| 25 | | // An array of elements who, in html4, by default, have an inline display and can have children |
| 26 | | // we use RegExp here since it should be a bit faster, also cleaner to check |
| 27 | | EnterParagraphs.prototype._html4_inlines_re = /^(a|abbr|acronym|b|bdo|big|cite|code|dfn|em|font|i|kbd|label|q|s|samp|select|small|span|strike|strong|sub|sup|textarea|tt|u|var)$/i; |
| 28 | | |
| 29 | | // Finds the first parent element of a given node whose display is probably not inline |
| 30 | | EnterParagraphs.prototype.parentBlock = function(node) { |
| 31 | | while (node.parentNode && (node.nodeType != 1 || this._html4_inlines_re.test(node.tagName))) |
| 32 | | node = node.parentNode; |
| 33 | | return node; |
| 34 | | }; |
| 35 | | |
| 36 | | // Internal function for recursively itterating over a all nodes in a fragment |
| 37 | | // If a callback function returns a non-null value, that is returned and the crawl is therefore broken |
| 38 | | EnterParagraphs.prototype.walkNodeChildren = function(me, callback) { |
| 39 | | if (me.firstChild) { |
| 40 | | var myChild = me.firstChild; |
| 41 | | var retVal; |
| 42 | | while (myChild) { |
| 43 | | if ((retVal = callback(this, myChild)) != null) |
| 44 | | return retVal; |
| 45 | | if ((retVal = this.walkNodeChildren(myChild, callback)) != null) |
| 46 | | return retVal; |
| 47 | | myChild = myChild.nextSibling; |
| 48 | | } |
| 49 | | } |
| 50 | | }; |
| 51 | | |
| 52 | | // Callback function to be performed on each node in the hierarchy |
| 53 | | // Sets flag to true if we find actual text or an element that's not usually displayed inline |
| 54 | | EnterParagraphs.prototype._isFilling = function(self, node) { |
| 55 | | if (node.nodeType == 1 && !self._html4_inlines_re.test(node.nodeName)) |
| | 24 | // Whitespace Regex |
| | 25 | EnterParagraphs.prototype._whiteSpace = /^\s*$/; |
| | 26 | // The pragmatic list of which elements a paragraph may not contain, and which may contain a paragraph |
| | 27 | EnterParagraphs.prototype._pExclusions = /^(address|blockquote|body|dd|div|dl|dt|fieldset|form|h1|h2|h3|h4|h5|h6|hr|li|noscript|ol|p|pre|table|ul)$/i; |
| | 28 | EnterParagraphs.prototype._pContainers = /^(body|del|div|fieldset|form|ins|map|noscript|object|td|th)$/i; |
| | 29 | // Elements which may not contain paragraphs, and would prefer a break to being split |
| | 30 | EnterParagraphs.prototype._pBreak = /^(address|pre|blockquote)$/i; |
| | 31 | // Elements which may not contain children |
| | 32 | EnterParagraphs.prototype._permEmpty = /^(area|base|basefont|br|col|frame|hr|img|input|isindex|link|meta|param)$/i; |
| | 33 | // Elements which count as content, as distinct from whitespace or containers |
| | 34 | EnterParagraphs.prototype._elemSolid = /^(applet|br|button|hr|img|input|table)$/i; |
| | 35 | // Elements which should get a new P, before or after, when enter is pressed at either end |
| | 36 | EnterParagraphs.prototype._pifySibling = /^(address|blockquote|del|div|dl|fieldset|form|h1|h2|h3|h4|h5|h6|hr|ins|map|noscript|object|ol|p|pre|table|ul|)$/i; |
| | 37 | EnterParagraphs.prototype._pifyForced = /^(ul|ol|dl|table)$/i; |
| | 38 | // Elements which should get a new P, before or after a close parent, when enter is pressed at either end |
| | 39 | EnterParagraphs.prototype._pifyParent = /^(dd|dt|li|td|th|tr)$/i; |
| | 40 | |
| | 41 | // Gecko's a bit lacking in some odd ways... |
| | 42 | EnterParagraphs.prototype.insertAdjacentElement = function(ref,pos,el) { |
| | 43 | |
| | 44 | if ( pos == 'BeforeBegin' ) ref.parentNode.insertBefore(el,ref); |
| | 45 | else if ( pos == 'AfterEnd' ) ref.nextSibling ? ref.parentNode.insertBefore(el,ref.nextSibling) : ref.parentNode.appendChild(el); |
| | 46 | else if ( pos == 'AfterBegin' && ref.firstChild ) ref.insertBefore(el,ref.firstChild); |
| | 47 | else if ( pos == 'BeforeEnd' || pos == 'AfterBegin' ) ref.appendChild(el); |
| | 48 | }; |
| | 49 | |
| | 50 | // Passes a global parent node or document fragment to forEachNode |
| | 51 | EnterParagraphs.prototype.forEachNodeUnder = function (top, fn, ltr, init, parm) { |
| | 52 | |
| | 53 | // Identify the first and last nodes to deal with |
| | 54 | var start, end; |
| | 55 | if ( top.nodeType == 11 && top.firstChild ) { |
| | 56 | start = top.firstChild; |
| | 57 | end = top.lastChild; |
| | 58 | } else start = end = top; |
| | 59 | while ( end.lastChild ) end = end.lastChild; |
| | 60 | |
| | 61 | // Pass onto forEachNode |
| | 62 | return this.forEachNode(start, end, fn, ltr, init, parm); |
| | 63 | }; |
| | 64 | |
| | 65 | // Throws each node into a function |
| | 66 | EnterParagraphs.prototype.forEachNode = function (left, right, fn, ltr, init, parm) { |
| | 67 | |
| | 68 | var xBro = function(elem, ltr) { return ( ltr ? elem.nextSibling : elem.previousSibling ); }; |
| | 69 | var xSon = function(elem, ltr) { return ( ltr ? elem.firstChild : elem.lastChild ); }; |
| | 70 | var walk, lookup, fnVal, ping = init; |
| | 71 | |
| | 72 | // Until we've hit the last node |
| | 73 | while ( walk != ltr ? right : left ) { |
| | 74 | |
| | 75 | // Progress to the next node |
| | 76 | if ( !walk ) walk = ltr ? left : right; |
| | 77 | else { |
| | 78 | if ( xSon(walk,ltr) ) walk = xSon(walk,ltr); |
| | 79 | else { |
| | 80 | if ( xBro(walk,ltr) ) walk = xBro(walk,ltr); |
| | 81 | else { |
| | 82 | lookup = walk; |
| | 83 | while ( !xBro(lookup,ltr) && lookup != (ltr ? right : left) ) lookup = lookup.parentNode; |
| | 84 | walk = ( lookup.nextSibling ? lookup.nextSibling : lookup ) ; |
| | 85 | if ( walk == right ) break; |
| | 86 | } } } |
| | 87 | |
| | 88 | fnVal = fn(this, walk, ping, parm, (walk==(ltr?right:left))); // Throw this node at the wanted function |
| | 89 | if ( fnVal[0] ) return fnVal[1]; // If this node wants us to return, return pong |
| | 90 | if ( fnVal[1] ) ping = fnVal[1]; // Otherwise, set pong to ping, to pass to the next node |
| | 91 | } |
| | 92 | return false; |
| | 93 | }; |
| | 94 | |
| | 95 | // forEachNode fn: Find a post-insertion node, only if all nodes are empty, or the first content |
| | 96 | EnterParagraphs.prototype._fenEmptySet = function (parent, node, pong, getCont, last) { |
| | 97 | |
| | 98 | // Mark this if it's the first base |
| | 99 | if ( !pong && !node.firstChild ) pong = node; |
| | 100 | |
| | 101 | // Check for content |
| | 102 | if ( (node.nodeType == 1 && parent._elemSolid.test(node.nodeName)) || |
| | 103 | (node.nodeType == 3 && !parent._whiteSpace.test(node.nodeValue)) || |
| | 104 | (node.nodeType != 1 && node.nodeType != 3) ) { |
| | 105 | |
| | 106 | return new Array(true, (getCont?node:false)); |
| | 107 | } |
| | 108 | |
| | 109 | // Only return the 'base' node if we didn't want content |
| | 110 | if ( last && !getCont ) return new Array(true, pong); |
| | 111 | return new Array(false, pong); |
| | 112 | }; |
| | 113 | |
| | 114 | // forEachNode fn: |
| | 115 | EnterParagraphs.prototype._fenCullIds = function (parent, node, pong, parm, last) { |
| | 116 | |
| | 117 | // Check for an id, blast it if it's in the store, otherwise add it |
| | 118 | if ( node.id ) pong[node.id] ? node.id = '' : pong[node.id] = true; |
| | 119 | return new Array(false,pong); |
| | 120 | }; |
| | 121 | |
| | 122 | // Grabs a range suitable for paragraph stuffing |
| | 123 | EnterParagraphs.prototype.processSide = function(rng, left) { |
| | 124 | |
| | 125 | var next = function(element, left) { return ( left ? element.previousSibling : element.nextSibling ); }; |
| | 126 | var node = left ? rng.startContainer : rng.endContainer; |
| | 127 | var offset = left ? rng.startOffset : rng.endOffset; |
| | 128 | var roam, start = node; |
| | 129 | |
| | 130 | // Never start with an element, because then the first roaming node might |
| | 131 | // be on the exclusion list and we wouldn't know until it was too late |
| | 132 | while ( start.nodeType == 1 && !this._permEmpty.test(start.nodeName) ) start = ( offset ? start.lastChild : start.firstChild ); |
| | 133 | |
| | 134 | // Climb the tree, left or right, until our course of action presents itself |
| | 135 | while ( roam = roam ? ( next(roam,left) ? next(roam,left) : roam.parentNode ) : start ) { |
| | 136 | |
| | 137 | if ( next(roam,left) ) { |
| | 138 | // If the next sibling's on the exclusion list, stop before it |
| | 139 | if ( this._pExclusions.test(next(roam,left).nodeName) ) { |
| | 140 | return this.processRng(rng, left, roam, next(roam,left), (left?'AfterEnd':'BeforeBegin'), true, false); |
| | 141 | } } else { |
| | 142 | // If our parent's on the container list, stop inside it |
| | 143 | if (this._pContainers.test(roam.parentNode.nodeName)) { |
| | 144 | return this.processRng(rng, left, roam, roam.parentNode, (left?'AfterBegin':'BeforeEnd'), true, false); |
| | 145 | } |
| | 146 | // If our parent's on the exclusion list, chop without wrapping |
| | 147 | else if (this._pExclusions.test(roam.parentNode.nodeName)) { |
| | 148 | if (this._pBreak.test(roam.parentNode.nodeName)) { |
| | 149 | return this.processRng(rng, left, roam, roam.parentNode, |
| | 150 | (left?'AfterBegin':'BeforeEnd'), false, (left?true:false)); |
| | 151 | } else { |
| | 152 | return this.processRng(rng, left, (roam = roam.parentNode), |
| | 153 | (next(roam,left) ? next(roam,left) : roam.parentNode), |
| | 154 | (next(roam,left) ? (left?'AfterEnd':'BeforeBegin') : (left?'AfterBegin':'BeforeEnd')), false, false); |
| | 155 | } } } } }; |
| | 156 | |
| | 157 | // Neighbour and insertion identify where the new node, roam, needs to enter |
| | 158 | // the document; landmarks in our selection will be deleted before insertion |
| | 159 | EnterParagraphs.prototype.processRng = function(rng, left, roam, neighbour, insertion, pWrap, preBr) { |
| | 160 | |
| | 161 | var node = left ? rng.startContainer : rng.endContainer; |
| | 162 | var offset = left ? rng.startOffset : rng.endOffset; |
| | 163 | |
| | 164 | // Define the range to cut, and extend the selection range to the same boundary |
| | 165 | var editor = this.editor; |
| | 166 | var newRng = editor._doc.createRange(); |
| | 167 | newRng.selectNode(roam); |
| | 168 | if (left) { |
| | 169 | newRng.setEnd(node, offset); |
| | 170 | rng.setStart(newRng.startContainer, newRng.startOffset); |
| | 171 | } else { |
| | 172 | newRng.setStart(node, offset); |
| | 173 | rng.setEnd(newRng.endContainer, newRng.endOffset); |
| | 174 | } |
| | 175 | |
| | 176 | // Clone the range and remove duplicate ids it would otherwise produce |
| | 177 | var cnt = newRng.cloneContents(); |
| | 178 | this.forEachNodeUnder(cnt, this._fenCullIds, true, this.takenIds, false); |
| | 179 | |
| | 180 | // Special case, for inserting paragraphs before some blocks when caret is at their zero offset |
| | 181 | var pify, pifyOffset, fill; |
| | 182 | pify = left ? (newRng.endContainer.nodeType == 3 ? true:false) : (newRng.startContainer.nodeType == 3 ? false:true); |
| | 183 | pifyOffset = pify ? newRng.startOffset : newRng.endOffset; |
| | 184 | pify = pify ? newRng.startContainer : newRng.endContainer; |
| | 185 | |
| | 186 | if ( this._pifyParent.test(pify.nodeName) && pify.parentNode.childNodes.item(0) == pify ) { |
| | 187 | while ( !this._pifySibling.test(pify.nodeName) ) pify = pify.parentNode; |
| | 188 | } |
| | 189 | |
| | 190 | if ( cnt.nodeType == 11 && !cnt.firstChild ) cnt.appendChild(editor._doc.createElement(pify.nodeName)); |
| | 191 | fill = this.forEachNodeUnder(cnt,this._fenEmptySet,true,false,false); |
| | 192 | |
| | 193 | if ( fill && this._pifySibling.test(pify.nodeName) && |
| | 194 | ( (pifyOffset == 0) || ( pifyOffset == 1 && this._pifyForced.test(pify.nodeName) ) ) ) { |
| | 195 | |
| | 196 | roam = editor._doc.createElement('p'); |
| | 197 | roam.appendChild(editor._doc.createElement('br')); |
| | 198 | |
| | 199 | if (left && pify.previousSibling) return new Array(pify.previousSibling, 'AfterEnd', roam); |
| | 200 | else if (!left && pify.nextSibling) return new Array(pify.nextSibling, 'BeforeBegin', roam); |
| | 201 | else return new Array(pify.parentNode, (left?'AfterBegin':'BeforeEnd'), roam); |
| | 202 | } |
| | 203 | |
| | 204 | // If our cloned contents are 'content'-less, shove a break in them |
| | 205 | if ( fill ) { |
| | 206 | if ( fill.nodeType == 3 ) fill = fill.parentNode; // Ill-concieved? |
| | 207 | if ( (fill.nodeType == 1 && !this._elemSolid.test()) || fill.nodeType == 11 ) fill.appendChild(editor._doc.createElement('br')); |
| | 208 | else fill.parentNode.insertBefore(editor._doc.createElement('br'),fill); |
| | 209 | } |
| | 210 | |
| | 211 | // And stuff a shiny new object with whatever contents we have |
| | 212 | roam = (pWrap || (cnt.nodeType == 11 && !cnt.firstChild)) ? editor._doc.createElement('p') : editor._doc.createDocumentFragment(); |
| | 213 | roam.appendChild(cnt); |
| | 214 | if (preBr) roam.appendChild(editor._doc.createElement('br')); |
| | 215 | |
| | 216 | // Return the nearest relative, relative insertion point and fragment to insert |
| | 217 | return new Array(neighbour, insertion, roam); |
| | 218 | }; |
| | 219 | |
| | 220 | // Called when a key is pressed in the editor |
| | 221 | EnterParagraphs.prototype.__onKeyPress = function(ev) { |
| | 222 | |
| | 223 | // If they've hit enter and shift is up, take it |
| | 224 | if (ev.keyCode == 13 && !ev.shiftKey && this.editor._iframe.contentWindow.getSelection) |
| | 225 | return this.handleEnter(ev); |
| | 226 | }; |
| | 227 | |
| | 228 | // Handles the pressing of an unshifted enter for Gecko |
| | 229 | EnterParagraphs.prototype.handleEnter = function(ev) { |
| | 230 | |
| | 231 | // Grab the selection and associated range |
| | 232 | var sel = this.editor._getSelection(); |
| | 233 | var rng = this.editor._createRange(sel); |
| | 234 | this.takenIds = new Object(); |
| | 235 | |
| | 236 | // Grab ranges for document re-stuffing, if appropriate |
| | 237 | var pStart = this.processSide(rng, true); |
| | 238 | var pEnd = this.processSide(rng, false); |
| | 239 | |
| | 240 | // Get rid of everything local to the selection |
| | 241 | sel.removeAllRanges(); |
| | 242 | rng.deleteContents(); |
| | 243 | |
| | 244 | // Grab a node we'll have after insertion, since fragments will be lost |
| | 245 | var holdEnd = this.forEachNodeUnder(pEnd[2], this._fenEmptySet, true, false, true); |
| | 246 | |
| | 247 | // Reinsert our carefully chosen document fragments |
| | 248 | if ( pStart ) this.insertAdjacentElement(pStart[0], pStart[1], pStart[2]); |
| | 249 | if ( pEnd.nodeType != 1 ) this.insertAdjacentElement(pEnd[0], pEnd[1], pEnd[2]); |
| | 250 | |
| | 251 | // Move the caret in front of the first good text element |
| | 252 | if ( this._permEmpty.test(holdEnd.nodeName) ) { |
| | 253 | var prodigal = 0; |
| | 254 | while ( holdEnd.parentNode.childNodes.item(prodigal) != holdEnd ) prodigal++; |
| | 255 | sel.collapse( holdEnd.parentNode, prodigal); |
| | 256 | } |
| | 257 | else sel.collapse(holdEnd, 0); |
| | 258 | editor.scrollToElement(holdEnd); |
| | 259 | editor.updateToolbar(); |
| | 260 | |
| | 261 | //====================== |
| | 262 | HTMLArea._stopEvent(ev); |
| 57 | | else if (node.nodeType == 3 && node.nodeValue != '') |
| 58 | | return true; |
| 59 | | return null; |
| 60 | | //alert(node.nodeName); |
| 61 | | }; |
| 62 | | |
| 63 | | // Inserts a node deeply on the left of a hierarchy of nodes |
| 64 | | EnterParagraphs.prototype.insertDeepLeftText = function(target, toInsert) { |
| 65 | | var falling = target; |
| 66 | | while (falling.firstChild && falling.firstChild.nodeType == 1) |
| 67 | | falling = falling.firstChild; |
| 68 | | //var refNode = falling.firstChild ? falling.firstChild : null; |
| 69 | | //falling.insertBefore(toInsert, refNode); |
| 70 | | falling.innerHTML = toInsert; |
| 71 | | }; |
| 72 | | |
| 73 | | // Kind of like a macros, for a frequent query... |
| 74 | | EnterParagraphs.prototype.isElem = function(node, type) { |
| 75 | | return node.nodeName.toLowerCase() == type.toLowerCase(); |
| 76 | | }; |
| 77 | | |
| 78 | | // The onKeyPress even that does all the work - nicely breaks the line into paragraphs |
| 79 | | EnterParagraphs.prototype.__onKeyPress = function(ev) { |
| 80 | | |
| 81 | | if (ev.keyCode == 13 && !ev.shiftKey && this.editor._iframe.contentWindow.getSelection) { |
| 82 | | |
| 83 | | var editor = this.editor; |
| 84 | | |
| 85 | | // Get the selection and solid references to what we're dealing with chopping |
| 86 | | var sel = editor._iframe.contentWindow.getSelection(); |
| 87 | | |
| 88 | | // Set the start and end points such that they're going /forward/ through the document |
| 89 | | var rngLeft = editor._doc.createRange(); var rngRight = editor._doc.createRange(); |
| 90 | | rngLeft.setStart(sel.anchorNode, sel.anchorOffset); rngRight.setStart(sel.focusNode, sel.focusOffset); |
| 91 | | rngLeft.collapse(true); rngRight.collapse(true); |
| 92 | | |
| 93 | | var direct = rngLeft.compareBoundaryPoints(rngLeft.START_TO_END, rngRight) < 0; |
| 94 | | |
| 95 | | var startNode = direct ? sel.anchorNode : sel.focusNode; |
| 96 | | var startOffset = direct ? sel.anchorOffset : sel.focusOffset; |
| 97 | | var endNode = direct ? sel.focusNode : sel.anchorNode; |
| 98 | | var endOffset = direct ? sel.focusOffset : sel.anchorOffset; |
| 99 | | |
| 100 | | // Find the parent blocks of nodes at either end, and their attributes if they're paragraphs |
| 101 | | var startBlock = this.parentBlock(startNode); var endBlock = this.parentBlock(endNode); |
| 102 | | var attrsLeft = new Array(); var attrsRight = new Array(); |
| 103 | | |
| 104 | | // If a list, let the browser take over, if we're in a paragraph, gather it's attributes |
| 105 | | if (this.isElem(startBlock, 'li') || this.isElem(endBlock, 'li')) |
| 106 | | return; |
| 107 | | |
| 108 | | if (this.isElem(startBlock, 'p')) { |
| 109 | | for (var i = 0; i < startBlock.attributes.length; i++) { |
| 110 | | attrsLeft[startBlock.attributes[i].nodeName] = startBlock.attributes[i].nodeValue; |
| 111 | | } |
| 112 | | } |
| 113 | | if (this.isElem(endBlock, 'p')) { |
| 114 | | for (var i = 0; i < endBlock.attributes.length; i++) { |
| 115 | | // If we start and end within one paragraph, don't duplicate the 'id' |
| 116 | | if (endBlock != startBlock || endBlock.attributes[i].nodeName.toLowerCase() != 'id') |
| 117 | | attrsRight[endBlock.attributes[i].nodeName] = endBlock.attributes[i].nodeValue; |
| 118 | | } |
| 119 | | } |
| 120 | | |
| 121 | | // Look for where to start and end our chopping - within surrounding paragraphs |
| 122 | | // if they exist, or at the edges of the containing block, otherwise |
| 123 | | var startChop = startNode; var endChop = endNode; |
| 124 | | |
| 125 | | while ((startChop.previousSibling && !this.isElem(startChop.previousSibling, 'p')) |
| 126 | | || (startChop.parentNode && startChop.parentNode != startBlock && startChop.parentNode.nodeType != 9)) |
| 127 | | startChop = startChop.previousSibling ? startChop.previousSibling : startChop.parentNode; |
| 128 | | |
| 129 | | while ((endChop.nextSibling && !this.isElem(endChop.nextSibling, 'p')) |
| 130 | | || (endChop.parentNode && endChop.parentNode != endBlock && endChop.parentNode.nodeType != 9)) |
| 131 | | endChop = endChop.nextSibling ? endChop.nextSibling : endChop.parentNode; |
| 132 | | |
| 133 | | // Set up new paragraphs |
| 134 | | var pLeft = editor._doc.createElement('p'); var pRight = editor._doc.createElement('p'); |
| 135 | | |
| 136 | | for (var attrName in attrsLeft) { |
| 137 | | var thisAttr = editor._doc.createAttribute(attrName); |
| 138 | | thisAttr.value = attrsLeft[attrName]; |
| 139 | | pLeft.setAttributeNode(thisAttr); |
| 140 | | } |
| 141 | | for (var attrName in attrsRight) { |
| 142 | | var thisAttr = editor._doc.createAttribute(attrName); |
| 143 | | thisAttr.value = attrsRight[attrName]; |
| 144 | | pRight.setAttributeNode(thisAttr); |
| 145 | | } |
| 146 | | |
| 147 | | // Get the ranges destined to be stuffed into new paragraphs |
| 148 | | rngLeft.setStartBefore(startChop); |
| 149 | | rngLeft.setEnd(startNode,startOffset); |
| 150 | | pLeft.appendChild(rngLeft.cloneContents()); // Copy into pLeft |
| 151 | | |
| 152 | | rngRight.setEndAfter(endChop); |
| 153 | | rngRight.setStart(endNode,endOffset); |
| 154 | | pRight.appendChild(rngRight.cloneContents()); // Copy into pRight |
| 155 | | |
| 156 | | // If either paragraph is empty, fill it with a nonbreakable space |
| 157 | | var foundBlock = false; |
| 158 | | foundBlock = this.walkNodeChildren(pLeft, this._isFilling); |
| 159 | | if (foundBlock != true) |
| 160 | | this.insertDeepLeftText(pLeft, ' '); |
| 161 | | |
| 162 | | foundBlock = false; |
| 163 | | foundBlock = this.walkNodeChildren(pRight, this._isFilling); |
| 164 | | if (foundBlock != true) |
| 165 | | this.insertDeepLeftText(pRight, ' '); |
| 166 | | |
| 167 | | // Get a range for everything to be replaced and replace it |
| 168 | | var rngAround = editor._doc.createRange(); |
| 169 | | |
| 170 | | if (!startChop.previousSibling && this.isElem(startChop.parentNode, 'p')) |
| 171 | | rngAround.setStartBefore(startChop.parentNode); |
| 172 | | else |
| 173 | | rngAround.setStart(rngLeft.startContainer, rngLeft.startOffset); |
| 174 | | |
| 175 | | if (!endChop.nextSibling && this.isElem(endChop.parentNode, 'p')) |
| 176 | | rngAround.setEndAfter(endChop.parentNode); |
| 177 | | else |
| 178 | | rngAround.setEnd(rngRight.endContainer, rngRight.endOffset); |
| 179 | | |
| 180 | | rngAround.deleteContents(); |
| 181 | | rngAround.insertNode(pRight); |
| 182 | | rngAround.insertNode(pLeft); |
| 183 | | |
| 184 | | // Set the selection to the start of the (second) new paragraph |
| 185 | | if (pRight.firstChild) { |
| 186 | | while (pRight.firstChild && this._html4_inlines_re.test(pRight.firstChild.nodeName)) |
| 187 | | pRight = pRight.firstChild; |
| 188 | | // Slip into any inline tags |
| 189 | | if (pRight.firstChild && pRight.firstChild.nodeType == 3) |
| 190 | | pRight = pRight.firstChild; // and text, if they've got it |
| 191 | | |
| 192 | | var rngCaret = editor._doc.createRange(); |
| 193 | | rngCaret.setStart(pRight, 0); |
| 194 | | rngCaret.collapse(true); |
| 195 | | |
| 196 | | sel = editor._iframe.contentWindow.getSelection(); |
| 197 | | sel.removeAllRanges(); |
| 198 | | sel.addRange(rngCaret); |
| 199 | | } |
| 200 | | |
| 201 | | // Stop the bubbling |
| 202 | | HTMLArea._stopEvent(ev); |
| 203 | | } |
| 204 | | }; |
| | 264 | }; |