diff -Naur wordpress-2004-08-30/wp-admin/admin-functions.php wordpress-smart-tags/wp-admin/admin-functions.php
--- wordpress-2004-08-30/wp-admin/admin-functions.php	Sun Aug  1 01:28:56 2004
+++ wordpress-smart-tags/wp-admin/admin-functions.php	Sun Sep  5 00:37:06 2004
@@ -87,10 +87,14 @@
 }
 
 function write_nested_categories($categories) {
+	if( get_settings('use_category_tagging') ) {
+		$disabled = 'disabled="1"';
+	}
+	
  foreach($categories as $category) {
    echo '<label for="category-', $category['cat_ID'], '" class="selectit"><input value="', $category['cat_ID'],
      '" type="checkbox" name="post_category[]" id="category-', $category['cat_ID'], '"',
-     ($category['checked'] ? ' checked="checked"' : ""), '/> ', htmlspecialchars($category['cat_name']), "</label>\n";
+     ($category['checked'] ? ' checked="checked"' : ""), "$disabled  /> ", htmlspecialchars($category['cat_name']), "</label>\n";
 
    if(isset($category['children'])) {
      echo "\n<span class='cat-nest'>\n";
@@ -354,6 +358,34 @@
 			");
 	}
 } // add_meta
+
+// save a post's categories
+//    adds support for tag style categories - kellan 8/27
+// if $mode is categories then $categories is a list of categories IDs
+// if $mode is tags then $categories is a list of strings
+function add_categories($post_ID, $categories, $mode='categories') {
+	global $wpdb;
+
+	if ( $mode == 'tags' ) {
+		$categories = tags_to_categories($categories);
+	}
+	
+	if (!$categories) $categories[] = 1;
+	
+	foreach ($categories as $post_category) {
+		// Double check it's not there already
+		$exists = $wpdb->get_row(
+			"SELECT * FROM $wpdb->post2cat 
+			WHERE post_id = $post_ID AND category_id = $post_category");
+		if (!$exists) { 
+			$wpdb->query("
+				INSERT INTO $wpdb->post2cat (post_id, category_id)
+				VALUES ($post_ID, $post_category)");
+		}
+	} // end foreach
+}
+
+
 
 function delete_meta($mid) {
 	global $wpdb;
diff -Naur wordpress-2004-08-30/wp-admin/admin-header.php wordpress-smart-tags/wp-admin/admin-header.php
--- wordpress-2004-08-30/wp-admin/admin-header.php	Sat Aug 21 14:25:19 2004
+++ wordpress-smart-tags/wp-admin/admin-header.php	Sun Sep  5 00:36:02 2004
@@ -3,6 +3,7 @@
 require_once('../wp-config.php');
 require_once(ABSPATH . '/wp-admin/auth.php');
 require(ABSPATH . '/wp-admin/admin-functions.php');
+require_once(ABSPATH . '/wp-admin/categories-as-tags-functions.php');
 
 $dogs = $wpdb->get_results("SELECT * FROM $wpdb->categories");
 foreach ($dogs as $catt) {
diff -Naur wordpress-2004-08-30/wp-admin/autocomplete.js wordpress-smart-tags/wp-admin/autocomplete.js
--- wordpress-2004-08-30/wp-admin/autocomplete.js	Wed Dec 31 16:00:00 1969
+++ wordpress-smart-tags/wp-admin/autocomplete.js	Sun Sep  5 00:27:45 2004
@@ -0,0 +1,238 @@
+<!--
+
+// provides auto-completion, and suggestion of related categories
+// when used in conjunction with categories-as-tags WP mod
+// lifted wholesale by Kellan (http://laughingmeme.org)
+
+// stolen from:
+// avar.icio.us - del.icio.us tag autocompletion and suggestion
+// by Yoz Grahame - yoz@yoz.com
+// v0.2 - 23/07/04
+
+// The autocompletion code within is a modified version of the
+// code created and explained by Nicholas C. Zakas in this
+// article:
+// http://www.sitepoint.com/article/1220/1
+// Thanks!
+
+var isOpera = navigator.userAgent.indexOf('Opera') > -1;
+var isIE = navigator.userAgent.indexOf('MSIE') > 1 && !isOpera;
+var isMoz = navigator.userAgent.indexOf('Mozilla/5.') == 0 && !isOpera;
+var sugLimit = 10; // maximum number of tag suggestions
+
+var seperator = ' ';
+
+function textboxSelect (oTextbox, iStart, iEnd) {
+
+   switch(arguments.length) {
+       case 1:
+           oTextbox.select();
+           break;
+
+       case 2:
+           iEnd = oTextbox.value.length;
+           /* falls through */
+           
+       case 3:          
+           if (isIE) {
+               var oRange = oTextbox.createTextRange();
+               oRange.moveStart("character", iStart);
+               oRange.moveEnd("character", -oTextbox.value.length + iEnd);      
+               oRange.select();                                              
+           } else if (isMoz){
+               oTextbox.setSelectionRange(iStart, iEnd);
+           }                    
+   }
+
+   oTextbox.focus();
+}
+
+function textboxReplaceSelect (oTextbox, sText) {
+
+   if (isIE) {
+       var oRange = document.selection.createRange();
+       oRange.text = sText;
+       oRange.collapse(true);
+       oRange.select();                                
+   } else if (isMoz) {
+       var iStart = oTextbox.selectionStart;
+       oTextbox.value = oTextbox.value.substring(0, iStart) + sText + oTextbox.value.substring(oTextbox.selectionEnd, oTextbox.value.length);
+       oTextbox.setSelectionRange(iStart + sText.length, iStart + sText.length);
+   }
+
+   oTextbox.focus();
+}
+
+function autocompleteMatch (sText, tagdata) {
+
+    if (sText == "")
+    {
+        return null;
+    }
+    
+   for (i in tagdata) {
+       if (i.indexOf(sText) == 0) {
+           return i;
+       }
+   }
+
+   return null;
+
+}
+
+function autocomplete(oTextbox, oEvent, tagdata) {
+
+   switch (oEvent.keyCode) {
+       case 38: //up arrow  
+       case 40: //down arrow
+       case 37: //left arrow
+       case 39: //right arrow
+       case 33: //page up  
+       case 34: //page down  
+       case 36: //home  
+       case 35: //end                  
+       case 16: //shift  
+       case 17: //ctrl  
+       case 18: //alt  
+       case 20: //caps lock
+       case 46: //delete
+       case 8: //backspace             
+           return true;
+           break;
+
+        // just complete the word and let me type the next.
+        // add a space and move cursor to end if word is being autocompleted
+        // otherwise, just pass event through
+       case 13: //enter  
+       case 9: //tab  
+       case 27: //esc  
+           var s = oTextbox.value;
+           var iLen = s.length;
+           
+           // modifications: we only want to match/change the last word, not the whole value
+           var word = s.substring(s.lastIndexOf(seperator)+1,iLen);
+
+           var sMatch = autocompleteMatch(word, tagdata);
+
+           if (sMatch != null) {
+               oTextbox.value += seperator;
+               textboxSelect(oTextbox, iLen+1);
+           }
+           return false;
+           break;
+
+       default:
+           textboxReplaceSelect(oTextbox, String.fromCharCode(isIE ? oEvent.keyCode : oEvent.charCode));
+           var s = oTextbox.value;
+           var iLen = s.length;
+           
+           // modifications: we only want to match/change the last word, not the whole value
+           var word = s.substring(s.lastIndexOf(seperator)+1,iLen);
+
+           var sMatch = autocompleteMatch(word, tagdata);
+
+           if (sMatch != null) {
+               oTextbox.value = s.substring(0,s.lastIndexOf(seperator)+1)+sMatch;
+               textboxSelect(oTextbox, iLen, oTextbox.value.length);
+           }
+            
+            makeSuggestions(oTextbox);
+            
+           return false;
+   }
+}
+
+// linked words to add to textbox
+
+function makeSuggestions(oTextbox)
+{
+    var sugbox = document.getElementById("suggest");
+    // delete the container div, thus losing all suggestions
+    var sugdiv = document.getElementById("sugdiv");
+    sugbox.removeChild(sugdiv);
+    // now bring it back
+    sugdiv = document.createElement("div");
+    sugdiv.setAttribute("id","sugdiv");
+    sugbox.appendChild(sugdiv);
+
+    var s = oTextbox.value;
+    var iLen = s.length;
+    
+    // split the current string into tags and compile a list of top suspects
+    
+    var taglist = s.split(seperator);
+    taglist.sort();
+    
+    var tagscores = new Object(); // object holding suggested tags and their totalled scores
+    
+    // loop through all the tags and total up the scores
+    for (i=0;i<taglist.length;i++) 
+    {
+        tt = taglist[i]; // top-level tag
+        tagscores[tt] = -1;   // we don't want this tag coming up as a suggestion
+        if (!tagdata[tt]) // does the tag exist?
+        {
+            continue; // if not, skip
+        }
+        
+        for (st in tagdata[tt])    // loop through second-level tags
+        {
+            if(tagscores[st] && tagscores[st] != -1)   // seen the tag yet, and can be added?
+            {
+                tagscores[st] += tagdata[tt][st];
+            }
+            else if(!tagscores[st])       // tag not yet seen
+            {
+                tagscores[st] = tagdata[tt][st];
+            }
+        }
+    }
+
+    // make a list of all possible suggestions
+    var suggs = new Array();  // comprehendez, savvy, understand, do you hear?
+    for (i in tagscores)
+    {
+        if (tagscores[i] > 0) {
+            suggs.push(i);
+        }
+    }
+    
+    // sort by score
+    suggs.sort( function (a,b){
+       if (tagscores[a] > tagscores[b] )
+            return -1;
+       else return 1;
+        }
+    );
+    // take the first 10 (or however many - sugLimit is set at the top of the code)
+    suggs = suggs.slice(0,sugLimit);
+    
+    // and sort into alphabetical order (you can safely comment this out)
+    suggs.sort();   
+
+    // then print them out in the right place
+    for (i=0;i<suggs.length;i++)
+    {
+        var word = suggs[i];
+        var link = document.createElement("a");
+        link.setAttribute("href","javascript:addWord('"+word+"')");
+        var t = document.createTextNode(word);
+        link.appendChild(t);
+        sugdiv.appendChild(link);
+        ss = seperator;
+        
+        // COMMENT OUT THE NEXT LINE TO REMOVE TAG SCORE DISPLAY
+        ss += "("+tagscores[word]+") "; 
+        
+        var spc = document.createTextNode(ss); // a blank space
+        sugdiv.appendChild(spc);
+    }   
+}
+
+function addWord(word)
+{
+    var oTextbox = document.forms["post"].post_category_tags;
+    oTextbox.value += seperator + word;
+    makeSuggestions(oTextbox);
+}
+
diff -Naur wordpress-2004-08-30/wp-admin/categories-as-tags-functions.php wordpress-smart-tags/wp-admin/categories-as-tags-functions.php
--- wordpress-2004-08-30/wp-admin/categories-as-tags-functions.php	Wed Dec 31 16:00:00 1969
+++ wordpress-smart-tags/wp-admin/categories-as-tags-functions.php	Sun Sep  5 00:40:30 2004
@@ -0,0 +1,113 @@
+<?php
+
+function edit_category_tags_html () {
+	$form_category_tags = '';
+	if (get_settings('use_category_tagging')) {
+		if (get_settings('use_category_tagging_autocomplete') ) {
+			$tag_data = get_related_tag_array();
+			$load_autocomplete_js = '<script src="autocomplete.js" type="text/javascript"></script>';
+			$load_autocomplete_js .= "<script>var tagdata = { $tag_data };</script>";
+			$autocomplete_canvas = '<div id="suggest"><div id="sugdiv"></div></div>';
+			$cat_actions = 'onkeypress="x = autocomplete(this, event, tagdata);makeSuggestions(this);return x" onChange="makeSuggestions(this)"';
+			$browser_autocomplete_off = ' autocomplete="off" ';
+		}
+		$form_category_tags =
+			$load_autocomplete_js .
+			'<fieldset id="tagsdiv" style="clear: both;"><legend><a href="#">Category Tags</a></legend>' . 	
+			'<div><input type="text" name="post_category_tags" style="width: 95%" ' .
+			'tabindex="1" value="' . $edited_category_tags . '" id="category_tags" ' . $cat_actions . $browser_autocomplete_off . '/>' . 
+			'</div>' . $autocomplete_canvas . '</fieldset>';
+	}
+	return $form_category_tags;
+}
+
+function get_related_tag_array ($as_js=true) {
+	global $wpdb;
+	
+	$sql = 
+		"SELECT c1.cat_name, c1.cat_ID, c2.cat_ID as related_id, c2.cat_name as related_cat,
+  		COUNT(w2.post_id) as weight 
+		FROM wp_post2cat w1, wp_post2cat w2, wp_categories as c1, wp_categories as c2 
+		WHERE w1.post_id = w2.post_id and w1.category_id <> w2.category_id and
+  			c1.cat_ID = w1.category_id and c2.cat_ID = w2.category_id
+		GROUP BY related_id, c1.cat_ID 
+		ORDER BY c1.cat_name, weight;";
+		
+	$results = $wpdb->get_results($sql);
+	
+	$related = array();
+	foreach ($results as $row) {
+		if (!isset($related[$row->cat_name])) {
+			$related[$row->cat_name] = array();
+		}
+		if ( !$as_js ) {
+			$related[$row->cat_name][$row->related_cat] = $row->weight;
+		}
+		else {
+			$related[$row->cat_name][] = sprintf("'%s':%d", $row->related_cat, $row->weight);
+		}
+	}
+	
+	if ( $as_js ) {
+		$tags = array();
+		foreach ( $related as $cat => $related_cats ) {
+			$tags[] = sprintf("'%s':{%s}", $cat, implode(',', $related_cats));
+		}
+		$tag_data = implode(",\n", $tags);
+		return $tag_data;
+	}
+	else {
+		return $related;
+	}
+}
+
+// look up (and create) category ids from a list of tags
+// - kellan 8/27
+function tags_to_categories ($cats) {
+	global $wpdb;
+
+	if ( !is_array($cats) and $cats) {
+		$cats_match = preg_split("/\s+/", $cats);
+		$cats = $cats_match;
+	}
+	
+	if ( !count($cats) ) return array();
+	
+	# have i mentioned i would kill for a map function in php?
+	$cat_strs = array();
+	foreach ( $cats as $c ) {
+		$c = trim($c);
+		$c = $wpdb->escape($c); $c = "'$c'"; 
+		$cat_strs[] = $c;
+	}
+
+	$cat_sql = join(',', $cat_strs);
+	$sql = "SELECT cat_ID,cat_name from $wpdb->categories WHERE cat_name IN ($cat_sql)";
+	$results = $wpdb->get_results($sql);
+	
+	$cat_ids 	= array();
+	$cat_found	= array();
+	if ($results) {
+		foreach ( $results as $row ) {
+			$cat_ids[]		= $row->cat_ID;
+			$cat_found[] 	= $row->cat_name;
+		}
+	}
+	
+	$cat_unknown = array_diff($cats, $cat_found);
+	if ( count($cat_unknown) ) {
+		$sql =  "INSERT INTO $wpdb->categories (cat_name, category_nicename, category_description) 
+			VALUES ( '%s', '%s', '%s')";
+		
+		foreach ( $cat_unknown as $new_cat ) {
+			$nice_cat = sanitize_title($new_cat);
+			$pretty_cat = ucwords( str_replace('_', ' ', $nice_cat) );
+			$wpdb->query(sprintf($sql, $new_cat, $nice_cat, $pretty_cat));
+			$cat_ids[] = mysql_insert_id($wpdb->dbh);
+		}
+	}
+	return $cat_ids;
+}
+
+
+?>
\ No newline at end of file
diff -Naur wordpress-2004-08-30/wp-admin/edit-form-advanced.php wordpress-smart-tags/wp-admin/edit-form-advanced.php
--- wordpress-2004-08-30/wp-admin/edit-form-advanced.php	Sun Aug  1 01:19:34 2004
+++ wordpress-smart-tags/wp-admin/edit-form-advanced.php	Sun Sep  5 00:27:45 2004
@@ -40,6 +40,16 @@
 } else {
 	$form_trackback = '';
 }
+
+$form_category_tags = '';
+if (get_settings('use_category_tagging')) {
+	$form_category_tags =
+		'<fieldset id="tagsdiv" style="clear: both;"><legend><a href="#">Category Tags</a></legend>' . 	
+		'<div><input type="text" name="post_category_tags" style="width: 95%" ' .
+		'tabindex="1" value="' . $edited_category_tags . '" id="category_tags" />' . 
+		'</div></fieldset>';
+}
+
 $saveasdraft = '<input name="save" type="submit" id="save" tabindex="6" value="' . __('Save and Continue Editing') . '" />';
 
 if (empty($post_status)) $post_status = 'draft';
@@ -96,6 +106,8 @@
     </fieldset>
 
 <br />
+<?php echo $form_category_tags ?>
+
 <fieldset style="clear:both">
 <legend><a href="http://wordpress.org/docs/reference/post/#excerpt" title="<?php _e('Help with excerpts') ?>"><?php _e('Excerpt') ?></a></legend>
 <div><textarea rows="1" cols="40" name="excerpt" tabindex="4" id="excerpt"><?php echo $excerpt ?></textarea></div>
diff -Naur wordpress-2004-08-30/wp-admin/edit-form.php wordpress-smart-tags/wp-admin/edit-form.php
--- wordpress-2004-08-30/wp-admin/edit-form.php	Sun Aug  1 01:19:34 2004
+++ wordpress-smart-tags/wp-admin/edit-form.php	Sun Sep  5 00:27:45 2004
@@ -20,6 +20,8 @@
 	$form_trackback = '';
 }
 
+$form_category_tags = edit_category_tags_html();
+
 $saveasdraft = '';
 
 
@@ -46,17 +48,19 @@
 </script>
 
 <div id="poststuff">
-    <fieldset id="titlediv">
+  <fieldset id="titlediv">
       <legend><a href="http://wordpress.org/docs/reference/post/#title" title="<?php _e('Help on titles') ?>"><?php _e('Title') ?></a></legend> 
 	  <div><input type="text" name="post_title" size="30" tabindex="1" value="<?php echo $edited_post_title; ?>" id="title" /></div>
-    </fieldset>
+  </fieldset>
 
-    <fieldset id="categorydiv">
+ <fieldset id="categorydiv">
       <legend><a href="http://wordpress.org/docs/reference/post/#category" title="<?php _e('Help on categories') ?>"><?php _e('Categories') ?></a></legend> 
 	  <div><?php dropdown_categories($default_post_cat); ?></div>
-    </fieldset>
+ </fieldset>
 
 <br />
+<?php echo $form_category_tags ?>
+
 <fieldset id="postdiv">
     <legend><a href="http://wordpress.org/docs/reference/post/#post" title="<?php _e('Help with post field') ?>"><?php _e('Post') ?></a></legend>
 		<div id="quicktags">
diff -Naur wordpress-2004-08-30/wp-admin/post.php wordpress-smart-tags/wp-admin/post.php
--- wordpress-2004-08-30/wp-admin/post.php	Sat Aug 28 01:54:44 2004
+++ wordpress-smart-tags/wp-admin/post.php	Sun Sep  5 00:39:45 2004
@@ -53,7 +53,6 @@
 		$excerpt = balanceTags($_POST['excerpt']);
 		$excerpt = format_to_post($excerpt);
 		$post_title = $_POST['post_title'];
-		$post_categories = $_POST['post_category'];
 		if(get_settings('use_geo_positions')) {
 			$latstr = $_POST['post_latf'];
 			$lonstr = $_POST['post_lonf'];
@@ -148,24 +147,16 @@
 
 	header("Location: $location"); // Send user on their way while we keep working
 
-
-	// Insert categories
-	// Check to make sure there is a category, if not just set it to some default
-	if (!$post_categories) $post_categories[] = 1;
-	foreach ($post_categories as $post_category) {
-		// Double check it's not there already
-		$exists = $wpdb->get_row("SELECT * FROM $wpdb->post2cat WHERE post_id = $post_ID AND category_id = $post_category");
-
-		 if (!$exists && $result) { 
-			$wpdb->query("
-			INSERT INTO $wpdb->post2cat
-			(post_id, category_id)
-			VALUES
-			($post_ID, $post_category)
-			");
-		}
+	if (get_settings('use_category_tagging')) {
+		$post_categories = $_POST['post_category_tags'];
+		$mode = 'tags';
 	}
-
+	else {
+		$post_categories = $_POST['post_category'];
+		$mode = 'categories';
+	}
+	
+	add_categories($post_ID, $post_categories, $mode);
 	add_meta($post_ID);
 	
 	if (isset($sleep_after_edit) && $sleep_after_edit > 0) {
@@ -236,6 +227,13 @@
 		$pinged = $postdata->pinged;
 		$post_name = $postdata->post_name;
 
+		$checked_categories = $wpdb->get_col("
+     		SELECT cat_name
+     		FROM $wpdb->categories, $wpdb->post2cat
+     		WHERE $wpdb->post2cat.category_id = cat_ID AND $wpdb->post2cat.post_id = '$post_ID'");
+     	
+     	$edited_category_tags = implode(' ', $checked_categories );
+     	
         if ($post_status == 'static') {
             include('edit-page-form.php');
         } else {
@@ -375,22 +373,29 @@
 		WHERE ID = $post_ID ");
 
 
-	// Now it's category time!
-	// First the old categories
-	$old_categories = $wpdb->get_col("SELECT category_id FROM $wpdb->post2cat WHERE post_id = $post_ID");
+	// using tags?  clear old tags, and re-insert
+	if (get_settings('use_category_tagging')) {
+		$post_categories = $_POST['post_category_tags'];
+		$wpdb->query("DELETE FROM $wpdb->post2cat WHERE post_id = $post_ID");
+		add_categories($post_ID, $post_categories, 'tags');	
+	}
+	else {
+		// Now it's category time!
+		// First the old categories
+		$old_categories = $wpdb->get_col("SELECT category_id FROM $wpdb->post2cat WHERE post_id = $post_ID");
 	
-	// Delete any?
-	foreach ($old_categories as $old_cat) {
-		if (!in_array($old_cat, $post_categories)) // If a category was there before but isn't now
-			$wpdb->query("DELETE FROM $wpdb->post2cat WHERE category_id = $old_cat AND post_id = $post_ID LIMIT 1");
-	}
+		// Delete any?
+		foreach ($old_categories as $old_cat) {
+			if (!in_array($old_cat, $post_categories)) // If a category was there before but isn't now
+				$wpdb->query("DELETE FROM $wpdb->post2cat WHERE category_id = $old_cat AND post_id = $post_ID LIMIT 1");
+		}
 	
-	// Add any?
-	foreach ($post_categories as $new_cat) {
-		if (!in_array($new_cat, $old_categories))
+		// Add any?
+		foreach ($post_categories as $new_cat) {
+			if (!in_array($new_cat, $old_categories))
 			$wpdb->query("INSERT INTO $wpdb->post2cat (post_id, category_id) VALUES ($post_ID, $new_cat)");
+		}
 	}
-	
 	if (isset($sleep_after_edit) && $sleep_after_edit > 0) {
 		sleep($sleep_after_edit);
 	}
