Index: openacs-4/packages/acs-templating/www/resources/xinha-nightly/modules/Gecko/paraHandlerBest.js =================================================================== RCS file: /usr/local/cvsroot/openacs-4/packages/acs-templating/www/resources/xinha-nightly/modules/Gecko/paraHandlerBest.js,v diff -u -r1.1 -r1.2 --- openacs-4/packages/acs-templating/www/resources/xinha-nightly/modules/Gecko/paraHandlerBest.js 2 Feb 2007 21:04:47 -0000 1.1 +++ openacs-4/packages/acs-templating/www/resources/xinha-nightly/modules/Gecko/paraHandlerBest.js 25 Feb 2007 19:06:07 -0000 1.2 @@ -1,907 +1,293 @@ -// tabs 2 - -/** -* @fileoverview By Adam Wright, for The University of Western Australia -* -* Distributed under the same terms as Xinha itself. -* This notice MUST stay intact for use (see license.txt). -* -* Heavily modified by Yermo Lamers of DTLink, LLC, College Park, Md., USA. -* For more info see http://www.areaedit.com -*/ - -/** -* plugin Info -*/ - -EnterParagraphs._pluginInfo = -{ - name : "EnterParagraphs", - version : "1.0", - developer : "Adam Wright", - developer_url : "http://www.hipikat.org/", - sponsor : "The University of Western Australia", - sponsor_url : "http://www.uwa.edu.au/", - license : "htmlArea" +EnterParagraphs._pluginInfo={name:"EnterParagraphs",version:"1.0",developer:"Adam Wright",developer_url:"http://www.hipikat.org/",sponsor:"The University of Western Australia",sponsor_url:"http://www.uwa.edu.au/",license:"htmlArea"}; +EnterParagraphs.prototype._whiteSpace=/^\s*$/; +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; +EnterParagraphs.prototype._pContainers=/^(body|del|div|fieldset|form|ins|map|noscript|object|td|th)$/i; +EnterParagraphs.prototype._pBreak=/^(address|pre|blockquote)$/i; +EnterParagraphs.prototype._permEmpty=/^(area|base|basefont|br|col|frame|hr|img|input|isindex|link|meta|param)$/i; +EnterParagraphs.prototype._elemSolid=/^(applet|br|button|hr|img|input|table)$/i; +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; +EnterParagraphs.prototype._pifyForced=/^(ul|ol|dl|table)$/i; +EnterParagraphs.prototype._pifyParent=/^(dd|dt|li|td|th|tr)$/i; +function EnterParagraphs(_1){ +this.editor=_1; +if(Xinha.is_gecko){ +this.onKeyPress=this.__onKeyPress; +} +} +EnterParagraphs.prototype.name="EnterParagraphs"; +EnterParagraphs.prototype.insertAdjacentElement=function(_2,_3,el){ +if(_3=="BeforeBegin"){ +_2.parentNode.insertBefore(el,_2); +}else{ +if(_3=="AfterEnd"){ +_2.nextSibling?_2.parentNode.insertBefore(el,_2.nextSibling):_2.parentNode.appendChild(el); +}else{ +if(_3=="AfterBegin"&&_2.firstChild){ +_2.insertBefore(el,_2.firstChild); +}else{ +if(_3=="BeforeEnd"||_3=="AfterBegin"){ +_2.appendChild(el); +} +} +} +} }; - -// ------------------------------------------------------------------ - -// "constants" - -/** -* Whitespace Regex -*/ - -EnterParagraphs.prototype._whiteSpace = /^\s*$/; - -/** -* The pragmatic list of which elements a paragraph may not contain -*/ - -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; - -/** -* elements which may contain a paragraph -*/ - -EnterParagraphs.prototype._pContainers = /^(body|del|div|fieldset|form|ins|map|noscript|object|td|th)$/i; - -/** -* Elements which may not contain paragraphs, and would prefer a break to being split -*/ - -EnterParagraphs.prototype._pBreak = /^(address|pre|blockquote)$/i; - -/** -* Elements which may not contain children -*/ - -EnterParagraphs.prototype._permEmpty = /^(area|base|basefont|br|col|frame|hr|img|input|isindex|link|meta|param)$/i; - -/** -* Elements which count as content, as distinct from whitespace or containers -*/ - -EnterParagraphs.prototype._elemSolid = /^(applet|br|button|hr|img|input|table)$/i; - -/** -* Elements which should get a new P, before or after, when enter is pressed at either end -*/ - -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; -EnterParagraphs.prototype._pifyForced = /^(ul|ol|dl|table)$/i; - -/** -* Elements which should get a new P, before or after a close parent, when enter is pressed at either end -*/ - -EnterParagraphs.prototype._pifyParent = /^(dd|dt|li|td|th|tr)$/i; - -// --------------------------------------------------------------------- - -/** -* EnterParagraphs Constructor -*/ - -function EnterParagraphs(editor) -{ - - this.editor = editor; - - // hook into the event handler to intercept key presses if we are using - // gecko (Mozilla/FireFox) - if (Xinha.is_gecko) - { - this.onKeyPress = this.__onKeyPress; - } - -} // end of constructor. - -// ------------------------------------------------------------------ - -/** -* name member for debugging -* -* This member is used to identify objects of this class in debugging -* messages. -*/ -EnterParagraphs.prototype.name = "EnterParagraphs"; - -/** -* Gecko's a bit lacking in some odd ways... -*/ -EnterParagraphs.prototype.insertAdjacentElement = function(ref,pos,el) -{ - if ( pos == 'BeforeBegin' ) - { - ref.parentNode.insertBefore(el,ref); - } - else if ( pos == 'AfterEnd' ) - { - ref.nextSibling ? ref.parentNode.insertBefore(el,ref.nextSibling) : ref.parentNode.appendChild(el); - } - else if ( pos == 'AfterBegin' && ref.firstChild ) - { - ref.insertBefore(el,ref.firstChild); - } - else if ( pos == 'BeforeEnd' || pos == 'AfterBegin' ) - { - ref.appendChild(el); - } - -}; // end of insertAdjacentElement() - -// ---------------------------------------------------------------- - -/** -* Passes a global parent node or document fragment to forEachNode -* -* @param root node root node to start search from. -* @param mode string function to apply to each node. -* @param direction string traversal direction "ltr" (left to right) or "rtl" (right_to_left) -* @param init boolean -*/ - -EnterParagraphs.prototype.forEachNodeUnder = function ( root, mode, direction, init ) -{ - - // Identify the first and last nodes to deal with - var start, end; - - // nodeType 11 is DOCUMENT_FRAGMENT_NODE which is a container. - if ( root.nodeType == 11 && root.firstChild ) - { - start = root.firstChild; - end = root.lastChild; - } - else - { - start = end = root; - } - // traverse down the right hand side of the tree getting the last child of the last - // child in each level until we reach bottom. - while ( end.lastChild ) - { - end = end.lastChild; - } - - return this.forEachNode( start, end, mode, direction, init); - -}; // end of forEachNodeUnder() - -// ----------------------------------------------------------------------- - -/** -* perform a depth first descent in the direction requested. -* -* @param left_node node "start node" -* @param right_node node "end node" -* @param mode string function to apply to each node. cullids or emptyset. -* @param direction string traversal direction "ltr" (left to right) or "rtl" (right_to_left) -* @param init boolean or object. -*/ - -EnterParagraphs.prototype.forEachNode = function (left_node, right_node, mode, direction, init) -{ - - // returns "Brother" node either left or right. - var getSibling = function(elem, direction) - { - return ( direction == "ltr" ? elem.nextSibling : elem.previousSibling ); - }; - - var getChild = function(elem, direction) - { - return ( direction == "ltr" ? elem.firstChild : elem.lastChild ); - }; - - var walk, lookup, fnReturnVal; - - // FIXME: init is a boolean in the emptyset case and an object in - // the cullids case. Used inconsistently. - - var next_node = init; - - // used to flag having reached the last node. - - var done_flag = false; - - // loop ntil we've hit the last node in the given direction. - // if we're going left to right that's the right_node and visa-versa. - - while ( walk != direction == "ltr" ? right_node : left_node ) - { - - // on first entry, walk here is null. So this is how - // we prime the loop with the first node. - - if ( !walk ) - { - walk = direction == "ltr" ? left_node : right_node; - } - else - { - - // is there a child node? - - if ( getChild(walk,direction) ) - { - - // descend down into the child. - - walk = getChild(walk,direction); - - } - else - { - - // is there a sibling node on this level? - - if ( getSibling(walk,direction) ) - { - // move to the sibling. - walk = getSibling(walk,direction); - } - else - { - lookup = walk; - - // climb back up the tree until we find a level where we are not the end - // node on the level (i.e. that we have a sibling in the direction - // we are searching) or until we reach the end. - - while ( !getSibling(lookup,direction) && lookup != (direction == "ltr" ? right_node : left_node) ) - { - lookup = lookup.parentNode; - } - - // did we find a level with a sibling? - - // walk = ( lookup.nextSibling ? lookup.nextSibling : lookup ) ; - - walk = ( getSibling(lookup,direction) ? getSibling(lookup,direction) : lookup ) ; - - } - } - - } // end of else walk. - - // have we reached the end? either as a result of the top while loop or climbing - // back out above. - - done_flag = (walk==( direction == "ltr" ? right_node : left_node)); - - // call the requested function on the current node. Functions - // return an array. - // - // Possible functions are _fenCullIds, _fenEmptySet - // - // The situation is complicated by the fact that sometimes we want to - // return the base node and sometimes we do not. - // - // next_node can be an object (this.takenIds), a node (text, el, etc) or false. - - switch( mode ) - { - - case "cullids": - - fnReturnVal = this._fenCullIds(walk, next_node ); - break; - - case "find_fill": - - fnReturnVal = this._fenEmptySet(walk, next_node, mode, done_flag); - break; - - case "find_cursorpoint": - - fnReturnVal = this._fenEmptySet(walk, next_node, mode, done_flag); - break; - - } - - // If this node wants us to return, return next_node - - if ( fnReturnVal[0] ) - { - return fnReturnVal[1]; - } - - // are we done with the loop? - - if ( done_flag ) - { - break; - } - - // Otherwise, pass to the next node - - if ( fnReturnVal[1] ) - { - next_node = fnReturnVal[1]; - } - - } // end of while loop - - return false; - -}; // end of forEachNode() - -// ------------------------------------------------------------------- - -/** -* Find a post-insertion node, only if all nodes are empty, or the first content -* -* @param node node current node beinge examined. -* @param next_node node next node to be examined. -* @param node string "find_fill" or "find_cursorpoint" -* @param last_flag boolean is this the last node? -*/ - -EnterParagraphs.prototype._fenEmptySet = function( node, next_node, mode, last_flag) -{ - - // Mark this if it's the first base - - if ( !next_node && !node.firstChild ) - { - next_node = node; - } - - // Is it an element node and is it considered content? (br, hr, etc) - // or is it a text node that is not just whitespace? - // or is it not an element node and not a text node? - - if ( (node.nodeType == 1 && this._elemSolid.test(node.nodeName)) || - (node.nodeType == 3 && !this._whiteSpace.test(node.nodeValue)) || - (node.nodeType != 1 && node.nodeType != 3) ) - { - - switch( mode ) - { - - case "find_fill": - - // does not return content. - - return new Array(true, false ); - break; - - case "find_cursorpoint": - - // returns content - - return new Array(true, node ); - break; - - } - - } - - // In either case (fill or findcursor) we return the base node. The avoids - // problems in terminal cases (beginning or end of document or container tags) - - if ( last_flag ) - { - return new Array( true, next_node ); - } - - return new Array( false, next_node ); - -}; // end of _fenEmptySet() - -// ------------------------------------------------------------------------------ - -/** -* remove duplicate Id's. -* -* @param ep_ref enterparagraphs reference to enterparagraphs object -*/ - -EnterParagraphs.prototype._fenCullIds = function ( ep_ref, node, pong ) -{ - - // Check for an id, blast it if it's in the store, otherwise add it - - if ( node.id ) - { - - pong[node.id] ? node.id = '' : pong[node.id] = true; - } - - return new Array(false,pong); - +EnterParagraphs.prototype.forEachNodeUnder=function(_5,_6,_7,_8){ +var _9,end; +if(_5.nodeType==11&&_5.firstChild){ +_9=_5.firstChild; +end=_5.lastChild; +}else{ +_9=end=_5; +} +while(end.lastChild){ +end=end.lastChild; +} +return this.forEachNode(_9,end,_6,_7,_8); }; - -// --------------------------------------------------------------------------------- - -/** -* Grabs a range suitable for paragraph stuffing -* -* @param rng Range -* @param search_direction string "left" or "right" -* -* @todo check blank node issue in roaming loop. -*/ - -EnterParagraphs.prototype.processSide = function( rng, search_direction) -{ - - var next = function(element, search_direction) - { - return ( search_direction == "left" ? element.previousSibling : element.nextSibling ); - }; - - var node = search_direction == "left" ? rng.startContainer : rng.endContainer; - var offset = search_direction == "left" ? rng.startOffset : rng.endOffset; - var roam, start = node; - - // Never start with an element, because then the first roaming node might - // be on the exclusion list and we wouldn't know until it was too late - - while ( start.nodeType == 1 && !this._permEmpty.test(start.nodeName) ) - { - start = ( offset ? start.lastChild : start.firstChild ); - } - - // Climb the tree, left or right, until our course of action presents itself - // - // if roam is NULL try start. - // if roam is NOT NULL, try next node in our search_direction - // If that node is NULL, get our parent node. - // - // If all the above turns out NULL end the loop. - // - // FIXME: gecko (firefox 1.0.3) - enter "test" into an empty document and press enter. - // sometimes this loop finds a blank text node, sometimes it doesn't. - - while ( roam = roam ? ( next(roam,search_direction) ? next(roam,search_direction) : roam.parentNode ) : start ) - { - - // next() is an inline function defined above that returns the next node depending - // on the direction we're searching. - - if ( next(roam,search_direction) ) - { - - // If the next sibling's on the exclusion list, stop before it - - if ( this._pExclusions.test(next(roam,search_direction).nodeName) ) - { - - return this.processRng(rng, search_direction, roam, next(roam,search_direction), (search_direction == "left"?'AfterEnd':'BeforeBegin'), true, false); - } - } - else - { - - // If our parent's on the container list, stop inside it - - if (this._pContainers.test(roam.parentNode.nodeName)) - { - - return this.processRng(rng, search_direction, roam, roam.parentNode, (search_direction == "left"?'AfterBegin':'BeforeEnd'), true, false); - } - else if (this._pExclusions.test(roam.parentNode.nodeName)) - { - - // chop without wrapping - - if (this._pBreak.test(roam.parentNode.nodeName)) - { - - return this.processRng(rng, search_direction, roam, roam.parentNode, - (search_direction == "left"?'AfterBegin':'BeforeEnd'), false, (search_direction == "left" ?true:false)); - } - else - { - - // the next(roam,search_direction) in this call is redundant since we know it's false - // because of the "if next(roam,search_direction)" above. - // - // the final false prevents this range from being wrapped in

's most likely - // because it's already wrapped. - - return this.processRng(rng, - search_direction, - (roam = roam.parentNode), - (next(roam,search_direction) ? next(roam,search_direction) : roam.parentNode), - (next(roam,search_direction) ? (search_direction == "left"?'AfterEnd':'BeforeBegin') : (search_direction == "left"?'AfterBegin':'BeforeEnd')), - false, - false); - } - } - } - } - -}; // end of processSide() - -// ------------------------------------------------------------------------------ - -/** -* processRng - process Range. -* -* Neighbour and insertion identify where the new node, roam, needs to enter -* the document; landmarks in our selection will be deleted before insertion -* -* @param rn Range original selected range -* @param search_direction string Direction to search in. -* @param roam node -* @param insertion string may be AfterBegin of BeforeEnd -* @return array -*/ - -EnterParagraphs.prototype.processRng = function(rng, search_direction, roam, neighbour, insertion, pWrap, preBr) -{ - var node = search_direction == "left" ? rng.startContainer : rng.endContainer; - var offset = search_direction == "left" ? rng.startOffset : rng.endOffset; - - // Define the range to cut, and extend the selection range to the same boundary - - var editor = this.editor; - var newRng = editor._doc.createRange(); - - newRng.selectNode(roam); - // extend the range in the given direction. - - if ( search_direction == "left") - { - newRng.setEnd(node, offset); - rng.setStart(newRng.startContainer, newRng.startOffset); - } - else if ( search_direction == "right" ) - { - - newRng.setStart(node, offset); - rng.setEnd(newRng.endContainer, newRng.endOffset); - } - // Clone the range and remove duplicate ids it would otherwise produce - - var cnt = newRng.cloneContents(); - - // in this case "init" is an object not a boolen. - - this.forEachNodeUnder( cnt, "cullids", "ltr", this.takenIds, false, false); - - // Special case, for inserting paragraphs before some blocks when caret is at - // their zero offset. - // - // Used to "open up space" in front of a list, table. Usefull if the list is at - // the top of the document. (otherwise you'd have no way of "moving it down"). - - var pify, pifyOffset, fill; - pify = search_direction == "left" ? (newRng.endContainer.nodeType == 3 ? true:false) : (newRng.startContainer.nodeType == 3 ? false:true); - pifyOffset = pify ? newRng.startOffset : newRng.endOffset; - pify = pify ? newRng.startContainer : newRng.endContainer; - - if ( this._pifyParent.test(pify.nodeName) && pify.parentNode.childNodes.item(0) == pify ) - { - while ( !this._pifySibling.test(pify.nodeName) ) - { - pify = pify.parentNode; - } - } - - // NODE TYPE 11 is DOCUMENT_FRAGMENT NODE - // I do not profess to understand any of this, simply applying a patch that others say is good - ticket:446 - if ( cnt.nodeType == 11 && !cnt.firstChild) - { - if (pify.nodeName != "BODY" || (pify.nodeName == "BODY" && pifyOffset != 0)) - { //WKR: prevent body tag in empty doc - cnt.appendChild(editor._doc.createElement(pify.nodeName)); - } - } - - // YmL: Added additional last parameter for fill case to work around logic - // error in forEachNode() - - fill = this.forEachNodeUnder(cnt, "find_fill", "ltr", false ); - - if ( fill && - this._pifySibling.test(pify.nodeName) && - ( (pifyOffset == 0) || ( pifyOffset == 1 && this._pifyForced.test(pify.nodeName) ) ) ) - { - - roam = editor._doc.createElement( 'p' ); - roam.innerHTML = " "; - - // roam = editor._doc.createElement('p'); - // roam.appendChild(editor._doc.createElement('br')); - - // for these cases, if we are processing the left hand side we want it to halt - // processing instead of doing the right hand side. (Avoids adding another

 

- // after the list etc. - - if ((search_direction == "left" ) && pify.previousSibling) - { - - return new Array(pify.previousSibling, 'AfterEnd', roam); - } - else if (( search_direction == "right") && pify.nextSibling) - { - - return new Array(pify.nextSibling, 'BeforeBegin', roam); - } - else - { - - return new Array(pify.parentNode, (search_direction == "left"?'AfterBegin':'BeforeEnd'), roam); - } - - } - - // If our cloned contents are 'content'-less, shove a break in them - - if ( fill ) - { - - // Ill-concieved? - // - // 3 is a TEXT node and it should be empty. - // - - if ( fill.nodeType == 3 ) - { - // fill = fill.parentNode; - - fill = editor._doc.createDocumentFragment(); - } - - if ( (fill.nodeType == 1 && !this._elemSolid.test()) || fill.nodeType == 11 ) - { - - // FIXME:/CHECKME: When Xinha is switched from WYSIWYG to text mode - // Xinha.getHTMLWrapper() will strip out the trailing br. Not sure why. - - // fill.appendChild(editor._doc.createElement('br')); - - var pterminator = editor._doc.createElement( 'p' ); - pterminator.innerHTML = " "; - - fill.appendChild( pterminator ); - - } - else - { - - // fill.parentNode.insertBefore(editor._doc.createElement('br'),fill); - - var pterminator = editor._doc.createElement( 'p' ); - pterminator.innerHTML = " "; - - fill.parentNode.insertBefore(parentNode,fill); - - } - } - - // YmL: If there was no content replace with fill - // (previous code did not use fill and we ended up with the - //

test

because Gecko was finding two empty text nodes - // when traversing on the right hand side of an empty document. - - if ( fill ) - { - - roam = fill; - } - else - { - // And stuff a shiny new object with whatever contents we have - - roam = (pWrap || (cnt.nodeType == 11 && !cnt.firstChild)) ? editor._doc.createElement('p') : editor._doc.createDocumentFragment(); - roam.appendChild(cnt); - } - - if (preBr) - { - roam.appendChild(editor._doc.createElement('br')); - } - // Return the nearest relative, relative insertion point and fragment to insert - - return new Array(neighbour, insertion, roam); - -}; // end of processRng() - -// ---------------------------------------------------------------------------------- - -/** -* are we an
  • that should be handled by the browser? -* -* there is no good way to "get out of" ordered or unordered lists from Javascript. -* We have to pass the onKeyPress 13 event to the browser so it can take care of -* getting us "out of" the list. -* -* The Gecko engine does a good job of handling all the normal
  • cases except the "press -* enter at the first position" where we want a

     

    inserted before the list. The -* built-in behavior is to open up a
  • before the current entry (not good). -* -* @param rng Range range. -*/ - -EnterParagraphs.prototype.isNormalListItem = function(rng) -{ - - var node, listNode; - - node = rng.startContainer; - - if (( typeof node.nodeName != 'undefined') && - ( node.nodeName.toLowerCase() == 'li' )) - { - - // are we a list item? - - listNode = node; - } - else if (( typeof node.parentNode != 'undefined' ) && - ( typeof node.parentNode.nodeName != 'undefined' ) && - ( node.parentNode.nodeName.toLowerCase() == 'li' )) - { - - // our parent is a list item. - - listNode = node.parentNode; - - } - else - { - // neither we nor our parent are a list item. this is not a normal - // li case. - - return false; - } - - // at this point we have a listNode. Is it the first list item? - - if ( ! listNode.previousSibling ) - { - // are we on the first character of the first li? - - if ( rng.startOffset == 0 ) - { - return false; - } - } - return true; - -}; // end of isNormalListItem() - -// ---------------------------------------------------------------------------------- -/** -* Called when a key is pressed in the editor -*/ - -EnterParagraphs.prototype.__onKeyPress = function(ev) -{ - - // If they've hit enter and shift is not pressed, handle it - - if (ev.keyCode == 13 && !ev.shiftKey && this.editor._iframe.contentWindow.getSelection) - { - return this.handleEnter(ev); - } - -}; // end of _onKeyPress() - -// ----------------------------------------------------------------------------------- - -/** -* Handles the pressing of an unshifted enter for Gecko -*/ - -EnterParagraphs.prototype.handleEnter = function(ev) -{ - - var cursorNode; - - // Grab the selection and associated range - - var sel = this.editor.getSelection(); - var rng = this.editor.createRange(sel); - - // if we are at the end of a list and the node is empty let the browser handle - // it to get us out of the list. - - if ( this.isNormalListItem(rng) ) - { - - return true; - } - - // as far as I can tell this isn't actually used. - - this.takenIds = new Object(); - - // Grab ranges for document re-stuffing, if appropriate - // - // pStart and pEnd are arrays consisting of - // [0] neighbor node - // [1] insertion type - // [2] roam - - var pStart = this.processSide(rng, "left"); - - var pEnd = this.processSide(rng, "right"); - - // used to position the cursor after insertion. - - cursorNode = pEnd[2]; - - // Get rid of everything local to the selection - - sel.removeAllRanges(); - rng.deleteContents(); - - // Grab a node we'll have after insertion, since fragments will be lost - // - // we'll use this to position the cursor. - - var holdEnd = this.forEachNodeUnder( cursorNode, "find_cursorpoint", "ltr", false, true); - - if ( ! holdEnd ) - { - alert( "INTERNAL ERROR - could not find place to put cursor after ENTER" ); - } - - // Insert our carefully chosen document fragments - - if ( pStart ) - { - - this.insertAdjacentElement(pStart[0], pStart[1], pStart[2]); - } - - if ( pEnd && pEnd.nodeType != 1) - { - - this.insertAdjacentElement(pEnd[0], pEnd[1], pEnd[2]); - } - - // Move the caret in front of the first good text element - - if ((holdEnd) && (this._permEmpty.test(holdEnd.nodeName) )) - { - - var prodigal = 0; - while ( holdEnd.parentNode.childNodes.item(prodigal) != holdEnd ) - { - prodigal++; - } - - sel.collapse( holdEnd.parentNode, prodigal); - } - else - { - - // holdEnd might be false. - - try - { - sel.collapse(holdEnd, 0); - - // interestingly, scrollToElement() scroll so the top if holdEnd is a text node. - - if ( holdEnd.nodeType == 3 ) - { - holdEnd = holdEnd.parentNode; - } - - this.editor.scrollToElement(holdEnd); - } - catch (e) - { - // we could try to place the cursor at the end of the document. - } - } - - this.editor.updateToolbar(); - - Xinha._stopEvent(ev); - - return true; - -}; // end of handleEnter() - -// END \ No newline at end of file +EnterParagraphs.prototype.forEachNode=function(_a,_b,_c,_d,_e){ +var _f=function(_10,_11){ +return (_11=="ltr"?_10.nextSibling:_10.previousSibling); +}; +var _12=function(_13,_14){ +return (_14=="ltr"?_13.firstChild:_13.lastChild); +}; +var _15,lookup,fnReturnVal; +var _16=_e; +var _17=false; +while(_15!=_d=="ltr"?_b:_a){ +if(!_15){ +_15=_d=="ltr"?_a:_b; +}else{ +if(_12(_15,_d)){ +_15=_12(_15,_d); +}else{ +if(_f(_15,_d)){ +_15=_f(_15,_d); +}else{ +lookup=_15; +while(!_f(lookup,_d)&&lookup!=(_d=="ltr"?_b:_a)){ +lookup=lookup.parentNode; +} +_15=(_f(lookup,_d)?_f(lookup,_d):lookup); +} +} +} +_17=(_15==(_d=="ltr"?_b:_a)); +switch(_c){ +case "cullids": +fnReturnVal=this._fenCullIds(_15,_16); +break; +case "find_fill": +fnReturnVal=this._fenEmptySet(_15,_16,_c,_17); +break; +case "find_cursorpoint": +fnReturnVal=this._fenEmptySet(_15,_16,_c,_17); +break; +} +if(fnReturnVal[0]){ +return fnReturnVal[1]; +} +if(_17){ +break; +} +if(fnReturnVal[1]){ +_16=fnReturnVal[1]; +} +} +return false; +}; +EnterParagraphs.prototype._fenEmptySet=function(_18,_19,_1a,_1b){ +if(!_19&&!_18.firstChild){ +_19=_18; +} +if((_18.nodeType==1&&this._elemSolid.test(_18.nodeName))||(_18.nodeType==3&&!this._whiteSpace.test(_18.nodeValue))||(_18.nodeType!=1&&_18.nodeType!=3)){ +switch(_1a){ +case "find_fill": +return new Array(true,false); +break; +case "find_cursorpoint": +return new Array(true,_18); +break; +} +} +if(_1b){ +return new Array(true,_19); +} +return new Array(false,_19); +}; +EnterParagraphs.prototype._fenCullIds=function(_1c,_1d,_1e){ +if(_1d.id){ +_1e[_1d.id]?_1d.id="":_1e[_1d.id]=true; +} +return new Array(false,_1e); +}; +EnterParagraphs.prototype.processSide=function(rng,_20){ +var _21=function(_22,_23){ +return (_23=="left"?_22.previousSibling:_22.nextSibling); +}; +var _24=_20=="left"?rng.startContainer:rng.endContainer; +var _25=_20=="left"?rng.startOffset:rng.endOffset; +var _26,start=_24; +while(start.nodeType==1&&!this._permEmpty.test(start.nodeName)){ +start=(_25?start.lastChild:start.firstChild); +} +while(_26=_26?(_21(_26,_20)?_21(_26,_20):_26.parentNode):start){ +if(_21(_26,_20)){ +if(this._pExclusions.test(_21(_26,_20).nodeName)){ +return this.processRng(rng,_20,_26,_21(_26,_20),(_20=="left"?"AfterEnd":"BeforeBegin"),true,false); +} +}else{ +if(this._pContainers.test(_26.parentNode.nodeName)){ +return this.processRng(rng,_20,_26,_26.parentNode,(_20=="left"?"AfterBegin":"BeforeEnd"),true,false); +}else{ +if(this._pExclusions.test(_26.parentNode.nodeName)){ +if(this._pBreak.test(_26.parentNode.nodeName)){ +return this.processRng(rng,_20,_26,_26.parentNode,(_20=="left"?"AfterBegin":"BeforeEnd"),false,(_20=="left"?true:false)); +}else{ +return this.processRng(rng,_20,(_26=_26.parentNode),(_21(_26,_20)?_21(_26,_20):_26.parentNode),(_21(_26,_20)?(_20=="left"?"AfterEnd":"BeforeBegin"):(_20=="left"?"AfterBegin":"BeforeEnd")),false,false); +} +} +} +} +} +}; +EnterParagraphs.prototype.processRng=function(rng,_28,_29,_2a,_2b,_2c,_2d){ +var _2e=_28=="left"?rng.startContainer:rng.endContainer; +var _2f=_28=="left"?rng.startOffset:rng.endOffset; +var _30=this.editor; +var _31=_30._doc.createRange(); +_31.selectNode(_29); +if(_28=="left"){ +_31.setEnd(_2e,_2f); +rng.setStart(_31.startContainer,_31.startOffset); +}else{ +if(_28=="right"){ +_31.setStart(_2e,_2f); +rng.setEnd(_31.endContainer,_31.endOffset); +} +} +var cnt=_31.cloneContents(); +this.forEachNodeUnder(cnt,"cullids","ltr",this.takenIds,false,false); +var _33,pifyOffset,fill; +_33=_28=="left"?(_31.endContainer.nodeType==3?true:false):(_31.startContainer.nodeType==3?false:true); +pifyOffset=_33?_31.startOffset:_31.endOffset; +_33=_33?_31.startContainer:_31.endContainer; +if(this._pifyParent.test(_33.nodeName)&&_33.parentNode.childNodes.item(0)==_33){ +while(!this._pifySibling.test(_33.nodeName)){ +_33=_33.parentNode; +} +} +if(cnt.nodeType==11&&!cnt.firstChild){ +if(_33.nodeName!="BODY"||(_33.nodeName=="BODY"&&pifyOffset!=0)){ +cnt.appendChild(_30._doc.createElement(_33.nodeName)); +} +} +fill=this.forEachNodeUnder(cnt,"find_fill","ltr",false); +if(fill&&this._pifySibling.test(_33.nodeName)&&((pifyOffset==0)||(pifyOffset==1&&this._pifyForced.test(_33.nodeName)))){ +_29=_30._doc.createElement("p"); +_29.innerHTML=" "; +if((_28=="left")&&_33.previousSibling){ +return new Array(_33.previousSibling,"AfterEnd",_29); +}else{ +if((_28=="right")&&_33.nextSibling){ +return new Array(_33.nextSibling,"BeforeBegin",_29); +}else{ +return new Array(_33.parentNode,(_28=="left"?"AfterBegin":"BeforeEnd"),_29); +} +} +} +if(fill){ +if(fill.nodeType==3){ +fill=_30._doc.createDocumentFragment(); +} +if((fill.nodeType==1&&!this._elemSolid.test())||fill.nodeType==11){ +var _34=_30._doc.createElement("p"); +_34.innerHTML=" "; +fill.appendChild(_34); +}else{ +var _34=_30._doc.createElement("p"); +_34.innerHTML=" "; +fill.parentNode.insertBefore(parentNode,fill); +} +} +if(fill){ +_29=fill; +}else{ +_29=(_2c||(cnt.nodeType==11&&!cnt.firstChild))?_30._doc.createElement("p"):_30._doc.createDocumentFragment(); +_29.appendChild(cnt); +} +if(_2d){ +_29.appendChild(_30._doc.createElement("br")); +} +return new Array(_2a,_2b,_29); +}; +EnterParagraphs.prototype.isNormalListItem=function(rng){ +var _36,listNode; +_36=rng.startContainer; +if((typeof _36.nodeName!="undefined")&&(_36.nodeName.toLowerCase()=="li")){ +listNode=_36; +}else{ +if((typeof _36.parentNode!="undefined")&&(typeof _36.parentNode.nodeName!="undefined")&&(_36.parentNode.nodeName.toLowerCase()=="li")){ +listNode=_36.parentNode; +}else{ +return false; +} +} +if(!listNode.previousSibling){ +if(rng.startOffset==0){ +return false; +} +} +return true; +}; +EnterParagraphs.prototype.__onKeyPress=function(ev){ +if(ev.keyCode==13&&!ev.shiftKey&&this.editor._iframe.contentWindow.getSelection){ +return this.handleEnter(ev); +} +}; +EnterParagraphs.prototype.handleEnter=function(ev){ +var _39; +var sel=this.editor.getSelection(); +var rng=this.editor.createRange(sel); +if(this.isNormalListItem(rng)){ +return true; +} +this.takenIds=new Object(); +var _3c=this.processSide(rng,"left"); +var _3d=this.processSide(rng,"right"); +_39=_3d[2]; +sel.removeAllRanges(); +rng.deleteContents(); +var _3e=this.forEachNodeUnder(_39,"find_cursorpoint","ltr",false,true); +if(!_3e){ +alert("INTERNAL ERROR - could not find place to put cursor after ENTER"); +} +if(_3c){ +this.insertAdjacentElement(_3c[0],_3c[1],_3c[2]); +} +if(_3d&&_3d.nodeType!=1){ +this.insertAdjacentElement(_3d[0],_3d[1],_3d[2]); +} +if((_3e)&&(this._permEmpty.test(_3e.nodeName))){ +var _3f=0; +while(_3e.parentNode.childNodes.item(_3f)!=_3e){ +_3f++; +} +sel.collapse(_3e.parentNode,_3f); +}else{ +try{ +sel.collapse(_3e,0); +if(_3e.nodeType==3){ +_3e=_3e.parentNode; +} +this.editor.scrollToElement(_3e); +} +catch(e){ +} +} +this.editor.updateToolbar(); +Xinha._stopEvent(ev); +return true; +}; +