Changeset 1161


Ignore:
Timestamp:
02/11/09 20:30:46 (11 years ago)
Author:
douglas
Message:

Current version of the fix... Still not 100%

File:
1 edited

Legend:

Unmodified
Added
Removed
  • branches/Ticket1226/paraHandlerBest.js

    r694 r1161  
    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* Determine if a cursor points to the end of it's containing node. 
     859*/ 
     860EnterParagraphs.prototype.cursorAtBeginning = function (cursorNode, cursorOffset) 
     861{ 
     862  if (cursorOffset == 0) 
     863  { 
     864    return true; 
     865  } 
     866  if (cursorNode.nodeType == TEXT_NODE) 
     867  { 
     868    // We're in the middle of a text node.  If the node is a whitespace node, 
     869    // we'll ignore it and treat it as if the cursor were at the beginning of 
     870    // the node, and not in it. 
     871    if (/\S/.test(cursorNode.nodeValue)) 
     872    { 
     873      return false; 
     874    } 
     875    cursorOffset = this.indexInParent(cursorNode); 
     876    cursorNode = cursorNode.parentNode; 
     877     
     878    // We need to make sure we there wasn't an error in indexInParent 
     879    if (cursorOffset === null) 
     880    { 
     881      return false; 
     882    } 
     883 
     884    // We have to check the new offset for the easy case. 
     885    if (cursorOffset == 0) 
     886    { 
     887      return true; 
     888    } 
     889  } 
     890  // At this point, if all of the nodes before the cursor are white space 
     891  // nodes, then the cursor is at the beginning of the node. 
     892  for (var node = cursorNode.childNodes[cursorOffset-1]; node; node = node.previousSibling) 
     893  { 
     894    if ((node.nodeType != TEXT_NODE) || (/\S/.test(node.nodeValue))) 
     895    { 
     896      return false; 
     897    } 
     898  } 
     899  return true; 
     900} 
     901/** 
    791902* Handles the pressing of an unshifted enter for Gecko 
    792903*/ 
     904 
     905EnterParagraphs.prototype.breakLine = function(ev, doc) 
     906{ 
     907  // Helper function that copies a DOM element and its attributes (except the 
     908  // id) without any of the contents. 
     909  function safeShallowCopy(node, doc) 
     910  { 
     911    var copy = doc.createElement(node.nodeName); 
     912    for (var index=0; index < node.attributes.length; ++index) 
     913    { 
     914      var attr = node.attributes[index]; 
     915      if ('id' != attr.name.toLowerCase()) 
     916      { 
     917        copy.setAttribute(attr.name, attr.value); 
     918      } 
     919    } 
     920    return copy; 
     921  } 
     922 
     923  // Helper function that will get the node immediately following the current 
     924  // node, but without descending into children nodes.  When looking at the 
     925  // markup of the document, this means that if a node to the right of this 
     926  // node in the text is at a lower depth in the DOM tree, than we will return 
     927  // it's first parent that is at our depth our higher in the tree. 
     928  function nextRootNode(node) 
     929  { 
     930    if (node.nextSibling) 
     931    { 
     932      return node.nextSibling; 
     933    } 
     934    for (var nextRoot = node.parentNode;nextRoot;nextRoot = nextRoot.parentNode) 
     935    { 
     936      if (nextRoot.nextSibling) 
     937      { 
     938        return nextRoot.nextSibling; 
     939      } 
     940    } 
     941  } 
     942 
     943  // A cursor is specified by a node and an offset, so we will split at that 
     944  // location.  It should be noted that if splitNode is a text node, 
     945  // splitOffset is an offset into the text contents.  If not, it is an index 
     946  // into the childNodes array. 
     947  function splitTree(root, splitNode, splitOffset, doc) 
     948  { 
     949    // Split root into two. 
     950    var breaker = safeShallowCopy(root, doc); 
     951    if (root.nextSibling) 
     952    { 
     953      breaker = root.parentNode.insertBefore(breaker,root.nextSibling); 
     954    } 
     955    else 
     956    { 
     957      breaker = root.parentNode.appendChild(breaker); 
     958    } 
     959 
     960    var insertNode = breaker; 
     961    for (;recreateStack.length>0;) 
     962    { 
     963      var stackEl = safeShallowCopy(recreateStack.pop(), doc) 
     964      insertNode.appendChild(stackEl); 
     965      // Move content here 
     966      insertNode = stackEl; 
     967    } 
     968 
     969    var innermostNode = insertNode; 
     970 
     971    var sourceNode = splitNode; 
     972    if (TEXT_NODE == sourceNode.nodeType) 
     973    { 
     974      var textNode = doc.createTextNode(sourceNode.nodeValue.substring(splitOffset,sourceNode.nodeValue.length)); 
     975      innermostNode = textNode = insertNode.appendChild(textNode); 
     976      sourceNode.nodeValue = sourceNode.nodeValue.substring(0,splitOffset); 
     977    } 
     978 
     979    // When splitting a tree, we need to take any nodes that are after the 
     980    // split and move them into their location in the new tree.  We can have 
     981    // siblings at each level of the tree, so we need to walk from the inside 
     982    // of the source outwards, and move the offending nodes to the equivalent 
     983    // position on the newly duplicated tree. 
     984 
     985    // Move insertNode from the inside outwards towards the root, moving any content nodes as we go. 
     986    while (insertNode != root.parentNode) 
     987    { 
     988      for (var moveNode=sourceNode.nextSibling;moveNode;) 
     989      { 
     990        // We have to take a reference to the next sibling before cutting out 
     991        // of the tree, or we will lose our place. 
     992 
     993        // nextNode can potentially be null.  This is not a problem. 
     994        var nextNode = moveNode.nextSibling; 
     995        var cutNode = moveNode.parentNode.removeChild(moveNode); 
     996        insertNode.appendChild(cutNode); 
     997        moveNode = nextNode; 
     998      } 
     999 
     1000      // Move both of our node pointers one step closer to the root node. 
     1001      sourceNode = sourceNode.parentNode; 
     1002      insertNode = insertNode.parentNode; 
     1003    } 
     1004 
     1005    // Below code needs to check for element node with empty text node. 
     1006    // An empty node is an text node of zero length or an element node with no 
     1007    // children, or whose only children are zero-length text nodes. 
     1008    function emptyNode(node) 
     1009    { 
     1010      if ((TEXT_NODE == node.nodeType) && (0 == node.nodeValue.length)) 
     1011      { 
     1012        // Text nodes are empty if there is no text. 
     1013        return true; 
     1014      } 
     1015 
     1016      if (ELEMENT_NODE == node.nodeType) 
     1017      { 
     1018        for (var child = node.firstChild; child; child = child.nextSibling) 
     1019        { 
     1020          if ((ELEMENT_NODE == child.nodeType) || (0 != child.nodeValue.length)) 
     1021          { 
     1022            // If there are any element children, or text nodes with text in 
     1023            // them, this node is not empty. 
     1024            return false; 
     1025          } 
     1026        } 
     1027 
     1028        // node has no childNodes that are elements and no childNodes that are 
     1029        // text nodes with text in them. 
     1030        return true; 
     1031      } 
     1032 
     1033      return false; 
     1034    } 
     1035 
     1036    function stuffEmptyNode(node, doc) 
     1037    { 
     1038      if (!emptyNode(node)) 
     1039      { 
     1040        return; 
     1041      } 
     1042 
     1043      if (TEXT_NODE == node.nodeType) 
     1044      { 
     1045        // Unicode equivalent of non breaking whitespace. 
     1046        node.nodeValue = '\u00a0'; 
     1047      } 
     1048      else if (0 == node.childNodes.length) 
     1049      { 
     1050        // Unicode equivalent of non breaking whitespace. 
     1051        node.appendChild(doc.createTextNode('\u00a0')); 
     1052      } 
     1053      else 
     1054      { 
     1055        // Unicode equivalent of non breaking whitespace. The node is empty, 
     1056        // but it has child nodes, so firstChild is guaranteed to be an empty 
     1057        // text node. 
     1058        node.firstChild.nodeValue = '\u00a0'; 
     1059      } 
     1060    } 
     1061 
     1062    // Make sure when we split the tree that we don't leave any empty nodes, as 
     1063    // that would have visual glitches. 
     1064    stuffEmptyNode(splitNode, doc); 
     1065    stuffEmptyNode(innermostNode, doc); 
     1066 
     1067    // So that we can correctly set the selection, we'll return a reference to 
     1068    // the inserted subtree. 
     1069    return innermostNode; 
     1070  } 
     1071  function insertLineBreak(cursorParent, cursorOffset, useNewline, doc) 
     1072  { 
     1073    if (TEXT_NODE == cursorParent.nodeType) 
     1074    { 
     1075      // The cursor points inside of a text node, we insert the newline 
     1076      // directly into the text. 
     1077      var splitNode = cursorParent; 
     1078      var splitOffset = cursorOffset; 
     1079      if (useNewline) 
     1080      { 
     1081        splitNode.nodeValue = splitNode.nodeValue.substring(0,splitOffset) + '\n' + splitNode.nodeValue.substring(splitOffset,splitNode.nodeValue.length); 
     1082      } 
     1083      else 
     1084      { 
     1085        var newTextNode = doc.createTextNode(splitNode.nodeValue.substring(splitOffset,splitNode.nodeValue.length)); 
     1086        var newBreakNode = doc.createElement('br'); 
     1087        splitNode.nodeValue = splitNode.nodeValue.substring(0,splitOffset); 
     1088 
     1089        var appendIndex = EnterParagraphs.prototype.indexInParent(cursorParent); 
     1090        if (appendIndex == cursorParent.parentNode.length-1) 
     1091        { 
     1092          newBreakNode = cursorParent.appendChild(newBreakNode); 
     1093          newTextNode = cursorParent.appendChild(newTextNode); 
     1094        } 
     1095        else 
     1096        { 
     1097          newTextNode = cursorParent.insertBefore(newTextNode, cursorParent.parentNode.childNodes[appendIndex+1]); 
     1098          newBreakNode = cursorParent.insertBefore(newBreakNode, newTextNode); 
     1099        } 
     1100        return newBreakNode; 
     1101      } 
     1102    } 
     1103    else if (0 == cursorParent.childNodes.length) 
     1104    { 
     1105      // The cursor is inside an empty element or document node, so we insert a txt node or break element as necessary. 
     1106      if (useNewline) 
     1107      { 
     1108        var breakingNode = doc.createTextNode('\n'); 
     1109        cursorParent.appendChild(breakingNode); 
     1110      } 
     1111      else 
     1112      { 
     1113        var breakingNode = doc.createElement('br'); 
     1114        return cursorParent.appendChild(breakingNode); 
     1115      } 
     1116    } 
     1117    else if ((cursorOffset == cursorParent.childNodes.length) && (TEXT_NODE == cursorParent.childNodes[cursorOffset-1].nodeType)) 
     1118    { 
     1119      // The cursor is at the after the last node, and the previous node is a 
     1120      // text node where we can insert the newline. 
     1121      if (useNewline) 
     1122      { 
     1123        var lastTextNode = cursorParent.childNodes[cursorOffset-1]; 
     1124        lastTextNode.nodeValue = lastTextNode.nodeValue + '\n'; 
     1125      } 
     1126      else 
     1127      { 
     1128        var breakingNode = doc.createElement('br'); 
     1129        return cursorParent.appendChild(breakingNode); 
     1130      } 
     1131    } 
     1132    else if (cursorOffset == cursorParent.childNodes.length) 
     1133    { 
     1134      // The cursor is at the after the last node, and the previous node is an 
     1135      // not text, so we must insert a text node. 
     1136      if (useNewline) 
     1137      { 
     1138        var breakingNode = doc.createTextNode('\n'); 
     1139        cursorParent.appendChild(breakingNode); 
     1140      } 
     1141      else 
     1142      { 
     1143        var breakingNode = doc.createElement('br'); 
     1144        return cursorParent.appendChild(breakingNode); 
     1145      } 
     1146    } 
     1147    else if (TEXT_NODE == cursorParent.childNodes[cursorOffset].nodeType) 
     1148    { 
     1149      // The cursor points to a text node, insert our newline there. 
     1150      if (useNewline) 
     1151      { 
     1152        var splitNode = cursorParent.childNodes[cursorOffset]; 
     1153        splitNode.nodeValue = '\n' + splitNode.nodeValue; 
     1154      } 
     1155      else 
     1156      { 
     1157        var breakingNode = doc.createElement('br'); 
     1158        return cursorParent.insertBefore(breakingNode, cursorParent[cursorOffset]); 
     1159      } 
     1160    } 
     1161    else if (TEXT_NODE == cursorParent.childNodes[cursorOffset-1].nodeType) 
     1162    { 
     1163      // The cursor points to an non-text node, but there is a text node just 
     1164      // before where we can insert a newline. 
     1165      if (useNewline) 
     1166      { 
     1167        var splitNode = cursorParent.childNodes[cursorOffset-1]; 
     1168        splitNode.nodeValue = splitNode.nodeValue + '\n'; 
     1169      } 
     1170      else 
     1171      { 
     1172        var breakingNode = doc.createElement('br'); 
     1173        return cursorParent.insertBefore(breakingNode, cursorParent[cursorOffset]); 
     1174      } 
     1175    } 
     1176    else 
     1177    { 
     1178      // The cursor points between two non-text nodes, so we must insert a text 
     1179      // node. 
     1180      if (useNewline) 
     1181      { 
     1182        var breakingNode = doc.createTextNode('\n'); 
     1183        cursorParent.insertBefore(breakingNode, cursorParent.childNodes[cursorOffset]); 
     1184      } 
     1185      else 
     1186      { 
     1187        var breakingNode = doc.createElement('br'); 
     1188        return cursorParent.insertBefore(breakingNode, cursorParent.childNodes[cursorOffset]); 
     1189      } 
     1190    } 
     1191  } 
     1192 
     1193  /* *********************************************************************** 
     1194                                 CODE 
     1195     *********************************************************************** */ 
     1196  // In the case of the user pressing enter, we have to break the line somehow. 
     1197  // If there is anything already selected, we interpret that the user wishes 
     1198  // for the content to be deleted. 
     1199   
     1200  var selection = this.editor.getSelection(); 
     1201  var range = this.editor.createRange(selection); 
     1202   
     1203  selection.collapseToStart(); 
     1204  range.deleteContents(); 
     1205 
     1206  // We do some magic manipulation to help with user intent. 
     1207  this.moveCursorOnEdge(selection); 
     1208 
     1209  // Take a reference to the cursor. 
     1210  var cursorParent = selection.anchorNode; 
     1211  var cursorOffset = selection.anchorOffset; 
     1212 
     1213  // Now that we have an empty selection, the process of breaking the line is a 
     1214  // bit simpler.  Our strategy for breaking the line is as follows: 
     1215 
     1216  // We will modify the cursor position in an attempt to guess the user's 
     1217  // intent.  When the cursor is at the inside edge of certain elements, we 
     1218  // work under the assumption that the user wished to select just outside of 
     1219  // that element. As such, we will move the cursor to just outside the 
     1220  // element, and then continue. 
     1221 
     1222  // Next, we find the first non-inline element that contains our cursor. 
     1223  // These can be broken into four types: 
     1224  // 1) Definition lists and their elements (dl, dt, dd) 
     1225  // 2) Other lists (ul, ol) 
     1226  // 3) Other containers (body, div, tr, pre, etc.) 
     1227  // 4) Other block elements (p, h3, li, th, td, etc.) 
     1228  // 
     1229  // If we are inside a definition (1) list, we try to guess the users intent 
     1230  //   as to whether they want to insert* a new term or a new definition. 
     1231  // If we are inside any other list (2) element, we will insert* an li element. 
     1232  // If we are in any other container (3), we will insert* a p element. 
     1233  // If we are in any other block (4) element, we split the block into two 
     1234  //   pieces and move* anything after the cursor to the second block. 
     1235  // 
     1236  // *When inserting or moving content, we must be sure to look at any 
     1237  // inline elements that wrap the cursor, properly close them off, and create 
     1238  // the same group of wrapping inline elements in the inserted/moved 
     1239  // element. This logic is incorporated into splitTree. 
     1240 
     1241  // Find the first wrapping non-inline element. (1-5 above) 
     1242  if (ELEMENT_NODE == cursorParent.nodeType) 
     1243  { 
     1244      // When the cursor is on an element node, it's before that element in the 
     1245      // document, and so we only want to consider its parent for deciding what 
     1246      // to do. The same is true when the cursor points to just before a text 
     1247      // node, so we only need to check the cursorParent. 
     1248      var wrapNode = cursorParent; 
     1249  } 
     1250  else if (TEXT_NODE == cursorParent.nodeType) 
     1251  { 
     1252      // Since we know that a text node is not the wrapper, we'll start with 
     1253      // its parent. 
     1254      var wrapNode = cursorParent.parentNode; 
     1255  } 
     1256  else 
     1257  { 
     1258      // We are dealing with an XML document.  This should be expanded to 
     1259      // handle these cases. 
     1260      // http://www.w3schools.com/Dom/dom_nodetype.asp 
     1261      alert('You have selected a node from an XML document, type ' + 
     1262            cursorParent.nodeType + '.\nXML documents are not ' + 
     1263            'yet supported.'); 
     1264      // Let the browser deal with it. 
     1265      return true; 
     1266  } 
     1267 
     1268  // This is an array used as a stack for recreating the current 'state' of 
     1269  // the cursor.  (eg. If the cursor is inside of an em tag inside of a p, 
     1270  // we'll add the em to the stack so that we can recreate it while splitting 
     1271  // the p.) 
     1272  var recreateStack = []; 
     1273 
     1274  while (!EnterParagraphs.prototype._pWrapper.test(wrapNode.nodeName)) 
     1275  { 
     1276    recreateStack.push(wrapNode); 
     1277    wrapNode = wrapNode.parentNode; 
     1278 
     1279    if (!wrapNode) 
     1280    { 
     1281      // Broken DOM, let the browser handle it. 
     1282      return true; 
     1283    } 
     1284  } 
     1285 
     1286  if (wrapNode.nodeName.toLowerCase() in {pre:''}) 
     1287  { 
     1288    insertLineBreak(cursorParent, cursorOffset, true, doc); 
     1289    this.editor.updateToolbar(); 
     1290     
     1291    Xinha._stopEvent(ev); 
     1292     
     1293    range.setStart(cursorParent, cursorOffset+1); 
     1294    range.setEnd(cursorParent, cursorOffset+1); 
     1295    selection.removeAllRanges(); 
     1296    selection.addRange(range); 
     1297    return false; 
     1298  } 
     1299  else if (wrapNode.nodeName.toLowerCase() in {body:'',div:'',fieldset:'',form:'',map:'',noscript:'','object':'',blockquote:''}) 
     1300  { 
     1301    // We know that the there are no block elements between the cursor and the 
     1302    // wrapNode, but there may be inline elements.  What we'll do is take 
     1303    // everything in the tree below wrapNode, embed it into a P element, and 
     1304    // then split the whole thing. 
     1305 
     1306    // The cursor might be at the ending edge of the wrapNode. 
     1307    // 1. Pointing to a text node <body>^This is text</body> 
     1308    // 2. Pointing to an inline node that is the child of the wrapNode.<body>^<em>text</em></body> 
     1309    // 3. Pointing to an inline node that is a non-direct descendant of the wrapNode.<body><q>^<em>text</em></q></body> 
     1310    // 4. Pointing to an inline node that is a non-direct descendant of the wrapNode.<body><q><em>text</em> this^</q></body> 
     1311    // 5. Pointing to the end of the wrapNode.<body>Here is some text.^</body> 
     1312    // 6. Pointing to a block node that is just inside of the wrapNode.<body>^<p>text</p></body> 
     1313    var startNode = cursorParent; 
     1314    for (;(startNode != wrapNode) && (startNode.parentNode != wrapNode);) 
     1315    { 
     1316      startNode = startNode.parentNode; 
     1317    } 
     1318 
     1319    if (TEXT_NODE == cursorParent.nodeType) 
     1320    { 
     1321      var treeRoot = cursorParent; 
     1322    } 
     1323    else if (cursorOffset == cursorParent.childNodes.length) 
     1324    { 
     1325      var embedNode = doc.createElement('p'); 
     1326      embedNode = wrapNode.appendChild(embedNode); 
     1327      // The unicode character below is a representation of a non-breaking 
     1328      // space we use to prevent the paragraph from having visual glitches. 
     1329      var emptyTextNode = doc.createTextNode('\u00a0'); 
     1330      emptyTextNode = embedNode.appendChild(emptyTextNode); 
     1331 
     1332      Xinha._stopEvent(ev); 
     1333 
     1334      range.setStart(emptyTextNode, 0); 
     1335      range.setEnd(emptyTextNode, 0); 
     1336      selection.removeAllRanges(); 
     1337      selection.addRange(range); 
     1338 
     1339      return false; 
     1340    } 
     1341    else 
     1342    { 
     1343      var treeRoot = cursorParent.childNodes[cursorOffset]; 
     1344    } 
     1345 
     1346    for (;wrapNode != treeRoot.parentNode;) 
     1347    { 
     1348      treeRoot = treeRoot.parentNode; 
     1349    } 
     1350 
     1351    // At this point, treeRoot points to the root of the subtree inside 
     1352    // wrapNode that containes our cursor.  If this happens to be a block level 
     1353    // element, we'll just insert a P node here.  Otherwise, we'll replace this 
     1354    // node with an empty P node, and then embed it into that P node. 
     1355 
     1356    if (EnterParagraphs.prototype._pWrapper.test(treeRoot.nodeName)) 
     1357    { 
     1358      var embedNode = doc.createElement('p'); 
     1359      embedNode = wrapNode.insertBefore(embedNode, treeRoot); 
     1360      // The unicode character below is a representation of a non-breaking 
     1361      // space we use to prevent the paragraph from having visual glitches. 
     1362      var emptyTextNode = doc.createTextNode('\u00a0'); 
     1363      emptyTextNode = embedNode.appendChild(emptyTextNode); 
     1364 
     1365      Xinha._stopEvent(ev); 
     1366 
     1367      range.setStart(treeRoot, 0); 
     1368      range.setEnd(treeRoot, 0); 
     1369      selection.removeAllRanges(); 
     1370      selection.addRange(range); 
     1371 
     1372      return false; 
     1373    } 
     1374    var embedNode = doc.createElement('p'); 
     1375 
     1376    treeRoot = wrapNode.replaceChild(embedNode, treeRoot); 
     1377 
     1378    treeRoot = embedNode.appendChild(treeRoot); 
     1379     
     1380    if ((TEXT_NODE == treeRoot.nodeType) && !/\S/.test(treeRoot.nodeValue)) 
     1381    { 
     1382      var newCursor = treeRoot; 
     1383    } 
     1384    else 
     1385    { 
     1386      var newCursor = splitTree(embedNode, treeRoot, cursorOffset, doc); 
     1387    } 
     1388  } 
     1389  else if (wrapNode.nodeName.toLowerCase() in {td:'',address:''}) 
     1390  { 
     1391    // Line breaks BR element 
     1392    var newCursor = insertLineBreak(cursorParent, cursorOffset, false, doc); 
     1393  } 
     1394  else if (wrapNode.nodeName.toLowerCase() in {dl:''}) 
     1395  { 
     1396    // Find the leftSibling of the cursorParent.  If none, insert dt (term) followed by dd (definition), 
     1397    // otherwise insert same as cursorParent followed by same as leftSibling. 
     1398    // Check to see if the leftSibling and rightSibling are the same and then just insert the one term. 
     1399    // XXX TODO 
     1400  } 
     1401  else if (wrapNode.nodeName.toLowerCase() in {dt:'',dd:'',li:'',h1:'',h2:'',h3:'',h4:'',h5:'',h6:'',p:''}) 
     1402  { 
     1403    // Split wrapNode into two. 
     1404    var newCursor = splitTree(wrapNode, cursorParent, cursorOffset, doc); 
     1405  } 
     1406  else if (wrapNode.nodeName.toLowerCase() in {ol:'',ul:''}) 
     1407  { 
     1408    // Insert li 
     1409    var breaker = doc.createElement('li'); 
     1410    if (TEXT_NODE == cursorParent.nodeType) 
     1411    { 
     1412      var newCursor = wrapNode.insertBefore(breaker,cursorParent); 
     1413    } 
     1414    else 
     1415    { 
     1416      var newCursor = wrapNode.insertBefore(breaker,cursorParent.childNodes[cursorOffset]); 
     1417    } 
     1418  } 
     1419 
     1420  this.editor.updateToolbar(); 
     1421   
     1422  Xinha._stopEvent(ev); 
     1423   
     1424  // We turn the newCursor node into a cursor and offset into the parent. 
     1425  var newOffset = 0; 
     1426  while (newCursor.parentNode.childNodes[newOffset] != newCursor) 
     1427  { 
     1428    newOffset++; 
     1429  } 
     1430  newCursor = newCursor.parentNode; 
     1431 
     1432  // Monkey the new cursor position into somewhere the user should actually be 
     1433  // typing. 
     1434   
     1435   
     1436  Xinha._stopEvent(ev); 
     1437  range.setStart(newCursor, newOffset); 
     1438  range.setEnd(newCursor, newOffset); 
     1439  selection.removeAllRanges(); 
     1440  selection.addRange(range); 
     1441  return false; 
     1442} 
     1443 
     1444/** 
     1445* If the cursor is on the edge of certain elements, we reposition it so that we 
     1446* can break the line in a way that's more useful to the user. 
     1447*/ 
     1448 
     1449EnterParagraphs.prototype.moveCursorOnEdge = function(selection) 
     1450{ 
     1451  // We'll only move the cursor if the selection is collapsed (ie. no contents) 
     1452  if ((selection.anchorNode != selection.focusNode) || 
     1453      (selection.anchorOffset != selection.focusOffset)) 
     1454  { 
     1455    return; 
     1456  } 
     1457 
     1458  // We now need to filter based on the element we are inside of.  If the 
     1459  // cursor is on a text node, we look at the parent of the node. 
     1460  var wrapNode = selection.anchorNode; 
     1461  if (TEXT_NODE == wrapNode.nodeType) 
     1462  { 
     1463    wrapNode = wrapNode.parentNode; 
     1464  } 
     1465   
     1466  // Check the wrapper against our lists of trigger nodes. 
     1467  if (!EnterParagraphs.prototype._pifyParent.test(wrapNode.nodeName) && 
     1468      !EnterParagraphs.prototype._pifySibling.test(wrapNode.nodeName)) 
     1469  { 
     1470    // We're lucky, no need to check for edges, let's just return. 
     1471    return; 
     1472  } 
     1473 
     1474  // Okay, time to perform edge checking.  If the cursor is inside of a text 
     1475  // node, the rules for edge detection are quite specialized, so we'll deal 
     1476  // with that first.  Since text nodes can't contain other nodes, we only have 
     1477  // to perform this check once.  We won't actually move the cursor here, just 
     1478  // our copy of it, because we won't know where it belongs until we're dealing 
     1479  // with the nodes themselves, rather than the text. 
     1480 
     1481  var cursorParent = selection.anchorNode; 
     1482  var cursorOffset = selection.anchorOffset; 
     1483 
     1484  while (this.cursorAtEnd(cursorParent, cursorOffset)) 
     1485  { 
     1486    if (TEXT_NODE == cursorParent.nodeType) 
     1487    { 
     1488      // If we're at the end and stuck inside of a text node, we move not just 
     1489      // out of the text node, but out of the node containing the text node. 
     1490      // The second part will be performed by the standard node moving logic 
     1491      // below.  We'll just move out of the text node here. 
     1492      var parentOffset = this.indexInParent(cursorParent); 
     1493      if (null === parentOffset) 
     1494      { 
     1495        // We can't do anything with this cursor, so return. 
     1496        return; 
     1497      } 
     1498 
     1499      cursorParent = cursorParent.parentNode; 
     1500      cursorOffset = parentOffset + 1; 
     1501    } 
     1502 
     1503    var parentOffset = this.indexInParent(cursorParent); 
     1504    if (null === parentOffset) 
     1505    { 
     1506      // We can't do anything with this cursor, so return. 
     1507      return; 
     1508    } 
     1509 
     1510    cursorParent = cursorParent.parentNode; 
     1511    cursorOffset = parentOffset + 1; 
     1512 
     1513    // If we are no longer inside of one of our trigger nodes, we're done. 
     1514    if (!this._pifyParent.test(cursorParent.parentNode.nodeName) && 
     1515        !this._pifySibling.test(cursorParent.parentNode.nodeName)) 
     1516    { 
     1517      // Move the real cursor. 
     1518      selection.removeAllRanges(); 
     1519      var range = this.editor.createRange(selection); 
     1520      range.setStart(cursorParent, cursorOffset); 
     1521      range.setEnd(cursorParent, cursorOffset); 
     1522      selection.addRange(range); 
     1523      return; 
     1524    } 
     1525  } 
     1526 
     1527  while (this.cursorAtBeginning(cursorParent, cursorOffset)) 
     1528  { 
     1529    if (TEXT_NODE == cursorParent.nodeType) 
     1530    { 
     1531      // If we're at the beginning and stuck inside of a text node, we move not 
     1532      // just out of the text node, but out of the node containing the text 
     1533      // node.  The second part will be performed by the standard node moving 
     1534      // logic below.  We'll just move out of the text node here. 
     1535      var parentOffset = this.indexInParent(cursorParent); 
     1536      if (null === parentOffset) 
     1537      { 
     1538        // We can't do anything with this cursor, so return. 
     1539        return; 
     1540      } 
     1541 
     1542      cursorParent = cursorParent.parentNode; 
     1543      cursorOffset = parentOffset; 
     1544    } 
     1545 
     1546    var parentOffset = this.indexInParent(cursorParent); 
     1547    if (null === parentOffset) 
     1548    { 
     1549      // We can't do anything with this cursor, so return. 
     1550      return; 
     1551    } 
     1552 
     1553    cursorParent = cursorParent.parentNode; 
     1554    cursorOffset = parentOffset; 
     1555 
     1556    // If we are no longer inside of one of our trigger nodes, we're done. 
     1557    if (!this._pifyParent.test(cursorParent.parentNode.nodeName) && 
     1558        !this._pifySibling.test(cursorParent.parentNode.nodeName)) 
     1559    { 
     1560      // Move the real cursor. 
     1561      selection.removeAllRanges(); 
     1562      var range = this.editor.createRange(selection); 
     1563      range.setStart(cursorParent, cursorOffset); 
     1564      range.setEnd(cursorParent, cursorOffset); 
     1565      selection.addRange(range); 
     1566      return; 
     1567    } 
     1568  } 
     1569} 
    7931570 
    7941571EnterParagraphs.prototype.handleEnter = function(ev) 
     
    8071584  if ( this.isNormalListItem(rng) ) 
    8081585  { 
    809      
    8101586    return true; 
    8111587  } 
Note: See TracChangeset for help on using the changeset viewer.