Mozile 0.8 Documentation


Table of Contents

1. Introduction
What is Mozile?
Why Use Mozile?
Getting Started
Getting Mozile
Using Mozile
Configuring Mozile
Saving Changes
Integrating Mozile
Learning More
2. Mozile Design
How Does Mozile Work?
Mozile's Organization
Mozile Directories
Mozile Modules
Mozile Tests
Loading Mozile
Editing
Events
Selection
RelaxNG
Global and Default Commands
Commands
States
Text and White Space
RelaxNG
Semantic Structure
RelaxNG Objects
Mozile Editing Schema
Graphical User Interface
3. Mozile Development
Helping to Develop Mozile
What You Need
Document Object Model
DOM
XML
XPath
Mozilla
Internet Explorer
Opera
Safari
JavaScript
The Language
Debugging
Coding Style
Testing with JsUnit
Documentation with JSDoc
Documentation with DocBook
Packaging Mozile

List of Examples

1.1. Including the mozile.js script
1.2. Making an element editable with contentEditable
1.3. Making an element editable with mozile.editElement("editor")
1.4. Configuring Mozile
1.5. Configuring Mozile with a file
1.6. Configure some basic Mozile options
1.7. Configure Mozile to edit without an RNG schema
1.8. Configure Mozile for XML editing
1.9. Configuring Mozile
2.1. Configure Mozile using core.js
2.2. A RelaxNG definition
2.3. Creating a command
2.4. Creating a command using script
2.5. Creating a command using element
3.1. Simple XPath example
3.2. XPath example with namespaces
3.3. JavaScript constructor
3.4. JavaScript constructor inheritance
3.5. Test example from tests/dom/dom.js
3.6. JSDoc example
3.7. Using Ant
3.8. Compiling Mozile
3.9. Building Mozile

Chapter 1. Introduction

What is Mozile?

Mozile adds what-you-see-is-what-you-get (WYSIWYG) editing to your web pages. The name "Mozile" comes from Mozilla Inline Editor. As of version 0.8, Mozile is no longer limited to Mozilla browsers like Firefox. But it's still an inline editor, which means that it allows you to edit the content of a web page without switching to another program, embedding an IFRAME, or using a special mode. With Mozile you can just click in the page and start editing, like you're using a word processor.

Mozile is an open source project, and is distributed free of charge under the terms of our license. Please see our homepage at http://mozile.mozdev.org for more information. Mozile is written using JavaScript, and version 0.8 is designed to work in most modern browsers, including Mozilla Firefox and Internet Explorer. However Mozile is still in development, and only Firefox 1.5 and Internet Explorer 6 are currently supported.

Why Use Mozile?

For a long time, people have been trying to edit rich content in their web browsers using HTML text boxes and awkward markup syntax. As wikis and content management systems become more popular, more and more content is being generated in our browsers. Why can't we have WYSIWYG editing, like in a word processor?

Without Mozile, there are two incompatible ways to do rich text editing in a browser. Internet Explorer allows you to set a contentEditable attribute on an element, and then edit the contents. Firefox and other Mozilla browsers have a setting called designMode, which lets you edit the whole page, like the old Netscape Composer. The Internet Explorer method is missing some important features, like redo. The Firefox designMode won't let you pick which parts of a page are editable. But the worst part is that both of these methods are trapped in the last decade: they use out of date HTML code like font tags, and they have no idea what CSS is. You're limited to the small number of built-in editing commands.

Mozile overcomes these problems in three ways:

  1. Mozile works the same way in both in Firefox and Internet Explorer, and we're working on support for other browsers as well.
  2. Mozile uses pure DOM methods to manipulate the document, so the code you get is nice, clean X/HTML, XML, and CSS.
  3. You can customize commands and add new ones, so you can edit the document in any way that JavaScript, DOM, and CSS allow.

Getting Started

Here's how you can get a copy of Mozile, modify your pages to use Mozile, and configure Mozile's features.

Getting Mozile

You can download the latest version of Mozile from our web site at http://downloads.mozdev.org/mozile/. Simply unzip the archive and copy the files to an accessible location on your web server.

The Mozile distribution includes demonstrations, documentation, a testing system, and tools for development. If you don't want to use these features, you can safely delete those folders: demos, doc, jsunit, tests, and tools. It is important to leave the images, lib, and src directories intact.

Using Mozile

You include Mozile in your web pages by adding an HTML script tag to the head of your HTML page. The script tag should look like this:

Example 1.1. Including the mozile.js script

<script type="text/javascript" src="path/to/mozile.js"></script>

The src attribute must be a valid path from the web page to Mozile's mozile.js file.

Next you have to specify which parts of the document are editable. You can do this in two ways:

  • Set the contentEditable attribute to true to make that element editable.

    Example 1.2. Making an element editable with contentEditable

    <div contentEditable="true">
    	<p>This paragraph is editable.</p>
    </div>

  • Use the mozile.editElements("editor") command to edit all elements with the class="editor" attribute. See the configuration section below for more configuration options.

    Example 1.3. Making an element editable with mozile.editElement("editor")

    <div class="editor">
    	<p>This paragraph is editable.</p>
    </div>

At this point you should be able to edit your web page with Mozile in any supported web browser.

Configuring Mozile

The default settings enable basic editing. But with another script element you can configure Mozile to suit your needs.

Example 1.4. Configuring Mozile

<script type="text/javascript" src="path/to/mozile.js"></script>
<script type="text/javascript">
	// Configuration Options
</script>

If you are using the same configuration options in many files, you can include them in a separate config.js file which all your pages can share.

Example 1.5. Configuring Mozile with a file

<script type="text/javascript" src="path/to/mozile.js"></script>
<script type="text/javascript" src="config.js"></script>

Here are some of the most important settings and configuration commands you can use with Mozile. Below are some example and a longer description of how they work. Configuration for the save system is described in the next section.

Important Configuration Settings

  • mozile.root - Indicates where the mozile directory is relative to the document. Mozile tries to set this automatically, but sometimes it is necessary to set the root manually.
  • mozile.useDesignMode - When true the document.designMode will be used in browsers that support it. When false designMode is not used, and Mozilla/Firefox users will have to press F7 to enable an editing cursor. Because of bugs with designMode the default is now false.
  • mozile.alternateSpace - Set this to "\u00A0" (a non-breaking space) if you want Mozile to insert spaces every time the user presses the space bar. This makes Mozile behave more like a word-processor. If no mozile.alternateSpace is specified, Mozile will keep the HTML code clean and ignore extra spaces. See the notes on white space below for more details.
  • mozile.emptyToken - By default this is set to "\u00A0" (a non-breaking space). In order to avoid display bugs, the empty token is automatically inserted into any empty element which has CSS display: block. See the notes on white space below for more details.
  • mozile.help - The URL of a help page which will be opened by a help command. Defaults to mozile.root/doc/html/index.html.
  • mozile.debug.alertLevel and mozile.debug.logLevel - These can be set to "suppress", "warn", "inform", or "debug", in order from quietest to most verbose. They indicate what kinds of messages the user should be alerted to, and which should be logged. To turn off all messages use "suppress".
  • mozile.defaultNS - Sets the default namespace URI. Should be null for HTML documents. For XHTML documents use "http://www.w3.org/1999/xhtml". For XML documents use the XHTML namespace or the default XML namespace.

Important Configuration Functions

  • mozile.require(module name) - Makes sure that a Mozile module is loaded.
  • mozile.editElement(element or id) - Makes a single element editable.
  • mozile.editElements(list or class) - Makes a set of elements editable.
  • mozile.editDocument() - Makes the whole document editable.
  • mozile.protectElement(element or id) - Protects a single element from editing.
  • mozile.protectElements(list or class) - Protects a set of elements from editing.
  • mozile.useSchema(schema file) - Tells Mozile to use an RNG schema to structure editing. The schema file argument is either an absolute path or a relative path from mozile.root to the schema file.
  • mozile.enableEditing(rich) - Enables editing without using an RNG schema. The rich argument can be false for basic text editing, and true for rich editing of elements. You probably want to use either mozile.useSchema() or mozile.enableEditing() but not both.

Here are some examples of configuration settings.

Example 1.6. Configure some basic Mozile options

<script type="text/javascript" src="path/to/mozile.js"></script>
<script type="text/javascript">
  // Specify the "mozile" directory.
  mozile.root = "path/to/";
  // Insert spaces like a word processor.
  mozile.alternateSpace = "\u00A0";
  // Wait until the document is loaded to run these commands.
  window.onload = function() {
    // Make all elements with class "editor" editable.
    mozile.editElements("editor");
    // Use the XHTML RNG schema.
    mozile.useSchema("lib/xhtml.rng");
  }
</script>

Example 1.7. Configure Mozile to edit without an RNG schema

<script type="text/javascript" src="path/to/mozile.js"></script>
<script type="text/javascript">
  // Enable rich text editing without an RNG schema.
  mozile.enableEditing(true);
  // Wait until the document is loaded to run these commands.
  window.onload = function() {
    // Make all elements with class "editor" editable.
    mozile.editElements("editor");
  }
</script>

Example 1.8. Configure Mozile for XML editing

<script type="text/javascript" src="path/to/mozile.js"></script>
<script type="text/javascript">
  // Set a default XML namespace.
  mozile.defaultNS = "http://somewhere.com/xml-namespace";
  // Make the whole document editable.
  mozile.editDocument();
  // Wait until the document is loaded to run these commands.
  window.onload = function() {
    // Specify an RNG schema file to use.
    mozile.useSchema("schemas/custom-schema.rng");
  }
</script>

The most important setting is mozile.root, which tells Mozile where to find the mozile directory containing all of the Mozile files. Mozile tries to automatically detect the location of the mozile directory by looking at the src attribute of the script tag which loaded mozile.js into the page. This should work, as long as the mozile.js is in the mozile directory. If you have moved the mozile.js file, then you must change the mozile.root setting so that it points from the web page to the mozile directory.

Another important function is mozile.require(). It makes sure that the named module has been loaded into Mozile. If one module depends on others, the dependencies will be loaded automatically. If there is a module that you do not want to load, you can either remove the mozile.require() line, or you can "comment out" that module by adding // to the beginning of the line. The // tells JavaScript to ignore the rest of the line after the double-slash, treating it as a comment and not as code.

After the modules are loaded, you can change other settings which apply to the loaded modules. In most cases you won't need to change any, but you can look at Mozile's API documentation for settings which you might like to change. You probably want to change the setting after the module has been loaded, so that you change doesn't get overridden.

Caution

If you have commented out a module, be sure to also comment out any settings that apply to that module. Otherwise there may be errors that will stop Mozile from running.

You can use the mozile.editElement(), mozile.editElements() and mozile.editDocument() methods to determine which parts of the document are editable. You can call mozile.editElement() and mozile.editElements() as many times as you like, but it only makes sense to call mozile.editDocument() once.

You can use the mozile.useSchema() and mozile.enableEditing() commands to determine how the document will be edited. It's better to use mozile.useSchema() when possible (see below) but sometimes the unstructured editing of mozile.enableEditing() is the right choice. There aren't many cases where you would want to use both of them.

A window.onload = function() { ... } block is used to specify code which is supposed to be executed only after the document has finished loading. It is usually best to put mozile.editElement(), mozile.editElements(), and mozile.useSchema() commands inside this block.

Saving Changes

The changes that Mozile makes to the document are reflected immediately in the DOM. Mozile provides tools to help you save the content of the document. Currently we have a simple "display" save method and one using HTTP POST. Save functionality is provided in mozile.save module. These save options are for web site designers to configure how Mozile works in their sites. In the future we plan to include more options for Mozile users to save their content.

The default save method is mozile.save.display, which takes the content of the document and displays it as source code using the mozile.gui.display() method. For the default HTML Toolbar GUI this means displaying the source in a new window. You can then copy and paste the source code as you like.

The mozile.save.post method uses XMLHttpRequest to send the content of the document to a web server using the HTTP POST operation.

General Save Configuration

  • mozile.save.target - Indicates the node which should be saved. The default is document.
  • mozile.save.format - Used to format the saved data. Can be "uppercase" or "lowercase", which will change the case of all tag names. Defaults to null which means no reformatting is done.
  • mozile.save.warn - When true (the default) the user will be warned before she leaves a document with unsaved changes.
  • mozile.save.method - Sets the method to be used by save operations.

Save Configuration for mozile.save.post

  • mozile.save.post.async - When true the POST is done asynchronously.
  • mozile.save.post.uri - The URI to be POSTed to. Security restrictions on XMLHttpRequest require that the URI must be in the same domain as the web page it is called from.
  • mozile.save.post.user - Optional. The name of the user to POST as.
  • mozile.save.post.password - Optional. The password for the user.
  • mozile.save.post.showResponse - When true the result of the POST operation will be displayed. This is meant to be used for debugging, and it defaults to false.

Here is an example using all of these save configuration options.

Example 1.9. Configuring Mozile

<script type="text/javascript" src="path/to/mozile.js"></script>
<script type="text/javascript">
  mozile.require("mozile.save.post");
  mozile.save.target = document;
  mozile.save.format = null;
  mozile.save.warn = true;
  mozile.save.method = mozile.save.post;
  mozile.save.post.async = true;
  mozile.save.post.uri = "http://somesite.com/save_data.php?id=Mozile";
  mozile.save.post.user = null;
  mozile.save.post.password = null;
  mozile.save.post.showResponse = false;
</script>

When you call the mozile.save.save() or mozile.save.saveAs() methods, Mozile will execute the save() or saveAs() methods of the current mozile.save.module.

Of course, you can write your own code for saving changes. The first step is to create a new instance of the mozile.save.Method class. You can also make use of the mozile.save.getContent() method, which takes a Node or a Document as its argument and returns a string representation.

Warning

You must take steps to ensure that the data submitted by your users is safe. Failure to do so could result in a cross site scripting (XSS) security vulnerability.

An XSS vulnerability can occur when your site accepts content generated by one user and displays it to another user without ensuring that the content does not include malicious code. For example, an attacker posts a message to a message board with malicious JavaScript embedded. The user loads the message in his browser and the attacker's JavaScript is executed: it reads the user's cookie or login information. Then the malicious code sends that information back to the attacker by setting the document.location to a URL which encodes the data, or by other means. Now the attacker has access to the user's cookie or login information, which may be used to hijack the user's account on the web site or to violate the user's privacy in other ways.

Mozile does not cause XSS vulnerabilities. Any vulnerability that exists in a web site can be exploited without Mozile. However, Mozile is usually used to edit rich HTML and submit that HTML back to a web site. Any web site that accepts HTML content from its users must be very careful to sanitize submitted data before displaying it.

JavaScript code can be embedded in script tags, src or onload attributes, or even in CSS with IE's expression statement. Not just JavaScript, but objects like Flash applets can also be used for XSS attacks. There's a wide variety of XSS attack vectors. Be very careful.

Every site has different needs and there is no one best way to sanitize HTML. Different tools exist for different programming languages which may be used to run a dynamic web site. Most CMS systems provide tools for sanitizing submitted data. Although we cannot offer specific advice, here are some resources that should be generally useful.

XSS Resources

  • HTML Tidy - Does not sanitize data, but formats HTML in a standard way. This will make it easier for sanitizers to do their work.
  • XSS Cheat Sheet - A long list of XSS vectors.
  • Bitflux XSS Prevention - An example of using regular expressions to sanitize HTML.

Programmers should make it a habit to check all data entered by users. The web is no exception.

Integrating Mozile

Mozile can work side-by-side with other JavaScript in your pages. Of course, integration will take some extra work, and you'll need to know more about how Mozile functions than the average user. Here's some information to get you started.

The first step will be to remove the parts of Mozile that you don't need. If you have your own GUI, then disable the mozile.gui module. If you have your own system for saving, disable the mozile.save module. The section on packaging Mozile below has more details.

The second step will be to tell Mozile what to do. Our development documentation includes the full Mozile API, generated from the source code. Here are some of the methods you might be interested in.

  • mozile.execCommand(name, arguments...) - Execute a Mozile command. For the list of available commands see mozile.edit.allCommands.
  • mozile.edit.enable() and disable() - These turn Mozile editing on and off throughout the whole document.
  • mozile.save.save() and saveAs() - Use Mozile's save system.
  • mozile.dom.selection.get() - Gets a cross-browser selection object which behaves like the Mozilla Selection object.

Learning More

The rest of this document includes more information on the design and development of Mozile. There are several other places where you can find more information and ask any questions you have:

Chapter 2. Mozile Design

How Does Mozile Work?

The Mozile code is written entirely in JavaScript, and editing operations are done entirely through the Document Object Mode (DOM). One of the benefits of this is that Mozile is not limited to editing HTML and XHTML documents, and we expect to support all sorts of XML documents in the future.

This section includes information on Mozile's design, which will help you understand how Mozile works "under the hood". If you're interested in customizing Mozile or in helping to develop it, read on! You don't need to read this section to use Mozile for basic editing. If there information here isn't enough, continue on to the development section.

Mozile's Organization

Mozile Directories

The root mozile directory contains the following sub-directories:

  • demos contains demonstrations of Mozile in action.
  • doc contains documentation, including this documentation and API documentation generated by JSDoc.
  • images contains image files used by Mozile.
  • jsunit contains the JsUnit testing framework, which drives Mozile's unit tests.
  • lib contains RelaxNG schemas which are used for structured editing and validation.
  • src contains the JavaScript code which makes Mozile work.
  • tests contains the unit testing code which verifies that Mozile is working correctly.

Most of these directories don't require further description, but a few do.

Mozile Modules

The layout of the src directory corresponds to Mozile's concept of code "modules". JavaScript has many virtues as a programming language, but it also lacks many important features that we take for granted in other languages. One of these features is support for code libraries, and that means namespace support is also lacking. Because there is no built-in way to package code into libraries and import the libraries into your code, JavaScript developers have come up with their own methods and conventions.

Mozile follows the lead of JavaScript toolkit projects such as Dojo. One of the basic data types in JavaScript is the Object, and objects can have properties which are also objects. Properties are accessed using the common "dot" notation, so we can have a hierarchy of objects such as parentObject.childObject.grandchildObject.foo, where foo is some property. This looks a lot like the way packages are accessed in Java, for example, and for most of our purposes it behaves the same way.

Because there are no namespaces in JavaScript, we keep Mozile's code separate from other code running in the same page by adopting a convention. We declare a global mozile object in the page, and all of the rest of our variables will be assigned as properties of the root mozile object or its descendants. We try to organize our code in a sensible way, like any code library would.

This is how we get Mozile's code modules. The file src/dom.js defines the mozile.dom object, which has as its children a bunch of properties like mozile.dom.TEXT_NODE, and a bunch of functions like mozile.dom.insertAfter() and mozile.dom.removeChildNodes(). (We often use "function" and "method" as synonyms, because the usual differences between them don't make as much sense in JavaScript as in other languages.)

Also, the file src/dom/TreeWalker.js defines an object called mozile.dom.TreeWalker. In this way modules can have sub-modules.

The mozile object itself is defined in src/core.js. This file has all the code required to bootstrap Mozile. After it is loaded, the mozile.loadModule() function is used to load other modules.

Every module file should begin with a license block, a JSDoc summary, and mozile.require() and mozile.provide() statements. The mozile.require() call will make sure that an dependencies are loaded before the rest of the module is loaded. The mozile.provide() call indicates what properties and functions the module will provide, but currently it has no real effect.

Mozile Tests

All of Mozile's testing code is kept in the tests directory, and the JsUnit code which runs the tests is kept in the jsunit directory.

Within the tests directory there are sub-directories for most of the modules. The tests/core directory is the default location for tests when they don't merit their own directory. The tests/shared directory contains the code which is common to all the tests.

Within each sub-directory, the test files are named either after the module which they test, or specific classes, objects, or functionality which belongs to that module. The point is simply to make it clear what is being tested by the particular test file.

The overriding goal is avoid any surprises by keeping the code separated by its function and storing it where you would expect to find it.

Loading Mozile

In this section we describe the steps that Mozile goes through as it loads itself and its modules into a web page.

Mozile loading starts when the user accesses a web page which includes an HTML script tag with a src attribute which points to the mozile.js file. Most browsers will immediately access the JavaScript file and evaluate its contents, although some may wait until the full HTML page is loaded.

The mozile.js file includes the code from all the Mozile modules, and has been compressed by removing all comments and unnecessary white space. The compression saves bandwidth for users downloading Mozile from your web site, but makes it very difficult for a person to read the code. The mozile-src.js file contains the same code as mozile.js, but has not been compressed. Both of these files are generated from the original modules in the src directory, which are divided into small parts and include lots of comments. When developing you should make changes to the original module files and not to the mozile.js and mozile-src.js files.

The most important module of all is mozile/src/core.js. It defines the root mozile object and the basic functions required to load other modules. For example, mozile.load() uses XMLHttpRequest to get the text contents of files. mozile.findRoot() is used to set the mozile.root variable if it hasn't already been set. mozile.loadModule() translates a module name like "mozile.edit.InsertionPoint" into the file path "mozile.root/src/edit/InsertionPoint.js". Then it fetches the file using mozile.load() and uses the JavaScript eval() function to evaluate the contents of the loaded file as code.

Caution

The eval() function call is made inside the mozile.loadModule() function. Because of JavaScript's scope rules, a global variable foo defined in a JavaScript file bar.js will become a local variable of mozile.loadModule, and not a global variable as might be expected. This is another good reason to always use the mozile object as a "namespace", since mozile.foo defined in bar.js will evaluate to mozile.foo, as expected.

JavaScript's eval() function evaluates from the beginning of the string to the end. When a mozile.require() call is encountered, it is immediately executed. mozile.require() will call mozile.loadModule(), which uses mozile.findModule() to check whether the named module has been defined already. If "mozile.edit.InsertionPoint" is given, then mozile.findModule() will test whether mozile, mozile.edit, and mozile.edit.InsertionPoint are all defined. If any one of these is not defined, then the mozile.edit.InsertionPoint module will be loaded. Once the required module (and all of its requirements, and so on) have been loaded, the evaluation of the original string will continue.

Caution

Mozile does not check for circular requirements, and so it is possible to create an infinite loop if two modules require each other.

When mozile.js has been completely loaded Mozile is ready to go. It's possible to call mozile.loadModule() or mozile.require() after this point to load modules dynamically, for example as part of a conditional statement. The mozile.load() function can be used to load other text files, although there are security limitations on the XMLHttpRequest system in most browsers which prevent loading files from web sites other than the one the page belongs to. For loading XML files you can also use the mozile.xml.load() function.

Since mozile.js is a single small file, it's ideal for Mozile deployment. But when developing Mozile it's usually more convenient to load the src/core.js file, set the mozile.root, and load the required modules dynamically. For example,

Example 2.1. Configure Mozile using core.js

<script type="text/javascript" src="path/to/src/core.js"></script>
<script type="text/javascript">
  // Specify the "mozile" directory.
  mozile.root = "path/to/";
  // Add modules as required.
  mozile.require("mozile.dom");
  mozile.require("mozile.xml");
  mozile.require("mozile.xpath");
  mozile.require("mozile.util");
  mozile.require("mozile.edit");
  mozile.require("mozile.edit.rich");
  mozile.require("mozile.event");
  mozile.require("mozile.save");
  mozile.require("mozile.gui");
  mozile.require("mozile.gui.htmlToolbar");
</script>

Editing

Once Mozile has been loaded, it acts in response to events triggered by the user. These can be mouse events, like clicking on a part of the document or a button, or keyboard events like typing or entering a command's keyboard shortcut. Mozile listens for these events and then uses them to trigger commands which make changes to the document.

Events

Events are part of the Document Object Model (DOM) defined by the W3C. Although there are differences between browsers in the way they handle events, the essentials are the same. The DOM is a representation of an HTML or XML document as a tree made up of nodes. There are many kinds of nodes, but we're usually only concerned with Text nodes and Element nodes, and sometimes Attr or "attribute" nodes. Any text displayed in the document will belong to a text node, and text nodes are the children of elements. Elements can contain text nodes as well as other elements. (We usually use the words "element" and "tag" as synonyms.) Every node has a parent node, until we get to the "root" of the tree, which is the documentElement. In HTML this is the html element. The parent of the documentElement is the document object, and it has no parent.

An event like a mouse click will start at the node that was clicked and travel up the tree from child to parent until it reaches the root. Keyboard events start at the documentElement or the body or the contentEditable container, depending on the browser, but they also work their way toward the root.

In order to catch the events as they pass through the DOM tree and do something about them, we need to assign "event listeners". The mozile.event.listen() method, called at the end of src/event.js, creates the event listeners and attaches them to the document.

Note

There are many different event types, such as "mouseclick" and "keypress", and mozile.event.listen() only assigns listeners for some of these. If you have a command that you want triggered by a certain event type, make sure that that type is included by mozile.event.listen().

The event listeners listen for the events, and when they "hear" one they pass the event object to mozile.event.handle(). mozile.event.handle() then does several things. It checks to see if the event took place in an editable area, which means that the original target node of the event has an ancestor with contentEditable="true" or it was marked as editable by mozile.editElement(). It can also pass the event to other functions and let them try to handle it. An event is "handled" when it triggers an appropriate action, like a command. Once the event is handled it will be cancelled using mozile.event.cancel(), and that will be the end of it. The event will stop moving through the DOM tree, and no other code will see it. If Mozile doesn't handle an event it will let the event keep travelling on its way.

The most important thing that events do is trigger commands, but there are a few more things to explain before we get to Mozile's command system.

Selection

A mouse click event will have an event.target property which indicates which node was clicked. Keyboard events don't have this property, so instead we determine the target node using the document's Selection object. The selection is displayed as a cursor or as highlighted text in the document.

Most of the time, browsers don't display a cursor in a web page. The arrows keys will move the whole page up and down. But when Internet Explorer detects the contentEditable attribute, or Firefox is in designMode, a cursor will appear. In Firefox you can also enable a cursor by pressing F7 to activate "Caret Browsing", which is an accessibility feature. In order to have a cursor displayed automatically, Mozile uses contentEditable and sometimes designMode. But that's all they are used for; the acutal editing operations are done using the DOM.

A selection can include a range of text and elements, or it can be "collapsed" to a single point. Related to the selection object is the Range object. Every selection contains at least one range, but a range does not have to be selected. The beginning and end points of a range or a selection are indicated by the pair of a node and an offset, which is an integer indicating where the point is within that node. In the case of a text node, the offset will be a number of characters, so you can select the third character of the text node. In the case of an element node, the offset will be the number of child nodes, so you can select the third child of the element node.

This description of nodes and offsets applies to Firefox and other browsers that comply with the W3C standard for ranges and selections. Internet Explorer has its own way of handling ranges and selections, which is incompatible. After a lot of work we've managed to make Internet Explorer behave according to the standards, for the most part. For the code that does this, see src/dom/InternetExplorerRange.js and src/dom/InternetExplorerSelection.js.

When mozile.event.handle() receives a keyboard event it looks for the current selection and uses the first node which contains both the start and the end of the selection as the target of the event. Once this is done, keyboard and mouse events are treated in the same way.

Mozile's mozile.edit.InsertionPoint (IP) object is related to ranges and selections. It also uses a node and an offset to indicate a location in the document. However, the insertion point code knows a lot more than the normal range and selection objects do about how to handle white space and which elements are allowed to contain text. So some of Mozile's code just uses the range and selection objects, while other code uses insertion points for finer control.

RelaxNG

Once mozile.event.handle() knows which node is the target of the current event, it needs to decide how to handle the event. Mozile is a context sensitive editor, which means that it's smart enough to tell the difference between editing a paragraph and editing a list, for example. We call this "structured editing"; Mozile knows about the way documents are structured, and helps maintain that structure as changes are made.

Mozile is sensitive to the structure of documents, but you have to tell it what the structure should be. We do this using a "schema language" called RelaxNG (RNG). All you have to do is tell Mozile where to find an RNG file for the document type you want to edit, and Mozile will do the rest.

Here is an example of how RNG describes the structure of an a element:

Example 2.2. A RelaxNG definition

<define name="a">
  <element name="a">
    <attribute name="href"/>
    <text/>
    <ref name="Inline.model"/>
  </element>
</define>

This bit of RNG says that an a element must have an href attribute, and that it's allowed to contain non-white-space text. It also refers to another definition named "Inline.model", which will have more details about what the a element is allowed to contain. RNG files can also contain Mozile Editing Schema information, which adds details about the commands associated with various elements. More about that below.

After mozile.event.handle() finds the target of an event, it looks for a RNG representation of that node. This will be a mozile.rng.Element object. If the node is a text node, it uses the parent element instead. Mozile stores all sorts of information from the RNG schema about the document's elements, and this includes what commands are associated with what kinds of elements.

So mozile.event.handle() takes an event, figures out the target node, and then figures out what mozile.rng.Element object matches up with the target node. Once it has this information, it calls the mozile.rng.Element.commands.trigger() method, and that method calls the commands associated with the mozile.rng.Element.

Note

Mozile 0.8 will also support the validation nodes and documents against RelaxNG schemas, however the validation system is not yet complete.

Global and Default Commands

Not all commands are attached to RNG system. Some commands are "global" and apply to the whole document, like Save, Undo, and Redo. These commands are grouped together in the mozile.edit.commands object. You can add a global command using mozile.edit.commands.addCommand().

You don't always need a RelaxNG schema in order to edit documents with Mozile. mozile.event.handle() first tries to use the RNG system, and if the event has not been handled it will test a set of default editing commands.

Default editing commands are grouped together in the mozile.edit.defaults object. You can add new ones using the mozile.edit.defaults.addCommand() method, and they are handled using the mozile.edit.defaults.trigger() method. However the easiest way to add a good selection of default editing commands is to use the mozile.enableEditing(rich) command in your configuration. If rich is false then only commands for inserting and removing text will be added as default editing commands. This means that elements cannot be created or destroyed, and only the text that they contain can be changed. If rich is true then text editing commands and commands for spitting, merging, and removing elements will be added.

In most cases using an RNG schema is recommended, because without an RNG schema to guide its actions Mozile will not be able to maintain the validity of the document. For example, Mozile will not be able to tell which elements are allowed to contain non-white-space text and which are not allowed. However there are cases where a schema is unavailable or undesirable, and the default commands serve that purpose.

Commands

All of Mozile's editing operations are performed by commands. There is a mozile.edit.Command "class", and several sub-classes with specialized functions. ("Class" is put in quotation marks because JavaScript doesn't have classes in the same sense that Java does. However, for most of our purposes they behave the same.) Commands can be executed and "unexecuted", so every editing operation can be done, undone, and redone. After undoing an editing command the document will be in exactly the same state as it was before you called the command in the first place.

Commands can be associated with mozile.rng.Element objects, such as text or element insertion commands, or with the document as a whole, like the Undo and Redo commands. They can also be associated with the GUI. Every command has a unique name and all of the commands are stored in an associative array named mozile.edit.allCommands.

Every command has the following methods:

  • respond() - given a change code, determines whether the command should be updated. See below for notes on change codes and the makesChanges and watchesChanges properties.
  • isAvailable() - given an event, determines whether the command is available for use in the current editing context.
  • isActive() - given an event, determines whether the command is active or disabled in the current editing context.
  • test() - given an event or other arguments, determines whether the command can be executed.
  • trigger() - takes an event and if the test is successful it executes the command. Use this method to activate a command as the result of a user event.
  • request() - takes a state and some arguments and if the test is successful it executes the command. Use this method when calling one command from another command.
  • prepare() - used to extract information from the event object and other arguments and store it in a mozile.edit.State object. The state object contains enough information for the command to be executed and unexecuted, and the state is stored by the undo system.
  • execute() - takes the state object and does the actual work of manuipulating the document.
  • unexecute() - takes the state object and undoes everything which execute() did, leaving the document exactly as it was.

There are other methods and properties which we will ignore for the time being. See below for more details.

The mozile.edit module defines a few basic text editing commands. The mozile.edit.rich module defines more primitive commands for editing text and elements. Very powerful commands can be created simply by combining these primitive commands. Here is a list of some commands built in to Mozile (they all belong to mozile.edit.*):

  • insertText
  • removeText
  • insertNode
  • removeNode
  • moveNode
  • remove removes combinations of text and elements
  • mergeNodes
  • splitNode
  • splitNodes

Here are the classes of commands that are available (they all belong to mozile.edit.*):

  • Command is a generic command
  • CommandGroup groups commands together, but does not perform editing operations.
  • Navigate moves the selection through the document.
  • Split splits an element and its contents in two.
  • Insert inserts either text or an element at the current selection.
  • Wrap wraps the current selection inside one or more new elements.
  • Unwrap removes a target element but keeps all the child nodes.
  • Replace replaces a target element with a new element, without changing the child nodes.
  • Style changes the CSS style of a target element.

These are the building blocks from which you can create powerful editing behaviours.

Commands are always grouped together by a CommandGroup. When the groups has its trigger(event) method called, it passes the event to the trigger() method for each command in thr group. If one of the commands is triggered, then the command is executed and the resulting state object is returned. That state object is stored in the undo system by mozile.edit.done(). Then the event is cancelled so it won't trigger any more commands.

If none of the commands are triggered, then the whole process is repeated with the target node's parent node. mozile.edit.handle() keeps trying all of the ancestor nodes, until the event triggers a command or the ancestor is no longer inside an editable element. Then it tries the default commands in the mozile.edit.defaults CommandGroup. If the event isn't handled by any command then Mozile lets the it continue on its way.

States

mozile.edit.State objects are very important for the command system. A command's prepare() method will prepare a new mozile.edit.State instance containing all of the information needed to execute and unexecute the command. When the command is executed the prepared state is sent to the command's execute() method and then stored by the undo system. When the command is undone, the state is sent to the unexecute() method. When the command is redone, the state is sent to the execute() method again.

Commands may add, remove, and alter the nodes in the document. When a command is undone, the structure of the document will be exactly the same as it was before the command as executed. However, the particular node objects that make up that structure may not be exactly the same as before. In other words, while the XPath addresses of all the parts of the document structure will be the same as before, the identity of particular DOM objects may have changed. For this reason the prepare() should almost always store references to nodes using their XPath addresses, which will stay the same, and not references to the node objects, which may change. Failure to do this can lead to strange bugs which only show up after a Redo operation.

Text and White Space

JavaScript supports Unicode characters, and so does Mozile. The key problem using Unicode is font support. Mozile will insert any Unicode character, but the browser might not be able to render it properly if the available fonts do not include the character. Keep this in mind.

White space is a tricky matter for Mozile. Like most programming languages, the rules for HTML and XML say that white space characters (like spaces and tabs) are treated differently than normal text. Multiple white space characters are considered equivalent to a single character. This allows you to add tabs and spaces to your HTML source, making it more readable without changing the way in which the document is displayed.

Word processors don't follow the same rules. You usually expect every press of the space bar to insert a space character. Since Mozile edits HTML and XML documents in a browser, but it tries to act like a word processor, this creates some problems. Even worse, different browsers handle white space in different ways.

There are two special white space cases that Mozile handles: inline text and blocks. The inline case includes HTML tags like strong and em, while the block case includes p and div. Both of these are values of the CSS display property.

The problem with blocks is that Mozilla's Gecko rendering engine will collapse a block that contain only normal white-space characters. So a p tag that contains a space character will look different than one that includes a non-breaking space character. Mozile handles this case using the mozile.emptyToken. By default this is set to the Unicode non-breaking space character \u00A0. When Mozile makes a change to a block element which removes all the non-white-space text, then an empty token will be inserted. If text is later added, the empty token is removed.

The inline case uses the mozile.alternateSpace setting. By default the value is null, and Mozile will ignore events which try to insert multiple consecutive spaces. A maximum of two consecutive spaces will be created by Mozile. This keeps the source code clean, but can cause some strange looking behaviour with the cursor around white space characers. If the alternateSpace is set to \u00A0 (or some other character), then Mozile will apply a somewhat complicated set of rules to insert space characters and alternate space characters when the user presses the space bar. Normally the two different characters will alternate, but sometimes two alternate space characters will be placed consecutively.

RelaxNG

In the previous section we saw what RNG does for Mozile. This section will go into more detail, particularly about Mozile Editing Schemas (MES).

Semantic Structure

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.

Note

The following paragraph explains planned functionality which is not yet complete.

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 strong and 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.

RelaxNG Objects

Before an RNG schema can be used, Mozile has to parse it. The mozile.useSchema() function starts the parsing operation.

The 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: mozile.rng.Element, mozile.rng.Text, mozile.rng.Choice, etc. So the parsing operation creates a new hierarchy of JavaScript objects which matches the hierarchy of RNG elements.

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 report() and getFirstError() methods which can be used to get information about the validation operation once it is complete.

The mozile.rng.Node objects also provide methods, such as mustContain() and 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.

Mozile Editing Schema

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.

Note

The MES system is currently still in flux, and some aspects may change.

There are five MES elements:

  • command - describes an editing command. This is the most important MES element.
  • group -groups commands together. Associated with the mozile.edit.CommandGroup class.
  • separator - divides commands.
  • define - used like the RNG define element to group elements for reuse. Must have a name attribute.
  • ref - used like the RNG ref element to refer to definitions. Must have a name attribute.

Mozile's MES system is aware of the rules for RNG define and 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.

When the 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.

The command and group tags can have the following attributes. All of them will become properties of the JavaScript 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, insertText has priority 10. Negative values can be assigned.
  • image - a path to an icon for the command in the images directory. Should be a 16x16 pixel PNG image.
  • tooltip - a longer description of the command or group.

These attributes apply only to command tags:

  • 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 Command.
  • text - text content to be inserted by the Insert command. To specify Unicode characters, use this syntax: &#x00A0;. See the Unicode Charts for the character codes. If an element is given it will be used instead.
  • element - the tag name of an element to be created by Wrap, Insert, or Replace commands. Inside a script tag you can set this.element to an Element object.
  • accel is 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 "node".
  • 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" or "state" change. A "text" value means updates on "node" and "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.
  • remove - when true the Insert command will perform a remove operation before inserting, removing any contents of the selection. When false it will move the contents of the selection into the inserted element, which is useful for elements like links (a tags). The default is true.
  • nested - when true the Wrap command will create wrappers inside other wrappers if told to do so. When false it will remove the wrapper if called inside another wrapper. The default is false.
  • target - used by the Navigate, Split, Unwrap, Replace, and Style commands 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 display: block).
    • localName [tagName] - the first element which has a local name matching the given tagName (matching is done lower case).
    Inside a script tag you can set this.target to a function which returns a node. Any time a target is used a direction can be specified.
  • direction - used wherever target is used, this indicates the direction to use when searching for the target. Can be ancestor (the default), next, previous, or descendant.
  • collapse - used by Navigate to determine whether the selection should be collapsed after it is moved. Can be null (the default), start, or end.
  • copyAttributes - when true the Replace command will copy all of the attributes of the target element into the replacement element. When false it will not. The default is true.
  • className - used t o set the class attribute of a created element.
  • styleName - determines the CSS style to be set for a created element or for the Style command class.
  • styleValue - determines the new CSS value to be set for a created element or for the Style command class.

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 element tag will be used as a template. Commands like Wrap and Insert will clone this template and insert the clone into the document. Wrap will then append other elements as children of the clone. If you set the import attribute to true Mozile will copy the element into the current namespace -- this is important when dealing with HTML elements.
  • script - can contain JavaScript code which will be evaluated inside the command object after it has been created and initialized. This allows you to customize the behaviour of the built-in Mozile command classes. Be careful of JavaScript scope rules, however. The this keyword will refer to the command object. We recommend including your JavaScript code as a CDATA section, as in the example below; this will avoid problems with the <, >, and & characters.

Creating your own commands is easy. Here are some examples.

Example 2.3. Creating a command

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"/>

No JavaScript is needed, however we are limited to using a single CSS style.


Example 2.4. Creating a command using script

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 command using element

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>

No JavaScript is needed. However, note that the 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.


Graphical User Interface

Note

The GUI code is new and still in flux. There may be changes to it.

Like any editor, Mozile can present the available editing commands as icons for the user to click and as keyboard shortcuts. The Mozile GUI isn't always necessary, but in most cases a GUI will be useful. Mozile 0.8 is designed to easily allow different GUI modules to be used, presenting different ways for the user to interact with the editor. At this point, however, only one GUI has been implemented: the HTML Toolbar interface mozile.gui.htmlToolbar.

Because Mozile is restricted to the web page that it is loaded in, any graphical interface that it presents must be part of the web page. The HTML Toolbar interface uses CSS and XHTML to display a small toolbar, and keep it positioned above the page contents at the top of the browser's document window. It provides buttons and menus, like most toolbars.

The HTML Toolbar is an instance of the abstract mozile.gui.Factory class. The mozile.event.handle() function passes events to the mozile.gui.update() function, which calls the update() method of whichever GUI Factory is assigned to mozile.gui.factory. You can change the GUI factory by calling destroy() on the current factory, and then setting mozile.gui.factory to the new factory.

Note

As of the writing of this document, the destroy() method hasn't been implemented.

The update() method takes the event and does whatever is needed to update the interface. It has access to the event object, which includes information about the target node. It can use the target node to lookup the appropriate RNG element, and discover its commands.

In the case of the HTML Toolbar interface, a new button is created for each command or command group. Command groups are rendered as menus, with menu items which may themselves be menus. The buttons are built using HTML elements, and are stored with the commands. The buttons are added and removed from the toolbar element as they are needed. CSS is used to display the toolbar at the top of the window.

The HTML Toolbar is designed to show only those commands which belong to the target node's RNG element, and some global commands like Undo and Redo. Another interface, with more space to display information, could display commands belonging to the ancestors of the target node as well.

Chapter 3. Mozile Development

Helping to Develop Mozile

Mozile is an open source project with a small but friendly community behind it. We welcome anyone willing to lend us a hand. Whether it's a bug report or spelling correction, a patch or a new module, every improvement is appreciated.

This chapter is meant to help you climb the learning curve and make it as easy as possible for you to contribute.

What You Need

Mozile doesn't require any fancy tools. Everything you need is probably on your computer already. If not, you can get it for free on the Internet.

Must Have

  • Browser - Mozile runs in a web browser, so you'll need one of those. Firefox is probably the best one to develop in since it's both free and cross-platform. You'll also have access to tools like the DOM Inspector, the Venkman JavaScript debugger, and helpful extensions like FireBug.
  • Text Editor - Except for images and zip files, all of Mozile's files can be edited with a normal text editor. You'll probably want a programmer's text editor, with more features than Windows Notepad, but the choice is entirely up to you.

Nice to Have

  • CVS Client - Not strictly necessary, but this will allow you to get the latest code from our CVS repository. There are many free and open-source CVS tools available, either as command-line or GUI applications. See the Mozile source page and contribution page.
  • Apache Ant - Ant is a Java-based build tool, like GNU Make. If you want to compile a custom set of Mozile modules into the mozile.js file, Ant makes it easy. See the packaging Mozile section below.
  • IRC Client - Not strictly necessary, but nice if you want to talk to other Mozile developers in our IRC channel: irc://irc.freenode.net/#mozile.

That's it. Read on for how you can use these tools to help develop Mozile.

Document Object Model

DOM Resources

DOM

All modern web browsers have some support for the W3C's Document Object Model, which describes an HTML or XML document as a tree made up of nodes. The trouble is that every browser differs in the way that it supports the DOM.

The mozile.dom module contains a number of functions that were written to bridge the gap between DOM implementations. Take a look at Mozile's API documentation for mozile.dom. The odds are that if some developer took the trouble to write a function for that module, then there must have been some cross-browser annoyance that prompted him to do it. You should take a look and consider using it in your code.

XML

The mozile.xml module bridges the differences between browsers when it comes to things like parsing and serializing XML. It also holds a list of common XML namespaces in mozile.xml.ns.

XML namespace support is important to Mozile. However, namespace support is not even as consistent between browsers as the rest of the DOM is. When Mozile is editing HTML documents, no namespaces should ever be used and all nodes should be HTML nodes. When editing XHTML and XML namespaces should always be used, and all nodes should be XML nodes.

Caution

Mozile's support for XML documents and namespaces is not yet complete.

XPath

In the DOM, each node is an object. You can navigate from one node to another. You can move nodes. You can create new nodes and destroy them. But as the structure of the document changes, and nodes move and are replaced, we need a way to access the document by its structure, not just by the nodes. Event if a node has moved from the first paragraph to the fifth paragraph, we still need a way to address the first node of the first paragraph.

XPath is a way of doing just this. The XPath specification is closely tied to the XSL specification, and you can find a good tutorial for bother of them at Zvon.org. You can think of XPath as like a file path; it's a hierarchy which starts at a root and leads to a node. For example:

Example 3.1. Simple XPath example

/html[1]/body[1]/div[2]/p[3]/span[1]/text()[1]

The numbers inside the square brackets tell us that we want the nth matching node, starting to count at 1. So in this example we want the first html element, and the first body element which is a child of the html element. Then we want the second div in the body, the third p inside that div, the first span in the p, and the first text node in the span.

XPath can get much more complicated than this. It can select sets of nodes, along different axes, matching different patterns, and using several built-in functions. However in Mozile we only use simple XPaths like these. The only other complication we use in Mozile is namespaces.

Example 3.2. XPath example with namespaces

/xhtml:html[1]/xhtml:body[1]/xhtml:div[2]/mathml:math[3]

In this example we have nodes from the XHTML and MathML namespaces, as indicated by the xhtml and mathml prefixes. That's about as complicated as it gets.

You can look at the API for the mozile.xpath module, but most of the time we just use mozile.xpath.getXPath() to get the XPath address of a node, and mozile.xpath.getNode() to get a node from and XPath address. Any time we need to store a location in the document which might be changed, we store it using XPath.

Mozilla

Mozilla's Gecko rendering engine is known for its standards support. There's also a lot to be said for having access to the Mozilla source code, to see what's happening under the hood. Most of the time, Mozilla's behaviour is the model we follow in Mozile. However, there are also known bugs. And not all of the browsers built on top of Gecko behave the same way in every situation.

One particularly problematic aspect of Mozilla is its designMode. By setting document.designMode = "on" an HTML page will enter a mode which allows editing pretty much the same as the old Netscape Composer. Using document.execCommand() you can add font tags, etc. You can also resize images and tables. Most importantly, you get a cursor which you can use for editing the document.

If the mozile.useDesignMode property is true, then Mozile will use designMode in order to get a cursor. No execCommand() calls are made, and designMode is turned off when outside an editable area.

The problem is that designMode hasn't seen nearly as much attention as other parts of Mozilla, and it has many bugs. The alternative is to use the "Caret Browsing" mode, which can be activated by the user by pressing F7, or by setting the browser preference accessibility.browsewithcaret. However, the former must be done manually, and the latter requires the UniversalXPConnect privilege, which is not given to remotely executed scripts (for good reason). Since designMode doesn't work in XHTML and XML documents, Caret Browsing is the only option. For XHTML and XML documents set mozile.useDesignMode = false.

Internet Explorer

Internet Explorer is the most popular browser on the Internet by a large margin. Because it's so dominant it's very difficult to ignore. We may be tempted to ignore it, because its poor standards support often makes development difficult. However, IE does support all the features necessary to make Mozile work. Mozile currently has support for IE 6, and we expect to support IE 7 in the future.

Internet Explorer supports designMode, like Mozilla does, with all of its shortcomings. IE also supports the superior contentEditable attribute, which allows selected elements to be made editable while others are not. Mozile uses contentEditable in order to have a cursor for editing, but it does not use execCommand() to perform any of its editing operations. When contentEditable is enabled, IE uses different white space rules than the standard HTML and XML rules. In most ways this makes editing more convenient, and similar functionality has to be explicitly coded by Mozile for Mozilla.

There are many differences in the way IE implements the DOM. The difference which has had the single largest effect on the Mozile architecture is the fact that DOM objects cannot be prototyped in IE the way they can in other browsers. In Mozilla you can declare a new method Text.prototype.foo(), and all text nodes will gain the foo() method. Text is treated as a normal JavaScript object with prototype-based inheritance. In IE this is not the case. Although prototyping of some DOM objects seems to be allowed, in general it is not. Prototyping of XML objects, for instance, is not permitted. Mozile 0.7 made extensive use of prototyping, adding methods like Node.isBlock(), which in many ways leads to more readable code. But Mozile 0.8 had to abandon this in favour of functions like mozile.edit.isBlock(node).

Most of the DOM Level 1 and DOM Level 2 specifications are implemented similarly by IE and Mozilla. IE has limited namespace support in its DOM methods. It doesn't allow HTML and XML nodes to mix as freely as Mozilla does. IE doesn't provide a TreeWalker object, and so Mozile provides one in mozile.dom.TreeWalker.

The largest gap between Mozilla and Internet Explorer that Mozile developers have had to bridge is between the two range and selection implementations. When the user highlights a range of text, we need to be able to determine the start and end points, and then perform manipulations on that range. Mozilla provides Range and Selection objects. End points are specified by the pair of a node and an integer offset; this could be a text node and a number of characters, or an element and a number of child nodes.

Internet Explorer provides a TextRange and a selection object. IE does not use pairs of nodes and points to indicate positions, just character offsets within an element. Since selections are critical for editing, a great deal of effort has been put in to wrapping the IE selection system in an interface which is compatible with the Mozilla system. See the mozile.dom.InternetExplorerRange and mozile.dom.InternetExplorerSelection modules. By accessing the window's selection using mozile.dom.selection.get() you can expect the selection object to behave according to the Mozilla model on all supported browsers.

Caution

The mozile.dom.InternetExplorerRange and mozile.dom.InternetExplorerSelection modules do not yet implement all of the Range and Selection methods. See the API documentation for details.

Internet Explorer's CSS support is not equivalent to Mozilla's in many ways. This affects Mozile's GUI code.

In general, Internet Explorer provides similar features to Mozilla. However be careful of the differences in the way the two implementations work.

Opera

Opera 9 adds a number of new features which make editing with Mozile possible. In most ways Opera does a very good job of matching Mozilla's behaviour, and so adapting Mozile's code to work with Opera is generally easy. However, "porting" Mozile to another browser is always time consuming, and Opera support will have to wait for someone to find that time.

Safari

The current release version of Safari for Mac OS X 10.4.6 lacks a number of features which Mozila requires, including Selection and Range objects, and XML namespace support. Development versions, however, include more and more of these requirements, so we expect to be able to support Safari at some point in the future.

Since Safari and Konqueror are based on the same WebKit/KHTML rendering engine, to a large extent what goes for one goes for the other. However there are important differences, and Konqueror does not always include the latest changes that Safari features.

JavaScript

JavaScript Resources

The Language

JavaScript is an interesting language. Although it lacks some important features, like library support, the core language is surprisingly elegant. In general it's the ugliness of APIs like the DOM, the frustration of cross-browser variations, and the difficulty debugging code that give JavaScript a bad name.

It's worthwhile to read the first part of JavaScript: The Definitive Guide to explore the design of the core language. Here is a brief, biased, incomplete, and probably incorrect overview. The idea here is not to give examples of syntax, but to explain some of the inner workings which are often overlooked.

JavaScript is an object-based language, but not object-oriented in the same way that Java is. It uses a C-style syntax which looks a fair bit like Java. But JavaScript is not Java, and not even "Java-lite".

JavaScript is a dynamically-typed interpreted language. It supports UTF-8 and is case sensitive. JavaScript code in web pages is run by the browser inside a sandbox, and so it does not have any access to objects outside the document and browser window.

The basic data types are null, undefined, Boolean, Number (a double), String, and Object. All of these data types are passed by value, except for Object which is passed by reference. Object is the most interesting type. There are many kinds of objects, including Array, Function, and Error. Objects work like dictionaries in other languages, with keys and values. The values can be any other data type, including other objects and functions. When we talk about an object foo and its "method" foo.bar() we mean that foo has a key named "bar" which has a function as a value. When we talk about foo's "property" foo.baz we mean that foo has a key "baz" with a value of some other data type (not a function).

So JavaScript has objects that look a lot like objects in other languages. It also has object inheritance, although it works differently than Java and C++. JavaScript uses prototype-based inheritance. Here's how it works. We have an object foo, and we set foo.a = "A". When there is no key the value will be undefined, so if we try to access foo.b or foo.c we will get undefined. However, we can create an object fud and set fud.a = "X" and fud.b = "B". Then we can set the prototype of foo to fud like this: foo.prototype = fud. Now we find that foo.a is "A", foo.b is "B", and foo.c is still undefined.

What's happening in this example? JavaScript checks foo's dictionary first, and if the desired key is found then the value is returned. If the key is not found, then JavaScript checks foo.prototype for another object and searches that object for the key. JavaScript will keep searching the fud.prototype, and so on along the chain of prototypes, until it finds a matching key or the chain ends. This is how inheritance works in JavaScript.

There are several built-in objects in JavaScript, all of which inherit from Object: Array, Boolean, Date, Function, Math, Number, RegExp, and String. Array uses numeric indexes instead of (actually in addition to) keys, and works like you expect an array to work. Boolean, Number, and String can be used to create primitive values, and they also provide the properties and methods for the primitive types. Every function in JavaScript is a Function object.

We've seen objects and inheritance, but we haven't yet seen anything like a Java or C++ class. JavaScript doesn't have classes, but it does have "constructor" functions. Here's an example:

Example 3.3. JavaScript constructor

function Rectangle(width, height) {
  this.width = width;
  this.height = height;					
}
Rectangle.prototype.getArea = function() {
  return this.width * this.height;
}

var r = new Rectangle(2, 3);

if(r.width * r.height == r.getArea()) {
  alert("Test passed.");
}

First we define a function named "Rectangle". The this keyword is special; it refers to the object in the current scope (more on scope in a moment). We also add another function named "getArea" to Rectangle.prototype. Then with the line var r = new Rectangle(2, 3) we create a new object and assign it to r. The new object becomes an "instance" of Rectangle because the Rectangle() function is executed as a constructor, assigning new properties to r. We can access r.width to get 2 and r.height to get 3, and we can call r.getArea() to get 6.

In many ways, Rectangle behaves like a class, and we use the new keyword to create instances of the class. The next step is to show how sub-classes work. We continue the example above, adding the following code:

Example 3.4. JavaScript constructor inheritance

function Square(width) {
  this.width = width;
  this.height = width;					
}
Square.prototype = new Rectangle;
Square.prototype.constructor = Square;

var s = new Square(4);

if(s.width * s.height == s.getArea()) {
  alert("Test passed.");
}

Again we define a constructor function, this time one named "Square". Then we set Square.prototype = new Rectangle, which establishes the chain of prototype-based inheritance. We also set Square.prototype.constructor = Square, to make it clear that the special constructor key, which is supposed to refer to the constructor function, refers to Square instead of Rectangle. After that we can create a new square and access the getArea() method using inheritance.

There are other ways to make JavaScript objects work like classes; this is the one we generally use in Mozile. But be careful! JavaScript is not Java or C++, and these "classes" are only similar and not the same.

One last important topic is JavaScript's rules for handling variable scope. The global scope in JavaScript can be considered a function like any other. When working in a browser the global object will have DOM properties, like window and document. When you refer to a variable inside a function, JavaScript will check a chain of scopes which works like the chain of prototypes. Variables are actually properties of the current function, which is an object like any other. First the JavaScript interpreter checks the current function for a variable with the matching name, then it checks the next function and the next until it finds the variable or reaches the global scope. If you declare a variable with the same name as one higher in the scope chain, JavaScript will think you want to use the original variable from the outer scope and overwrite it.

JavaScript does not include a notion of public, private, and protected visibility for variables and methods. We rely on the scoping rules and the convention that names starting with an underscore, like foo._bar(), are intended to be private and should not be used outside the object that defines them.

Debugging

Debugging JavaScript is notoriously difficult. Although there are very few differences in the JavaScript language implementation between browsers, there are lots of places where incompatibilities and bugs can slip in. The fact that Mozile uses eval() to load much of the code aggravates the situation, because it means that the line numbers in most error messages are useless. There's no silver bullet, but there are some things that will make life easier.

Test-driven development can help. See below for Mozile's JsUnit test system.

If you are using a Mozilla browser, set the javascript.options.strict and javascript.options.showInConsole preferences to true. This tells the JavaScript interpreter to send warning messages when it notices undefined variables, or an = where == is expected, or useless expressions. It might help you catch a problem early. You can access the browser preferences by typing about:config into the address bar of the browser.

Important

When you make changes to Mozile code, please make sure that they don't cause any unnecessary warnings. We should only see warnings from Mozile if something has actually gone wrong.

Mozile provides some debugging tools in the mozile.debug module (in src/core.js). The mozile.debug.warn(), mozile.debug.inform(), and mozile.debug.debug() functions will log debugging messages and store them in mozile.debug.messages. By changing the mozile.debug.alertLevel and mozile.debug.logLevel you can decide which kinds of messages are displayed using alerts, which are silently logged, and which are ignored. The "Debug" command on the Mozile toolbar (shortcut "Command-D") will open a new window to display logged messages.

Mozilla's Venkman JavaScript Debugger is a very powerful tool for tracking down errors. By setting the "Debug - Error Trigger" and "Debug - Throw Trigger" you can catch problems as they occur, look at the values of variables, and step through the code. Be sure to use the "Pretty Print" mode to display the JavaScript code as it appears to Mozilla. This is important for code executed using eval(), which includes almost all of the Mozile code. One difficulty is that most of Mozile's functions are "anonymous" and the name of the object the function belongs to won't be displayed in "Pretty Print" mode. Tracking down the function in the source code may require some clever use of text search like the Unix grep command.

The Microsoft Script Debugger does many of the same things as Venkman, but does them in different ways.

The best method when writing new Mozile code is to use just one browser until the code is working, make sure you have good tests written and helpful debugging messages ready, and then "port" the code to other browsers. Just make sure that the changes you make while porting don't break the code in the original browser.

Coding Style

The Dojo JavaScript Style Guide is a pretty good place to start. For the sake of the next person to read the code it's nice if there's consistency. But we aren't going to be too strict about style.

Here's what we would like to see: Please make an effort to use tabs, Unix file endings (LF), and UTF-8 encoded files. Include the license block at the top of all code files. And please make use of the JSDoc system of documentation in comments for all methods and properties.

Testing with JsUnit

JsUnit Resources

JsUnit belongs to the xUnit family of testing tools, the most popular of which is JUnit for Java. Unit tests are a means of ensuring that small "units" of code function they way that they should by performing automated tests on their interfaces.

JsUnit tests run inside test pages, which are normal web pages with some extra JavaScript code. The test pages are loaded into a "Test Runner" which controls the tests and keeps track of the results. The test runner calls test functions which do the real work. The test functions call "assertion" functions of all sorts, such as assertTrue() and assertEquals(), which check to make sure that the target code is doing what it should be doing. The test runner displays a green bar, which gets longer as tests are completed. If even one test fails then the bar turns red and a message about the failed test is displayed.

Testing manually is slow and mind-numbing work. Something will always be missed, and developers will avoid doing it. Automated testing is fast and accurate. When all the tests are "green" you feel good about it. Run the tests after every non-trivial change you make!

If we have a comprehensive test suite, and all the tests succeed, then we can be fairly confident that the code is working the way it should. The more good tests, the more confident we feel. More importantly, if we make a change to the code and the test suite is still green, then we can be fairly certain that we haven't introduced a bug. We run the test suite before committing new code to CVS, and if it isn't green we stop and find the problem. This way the code you get from CVS will always be "green", if the test suite fails you can be pretty sure that the changes you made broke something.

Important

If you see Firefox 1.5 fail any HTML, XHTML, or XML test, or you see Internet Explorer 6 fail any HTML test, make sure that the other developers know about it!

Test-driven development is the idea that you write the tests before you write the code, and then you write the code to satisfy the test. It's not a bad way of doing things. Programming methodologies like agile software development and extreme programming strongly encourage the use of tests. It's hard to argue that tests aren't a good idea. The only reason to skip writing tests is to save time in the short term, but it'll cost you more time later if you miss an important bug for lack of a test. Unit tests also help us port Mozile to new browsers by isolating the problematic differences between them. We aren't going to force you to adopt any particular programming methodology, but we strongly encourage you to write tests for any code you change or add. We're much more likely to accept changes into Mozile if they come with good tests. And we won't accept any changes that cause the test suite to fail.

Mozile is currently using a slightly modified version of JsUnit 2.2 alpha. The JsUnit files are stored in www/0.8/jsunit/, while Mozile's test files are stored in www/0.8/tests/. The tests/index.xml file provides an index of all the tests, with buttons which trigger test runs, individually and in groups, inside HTML, XHTML, and XML test pages. The tests/shared directory contains the test pages, which are shared by all the tests. The other subdirectories of the tests directory contain the JavaScript test files, organized by module. If a module doesn't have its own subdirectory, the tests will be stored in tests/core/.

The steps we take to get from index.xml to a test are somewhat complicated. Say we want to run the mozile.dom tests, which are stored in the tests/dom/dom.js file. We point the browser to index.xml, which uses index.xsl and index.js to give us a nice dynamic web page. Then find the table row marked "mozile.dom.* :: DOM" and press the corresponding "XHTML" button. A new window opens with the URL file://../jsunit/testRunner.html?autoRun=true& testpage=../tests/shared/testpage.html&test=[unit:dom/dom] which is the Test Runner page with a bunch of additional arguments. The arguments say that the tests/shared/testpage.html test page will be used, and the [unit:dom/dom] test will be run. The test runner page loads, and it loads the tests/shared/testpage.html test page inside a frame. The tests/dom/dom.js test file is then loaded into the tests/shared/testpage.html test page. Then the test runner does the work of running the tests.

It's also possible to run test suites (groups of tests), and tests for HTML, XHTML, and XML documents. Mozile's test system is more complicated than the minimum necessary for JsUnit testing, but it provides a lot of flexibility. All of the Mozile tests can be run, for all of the document types, with a single button press.

Although the process used to load the tests is fairly complicated, writing the tests is straight-forward. Here is an example from the tests/dom/dom.js test file.

Example 3.5. Test example from tests/dom/dom.js

mozile.require("mozile.dom");
mozile.require("mozile.xml");

var name = "mozile.dom.*";

/**
 * Expose the functions named in this array to JsUnit.
 * Make sure to keep it up to date!
 */
function exposeTestFunctionNames() {
  return ["testIsHTML", "testGetters", "testNamespace", "testCreate", 
    "testRemove", "testIgnore", "testIsWhitespace"];
}

/**
 *
 */
function testIsHTML() {
  // HTML Case
  if(document.documentElement.nodeName.toLowerCase() == "html") {
    assertTrue("This is HTML", mozile.dom.isHTML(document));
  }
  // XML Case
  else {
    assertFalse("This is not HTML", mozile.dom.isHTML(document));
  }
}
...

The mozile.require("mozile.dom"); lines are just the same as in any module. The var name = "mozile.dom.*"; line helps identify the test file in failure messages. The exposeTestFunctionNames() function returns an array with the names of all the test functions in this file.

Important

Make sure the exposeTestFunctionNames() function is up-to-date, or your tests might not run.

The testIsHTML() test function is simple. It includes a conditional, which determines which test should be run, and then assertTrue() and assertFalse() statements which contain a brief message and tell the test runner what the result of the mozile.dom.isHTML(document) call should be. If the result does not match what is asserted, the test will fail and the test status bar will turn red.

Most test functions are more complicated that this one, but they all come down to a series of assertion functions. See the JsUnit documentation for a full list of assertion functions.

There are a few things to keep in mind when running the tests. File paths can be tricky, because the test page is inside a frame of the test runner page, and those files are in different locations. Also, the test functions may be executed in a different page than the one they were loaded into; the file paths can be different inside test functions than they are in the global namespace of the test file.

The HTML, XHTML, and XML test pages in tests/shared are structurally very similar, but there are differences in MIME types and namespaces which may cause browsers to fail on one and not the others. This is why we test all three kinds.

You can make use of the util.js files in different places inside the tests test directory to store code which is shared between test files.

Important

Please do your best to update and improve the test suite. Make sure the test suite runs "green" before sending us the changes to your code. We can't add your changes to Mozile until the test suite runs green.

Documentation with JSDoc

Note

JSDoc is written in Perl, so you'll need to have Perl installed to run it.

JSDoc documents JavaScript files in the same way that JavaDoc documents Java files. It takes specially formatted comments from the code file and generates HTML documentation files. Here's an example from Mozile:

Example 3.6. JSDoc example

/**
 * Inserts the newNode after the refNode in the refNode's parent node.
 * @param {Node} newNode The node to insert.
 * @param {Node} refNode Insert the new node after this node.
 * @type Node
 */
mozile.dom.insertAfter = function(newNode, refNode) {

The opening /** indicates to JSDoc that it should pay attention to this comment. The first sentence describes the function. The @param statements indicate parameters (i.e. arguments) for the function. The {Node} entry tells JSDoc that the parameter's type is Node. The parameter also includes a name and description. Finally @type provides the return type of the function. Specifying a type in a dynamically typed language like JavaScript isn't always necessary, but it helps other developers to understand the code. The JSDoc homepage provides a full list of tags and their usage.

We use JSDoc in all of the files in the code directory, as well as in all JavaScript files and most CSS and XML files throughout Mozile. New JSDoc HTML files are generated before every Mozile release, and periodically between releases. You don't need to update the HTML files yourself when you make a change to Mozile, but please keep the JSDoc comments in the source files up to date.

To generate the JSDoc HTML files, we first remove all the *.html files in doc/jsdoc. Then we run the JSDoc script over all the files in src, using the -d option to set the destination directory:

cd ~/working_copy/mozilewww/0.8/src/
/path/to/jsdoc.pl -d ../doc/jsdoc *

Important

Please write and revise JSDoc comments for the code you're working on, so that the API documentation is always up-to-date.

Documentation with DocBook

DocBook Resources

This document is written using DocBook and translated into HTML using the XSL stylesheets from The DocBook Project. Please see the DocBook XSL: The Complete Guide for information on using XSL to transform DocBook files.

New HTML files are generated, using the html/docbook.xsl and html/chunk.xsl stylesheets, before every release and periodically between releases. You don't have to update the generated files yourself when you make a change, but please update the DocBook file.

Important

Please update the DocBook version of this documentation as you make changes to Mozile, so that the documentation is always up-to-date.

Packaging Mozile

Packaging Resources

Mozile packages are made by copying the main Mozile directory, removing unnecessary files, and zipping the directory into an archive labelled mozile-X.Y.Z.zip, where X.Y.Z is the current version number.

Packages are uploaded to the CVS repository in the downloads directory. They are mirrored on the Mozdev download mirrors, but it can take several hours for all of the mirrors to update.

To make a Mozile package, or to compile a series of Mozile modules into a single mozile.js file, you can use our Apache Ant build script. You'll need Ant version 1.6.x installed. The tools/build.xml file contains the logic, and tools/build.properties contains user-modifiable settings. You just cd to the tools directory, then call ant. Here are some examples:

Important

Mozile's Ant build script is new. Let us know if you have any problems with it.

Example 3.7. Using Ant

Just run ant and a help message will be displayed. It lists the most important Ant build "targets" for Mozile: compile, doc, build, package, etc.

$ cd /mozile/tools
$ ant
Buildfile: build.xml

help:
     [echo] Mozile build tools. Targets:
     [echo]  help       Display this help message.
     [echo]  compile    Concatenate and compress source files into 'mozile.js'.
     [echo]  doc        Generate all documentation files.
     [echo]    docbook  Generate DocBook files.
     [echo]    jsdoc    Generate JSDoc files.
     [echo]  build      Assemble all of the files to be distributed.
     [echo]  package    Package everything into a zip archive.
     [echo]  release    Build everything and package Mozile for release.
     [echo]  clean      Clean up.

BUILD SUCCESSFUL
Total time: 1 second

Example 3.8. Compiling Mozile

The ant compile command will "compile" a version of mozile.js based on the modules property in the build.properties file. It concatenates those files together in sequence, then uses tools/jsmin.jar to strip all the comments and unnecessary white space. If the mozile.js file is newer than all the files in the src directory then Ant won't bother compiling it again.

$ ant compile
Buildfile: build.xml

init:

compile.check:

compile:
   [delete] Deleting directory /mozile/www/0.8/build/compile
    [mkdir] Created dir: /mozile/www/0.8/build/compile
     [copy] Copying 15 files to /mozile/www/0.8/build/compile

BUILD SUCCESSFUL
Total time: 10 seconds
$ ant build

Example 3.9. Building Mozile

The ant clean build command will first clean up any new directories Ant might have created. Then it will assemble all of the Mozile files into build, ignoring unwanted files like CVS directories. The files copied are controlled by settings in the build.properties file.

$ ant clean build
Buildfile: build.xml

clean:
   [delete] Deleting directory /mozile/www/0.8/build   
   [delete] Deleting directory /mozile/www/0.8/release

init:

build:
    [mkdir] Created dir: /mozile/www/0.8/build
     [copy] Copying 5 files to /mozile/www/0.8/build    
    [mkdir] Created dir: /mozile/www/0.8/build/demos     
     [copy] Copying 11 files to /mozile/www/0.8/build/demos
    [mkdir] Created dir: /mozile/www/0.8/build/doc
     [copy] Copying 122 files to /mozile/www/0.8/build/doc
    [mkdir] Created dir: /mozile/www/0.8/build/images
     [copy] Copying 45 files to /mozile/www/0.8/build/images
    [mkdir] Created dir: /mozile/www/0.8/build/jsunit
     [copy] Copying 44 files to /mozile/www/0.8/build/jsunit
    [mkdir] Created dir: /mozile/www/0.8/build/lib
     [copy] Copying 41 files to /mozile/www/0.8/build/lib
    [mkdir] Created dir: /mozile/www/0.8/build/src
     [copy] Copying 17 files to /mozile/www/0.8/build/src
    [mkdir] Created dir: /mozile/www/0.8/build/tests     
     [copy] Copying 87 files to /mozile/www/0.8/build/tests
    [mkdir] Created dir: /mozile/www/0.8/build/tools     
     [copy] Copying 7 files to /mozile/www/0.8/build/tools

BUILD SUCCESSFUL
Total time: 12 seconds

In order to use all of the targets in the build.xml script you'll need to have some other tools installed. For example, you need JSDoc installed to generate the JSDoc files, and you need the DocBook XSL files and Saxon installed in order to generate the DocBook documentation. Take a look at the comments in the build.properties file for the details.