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