/* * file: BlockQuoteAction.js * * @BEGINLICENSE * Copyright 2010 Brook Novak (email : brooknovak@seaweed-editor.com) * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA * @ENDLICENSE */ // @DEPENDS: UndoMan bootstrap.provides("actions.BlockQuoteAction"); _registerAction("Blockquote", { /** * An undoable blockquote action. Encapsulates a range with a block quote. If there is any * block quotes within the range, or if the range is within a block quote, then the * block quote will be removed instead. * * @author Brook Novak * * @param {Node} startNode (Optional) The starting dom node of the range to align. * If not provided then the current selection will be used. * If provieded must also provide endNode * * @param {Node} endNode (Optional) The ending dom node of the range to align. Can be the same as start node * If not provided then the current selection will be used. * */ exec : function(startNode, endNode) { // Auto-set range if not provided. if (!startNode) { if (!this.selBefore) return; // Nothing to select if (this.selBefore.endNode) { startNode = this.selBeforeOrdered.startNode; endNode = this.selBeforeOrdered.endNode; } else startNode = endNode = this.selBefore.startNode; } debug.assert(endNode, "Supplied start node but not the end node"); // Is there a block quote in the given range? var bq = null, ca = _getCommonAncestor(startNode, endNode, true); if (isBlockQuote(ca)) bq = ca; else { _visitAllNodes(ca, startNode, true, function(domNode){ if (isBlockQuote(domNode)) bq = domNode; return bq == null && domNode != endNode; }); } // Is the range inside a block quote? if (!bq) { bq = _findAncestor(ca, docBody, isBlockQuote, true) || _findAncestor(startNode, ca, isBlockQuote, true); // Initial traversal will have missed these nodes // Check that looking outside range was ok (not venturing outside editable area) if (!de.doc.isNodeEditable(bq)) bq = null; } if (bq) { // Move block quote children outside of block quote while(bq.firstChild) { var migrant = bq.firstChild; _execOp(_Operation.REMOVE_NODE, migrant); _execOp(_Operation.INSERT_NODE, migrant, bq.parentNode, _indexInParent(bq)); } // Remove the block quote _execOp(_Operation.REMOVE_NODE, bq); } else { // Encapsulate range with a block quote // Normalize containers in range and get list of all the containers var containers = _getNormalizedContainerRange(startNode, endNode); var newbq = $createElement("blockquote"); if (containers.length > 0 && _isValidRelationship(newbq , containers[0].parentNode)) { // Add the new block quote to the document _execOp(_Operation.INSERT_NODE, newbq, containers[0].parentNode, _indexInParent(containers[0])); // Migrate containers into block quote for (var i in containers) { var con = containers[i]; _execOp(_Operation.REMOVE_NODE, con); _execOp(_Operation.INSERT_NODE, con, newbq); } } } this.selAfter = this.selBefore; function isBlockQuote(domNode) { return _nodeName(domNode) == "blockquote"; } } });