Fixed a problem where it would jump up and down the page when
[cocanwiki.git] / html / _js / editor.js
1 /* Javascript for OCAMLWIKI.
2  * Copyright (C) 2004 Merjis Ltd.
3  * $Id: editor.js,v 1.4 2004/10/26 17:50:23 rich Exp $
4  */
5
6 var delay = 1000;               // Delay in milliseconds before updating.
7
8 function update_preview (content_id, preview_id)
9 {
10   // Updating is quite expensive, so only update after a period of apparent
11   // inactivity.
12   var preview = document.getElementById (preview_id);
13   if (preview.timer) clearTimeout (preview.timer);
14   preview.timer =
15     setTimeout ("update_preview_now ('" + content_id + "', '" +
16                 preview_id + "')", delay);
17 }
18
19 function update_preview_now (content_id, preview_id)
20 {
21   // Remove the timer.
22   var preview = document.getElementById (preview_id);
23   if (preview.timer) preview.timer = null;
24
25   // Get the Wiki-markup content from the content textarea.
26   var content = document.getElementById (content_id).value;
27
28   // Send the Wiki-markup to the server to be turned into XHTML.
29   var http = document.all ?
30     new ActiveXObject ('Microsoft.XMLHTTP') : new XMLHttpRequest ();
31   if (http)
32     {
33       http.open ('POST', '/_bin/preview.cmo', false);
34       http.setRequestHeader ('Content-Type',
35                              'application/x-www-form-urlencoded');
36       http.send ('content=' + encodeURIComponent (content));
37
38       var xhtml = http.responseText;
39
40       // Next line fails with my copy of IE if the text contains a
41       // link (ie. <a href...>).  It is unclear why.  The error is:
42       // "Unknown runtime error"
43       preview.innerHTML = xhtml;
44     }
45 }
46
47 // Initialise the edit_buttons for a section.
48 // We do this in Javascript so that non-Javascript users won't see
49 // any buttons (they wouldn't be functional for them anyway).
50 function init_edit_buttons (div_id, content_id, preview_id)
51 {
52   // HTML for the edit buttons.
53   var args = "'" + content_id + "', '" + preview_id + "'";
54   var edit_buttons_html =
55     "<a onmousedown=\"link(" + args + "); return false;\" title=\"Select some text and click this to make a link\"><u>Link</u></a>" +
56     "<span class=\"spacer\"></span>" +
57     "<a onmousedown=\"bold(" + args + "); return false;\" title=\"Select some text and click this to make it bold\"><strong>Bold</strong></a>" +
58     "<a onmousedown=\"italic(" + args + "); return false;\" title=\"Select some text and click this to make it italic\"><em>Italic</em></a>" +
59     "<a onmousedown=\"strikeout(" + args + "); return false;\" title=\"Select some text and click this to strike through it\"><s>Strike</s></a>" +
60     "<span class=\"spacer\"></span>" +
61     "<a onmousedown=\"bullet(" + args + "); return false;\" title=\"Select lines of text and click this to make it a bullet list\"><big>&#x2219;&#8212;</big></a>" +
62     "<a onmousedown=\"numbered(" + args + "); return false;\" title=\"Select lines of text and click this to make it a numbered list\">1&#8212;</a>" +
63     "<br/>"
64     ;
65   var div = document.getElementById (div_id);
66   div.innerHTML = edit_buttons_html;
67 }
68
69 function replace_selection (content_id, fn, warning)
70 {
71   var textarea = document.getElementById (content_id);
72
73   if (textarea.setSelectionRange) { // Mozilla
74     var start = textarea.selectionStart;
75     var end = textarea.selectionEnd;
76     if (start != end) {
77       var text = textarea.value.substring (start, end);
78       var replacement = fn (text);
79       textarea.value = textarea.value.substring (0, start) +
80         replacement + textarea.value.substring (end);
81     } else
82       if (warning) alert (warning);
83   } else if (document.selection) { // IE
84     var range = document.selection.createRange ();
85     if (range.parentElement () == textarea && range.text != "") {
86       var text = range.text;
87       var len = text.length;
88
89       // IE6 bug workaround: If the end of the selection is the end of
90       // a line, then we must remember to append a \n character after
91       // doing the replacement.  Note the incredible lengths we have to
92       // go to here because IE is a piece of shit.
93       var dup = range.duplicate ();
94       dup.moveEnd ("character", 2);
95       append =
96         dup.text.substring (dup.text.length-3, dup.text.length-1) == "\r\n";
97
98       // Replace \r\n with just \n.
99       text = text.replace ("\r\n", "\n");
100
101       // IE's selections often include a trailing space or carriage
102       // return.  Ignore this whitespace when calling the replacement
103       // function.
104       var replacement;
105       if ((ar = text.match (/(\s+)$/))) {
106         var ws = ar[1];
107         text = text.substring (0, len - ws.length);
108         replacement = fn (text);
109         replacement = replacement + ws;
110       } else
111         replacement = fn (range.text);
112
113       // See IE bug workaround above.
114       if (append) replacement += "\n";
115
116       range.text = replacement;
117     } else
118         if (warning) alert (warning);
119   }
120 }
121
122 function link (content_id, preview_id)
123 {
124   var fn = function (text) {
125     if (ar = text.match (/^\[\[(.*)\]\]$/))
126       return ar[1];
127     else
128       return "[[" + text + "]]";
129   };
130   var warning = "Select an area of text first.";
131   replace_selection (content_id, fn, warning);
132   update_preview_now (content_id, preview_id);
133 }
134
135 function bold (content_id, preview_id)
136 {
137   var fn = function (text) {
138     if (ar = text.match (/^<b>(.*)<\/b>$/))
139       return ar[1];
140     else
141       return "<b>" + text + "</b>";
142   };
143   var warning = "Select an area of text first.";
144   replace_selection (content_id, fn, warning);
145   update_preview_now (content_id, preview_id);
146 }
147
148 function italic (content_id, preview_id)
149 {
150   var fn = function (text) {
151     if (ar = text.match (/^<i>(.*)<\/i>$/))
152       return ar[1];
153     else
154       return "<i>" + text + "</i>";
155   };
156   var warning = "Select an area of text first.";
157   replace_selection (content_id, fn, warning);
158   update_preview_now (content_id, preview_id);
159 }
160
161 function strikeout (content_id, preview_id)
162 {
163   var fn = function (text) {
164     if (ar = text.match (/^<s>(.*)<\/s>$/))
165       return ar[1];
166     else
167       return "<s>" + text + "</s>";
168   };
169   var warning = "Select an area of text first.";
170   replace_selection (content_id, fn, warning);
171   update_preview_now (content_id, preview_id);
172 }
173
174 function bullet (content_id, preview_id)
175 {
176   var fn = function (text) {
177     if (text.match (/^\*/))
178       return text.replace (/^\* /gm, "");
179     else
180       return "* " + text.replace (/\n/g, "\n* ");
181   };
182   var warning = "Select some whole lines of text first.";
183   replace_selection (content_id, fn, warning);
184   update_preview_now (content_id, preview_id);
185 }
186
187 function numbered (content_id, preview_id)
188 {
189   var fn = function (text) {
190     if (text.match (/^#/))
191       return text.replace (/^# /gm, "");
192     else
193       return "# " + text.replace (/\n/g, "\n# ");
194   };
195   var warning = "Select some whole lines of text first.";
196   replace_selection (content_id, fn, warning);
197   update_preview_now (content_id, preview_id);
198 }