Automatically Choosing Spelling Dictionaries

One of the most commonly changed settings for EditLive! is the location of the spelling dictionary. It's not only annoying to keep the URL to the dictionary correct across different sites but it can also be difficult to support clients using different languages. Fortunately, EditLive! 6.0 introduced automatic selection of dictionaries. The correct dictionary for the user's locale is automatically selected and downloaded without needing to be specified in the configuration file.

To take advantage of this all you have to do is:

  1. Make sure that you copied the dictionaries folder into the download directory for EditLive! This should already be the case for most deployments.
  2. Remove the "jar" attribute from the spellCheck element in your configuration file.

Of course, if you want to specify a specific language regardless of the user's locale or a custom dictionary you can still specify the download location for the spelling jar as usual. Otherwise, sit back, relax and let EditLive! do all the work.

Replacing A Textarea With EditLive!

One really useful technique for integrating EditLive! into an existing system is to configure the system to simply output a plain text area with the content to edit and then use JavaScript to replace the text area with EditLive! Since using a plain text area for editing is generally fairly simple to achieve on the back-end, this is often a quick way to integrate EditLive! without having to make significant changes to the back-end system.

To start with, lets assume that the system has been configured to output the content into a text area called "content":

...
<textarea name="content" id="content" rows="10" cols="60">
&lt;p&gt;This is the content&lt;/p&gt;
</textarea>
...

We'll also need to get the back-end system to include our JavaScript file:

<script language="JavaScript" type="text/javascript" src="loadEditLive.js"></script>

The loadEditLive.js file will do all the hard work of replacing the text area. That one line to include the script file is the only change we need to make to what the back-end system outputs so there is minimal changes required. The JavaScript to make this work is a little bit technical and involves working around the odd browser bug so if you want to get up and running really quickly you can just download the finished JavaScript file. If you have problems you can always come back to understand what the script is doing so you can modify it for your environment.

Still here? Good, lets get into the technical details. First we define a couple of variables that are likely to change based on your particular environment - where the EditLive! files are stored and the ID of the textarea to replace:

/* The URL to the editlivejava directory. */
var editliveDownloadDirectory = "editlivejava";

/* The ID of the textarea we want to replace. */
var textAreaId = "content";

Secondly, we write out a script tag to load the EditLive! JavaScript library:

document.write("<script language='JavaScript' type='text/javascript' src='" +
  editliveDownloadDirectory + "/editlivejava.js'></script>");

To make sure that the textarea and the EditLive! JavaScript library we just inserted have actually loaded, we defer the rest of the loading until the document's onload event fires. We also make sure that we store the original onload function so that any existing functionality isn't broken:

var originalOnLoad = window.onload;
window.onload = function() {
  loadEditLive();
  if (originalOnLoad) {
    originalOnLoad.apply(window);
  }
}

Now we need to define the loadEditLive() function that actually loads EditLive! The first thing to do is to get the textarea we need to replace, find out it's width and height and hide it so it's out of the way:

/* Get the textarea we need to replace. */
var textarea = document.getElementById(textAreaId);

/* We use the offsetWidth and offsetHeight properties to determine
the actual size of the textarea in pixels. */
var width = textarea.offsetWidth;
var height = textarea.offsetHeight;

/** Hide the text area. */
textarea.style.display = "none";

Next we set up an instance of EditLive! just like normal, using the standard JavaScript APIs and configuring it as required. Note that we set AutoSubmit to false so that EditLive! doesn't create its own textarea to submit content back in. We'll handle storing the content back into the existing text area in a moment.

var editlive = new EditLiveJava("EditLive", width, height);
editlive.setDownloadDirectory(editliveDownloadDirectory);
editlive.setConfigurationFile(editliveDownloadDirectory + "/sample_eljconfig.xml");

/* Make sure that autosubmit is set to false as we'll
   manually store the content before submitting. */
editlive.setAutoSubmit(false);

Now we need to set the content of the editor to the content that the back-end system put into the textarea:

editlive.setBody(encodeURIComponent(textarea.value));

The final step in getting the editor to load is to actually insert it into the document. Since we deferred loading until the onload event fired, we can't just use editlive.show() like normal because it calls document.write which would replace the entire document content. Instead, we'll use the getAppletHTML() method to retrieve the HTML that loads EditLive! and insert it into the document ourselves:

var editliveDiv = document.createElement("div");
textarea.parentNode.insertBefore(editliveDiv, textarea);
editliveDiv.innerHTML = editlive.getAppletHTML();

At this point the editor will fully load in the page in place of the textarea. However, when we submit the form the original content submits instead of the modified version from EditLive! This is because we set AutoSubmit to false earlier and haven't yet copied the content back into the original textarea. To take care of that, we add an onsubmit function to the form the textarea is in using the same technique we used to add the onload handler, again making sure we preserve any existing onsubmit function:

var form = textarea.form;
var originalOnSubmit = form.onsubmit;
form.onsubmit = function() {
  storeEditLiveContent();
  if (originalOnSubmit) {
    originalOnSubmit.apply(form);
  }
}

The storeEditLiveContent is fairly straight forward - simply retrieve the content from EditLive! and store it in the textarea. There is one minor complication caused by Safari where setting the value of a hidden textarea doesn't actually do anything. To work around that bug, we simply set the innerText of the textarea when running on Safari.

function storeEditLiveContent() {
  var textarea = document.getElementById(textAreaId);
  var editliveApplet = document.getElementById("EditLive_elj");
  var content = editliveApplet.getBody();
  /* Safari has a bug where setting the value of a hidden textarea
  has no effect. To work around this we set the innerText instead.
  The IsSafari variable is defined in editlivejava.js
  */
  if (IsSafari) {
    textarea.innerText = content;
  } else {
    textarea.value = content;
  }
}

That's it. Not as straight forward as most things with EditLive! but fortunately, now that we've developed the JavaScript file, it's simple to apply it where ever we need it. The full JavaScript is below so it's easy to copy and paste:

/* The URL to the editlivejava directory. */
var editliveDownloadDirectory = "editlivejava";

/* The ID of the textarea we want to replace. */
var textAreaId = "content";

/* Write out a script tag to load the EditLive! JavaScript library. */
document.write("<script language='JavaScript' type='text/javascript' src='" +
  editliveDownloadDirectory + "/editlivejava.js'></script>");

/* Add an onload handler to do the actual replacement.
This ensures that the document and the script library we loaded above have completed loading.
We store the original onload event to ensure that we don't break any existing functionality that may be implemented there.
*/
var originalOnLoad = window.onload;
window.onload = function() {
  loadEditLive();
  if (originalOnLoad) {
  	originalOnLoad.apply(window);
  }
}

function loadEditLive() {
  /* Get the textarea we need to replace. */
  var textarea = document.getElementById(textAreaId);

  /* We use the offsetWidth and offsetHeight properties to determine the actual size of the textarea in pixels. */
  var width = textarea.offsetWidth;
  var height = textarea.offsetHeight;

  /** Hide the text area. */
  textarea.style.display = "none";

  /** Set up EditLive! as normal. Any of the normal EditLive! instantiation options can be configured here.
  Note however that we don't call the show() method, the EditLive! instance is inserted into the document below.
  */
  var editlive = new EditLiveJava("EditLive", width, height);
  editlive.setDownloadDirectory(editliveDownloadDirectory);
  editlive.setConfigurationFile(editliveDownloadDirectory + "/sample_eljconfig.xml");

  /* Make sure that autosubmit is set to false as we'll manually store the content before submitting. */
  editlive.setAutoSubmit(false);

  /* Load the content from the text area into EditLive! */
  editlive.setBody(encodeURIComponent(textarea.value));

  /* Insert EditLive! into the document by creating a DIV to contain the editor, adding the DIV to the document where
  the textarea is and setting the content of the DIV to the editor's instantiation HTML. */
  var editliveDiv = document.createElement("div");
  textarea.parentNode.insertBefore(editliveDiv, textarea);

  /** getAppletHTML() returns the HTML required to load EditLive! We load this directly into the div we created for the editor. */
  editliveDiv.innerHTML = editlive.getAppletHTML();

  /* Now we need to add an onsubmit() handler to store the content back into the hidden DIV before we submit. */
  var form = textarea.form;
  var originalOnSubmit = form.onsubmit;
  form.onsubmit = function() {
    storeEditLiveContent();
    if (originalOnSubmit) {
      originalOnSubmit.apply(form);
    }
  }
}

/* Handles storing the content back into the text area so that it submits back to the server.
We use LiveConnect here to make the call work synchronously instead of using the EditLive! JavaScript apis which work asynchronously.
*/
function storeEditLiveContent() {
  var textarea = document.getElementById(textAreaId);
  var editliveApplet = document.getElementById("EditLive_elj");
  var content = editliveApplet.getBody();
  /* Safari has a bug where setting the value of a hidden textarea has no effect. To work around this we set the innerText instead.
  The IsSafari variable is defined in editlivejava.js
  */
  if (IsSafari) {
    textarea.innerText = content;
  } else {
    textarea.value = content;
  }
}

Here's the HTML page with the script and the textarea all put together:

<html>
<head>
<title>Replacing A Textarea with EditLive!</title>
<script language="JavaScript" type="text/javascript" src="loadEditLive.js"></script>
</head>
<body>
<p>We want to replace the textarea below with EditLive!</p>
<form method="POST" action="form_out.jsp">
<textarea name="content" id="content" rows="40" cols="100">&lt;p&gt;This is the content&lt;/p&gt;</textarea>

<p><input type="submit" /></p>
</form>
</body>
</html>

You can also download both files.

Named Entities or Numeric Entities?

There are two types of entities in HTML and XML, named entities like &nbsp; and numeric entities like &#160;. Once the document is parsed, there's no difference between the two types, but in specific situations one type might be preferred over the other and in some cases you have to use numeric entities.

When you're working with plain HTML the best choice is to use named entities when one is available because they're easier to read. Since the HTML standard defines a wide range of named entities and they are reliably supported by browsers.

If you're using XHTML, the XHTML DTD defines the named entities so generally they should be available and safe to use. However, when parsing with a non-validating XML parser (including many browsers in standards compliant XHTML mode) there's no requirement that external DTDs be loaded. If the DTD isn't loaded the named entities will cause parsing errors. So when using XHTML it's safest to use numeric entities to avoid any problems.

EditLive! automatically takes care of these details based on the setting in the htmlFilter element in the configuration file. If you set outputXHTML or outputXML to true, EditLive! will automatically use numeric entities and if both of those are false EditLive! will use named entities. It will even convert existing entities to the right form.

There are two exceptions to this, &amp; and &quot; are defined as part of the XML standard and work in every XML parser. As such, EditLive! will always use named entities for ampersands and quotes.

 

Why International Characters Display As Boxes

Sometimes when editing content that uses international characters, particularly from Asian languages, the characters appear as little boxes. The good news is that this nearly always means you've sorted out all the character encoding issues and the content is being loaded in without corruption. So why don't the characters display correctly? It's all about the font.

First, a little background on what fonts actually do - they're more than just a pretty (font) face. It's important to understand that computers don't think of characters like we do, like everything else to a computer, a character is just a sequence of 1s and 0s. When the character is displayed on screen (or printed) the computer has to translate that sequence of 1s and 0s into something that a human would recognize. That's where fonts come in. The fonts on your computer contain a mapping between the computer's representation of a character and the actual glyph you see on screen.

The problem is, it's time consuming and expensive to develop fonts - each and every character has to be carefully drawn in the font along with a lot of other information to get the font to display just right in any situation. To reduce this cost, most fonts don't include a rendering for every possible character and when they don't support a character they render it as a little box.

So, how do we solve the problem?

First we need to make sure that there is at least one font on your system that supports the characters you're using. To test this, see if your browser can render the characters correctly.

The first option is to use a font that supports the characters your using. Since EditLive!, like most browsers, defaults to Times New Roman for the font you'll probably need to explicitly specify the font using the CSS stylesheet, for example:

body {
    font-family: "Hiragino Mincho Pro";
}

Which font to use will depend on what fonts you have installed on your machine, what OS you use and what characters you need to support. Hiragino Minco Pro is a font available on Mac OS X that support Japanese characters.

All that's fairly complex, fortunately there's another option. You'll notice that even without specifying the right font, browsers will tend to render the characters correctly. That's because they support "font fallback". If the required character isn't support by the current font, the browser automatically picks a different font that does support it. Fortunately, font fallback was added to Java in Java 1.5, so you can solve the problem simply by upgrading to Java 1.5 and above (and making sure there's at least one font that supports the international characters).

If instead of little boxes you're seeing question marks, corrupted content or little boxes every second character, you've probably got actual character set problems. Take a look at the previous article on character sets for some tips on tracking down and solving the problem.

Solving Character Set Issues With Legacy Systems

While all good modern software supports non-english text (usually by supporting Unicode), sometimes we still have to work with software that either doesn't yet have Unicode support, or isn't correctly configured for Unicode support. Fortunately, EditLive! is designed to not only work in these situations but provides a really useful setting to ensure that the content coming out of EditLive! is safe to pass on to the non-unicode parts of the system. It's as simple as one JavaScript call:

editlive.setOutputCharset("US-ASCII");

The setOutputCharset call tells EditLive! to ensure that it's output is compatible with the specified character set. You can still use the full set of Unicode characters, EditLive! will simply HTML encode them so that they pass through legacy systems without being corrupted. Users can paste content with special characters from any source, including Microsoft Word and other web pages and EditLive! will make sure that the content is safely encoded so that the output is still completely valid US-ASCII.

You can specify any supported character set in the setOutputCharset call, for instance if your systems fully support Unicode, you could use it to gradually convert your content from the legacy character set to UTF-8. Generally though, it's most useful to set it to US-ASCII to ensure compatibility.

If the entire document is being retrieved, EditLive! will even update the Content-Type meta tag to indicate the new character set or insert one if there isn't one already. This ensures that compliant HTML parsers will automatically use the right character set to parse the document.

One important thing to note about setOutputCharset is that it doesn't affect the character set that EditLive! uses when it loads in the content, only when it outputs it. This allows you to load in content using the character set the content is currently stored in (assuming it hasn't already been corrupted by the legacy systems) and EditLive! will convert it to the specified output character set.

Finally, if you are seeing problems with character set support in your system, there are some common places where character set issues occur:

  • the character set where the content is stored (the database or filesystem).
  • the character set of the web servers or application servers, both for displaying the content to users, and editing the content.
  • the way in which data is read from the database (try outputting the data after reading it from the database).
  • the encoding of data before it is transferred into EditLive! (the URL encoding required before setting the body, document or content).

See Also

 

Accepting All Changes With JavaScript

In previous installments we've looked at ways to detect the presence of track changes data and accept the changes using XSLT. This time round, we'll use JavaScript string parsing to accept all the changes. This technique is useful for any environments where an XSLT processor isn't readily available1. It should be reasonably straight forward to port the example code to other programming languages.

The first step is to remove the <change> tags within the document content. As the tag is always an empty tag, this is a very simple regular expression:

source = source.replace(/<change[^>]*>/gim, '');

Firstly the pattern <change[^>]*> is a pattern we'll use regularly to match a specific tag regardless of what attributes it has. Secondly, the three flags we specific, gim, mean that the expression is a global match and so will match multiple tags on the same line (g), case insensitive (i) and matches across multiple lines (m).

The second step is to find the start of the div containing the track changes element:

var startTrackChangesDiv = source.search(/<div [^>]*>\s*<trackchanges [^>]*>/gim);

We again use the standard pattern for matching a specific tag, but this time we combine two of them separated by whitespace. Firstly, we find the div start tag (<div [^>]*>), then any amount of pure white space (\s*) then the trackchanges start tag (<trackchanges [^>]*>). The startTrackChangesDiv variable now contains the offset of the div start tag.

To remove the DIV we need to know where it ends. This is much simpler, since the DIV is always inserted as the last thing in the body, so it's always the last closing div tag in the document.

var endTrackChangesDiv = source.lastIndexOf('</div>') + '</div>'.length;

Finally, we remove the entire div by extracting just the content before the start of the div and after the end of the div:

source = source.substring(0, startTrackChangesDiv) +
        source.substring(endTrackChangesDiv);

The complete function winds up as:

function acceptAllChanges(source) {
    source = source.replace(/<change[^>]*>/gim, '');
    var startTrackChangesDiv = source.search(/<div [^>]*>\s+<trackchanges [^>]*>/gim);
    var endTrackChangesDiv = source.lastIndexOf('</div>') + '</div>'.length;
    source = source.substring(0, startTrackChangesDiv) +
        source.substring(endTrackChangesDiv);
    return source;
}

The end result is the document as if all the changes had been accepted in the editor, ready for publishing to the world.

1 - while XSLT is available in modern browsers via JavaScript, it's often easier to use this string parsing approach than to handle the browser incompatibilities with XSLT.