core.js

Summary

Mozile's core properties, methods, and classes.

Project Homepage: http://mozile.mozdev.org

Version: 0.8 $Id: overview-summary-core.js.html,v 1.15 2008/02/20 18:47:09 jameso Exp $

Author: James A. Overton


Class Summary
mozile.Module  

/* ***** BEGIN LICENSE BLOCK *****
 * Licensed under Version: MPL 1.1/GPL 2.0/LGPL 2.1
 * Full Terms at http://mozile.mozdev.org/0.8/LICENSE
 *
 * Software distributed under the License is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
 * for the specific language governing rights and limitations under the
 * License.
 *
 * The Original Code is James A. Overton's code (james@overton.ca).
 *
 * The Initial Developer of the Original Code is James A. Overton.
 * Portions created by the Initial Developer are Copyright (C) 2005-2006
 * the Initial Developer. All Rights Reserved.
 *
 * Contributor(s):
 *	James A. Overton <james@overton.ca>
 *
 * ***** END LICENSE BLOCK ***** */

/**
 * @fileoverview Mozile's core properties, methods, and classes.
 * <p>Project Homepage: http://mozile.mozdev.org 
 * @author James A. Overton <james@overton.ca>
 * @version 0.8
 * $Id: overview-summary-core.js.html,v 1.15 2008/02/20 18:47:09 jameso Exp $
 */

// End of Headers

// If Dojo is present, provide Mozile as a Dojo module.
if(window.dojo) dojo.provide("mozile.src.core");

// Remove any previous copy of Mozile 
if(window.mozile && mozile.gui) mozile.gui.destroy();


/**
 * The root Mozile object.
 * @type Object
 */
if(!window.mozile) mozile = {};

if(!mozile.window) mozile.window = window;
if(!mozile.document) mozile.document = document;
if(!mozile.filesep) mozile.filesep = "/";
if(!mozile.linesep) mozile.linesep = "\n";
if(!mozile.locale) {
	mozile.locale = "en-us";
	if(navigator.language) mozile.locale = navigator.language;
	if(navigator.userLanguage) mozile.locale = navigator.userLanguage;
}
if(!mozile.root) mozile.root = "";

/**
 * Set the version number.
 * @type Object
 */
mozile.version = {
	major: "0",
	minor: "8",
	micro: "0",
	flag:  "a1",
	toString: function() {
		return this.major + "." + this.minor + "." + this.micro + this.flag;
	}
}

/**
 * Other Mozile information.
 */
mozile.about = "Mozile is a WYSIWYG inline edtior for XHTML and XML+CSS files.";
mozile.copyright = "Copyright 2006, The Mozile Team";
mozile.license = "MPL 1.1/GPL 2.0/LGPL 2.1, http://mozile.mozdev.org/0.8/LICENSE";
mozile.homepage = "http://mozile.mozdev.org";
mozile.credits = "James A. Overton, Conor Dowling, Max d'Ayala, Christian Stocker, Paul Everitt, David Palm, Richard Prescott, Lars Kiilerich, Kingsley Kerse, Tobias Minich, Andreas Schamberger, and others...";
mozile.acknowledgements = "Icons created by Mark James <http://www.famfamfam.com/lab/icons/silk/>";



/**** Configuraton Defaults ****/

/**
 * Indicates whether Mozile has been "compiled" into a single file. This is set automatically by Mozile's build tools.
 * @type Boolean
 */
mozile.precompiled = false;

/**
 * Indicates whether module requirements should be deferred until mozile.loadDeferred() is called. Used when Mozile is precompiled. This is set automatically by Mozile's build tools.
 * @type Boolean
 */
mozile.deferRequirements = false;

/**
 * When true, designMode will be used by Mozile in browsers that support it. When false designMode will not be changed.
 * @type String
 */
mozile.help = ["doc", "html", "index.html"].join(mozile.filesep);

/**
 * When true, designMode will be used by Mozile in browsers that support it. When false designMode will not be changed.
 * @type Boolean
 */
mozile.useDesignMode = false;

/**
 * The default namespace is null. This means that the document's namespace is always used, which will usually be HTML.
 * @type String
 */
mozile.defaultNS = null;

/**
 * The empty token is used when there is a text node which is only a place-holder inside an empty element. By default it is a non-breaking space character.
 * @type String
 */
mozile.emptyToken = "\u00A0"; 
// \u00A0 = non-breaking space
// \uFEFF = zero-width non-breaking space

/**
 * Specify a non-breaking space character to be used throughout.
 * @type String
 */
mozile.alternateSpace = null;
// \u00A0 = non-breaking space

/**
 * The default update interval in milliseconds.
 * @type Integer
 */
mozile.updateInterval = 500;


/**** Bootstrapping Methods ****/

/**
 * A cross-browser compatible loading method. 
 * Uses XMLHttpRequest to get the text content of a file.
 * @param {String} uri The URI of the file to be loaded.
 * @type String
 */
mozile.load = function(uri) { 
	var XHR;

	try{
		if(mozile.window.XMLHttpRequest) {
			XHR = new XMLHttpRequest();
			XHR.open("GET", uri, false);
			XHR.send(null);
		}
		else if(mozile.window.ActiveXObject) {
			XHR = new ActiveXObject('Microsoft.XMLHTTP');
			XHR.open("GET", uri, false);
			XHR.send();
		}
	} catch(e) { 
		if(mozile.debug) mozile.debug.inform("mozile.load", "File load failed loading '"+ uri +"' with error message:\n"+ e);
		return null;
	}
	
	if(XHR) {	
		//alert(XHR +" "+ XHR.status);
		if(XHR.status == 0 || XHR.status == 200) return XHR.responseText;
		else {
			if(mozile.debug) mozile.debug.inform("mozile.load", "File load failed with status '"+ XHR.status +"' and message:\n"+ XHR.responseText);
			return null;
		}
	}
	
	if(mozile.debug) mozile.debug.inform("mozile.load", "No XMLHttpRequest available when trying to load '"+ uri +"'.");
	return null;
}

/**
 * Searches for a script tag with src like "mozile.js". Returns the path leading to "mozile.js".
 * @type String
 */
mozile.findRoot = function() {
	// Special case, mozileRoot is already defined.
	if(mozile.window.mozileRoot != undefined) return mozileRoot;

	// Search the document for script elements.
	var scripts = mozile.document.getElementsByTagName("script");
	var matches = ["mozile.js", "mozile-src.js"];
	var src, index, i, j;
	for(i=0; i < scripts.length; i++) {
		src = scripts[i].getAttribute("src");
		if(!src) continue;
		for(j=0; j < matches.length; j++) {
			index = src.indexOf(matches[j]);
			if(index > -1) return src.substring(0, index);
		}
	}
	return "";
}

// Detect the mozile.root.
mozile.root = mozile.findRoot();



/**** Module System ****/

/**
 * This system a simpler version of the one used by the Dojo Toolkit (http://dojotoolkit.org).
 * "Modules" are just the parts of a hierarchy of objects building on the global "mozile" object. At each level they may contain properties, methods, or classes.
 * Names follow the familiar library naming convention: e.g. "mozile.edit.InsertionPoint".
 * <p>Some modules are stored in their own JavaScript files in the "src" directory of the Mozile installation.
 * Module files must specify their requirements and the sub-modules they provide using mozile.require() and mozile.provide().
 * <p>The module can be retreived using mozile.findModule().
 * Modules are loaded with mozile.loadModule()
 */

/**
 * Finds an object in the "mozile" hierarchy, if it exists.
 * Given a name of the format "mozile.foo.bar", it starts with the global "window" object and checks for the presence of a "mozile" property.
 * Then it checks the "mozile" object for a "foo" property, and so on.
 * @param {String} moduleName A module name of the form "mozile.foo" or "mozile.foo.Bar".
 * @type Object
 */
mozile.findModule = function(moduleName) {
	var levels = moduleName.split(".");
	var current = window;
	for(var i=0; i < levels.length; i++) {
		if(!current[levels[i]]) return null;
		current = current[levels[i]];
	}
	return current;
}

/**
 * Tries to load a module of code.
 * The method first tries to load the file "$ROOT/src/foo/bar.js".
 * If that fails, it tries to load "$ROOT/src/foo.js".
 * <p>When a file is successfully loaded it is then evaluated using eval().
 * Note that it is evaluated in the context of this function, which will cause problems for any variables in the file which are declared outside the "mozile" hierarchy.
 * @param {String} moduleName A module name of the form "mozile.foo" or "mozile.foo.Bar".
 * @type Boolean
 */
mozile.loadModule = function(moduleName) {
	if(!moduleName) return false;
	//mozile.debug.debug("mozile.loadModule", "Loading module: "+ moduleName);

	var filepath = [mozile.root];
	var levels = moduleName.split(".");
	for(var i=0; i < levels.length; i++) {
		if(levels[i] == "*") levels = levels.splice(i,1);
	}
	if(levels[0] && levels[0] == "mozile") {
		levels.shift();
		if(levels[0] && levels[0] == "test") {
			levels.shift();
			filepath.push("tests");
		}
		else filepath.push("src");
	}
	else return false;
	if(levels.length == 0) return false; 
	//alert("Loading module "+moduleName);

	while(levels.length > 1) {
		filepath.push(levels.shift());
	}
	filepath.push(levels.pop() +".js");

	// Try to load the file.
	var file = mozile.load(mozile.joinPaths(filepath));

	// Fix problem loading within JsUnit test files.
	if(!file && mozile.test && mozile.test.root) {
		filepath.shift();
		filepath.unshift(mozile.test.root);
		file = mozile.load(mozile.joinPaths(filepath));
	}

	// If there is no file, try to load the parent module.
	if(!file) {
		//alert("No file for '"+ moduleName +"' at '"+ mozile.joinPaths(filepath) +"' starting at '"+ location +"'.");
		var parent = moduleName.substring(0, moduleName.lastIndexOf("."));
		return mozile.loadModule(parent);
	}
	
	// Try to evaluate the file.
	try {
		eval(file);
		return true;
	} catch(e) {
		mozile.debug.inform("mozile.loadModule", "Error evaluating module '"+ moduleName +"' in file '"+ mozile.joinPaths(filepath) +"'.\n"+ mozile.dumpError(e));
		return false;
	}
}

/**
 * An array of modules which have been required but had loading deferred.
 * @type Array
 */
mozile.deferrals = new Array();

/**
 * Loads a module if it has not already been loaded.
 * This method is used by modules to indicate that they require the support of another module.
 * @param {String} moduleName A module name of the form "mozile.foo" or "mozile.foo.Bar".
 * @type Boolean
 */
mozile.require = function(moduleName) {
	if(!mozile.findModule(moduleName)) {
		if(mozile.deferRequirements) {
			//mozile.debug.debug("mozile.require", "Deferring module load: "+ moduleName);
			mozile.deferrals.push(moduleName);
			return false;
		}
		return mozile.loadModule(moduleName);
	}
	else return false;
}

/**
 * Uses require() to load each module on the deferrals list. Clears the list.
 * @type Void
 */
mozile.loadDeferred = function() {
	//mozile.debug.inform("mozile.loadDeferred", "Deferred modules: "+ mozile.deferrals.length);
	while(mozile.deferrals.length) {
		mozile.require(mozile.deferrals.shift());
	}
}

/**
 * This function is used by modules to indicate what properties, methods, and classes they provide.
 * Currently the function does nothing, but its use it still required for all modules.
 * @type Void
 */
mozile.provide = function() {
	// startPackage?
}

/**
 * Sets the moziel.window and mozile.document propeties based on a given window object.
 * @type Void
 */
mozile.setTarget = function(win) {
	if(mozile.save) {
		win.onbeforeunload = mozile.save.onbeforeunload;
		if(mozile.save.target = mozile.document)
			mozile.save.target = win.document;
	}

	mozile.window = win;
	mozile.document = win.document;
	win.mozile = mozile;
}

/**
 * Loads a localization object for the given module, bundle, and locale.
 * @param {String} moduleName The name of the module to find the localization in.
 * @param {String} bindleName The particular localization file to use.
 * @param {String} locale Optional. The locale to use. Defaults to mozile.locale.
 * @type Object
 */
mozile.getLocalization = function(moduleName, bundleName, locale) {
	mozile.require("mozile.util");
	if(!locale || typeof(locale) != "string") locale = mozile.locale;
	if(!locale || typeof(locale) != "string") return {};
	locale = locale.toLowerCase();
	
	moduleName = moduleName.replace(/^mozile\./m, "src.");
	var packagePath = moduleName.replace(/\./g, mozile.filesep);
	var modulePath = mozile.joinPaths(mozile.root, packagePath, "nls");
	mozile.debug.debug(packagePath);
	
	// Load default localization.
	var path = mozile.joinPaths(modulePath, bundleName + ".js");
	var code = mozile.load(path);
	var localization = {};
	var extension;
	if(code) localization = mozile.util.parseJSON(code);
	
	// Try loading general localization, e.g. "en".
	var family = locale.split("-")[0];
	path = mozile.joinPaths(modulePath, family, bundleName + ".js");
	code = mozile.load(path);
	if(code) {
		extension = mozile.util.parseJSON(code);
		localization = mozile._extendLocalization(localization, extension);
	}

	// Try loading specific localization, e.g "en-ca".
	path = mozile.joinPaths(modulePath, locale, bundleName + ".js");
	code = mozile.load(path);
	if(code) {
		extension = mozile.util.parseJSON(code);
		localization = mozile._extendLocalization(localization, extension);
	}
	
	return localization;
}

/**
 * Extends a general localization object using a more specific one. Values in the specific object override the general one.
 * @private
 * @param {Object} general The localization object to be extended.
 * @param {Object} specific The localization object which extends the general object.
 * @type Object
 */
mozile._extendLocalization = function(general, specific) {
	if(!general || typeof(general) != "object") return {};
	if(!specific || typeof(specific) != "object") return general;

	var key, value;
	for(key in specific) {
		value = specific[key];
		if(value instanceof Array) general[key] = value;
		else if(typeof(value) == "object" && 
			typeof(general[key]) == "object") {
			mozile._extendLocalization(general[key], value);
		}
		else general[key] = value;
	}

	return general;
}




/**
 * A container for Mozile code.
 * This is a hack to make JSDoc show modules properly.
 * @constructor
 */
mozile.Module = function() {};





/**** File Tools ****/

/**
 * Determines whether a path is absolute.
 * @param {String} path The path to check.
 * @type Boolean
 */
mozile.isPathAbsolute = function(path) {
	if(!path) return false;
	path = path.toString();
	if(path.match(/^[a-zA-Z]+\:\/\//m)) return true;
	return false;
}

/**
 * Strips non-path information from a URL.  
 * @param {String} url The URL to strip.
 * @type String
 */
mozile.getPath = function(url) {
	if(!url) return "";
	url = url.toString();
	
	// Strip GET arguments.
	if(url.indexOf("?") > -1) url = url.substring(0, url.indexOf("?"));
	
	return url;
}

/**
 * Gets a directory from a path. Be careful, because this method isn't very smart.
 * TODO: Handle Windows paths.
 * @param {String} path The path to use.
 * @type String
 */
mozile.getDirectory = function(path) {
	if(!path) return "";
	path = path.toString();
	path = mozile.getPath(path);
	var partial;
	
	// Strip everything after the last file separator.
	if(path.indexOf(mozile.filesep) > -1) {
		if(path.indexOf(":///") > -1) {
			partial = path.substring(path.indexOf(":///")+4);
			if(partial.indexOf(mozile.filesep) == -1) return path;
		}
		else if(path.indexOf("://") > -1) {
			partial = path.substring(path.indexOf("://")+3);
			if(partial.indexOf(mozile.filesep) == -1) return path;
		}
		return path.substring(0, path.lastIndexOf(mozile.filesep)+1);
	}
	
	return path;
}

/**
 * Ensures that a path is absolute. Appends the path to the root if it is not absolute.
 * @param {String} path The path to convert.
 * @param {String} root An absolute root URL. If none is given the document location is used.
 * @type String
 */
mozile.getAbsolutePath = function(path, root) {
	if(!path) return "";
	path = path.toString();
	if(mozile.isPathAbsolute(path)) return path;
	
	if(!root) root = location;
	root = mozile.getDirectory(root);
	root = mozile.getAbsolutePath(root);
	return mozile.joinPaths(root, path);
}

/**
 * Concatenates any number of strings using the mozile.filesep character.
 * @param {String} path Paths to join. Accepts any number of string arguments.
 * @type String
 */
mozile.joinPaths = function() {
	var path = "";
	var args, part;

	// If there are no arguments, return the empty string.
	if(arguments.length == 0) return path;
	// If there is one argument, and it's an array, use it as the array to join.
	else if(arguments.length == 1 && arguments[0] instanceof Array)
		args = arguments[0];
	// Otherwise, use the arguments array itself as the array to join.
	else args = arguments;

	for(var i=0; i < args.length; i++) {
		part = args[i];
		if(typeof(part) != "string") continue;
		if(path) {
			// Remove a "/" if they're doubled.
			if(path.charAt(path.length - 1) == mozile.filesep) {
				if(part.charAt(0) == mozile.filesep)
					part = part.substring(1);
			}
			// Add a "/" if there is none.
			else {
				if(part.charAt(0) != mozile.filesep)
					part = mozile.filesep + part;
			}
		}
		path += part;
	}
	return path;
}




/**
 * Ensures that a path is absolute. Appends the path to the root if it is not absolute.
 * @param {String} path The path to convert.
 * @param {String} root An absolute root URL. If none is given the document location is used.
 * @type String
 */
mozile.prompt = function(title, message, value, fn, scope, multiline) {
	if(!message || typeof(message) != "string") return false;
	if(!fn || typeof(fn) != "function") return false;

	// Ask the GUI factory for a prompt.
	if(mozile.gui && mozile.gui.factory && mozile.gui.factory.prompt &&
		typeof(mozile.gui.factory.prompt) == "function") {
		mozile.gui.factory.prompt(title, message, value, fn, scope, multiline);
	}
	// Use the built in JavaScript prompt.
	else {
		var result = prompt(message, value);
		var status = "ok";
		if(result === null) status = "cancel";
		fn.call(scope || mozile.window, status, result);
	}
	
	return true;
}



/**** Debugging Tools ****/

/**
 * Tools for debugging Mozile.
 * @type Object
 */
mozile.debug = new Object();
// JSDoc hack
mozile.debug.prototype = new mozile.Module;

/**
 * Indicates the level of debugging information to be delivered to the user.
 * Values can be "suppress", "warn", "inform", or "debug", in order from least verbose to most verbose.
 * @type String
 */
mozile.debug.alertLevel = "suppress";

/**
 * Indicates the level of debugging information to be stored in the messages array.
 * Values can be "suppress", "warn", "inform", or "debug", in order from least verbose to most verbose.
 * @type String
 */
mozile.debug.logLevel = "warn";

/**
 * An array of debugging messages.
 * @type Array
 */
mozile.debug.messages = new Array();

/**
 * A window where logged messages are sent.
 * @type Window
 */
mozile.debug.window = null;

/**
 * Compares two debugging levels.
 * @param {String} type The level to check against. Can be "alert" or "log".
 * @param {String} level The level to check. Can be "suppress", "warn", "inform", or "debug".
 * @type Boolean
 */
mozile.debug.isSelected = function(type, level) {
	// Setup
	if(typeof(type) != "string") return false;
	type = type.toLowerCase();
	var checkLevel;
	if(type == "alert") checkLevel = mozile.debug.alertLevel;
	else if(type == "log") checkLevel = mozile.debug.logLevel;
	else return false;
	checkLevel = checkLevel.toLowerCase();
	if(typeof(level) != "string") return false;
	level = level.toLowerCase();

	// Compare levels
	if(checkLevel == "suppress") return false;
	if(checkLevel == "warn") {
		if(level =="warn") return true;
		else return false;
	}
	if(checkLevel == "inform") {
		if(level == "warn" || level == "inform") return true;
		else return false;
	}
	if(checkLevel == "debug") return true;

	return false;
}


/**
 * A high priority debugging notification.
 * @param {String} caller The name of the function sending the notification.
 * @param {String} message The warning.
 * @type Void
 */
mozile.debug.warn = function(caller, message) {
	var level = "warn";

	var msg = "Mozile Warning ["+ caller +"] "+ message;
	if(mozile.debug.isSelected("alert", level)) {
		if(mozile.window.warn) warn(msg); // JsUnit tracing
		else mozile.alert(msg);
	}
	if(mozile.debug.isSelected("log", level)) {
		mozile.debug.log(caller, level, message);
	}
}

/**
 * A medium priority debugging notification.
 * @param {String} caller The name of the function sending the notification.
 * @param {String} message The warning.
 * @type Void
 */
mozile.debug.inform = function(caller, message) {
	var level = "inform";

	var msg = "Mozile Information ["+ caller +"] "+ message;
	if(mozile.debug.isSelected("alert", level)) {
		if(mozile.window.inform) inform(msg); // JsUnit tracing
		else mozile.alert(msg);
	}
	if(mozile.debug.isSelected("log", level)) {
		mozile.debug.log(caller, level, message);
	}
}

/**
 * A low priority debugging notification.
 * @param {String} caller The name of the function sending the notification.
 * @param {String} message The warning.
 * @type Void
 */
mozile.debug.debug = function(caller, message) {
	var level = "debug";

	var msg = "Mozile Debugging ["+ caller +"] "+ message;
	if(mozile.debug.isSelected("alert", level)) {
		if(mozile.window.debug) debug(msg); // JsUnit tracing
		else mozile.alert(msg);
	}
	if(mozile.debug.isSelected("log", level)) {
		mozile.debug.log(caller, level, message);
	}
}

/**
 * Store a debugging message in the messages array.
 * @param {String} caller The name of the function sending the warning.
 * @param {String} level The level of the message.
 * @param {String} message The warning.
 * @type Void
 */
mozile.debug.log = function(caller, level, message) {
	var date = new Date();
	var msg =  { 
		caller: caller, 
		level: level, 
		message: message, 
		date: date.toLocaleString(),
		toString: function() {
			return this.level.toUpperCase() +" ("+ this.date +") ["+ this.caller +"] "+ this.message;
		}
	};
	mozile.debug.messages.push(msg);
	
	// If the window is open, print the message to it and scroll to the bottom.
	if(mozile.debug.window && mozile.debug.window.document) {
		mozile.debug.window.document.write(msg +"<br/>\n");
		mozile.debug.window.scroll(0, document.body.clientHeight);
	}
	// If Firebug is present, log the message
	if(mozile.window.console) console.info(msg);
}

/**
 * Write all logged debugging messages to a new window.
 * @type Void
 */
mozile.debug.show = function() {
	if(!mozile.debug.window || !mozile.debug.window.document) {
		mozile.debug.window = window.open("", "MozileDebugging", "");
		mozile.debug.window.document.write("<h3>Mozile Debugging Messages</h3>");
		mozile.debug.window.document.write(mozile.debug.messages.join("<br/>\n") + "<br/>\n");
	}
	else mozile.debug.window = mozile.window.open("", "MozileDebugging", "");
}


/**
 * Informs the user about a problem. 
 * The method used to inform the user varies by what
 * @param {String} message
 * @type Void
 */
mozile.alert = function(message) {
	// TODO: Firebug support
	// TODO: Safari console support
	alert(message);
}


/**
 * Dumps information from error objects.
 * Internet Explorer needs special attention.
 * @param object The error object or object that the error is attached to.
 * @type String
 */
mozile.dumpError = function(object) {
	if(typeof(object) == "string") return object;
	if(!mozile.browser.isIE) return object.toString();
	var fields;

	// Error Object case
	if(object && object.description) {	
		fields = [
			"Name: "+ object.name,
			"Number: "+ object.number,
			"Message: "+ object.message,
			"Description: "+ object.description,
		];
		return fields.join("\n");
	}

	// parseError case
	if(!object) object = mozile.document;
	if(!object.parseError) object = mozile.document;
	if(!object.parseError) object = mozile.window;
	if(!object.parseError) return "[No error to parse]";
	fields = [
		"Error Code: "+ object.parseError.errorCode,
		"File Position: "+object.parseError.filepos,
		"Line: "+object.parseError.line,
		"Line Position: "+object.parseError.linepos,
		"Reason: "+object.parseError.reason,
		"Source Text: "+object.parseError.srcText,
		"Url: "+object.parseError.url
	];
	
	return fields.join("\n");
}

/**
 * Makes a single element editable.
 * @param elementOrId Either a DOM element or the id of an element.
 * @type Void
 */
mozile.editElement = function(elementOrId) {
	var element;
	if(typeof(elementOrId) == "string") {
		// HTML case
		if(mozile.document.documentElement.nodeName.toLowerCase() == "html") {
			element = mozile.document.getElementById(elementOrId);
		}
		// XML case
		else {
			mozile.require("mozile.dom");
			var results = mozile.dom.getElements("id", elementOrId, null, true);
			if(results.length) element = results[0];
			else return false;
		}
	}
	else if(elementOrId.nodeType && 
		elementOrId.nodeType == mozile.dom.ELEMENT_NODE) {
		element = elementOrId;
	}

	if(element) return mozile._markEditable(element, true);

	return false;
}

/**
 * Makes a set of elements editable.
 * @param listOrValue Either an array (or nodeList) of DOM elements or the value of an attribute (defaults to the "class" attribute).
 * @param {String} attribute Optional. The name of an attribute to search for. Defaults to "class". If the value is "local name" then the local name of the element is used (lowercase).
 * @type Void
 */
mozile.editElements = function(listOrValue, name) {
	var list;
	if(typeof(listOrValue) == "string") {
		mozile.require("mozile.dom");
		list = mozile.dom.getElements(name, listOrValue);
	}
	else if(listOrValue.length) {
		list = listOrValue;
	}

	if(list.length) {
		for(var i=0; i < list.length; i++) {
			if(list[i] && list[i].nodeType && 
				list[i].nodeType == mozile.dom.ELEMENT_NODE) {
				mozile.editElement(list[i]);
			}
		}
	}
	
	return undefined;
}

/**
 * Makes the whole document editable.
 * @param {Document} doc Optional. The document to make editable. Defaults to the current document.
 * @type Void
 */
mozile.editDocument = function(doc) {
	if(!doc) doc = mozile.document;
	mozile.editElement(doc.documentElement);
}

/**
 * Prevents a single element from editing.
 * @param elementOrId Either a DOM element or the id of an element.
 * @type Boolean
 */
mozile.protectElement = function(elementOrId) {
	var element;
	if(typeof(elementOrId) == "string") {
		// HTML case
		if(mozile.document.documentElement.nodeName.toLowerCase() == "html") {
			element = mozile.document.getElementById(elementOrId);
		}
		// XML case
		else {
			mozile.require("mozile.dom");
			var results = mozile.dom.getElements("id", elementOrId, null, true);
			if(results.length) element = results[0];
			else return false;
		}
	}
	else if(elementOrId.nodeType && 
		elementOrId.nodeType == mozile.dom.ELEMENT_NODE) {
		element = elementOrId;
	}

	if(element) return mozile._markEditable(element, false);

	return false;
}

/**
 * Protects a set of elements from editing.
 * @param listOrValue Either an array (or nodeList) of DOM elements or the value of an attribute (defaults to the "class" attribute).
 * @param {String} attribute Optional. The name of an attribute to search for. Defaults to "class". If the value is "local name" then the local name of the element is used (lowercase).
 * @type Void
 */
mozile.protectElements = function(listOrValue, name) {
	var list;
	if(typeof(listOrValue) == "string") {
		mozile.require("mozile.dom");
		list = mozile.dom.getElements(name, listOrValue);
	}
	else if(listOrValue.length) {
		list = listOrValue;
	}

	if(list.length) {
		for(var i=0; i < list.length; i++) {
			if(list[i] && list[i].nodeType && 
				list[i].nodeType == mozile.dom.ELEMENT_NODE) {
				mozile.protectElement(list[i]);
			}
		}
	}
	
	return undefined;
}

/**
 * Marks an element as editable or not editable.
 * @private
 * @param {Element} element The element to mark.
 * @param {Boolean} value The value to set.
 * @type Boolean
 */
mozile._markEditable = function(element, value) {
	if(!element) return null;
	mozile.require("mozile.edit");
	
	// Clear a protected mark.
	if(value === true && mozile.edit.setMark(element, "editable") === false &&
		mozile.edit.getContainer(element.parentNode)) {
		mozile.edit.setMark(element, "editable", undefined);
		if(mozile.edit.getMark(element, "contentEditable") == undefined) {
			element.removeAttribute("contentEditable");
		}
		element.ondrop = null;
		element.onpaste = null;
		return value;
	}
	
	mozile.edit.setMark(element, "editable", value);
	// Store the value of contentEditable.
	if(mozile.edit.getMark(element, "contentEditable") == undefined) {
		switch(element.getAttribute("contentEditable")) {
			case "true":
				mozile.edit.setMark(element, "contentEditable", true);
				break;
			case "false":
				mozile.edit.setMark(element, "contentEditable", false);
				break;
		}
	}

	// Set contentEditable to true.
	element.setAttribute("contentEditable", String(value));

	// Enable/disable drag and paste.
	if(value) {
		element.ondrop = mozile._nullFunction;
		element.onbeforepaste = mozile.edit.paste._preExecute;
		element.onpaste = mozile._nullFunction;
		mozile.dom.addClass(element, "mozileEditorContainer");
	}
	else {
		element.ondrop = null;
		element.onbeforepaste = null;
		element.onpaste = null;
		mozile.dom.removeClass(element, "mozileEditorContainer");
	}

	return value;
}

mozile._nullFunction = function() {
	mozile.window.event.returnValue = false;
	mozile.window.event.cancelBubble = true;
	return false; 
}


/**
 * Makes all text nodes inside editable elements editable by adding default commands to mozile.edit.
 * @param {Boolean} rich Optional. When true rich editing commands are included.
 * @type Void
 */
mozile.enableEditing = function(rich) {
	mozile.require("mozile.edit");
	mozile.edit.defaults.addCommand(mozile.edit.navigateLeftRight);
	mozile.edit.defaults.addCommand(mozile.edit.insertText);
	mozile.edit.defaults.addCommand(mozile.edit.removeText);
	
	if(rich) {
		mozile.require("mozile.edit.rich");
		mozile.edit.defaults.addCommand(mozile.edit.remove);
		//var splitBlocks = new mozile.edit.Split("splitBlocks");
		//splitBlocks.accel = "Return Enter";
		//mozile.edit.defaults.addCommand(splitBlocks);
	}
}

/**
 * Load the target RNG schema and use it as the schema for this document.
 * @param {String} target The desired schema. Can be any value accepted by mozile.rng.Schema.parse().
 * @param {String} localeBund Optional. The name of the localization bundle to use. If none is given, the method uses a name based on the target.
 * @type Boolean
 * @return True if the schema is found and parsed correctly.
 */
mozile.useSchema = function(target, localeBundle) {
	try {
		mozile.require("mozile.rng");
		mozile.require("mozile.edit");
		mozile.require("mozile.edit.rich");
		mozile.edit.extendRNG();
		mozile.schema = new mozile.rng.Schema();
		var validation = mozile.schema.parse(target);
		if(validation.isValid) {
			// Try to find the localeBundle name automatically.
			if(!localeBundle) {
				var match = target.match(/\/(\S+)\.(rng|xml)$/mi);
				if(match) localeBundle = match[1];
			}
			var localization = mozile.getLocalization(
				mozile.getDirectory(target), localeBundle);
			mozile.edit.generateCommands(mozile.schema, localization);
			mozile.schema.strings = localization;
			return true;
		}
		else {
			mozile.debug.inform("mozile.useSchema", "Schema validation failed.\n"+ validation.report(true));
			return false;
		}
	} catch(e) {
		mozile.debug.inform("mozile.useSchema", "Could not create schema for target '"+ target +"' because of an error:\n"+ mozile.dumpError(e));
		return false;
	}
}



/**** Operating System Detection ****/

/**
 * Tools for dealing with the operating system.
 * @type Object
 */
mozile.os = new Object();
// JSDoc hack
mozile.os.prototype = new mozile.Module;

/**
 * Check to see if the browser is running on a Mac.
 * @type Boolean
 */
mozile.os.isMac = false;
if(navigator.userAgent.match(/Macintosh/)) mozile.os.isMac = true;



/**** Browser Detection ****/

/**
 * Tools for dealing with the browser.
 * @type Object
 */
mozile.browser = new Object();
// JSDoc hack
mozile.browser.prototype = new mozile.Module;


/**
 * Check to see if this browser is Mozilla.
 * @type Boolean
 */
mozile.browser.isMozilla = false;

/**
 * If this is a Mozilla browser, this stores a number representing the major and minor versions. E.g. "1.7" or "1.8".
 * @type Number
 */
mozile.browser.mozillaVersion = 0;

mozile.browser.mozillaVersion = navigator.userAgent.match(/rv\:(\d+\.\d+)/);
if(mozile.browser.mozillaVersion && 
	Number(mozile.browser.mozillaVersion[1])) {
	mozile.browser.isMozilla = true;
	mozile.browser.mozillaVersion = Number(mozile.browser.mozillaVersion[1]);
}

/**
 * Check to see if this browser is Internet Explorer.
 * @type Boolean
 */
mozile.browser.isIE = false;
if(navigator.userAgent.match(/MSIE/)) mozile.browser.isIE = true;

/**
 * Check to see if this browser is Safari.
 * @type Boolean
 */
mozile.browser.isSafari = false;
if(navigator.userAgent.match(/Safari/)) mozile.browser.isSafari = true;

/**
 * If this is a Safari browser, this property stores a number representing the WebKit version. E.g. "418" or "420".
 * @type Number
 */
mozile.browser.safariVersion = 0;

mozile.browser.safariVersion = navigator.userAgent.match(/AppleWebKit\/(\d+)/);
if(mozile.browser.safariVersion && 
	Number(mozile.browser.safariVersion[1])) {
	mozile.browser.safariVersion = Number(mozile.browser.safariVersion[1]);
}

/**
 * Check to see if this browser is Opera.
 * @type Boolean
 */
mozile.browser.isOpera = false;
if(navigator.userAgent.match(/Opera/)) mozile.browser.isOpera = true;




/**** Final Configuration ****/

// Detect the content type as HTML or XML in Mozilla.
if(mozile.browser.isMozilla) {
	if(mozile.document.contentType && mozile.document.contentType == "text/html") {
		mozile.defaultNS = null;
	}
	else {
		mozile.defaultNS = "http://www.w3.org/1999/xhtml";
	}
}



Documentation generated by JSDoc on Wed Feb 20 13:25:28 2008