      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