summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorgoatchurch <devnull@localhost>2009-06-28 21:22:16 +0100
committergoatchurch <devnull@localhost>2009-06-28 21:22:16 +0100
commitdb5e315db022cd253a9f7224637228bf62449ec6 (patch)
tree31c8b19c459bcbb95fad3c7c4706bd504062b625
parent4c87ce59d350b3d8c3458f538ea6c1b6e5cfa455 (diff)
downloadtroggle-db5e315db022cd253a9f7224637228bf62449ec6.tar.gz
troggle-db5e315db022cd253a9f7224637228bf62449ec6.tar.bz2
troggle-db5e315db022cd253a9f7224637228bf62449ec6.zip
[svn] forgot to add directory
-rw-r--r--media/CodeMirror-0.62/bigtest.html1296
-rw-r--r--media/CodeMirror-0.62/htmltest.html53
-rw-r--r--media/CodeMirror-0.62/index.html182
-rw-r--r--media/CodeMirror-0.62/manual.html622
4 files changed, 2153 insertions, 0 deletions
diff --git a/media/CodeMirror-0.62/bigtest.html b/media/CodeMirror-0.62/bigtest.html
new file mode 100644
index 0000000..04ebcda
--- /dev/null
+++ b/media/CodeMirror-0.62/bigtest.html
@@ -0,0 +1,1296 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+ <head>
+ <script src="js/codemirror.js" type="text/javascript"></script>
+ <title>CodeMirror: JavaScript demonstration</title>
+ <link rel="stylesheet" type="text/css" href="css/docs.css"/>
+ </head>
+ <body style="padding: 20px;">
+
+<p>This page demonstrates <a href="index.html">CodeMirror</a>'s
+JavaScript parser. Note that the ugly buttons at the top are not are
+not part of CodeMirror proper -- they demonstrate the way it can be
+embedded in a web-application.</p>
+
+<div class="border">
+<textarea id="code" cols="120" rows="30">
+/* The Editor object manages the content of the editable frame. It
+ * catches events, colours nodes, and indents lines. This file also
+ * holds some functions for transforming arbitrary DOM structures into
+ * plain sequences of &lt;span> and &lt;br> elements
+ */
+
+// Make sure a string does not contain two consecutive 'collapseable'
+// whitespace characters.
+function makeWhiteSpace(n) {
+ var buffer = [], nb = true;
+ for (; n > 0; n--) {
+ buffer.push((nb || n == 1) ? nbsp : " ");
+ nb = !nb;
+ }
+ return buffer.join("");
+}
+
+// Create a set of white-space characters that will not be collapsed
+// by the browser, but will not break text-wrapping either.
+function fixSpaces(string) {
+ if (string.charAt(0) == " ") string = nbsp + string.slice(1);
+ return string.replace(/[\t \u00a0]{2,}/g, function(s) {return makeWhiteSpace(s.length);});
+}
+
+function cleanText(text) {
+ return text.replace(/\u00a0/g, " ").replace(/\u200b/g, "");
+}
+
+// Create a SPAN node with the expected properties for document part
+// spans.
+function makePartSpan(value, doc) {
+ var text = value;
+ if (value.nodeType == 3) text = value.nodeValue;
+ else value = doc.createTextNode(text);
+
+ var span = doc.createElement("SPAN");
+ span.isPart = true;
+ span.appendChild(value);
+ span.currentText = text;
+ return span;
+}
+
+// On webkit, when the last BR of the document does not have text
+// behind it, the cursor can not be put on the line after it. This
+// makes pressing enter at the end of the document occasionally do
+// nothing (or at least seem to do nothing). To work around it, this
+// function makes sure the document ends with a span containing a
+// zero-width space character. The traverseDOM iterator filters such
+// character out again, so that the parsers won't see them. This
+// function is called from a few strategic places to make sure the
+// zwsp is restored after the highlighting process eats it.
+var webkitLastLineHack = webkit ?
+ function(container) {
+ var last = container.lastChild;
+ if (!last || !last.isPart || last.textContent != "\u200b")
+ container.appendChild(makePartSpan("\u200b", container.ownerDocument));
+ } : function() {};
+
+var Editor = (function(){
+ // The HTML elements whose content should be suffixed by a newline
+ // when converting them to flat text.
+ var newlineElements = {"P": true, "DIV": true, "LI": true};
+
+ function asEditorLines(string) {
+ return fixSpaces(string.replace(/\t/g, " ").replace(/\u00a0/g, " ")).replace(/\r\n?/g, "\n").split("\n");
+ }
+
+ // Helper function for traverseDOM. Flattens an arbitrary DOM node
+ // into an array of textnodes and &lt;br> tags.
+ function simplifyDOM(root) {
+ var doc = root.ownerDocument;
+ var result = [];
+ var leaving = true;
+
+ function simplifyNode(node) {
+ if (node.nodeType == 3) {
+ var text = node.nodeValue = fixSpaces(node.nodeValue.replace(/[\r\u200b]/g, "").replace(/\n/g, " "));
+ if (text.length) leaving = false;
+ result.push(node);
+ }
+ else if (node.nodeName == "BR" &amp;&amp; node.childNodes.length == 0) {
+ leaving = true;
+ result.push(node);
+ }
+ else {
+ forEach(node.childNodes, simplifyNode);
+ if (!leaving &amp;&amp; newlineElements.hasOwnProperty(node.nodeName)) {
+ leaving = true;
+ result.push(doc.createElement("BR"));
+ }
+ }
+ }
+
+ simplifyNode(root);
+ return result;
+ }
+
+ // Creates a MochiKit-style iterator that goes over a series of DOM
+ // nodes. The values it yields are strings, the textual content of
+ // the nodes. It makes sure that all nodes up to and including the
+ // one whose text is being yielded have been 'normalized' to be just
+ // &lt;span> and &lt;br> elements.
+ // See the story.html file for some short remarks about the use of
+ // continuation-passing style in this iterator.
+ function traverseDOM(start){
+ function yield(value, c){cc = c; return value;}
+ function push(fun, arg, c){return function(){return fun(arg, c);};}
+ function stop(){cc = stop; throw StopIteration;};
+ var cc = push(scanNode, start, stop);
+ var owner = start.ownerDocument;
+ var nodeQueue = [];
+
+ // Create a function that can be used to insert nodes after the
+ // one given as argument.
+ function pointAt(node){
+ var parent = node.parentNode;
+ var next = node.nextSibling;
+ return function(newnode) {
+ parent.insertBefore(newnode, next);
+ };
+ }
+ var point = null;
+
+ // Insert a normalized node at the current point. If it is a text
+ // node, wrap it in a &lt;span>, and give that span a currentText
+ // property -- this is used to cache the nodeValue, because
+ // directly accessing nodeValue is horribly slow on some browsers.
+ // The dirty property is used by the highlighter to determine
+ // which parts of the document have to be re-highlighted.
+ function insertPart(part){
+ var text = "\n";
+ if (part.nodeType == 3) {
+ select.snapshotChanged();
+ part = makePartSpan(part, owner);
+ text = part.currentText;
+ }
+ part.dirty = true;
+ nodeQueue.push(part);
+ point(part);
+ return text;
+ }
+
+ // Extract the text and newlines from a DOM node, insert them into
+ // the document, and yield the textual content. Used to replace
+ // non-normalized nodes.
+ function writeNode(node, c){
+ var toYield = [];
+ forEach(simplifyDOM(node), function(part) {
+ toYield.push(insertPart(part));
+ });
+ return yield(toYield.join(""), c);
+ }
+
+ // Check whether a node is a normalized &lt;span> element.
+ function partNode(node){
+ if (node.isPart &amp;&amp; node.childNodes.length == 1 &amp;&amp; node.firstChild.nodeType == 3) {
+ node.currentText = node.firstChild.nodeValue;
+ return !/[\n\t\r]/.test(node.currentText);
+ }
+ return false;
+ }
+
+ // Handle a node. Add its successor to the continuation if there
+ // is one, find out whether the node is normalized. If it is,
+ // yield its content, otherwise, normalize it (writeNode will take
+ // care of yielding).
+ function scanNode(node, c){
+ if (node.nextSibling)
+ c = push(scanNode, node.nextSibling, c);
+
+ if (partNode(node)){
+ nodeQueue.push(node);
+ return yield(node.currentText, c);
+ }
+ else if (node.nodeName == "BR") {
+ nodeQueue.push(node);
+ return yield("\n", c);
+ }
+ else {
+ point = pointAt(node);
+ removeElement(node);
+ return writeNode(node, c);
+ }
+ }
+
+ // MochiKit iterators are objects with a next function that
+ // returns the next value or throws StopIteration when there are
+ // no more values.
+ return {next: function(){return cc();}, nodes: nodeQueue};
+ }
+
+ // Determine the text size of a processed node.
+ function nodeSize(node) {
+ if (node.nodeName == "BR")
+ return 1;
+ else
+ return node.currentText.length;
+ }
+
+ // Search backwards through the top-level nodes until the next BR or
+ // the start of the frame.
+ function startOfLine(node) {
+ while (node &amp;&amp; node.nodeName != "BR") node = node.previousSibling;
+ return node;
+ }
+ function endOfLine(node, container) {
+ if (!node) node = container.firstChild;
+ else if (node.nodeName == "BR") node = node.nextSibling;
+
+ while (node &amp;&amp; node.nodeName != "BR") node = node.nextSibling;
+ return node;
+ }
+
+ // Replace all DOM nodes in the current selection with new ones.
+ // Needed to prevent issues in IE where the old DOM nodes can be
+ // pasted back into the document, still holding their old undo
+ // information.
+ function scrubPasted(container, start, start2) {
+ var end = select.selectionTopNode(container, true),
+ doc = container.ownerDocument;
+ if (start != null &amp;&amp; start.parentNode != container) start = start2;
+ if (start === false) start = null;
+ if (start == end || !end || !container.firstChild) return;
+
+ var clear = traverseDOM(start ? start.nextSibling : container.firstChild);
+ while (end.parentNode == container) try{clear.next();}catch(e){break;}
+ forEach(clear.nodes, function(node) {
+ var newNode = node.nodeName == "BR" ? doc.createElement("BR") : makePartSpan(node.currentText, doc);
+ container.replaceChild(newNode, node);
+ });
+ }
+
+ // Client interface for searching the content of the editor. Create
+ // these by calling CodeMirror.getSearchCursor. To use, call
+ // findNext on the resulting object -- this returns a boolean
+ // indicating whether anything was found, and can be called again to
+ // skip to the next find. Use the select and replace methods to
+ // actually do something with the found locations.
+ function SearchCursor(editor, string, fromCursor) {
+ this.editor = editor;
+ this.history = editor.history;
+ this.history.commit();
+
+ // Are we currently at an occurrence of the search string?
+ this.atOccurrence = false;
+ // The object stores a set of nodes coming after its current
+ // position, so that when the current point is taken out of the
+ // DOM tree, we can still try to continue.
+ this.fallbackSize = 15;
+ var cursor;
+ // Start from the cursor when specified and a cursor can be found.
+ if (fromCursor &amp;&amp; (cursor = select.cursorPos(this.editor.container))) {
+ this.line = cursor.node;
+ this.offset = cursor.offset;
+ }
+ else {
+ this.line = null;
+ this.offset = 0;
+ }
+ this.valid = !!string;
+
+ // Create a matcher function based on the kind of string we have.
+ var target = string.split("\n"), self = this;;
+ this.matches = (target.length == 1) ?
+ // For one-line strings, searching can be done simply by calling
+ // indexOf on the current line.
+ function() {
+ var match = cleanText(self.history.textAfter(self.line).slice(self.offset)).indexOf(string);
+ if (match > -1)
+ return {from: {node: self.line, offset: self.offset + match},
+ to: {node: self.line, offset: self.offset + match + string.length}};
+ } :
+ // Multi-line strings require internal iteration over lines, and
+ // some clunky checks to make sure the first match ends at the
+ // end of the line and the last match starts at the start.
+ function() {
+ var firstLine = cleanText(self.history.textAfter(self.line).slice(self.offset));
+ var match = firstLine.lastIndexOf(target[0]);
+ if (match == -1 || match != firstLine.length - target[0].length)
+ return false;
+ var startOffset = self.offset + match;
+
+ var line = self.history.nodeAfter(self.line);
+ for (var i = 1; i &lt; target.length - 1; i++) {
+ if (cleanText(self.history.textAfter(line)) != target[i])
+ return false;
+ line = self.history.nodeAfter(line);
+ }
+
+ if (cleanText(self.history.textAfter(line)).indexOf(target[target.length - 1]) != 0)
+ return false;
+
+ return {from: {node: self.line, offset: startOffset},
+ to: {node: line, offset: target[target.length - 1].length}};
+ };
+ }
+
+ SearchCursor.prototype = {
+ findNext: function() {
+ if (!this.valid) return false;
+ this.atOccurrence = false;
+ var self = this;
+
+ // Go back to the start of the document if the current line is
+ // no longer in the DOM tree.
+ if (this.line &amp;&amp; !this.line.parentNode) {
+ this.line = null;
+ this.offset = 0;
+ }
+
+ // Set the cursor's position one character after the given
+ // position.
+ function saveAfter(pos) {
+ if (self.history.textAfter(pos.node).length &lt; pos.offset) {
+ self.line = pos.node;
+ self.offset = pos.offset + 1;
+ }
+ else {
+ self.line = self.history.nodeAfter(pos.node);
+ self.offset = 0;
+ }
+ }
+
+ while (true) {
+ var match = this.matches();
+ // Found the search string.
+ if (match) {
+ this.atOccurrence = match;
+ saveAfter(match.from);
+ return true;
+ }
+ this.line = this.history.nodeAfter(this.line);
+ this.offset = 0;
+ // End of document.
+ if (!this.line) {
+ this.valid = false;
+ return false;
+ }
+ }
+ },
+
+ select: function() {
+ if (this.atOccurrence) {
+ select.setCursorPos(this.editor.container, this.atOccurrence.from, this.atOccurrence.to);
+ select.scrollToCursor(this.editor.container);
+ }
+ },
+
+ replace: function(string) {
+ if (this.atOccurrence) {
+ var end = this.editor.replaceRange(this.atOccurrence.from, this.atOccurrence.to, string);
+ this.line = end.node;
+ this.offset = end.offset;
+ this.atOccurrence = false;
+ }
+ }
+ };
+
+ // The Editor object is the main inside-the-iframe interface.
+ function Editor(options) {
+ this.options = options;
+ window.indentUnit = options.indentUnit;
+ this.parent = parent;
+ this.doc = document;
+ var container = this.container = this.doc.body;
+ this.win = window;
+ this.history = new History(container, options.undoDepth, options.undoDelay,
+ this, options.onChange);
+ var self = this;
+
+ if (!Editor.Parser)
+ throw "No parser loaded.";
+ if (options.parserConfig &amp;&amp; Editor.Parser.configure)
+ Editor.Parser.configure(options.parserConfig);
+
+ if (!options.readOnly)
+ select.setCursorPos(container, {node: null, offset: 0});
+
+ this.dirty = [];
+ if (options.content)
+ this.importCode(options.content);
+ else // FF acts weird when the editable document is completely empty
+ container.appendChild(this.doc.createElement("BR"));
+
+ if (!options.readOnly) {
+ if (options.continuousScanning !== false) {
+ this.scanner = this.documentScanner(options.linesPerPass);
+ this.delayScanning();
+ }
+
+ function setEditable() {
+ // In IE, designMode frames can not run any scripts, so we use
+ // contentEditable instead.
+ if (document.body.contentEditable != undefined &amp;&amp; internetExplorer)
+ document.body.contentEditable = "true";
+ else
+ document.designMode = "on";
+
+ document.documentElement.style.borderWidth = "0";
+ if (!options.textWrapping)
+ container.style.whiteSpace = "nowrap";
+ }
+
+ // If setting the frame editable fails, try again when the user
+ // focus it (happens when the frame is not visible on
+ // initialisation, in Firefox).
+ try {
+ setEditable();
+ }
+ catch(e) {
+ var focusEvent = addEventHandler(document, "focus", function() {
+ focusEvent();
+ setEditable();
+ }, true);
+ }
+
+ addEventHandler(document, "keydown", method(this, "keyDown"));
+ addEventHandler(document, "keypress", method(this, "keyPress"));
+ addEventHandler(document, "keyup", method(this, "keyUp"));
+
+ function cursorActivity() {self.cursorActivity(false);}
+ addEventHandler(document.body, "mouseup", cursorActivity);
+ addEventHandler(document.body, "paste", function(event) {
+ cursorActivity();
+ if (internetExplorer) {
+ var text = null;
+ try {text = window.clipboardData.getData("Text");}catch(e){}
+ if (text != null) {
+ self.replaceSelection(text);
+ event.stop();
+ }
+ else {
+ var start = select.selectionTopNode(self.container, true),
+ start2 = start &amp;&amp; start.previousSibling;
+ setTimeout(function(){scrubPasted(self.container, start, start2);}, 0);
+ }
+ }
+ });
+ addEventHandler(document.body, "cut", cursorActivity);
+
+ if (this.options.autoMatchParens)
+ addEventHandler(document.body, "click", method(this, "scheduleParenBlink"));
+ }
+ }
+
+ function isSafeKey(code) {
+ return (code >= 16 &amp;&amp; code &lt;= 18) || // shift, control, alt
+ (code >= 33 &amp;&amp; code &lt;= 40); // arrows, home, end
+ }
+
+ Editor.prototype = {
+ // Import a piece of code into the editor.
+ importCode: function(code) {
+ this.history.push(null, null, asEditorLines(code));
+ this.history.reset();
+ },
+
+ // Extract the code from the editor.
+ getCode: function() {
+ if (!this.container.firstChild)
+ return "";
+
+ var accum = [];
+ select.markSelection(this.win);
+ forEach(traverseDOM(this.container.firstChild), method(accum, "push"));
+ webkitLastLineHack(this.container);
+ select.selectMarked();
+ return cleanText(accum.join(""));
+ },
+
+ checkLine: function(node) {
+ if (node === false || !(node == null || node.parentNode == this.container))
+ throw parent.CodeMirror.InvalidLineHandle;
+ },
+
+ cursorPosition: function(start) {
+ if (start == null) start = true;
+ var pos = select.cursorPos(this.container, start);
+ if (pos) return {line: pos.node, character: pos.offset};
+ else return {line: null, character: 0};
+ },
+
+ firstLine: function() {
+ return null;
+ },
+
+ lastLine: function() {
+ if (this.container.lastChild) return startOfLine(this.container.lastChild);
+ else return null;
+ },
+
+ nextLine: function(line) {
+ this.checkLine(line);
+ var end = endOfLine(line, this.container);
+ return end || false;
+ },
+
+ prevLine: function(line) {
+ this.checkLine(line);
+ if (line == null) return false;
+ return startOfLine(line.previousSibling);
+ },
+
+ selectLines: function(startLine, startOffset, endLine, endOffset) {
+ this.checkLine(startLine);
+ var start = {node: startLine, offset: startOffset}, end = null;
+ if (endOffset !== undefined) {
+ this.checkLine(endLine);
+ end = {node: endLine, offset: endOffset};
+ }
+ select.setCursorPos(this.container, start, end);
+ select.scrollToCursor(this.container);
+ },
+
+ lineContent: function(line) {
+ this.checkLine(line);
+ var accum = [];
+ for (line = line ? line.nextSibling : this.container.firstChild;
+ line &amp;&amp; line.nodeName != "BR"; line = line.nextSibling)
+ accum.push(nodeText(line));
+ return cleanText(accum.join(""));
+ },
+
+ setLineContent: function(line, content) {
+ this.history.commit();
+ this.replaceRange({node: line, offset: 0},
+ {node: line, offset: this.history.textAfter(line).length},
+ content);
+ this.addDirtyNode(line);
+ this.scheduleHighlight();
+ },
+
+ insertIntoLine: function(line, position, content) {
+ var before = null;
+ if (position == "end") {
+ before = endOfLine(line, this.container);
+ }
+ else {
+ for (var cur = line ? line.nextSibling : this.container.firstChild; cur; cur = cur.nextSibling) {
+ if (position == 0) {
+ before = cur;
+ break;
+ }
+ var text = (cur.innerText || cur.textContent || cur.nodeValue || "");
+ if (text.length > position) {
+ before = cur.nextSibling;
+ content = text.slice(0, position) + content + text.slice(position);
+ removeElement(cur);
+ break;
+ }
+ position -= text.length;
+ }
+ }
+
+ var lines = asEditorLines(content), doc = this.container.ownerDocument;
+ for (var i = 0; i &lt; lines.length; i++) {
+ if (i > 0) this.container.insertBefore(doc.createElement("BR"), before);
+ this.container.insertBefore(makePartSpan(lines[i], doc), before);
+ }
+ this.addDirtyNode(line);
+ this.scheduleHighlight();
+ },
+
+ // Retrieve the selected text.
+ selectedText: function() {
+ var h = this.history;
+ h.commit();
+
+ var start = select.cursorPos(this.container, true),
+ end = select.cursorPos(this.container, false);
+ if (!start || !end) return "";
+
+ if (start.node == end.node)
+ return h.textAfter(start.node).slice(start.offset, end.offset);
+
+ var text = [h.textAfter(start.node).slice(start.offset)];
+ for (pos = h.nodeAfter(start.node); pos != end.node; pos = h.nodeAfter(pos))
+ text.push(h.textAfter(pos));
+ text.push(h.textAfter(end.node).slice(0, end.offset));
+ return cleanText(text.join("\n"));
+ },
+
+ // Replace the selection with another piece of text.
+ replaceSelection: function(text) {
+ this.history.commit();
+ var start = select.cursorPos(this.container, true),
+ end = select.cursorPos(this.container, false);
+ if (!start || !end) return;
+
+ end = this.replaceRange(start, end, text);
+ select.setCursorPos(this.container, start, end);
+ },
+
+ replaceRange: function(from, to, text) {
+ var lines = asEditorLines(text);
+ lines[0] = this.history.textAfter(from.node).slice(0, from.offset) + lines[0];
+ var lastLine = lines[lines.length - 1];
+ lines[lines.length - 1] = lastLine + this.history.textAfter(to.node).slice(to.offset);
+ var end = this.history.nodeAfter(to.node);
+ this.history.push(from.node, end, lines);
+ return {node: this.history.nodeBefore(end),
+ offset: lastLine.length};
+ },
+
+ getSearchCursor: function(string, fromCursor) {
+ return new SearchCursor(this, string, fromCursor);
+ },
+
+ // Re-indent the whole buffer
+ reindent: function() {
+ if (this.container.firstChild)
+ this.indentRegion(null, this.container.lastChild);
+ },
+
+ grabKeys: function(eventHandler, filter) {
+ this.frozen = eventHandler;
+ this.keyFilter = filter;
+ },
+ ungrabKeys: function() {
+ this.frozen = "leave";
+ this.keyFilter = null;
+ },
+
+ // Intercept enter and tab, and assign their new functions.
+ keyDown: function(event) {
+ if (this.frozen == "leave") this.frozen = null;
+ if (this.frozen &amp;&amp; (!this.keyFilter || this.keyFilter(event.keyCode))) {
+ event.stop();
+ this.frozen(event);
+ return;
+ }
+
+ var code = event.keyCode;
+ // Don't scan when the user is typing.
+ this.delayScanning();
+ // Schedule a paren-highlight event, if configured.
+ if (this.options.autoMatchParens)
+ this.scheduleParenBlink();
+
+ if (code == 13) { // enter
+ if (event.ctrlKey) {
+ this.reparseBuffer();
+ }
+ else {
+ select.insertNewlineAtCursor(this.win);
+ this.indentAtCursor();
+ select.scrollToCursor(this.container);
+ }
+ event.stop();
+ }
+ else if (code == 9 &amp;&amp; this.options.tabMode != "default") { // tab
+ this.handleTab(!event.ctrlKey &amp;&amp; !event.shiftKey);
+ event.stop();
+ }
+ else if (code == 32 &amp;&amp; event.shiftKey &amp;&amp; this.options.tabMode == "default") { // space
+ this.handleTab(true);
+ event.stop();
+ }
+ else if ((code == 219 || code == 221) &amp;&amp; event.ctrlKey) {
+ this.blinkParens(event.shiftKey);
+ event.stop();
+ }
+ else if (event.metaKey &amp;&amp; (code == 37 || code == 39)) { // Meta-left/right
+ var cursor = select.selectionTopNode(this.container);
+ if (cursor === false || !this.container.firstChild) return;
+
+ if (code == 37) select.focusAfterNode(startOfLine(cursor), this.container);
+ else {
+ end = endOfLine(cursor, this.container);
+ select.focusAfterNode(end ? end.previousSibling : this.container.lastChild, this.container);
+ }
+ event.stop();
+ }
+ else if (event.ctrlKey || event.metaKey) {
+ if ((event.shiftKey &amp;&amp; code == 90) || code == 89) { // shift-Z, Y
+ select.scrollToNode(this.history.redo());
+ event.stop();
+ }
+ else if (code == 90 || code == 8) { // Z, backspace
+ select.scrollToNode(this.history.undo());
+ event.stop();
+ }
+ else if (code == 83 &amp;&amp; this.options.saveFunction) { // S
+ this.options.saveFunction();
+ event.stop();
+ }
+ }
+ },
+
+ // Check for characters that should re-indent the current line,
+ // and prevent Opera from handling enter and tab anyway.
+ keyPress: function(event) {
+ var electric = /indent|default/.test(this.options.tabMode) &amp;&amp; Editor.Parser.electricChars;
+ // Hack for Opera, and Firefox on OS X, in which stopping a
+ // keydown event does not prevent the associated keypress event
+ // from happening, so we have to cancel enter and tab again
+ // here.
+ if ((this.frozen &amp;&amp; (!this.keyFilter || this.keyFilter(event.keyCode))) ||
+ event.code == 13 || (event.code == 9 &amp;&amp; this.options.tabMode != "default") ||
+ (event.keyCode == 32 &amp;&amp; event.shiftKey &amp;&amp; this.options.tabMode == "default"))
+ event.stop();
+ else if (electric &amp;&amp; electric.indexOf(event.character) != -1)
+ this.parent.setTimeout(method(this, "indentAtCursor"), 0);
+ },
+
+ // Mark the node at the cursor dirty when a non-safe key is
+ // released.
+ keyUp: function(event) {
+ this.cursorActivity(isSafeKey(event.keyCode));
+ },
+
+ // Indent the line following a given &lt;br>, or null for the first
+ // line. If given a &lt;br> element, this must have been highlighted
+ // so that it has an indentation method. Returns the whitespace
+ // element that has been modified or created (if any).
+ indentLineAfter: function(start, direction) {
+ // whiteSpace is the whitespace span at the start of the line,
+ // or null if there is no such node.
+ var whiteSpace = start ? start.nextSibling : this.container.firstChild;
+ if (whiteSpace &amp;&amp; !hasClass(whiteSpace, "whitespace"))
+ whiteSpace = null;
+
+ // Sometimes the start of the line can influence the correct
+ // indentation, so we retrieve it.
+ var firstText = whiteSpace ? whiteSpace.nextSibling : (start ? start.nextSibling : this.container.firstChild);
+ var nextChars = (start &amp;&amp; firstText &amp;&amp; firstText.currentText) ? firstText.currentText : "";
+
+ // Ask the lexical context for the correct indentation, and
+ // compute how much this differs from the current indentation.
+ var newIndent = 0, curIndent = whiteSpace ? whiteSpace.currentText.length : 0;
+ if (direction != null &amp;&amp; this.options.tabMode == "shift")
+ newIndent = direction ? curIndent + indentUnit : Math.max(0, curIndent - indentUnit)
+ else if (start)
+ newIndent = start.indentation(nextChars, curIndent, direction);
+ else if (Editor.Parser.firstIndentation)
+ newIndent = Editor.Parser.firstIndentation(nextChars, curIndent, direction);
+ var indentDiff = newIndent - curIndent;
+
+ // If there is too much, this is just a matter of shrinking a span.
+ if (indentDiff &lt; 0) {
+ if (newIndent == 0) {
+ if (firstText) select.snapshotMove(whiteSpace.firstChild, firstText.firstChild, 0);
+ removeElement(whiteSpace);
+ whiteSpace = null;
+ }
+ else {
+ select.snapshotMove(whiteSpace.firstChild, whiteSpace.firstChild, indentDiff, true);
+ whiteSpace.currentText = makeWhiteSpace(newIndent);
+ whiteSpace.firstChild.nodeValue = whiteSpace.currentText;
+ }
+ }
+ // Not enough...
+ else if (indentDiff > 0) {
+ // If there is whitespace, we grow it.
+ if (whiteSpace) {
+ whiteSpace.currentText = makeWhiteSpace(newIndent);
+ whiteSpace.firstChild.nodeValue = whiteSpace.currentText;
+ }
+ // Otherwise, we have to add a new whitespace node.
+ else {
+ whiteSpace = makePartSpan(makeWhiteSpace(newIndent), this.doc);
+ whiteSpace.className = "whitespace";
+ if (start) insertAfter(whiteSpace, start);
+ else this.container.insertBefore(whiteSpace, this.container.firstChild);
+ }
+ if (firstText) select.snapshotMove(firstText.firstChild, whiteSpace.firstChild, curIndent, false, true);
+ }
+ if (indentDiff != 0) this.addDirtyNode(start);
+ return whiteSpace;
+ },
+
+ // Re-highlight the selected part of the document.
+ highlightAtCursor: function() {
+ var pos = select.selectionTopNode(this.container, true);
+ var to = select.selectionTopNode(this.container, false);
+ if (pos === false || to === false) return;
+
+ select.markSelection(this.win);
+ if (this.highlight(pos, endOfLine(to, this.container), true, 20) === false)
+ return false;
+ select.selectMarked();
+ return true;
+ },
+
+ // When tab is pressed with text selected, the whole selection is
+ // re-indented, when nothing is selected, the line with the cursor
+ // is re-indented.
+ handleTab: function(direction) {
+ if (this.options.tabMode == "spaces") {
+ select.insertTabAtCursor(this.win);
+ }
+ else if (!select.somethingSelected(this.win)) {
+ this.indentAtCursor(direction);
+ }
+ else {
+ var start = select.selectionTopNode(this.container, true),
+ end = select.selectionTopNode(this.container, false);
+ if (start === false || end === false) return;
+ this.indentRegion(start, end, direction);
+ }
+ },
+
+ // Delay (or initiate) the next paren blink event.
+ scheduleParenBlink: function() {
+ if (this.parenEvent) this.parent.clearTimeout(this.parenEvent);
+ var self = this;
+ this.parenEvent = this.parent.setTimeout(function(){self.blinkParens();}, 300);
+ },
+
+ // Take the token before the cursor. If it contains a character in
+ // '()[]{}', search for the matching paren/brace/bracket, and
+ // highlight them in green for a moment, or red if no proper match
+ // was found.
+ blinkParens: function(jump) {
+ // Clear the event property.
+ if (this.parenEvent) this.parent.clearTimeout(this.parenEvent);
+ this.parenEvent = null;
+
+ // Extract a 'paren' from a piece of text.
+ function paren(node) {
+ if (node.currentText) {
+ var match = node.currentText.match(/^[\s\u00a0]*([\(\)\[\]{}])[\s\u00a0]*$/);
+ return match &amp;&amp; match[1];
+ }
+ }
+ // Determine the direction a paren is facing.
+ function forward(ch) {
+ return /[\(\[\{]/.test(ch);
+ }
+
+ var ch, self = this, cursor = select.selectionTopNode(this.container, true);
+ if (!cursor || !this.highlightAtCursor()) return;
+ cursor = select.selectionTopNode(this.container, true);
+ if (!(cursor &amp;&amp; ((ch = paren(cursor)) || (cursor = cursor.nextSibling) &amp;&amp; (ch = paren(cursor)))))
+ return;
+ // We only look for tokens with the same className.
+ var className = cursor.className, dir = forward(ch), match = matching[ch];
+
+ // Since parts of the document might not have been properly
+ // highlighted, and it is hard to know in advance which part we
+ // have to scan, we just try, and when we find dirty nodes we
+ // abort, parse them, and re-try.
+ function tryFindMatch() {
+ var stack = [], ch, ok = true;;
+ for (var runner = cursor; runner; runner = dir ? runner.nextSibling : runner.previousSibling) {
+ if (runner.className == className &amp;&amp; runner.nodeName == "SPAN" &amp;&amp; (ch = paren(runner))) {
+ if (forward(ch) == dir)
+ stack.push(ch);
+ else if (!stack.length)
+ ok = false;
+ else if (stack.pop() != matching[ch])
+ ok = false;
+ if (!stack.length) break;
+ }
+ else if (runner.dirty || runner.nodeName != "SPAN" &amp;&amp; runner.nodeName != "BR") {
+ return {node: runner, status: "dirty"};
+ }
+ }
+ return {node: runner, status: runner &amp;&amp; ok};
+ }
+ // Temporarily give the relevant nodes a colour.
+ function blink(node, ok) {
+ node.style.fontWeight = "bold";
+ node.style.color = ok ? "#8F8" : "#F88";
+ self.parent.setTimeout(function() {node.style.fontWeight = ""; node.style.color = "";}, 500);
+ }
+
+ while (true) {
+ var found = tryFindMatch();
+ if (found.status == "dirty") {
+ this.highlight(found.node, 1);
+ // Needed because in some corner cases a highlight does not
+ // reach a node.
+ found.node.dirty = false;
+ continue;
+ }
+ else {
+ blink(cursor, found.status);
+ if (found.node) {
+ blink(found.node, found.status);
+ if (jump) select.focusAfterNode(found.node.previousSibling, this.container);
+ }
+ break;
+ }
+ }
+ },
+
+ // Adjust the amount of whitespace at the start of the line that
+ // the cursor is on so that it is indented properly.
+ indentAtCursor: function(direction) {
+ if (!this.container.firstChild) return;
+ // The line has to have up-to-date lexical information, so we
+ // highlight it first.
+ if (!this.highlightAtCursor()) return;
+ var cursor = select.selectionTopNode(this.container, false);
+ // If we couldn't determine the place of the cursor,
+ // there's nothing to indent.
+ if (cursor === false)
+ return;
+ var lineStart = startOfLine(cursor);
+ var whiteSpace = this.indentLineAfter(lineStart, direction);
+ if (cursor == lineStart &amp;&amp; whiteSpace)
+ cursor = whiteSpace;
+ // This means the indentation has probably messed up the cursor.
+ if (cursor == whiteSpace)
+ select.focusAfterNode(cursor, this.container);
+ },
+
+ // Indent all lines whose start falls inside of the current
+ // selection.
+ indentRegion: function(start, end, direction) {
+ var current = (start = startOfLine(start)), before = start &amp;&amp; startOfLine(start.previousSibling);
+ if (end.nodeName != "BR") end = endOfLine(end, this.container);
+
+ do {
+ if (current) this.highlight(before, current, true);
+ this.indentLineAfter(current, direction);
+ before = current;
+ current = endOfLine(current, this.container);
+ } while (current != end);
+ select.setCursorPos(this.container, {node: start, offset: 0}, {node: end, offset: 0});
+ },
+
+ // Find the node that the cursor is in, mark it as dirty, and make
+ // sure a highlight pass is scheduled.
+ cursorActivity: function(safe) {
+ if (internetExplorer) {
+ this.container.createTextRange().execCommand("unlink");
+ this.selectionSnapshot = select.selectionCoords(this.win);
+ }
+
+ var activity = this.options.cursorActivity;
+ if (!safe || activity) {
+ var cursor = select.selectionTopNode(this.container, false);
+ if (cursor === false || !this.container.firstChild) return;
+ cursor = cursor || this.container.firstChild;
+ if (activity) activity(cursor);
+ if (!safe) {
+ this.scheduleHighlight();
+ this.addDirtyNode(cursor);
+ }
+ }
+ },
+
+ reparseBuffer: function() {
+ forEach(this.container.childNodes, function(node) {node.dirty = true;});
+ if (this.container.firstChild)
+ this.addDirtyNode(this.container.firstChild);
+ },
+
+ // Add a node to the set of dirty nodes, if it isn't already in
+ // there.
+ addDirtyNode: function(node) {
+ node = node || this.container.firstChild;
+ if (!node) return;
+
+ for (var i = 0; i &lt; this.dirty.length; i++)
+ if (this.dirty[i] == node) return;
+
+ if (node.nodeType != 3)
+ node.dirty = true;
+ this.dirty.push(node);
+ },
+
+ // Cause a highlight pass to happen in options.passDelay
+ // milliseconds. Clear the existing timeout, if one exists. This
+ // way, the passes do not happen while the user is typing, and
+ // should as unobtrusive as possible.
+ scheduleHighlight: function() {
+ // Timeouts are routed through the parent window, because on
+ // some browsers designMode windows do not fire timeouts.
+ var self = this;
+ this.parent.clearTimeout(this.highlightTimeout);
+ this.highlightTimeout = this.parent.setTimeout(function(){self.highlightDirty();}, this.options.passDelay);
+ },
+
+ // Fetch one dirty node, and remove it from the dirty set.
+ getDirtyNode: function() {
+ while (this.dirty.length > 0) {
+ var found = this.dirty.pop();
+ // IE8 sometimes throws an unexplainable 'invalid argument'
+ // exception for found.parentNode
+ try {
+ // If the node has been coloured in the meantime, or is no
+ // longer in the document, it should not be returned.
+ while (found &amp;&amp; found.parentNode != this.container)
+ found = found.parentNode
+ if (found &amp;&amp; (found.dirty || found.nodeType == 3))
+ return found;
+ } catch (e) {}
+ }
+ return null;
+ },
+
+ // Pick dirty nodes, and highlight them, until
+ // options.linesPerPass lines have been highlighted. The highlight
+ // method will continue to next lines as long as it finds dirty
+ // nodes. It returns an object indicating the amount of lines
+ // left, and information about the place where it stopped. If
+ // there are dirty nodes left after this function has spent all
+ // its lines, it shedules another highlight to finish the job.
+ highlightDirty: function(force) {
+ // Prevent FF from raising an error when it is firing timeouts
+ // on a page that's no longer loaded.
+ if (!window.select) return;
+
+ var lines = force ? Infinity : this.options.linesPerPass;
+ if (!this.options.readOnly) select.markSelection(this.win);
+ var start;
+ while (lines > 0 &amp;&amp; (start = this.getDirtyNode())){
+ var result = this.highlight(start, lines);
+ if (result) {
+ lines = result.left;
+ if (result.node &amp;&amp; result.dirty)
+ this.addDirtyNode(result.node);
+ }
+ }
+ if (!this.options.readOnly) select.selectMarked();
+ if (start)
+ this.scheduleHighlight();
+ return this.dirty.length == 0;
+ },
+
+ // Creates a function that, when called through a timeout, will
+ // continuously re-parse the document.
+ documentScanner: function(linesPer) {
+ var self = this, pos = null;
+ return function() {
+ // If the current node is no longer in the document... oh
+ // well, we start over.
+ if (pos &amp;&amp; pos.parentNode != self.container)
+ pos = null;
+ select.markSelection(self.win);
+ var result = self.highlight(pos, linesPer, true);
+ select.selectMarked();
+ var newPos = result ? (result.node &amp;&amp; result.node.nextSibling) : null;
+ pos = (pos == newPos) ? null : newPos;
+ self.delayScanning();
+ };
+ },
+
+ // Starts the continuous scanning process for this document after
+ // a given interval.
+ delayScanning: function() {
+ if (this.scanner) {
+ this.parent.clearTimeout(this.documentScan);
+ this.documentScan = this.parent.setTimeout(this.scanner, this.options.continuousScanning);
+ }
+ },
+
+ // The function that does the actual highlighting/colouring (with
+ // help from the parser and the DOM normalizer). Its interface is
+ // rather overcomplicated, because it is used in different
+ // situations: ensuring that a certain line is highlighted, or
+ // highlighting up to X lines starting from a certain point. The
+ // 'from' argument gives the node at which it should start. If
+ // this is null, it will start at the beginning of the document.
+ // When a number of lines is given with the 'target' argument, it
+ // will highlight no more than that amount of lines. If this
+ // argument holds a DOM node, it will highlight until it reaches
+ // that node. If at any time it comes across a 'clean' line (no
+ // dirty nodes), it will stop, except when 'cleanLines' is true.
+ highlight: function(from, target, cleanLines, maxBacktrack){
+ var container = this.container, self = this, active = this.options.activeTokens;
+ var lines = (typeof target == "number" ? target : null);
+
+ if (!container.firstChild)
+ return;
+ // Backtrack to the first node before from that has a partial
+ // parse stored.
+ while (from &amp;&amp; (!from.parserFromHere || from.dirty)) {
+ from = from.previousSibling;
+ if (maxBacktrack != null &amp;&amp; from.nodeName == "BR" &amp;&amp; (--maxBacktrack) &lt; 0)
+ return false;
+ }
+ // If we are at the end of the document, do nothing.
+ if (from &amp;&amp; !from.nextSibling)
+ return;
+
+ // Check whether a part (&lt;span> node) and the corresponding token
+ // match.
+ function correctPart(token, part){
+ return !part.reduced &amp;&amp; part.currentText == token.value &amp;&amp; part.className == token.style;
+ }
+ // Shorten the text associated with a part by chopping off
+ // characters from the front. Note that only the currentText
+ // property gets changed. For efficiency reasons, we leave the
+ // nodeValue alone -- we set the reduced flag to indicate that
+ // this part must be replaced.
+ function shortenPart(part, minus){
+ part.currentText = part.currentText.substring(minus);
+ part.reduced = true;
+ }
+ // Create a part corresponding to a given token.
+ function tokenPart(token){
+ var part = makePartSpan(token.value, self.doc);
+ part.className = token.style;
+ return part;
+ }
+
+ function maybeTouch(node) {
+ if (node) {
+ if (node.nextSibling != node.oldNextSibling) {
+ self.history.touch(node);
+ node.oldNextSibling = node.nextSibling;
+ }
+ }
+ else {
+ if (self.container.firstChild != self.container.oldFirstChild) {
+ self.history.touch(node);
+ self.container.oldFirstChild = self.container.firstChild;
+ }
+ }
+ }
+
+ // Get the token stream. If from is null, we start with a new
+ // parser from the start of the frame, otherwise a partial parse
+ // is resumed.
+ var traversal = traverseDOM(from ? from.nextSibling : container.firstChild),
+ stream = stringStream(traversal),
+ parsed = from ? from.parserFromHere(stream) : Editor.Parser.make(stream);
+
+ // parts is an interface to make it possible to 'delay' fetching
+ // the next DOM node until we are completely done with the one
+ // before it. This is necessary because often the next node is
+ // not yet available when we want to proceed past the current
+ // one.
+ var parts = {
+ current: null,
+ // Fetch current node.
+ get: function(){
+ if (!this.current)
+ this.current = traversal.nodes.shift();
+ return this.current;
+ },
+ // Advance to the next part (do not fetch it yet).
+ next: function(){
+ this.current = null;
+ },
+ // Remove the current part from the DOM tree, and move to the
+ // next.
+ remove: function(){
+ container.removeChild(this.get());
+ this.current = null;
+ },
+ // Advance to the next part that is not empty, discarding empty
+ // parts.
+ getNonEmpty: function(){
+ var part = this.get();
+ // Allow empty nodes when they are alone on a line, needed
+ // for the FF cursor bug workaround (see select.js,
+ // insertNewlineAtCursor).
+ while (part &amp;&amp; part.nodeName == "SPAN" &amp;&amp; part.currentText == "") {
+ var old = part;
+ this.remove();
+ part = this.get();
+ // Adjust selection information, if any. See select.js for details.
+ select.snapshotMove(old.firstChild, part &amp;&amp; (part.firstChild || part), 0);
+ }
+ return part;
+ }
+ };
+
+ var lineDirty = false, prevLineDirty = true, lineNodes = 0;
+
+ // This forEach loops over the tokens from the parsed stream, and
+ // at the same time uses the parts object to proceed through the
+ // corresponding DOM nodes.
+ forEach(parsed, function(token){
+ var part = parts.getNonEmpty();
+
+ if (token.value == "\n"){
+ // The idea of the two streams actually staying synchronized
+ // is such a long shot that we explicitly check.
+ if (part.nodeName != "BR")
+ throw "Parser out of sync. Expected BR.";
+
+ if (part.dirty || !part.indentation) lineDirty = true;
+ maybeTouch(from);
+ from = part;
+
+ // Every &lt;br> gets a copy of the parser state and a lexical
+ // context assigned to it. The first is used to be able to
+ // later resume parsing from this point, the second is used
+ // for indentation.
+ part.parserFromHere = parsed.copy();
+ part.indentation = token.indentation;
+ part.dirty = false;
+
+ // If the target argument wasn't an integer, go at least
+ // until that node.
+ if (lines == null &amp;&amp; part == target) throw StopIteration;
+
+ // A clean line with more than one node means we are done.
+ // Throwing a StopIteration is the way to break out of a
+ // MochiKit forEach loop.
+ if ((lines != null &amp;&amp; --lines &lt;= 0) || (!lineDirty &amp;&amp; !prevLineDirty &amp;&amp; lineNodes > 1 &amp;&amp; !cleanLines))
+ throw StopIteration;
+ prevLineDirty = lineDirty; lineDirty = false; lineNodes = 0;
+ parts.next();
+ }
+ else {
+ if (part.nodeName != "SPAN")
+ throw "Parser out of sync. Expected SPAN.";
+ if (part.dirty)
+ lineDirty = true;
+ lineNodes++;
+
+ // If the part matches the token, we can leave it alone.
+ if (correctPart(token, part)){
+ part.dirty = false;
+ parts.next();
+ }
+ // Otherwise, we have to fix it.
+ else {
+ lineDirty = true;
+ // Insert the correct part.
+ var newPart = tokenPart(token);
+ container.insertBefore(newPart, part);
+ if (active) active(newPart, token, self);
+ var tokensize = token.value.length;
+ var offset = 0;
+ // Eat up parts until the text for this token has been
+ // removed, adjusting the stored selection info (see
+ // select.js) in the process.
+ while (tokensize > 0) {
+ part = parts.get();
+ var partsize = part.currentText.length;
+ select.snapshotReplaceNode(part.firstChild, newPart.firstChild, tokensize, offset);
+ if (partsize > tokensize){
+ shortenPart(part, tokensize);
+ tokensize = 0;
+ }
+ else {
+ tokensize -= partsize;
+ offset += partsize;
+ parts.remove();
+ }
+ }
+ }
+ }
+ });
+ maybeTouch(from);
+ webkitLastLineHack(this.container);
+
+ // The function returns some status information that is used by
+ // hightlightDirty to determine whether and where it has to
+ // continue.
+ return {left: lines,
+ node: parts.getNonEmpty(),
+ dirty: lineDirty};
+ }
+ };
+
+ return Editor;
+})();
+
+ addEventHandler(window, "load", function() {
+ var CodeMirror = window.frameElement.CodeMirror;
+ CodeMirror.editor = new Editor(CodeMirror.options);
+ this.parent.setTimeout(method(CodeMirror, "init"), 0);
+ });
+</textarea>
+</div>
+
+<script type="text/javascript">
+ var textarea = document.getElementById('code');
+ var editor = new CodeMirror(CodeMirror.replace(textarea), {
+ height: "750px",
+ width: "100%",
+ content: textarea.value,
+ parserfile: ["tokenizejavascript.js", "parsejavascript.js"],
+ stylesheet: "css/jscolors.css",
+ path: "js/",
+ autoMatchParens: true,
+ initCallback: function(editor){editor.win.document.body.lastChild.scrollIntoView();}
+ });
+</script>
+
+ </body>
+</html>
diff --git a/media/CodeMirror-0.62/htmltest.html b/media/CodeMirror-0.62/htmltest.html
new file mode 100644
index 0000000..a737522
--- /dev/null
+++ b/media/CodeMirror-0.62/htmltest.html
@@ -0,0 +1,53 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+ <head>
+ <script src="js/codemirror.js" type="text/javascript"></script>
+ <title>CodeMirror: HTML/XML demonstration</title>
+ <link rel="stylesheet" type="text/css" href="css/docs.css"/>
+ <style type="text/css">
+ .CodeMirror-line-numbers {
+ width: 2.2em;
+ color: #aaa;
+ background-color: #eee;
+ text-align: right;
+ padding-right: .3em;
+ font-size: 10pt;
+ font-family: monospace;
+ padding-top: .4em;
+ }
+ </style>
+ </head>
+ <body style="padding: 20px;">
+
+<p>This is a simple demonstration of the XML/HTML indentation module
+for <a href="index.html">CodeMirror</a>. The <a
+href="js/parsexml.js">javascript</a> file contains some comments with
+more information.</p>
+
+<div style="border: 1px solid black; padding: 0px;">
+<textarea id="code" cols="120" rows="30">
+&lt;html style="color: green"&gt;
+ &lt;!-- this is a comment --&gt;
+ &lt;head&gt;
+ &lt;title&gt;HTML Example&lt;/title&gt;
+ &lt;/head&gt;
+ &lt;body&gt;
+ The indentation tries to be &lt;em&gt;somewhat &amp;quot;do what
+ I mean&amp;quot;&lt;/em&gt;... but might not match your style.
+ &lt;/body&gt;
+&lt;/html&gt;
+</textarea>
+</div>
+
+<script type="text/javascript">
+ var editor = CodeMirror.fromTextArea('code', {
+ height: "350px",
+ parserfile: "parsexml.js",
+ stylesheet: "css/xmlcolors.css",
+ path: "js/",
+ continuousScanning: 500,
+ lineNumbers: true,
+ textWrapping: false
+ });
+</script>
+ </body>
+</html>
diff --git a/media/CodeMirror-0.62/index.html b/media/CodeMirror-0.62/index.html
new file mode 100644
index 0000000..58356bd
--- /dev/null
+++ b/media/CodeMirror-0.62/index.html
@@ -0,0 +1,182 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+ <head>
+ <title>CodeMirror: In-browser code editing</title>
+
+ <link rel="stylesheet" type="text/css" href="css/docs.css"/>
+ <style type="text/css">
+ div.top {text-align: center;}
+ div.top h1 {margin-bottom: 0;}
+ div.top h2 {margin-top: 0; margin-bottom: 1.5em;}
+ div.donate span {cursor: pointer; text-decoration: underline;}
+ div.donate {font-size: 70%; margin-top: 1em; width: 155px; padding: 10px; border: 1px solid #c44;}
+ </style>
+ </head>
+ <body>
+
+<div class="top">
+ <h1>CodeMirror</h1>
+ <h2 class="underline">In-browser code editing made almost bearable</h2>
+</div>
+<div style="float: right; padding-left: 10px;">
+ <form action="https://www.paypal.com/cgi-bin/webscr" method="post" id="paypal">
+ <input type="hidden" name="cmd" value="_s-xclick"/>
+ <input type="hidden" name="hosted_button_id" value="3701544"/>
+ </form>
+ <img src="css/people.jpg" alt=""/><br/>
+ <div class="donate">
+ Make a donation:
+ <ul style="margin: 0 2em; padding: 0">
+ <li><span onclick="document.getElementById('paypal').submit();">Paypal</span></li>
+ <li><span onclick="document.getElementById('bankinfo').style.display = 'block';">Bank</span></li>
+ </ul>
+ <div id="bankinfo" style="display: none; font-size: 80%;">
+ Bank: <i>Rabobank</i><br/>
+ Country: <i>Netherlands</i><br/>
+ SWIFT: <i>RABONL2U</i><br/>
+ Account: <i>147850770</i><br/>
+ Name: <i>Marijn Haverbeke</i><br/>
+ IBAN: <i>NL26 RABO 0147 8507 70</i>
+ </div>
+ </div>
+</div>
+
+<p>CodeMirror is a JavaScript library that can be used to create a
+relatively pleasant editor interface for code-like content &#x2015;
+computer programs, HTML markup, and similar. If a parser has been
+written for the language you are editing (see below for a list of
+supported languages), the code will be coloured, and the editor will
+help you with indentation.</p>
+
+<p>To get a look at CodeMirror, see the test pages for the various
+parsers...</p>
+
+<ul>
+ <li><a href="jstest.html">JavaScript</a></li>
+ <li><a href="htmltest.html">XML/HTML</a></li>
+ <li><a href="csstest.html">CSS</a></li>
+ <li><a href="sparqltest.html">SPARQL</a></li>
+ <li><a href="mixedtest.html">HTML mixed-mode</a></li>
+ <li><a href="contrib/php/index.html">HTML+PHP mixed-mode</a> (courtesy of <a href="contrib/php/LICENSE">Yahoo!</a>)</li>
+ <li><a href="contrib/python/index.html">Python</a> (courtesy of <a href="contrib/python/LICENSE">Timothy Farrell</a>)</li>
+ <li><a href="contrib/lua/index.html">Lua</a> (courtesy of <a href="http://francio.pl/">Franciszek Wawrzak</a>)</li>
+ <li><a href="http://stuff.hantl.cz/ruby-in-codemirror/">Ruby</a> (by Michal Hantl, <a href="http://github.com/hakunin/ruby-in-codemirror/tree/master">unfinished</a>)</li>
+</ul>
+
+<p>Or take a look at some real-world uses of the system...</p>
+
+<ul>
+ <li><a href="http://github.com/darwin/firerainbow">FireRainbow: syntax colouring for Firebug</a></li>
+ <li><a href="http://dev.freebaseapps.com/">Freebase's Acre IDE</a></li>
+ <li><a href="http://kml-samples.googlecode.com/svn/trunk/interactive/index.html">Google Earth KML sampler</a></li>
+ <li><a href="http://eloquentjavascript.net/chapter1.html">Eloquent JavaScript's console</a></li>
+ <li><a href="http://demo.qooxdoo.org/current/playground/#Hello_World">The qooxdoo playground</a></li>
+ <li><a href="http://billmill.org/static/canvastutorial/index.html">A cool tutorial about the &lt;canvas> element</a></li>
+ <li><a href="http://orc.csres.utexas.edu/tryorc.shtml">An online IDE for the Orc programming language</a></li>
+ <li><a href="http://code.google.com/apis/ajax/playground">Google's API playground</a></li>
+</ul>
+
+<h2>Releases</h2>
+
+<p class="rel"><em>30-05-2009</em>: <a
+href="http://marijn.haverbeke.nl/codemirror/codemirror-0.62.zip">Version
+0.62</a>: Introduces <a href="contrib/python/index.html">Python</a>
+and <a href="contrib/lua/index.html">Lua</a> parsers. Add
+<code>setParser</code> (on-the-fly mode changing) and
+<code>clearHistory</code> methods. Make parsing passes time-based
+instead of lines-based (see the <code>passTime</code> option).</p>
+
+<p class="rel"><em>04-03-2009</em>: <a
+href="http://marijn.haverbeke.nl/codemirror/codemirror-0.61.zip">Version
+0.61</a>: Add line numbers support (see <code>lineNumbers</code>
+option in <a href="manual.html">manual</a>). Support a mode where tab
+'shifts' indentation instead of resetting it (see
+<code>tabMode="shift"</code>). Add <code>indentUnit</code> option to
+configure indentation depths. Make it possible to grab the editor's
+keyboard input, which is useful when popping up dialogs (see
+<code>grabKeys</code>/<code>ungrabKeys</code>). Fix a lot of small
+bugs, among which the various issues related to pasting in Internet
+Explorer.</p>
+
+<p class="rel"><em>29-12-2008</em>: <a
+href="http://marijn.haverbeke.nl/codemirror/codemirror-0.60.zip">Version
+0.60</a>: This release makes lots of internal changes, so test before
+you upgrade. More robust selection-preservation on IE, allowing styles
+with different font sizes. New <code>activeTokens</code> and
+<code>cursorActivity</code> callbacks, and a more powerful, line-based
+interface for inspecting and manipulating the content of the editor
+(see <a href="manual.html">manual</a>). Fixes the
+<code>replaceSelection</code> problem in IE, and a lot of other,
+smaller issues.</p>
+
+<p class="rel"><em>28-09-2008</em>: <a
+href="http://marijn.haverbeke.nl/codemirror/codemirror-0.58.zip">Version
+0.58</a>: Add parsers for SPARQL and HTML-mixed-mode (nests CSS and JS
+parsers). Also: bracket highlighting, a 'dumb tabs' mode, an
+<code>onChange</code> callback, and heaps of bugfixes. See the manual
+for details on the new features.</p>
+
+<p class="rel"><em>04-07-2008</em>: <a
+href="http://marijn.haverbeke.nl/codemirror/codemirror-0.57.zip">Version
+0.57</a>: A CSS parser and a nice tokenizer framework, bugfixes in the
+XML parser, a few browser-issue workarounds, one of which should fix
+the age-old Firefox cursor-showing-on-wrong line bug.</p>
+
+<h2 id="supported">Supported browsers</h2>
+
+<p>At this time, the following browsers are supported:</p>
+
+<ul>
+ <li>Firefox 1.5 or higher</li>
+ <li>Internet Explorer 6 or higher</li>
+ <li>Safari 3 or higher</li>
+ <li>Opera 9.52 or higher</li>
+ <li>Chrome</li>
+</ul>
+
+<p>Making it work on other browsers that have decent support for the
+W3C DOM model should not be too hard, but I am not actively testing
+against those.</p>
+
+<h2>Getting the code</h2>
+
+<p>All of CodeMirror is released under a <a
+href="LICENSE">zlib-style</a> license. To get it, you can download the
+<a href="http://marijn.haverbeke.nl/codemirror/codemirror.zip">latest
+release</a> or the current <a
+href="http://marijn.haverbeke.nl/codemirror/codemirror-latest.zip">development
+snapshot</a> as zip files, or use the <a
+href="http://www.darcs.net/">darcs</a> version control system to get
+the repository:</p>
+
+<pre class="code">darcs get http://marijn.haverbeke.nl/codemirror</pre>
+
+<p>This second method is recommended if you are planning to hack on
+CodeMirror &#x2015; it makes it easy to record your patches and share them
+with me. To see the repository online, visit the <a
+href="http://marijn.haverbeke.nl/darcsweb.cgi?r=CodeMirror">CodeMirror
+darcsweb</a>.</p>
+
+<h2>Support</h2>
+
+<p>There is a <a
+href="http://groups.google.com/group/codemirror">Google group</a> (a
+sort of mailing list/newsgroup thingy) for discussion and news related
+to CodeMirror. You can also e-mail me directly: <a
+href="mailto:marijnh@gmail.com">Marijn Haverbeke</a>.</p>
+
+<h2>Documentation</h2>
+
+<ul>
+ <li>The <a href="manual.html">manual</a> is all most users will need
+ to read (or skim).</li>
+ <li>If you're interested in working on the code, <a
+ href="story.html">this document</a> about CodeMirror's architecture
+ will be useful.</li>
+ <li>The <a
+ href="http://marijn.haverbeke.nl/darcsweb.cgi?r=CodeMirror;a=tree">source
+ code</a> is, for the most part, rather well commented, so if all
+ else fails, you can try reading it.</li>
+</ul>
+
+ </body>
+</html>
diff --git a/media/CodeMirror-0.62/manual.html b/media/CodeMirror-0.62/manual.html
new file mode 100644
index 0000000..cc8131f
--- /dev/null
+++ b/media/CodeMirror-0.62/manual.html
@@ -0,0 +1,622 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+ <head>
+ <title>CodeMirror user manual</title>
+ <link rel="stylesheet" type="text/css" href="css/docs.css"/>
+ </head>
+ <body>
+ <h1 class="underline">CodeMirror user manual</h1>
+
+ <h2>Contents</h2>
+
+ <ul>
+ <li><a href="#useage">Basic Useage</a></li>
+ <li><a href="#configuration">Configuration</a></li>
+ <li><a href="#parsers">Parsers</a></li>
+ <li><a href="#programming">Programming Interface</a></li>
+ <li><a href="#writeparser">Writing a Parser</a></li>
+ </ul>
+
+ <h2 id="useage">Basic Usage</h2>
+
+ <p>Inside the editor, the tab key is used to re-indent the current
+ selection (or the current line when nothing is selected), and
+ pressing enter will, apart from inserting a line break,
+ automatically indent the new line. Pressing control-enter will
+ cause the whole buffer to be re-coloured, which can be helpful
+ when some colouring has become out-of-date without the editor
+ noticing it.</p>
+
+ <p>The editor sports an undo/redo system, accessible with
+ control-z (undo) and control-y (redo). Safari will not allow
+ client scripts to capture control-z presses, but you can use
+ control-backspace instead on that browser.</p>
+
+ <p>The key-combination control-[ triggers a paren-blink: If the
+ cursor is directly after a '(', ')', '[', ']', '{', or '}', the
+ editor looks for the matching character, and highlights these
+ characters for a moment. There is an option to enable this to
+ happen any time the user types something or moves the cursor.</p>
+
+ <p>To use CodeMirror in a document, you should add a script tag to
+ load <a href="js/codemirror.js"><code>codemirror.js</code></a>. This
+ adds two objects to your environment, <code>CodeMirror</code> and
+ <code>CodeMirrorConfig</code>. The first is the interface to the
+ editor, the second can be used to configure it. (Note that this is
+ the only name-space pollution you can expect from CodeMirror --
+ all other cruft is kept inside the IFRAMEs that it creates when
+ you open an editor.)</p>
+
+ <p>To add an editor to a document, you must choose a place, a
+ parser, and a style-sheet for it. For example, to append an
+ XML editor to the body of the document, you do:</p>
+
+ <pre class="code">var editor = new CodeMirror(document.body, {
+ parserfile: "parsexml.js",
+ stylesheet: "xmlcolors.css"
+});</pre>
+
+ <p>The first argument to the <code>CodeMirror</code> constructor
+ can be a DOM node, in which case the editor gets appended to that
+ node, or a function, which will be called with the IFRAME node as
+ argument, and which is expected to place that node somewhere in
+ the document.</p>
+
+ <p>The second (optional) argument is an object that specifies
+ options. A set of default options (see below) is present in the
+ <code>CodeMirrorConfig</code> object, but each instance of the
+ editor can be given a set of specific options to override these
+ defaults. In this case, we specified that the parser should be
+ loaded from the <a
+ href="js/parsexml.js"><code>"parsexml.js"</code></a> file, and
+ that <a href="css/xmlcolors.css"><code>"xmlcolors.css"</code></a>
+ should be used to specify the colours of the code.</p>
+
+ <p>Another example:</p>
+
+ <pre class="code">var editor = new CodeMirror(CodeMirror.replace("inputfield"), {
+ parserfile: ["tokenizejavascript.js", "parsejavascript.js"],
+ path: "lib/codemirror/js/",
+ stylesheet: "lib/codemirror/css/jscolors.css",
+ content: document.getElementById("inputfield").value
+});</pre>
+
+ <p>Here we use the utility function
+ <code>CodeMirror.replace</code> to create a function that will
+ replace a node in the current document (given either directly or
+ by ID) with the editor. We also select the JavaScript parser this
+ time, and give a <code>path</code> option to tell the editor that
+ its files are not located in the same directory as the current
+ HTML page, but in <code>"lib/codemirror/"</code>.</p>
+
+ <p>There is a function
+ <code>CodeMirror.isProbablySupported()</code> that causes some
+ 1998-style browser detection to happen, returning
+ <code>false</code> if CodeMirror is probably not supported on the
+ browser, <code>true</code> if it probably is, and
+ <code>null</code> if it has no idea. As the name suggests, this is
+ not something you can rely on, but it's usually better than
+ nothing.</p>
+
+ <p>Another utility function, <code>CodeMirror.fromTextArea</code>,
+ will, given a textarea node or the id of such a node, hide the
+ textarea and replace it with a CodeMirror frame. If the textarea
+ was part of a form, an <code>onsubmit</code> handler will be
+ registered with this form, which will load the content of the
+ editor into the textarea, so that it can be submitted as normal.
+ This function optionally takes a configuration object as second
+ argument.</p>
+
+ <pre class="code">var editor = CodeMirror.fromTextArea("inputfield", {
+ parserfile: ["tokenizejavascript.js", "parsejavascript.js"],
+ path: "lib/codemirror/js/",
+ stylesheet: "lib/codemirror/css/jscolors.css"
+});</pre>
+
+ <p>The reason that the script path has to be configured is that
+ CodeMirror will load in a bunch of extra files when an editor is
+ created (the parser script, among others). To be able to do this,
+ it has to know where to find them. These are all the JavaScript
+ files that are part of CodeMirror itself:</p>
+
+ <dl>
+ <dt><a href="js/codemirror.js"><code>codemirror.js</code></a></dt>
+ <dd>Main interface, takes care of default configuration and the
+ definition of editor frames. Include this in your HTML
+ document.</dd>
+ <dt><a href="js/editor.js"><code>editor.js</code></a></dt> <dd>The
+ code that takes care of reacting to user input, colouring text,
+ and indenting lines.</dd>
+ <dt><a href="js/util.js"><code>util.js</code></a></dt> <dd>A few
+ generic utility functions.</dd>
+ <dt><a
+ href="js/undo.js"><code>undo.js</code></a></dt>
+ <dd>Implements the undo history for the editor.</dd>
+ <dt><a
+ href="js/stringstream.js"><code>stringstream.js</code></a></dt>
+ <dd>Objects for wrapping the textual input to the parser.</dd>
+ <dt><a href="js/select.js"><code>select.js</code></a></dt> <dd>Some
+ helper utilities for working with selected text and cursor
+ positions.</dd>
+ <dt><a href="js/tokenize.js"><code>tokenize.js</code></a></dt>
+ <dd>Helper framework for writing tokenisers.</dd>
+ </dl>
+
+ <p>Most of these are rather full of comments, which can be useful
+ when you are trying to figure out how they work, but wastes a lot
+ of bandwidth in a production system. Take a look at the
+ description of the <code>basefiles</code> option below if you want
+ to concatenate and minimise the library.</p>
+
+ <p>Apart from these, there are files that implement the various
+ parsers. These all start with either <code>parse</code> or
+ <code>tokenize</code>.</p>
+
+ <h2 id="configuration">Configuration</h2>
+
+ <p>There are three ways to configure CodeMirror:</p>
+
+ <ul>
+ <li>If you define a global <code>CodeMirrorConfig</code> object
+ before loading <a
+ href="js/codemirror.js"><code>codemirror.js</code></a>, the
+ configuration options in that object will override the
+ defaults.</li>
+ <li>By assigning to the properties of the
+ <code>CodeMirrorConfig</code> object, configuration defaults can
+ be overridden after loading <a
+ href="js/codemirror.js"><code>codemirror.js</code></a>.</li>
+ <li>The <code>CodeMirror</code> constructor can be given a second
+ argument, an object, which will override some options for just
+ that editor. Options not mentioned in this object will default to
+ the values in the <code>CodeMirrorConfig</code> object.</li>
+ </ul>
+
+ <p>The options that can be specified are these (most have sensible
+ defaults specified in <a
+ href="js/codemirror.js"><code>codemirror.js</code></a>):</p>
+
+ <dl>
+
+ <dt><code>stylesheet</code></dt><dd>The file name of the style-sheet
+ that should be used to colour the code in the editor frame. See <a
+ href="css/jscolors.css"><code>jscolors.css</code></a> for an
+ example.</dd>
+
+ <dt><code>path</code></dt><dd>The path that is prefixed to
+ script file names when they are loaded into an IFRAME. (Note that
+ this is not applied to the style-sheet file name.)</dd>
+
+ <dt><code>parserfile</code></dt><dd>A file name string, or an
+ array of strings that name the files containing the parser. See
+ below for the interface that such a parser should
+ implement.</dd>
+
+ <dt><code>basefiles</code></dt><dd>An array of strings naming
+ the files containing the base CodeMirror functionality. Defaults
+ to <code>["util.js", "stringstream.js", "select.js", "undo.js",
+ "editor.js", "tokenize.js"]</code>, but if you put them all into
+ a single file to reduce latency, or add some functionality, you
+ might have to adjust that.</dd>
+
+ <dt><code>iframeClass</code></dt><dd>Set this to a string to
+ give the IFRAME node created for the editor a custom CSS class.
+ Defaults to <code>null</code>.</dd>
+
+ <dt><code>passDelay</code></dt><dd>Gives the amount of
+ milliseconds between colouring passes. Defaults to 200.</dd>
+
+ <dt><code>passTime</code></dt><dd>Specifies the maximum amount
+ of time that the highlighter will spend in one shot. Setting
+ this too high will cause the editor to 'freeze' the browser for
+ noticeable intervals. Defaults to 50.</dd>
+
+ <dt><code>continuousScanning</code></dt><dd>Configure continuous
+ scanning of the document. When <code>false</code>, scanning is
+ disabled. When set to a number, say <code>N</code>, a
+ 'background' process will scan the document for
+ <code>passTime</code> (see above) milliseconds every
+ <code>N</code> milliseconds, regardless of whether anything
+ changed. This makes sure non-local changes propagate through the
+ document, and will help keep everything consistent. It does add
+ extra processing cost, even for an idle editor. Default value is
+ <code>false</code>.</dd>
+
+ <dt><code>autoMatchParens</code></dt><dd>When <code>true</code>,
+ will cause parens to be matched every time a key is pressed or
+ the user clicks on the document. Defaults to <code>false</code>.
+ Might be expensive for big documents.</dd>
+
+ <dt><code>saveFunction</code></dt><dd>If given a function
+ value, that function will be invoked when the user presses
+ control-s. You should advise your Opera users to use
+ control-shift-s instead, since plain control-s will bring up the
+ 'save page' dialog. Defaults to <code>null</code>.</dd>
+
+ <dt><code>undoDepth</code></dt><dd>Maximum length of the undo
+ history. Default is 50.</dd>
+
+ <dt><code>onChange</code></dt><dd>An optional function of zero
+ arguments that gets called whenever the document is changed.
+ Happens at undo-commit time, not instantaniously.</dd>
+
+ <dt><code>undoDelay</code></dt><dd>When nothing is done in the
+ editor for this amount of milliseconds, pending changes get
+ added to the undo history. Setting this lower will give the undo
+ functionality a finer granularity. Defaults to 800.</dd>
+
+ <dt><code>width</code>, <code>height</code></dt><dd>The size of
+ the editor frame, given as a style-sheet quantities (for example
+ <code>"600px"</code> or <code>"100%"</code>).</dd>
+
+ <dt><code>disableSpellcheck</code></dt><dd>Should the editor
+ disable spell-checking on browsers that support it (Firefox 2+).
+ Default is <code>true</code>, since for most code spell-checking
+ is useless.</dd>
+
+ <dt><code>textWrapping</code></dt><dd>Can be used to disable or
+ enable text-wrapping in the editor frame. Default is
+ <code>true</code>.</dd>
+
+ <dt><code>lineNumbers</code></dt><dd>Show line numbers to the
+ left of the editor. This requires you to specify a style for the
+ <code>CodeMirror-line-numbers</code> CSS class (in the outer
+ document) to configure the width, font, colors, etcetera for the
+ line-number DIV. You have to make sure that lines in the
+ numbering element have the same height as lines in the editor.
+ This is most easily done by giving them both the same font and
+ an absolute ('pt' or 'px') font size. This option defaults to
+ <code>false</code>. When enabling this, you have to disable
+ <code>textWrapping</code>, since the line numbers don't take
+ wrapped lines into account.</dd>
+
+ <dt><code>indentUnit</code></dt><dd>An integer that specifies
+ the amount of spaces one 'level' of indentation should add.
+ Default is <code>2</code>.</dd>
+
+ <dt><code>tabMode</code></dt><dd>Determines what the effect of
+ pressing tab is. Possibilities are:
+ <dl>
+ <dt><code>"indent"</code></dt><dd>The default. Causes tab to
+ adjust the indentation of the selection or current line using
+ the parser's rules.</dd>
+ <dt><code>"spaces"</code></dt><dd>Pressing tab simply inserts
+ four spaces.</dd>
+ <dt><code>"default"</code></dt><dd>CodeMirror does not
+ interfere with the tab key, but leaves it to the browser to
+ handle it. Binds shift-space to regular indentation
+ behaviour.</dd>
+ <dt><code>"shift"</code></dt><dd>Pressing tab indents the
+ current line (or selection) one <code>indentUnit</code>
+ deeper, pressing shift-tab or ctrl-tab (whichever your browser
+ does not interfere with), un-indents it.</dd>
+ </dl></dd>
+
+ <dt><code>reindentOnLoad</code></dt><dd>When <code>true</code>,
+ this causes the content of the editor to be reindented
+ immediately when the editor loads. Defaults to
+ <code>false</code>.</dd>
+
+ <dt><code>readOnly</code></dt><dd>When set to <code>true</code>,
+ the document is not editable.</dd>
+
+ <dt><code>initCallback</code></dt><dd>If set to a function, this
+ will be called (with the editor object as its argument) after
+ the editor has finished initialising.</dd>
+
+ <dt><code>cursorActivity</code></dt><dd>A function that is
+ called every time the cursor moves, with the top-level node that
+ the cursor is inside or next to as an argument. Can be used to
+ have some controls react to the context of the cursor.</dd>
+
+ <dt><code>activeTokens</code></dt><dd>Can be set to a function
+ that will be called with <code>(spanNode, tokenObject,
+ editor)</code> arguments whenever a new token node is being
+ added to the document. Can be used to do things like add event
+ handlers to nodes. Should <em>not</em> change the DOM structure
+ of the node (so no turning the span into a link), since this
+ will greatly confuse the editor.</dd>
+
+ <dt id="parserConfig"><code>parserConfig</code></dt><dd>An
+ object value that is passed along to the parser to configure it.
+ What this object should look like depends on the parser
+ used.</dd>
+
+ <dt><code>content</code></dt><dd>The starting content of the
+ editor. You'll probably not want to provide a global default for
+ this, but add it to the <code>options</code> object passed to
+ individual editors as they are created.</dd>
+
+ </dl>
+
+ <h2 id="parsers">Parsers</h2>
+
+ <p>(If you want to use a CodeMirror parser to highlight a piece of
+ text, without creating an editor, see <a
+ href="highlight.html">this example</a>, and the <code><a
+ href="js/highlight.js">highlight.js</a></code> script.)</p>
+
+ <p>The following parsers come with the distribution of CodeMirror:</p>
+
+ <dl>
+ <dt><code><a href="js/parsexml.js">parsexml.js</a></code> (<a
+ href="htmltest.html">demo</a>)</dt><dd>A HTML/XML parser. Takes
+ a <code>useHTMLKludges</code> configuration option (see the
+ <code><a href="#parserConfig">parserConfig</a></code> option
+ above), which specifies whether the content of the editor is
+ HTML or XML, and things like self-closing tags (<code>br</code>,
+ <code>img</code>) exist. This defaults to <code>true</code>.
+ Example colours for the styles that this parser uses are defined
+ in <code><a
+ href="css/xmlcolors.css">css/xmlcolors.css</a></code>.</dd>
+
+ <dt><code><a
+ href="js/tokenizejavascript.js">tokenizejavascript.js</a></code>,
+ <code><a
+ href="js/parsejavascript.js">parseejavascript.js</a></code> (<a
+ href="jstest.html">demo</a>)</dt><dd>The JavaScript parser.
+ Example colours in <code><a
+ href="css/jscolors.css">css/jscolors.css</a></code></dd>
+
+ <dt><code><a href="js/parsecss.js">parsecss.js</a></code> (<a
+ href="csstest.html">demo</a>)</dt><dd>A CSS parser. Styles in
+ <code><a
+ href="css/csscolors.css">css/csscolors.css</a></code></dd>
+
+ <dt><code><a
+ href="js/parsehtmlmixed.js">parsehtmlmixed.js</a></code> (<a
+ href="mixedtest.html">demo</a>)</dt><dd>A mixed-mode HTML
+ parser. Requires the XML, JavaScript, and CSS parsers to also be
+ loaded, so your <code>parserfile</code> option looks something
+ like <code>["parsexml.js", "parsecss.js",
+ "tokenizejavascript.js", "parsejavascript.js",
+ "parsehtmlmixed.js"]</code>.</dd>
+
+ <dt><code><a href="js/parsesparql.js">parsesparql.js</a></code>
+ (<a href="sparqltest.html">demo</a>)</dt><dd>Parses the <a
+ href="http://en.wikipedia.org/wiki/SPARQL">SPARQL</a> query
+ language. Example styles in <code><a
+ href="css/sparqlcolors.css">css/sparqlcolors.css</a></code></dd>
+
+ <dt><code><a
+ href="js/parsedummy.js">parsedummy.js</a></code></dt><dd>A
+ 'dummy' parser to make it possible to edit plain text, or
+ documents for which no suitable parser exists.</dd>
+
+ <dt><code><a
+ href="contrib/php/js/parsephp.js">contrib/php/js/parsephp.js</a></code>
+ (<a href="contrib/php/index.html">demo</a>)</dt><dd>PHP
+ parser.</dd>
+
+ <dt><code><a
+ href="contrib/python/js/parsepython.js">contrib/python/js/parsepython.js</a></code>
+ (<a href="contrib/python/index.html">demo</a>)</dt><dd>Python
+ parser.</dd>
+
+ <dt><code><a href="contrib/lua/js/parselua.js">contrib/lua/js/parselua.js</a></code>
+ (<a href="contrib/lua/index.html">demo</a>)</dt><dd>Lua
+ parser.</dd>
+
+ </dl>
+
+ <h2 id="programming">Programming Interface</h2>
+
+ <p>To be as flexible as possible, CodeMirror implements a very
+ plain editable field, without any accompanying buttons, bells, and
+ whistles. <code>CodeMirror</code> objects do, however, provide a
+ number of methods that make it possible to add extra functionality
+ around the editor. <a
+ href="js/mirrorframe.js"><code>mirrorframe.js</code></a> provides a
+ basic example of their usage.</p>
+
+ <dl>
+
+ <dt><code>getCode()</code> &#8594;
+ <code>string</code></dt><dd>Returns the current content of the
+ editor, as a string.</dd>
+
+ <dt><code>setCode(string)</code></dt><dd>Replaces the current
+ content of the editor with the given value.</dd>
+
+ <dt><code>focus()</code></dt><dd>Gives focus to the editor
+ frame.</dd>
+
+ <dt><code>currentLine()</code> &#8594;
+ <code>number</code></dt><dd>Returns the line on which the cursor
+ is currently sitting. <span class="warn">(Deprecated, see the
+ line-based interface below)</span></dd>
+
+ <dt><code>jumpToLine(number)</code></dt><dd>Moves the cursor to
+ the start of the given line. <span
+ class="warn">(Deprecated)</span></dd>
+
+ <dt><code>selection()</code> &#8594;
+ <code>string</code></dt><dd>Returns the text that is currently
+ selected in the editor.</dd>
+
+ <dt><code>replaceSelection(string)</code></dt><dd>Replaces the
+ currently selected text with the given string. Will also cause
+ the editor frame to gain focus.</dd>
+
+ <dt><code>reindent()</code></dt><dd>Automatically re-indent the
+ whole document.</dd>
+
+ <dt><code>reindentSelection()</code></dt><dd>Automatically
+ re-indent the selected lines.</dd>
+
+ <dt><code>getSearchCursor(string, atCursor)</code> &#8594;
+ <code>cursor</code></dt><dd>The first argument indicates the
+ string that should be searched for, and the second indicates
+ whether searching should start at the cursor (<code>true</code>)
+ or at the start of the document (<code>false</code>). Returns an
+ object that provides an interface for searching. Call its
+ <code>findNext()</code> method to search for an occurrence of
+ the given string. This returns <code>true</code> if something is
+ found, or <code>false</code> if the end of document is reached.
+ When an occurrence has been found, you can call
+ <code>select()</code> to select it, or
+ <code>replace(string)</code> to replace it with a given string.
+ Note that letting the user change the document, or
+ programmatically changing it in any way except for calling
+ <code>replace</code> on the cursor itself, might cause a cursor
+ object to skip back to the beginning of the document.</dd>
+
+ <dt><code>undo()</code></dt><dd>Undo one changeset, if available.</dd>
+ <dt><code>redo()</code></dt><dd>Redo one changeset, if available.</dd>
+ <dt><code>historySize() &#8594; object</code></dt><dd>Get a
+ <code>{undo, redo}</code> object holding the sizes of the undo
+ and redo histories.</dd>
+ <dt><code>clearHistory()</code></dt><dd>Drop all history
+ information.</dd>
+
+ <dt><code>grabKeys(callback, filter)</code></dt><dd>Route
+ keyboard input in the editor to a callback function. This
+ function is given a slightly normalised (see
+ <code>normalizeEvent</code> in <a
+ href="js/util.js"><code>util.js</code></a>) <code>keydown</code>
+ event object. If a second argument is given, this will be used
+ to determine which events to apply the callback to. It should
+ take a key code (as in <code>event.keyCode</code>), and return a
+ boolean, where <code>true</code> means the event should be
+ routed to the callback, and <code>false</code> leaves the key to
+ perform its normal behaviour.</dd>
+ <dt><code>ungrabKeys()</code></dt><dd>Revert the effect of
+ <code>grabKeys</code>.</dd>
+
+ <dt><code>setParser(name)</code></dt><dd>Change the active
+ parser. To use this you'll have to load more than one parser
+ (put the one you want to use as default at the end of the list).
+ Then call this function with a string containing the name of the
+ parser you want to switch to (see the parser script file to find
+ the name, it'll be something like <code>CSSParser</code>).</dd>
+ </dl>
+
+ <p>For detailed interaction with the content of the editor,
+ CodeMirror exposes a line-oriented interface, which allows you to
+ inspect and manipulate the document line by line. Line handles
+ should be considered opaque (they are in fact the <code>BR</code>
+ nodes at the start of the line), except that the value
+ <code>false</code> (but <em>not</em> <code>null</code>) always
+ denotes an invalid value. Since changing the document might cause
+ some line handles to become invalid, every function that takes
+ them as argument can throw
+ <code>CodeMirror.InvalidLineHandle</code>. These are the relevant
+ methods:</p>
+
+ <dl>
+ <dt><code>cursorPosition(start)</code> &#8594;
+ <code>object</code></dt><dd>Retrieve a <code>{line,
+ character}</code> object representing the cursor position.
+ <code>start</code> defaults to <code>true</code> and determines
+ if the startpoint or the endpoint of the selection is used.</dd>
+ <dt><code>firstLine()</code> &#8594;
+ <code>handle</code></dt><dd>Get the first line of the
+ document.</dd>
+ <dt><code>lastLine()</code> &#8594;
+ <code>handle</code></dt><dd>The last line.</dd>
+ <dt><code>nextLine(handle)</code> &#8594;
+ <code>handle</code></dt><dd>Get the line after the given one, or
+ <code>false</code> if that was the last line.</dd>
+ <dt><code>prevLine(handle)</code> &#8594;
+ <code>handle</code></dt><dd>Find the line before the given one,
+ return <code>false</code> if that was the first line.</dd>
+ <dt><code>nthLine(number)</code> &#8594;
+ <code>handle</code></dt><dd>Find the Nth line of the document.
+ Note that the first line counts as one, not zero. Returns
+ <code>false</code> if there is no such line.</dd>
+ <dt><code>lineContent(handle)</code> &#8594;
+ <code>string</code></dt><dd>Retrieve the content of the
+ line.</dd>
+ <dt><code>setLineContent(handle, string)</code></dt><dd>Replace
+ the content of the line with the given string.</dd>
+ <dt><code>lineNumber(handle)</code> &#8594;
+ <code>number</code></dt><dd>Ask which line of the document
+ (1-based) the given line is.</dd>
+ <dt><code>selectLines(startHandle, startOffset,
+ endHandle, endOffset)</code></dt><dd>Move the selection to a
+ specific point. <code>endHandle</code> and
+ <code>endOffset</code> can be omitted to just place the cursor
+ somewhere without selecting any text.</dd>
+ <dt><code>insertIntoLine(handle, position,
+ text)</code></dt><dd>Insert a piece of text into a line.
+ <code>position</code> can be an integer, specifying the position
+ in the line where the text should be inserted, or the string
+ <code>"end"</code>, for the end of the line.</dd>
+ </dl>
+
+ <h2 id="writeparser">Writing a Parser</h2>
+
+ <p>A parser is implemented by one or more files (see
+ <code>parserfile</code> above) which, when loaded, add a
+ <code>Parser</code> object to the <code>Editor</code> object
+ defined by <a href="js/editor.js"><code>editor.js</code></a>. This
+ object must support the following interface:</p>
+
+ <dl>
+
+ <dt><code>make(stream)</code></dt><dd>A function that, given a
+ string stream (see <a
+ href="js/stringstream.js"><code>stringstream.js</code></a>),
+ creates a parser. The behaviour of this parser is described
+ below.</dd>
+
+ <dt><code>electricChars</code></dt><dd>An optional string
+ containing the characters that, when typed, should cause the
+ indentation of the current line to be recomputed (for example
+ <code>"{}"</code> for c-like languages).</dd>
+
+ <dt><code>configure(object)</code></dt><dd>An optional function
+ that can be used to configure the parser. If it exists, and an
+ editor is given a <code>parserConfig</code> option, it will be
+ called with the value of that option.</dd>
+
+ <dt><code>firstIndentation(chars, current,
+ direction)</code></dt><dd>An optional function that is used to
+ determine the proper indentation of the first line of a
+ document. When not provided, <code>0</code> is used.</dd>
+ </dl>
+
+ <p>When the <code>make</code> method is called with a string
+ stream, it should return a MochiKit-style iterator: an object with
+ a <code>next</code> method, which will raise
+ <code>StopIteration</code> when it is at its end (see <a
+ href="http://bob.pythonmac.org/archives/2005/07/06/iteration-in-javascript/">this</a>
+ for details). This iterator, when called, will consume input from
+ the string stream, and produce a token object.</p>
+
+ <p>Token objects represent a single significant piece of the text
+ that is being edited. A token object must have a
+ <code>value</code> property holding the text it stands for, and a
+ <code>style</code> property with the CSS class that should be used
+ to colour this element. This can be anything, except that any
+ whitespace at the start of a line should <em>always</em> have
+ class <code>"whitespace"</code>: The editor must be able to
+ recognize these when it indents lines. Furthermore, each newline
+ character <em>must</em> have its own separate token, which has an
+ <code>indentation</code> property holding a function that can be
+ used to determine the proper indentation level for the next line.
+ This function optionally takes the text in the first token of the
+ next line, the current indentation of the line, and the
+ 'direction' of the indentation as arguments, which it can use to
+ adjust the indentation level. The direction argument is only
+ useful for modes in which lines do not have a fixed indentation,
+ and can be modified by multiple tab presses. It is
+ <code>null</code> for 'default' indentations (like what happens
+ when the user presses enter), <code>true</code> for regular tab
+ presses, and <code>false</code> for control-tab or shift-tab.</p>
+
+ <p>So far this should be pretty easy. The hard part is that this
+ iterator must also have a <code>copy</code> method. This method,
+ called without arguments, returns a function representing the
+ current state of the parser. When this state function is later
+ called with a string stream as its argument, it returns a parser
+ object that resumes parsing using the old state and the new input
+ stream. It may assume that only one parser is active at a time,
+ and can clobber the state of the old parser if it wants.</p>
+
+ <p>For examples, see <a
+ href="js/parsejavascript.js"><code>parsejavascript.js</code></a>,
+ <a href="js/parsexml.js"><code>parsexml.js</code></a>, and <a
+ href="js/parsecss.js"><code>parsecss.js</code></a>.</p>
+
+ </body>
+</html>