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