1 /**
  2  * RichDom for Internet Explorer 6 and 7
  3  */
  4 xq.RichDomTrident = Class.create(xq.RichDom, {
  5 	makePlaceHolder: function() {
  6 		return this.createTextNode(" ");
  7 	},
  8 	
  9 	makePlaceHolderString: function() {
 10 		return ' ';
 11 	},
 12 	
 13 	makeEmptyParagraph: function() {
 14 		return this.createElementFromHtml("<p> </p>");
 15 	},
 16 
 17 	isPlaceHolder: function(node) {
 18 		return false;
 19 	},
 20 
 21 	getOuterHTML: function(element) {
 22 		return element.outerHTML;
 23 	},
 24 	
 25 	insertNode: function(node) {
 26 		if(this.hasSelection()) this.collapseSelection(true);
 27 		
 28 		this.rng().pasteHTML('<span id="xquared_temp"></span>');
 29 		var marker = this.$('xquared_temp');
 30 		if(node.id == 'xquared_temp') return marker;
 31 		
 32 		marker.replaceNode(node);
 33 		return node;
 34 	},
 35 	
 36 	removeTrailingWhitespace: function(block) {
 37 		if(!block) return;
 38 		
 39 		// TODO: reimplement to handle atomic tags and so on. (use DomTree)
 40 		if(this.tree.isBlockContainer(block)) return;
 41 		if(this.isEmptyBlock(block)) return;
 42 		
 43 		var text = block.innerText;
 44 		var lastCharCode = text.charCodeAt(text.length - 1);
 45 		if(text.length <= 1 || ![32,160].include(lastCharCode)) return;
 46 		
 47 		var node = block;
 48 		
 49 		while(node && node.nodeType != 3) node = node.lastChild;
 50 		
 51 		if(!node) return;
 52 		
 53 		// DO NOT REMOVE OR MODIFY FOLLOWING CODE:
 54 		//
 55 		// Modifying following code crash IE7
 56 		var nodeValue = node.nodeValue;
 57 		if(nodeValue.length <= 1) {
 58 			this.deleteNode(node, true);
 59 		} else {
 60 			node.nodeValue = nodeValue.substring(0, nodeValue.length - 1);
 61 		}
 62 	},
 63 	
 64 	correctEmptyElement: function(element) {
 65 		if(!element || element.nodeType != 1 || this.tree.isAtomic(element)) return;
 66 		
 67 		if(element.firstChild) {
 68 			this.correctEmptyElement(element.firstChild);
 69 		} else {
 70 			element.innerHTML = " ";
 71 		}
 72 	},
 73 
 74 	copyAttributes: function(from, to, copyId) {
 75 		to.mergeAttributes(from, !copyId);
 76 	},
 77 
 78 	correctParagraph: function() {
 79 		if(!this.hasFocus()) return false;
 80 		if(this.hasSelection()) return false;
 81 		
 82 		var block = this.getCurrentElement();
 83 		
 84 		if(block.nodeName == "BODY") {
 85 			// check for atomic block element such as HR
 86 			block = this.insertNode(this.makeEmptyParagraph());
 87 			var next = block.nextSibling;
 88 			if(this.tree.isAtomic(next)) {
 89 				block = this.insertNodeAt(block, next, "after");
 90 				this.placeCaretAtStartOf(block);
 91 				
 92 				var nextBlock = this.tree.findForward(
 93 					block,
 94 					function(node) {return this.tree.isBlock(node) && !this.tree.isBlockOnlyContainer(node)}.bind(this)
 95 				);
 96 				if(nextBlock) {
 97 					this.deleteNode(block);
 98 					this.placeCaretAtStartOf(nextBlock);
 99 				}
100 				return true;
101 			} else {
102 				var nextBlock = this.tree.findForward(
103 					block,
104 					function(node) {return this.tree.isBlock(node) && !this.tree.isBlockOnlyContainer(node)}.bind(this)
105 				);
106 				if(nextBlock) {
107 					this.deleteNode(block);
108 					this.placeCaretAtStartOf(nextBlock);
109 				}
110 				return true;
111 			}
112 		} else {
113 			block = this.getCurrentBlockElement();
114 			if(block.nodeType == 3) block = block.parentNode;
115 			
116 			if(this.tree.hasMixedContents(block)) {
117 				var marker = this.pushMarker();
118 				this.wrapAllInlineOrTextNodesAs("P", block, true);
119 				this.popMarker(true);
120 				return true;
121 			} else if((this.tree.isTextOrInlineNode(block.previousSibling) || this.tree.isTextOrInlineNode(block.nextSibling)) && this.tree.hasMixedContents(block.parentNode)) {
122 				// IE?서??Block?Inline/Text??접??경우 getCurrentElement ?이 ?작?한??
123 				// ?라???재 Block 주?까? ?번???아주어???다.
124 				this.wrapAllInlineOrTextNodesAs("P", block.parentNode, true);
125 				return true;
126 			} else {
127 				return false;
128 			}
129 		}
130 	},
131 	
132 	
133 	
134 	//////
135 	// Commands
136 	execCommand: function(commandId, param) {
137 		return this.doc.execCommand(commandId, false, param);
138 	},
139 	
140 	applyBackgroundColor: function(color) {
141 		this.execCommand("BackColor", color);
142 	},
143 	
144 	applyEmphasis: function() {
145 		// Generate <i> tag. It will be replaced with <emphasis> tag during cleanup phase.
146 		this.execCommand("Italic");
147 	},
148 	applyStrongEmphasis: function() {
149 		// Generate <b> tag. It will be replaced with <strong> tag during cleanup phase.
150 		this.execCommand("Bold");
151 	},
152 	applyStrike: function() {
153 		// Generate <strike> tag. It will be replaced with <style class="strike"> tag during cleanup phase.
154 		this.execCommand("strikethrough");
155 	},
156 	applyUnderline: function() {
157 		// Generate <u> tag. It will be replaced with <em class="underline"> tag during cleanup phase.
158 		this.execCommand("underline");
159 	},
160 	applyRemoveFormat: function() {
161 		this.execCommand("RemoveFormat");
162 		this.execCommand("Unlink");
163 	},
164 	execHeading: function(level) {
165 		this.execCommand("FormatBlock", "<H" + level + ">");
166 	},
167 
168 
169 
170 	//////
171 	// Focus/Caret/Selection
172 	
173 	focus: function() {
174 		this.win.focus();
175 		
176 		// ?게 ?으?초기??caret??P 밖에 ?치?면??		// getCurrentElement??면 P?리턴?는 기이???상??발생.
177 		if(!this._focusedBefore) {
178 			this.correctParagraph();
179 			this.placeCaretAtStartOf(this.getCurrentBlockElement());
180 			this._focusedBefore = true;
181 		}
182 	},
183 
184 	sel: function() {
185 		return this.doc.selection;
186 	},
187 	
188 	rng: function() {
189 		try {
190 			var sel = this.sel();
191 			return (sel == null) ? null : sel.createRange();
192 		} catch(ignored) {
193 			// IE often fails
194 			return null;
195 		}
196 	},
197 	
198 	hasSelection: function() {
199 		var selectionType = this.sel().type.toLowerCase();
200 		if("none" == selectionType) return false;
201 		if("text" == selectionType && this.getSelectionAsHtml().length == 0) return false;
202 		return true;
203 	},
204 	deleteSelection: function() {
205 		if(this.getSelectionAsText() != "") this.sel().clear();
206 	},
207 	
208 	placeCaretAtStartOf: function(element) {
209 		// If there's no empty span, caret sometimes moves into a previous node.
210 		var ph = this.insertNodeAt(this.createElement("SPAN"), element, "start");
211 		this.selectElement(ph);
212 		this.collapseSelection(false);
213 		this.deleteNode(ph);
214 	},
215 	
216 	selectElement: function(element, entireElement) {
217 		if(!element) throw "[element] is null";
218 		if(element.nodeType != 1) throw "[element] is not an element";
219 		
220 		var rng = this.rng();
221 		rng.moveToElementText(element);
222 		rng.select();
223 	},
224 
225 	selectBlocksBetween: function(start, end) {
226 		var rng = this.rng();
227 		var rngTemp = this.rng();
228 
229 		rngTemp.moveToElementText(start);
230 		rng.setEndPoint("StartToStart", rngTemp);
231 		
232 		rngTemp.moveToElementText(end);
233 		rng.setEndPoint("EndToEnd", rngTemp);
234 		
235 		rng.select();
236 	},
237 	
238 	collapseSelection: function(toStart) {
239 		var rng = this.rng();
240 		rng.collapse(toStart);
241 		rng.select();
242 	},
243 	
244 	getSelectionAsHtml: function() {
245 		var rng = this.rng()
246 		return rng && rng.htmlText ? rng.htmlText : ""
247 	},
248 	
249 	getSelectionAsText: function() {
250 		var rng = this.rng();
251 		return rng && rng.text ? rng.text : "";
252 	},
253 	
254 	hasImportantAttributes: function(element) {
255 		return !!(element.id || element.className || element.style.cssText);
256 	},
257 
258 	isEmptyBlock: function(element) {
259 		if(!element.hasChildNodes()) return true;
260 		if(element.nodeType == 3 && !element.nodeValue) return true;
261 		if([" ", " ", ""].include(element.innerHTML)) return true;
262 		
263 		return false;
264 	},
265 	
266 	getLastChild: function(element) {
267 		if(!element || !element.hasChildNodes()) return null;
268 		
269 		var nodes = $A(element.childNodes).reverse();
270 		
271 		for(var i = 0; i < nodes.length; i++) {
272 			if(nodes[i].nodeType != 3 || nodes[i].nodeValue.length != 0) return nodes[i];
273 		}
274 		
275 		return null;
276 	},
277 	
278 	getCurrentElement: function() {
279 		if(this.sel().type.toLowerCase() == "control") return this.rng().item(0);
280 		return this.rng().parentElement();
281 	},
282 	
283 	getBlockElementAtSelectionStart: function() {
284 		var rng = this.rng();
285 		var dup = rng.duplicate();
286 		dup.collapse(true);
287 		
288 		var result = this.getParentBlockElementOf(dup.parentElement());
289 		if(result.nodeName == "BODY") result = result.firstChild;
290 		
291 		return result;
292 	},
293 	
294 	getBlockElementAtSelectionEnd: function() {
295 		var rng = this.rng();
296 		var dup = rng.duplicate();
297 		dup.collapse(false);
298 		
299 		var result = this.getParentBlockElementOf(dup.parentElement());
300 		if(result.nodeName == "BODY") result = result.lastChild;
301 
302 		return result;
303 	},
304 	
305 	getBlockElementsAtSelectionEdge: function(naturalOrder, ignoreEmptyEdges) {
306 		return [
307 			this.getBlockElementAtSelectionStart(),
308 			this.getBlockElementAtSelectionEnd()
309 		];
310 	},
311 	
312 	isCaretAtBlockStart: function() {
313 		if(this.isCaretAtEmptyBlock()) return true;
314 		if(this.hasSelection()) return false;
315 		var node = this.getCurrentBlockElement();
316 		var marker = this.pushMarker();
317 		
318 		var isTrue = false;
319 		while (node = this.getFirstChild(node)) {
320 			if (node == marker) {
321 				isTrue = true;
322 				break;
323 			}
324 		}
325 		
326 		this.popMarker();
327 		
328 		return isTrue;
329 	},
330 	isCaretAtBlockEnd: function() {
331 		if(this.isCaretAtEmptyBlock()) return true;
332 		if(this.hasSelection()) return false;
333 		var node = this.getCurrentBlockElement();
334 		var marker = this.pushMarker();
335 		var isTrue = false;
336 		while (node = this.getLastChild(node)) {
337 			var nodeValue = node.nodeValue;
338 			
339 			if (node == marker) {
340 				isTrue = true;
341 				break;
342 			} else if(
343 				node.nodeType == 3 &&
344 				node.previousSibling == marker &&
345 				(nodeValue == " " || (nodeValue.length == 1 && nodeValue.charCodeAt(0) == 160))
346 			) {
347 				isTrue = true;
348 				break;
349 			}
350 		}
351 		
352 		this.popMarker();
353 		return isTrue;
354 	},
355 	saveSelection: function() {
356 		return this.rng();
357 	},
358 	restoreSelection: function(bookmark) {
359 		bookmark.select();
360 	}
361 });
362