edit.js
Summary
Tools for basic text editing operations.
Version: 0.8
$Id: overview-summary-edit.js.html,v 1.12 2008/02/20 18:47:09 jameso Exp $
Author: James A. Overton
mozile.require("mozile.dom");
mozile.require("mozile.xml");
mozile.provide("mozile.edit.*");
mozile.provide("mozile.execCommand");
mozile.edit = new Object();
mozile.edit.prototype = new mozile.Module;
mozile.edit.strings = mozile.getLocalization("mozile.edit", "commands");
mozile.edit.editable = true;
mozile.edit.status = false;
mozile.edit.NEXT = 1;
mozile.edit.PREVIOUS = -1;
mozile.edit.marked = new Array();
mozile.edit.allCommands = new Object();
mozile.edit.allCommands.toString = function() {
var keys = new Array();
for(var key in this) {
switch(key) {
case "toString":
case "undefined":
break;
default: keys.push(key);
}
}
keys.sort();
return keys.join(", ");
}
mozile.edit.keyCodes = {
8: "Backspace",
9: "Tab",
12: "Clear",
13: "Return",
14: "Enter",
19: "Pause",
27: "Escape",
32: "Space",
33: "Page-Up",
34: "Page-Down",
35: "End",
36: "Home",
37: "Left",
38: "Up",
39: "Right",
40: "Down",
45: "Insert",
46: "Delete",
112: "F1",
113: "F2",
114: "F3",
115: "F4",
116: "F5",
117: "F6",
118: "F7",
119: "F8",
121: "F9",
122: "F10",
123: "F11",
123: "F12"
}
mozile.edit.getContainer = function(element) {
if(!element || !element.nodeType) return null;
if(element.nodeType != mozile.dom.ELEMENT_NODE) element = element.parentNode;
if(!element || !element.nodeType) return null;
var doc = element.ownerDocument;
while(element && element.nodeType &&
element.nodeType == mozile.dom.ELEMENT_NODE) {
if(mozile.edit.isEditableElement(element)) return element;
switch(element.getAttribute("contentEditable")) {
case "true":
mozile.editElement(element);
return element;
case "false":
return null;
}
element = element.parentNode;
}
return null;
}
mozile.edit.isEditable = function(node) {
if(!node) return false;
var container = mozile.edit.getContainer(node)
if(container && container != node) return true;
else return false;
}
mozile.edit.isEditableElement = function(element) {
if(element && mozile.edit.getMark(element, "editable")) return true;
return false;
}
mozile.edit.setStatus = function(status) {
status = Boolean(status);
if(mozile.edit.status != status) {
mozile.edit.status = status;
if(mozile.useDesignMode == true &&
typeof(mozile.document.documentElement.contentEditable) == "undefined") {
mozile.document.designMode = (status) ? "on" : "off";
}
}
return mozile.edit.status;
}
mozile.edit.start = function() {
mozile.edit.editable = true;
mozile.edit.setStatus(true);
if(mozile.gui) mozile.gui.show();
var list = mozile.edit.getMarked("editingDisabled", true);
for(var i=0; i < list.length; i++) {
mozile.edit.setMark(list[i], "editingDisabled", undefined);
list[i].setAttribute("contentEditable", "true");
}
return mozile.edit.editable;
}
mozile.edit.stop = function() {
mozile.edit.editable = false;
mozile.edit.setStatus(false);
if(mozile.gui) mozile.gui.hide();
var list = mozile.dom.getElements("contentEditable", "true");
for(var i=0; i < list.length; i++) {
mozile.edit.setMark(list[i], "editingDisabled", true);
list[i].setAttribute("contentEditable", "false");
}
return mozile.edit.editable;
}
mozile.edit.setMark = function(element, key, value) {
if(!element || element.nodeType == undefined) return null;
if(element.nodeType != mozile.dom.ELEMENT_NODE) return null;
if(!key || typeof(key) != "string") return null;
try {
if(element.mozile == undefined || typeof(element.mozile) != "object") {
element.mozile = new Object();
mozile.edit.marked.push(element);
}
element.mozile[key] = value;
return value;
} catch(e) {
return null;
}
}
mozile.edit.getMark = function(element, key) {
if(!element || element.nodeType == undefined) return undefined;
if(element.nodeType != mozile.dom.ELEMENT_NODE) return undefined;
if(!key || typeof(key) != "string") return undefined;
if(element.mozile == undefined || !element.mozile) return undefined;
if(element.mozile[key] == undefined) return undefined;
return element.mozile[key];
}
mozile.edit.getMarked = function(key, value) {
var list = new Array();
if(!key || typeof(key) != "string") return list;
var element;
for(var i=0; i < mozile.edit.marked.length; i++) {
element = mozile.edit.marked[i];
if(!element.mozile || element.mozile[key] == undefined) continue;
if(value === undefined) list.push(element);
else if(element.mozile[key] == value) list.push(element);
}
return list;
}
mozile.edit.lookupRNG = function(node) {
if(!node) return null;
var element = node;
if(node.nodeType != mozile.dom.ELEMENT_NODE) element = node.parentNode;
if(!mozile.schema) return null;
var name = mozile.dom.getLocalName(element);
if(name && mozile.dom.isHTML(node)) name = name.toLowerCase();
var matches = mozile.schema.getNodes("element", name);
if(matches.length > 0) return matches[0];
else return null;
}
mozile.edit.parseMES = function(container, node, localization) {
if(node.nodeType != mozile.dom.ELEMENT_NODE) return;
var command, define;
for(var i=0; i < node.childNodes.length; i++) {
var child = node.childNodes[i];
switch(mozile.dom.getNamespaceURI(child)) {
case mozile.xml.ns.mes:
switch(mozile.dom.getLocalName(child)) {
case "ref":
define = mozile.edit.followMESRef(child);
if(define) mozile.edit.parseMES(container, define, localization);
break;
case "command":
command = mozile.edit.generateCommand(child, localization);
if(command) container.addCommand(command);
break;
case "group":
command = mozile.edit.generateCommand(child, localization);
if(command) {
container.addCommand(command);
if(command._commands.length == 0)
mozile.edit.parseMES(command, child, localization);
}
break;
}
break;
case mozile.xml.ns.rng:
if(child.nodeName == "ref") {
var name = child.getAttribute("name");
if(!name) continue;
define = mozile.schema._root.getDefinition(name);
mozile.edit.parseMES(container, define._element, localization);
}
break;
}
}
}
mozile.edit.followMESRef = function(element) {
var define = mozile.edit.getMark(element, "define");
if(define && define.nodeType && define.nodeType == mozile.dom.ELEMENT_NODE)
return define;
var name = element.getAttribute("name");
if(!name) return null;
var node = element;
while(node) {
if(mozile.dom.getNamespaceURI(node) == mozile.xml.ns.rng &&
mozile.dom.getLocalName(node) == "grammar") break;
else node = node.parentNode;
}
if(!node) return null;
define = null;
var child;
for(var i=0; i < node.childNodes.length; i++) {
child = node.childNodes[i];
if(mozile.dom.getNamespaceURI(child) == mozile.xml.ns.mes &&
mozile.dom.getLocalName(child) == "define" &&
child.getAttribute("name") == name) {
define = child;
break;
}
}
if(define) {
mozile.edit.setMark(element, "define", define);
return define;
}
else return null;
}
mozile.edit.generateCommands = function(schema, localization) {
var elements = schema.getNodes("element");
var name, uniqueName;
var j=0;
for(var i=0; i < elements.length; i++) {
if(elements[i].commands == undefined) {
name = elements[i].getName() + "_commands";
uniqueName = name;
if(mozile.edit.getCommand(uniqueName)) {
j=0;
while(true) {
uniqueName = name +"_"+ j;
if(!mozile.edit.getCommand(uniqueName)) break;
j++;
}
}
elements[i].commands = new mozile.edit.CommandGroup(uniqueName);
}
elements[i].commands.addCommand(mozile.edit.navigateLeftRight);
if(elements[i].mayContain("text")) {
elements[i].commands.addCommand(mozile.edit.insertText);
elements[i].commands.addCommand(mozile.edit.removeText);
}
if(mozile.edit.remove) elements[i].commands.addCommand(mozile.edit.remove);
mozile.edit.parseMES(elements[i].commands, elements[i]._element, localization);
}
}
mozile.edit.generateCommand = function(node, localization) {
var name = node.getAttribute("name");
if(!name) return null;
if(mozile.edit.allCommands[name]) return mozile.edit.allCommands[name];
var command;
if(mozile.dom.getLocalName(node) == "command") {
var className = node.getAttribute("class");
if(className && mozile.edit[className]) {
eval("command = new mozile.edit."+ className +"(name)");
}
else command = new mozile.edit.Command(name);
var child = node.firstChild;
while(child) {
if(child.nodeType == mozile.dom.ELEMENT_NODE) {
switch(mozile.dom.getLocalName(child)) {
case "element":
var element = mozile.dom.getFirstChildElement(child);
if(child.getAttribute("import") == "true")
element = mozile.dom.importNode(element, true);
if(element) command.element = element;
break;
case "script":
command.script = child;
break;
}
}
child = child.nextSibling;
}
}
else if(mozile.dom.getLocalName(node) == "group") {
command = new mozile.edit.CommandGroup(name);
}
else return null;
command.node = node;
var properties = ["priority", "label", "image", "tooltip", "accel", "makesChanges", "watchesChanges", "element", "text", "remove", "nested", "direction", "target", "collapse", "copyAttributes", "className", "styleName", "styleValue"];
for(var i=0; i < properties.length; i++) {
var property = properties[i];
if(node.getAttribute(property)) {
var value = node.getAttribute(property);
if(value.toLowerCase() == "true") value = true;
else if(value.toLowerCase() == "false") value = false;
command[property] = value;
}
}
command.localize(localization);
if(command.accel) {
command.accels = mozile.edit.splitAccelerators(command.accel);
}
if(command.target && !command.direction) {
command.direction = null;
}
if(command.script) {
child = command.script.firstChild;
while(child) {
if(child.nodeType == mozile.dom.TEXT_NODE ||
child.nodeType == mozile.dom.CDATA_SECTION_NODE) {
command.evaluate(child.data);
}
child = child.nextSibling;
}
}
return command;
}
mozile.edit.checkAccelerators = function(event, accelerators) {
if(!event) return false;
if(typeof(accelerators) != "object" || !accelerators.length) return false;
for(var i=0; i < accelerators.length; i++) {
if(mozile.edit.checkAccelerator(event, accelerators[i])) return true;
}
return false;
}
mozile.edit.checkAccelerator = function(event, accelerator) {
if(!event) return false;
if(typeof(accelerator) != "string") return false;
if(mozile.browser.isIE) {
if(event.type != "keydown") {
if(event.type != "keypress") return false;
if(event.keyCode && !mozile.edit.keyCodes[event.keyCode]) return false;
}
}
else if(event.type != "keypress") return false;
if(event.accel == undefined) event.accel = mozile.edit.generateAccelerator(event);
if(event.accel.toLowerCase() == accelerator.toLowerCase()) return true;
else return false;
}
mozile.edit.generateAccelerator = function(event) {
if(!event) return "";
var accel = "";
if(event.metaKey) accel = accel + "Meta-";
if(event.ctrlKey) accel = accel + "Control-";
if(event.altKey) accel = accel + "Alt-";
if(event.shiftKey) accel = accel + "Shift-";
if(event.keyCode && mozile.edit.convertKeyCode(event.keyCode)) {
accel = accel + mozile.edit.convertKeyCode(event.keyCode);
}
else if(event.charCode == 32) accel = accel + "Space";
else accel = accel + String.fromCharCode(event.charCode).toUpperCase();
var command = "Control";
if(mozile.os.isMac) command = "Meta";
accel = accel.replace(command, "Command");
return accel;
}
mozile.edit.splitAccelerators = function(accelerators) {
var accels = new Array();
var split = accelerators.split(/\s/);
var accel;
for(var i=0; i < split.length; i++) {
accel = split[i];
accel = accel.replace(/\s+/g, "");
if(accel) accels.push(accel);
}
return accels;
}
mozile.edit.parseAccelerator = function(accelerator) {
accelerator = accelerator.replace(/\s.*/, "");
var accel = {
command: false,
meta: false,
ctrl: false,
alt: false,
shift: false,
charCode: 0,
character: "",
abbr: ""
}
if(accelerator.indexOf("Command") > -1) accel.command = true;
if(accelerator.indexOf("Meta") > -1) accel.meta = true;
if(accelerator.indexOf("Control") > -1) accel.ctrl = true;
if(accelerator.indexOf("Alt") > -1) accel.alt = true;
if(accelerator.indexOf("Shift") > -1) accel.shift = true;
accel.character = accelerator.substring(accelerator.lastIndexOf("-")+1);
if(mozile.os.isMac) {
if(accel.ctrl) accel.abbr += "\u2303";
if(accel.alt) accel.abbr += "\u2325";
if(accel.shift) accel.abbr += "\u21E7";
if(accel.command) accel.abbr += "\u2318";
accel.abbr += accel.character;
}
else {
if(accel.command) accel.abbr += "Ctrl+";
if(accel.alt) accel.abbr += "Alt+";
if(accel.shift) accel.abbr += "Shift+";
accel.abbr += accel.character;
}
return accel;
}
mozile.edit.convertKeyCode = function(keyCode) {
if(mozile.edit.keyCodes[keyCode]) return mozile.edit.keyCodes[keyCode];
else return null;
}
mozile.edit.getCommand = function(name) {
if(mozile.edit.allCommands[name]) return mozile.edit.allCommands[name];
else return null;
}
mozile.edit._isHigherThan = {
none: {none: 0, selection: 0, state: 0, text: 0, node: 0},
selection: {none: 1, selection: 0, state: 0, text: 0, node: 0},
state: {none: 1, selection: 1, state: 0, text: 0, node: 0},
text: {none: 1, selection: 1, state: 1, text: 0, node: 0},
node: {none: 1, selection: 1, state: 1, text: 1, node: 0}
}
mozile.edit.isHigherPriority = function(higher, lower) {
if(this._isHigherThan[higher] == undefined) return false;
if(this._isHigherThan[higher][lower] == undefined) return true;
return Boolean(this._isHigherThan[higher][lower]);
}
mozile.execCommand = function(name, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10) {
if(!name) return null;
var command = mozile.edit.getCommand(name);
if(!command) return null;
var state = command.request(null, true, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10);
if(!state) return state;
mozile.edit.done(state);
if(state.changesMade) {
var event = new Object();
mozile.edit._getNode(event);
if(mozile.gui) mozile.gui.update(event, state.changesMade);
}
return state;
}
mozile.edit.extendRNG = function() {
mozile.rng.Element.prototype.create = function(parent) {
var node = mozile.dom.createElement(this.getName());
if(parent) {
parent.appendChild(node);
return parent;
}
else return node;
}
}
if(mozile.rng) mozile.edit.extendRNG();
mozile.edit.State = function(command, selection) {
this.command = command;
this.selection = null;
if(selection !== false) {
if(!selection) selection = mozile.dom.selection.get();
this.selection = { before: selection.store() };
}
this.reversible = true;
this.cancel = true;
this.changesMade = command.makesChanges;
this.delayExecution = false;
this.executed = false;
}
mozile.edit.State.prototype.toString = function() {
return "[object mozile.edit.State]";
}
mozile.edit.State.prototype.storeNode = function(input) {
if(!input) return null;
if(typeof(input) == "string") {
if(input.indexOf("/") != 0) return null;
return input;
}
else {
var xpath = mozile.xpath.getXPath(input);
if(xpath) return xpath;
else return null;
}
return null;
}
mozile.edit.Command = function(name, localization) {
this.name = name;
this.group = false;
this.makesChanges = "node";
this.watchesChanges = "node";
this.strings = {};
this.localize(localization);
mozile.edit.allCommands[this.name] = this;
}
mozile.edit.Command.prototype.localize = function(localization) {
var properties = ["accel", "accels", "label", "tooltip", "image"];
if(!localization) localization = mozile.edit.strings;
if(!localization) return false;
if(localization[this.name]) {
this.strings = localization[this.name];
for(var i=0; i < properties.length; i++) {
if(localization[this.name][properties[i]])
this[properties[i]] = localization[this.name][properties[i]]
}
}
return true;
}
mozile.edit.Command.prototype.toString = function() {
return "[object mozile.edit.Command '"+ this.name +"']";
}
mozile.edit.Command.prototype.evaluate = function(code) {
eval(code);
}
mozile.edit.Command.prototype.respond = function(change) {
if(this.watchesChanges == change) return true;
return mozile.edit.isHigherPriority(change, this.watchesChanges);
}
mozile.edit.Command.prototype.isAvailable = function(event) {
return true;
}
mozile.edit.Command.prototype.isActive = function(event) {
return false;
}
mozile.edit.Command.prototype.test = function(event) {
if(event) {
if(this.accels) return mozile.edit.checkAccelerators(event, this.accels);
else if(this.accel) {
this.accels = mozile.edit.splitAccelerators(this.accel);
return mozile.edit.checkAccelerator(event, this.accel);
}
else return false;
}
return true;
}
mozile.edit.Command.prototype.prepare = function(event) {
var state = new mozile.edit.State(this);
if(this.prompt) {
if(!this.prompt(event, state)) return null;
}
return state;
}
mozile.edit.Command.prototype.trigger = function(event) {
if(window.trigger) window.trigger.push(this.name);
if(this.test(event)) {
var state = this.prepare(event);
if(state.delayExecution) return "delayed";
else return this.execute(state, true);
}
return null;
}
mozile.edit.Command.prototype.request = function(state, fresh, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10) {
var test = this.test(null, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10);
if(!test) return null;
var newState = this.prepare(null, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10);
if(!newState) return null;
if(newState.delayExecution) return "delayed";
newState = this.execute(newState, fresh);
if(!newState || !newState.executed) return null;
if(state && typeof(state) == "object") {
if(!state.actions) state.actions = new Array();
state.actions.push(newState);
}
return newState;
}
mozile.edit.Command.prototype.execute = function(state, fresh) {
mozile.debug.inform("mozile.edit.Command.execute", "Command '"+ this.name +"' executed with state "+ state);
state.executed = true;
return state;
}
mozile.edit.Command.prototype.unexecute = function(state, fresh) {
var selection = mozile.dom.selection.get();
if(!fresh && state.selection && state.selection.after)
selection.restore(state.selection.after);
if(state.actions) {
for(var i = state.actions.length - 1; i >= 0; i--) {
state.actions[i] = state.actions[i].command.unexecute(state.actions[i], fresh);
if(state.actions[i].executed) mozile.debug.inform(this.name +".unexecute", "Child command "+ i +" failed to unexecute.");
}
}
if(state.selection && state.selection.before)
selection.restore(state.selection.before);
state.executed = false;
return state;
}
mozile.edit.CommandGroup = function(name, localization) {
this.name = name;
this.group = true;
this.makesChanges = "none";
this.watchesChanges = "none";
this._commands = new Array();
this._priority = new Array();
this.localize(localization);
mozile.edit.allCommands[this.name] = this;
}
mozile.edit.CommandGroup.prototype = new mozile.edit.Command;
mozile.edit.CommandGroup.prototype.constructor = mozile.edit.CommandGroup;
mozile.edit.CommandGroup.prototype.toString = function() {
return "[object mozile.edit.CommandGroup '"+ this.name +"']";
}
mozile.edit.CommandGroup.prototype.addCommand = function(command, previousCommand) {
if(!command) return null;
if(!this._commands) this._commands = new Array();
if(!this._priority) this._priority = new Array();
for(var i=0; i < this._commands.length; i++) {
if(this._commands[i] == command) return null;
}
var added = false;
if(previousCommand) {
for(i=0; i < this._commands.length - 1; i++) {
if(this._commands[i] == previousCommand) {
this._commands.splice(i+1, 0, command);
added = true;
break;
}
}
if(!added) {
this._commands.unshift(command);
added = true;
}
}
if(!added) this._commands.push(command);
this._priority.push(command);
this._priority.sort(this.compareCommands);
return command;
}
mozile.edit.CommandGroup.prototype.removeCommands = function() {
this._commands = new Array();
this._priority = new Array();
}
mozile.edit.CommandGroup.prototype.compareCommands = function(command1, command2) {
if(command1.priority == undefined || Number(command1.priority) == NaN)
command1.priority = 0;
if(command2.priority == undefined || Number(command2.priority) == NaN)
command2.priority = 0;
return command2.priority - command1.priority;
}
mozile.edit.CommandGroup.prototype.trigger = function(event) {
if(!this._priority) return null;
var state;
for(var i=0; i < this._priority.length; i++) {
state = this._priority[i].trigger(event);
if(state) return state;
}
return null;
}
mozile.edit.commands = new mozile.edit.CommandGroup("commands");
mozile.edit.defaults = new mozile.edit.CommandGroup("defaults");
mozile.document._undoStack = new Array();
mozile.document._undoIndex = -1;
mozile.edit.currentState = null;
mozile.edit.dumpUndoStack = function() {
if(!mozile.document._undoStack) {
mozile.document._undoStack = new Array();
mozile.document._undoIndex = -1;
}
var entries = new Array("Undo Stack [ "+ mozile.document._undoIndex +" / "+ mozile.document._undoStack.length +" ]");
for(var i=0; i < mozile.document._undoStack.length; i++) {
var picked = " ";
if(i == mozile.document._undoIndex) picked = "> "
entries.push(picked + i +". "+ mozile.document._undoStack[i].command.name);
}
return entries.join("\n");
}
mozile.edit.done = function(state) {
if(!state || !state.reversible) return;
if(!mozile.document._undoStack) {
mozile.document._undoStack = new Array();
mozile.document._undoIndex = -1;
}
mozile.document._undoStack = mozile.document._undoStack.slice(0, mozile.document._undoIndex + 1);
mozile.document._undoStack.push(state);
mozile.document._undoIndex = mozile.document._undoStack.length - 1;
}
mozile.edit.getCurrentState = function(doc) {
if(!doc) doc = mozile.document;
if(!doc) return null;
if(!doc._undoStack) return null;
if(doc._undoIndex === undefined) return null;
return doc._undoStack[doc._undoIndex];
}
mozile.edit.save = new mozile.edit.Command("save");
mozile.edit.save.makesChanges = "none";
mozile.edit.save.watchesChanges = "state";
mozile.edit.commands.addCommand(mozile.edit.save);
mozile.edit.save.isAvailable = function(event) {
if(!mozile.save) return false;
if(mozile.save.isSaved()) return false;
else return true;
}
mozile.edit.save.execute = function(state, fresh) {
mozile.save.save();
state.reversible = false;
state.executed = true;
return state;
}
mozile.edit.source = new mozile.edit.Command("source");
mozile.edit.source.makesChanges = "none";
mozile.edit.source.watchesChanges = "none";
mozile.edit.commands.addCommand(mozile.edit.source);
mozile.edit.source.execute = function(state, fresh) {
if(mozile.save && mozile.gui){
var content = mozile.save.getContent(mozile.document);
content = mozile.save.cleanMarkup(content);
mozile.gui.display("<h3>Page Source</h3>\n<pre>"+ content +"</pre>");
}
state.reversible = false;
state.executed = true;
return state;
}
mozile.edit.debug = new mozile.edit.Command("debug");
mozile.edit.debug.makesChanges = "none";
mozile.edit.debug.watchesChanges = "none";
mozile.edit.commands.addCommand(mozile.edit.debug);
mozile.edit.debug.execute = function(state, fresh) {
mozile.debug.show();
state.reversible = false;
state.executed = true;
return state;
}
mozile.edit.undo = new mozile.edit.Command("undo");
mozile.edit.undo.makesChanges = "node";
mozile.edit.undo.watchesChanges = "state";
mozile.edit.commands.addCommand(mozile.edit.undo);
mozile.edit.undo.test = function(event) {
if(mozile.document._undoIndex == undefined ||
mozile.document._undoIndex < 0) return false;
if(event) {
return mozile.edit.checkAccelerator(event, this.accel);
}
return true;
}
mozile.edit.undo.isAvailable = function(event) {
if(mozile.document._undoIndex == undefined ||
mozile.document._undoIndex < 0) return false;
else return true;
}
mozile.edit.undo.prepare = function(event, repeated) {
var state = new mozile.edit.State(this, false);
state.repeated = false;
if(repeated) state.repeated = repeated;
if(event) state.repeated = event.repeat;
state.reversible = false;
return state;
}
mozile.edit.undo.execute = function(state, fresh) {
var undoState = mozile.document._undoStack[mozile.document._undoIndex];
if(undoState) {
undoState.command.unexecute(undoState, false);
mozile.document._undoIndex--;
state.changesMade = undoState.changesMade;
}
state.executed = true;
return state;
}
mozile.edit.redo = new mozile.edit.Command("redo");
mozile.edit.redo.makesChanges = "node";
mozile.edit.redo.watchesChanges = "state";
mozile.edit.commands.addCommand(mozile.edit.redo);
mozile.edit.redo.test = function(event) {
if(mozile.document._undoIndex == undefined ||
mozile.document._undoIndex + 1 >= mozile.document._undoStack.length) return false;
if(event) {
return mozile.edit.checkAccelerator(event, this.accel);
}
return true;
}
mozile.edit.redo.isAvailable = function(event) {
if(mozile.document._undoIndex == undefined ||
mozile.document._undoIndex + 1 >= mozile.document._undoStack.length) return false;
else return true;
}
mozile.edit.redo.prepare = function(event, repeated) {
var state = new mozile.edit.State(this, false);
state.repeated = false;
if(repeated) state.repeated = repeated;
if(event) state.repeated = event.repeat;
state.reversible = false;
return state;
}
mozile.edit.redo.execute = function(state, fresh) {
var redoState = mozile.document._undoStack[mozile.document._undoIndex + 1];
if(redoState) {
mozile.document._undoIndex++;
redoState.command.execute(redoState, false);
state.changesMade = redoState.changesMade;
}
state.executed = true;
return state;
}
mozile.edit.executionGroup = new mozile.edit.Command("ExecutionGroup");
mozile.edit.executionGroup.test = function(event) {
if(event) return false;
return true;
}
mozile.edit.executionGroup.execute = function(state, fresh) {
var selection = mozile.dom.selection.get();
if(!fresh) selection.restore(state.selection.before);
if(state.actions) {
for(var i=0; i < state.actions.length; i++) {
state.actions[i] = state.actions[i].command.execute(state.actions[i], fresh);
if(!state.actions[i].executed) mozile.debug.inform(this.name +".unexecute", "Child command "+ i +" failed to execute.");
}
}
state.selection.after = selection.store();
state.executed = true;
return state;
}
mozile.edit.clipboard = null;
mozile.edit.updateClipboard = function() {
}
mozile.edit.copy = new mozile.edit.Command("copy");
mozile.edit.copy.makesChanges = "node";
mozile.edit.copy.watchesChanges = "selection";
mozile.edit.commands.addCommand(mozile.edit.copy);
mozile.edit.copy.isAvailable = function(event) {
var selection;
if(event && event.selection) selection = event.selection;
if(!selection) selection = mozile.dom.selection.get();
if(selection.isCollapsed) return false;
return true;
}
mozile.edit.copy.test = function(event) {
if(event) {
if(this.accels) return mozile.edit.checkAccelerators(event, this.accels);
else if(this.accel) {
this.accels = mozile.edit.splitAccelerators(this.accel);
return mozile.edit.checkAccelerator(event, this.accel);
}
else return false;
}
var selection;
if(event && event.selection) selection = event.selection;
if(!selection) selection = mozile.dom.selection.get();
if(selection.isCollapsed) return false;
return true;
}
mozile.edit.copy.prepare = function(event) {
var state = new mozile.edit.State(this, false);
state.reversible = false;
state.cancel = false;
return state;
}
mozile.edit.copy.execute = function(state, fresh) {
var selection = mozile.dom.selection.get();
var range = selection.getRangeAt(0);
if(range.commonAncestorContainer.nodeType == mozile.dom.TEXT_NODE ||
!mozile.edit.rich) {
mozile.edit.clipboard = range.toString();
}
else mozile.edit.clipboard = range.cloneContents();
state.executed = true;
return state;
}
mozile.edit.cut = new mozile.edit.Command("cut");
mozile.edit.cut.makesChanges = "node";
mozile.edit.cut.watchesChanges = "selection";
mozile.edit.commands.addCommand(mozile.edit.cut);
mozile.edit.cut.isAvailable = function(event) {
var selection;
if(event && event.selection) selection = event.selection;
if(!selection) selection = mozile.dom.selection.get();
if(selection.isCollapsed) return false;
return true;
}
mozile.edit.cut.test = function(event) {
if(event) {
if(!event.editable) return false;
if(!mozile.edit.checkAccelerator(event, this.accel)) return false;
if(!mozile.edit.rich) {
if(event.node) return false;
if(event.node.nodeType != mozile.dom.TEXT_NODE) return false;
}
}
var selection;
if(event && event.selection) selection = event.selection;
if(!selection) selection = mozile.dom.selection.get();
if(selection.isCollapsed) return false;
return true;
}
mozile.edit.cut.execute = function(state, fresh) {
var selection = mozile.dom.selection.get();
if(!fresh) selection.restore(state.selection.before);
var range = selection.getRangeAt(0);
state.actions = new Array();
if(range.commonAncestorContainer.nodeType == mozile.dom.TEXT_NODE ||
!mozile.edit.rich) {
mozile.edit.clipboard = range.toString();
mozile.edit.removeText.request(state, fresh);
}
else {
mozile.edit.clipboard = range.cloneContents();
mozile.edit.remove.request(state, fresh);
}
state.selection.after = selection.store();
state.executed = true;
return state;
}
mozile.edit.paste = new mozile.edit.Command("paste");
mozile.edit.commands.addCommand(mozile.edit.paste);
mozile.edit.paste.isAvailable = function(event) {
if(!event || !event.editable) return false;
if(mozile.edit.clipboard) return true;
return false;
}
mozile.edit.paste.test = function(event) {
if(event && mozile.browser.isIE) return false;
if(!mozile.edit.clipboard) return false;
if(event) {
if(!event.editable) return false;
if(!mozile.edit.checkAccelerator(event, this.accel)) return false;
if(!mozile.edit.rich) {
if(typeof(mozile.edit.clipboard) != "string") return false;
if(!event.node) return false;
if(event.node.nodeType != mozile.dom.TEXT_NODE) return false;
}
}
return true;
}
mozile.edit.paste.prepare = function(event) {
var state = new mozile.edit.State(this);
if(typeof(mozile.edit.clipboard) == "string") {
state.content = mozile.edit.clipboard;
}
else state.content = mozile.edit.clipboard.cloneNode(true);
state.reversible = true;
return state;
}
mozile.edit.paste.execute = function(state, fresh) {
var selection = mozile.dom.selection.get();
if(!fresh) selection.restore(state.selection.before);
var range = selection.getRangeAt(0);
state.actions = new Array();
if(!selection.isCollapsed) {
if(mozile.edit.remove) mozile.edit.remove.request(state, fresh);
else mozile.edit.removeText.request(state, fresh);
}
if(typeof(state.content) == "string") {
mozile.edit.insertText.request(state, fresh, mozile.edit.NEXT, state.content);
}
else {
var previousNode = null;
var nextNode = null;
if(selection.focusNode.nodeType == mozile.dom.TEXT_NODE) {
var newState = mozile.edit.splitNode.request(state, fresh,
selection.focusNode, selection.focusOffset);
previousNode = newState.oldContainer;
nextNode = newState.newContainer;
}
else {
previousNode = selection.focusNode.childNodes[selection.focusOffset - 1];
nextNode = selection.focusNode.childNodes[selection.focusOffset];
}
var moveNode, firstNode, lastNode;
for(var i=state.content.childNodes.length-1; i >= 0; i--) {
moveNode = state.content.childNodes[i].cloneNode(true);
mozile.edit.insertNode.request(state, fresh, null, previousNode, moveNode);
if(i == state.content.childNodes.length-1) lastNode = moveNode;
if(i == 0) firstNode = moveNode;
}
var IP = mozile.edit.getInsertionPoint(firstNode, mozile.edit.NEXT);
if(IP) {
IP.select();
IP = mozile.edit.getInsertionPoint(lastNode, mozile.edit.PREVIOUS);
if(IP) IP.extend();
}
mozile.edit._normalize(state, fresh, lastNode, nextNode);
mozile.edit._normalize(state, fresh, previousNode, firstNode);
}
state.selection.after = selection.store();
state.executed = true;
return state;
}
mozile.edit.paste._preExecute = function() {
var container = mozile.dom.createElement("div");
container.setAttribute("contentEditable", true);
container.style.height = "0px";
container.appendChild(mozile.document.createTextNode(""));
mozile.dom.getBody().appendChild(container);
mozile.edit.paste._pasteContainer = container;
var selection = mozile.dom.selection.get();
mozile.edit.paste._storedSelection = selection.store();
selection.collapse(container.firstChild, 0);
mozile.window.setTimeout(mozile.edit.paste._postExecute, 50);
}
mozile.edit.paste._postExecute = function() {
var container = mozile.edit.paste._pasteContainer;
if(!container) return null;
container.parentNode.removeChild(container);
mozile.edit.clipboard = mozile.edit.paste._cleanHTML(container);
var selection = mozile.dom.selection.get();
selection.restore(mozile.edit.paste._storedSelection);
mozile.execCommand("paste");
delete mozile.edit.paste._pasteContainer;
delete mozile.edit.paste._storedSelection;
}
mozile.edit.paste._cleanHTML = function(element) {
for(var i=0; i < element.all.length; i++) {
element.all[i].removeAttribute("className", "", 0);
css = element.all[i].style.cssText;
css = css.replace(/mso-.*?(;|$)/mg, "");
element.all[i].style.cssText = css;
}
return element;
}
mozile.edit.test = new mozile.edit.Command("test");
mozile.edit.commands.addCommand(mozile.edit.test);
mozile.edit.test.execute = function(state, fresh) {
mozile.require("mozile.util");
var output = new Array();
output.push("Debugging Information:");
output.push("Undo: "+ mozile.document._undoIndex +" / "+ mozile.document._undoStack.length);
var selection = mozile.dom.selection.get();
output.push("Selection:\n"+ mozile.util.dumpValues(selection.store()));
var element = selection.focusNode;
if(element.nodeType != mozile.dom.ELEMENT_NODE) element = element.parentNode;
var rng = mozile.edit.lookupRNG(element);
if(rng) {
if(rng.getName()) output.push("RNG: "+ rng +" "+ rng.getName());
else output.push("RNG: "+ rng);
output.push("Text? "+ rng.mayContain("text"));
}
else output.push("No matching RNG object.");
alert(output.join("\n"));
state.reversible = false;
state.executed = true;
return state;
}
mozile.edit.tweak = new mozile.edit.Command("tweak");
mozile.edit.commands.addCommand(mozile.edit.tweak);
mozile.edit.tweak.execute = function(state, fresh) {
if(mozile.browser.isIE) {
var selection = mozile.dom.selection.get();
var range = selection.getRangeAt(0);
range._range.move("character", 1);
selection.removeAllRanges();
selection.addRange(range);
}
state.reversible = false;
state.executed = true;
return state;
}
mozile.require("mozile.edit.InsertionPoint");
mozile.edit.navigateLeftRight = new mozile.edit.Command("navigateLeftRight");
mozile.edit.navigateLeftRight.priority = 15;
mozile.edit.navigateLeftRight.accel = "Left Right";
mozile.edit.navigateLeftRight.accels =
mozile.edit.splitAccelerators(mozile.edit.navigateLeftRight.accel);
mozile.edit.navigateLeftRight.makesChanges = "none";
mozile.edit.navigateLeftRight.watchesChanges = "none";
mozile.edit.navigateLeftRight.prepare = function(event, direction, extend) {
var state = new mozile.edit.State(this, false);
state.direction = mozile.edit.NEXT;
if(direction) state.direction = direction;
else if(event && event.keyCode == 37) state.direction = mozile.edit.PREVIOUS;
state.extend = false;
if(extend) state.extend = extend;
else if(event) state.extend = event.shiftKey;
state.reversible = false;
return state;
}
mozile.edit.navigateLeftRight.execute = function(state, fresh) {
var selection = mozile.dom.selection.get();
if(selection.isCollapsed || state.extend) {
var IP = selection.getInsertionPoint();
IP.seek(state.direction, true, mozile.document.documentElement);
if(state.extend) IP.extend();
else IP.select();
}
else {
if(state.direction == mozile.edit.NEXT) selection.collapseToEnd();
else selection.collapseToStart();
}
state.executed = true;
return state;
}
mozile.edit.Navigate = function(name) {
this.name = name;
this.group = false;
this.remove = true;
this.makesChanges = "none";
this.watchesChanges = "none";
this.target = "text";
this.direction = "next";
this.collapse = null;
mozile.edit.allCommands[this.name] = this;
}
mozile.edit.Navigate.prototype = new mozile.edit.Command;
mozile.edit.Navigate.prototype.constructor = mozile.edit.Navigate;
mozile.edit.Navigate.prototype.prepare = function(event) {
var state = new mozile.edit.State(this);
var target = mozile.edit._getTarget(event, this.target, this.direction);
state.target = state.storeNode(target);
if(this.prompt) {
if(!this.prompt(event, state)) return null;
}
state.reversible = false;
return state;
}
mozile.edit.Navigate.prototype.execute = function(state, fresh) {
var selection = mozile.dom.selection.get();
var target = mozile.xpath.getNode(state.target);
var direction = mozile.edit.NEXT;
if(this.direction == "previous") direction = mozile.edit.PREVIOUS;
var IP = mozile.edit.getInsertionPoint(target, direction);
if(IP) {
IP.select();
IP = mozile.edit.getInsertionPoint(target, -1 * direction);
if(IP) IP.extend();
if(this.collapse == "start") selection.collapseToStart();
else if(this.collapse == "end") selection.collapseToEnd();
selection.scroll();
}
state.executed = true;
return state;
}
mozile.edit.insertText = new mozile.edit.Command("insertText");
mozile.edit.insertText.priority = 10;
mozile.edit.insertText.makesChanges = "text";
mozile.edit.insertText.watchesChanges = "none";
mozile.edit.insertText.test = function(event, direction, content, node) {
if(event) {
if(mozile.browser.isIE) {
if(event.charCode == 32) {
if(event.type != "keydown") return false;
}
else if(event.type != "keypress") return false;
}
else if(event.type != "keypress") return false;
if(event.ctrlKey || event.metaKey) return false;
if(!mozile.os.isMac && event.altKey) return false;
if(!node && event.charCode == 32 && !mozile.alternateSpace) {
var range = event.range;
if(!range) range = mozile.dom.selection.get().getRangeAt(0);
if(range.startContainer.nodeType == mozile.dom.TEXT_NODE) {
if(range.startContainer.data.charAt(range.startOffset-1) == " ") {
return false;
}
}
}
if(event.charCode && event.charCode >= 32) {
if(mozile.browser.isSafari &&
event.charCode >= 63232 &&
event.charCode <= 63235) return false;
return true;
}
return false;
}
else {
if(typeof(content) != "string") return false;
}
return true;
}
mozile.edit.insertText.prepare = function(event, direction, content, node) {
var state = new mozile.edit.State(this);
state.direction = mozile.edit.NEXT;
if(direction) state.direction = direction;
state.content = " ";
if(content) state.content = content;
else if(event) state.content = String.fromCharCode(event.charCode);
state.collapse = false;
if(event) state.collapse = true;
state.node = state.storeNode(node);
state.remove = false;
var selection = null;
if(event && event.selection) selection = event.selection;
else selection = mozile.dom.selection.get();
if(mozile.alternateSpace && !state.node && state.content == " ") {
var range = selection.getRangeAt(0);
var alt = mozile.alternateSpace;
var text = range.startContainer;
var offset = range.startOffset;
var nextChar = null;
if(range.endContainer.nodeType == mozile.dom.TEXT_NODE)
nextChar = range.endContainer.data.charAt(range.endOffset);
var previousChar = null;
var previousAlt = false;
if(text.nodeType == mozile.dom.TEXT_NODE) {
previousChar = text.data.charAt(offset-1);
var data = text.data.substring(0, offset);
if(offset && data.lastIndexOf(alt) + alt.length == offset) {
previousAlt = true;
previousChar = text.data.charAt(offset - alt.length - 1);
}
}
if(previousAlt) {
if(previousChar && previousChar != " ") {
state.remove = true;
state.content = " " + alt;
}
else if(nextChar && nextChar == " ") state.content = alt;
}
else if(nextChar && nextChar == " ") state.content = alt;
else if(!previousChar || previousChar == " ") state.content = alt;
}
return state;
}
mozile.edit.insertText.execute = function(state, fresh) {
var selection = mozile.dom.selection.get();
if(!fresh) selection.restore(state.selection.before);
state.emptyToken = false;
state.actions = new Array();
if(!state.node && !selection.isCollapsed) {
if(mozile.edit.remove) mozile.edit.remove.request(state, fresh, state.direction, null, true);
else mozile.edit.removeText.request(state, fresh, state.direction);
}
if(state.node) {
var node = mozile.xpath.getNode(state.node);
state.changedNode = node;
state.oldData = node.data;
node.data = state.content;
if(state.direction == mozile.edit.NEXT) {
selection.collapse(node, 0);
selection.extend(node, node.data.length);
}
else {
selection.collapse(node, node.data.length);
selection.extend(node, 0);
}
state.selection.after = selection.store();
}
else if(mozile.edit.isEmptyToken(selection.focusNode)) {
state.emptyToken = true;
selection.focusNode.data = state.content;
if(!state.collapse && state.content.length > 0) {
selection.collapse(selection.focusNode, 0);
selection.extend(selection.focusNode, state.content.length);
}
else selection.collapse(selection.focusNode, state.content.length);
state.selection.after = selection.store();
}
else if(selection.focusNode.nodeType != mozile.dom.TEXT_NODE) {
state.newNode = mozile.document.createTextNode(state.content);
if(selection.focusOffset == 0) mozile.dom.prependChild(state.newNode, selection.focusNode);
else selection.focusNode.insertBefore(state.newNode, selection.focusNode.childNodes[selection.focusOffset]);
if(!state.collapse && state.newNode.data.length > 0) {
selection.collapse(state.newNode, 0);
selection.extend(state.newNode, state.newNode.data.length);
}
else selection.collapse(state.newNode, state.newNode.data.length);
state.selection.after = selection.store();
}
else {
var text = selection.focusNode;
var offset = selection.focusOffset;
if(state.remove) {
mozile.edit.removeText.request(state, fresh, -1 * state.direction, mozile.alternateSpace);
offset -= mozile.alternateSpace.length;
}
text.insertData(offset, state.content);
var newOffset = offset + (state.direction * state.content.length);
if(!state.collapse && offset != newOffset) {
selection.collapse(text, offset);
selection.extend(text, newOffset);
}
else selection.collapse(text, newOffset);
if(state.actions.length == 0)
state.selection.after = selection.store(state.selection.before, newOffset);
else state.selection.after = selection.store();
}
state.executed = true;
return state;
}
mozile.edit.insertText.unexecute = function(state, fresh) {
var selection = mozile.dom.selection.get();
if(!fresh) selection.restore(state.selection.after);
if(state.changedNode) state.changedNode.data = state.oldData;
else if(state.emptyToken) selection.focusNode.data = mozile.emptyToken;
else if(state.newNode) state.newNode.parentNode.removeChild(state.newNode);
else selection.focusNode.deleteData(selection.focusOffset - state.content.length, state.content.length);
for(var i = state.actions.length - 1; i >= 0; i--) {
state.actions[i] = state.actions[i].command.unexecute(state.actions[i], fresh);
if(state.actions[i].executed) throw("Error: mozile.edit.inertText.unexecute Child command unexecute failed at action "+ i +".");
}
selection.restore(state.selection.before);
state.executed = false;
return state;
}
mozile.edit.removeText = new mozile.edit.Command("removeText");
mozile.edit.removeText.priority = 10;
mozile.edit.removeText.makesChanges = "text";
mozile.edit.removeText.watchesChanges = "none";
mozile.edit.removeText.test = function(event, direction, content) {
var dir;
if(event) {
if(mozile.edit.remove) return false;
if(mozile.edit.checkAccelerator(event, "Backspace"))
dir = mozile.edit.PREVIOUS;
else if(mozile.edit.checkAccelerator(event, "Delete"))
dir = mozile.edit.NEXT;
if(!dir) return false;
}
if(!dir) dir = mozile.edit.PREVIOUS;
if(direction == mozile.edit.NEXT) dir = direction;
if(!event) event = {type: "fake"};
var node = mozile.edit._getNode(event);
if(!node || node.nodeType != mozile.dom.TEXT_NODE) return false;
if(event.type != "fake") {
var selection = event.selection;
if(selection.isCollapsed) {
var IP = selection.getInsertionPoint(true);
if(!IP) return false;
if(!IP.seek(dir)) return false;
if(!IP || IP.getNode() !== node) return false;
}
return true;
}
else return true;
}
mozile.edit.removeText.prepare = function(event, direction, content) {
var state = new mozile.edit.State(this);
state.direction = mozile.edit.PREVIOUS;
if(direction) state.direction = direction;
else if(event && mozile.edit.convertKeyCode(event.keyCode) == "Delete")
state.direction = mozile.edit.NEXT;
state.content = null;
if(content) state.content = content;
return state;
}
mozile.edit.removeText.execute = function(state, fresh) {
var selection = mozile.dom.selection.get();
if(!fresh) selection.restore(state.selection.before);
if(!state.direction) state.direction = mozile.edit.PREVIOUS;
if(selection.isCollapsed) {
var firstOffset = selection.focusOffset;
var secondOffset = selection.focusOffset;
if(!state.content) {
var IP = selection.getInsertionPoint();
IP.seek(state.direction);
if(state.direction == mozile.edit.PREVIOUS) firstOffset = IP.getOffset();
else secondOffset = IP.getOffset();
state.content = selection.focusNode.data.substring(firstOffset, secondOffset);
}
else {
if(state.direction == mozile.edit.PREVIOUS)
firstOffset -= state.content.length;
else secondOffset += state.content.length;
}
if(firstOffset < 0) firstOffset = 0;
if(firstOffset + state.content.length <= selection.focusNode.data.length) {
selection.focusNode.deleteData(firstOffset, state.content.length);
selection.collapse(selection.focusNode, firstOffset);
state.selection.after = selection.store(state.selection.before, firstOffset);
}
else mozile.debug.debug("mozile.edit.removeText.execute", "Content length too great. firstOffset="+ firstOffset +" content='"+ state.content +"' data='"+ selection.focusNode.data +"'");
}
else {
var range = selection.getRangeAt(0);
if(mozile.browser.isIE && range.startContainer != range.endContainer) {
if(range.endOffset == 0) range.setEnd(range.startContainer, range.startContainer.data.length);
else range.setStart(range.endContainer, 0);
}
state.content = range.startContainer.data.substring(range.startOffset, range.endOffset);
range.startContainer.deleteData(range.startOffset, range.endOffset - range.startOffset);
selection.collapse(range.startContainer, range.startOffset);
state.selection.after = selection.store(state.selection.before, range.startOffset);
}
state.executed = true;
return state;
}
mozile.edit.removeText.unexecute = function(state, fresh) {
var selection = mozile.dom.selection.get();
if(!fresh) selection.restore(state.selection.after);
if(!selection || !selection.focusNode) throw("Error: mozile.edit.removeText.unexecute no selection.focusNode");
selection.focusNode.insertData(selection.focusOffset, state.content);
selection.restore(state.selection.before);
state.executed = false;
return state;
}
mozile.edit.findText = new mozile.edit.Command("findText");
mozile.edit.findText.makesChanges = "none";
mozile.edit.findText.watchesChanges = "none";
mozile.edit.findText.test = function(event, targetText, direction, ignoreCase, wrapAround, container) {
if(!targetText) return false;
return true;
}
mozile.edit.findText.prepare = function(event, targetText, direction, ignoreCase, wrapAround, container) {
var state = new mozile.edit.State(this);
state.reversible = false;
state.targetText = targetText;
state.direction = mozile.edit.NEXT;
if(direction) state.direction = direction;
state.ignoreCase = false;
if(ignoreCase) state.ignoreCase = true;
state.wrapAround = true;
if(wrapAround === false || wrapAround === null) state.wrapAround = false;
state.container = null;
if(container) state.container = state.storeNode(container);
return state;
}
mozile.edit.findText.execute = function(state, fresh) {
var selection = mozile.dom.selection.get();
if(!fresh) selection.restore(state.selection.before);
var direction = state.direction;
var target = state.targetText;
if(direction == mozile.edit.NEXT) selection.collapseToEnd();
else selection.collapseToStart();
var startIP = selection.getInsertionPoint();
var IP = new mozile.edit.InsertionPoint(startIP.getNode(), startIP.getOffset());
var container;
if(state.container) container = mozile.xpath.getNode(state.container);
else container = mozile.edit.getContainer(startIP.getNode());
var wrapped = false;
var offset = 0;
if(direction == mozile.edit.PREVIOUS) offset = 1;
var matched = "";
var index = 0;
var matchStart;
while(true) {
var character = IP.charAt();
if(direction == mozile.edit.NEXT) position = matched.length;
else position = target.length - matched.length - 1;
if(position >= 0) {
var compare = false;
if(state.ignoreCase) {
if(character.toLowerCase() == target.charAt(position).toLowerCase())
compare = true;
}
else if(character == target.charAt(position)) compare = true;
if(compare) {
if(matched.length == 0) {
matchStart = new mozile.edit.InsertionPoint(
IP.getNode(), IP.getOffset() + offset);
}
if(direction == mozile.edit.NEXT) matched = matched + character;
else matched = character + matched;
if(matched.length == target.length) {
if(direction == mozile.edit.NEXT) IP.next();
break;
}
}
else if(character) {
matched = "";
matchStart = null;
}
}
if(state.wrapAround) {
if(wrapped && startIP.getNode() == IP.getNode()) {
if(direction == mozile.edit.NEXT) {
if(IP.getOffset() >= startIP.getOffset()) break;
}
else {
if(IP.getOffset() <= startIP.getOffset()) break;
}
}
if(!IP.seek(direction, false, container)) {
if(wrapped) break;
wrapped = true;
matched = "";
matchStart = null;
IP = mozile.edit.getInsertionPoint(container, direction, true);
}
}
else if(!IP.seek(direction, false, container)) break;
}
if(matchStart) {
if(direction == mozile.edit.NEXT) {
matchStart.select();
IP.extend();
}
else {
IP.select();
matchStart.extend();
}
}
state.executed = true;
return state;
}
mozile.edit.replaceText = new mozile.edit.Command("replaceText");
mozile.edit.replaceText.makesChanges = "text";
mozile.edit.replaceText.watchesChanges = "none";
mozile.edit.replaceText.test = function(event, targetText, replacementText, ignoreCase, wrapAround, container) {
if(!targetText) return false;
if(!replacementText) return false;
return true;
}
mozile.edit.replaceText.prepare = function(event, targetText, replacementText, ignoreCase, wrapAround, container) {
var state = new mozile.edit.State(this);
state.targetText = targetText;
state.replacementText = replacementText;
state.ignoreCase = false;
if(ignoreCase) state.ignoreCase = true;
state.wrapAround = false;
if(wrapAround === false || wrapAround === null) state.wrapAround = false;
state.container = null;
if(container) state.container = state.storeNode(container);
return state;
}
mozile.edit.replaceText.execute = function(state, fresh) {
var selection = mozile.dom.selection.get();
if(!fresh) selection.restore(state.selection.before);
state.actions = new Array();
var direction = mozile.edit.NEXT;
var target = state.targetText;
var replacement = state.replacementText;
var selection = mozile.dom.selection.get();
selection.restore();
selection.collapseToEnd();
var startIP = selection.getInsertionPoint();
var IP = new mozile.edit.InsertionPoint(startIP.getNode(), startIP.getOffset());
var container;
if(state.container) container = mozile.xpath.getNode(state.container);
else container = mozile.edit.getContainer(startIP.getNode());
var wrapped = false;
var matched = "";
var matchStart, compare;
while(true) {
var character = IP.charAt();
position = matched.length;
compare = false;
if(state.ignoreCase) {
if(character.toLowerCase() == target.charAt(position).toLowerCase())
compare = true;
}
else if(character == target.charAt(position)) compare = true;
if(compare) {
if(matched.length == 0) {
matchStart = new mozile.edit.InsertionPoint(
IP.getNode(), IP.getOffset());
}
matched = matched + character;
if(matched.length == target.length) {
IP.next();
matchStart.select();
IP.extend();
if(mozile.event) {
mozile.event.storeSelection({selection: selection});
}
mozile.edit.insertText.request(state, false, null, replacement);
}
}
else if(character) {
matched = "";
matchStart = null;
}
if(state.wrapAround) {
if(wrapped && startIP.getNode() == IP.getNode()) {
if(IP.getOffset() >= startIP.getOffset()) break;
}
if(!IP.seek(direction, false, container)) {
if(wrapped) break;
wrapped = true;
IP = mozile.edit.getInsertionPoint(container, direction, true);
}
}
else if(!IP.seek(direction, false, container)) break;
}
state.selection.after = selection.store();
state.executed = true;
return state;
}
mozile.edit.showHidden = new mozile.edit.Command("showHidden");
mozile.edit.showHidden.makesChanges = "node";
mozile.edit.showHidden.watchesChanges = "node";
mozile.edit.showHidden.linkElement = null;
mozile.edit.commands.addCommand(mozile.edit.showHidden);
mozile.edit.showHidden.isActive = function(event) {
if(this.linkElement) return true;
return false;
}
mozile.edit.showHidden.test = function(event) {
if(event) {
if(!mozile.edit.checkAccelerators(event, this.accels)) return false;
}
return true;
}
mozile.edit.showHidden.execute = function(state, fresh) {
if(this.linkElement && this.linkElement.parentNode) {
this.linkElement.parentNode.removeChild(this.linkElement);
this.linkElement = null;
}
else {
var path = mozile.joinPaths(mozile.root, "src", "gui", "hidden.css");
this.linkElement = mozile.dom.addStyleSheet(path);
}
state.reversible = false;
state.executed = true;
return state;
}
mozile.edit.isBlock = function(node) {
if(!node) return false;
if(node.nodeType != mozile.dom.ELEMENT_NODE) return false;
var display = mozile.dom.getStyle(node, "display");
switch(display) {
case "block":
case "list-item":
return true;
}
return false;
}
mozile.edit.getParentBlock = function(node) {
while(node) {
if(mozile.edit.isBlock(node)) return node;
else node = node.parentNode;
}
return null;
}
mozile.edit.mayContainText = function(node) {
if(node && node.nodeType == mozile.dom.TEXT_NODE) node = node.parentNode;
if(node && node.nodeType == mozile.dom.ELEMENT_NODE) {
var rng = mozile.edit.lookupRNG(node);
if(rng) return rng.mayContain("text");
else {
if(mozile.edit.getMark(node, "mayContainText") == true) return true;
mozile.debug.debug("mozile.edit.mayContainText", "No RNG Element for element named '"+ node.nodeName +"'.");
for(var i=0; i < node.childNodes.length; i++) {
if(mozile.edit.isTextEditable(node.childNodes[i]))
return mozile.edit.setMark(node, "mayContainText", true);
}
return false;
}
}
return false;
}
mozile.edit.isTextEditable = function(node) {
if(node.nodeType != mozile.dom.TEXT_NODE) return false;
if(!node.data) return false;
if(mozile.edit.isEmptyToken(node)) return true;
if(mozile.dom.isWhitespace(node)) return false;
return true;
}
mozile.edit.isChildless = function(node) {
if(node.nodeType == mozile.dom.COMMENT_NODE) return true;
if(node.nodeType != mozile.dom.ELEMENT_NODE) return false;
var rng = mozile.edit.lookupRNG(node);
if(rng) {
if(rng.mayContain("element")) return false;
else return true;
}
else return false;
}
mozile.edit.createEmptyToken = function() {
return mozile.document.createTextNode(mozile.emptyToken);
}
mozile.edit.isEmptyToken = function(node) {
if(node && node.nodeType == mozile.dom.TEXT_NODE &&
node.data == mozile.emptyToken) return true;
else return false;
}
mozile.edit.containsEmptyToken = function(node, offset) {
if(!node || node.nodeType != mozile.dom.TEXT_NODE) return false;
if(offset == undefined || Number(offset)) {
if(node.data.indexOf(mozile.emptyToken) > -1) return true;
else return false;
}
else {
var data = node.data.substring(offset);
if(data.indexOf(mozile.emptyToken) == 0) return true;
else return false;
}
}
mozile.edit.isEmpty = function(node) {
switch(node.nodeType) {
case mozile.dom.TEXT_NODE:
if(node.data.match(/\S/)) return false;
if(mozile.edit.isEmptyToken(node)) return false;
return true;
case mozile.dom.ELEMENT_NODE:
var children = node.childNodes;
var i=0;
for(i=0; i < children.length; i++) {
if(children[i].nodeType == mozile.dom.TEXT_NODE &&
!mozile.edit.isEmpty(children[i]) )
return false;
}
for(i=0; i < children.length; i++) {
if(children[i].nodeType == mozile.dom.ELEMENT_NODE &&
!mozile.edit.isEmpty(children[i]) )
return false;
}
return true;
default:
return true;
}
}
mozile.edit._getElementName = function(command) {
var elementName;
if(typeof(command.element) == "string") elementName = command.element;
else if(command.element && command.element.cloneNode)
elementName = mozile.dom.getLocalName(command.element);
elementName = elementName.toLowerCase();
return elementName;
}
mozile.edit._getNode = function(event) {
var node;
if(event && event.node) node = event.node;
if(!node) {
var selection;
if(event && event.selection) selection = event.selection;
if(!selection) selection = mozile.dom.selection.get();
if(!selection) return false;
var range;
if(event && event.range) range = event.range;
if(!selection.rangeCount) return false;
if(!range) range = selection.getRangeAt(0);
if(!range) return false;
node = range.commonAncestorContainer;
if(event) {
event.selection = selection;
event.range = range;
event.node = node;
}
}
if(!node) return null;
else return node;
}
mozile.edit._getTarget = function(event, target, direction, allowInvisible) {
if(!direction) direction = "ancestor";
if(direction != "ancestor" && direction != "descendant" &&
direction != "next" && direction != "previous") {
mozile.debug.debug("mozile.edit._getTarget", "Invalid direction '"+ direction +"'.");
return null;
}
if(typeof(target) == "string") {
if(event && !event.targetCache) event.targetCache = new Object();
var cacheKey = target.replace(" ", "_") + "__" + direction;
if(event && event.targetCache[cacheKey])
return event.targetCache[cacheKey];
}
var node = mozile.edit._getNode(event);
if(!node) return null;
var test, result;
if(typeof(target) == "function") {
result = target(event, null);
}
else if(typeof(target) == "string") {
if(target.toLowerCase() == "any") {
test = function(node) {
if(node) return true;
else return false;
}
}
else if(target.toLowerCase() == "text") {
test = function(node) {
if(node.nodeType == mozile.dom.TEXT_NODE) return true;
else return false;
}
}
else if(target.toLowerCase() == "element") {
test = function(node) {
if(node.nodeType == mozile.dom.ELEMENT_NODE) return true;
else return false;
}
}
else if(target.toLowerCase() == "block") {
test = function(node) {
if(mozile.edit.isBlock(node)) return true;
else return false;
}
}
else if(target.toLowerCase().indexOf("localname") == 0) {
var name = target.substring(10);
name = name.toLowerCase();
test = function(node) {
var localName = mozile.dom.getLocalName(node);
if(localName && localName.toLowerCase() == name) return true;
else return false;
}
}
else return null;
}
else return null;
var treeWalker;
if(test && !result) {
if(direction != "ancestor" && !treeWalker) {
var root = mozile.document.documentElement;
if(direction == "descendant") {
root = node;
if(root.nodeType != mozile.dom.ELEMENT_NODE) root = root.parentNode;
direction = "next";
}
treeWalker = document.createTreeWalker(root, mozile.dom.NodeFilter.SHOW_ALL, null, false);
treeWalker.currentNode = node;
}
var startNode = node;
while(node) {
if(direction == "next") node = treeWalker.nextNode();
else if(direction == "previous") {
node = treeWalker.previousNode();
if(mozile.dom.isAncestorOf(node, startNode)) continue;
}
if(node && test(node) && mozile.edit.isEditable(node) &&
(allowInvisible || mozile.dom.isVisible(node)) &&
mozile.edit.getInsertionPoint(node, mozile.edit.NEXT)) {
result = node;
break;
}
if(direction == "ancestor") node = node.parentNode;
}
}
if(result) {
if(event) event.targetCache[cacheKey] = result;
return result;
}
else return null;
}
mozile.enableEditing(false);
Documentation generated by
JSDoc on Wed Feb 20 13:25:28 2008