Annotation of loncom/html/htmlarea/htmlarea.js, revision 1.2

1.1       www         1: //
                      2: // htmlArea v3.0 - Copyright (c) 2002 interactivetools.com, inc.
                      3: // This copyright notice MUST stay intact for use (see license.txt).
                      4: //
                      5: // A free WYSIWYG editor replacement for <textarea> fields.
                      6: // For full source code and docs, visit http://www.interactivetools.com/
                      7: //
                      8: // Version 3.0 developed by Mihai Bazon for InteractiveTools.
                      9: //	     http://students.infoiasi.ro/~mishoo
                     10: //
1.2     ! www        11: // $Id: htmlarea.js,v 1.1 2004/02/18 08:07:15 www Exp $
1.1       www        12: 
                     13: // Creates a new HTMLArea object.  Tries to replace the textarea with the given
                     14: // ID with it.
                     15: function HTMLArea(textarea, config) {
                     16: 	if (HTMLArea.checkSupportedBrowser()) {
                     17: 		if (typeof config == "undefined") {
                     18: 			this.config = new HTMLArea.Config();
                     19: 		} else {
                     20: 			this.config = config;
                     21: 		}
                     22: 		this._htmlArea = null;
                     23: 		this._textArea = textarea;
                     24: 		this._editMode = "wysiwyg";
                     25: 		this.plugins = {};
                     26: 		this._timerToolbar = null;
                     27: 		this._mdoc = document; // cache the document, we need it in plugins
                     28: 	}
                     29: };
                     30: 
                     31: HTMLArea.Config = function () {
                     32: 	this.version = "3.0";
                     33: 
                     34: 	this.width = "auto";
                     35: 	this.height = "auto";
                     36: 
                     37: 	// enable creation of a status bar?
                     38: 	this.statusBar = true;
                     39: 
                     40: 	// the next parameter specifies whether the toolbar should be included
                     41: 	// in the size or not.
                     42: 	this.sizeIncludesToolbar = true;
                     43: 
                     44: 	// style included in the iframe document
                     45: 	this.pageStyle = "body { background-color: #fff; font-family: verdana,sans-serif; }";
                     46: 	if (typeof _editor_url != "undefined") {
                     47: 		this.editorURL = _editor_url;
                     48: 	} else {
                     49: 		this.editorURL = "";
                     50: 	}
                     51: 
                     52: 	// URL-s
                     53: 	this.imgURL = "images/";
                     54: 	this.popupURL = "popups/";
                     55: 
                     56: 	// configuration for plugins
                     57: 	this.plugins = {};
                     58: 
                     59: 	/** CUSTOMIZING THE TOOLBAR
                     60: 	 * -------------------------
                     61: 	 *
                     62: 	 * It is recommended that you customize the toolbar contents in an
                     63: 	 * external file (i.e. the one calling HTMLArea) and leave this one
                     64: 	 * unchanged.  That's because when we (InteractiveTools.com) release a
                     65: 	 * new official version, it's less likely that you will have problems
                     66: 	 * upgrading HTMLArea.
                     67: 	 */
                     68: 	this.toolbar = [
                     69: 		[ "fontname", "space",
                     70: 		  "fontsize", "space",
                     71: 		  "formatblock", "space",
                     72: 		  "bold", "italic", "underline", "separator",
                     73: 		  "strikethrough", "subscript", "superscript", "separator",
                     74: 		  "copy", "cut", "paste", "space", "undo", "redo" ],
                     75: 		
                     76: 		[ "justifyleft", "justifycenter", "justifyright", "justifyfull", "separator",
                     77: 		  "insertorderedlist", "insertunorderedlist", "outdent", "indent", "separator",
                     78: 		  "forecolor", "hilitecolor", "textindicator", "separator",
                     79: 		  "inserthorizontalrule", "createlink", "insertimage", "inserttable", "htmlmode", "separator",
                     80: 		  "popupeditor", "separator", "showhelp", "about" ]
                     81: 		];
                     82: 
                     83: 	this.fontname = {
                     84: 		"Arial":	   'arial,helvetica,sans-serif',
                     85: 		"Courier New":	   'courier new,courier,monospace',
                     86: 		"Georgia":	   'georgia,times new roman,times,serif',
                     87: 		"Tahoma":	   'tahoma,arial,helvetica,sans-serif',
                     88: 		"Times New Roman": 'times new roman,times,serif',
                     89: 		"Verdana":	   'verdana,arial,helvetica,sans-serif',
                     90: 		"impact":	   'impact',
                     91: 		"WingDings":	   'wingdings'
                     92: 	};
                     93: 
                     94: 	this.fontsize = {
                     95: 		"1 (8 pt)":  "1",
                     96: 		"2 (10 pt)": "2",
                     97: 		"3 (12 pt)": "3",
                     98: 		"4 (14 pt)": "4",
                     99: 		"5 (18 pt)": "5",
                    100: 		"6 (24 pt)": "6",
                    101: 		"7 (36 pt)": "7"
                    102: 	};
                    103: 
                    104: 	this.formatblock = {
                    105: 		"Heading 1": "h1",
                    106: 		"Heading 2": "h2",
                    107: 		"Heading 3": "h3",
                    108: 		"Heading 4": "h4",
                    109: 		"Heading 5": "h5",
                    110: 		"Heading 6": "h6",
                    111: 		"Normal": "p",
                    112: 		"Address": "address",
                    113: 		"Formatted": "pre"
                    114: 	};
                    115: 
                    116: 	this.customSelects = {};
                    117: 
                    118: 	function cut_copy_paste(e, cmd, obj) {
                    119: 		try {
                    120: 			e.execCommand(cmd);
                    121: 		} catch (e) {
                    122: 			if (HTMLArea.is_gecko) {
                    123: 				alert("Some revisions of Mozilla/Gecko do not support programatic " +
                    124: 				      "access to cut/copy/paste functions, for security reasons.  " +
                    125: 				      "Your browser is one of them.  Please use the standard key combinations:\n" +
                    126: 				      "CTRL-X for cut, CTRL-C for copy, CTRL-V for paste.");
                    127: 				obj.element.style.display = "none";
                    128: 			}
                    129: 		}
                    130: 	};
                    131: 
                    132: 	// ADDING CUSTOM BUTTONS: please read below!
                    133: 	// format of the btnList elements is "ID: [ ToolTip, Icon, Enabled in text mode?, ACTION ]"
                    134: 	//    - ID: unique ID for the button.  If the button calls document.execCommand
                    135: 	//	    it's wise to give it the same name as the called command.
                    136: 	//    - ACTION: function that gets called when the button is clicked.
                    137: 	//              it has the following prototype:
                    138: 	//                 function(editor, buttonName)
                    139: 	//              - editor is the HTMLArea object that triggered the call
                    140: 	//              - buttonName is the ID of the clicked button
                    141: 	//              These 2 parameters makes it possible for you to use the same
                    142: 	//              handler for more HTMLArea objects or for more different buttons.
                    143: 	//    - ToolTip: default tooltip, for cases when it is not defined in the -lang- file (HTMLArea.I18N)
                    144: 	//    - Icon: path to an icon image file for the button (TODO: use one image for all buttons!)
                    145: 	//    - Enabled in text mode: if false the button gets disabled for text-only mode; otherwise enabled all the time.
                    146: 	this.btnList = {
                    147: 		bold: [ "Bold", "images/ed_format_bold.gif", false, function(e) {e.execCommand("bold");} ],
                    148: 		italic: [ "Italic", "images/ed_format_italic.gif", false, function(e) {e.execCommand("italic");} ],
                    149: 		underline: [ "Underline", "images/ed_format_underline.gif", false, function(e) {e.execCommand("underline");} ],
                    150: 		strikethrough: [ "Strikethrough", "images/ed_format_strike.gif", false, function(e) {e.execCommand("strikethrough");} ],
                    151: 		subscript: [ "Subscript", "images/ed_format_sub.gif", false, function(e) {e.execCommand("subscript");} ],
                    152: 		superscript: [ "Superscript", "images/ed_format_sup.gif", false, function(e) {e.execCommand("superscript");} ],
                    153: 		justifyleft: [ "Justify Left", "images/ed_align_left.gif", false, function(e) {e.execCommand("justifyleft");} ],
                    154: 		justifycenter: [ "Justify Center", "images/ed_align_center.gif", false, function(e) {e.execCommand("justifycenter");} ],
                    155: 		justifyright: [ "Justify Right", "images/ed_align_right.gif", false, function(e) {e.execCommand("justifyright");} ],
                    156: 		justifyfull: [ "Justify Full", "images/ed_align_justify.gif", false, function(e) {e.execCommand("justifyfull");} ],
                    157: 		insertorderedlist: [ "Ordered List", "images/ed_list_num.gif", false, function(e) {e.execCommand("insertorderedlist");} ],
                    158: 		insertunorderedlist: [ "Bulleted List", "images/ed_list_bullet.gif", false, function(e) {e.execCommand("insertunorderedlist");} ],
                    159: 		outdent: [ "Decrease Indent", "images/ed_indent_less.gif", false, function(e) {e.execCommand("outdent");} ],
                    160: 		indent: [ "Increase Indent", "images/ed_indent_more.gif", false, function(e) {e.execCommand("indent");} ],
                    161: 		forecolor: [ "Font Color", "images/ed_color_fg.gif", false, function(e) {e.execCommand("forecolor");} ],
                    162: 		hilitecolor: [ "Background Color", "images/ed_color_bg.gif", false, function(e) {e.execCommand("hilitecolor");} ],
                    163: 		inserthorizontalrule: [ "Horizontal Rule", "images/ed_hr.gif", false, function(e) {e.execCommand("inserthorizontalrule");} ],
                    164: 		createlink: [ "Insert Web Link", "images/ed_link.gif", false, function(e) {e.execCommand("createlink", true);} ],
                    165: 		insertimage: [ "Insert Image", "images/ed_image.gif", false, function(e) {e.execCommand("insertimage");} ],
                    166: 		inserttable: [ "Insert Table", "images/insert_table.gif", false, function(e) {e.execCommand("inserttable");} ],
                    167: 		htmlmode: [ "Toggle HTML Source", "images/ed_html.gif", true, function(e) {e.execCommand("htmlmode");} ],
                    168: 		popupeditor: [ "Enlarge Editor", "images/fullscreen_maximize.gif", true, function(e) {e.execCommand("popupeditor");} ],
                    169: 		about: [ "About this editor", "images/ed_about.gif", true, function(e) {e.execCommand("about");} ],
                    170: 		showhelp: [ "Help using editor", "images/ed_help.gif", true, function(e) {e.execCommand("showhelp");} ],
                    171: 		undo: [ "Undoes your last action", "images/ed_undo.gif", false, function(e) {e.execCommand("undo");} ],
                    172: 		redo: [ "Redoes your last action", "images/ed_redo.gif", false, function(e) {e.execCommand("redo");} ],
                    173: 		cut: [ "Cut selection", "images/ed_cut.gif", false, cut_copy_paste ],
                    174: 		copy: [ "Copy selection", "images/ed_copy.gif", false, cut_copy_paste ],
                    175: 		paste: [ "Paste from clipboard", "images/ed_paste.gif", false, cut_copy_paste ]
                    176: 	};
                    177: 	/* ADDING CUSTOM BUTTONS
                    178: 	 * ---------------------
                    179: 	 *
                    180: 	 * It is recommended that you add the custom buttons in an external
                    181: 	 * file and leave this one unchanged.  That's because when we
                    182: 	 * (InteractiveTools.com) release a new official version, it's less
                    183: 	 * likely that you will have problems upgrading HTMLArea.
                    184: 	 *
                    185: 	 * Example on how to add a custom button when you construct the HTMLArea:
                    186: 	 *
                    187: 	 *   var editor = new HTMLArea("your_text_area_id");
                    188: 	 *   var cfg = editor.config; // this is the default configuration
                    189: 	 *   cfg.btnList["my-hilite"] =
                    190: 	 *	[ function(editor) { editor.surroundHTML('<span style="background:yellow">', '</span>'); }, // action
                    191: 	 *	  "Highlight selection", // tooltip
                    192: 	 *	  "my_hilite.gif", // image
                    193: 	 *	  false // disabled in text mode
                    194: 	 *	];
                    195: 	 *   cfg.toolbar.push(["linebreak", "my-hilite"]); // add the new button to the toolbar
                    196: 	 *
                    197: 	 * An alternate (also more convenient and recommended) way to
                    198: 	 * accomplish this is to use the registerButton function below.
                    199: 	 */
                    200: 	// initialize tooltips from the I18N module
                    201: 	for (var i in this.btnList) {
                    202: 		var btn = this.btnList[i];
                    203: 		if (typeof HTMLArea.I18N.tooltips[i] != "undefined") {
                    204: 			btn[0] = HTMLArea.I18N.tooltips[i];
                    205: 		}
                    206: 	}
                    207: };
                    208: 
                    209: /** Helper function: register a new button with the configuration.  It can be
                    210:  * called with all 5 arguments, or with only one (first one).  When called with
                    211:  * only one argument it must be an object with the following properties: id,
                    212:  * tooltip, image, textMode, action.  Examples:
                    213:  *
                    214:  * 1. config.registerButton("my-hilite", "Hilite text", "my-hilite.gif", false, function(editor) {...});
                    215:  * 2. config.registerButton({
                    216:  *      id       : "my-hilite",      // the ID of your button
                    217:  *      tooltip  : "Hilite text",    // the tooltip
                    218:  *      image    : "my-hilite.gif",  // image to be displayed in the toolbar
                    219:  *      textMode : false,            // disabled in text mode
                    220:  *      action   : function(editor) { // called when the button is clicked
                    221:  *                   editor.surroundHTML('<span class="hilite">', '</span>');
                    222:  *                 },
                    223:  *      context  : "p"               // will be disabled if outside a <p> element
                    224:  *    });
                    225:  */
                    226: HTMLArea.Config.prototype.registerButton = function(id, tooltip, image, textMode, action, context) {
                    227: 	var the_id;
                    228: 	if (typeof id == "string") {
                    229: 		the_id = id;
                    230: 	} else if (typeof id == "object") {
                    231: 		the_id = id.id;
                    232: 	} else {
                    233: 		alert("ERROR [HTMLArea.Config::registerButton]:\ninvalid arguments");
                    234: 		return false;
                    235: 	}
                    236: 	// check for existing id
                    237: 	if (typeof this.customSelects[the_id] != "undefined") {
                    238: 		alert("WARNING [HTMLArea.Config::registerDropdown]:\nA dropdown with the same ID already exists.");
                    239: 	}
                    240: 	if (typeof this.btnList[the_id] != "undefined") {
                    241: 		alert("WARNING [HTMLArea.Config::registerDropdown]:\nA button with the same ID already exists.");
                    242: 	}
                    243: 	switch (typeof id) {
                    244: 	    case "string": this.btnList[id] = [ tooltip, image, textMode, action, context ]; break;
                    245: 	    case "object": this.btnList[id.id] = [ id.tooltip, id.image, id.textMode, id.action, id.context ]; break;
                    246: 	}
                    247: };
                    248: 
                    249: /** The following helper function registers a dropdown box with the editor
                    250:  * configuration.  You still have to add it to the toolbar, same as with the
                    251:  * buttons.  Call it like this:
                    252:  *
                    253:  * FIXME: add example
                    254:  */
                    255: HTMLArea.Config.prototype.registerDropdown = function(object) {
                    256: 	// check for existing id
                    257: 	if (typeof this.customSelects[object.id] != "undefined") {
                    258: 		alert("WARNING [HTMLArea.Config::registerDropdown]:\nA dropdown with the same ID already exists.");
                    259: 	}
                    260: 	if (typeof this.btnList[object.id] != "undefined") {
                    261: 		alert("WARNING [HTMLArea.Config::registerDropdown]:\nA button with the same ID already exists.");
                    262: 	}
                    263: 	this.customSelects[object.id] = object;
                    264: };
                    265: 
                    266: /** Helper function: replace all TEXTAREA-s in the document with HTMLArea-s. */
                    267: HTMLArea.replaceAll = function(config) {
                    268: 	var tas = document.getElementsByTagName("textarea");
                    269: 	for (var i = tas.length; i > 0; (new HTMLArea(tas[--i], config)).generate());
                    270: };
                    271: 
                    272: /** Helper function: replaces the TEXTAREA with the given ID with HTMLArea. */
                    273: HTMLArea.replace = function(id, config) {
                    274: 	var ta = document.getElementById(id);
                    275: 	return ta ? (new HTMLArea(ta, config)).generate() : null;
                    276: };
                    277: 
                    278: // Creates the toolbar and appends it to the _htmlarea
                    279: HTMLArea.prototype._createToolbar = function () {
                    280: 	var editor = this;	// to access this in nested functions
                    281: 
                    282: 	var toolbar = document.createElement("div");
                    283: 	this._toolbar = toolbar;
                    284: 	toolbar.className = "toolbar";
                    285: 	toolbar.unselectable = "1";
                    286: 	var tb_row = null;
                    287: 	var tb_objects = new Object();
                    288: 	this._toolbarObjects = tb_objects;
                    289: 
                    290: 	// creates a new line in the toolbar
                    291: 	function newLine() {
                    292: 		var table = document.createElement("table");
                    293: 		table.border = "0px";
                    294: 		table.cellSpacing = "0px";
                    295: 		table.cellPadding = "0px";
                    296: 		toolbar.appendChild(table);
                    297: 		// TBODY is required for IE, otherwise you don't see anything
                    298: 		// in the TABLE.
                    299: 		var tb_body = document.createElement("tbody");
                    300: 		table.appendChild(tb_body);
                    301: 		tb_row = document.createElement("tr");
                    302: 		tb_body.appendChild(tb_row);
                    303: 	}; // END of function: newLine
                    304: 	// init first line
                    305: 	newLine();
                    306: 
                    307: 	// updates the state of a toolbar element.  This function is member of
                    308: 	// a toolbar element object (unnamed objects created by createButton or
                    309: 	// createSelect functions below).
                    310: 	function setButtonStatus(id, newval) {
                    311: 		var oldval = this[id];
                    312: 		var el = this.element;
                    313: 		if (oldval != newval) {
                    314: 			switch (id) {
                    315: 			    case "enabled":
                    316: 				if (newval) {
                    317: 					HTMLArea._removeClass(el, "buttonDisabled");
                    318: 					el.disabled = false;
                    319: 				} else {
                    320: 					HTMLArea._addClass(el, "buttonDisabled");
                    321: 					el.disabled = true;
                    322: 				}
                    323: 				break;
                    324: 			    case "active":
                    325: 				if (newval) {
                    326: 					HTMLArea._addClass(el, "buttonPressed");
                    327: 				} else {
                    328: 					HTMLArea._removeClass(el, "buttonPressed");
                    329: 				}
                    330: 				break;
                    331: 			}
                    332: 			this[id] = newval;
                    333: 		}
                    334: 	}; // END of function: setButtonStatus
                    335: 
                    336: 	// this function will handle creation of combo boxes.  Receives as
                    337: 	// parameter the name of a button as defined in the toolBar config.
                    338: 	// This function is called from createButton, above, if the given "txt"
                    339: 	// doesn't match a button.
                    340: 	function createSelect(txt) {
                    341: 		var options = null;
                    342: 		var el = null;
                    343: 		var cmd = null;
                    344: 		var customSelects = editor.config.customSelects;
                    345: 		var context = null;
                    346: 		switch (txt) {
                    347: 		    case "fontsize":
                    348: 		    case "fontname":
                    349: 		    case "formatblock":
                    350: 			// the following line retrieves the correct
                    351: 			// configuration option because the variable name
                    352: 			// inside the Config object is named the same as the
                    353: 			// button/select in the toolbar.  For instance, if txt
                    354: 			// == "formatblock" we retrieve config.formatblock (or
                    355: 			// a different way to write it in JS is
                    356: 			// config["formatblock"].
                    357: 			options = editor.config[txt];
                    358: 			cmd = txt;
                    359: 			break;
                    360: 		    default:
                    361: 			// try to fetch it from the list of registered selects
                    362: 			cmd = txt;
                    363: 			var dropdown = customSelects[cmd];
                    364: 			if (typeof dropdown != "undefined") {
                    365: 				options = dropdown.options;
                    366: 				context = dropdown.context;
                    367: 			} else {
                    368: 				alert("ERROR [createSelect]:\nCan't find the requested dropdown definition");
                    369: 			}
                    370: 			break;
                    371: 		}
                    372: 		if (options) {
                    373: 			el = document.createElement("select");
                    374: 			var obj = {
                    375: 				name	: txt, // field name
                    376: 				element : el,	// the UI element (SELECT)
                    377: 				enabled : true, // is it enabled?
                    378: 				text	: false, // enabled in text mode?
                    379: 				cmd	: cmd, // command ID
                    380: 				state	: setButtonStatus, // for changing state
                    381: 				context : context
                    382: 			};
                    383: 			tb_objects[txt] = obj;
                    384: 			for (var i in options) {
                    385: 				var op = document.createElement("option");
                    386: 				op.appendChild(document.createTextNode(i));
                    387: 				op.value = options[i];
                    388: 				el.appendChild(op);
                    389: 			}
                    390: 			HTMLArea._addEvent(el, "change", function () {
                    391: 				editor._comboSelected(el, txt);
                    392: 			});
                    393: 		}
                    394: 		return el;
                    395: 	}; // END of function: createSelect
                    396: 
                    397: 	// appends a new button to toolbar
                    398: 	function createButton(txt) {
                    399: 		// the element that will be created
                    400: 		var el = null;
                    401: 		var btn = null;
                    402: 		switch (txt) {
                    403: 		    case "separator":
                    404: 			el = document.createElement("div");
                    405: 			el.className = "separator";
                    406: 			break;
                    407: 		    case "space":
                    408: 			el = document.createElement("div");
                    409: 			el.className = "space";
                    410: 			break;
                    411: 		    case "linebreak":
                    412: 			newLine();
                    413: 			return false;
                    414: 		    case "textindicator":
                    415: 			el = document.createElement("div");
                    416: 			el.appendChild(document.createTextNode("A"));
                    417: 			el.className = "indicator";
                    418: 			el.title = HTMLArea.I18N.tooltips.textindicator;
                    419: 			var obj = {
                    420: 				name	: txt, // the button name (i.e. 'bold')
                    421: 				element : el, // the UI element (DIV)
                    422: 				enabled : true, // is it enabled?
                    423: 				active	: false, // is it pressed?
                    424: 				text	: false, // enabled in text mode?
                    425: 				cmd	: "textindicator", // the command ID
                    426: 				state	: setButtonStatus // for changing state
                    427: 			};
                    428: 			tb_objects[txt] = obj;
                    429: 			break;
                    430: 		    default:
                    431: 			btn = editor.config.btnList[txt];
                    432: 		}
                    433: 		if (!el && btn) {
                    434: 			el = document.createElement("div");
                    435: 			el.title = btn[0];
                    436: 			el.className = "button";
                    437: 			// let's just pretend we have a button object, and
                    438: 			// assign all the needed information to it.
                    439: 			var obj = {
                    440: 				name	: txt, // the button name (i.e. 'bold')
                    441: 				element : el, // the UI element (DIV)
                    442: 				enabled : true, // is it enabled?
                    443: 				active	: false, // is it pressed?
                    444: 				text	: btn[2], // enabled in text mode?
                    445: 				cmd	: btn[3], // the command ID
                    446: 				state	: setButtonStatus, // for changing state
                    447: 				context : btn[4] || null // enabled in a certain context?
                    448: 			};
                    449: 			tb_objects[txt] = obj;
                    450: 			// handlers to emulate nice flat toolbar buttons
                    451: 			HTMLArea._addEvent(el, "mouseover", function () {
                    452: 				if (obj.enabled) {
                    453: 					HTMLArea._addClass(el, "buttonHover");
                    454: 				}
                    455: 			});
                    456: 			HTMLArea._addEvent(el, "mouseout", function () {
                    457: 				if (obj.enabled) with (HTMLArea) {
                    458: 					_removeClass(el, "buttonHover");
                    459: 					_removeClass(el, "buttonActive");
                    460: 					(obj.active) && _addClass(el, "buttonPressed");
                    461: 				}
                    462: 			});
                    463: 			HTMLArea._addEvent(el, "mousedown", function (ev) {
                    464: 				if (obj.enabled) with (HTMLArea) {
                    465: 					_addClass(el, "buttonActive");
                    466: 					_removeClass(el, "buttonPressed");
                    467: 					_stopEvent(is_ie ? window.event : ev);
                    468: 				}
                    469: 			});
                    470: 			// when clicked, do the following:
                    471: 			HTMLArea._addEvent(el, "click", function (ev) {
                    472: 				if (obj.enabled) with (HTMLArea) {
                    473: 					_removeClass(el, "buttonActive");
                    474: 					_removeClass(el, "buttonHover");
                    475: 					obj.cmd(editor, obj.name, obj);
                    476: 					_stopEvent(is_ie ? window.event : ev);
                    477: 				}
                    478: 			});
                    479: 			var img = document.createElement("img");
                    480: 			img.src = editor.imgURL(btn[1]);
                    481: 			img.style.width = "18px";
                    482: 			img.style.height = "18px";
                    483: 			el.appendChild(img);
                    484: 		} else if (!el) {
                    485: 			el = createSelect(txt);
                    486: 		}
                    487: 		if (el) {
                    488: 			var tb_cell = document.createElement("td");
                    489: 			tb_row.appendChild(tb_cell);
                    490: 			tb_cell.appendChild(el);
                    491: 		} else {
                    492: 			alert("FIXME: Unknown toolbar item: " + txt);
                    493: 		}
                    494: 		return el;
                    495: 	};
                    496: 
                    497: 	var first = true;
                    498: 	for (var i in this.config.toolbar) {
                    499: 		if (!first) {
                    500: 			createButton("linebreak");
                    501: 		} else {
                    502: 			first = false;
                    503: 		}
                    504: 		var group = this.config.toolbar[i];
                    505: 		for (var j in group) {
                    506: 			var code = group[j];
                    507: 			if (/^([IT])\[(.*?)\]/.test(code)) {
                    508: 				// special case, create text label
                    509: 				var l7ed = RegExp.$1 == "I"; // localized?
                    510: 				var label = RegExp.$2;
                    511: 				if (l7ed) {
                    512: 					label = HTMLArea.I18N.custom[label];
                    513: 				}
                    514: 				var tb_cell = document.createElement("td");
                    515: 				tb_row.appendChild(tb_cell);
                    516: 				tb_cell.className = "label";
                    517: 				tb_cell.innerHTML = label;
                    518: 			} else {
                    519: 				createButton(code);
                    520: 			}
                    521: 		}
                    522: 	}
                    523: 
                    524: 	this._htmlArea.appendChild(toolbar);
                    525: };
                    526: 
                    527: HTMLArea.prototype._createStatusBar = function() {
                    528: 	var div = document.createElement("div");
                    529: 	div.className = "statusBar";
                    530: 	this._htmlArea.appendChild(div);
                    531: 	this._statusBar = div;
                    532: 	div.appendChild(document.createTextNode(HTMLArea.I18N.msg["Path"] + ": "));
                    533: 	// creates a holder for the path view
                    534: 	div = document.createElement("span");
                    535: 	div.className = "statusBarTree";
                    536: 	this._statusBarTree = div;
                    537: 	this._statusBar.appendChild(div);
                    538: 	if (!this.config.statusBar) {
                    539: 		// disable it...
                    540: 		div.style.display = "none";
                    541: 	}
                    542: };
                    543: 
                    544: // Creates the HTMLArea object and replaces the textarea with it.
                    545: HTMLArea.prototype.generate = function () {
                    546: 	var editor = this;	// we'll need "this" in some nested functions
                    547: 	// get the textarea
                    548: 	var textarea = this._textArea;
                    549: 	if (typeof textarea == "string") {
                    550: 		// it's not element but ID
                    551: 		this._textArea = textarea = document.getElementById(textarea);
                    552: 	}
                    553: 	this._ta_size = {
                    554: 		w: textarea.offsetWidth,
                    555: 		h: textarea.offsetHeight
                    556: 	};
                    557: 	textarea.style.display = "none";
                    558: 
                    559: 	// create the editor framework
                    560: 	var htmlarea = document.createElement("div");
                    561: 	htmlarea.className = "htmlarea";
                    562: 	this._htmlArea = htmlarea;
                    563: 
                    564: 	// insert the editor before the textarea.
                    565: 	textarea.parentNode.insertBefore(htmlarea, textarea);
                    566: 
                    567: 	if (textarea.form) {
                    568: 		// we have a form, on submit get the HTMLArea content and
                    569: 		// update original textarea.
                    570: 		textarea.form.onsubmit = function() {
                    571: 			editor._textArea.value = editor.getHTML();
                    572: 		};
                    573: 	}
                    574: 
                    575: 	// add a handler for the "back/forward" case -- on body.unload we save
                    576: 	// the HTML content into the original textarea.
                    577: 	window.onunload = function() {
                    578: 		editor._textArea.value = editor.getHTML();
                    579: 	};
                    580: 
                    581: 	// creates & appends the toolbar
                    582: 	this._createToolbar();
                    583: 
                    584: 	// create the IFRAME
                    585: 	var iframe = document.createElement("iframe");
                    586: 	htmlarea.appendChild(iframe);
                    587: 
                    588: 	this._iframe = iframe;
                    589: 
                    590: 	// creates & appends the status bar, if the case
                    591: 	this._createStatusBar();
                    592: 
                    593: 	// remove the default border as it keeps us from computing correctly
                    594: 	// the sizes.  (somebody tell me why doesn't this work in IE)
                    595: 
                    596: 	if (!HTMLArea.is_ie) {
                    597: 		iframe.style.borderWidth = "1px";
                    598: 	// iframe.frameBorder = "1";
                    599: 	// iframe.marginHeight = "0";
                    600: 	// iframe.marginWidth = "0";
                    601: 	}
                    602: 
                    603: 	// size the IFRAME according to user's prefs or initial textarea
                    604: 	var height = (this.config.height == "auto" ? (this._ta_size.h + "px") : this.config.height);
                    605: 	height = parseInt(height);
                    606: 	var width = (this.config.width == "auto" ? (this._ta_size.w + "px") : this.config.width);
                    607: 	width = parseInt(width);
                    608: 
                    609: 	if (!HTMLArea.is_ie) {
                    610: 		height -= 2;
                    611: 		width -= 2;
                    612: 	}
                    613: 
                    614: 	iframe.style.width = width + "px";
                    615: 	if (this.config.sizeIncludesToolbar) {
                    616: 		// substract toolbar height
                    617: 		height -= this._toolbar.offsetHeight;
                    618: 		height -= this._statusBar.offsetHeight;
                    619: 	}
                    620: 	if (height < 0) {
                    621: 		height = 0;
                    622: 	}
                    623: 	iframe.style.height = height + "px";
                    624: 
                    625: 	// the editor including the toolbar now have the same size as the
                    626: 	// original textarea.. which means that we need to reduce that a bit.
                    627: 	textarea.style.width = iframe.style.width;
                    628:  	textarea.style.height = iframe.style.height;
                    629: 
                    630: 	// IMPORTANT: we have to allow Mozilla a short time to recognize the
                    631: 	// new frame.  Otherwise we get a stupid exception.
                    632: 	function initIframe() {
                    633: 		var doc = editor._iframe.contentWindow.document;
                    634: 		if (!doc) {
                    635: 			// Try again..
                    636: 			// FIXME: don't know what else to do here.  Normally
                    637: 			// we'll never reach this point.
                    638: 			if (HTMLArea.is_gecko) {
                    639: 				setTimeout(initIframe, 10);
                    640: 				return false;
                    641: 			} else {
                    642: 				alert("ERROR: IFRAME can't be initialized.");
                    643: 			}
                    644: 		}
                    645: 		if (HTMLArea.is_gecko) {
                    646: 			// enable editable mode for Mozilla
                    647: 			doc.designMode = "on";
                    648: 		}
                    649: 		editor._doc = doc;
                    650: 		doc.open();
                    651: 		var html = "<html>\n";
                    652: 		html += "<head>\n";
                    653: 		html += "<style>" + editor.config.pageStyle + "</style>\n";
                    654: 		html += "</head>\n";
                    655: 		html += "<body>\n";
                    656: 		html += editor._textArea.value;
                    657: 		html += "</body>\n";
                    658: 		html += "</html>";
                    659: 		doc.write(html);
                    660: 		doc.close();
                    661: 
                    662: 		if (HTMLArea.is_ie) {
                    663: 			// enable editable mode for IE.	 For some reason this
                    664: 			// doesn't work if done in the same place as for Gecko
                    665: 			// (above).
                    666: 			doc.body.contentEditable = true;
                    667: 		}
                    668: 
                    669: 		editor.focusEditor();
                    670: 		// intercept some events; for updating the toolbar & keyboard handlers
                    671: 		HTMLArea._addEvents
                    672: 			(doc, ["keydown", "keypress", "mousedown", "mouseup", "drag"],
                    673: 			 function (event) {
                    674: 				 return editor._editorEvent(HTMLArea.is_ie ? editor._iframe.contentWindow.event : event);
                    675: 			 });
                    676: 		editor.updateToolbar();
                    677: 	};
                    678: 	setTimeout(initIframe, HTMLArea.is_gecko ? 10 : 0);
                    679: };
                    680: 
                    681: // Switches editor mode; parameter can be "textmode" or "wysiwyg".  If no
                    682: // parameter was passed this function toggles between modes.
                    683: HTMLArea.prototype.setMode = function(mode) {
                    684: 	if (typeof mode == "undefined") {
                    685: 		mode = ((this._editMode == "textmode") ? "wysiwyg" : "textmode");
                    686: 	}
                    687: 	switch (mode) {
                    688: 	    case "textmode":
                    689: 		this._textArea.value = this.getHTML();
                    690: 		this._iframe.style.display = "none";
                    691: 		this._textArea.style.display = "block";
                    692: 		if (this.config.statusBar) {
                    693: 			this._statusBar.innerHTML = HTMLArea.I18N.msg["TEXT_MODE"];
                    694: 		}
                    695: 		break;
                    696: 	    case "wysiwyg":
                    697: 		if (HTMLArea.is_gecko) {
                    698: 			// disable design mode before changing innerHTML
                    699: 			this._doc.designMode = "off";
                    700: 		}
                    701: 		this._doc.body.innerHTML = this.getHTML();
                    702: 		this._iframe.style.display = "block";
                    703: 		this._textArea.style.display = "none";
                    704: 		if (HTMLArea.is_gecko) {
                    705: 			// we need to refresh that info for Moz-1.3a
                    706: 			this._doc.designMode = "on";
                    707: 		}
                    708: 		if (this.config.statusBar) {
                    709: 			this._statusBar.innerHTML = '';
                    710: 			this._statusBar.appendChild(document.createTextNode(HTMLArea.I18N.msg["Path"] + ": "));
                    711: 			this._statusBar.appendChild(this._statusBarTree);
                    712: 		}
                    713: 		break;
                    714: 	    default:
                    715: 		alert("Mode <" + mode + "> not defined!");
                    716: 		return false;
                    717: 	}
                    718: 	this._editMode = mode;
                    719: 	this.focusEditor();
                    720: };
                    721: 
                    722: /***************************************************
                    723:  *  Category: PLUGINS
                    724:  ***************************************************/
                    725: 
                    726: // Create the specified plugin and register it with this HTMLArea
                    727: HTMLArea.prototype.registerPlugin = function(pluginName) {
                    728: 	this.plugins[pluginName] = eval("new " + pluginName + "(this);");
                    729: };
                    730: 
                    731: // static function that loads the required plugin and lang file, based on the
                    732: // language loaded already for HTMLArea.  You better make sure that the plugin
                    733: // _has_ that language, otherwise shit might happen ;-)
                    734: HTMLArea.loadPlugin = function(pluginName) {
                    735: 	var editorurl = '';
                    736: 	if (typeof _editor_url != "undefined") {
                    737: 		editorurl = _editor_url + "/";
                    738: 	}
                    739: 	var dir = editorurl + "plugins/" + pluginName;
                    740: 	var plugin = pluginName.replace(/([a-z])([A-Z])([a-z])/g,
                    741: 					function (str, l1, l2, l3) {
                    742: 						return l1 + "-" + l2.toLowerCase() + l3;
                    743: 					}).toLowerCase() + ".js";
                    744: 	document.write("<script type='text/javascript' src='" + dir + "/" + plugin + "'></script>");
                    745: 	document.write("<script type='text/javascript' src='" + dir + "/lang/" + HTMLArea.I18N.lang + ".js'></script>");
                    746: };
                    747: 
                    748: /***************************************************
                    749:  *  Category: EDITOR UTILITIES
                    750:  ***************************************************/
                    751: 
                    752: HTMLArea.prototype.forceRedraw = function() {
                    753: 	this._doc.body.style.visibility = "hidden";
                    754: 	this._doc.body.style.visibility = "visible";
                    755: 	// this._doc.body.innerHTML = this.getInnerHTML();
                    756: };
                    757: 
                    758: // focuses the iframe window.  returns a reference to the editor document.
                    759: HTMLArea.prototype.focusEditor = function() {
                    760: 	switch (this._editMode) {
                    761: 	    case "wysiwyg" : this._iframe.contentWindow.focus(); break;
                    762: 	    case "textmode": this._textArea.focus(); break;
                    763: 	    default	   : alert("ERROR: mode " + this._editMode + " is not defined");
                    764: 	}
                    765: 	return this._doc;
                    766: };
                    767: 
                    768: // updates enabled/disable/active state of the toolbar elements
                    769: HTMLArea.prototype.updateToolbar = function(noStatus) {
                    770: 	var doc = this._doc;
                    771: 	var text = (this._editMode == "textmode");
                    772: 	var ancestors = null;
                    773: 	if (!text) {
                    774: 		ancestors = this.getAllAncestors();
                    775: 		if (this.config.statusBar && !noStatus) {
                    776: 			this._statusBarTree.innerHTML = ''; // clear
                    777: 			for (var i = ancestors.length; --i >= 0;) {
                    778: 				var el = ancestors[i];
                    779: 				if (!el) {
                    780: 					// hell knows why we get here; this
                    781: 					// could be a classic example of why
                    782: 					// it's good to check for conditions
                    783: 					// that are impossible to happen ;-)
                    784: 					continue;
                    785: 				}
                    786: 				var a = document.createElement("a");
                    787: 				a.href = "#";
                    788: 				a.el = el;
                    789: 				a.editor = this;
                    790: 				a.onclick = function() {
                    791: 					this.blur();
                    792: 					this.editor.selectNodeContents(this.el);
                    793: 					this.editor.updateToolbar(true);
                    794: 					return false;
                    795: 				};
                    796: 				a.oncontextmenu = function() {
                    797: 					// TODO: add context menu here
                    798: 					this.blur();
                    799: 					var info = "Inline style:\n\n";
                    800: 					info += this.el.style.cssText.split(/;\s*/).join(";\n");
                    801: 					alert(info);
                    802: 					return false;
                    803: 				};
                    804: 				var txt = el.tagName.toLowerCase();
                    805: 				a.title = el.style.cssText;
                    806: 				if (el.id) {
                    807: 					txt += "#" + el.id;
                    808: 				}
                    809: 				if (el.className) {
                    810: 					txt += "." + el.className;
                    811: 				}
                    812: 				a.appendChild(document.createTextNode(txt));
                    813: 				this._statusBarTree.appendChild(a);
                    814: 				if (i != 0) {
                    815: 					this._statusBarTree.appendChild(document.createTextNode(String.fromCharCode(0xbb)));
                    816: 				}
                    817: 			}
                    818: 		}
                    819: 	}
                    820: 	for (var i in this._toolbarObjects) {
                    821: 		var btn = this._toolbarObjects[i];
                    822: 		var cmd = i;
                    823: 		var inContext = true;
                    824: 		if (btn.context && !text) {
                    825: 			inContext = false;
                    826: 			var context = btn.context;
                    827: 			var attrs = [];
                    828: 			if (/(.*)\[(.*?)\]/.test(context)) {
                    829: 				context = RegExp.$1;
                    830: 				attrs = RegExp.$2.split(",");
                    831: 			}
                    832: 			context = context.toLowerCase();
                    833: 			var match = (context == "*");
                    834: 			for (var k in ancestors) {
                    835: 				if (!ancestors[k]) {
                    836: 					// the impossible really happens.
                    837: 					continue;
                    838: 				}
                    839: 				if (match || (ancestors[k].tagName.toLowerCase() == context)) {
                    840: 					inContext = true;
                    841: 					for (var ka in attrs) {
                    842: 						if (!eval("ancestors[k]." + attrs[ka])) {
                    843: 							inContext = false;
                    844: 							break;
                    845: 						}
                    846: 					}
                    847: 					if (inContext) {
                    848: 						break;
                    849: 					}
                    850: 				}
                    851: 			}
                    852: 		}
                    853: 		btn.state("enabled", (!text || btn.text) && inContext);
                    854: 		if (typeof cmd == "function") {
                    855: 			continue;
                    856: 		}
                    857: 		// look-it-up in the custom dropdown boxes
                    858: 		var dropdown = this.config.customSelects[cmd];
                    859: 		if ((!text || btn.text) && (typeof dropdown != "undefined")) {
                    860: 			dropdown.refresh(this);
                    861: 			continue;
                    862: 		}
                    863: 		switch (cmd) {
                    864: 		    case "fontname":
                    865: 		    case "fontsize":
                    866: 		    case "formatblock":
                    867: 			if (!text) {
                    868: 				var value = ("" + doc.queryCommandValue(cmd)).toLowerCase();
                    869: 				if (!value) {
                    870: 					// FIXME: what do we do here?
                    871: 					break;
                    872: 				}
                    873: 				// HACK -- retrieve the config option for this
                    874: 				// combo box.  We rely on the fact that the
                    875: 				// variable in config has the same name as
                    876: 				// button name in the toolbar.
                    877: 				var options = this.config[cmd];
                    878: 				var k = 0;
                    879: 				// btn.element.selectedIndex = 0;
                    880: 				for (var j in options) {
                    881: 					// FIXME: the following line is scary.
                    882: 					if ((j.toLowerCase() == value) ||
                    883: 					    (options[j].substr(0, value.length).toLowerCase() == value)) {
                    884: 						btn.element.selectedIndex = k;
                    885: 						break;
                    886: 					}
                    887: 					++k;
                    888: 				}
                    889: 			}
                    890: 			break;
                    891: 		    case "textindicator":
                    892: 			if (!text) {
                    893: 				try {with (btn.element.style) {
                    894: 					backgroundColor = HTMLArea._makeColor(
                    895: 						doc.queryCommandValue(HTMLArea.is_ie ? "backcolor" : "hilitecolor"));
                    896: 					if (/transparent/i.test(backgroundColor)) {
                    897: 						// Mozilla
                    898: 						backgroundColor = HTMLArea._makeColor(doc.queryCommandValue("backcolor"));
                    899: 					}
                    900: 					color = HTMLArea._makeColor(doc.queryCommandValue("forecolor"));
                    901: 					fontFamily = doc.queryCommandValue("fontname");
                    902: 					fontWeight = doc.queryCommandState("bold") ? "bold" : "normal";
                    903: 					fontStyle = doc.queryCommandState("italic") ? "italic" : "normal";
                    904: 				}} catch (e) {
                    905: 					// alert(e + "\n\n" + cmd);
                    906: 				}
                    907: 			}
                    908: 			break;
                    909: 		    case "htmlmode": btn.state("active", text); break;
                    910: 		    default:
                    911: 			try {
                    912: 				btn.state("active", (!text && doc.queryCommandState(cmd)));
                    913: 			} catch (e) {}
                    914: 		}
                    915: 	}
                    916: };
                    917: 
                    918: /** Returns a node after which we can insert other nodes, in the current
                    919:  * selection.  The selection is removed.  It splits a text node, if needed.
                    920:  */
                    921: HTMLArea.prototype.insertNodeAtSelection = function(toBeInserted) {
                    922: 	if (!HTMLArea.is_ie) {
                    923: 		var sel = this._getSelection();
                    924: 		var range = this._createRange(sel);
                    925: 		// remove the current selection
                    926: 		sel.removeAllRanges();
                    927: 		range.deleteContents();
                    928: 		var node = range.startContainer;
                    929: 		var pos = range.startOffset;
                    930: 		switch (node.nodeType) {
                    931: 		    case 3: // Node.TEXT_NODE
                    932: 			// we have to split it at the caret position.
                    933: 			if (toBeInserted.nodeType == 3) {
                    934: 				// do optimized insertion
                    935: 				node.insertData(pos, toBeInserted.data);
                    936: 				range = this._createRange();
                    937: 				range.setEnd(node, pos + toBeInserted.length);
                    938: 				range.setStart(node, pos + toBeInserted.length);
                    939: 				sel.addRange(range);
                    940: 			} else {
                    941: 				node = node.splitText(pos);
                    942: 				var selnode = toBeInserted;
                    943: 				if (toBeInserted.nodeType == 11 /* Node.DOCUMENT_FRAGMENT_NODE */) {
                    944: 					selnode = selnode.firstChild;
                    945: 				}
                    946: 				node.parentNode.insertBefore(toBeInserted, node);
                    947: 				this.selectNodeContents(selnode);
                    948: 				this.updateToolbar();
                    949: 			}
                    950: 			break;
                    951: 		    case 1: // Node.ELEMENT_NODE
                    952: 			var selnode = toBeInserted;
                    953: 			if (toBeInserted.nodeType == 11 /* Node.DOCUMENT_FRAGMENT_NODE */) {
                    954: 				selnode = selnode.firstChild;
                    955: 			}
                    956: 			node.insertBefore(toBeInserted, node.childNodes[pos]);
                    957: 			this.selectNodeContents(selnode);
                    958: 			this.updateToolbar();
                    959: 			break;
                    960: 		}
                    961: 	} else {
                    962: 		return null;	// this function not yet used for IE <FIXME>
                    963: 	}
                    964: };
                    965: 
                    966: // Returns the deepest node that contains both endpoints of the selection.
                    967: HTMLArea.prototype.getParentElement = function() {
                    968: 	var sel = this._getSelection();
                    969: 	var range = this._createRange(sel);
                    970: 	if (HTMLArea.is_ie) {
                    971: 		return range.parentElement ? range.parentElement() : this._doc.body;
                    972: 	} else {
                    973: 		var p = range.commonAncestorContainer;
                    974: 		while (p.nodeType == 3) {
                    975: 			p = p.parentNode;
                    976: 		}
                    977: 		return p;
                    978: 	}
                    979: };
                    980: 
                    981: // Returns an array with all the ancestor nodes of the selection.
                    982: HTMLArea.prototype.getAllAncestors = function() {
                    983: 	var p = this.getParentElement();
                    984: 	var a = [];
                    985: 	while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
                    986: 		a.push(p);
                    987: 		p = p.parentNode;
                    988: 	}
                    989: 	a.push(this._doc.body);
                    990: 	return a;
                    991: };
                    992: 
                    993: // Selects the contents inside the given node
                    994: HTMLArea.prototype.selectNodeContents = function(node, pos) {
                    995: 	this.focusEditor();
                    996: 	this.forceRedraw();
                    997: 	var range;
                    998: 	var collapsed = (typeof pos != "undefined");
                    999: 	if (HTMLArea.is_ie) {
                   1000: 		range = this._doc.body.createTextRange();
                   1001: 		range.moveToElementText(node);
                   1002: 		(collapsed) && range.collapse(pos);
                   1003: 		range.select();
                   1004: 	} else {
                   1005: 		var sel = this._getSelection();
                   1006: 		range = this._doc.createRange();
                   1007: 		range.selectNodeContents(node);
                   1008: 		(collapsed) && range.collapse(pos);
                   1009: 		sel.removeAllRanges();
                   1010: 		sel.addRange(range);
                   1011: 	}
                   1012: };
                   1013: 
                   1014: /** Call this function to insert HTML code at the current position.  It deletes
                   1015:  * the selection, if any.
                   1016:  */
                   1017: HTMLArea.prototype.insertHTML = function(html) {
                   1018: 	var sel = this._getSelection();
                   1019: 	var range = this._createRange(sel);
                   1020: 	if (HTMLArea.is_ie) {
                   1021: 		range.pasteHTML(html);
                   1022: 	} else {
                   1023: 		// construct a new document fragment with the given HTML
                   1024: 		var fragment = this._doc.createDocumentFragment();
                   1025: 		var div = this._doc.createElement("div");
                   1026: 		div.innerHTML = html;
                   1027: 		while (div.firstChild) {
                   1028: 			// the following call also removes the node from div
                   1029: 			fragment.appendChild(div.firstChild);
                   1030: 		}
                   1031: 		// this also removes the selection
                   1032: 		var node = this.insertNodeAtSelection(fragment);
                   1033: 	}
                   1034: };
                   1035: 
                   1036: /**
                   1037:  *  Call this function to surround the existing HTML code in the selection with
                   1038:  *  your tags.  FIXME: buggy!  This function will be deprecated "soon".
                   1039:  */
                   1040: HTMLArea.prototype.surroundHTML = function(startTag, endTag) {
                   1041: 	var html = this.getSelectedHTML();
                   1042: 	// the following also deletes the selection
                   1043: 	this.insertHTML(startTag + html + endTag);
                   1044: };
                   1045: 
                   1046: /// Retrieve the selected block
                   1047: HTMLArea.prototype.getSelectedHTML = function() {
                   1048: 	var sel = this._getSelection();
                   1049: 	var range = this._createRange(sel);
                   1050: 	var existing = null;
                   1051: 	if (HTMLArea.is_ie) {
                   1052: 		existing = range.htmlText;
                   1053: 	} else {
                   1054: 		existing = HTMLArea.getHTML(range.cloneContents(), false);
                   1055: 	}
                   1056: 	return existing;
                   1057: };
                   1058: 
                   1059: // Called when the user clicks on "InsertImage" button
                   1060: HTMLArea.prototype._insertImage = function() {
                   1061: 	var editor = this;	// for nested functions
                   1062: 	this._popupDialog("insert_image.html", function(param) {
                   1063: 		if (!param) {	// user must have pressed Cancel
                   1064: 			return false;
                   1065: 		}
                   1066: 		var sel = editor._getSelection();
                   1067: 		var range = editor._createRange(sel);
                   1068: 		editor._doc.execCommand("insertimage", false, param["f_url"]);
                   1069: 		var img = null;
                   1070: 		if (HTMLArea.is_ie) {
                   1071: 			img = range.parentElement();
                   1072: 			// wonder if this works...
                   1073: 			if (img.tagName.toLowerCase() != "img") {
                   1074: 				img = img.previousSibling;
                   1075: 			}
                   1076: 		} else {
                   1077: 			img = range.startContainer.previousSibling;
                   1078: 		}
                   1079: 		for (field in param) {
                   1080: 			var value = param[field];
                   1081: 			if (!value) {
                   1082: 				continue;
                   1083: 			}
                   1084: 			switch (field) {
                   1085: 			    case "f_alt"    : img.alt	 = value; break;
                   1086: 			    case "f_border" : img.border = parseInt(value); break;
                   1087: 			    case "f_align"  : img.align	 = value; break;
                   1088: 			    case "f_vert"   : img.vspace = parseInt(value); break;
                   1089: 			    case "f_horiz"  : img.hspace = parseInt(value); break;
                   1090: 			}
                   1091: 		}
                   1092: 	}, null);
                   1093: };
                   1094: 
                   1095: // Called when the user clicks the Insert Table button
                   1096: HTMLArea.prototype._insertTable = function() {
                   1097: 	var sel = this._getSelection();
                   1098: 	var range = this._createRange(sel);
                   1099: 	var editor = this;	// for nested functions
                   1100: 	this._popupDialog("insert_table.html", function(param) {
                   1101: 		if (!param) {	// user must have pressed Cancel
                   1102: 			return false;
                   1103: 		}
                   1104: 		var doc = editor._doc;
                   1105: 		// create the table element
                   1106: 		var table = doc.createElement("table");
                   1107: 		// assign the given arguments
                   1108: 		for (var field in param) {
                   1109: 			var value = param[field];
                   1110: 			if (!value) {
                   1111: 				continue;
                   1112: 			}
                   1113: 			switch (field) {
                   1114: 			    case "f_width"   : table.style.width = value + param["f_unit"]; break;
                   1115: 			    case "f_align"   : table.align	 = value; break;
                   1116: 			    case "f_border"  : table.border	 = parseInt(value); break;
                   1117: 			    case "f_spacing" : table.cellspacing = parseInt(value); break;
                   1118: 			    case "f_padding" : table.cellpadding = parseInt(value); break;
                   1119: 			}
                   1120: 		}
                   1121: 		var tbody = doc.createElement("tbody");
                   1122: 		table.appendChild(tbody);
                   1123: 		for (var i = 0; i < param["f_rows"]; ++i) {
                   1124: 			var tr = doc.createElement("tr");
                   1125: 			tbody.appendChild(tr);
                   1126: 			for (var j = 0; j < param["f_cols"]; ++j) {
                   1127: 				var td = doc.createElement("td");
                   1128: 				tr.appendChild(td);
                   1129: 				// Mozilla likes to see something inside the cell.
                   1130: 				(HTMLArea.is_gecko) && td.appendChild(doc.createElement("br"));
                   1131: 			}
                   1132: 		}
                   1133: 		if (HTMLArea.is_ie) {
                   1134: 			range.pasteHTML(table.outerHTML);
                   1135: 		} else {
                   1136: 			// insert the table
                   1137: 			editor.insertNodeAtSelection(table);
                   1138: 		}
                   1139: 		return true;
                   1140: 	}, null);
                   1141: };
                   1142: 
                   1143: /***************************************************
                   1144:  *  Category: EVENT HANDLERS
                   1145:  ***************************************************/
                   1146: 
                   1147: // el is reference to the SELECT object
                   1148: // txt is the name of the select field, as in config.toolbar
                   1149: HTMLArea.prototype._comboSelected = function(el, txt) {
                   1150: 	this.focusEditor();
                   1151: 	var value = el.options[el.selectedIndex].value;
                   1152: 	switch (txt) {
                   1153: 	    case "fontname":
                   1154: 	    case "fontsize": this.execCommand(txt, false, value); break;
                   1155: 	    case "formatblock":
                   1156: 		(HTMLArea.is_ie) && (value = "<" + value + ">");
                   1157: 		this.execCommand(txt, false, value);
                   1158: 		break;
                   1159: 	    default:
                   1160: 		// try to look it up in the registered dropdowns
                   1161: 		var dropdown = this.config.customSelects[txt];
                   1162: 		if (typeof dropdown != "undefined") {
                   1163: 			dropdown.action(this);
                   1164: 		} else {
                   1165: 			alert("FIXME: combo box " + txt + " not implemented");
                   1166: 		}
                   1167: 	}
                   1168: };
                   1169: 
                   1170: // the execCommand function (intercepts some commands and replaces them with
                   1171: // our own implementation)
                   1172: HTMLArea.prototype.execCommand = function(cmdID, UI, param) {
                   1173: 	var editor = this;	// for nested functions
                   1174: 	this.focusEditor();
                   1175: 	switch (cmdID.toLowerCase()) {
                   1176: 	    case "htmlmode" : this.setMode(); break;
                   1177: 	    case "hilitecolor":
                   1178: 		(HTMLArea.is_ie) && (cmdID = "backcolor");
                   1179: 	    case "forecolor":
                   1180: 		this._popupDialog("select_color.html", function(color) {
                   1181: 			if (color) { // selection not canceled
                   1182: 				editor._doc.execCommand(cmdID, false, "#" + color);
                   1183: 			}
                   1184: 		}, HTMLArea._colorToRgb(this._doc.queryCommandValue(cmdID)));
                   1185: 		break;
                   1186: 	    case "createlink":
                   1187: 		if (HTMLArea.is_ie || !UI) {
                   1188: 			this._doc.execCommand(cmdID, UI, param);
                   1189: 		} else {
                   1190: 			// browser is Mozilla & wants UI
                   1191: 			var param;
                   1192: 			if ((param = prompt("Enter URL"))) {
                   1193: 				this._doc.execCommand(cmdID, false, param);
                   1194: 			}
                   1195: 		}
                   1196: 		break;
                   1197: 	    case "popupeditor":
                   1198: 		if (HTMLArea.is_ie) {
                   1199: 			window.open(this.popupURL("fullscreen.html"), "ha_fullscreen",
                   1200: 				    "toolbar=no,location=no,directories=no,status=no,menubar=no," +
                   1201: 				    "scrollbars=no,resizable=yes,width=640,height=480");
                   1202: 		} else {
                   1203: 			window.open(this.popupURL("fullscreen.html"), "ha_fullscreen",
                   1204: 				    "toolbar=no,menubar=no,personalbar=no,width=640,height=480," +
                   1205: 				    "scrollbars=no,resizable=yes");
                   1206: 		}
                   1207: 		// pass this object to the newly opened window
                   1208: 		HTMLArea._object = this;
                   1209: 		break;
                   1210: 	    case "inserttable": this._insertTable(); break;
                   1211: 	    case "insertimage": this._insertImage(); break;
                   1212: 	    case "about"    : this._popupDialog("about.html", null, null); break;
                   1213: 	    case "showhelp" : window.open("reference.html", "ha_help"); break;
                   1214: 	    default: this._doc.execCommand(cmdID, UI, param);
                   1215: 	}
                   1216: 	this.updateToolbar();
                   1217: 	return false;
                   1218: };
                   1219: 
                   1220: /** A generic event handler for things that happen in the IFRAME's document.
                   1221:  * This function also handles key bindings. */
                   1222: HTMLArea.prototype._editorEvent = function(ev) {
                   1223: 	var editor = this;
                   1224: 	var keyEvent = (HTMLArea.is_ie && ev.type == "keydown") || (ev.type == "keypress");
                   1225: 	if (keyEvent && ev.ctrlKey) {
                   1226: 		var sel = null;
                   1227: 		var range = null;
                   1228: 		var key = String.fromCharCode(HTMLArea.is_ie ? ev.keyCode : ev.charCode).toLowerCase();
                   1229: 		var cmd = null;
                   1230: 		var value = null;
                   1231: 		switch (key) {
                   1232: 		    case 'a':
                   1233: 			if (!HTMLArea.is_ie) {
                   1234: 				// KEY select all
                   1235: 				sel = this._getSelection();
                   1236: 				sel.removeAllRanges();
                   1237: 				range = this._createRange();
                   1238: 				range.selectNodeContents(this._doc.body);
                   1239: 				sel.addRange(range);
                   1240: 				HTMLArea._stopEvent(ev);
                   1241: 			}
                   1242: 			break;
                   1243: 
                   1244: 			// simple key commands follow
                   1245: 
                   1246: 		    case 'b': cmd = "bold"; break;
                   1247: 		    case 'i': cmd = "italic"; break;
                   1248: 		    case 'u': cmd = "underline"; break;
                   1249: 		    case 's': cmd = "strikethrough"; break;
                   1250: 		    case 'l': cmd = "justifyleft"; break;
                   1251: 		    case 'e': cmd = "justifycenter"; break;
                   1252: 		    case 'r': cmd = "justifyright"; break;
                   1253: 		    case 'j': cmd = "justifyfull"; break;
                   1254: 
                   1255: 			// headings
                   1256: 		    case '1':
                   1257: 		    case '2':
                   1258: 		    case '3':
                   1259: 		    case '4':
                   1260: 		    case '5':
                   1261: 		    case '6':
                   1262: 			cmd = "formatblock";
                   1263: 			value = "h" + key;
                   1264: 			if (HTMLArea.is_ie) {
                   1265: 				value = "<" + value + ">";
                   1266: 			}
                   1267: 			break;
                   1268: 		}
                   1269: 		if (cmd) {
                   1270: 			// execute simple command
                   1271: 			this.execCommand(cmd, false, value);
                   1272: 			HTMLArea._stopEvent(ev);
                   1273: 		}
                   1274: 	}
                   1275: 	/*
                   1276: 	else if (keyEvent) {
                   1277: 		// other keys here
                   1278: 		switch (ev.keyCode) {
                   1279: 		    case 13: // KEY enter
                   1280: 			// if (HTMLArea.is_ie) {
                   1281: 			this.insertHTML("<br />");
                   1282: 			HTMLArea._stopEvent(ev);
                   1283: 			// }
                   1284: 			break;
                   1285: 		}
                   1286: 	}
                   1287: 	*/
                   1288: 	// update the toolbar state after some time
                   1289: 	if (editor._timerToolbar) {
                   1290: 		clearTimeout(editor._timerToolbar);
                   1291: 	}
                   1292: 	editor._timerToolbar = setTimeout(function() {
                   1293: 		editor.updateToolbar();
                   1294: 		editor._timerToolbar = null;
                   1295: 	}, 50);
                   1296: };
                   1297: 
                   1298: // retrieve the HTML
                   1299: HTMLArea.prototype.getHTML = function() {
                   1300: 	switch (this._editMode) {
                   1301: 	    case "wysiwyg"  : return HTMLArea.getHTML(this._doc.body, false);
                   1302: 	    case "textmode" : return this._textArea.value;
                   1303: 	    default	    : alert("Mode <" + mode + "> not defined!");
                   1304: 	}
                   1305: 	return false;
                   1306: };
                   1307: 
                   1308: // retrieve the HTML (fastest version, but uses innerHTML)
                   1309: HTMLArea.prototype.getInnerHTML = function() {
                   1310: 	switch (this._editMode) {
                   1311: 	    case "wysiwyg"  : return this._doc.body.innerHTML;
                   1312: 	    case "textmode" : return this._textArea.value;
                   1313: 	    default	    : alert("Mode <" + mode + "> not defined!");
                   1314: 	}
                   1315: 	return false;
                   1316: };
                   1317: 
                   1318: // completely change the HTML inside
                   1319: HTMLArea.prototype.setHTML = function(html) {
                   1320: 	switch (this._editMode) {
                   1321: 	    case "wysiwyg"  : this._doc.body.innerHTML = html; break;
                   1322: 	    case "textmode" : this._textArea.value = html; break;
                   1323: 	    default	    : alert("Mode <" + mode + "> not defined!");
                   1324: 	}
                   1325: 	return false;
                   1326: };
                   1327: 
                   1328: /***************************************************
                   1329:  *  Category: UTILITY FUNCTIONS
                   1330:  ***************************************************/
                   1331: 
                   1332: // browser identification
                   1333: 
                   1334: HTMLArea.agt = navigator.userAgent.toLowerCase();
                   1335: HTMLArea.is_ie	   = ((HTMLArea.agt.indexOf("msie") != -1) && (HTMLArea.agt.indexOf("opera") == -1));
                   1336: HTMLArea.is_opera  = (HTMLArea.agt.indexOf("opera") != -1);
                   1337: HTMLArea.is_mac	   = (HTMLArea.agt.indexOf("mac") != -1);
                   1338: HTMLArea.is_mac_ie = (HTMLArea.is_ie && HTMLArea.is_mac);
                   1339: HTMLArea.is_win_ie = (HTMLArea.is_ie && !HTMLArea.is_mac);
                   1340: HTMLArea.is_gecko  = (navigator.product == "Gecko");
                   1341: 
                   1342: // variable used to pass the object to the popup editor window.
                   1343: HTMLArea._object = null;
                   1344: 
                   1345: // FIXME!!! this should return false for IE < 5.5
                   1346: HTMLArea.checkSupportedBrowser = function() {
                   1347: 	if (HTMLArea.is_gecko) {
                   1348: 		if (navigator.productSub < 20021201) {
1.2     ! www      1349: 			window.status="You need at least Mozilla-1.3 Alpha. " +
        !          1350: 			      "Sorry, your Gecko is not supported.";
1.1       www      1351: 			return false;
                   1352: 		}
                   1353: 		if (navigator.productSub < 20030210) {
1.2     ! www      1354: 			window.status="Mozilla < 1.3 Beta is not supported! " +
        !          1355: 			      "I'll try, though, but it might not work.";
1.1       www      1356: 		}
                   1357: 	}
                   1358: 	return HTMLArea.is_gecko || HTMLArea.is_ie;
                   1359: };
                   1360: 
                   1361: // selection & ranges
                   1362: 
                   1363: // returns the current selection object
                   1364: HTMLArea.prototype._getSelection = function() {
                   1365: 	if (HTMLArea.is_ie) {
                   1366: 		return this._doc.selection;
                   1367: 	} else {
                   1368: 		return this._iframe.contentWindow.getSelection();
                   1369: 	}
                   1370: };
                   1371: 
                   1372: // returns a range for the current selection
                   1373: HTMLArea.prototype._createRange = function(sel) {
                   1374: 	if (HTMLArea.is_ie) {
                   1375: 		return sel.createRange();
                   1376: 	} else {
                   1377: 		this.focusEditor();
                   1378: 		if (typeof sel != "undefined") {
                   1379: 			return sel.getRangeAt(0);
                   1380: 		} else {
                   1381: 			return this._doc.createRange();
                   1382: 		}
                   1383: 	}
                   1384: };
                   1385: 
                   1386: // event handling
                   1387: 
                   1388: HTMLArea._addEvent = function(el, evname, func) {
                   1389: 	if (HTMLArea.is_ie) {
                   1390: 		el.attachEvent("on" + evname, func);
                   1391: 	} else {
                   1392: 		el.addEventListener(evname, func, true);
                   1393: 	}
                   1394: };
                   1395: 
                   1396: HTMLArea._addEvents = function(el, evs, func) {
                   1397: 	for (var i in evs) {
                   1398: 		HTMLArea._addEvent(el, evs[i], func);
                   1399: 	}
                   1400: };
                   1401: 
                   1402: HTMLArea._removeEvent = function(el, evname, func) {
                   1403: 	if (HTMLArea.is_ie) {
                   1404: 		el.detachEvent("on" + evname, func);
                   1405: 	} else {
                   1406: 		el.removeEventListener(evname, func, true);
                   1407: 	}
                   1408: };
                   1409: 
                   1410: HTMLArea._removeEvents = function(el, evs, func) {
                   1411: 	for (var i in evs) {
                   1412: 		HTMLArea._removeEvent(el, evs[i], func);
                   1413: 	}
                   1414: };
                   1415: 
                   1416: HTMLArea._stopEvent = function(ev) {
                   1417: 	if (HTMLArea.is_ie) {
                   1418: 		ev.cancelBubble = true;
                   1419: 		ev.returnValue = false;
                   1420: 	} else {
                   1421: 		ev.preventDefault();
                   1422: 		ev.stopPropagation();
                   1423: 	}
                   1424: };
                   1425: 
                   1426: HTMLArea._removeClass = function(el, className) {
                   1427: 	if (!(el && el.className)) {
                   1428: 		return;
                   1429: 	}
                   1430: 	var cls = el.className.split(" ");
                   1431: 	var ar = new Array();
                   1432: 	for (var i = cls.length; i > 0;) {
                   1433: 		if (cls[--i] != className) {
                   1434: 			ar[ar.length] = cls[i];
                   1435: 		}
                   1436: 	}
                   1437: 	el.className = ar.join(" ");
                   1438: };
                   1439: 
                   1440: HTMLArea._addClass = function(el, className) {
                   1441: 	// remove the class first, if already there
                   1442: 	HTMLArea._removeClass(el, className);
                   1443: 	el.className += " " + className;
                   1444: };
                   1445: 
                   1446: HTMLArea._hasClass = function(el, className) {
                   1447: 	if (!(el && el.className)) {
                   1448: 		return false;
                   1449: 	}
                   1450: 	var cls = el.className.split(" ");
                   1451: 	for (var i = cls.length; i > 0;) {
                   1452: 		if (cls[--i] == className) {
                   1453: 			return true;
                   1454: 		}
                   1455: 	}
                   1456: 	return false;
                   1457: };
                   1458: 
                   1459: HTMLArea.isBlockElement = function(el) {
                   1460: 	var blockTags = " body form textarea fieldset ul ol dl li div " +
                   1461: 		"p h1 h2 h3 h4 h5 h6 quote pre table thead " +
                   1462: 		"tbody tfoot tr td iframe address ";
                   1463: 	return (blockTags.indexOf(" " + el.tagName.toLowerCase() + " ") != -1);
                   1464: };
                   1465: 
                   1466: HTMLArea.needsClosingTag = function(el) {
                   1467: 	var closingTags = " script style div span tr td tbody table em strong font a ";
                   1468: 	return (closingTags.indexOf(" " + el.tagName.toLowerCase() + " ") != -1);
                   1469: };
                   1470: 
                   1471: // performs HTML encoding of some given string
                   1472: HTMLArea.htmlEncode = function(str) {
                   1473: 	// we don't need regexp for that, but.. so be it for now.
                   1474: 	str = str.replace(/&/ig, "&amp;");
                   1475: 	str = str.replace(/</ig, "&lt;");
                   1476: 	str = str.replace(/>/ig, "&gt;");
                   1477: 	str = str.replace(/\x22/ig, "&quot;");
                   1478: 	// \x22 means '"' -- we use hex reprezentation so that we don't disturb
                   1479: 	// JS compressors (well, at least mine fails.. ;)
                   1480: 	return str;
                   1481: };
                   1482: 
                   1483: // Retrieves the HTML code from the given node.	 This is a replacement for
                   1484: // getting innerHTML, using standard DOM calls.
                   1485: HTMLArea.getHTML = function(root, outputRoot) {
                   1486: 	var html = "";
                   1487: 	switch (root.nodeType) {
                   1488: 	    case 1: // Node.ELEMENT_NODE
                   1489: 	    case 11: // Node.DOCUMENT_FRAGMENT_NODE
                   1490: 		var closed;
                   1491: 		var i;
                   1492: 		if (outputRoot) {
                   1493: 			closed = (!(root.hasChildNodes() || HTMLArea.needsClosingTag(root)));
                   1494: 			html = "<" + root.tagName.toLowerCase();
                   1495: 			var attrs = root.attributes;
                   1496: 			for (i = 0; i < attrs.length; ++i) {
                   1497: 				var a = attrs.item(i);
                   1498: 				if (!a.specified) {
                   1499: 					continue;
                   1500: 				}
                   1501: 				var name = a.nodeName.toLowerCase();
                   1502: 				if (/_moz/.test(name)) {
                   1503: 					// Mozilla reports some special tags
                   1504: 					// here; we don't need them.
                   1505: 					continue;
                   1506: 				}
                   1507: 				var value;
                   1508: 				if (name != "style") {
                   1509: 					// IE5.5 reports 25 when cellSpacing is
                   1510: 					// 1; other values might be doomed too.
                   1511: 					// For this reason we extract the
                   1512: 					// values directly from the root node.
                   1513: 					// I'm starting to HATE JavaScript
                   1514: 					// development.  Browser differences
                   1515: 					// suck.
                   1516: 					if (typeof root[a.nodeName] != "undefined") {
                   1517: 						value = root[a.nodeName];
                   1518: 					} else {
                   1519: 						value = a.nodeValue;
                   1520: 					}
                   1521: 				} else { // IE fails to put style in attributes list
                   1522: 					// FIXME: cssText reported by IE is UPPERCASE
                   1523: 					value = root.style.cssText;
                   1524: 				}
                   1525: 				if (/_moz/.test(value)) {
                   1526: 					// Mozilla reports some special tags
                   1527: 					// here; we don't need them.
                   1528: 					continue;
                   1529: 				}
                   1530: 				html += " " + name + '="' + value + '"';
                   1531: 			}
                   1532: 			html += closed ? " />" : ">";
                   1533: 		}
                   1534: 		for (i = root.firstChild; i; i = i.nextSibling) {
                   1535: 			html += HTMLArea.getHTML(i, true);
                   1536: 		}
                   1537: 		if (outputRoot && !closed) {
                   1538: 			html += "</" + root.tagName.toLowerCase() + ">";
                   1539: 		}
                   1540: 		break;
                   1541: 	    case 3: // Node.TEXT_NODE
                   1542: 		html = HTMLArea.htmlEncode(root.data);
                   1543: 		break;
                   1544: 	    case 8: // Node.COMMENT_NODE
                   1545: 		html = "<!--" + root.data + "-->";
                   1546: 		break;		// skip comments, for now.
                   1547: 	}
                   1548: 	return html;
                   1549: };
                   1550: 
                   1551: // creates a rgb-style color from a number
                   1552: HTMLArea._makeColor = function(v) {
                   1553: 	if (typeof v != "number") {
                   1554: 		// already in rgb (hopefully); IE doesn't get here.
                   1555: 		return v;
                   1556: 	}
                   1557: 	// IE sends number; convert to rgb.
                   1558: 	var r = v & 0xFF;
                   1559: 	var g = (v >> 8) & 0xFF;
                   1560: 	var b = (v >> 16) & 0xFF;
                   1561: 	return "rgb(" + r + "," + g + "," + b + ")";
                   1562: };
                   1563: 
                   1564: // returns hexadecimal color representation from a number or a rgb-style color.
                   1565: HTMLArea._colorToRgb = function(v) {
                   1566: 	// returns the hex representation of one byte (2 digits)
                   1567: 	function hex(d) {
                   1568: 		return (d < 16) ? ("0" + d.toString(16)) : d.toString(16);
                   1569: 	};
                   1570: 
                   1571: 	if (typeof v == "number") {
                   1572: 		// we're talking to IE here
                   1573: 		var r = v & 0xFF;
                   1574: 		var g = (v >> 8) & 0xFF;
                   1575: 		var b = (v >> 16) & 0xFF;
                   1576: 		return "#" + hex(r) + hex(g) + hex(b);
                   1577: 	}
                   1578: 
                   1579: 	if (v.substr(0, 3) == "rgb") {
                   1580: 		// in rgb(...) form -- Mozilla
                   1581: 		var re = /rgb\s*\(\s*([0-9]+)\s*,\s*([0-9]+)\s*,\s*([0-9]+)\s*\)/;
                   1582: 		if (v.match(re)) {
                   1583: 			var r = parseInt(RegExp.$1);
                   1584: 			var g = parseInt(RegExp.$2);
                   1585: 			var b = parseInt(RegExp.$3);
                   1586: 			return "#" + hex(r) + hex(g) + hex(b);
                   1587: 		}
                   1588: 		// doesn't match RE?!  maybe uses percentages or float numbers
                   1589: 		// -- FIXME: not yet implemented.
                   1590: 		return null;
                   1591: 	}
                   1592: 
                   1593: 	if (v[0] == "#") {
                   1594: 		// already hex rgb (hopefully :D )
                   1595: 		return v;
                   1596: 	}
                   1597: 
                   1598: 	// if everything else fails ;)
                   1599: 	return null;
                   1600: };
                   1601: 
                   1602: // modal dialogs for Mozilla (for IE we're using the showModalDialog() call).
                   1603: 
                   1604: // receives an URL to the popup dialog and a function that receives one value;
                   1605: // this function will get called after the dialog is closed, with the return
                   1606: // value of the dialog.
                   1607: HTMLArea.prototype._popupDialog = function(url, action, init) {
                   1608: 	Dialog(this.popupURL(url), action, init);
                   1609: };
                   1610: 
                   1611: // paths
                   1612: 
                   1613: HTMLArea.prototype.imgURL = function(file, plugin) {
                   1614: 	if (typeof plugin == "undefined") {
                   1615: 		return this.config.editorURL + file;
                   1616: 	} else {
                   1617: 		return this.config.editorURL + "plugins/" + plugin + "/img/" + file;
                   1618: 	}
                   1619: };
                   1620: 
                   1621: HTMLArea.prototype.popupURL = function(file) {
                   1622: 	return this.config.editorURL + this.config.popupURL + file;
                   1623: };
                   1624: 
                   1625: // EOF
                   1626: // Local variables: //
                   1627: // c-basic-offset:8 //
                   1628: // indent-tabs-mode:t //
                   1629: // End: //

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>