| Joomla! | PHP Cross Reference | Web Portals |
1 /* CodeMirror main module (http://codemirror.net/) 2 * 3 * Implements the CodeMirror constructor and prototype, which take care 4 * of initializing the editor frame, and providing the outside interface. 5 */ 6 7 // The CodeMirrorConfig object is used to specify a default 8 // configuration. If you specify such an object before loading this 9 // file, the values you put into it will override the defaults given 10 // below. You can also assign to it after loading. 11 var CodeMirrorConfig = window.CodeMirrorConfig || {}; 12 13 var CodeMirror = (function(){ 14 function setDefaults(object, defaults) { 15 for (var option in defaults) { 16 if (!object.hasOwnProperty(option)) 17 object[option] = defaults[option]; 18 } 19 } 20 function forEach(array, action) { 21 for (var i = 0; i < array.length; i++) 22 action(array[i]); 23 } 24 function createHTMLElement(el) { 25 if (document.createElementNS && document.documentElement.namespaceURI !== null) 26 return document.createElementNS("http://www.w3.org/1999/xhtml", el) 27 else 28 return document.createElement(el) 29 } 30 31 // These default options can be overridden by passing a set of 32 // options to a specific CodeMirror constructor. See manual.html for 33 // their meaning. 34 setDefaults(CodeMirrorConfig, { 35 stylesheet: [], 36 path: "", 37 parserfile: [], 38 basefiles: ["util.js", "stringstream.js", "select.js", "undo.js", "editor.js", "tokenize.js"], 39 iframeClass: null, 40 passDelay: 200, 41 passTime: 50, 42 lineNumberDelay: 200, 43 lineNumberTime: 50, 44 continuousScanning: false, 45 saveFunction: null, 46 onLoad: null, 47 onChange: null, 48 undoDepth: 50, 49 undoDelay: 800, 50 disableSpellcheck: true, 51 textWrapping: true, 52 readOnly: false, 53 width: "", 54 height: "300px", 55 minHeight: 100, 56 onDynamicHeightChange: null, 57 autoMatchParens: false, 58 markParen: null, 59 unmarkParen: null, 60 parserConfig: null, 61 tabMode: "indent", // or "spaces", "default", "shift" 62 enterMode: "indent", // or "keep", "flat" 63 electricChars: true, 64 reindentOnLoad: false, 65 activeTokens: null, 66 onCursorActivity: null, 67 lineNumbers: false, 68 firstLineNumber: 1, 69 onLineNumberClick: null, 70 indentUnit: 2, 71 domain: null, 72 noScriptCaching: false, 73 incrementalLoading: false 74 }); 75 76 function addLineNumberDiv(container, firstNum) { 77 var nums = createHTMLElement("div"), 78 scroller = createHTMLElement("div"); 79 nums.style.position = "absolute"; 80 nums.style.height = "100%"; 81 if (nums.style.setExpression) { 82 try {nums.style.setExpression("height", "this.previousSibling.offsetHeight + 'px'");} 83 catch(e) {} // Seems to throw 'Not Implemented' on some IE8 versions 84 } 85 nums.style.top = "0px"; 86 nums.style.left = "0px"; 87 nums.style.overflow = "hidden"; 88 container.appendChild(nums); 89 scroller.className = "CodeMirror-line-numbers"; 90 nums.appendChild(scroller); 91 scroller.innerHTML = "<div>" + firstNum + "</div>"; 92 return nums; 93 } 94 95 function frameHTML(options) { 96 if (typeof options.parserfile == "string") 97 options.parserfile = [options.parserfile]; 98 if (typeof options.basefiles == "string") 99 options.basefiles = [options.basefiles]; 100 if (typeof options.stylesheet == "string") 101 options.stylesheet = [options.stylesheet]; 102 103 var sp = " spellcheck=\"" + (options.disableSpellcheck ? "false" : "true") + "\""; 104 var html = ["<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0 Transitional//EN\" \"http://www.w3.org/TR/html4/loose.dtd\"><html" + sp + "><head>"]; 105 // Hack to work around a bunch of IE8-specific problems. 106 html.push("<meta http-equiv=\"X-UA-Compatible\" content=\"IE=EmulateIE7\"/>"); 107 var queryStr = options.noScriptCaching ? "?nocache=" + new Date().getTime().toString(16) : ""; 108 forEach(options.stylesheet, function(file) { 109 html.push("<link rel=\"stylesheet\" type=\"text/css\" href=\"" + file + queryStr + "\"/>"); 110 }); 111 forEach(options.basefiles.concat(options.parserfile), function(file) { 112 if (!/^https?:/.test(file)) file = options.path + file; 113 html.push("<script type=\"text/javascript\" src=\"" + file + queryStr + "\"><" + "/script>"); 114 }); 115 html.push("</head><body style=\"border-width: 0;\" class=\"editbox\"" + sp + "></body></html>"); 116 return html.join(""); 117 } 118 119 var internetExplorer = document.selection && window.ActiveXObject && /MSIE/.test(navigator.userAgent); 120 121 function CodeMirror(place, options) { 122 // Use passed options, if any, to override defaults. 123 this.options = options = options || {}; 124 setDefaults(options, CodeMirrorConfig); 125 126 // Backward compatibility for deprecated options. 127 if (options.dumbTabs) options.tabMode = "spaces"; 128 else if (options.normalTab) options.tabMode = "default"; 129 if (options.cursorActivity) options.onCursorActivity = options.cursorActivity; 130 131 var frame = this.frame = createHTMLElement("iframe"); 132 if (options.iframeClass) frame.className = options.iframeClass; 133 frame.frameBorder = 0; 134 frame.style.border = "0"; 135 frame.style.width = '100%'; 136 frame.style.height = '100%'; 137 // display: block occasionally suppresses some Firefox bugs, so we 138 // always add it, redundant as it sounds. 139 frame.style.display = "block"; 140 141 var div = this.wrapping = createHTMLElement("div"); 142 div.style.position = "relative"; 143 div.className = "CodeMirror-wrapping"; 144 div.style.width = options.width; 145 div.style.height = (options.height == "dynamic") ? options.minHeight + "px" : options.height; 146 // This is used by Editor.reroutePasteEvent 147 var teHack = this.textareaHack = createHTMLElement("textarea"); 148 div.appendChild(teHack); 149 teHack.style.position = "absolute"; 150 teHack.style.left = "-10000px"; 151 teHack.style.width = "10px"; 152 teHack.tabIndex = 100000; 153 154 // Link back to this object, so that the editor can fetch options 155 // and add a reference to itself. 156 frame.CodeMirror = this; 157 if (options.domain && internetExplorer) { 158 this.html = frameHTML(options); 159 frame.src = "javascript:(function(){document.open();" + 160 (options.domain ? "document.domain=\"" + options.domain + "\";" : "") + 161 "document.write(window.frameElement.CodeMirror.html);document.close();})()"; 162 } 163 else { 164 frame.src = "javascript:;"; 165 } 166 167 if (place.appendChild) place.appendChild(div); 168 else place(div); 169 div.appendChild(frame); 170 if (options.lineNumbers) this.lineNumbers = addLineNumberDiv(div, options.firstLineNumber); 171 172 this.win = frame.contentWindow; 173 if (!options.domain || !internetExplorer) { 174 this.win.document.open(); 175 this.win.document.write(frameHTML(options)); 176 this.win.document.close(); 177 } 178 } 179 180 CodeMirror.prototype = { 181 init: function() { 182 // Deprecated, but still supported. 183 if (this.options.initCallback) this.options.initCallback(this); 184 if (this.options.onLoad) this.options.onLoad(this); 185 if (this.options.lineNumbers) this.activateLineNumbers(); 186 if (this.options.reindentOnLoad) this.reindent(); 187 if (this.options.height == "dynamic") this.setDynamicHeight(); 188 }, 189 190 getCode: function() {return this.editor.getCode();}, 191 setCode: function(code) {this.editor.importCode(code);}, 192 selection: function() {this.focusIfIE(); return this.editor.selectedText();}, 193 reindent: function() {this.editor.reindent();}, 194 reindentSelection: function() {this.focusIfIE(); this.editor.reindentSelection(null);}, 195 196 focusIfIE: function() { 197 // in IE, a lot of selection-related functionality only works when the frame is focused 198 if (this.win.select.ie_selection && document.activeElement != this.frame) 199 this.focus(); 200 }, 201 focus: function() { 202 this.win.focus(); 203 if (this.editor.selectionSnapshot) // IE hack 204 this.win.select.setBookmark(this.win.document.body, this.editor.selectionSnapshot); 205 }, 206 replaceSelection: function(text) { 207 this.focus(); 208 this.editor.replaceSelection(text); 209 return true; 210 }, 211 replaceChars: function(text, start, end) { 212 this.editor.replaceChars(text, start, end); 213 }, 214 getSearchCursor: function(string, fromCursor, caseFold) { 215 return this.editor.getSearchCursor(string, fromCursor, caseFold); 216 }, 217 218 undo: function() {this.editor.history.undo();}, 219 redo: function() {this.editor.history.redo();}, 220 historySize: function() {return this.editor.history.historySize();}, 221 clearHistory: function() {this.editor.history.clear();}, 222 223 grabKeys: function(callback, filter) {this.editor.grabKeys(callback, filter);}, 224 ungrabKeys: function() {this.editor.ungrabKeys();}, 225 226 setParser: function(name, parserConfig) {this.editor.setParser(name, parserConfig);}, 227 setSpellcheck: function(on) {this.win.document.body.spellcheck = on;}, 228 setStylesheet: function(names) { 229 if (typeof names === "string") names = [names]; 230 var activeStylesheets = {}; 231 var matchedNames = {}; 232 var links = this.win.document.getElementsByTagName("link"); 233 // Create hashes of active stylesheets and matched names. 234 // This is O(n^2) but n is expected to be very small. 235 for (var x = 0, link; link = links[x]; x++) { 236 if (link.rel.indexOf("stylesheet") !== -1) { 237 for (var y = 0; y < names.length; y++) { 238 var name = names[y]; 239 if (link.href.substring(link.href.length - name.length) === name) { 240 activeStylesheets[link.href] = true; 241 matchedNames[name] = true; 242 } 243 } 244 } 245 } 246 // Activate the selected stylesheets and disable the rest. 247 for (var x = 0, link; link = links[x]; x++) { 248 if (link.rel.indexOf("stylesheet") !== -1) { 249 link.disabled = !(link.href in activeStylesheets); 250 } 251 } 252 // Create any new stylesheets. 253 for (var y = 0; y < names.length; y++) { 254 var name = names[y]; 255 if (!(name in matchedNames)) { 256 var link = this.win.document.createElement("link"); 257 link.rel = "stylesheet"; 258 link.type = "text/css"; 259 link.href = name; 260 this.win.document.getElementsByTagName('head')[0].appendChild(link); 261 } 262 } 263 }, 264 setTextWrapping: function(on) { 265 if (on == this.options.textWrapping) return; 266 this.win.document.body.style.whiteSpace = on ? "" : "nowrap"; 267 this.options.textWrapping = on; 268 if (this.lineNumbers) { 269 this.setLineNumbers(false); 270 this.setLineNumbers(true); 271 } 272 }, 273 setIndentUnit: function(unit) {this.win.indentUnit = unit;}, 274 setUndoDepth: function(depth) {this.editor.history.maxDepth = depth;}, 275 setTabMode: function(mode) {this.options.tabMode = mode;}, 276 setEnterMode: function(mode) {this.options.enterMode = mode;}, 277 setLineNumbers: function(on) { 278 if (on && !this.lineNumbers) { 279 this.lineNumbers = addLineNumberDiv(this.wrapping,this.options.firstLineNumber); 280 this.activateLineNumbers(); 281 } 282 else if (!on && this.lineNumbers) { 283 this.wrapping.removeChild(this.lineNumbers); 284 this.wrapping.style.paddingLeft = ""; 285 this.lineNumbers = null; 286 } 287 }, 288 289 cursorPosition: function(start) {this.focusIfIE(); return this.editor.cursorPosition(start);}, 290 firstLine: function() {return this.editor.firstLine();}, 291 lastLine: function() {return this.editor.lastLine();}, 292 nextLine: function(line) {return this.editor.nextLine(line);}, 293 prevLine: function(line) {return this.editor.prevLine(line);}, 294 lineContent: function(line) {return this.editor.lineContent(line);}, 295 setLineContent: function(line, content) {this.editor.setLineContent(line, content);}, 296 removeLine: function(line){this.editor.removeLine(line);}, 297 insertIntoLine: function(line, position, content) {this.editor.insertIntoLine(line, position, content);}, 298 selectLines: function(startLine, startOffset, endLine, endOffset) { 299 this.win.focus(); 300 this.editor.selectLines(startLine, startOffset, endLine, endOffset); 301 }, 302 nthLine: function(n) { 303 var line = this.firstLine(); 304 for (; n > 1 && line !== false; n--) 305 line = this.nextLine(line); 306 return line; 307 }, 308 lineNumber: function(line) { 309 var num = 0; 310 while (line !== false) { 311 num++; 312 line = this.prevLine(line); 313 } 314 return num; 315 }, 316 jumpToLine: function(line) { 317 if (typeof line == "number") line = this.nthLine(line); 318 this.selectLines(line, 0); 319 this.win.focus(); 320 }, 321 currentLine: function() { // Deprecated, but still there for backward compatibility 322 return this.lineNumber(this.cursorLine()); 323 }, 324 cursorLine: function() { 325 return this.cursorPosition().line; 326 }, 327 cursorCoords: function(start) {return this.editor.cursorCoords(start);}, 328 329 activateLineNumbers: function() { 330 var frame = this.frame, win = frame.contentWindow, doc = win.document, body = doc.body, 331 nums = this.lineNumbers, scroller = nums.firstChild, self = this; 332 var barWidth = null; 333 334 nums.onclick = function(e) { 335 var handler = self.options.onLineNumberClick; 336 if (handler) { 337 var div = (e || window.event).target || (e || window.event).srcElement; 338 var num = div == nums ? NaN : Number(div.innerHTML); 339 if (!isNaN(num)) handler(num, div); 340 } 341 }; 342 343 function sizeBar() { 344 if (frame.offsetWidth == 0) return; 345 for (var root = frame; root.parentNode; root = root.parentNode){} 346 if (!nums.parentNode || root != document || !win.Editor) { 347 // Clear event handlers (their nodes might already be collected, so try/catch) 348 try{clear();}catch(e){} 349 clearInterval(sizeInterval); 350 return; 351 } 352 353 if (nums.offsetWidth != barWidth) { 354 barWidth = nums.offsetWidth; 355 frame.parentNode.style.paddingLeft = barWidth + "px"; 356 } 357 } 358 function doScroll() { 359 nums.scrollTop = body.scrollTop || doc.documentElement.scrollTop || 0; 360 } 361 // Cleanup function, registered by nonWrapping and wrapping. 362 var clear = function(){}; 363 sizeBar(); 364 var sizeInterval = setInterval(sizeBar, 500); 365 366 function ensureEnoughLineNumbers(fill) { 367 var lineHeight = scroller.firstChild.offsetHeight; 368 if (lineHeight == 0) return; 369 var targetHeight = 50 + Math.max(body.offsetHeight, Math.max(frame.offsetHeight, body.scrollHeight || 0)), 370 lastNumber = Math.ceil(targetHeight / lineHeight); 371 for (var i = scroller.childNodes.length; i <= lastNumber; i++) { 372 var div = createHTMLElement("div"); 373 div.appendChild(document.createTextNode(fill ? String(i + self.options.firstLineNumber) : "\u00a0")); 374 scroller.appendChild(div); 375 } 376 } 377 378 function nonWrapping() { 379 function update() { 380 ensureEnoughLineNumbers(true); 381 doScroll(); 382 } 383 self.updateNumbers = update; 384 var onScroll = win.addEventHandler(win, "scroll", doScroll, true), 385 onResize = win.addEventHandler(win, "resize", update, true); 386 clear = function(){ 387 onScroll(); onResize(); 388 if (self.updateNumbers == update) self.updateNumbers = null; 389 }; 390 update(); 391 } 392 393 function wrapping() { 394 var node, lineNum, next, pos, changes = [], styleNums = self.options.styleNumbers; 395 396 function setNum(n, node) { 397 // Does not typically happen (but can, if you mess with the 398 // document during the numbering) 399 if (!lineNum) lineNum = scroller.appendChild(createHTMLElement("div")); 400 if (styleNums) styleNums(lineNum, node, n); 401 // Changes are accumulated, so that the document layout 402 // doesn't have to be recomputed during the pass 403 changes.push(lineNum); changes.push(n); 404 pos = lineNum.offsetHeight + lineNum.offsetTop; 405 lineNum = lineNum.nextSibling; 406 } 407 function commitChanges() { 408 for (var i = 0; i < changes.length; i += 2) 409 changes[i].innerHTML = changes[i + 1]; 410 changes = []; 411 } 412 function work() { 413 if (!scroller.parentNode || scroller.parentNode != self.lineNumbers) return; 414 415 var endTime = new Date().getTime() + self.options.lineNumberTime; 416 while (node) { 417 setNum(next++, node.previousSibling); 418 for (; node && !win.isBR(node); node = node.nextSibling) { 419 var bott = node.offsetTop + node.offsetHeight; 420 while (scroller.offsetHeight && bott - 3 > pos) { 421 var oldPos = pos; 422 setNum(" "); 423 if (pos <= oldPos) break; 424 } 425 } 426 if (node) node = node.nextSibling; 427 if (new Date().getTime() > endTime) { 428 commitChanges(); 429 pending = setTimeout(work, self.options.lineNumberDelay); 430 return; 431 } 432 } 433 while (lineNum) setNum(next++); 434 commitChanges(); 435 doScroll(); 436 } 437 function start(firstTime) { 438 doScroll(); 439 ensureEnoughLineNumbers(firstTime); 440 node = body.firstChild; 441 lineNum = scroller.firstChild; 442 pos = 0; 443 next = self.options.firstLineNumber; 444 work(); 445 } 446 447 start(true); 448 var pending = null; 449 function update() { 450 if (pending) clearTimeout(pending); 451 if (self.editor.allClean()) start(); 452 else pending = setTimeout(update, 200); 453 } 454 self.updateNumbers = update; 455 var onScroll = win.addEventHandler(win, "scroll", doScroll, true), 456 onResize = win.addEventHandler(win, "resize", update, true); 457 clear = function(){ 458 if (pending) clearTimeout(pending); 459 if (self.updateNumbers == update) self.updateNumbers = null; 460 onScroll(); 461 onResize(); 462 }; 463 } 464 (this.options.textWrapping || this.options.styleNumbers ? wrapping : nonWrapping)(); 465 }, 466 467 setDynamicHeight: function() { 468 var self = this, activity = self.options.onCursorActivity, win = self.win, body = win.document.body, 469 lineHeight = null, timeout = null, vmargin = 2 * self.frame.offsetTop; 470 body.style.overflowY = "hidden"; 471 win.document.documentElement.style.overflowY = "hidden"; 472 this.frame.scrolling = "no"; 473 474 function updateHeight() { 475 var trailingLines = 0, node = body.lastChild, computedHeight; 476 while (node && win.isBR(node)) { 477 if (!node.hackBR) trailingLines++; 478 node = node.previousSibling; 479 } 480 if (node) { 481 lineHeight = node.offsetHeight; 482 computedHeight = node.offsetTop + (1 + trailingLines) * lineHeight; 483 } 484 else if (lineHeight) { 485 computedHeight = trailingLines * lineHeight; 486 } 487 if (computedHeight) { 488 if (self.options.onDynamicHeightChange) 489 computedHeight = self.options.onDynamicHeightChange(computedHeight); 490 if (computedHeight) 491 self.wrapping.style.height = Math.max(vmargin + computedHeight, self.options.minHeight) + "px"; 492 } 493 } 494 setTimeout(updateHeight, 300); 495 self.options.onCursorActivity = function(x) { 496 if (activity) activity(x); 497 clearTimeout(timeout); 498 timeout = setTimeout(updateHeight, 100); 499 }; 500 } 501 }; 502 503 CodeMirror.InvalidLineHandle = {toString: function(){return "CodeMirror.InvalidLineHandle";}}; 504 505 CodeMirror.replace = function(element) { 506 if (typeof element == "string") 507 element = document.getElementById(element); 508 return function(newElement) { 509 element.parentNode.replaceChild(newElement, element); 510 }; 511 }; 512 513 CodeMirror.fromTextArea = function(area, options) { 514 if (typeof area == "string") 515 area = document.getElementById(area); 516 517 options = options || {}; 518 if (area.style.width && options.width == null) 519 options.width = area.style.width; 520 if (area.style.height && options.height == null) 521 options.height = area.style.height; 522 if (options.content == null) options.content = area.value; 523 524 function updateField() { 525 area.value = mirror.getCode(); 526 } 527 if (area.form) { 528 if (typeof area.form.addEventListener == "function") 529 area.form.addEventListener("submit", updateField, false); 530 else 531 area.form.attachEvent("onsubmit", updateField); 532 if (typeof area.form.submit == "function") { 533 var realSubmit = area.form.submit; 534 function wrapSubmit() { 535 updateField(); 536 // Can't use realSubmit.apply because IE6 is too stupid 537 area.form.submit = realSubmit; 538 area.form.submit(); 539 area.form.submit = wrapSubmit; 540 } 541 area.form.submit = wrapSubmit; 542 } 543 } 544 545 function insert(frame) { 546 if (area.nextSibling) 547 area.parentNode.insertBefore(frame, area.nextSibling); 548 else 549 area.parentNode.appendChild(frame); 550 } 551 552 area.style.display = "none"; 553 var mirror = new CodeMirror(insert, options); 554 mirror.save = updateField; 555 mirror.toTextArea = function() { 556 updateField(); 557 area.parentNode.removeChild(mirror.wrapping); 558 area.style.display = ""; 559 if (area.form) { 560 if (typeof area.form.submit == "function") 561 area.form.submit = realSubmit; 562 if (typeof area.form.removeEventListener == "function") 563 area.form.removeEventListener("submit", updateField, false); 564 else 565 area.form.detachEvent("onsubmit", updateField); 566 } 567 }; 568 569 return mirror; 570 }; 571 572 CodeMirror.isProbablySupported = function() { 573 // This is rather awful, but can be useful. 574 var match; 575 if (window.opera) 576 return Number(window.opera.version()) >= 9.52; 577 else if (/Apple Computer, Inc/.test(navigator.vendor) && (match = navigator.userAgent.match(/Version\/(\d+(?:\.\d+)?)\./))) 578 return Number(match[1]) >= 3; 579 else if (document.selection && window.ActiveXObject && (match = navigator.userAgent.match(/MSIE (\d+(?:\.\d*)?)\b/))) 580 return Number(match[1]) >= 6; 581 else if (match = navigator.userAgent.match(/gecko\/(\d{8})/i)) 582 return Number(match[1]) >= 20050901; 583 else if (match = navigator.userAgent.match(/AppleWebKit\/(\d+)/)) 584 return Number(match[1]) >= 525; 585 else 586 return null; 587 }; 588 589 return CodeMirror; 590 })();
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
title