home *** CD-ROM | disk | FTP | other *** search
/ Chip 2004 April / CMCD0404.ISO / Software / Freeware / Programare / groupoffice-com-2.01 / controls / htmlarea / plugins / EnterParagraphs / enter-paragraphs.js
Text File  |  2004-03-08  |  8KB  |  205 lines

  1. // Modification to htmlArea to insert Paragraphs instead of
  2. // linebreaks, under Gecko engines, circa January 2004
  3. // By Adam Wright, for The University of Western Australia
  4. //
  5. // Distributed under the same terms as HTMLArea itself.
  6. // This notice MUST stay intact for use (see license.txt).
  7.  
  8. function EnterParagraphs(editor, params) {
  9.     this.editor = editor;
  10.     // activate only if we're talking to Gecko
  11.     if (HTMLArea.is_gecko)
  12.         this.onKeyPress = this.__onKeyPress;
  13. };
  14.  
  15. EnterParagraphs._pluginInfo = {
  16.     name          : "EnterParagraphs",
  17.     version       : "1.0",
  18.     developer     : "Adam Wright",
  19.     developer_url : "http://blog.hipikat.org/",
  20.     sponsor       : "The University of Western Australia",
  21.     sponsor_url   : "http://www.uwa.edu.au/",
  22.     license       : "htmlArea"
  23. };
  24.  
  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))
  56.         return true;
  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. };
  205.