interface.js

Summary

This extends "core.js" to include interface elements such as a toolbar, several popup dialogs, as well as configurable commands.

Version: 0.7

Author: James A. Overton


Class Summary
MozileInterface
MozileCommandList
MozileCommand
MozileCommandSeparator

/* ***** BEGIN LICENSE BLOCK *****
 * Licensed under Version: MPL 1.1/GPL 2.0/LGPL 2.1
 * Full Terms at http://mozile.mozdev.org/license2.html
 *
 * 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 This extends "core.js" to include interface elements such as a toolbar, several popup dialogs, as well as configurable commands.
 * @link http://mozile.mozdev.org 
 * @author James A. Overton <james@overton.ca>
 * @version 0.7
 */



/** 
 * An associative array containing entries for every save method available, the default method, and the user's custom method. This is a key-value array, where the key is the method name and the value is an array. All the value-arrays include the keys: value, label, function. 
 * @type Object
 */
MozileMediator.prototype.getSaveModules = function() {
 	if(!this._saveModules) {
 		this._saveModules = new Object();

			// Define the built in dialog save method.
		var dialog = new MozileModule("Dialog");
		dialog.setOption("label", "Save to Dialog");
		dialog.save = function() {
			mozile.getSourceInterface().show();
		}
		this.addSaveModule(dialog);
		this.setDefaultSaveModule(dialog);
		this.setSaveOption("default", "content", mozile.getOption("content"));
		this.setSaveOption("default", "format", mozile.getMode());
 	}
 	return this._saveModules;
}

/** 
 * Gets a module from the list by its name.
 * @param {String} name
 * @type MozileModule
 */
MozileMediator.prototype.getSaveModule = function(name) {
 	if(this.getSaveModules()[name]) return this.getSaveModules()[name];
 	else return undefined;
}

/** 
 * Gets a save option. First checks for the value in the given mode's array, then tries to set a defaultValue, then searches the mode's module's options, and finally returns mozile.getOption(key).
 * @param {String} mode Can be "default" or "custom" or "current". "Current searches "custom" first and "default" second, and then assigns any values to "custom".
 * @param {String} key 
 * @param defaultValue If there is no matching key for the given mode, the defaultValue will be assigned to that key.
 * @return The value if found or the defaultValue if given. Otherwise undefined.
 */
MozileMediator.prototype.getSaveOption = function(mode, key, defaultValue) {
	if(!this._saveOptions) this._saveOptions = new Object();
	if(mode == "default" || mode == "custom") {
		if(!this._saveOptions[mode]) this._saveOptions[mode] = new Object();
		if(this._saveOptions[mode][key] != undefined) return this._saveOptions[mode][key];
		else if (defaultValue != undefined)	return this.setSaveOption(mode, key,  defaultValue);
		else if (this._saveOptions[mode]["module"] && 
				this._saveOptions[mode]["module"].getOption(key) != undefined) 
					return this._saveOptions[mode]["module"].getOption(key);
		else return this.getOption(key);
	}
	else if (mode == "current") {
		if(this.getSaveOption("custom", key) != undefined) return this.getSaveOption("custom", key);
		else if(this.getSaveOption("default", key) != undefined) return this.getSaveOption("default", key);
		else if (defaultValue != undefined)	return this.setSaveOption("custom", key,  defaultValue);
		else return undefined;
	}
	else throw Error("Invalid save mode");
}

/** 
 * Sets a module from the list by its name.
 * @param {String} mode Can be "default" or "custom" or "current".
 * @param {String} key 
 * @param value The value to be assigned.
 * @return The value given.
 */
MozileMediator.prototype.setSaveOption = function(mode, key, value) {
	if(!this._saveOptions) this._saveOptions = new Object();
	if(mode == "default" || mode == "custom") {
		if(!this._saveOptions[mode]) this._saveOptions[mode] = new Object();
		this._saveOptions[mode][key] = value;
		return value;
	}
	else if (mode == "current") {
		if(this._saveOptions["custom"]) return this.setSaveOption("custom", key, value);
		else return this.setSaveOption("default", key, value);
	}
	else throw Error("Invalid save mode");
}

/** 
 * Sets the default save module.
 * @param {MozileModule} module
 * @type Void
 */
MozileMediator.prototype.useDefaultSaveOptions = function() {
	if(this._saveOptions["custom"]) this._saveOptions["custom"] = null;
}

/** 
 * Sets the default save module.
 * @param {MozileModule} module
 * @type MozileModule
 */
MozileMediator.prototype.setDefaultSaveModule = function(module) {
	this.setSaveOption("default", "module", module);
	return module;
}

/** 
 * Sets the custom save module.
 * @param {MozileModule} module
 * @type MozileModule
 */
MozileMediator.prototype.setCustomSaveModule = function(module) {
	this.setSaveOption("custom", "module", module);
	return module;
}

/** 
 * Adds a module to the modules list.
 * @param {MozileModule} module
 * @type MozileModule
 * @return The module object given.
 */
MozileMediator.prototype.addSaveModule = function(module) {
	if(module.getName()) this.getSaveModules()[module.getName()] = module;
	else throw Error("Invalid module.");
	return module;
}

/** 
 * An associative array containing all of the commands (but not the command lists) registered with this Mozile object. The keys are the id strings the commands, and values the command objects.
 * @type Object
 */
MozileMediator.prototype.getCommands = function() {
 	if(!this._commands) this._commands = new Object();
 	return this._commands;
}

/** 
 * Gets a command from the list by its id.
 * @param {String} id
 * @type MozileCommand
 */
MozileMediator.prototype.getCommand = function(id) {
 	if(this.getCommands()[id]) return this.getCommands()[id];
 	else return undefined;
}

/** 
 * Adds a command to the commands list.
 * @param {MozileCommand} command
 * @type MozileCommand
 * @return The command object given.
 */
MozileMediator.prototype.addCommand = function(command) {
	if(command.getId()) this.getCommands()[command.getId()] = command;
	else throw Error("Invalid command.");
	return command;
}

/** 
 * An array containing all of the keyboard shortcuts (accelerators) for Mozile commands. The keys are strings of the format "Control-Shift-F", while the values are the command objects.
 * @type Object
 */
MozileMediator.prototype.getAccelerators = function() {
 	if(!this._accelerators) this._accelerators = new Object();
 	return this._accelerators;
}

/** 
 * Gets a command from the list by its accelerator string.
 * @param {String} accel
 * @type MozileCommand
 */
MozileMediator.prototype.getAccelerator = function(accel) {
 	if(this.getAccelerators()[accel]) return this.getAccelerators()[accel];
 	else return undefined;
}

/** 
 * Adds an accelerator string and a command to the accelerator list.
 * @param {String} accel The accelerator string.
 * @param {MozileCommand} command
 * @type MozileCommand
 * @return The command object given.
 */
MozileMediator.prototype.addAccelerator = function(accel, command) {
	this.getAccelerators()[accel] = command;
	return command;
}




/** 
 * Gets Mozile's "About" interface box element. This interface describes Mozile. The interface is created if necessary.
 * @type MozileInterface
 */
MozileMediator.prototype.getAboutInterface = function() {
	if(!this._aboutInterface) {
		this._aboutInterface = new MozileInterface(this.getRoot()+"core/about.xml", "MozileAboutInterface: width=450px");
		this._aboutInterface.init = function() {
			document.getElementById("mozileVersion").value = mozile.getVersion();
		}
	}	
	return this._aboutInterface;
}

/** 
 * Gets Mozile's "Save As" interface box element. The interface is created if necessary.
 * @type MozileInterface
 */
MozileMediator.prototype.getSaveInterface = function() {
	if(!this._saveInterface) {
			// Save Interface
		this._saveInterface = new MozileInterface(this.getRoot()+"core/save.xml", "MozileSaveInterface: width=330px");
		
		
		/**** Define Save Interface Methods ****/

		/** Mozile Save Init -
		 * Initialize the Save interface. First, find the elements for the global variables, and then set them up.
		 * 
		 * @return Always true.
		 */	
		this._saveInterface.init = function() {
			this.savePreset = document.getElementById("mozileSavePresetList");
			this.saveContent = document.getElementById("mozileSaveContentList");
			this.saveFormat = document.getElementById("mozileSaveFormatList");
			this.saveMethod = document.getElementById("mozileSaveMethodList");
			
				// Clear save methods
			while(this.saveMethod.length) {
				this.saveMethod.removeChild(this.saveMethod.firstChild);
			}
			
			// Generate save method options
			var option;
			for(key in mozile.getSaveModules()) {
				option = document.createElement("option");
				option.setAttribute("value", mozile.getSaveModule(key).getName());
				option.appendChild(document.createTextNode(mozile.getSaveModule(key).getOption("label")));
				this.saveMethod.appendChild(option);
			}
			
			this.restore("current");
		}
		
		/** Mozile Method Fields -
		 * Show and hide various fields. Right now we only have one: mozileSaveURLBox.
		 * 
		 * @return Always true.
		 */	
		this._saveInterface.methodFields = function() {
			var urlBox = document.getElementById("mozileSaveURLBox");
			var url = document.getElementById("mozileSaveToURL");
			if(mozile.getSaveModule(this.saveMethod.value).getOption("showURL")) {
				urlBox.collapsed = false;
				if(url.value == "" || url.value == "undefined") url.value = mozile.getSaveModule(this.saveMethod.value).getOption("url");
			}
			else urlBox.collapsed = true;
		}
		
		/** Mozile Restore Default -
		 * Restore the page default settings, if they exist
		 * 
		 * @return Always true.
		 */	
		this._saveInterface.restore = function(mode) {
			if(mode == undefined) mode = this.savePreset.value;
			try {
				this.saveMethod.value = mozile.getSaveOption(mode, "module").getName();
				this.saveContent.value = mozile.getSaveOption(mode, "content");
				this.saveFormat.value = mozile.getSaveOption(mode, "format");
				document.getElementById("mozileSaveToURL").value = mozile.getSaveOption(mode, "url");
			}
			catch(e) {
				// fail silently
			}
			this.methodFields();
		}
		
		/** Mozile Save Changes -
		 * If there have been changes, set the "preset" menu to "custom" and store the changes.
		 * 
		 * @return Always true.
		 */	
		this._saveInterface.changed = function() {
			this.methodFields();
			this.storeCustom();
			this.savePreset.value = "custom";
		}
		
		/** Mozile Save Changes -
		 * When the "save" button is pressed, collect all the values that have been set,  and call Mozile.save().
		 * 
		 * @return Always true.
		 */	
		this._saveInterface.done = function() {
			if(this.savePreset.value == "default") {
				mozile.useDefaultSaveOptions();
			}
			else {
				this.storeCustom();
			}
			this.hide();
				// Tell the save module to prompt for more information (if possible).
			mozile.setSaveOption("current", "prompt", true);
			mozile.getSaveOption("current", "module").save();
			mozile.changesSaved = true;
		}
		
		/** Mozile Store Custom -
		 * Store the custom values of all the menus and fields.
		 * 
		 * @return Always true.
		 */	
		this._saveInterface.storeCustom = function() {
			mozile.setCustomSaveModule(mozile.getSaveModule(this.saveMethod.value));
			mozile.setSaveOption("custom", "content", this.saveContent.value);
			mozile.setSaveOption("custom", "format", this.saveFormat.value);
			if(mozile.getSaveModule(this.saveMethod.value).getOption("showURL")) {
				mozile.setSaveOption("custom", "url", document.getElementById("mozileSaveToURL").value);
			}
		}

	}	
	return this._saveInterface;
}

/** 
 * Gets Mozile's "Source" interface box element. This interface displays the source code for the document when the "Display" save method is used. The interface is created if necessary.
 * @type MozileInterface
 */
MozileMediator.prototype.getSourceInterface = function() {
	if(!this._sourceInterface) {
		this._sourceInterface = new MozileInterface(this.getRoot()+"core/source.xml", "MozileSourceInterface: width=80%");
		this._sourceInterface.init = function() {
			document.getElementById("mozileSourceContent").value = mozile.getSaveOption("current", "content");
			document.getElementById("mozileSourceFormat").value = mozile.getSaveOption("current", "format");
			document.getElementById("mozileSourceText").value = mozile.content();
		}
		this._sourceInterface.update = function() {
			var content = document.getElementById("mozileSourceContent").value;
			var format = document.getElementById("mozileSourceFormat").value;
			if(format == "XHTML") format = "XML";
			var text = eval("mozile."+ content +"To"+ format +"()");
			if(text) document.getElementById("mozileSourceText").value = text;
		}
	}	
	return this._sourceInterface;
}

/** 
 * Gets Mozile's "Message" interface box element. This interface shows the status and debugging messages that Mozile has collected. The interface is created if necessary.
 * @type MozileInterface
 */
MozileMediator.prototype.getMessageInterface = function() {
	if(!this._messageInterface) {
		this._messageInterface = new MozileInterface(this.getRoot()+"core/message.xml", "MozileMessageInterface: width=80%");
		this._messageInterface.init = function() {
			document.getElementById("mozileVersion").value = mozile.getVersion();
		}
		

/**** Debug Interface Configuration ****/

		/** Mozile Debug Init -
		 * Initializes the debugging dialog.
		 *
		 * @param event The event object which triggers the initialization.
		 * @return Always true.
		 */
		this._messageInterface.init = function() {
			this.reset();
		}
			
		/** Mozile Debug Reset -
		 * Clears the filter and rebuilds the bug list.
		 *
		 * @param event The event object which triggers the initialization.
		 * @return Always true.
		 */
		this._messageInterface.reset = function() {
			document.getElementById("mozileFilterText").value = "";
			document.getElementById("mozileFilterLevel").selectedIndex=0;
			this.filter();
		}
			
		/** Mozile Debug Clear -
		 * Clears the list and empties the mozileDebugArray.
		 *
		 * @param event The event object which triggers the initialization.
		 * @return Always true.
		 */
		this._messageInterface.clear = function() {
			mozileDebugList = new Array();
			this.reset();
		}
			
		/** Mozile Debug Filter -
		 * Rebuilds the bug list, filtering based on the content of the filterText input field.
		 *
		 * @return Always true.
		 */
		this._messageInterface.filter = function() {
			
			var selected = document.getElementById("mozileFilterLevel").value;
			var text = document.getElementById("mozileFilterText").value;
			
			//var mozileDebugList = mozileDebugList;
			var vbox = document.getElementById("mozileMessageBox");
			while(vbox.childNodes.length) {
				vbox.removeChild(vbox.firstChild);
			}
			var count = 0;	
			
			// Loop over all the bug entries
			for(var i=0; i < mozileDebugList.length; i++) {
				var bug = mozileDebugList[i];
					// If the selected value is "status", and this entry is not a "Status Message", then ignore it.
				if(selected == "status" && !bug[1]["Status Message"] ) continue;
					// if the selected level is lower, ignore this entry
				if(selected > bug[2]) continue;			
				
				var pattern = new RegExp(text, "i"); // case insensitive 
				var search = pattern.test(bug[0]) && pattern.test(bug[1]["File"]) && pattern.test(bug[1]["Function"]) && pattern.test(bug[3]);
				if(pattern.test(bug[0]) || pattern.test(bug[1]["File"]) || pattern.test(bug[1]["Function"]) || pattern.test(bug[3])) {
					count++;	
					vbox.appendChild(this.entry(mozileDebugList[i]));
				}
			}
			
			document.getElementById("mozileMessageCount").value="Showing "+count+" of "+mozileDebugList.length+" Messages";
		}
			
		/** Mozile Debug Entry -
		 * Create an entry for the debugging item, which consists of an hbox filled with description elements.
		 *
		 * @param bug The array for this bug entry.
		 * @return The empty node.
		 */
		this._messageInterface.entry = function(bug) {
			var hbox = document.createElementNS(XULNS, "hbox");
			hbox.setAttribute("class", "level"+bug[2]);
			hbox.setAttribute("align", "center");
			
			var datestamp = document.createElementNS(XULNS, "description");
			datestamp.setAttribute("class","datestamp");
			datestamp.setAttribute("value", bug[0]);
			hbox.appendChild(datestamp);
			
			var button = document.createElementNS(XULNS, "button");
			button.setAttribute("class","functionButton");
			button.setAttribute("type", "menu");
			button.setAttribute("image", mozile.getRoot()+"images/info.png");
			
			var menupopup = document.createElementNS(XULNS, "menupopup");
			var functions;
			for(key in bug[1]) {
				functions = document.createElementNS(XULNS, "description");
				functions.setAttribute("class","functions");
				functions.appendChild(document.createTextNode(key +" = "+ bug[1][key]));
				menupopup.appendChild(functions);
			}
			functions = document.createElementNS(XULNS, "description");
			functions.setAttribute("class","functions");
			functions.appendChild(document.createTextNode("Level = "+ bug[2]));
			menupopup.appendChild(functions);
			
			button.appendChild(menupopup);
			hbox.appendChild(button);
			
			var message = document.createElementNS(XULNS, "description");
			message.setAttribute("class","message");
			message.appendChild(document.createTextNode(bug[3]));
			hbox.appendChild(message);
			return hbox;
		}

	}	
	return this._messageInterface;
}





/**
 * Does all the work required to add the Mozile toolbar to the current document. This includes adding a new style sheet to the document, and adding a CSS rule which associates an XBL binding with the body element (in the case of X/HTML), or a new "mozileToolbar" element (which is inserted into an XML document).
 * @type MozileCommandList
 * @return The root command list for the toolbar.
 */
MozileMediator.prototype.getCommandList = function() {
	if(!this._commandList) {
		
		// Create base command list object, and populate it.
		this._commandList = new MozileCommandList("MozileCommandList: id=Mozile-RootList, label='Mozile Root List'");
		
		// Create the basic Mozile command list.
		var mozileList = this._commandList.createCommand("MozileCommandList: id=Mozile-firstList, label='Mozile First List', image='"+this.getRoot()+"images/Mozile-20.png', buttonPosition=0, menuPosition=0");
		
	
		var about = mozileList.createCommand("MozileCommand: id=Mozile-About, label='About Mozile'");
		about.execute = function(event) {
			mozile.getAboutInterface().show();
		}
		
			// Accelerator List (Keyboard Shortcuts)
		var accel = mozileList.createCommand("MozileCommand: id=Mozile-Accelerators, label='Keyboard Shortcuts'");
		accel.execute = function(event) {
			if(mozile.getOption("keyboardShortcuts")) {
				var message = "Mozile defines the following keyboard shortcuts:";
				var accels = mozile.getAccelerators();
				for(key in accels) {
					message = message +"\n"+ mozile.getAccelerator(key).getOption("label") +" => "+key;
				}
				alert(message);
			} 
			else {
				alert("Mozile keyboard shortcuts have been turned off.");
			}
		}
	
		var debug = mozileList.createCommand("MozileCommand: id=Mozile-Debug, label=Debug");
		debug.execute = function(event) {
			mozile.getMessageInterface().show();
		}
		
		var report = mozileList.createCommand("MozileCommand: id=Mozile-ReportBugs, label='Report a Bug'");
		report.execute = function(event) {
			window.open("http://mozile.mozdev.org/bugs.html", "Mozile Bugs", "");
		}
		
		var home = mozileList.createCommand("MozileCommand: id=Mozile-Home, label='Mozile Home'");
		home.execute = function(event) {
			window.open("http://mozile.mozdev.org", "Mozile Home", "");
		}
		
		var help = mozileList.createCommand("MozileCommand: id=Mozile-Help, label='Help'");
		help.execute = function(event) {
			window.open(mozile.getRoot()+"docs/index.html", "Mozile Help", "");
		}
		
		var save = this._commandList.createCommand("MozileCommand: id=Mozile-Save, label=Save, tooltip='Save to a dialog', accelerator='Command-S', image='"+this.getRoot()+"images/filesave.png'");
		save.execute = function(event) {
			mozile.save();
		}
	
		var saveAs = this._commandList.createCommand("MozileCommand: id=Mozile-SaveAs, label='Save As', tooltip='Save as...', accelerator='Command-Shift-S', image='"+this.getRoot()+"images/filesaveas.png'");
		saveAs.execute = function(event) {
			mozile.saveAs();
		}

		this._commandList.createCommand("MozileCommandSeparator: id=Mozile-RootSeparator");
	}
	return this._commandList;
}


/** 
 * Does all the work required to add the Mozile toolbar to the current document. This includes adding a new style sheet to the document, and adding a CSS rule which associates an XBL binding with the body element (in the case of X/HTML), or a new "mozileToolbar" element (which is inserted into an XML document).
 * @type Element
 */
MozileMediator.prototype.getToolbar = function() {
	if(this.isExtension()) return null;
	if(!this._toolbar) {
		// Add basic style-sheet to the document.
		var link = new MozileLinkResource("Mozile-Core-interface.css", this.getRoot() + "core/interface.css")
		link.load();
		
		// Add a new rule to the end of the sheet which binds the toolbar to the body element.
		var rule;
		if(document.documentElement.tagName.toLowerCase()=="html") {
			rule = "body { -moz-binding: url(" + this.getRoot() +"core/interface.xml#toolbar); }";
		}
		else {
			rule = "mozileToolbar { -moz-binding: url(" + this.getRoot() +"core/interface.xml#toolbar); }";
			document.documentElement.insertBefore(document.createElement("mozileToolbar"), document.documentElement.firstChild);
		}
		this.getStyleSheet().insertRule(rule, this.getStyleSheet().cssRules.length);
		return null;
	}
	else return this._toolbar;
}


/** 
 * Gets the statusbar element, creating it fi necessary.
 * @type Void
 */
MozileMediator.prototype.getStatusbar = function() {
	if(!this._statusbar) {
		this.getToolbar();
		return null;
	}
	return this._statusbar;
}


/** 
 * Hides the Mozile toolbar.
 * @type Void
 */
MozileMediator.prototype.hideToolbar = function() {
	var f = new Array();
	f["File"] = "core/interface.js";
	f["Function"] = "Mozile.hideToolbar()";
	this.debug(f,1,"Hiding toolbar");
	
	if(this.isExtension())  return;
	if(!this.isEditable()) return;
	if(this.getToolbar() == null) return;
	
	this.getToolbar().collapsed = true;
	this.getStatusbar().collapsed = true;
	if(this._toolbarInterval) window.clearInterval(this._toolbarInterval);
}


/** Mozile - Show Toolbar -
 * Shows (unhides) the Mozile toolbar. If this is the first time that the toolbar has been shown, then it calls this.initializeToolbar().
 * 
 * @return True if successful.
 */
MozileMediator.prototype.showToolbar = function() {
	var f = new Array();
	f["File"] = "core/interface.js";
	f["Function"] = "Mozile.showToolbar()";
	//this.debug(f,1,"Showing toolbar. First time? " + this._firstToolbarShow);
	
	if(this.isExtension()) return false;
	if(!this.isEditable()) return false;
	
	if(this._firstToolbarShow) {
		if(mozile.getToolbar() == null) {
			return false;
		}
		
		if(this.getOption("toolbarPosition") == "absolute") {
			this.getToolbar().style.position = "absolute";
			this.getStatusbar().style.position = "absolute";
		}
		
		this.getToolbar().appendChild(this.getCommandList().getBox());
		this.storeState("Initial state");
		this._firstToolbarShow=false;
	}
	
	if(this.getOption("toolbarUpdateFrequency") == 2) {
		this._toolbarInterval = window.setInterval("mozile.moveToolbar()", mozile.getOption("defaultInterval"));
	}
	
	if(this.getCommandList().updateBox()) {
		this.getToolbar().removeChild(this.getToolbar().firstChild);
		this.getToolbar().appendChild(this.getCommandList().getBox());
	}
		
	this.getToolbar().collapsed = false;
	this.getStatusbar().collapsed = false;
	
	this.moveToolbar();
	
	return true;
}

/** Mozile - Move Toolbar -
 * Centres the Mozile toolbar and statusbar in the window, by calculating the proper size of the CSS "left" property.
 * 
 * @return True if successful.
 */
MozileMediator.prototype.moveToolbar = function() {
	var f = new Array();
	f["File"] = "core/interface.js";
	f["Function"] = "Mozile.moveToolbar()";
	//this.debug(f,1,"Moving toolbar.");
	
	if(this.isExtension()) return false;
	if(!this.isEditable()) return false;
	if(this.getToolbar() == null) return false;

	if(!this.lastWindowSize) {
		this.lastWindowSize = new Array();
	}
	else {	
		if(this.lastWindowSize["innerWidth"] == window.innerWidth && 
			this.lastWindowSize["innerHeight"] == window.innerHeight &&
			this.lastWindowSize["pageXOffset"] == window.pageXOffset &&
			this.lastWindowSize["pageYOffset"] == window.pageYOffset &&
			this.lastWindowSize["toolbarWidth"] == this.getToolbar().boxObject.width) {
			return true;
		}
	}
		
	this.debug(f,1,"Moving toolbar.");
	
	// This seems unnecessary, but it makes the this.getToolbar().boxObject.width read properly (or at least better).
	this.getToolbar().style.left= "0px";
	// Set the left position so the toolbars are centred.
	if(window.innerWidth - this.getToolbar().boxObject.width >= 0) {
		this.getToolbar().style.left = (window.innerWidth - this.getToolbar().boxObject.width) / 2 +"px";
	}
	else {
		this.getToolbar().style.left = "0px";
	}
	if(window.innerWidth - this.getStatusbar().boxObject.width >= 0) {
		this.getStatusbar().style.left = (window.innerWidth - this.getStatusbar().boxObject.width) / 2 +"px";
	} 
	else {
		this.getStatusbar().style.left = "0px";
	}

	
	// If the positioning type is "absolute" set the top position for the toolbars.
	if(this.getToolbar().style.position == "absolute") {
		this.debug(f,1,"Moving toolbars");
		this.getToolbar().style.top = window.pageYOffset - 1 +"px";
		this.getStatusbar().style.top = window.pageYOffset + window.innerHeight - this.getStatusbar().boxObject.height + 1 +"px";	
	}
	// Store the current size and position.
	this.lastWindowSize["innerWidth"] = window.innerWidth;
	this.lastWindowSize["innerHeight"] = window.innerHeight;
	this.lastWindowSize["pageXOffset"] = window.pageXOffset;
	this.lastWindowSize["pageYOffset"] = window.pageYOffset;
	this.lastWindowSize["toolbarWidth"] = this.getToolbar().boxObject.width;

	
	//this.debug(f,2, window.innerWidth +" "+ this.getToolbar().style.left +"/"+ this.getToolbar().boxObject.width +" "+ this.statusbar.style.left +"/"+ this.statusbar.boxObject.width);
	return true;
}


/** Mozile - Update Toolbar -
 * Updates all of the commands in the commandList so that they will reflect the current selection.
 * 
 * @param force Optional Boolean, forces update when "true" and toolbarUpdateFrequency > 0.
 * @return True if successful.
 */
MozileMediator.prototype.updateToolbar = function() {
	var f = new Array();
	f["File"] = "core/interface.js";
	f["Function"] = "Mozile.updateToolbar()";
	this.debug(f,1,"Updating toolbar");
	
	if(this.isExtension())  return false;
	if(!this.isEditable()) return false;
	
	if(this.getOption("toolbarUpdateFrequency")==0) return true;
	
	var force = false;
	if(arguments.length > 0) {
		force = arguments[0];
	}
	
	var selection = mozile.getSelection();
	
	// If the update is not being forced, then check to see if it is required.
	if(!force) {
		// Don't update if the node has not changed.
		if(selection.focusNode == this._lastFocusNode) {
			this.debug(f,0,"Toolbar update not required");
			return true;
		}	
	}
	
	this.debug(f,1,"Updating toolbar");
	this._lastFocusNode = selection.focusNode;
	
	// Update all the commands in the command list
	for(var id in this.getCommands()) {
		try {
			this.getCommand(id).update();
		} catch(e) {
			//alert("Error "+e+"\n"+command);
		}
		
	}
	
	this.moveToolbar();
	
	return true;
}



/** Mozile - Save -
 * This function sets the Mozile.saveConfig property and calls the appropriate save function from Mozile Save List: either "default" or "custom". If neither exists, then it opens the Save As dialog.
 * 
 * @return Always true.
 */
MozileMediator.prototype.save = function() {
	this.setSaveOption("current", "prompt", false);
	this.getSaveOption("current", "module").save();
	this.changesSaved = true;
}


/** Mozile - Save As -
 * Opens Mozile's Save As dialog.
 * 
 * @return Always true.
 */
MozileMediator.prototype.saveAs = function() {
	mozile.getSaveInterface().show();
}




/** Mozile - Execute Command -
 * Calls the command() method of the command object corresponding to the given id.
 * 
 * @param id The id of the node which triggered the command. 
 * @param event The event which triggered this command. 
 * @return Always true.
 */
MozileMediator.prototype.executeCommand = function(id, event) {
	var f = new Array();
	f["File"] = "core/interface.js";
	f["Function"] = "Mozile.executeCommand()";
	this.debug(f,1,"Executing command "+ id +" "+ event);
	// Remove the "-Button" or "-Menuitem" part of the id, if there is one.
	var cleanId = /(.*)(\-Button|\-Menuitem)$/;
	var result = cleanId.exec(id);
	//alert(result);
	var commandId;
	if(result) commandId = result[1];
	else commandId = id;
	if(!this.getCommand(commandId)) return false;
	// If the command id is registered, call the command method of the corresponding command object.
	else {
		var command = this.getCommand(commandId);
		
		// If the keyCounter is not 0, then something has changed since the last command, so store the state.
		if(this.keyCounter != 0) this.storeState(command);
		
		// Execute the command
		result = command.execute(event);
		
		// If there is some result, and this is not an undo or redo command, store the state after the change.
		if(result && command.getId()!="Mozile-Undo" && command.getId()!="Mozile-Redo") this.storeState(command);
		this.updateToolbar(true);
		return true;
	}
}










/****  Mozile Command Object ****/
/** Mozile Command Object -
 * Creates a Mozile command object, which controls the button, menuitem, and code associated with a Mozile editing command.
 * <p>Configuration String format (particular command objects may have other options): "MozileCommandObject: id=CommandId, label='Command Label', tooltip='Command Tool Tip', image='/path/to/image.file', accesskey=C, accelerator='Command-C', buttonPosition=0, menuPosition=0, debugLevel=0"
 * <p>Accelerator format must follow this sequence: "Command-Meta-Control-Alt-Shift-Key". Mozile will check the UserAgent string for the browser, and replace "Command" with "Control" on Linux and Windows, or "Meta" on Macintosh. One of "Command", "Control" or "Meta" must be present, and "Key" is a single uppercase character.
 * @constructor
 * @param {String} configString A properly formatted configuration string.
 */
function MozileCommand(configString) {

	/**
	 * @private
	 * @type String
	 */
	this._configString = String(configString);

}

MozileCommand.prototype = new MozileComponent;
MozileCommand.prototype.constructor = MozileCommand;

/**
 * Gets the id for the module. Throws an error if none exists.
 * @type String
 */
MozileCommand.prototype.getId = function() {
	if(!this.getOption("id")) throw Error("Invalid configuration string.");
	return this.getOption("id");
}

/**
 * @type XULElement
 */
MozileCommand.prototype.getButton = function() {
	if(!this._button) this._createButton();
	return this._button;
}

/**
 * @type XULElement
 */
MozileCommand.prototype.getMenuitem = function() {
	if(!this._menuitem) this._createMenuitem();
	return this._menuitem;
}

/**
 * Checks to see if the command is active in the current selection.
 * @type Boolean
 */
MozileCommand.prototype.isActive = function() {
	return false;
}

/**
 * Updates the button and menuitem.
 * @type Void
 */
MozileCommand.prototype.update = function() {
	var active = this.isActive();
	this.getButton().setAttribute("active", active);
	this.getMenuitem().setAttribute("checked", active);
}

/**
 * Executes the command.
 * @param {Event} event The event object which triggered the command.
 * @type Void
 */
MozileCommand.prototype.execute = function(event) {
	alert("Command "+ this.getId() +" executed by event "+ event);
}


/** 
 * Creates an XUL button element for this command.
 * @private
 * @type XULElement
 */
MozileCommand.prototype._createButton = function() {
	this._button = document.createElementNS(XULNS, "toolbarbutton");
	this._button.setAttribute("id", this.getId() +"-Button");
	this._button.setAttribute("class", "mozileButton");
	this._button.setAttribute("image", this.getOption("image"));
	this._button.setAttribute("label", this.getOption("label"));
	if(this.getOption("tooltip")) this._button.setAttribute("tooltiptext", this.getOption("tooltip"));
	return this._button;
}

/** 
 * Creates an XUL menuitem element for this command.
 * @private
 * @type XULElement
 */
MozileCommand.prototype._createMenuitem = function() {
	this._menuitem = document.createElementNS(XULNS, "menuitem");
	this._menuitem.setAttribute("id", this.getId() +"-Menuitem");
	this._menuitem.setAttribute("class", "mozileMenuitem");
	//this._menuitem.setAttribute("type", "checkbox");
	this._menuitem.setAttribute("label", this.getOption("label"));
	if(this.getOption("tooltip")) this._menuitem.setAttribute("tooltiptext", this.getOption("tooltip"));
	if(this.getOption("accesskey")) this._menuitem.setAttribute("accesskey", this.getOption("accesskey"));
	return this._menuitem;
}



/** 
 * A very simple command which does nothing except get displayed as a toolbarseparator or menuseparator.
 * @param {String} configString A properly formatted configuration string.
 */
function MozileCommandSeparator(configString) {

	/**
	 * @private
	 * @type String
	 */
	this._configString = String(configString);

}

MozileCommandSeparator.prototype = new MozileCommand();
MozileCommandSeparator.prototype.constructor = MozileCommandSeparator;


/**
 * Overrides parent class and does nothing.
 * @type Void
 */
MozileCommandSeparator.prototype.update = function() { }

/** 
 * Creates an XUL separator element for this command.
 * @private
 * @type XULElement
 */
MozileCommandSeparator.prototype._createButton = function() {
	this._button = document.createElementNS(XULNS, "separator");
	this._button.setAttribute("id", this.getId() +"-Separator");
	this._button.setAttribute("class", "mozileSeparator");
	return this._button;
}

/** 
 * Creates an XUL menuseparator element for this command.
 * @private
 * @type XULElement
 */
MozileCommandSeparator.prototype._createMenuitem = function() {
	this._menuitem = document.createElementNS(XULNS, "menuseparator");
	this._menuitem.setAttribute("id", this.getId() +"-Menuseparator");
	this._menuitem.setAttribute("class", "mozileMenuseparator");
	return this._menuitem;
}




/** Mozile Command List Object -
 * Creates a Mozile command list object, inheriting from MozileCommand, which controls the box and menu associated with a group of Mozile editing commands.
 * @constructor
 * @param {String} configString A properly formatted configuration string.
 */
function MozileCommandList(configString) {

	/**
	 * @private
	 * @type String
	 */
	this._configString = String(configString);

	/**
	 * Holds references to the commands that have requested buttons, in the order they should be displayed.
	 * @private
	 * @type Array
	 */
	this._buttonArray = new Array();

	/**
	 * Holds references to the commands that have requested menuitems, in the order they should be displayed.
	 * @private
	 * @type Array
	 */
	this._menuArray = new Array();
	
}

MozileCommandList.prototype = new MozileCommand;
MozileCommandList.prototype.constructor = MozileCommandList;

/**
 * Lists have menus, not menuitems. Returns getMenu().
 * @type XULElement
 */
MozileCommandList.prototype.getMenuitem = function() {
	return this.getMenu();
}

/**
 * Gets the div element which will contain the list's commands.
 * @type HTMLDivElement
 */
MozileCommandList.prototype.getBox = function() {
	if(!this._box) this._createBox();
	return this._box;
}

/**
 * Gets the menu element which will contain the list's commands.
 * @type XULElement
 */
MozileCommandList.prototype.getMenu = function() {
	if(!this._menu) this._createMenu();
	return this._menu;
}

/**
 * Gets the commandList object.
 * @type Object
 */
MozileCommandList.prototype.getCommands = function() {
	if(!this._commands) this._commands = new Object;
	return this._commands;
}

/**
 * Gets a command from the command list given its id.
 * @type Object
 */
MozileCommandList.prototype.getCommand = function(id) {
	if(this.getCommands()[id]) return this.getCommands()[id];
	else return undefined;
}

/** 
 * Adds a command to the commands list.
 * @param {MozileCommand} command
 * @type MozileCommand
 * @return The command object given.
 */
MozileCommandList.prototype.addCommand = function(command) {
	if(command.getId()) this.getCommands()[command.getId()] = command;
	else throw Error("Invalid command.");
	return command;
}

/**
 * Gets the menupopup element which will contain the list's commands.
 * @private
 * @type XULElement
 */
MozileCommandList.prototype._getMenupopup = function() {
	if(!this._menupopup) this._createMenupopup();
	return this._menupopup;
}

/**
 * Updates all commands on the list.
 * @type Void
 */
MozileCommandList.prototype.update = function() {
	for(var command in this.getCommands()) {
		if(typeof(command)=="Object") command.update();
	}
}

/**
 * Makes sure that if the number of buttons has changed the box is updated.
 * @type Boolean
 */
MozileCommandList.prototype.updateBox = function() {
	if(this._buttonArray.length != this.getBox().childNodes.length) {
		this._createBox();
		this.update();
		return true;
	}
	return false;
}

/**
 * Lists cannot be executed - overrides parent with an empty function.
 * @param {Event} event The event object which triggered the command.
 * @type Void
 */
MozileCommandList.prototype.execute = function(event) { }


/** 
 * Creates a new command, adds it to the command list and mozile command list, and adds it to the buttonArray and menuArray as needed.
 * @param {String} configString A properly formatted configuration string.
 * @type Command
 * @return The new commmand.
 */
MozileCommandList.prototype.createCommand = function(configString) {
	var firstWord = /\s*(\w*)/;
	var name = firstWord.exec(configString)[1];
	if(!name) throw Error("Invalid configuration string.");

	var command = eval("new "+ name +"(configString)");
	try {
		command.getId();
	}
	catch(e) {
		return undefined;
	}
	
		// Register the command locally and globally, and the with global accelerator list.
	this.addCommand(command);
	mozile.addCommand(command);
	if(command.getOption("accelerator")) {
		var accel = command.getOption("accelerator");
		if(mozile.getOperatingSystem()=="Mac") {
			accel = accel.replace("Command", "Meta");
		}
		else {
			accel = accel.replace("Command", "Control");
		}
		mozile.addAccelerator(accel, command);
	}

		// Position the button, either at the end or at the given index.
	var position = command.getOption("buttonPosition");
	if(position == undefined) this._buttonArray.push(command);
	else {
		if(this._buttonArray[position] == null) this._buttonArray[position] = command;
		else this._buttonArray.splice(position, 0, command);
	}

		// Position the menuitem, either at the end or at the given index.
	position = command.getOption("menuPosition");
	if(position == undefined) this._menuArray.push(command);
	else {
		if(this._menuArray[position] == null) this._menuArray[position] = command;
		else this._menuArray.splice(position, 0, command);
	}
	
	return command;
}


/** 
 * Creates an XUL button element for this command.
 * @private
 * @type XULElement
 */
MozileCommandList.prototype._createButton = function() {
	this._button = document.createElementNS(XULNS, "toolbarbutton");
	this._button.setAttribute("id", this.getId() +"-Button");
	this._button.setAttribute("class", "mozileButton");
	this._button.setAttribute("image", this.getOption("image"));
	this._button.setAttribute("label", this.getOption("label"));
	if(this.getOption("tooltip")) this._button.setAttribute("tooltiptext", this.getOption("tooltip"));
	this._button.setAttribute("type", "menu");
	
		// Hack to fix problem with toolbarbutton menupopups in Mozilla 1.7.11 and 1.7.12. The boxObject.screenY is wrong by event.pageY pixels, so we fix it. Already fixed in 1.8+.
	if(mozile.getBrowserName()=="Mozilla" && mozile.getBrowserVersion().indexOf("1.7")>=0) {
		this._button.setAttribute("onpopupshown", "this.firstChild.autoPosition = false; this.firstChild.moveTo(this.boxObject.screenX, this.boxObject.screenY+this.boxObject.height-event.pageY)");
	}
	
	this._button.appendChild(this._getMenupopup());
	return this._button;
}


/** 
 * Lists have menus, not menuitems. Gets the menu instead.
 * @private
 * @type Void
 */
MozileCommandList.prototype._createMenuitem = function() {	return this.getMenu(); }

/** 
 * Creates an XHTML div element to hold the commands on this list.
 * @private
 * @type HTMLDivElement
 */
MozileCommandList.prototype._createBox = function() {
	this._box = document.createElementNS(XHTMLNS, "div");
	this._box.setAttribute("id", this.getId() +"-Box");
	this._box.setAttribute("class", "mozileBox");
	this._box.setAttribute("label", this.getOption("label"));
	if(this.getOption("tooltip")) this._box.setAttribute("tooltiptext", this.getOption("tooltip"));
	
		// Add all the buttons from the buttonArray
	for(var i=0; i < this._buttonArray.length; i++) {
		if(this._buttonArray[i]) {
			this._box.appendChild(this._buttonArray[i].getButton());
		}
	}
	
	return this._box;
}

/** 
 * Creates an XUL menupopup element to hold the commands on this list.
 * @private
 * @type XULElement
 */
MozileCommandList.prototype._createMenu = function() {
	this._menu = document.createElementNS(XULNS, "menu");
	this._menu.setAttribute("id", this.getId() +"-Menu");
	this._menu.setAttribute("class", "mozileMenu");
	this._menu.setAttribute("label", this.getOption("label"));
	if(this.getOption("tooltip")) this._menu.setAttribute("tooltiptext", this.getOption("tooltip"));
	if(this.getOption("accesskey")) this._menu.setAttribute("accesskey", this.getOption("accesskey"));
	
	this._menu.appendChild(this._getMenupopup());
	return this._menu;
}


/** 
 * Creates a menupopup element with child menuitems for each command on the _menuArray.
 * @private
 * @type XULElement
 */
MozileCommandList.prototype._createMenupopup = function() {
	/**
	 * @private
	 * @type XULElement
	 */
	this._menupopup = document.createElementNS(XULNS, "menupopup");
	for(var i=0; i < this._menuArray.length; i++) {
		if(this._menuArray[i]) {
			this._menupopup.appendChild(this._menuArray[i].getMenuitem());
		}
	}
	return this._menupopup;
}




/** Mozile Module -
 * Subclass of MozileComponent designed for manipulating interfaces (e.g. popups, toolbar, etc.). Configuration string format is "InterfaceName: optionKey1=optionValue1, optionKey2=optionValue2".
 * @constructor
 * @param {Element} element The root element for the interface.
 * @param {String} configString A properly formatted configuration string.
 */
function MozileInterface(element, configString) {

	if(!element) throw Error("Invalid element.");
	
	/**
	 * @private
	 * @type String
	 */
	this._element = element;

	/**
	 * @private
	 * @type String
	 */
	this._configString = String(configString);

}

MozileInterface.prototype = new MozileComponent;
MozileInterface.prototype.constructor = MozileInterface;


/**
 * Gets the root element for the interface. If necessary, loads an external XML file and returns the documentElement.
 * <p>If the interface was created with an element, that element will be returned. If the interface was created with a URL, then we try to load the URL and get the documentElement. If this script was injected by the Mozile Extension, then we require a bit of a hack: The Extension will have also injected the interface, so instead of fetching it we look for its id instead.
 * @type Element
 */
MozileInterface.prototype.getElement = function() {
	if(!this._element || typeof(this._element) == typeof("")) {
		if(mozileScriptSource.match("^chrome://")) {
			try {
				this._element = document.getElementById(this.getName());
				this._element.collapsed = false;
				this._element.parentNode.removeChild(this._element);
			}
			catch(e) {
				throw Error("Failed to find preloaded interface with id '"+ this.getName() +"'\n"+ e);
			}
		}
		else {
			try {
				this._url = this._element;
				var xmlDoc = document.implementation.createDocument("", "", null);
				xmlDoc.async = false;
				var loaded = xmlDoc.load(this._url);
				if(!loaded) throw Error("Could not load interface.");
				this._element = document.importNode(xmlDoc.documentElement, true);
			}
			catch(e) {
				throw Error("Failed to load external interface at "+ this._element +"\n"+ e);
			}
		}
	}
	return this._element;
}

/**
 * Returns the parent of the interface's root element, finding it if necessary. In an HTML document the parent is the "body" tag. Otherwise it's the documentElement.
 * @type Element
 */
MozileInterface.prototype.getParent = function() {
	if(!this._parent) {	
		if(document.documentElement.tagName.toLowerCase()=="html") this._parent = document.getElementsByTagName("body")[0];
		else this._parent = document.documentElement;
	}
	return this._parent;
}

var mozileCurrentInterface;

/**
 * Reveal the interface.
 * @type Void
 */
MozileInterface.prototype.show = function() {
		// Don't show again if already showing.
	if(this.getElement().parentNode && this.getElement().parentNode == this.getParent()) return;
	
	try {
		if(this._nextSibling) {
			this.getParent().insertBefore(this.getElement(), this._nextSibling);
		}
		else {
			this.getParent().appendChild(this.getElement());
		}
	}
	catch(e) { }
	
	this.getElement().position = "fixed";
	
	if(mozileCurrentInterface) mozileCurrentInterface.hide();
	mozileCurrentInterface = this;
	
	if(!this._interval) this._interval = window.setInterval("mozileCurrentInterface.center()", mozile.getOption("defaultInterval"));
	
	this.init();
}

/**
 * Hide the interface.
 * @type Void
 */
MozileInterface.prototype.hide = function() {
		// Hide doesn't work if the element isn't showing.
	if(!this.getElement().parentNode) return;
	
	this._nextSibling = this.getElement().nextSibling;
	this.getParent().removeChild(this.getElement());
	
	mozileCurrentInterface = undefined;
	if(this._interval) {
		window.clearInterval(this._interval);
		this._interval = undefined;
	}
}



/**
 * Center the interface in the window.
 * @type Void
 */
MozileInterface.prototype.center = function() {
		// Apply positioning options.
	var options = ["width", "height", "top", "left", "right", "bottom"];
	for(var i=0; i < options.length; i++) {
		if(this.getOption(options[i])) {	
			this.getElement().style[options[i]] = this.getOption(options[i]);
		}
	}
	
		// Try to center the box.
	try {
		if(this.getElement().boxObject) {
			var left = (window.innerWidth - this.getElement().boxObject.width) / window.innerWidth * 50 ;
			if(left > 0) this.getElement().style.left = left + "%";
			var top = (window.innerHeight - this.getElement().boxObject.height) / window.innerHeight * 50;
			if(top > 0) this.getElement().style.top = top + "%";
		}
	} catch(e) { }
}

/**
 * Initialize the interface.
 * @type Void
 */
MozileInterface.prototype.init = function() { }




Documentation generated by JSDoc on Thu Feb 16 20:20:37 2006