From 60e78f95a67fb588f3a7011fd94ef3178db58cdb Mon Sep 17 00:00:00 2001 From: Pim Snel Date: Wed, 21 Apr 2004 20:49:29 +0000 Subject: [PATCH] add EnterParagraphs plugin --- .../EnterParagraphs/enter-paragraphs.js | 204 ++++++++++++++++++ 1 file changed, 204 insertions(+) create mode 100755 phpgwapi/js/htmlarea/plugins/EnterParagraphs/enter-paragraphs.js diff --git a/phpgwapi/js/htmlarea/plugins/EnterParagraphs/enter-paragraphs.js b/phpgwapi/js/htmlarea/plugins/EnterParagraphs/enter-paragraphs.js new file mode 100755 index 0000000000..c9a768da12 --- /dev/null +++ b/phpgwapi/js/htmlarea/plugins/EnterParagraphs/enter-paragraphs.js @@ -0,0 +1,204 @@ +// Modification to htmlArea to insert Paragraphs instead of +// linebreaks, under Gecko engines, circa January 2004 +// By Adam Wright, for The University of Western Australia +// +// Distributed under the same terms as HTMLArea itself. +// This notice MUST stay intact for use (see license.txt). + +function EnterParagraphs(editor, params) { + this.editor = editor; + // activate only if we're talking to Gecko + if (HTMLArea.is_gecko) + this.onKeyPress = this.__onKeyPress; +}; + +EnterParagraphs._pluginInfo = { + name : "EnterParagraphs", + version : "1.0", + developer : "Adam Wright", + developer_url : "http://blog.hipikat.org/", + sponsor : "The University of Western Australia", + sponsor_url : "http://www.uwa.edu.au/", + license : "htmlArea" +}; + +// An array of elements who, in html4, by default, have an inline display and can have children +// we use RegExp here since it should be a bit faster, also cleaner to check +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; + +// Finds the first parent element of a given node whose display is probably not inline +EnterParagraphs.prototype.parentBlock = function(node) { + while (node.parentNode && (node.nodeType != 1 || this._html4_inlines_re.test(node.tagName))) + node = node.parentNode; + return node; +}; + +// Internal function for recursively itterating over a all nodes in a fragment +// If a callback function returns a non-null value, that is returned and the crawl is therefore broken +EnterParagraphs.prototype.walkNodeChildren = function(me, callback) { + if (me.firstChild) { + var myChild = me.firstChild; + var retVal; + while (myChild) { + if ((retVal = callback(this, myChild)) != null) + return retVal; + if ((retVal = this.walkNodeChildren(myChild, callback)) != null) + return retVal; + myChild = myChild.nextSibling; + } + } +}; + +// Callback function to be performed on each node in the hierarchy +// Sets flag to true if we find actual text or an element that's not usually displayed inline +EnterParagraphs.prototype._isFilling = function(self, node) { + if (node.nodeType == 1 && !self._html4_inlines_re.test(node.nodeName)) + return true; + else if (node.nodeType == 3 && node.nodeValue != '') + return true; + return null; + //alert(node.nodeName); +}; + +// Inserts a node deeply on the left of a hierarchy of nodes +EnterParagraphs.prototype.insertDeepLeftText = function(target, toInsert) { + var falling = target; + while (falling.firstChild && falling.firstChild.nodeType == 1) + falling = falling.firstChild; + //var refNode = falling.firstChild ? falling.firstChild : null; + //falling.insertBefore(toInsert, refNode); + falling.innerHTML = toInsert; +}; + +// Kind of like a macros, for a frequent query... +EnterParagraphs.prototype.isElem = function(node, type) { + return node.nodeName.toLowerCase() == type.toLowerCase(); +}; + +// The onKeyPress even that does all the work - nicely breaks the line into paragraphs +EnterParagraphs.prototype.__onKeyPress = function(ev) { + + if (ev.keyCode == 13 && !ev.shiftKey && this.editor._iframe.contentWindow.getSelection) { + + var editor = this.editor; + + // Get the selection and solid references to what we're dealing with chopping + var sel = editor._iframe.contentWindow.getSelection(); + + // Set the start and end points such that they're going /forward/ through the document + var rngLeft = editor._doc.createRange(); var rngRight = editor._doc.createRange(); + rngLeft.setStart(sel.anchorNode, sel.anchorOffset); rngRight.setStart(sel.focusNode, sel.focusOffset); + rngLeft.collapse(true); rngRight.collapse(true); + + var direct = rngLeft.compareBoundaryPoints(rngLeft.START_TO_END, rngRight) < 0; + + var startNode = direct ? sel.anchorNode : sel.focusNode; + var startOffset = direct ? sel.anchorOffset : sel.focusOffset; + var endNode = direct ? sel.focusNode : sel.anchorNode; + var endOffset = direct ? sel.focusOffset : sel.anchorOffset; + + // Find the parent blocks of nodes at either end, and their attributes if they're paragraphs + var startBlock = this.parentBlock(startNode); var endBlock = this.parentBlock(endNode); + var attrsLeft = new Array(); var attrsRight = new Array(); + + // If a list, let the browser take over, if we're in a paragraph, gather it's attributes + if (this.isElem(startBlock, 'li') || this.isElem(endBlock, 'li')) + return; + + if (this.isElem(startBlock, 'p')) { + for (var i = 0; i < startBlock.attributes.length; i++) { + attrsLeft[startBlock.attributes[i].nodeName] = startBlock.attributes[i].nodeValue; + } + } + if (this.isElem(endBlock, 'p')) { + for (var i = 0; i < endBlock.attributes.length; i++) { + // If we start and end within one paragraph, don't duplicate the 'id' + if (endBlock != startBlock || endBlock.attributes[i].nodeName.toLowerCase() != 'id') + attrsRight[endBlock.attributes[i].nodeName] = endBlock.attributes[i].nodeValue; + } + } + + // Look for where to start and end our chopping - within surrounding paragraphs + // if they exist, or at the edges of the containing block, otherwise + var startChop = startNode; var endChop = endNode; + + while ((startChop.previousSibling && !this.isElem(startChop.previousSibling, 'p')) + || (startChop.parentNode && startChop.parentNode != startBlock && startChop.parentNode.nodeType != 9)) + startChop = startChop.previousSibling ? startChop.previousSibling : startChop.parentNode; + + while ((endChop.nextSibling && !this.isElem(endChop.nextSibling, 'p')) + || (endChop.parentNode && endChop.parentNode != endBlock && endChop.parentNode.nodeType != 9)) + endChop = endChop.nextSibling ? endChop.nextSibling : endChop.parentNode; + + // Set up new paragraphs + var pLeft = editor._doc.createElement('p'); var pRight = editor._doc.createElement('p'); + + for (var attrName in attrsLeft) { + var thisAttr = editor._doc.createAttribute(attrName); + thisAttr.value = attrsLeft[attrName]; + pLeft.setAttributeNode(thisAttr); + } + for (var attrName in attrsRight) { + var thisAttr = editor._doc.createAttribute(attrName); + thisAttr.value = attrsRight[attrName]; + pRight.setAttributeNode(thisAttr); + } + + // Get the ranges destined to be stuffed into new paragraphs + rngLeft.setStartBefore(startChop); + rngLeft.setEnd(startNode,startOffset); + pLeft.appendChild(rngLeft.cloneContents()); // Copy into pLeft + + rngRight.setEndAfter(endChop); + rngRight.setStart(endNode,endOffset); + pRight.appendChild(rngRight.cloneContents()); // Copy into pRight + + // If either paragraph is empty, fill it with a nonbreakable space + var foundBlock = false; + foundBlock = this.walkNodeChildren(pLeft, this._isFilling); + if (foundBlock != true) + this.insertDeepLeftText(pLeft, ' '); + + foundBlock = false; + foundBlock = this.walkNodeChildren(pRight, this._isFilling); + if (foundBlock != true) + this.insertDeepLeftText(pRight, ' '); + + // Get a range for everything to be replaced and replace it + var rngAround = editor._doc.createRange(); + + if (!startChop.previousSibling && this.isElem(startChop.parentNode, 'p')) + rngAround.setStartBefore(startChop.parentNode); + else + rngAround.setStart(rngLeft.startContainer, rngLeft.startOffset); + + if (!endChop.nextSibling && this.isElem(endChop.parentNode, 'p')) + rngAround.setEndAfter(endChop.parentNode); + else + rngAround.setEnd(rngRight.endContainer, rngRight.endOffset); + + rngAround.deleteContents(); + rngAround.insertNode(pRight); + rngAround.insertNode(pLeft); + + // Set the selection to the start of the (second) new paragraph + if (pRight.firstChild) { + while (pRight.firstChild && this._html4_inlines_re.test(pRight.firstChild.nodeName)) + pRight = pRight.firstChild; + // Slip into any inline tags + if (pRight.firstChild && pRight.firstChild.nodeType == 3) + pRight = pRight.firstChild; // and text, if they've got it + + var rngCaret = editor._doc.createRange(); + rngCaret.setStart(pRight, 0); + rngCaret.collapse(true); + + sel = editor._iframe.contentWindow.getSelection(); + sel.removeAllRanges(); + sel.addRange(rngCaret); + } + + // Stop the bubbling + HTMLArea._stopEvent(ev); + } +};