forked from extern/egroupware
0a28f3812e
- Inclusion of the following javascript directories: * Connector: javascript object to interface xmlhttprequest object. This object allows asynchronous posts and support for messages while this post is being done, such as "wait, contacting server", etc. * JsAPI: general javascript functions and methods * jsolait: performs conversion from a xmlrpc message to a javascript object * xmlRpcMsgCreator: performs conversion from a javascript object to a xmlrpc message * dynapi: cross-browser class to draw layers - Update in setup version: now is 1.0.1.008; Update your versions. There was made a change in phpgw_vfs2_files table in handling of modified files. - Upgrade of vfs2 classes and PclZip class - Changes in javascript object and common object to allow the javascript backend to applications to work (now just filescenter will use it...)
205 lines
8.3 KiB
JavaScript
Executable File
205 lines
8.3 KiB
JavaScript
Executable File
// 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);
|
|
}
|
|
};
|