Permalink
Browse files

Include CodeMirror for proper markdown editing.

  • Loading branch information...
1 parent aa0c257 commit bca56a4f285bdeab31699c02ea065bdd16dce55f Michael Aufreiter committed Mar 9, 2012
Showing with 3,546 additions and 0 deletions.
  1. +2,906 −0 _includes/vendor/codemirror/codemirror.js
  2. +245 −0 _includes/vendor/codemirror/markdown.js
  3. +267 −0 _includes/vendor/codemirror/xml.js
  4. +4 −0 boot.js
  5. +112 −0 codemirror.css
  6. +1 −0 index.html
  7. +11 −0 styles.css
View
2,906 _includes/vendor/codemirror/codemirror.js
2,906 additions, 0 deletions not shown because the diff is too large. Please use a local Git client to view these changes.
View
245 _includes/vendor/codemirror/markdown.js
@@ -0,0 +1,245 @@
+CodeMirror.defineMode("markdown", function(cmCfg, modeCfg) {
+
+ var htmlMode = CodeMirror.getMode(cmCfg, { name: 'xml', htmlMode: true });
+
+ var header = 'header'
+ , code = 'comment'
+ , quote = 'quote'
+ , list = 'string'
+ , hr = 'hr'
+ , linktext = 'link'
+ , linkhref = 'string'
+ , em = 'em'
+ , strong = 'strong'
+ , emstrong = 'emstrong';
+
+ var hrRE = /^([*\-=_])(?:\s*\1){2,}\s*$/
+ , ulRE = /^[*\-+]\s+/
+ , olRE = /^[0-9]+\.\s+/
+ , headerRE = /^(?:\={3,}|-{3,})$/
+ , textRE = /^[^\[*_\\<>`]+/;
+
+ function switchInline(stream, state, f) {
+ state.f = state.inline = f;
+ return f(stream, state);
+ }
+
+ function switchBlock(stream, state, f) {
+ state.f = state.block = f;
+ return f(stream, state);
+ }
+
+
+ // Blocks
+
+ function blankLine(state) {
+ // Reset EM state
+ state.em = false;
+ // Reset STRONG state
+ state.strong = false;
+ return null;
+ }
+
+ function blockNormal(stream, state) {
+ var match;
+ if (state.indentationDiff >= 4) {
+ state.indentation -= state.indentationDiff;
+ stream.skipToEnd();
+ return code;
+ } else if (stream.eatSpace()) {
+ return null;
+ } else if (stream.peek() === '#' || stream.match(headerRE)) {
+ state.header = true;
+ } else if (stream.eat('>')) {
+ state.indentation++;
+ state.quote = true;
+ } else if (stream.peek() === '[') {
+ return switchInline(stream, state, footnoteLink);
+ } else if (stream.match(hrRE, true)) {
+ return hr;
+ } else if (match = stream.match(ulRE, true) || stream.match(olRE, true)) {
+ state.indentation += match[0].length;
+ return list;
+ }
+
+ return switchInline(stream, state, state.inline);
+ }
+
+ function htmlBlock(stream, state) {
+ var style = htmlMode.token(stream, state.htmlState);
+ if (style === 'tag' && state.htmlState.type !== 'openTag' && !state.htmlState.context) {
+ state.f = inlineNormal;
+ state.block = blockNormal;
+ }
+ return style;
+ }
+
+
+ // Inline
+ function getType(state) {
+ var styles = [];
+
+ if (state.strong) { styles.push(state.em ? emstrong : strong); }
+ else if (state.em) { styles.push(em); }
+
+ if (state.header) { styles.push(header); }
+ if (state.quote) { styles.push(quote); }
+
+ return styles.length ? styles.join(' ') : null;
+ }
+
+ function handleText(stream, state) {
+ if (stream.match(textRE, true)) {
+ return getType(state);
+ }
+ return undefined;
+ }
+
+ function inlineNormal(stream, state) {
+ var style = state.text(stream, state)
+ if (typeof style !== 'undefined')
+ return style;
+
+ var ch = stream.next();
+
+ if (ch === '\\') {
+ stream.next();
+ return getType(state);
+ }
+ if (ch === '`') {
+ return switchInline(stream, state, inlineElement(code, '`'));
+ }
+ if (ch === '[') {
+ return switchInline(stream, state, linkText);
+ }
+ if (ch === '<' && stream.match(/^\w/, false)) {
+ stream.backUp(1);
+ return switchBlock(stream, state, htmlBlock);
+ }
+
+ var t = getType(state);
+ if (ch === '*' || ch === '_') {
+ if (stream.eat(ch)) {
+ return (state.strong = !state.strong) ? getType(state) : t;
+ }
+ return (state.em = !state.em) ? getType(state) : t;
+ }
+
+ return getType(state);
+ }
+
+ function linkText(stream, state) {
+ while (!stream.eol()) {
+ var ch = stream.next();
+ if (ch === '\\') stream.next();
+ if (ch === ']') {
+ state.inline = state.f = linkHref;
+ return linktext;
+ }
+ }
+ return linktext;
+ }
+
+ function linkHref(stream, state) {
+ stream.eatSpace();
+ var ch = stream.next();
+ if (ch === '(' || ch === '[') {
+ return switchInline(stream, state, inlineElement(linkhref, ch === '(' ? ')' : ']'));
+ }
+ return 'error';
+ }
+
+ function footnoteLink(stream, state) {
+ if (stream.match(/^[^\]]*\]:/, true)) {
+ state.f = footnoteUrl;
+ return linktext;
+ }
+ return switchInline(stream, state, inlineNormal);
+ }
+
+ function footnoteUrl(stream, state) {
+ stream.eatSpace();
+ stream.match(/^[^\s]+/, true);
+ state.f = state.inline = inlineNormal;
+ return linkhref;
+ }
+
+ function inlineRE(endChar) {
+ if (!inlineRE[endChar]) {
+ // match any not-escaped-non-endChar and any escaped char
+ // then match endChar or eol
+ inlineRE[endChar] = new RegExp('^(?:[^\\\\\\' + endChar + ']|\\\\.)*(?:\\' + endChar + '|$)');
+ }
+ return inlineRE[endChar];
+ }
+
+ function inlineElement(type, endChar, next) {
+ next = next || inlineNormal;
+ return function(stream, state) {
+ stream.match(inlineRE(endChar));
+ state.inline = state.f = next;
+ return type;
+ };
+ }
+
+ return {
+ startState: function() {
+ return {
+ f: blockNormal,
+
+ block: blockNormal,
+ htmlState: htmlMode.startState(),
+ indentation: 0,
+
+ inline: inlineNormal,
+ text: handleText,
+ em: false,
+ strong: false,
+ header: false,
+ quote: false
+ };
+ },
+
+ copyState: function(s) {
+ return {
+ f: s.f,
+
+ block: s.block,
+ htmlState: CodeMirror.copyState(htmlMode, s.htmlState),
+ indentation: s.indentation,
+
+ inline: s.inline,
+ text: s.text,
+ em: s.em,
+ strong: s.strong,
+ header: s.header,
+ quote: s.quote
+ };
+ },
+
+ token: function(stream, state) {
+ if (stream.sol()) {
+ if (stream.match(/^\s*$/, true)) { return blankLine(state); }
+
+ // Reset state.header
+ state.header = false;
+ // Reset state.quote
+ state.quote = false;
+
+ state.f = state.block;
+ var indentation = stream.match(/^\s*/, true)[0].replace(/\t/g, ' ').length;
+ state.indentationDiff = indentation - state.indentation;
+ state.indentation = indentation;
+ if (indentation > 0) { return null; }
+ }
+ return state.f(stream, state);
+ },
+
+ blankLine: blankLine,
+
+ getType: getType
+ };
+
+});
+
+CodeMirror.defineMIME("text/x-markdown", "markdown");
View
267 _includes/vendor/codemirror/xml.js
@@ -0,0 +1,267 @@
+CodeMirror.defineMode("xml", function(config, parserConfig) {
+ var indentUnit = config.indentUnit;
+ var Kludges = parserConfig.htmlMode ? {
+ autoSelfClosers: {"br": true, "img": true, "hr": true, "link": true, "input": true,
+ "meta": true, "col": true, "frame": true, "base": true, "area": true},
+ doNotIndent: {"pre": true},
+ allowUnquoted: true,
+ allowMissing: false
+ } : {autoSelfClosers: {}, doNotIndent: {}, allowUnquoted: false, allowMissing: false};
+ var alignCDATA = parserConfig.alignCDATA;
+
+ // Return variables for tokenizers
+ var tagName, type;
+
+ function inText(stream, state) {
+ function chain(parser) {
+ state.tokenize = parser;
+ return parser(stream, state);
+ }
+
+ var ch = stream.next();
+ if (ch == "<") {
+ if (stream.eat("!")) {
+ if (stream.eat("[")) {
+ if (stream.match("CDATA[")) return chain(inBlock("atom", "]]>"));
+ else return null;
+ }
+ else if (stream.match("--")) return chain(inBlock("comment", "-->"));
+ else if (stream.match("DOCTYPE", true, true)) {
+ stream.eatWhile(/[\w\._\-]/);
+ return chain(doctype(1));
+ }
+ else return null;
+ }
+ else if (stream.eat("?")) {
+ stream.eatWhile(/[\w\._\-]/);
+ state.tokenize = inBlock("meta", "?>");
+ return "meta";
+ }
+ else {
+ type = stream.eat("/") ? "closeTag" : "openTag";
+ stream.eatSpace();
+ tagName = "";
+ var c;
+ while ((c = stream.eat(/[^\s\u00a0=<>\"\'\/?]/))) tagName += c;
+ state.tokenize = inTag;
+ return "tag";
+ }
+ }
+ else if (ch == "&") {
+ var ok;
+ if (stream.eat("#")) {
+ if (stream.eat("x")) {
+ ok = stream.eatWhile(/[a-fA-F\d]/) && stream.eat(";");
+ } else {
+ ok = stream.eatWhile(/[\d]/) && stream.eat(";");
+ }
+ } else {
+ ok = stream.eatWhile(/[\w\.\-:]/) && stream.eat(";");
+ }
+ return ok ? "atom" : "error";
+ }
+ else {
+ stream.eatWhile(/[^&<]/);
+ return null;
+ }
+ }
+
+ function inTag(stream, state) {
+ var ch = stream.next();
+ if (ch == ">" || (ch == "/" && stream.eat(">"))) {
+ state.tokenize = inText;
+ type = ch == ">" ? "endTag" : "selfcloseTag";
+ return "tag";
+ }
+ else if (ch == "=") {
+ type = "equals";
+ return null;
+ }
+ else if (/[\'\"]/.test(ch)) {
+ state.tokenize = inAttribute(ch);
+ return state.tokenize(stream, state);
+ }
+ else {
+ stream.eatWhile(/[^\s\u00a0=<>\"\'\/?]/);
+ return "word";
+ }
+ }
+
+ function inAttribute(quote) {
+ return function(stream, state) {
+ while (!stream.eol()) {
+ if (stream.next() == quote) {
+ state.tokenize = inTag;
+ break;
+ }
+ }
+ return "string";
+ };
+ }
+
+ function inBlock(style, terminator) {
+ return function(stream, state) {
+ while (!stream.eol()) {
+ if (stream.match(terminator)) {
+ state.tokenize = inText;
+ break;
+ }
+ stream.next();
+ }
+ return style;
+ };
+ }
+ function doctype(depth) {
+ return function(stream, state) {
+ var ch;
+ while ((ch = stream.next()) != null) {
+ if (ch == "<") {
+ state.tokenize = doctype(depth + 1);
+ return state.tokenize(stream, state);
+ } else if (ch == ">") {
+ if (depth == 1) {
+ state.tokenize = inText;
+ break;
+ } else {
+ state.tokenize = doctype(depth - 1);
+ return state.tokenize(stream, state);
+ }
+ }
+ }
+ return "meta";
+ };
+ }
+
+ var curState, setStyle;
+ function pass() {
+ for (var i = arguments.length - 1; i >= 0; i--) curState.cc.push(arguments[i]);
+ }
+ function cont() {
+ pass.apply(null, arguments);
+ return true;
+ }
+
+ function pushContext(tagName, startOfLine) {
+ var noIndent = Kludges.doNotIndent.hasOwnProperty(tagName) || (curState.context && curState.context.noIndent);
+ curState.context = {
+ prev: curState.context,
+ tagName: tagName,
+ indent: curState.indented,
+ startOfLine: startOfLine,
+ noIndent: noIndent
+ };
+ }
+ function popContext() {
+ if (curState.context) curState.context = curState.context.prev;
+ }
+
+ function element(type) {
+ if (type == "openTag") {
+ curState.tagName = tagName;
+ return cont(attributes, endtag(curState.startOfLine));
+ } else if (type == "closeTag") {
+ var err = false;
+ if (curState.context) {
+ err = curState.context.tagName != tagName;
+ } else {
+ err = true;
+ }
+ if (err) setStyle = "error";
+ return cont(endclosetag(err));
+ }
+ return cont();
+ }
+ function endtag(startOfLine) {
+ return function(type) {
+ if (type == "selfcloseTag" ||
+ (type == "endTag" && Kludges.autoSelfClosers.hasOwnProperty(curState.tagName.toLowerCase())))
+ return cont();
+ if (type == "endTag") {pushContext(curState.tagName, startOfLine); return cont();}
+ return cont();
+ };
+ }
+ function endclosetag(err) {
+ return function(type) {
+ if (err) setStyle = "error";
+ if (type == "endTag") { popContext(); return cont(); }
+ setStyle = "error";
+ return cont(arguments.callee);
+ }
+ }
+
+ function attributes(type) {
+ if (type == "word") {setStyle = "attribute"; return cont(attribute, attributes);}
+ if (type == "endTag" || type == "selfcloseTag") return pass();
+ setStyle = "error";
+ return cont(attributes);
+ }
+ function attribute(type) {
+ if (type == "equals") return cont(attvalue, attributes);
+ if (!Kludges.allowMissing) setStyle = "error";
+ return (type == "endTag" || type == "selfcloseTag") ? pass() : cont();
+ }
+ function attvalue(type) {
+ if (type == "string") return cont(attvaluemaybe);
+ if (type == "word" && Kludges.allowUnquoted) {setStyle = "string"; return cont();}
+ setStyle = "error";
+ return (type == "endTag" || type == "selfCloseTag") ? pass() : cont();
+ }
+ function attvaluemaybe(type) {
+ if (type == "string") return cont(attvaluemaybe);
+ else return pass();
+ }
+
+ return {
+ startState: function() {
+ return {tokenize: inText, cc: [], indented: 0, startOfLine: true, tagName: null, context: null};
+ },
+
+ token: function(stream, state) {
+ if (stream.sol()) {
+ state.startOfLine = true;
+ state.indented = stream.indentation();
+ }
+ if (stream.eatSpace()) return null;
+
+ setStyle = type = tagName = null;
+ var style = state.tokenize(stream, state);
+ state.type = type;
+ if ((style || type) && style != "comment") {
+ curState = state;
+ while (true) {
+ var comb = state.cc.pop() || element;
+ if (comb(type || style)) break;
+ }
+ }
+ state.startOfLine = false;
+ return setStyle || style;
+ },
+
+ indent: function(state, textAfter, fullLine) {
+ var context = state.context;
+ if ((state.tokenize != inTag && state.tokenize != inText) ||
+ context && context.noIndent)
+ return fullLine ? fullLine.match(/^(\s*)/)[0].length : 0;
+ if (alignCDATA && /<!\[CDATA\[/.test(textAfter)) return 0;
+ if (context && /^<\//.test(textAfter))
+ context = context.prev;
+ while (context && !context.startOfLine)
+ context = context.prev;
+ if (context) return context.indent + indentUnit;
+ else return 0;
+ },
+
+ compareStates: function(a, b) {
+ if (a.indented != b.indented || a.tokenize != b.tokenize) return false;
+ for (var ca = a.context, cb = b.context; ; ca = ca.prev, cb = cb.prev) {
+ if (!ca || !cb) return ca == cb;
+ if (ca.tagName != cb.tagName) return false;
+ }
+ },
+
+ electricChars: "/"
+ };
+});
+
+CodeMirror.defineMIME("application/xml", "xml");
+CodeMirror.defineMIME("text/html", {name: "xml", htmlMode: true});
View
4 boot.js
@@ -9,6 +9,9 @@
{% include vendor/textile.js %}
{% include vendor/base64.js %}
{% include vendor/github.js %}
+{% include vendor/codemirror/codemirror.js %}
+{% include vendor/codemirror/xml.js %}
+{% include vendor/codemirror/markdown.js %}
{% include vendor/jquery.cookie.js %}
window.app = {
@@ -36,6 +39,7 @@ window.app.utils.serial = function () {
{% include views/application.js %}
{% include views/header.js %}
{% include views/posts.js %}
+{% include views/post.js %}
// Keep session here?
window.session = {};
View
112 codemirror.css
@@ -0,0 +1,112 @@
+.CodeMirror {
+ line-height: 1em;
+ font-family: monospace;
+}
+
+.CodeMirror-scroll {
+ overflow: auto;
+ height: 300px;
+ /* This is needed to prevent an IE[67] bug where the scrolled content
+ is visible outside of the scrolling box. */
+ position: relative;
+ outline: none;
+}
+
+.CodeMirror-gutter {
+ position: absolute; left: 0; top: 0;
+ z-index: 10;
+ background-color: #f7f7f7;
+ border-right: 1px solid #eee;
+ min-width: 2em;
+ height: 100%;
+}
+.CodeMirror-gutter-text {
+ color: #aaa;
+ text-align: right;
+ padding: .4em .2em .4em .4em;
+ white-space: pre !important;
+}
+.CodeMirror-lines {
+ padding: .4em;
+ white-space: pre;
+}
+
+.CodeMirror pre {
+ -moz-border-radius: 0;
+ -webkit-border-radius: 0;
+ -o-border-radius: 0;
+ border-radius: 0;
+ border-width: 0; margin: 0; padding: 0; background: transparent;
+ font-family: inherit;
+ font-size: inherit;
+ padding: 0; margin: 0;
+ white-space: pre;
+ word-wrap: normal;
+}
+
+.CodeMirror-wrap pre {
+ word-wrap: break-word;
+ white-space: pre-wrap;
+}
+.CodeMirror-wrap .CodeMirror-scroll {
+ overflow-x: hidden;
+}
+
+.CodeMirror textarea {
+ outline: none !important;
+}
+
+.CodeMirror pre.CodeMirror-cursor {
+ z-index: 10;
+ position: absolute;
+ visibility: hidden;
+ border-left: 1px solid black;
+ border-right:none;
+ width:0;
+}
+.CodeMirror pre.CodeMirror-cursor.CodeMirror-overwrite {}
+.CodeMirror-focused pre.CodeMirror-cursor {
+ visibility: visible;
+}
+
+div.CodeMirror-selected { background: #d9d9d9; }
+.CodeMirror-focused div.CodeMirror-selected { background: #d7d4f0; }
+
+.CodeMirror-searching {
+ background: #ffa;
+ background: rgba(255, 255, 0, .4);
+}
+
+/* Default theme */
+
+.cm-s-default span.cm-keyword {color: #708;}
+.cm-s-default span.cm-atom {color: #219;}
+.cm-s-default span.cm-number {color: #164;}
+.cm-s-default span.cm-def {color: #00f;}
+.cm-s-default span.cm-variable {color: black;}
+.cm-s-default span.cm-variable-2 {color: #05a;}
+.cm-s-default span.cm-variable-3 {color: #085;}
+.cm-s-default span.cm-property {color: black;}
+.cm-s-default span.cm-operator {color: black;}
+.cm-s-default span.cm-comment {color: #a50;}
+.cm-s-default span.cm-string {color: #a11;}
+.cm-s-default span.cm-string-2 {color: #f50;}
+.cm-s-default span.cm-meta {color: #555;}
+.cm-s-default span.cm-error {color: #f00;}
+.cm-s-default span.cm-qualifier {color: #555;}
+.cm-s-default span.cm-builtin {color: #30a;}
+.cm-s-default span.cm-bracket {color: #cc7;}
+.cm-s-default span.cm-tag {color: #170;}
+.cm-s-default span.cm-attribute {color: #00c;}
+.cm-s-default span.cm-header {color: #a0a;}
+.cm-s-default span.cm-quote {color: #090;}
+.cm-s-default span.cm-hr {color: #999;}
+.cm-s-default span.cm-link {color: #00c;}
+
+span.cm-header, span.cm-strong {font-weight: bold;}
+span.cm-em {font-style: italic;}
+span.cm-emstrong {font-style: italic; font-weight: bold;}
+span.cm-link {text-decoration: underline;}
+
+div.CodeMirror span.CodeMirror-matchingbracket {color: #0f0;}
+div.CodeMirror span.CodeMirror-nonmatchingbracket {color: #f22;}
View
1 index.html
@@ -6,6 +6,7 @@
<meta charset='UTF-8'/>
<title>Columnist</title>
<link href='styles.css' type='text/css' rel='stylesheet'/>
+ <link href='codemirror.css' type='text/css' rel='stylesheet'/>
</head>
<body>
View
11 styles.css
@@ -588,3 +588,14 @@ table td {
#posts a {
border: none;
}
+
+/* Editor */
+
+.CodeMirror {
+ background: white;
+ width: 920px;
+ margin: auto;
+ -webkit-box-shadow: 0px 1px 4px rgba(0, 0, 0, 0.35), inset 0px 0px 1px rgba(255, 255, 255, 0.15);
+ -moz-box-shadow: 0px 1px 4px rgba(0, 0, 0, 0.35), inset 0px 0px 1px rgba(255, 255, 255, 0.15);
+ box-shadow: 0px 1px 4px rgba(0, 0, 0, 0.35), inset 0px 0px 1px rgba(255, 255, 255, 0.15);
+}

0 comments on commit bca56a4

Please sign in to comment.