Changeset 862


Ignore:
Timestamp:
07/10/07 16:38:51 (12 years ago)
Author:
ray
Message:

update branch with changes from trunk

Location:
branches/ray
Files:
858 edited
8 copied

Legend:

Unmodified
Added
Removed
  • branches/ray/Xinha.css

    r786 r862  
    7474  margin-bottom: 1px; 
    7575  color: ButtonText; 
     76  height: 17px; 
    7677} 
    7778 
  • branches/ray/XinhaCore.js

    • Property svn:keywords changed from LastChangedDate LastChangedRevision LastChangedBy HeadURL Id to LastChangedDate LastChangedRevision LastChangedBy HeadURL Id Rev
    r796 r862  
    103103  _editor_skin = ""; 
    104104} 
    105  
     105/** 
     106* The list of Xinha editors on the page. May be multiple editors. 
     107* You can access each editor object through this global variable. 
     108* 
     109* Example:<br /> 
     110* <code> 
     111*       var html = __xinhas[0].getEditorContent(); // gives you the HTML of the first editor in the page 
     112* </code> 
     113*/ 
    106114var __xinhas = []; 
    107115 
    108116// browser identification 
     117/** Cache the user agent for the following checks 
     118 * @private 
     119 */ 
    109120Xinha.agt       = navigator.userAgent.toLowerCase(); 
     121/** Browser is Microsoft Internet Explorer 
     122@type string  
     123*/ 
    110124Xinha.is_ie    = ((Xinha.agt.indexOf("msie") != -1) && (Xinha.agt.indexOf("opera") == -1)); 
     125/** Version Number, if browser is Microsoft Internet Explorer 
     126@type string  
     127*/ 
    111128Xinha.ie_version= parseFloat(Xinha.agt.substring(Xinha.agt.indexOf("msie")+5)); 
     129/** Browser is Opera 
     130@type string  
     131*/ 
    112132Xinha.is_opera  = (Xinha.agt.indexOf("opera") != -1); 
     133/** Version Number, if browser is Opera  
     134@type string  
     135*/ 
     136Xinha.opera_version = navigator.appVersion.substring(0, navigator.appVersion.indexOf(" "))*1; 
     137/** Browserengine is KHTML (Konqueror, Safari) 
     138@type string  
     139*/ 
    113140Xinha.is_khtml  = (Xinha.agt.indexOf("khtml") != -1); 
     141/** Browser is Safari 
     142@type string  
     143*/ 
    114144Xinha.is_safari  = (Xinha.agt.indexOf("safari") != -1); 
    115 Xinha.opera_version = navigator.appVersion.substring(0, navigator.appVersion.indexOf(" "))*1; 
     145/** OS is MacOS 
     146@type string  
     147*/ 
    116148Xinha.is_mac       = (Xinha.agt.indexOf("mac") != -1); 
     149/** Browser is Microsoft Internet Explorer Mac 
     150@type string  
     151*/ 
    117152Xinha.is_mac_ie = (Xinha.is_ie && Xinha.is_mac); 
     153/** Browser is Microsoft Internet Explorer Windows 
     154@type string  
     155*/ 
    118156Xinha.is_win_ie = (Xinha.is_ie && !Xinha.is_mac); 
     157/** Browserengine is Gecko (Mozilla) 
     158@type string  
     159*/ 
    119160Xinha.is_gecko  = (navigator.product == "Gecko" && !Xinha.is_safari); // Safari lies! 
     161/** File is opened locally opened ("file://" protocol) 
     162 * @type string 
     163 * @private 
     164 */ 
    120165Xinha.isRunLocally = document.URL.toLowerCase().search(/^file:/) != -1; 
     166/** Editing is enabled by document.designMode (Gecko, Opera), as opposed to contenteditable (IE) 
     167 * @type string 
     168 * @private 
     169 */ 
    121170Xinha.is_designMode = (typeof document.designMode != 'undefined' && !Xinha.is_ie); // IE has designMode, but we're not using it 
     171 
     172/** Check if Xinha can run in the used browser, otherwise the textarea will be remain unchanged 
     173 * @type Boolean 
     174 * @private 
     175 */ 
    122176Xinha.checkSupportedBrowser = function() 
    123177{ 
     
    140194  return Xinha.is_gecko || (Xinha.is_opera && Xinha.opera_version >= 9.1) || Xinha.ie_version >= 5.5; 
    141195}; 
    142  
     196/** Cache result of checking for browser support 
     197 * @type Boolean 
     198 * @private 
     199 */ 
    143200Xinha.isSupportedBrowser = Xinha.checkSupportedBrowser(); 
    144201 
     
    148205} 
    149206 
    150 // Creates a new Xinha object.  Tries to replace the textarea with the given 
    151 // ID with it. 
     207/** Creates a new Xinha object 
     208 * @version $Rev$ $LastChangedDate$ 
     209 * @constructor 
     210 * @param {String|DomNode}   textarea the textarea to replace; can be either only the id or the DOM object as returned by document.getElementById() 
     211 * @param {Xinha.Config} config optional if no Xinha.Config object is passed, the default config is used 
     212 */ 
    152213function Xinha(textarea, config) 
    153214{ 
     
    161222  if ( typeof config == "undefined" ) 
    162223  { 
     224                /** The configuration used in the editor 
     225                 * @type Xinha.Config 
     226                 */ 
    163227    this.config = new Xinha.Config(); 
    164228  } 
     
    167231    this.config = config; 
    168232  } 
    169   this._htmlArea = null; 
    170233 
    171234  if ( typeof textarea != 'object' ) 
     
    173236    textarea = Xinha.getElementById('textarea', textarea); 
    174237  } 
     238  /** This property references the original textarea, which is at the same time the editor in text mode 
     239   * @type DomNode textarea 
     240   */ 
    175241  this._textArea = textarea; 
    176242  this._textArea.spellcheck = false; 
    177       
    178   // Before we modify anything, get the initial textarea size 
     243  Xinha.freeLater(this, '_textArea'); 
     244   
     245  //  
     246  /** Before we modify anything, get the initial textarea size 
     247   * @private 
     248   * @type Object w,h  
     249   */ 
    179250  this._initial_ta_size = 
    180251  { 
     
    187258    if (!document.getElementById("loading_" + textarea.id)) 
    188259    { 
    189         Xinha.createLoadingMessage(textarea); 
     260      Xinha.createLoadingMessage(textarea); 
    190261    } 
    191262    this.setLoadingMessage(Xinha._lc("Constructing object")); 
    192263  } 
    193264 
     265  /** the current editing mode 
     266  * @private  
     267  * @type string "wysiwyg"|"text" 
     268  */ 
    194269  this._editMode = "wysiwyg"; 
     270  /** this object holds the plugins used in the editor 
     271  * @private  
     272  * @type Object 
     273  */ 
    195274  this.plugins = {}; 
     275  /** periodically updates the toolbar 
     276  * @private  
     277  * @type timeout 
     278  */ 
    196279  this._timerToolbar = null; 
     280  /** periodically takes a snapshot of the current editor content 
     281  * @private  
     282  * @type timeout 
     283  */ 
    197284  this._timerUndo = null; 
     285  /** holds the undo snapshots 
     286  * @private  
     287  * @type Array 
     288  */ 
    198289  this._undoQueue = [this.config.undoSteps]; 
     290  /** the current position in the undo queue  
     291  * @private  
     292  * @type integer 
     293  */ 
    199294  this._undoPos = -1; 
     295  /** use our own undo implementation (true) or the browser's (false)  
     296  * @private  
     297  * @type Boolean 
     298  */ 
    200299  this._customUndo = true; 
     300  /** the document object of the page Xinha is embedded in 
     301  * @private  
     302  * @type document 
     303  */ 
    201304  this._mdoc = document; // cache the document, we need it in plugins 
     305  /** doctype of the edited document (fullpage mode) 
     306  * @private  
     307  * @type string 
     308  */ 
    202309  this.doctype = ''; 
     310  /** running number that identifies the current editor 
     311  * @public  
     312  * @type integer 
     313  */ 
    203314  this.__htmlarea_id_num = __xinhas.length; 
    204315  __xinhas[this.__htmlarea_id_num] = this; 
    205  
     316         
     317  /** holds the events for use with the notifyOn/notifyOf system 
     318  * @private  
     319  * @type Object 
     320  */ 
    206321  this._notifyListeners = {}; 
    207322 
     
    243358    Xinha.freeLater(panels[i], 'div'); 
    244359  } 
     360  /** holds the panels 
     361  * @private  
     362  * @type Array 
     363  */ 
    245364  // finally store the variable 
    246365  this._panels = panels; 
    247  
    248   Xinha.freeLater(this, '_textArea'); 
     366         
     367  // Init some properties that are defined later 
     368  /** The statusbar container 
     369   * @type DomNode statusbar div 
     370   */ 
     371  this._statusBar = null; 
     372  /** The DOM path that is shown in the statusbar in wysiwyg mode 
     373   * @private 
     374   * @type DomNode 
     375   */ 
     376  this._statusBarTree = null; 
     377  /** The message that is shown in the statusbar in text mode 
     378   * @private 
     379   * @type DomNode 
     380   */ 
     381  this._statusBarTextMode = null; 
     382  /** Holds the items of the DOM path that is shown in the statusbar in wysiwyg mode 
     383   * @private 
     384   * @type Array tag names 
     385   */ 
     386  this._statusBarItems = []; 
     387  /** Holds the parts (table cells) of the UI (toolbar, panels, statusbar) 
     388 
     389   * @type Object framework parts 
     390   */ 
     391  this._framework = {}; 
     392  /** Them whole thing (table) 
     393   * @private 
     394   * @type DomNode 
     395   */ 
     396  this._htmlArea = null; 
     397  /** This is the actual editable area.<br /> 
     398   *  Technically it's an iframe that's made editable using window.designMode = 'on', respectively document.body.contentEditable = true (IE).<br /> 
     399   *  Use this property to get a grip on the iframe's window features<br /> 
     400   * 
     401   * @type window 
     402   */ 
     403  this._iframe = null; 
     404  /** The document object of the iframe.<br /> 
     405  *   Use this property to perform DOM operations on the edited document 
     406  * @type document 
     407  */ 
     408  this._doc = null; 
     409  /** The toolbar 
     410   *  @private 
     411   *  @type DomNode  
     412   */ 
     413  this._toolBar = this._toolbar = null; //._toolbar is for legacy, ._toolBar is better thanks. 
     414  /** Holds the botton objects 
     415   *  @private 
     416   *  @type Object 
     417   */ 
     418  this._toolbarObjects = {}; 
     419   
    249420} 
    250421 
     
    253424 
    254425// cache some regexps 
     426/** Identifies HTML tag names 
     427* @type RegExp 
     428*/ 
    255429Xinha.RE_tagName  = /(<\/|<)\s*([^ \t\n>]+)/ig; 
     430/** Exracts DOCTYPE string from HTML 
     431* @type RegExp 
     432*/ 
    256433Xinha.RE_doctype  = /(<!doctype((.|\n)*?)>)\n?/i; 
     434/** Finds head section in HTML 
     435* @type RegExp 
     436*/ 
    257437Xinha.RE_head     = /<head>((.|\n)*?)<\/head>/i; 
     438/** Finds body section in HTML 
     439* @type RegExp 
     440*/ 
    258441Xinha.RE_body     = /<body[^>]*>((.|\n|\r|\t)*?)<\/body>/i; 
     442/** Special characters that need to be escaped when dynamically creating a RegExp from an arbtrary string 
     443* @private 
     444* @type RegExp 
     445*/ 
    259446Xinha.RE_Specials = /([\/\^$*+?.()|{}[\]])/g; 
     447/** When dynamically creating a RegExp from an arbtrary string, some charactes that have special meanings in regular expressions have to be escaped. 
     448*   Run any string through this function to escape reserved characters. 
     449* @param {string} string the string to be escaped 
     450* @returns string 
     451*/ 
     452Xinha.escapeStringForRegExp = function (string) 
     453{ 
     454  return string.replace(Xinha.RE_Specials, '\\$1'); 
     455} 
     456/** Identifies email addresses 
     457* @type RegExp 
     458*/ 
    260459Xinha.RE_email    = /[_a-z\d\-\.]{3,}@[_a-z\d\-]{2,}(\.[_a-z\d\-]{2,})+/i; 
     460/** Identifies URLs 
     461* @type RegExp 
     462*/ 
    261463Xinha.RE_url      = /(https?:\/\/)?(([a-z0-9_]+:[a-z0-9_]+@)?[a-z0-9_-]{2,}(\.[a-z0-9_-]{2,}){2,}(:[0-9]+)?(\/\S+)*)/i; 
    262464 
     465 
     466 
     467/** 
     468 * This class creates an object that can be passed to the Xinha constructor as a parameter. 
     469 * Set the object's properties as you need to configure the editor (toolbar etc.) 
     470 * @version $Rev$ $LastChangedDate$ 
     471 * @constructor 
     472 */ 
    263473Xinha.Config = function() 
    264474{ 
    265475  var cfg = this; 
    266476  this.version = Xinha.version.Revision; 
    267  
    268   // Width and Height 
    269   //  you may set these as follows 
    270   //  width = 'auto'      -- the width of the original textarea will be used 
    271   //  width = 'toolbar'   -- the width of the toolbar will be used 
    272   //  width = '<css measure>' -- use any css measurement, eg width = '75%' 
    273   // 
    274   //  height = 'auto'     -- the height of the original textarea 
    275   //  height = '<css measure>' -- any css measurement, eg height = '480px' 
     477   
     478 /** This property controls the width of the editor.<br /> 
     479  *  Allowed values are 'auto', 'toolbar' or a numeric value followed by "px".<br /> 
     480  *  <code>auto</code>: let Xinha choose the width to use.<br /> 
     481  *  <code>toolbar</code>: compute the width size from the toolbar width.<br /> 
     482  *  <code>numeric value</code>: forced width in pixels ('600px').<br /> 
     483  *  
     484  *  Default: <code>"auto"</code> 
     485  * @type String 
     486  */ 
    276487  this.width  = "auto"; 
     488 /** This property controls the height of the editor.<br /> 
     489  *  Allowed values are 'auto' or a numeric value followed by px.<br /> 
     490  *  <code>"auto"</code>: let Xinha choose the height to use.<br /> 
     491  *  <code>numeric value</code>: forced height in pixels ('200px').<br /> 
     492  *  Default: <code>"auto"</code>  
     493  * @type String 
     494  */ 
    277495  this.height = "auto"; 
    278496 
    279   // the next parameter specifies whether the toolbar should be included 
    280   // in the size above, or are extra to it.  If false then it's recommended 
    281   // to have explicit pixel sizes above (or on your textarea and have auto above) 
     497 /** Specifies whether the toolbar should be included 
     498  *  in the size, or are extra to it.  If false then it's recommended 
     499  *  to have the size set as explicit pixel sizes (either in Xinha.Config or on your textarea)<br /> 
     500  * 
     501  *  Default: <code>true</code> 
     502  * 
     503  *  @type Boolean 
     504  */ 
    282505  this.sizeIncludesBars = true; 
    283  
    284   // the next parameter specifies whether the panels should be included 
    285   // in the size above, or are extra to it.  If false then it's recommended 
    286   // to have explicit pixel sizes above (or on your textarea and have auto above) 
     506 /** 
     507  * Specifies whether the panels should be included 
     508  * in the size, or are extra to it.  If false then it's recommended 
     509  * to have the size set as explicit pixel sizes (either in Xinha.Config or on your textarea)<br /> 
     510  *   
     511  *  Default: <code>true</code> 
     512  * 
     513  *  @type Boolean 
     514  */ 
    287515  this.sizeIncludesPanels = true; 
    288516 
    289   // each of the panels has a dimension, for the left/right it's the width 
    290   // for the top/bottom it's the height. 
    291   // 
    292   // WARNING: PANEL DIMENSIONS MUST BE SPECIFIED AS PIXEL WIDTHS 
     517 /** 
     518  * each of the panels has a dimension, for the left/right it's the width 
     519  * for the top/bottom it's the height. 
     520  * 
     521  * WARNING: PANEL DIMENSIONS MUST BE SPECIFIED AS PIXEL WIDTHS<br /> 
     522  *Default values:   
     523  *<pre> 
     524  *       xinha_config.panel_dimensions = 
     525  *   { 
     526  *         left:   '200px', // Width 
     527  *         right:  '200px', 
     528  *         top:    '100px', // Height 
     529  *         bottom: '100px' 
     530  *       } 
     531  *</pre> 
     532  *  @type Object 
     533  */ 
    293534  this.panel_dimensions = 
    294535  { 
     
    299540  }; 
    300541 
    301   // enable creation of a status bar? 
     542 /**  To make the iframe width narrower than the toolbar width, e.g. to maintain 
     543  *   the layout when editing a narrow column of text, set the next parameter (in pixels).<br /> 
     544  * 
     545  *  Default: <code>true</code> 
     546  * 
     547  *  @type Integer|null 
     548  */ 
     549  this.iframeWidth = null; 
     550  
     551 /** Enable creation of the status bar?<br /> 
     552  * 
     553  *  Default: <code>true</code> 
     554  * 
     555  *  @type Boolean  
     556  */ 
    302557  this.statusBar = true; 
    303558 
    304   // intercept ^V and use the Xinha paste command 
    305   // If false, then passes ^V through to browser editor widget 
     559 /** Intercept ^V and use the Xinha paste command 
     560  *  If false, then passes ^V through to browser editor widget, which is the only way it works without problems in Mozilla<br /> 
     561  * 
     562  *  Default: <code>false</code> 
     563  * 
     564  *  @type Boolean 
     565  */ 
    306566  this.htmlareaPaste = false; 
    307  
    308   this.mozParaHandler = 'best'; // set to 'built-in', 'dirty' or 'best' 
    309                                 // built-in: will (may) use 'br' instead of 'p' tags 
    310                                 // dirty   : will use p and work good enough for the majority of cases, 
    311                                 // best    : works the best, but it's about 12kb worth of javascript 
    312                                 //   and will probably be slower than 'dirty'.  This is the "EnterParagraphs" 
    313                                 //   plugin from "hipikat", rolled in to be part of the core code 
    314  
    315567   
    316   // possible values  
    317   //    'DOMwalk' (the "original") 
    318   //    'TransformInnerHTML' (this used to be the GetHtml plugin) 
     568 /** <strong>Gecko only:</strong> Let the built-in routine for handling the <em>return</em> key decide if to enter <em>br</em> or <em>p</em> tags, 
     569  *  or use a custom implementation.<br /> 
     570  *  For information about the rules applied by Gecko, <a href="http://www.mozilla.org/editor/rules.html">see Mozilla website</a> <br /> 
     571  *  Possible values are <em>built-in</em> or <em>best</em><br /> 
     572  * 
     573  *  Default: <code>"best"</code> 
     574  * 
     575  *  @type String 
     576  */ 
     577  this.mozParaHandler = 'best';  
     578   
     579 /** This determines the method how the HTML output is generated. 
     580  *  There are two choices: 
     581  *  
     582  *<table border="1"> 
     583  *   <tr> 
     584  *       <td><em>DOMwalk</em></td> 
     585  *       <td>This is the classic and proven method. It recusively traverses the DOM tree  
     586  *           and builds the HTML string "from scratch". Tends to be a bit slow, especially in IE.</td> 
     587  *   </tr> 
     588  *   <tr> 
     589  *       <td><em>TransformInnerHTML</em></td> 
     590  *       <td>This method uses the JavaScript innerHTML property and relies on Regular Expressions to produce 
     591  *            clean XHTML output. This method is much faster than the other one.</td> 
     592  *     </tr> 
     593  * </table> 
     594  * 
     595  *  Default: <code>"DOMwalk"</code> 
     596  * 
     597  * @type String 
     598  */ 
    319599  this.getHtmlMethod = 'DOMwalk'; 
    320600   
    321   // maximum size of the undo queue 
     601  /** Maximum size of the undo queue<br /> 
     602   *  Default: <code>20</code> 
     603   *  @type Integer 
     604   */ 
    322605  this.undoSteps = 20; 
    323606 
    324   // the time interval at which undo samples are taken 
    325   this.undoTimeout = 500;       // 1/2 sec. 
    326  
    327   // set this to true if you want to explicitly right-justify when  
    328   // setting the text direction to right-to-left 
     607  /** The time interval at which undo samples are taken<br /> 
     608   *  Default: <code>500</code> (1/2 sec) 
     609   *  @type Integer milliseconds 
     610   */ 
     611  this.undoTimeout = 500; 
     612 
     613  /** Set this to true if you want to explicitly right-justify when setting the text direction to right-to-left<br /> 
     614   *  Default: <code>false</code> 
     615   *  @type Boolean 
     616   */ 
    329617  this.changeJustifyWithDirection = false; 
    330618 
    331   // if true then Xinha will retrieve the full HTML, starting with the 
    332   // <HTML> tag. 
     619  /** If true then Xinha will retrieve the full HTML, starting with the &lt;HTML&gt; tag.<br /> 
     620   *  Default: <code>false</code> 
     621   *  @type Boolean 
     622   */ 
    333623  this.fullPage = false; 
    334624 
    335   // style included in the iframe document 
     625  /** Raw style definitions included in the edited document<br /> 
     626   *  When a lot of inline style is used, perhaps it is wiser to use one or more external stylesheets.<br /> 
     627   *  To set tags P in red, H1 in blue andn A not underlined, we may do the following 
     628   *<pre> 
     629   * xinha_config.pageStyle = 
     630   *  'p { color:red; }\n' + 
     631   *  'h1 { color:bleu; }\n' + 
     632   *  'a {text-decoration:none; }'; 
     633   *</pre> 
     634   *  Default: <code>""</code> (empty) 
     635   *  @type String 
     636   */ 
    336637  this.pageStyle = ""; 
    337638 
    338   // external stylesheets to load (REFERENCE THESE ABSOLUTELY) 
     639  /** Array of external stylesheets to load. (Reference these absolutely)<br /> 
     640   *  Example<br /> 
     641   *  <pre>xinha_config.pageStyleSheets = ["/css/myPagesStyleSheet.css","/css/anotherOne.css"];</pre> 
     642   *  Default: <code>[]</code> (empty) 
     643   *  @type Array 
     644   */ 
    339645  this.pageStyleSheets = []; 
    340646 
    341647  // specify a base href for relative links 
     648  /** Specify a base href for relative links<br /> 
     649   *  ATTENTION: this does not work as expected and needs t be changed, see Ticket #961 <br /> 
     650   *  Default: <code>null</code> 
     651   *  @type String|null 
     652   */ 
    342653  this.baseHref = null; 
    343654 
    344   // when the editor is in different directory depth as the edited page relative image sources 
    345   // will break the display of your images 
    346   // this fixes an issue where Mozilla converts the urls of images and links that are on the same server  
    347   // to relative ones (../) when dragging them around in the editor (Ticket #448) 
     655  /** If true, relative URLs (../) will be made absolute.  
     656   *  When the editor is in different directory depth  
     657   *  as the edited page relative image sources will break the display of your images. 
     658   *  this fixes an issue where Mozilla converts the urls of images and links that are on the same server  
     659   *  to relative ones (../) when dragging them around in the editor (Ticket #448)<br /> 
     660   *  Default: <code>true</code> 
     661   *  @type Boolean 
     662   */ 
    348663  this.expandRelativeUrl = true; 
    349664   
    350   //   we can strip the base href out of relative links to leave them relative, reason for this 
    351   //   especially if you don't specify a baseHref is that mozilla at least (& IE ?) will prefix 
    352   //   the baseHref to any relative links to make them absolute, which isn't what you want most the time. 
     665 /**  We can strip the server part out of URL to make/leave them semi-absolute, reason for this 
     666   *  is that the browsers will prefix  the server to any relative links to make them absolute,  
     667   *  which isn't what you want most the time.<br /> 
     668   *  Default: <code>true</code> 
     669   *  @type Boolean 
     670   */ 
    353671  this.stripBaseHref = true; 
    354672 
    355   // and we can strip the url of the editor page from named links (eg <a href="#top">...</a>) 
    356   //  reason for this is that mozilla at least (and IE ?) prefixes location.href to any 
    357   //  that don't have a url prefixing them 
     673   /**  We can strip the url of the editor page from named links (eg &lt;a href="#top"&gt;...&lt;/a&gt;) 
     674   *  reason for this is that mozilla at least (and IE ?) prefixes location.href to any anchor 
     675   *  that don't have a url prefixing them<br /> 
     676   *  Default: <code>true</code> 
     677   *  @type Boolean 
     678   */ 
    358679  this.stripSelfNamedAnchors = true; 
    359680 
    360   // sometimes high-ascii in links can cause problems for servers (basically they don't recognise them) 
    361   //  so you can use this flag to ensure that all characters other than the normal ascii set (actually 
    362   //  only ! through ~) are escaped in URLs to % codes 
     681  /** In URLs all characters above ASCII value 127 have to be encoded using % codes<br /> 
     682   *  Default: <code>true</code> 
     683   *  @type Boolean 
     684   */ 
    363685  this.only7BitPrintablesInURLs = true; 
    364686 
    365   // if you are putting the HTML written in Xinha into an email you might want it to be 7-bit 
    366   //  characters only.  This config option (off by default) will convert all characters consuming 
    367   //  more than 7bits into UNICODE decimal entity references (actually it will convert anything 
    368   //  below <space> (chr 20) except cr, lf and tab and above <tilde> (~, chr 7E)) 
     687  
     688  /** If you are putting the HTML written in Xinha into an email you might want it to be 7-bit 
     689   *  characters only.  This config option will convert all characters consuming 
     690   *  more than 7bits into UNICODE decimal entity references (actually it will convert anything 
     691   *  below <space> (chr 20) except cr, lf and tab and above <tilde> (~, chr 7E))<br /> 
     692   *  Default: <code>false</code> 
     693   *  @type Boolean 
     694   */ 
    369695  this.sevenBitClean = false; 
    370696 
    371   // sometimes we want to be able to replace some string in the html comng in and going out 
    372   //  so that in the editor we use the "internal" string, and outside and in the source view 
    373   //  we use the "external" string  this is useful for say making special codes for 
    374   //  your absolute links, your external string might be some special code, say "{server_url}" 
    375   //  an you say that the internal represenattion of that should be http://your.server/ 
     697 
     698  /** Sometimes we want to be able to replace some string in the html comng in and going out 
     699   *  so that in the editor we use the "internal" string, and outside and in the source view 
     700   *  we use the "external" string  this is useful for say making special codes for 
     701   *  your absolute links, your external string might be some special code, say "{server_url}" 
     702   *  an you say that the internal represenattion of that should be http://your.server/<br /> 
     703   *  Example:  <code>{'external_string' : 'internal_string'}</code><br /> 
     704   *  Default: <code>{}</code> (empty) 
     705   *  @type Object 
     706   */ 
    376707  this.specialReplacements = {}; // { 'external_string' : 'internal_string' } 
    377708 
    378   // set to true if you want Word code to be cleaned upon Paste 
     709 /** Set to true if you want Word code to be cleaned upon Paste. This only works if  
     710   * you use the toolbr button to paste, not ^V. This means that due to the restrictions 
     711   * regarding pasting, this actually has no real effect in Mozilla <br /> 
     712   *  Default: <code>true</code> 
     713   *  @type Boolean 
     714   */ 
    379715  this.killWordOnPaste = true; 
    380716 
    381   // enable the 'Target' field in the Make Link dialog 
     717  /** Enable the 'Target' field in the Make Link dialog. Note that the target attribute is invalid in (X)HTML strict<br /> 
     718   *  Default: <code>true</code> 
     719   *  @type Boolean 
     720   */ 
    382721  this.makeLinkShowsTarget = true; 
    383722 
    384   // CharSet of the iframe, default is the charset of the document 
     723  /** CharSet of the iframe, default is the charset of the document 
     724   *  @type String 
     725   */ 
    385726  this.charSet = (typeof document.characterSet != 'undefined') ? document.characterSet : document.charset; 
    386727 
    387   // Whether the edited document should be rendered in Quirksmode or Standard Compliant (Strict) Mode 
    388   // This is commonly known as the "doctype switch" 
    389   // for details read here http://www.quirksmode.org/css/quirksmode.html 
    390   // 
    391   // Possible values: 
    392   //    true     :  Quirksmode is used 
    393   //    false    :  Strict mode is used 
    394   // leave empty :  the mode of the document Xinha is in is used 
    395   this.browserQuirksMode = ''; 
     728 /** Whether the edited document should be rendered in Quirksmode or Standard Compliant (Strict) Mode.<br /> 
     729   * This is commonly known as the "doctype switch"<br /> 
     730   * for details read here http://www.quirksmode.org/css/quirksmode.html 
     731   * 
     732   * Possible values:<br /> 
     733   *    true     :  Quirksmode is used<br /> 
     734   *    false    :  Strict mode is used<br /> 
     735   *    null (default):  the mode of the document Xinha is in is used 
     736   * @type Boolean|null 
     737   */ 
     738  this.browserQuirksMode = null; 
    396739 
    397740  // URL-s 
     
    399742  this.popupURL = "popups/"; 
    400743 
    401   // remove tags (these have to be a regexp, or null if this functionality is not desired) 
     744  /** Remove given tags when rendering the HTML (these have to be a regexp, or null if this functionality is not desired)<br /> 
     745   *  Default: <code>null</code> 
     746   *  @type RegExp|null 
     747   */ 
    402748  this.htmlRemoveTags = null; 
    403749 
    404   // Turning this on will turn all "linebreak" and "separator" items in your toolbar into soft-breaks, 
    405   // this means that if the items between that item and the next linebreak/separator can 
    406   // fit on the same line as that which came before then they will, otherwise they will 
    407   // float down to the next line. 
    408  
    409   // If you put a linebreak and separator next to each other, only the separator will 
    410   // take effect, this allows you to have one toolbar that works for both flowToolbars = true and false 
    411   // infact the toolbar below has been designed in this way, if flowToolbars is false then it will 
    412   // create explictly two lines (plus any others made by plugins) breaking at justifyleft, however if 
    413   // flowToolbars is false and your window is narrow enough then it will create more than one line 
    414   // even neater, if you resize the window the toolbars will reflow.  Niiiice. 
    415  
     750 /** Turning this on will turn all "linebreak" and "separator" items in your toolbar into soft-breaks, 
     751   * this means that if the items between that item and the next linebreak/separator can 
     752   * fit on the same line as that which came before then they will, otherwise they will 
     753   * float down to the next line. 
     754 
     755   * If you put a linebreak and separator next to each other, only the separator will 
     756   * take effect, this allows you to have one toolbar that works for both flowToolbars = true and false 
     757   * infact the toolbar below has been designed in this way, if flowToolbars is false then it will 
     758   * create explictly two lines (plus any others made by plugins) breaking at justifyleft, however if 
     759   * flowToolbars is false and your window is narrow enough then it will create more than one line 
     760   * even neater, if you resize the window the toolbars will reflow.  <br /> 
     761   *  Default: <code>true</code> 
     762   *  @type Boolean 
     763   */ 
    416764  this.flowToolbars = true; 
    417765   
    418   // set to true if you want the loading panel to show at startup 
     766  /** Set to center or right to change button alignment in toolbar 
     767   *  @type String 
     768   */ 
     769  this.toolbarAlign = "left"; 
     770   
     771  /** Set to true if you want the loading panel to show at startup<br /> 
     772   *  Default: <code>false</code> 
     773   *  @type Boolean 
     774   */ 
    419775  this.showLoading = false; 
    420  
    421   // set to false if you want to allow JavaScript in the content, otherwise <script> tags are stripped out 
     776   
     777  /** Set to false if you want to allow JavaScript in the content, otherwise &lt;script&gt; tags are stripped out.<br /> 
     778   *  This currently only affects the "DOMwalk" getHtmlMethod.<br /> 
     779   *  Default: <code>true</code> 
     780   *  @type Boolean 
     781   */ 
    422782  this.stripScripts = true; 
    423783 
    424   // see if the text just typed looks like a URL, or email address 
    425   // and link it appropriatly 
    426   // Note: Setting this option to false only affects Mozilla based browsers. 
    427   // In InternetExplorer this is native behaviour and cannot be turned off. 
    428   this.convertUrlsToLinks = true; 
    429  
    430   // size of color picker cells 
     784 /** See if the text just typed looks like a URL, or email address 
     785   * and link it appropriatly 
     786   * Note: Setting this option to false only affects Mozilla based browsers. 
     787   * In InternetExplorer this is native behaviour and cannot be turned off.<br /> 
     788   *  Default: <code>true</code> 
     789   *  @type Boolean 
     790   */ 
     791   this.convertUrlsToLinks = true; 
     792 
     793 
     794 /** Size of color picker cells<br /> 
     795   * Use number + "px"<br /> 
     796   *  Default: <code>"6px"</code> 
     797   *  @type String 
     798   */ 
    431799  this.colorPickerCellSize = '6px'; 
    432   // granularity of color picker cells (number per column/row) 
     800 /** Granularity of color picker cells (number per column/row)<br /> 
     801   *  Default: <code>18</code> 
     802   *  @type Integer 
     803   */ 
    433804  this.colorPickerGranularity = 18; 
    434   // position of color picker from toolbar button 
     805 /** Position of color picker from toolbar button<br /> 
     806   *  Default: <code>"bottom,right"</code> 
     807   *  @type String 
     808   */ 
    435809  this.colorPickerPosition = 'bottom,right'; 
    436   // set to true to show websafe checkbox in picker 
     810  /** Set to true to show only websafe checkbox in picker<br /> 
     811   *  Default: <code>false</code> 
     812   *  @type Boolean 
     813   */ 
    437814  this.colorPickerWebSafe = false; 
    438   // number of recent colors to remember 
     815 /** Number of recent colors to remember<br /> 
     816   *  Default: <code>20</code> 
     817   *  @type Integer 
     818   */ 
    439819  this.colorPickerSaveColors = 20; 
    440820 
    441   // start up the editor in fullscreen mode 
     821  /** Start up the editor in fullscreen mode<br /> 
     822   *  Default: <code>false</code> 
     823   *  @type Boolean 
     824   */ 
    442825  this.fullScreen = false; 
    443826   
    444   // you can tell the fullscreen mode to leave certain margins on each side 
    445   // the value is an array with the values for [top,right,bottom,left] in that order 
     827 /** You can tell the fullscreen mode to leave certain margins on each side.<br /> 
     828   *  The value is an array with the values for <code>[top,right,bottom,left]</code> in that order<br /> 
     829   *  Default: <code>[0,0,0,0]</code> 
     830   *  @type Array 
     831   */ 
    446832  this.fullScreenMargins = [0,0,0,0]; 
    447833   
    448   /** CUSTOMIZING THE TOOLBAR 
    449    * ------------------------- 
    450    * 
    451    * It is recommended that you customize the toolbar contents in an 
    452    * external file (i.e. the one calling Xinha) and leave this one 
    453    * unchanged.  That's because when we (InteractiveTools.com) release a 
    454    * new official version, it's less likely that you will have problems 
    455    * upgrading Xinha. 
    456    */ 
     834  /** This array orders all buttons except plugin buttons in the toolbar. Plugin buttons typically look for one  
     835   *  a certain button in the toolbar and place themselves next to it. 
     836   * Default value: 
     837   *<pre> 
     838   *xinha_config.toolbar = 
     839   * [ 
     840   *   ["popupeditor"], 
     841   *   ["separator","formatblock","fontname","fontsize","bold","italic","underline","strikethrough"], 
     842   *   ["separator","forecolor","hilitecolor","textindicator"], 
     843   *   ["separator","subscript","superscript"], 
     844   *   ["linebreak","separator","justifyleft","justifycenter","justifyright","justifyfull"], 
     845   *   ["separator","insertorderedlist","insertunorderedlist","outdent","indent"], 
     846   *   ["separator","inserthorizontalrule","createlink","insertimage","inserttable"], 
     847   *   ["linebreak","separator","undo","redo","selectall","print"], (Xinha.is_gecko ? [] : ["cut","copy","paste","overwrite","saveas"]), 
     848   *   ["separator","killword","clearfonts","removeformat","toggleborders","splitblock","lefttoright", "righttoleft"], 
     849   *   ["separator","htmlmode","showhelp","about"] 
     850   * ]; 
     851   *</pre> 
     852   * @type Array 
     853   */   
    457854  this.toolbar = 
    458855  [ 
     
    469866  ]; 
    470867 
    471  
     868  /** The fontnames listed in the fontname dropdown 
     869   * Default value: 
     870   *<pre> 
     871   *xinha_config.fontname = 
     872   *{ 
     873   *  "&mdash; font &mdash;" : '', 
     874   *  "Arial"                : 'arial,helvetica,sans-serif', 
     875   *  "Courier New"          : 'courier new,courier,monospace', 
     876   *  "Georgia"              : 'georgia,times new roman,times,serif', 
     877   *  "Tahoma"               : 'tahoma,arial,helvetica,sans-serif', 
     878   *  "Times New Roman"      : 'times new roman,times,serif', 
     879   *  "Verdana"              : 'verdana,arial,helvetica,sans-serif', 
     880   *  "impact"               : 'impact', 
     881   *  "WingDings"            : 'wingdings' 
     882   *}; 
     883   *</pre> 
     884   * @type Object 
     885   */ 
    472886  this.fontname = 
    473887  { 
    474888    "&mdash; font &mdash;": '', 
    475     "Arial":             'arial,helvetica,sans-serif', 
    476     "Courier New":         'courier new,courier,monospace', 
    477     "Georgia":         'georgia,times new roman,times,serif', 
    478     "Tahoma":            'tahoma,arial,helvetica,sans-serif', 
    479     "Times New Roman": 'times new roman,times,serif', 
    480     "Verdana":         'verdana,arial,helvetica,sans-serif', 
    481     "impact":            'impact', 
    482     "WingDings":             'wingdings' 
     889    "Arial"           : 'arial,helvetica,sans-serif', 
     890    "Courier New"     : 'courier new,courier,monospace', 
     891    "Georgia"         : 'georgia,times new roman,times,serif', 
     892    "Tahoma"          : 'tahoma,arial,helvetica,sans-serif', 
     893    "Times New Roman" : 'times new roman,times,serif', 
     894    "Verdana"         : 'verdana,arial,helvetica,sans-serif', 
     895    "impact"          : 'impact', 
     896    "WingDings"       : 'wingdings'  
    483897  }; 
    484898 
     899  /** The fontsizes listed in the fontsize dropdown 
     900   * Default value: 
     901   *<pre> 
     902   *xinha_config.fontsize = 
     903   *{ 
     904   *  "&mdash; size &mdash;": "", 
     905   *  "1 (8 pt)" : "1", 
     906   *  "2 (10 pt)": "2", 
     907   *  "3 (12 pt)": "3", 
     908   *  "4 (14 pt)": "4", 
     909   *  "5 (18 pt)": "5", 
     910   *  "6 (24 pt)": "6", 
     911   *  "7 (36 pt)": "7" 
     912   *}; 
     913   *</pre> 
     914   * @type Object 
     915   */ 
    485916  this.fontsize = 
    486917  { 
     
    494925    "7 (36 pt)": "7" 
    495926  }; 
    496  
     927  /** The tags listed in the formatblock dropdown 
     928   * Default value: 
     929   *<pre> 
     930   *xinha_config.formatblock = 
     931   *{ 
     932   *  "&mdash; size &mdash;": "", 
     933   *  "1 (8 pt)" : "1", 
     934   *  "2 (10 pt)": "2", 
     935   *  "3 (12 pt)": "3", 
     936   *  "4 (14 pt)": "4", 
     937   *  "5 (18 pt)": "5", 
     938   *  "6 (24 pt)": "6", 
     939   *  "7 (36 pt)": "7" 
     940   *}; 
     941   *</pre> 
     942   * @type Object 
     943   */ 
    497944  this.formatblock = 
    498945  { 
     
    508955    "Formatted": "pre" 
    509956  }; 
    510  
     957  /** ?? 
     958   * Default: <code>{}</code> 
     959   * @type Object 
     960   */ 
    511961  this.customSelects = {}; 
    512962 
    513   function cut_copy_paste(e, cmd, obj) { e.execCommand(cmd); } 
    514  
     963  /** Switches on some debugging (only in execCommand() as far as I see at the moment)<br /> 
     964   * 
     965   * Default: <code>true</code> 
     966   * @type Boolean 
     967   */ 
    515968  this.debug = true; 
    516969 
    517   // ATTENTION: implementing custom image/link/table popups using this uris is DEPRECATED 
    518970  this.URIs = 
    519971  { 
     
    528980 
    529981 
    530   // ADDING CUSTOM BUTTONS: please read below! 
    531   // format of the btnList elements is "ID: [ ToolTip, Icon, Enabled in text mode?, ACTION ]" 
    532   //    - ID: unique ID for the button.  If the button calls document.execCommand 
    533   //        it's wise to give it the same name as the called command. 
    534   //    - ACTION: function that gets called when the button is clicked. 
    535   //              it has the following prototype: 
    536   //                 function(editor, buttonName) 
    537   //              - editor is the Xinha object that triggered the call 
    538   //              - buttonName is the ID of the clicked button 
    539   //              These 2 parameters makes it possible for you to use the same 
    540   //              handler for more Xinha objects or for more different buttons. 
    541   //    - ToolTip: tooltip, will be translated below 
    542   //    - Icon: path to an icon image file for the button 
    543   //            OR; you can use an 18x18 block of a larger image by supllying an array 
    544   //            that has three elemtents, the first is the larger image, the second is the column 
    545   //            the third is the row.  The ros and columns numbering starts at 0 but there is 
    546   //            a header row and header column which have numbering to make life easier. 
    547   //            See images/buttons_main.gif to see how it's done. 
    548   //    - Enabled in text mode: if false the button gets disabled for text-only mode; otherwise enabled all the time. 
     982   /** The button list conains the definitions of the toolbar button. Normally, there's nothing to change here :)  
     983   * <div style="white-space:pre">ADDING CUSTOM BUTTONS: please read below! 
     984   * format of the btnList elements is "ID: [ ToolTip, Icon, Enabled in text mode?, ACTION ]" 
     985   *    - ID: unique ID for the button.  If the button calls document.execCommand 
     986   *        it's wise to give it the same name as the called command. 
     987   *    - ACTION: function that gets called when the button is clicked. 
     988   *              it has the following prototype: 
     989   *                 function(editor, buttonName) 
     990   *              - editor is the Xinha object that triggered the call 
     991   *              - buttonName is the ID of the clicked button 
     992   *              These 2 parameters makes it possible for you to use the same 
     993   *              handler for more Xinha objects or for more different buttons. 
     994   *    - ToolTip: tooltip, will be translated below 
     995   *    - Icon: path to an icon image file for the button 
     996   *            OR; you can use an 18x18 block of a larger image by supllying an array 
     997   *            that has three elemtents, the first is the larger image, the second is the column 
     998   *            the third is the row.  The ros and columns numbering starts at 0 but there is 
     999   *            a header row and header column which have numbering to make life easier. 
     1000   *            See images/buttons_main.gif to see how it's done. 
     1001   *    - Enabled in text mode: if false the button gets disabled for text-only mode; otherwise enabled all the time.</div> 
     1002   * @type Object 
     1003   */ 
    5491004  this.btnList = 
    5501005  { 
     
    5731028    undo: [ "Undoes your last action", ["ed_buttons_main.gif",4,2], false, function(e) { e.execCommand("undo"); } ], 
    5741029    redo: [ "Redoes your last action", ["ed_buttons_main.gif",5,2], false, function(e) { e.execCommand("redo"); } ], 
    575     cut: [ "Cut selection", ["ed_buttons_main.gif",5,0], false, cut_copy_paste ], 
    576     copy: [ "Copy selection", ["ed_buttons_main.gif",4,0], false, cut_copy_paste ], 
    577     paste: [ "Paste from clipboard", ["ed_buttons_main.gif",4,1], false, cut_copy_paste ], 
     1030    cut: [ "Cut selection", ["ed_buttons_main.gif",5,0], false,  function (e, cmd) { e.execCommand(cmd); } ], 
     1031    copy: [ "Copy selection", ["ed_buttons_main.gif",4,0], false,  function (e, cmd) { e.execCommand(cmd); } ], 
     1032    paste: [ "Paste from clipboard", ["ed_buttons_main.gif",4,1], false,  function (e, cmd) { e.execCommand(cmd); } ], 
    5781033    selectall: [ "Select all", "ed_selectall.gif", false, function(e) {e.execCommand("selectall");} ], 
    5791034 
     
    6011056  }; 
    6021057 
    603   /* ADDING CUSTOM BUTTONS 
    604    * --------------------- 
    605    * 
    606    * It is recommended that you add the custom buttons in an external 
    607    * file and leave this one unchanged.  That's because when we 
    608    * (InteractiveTools.com) release a new official version, it's less 
    609    * likely that you will have problems upgrading Xinha. 
    610    * 
    611    * Example on how to add a custom button when you construct the Xinha: 
    612    * 
    613    *   var editor = new Xinha("your_text_area_id"); 
    614    *   var cfg = editor.config; // this is the default configuration 
    615    *   cfg.btnList["my-hilite"] = 
    616    *    [ function(editor) { editor.surroundHTML('<span style="background:yellow">', '</span>'); }, // action 
    617    *      "Highlight selection", // tooltip 
    618    *      "my_hilite.gif", // image 
    619    *      false // disabled in text mode 
    620    *    ]; 
    621    *   cfg.toolbar.push(["linebreak", "my-hilite"]); // add the new button to the toolbar 
    622    * 
    623    * An alternate (also more convenient and recommended) way to 
    624    * accomplish this is to use the registerButton function below. 
    625    */ 
     1058 
    6261059  // initialize tooltips from the I18N module and generate correct image path 
    6271060  for ( var i in this.btnList ) 
     
    6451078 
    6461079}; 
    647  
     1080/** ADDING CUSTOM BUTTONS 
     1081*   --------------------- 
     1082* 
     1083* 
     1084* Example on how to add a custom button when you construct the Xinha: 
     1085* 
     1086*   var editor = new Xinha("your_text_area_id"); 
     1087*   var cfg = editor.config; // this is the default configuration 
     1088*   cfg.btnList["my-hilite"] = 
     1089*       [ function(editor) { editor.surroundHTML('<span style="background:yellow">', '</span>'); }, // action 
     1090*         "Highlight selection", // tooltip 
     1091*         "my_hilite.gif", // image 
     1092*         false // disabled in text mode 
     1093*       ]; 
     1094*   cfg.toolbar.push(["linebreak", "my-hilite"]); // add the new button to the toolbar 
     1095* 
     1096* An alternate (also more convenient and recommended) way to 
     1097* accomplish this is to use the registerButton function below. 
     1098*/ 
    6481099/** Helper function: register a new button with the configuration.  It can be 
    6491100 * called with all 5 arguments, or with only one (first one).  When called with 
    6501101 * only one argument it must be an object with the following properties: id, 
    651  * tooltip, image, textMode, action.  Examples: 
    652  * 
    653  * 1. config.registerButton("my-hilite", "Hilite text", "my-hilite.gif", false, function(editor) {...}); 
    654  * 2. config.registerButton({ 
     1102 * tooltip, image, textMode, action.<br />   
     1103 *  
     1104 * Examples:<br /> 
     1105 *<pre> 
     1106 * config.registerButton("my-hilite", "Hilite text", "my-hilite.gif", false, function(editor) {...}); 
     1107 * config.registerButton({ 
    6551108 *      id       : "my-hilite",      // the ID of your button 
    6561109 *      tooltip  : "Hilite text",    // the tooltip 
     
    6611114 *                 }, 
    6621115 *      context  : "p"               // will be disabled if outside a <p> element 
    663  *    }); 
     1116 *    });</pre> 
    6641117 */ 
    6651118Xinha.Config.prototype.registerButton = function(id, tooltip, image, textMode, action, context) 
     
    7041157    side = 'right'; 
    7051158  } 
    706   this.setLoadingMessage(Xinha._lc('Register '+ side + 'panel')); 
     1159  this.setLoadingMessage('Register ' + side + ' panel '); 
    7071160  var panel = this.addPanel(side); 
    7081161  if ( object ) 
     
    9301383  } 
    9311384}; 
    932  
     1385/** Alias of Xinha.Config.prototype.hideSomeButtons() 
     1386* @type Function 
     1387*/ 
    9331388Xinha.Config.prototype.removeToolbarElement = Xinha.Config.prototype.hideSomeButtons; 
    9341389 
    935 /** Helper function: replace all TEXTAREA-s in the document with Xinha-s. */ 
     1390/** Helper function: replace all TEXTAREA-s in the document with Xinha-s.  
     1391* @param {Xinha.Config} optional config  
     1392*/ 
    9361393Xinha.replaceAll = function(config) 
    9371394{ 
     
    9441401}; 
    9451402 
    946 /** Helper function: replaces the TEXTAREA with the given ID with Xinha. */ 
     1403/** Helper function: replaces the TEXTAREA with the given ID with Xinha.  
     1404* @param {string} id id of the textarea to replace  
     1405* @param {Xinha.Config} optional config  
     1406*/ 
    9471407Xinha.replace = function(id, config) 
    9481408{ 
     
    9501410  return ta ? (new Xinha(ta, config)).generate() : null; 
    9511411}; 
    952  
    953 // Creates the toolbar and appends it to the _htmlarea 
     1412  
     1413/** Creates the toolbar and appends it to the _htmlarea 
     1414* @private 
     1415* @returns {DomNode} toolbar 
     1416*/ 
    9541417Xinha.prototype._createToolbar = function () 
    9551418{ 
     
    9761439}; 
    9771440 
    978 // FIXME : function never used, can probably be removed from source 
     1441/** FIXME : function never used, can probably be removed from source 
     1442* @private 
     1443* @deprecated 
     1444*/ 
    9791445Xinha.prototype._setConfig = function(config) 
    9801446{ 
    9811447        this.config = config; 
    9821448}; 
    983  
     1449/** FIXME: How can this be used?? 
     1450* @private 
     1451*/ 
    9841452Xinha.prototype._addToolbar = function() 
    9851453{ 
     
    9901458 * Create a break element to add in the toolbar 
    9911459 * 
    992  * @return {Object} HTML element to add 
     1460 * @return {DomNode} HTML element to add 
    9931461 * @private 
    9941462 */ 
     
    10041472}; 
    10051473 
    1006 // separate from previous createToolBar to allow dynamic change of toolbar 
     1474 
     1475/** separate from previous createToolBar to allow dynamic change of toolbar 
     1476 * @private 
     1477 * @return {DomNode} toolbar 
     1478 */ 
    10071479Xinha.prototype._createToolbar1 = function (editor, toolbar, tb_objects) 
    10081480{ 
     
    14011873//         Why the hell this is not in the config object ? 
    14021874var use_clone_img = false; 
     1875/** creates a button (i.e. container element + image) 
     1876 * @private 
     1877 * @return {DomNode} conteainer element 
     1878 */ 
    14031879Xinha.makeBtnImg = function(imgDef, doc) 
    14041880{ 
     
    14741950  return i_contain; 
    14751951}; 
    1476  
     1952/** creates the status bar  
     1953 * @private 
     1954 * @return {DomNode} status bar 
     1955 */ 
    14771956Xinha.prototype._createStatusBar = function() 
    14781957{ 
    1479   this.setLoadingMessage(Xinha._lc('Create StatusBar')); 
     1958  this.setLoadingMessage(Xinha._lc('Create Statusbar')); 
    14801959  var statusbar = document.createElement("div"); 
    14811960  statusbar.className = "statusBar"; 
     
    14881967  div.className = "statusBarTree"; 
    14891968  div.innerHTML = Xinha._lc("Path") + ": "; 
     1969 
    14901970  this._statusBarTree = div; 
    14911971  Xinha.freeLater(this, '_statusBarTree'); 
     
    14951975  div.innerHTML = Xinha._lc("You are in TEXT MODE.  Use the [<>] button to switch back to WYSIWYG."); 
    14961976  div.style.display = "none"; 
     1977 
    14971978  this._statusBarTextMode = div; 
    14981979  Xinha.freeLater(this, '_statusBarTextMode'); 
     
    15041985    statusbar.style.display = "none"; 
    15051986  } 
    1506  
    1507   this._statusBarItems = []; 
    1508    
    15091987  return statusbar; 
    15101988}; 
    15111989 
    1512 // Creates the Xinha object and replaces the textarea with it. 
     1990/** Creates the Xinha object and replaces the textarea with it. Loads required files. 
     1991 *  @returns {Boolean} 
     1992 */ 
    15131993Xinha.prototype.generate = function () 
    15141994{ 
     
    15181998  var editor = this;  // we'll need "this" in some nested functions 
    15191999  var url; 
     2000  var found = false; 
     2001  var links = document.getElementsByTagName("link"); 
     2002 
     2003  if (!document.getElementById("XinhaCoreDesign")) 
     2004  { 
     2005    _editor_css = (typeof _editor_css == "string") ? _editor_css : "Xinha.css"; 
     2006    for(i = 0; i<links.length; i++) 
     2007    { 
     2008      if ( ( links[i].rel == "stylesheet" ) && ( links[i].href == _editor_url + _editor_css ) ) 
     2009      { 
     2010        found = true; 
     2011      } 
     2012    } 
     2013    if ( !found ) 
     2014    { 
     2015      Xinha.loadStyle(_editor_css,null,"XinhaCoreDesign"); 
     2016    } 
     2017  } 
    15202018   
    1521   if (!document.getElementById("XinhaCoreDesign")) 
    1522   { 
    1523     Xinha.loadStyle(typeof _editor_css == "string" ? _editor_css : "Xinha.css",null,"XinhaCoreDesign"); 
     2019  if ( _editor_skin !== "" && !document.getElementById("XinhaSkin")) 
     2020  { 
     2021    found = false; 
     2022    for(i = 0; i<links.length; i++) 
     2023    { 
     2024      if ( ( links[i].rel == "stylesheet" ) && ( links[i].href == _editor_url + 'skins/' + _editor_skin + '/skin.css' ) ) 
     2025      { 
     2026        found = true; 
     2027      } 
     2028    } 
     2029    if ( !found ) 
     2030    { 
     2031      Xinha.loadStyle('skins/' + _editor_skin + '/skin.css',null,"XinhaSkin") 
     2032    } 
    15242033  } 
    15252034   
     
    15462055  } 
    15472056 
    1548   url = _editor_url + 'modules/Dialogs/dialog.js'; 
    1549   if ( typeof Dialog == 'undefined' && !document.getElementById(url) ) 
    1550   { 
    1551     Xinha._loadback( url, this.generate, this ); 
     2057  if ( typeof Dialog == 'undefined' && !Xinha._loadback( _editor_url + 'modules/Dialogs/dialog.js', this.generate, this ) ) 
     2058  {     
    15522059    return false; 
    15532060  } 
    15542061 
    1555   url = _editor_url + 'modules/Dialogs/XinhaDialog.js'; 
    1556   if ( typeof Xinha.Dialog == 'undefined' && !document.getElementById(url) ) 
    1557   { 
    1558     Xinha._loadback( url , this.generate, this ); 
     2062  if ( typeof Xinha.Dialog == 'undefined' &&  !Xinha._loadback( _editor_url + 'modules/Dialogs/XinhaDialog.js' , this.generate, this ) ) 
     2063  {     
    15592064    return false; 
    15602065  } 
     
    16112116          } 
    16122117          else if ( typeof InsertTable != 'undefined') editor.registerPlugin('InsertTable'); 
    1613         break;         
     2118        break; 
    16142119      } 
    16152120    } 
     
    16522157  } 
    16532158  else editor.registerPlugin('GetHtmlImplementation'); 
    1654    
    1655  
    1656   if ( _editor_skin !== "" ) 
    1657   { 
    1658     var found = false; 
    1659     var head = document.getElementsByTagName("head")[0]; 
    1660     var links = document.getElementsByTagName("link"); 
    1661     for(i = 0; i<links.length; i++) 
    1662     { 
    1663       if ( ( links[i].rel == "stylesheet" ) && ( links[i].href == _editor_url + 'skins/' + _editor_skin + '/skin.css' ) ) 
    1664       { 
    1665         found = true; 
    1666       } 
    1667     } 
    1668     if ( !found ) 
    1669     { 
    1670       var link = document.createElement("link"); 
    1671       link.type = "text/css"; 
    1672       link.href = _editor_url + 'skins/' + _editor_skin + '/skin.css'; 
    1673       link.rel = "stylesheet"; 
    1674       head.appendChild(link); 
    1675     } 
    1676   } 
    16772159   
    16782160  // create the editor framework, yah, table layout I know, but much easier 
     
    18442326    { 
    18452327      textarea.value = editor.outwardHtml(editor.getHTML()); 
    1846       var x = xinha.parentNode.replaceChild(textarea,xinha); 
    1847       // put it back into the page to let Xinha.collectGarbageForIE() do its work afterwards 
    1848       textarea.style.display = ""; 
    1849       x.style.display = 'none'; 
    1850       document.body.appendChild(x); 
     2328      if (!Xinha.is_ie) 
     2329      { 
     2330        xinha.parentNode.replaceChild(textarea,xinha); 
     2331      } 
    18512332      return true; 
    18522333    } 
     
    19102391 *    false   = the tool & status bars will appear outside the width & height confines 
    19112392 * 
     2393 * @private 
    19122394 */ 
    19132395 
     
    19582440/** 
    19592441 *  Size the editor to a specific size, or just refresh the size (when window resizes for example) 
    1960  *  @param width optional width (CSS specification) 
    1961  *  @param height optional height (CSS specification) 
    1962  *  @param includingBars optional boolean to indicate if the size should include or exclude tool & status bars 
     2442 *  @param {string} width optional width (CSS specification) 
     2443 *  @param {string} height optional height (CSS specification) 
     2444 *  @param {Boolean} includingBars optional to indicate if the size should include or exclude tool & status bars 
     2445 *  @param {Boolean} includingPanels optional to indicate if the size should include or exclude panels 
    19632446 */ 
    19642447Xinha.prototype.sizeEditor = function(width, height, includingBars, includingPanels) 
     
    21572640  this._risizing = false; 
    21582641}; 
    2159  
     2642/** FIXME: Never used, what is this for?  
     2643* @param {string} side  
     2644* @param {Object} 
     2645*/ 
     2646Xinha.prototype.registerPanel = function(side, object) 
     2647{ 
     2648  if ( !side ) 
     2649  { 
     2650    side = 'right'; 
     2651  } 
     2652  this.setLoadingMessage('Register ' + side + ' panel '); 
     2653  var panel = this.addPanel(side); 
     2654  if ( object ) 
     2655  { 
     2656    object.drawPanelIn(panel); 
     2657  } 
     2658}; 
     2659/** Creates a panel in the panel container on the specified side 
     2660* @param {String} side the panel container to which the new panel will be added<br /> 
     2661*                                                                       Possible values are: "right","left","top","bottom" 
     2662* @returns {DomNode} Panel div 
     2663*/ 
    21602664Xinha.prototype.addPanel = function(side) 
    21612665{ 
     
    21752679  return div; 
    21762680}; 
    2177  
    2178  
     2681/** Removes a panel 
     2682* @param {DomNode} panel object as returned by Xinha.prototype.addPanel() 
     2683*/ 
    21792684Xinha.prototype.removePanel = function(panel) 
    21802685{ 
     
    21912696  this.notifyOf('panel_change', {'action':'remove','panel':panel}); 
    21922697}; 
    2193  
     2698/** Hides a panel 
     2699* @param {DomNode} panel object as returned by Xinha.prototype.addPanel() 
     2700*/ 
    21942701Xinha.prototype.hidePanel = function(panel) 
    21952702{ 
     
    22022709  } 
    22032710}; 
    2204  
     2711/** Shows a panel 
     2712* @param {DomNode} panel object as returned by Xinha.prototype.addPanel() 
     2713*/ 
    22052714Xinha.prototype.showPanel = function(panel) 
    22062715{ 
     
    22132722  } 
    22142723}; 
    2215  
     2724/** Hides the panel(s) on one or more sides 
     2725* @param {Array} sides the sides on which the panels shall be hidden 
     2726*/ 
    22162727Xinha.prototype.hidePanels = function(sides) 
    22172728{ 
     
    22322743  this.notifyOf('panel_change', {'action':'multi_hide','sides':sides}); 
    22332744}; 
    2234  
     2745/** Shows the panel(s) on one or more sides 
     2746* @param {Array} sides the sides on which the panels shall be hidden 
     2747*/ 
    22352748Xinha.prototype.showPanels = function(sides) 
    22362749{ 
     
    22512764  this.notifyOf('panel_change', {'action':'multi_show','sides':sides}); 
    22522765}; 
    2253  
     2766/** Returns an array containig all properties that are set in an object 
     2767* @param {Object} obj 
     2768* @returns {Array} 
     2769*/ 
    22542770Xinha.objectProperties = function(obj) 
    22552771{ 
     
    22622778}; 
    22632779 
    2264 /* 
    2265  * EDITOR ACTIVATION NOTES: 
     2780/** Checks if editor is active 
     2781 *<br /> 
     2782 * EDITOR ACTIVATION NOTES:<br /> 
    22662783 *  when a page has multiple Xinha editors, ONLY ONE should be activated at any time (this is mostly to 
    22672784 *  work around a bug in Mozilla, but also makes some sense).  No editor should be activated or focused 
    22682785 *  automatically until at least one editor has been activated through user action (by mouse-clicking in 
    22692786 *  the editor). 
     2787 * @private 
     2788 * @returns {Boolean} 
    22702789 */ 
    22712790Xinha.prototype.editorIsActivated = function() 
     
    22802799  } 
    22812800}; 
    2282  
     2801/**  We need to know that at least one editor on the page has been activated 
     2802*    this is because we will not focus any editor until an editor has been activated 
     2803* @private 
     2804* @type {Boolean} 
     2805*/ 
    22832806Xinha._someEditorHasBeenActivated = false; 
    2284 Xinha._currentlyActiveEditor      = false; 
     2807/**  Stores a reference to the currently active editor 
     2808* @private 
     2809* @type {Xinha} 
     2810*/ 
     2811Xinha._currentlyActiveEditor      = null; 
     2812/** Enables one editor for editing, e.g. by a click in the editing area or after it has been  
     2813 *  deactivated programmatically before  
     2814 * @private 
     2815 * @returns {Boolean} 
     2816 */ 
    22852817Xinha.prototype.activateEditor = function() 
    22862818{ 
     
    23122844    } catch (ex) {} 
    23132845  } 
    2314   else if ( !Xinha.is_gecko && this._doc.body.contentEditable !== true ) 
     2846  else if ( Xinha.is_ie&& this._doc.body.contentEditable !== true ) 
    23152847  { 
    23162848    this._doc.body.contentEditable = true; 
    2317   } 
    2318  
    2319   // We need to know that at least one editor on the page has been activated 
    2320   // this is because we will not focus any editor until an editor has been activated 
     2849 
     2850    if (this._iframe.contentWindow.event.srcElement.tagName.toLowerCase() == 'html') // if  clicked below the text (=body), the text cursor does not appear, see #1019  
     2851    { 
     2852      var r = this._doc.body.createTextRange(); 
     2853      setTimeout (function () { r.collapse();  r.select();},100); // won't do without timeout, dunno why 
     2854    } 
     2855  } 
     2856 
    23212857  Xinha._someEditorHasBeenActivated = true; 
    23222858  Xinha._currentlyActiveEditor      = this; 
     
    23252861  this.enableToolbar(); 
    23262862}; 
    2327  
     2863/** Disables the editor  
     2864 * @private 
     2865 */ 
    23282866Xinha.prototype.deactivateEditor = function() 
    23292867{ 
     
    23542892  Xinha._currentlyActiveEditor = false; 
    23552893}; 
    2356  
     2894/** Creates the iframe (editable area) 
     2895 * @private 
     2896 */ 
    23572897Xinha.prototype.initIframe = function() 
    23582898{ 
     
    24673007 * Delay a function until the document is ready for operations. 
    24683008 * See ticket:547 
    2469  * @param {object} F (Function) The function to call once the document is ready 
    24703009 * @public 
    2471  */ 
    2472 Xinha.prototype.whenDocReady = function(F) 
    2473 { 
    2474   var E = this; 
     3010 * @param {Function} f  The function to call once the document is ready 
     3011 */ 
     3012Xinha.prototype.whenDocReady = function(f) 
     3013{ 
     3014  var e = this; 
    24753015  if ( this._doc && this._doc.body ) 
    24763016  { 
    2477     F(); 
     3017    f(); 
    24783018  } 
    24793019  else 
    24803020  { 
    2481     setTimeout(function() { E.whenDocReady(F); }, 50); 
    2482   } 
    2483 }; 
    2484  
    2485 // Switches editor mode; parameter can be "textmode" or "wysiwyg".  If no 
    2486 // parameter was passed this function toggles between modes. 
     3021    setTimeout(function() { e.whenDocReady(f); }, 50); 
     3022  } 
     3023}; 
     3024 
     3025 
     3026/** Switches editor mode between wysiwyg and text (HTML) 
     3027 * @param {String} mode optional "textmode" or "wysiwyg", if omitted, toggles between modes. 
     3028 */ 
    24873029Xinha.prototype.setMode = function(mode) 
    24883030{ 
     
    25453087  } 
    25463088}; 
    2547  
     3089/** Sets the HTML in fullpage mode. Actually the whole iframe document is rewritten. 
     3090 * @private 
     3091 * @param {String} html 
     3092 */ 
    25483093Xinha.prototype.setFullHTML = function(html) 
    25493094{ 
     
    25903135  } 
    25913136}; 
    2592  
     3137/** Initialize some event handlers 
     3138 * @private 
     3139 */ 
    25933140Xinha.prototype.setEditorEvents = function() 
    25943141{ 
    25953142  var editor=this; 
    2596   var doc=this._doc; 
     3143  var doc = (Xinha.is_ie) ? this._doc.getElementsByTagName("html")[0] : this._doc; // #1019 Cusor not jumping to editable part of window when clicked in IE, see also #1039 
     3144 
    25973145  editor.whenDocReady( 
    25983146    function() 
     
    26463194 ***************************************************/ 
    26473195 
    2648 // Create the specified plugin and register it with this Xinha 
    2649 // return the plugin created to allow refresh when necessary 
     3196 
     3197/** Create the specified plugin and register it with this Xinha 
     3198 *  return the plugin created to allow refresh when necessary.<br /> 
     3199 *  <strong>This is only useful if Xinha is generated without using Xinha.makeEditors()</strong> 
     3200 */ 
    26503201Xinha.prototype.registerPlugin = function() 
    26513202{ 
     
    26683219  return this.registerPlugin2(plugin, args); 
    26693220}; 
    2670  
    2671 // this is the variant of the function above where the plugin arguments are 
    2672 // already packed in an array.  Externally, it should be only used in the 
    2673 // full-screen editor code, in order to initialize plugins with the same 
    2674 // parameters as in the opener window. 
     3221/** This is the variant of the function above where the plugin arguments are 
     3222 * already packed in an array.  Externally, it should be only used in the 
     3223 * full-screen editor code, in order to initialize plugins with the same 
     3224 * parameters as in the opener window. 
     3225 * @private 
     3226 */ 
    26753227Xinha.prototype.registerPlugin2 = function(plugin, args) 
    26763228{ 
     
    27053257}; 
    27063258 
    2707 // static function that loads the required plugin and lang file, based on the 
    2708 // language loaded already for Xinha.  You better make sure that the plugin 
    2709 // _has_ that language, otherwise shit might happen ;-) 
     3259 
     3260/** Dynamically returns the directory from which the plugins are loaded<br /> 
     3261 *  This could be overridden to change the dir<br /> 
     3262 *  @TODO: Wouldn't this be better as a config option? 
     3263 * @private 
     3264 * @param {String} pluginName 
     3265 * @returns {String} path to plugin 
     3266 */ 
    27103267Xinha.getPluginDir = function(pluginName) 
    27113268{ 
    27123269  return _editor_url + "plugins/" + pluginName; 
    27133270}; 
    2714  
     3271/** Static function that loads the given plugin 
     3272 * @param {String} pluginName 
     3273 * @param {Function} callback function to be called when file is loaded 
     3274 * @param {String} plugin_file URL of the file to load 
     3275 * @returns {Boolean} true if plugin loaded, false otherwise 
     3276 */ 
    27153277Xinha.loadPlugin = function(pluginName, callback, plugin_file) 
    27163278{ 
     
    27203282  // @todo : try to avoid the use of eval() 
    27213283  // Might already be loaded 
    2722   if ( eval('typeof ' + pluginName) != 'undefined' ) 
     3284  //if ( eval('typeof ' + pluginName) != 'undefined' ) 
     3285  // @todo: try if this works to avoid the use of eval, I've been never getting here when testing 
     3286  if ( typeof window['pluginName'] != 'undefined' ) 
    27233287  { 
    27243288    if ( callback ) 
     
    27393303  return false; 
    27403304}; 
    2741  
     3305/** Stores a status for each loading plugin that may be one of "loading","ready", or "failed" 
     3306 * @private 
     3307 * @type {Object}  
     3308 */ 
    27423309Xinha._pluginLoadStatus = {}; 
    27433310 
     3311/** Static function that loads the plugins (see xinha_plugins in NewbieGuide) 
     3312 * @param {Array} plugins 
     3313 * @param {Function} callbackIfNotReady function that is called repeatedly until all files are 
     3314 * @returns {Boolean} true if all plugins are loaded, false otherwise 
     3315 */ 
    27443316Xinha.loadPlugins = function(plugins, callbackIfNotReady) 
    27453317{ 
     
    28143386}; 
    28153387 
    2816 // refresh plugin by calling onGenerate or onGenerateOnce method. 
     3388//  
     3389/** Refresh plugin by calling onGenerate or onGenerateOnce method. 
     3390 * @private 
     3391 * @param {PluginInstance} plugin 
     3392 */ 
    28173393Xinha.refreshPlugin = function(plugin) 
    28183394{ 
     
    28283404}; 
    28293405 
    2830 /** Call a method of all plugins which define the method using the supplied arguments. 
     3406/** Call a method of all plugins which define the method using the supplied arguments.<br /><br /> 
    28313407 * 
    2832  *  Example: editor.firePluginEvent('onExecCommand', 'paste') 
     3408 *  Example: <code>editor.firePluginEvent('onExecCommand', 'paste')</code><br /> 
     3409 *           The plugin would then define a method<br /> 
     3410 *           <code>PluginName.prototype.onExecCommand = function (cmdID, UI, param) {do something...}</code><br /><br /> 
     3411 *           The following methodNames are currently available:<br /> 
     3412 *  <table border="1"> 
     3413 *    <tr> 
     3414 *       <th>methodName</th><th>Parameters</th> 
     3415 *     </tr> 
     3416 *     <tr> 
     3417 *       <td>onExecCommand</td><td> cmdID, UI, param</td> 
     3418 *     </tr> 
     3419 *     <tr> 
     3420 *       <td>onKeyPress</td><td>ev</td> 
     3421 *     </tr>  
     3422 *     <tr> 
     3423 *       <td>onMouseDown</td><td>ev</td> 
     3424 *     </tr> 
     3425 * </table><br /><br /> 
    28333426 *   
    28343427 *  The browser specific plugin (if any) is called last.  The result of each call is  
     
    28363429 *  will get the event, a false return means the event will continue to fire. 
    28373430 * 
    2838  *  @param methodName 
    2839  *  @param arguments to pass to the method, optional [2..n]  
     3431 *  @param {String} methodName 
     3432 *  @param {mixed} arguments to pass to the method, optional [2..n]  
     3433 *  @returns {Boolean} 
    28403434 */ 
    28413435 
     
    28773471  return false; 
    28783472} 
    2879  
     3473/** Adds a stylesheet to the document 
     3474 * @param {String} style name of the stylesheet file 
     3475 * @param {String} plugin optional name of a plugin; if passed this function looks for the stylesheet file in the plugin directory  
     3476 * @param {String} id optional a unique id for identifiing the created link element, e.g. for avoiding double loading  
     3477 *                 or later removing it again 
     3478 */ 
    28803479Xinha.loadStyle = function(style, plugin, id) 
    28813480{ 
     
    28833482  if ( plugin ) 
    28843483  { 
    2885     url += "plugins/" + plugin + "/"; 
     3484    url = Xinha.getPluginDir( plugin ) + "/"; 
    28863485  } 
    28873486  url += style; 
     
    28993498  link.rel = "stylesheet"; 
    29003499  link.href = url; 
     3500  link.type = "text/css"; 
    29013501  if (id) link.id = id; 
    29023502  head.appendChild(link); 
     
    29073507 *  Category: EDITOR UTILITIES 
    29083508 ***************************************************/ 
    2909  
     3509/** Utility function: Outputs the structure of the edited document */ 
    29103510Xinha.prototype.debugTree = function() 
    29113511{ 
     
    29383538  document.body.appendChild(ta); 
    29393539}; 
     3540/** Extracts the textual content of a given node 
     3541 * @param {DomNode} el 
     3542 */ 
    29403543 
    29413544Xinha.getInnerText = function(el) 
     
    29553558  return txt; 
    29563559}; 
    2957  
     3560/** Cleans dirty HTML from MS word; always cleans the whole editor content 
     3561 *  @TODO: move this in a separate file 
     3562 *  @TODO: turn this into a static function that cleans a given string 
     3563 */ 
    29583564Xinha.prototype._wordClean = function() 
    29593565{ 
     
    30853691}; 
    30863692 
     3693/** Removes &lt;font&gt; tags; always cleans the whole editor content 
     3694 *  @TODO: move this in a separate file 
     3695 *  @TODO: turn this into a static function that cleans a given string 
     3696 */ 
    30873697Xinha.prototype._clearFonts = function() 
    30883698{ 
     
    31183728}; 
    31193729 
     3730/** Sometimes the display has to be refreshed to make DOM changes visible (?) (Gecko bug?)  */ 
    31203731Xinha.prototype.forceRedraw = function() 
    31213732{ 
     
    31253736}; 
    31263737 
    3127 // focuses the iframe window.  returns a reference to the editor document. 
     3738/** Focuses the iframe window.  
     3739 * @returns {document} a reference to the editor document 
     3740 */ 
    31283741Xinha.prototype.focusEditor = function() 
    31293742{ 
     
    31563769}; 
    31573770 
    3158 // takes a snapshot of the current text (for undo) 
     3771/** Takes a snapshot of the current text (for undo) 
     3772 * @private 
     3773 */ 
    31593774Xinha.prototype._undoTakeSnapshot = function() 
    31603775{ 
     
    31823797  } 
    31833798}; 
    3184  
     3799/** Custom implementation of undo functionality 
     3800 * @private 
     3801 */ 
    31853802Xinha.prototype.undo = function() 
    31863803{ 
     
    31983815  } 
    31993816}; 
    3200  
     3817/** Custom implementation of redo functionality 
     3818 * @private 
     3819 */ 
    32013820Xinha.prototype.redo = function() 
    32023821{ 
     
    32143833  } 
    32153834}; 
    3216  
     3835/** Disables (greys out) the buttons of the toolbar 
     3836 * @param {Array} except this array contains ids of toolbar objects that will not be disabled 
     3837 */ 
    32173838Xinha.prototype.disableToolbar = function(except) 
    32183839{ 
     
    32453866  } 
    32463867}; 
    3247  
     3868/** Enables the toolbar again when disabled by disableToolbar() */ 
    32483869Xinha.prototype.enableToolbar = function() 
    32493870{ 
     
    32513872}; 
    32523873 
    3253 if ( !Array.prototype.contains ) 
    3254 { 
    3255   Array.prototype.contains = function(needle) 
    3256   { 
    3257     var haystack = this; 
    3258     for ( var i = 0; i < haystack.length; i++ ) 
    3259     { 
    3260       if ( needle == haystack[i] ) 
    3261       { 
    3262         return true; 
    3263       } 
    3264     } 
    3265     return false; 
    3266   }; 
    3267 } 
    3268  
    3269 if ( !Array.prototype.indexOf ) 
    3270 { 
    3271   Array.prototype.indexOf = function(needle) 
    3272   { 
    3273     var haystack = this; 
    3274     for ( var i = 0; i < haystack.length; i++ ) 
    3275     { 
    3276       if ( needle == haystack[i] ) 
    3277       { 
    3278         return i; 
    3279       } 
    3280     } 
    3281     return null; 
    3282   }; 
    3283 } 
    3284  
     3874/** Updates enabled/disable/active state of the toolbar elements, the statusbar and other things 
     3875 *  This function is called on every key stroke as well as by a timer on a regular basis.<br /> 
     3876 *  Plugins have the opportunity to implement a prototype.onUpdateToolbar() method, which will also 
     3877 *  be called by this function. 
     3878 * @param {Boolean} noStatus private use Exempt updating of statusbar 
     3879 */ 
    32853880// FIXME : this function needs to be splitted in more functions. 
    32863881// It is actually to heavy to be understable and very scary to manipulate 
    3287 // updates enabled/disable/active state of the toolbar elements 
    32883882Xinha.prototype.updateToolbar = function(noStatus) 
    32893883{ 
     
    36094203}; 
    36104204 
    3611  
    3612 // moved Xinha.prototype.insertNodeAtSelection() to browser specific file 
    3613 // moved Xinha.prototype.getParentElement() to browser specific file 
    3614  
    3615 // Returns an array with all the ancestor nodes of the selection. 
     4205/** Returns an array with all the ancestor nodes of the selection or current cursor position. 
     4206* @returns {Array} 
     4207*/ 
    36164208Xinha.prototype.getAllAncestors = function() 
    36174209{ 
     
    36274219}; 
    36284220 
    3629 // Returns the deepest ancestor of the selection that is of the current type 
     4221/** Traverses the DOM upwards and returns the first element that is of one of the specified types 
     4222 *  @param {Selection} sel  Selection object as returned by getSelection 
     4223 *  @param {Array} types Array of HTML tag names (lower case) 
     4224 *  @returns {DomNode|null}  
     4225 */ 
    36304226Xinha.prototype._getFirstAncestor = function(sel, types) 
    36314227{ 
     
    36764272}; 
    36774273 
    3678 // moved Xinha.prototype._activeElement() to browser specific file 
    3679 // moved Xinha.prototype._selectionEmpty() to browser specific file 
    3680  
     4274/** Traverses the DOM upwards and returns the first element that is a block level element 
     4275 *  @param {Selection} sel  Selection object as returned by getSelection 
     4276 *  @returns {DomNode|null}  
     4277 */ 
    36814278Xinha.prototype._getAncestorBlock = function(sel) 
    36824279{ 
     
    37254322}; 
    37264323 
     4324/** What's this? does nothing, has to be removed 
     4325 *  
     4326 * @deprecated 
     4327 */ 
    37274328Xinha.prototype._createImplicitBlock = function(type) 
    37284329{ 
     
    37474348 
    37484349 
    3749 // moved Xinha.prototype.selectNodeContents() to browser specific file 
    3750 // moved Xinha.prototype.insertHTML() to browser specific file 
    37514350 
    37524351/** 
    37534352 *  Call this function to surround the existing HTML code in the selection with 
    3754  *  your tags.  FIXME: buggy!  This function will be deprecated "soon". 
     4353 *  your tags.  FIXME: buggy! Don't use this  
    37554354 * @todo: when will it be deprecated ? Can it be removed already ? 
     4355 * @private (tagged private to not further promote use of this function) 
     4356 * @deprecated 
    37564357 */ 
    37574358Xinha.prototype.surroundHTML = function(startTag, endTag) 
     
    37624363}; 
    37634364 
    3764 // moved  Xinha.prototype.getSelectedHTML() to browser specific file 
    3765  
    3766 /// Return true if we have some selection 
     4365/** Return true if we have some selection 
     4366 *  @returns {Boolean}  
     4367 */ 
    37674368Xinha.prototype.hasSelectedText = function() 
    37684369{ 
     
    37704371  return this.getSelectedHTML() !== ''; 
    37714372}; 
    3772  
    3773 // moved Xinha.prototype._createLink() to popups/link.js 
    3774 // moved Xinha.prototype._insertImage() to popups/insert_image.js 
    3775  
    3776 // Called when the user clicks the Insert Table button 
    3777  
    37784373 
    37794374/*************************************************** 
     
    37814376 ***************************************************/ 
    37824377 
    3783 // el is reference to the SELECT object 
    3784 // txt is the name of the select field, as in config.toolbar 
     4378/** onChange handler for dropdowns in toolbar  
     4379 *  @private 
     4380 *  @param {DomNode} el Reference to the SELECT object 
     4381 *  @param {String} txt  The name of the select field, as in config.toolbar 
     4382 *  @returns {DomNode|null}  
     4383 */ 
    37854384Xinha.prototype._comboSelected = function(el, txt) 
    37864385{ 
     
    38214420}; 
    38224421 
    3823 /** 
    3824  * Open a popup to select the hilitecolor or forecolor 
    3825  * 
     4422/** Open a popup to select the hilitecolor or forecolor 
     4423 * @private 
    38264424 * @param {String} cmdID The commande ID (hilitecolor or forecolor) 
    3827  * @private 
    38284425 */ 
    38294426Xinha.prototype._colorSelector = function(cmdID) 
     
    38824479}; 
    38834480 
    3884 // the execCommand function (intercepts some commands and replaces them with 
    3885 // our own implementation) 
     4481/** This is a wrapper for the browser's execCommand function that handles things like  
     4482 *  formatting, inserting elements, etc.<br /> 
     4483 *  It intercepts some commands and replaces them with our own implementation.<br /> 
     4484 *  It provides a hook for the "firePluginEvent" system ("onExecCommand").<br /><br /> 
     4485 *  For reference see:<br /> 
     4486 *     <a href="http://www.mozilla.org/editor/midas-spec.html">Mozilla implementation</a><br /> 
     4487 *     <a href="http://msdn.microsoft.com/workshop/author/dhtml/reference/methods/execcommand.asp">MS implementation</a> 
     4488 * 
     4489 *  @see Xinha#firePluginEvent 
     4490 *  @param {String} cmdID command to be executed as defined in the browsers implemantations or Xinha custom 
     4491 *  @param {Boolean} UI for compatibility with the execCommand syntax; false in most (all) cases 
     4492 *  @param {Mixed} param Some commands require parameters 
     4493 *  @returns {Boolean} always false  
     4494 */ 
    38864495Xinha.prototype.execCommand = function(cmdID, UI, param) 
    38874496{ 
     
    40134622}; 
    40144623 
    4015 /** A generic event handler for things that happen in the IFRAME's document. 
    4016  * @todo: this function is *TOO* generic, it needs to be splitted in more specific handlers 
    4017  * This function also handles key bindings. */ 
     4624/** A generic event handler for things that happen in the IFRAME's document.<br /> 
     4625 *  It provides two hooks for the "firePluginEvent" system:<br /> 
     4626 *   "onKeyPress"<br /> 
     4627 *   "onMouseDown" 
     4628 *  @see Xinha#firePluginEvent 
     4629 *  @param {Event} ev 
     4630 */ 
    40184631Xinha.prototype._editorEvent = function(ev) 
    40194632{ 
     
    40654678}; 
    40664679 
    4067 // handles ctrl + key shortcuts  
     4680/** Handles ctrl + key shortcuts  
     4681 *  @TODO: make this mor flexible 
     4682 *  @private 
     4683 *  @param {Event} ev 
     4684 */ 
    40684685Xinha.prototype._shortCuts = function (ev) 
    40694686{ 
     
    41114728  } 
    41124729}; 
    4113  
     4730/** Changes the type of a given node 
     4731 *  @param {DomNode} el The element to convert 
     4732 *  @param {String} newTagName The type the element will be converted to 
     4733 *  @returns {DomNode} A reference to the new element 
     4734 */ 
    41144735Xinha.prototype.convertNode = function(el, newTagName) 
    41154736{ 
     
    41224743}; 
    41234744 
    4124 // moved Xinha.prototype.checkBackspace() to browser specific file 
    4125 // moved Xinha.prototype.dom_checkInsertP() to browser specific file 
    4126  
     4745/** Scrolls the editor iframe to a given element or to the cursor 
     4746 *  @param {DomNode} e optional The element to scroll to; if ommitted, element the element the cursor is in 
     4747 */ 
    41274748Xinha.prototype.scrollToElement = function(e) 
    41284749{ 
     
    41384759}; 
    41394760 
    4140 // retrieve the HTML 
     4761/** Get the edited HTML 
     4762 *   
     4763 *  @public 
     4764 *  @returns {String} HTML content 
     4765 */ 
     4766Xinha.prototype.getEditorContent = function() 
     4767{ 
     4768  return this.outwardHtml(this.getHTML()); 
     4769} 
     4770 
     4771/** Completely change the HTML inside the editor 
     4772 * 
     4773 *  @public 
     4774 *  @param {String} html new content 
     4775 */ 
     4776Xinha.prototype.setEditorContent = function(html) 
     4777{ 
     4778  this.setHTML(this.inwardHtml(html)); 
     4779} 
     4780 
     4781/** Get the raw edited HTML, should not be used without Xinha.prototype.outwardHtml() 
     4782 *   
     4783 *  @private 
     4784 *  @returns {String} HTML content 
     4785 */ 
    41414786Xinha.prototype.getHTML = function() 
    41424787{ 
     
    41474792      if ( !this.config.fullPage ) 
    41484793      { 
    4149         html = Xinha.getHTML(this._doc.body, false, this); 
     4794        html = Xinha.getHTML(this._doc.body, false, this).trim(); 
    41504795      } 
    41514796      else 
     
    41644809}; 
    41654810 
     4811/** Performs various transformations of the HTML used internally, complement to Xinha.prototype.inwardHtml()   
     4812 *  Plugins can provide their own, additional transformations by defining a plugin.prototype.outwardHtml() implematation, 
     4813 *  which is called by this function 
     4814 * 
     4815 *  @private 
     4816 *  @see Xinha#inwardHtml 
     4817 *  @param {String} html 
     4818 *  @returns {String} HTML content 
     4819 */ 
    41664820Xinha.prototype.outwardHtml = function(html) 
    41674821{ 
     
    41864840  html = html.replace(/(<[^>]*onmouseup=['"])if\(window\.top &amp;&amp; window\.top\.Xinha\)\{return false\}/gi,'$1'); 
    41874841 
     4842 
    41884843  // Figure out what our server name is, and how it's referenced 
    41894844  var serverBase = location.href.replace(/(https?:\/\/[^\/]*)\/.*/, '$1') + '/'; 
     
    42214876}; 
    42224877 
     4878/** Performs various transformations of the HTML to be edited  
     4879 *  Plugins can provide their own, additional transformations by defining a plugin.prototype.inwardHtml() implematation, 
     4880 *  which is called by this function 
     4881 *   
     4882 *  @private 
     4883 *  @see Xinha#outwardHtml 
     4884 *  @param {String} html   
     4885 *  @returns {String} transformed HTML 
     4886 */ 
    42234887Xinha.prototype.inwardHtml = function(html) 
    42244888 
     
    42614925  return html; 
    42624926}; 
    4263  
     4927/** Apply the replacements defined in Xinha.Config.specialReplacements 
     4928 *   
     4929 *  @private 
     4930 *  @see Xinha#inwardSpecialReplacements 
     4931 *  @param {String} html 
     4932 *  @returns {String}  transformed HTML 
     4933 */ 
    42644934Xinha.prototype.outwardSpecialReplacements = function(html) 
    42654935{ 
     
    42744944    }  
    42754945    // alert('out : ' + from + '=>' + to); 
    4276     var reg = new RegExp(from.replace(Xinha.RE_Specials, '\\$1'), 'g'); 
     4946    var reg = new RegExp(Xinha.escapeStringForRegExp(from), 'g'); 
    42774947    html = html.replace(reg, to.replace(/\$/g, '$$$$')); 
    42784948    //html = html.replace(from, to); 
     
    42804950  return html; 
    42814951}; 
    4282  
     4952/** Apply the replacements defined in Xinha.Config.specialReplacements 
     4953 *   
     4954 *  @private 
     4955 *  @see Xinha#outwardSpecialReplacements 
     4956 *  @param {String} html 
     4957 *  @returns {String}  transformed HTML 
     4958 */ 
    42834959Xinha.prototype.inwardSpecialReplacements = function(html) 
    42844960{ 
     
    42974973    // html = html.replace(reg, to); 
    42984974    // html = html.replace(from, to); 
    4299     var reg = new RegExp(from.replace(Xinha.RE_Specials, '\\$1'), 'g'); 
     4975    var reg = new RegExp(Xinha.escapeStringForRegExp(from), 'g'); 
    43004976    html = html.replace(reg, to.replace(/\$/g, '$$$$')); // IE uses doubled dollar signs to escape backrefs, also beware that IE also implements $& $_ and $' like perl. 
    43014977  } 
    43024978  return html; 
    43034979}; 
    4304  
     4980/** Transforms the paths in src & href attributes 
     4981 *   
     4982 *  @private 
     4983 *  @see Xinha.Config#expandRelativeUrl 
     4984 *  @see Xinha.Config#stripSelfNamedAnchors 
     4985 *  @see Xinha.Config#stripBaseHref 
     4986 *  @see Xinha.Config#baseHref 
     4987 *  @param {String} html  
     4988 *  @returns {String} transformed HTML 
     4989 */ 
    43054990Xinha.prototype.fixRelativeLinks = function(html) 
    43064991{ 
     
    43205005        base_m = b.match( relPath ); 
    43215006        absPath = url[2].replace(/(\.\.\/)*/,base_m[1]); 
    4322         html = html.replace( new RegExp(url[2].replace( Xinha.RE_Specials, '\\$1' ) ),absPath ); 
     5007        html = html.replace( new RegExp(Xinha.escapeStringForRegExp(url[2])),absPath ); 
    43235008      } 
    43245009    } 
     
    43275012  if ( typeof this.config.stripSelfNamedAnchors != 'undefined' && this.config.stripSelfNamedAnchors ) 
    43285013  { 
    4329     var stripRe = new RegExp(document.location.href.replace(/&/g,'&amp;').replace(Xinha.RE_Specials, '\\$1') + '(#[^\'" ]*)', 'g'); 
     5014    var stripRe = new RegExp(Xinha.escapeStringForRegExp(document.location.href.replace(/&/g,'&amp;')) + '(#[^\'" ]*)', 'g'); 
    43305015    html = html.replace(stripRe, '$1'); 
    43315016  } 
     
    43365021    if ( typeof this.config.baseHref != 'undefined' && this.config.baseHref !== null ) 
    43375022    { 
    4338       baseRe = new RegExp( "((href|src|background)=\")(" + this.config.baseHref.replace( Xinha.RE_Specials, '\\$1' ) + ")", 'g' ); 
     5023      baseRe = new RegExp( "((href|src|background)=\")(" + Xinha.escapeStringForRegExp(this.config.baseHref) + ")", 'g' ); 
    43395024    } 
    43405025    else 
    43415026    { 
    4342       baseRe = new RegExp( "((href|src|background)=\")(" + document.location.href.replace( /^(https?:\/\/[^\/]*)(.*)/, '$1' ).replace( Xinha.RE_Specials, '\\$1' ) + ")", 'g' ); 
     5027      baseRe = new RegExp( "((href|src|background)=\")(" +  Xinha.escapeStringForRegExp(document.location.href.replace( /^(https?:\/\/[^\/]*)(.*)/, '$1' )) + ")", 'g' ); 
    43435028    } 
    43445029 
     
    43495034}; 
    43505035 
    4351 // retrieve the HTML (fastest version, but uses innerHTML) 
     5036/** retrieve the HTML (fastest version, but uses innerHTML) 
     5037 *   
     5038 *  @private 
     5039 *  @returns {String} HTML content 
     5040 */ 
    43525041Xinha.prototype.getInnerHTML = function() 
    43535042{ 
     
    43815070}; 
    43825071 
    4383 // completely change the HTML inside 
     5072/** Completely change the HTML inside 
     5073 * 
     5074 *  @private 
     5075 *  @param {String} html new content, should have been run through inwardHtml() first 
     5076 */ 
    43845077Xinha.prototype.setHTML = function(html) 
    43855078{ 
     
    43955088}; 
    43965089 
    4397 // sets the given doctype (useful when config.fullPage is true) 
     5090/** sets the given doctype (useful only when config.fullPage is true) 
     5091 *   
     5092 *  @private 
     5093 *  @param {String} doctype 
     5094 */ 
    43985095Xinha.prototype.setDoctype = function(doctype) 
    43995096{ 
     
    44055102 ***************************************************/ 
    44065103 
    4407 // variable used to pass the object to the popup editor window. 
     5104/** Variable used to pass the object to the popup editor window. 
     5105 *  @FIXME: Is this in use? 
     5106 *  @deprecated  
     5107 *  @private 
     5108 *  @type {Object} 
     5109 */ 
    44085110Xinha._object = null; 
    44095111 
    4410 // function that returns a clone of the given object 
     5112/** function that returns a clone of the given object 
     5113 *   
     5114 *  @private 
     5115 *  @param {Object} obj 
     5116 *  @returns {Object} cloned object 
     5117 */ 
    44115118Xinha.cloneObject = function(obj) 
    44125119{ 
     
    44475154  return newObj; 
    44485155}; 
    4449  
    4450 // selection & ranges 
    4451  
    4452 // moved Xinha.prototype._getSelection() to browser specific file 
    4453 // moved Xinha.prototype._createRange()  to browser specific file 
    4454  
    4455 // event handling 
    44565156 
    44575157/** Event Flushing 
     
    44605160 *  onunload, it will remove any event listeners (that were added 
    44615161 *  through _addEvent(s)) and clear any DOM-0 events. 
    4462  */ 
    4463 Xinha._eventFlushers = []; 
     5162 *  @private 
     5163 * 
     5164 */ 
    44645165Xinha.flushEvents = function() 
    44655166{ 
     
    45115212  // alert('Flushed ' + x + ' events.'); 
    45125213}; 
     5214 /** Holds the events to be flushed 
     5215  * @type Array 
     5216  */ 
     5217Xinha._eventFlushers = []; 
    45135218 
    45145219if ( document.addEventListener ) 
    45155220{ 
     5221 /** adds an event listener for the specified element and event type 
     5222 *   
     5223 *  @public 
     5224 *  @see   Xinha#_addEvents 
     5225 *  @see   Xinha#addDom0Event 
     5226 *  @see   Xinha#prependDom0Event 
     5227 *  @param {DomNode}  el the DOM element the event should be attached to  
     5228 *  @param {String}   evname the name of the event to listen for (without leading "on") 
     5229 *  @param {function} func the function to be called when the event is fired 
     5230 */ 
    45165231  Xinha._addEvent = function(el, evname, func) 
    45175232  { 
     
    45195234    Xinha._eventFlushers.push([el, evname, func]); 
    45205235  }; 
     5236  
     5237 /** removes an event listener previously added 
     5238 *   
     5239 *  @public 
     5240 *  @see   Xinha#_removeEvents 
     5241 *  @param {DomNode}  el the DOM element the event should be removed from  
     5242 *  @param {String}   evname the name of the event the listener should be removed from (without leading "on") 
     5243 *  @param {function} func the function to be removed 
     5244 */ 
    45215245  Xinha._removeEvent = function(el, evname, func) 
    45225246  { 
    45235247    el.removeEventListener(evname, func, true); 
    45245248  }; 
     5249  
     5250 /** stops bubbling of the event, if no further listeners should be triggered 
     5251 *   
     5252 *  @public 
     5253 *  @param {event} ev the event to be stopped 
     5254 */ 
    45255255  Xinha._stopEvent = function(ev) 
    45265256  { 
     
    45295259  }; 
    45305260} 
     5261 /** same as above, for IE 
     5262 *   
     5263 */ 
    45315264else if ( document.attachEvent ) 
    45325265{ 
     
    45705303  }; 
    45715304} 
    4572  
     5305 /** add several events at once to one element 
     5306 *   
     5307 *  @public 
     5308 *  @see Xinha#_addEvent 
     5309 *  @param {DomNode}  el the DOM element the event should be attached to  
     5310 *  @param {Array}    evs the names of the event to listen for (without leading "on") 
     5311 *  @param {function} func the function to be called when the event is fired 
     5312 */ 
    45735313Xinha._addEvents = function(el, evs, func) 
    45745314{ 
     
    45785318  } 
    45795319}; 
    4580  
     5320 /** remove several events at once to from element 
     5321 *   
     5322 *  @public 
     5323 *  @see Xinha#_removeEvent 
     5324 *  @param {DomNode}  el the DOM element the events should be remove from 
     5325 *  @param {Array}    evs the names of the events the listener should be removed from (without leading "on") 
     5326 *  @param {function} func the function to be removed 
     5327 */ 
    45815328Xinha._removeEvents = function(el, evs, func) 
    45825329{ 
     
    46005347 * whether subsequent handlers will be triggered (ie that the event will 
    46015348 * continue or be canceled). 
    4602  * 
     5349 *   
     5350 *  @public 
     5351 *  @see Xinha#_addEvent 
     5352 *  @see Xinha#prependDom0Event 
     5353 *  @param {DomNode}  el the DOM element the event should be attached to  
     5354 *  @param {String}   ev the name of the event to listen for (without leading "on") 
     5355 *  @param {function} fn the function to be called when the event is fired 
    46035356 */ 
    46045357 
     
    46105363 
    46115364 
    4612 /** 
    4613  * See addDom0Event, the difference is that handlers registered using 
    4614  * prependDom0Event will be triggered before existing DOM-0 events of the 
    4615  * same name on the same element. 
     5365/** See addDom0Event, the difference is that handlers registered using 
     5366 *  prependDom0Event will be triggered before existing DOM-0 events of the 
     5367 *  same name on the same element. 
     5368 *   
     5369 *  @public 
     5370 *  @see Xinha#_addEvent 
     5371 *  @see Xinha#addDom0Event 
     5372 *  @param {DomNode}  the DOM element the event should be attached to  
     5373 *  @param {String}   the name of the event to listen for (without leading "on") 
     5374 *  @param {function} the function to be called when the event is fired 
    46165375 */ 
    46175376 
     
    46255384 * Prepares an element to receive more than one DOM-0 event handler 
    46265385 * when handlers are added via addDom0Event and prependDom0Event. 
     5386 * 
     5387 * @private 
    46275388 */ 
    46285389Xinha._prepareForDom0Events = function(el, ev) 
     
    46925453}; 
    46935454 
    4694 Xinha._removeClass = function(el, className) 
    4695 { 
    4696   if ( ! ( el && el.className ) ) 
    4697   { 
    4698     return; 
    4699   } 
    4700   var cls = el.className.split(" "); 
    4701   var ar = []; 
    4702   for ( var i = cls.length; i > 0; ) 
    4703   { 
    4704     if ( cls[--i] != className ) 
    4705     { 
    4706       ar[ar.length] = cls[i]; 
    4707     } 
    4708   } 
    4709   el.className = ar.join(" "); 
    4710 }; 
    4711  
    4712 Xinha._addClass = function(el, className) 
    4713 { 
    4714   // remove the class first, if already there 
    4715   Xinha._removeClass(el, className); 
    4716   el.className += " " + className; 
    4717 }; 
    4718  
    4719 Xinha._hasClass = function(el, className) 
    4720 { 
    4721   if ( ! ( el && el.className ) ) 
    4722   { 
    4723     return false; 
    4724   } 
    4725   var cls = el.className.split(" "); 
    4726   for ( var i = cls.length; i > 0; ) 
    4727   { 
    4728     if ( cls[--i] == className ) 
    4729     { 
    4730       return true; 
    4731     } 
    4732   } 
    4733   return false; 
    4734 }; 
    4735  
     5455/** List of tag names that are defined as block level elements in HTML 
     5456 *   
     5457 *  @private 
     5458 *  @see Xinha#isBlockElement 
     5459 *  @type {String} 
     5460 */ 
    47365461Xinha._blockTags = " body form textarea fieldset ul ol dl li div " + 
    47375462"p h1 h2 h3 h4 h5 h6 quote pre table thead " + 
    47385463"tbody tfoot tr td th iframe address blockquote "; 
     5464 
     5465/** Checks if one element is in the list of elements that are defined as block level elements in HTML 
     5466 *   
     5467 *  @param {DomNode}  el The DOM element to check 
     5468 *  @returns {Boolean} 
     5469 */ 
    47395470Xinha.isBlockElement = function(el) 
    47405471{ 
    47415472  return el && el.nodeType == 1 && (Xinha._blockTags.indexOf(" " + el.tagName.toLowerCase() + " ") != -1); 
    47425473}; 
    4743  
     5474/** List of tag names that are allowed to contain a paragraph 
     5475 *   
     5476 *  @private 
     5477 *  @see Xinha#isParaContainer 
     5478 *  @type {String} 
     5479 */ 
    47445480Xinha._paraContainerTags = " body td th caption fieldset div"; 
     5481/** Checks if one element is in the list of elements that are allowed to contain a paragraph in HTML 
     5482 *   
     5483 *  @param {DomNode}  el The DOM element to check 
     5484 *  @returns {Boolean} 
     5485 */ 
    47455486Xinha.isParaContainer = function(el) 
    47465487{ 
     
    47485489}; 
    47495490 
    4750 // These are all the tags for which the end tag is not optional or  
    4751 // forbidden, taken from the list at: 
    4752 //   http://www.w3.org/TR/REC-html40/index/elements.html 
     5491 
     5492/* * These are all the tags for which the end tag is not optional or  forbidden, taken from the list at: 
     5493 *   http: www.w3.org/TR/REC-html40/index/elements.html 
     5494 *   
     5495 *  @private 
     5496 *  @see Xinha#needsClosingTag 
     5497 *  @type {String} 
     5498 */ 
    47535499Xinha._closingTags = " a abbr acronym address applet b bdo big blockquote button caption center cite code del dfn dir div dl em fieldset font form frameset h1 h2 h3 h4 h5 h6 i iframe ins kbd label legend map menu noframes noscript object ol optgroup pre q s samp script select small span strike strong style sub sup table textarea title tt u ul var "; 
    47545500 
     5501/** Checks if one element is in the list of elements for which the end tag is not optional or  forbidden in HTML 
     5502 *   
     5503 *  @param {DomNode}  el The DOM element to check 
     5504 *  @returns {Boolean} 
     5505 */ 
    47555506Xinha.needsClosingTag = function(el) 
    47565507{ 
     
    47585509}; 
    47595510 
    4760 // performs HTML encoding of some given string 
     5511/** Performs HTML encoding of some given string (converts HTML special characters to entities) 
     5512 *   
     5513 *  @param {String}  str The unencoded input 
     5514 *  @returns {String} The encoded output 
     5515 */ 
    47615516Xinha.htmlEncode = function(str) 
    47625517{ 
     
    47765531}; 
    47775532 
    4778 // moved Xinha.getHTML() to getHTML.js  
     5533/** Strips host-part of URL which is added by browsers to links relative to server root 
     5534 *   
     5535 *  @param {String}  string  
     5536 *  @returns {String}  
     5537 */ 
    47795538Xinha.prototype.stripBaseURL = function(string) 
    47805539{ 
     
    47835542    return string; 
    47845543  } 
    4785   // strip host-part of URL which is added by MSIE to links relative to server root 
    47865544  var baseurl = this.config.baseHref.replace(/^(https?:\/\/[^\/]+)(.*)$/, '$1'); 
    47875545  var basere = new RegExp(baseurl); 
    47885546  return string.replace(basere, ""); 
    47895547}; 
    4790  
     5548/** Removes whitespace from beginning and end of a string 
     5549 *   
     5550 *  @returns {String}  
     5551 */ 
    47915552String.prototype.trim = function() 
    47925553{ 
     
    47945555}; 
    47955556 
    4796 // creates a rgb-style color from a number 
     5557/** Creates a rgb-style rgb(r,g,b) color from a (24bit) number 
     5558 *   
     5559 *  @param {Integer} 
     5560 *  @returns {String} rgb(r,g,b) color definition 
     5561 */ 
    47975562Xinha._makeColor = function(v) 
    47985563{ 
     
    48095574}; 
    48105575 
    4811 // returns hexadecimal color representation from a number or a rgb-style color. 
     5576/** Returns hexadecimal color representation from a number or a rgb-style color. 
     5577 *   
     5578 *  @param {String|Integer} v rgb(r,g,b) or 24bit color definition 
     5579 *  @returns {String} #RRGGBB color definition 
     5580 */ 
    48125581Xinha._colorToRgb = function(v) 
    48135582{ 
     
    48595628}; 
    48605629 
    4861 // modal dialogs for Mozilla (for IE we're using the showModalDialog() call). 
    4862  
    4863 // receives an URL to the popup dialog and a function that receives one value; 
    4864 // this function will get called after the dialog is closed, with the return 
    4865 // value of the dialog. 
     5630/** Modal popup dialogs 
     5631 *   
     5632 *  @param {String} url URL to the popup dialog 
     5633 *  @param {Function} action A function that receives one value; this function will get called  
     5634 *                    after the dialog is closed, with the return value of the dialog. 
     5635 *  @param {Mixed} init A variable that is passed to the popup window to pass arbitrary data 
     5636 */ 
    48665637Xinha.prototype._popupDialog = function(url, action, init) 
    48675638{ 
     
    48695640}; 
    48705641 
    4871 // paths 
    4872  
     5642/** Creates a path in the form _editor_url + "plugins/" + plugin + "/img/" + file 
     5643 *   
     5644 *  @deprecated 
     5645 *  @param {String} file Name of the image 
     5646 *  @param {String} plugin optional If omitted, simply _editor_url + file is returned  
     5647 *  @returns {String} 
     5648 */ 
    48735649Xinha.prototype.imgURL = function(file, plugin) 
    48745650{ 
     
    48825658  } 
    48835659}; 
    4884  
     5660/** Creates a path 
     5661 *   
     5662 *  @deprecated 
     5663 *  @param {String} file Name of the popup 
     5664 *  @returns {String} 
     5665 */ 
    48855666Xinha.prototype.popupURL = function(file) 
    48865667{ 
     
    49075688}; 
    49085689 
    4909 /** 
    4910  * FIX: Internet Explorer returns an item having the _name_ equal to the given 
     5690/** FIX: Internet Explorer returns an item having the _name_ equal to the given 
    49115691 * id, even if it's not having any id.  This way it can return a different form 
    4912  * field even if it's not a textarea.  This workarounds the problem by 
     5692 * field, even if it's not a textarea.  This workarounds the problem by 
    49135693 * specifically looking to search only elements having a certain tag name. 
     5694 * @param {String} tag The tag name to limit the return to 
     5695 * @param {String} id 
     5696 * @returns {DomNode} 
    49145697 */ 
    49155698Xinha.getElementById = function(tag, id) 
     
    49275710 
    49285711 
    4929 /** Use some CSS trickery to toggle borders on tables */ 
     5712/** Use some CSS trickery to toggle borders on tables  
     5713 *      @returns {Boolean} always true 
     5714 */ 
    49305715 
    49315716Xinha.prototype._toggleBorders = function() 
     
    49575742  return true; 
    49585743}; 
    4959  
     5744/** Adds styles for internal use to the edited document 
     5745 *   
     5746 *  @private 
     5747 *  @see Xinha#stripCoreCSS 
     5748 *  @param {String} html optional   
     5749 *  @returns {String} html HTML with added styles or only styles if html omitted 
     5750 */ 
    49605751Xinha.addCoreCSS = function(html) 
    49615752{ 
     
    49805771    } 
    49815772} 
    4982  
     5773/** Remove internal styles 
     5774 *   
     5775 *  @private 
     5776 *  @see Xinha#addCoreCSS 
     5777 *  @param {String} html  
     5778 *  @returns {String}  
     5779 */ 
    49835780Xinha.stripCoreCSS = function(html) 
    49845781{ 
    49855782  return html.replace(/<style[^>]+title="Xinha Internal CSS"(.|\n)*?<\/style>/i, '');  
    49865783} 
    4987  
     5784/** Removes one CSS class (that is one of possible more parts  
     5785 *   separated by spaces) from a given element 
     5786 *   
     5787 *  @see Xinha#_removeClasses 
     5788 *  @param {DomNode}  el The DOM element the class will be removed from 
     5789 *  @param {String}   className The class to be removed 
     5790 */ 
     5791Xinha._removeClass = function(el, className) 
     5792{ 
     5793  if ( ! ( el && el.className ) ) 
     5794  { 
     5795    return; 
     5796  } 
     5797  var cls = el.className.split(" "); 
     5798  var ar = []; 
     5799  for ( var i = cls.length; i > 0; ) 
     5800  { 
     5801    if ( cls[--i] != className ) 
     5802    { 
     5803      ar[ar.length] = cls[i]; 
     5804    } 
     5805  } 
     5806  el.className = ar.join(" "); 
     5807}; 
     5808/** Adds one CSS class  to a given element (that is, it expands its className property by the given string, 
     5809 *  separated by a space) 
     5810 *   
     5811 *  @see Xinha#addClasses 
     5812 *  @param {DomNode}  el The DOM element the class will be added to 
     5813 *  @param {String}   className The class to be added 
     5814 */ 
     5815Xinha._addClass = function(el, className) 
     5816{ 
     5817  // remove the class first, if already there 
     5818  Xinha._removeClass(el, className); 
     5819  el.className += " " + className; 
     5820}; 
     5821 
     5822/** Adds CSS classes  to a given element (that is, it expands its className property by the given string, 
     5823 *  separated by a space, thereby checking that no class is doubly added) 
     5824 *   
     5825 *  @see Xinha#addClass 
     5826 *  @param {DomNode}  el The DOM element the classes will be added to 
     5827 *  @param {String}   classes The classes to be added 
     5828 */ 
    49885829Xinha.addClasses = function(el, classes) 
    49895830{ 
     
    50115852}; 
    50125853 
     5854/** Removes CSS classes (that is one or more of possibly several parts  
     5855 *   separated by spaces) from a given element 
     5856 *   
     5857 *  @see Xinha#_removeClasses 
     5858 *  @param {DomNode}  el The DOM element the class will be removed from 
     5859 *  @param {String}   className The class to be removed 
     5860 */ 
    50135861Xinha.removeClasses = function(el, classes) 
    50145862{ 
     
    50355883}; 
    50365884 
    5037 /** Alias these for convenience */ 
     5885/** Alias of Xinha._addClass() 
     5886 *  @see Xinha#_addClass 
     5887 */ 
    50385888Xinha.addClass       = Xinha._addClass; 
     5889/** Alias of Xinha.Xinha._removeClass() 
     5890 *  @see Xinha#_removeClass 
     5891 */ 
    50395892Xinha.removeClass    = Xinha._removeClass; 
     5893/** Alias of Xinha.addClasses() 
     5894 *  @see Xinha#addClasses 
     5895 */ 
    50405896Xinha._addClasses    = Xinha.addClasses; 
     5897/** Alias of Xinha.removeClasses() 
     5898 *  @see Xinha#removeClasses 
     5899 */ 
    50415900Xinha._removeClasses = Xinha.removeClasses; 
    50425901 
    5043 /** Use XML HTTPRequest to post some data back to the server and do something 
    5044  * with the response (asyncronously!), this is used by such things as the tidy functions 
     5902/** Checks if one element has set the given className 
     5903 *   
     5904 *  @param {DomNode}  el The DOM element to check 
     5905 *  @param {String}   className The class to be looked for 
     5906 *  @returns {Boolean} 
     5907 */ 
     5908Xinha._hasClass = function(el, className) 
     5909{ 
     5910  if ( ! ( el && el.className ) ) 
     5911  { 
     5912    return false; 
     5913  } 
     5914  var cls = el.className.split(" "); 
     5915  for ( var i = cls.length; i > 0; ) 
     5916  { 
     5917    if ( cls[--i] == className ) 
     5918    { 
     5919      return true; 
     5920    } 
     5921  } 
     5922  return false; 
     5923}; 
     5924 
     5925/** Use XMLHTTPRequest to post some data back to the server and do something 
     5926 *  with the response (asyncronously!), this is used by such things as the tidy functions 
     5927 *  @param {String} url The address for the HTTPRequest 
     5928 *  @param {Object} data The data to be passed to the server like {name:"value"} 
     5929 *  @param {Function} handler A function that is called when an answer is received from the server with the responseText  
     5930 *                             as argument                              
    50455931 */ 
    50465932Xinha._postback = function(url, data, handler) 
     
    50885974}; 
    50895975 
     5976/** Use XMLHTTPRequest to receive some data from the server and do something 
     5977 *  with the it (asyncronously!) 
     5978 *  @param {String} url The address for the HTTPRequest 
     5979 *  @param {Function} handler A function that is called when an answer is received from the server with the responseText  
     5980 *                             as argument                              
     5981 */ 
    50905982Xinha._getback = function(url, handler) 
    50915983{ 
     
    51126004  req.send(null); 
    51136005}; 
    5114  
     6006/** Use XMLHTTPRequest to receive some data from the server syncronously 
     6007 *  @param {String} url The address for the HTTPRequest 
     6008 */ 
    51156009Xinha._geturlcontent = function(url) 
    51166010{ 
     
    51326026}; 
    51336027 
    5134 /** 
    5135  * Unless somebody already has, make a little function to debug things 
    5136  */ 
     6028// Unless somebody already has, make a little function to debug things 
     6029 
    51376030if ( typeof dump == 'undefined' ) 
    51386031{ 
     
    51486041  } 
    51496042} 
    5150  
     6043if ( !Array.prototype.contains ) 
     6044{ 
     6045  /** Walks through an array and checks if the specified item exists in it 
     6046  * @param {String} needle The string to search for 
     6047  * @returns {Boolean} True if item found, false otherwise  
     6048  */ 
     6049  Array.prototype.contains = function(needle) 
     6050  { 
     6051    var haystack = this; 
     6052    for ( var i = 0; i < haystack.length; i++ ) 
     6053    { 
     6054      if ( needle == haystack[i] ) 
     6055      { 
     6056        return true; 
     6057      } 
     6058    } 
     6059    return false; 
     6060  }; 
     6061} 
     6062 
     6063if ( !Array.prototype.indexOf ) 
     6064{ 
     6065  /** Walks through an array and, if the specified item exists in it, returns the position 
     6066  * @param {String} needle The string to search for 
     6067  * @returns {Integer|null} Index position if item found, null otherwise  
     6068  */ 
     6069  Array.prototype.indexOf = function(needle) 
     6070  { 
     6071    var haystack = this; 
     6072    for ( var i = 0; i < haystack.length; i++ ) 
     6073    { 
     6074      if ( needle == haystack[i] ) 
     6075      { 
     6076        return i; 
     6077      } 
     6078    } 
     6079    return null; 
     6080  }; 
     6081} 
     6082if ( !Array.prototype.append ) 
     6083{ 
     6084  /** Adds an item to an array 
     6085   * @param {Mixed} a Item to add 
     6086   * @returns {Array} The array including the newly added item 
     6087   */ 
     6088  Array.prototype.append  = function(a) 
     6089  { 
     6090    for ( var i = 0; i < a.length; i++ ) 
     6091    { 
     6092      this.push(a[i]); 
     6093    } 
     6094    return this; 
     6095  }; 
     6096} 
     6097/** Returns true if all elements of <em>a2</em> are also contained in <em>a1</em> (at least I think this is what it does) 
     6098* @param {Array} a1 
     6099* @param {Array} a2 
     6100* @returns {Boolean} 
     6101*/ 
    51516102Xinha.arrayContainsArray = function(a1, a2) 
    51526103{ 
     
    51716122  return all_found; 
    51726123}; 
    5173  
     6124/** Walks through an array and applies a filter function to each item 
     6125* @param {Array} a1 The array to filter 
     6126* @param {Function} filterfn If this function returns true, the item is added to the new array 
     6127* @returns {Array} Filtered array 
     6128*/ 
    51746129Xinha.arrayFilter = function(a1, filterfn) 
    51756130{ 
     
    51846139  return new_a; 
    51856140}; 
    5186  
     6141/** Converts a Collection object to an array  
     6142* @param {Collection} collection The array to filter 
     6143* @returns {Array} Array containing the item of collection 
     6144*/ 
     6145Xinha.collectionToArray = function(collection) 
     6146{ 
     6147  var array = [ ]; 
     6148  for ( var i = 0; i < collection.length; i++ ) 
     6149  { 
     6150    array.push(collection.item(i)); 
     6151  } 
     6152  return array; 
     6153}; 
     6154 
     6155/** Index for Xinha.uniq function  
     6156*       @private 
     6157*/ 
    51876158Xinha.uniq_count = 0; 
     6159/** Returns a string that is unique on the page 
     6160*       @param {String} prefix This string is prefixed to a running number 
     6161*   @returns {String} 
     6162*/ 
    51886163Xinha.uniq = function(prefix) 
    51896164{ 
     
    51916166}; 
    51926167 
    5193 /** New language handling functions **/ 
    5194  
     6168// New language handling functions 
    51956169 
    51966170/** Load a language file. 
    51976171 *  This function should not be used directly, Xinha._lc will use it when necessary. 
    5198  * @param context Case sensitive context name, eg 'Xinha', 'TableOperations', ... 
     6172 *  @private 
     6173 *  @param {String} context Case sensitive context name, eg 'Xinha', 'TableOperations', ... 
     6174 *  @returns {Object} 
    51996175 */ 
    52006176Xinha._loadlang = function(context,url) 
     
    52186194    else 
    52196195    { 
    5220       Xinha.setLoadingMessage("Language is loaded"); 
     6196      Xinha.setLoadingMessage("Loading language"); 
    52216197      url = _editor_url+"lang/"+_editor_lang+".js"; 
    52226198    } 
     
    52456221 
    52466222/** Return a localised string. 
    5247  * @param string    English language string. It can also contain variables in the form "Some text with $variable=replaced text$".  
     6223 * @param {String} string English language string. It can also contain variables in the form "Some text with $variable=replaced text$".  
    52486224 *                  This replaces $variable in "Some text with $variable" with "replaced text" 
    5249  * @param context   Case sensitive context name, eg 'Xinha' (default), 'TableOperations'... 
    5250  * @param replace   Replace $variables in String, eg {foo: 'replaceText'} ($foo in string will be replaced) 
     6225 * @param {String} context   Case sensitive context name, eg 'Xinha' (default), 'TableOperations'... 
     6226 * @param {Object} replace   Replace $variables in String, eg {foo: 'replaceText'} ($foo in string will be replaced by replaceText) 
    52516227 */ 
    52526228Xinha._lc = function(string, context, replace) 
     
    53536329  return ret; 
    53546330}; 
    5355  
     6331/** Walks through the children of a given element and checks if any of the are visible (= not display:none) 
     6332 * @param {DomNode} el  
     6333 * @returns {Boolean}  
     6334 */ 
    53566335Xinha.hasDisplayedChildren = function(el) 
    53576336{ 
     
    53706349}; 
    53716350 
    5372 /** 
    5373  * Load a javascript file by inserting it in the HEAD tag and eventually call a function when loaded 
     6351/** Load a javascript file by inserting it in the HEAD tag and eventually call a function when loaded 
    53746352 * 
    5375  * Note that this method cannot be abstracted into browser specific files 
     6353 *  Note that this method cannot be abstracted into browser specific files 
    53766354 *  because this method LOADS the browser specific files.  Hopefully it should work for most 
    53776355 *  browsers as it is. 
    53786356 * 
    5379  * @param {string} U (Url)      Source url of the file to load 
    5380  * @param {object} C {Callback} Callback function to launch once ready (optional) 
    5381  * @param {object} O (scOpe)    Application scope for the callback function (optional) 
    5382  * @param {object} B (Bonus}    Arbitrary object send as a param to the callback function (optional) 
    5383  * @public 
    5384  *  
    5385  */ 
    5386   
     6357 * @param {String} url               Source url of the file to load 
     6358 * @param {Object} callback optional Callback function to launch once ready  
     6359 * @param {Object} scope    optional Application scope for the callback function 
     6360 * @param {Object} bonus    optional Arbitrary object send as a param to the callback function 
     6361 */ 
    53876362Xinha._loadback = function(url, callback, scope, bonus) 
    53886363 
     
    54136388}; 
    54146389 
    5415 Xinha.collectionToArray = function(collection) 
    5416 { 
    5417   var array = [ ]; 
    5418   for ( var i = 0; i < collection.length; i++ ) 
    5419   { 
    5420     array.push(collection.item(i)); 
    5421   } 
    5422   return array; 
    5423 }; 
    5424  
    5425 if ( !Array.prototype.append ) 
    5426 { 
    5427   Array.prototype.append  = function(a) 
    5428   { 
    5429     for ( var i = 0; i < a.length; i++ ) 
    5430     { 
    5431       this.push(a[i]); 
    5432     } 
    5433     return this; 
    5434   }; 
    5435 } 
    5436  
     6390/** Xinha's main loading function (see NewbieGuide) 
     6391 * @param {Array} editor_names 
     6392 * @param {Xinha.Config} default_config 
     6393 * @param {Array} plugin_names 
     6394 * @returns {Object} An object that contains references to all created editors indexed by the IDs of the textareas  
     6395 */ 
    54376396Xinha.makeEditors = function(editor_names, default_config, plugin_names) 
    54386397{ 
     
    54586417  return editors; 
    54596418}; 
    5460  
     6419/** Another main loading function (see NewbieGuide) 
     6420 * @param {Object} editors As returned by Xinha.makeEditors() 
     6421 */ 
    54616422Xinha.startEditors = function(editors) 
    54626423{ 
     
    54716432  } 
    54726433}; 
    5473  
     6434/** Registers the loaded plugins with the editor 
     6435 * @private 
     6436 * @param {Array} plugin_names 
     6437 */ 
    54746438Xinha.prototype.registerPlugins = function(plugin_names) 
    54756439{ 
     
    54816445    { 
    54826446      this.setLoadingMessage(Xinha._lc('Register plugin $plugin', 'Xinha', {'plugin': plugin_names[i]})); 
    5483       this.registerPlugin(eval(plugin_names[i])); 
    5484     } 
    5485   } 
    5486 }; 
    5487  
    5488 /** Utility function to base64_encode some arbitrary data, uses the builtin btoa() if it exists (Moz) */ 
    5489  
     6447      this.registerPlugin(plugin_names[i]); 
     6448    } 
     6449  } 
     6450}; 
     6451 
     6452/** Utility function to base64_encode some arbitrary data, uses the builtin btoa() if it exists (Moz)  
     6453*  @param {String} input 
     6454*  @returns {String} 
     6455*/ 
    54906456Xinha.base64_encode = function(input) 
    54916457{ 
     
    55226488}; 
    55236489 
    5524 /** Utility function to base64_decode some arbitrary data, uses the builtin atob() if it exists (Moz) */ 
    5525  
     6490/** Utility function to base64_decode some arbitrary data, uses the builtin atob() if it exists (Moz) 
     6491 *  @param {String} input 
     6492 *  @returns {String} 
     6493 */ 
    55266494Xinha.base64_decode = function(input) 
    55276495{ 
     
    55606528  return output; 
    55616529}; 
    5562  
     6530/** Removes a node from the DOM 
     6531 *  @param {DomNode} el The element to be removed 
     6532 *  @returns {DomNode} The removed element 
     6533 */ 
    55636534Xinha.removeFromParent = function(el) 
    55646535{ 
     
    55716542  return el; 
    55726543}; 
    5573  
     6544/** Checks if some element has a parent node 
     6545 *  @param {DomNode} el  
     6546 *  @returns {Boolean} 
     6547 */ 
    55746548Xinha.hasParentNode = function(el) 
    55756549{ 
     
    55886562}; 
    55896563 
    5590 // moved Xinha.getOuterHTML() to browser specific file 
    5591  
    5592 // detect the size of visible area 
    5593 // when calling from a popup window, call Xinha.viewportSize(window) to get the size of the popup 
     6564/** Detect the size of visible area 
     6565 *  @param {Window} scope optional When calling from a popup window, pass its window object to get the values of the popup 
     6566 *  @returns {Object} Object with Integer properties x and y 
     6567 */ 
    55946568Xinha.viewportSize = function(scope) 
    55956569{ 
     
    56146588  return {'x':x,'y':y}; 
    56156589}; 
    5616  
     6590/** Detect the size of the whole document 
     6591 *  @param {Window} scope optional When calling from a popup window, pass its window object to get the values of the popup 
     6592 *  @returns {Object} Object with Integer properties x and y 
     6593 */ 
    56176594Xinha.pageSize = function(scope) 
    56186595{ 
     
    56356612  return {'x':x,'y':y}; 
    56366613}; 
    5637  
     6614/** Detect the current scroll position 
     6615 *  @param {Window} scope optional When calling from a popup window, pass its window object to get the values of the popup 
     6616 *  @returns {Object} Object with Integer properties x and y 
     6617 */ 
    56386618Xinha.prototype.scrollPos = function(scope) 
    56396619{ 
     
    56596639}; 
    56606640 
    5661 /**  
    5662  *  Calculate the top and left pixel position of an element in the DOM. 
    5663  * 
    5664  *  @param   element HTML Element DOM Node 
    5665  *  @returns Object with integer properties top and left 
     6641/** Calculate the top and left pixel position of an element in the DOM. 
     6642 *  @param  {DomNode} element HTML Element 
     6643 *  @returns {Object} Object with Integer properties top and left 
    56666644 */ 
    56676645  
     
    56816659  return { top:curtop, left:curleft }; 
    56826660} 
    5683  
    5684 // find X position of an element 
     6661/** Find left pixel position of an element in the DOM. 
     6662 *  @param  {DomNode} element HTML Element 
     6663 *  @returns {Integer}  
     6664 */ 
    56856665Xinha.findPosX = function(obj) 
    56866666{ 
     
    56966676  return curleft; 
    56976677}; 
    5698  
    5699 // find Y position of an element 
     6678/** Find top pixel position of an element in the DOM. 
     6679 *  @param  {DomNode} element HTML Element 
     6680 *  @returns {Integer}  
     6681 */ 
    57006682Xinha.findPosY = function(obj) 
    57016683{ 
     
    57386720  loading_message.className = "loading"; 
    57396721   
    5740   loading_message.style.left = (Xinha.findPosX(textarea) + textarea.offsetWidth / 2) - 106 +  'px'; 
     6722  loading_message.style.left = Xinha.findPosX(textarea) +  'px'; 
    57416723  loading_message.style.top = (Xinha.findPosY(textarea) + textarea.offsetHeight / 2) - 50 +  'px'; 
     6724  loading_message.style.width =  textarea.offsetWidth +  'px'; 
     6725   
    57426726  // main static message 
    57436727  var loading_main = document.createElement("div"); 
     
    57496733  loading_sub.className = "loading_sub"; 
    57506734  loading_sub.id = "loading_sub_" + textarea.id; 
    5751   text = text ? text : Xinha._lc("Constructing main object"); 
     6735  text = text ? text : Xinha._lc("Constructing object"); 
    57526736  loading_sub.appendChild(document.createTextNode(text)); 
    57536737  loading_message.appendChild(loading_main); 
     
    57626746}; 
    57636747 
    5764 Xinha.prototype.setLoadingMessage = function(string) 
     6748Xinha.prototype.setLoadingMessage = function(subMessage, mainMessage) 
    57656749{ 
    57666750  if ( !document.getElementById("loading_sub_" + this._textArea.id) ) 
     
    57686752    return; 
    57696753  } 
    5770   document.getElementById("loading_main_" + this._textArea.id).innerHTML = Xinha._lc("Loading in progress. Please wait!"); 
    5771   document.getElementById("loading_sub_" + this._textArea.id).innerHTML = string; 
     6754  document.getElementById("loading_main_" + this._textArea.id).innerHTML = mainMessage ? mainMessage : Xinha._lc("Loading in progress. Please wait!"); 
     6755  document.getElementById("loading_sub_" + this._textArea.id).innerHTML = subMessage; 
    57726756}; 
    57736757 
     
    57996783}; 
    58006784 
     6785/** List of objects that have to be trated on page unload in order to work around the broken  
     6786 * Garbage Collector in IE 
     6787 * @private 
     6788 * @see Xinha#freeLater 
     6789 * @see Xinha#free 
     6790 * @see Xinha#collectGarbageForIE 
     6791 */ 
    58016792Xinha.toFree = []; 
     6793/** Adds objects to Xinha.toFree  
     6794 * @param {Object} object The object to free memory 
     6795 * @param (String} prop optional  The property to release 
     6796 * @private 
     6797 * @see Xinha#toFree 
     6798 * @see Xinha#free 
     6799 * @see Xinha#collectGarbageForIE 
     6800 */ 
    58026801Xinha.freeLater = function(obj,prop) 
    58036802{ 
     
    58056804}; 
    58066805 
    5807 /** 
    5808  * Release memory properties from object 
    5809  * @param {object} object The object to free memory 
    5810  * @param (string} prop   The property to release (optional) 
     6806/** Release memory properties from object 
     6807 * @param {Object} object The object to free memory 
     6808 * @param (String} prop optional The property to release 
    58116809 * @private 
     6810 * @see Xinha#collectGarbageForIE 
     6811 * @see Xinha#free 
    58126812 */ 
    58136813Xinha.free = function(obj, prop) 
     
    58276827 
    58286828/** IE's Garbage Collector is broken very badly.  We will do our best to  
    5829  *   do it's job for it, but we can't be perfect. 
     6829 *   do it's job for it, but we can't be perfect. Takes all objects from Xinha.free and releases sets the null 
     6830 * @private 
     6831 * @see Xinha#toFree 
     6832 * @see Xinha#free 
    58306833 */ 
    58316834 
    58326835Xinha.collectGarbageForIE = function()  
    5833 { 
     6836{   
    58346837  Xinha.flushEvents();    
    58356838  for ( var x = 0; x < Xinha.toFree.length; x++ ) 
     
    58466849 
    58476850/** Insert a node at the current selection point.  
    5848  * @param toBeInserted DomNode 
     6851 * @param {DomNode} toBeInserted 
    58496852 */ 
    58506853 
     
    58526855 
    58536856/** Get the parent element of the supplied or current selection.  
    5854  *  @param   sel optional selection as returned by getSelection 
    5855  *  @returns DomNode 
     6857 *  @param {Selection} sel optional selection as returned by getSelection 
     6858 *  @returns {DomNode} 
    58566859 */ 
    58576860   
     
    58636866 * at the bottom of the editor, or a "control" (eg image) 
    58646867 * 
    5865  * @returns null | DomNode 
     6868 * @returns {DomNode|null} 
    58666869 */ 
    58676870  
     
    58706873/**  
    58716874 * Determines if the given selection is empty (collapsed). 
    5872  * @param selection Selection object as returned by getSelection 
    5873  * @returns true|false 
     6875 * @param {Selection} sel Selection object as returned by getSelection 
     6876 * @returns {Boolean} 
    58746877 */ 
    58756878  
    58766879Xinha.prototype.selectionEmpty        = function(sel) { Xinha.notImplemented("selectionEmpty"); } 
     6880/**  
     6881 * Returns a range object to be stored  
     6882 * and later restored with Xinha.prototype.restoreSelection() 
     6883 * @returns {Range} 
     6884 */ 
     6885 
     6886Xinha.prototype.saveSelection = function() { Xinha.notImplemented("saveSelection"); } 
     6887 
     6888/** Restores a selection previously stored 
     6889 * @param {Range} savedSelection Range object as returned by Xinha.prototype.restoreSelection() 
     6890 */ 
     6891Xinha.prototype.restoreSelection = function(savedSelection)  { Xinha.notImplemented("restoreSelection"); } 
    58776892 
    58786893/** 
     
    58806895 * the node itself is selected for manipulation. 
    58816896 * 
    5882  * @param node DomNode  
    5883  * @param pos  Set to a numeric position inside the node to collapse the cursor here if possible.  
    5884  */ 
    5885   
     6897 * @param {DomNode} node  
     6898 * @param {Integer} pos  Set to a numeric position inside the node to collapse the cursor here if possible.  
     6899 */ 
    58866900Xinha.prototype.selectNodeContents    = function(node,pos) { Xinha.notImplemented("selectNodeContents"); } 
    58876901 
    58886902/** Insert HTML at the current position, deleting the selection if any.  
    58896903 *   
    5890  *  @param html string 
     6904 *  @param {String} html 
    58916905 */ 
    58926906  
     
    58956909/** Get the HTML of the current selection.  HTML returned has not been passed through outwardHTML. 
    58966910 * 
    5897  * @returns string 
     6911 * @returns {String} 
    58986912 */ 
    58996913Xinha.prototype.getSelectedHTML       = function() { Xinha.notImplemented("getSelectedHTML"); } 
     
    59016915/** Get a Selection object of the current selection.  Note that selection objects are browser specific. 
    59026916 * 
    5903  * @returns Selection 
     6917 * @returns {Selection} 
    59046918 */ 
    59056919  
     
    59076921 
    59086922/** Create a Range object from the given selection.  Note that range objects are browser specific. 
    5909  * 
    5910  *  @param sel Selection object (see getSelection) 
    5911  *  @returns Range 
    5912  */ 
    5913   
     6923 *  @see Xinha#getSelection 
     6924 *  @param {Selection} sel Selection object  
     6925 *  @returns {Range} 
     6926 */ 
    59146927Xinha.prototype.createRange           = function(sel) { Xinha.notImplemented("createRange"); } 
    59156928 
    59166929/** Determine if the given event object is a keydown/press event. 
    59176930 * 
    5918  *  @param event Event  
    5919  *  @returns true|false 
     6931 *  @param {Event} event  
     6932 *  @returns {Boolean} 
    59206933 */ 
    59216934  
     
    59256938 *  which for Xinha is a shortcut.  Note that CTRL-ALT-<key> is not a shortcut. 
    59266939 * 
    5927  *  @param    keyEvent 
    5928  *  @returns  true|false 
     6940 *  @param    {Event} keyEvent 
     6941 *  @returns  {Boolean} 
    59296942 */ 
    59306943  
     
    59426955 *  this method will return 'a', press SHIFT-a and it will return 'A'. 
    59436956 *  
    5944  *  @param   keyEvent 
    5945  *  @returns string 
     6957 *  @param   {Event} keyEvent 
     6958 *  @returns {String} 
    59466959 */ 
    59476960                                    
     
    59506963/** Return the HTML string of the given Element, including the Element. 
    59516964 *  
    5952  * @param element HTML Element DomNode 
    5953  * @returns string 
     6965 * @param {DomNode} element HTML Element 
     6966 * @returns {String} 
    59546967 */ 
    59556968  
     
    59586971/** Get a new XMLHTTPRequest Object ready to be used.  
    59596972 * 
    5960  * @returns object XMLHTTPRequest  
     6973 * @returns {XMLHTTPRequest} 
    59616974 */ 
    59626975 
     
    59816994  
    59826995// Compatability - all these names are deprecated and will be removed in a future version 
     6996/** Alias of activeElement() 
     6997 * @see Xinha#activeElement 
     6998 * @deprecated 
     6999 * @returns {DomNode|null} 
     7000 */ 
    59837001Xinha.prototype._activeElement  = function(sel) { return this.activeElement(sel); } 
     7002/** Alias of selectionEmpty() 
     7003 * @see Xinha#selectionEmpty 
     7004 * @deprecated 
     7005 * @param {Selection} sel Selection object as returned by getSelection 
     7006 * @returns {Boolean} 
     7007 */ 
    59847008Xinha.prototype._selectionEmpty = function(sel) { return this.selectionEmpty(sel); } 
     7009/** Alias of getSelection() 
     7010 * @see Xinha#getSelection 
     7011 * @deprecated 
     7012 * @returns {Selection} 
     7013 */ 
    59857014Xinha.prototype._getSelection   = function() { return this.getSelection(); } 
     7015/** Alias of createRange() 
     7016 * @see Xinha#createRange 
     7017 * @deprecated 
     7018 * @param {Selection} sel Selection object 
     7019 * @returns {Range} 
     7020 */ 
    59867021Xinha.prototype._createRange    = function(sel) { return this.createRange(sel); } 
    59877022HTMLArea = Xinha; 
    59887023 
    59897024Xinha.init(); 
    5990 Xinha.addDom0Event(window,'unload',Xinha.collectGarbageForIE); 
    5991  
     7025 
     7026if (Xinha.is_ie) 
     7027{ 
     7028  Xinha.addDom0Event(window,'unload',Xinha.collectGarbageForIE); 
     7029} 
    59927030Xinha.notImplemented = function(methodName)  
    59937031{ 
  • branches/ray/contrib/compress.php

    r761 r862  
    11<? 
    2 die("Run this script to batch-compress the current Xinha snapshot. To run the script, open the file and uncomment the die() command"); 
     2die("Run this script to batch-compress the current Xinha snapshot. To run the script, open the file and comment out the die() command"); 
    33 
    44error_reporting(E_ALL); 
     
    5656        copy($file,$file."_uncompr.js"); 
    5757        exec("java -jar ${cwd}/dojo_js_compressor.jar -c ${file}_uncompr.js > $file 2>&1"); 
    58         unlink($file."_uncompr.js"); 
     58        if (preg_match('/js: ".*?", line \d+:/',file_get_contents($file))) 
     59        { 
     60                unlink($file); 
     61                rename($file."_uncompr.js",$file); 
     62        } 
     63        else 
     64        { 
     65                unlink($file."_uncompr.js"); 
     66        } 
    5967} 
    6068print "Operation complete." 
  • branches/ray/lang/b5.js

    r60 r862  
    3030  "Toggle HTML Source": "切換HTML原始碌", 
    3131  "Enlarge Editor": "攟倧", 
    32   "About this editor": "關斌 HTMLArea", 
     32  "About this editor": "關斌 Xinha", 
    3333  "Help using editor": "說明", 
    3434  "Current style": "字體䟋子" 
  • branches/ray/lang/ch.js

    r104 r862  
    3333  "Toggle HTML Source": "切換HTML原始碌", 
    3434  "Enlarge Editor": "䌞出線茯系統", 
    35   "About this editor": "關斌 HTMLArea", 
     35  "About this editor": "關斌 Xinha", 
    3636  "Help using editor": "說明", 
    3737  "Current style": "字體䟋子", 
  • branches/ray/lang/de.js

    r761 r862  
    148148 
    149149  // Loading messages 
    150   "Loading in progress. Please wait !": "Editor wird geladen. Bitte warten !", 
    151   "Constructing main object": "Hauptteil wird erzeugt", 
    152   "Create Toolbar": "Bearbeitungswerkzeuge werden angelegt", 
    153   "Register panel right": "Erzeugt rechte Leiste", 
    154   "Register panel left": "Erzeugt linke Leiste", 
    155   "Register panel top": "Erzeugt obere Leiste", 
    156   "Register panel bottom": "Erzeugt untere Leiste", 
     150  "Loading in progress. Please wait!": "Editor wird geladen. Bitte warten !", 
     151  "Loading plugin $plugin" : "Plugin $plugin wird geladen", 
     152  "Register plugin $plugin" : "Plugin $plugin wird registriert",  
     153  "Constructing object": "Objekt wird generiert", 
     154  "Generate Xinha framework": "Xinha Framework wird generiert", 
     155  "Init editor size":"Größe wird berechnet",  
     156  "Create Toolbar": "Werkzeugleiste wird generiert", 
     157  "Create Statusbar" : "Statusleiste wird generiert", 
     158  "Register right panel" : "Rechtes Panel wird generiert",  
     159  "Register left panel" : "Linkes Panel wird generiert",  
     160  "Register bottom panel" : "Unteres Panel wird generiert",  
     161  "Register top panel" : "Oberes Panel wird generiert",  
     162  "Finishing" : "Laden wird abgeschlossen",  
    157163   
    158164  // ColorPicker 
  • branches/ray/lang/fr.js

    r471 r862  
    152152 
    153153  // Loading messages 
    154   "Loading in progress. Please wait !": "Chargement en cours. Veuillez patienter !", 
    155   "Constructing main object": "Construction de l'objet principal", 
     154  "Loading in progress. Please wait!": "Chargement en cours. Veuillez patienter!", 
     155  "Finishing" : "Chargement bientÃŽt terminé",  
    156156  "Constructing object": "Construction de l'objet", 
    157   "Register panel right": "Enregistrement du panneau droit", 
    158   "Register panel left": "Enregistrement du panneau gauche", 
    159   "Register panel top": "Enregistrement du panneau supérieur", 
    160   "Register panel bottom": "Enregistrement du panneau inférieur", 
    161157  "Create Toolbar": "Construction de la barre d'icones", 
    162   "Create StatusBar": "Construction de la barre de status", 
    163   "Generate Xinha object": "Génération de l'objet Xinha", 
     158  "Create Statusbar": "Construction de la barre de status", 
     159  "Register right panel" : "Enregistrement du panneau droit",  
     160  "Register left panel" : "Enregistrement du panneau gauche",  
     161  "Register bottom panel" : "Enregistrement du panneau supérieur",  
     162  "Register top panel" : "Enregistrement du panneau inférieur",  
     163  "Generate Xinha framework": "Génération de Xinha", 
    164164  "Init editor size": "Initialisation de la taille d'édition", 
    165165  "Init IFrame": "Initialisation de l'iframe", 
    166   "Register plugin $plugin": "Enregistrement du plugin $plugin" 
     166  "Register plugin $plugin": "Enregistrement du plugin $plugin", 
     167  "Loading plugin $plugin" : "Chargement du plugin $plugin" 
     168 
    167169}; 
  • branches/ray/lang/gb.js

    r60 r862  
    3131  "Enlarge Editor": "攟倧", 
    3232  "About this editor": "å 
    33 ³æ–Œ HTMLArea", 
     33³æ–Œ Xinha", 
    3434  "Help using editor": "诎明", 
    3535  "Current style": "字䜓䟋子" 
  • branches/ray/lang/it.js

    r104 r862  
    2727  "Toggle HTML Source": "Visualizza/nascondi sorgente HTML", 
    2828  "Enlarge Editor": "Allarga editor", 
    29   "About this editor": "Informazioni su HTMLArea", 
     29  "About this editor": "Informazioni su Xinha", 
    3030  "Help using editor": "Aiuto", 
    3131  "Current style": "Stile corrente", 
  • branches/ray/lang/ja.js

    r761 r862  
    185185 
    186186  // Loading messages 
    187   "Loading in progress. Please wait !": "ロヌド䞭です。しばらくお埠
     187  "Loading in progress. Please wait!": "ロヌド䞭です。しばらくお埠
    188188ちください", 
    189   "Constructing main object": "構成䞭 main object", 
    190   "Constructing object": "構成䞭 object", 
    191   "Register panel right": "登録 右パネル", 
    192   "Register panel left": "登録 巊パネル", 
    193   "Register panel top": "登録 䞊パネル", 
    194   "Register panel bottom": "登録 䞋パネル", 
    195   "Create Toolbar": "䜜成 ツヌルバヌ", 
    196   "Create StatusBar": "䜜成 ステヌタスバヌ", 
    197   "Generate Xinha object": "生成 Xinha object", 
    198   "Init editor size": "初期化 ゚ディタのサむズ", 
    199   "Init IFrame": "初期化 IFrame", 
    200   "Register plugin $plugin": "プラグむンの登録 $plugin" 
     189  "Loading plugin $plugin" : "ロヌド䞭プラグむン $plugin", 
     190  "Register plugin $plugin" : "登録䞭プラグむン $plugin",  
     191  "Constructing object": "オブゞェクト構築䞭", 
     192  "Generate Xinha framework": "Xinhaフレヌムワヌク生成䞭", 
     193  "Init editor size":"゚ディタサむズの初期化",  
     194  "Create Toolbar": "ツヌルバヌの䜜成", 
     195  "Create Statusbar" : "ステヌタスバヌの䜜成", 
     196  "Register right panel" : "登録 右パネル",  
     197  "Register left panel" : "登録 巊パネル",  
     198  "Register bottom panel" : "登録 䞋パネル",  
     199  "Register top panel" : "登録 䞊パネル",  
     200  "Finishing" : "完了", 
     201   
     202  // ColorPicker 
     203  "Click a color..." : "色をクリック...", 
     204  "Sample" : "サンプル", 
     205  "Web Safe: " : "Webセヌフ: ", 
     206  "Color: " : "色: "   
    201207}; 
  • branches/ray/lang/nl.js

    r104 r862  
    3737  "Enlarge Editor": "Vergroot Editor", 
    3838  "About this editor": "Over deze editor", 
    39   "Help using editor": "HTMLArea help", 
     39  "Help using editor": "Xinha help", 
    4040  "Current style": "Huidige stijl", 
    4141  "Undoes your last action": "Ongedaan maken", 
  • branches/ray/modules/Dialogs/XinhaDialog.js

    r806 r862  
    169169    style.top = '3px'; 
    170170    style.left = '2px'; 
     171    ondrag = function () {return false;}; 
    171172  } 
    172173  captionBar.style.paddingLeft = '22px'; 
     
    661662Xinha.Dialog.prototype.posBackground = function(pos) 
    662663{ 
    663   this.background.style.top  = pos.top; 
    664   this.background.style.left = pos.left; 
     664  if (this.background.style.display != 'none') 
     665  { 
     666    this.background.style.top  = pos.top; 
     667    this.background.style.left = pos.left; 
     668  } 
    665669} 
    666670Xinha.Dialog.prototype.resizeBackground = function(size) 
    667671{ 
    668   this.background.style.width  = size.width; 
    669   this.background.style.height = size.height; 
     672  if (this.background.style.display != 'none') 
     673  { 
     674    this.background.style.width  = size.width; 
     675    this.background.style.height = size.height; 
     676  } 
    670677} 
    671678Xinha.Dialog.prototype.posDialog = function(pos) 
  • branches/ray/modules/Gecko/Gecko.js

    r806 r862  
    740740        var sel = this.getSelection(); 
    741741        sel.getRangeAt(0).deleteContents(); 
     742        this._iframe.contentWindow.focus(); 
    742743      } 
    743744    }