File "htmlarea.js"
Full Path: /home/analogde/www/files02/htmlarea/htmlarea.js
File size: 36.17 KB
MIME-type: text/plain
Charset: utf-8
//
// htmlArea v3.0 - Copyright (c) 2002 interactivetools.com, inc.
// This copyright notice MUST stay intact for use (see license.txt).
//
// A free WYSIWYG editor replacement for <textarea> fields.
// For full source code and docs, visit http://www.interactivetools.com/
//
// Version 3.0 developed by Mihai Bazon for InteractiveTools.
// http://students.infoiasi.ro/~mishoo
//
// $Id: htmlarea.js,v 1.3 2003/02/17 13:57:07 mishoo Exp $
// Creates a new HTMLArea object. Tries to replace the textarea with the given
// ID with it.
function HTMLArea(textarea, config) {
if (HTMLArea.checkSupportedBrowser()) {
if (typeof config == "undefined") {
this.config = new HTMLArea.Config();
} else {
this.config = config;
}
if (this.config.debug) {
// alert("DEBUG ON!!");
}
this._htmlArea = null;
this._textArea = textarea;
this._mode = "wysiwyg";
}
};
HTMLArea.Config = function () {
this.version = "3.0";
this.width = "auto";
this.height = "auto";
// the next parameter specifies whether the toolbar should be included
// in the size or not.
this.sizeIncludesToolbar = true;
this.bodyStyle = "background-color: #fff; font-family: verdana,sans-serif";
this.editorURL = "htmlarea/";
// URL-s
this.imgURL = "images/";
this.popupURL = "popups/";
this.debug = 0;
this.replaceNextLines = 0;
this.plainTextInput = 0;
this.toolbar = [ [ "fontname", "space" ],
[ "fontsize", "space" ],
[ "formatblock", "space"],
[ "bold", "italic", "underline", "separator" ],
[ "strikethrough", "subscript", "superscript", "linebreak" ],
[ "justifyleft", "justifycenter", "justifyright", "justifyfull", "separator" ],
[ "orderedlist", "unorderedlist", "outdent", "indent", "separator" ],
[ "forecolor", "backcolor", "textindicator", "separator" ],
[ "horizontalrule", "createlink", "insertimage", "inserttable", "htmlmode", "separator" ]
];
this.fontname = {
"Arial": 'arial,helvetica,sans-serif',
"Courier New": 'courier new,courier,monospace',
"Georgia": 'georgia,times new roman,times,serif',
"Tahoma": 'tahoma,arial,helvetica,sans-serif',
"Times New Roman": 'times new roman,times,serif',
"Verdana": 'verdana,arial,helvetica,sans-serif',
"impact": 'impact',
"WingDings": 'wingdings'
};
this.fontsize = {
"1 (8 pt)": "1",
"2 (10 pt)": "2",
"3 (12 pt)": "3",
"4 (14 pt)": "4",
"5 (18 pt)": "5",
"6 (24 pt)": "6",
"7 (36 pt)": "7"
};
this.formatblock = {
"Heading 1": "h1",
"Heading 2": "h2",
"Heading 3": "h3",
"Heading 4": "h4",
"Heading 5": "h5",
"Heading 6": "h6",
"Normal": "p",
"Address": "address",
"Formatted": "pre"
};
// ID CMD ToolTip Icon Enabled in text mode?
this.btnList = {
bold: ["Bold", "Bold", "ed_format_bold.gif", false],
italic: ["Italic", "Italic", "ed_format_italic.gif", false],
underline: ["Underline", "Underline", "ed_format_underline.gif", false],
strikethrough: ["StrikeThrough", "Strikethrough", "ed_format_strike.gif", false],
subscript: ["SubScript", "Subscript", "ed_format_sub.gif", false],
superscript: ["SuperScript", "Superscript", "ed_format_sup.gif", false],
justifyleft: ["JustifyLeft", "Justify Left", "ed_align_left.gif", false],
justifycenter: ["JustifyCenter", "Justify Center", "ed_align_center.gif", false],
justifyright: ["JustifyRight", "Justify Right", "ed_align_right.gif", false],
justifyfull: ["JustifyFull", "Justify Full", "ed_align_justify.gif", false],
orderedlist: ["InsertOrderedList", "Ordered List", "ed_list_num.gif", false],
unorderedlist: ["InsertUnorderedList", "Bulleted List", "ed_list_bullet.gif", false],
outdent: ["Outdent", "Decrease Indent", "ed_indent_less.gif", false],
indent: ["Indent", "Increase Indent", "ed_indent_more.gif", false],
forecolor: ["ForeColor", "Font Color", "ed_color_fg.gif", false],
backcolor: ["BackColor", "Background Color", "ed_color_bg.gif", false],
horizontalrule: ["InsertHorizontalRule", "Horizontal Rule", "ed_hr.gif", false],
createlink: ["CreateLink", "Insert Web Link", "ed_link.gif", false],
insertimage: ["InsertImage", "Insert Image", "ed_image.gif", false],
inserttable: ["InsertTable", "Insert Table", "insert_table.gif", false],
htmlmode: ["HtmlMode", "Toggle HTML Source", "ed_html.gif", true],
popupeditor: ["popupeditor", "Enlarge Editor", "fullscreen_maximize.gif", true],
about: ["about", "About this editor", "ed_about.gif", true],
help: ["showhelp", "Help using editor", "ed_help.gif", true]
};
// initialize tooltips from the I18N module
for (var i in this.btnList) {
var btn = this.btnList[i];
if (typeof HTMLArea.I18N.tooltips[i] != "undefined") {
btn[1] = HTMLArea.I18N.tooltips[i];
}
}
};
/** Helper function: replace all TEXTAREA-s in the document with HTMLArea-s. */
HTMLArea.replaceAll = function() {
var tas = document.getElementsByTagName("textarea");
for (var i = tas.length; i > 0; (new HTMLArea(tas[--i])).generate());
};
// Creates the toolbar and appends it to the _htmlarea
HTMLArea.prototype._createToolbar = function () {
var editor = this; // to access this in nested functions
var toolbar = document.createElement("div");
this._toolbar = toolbar;
toolbar.className = "toolbar";
toolbar.unselectable = "1";
if (editor.config.debug) {
toolbar.style.border = "1px solid red";
}
var tb_row = null;
var tb_objects = new Object();
this._toolbarObjects = tb_objects;
// creates a new line in the toolbar
function newLine() {
var table = document.createElement("table");
table.border = "0px";
table.cellSpacing = "0px";
table.cellPadding = "0px";
toolbar.appendChild(table);
// TBODY is required for IE, otherwise you don't see anything
// in the TABLE.
var tb_body = document.createElement("tbody");
table.appendChild(tb_body);
tb_row = document.createElement("tr");
tb_body.appendChild(tb_row);
};
// init first line
newLine();
// appends a new button to toolbar
function createButton(txt) {
// updates the state of a toolbar element
function setButtonStatus(id, newval) {
var oldval = this[id];
var el = this.element;
if (oldval != newval) {
switch (id) {
case "enabled":
if (newval) {
HTMLArea._removeClass(el, "buttonDisabled");
el.disabled = false;
} else {
HTMLArea._addClass(el, "buttonDisabled");
el.disabled = true;
}
break;
case "active":
if (newval) {
HTMLArea._addClass(el, "buttonPressed");
} else {
HTMLArea._removeClass(el, "buttonPressed");
}
break;
}
this[id] = newval;
}
};
// this function will handle creation of combo boxes
function createSelect() {
var options = null;
var el = null;
var cmd = null;
switch (txt) {
case "fontsize":
case "fontname":
case "formatblock":
options = editor.config[txt]; // HACK ;)
cmd = txt;
break;
}
if (options) {
el = document.createElement("select");
var obj = {
name: txt, // field name
element: el, // the UI element (SELECT)
enabled: true, // is it enabled?
text: false, // enabled in text mode?
cmd: cmd, // command ID
state: setButtonStatus // for changing state
};
tb_objects[txt] = obj;
for (var i in options) {
var op = document.createElement("option");
op.appendChild(document.createTextNode(i));
op.value = options[i];
el.appendChild(op);
}
HTMLArea._addEvent(el, "change", function () {
editor._comboSelected(el, txt);
});
}
return el;
};
// the element that will be created
var el = null;
var btn = null;
switch (txt) {
case "separator":
el = document.createElement("div");
el.className = "separator";
break;
case "space":
el = document.createElement("div");
el.className = "space";
break;
case "linebreak":
newLine();
return false;
case "textindicator":
el = document.createElement("div");
el.appendChild(document.createTextNode("A"));
el.className = "indicator";
el.title = HTMLArea.I18N.tooltips.textindicator;
var obj = {
name: txt, // the button name (i.e. 'bold')
element: el, // the UI element (DIV)
enabled: true, // is it enabled?
active: false, // is it pressed?
text: false, // enabled in text mode?
cmd: "textindicator", // the command ID
state: setButtonStatus // for changing state
};
tb_objects[txt] = obj;
break;
default:
btn = editor.config.btnList[txt];
break;
}
if (!el && btn) {
el = document.createElement("div");
el.title = btn[1];
el.className = "button";
// let's just pretend we have a button object, and
// assign all the needed information to it.
var obj = {
name: txt, // the button name (i.e. 'bold')
element: el, // the UI element (DIV)
enabled: true, // is it enabled?
active: false, // is it pressed?
text: btn[3], // enabled in text mode?
cmd: btn[0], // the command ID
state: setButtonStatus // for changing state
};
tb_objects[txt] = obj;
// handlers to emulate nice flat toolbar buttons
HTMLArea._addEvent(el, "mouseover", function () {
if (obj.enabled) {
HTMLArea._addClass(el, "buttonHover");
}
});
HTMLArea._addEvent(el, "mouseout", function () {
if (obj.enabled) with (HTMLArea) {
_removeClass(el, "buttonHover");
_removeClass(el, "buttonActive");
(obj.active) && _addClass(el, "buttonPressed");
}
});
HTMLArea._addEvent(el, "mousedown", function (ev) {
if (obj.enabled) with (HTMLArea) {
_addClass(el, "buttonActive");
_removeClass(el, "buttonPressed");
_stopEvent(is_ie ? window.event : ev);
}
});
// when clicked, do the following:
HTMLArea._addEvent(el, "click", function (ev) {
if (obj.enabled) with (HTMLArea) {
_removeClass(el, "buttonActive");
_removeClass(el, "buttonHover");
editor._buttonClicked(txt);
_stopEvent(is_ie ? window.event : ev);
}
});
var img = document.createElement("img");
img.src = editor.imgURL(btn[2]);
el.appendChild(img);
} else if (!el) {
el = createSelect();
}
if (el) {
var tb_cell = document.createElement("td");
tb_row.appendChild(tb_cell);
tb_cell.appendChild(el);
} else {
alert("FIXME: Unknown toolbar item: " + txt);
}
return el;
};
for (var i in this.config.toolbar) {
var group = this.config.toolbar[i];
for (var j in group) {
createButton(group[j]);
}
}
this._htmlArea.appendChild(toolbar);
};
// Creates the HTMLArea object and replaces the textarea with it.
HTMLArea.prototype.generate = function () {
var editor = this; // we'll need "this" in some nested functions
// get the textarea
var textarea = this._textArea;
if (typeof textarea == "string") {
// it's not element but ID
this._textArea = textarea = document.getElementById(textarea);
}
this._ta_size = {
w: textarea.offsetWidth,
h: textarea.offsetHeight
};
// hide the textarea
textarea.style.display = "none";
// create the editor framework
var htmlarea = document.createElement("div");
htmlarea.className = "htmlarea";
this._htmlArea = htmlarea;
// insert the editor before the textarea.
textarea.parentNode.insertBefore(htmlarea, textarea);
// retrieve the HTML on submit
HTMLArea._addEvent(textarea.form, "submit", function (event) {
editor._formSubmit(HTMLArea.is_ie ? window.event : event);
});
// creates & appends the toolbar
this._createToolbar();
// create the IFRAME
var iframe = document.createElement("iframe");
htmlarea.appendChild(iframe);
this._iframe = iframe;
// remove the default border as it keeps us from computing correctly
// the sizes. (somebody tell me why doesn't this work in IE)
// iframe.style.border = "none";
// iframe.frameborder = "0";
// size the IFRAME according to user's prefs or initial textarea
var height = (this.config.height == "auto" ? (this._ta_size.h + "px") : this.config.height);
height = parseInt(height);
var width = (this.config.width == "auto" ? (this._ta_size.w + "px") : this.config.width);
width = parseInt(width);
iframe.style.width = width + "px";
if (this.config.sizeIncludesToolbar) {
// substract toolbar height
height -= this._toolbar.offsetHeight;
}
iframe.style.height = height + "px";
// now create a secondary textarea so that we can switch between
// WYSIWYG & text mode.
textarea = document.createElement("textarea");
// hidden by default
textarea.style.display = "none";
// make it the same size as the editor
textarea.style.width = iframe.style.width;
textarea.style.height = iframe.style.height;
// insert it after the iframe
htmlarea.appendChild(textarea);
// remember it for later
this._textArea2 = textarea;
// IMPORTANT: we have to allow Mozilla a short time to recognize the
// new frame. Otherwise we get a stupid exception.
function initIframe() {
var doc = editor._iframe.contentWindow.document;
if (!doc) {
if (HTMLArea.is_gecko) {
setTimeout(function () { editor._initIframe(); }, 10);
return false;
} else {
alert("ERROR: IFRAME can't be initialized.");
}
}
if (HTMLArea.is_gecko) {
// enable editable mode for Mozilla
doc.designMode = "on";
}
editor._doc = doc;
doc.open();
var html = "<html>\n";
html += "<head>\n";
html += "<style> body { " + editor.config.bodyStyle + " } </style>\n";
html += "</head>\n";
html += "<body>\n";
html += editor._textArea.value;
html += "</body>\n";
html += "</html>";
doc.write(html);
doc.close();
if (HTMLArea.is_ie) {
// enable editable mode for IE. For some reason this
// doesn't work if done in the same place as for Gecko
// (above).
doc.body.contentEditable = true;
}
editor.focusEditor();
// intercept some events; for updating the toolbar & keyboard handlers
HTMLArea._addEvents
(doc, ["keydown", "keypress", "mousedown", "mouseup", "drag"],
function (event) {
return editor._editorEvent(HTMLArea.is_ie ? editor._iframe.contentWindow.event : event);
});
editor.updateToolbar();
editor.focusEditor();
};
setTimeout(initIframe, HTMLArea.is_gecko ? 10 : 0);
};
// Switches editor mode; parameter can be "textmode" or "wysiwyg"
HTMLArea.prototype.setMode = function(mode) {
switch (mode) {
case "textmode":
this._textArea2.value = this.getHTML();
this._iframe.style.display = "none";
this._textArea2.style.display = "block";
break;
case "wysiwyg":
this._doc.body.innerHTML = this.getHTML();
this._iframe.style.display = "block";
this._textArea2.style.display = "none";
if (HTMLArea.is_gecko) {
// we need to refresh that info for Moz-1.3a
this._doc.designMode = "on";
}
break;
default:
alert("Mode <" + mode + "> not defined!");
return false;
}
this._mode = mode;
this.focusEditor();
};
/***************************************************
* Category: EDITOR UTILITIES
***************************************************/
// focuses the iframe window. returns a reference to the editor document.
HTMLArea.prototype.focusEditor = function() {
switch (this._mode) {
case "wysiwyg":
this._iframe.contentWindow.focus();
break;
case "textmode":
this._textArea2.focus();
break;
default:
alert("ERROR: mode " + this._mode + " is not defined");
break;
}
return this._doc;
};
// updates enabled/disable/active state of the toolbar elements
HTMLArea.prototype.updateToolbar = function() {
var doc = this._doc;
var text = (this._mode == "textmode");
for (var i in this._toolbarObjects) {
var btn = this._toolbarObjects[i];
var cmd = btn.cmd;
if (typeof cmd == "function") {
continue;
}
cmd = cmd.toLowerCase();
btn.state("enabled", !text || btn.text);
switch (cmd) {
case "fontname":
case "fontsize":
case "formatblock":
if (!text) {
var value = ("" + doc.queryCommandValue(cmd)).toLowerCase();
if (!value) {
// FIXME: what do we do here?
break;
}
var options = this.config[i]; // HACK!!
var k = 0;
// btn.element.selectedIndex = 0;
for (var j in options) {
// FIXME: the following line is scary.
if ((j.toLowerCase() == value) ||
(options[j].substr(0, value.length).toLowerCase() == value)) {
btn.element.selectedIndex = k;
break;
}
++k;
}
}
break;
case "textindicator":
if (!text) {
try {with (btn.element.style) {
backgroundColor = HTMLArea._makeColor(doc.queryCommandValue("backcolor"));
color = HTMLArea._makeColor(doc.queryCommandValue("forecolor"));
fontFamily = doc.queryCommandValue("fontname");
fontWeight = doc.queryCommandState("bold") ? "bold" : "normal";
fontStyle = doc.queryCommandState("italic") ? "italic" : "normal";
}} catch (e) {
alert(e + "\n\n" + cmd);
}
}
break;
case "htmlmode":
btn.state("active", text);
break;
default:
try {
btn.state("active", (!text && doc.queryCommandState(cmd)));
} catch (e) {}
break;
}
}
};
/** Returns a node after which we can insert other nodes, in the current
* selection. The selection is removed. It splits a text node, if needed.
*/
HTMLArea.prototype.insertNodeAtSelection = function(toBeInserted) {
if (!HTMLArea.is_ie) {
var sel = this._getSelection();
var range = this._createRange(sel);
// remove the current selection
sel.removeAllRanges();
range.deleteContents();
var node = range.startContainer;
var pos = range.startOffset;
range = this._createRange();
switch (node.nodeType) {
case 3: // Node.TEXT_NODE
// we have to split it at the caret position.
if (toBeInserted.nodeType == 3) {
// do optimized insertion
node.insertData(pos, toBeInserted.data);
range.setEnd(node, pos + toBeInserted.length);
range.setStart(node, pos + toBeInserted.length);
} else {
node = node.splitText(pos);
node.parentNode.insertBefore(toBeInserted, node);
range.setStart(node, 0);
range.setEnd(node, 0);
}
break;
case 1: // Node.ELEMENT_NODE
node = node.childNodes[pos];
node.parentNode.insertBefore(toBeInserted, node);
range.setStart(node, 0);
range.setEnd(node, 0);
break;
}
sel.addRange(range);
} else {
return null; // this function not yet used for IE <FIXME>
}
};
/** Call this function to insert HTML code at the current position. It deletes
* the selection, if any.
*/
HTMLArea.prototype.insertHTML = function(html) {
var sel = this._getSelection();
var range = this._createRange(sel);
if (HTMLArea.is_ie) {
range.pasteHTML(html);
} else {
// construct a new document fragment with the given HTML
var fragment = this._doc.createDocumentFragment();
var div = this._doc.createElement("div");
div.innerHTML = html;
while (div.firstChild) {
// the following call also removes the node from div
fragment.appendChild(div.firstChild);
}
// this also removes the selection
var node = this.insertNodeAtSelection(fragment);
}
};
/**
* Call this function to surround the existing HTML code in the selection with
* your tags.
*/
HTMLArea.prototype.surroundHTML = function(startTag, endTag) {
var html = this.getSelectedHTML();
// the following also deletes the selection
this.insertHTML(startTag + html + endTag);
};
/// Retrieve the selected block
HTMLArea.prototype.getSelectedHTML = function() {
var sel = this._getSelection();
var range = this._createRange(sel);
var existing = null;
if (HTMLArea.is_ie) {
existing = range.htmlText;
} else {
existing = HTMLArea.getHTML(range.cloneContents(), false);
}
return existing;
};
// Called when the user clicks on "InsertImage" button
HTMLArea.prototype._insertImage = function() {
var sel = this._getSelection();
var range = this._createRange(sel);
var editor = this; // for nested functions
this._popupDialog("insert_image.html", function(param) {
if (!param) { // user must have pressed Cancel
return false;
}
editor._execCommand("insertimage", false, param["f_url"]);
var img = null;
if (HTMLArea.is_ie) {
img = range.parentElement();
// wonder if this works...
if (img.tagName.toLowerCase() != "img") {
img = img.previousSibling;
}
} else {
img = range.startContainer.previousSibling;
}
for (field in param) {
var value = param[field];
if (!value) {
continue;
}
switch (field) {
case "f_alt":
img.alt = value;
break;
case "f_border":
img.border = parseInt(value);
break;
case "f_align":
img.align = value;
break;
case "f_vert":
img.vspace = parseInt(value);
break;
case "f_horiz":
img.hspace = parseInt(value);
break;
}
}
}, null);
};
// Called when the user clicks the Insert Table button
HTMLArea.prototype._insertTable = function() {
var sel = this._getSelection();
var range = this._createRange(sel);
var editor = this; // for nested functions
this._popupDialog("insert_table.html", function(param) {
if (!param) { // user must have pressed Cancel
return false;
}
var doc = editor._doc;
// create the table element
var table = doc.createElement("table");
// assign the given arguments
for (var field in param) {
var value = param[field];
if (!value) {
continue;
}
switch (field) {
case "f_width":
table.style.width = value + param["f_unit"];
break;
case "f_align":
table.align = value;
break;
case "f_border":
table.border = parseInt(value);
break;
case "f_spacing":
table.cellspacing = parseInt(value);
break;
case "f_padding":
table.cellpadding = parseInt(value);
break;
}
}
var tbody = doc.createElement("tbody");
table.appendChild(tbody);
for (var i = 0; i < param["f_rows"]; ++i) {
var tr = doc.createElement("tr");
tbody.appendChild(tr);
for (var j = 0; j < param["f_cols"]; ++j) {
var td = doc.createElement("td");
tr.appendChild(td);
if (HTMLArea.is_gecko) {
// Mozilla likes to see something
// inside the cell.
td.appendChild(doc.createElement("br"));
}
}
}
if (HTMLArea.is_ie) {
range.pasteHTML(HTMLArea.getHTML(table, true));
} else {
// insert the table
editor.insertNodeAtSelection(table);
}
return true;
}, null);
};
/***************************************************
* Category: EVENT HANDLERS
***************************************************/
// txt is the name of the button, as in config.toolbar
HTMLArea.prototype._buttonClicked = function(txt) {
var editor = this; // needed in nested functions
this.focusEditor();
var btn = this.config.btnList[txt];
if (!btn) {
alert("FIXME: Unconfigured button!");
return false;
}
var cmd = btn[0];
if (typeof cmd == "function") {
return cmd(this, txt);
}
switch (cmd.toLowerCase()) {
case "htmlmode":
this.setMode(this._mode != "textmode" ? "textmode" : "wysiwyg");
break;
case "forecolor":
case "backcolor":
this._popupDialog("select_color.html", function(color) {
editor._execCommand(cmd, false, "#" + color);
}, HTMLArea._colorToRgb(this._doc.queryCommandValue(btn[0])));
break;
case "createlink":
this._execCommand(cmd, true);
break;
case "insertimage":
this._insertImage();
break;
case "inserttable":
this._insertTable();
break;
case "popupeditor":
if (HTMLArea.is_ie) {
window.open(this.popupURL("fullscreen.html"), "ha_fullscreen",
"toolbar=no,location=no,directories=no,status=yes,menubar=no," +
"scrollbars=no,resizable=yes,width=640,height=480");
} else {
window.open(this.popupURL("fullscreen.html"), "ha_fullscreen",
"toolbar=no,menubar=no,personalbar=no,width=640,height=480," +
"scrollbars=no,resizable=yes");
}
// pass this object to the newly opened window
HTMLArea._object = this;
break;
case "about":
this._popupDialog("about.html", null, null);
break;
case "help":
alert("Help not implemented");
break;
default:
this._execCommand(btn[0], false, "");
break;
}
this.updateToolbar();
return false;
};
// el is reference to the SELECT object
// txt is the name of the select field, as in config.toolbar
HTMLArea.prototype._comboSelected = function(el, txt) {
this.focusEditor();
var value = el.options[el.selectedIndex].value;
switch (txt) {
case "fontname":
case "fontsize":
this._execCommand(txt, false, value);
break;
case "formatblock":
if (HTMLArea.is_ie) { // sad but true
value = "<" + value + ">";
}
this._execCommand(txt, false, value);
break;
default:
alert("FIXME: combo box " + txt + " not implemented");
break;
}
};
// the execCommand function (intercepts some commands and replaces them with
// our own implementation)
HTMLArea.prototype._execCommand = function(cmdID, UI, param) {
switch (cmdID.toLowerCase()) {
case "createlink":
if (HTMLArea.is_ie || !UI) {
this._doc.execCommand(cmdID, UI, param);
} else {
// browser is Mozilla & wants UI
if ((param = prompt("Enter URL"))) {
this._doc.execCommand(cmdID, false, param);
}
}
break;
case "backcolor":
if (HTMLArea.is_ie) {
this._doc.execCommand(cmdID, UI, param);
} else {
this._doc.execCommand("hilitecolor", UI, param);
}
break;
default:
this._doc.execCommand(cmdID, UI, param);
break;
}
this.focusEditor();
};
/** A generic event handler for things that happen in the IFRAME's document.
* This function also handles key bindings. */
HTMLArea.prototype._editorEvent = function(ev) {
var editor = this;
var keyEvent = (HTMLArea.is_ie && ev.type == "keydown") || (ev.type == "keypress");
if (keyEvent && ev.ctrlKey) {
var sel = null;
var range = null;
var key = String.fromCharCode(HTMLArea.is_ie ? ev.keyCode : ev.charCode).toLowerCase();
var cmd = null;
var value = null;
switch (key) {
case 'a':
if (!HTMLArea.is_ie) {
// KEY select all
sel = this._getSelection();
sel.removeAllRanges();
range = this._createRange();
range.selectNodeContents(this._doc.body);
sel.addRange(range);
HTMLArea._stopEvent(ev);
}
break;
// simple key commands follow
case 'b': // KEY bold
(!HTMLArea.is_ie) && (cmd = "bold");
break;
case 'i': // KEY italic
(!HTMLArea.is_ie) && (cmd = "italic");
break;
case 'u': // KEY underline
(!HTMLArea.is_ie) && (cmd = "underline");
break;
case 's': // KEY justify full
cmd = "strikethrough";
break;
case 'l': // KEY justify left
cmd = "justifyleft";
break;
case 'e': // KEY justify center
cmd = "justifycenter";
break;
case 'r': // KEY justify right
cmd = "justifyright";
break;
case 'j': // KEY justify full
cmd = "justifyfull";
break;
// headings
case '1': // KEY heading 1
case '2': // KEY heading 2
case '3': // KEY heading 3
case '4': // KEY heading 4
case '5': // KEY heading 5
case '6': // KEY heading 6
cmd = "formatblock";
value = "h" + key;
if (HTMLArea.is_ie) {
value = "<" + value + ">";
}
break;
}
if (cmd) {
// execute simple command
this._execCommand(cmd, false, value);
HTMLArea._stopEvent(ev);
}
}
/*
else if (keyEvent) {
// other keys here
switch (ev.keyCode) {
case 13: // KEY enter
// if (HTMLArea.is_ie) {
this.insertHTML("<br />");
HTMLArea._stopEvent(ev);
// }
break;
}
}
*/
// update the toolbar state after some time
setTimeout(function() {
editor.updateToolbar();
}, 50);
};
// gets called before the form is submitted
HTMLArea.prototype._formSubmit = function(ev) {
// retrieve the HTML
this._textArea.value = this.getHTML();
};
// retrieve the HTML
HTMLArea.prototype.getHTML = function() {
switch (this._mode) {
case "wysiwyg":
return HTMLArea.getHTML(this._doc.body, false);
case "textmode":
return this._textArea2.value;
default:
alert("Mode <" + mode + "> not defined!");
return false;
}
};
// retrieve the HTML (fastest version, but uses innerHTML)
HTMLArea.prototype.getInnerHTML = function() {
switch (this._mode) {
case "wysiwyg":
return this._doc.body.innerHTML;
case "textmode":
return this._textArea2.value;
default:
alert("Mode <" + mode + "> not defined!");
return false;
}
};
// completely change the HTML inside
HTMLArea.prototype.setHTML = function(html) {
switch (this._mode) {
case "wysiwyg":
this._doc.body.innerHTML = html;
break;
case "textmode":
this._textArea2.value = html;
break;
default:
alert("Mode <" + mode + "> not defined!");
}
return false;
};
/***************************************************
* Category: UTILITY FUNCTIONS
***************************************************/
// browser identification
HTMLArea.agt = navigator.userAgent.toLowerCase();
HTMLArea.is_ie = ((HTMLArea.agt.indexOf("msie") != -1) && (HTMLArea.agt.indexOf("opera") == -1));
HTMLArea.is_opera = (HTMLArea.agt.indexOf("opera") != -1);
HTMLArea.is_mac = (HTMLArea.agt.indexOf("mac") != -1);
HTMLArea.is_mac_ie = (HTMLArea.is_ie && HTMLArea.is_mac);
HTMLArea.is_win_ie = (HTMLArea.is_ie && !HTMLArea.is_mac);
HTMLArea.is_gecko = (navigator.product == "Gecko");
// variable used to pass the object to the popup editor window.
HTMLArea._object = null;
// FIXME!!! this should return false for IE < 5.5
HTMLArea.checkSupportedBrowser = function() {
/*
var gigi = "Navigator:\n\n";
for (var i in navigator) {
gigi += i + " = " + navigator[i] + "\n";
}
alert(gigi);
*/
if (HTMLArea.is_gecko) {
if (navigator.productSub < 20021201) {
alert("You need at least Mozilla-1.3 Alpha.\n" +
"Sorry, your Gecko is not supported.");
return false;
}
if (navigator.productSub < 20030210) {
alert("Mozilla < 1.3 Beta is not supported!\n" +
"I'll try, though, but it might not work.");
}
}
return HTMLArea.is_gecko || HTMLArea.is_ie;
};
// selection & ranges
// returns the current selection object
HTMLArea.prototype._getSelection = function() {
if (HTMLArea.is_ie) {
return this._doc.selection;
} else {
return this._iframe.contentWindow.getSelection();
}
};
// returns a range for the current selection
HTMLArea.prototype._createRange = function(sel) {
if (HTMLArea.is_ie) {
return sel.createRange();
} else {
this.focusEditor();
if (sel) {
return sel.getRangeAt(0);
} else {
return this._doc.createRange();
}
}
};
// event handling
HTMLArea._addEvent = function(el, evname, func) {
if (HTMLArea.is_ie) {
el.attachEvent("on" + evname, func);
} else {
el.addEventListener(evname, func, true);
}
};
HTMLArea._addEvents = function(el, evs, func) {
for (var i in evs) {
HTMLArea._addEvent(el, evs[i], func);
}
};
HTMLArea._removeEvent = function(el, evname, func) {
if (HTMLArea.is_ie) {
el.detachEvent("on" + evname, func);
} else {
el.removeEventListener(evname, func, true);
}
};
HTMLArea._removeEvents = function(el, evs, func) {
for (var i in evs) {
HTMLArea._removeEvent(el, evs[i], func);
}
};
HTMLArea._stopEvent = function(ev) {
if (HTMLArea.is_ie) {
ev.cancelBubble = true;
ev.returnValue = false;
} else {
ev.preventDefault();
ev.stopPropagation();
}
};
HTMLArea._removeClass = function(el, className) {
if (!(el && el.className)) {
return;
}
var cls = el.className.split(" ");
var ar = new Array();
for (var i = cls.length; i > 0;) {
if (cls[--i] != className) {
ar[ar.length] = cls[i];
}
}
el.className = ar.join(" ");
};
HTMLArea._addClass = function(el, className) {
// remove the class first, if already there
HTMLArea._removeClass(el, className);
el.className += " " + className;
};
HTMLArea._hasClass = function(el, className) {
if (!(el && el.className)) {
return false;
}
var cls = el.className.split(" ");
for (var i = cls.length; i > 0;) {
if (cls[--i] == className) {
return true;
}
}
return false;
};
HTMLArea._isBlockElement = function(el) {
var blockTags = " body form textarea fieldset ul ol dl li div " +
"p h1 h2 h3 h4 h5 h6 quote pre table thead " +
"tbody tfoot tr td iframe ";
return (blockTags.indexOf(" " + el.tagName.toLowerCase() + " ") != -1);
};
HTMLArea._needsClosingTag = function(el) {
var closingTags = " script style div span ";
return (closingTags.indexOf(" " + el.tagName.toLowerCase() + " ") != -1);
};
// Retrieves the HTML code from the given node. This is a replacement for
// getting innerHTML, using standard DOM calls.
HTMLArea.getHTML = function(root, outputRoot) {
function encode(str) {
// we don't need regexp for that, but.. so be it for now.
str = str.replace(/&/ig, "&");
str = str.replace(/</ig, "<");
str = str.replace(/>/ig, ">");
str = str.replace(/\"/ig, """);
return str;
};
var html = "";
switch (root.nodeType) {
case 1: // Node.ELEMENT_NODE
case 11: // Node.DOCUMENT_FRAGMENT_NODE
var closed;
var i;
if (outputRoot) {
closed = (!(root.hasChildNodes() || HTMLArea._needsClosingTag(root)));
html = "<" + root.tagName.toLowerCase();
var attrs = root.attributes;
for (i = 0; i < attrs.length; ++i) {
var a = attrs.item(i);
if (!a.specified) {
continue;
}
var name = a.name.toLowerCase();
if (name.substr(0, 4) == "_moz") {
// Mozilla reports some special tags
// here; we don't need them.
continue;
}
var value;
if (name != 'style') {
value = a.value;
} else { // IE fails to put style in attributes list
value = root.style.cssText.toLowerCase();
}
if (value.substr(0, 4) == "_moz") {
// Mozilla reports some special tags
// here; we don't need them.
continue;
}
html += " " + name + '="' + value + '"';
}
html += closed ? " />" : ">";
}
for (i = root.firstChild; i; i = i.nextSibling) {
html += HTMLArea.getHTML(i, true);
}
if (outputRoot && !closed) {
html += "</" + root.tagName.toLowerCase() + ">";
}
break;
case 3: // Node.TEXT_NODE
html = encode(root.data);
break;
case 8: // Node.COMMENT_NODE
html = "<!--" + root.data + "-->";
break; // skip comments, for now.
}
return html;
};
// creates a rgb-style color from a number
HTMLArea._makeColor = function(v) {
if (typeof v != "number") {
// already in rgb (hopefully); IE doesn't get here.
return v;
}
// IE sends number; convert to rgb.
var r = v & 0xFF;
var g = (v >> 8) & 0xFF;
var b = (v >> 16) & 0xFF;
return "rgb(" + r + "," + g + "," + b + ")";
};
// returns hexadecimal color representation from a number or a rgb-style color.
HTMLArea._colorToRgb = function(v) {
// returns the hex representation of one byte (2 digits)
function hex(d) {
return (d < 16) ? ("0" + d.toString(16)) : d.toString(16);
};
if (typeof v == "number") {
// we're talking to IE here
var r = v & 0xFF;
var g = (v >> 8) & 0xFF;
var b = (v >> 16) & 0xFF;
return "#" + hex(r) + hex(g) + hex(b);
}
if (v.substr(0, 3) == "rgb") {
// in rgb(...) form -- Mozilla
var re = /rgb\s*\(\s*([0-9]+)\s*,\s*([0-9]+)\s*,\s*([0-9]+)\s*\)/;
if (v.match(re)) {
var r = parseInt(RegExp.$1);
var g = parseInt(RegExp.$2);
var b = parseInt(RegExp.$3);
return "#" + hex(r) + hex(g) + hex(b);
}
// doesn't match RE?! maybe uses percentages or float numbers
// -- FIXME: not yet implemented.
return null;
}
if (v[0] == "#") {
// already hex rgb (hopefully :D )
return v;
}
// if everything else fails ;)
return null;
};
// modal dialogs for Mozilla (for IE we're using the showModalDialog() call).
// receives an URL to the popup dialog and a function that receives one value;
// this function will get called after the dialog is closed, with the return
// value of the dialog.
HTMLArea.prototype._popupDialog = function(url, action, init) {
Dialog(this.popupURL(url), action, init);
};
// paths
HTMLArea.prototype.imgURL = function(file) {
return this.config.editorURL + this.config.imgURL + file;
};
HTMLArea.prototype.popupURL = function(file) {
return this.config.editorURL + this.config.popupURL + file;
};
// EOF
// Local variables: //
// c-basic-offset:8 //
// indent-tabs-mode:t //
// End: //