Aug 25, 2000
Not too long ago I sent a request to the WWWDEV mailing list for help building a WYSIWYG text editor for the web. While I received a number of helpful responses, nobody was able to find exactly what I was looking for. But happily, I was able to unlock the secrets and create the tool I wanted. Now I want to share.
The idea behind my text editor is that it should not be necessary to type out all those HTML tags when entering data into an online form. Indeed, the form should work a lot like a word processor, so that when you type in italics, for example, you see italics in the text editing window. Indeed, in the ideal world, it would accept keystroke shortcuts, such as ctl/i for italics and ctl/b for bold.Just like, say, Microsoft word.
My text editor is now complete and being used to accept new messages in my discussion list software. You are invited to come to my list and have a look; here is the link: http://www.munimall.net/scripts/downes/clist/publiclist.cgi - you can also read a lot more about how to build such a tool for yourself (it's not that hard).
Some caveats, however. The WYSIWYG editor only works for Internet Explorer 4 or 5. It has only been tested on Internet Explorer 5. Netscape viewers will see a rather plain HTML forms textbox. It ought to work on a Mac, but I have no idea whether it will or not; let me know (if you can't type into the form, email me at mailto: sdownes@ualberta.ca
OK, that was the introduction. Now let me get into the specifics of how it was done. And even before that, credit where credit is due. The concept and much of the implementation was done by SiteExperts's Scott Isaacs. My work involved designing a CGI back end, rewriting some of the Javascript and extending the HTML to incorporate other data elements.
That said...
The WYSIWYG editor becomes possible because of Internet Explorer's 'edit mode'. The concept behind the WYSIWYG editor involves embedding an instance of an IE editor into a web page as an object. Because the IE editor has values which can be accessed using Javascript, you can put text into the editor, manipulate it in the editor, and then extract it to save to a CGI script.
Thus, there are three major components to the WYSIWYG editor:
- The HTML page into which the editor is embedded
- The editor itself
- The CGI script which handles the input and output
Because my implementation involves placing text from a database in to the editor to be revised or edited, I used a CGI script to create the HTML page as well as to handle the input from the WYSIWYG form.
Let's look first, then, at the HTML page. Here is what my CGI script produces:
There are four major things to notice about this script:
- - This is where your text to be edited is displayed. My CGI script retrieves the text from the database and then places it into the space between the two textarea tags. This gives me something my Javascripts can move into the editor (specifically, document.form.text.value)
- document.all.text.value = document.all.editBox.html - This is where the text comes out of the editor and back into the form. Notice that this routine is called when you click the Submit button.
- document.all.editBox.html = document.all.text.value - This is where the text goes from the form into the text editor. This routine is called when the IE Editor has loaded and is ready to accept commands (this is the part that was driving me nuts earlier: my Javascript routine wanted to load the text into the editor before it had finished initializing.
- - This is the HTML used to actually embed the IE Editor into my web page. Note that it is embedded after all the Javascripts are defined.
One more note: the script above works only for Internet Explorer. This means you have to detect the browser and write an alternative version of the form for Netscape users.
The second major component of the system is the file editor.htm which I load into the HTML page above. This file is essentially a series of Javascript and DHTML functions which allow the user to interact with the contents of the form. Here is the code:
Message Editor
var bLoad=false,public_description=new Editorfunction Editor() {
this.put_html=put_html;
this.get_html=get_html;
this.testHTML=testHTML
this.bReady = false
}function cleanupHTML() {
 bodyTags=idEdit.document.body.all, i
 for (i=bodyTags.tags("FONT").length-1;i >= 0;i--)
 if (bodyTags.tags("FONT")[i].style.backgroundColor="#ffffff") {
  bodyTags.tags("FONT")[i].style.backgroundColor=""
  if (bodyTags.tags("FONT")[i].outerHTML.substring(0,6)=="")
   bodyTags.tags("FONT")[i].outerHTML = bodyTags.tags("FONT")[i].innerHTML
 }
}function testHTML(bAllowHead,extras) {
 mW.click()
 var badStuff=new Array("IFRAME","SCRIPT","LAYER","ILAYER","OBJECT","APPLET","EMBED","FORM", "INPUT","BUTTON","TEXTAREA"),headStuff=new Array("HTML","BODY","TITLE","BASE","LINK","META","STYLE"),hasStuff=new Array(),bodyTags=idEdit.document.body.all,i=0
 for (i=0;i   if (bodyTags.tags(badStuff[i]).length>0)
     hasStuff[hasStuff.length]=badStuff[i]
 if (!bAllowHead)
   for (i=0;i     if (bodyTags.tags(headStuff[i]).length>0)
       hasStuff[hasStuff.length]=headStuff[i]
 if (extras!=null)
   for (i=0;i     if (bodyTags.tags(extras[i]).length>0)
       hasStuff[hasStuff.length]=extras[i]
 var str=""
 if (hasStuff.length>0) {
   str="Please remove the following HTML Tags from your message and resubmit:"
   for (i=0;i      str ="\n " hasStuff[i]
   str = "\nRemember, when using HTML Mode you may need to escape \nthe brackets surrounding tags (< and >) with < and >"
   setTimeout("mH.click()",0)
 }
 return str
}function get_html() {
if (bMode) {
 cleanupHTML()
 return idEdit.document.body.innerHTML
}
else
return idEdit.document.body.innerText;
}
function put_html(sVal) {
if (bMode)
idEdit.document.body.innerHTML=sVal
else
idEdit.document.body.innerText=sVal
}
var sHeader="",bMode=true,sel=nullfunction displayError() {alert("Formatting toolbar is only accessible in WYSIWYG mode");idEdit.focus()}
function format(what,opt) {
 if (!bMode) {
  displayError()
  return
 }
 if (opt=="removeFormat"){
  what=opt;opt=null
 }
 if (opt=="CustomFont")
 opt = prompt("Format your text with what font face?","Geneva, Arial, Sans-Serif")
 if ((opt=="") && (what=="forecolor"))
 opt = prompt("Format your text with what color?","Black")
 if (bMode) {
  if (opt==null)
    idEdit.document.execCommand(what)
  else
    idEdit.document.execCommand(what,"",opt)
  var s=idEdit.document.selection.createRange(),p=s.parentElement()ÂÂ
  idEdit.focus()
 }
 sel=null
}function getEl(sTag,start) {
 while ((start!=null) && (start.tagName!=sTag))
   start = start.parentElement
 return start
}function createLink() {
 if (!bMode) {
  displayError()
  return
 }
 var isA = getEl("A",idEdit.document.selection.createRange().parentElement())
 var str=prompt("Where do you want to link to? (eg., http:\/\/www.NewsTrolls.com\/)",isA ? isA.href : "http:\/\/")
 if ((str!=null) && (str!="http://")) {
  if ((idEdit.document.selection.type=="None") && (!isA)) {
    var sel=idEdit.document.selection.createRange()
    sel.pasteHTML("" str " ")
    sel.select()
  }
  else
    format("CreateLink",str)
 }
 else
  idEdit.focus()
}function setMode(bNewMode) {
 if (bNewMode!=bMode) {
 if (bNewMode) {
  var sContents=idEdit.document.body.innerText
  idEdit.document.open()
  idEdit.document.write(sHeader)
  idEdit.document.close()
  idEdit.document.body.innerHTML=sContents
 }
 else {
  cleanupHTML()
  var sContents=idEdit.document.body.innerHTML
  idEdit.document.open()
  idEdit.document.write("")
  idEdit.document.close()
  idEdit.document.body.innerText=sContents
 }
 bMode=bNewMode
 for (var i=0;i  htmlOnly.children[i].disabled=(!bMode)
 }
 modeA.className=bMode?"current":"";modeB.className=bMode?"":"current"
 idEdit.focus()
}
Place this code into the file editor.htm which is loaded into your HTML page. There may be some line feeds because of spacing; you should probaby grab the code using a right-click from this url: http://www.munimall.net/sdownes/editor.htm Those of you conversant with Javascript and DHTML can see how you can add additional content elements... for myself, I intend to create a mechanism for adding images to my HTML pages.
The third and final componet of the WYSIWYG editor is the CGI script which handles the forms input. Any forms handling script will do; the CGI perl module is a popular choice. My own CGI is a simplified version of that:
sub parse_form {
  my $name;
  my $value;
  my @pairs;  if ($ENV{'REQUEST_METHOD'} eq 'GET') {
     # Split the name-value pairs
     @pairs = split(/&/, $ENV{'QUERY_STRING'});
  }
  elsif ($ENV{'REQUEST_METHOD'} eq 'POST') {
     # Get the input
     read(STDIN, $buffer, $ENV{'CONTENT_LENGTH'});
ÂÂ
     # Split the name-value pairs
     @pairs = split(/&/, $buffer);
  }
  else {
     &print_error("Invalid request method in forms submission. Use GET or POST");
  }  foreach $pair (@pairs) {
   ÂÂ
     # We can't simply split name & value using = in case
     # someone uses = in their input, eg. URL with CGI input     # First, we'll un-webify it
     $pair =~ s/%([a-fA-F0-9][a-fA-F0-9])/pack("C", hex($1))/eg;
     @SEGS = split("=",$pair);
     $name = shift @SEGS;
     $value = join '=', @SEGS;
ÂÂ
     $name =~ tr/ / /;     $value =~ s/\ / /;
     $value =~ tr/ / /;     # If they try to include server side includes, erase them, so they
     # arent a security risk if the html gets returned. Another
     # security hole plugged up.
     $value =~ s///g;
     if ($FORM{$name} && ($value ne "")) {
 $FORM{$name} = "$FORM{$name}, $value";
 }
     elsif ($value ne "") {
        $FORM{$name} = $value;
     }
  }
  return %FORM;
}This will place your input values into a hash, %FORM. The text input from the HTML form is stored in $FORM{'text'}. You can manipulate this text input just as you would any other text input; I save it to a file as follows:
 $filename = $CGI_Dir . "clist/" . $topicid . "/titlescreen.txt";
 open OUT,">$filename" or $page .= "Error writing $filename: $!";
 print OUT "$FORM{'text'}";
 close OUT;These are the essential components to a WYSIWYG browser-based HTML editor. If you can fling perl, Javascript and DHTML around you will be able to customize these scripts to suit your tastes. If not, feel free at least to try the script by clicking on the Comment on this Topic link below and leaving me a message.
ÂÂ
Stephen Downes, Casselman, Canada
stephen@downes.ca
Last Updated: Dec 15, 2024 4:44 p.m.