In the previous section we saw what RNG does for Mozile. This section will go into more detail, particularly about Mozile Editing Schemas (MES).
Mozile 0.8 is designed to be an XHTML and XML+CSS editor. At the current time XML editing is not complete, however many of the features required to support XML editing are already in place.
A key difference between a rich-text document and an XML document is that the XML document has more structure. This is often called "semantic" structure, because XML tags carry information about the meaning of their contents. A rich-text file might have a selection of text which is bold and in large font. When a person reads the document she will recognize the selection as a headline, because of its format and position, and she will expect the headline to be a very brief summary of the rest of the document. However, that information about the meaning of the bold text isn't readily available to a computer program. In an equivalent XML document the selection will be marked with a
headline tag, and a stylesheet would be used to display it as bold and in large font. This way both the person and the computer can easily pick out the headline.
A computer might not understand the meaning of the headline, not in the way a person does, but it can understand the structure of an XML document if it's properly described. There are several ways to describe XML structure, including Document Type Definitions (DTD) and the XML Schema language. However many people prefer RelaxNG, and this is the method that Mozile supports.
XML stands for eXtensible Markup Language; it's really a set of rules for creating particular markup languages. We usually call a particular XML markup language an "XML dialect". Examples include XHTML, DocBook, MathML, SVG, OpenDocument, etc. A RelaxNG schema describes the structure of the XML dialect. It tells us the elements, attributes, and text nodes which are allowed, and the patterns in which they can occur. One RNG schema describes all the documents that share the same XML dialect.
For an XML dialect with a small number of different tags and very strict rules, the RNG schema will be small. On the other hand, XHTML and DocBook have a large number of different tags, and the tags can be mixed quite freely, so their RNG schemas are large and complex.
Once you have an XML document and an RNG schema, you can check the document against the schema to make sure that it's valid. This is called validation, and Mozile has some support for validating XML documents with RNG schemas. We're working on improving it.
But when you're editing a document with all this rich structure, it's less important to know that the document was valid, and more important to know that document stays valid as you make your changes. This is the main reason that Mozile uses RNG.
This is what we mean by "structured editing". Using the information from an RNG schema, Mozile is able to present the user with editing options that are appropriate to the structure of the document, and to verify that the changes do not make the structure invalid. For example, inside an XHTML
p paragraph you are allowed to insert
span tags, but not another
p tag, or a
script tag or an
li tag. Doing so would violate the RNG schema for XHTML, and so Mozile doesn't allow it. Mozile doesn't even present the option.
Before an RNG schema can be used, Mozile has to parse it. The
mozile.useSchema() function starts the parsing operation.
mozile.rng module contains a number of classes. Every time a new schema is parsed, a new
mozile.rng.Schema object is created. It loads the RNG file and then creates a new object for each RNG element it encounters.
mozile.rng.Node is the parent class for these objects, and there is a sub-class for each element type available in RNG:
Each of the
mozile.rng.Node objects has a
validate() method, which takes a node from the document and validates it against part of the RNG schema. The validation applies to the target node and all of its descendants. In this way we can validate the whole document or just pieces. Information about the validation operation is passed between objects by a
mozile.rng.Validation object. The validation object is returned when the validation operation is complete. It has an
isValid Boolean property which indicates whether the validation succeeded or failed. It also has
getFirstError() methods which can be used to get information about the validation operation once it is complete.
mozile.rng.Node objects also provide methods, such as
mayContain(), which we can use to determine what kinds of nodes are allowed by the RNG schema.
Of the RNG objects, the
mozile.rng.Element object is the most important. Validation, events, and editing commands all focus on the elements of the document.
A RelaxNG schema provides information about the structure of an XML document. We can use this information to figure out which commands should be allowed. But there are many cases when we need more specific information about which editing commands are allowed, and how those commands function.
For this we use another XML dialect called Mozile Editing Schema (MES). RNG files are XML, and they allow "annotations" in the form of XML element from other namespaces. We mix RNG and MES elements in the same file, using different namespaces, so MES can take advantage of the RNG structure.
There are five MES elements:
command- describes an editing command. This is the most important MES element.
group-groups commands together. Associated with the
separator- divides commands.
define- used like the RNG
defineelement to group elements for reuse. Must have a
ref- used like the RNG
refelement to refer to definitions. Must have a
Mozile's MES system is aware of the rules for RNG
ref elements, and it will follow RNG references as it searches for
command elements. However the RNG parser will ignore all of the MES elements.
mozile.useSchema() function is called, the RNG schema will be loaded and parsed. Mozile will create the RNG objects. Then it will run through the list of all the
mozile.rng.Elements and for each one of these it will generate the appropriate commands and attach them. The command names must be unique, and Mozile will not generate a new command if the name is already taken.
Command object which is generated from the MES.
name- a unique name which identifies the command or group among all the others. Should only contain alphanumeric characters.
label- a short description which will appear on buttons and menu items.
priority- a number which allows you to have some commands run before others. The default is 0, with higher numbers meaning higher priority. For example,
insertTexthas priority 10. Negative values can be assigned.
image- a path to an icon for the command in the
imagesdirectory. Should be a 16x16 pixel PNG image.
tooltip- a longer description of the command or group.
These attributes apply only to
class- the name of a command class, as described above. When the MES is parsed by Mozile, a new instance of the class will be created. The default is
text- text content to be inserted by the
Insertcommand. To specify Unicode characters, use this syntax:
. See the Unicode Charts for the character codes. If an
elementis given it will be used instead.
element- the tag name of an element to be created by
Replacecommands. Inside a
scripttag you can set
accelis an "accelerator" or keyboard shortcut combination. In order to work across platforms we usually use the "Command" keyword, which translates to Ctrl on Windows and Linux and ⌘ on Mac OS X. The sequence should be "Command-Meta-Control-Alt-Shift-Character", where "Character" is a single upper case letter (like "A") or the name of a key (like "Enter"). Key names include: "Backspace", "Tab", "Clear", "Return", "Enter", "Pause", "Escape", "Space", "Page-Up", "Page-Down", "End", "Home", "Left", "Up", "Right", "Down", "Insert", "Delete", and "F1" through "F12". Examples of accelerators include: "Command-U", "Command-Shift-U", "Command-Alt-Shift-U", "Shift-U", "Enter", "Command-Enter", "Shift-Left", etc. You can also specify a space separated list of accelerators, any of which will trigger the command: "Enter Command-Enter Command-Alt-Enter".
makesChanges- a "change code" which indicates what kinds of change the command makes. Can be
"none"if no changes are made;
"state"if only the undo state is changed;
"text"if only text node data is changed; or
"node"which includes changes to elements and attributes. A
"text"change includes a
"state"change, and a
"node"change includes both of these. The default value is
watchesChanges- indicates which change codes the command is sensitive to. The possible values are the same as those for
makesChanges. The default is
"node", which means that the command will update only when the node changes, and not on a
"text"value means updates on
"text"changes, but not
"state"changes. A value of
"none"indicates that this command never needs to update. This value is used by the
respond()method to determine if a command's GUI representation should be checked for updates.
Insertcommand will perform a remove operation before inserting, removing any contents of the selection. When
falseit will move the contents of the selection into the inserted element, which is useful for elements like links (
atags). The default is
Wrapcommand will create wrappers inside other wrappers if told to do so. When
falseit will remove the wrapper if called inside another wrapper. The default is
target- used by the
Stylecommands to determine which node will be manipulated. Values can be:
any- the first node found.
text- the first text node found.
element- the first element found.
block- the first element which counts as a block-level element (usually because it has CSS
localName [tagName]- the first element which has a local name matching the given
tagName(matching is done lower case).
scripttag you can set
this.targetto a function which returns a node. Any time a target is used a
directioncan be specified.
direction- used wherever
targetis used, this indicates the direction to use when searching for the target. Can be
collapse- used by
Navigateto determine whether the selection should be collapsed after it is moved. Can be
Replacecommand will copy all of the attributes of the target element into the replacement element. When
falseit will not. The default is
className- used t o set the
classattribute of a created element.
styleName- determines the CSS style to be set for a created element or for the
styleValue- determines the new CSS value to be set for a created element or for the
command elements can also have child elements. Both of these are optional, and no more than one of each should be included.
element- the first child element inside the
elementtag will be used as a template. Commands like
Insertwill clone this template and insert the clone into the document.
Wrapwill then append other elements as children of the clone. If you set the
trueMozile will copy the element into the current namespace -- this is important when dealing with HTML elements.
Creating your own commands is easy. Here are some examples.
Example 2.3. Creating a
Here is a simple command that will wrap a selection in a
span tag and use CSS to underline the text.
<m:command name="underline" class="Wrap" accel="Command-U" element="span" styleName="text-decoration" styleValue="underline" label="Underline" image="silk/text_underline" tooltip="Underline text"/>
Example 2.4. Creating a
This command will underline selected text and make it red.
<m:command name="underline" class="Wrap" accel="Command-U" label="Underline" image="silk/text_underline" tooltip="Underline text"> <m:script> <![CDATA[ this.element = mozile.dom.createElement("span"); mozile.dom.setStyle(this.element, "text-decoration", "underline"); mozile.dom.setStyle(this.element, "color", "red"); ]]> </m:script> </m:command>
The command has the unique name "underline". It uses the
mozile.edit.Wrap class to do most of the work. You can use the keyboard shortcut "Command-U" to trigger the command; on Windows and Linux this means Ctrl+U, and on Mac OS X it means ⌘+U. The icon for the button will be
images/silk/text_underline.png, the label will be "Underline", and the tooltip will be "Underline text", if these are displayed by the GUI. The script tag says to create a new
span element and set the "text-decoration" style to "underline".
Example 2.5. Creating a
The same thing can be done using the
element tag, with a few reservations.
<m:command name="underline" class="Wrap" accel="Command-U" label="Underline" image="silk/text_underline" tooltip="Underline text"> <m:element import="true"> <span xmlns="http://www.w3.org/1999/xhtml" style="text-decoration: underline; color: red"/> </m:element> </m:command>
span element has the attribute
xmlns="http://www.w3.org/1999/xhtml", which indicates that it is an XHTML element. However, since the
import attribute is set Mozile will import a copy of the element into the current document and namespace, whatever that might be. When dealing with HTML document
import is important! For XHTML and XML documents you may want to mix namespaces.