  Patch to MoinMoin 1.3.5 to provide a special character insert bar in
  the page editor.
  By Deron E. Meranda <http://deron.meranda.us/> April 2005
  Released under the GNU General License (GPL) version 2 or later.
  Portions of this code (Javascript portion) are derived from GPL'ed
  code from to the Wikimedia project.

--- MoinMoin/config.py.orig	2005-07-17 10:22:37.000000000 -0400
+++ MoinMoin/config.py	2005-08-17 15:39:56.500526704 -0400
@@ -99,6 +99,172 @@
     "{o}":   (15, 15, 0, "star_off.png"),
 }
 
+# Inserters for page edit.
+#
+# A list of tuples, each describing one insert selector.  Each
+# tuple consists of five string, of the form:
+#    (open, close, sample_body, label, description)
+# The open and close strings define the character(s) to insert before
+# and after the cursor.  The sample_body will be placed between the
+# open and close, unless a region is currently selected in which case
+# the selected region is left alone.  The label is what's shown in
+# the insert bar area, and the description is the tool-tip title.
+#
+# These strings are output as raw HTML, so be sure to use proper
+# escaping.  Also escape single-quotes with backslashes as in \\' and
+# double-quotes as &quot;.  The label and description are optional,
+# and if missing will be constructed from the open, close, and body
+# values.
+#
+# You can supply simple strings rather than tuples in the list to add
+# nonselectable text between inserters (again escape appropriately).
+# An empty value or '\n' will result in a forced line break.
+editor_insert_tags = [
+    ('&#192;','','','','Latin capital A with grave'),
+    ('&#224;','','','','Latin small a with grave'),
+    ('&#200;','','','','Latin capital E with grave'),
+    ('&#232;','','','','Latin small e with grave'),
+    ('&#204;','','','','Latin capital I with grave'),
+    ('&#236;','','','','Latin small i with grave'),
+    ('&#210;','','','','Latin capital O with grave'),
+    ('&#242;','','','','Latin small o with grave'),
+    ('&#217;','','','','Latin capital U with grave'),
+    ('&#249;','','','','Latin small u with grave'),
+    ' ',
+    ('&#193;','','','','Latin capital A with acute'),
+    ('&#225;','','','','Latin small a with acute'),
+    ('&#201;','','','','Latin capital E with acute'),
+    ('&#233;','','','','Latin small e with acute'),
+    ('&#205;','','','','Latin capital I with acute'),
+    ('&#237;','','','','Latin small i with acute'),
+    ('&#211;','','','','Latin capital O with acute'),
+    ('&#243;','','','','Latin small o with acute'),
+    ('&#218;','','','','Latin capital U with acute'),
+    ('&#250;','','','','Latin small u with acute'),
+    ('&#221;','','','','Latin capital Y with acute'),
+    ('&#253;','','','','Latin small y with acute'),
+    ' ',
+    ('&#194;','','','','Latin capital A with circumflex'),
+    ('&#226;','','','','Latin small a with circumflex'),
+    ('&#202;','','','','Latin capital E with circumflex'),
+    ('&#234;','','','','Latin small e with circumflex'),
+    ('&#206;','','','','Latin capital I with circumflex'),
+    ('&#238;','','','','Latin small i with circumflex'),
+    ('&#212;','','','','Latin capital O with circumflex'),
+    ('&#244;','','','','Latin small o with circumflex'),
+    ('&#219;','','','','Latin capital U with circumflex'),
+    ('&#251;','','','','Latin small u with circumflex'),
+    ' ',
+    ('&#196;','','','','Latin capital A with dieresis'),
+    ('&#228;','','','','Latin small a with dieresis'),
+    ('&#203;','','','','Latin capital E with dieresis'),
+    ('&#235;','','','','Latin small e with dieresis'),
+    ('&#207;','','','','Latin capital I with dieresis'),
+    ('&#239;','','','','Latin small i with dieresis'),
+    ('&#214;','','','','Latin capital O with dieresis'),
+    ('&#246;','','','','Latin small o with dieresis'),
+    ('&#220;','','','','Latin capital U with dieresis'),
+    ('&#252;','','','','Latin small u with dieresis'),
+    ('&#376;','','','','Latin capital Y with dieresis'),
+    ('&#255;','','','','Latin small y with dieresis'),
+    ' ',
+    ('&#195;','','','','Latin capital A with tilde'),
+    ('&#227;','','','','Latin small a with tilde'),
+    ('&#209;','','','','Latin capital N with tilde'),
+    ('&#241;','','','','Latin small n with tilde'),
+    ('&#213;','','','','Latin capital O with tilde'),
+    ('&#245;','','','','Latin small o with tilde'),
+    ' ',
+    ('&#231;','','','','Latin small c with cedilla'),
+    ('&#199;','','','','Latin capital C  with cedilla'),
+    ' ',
+    ('&#223;','','','','Latin small sharp s'),
+    ('&#216;','','','','Latin capital O with stroke'),
+    ('&#248;','','','','Latin small o with stroke'),
+    ('&#208;','','','','Latin capital letter eth'),
+    ('&#240;','','','','Latin small letter Eth'),
+    ('&#222;','','','','Latin capital letter Thorn'),
+    ('&#254;','','','','Latin small letter thorn'),
+    ' ',
+    ('&#197;','','','','Latin capital A with ring above'),
+    ('&#229;','','','','Latin small a with ring above'),
+    ' ',
+    ('&#198;','','','','Latin capital ligature AE'),
+    ('&#230;','','','','Latin small ligature ae'),
+    ('&#338;','','','','Latin capital ligature OE'),
+    ('&#339;','','','','Latin small ligature oe'),
+    ('&#64256;','','','','Latin small ligature ff'),
+    ('&#64257;','','','','Latin small ligature fi'),
+    ('&#64258;','','','','Latin small ligature fl'),
+    None,
+    'Pairs:&nbsp;',
+    ('&#8220;','&#8221;','text','&#8220;&nbsp;&#8221; ','double-quoted text'),
+    ' ',
+    ('&#8216;','&#8217;','text','&#8216;&nbsp;&#8217; ','single-quoted text'),
+    ' ',
+    ('&#171;','&#187;','text','&#171;&nbsp;&#187; ','guillemot-quoted text'),
+    ' ',
+    ('[',']','text','[&nbsp;] ','square-bracketed text'),
+    ('{','}','text','{&nbsp;} ','braced text'),
+    ('<','>','text','<&nbsp;> ','angle-bracketed text'),
+    ' &nbsp;Punctuation:&nbsp;',
+    ('&#161;','','','','Inverted exclamation mark'),
+    ('&#191;','','','','Inverted question mark'),
+    ('&#162;','','','','Cent sign'),
+    ('&#163;','','','','Pound sign'),
+    ('&#165;','','','','Yen sign'),
+    ('&#8364;','','','','Euro sign'),
+    ' ',
+    ('&ndash;','','','','en-dash'),
+    ('&mdash;','','','','em-dash'),
+    ' ',
+    ('&nbsp;','','','&#9109;','non-breaking space'),
+    ' ',
+    ('&#8230;','','','','horizontal elipsis'),
+    ('&#8226;','','','','bullet'),
+    ('&copy;','','','','copyright sign'),
+    ('&reg;','','','','registered trademark sign'),
+    ('&#8482;','','','','trademark sign'),
+    ('&#8224;','','','','dagger'),
+    ('&#8225;','','','','double dagger'),
+    ('&#167;','','','','section sign'),
+    ' &nbsp;Math:&nbsp;',
+    ('&#215;','','','','multiplication sign'),
+    ('&#247;','','','','division sign'),
+    ('+','','','','plus sign'),
+    ('&#8722;','','','','minus sign'),
+    ('&#177;','','','','plus-or-minus sign'),
+    ('=','','','','equal to'),
+    ('&#8800;','','','','not equal to'),
+    ('&#8804;','','','','less than or equal to'),
+    ('&#8805;','','','','greater than or equal to'),
+    ('&#188;','','','','vulgar fraction one-quarter'),
+    ('&#189;','','','','vulgar fraction one-half'),
+    ('&#190;','','','','vulgar fraction three-quarters'),
+    ('&#178;','','','','superscript digit two, squared'),
+    ('&#179;','','','','superscript digit three, cubed'),
+    ('&#181;','','','','micro sign, 1/1000000th'),
+    ('&#176;','','','','degree sign'),
+    None,
+    ' Formatting: ',
+    ("\\'\\'","\\'\\'",'italic','<i>italic</i> ','italics text'),
+    ("\\'\\'\\'","\\'\\'\\'",'bold','<b>bold</b> ','boldface text'),
+    ("__","__",'underline','<u>underline</u> ','underlined text'),
+    #("^","^",'superscript','<sup>super</sup>','superscript text'),
+    #(",,",",,",'subscript','<sub>sub</sub>','subscript text'),
+    ("{{{","}}}",'monospace','<tt>monospace</tt> ','monospace text'),
+    ' ',
+    ('[',']','http://example.com/','<u>URL</u> ','URL hyperlink'),
+    ' ',
+    ('[&quot;','&quot;]','Example page','<u>WikiName</u> ','A quoted-stye Wiki Name'),
+    ' ',
+    ('= ',' =','heading','<b>H1</b> ','heading level 1'),
+    ('== ',' ==','heading','<b>H2</b> ','heading level 2'),
+    ('=== ',' ===','heading','<b>H3</b> ','heading level 3'),
+    ('[[BR]]\\n','','','linebreak','force a new line'),
+]
+
+
 # unicode: set the char types (upper, lower, digits, spaces)
 from MoinMoin.util.chartypes import _chartypes
 for key, val in _chartypes.items():
--- MoinMoin/multiconfig.py.orig	2005-07-30 11:58:51.000000000 -0400
+++ MoinMoin/multiconfig.py	2005-08-17 15:42:30.330184261 -0400
@@ -182,6 +182,7 @@
     default_lang = 'en'
     default_markup = 'wiki'
     docbook_html_dir = r"/usr/share/xml/docbook/stylesheet/nwalsh/html/" # correct for debian sarge
+    edit_insert_tags_enabled = 1
     edit_locking = 'warn 10' # None, 'warn <timeout mins>', 'lock <timeout mins>'
     edit_rows = 30
     hosts_deny = []
--- MoinMoin/PageEditor.py.orig	2005-07-27 11:51:39.000000000 -0400
+++ MoinMoin/PageEditor.py	2005-08-17 15:39:56.502526700 -0400
@@ -75,6 +75,89 @@
 </script>
 """
 
+# This javascript was derived from GPL code from the Wikimedia project.
+_insert_tags_js = """
+<script type="text/javascript">
+var clientPC = navigator.userAgent.toLowerCase(); // Get client info
+var is_gecko = ((clientPC.indexOf('gecko')!=-1) && (clientPC.indexOf('spoofer')==-1)
+                && (clientPC.indexOf('khtml') == -1) && (clientPC.indexOf('netscape/7.0')==-1));
+var is_safari = ((clientPC.indexOf('AppleWebKit')!=-1) && (clientPC.indexOf('spoofer')==-1));
+var is_khtml = (navigator.vendor == 'KDE' || ( document.childNodes && !document.all && !navigator.taintEnabled ));
+if (clientPC.indexOf('opera')!=-1) {
+    var is_opera = true;
+    var is_opera_preseven = (window.opera && !document.childNodes);
+    var is_opera_seven = (window.opera && document.childNodes);
+}
+// apply tagOpen/tagClose to selection in textarea,
+// use sampleText instead of selection if there is none
+// copied and adapted from phpBB
+function insertTags(tagOpen, tagClose, sampleText) {
+	var txtarea = document.getElementById('editor-textarea');
+	// var txtarea = document.editform.wpTextbox1;
+        if( ! txtarea ) {
+         alert('Can not locate text area control');
+        }
+	else if(document.selection  && !is_gecko) {
+		// IE
+		var theSelection = document.selection.createRange().text;
+		if(!theSelection) { theSelection=sampleText;}
+		txtarea.focus();
+		if(theSelection.charAt(theSelection.length - 1) == " "){// exclude ending space char, if any
+			theSelection = theSelection.substring(0, theSelection.length - 1);
+			document.selection.createRange().text = tagOpen + theSelection + tagClose + " ";
+		} else {
+			document.selection.createRange().text = tagOpen + theSelection + tagClose;
+		}
+	} else if(txtarea.selectionStart || txtarea.selectionStart == '0') {
+        	// Mozilla
+ 		var startPos = txtarea.selectionStart;
+		var endPos = txtarea.selectionEnd;
+		var scrollTop=txtarea.scrollTop;
+		var myText = (txtarea.value).substring(startPos, endPos);
+		if(!myText) { myText=sampleText;}
+		if(myText.charAt(myText.length - 1) == " "){ // exclude ending space char, if any
+			subst = tagOpen + myText.substring(0, (myText.length - 1)) + tagClose + " ";
+		} else {
+			subst = tagOpen + myText + tagClose;
+		}
+		txtarea.value = txtarea.value.substring(0, startPos) + subst +
+		  txtarea.value.substring(endPos, txtarea.value.length);
+		txtarea.focus();
+
+		var cPos=startPos+(tagOpen.length+myText.length+tagClose.length);
+		txtarea.selectionStart=cPos;
+		txtarea.selectionEnd=cPos;
+		txtarea.scrollTop=scrollTop;
+	} else {
+		// All others
+		var copy_alertText=alertText;
+		var re1=new RegExp("\\$1","g");
+		var re2=new RegExp("\\$2","g");
+		copy_alertText=copy_alertText.replace(re1,sampleText);
+		copy_alertText=copy_alertText.replace(re2,tagOpen+sampleText+tagClose);
+		var text;
+		if (sampleText) {
+			text=prompt(copy_alertText);
+		} else {
+			text="";
+		}
+		if(!text) { text=sampleText;}
+		text=tagOpen+text+tagClose;
+		document.infoform.infobox.value=text;
+		// in Safari this causes scrolling
+		if(!is_safari) {
+			txtarea.focus();
+		}
+		noOverwrite=true;
+	}
+	// reposition cursor if possible
+	if (txtarea && txtarea.createTextRange) {
+		txtarea.caretPos = document.selection.createRange().duplicate();
+	}
+}
+</script>
+"""
+
 
 #############################################################################
 ### PageEditor - Edit pages
@@ -269,19 +352,23 @@
         status = [msg for msg in status if msg]
         status = ' '.join(status)
         status = Status(self.request, content=status)
-        
-        wikiutil.send_title(self.request,
-            title % {'pagename': self.split_title(self.request),},
-            page=self,
-            pagename=self.page_name, msg=status,
-            body_onload=self.lock.locktype and 'countdown()' or '', # broken / bug in Mozilla 1.5, when using #preview
-            html_head=self.lock.locktype and (
+
+	html_head = self.lock.locktype and (
                 _countdown_js % {
                      'lock_timeout': lock_timeout,
                      'lock_expire': lock_expire,
                      'lock_mins': lock_mins,
                      'lock_secs': lock_secs,
                     }) or ''
+        if self.request.cfg.edit_insert_tags_enabled:
+            html_head += _insert_tags_js
+
+        wikiutil.send_title(self.request,
+            title % {'pagename': self.split_title(self.request),},
+            page=self,
+            pagename=self.page_name, msg=status,
+            body_onload=self.lock.locktype and 'countdown()' or '', # broken / bug in Mozilla 1.5, when using #preview
+            html_head=html_head
         )
         
         self.request.write(self.request.formatter.startContent("content"))
@@ -365,6 +452,30 @@
 
         self.request.write('</p>')
 
+        # Character tags insert selection box
+        if self.request.cfg.edit_insert_tags_enabled:
+            self.request.write('<div id="editor-insert-tags">')
+            self.request.write(_("Insert: "))
+            for tag in config.editor_insert_tags:
+                if not tag or tag=='\n':
+                    self.request.write('<br />')
+                    continue
+                elif tag==' ':
+                    self.request.write('&nbsp; ')
+                    continue
+                elif type(tag) is type(' ') or type(tag) is type(u' '):
+                    self.request.write(tag)
+                    continue
+                tag_open, tag_close, tag_body, tag_name, tag_description = tag
+                if not tag_name:
+                    tag_name = tag_open + ' ' + tag_close
+                if not tag_description:
+                    tag_description = tag_name
+                self.request.write("<a href=\"javascript:insertTags('%s','%s','%s')\" title=\"%s\">%s</a>" \
+                                   % (tag_open,tag_close,tag_body,tag_description,tag_name) )
+            self.request.write('</div>')
+        
+        # Change description
         self.request.write("<p>", _("Optional comment about this change"),
             '<br><input id="editor-comment" type="text" name="comment" value="%s" maxlength="80"></p>' % (
                 wikiutil.escape(kw.get('comment', ''), 1), ))
--- wiki/htdocs/classic/css/screen.css.orig	2005-06-26 11:43:05.000000000 -0400
+++ wiki/htdocs/classic/css/screen.css	2005-08-17 15:39:56.503526697 -0400
@@ -261,6 +261,21 @@
 	background-color: white;
 }
 
+div#editor-insert-tags {
+	border: 2px solid #c0c0c0;
+	padding-left: 4pt; padding-right: 4pt;
+	background-color: #e5e5e5; color: black;
+	font-size: 9pt;
+}
+#editor-insert-tags a {
+	text-decoration: none;
+	padding-left: 0.2pt; padding-right: 0.2pt;
+}
+#editor-insert-tags a:hover {
+	background-color: yellow; color: black;
+	text-decoration: underline;
+}
+
 /* We use here dumb css1 ids becuase of IE suckiness */
 #editor-textarea, #editor-comment {
     width: 100%;
--- wiki/htdocs/modern/css/screen.css.orig	2005-06-26 11:43:05.000000000 -0400
+++ wiki/htdocs/modern/css/screen.css	2005-08-17 15:39:56.504526695 -0400
@@ -276,6 +276,21 @@
 	padding: 10px 30px 20px 30px;
 }
 
+div#editor-insert-tags {
+	border: 1px solid #5588bb;
+	padding-left: 4pt; padding-right: 4pt;
+	background-color: #f0f8ff; color: black;
+	font-size: 9pt;
+}
+#editor-insert-tags a {
+	text-decoration: none;
+	padding-left: 0.2pt; padding-right: 0.2pt;
+}
+#editor-insert-tags a:hover {
+	background-color: yellow; color: black;
+	text-decoration: underline;
+}
+
 /* We use here dumb css1 ids because of IE suckiness */
 #editor-textarea, #editor-comment {
     width: 100%;
--- wiki/htdocs/rightsidebar/css/screen.css.orig	2005-07-31 06:27:41.000000000 -0400
+++ wiki/htdocs/rightsidebar/css/screen.css	2005-08-17 15:44:16.821950730 -0400
@@ -211,6 +211,22 @@
     width: 100%;
 }
 
+div#editor-insert-tags {
+	border: 1px solid #9c9c9c;
+	padding-left: 4pt; padding-right: 4pt;
+	background-color: #F0ECE6; color: black;
+	font-size: 9pt;
+}
+#editor-insert-tags a {
+	text-decoration: none;
+	padding-left: 0.2pt; padding-right: 0.2pt;
+}
+#editor-insert-tags a:hover {
+	background-color: yellow; color: black;
+	text-decoration: underline;
+}
+
+
 /* We use here dumb css1 ids because of IE suckiness */
 #editor-textarea, #editor-comment {
     width: 100%;
--- wiki/underlay/pages/HelpOnConfiguration/revisions/00000001.orig	2005-08-04 15:15:29.000000000 -0400
+++ wiki/underlay/pages/HelpOnConfiguration/revisions/00000001	2005-08-17 15:45:29.692792544 -0400
@@ -185,6 +185,7 @@
 || default_lang || 'en' || Default language for user interface and page content, see HelpOnLanguages!||
 || default_markup || 'wiki' || Default page parser / format (name of module in `MoinMoin.parser`)  ||
 || docbook_html_dir || '...' || Path to the docbook html directory (optional, used by the docbook parser). The default value is correct for Debian Sarge. ||
+|| edit_insert_tags_enabled || 1 || Show a special character insert helper in the page editor (requires Javascript) ||
 || edit_locking || 'warn 10' || Editor locking policy: `None`, `'warn <timeout in minutes>'`, or `'lock <timeout in minutes>'` ||
 || edit_rows || 30 || Default height of the edit box ||
 || hosts_deny || `[]` || List of denied IPs; if an IP ends with a dot, it denies a whole subnet (class A, B or C) ||
