Changeset 1338


Ignore:
Timestamp:
02/03/18 09:32:26 (11 months ago)
Author:
gogo
Message:

Rollback [1335], see #1226 for why

File:
1 edited

Legend:

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

    r1335 r1338  
    3030// "constants" 
    3131 
    32 // Set up the node type constants for browsers that don't support them. 
    33 var ELEMENT_NODE = ELEMENT_NODE || 1; 
    34 var ATTRIBUTE_NODE = ATTRIBUTE_NODE || 2; 
    35 var TEXT_NODE = TEXT_NODE || 3; 
    36 var COMMENT_NODE = COMMENT_NODE || 8; 
    37 var DOCUMENT_NODE = DOCUMENT_NODE || 9; 
    3832/** 
    3933* Whitespace Regex 
     
    5347 
    5448EnterParagraphs.prototype._pContainers = /^(body|del|div|fieldset|form|ins|map|noscript|object|td|th)$/i; 
    55 EnterParagraphs.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; 
    5649 
    5750/** 
     
    7467 
    7568/** 
    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. 
     69* Elements which should get a new P, before or after, when enter is pressed at either end 
    7770*/ 
    7871 
     
    8174 
    8275/** 
    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. 
     76* Elements which should get a new P, before or after a close parent, when enter is pressed at either end 
    8577*/ 
    8678 
     
    438430{ 
    439431   
    440   var next = function(element, search_direction) { 
     432  var next = function(element, search_direction) 
     433        { 
    441434    return ( search_direction == "left" ? element.previousSibling : element.nextSibling ); 
    442   }; 
     435        }; 
    443436   
    444437  var node = search_direction == "left" ? rng.startContainer : rng.endContainer; 
     
    449442  // be on the exclusion list and we wouldn't know until it was too late 
    450443   
    451   while ( start.nodeType == 1 && !this._permEmpty.test(start.nodeName) ) { 
     444  while ( start.nodeType == 1 && !this._permEmpty.test(start.nodeName) ) 
     445  { 
    452446    start = ( offset ? start.lastChild : start.firstChild ); 
    453447  } 
     
    464458  // sometimes this loop finds a blank text node, sometimes it doesn't. 
    465459   
    466   roam = roam ? ( next(roam,search_direction) ? next(roam,search_direction) : roam.parentNode ) : start; 
    467   while (roam) { 
    468  
     460  while ( roam = roam ? ( next(roam,search_direction) ? next(roam,search_direction) : roam.parentNode ) : start ) 
     461  { 
     462     
    469463    // next() is an inline function defined above that returns the next node depending 
    470464    // on the direction we're searching. 
    471  
    472     if ( next(roam,search_direction) ) { 
    473  
     465     
     466    if ( next(roam,search_direction) ) 
     467    { 
     468       
    474469      // If the next sibling's on the exclusion list, stop before it 
    475  
    476       if ( this._pExclusions.test(next(roam,search_direction).nodeName) ) { 
    477  
     470       
     471      if ( this._pExclusions.test(next(roam,search_direction).nodeName) ) 
     472      { 
     473         
    478474        return this.processRng(rng, search_direction, roam, next(roam,search_direction), (search_direction == "left"?'AfterEnd':'BeforeBegin'), true, false); 
    479475      } 
    480     } else { 
    481  
     476    } 
     477    else 
     478    { 
     479       
    482480      // If our parent's on the container list, stop inside it 
    483  
    484       if (this._pContainers.test(roam.parentNode.nodeName)) { 
    485  
     481       
     482      if (this._pContainers.test(roam.parentNode.nodeName)) 
     483      { 
     484         
    486485        return this.processRng(rng, search_direction, roam, roam.parentNode, (search_direction == "left"?'AfterBegin':'BeforeEnd'), true, false); 
    487       } else if (this._pExclusions.test(roam.parentNode.nodeName)) { 
    488  
     486      } 
     487      else if (this._pExclusions.test(roam.parentNode.nodeName)) 
     488      { 
     489         
    489490        // chop without wrapping 
    490  
    491         if (this._pBreak.test(roam.parentNode.nodeName)) { 
    492  
     491         
     492        if (this._pBreak.test(roam.parentNode.nodeName)) 
     493        { 
     494           
    493495          return this.processRng(rng, search_direction, roam, roam.parentNode, 
    494496            (search_direction == "left"?'AfterBegin':'BeforeEnd'), false, (search_direction == "left" ?true:false)); 
    495         } else { 
    496  
     497        } 
     498        else 
     499        { 
     500           
    497501          // the next(roam,search_direction) in this call is redundant since we know it's false 
    498502          // because of the "if next(roam,search_direction)" above. 
     
    500504          // the final false prevents this range from being wrapped in <p>'s most likely 
    501505          // because it's already wrapped. 
    502  
     506           
    503507          return this.processRng(rng, 
    504508            search_direction, 
     
    511515      } 
    512516    } 
    513     roam = roam ? ( next(roam,search_direction) ? next(roam,search_direction) : roam.parentNode ) : start; 
    514517  } 
    515518   
     
    778781  if (ev.keyCode == 13 && !ev.shiftKey && this.editor._iframe.contentWindow.getSelection) 
    779782  { 
    780     return this.breakLine(ev, this.editor._doc); 
     783    return this.handleEnter(ev); 
    781784  } 
    782785   
     
    786789 
    787790/** 
    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  
    793 EnterParagraphs.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 */ 
    816 EnterParagraphs.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 */ 
    860 EnterParagraphs.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(); 
    971 xinha.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 */ 
    1088 EnterParagraphs.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 /** 
    1130791* Handles the pressing of an unshifted enter for Gecko 
    1131792*/ 
    1132  
    1133 EnterParagraphs.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  
    1741 EnterParagraphs.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 } 
    1860793 
    1861794EnterParagraphs.prototype.handleEnter = function(ev) 
     
    1874807  if ( this.isNormalListItem(rng) ) 
    1875808  { 
     809     
    1876810    return true; 
    1877811  } 
Note: See TracChangeset for help on using the changeset viewer.