Changeset 672


Ignore:
Timestamp:
01/18/07 15:13:28 (13 years ago)
Author:
gogo
Message:

Cleanup a few things..
ticket:901 - the config variable (debatable if required) should be on by default, because IE does it by default (built in), this variable only affects Gecko, and the
functionality once worked as default, before a bug made it stop working.
ticket:10 changset:635 - cleaned up the separation of browser specific functionality and made it use the plugin architecture to do it's job. There is more to do in
cleaning up browser specific bits of XinhaCore?.js, for example EnterParagraphs? plugin loading should be part of the Gecko layer now, browser specific config variables
should move to for example "Xinha.Gecko.paragraphHandler". I also think the browser specific files should be in a folder, perhaps Browser/Gecko?.js instead of
functionsMozilla.js

Location:
trunk
Files:
4 edited

Legend:

Unmodified
Added
Removed
  • trunk/XinhaCore.js

    r671 r672  
    374374  // see if the text just typed looks like a URL, or email address 
    375375  // and link it appropriatly 
    376   this.convertUrlsToLinks = false; 
     376  this.convertUrlsToLinks = true; 
    377377 
    378378  // size of color picker cells 
     
    14461446  var i; 
    14471447  var editor = this;    // we'll need "this" in some nested functions 
     1448   
     1449  // Now load a specific browser plugin which will implement the above for us. 
     1450  if (Xinha.is_ie) 
     1451  { 
     1452    if ( typeof InternetExplorer == 'undefined' ) 
     1453    {             
     1454      Xinha.loadPlugin("InternetExplorer", function() { editor.generate(); }, _editor_url + 'functionsIE.js' ); 
     1455      return false; 
     1456    } 
     1457    editor._browserSpecificPlugin = editor.registerPlugin('InternetExplorer'); 
     1458  } 
     1459  else 
     1460  { 
     1461    if ( typeof Gecko == 'undefined' ) 
     1462    {             
     1463      Xinha.loadPlugin("Gecko", function() { editor.generate(); }, _editor_url + 'functionsMozilla.js' ); 
     1464      return false; 
     1465    } 
     1466    editor._browserSpecificPlugin = editor.registerPlugin('Gecko'); 
     1467  } 
     1468 
    14481469  this.setLoadingMessage('Generate Xinha object'); 
    14491470 
     
    25292550}; 
    25302551 
    2531 Xinha.loadPlugin = function(pluginName, callback) 
     2552Xinha.loadPlugin = function(pluginName, callback, plugin_file) 
    25322553{ 
    25332554  // @todo : try to avoid the use of eval() 
     
    25422563  } 
    25432564 
    2544   var dir = this.getPluginDir(pluginName); 
    2545   var plugin = pluginName.replace(/([a-z])([A-Z])([a-z])/g, function (str, l1, l2, l3) { return l1 + "-" + l2.toLowerCase() + l3; }).toLowerCase() + ".js"; 
    2546   var plugin_file = dir + "/" + plugin; 
    2547  
     2565  if(!plugin_file) 
     2566  { 
     2567    var dir = this.getPluginDir(pluginName); 
     2568    var plugin = pluginName.replace(/([a-z])([A-Z])([a-z])/g, function (str, l1, l2, l3) { return l1 + "-" + l2.toLowerCase() + l3; }).toLowerCase() + ".js"; 
     2569    plugin_file = dir + "/" + plugin; 
     2570  } 
     2571   
    25482572  Xinha._loadback(plugin_file, callback ? function() { callback(pluginName); } : null); 
    25492573  return false; 
     
    25512575 
    25522576Xinha._pluginLoadStatus = {}; 
    2553 Xinha._browserSpecificFunctionsLoaded = false; 
     2577 
    25542578Xinha.loadPlugins = function(plugins, callbackIfNotReady) 
    25552579{ 
     
    25582582  var nuPlugins = Xinha.cloneObject(plugins); 
    25592583 
    2560   if ( !Xinha._browserSpecificFunctionsLoaded ) 
    2561   { 
    2562         if (Xinha.is_ie) 
    2563         { 
    2564                 Xinha._loadback(_editor_url + "functionsIE.js",callbackIfNotReady); 
    2565         } 
    2566         else 
    2567         { 
    2568                 Xinha._loadback(_editor_url + "functionsMozilla.js",callbackIfNotReady); 
    2569         } 
    2570         return false; 
    2571   } 
    25722584  while ( nuPlugins.length ) 
    25732585  { 
     
    38533865{ 
    38543866  var editor = this; 
    3855   var keyEvent = (Xinha.is_ie && ev.type == "keydown") || (!Xinha.is_ie && ev.type == "keypress"); 
     3867  var keyEvent = this.isKeyEvent(ev); 
    38563868 
    38573869  //call events of textarea 
     
    38603872    editor._textArea['on'+ev.type](); 
    38613873  } 
    3862  
    3863   if ( Xinha.is_gecko && keyEvent && ev.ctrlKey &&  this._unLink && this._unlinkOnUndo ) 
    3864   { 
    3865     if ( String.fromCharCode(ev.charCode).toLowerCase() == 'z' ) 
    3866     { 
    3867       Xinha._stopEvent(ev); 
    3868       this._unLink(); 
    3869       editor.updateToolbar(); 
    3870       return; 
    3871     } 
    3872   } 
    3873  
     3874   
    38743875  if ( keyEvent ) 
    38753876  { 
     3877    // Run the ordinary plugins first 
    38763878    for ( var i in editor.plugins ) 
    38773879    { 
    38783880      var plugin = editor.plugins[i].instance; 
     3881       
     3882      if ( plugin == editor._browserSpecificPlugin) continue; 
     3883       
    38793884      if ( plugin && typeof plugin.onKeyPress == "function" ) 
    38803885      { 
     
    38853890      } 
    38863891    } 
     3892     
     3893    // Now run the browser specific one 
     3894    if(typeof editor._browserSpecificPlugin.onKeyPress == "function") 
     3895    { 
     3896      if(editor._browserSpecificPlugin.onKeyPress(ev))  
     3897      { 
     3898        return false; 
     3899      } 
     3900    } 
    38873901  } 
    38883902 
     
    38913905        this._shortCuts(ev); 
    38923906  } 
    3893   else if ( keyEvent && Xinha.is_gecko ) 
    3894   { 
    3895     this.mozKey( ev, keyEvent ); 
    3896   } 
    3897  
     3907   
    38983908  // update the toolbar state after some time 
    38993909  if ( editor._timerToolbar ) 
     
    58205830  } 
    58215831}; 
    5822 // backward compatibility 
     5832 
     5833 
     5834// The following methods may be over-ridden or extended by the browser specific 
     5835// javascript files. 
     5836 
     5837 
     5838/** Insert a node at the current selection point.  
     5839 * @param toBeInserted DomNode 
     5840 */ 
     5841 
     5842Xinha.prototype.insertNodeAtSelection = function(toBeInserted) { Xinha.notImplemented("insertNodeAtSelection"); } 
     5843 
     5844/** Get the parent element of the supplied or current selection.  
     5845 *  @param   sel optional selection as returned by getSelection 
     5846 *  @returns DomNode 
     5847 */ 
     5848   
     5849Xinha.prototype.getParentElement      = function(sel) { Xinha.notImplemented("getParentElement"); } 
     5850 
     5851/** 
     5852 * Returns the selected element, if any.  That is, 
     5853 * the element that you have last selected in the "path" 
     5854 * at the bottom of the editor, or a "control" (eg image) 
     5855 * 
     5856 * @returns null | DomNode 
     5857 */ 
     5858  
     5859Xinha.prototype.activeElement         = function(sel) { Xinha.notImplemented("activeElement"); } 
     5860 
     5861/**  
     5862 * Determines if the given selection is empty (collapsed). 
     5863 * @param selection Selection object as returned by getSelection 
     5864 * @returns true|false 
     5865 */ 
     5866  
     5867Xinha.prototype.selectionEmpty        = function(sel) { Xinha.notImplemented("selectionEmpty"); } 
     5868 
     5869/** 
     5870 * Selects the contents of the given node.  If the node is a "control" type element, (image, form input, table) 
     5871 * the node itself is selected for manipulation. 
     5872 * 
     5873 * @param node DomNode  
     5874 * @param pos  Set to a numeric position inside the node to collapse the cursor here if possible.  
     5875 */ 
     5876  
     5877Xinha.prototype.selectNodeContents    = function(node,pos) { Xinha.notImplemented("selectNodeContents"); } 
     5878 
     5879/** Insert HTML at the current position, deleting the selection if any.  
     5880 *   
     5881 *  @param html string 
     5882 */ 
     5883  
     5884Xinha.prototype.insertHTML            = function(html) { Xinha.notImplemented("insertHTML"); } 
     5885 
     5886/** Get the HTML of the current selection.  HTML returned has not been passed through outwardHTML. 
     5887 * 
     5888 * @returns string 
     5889 */ 
     5890Xinha.prototype.getSelectedHTML       = function() { Xinha.notImplemented("getSelectedHTML"); } 
     5891 
     5892/** Get a Selection object of the current selection.  Note that selection objects are browser specific. 
     5893 * 
     5894 * @returns Selection 
     5895 */ 
     5896  
     5897Xinha.prototype.getSelection          = function() { Xinha.notImplemented("getSelection"); } 
     5898 
     5899/** Create a Range object from the given selection.  Note that range objects are browser specific. 
     5900 * 
     5901 *  @param sel Selection object (see getSelection) 
     5902 *  @returns Range 
     5903 */ 
     5904  
     5905Xinha.prototype.createRange           = function(sel) { Xinha.notImplemented("createRange"); } 
     5906 
     5907/** Determine if the given event object is a keydown/press event. 
     5908 * 
     5909 *  @param event Event  
     5910 *  @returns true|false 
     5911 */ 
     5912  
     5913Xinha.prototype.isKeyEvent            = function(event) { Xinha.notImplemented("isKeyEvent"); } 
     5914 
     5915/** Return the HTML string of the given Element, including the Element. 
     5916 *  
     5917 * @param element HTML Element DomNode 
     5918 * @returns string 
     5919 */ 
     5920  
     5921Xinha.getOuterHTML = function(element) { Xinha.notImplemented("getOuterHTML"); } 
     5922 
     5923 
     5924 
     5925// Compatability - all these names are deprecated and will be removed in a future version 
     5926Xinha.prototype._activeElement  = function(sel) { return this.activeElement(sel); } 
     5927Xinha.prototype._selectionEmpty = function(sel) { return this.selectionEmpty(sel); } 
     5928Xinha.prototype._getSelection   = function() { return this.getSelection(); } 
     5929Xinha.prototype._createRange    = function(sel) { return this.createRange(sel); } 
    58235930HTMLArea = Xinha; 
     5931 
    58245932Xinha.init(); 
    58255933Xinha.addDom0Event(window,'unload',Xinha.collectGarbageForIE); 
     5934 
     5935Xinha.notImplemented = function(methodName)  
     5936{ 
     5937  throw new Error("Method Not Implemented", "Part of Xinha has tried to call the " + methodName + " method which has not been implemented."); 
     5938} 
  • trunk/examples/testbed.html

    r664 r672  
    5555      xinha_plugins = xinha_plugins ? xinha_plugins : 
    5656      [ 
    57         'CharacterMap' 
     57        'CharacterMap', 'SpellChecker', 'Linker' 
    5858      ]; 
    5959             // THIS BIT OF JAVASCRIPT LOADS THE PLUGINS, NO TOUCHING  :) 
  • trunk/functionsIE.js

    r649 r672  
    1 /** Returns a node after which we can insert other nodes, in the current 
    2  * selection.  The selection is removed.  It splits a text node, if needed. 
    3  */ 
     1 
     2  /*--------------------------------------:noTabs=true:tabSize=2:indentSize=2:-- 
     3    --  Xinha (is not htmlArea) - http://xinha.gogo.co.nz/ 
     4    -- 
     5    --  Use of Xinha is granted by the terms of the htmlArea License (based on 
     6    --  BSD license)  please read license.txt in this package for details. 
     7    -- 
     8    --  Xinha was originally based on work by Mihai Bazon which is: 
     9    --      Copyright (c) 2003-2004 dynarch.com. 
     10    --      Copyright (c) 2002-2003 interactivetools.com, inc. 
     11    --      This copyright notice MUST stay intact for use. 
     12    -- 
     13    -- This is the Internet Explorer compatability plugin, part of the  
     14    -- Xinha core. 
     15    -- 
     16    --  The file is loaded as a special plugin by the Xinha Core when 
     17    --  Xinha is being run under an Internet Explorer based browser. 
     18    -- 
     19    --  It provides implementation and specialisation for various methods 
     20    --  in the core where different approaches per browser are required. 
     21    -- 
     22    --  Design Notes:: 
     23    --   Most methods here will simply be overriding Xinha.prototype.<method> 
     24    --   and should be called that, but methods specific to IE should  
     25    --   be a part of the InternetExplorer.prototype, we won't trample on  
     26    --   namespace that way. 
     27    -- 
     28    --  $HeadURL$ 
     29    --  $LastChangedDate$ 
     30    --  $LastChangedRevision$ 
     31    --  $LastChangedBy$ 
     32    --------------------------------------------------------------------------*/ 
     33                                                     
     34InternetExplorer._pluginInfo = { 
     35  name          : "Internet Explorer", 
     36  origin        : "Xinha Core", 
     37  version       : "$LastChangedRevision$", 
     38  developer     : "The Xinha Core Developer Team", 
     39  developer_url : "$HeadURL$", 
     40  license       : "htmlArea" 
     41}; 
     42 
     43function InternetExplorer(editor) { 
     44  this.editor = editor;   
     45  editor.InternetExplorer = this; // So we can do my_editor.InternetExplorer.doSomethingIESpecific(); 
     46} 
     47 
     48/** Allow Internet Explorer to handle some key events in a special way. 
     49 */ 
     50   
     51InternetExplorer.prototype.onKeyPress = function(ev) 
     52{ 
     53  switch(ev.keyCode)  
     54  { 
     55    case 8: // KEY backspace 
     56    case 46: // KEY delete 
     57    { 
     58      if(this.handleBackspace()) 
     59      { 
     60        Xinha._stopEvent(ev); 
     61        return true; 
     62      } 
     63    } 
     64    break; 
     65  } 
     66   
     67  return false; 
     68} 
     69 
     70/** When backspace is hit, the IE onKeyPress will execute this method. 
     71 *  It preserves links when you backspace over them and apparently  
     72 *  deletes control elements (tables, images, form fields) in a better 
     73 *  way. 
     74 * 
     75 *  @returns true|false True when backspace has been handled specially 
     76 *   false otherwise (should pass through).  
     77 */ 
     78 
     79InternetExplorer.prototype.handleBackspace = function() 
     80{ 
     81  var editor = this.editor; 
     82  var sel = editor._getSelection(); 
     83  if ( sel.type == 'Control' ) 
     84  { 
     85    var elm = editor._activeElement(sel); 
     86    Xinha.removeFromParent(elm); 
     87    return true; 
     88  } 
     89 
     90  // This bit of code preseves links when you backspace over the 
     91  // endpoint of the link in IE.  Without it, if you have something like 
     92  //    link_here | 
     93  // where | is the cursor, and backspace over the last e, then the link 
     94  // will de-link, which is a bit tedious 
     95  var range = editor._createRange(sel); 
     96  var r2 = range.duplicate(); 
     97  r2.moveStart("character", -1); 
     98  var a = r2.parentElement(); 
     99  // @fixme: why using again a regex to test a single string ??? 
     100  if ( a != range.parentElement() && ( /^a$/i.test(a.tagName) ) ) 
     101  { 
     102    r2.collapse(true); 
     103    r2.moveEnd("character", 1); 
     104    r2.pasteHTML(''); 
     105    r2.select(); 
     106    return true; 
     107  } 
     108}; 
     109 
     110/*--------------------------------------------------------------------------*/ 
     111/*------- IMPLEMENTATION OF THE ABSTRACT "Xinha.prototype" METHODS ---------*/ 
     112/*--------------------------------------------------------------------------*/ 
     113 
     114/** Insert a node at the current selection point.  
     115 * @param toBeInserted DomNode 
     116 */ 
     117 
    4118Xinha.prototype.insertNodeAtSelection = function(toBeInserted) 
    5119{ 
    6   return null;  // this function not yet used for IE <FIXME> 
    7 }; 
    8  
    9 // Returns the deepest node that contains both endpoints of the selection. 
     120  Xinha.notImplemented('insertNodeAtSelection'); 
     121}; 
     122 
     123   
     124/** Get the parent element of the supplied or current selection.  
     125 *  @param   sel optional selection as returned by getSelection 
     126 *  @returns DomNode 
     127 */ 
     128  
    10129Xinha.prototype.getParentElement = function(sel) 
    11130{ 
     
    55174 * at the bottom of the editor, or a "control" (eg image) 
    56175 * 
    57  * @returns null | element 
    58  */ 
     176 * @returns null | DomNode 
     177 */ 
     178  
    59179Xinha.prototype._activeElement = function(sel) 
    60180{ 
     
    104224}; 
    105225 
    106   Xinha.prototype._selectionEmpty = function(sel) 
     226/**  
     227 * Determines if the given selection is empty (collapsed). 
     228 * @param selection Selection object as returned by getSelection 
     229 * @returns true|false 
     230 */ 
     231  
     232Xinha.prototype.selectionEmpty = function(sel) 
    107233{ 
    108234  if ( !sel ) 
     
    114240}; 
    115241 
    116 // Selects the contents inside the given node 
     242/** 
     243 * Selects the contents of the given node.  If the node is a "control" type element, (image, form input, table) 
     244 * the node itself is selected for manipulation. 
     245 * 
     246 * @param node DomNode  
     247 * @param pos  Set to a numeric position inside the node to collapse the cursor here if possible.  
     248 */ 
     249  
    117250Xinha.prototype.selectNodeContents = function(node, pos) 
    118251{ 
     
    136269}; 
    137270   
    138 /** Call this function to insert HTML code at the current position.  It deletes 
    139  * the selection, if any. 
    140  */ 
     271/** Insert HTML at the current position, deleting the selection if any.  
     272 *   
     273 *  @param html string 
     274 */ 
     275  
    141276Xinha.prototype.insertHTML = function(html) 
    142277{ 
     
    148283 
    149284 
    150 // Retrieve the selected block 
     285/** Get the HTML of the current selection.  HTML returned has not been passed through outwardHTML. 
     286 * 
     287 * @returns string 
     288 */ 
     289  
    151290Xinha.prototype.getSelectedHTML = function() 
    152291{ 
     
    166305  return ''; 
    167306}; 
    168  
    169 Xinha.prototype.checkBackspace = function() 
    170 { 
    171   var sel = this._getSelection(); 
    172   if ( sel.type == 'Control' ) 
    173   { 
    174     var elm = this._activeElement(sel); 
    175     Xinha.removeFromParent(elm); 
    176     return true; 
    177   } 
    178  
    179   // This bit of code preseves links when you backspace over the 
    180   // endpoint of the link in IE.  Without it, if you have something like 
    181   //    link_here | 
    182   // where | is the cursor, and backspace over the last e, then the link 
    183   // will de-link, which is a bit tedious 
    184   var range = this._createRange(sel); 
    185   var r2 = range.duplicate(); 
    186   r2.moveStart("character", -1); 
    187   var a = r2.parentElement(); 
    188   // @fixme: why using again a regex to test a single string ??? 
    189   if ( a != range.parentElement() && ( /^a$/i.test(a.tagName) ) ) 
    190   { 
    191     r2.collapse(true); 
    192     r2.moveEnd("character", 1); 
    193     r2.pasteHTML(''); 
    194     r2.select(); 
    195     return true; 
    196   } 
    197 }; 
    198    
    199 // returns the current selection object 
    200 Xinha.prototype._getSelection = function() 
     307   
     308/** Get a Selection object of the current selection.  Note that selection objects are browser specific. 
     309 * 
     310 * @returns Selection 
     311 */ 
     312  
     313Xinha.prototype.getSelection = function() 
    201314{ 
    202315  return this._doc.selection; 
    203316}; 
    204317 
    205 // returns a range for the current selection 
    206 Xinha.prototype._createRange = function(sel) 
     318/** Create a Range object from the given selection.  Note that range objects are browser specific. 
     319 * 
     320 *  @param sel Selection object (see getSelection) 
     321 *  @returns Range 
     322 */ 
     323  
     324Xinha.prototype.createRange = function(sel) 
    207325{ 
    208326  return sel.createRange(); 
    209327}; 
    210328 
     329/** Determine if the given event object is a keydown/press event. 
     330 * 
     331 *  @param event Event  
     332 *  @returns true|false 
     333 */ 
     334  
     335Xinha.prototype.isKeyEvent = function(event) 
     336{ 
     337  return event.type == "keydown"; 
     338} 
     339 
     340/** Return the HTML string of the given Element, including the Element. 
     341 *  
     342 * @param element HTML Element DomNode 
     343 * @returns string 
     344 */ 
     345  
    211346Xinha.getOuterHTML = function(element) 
    212347{ 
     
    214349}; 
    215350   
    216 //What is this supposed to do??? it's never used  
    217 Xinha.prototype._formatBlock = function(block_format) 
    218 { 
    219  
    220 }; 
    221  
    222 Xinha._browserSpecificFunctionsLoaded = true; 
  • trunk/functionsMozilla.js

    r649 r672  
    1 /** Returns a node after which we can insert other nodes, in the current 
    2  * selection.  The selection is removed.  It splits a text node, if needed. 
    3  */ 
    4 Xinha.prototype.insertNodeAtSelection = function(toBeInserted) 
    5 { 
    6   var sel = this._getSelection(); 
    7   var range = this._createRange(sel); 
    8   // remove the current selection 
    9   sel.removeAllRanges(); 
    10   range.deleteContents(); 
    11   var node = range.startContainer; 
    12   var pos = range.startOffset; 
    13   var selnode = toBeInserted; 
    14   switch ( node.nodeType ) 
    15   { 
    16     case 3: // Node.TEXT_NODE 
    17       // we have to split it at the caret position. 
    18       if ( toBeInserted.nodeType == 3 ) 
    19       { 
    20         // do optimized insertion 
    21         node.insertData(pos, toBeInserted.data); 
    22         range = this._createRange(); 
    23         range.setEnd(node, pos + toBeInserted.length); 
    24         range.setStart(node, pos + toBeInserted.length); 
    25         sel.addRange(range); 
    26       } 
    27       else 
    28       { 
    29         node = node.splitText(pos); 
    30         if ( toBeInserted.nodeType == 11 /* Node.DOCUMENT_FRAGMENT_NODE */ ) 
    31         { 
    32           selnode = selnode.firstChild; 
    33         } 
    34         node.parentNode.insertBefore(toBeInserted, node); 
    35         this.selectNodeContents(selnode); 
    36         this.updateToolbar(); 
    37       } 
    38     break; 
    39     case 1: // Node.ELEMENT_NODE 
    40       if ( toBeInserted.nodeType == 11 /* Node.DOCUMENT_FRAGMENT_NODE */ ) 
    41       { 
    42         selnode = selnode.firstChild; 
    43       } 
    44       node.insertBefore(toBeInserted, node.childNodes[pos]); 
    45       this.selectNodeContents(selnode); 
    46       this.updateToolbar(); 
    47     break; 
    48   } 
    49 }; 
    50    
    51 // Returns the deepest node that contains both endpoints of the selection. 
    52 Xinha.prototype.getParentElement = function(sel) 
    53 { 
    54   if ( typeof sel == 'undefined' ) 
    55   { 
    56     sel = this._getSelection(); 
    57   } 
    58   var range = this._createRange(sel); 
    59   try 
    60   { 
    61     var p = range.commonAncestorContainer; 
    62     if ( !range.collapsed && range.startContainer == range.endContainer && 
    63         range.startOffset - range.endOffset <= 1 && range.startContainer.hasChildNodes() ) 
    64     { 
    65       p = range.startContainer.childNodes[range.startOffset]; 
    66     } 
    67     /* 
    68     alert(range.startContainer + ":" + range.startOffset + "\n" + 
    69           range.endContainer + ":" + range.endOffset); 
    70     */ 
    71     while ( p.nodeType == 3 ) 
    72     { 
    73       p = p.parentNode; 
    74     } 
    75     return p; 
    76   } 
    77   catch (ex) 
    78   { 
    79     return null; 
    80   } 
    81 }; 
    82  
    83 /** 
    84  * Returns the selected element, if any.  That is, 
    85  * the element that you have last selected in the "path" 
    86  * at the bottom of the editor, or a "control" (eg image) 
    87  * 
    88  * @returns null | element 
    89  */ 
    90 Xinha.prototype._activeElement = function(sel) 
    91 { 
    92   if ( ( sel === null ) || this._selectionEmpty(sel) ) 
    93   { 
    94     return null; 
    95   } 
    96  
    97   // For Mozilla we just see if the selection is not collapsed (something is selected) 
    98   // and that the anchor (start of selection) is an element.  This might not be totally 
    99   // correct, we possibly should do a simlar check to IE? 
    100   if ( !sel.isCollapsed ) 
    101   {       
    102     if ( sel.anchorNode.childNodes.length > sel.anchorOffset && sel.anchorNode.childNodes[sel.anchorOffset].nodeType == 1 ) 
    103     { 
    104       return sel.anchorNode.childNodes[sel.anchorOffset]; 
    105     } 
    106     else if ( sel.anchorNode.nodeType == 1 ) 
    107     { 
    108       return sel.anchorNode; 
    109     } 
    110     else 
    111     { 
    112       return null; // return sel.anchorNode.parentNode; 
    113     } 
    114   } 
    115   return null; 
    116 }; 
    117    
    118 Xinha.prototype._selectionEmpty = function(sel) 
    119 { 
    120   if ( !sel ) 
    121   { 
    122     return true; 
    123   } 
    124  
    125   if ( typeof sel.isCollapsed != 'undefined' ) 
    126   {       
    127     return sel.isCollapsed; 
    128   } 
    129  
    130   return true; 
    131 }; 
    132    
    133 // Selects the contents inside the given node 
    134 Xinha.prototype.selectNodeContents = function(node, pos) 
    135 { 
    136   this.focusEditor(); 
    137   this.forceRedraw(); 
    138   var range; 
    139   var collapsed = typeof pos == "undefined" ? true : false; 
    140   var sel = this._getSelection(); 
    141   range = this._doc.createRange(); 
    142   // Tables and Images get selected as "objects" rather than the text contents 
    143   if ( collapsed && node.tagName && node.tagName.toLowerCase().match(/table|img|input|textarea|select/) ) 
    144   { 
    145     range.selectNode(node); 
    146   } 
    147   else 
    148   { 
    149     range.selectNodeContents(node); 
    150     //(collapsed) && range.collapse(pos); 
    151   } 
    152   sel.removeAllRanges(); 
    153   sel.addRange(range); 
    154 }; 
    155    
    156 /** Call this function to insert HTML code at the current position.  It deletes 
    157  * the selection, if any. 
    158  */ 
    159 Xinha.prototype.insertHTML = function(html) 
    160 { 
    161   var sel = this._getSelection(); 
    162   var range = this._createRange(sel); 
    163   this.focusEditor(); 
    164   // construct a new document fragment with the given HTML 
    165   var fragment = this._doc.createDocumentFragment(); 
    166   var div = this._doc.createElement("div"); 
    167   div.innerHTML = html; 
    168   while ( div.firstChild ) 
    169   { 
    170     // the following call also removes the node from div 
    171     fragment.appendChild(div.firstChild); 
    172   } 
    173   // this also removes the selection 
    174   var node = this.insertNodeAtSelection(fragment); 
    175 }; 
    176  
    177 // Retrieve the selected block 
    178 Xinha.prototype.getSelectedHTML = function() 
    179 { 
    180   var sel = this._getSelection(); 
    181   var range = this._createRange(sel); 
    182   return Xinha.getHTML(range.cloneContents(), false, this); 
    183 }; 
    184    
    185 Xinha.prototype.checkBackspace = function() 
    186 { 
    187   var self = this; 
    188   setTimeout( 
    189     function() 
    190     { 
    191       var sel = self._getSelection(); 
    192       var range = self._createRange(sel); 
    193       var SC = range.startContainer; 
    194       var SO = range.startOffset; 
    195       var EC = range.endContainer; 
    196       var EO = range.endOffset; 
    197       var newr = SC.nextSibling; 
    198       if ( SC.nodeType == 3 ) 
    199       { 
    200         SC = SC.parentNode; 
    201       } 
    202       if ( ! ( /\S/.test(SC.tagName) ) ) 
    203       { 
    204         var p = document.createElement("p"); 
    205         while ( SC.firstChild ) 
    206         { 
    207           p.appendChild(SC.firstChild); 
    208         } 
    209         SC.parentNode.insertBefore(p, SC); 
    210         Xinha.removeFromParent(SC); 
    211         var r = range.cloneRange(); 
    212         r.setStartBefore(newr); 
    213         r.setEndAfter(newr); 
    214         r.extractContents(); 
    215         sel.removeAllRanges(); 
    216         sel.addRange(r); 
    217       } 
    218     }, 
    219     10); 
    220 }; 
    221  
    222 // returns the current selection object 
    223 Xinha.prototype._getSelection = function() 
    224 { 
    225   return this._iframe.contentWindow.getSelection(); 
    226 }; 
    227    
    228 // returns a range for the current selection 
    229 Xinha.prototype._createRange = function(sel) 
    230 { 
    231   this.activateEditor(); 
    232   if ( typeof sel != "undefined" ) 
    233   { 
    234     try 
    235     { 
    236       return sel.getRangeAt(0); 
    237     } 
    238     catch(ex) 
    239     { 
    240       return this._doc.createRange(); 
    241     } 
    242   } 
    243   else 
    244   { 
    245     return this._doc.createRange(); 
    246   } 
    247 }; 
    248  
    249 Xinha.getOuterHTML = function(element) 
    250 { 
    251   return (new XMLSerializer()).serializeToString(element); 
    252 }; 
    253    
    254 //What is this supposed to do??? it's never used  
    255 //ray 
    256 Xinha.prototype._formatBlock = function(block_format) 
    257 { 
    258   var ancestors = this.getAllAncestors(); 
    259   var apply_to, x = null; 
    260   // Block format can be a tag followed with class defs 
    261   //  eg div.blue.left 
    262   var target_tag = null; 
    263   var target_classNames = [ ]; 
    264  
    265   if ( block_format.indexOf('.') >= 0 ) 
    266   { 
    267     target_tag = block_format.substr(0, block_format.indexOf('.')).toLowerCase(); 
    268     target_classNames = block_format.substr(block_format.indexOf('.'), block_format.length - block_format.indexOf('.')).replace(/\./g, '').replace(/^\s*/, '').replace(/\s*$/, '').split(' '); 
    269   } 
    270   else 
    271   { 
    272     target_tag = block_format.toLowerCase(); 
    273   } 
    274  
    275   var sel = this._getSelection(); 
    276   var rng = this._createRange(sel); 
    277  
    278   if ( Xinha.is_gecko ) 
    279   { 
    280     if ( sel.isCollapsed ) 
    281     { 
    282       // With no selection we want to apply to the whole contents of the ancestor block 
    283       apply_to = this._getAncestorBlock(sel); 
    284       if ( apply_to === null ) 
    285       { 
    286         // If there wasn't an ancestor, make one. 
    287         apply_to = this._createImplicitBlock(sel, target_tag); 
    288       } 
    289     } 
    290     else 
    291     { 
    292       // With a selection it's more tricky 
    293       switch ( target_tag ) 
    294       { 
    295  
    296         case 'h1': 
    297         case 'h2': 
    298         case 'h3': 
    299         case 'h4': 
    300         case 'h5': 
    301         case 'h6': 
    302         case 'h7': 
    303           apply_to = []; 
    304           var search_tags = ['h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'h7']; 
    305           for ( var y = 0; y < search_tags.length; y++ ) 
    306           { 
    307             var headers = this._doc.getElementsByTagName(search_tags[y]); 
    308             for ( x = 0; x < headers.length; x++ ) 
    309             { 
    310               if ( sel.containsNode(headers[x]) ) 
    311               { 
    312                 apply_to[apply_to.length] = headers[x]; 
    313               } 
    314             } 
    315           } 
    316           if ( apply_to.length > 0) 
    317           { 
    318             break; 
    319           } 
    320           // If there wern't any in the selection drop through 
    321         case 'div': 
    322           apply_to = this._doc.createElement(target_tag); 
    323           apply_to.appendChild(rng.extractContents()); 
    324           rng.insertNode(apply_to); 
    325         break; 
    326  
    327         case 'p': 
    328         case 'center': 
    329         case 'pre': 
    330         case 'ins': 
    331         case 'del': 
    332         case 'blockquote': 
    333         case 'address': 
    334           apply_to = []; 
    335           var paras = this._doc.getElementsByTagName(target_tag); 
    336           for ( x = 0; x < paras.length; x++ ) 
    337           { 
    338             if ( sel.containsNode(paras[x]) ) 
    339             { 
    340               apply_to[apply_to.length] = paras[x]; 
    341             } 
    342           } 
    343  
    344           if ( apply_to.length === 0 ) 
    345           { 
    346             sel.collapseToStart(); 
    347             return this._formatBlock(block_format); 
    348           } 
    349         break; 
    350       } 
    351     } 
    352   } 
    353 }; 
    354  
    355  
    356 // IE's textRange and selection object is woefully inadequate, 
    357 // which means this fancy stuff is gecko only sorry :-| 
    358 // Die Bill, Die.  (IE supports it somewhat nativly though) 
    359 Xinha.prototype.mozKey = function ( ev, keyEvent ) 
    360 { 
    361   var editor = this; 
     1 
     2  /*--------------------------------------:noTabs=true:tabSize=2:indentSize=2:-- 
     3    --  Xinha (is not htmlArea) - http://xinha.gogo.co.nz/ 
     4    -- 
     5    --  Use of Xinha is granted by the terms of the htmlArea License (based on 
     6    --  BSD license)  please read license.txt in this package for details. 
     7    -- 
     8    --  Xinha was originally based on work by Mihai Bazon which is: 
     9    --      Copyright (c) 2003-2004 dynarch.com. 
     10    --      Copyright (c) 2002-2003 interactivetools.com, inc. 
     11    --      This copyright notice MUST stay intact for use. 
     12    -- 
     13    -- This is the Gecko compatability plugin, part of the Xinha core. 
     14    -- 
     15    --  The file is loaded as a special plugin by the Xinha Core when 
     16    --  Xinha is being run under a Gecko based browser with the Midas 
     17    --  editing API. 
     18    -- 
     19    --  It provides implementation and specialisation for various methods 
     20    --  in the core where different approaches per browser are required. 
     21    -- 
     22    --  Design Notes:: 
     23    --   Most methods here will simply be overriding Xinha.prototype.<method> 
     24    --   and should be called that, but methods specific to Gecko should  
     25    --   be a part of the Gecko.prototype, we won't trample on namespace 
     26    --   that way. 
     27    -- 
     28    --  $HeadURL$ 
     29    --  $LastChangedDate$ 
     30    --  $LastChangedRevision$ 
     31    --  $LastChangedBy$ 
     32    --------------------------------------------------------------------------*/ 
     33                                                     
     34Gecko._pluginInfo = { 
     35  name          : "Gecko", 
     36  origin        : "Xinha Core", 
     37  version       : "$LastChangedRevision$", 
     38  developer     : "The Xinha Core Developer Team", 
     39  developer_url : "$HeadURL$", 
     40  license       : "htmlArea" 
     41}; 
     42 
     43function Gecko(editor) { 
     44  this.editor = editor;   
     45  editor.Gecko = this; 
     46} 
     47 
     48/** Allow Gecko to handle some key events in a special way. 
     49 */ 
     50   
     51Gecko.prototype.onKeyPress = function(ev) 
     52{ 
     53  var editor = this.editor; 
     54   
     55  if ( ev.ctrlKey &&  editor._unLink && editor._unlinkOnUndo ) 
     56  { 
     57    if ( String.fromCharCode(ev.charCode).toLowerCase() == 'z' ) 
     58    { 
     59      Xinha._stopEvent(ev); 
     60      editor._unLink(); 
     61      editor.updateToolbar(); 
     62      return true; // Stop further 
     63    } 
     64  } 
     65   
    36266  var s = editor._getSelection(); 
    36367  var autoWrap = function (textNode, tag) 
     
    37377    rightText.data = ' ' + rightText.data; 
    37478 
    375     if ( Xinha.is_ie ) 
    376     { 
    377       var r = editor._createRange(s); 
    378       s.moveToElementText(rightText); 
    379       s.move('character', 1); 
    380     } 
    381     else 
    382     { 
    383       s.collapse(rightText, 1); 
    384     } 
    385     Xinha._stopEvent(ev); 
     79    s.collapse(rightText, 1); 
     80    // Xinha._stopEvent(ev); 
    38681 
    38782    editor._unLink = function() 
     
    40499    // and link it appropriatly 
    405100    case 32: 
    406       if ( this.config.convertUrlsToLinks && s && s.isCollapsed && s.anchorNode.nodeType == 3 && s.anchorNode.data.length > 3 && s.anchorNode.data.indexOf('.') >= 0 ) 
     101      if ( editor.config.convertUrlsToLinks && s && s.isCollapsed && s.anchorNode.nodeType == 3 && s.anchorNode.data.length > 3 && s.anchorNode.data.indexOf('.') >= 0 ) 
    407102      { 
    408103        var midStart = s.anchorNode.data.substring(0,s.anchorOffset).search(/\S{4,}$/); 
     
    412107        } 
    413108 
    414         if ( this._getFirstAncestor(s, 'a') ) 
     109        if ( editor._getFirstAncestor(s, 'a') ) 
    415110        { 
    416111          break; // already in an anchor 
     
    452147 
    453148    default: 
    454       if ( ev.keyCode == 27 || ( this._unlinkOnUndo && ev.ctrlKey && ev.which == 122 ) ) 
    455       { 
    456         if ( this._unLink ) 
    457         { 
    458           this._unLink(); 
     149      if ( ev.keyCode == 27 || ( editor._unlinkOnUndo && ev.ctrlKey && ev.which == 122 ) ) 
     150      { 
     151        if ( editor._unLink ) 
     152        { 
     153          editor._unLink(); 
    459154          Xinha._stopEvent(ev); 
    460155        } 
     
    463158      else if ( ev.which || ev.keyCode == 8 || ev.keyCode == 46 ) 
    464159      { 
    465         this._unlinkOnUndo = false; 
     160        editor._unlinkOnUndo = false; 
    466161 
    467162        if ( s.anchorNode && s.anchorNode.nodeType == 3 ) 
    468163        { 
    469164          // See if we might be changing a link 
    470           var a = this._getFirstAncestor(s, 'a'); 
     165          var a = editor._getFirstAncestor(s, 'a'); 
    471166          // @todo: we probably need here to inform the setTimeout below that we not changing a link and not start another setTimeout 
    472167          if ( !a ) 
     
    513208  } 
    514209 
    515  
    516210  // other keys here 
    517211  switch (ev.keyCode) 
    518212  { 
    519213    case 13: // KEY enter 
    520       if ( Xinha.is_gecko && !ev.shiftKey && this.config.mozParaHandler == 'dirty' ) 
    521       { 
    522         this.dom_checkInsertP(); 
     214      if( !ev.shiftKey && editor.config.mozParaHandler == 'dirty' ) 
     215      { 
     216        editor.dom_checkInsertP(); 
    523217        Xinha._stopEvent(ev); 
    524218      } 
     
    526220    case 8: // KEY backspace 
    527221    case 46: // KEY delete 
    528       if ( ( Xinha.is_gecko && !ev.shiftKey ) || Xinha.is_ie ) 
    529       { 
    530         if ( this.checkBackspace() ) 
    531         { 
    532           Xinha._stopEvent(ev); 
    533         } 
     222      if ( !ev.shiftKey && this.handleBackspace() ) 
     223      { 
     224        Xinha._stopEvent(ev); 
    534225      } 
    535226    break; 
    536227  } 
     228   
     229  return false; // Let other plugins etc continue from here. 
    537230} 
    538231 
    539 Xinha._browserSpecificFunctionsLoaded = true; 
     232/** When backspace is hit, the Gecko onKeyPress will execute this method. 
     233 *  I don't remember what the exact purpose of this is though :-( 
     234 */ 
     235  
     236Gecko.prototype.handleBackspace = function() 
     237{ 
     238  var editor = this.editor; 
     239  setTimeout( 
     240    function() 
     241    { 
     242      var sel   = editor.getSelection(); 
     243      var range = editor.createRange(sel); 
     244      var SC = range.startContainer; 
     245      var SO = range.startOffset; 
     246      var EC = range.endContainer; 
     247      var EO = range.endOffset; 
     248      var newr = SC.nextSibling; 
     249      if ( SC.nodeType == 3 ) 
     250      { 
     251        SC = SC.parentNode; 
     252      } 
     253      if ( ! ( /\S/.test(SC.tagName) ) ) 
     254      { 
     255        var p = document.createElement("p"); 
     256        while ( SC.firstChild ) 
     257        { 
     258          p.appendChild(SC.firstChild); 
     259        } 
     260        SC.parentNode.insertBefore(p, SC); 
     261        Xinha.removeFromParent(SC); 
     262        var r = range.cloneRange(); 
     263        r.setStartBefore(newr); 
     264        r.setEndAfter(newr); 
     265        r.extractContents(); 
     266        sel.removeAllRanges(); 
     267        sel.addRange(r); 
     268      } 
     269    }, 
     270    10); 
     271}; 
     272 
     273/*--------------------------------------------------------------------------*/ 
     274/*------- IMPLEMENTATION OF THE ABSTRACT "Xinha.prototype" METHODS ---------*/ 
     275/*--------------------------------------------------------------------------*/ 
     276 
     277/** Insert a node at the current selection point.  
     278 * @param toBeInserted DomNode 
     279 */ 
     280 
     281Xinha.prototype.insertNodeAtSelection = function(toBeInserted) 
     282{ 
     283  var sel = this._getSelection(); 
     284  var range = this._createRange(sel); 
     285  // remove the current selection 
     286  sel.removeAllRanges(); 
     287  range.deleteContents(); 
     288  var node = range.startContainer; 
     289  var pos = range.startOffset; 
     290  var selnode = toBeInserted; 
     291  switch ( node.nodeType ) 
     292  { 
     293    case 3: // Node.TEXT_NODE 
     294      // we have to split it at the caret position. 
     295      if ( toBeInserted.nodeType == 3 ) 
     296      { 
     297        // do optimized insertion 
     298        node.insertData(pos, toBeInserted.data); 
     299        range = this._createRange(); 
     300        range.setEnd(node, pos + toBeInserted.length); 
     301        range.setStart(node, pos + toBeInserted.length); 
     302        sel.addRange(range); 
     303      } 
     304      else 
     305      { 
     306        node = node.splitText(pos); 
     307        if ( toBeInserted.nodeType == 11 /* Node.DOCUMENT_FRAGMENT_NODE */ ) 
     308        { 
     309          selnode = selnode.firstChild; 
     310        } 
     311        node.parentNode.insertBefore(toBeInserted, node); 
     312        this.selectNodeContents(selnode); 
     313        this.updateToolbar(); 
     314      } 
     315    break; 
     316    case 1: // Node.ELEMENT_NODE 
     317      if ( toBeInserted.nodeType == 11 /* Node.DOCUMENT_FRAGMENT_NODE */ ) 
     318      { 
     319        selnode = selnode.firstChild; 
     320      } 
     321      node.insertBefore(toBeInserted, node.childNodes[pos]); 
     322      this.selectNodeContents(selnode); 
     323      this.updateToolbar(); 
     324    break; 
     325  } 
     326}; 
     327   
     328/** Get the parent element of the supplied or current selection.  
     329 *  @param   sel optional selection as returned by getSelection 
     330 *  @returns DomNode 
     331 */ 
     332  
     333Xinha.prototype.getParentElement = function(sel) 
     334{ 
     335  if ( typeof sel == 'undefined' ) 
     336  { 
     337    debugger; 
     338    sel = this._getSelection(); 
     339  } 
     340  var range = this._createRange(sel); 
     341  try 
     342  { 
     343    var p = range.commonAncestorContainer; 
     344    if ( !range.collapsed && range.startContainer == range.endContainer && 
     345        range.startOffset - range.endOffset <= 1 && range.startContainer.hasChildNodes() ) 
     346    { 
     347      p = range.startContainer.childNodes[range.startOffset]; 
     348    } 
     349 
     350    while ( p.nodeType == 3 ) 
     351    { 
     352      p = p.parentNode; 
     353    } 
     354    return p; 
     355  } 
     356  catch (ex) 
     357  { 
     358    return null; 
     359  } 
     360}; 
     361 
     362/** 
     363 * Returns the selected element, if any.  That is, 
     364 * the element that you have last selected in the "path" 
     365 * at the bottom of the editor, or a "control" (eg image) 
     366 * 
     367 * @returns null | DomNode 
     368 */ 
     369 
     370Xinha.prototype.activeElement = function(sel) 
     371{ 
     372  if ( ( sel === null ) || this._selectionEmpty(sel) ) 
     373  { 
     374    return null; 
     375  } 
     376 
     377  // For Mozilla we just see if the selection is not collapsed (something is selected) 
     378  // and that the anchor (start of selection) is an element.  This might not be totally 
     379  // correct, we possibly should do a simlar check to IE? 
     380  if ( !sel.isCollapsed ) 
     381  {       
     382    if ( sel.anchorNode.childNodes.length > sel.anchorOffset && sel.anchorNode.childNodes[sel.anchorOffset].nodeType == 1 ) 
     383    { 
     384      return sel.anchorNode.childNodes[sel.anchorOffset]; 
     385    } 
     386    else if ( sel.anchorNode.nodeType == 1 ) 
     387    { 
     388      return sel.anchorNode; 
     389    } 
     390    else 
     391    { 
     392      return null; // return sel.anchorNode.parentNode; 
     393    } 
     394  } 
     395  return null; 
     396}; 
     397 
     398/**  
     399 * Determines if the given selection is empty (collapsed). 
     400 * @param selection Selection object as returned by getSelection 
     401 * @returns true|false 
     402 */ 
     403  
     404Xinha.prototype.selectionEmpty = function(sel) 
     405{ 
     406  if ( !sel ) 
     407  { 
     408    return true; 
     409  } 
     410 
     411  if ( typeof sel.isCollapsed != 'undefined' ) 
     412  {       
     413    return sel.isCollapsed; 
     414  } 
     415 
     416  return true; 
     417}; 
     418 
     419 
     420/** 
     421 * Selects the contents of the given node.  If the node is a "control" type element, (image, form input, table) 
     422 * the node itself is selected for manipulation. 
     423 * 
     424 * @param node DomNode  
     425 * @param pos  Set to a numeric position inside the node to collapse the cursor here if possible.  
     426 */ 
     427  
     428Xinha.prototype.selectNodeContents = function(node, pos) 
     429{ 
     430  this.focusEditor(); 
     431  this.forceRedraw(); 
     432  var range; 
     433  var collapsed = typeof pos == "undefined" ? true : false; 
     434  var sel = this._getSelection(); 
     435  range = this._doc.createRange(); 
     436  // Tables and Images get selected as "objects" rather than the text contents 
     437  if ( collapsed && node.tagName && node.tagName.toLowerCase().match(/table|img|input|textarea|select/) ) 
     438  { 
     439    range.selectNode(node); 
     440  } 
     441  else 
     442  { 
     443    range.selectNodeContents(node); 
     444    //(collapsed) && range.collapse(pos); 
     445  } 
     446  sel.removeAllRanges(); 
     447  sel.addRange(range); 
     448}; 
     449   
     450/** Insert HTML at the current position, deleting the selection if any.  
     451 *   
     452 *  @param html string 
     453 */ 
     454  
     455Xinha.prototype.insertHTML = function(html) 
     456{ 
     457  var sel = this._getSelection(); 
     458  var range = this._createRange(sel); 
     459  this.focusEditor(); 
     460  // construct a new document fragment with the given HTML 
     461  var fragment = this._doc.createDocumentFragment(); 
     462  var div = this._doc.createElement("div"); 
     463  div.innerHTML = html; 
     464  while ( div.firstChild ) 
     465  { 
     466    // the following call also removes the node from div 
     467    fragment.appendChild(div.firstChild); 
     468  } 
     469  // this also removes the selection 
     470  var node = this.insertNodeAtSelection(fragment); 
     471}; 
     472 
     473/** Get the HTML of the current selection.  HTML returned has not been passed through outwardHTML. 
     474 * 
     475 * @returns string 
     476 */ 
     477  
     478Xinha.prototype.getSelectedHTML = function() 
     479{ 
     480  var sel = this._getSelection(); 
     481  var range = this._createRange(sel); 
     482  return Xinha.getHTML(range.cloneContents(), false, this); 
     483}; 
     484   
     485 
     486/** Get a Selection object of the current selection.  Note that selection objects are browser specific. 
     487 * 
     488 * @returns Selection 
     489 */ 
     490  
     491Xinha.prototype.getSelection = function() 
     492{ 
     493  return this._iframe.contentWindow.getSelection(); 
     494}; 
     495   
     496/** Create a Range object from the given selection.  Note that range objects are browser specific. 
     497 * 
     498 *  @param sel Selection object (see getSelection) 
     499 *  @returns Range 
     500 */ 
     501  
     502Xinha.prototype.createRange = function(sel) 
     503{ 
     504  this.activateEditor(); 
     505  if ( typeof sel != "undefined" ) 
     506  { 
     507    try 
     508    { 
     509      return sel.getRangeAt(0); 
     510    } 
     511    catch(ex) 
     512    { 
     513      return this._doc.createRange(); 
     514    } 
     515  } 
     516  else 
     517  { 
     518    return this._doc.createRange(); 
     519  } 
     520}; 
     521 
     522/** Determine if the given event object is a keydown/press event. 
     523 * 
     524 *  @param event Event  
     525 *  @returns true|false 
     526 */ 
     527  
     528Xinha.prototype.isKeyEvent = function(event) 
     529{ 
     530  return event.type == "keypress"; 
     531} 
     532 
     533/** Return the HTML string of the given Element, including the Element. 
     534 *  
     535 * @param element HTML Element DomNode 
     536 * @returns string 
     537 */ 
     538  
     539Xinha.getOuterHTML = function(element) 
     540{ 
     541  return (new XMLSerializer()).serializeToString(element); 
     542}; 
Note: See TracChangeset for help on using the changeset viewer.