Index: /MediaWiki/trunk/FCKeditor.php
===================================================================
--- /MediaWiki/trunk/FCKeditor.php	(revision 3190)
+++ /MediaWiki/trunk/FCKeditor.php	(revision 3191)
@@ -76,4 +76,5 @@
 $wgAjaxExportList[] = 'wfSajaxSearchImageFCKeditor';
 $wgAjaxExportList[] = 'wfSajaxSearchArticleFCKeditor';
+$wgAjaxExportList[] = 'wfSajaxSearchCategoryFCKeditor';
 $wgAjaxExportList[] = 'wfSajaxWikiToHTML';
 $wgAjaxExportList[] = 'wfSajaxGetImageUrl';
Index: /MediaWiki/trunk/FCKeditorSajax.body.php
===================================================================
--- /MediaWiki/trunk/FCKeditorSajax.body.php	(revision 3190)
+++ /MediaWiki/trunk/FCKeditorSajax.body.php	(revision 3191)
@@ -165,4 +165,54 @@
 }
 
+function wfSajaxSearchCategoryFCKeditor()
+{
+	global $wgContLang, $wgOut;
+	$ns = NS_CATEGORY;
+	$db =& wfGetDB( DB_SLAVE );
+	$m_sql="SELECT tmpSelectCat1.cl_to AS title FROM ".$db->tableName('categorylinks')." AS tmpSelectCat1 ".
+		"LEFT JOIN ".$db->tableName('page')." AS tmpSelectCatPage ON ( tmpSelectCat1.cl_to = tmpSelectCatPage.page_title ".
+		"AND tmpSelectCatPage.page_namespace =$ns ) ".
+		"LEFT JOIN ".$db->tableName('categorylinks')." AS tmpSelectCat2 ON tmpSelectCatPage.page_id = tmpSelectCat2.cl_from ".
+		"WHERE tmpSelectCat2.cl_from IS NULL GROUP BY tmpSelectCat1.cl_to";
+
+	$res = $db->query($m_sql,__METHOD__ );
+
+	$ret = "";
+	$i=0;
+	while ( ( $row = $db->fetchObject( $res ) ) ) {
+		$ret .= $row->title ."\n";
+		$sub = explode("\n",wfSajaxSearchCategoryChildrenFCKeditor($row->title));
+		foreach($sub as $subrow)if(strlen($subrow)>0)$ret.=" ".$subrow."\n";
+	}
+
+	return $ret;
+}
+
+function wfSajaxSearchCategoryChildrenFCKeditor($m_root)
+{
+	global $wgContLang, $wgOut;
+	$limit = 50;
+	$ns = NS_CATEGORY;
+	$m_root = str_replace("'","\'",$m_root);
+	$db =& wfGetDB( DB_SLAVE );
+	$m_sql ="SELECT tmpSelectCatPage.page_title AS title FROM ".$db->tableName('categorylinks')." AS tmpSelectCat ".
+			"LEFT JOIN ".$db->tableName('page')." AS tmpSelectCatPage ON tmpSelectCat.cl_from = tmpSelectCatPage.page_id ".
+			"WHERE tmpSelectCat.cl_to LIKE '$m_root' AND tmpSelectCatPage.page_namespace = $ns";
+
+
+	$res = $db->query($m_sql,__METHOD__ );
+
+	$ret = "";
+	$i=0;
+	while ( ( $row = $db->fetchObject( $res ) ) ) {
+		$ret .= $row->title ."\n";
+		$sub = explode("\n",wfSajaxSearchCategoryChildrenFCKeditor($row->title));
+		foreach($sub as $subrow)if(strlen($subrow)>0)$ret.=" ".$subrow."\n";
+
+	}
+
+	return $ret;
+}
+
 function wfSajaxSearchTemplateFCKeditor($empty) {
 	global $wgContLang, $wgOut;
Index: /MediaWiki/trunk/fckeditor_config.js
===================================================================
--- /MediaWiki/trunk/fckeditor_config.js	(revision 3190)
+++ /MediaWiki/trunk/fckeditor_config.js	(revision 3191)
@@ -13,5 +13,5 @@
 	['Undo','Redo','-','Find','Replace','-','SelectAll','RemoveFormat'],
 	['SpecialChar','Table','Image','Rule'],
-	['MW_Template','MW_Special','MW_Ref','MW_References','MW_Math'],
+	['MW_Template','MW_Special','MW_Ref','MW_References','MW_Math','Category'],
 	'/',
 	['FontFormat'],
Index: /MediaWiki/trunk/plugins/mediawiki/dialogs/category.html
===================================================================
--- /MediaWiki/trunk/plugins/mediawiki/dialogs/category.html	(revision 3191)
+++ /MediaWiki/trunk/plugins/mediawiki/dialogs/category.html	(revision 3191)
@@ -0,0 +1,395 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<!--
+ * FCKeditor - The text editor for Internet - http://www.fckeditor.net
+ * Copyright (C) 2003-2007 Frederico Caldeira Knabben
+ *
+ * == BEGIN LICENSE ==
+ *
+ * Licensed under the terms of any of the following licenses at your
+ * choice:
+ *
+ *  - GNU General Public License Version 2 or later (the "GPL")
+ *    http://www.gnu.org/licenses/gpl.html
+ *
+ *  - GNU Lesser General Public License Version 2.1 or later (the "LGPL")
+ *    http://www.gnu.org/licenses/lgpl.html
+ *
+ *  - Mozilla Public License Version 1.1 or later (the "MPL")
+ *    http://www.mozilla.org/MPL/MPL-1.1.html
+ *
+ * == END LICENSE ==
+ *
+ * Category dialog window.
+-->
+<html>
+<head>
+<title>Categories</title>
+<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+<meta name="robots" content="noindex, nofollow" />
+<script type="text/javascript">
+var oEditor = window.parent.InnerDialogLoaded();
+var FCK = oEditor.FCK;
+var FCKLang = oEditor.FCKLang;
+var FCKConfig = oEditor.FCKConfig;
+var FCKRegexLib = oEditor.FCKRegexLib;
+var FCKTools = oEditor.FCKTools;
+var FCKBrowserInfo = oEditor.FCKBrowserInfo;
+var EditorDocument = oEditor.FCK.EditorDocument;
+
+document.write( '<script src="' + FCKConfig.BasePath + 'dialog/common/fck_dialog_common.js" type="text/javascript"><\/script>' );
+
+window.onload = function()
+{
+	// Translate the dialog box texts.
+	oEditor.FCKLanguageManager.TranslatePage( document );
+
+	// Load the selected link information (if any).
+	InitSelected();
+	SetSearchMessage( FCKLang.wikiLoadingCategories || 'loading categories...' );
+	oEditor.window.parent.sajax_request_type = 'GET';
+	oEditor.window.parent.sajax_do_call( 'wfSajaxSearchCategoryFCKeditor', [], InitCategoryTree );
+
+	// Activate the "OK" button.
+	window.parent.SetOkButton( true );
+	window.parent.SetAutoSize( true );
+};
+
+var selectedCats;
+
+function InitSelected()
+{
+	selectedCats = new Array();
+	var node = EditorDocument;
+	while ( node )
+	{
+		if ( node.nodeType == 1 && node.tagName.toLowerCase() == 'a' )
+		{
+			// Get the actual Link href.
+			var sHRef = node.getAttribute( '_fcksavedurl' );
+			if ( sHRef == null )
+				sHRef = node.getAttribute( 'href', 2 ) || '';
+			if ( sHRef.StartsWith( 'Category:' ) )
+			{
+				var select = GetE( 'xCategories' );
+				var cat = sHRef.slice( 9 );
+				SelectCategory( cat, -1 );
+			}
+		}
+		node = FCKTools.GetNextNode( node, EditorDocument );
+	}
+}
+
+function SelectCategory( cat, catTreeRow )
+{
+	var select, row = parseInt( catTreeRow );
+	if ( row >= 0 )
+	{
+		select = GetE( 'xWikiResults' );
+		cat = select.options[ row ].text;
+		var lvl = 0;
+		while ( cat.charAt( lvl ) == placeholder )
+			lvl++;
+		cat = cat.slice( lvl );
+		if ( cat.charAt( 0 ) == '[' && cat.charAt( cat.length - 1 ) == ']' )
+			cat = cat.substring( 1, cat.length - 1 );
+	}
+
+	if ( selectedCats[ cat ] )
+		delete selectedCats[ cat ];
+	else
+		selectedCats[ cat ] = cat;
+
+	select = GetE( 'xCategories' );
+
+	while ( select.options.length > 0 )
+		select.remove( 0 );
+
+	for ( cat in selectedCats )
+		FCKTools.AddSelectOption( select, cat, cat );
+}
+
+var catTree;
+
+function InitCategoryTree( result )
+{
+	SetSearchMessage( FCKLang.wikiLnkStartTyping || 'start typing in the above field' );
+
+	catTree = new Object();
+	var levelsHead = new Array( 'root' );
+	var levelsBody = new Array( '' );
+
+	var results = result.responseText.Trim().split( '\n' );
+	var previousLvl = -1;
+	for ( var i = 0 ; i < results.length ; i++ )
+	{
+		var lvl = 0;
+		while ( results[ i ].charAt( lvl ) == ' ' )
+			lvl++;
+		var t = results[ i ].slice( lvl );
+		for ( var j = previousLvl ; j > lvl - 1 ; j-- )
+		{
+
+			if ( levelsBody[ j + 1 ] != '' )
+				catTree[ levelsHead[ j + 1 ] ] = levelsBody[ j + 1 ];
+			delete levelsHead[ j + 1 ];
+			delete levelsBody[ j + 1 ];
+		}
+		if ( lvl > previousLvl )
+			levelsBody[ lvl ] = t;
+		else
+			levelsBody[ lvl ] = levelsBody[ lvl ] + ' ' + t;
+		levelsHead[ lvl + 1 ] = t;
+		levelsBody[ lvl + 1 ] = '';
+		previousLvl = lvl;
+	}
+	for ( var j = previousLvl ; j >= -1 ; j-- )
+	{
+		if ( levelsBody[ j + 1 ] != '' )
+			catTree[ levelsHead[ j + 1 ] ] = levelsBody[ j + 1 ];
+		delete levelsHead[ j + 1 ];
+		delete levelsBody[ j + 1 ];
+	}
+
+	ShowCategoriesSubTree( -1 );
+}
+
+var placeholder = '.';
+
+//draw category subtree
+function ShowCategoriesSubTree( rowInTree )
+{
+	var row = parseInt( rowInTree );
+	var select = GetE( 'xWikiResults' );
+	var root = 'root';
+	var lvl = -1;
+	var prefix = '';
+	if ( row >= 0 )
+	{
+		root = select.options[ row ].text;
+		lvl = 0;
+		while ( root.charAt( lvl ) == placeholder )
+			lvl++;
+		root = root.slice( lvl );
+		if ( root.charAt( 0 ) == '[' && root.charAt( root.length - 1 ) == ']' )
+			root = root.substring( 1, root.length - 1 );
+		prefix = new Array( lvl + 1 + 3 ).join( placeholder );
+	}
+	if ( !catTree[ root ] )
+		return;
+
+	var itCount = select.options.length;
+	var itSkip = row + 1;
+	var opts = new Array();
+	for ( var i = row + 1 ; i < itCount ; i++ )
+	{
+		var t = select.options[ i ].text;
+		var sublvl = 0;
+		while ( t.charAt( sublvl ) == placeholder )
+			sublvl++;
+		if ( sublvl > lvl )
+			itSkip = i + 1;
+		else
+			break;
+	}
+	for ( var i = itCount - 1 ; i > row ; i-- )
+	{
+		var t = select.options[ i ].text;
+		if ( i >= itSkip )
+			opts.push( t );
+		select.remove( i );
+	}
+	if ( itSkip == row + 1 )
+	{
+		var cats = catTree[ root ].split( ' ' );
+
+		for ( var k in cats )
+		{
+			var p = cats[ k ];
+			if ( catTree[ cats[ k ] ] )
+				p = '[' + p + ']';
+			var e = FCKTools.AddSelectOption( select, prefix + p, ++row );
+			if ( catTree[ cats[ k ] ] )
+				e.style.color = '#00f';
+
+		}
+	}
+	for ( var i = opts.length - 1 ; i >= 0 ; i-- )
+	{
+		var e = FCKTools.AddSelectOption( select, opts[ i ], ++row );
+		if ( opts[ i ].indexOf( '[' ) >= 0 )
+			e.style.color = '#00f';
+	}
+
+}
+
+//draw filtered
+function ShowFilteredCategories( filter )
+{
+	var select = GetE( 'xWikiResults' );
+	while ( select.options.length > 0 )
+		select.remove( 0 );
+	var found = new Object();
+	if ( filter.length == 0 )
+	{
+		ShowCategoriesSubTree( -1 );
+		return;
+	}
+	filter = filter.toLowerCase();
+	var row = -1;
+	for ( var folder in catTree )
+	{
+		var cats = catTree[ folder ].split( ' ' );
+		for ( var k in cats )
+		{
+			var p = cats[ k ].toLowerCase();
+			if ( p.indexOf( filter ) >= 0 )
+			{
+				if ( found[ cats[ k ] ] )
+					;
+				else
+				{
+					found[ cats[ k ] ] = cats[ k ];
+					FCKTools.AddSelectOption( select, cats[ k ], ++row );
+				}
+			}
+		}
+	}
+}
+
+function AddNew()
+{
+	var select = GetE( 'txtUrl' );
+	if ( select.value.Trim() )
+		SelectCategory( select.value, -1 );
+	select.value = '';
+}
+
+//#### The OK button was hit.
+function Ok()
+{
+	var nodes = new Array();
+	var node = EditorDocument;
+	var nodeNext;
+	var s = '';
+	var i = 0;
+	while ( node )
+	{
+		nodeNext = FCKTools.GetNextNode( node, EditorDocument );
+		if ( node.nodeType == 1 && node.tagName.toLowerCase() == 'a' )
+		{
+			// Get the actual Link href.
+			var sHRef = node.getAttribute( '_fcksavedurl' );
+			if ( sHRef == null )
+				sHRef = node.getAttribute( 'href', 2 ) || '';
+			if ( sHRef.StartsWith( 'Category:' ) )
+				nodes[ i++ ] = node;
+		}
+		node = nodeNext;
+	}
+	for ( var i = 0 ; i < nodes.length ; i++ )
+		nodes[ i ].parentNode.removeChild( nodes[ i ] );
+
+	for ( var cat in selectedCats )
+		AddCategoryLink( cat );
+
+	CleanUpCategoryLinks();
+
+	return true;
+}
+
+function CleanUpCategoryLinks()
+{
+	var node = EditorDocument;
+	var nodes = [];
+	while ( node )
+	{
+		if ( node.nodeType == 1 && node.tagName.toLowerCase() == 'a' )
+		{
+			// Get the actual Link href.
+			var sHRef = node.getAttribute( '_fcksavedurl' );
+			if ( sHRef == null )
+				sHRef = node.getAttribute( 'href', 2 ) || '';
+			if ( sHRef.StartsWith( 'Category:' ) )
+				nodes.push(node);
+		}
+		node = FCKTools.GetNextNode( node, EditorDocument );
+	}
+
+	for ( var i = 0; i < nodes.length ; i++ )
+		EditorDocument.body.appendChild( nodes[i] );
+}
+
+function AddCategoryLink( cat )
+{
+	var sUri = 'Category:' + cat;
+	var sInnerHtml;
+
+	// If no link is selected, create a new one (it may result in more than one link creation - #220).
+	var aLinks = oEditor.FCK.CreateLink( sUri );
+
+	// If no selection, no links are created, so use the uri as the link text (by dom, 2006-05-26)
+	var aHasSelection = (aLinks.length > 0);
+	if ( !aHasSelection )
+	{
+		sInnerHtml = sUri;
+
+		var oLinkPathRegEx = new RegExp( "//?([^?\"']+)([?].*)?$" );
+		var asLinkPath = oLinkPathRegEx.exec( sUri );
+		if ( asLinkPath != null )
+			sInnerHtml = asLinkPath[ 1 ]; // use matched path
+
+		// Create a new (empty) anchor.
+		aLinks = [ oEditor.FCK.InsertElement( 'a' ) ];
+	}
+
+	oEditor.FCKUndo.SaveUndoStep();
+
+	for ( var i = 0 ; i < aLinks.length ; i++ )
+	{
+		oLink = aLinks[ i ];
+
+		if ( aHasSelection )
+			sInnerHtml = oLink.innerHTML; // Save the innerHTML (IE changes it if it is like an URL).
+
+		oLink.href = sUri;
+		SetAttribute( oLink, '_fcksavedurl', sUri );
+
+		oLink.innerHTML = sInnerHtml; // Set (or restore) the innerHTML
+	}
+
+	return true;
+}
+
+//#### Called while the user types the URL.
+function OnUrlChange()
+{
+	var link = GetE( 'txtUrl' ).value.Trim();
+	ShowFilteredCategories( link );
+	return;
+}
+
+function SetSearchMessage( message )
+{
+	GetE( 'xWikiSearchStatus' ).innerHTML = message;
+}
+</script>
+</head>
+<body scroll="no" style="overflow: hidden">
+<div id="divInfo">
+<div id="divLinkTypeUrl"><span fcklang="wikiSelectedCategories">Selected categories</span><br />
+<select id="xCategories" size="10" style="width: 100%; height: 70px"
+	ondblclick="SelectCategory( this.value,-1);">
+</select><br />
+<span fcklang="wikiSearchCategory">Search category</span><br />
+<input id="txtUrl" style="width: 76%" type="text"
+	onkeyup="OnUrlChange();" /> <input id="btnNew" style="width: 22%"
+	type="button" onclick="AddNew();" value="Add new" fcklang="wikiAddNewCategory"/> <br />
+<span fcklang="wikiCategoryTree">Category tree</span> (<span fcklang="wikiLnkStartTyping" id="xWikiSearchStatus">start typing in the
+above field</span>)<br />
+<select id="xWikiResults" size="10" style="width: 100%; height: 300px"
+	onclick="ShowCategoriesSubTree( this.value );"
+	ondblclick="SelectCategory('', this.value );">
+</select></div>
+</div>
+</body>
+</html>
Index: /MediaWiki/trunk/plugins/mediawiki/fckplugin.js
===================================================================
--- /MediaWiki/trunk/plugins/mediawiki/fckplugin.js	(revision 3190)
+++ /MediaWiki/trunk/plugins/mediawiki/fckplugin.js	(revision 3191)
@@ -83,4 +83,8 @@
 FCKToolbarItems.RegisterItem( 'MW_Special', tbButton ) ;
 
+tbButton = new FCKToolbarButton( 'Category', 'Categories', 'Insert/Edit categories' ) ;
+tbButton.IconPath = FCKConfig.PluginsPath + 'mediawiki/images/tb_icon_category.gif' ;
+FCKToolbarItems.RegisterItem( 'Category', tbButton ) ;
+
 // Override some dialogs.
 FCKCommands.RegisterCommand( 'MW_Template', new FCKDialogCommand( 'MW_Template', ( FCKLang.wikiCmdTemplate || 'Template Properties' ), FCKConfig.PluginsPath + 'mediawiki/dialogs/template.html', 400, 330 ) ) ;
@@ -89,4 +93,5 @@
 FCKCommands.RegisterCommand( 'MW_Special', new FCKDialogCommand( 'MW_Special', ( FCKLang.wikiCmdSpecial || 'Special Tag Properties' ), FCKConfig.PluginsPath + 'mediawiki/dialogs/special.html', 400, 330 ) ) ;
 FCKCommands.RegisterCommand( 'Link', new FCKDialogCommand( 'Link', FCKLang.DlgLnkWindowTitle, FCKConfig.PluginsPath + 'mediawiki/dialogs/link.html', 400, 250 ) ) ;
+FCKCommands.RegisterCommand( 'Category', new FCKDialogCommand( 'Category', ( FCKLang.wikiCmdCategories || 'Categories' ), FCKConfig.PluginsPath + 'mediawiki/dialogs/category.html', 400, 500 ) ) ;
 FCKCommands.RegisterCommand( 'Image', new FCKDialogCommand( 'Image', FCKLang.DlgImgTitle, FCKConfig.PluginsPath + 'mediawiki/dialogs/image.html', 450, 300 ) ) ;
 
Index: /MediaWiki/trunk/plugins/mediawiki/lang/en.js
===================================================================
--- /MediaWiki/trunk/plugins/mediawiki/lang/en.js	(revision 3190)
+++ /MediaWiki/trunk/plugins/mediawiki/lang/en.js	(revision 3191)
@@ -16,5 +16,11 @@
 FCKLang.wikiCmdReference			= 'Reference Properties';
 FCKLang.wikiCmdFormula				= 'Formula';
+FCKLang.wikiCmdCategories			= 'Categories';
 FCKLang.wikiLoadingWikitext		= 'Loading Wikitext. Please wait...';
+FCKLang.wikiLoadingCategories		= 'loading categories...';
+FCKLang.wikiSearchCategory			= 'Search category';
+FCKLang.wikiSelectedCategories		= 'Selected categories';
+FCKLang.wikiAddNewCategory			= 'Add new';
+FCKLang.wikiCategoryTree			= 'Category tree';
 FCKLang.wikiMnuTemplate				= 'Template Properties';
 FCKLang.wikiMnuMagicWord			= 'Modify Magic Word';
Index: /MediaWiki/trunk/plugins/mediawiki/lang/pl.js
===================================================================
--- /MediaWiki/trunk/plugins/mediawiki/lang/pl.js	(revision 3190)
+++ /MediaWiki/trunk/plugins/mediawiki/lang/pl.js	(revision 3191)
@@ -16,5 +16,11 @@
 FCKLang.wikiCmdReference			= 'Właściwości przypisu';
 FCKLang.wikiCmdFormula				= 'Właściwości formuły matematycznej';
+FCKLang.wikiCmdCategories			= 'Kategorie';
 FCKLang.wikiLoadingWikitext		= '&nbsp;Konwertuję format wiki. Proszę czekać...&nbsp;';
+FCKLang.wikiLoadingCategories		= 'Ładuję kategorie...';
+FCKLang.wikiSearchCategory			= 'Szukaj kategorii';
+FCKLang.wikiSelectedCategories		= 'Wybrane kategorie';
+FCKLang.wikiAddNewCategory			= 'Dodaj nową';
+FCKLang.wikiCategoryTree			= 'Drzewo kategorii';
 FCKLang.wikiMnuTemplate				= 'Właściwości szablonu';
 FCKLang.wikiMnuMagicWord			= 'Zmodyfikuj "magiczne słowo"';
