codemirror.js 23 KB

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