1 /**
  2  * RichDom for Internet Explorer 6 and 7
  3  */
  4 xq.RichDomTrident = xq.Class(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].indexOf(lastCharCode) == -1) 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 				
107 				if(nextBlock) {
108 					this.deleteNode(block);
109 					this.placeCaretAtStartOf(nextBlock);
110 				} else {
111 					this.placeCaretAtStartOf(block);
112 				}
113 				
114 				return true;
115 			}
116 		} else {
117 			block = this.getCurrentBlockElement();
118 			if(block.nodeType == 3) block = block.parentNode;
119 			
120 			if(this.tree.hasMixedContents(block)) {
121 				var marker = this.pushMarker();
122 				this.wrapAllInlineOrTextNodesAs("P", block, true);
123 				this.popMarker(true);
124 				return true;
125 			} else if((this.tree.isTextOrInlineNode(block.previousSibling) || this.tree.isTextOrInlineNode(block.nextSibling)) && this.tree.hasMixedContents(block.parentNode)) {
126 				// IE?서??Block?Inline/Text??접??경우 getCurrentElement ?이 ?작?한??
127 				// ?라???재 Block 주?까? ?번???아주어???다.
128 				this.wrapAllInlineOrTextNodesAs("P", block.parentNode, true);
129 				return true;
130 			} else {
131 				return false;
132 			}
133 		}
134 	},
135 	
136 	
137 	
138 	//////
139 	// Commands
140 	execCommand: function(commandId, param) {
141 		return this.doc.execCommand(commandId, false, param);
142 	},
143 	
144 	applyBackgroundColor: function(color) {
145 		this.execCommand("BackColor", color);
146 	},
147 	
148 	applyEmphasis: function() {
149 		// Generate <i> tag. It will be replaced with <emphasis> tag during cleanup phase.
150 		this.execCommand("Italic");
151 	},
152 	applyStrongEmphasis: function() {
153 		// Generate <b> tag. It will be replaced with <strong> tag during cleanup phase.
154 		this.execCommand("Bold");
155 	},
156 	applyStrike: function() {
157 		// Generate <strike> tag. It will be replaced with <style class="strike"> tag during cleanup phase.
158 		this.execCommand("strikethrough");
159 	},
160 	applyUnderline: function() {
161 		// Generate <u> tag. It will be replaced with <em class="underline"> tag during cleanup phase.
162 		this.execCommand("underline");
163 	},
164 	applyRemoveFormat: function() {
165 		this.execCommand("RemoveFormat");
166 		this.execCommand("Unlink");
167 	},
168 	execHeading: function(level) {
169 		this.execCommand("FormatBlock", "<H" + level + ">");
170 	},
171 
172 
173 
174 	//////
175 	// Focus/Caret/Selection
176 	
177 	focus: function() {
178 		this.win.focus();
179 		
180 		// ?게 ?으?초기??caret??P 밖에 ?치?면??		// getCurrentElement??면 P?리턴?는 기이???상??발생.
181 		if(!this._focusedBefore) {
182 			this.correctParagraph();
183 			this.placeCaretAtStartOf(this.getCurrentBlockElement());
184 			this._focusedBefore = true;
185 		}
186 	},
187 
188 	sel: function() {
189 		return this.doc.selection;
190 	},
191 	
192 	rng: function() {
193 		try {
194 			var sel = this.sel();
195 			return (sel == null) ? null : sel.createRange();
196 		} catch(ignored) {
197 			// IE often fails
198 			return null;
199 		}
200 	},
201 	
202 	hasSelection: function() {
203 		var selectionType = this.sel().type.toLowerCase();
204 		if("none" == selectionType) return false;
205 		if("text" == selectionType && this.getSelectionAsHtml().length == 0) return false;
206 		return true;
207 	},
208 	deleteSelection: function() {
209 		if(this.getSelectionAsText() != "") this.sel().clear();
210 	},
211 	
212 	placeCaretAtStartOf: function(element) {
213 		// If there's no empty span, caret sometimes moves into a previous node.
214 		var ph = this.insertNodeAt(this.createElement("SPAN"), element, "start");
215 		this.selectElement(ph);
216 		this.collapseSelection(false);
217 		this.deleteNode(ph);
218 	},
219 	
220 	selectElement: function(element, entireElement) {
221 		if(!element) throw "[element] is null";
222 		if(element.nodeType != 1) throw "[element] is not an element";
223 		
224 		var rng = this.rng();
225 		rng.moveToElementText(element);
226 		rng.select();
227 	},
228 
229 	selectBlocksBetween: function(start, end) {
230 		var rng = this.rng();
231 		var rngTemp = this.rng();
232 
233 		rngTemp.moveToElementText(start);
234 		rng.setEndPoint("StartToStart", rngTemp);
235 		
236 		rngTemp.moveToElementText(end);
237 		rng.setEndPoint("EndToEnd", rngTemp);
238 		
239 		rng.select();
240 	},
241 	
242 	collapseSelection: function(toStart) {
243 		var rng = this.rng();
244 		rng.collapse(toStart);
245 		rng.select();
246 	},
247 	
248 	getSelectionAsHtml: function() {
249 		var rng = this.rng()
250 		return rng && rng.htmlText ? rng.htmlText : ""
251 	},
252 	
253 	getSelectionAsText: function() {
254 		var rng = this.rng();
255 		return rng && rng.text ? rng.text : "";
256 	},
257 	
258 	hasImportantAttributes: function(element) {
259 		return !!(element.id || element.className || element.style.cssText);
260 	},
261 
262 	isEmptyBlock: function(element) {
263 		if(!element.hasChildNodes()) return true;
264 		if(element.nodeType == 3 && !element.nodeValue) return true;
265 		if([" ", " ", ""].indexOf(element.innerHTML) != -1) return true;
266 		
267 		return false;
268 	},
269 	
270 	getLastChild: function(element) {
271 		if(!element || !element.hasChildNodes()) return null;
272 		
273 		var nodes = xq.$A(element.childNodes).reverse();
274 		
275 		for(var i = 0; i < nodes.length; i++) {
276 			if(nodes[i].nodeType != 3 || nodes[i].nodeValue.length != 0) return nodes[i];
277 		}
278 		
279 		return null;
280 	},
281 	
282 	getCurrentElement: function() {
283 		if(this.sel().type.toLowerCase() == "control") return this.rng().item(0);
284 		return this.rng().parentElement();
285 	},
286 	
287 	getBlockElementAtSelectionStart: function() {
288 		var rng = this.rng();
289 		var dup = rng.duplicate();
290 		dup.collapse(true);
291 		
292 		var result = this.getParentBlockElementOf(dup.parentElement());
293 		if(result.nodeName == "BODY") result = result.firstChild;
294 		
295 		return result;
296 	},
297 	
298 	getBlockElementAtSelectionEnd: function() {
299 		var rng = this.rng();
300 		var dup = rng.duplicate();
301 		dup.collapse(false);
302 		
303 		var result = this.getParentBlockElementOf(dup.parentElement());
304 		if(result.nodeName == "BODY") result = result.lastChild;
305 
306 		return result;
307 	},
308 	
309 	getBlockElementsAtSelectionEdge: function(naturalOrder, ignoreEmptyEdges) {
310 		return [
311 			this.getBlockElementAtSelectionStart(),
312 			this.getBlockElementAtSelectionEnd()
313 		];
314 	},
315 	
316 	isCaretAtBlockStart: function() {
317 		if(this.isCaretAtEmptyBlock()) return true;
318 		if(this.hasSelection()) return false;
319 		var node = this.getCurrentBlockElement();
320 		var marker = this.pushMarker();
321 		
322 		var isTrue = false;
323 		while (node = this.getFirstChild(node)) {
324 			if (node == marker) {
325 				isTrue = true;
326 				break;
327 			}
328 		}
329 		
330 		this.popMarker();
331 		
332 		return isTrue;
333 	},
334 	isCaretAtBlockEnd: function() {
335 		if(this.isCaretAtEmptyBlock()) return true;
336 		if(this.hasSelection()) return false;
337 		var node = this.getCurrentBlockElement();
338 		var marker = this.pushMarker();
339 		var isTrue = false;
340 		while (node = this.getLastChild(node)) {
341 			var nodeValue = node.nodeValue;
342 			
343 			if (node == marker) {
344 				isTrue = true;
345 				break;
346 			} else if(
347 				node.nodeType == 3 &&
348 				node.previousSibling == marker &&
349 				(nodeValue == " " || (nodeValue.length == 1 && nodeValue.charCodeAt(0) == 160))
350 			) {
351 				isTrue = true;
352 				break;
353 			}
354 		}
355 		
356 		this.popMarker();
357 		return isTrue;
358 	},
359 	saveSelection: function() {
360 		return this.rng();
361 	},
362 	restoreSelection: function(bookmark) {
363 		bookmark.select();
364 	}
365 });
366