element
+ * });
+ */
+HTMLArea.Config.prototype.registerButton = function(id, tooltip, image, textMode, action, context) {
+ var the_id;
+ if (typeof id == "string") {
+ the_id = id;
+ } else if (typeof id == "object") {
+ the_id = id.id;
+ } else {
+ alert("ERROR [HTMLArea.Config::registerButton]:\ninvalid arguments");
+ return false;
+ }
+ // check for existing id
+ if (typeof this.customSelects[the_id] != "undefined") {
+ alert("WARNING [HTMLArea.Config::registerDropdown]:\nA dropdown with the same ID already exists.");
+ }
+ if (typeof this.btnList[the_id] != "undefined") {
+ alert("WARNING [HTMLArea.Config::registerDropdown]:\nA button with the same ID already exists.");
+ }
+ switch (typeof id) {
+ case "string": this.btnList[id] = [ tooltip, image, textMode, action, context ]; break;
+ case "object": this.btnList[id.id] = [ id.tooltip, id.image, id.textMode, id.action, id.context ]; break;
+ }
+};
+
+/** The following helper function registers a dropdown box with the editor
+ * configuration. You still have to add it to the toolbar, same as with the
+ * buttons. Call it like this:
+ *
+ * FIXME: add example
+ */
+HTMLArea.Config.prototype.registerDropdown = function(object) {
+ // check for existing id
+ if (typeof this.customSelects[object.id] != "undefined") {
+ alert("WARNING [HTMLArea.Config::registerDropdown]:\nA dropdown with the same ID already exists.");
+ }
+ if (typeof this.btnList[object.id] != "undefined") {
+ alert("WARNING [HTMLArea.Config::registerDropdown]:\nA button with the same ID already exists.");
+ }
+ this.customSelects[object.id] = object;
+};
+
+/** Helper function: replace all TEXTAREA-s in the document with HTMLArea-s. */
+HTMLArea.replaceAll = function(config) {
+ var tas = document.getElementsByTagName("textarea");
+ for (var i = tas.length; i > 0; (new HTMLArea(tas[--i], config)).generate());
+};
+
+/** Helper function: replaces the TEXTAREA with the given ID with HTMLArea. */
+HTMLArea.replace = function(id, config) {
+ var ta = document.getElementById(id);
+ return ta ? (new HTMLArea(ta, config)).generate() : null;
+};
+
+// 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";
+ 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);
+ }; // END of function: newLine
+ // init first line
+ newLine();
+
+ // updates the state of a toolbar element. This function is member of
+ // a toolbar element object (unnamed objects created by createButton or
+ // createSelect functions below).
+ 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;
+ }
+ }; // END of function: setButtonStatus
+
+ // this function will handle creation of combo boxes. Receives as
+ // parameter the name of a button as defined in the toolBar config.
+ // This function is called from createButton, above, if the given "txt"
+ // doesn't match a button.
+ function createSelect(txt) {
+ var options = null;
+ var el = null;
+ var cmd = null;
+ var customSelects = editor.config.customSelects;
+ var context = null;
+ switch (txt) {
+ case "fontsize":
+ case "fontname":
+ case "formatblock":
+ // the following line retrieves the correct
+ // configuration option because the variable name
+ // inside the Config object is named the same as the
+ // button/select in the toolbar. For instance, if txt
+ // == "formatblock" we retrieve config.formatblock (or
+ // a different way to write it in JS is
+ // config["formatblock"].
+ options = editor.config[txt];
+ cmd = txt;
+ break;
+ default:
+ // try to fetch it from the list of registered selects
+ cmd = txt;
+ var dropdown = customSelects[cmd];
+ if (typeof dropdown != "undefined") {
+ options = dropdown.options;
+ context = dropdown.context;
+ } else {
+ alert("ERROR [createSelect]:\nCan't find the requested dropdown definition");
+ }
+ 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
+ context : context
+ };
+ 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;
+ }; // END of function: createSelect
+
+ // appends a new button to toolbar
+ function createButton(txt) {
+ // 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];
+ }
+ if (!el && btn) {
+ el = document.createElement("div");
+ el.title = btn[0];
+ 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[2], // enabled in text mode?
+ cmd : btn[3], // the command ID
+ state : setButtonStatus, // for changing state
+ context : btn[4] || null // enabled in a certain context?
+ };
+ 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");
+ obj.cmd(editor, obj.name, obj);
+ _stopEvent(is_ie ? window.event : ev);
+ }
+ });
+ var img = document.createElement("img");
+ img.src = editor.imgURL(btn[1]);
+ img.style.width = "18px";
+ img.style.height = "18px";
+ el.appendChild(img);
+ } else if (!el) {
+ el = createSelect(txt);
+ }
+ 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;
+ };
+
+ var first = true;
+ for (var i in this.config.toolbar) {
+ if (!first) {
+ createButton("linebreak");
+ } else {
+ first = false;
+ }
+ var group = this.config.toolbar[i];
+ for (var j in group) {
+ var code = group[j];
+ if (/^([IT])\[(.*?)\]/.test(code)) {
+ // special case, create text label
+ var l7ed = RegExp.$1 == "I"; // localized?
+ var label = RegExp.$2;
+ if (l7ed) {
+ label = HTMLArea.I18N.custom[label];
+ }
+ var tb_cell = document.createElement("td");
+ tb_row.appendChild(tb_cell);
+ tb_cell.className = "label";
+ tb_cell.innerHTML = label;
+ } else {
+ createButton(code);
+ }
+ }
+ }
+
+ this._htmlArea.appendChild(toolbar);
+};
+
+HTMLArea.prototype._createStatusBar = function() {
+ var div = document.createElement("div");
+ div.className = "statusBar";
+ this._htmlArea.appendChild(div);
+ this._statusBar = div;
+ div.appendChild(document.createTextNode(HTMLArea.I18N.msg["Path"] + ": "));
+ // creates a holder for the path view
+ div = document.createElement("span");
+ div.className = "statusBarTree";
+ this._statusBarTree = div;
+ this._statusBar.appendChild(div);
+ if (!this.config.statusBar) {
+ // disable it...
+ div.style.display = "none";
+ }
+};
+
+// 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
+ };
+ 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);
+
+ if (textarea.form) {
+ // we have a form, on submit get the HTMLArea content and
+ // update original textarea.
+ textarea.form.onsubmit = function() {
+ editor._textArea.value = editor.getHTML();
+ };
+ }
+
+ // add a handler for the "back/forward" case -- on body.unload we save
+ // the HTML content into the original textarea.
+ window.onunload = function() {
+ editor._textArea.value = editor.getHTML();
+ };
+
+ // creates & appends the toolbar
+ this._createToolbar();
+
+ // create the IFRAME
+ var iframe = document.createElement("iframe");
+ htmlarea.appendChild(iframe);
+
+ this._iframe = iframe;
+
+ // creates & appends the status bar, if the case
+ this._createStatusBar();
+
+ // remove the default border as it keeps us from computing correctly
+ // the sizes. (somebody tell me why doesn't this work in IE)
+
+ if (!HTMLArea.is_ie) {
+ iframe.style.borderWidth = "1px";
+ // iframe.frameBorder = "1";
+ // iframe.marginHeight = "0";
+ // iframe.marginWidth = "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);
+
+ if (!HTMLArea.is_ie) {
+ height -= 2;
+ width -= 2;
+ }
+
+ iframe.style.width = width + "px";
+ if (this.config.sizeIncludesToolbar) {
+ // substract toolbar height
+ height -= this._toolbar.offsetHeight;
+ height -= this._statusBar.offsetHeight;
+ }
+ if (height < 0) {
+ height = 0;
+ }
+ iframe.style.height = height + "px";
+
+ // the editor including the toolbar now have the same size as the
+ // original textarea.. which means that we need to reduce that a bit.
+ textarea.style.width = iframe.style.width;
+ textarea.style.height = iframe.style.height;
+
+ // 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) {
+ // Try again..
+ // FIXME: don't know what else to do here. Normally
+ // we'll never reach this point.
+ if (HTMLArea.is_gecko) {
+ setTimeout(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 = "\n";
+ html += "
\n";
+ html += "\n";
+ html += "\n";
+ html += "\n";
+ html += editor._textArea.value;
+ html += "\n";
+ 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();
+ };
+ setTimeout(initIframe, HTMLArea.is_gecko ? 10 : 0);
+};
+
+// Switches editor mode; parameter can be "textmode" or "wysiwyg". If no
+// parameter was passed this function toggles between modes.
+HTMLArea.prototype.setMode = function(mode) {
+ if (typeof mode == "undefined") {
+ mode = ((this._editMode == "textmode") ? "wysiwyg" : "textmode");
+ }
+ switch (mode) {
+ case "textmode":
+ this._textArea.value = this.getHTML();
+ this._iframe.style.display = "none";
+ this._textArea.style.display = "block";
+ if (this.config.statusBar) {
+ this._statusBar.innerHTML = HTMLArea.I18N.msg["TEXT_MODE"];
+ }
+ break;
+ case "wysiwyg":
+ if (HTMLArea.is_gecko) {
+ // disable design mode before changing innerHTML
+ this._doc.designMode = "off";
+ }
+ this._doc.body.innerHTML = this.getHTML();
+ this._iframe.style.display = "block";
+ this._textArea.style.display = "none";
+ if (HTMLArea.is_gecko) {
+ // we need to refresh that info for Moz-1.3a
+ this._doc.designMode = "on";
+ }
+ if (this.config.statusBar) {
+ this._statusBar.innerHTML = '';
+ this._statusBar.appendChild(document.createTextNode(HTMLArea.I18N.msg["Path"] + ": "));
+ this._statusBar.appendChild(this._statusBarTree);
+ }
+ break;
+ default:
+ alert("Mode <" + mode + "> not defined!");
+ return false;
+ }
+ this._editMode = mode;
+ this.focusEditor();
+};
+
+/***************************************************
+ * Category: PLUGINS
+ ***************************************************/
+
+// Create the specified plugin and register it with this HTMLArea
+HTMLArea.prototype.registerPlugin = function(pluginName) {
+ this.plugins[pluginName] = eval("new " + pluginName + "(this);");
+};
+
+// static function that loads the required plugin and lang file, based on the
+// language loaded already for HTMLArea. You better make sure that the plugin
+// _has_ that language, otherwise shit might happen ;-)
+HTMLArea.loadPlugin = function(pluginName) {
+ var editorurl = '';
+ if (typeof _editor_url != "undefined") {
+ editorurl = _editor_url + "/";
+ }
+ var dir = editorurl + "plugins/" + pluginName;
+ var plugin = pluginName.replace(/([a-z])([A-Z])([a-z])/g,
+ function (str, l1, l2, l3) {
+ return l1 + "-" + l2.toLowerCase() + l3;
+ }).toLowerCase() + ".js";
+ document.write("");
+ document.write("");
+};
+
+/***************************************************
+ * Category: EDITOR UTILITIES
+ ***************************************************/
+
+HTMLArea.prototype.forceRedraw = function() {
+ this._doc.body.style.visibility = "hidden";
+ this._doc.body.style.visibility = "visible";
+ // this._doc.body.innerHTML = this.getInnerHTML();
+};
+
+// focuses the iframe window. returns a reference to the editor document.
+HTMLArea.prototype.focusEditor = function() {
+ switch (this._editMode) {
+ case "wysiwyg" : this._iframe.contentWindow.focus(); break;
+ case "textmode": this._textArea.focus(); break;
+ default : alert("ERROR: mode " + this._editMode + " is not defined");
+ }
+ return this._doc;
+};
+
+// updates enabled/disable/active state of the toolbar elements
+HTMLArea.prototype.updateToolbar = function(noStatus) {
+ var doc = this._doc;
+ var text = (this._editMode == "textmode");
+ var ancestors = null;
+ if (!text) {
+ ancestors = this.getAllAncestors();
+ if (this.config.statusBar && !noStatus) {
+ this._statusBarTree.innerHTML = ''; // clear
+ for (var i = ancestors.length; --i >= 0;) {
+ var el = ancestors[i];
+ if (!el) {
+ // hell knows why we get here; this
+ // could be a classic example of why
+ // it's good to check for conditions
+ // that are impossible to happen ;-)
+ continue;
+ }
+ var a = document.createElement("a");
+ a.href = "#";
+ a.el = el;
+ a.editor = this;
+ a.onclick = function() {
+ this.blur();
+ this.editor.selectNodeContents(this.el);
+ this.editor.updateToolbar(true);
+ return false;
+ };
+ a.oncontextmenu = function() {
+ // TODO: add context menu here
+ this.blur();
+ var info = "Inline style:\n\n";
+ info += this.el.style.cssText.split(/;\s*/).join(";\n");
+ alert(info);
+ return false;
+ };
+ var txt = el.tagName.toLowerCase();
+ a.title = el.style.cssText;
+ if (el.id) {
+ txt += "#" + el.id;
+ }
+ if (el.className) {
+ txt += "." + el.className;
+ }
+ a.appendChild(document.createTextNode(txt));
+ this._statusBarTree.appendChild(a);
+ if (i != 0) {
+ this._statusBarTree.appendChild(document.createTextNode(String.fromCharCode(0xbb)));
+ }
+ }
+ }
+ }
+ for (var i in this._toolbarObjects) {
+ var btn = this._toolbarObjects[i];
+ var cmd = i;
+ var inContext = true;
+ if (btn.context && !text) {
+ inContext = false;
+ var context = btn.context;
+ var attrs = [];
+ if (/(.*)\[(.*?)\]/.test(context)) {
+ context = RegExp.$1;
+ attrs = RegExp.$2.split(",");
+ }
+ context = context.toLowerCase();
+ var match = (context == "*");
+ for (var k in ancestors) {
+ if (!ancestors[k]) {
+ // the impossible really happens.
+ continue;
+ }
+ if (match || (ancestors[k].tagName.toLowerCase() == context)) {
+ inContext = true;
+ for (var ka in attrs) {
+ if (!eval("ancestors[k]." + attrs[ka])) {
+ inContext = false;
+ break;
+ }
+ }
+ if (inContext) {
+ break;
+ }
+ }
+ }
+ }
+ btn.state("enabled", (!text || btn.text) && inContext);
+ if (typeof cmd == "function") {
+ continue;
+ }
+ // look-it-up in the custom dropdown boxes
+ var dropdown = this.config.customSelects[cmd];
+ if ((!text || btn.text) && (typeof dropdown != "undefined")) {
+ dropdown.refresh(this);
+ continue;
+ }
+ 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;
+ }
+ // HACK -- retrieve the config option for this
+ // combo box. We rely on the fact that the
+ // variable in config has the same name as
+ // button name in the toolbar.
+ var options = this.config[cmd];
+ 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(HTMLArea.is_ie ? "backcolor" : "hilitecolor"));
+ if (/transparent/i.test(backgroundColor)) {
+ // Mozilla
+ 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) {}
+ }
+ }
+};
+
+/** 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;
+ 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 = this._createRange();
+ range.setEnd(node, pos + toBeInserted.length);
+ range.setStart(node, pos + toBeInserted.length);
+ sel.addRange(range);
+ } else {
+ node = node.splitText(pos);
+ var selnode = toBeInserted;
+ if (toBeInserted.nodeType == 11 /* Node.DOCUMENT_FRAGMENT_NODE */) {
+ selnode = selnode.firstChild;
+ }
+ node.parentNode.insertBefore(toBeInserted, node);
+ this.selectNodeContents(selnode);
+ this.updateToolbar();
+ }
+ break;
+ case 1: // Node.ELEMENT_NODE
+ var selnode = toBeInserted;
+ if (toBeInserted.nodeType == 11 /* Node.DOCUMENT_FRAGMENT_NODE */) {
+ selnode = selnode.firstChild;
+ }
+ node.insertBefore(toBeInserted, node.childNodes[pos]);
+ this.selectNodeContents(selnode);
+ this.updateToolbar();
+ break;
+ }
+ } else {
+ return null; // this function not yet used for IE
+ }
+};
+
+// Returns the deepest node that contains both endpoints of the selection.
+HTMLArea.prototype.getParentElement = function() {
+ var sel = this._getSelection();
+ var range = this._createRange(sel);
+ if (HTMLArea.is_ie) {
+ return range.parentElement ? range.parentElement() : this._doc.body;
+ } else {
+ var p = range.commonAncestorContainer;
+ while (p.nodeType == 3) {
+ p = p.parentNode;
+ }
+ return p;
+ }
+};
+
+// Returns an array with all the ancestor nodes of the selection.
+HTMLArea.prototype.getAllAncestors = function() {
+ var p = this.getParentElement();
+ var a = [];
+ while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
+ a.push(p);
+ p = p.parentNode;
+ }
+ a.push(this._doc.body);
+ return a;
+};
+
+// Selects the contents inside the given node
+HTMLArea.prototype.selectNodeContents = function(node, pos) {
+ this.focusEditor();
+ this.forceRedraw();
+ var range;
+ var collapsed = (typeof pos != "undefined");
+ if (HTMLArea.is_ie) {
+ range = this._doc.body.createTextRange();
+ range.moveToElementText(node);
+ (collapsed) && range.collapse(pos);
+ range.select();
+ } else {
+ var sel = this._getSelection();
+ range = this._doc.createRange();
+ range.selectNodeContents(node);
+ (collapsed) && range.collapse(pos);
+ sel.removeAllRanges();
+ sel.addRange(range);
+ }
+};
+
+/** 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. FIXME: buggy! This function will be deprecated "soon".
+ */
+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 editor = this; // for nested functions
+ this._popupDialog("insert_image.html", function(param) {
+ if (!param) { // user must have pressed Cancel
+ return false;
+ }
+ var sel = editor._getSelection();
+ var range = editor._createRange(sel);
+ editor._doc.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);
+ // Mozilla likes to see something inside the cell.
+ (HTMLArea.is_gecko) && td.appendChild(doc.createElement("br"));
+ }
+ }
+ if (HTMLArea.is_ie) {
+ range.pasteHTML(table.outerHTML);
+ } else {
+ // insert the table
+ editor.insertNodeAtSelection(table);
+ }
+ return true;
+ }, null);
+};
+
+/***************************************************
+ * Category: EVENT HANDLERS
+ ***************************************************/
+
+// 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":
+ (HTMLArea.is_ie) && (value = "<" + value + ">");
+ this.execCommand(txt, false, value);
+ break;
+ default:
+ // try to look it up in the registered dropdowns
+ var dropdown = this.config.customSelects[txt];
+ if (typeof dropdown != "undefined") {
+ dropdown.action(this);
+ } else {
+ alert("FIXME: combo box " + txt + " not implemented");
+ }
+ }
+};
+
+// the execCommand function (intercepts some commands and replaces them with
+// our own implementation)
+HTMLArea.prototype.execCommand = function(cmdID, UI, param) {
+ var editor = this; // for nested functions
+ this.focusEditor();
+ switch (cmdID.toLowerCase()) {
+ case "htmlmode" : this.setMode(); break;
+ case "hilitecolor":
+ (HTMLArea.is_ie) && (cmdID = "backcolor");
+ case "forecolor":
+ this._popupDialog("select_color.html", function(color) {
+ if (color) { // selection not canceled
+ editor._doc.execCommand(cmdID, false, "#" + color);
+ }
+ }, HTMLArea._colorToRgb(this._doc.queryCommandValue(cmdID)));
+ break;
+ case "createlink":
+ if (HTMLArea.is_ie || !UI) {
+ this._doc.execCommand(cmdID, UI, param);
+ } else {
+ // browser is Mozilla & wants UI
+ var param;
+ if ((param = prompt("Enter URL"))) {
+ this._doc.execCommand(cmdID, false, param);
+ }
+ }
+ break;
+ case "popupeditor":
+ if (HTMLArea.is_ie) {
+ window.open(this.popupURL("fullscreen.html"), "ha_fullscreen",
+ "toolbar=no,location=no,directories=no,status=no,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 "inserttable": this._insertTable(); break;
+ case "insertimage": this._insertImage(); break;
+ case "about" : this._popupDialog("about.html", null, null); break;
+ case "showhelp" : window.open("reference.html", "ha_help"); break;
+ default: this._doc.execCommand(cmdID, UI, param);
+ }
+ this.updateToolbar();
+ return false;
+};
+
+/** 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': cmd = "bold"; break;
+ case 'i': cmd = "italic"; break;
+ case 'u': cmd = "underline"; break;
+ case 's': cmd = "strikethrough"; break;
+ case 'l': cmd = "justifyleft"; break;
+ case 'e': cmd = "justifycenter"; break;
+ case 'r': cmd = "justifyright"; break;
+ case 'j': cmd = "justifyfull"; break;
+
+ // headings
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '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("
");
+ HTMLArea._stopEvent(ev);
+ // }
+ break;
+ }
+ }
+ */
+ // update the toolbar state after some time
+ if (editor._timerToolbar) {
+ clearTimeout(editor._timerToolbar);
+ }
+ editor._timerToolbar = setTimeout(function() {
+ editor.updateToolbar();
+ editor._timerToolbar = null;
+ }, 50);
+};
+
+// retrieve the HTML
+HTMLArea.prototype.getHTML = function() {
+ switch (this._editMode) {
+ case "wysiwyg" : return HTMLArea.getHTML(this._doc.body, false);
+ case "textmode" : return this._textArea.value;
+ default : alert("Mode <" + mode + "> not defined!");
+ }
+ return false;
+};
+
+// retrieve the HTML (fastest version, but uses innerHTML)
+HTMLArea.prototype.getInnerHTML = function() {
+ switch (this._editMode) {
+ case "wysiwyg" : return this._doc.body.innerHTML;
+ case "textmode" : return this._textArea.value;
+ default : alert("Mode <" + mode + "> not defined!");
+ }
+ return false;
+};
+
+// completely change the HTML inside
+HTMLArea.prototype.setHTML = function(html) {
+ switch (this._editMode) {
+ case "wysiwyg" : this._doc.body.innerHTML = html; break;
+ case "textmode" : this._textArea.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() {
+ 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 (typeof sel != "undefined") {
+ 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 address ";
+ return (blockTags.indexOf(" " + el.tagName.toLowerCase() + " ") != -1);
+};
+
+HTMLArea.needsClosingTag = function(el) {
+ var closingTags = " script style div span tr td tbody table em strong font a ";
+ return (closingTags.indexOf(" " + el.tagName.toLowerCase() + " ") != -1);
+};
+
+// performs HTML encoding of some given string
+HTMLArea.htmlEncode = function(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(/\x22/ig, """);
+ // \x22 means '"' -- we use hex reprezentation so that we don't disturb
+ // JS compressors (well, at least mine fails.. ;)
+ return str;
+};
+
+// Retrieves the HTML code from the given node. This is a replacement for
+// getting innerHTML, using standard DOM calls.
+HTMLArea.getHTML = function(root, outputRoot) {
+ 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.nodeName.toLowerCase();
+ if (/_moz/.test(name)) {
+ // Mozilla reports some special tags
+ // here; we don't need them.
+ continue;
+ }
+ var value;
+ if (name != "style") {
+ // IE5.5 reports 25 when cellSpacing is
+ // 1; other values might be doomed too.
+ // For this reason we extract the
+ // values directly from the root node.
+ // I'm starting to HATE JavaScript
+ // development. Browser differences
+ // suck.
+ if (typeof root[a.nodeName] != "undefined") {
+ value = root[a.nodeName];
+ } else {
+ value = a.nodeValue;
+ }
+ } else { // IE fails to put style in attributes list
+ // FIXME: cssText reported by IE is UPPERCASE
+ value = root.style.cssText;
+ }
+ if (/_moz/.test(value)) {
+ // 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 = HTMLArea.htmlEncode(root.data);
+ break;
+ case 8: // Node.COMMENT_NODE
+ html = "";
+ 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, plugin) {
+ if (typeof plugin == "undefined") {
+ return this.config.editorURL + file;
+ } else {
+ return this.config.editorURL + "plugins/" + plugin + "/img/" + 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: //
Index: openacs-4/packages/acs-templating/www/resources/htmlarea/index.html
===================================================================
RCS file: /usr/local/cvsroot/openacs-4/packages/acs-templating/www/resources/htmlarea/index.html,v
diff -u
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ openacs-4/packages/acs-templating/www/resources/htmlarea/index.html 4 Mar 2004 18:32:10 -0000 1.1
@@ -0,0 +1,190 @@
+
+
+
+ HTMLArea -- the free, customizable online editor
+
+
+
+
+
+
+ HTMLArea -- the free
customizable online editor
+
+
+ HTMLArea is a free, customizable online editor. It works inside your
+ browser. It uses a non-standard feature implemented in Internet
+ Explorer 5.5 or better for Windows and Mozilla 1.3 or better (any
+ platform), therefore it will only work in one of these browsers.
+
+
+
+ HTMLArea is copyright InteractiveTools.com and
+ released under a BSD-style license. HTMLArea is created and developed
+ upto version 2.03 by InteractiveTools.com. Version 3.0 developed by
+ Mihai Bazon for
+ InteractiveTools. It contains code sponsored by other companies as
+ well.
+
+
+ Online demos
+
+
+
+ Installation
+
+
+ Installation is (or should be) easy. You need to unpack the ZIP file
+ in a directory accessible through your webserver. Supposing you
+ unpack in your DocumentRoot and your DocumentRoot is
+ /var/www/html as in a standard RedHat installation, you need
+ to acomplish the following steps: (the example is for a Unix-like
+ operating system)
+
+
+
+cd /var/www/html
+unzip /path/to/archive/HTMLArea-3.0-beta.zip
+mv HTMLArea-3.0-beta htmlarea
+find htmlarea/ -type f -exec chmod 644 {} \;
+find htmlarea/ -type d -exec chmod 755 {} \;
+find htmlarea/ -name "*.cgi" -exec chmod 755 {} \;
+
+
+ Notes. You may chose to symlink "htmlarea" to "HTMLArea-3.0-beta", in which case your server needs to be configured to
+ "FollowSymLinks". You need to make sure that *.cgi files are
+ interpreted as CGI scripts. If you want to use the SpellChecker
+ plugin you need to have a recent version of Perl installed (I
+ recommend 5.8.0) on the server, and the module Text::Aspell, available
+ from CPAN. More info in "plugins/SpellChecker/readme-tech.html".
+
+
+ About how to setup your pages to use the editor, please read the
+ [outdated yet generally valid] documentation.
+
+ Status and links
+
+ HTMLArea has reached version 3.0. As of this version, it
+ supports:
+
+
+
+ - Customizable toolbar
+
+ - Easy internationalization
+
+ - Plugin-based infrastructure
+
+ - Delivers W3-compliant HTML (with few exceptions)
+
+ - Has a subset of Microsoft Word's keyboard shortcuts
+
+ - Full-screen editor
+
+ - Advanced table operations (by external plugin
+ "TableOperations")
+
+ - Spell checker (by external plugin "SpellChecker")
+
+ - probably more... ;-)
+
+
+
+ We have a project page
+ at SourceForge.net. There you can
+ also find out how
+ to retrieve the code from CVS, or you can browse
+ the CVS online. We also have a bug
+ system, a patch
+ tracking system and a feature
+ request page.
+
+ We invite you to say everything you want about HTMLArea on the
+ forums at InteractiveTools.com. There you should also find the
+ latest news.
+
+ Sometimes I post news about the latest developments on my personal homepage.
+
+ "It doesn't work, what's wrong?"
+
+ If it doesn't work, you have several options:
+
+
+
+ - Post a message to the forum. Describe your problem in as much
+ detail as possible. Include errors you might find in the JavaScript
+ console (if you are a Mozilla user), or errors displayed by IE (though
+ they're most of the times useless).
+
+ - If you're positive that you discovered a bug in HTMLArea then feel
+ free to fill a bug report in our bug system. If you have the time you
+ should check to see if a similar bug was reported or not; it might be
+ fixed already in the CVS ;-) If you're positive that a similar bug was
+ not yet reported, do fill a bug report and please include as much
+ detail as possible, such as your browser, OS, errors from JavaScript
+ console, etc.
+
+ - If you want a new feature to be implemented, post it on the
+ features request and someone will hopefully take care of it.
+
+
+
+ You can contact me directly
+ only if you want to pay me for implementing custom features to
+ HTMLArea. If you want to sponsor these features (that is, allow them to
+ get back into the public HTMLArea distribution) I'll be cheaper. ;-)
+
+
+ Mihai Bazon
+
+
+Last modified on Tue Aug 12 00:23:26 2003
+
+
+
+
+
+
Index: openacs-4/packages/acs-templating/www/resources/htmlarea/license.txt
===================================================================
RCS file: /usr/local/cvsroot/openacs-4/packages/acs-templating/www/resources/htmlarea/license.txt,v
diff -u
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ openacs-4/packages/acs-templating/www/resources/htmlarea/license.txt 4 Mar 2004 18:32:10 -0000 1.1
@@ -0,0 +1,13 @@
+htmlArea License (based on BSD license)
+Copyright (c) 2002, interactivetools.com, inc.
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
+
+1) Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
+
+2) Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
+
+3) Neither the name of interactivetools.com, inc. nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
Index: openacs-4/packages/acs-templating/www/resources/htmlarea/popupdiv.js
===================================================================
RCS file: /usr/local/cvsroot/openacs-4/packages/acs-templating/www/resources/htmlarea/popupdiv.js,v
diff -u
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ openacs-4/packages/acs-templating/www/resources/htmlarea/popupdiv.js 4 Mar 2004 18:32:10 -0000 1.1
@@ -0,0 +1,369 @@
+/** This file is derived from PopupDiv, developed by Mihai Bazon for
+ * SamWare.net. Modifications were needed to make it usable in HTMLArea.
+ * HTMLArea is a free WYSIWYG online HTML editor from InteractiveTools.com.
+ *
+ * This file does not function standalone. It is dependent of global functions
+ * defined in HTMLArea-3.0 (htmlarea.js).
+ *
+ * Please see file htmlarea.js for further details.
+ **/
+
+var is_ie = ( (navigator.userAgent.toLowerCase().indexOf("msie") != -1) &&
+ (navigator.userAgent.toLowerCase().indexOf("opera") == -1) );
+var is_compat = (document.compatMode == "BackCompat");
+
+function PopupDiv(editor, titleText, handler, initFunction) {
+ var self = this;
+
+ this.editor = editor;
+ this.doc = editor._mdoc;
+ this.handler = handler;
+
+ var el = this.doc.createElement("div");
+ el.className = "content";
+
+ var popup = this.doc.createElement("div");
+ popup.className = "dialog popupdiv";
+ this.element = popup;
+ var s = popup.style;
+ s.position = "absolute";
+ s.left = "0px";
+ s.top = "0px";
+
+ var title = this.doc.createElement("div");
+ title.className = "title";
+ this.title = title;
+ popup.appendChild(title);
+
+ HTMLArea._addEvent(title, "mousedown", function(ev) {
+ self._dragStart(is_ie ? window.event : ev);
+ });
+
+ var button = this.doc.createElement("div");
+ button.className = "button";
+ title.appendChild(button);
+ button.innerHTML = "×";
+ title.appendChild(this.doc.createTextNode(titleText));
+ this.titleText = titleText;
+
+ button.onmouseover = function() {
+ this.className += " button-hilite";
+ };
+ button.onmouseout = function() {
+ this.className = this.className.replace(/\s*button-hilite\s*/g, " ");
+ };
+ button.onclick = function() {
+ this.className = this.className.replace(/\s*button-hilite\s*/g, " ");
+ self.close();
+ };
+
+ popup.appendChild(el);
+ this.content = el;
+
+ this.doc.body.appendChild(popup);
+
+ this.dragging = false;
+ this.onShow = null;
+ this.onClose = null;
+ this.modal = false;
+
+ initFunction(this);
+};
+
+PopupDiv.currentPopup = null;
+
+PopupDiv.prototype.showAtElement = function(el, mode) {
+ this.defaultSize();
+ var pos, ew, eh;
+ var popup = this.element;
+ popup.style.display = "block";
+ var w = popup.offsetWidth;
+ var h = popup.offsetHeight;
+ popup.style.display = "none";
+ if (el != window) {
+ pos = PopupDiv.getAbsolutePos(el);
+ ew = el.offsetWidth;
+ eh = el.offsetHeight;
+ } else {
+ pos = {x:0, y:0};
+ var size = PopupDiv.getWindowSize();
+ ew = size.x;
+ eh = size.y;
+ }
+ var FX = false, FY = false;
+ if (mode.indexOf("l") != -1) {
+ pos.x -= w;
+ FX = true;
+ }
+ if (mode.indexOf("r") != -1) {
+ pos.x += ew;
+ FX = true;
+ }
+ if (mode.indexOf("t") != -1) {
+ pos.y -= h;
+ FY = true;
+ }
+ if (mode.indexOf("b") != -1) {
+ pos.y += eh;
+ FY = true;
+ }
+ if (mode.indexOf("c") != -1) {
+ FX || (pos.x += Math.round((ew - w) / 2));
+ FY || (pos.y += Math.round((eh - h) / 2));
+ }
+ this.showAt(pos.x, pos.y);
+};
+
+PopupDiv.prototype.defaultSize = function() {
+ var s = this.element.style;
+ var cs = this.element.currentStyle;
+ var addX = (is_ie && is_compat) ? (parseInt(cs.borderLeftWidth) +
+ parseInt(cs.borderRightWidth) +
+ parseInt(cs.paddingLeft) +
+ parseInt(cs.paddingRight)) : 0;
+ var addY = (is_ie && is_compat) ? (parseInt(cs.borderTopWidth) +
+ parseInt(cs.borderBottomWidth) +
+ parseInt(cs.paddingTop) +
+ parseInt(cs.paddingBottom)) : 0;
+ s.display = "block";
+ s.width = (this.content.offsetWidth + addX) + "px";
+ s.height = (this.content.offsetHeight + this.title.offsetHeight) + "px";
+ s.display = "none";
+};
+
+PopupDiv.prototype.showAt = function(x, y) {
+ this.defaultSize();
+ var s = this.element.style;
+ s.display = "block";
+ s.left = x + "px";
+ s.top = y + "px";
+ this.hideShowCovered();
+
+ PopupDiv.currentPopup = this;
+ HTMLArea._addEvents(this.doc.body, ["mousedown", "click"], PopupDiv.checkPopup);
+ HTMLArea._addEvents(this.editor._doc.body, ["mousedown", "click"], PopupDiv.checkPopup);
+ if (is_ie && this.modal) {
+ this.doc.body.setCapture(false);
+ this.doc.body.onlosecapture = function() {
+ (PopupDiv.currentPopup) && (this.doc.body.setCapture(false));
+ };
+ }
+ window.event && HTMLArea._stopEvent(window.event);
+
+ if (typeof this.onShow == "function") {
+ this.onShow();
+ } else if (typeof this.onShow == "string") {
+ eval(this.onShow);
+ }
+
+ var field = this.element.getElementsByTagName("input")[0];
+ if (!field) {
+ field = this.element.getElementsByTagName("select")[0];
+ }
+ if (!field) {
+ field = this.element.getElementsByTagName("textarea")[0];
+ }
+ if (field) {
+ field.focus();
+ }
+};
+
+PopupDiv.prototype.close = function() {
+ this.element.style.display = "none";
+ PopupDiv.currentPopup = null;
+ this.hideShowCovered();
+ HTMLArea._removeEvents(this.doc.body, ["mousedown", "click"], PopupDiv.checkPopup);
+ HTMLArea._removeEvents(this.editor._doc.body, ["mousedown", "click"], PopupDiv.checkPopup);
+ is_ie && this.modal && this.doc.body.releaseCapture();
+ if (typeof this.onClose == "function") {
+ this.onClose();
+ } else if (typeof this.onClose == "string") {
+ eval(this.onClose);
+ }
+ this.element.parentNode.removeChild(this.element);
+};
+
+PopupDiv.prototype.getForm = function() {
+ var forms = this.content.getElementsByTagName("form");
+ return (forms.length > 0) ? forms[0] : null;
+};
+
+PopupDiv.prototype.callHandler = function() {
+ var tags = ["input", "textarea", "select"];
+ var params = new Object();
+ for (var ti in tags) {
+ var tag = tags[ti];
+ var els = this.content.getElementsByTagName(tag);
+ for (var j = 0; j < els.length; ++j) {
+ var el = els[j];
+ params[el.name] = el.value;
+ }
+ }
+ this.handler(this, params);
+ return false;
+};
+
+PopupDiv.getAbsolutePos = function(el) {
+ var r = { x: el.offsetLeft, y: el.offsetTop };
+ if (el.offsetParent) {
+ var tmp = PopupDiv.getAbsolutePos(el.offsetParent);
+ r.x += tmp.x;
+ r.y += tmp.y;
+ }
+ return r;
+};
+
+PopupDiv.getWindowSize = function() {
+ if (window.innerHeight) {
+ return { y: window.innerHeight, x: window.innerWidth };
+ }
+ if (this.doc.body.clientHeight) {
+ return { y: this.doc.body.clientHeight, x: this.doc.body.clientWidth };
+ }
+ return { y: this.doc.documentElement.clientHeight, x: this.doc.documentElement.clientWidth };
+};
+
+PopupDiv.prototype.hideShowCovered = function () {
+ var self = this;
+ function isContained(el) {
+ while (el) {
+ if (el == self.element) {
+ return true;
+ }
+ el = el.parentNode;
+ }
+ return false;
+ };
+ var tags = new Array("applet", "select");
+ var el = this.element;
+
+ var p = PopupDiv.getAbsolutePos(el);
+ var EX1 = p.x;
+ var EX2 = el.offsetWidth + EX1;
+ var EY1 = p.y;
+ var EY2 = el.offsetHeight + EY1;
+
+ if (el.style.display == "none") {
+ EX1 = EX2 = EY1 = EY2 = 0;
+ }
+
+ for (var k = tags.length; k > 0; ) {
+ var ar = this.doc.getElementsByTagName(tags[--k]);
+ var cc = null;
+
+ for (var i = ar.length; i > 0;) {
+ cc = ar[--i];
+ if (isContained(cc)) {
+ cc.style.visibility = "visible";
+ continue;
+ }
+
+ p = PopupDiv.getAbsolutePos(cc);
+ var CX1 = p.x;
+ var CX2 = cc.offsetWidth + CX1;
+ var CY1 = p.y;
+ var CY2 = cc.offsetHeight + CY1;
+
+ if ((CX1 > EX2) || (CX2 < EX1) || (CY1 > EY2) || (CY2 < EY1)) {
+ cc.style.visibility = "visible";
+ } else {
+ cc.style.visibility = "hidden";
+ }
+ }
+ }
+};
+
+PopupDiv.prototype._dragStart = function (ev) {
+ if (this.dragging) {
+ return false;
+ }
+ this.dragging = true;
+ PopupDiv.currentPopup = this;
+ var posX = ev.clientX;
+ var posY = ev.clientY;
+ if (is_ie) {
+ posY += this.doc.body.scrollTop;
+ posX += this.doc.body.scrollLeft;
+ } else {
+ posY += window.scrollY;
+ posX += window.scrollX;
+ }
+ var st = this.element.style;
+ this.xOffs = posX - parseInt(st.left);
+ this.yOffs = posY - parseInt(st.top);
+ HTMLArea._addEvent(this.doc, "mousemove", PopupDiv.dragIt);
+ HTMLArea._addEvent(this.doc, "mouseover", HTMLArea._stopEvent);
+ HTMLArea._addEvent(this.doc, "mouseup", PopupDiv.dragEnd);
+ HTMLArea._stopEvent(ev);
+};
+
+PopupDiv.dragIt = function (ev) {
+ var popup = PopupDiv.currentPopup;
+ if (!(popup && popup.dragging)) {
+ return false;
+ }
+ is_ie && (ev = window.event);
+ var posX = ev.clientX;
+ var posY = ev.clientY;
+ if (is_ie) {
+ posY += this.doc.body.scrollTop;
+ posX += this.doc.body.scrollLeft;
+ } else {
+ posY += window.scrollY;
+ posX += window.scrollX;
+ }
+ popup.hideShowCovered();
+ var st = popup.element.style;
+ st.left = (posX - popup.xOffs) + "px";
+ st.top = (posY - popup.yOffs) + "px";
+ HTMLArea._stopEvent(ev);
+};
+
+PopupDiv.dragEnd = function () {
+ var popup = PopupDiv.currentPopup;
+ if (!popup) {
+ return false;
+ }
+ popup.dragging = false;
+ HTMLArea._removeEvent(popup.doc, "mouseup", PopupDiv.dragEnd);
+ HTMLArea._removeEvent(popup.doc, "mouseover", HTMLArea._stopEvent);
+ HTMLArea._removeEvent(popup.doc, "mousemove", PopupDiv.dragIt);
+ popup.hideShowCovered();
+};
+
+PopupDiv.checkPopup = function (ev) {
+ is_ie && (ev = window.event);
+ var el = is_ie ? ev.srcElement : ev.target;
+ var cp = PopupDiv.currentPopup;
+ for (; (el != null) && (el != cp.element); el = el.parentNode);
+ if (el == null) {
+ cp.modal || ev.type == "mouseover" || cp.close();
+ HTMLArea._stopEvent(ev);
+ }
+};
+
+PopupDiv.prototype.addButtons = function() {
+ var self = this;
+ var div = this.doc.createElement("div");
+ this.content.appendChild(div);
+ div.className = "buttons";
+ for (var i = 0; i < arguments.length; ++i) {
+ var btn = arguments[i];
+ var button = this.doc.createElement("button");
+ div.appendChild(button);
+ button.innerHTML = HTMLArea.I18N.buttons[btn];
+ switch (btn) {
+ case "ok":
+ button.onclick = function() {
+ self.callHandler();
+ self.close();
+ };
+ break;
+ case "cancel":
+ button.onclick = function() {
+ self.close();
+ };
+ break;
+ }
+ }
+};
Index: openacs-4/packages/acs-templating/www/resources/htmlarea/popupwin.js
===================================================================
RCS file: /usr/local/cvsroot/openacs-4/packages/acs-templating/www/resources/htmlarea/popupwin.js,v
diff -u
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ openacs-4/packages/acs-templating/www/resources/htmlarea/popupwin.js 4 Mar 2004 18:32:10 -0000 1.1
@@ -0,0 +1,125 @@
+function PopupWin(editor, title, handler, initFunction) {
+ this.editor = editor;
+ this.handler = handler;
+ var dlg = window.open("", "__ha_dialog",
+ "toolbar=no,menubar=no,personalbar=no,width=600,height=600," +
+ "scrollbars=no,resizable=no");
+ this.window = dlg;
+ var doc = dlg.document;
+ this.doc = doc;
+ var self = this;
+
+ var base = document.baseURI || document.URL;
+ if (base && base.match(/(.*)\/([^\/]+)/)) {
+ base = RegExp.$1 + "/";
+ }
+ this.baseURL = base;
+
+ doc.open();
+ var html = "" + title + "\n";
+ // html += "\n";
+ html += "\n";
+ html += "";
+ doc.write(html);
+ doc.close();
+
+ // sometimes I Hate Mozilla... ;-(
+ function init2() {
+ var body = doc.body;
+ if (!body) {
+ setTimeout(init2, 25);
+ return false;
+ }
+ dlg.title = title;
+ doc.documentElement.style.padding = "0px";
+ doc.documentElement.style.margin = "0px";
+ var content = doc.createElement("div");
+ content.className = "content";
+ self.content = content;
+ body.appendChild(content);
+ self.element = body;
+ initFunction(self);
+ dlg.focus();
+ };
+ init2();
+};
+
+PopupWin.prototype.callHandler = function() {
+ var tags = ["input", "textarea", "select"];
+ var params = new Object();
+ for (var ti in tags) {
+ var tag = tags[ti];
+ var els = this.content.getElementsByTagName(tag);
+ for (var j = 0; j < els.length; ++j) {
+ var el = els[j];
+ var val = el.value;
+ if (el.tagName.toLowerCase() == "input") {
+ if (el.type == "checkbox") {
+ val = el.checked;
+ }
+ }
+ params[el.name] = val;
+ }
+ }
+ this.handler(this, params);
+ return false;
+};
+
+PopupWin.prototype.close = function() {
+ this.window.close();
+};
+
+PopupWin.prototype.addButtons = function() {
+ var self = this;
+ var div = this.doc.createElement("div");
+ this.content.appendChild(div);
+ div.className = "buttons";
+ for (var i = 0; i < arguments.length; ++i) {
+ var btn = arguments[i];
+ var button = this.doc.createElement("button");
+ div.appendChild(button);
+ button.innerHTML = HTMLArea.I18N.buttons[btn];
+ switch (btn) {
+ case "ok":
+ button.onclick = function() {
+ self.callHandler();
+ self.close();
+ return false;
+ };
+ break;
+ case "cancel":
+ button.onclick = function() {
+ self.close();
+ return false;
+ };
+ break;
+ }
+ }
+};
+
+PopupWin.prototype.showAtElement = function() {
+ var self = this;
+ // Mozilla needs some time to realize what's goin' on..
+ setTimeout(function() {
+ var w = self.content.offsetWidth + 4;
+ var h = self.content.offsetHeight + 4;
+ // size to content -- that's fuckin' buggy in all fuckin' browsers!!!
+ // so that we set a larger size for the dialog window and then center
+ // the element inside... phuck!
+
+ // center...
+ var el = self.content;
+ var s = el.style;
+ // s.width = el.offsetWidth + "px";
+ // s.height = el.offsetHeight + "px";
+ s.position = "absolute";
+ s.left = (w - el.offsetWidth) / 2 + "px";
+ s.top = (h - el.offsetHeight) / 2 + "px";
+ if (HTMLArea.is_gecko) {
+ self.window.innerWidth = w;
+ self.window.innerHeight = h;
+ } else {
+ self.window.resizeTo(w + 8, h + 35);
+ }
+ }, 25);
+};
Index: openacs-4/packages/acs-templating/www/resources/htmlarea/reference.html
===================================================================
RCS file: /usr/local/cvsroot/openacs-4/packages/acs-templating/www/resources/htmlarea/reference.html,v
diff -u
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ openacs-4/packages/acs-templating/www/resources/htmlarea/reference.html 4 Mar 2004 18:32:10 -0000 1.1
@@ -0,0 +1,522 @@
+
+
+HTMLArea-3.0 Reference
+
+
+
+
+
+
+
+
+
+
+
+
+HTMLArea-3.0 Documentation
+
+
+
+ This documentation contains valid information, but is outdated in the
+ terms that it does not covers all the features of HTMLArea. A new
+ documentation project will be started, based on LaTeX.
+
+
+
+
+Introduction
+
+What is HTMLArea?
+
+HTMLArea is a free WYSIWYG editor replacement for <textarea>
+fields. By adding a few simple lines of JavaScript code to your web page
+you can replace a regular textarea with a rich text editor that lets your
+users do the following:
+
+
+ - Format text to be bold, italicized, or underlined.
+ - Change the face, size, style and color.
+ - Left, center, or right-justify paragraphs.
+ - Make bulleted or numbered lists.
+ - Indent or un-indent paragraphs.
+ - Insert a horizontal line.
+ - Insert hyperlinks and images.
+ - View the raw HTML source of what they're editing.
+ - and much more...
+
+
+Some of the interesting features of HTMLArea that set's it apart from
+other web based WYSIWYG editors are as follows:
+
+
+ - It's lightweight, fast loading and can transform a regular textarea
+ into a rich-text editor with a single line of JavaScript.
+ - Generates clean, valid HTML.
+ - It's 100% backwards compatible with older or non-supported browsers
+ (they get the original textarea field).
+ - It's free and can be incorporated into any free or commercial
+ program.
+ - It works with any server-side languages (ASP, PHP, Perl, Java,
+ etc).
+ - It's written in JavaScript and can be easily viewed, modified or
+ extended.
+ - It remembers entered content when a user navigates away and then hits
+ "back" in their browser.
+ - Since it replaces existing textareas it doesn't require a lot of code
+ to add it to your pages (just one line).
+ - Did we mention it was free? ;-)
+
+
+Is it really free? What's the catch?
+
+Yes! It's really free. You can use it, modify it, distribute it with your
+software, or do just about anything you like with it.
+
+What are the browser requirements?
+
+HTMLArea requires Internet Explorer >= 5.5
+(Windows only), or Mozilla >= 1.3-Beta on any platform.
+Any browser based on Gecko will
+also work, provided that Gecko version is at least the one included in
+Mozilla-1.3-Beta (for example, Galeon-1.2.8). However, it is backwards
+compatible with other browsers. They will get a regular textarea field
+instead of a WYSIWYG editor.
+
+Can I see an example of what it looks like?
+
+Just make sure you're using one of the browsers mentioned above and see
+below.
+
+
+
+Where can I find out more info, download the latest version and talk to
+other HTMLArea users?
+
+You can find out more about HTMLArea and download the latest version on
+the HTMLArea
+homepage and you can talk to other HTMLArea users and post any comments
+or suggestions you have in the HTMLArea forum.
+
+Keyboard shortcuts
+
+The editor provides the following key combinations:
+
+
+ - CTRL-A -- select all
+ - CTRL-B -- bold
+ - CTRL-I -- italic
+ - CTRL-U -- underline
+ - CTRL-S -- strikethrough
+ - CTRL-L -- justify left
+ - CTRL-E -- justify center
+ - CTRL-R -- justify right
+ - CTRL-J -- justify full
+ - CTRL-1 .. CTRL-6 -- headings (<h1> .. <h6>)
+
+
+Installation
+
+How do I add HTMLArea to my web page?
+
+It's easy. First you need to upload HTMLArea files to your website.
+Just follow these steps.
+
+
+ - Download the latest version from the htmlArea
+ homepage.
+ - Unzip the files onto your local computer (making sure to maintain the
+ directory structure contained in the zip).
+ - Create a new folder on your website called /htmlarea/ (make sure it's
+ NOT inside the cgi-bin).
+ - Transfer all the HTMLArea files from your local computer into the
+ /htmlarea/ folder on your website.
+ - Open the example page /htmlarea/example.html with your browser to make
+ sure everything works.
+
+
+Once htmlArea is on your website all you need to do is add some
+JavaScript to any pages that you want to add WYSIWYG editors to. Here's how
+to do that.
+
+
+
+ - Include the "htmlarea.js" script:
+
<script type="text/javascript" src="/htmlarea/htmlarea.js"></script>
+
+
+ - If you are using popup dialogs, i.e. for insert table, insert image,
+ select color, then you need to include the "dialog.js" file. This is
+ recommended anyway.
+
<script type="text/javascript" src="/htmlarea/dialog.js"></script>
+
+
+ - Include the corresponding language definition file. Note:
+ internationalization is available only since version 3.0. Check the files
+ containing "lang" in the distribution ZIP. If your preferred language is
+ not there yet and you decide to write it, please consider sending it to
+ us so that it gets included in the next release.
+
<script type="text/javascript" src="/htmlarea/lang/en.js"></script>
+
+ - Include the stylesheet (be sure to put this inside the HEAD tag):
+
<style type="text/css">@import url(/htmlarea/htmlarea.css)</style>
+
+
+ If you want to change all your <textarea>-s into
+ HTMLArea-s then you can use the simplest way to create HTMLArea:
+ <script type="text/javascript" defer="1">
+ HTMLArea.replaceAll();
+</script>
+ Note: you can also add the
+ HTMLArea.replaceAll()
code to the onload
+ event handler for the body
element, if you find it more appropriate.
+
+ A different approach, if you have more than one textarea and only want
+ to change one of them, is to use HTMLArea.replace("id")
--
+ pass the id
of your textarea. Do not use the
+ name
attribute anymore, it's not a standard solution!
+
+
+
+I want to change the editor settings, how do I do that?
+
+While it's true that all you need is one line of JavaScript to create an
+htmlArea WYSIWYG editor, you can also specify more config settings in the
+code to control how the editor works and looks. Here's an example of some of
+the available settings:
+
+var config = new HTMLArea.Config(); config.width = '90%';
+config.height = '200px';
+
+config.pageStyle =
+ 'body { background-color: yellow; color: black; font-family: verdana,sans-serif } ' +
+ 'p { font-width: bold; } ';
+
+HTMLArea.replace('id', config);
+
+Important: It's recommended that you add
+custom features and configuration to a separate file. This will ensure you
+that when we release a new official version of HTMLArea you'll have no
+trouble upgrading it.
+
+How do I customize the toolbar?
+
+Using the configuration object introduced above allows you to completely
+control what the toolbar contains. Following is an example of a one-line,
+customized toolbar, much simpler than the default one:
+
+var config = new HTMLArea.Config();
+config.toolbar = [
+ ['fontname', 'space',
+ 'fontsize', 'space',
+ 'formatblock', 'space',
+ 'bold', 'italic', 'underline']
+];
+HTMLArea.replace('id', config);
+
+The toolbar is an Array of Array objects. Each array in the toolbar
+defines a new line. The default toolbar looks like this:
+
+config.toolbar = [
+[ "fontname", "space",
+ "fontsize", "space",
+ "formatblock", "space",
+ "bold", "italic", "underline", "separator",
+ "strikethrough", "subscript", "superscript", "separator",
+ "copy", "cut", "paste", "space", "undo", "redo" ],
+
+[ "justifyleft", "justifycenter", "justifyright", "justifyfull", "separator",
+ "insertorderedlist", "insertunorderedlist", "outdent", "indent", "separator",
+ "forecolor", "hilitecolor", "textindicator", "separator",
+ "inserthorizontalrule", "createlink", "insertimage", "inserttable", "htmlmode", "separator",
+ "popupeditor", "separator", "showhelp", "about" ]
+];
+
+Except three strings, all others in the examples above need to be defined
+in the config.btnList
object (detailed a bit later in this
+document). The three exceptions are: 'space', 'separator' and 'linebreak'.
+These three have the following meaning, and need not be present in
+btnList
:
+
+
+ - 'space' -- Inserts a space of 5 pixels (the width is configurable by external
+ CSS) at the current
+ position in the toolbar.
+ - 'separator' -- Inserts a small vertical separator, for visually grouping related
+ buttons.
+ - 'linebreak' -- Starts a new line in the toolbar. Subsequent controls will be
+ inserted on the new line.
+
+
+Important: It's recommended that you add
+custom features and configuration to a separate file. This will ensure you
+that when we release a new official version of HTMLArea you'll have no
+trouble upgrading it.
+
+How do I create custom buttons?
+
+By design, the toolbar is easily extensible. For adding a custom button
+one needs to follow two steps.
+
+1. Register the button in config.btnList
.
+
+For each button in the toolbar, HTMLArea needs to know the following
+information:
+
+ - a name for it (we call it the ID of the button);
+ - the path to an image to be displayed in the toolbar;
+ - a tooltip for it;
+ - whether the button is enabled or not in text mode;
+ - what to do when the button is clicked;
+
+You need to provide all this information for registering a new button
+too. The button ID can be any string identifier and it's used when
+defining the toolbar, as you saw above. We recommend starting
+it with "my-" so that it won't clash with the standard ID-s (those from
+the default toolbar).
+
+Register button example #1
+
+var config = new HTMLArea.Config();
+config.registerButton("my-hilite", "Highlight text", "my-hilite.gif", false,
+ function(editor, id) {
+ editor.surroundHTML('<span class="hilite">', '</span>');
+ }
+);
+
+An alternate way of calling registerButton is exemplified above. Though
+the code might be a little bit larger, using this form makes your code more
+maintainable. It doesn't even needs comments as it's pretty clear.
+
+Register button example #2
+
+var config = new HTMLArea.Config();
+config.registerButton({
+ id : "my-hilite",
+ tooltip : "Highlight text",
+ image : "my-hilite.gif",
+ textMode : false,
+ action : function(editor, id) {
+ editor.surroundHTML('<span class="hilite">', '</span>');
+ }
+});
+
+You might notice that the "action" function receives two parameters:
+editor and id. In the examples above we only used the
+editor parameter. But it could be helpful for you to understand
+both:
+
+
+ - editor is a reference to the HTMLArea object. Since our entire
+ code now has an OOP-like
+ design, you need to have a reference to
+ the editor object in order to do things with it. In previous versions of
+ HTMLArea, in order to identify the object an ID was used -- the ID of the
+ HTML element. In this version ID-s are no longer necessary.
+
+ - id is the button ID. Wondering why is this useful? Well, you
+ could use the same handler function (presuming that it's not an anonymous
+ function like in the examples above) for more buttons. You can see an example a bit later in this document.
+
+
+2. Inserting it into the toolbar
+
+At this step you need to specify where in the toolbar to insert the
+button, or just create the whole toolbar again as you saw in the previous
+section. You use the button ID, as shown in the examples of customizing the
+toolbar in the previous section.
+
+For the sake of completion, following there are another examples.
+
+Append your button to the default toolbar
+
+config.toolbar.push([ "my-hilite" ]);
+
+Customized toolbar
+
+config.toolbar = [
+ ['fontname', 'space',
+ 'fontsize', 'space',
+ 'formatblock', 'space',
+ 'separator', 'my-hilite', 'separator', 'space', 'bold', 'italic', 'underline', 'space']
+];
+
+Note: in the example above our new button is
+between two vertical separators. But this is by no means required. You can
+put it wherever you like. Once registered in the btnList (step 1) your custom button behaves just like a default
+button.
+
+Important: It's recommended that you add
+custom features and configuration to a separate file. This will ensure you
+that when we release a new official version of HTMLArea you'll have no
+trouble upgrading it.
+
+A complete example
+
+Please note that it is by no means necessary to include the following
+code into the htmlarea.js file. On the contrary, it might not work there.
+The configuration system is designed such that you can always customize the
+editor from outside files, thus keeping the htmlarea.js file
+intact. This will make it easy for you to upgrade your HTMLArea when we
+release a new official version. OK, I promise it's the last time I said
+this. ;)
+
+function clickHandler(editor, buttonId) {
+ switch (buttonId) {
+ case "my-toc":
+ editor.insertHTML("<h1>Table Of Contents</h1>");
+ break;
+ case "my-date":
+ editor.insertHTML((new Date()).toString());
+ break;
+ case "my-bold":
+ editor.execCommand("bold");
+ editor.execCommand("italic");
+ break;
+ case "my-hilite":
+ editor.surroundHTML("<span class=\"hilite\">", "</span>");
+ break;
+ }
+};
+
+var config = new HTMLArea.Config();
+
+config.registerButton("my-toc", "Insert TOC", "my-toc.gif", false, clickHandler);
+config.registerButton("my-date", "Insert date/time", "my-date.gif", false, clickHandler);
+config.registerButton("my-bold", "Toggle bold/italic", "my-bold.gif", false, clickHandler);
+config.registerButton("my-hilite", "Hilite selection", "my-hilite.gif", false, clickHandler);
+
+config.toolbar.push(["linebreak", "my-toc", "my-date", "my-bold", "my-hilite"]);
+
+HTMLArea.replace("textAreaID", config);
+
+
+
+© InteractiveTools.com 2002, 2003.
+
+HTMLArea v3.0 developed by Mihai Bazon for
+InteractiveTools.com.
+
+Documentation written by Mihai Bazon.
+
+
+Last modified on Sun Aug 3 16:11:23 2003
+
+
+
Index: openacs-4/packages/acs-templating/www/resources/htmlarea/release-notes.html
===================================================================
RCS file: /usr/local/cvsroot/openacs-4/packages/acs-templating/www/resources/htmlarea/release-notes.html,v
diff -u
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ openacs-4/packages/acs-templating/www/resources/htmlarea/release-notes.html 4 Mar 2004 18:32:10 -0000 1.1
@@ -0,0 +1,82 @@
+
+
+
+ HTMLArea-3.0-beta release notes
+
+
+
+
+ HTMLArea-3.0-beta release notes
+
+ This release was compiled on Aug 11, 2003 [21:30] GMT.
+
+
+ Changes since 3.0-Alpha:
+
+
+
+ - Performance improvements.
+
+ - Many bugs fixed.
+
+ - Plugin infrastructure.
+
+ - TableOperations plugin.
+
+ - SpellChecker plugin.
+
+ - Status bar.
+
+ - API for registering custom buttons and drop-down boxes in the
+ toolbar.
+
+ - Toolbar can contain text labels.
+
+ - Cut, copy, paste, undo, redo buttons.
+
+
+
+ Rationale for Beta
+
+ Why was this released as "Beta"? The code is quite stable and it
+ didn't deserve a "Beta" qualification. However, there are some things
+ left to do for the real 3.0 version. These things will not affect the
+ API to work with HTMLArea, in other words, you can install the Beta
+ right now and then install the final release without modifying your
+ code. That's if you don't modify HTMLArea itself. ;-)
+
+ To-Do before 3.0 final
+
+
+
+ - We should use a single popup interface. Currently there are two:
+ dialog.js and popupwin.js; dialog.js emulates modal dialogs, which
+ sucks when you want to open "select-color" from another popup and not
+ from the editor itself. Very buggy in IE. We should probably use only
+ modeless dialogs (that is, popupwin.js).
+
+ - Internationalization for the SpellChecker plugin.
+
+ - Internationalization for the TableOperations plugin.
+
+ - People who sent translations are invited to re-iterate through
+ their work and make it up-to-date with lang/en.js which is the main
+ lang file for HTMLArea-3.0. Some things have changed but not all
+ translations are updated.
+
+ - Documentation.
+
+
+
+
+
+ Mihai Bazon
+
+
+Last modified on Sun Aug 10 19:31:39 2003
+
+
+
+
+
+
Index: openacs-4/packages/acs-templating/www/resources/htmlarea/test.cgi
===================================================================
RCS file: /usr/local/cvsroot/openacs-4/packages/acs-templating/www/resources/htmlarea/Attic/test.cgi,v
diff -u
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ openacs-4/packages/acs-templating/www/resources/htmlarea/test.cgi 4 Mar 2004 18:32:10 -0000 1.1
@@ -0,0 +1,21 @@
+#! /usr/bin/perl -w
+#
+#
+#
+
+
+
+use CGI;
+
+print "Content-type: text/html\n\n";
+$c = new CGI;
+$ta = $c->param('ta');
+
+print <
+
+
+$ta
+
+