Ignore:
Timestamp:
04/16/05 06:00:31 (15 years ago)
Author:
yermol
Message:

work-in-progress commit to Unified Backend branch.

added devutils directory for server side convenience scripts

svn_commit.pl front-end to svn commit to update about.html on submit.
makedocs.pl to generates JSDoc and PHPDoc documentation.

added Configure.php script to generate cli script paths and to set perms.
added index.html
added README, INSTALL, README_DEVELOPERS
added examples/simple_example.html
added ddt.js debug trace message to text area class.
htmlarea.js reworked

  • reorganized to group related pieces together.
  • JSDoc headers added to all methods
  • debugging trace messages added to entry points of most methods.
Location:
branches/unified_backend
Files:
25 added
1 deleted
4 edited

Legend:

Unmodified
Added
Removed
  • branches/unified_backend/examples/full_example-body.html

    r35 r72  
    3131  </script> 
    3232 
     33  <!--  load in debug trace message class --> 
     34 
     35  <script type="text/javascript" src="../ddt.js"></script> 
     36 
     37  <!-- create a global trace message object --> 
     38 
     39  <script type="text/javascript"> 
     40  // var startupDDT = new DDT( "startup_trace" ); 
     41  // startupDDT._ddtOnPopup(); 
     42  </script> 
     43   
    3344  <!-- Load up the actual editor core --> 
    3445  <script type="text/javascript" src="../htmlarea.js"></script> 
     
    152163 
    153164        <div id="lipsum" style="display:none"> 
    154           <p>Lorem ipsum dolor sit amet, consectetuer adipiscing elit. 
    155           Aliquam et tellus vitae justo varius placerat. Suspendisse iaculis 
    156           velit semper dolor. Donec gravida tincidunt mi. Curabitur tristique 
    157           ante elementum turpis. Aliquam nisl. Nulla posuere neque non 
    158           tellus. Morbi vel nibh. Cum sociis natoque penatibus et magnis dis 
    159           parturient montes, nascetur ridiculus mus. Nam nec wisi. In wisi. 
    160           Curabitur pharetra bibendum lectus.</p> 
     165          <p>This is an example of a Xinha editor. The two extra 
     166                         <b>&lt;&gt;</b> buttons on the right of the toolbar  
     167                         turn on debugging trace messages.</p> 
     168 
     169          <p>This branch of Xinha development is called the Unified 
     170                         Backend branch. It includes a number of changes over the 
     171                         trunk<p> 
    161172 
    162173          <ul> 
    163             <li> Phasellus et massa sed diam viverra semper.  </li> 
    164             <li> Mauris tincidunt felis in odio.              </li> 
    165             <li> Nulla placerat nunc ut pede.                 </li> 
    166             <li> Vivamus ultrices mi sit amet urna.           </li> 
    167             <li> Quisque sed augue quis nunc laoreet volutpat.</li> 
    168             <li> Nunc sit amet metus in tortor semper mattis. </li> 
     174            <li> JSDoc documentation headers on all functions (partially complete)  </li> 
     175            <li> _ddt() debugging trace messages added to most methods. </li> 
     176            <li> svn_commit.php front end to svn commit. </li> 
     177            <li> Configure.php script to generate server side scripts and permissions. </li> 
     178            <li> makedocs.pl script to generate documentation.</li> 
    169179          </ul> 
     180 
     181                         <p>For more information see the README.txt and README_DEVELOPERS.txt 
     182                         file from the Xinha_ub branch.</p> 
    170183        </div> 
    171184        <script src="full_example.js"></script> 
  • branches/unified_backend/examples/full_example-menu.html

    r43 r72  
    2323<body> 
    2424  <h1>Xinha Examples</h1> 
     25 
     26  <br> 
     27 
     28  <a target="_top" href="../index.html">Back to Index</a> 
    2529 
    2630  <p> 
  • branches/unified_backend/htmlarea.js

    r64 r72  
    1  
    2   /*--------------------------------------:noTabs=true:tabSize=2:indentSize=2:-- 
    3     --  Xinha (is not htmlArea) - http://xinha.gogo.co.nz/ 
    4     -- 
    5     --  Use of Xinha is granted by the terms of the htmlArea License (based on 
    6     --  BSD license)  please read license.txt in this package for details. 
    7     -- 
    8     --  Xinha was originally based on work by Mihai Bazon which is: 
    9     --      Copyright (c) 2003-2004 dynarch.com. 
    10     --      Copyright (c) 2002-2003 interactivetools.com, inc. 
    11     --      This copyright notice MUST stay intact for use. 
    12     -- 
    13     --  Developers - Coding Style: 
    14     --   For the sake of not committing needlessly conflicting changes, 
    15     -- 
    16     --   * New code to be indented with 2 spaces ("soft tab"). 
    17     --   * New code preferably uses BSD-Style Bracing 
    18     --      if(foo) 
    19     --      { 
    20     --        bar(); 
    21     --      } 
    22     --   * Don't change brace styles unless you're working on the non BSD-Style 
    23     --     area (so we don't get spurious changes in line numbering). 
    24     --   * Don't change indentation unless you're working on the badly indented 
    25     --     area (so we don't get spurious changes of large blocks of code). 
    26     --   * Jedit is the recommended editor, a comment of this format should be 
    27     --     included in the top 10 lines of the file (see the embedded edit mode) 
    28     -- 
    29     --  $HeadURL$ 
    30     --  $LastChangedDate$ 
    31     --  $LastChangedRevision$ 
    32     --  $LastChangedBy$ 
    33     --------------------------------------------------------------------------*/ 
    34  
    35 if (typeof _editor_url == "string") { 
     1// --------------------------------------:noTabs=true:tabSize=2:indentSize=2:-- 
     2 
     3/** 
     4* @fileoverview Xinha (is not htmlArea) {@link http://xinha.gogo.co.nz/ Xinha.org} 
     5* 
     6* Use of Xinha is granted by the terms of the htmlArea License (based on 
     7* BSD license)  please read license.txt in this package for details. 
     8*  
     9* Xinha was originally based on work by Mihai Bazon which is: 
     10*    Copyright (c) 2003-2004 dynarch.com. 
     11*    Copyright (c) 2002-2003 interactivetools.com, inc. 
     12*    This copyright notice MUST stay intact for use. 
     13* 
     14* $HeadURL$ 
     15* $LastChangedDate$ 
     16* $LastChangedBy$ 
     17* @version $LastChangedRevision$ 
     18* 
     19* @author Mihai Bazon. http://dynarch.com/mishoo 
     20* @author James Sleeman http://code.gogo.co.nz Xinha Branch 
     21* @author Yermo Lamers http://www.formvista.com (unified backend modifications) 
     22*/ 
     23 
     24/** 
     25* Xinha Unified Backend Branch 
     26* 
     27* This development version of Xinha is the Unified Backend Branch where 
     28* all client request to the server are routed through a single server 
     29* side script 
     30*/ 
     31 
     32// sections marked with DDT open and close brackets are stripped out by  
     33// the make_runtime.sh utility. Individual _ddt() calls are also automatically 
     34// stripped out. 
     35 
     36// -------------------------------------------------------------------------- 
     37//                              INITIAL SETUP 
     38// -------------------------------------------------------------------------- 
     39 
     40if (typeof _editor_url == "string")  
     41  { 
    3642  // Leave exactly one backslash at the end of _editor_url 
    3743  _editor_url = _editor_url.replace(/\x2f*$/, '/'); 
    38 } else { 
     44  }  
     45else  
     46  { 
    3947  alert("WARNING: _editor_url is not set!  You should set this variable to the editor files path; it should preferably be an absolute path, like in '/htmlarea/', but it can be relative if you prefer.  Further we will try to load the editor files correctly but we'll probably fail."); 
    4048  _editor_url = ''; 
    41 } 
    42  
    43 // make sure we have a language 
    44 if (typeof _editor_lang == "string") { 
     49  } 
     50 
     51// make sure we have a language. Default to english if not. 
     52 
     53if (typeof _editor_lang == "string")  
     54  { 
    4555  _editor_lang = _editor_lang.toLowerCase(); 
    46 } else { 
     56  }  
     57else  
     58  { 
    4759  _editor_lang = "en"; 
    48 } 
     60  } 
     61 
     62/** 
     63* the list of HTML Area editors on the page. May be multiple editors. 
     64*/ 
    4965 
    5066var __htmlareas = [ ]; 
     67 
     68/** 
     69* variable used to pass the object to the popup editor window. 
     70*/ 
     71 
     72HTMLArea._object = null; 
     73 
     74/** 
     75* uniq_count 
     76* 
     77* @see HTMLArea.uniq() 
     78*/ 
     79 
     80HTMLArea.uniq_count = 0; 
     81 
     82/** 
     83* blockTags 
     84*/ 
     85 
     86HTMLArea._blockTags = " body form textarea fieldset ul ol dl li div " + 
     87"p h1 h2 h3 h4 h5 h6 quote pre table thead " + 
     88"tbody tfoot tr td th iframe address blockquote"; 
     89 
     90/** 
     91* paragraph container tags 
     92*/ 
     93 
     94HTMLArea._paraContainerTags = " body td th caption fieldset div"; 
     95 
     96/** 
     97* closing tags 
     98*/ 
     99 
     100HTMLArea._closingTags = " head script style div span tr td tbody table em strong b i code cite dfn abbr acronym font a title "; 
    51101 
    52102// browser identification 
     
    59109HTMLArea.is_gecko  = (navigator.product == "Gecko"); 
    60110 
    61 // Creates a new HTMLArea object.  Tries to replace the textarea with the given 
    62 // ID with it. 
    63 function HTMLArea(textarea, config) { 
    64   if (HTMLArea.checkSupportedBrowser()) { 
    65     if (typeof config == "undefined") { 
     111// cache some regexps that we'll use often. 
     112 
     113HTMLArea.RE_tagName = /(<\/|<)\s*([^ \t\n>]+)/ig; 
     114HTMLArea.RE_doctype = /(<!doctype((.|\n)*?)>)\n?/i; 
     115HTMLArea.RE_head    = /<head>((.|\n)*?)<\/head>/i; 
     116HTMLArea.RE_body    = /<body[^>]*>((.|\n)*?)<\/body>/i; 
     117HTMLArea.RE_Specials = /([\/\^$*+?.()|{}[\]])/g; 
     118HTMLArea.RE_email    = /[a-z0-9_]{3,}@[a-z0-9_-]{2,}(\.[a-z0-9_-]{2,})+/i; 
     119HTMLArea.RE_url      = /(https?:\/\/)?(([a-z0-9_]+:[a-z0-9_]+@)?[a-z0-9_-]{2,}(\.[a-z0-9_-]{2,}){2,}(:[0-9]+)?(\/\S+)*)/i; 
     120 
     121/** 
     122* onLoad handler 
     123* 
     124* I'm assuming this is set to a NULL function so it can easily be overridden 
     125* from the calling page. This function is called from init() 
     126* 
     127* @see #HTMLArea.init 
     128*/ 
     129 
     130HTMLArea.onload = function(){}; 
     131 
     132/** 
     133* list of scripts to load during init() 
     134* 
     135* @see #HTMLArea.init 
     136* @see #HTMLArea.loadScript 
     137*/ 
     138 
     139HTMLArea._scripts = []; 
     140 
     141// --------------------------------------------------- 
     142 
     143// [STRIP 
     144 
     145/** 
     146* ddt shorthand for startup code 
     147* 
     148* A number of functions are called prior to HTMLArea object 
     149* construction. This is a shorthand to clean up the calls in 
     150* all these methods.  
     151*/ 
     152 
     153HTMLArea._ddt = function( msg ) 
     154  { 
     155 
     156  if ( typeof startupDDT != 'undefined' ) 
     157     startupDDT._ddt( msg ); 
     158 
     159  } 
     160// STRIP] 
     161 
     162// ---------------------------------------------------------------- 
     163//            HTMLArea (i.e. XINHA) CONFIG CLASS 
     164// ---------------------------------------------------------------- 
     165 
     166/** 
     167* Configuration Constructor. Sets default config values which can be  
     168* overridden from the calling page. 
     169* 
     170* @class This class manages an HTMLArea configuration. 
     171* 
     172* @constructor 
     173* @todo turning on ddt object here causes HTMLArea.cloneObject() to fail withh an "obj.constructor has no properties" errors. 
     174*/ 
     175 
     176HTMLArea.Config = function ()  
     177{ 
     178 
     179  var cfg = this; 
     180  this.version = "3.0"; 
     181 
     182  /** 
     183  * config class trace object, if we have a textarea defined. 
     184  * 
     185  * The idea here is to share the trace message box with the Xinha instance 
     186  * covering this textarea. 
     187  */ 
     188 
     189  // [STRIP 
     190 
     191  // FIXME: see todo above - this causes an exception. So for the moment 
     192  // we have no debugging in the Config object. 
     193 
     194  //if ( arguments[0] != null ) 
     195  //  this.ddt = new DDT( arguments[0] ) 
     196  //else 
     197  //  this.ddt = new DDT( "HTMLArea.Config" ); 
     198  // 
     199  // uncomment this to turn on Config trace messages 
     200  // this.ddt._ddtOn(); 
     201  // STRIP] 
     202 
     203  /** 
     204  * constrain the width of the editor to the toolbar 
     205  */ 
     206 
     207  this.width = "toolbar"; 
     208 
     209  /** 
     210  * auto-size the height 
     211  */ 
     212 
     213  this.height = "auto"; 
     214 
     215  /** 
     216  * default language of the editor 
     217  */ 
     218 
     219  this.lang = "en"; 
     220 
     221  /** 
     222  * lcBackend - what's this used for? 
     223  */ 
     224 
     225  this.lcBackend = "lcbackend.php?lang=$lang&context=$context"; 
     226 
     227  /** 
     228  * enable creation of a status bar? 
     229  */ 
     230 
     231  this.statusBar = true; 
     232 
     233  /** 
     234  * intercept ^V? 
     235  * 
     236  * intercept ^V and use the HTMLArea paste command 
     237  * If false, then passes ^V through to browser editor widget 
     238  */ 
     239 
     240  this.htmlareaPaste = false; 
     241 
     242  /** 
     243  * paragraph handler to use. 
     244  * 
     245  * set to 'built-in', 'dirty' or 'best' 
     246  * built-in: will (may) use 'br' instead of 'p' tags 
     247  * dirty   : will use p and work good enough for the majority of cases, 
     248  * best    : works the best, but it's about 12kb worth of javascript 
     249  * and will probably be slower than 'dirty'.  This is the "EnterParagraphs" 
     250  * plugin from "hipikat", rolled in to be part of the core code 
     251  */ 
     252 
     253  this.mozParaHandler = 'best';  
     254 
     255  /** 
     256  * maximum size of the undo queue 
     257  * 
     258  * @see HTMLArea.prototype._undoTakeSnapshot() 
     259  */ 
     260 
     261  this.undoSteps = 20; 
     262 
     263  /** 
     264  * the time interval at which undo samples are taken 
     265  * 
     266  * @see HTMLArea.prototype.updateToolbar() 
     267  */ 
     268 
     269  this.undoTimeout = 500;       // 1/2 sec. 
     270 
     271  /** 
     272  * include toolbar in size calculation 
     273  */ 
     274 
     275  this.sizeIncludesToolbar = true; 
     276 
     277  /** 
     278  * retrieve full HTML? 
     279  * 
     280  * if true then HTMLArea will retrieve the full HTML, starting with the 
     281  * <HTML> tag. 
     282  */ 
     283 
     284  this.fullPage = false; 
     285 
     286  /** 
     287  * style included in the iframe document 
     288  */ 
     289 
     290  this.pageStyle = ""; 
     291 
     292  /** 
     293  * external stylesheets to load (REFERENCE THESE ABSOLUTELY) 
     294  * 
     295  * @see http://xinha.gogo.co.nz/punbb/viewtopic.php?id=143 
     296  */  
     297 
     298  this.pageStyleSheets = [ ]; 
     299 
     300  /** 
     301  * specify a base href for relative links 
     302  * 
     303  * @see HTMLArea.prototype.initIframe() 
     304  * @see HTMLArea.prototype.fixRelativeLinks() 
     305  */ 
     306 
     307  this.baseHref  = null; 
     308 
     309  /** 
     310  * force hrefs to be relative? 
     311  * 
     312  * we can strip the base href out of relative links to leave them relative, reason for this 
     313  * especially if you don't specify a baseHref is that mozilla at least (& IE ?) will prefix 
     314  * the baseHref to any relative links to make them absolute, which isn't what you want most the time. 
     315  * 
     316  * @see HTMLArea.prototype.fixRelativeLinks() 
     317  */ 
     318 
     319  this.stripBaseHref = true; 
     320 
     321  /** 
     322  * remove local anchors? 
     323  * 
     324  * and we can strip the url of the editor page from named links (eg <a href="#top">...</a>) 
     325  * reason for this is that mozilla at least (and IE ?) prefixes location.href to any 
     326  * that don't have a url prefixing them 
     327  */ 
     328 
     329  this.stripSelfNamedAnchors = true; 
     330 
     331  /** 
     332  * special replacements 
     333  * 
     334  * sometimes we want to be able to replace some string in the html comng in and going out 
     335  * so that in the editor we use the "internal" string, and outside and in the source view 
     336  * we use the "external" string  this is useful for say making special codes for 
     337  * your absolute links, your external string might be some special code, say "{server_url}" 
     338  * an you say that the internal represenattion of that should be http://your.server/ 
     339  * 
     340  * @see HTMLArea.prototype.outwardSpecialReplacements() 
     341  * @see HTMLArea.prototype.inwardSpecialReplacements() 
     342  */ 
     343 
     344  this.specialReplacements = { }; // { 'external_string' : 'internal_string' } 
     345 
     346  /** 
     347  * set to true if you want Word code to be cleaned upon Paste 
     348  * 
     349  * @see HTMLArea.prototype.execCommand() 
     350  */ 
     351 
     352  this.killWordOnPaste = true; 
     353 
     354  /** 
     355  * enable the 'Target' field in the Make Link dialog 
     356  * 
     357  * this is for the simple default internal Link dialog, not the Linker plugin. 
     358  * 
     359  * @see HTMLArea.prototype._createLink() 
     360  */ 
     361 
     362  this.makeLinkShowsTarget = true; 
     363 
     364  /** 
     365  * BaseURL included in the iframe document 
     366  * 
     367  * @see HTMLArea.prototype._insertImage() 
     368  * @see HTMLArea.prototype.stripBaseURL() 
     369  */ 
     370 
     371  this.baseURL = document.baseURI || document.URL; 
     372 
     373  // make sure the baseURL contains a trailing / 
     374 
     375  if (this.baseURL && this.baseURL.match(/(.*)\/([^\/]+)/)) 
     376    this.baseURL = RegExp.$1 + "/"; 
     377 
     378  /** 
     379  * CharSet of the iframe, default is the charset of the document 
     380  */ 
     381 
     382  this.charSet = HTMLArea.is_gecko ? document.characterSet : document.charset; 
     383 
     384  /**  
     385  * relative location of default editor images/buttons 
     386  */ 
     387 
     388  this.imgURL = "images/"; 
     389 
     390  /**  
     391  * relative location of popups 
     392  */ 
     393 
     394  this.popupURL = "popups/"; 
     395 
     396  /** 
     397  * absolute location of help file 
     398  */ 
     399 
     400  this.helpURL  = _editor_url + "reference.html"; 
     401 
     402  /** 
     403  * remove tags? 
     404  * 
     405  *  (these have to be a regexp, or null if this functionality is not desired) 
     406  */ 
     407 
     408  this.htmlRemoveTags = null; 
     409 
     410  /**  
     411  * default toolbar. 
     412  * 
     413  * Customize the toolbar contents in an external file (i.e. the one calling HTMLArea) 
     414  * Do not edit the toolbar here. 
     415  *  
     416  * This toolbar definition is used all over the place. 
     417  */ 
     418 
     419  this.toolbar = 
     420    [ 
     421      ["popupeditor","separator"], 
     422      ["formatblock","fontname","fontsize","bold","italic","underline","strikethrough","separator"], 
     423      ["forecolor","hilitecolor","textindicator","separator"], 
     424      ["subscript","superscript"], 
     425      ["linebreak","justifyleft","justifycenter","justifyright","justifyfull","separator"], 
     426      ["insertorderedlist","insertunorderedlist","outdent","indent","separator"], 
     427      ["inserthorizontalrule","createlink","insertimage","inserttable","separator"], 
     428      ["undo","redo"], (HTMLArea.is_gecko ? [] : ["cut","copy","paste"]),["separator"], 
     429      ["killword","removeformat","toggleborders","lefttoright", "righttoleft", "separator","htmlmode","about"] 
     430 
     431                // [STRIP 
     432                ,["object_debug_toggle"] 
     433                ,["static_debug_toggle"] 
     434                // STRIP] 
     435 
     436    ]; 
     437 
     438  /** 
     439  * Dimensions of the "Right Side" panel, when present 
     440  * 
     441  * @see   HTMLArea.prototype.setInnerSize() 
     442  */ 
     443 
     444  this.panel_dimensions = 
     445    { 
     446    left:   '200px', // Width 
     447    right:  '200px', 
     448    top:    '100px', // Height 
     449    bottom: '100px' 
     450    }; 
     451 
     452  /** 
     453  * fonts list for font dropdown. 
     454  * 
     455  * @see HTMLArea.prototype._createToolbar1() 
     456  * @see HTMLArea.prototype.updateToolbar() 
     457  * @see HTMLArea.prototype._comboSelected() 
     458  */ 
     459 
     460  this.fontname =  
     461    { 
     462    "&mdash; font &mdash;":         '', 
     463    "Arial":       'arial,helvetica,sans-serif', 
     464    "Courier New":         'courier new,courier,monospace', 
     465    "Georgia":     'georgia,times new roman,times,serif', 
     466    "Tahoma":      'tahoma,arial,helvetica,sans-serif', 
     467    "Times New Roman": 'times new roman,times,serif', 
     468    "Verdana":     'verdana,arial,helvetica,sans-serif', 
     469    "impact":      'impact', 
     470    "WingDings":           'wingdings' 
     471    }; 
     472 
     473  /** 
     474  * fontsize dropdown 
     475  * 
     476  * @see HTMLArea.prototype._createToolbar1() 
     477  * @see HTMLArea.prototype.updateToolbar() 
     478  * @see HTMLArea.prototype._comboSelected() 
     479  */ 
     480 
     481  this.fontsize =  
     482    { 
     483    "&mdash; size &mdash;"  : "", 
     484    "1 (8 pt)" : "1", 
     485    "2 (10 pt)": "2", 
     486    "3 (12 pt)": "3", 
     487    "4 (14 pt)": "4", 
     488    "5 (18 pt)": "5", 
     489    "6 (24 pt)": "6", 
     490    "7 (36 pt)": "7" 
     491    }; 
     492 
     493  /** 
     494  * format block dropdown 
     495  */ 
     496 
     497  this.formatblock =  
     498    { 
     499    "&mdash; format &mdash;"  : "", 
     500    "Heading 1": "h1", 
     501    "Heading 2": "h2", 
     502    "Heading 3": "h3", 
     503    "Heading 4": "h4", 
     504    "Heading 5": "h5", 
     505    "Heading 6": "h6", 
     506    "Normal"   : "p", 
     507    "Address"  : "address", 
     508    "Formatted": "pre" 
     509    }; 
     510 
     511  /** 
     512  * custom dropdown boxes. 
     513  */  
     514 
     515  this.customSelects = {}; 
     516 
     517  /** 
     518  * default cut_copy_paste handler 
     519  */ 
     520 
     521  function cut_copy_paste(e, cmd, obj)  
     522    { 
     523    e.execCommand(cmd); 
     524    }; 
     525 
     526  /** 
     527  * turns on original rudimentary debugging 
     528  */ 
     529 
     530  this.debug = true; 
     531 
     532  /** 
     533  * various often used URI's 
     534  */ 
     535 
     536  this.URIs =  
     537    { 
     538    "blank": "popups/blank.html", 
     539    "link": "link.html", 
     540    "insert_image": "insert_image.html", 
     541    "insert_table": "insert_table.html", 
     542    "select_color": "select_color.html", 
     543    "fullscreen": "fullscreen.html", 
     544    "about": "about.html", 
     545    "mozilla_security": "http://mozilla.org/editor/midasdemo/securityprefs.html" 
     546    }; 
     547 
     548  /** 
     549  * Custom Button List 
     550  * 
     551  * ADDING CUSTOM BUTTONS: please read below! 
     552  * 
     553  * format of the btnList elements is "ID: [ ToolTip, Icon, Enabled in text mode?, ACTION ]" 
     554  *    - ID: unique ID for the button.  If the button calls document.execCommand 
     555  *         it's wise to give it the same name as the called command. 
     556  *    - ACTION: function that gets called when the button is clicked. 
     557  *              it has the following prototype: 
     558  *                 function(editor, buttonName) 
     559  *              - editor is the HTMLArea object that triggered the call 
     560  *              - buttonName is the ID of the clicked button 
     561  *              These 2 parameters makes it possible for you to use the same 
     562  *              handler for more HTMLArea objects or for more different buttons. 
     563  *    - ToolTip: tooltip, will be translated below 
     564  *    - Icon: path to an icon image file for the button 
     565  *            OR; you can use an 18x18 block of a larger image by supllying an array 
     566  *            that has three elemtents, the first is the larger image, the second is the column 
     567  *            the third is the row.  The ros and columns numbering starts at 0 but there is 
     568  *            a header row and header column which have numbering to make life easier. 
     569  *            See images/buttons_main.gif to see how it's done. 
     570  *    - Enabled in text mode: if false the button gets disabled for text-only mode; otherwise enabled all the time. 
     571  * 
     572  * ---------------------------------- 
     573  * Old Interactivetools.com Comments- 
     574  * 
     575  * Example on how to add a custom button when you construct the HTMLArea: 
     576  * 
     577  *   var editor = new HTMLArea("your_text_area_id"); 
     578  *   var cfg = editor.config; // this is the default configuration 
     579  *   cfg.btnList["my-hilite"] = 
     580  *     [ function(editor) { editor.surroundHTML('<span style="background:yellow">', '</span>'); }, // action 
     581  *       "Highlight selection", // tooltip 
     582  *       "my_hilite.gif", // image 
     583  *       false // disabled in text mode 
     584  *     ]; 
     585  *   cfg.toolbar.push(["linebreak", "my-hilite"]); // add the new button to the toolbar 
     586  * ---------------------------------- 
     587  * 
     588  * An alternate (also more convenient and recommended) way to 
     589  * accomplish this is to use the registerButton function below. 
     590  * 
     591  * @see HTMLArea.Config.prototype.registerButton() 
     592  * @see HTMLArea.prototype._createToolbar1() 
     593  */ 
     594 
     595  this.btnList =  
     596    { 
     597    bold:          [ "Bold",   ["ed_buttons_main.gif",3,2], false, function(e) {e.execCommand("bold");} ], 
     598    italic:        [ "Italic", ["ed_buttons_main.gif",2,2], false, function(e) {e.execCommand("italic");} ], 
     599    underline:     [ "Underline", ["ed_buttons_main.gif",2,0], false, function(e) {e.execCommand("underline");} ], 
     600    strikethrough: [ "Strikethrough", ["ed_buttons_main.gif",3,0], false, function(e) {e.execCommand("strikethrough");} ], 
     601    subscript:     [ "Subscript", ["ed_buttons_main.gif",3,1], false, function(e) {e.execCommand("subscript");} ], 
     602    superscript:   [ "Superscript", ["ed_buttons_main.gif",2,1], false, function(e) {e.execCommand("superscript");} ], 
     603 
     604    justifyleft:   [ "Justify Left", ["ed_buttons_main.gif",0,0], false, function(e) {e.execCommand("justifyleft");} ], 
     605    justifycenter: [ "Justify Center", ["ed_buttons_main.gif",1,1], false, function(e){e.execCommand("justifycenter");}], 
     606    justifyright: [ "Justify Right", ["ed_buttons_main.gif",1,0], false, function(e) {e.execCommand("justifyright");} ], 
     607    justifyfull: [ "Justify Full", ["ed_buttons_main.gif",0,1], false, function(e) {e.execCommand("justifyfull");} ], 
     608 
     609 
     610    orderedlist: [ "Ordered List", ["ed_buttons_main.gif",0,3], false, function(e) {e.execCommand("insertorderedlist");} ], 
     611    unorderedlist: [ "Bulleted List", ["ed_buttons_main.gif",1,3], false, function(e) {e.execCommand("insertunorderedlist");} ], 
     612    insertorderedlist: [ "Ordered List", ["ed_buttons_main.gif",0,3], false, function(e) {e.execCommand("insertorderedlist");} ], 
     613    insertunorderedlist: [ "Bulleted List", ["ed_buttons_main.gif",1,3], false, function(e) {e.execCommand("insertunorderedlist");} ], 
     614 
     615    outdent: [ "Decrease Indent", ["ed_buttons_main.gif",1,2], false, function(e) {e.execCommand("outdent");} ], 
     616    indent: [ "Increase Indent",["ed_buttons_main.gif",0,2], false, function(e) {e.execCommand("indent");} ], 
     617    forecolor: [ "Font Color", ["ed_buttons_main.gif",3,3], false, function(e) {e.execCommand("forecolor");} ], 
     618    hilitecolor: [ "Background Color", ["ed_buttons_main.gif",2,3], false, function(e) {e.execCommand("hilitecolor");} ], 
     619 
     620    undo: [ "Undoes your last action", ["ed_buttons_main.gif",4,2], false, function(e) {e.execCommand("undo");} ], 
     621    redo: [ "Redoes your last action", ["ed_buttons_main.gif",5,2], false, function(e) {e.execCommand("redo");} ], 
     622    cut: [ "Cut selection", ["ed_buttons_main.gif",5,0], false, cut_copy_paste ], 
     623    copy: [ "Copy selection", ["ed_buttons_main.gif",4,0], false, cut_copy_paste ], 
     624    paste: [ "Paste from clipboard", ["ed_buttons_main.gif",4,1], false, cut_copy_paste ], 
     625 
     626 
     627 
     628    inserthorizontalrule: [ "Horizontal Rule", ["ed_buttons_main.gif",6,0], false, function(e) {e.execCommand("inserthorizontalrule");} ], 
     629    createlink: [ "Insert Web Link", ["ed_buttons_main.gif",6,1], false, function(e) {e._createLink();} ], 
     630    insertimage: [ "Insert/Modify Image", ["ed_buttons_main.gif",6,3], false, function(e) {e.execCommand("insertimage");} ], 
     631    inserttable: [ "Insert Table", ["ed_buttons_main.gif",6,2], false, function(e) {e.execCommand("inserttable");} ], 
     632 
     633 
     634    htmlmode: [ "Toggle HTML Source", ["ed_buttons_main.gif",7,0], true, function(e) {e.execCommand("htmlmode");} ], 
     635    toggleborders: [ "Toggle Borders", ["ed_buttons_main.gif",7,2], false, function(e) { e._toggleBorders() } ], 
     636    print:         [ "Print document", ["ed_buttons_main.gif",8,1], false, function(e) {e._iframe.contentWindow.print();} ], 
     637    popupeditor: [ "Enlarge Editor", "fullscreen_maximize.gif", true, 
     638      function(e, objname, obj) 
     639      { 
     640        e.execCommand("popupeditor"); 
     641      } ], 
     642    about: [ "About this editor", ["ed_buttons_main.gif",8,2], true, function(e) {e.execCommand("about");} ], 
     643    showhelp: [ "Help using editor", ["ed_buttons_main.gif",9,2], true, function(e) {e.execCommand("showhelp");} ], 
     644 
     645    splitblock:    [ "Split Block", "ed_splitblock.gif", false, function(e) {e._splitBlock();} ], 
     646    lefttoright: [ "Direction left to right", ["ed_buttons_main.gif",0,4], false, function(e) {e.execCommand("lefttoright");} ], 
     647    righttoleft: [ "Direction right to left", ["ed_buttons_main.gif",1,4], false, function(e) {e.execCommand("righttoleft");} ], 
     648 
     649    wordclean:     [ "MS Word Cleaner", ["ed_buttons_main.gif",5,3], false, function(e) {e._wordClean();} ], 
     650    clearfonts:    [ "Clear Inline Font Specifications", ["ed_buttons_main.gif",5,4], false, function(e) {e._clearFonts();} ], 
     651    removeformat:  [ "Remove formatting", ["ed_buttons_main.gif",4,4], false, function(e) {e.execCommand("removeformat");} ], 
     652    killword:      [ "Clear MSOffice tags", ["ed_buttons_main.gif",4,3], false, function(e) {e.execCommand("killword");} ] 
     653 
     654    // [STRIP 
     655    ,object_debug_toggle: [ "Toggle Debug of HTMLArea object", ["ed_buttons_main.gif",7,0], true, function(e) {e.ddt._ddtToggle();} ] 
     656    ,static_debug_toggle: [ "Toggle Debug of HTMLArea static methods (popup)", ["ed_buttons_main.gif",7,0], true, function(e) {e.ddt._ddtTogglePopup();} ] 
     657         // STRIP] 
     658 
     659    };  // end of btnList definition 
     660 
     661  // initialize tooltips from the I18N module and generate correct image path 
     662 
     663  for (var i in this.btnList)  
     664    { 
     665    var btn = this.btnList[i]; 
     666 
     667    if(typeof btn[1] != 'string') 
     668      { 
     669      btn[1][0] = _editor_url + this.imgURL + btn[1][0]; 
     670      } 
     671    else 
     672      { 
     673      btn[1] = _editor_url + this.imgURL + btn[1]; 
     674      } 
     675 
     676    btn[0] = HTMLArea._lc(btn[0]); //initialize tooltip 
     677 
     678    } 
     679 
     680  };  // end of HTMLArea.Config() 
     681 
     682// ---------------------------------------------- 
     683 
     684/**  
     685* register new button Helper function  
     686*  
     687* register a new button with the configuration.  It can be 
     688* called with all 5 arguments, or with only one (first one).  When called with 
     689* only one argument it must be an object with the following properties: id, 
     690* tooltip, image, textMode, action.  Examples: 
     691* 
     692* 1. config.registerButton("my-hilite", "Hilite text", "my-hilite.gif", false, function(editor) {...}); 
     693* 2. config.registerButton({ 
     694*      id       : "my-hilite",      // the ID of your button 
     695*      tooltip  : "Hilite text",    // the tooltip 
     696*      image    : "my-hilite.gif",  // image to be displayed in the toolbar 
     697*      textMode : false,            // disabled in text mode 
     698*      action   : function(editor) { // called when the button is clicked 
     699*                   editor.surroundHTML('<span class="hilite">', '</span>'); 
     700*                 }, 
     701*      context  : "p"               // will be disabled if outside a <p> element 
     702*    }); 
     703*/ 
     704 
     705HTMLArea.Config.prototype.registerButton = function(id, tooltip, image, textMode, action, context)  
     706  { 
     707 
     708  var the_id; 
     709  if (typeof id == "string")  
     710    { 
     711    the_id = id; 
     712    }  
     713  else if (typeof id == "object")  
     714    { 
     715    the_id = id.id; 
     716    }  
     717  else  
     718    { 
     719    alert("ERROR [HTMLArea.Config::registerButton]:\ninvalid arguments"); 
     720    return false; 
     721    } 
     722 
     723  // check for existing id 
     724  
     725  if (typeof this.customSelects[the_id] != "undefined")  
     726    { 
     727    // alert("WARNING [HTMLArea.Config::registerDropdown]:\nA dropdown with the same ID already exists."); 
     728   
     729    HTMLArea._ddt("registerButton(): WARNING [HTMLArea.Config::registerDropdown]: A dropdown with the same ID already exists."); 
     730    } 
     731 
     732  if (typeof this.btnList[the_id] != "undefined")  
     733    { 
     734    // alert("WARNING [HTMLArea.Config::registerDropdown]:\nA button with the same ID already exists."); 
     735    HTMLArea._ddt( "registerbutton(): WARNING [HTMLArea.Config::registerDropdown]:A button with the same ID already exists."); 
     736    } 
     737 
     738  switch (typeof id)  
     739    { 
     740    case "string": this.btnList[id] = [ tooltip, image, textMode, action, context ]; break; 
     741    case "object": this.btnList[id.id] = [ id.tooltip, id.image, id.textMode, id.action, id.context ]; break; 
     742    } 
     743 
     744  };  // end of registerButton() 
     745 
     746// ------------------------------------------------- 
     747 
     748/**  
     749* registerDropdown 
     750* 
     751* The following helper function registers a dropdown box with the editor 
     752* configuration.  You still have to add it to the toolbar, same as with the 
     753* buttons.  Call it like this: 
     754* 
     755* FIXME: add example 
     756*/ 
     757 
     758HTMLArea.Config.prototype.registerDropdown = function(object)  
     759  { 
     760  // check for existing id 
     761 
     762  if (typeof this.customSelects[object.id] != "undefined")  
     763    { 
     764         // alert("WARNING [HTMLArea.Config::registerDropdown]:\nA dropdown with the same ID already exists."); 
     765    HTMLArea._ddt("registerDropdown(): WARNING [HTMLArea.Config::registerDropdown]:\nA dropdown with the same ID already exists."); 
     766    } 
     767 
     768  if (typeof this.btnList[object.id] != "undefined")  
     769    { 
     770         // alert("WARNING [HTMLArea.Config::registerDropdown]:\nA button with the same ID already exists."); 
     771    HTMLArea._ddt("registerDropdown(): WARNING [HTMLArea.Config::registerDropdown]:\nA button with the same ID already exists."); 
     772    } 
     773 
     774  this.customSelects[object.id] = object; 
     775 
     776  };  // end of registerDropdown() 
     777 
     778// ------------------------------------------------------------- 
     779 
     780/**  
     781* Remove some buttons or drop-down boxes from the toolbar. 
     782* 
     783* Pass as the only parameter a string containing button/drop-down names 
     784* delimited by spaces.  Note that the string should also begin with a space 
     785* and end with a space.  Example: 
     786* 
     787*   config.hideSomeButtons(" fontname fontsize textindicator "); 
     788* 
     789* It's useful because it's easier to remove stuff from the default toolbar than 
     790* create a brand new toolbar ;-) 
     791*/ 
     792 
     793HTMLArea.Config.prototype.hideSomeButtons = function(remove)  
     794  { 
     795  var toolbar = this.toolbar; 
     796  for (var i = toolbar.length; --i >= 0;)  
     797    { 
     798    var line = toolbar[i]; 
     799    for (var j = line.length; --j >= 0; )  
     800           { 
     801      if (remove.indexOf(" " + line[j] + " ") >= 0)  
     802                  { 
     803        var len = 1; 
     804        if (/separator|space/.test(line[j + 1]))  
     805                    { 
     806          len = 2; 
     807          } 
     808 
     809        line.splice(j, len); 
     810        } 
     811      } 
     812    } 
     813  }; 
     814 
     815 
     816// ------------------------------------------------------------------------ 
     817//          HTMLAREA CLASS METHODS - BEFORE HTMLArea OBJECT IS BUILT 
     818// ------------------------------------------------------------------------ 
     819 
     820/** 
     821* loadScript  
     822* 
     823* add a script to the list of files to be loaded during init() 
     824* 
     825* @see HTMLArea.init() 
     826*/ 
     827 
     828HTMLArea.loadScript = function(url, plugin)  
     829  { 
     830 
     831  HTMLArea._ddt( "loadScript(): Top with url '" + url + "' and plugin '" + plugin + "'" ); 
     832 
     833  if (plugin) 
     834    { 
     835    url = HTMLArea.getPluginDir(plugin) + '/' + url; 
     836    } 
     837 
     838  this._scripts.push(url); 
     839 
     840  }; 
     841 
     842// [STRIP 
     843// 
     844// Load in the debugging trace class if it has not already been loaded 
     845 
     846if ( typeof DDT == 'undefined' ) 
     847  HTMLArea.loadScript(_editor_url + "ddt.js"); 
     848// STRIP] 
     849 
     850HTMLArea.loadScript(_editor_url + "dialog.js"); 
     851HTMLArea.loadScript(_editor_url + "inline-dialog.js"); 
     852HTMLArea.loadScript(_editor_url + "popupwin.js"); 
     853 
     854// --------------------------------------------------- 
     855 
     856/** 
     857* actually does the scripts loading and fires onLoad handler 
     858* 
     859* Loads the scripts scheduled by loadScript(). The algorithm here  
     860* puts the loadNextScript() temporary function into the onreadystatechange 
     861* (for MSIE) or onLoad (for Gecko) event handler for each script it loads.  
     862* Thus, when a script is completely loaded the onreadystatechange/onLoad handler  
     863* is called which then loads in the next script and so forth. This makes  
     864* certain that each script is completely loaded before the next one is started. 
     865* 
     866* HTMLArea.init() is called at the bottom of this file ( htmlarea.js - see last line of file)  
     867* and as a result the HTMLArea object is not yet constructed. 
     868* 
     869* @see loadScript() 
     870*/ 
     871 
     872HTMLArea.init = function()  
     873  { 
     874 
     875  HTMLArea._ddt( "init(): top" ); 
     876 
     877  var head = document.getElementsByTagName("head")[0]; 
     878  var current = 0; 
     879  var savetitle = document.title; 
     880 
     881  // the event handler we should plug loadNextScript into. Depends 
     882  // on the browser we are using. 
     883 
     884  var evt = HTMLArea.is_ie ? "onreadystatechange" : "onload"; 
     885 
     886  // plugged in the onLoad or onreadystatechange handler to load the 
     887  // next script if any. 
     888 
     889  function loadNextScript()  
     890    { 
     891 
     892    // if we are MSIE and we are not ready to load a script (finished 
     893    // loading the last script?) just return. 
     894 
     895    if (current > 0 && HTMLArea.is_ie && 
     896        !/loaded|complete/.test(window.event.srcElement.readyState)) 
     897      { 
     898 
     899      // MSIE is not ready. 
     900 
     901      HTMLArea._ddt( "init(): MSIE ready state not ready '" + window.event.srcElement.readyState + "'" ); 
     902 
     903      return; 
     904      } 
     905 
     906    // if there are still scripts to load load them. 
     907 
     908    if (current < HTMLArea._scripts.length)  
     909      { 
     910      var url = HTMLArea._scripts[current++]; 
     911      document.title = "[HTMLArea: loading script " + current + "/" + HTMLArea._scripts.length + "]"; 
     912      var script = document.createElement("script"); 
     913      script.type = "text/javascript"; 
     914      script.src = url; 
     915 
     916      HTMLArea._ddt( "loadNextScript(): loading '" + url + "'" ); 
     917 
     918      // the magic step. evt depends on the browser (See above) 
     919 
     920      script[evt] = loadNextScript; 
     921      head.appendChild(script); 
     922      }  
     923    else  
     924      { 
     925      document.title = savetitle; 
     926 
     927      HTMLArea._ddt( "loadNextScript(): end of list reached. Firing HTMLAreaonLoad handler which is '" + HTMLArea.onLoad + "'" ); 
     928 
     929           // fire the onLoad handler. See HTMLArea.onload() up top.  
     930           // By default this is a null function. 
     931          
     932      HTMLArea.onload(); 
     933      } 
     934    }; 
     935 
     936  HTMLArea._ddt( "init(): calling first loadNextScript()" ); 
     937 
     938  // start the chain of script loading. 
     939 
     940  loadNextScript(); 
     941 
     942  };  // end of init(); 
     943 
     944// ----------------------------------- 
     945 
     946/**  
     947* replace all TEXTAREA-s in the document with HTMLArea-s.  
     948*/ 
     949 
     950HTMLArea.replaceAll = function(config)  
     951  { 
     952 
     953  HTMLArea._ddt( "replaceAll(): top" ); 
     954 
     955  var tas = document.getElementsByTagName("textarea"); 
     956  for (var i = tas.length; i > 0; (new HTMLArea(tas[--i], config)).generate()); 
     957  }; 
     958 
     959// ---------------------------------------------- 
     960 
     961/**  
     962* Helper function: replaces the TEXTAREA with the given ID with HTMLArea.  
     963*/ 
     964 
     965HTMLArea.replace = function(id, config) 
     966  { 
     967 
     968  HTMLArea._ddt( "replace(): top with id '" + id + "'" ); 
     969 
     970  var ta = HTMLArea.getElementById("textarea", id); 
     971  if (ta) 
     972    { 
     973    var taobj = new HTMLArea(ta, config); 
     974    taobj.generate(); 
     975    return taobj; 
     976    } 
     977  else 
     978    { 
     979    return null; 
     980    }; 
     981  }; 
     982 
     983// ------------------------------------------------------ 
     984 
     985/** 
     986* a hack 
     987*/ 
     988 
     989use_clone_img = false; 
     990 
     991/** 
     992* makeBtnImg() 
     993*/ 
     994 
     995HTMLArea.makeBtnImg = function(imgDef, doc) 
     996  { 
     997 
     998  HTMLArea._ddt( "== makeBtnImg(): top" ); 
     999 
     1000  if(!doc) doc = document; 
     1001 
     1002  if (!doc._htmlareaImgCache) 
     1003    { 
     1004    doc._htmlareaImgCache = { }; 
     1005    } 
     1006 
     1007  var i_contain = null; 
     1008 
     1009  if(HTMLArea.is_ie && ((!doc.compatMode) || (doc.compatMode && doc.compatMode == "BackCompat"))) 
     1010    { 
     1011    i_contain = doc.createElement('span'); 
     1012    } 
     1013  else 
     1014    { 
     1015    i_contain = doc.createElement('div'); 
     1016    i_contain.style.position = 'relative'; 
     1017    } 
     1018 
     1019  i_contain.style.overflow = 'hidden'; 
     1020  i_contain.style.width = "18px"; 
     1021  i_contain.style.height = "18px"; 
     1022 
     1023  var img = null; 
     1024 
     1025  if(typeof imgDef == 'string') 
     1026    { 
     1027 
     1028    if(doc._htmlareaImgCache[imgDef]) 
     1029      { 
     1030      img = doc._htmlareaImgCache[imgDef].cloneNode(); 
     1031      } 
     1032    else 
     1033      { 
     1034      img = doc.createElement("img"); 
     1035      img.src = imgDef; 
     1036      img.style.width = "18px"; 
     1037      img.style.height = "18px"; 
     1038 
     1039      if(use_clone_img) 
     1040        doc._htmlareaImgCache[imgDef] = img.cloneNode(); 
     1041      } 
     1042    } 
     1043  else 
     1044    { 
     1045 
     1046    if(doc._htmlareaImgCache[imgDef[0]]) 
     1047      { 
     1048      img = doc._htmlareaImgCache[imgDef[0]].cloneNode(); 
     1049      } 
     1050    else 
     1051      { 
     1052      img = doc.createElement("img"); 
     1053      img.src = imgDef[0]; 
     1054      img.style.position = 'relative'; 
     1055 
     1056      if(use_clone_img) 
     1057        doc._htmlareaImgCache[imgDef[0]] = img.cloneNode(); 
     1058      } 
     1059 
     1060    img.style.top  = imgDef[2] ? ('-' + (18 * (imgDef[2] + 1)) + 'px') : '-18px'; 
     1061    img.style.left = imgDef[1] ? ('-' + (18 * (imgDef[1] + 1)) + 'px') : '-18px'; 
     1062    } 
     1063 
     1064  i_contain.appendChild(img); 
     1065 
     1066  HTMLArea._ddt( "== makeBtnImg(): bottom" ); 
     1067 
     1068  return i_contain; 
     1069 
     1070  }  // end of HTMLArea.makeBtnImg() 
     1071 
     1072// ----------------------------------------------------- 
     1073 
     1074/** 
     1075* getPluginDir() 
     1076* 
     1077* static function that loads the required plugin and lang file, based on the 
     1078* language loaded already for HTMLArea.  You better make sure that the plugin 
     1079* _has_ that language, otherwise shit might happen ;-) 
     1080*/ 
     1081 
     1082HTMLArea.getPluginDir = function(pluginName)  
     1083  { 
     1084  return _editor_url + "plugins/" + pluginName; 
     1085  }; 
     1086 
     1087// ---------------------------------------------------- 
     1088 
     1089/** 
     1090* loadPlugin - load a plugin 
     1091* 
     1092* This method is also called prior to HTMLArea object construction.  
     1093* 
     1094* @see HTMLArea.loadPlugins() 
     1095*/ 
     1096 
     1097HTMLArea.loadPlugin = function(pluginName, callback)  
     1098{ 
     1099 
     1100  HTMLArea._ddt( "loadPlugin(): loading plugin '" + pluginName + "'" ); 
     1101 
     1102  // Might already be loaded 
     1103 
     1104  if ( eval('typeof ' + pluginName) != 'undefined' ) 
     1105    { 
     1106 
     1107    HTMLArea._ddt( "loadPlugins(): plugin '" + pluginName + "' already loaded" ); 
     1108 
     1109    if (callback) 
     1110      { 
     1111 
     1112      HTMLArea._ddt( "loadPlugins(): calling plugin '" + pluginName + "' callback" ); 
     1113 
     1114      callback(); 
     1115      } 
     1116 
     1117    return; 
     1118    } 
     1119 
     1120  var dir = this.getPluginDir(pluginName); 
     1121  var plugin = pluginName.replace(/([a-z])([A-Z])([a-z])/g, 
     1122          function (str, l1, l2, l3) { 
     1123            return l1 + "-" + l2.toLowerCase() + l3; 
     1124          }).toLowerCase() + ".js"; 
     1125 
     1126  var plugin_file = dir + "/" + plugin; 
     1127 
     1128  if ( callback ) 
     1129    { 
     1130    HTMLArea._ddt( "loadPlugin(): callback defined. Using _loadback() to load plugin" ); 
     1131 
     1132    HTMLArea._loadback(plugin_file, callback); 
     1133    } 
     1134  else 
     1135    { 
     1136    HTMLArea._ddt( "loadPlugin(): callback not defined. writing javascript include line to document." ); 
     1137 
     1138    document.write("<script type='text/javascript' src='" + plugin_file + "'></script>"); 
     1139    } 
     1140 
     1141  };  // end of loadPlugin() 
     1142 
     1143// -------------------------------------------------------- 
     1144 
     1145/** 
     1146* loadPlugins 
     1147* 
     1148* This is called from the page to load in the plugin javascript 
     1149* files. This method is called before the HTMLArea object is 
     1150* constructed. 
     1151*/ 
     1152 
     1153HTMLArea.loadPlugins = function(plugins, callbackIfNotReady) 
     1154  { 
     1155 
     1156  HTMLArea._ddt( "loadPlugins(): top" ); 
     1157 
     1158  // make a copy of the plugins array that we'll chip away at. 
     1159 
     1160  var nuPlugins = HTMLArea.cloneObject(plugins); 
     1161 
     1162  // loop over the array peeling off a plugin at a time until none are left. 
     1163 
     1164  while(nuPlugins.length) 
     1165    { 
     1166 
     1167    // Might already be loaded 
     1168 
     1169    if ( eval('typeof ' + nuPlugins[nuPlugins.length-1]) != 'undefined' ) 
     1170      { 
     1171      var poppedPlugin = nuPlugins.pop(); 
     1172 
     1173      HTMLArea._ddt( "loadPlugins(): plugin '" + poppedPlugin + "' was already loaded" ); 
     1174 
     1175      } 
     1176    else 
     1177      { 
     1178      break; 
     1179      } 
     1180 
     1181    } 
     1182 
     1183  // if there were no plugins just return. 
     1184 
     1185  if( !nuPlugins.length ) 
     1186    { 
     1187 
     1188    HTMLArea._ddt( "loadPlugins(): no plugins left to load" ); 
     1189 
     1190    return true; 
     1191    } 
     1192 
     1193  // personally, I find this using a function definition as an argument to a method 
     1194  // really ugly. Here's we're pulling off the current plugin to load (argument 1),  
     1195  // and setting the "callback function" (the function that's called after the plugin 
     1196  // javascript file is successfully loaded) to run loadPlugins .. essentially  
     1197  // an indirect recursive algorithm. 
     1198 
     1199  HTMLArea.loadPlugin( nuPlugins.pop(), 
     1200      function() 
     1201      { 
     1202        if(HTMLArea.loadPlugins(nuPlugins, callbackIfNotReady)) 
     1203        { 
     1204          if(typeof callbackIfNotReady == 'function') 
     1205          { 
     1206            callbackIfNotReady(); 
     1207          } 
     1208        } 
     1209      } 
     1210    );  // this is the end of the HTMLArea.loadPlugin() recursive call. 
     1211 
     1212  HTMLArea._ddt( "loadPlugins(): end" ); 
     1213 
     1214  return false; 
     1215 
     1216  }  // end of HTMLArea.loadPlugins() 
     1217 
     1218// ---------------------------------------- 
     1219 
     1220/** 
     1221* refresh plugin by calling onGenerate or onGenerateOnce method. 
     1222*/ 
     1223 
     1224HTMLArea.refreshPlugin = function(plugin)  
     1225  { 
     1226 
     1227  HTMLArea._ddt( "refreshPlugin(): top" ); 
     1228 
     1229  if (typeof plugin.onGenerate == "function") 
     1230    { 
     1231    HTMLArea._ddt( "refreshPlugin(): onGenerate is a function. calling onGenerate for '" + plugin + "'" ); 
     1232    plugin.onGenerate(); 
     1233    } 
     1234 
     1235  if (typeof plugin.onGenerateOnce == "function")  
     1236    { 
     1237    HTMLArea._ddt( "refreshPlugin(): onGenerateOnce is a function. calling onGenerateOnce for '" + plugin + "'" ); 
     1238    plugin.onGenerateOnce(); 
     1239    plugin.onGenerateOnce = null; 
     1240    } 
     1241 
     1242  }; 
     1243 
     1244// --------------------------------------------------- 
     1245 
     1246/** 
     1247* loadStyle() 
     1248*/ 
     1249 
     1250HTMLArea.loadStyle = function(style, plugin)  
     1251  { 
     1252  var url = _editor_url || ''; 
     1253 
     1254  HTMLArea._ddt( "loadStyle(): top with style '" + style + "' and plugin '" + plugin + "'" ); 
     1255   
     1256  if (typeof plugin != "undefined")  
     1257    { 
     1258    url += "plugins/" + plugin + "/"; 
     1259    } 
     1260 
     1261  url += style; 
     1262 
     1263  if (/^\//.test(style)) 
     1264    url = style; 
     1265 
     1266  var head = document.getElementsByTagName("head")[0]; 
     1267  var link = document.createElement("link"); 
     1268  link.rel = "stylesheet"; 
     1269  link.href = url; 
     1270  head.appendChild(link); 
     1271 
     1272  HTMLArea._ddt( "loadStyle(): appending '" + link + "' to document" ); 
     1273 
     1274  //document.write("<style type='text/css'>@import url(" + url + ");</style>"); 
     1275 
     1276  };  // end of loadStyle() 
     1277 
     1278HTMLArea.loadStyle(typeof _editor_css == "string" ? _editor_css : "htmlarea.css"); 
     1279 
     1280// --------------------------------------------------- 
     1281 
     1282/** 
     1283* objectProperties 
     1284*/ 
     1285 
     1286HTMLArea.objectProperties = function(obj) 
     1287  { 
     1288  var props = [ ]; 
     1289  for(var x in obj) 
     1290         { 
     1291    props[props.length] = x; 
     1292    } 
     1293  return props; 
     1294  } 
     1295 
     1296// -------------------------------------------------------- 
     1297 
     1298/** 
     1299* getInnerText() 
     1300*/ 
     1301 
     1302HTMLArea.getInnerText = function(el)  
     1303  { 
     1304 
     1305  HTMLArea._ddt( "getInnerText(): top" ); 
     1306 
     1307  var txt = '', i; 
     1308 
     1309  for (i = el.firstChild; i; i = i.nextSibling)  
     1310    { 
     1311    if (i.nodeType == 3) 
     1312      txt += i.data; 
     1313    else if (i.nodeType == 1) 
     1314      txt += HTMLArea.getInnerText(i); 
     1315    } 
     1316 
     1317  return txt; 
     1318 
     1319  };  // end of getInnerText() 
     1320 
     1321// ----------------------------------------------------------- 
     1322 
     1323/** 
     1324* returns a clone of the given object 
     1325* 
     1326*/ 
     1327 
     1328HTMLArea.cloneObject = function(obj)  
     1329  { 
     1330 
     1331  HTMLArea._ddt( "cloneObject(): top" ); 
     1332 
     1333  if (!obj) return null; 
     1334 
     1335  var newObj = new Object; 
     1336 
     1337  // check for array objects 
     1338   
     1339  if (obj.constructor.toString().indexOf("function Array(") == 1)  
     1340    { 
     1341    newObj = obj.constructor(); 
     1342    } 
     1343 
     1344  // check for function objects (as usual, IE is fucked up) 
     1345  if (obj.constructor.toString().indexOf("function Function(") == 1)  
     1346    { 
     1347    newObj = obj; // just copy reference to it 
     1348    }  
     1349  else for (var n in obj)  
     1350    { 
     1351    var node = obj[n]; 
     1352    if (typeof node == 'object') { newObj[n] = HTMLArea.cloneObject(node); } 
     1353    else                         { newObj[n] = node; } 
     1354    } 
     1355 
     1356  return newObj; 
     1357 
     1358  }; 
     1359 
     1360// ---------------------------------------------------------- 
     1361 
     1362/** 
     1363* checkSupportedBrowser() 
     1364* 
     1365* @todo FIXME!!! this should return false for IE < 5.5 
     1366*/ 
     1367 
     1368HTMLArea.checkSupportedBrowser = function()  
     1369  { 
     1370 
     1371  HTMLArea._ddt( "checkSupportedBrowser(): top" ); 
     1372 
     1373  if (HTMLArea.is_gecko)  
     1374    { 
     1375 
     1376    if (navigator.productSub < 20021201)  
     1377           { 
     1378      alert("You need at least Mozilla-1.3 Alpha.\n" + 
     1379            "Sorry, your Gecko is not supported."); 
     1380      return false; 
     1381      } 
     1382 
     1383    if (navigator.productSub < 20030210)  
     1384           { 
     1385      alert("Mozilla < 1.3 Beta is not supported!\n" + 
     1386            "I'll try, though, but it might not work."); 
     1387      } 
     1388    } 
     1389 
     1390  return HTMLArea.is_gecko || HTMLArea.is_ie; 
     1391 
     1392  }; 
     1393 
     1394// ---------------------------------------- 
     1395/** 
     1396* getElementById() 
     1397* 
     1398* FIX: Internet Explorer returns an item having the _name_ equal to the given 
     1399* id, even if it's not having any id.  This way it can return a different form 
     1400* field even if it's not a textarea.  This workarounds the problem by 
     1401* specifically looking to search only elements having a certain tag name. 
     1402*/ 
     1403 
     1404HTMLArea.getElementById = function(tag, id)  
     1405  { 
     1406 
     1407  HTMLArea._ddt( "getElementById(): top with tag '" + tag + "' id '" + id + "'" ); 
     1408 
     1409  var el, i, objs = document.getElementsByTagName(tag); 
     1410  for (i = objs.length; --i >= 0 && (el = objs[i]);) 
     1411    if (el.id == id) 
     1412      return el; 
     1413  return null; 
     1414 
     1415  }; 
     1416 
     1417// --------------------------------------------------------------------------- 
     1418 
     1419/**  
     1420* _postback() 
     1421* 
     1422* Use XML HTTPRequest to post some data back to the server and do something 
     1423* with the response (asyncronously!), this is used by such things as the tidy functions 
     1424*/ 
     1425 
     1426HTMLArea._postback = function(url, data, handler) 
     1427  { 
     1428 
     1429  HTMLArea._ddt( "_postback() : top with url '" + url + "'" ); 
     1430 
     1431  var req = null; 
     1432  if(HTMLArea.is_ie) 
     1433    { 
     1434    req = new ActiveXObject("Microsoft.XMLHTTP"); 
     1435    } 
     1436  else 
     1437    { 
     1438    req = new XMLHttpRequest(); 
     1439    } 
     1440 
     1441  var content = ''; 
     1442  for(var i in data) 
     1443    { 
     1444    content += (content.length ? '&' : '') + i + '=' + encodeURIComponent(data[i]); 
     1445    } 
     1446 
     1447  function callBack() 
     1448    { 
     1449    if(req.readyState == 4) 
     1450      { 
     1451      if(req.status == 200) 
     1452        { 
     1453        if(typeof handler == 'function') 
     1454          handler(req.responseText, req); 
     1455        } 
     1456      else 
     1457        { 
     1458        alert('An error has occurred: ' + req.statusText); 
     1459        } 
     1460      } 
     1461    } 
     1462 
     1463  req.onreadystatechange = callBack; 
     1464 
     1465  req.open('POST', url, true); 
     1466  req.setRequestHeader( 'Content-Type', 'application/x-www-form-urlencoded; charset=UTF-8' ); 
     1467  //alert(content); 
     1468  req.send(content); 
     1469 
     1470  } // end of _postback() 
     1471 
     1472// ----------------------------------------------------------------------- 
     1473 
     1474/** 
     1475* _getback() 
     1476*/ 
     1477 
     1478HTMLArea._getback = function(url, handler) 
     1479  { 
     1480 
     1481  HTMLArea._ddt( "_getback(): top" ); 
     1482 
     1483  var req = null; 
     1484  if(HTMLArea.is_ie) 
     1485    { 
     1486    req = new ActiveXObject("Microsoft.XMLHTTP"); 
     1487    } 
     1488  else 
     1489    { 
     1490    req = new XMLHttpRequest(); 
     1491    } 
     1492 
     1493  function callBack() 
     1494    { 
     1495    if(req.readyState == 4) 
     1496      { 
     1497      if(req.status == 200) 
     1498        { 
     1499        handler(req.responseText, req); 
     1500        } 
     1501      else 
     1502        {  
     1503        alert('An error has occurred: ' + req.statusText); 
     1504        } 
     1505      } 
     1506    } 
     1507 
     1508  req.onreadystatechange = callBack; 
     1509  req.open('GET', url, true); 
     1510  req.send(null); 
     1511 
     1512  }  // end of _getback() 
     1513 
     1514// ---------------------------------------------------------- 
     1515 
     1516/** 
     1517* _geturlcontent() 
     1518*/ 
     1519 
     1520HTMLArea._geturlcontent = function(url) 
     1521  { 
     1522 
     1523  HTMLArea._ddt( "_geturlcontent(): top with url '" + url + "'" ); 
     1524 
     1525  var req = null; 
     1526  if(HTMLArea.is_ie) 
     1527    { 
     1528    req = new ActiveXObject("Microsoft.XMLHTTP"); 
     1529    } 
     1530  else 
     1531    { 
     1532    req = new XMLHttpRequest(); 
     1533    } 
     1534 
     1535  // Synchronous! 
     1536  req.open('GET', url, false); 
     1537  req.send(null); 
     1538  if(req.status == 200) 
     1539    { 
     1540    return req.responseText; 
     1541    } 
     1542  else 
     1543    { 
     1544    return ''; 
     1545    } 
     1546 
     1547  } // end of _geturlcontent() 
     1548 
     1549// ------------------------------------------------------ 
     1550 
     1551/** 
     1552* arrayContainsArray() 
     1553*/ 
     1554 
     1555HTMLArea.arrayContainsArray = function(a1, a2) 
     1556  { 
     1557 
     1558  HTMLArea._ddt( "arrayContainsArray(): top" ); 
     1559 
     1560  var all_found = true; 
     1561  for(var x = 0; x < a2.length; x++) 
     1562    { 
     1563    var found = false; 
     1564    for(var i = 0; i < a1.length; i++) 
     1565      { 
     1566      if(a1[i] == a2[x]) 
     1567        { 
     1568        found = true; 
     1569        break; 
     1570        } 
     1571      } 
     1572 
     1573    if(!found) 
     1574      { 
     1575      all_found = false; 
     1576      break; 
     1577      } 
     1578    } 
     1579 
     1580  return all_found; 
     1581 
     1582  } 
     1583 
     1584// ----------------------------------------------------------------- 
     1585 
     1586/** 
     1587* arrayFilter() 
     1588*/ 
     1589 
     1590HTMLArea.arrayFilter = function(a1, filterfn) 
     1591  { 
     1592 
     1593  HTMLArea._ddt( "arrayFilter(): top" ); 
     1594 
     1595  var new_a = [ ]; 
     1596  for(var x = 0; x < a1.length; x++) 
     1597    { 
     1598    if(filterfn(a1[x])) 
     1599      new_a[new_a.length] = a1[x]; 
     1600    } 
     1601 
     1602  return new_a; 
     1603 
     1604  } 
     1605 
     1606// --------------------------- 
     1607 
     1608/** 
     1609* uniq() 
     1610*/ 
     1611 
     1612HTMLArea.uniq = function(prefix) 
     1613  { 
     1614  return prefix + HTMLArea.uniq_count++; 
     1615  } 
     1616 
     1617// -------------------------------------------------- 
     1618 
     1619/**  
     1620* Load a language file. 
     1621* 
     1622*  This function should not be used directly, HTMLArea._lc will use it when necessary. 
     1623* 
     1624* @param context Case sensitive context name, eg 'HTMLArea', 'TableOperations', ... 
     1625*/ 
     1626 
     1627HTMLArea._loadlang = function(context) 
     1628  { 
     1629 
     1630  HTMLArea._ddt( "_loadlang(): top" ); 
     1631 
     1632  if(typeof _editor_lcbackend == "string") 
     1633    { 
     1634    //use backend 
     1635    var url = _editor_lcbackend; 
     1636    url = url.replace(/%lang%/, _editor_lang); 
     1637    url = url.replace(/%context%/, context); 
     1638    } 
     1639  else 
     1640    { 
     1641    //use internal files 
     1642    if(context != 'HTMLArea')  
     1643           { 
     1644      var url = _editor_url+"/plugins/"+context+"/lang/"+_editor_lang+".js"; 
     1645      }  
     1646         else  
     1647           { 
     1648      var url = _editor_url+"/lang/"+_editor_lang+".js"; 
     1649      } 
     1650    } 
     1651 
     1652  var lang; 
     1653  var langData = HTMLArea._geturlcontent(url); 
     1654  if(langData != "")  
     1655    { 
     1656    try  
     1657           { 
     1658      eval('lang = ' + langData); 
     1659      }  
     1660         catch(Error)  
     1661           { 
     1662      alert('Error reading Language-File ('+url+'):\n'+Error.toString()); 
     1663      lang = { } 
     1664      } 
     1665    }  
     1666  else  
     1667    { 
     1668    lang = { }; 
     1669    } 
     1670 
     1671  return lang; 
     1672 
     1673  }  // end of _loadlang() 
     1674 
     1675// ------------------------------------------------------ 
     1676 
     1677/**  
     1678* Return a localised string. 
     1679* 
     1680* @param string    English language string 
     1681* @param context   Case sensitive context name, eg 'HTMLArea' (default), 'TableOperations'... 
     1682*/ 
     1683 
     1684HTMLArea._lc = function(string, context) 
     1685  { 
     1686 
     1687  HTMLArea._ddt( "_lc: top with string '" + string + "'" ); 
     1688 
     1689  if(_editor_lang == "en") 
     1690    { 
     1691    return string; 
     1692    } 
     1693 
     1694  if(typeof HTMLArea._lc_catalog == 'undefined') 
     1695    { 
     1696    HTMLArea._lc_catalog = [ ]; 
     1697    } 
     1698 
     1699  if(typeof context == 'undefined') 
     1700    { 
     1701    context = 'HTMLArea'; 
     1702    } 
     1703 
     1704  if(typeof HTMLArea._lc_catalog[context] == 'undefined') 
     1705    { 
     1706    HTMLArea._lc_catalog[context] = HTMLArea._loadlang(context); 
     1707    } 
     1708 
     1709  if(typeof HTMLArea._lc_catalog[context][string] == 'undefined') 
     1710    { 
     1711    return string; // Indicate it's untranslated 
     1712    } 
     1713  else 
     1714    { 
     1715    return HTMLArea._lc_catalog[context][string]; 
     1716    } 
     1717 
     1718  }  // end of _lc() 
     1719 
     1720// -------------------------------------------------------------------- 
     1721 
     1722/** 
     1723* hasDisplayedChildren() 
     1724*/ 
     1725 
     1726HTMLArea.hasDisplayedChildren = function(el) 
     1727  { 
     1728 
     1729  HTMLArea._ddt( "hasDisplayedChildren(): top" ); 
     1730 
     1731  var children = el.childNodes; 
     1732  for(var i =0; i < children.length;i++) 
     1733    { 
     1734    if(children[i].tagName) 
     1735      { 
     1736      if(children[i].style.display != 'none') 
     1737        { 
     1738        return true; 
     1739        } 
     1740      } 
     1741    } 
     1742 
     1743  return false; 
     1744 
     1745  } 
     1746 
     1747// ---------------------------------------------------------- 
     1748 
     1749/** 
     1750* _loadback() 
     1751*/ 
     1752 
     1753HTMLArea._loadback = function(src, callback) 
     1754  { 
     1755 
     1756  HTMLArea._ddt( "_loadback(): top with src '" + src + "'" ); 
     1757 
     1758  var head = document.getElementsByTagName("head")[0]; 
     1759  var evt = HTMLArea.is_ie ? "onreadystatechange" : "onload"; 
     1760 
     1761  var script = document.createElement("script"); 
     1762  script.type = "text/javascript"; 
     1763  script.src = src; 
     1764  script[evt] = function() 
     1765    { 
     1766    if(HTMLArea.is_ie && !/loaded|complete/.test(window.event.srcElement.readyState))  return; 
     1767    callback(); 
     1768    } 
     1769 
     1770  head.appendChild(script); 
     1771 
     1772  }; 
     1773 
     1774// ----------------------------------------------------- 
     1775 
     1776/** 
     1777* collectionToArray() 
     1778*/ 
     1779 
     1780HTMLArea.collectionToArray = function(collection) 
     1781  { 
     1782 
     1783  HTMLArea._ddt( "collectionToArray(): top" ); 
     1784 
     1785  var array = [ ]; 
     1786  for(var i = 0; i < collection.length; i++) 
     1787    { 
     1788    array.push(collection.item(i)); 
     1789    } 
     1790  return array; 
     1791 
     1792  } 
     1793 
     1794// -------------------------------------------------------- 
     1795 
     1796/** 
     1797* makeEditors() 
     1798*/ 
     1799 
     1800HTMLArea.makeEditors = function(editor_names, default_config, plugin_names) 
     1801  { 
     1802 
     1803  HTMLArea._ddt( "makeEditors(): top" ); 
     1804 
     1805  if(typeof default_config == 'function') 
     1806    { 
     1807    default_config = default_config(); 
     1808    } 
     1809 
     1810  var editors = { }; 
     1811  for(var x = 0; x < editor_names.length; x++) 
     1812    { 
     1813    editors[editor_names[x]] = new HTMLArea(editor_names[x], HTMLArea.cloneObject(default_config)); 
     1814    if(plugin_names) 
     1815      { 
     1816      for(var i = 0; i < plugin_names.length; i++) 
     1817        { 
     1818        editors[editor_names[x]].registerPlugin(eval(plugin_names[i])); 
     1819        } 
     1820      } 
     1821    } 
     1822  return editors; 
     1823 
     1824  } 
     1825 
     1826// ---------------------------------------------------------------------- 
     1827 
     1828/** 
     1829* startEditors() 
     1830*/ 
     1831 
     1832HTMLArea.startEditors = function(editors) 
     1833  { 
     1834 
     1835  HTMLArea._ddt( "startEditors(): top" ); 
     1836 
     1837  for(var i in editors) 
     1838    { 
     1839    if(editors[i].generate) editors[i].generate(); 
     1840    } 
     1841 
     1842  } 
     1843 
     1844// --------------------------------------------------------- 
     1845/** 
     1846* _makeColor() 
     1847* 
     1848* creates a rgb-style color from a number 
     1849*/ 
     1850 
     1851HTMLArea._makeColor = function(v)  
     1852  { 
     1853 
     1854  HTMLArea._ddt( "_makeColor(): top" ); 
     1855 
     1856  if (typeof v != "number")  
     1857    { 
     1858    // already in rgb (hopefully); IE doesn't get here. 
     1859    return v; 
     1860    } 
     1861 
     1862  // IE sends number; convert to rgb. 
     1863  var r = v & 0xFF; 
     1864  var g = (v >> 8) & 0xFF; 
     1865  var b = (v >> 16) & 0xFF; 
     1866 
     1867  return "rgb(" + r + "," + g + "," + b + ")"; 
     1868 
     1869  }; 
     1870 
     1871// ----------------------------------------------------- 
     1872 
     1873/** 
     1874* _colorToRgb() 
     1875* 
     1876* returns hexadecimal color representation from a number or a rgb-style color. 
     1877*/ 
     1878 
     1879HTMLArea._colorToRgb = function(v)  
     1880  { 
     1881  if (!v) 
     1882    return ''; 
     1883 
     1884  // returns the hex representation of one byte (2 digits) 
     1885  function hex(d)  
     1886    { 
     1887    return (d < 16) ? ("0" + d.toString(16)) : d.toString(16); 
     1888    }; 
     1889 
     1890  if (typeof v == "number")  
     1891    { 
     1892    // we're talking to IE here 
     1893    var r = v & 0xFF; 
     1894    var g = (v >> 8) & 0xFF; 
     1895    var b = (v >> 16) & 0xFF; 
     1896    return "#" + hex(r) + hex(g) + hex(b); 
     1897    } 
     1898 
     1899  if (v.substr(0, 3) == "rgb")  
     1900    { 
     1901    // in rgb(...) form -- Mozilla 
     1902    var re = /rgb\s*\(\s*([0-9]+)\s*,\s*([0-9]+)\s*,\s*([0-9]+)\s*\)/; 
     1903    if (v.match(re))  
     1904           { 
     1905      var r = parseInt(RegExp.$1); 
     1906      var g = parseInt(RegExp.$2); 
     1907      var b = parseInt(RegExp.$3); 
     1908      return "#" + hex(r) + hex(g) + hex(b); 
     1909      } 
     1910 
     1911    // doesn't match RE?!  maybe uses percentages or float numbers 
     1912    // -- FIXME: not yet implemented. 
     1913 
     1914    return null; 
     1915 
     1916    } 
     1917 
     1918  if (v.substr(0, 1) == "#")  
     1919    { 
     1920    // already hex rgb (hopefully :D ) 
     1921    return v; 
     1922    } 
     1923 
     1924  // if everything else fails ;) 
     1925  return null; 
     1926 
     1927  }; // end of _colorToRgb() 
     1928 
     1929// ----------------------------------------- 
     1930 
     1931/** 
     1932* isBlockElement() 
     1933*/ 
     1934 
     1935HTMLArea.isBlockElement = function(el)  
     1936  { 
     1937  return el && el.nodeType == 1 && (HTMLArea._blockTags.indexOf(" " + el.tagName.toLowerCase() + " ") != -1); 
     1938  }; 
     1939 
     1940// --------------------------------------------- 
     1941 
     1942/** 
     1943* isParaContainer() 
     1944*/ 
     1945 
     1946HTMLArea.isParaContainer = function(el) 
     1947  { 
     1948  return el && el.nodeType == 1 && (HTMLArea._paraContainerTags.indexOf(" " + el.tagName.toLowerCase() + " ") != -1); 
     1949  } 
     1950 
     1951// --------------------------------------------- 
     1952 
     1953/** 
     1954* needsClosingTag() 
     1955*/ 
     1956 
     1957HTMLArea.needsClosingTag = function(el)  
     1958  { 
     1959  return el && el.nodeType == 1 && (HTMLArea._closingTags.indexOf(" " + el.tagName.toLowerCase() + " ") != -1); 
     1960  }; 
     1961 
     1962// --------------------------------------------- 
     1963 
     1964/** 
     1965* htmlEncode() 
     1966* 
     1967* performs HTML encoding of some given string 
     1968*/ 
     1969 
     1970HTMLArea.htmlEncode = function(str)  
     1971  { 
     1972  if(typeof str.replace == 'undefined') str = str.toString(); 
     1973 
     1974  // we don't need regexp for that, but.. so be it for now. 
     1975 
     1976  str = str.replace(/&/ig, "&amp;"); 
     1977  str = str.replace(/</ig, "&lt;"); 
     1978  str = str.replace(/>/ig, "&gt;"); 
     1979  str = str.replace(/\xA0/g, "&nbsp;"); // Decimal 160, non-breaking-space 
     1980  str = str.replace(/\x22/g, "&quot;"); 
     1981  // \x22 means '"' -- we use hex reprezentation so that we don't disturb 
     1982  // JS compressors (well, at least mine fails.. ;) 
     1983  return str; 
     1984 
     1985  }; 
     1986 
     1987// ------------------------------------------------------- 
     1988 
     1989/** 
     1990* getHTML() 
     1991* 
     1992* Retrieves the HTML code from the given node.   This is a replacement for 
     1993* getting innerHTML, using standard DOM calls. 
     1994* Wrapper catch a Mozilla-Exception with non well formed html source code 
     1995*/ 
     1996 
     1997HTMLArea.getHTML = function(root, outputRoot, editor) 
     1998  { 
     1999  try 
     2000    { 
     2001    return HTMLArea.getHTMLWrapper(root,outputRoot,editor); 
     2002    } 
     2003  catch(e) 
     2004    { 
     2005    alert('Your Document is not well formed. Check JavaScript console for details.'); 
     2006    return editor._iframe.contentWindow.document.body.innerHTML; 
     2007    } 
     2008 
     2009  } 
     2010 
     2011// ------------------------------------------------------------ 
     2012 
     2013/** 
     2014* getHTMLWrapper() 
     2015*/ 
     2016 
     2017HTMLArea.getHTMLWrapper = function(root, outputRoot, editor)  
     2018  { 
     2019 
     2020  HTMLArea._ddt( "getHTMLWrapper(): top" ); 
     2021 
     2022  var html = ""; 
     2023 
     2024  switch (root.nodeType)  
     2025    { 
     2026    case 10:// Node.DOCUMENT_TYPE_NODE 
     2027    case 6: // Node.ENTITY_NODE 
     2028    case 12:// Node.NOTATION_NODE 
     2029      // this all are for the document type, probably not necessary 
     2030      break; 
     2031 
     2032    case 2: // Node.ATTRIBUTE_NODE 
     2033 
     2034      // Never get here, this has to be handled in the ELEMENT case because 
     2035      // of IE crapness requring that some attributes are grabbed directly from 
     2036      // the attribute (nodeValue doesn't return correct values), see 
     2037      //http://groups.google.com/groups?hl=en&lr=&ie=UTF-8&oe=UTF-8&safe=off&selm=3porgu4mc4ofcoa1uqkf7u8kvv064kjjb4%404ax.com 
     2038      // for information 
     2039 
     2040      break; 
     2041 
     2042    case 4: // Node.CDATA_SECTION_NODE 
     2043      // Mozilla seems to convert CDATA into a comment when going into wysiwyg mode, 
     2044      //  don't know about IE 
     2045      html += '<![CDATA[' + root.data + ']]>'; 
     2046      break; 
     2047 
     2048    case 5: // Node.ENTITY_REFERENCE_NODE 
     2049      html += '&' + root.nodeValue + ';'; 
     2050      break; 
     2051 
     2052    case 7: // Node.PROCESSING_INSTRUCTION_NODE 
     2053      // PI's don't seem to survive going into the wysiwyg mode, (at least in moz) 
     2054      // so this is purely academic 
     2055      html += '<?' + root.target + ' ' + root.data + ' ?>'; 
     2056      break; 
     2057 
     2058 
     2059    case 1: // Node.ELEMENT_NODE 
     2060    case 11: // Node.DOCUMENT_FRAGMENT_NODE 
     2061    case 9: // Node.DOCUMENT_NODE 
     2062      { 
     2063      var closed; 
     2064      var i; 
     2065      var root_tag = (root.nodeType == 1) ? root.tagName.toLowerCase() : ''; 
     2066      if (root_tag == 'br' && !root.nextSibling) 
     2067        break; 
     2068 
     2069      if (outputRoot) 
     2070        outputRoot = !(editor.config.htmlRemoveTags && editor.config.htmlRemoveTags.test(root_tag)); 
     2071 
     2072      if (HTMLArea.is_ie && root_tag == "head")  
     2073                  { 
     2074        if (outputRoot) 
     2075          html += "<head>"; 
     2076        // lowercasize 
     2077        var save_multiline = RegExp.multiline; 
     2078        RegExp.multiline = true; 
     2079        var txt = root.innerHTML.replace(HTMLArea.RE_tagName, function(str, p1, p2)  
     2080                    { 
     2081          return p1 + p2.toLowerCase(); 
     2082          }); 
     2083 
     2084        RegExp.multiline = save_multiline; 
     2085        html += txt; 
     2086        if (outputRoot) 
     2087          html += "</head>"; 
     2088        break; 
     2089        }  
     2090                else if (outputRoot)  
     2091                  { 
     2092        closed = (!(root.hasChildNodes() || HTMLArea.needsClosingTag(root))); 
     2093        html = "<" + root.tagName.toLowerCase(); 
     2094        var attrs = root.attributes; 
     2095        for (i = 0; i < attrs.length; ++i)  
     2096                    { 
     2097          var a = attrs.item(i); 
     2098          if (!a.specified)  
     2099                           { 
     2100            continue; 
     2101            } 
     2102 
     2103          var name = a.nodeName.toLowerCase(); 
     2104          if (/_moz_editor_bogus_node/.test(name))  
     2105                           { 
     2106            html = ""; 
     2107            break; 
     2108            } 
     2109 
     2110          if (/(_moz)|(contenteditable)|(_msh)/.test(name))  
     2111                      { 
     2112            // avoid certain attributes 
     2113            continue; 
     2114            } 
     2115 
     2116          var value; 
     2117 
     2118          if (name != "style")  
     2119                           { 
     2120 
     2121            // IE5.5 reports 25 when cellSpacing is 
     2122            // 1; other values might be doomed too. 
     2123            // For this reason we extract the 
     2124            // values directly from the root node. 
     2125            // I'm starting to HATE JavaScript 
     2126            // development.  Browser differences 
     2127            // suck. 
     2128            // 
     2129            // Using Gecko the values of href and src are converted to absolute links 
     2130            // unless we get them using nodeValue() 
     2131 
     2132            if (typeof root[a.nodeName] != "undefined" && name != "href" && name != "src" && !/^on/.test(name))  
     2133                                  { 
     2134              value = root[a.nodeName]; 
     2135              }  
     2136                                else  
     2137                                  { 
     2138              value = a.nodeValue; 
     2139 
     2140              // IE seems not willing to return the original values - it converts to absolute 
     2141              // links using a.nodeValue, a.value, a.stringValue, root.getAttribute("href") 
     2142              // So we have to strip the baseurl manually :-/ 
     2143 
     2144              if (HTMLArea.is_ie && (name == "href" || name == "src"))  
     2145                                    { 
     2146                value = editor.stripBaseURL(value); 
     2147                } 
     2148              } 
     2149            }  
     2150                         else  
     2151                           {  
     2152                                 
     2153                                // IE fails to put style in attributes list 
     2154            // FIXME: cssText reported by IE is UPPERCASE 
     2155 
     2156            value = root.style.cssText; 
     2157            } 
     2158 
     2159          if (/^(_moz)?$/.test(value))  
     2160                           { 
     2161            // Mozilla reports some special tags 
     2162            // here; we don't need them. 
     2163            continue; 
     2164            } 
     2165 
     2166          html += " " + name + '="' + HTMLArea.htmlEncode(value) + '"'; 
     2167 
     2168          }  // end of for loop. 
     2169 
     2170        if (html != "")  
     2171                    { 
     2172          html += closed ? " />" : ">"; 
     2173          } 
     2174 
     2175        }  // end of else if outputroot 
     2176 
     2177      for (i = root.firstChild; i; i = i.nextSibling)  
     2178                  { 
     2179        html += HTMLArea.getHTMLWrapper(i, true, editor); 
     2180        } 
     2181 
     2182      if (outputRoot && !closed)  
     2183                  { 
     2184        html += "</" + root.tagName.toLowerCase() + ">"; 
     2185        } 
     2186 
     2187      break; 
     2188 
     2189      }  // end of case 1,11,9 
     2190 
     2191    case 3: // Node.TEXT_NODE 
     2192 
     2193      // If a text node is alone in an element and all spaces, replace it with an non breaking one 
     2194      // This partially undoes the damage done by moz, which translates '&nbsp;'s into spaces in the data element 
     2195 
     2196      html = /^script|style$/i.test(root.parentNode.tagName) ? root.data : HTMLArea.htmlEncode(root.data); 
     2197      break; 
     2198 
     2199    case 8: // Node.COMMENT_NODE 
     2200      html = "<!--" + root.data + "-->"; 
     2201      break;            // skip comments, for now. 
     2202 
     2203    }  // end of switch 
     2204 
     2205  return html; 
     2206 
     2207  };  // end of getHTMLWrapper() 
     2208 
     2209// ----------------------------------------------------------- 
     2210 
     2211/** 
     2212* addClasses() 
     2213*/ 
     2214 
     2215HTMLArea.addClasses = function(el, classes) 
     2216  { 
     2217 
     2218  HTMLArea._ddt( "addClasses(): top" ); 
     2219 
     2220  if(el != null) 
     2221    { 
     2222    var thiers = el.className.trim().split(' '); 
     2223    var ours   = classes.split(' '); 
     2224    for(var x = 0; x < ours.length; x++) 
     2225      { 
     2226      var exists = false; 
     2227      for(var i = 0; exists == false && i < thiers.length; i++) 
     2228        { 
     2229        if(thiers[i] == ours[x]) 
     2230          { 
     2231          exists = true; 
     2232          } 
     2233        } 
     2234      if(exists == false) 
     2235        { 
     2236        thiers[thiers.length] = ours[x]; 
     2237        } 
     2238      } 
     2239    el.className = thiers.join(' ').trim(); 
     2240    } 
     2241  } 
     2242 
     2243// -------------------------------------------------------------- 
     2244 
     2245/** 
     2246* removeClasses() 
     2247*/ 
     2248 
     2249HTMLArea.removeClasses = function(el, classes) 
     2250  { 
     2251 
     2252  HTMLArea._ddt( "removeClasses(): top" ); 
     2253 
     2254  var existing    = el.className.trim().split(); 
     2255  var new_classes = [ ]; 
     2256  var remove      = classes.trim().split(); 
     2257 
     2258  for(var i = 0; i < existing.length; i++) 
     2259    { 
     2260    var found = false; 
     2261    for(var x = 0; x < remove.length && !found; x++) 
     2262      { 
     2263      if(existing[i] == remove[x]) 
     2264        { 
     2265        found = true; 
     2266        } 
     2267      } 
     2268    if(!found) 
     2269      { 
     2270      new_classes[new_classes.length] = existing[i]; 
     2271      } 
     2272    } 
     2273 
     2274  return new_classes.join(' '); 
     2275 
     2276  } 
     2277 
     2278// --------------------------------------------------- 
     2279 
     2280/** 
     2281* _addClass() 
     2282*/ 
     2283 
     2284HTMLArea._addClass = function(el, className)  
     2285  { 
     2286  // remove the class first, if already there 
     2287  HTMLArea._removeClass(el, className); 
     2288  el.className += " " + className; 
     2289  }; 
     2290 
     2291// ---------------------------------------------------- 
     2292 
     2293/** 
     2294* _hasClass() 
     2295*/ 
     2296 
     2297HTMLArea._hasClass = function(el, className)  
     2298  { 
     2299  if (!(el && el.className))  
     2300    { 
     2301    return false; 
     2302    } 
     2303  var cls = el.className.split(" "); 
     2304 
     2305  for (var i = cls.length; i > 0;)  
     2306    { 
     2307    if (cls[--i] == className)  
     2308           { 
     2309      return true; 
     2310      } 
     2311    } 
     2312 
     2313  return false; 
     2314 
     2315  }; 
     2316 
     2317// ----------------------------------------------------- 
     2318 
     2319/** 
     2320* _removeClass() 
     2321*/ 
     2322 
     2323HTMLArea._removeClass = function(el, className)  
     2324  { 
     2325 
     2326  if (!(el && el.className))  
     2327    { 
     2328    return; 
     2329    } 
     2330 
     2331  var cls = el.className.split(" "); 
     2332  var ar = new Array(); 
     2333  for (var i = cls.length; i > 0;)  
     2334    { 
     2335    if (cls[--i] != className)  
     2336           { 
     2337      ar[ar.length] = cls[i]; 
     2338      } 
     2339    } 
     2340 
     2341  el.className = ar.join(" "); 
     2342 
     2343  }; 
     2344 
     2345// -------------------------------------------- 
     2346 
     2347/**  
     2348* Alias these for convenience  
     2349*/ 
     2350 
     2351HTMLArea.addClass       = HTMLArea._addClass; 
     2352HTMLArea.removeClass    = HTMLArea._removeClass; 
     2353HTMLArea._addClasses    = HTMLArea.addClasses; 
     2354HTMLArea._removeClasses = HTMLArea.removeClasses; 
     2355 
     2356// -------------------------------------------- 
     2357 
     2358/** 
     2359* _addEvent() 
     2360*/ 
     2361 
     2362HTMLArea._addEvent = function(el, evname, func)  
     2363  { 
     2364  if (HTMLArea.is_ie)  
     2365    { 
     2366    el.attachEvent("on" + evname, func); 
     2367    }  
     2368  else  
     2369    { 
     2370    el.addEventListener(evname, func, true); 
     2371    } 
     2372  }; 
     2373 
     2374// ----------------------------------------------- 
     2375 
     2376/** 
     2377* _addEvents() 
     2378*/ 
     2379 
     2380HTMLArea._addEvents = function(el, evs, func)  
     2381  { 
     2382  for (var i = evs.length; --i >= 0;)  
     2383    { 
     2384    HTMLArea._addEvent(el, evs[i], func); 
     2385    } 
     2386 
     2387  }; 
     2388 
     2389// ----------------------------------------------- 
     2390 
     2391/** 
     2392* _removeEvent() 
     2393*/ 
     2394 
     2395HTMLArea._removeEvent = function(el, evname, func)  
     2396  { 
     2397  if (HTMLArea.is_ie)  
     2398    { 
     2399    el.detachEvent("on" + evname, func); 
     2400    }  
     2401  else  
     2402    { 
     2403    el.removeEventListener(evname, func, true); 
     2404    } 
     2405 
     2406  }; 
     2407 
     2408// ------------------------------------------------- 
     2409 
     2410/** 
     2411* _removeEvents() 
     2412*/ 
     2413 
     2414HTMLArea._removeEvents = function(el, evs, func)  
     2415  { 
     2416  for (var i = evs.length; --i >= 0;)  
     2417    { 
     2418    HTMLArea._removeEvent(el, evs[i], func); 
     2419    } 
     2420 
     2421  }; 
     2422 
     2423// ------------------------------------------------- 
     2424 
     2425/** 
     2426* _stopEvent() 
     2427*/ 
     2428 
     2429HTMLArea._stopEvent = function(ev)  
     2430  { 
     2431  if (HTMLArea.is_ie)  
     2432    { 
     2433    ev.cancelBubble = true; 
     2434    ev.returnValue = false; 
     2435    }  
     2436  else  
     2437    { 
     2438    ev.preventDefault(); 
     2439    ev.stopPropagation(); 
     2440    } 
     2441 
     2442  }; 
     2443 
     2444// ------------------------------------------------------------------- 
     2445//                 HTMLAREA (i.e. XINHA) MAIN CLASS 
     2446// ------------------------------------------------------------------- 
     2447 
     2448/** 
     2449* Creates a new HTMLArea object but does not cause it to be displayed.   
     2450* 
     2451* @class This is the main Xinha class the manages the HTML Editors functions. 
     2452* 
     2453* If this is not a supported browser the textarea is left untouched. 
     2454* 
     2455* @constructor 
     2456* @see HTMLArea.Config 
     2457* @see #generate 
     2458*/ 
     2459 
     2460function HTMLArea(textarea, config)  
     2461  { 
     2462 
     2463  // if this is not a supported browser degrade gracefully to a normal 
     2464  // textarea. 
     2465 
     2466  if (HTMLArea.checkSupportedBrowser())  
     2467    { 
     2468    if (typeof config == "undefined")  
     2469      { 
    662470      this.config = new HTMLArea.Config(); 
    67     } else { 
     2471      }  
     2472    else  
     2473      { 
    682474      this.config = config; 
    69     } 
     2475      } 
     2476 
     2477    // [STRIP 
     2478    // create a ddt debug trace object. textarea is a string here. 
     2479 
     2480    this.ddt = new DDT( textarea ); 
     2481 
     2482    // uncomment to turn on debugging messages. 
     2483 
     2484    // this.ddt._ddtOn(); 
     2485    // STRIP] 
     2486 
     2487    this.ddt._ddt("HTMLArea(): DDT Trace System Initialized."); 
     2488 
    702489    this._htmlArea = null; 
    712490    this._textArea = textarea; 
     
    862505    // Panels 
    872506    var panels = this._panels = 
    88     { 
     2507      { 
    892508      right: 
    90       { 
     2509        { 
    912510        on: true, 
    922511        div:    document.createElement('div'), 
     
    942513      }, 
    952514      left: 
    96       { 
     2515        { 
    972516        on: true, 
    982517        div:    document.createElement('div'), 
    992518        panels: [ ] 
    100       }, 
     2519        }, 
    1012520      top: 
    102       { 
     2521        { 
    1032522        on: true, 
    1042523        div:    document.createElement('div'), 
    1052524        panels: [ ] 
    106       }, 
     2525        }, 
    1072526      bottom: 
    108       { 
     2527        { 
    1092528        on: true, 
    1102529        div:    document.createElement('div'), 
    1112530        panels: [ ] 
    112       } 
    113     }; 
     2531        } 
     2532      }; 
    1142533 
    1152534    for(var i in panels) 
    116     { 
     2535      { 
    1172536      panels[i].div.className = 'panels ' + i; 
    118     } 
    119   } 
    120 }; 
    121  
    122 HTMLArea.onload = function(){}; 
    123 HTMLArea._scripts = []; 
    124  
    125 HTMLArea.loadScript = function(url, plugin) { 
    126   if (plugin) 
    127     url = HTMLArea.getPluginDir(plugin) + '/' + url; 
    128   this._scripts.push(url); 
    129 }; 
    130  
    131  
    132 HTMLArea.init = function() { 
    133  
    134   var head = document.getElementsByTagName("head")[0]; 
    135   var current = 0; 
    136   var savetitle = document.title; 
    137   var evt = HTMLArea.is_ie ? "onreadystatechange" : "onload"; 
    138   function loadNextScript() { 
    139     if (current > 0 && HTMLArea.is_ie && 
    140         !/loaded|complete/.test(window.event.srcElement.readyState)) 
    141       return; 
    142     if (current < HTMLArea._scripts.length) { 
    143       var url = HTMLArea._scripts[current++]; 
    144       document.title = "[HTMLArea: loading script " + current + "/" + HTMLArea._scripts.length + "]"; 
    145       var script = document.createElement("script"); 
    146       script.type = "text/javascript"; 
    147       script.src = url; 
    148       script[evt] = loadNextScript; 
    149       head.appendChild(script); 
    150     } else { 
    151       document.title = savetitle; 
    152       HTMLArea.onload(); 
    153     } 
    154   }; 
    155   loadNextScript(); 
    156 }; 
    157  
    158 HTMLArea.loadScript(_editor_url + "dialog.js"); 
    159 HTMLArea.loadScript(_editor_url + "inline-dialog.js"); 
    160 HTMLArea.loadScript(_editor_url + "popupwin.js"); 
    161  
    162 // cache some regexps 
    163 HTMLArea.RE_tagName = /(<\/|<)\s*([^ \t\n>]+)/ig; 
    164 HTMLArea.RE_doctype = /(<!doctype((.|\n)*?)>)\n?/i; 
    165 HTMLArea.RE_head    = /<head>((.|\n)*?)<\/head>/i; 
    166 HTMLArea.RE_body    = /<body[^>]*>((.|\n)*?)<\/body>/i; 
    167 HTMLArea.RE_Specials = /([\/\^$*+?.()|{}[\]])/g; 
    168 HTMLArea.RE_email    = /[a-z0-9_]{3,}@[a-z0-9_-]{2,}(\.[a-z0-9_-]{2,})+/i; 
    169 HTMLArea.RE_url      = /(https?:\/\/)?(([a-z0-9_]+:[a-z0-9_]+@)?[a-z0-9_-]{2,}(\.[a-z0-9_-]{2,}){2,}(:[0-9]+)?(\/\S+)*)/i; 
    170  
    171 HTMLArea.Config = function () { 
    172   var cfg = this; 
    173   this.version = "3.0"; 
    174  
    175   this.width = "toolbar"; 
    176   this.height = "auto"; 
    177  
    178   //language of the editor 
    179   this.lang = "en"; 
    180  
    181   // 
    182   this.lcBackend = "lcbackend.php?lang=$lang&context=$context"; 
    183  
    184   // enable creation of a status bar? 
    185   this.statusBar = true; 
    186  
    187   // intercept ^V and use the HTMLArea paste command 
    188   // If false, then passes ^V through to browser editor widget 
    189   this.htmlareaPaste = false; 
    190  
    191   this.mozParaHandler = 'best'; // set to 'built-in', 'dirty' or 'best' 
    192                                 // built-in: will (may) use 'br' instead of 'p' tags 
    193                                 // dirty   : will use p and work good enough for the majority of cases, 
    194                                 // best    : works the best, but it's about 12kb worth of javascript 
    195                                 //   and will probably be slower than 'dirty'.  This is the "EnterParagraphs" 
    196                                 //   plugin from "hipikat", rolled in to be part of the core code 
    197  
    198   // maximum size of the undo queue 
    199   this.undoSteps = 20; 
    200  
    201   // the time interval at which undo samples are taken 
    202   this.undoTimeout = 500;       // 1/2 sec. 
    203  
    204   // the next parameter specifies whether the toolbar should be included 
    205   // in the size or not. 
    206   this.sizeIncludesToolbar = true; 
    207  
    208   // if true then HTMLArea will retrieve the full HTML, starting with the 
    209   // <HTML> tag. 
    210   this.fullPage = false; 
    211  
    212   // style included in the iframe document 
    213   this.pageStyle = ""; 
    214  
    215   // external stylesheets to load (REFERENCE THESE ABSOLUTELY) 
    216   this.pageStyleSheets = [ ]; 
    217  
    218   // specify a base href for relative links 
    219   this.baseHref  = null; 
    220  
    221   // we can strip the base href out of relative links to leave them relative, reason for this 
    222   //   especially if you don't specify a baseHref is that mozilla at least (& IE ?) will prefix 
    223   //   the baseHref to any relative links to make them absolute, which isn't what you want most the time. 
    224   this.stripBaseHref = true; 
    225  
    226   // and we can strip the url of the editor page from named links (eg <a href="#top">...</a>) 
    227   //  reason for this is that mozilla at least (and IE ?) prefixes location.href to any 
    228   //  that don't have a url prefixing them 
    229   this.stripSelfNamedAnchors = true; 
    230  
    231   // sometimes we want to be able to replace some string in the html comng in and going out 
    232   //  so that in the editor we use the "internal" string, and outside and in the source view 
    233   //  we use the "external" string  this is useful for say making special codes for 
    234   //  your absolute links, your external string might be some special code, say "{server_url}" 
    235   //  an you say that the internal represenattion of that should be http://your.server/ 
    236   this.specialReplacements = { }; // { 'external_string' : 'internal_string' } 
    237  
    238   // set to true if you want Word code to be cleaned upon Paste 
    239   this.killWordOnPaste = true; 
    240  
    241   // enable the 'Target' field in the Make Link dialog 
    242   this.makeLinkShowsTarget = true; 
    243  
    244   // BaseURL included in the iframe document 
    245   this.baseURL = document.baseURI || document.URL; 
    246   if (this.baseURL && this.baseURL.match(/(.*)\/([^\/]+)/)) 
    247     this.baseURL = RegExp.$1 + "/"; 
    248  
    249   // CharSet of the iframe, default is the charset of the document 
    250   this.charSet = HTMLArea.is_gecko ? document.characterSet : document.charset; 
    251  
    252   // URL-s 
    253   this.imgURL = "images/"; 
    254   this.popupURL = "popups/"; 
    255   this.helpURL  = _editor_url + "reference.html"; 
    256  
    257   // remove tags (these have to be a regexp, or null if this functionality is not desired) 
    258   this.htmlRemoveTags = null; 
    259  
    260   /** CUSTOMIZING THE TOOLBAR 
    261    * ------------------------- 
    262    * 
    263    * It is recommended that you customize the toolbar contents in an 
    264    * external file (i.e. the one calling HTMLArea) and leave this one 
    265    * unchanged.  That's because when we (InteractiveTools.com) release a 
    266    * new official version, it's less likely that you will have problems 
    267    * upgrading HTMLArea. 
    268    */ 
    269   this.toolbar = 
    270   [ 
    271     ["popupeditor","separator"], 
    272     ["formatblock","fontname","fontsize","bold","italic","underline","strikethrough","separator"], 
    273     ["forecolor","hilitecolor","textindicator","separator"], 
    274     ["subscript","superscript"], 
    275     ["linebreak","justifyleft","justifycenter","justifyright","justifyfull","separator"], 
    276     ["insertorderedlist","insertunorderedlist","outdent","indent","separator"], 
    277     ["inserthorizontalrule","createlink","insertimage","inserttable","separator"], 
    278     ["undo","redo"], (HTMLArea.is_gecko ? [] : ["cut","copy","paste"]),["separator"], 
    279     ["killword","removeformat","toggleborders","lefttoright", "righttoleft", "separator","htmlmode","about"] 
    280   ]; 
    281  
    282   // Width of the "Right Side" panel, when present 
    283   this.panel_dimensions = 
    284   { 
    285     left:   '200px', // Width 
    286     right:  '200px', 
    287     top:    '100px', // Height 
    288     bottom: '100px' 
    289   } 
    290  
    291   this.fontname = { 
    292     "&mdash; font &mdash;":         '', 
    293     "Arial":       'arial,helvetica,sans-serif', 
    294     "Courier New":         'courier new,courier,monospace', 
    295     "Georgia":     'georgia,times new roman,times,serif', 
    296     "Tahoma":      'tahoma,arial,helvetica,sans-serif', 
    297     "Times New Roman": 'times new roman,times,serif', 
    298     "Verdana":     'verdana,arial,helvetica,sans-serif', 
    299     "impact":      'impact', 
    300     "WingDings":           'wingdings' 
    301   }; 
    302  
    303   this.fontsize = { 
    304     "&mdash; size &mdash;"  : "", 
    305     "1 (8 pt)" : "1", 
    306     "2 (10 pt)": "2", 
    307     "3 (12 pt)": "3", 
    308     "4 (14 pt)": "4", 
    309     "5 (18 pt)": "5", 
    310     "6 (24 pt)": "6", 
    311     "7 (36 pt)": "7" 
    312   }; 
    313  
    314   this.formatblock = { 
    315     "&mdash; format &mdash;"  : "", 
    316     "Heading 1": "h1", 
    317     "Heading 2": "h2", 
    318     "Heading 3": "h3", 
    319     "Heading 4": "h4", 
    320     "Heading 5": "h5", 
    321     "Heading 6": "h6", 
    322     "Normal"   : "p", 
    323     "Address"  : "address", 
    324     "Formatted": "pre" 
    325   }; 
    326  
    327   this.customSelects = {}; 
    328  
    329   function cut_copy_paste(e, cmd, obj) { 
    330     e.execCommand(cmd); 
    331   }; 
    332  
    333   this.debug = true; 
    334  
    335   this.URIs = { 
    336    "blank": "popups/blank.html", 
    337    "link": "link.html", 
    338    "insert_image": "insert_image.html", 
    339    "insert_table": "insert_table.html", 
    340    "select_color": "select_color.html", 
    341    "fullscreen": "fullscreen.html", 
    342    "about": "about.html", 
    343    "mozilla_security": "http://mozilla.org/editor/midasdemo/securityprefs.html" 
    344   }; 
    345  
    346  
    347   // ADDING CUSTOM BUTTONS: please read below! 
    348   // format of the btnList elements is "ID: [ ToolTip, Icon, Enabled in text mode?, ACTION ]" 
    349   //    - ID: unique ID for the button.  If the button calls document.execCommand 
    350   //        it's wise to give it the same name as the called command. 
    351   //    - ACTION: function that gets called when the button is clicked. 
    352   //              it has the following prototype: 
    353   //                 function(editor, buttonName) 
    354   //              - editor is the HTMLArea object that triggered the call 
    355   //              - buttonName is the ID of the clicked button 
    356   //              These 2 parameters makes it possible for you to use the same 
    357   //              handler for more HTMLArea objects or for more different buttons. 
    358   //    - ToolTip: tooltip, will be translated below 
    359   //    - Icon: path to an icon image file for the button 
    360   //            OR; you can use an 18x18 block of a larger image by supllying an array 
    361   //            that has three elemtents, the first is the larger image, the second is the column 
    362   //            the third is the row.  The ros and columns numbering starts at 0 but there is 
    363   //            a header row and header column which have numbering to make life easier. 
    364   //            See images/buttons_main.gif to see how it's done. 
    365   //    - Enabled in text mode: if false the button gets disabled for text-only mode; otherwise enabled all the time. 
    366   this.btnList = { 
    367     bold:          [ "Bold",   ["ed_buttons_main.gif",3,2], false, function(e) {e.execCommand("bold");} ], 
    368     italic:        [ "Italic", ["ed_buttons_main.gif",2,2], false, function(e) {e.execCommand("italic");} ], 
    369     underline:     [ "Underline", ["ed_buttons_main.gif",2,0], false, function(e) {e.execCommand("underline");} ], 
    370     strikethrough: [ "Strikethrough", ["ed_buttons_main.gif",3,0], false, function(e) {e.execCommand("strikethrough");} ], 
    371     subscript:     [ "Subscript", ["ed_buttons_main.gif",3,1], false, function(e) {e.execCommand("subscript");} ], 
    372     superscript:   [ "Superscript", ["ed_buttons_main.gif",2,1], false, function(e) {e.execCommand("superscript");} ], 
    373  
    374     justifyleft:   [ "Justify Left", ["ed_buttons_main.gif",0,0], false, function(e) {e.execCommand("justifyleft");} ], 
    375     justifycenter: [ "Justify Center", ["ed_buttons_main.gif",1,1], false, function(e){e.execCommand("justifycenter");}], 
    376     justifyright: [ "Justify Right", ["ed_buttons_main.gif",1,0], false, function(e) {e.execCommand("justifyright");} ], 
    377     justifyfull: [ "Justify Full", ["ed_buttons_main.gif",0,1], false, function(e) {e.execCommand("justifyfull");} ], 
    378  
    379  
    380     orderedlist: [ "Ordered List", ["ed_buttons_main.gif",0,3], false, function(e) {e.execCommand("insertorderedlist");} ], 
    381     unorderedlist: [ "Bulleted List", ["ed_buttons_main.gif",1,3], false, function(e) {e.execCommand("insertunorderedlist");} ], 
    382     insertorderedlist: [ "Ordered List", ["ed_buttons_main.gif",0,3], false, function(e) {e.execCommand("insertorderedlist");} ], 
    383     insertunorderedlist: [ "Bulleted List", ["ed_buttons_main.gif",1,3], false, function(e) {e.execCommand("insertunorderedlist");} ], 
    384  
    385     outdent: [ "Decrease Indent", ["ed_buttons_main.gif",1,2], false, function(e) {e.execCommand("outdent");} ], 
    386     indent: [ "Increase Indent",["ed_buttons_main.gif",0,2], false, function(e) {e.execCommand("indent");} ], 
    387     forecolor: [ "Font Color", ["ed_buttons_main.gif",3,3], false, function(e) {e.execCommand("forecolor");} ], 
    388     hilitecolor: [ "Background Color", ["ed_buttons_main.gif",2,3], false, function(e) {e.execCommand("hilitecolor");} ], 
    389  
    390     undo: [ "Undoes your last action", ["ed_buttons_main.gif",4,2], false, function(e) {e.execCommand("undo");} ], 
    391     redo: [ "Redoes your last action", ["ed_buttons_main.gif",5,2], false, function(e) {e.execCommand("redo");} ], 
    392     cut: [ "Cut selection", ["ed_buttons_main.gif",5,0], false, cut_copy_paste ], 
    393     copy: [ "Copy selection", ["ed_buttons_main.gif",4,0], false, cut_copy_paste ], 
    394     paste: [ "Paste from clipboard", ["ed_buttons_main.gif",4,1], false, cut_copy_paste ], 
    395  
    396  
    397  
    398     inserthorizontalrule: [ "Horizontal Rule", ["ed_buttons_main.gif",6,0], false, function(e) {e.execCommand("inserthorizontalrule");} ], 
    399     createlink: [ "Insert Web Link", ["ed_buttons_main.gif",6,1], false, function(e) {e._createLink();} ], 
    400     insertimage: [ "Insert/Modify Image", ["ed_buttons_main.gif",6,3], false, function(e) {e.execCommand("insertimage");} ], 
    401     inserttable: [ "Insert Table", ["ed_buttons_main.gif",6,2], false, function(e) {e.execCommand("inserttable");} ], 
    402  
    403  
    404     htmlmode: [ "Toggle HTML Source", ["ed_buttons_main.gif",7,0], true, function(e) {e.execCommand("htmlmode");} ], 
    405     toggleborders: [ "Toggle Borders", ["ed_buttons_main.gif",7,2], false, function(e) { e._toggleBorders() } ], 
    406     print:         [ "Print document", ["ed_buttons_main.gif",8,1], false, function(e) {e._iframe.contentWindow.print();} ], 
    407     popupeditor: [ "Enlarge Editor", "fullscreen_maximize.gif", true, 
    408       function(e, objname, obj) 
    409       { 
    410         e.execCommand("popupeditor"); 
    411       } ], 
    412     about: [ "About this editor", ["ed_buttons_main.gif",8,2], true, function(e) {e.execCommand("about");} ], 
    413     showhelp: [ "Help using editor", ["ed_buttons_main.gif",9,2], true, function(e) {e.execCommand("showhelp");} ], 
    414  
    415     splitblock:    [ "Split Block", "ed_splitblock.gif", false, function(e) {e._splitBlock();} ], 
    416     lefttoright: [ "Direction left to right", ["ed_buttons_main.gif",0,4], false, function(e) {e.execCommand("lefttoright");} ], 
    417     righttoleft: [ "Direction right to left", ["ed_buttons_main.gif",1,4], false, function(e) {e.execCommand("righttoleft");} ], 
    418  
    419     wordclean:     [ "MS Word Cleaner", ["ed_buttons_main.gif",5,3], false, function(e) {e._wordClean();} ], 
    420     clearfonts:    [ "Clear Inline Font Specifications", ["ed_buttons_main.gif",5,4], false, function(e) {e._clearFonts();} ], 
    421     removeformat:  [ "Remove formatting", ["ed_buttons_main.gif",4,4], false, function(e) {e.execCommand("removeformat");} ], 
    422     killword:      [ "Clear MSOffice tags", ["ed_buttons_main.gif",4,3], false, function(e) {e.execCommand("killword");} ] 
    423  
    424   }; 
    425   /* ADDING CUSTOM BUTTONS 
    426    * --------------------- 
    427    * 
    428    * It is recommended that you add the custom buttons in an external 
    429    * file and leave this one unchanged.  That's because when we 
    430    * (InteractiveTools.com) release a new official version, it's less 
    431    * likely that you will have problems upgrading HTMLArea. 
    432    * 
    433    * Example on how to add a custom button when you construct the HTMLArea: 
    434    * 
    435    *   var editor = new HTMLArea("your_text_area_id"); 
    436    *   var cfg = editor.config; // this is the default configuration 
    437    *   cfg.btnList["my-hilite"] = 
    438    *    [ function(editor) { editor.surroundHTML('<span style="background:yellow">', '</span>'); }, // action 
    439    *      "Highlight selection", // tooltip 
    440    *      "my_hilite.gif", // image 
    441    *      false // disabled in text mode 
    442    *    ]; 
    443    *   cfg.toolbar.push(["linebreak", "my-hilite"]); // add the new button to the toolbar 
    444    * 
    445    * An alternate (also more convenient and recommended) way to 
    446    * accomplish this is to use the registerButton function below. 
    447    */ 
    448   // initialize tooltips from the I18N module and generate correct image path 
    449   for (var i in this.btnList) { 
    450     var btn = this.btnList[i]; 
    451     if(typeof btn[1] != 'string') 
    452     { 
    453       btn[1][0] = _editor_url + this.imgURL + btn[1][0]; 
    454     } 
    455     else 
    456     { 
    457       btn[1] = _editor_url + this.imgURL + btn[1]; 
    458     } 
    459     btn[0] = HTMLArea._lc(btn[0]); //initialize tooltip 
    460   } 
    461 }; 
    462  
    463 /** Helper function: register a new button with the configuration.  It can be 
    464  * called with all 5 arguments, or with only one (first one).  When called with 
    465  * only one argument it must be an object with the following properties: id, 
    466  * tooltip, image, textMode, action.  Examples: 
    467  * 
    468  * 1. config.registerButton("my-hilite", "Hilite text", "my-hilite.gif", false, function(editor) {...}); 
    469  * 2. config.registerButton({ 
    470  *      id       : "my-hilite",      // the ID of your button 
    471  *      tooltip  : "Hilite text",    // the tooltip 
    472  *      image    : "my-hilite.gif",  // image to be displayed in the toolbar 
    473  *      textMode : false,            // disabled in text mode 
    474  *      action   : function(editor) { // called when the button is clicked 
    475  *                   editor.surroundHTML('<span class="hilite">', '</span>'); 
    476  *                 }, 
    477  *      context  : "p"               // will be disabled if outside a <p> element 
    478  *    }); 
    479  */ 
    480 HTMLArea.Config.prototype.registerButton = function(id, tooltip, image, textMode, action, context) { 
    481   var the_id; 
    482   if (typeof id == "string") { 
    483     the_id = id; 
    484   } else if (typeof id == "object") { 
    485     the_id = id.id; 
    486   } else { 
    487     alert("ERROR [HTMLArea.Config::registerButton]:\ninvalid arguments"); 
    488     return false; 
    489   } 
    490   // check for existing id 
    491   if (typeof this.customSelects[the_id] != "undefined") { 
    492     // alert("WARNING [HTMLArea.Config::registerDropdown]:\nA dropdown with the same ID already exists."); 
    493   } 
    494   if (typeof this.btnList[the_id] != "undefined") { 
    495     // alert("WARNING [HTMLArea.Config::registerDropdown]:\nA button with the same ID already exists."); 
    496   } 
    497   switch (typeof id) { 
    498       case "string": this.btnList[id] = [ tooltip, image, textMode, action, context ]; break; 
    499       case "object": this.btnList[id.id] = [ id.tooltip, id.image, id.textMode, id.action, id.context ]; break; 
    500   } 
    501 }; 
    502  
    503 HTMLArea.prototype.registerPanel = function(side, object) 
    504 { 
    505   if(!side) side = 'right'; 
    506   var panel = this.addPanel(side); 
    507   if(object) 
    508   { 
    509     object.drawPanelIn(panel); 
    510   } 
    511 } 
    512  
    513 /** The following helper function registers a dropdown box with the editor 
    514  * configuration.  You still have to add it to the toolbar, same as with the 
    515  * buttons.  Call it like this: 
    516  * 
    517  * FIXME: add example 
    518  */ 
    519 HTMLArea.Config.prototype.registerDropdown = function(object) { 
    520   // check for existing id 
    521   if (typeof this.customSelects[object.id] != "undefined") { 
    522     // alert("WARNING [HTMLArea.Config::registerDropdown]:\nA dropdown with the same ID already exists."); 
    523   } 
    524   if (typeof this.btnList[object.id] != "undefined") { 
    525     // alert("WARNING [HTMLArea.Config::registerDropdown]:\nA button with the same ID already exists."); 
    526   } 
    527   this.customSelects[object.id] = object; 
    528 }; 
    529  
    530 /** Call this function to remove some buttons/drop-down boxes from the toolbar. 
    531  * Pass as the only parameter a string containing button/drop-down names 
    532  * delimited by spaces.  Note that the string should also begin with a space 
    533  * and end with a space.  Example: 
    534  * 
    535  *   config.hideSomeButtons(" fontname fontsize textindicator "); 
    536  * 
    537  * It's useful because it's easier to remove stuff from the defaul toolbar than 
    538  * create a brand new toolbar ;-) 
    539  */ 
    540 HTMLArea.Config.prototype.hideSomeButtons = function(remove) { 
    541   var toolbar = this.toolbar; 
    542   for (var i = toolbar.length; --i >= 0;) { 
    543     var line = toolbar[i]; 
    544     for (var j = line.length; --j >= 0; ) { 
    545       if (remove.indexOf(" " + line[j] + " ") >= 0) { 
    546         var len = 1; 
    547         if (/separator|space/.test(line[j + 1])) { 
    548           len = 2; 
    549         } 
    550         line.splice(j, len); 
    551       } 
    552     } 
    553   } 
    554 }; 
    555  
    556 /** Helper function: replace all TEXTAREA-s in the document with HTMLArea-s. */ 
    557 HTMLArea.replaceAll = function(config) { 
    558   var tas = document.getElementsByTagName("textarea"); 
    559   for (var i = tas.length; i > 0; (new HTMLArea(tas[--i], config)).generate()); 
    560 }; 
    561  
    562 /** Helper function: replaces the TEXTAREA with the given ID with HTMLArea. */ 
    563 HTMLArea.replace = function(id, config) 
    564 { 
    565   var ta = HTMLArea.getElementById("textarea", id); 
    566   if (ta) 
    567   { 
    568     var taobj = new HTMLArea(ta, config); 
    569     taobj.generate(); 
    570     return taobj; 
    571   } 
    572   else 
    573   { 
    574     return null; 
    575   }; 
    576 }; 
    577  
    578 // Creates the toolbar and appends it to the _htmlarea 
    579 HTMLArea.prototype._createToolbar = function () { 
     2537      } 
     2538 
     2539    }  // end of if we are a supported browser. 
     2540 
     2541  this.ddt._ddt( "HTMLArea(): end" ); 
     2542 
     2543  };  // end of HTMLArea() constructor 
     2544 
     2545// ----------------------------------- 
     2546 
     2547/** 
     2548* Creates the toolbar and appends it to the _htmlarea 
     2549*/ 
     2550 
     2551HTMLArea.prototype._createToolbar = function ()  
     2552  { 
     2553 
     2554  this.ddt._ddt( "_createToolbar(): top" ); 
     2555 
    5802556  var editor = this;    // to access this in nested functions 
    5812557 
     
    5892565  this._toolbarObjects = tb_objects; 
    5902566 
    591         this._createToolbar1(editor, toolbar, tb_objects); 
    592         this._htmlArea.appendChild(toolbar); 
    593 } 
    594  
    595  
    596 HTMLArea.prototype._setConfig = function(config) { 
    597         this.config = config; 
    598 } 
    599  
    600 HTMLArea.prototype._addToolbar = function() { 
    601         this._createToolbar1(this, this._toolbar, this._toolbarObjects); 
    602 } 
    603  
    604 // separate from previous createToolBar to allow dynamic change of toolbar 
    605 HTMLArea.prototype._createToolbar1 = function (editor, toolbar, tb_objects) { 
     2567  this._createToolbar1(editor, toolbar, tb_objects); 
     2568  this._htmlArea.appendChild(toolbar); 
     2569 
     2570  }  // end of _createToolbar() 
     2571 
     2572// ----------------------------------- 
     2573 
     2574/** 
     2575* registerPanel() 
     2576*/ 
     2577 
     2578HTMLArea.prototype.registerPanel = function(side, object) 
     2579  { 
     2580 
     2581  this.ddt._ddt( "registerPanel(): top with side '" + side + "'" ); 
     2582 
     2583  if(!side) side = 'right'; 
     2584 
     2585  var panel = this.addPanel(side); 
     2586 
     2587  if(object) 
     2588    { 
     2589    object.drawPanelIn(panel); 
     2590    } 
     2591  } 
     2592 
     2593// ----------------------------------- 
     2594 
     2595/** 
     2596* _setConfig() 
     2597*/ 
     2598 
     2599HTMLArea.prototype._setConfig = function(config)  
     2600  { 
     2601  this.config = config; 
     2602  } 
     2603 
     2604// ----------------------------------- 
     2605 
     2606/** 
     2607* _addToolbar() 
     2608*/ 
     2609 
     2610HTMLArea.prototype._addToolbar = function()  
     2611  { 
     2612  this._createToolbar1(this, this._toolbar, this._toolbarObjects); 
     2613  } 
     2614 
     2615// ------------------------------------------------------ 
     2616 
     2617/** 
     2618* separate from previous createToolBar to allow dynamic change of toolbar 
     2619*/ 
     2620 
     2621HTMLArea.prototype._createToolbar1 = function (editor, toolbar, tb_objects)  
     2622  { 
     2623 
     2624  this.ddt._ddt( "_createToolbar1(): top" ); 
    6062625 
    6072626  // creates a new line in the toolbar 
    608   function newLine() { 
     2627 
     2628  function newLine( editor )  
     2629    { 
     2630 
     2631         editor.ddt._ddt( "newLine(): top" ); 
     2632 
    6092633    var table = document.createElement("table"); 
    6102634    table.border = "0px"; 
     
    6182642    tb_row = document.createElement("tr"); 
    6192643    tb_body.appendChild(tb_row); 
    620   }; // END of function: newLine 
     2644 
     2645    }; // END of function: newLine 
     2646 
    6212647  // init first line 
    622   newLine(); 
    623  
    624   // updates the state of a toolbar element.  This function is member of 
    625   // a toolbar element object (unnamed objects created by createButton or 
    626   // createSelect functions below). 
    627   function setButtonStatus(id, newval) { 
     2648 
     2649  newLine( this ); 
     2650 
     2651  /** 
     2652  * updates the state of a toolbar element.   
     2653  * 
     2654  * This function is member of 
     2655  * a toolbar element object (unnamed objects created by createButton or 
     2656  * createSelect functions below). 
     2657  * 
     2658  * We don't have access to the editor object from within this function so we're 
     2659  * sending these messages to the startupWindow.  
     2660  * 
     2661  * @todo provide a way to get this instance of the editor object. 
     2662  */ 
     2663 
     2664  function setButtonStatus(id, newval )  
     2665    { 
     2666 
     2667         HTMLArea._ddt( "== setButtonStatus() : top with id '" + id + "' value '" + newval + '"' ); 
     2668 
    6282669    var oldval = this[id]; 
    6292670    var el = this.element; 
    630     if (oldval != newval) { 
    631       switch (id) { 
    632           case "enabled": 
    633         if (newval) { 
    634           HTMLArea._removeClass(el, "buttonDisabled"); 
    635           el.disabled = false; 
    636         } else { 
    637           HTMLArea._addClass(el, "buttonDisabled"); 
    638           el.disabled = true; 
    639         } 
    640         break; 
    641           case "active": 
    642         if (newval) { 
    643           HTMLArea._addClass(el, "buttonPressed"); 
    644         } else { 
    645           HTMLArea._removeClass(el, "buttonPressed"); 
    646         } 
    647         break; 
    648       } 
     2671 
     2672    if (oldval != newval)  
     2673           { 
     2674      switch (id)  
     2675                  { 
     2676        case "enabled": 
     2677          if (newval)  
     2678                           { 
     2679            HTMLArea._removeClass(el, "buttonDisabled"); 
     2680            el.disabled = false; 
     2681            }  
     2682                         else  
     2683                           { 
     2684            HTMLArea._addClass(el, "buttonDisabled"); 
     2685            el.disabled = true; 
     2686            } 
     2687          break; 
     2688        case "active": 
     2689          if (newval)  
     2690                           { 
     2691            HTMLArea._addClass(el, "buttonPressed"); 
     2692            } 
     2693                         else  
     2694                           { 
     2695            HTMLArea._removeClass(el, "buttonPressed"); 
     2696            } 
     2697          break; 
     2698 
     2699        }  // end of switch 
     2700 
    6492701      this[id] = newval; 
    650     } 
    651   }; // END of function: setButtonStatus 
    652  
    653   // this function will handle creation of combo boxes.  Receives as 
    654   // parameter the name of a button as defined in the toolBar config. 
    655   // This function is called from createButton, above, if the given "txt" 
    656   // doesn't match a button. 
    657   function createSelect(txt) { 
     2702      } 
     2703 
     2704    }; // END of function: setButtonStatus 
     2705 
     2706  /** 
     2707  * createSelect() - creates combo boxes 
     2708  * 
     2709  * this function will handle creation of combo boxes.  Receives as 
     2710  * parameter the name of a button as defined in the toolBar config. 
     2711  * This function is called from createButton, above, if the given "txt" 
     2712  * doesn't match a button. 
     2713  * 
     2714  * This is another function where we don't have good access to the editor 
     2715  * object so we dump trace messages into the startup box. Not ideal. 
     2716  * 
     2717  * @todo provide a way to get this instance of the editor object. 
     2718  */ 
     2719 
     2720  function createSelect(txt)  
     2721    { 
     2722 
     2723         HTMLArea._ddt( "== createSelect(): top with text '" + txt + "'" ); 
     2724 
    6582725    var options = null; 
    6592726    var el = null; 
     
    6622729    var context = null; 
    6632730    var tooltip = ""; 
    664     switch (txt) { 
    665         case "fontsize": 
    666         case "fontname": 
    667         case "formatblock": 
    668       // the following line retrieves the correct 
    669       // configuration option because the variable name 
    670       // inside the Config object is named the same as the 
    671       // button/select in the toolbar.  For instance, if txt 
    672       // == "formatblock" we retrieve config.formatblock (or 
    673       // a different way to write it in JS is 
    674       // config["formatblock"]. 
    675       options = editor.config[txt]; 
    676       cmd = txt; 
    677       break; 
    678         default: 
    679       // try to fetch it from the list of registered selects 
    680       cmd = txt; 
    681       var dropdown = customSelects[cmd]; 
    682       if (typeof dropdown != "undefined") { 
    683         options = dropdown.options; 
    684         context = dropdown.context; 
    685         if (typeof dropdown.tooltip != "undefined") { 
    686           tooltip = dropdown.tooltip; 
    687         } 
    688       } else { 
    689         alert("ERROR [createSelect]:\nCan't find the requested dropdown definition"); 
    690       } 
    691       break; 
    692     } 
    693     if (options) { 
     2731 
     2732    switch (txt)  
     2733           { 
     2734      case "fontsize": 
     2735      case "fontname": 
     2736      case "formatblock": 
     2737 
     2738        // the following line retrieves the correct 
     2739        // configuration option because the variable name 
     2740        // inside the Config object is named the same as the 
     2741        // button/select in the toolbar.  For instance, if txt 
     2742        // == "formatblock" we retrieve config.formatblock (or 
     2743        // a different way to write it in JS is 
     2744        // config["formatblock"]. 
     2745 
     2746        options = editor.config[txt]; 
     2747        cmd = txt; 
     2748        break; 
     2749 
     2750      default: 
     2751        // try to fetch it from the list of registered selects 
     2752 
     2753        cmd = txt; 
     2754        var dropdown = customSelects[cmd]; 
     2755 
     2756        if (typeof dropdown != "undefined")  
     2757                    { 
     2758          options = dropdown.options; 
     2759          context = dropdown.context; 
     2760          if (typeof dropdown.tooltip != "undefined")  
     2761                           { 
     2762            tooltip = dropdown.tooltip; 
     2763            } 
     2764          }  
     2765                  else  
     2766                    { 
     2767          alert("ERROR [createSelect]:\nCan't find the requested dropdown definition"); 
     2768          } 
     2769        break; 
     2770 
     2771      } // end of switch 
     2772 
     2773    if (options)  
     2774           { 
    6942775      el = document.createElement("select"); 
     2776 
    6952777      el.title = tooltip; 
    696       var obj = { 
     2778 
     2779      var obj =  
     2780                  { 
    6972781        name    : txt, // field name 
    6982782        element : el,   // the UI element (SELECT) 
     
    7022786        state   : setButtonStatus, // for changing state 
    7032787        context : context 
    704       }; 
     2788        }; 
     2789 
    7052790      tb_objects[txt] = obj; 
    706       for (var i in options) { 
     2791 
     2792      for (var i in options)  
     2793                  { 
    7072794        var op = document.createElement("option"); 
    7082795        op.innerHTML = i; 
    7092796        op.value = options[i]; 
    7102797        el.appendChild(op); 
    711       } 
    712       HTMLArea._addEvent(el, "change", function () { 
    713         editor._comboSelected(el, txt); 
    714       }); 
    715     } 
     2798        } 
     2799 
     2800      HTMLArea._addEvent(el, "change",  
     2801                  function ()  
     2802                    { 
     2803          editor._comboSelected(el, txt); 
     2804          }); 
     2805      } 
     2806 
     2807         HTMLArea._ddt( "createSelect(): end" ); 
     2808 
    7162809    return el; 
     2810 
    7172811  }; // END of function: createSelect 
    7182812 
    719   // appends a new button to toolbar 
    720   function createButton(txt) { 
     2813  // -------------------------------- 
     2814 
     2815  /** 
     2816  * appends a new button to toolbar 
     2817  */ 
     2818 
     2819  function createButton(txt, editor )  
     2820    { 
     2821 
     2822         editor.ddt._ddt( "createButton(): top with text '" + txt + "'" ); 
     2823 
    7212824    // the element that will be created 
     2825 
    7222826    var el = null; 
    7232827    var btn = null; 
    724     switch (txt) { 
    725         case "separator": 
    726       el = document.createElement("div"); 
    727       el.className = "separator"; 
    728       break; 
    729         case "space": 
    730       el = document.createElement("div"); 
    731       el.className = "space"; 
    732       break; 
    733         case "linebreak": 
    734       newLine(); 
    735       return false; 
    736         case "textindicator": 
    737       el = document.createElement("div"); 
    738       el.appendChild(document.createTextNode("A")); 
    739       el.className = "indicator"; 
    740       el.title = HTMLArea._lc("Current style"); 
    741       var obj = { 
    742         name    : txt, // the button name (i.e. 'bold') 
    743         element : el, // the UI element (DIV) 
    744         enabled : true, // is it enabled? 
    745         active  : false, // is it pressed? 
    746         text    : false, // enabled in text mode? 
    747         cmd     : "textindicator", // the command ID 
    748         state   : setButtonStatus // for changing state 
    749       }; 
    750       tb_objects[txt] = obj; 
    751       break; 
    752         default: 
    753       btn = editor.config.btnList[txt]; 
    754     } 
    755     if (!el && btn) { 
     2828 
     2829    switch (txt)  
     2830           { 
     2831      case "separator": 
     2832        el = document.createElement("div"); 
     2833        el.className = "separator"; 
     2834        break; 
     2835 
     2836      case "space": 
     2837        el = document.createElement("div"); 
     2838        el.className = "space"; 
     2839        break; 
     2840 
     2841      case "linebreak": 
     2842        newLine( editor ); 
     2843        return false; 
     2844 
     2845      case "textindicator": 
     2846        el = document.createElement("div"); 
     2847        el.appendChild(document.createTextNode("A")); 
     2848        el.className = "indicator"; 
     2849        el.title = HTMLArea._lc("Current style"); 
     2850 
     2851        var obj =  
     2852                    { 
     2853          name  : txt, // the button name (i.e. 'bold') 
     2854          element : el, // the UI element (DIV) 
     2855          enabled : true, // is it enabled? 
     2856          active        : false, // is it pressed? 
     2857          text  : false, // enabled in text mode? 
     2858          cmd   : "textindicator", // the command ID 
     2859          state : setButtonStatus // for changing state 
     2860          }; 
     2861 
     2862        tb_objects[txt] = obj; 
     2863 
     2864        break; 
     2865 
     2866      default: 
     2867        btn = editor.config.btnList[txt]; 
     2868      } 
     2869 
     2870    if (!el && btn)  
     2871           { 
    7562872      el = document.createElement("a"); 
    7572873      el.style.display = 'block'; 
     
    7602876      el.title = btn[0]; 
    7612877      el.className = "button"; 
     2878 
    7622879      // let's just pretend we have a button object, and 
    7632880      // assign all the needed information to it. 
    764       var obj = { 
     2881 
     2882      var obj =  
     2883                  { 
    7652884        name    : txt, // the button name (i.e. 'bold') 
    7662885        element : el, // the UI element (DIV) 
     
    7712890        state   : setButtonStatus, // for changing state 
    7722891        context : btn[4] || null // enabled in a certain context? 
    773       }; 
     2892        }; 
     2893 
    7742894      tb_objects[txt] = obj; 
     2895 
    7752896      // handlers to emulate nice flat toolbar buttons 
    776       HTMLArea._addEvent(el, "mouseout", function () { 
    777         if (obj.enabled) with (HTMLArea) { 
    778           //_removeClass(el, "buttonHover"); 
    779           _removeClass(el, "buttonActive"); 
    780           (obj.active) && _addClass(el, "buttonPressed"); 
    781         } 
    782       }); 
    783  
    784       HTMLArea._addEvent(el, "mousedown", function (ev) { 
    785         if (obj.enabled) with (HTMLArea) { 
    786           _addClass(el, "buttonActive"); 
    787           _removeClass(el, "buttonPressed"); 
    788           _stopEvent(is_ie ? window.event : ev); 
    789         } 
    790       }); 
     2897 
     2898      HTMLArea._addEvent(el, "mouseout",  
     2899                  function ()  
     2900                    { 
     2901          if (obj.enabled) with (HTMLArea)  
     2902                      { 
     2903            //_removeClass(el, "buttonHover"); 
     2904            _removeClass(el, "buttonActive"); 
     2905            (obj.active) && _addClass(el, "buttonPressed"); 
     2906          } 
     2907        }); 
     2908 
     2909      HTMLArea._addEvent(el, "mousedown",  
     2910                  function (ev)  
     2911                    { 
     2912          if (obj.enabled) with (HTMLArea)  
     2913                           { 
     2914            _addClass(el, "buttonActive"); 
     2915            _removeClass(el, "buttonPressed"); 
     2916            _stopEvent(is_ie ? window.event : ev); 
     2917            } 
     2918          }); 
     2919 
    7912920      // when clicked, do the following: 
    792       HTMLArea._addEvent(el, "click", function (ev) { 
    793         if (obj.enabled) with (HTMLArea) { 
    794           _removeClass(el, "buttonActive"); 
    795           //_removeClass(el, "buttonHover"); 
    796           if(HTMLArea.is_gecko) 
    797           { 
    798             editor.activateEditor(); 
    799           } 
    800           obj.cmd(editor, obj.name, obj); 
    801           _stopEvent(is_ie ? window.event : ev); 
    802         } 
    803       }); 
     2921 
     2922      HTMLArea._addEvent(el, "click",  
     2923                  function (ev)  
     2924                    { 
     2925          if (obj.enabled) with (HTMLArea)  
     2926                           { 
     2927            _removeClass(el, "buttonActive"); 
     2928            //_removeClass(el, "buttonHover"); 
     2929 
     2930            if ( HTMLArea.is_gecko ) 
     2931              { 
     2932              editor.activateEditor(); 
     2933              } 
     2934 
     2935            obj.cmd(editor, obj.name, obj); 
     2936 
     2937            _stopEvent(is_ie ? window.event : ev); 
     2938            } 
     2939          }); 
    8042940 
    8052941      var i_contain = HTMLArea.makeBtnImg(btn[1]); 
     
    8082944 
    8092945      obj.imgel = img; 
     2946 
    8102947      obj.swapImage = function(newimg) 
    811       { 
     2948        { 
    8122949        if(typeof newimg != 'string') 
    813         { 
     2950          { 
    8142951          img.src = newimg[0]; 
    8152952          img.style.position = 'relative'; 
    8162953          img.style.top  = newimg[2] ? ('-' + (18 * (newimg[2] + 1)) + 'px') : '-18px'; 
    8172954          img.style.left = newimg[1] ? ('-' + (18 * (newimg[1] + 1)) + 'px') : '-18px'; 
    818         } 
     2955          } 
    8192956        else 
    820         { 
     2957          { 
    8212958          obj.imgel.src = newimg; 
    8222959          img.style.top = '0px'; 
    8232960          img.style.left = '0px'; 
     2961          } 
    8242962        } 
    825       } 
    826  
    827     } else if (!el) { 
     2963 
     2964      }  
     2965         else if (!el)  
     2966           { 
    8282967      el = createSelect(txt); 
    829     } 
    830     if (el) { 
     2968      } 
     2969 
     2970    if (el)  
     2971           { 
    8312972      var tb_cell = document.createElement("td"); 
    8322973      tb_row.appendChild(tb_cell); 
    8332974      tb_cell.appendChild(el); 
    834     } else { 
     2975      }  
     2976         else  
     2977           { 
    8352978      alert("FIXME: Unknown toolbar item: " + txt); 
    836     } 
     2979      } 
     2980 
    8372981    return el; 
    838   }; 
     2982 
     2983    };  // end of in-line function createButton() 
    8392984 
    8402985  var first = true; 
    841   for (var i = 0; i < this.config.toolbar.length; ++i) { 
    842     if (!first) { 
     2986 
     2987  for (var i = 0; i < this.config.toolbar.length; ++i)  
     2988    { 
     2989    if (!first)  
     2990           { 
    8432991      // createButton("linebreak"); 
    844     } else { 
     2992      }  
     2993         else  
     2994           { 
    8452995      first = false; 
    846     } 
    847     if(this.config.toolbar[i] == null) this.config.toolbar[i] = ['separator']; 
     2996      } 
     2997 
     2998    if (this.config.toolbar[i] == null)  
     2999           this.config.toolbar[i] = ['separator']; 
     3000 
    8483001    var group = this.config.toolbar[i]; 
    8493002 
    8503003    for (var j = 0; j < group.length; ++j) 
    851     { 
     3004      { 
    8523005      var code = group[j]; 
     3006 
    8533007      if (/^([IT])\[(.*?)\]/.test(code)) 
    854       { 
     3008        { 
     3009 
    8553010        // special case, create text label 
     3011 
    8563012        var l7ed = RegExp.$1 == "I"; // localized? 
    8573013        var label = RegExp.$2; 
    858         if (l7ed) { 
    859           label = HTMLArea._lc(label); 
    860         } 
     3014 
     3015        if (l7ed)  
     3016                    { 
     3017               label = HTMLArea._lc(label); 
     3018          } 
     3019 
    8613020        var tb_cell = document.createElement("td"); 
    8623021        tb_row.appendChild(tb_cell); 
    8633022        tb_cell.className = "label"; 
    8643023        tb_cell.innerHTML = label; 
    865       } 
     3024        } 
    8663025      else if(typeof code != 'function') 
    867       { 
    868         createButton(code); 
    869       } 
    870     } 
    871   } 
    872 }; 
    873  
    874 use_clone_img = false; 
    875 HTMLArea.makeBtnImg = function(imgDef, doc) 
    876 { 
    877   if(!doc) doc = document; 
    878  
    879   if(!doc._htmlareaImgCache) 
    880   { 
    881     doc._htmlareaImgCache = { }; 
    882   } 
    883  
    884   var i_contain = null; 
    885   if(HTMLArea.is_ie && ((!doc.compatMode) || (doc.compatMode && doc.compatMode == "BackCompat"))) 
    886   { 
    887     i_contain = doc.createElement('span'); 
    888   } 
    889   else 
    890   { 
    891     i_contain = doc.createElement('div'); 
    892     i_contain.style.position = 'relative'; 
    893   } 
    894  
    895   i_contain.style.overflow = 'hidden'; 
    896   i_contain.style.width = "18px"; 
    897   i_contain.style.height = "18px"; 
    898  
    899  
    900   var img = null; 
    901   if(typeof imgDef == 'string') 
    902   { 
    903     if(doc._htmlareaImgCache[imgDef]) 
    904     { 
    905       img = doc._htmlareaImgCache[imgDef].cloneNode(); 
    906     } 
    907     else 
    908     { 
    909       img = doc.createElement("img"); 
    910       img.src = imgDef; 
    911       img.style.width = "18px"; 
    912       img.style.height = "18px"; 
    913       if(use_clone_img) 
    914         doc._htmlareaImgCache[imgDef] = img.cloneNode(); 
    915     } 
    916   } 
    917   else 
    918   { 
    919     if(doc._htmlareaImgCache[imgDef[0]]) 
    920     { 
    921       img = doc._htmlareaImgCache[imgDef[0]].cloneNode(); 
    922     } 
    923     else 
    924     { 
    925       img = doc.createElement("img"); 
    926       img.src = imgDef[0]; 
    927       img.style.position = 'relative'; 
    928       if(use_clone_img) 
    929         doc._htmlareaImgCache[imgDef[0]] = img.cloneNode(); 
    930     } 
    931     img.style.top  = imgDef[2] ? ('-' + (18 * (imgDef[2] + 1)) + 'px') : '-18px'; 
    932     img.style.left = imgDef[1] ? ('-' + (18 * (imgDef[1] + 1)) + 'px') : '-18px'; 
    933   } 
    934   i_contain.appendChild(img); 
    935   return i_contain; 
    936 } 
    937  
    938 HTMLArea.prototype._createStatusBar = function() { 
     3026        { 
     3027        createButton(code, this); 
     3028        } 
     3029      } 
     3030    } 
     3031  };  // end of HTMLArea.prototype._createToolBar1 
     3032 
     3033// ----------------------------------------------------- 
     3034 
     3035/** 
     3036* _createStatusBar() 
     3037*/ 
     3038 
     3039HTMLArea.prototype._createStatusBar = function()  
     3040  { 
     3041 
     3042  this.ddt._ddt( "_createStatusBar(): top" ); 
     3043 
    9393044  var statusbar = document.createElement("div"); 
    9403045  statusbar.className = "statusBar"; 
    9413046  this._htmlArea.appendChild(statusbar); 
    9423047  this._statusBar = statusbar; 
     3048 
    9433049  // statusbar.appendChild(document.createTextNode(HTMLArea._lc("Path") + ": ")); 
    9443050  // creates a holder for the path view 
     3051 
    9453052  div = document.createElement("span"); 
    9463053  div.className = "statusBarTree"; 
     
    9483055  this._statusBarTree = div; 
    9493056  this._statusBar.appendChild(div); 
    950   if (!this.config.statusBar) { 
     3057 
     3058  if (!this.config.statusBar)  
     3059    { 
    9513060    // disable it... 
    9523061    statusbar.style.display = "none"; 
    953   } 
    954 }; 
    955  
    956 // Creates the HTMLArea object and replaces the textarea with it. 
     3062    } 
     3063  };    // end of HTMLArea.prototype._createStatusBar() 
     3064 
     3065// ---------------------------------------------------------------- 
     3066 
     3067/** 
     3068* Completes the setup of the HTMLArea object and replaces the textarea with it. 
     3069* 
     3070* @see HTMLArea 
     3071*/ 
     3072 
    9573073HTMLArea.prototype.generate = function () 
    958 { 
     3074  { 
     3075 
     3076  this.ddt._ddt( "generate(): top" ); 
     3077 
    9593078  var editor = this;    // we'll need "this" in some nested functions 
    9603079 
    9613080  // If this is gecko, set up the paragraph handling now 
    962   if(HTMLArea.is_gecko) 
    963   { 
     3081 
     3082  if (HTMLArea.is_gecko) 
     3083    { 
    9643084    switch(editor.config.mozParaHandler) 
    965     { 
     3085      { 
    9663086      case 'best': 
    967       { 
    968         if(typeof EnterParagraphs == 'undefined') 
    9693087        { 
     3088        if (typeof EnterParagraphs == 'undefined') 
     3089          { 
    9703090          EnterParagraphs = 'null'; 
    9713091          HTMLArea._loadback 
    9723092            (_editor_url + 'plugins/EnterParagraphs/enter-paragraphs.js', function() { editor.registerPlugin('EnterParagraphs'); editor.generate(); } ); 
    9733093          return false; 
     3094          } 
    9743095        } 
    975       } 
    976       break; 
     3096 
     3097        break; 
    9773098 
    9783099      case 'dirty'   : 
    9793100      case 'built-in': 
    9803101      default        : 
    981       { 
     3102        { 
    9823103        // See _editorEvent 
    983       } 
    984       break; 
    985     } 
    986   } 
     3104        } 
     3105 
     3106        break; 
     3107 
     3108      } // end of switch. 
     3109 
     3110    }  // end of special support for gecko. 
    9873111 
    9883112  // get the textarea 
     3113 
    9893114  var textarea = this._textArea; 
     3115 
    9903116  if (typeof textarea == "string") 
    991   { 
     3117    { 
    9923118    this._textArea = textarea = HTMLArea.getElementById("textarea", textarea); 
    993   } 
     3119    } 
     3120 
    9943121  this._ta_size = 
    995   { 
     3122    { 
    9963123    w: textarea.offsetWidth, 
    9973124    h: textarea.offsetHeight 
    998   }; 
     3125    }; 
    9993126 
    10003127  // create the editor framework 
     
    10023129  htmlarea.className = "htmlarea"; 
    10033130  this._htmlArea = htmlarea; 
    1004   if(this.config.width != 'auto' && this.config.width != 'toolbar') 
    1005   { 
     3131 
     3132  if ( this.config.width != 'auto' && this.config.width != 'toolbar' ) 
     3133    { 
    10063134    htmlarea.style.width = this.config.width; 
    1007   } 
     3135    } 
    10083136 
    10093137  // insert the editor before the textarea. 
     
    10283156  iframe.src = _editor_url + editor.config.URIs["blank"]; 
    10293157  this._iframe = iframe; 
    1030  
    10313158 
    10323159  // - I don't think this is required, see the .htmlarea iframe in htmlarea.css 
     
    10373164  // } 
    10383165 
    1039  
    10403166  // Add the panels 
    10413167  for(var i in this._panels) 
    1042   { 
     3168    { 
    10433169    innerEditor.appendChild(this._panels[i].div); 
    1044   } 
     3170    } 
    10453171 
    10463172  // creates & appends the status bar 
    10473173  this._createStatusBar(); 
    10483174 
    1049  
    10503175  // Set up event listeners for saving the iframe content to the textarea 
    1051   if (textarea.form) { 
     3176  if (textarea.form)  
     3177    { 
     3178 
    10523179    // we have a form, on submit get the HTMLArea content and 
    10533180    // update original textarea. 
     3181 
    10543182    var f = textarea.form; 
     3183 
    10553184    if (typeof f.__msh_prevOnSubmit == "undefined") 
    1056     { 
     3185      { 
    10573186      f.__msh_prevOnSubmit = []; 
    10583187      if (typeof f.onsubmit == "function") 
    1059       { 
     3188        { 
    10603189        var funcref = f.onsubmit; 
    10613190        f.__msh_prevOnSubmit.push(funcref); 
    10623191        f.onsubmit = null; 
    1063       } 
     3192        } 
    10643193 
    10653194      f.onsubmit = function() 
    1066       { 
     3195        { 
     3196 
     3197                  this.ddt._ddt( "generate(): f.onsubmit(): top" ); 
     3198 
    10673199        var a = this.__msh_prevOnSubmit; 
     3200 
    10683201        // call previous submit methods if they were there. 
    10693202        var allOK = true; 
     3203 
    10703204        for (var i = a.length; --i >= 0;) 
    1071         { 
     3205          { 
    10723206          // We want the handler to be a member of the form, not the array, so that "this" will work correctly 
    10733207          this.__msh_tempEventHandler = a[i]; 
     3208 
    10743209          if(this.__msh_tempEventHandler() == false) 
    1075           { 
     3210            { 
    10763211            allOK = false; 
    10773212            break; 
     3213            } 
    10783214          } 
     3215 
     3216        return allOK; 
    10793217        } 
    1080         return allOK; 
    1081       } 
    1082     } 
     3218      } 
     3219 
    10833220    f.__msh_prevOnSubmit.push(function() {editor._textArea.value = editor.outwardHtml(editor.getHTML());}); 
    10843221 
    10853222    if (typeof f.__msh_prevOnReset == "undefined") 
    1086     { 
     3223      { 
    10873224      f.__msh_prevOnReset = []; 
     3225 
    10883226      if (typeof f.onreset == "function") 
    1089       { 
     3227        { 
    10903228        var funcref = f.onreset; 
    10913229        f.__msh_prevOnReset.push(funcref); 
    10923230        f.onreset = null; 
    1093       } 
     3231        } 
    10943232 
    10953233      f.onreset = function() 
    1096       { 
     3234        { 
     3235 
     3236        this.ddt._ddt( "generate(): f.onreset(): top" ); 
     3237 
    10973238        var a = this.__msh_prevOnReset; 
    10983239        // call previous submit methods if they were there. 
    10993240        var allOK = true; 
    11003241        for (var i = a.length; --i >= 0;) 
    1101         { 
     3242          { 
    11023243          if(a[i]() == false) 
    1103           { 
     3244            { 
    11043245            allOK = false; 
    11053246            break; 
     3247            } 
    11063248          } 
     3249 
     3250        return allOK; 
    11073251        } 
    1108         return allOK; 
    1109       } 
    1110     } 
    1111     f.__msh_prevOnReset.push(function() {editor.setHTML(editor._textArea.value); editor.updateToolbar();}); 
    1112   } 
     3252 
     3253      }  // end of if msh_prevOnReset was undefined 
     3254 
     3255    f.__msh_prevOnReset.push( function() {editor.setHTML(editor._textArea.value); editor.updateToolbar();}); 
     3256 
     3257    }  // end of if textarea.form 
    11133258 
    11143259  // add a handler for the "back/forward" case -- on body.unload we save 
    11153260  // the HTML content into the original textarea. 
    1116   try { 
     3261 
     3262  try  
     3263    { 
    11173264    HTMLArea._addEvent(window, 'unload', function() {textarea.value = editor.outwardHtml(editor.getHTML());} ); 
    1118   } catch(e) {}; 
     3265    } catch(e) {}; 
    11193266 
    11203267  // Hide textarea 
     3268 
    11213269  textarea.style.display = "none"; 
    11223270 
     
    11263274 
    11273275  switch(this.config.height) 
    1128   { 
     3276    { 
    11293277    // "auto" means the same height as the original textarea 
    11303278    case 'auto' : { height = parseInt(this._ta_size.h);    break; } 
    11313279    // otherwise we expect it to be a PIXEL height 
    11323280    default     : { height = parseInt(this.config.height); break; } 
    1133   } 
     3281    } 
    11343282 
    11353283  switch(this.config.width) 
    1136   { 
     3284    { 
    11373285    // toolbar means the width is the same as the toolbar 
    11383286    case 'toolbar': {width = parseInt(this._toolbar.offsetWidth); break; } 
     
    11413289    // otherwise it is expected to be a PIXEL width 
    11423290    default       : {width = parseInt(this.config.width);        break; } 
    1143   } 
     3291    } 
    11443292 
    11453293  if (this.config.sizeIncludesToolbar) 
    1146   { 
     3294    { 
    11473295    // substract toolbar height 
    11483296    height -= this._toolbar.offsetHeight; 
    11493297    height -= this._statusBar.offsetHeight; 
    1150   } 
     3298    } 
    11513299 
    11523300  // Minimal size = 100x100 
     
    11573305  this.notifyOn('panel_change',function(){editor.setInnerSize();}); 
    11583306 
     3307  this.ddt._ddt( "generate(): bottom before timeout call to initIframe()" ); 
    11593308 
    11603309  // IMPORTANT: we have to allow Mozilla a short time to recognize the 
     
    11623311 
    11633312  setTimeout(function() { editor.initIframe()}, 50); 
    1164 }; 
    1165  
    1166   /** Size the htmlArea according to the available space 
    1167    *   Width and Height include toolbar! 
    1168    **/ 
    1169  
    1170   HTMLArea.prototype.getInnerSize = function() 
    1171   { 
    1172     return this._innerSize; 
    1173   } 
    1174  
    1175   HTMLArea.prototype.setInnerSize = function(width, height) 
    1176   { 
    1177     if(typeof width == 'undefined' || width == null) 
    1178     { 
    1179       width  = this._innerSize.width; 
    1180     } 
    1181  
    1182     if(typeof height == 'undefined' || height == null) 
    1183     { 
    1184       height  = this._innerSize.height; 
    1185     } 
    1186  
    1187     this._innerSize = {'width':width,'height':height}; 
    1188  
    1189     var editorWidth  = width; 
    1190     var editorHeight = height; 
    1191     var editorLeft   = 0; 
    1192     var editorTop    = 0; 
    1193     var panels = this._panels; 
    1194  
    1195     var panel = panels.right; 
    1196     if(panel.on && panel.panels.length && HTMLArea.hasDisplayedChildren(panel.div)) 
    1197     { 
    1198       panel.div.style.position = 'absolute'; 
    1199       panel.div.style.width    = parseInt(this.config.panel_dimensions.right) + (HTMLArea.ie_ie ? -1 : -2) + 'px'; 
    1200       panel.div.style.height   = height + (HTMLArea.is_ie ? -1 : -1) + 'px'; 
    1201       panel.div.style.top      = '0px'; 
    1202       panel.div.style.right    = (HTMLArea.is_ie ? 1 : 2) + 'px'; 
    1203       panel.div.style.padding  = "0px"; 
    1204       panel.div.style.overflow = "auto"; 
    1205       panel.div.style.display  = 'block'; 
    1206       editorWidth -= parseInt(this.config.panel_dimensions.right) + (HTMLArea.is_ie ? 2 : 0); 
    1207     } 
    1208     else 
    1209     { 
    1210       panel.div.style.display  = 'none'; 
    1211     } 
    1212  
    1213     var panel = panels.left; 
    1214     if(panel.on && panel.panels.length && HTMLArea.hasDisplayedChildren(panel.div)) 
    1215     { 
    1216       panel.div.style.position = 'absolute'; 
    1217       panel.div.style.width    = parseInt(this.config.panel_dimensions.left) + (HTMLArea.ie_ie ? -1 : -1) + 'px'; 
    1218       panel.div.style.height   = height + (HTMLArea.is_ie ? -1 : -1) + 'px'; 
    1219       panel.div.style.top      = '0px'; 
    1220       panel.div.style.left     = (HTMLArea.is_ie ? 0 : 0) + 'px'; 
    1221       panel.div.style.padding  = "0px"; 
    1222       panel.div.style.overflow = "auto"; 
    1223       panel.div.style.display  = "block"; 
    1224       editorWidth -= parseInt(this.config.panel_dimensions.left) + (HTMLArea.is_ie ? 2 : 0); 
    1225       editorLeft   = parseInt(this.config.panel_dimensions.left) + (HTMLArea.is_ie ? 2 : 0) + 'px'; 
    1226     } 
    1227     else 
    1228     { 
    1229       panel.div.style.display  = 'none'; 
    1230     } 
    1231  
    1232     var panel = panels.top; 
    1233     if(panel.on && panel.panels.length && HTMLArea.hasDisplayedChildren(panel.div)) 
    1234     { 
    1235       panel.div.style.position = 'absolute'; 
    1236       panel.div.style.top      = '0px'; 
    1237       panel.div.style.left     = '0px'; 
    1238       panel.div.style.width    = width  + 'px'; 
    1239       panel.div.style.height   = parseInt(this.config.panel_dimensions.top) + 'px'; 
    1240       panel.div.style.padding  = "0px"; 
    1241       panel.div.style.overflow = "auto"; 
    1242       panel.div.style.display  = "block"; 
    1243       editorHeight -= parseInt(this.config.panel_dimensions.top); 
    1244       editorTop     = parseInt(this.config.panel_dimensions.top) + 'px'; 
    1245     } 
    1246     else 
    1247     { 
    1248       panel.div.style.display  = 'none'; 
    1249     } 
    1250  
    1251     var panel = panels.bottom; 
    1252     if(panel.on && panel.panels.length && HTMLArea.hasDisplayedChildren(panel.div)) 
    1253     { 
    1254       panel.div.style.position = 'absolute'; 
    1255       panel.div.style.bottom   = '0px'; 
    1256       panel.div.style.left     = '0px'; 
    1257       panel.div.style.width    = width  + 'px'; 
    1258       panel.div.style.height   = parseInt(this.config.panel_dimensions.bottom) + 'px'; 
    1259       panel.div.style.padding  = "0px"; 
    1260       panel.div.style.overflow = "auto"; 
    1261       panel.div.style.display  = "block"; 
    1262       editorHeight -= parseInt(this.config.panel_dimensions.bottom); 
    1263     } 
    1264     else 
    1265     { 
    1266       panel.div.style.display  = 'none'; 
    1267     } 
    1268  
    1269     // Set the dimensions of the container 
    1270     this.innerEditor.style.width  = width  + 'px'; 
    1271     this.innerEditor.style.height = height + 'px'; 
    1272     this.innerEditor.style.position = 'relative'; 
    1273  
    1274     // and the iframe 
    1275     this._iframe.style.width  = editorWidth  + 'px'; 
    1276     this._iframe.style.height = editorHeight + 'px'; 
    1277     this._iframe.style.position = 'absolute'; 
    1278     this._iframe.style.left = editorLeft; 
    1279     this._iframe.style.top  = editorTop; 
    1280  
    1281  
    1282     // the editor including the toolbar now have the same size as the 
    1283     // original textarea.. which means that we need to reduce that a bit. 
    1284     this._textArea.style.width  = editorWidth  + 'px'; 
    1285     this._textArea.style.height = editorHeight + 'px'; 
    1286     this._textArea.style.position = 'absolute'; 
    1287     this._textArea.style.left = editorLeft; 
    1288     this._textArea.style.top  = editorTop; 
    1289  
    1290     this.notifyOf('resize', {'width':width,'height':height,'editorWidth':editorWidth,'editorHeight':editorHeight,'editorTop':editorTop,'editorLeft':editorLeft}); 
    1291   } 
    1292  
    1293   HTMLArea.prototype.addPanel = function(side) 
    1294   { 
    1295     var div = document.createElement('div'); 
    1296     div.side = side; 
    1297     HTMLArea.addClasses(div, 'panel'); 
    1298     this._panels[side].panels.push(div); 
    1299     this._panels[side].div.appendChild(div); 
    1300  
    1301     this.notifyOf('panel_change', {'action':'add','panel':div}); 
    1302  
    1303     return div; 
    1304   } 
    1305  
    1306   HTMLArea.prototype.removePanel = function(panel) 
    1307   { 
    1308     this._panels[panel.side].div.removeChild(panel); 
    1309     var clean = [ ]; 
    1310     for(var i = 0; i < this._panels[panel.side].panels.length; i++) 
    1311     { 
    1312       if(this._panels[panel.side].panels[i] != panel) 
    1313       { 
    1314         clean.push(this._panels[panel.side].panels[i]); 
    1315       } 
    1316     } 
    1317     this._panels[panel.side].panels = clean; 
    1318     this.notifyOf('panel_change', {'action':'remove','panel':panel}); 
    1319   } 
    1320  
    1321   HTMLArea.prototype.hidePanel = function(panel) 
    1322   { 
    1323     if(panel) 
    1324   { 
    1325     panel.style.display = 'none'; 
    1326     this.notifyOf('panel_change', {'action':'hide','panel':panel}); 
    1327   } 
    1328   } 
    1329  
    1330   HTMLArea.prototype.showPanel = function(panel) 
    1331   { 
    1332     if(panel) 
    1333   { 
    1334     panel.style.display = ''; 
    1335     this.notifyOf('panel_change', {'action':'show','panel':panel}); 
    1336     } 
    1337   } 
    1338  
    1339   HTMLArea.prototype.hidePanels = function(sides) 
    1340   { 
    1341     if(typeof sides == 'undefined') 
    1342     { 
    1343       sides = ['left','right','top','bottom']; 
    1344     } 
    1345  
    1346     var reShow = []; 
    1347     for(var i = 0; i < sides.length;i++) 
    1348     { 
    1349       if(this._panels[sides[i]].on) 
    1350       { 
    1351         reShow.push(sides[i]); 
    1352         this._panels[sides[i]].on = false; 
    1353       } 
    1354     } 
    1355     this.notifyOf('panel_change', {'action':'multi_hide','sides':sides}); 
    1356   } 
    1357  
    1358   HTMLArea.prototype.showPanels = function(sides) 
    1359   { 
    1360     if(typeof sides == 'undefined') 
    1361     { 
    1362       sides = ['left','right','top','bottom']; 
    1363     } 
    1364  
    1365     var reHide = []; 
    1366     for(var i = 0; i < sides.length;i++) 
    1367     { 
    1368       if(!this._panels[sides[i]].on) 
    1369       { 
    1370         reHide.push(sides[i]); 
    1371         this._panels[sides[i]].on = true; 
    1372       } 
    1373     } 
    1374     this.notifyOf('panel_change', {'action':'multi_show','sides':sides}); 
    1375   } 
    1376  
    1377   HTMLArea.objectProperties = function(obj) 
    1378   { 
    1379     var props = [ ]; 
    1380     for(var x in obj) 
    1381     { 
    1382       props[props.length] = x; 
    1383     } 
    1384     return props; 
    1385   } 
    1386  
    1387   HTMLArea.prototype.activateEditor = function() 
    1388   { 
    1389     if (HTMLArea.is_gecko && this._doc.designMode != 'on') 
    1390     { 
    1391       try{HTMLArea.last_on.designMode = 'off';} catch(e) { } 
    1392       if(this._iframe.style.display == 'none') 
    1393       { 
    1394         this._iframe.style.display = ''; 
    1395         this._doc.designMode = 'on'; 
    1396         this._iframe.style.display = 'none'; 
    1397       } 
    1398       else 
    1399       { 
    1400         this._doc.designMode = 'on'; 
    1401       } 
    1402     } 
    1403     else 
    1404     { 
    1405       this._doc.body.contentEditable = true; 
    1406     } 
    1407     HTMLArea.last_on = this._doc; 
    1408   } 
    1409  
    1410   HTMLArea.prototype.deactivateEditor = function() 
    1411   { 
    1412     if(HTMLArea.is_gecko && this._doc.designMode == 'on') 
    1413     { 
    1414       this._doc.designMode = 'off'; 
    1415       HTMLArea.last_on = null; 
    1416     } 
    1417     else 
    1418     { 
    1419       this._doc.body.contentEditable = false; 
    1420     } 
    1421   } 
    1422  
    1423   HTMLArea.prototype.initIframe = function() 
    1424   { 
    1425     var doc = null; 
    1426     var editor = this; 
    1427     try 
    1428     { 
    1429       doc = editor._iframe.contentWindow.document; 
    1430       if (!doc) { 
    1431         // Try again.. 
    1432         // FIXME: don't know what else to do here.  Normally 
    1433         // we'll never reach this point. 
    1434         if (HTMLArea.is_gecko) { 
    1435           setTimeout(function() { editor.initIframe()}, 50); 
    1436           return false; 
    1437         } else { 
    1438           alert("ERROR: IFRAME can't be initialized."); 
     3313 
     3314  };  // end of HTMLArea.prototype.generate() 
     3315 
     3316// --------------------------------------------------- 
     3317 
     3318/** 
     3319* initFrame 
     3320* 
     3321* @see #generate HTMLArea.prototype.generate() 
     3322*/ 
     3323 
     3324HTMLArea.prototype.initIframe = function() 
     3325  { 
     3326  var doc = null; 
     3327  var editor = this; 
     3328 
     3329  this.ddt._ddt( "initFrame(): top" ); 
     3330 
     3331  try 
     3332    { 
     3333    doc = editor._iframe.contentWindow.document; 
     3334 
     3335    if (!doc)  
     3336           { 
     3337      // Try again.. 
     3338      // FIXME: don't know what else to do here.  Normally 
     3339      // we'll never reach this point. 
     3340 
     3341      if (HTMLArea.is_gecko)  
     3342                  { 
     3343        setTimeout(function() { editor.initIframe()}, 50); 
     3344        return false; 
     3345        }  
     3346                else  
     3347                  { 
     3348        alert("ERROR: IFRAME can't be initialized."); 
    14393349        } 
    14403350      } 
    14413351    } 
    1442     catch(e) 
    1443     { 
    1444       setTimeout(function() { editor.initIframe()}, 50); 
    1445     } 
    1446  
    1447     if (!editor.config.fullPage) { 
    1448       doc.open(); 
    1449       var html = "<html>\n"; 
    1450       html += "<head>\n"; 
    1451       html += "<meta http-equiv=\"Content-Type\" content=\"text/html; charset=" + editor.config.charSet + "\">\n"; 
    1452       if(typeof editor.config.baseHref != 'undefined' && editor.config.baseHref != null) 
    1453       { 
    1454         html += "<base href=\"" + editor.config.baseHref + "\"/>\n"; 
    1455       } 
    1456       html += "<style title=\"table borders\">" 
    1457            + ".htmtableborders, .htmtableborders td, .htmtableborders th {border : 1px dashed lightgrey ! important;} \n" 
    1458            + "</style>\n"; 
    1459       html += "<style>" 
    1460            + editor.config.pageStyle + "\n" 
    1461            + "html, body { border: 0px; } \n" 
    1462            + "span.macro, span.macro ul, span.macro div, span.macro p {background : #CCCCCC;}\n" 
    1463            + "</style>\n"; 
    1464       if(typeof editor.config.pageStyleSheets !== 'undefined') 
    1465       { 
    1466         for(style_i = 0; style_i < editor.config.pageStyleSheets.length; style_i++) 
     3352  catch(e) 
     3353    { 
     3354    setTimeout(function() { editor.initIframe()}, 50); 
     3355    } 
     3356 
     3357  if (!editor.config.fullPage)  
     3358    { 
     3359    doc.open(); 
     3360    var html = "<html>\n"; 
     3361    html += "<head>\n"; 
     3362    html += "<meta http-equiv=\"Content-Type\" content=\"text/html; charset=" + editor.config.charSet + "\">\n"; 
     3363 
     3364    if(typeof editor.config.baseHref != 'undefined' && editor.config.baseHref != null) 
     3365      { 
     3366      html += "<base href=\"" + editor.config.baseHref + "\"/>\n"; 
     3367      } 
     3368 
     3369    html += "<style title=\"table borders\">" 
     3370         + ".htmtableborders, .htmtableborders td, .htmtableborders th {border : 1px dashed lightgrey ! important;} \n" 
     3371         + "</style>\n"; 
     3372 
     3373    html += "<style>" 
     3374         + editor.config.pageStyle + "\n" 
     3375         + "html, body { border: 0px; } \n" 
     3376         + "span.macro, span.macro ul, span.macro div, span.macro p {background : #CCCCCC;}\n" 
     3377         + "</style>\n"; 
     3378 
     3379    if (typeof editor.config.pageStyleSheets !== 'undefined') 
     3380      { 
     3381      for(style_i = 0; style_i < editor.config.pageStyleSheets.length; style_i++) 
    14673382        { 
    1468           if(editor.config.pageStyleSheets[style_i].length > 0) 
     3383        if (editor.config.pageStyleSheets[style_i].length > 0) 
    14693384              html += "<link rel=\"stylesheet\" type=\"text/css\" href=\"" + editor.config.pageStyleSheets[style_i] + "\">"; 
    14703385            //html += "<style> @import url('" + editor.config.pageStyleSheets[style_i] + "'); </style>\n"; 
    14713386        } 
    14723387      } 
    1473       html += "</head>\n"; 
    1474       html += "<body>\n"; 
    1475       html +=   editor.inwardHtml(editor._textArea.value); 
    1476       html += "</body>\n"; 
    1477       html += "</html>"; 
    1478       doc.write(html); 
    1479       doc.close(); 
    1480     } else { 
    1481       var html = editor.inwardHtml(editor._textArea.value); 
    1482       if (html.match(HTMLArea.RE_doctype)) { 
    1483         editor.setDoctype(RegExp.$1); 
    1484         html = html.replace(HTMLArea.RE_doctype, ""); 
    1485       } 
    1486       doc.open(); 
    1487       doc.write(html); 
    1488       doc.close(); 
    1489     } 
    1490  
    1491     this._doc = doc; 
    1492  
    1493     // If we have multiple editors some bug in Mozilla makes some lose editing ability 
    1494     if(HTMLArea.is_gecko) 
    1495     { 
    1496       HTMLArea._addEvents( 
     3388 
     3389    html += "</head>\n"; 
     3390    html += "<body>\n"; 
     3391    html +=   editor.inwardHtml(editor._textArea.value); 
     3392    html += "</body>\n"; 
     3393    html += "</html>"; 
     3394    doc.write(html); 
     3395    doc.close(); 
     3396    }  
     3397  else  
     3398    { 
     3399    var html = editor.inwardHtml(editor._textArea.value); 
     3400    if (html.match(HTMLArea.RE_doctype))  
     3401           { 
     3402      editor.setDoctype(RegExp.$1); 
     3403      html = html.replace(HTMLArea.RE_doctype, ""); 
     3404      } 
     3405    doc.open(); 
     3406    doc.write(html); 
     3407    doc.close(); 
     3408    } 
     3409 
     3410  this._doc = doc; 
     3411 
     3412  // If we have multiple editors some bug in Mozilla makes some lose editing ability 
     3413 
     3414  if(HTMLArea.is_gecko) 
     3415    { 
     3416    HTMLArea._addEvents( 
    14973417        editor._iframe.contentWindow, 
    14983418        ["mousedown"], 
     
    15003420      ); 
    15013421    } 
    1502     else 
    1503     { 
    1504       editor.activateEditor(); 
    1505     } 
    1506  
    1507     // editor.focusEditor(); 
    1508     // intercept some events; for updating the toolbar & keyboard handlers 
    1509     HTMLArea._addEvents 
     3422  else 
     3423    { 
     3424    editor.activateEditor(); 
     3425    } 
     3426 
     3427  // editor.focusEditor(); 
     3428  // intercept some events; for updating the toolbar & keyboard handlers 
     3429 
     3430  HTMLArea._addEvents 
    15103431      (doc, ["keydown", "keypress", "mousedown", "mouseup", "drag"], 
    15113432       function (event) { 
     
    15133434       }); 
    15143435 
    1515     // check if any plugins have registered refresh handlers 
    1516     for (var i in editor.plugins) { 
    1517       var plugin = editor.plugins[i].instance; 
    1518       HTMLArea.refreshPlugin(plugin); 
    1519     } 
    1520  
    1521     if(typeof editor._onGenerate == "function") { editor._onGenerate();} 
    1522  
    1523     setTimeout(function() { 
    1524 //      editor.updateToolbar(); 
    1525     }, 250); 
    1526  
    1527     if (typeof editor.onGenerate == "function") 
    1528       editor.onGenerate(); 
     3436  // check if any plugins have registered refresh handlers 
     3437 
     3438  for (var i in editor.plugins)  
     3439    { 
     3440    var plugin = editor.plugins[i].instance; 
     3441    HTMLArea.refreshPlugin(plugin); 
     3442    } 
     3443 
     3444  if (typeof editor._onGenerate == "function") { editor._onGenerate();} 
     3445 
     3446  setTimeout(function() { 
     3447  //      editor.updateToolbar(); 
     3448      }, 250); 
     3449 
     3450  if (typeof editor.onGenerate == "function") 
     3451    editor.onGenerate(); 
     3452 
     3453  }  // end of initFrame() 
     3454 
     3455// ---------------------------------------------------- 
     3456 
     3457/**  
     3458* Size of the htmlArea according to the available space 
     3459* 
     3460*   Width and Height include toolbar! 
     3461*/ 
     3462 
     3463HTMLArea.prototype.getInnerSize = function() 
     3464  { 
     3465  return this._innerSize; 
    15293466  } 
    15303467 
    1531 // Switches editor mode; parameter can be "textmode" or "wysiwyg".  If no 
    1532 // parameter was passed this function toggles between modes. 
    1533 HTMLArea.prototype.setMode = function(mode) { 
    1534   if (typeof mode == "undefined") { 
     3468// ----------------------------------------------------- 
     3469 
     3470/** 
     3471* setInnerSize 
     3472*/ 
     3473 
     3474HTMLArea.prototype.setInnerSize = function(width, height) 
     3475  { 
     3476 
     3477  this.ddt._ddt( "setInnerSize(): top with width '" + width + "' height '" + height + "'" ); 
     3478 
     3479  if (typeof width == 'undefined' || width == null) 
     3480    { 
     3481    width  = this._innerSize.width; 
     3482    } 
     3483 
     3484  if (typeof height == 'undefined' || height == null) 
     3485    { 
     3486    height  = this._innerSize.height; 
     3487    } 
     3488 
     3489  this._innerSize = {'width':width,'height':height}; 
     3490 
     3491  var editorWidth  = width; 
     3492  var editorHeight = height; 
     3493  var editorLeft   = 0; 
     3494  var editorTop    = 0; 
     3495  var panels = this._panels; 
     3496 
     3497  var panel = panels.right; 
     3498 
     3499  if (panel.on && panel.panels.length && HTMLArea.hasDisplayedChildren(panel.div)) 
     3500    { 
     3501    panel.div.style.position = 'absolute'; 
     3502    panel.div.style.width    = parseInt(this.config.panel_dimensions.right) + (HTMLArea.ie_ie ? -1 : -2) + 'px'; 
     3503    panel.div.style.height   = height + (HTMLArea.is_ie ? -1 : -1) + 'px'; 
     3504    panel.div.style.top      = '0px'; 
     3505    panel.div.style.right    = (HTMLArea.is_ie ? 1 : 2) + 'px'; 
     3506    panel.div.style.padding  = "0px"; 
     3507    panel.div.style.overflow = "auto"; 
     3508    panel.div.style.display  = 'block'; 
     3509    editorWidth -= parseInt(this.config.panel_dimensions.right) + (HTMLArea.is_ie ? 2 : 0); 
     3510    } 
     3511  else 
     3512    { 
     3513    panel.div.style.display  = 'none'; 
     3514    } 
     3515 
     3516  var panel = panels.left; 
     3517 
     3518  if (panel.on && panel.panels.length && HTMLArea.hasDisplayedChildren(panel.div)) 
     3519    { 
     3520    panel.div.style.position = 'absolute'; 
     3521    panel.div.style.width    = parseInt(this.config.panel_dimensions.left) + (HTMLArea.ie_ie ? -1 : -1) + 'px'; 
     3522    panel.div.style.height   = height + (HTMLArea.is_ie ? -1 : -1) + 'px'; 
     3523    panel.div.style.top      = '0px'; 
     3524    panel.div.style.left     = (HTMLArea.is_ie ? 0 : 0) + 'px'; 
     3525    panel.div.style.padding  = "0px"; 
     3526    panel.div.style.overflow = "auto"; 
     3527    panel.div.style.display  = "block"; 
     3528    editorWidth -= parseInt(this.config.panel_dimensions.left) + (HTMLArea.is_ie ? 2 : 0); 
     3529    editorLeft   = parseInt(this.config.panel_dimensions.left) + (HTMLArea.is_ie ? 2 : 0) + 'px'; 
     3530    } 
     3531  else 
     3532    { 
     3533    panel.div.style.display  = 'none'; 
     3534    } 
     3535 
     3536  var panel = panels.top; 
     3537 
     3538  if (panel.on && panel.panels.length && HTMLArea.hasDisplayedChildren(panel.div)) 
     3539    { 
     3540    panel.div.style.position = 'absolute'; 
     3541    panel.div.style.top      = '0px'; 
     3542    panel.div.style.left     = '0px'; 
     3543    panel.div.style.width    = width  + 'px'; 
     3544    panel.div.style.height   = parseInt(this.config.panel_dimensions.top) + 'px'; 
     3545    panel.div.style.padding  = "0px"; 
     3546    panel.div.style.overflow = "auto"; 
     3547    panel.div.style.display  = "block"; 
     3548    editorHeight -= parseInt(this.config.panel_dimensions.top); 
     3549    editorTop     = parseInt(this.config.panel_dimensions.top) + 'px'; 
     3550    } 
     3551  else 
     3552    { 
     3553    panel.div.style.display  = 'none'; 
     3554    } 
     3555 
     3556  var panel = panels.bottom; 
     3557 
     3558  if(panel.on && panel.panels.length && HTMLArea.hasDisplayedChildren(panel.div)) 
     3559    { 
     3560    panel.div.style.position = 'absolute'; 
     3561    panel.div.style.bottom   = '0px'; 
     3562    panel.div.style.left     = '0px'; 
     3563    panel.div.style.width    = width  + 'px'; 
     3564    panel.div.style.height   = parseInt(this.config.panel_dimensions.bottom) + 'px'; 
     3565    panel.div.style.padding  = "0px"; 
     3566    panel.div.style.overflow = "auto"; 
     3567    panel.div.style.display  = "block"; 
     3568    editorHeight -= parseInt(this.config.panel_dimensions.bottom); 
     3569    } 
     3570  else 
     3571    { 
     3572    panel.div.style.display  = 'none'; 
     3573    } 
     3574 
     3575  // Set the dimensions of the container 
     3576  this.innerEditor.style.width  = width  + 'px'; 
     3577  this.innerEditor.style.height = height + 'px'; 
     3578  this.innerEditor.style.position = 'relative'; 
     3579 
     3580  // and the iframe 
     3581  this._iframe.style.width  = editorWidth  + 'px'; 
     3582  this._iframe.style.height = editorHeight + 'px'; 
     3583  this._iframe.style.position = 'absolute'; 
     3584  this._iframe.style.left = editorLeft; 
     3585  this._iframe.style.top  = editorTop; 
     3586 
     3587  // the editor including the toolbar now have the same size as the 
     3588  // original textarea.. which means that we need to reduce that a bit. 
     3589  this._textArea.style.width  = editorWidth  + 'px'; 
     3590  this._textArea.style.height = editorHeight + 'px'; 
     3591  this._textArea.style.position = 'absolute'; 
     3592  this._textArea.style.left = editorLeft; 
     3593  this._textArea.style.top  = editorTop; 
     3594 
     3595  this.notifyOf('resize', {'width':width,'height':height,'editorWidth':editorWidth,'editorHeight':editorHeight,'editorTop':editorTop,'editorLeft':editorLeft}); 
     3596 
     3597  }  // end of setInnerSize 
     3598 
     3599/** 
     3600* addPanel 
     3601*/ 
     3602 
     3603HTMLArea.prototype.addPanel = function(side) 
     3604  { 
     3605  var div = document.createElement('div'); 
     3606  div.side = side; 
     3607  HTMLArea.addClasses(div, 'panel'); 
     3608  this._panels[side].panels.push(div); 
     3609  this._panels[side].div.appendChild(div); 
     3610 
     3611  this.notifyOf('panel_change', {'action':'add','panel':div}); 
     3612 
     3613  return div; 
     3614  } 
     3615 
     3616/** 
     3617* removePanel 
     3618*/ 
     3619 
     3620HTMLArea.prototype.removePanel = function(panel) 
     3621  { 
     3622  this._panels[panel.side].div.removeChild(panel); 
     3623  var clean = [ ]; 
     3624 
     3625  for(var i = 0; i < this._panels[panel.side].panels.length; i++) 
     3626    { 
     3627    if(this._panels[panel.side].panels[i] != panel) 
     3628      { 
     3629      clean.push(this._panels[panel.side].panels[i]); 
     3630      } 
     3631    } 
     3632  this._panels[panel.side].panels = clean; 
     3633  this.notifyOf('panel_change', {'action':'remove','panel':panel}); 
     3634 
     3635  } 
     3636 
     3637/** 
     3638* hidePanel 
     3639*/ 
     3640 
     3641HTMLArea.prototype.hidePanel = function(panel) 
     3642  { 
     3643  if (panel) 
     3644    { 
     3645    panel.style.display = 'none'; 
     3646    this.notifyOf('panel_change', {'action':'hide','panel':panel}); 
     3647    } 
     3648  } 
     3649 
     3650/** 
     3651* showPanel  
     3652*/ 
     3653 
     3654HTMLArea.prototype.showPanel = function(panel) 
     3655  { 
     3656  if ( panel ) 
     3657    { 
     3658    panel.style.display = ''; 
     3659    this.notifyOf('panel_change', {'action':'show','panel':panel}); 
     3660    } 
     3661  } 
     3662 
     3663/** 
     3664* hidePanels 
     3665*/ 
     3666 
     3667HTMLArea.prototype.hidePanels = function(sides) 
     3668  { 
     3669  if(typeof sides == 'undefined') 
     3670    { 
     3671    sides = ['left','right','top','bottom']; 
     3672    } 
     3673 
     3674  var reShow = []; 
     3675 
     3676  for(var i = 0; i < sides.length;i++) 
     3677    { 
     3678    if(this._panels[sides[i]].on) 
     3679      { 
     3680      reShow.push(sides[i]); 
     3681      this._panels[sides[i]].on = false; 
     3682      } 
     3683    } 
     3684  this.notifyOf('panel_change', {'action':'multi_hide','sides':sides}); 
     3685  } 
     3686 
     3687/**  
     3688* showPanels 
     3689*/ 
     3690 
     3691HTMLArea.prototype.showPanels = function(sides) 
     3692  { 
     3693  if(typeof sides == 'undefined') 
     3694    { 
     3695    sides = ['left','right','top','bottom']; 
     3696    } 
     3697 
     3698  var reHide = []; 
     3699  for(var i = 0; i < sides.length;i++) 
     3700    { 
     3701    if(!this._panels[sides[i]].on) 
     3702      { 
     3703      reHide.push(sides[i]); 
     3704      this._panels[sides[i]].on = true; 
     3705      } 
     3706    } 
     3707  this.notifyOf('panel_change', {'action':'multi_show','sides':sides}); 
     3708  } 
     3709 
     3710/** 
     3711* activateEditor 
     3712*/ 
     3713 
     3714HTMLArea.prototype.activateEditor = function() 
     3715  { 
     3716 
     3717  this.ddt._ddt( "activateEditor(): top" ); 
     3718 
     3719  if (HTMLArea.is_gecko && this._doc.designMode != 'on') 
     3720    { 
     3721 
     3722    try{HTMLArea.last_on.designMode = 'off';} catch(e) { } 
     3723 
     3724    if(this._iframe.style.display == 'none') 
     3725      { 
     3726      this._iframe.style.display = ''; 
     3727      this._doc.designMode = 'on'; 
     3728      this._iframe.style.display = 'none'; 
     3729      } 
     3730    else 
     3731      { 
     3732      this._doc.designMode = 'on'; 
     3733      } 
     3734    } 
     3735  else 
     3736    { 
     3737    this._doc.body.contentEditable = true; 
     3738    } 
     3739 
     3740  HTMLArea.last_on = this._doc; 
     3741 
     3742  }  // end of activateEditor() 
     3743 
     3744/** 
     3745* deactivateEditor() 
     3746*/ 
     3747 
     3748HTMLArea.prototype.deactivateEditor = function() 
     3749  { 
     3750 
     3751  this.ddt._ddt( "deactivateEditor(): top" ); 
     3752 
     3753  if (HTMLArea.is_gecko && this._doc.designMode == 'on') 
     3754    { 
     3755    this._doc.designMode = 'off'; 
     3756    HTMLArea.last_on = null; 
     3757    } 
     3758  else 
     3759    { 
     3760    this._doc.body.contentEditable = false; 
     3761    } 
     3762  } 
     3763 
     3764 
     3765/** 
     3766* Switches editor mode;  
     3767* 
     3768* parameter can be "textmode" or "wysiwyg".  If no 
     3769* parameter was passed this function toggles between modes. 
     3770*/ 
     3771 
     3772HTMLArea.prototype.setMode = function(mode)  
     3773  { 
     3774  if (typeof mode == "undefined")  
     3775    { 
    15353776    mode = ((this._editMode == "textmode") ? "wysiwyg" : "textmode"); 
    1536   } 
    1537   switch (mode) { 
     3777    } 
     3778 
     3779  switch (mode)  
     3780    { 
    15383781    case "textmode": 
    1539     { 
     3782      { 
    15403783      var html = this.outwardHtml(this.getHTML()); 
    15413784      this._textArea.value = html; 
     
    15463789      this._textArea.style.display = "block"; 
    15473790      if (this.config.statusBar) 
    1548       { 
     3791        { 
    15493792        this._statusBar.innerHTML = HTMLArea._lc("You are in TEXT MODE.  Use the [<>] button to switch back to WYSIWYG."); 
    1550       } 
     3793        } 
    15513794 
    15523795      this.notifyOf('modechange', {'mode':'text'}); 
    15533796      break; 
    1554     } 
     3797      } 
    15553798 
    15563799    case "wysiwyg": 
    1557     { 
     3800      { 
    15583801      var html = this.inwardHtml(this.getHTML()); 
    15593802      this.deactivateEditor(); 
    15603803      if (!this.config.fullPage) 
    1561       { 
     3804        { 
    15623805        this._doc.body.innerHTML = html; 
    1563       } 
     3806        } 
    15643807      else 
    1565       { 
     3808        { 
    15663809        this.setFullHTML(html); 
    1567       } 
     3810        } 
    15683811      this._iframe.style.display   = ''; 
    15693812      this._textArea.style.display = "none"; 
    15703813      this.activateEditor(); 
    15713814      if (this.config.statusBar) 
    1572       { 
     3815        { 
    15733816        this._statusBar.innerHTML = ''; 
    15743817        this._statusBar.appendChild(this._statusBarTree); 
    1575       } 
     3818        }  
    15763819 
    15773820      this.notifyOf('modechange', {'mode':'wysiwyg'}); 
    15783821      break; 
    1579     } 
     3822      } 
    15803823 
    15813824    default: 
    1582     { 
     3825      { 
    15833826      alert("Mode <" + mode + "> not defined!"); 
    15843827      return false; 
    1585     } 
    1586   } 
     3828      } 
     3829    } 
     3830 
    15873831  this._editMode = mode; 
     3832 
    15883833  // this.focusEditor(); 
    15893834 
    1590   for (var i in this.plugins) { 
     3835  for (var i in this.plugins)  
     3836    { 
    15913837    var plugin = this.plugins[i].instance; 
    15923838    if (typeof plugin.onMode == "function") plugin.onMode(mode); 
    1593   } 
    1594 }; 
    1595  
    1596 HTMLArea.prototype.setFullHTML = function(html) { 
     3839    } 
     3840  };  // end of setMode() 
     3841 
     3842// ------------------------------------------------------------ 
     3843 
     3844/** 
     3845* setFullHTML() 
     3846*/ 
     3847 
     3848HTMLArea.prototype.setFullHTML = function(html)  
     3849  { 
     3850 
     3851  this.ddt._ddt( "setFullHTML(): top" ); 
     3852 
    15973853  var save_multiline = RegExp.multiline; 
    15983854  RegExp.multiline = true; 
    1599   if (html.match(HTMLArea.RE_doctype)) { 
     3855 
     3856  if (html.match(HTMLArea.RE_doctype))  
     3857    { 
    16003858    this.setDoctype(RegExp.$1); 
    16013859    html = html.replace(HTMLArea.RE_doctype, ""); 
    1602   } 
     3860    } 
     3861 
    16033862  RegExp.multiline = save_multiline; 
    1604   if (!HTMLArea.is_ie) { 
     3863 
     3864  if (!HTMLArea.is_ie)  
     3865    { 
    16053866    if (html.match(HTMLArea.RE_head)) 
    16063867      this._doc.getElementsByTagName("head")[0].innerHTML = RegExp.$1; 
     3868 
    16073869    if (html.match(HTMLArea.RE_body)) 
    16083870      this._doc.getElementsByTagName("body")[0].innerHTML = RegExp.$1; 
    1609   } else { 
     3871 
     3872    }  
     3873  else  
     3874    { 
    16103875    var html_re = /<html>((.|\n)*?)<\/html>/i; 
    16113876    html = html.replace(html_re, "$1"); 
     
    16163881    // this._doc.body.contentEditable = true; 
    16173882    return true; 
    1618   } 
    1619 }; 
    1620  
    1621 /*************************************************** 
    1622  *  Category: PLUGINS 
    1623  ***************************************************/ 
    1624  
    1625 // Create the specified plugin and register it with this HTMLArea 
    1626 // return the plugin created to allow refresh when necessary 
    1627 HTMLArea.prototype.registerPlugin = function() { 
     3883    } 
     3884  };  // end of setFullHTML 
     3885 
     3886// --------------------------------- 
     3887 
     3888/** 
     3889* registerPlugin() 
     3890* 
     3891* Create the specified plugin and register it with this HTMLArea 
     3892* return the plugin created to allow refresh when necessary 
     3893*/ 
     3894 
     3895HTMLArea.prototype.registerPlugin = function()  
     3896  { 
     3897 
     3898  this.ddt._ddt( "registerPlugin(): top" ); 
     3899 
    16283900  var plugin = arguments[0]; 
    16293901  var args = []; 
    16303902  for (var i = 1; i < arguments.length; ++i) 
    16313903    args.push(arguments[i]); 
     3904 
    16323905  return this.registerPlugin2(plugin, args); 
    1633 }; 
    1634  
    1635 // this is the variant of the function above where the plugin arguments are 
    1636 // already packed in an array.  Externally, it should be only used in the 
    1637 // full-screen editor code, in order to initialize plugins with the same 
    1638 // parameters as in the opener window. 
    1639 HTMLArea.prototype.registerPlugin2 = function(plugin, args) { 
     3906  }; 
     3907 
     3908/** 
     3909* registerPlugin2 
     3910* 
     3911* this is the variant of registerPlugin where the plugin arguments are 
     3912* already packed in an array.  Externally, it should be only used in the 
     3913* full-screen editor code, in order to initialize plugins with the same 
     3914* parameters as in the opener window. 
     3915*/ 
     3916 
     3917HTMLArea.prototype.registerPlugin2 = function(plugin, args)  
     3918  { 
     3919 
     3920  this.ddt._ddt( "registerPlugin2(): top" ); 
     3921 
    16403922  if (typeof plugin == "string") 
    16413923    plugin = eval(plugin); 
    1642   if (typeof plugin == "undefined") { 
     3924 
     3925  if (typeof plugin == "undefined")  
     3926    { 
     3927 
     3928         this.ddt._ddt( "registerPlugin2(): INTERNAL ERROR: plugin is undefined. " ); 
     3929 
    16433930    /* FIXME: This should never happen. But why does it do? */ 
    16443931    return false; 
    1645   } 
     3932    } 
     3933 
    16463934  var obj = new plugin(this, args); 
    1647   if (obj) { 
     3935 
     3936  if (obj)  
     3937    { 
    16483938    var clone = {}; 
    16493939    var info = plugin._pluginInfo; 
     3940 
    16503941    for (var i in info) 
    16513942      clone[i] = info[i]; 
     3943 
    16523944    clone.instance = obj; 
    16533945    clone.args = args; 
    16543946    this.plugins[plugin._pluginInfo.name] = clone; 
    16553947    return obj; 
    1656   } else 
     3948    }  
     3949  else 
    16573950    alert("Can't register plugin " + plugin.toString() + "."); 
    1658 }; 
    1659  
    1660 // static function that loads the required plugin and lang file, based on the 
    1661 // language loaded already for HTMLArea.  You better make sure that the plugin 
    1662 // _has_ that language, otherwise shit might happen ;-) 
    1663 HTMLArea.getPluginDir = function(pluginName) { 
    1664   return _editor_url + "plugins/" + pluginName; 
    1665 }; 
    1666  
    1667 HTMLArea.loadPlugin = function(pluginName, callback) { 
    1668   // Might already be loaded 
    1669   if(eval('typeof ' + pluginName) != 'undefined') 
    1670   { 
    1671     if(callback) 
    1672     { 
    1673       callback(); 
    1674     } 
    1675     return; 
    1676   } 
    1677  
    1678   var dir = this.getPluginDir(pluginName); 
    1679   var plugin = pluginName.replace(/([a-z])([A-Z])([a-z])/g, 
    1680           function (str, l1, l2, l3) { 
    1681             return l1 + "-" + l2.toLowerCase() + l3; 
    1682           }).toLowerCase() + ".js"; 
    1683   var plugin_file = dir + "/" + plugin; 
    1684  
    1685   if(callback) 
    1686   { 
    1687     HTMLArea._loadback(plugin_file, callback); 
    1688   } 
    1689   else 
    1690   { 
    1691     document.write("<script type='text/javascript' src='" + plugin_file + "'></script>"); 
    1692   } 
    1693 }; 
    1694  
    1695 HTMLArea.loadPlugins = function(plugins, callbackIfNotReady) 
    1696 { 
    1697   var nuPlugins = HTMLArea.cloneObject(plugins); 
    1698  
    1699   while(nuPlugins.length) 
    1700   { 
    1701     // Might already be loaded 
    1702     if(eval('typeof ' + nuPlugins[nuPlugins.length-1]) != 'undefined') 
    1703     { 
    1704       nuPlugins.pop(); 
    1705     } 
    1706     else 
    1707     { 
    1708       break; 
    1709     } 
    1710   } 
    1711  
    1712   if(!nuPlugins.length) 
    1713   { 
    1714     return true; 
    1715   } 
    1716  
    1717   HTMLArea.loadPlugin 
    1718   (nuPlugins.pop(), 
    1719       function() 
    1720       { 
    1721         if(HTMLArea.loadPlugins(nuPlugins, callbackIfNotReady)) 
    1722         { 
    1723           if(typeof callbackIfNotReady == 'function') 
    1724           { 
    1725             callbackIfNotReady(); 
    1726           } 
    1727         } 
    1728       } 
    1729   ); 
    1730   return false; 
    1731 } 
    1732  
    1733 // refresh plugin by calling onGenerate or onGenerateOnce method. 
    1734 HTMLArea.refreshPlugin = function(plugin) { 
    1735   if (typeof plugin.onGenerate == "function") 
    1736     plugin.onGenerate(); 
    1737   if (typeof plugin.onGenerateOnce == "function") { 
    1738     plugin.onGenerateOnce(); 
    1739     plugin.onGenerateOnce = null; 
    1740   } 
    1741 }; 
    1742  
    1743 HTMLArea.loadStyle = function(style, plugin) { 
    1744   var url = _editor_url || ''; 
    1745   if (typeof plugin != "undefined") { 
    1746     url += "plugins/" + plugin + "/"; 
    1747   } 
    1748   url += style; 
    1749   if (/^\//.test(style)) 
    1750     url = style; 
    1751   var head = document.getElementsByTagName("head")[0]; 
    1752   var link = document.createElement("link"); 
    1753   link.rel = "stylesheet"; 
    1754   link.href = url; 
    1755   head.appendChild(link); 
    1756   //document.write("<style type='text/css'>@import url(" + url + ");</style>"); 
    1757 }; 
    1758 HTMLArea.loadStyle(typeof _editor_css == "string" ? _editor_css : "htmlarea.css"); 
    1759  
    1760 /*************************************************** 
    1761  *  Category: EDITOR UTILITIES 
    1762  ***************************************************/ 
    1763  
    1764 HTMLArea.prototype.debugTree = function() { 
     3951 
     3952  };  // end of registerPlugin2 
     3953 
     3954// ------------------------------- 
     3955 
     3956/** 
     3957* debugTree 
     3958* 
     3959* @deprecated 
     3960*/ 
     3961 
     3962HTMLArea.prototype.debugTree = function()  
     3963  { 
    17653964  var ta = document.createElement("textarea"); 
    17663965  ta.style.width = "100%"; 
    17673966  ta.style.height = "20em"; 
    17683967  ta.value = ""; 
    1769   function debug(indent, str) { 
     3968 
     3969  function debug(indent, str)  
     3970    { 
    17703971    for (; --indent >= 0;) 
    17713972      ta.value += " "; 
    17723973    ta.value += str + "\n"; 
    1773   }; 
    1774   function _dt(root, level) { 
     3974    }; 
     3975 
     3976  function _dt(root, level)  
     3977    { 
    17753978    var tag = root.tagName.toLowerCase(), i; 
    17763979    var ns = HTMLArea.is_ie ? root.scopeName : root.prefix; 
     
    17793982      if (i.nodeType == 1) 
    17803983        _dt(i, level + 2); 
    1781   }; 
     3984    }; 
     3985 
    17823986  _dt(this._doc.body, 0); 
    17833987  document.body.appendChild(ta); 
    1784 }; 
    1785  
    1786 HTMLArea.getInnerText = function(el) { 
    1787   var txt = '', i; 
    1788   for (i = el.firstChild; i; i = i.nextSibling) { 
    1789     if (i.nodeType == 3) 
    1790       txt += i.data; 
    1791     else if (i.nodeType == 1) 
    1792       txt += HTMLArea.getInnerText(i); 
    1793   } 
    1794   return txt; 
    1795 }; 
    1796  
    1797 HTMLArea.prototype._wordClean = function() { 
    1798   var 
    1799     editor = this, 
    1800     stats = { 
    1801       empty_tags : 0, 
    1802       mso_class  : 0, 
    1803       mso_style  : 0, 
    1804       mso_xmlel  : 0, 
    1805       orig_len   : this._doc.body.innerHTML.length, 
    1806       T          : (new Date()).getTime() 
    1807     }, 
    1808     stats_txt = { 
    1809       empty_tags : "Empty tags removed: ", 
    1810       mso_class  : "MSO class names removed: ", 
    1811       mso_style  : "MSO inline style removed: ", 
    1812       mso_xmlel  : "MSO XML elements stripped: " 
     3988 
     3989  }; 
     3990 
     3991// ------------------------------------------------- 
     3992 
     3993/** 
     3994* wordclean - clean out MS Word tags?  
     3995* 
     3996*/ 
     3997 
     3998HTMLArea.prototype._wordClean = function()  
     3999  { 
     4000  var editor = this; 
     4001 
     4002  this.ddt._ddt( "_wordClean(): top" ); 
     4003 
     4004  var stats =  
     4005    { 
     4006    empty_tags : 0, 
     4007    mso_class  : 0, 
     4008    mso_style  : 0, 
     4009    mso_xmlel  : 0, 
     4010    orig_len   : this._doc.body.innerHTML.length, 
     4011    T          : (new Date()).getTime() 
    18134012    }; 
    1814   function showStats() { 
     4013 
     4014  var stats_txt =  
     4015    { 
     4016    empty_tags : "Empty tags removed: ", 
     4017    mso_class  : "MSO class names removed: ", 
     4018    mso_style  : "MSO inline style removed: ", 
     4019    mso_xmlel  : "MSO XML elements stripped: " 
     4020    }; 
     4021 
     4022  function showStats()  
     4023    { 
    18154024    var txt = "HTMLArea word cleaner stats: \n\n"; 
    18164025    for (var i in stats) 
     
    18214030    txt += "Clean-up took " + (((new Date()).getTime() - stats.T) / 1000) + " seconds"; 
    18224031    alert(txt); 
    1823   }; 
    1824   function clearClass(node) { 
     4032    }; 
     4033 
     4034  function clearClass(node)  
     4035    { 
    18254036    var newc = node.className.replace(/(^|\s)mso.*?(\s|$)/ig, ' '); 
    1826     if (newc != node.className) { 
     4037    if (newc != node.className)  
     4038           { 
    18274039      node.className = newc; 
    1828       if (!/\S/.test(node.className)) { 
     4040      if (!/\S/.test(node.className))  
     4041                  { 
    18294042        node.removeAttribute("className"); 
    18304043        ++stats.mso_class; 
    1831       } 
    1832     } 
    1833   }; 
    1834   function clearStyle(node) { 
     4044        } 
     4045      } 
     4046    }; 
     4047 
     4048  function clearStyle(node)  
     4049    { 
    18354050    var declarations = node.style.cssText.split(/\s*;\s*/); 
    18364051    for (var i = declarations.length; --i >= 0;) 
    18374052      if (/^mso|^tab-stops/i.test(declarations[i]) || 
    1838           /^margin\s*:\s*0..\s+0..\s+0../i.test(declarations[i])) { 
     4053          /^margin\s*:\s*0..\s+0..\s+0../i.test(declarations[i]))  
     4054             { 
    18394055        ++stats.mso_style; 
    18404056        declarations.splice(i, 1); 
    1841       } 
     4057        } 
     4058 
    18424059    node.style.cssText = declarations.join("; "); 
    1843   }; 
    1844   function stripTag(el) { 
     4060 
     4061    }; 
     4062 
     4063  function stripTag(el)  
     4064    { 
    18454065    if (HTMLArea.is_ie) 
    18464066      el.outerHTML = HTMLArea.htmlEncode(el.innerText); 
    1847     else { 
     4067    else  
     4068           { 
    18484069      var txt = document.createTextNode(HTMLArea.getInnerText(el)); 
    18494070      el.parentNode.insertBefore(txt, el); 
    18504071      el.parentNode.removeChild(el); 
    1851     } 
     4072      } 
     4073 
    18524074    ++stats.mso_xmlel; 
    1853   }; 
    1854   function checkEmpty(el) { 
     4075    }; 
     4076 
     4077  function checkEmpty(el)  
     4078    { 
    18554079    if (/^(a|span|b|strong|i|em|font)$/i.test(el.tagName) && 
    1856         !el.firstChild) { 
     4080        !el.firstChild)  
     4081           { 
    18574082      el.parentNode.removeChild(el); 
    18584083      ++stats.empty_tags; 
    1859     } 
    1860   }; 
    1861   function parseTree(root) { 
     4084      } 
     4085    }; 
     4086 
     4087  function parseTree(root)  
     4088    { 
    18624089    var tag = root.tagName.toLowerCase(), i, next; 
    1863     if ((HTMLArea.is_ie && root.scopeName != 'HTML') || (!HTMLArea.is_ie && /:/.test(tag))) { 
     4090    if ((HTMLArea.is_ie && root.scopeName != 'HTML') || (!HTMLArea.is_ie && /:/.test(tag)))  
     4091           { 
    18644092      stripTag(root); 
    18654093      return false; 
    1866     } else { 
     4094      }  
     4095         else  
     4096           { 
     4097 
    18674098      clearClass(root); 
    18684099      clearStyle(root); 
    1869       for (i = root.firstChild; i; i = next) { 
     4100 
     4101      for (i = root.firstChild; i; i = next)  
     4102                  { 
    18704103        next = i.nextSibling; 
    18714104        if (i.nodeType == 1 && parseTree(i)) 
    18724105          checkEmpty(i); 
    1873       } 
    1874     } 
     4106        } 
     4107      } 
    18754108    return true; 
    1876   }; 
     4109    }; 
     4110 
    18774111  parseTree(this._doc.body); 
     4112 
    18784113  // showStats(); 
    18794114  // this.debugTree(); 
     
    18814116  // this.setHTML(this.getInnerHTML()); 
    18824117  // this.forceRedraw(); 
     4118 
     4119  this.ddt._ddt( "_wordClean(): bottom" ); 
     4120 
    18834121  this.updateToolbar(); 
    1884 }; 
    1885  
    1886 HTMLArea.prototype._clearFonts = function() { 
     4122 
     4123  };  // end of _wordClean() 
     4124 
     4125// ------------------------------------------------ 
     4126 
     4127/** 
     4128* _clearFonts() 
     4129*/ 
     4130 
     4131HTMLArea.prototype._clearFonts = function()  
     4132  { 
     4133 
     4134  this.ddt._ddt( "_clearFonts(): top" ); 
     4135   
    18874136  var D = this.getInnerHTML(); 
    18884137 
    18894138  if(confirm('Would you like to clear font typefaces?')) 
    1890   { 
     4139    { 
    18914140    D = D.replace(/face="[^"]*"/gi, ''); 
    18924141    D = D.replace(/font-family:[^;}"']+;?/gi, ''); 
    1893   } 
    1894  
    1895   if(confirm('Would you like to clear font sizes?')) 
    1896   { 
     4142    } 
     4143 
     4144  if (confirm('Would you like to clear font sizes?')) 
     4145    { 
    18974146    D = D.replace(/size="[^"]*"/gi, ''); 
    18984147    D = D.replace(/font-size:[^;}"']+;?/gi, ''); 
    1899   } 
    1900  
    1901   if(confirm('Would you like to clear font colours?')) 
    1902   { 
     4148    } 
     4149 
     4150  if (confirm('Would you like to clear font colours?')) 
     4151    { 
    19034152    D = D.replace(/color="[^"]*"/gi, ''); 
    19044153    D = D.replace(/([^-])color:[^;}"']+;?/gi, '$1'); 
    1905   } 
     4154    } 
    19064155 
    19074156  D = D.replace(/(style|class)="\s*"/gi, ''); 
     
    19094158  this.setHTML(D); 
    19104159  this.updateToolbar(); 
    1911 } 
     4160 
     4161  } 
     4162 
     4163// ------------------------------------------- 
     4164 
     4165/** 
     4166* _splitBlock() 
     4167*/ 
    19124168 
    19134169HTMLArea.prototype._splitBlock = function() 
    1914 { 
     4170  { 
     4171 
     4172  this.ddt._ddt( "_splitBlock(): top" ); 
     4173 
    19154174  this._doc.execCommand('formatblock', false, '<div>'); 
    1916 } 
    1917  
    1918 HTMLArea.prototype.forceRedraw = function() { 
     4175  } 
     4176 
     4177// -------------------------------------------- 
     4178 
     4179/** 
     4180* forceRedraw() 
     4181*/ 
     4182 
     4183HTMLArea.prototype.forceRedraw = function()  
     4184  { 
     4185 
     4186  this.ddt._ddt( "forceRedraw(): top" ); 
     4187 
    19194188  this._doc.body.style.visibility = "hidden"; 
    19204189  this._doc.body.style.visibility = "visible"; 
    19214190  // this._doc.body.innerHTML = this.getInnerHTML(); 
    1922 }; 
    1923  
    1924 // focuses the iframe window.  returns a reference to the editor document. 
    1925 HTMLArea.prototype.focusEditor = function() { 
    1926   switch (this._editMode) { 
    1927       // notice the try { ... } catch block to avoid some rare exceptions in FireFox 
    1928       // (perhaps also in other Gecko browsers). Manual focus by user is required in 
    1929         // case of an error. Somebody has an idea? 
    1930       case "wysiwyg" : 
     4191  }; 
     4192 
     4193// ------------------------------------------- 
     4194 
     4195/** 
     4196* focusEditor() 
     4197* 
     4198* focuses the iframe window.  returns a reference to the editor document. 
     4199*/ 
     4200 
     4201HTMLArea.prototype.focusEditor = function()  
     4202  { 
     4203 
     4204  this.ddt._ddt( "forceRedraw(): top _editMode is '" + this._editMode + "'" ); 
     4205 
     4206  switch (this._editMode)  
     4207    { 
     4208    // notice the try { ... } catch block to avoid some rare exceptions in FireFox 
     4209    // (perhaps also in other Gecko browsers). Manual focus by user is required in 
     4210    // case of an error. Somebody has an idea? 
     4211 
     4212    case "wysiwyg" : 
    19314213      try 
    1932       { 
     4214        { 
     4215 
    19334216        // We don't want to focus the field unless at least one field has been activated. 
     4217 
    19344218        if(HTMLArea.last_on) 
    1935         { 
     4219          { 
    19364220          this.activateEditor(); 
    19374221          this._iframe.contentWindow.focus(); 
    1938         } 
    1939  
    1940       } catch (e) {} break; 
    1941       case "textmode": try { this._textArea.focus() } catch (e) {} break; 
    1942       default      : alert("ERROR: mode " + this._editMode + " is not defined"); 
    1943   } 
     4222          } 
     4223 
     4224        } catch (e) {} break; 
     4225 
     4226    case "textmode":  
     4227           try  
     4228                  {  
     4229                  this._textArea.focus()  
     4230                  } catch (e) {} break; 
     4231 
     4232    default:  
     4233 
     4234           alert("ERROR: mode " + this._editMode + " is not defined"); 
     4235    } 
     4236 
    19444237  return this._doc; 
    1945 }; 
    1946  
    1947 // takes a snapshot of the current text (for undo) 
    1948 HTMLArea.prototype._undoTakeSnapshot = function() { 
     4238 
     4239  }; 
     4240 
     4241// ----------------------------------------------- 
     4242 
     4243/** 
     4244* takes an undo snapshot 
     4245* 
     4246* takes a snapshot of the current text (for undo) 
     4247*/ 
     4248 
     4249HTMLArea.prototype._undoTakeSnapshot = function()  
     4250  { 
     4251 
     4252  this.ddt._ddt( "_undoTakeSnapshot(): top" ); 
     4253 
    19494254  ++this._undoPos; 
    1950   if (this._undoPos >= this.config.undoSteps) { 
     4255 
     4256  if (this._undoPos >= this.config.undoSteps)  
     4257    { 
    19514258    // remove the first element 
    19524259    this._undoQueue.shift(); 
    19534260    --this._undoPos; 
    1954   } 
     4261    } 
     4262 
    19554263  // use the fasted method (getInnerHTML); 
    19564264  var take = true; 
     
    19584266  if (this._undoPos > 0) 
    19594267    take = (this._undoQueue[this._undoPos - 1] != txt); 
    1960   if (take) { 
     4268 
     4269  if (take)  
     4270    { 
    19614271    this._undoQueue[this._undoPos] = txt; 
    1962   } else { 
     4272    }  
     4273  else  
     4274    { 
    19634275    this._undoPos--; 
    1964   } 
    1965 }; 
    1966  
    1967 HTMLArea.prototype.undo = function() { 
    1968   if (this._undoPos > 0) { 
     4276    } 
     4277 
     4278  }; 
     4279 
     4280// ---------------------------------------- 
     4281 
     4282/** 
     4283* undo() 
     4284*/ 
     4285 
     4286HTMLArea.prototype.undo = function()  
     4287  { 
     4288 
     4289  this.ddt._ddt( "undo(): top" ); 
     4290 
     4291  if (this._undoPos > 0)  
     4292    { 
    19694293    var txt = this._undoQueue[--this._undoPos]; 
    19704294    if (txt) this.setHTML(txt); 
    19714295    else ++this._undoPos; 
    1972   } 
    1973 }; 
    1974  
    1975 HTMLArea.prototype.redo = function() { 
    1976   if (this._undoPos < this._undoQueue.length - 1) { 
     4296    } 
     4297  }; 
     4298 
     4299// ----------------------------------------- 
     4300 
     4301/** 
     4302* redo() 
     4303*/ 
     4304 
     4305HTMLArea.prototype.redo = function()  
     4306  { 
     4307 
     4308  this.ddt._ddt( "redo(): top" ); 
     4309 
     4310  if (this._undoPos < this._undoQueue.length - 1)  
     4311    { 
    19774312    var txt = this._undoQueue[++this._undoPos]; 
    19784313    if (txt) this.setHTML(txt); 
    19794314    else --this._undoPos; 
     4315    } 
     4316  }; 
     4317 
     4318// ------------------------------------------------- 
     4319 
     4320/** 
     4321* disableToolbar() 
     4322*/ 
     4323 
     4324HTMLArea.prototype.disableToolbar = function(except) 
     4325  { 
     4326 
     4327  this.ddt._ddt( "disableToolbar(): top" ); 
     4328 
     4329  if(typeof except == 'undefined') 
     4330    { 
     4331    except = [ ]; 
     4332    } 
     4333  else if(typeof except != 'object') 
     4334    { 
     4335    except = [except]; 
     4336    } 
     4337 
     4338  for (var i in this._toolbarObjects) 
     4339    { 
     4340    var btn = this._toolbarObjects[i]; 
     4341    if (except.contains(i)) 
     4342      { 
     4343      continue; 
     4344      } 
     4345 
     4346    btn.state("enabled", false); 
     4347    } 
    19804348  } 
    1981 }; 
    1982  
    1983 HTMLArea.prototype.disableToolbar = function(except) 
    1984 { 
    1985   if(typeof except == 'undefined') 
    1986   { 
    1987     except = [ ]; 
     4349 
     4350// ------------------------------------------- 
     4351 
     4352/** 
     4353* enableToolbar() 
     4354*/ 
     4355 
     4356HTMLArea.prototype.enableToolbar = function() 
     4357  { 
     4358 
     4359  this.ddt._ddt( "enableToolbar(): top" ); 
     4360 
     4361  this.updateToolbar(); 
    19884362  } 
    1989   else if(typeof except != 'object') 
    1990   { 
    1991     except = [except]; 
    1992   } 
    1993  
    1994   for (var i in this._toolbarObjects) 
    1995   { 
    1996     var btn = this._toolbarObjects[i]; 
    1997     if(except.contains(i)) 
    1998     { 
    1999       continue; 
    2000     } 
    2001     btn.state("enabled", false); 
    2002   } 
    2003 } 
    2004  
    2005 HTMLArea.prototype.enableToolbar = function() 
    2006 { 
    2007   this.updateToolbar(); 
    2008 } 
    2009  
    2010 if(!Array.prototype.contains) 
    2011 { 
    2012   Array.prototype.contains = function(needle) 
    2013   { 
    2014    var haystack = this; 
    2015    for(var i = 0; i < haystack.length; i++) 
    2016     { 
    2017       if(needle == haystack[i]) return true; 
    2018     } 
    2019  
    2020     return false; 
    2021   } 
    2022 } 
    2023  
    2024 if(!Array.prototype.indexOf) 
    2025 { 
    2026   Array.prototype.indexOf = function(needle) 
    2027   { 
    2028     var haystack = this; 
    2029     for(var i = 0; i < haystack.length; i++) 
    2030     { 
    2031       if(needle == haystack[i]) return i; 
    2032     } 
    2033  
    2034     return null; 
    2035   } 
    2036 } 
    2037  
    2038  
    2039 // updates enabled/disable/active state of the toolbar elements 
    2040 HTMLArea.prototype.updateToolbar = function(noStatus) { 
     4363 
     4364// -------------------------------------------------- 
     4365 
     4366/** 
     4367* updates enabled/disable/active state of the toolbar elements 
     4368*/ 
     4369 
     4370HTMLArea.prototype.updateToolbar = function(noStatus)  
     4371  { 
    20414372  var doc = this._doc; 
    20424373  var text = (this._editMode == "textmode"); 
    20434374  var ancestors = null; 
    2044   if (!text) { 
     4375 
     4376  this.ddt._ddt( "updateToolbar(): top" ); 
     4377 
     4378  if (!text)  
     4379    { 
    20454380    ancestors = this.getAllAncestors(); 
    2046     if (this.config.statusBar && !noStatus) { 
     4381    if (this.config.statusBar && !noStatus)  
     4382           { 
    20474383      this._statusBarTree.innerHTML = HTMLArea._lc("Path") + ": "; // clear 
    2048       for (var i = ancestors.length; --i >= 0;) { 
     4384      for (var i = ancestors.length; --i >= 0;)  
     4385                  { 
    20494386        var el = ancestors[i]; 
    2050         if (!el) { 
     4387        if (!el)  
     4388                    { 
     4389 
     4390                         this.ddt._ddt( "updateToolbar(): INTERNAL ERROR" ); 
     4391 
    20514392          // hell knows why we get here; this 
    20524393          // could be a classic example of why 
     
    20544395          // that are impossible to happen ;-) 
    20554396          continue; 
    2056         } 
     4397          } 
     4398 
    20574399        var a = document.createElement("a"); 
    20584400        a.href = "javascript:void(0)"; 
    20594401        a.el = el; 
    20604402        a.editor = this; 
    2061         a.onclick = function() { 
     4403 
     4404        a.onclick = function()  
     4405                    { 
    20624406          this.blur(); 
    20634407          this.editor.selectNodeContents(this.el); 
    20644408          this.editor.updateToolbar(true); 
    20654409          return false; 
    2066         }; 
    2067         a.oncontextmenu = function() { 
     4410          }; 
     4411 
     4412        a.oncontextmenu = function()  
     4413                    { 
     4414 
    20684415          // TODO: add context menu here 
     4416 
    20694417          this.blur(); 
    20704418          var info = "Inline style:\n\n"; 
     
    20724420          alert(info); 
    20734421          return false; 
    2074         }; 
     4422          }; 
     4423 
    20754424        var txt = el.tagName.toLowerCase(); 
    20764425        a.title = el.style.cssText; 
    2077         if (el.id) { 
     4426        if (el.id)  
     4427                    { 
    20784428          txt += "#" + el.id; 
    2079         } 
    2080         if (el.className) { 
     4429          } 
     4430 
     4431        if (el.className)  
     4432                    { 
    20814433          txt += "." + el.className; 
    2082         } 
     4434          } 
     4435 
    20834436        a.appendChild(document.createTextNode(txt)); 
    20844437        this._statusBarTree.appendChild(a); 
    2085         if (i != 0) { 
     4438        if (i != 0)  
     4439                    { 
    20864440          this._statusBarTree.appendChild(document.createTextNode(String.fromCharCode(0xbb))); 
    2087         } 
    2088       } 
    2089     } 
    2090   } 
    2091  
    2092   for (var i in this._toolbarObjects) { 
     4441          } 
     4442 
     4443        } // end of for loop. 
     4444      } 
     4445    } // end of if !text 
     4446 
     4447  for (var i in this._toolbarObjects)  
     4448    { 
    20934449    var btn = this._toolbarObjects[i]; 
    20944450    var cmd = i; 
    20954451    var inContext = true; 
    2096     if (btn.context && !text) { 
     4452 
     4453    if (btn.context && !text)  
     4454           { 
    20974455      inContext = false; 
    20984456      var context = btn.context; 
    20994457      var attrs = []; 
    2100       if (/(.*)\[(.*?)\]/.test(context)) { 
     4458 
     4459      if (/(.*)\[(.*?)\]/.test(context))  
     4460                  { 
    21014461        context = RegExp.$1; 
    21024462        attrs = RegExp.$2.split(","); 
    2103       } 
     4463        } 
     4464 
    21044465      context = context.toLowerCase(); 
    21054466      var match = (context == "*"); 
    2106       for (var k = 0; k < ancestors.length; ++k) { 
    2107         if (!ancestors[k]) { 
     4467      for (var k = 0; k < ancestors.length; ++k)  
     4468                  { 
     4469        if (!ancestors[k])  
     4470                    { 
     4471 
     4472                         this.ddt._ddt( "updateToolbar(): INTERNAL ERROR" ); 
     4473 
    21084474          // the impossible really happens. 
    21094475          continue; 
    2110         } 
    2111         if (match || (ancestors[k].tagName.toLowerCase() == context)) { 
     4476          } 
     4477 
     4478        if (match || (ancestors[k].tagName.toLowerCase() == context))  
     4479                    { 
    21124480          inContext = true; 
    2113           for (var ka = 0; ka < attrs.length; ++ka) { 
    2114             if (!eval("ancestors[k]." + attrs[ka])) { 
     4481          for (var ka = 0; ka < attrs.length; ++ka)  
     4482                           { 
     4483            if (!eval("ancestors[k]." + attrs[ka]))  
     4484                                  { 
    21154485              inContext = false; 
    21164486              break; 
     4487              } 
     4488            } 
     4489 
     4490          if (inContext)  
     4491                           { 
     4492            break; 
    21174493            } 
    21184494          } 
    2119           if (inContext) { 
    2120             break; 
    2121           } 
    21224495        } 
    21234496      } 
    2124     } 
     4497 
    21254498    btn.state("enabled", (!text || btn.text) && inContext); 
    2126     if (typeof cmd == "function") { 
     4499    if (typeof cmd == "function")  
     4500           { 
    21274501      continue; 
    2128     } 
     4502      } 
     4503 
    21294504    // look-it-up in the custom dropdown boxes 
     4505 
    21304506    var dropdown = this.config.customSelects[cmd]; 
    2131     if ((!text || btn.text) && (typeof dropdown != "undefined")) { 
     4507    if ((!text || btn.text) && (typeof dropdown != "undefined"))  
     4508           { 
    21324509      dropdown.refresh(this); 
    21334510      continue; 
    2134     } 
     4511      } 
     4512 
    21354513    switch (cmd) 
    2136     { 
    2137         case "fontname": 
    2138         case "fontsize": 
    2139       { 
    2140       if (!text) try { 
    2141         var value = ("" + doc.queryCommandValue(cmd)).toLowerCase(); 
    2142         if (!value) { 
     4514      { 
     4515      case "fontname": 
     4516      case "fontsize": 
     4517        { 
     4518        if (!text) try  
     4519                    { 
     4520          var value = ("" + doc.queryCommandValue(cmd)).toLowerCase(); 
     4521 
     4522          if (!value)  
     4523                           { 
     4524            btn.element.selectedIndex = 0; 
     4525            break; 
     4526            } 
     4527 
     4528          // HACK -- retrieve the config option for this 
     4529          // combo box.  We rely on the fact that the 
     4530          // variable in config has the same name as 
     4531          // button name in the toolbar. 
     4532 
     4533          var options = this.config[cmd]; 
     4534          var k = 0; 
     4535          for (var j in options) 
     4536            { 
     4537            // FIXME: the following line is scary. 
     4538            if ((j.toLowerCase() == value) || (options[j].substr(0, value.length).toLowerCase() == value)) 
     4539              { 
     4540              btn.element.selectedIndex = k; 
     4541              throw "ok"; 
     4542              } 
     4543            ++k; 
     4544            } 
    21434545          btn.element.selectedIndex = 0; 
    2144           break; 
     4546          } catch(e) {}; 
    21454547        } 
    2146  
    2147         // HACK -- retrieve the config option for this 
    2148         // combo box.  We rely on the fact that the 
    2149         // variable in config has the same name as 
    2150         // button name in the toolbar. 
    2151         var options = this.config[cmd]; 
    2152         var k = 0; 
    2153           for (var j in options) 
    2154           { 
    2155           // FIXME: the following line is scary. 
    2156             if ((j.toLowerCase() == value) || (options[j].substr(0, value.length).toLowerCase() == value)) 
    2157             { 
    2158             btn.element.selectedIndex = k; 
    2159             throw "ok"; 
    2160           } 
    2161           ++k; 
    2162         } 
    2163         btn.element.selectedIndex = 0; 
    2164       } catch(e) {}; 
    2165       } 
    2166       break; 
     4548        break; 
    21674549 
    21684550      // It's better to search for the format block by tag name from the 
     
    21704552      //  things like 'heading 1' for 'h1', which breaks things if you want 
    21714553      //  to call your heading blocks 'header 1'.  Stupid MS. 
     4554 
    21724555      case "formatblock"  : 
    2173       { 
     4556        { 
    21744557        var blocks = [ ]; 
    21754558        for(var i in this.config['formatblock']) 
    2176         { 
     4559          { 
    21774560          blocks[blocks.length] = this.config['formatblock'][i]; 
    2178         } 
     4561          } 
    21794562 
    21804563        var deepestAncestor = this._getFirstAncestor(this._getSelection(), blocks); 
    21814564        if(deepestAncestor) 
    2182         { 
     4565          { 
    21834566          for(var x= 0; x < blocks.length; x++) 
    2184           { 
     4567            { 
    21854568            if(blocks[x].toLowerCase() == deepestAncestor.tagName.toLowerCase()) 
    2186             { 
     4569              { 
    21874570              btn.element.selectedIndex = x; 
     4571              } 
    21884572            } 
    21894573          } 
     4574        else 
     4575          { 
     4576          btn.element.selectedIndex = 0; 
     4577          } 
    21904578        } 
    2191         else 
    2192         { 
    2193           btn.element.selectedIndex = 0; 
    2194         } 
    2195       } 
    21964579        break; 
    21974580 
    2198         case "textindicator": 
    2199       if (!text) { 
    2200         try {with (btn.element.style) { 
    2201           backgroundColor = HTMLArea._makeColor( 
    2202             doc.queryCommandValue(HTMLArea.is_ie ? "backcolor" : "hilitecolor")); 
    2203           if (/transparent/i.test(backgroundColor)) { 
    2204             // Mozilla 
    2205             backgroundColor = HTMLArea._makeColor(doc.queryCommandValue("backcolor")); 
     4581      case "textindicator": 
     4582        if (!text)  
     4583                    { 
     4584          try {with (btn.element.style)  
     4585                           { 
     4586            backgroundColor = HTMLArea._makeColor( 
     4587              doc.queryCommandValue(HTMLArea.is_ie ? "backcolor" : "hilitecolor")); 
     4588 
     4589            if (/transparent/i.test(backgroundColor))  
     4590                                  { 
     4591              // Mozilla 
     4592              backgroundColor = HTMLArea._makeColor(doc.queryCommandValue("backcolor")); 
     4593              } 
     4594 
     4595            color = HTMLArea._makeColor(doc.queryCommandValue("forecolor")); 
     4596            fontFamily = doc.queryCommandValue("fontname"); 
     4597            fontWeight = doc.queryCommandState("bold") ? "bold" : "normal"; 
     4598            fontStyle = doc.queryCommandState("italic") ? "italic" : "normal"; 
     4599            }} catch (e)  
     4600                                { 
     4601            // alert(e + "\n\n" + cmd); 
     4602            } 
    22064603          } 
    2207           color = HTMLArea._makeColor(doc.queryCommandValue("forecolor")); 
    2208           fontFamily = doc.queryCommandValue("fontname"); 
    2209           fontWeight = doc.queryCommandState("bold") ? "bold" : "normal"; 
    2210           fontStyle = doc.queryCommandState("italic") ? "italic" : "normal"; 
    2211         }} catch (e) { 
    2212           // alert(e + "\n\n" + cmd); 
    2213         } 
    2214       } 
    2215       break; 
    2216         case "htmlmode": btn.state("active", text); break; 
    2217         case "lefttoright": 
    2218         case "righttoleft": 
    2219       var el = this.getParentElement(); 
    2220       while (el && !HTMLArea.isBlockElement(el)) 
    2221         el = el.parentNode; 
    2222       if (el) 
    2223         btn.state("active", (el.style.direction == ((cmd == "righttoleft") ? "rtl" : "ltr"))); 
    2224       break; 
    2225         default: 
    2226       cmd = cmd.replace(/(un)?orderedlist/i, "insert$1orderedlist"); 
    2227       try { 
    2228         btn.state("active", (!text && doc.queryCommandState(cmd))); 
    2229       } catch (e) {} 
    2230     } 
    2231   } 
     4604        break; 
     4605 
     4606      case "htmlmode": btn.state("active", text); break; 
     4607      case "lefttoright": 
     4608      case "righttoleft": 
     4609 
     4610        var el = this.getParentElement(); 
     4611        while (el && !HTMLArea.isBlockElement(el)) 
     4612          el = el.parentNode; 
     4613 
     4614        if (el) 
     4615          btn.state("active", (el.style.direction == ((cmd == "righttoleft") ? "rtl" : "ltr"))); 
     4616        break; 
     4617 
     4618      default: 
     4619        cmd = cmd.replace(/(un)?orderedlist/i, "insert$1orderedlist"); 
     4620        try  
     4621                    { 
     4622          btn.state("active", (!text && doc.queryCommandState(cmd))); 
     4623          } catch (e) {} 
     4624      } 
     4625 
     4626    } // end of for loop over toolbar objects 
     4627 
    22324628  // take undo snapshots 
    2233   if (this._customUndo && !this._timerUndo) { 
     4629 
     4630  if (this._customUndo && !this._timerUndo)  
     4631    { 
    22344632    this._undoTakeSnapshot(); 
    22354633    var editor = this; 
    2236     this._timerUndo = setTimeout(function() { 
     4634    this._timerUndo = setTimeout(function()  
     4635           { 
    22374636      editor._timerUndo = null; 
    2238     }, this.config.undoTimeout); 
    2239   } 
     4637      }, this.config.undoTimeout); 
     4638    } 
    22404639 
    22414640  // Insert a space in certain locations, this is just to make editing a little 
     
    22464645  //  original problem was I was trying to solve with it.  I think perhaps that EnterParagraphs 
    22474646  //  might solve the problem, whatever the hell it was.  I'm going senile, I'm sure. 
     4647 
    22484648  if(0 && HTMLArea.is_gecko) 
    2249   { 
     4649    { 
    22504650    var s = this._getSelection(); 
     4651 
    22514652    // If the last character in the last text node of the parent tag 
    22524653    // and the parent tag is not a block tag 
    2253     if(s && s.isCollapsed && s.anchorNode 
     4654 
     4655    if (s && s.isCollapsed && s.anchorNode 
    22544656         && s.anchorNode.parentNode.tagName.toLowerCase() != 'body' 
    22554657         && s.anchorNode.nodeType == 3 && s.anchorOffset == s.anchorNode.length 
     
    22604662         && !HTMLArea.isBlockElement(s.anchorNode.parentNode) 
    22614663      ) 
    2262     { 
     4664      { 
     4665 
    22634666      // Insert hair-width-space after the close tag if there isn't another text node on the other side 
    22644667      // It could also work with zero-width-space (\u200B) but I don't like it so much. 
    22654668      // Perhaps this won't work well in various character sets and we should use plain space (20)? 
     4669 
    22664670      try 
    2267       { 
     4671        { 
    22684672        s.anchorNode.parentNode.parentNode.insertBefore 
    22694673          (this._doc.createTextNode('\t'), s.anchorNode.parentNode.nextSibling); 
    2270       } 
     4674        } 
    22714675      catch(e) 
    2272       { 
     4676        { 
    22734677        // Disregard 
    2274       } 
    2275     } 
    2276   } 
     4678        } 
     4679      } 
     4680    } // end of disabled block. 
    22774681 
    22784682  // check if any plugins have registered refresh handlers 
    2279   for (var i in this.plugins) { 
     4683 
     4684  for (var i in this.plugins)  
     4685    { 
    22804686    var plugin = this.plugins[i].instance; 
    22814687    if (typeof plugin.onUpdateToolbar == "function") 
    22824688      plugin.onUpdateToolbar(); 
    2283   } 
    2284  
    2285 } 
    2286  
    2287 /** Returns a node after which we can insert other nodes, in the current 
    2288  * selection.  The selection is removed.  It splits a text node, if needed. 
    2289  */ 
    2290 HTMLArea.prototype.insertNodeAtSelection = function(toBeInserted) { 
    2291   if (!HTMLArea.is_ie) { 
     4689    } 
     4690 
     4691  }  // end of updateToolbar() 
     4692 
     4693// ------------------------------------------------------------ 
     4694 
     4695/**  
     4696* insertNodeAtSelection() 
     4697* 
     4698* Returns a node after which we can insert other nodes, in the current 
     4699* selection.  The selection is removed.  It splits a text node, if needed. 
     4700*/ 
     4701 
     4702HTMLArea.prototype.insertNodeAtSelection = function(toBeInserted)  
     4703  { 
     4704 
     4705  this.ddt._ddt( "insertNodeAtSelection(): top" ); 
     4706 
     4707  if (!HTMLArea.is_ie)  
     4708    { 
    22924709    var sel = this._getSelection(); 
    22934710    var range = this._createRange(sel); 
     
    22974714    var node = range.startContainer; 
    22984715    var pos = range.startOffset; 
    2299     switch (node.nodeType) { 
    2300         case 3: // Node.TEXT_NODE 
    2301       // we have to split it at the caret position. 
    2302       if (toBeInserted.nodeType == 3) { 
    2303         // do optimized insertion 
    2304         node.insertData(pos, toBeInserted.data); 
    2305         range = this._createRange(); 
    2306         range.setEnd(node, pos + toBeInserted.length); 
    2307         range.setStart(node, pos + toBeInserted.length); 
    2308         sel.addRange(range); 
    2309       } else { 
    2310         node = node.splitText(pos); 
     4716    switch (node.nodeType)  
     4717           { 
     4718      case 3: // Node.TEXT_NODE 
     4719 
     4720        // we have to split it at the caret position. 
     4721 
     4722        if (toBeInserted.nodeType == 3)  
     4723                    { 
     4724          // do optimized insertion 
     4725          node.insertData(pos, toBeInserted.data); 
     4726          range = this._createRange(); 
     4727          range.setEnd(node, pos + toBeInserted.length); 
     4728          range.setStart(node, pos + toBeInserted.length); 
     4729          sel.addRange(range); 
     4730          }  
     4731                  else  
     4732                    { 
     4733          node = node.splitText(pos); 
     4734          var selnode = toBeInserted; 
     4735          if (toBeInserted.nodeType == 11 /* Node.DOCUMENT_FRAGMENT_NODE */)  
     4736                           { 
     4737            selnode = selnode.firstChild; 
     4738            } 
     4739 
     4740          node.parentNode.insertBefore(toBeInserted, node); 
     4741          this.selectNodeContents(selnode); 
     4742          this.updateToolbar(); 
     4743          } 
     4744        break; 
     4745 
     4746      case 1: // Node.ELEMENT_NODE 
     4747 
    23114748        var selnode = toBeInserted; 
    2312         if (toBeInserted.nodeType == 11 /* Node.DOCUMENT_FRAGMENT_NODE */) { 
     4749        if (toBeInserted.nodeType == 11 /* Node.DOCUMENT_FRAGMENT_NODE */)  
     4750                    { 
    23134751          selnode = selnode.firstChild; 
    2314         } 
    2315         node.parentNode.insertBefore(toBeInserted, node); 
     4752          } 
     4753 
     4754        node.insertBefore(toBeInserted, node.childNodes[pos]); 
    23164755        this.selectNodeContents(selnode); 
    23174756        this.updateToolbar(); 
    2318       } 
    2319       break; 
    2320         case 1: // Node.ELEMENT_NODE 
    2321       var selnode = toBeInserted; 
    2322       if (toBeInserted.nodeType == 11 /* Node.DOCUMENT_FRAGMENT_NODE */) { 
    2323         selnode = selnode.firstChild; 
    2324       } 
    2325       node.insertBefore(toBeInserted, node.childNodes[pos]); 
    2326       this.selectNodeContents(selnode); 
    2327       this.updateToolbar(); 
    2328       break; 
    2329     } 
    2330   } else { 
     4757        break; 
     4758      } 
     4759    }  
     4760  else  
     4761    { 
    23314762    return null;        // this function not yet used for IE <FIXME> 
    2332   } 
    2333 }; 
    2334  
    2335 // Returns the deepest node that contains both endpoints of the selection. 
    2336 HTMLArea.prototype.getParentElement = function(sel) { 
    2337   if(typeof sel == 'undefined') 
    2338   { 
     4763    } 
     4764 
     4765  };  // end of insertNodeAtSelection() 
     4766 
     4767// -------------------------------------------- 
     4768 
     4769/** 
     4770* getParentElement() 
     4771* 
     4772* Returns the deepest node that contains both endpoints of the selection. 
     4773*/ 
     4774 
     4775HTMLArea.prototype.getParentElement = function(sel)  
     4776  { 
     4777 
     4778  this.ddt._ddt( "getParentElement(): top" ); 
     4779 
     4780  if (typeof sel == 'undefined') 
     4781    { 
    23394782    sel = this._getSelection(); 
    2340   } 
     4783    } 
     4784 
    23414785  var range = this._createRange(sel); 
    2342   if (HTMLArea.is_ie) { 
    2343     switch (sel.type) { 
    2344         case "Text": 
    2345         case "None": 
    2346       // It seems that even for selection of type "None", 
    2347       // there _is_ a parent element and it's value is not 
    2348       // only correct, but very important to us.  MSIE is 
    2349       // certainly the buggiest browser in the world and I 
    2350       // wonder, God, how can Earth stand it? 
    2351       return range.parentElement(); 
    2352         case "Control": 
    2353       return range.item(0); 
    2354         default: 
    2355       return this._doc.body; 
    2356     } 
    2357   } else try { 
     4786 
     4787  if (HTMLArea.is_ie)  
     4788    { 
     4789    switch (sel.type)  
     4790           { 
     4791      case "Text": 
     4792      case "None": 
     4793 
     4794        // It seems that even for selection of type "None", 
     4795        // there _is_ a parent element and it's value is not 
     4796        // only correct, but very important to us.  MSIE is 
     4797        // certainly the buggiest browser in the world and I 
     4798        // wonder, God, how can Earth stand it? 
     4799 
     4800        return range.parentElement(); 
     4801 
     4802      case "Control": 
     4803        return range.item(0); 
     4804      default: 
     4805