C. M. Sperberg-McQueen
Initial description 8 June 2018, last rev. 11 June 2018
This document describes the use of the TinyMCE editor for mixed content in the TLRR project's XForms-based editing interface for trial records. It was written to record and document the way various components interact, some of the technical issues faced along the way, and our (current) solutions to those technical issues.
The document is addressed to those seeking to understand how to use the TinyMCE plugin for XSLTForms in the context of a more complex larger form. Readers not familiar with XForms in general, and XSLTForms and TinyMCE in particular, are unlikely to find the document compelling reading.
Contents:
This section identifies some background information relevant to the use of Tiny MCE inthe TLRR project. It should probably be expanded a bit; at the moment, it's rather terse.
The TLRR-2 project is preparing a second edition of the work Trials in the Late Roman Republic, 149 BC to 50 BC, originally published in book form in 1990 by Michael C. Alexander.
The second edition is being prepared in XML, and the principal editing interface for the distributed team collaborating on the new edition is supplied by XForms.
TLRR currently uses XSLTForms, an implementation of XForms by Alain Couthures of AgenceXML. XSLTForms uses in-browser support for XSLT to transform an document written in XHTML + XForms into a pure HTML document which loads a Javascript library to handle user interactions with the form. All the code supporting our XForms thus executes in the user's browser. This reduces load on our server, but has the disadvantage that bugs in browser support for relevant standards, and breaking changes in browser interfaces, can cause problems.
TLRR's records of trials exhibit a mixture of highly structured
and tightly constrained data with much more loosely constrained
information. All trial descriptions are readily divisible
divisible into a consistent sequence of fields, but even when the
information in a given field (e.g. date
) is mostly
fairly straightforward (a year), there will be numerous exceptions
with a slighly more complex structure (e.g. a terminus ad quem or
terminus a quo), and a few with an extremely complex structure.
The value to be assigned to any field may be complely unknown,
conjectured, uncertain, probable, or certain (for some suitably
cautious definition of certainty).
So any given field may exhibit a range of structures, from simple and predictable to a short paragraph in academic prose, with footnotes.
In consequence, editing mixed content is an absolute requirement for the project.
There has been discussion in the structured-markup community for decades concerning the best way to enable users who are not expert in structured markup to edit structured data; answers have included SGML and XML editors, training the users (so they cease to be inexpert), customizations of SGML and XML editors, and so on.
So far, TLRR has focused on trying to solve the problem using XForms. For editing the structure of a trial record, XForms has proven (we believe) quite suitable. For editing the text of paragraphs, footnotes, and other mixed content, we continue to look for good solutions. Our trial editor is called Dexter; we have experimented, and continue to experiment, with different techniques for editing mixed content, which are used as loadable subforms within Dexter.
We have developed a number of ways to edit mixed content within an XForm; several of them possess, we believe, a certain technical interest and ingenuity. But none of them make it easy for the historians in the project to edit the footnote to a description of the date of a trial.
Pointer should be provided pointing to the publicly visible version of Dexter. And that publicly visible version should be brought up to date.
TinyMCE (MCE = ‘mixed-content editor’) is one of a large number of Javascript libraries which provide HTML editing in a browser environment.
Like other similar tools (including CKEditor), TinyMCE can be used as an editing control in XSLTForms, as a short demo on the AgenceXML website illustrates.
As the XSLTForms demo illustrates, using TinyMCE in an XForms context involves several steps and constraints:
The XForm must load the TinyMCE Javascript library. The AgenceXML demo illustrates the way:
<script type="text/javascript" src="xsltforms/scripts/tinymce_4.0.21/tinymce.min.js" data-uri="http://www.tinymce.com" data-version="4.0.21">/* */</script>
Observation: The script
element is
not expected within the xf:model
element,
so in a valid XForm the script
element will be
outside the model.
The XForm must load an XSD schema which specifies a user-defined simple type with the following properties:
xf:HTMLFragment
,
where the prefix xf
is bound to the namespace
http://www.w3.org/2002/xforms
.xsd:restriction
element in the type
definition carries the attribute-value specification
xsltforms:rte="TinyMCE"
, where the prefix
xsltforms
is bound to the namespace
http://www.agencexml.com/xsltforms
.xsd:annotation
element which in turn contains
an xsd:appinfo
element. This element should
describe a Javascript object to be passed to TinyMCE's
tinymce.init()
function. For example:
plugins: ["code"], toolbar: 'undo redo | insertperson insertprocedure italic | bullist', content_css: 'test-mce.css', ...The property names to be used are those defined by the TinyMCE documentation. Note that XSLTForms adds some further customizations to those specified by the user. A list would be useful.
xf:HTMLFragment
).As usual in XForms, the schema document may
either be embedded in the form itself (as illustrated by the
AgenceXML demo) or located in an external resource
referred to by the xf:model/@schema
attribute, as in
<xf:model schema="rte-schema.xsd"> ... </xf:model>
Each element (or attribute?) to be edited using TinyMCE should be bound to the user-defined simple type.
Each element (or attribute?) to be edited using TinyMCE must be presented to TinyMCE as a string, not as a mixture of text and sub-elements.
Thus the document instance for the TinyMCE demo on the XSLTForms site is specified not as a normal XML document:
<!--* NOT this way *--> <instance id="main"> <data xmlns=""> <richtext> <p>Paragraph <i>number one</i></p></richtext> </richtext> </data> </instance>but instead as an element whose content is ampersand-escaped HTML:
<!--* THIS way *--> <instance id="main"> <data xmlns=""> <richtext> <p>Paragraph <i>number one</i></p></richtext> ></richtext> </data> </instance>
Finally, the element to be edited is bound
in the usual way
to a textarea
widget which
specifies the mediatype application/xhtml+xml
:
<textarea ref="richtext" class="large-textarea" mediatype="application/xhtml+xml"/>
A number of technical questions arise which are not specific to the TLRR context. Among them are these, some of which remain to be answered.
Is the definition of xf:HTMLFragment
available for consultation?
No type definition for this type is exposed, that I
am aware of. The string HTMLFragment
does
not occur in the XForms 1.1 spec, so it appears to be a magic
name. For all intents and purposes, it appears to be a
vacuous restriction of xsd:string
.
Does the name of the type to which an element is bound matter?
The AgenceXML demo defines a type named
rte:standardHTML
, where the prefix rte
is bound to the namespace
http://www.agencexml.com/xsltforms/rte
.
Is this the name users of TinyMCE are required to use?
Answer:
No, the name is user-determined, not fixed by XSLTForms.
The Javascript code contains
a check for types derived from xf:HTMLFragment
,
but not for types of the name
rte:standardHTML
.
Users can use any name they wish.
Example: see the demo of TinyMCE with user customization, an external schema, and a user-specific type name.
Can non-HTML elements be edited? How?
Yes. The properties custom_elements
and
valid_elements
can be used to allow non-HTML
elements in the material being edited. The CSS specified in the
file pointed to by the content_css
property can
specify distinctive presentation for them.
And the TinyMCE setup()
function can
be used to specify custom buttons to add or modify the
custom elements.
Can validation be performed beyond providing a list of valid elements? How?
Only within fairly narrow limits: for any element, including custom elements, one may specify a list of allowed children.
This appears to mean that one can constrain the
parent/child relation but not the sequence of siblings, as
if one were writing a schema where every content model
has the form (#PCDATA | a | b | ... | z)*
.
Can TinyMCE be used in a subform? If so, must the schema be loaded by the subform, or by the main form, or both?
TBD.
The question arises for all the material that must be provided.
The schema.
Conjecture: Schema must be loaded in main form. Rationale: Subform loading generally takes very little information from the header of the subform, so I suspect that the schema must be loaded by the main form, and that it doesn't matter whether it's loaded by the subform. (Of course, if the subform is to be used stand-alone, even for testing, then the schema will need to be loaded there, too.)
The bindings.
Conjecture: Element bindings must be performed in the main form.
Loading TinyMCE.
Observation: TinyMCE must be loaded by the main form.
Can multiple types be defined? Can different elements be bound to different types?
If so, this would allow different customizations of TinyMCE to be applied to different elements.
Conjecture: Yes.
To be confirmed or denied.
These issues may not be relevant outside the TLRR context.
Since when we read and write trial records in communication with the server, they need to be in normal XML, not in an ampersand-escaped XML serialization, we will need to translate any element into ampersand-escaped form before editing it with TinyMCE.
We already have code to do this, for the Ianua interface (which presents the user with a text area containing raw-bits XML, angle brackets and all).
Details to be supplied.
The elements we wish to be able to edit with TinyMCE,
like charge
and date
and
p
, are not in the normal course of events
represented as ampersand-escaped XML. It would be
inaccurate, therefore, to bind those elements to the
simple types defined for customizing TinyMCE.
But if the elements are not bound to a suitable type, TinyMCE is not invoked.
The solution we are currently trying works like this:
frob
(for ‘frobnicated’), in a
secondary document instance.frob
element into the main
document instance as the child of E./frob
.xf:bind
element to
bind E/frob
to a suitable type.On deactivation of the editor, the results returned will need to be de-frobnicated and used to re-populate E in the usual way.
This differs from the existing pattern for frobnication in inserting a distinct element for the frobnicated content, which can be bound to a distinctive type. That might be worth doing for the elements that currently use serialization and Goldfarb Normal Form.
Which elements do we wish to be able to edit with TinyMCE, and how do their content models currently differ?
The form currently embeds the mixed-content control (which offers editing with our different tools for mixed-content editing: Lacrimae, Ianua, and now Humilis) for the following elements:
NB
date
ccGrp/*/*
(i.e. procedure
, court
,
en
, p
)other/p
outcome
description/p
Mixed-content editing is also offered for parts of name lists (in RE-namelist-ianua.xml):
person-entry
source
bibref
The content models are these. The parameter entity links
expands
to trialref | procedure | person | court | ptr
; the
parameter entity hyperlinks
expands to %links; | ref
.
The ref
and ptr
elements are conventional
generic hyperlinks in the style of their TEI counterparts. The other links are,
as their names suggest, specialized pointers to specific kinds of things.
(#PCDATA | br | en | i | ix | %links;)*
= %misc-value
(for outcome
)(#PCDATA | en | i | %hyperlinks; | sup )*
= %bibl-data
(for source
, bibref
)(#PCDATA | en | i | ix | %links;)*
= %single-value
(for NB
and date
)(#PCDATA | en | i | %links;)*
= person-entry
: (#PCDATA | en | i | %links; | sup | span)*
= p
(#PCDATA)
= court
, procedure
: (p | list)+
= en
Defining buttons and actions, placing buttons in toolbar.
to be supplied
to be supplied
This section presents an unsystematic list of things that can go wrong, and what the apparent cause was, when they went wrong in efforts to use TinyMCE in this project.
At the time this paragraph is written (June 2018), the most recent major version of XSLTForms is xsltforms-1.0 (647), which includes versions 4.5.2 and 4.7.1 of TinyMCE; the most recent release of TinyMCE is 4.7.13, which does not work.
The symptom is that the widget loads but the text area is blank.
It is not clear which release of TinyMCE is the most recent one that works with XSLTForms 1.0, or what broke between 4.7.1 and 4.7.13.
There are a variety of other things one can do wrong in trying to set up an XForm to use TinyMCE. Some of them are trivial but nonetheless irritating and time consuming. A partial list of things one can do wrong follows.
Botch the link to the Javascript
The TinyMCE distribution package wraps the file
tinymce.min.js in directory js/tinymce; the
versions include with XForms use a shallower directory tree.
When switching from one to the other, it's easy to produce
a bad link, e.g.
.../scripts/tinymce_4.7.
n/tinymce.min.js
instead of
.../scripts/tinymce_4.7.
n/js/tinymce/tinymce.min.js
or vice versa.
When this happens, an attempt to load the form produces an XSLTForms Loading ... message which never goes away.
Botch the namespace declarations.
The error message “Schema for namespace unknown (prefix:tlrr) not defined for type f-single-value” indicates a problem with the binding of the element we want to edit to the indicated type.
It does not, however, mean that the schema was not loaded, at least not in the form indicated. The key words here are “namespace unknown (prefix:tlrr)”, which suggest a namespace prefix-binding issue.
The namespace prefix should be bound both in the main form and in the subform. (Even though the error does not arise when the subform is run as a stand-alone form and lacks any binding for the prefix. This makes no sense to me.)
This section reports on currently unresolved issues. When an issue here is resolved, relevant information should be added above. (Issues listed here but marked Resolved are have been left here for the moment, awaiting the editorial work needed to identify the crucial points and rework the text above.)
Currently (12 June 2018), editing with TinyMCE works more or less well for text already present in the document. But if one wishes to add (for example) new text after a tagged phrase at the end of a paragraph, it is difficult (or impossible) to position the cursor there.
As is sometimes the case, it's difficult in a markup-free interface to distinguish between positioning the cursor before and after a given tag.
Question: would providing explicit ::after
pseudo-elements for every element make it easier?
Currently (12 June 2018), selecting a phrase and clicking the
button to tag it as a particular kind of element seems invariably
to introduce a paragraph break after the new element.
Currently (12 June 2018), TinyMCE reacts very very slowly to each keystroke; it is easy to get several characters ahead.
The form behaves as if I had set incremental="true"
,
even after I explicitly flagged incremental="false"
on the textarea widget used for TinyMCE.
Currently (11 June 2018), the cursor moves to the beginning of a containing element, or the beginning of the TinyMCE editing window, pretty much after every keystroke.
For example:
the
note in the date field of trial 1 (ZAA) reads “On the date see Cic. Att. 12.5b.”
When the user opens the TinyMCE widget to edit the date field,
positions the cursor before the word “date”,
and types “rationale” (as the first step in changing
the text to read “On the rationale for this dating ...”),
the result is that the r remains before the word date
and the rest of the word is placed in mostly reversed order at
the beginning of the
date
field.
Some of the toy examples exhibit the same behavior, others not.
Question: Is the external schema the only difference here?
Question: Is this independent of TinyMCE versions, or version-dependent?
Resolved.
Currently (11 June 2018), the TinyMCE subform appears to be
loading and displaying the element being edited in accordance with
the CSS provided: .
However, attempts to save the result of TinyMCE editing fail. Clicking the Done editing date (Humilis) button has no visible effect.
Insertion of messages in the relevant action sequence shows
that the sequence was failing on
<xf:delete context="instance('buffers')/frob/node()"/>
.
Things improve when this is replaced with
<xf:setvalue ref="instance('buffers')/frob" value=" '' "/>
The value should always be a string in any case, so setvalue
is not wrong.
Parsing the serialized-XML string fails with a "Cannot parse XML" message,
on the action <xf:setnode ref="." inner="instance('buffers')/frob"/>
:
Further diagnostics suggest that the intermediate buffer is not
being updated successfully, so that what is presented to
the setnode
element is an empty string.
Switching to the instruction <xf:setvalue
ref="instance('buffers')/frob"
value="string(context()/frob)"/>
resolves the problem.
Solved.
Currently (11 June 2018), the content of the date
element is not being styled as specified in tlrr-tinymce.css.
Is this because the stylesheet is not being loaded?
Or because TinyMCE is not seeing the elements?
Diagnosis: Under Resources, the browser's developer tool shows that tlrr-tinymce.css has been loaded. (It's hidden as a sub-item under edit.sh for reasons I don't understand.) So it's not that the stylesheet is not loading.
Diagnosis: Under Elements, the browser's
developer tool shows that TinyMCE appears within an iframe containing
an HTML document. The body of that document should contain
the date
element being edited, but instead it shows
as containing just a p
whose contents are the string
value of the date
element.
Solved.
Currently (11 June 2018), the content of the date
element is not being parsed correctly (or at all) by TinyMCE.
Diagnosis: xf:setnode
with
a value constructed in the following way does not work.
<xf:setnode ref="." inner="concat('<frob>', string(instance('buffers')/frob), '</frob>')" />
Instead use
<xf:delete ref="./child::node()"/> <xf:insert context="." origin="instance('buffers')/frob"/>
Q. Would setnode work with a different value?
Tasks to be undertaken before work on this document is finished (not necessarily in this order):
Make a minimal demonstration subform.
Make the intended subform work in Dexter.
In the freestanding XForm for TinyMCE, in the small demo subform, and in the Dexter subform, systematically introduce changes and errors:
script
element (inside model
,
outside).script/@src
attribute.
(Does it need to be within XSLTForms?)script/@data-version
attribute.model/@schema
.xf:HTMLFragment
be
the base type, or can it be the base type of the base type?xsd:restriction / @xsltforms:rte
attribute.xf:bind
.xf:textarea/@mediatype
.For each change, note the behavior exhibited in the browser.
File problem reports for the TinyMCE / XSLTForms compatibility issues found:
This involves constructing minimal reproducible examples, identifying the version of TinyMCE at which things break, and attempting to identify the proximate cause of the problem.
Last updated 11 June 2018
Photo "Foro romano in crepusculo
© 2013 by MauroPPP;
some rights reserved.