The postings on this site are the contributor's and don’t necessarily represent IBM’s positions, strategies or opinions.
July 16th, 2008 Austin Arnold Posted in GWT | 5 Comments »
In order for Blueprint to be a useful tool at capturing documentation, it needed to have a better rich text editing experience. In this post I will describe how to integrate the TinyMCE rich text editor into a GWT application, mainly focusing on the user-interface aspect. I will provide examples in which you can utilize methods from the TinyMCE API, as well as ways you can add your own functionality to the editor.
The previous solution we had for rich text editing was to use JSNI methods to call execCommand on a textarea in “design mode”. While this method was reasonable for simple styling like bolding and underlining text, it would not perform well with cross-browser behavior. The HTML generated by one browser for a simple command like “Bold” would be different than another. Internet Explorer, for example, would use <strong> rather than <b> (Firefox browsers), for bolding text. This example does not cause too much pain but you can just imagine the strange variations that can occur for complex operations like converting paragraphs into lists. For this reason, we decided to go with an editor that was already built to deal with many of these inconsistencies and would generate compliant XHTML output. TinyMCE has created a platform independent web based Javascript HTML WYSIWYG editor with an extensive set of configurable options which made it an ideal solution for Blueprint.
As described on their installation instructions, there are several steps needed to install the editor in your GWT application. The first is to include the source Javascript file into your HTML page. This is done the same way you would include any javascript file:
<script language=”javascript” type=”text/javascript” src=”../tiny_mce.js”></script>
Once you have that, then you need to initialize the editor instances. This can be done in another line on the HTML page but if you needed to have several different editor configs (say one that resizes and one that doesn’t), you would need to create an additional line. Because the number of variations is rather large it would probably be a good idea to parameterize this. If you are going to do that, then there is no reason why you shouldn’t go ahead and do it in Java. The following is an example of how to initialize editors with a default configuration:
public static native void initializeEditor(String themeType) /*-{
$wnd.tinyMCE.init({
mode : “none”,
theme : themeType
});
}-*/;
There are many config options that can be applied here but this is a basic example. This needs to be called once before any editors are rendered to the page. In GWT, however, the order in which this method is used is important because a race condition can occur. If you decide to call this method when your page loads, the compiled GWT javascript may execute quicker than the source file is actually getting received from the server (especially if compression is turned on). If you are loading the editors dynamically (as we do in Blueprint), the best place to call the initializeEditor method is right before the first editor is rendered.
The next step is to be able to dynamically turn DOM elements into editable instances. Because TinyMCE actually creates an IFRAME to edit with and hides the element that was specified to be “converted”, the element can be anything containing text (divs, textareas, etc). The following method converts an element with the specified id to an editor instance:
public static native void attachEditor(String itemId) /*-{
$wnd.tinyMCE.execCommand(’mceAddControl’, false, itemId);
}-*/;
To remove the editor from the instance, the method would be:
public static native void removeEditor(String itemId) /*-{
$wnd.tinyMCE.execCommand(’mceRemoveControl’, false, itemId);
}-*/;
With these steps, you can now dynamically load and unload editor instances in your GWT application. As you can tell the execCommand is not a straight execCommand on the container (because mceAddControl is defined in the Javascript). A list of the other useful commands you can use are also available.
Now let’s say you want to use some of TinyMCE’s API methods for more advanced operations. What if you wanted to get the content of the editor, do your own HTML parsing in Java, then put the content back into the editor? This is a reasonable example that will require two methods from the TinyMCE API.
public static native String getContent() /*-{
var html = null;
if ($wnd.tinyMCE.activeEditor) {
html = $wnd.tinyMCE.activeEditor.getContent();
}
return html;
}-*/;
This method will get the current HTML content of the editor. There are some things you need to be aware of though. While the method looks innocent enough, calling getContent in the editor will actually cause a “clean-up” to get fired. TinyMCE has a broad set of HTML parsing and tidying operations it performs to ensure strict XHTML 1.0 compatibility. This usually is not a bad thing but the HTML you get may be slightly different than what you’d expect. Be sure to read the manual and wiki for some of these items. After you have done whatever you want with the HTML, now you need to put it back into the editor. This method will set the content of the editor:
public static native void setContent(String content) /*-{
if ($wnd.tinyMCE.activeEditor) {
$wnd.tinyMCE.activeEditor.setContent(content);
}
}-*/;
Just using a simple JSNI call, you can access everything in the TinyMCE library.
Now that you can harness all of the methods that TinyMCE offers for the editor with JSNI calls, it would also be useful for the editor to use functionality from Java methods that you have written for your application, rather than default behavior of the editor. An example for how we do this in Blueprint is by adding a new button to the toolbar. The following config will demonstrate how to add a button to the toolbar that, when clicked, will call a Java method:
public static native void initializeEditor() /*-{
$wnd.tinyMCE.init({
mode : “none”,
theme : “advanced”,
theme_advanced_buttons1 : “newbutton”,
theme_advanced_buttons2 : “”,
theme_advanced_buttons3 : “”,
setup : function(ed) {
ed.addButton(’New Button’, {
title : ‘New Button’,
onclick : function() {
@classpath::newButtonMethod()();
}
});
}
});
}-*/;
With this new initializeEditor method, all editor instances will have just one button that we created called “newbutton” and when it is pressed, will execute the Java method newButtonMethod. The ‘setup’ variable in the config is a very useful place for putting in your own editor logic. With the ‘ed’ variable you can add Java method calls for any event listener that the API provides.
With the ability to communicate from Java to TinyMCE and vice versa, you have all the functionality you need to integrate the editor into your GWT application.
August 22nd, 2008 at 8:17 am
Hi,
thanks for this nice article.
But when I try to use tinyMCE with GWT I’ve several problems.
1) When I try to set a html text to the widget I see the raw html code for a while in the TextArea (after some time the tinyMCE is shown which renders the html code).
2) In some cases the initialization code has no effect (toolbars and so on).
I’ve no problems when I load the initialization code already in my base html file.
BUT then I’ve the problem to set the right language.
Regards,
Martin
August 27th, 2008 at 9:58 am
Thanks for the comment!
1) When I saw this, all I could really conclude from it was that the browser was just too slow at executing the tinymce javascript. I got around this by styling the textarea as follows:
.textarea { color: white; background-color: white }
So even though the textarea will render with the raw html, you won’t see it.
2) As I mentioned in the article, if you only need one initialization config, then it will probably be fine to put it in the base html file. But, if you want to put it in the GWT code, make sure you are not throwing some JS error related to “‘$wnd.tinyMCE’ is null”, that means you are hitting the race condition when the source file has not been completed downloaded.
The last thing I can think of is to defer the attachment of the editor until after the initialize code is completed.
Hope this helps!
Thanks,
Austin
December 4th, 2008 at 5:39 am
[...] http://development.lombardi.com/?p=43 [...]
June 30th, 2009 at 4:37 pm
Any chance you could publish the complete working wrapper code.
Thx
June 30th, 2009 at 9:19 pm
It’s unlikely but I’ll ask.