Changeset 1335


Ignore:
Timestamp:
02/03/18 07:37:27 (3 weeks ago)
Author:
gogo
Message:

#1226 - Gecko problems with paraHandlerBest

I actually checked the existing paraHandlerBest in FF 58 to see if I could reproduce the problem still and I couldn't so maybe FF fixed it anyway in the 7 years since it was first reported.

But despite that I'm committing the paraHandlerBest.js contributed by ejucovy and douglas, since it seems to work just as well and they probably actually use Firefox, so, yeah.

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/modules/Gecko/paraHandlerBest.js

    r694 r1335  
    3030// "constants" 
    3131 
     32// Set up the node type constants for browsers that don't support them. 
     33var ELEMENT_NODE = ELEMENT_NODE || 1; 
     34var ATTRIBUTE_NODE = ATTRIBUTE_NODE || 2; 
     35var TEXT_NODE = TEXT_NODE || 3; 
     36var COMMENT_NODE = COMMENT_NODE || 8; 
     37var DOCUMENT_NODE = DOCUMENT_NODE || 9; 
    3238/** 
    3339* Whitespace Regex 
     
    4753 
    4854EnterParagraphs.prototype._pContainers = /^(body|del|div|fieldset|form|ins|map|noscript|object|td|th)$/i; 
     55EnterParagraphs.prototype._pWrapper = /^(body|d[ltd]|table|[uo]l|div|p|h[1-6]|li|t[hrd]|del|fieldset|ins|form|map|noscript|object|address|blockquote|pre)$/i; 
    4956 
    5057/** 
     
    6774 
    6875/** 
    69 * Elements which should get a new P, before or after, when enter is pressed at either end 
     76* When the cursor is at the inside edge of one of these elements, we will move the cursor just outside the element, and insert a P element there. 
    7077*/ 
    7178 
     
    7481 
    7582/** 
    76 * Elements which should get a new P, before or after a close parent, when enter is pressed at either end 
     83* When the cursor is at the inside edge of one of these elements, and this element's outside edge is just at the inside edge of its immediate parent, 
     84* we will move the cursor to the outside edge of the immediate parent, and insert a P element there. 
    7785*/ 
    7886 
     
    430438{ 
    431439   
    432   var next = function(element, search_direction) 
    433         { 
     440  var next = function(element, search_direction) { 
    434441    return ( search_direction == "left" ? element.previousSibling : element.nextSibling ); 
    435         }; 
     442  }; 
    436443   
    437444  var node = search_direction == "left" ? rng.startContainer : rng.endContainer; 
     
    442449  // be on the exclusion list and we wouldn't know until it was too late 
    443450   
    444   while ( start.nodeType == 1 && !this._permEmpty.test(start.nodeName) ) 
    445   { 
     451  while ( start.nodeType == 1 && !this._permEmpty.test(start.nodeName) ) { 
    446452    start = ( offset ? start.lastChild : start.firstChild ); 
    447453  } 
     
    458464  // sometimes this loop finds a blank text node, sometimes it doesn't. 
    459465   
    460   while ( roam = roam ? ( next(roam,search_direction) ? next(roam,search_direction) : roam.parentNode ) : start ) 
    461   { 
    462      
     466  roam = roam ? ( next(roam,search_direction) ? next(roam,search_direction) : roam.parentNode ) : start; 
     467  while (roam) { 
     468 
    463469    // next() is an inline function defined above that returns the next node depending 
    464470    // on the direction we're searching. 
    465      
    466     if ( next(roam,search_direction) ) 
    467     { 
    468        
     471 
     472    if ( next(roam,search_direction) ) { 
     473 
    469474      // If the next sibling's on the exclusion list, stop before it 
    470        
    471       if ( this._pExclusions.test(next(roam,search_direction).nodeName) ) 
    472       { 
    473          
     475 
     476      if ( this._pExclusions.test(next(roam,search_direction).nodeName) ) { 
     477 
    474478        return this.processRng(rng, search_direction, roam, next(roam,search_direction), (search_direction == "left"?'AfterEnd':'BeforeBegin'), true, false); 
    475479      } 
    476     } 
    477     else 
    478     { 
    479        
     480    } else { 
     481 
    480482      // If our parent's on the container list, stop inside it 
    481        
    482       if (this._pContainers.test(roam.parentNode.nodeName)) 
    483       { 
    484          
     483 
     484      if (this._pContainers.test(roam.parentNode.nodeName)) { 
     485 
    485486        return this.processRng(rng, search_direction, roam, roam.parentNode, (search_direction == "left"?'AfterBegin':'BeforeEnd'), true, false); 
    486       } 
    487       else if (this._pExclusions.test(roam.parentNode.nodeName)) 
    488       { 
    489          
     487      } else if (this._pExclusions.test(roam.parentNode.nodeName)) { 
     488 
    490489        // chop without wrapping 
    491          
    492         if (this._pBreak.test(roam.parentNode.nodeName)) 
    493         { 
    494            
     490 
     491        if (this._pBreak.test(roam.parentNode.nodeName)) { 
     492 
    495493          return this.processRng(rng, search_direction, roam, roam.parentNode, 
    496494            (search_direction == "left"?'AfterBegin':'BeforeEnd'), false, (search_direction == "left" ?true:false)); 
    497         } 
    498         else 
    499         { 
    500            
     495        } else { 
     496 
    501497          // the next(roam,search_direction) in this call is redundant since we know it's false 
    502498          // because of the "if next(roam,search_direction)" above. 
     
    504500          // the final false prevents this range from being wrapped in <p>'s most likely 
    505501          // because it's already wrapped. 
    506            
     502 
    507503          return this.processRng(rng, 
    508504            search_direction, 
     
    515511      } 
    516512    } 
     513    roam = roam ? ( next(roam,search_direction) ? next(roam,search_direction) : roam.parentNode ) : start; 
    517514  } 
    518515   
     
    781778  if (ev.keyCode == 13 && !ev.shiftKey && this.editor._iframe.contentWindow.getSelection) 
    782779  { 
    783     return this.handleEnter(ev); 
     780    return this.breakLine(ev, this.editor._doc); 
    784781  } 
    785782   
     
    789786 
    790787/** 
     788* Helper function to find the index of the given node with its parent's 
     789* childNodes array.  If there is any problem with the lookup, we'll return 
     790* NULL. 
     791*/ 
     792 
     793EnterParagraphs.prototype.indexInParent = function (el) 
     794{ 
     795  if (!el.parentNode || !el.parentNode.childNodes) 
     796  { 
     797    // The element is at the root of the tree, or it's a broken node. 
     798    return null; 
     799  } 
     800 
     801  for (var index=0; index<el.parentNode.childNodes.length; ++index) 
     802  { 
     803    if (el == el.parentNode.childNodes[index]) 
     804    { 
     805      return index; 
     806    } 
     807  } 
     808 
     809  // This will only happen if the DOM node is broken... 
     810  return null; 
     811} 
     812 
     813/* 
     814* Determine if a cursor points to the end of it's containing node. 
     815*/ 
     816EnterParagraphs.prototype.cursorAtEnd = function (cursorNode, cursorOffset) 
     817{ 
     818  if (cursorNode.nodeType == TEXT_NODE) 
     819  { 
     820    if (cursorOffset == cursorNode.nodeValue.length) 
     821    { 
     822      return true; 
     823    } 
     824    // We're in the middle of a text node.  If the node is a whitespace node, 
     825    // we'll ignore it and treat it as if the cursor were after the node, and 
     826    // not in it. 
     827    if (/\S/.test(cursorNode.nodeValue)) 
     828    { 
     829      return false; 
     830    } 
     831    cursorOffset = this.indexInParent(cursorNode) + 1; 
     832    cursorNode = cursorNode.parentNode; 
     833     
     834    // We need to make sure we there wasn't an error in indexInParent 
     835    if (cursorOffset === null) 
     836    { 
     837      return false; 
     838    } 
     839  } 
     840  // The easy case, it's after the last node... 
     841  if (cursorOffset == cursorNode.childNodes.length) 
     842  { 
     843    return true; 
     844  } 
     845  // At this point, if the pointed to node is a whitespace node, and all of 
     846  // it's nextSiblings are also whitespace node, then the cursor is at the end 
     847  // of the node. 
     848  for (var node = cursorNode.childNodes[cursorOffset]; node; node = node.nextSibling) 
     849  { 
     850    if ((node.nodeType != TEXT_NODE) || (/\S/.test(node.nodeValue))) 
     851    { 
     852      return false; 
     853    } 
     854  } 
     855  return true; 
     856} 
     857/* 
     858* Test suite for this, because it's really tough to get right. 
     859*/ 
     860EnterParagraphs.RunTests = function(xinha, debug) 
     861{ 
     862  var test = function(message, before, cursorBefore, after, cursorAfter, cursorAfter2) { 
     863    console.group('Test: ', message); 
     864    if (before !== null) { 
     865      xinha.setHTML(before); 
     866    } 
     867    // Do something 
     868    var cAnchor, cOffset; 
     869 
     870    var mockEvent = { 
     871      preventDefault: function() {if (debug) console.log("Preventing default.");}, 
     872      stopPropagation: function() {if (debug) console.log("Stopping propagation.");}, 
     873    } 
     874    function setCursor(commands) { 
     875      cAnchor = xinha._doc.body; 
     876      cOffset = 0; 
     877      try { 
     878        for (var index=0; index<commands.length; ++index) { 
     879          var command = commands[index]; 
     880          if ('id' == command[0]) { 
     881              cAnchor = xinha._doc.getElementById(command[1]); 
     882          } else if ('firsttag' == command[0]) { 
     883              cAnchor = xinha._doc.getElementsByTagName(command[1])[0]; 
     884          } else if ('child' == command[0]) { 
     885              cAnchor = cAnchor.childNodes[command[1]]; 
     886          } else if ('next' == command[0]) { 
     887            for (var next=command[1]; next > 0 && cAnchor.nextSibling; --next) { 
     888              cAnchor = cAnchor.nextSibling; 
     889            } 
     890          } else if ('previous' == command[0]) { 
     891            for (var previous=command[1]; previous > 0 && cAnchor.previousSibling; --previous) { 
     892              cAnchor = cAnchor.previousSibling; 
     893            } 
     894          } else if ('offset' == command[0]) { 
     895            if (command[1] == 'length') { 
     896              if (TEXT_NODE == cAnchor.nodeType) { 
     897                cOffset = cAnchor.nodeValue.length; 
     898              } else { 
     899                cOffset = cAnchor.childNodes.length; 
     900              } 
     901            } else if (command[1] < 0) { 
     902              if (TEXT_NODE == cAnchor.nodeType) { 
     903                cOffset = cAnchor.nodeValue.length + command[1]; 
     904              } else { 
     905                cOffset = cAnchor.childNodes.length + command[1]; 
     906              } 
     907            } else { 
     908              cOffset = command[1]; 
     909            } 
     910          } 
     911        } 
     912      } catch(e) { 
     913        cAnchor = null; 
     914        cOffset = null; 
     915      } 
     916    } 
     917 
     918    setCursor(cursorBefore); 
     919 
     920    var selection = xinha.getSelection(); 
     921    var range = xinha.createRange(selection); 
     922 
     923    range.setStart(cAnchor, cOffset); 
     924    range.setEnd(cAnchor, cOffset); 
     925    selection.removeAllRanges(); 
     926    selection.addRange(range); 
     927 
     928    // Breakline 
     929    try { 
     930      xinha.plugins['EnterParagraphs'].instance.breakLine(mockEvent, xinha._doc); 
     931    } catch (e) { 
     932      console.error('Breakline threw exception ', e); 
     933      console.groupEnd(); 
     934      return; 
     935    } 
     936 
     937    var selection = xinha.getSelection(); 
     938    var range = xinha.createRange(selection); 
     939 
     940    setCursor(cursorAfter); 
     941 
     942    if ((selection.anchorNode != cAnchor) || (selection.anchorOffset != cOffset)) { 
     943      // Sometimes there are multiple equivalent selection, let's see if we received alternatives. 
     944      if (typeof cursorAfter2 != 'undefined') { 
     945        setCursor(cursorAfter2); 
     946        if ((selection.anchorNode != cAnchor) || (selection.anchorOffset != cOffset)) { 
     947          console.error('Actual anchor: ' + selection.anchorNode + 
     948                       '\nActual offset: ' + selection.anchorOffset + 
     949                       '\nExpected anchor: ' + cAnchor + 
     950                       '\nExpected offset: ' + cOffset); 
     951        } 
     952      } else { 
     953        console.error('Actual anchor: ' + selection.anchorNode + 
     954                     '\nActual offset: ' + selection.anchorOffset + 
     955                     '\nExpected anchor: ' + cAnchor + 
     956                     '\nExpected offset: ' + cOffset); 
     957      } 
     958    } 
     959 
     960    result = xinha.getInnerHTML(); 
     961    if (result.trim() == after.trim()) { 
     962      console.info('Success!'); 
     963    } else { 
     964      console.error('Was: \n`' + before + 
     965                   '`\nExpected: \n`' + after + 
     966                   '`\nGot: \n`' + result + '`'); 
     967    } 
     968    console.groupEnd(); 
     969  } 
     970  contentBackup = xinha.getInnerHTML(); 
     971xinha.setHTML(""); 
     972  console.group('Running tests:'); 
     973  /* 
     974     The initial content on browser load seems to be: 
     975     <body><br />\n</body> 
     976     That's a break tag and a whitespace text node containing a newline character. 
     977    */ 
     978  test('Initial Xinha Content', 
     979       null, [], 
     980       '<p>&nbsp;</p><p><br></p>\n', [['child', 1]]);  // Mozilla kicks off a trailing newline.  Do I care about this? 
     981  test('Initial Xinha Content: Recreated', 
     982       '<br>\n', [], 
     983       '<p>&nbsp;</p><p><br></p>\n', [['child', 1]]);  // Mozilla kicks off a trailing newline.  Do I care about this? 
     984 
     985  test('Empty Body', 
     986       '', [], 
     987       '<p>&nbsp;</p><p><br></p>', [['child', 1]], 
     988                                   [['child', 1], ['child', 0]]); 
     989 
     990  test('Text node in body: text node', 
     991       'Hi', [], // Point to text node 
     992       '<p>&nbsp;</p><p>Hi</p>', [['child', 1]], 
     993                                 [['child', 1], ['child', 0]]); 
     994  test('Text node in body: first char', 
     995       'Hi', [['child', 0]], // Point to 'H' 
     996       '<p>&nbsp;</p><p>Hi</p>', [['child', 1]], 
     997                                 [['child', 1], ['child', 0]]); 
     998  test('Text node in body: split text', 
     999       'Hi', [['child', 0], ['offset', 1]], // Point to 'i' 
     1000       '<p>H</p><p>i</p>', [['child', 1]], 
     1001                           [['child', 1], ['child', 0]]); 
     1002  test('Text node in body: after text', 
     1003       'Hi', [['child', 0], ['offset', 'length']], // Point after 'i' 
     1004       '<p>Hi</p><p>&nbsp;</p>', [['child', 1]], 
     1005                                 [['child', 1], ['child', 0]]); 
     1006  test('Text node in body: after text node', 
     1007       'Hi', [['offset', 'length']], // Point after text node 
     1008       'Hi<p>&nbsp;</p>', [['child', 1]],  // This is not ideal output, but the line breaker never sees the text node and 
     1009                          [['child', 1], ['child', 0]]); // so can't do anything about it. 
     1010 
     1011  // For the next two tests, Douglas thinks the ideal output would be (either of): 
     1012  //     [['child', 1], ['child', 0]], 
     1013  //     [['child', 1], ['child', 0], ['child', 0]]); 
     1014  // That is, with the cursor inside the <em> node. 
     1015  // I (ejucovy) think that the output [['child', 1]], inside the second <p> 
     1016  // but outside the <em>, is better.  It's also what we're actually getting back 
     1017  // from the browser, so let's go with that and leave this here for posterity.. 
     1018  test('Body with inline tag: em node', 
     1019       '<em>hi</em>', [], // Point to document body 
     1020       '<p>&nbsp;</p><p><em>hi</em></p>', [['child', 1]]); 
     1021  test('Body with inline em: inside em node', 
     1022       '<em>hi</em>', [['child', 0]], // Point to beginning of em node (before h) 
     1023       '<p>&nbsp;</p><p><em>hi</em></p>', [['child', 1]]); 
     1024 
     1025  test('Body with inline tag: text node', 
     1026       '<em>hi</em>', [['child', 0]], 
     1027       '<p>&nbsp;</p><p><em>hi</em></p>', [['child', 1], ['child', 0]], 
     1028                                          [['child', 1], ['child', 0], ['child', 0]]); 
     1029  test('Body with inline tag: first char', 
     1030       '<em>hi</em>', [['child', 0], ['child', 0]], 
     1031       '<p>&nbsp;</p><p><em>hi</em></p>', [['child', 1], ['child', 0]], 
     1032                                          [['child', 1], ['child', 0], ['child', 0]]); 
     1033  test('Body with inline tag: split text', 
     1034       '<em>hi</em>', [['child', 0], ['child', 0], ['offset', 1]], 
     1035       '<p><em>h</em></p><p><em>i</em></p>', [['child', 1], ['child', 0]], 
     1036                                             [['child', 1], ['child', 0], ['child', 0]]); 
     1037  test('Body with inline tag: after text', 
     1038       '<em>hi</em>', [['child', 0], ['child', 0], ['offset', 'length']], 
     1039       '<p><em>hi</em></p><p>&nbsp;</p>', [['child', 1], ['child', 0]], 
     1040                                          [['child', 1], ['child', 0], ['child', 0]]); 
     1041  // I hate that this is expected behavior, but the split code doesn't see the em tag in these two cases. 
     1042  test('Body with inline tag: after text node', 
     1043       '<em>hi</em>', [['child', 0], ['offset', 'length']], 
     1044       '<em>hi</em><p>&nbsp;</p>', [['child', 1], ['child', 0]], 
     1045                                          [['child', 1], ['child', 0], ['child', 0]]); 
     1046  test('Body with inline tag: after em node', 
     1047       '<em>hi</em>', [['offset', 'length']], 
     1048       '<em>hi</em><p>&nbsp;</p>', [['child', 1], ['child', 0]], 
     1049                                          [['child', 1], ['child', 0], ['child', 0]]); 
     1050 
     1051  /***************  Repeat the header block for each header level once the tests are passing *********************/ 
     1052  test('Split header 1: h1 node', 
     1053       '<h1>hi</h1>', [], 
     1054       '<p>&nbsp;</p><h1>hi</h1>', [['child', 1]], 
     1055                                   [['child', 1], ['child', 0]]); 
     1056  test('Split header 1: text node', 
     1057       '<h1>hi</h1>', [['child', 0]], 
     1058       '<p>&nbsp;</p><h1>hi</h1>', [['child', 1]], 
     1059                                   [['child', 1], ['child', 0]]); 
     1060  test('Split header 1: first char', 
     1061       '<h1>hi</h1>', [['child', 0], ['child', 0]], 
     1062       '<p>&nbsp;</p><h1>hi</h1>', [['child', 1]], 
     1063                                   [['child', 1], ['child', 0]]); 
     1064  test('Split header 1: split text', 
     1065       '<h1>hi</h1>', [['child', 0], ['child', 0], ['offset', 1]], 
     1066       '<h1>h</h1><h1>i</h1>', [['child', 1]], 
     1067                               [['child', 1], ['child', 0]]); 
     1068  test('Split header 1: after text', 
     1069       '<h1>hi</h1>', [['child', 0], ['child', 0], ['offset', 'length']], 
     1070       '<h1>hi</h1><p>&nbsp;</p>', [['child', 1]], 
     1071                                   [['child', 1], ['child', 0]]); 
     1072  test('Split header 1: after text node', 
     1073       '<h1>hi</h1>', [['child', 0], ['offset', 'length']], 
     1074       '<h1>hi</h1><p>&nbsp;</p>', [['child', 1]], 
     1075                                   [['child', 1], ['child', 0]]); 
     1076  test('Split header 1: after h1 node', 
     1077       '<h1>hi</h1>', [['offset', 'length']], 
     1078       '<h1>hi</h1><p>&nbsp;</p>', [['child', 1]], 
     1079                                   [['child', 1], ['child', 0]]); 
     1080 
     1081  console.groupEnd(); 
     1082  xinha.setHTML(contentBackup); 
     1083  // EnterParagraphs.RunTests(xinha_editors['myTextArea']) 
     1084} 
     1085/* 
     1086* Determine if a cursor points to the end of it's containing node. 
     1087*/ 
     1088EnterParagraphs.prototype.cursorAtBeginning = function (cursorNode, cursorOffset) 
     1089{ 
     1090  if (cursorOffset == 0) 
     1091  { 
     1092    return true; 
     1093  } 
     1094  if (cursorNode.nodeType == TEXT_NODE) 
     1095  { 
     1096    // We're in the middle of a text node.  If the node is a whitespace node, 
     1097    // we'll ignore it and treat it as if the cursor were at the beginning of 
     1098    // the node, and not in it. 
     1099    if (/\S/.test(cursorNode.nodeValue)) 
     1100    { 
     1101      return false; 
     1102    } 
     1103    cursorOffset = this.indexInParent(cursorNode); 
     1104    cursorNode = cursorNode.parentNode; 
     1105     
     1106    // We need to make sure we there wasn't an error in indexInParent 
     1107    if (cursorOffset === null) 
     1108    { 
     1109      return false; 
     1110    } 
     1111 
     1112    // We have to check the new offset for the easy case. 
     1113    if (cursorOffset == 0) 
     1114    { 
     1115      return true; 
     1116    } 
     1117  } 
     1118  // At this point, if all of the nodes before the cursor are white space 
     1119  // nodes, then the cursor is at the beginning of the node. 
     1120  for (var node = cursorNode.childNodes[cursorOffset-1]; node; node = node.previousSibling) 
     1121  { 
     1122    if ((node.nodeType != TEXT_NODE) || (/\S/.test(node.nodeValue))) 
     1123    { 
     1124      return false; 
     1125    } 
     1126  } 
     1127  return true; 
     1128} 
     1129/** 
    7911130* Handles the pressing of an unshifted enter for Gecko 
    7921131*/ 
     1132 
     1133EnterParagraphs.prototype.breakLine = function(ev, doc) 
     1134{ 
     1135  // Helper function that copies a DOM element and its attributes (except the 
     1136  // id) without any of the contents. 
     1137  var safeShallowCopy = function(node, doc) 
     1138  { 
     1139    var copy = doc.createElement(node.nodeName); 
     1140    for (var index=0; index < node.attributes.length; ++index) 
     1141    { 
     1142      var attr = node.attributes[index]; 
     1143      if ('id' != attr.name.toLowerCase()) 
     1144      { 
     1145        copy.setAttribute(attr.name, attr.value); 
     1146      } 
     1147    } 
     1148    return copy; 
     1149  } 
     1150 
     1151  // Helper function that will get the node immediately following the current 
     1152  // node, but without descending into children nodes.  When looking at the 
     1153  // markup of the document, this means that if a node to the right of this 
     1154  // node in the text is at a lower depth in the DOM tree, than we will return 
     1155  // it's first parent that is at our depth our higher in the tree. 
     1156  var nextRootNode = function(node) 
     1157  { 
     1158    if (node.nextSibling) 
     1159    { 
     1160      return node.nextSibling; 
     1161    } 
     1162    for (var nextRoot = node.parentNode;nextRoot;nextRoot = nextRoot.parentNode) 
     1163    { 
     1164      if (nextRoot.nextSibling) 
     1165      { 
     1166        return nextRoot.nextSibling; 
     1167      } 
     1168    } 
     1169  } 
     1170 
     1171  // A cursor is specified by a node and an offset, so we will split at that 
     1172  // location.  It should be noted that if splitNode is a text node, 
     1173  // splitOffset is an offset into the text contents.  If not, it is an index 
     1174  // into the childNodes array. 
     1175  var splitTree = function(root, splitNode, splitOffset, doc) 
     1176  { 
     1177    // Split root into two. 
     1178    var breaker = safeShallowCopy(root, doc); 
     1179    if (root.nextSibling) 
     1180    { 
     1181      breaker = root.parentNode.insertBefore(breaker,root.nextSibling); 
     1182    } 
     1183    else 
     1184    { 
     1185      breaker = root.parentNode.appendChild(breaker); 
     1186    } 
     1187 
     1188    var insertNode = breaker; 
     1189    // XXX TODO don't use a closure to access this, pass it in... 
     1190    for (;recreateStack.length>0;) 
     1191    { 
     1192      var stackEl = safeShallowCopy(recreateStack.pop(), doc) 
     1193      insertNode.appendChild(stackEl); 
     1194      // Move content here 
     1195      insertNode = stackEl; 
     1196    } 
     1197 
     1198    // We need to keep track of the new cursor location.  When our cursor is in 
     1199    // the middle of a text node, the new cursor will be at the beginning of 
     1200    // the text node we create to contain the text to the right of the cursor. 
     1201    // Otherwise, the cursor will point to a node, and the new cursor needs to 
     1202    // point to that node in it's new location. 
     1203    var newCursorNode = null; 
     1204 
     1205    var sourceNode = splitNode; 
     1206    var sourceOffset = splitOffset; 
     1207    if (TEXT_NODE == sourceNode.nodeType) 
     1208    { 
     1209      var textNode = doc.createTextNode(sourceNode.nodeValue.substring(splitOffset,sourceNode.nodeValue.length)); 
     1210      newCursorNode = textNode = insertNode.appendChild(textNode); 
     1211      sourceNode.nodeValue = sourceNode.nodeValue.substring(0,splitOffset); 
     1212    } 
     1213 
     1214    // When splitting a tree, we need to take any nodes that are after the 
     1215    // split and move them into their location in the new tree.  We can have 
     1216    // siblings at each level of the tree, so we need to walk from the inside 
     1217    // of the source outwards, and move the offending nodes to the equivalent 
     1218    // position on the newly duplicated tree. 
     1219 
     1220    // Move insertNode from the inside outwards towards the root, moving any 
     1221    // content nodes as we go.  We'll make sure that we can do the same with 
     1222    // sourceNode 
     1223    while (insertNode != root.parentNode) 
     1224    { 
     1225      for (var moveNode=sourceNode.childNodes[sourceOffset];moveNode;) 
     1226      { 
     1227        // We have to take a reference to the next sibling before cutting out 
     1228        // of the tree, or we will lose our place. 
     1229 
     1230        // nextNode can potentially be null.  This is not a problem. 
     1231        var nextNode = moveNode.nextSibling; 
     1232        var cutNode = moveNode.parentNode.removeChild(moveNode); 
     1233        insertNode.appendChild(cutNode); 
     1234        moveNode = nextNode; 
     1235      } 
     1236 
     1237      // Move both of our node pointers one step closer to the root node. 
     1238      sourceOffset = EnterParagraphs.prototype.indexInParent(sourceNode); 
     1239      sourceNode = sourceNode.parentNode; 
     1240      insertNode = insertNode.parentNode; 
     1241    } 
     1242 
     1243    // Below code needs to check for element node with empty text node. 
     1244    // An empty node is an text node of zero length or an element node with no 
     1245    // children, or whose only children are zero-length text nodes. 
     1246    var emptyNode = function(node) 
     1247    { 
     1248      if ((TEXT_NODE == node.nodeType) && (0 == node.nodeValue.length)) 
     1249      { 
     1250        // Text nodes are empty if there is no text. 
     1251        return true; 
     1252      } 
     1253 
     1254      if (ELEMENT_NODE == node.nodeType) 
     1255      { 
     1256        for (var child = node.firstChild; child; child = child.nextSibling) 
     1257        { 
     1258          if ((ELEMENT_NODE == child.nodeType) || (0 != child.nodeValue.length)) 
     1259          { 
     1260            // If there are any element children, or text nodes with text in 
     1261            // them, this node is not empty. 
     1262            return false; 
     1263          } 
     1264        } 
     1265 
     1266        // node has no childNodes that are elements and no childNodes that are 
     1267        // text nodes with text in them. 
     1268        return true; 
     1269      } 
     1270 
     1271      return false; 
     1272    } 
     1273 
     1274    var stuffEmptyNode = function(node, doc) 
     1275    { 
     1276      if (!emptyNode(node)) 
     1277      { 
     1278        return; 
     1279      } 
     1280 
     1281      if (TEXT_NODE == node.nodeType) 
     1282      { 
     1283        // Unicode equivalent of non breaking whitespace. 
     1284        node.nodeValue = '\u00a0'; 
     1285      } 
     1286      else if (0 == node.childNodes.length) 
     1287      { 
     1288        // Unicode equivalent of non breaking whitespace. 
     1289        node.appendChild(doc.createTextNode('\u00a0')); 
     1290      } 
     1291      else 
     1292      { 
     1293        // Unicode equivalent of non breaking whitespace. The node is empty, 
     1294        // but it has child nodes, so firstChild is guaranteed to be an empty 
     1295        // text node. 
     1296        node.firstChild.nodeValue = '\u00a0'; 
     1297      } 
     1298    } 
     1299 
     1300    // If the cursor node wasn't created by the split (it was moved), then that 
     1301    // means we need to point to the inside of our brand new tree. 
     1302    if (!newCursorNode) 
     1303    { 
     1304      newCursorNode = breaker.childNodes[0]; 
     1305    } 
     1306 
     1307    // Make sure when we split the tree that we don't leave any empty nodes, as 
     1308    // that would have visual glitches. 
     1309    stuffEmptyNode(splitNode, doc); 
     1310    stuffEmptyNode(newCursorNode, doc); 
     1311 
     1312    // So that we can correctly set the selection, we'll return a reference to 
     1313    // the inserted subtree. 
     1314    return newCursorNode; 
     1315  } 
     1316  var insertLineBreak = function(cursorParent, cursorOffset, useNewline, doc) 
     1317  { 
     1318    if (TEXT_NODE == cursorParent.nodeType) 
     1319    { 
     1320      // The cursor points inside of a text node, we insert the newline 
     1321      // directly into the text. 
     1322      var splitNode = cursorParent; 
     1323      var splitOffset = cursorOffset; 
     1324      if (useNewline) 
     1325      { 
     1326        splitNode.nodeValue = splitNode.nodeValue.substring(0,splitOffset) + '\n' + splitNode.nodeValue.substring(splitOffset,splitNode.nodeValue.length); 
     1327      } 
     1328      else 
     1329      { 
     1330        var newTextNode = doc.createTextNode(splitNode.nodeValue.substring(splitOffset,splitNode.nodeValue.length)); 
     1331        var newBreakNode = doc.createElement('br'); 
     1332        splitNode.nodeValue = splitNode.nodeValue.substring(0,splitOffset); 
     1333 
     1334        var appendIndex = EnterParagraphs.prototype.indexInParent(cursorParent); 
     1335        if (appendIndex == cursorParent.parentNode.length-1) 
     1336        { 
     1337          newBreakNode = cursorParent.appendChild(newBreakNode); 
     1338          newTextNode = cursorParent.appendChild(newTextNode); 
     1339        } 
     1340        else 
     1341        { 
     1342          newTextNode = cursorParent.insertBefore(newTextNode, cursorParent.parentNode.childNodes[appendIndex+1]); 
     1343          newBreakNode = cursorParent.insertBefore(newBreakNode, newTextNode); 
     1344        } 
     1345        return newBreakNode; 
     1346      } 
     1347    } 
     1348    else if (0 == cursorParent.childNodes.length) 
     1349    { 
     1350      // The cursor is inside an empty element or document node, so we insert a txt node or break element as necessary. 
     1351      if (useNewline) 
     1352      { 
     1353        var breakingNode = doc.createTextNode('\n'); 
     1354        cursorParent.appendChild(breakingNode); 
     1355      } 
     1356      else 
     1357      { 
     1358        var breakingNode = doc.createElement('br'); 
     1359        return cursorParent.appendChild(breakingNode); 
     1360      } 
     1361    } 
     1362    else if ((cursorOffset == cursorParent.childNodes.length) && (TEXT_NODE == cursorParent.childNodes[cursorOffset-1].nodeType)) 
     1363    { 
     1364      // The cursor is at the after the last node, and the previous node is a 
     1365      // text node where we can insert the newline. 
     1366      if (useNewline) 
     1367      { 
     1368        var lastTextNode = cursorParent.childNodes[cursorOffset-1]; 
     1369        lastTextNode.nodeValue = lastTextNode.nodeValue + '\n'; 
     1370      } 
     1371      else 
     1372      { 
     1373        var breakingNode = doc.createElement('br'); 
     1374        return cursorParent.appendChild(breakingNode); 
     1375      } 
     1376    } 
     1377    else if (cursorOffset == cursorParent.childNodes.length) 
     1378    { 
     1379      // The cursor is at the after the last node, and the previous node is an 
     1380      // not text, so we must insert a text node. 
     1381      if (useNewline) 
     1382      { 
     1383        var breakingNode = doc.createTextNode('\n'); 
     1384        cursorParent.appendChild(breakingNode); 
     1385      } 
     1386      else 
     1387      { 
     1388        var breakingNode = doc.createElement('br'); 
     1389        return cursorParent.appendChild(breakingNode); 
     1390      } 
     1391    } 
     1392    else if (TEXT_NODE == cursorParent.childNodes[cursorOffset].nodeType) 
     1393    { 
     1394      // The cursor points to a text node, insert our newline there. 
     1395      if (useNewline) 
     1396      { 
     1397        var splitNode = cursorParent.childNodes[cursorOffset]; 
     1398        splitNode.nodeValue = '\n' + splitNode.nodeValue; 
     1399      } 
     1400      else 
     1401      { 
     1402        var breakingNode = doc.createElement('br'); 
     1403        return cursorParent.insertBefore(breakingNode, cursorParent[cursorOffset]); 
     1404      } 
     1405    } 
     1406    else if (TEXT_NODE == cursorParent.childNodes[cursorOffset-1].nodeType) 
     1407    { 
     1408      // The cursor points to an non-text node, but there is a text node just 
     1409      // before where we can insert a newline. 
     1410      if (useNewline) 
     1411      { 
     1412        var splitNode = cursorParent.childNodes[cursorOffset-1]; 
     1413        splitNode.nodeValue = splitNode.nodeValue + '\n'; 
     1414      } 
     1415      else 
     1416      { 
     1417        var breakingNode = doc.createElement('br'); 
     1418        return cursorParent.insertBefore(breakingNode, cursorParent[cursorOffset]); 
     1419      } 
     1420    } 
     1421    else 
     1422    { 
     1423      // The cursor points between two non-text nodes, so we must insert a text 
     1424      // node. 
     1425      if (useNewline) 
     1426      { 
     1427        var breakingNode = doc.createTextNode('\n'); 
     1428        cursorParent.insertBefore(breakingNode, cursorParent.childNodes[cursorOffset]); 
     1429      } 
     1430      else 
     1431      { 
     1432        var breakingNode = doc.createElement('br'); 
     1433        return cursorParent.insertBefore(breakingNode, cursorParent.childNodes[cursorOffset]); 
     1434      } 
     1435    } 
     1436  } 
     1437 
     1438  /* *********************************************************************** 
     1439                                 CODE 
     1440     *********************************************************************** */ 
     1441  // In the case of the user pressing enter, we have to break the line somehow. 
     1442  // If there is anything already selected, we interpret that the user wishes 
     1443  // for the content to be deleted. 
     1444   
     1445  var selection = this.editor.getSelection(); 
     1446  var range = this.editor.createRange(selection); 
     1447   
     1448  selection.collapseToStart(); 
     1449  range.deleteContents(); 
     1450 
     1451  // We do some magic manipulation to help with user intent. 
     1452  this.moveCursorOnEdge(selection); 
     1453 
     1454  // Take a reference to the cursor. 
     1455  var cursorParent = selection.anchorNode; 
     1456  var cursorOffset = selection.anchorOffset; 
     1457 
     1458  // Now that we have an empty selection, the process of breaking the line is a 
     1459  // bit simpler.  Our strategy for breaking the line is as follows: 
     1460 
     1461  // We will modify the cursor position in an attempt to guess the user's 
     1462  // intent.  When the cursor is at the inside edge of certain elements, we 
     1463  // work under the assumption that the user wished to select just outside of 
     1464  // that element. As such, we will move the cursor to just outside the 
     1465  // element, and then continue. 
     1466 
     1467  // Next, we find the first non-inline element that contains our cursor. 
     1468  // These can be broken into four types: 
     1469  // 1) Definition lists and their elements (dl, dt, dd) 
     1470  // 2) Other lists (ul, ol) 
     1471  // 3) Other containers (body, div, tr, pre, etc.) 
     1472  // 4) Other block elements (p, h3, li, th, td, etc.) 
     1473  // 
     1474  // If we are inside a definition (1) list, we try to guess the users intent 
     1475  //   as to whether they want to insert* a new term or a new definition. 
     1476  // If we are inside any other list (2) element, we will insert* an li element. 
     1477  // If we are in any other container (3), we will insert* a p element. 
     1478  // If we are in any other block (4) element, we split the block into two 
     1479  //   pieces and move* anything after the cursor to the second block. 
     1480  // 
     1481  // *When inserting or moving content, we must be sure to look at any 
     1482  // inline elements that wrap the cursor, properly close them off, and create 
     1483  // the same group of wrapping inline elements in the inserted/moved 
     1484  // element. This logic is incorporated into splitTree. 
     1485 
     1486  // Find the first wrapping non-inline element. (1-5 above) 
     1487  if (ELEMENT_NODE == cursorParent.nodeType) 
     1488  { 
     1489      // When the cursor is on an element node, it's before that element in the 
     1490      // document, and so we only want to consider its parent for deciding what 
     1491      // to do. The same is true when the cursor points to just before a text 
     1492      // node, so we only need to check the cursorParent. 
     1493      var wrapNode = cursorParent; 
     1494  } 
     1495  else if (TEXT_NODE == cursorParent.nodeType) 
     1496  { 
     1497      // Since we know that a text node is not the wrapper, we'll start with 
     1498      // its parent. 
     1499      var wrapNode = cursorParent.parentNode; 
     1500  } 
     1501  else 
     1502  { 
     1503      // We are dealing with an XML document.  This should be expanded to 
     1504      // handle these cases. 
     1505      // http://www.w3schools.com/Dom/dom_nodetype.asp 
     1506      alert('You have selected a node from an XML document, type ' + 
     1507            cursorParent.nodeType + '.\nXML documents are not ' + 
     1508            'yet supported.'); 
     1509      // Let the browser deal with it. 
     1510      return true; 
     1511  } 
     1512 
     1513  // This is an array used as a stack for recreating the current 'state' of 
     1514  // the cursor.  (eg. If the cursor is inside of an em tag inside of a p, 
     1515  // we'll add the em to the stack so that we can recreate it while splitting 
     1516  // the p.) 
     1517  var recreateStack = []; 
     1518 
     1519  while (!EnterParagraphs.prototype._pWrapper.test(wrapNode.nodeName)) 
     1520  { 
     1521    recreateStack.push(wrapNode); 
     1522    wrapNode = wrapNode.parentNode; 
     1523 
     1524    if (!wrapNode) 
     1525    { 
     1526      // Broken DOM, let the browser handle it. 
     1527      return true; 
     1528    } 
     1529  } 
     1530 
     1531  if (wrapNode.nodeName.toLowerCase() in {pre:''}) 
     1532  { 
     1533    insertLineBreak(cursorParent, cursorOffset, true, doc); 
     1534    this.editor.updateToolbar(); 
     1535     
     1536    Xinha._stopEvent(ev); 
     1537     
     1538    range.setStart(cursorParent, cursorOffset+1); 
     1539    range.setEnd(cursorParent, cursorOffset+1); 
     1540    selection.removeAllRanges(); 
     1541    selection.addRange(range); 
     1542    return false; 
     1543  } 
     1544  else if (wrapNode.nodeName.toLowerCase() in {body:'',div:'',fieldset:'',form:'',map:'',noscript:'','object':'',blockquote:''}) 
     1545  { 
     1546    // We know that the there are no block elements between the cursor and the 
     1547    // wrapNode, but there may be inline elements.  What we'll do is take 
     1548    // everything in the tree below wrapNode, embed it into a P element, and 
     1549    // then split the whole thing. 
     1550 
     1551    // The cursor might be at the ending edge of the wrapNode. 
     1552    // 0. Pointing inside a completely empty element <body></body> 
     1553    // 1. Pointing to a text node <body>^This is text</body> 
     1554    // 2. Pointing to an inline node that is the child of the wrapNode.<body>^<em>text</em></body> 
     1555    // 3. Pointing to an inline node that is a non-direct descendant of the wrapNode.<body><q>^<em>text</em></q></body> 
     1556    // 4. Pointing to an inline node that is a non-direct descendant of the wrapNode.<body><q><em>text</em> this^</q></body> 
     1557    // 5. Pointing to the end of the wrapNode.<body>Here is some text.^</body> 
     1558    // 6. Pointing to a block node that is just inside of the wrapNode.<body>^<p>text</p></body> 
     1559 
     1560    // In the special case of a completely empty node, the cursor is still 
     1561    // visible, and the user expects to have two lines after hitting the enter 
     1562    // key.  We'll add two paragraphs to any of these nodes if they are empty. 
     1563    if (!wrapNode.firstChild) { 
     1564      var embedNode1 = doc.createElement('p'); 
     1565      var embedNode2 = doc.createElement('p'); 
     1566      embedNode1 = wrapNode.appendChild(embedNode1); 
     1567      embedNode2 = wrapNode.appendChild(embedNode2); 
     1568      // The unicode character below is a representation of a non-breaking 
     1569      // space we use to prevent the paragraph from having visual glitches. 
     1570      var emptyTextNode1 = doc.createTextNode('\u00a0'); 
     1571      var emptyTextNode2 = doc.createTextNode('\u00a0'); 
     1572      emptyTextNode1 = embedNode1.appendChild(emptyTextNode1); 
     1573      emptyTextNode2 = embedNode2.appendChild(emptyTextNode2); 
     1574 
     1575      Xinha._stopEvent(ev); 
     1576 
     1577      range.setStart(emptyTextNode2, 0); 
     1578      range.setEnd(emptyTextNode2, 0); 
     1579      selection.removeAllRanges(); 
     1580      selection.addRange(range); 
     1581 
     1582      return false; 
     1583    } 
     1584 
     1585    var startNode = cursorParent; 
     1586    for (;(startNode != wrapNode) && (startNode.parentNode != wrapNode);) 
     1587    { 
     1588      startNode = startNode.parentNode; 
     1589    } 
     1590 
     1591    if (TEXT_NODE == cursorParent.nodeType) 
     1592    { 
     1593      var treeRoot = cursorParent; 
     1594    } 
     1595    else if (cursorOffset == cursorParent.childNodes.length) 
     1596    { 
     1597      var embedNode = doc.createElement('p'); 
     1598      embedNode = wrapNode.appendChild(embedNode); 
     1599      // The unicode character below is a representation of a non-breaking 
     1600      // space we use to prevent the paragraph from having visual glitches. 
     1601      var emptyTextNode = doc.createTextNode('\u00a0'); 
     1602      emptyTextNode = embedNode.appendChild(emptyTextNode); 
     1603 
     1604      Xinha._stopEvent(ev); 
     1605 
     1606      range.setStart(emptyTextNode, 0); 
     1607      range.setEnd(emptyTextNode, 0); 
     1608      selection.removeAllRanges(); 
     1609      selection.addRange(range); 
     1610 
     1611      return false; 
     1612    } 
     1613    else 
     1614    { 
     1615      var treeRoot = cursorParent.childNodes[cursorOffset]; 
     1616    } 
     1617 
     1618    for (;wrapNode != treeRoot.parentNode;) 
     1619    { 
     1620      treeRoot = treeRoot.parentNode; 
     1621    } 
     1622 
     1623    // At this point, treeRoot points to the root of the subtree inside 
     1624    // wrapNode that containes our cursor.  If this happens to be a block level 
     1625    // element, we'll just insert a P node here.  Otherwise, we'll replace this 
     1626    // node with an empty P node, and then embed it into that P node. 
     1627 
     1628    if (EnterParagraphs.prototype._pWrapper.test(treeRoot.nodeName)) 
     1629    { 
     1630      var embedNode = doc.createElement('p'); 
     1631      embedNode = wrapNode.insertBefore(embedNode, treeRoot); 
     1632      // The unicode character below is a representation of a non-breaking 
     1633      // space we use to prevent the paragraph from having visual glitches. 
     1634      var emptyTextNode = doc.createTextNode('\u00a0'); 
     1635      emptyTextNode = embedNode.appendChild(emptyTextNode); 
     1636 
     1637      Xinha._stopEvent(ev); 
     1638 
     1639      range.setStart(treeRoot, 0); 
     1640      range.setEnd(treeRoot, 0); 
     1641      selection.removeAllRanges(); 
     1642      selection.addRange(range); 
     1643 
     1644      return false; 
     1645    } 
     1646    var embedNode = doc.createElement('p'); 
     1647 
     1648    treeRoot = wrapNode.replaceChild(embedNode, treeRoot); 
     1649 
     1650    treeRoot = embedNode.appendChild(treeRoot); 
     1651     
     1652    if ((TEXT_NODE == treeRoot.nodeType) && !/\S/.test(treeRoot.nodeValue)) 
     1653    { 
     1654      var newCursor = treeRoot; 
     1655    } 
     1656    else if (TEXT_NODE == treeRoot.nodeType) 
     1657    { 
     1658      var newCursor = splitTree(embedNode, treeRoot, cursorOffset, doc); 
     1659    } 
     1660    else 
     1661    { 
     1662      var parentOffset = this.indexInParent(treeRoot); 
     1663      if (null === parentOffset) 
     1664      { 
     1665        // We can't do anything with this cursor, so return. 
     1666        return; 
     1667      } 
     1668      var newCursor = splitTree(embedNode, treeRoot.parentNode, parentOffset, doc); 
     1669    } 
     1670  } 
     1671  else if (wrapNode.nodeName.toLowerCase() in {td:'',address:''}) 
     1672  { 
     1673    // Line breaks BR element 
     1674    var newCursor = insertLineBreak(cursorParent, cursorOffset, false, doc); 
     1675  } 
     1676  else if (wrapNode.nodeName.toLowerCase() in {dl:''}) 
     1677  { 
     1678    // Find the leftSibling of the cursorParent.  If none, insert dt (term) followed by dd (definition), 
     1679    // otherwise insert same as cursorParent followed by same as leftSibling. 
     1680    // Check to see if the leftSibling and rightSibling are the same and then just insert the one term. 
     1681    // XXX TODO 
     1682  } 
     1683  else if (wrapNode.nodeName.toLowerCase() in {h1:'',h2:'',h3:'',h4:'',h5:'',h6:'',p:''}) 
     1684  { 
     1685    // Split wrapNode into two. 
     1686    var newCursor = splitTree(wrapNode, cursorParent, cursorOffset, doc); 
     1687  } 
     1688  else if (wrapNode.nodeName.toLowerCase() in {dt:'',dd:'',li:''}) 
     1689  { 
     1690    // To the bane of software developers the world over, users expect to be 
     1691    // able to hit enter twice to end a list, whether at the end or in the 
     1692    // middle.  This means that we need to have special handling for list items 
     1693    // to check for the second return.  We do this by testing to see if the 
     1694    // current list item is empty, and if so, deleting it, splitting the list 
     1695    // into two if necessary, and inserting a paragraph. 
     1696    var newCursor = splitTree(wrapNode, cursorParent, cursorOffset, doc); 
     1697  } 
     1698  else if (wrapNode.nodeName.toLowerCase() in {ol:'',ul:''}) 
     1699  { 
     1700    // Insert li 
     1701    var breaker = doc.createElement('li'); 
     1702    if (TEXT_NODE == cursorParent.nodeType) 
     1703    { 
     1704      var newCursor = wrapNode.insertBefore(breaker,cursorParent); 
     1705    } 
     1706    else 
     1707    { 
     1708      var newCursor = wrapNode.insertBefore(breaker,cursorParent.childNodes[cursorOffset]); 
     1709    } 
     1710  } 
     1711 
     1712  this.editor.updateToolbar(); 
     1713   
     1714  Xinha._stopEvent(ev); 
     1715   
     1716  // We turn the newCursor node into a cursor and offset into the parent. 
     1717  var newOffset = 0; 
     1718  while (newCursor.parentNode.childNodes[newOffset] != newCursor) 
     1719  { 
     1720    newOffset++; 
     1721  } 
     1722  newCursor = newCursor.parentNode; 
     1723 
     1724  // Monkey the new cursor position into somewhere the user should actually be 
     1725  // typing. 
     1726   
     1727   
     1728  Xinha._stopEvent(ev); 
     1729  range.setStart(newCursor, newOffset); 
     1730  range.setEnd(newCursor, newOffset); 
     1731  selection.removeAllRanges(); 
     1732  selection.addRange(range); 
     1733  return false; 
     1734} 
     1735 
     1736/** 
     1737* If the cursor is on the edge of certain elements, we reposition it so that we 
     1738* can break the line in a way that's more useful to the user. 
     1739*/ 
     1740 
     1741EnterParagraphs.prototype.moveCursorOnEdge = function(selection) 
     1742{ 
     1743  // We'll only move the cursor if the selection is collapsed (ie. no contents) 
     1744  if ((selection.anchorNode != selection.focusNode) || 
     1745      (selection.anchorOffset != selection.focusOffset)) 
     1746  { 
     1747    return; 
     1748  } 
     1749 
     1750  // We now need to filter based on the element we are inside of.  If the 
     1751  // cursor is on a text node, we look at the parent of the node. 
     1752  var wrapNode = selection.anchorNode; 
     1753  if (TEXT_NODE == wrapNode.nodeType) 
     1754  { 
     1755    wrapNode = wrapNode.parentNode; 
     1756  } 
     1757   
     1758  // Check the wrapper against our lists of trigger nodes. 
     1759  if (!EnterParagraphs.prototype._pifyParent.test(wrapNode.nodeName) && 
     1760      !EnterParagraphs.prototype._pifySibling.test(wrapNode.nodeName)) 
     1761  { 
     1762    // We're lucky, no need to check for edges, let's just return. 
     1763    return; 
     1764  } 
     1765 
     1766  // Okay, time to perform edge checking.  If the cursor is inside of a text 
     1767  // node, the rules for edge detection are quite specialized, so we'll deal 
     1768  // with that first.  Since text nodes can't contain other nodes, we only have 
     1769  // to perform this check once.  We won't actually move the cursor here, just 
     1770  // our copy of it, because we won't know where it belongs until we're dealing 
     1771  // with the nodes themselves, rather than the text. 
     1772 
     1773  var cursorParent = selection.anchorNode; 
     1774  var cursorOffset = selection.anchorOffset; 
     1775 
     1776  for (;this.cursorAtEnd(cursorParent, cursorOffset);) 
     1777  { 
     1778    if (TEXT_NODE == cursorParent.nodeType) 
     1779    { 
     1780      // If we're at the end and stuck inside of a text node, we move out of 
     1781      // the text node, which is a simpler case, than continue. 
     1782      var parentOffset = this.indexInParent(cursorParent); 
     1783      if (null === parentOffset) 
     1784      { 
     1785        // We can't do anything with this cursor, so return. 
     1786        return; 
     1787      } 
     1788 
     1789      cursorParent = cursorParent.parentNode; 
     1790      cursorOffset = parentOffset + 1; 
     1791      continue; 
     1792    } 
     1793 
     1794    var parentOffset = this.indexInParent(cursorParent); 
     1795    if (null === parentOffset) 
     1796    { 
     1797      // We can't do anything with this cursor, so return. 
     1798      return; 
     1799    } 
     1800 
     1801    cursorParent = cursorParent.parentNode; 
     1802    cursorOffset = parentOffset + 1; 
     1803 
     1804    // If we are no longer inside of one of our trigger nodes, we're done. 
     1805    if (!this._pifyParent.test(cursorParent.nodeName) && 
     1806        !this._pifySibling.test(cursorParent.nodeName)) 
     1807    { 
     1808      // Move the real cursor. 
     1809      selection.removeAllRanges(); 
     1810      var range = this.editor.createRange(selection); 
     1811      range.setStart(cursorParent, cursorOffset); 
     1812      range.setEnd(cursorParent, cursorOffset); 
     1813      selection.addRange(range); 
     1814      return; 
     1815    } 
     1816  } 
     1817 
     1818  for (;this.cursorAtBeginning(cursorParent, cursorOffset);) 
     1819  { 
     1820    if (TEXT_NODE == cursorParent.nodeType) 
     1821    { 
     1822      // If we're at the beginning and stuck inside of a text node, we move out 
     1823      // of the text node, which is a simpler case, than continue. 
     1824      var parentOffset = this.indexInParent(cursorParent); 
     1825      if (null === parentOffset) 
     1826      { 
     1827        // We can't do anything with this cursor, so return. 
     1828        return; 
     1829      } 
     1830 
     1831      cursorParent = cursorParent.parentNode; 
     1832      cursorOffset = parentOffset; 
     1833      continue; 
     1834    } 
     1835 
     1836    var parentOffset = this.indexInParent(cursorParent); 
     1837    if (null === parentOffset) 
     1838    { 
     1839      // We can't do anything with this cursor, so return. 
     1840      return; 
     1841    } 
     1842 
     1843    cursorParent = cursorParent.parentNode; 
     1844    cursorOffset = parentOffset; 
     1845 
     1846    // If we are no longer inside of one of our trigger nodes, we're done. 
     1847    if (!this._pifyParent.test(cursorParent.nodeName) && 
     1848        !this._pifySibling.test(cursorParent.nodeName)) 
     1849    { 
     1850      // Move the real cursor. 
     1851      selection.removeAllRanges(); 
     1852      var range = this.editor.createRange(selection); 
     1853      range.setStart(cursorParent, cursorOffset); 
     1854      range.setEnd(cursorParent, cursorOffset); 
     1855      selection.addRange(range); 
     1856      return; 
     1857    } 
     1858  } 
     1859} 
    7931860 
    7941861EnterParagraphs.prototype.handleEnter = function(ev) 
     
    8071874  if ( this.isNormalListItem(rng) ) 
    8081875  { 
    809      
    8101876    return true; 
    8111877  } 
Note: See TracChangeset for help on using the changeset viewer.