]> code.citadel.org Git - citadel.git/blob - webcit/static/richtext.js
* Message entry using Kevin Roth's excellent cross-platform richtext
[citadel.git] / webcit / static / richtext.js
1 // Cross-Browser Rich Text Editor
2 // http://www.kevinroth.com/rte/demo.htm
3 // Written by Kevin Roth (kevin@NOSPAMkevinroth.com - remove NOSPAM)
4
5 //init variables
6 var isRichText = false;
7 var rng;
8 var currentRTE;
9 var allRTEs = "";
10
11 var isIE;
12 var isGecko;
13 var isSafari;
14 var isKonqueror;
15
16 var imagesPath;
17 var includesPath;
18 var cssFile;
19
20
21 function initRTE(imgPath, incPath, css) {
22         //set browser vars
23         var ua = navigator.userAgent.toLowerCase();
24         isIE = ((ua.indexOf("msie") != -1) && (ua.indexOf("opera") == -1) && (ua.indexOf("webtv") == -1)); 
25         isGecko = (ua.indexOf("gecko") != -1);
26         isSafari = (ua.indexOf("safari") != -1);
27         isKonqueror = (ua.indexOf("konqueror") != -1);
28         
29         //check to see if designMode mode is available
30         if (document.getElementById && document.designMode && !isSafari && !isKonqueror) {
31                 isRichText = true;
32         }
33         
34         if (!isIE) document.captureEvents(Event.MOUSEOVER | Event.MOUSEOUT | Event.MOUSEDOWN | Event.MOUSEUP);
35         document.onmouseover = raiseButton;
36         document.onmouseout  = normalButton;
37         document.onmousedown = lowerButton;
38         document.onmouseup   = raiseButton;
39         
40         //set paths vars
41         imagesPath = imgPath;
42         includesPath = incPath;
43         cssFile = css;
44         
45         if (isRichText) document.writeln('<style type="text/css">@import "' + includesPath + 'rte.css";</style>');
46         
47         //for testing standard textarea, uncomment the following line
48         //isRichText = false;
49 }
50
51 function writeRichText(rte, html, width, height, buttons, readOnly) {
52         if (isRichText) {
53                 if (allRTEs.length > 0) allRTEs += ";";
54                 allRTEs += rte;
55                 writeRTE(rte, html, width, height, buttons, readOnly);
56         } else {
57                 writeDefault(rte, html, width, height, buttons, readOnly);
58         }
59 }
60
61 function writeDefault(rte, html, width, height, buttons, readOnly) {
62         if (!readOnly) {
63                 document.writeln('<textarea name="' + rte + '" id="' + rte + '" style="width: ' + width + 'px; height: ' + height + 'px;">' + html + '</textarea>');
64         } else {
65                 document.writeln('<textarea name="' + rte + '" id="' + rte + '" style="width: ' + width + 'px; height: ' + height + 'px;" readonly>' + html + '</textarea>');
66         }
67 }
68
69 function raiseButton(e) {
70         if (isIE) {
71                 var el = window.event.srcElement;
72         } else {
73                 var el= e.target;
74         }
75         
76         className = el.className;
77         if (className == 'rteImage' || className == 'rteImageLowered') {
78                 el.className = 'rteImageRaised';
79         }
80 }
81
82 function normalButton(e) {
83         if (isIE) {
84                 var el = window.event.srcElement;
85         } else {
86                 var el= e.target;
87         }
88         
89         className = el.className;
90         if (className == 'rteImageRaised' || className == 'rteImageLowered') {
91                 el.className = 'rteImage';
92         }
93 }
94
95 function lowerButton(e) {
96         if (isIE) {
97                 var el = window.event.srcElement;
98         } else {
99                 var el= e.target;
100         }
101         
102         className = el.className;
103         if (className == 'rteImage' || className == 'rteImageRaised') {
104                 el.className = 'rteImageLowered';
105         }
106 }
107
108 function writeRTE(rte, html, width, height, buttons, readOnly) {
109         if (readOnly) buttons = false;
110         
111         //adjust minimum table widths
112         if (isIE) {
113                 if (buttons && (width < 600)) width = 600;
114                 var tablewidth = width;
115         } else {
116                 if (buttons && (width < 500)) width = 500;
117                 var tablewidth = width + 4;
118         }
119         
120         if (buttons == true) {
121                 document.writeln('<table class="rteBack" cellpadding=2 cellspacing=0 id="Buttons1_' + rte + '" width="' + tablewidth + '">');
122                 document.writeln('      <tr>');
123                 document.writeln('              <td>');
124                 document.writeln('                      <select id="formatblock_' + rte + '" onchange="Select(\'' + rte + '\', this.id);">');
125                 document.writeln('                              <option value="">[Style]</option>');
126                 document.writeln('                              <option value="<p>">Paragraph</option>');
127                 document.writeln('                              <option value="<h1>">Heading 1 <h1></option>');
128                 document.writeln('                              <option value="<h2>">Heading 2 <h2></option>');
129                 document.writeln('                              <option value="<h3>">Heading 3 <h3></option>');
130                 document.writeln('                              <option value="<h4>">Heading 4 <h4></option>');
131                 document.writeln('                              <option value="<h5>">Heading 5 <h5></option>');
132                 document.writeln('                              <option value="<h6>">Heading 6 <h6></option>');
133                 document.writeln('                              <option value="<address>">Address <ADDR></option>');
134                 document.writeln('                              <option value="<pre>">Formatted <pre></option>');
135                 document.writeln('                      </select>');
136                 document.writeln('              </td>');
137                 document.writeln('              <td>');
138                 document.writeln('                      <select id="fontname_' + rte + '" onchange="Select(\'' + rte + '\', this.id)">');
139                 document.writeln('                              <option value="Font" selected>[Font]</option>');
140                 document.writeln('                              <option value="Arial, Helvetica, sans-serif">Arial</option>');
141                 document.writeln('                              <option value="Courier New, Courier, mono">Courier New</option>');
142                 document.writeln('                              <option value="Times New Roman, Times, serif">Times New Roman</option>');
143                 document.writeln('                              <option value="Verdana, Arial, Helvetica, sans-serif">Verdana</option>');
144                 document.writeln('                      </select>');
145                 document.writeln('              </td>');
146                 document.writeln('              <td>');
147                 document.writeln('                      <select unselectable="on" id="fontsize_' + rte + '" onchange="Select(\'' + rte + '\', this.id);">');
148                 document.writeln('                              <option value="Size">[Size]</option>');
149                 document.writeln('                              <option value="1">1</option>');
150                 document.writeln('                              <option value="2">2</option>');
151                 document.writeln('                              <option value="3">3</option>');
152                 document.writeln('                              <option value="4">4</option>');
153                 document.writeln('                              <option value="5">5</option>');
154                 document.writeln('                              <option value="6">6</option>');
155                 document.writeln('                              <option value="7">7</option>');
156                 document.writeln('                      </select>');
157                 document.writeln('              </td>');
158                 document.writeln('              <td width="100%">');
159                 document.writeln('              </td>');
160                 document.writeln('      </tr>');
161                 document.writeln('</table>');
162                 document.writeln('<table class="rteBack" cellpadding="0" cellspacing="0" id="Buttons2_' + rte + '" width="' + tablewidth + '">');
163                 document.writeln('      <tr>');
164                 document.writeln('              <td><img class="rteImage" src="' + imagesPath + 'bold.gif" width="25" height="24" alt="Bold" title="Bold" onClick="FormatText(\'' + rte + '\', \'bold\', \'\')"></td>');
165                 document.writeln('              <td><img class="rteImage" src="' + imagesPath + 'italic.gif" width="25" height="24" alt="Italic" title="Italic" onClick="FormatText(\'' + rte + '\', \'italic\', \'\')"></td>');
166                 document.writeln('              <td><img class="rteImage" src="' + imagesPath + 'underline.gif" width="25" height="24" alt="Underline" title="Underline" onClick="FormatText(\'' + rte + '\', \'underline\', \'\')"></td>');
167                 document.writeln('              <td><img class="rteVertSep" src="' + imagesPath + 'blackdot.gif" width="1" height="20" border="0" alt=""></td>');
168                 document.writeln('              <td><img class="rteImage" src="' + imagesPath + 'left_just.gif" width="25" height="24" alt="Align Left" title="Align Left" onClick="FormatText(\'' + rte + '\', \'justifyleft\', \'\')"></td>');
169                 document.writeln('              <td><img class="rteImage" src="' + imagesPath + 'centre.gif" width="25" height="24" alt="Center" title="Center" onClick="FormatText(\'' + rte + '\', \'justifycenter\', \'\')"></td>');
170                 document.writeln('              <td><img class="rteImage" src="' + imagesPath + 'right_just.gif" width="25" height="24" alt="Align Right" title="Align Right" onClick="FormatText(\'' + rte + '\', \'justifyright\', \'\')"></td>');
171                 document.writeln('              <td><img class="rteImage" src="' + imagesPath + 'justifyfull.gif" width="25" height="24" alt="Justify Full" title="Justify Full" onclick="FormatText(\'' + rte + '\', \'justifyfull\', \'\')"></td>');
172                 document.writeln('              <td><img class="rteVertSep" src="' + imagesPath + 'blackdot.gif" width="1" height="20" border="0" alt=""></td>');
173                 document.writeln('              <td><img class="rteImage" src="' + imagesPath + 'hr.gif" width="25" height="24" alt="Horizontal Rule" title="Horizontal Rule" onClick="FormatText(\'' + rte + '\', \'inserthorizontalrule\', \'\')"></td>');
174                 document.writeln('              <td><img class="rteVertSep" src="' + imagesPath + 'blackdot.gif" width="1" height="20" border="0" alt=""></td>');
175                 document.writeln('              <td><img class="rteImage" src="' + imagesPath + 'numbered_list.gif" width="25" height="24" alt="Ordered List" title="Ordered List" onClick="FormatText(\'' + rte + '\', \'insertorderedlist\', \'\')"></td>');
176                 document.writeln('              <td><img class="rteImage" src="' + imagesPath + 'list.gif" width="25" height="24" alt="Unordered List" title="Unordered List" onClick="FormatText(\'' + rte + '\', \'insertunorderedlist\', \'\')"></td>');
177                 document.writeln('              <td><img class="rteVertSep" src="' + imagesPath + 'blackdot.gif" width="1" height="20" border="0" alt=""></td>');
178                 document.writeln('              <td><img class="rteImage" src="' + imagesPath + 'outdent.gif" width="25" height="24" alt="Outdent" title="Outdent" onClick="FormatText(\'' + rte + '\', \'outdent\', \'\')"></td>');
179                 document.writeln('              <td><img class="rteImage" src="' + imagesPath + 'indent.gif" width="25" height="24" alt="Indent" title="Indent" onClick="FormatText(\'' + rte + '\', \'indent\', \'\')"></td>');
180                 document.writeln('              <td><div id="forecolor_' + rte + '"><img class="rteImage" src="' + imagesPath + 'textcolor.gif" width="25" height="24" alt="Text Color" title="Text Color" onClick="FormatText(\'' + rte + '\', \'forecolor\', \'\')"></div></td>');
181                 document.writeln('              <td><div id="hilitecolor_' + rte + '"><img class="rteImage" src="' + imagesPath + 'bgcolor.gif" width="25" height="24" alt="Background Color" title="Background Color" onClick="FormatText(\'' + rte + '\', \'hilitecolor\', \'\')"></div></td>');
182                 document.writeln('              <td><img class="rteVertSep" src="' + imagesPath + 'blackdot.gif" width="1" height="20" border="0" alt=""></td>');
183                 document.writeln('              <td><img class="rteImage" src="' + imagesPath + 'hyperlink.gif" width="25" height="24" alt="Insert Link" title="Insert Link" onClick="FormatText(\'' + rte + '\', \'createlink\')"></td>');
184                 document.writeln('              <td><img class="rteImage" src="' + imagesPath + 'image.gif" width="25" height="24" alt="Add Image" title="Add Image" onClick="AddImage(\'' + rte + '\')"></td>');
185                 if (isIE) {
186                         document.writeln('              <td><img class="rteImage" src="' + imagesPath + 'spellcheck.gif" width="25" height="24" alt="Spell Check" title="Spell Check" onClick="checkspell()"></td>');
187                 }
188 //              document.writeln('              <td><img class="rteVertSep" src="' + imagesPath + 'blackdot.gif" width="1" height="20" border="0" alt=""></td>');
189 //              document.writeln('              <td><img class="rteImage" src="' + imagesPath + 'cut.gif" width="25" height="24" alt="Cut" title="Cut" onClick="FormatText(\'' + rte + '\', \'cut\')"></td>');
190 //              document.writeln('              <td><img class="rteImage" src="' + imagesPath + 'copy.gif" width="25" height="24" alt="Copy" title="Copy" onClick="FormatText(\'' + rte + '\', \'copy\')"></td>');
191 //              document.writeln('              <td><img class="rteImage" src="' + imagesPath + 'paste.gif" width="25" height="24" alt="Paste" title="Paste" onClick="FormatText(\'' + rte + '\', \'paste\')"></td>');
192 //              document.writeln('              <td><img class="rteVertSep" src="' + imagesPath + 'blackdot.gif" width="1" height="20" border="0" alt=""></td>');
193 //              document.writeln('              <td><img class="rteImage" src="' + imagesPath + 'undo.gif" width="25" height="24" alt="Undo" title="Undo" onClick="FormatText(\'' + rte + '\', \'undo\')"></td>');
194 //              document.writeln('              <td><img class="rteImage" src="' + imagesPath + 'redo.gif" width="25" height="24" alt="Redo" title="Redo" onClick="FormatText(\'' + rte + '\', \'redo\')"></td>');
195                 document.writeln('              <td width="100%"></td>');
196                 document.writeln('      </tr>');
197                 document.writeln('</table>');
198         }
199         document.writeln('<iframe id="' + rte + '" name="' + rte + '" width="' + width + 'px" height="' + height + 'px"></iframe>');
200         if (!readOnly) document.writeln('<br /><input type="checkbox" id="chkSrc' + rte + '" onclick="toggleHTMLSrc(\'' + rte + '\');" />&nbsp;View Source');
201         document.writeln('<iframe width="154" height="104" id="cp' + rte + '" src="' + includesPath + 'palette.htm" marginwidth="0" marginheight="0" scrolling="no" style="visibility:hidden; display: none; position: absolute;"></iframe>');
202         document.writeln('<input type="hidden" id="hdn' + rte + '" name="' + rte + '" value="">');
203         document.getElementById('hdn' + rte).value = html;
204         enableDesignMode(rte, html, readOnly);
205 }
206
207 function enableDesignMode(rte, html, readOnly) {
208         var frameHtml = "<html id=\"" + rte + "\">\n";
209         frameHtml += "<head>\n";
210         //to reference your stylesheet, set href property below to your stylesheet path and uncomment
211         if (cssFile.length > 0) {
212                 frameHtml += "<link media=\"all\" type=\"text/css\" href=\"" + cssFile + "\" rel=\"stylesheet\">\n";
213         } else {
214                 frameHtml += "<style>\n";
215                 frameHtml += "body {\n";
216                 frameHtml += "  background: #FFFFFF;\n";
217                 frameHtml += "  margin: 0px;\n";
218                 frameHtml += "  padding: 0px;\n";
219                 frameHtml += "}\n";
220                 frameHtml += "</style>\n";
221         }
222         frameHtml += "</head>\n";
223         frameHtml += "<body>\n";
224         frameHtml += html + "\n";
225         frameHtml += "</body>\n";
226         frameHtml += "</html>";
227         
228         if (document.all) {
229                 var oRTE = frames[rte].document;
230                 oRTE.open();
231                 oRTE.write(frameHtml);
232                 oRTE.close();
233                 if (!readOnly) oRTE.designMode = "On";
234         } else {
235                 try {
236                         if (!readOnly) document.getElementById(rte).contentDocument.designMode = "on";
237                         try {
238                                 var oRTE = document.getElementById(rte).contentWindow.document;
239                                 oRTE.open();
240                                 oRTE.write(frameHtml);
241                                 oRTE.close();
242                                 if (isGecko && !readOnly) {
243                                         //attach a keyboard handler for gecko browsers to make keyboard shortcuts work
244                                         oRTE.addEventListener("keypress", kb_handler, true);
245                                 }
246                         } catch (e) {
247                                 alert("Error preloading content.");
248                         }
249                 } catch (e) {
250                         //gecko may take some time to enable design mode.
251                         //Keep looping until able to set.
252                         if (isGecko) {
253                                 setTimeout("enableDesignMode('" + rte + "', '" + html + "', " + readOnly + ");", 10);
254                         } else {
255                                 return false;
256                         }
257                 }
258         }
259 }
260
261 function updateRTEs() {
262         var vRTEs = allRTEs.split(";");
263         for (var i = 0; i < vRTEs.length; i++) {
264                 updateRTE(vRTEs[i]);
265         }
266 }
267
268 function updateRTE(rte) {
269         if (!isRichText) return;
270         
271         //set message value
272         var oHdnMessage = document.getElementById('hdn' + rte);
273         var oRTE = document.getElementById(rte);
274         var readOnly = false;
275         
276         //check for readOnly mode
277         if (document.all) {
278                 if (frames[rte].document.designMode != "On") readOnly = true;
279         } else {
280                 if (document.getElementById(rte).contentDocument.designMode != "on") readOnly = true;
281         }
282         
283         if (isRichText && !readOnly) {
284                 //if viewing source, switch back to design view
285                 if (document.getElementById("chkSrc" + rte).checked) {
286                         document.getElementById("chkSrc" + rte).checked = false;
287                         toggleHTMLSrc(rte);
288                 }
289                 
290                 if (oHdnMessage.value == null) oHdnMessage.value = "";
291                 if (document.all) {
292                         oHdnMessage.value = frames[rte].document.body.innerHTML;
293                 } else {
294                         oHdnMessage.value = oRTE.contentWindow.document.body.innerHTML;
295                 }
296                 
297                 //if there is no content (other than formatting) set value to nothing
298                 if (stripHTML(oHdnMessage.value.replace("&nbsp;", " ")) == "" 
299                         && oHdnMessage.value.toLowerCase().search("<hr") == -1
300                         && oHdnMessage.value.toLowerCase().search("<img") == -1) oHdnMessage.value = "";
301                 //fix for gecko
302                 if (escape(oHdnMessage.value) == "%3Cbr%3E%0D%0A%0D%0A%0D%0A") oHdnMessage.value = "";
303         }
304 }
305
306 function toggleHTMLSrc(rte) {
307         //contributed by Bob Hutzel (thanks Bob!)
308         var oRTE;
309         if (document.all) {
310                 oRTE = frames[rte].document;
311         } else {
312                 oRTE = document.getElementById(rte).contentWindow.document;
313         }
314         
315         if (document.getElementById("chkSrc" + rte).checked) {
316                 document.getElementById("Buttons1_" + rte).style.visibility = "hidden";
317                 document.getElementById("Buttons2_" + rte).style.visibility = "hidden";
318                 if (document.all) {
319                         oRTE.body.innerText = oRTE.body.innerHTML;
320                 } else {
321                         var htmlSrc = oRTE.createTextNode(oRTE.body.innerHTML);
322                         oRTE.body.innerHTML = "";
323                         oRTE.body.appendChild(htmlSrc);
324                 }
325         } else {
326                 document.getElementById("Buttons1_" + rte).style.visibility = "visible";
327                 document.getElementById("Buttons2_" + rte).style.visibility = "visible";
328                 if (document.all) {
329                         //fix for IE
330                         var output = escape(oRTE.body.innerText);
331                         output = output.replace("%3CP%3E%0D%0A%3CHR%3E", "%3CHR%3E");
332                         output = output.replace("%3CHR%3E%0D%0A%3C/P%3E", "%3CHR%3E");
333                         
334                         oRTE.body.innerHTML = unescape(output);
335                 } else {
336                         var htmlSrc = oRTE.body.ownerDocument.createRange();
337                         htmlSrc.selectNodeContents(oRTE.body);
338                         oRTE.body.innerHTML = htmlSrc.toString();
339                 }
340         }
341 }
342
343 //Function to format text in the text box
344 function FormatText(rte, command, option) {
345         var oRTE;
346         if (document.all) {
347                 oRTE = frames[rte];
348                 
349                 //get current selected range
350                 var selection = oRTE.document.selection; 
351                 if (selection != null) {
352                         rng = selection.createRange();
353                 }
354         } else {
355                 oRTE = document.getElementById(rte).contentWindow;
356                 
357                 //get currently selected range
358                 var selection = oRTE.getSelection();
359                 rng = selection.getRangeAt(selection.rangeCount - 1).cloneRange();
360         }
361         
362         try {
363                 if ((command == "forecolor") || (command == "hilitecolor")) {
364                         //save current values
365                         parent.command = command;
366                         currentRTE = rte;
367                         
368                         //position and show color palette
369                         buttonElement = document.getElementById(command + '_' + rte);
370                         // Ernst de Moor: Fix the amount of digging parents up, in case the RTE editor itself is displayed in a div.
371                         document.getElementById('cp' + rte).style.left = getOffsetLeft(buttonElement, 4) + "px";
372                         document.getElementById('cp' + rte).style.top = (getOffsetTop(buttonElement, 4) + buttonElement.offsetHeight + 4) + "px";
373                         if (document.getElementById('cp' + rte).style.visibility == "hidden") {
374                                 document.getElementById('cp' + rte).style.visibility = "visible";
375                                 document.getElementById('cp' + rte).style.display = "inline";
376                         } else {
377                                 document.getElementById('cp' + rte).style.visibility = "hidden";
378                                 document.getElementById('cp' + rte).style.display = "none";
379                         }
380                 } else if (command == "createlink") {
381                         var szURL = prompt("Enter a URL:", "");
382                         try {
383                                 //ignore error for blank urls
384                                 oRTE.document.execCommand("Unlink", false, null);
385                                 oRTE.document.execCommand("CreateLink", false, szURL);
386                         } catch (e) {
387                                 //do nothing
388                         }
389                 } else {
390                         oRTE.focus();
391                         oRTE.document.execCommand(command, false, option);
392                         oRTE.focus();
393                 }
394         } catch (e) {
395                 alert(e);
396         }
397 }
398
399 //Function to set color
400 function setColor(color) {
401         var rte = currentRTE;
402         var oRTE;
403         if (document.all) {
404                 oRTE = frames[rte];
405         } else {
406                 oRTE = document.getElementById(rte).contentWindow;
407         }
408         
409         var parentCommand = parent.command;
410         if (document.all) {
411                 //retrieve selected range
412                 var sel = oRTE.document.selection; 
413                 if (parentCommand == "hilitecolor") parentCommand = "backcolor";
414                 if (sel != null) {
415                         var newRng = sel.createRange();
416                         newRng = rng;
417                         newRng.select();
418                 }
419         }
420         oRTE.focus();
421         oRTE.document.execCommand(parentCommand, false, color);
422         oRTE.focus();
423         document.getElementById('cp' + rte).style.visibility = "hidden";
424         document.getElementById('cp' + rte).style.display = "none";
425 }
426
427 //Function to add image
428 function AddImage(rte) {
429         var oRTE;
430         if (document.all) {
431                 oRTE = frames[rte];
432                 
433                 //get current selected range
434                 var selection = oRTE.document.selection; 
435                 if (selection != null) {
436                         rng = selection.createRange();
437                 }
438         } else {
439                 oRTE = document.getElementById(rte).contentWindow;
440                 
441                 //get currently selected range
442                 var selection = oRTE.getSelection();
443                 rng = selection.getRangeAt(selection.rangeCount - 1).cloneRange();
444         }
445         
446         imagePath = prompt('Enter Image URL:', 'http://');                              
447         if ((imagePath != null) && (imagePath != "")) {
448                 oRTE.focus();
449                 oRTE.document.execCommand('InsertImage', false, imagePath);
450                 oRTE.focus();
451         }
452 }
453
454 //function to perform spell check
455 function checkspell() {
456         try {
457                 var tmpis = new ActiveXObject("ieSpell.ieSpellExtension");
458                 tmpis.CheckAllLinkedDocuments(document);
459         }
460         catch(exception) {
461                 if(exception.number==-2146827859) {
462                         if (confirm("ieSpell not detected.  Click Ok to go to download page."))
463                                 window.open("http://www.iespell.com/download.php","DownLoad");
464                 } else {
465                         alert("Error Loading ieSpell: Exception " + exception.number);
466                 }
467         }
468 }
469
470 // Ernst de Moor: Fix the amount of digging parents up, in case the RTE editor itself is displayed in a div.
471 function getOffsetTop(elm, parents_up) {
472         var mOffsetTop = elm.offsetTop;
473         var mOffsetParent = elm.offsetParent;
474         
475         if(!parents_up) {
476                 parents_up = 10000; // arbitrary big number
477         }
478         while(parents_up>0 && mOffsetParent) {
479                 mOffsetTop += mOffsetParent.offsetTop;
480                 mOffsetParent = mOffsetParent.offsetParent;
481                 parents_up--;
482         }
483         
484         return mOffsetTop;
485 }
486
487 // Ernst de Moor: Fix the amount of digging parents up, in case the RTE editor itself is displayed in a div.
488 function getOffsetLeft(elm, parents_up) {
489         var mOffsetLeft = elm.offsetLeft;
490         var mOffsetParent = elm.offsetParent;
491         
492         if(!parents_up) {
493                 parents_up = 10000; // arbitrary big number
494         }
495         while(parents_up>0 && mOffsetParent) {
496                 mOffsetLeft += mOffsetParent.offsetLeft;
497                 mOffsetParent = mOffsetParent.offsetParent;
498                 parents_up--;
499         }
500         
501         return mOffsetLeft;
502 }
503
504 function Select(rte, selectname) {
505         var oRTE;
506         if (document.all) {
507                 oRTE = frames[rte];
508                 
509                 //get current selected range
510                 var selection = oRTE.document.selection; 
511                 if (selection != null) {
512                         rng = selection.createRange();
513                 }
514         } else {
515                 oRTE = document.getElementById(rte).contentWindow;
516                 
517                 //get currently selected range
518                 var selection = oRTE.getSelection();
519                 rng = selection.getRangeAt(selection.rangeCount - 1).cloneRange();
520         }
521         
522         var idx = document.getElementById(selectname).selectedIndex;
523         // First one is always a label
524         if (idx != 0) {
525                 var selected = document.getElementById(selectname).options[idx].value;
526                 var cmd = selectname.replace('_' + rte, '');
527                 oRTE.focus();
528                 oRTE.document.execCommand(cmd, false, selected);
529                 oRTE.focus();
530                 document.getElementById(selectname).selectedIndex = 0;
531         }
532 }
533
534 function kb_handler(evt) {
535         var rte = evt.target.id;
536         
537         //contributed by Anti Veeranna (thanks Anti!)
538         if (evt.ctrlKey) {
539                 var key = String.fromCharCode(evt.charCode).toLowerCase();
540                 var cmd = '';
541                 switch (key) {
542                         case 'b': cmd = "bold"; break;
543                         case 'i': cmd = "italic"; break;
544                         case 'u': cmd = "underline"; break;
545                 };
546
547                 if (cmd) {
548                         FormatText(rte, cmd, true);
549                         //evt.target.ownerDocument.execCommand(cmd, false, true);
550                         // stop the event bubble
551                         evt.preventDefault();
552                         evt.stopPropagation();
553                 }
554         }
555 }
556
557 function docChanged (evt) {
558         alert('changed');
559 }
560
561 function stripHTML(oldString) {
562         var newString = oldString.replace(/(<([^>]+)>)/ig,"");
563         
564         //replace carriage returns and line feeds
565    newString = newString.replace(/\r\n/g," ");
566    newString = newString.replace(/\n/g," ");
567    newString = newString.replace(/\r/g," ");
568         
569         //trim string
570         newString = trim(newString);
571         
572         return newString;
573 }
574
575 function trim(inputString) {
576    // Removes leading and trailing spaces from the passed string. Also removes
577    // consecutive spaces and replaces it with one space. If something besides
578    // a string is passed in (null, custom object, etc.) then return the input.
579    if (typeof inputString != "string") return inputString;
580    var retValue = inputString;
581    var ch = retValue.substring(0, 1);
582         
583    while (ch == " ") { // Check for spaces at the beginning of the string
584       retValue = retValue.substring(1, retValue.length);
585       ch = retValue.substring(0, 1);
586    }
587    ch = retValue.substring(retValue.length-1, retValue.length);
588         
589    while (ch == " ") { // Check for spaces at the end of the string
590       retValue = retValue.substring(0, retValue.length-1);
591       ch = retValue.substring(retValue.length-1, retValue.length);
592    }
593         
594         // Note that there are two spaces in the string - look for multiple spaces within the string
595    while (retValue.indexOf("  ") != -1) {
596                 // Again, there are two spaces in each of the strings
597       retValue = retValue.substring(0, retValue.indexOf("  ")) + retValue.substring(retValue.indexOf("  ")+1, retValue.length);
598    }
599    return retValue; // Return the trimmed string back to the user
600 }