XSLTForms/TinyMCE

Like other XForms implementations, XSLTForms supports a non-standard control for editing mixed content (XML with a mixture of elements and character data).

XSLTForms can (at least in principle) be used either with CKEditor or with TinyMCE. This page describes the use of TinyMCE with XSLTForms. It is based on version 638 of XSLTForms; behavior may vary in other versions.

Framework for using TinyMCE with XSLTForms
To use TinyMCE as a control in an XForm, several things are necessary. A simple minimal example of using TinyMCE is available on the AgenceXML web site; it illustrates all the following points.


 * A  element should point to the TinyMCE library and specify the TinyMCE version number.
 * An XSD schema should be provided, which defines an appropriate simple type for the element to be edited using TinyMCE; an  element in the definition of this type contains the initialization object used by TinyMCE to customize the editor.
 * The element to be edited using TinyMCE should be bound to the simple type declared in the XSD schema.

Pointing to the TinyMCE Javascript and supplying a TinyMCE version number
Include an XHTML  element with the following attributes:
 * with the value
 * provides a reference to the TinyMCE library (relative to the XForm)
 * specifies which rich-text editor is being used; for TinyMCE, use the value
 * specifies what version of TinyMCE is being used

The content can be vacuous; a Javascript comment will do.

For example:

The data-version attribute is used by XSLTForms to adjust its calls to TinyMCE; it distinguishes versions 3 and 4 of TinyMCE. (Or, to be more precise, it distinguishes versions whose number begins with "3." and all other versions.) Note, however, that not all versions of TinyMCE 4 work with XSLTForms version 638 (see below).

Supplying a richtext type and a TinyMCE initialization object
A key part of using TinyMCE with XSLTForms is to provide an xsd:schema element, which can be embedded in the XHTML header. A very simple example follows:

As can be seen, this schema defines a simple type with a number of properties worth noting:
 * The type's expanded name is .  (It's not clear whether the name must be this one or can vary.  [Conjecture (unconfirmed):  it can vary, so that different instances of TinyMCE with different customizations can co-exist in the same form.])
 * Its base type is .  (This appears to be essential; there is code in xsltforms.js which checks for this.)
 * The  element in its definition carries the attribute-value specification   (where the prefix   is bound to namespace  ).  XSLTForms uses the value of this attribute to distinguish code specific to TinyMCE from code specific to CKEditor.
 * The type definition contains an  element whose character-data content is a Javascript object.  Strictly speaking, the   element is optional (a default of   is assumed if none is present), but in practice it will almost always be needed.  The Javascript object described in the   element will (with some modifications) be passed to TinyMCE as the argument to the TinyMCE   function; it is the essential mechanism for customizing the TinyMCE.

Binding the instance to the richtext type
The element(s) to be edited using TinyMCE should be bound to the type declared for them in the schema described above.

For example:

Note that the content of the element to be edited should be character data only: a string, with no child elements. The string may (and normally will) contain the serialized XML representation of elements and attributes.

See Serial form and XML form, below.

Serial form and XML form
The use of mixed-content editors like TinyMCE and CKEditor requires that the form designer distinguish carefully between what might be called the normal XML form of an XML document or fragment and the serialized form of the same material. Some other constructs pose the same challenge, like the  and   functions and the   action. (This topic has broad potential for confusion, since it is the serialized form of an XML document which is defined by the XML specification; the fact that we will sometimes be writing the serialized form of an XML element within an XML document also complicates things. The reader who finds the discussion here confusing is asked to be patient; experimentation with a form may also help.)

XML form
What we here call the XML form of an instance document is represented in two different ways at different moments:
 * In the XHTML+XForms document itself, it's represented as a normal XML document or element, using XML syntax.
 * in a running instance of a form being delivered with XSLTForms, it's represented as a DOM object.

The former is illustrated by the following XForm fragment, showing an instance named normal-document whose root element is named  and has one child named , which in turn has a child named.



When this XML is read by an XForms processor, a data structure is built to represent it, following the rules of the Document Object Model (DOM).

In editing mixed content, however, TinyMCE does not operate on the DOM representation of the data; it operates on a string containing tags marked with angle brackets: the serialized form of the data to be edited. (The reasons for this design choice are obscure.)

Serialized form
If a document instance is intended to be edited using TinyMCE, using the normal XML form for the document will lead to unsatisfactory results. (TinyMCE will get the string value of the  element, perhaps wrap it in a   element (depending on configuration), and lose details like the attributes on  .)  To be usefully editable using TinyMCE, the instance document must be presented in serialized form, that is as a string containing a sequence of characters including angle brackets. To write such a string in an XML context (e.g. an XForm), one must escape the left angle brackets and ampersands appearing in the serialized form of the data. When this is done, the  element will look something like this:



Or alternatively (using a CDATA marked section) like this:



Like the XML form, the serialized form of an instance document is represented in two different ways at different moments:
 * in a running instance of a form being delivered with XSLTForms, it's represented as a string of characters which conforms to XML syntax rules.
 * In the XHTML+XForms document itself, it's represented as an escaped string.

Moving back and forth
If TinyMCE is to be used on a document instance (or part of an instance) which has a normal XML structure, then it will be necessary to serialize the document. The  function is helpful here. After the user has edited the serialized version of the data, if it is desired to restore its normal XML structure, the serialized data must be reparsed; the  extension element is useful for this purpose.

[Example needed, showing movement back and forth from normal form to serialized form.]

Styling the control
The XForms rich-text control can be styled in the usual way for XSLTForms.

For example, the following  element sets the font family, height, and width for textarea controls within an element of class.

The actual XForms control can then be linked to this style simply by assigning it to the class :

For more information on styling controls in XForms, see this wikibook's discussion of CSS.

Customizing the TinyMCE editor
The basic mechanism for customizing TinyMCE is the initialization object. This is a user-specified Javascript object which TinyMCE consults during initialization.

In the TinyMCE documentation, this is shown as the function argument in a call to the TinyMCE  function. Examples in the TinyMCE documentation may look something like the example below; this shows a simple initialization object with the properties  and.

In XSLTForms, the call to the TinyMCE  function is handled by XSLTForms, not by the XForm author. So the initialization object is specified not in the call to  but as the character content of the   element in the simple type to which the element to be edited is bound. In the XSLTForms context, the  property is not used (if supplied, it is ignored, overwritten by XSLTForms), so the equivalent of the initialization object shown above will look something like this (the surrounding schema is not shown):

The following subsections describe a few simple customization examples, but for full information on the possible ways of customizing TinyMCE, see the documentation for the version of TinyMCE you are using. (The examples here use TinyMCE 4.)

With a few exceptions, every initialization property described by the TinyMCE documentation can be specified within the  element and XSLTForms will pass its value through to TinyMCE, so the effect will be as described by the TinyMCE documentation.

The exceptions are these:


 * The  property (stressed heavily by the TinyMCE documentation, because required by TinyMCE) is supplied by XSLTForms, based on the type bindings of the data. Any   value passed in by the user will be overwritten with an ID generated by XSLTForms for the particular control instance being initialized.


 * The  property (which doesn't seem to be described by the TinyMCE documentation, but may be used in an   call) is overwritten by XSLTForms with the value.


 * The  property (whose value is a function, which TinyMCE evaluates during initialization of an editor instance) is supplied by XSLTForms. Any   value passed in by the user will be overwritten.  TinyMCE customizations which require use of the   property, including the specification of custom buttons, are thus not possible.  For a workaround, see the discussion of custom buttons, below.

Styling elements within the TinyMCE editor
A stylesheet to be used for the display of elements within the TinyMCE editor can be supplied by giving its URI (relative to the location of the XForm) as the value of the 'content_css' property in the TinyMCE initialization object. For example:

Setting the toolbar, menus, etc.
The  property can be used to specify what goes in the toolbar; it is helpful for eliminating unwanted tools in order to have a simpler interface.

'Custom' elements and 'valid' elements
TinyMCE makes an effort to produce clean HTML, and to allow users of the TinyMCE library to constrain the HTML further.

If the initialization object specifies a list of, other elements in the data will be stripped out when the data are cleaned up (this happens before data are sent back to XSLTForms, as well as at other times). So  can be used to restrict the data being edited to a specified set of elements.

Non-XHTML elements can also be specified, in the  property.

XML elements not in XHTML
In a typical XForms application, the XML being edited is not necessarily XHTML; elements with names not known from the XHTML spec can be supported, by specifying their names as the value of the  property.

[It appears to be necessary to specify custom elements both in the  property and in the   property. Testing for further information would be a good idea.]

It is also possible to specify what elements are allowed as children of a particular element. During data cleanup, TinyMCE may restructure the data if it thinks an element present in the data is not legal as a child of its parent. [Further details needed.]

Where to find TinyMCE, where to put TinyMCE
For some time, XSLTForms has shipped with TinyMCE version 3.4.6; the code is placed within the XSLTForms directory at the location. When other versions of TinyMCE are used, it is convenient to download the minimized version from the TinyMCE web site and install it in an appropriately named subdirectory of. Other locations may also work.

TinyMCE has changed somewhat since version 3.4.6; some changes involve look and feel (TinyMCE 3 looks dated compared to 4), others involve the API.

Which versions of TinyMCE does XSLTForms support?
XSLTForms 638 supports both version 3 and version 4 of TinyMCE; the minimal example of TinyMCE given on the AgenceXML web site uses TinyMCE 4.0.28. But not all versions of TinyMCE 4 appear to work with XSLTForms version 638; the most recent version that does appear to work is 4.3.12 (of May 2016). Later versions produce a "Loading..." message which never goes away, and an error message on the Javascript error console.

N.B. It is currently expected that release 640 of XSLTForms will support the most recent version of TinyMCE, 4.5.3.

The method
To define custom buttons for the editor, the TinyMCE documentation suggests calling the  method on the TinyMCE editor object; the argument to the method is an object specifying a button identifier, a button label or icon, a function to run when the button is clicked, etc.

The TinyMCE documentation gives as an example:

The callback function
An obvious first complication is that the editor object in question is not available until the TinyMCE  function is called. So the code shown above cannot simply be placed in a  element in the page. Instead, the user supplies a callback function to TinyMCE, to be executed as part of initialization. (Some readers may know callback functions under other names like hooks or user exits.) The user does this by supplying the function as the value of the   property in the TinyMCE initialization object; the   function gets one argument, the editor object.

Button actions
In practice, other issues may arise. If the purpose of the button is to do for a custom phrase-level element what the builtin  and   buttons do for the HTML   and   elements, then first the custom elements need to be declared and made valid, using the   and   properties described above.

Second, there must be functions to perform the action desired when the button is clicked. As is shown above, the function should take no arguments. In order to interact with the editor, therefore, we need to declare the function in a context where the TinyMCE editor object is available: namely, inside the   function, where the editor object is passed in as an argument.

An example
Suppose that we want to tag the currently selected text with a custom element, i.e. insert a start-tag before the current selection and an end-tag after it. To reduce redundancy in the code, we can define this with a generic Javascript function that takes the element type name as an argument, and any number of element-specific functions which call the generic function:

Putting everything together into the initialization object, we get an  element like the following:

Necessary modifications to XSLTForms 638
The remaining complication is that while the initialization object just shown will work with TinyMCE, by default XSLTForms overwrites the user-supplied  property with its own setup function.

To allow both the standard XSLTForms setup and the user-supplied setup to be executed by TinyMCE, three changes are needed in the code of xsltforms.js version 638 (applicability to other versions cannot, of course, be guaranteed). All three changes occur in the definition of a function named. In XSLTForms 638, this is easily found by searching for the string " ", which appears only in that function.

N.B. It is currently expected that release r640 will include the patch, or an equivalent patch, so the changes described here will be unnecessary.

1. After the line reading, insert the lines

This defines a property named  (the name is chosen to minimize the likelihood of conflict with any new properties in future versions of TinyMCE), whose value is the user-supplied value of the   property, if there is one, and otherwise a vacuous function which does nothing.

2. Immediately following, there is a conditional testing for TinyMCE version 3.*; each branch of the if/then/else assigns a value to the  property.

In the  branch, change the function by inserting a call to. Replace

with

3. In the, do the same thing. For

substitute

With these changes, it becomes possible to define custom buttons for the XSLTForms mixed content control using TinyMCE.