From d61280d03dd6e09ef5029fe5b1c2f69b82e7878b Mon Sep 17 00:00:00 2001 From: Wes Cossick Date: Tue, 14 Jul 2015 16:07:56 -0500 Subject: [PATCH] Use GitHub flavored markdown --- source files/codemirror/gfm.js | 111 +++++++++++++++++++++++++++++ source files/codemirror/overlay.js | 85 ++++++++++++++++++++++ source files/simplemde.js | 2 +- source files/theme.css | 4 ++ 4 files changed, 201 insertions(+), 1 deletion(-) create mode 100644 source files/codemirror/gfm.js create mode 100644 source files/codemirror/overlay.js diff --git a/source files/codemirror/gfm.js b/source files/codemirror/gfm.js new file mode 100644 index 0000000..cf9b8c9 --- /dev/null +++ b/source files/codemirror/gfm.js @@ -0,0 +1,111 @@ +// CodeMirror, copyright (c) by Marijn Haverbeke and others +// Distributed under an MIT license: http://codemirror.net/LICENSE +(function(mod) { + if (typeof exports == "object" && typeof module == "object") // CommonJS + mod(require("../../lib/codemirror"), require("../markdown/markdown"), require("../../addon/mode/overlay")); + else if (typeof define == "function" && define.amd) // AMD + define(["../../lib/codemirror", "../markdown/markdown", "../../addon/mode/overlay"], mod); + else // Plain browser env + mod(CodeMirror); +})(function(CodeMirror) { + "use strict"; + + CodeMirror.defineMode("gfm", function(config, modeConfig) { + var codeDepth = 0; + + function blankLine(state) { + state.code = false; + return null; + } + var gfmOverlay = { + startState: function() { + return { + code: false, + codeBlock: false, + ateSpace: false + }; + }, + copyState: function(s) { + return { + code: s.code, + codeBlock: s.codeBlock, + ateSpace: s.ateSpace + }; + }, + token: function(stream, state) { + state.combineTokens = null; + + // Hack to prevent formatting override inside code blocks (block and inline) + if (state.codeBlock) { + if (stream.match(/^```/)) { + state.codeBlock = false; + return null; + } + stream.skipToEnd(); + return null; + } + if (stream.sol()) { + state.code = false; + } + if (stream.sol() && stream.match(/^```/)) { + stream.skipToEnd(); + state.codeBlock = true; + return null; + } + // If this block is changed, it may need to be updated in Markdown mode + if (stream.peek() === '`') { + stream.next(); + var before = stream.pos; + stream.eatWhile('`'); + var difference = 1 + stream.pos - before; + if (!state.code) { + codeDepth = difference; + state.code = true; + } else { + if (difference === codeDepth) { // Must be exact + state.code = false; + } + } + return null; + } else if (state.code) { + stream.next(); + return null; + } + // Check if space. If so, links can be formatted later on + if (stream.eatSpace()) { + state.ateSpace = true; + return null; + } + if (stream.sol() || state.ateSpace) { + state.ateSpace = false; + } + if (stream.match(/^((?:[a-z][\w-]+:(?:\/{1,3}|[a-z0-9%])|www\d{0,3}[.]|[a-z0-9.\-]+[.][a-z]{2,4}\/)(?:[^\s()<>]|\([^\s()<>]*\))+(?:\([^\s()<>]*\)|[^\s`*!()\[\]{};:'".,<>?«»“”‘’]))/i) && + stream.string.slice(stream.start - 2, stream.start) != "](") { + // URLs + // Taken from http://daringfireball.net/2010/07/improved_regex_for_matching_urls + // And then (issue #1160) simplified to make it not crash the Chrome Regexp engine + state.combineTokens = true; + return "link"; + } + stream.next(); + return null; + }, + blankLine: blankLine + }; + + var markdownConfig = { + underscoresBreakWords: false, + taskLists: true, + fencedCodeBlocks: true, + strikethrough: true + }; + for (var attr in modeConfig) { + markdownConfig[attr] = modeConfig[attr]; + } + markdownConfig.name = "markdown"; + return CodeMirror.overlayMode(CodeMirror.getMode(config, markdownConfig), gfmOverlay); + + }, "markdown"); + + CodeMirror.defineMIME("text/x-gfm", "gfm"); +}); \ No newline at end of file diff --git a/source files/codemirror/overlay.js b/source files/codemirror/overlay.js new file mode 100644 index 0000000..0b7d7ab --- /dev/null +++ b/source files/codemirror/overlay.js @@ -0,0 +1,85 @@ +// CodeMirror, copyright (c) by Marijn Haverbeke and others +// Distributed under an MIT license: http://codemirror.net/LICENSE + +// Utility function that allows modes to be combined. The mode given +// as the base argument takes care of most of the normal mode +// functionality, but a second (typically simple) mode is used, which +// can override the style of text. Both modes get to parse all of the +// text, but when both assign a non-null style to a piece of code, the +// overlay wins, unless the combine argument was true and not overridden, +// or state.overlay.combineTokens was true, in which case the styles are +// combined. + +(function(mod) { + if (typeof exports == "object" && typeof module == "object") // CommonJS + mod(require("../../lib/codemirror")); + else if (typeof define == "function" && define.amd) // AMD + define(["../../lib/codemirror"], mod); + else // Plain browser env + mod(CodeMirror); +})(function(CodeMirror) { +"use strict"; + +CodeMirror.overlayMode = function(base, overlay, combine) { + return { + startState: function() { + return { + base: CodeMirror.startState(base), + overlay: CodeMirror.startState(overlay), + basePos: 0, baseCur: null, + overlayPos: 0, overlayCur: null, + streamSeen: null + }; + }, + copyState: function(state) { + return { + base: CodeMirror.copyState(base, state.base), + overlay: CodeMirror.copyState(overlay, state.overlay), + basePos: state.basePos, baseCur: null, + overlayPos: state.overlayPos, overlayCur: null + }; + }, + + token: function(stream, state) { + if (stream != state.streamSeen || + Math.min(state.basePos, state.overlayPos) < stream.start) { + state.streamSeen = stream; + state.basePos = state.overlayPos = stream.start; + } + + if (stream.start == state.basePos) { + state.baseCur = base.token(stream, state.base); + state.basePos = stream.pos; + } + if (stream.start == state.overlayPos) { + stream.pos = stream.start; + state.overlayCur = overlay.token(stream, state.overlay); + state.overlayPos = stream.pos; + } + stream.pos = Math.min(state.basePos, state.overlayPos); + + // state.overlay.combineTokens always takes precedence over combine, + // unless set to null + if (state.overlayCur == null) return state.baseCur; + else if (state.baseCur != null && + state.overlay.combineTokens || + combine && state.overlay.combineTokens == null) + return state.baseCur + " " + state.overlayCur; + else return state.overlayCur; + }, + + indent: base.indent && function(state, textAfter) { + return base.indent(state.base, textAfter); + }, + electricChars: base.electricChars, + + innerMode: function(state) { return {state: state.base, mode: base}; }, + + blankLine: function(state) { + if (base.blankLine) base.blankLine(state.base); + if (overlay.blankLine) overlay.blankLine(state.overlay); + } + }; +}; + +}); \ No newline at end of file diff --git a/source files/simplemde.js b/source files/simplemde.js index be19fcb..1618dd4 100644 --- a/source files/simplemde.js +++ b/source files/simplemde.js @@ -531,7 +531,7 @@ SimpleMDE.prototype.render = function(el) { keyMaps['Shift-Tab'] = 'shiftTabAndIndentContinueMarkdownList'; this.codemirror = CodeMirror.fromTextArea(el, { - mode: 'markdown', + mode: 'gfm', theme: 'paper', tabSize: (options.tabSize != undefined) ? options.tabSize : 2, indentWithTabs: (options.indentWithTabs === false) ? false : true, diff --git a/source files/theme.css b/source files/theme.css index a955a9f..de2331b 100644 --- a/source files/theme.css +++ b/source files/theme.css @@ -472,4 +472,8 @@ div.CodeMirror span.CodeMirror-nonmatchingbracket { .CodeMirror .CodeMirror-code .cm-comment { background: rgba(0, 0, 0, .05); border-radius: 2px; +} + +.CodeMirror .CodeMirror-code .cm-strikethrough { + text-decoration: line-through; } \ No newline at end of file