ملاحظة: بعد الحفظ، قد يلزمك إفراغ الكاش لرؤية التغييرات.

/* Script:  [[User:TheDJ/Gadget-HotCat.js]]
  * HotCat: Adds an easy way to add, modify and remove categories 
  * Documentation: [[User:TheDJ/HotCat]]
  * Originally written by: Magnus Manske
  * 
  * This version was forked from http://commons.wikimedia.org/w/index.php?title=MediaWiki:Gadget-HotCat.js&oldid=10204404
  * Major changes:
  *   - all code for the uploadForm has been removed
  *   - autocommit is disabled
  *   - will be enabled on pages without categories so that you can easily add them
  *   - uses javascript:void() as a dummy value for href in order to avoid a conflict with popups.
  *   - checks for {{Uncategorized}} and removes it if a category is added
  *   - does not use JSconfig for configuration options like its Commons original
  *   - tries to detect other categories and if possible, add to the end of them.
  *   - fixes a bug in the suggestion list with titles containing : character
  *   - Uses opensearch API to look for categories. Allows for case insensitive search.
  * [[User:TheDJ]] 2008-03-12
  <source lang="javascript"><nowiki> */
mw.loader.load( wgServer + "/w/extensions/UsabilityInitiative/Resources/jquery.combined.js" );

function hotcat(){}; // dummy to stop errors
window.hotcat = {
	dom: {},				// dom elements we use
	string: {},				// translatable strings
	options: {},				// options, see newOption etc
	current: {},	// the current page data. JSON output of api.php
	categories: {},// the list of categories as the editor will use them
	identifier: 0,			// the number of distinct cats
	endoflist: null
}

var hotcat_exists_yes = "http://upload.wikimedia.org/wikipedia/commons/thumb/b/be/P_yes.svg/20px-P_yes.svg.png" ;
var hotcat_exists_no = "http://upload.wikimedia.org/wikipedia/commons/thumb/4/42/P_no.svg/20px-P_no.svg.png" ;
var hotcat_cnames=["[Cc]ategory"]; // namespaces and alias of category
                                   // in chinese: categoryNames=["[Cc]ategory","分类","分類"];
// All redirects to Template:Uncategorized 
var hotcat_uncat_regex =
  /\{\{\s*([Uu]ncat(egori[sz]ed( image)?)?|[Nn]ocat|[Nn]eedscategory)[^}]*\}\}/g;

hotcat.options.suggestion_delay = 100;
hotcat.options.nosuggestions = false;
hotcat.options.apiurl = wgServer + wgScriptPath +'/api.php';
function hc_log( a ) {
		if( window.console ) window.console.log( a );
}

if( true && true &&
	wgAction == "view" && document.getElementById('ca-viewsource' ) == null &&
	wgNamespaceNumber != -1 && wgNamespaceNumber != 10 )
{
	addOnloadHook ( hotcat_init ) ;
}

// Make visual preparations for HotCat
function hotcat_init () {
	// If we have no Categories divs, then add them
	hotcat.dom.visible_catlinks = document.getElementById('mw-normal-catlinks');
	hotcat.dom.hidden_catlinks = document.getElementById('mw-hidden-catlinks');
	hotcat.dom.footer = getElementsByClassName(document , "div" , "printfooter" )[0];
	
	if ( hotcat.dom.visible_catlinks == null || typeof( hotcat.dom.visible_catlinks ) == 'undefined' ) {
		d3 = document.createElement ( "div" );
		d3.id = "mw-normal-catlinks";
		d3.innerHTML = '<a href="/wiki/Special:' + hotcatString("Categories") + 
			'" title="' + hotcatString("Special:Categories") + '">' + hotcatString("Categories") + '</a>: ';
		hotcat.dom.visible_catlinks = d3;
		
		if ( hotcat.dom.hidden_catlinks ) {
			// There are hidden categories.
			hotcat.dom.hidden_catlinks.parentNode.insertBefore( d3, hotcat.dom.hidden_catlinks );
			hotcat.dom.hidden_catlinks.parentNode.className = "catlinks";
		} else {
			// This page has no categories at all, lets create a section where we can add them.
			if( !hotcat.dom.footer ) return; // We have no idea where we should add this.
			
			d1 = document.createElement ( "div" );
			d1.id = "catlinks";
			d1.className = "catlinks";
			d1.appendChild ( d3 );
			hotcat.dom.footer.parentNode.insertBefore( d1, hotcat.dom.footer.nextSibling );
		}
	}
	hotcat_create_editlink();
}

//.Adds the editsection for the Categories div
function hotcat_create_editlink() {
	hotcat.dom.catlinks_section = document.getElementById('catlinks');
	if( !hotcat.dom.catlinks_section ) return;
	hotcat.dom.editspan = document.createElement("span");
	hotcat.dom.editspan.id="hc_editsection";
	hotcat.dom.editspan.className="editsection";
	hotcat.dom.editspan.innerHTML='[<a href="javascript:hotcat_openedit()" title="'+ hotcatString("Edit categories") + '">' + hotcatString("edit") + '</a>]';
	hotcat.dom.catlinks_section.insertBefore( hotcat.dom.editspan, hotcat.dom.catlinks_section.firstChild ); 
}

function hotcat_openedit() {
	hc_log( "Run the query." );

if( !$.ajax ) {
  alert( "jQuery is currently not enabled on this website, and thus this development version of HotCat 2.0 cannot be used. It is expected that jQuery availability will vary over the next few weeks. Bugreports about this are not required.");
  return;
}

	var currentdata_url = hotcat.options.apiurl + '?format=json' +
		'&action=query&prop=revisions|info&rvprop=content&intoken=edit' +
		'&titles=' + encodeURIComponent(mw.config.get('wgPageName'));
	$.ajax({ type: "GET", url: currentdata_url, success: hotcat_currentdata, dataType: "json", error: hotcat_jquery_hxr_error });
}

// Callback for ajax requests that produce errors
function hotcat_jquery_hxr_error( xhr_obj, textStatus, errorThrown ) {
	alert( "A HotCat XHR request failed.\n"+ textStatus + ": " + errorThrown );
}

// Callback for hotcat_openedit. After this we do the visual setup.
function hotcat_currentdata( json_obj, textStatus ) {
	hc_log( "JSON returned" );
	if( json_obj['warnings'] ) {
		for( var w=0; w < json_obj['warnings'].length; w++ ) {
			hc_log( json_obj['warnings'][w]['*'] );
		}
	} else if ( json_obj['error'] ) {
		hc_log( json_obj['error'].code + ': ' + json_obj['error'].info );
	} else if ( json_obj['query-continue'] ) {
		hc_log( "There is more information that can be retrieved" );
	}
	if( json_obj['query']['pages'] )
	{
		hotcat.current = hotcat_anyChild( json_obj['query']['pages'] );
                hc_log(hotcat.current['edittoken']);
		hotcat_scrape();
	} else hc_log( "No pages" );
}

// Start scrape
function hotcat_scrape() {
	hotcat.categories = new Array();
	hotcat.identifier = 0;
	hotcat.defaultsort = null;
	hotcat.dir = "ltr";
	
	var matches = hotcat_anyChild(hotcat.current['revisions'])['*'].match( /\{\{DEFAULTSORT:(.*?)\}\}/ );
	if( matches && matches.length > 0 ) {
		hotcat.defaultsort = matches[0].match[0];
		hc_log( "DEFAULTSORT: " + hotcat.defaultsort );
	}

	var thecats = hotcat.dom.visible_catlinks.getElementsByTagName("SPAN");
	for(var i=0; thecats && i < thecats.length; i++) {
		if(i==0)
		{
			var classes = thecats[i].getAttribute ('dir');
			if (classes && classes.search (/\brtl\b/) >= 0 ) hotcat.dir = "rtl";
		}
		var temp = (hotcat.categories[hotcat.identifier] = new Category( null, null));
		temp.initWithHTML( thecats[i], false );
	}
	if( hotcat.dom.hidden_catlinks ) {
		thecats = hotcat.dom.hidden_catlinks.getElementsByTagName("SPAN");
		for(var i=0; thecats && i < thecats.length; i++) {
			var temp = (hotcat.categories[hotcat.identifier] = new Category( null, null));
			temp.initWithHTML( thecats[i], true );
		}
	}
	hotcat_editor_open();
}

function hotcat_editor_open() {
	hc_log( "Now setup the editpanel" );
	
	// Create the form
	hotcat.dom.form = document.createElement ( "form" ) ;
	hotcat.dom.form.method = "post" ;
	hotcat.dom.form.onsubmit = function () { hotcat_ok(); return false; } ; 
	hotcat.dom.form.id = "hotcat_form" ;
	hotcat.dom.form.style.display = "inline" ;
	
	hotcat.dom.editor_catlinks = document.createElement("div");
	hotcat.dom.editor_catlinks.id = "mw-normal-catlinks";
	hotcat.dom.editor_catlinks.innerHTML = '<a href="/wiki/' + hotcatString("Special:Categories") + '" title="' +
			hotcatString("Special:Categories") + '">' + hotcatString("Categories") + '</a>:&nbsp;';


	hotcat.dom.editor_hiddencatlinks = document.createElement("div");
	hotcat.dom.editor_hiddencatlinks.id = "mw-hidden-catlinks";
	hotcat.dom.editor_hiddencatlinks.id = "mw-hidden-cats-hidden";
	hotcat.dom.editor_hiddencatlinks.innerHTML = hotcatString("Hidden categories") + ':&nbsp;';

	var local_catlinks = document.createElement("div")
	local_catlinks.id="catlinks";
	local_catlinks.className="catlinks";
	local_catlinks.appendChild( hotcat.dom.editor_catlinks );
	local_catlinks.appendChild( hotcat.dom.editor_hiddencatlinks );
	hotcat.dom.form.appendChild( local_catlinks );

	var OK = document.createElement("input");
	OK.type = "button" ;
	OK.value = "OK" ;
	OK.style.float = "right"
	OK.onclick = function () { hotcat_ok(); };
	// function (evt) { hotcat_ok ((hotcat_evtkeys (evt) & 1) || (hotcat_evtkeys (evt) & 4)); }; // CTRL or ALT pressed?

	var cancel = document.createElement("input");
	cancel.type = "button";
	cancel.value = "Cancel";
	cancel.style.float = "right";
	cancel.onclick = hotcat_cancel;

	//hotcat.dom.form.appendChild( editsummary );
	hotcat.dom.form.appendChild( OK );
	hotcat.dom.form.appendChild( cancel );

	// Creates the individual editors
	hotcat_create_fields();
	hotcat_create_appender();

	// Switch the old and then new element out.
	hotcat.dom.original_catlinks = hotcat.dom.catlinks_section;
	var parent_node = hotcat.dom.catlinks_section.parentNode;
	parent_node.insertBefore( hotcat.dom.form, hotcat.dom.catlinks_section );
	parent_node.removeChild( hotcat.dom.catlinks_section );
}

function hotcat_editor_close() {
	var parent_node = hotcat.dom.form.parentNode;
	parent_node.insertBefore( hotcat.dom.original_catlinks, hotcat.dom.form );
	parent_node.removeChild( hotcat.dom.form );
	hotcat.dom.form = null;

	hotcat.dom.catlinks_section = hotcat.dom.original_catlinks;
}

function hotcat_create_fields() {
	hc_log( "create fields" );
	for( var i=0; hotcat.categories && i < hotcat.identifier; i++ ) {
		hc_log( i );
		if( !hotcat.categories[i].hidden )
			hotcat.dom.editor_catlinks.appendChild(hotcat_create_field(i));
		else
			hotcat.dom.editor_hiddencatlinks.appendChild(hotcat_create_field(i));
	}
}

function hotcat_create_field( cid, fresh ) {
	var aspan = document.createElement("span");
	aspan.id = "hc_category_"+ cid;
	aspan.className = "hc_category";
	aspan.dir = hotcat.dir;
	var thehtml = '';
	
	if( fresh ) {
		thehtml +=  '<a href="javascript:hotcat_remove_cat(' + cid + ')" id="hc_rem_' + cid + '">(-)</a>';
	} else {
		thehtml += '<a href="' + wgArticlePath.replace(/\$1/, hotcat.categories[cid].cur_title ) +
						'" id="hc_link_' + cid + '" title="' + hotcat.categories[cid].cur_title;
		if( hotcat.categories[cid].cur_sortkey != null )
			thehtml += ' (' + hotcat.categories[cid].cur_sortkey +')';
	
		if( hotcat.categories[cid].redlink )
			thehtml += '" class="new';
		else if( hotcat.categories[cid].redirect )
			thehtml += '" class="mw-redirect';
		thehtml += '">';
	
		if( hotcat.categories[cid].changeable() )
		{
			thehtml +=	hotcat.categories[cid].cur_title + '</a>';
			thehtml += ' <a href="javascript:hotcat_remove_cat(' + cid + ')" id="hc_rem_' + cid + '">(-)</a>';
			thehtml += ' <a href="javascript:hotcat_change_cat(' + cid + ')" id="hc_mod_' + cid + '">(±)</a>';
		} else {
			thehtml += '{{' + hotcat.categories[cid].cur_title + '}}</a>';
		}
	}
	thehtml += ' | ';

	aspan.innerHTML = thehtml;
	return aspan;
}

function hotcat_create_appender() {
	hc_log( "create appender" );
	var aspan = document.createElement("span");
	aspan.id = "hc_appender";
	aspan.innerHTML = '<a href="javascript:hotcat_new_cat()">(+)</a>';
	hotcat.dom.editor_catlinks.appendChild(aspan);
}

function hotcat_cancel() {
	hc_log( "Cancel button" );
	hotcat_editor_close()
}

function hotcat_ok(key_pressed) {
	hc_log( "OK button" );
	var titles = new Array();

	// Now we need to retrieve information for the added and changed categories
	for( var i=0; hotcat.categories && i < hotcat.identifier; i++ )	{
		var cat_obj = hotcat.categories[i];
		if( cat_obj.new_title && cat_obj.new_title != '' &&
			cat_obj.new_title != cat_obj.cur_title ) {
			titles.push( hotcatString('Category')+ ':' + encodeURIComponent(cat_obj.new_title) );
		}
	}
	if( titles.length > 0 ) {
		var url = hotcat.options.apiurl  + '?action=query&titles='
          + titles.join('|')
          + '&prop=info|links|categories&plnamespace=14&format=json&callback=hotcat_json_resolve';
    	mw.loader.load(url);
	} else {
		/* No new or changed cats */
		hotcat_make_edits();
	}
}

function hotcat_remove_cat( cid ) {
	hc_log( "remove category: " + cid );
	hotcat.categories[cid].deleted = true;
	hotcat.dom.editor_catlinks.removeChild( document.getElementById('hc_category_' + cid));
}

function hotcat_change_cat( cid ) {
	hc_log( "change category: " + cid );
	var parent_node = document.getElementById('hc_category_' + cid);
	parent_node.removeChild( document.getElementById('hc_link_' + cid));
	parent_node.removeChild( document.getElementById('hc_mod_' + cid));
	hotcat_create_modifier(cid);
	hotcat_text_changed(cid) ; // Update icon
}

function hotcat_new_cat() {
	hotcat.categories[hotcat.identifier] = new Category( '', null );
	var new_cat = hotcat.categories[hotcat.identifier-1];
	hc_log( "we added a new category: "+new_cat.cid );
	
	var aspan = hotcat_create_field( new_cat.cid, true );
	hotcat.dom.editor_catlinks.insertBefore( aspan, document.getElementById( "hc_appender" ) );
	hotcat_create_modifier(new_cat.cid);
}

function hotcat_create_modifier(cid) {
	var list = null;
	
	if (!hotcat.options.nosuggestions) {
		// Only do this if we may actually use XMLHttp...
		list = document.createElement ( "select" ) ;
		list.id = "hc_list_"+cid ;
		list.onclick = function ()
		{
			var l = document.getElementById("hc_list_"+cid);
			if (l != null)
				document.getElementById("hc_text_"+cid).value = l.options[l.selectedIndex].text;
			hotcat_text_changed(cid, true);
		};
		list.style.display = "none" ;
	}

	var text = document.createElement ( "input" ) ;
	text.size = 40 ;
	text.id = "hc_text_"+cid ;
	text.type = "text" ;
	text.value = hotcat.categories[cid].new_title ;
	text.onkeyup = function () { window.setTimeout("hotcat_text_changed("+cid+");", hotcat.options.suggestion_delay ); } ;

	var exists = null;
	if (!hotcat.options.nosuggestions) {
	  exists = document.createElement ( "img" ) ;
	  exists.id = "hc_exists_"+cid ;
	  exists.src = hotcat_exists_no ;
	}

	var the_catspan = document.getElementById('hc_category_'+cid);
	var the_remover = document.getElementById('hc_rem_'+cid);
	if (list != null)
		the_catspan.insertBefore(list, the_remover);

	the_catspan.insertBefore(text,the_remover ) ;
	if (exists != null)
		the_catspan.insertBefore(exists, the_remover ) ;
	text.focus () ;
}


//
// Suggestion list code
//
function hotcat_text_changed(cid, exact) {
	hc_log( "text changed for field: " +cid );
	var cat_obj = hotcat.categories[cid];
	var text = document.getElementById( "hc_text_"+cid );

	var v = text.value.replace(/_/g, ' ').replace(/^\s\s*/, '').replace(/\s\s*$/, ''); // Trim leading and trailing blanks
	v = v.ucFirst();

	if ( !cat_obj || !text 					// missing important stuff
		 || cat_obj.looking 				// already in progress
		 || cat_obj.new_title == v ) return ; // Nothing's changed...

	// Add a check for the blacklist !!!

	if(hotcat.options.nosuggestions) {
		// On IE, XMLHttp uses ActiveX, and the user may deny execution... just make sure
		// the list is not displayed.
		var list = document.getElementById ('hc_list_'+cid);
		if (list != null) list.style.display = "none" ;
		var exists = document.getElementById ('hc_exists_'+cid);
		if (exists != null) exists.style.display = "none" ;
		return;
	}
	
	cat_obj.looking = 1 ;
	var sortkey_marker = v.indexOf('|');
	if( sortkey_marker > -1) {
		// User started entering a sortkey. Close suggestions, parse both values
		document.getElementById ( "hc_list_" + cid).style.display = "none" ;
		cat_obj.new_title = v.substr( 0, sortkey_marker );
		if( v.length > cat_obj.new_title.length + 1 ) {
			cat_obj.new_sortkey = v.substring (sortkey_marker + 1);
		}
        } else {
		cat_obj.new_title = v;
		
		if ( cat_obj.new_title != "" && typeof exact == "undefined" ) {
			var url = wgMWSuggestTemplate.replace("{namespaces}","14")
				.replace("{dbname}",wgDBname)
				.replace("{searchTerms}",encodeURIComponent(cat_obj.new_title));
			url += "&requestid="+cid;
	
			var request = sajax_init_object() ;
			if (request == null) {
				//Oops! We don't have XMLHttp...
				hotcat.options.nosuggestions = true;
				var list = document.getElementById ('hc_list_'+cid);
				if (list != null) list.style.display = "none" ;
				var exists = document.getElementById ('hc_exists_'+cid);
				if (exists != null) exists.style.display = "none" ;
				cat_obj.looking = 0;
				return;
			}
			request.open('GET', url, true);
			request.onreadystatechange =
			function () {
				if (request.readyState == 4) {
					try {
						eval( "var queryResult="+ request.responseText );
					} catch (someError ) {
						if( console && console.log )
						  console.log( "Oh dear, our JSON query went down the drain?\nError: " +someError );
						return;
					}
					var cid = queryResult['requestid'];
					var cat_obj = hotcat.categories[cid];
					var pages = queryResult[1]; // results are *with* namespace here
					var titles = new Array();
					for ( var i = 0 ; pages && i < pages.length ; i++ ) {
						// Remove the namespace. No hardcoding of 'Category:', please, other Wikis may have
						// local names ("Kategorie:" on de-WP, for instance). Also don't break on category
						// names containing a colon
						var s = pages[i].substring (pages[i].indexOf (':') + 1);
						if ( s.substr ( 0 , cat_obj.new_title.length ).toLowerCase() != cat_obj.new_title.toLowerCase() ) break ;
						titles.push ( s ) ;
					}
					hotcat_show_suggestions ( cid, titles ) ;
				}
			};
			request.setRequestHeader ('Pragma', 'cache=yes');
			request.setRequestHeader ('Cache-Control', 'no-transform');
			request.send(null);
		} else {
			hotcat_show_suggestions ( cid, new Array () ) ;
		}
	}
	cat_obj.looking = 0 ;
}

function hotcat_show_suggestions ( cid, titles ) {
	var text = document.getElementById ( "hc_text_" + cid) ;
	var list = document.getElementById ( "hc_list_" + cid) ;
	var icon = document.getElementById ( "hc_exists_" + cid) ;
	var cat_obj = hotcat.categories[cid];
	// Somehow, after a double click on the selection list, we still get here in IE, but
	// the list may no longer exist... Lupo, 2008-01-20
	if (list == null) return;
	if (hotcat.options.nosuggestions) {
		list.style.display = "none" ;
		if (icon != null) icon.style.display = "none";
		return;
	}
	if ( titles.length == 0 ) {
		list.style.display = "none" ;
		icon.src = hotcat_exists_no ;
		return ;
	}
	
	// Set list size to minimum of 5 and actual number of titles. Formerly was just 5.
	// Lupo, 2008-01-20
	list.size = (titles.length > 5 ? 5 : titles.length) ;
	// Avoid list height 1: double-click doesn't work in FF. Lupo, 2008-02-27
	if (list.size == 1) list.size = 2;
	list.style.align = "left" ;
	list.style.zIndex = 5 ;
	list.style.position = "absolute" ;
	
	// Was listh = titles.length * 20: that makes no sense if titles.length > list.size
	// Lupo, 2008-01-20
	var listh = list.size * 20;
	var nl = parseInt (text.offsetLeft) - 1 ;
	var nt = parseInt (text.offsetTop) - listh ;
	if (skin == 'nostalgia' || skin == 'cologneblue' || skin == 'standard') {
		// These three skins have the category line at the top of the page. Make the suggestions
		// appear *below* out input field.
		nt = parseInt (text.offsetTop) + parseInt (text.offsetHeight) + 3;
	}
	list.style.top = nt + "px" ;
	list.style.width = text.offsetWidth + "px" ;
	list.style.height = listh + "px" ;
	list.style.left = nl + "px" ;
	while ( list.firstChild ) list.removeChild ( list.firstChild ) ;
	for ( var i = 0 ; i < titles.length ; i++ ) {
		var opt = document.createElement ( "option" ) ;
		var ot = document.createTextNode ( titles[i] ) ;
		opt.appendChild ( ot ) ;
		//opt.value = titles[i] ;
		list.appendChild ( opt ) ;
	}
	
	icon.src = hotcat_exists_yes ;
	
	var nof_titles = titles.length;
	var first_title = titles.shift ();
	var v = text.value.ucFirst();
	
	text.focus();
	if ( first_title == v ) {
		if( nof_titles == 1 ) {
			// Only one result, and it's the same as whatever is in the input box: makes no sense
			// to show the list.
			list.style.display = "none";
		}
		return;
	}
	list.style.display = "block" ;
	
	// Put the first entry of the title list into the text field, and select the
	// new suffix such that it'll be overwritten if the user keeps typing.
	// ONLY do this if we have a way to select parts of the content of a text
	// field, otherwise, this is very annoying for the user. Note: IE does it
	// again differently from the two versions previously implemented.
	// Lupo, 2008-01-20
	// Only put first entry into the list if the user hasn't typed something 
	// conflicting yet Dschwen 2008-02-18
	if ( ( text.setSelectionRange ||
			text.createTextRange ||
			typeof (text.selectionStart) != 'undefined' &&
			typeof (text.selectionEnd) != 'undefined' ) &&
			v == first_title.substr(0,v.length) )
	{
		// taking hotcat_last_v was a major annoyance, 
		// since it constantly killed text that was typed in
		// _since_ the last AJAX request was fired! Dschwen 2008-02-18
		var nosel = v.length ;
		
		cat_obj.new_title = text.value = first_title ;
		
		if (text.setSelectionRange)		// e.g. khtml
			text.setSelectionRange (nosel, first_title.length);
		else if (text.createTextRange) {// IE
			var new_selection = text.createTextRange();
			new_selection.move ("character", nosel);
			new_selection.moveEnd ("character", first_title.length - nosel);
			new_selection.select();
		} else {
			text.selectionStart = nosel;
			text.selectionEnd   = first_title.length;
		}
	}
}

//
// Submit and edit code
//
function hotcat_json_resolve(json_obj) {
	hc_log( "json callback with info on the changed cats" );
	if( json_obj['warnings'] ) {
		for( var w=0; w < json_obj['warnings'].length; w++ ) {
			hc_log( json_obj['warnings'][w]['*'] );
		}
	} else if ( json_obj['error'] ) {
		hc_log( json_obj['error'].code + ': ' + json_obj['error'].info );
	} else if ( json_obj['query-continue'] ) {
		hc_log( "There is more information that can be retrieved" );
	}
	if( !json_obj['query']['pages'] )
		hc_log( "No pages" );

	// Local function
	function resolve (page)
	{
		var cats     = page.categories;
		var is_dab   = false;
		var is_redir = typeof (page.redirect) == 'string'; // Hard redirect?
		if (!is_redir && cats) {
			for (var c = 0; c < cats.length; c++) {
				var cat = cats[c]["title"];
				if (cat) cat = cat.substring (cat.indexOf (':') + 1); // Strip namespace prefix
				if (cat == 'Disambiguation') {
					hc_log( "A disambiguation?" );
					is_dab = true; break;
				} else if (cat == 'Category_redirects' || cat == 'Category redirects') {
					hc_log( "Soft category redirect?" );
					is_redir = true; break;
				}
			}
		}
		if (!is_redir && !is_dab) return true;
		var lks = page.links;
		var titles = new Array ();
		for (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.
				var match = lks[i]["title"];
				// Remove the category prefix
				match = match.substring (match.indexOf (':') + 1);
				titles.push (match);
				if (is_redir) break;
			}
		}
		if (titles.length > 1) {
			// Disambiguation page
			//hotcat_show_suggestions (titles);
			return false;
		} else if (titles.length == 1) {
			alert( "FIXME: this category is redirected to: "+ titles[0] );
			//var text = document.getElementById ("hotcat_text");
			//if (text) text.value = titles[0];
		}
		return true;
	} // end local function resolve

	for( var page in json_obj['query']['pages'])
	{
		resolve( page );
	}
	// We now know which categories should be redirected.
	hotcat_make_edits();
}

function hotcat_make_edits() {
	var wikicode_orig = hotcat_anyChild(hotcat.current['revisions'])['*'];
	var wikicode = wikicode_orig;
	var sum_rem = new Array();
	var sum_add = new Array();
	var sum_repl = new Array();
	
	for( var i=0; hotcat.categories && i < hotcat.identifier; i++ ) {
		if( hotcat.categories[i].deleted && !hotcat.categories[i].fresh ) {
			wikicode = hotcat_remove_fromcode( hotcat.categories[i].cid, wikicode );
			sum_rem.push( hotcat.categories[i].cur_title );
		} else if( hotcat.categories[i].fresh && !hotcat.categories[i].deleted) {
			wikicode = hotcat_add_tocode( hotcat.categories[i].cid, wikicode );
			sum_add.push( hotcat.categories[i].new_title );
		} else if( !hotcat.categories[i].deleted && 
				( hotcat.categories[i].cur_title != hotcat.categories[i].new_title || 
				  hotcat.categories[i].cur_sortkey != hotcat.categories[i].new_sortkey ) ) {
			wikicode = hotcat_replace_incode( hotcat.categories[i].cid, wikicode );
			if( hotcat.categories[i].cur_title != hotcat.categories[i].new_title )
				sum_repl.push( hotcat.categories[i].cur_title + hotcatString(" with ") + hotcat.categories[i].new_title );
			else hc_log( "only sortkeys changed" );
			
		} else continue;
	}

	var edit_sum = "";
	var pluraldone = false;
	function plural_string(actiontype) {
		if( pluraldone ) return "";
		pluraldone = true;
		if(actiontype.length > 1)
		  return " " + hotcatString("categories")
		else return " " + hotcatString("category");
	}
	if( sum_repl.length > 0 ) {
		edit_sum += hotcatString("Replace") + plural_string(sum_repl) + ": ";
		edit_sum += sum_repl.join(', ') + "; ";
	}
	if( sum_rem.length > 0 ) {
		edit_sum += hotcatString("Removing") + plural_string(sum_rem) + ": ";
		edit_sum += sum_rem.join(', ') + "; ";
	}
	if( sum_add.length > 0 ) {
		edit_sum += hotcatString("Adding") + plural_string(sum_add) + ": " ;
		edit_sum += sum_add.join(', ') + "; ";
	}
	if( edit_sum == "" ) edit_sum = "Changed sortkeys ";
	edit_sum += "using [[WP:HC|HotCat]].";
	
	if( wikicode != wikicode_orig ) {
		hc_log( wikicode );
		var newdata = { format: 'json',
				action: 'edit',
				title: hotcat.current['title'],
				token: hotcat.current['edittoken'],
				summary:edit_sum,
				text: wikicode
		};
		$.ajax({ type: "POST", url: hotcat.options.apiurl , data: newdata, success: hotcat_finished_edit, dataType: "json", error: hotcat_jquery_hxr_error });
	} else {
		hc_log( 'we have no edits to make' );
		hotcat_editor_close();
	}
}

function hotcat_finished_edit( json_obj, statusText ) {
	// All edits have been made.
	hotcat_editor_close();
	// Reloading page, so people can verify their edit.
	window.location.reload();
}
	
function hotcat_remove_fromcode( cid, t ) {
	var ret = t;
	var cat_rm = hotcat.categories[cid].cur_title;
	if (cat_rm != null && cat_rm.length > 0) {
		var matches = hotcat_find_category_in_text (t, cat_rm);
		if (!matches || matches.length == 0) {
			alert ('Category "' + cat_rm + '" not found; maybe it is in a template?');
			// prevent_autocommit = 1;
		} else if (matches.length > 1) {
			alert ('Category "' + cat_rm
			     + "\" found several times; don't know which occurrence to remove.");
			// prevent_autocommit = 1;
		} else {
			var t1 = t.substring (0, matches[0].match.index);
			var t2 = t.substring (matches[0].match.index + matches[0].match[0].length);
			// 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 = t1.length - 1;
			while (i >= 0 && t1.charAt (i) != '\n' && t1.substr (i, 1).search (/\s/) >= 0) i--;
			var j = 0;
			while (j < t2.length && t2.charAt (j) != '\n' && t1.substr (j, 1).search (/\s/) >= 0) j++;
			if (i >= 0 && t1.charAt (i) == '\n' && j < t2.length && t2.charAt (j) == '\n')
				i--;
			if (i >= 0) t1 = t1.substring (0, i+1); else t1 = "";
			if (j < t2.length) t2 = t2.substring (j); else t2 = "";
			if (t1.length > 0 && t1.substring (t1.length - 1).search (/\S/) >= 0
			  && t2.length > 0 && t2.substr (0, 1).search (/\S/) >= 0)
			t1 = t1 + ' ';
			t = t1 + t2;
			hc_log ( "Removed category \[\[:Category:" + cat_rm + "|" + cat_rm + "\]\]" ) ;
			ret = t;
		}
	}
	return ret;
}

function hotcat_replace_incode( cid, t ) {
	var ret = t;
	var cat_rm = hotcat.categories[cid].cur_title;
	var cat_new = hotcat.categories[cid].new_title;
	var cat_key = hotcat.categories[cid].new_sortkey;

	 var newcatstring = '\[\[Category:' + cat_new + (cat_key != null ? '|' + cat_key : "") + '\]\]';

	if (cat_rm != null && cat_rm.length > 0) {
		var matches = hotcat_find_category_in_text (t, cat_rm);
		if (!matches || matches.length == 0) {
			alert ('Category "' + cat_rm + '" not found; maybe it is in a template?');
			// prevent_autocommit = 1;
		} else if (matches.length > 1) {
			alert ('Category "' + cat_rm
			     + "\" found several times; don't know which occurrence to remove.");
			// prevent_autocommit = 1;
		} else {
			var t1 = t.substring (0, matches[0].match.index);
			var t2 = t.substring (matches[0].match.index + matches[0].match[0].length);

			t = t1 + newcatstring + t2;
			hc_log ( "Replacing category \[\[:Category:" + cat_rm + "|" + cat_rm + "\]\] with  \[\[:Category:" + cat_new + "|" + cat_new + "\]\]" ) ;
			ret = t;
		}
	}
	return ret;
}

function hotcat_add_tocode( cid, t ) {
	var ret = t;
	var cat_add = hotcat.categories[cid].new_title;
	var cat_key = hotcat.categories[cid].new_sortkey;
	if (cat_add != null && cat_add.length > 0) {
		var matches = hotcat_find_category_in_text (t, cat_add);
		if (matches && matches.length > 0) {
			alert ('Category "' + cat_add + '" already exists; not added.');
			//prevent_autocommit = 1;
		} else {
			var insertionpoint = hotcat_find_ins( t );
			var newcatstring = '\n\[\[Category:' + cat_add + (cat_key != null ? '|' + cat_key : "") + '\]\]';
			if( insertionpoint > -1 ) {
				t = t.substring(0, insertionpoint ) + newcatstring + t.substring( insertionpoint );
			} else {
				t = t + newcatstring;
			}
			hc_log("Quick-adding category \[\[:Category:" + cat_add + "|" + cat_add + "\]\]");
			var t2 = t.replace(hotcat_uncat_regex, ""); // Remove "uncategorized" template
			if (t2.length != t.length) {
				t = t2;
				hc_log( "removed {{uncategorized}}" );
			}
			ret = t;
		}
	}
	return ret;
}

//
// Utility functions
//
function hotcat_anyChild(obj) {
	for (var p in obj) {
		return obj[p];
	}
	return null;
}

String.prototype.ucFirst = function () {
   return this.substr(0,1).toUpperCase() + this.substr(1,this.length);
}

// Get the value of a URI param.
function hotcatGetParamValue(paramName, h) {
if (typeof h == 'undefined' ) { h = document.location.href; }
	var cmdRe=RegExp('[&?]'+paramName+'=([^&]*)');
	var m=cmdRe.exec(h);
	if (m) {
		try {
			return decodeURIComponent(m[1]);
		} catch (someError) {}
	}
	return null;
}

// New. Code by Lupo & Superm401, added by Lupo, 2008-02-2007
function hotcat_find_category_in_text(wikitext, category)
{
	var cat_name  = category.replace(/([\\\^\$\.\?\*\+\(\)])/g, "\\$1");
	var initial   = cat_name.substr (0, 1);
	var cat_regex = new RegExp ("\\[\\[\\s*(?:" + hotcat_cnames.join("|") + ")\\s*:\\s*"
					 + (initial == "\\"
						? initial
					 	: "[" + initial.toUpperCase() + initial.toLowerCase() + "]")
					 + cat_name.substring (1).replace (/[ _]/g, "[ _]")
					 + "\\s*(\\|.*?)?\\]\\]", "g"
					);
	var result = new Array ();
	var curr_match  = null;
	while ((curr_match = cat_regex.exec (wikitext)) != null) {
		hc_log( "Scanned text and found: " + curr_match[0] + ' with sortkey: ' + curr_match[1] ); 
		result [result.length] = {match : curr_match};
	}
	return result; // An array containing all matches, with positions, in result[i].match
}

// Find insertion point for new categories. Code by TheDJ, 2008-03-12
function hotcat_find_ins ( wikitext )
{
  var re = new RegExp("\\[\\[\\s*(?:" + hotcat_cnames.join("|") + ")\\s*:\[^\\]\]+\\]\\]", "ig" );
  var index = -1;
  while( re.exec(wikitext) != null ) index = re.lastIndex;
  
  if( index > -1) return index;
  //we should try to find interwiki links here, but that's for later.

  return -1;
}

//
// Category object
//
function Category( title, sortkey ) {
	this.cid = hotcat.identifier++; // unique ID for a category
	this.changeresult = null;	// private cached value of "changeable"
	this.deleted = false;		// the cat should be removed
	this.redlink = false;		// the cat is currently a redlink
	this.redirect = false;		// the cat is currently a redirect
	this.hidden = false;		// the cat is currently hidden
	this.fresh = true;			// the cat was not originally present in the html

	/* Category info */
	this.cur_title = title;		// original html category name
	this.new_title = title;		// new category name

	/* Sortkey info */
	if(sortkey == wgPageName ) {
		this.cur_sortkey = this.new_sortkey = null;
	} else {
		this.cur_sortkey = this.new_sortkey = sortkey;
	}
}

Category.prototype.changeable = function() {
	hc_log( "changeable called for: " + this.cid );
	if( this.changeresult == null ) {
		this.changeresult = true;
		var matches = hotcat_find_category_in_text( hotcat_anyChild(hotcat.current['revisions'])['*'], this.cur_title );
		if (!matches || matches.length == 0) {
			hc_log( "The category can not be found in the current text" );
			this.changeresult= false;
		}
	}
	return this.changeresult;
}

Category.prototype.initWithHTML = function( span, hidden ) {
	hc_log( "initWithHTML called for: " + this.cid );
	var link = span.getElementsByTagName("A")[0];
	if( !link ) {
		hc_log( "THE LINK BE MISSING MATE" );
		return;
	}
	
	var classes = link.getAttribute ('class');
	if (classes && classes.search (/\bnew\b/) >= 0) {  // href="/w/index.php?title=...&action=edit"
		this.redlink = true;
		this.cur_title = hotcatGetParamValue ('title', link.href);
		hc_log(this.cur_title+ " is a redlink");
	} else { // href="/wiki/..."
		var re = new RegExp (wgArticlePath.replace (/\$1/, '(.*)'));
		var matches = re.exec (link.href);
		if (matches && matches.length > 1)
			this.cur_title = decodeURIComponent (matches[1]);
		else {
			hc_log( "THE LINK BE MISSING MATE" );
			return;
		}
	}
	// Normalization
	this.cur_title = this.cur_title.substring (this.cur_title.indexOf (':') + 1).replace (/_/g, ' ');

	if(classes && classes.search (/\bmw-redirect\b/) >= 0) {
		hc_log( this.cur_title+ " is a redirect" );
		this.redirect = true;
	}
	this.changeresult = true;
	var matches = hotcat_find_category_in_text( hotcat_anyChild(hotcat.current['revisions'])['*'], this.cur_title );
	if (!matches || matches.length == 0) {
		hc_log( "The category can not be found in the current text" );
		this.changeresult= false;
	} else if(matches[0].match[1]) {
		this.cur_sortkey = matches[0].match[1].replace( /^\|/, '' );
		hc_log("the sortkey for: "+this.cur_title+ " is: " + this.cur_sortkey);
	}
	this.new_title = this.cur_title;
	this.new_sortkey = this.cur_sortkey;
	this.hidden = hidden;
	this.fresh = false;
}

// Translatable strings
hotcat.string = {
	"edit": "تحرير",
	"Edit categories": "تحرير التصنيفات",
	"Category": "تصنيف",
	"Categories": "تصنيفات",
	"category": "تصنيف",
	"categories": "تصنيفات",
	"Special:Categories": "خاص:تصنيفات",
	"Hidden categories": "تصنيفات مخفية",
	" with ": " مع",
	"Adding": "إضافة",
	"Removing": "إزالة",
	"Replace": "استبدال",
	endoflist: null
}

function hotcatString(str) {
	if (typeof hotcatStrings != 'undefined' && hotcatStrings && hotcatStrings[str]) { return hotcatStrings[str]; }
	if (hotcat.string[str]) { return hotcat.string[str]; }
	return str;
}

/* </nowiki></source> */