source: gs3-extensions/seaweed-debug/trunk/src/actions/InsertHTMLAction.js@ 25160

Last change on this file since 25160 was 25160, checked in by sjm84, 12 years ago

Initial cut at a version of seaweed for debugging purposes. Check it out live into the web/ext folder

File size: 8.2 KB
Line 
1/*
2 * file: InsertHTMLAction.js
3 *
4 * @BEGINLICENSE
5 * Copyright 2010 Brook Novak (email : [email protected])
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
17 * @ENDLICENSE
18 */
19
20// @DEPENDS: UndoMan
21bootstrap.provides("actions.InsertHTMLAction");
22
23_registerAction("InsertHTML", {
24
25 /**
26 * @class
27 * An undoable insertion action. Inserts HTML into the document
28 *
29 * @author Brook Novak
30 *
31 * @param {String} html The HTML to insert.
32 *
33 * @param {Node} parentNode The parent node of the HTML to insert into.
34 *
35 * @param {Node} refNode The node to insert next to, or inside of (if a text node).
36 * Null if parentNode has no children, in which case the HTML will be inserted
37 * as the only children of parentNode.
38 *
39 * @param {Number} index If refNode is null this is not applicable.
40 * If refNode is a Element node, then index can either be 0 or 1. 0 indicates that the
41 * HTML should be inserted before the refNode, and 1 indicates that it should be inserted after.
42 * Otherwise, if refNode is a Text Node, then index can range from 0 to the length of the text.
43 * Where the index indicates that the html should be inserted before the charactor at the given index.
44 * If index is zero, then the html will be inserted before the text node. If index is the length of the
45 * text, then the html will be inserted after the text node. Otherwise the HTML text will be
46 * inserted within the text run... splitting the text node in two.
47 *
48 */
49 exec : function(html, parentNode, refNode, index) {
50
51 debug.assert(refNode || (!refNode && parentNode.childNodes.length == 0));
52 debug.assert(!refNode || index >= 0);
53 debug.assert(!refNode ||
54 (refNode.nodeType == Node.TEXT_NODE && index <= refNode.nodeValue.length) ||
55 (refNode.nodeType != Node.TEXT_NODE && index <= 1)
56 );
57
58 var domRoots = [];
59
60 // Get all root elements in HTML
61 var domTree = parentNode == docBody ? $createElement("div") : parentNode.cloneNode(false);
62 domTree.innerHTML = html;
63
64 var root = domTree.firstChild;
65 while(root) {
66 domRoots.push(root);
67 root = root.nextSibling;
68 }
69
70 // Disconnect roots from temporary container
71 for (var i = 0; i < domRoots.length; i++) {
72
73 domTree.removeChild(domRoots[i]);
74
75 var spanWrapper = 0;
76 if (domRoots[i].nodeType == Node.TEXT_NODE) {
77 spanWrapper = $createElement("span");
78 spanWrapper.appendChild(domRoots[i]);
79 }
80
81 // Add a renderable char at the end of the container... this will avoid
82 // consildation from changing existing contents
83 var endNode = document.createTextNode("X");
84 parentNode.appendChild(endNode);
85
86 // Consolidate the dom root's whitespace sequences
87 parentNode.appendChild(spanWrapper ? spanWrapper : domRoots[i]);
88
89 _recordOperations = false;
90 _consolidateWSSeqs(domRoots[i], false);
91 _recordOperations = true;
92
93 if (spanWrapper) {
94
95 domRoots.splice(i, 1); // remove this dom root, it is to be replaced or entirly removes
96
97 // Consolidation can completly remove nodes, so test each consolidated sub-tree to
98 // see if they should be inserted
99 if (!spanWrapper.firstChild)
100 i --;
101
102 else {
103 // A text node can be split into several text nodes after consolidation
104 var wrappedChild;
105 while(wrappedChild = spanWrapper.firstChild) {
106 spanWrapper.removeChild(wrappedChild);
107 domRoots.splice(i, 0, wrappedChild);
108 i++;
109 }
110 i--;
111 }
112
113 parentNode.removeChild(spanWrapper);
114
115 } else
116 parentNode.removeChild(domRoots[i]);
117
118
119 parentNode.removeChild(endNode);
120
121 }
122
123 // If the target node is a placeholder, then the placeholder should be replaced with the new html
124 var phRoot;
125 if (de.doc.isESPlaceHolder(parentNode, false) || de.doc.isMNPlaceHolder(parentNode, false))
126 phRoot = parentNode;
127 else if (refNode && (de.doc.isESPlaceHolder(refNode, false) || de.doc.isMNPlaceHolder(refNode, false)))
128 phRoot = refNode;
129
130 if (phRoot) { // Get the placeholder root and adjust the parent node / ref node
131 while (phRoot.parentNode &&
132 (de.doc.isESPlaceHolder(phRoot.parentNode, false) || de.doc.isMNPlaceHolder(phRoot.parentNode, false))) {
133 phRoot = phRoot.parentNode;
134 }
135 targetNode = phRoot;
136
137 parentNode = phRoot.parentNode;
138 refNode = phRoot; // See later .. it will be removed
139 }
140
141 var normalizeTargetWS;
142
143 // Does the insertion point need to split a text node in two?
144 if (refNode &&
145 refNode.nodeType == Node.TEXT_NODE &&
146 index > 0 &&
147 index < _nodeLength(refNode)) {
148
149 var rem = _execOp(_Operation.SPLIT_TEXT_NODE, refNode, index);
150
151 // If inserting HTML an a whitespace point, then must convert to all NBSP then normalize later
152 if (_isAllWhiteSpace(rem.nodeValue.charAt(0)) || _isAllWhiteSpace(refNode.nodeValue.charAt(index-1))) {
153 normalizeTargetWS = refNode.parentNode;
154 _convertWSToNBSP(normalizeTargetWS);
155 }
156
157 }
158
159 // Get the sibling node to begin inserting the dom afterwards.
160 var afterNode;
161 if (!refNode) afterNode = null;
162 else if (index == 0) afterNode = refNode.previousSibling;
163 else afterNode = refNode;
164
165 // Insert the DOM nodes
166 for (var i in domRoots) {
167 if (!afterNode)
168 _execOp(_Operation.INSERT_NODE, domRoots[i], parentNode, parentNode.firstChild ? _indexInParent(parentNode.firstChild) : null)
169 else _execOp(_Operation.INSERT_NODE, domRoots[i], parentNode, _indexInParent(afterNode) + 1);
170 afterNode = domRoots[i];
171 }
172
173 // Normalize whitespace sequences if need to
174 if (normalizeTargetWS)
175 _consolidateWSSeqs(normalizeTargetWS);
176
177 // Need to remove any place holders?
178 if (refNode && (de.doc.isMNPlaceHolder(refNode) || de.doc.isESPlaceHolder(refNode)))
179 _execOp(_Operation.REMOVE_NODE, refNode);
180
181 if (this.flags & de.UndoMan.ExecFlag.UPDATE_SELECTION) {
182 var cDesc = de.cursor.getNearestCursorDesc(
183 domRoots[domRoots.length-1],
184 _nodeLength(domRoots[domRoots.length-1], 2) - 1,
185 true,
186 _nodeName(domRoots[domRoots.length-1]) != "br");
187 if (cDesc)
188 this.selAfter = {startNode : cDesc.domNode, startIndex : cDesc.relIndex + (cDesc.domNode.nodeType == Node.TEXT_NODE && cDesc.isRightOf ? 1 : 0)};
189 }
190
191 }
192});
193
194
Note: See TracBrowser for help on using the repository browser.