XForms provides a strict processing model for XML content. The XForms standard defines controls (text input, combo boxes, text areas, and more) that allow for editing text within a given XML element or attribute. Using the proliferation of rich text editing across many Web-based applications (such as e-mail, blogs, and wikis), the XForms set of controls can be expanded to accommodate this.
Some approaches will be addressed but there can be many others. It is not the intent of this article to single out a specific XForms implementation or widget for rich text editing, but to illustrate some real-world approaches to solving the problem.
Selection of rich text editor widgets
There are many, many HTML and ECMAScript rich text editors for HTML content. Below outlines some key factors in making your choice:
Price: There are many that are either freely available or for purchase. See the resources for information on how to find these.
XHTML: Since XForm's data model is XML-based, it seemed most appropriate to go with an editor that supports XHTML (an XML-ized version of HTML).
Completeness of HTML support: Another factor is the included overall capability: lists, tables, styling.
Browser support: Firefox is primarily used to develop and test this article, but overall support is considered.
Extensibility: For adding in needed features to the editor or limiting existing behavior.
So for the purposes of this article, we'll use Dojo and provide a sample using FCKEditor, as well.
Before getting too far, it is always a good idea to outline an approach to the solution. Since we require XForms and a rich text editor, we'll also need a mechanism to bind the editor's content to an XForm's instance. This could be accomplished by writing a bunch of JavaScript or using another technology for binding user instance controls, called XML Binding Language (XBL). Mozilla XForms provides a way of extending existing user interface controls using XBL, which also makes this choice desirable. For more information about XBL and extending Mozilla XForm's controls in this way, check out the resources.
The control will first render in a read-only mode. Select the Edit button to enable the rich text editing. If you are satisfied with the changes, then select Save; otherwise, select Cancel.
The application that we use in this article is a simple form for posting an entry to a blog. The application gathers a textual summary from the user, as well as the full blog post, which is comprised of rich text markup (XHTML). Here's a sample of the editing interface:
Figure 1. XForms Rich Text Editor
Once you are satisfied with the entry, you can then can submit the posting to the blog application.
Here's the resulting XML instance from the editing session above:
Listing 1. Sample blog entry XML instance
<?xml version="1.0" encoding="UTF-8"?>
<blog>
<subject>Sample Blog Entry </subject>
<content>
<span xmlns="http://www.w3.org/1999/xhtml">Sample <b>BOLD</b> text<br/>
and <i>italics</i> and <u>underline</u> and<br/>
<strike><u><i><b>all together</b></i></u></strike><br/></span>
</content>
<tags/>
</blog> |
Defining the custom rich text editor control
In this section, we'll highlight what is needed to define the custom control. As
unnatural as it may seem, we'll extend the <xforms:trigger> control to provide this capability. This is chosen due to the fact that it allows binding to XML complex content models (XML nodes that have attributes and/or elements). If we had chosen textarea, we'd have received an xforms-binding-exception, since it is restricted to XML simple content models (XML nodes that only have text content).
Binding the custom control using CSS
In order to bind the control to the XBL implementation, we'll use CSS selectors to do this, by element and attribute value matching. We are using <xforms:trigger> and we'll qualify it by using the attribute appearance with a value of "rte". So then the XForms control would become this:
Listing 2. XForms custom trigger control
<xforms:trigger ref="/blog/content" appearance="rte">
<xforms:label>Content: </xforms:label>
</xforms:trigger> |
The CSS to then link this to our XBL binding is:
Listing 3. CSS binding to XBL control
xforms|trigger[appearance='rte'] {-moz-binding: url('rte-dojo.xbl#richtext'); } |
As we have outlined, this selects the <xforms:trigger> with appearance='rte' and binds it to the rte-dojo.xbl file, with the binding identified by 'richtext'. The -moz-binding CSS property is specific to the Mozilla implementation.
There are four main components that enable the use of the editor widget to be used embedded within the XForm's controls. These are the binding declaration, content, properties, and methods.
Binding declaration:
The following is the binding declaration that was used for this scenario. One of
the important pieces to note in this section is the extension of the xformswidget-base. This provides access to the nslXFormsUIWidget interface. This interface allows the controls to
interact with the XForms engine. For further details on custom controls, see the resources.
Listing 4. XBL binding declaration
<xbl:binding id="richtext"
extends="chrome://xforms/content/xforms.xml#xformswidget-base"> |
Content:
The content section of the XBL defines the default content that will be inserted as a
child of the bound node. The <div> created below with
the anonid="content" is used as a place holder where the bound content will be inserted depending on the current node action. This section was also used to create the Edit, Save, and Cancel buttons. By default, the buttons are set to 'display:none'. These buttons are enabled through the method implementations provided in this binding.
Listing 5. XBL content definition
<xbl:content>
<span>
<xbl:children />
</span>
<div anonid="content" onclick="javascript:parentNode.displayEditor();"
style="border: thin black solid; background-color:
rgb(211,233,254); width: 400px;">
</div>
<input anonid="editButton" type="button" value="Edit"
style="display:none"
onclick="javascript:parentNode.displayEditor();" />
<input anonid="saveButton" type="button" value="Save"
style="display:none"
onclick="javascript:parentNode.saveEditorChanges();" />
<input anonid="cancelButton" type="button" value="Cancel"
style="display:none"
onclick="javascript:parentNode.cancelEditorChanges();" />
</xbl:content> |
Properties:
The properties provide a way of defining attributes for this binding. An attribute is
needed to create and store a unique ID for the current editor instance of the bound
control using base functions available in the dojo.dom
module. It provides a unique ID that can be used by the various controls to access the
widget by ID.
Listing 6. XBL property definition
<xbl:implementation implements="nsIXFormsUIWidget">
<xbl:property name="editorId">
<xbl:getter>
<![CDATA[
if(this.getAttribute('editorId'))
return this.getAttribute('editorId');
else
this.setAttribute('editorId',dojo.dom.getUniqueId());
return this.getAttribute('editorId');
]]>
</xbl:getter>
</xbl:property> |
Methods:
The exposed methods extend on the base interfaces provided through the nslXFormsUIWidget as well as custom methods used to instantiate the editor widget, save content, and cancel changes.
Refresh - The refresh method below extends the base interface provided by the
nslXFormsUIWidget. This method is invoked when the XForms refresh event is dispatched. The first step is to remove any nodes that may have been left in the content section during any previous usages. The next step is to use the accessor's interface (nslXFormsAccessors) to retrieve the value of the node in the bound instance for this control. This content is then inserted as a child of the content node.
Listing 7. XBL refresh method
<xbl:method name="refresh">
<xbl:body>
<![CDATA[
var content =
document.getAnonymousElementByAttribute(this,"anonid","content");
// Need to replace the div content with boundNode's children <content>
dojo.dom.removeChildren(content);
dojo.dom.copyChildren(this.accessors.getBoundNode(), content);
// Show Edit button, hide Save and Cancel
document.getAnonymousElementByAttribute(
this,"anonid","editButton").style["display"]="";
document.getAnonymousElementByAttribute(
this,"anonid","saveButton").style["display"]="none";
document.getAnonymousElementByAttribute(
this,"anonid","cancelButton").style["display"]="none";
return;
]]>
</xbl:body>
</xbl:method> |
displayEditor - The displayEditor method is invoked through the Edit button created in the content section. This creates the Dojo Editor2 widget programmatically and replaces the content with the values in the bound node from the XML Instance.
Listing 8. XBL displayEditor method
<xbl:method name="displayEditor">
<xbl:body>
<![CDATA[
var content =
document.getAnonymousElementByAttribute(this,"anonid","content");
var newWidget = dojo.widget.createWidget("Editor2", // widgetType
{id: this.editorId,
toolbarTemplatePath: "rte-toolbar.html",
toolbarTemplateCssPath: "EditorToolbar.css"
}, // widget attributes, for example {title: "Some Title"}
content);
// replace editor contents with that of the bound node
newWidget.replaceEditorContent(
dojo.dom.innerXML(this.accessors.getBoundNode()));
// Hide Edit button, show Save and Cancel
document.getAnonymousElementByAttribute(
this,"anonid","editButton").style["display"]="none";
document.getAnonymousElementByAttribute(
this,"anonid","saveButton").style["display"]="";
document.getAnonymousElementByAttribute(
this,"anonid","cancelButton").style["display"]="";
return;
]]>
</xbl:body>
</xbl:method> |
saveEditorChanges - This method is invoked through the Save button. This method captures the current bound node and removes the content and replaces that with the value from the current editor content. Calls are then made to refresh and rebuild the XForms model. As a last step, the clobberFocus and destroy methods are called to remove the instance of the editor widget.
Listing 9. XBL saveEditorChanges method
<xbl:method name="saveEditorChanges">
<xbl:body>
<![CDATA[
var content = document.getAnonymousElementByAttribute(this,"anonid","content");
var boundNode = this.accessors.getBoundNode();
// retrieve the current widget
var editedNode = dojo.widget.byId(this.editorId);
// retrieve the contents of the editor
var newNode = editedNode.getEditorContent();
// generate span stub to hold contents
var stubSpan = document.createElement("span");
// set the innerHtml
stubSpan.innerHTML=newNode;
// swap out the contents of the bound node with editor's content
dojo.dom.removeChildren(boundNode);
dojo.dom.copyChildren(stubSpan, boundNode);
editedNode.clobberFocus();
editedNode.destroy(true);
model = document.getElementById("mMain");
model.rebuild();
model.refresh();
]]>
</xbl:body>
</xbl:method> |
cancelEditorChanges - The cancelEditorChanges performs calls the clobberFocus and destroy methods on the active editor widget. This will cause all changes to be lost.
Listing 10. XBL cancelEditorChanges method
<xbl:method name="cancelEditorChanges">
<xbl:body>
<![CDATA[
// retrieve the current widget and refresh
var editedNode = dojo.widget.byId(this.editorId);
editedNode.clobberFocus();
editedNode.destroy(true);
this.refresh();
]]>
</xbl:body>
</xbl:method> |
While the Editor2 widget in the Dojo Toolkit offers a rich and extensible set of plugins for a rich text editor, there were some small changes required for the purposes of this sample. Portions of the original rich text templates and JavaScript modules create non-compliant XHTML markup. This can create errors when rendering and storing the content in the XML instance of an XForm. Only a subset of these templates and modules were included to simplify the demo. The patch provided with this demo ensures that the resulting content from the rich text editor will update the XML instance properly.
The following steps will need to be completed to apply the patch and test the sample.
- Download xforms-rte-sample.zip from the download section.
- Download Dojo 0.4.3 from http://www.dojotoolkit.org/downloads.
- Unzip the dojo-sample.zip into a folder.
- Unzip the Dojo 0.4.3 download into the folder labeled
Dojounder thesamplefolder. This should give you the following structure:sample -> Dojo -> dojo-0.4.3-ajax. - Locate the
XForms_XBL_Editor_Patch.txtin thesample\Dojofolder. - Using patch software, like patch.exe or Eclipse, apply the patch for Dojo. Further details on applying patches can be found in the resources section.
This article focuses on outlining how to place a simple rich text editor within an XForms application. If further capabilities are needed from the editor, it is a matter of consulting the rich text editor's resources to determine how best to do this.
There may be a scenario where the rich text markup that the instance uses is not XHTML. If this is the case, this approach may still be valid as long as there is a transformation possible between the rich text markup and XHTML. These transformations can be programmed into the displayEditor method, which instead of loading the nodes directly, first transform them to XHTML and then load the contents of the editor with that. Next when the save is initiated, transform the XHTML into the rich text markup prior to storing those nodes back into the XForms instance.
By following some of the integration rules defined by XForms, XBL, and a rich text editor, the end result is a simple and powerful addition to the XForms set of controls. This can further enable the application of XForms in a variety of applications, such as blogs, e-mails, social networking sites, and more. These can then leverage the built-in capabilities of XForms for validation, XML submission, declarative programming, and more.
| Description | Name | Size | Download method |
|---|---|---|---|
| Samples for both Dojo and FCKeditor | xforms-rte-code.zip | 8KB | HTTP |
Information about download methods
Learn
- Explore the developerWorks XForms space.
- Dig into XForms at the home page of W3C XForms,
which has links to the official XForms specification, as well as to a wide variety of XForms rendering options.
- Find out more about the XML Binding Language (XBL),
which contains the latest W3C working draft.
- Learn more about Mozilla XForms custom controls utilizing XBL.
- Find out more about XHTML, Cascading Style Sheets (CSS), XML, XML Events, XPath,
and other related standards at the W3C site.
- How to apply software patches from resources in Wikipedia.
- See which Web-based rich text editor is right
for you by examining this comparison table.
Get products and technologies
-
Mozilla XForms:
Render your standards-compliant forms in Mozilla Firefox using this plug-in.
-
Dojo toolkit:
JavaScript libraries to ease Ajax development.
-
FCKeditor:
An extensible browser-based rich text editor.
Discuss
-
Get answers to your questions about XForms
on its discussion forum.
Steve Speicher is an IBM Senior Software Engineer working on emerging standards, both infrastructure based and industry verticals (healthcare). He is a member of the W3C Compound Document Formats Working Group, he led the development of the Compound XML Document Toolkit, and he uses Model-Driven Development (MDD) to improve the development of standards. He has previously worked on change and release management tools in the Rational division and in IBM internal tools.
Comments (Undergoing maintenance)





