summaryrefslogtreecommitdiff
path: root/static/bower_components/simplemde/src/js
diff options
context:
space:
mode:
authorMatt Arnold2025-04-09 15:24:29 -0400
committerMatt Arnold2025-04-09 15:24:29 -0400
commitd1745a9c1e46d43af005ac966cf4170192b76f97 (patch)
tree7de2e583ce0729915ac33dd177099c29ef5d432d /static/bower_components/simplemde/src/js
parentd6b7302b791b95b69dd2334e1119e697bd58cab3 (diff)
Supercommit
Diffstat (limited to 'static/bower_components/simplemde/src/js')
-rw-r--r--static/bower_components/simplemde/src/js/codemirror/tablist.js44
-rw-r--r--static/bower_components/simplemde/src/js/simplemde.js2028
2 files changed, 2072 insertions, 0 deletions
diff --git a/static/bower_components/simplemde/src/js/codemirror/tablist.js b/static/bower_components/simplemde/src/js/codemirror/tablist.js
new file mode 100644
index 0000000..e6cf2d4
--- /dev/null
+++ b/static/bower_components/simplemde/src/js/codemirror/tablist.js
@@ -0,0 +1,44 @@
+// CodeMirror, copyright (c) by Marijn Haverbeke and others
+// Distributed under an MIT license: http://codemirror.net/LICENSE
+
+var CodeMirror = require("codemirror");
+
+CodeMirror.commands.tabAndIndentMarkdownList = function (cm) {
+ var ranges = cm.listSelections();
+ var pos = ranges[0].head;
+ var eolState = cm.getStateAfter(pos.line);
+ var inList = eolState.list !== false;
+
+ if (inList) {
+ cm.execCommand("indentMore");
+ return;
+ }
+
+ if (cm.options.indentWithTabs) {
+ cm.execCommand("insertTab");
+ }
+ else {
+ var spaces = Array(cm.options.tabSize + 1).join(" ");
+ cm.replaceSelection(spaces);
+ }
+};
+
+CodeMirror.commands.shiftTabAndUnindentMarkdownList = function (cm) {
+ var ranges = cm.listSelections();
+ var pos = ranges[0].head;
+ var eolState = cm.getStateAfter(pos.line);
+ var inList = eolState.list !== false;
+
+ if (inList) {
+ cm.execCommand("indentLess");
+ return;
+ }
+
+ if (cm.options.indentWithTabs) {
+ cm.execCommand("insertTab");
+ }
+ else {
+ var spaces = Array(cm.options.tabSize + 1).join(" ");
+ cm.replaceSelection(spaces);
+ }
+};
diff --git a/static/bower_components/simplemde/src/js/simplemde.js b/static/bower_components/simplemde/src/js/simplemde.js
new file mode 100644
index 0000000..016d693
--- /dev/null
+++ b/static/bower_components/simplemde/src/js/simplemde.js
@@ -0,0 +1,2028 @@
+/*global require,module*/
+"use strict";
+var CodeMirror = require("codemirror");
+require("codemirror/addon/edit/continuelist.js");
+require("./codemirror/tablist");
+require("codemirror/addon/display/fullscreen.js");
+require("codemirror/mode/markdown/markdown.js");
+require("codemirror/addon/mode/overlay.js");
+require("codemirror/addon/display/placeholder.js");
+require("codemirror/addon/selection/mark-selection.js");
+require("codemirror/mode/gfm/gfm.js");
+require("codemirror/mode/xml/xml.js");
+var CodeMirrorSpellChecker = require("codemirror-spell-checker");
+var marked = require("marked");
+
+
+// Some variables
+var isMac = /Mac/.test(navigator.platform);
+
+// Mapping of actions that can be bound to keyboard shortcuts or toolbar buttons
+var bindings = {
+ "toggleBold": toggleBold,
+ "toggleItalic": toggleItalic,
+ "drawLink": drawLink,
+ "toggleHeadingSmaller": toggleHeadingSmaller,
+ "toggleHeadingBigger": toggleHeadingBigger,
+ "drawImage": drawImage,
+ "toggleBlockquote": toggleBlockquote,
+ "toggleOrderedList": toggleOrderedList,
+ "toggleUnorderedList": toggleUnorderedList,
+ "toggleCodeBlock": toggleCodeBlock,
+ "togglePreview": togglePreview,
+ "toggleStrikethrough": toggleStrikethrough,
+ "toggleHeading1": toggleHeading1,
+ "toggleHeading2": toggleHeading2,
+ "toggleHeading3": toggleHeading3,
+ "cleanBlock": cleanBlock,
+ "drawTable": drawTable,
+ "drawHorizontalRule": drawHorizontalRule,
+ "undo": undo,
+ "redo": redo,
+ "toggleSideBySide": toggleSideBySide,
+ "toggleFullScreen": toggleFullScreen
+};
+
+var shortcuts = {
+ "toggleBold": "Cmd-B",
+ "toggleItalic": "Cmd-I",
+ "drawLink": "Cmd-K",
+ "toggleHeadingSmaller": "Cmd-H",
+ "toggleHeadingBigger": "Shift-Cmd-H",
+ "cleanBlock": "Cmd-E",
+ "drawImage": "Cmd-Alt-I",
+ "toggleBlockquote": "Cmd-'",
+ "toggleOrderedList": "Cmd-Alt-L",
+ "toggleUnorderedList": "Cmd-L",
+ "toggleCodeBlock": "Cmd-Alt-C",
+ "togglePreview": "Cmd-P",
+ "toggleSideBySide": "F9",
+ "toggleFullScreen": "F11"
+};
+
+var getBindingName = function(f) {
+ for(var key in bindings) {
+ if(bindings[key] === f) {
+ return key;
+ }
+ }
+ return null;
+};
+
+var isMobile = function() {
+ var check = false;
+ (function(a) {
+ if(/(android|bb\d+|meego).+mobile|avantgo|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|iris|kindle|lge |maemo|midp|mmp|mobile.+firefox|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|series(4|6)0|symbian|treo|up\.(browser|link)|vodafone|wap|windows ce|xda|xiino|android|ipad|playbook|silk/i.test(a) || /1207|6310|6590|3gso|4thp|50[1-6]i|770s|802s|a wa|abac|ac(er|oo|s\-)|ai(ko|rn)|al(av|ca|co)|amoi|an(ex|ny|yw)|aptu|ar(ch|go)|as(te|us)|attw|au(di|\-m|r |s )|avan|be(ck|ll|nq)|bi(lb|rd)|bl(ac|az)|br(e|v)w|bumb|bw\-(n|u)|c55\/|capi|ccwa|cdm\-|cell|chtm|cldc|cmd\-|co(mp|nd)|craw|da(it|ll|ng)|dbte|dc\-s|devi|dica|dmob|do(c|p)o|ds(12|\-d)|el(49|ai)|em(l2|ul)|er(ic|k0)|esl8|ez([4-7]0|os|wa|ze)|fetc|fly(\-|_)|g1 u|g560|gene|gf\-5|g\-mo|go(\.w|od)|gr(ad|un)|haie|hcit|hd\-(m|p|t)|hei\-|hi(pt|ta)|hp( i|ip)|hs\-c|ht(c(\-| |_|a|g|p|s|t)|tp)|hu(aw|tc)|i\-(20|go|ma)|i230|iac( |\-|\/)|ibro|idea|ig01|ikom|im1k|inno|ipaq|iris|ja(t|v)a|jbro|jemu|jigs|kddi|keji|kgt( |\/)|klon|kpt |kwc\-|kyo(c|k)|le(no|xi)|lg( g|\/(k|l|u)|50|54|\-[a-w])|libw|lynx|m1\-w|m3ga|m50\/|ma(te|ui|xo)|mc(01|21|ca)|m\-cr|me(rc|ri)|mi(o8|oa|ts)|mmef|mo(01|02|bi|de|do|t(\-| |o|v)|zz)|mt(50|p1|v )|mwbp|mywa|n10[0-2]|n20[2-3]|n30(0|2)|n50(0|2|5)|n7(0(0|1)|10)|ne((c|m)\-|on|tf|wf|wg|wt)|nok(6|i)|nzph|o2im|op(ti|wv)|oran|owg1|p800|pan(a|d|t)|pdxg|pg(13|\-([1-8]|c))|phil|pire|pl(ay|uc)|pn\-2|po(ck|rt|se)|prox|psio|pt\-g|qa\-a|qc(07|12|21|32|60|\-[2-7]|i\-)|qtek|r380|r600|raks|rim9|ro(ve|zo)|s55\/|sa(ge|ma|mm|ms|ny|va)|sc(01|h\-|oo|p\-)|sdk\/|se(c(\-|0|1)|47|mc|nd|ri)|sgh\-|shar|sie(\-|m)|sk\-0|sl(45|id)|sm(al|ar|b3|it|t5)|so(ft|ny)|sp(01|h\-|v\-|v )|sy(01|mb)|t2(18|50)|t6(00|10|18)|ta(gt|lk)|tcl\-|tdg\-|tel(i|m)|tim\-|t\-mo|to(pl|sh)|ts(70|m\-|m3|m5)|tx\-9|up(\.b|g1|si)|utst|v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|\-v)|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c(\-| )|webc|whit|wi(g |nc|nw)|wmlb|wonu|x700|yas\-|your|zeto|zte\-/i.test(a.substr(0, 4))) check = true;
+ })(navigator.userAgent || navigator.vendor || window.opera);
+ return check;
+};
+
+
+/**
+ * Fix shortcut. Mac use Command, others use Ctrl.
+ */
+function fixShortcut(name) {
+ if(isMac) {
+ name = name.replace("Ctrl", "Cmd");
+ } else {
+ name = name.replace("Cmd", "Ctrl");
+ }
+ return name;
+}
+
+
+/**
+ * Create icon element for toolbar.
+ */
+function createIcon(options, enableTooltips, shortcuts) {
+ options = options || {};
+ var el = document.createElement("a");
+ enableTooltips = (enableTooltips == undefined) ? true : enableTooltips;
+
+ if(options.title && enableTooltips) {
+ el.title = createTootlip(options.title, options.action, shortcuts);
+
+ if(isMac) {
+ el.title = el.title.replace("Ctrl", "⌘");
+ el.title = el.title.replace("Alt", "⌥");
+ }
+ }
+
+ el.tabIndex = -1;
+ el.className = options.className;
+ return el;
+}
+
+function createSep() {
+ var el = document.createElement("i");
+ el.className = "separator";
+ el.innerHTML = "|";
+ return el;
+}
+
+function createTootlip(title, action, shortcuts) {
+ var actionName;
+ var tooltip = title;
+
+ if(action) {
+ actionName = getBindingName(action);
+ if(shortcuts[actionName]) {
+ tooltip += " (" + fixShortcut(shortcuts[actionName]) + ")";
+ }
+ }
+
+ return tooltip;
+}
+
+/**
+ * The state of CodeMirror at the given position.
+ */
+function getState(cm, pos) {
+ pos = pos || cm.getCursor("start");
+ var stat = cm.getTokenAt(pos);
+ if(!stat.type) return {};
+
+ var types = stat.type.split(" ");
+
+ var ret = {},
+ data, text;
+ for(var i = 0; i < types.length; i++) {
+ data = types[i];
+ if(data === "strong") {
+ ret.bold = true;
+ } else if(data === "variable-2") {
+ text = cm.getLine(pos.line);
+ if(/^\s*\d+\.\s/.test(text)) {
+ ret["ordered-list"] = true;
+ } else {
+ ret["unordered-list"] = true;
+ }
+ } else if(data === "atom") {
+ ret.quote = true;
+ } else if(data === "em") {
+ ret.italic = true;
+ } else if(data === "quote") {
+ ret.quote = true;
+ } else if(data === "strikethrough") {
+ ret.strikethrough = true;
+ } else if(data === "comment") {
+ ret.code = true;
+ } else if(data === "link") {
+ ret.link = true;
+ } else if(data === "tag") {
+ ret.image = true;
+ } else if(data.match(/^header(\-[1-6])?$/)) {
+ ret[data.replace("header", "heading")] = true;
+ }
+ }
+ return ret;
+}
+
+
+// Saved overflow setting
+var saved_overflow = "";
+
+/**
+ * Toggle full screen of the editor.
+ */
+function toggleFullScreen(editor) {
+ // Set fullscreen
+ var cm = editor.codemirror;
+ cm.setOption("fullScreen", !cm.getOption("fullScreen"));
+
+
+ // Prevent scrolling on body during fullscreen active
+ if(cm.getOption("fullScreen")) {
+ saved_overflow = document.body.style.overflow;
+ document.body.style.overflow = "hidden";
+ } else {
+ document.body.style.overflow = saved_overflow;
+ }
+
+
+ // Update toolbar class
+ var wrap = cm.getWrapperElement();
+
+ if(!/fullscreen/.test(wrap.previousSibling.className)) {
+ wrap.previousSibling.className += " fullscreen";
+ } else {
+ wrap.previousSibling.className = wrap.previousSibling.className.replace(/\s*fullscreen\b/, "");
+ }
+
+
+ // Update toolbar button
+ var toolbarButton = editor.toolbarElements.fullscreen;
+
+ if(!/active/.test(toolbarButton.className)) {
+ toolbarButton.className += " active";
+ } else {
+ toolbarButton.className = toolbarButton.className.replace(/\s*active\s*/g, "");
+ }
+
+
+ // Hide side by side if needed
+ var sidebyside = cm.getWrapperElement().nextSibling;
+ if(/editor-preview-active-side/.test(sidebyside.className))
+ toggleSideBySide(editor);
+}
+
+
+/**
+ * Action for toggling bold.
+ */
+function toggleBold(editor) {
+ _toggleBlock(editor, "bold", editor.options.blockStyles.bold);
+}
+
+
+/**
+ * Action for toggling italic.
+ */
+function toggleItalic(editor) {
+ _toggleBlock(editor, "italic", editor.options.blockStyles.italic);
+}
+
+
+/**
+ * Action for toggling strikethrough.
+ */
+function toggleStrikethrough(editor) {
+ _toggleBlock(editor, "strikethrough", "~~");
+}
+
+/**
+ * Action for toggling code block.
+ */
+function toggleCodeBlock(editor) {
+ var fenceCharsToInsert = editor.options.blockStyles.code;
+
+ function fencing_line(line) {
+ /* return true, if this is a ``` or ~~~ line */
+ if(typeof line !== "object") {
+ throw "fencing_line() takes a 'line' object (not a line number, or line text). Got: " + typeof line + ": " + line;
+ }
+ return line.styles && line.styles[2] && line.styles[2].indexOf("formatting-code-block") !== -1;
+ }
+
+ function token_state(token) {
+ // base goes an extra level deep when mode backdrops are used, e.g. spellchecker on
+ return token.state.base.base || token.state.base;
+ }
+
+ function code_type(cm, line_num, line, firstTok, lastTok) {
+ /*
+ * Return "single", "indented", "fenced" or false
+ *
+ * cm and line_num are required. Others are optional for efficiency
+ * To check in the middle of a line, pass in firstTok yourself.
+ */
+ line = line || cm.getLineHandle(line_num);
+ firstTok = firstTok || cm.getTokenAt({
+ line: line_num,
+ ch: 1
+ });
+ lastTok = lastTok || (!!line.text && cm.getTokenAt({
+ line: line_num,
+ ch: line.text.length - 1
+ }));
+ var types = firstTok.type ? firstTok.type.split(" ") : [];
+ if(lastTok && token_state(lastTok).indentedCode) {
+ // have to check last char, since first chars of first line aren"t marked as indented
+ return "indented";
+ } else if(types.indexOf("comment") === -1) {
+ // has to be after "indented" check, since first chars of first indented line aren"t marked as such
+ return false;
+ } else if(token_state(firstTok).fencedChars || token_state(lastTok).fencedChars || fencing_line(line)) {
+ return "fenced";
+ } else {
+ return "single";
+ }
+ }
+
+ function insertFencingAtSelection(cm, cur_start, cur_end, fenceCharsToInsert) {
+ var start_line_sel = cur_start.line + 1,
+ end_line_sel = cur_end.line + 1,
+ sel_multi = cur_start.line !== cur_end.line,
+ repl_start = fenceCharsToInsert + "\n",
+ repl_end = "\n" + fenceCharsToInsert;
+ if(sel_multi) {
+ end_line_sel++;
+ }
+ // handle last char including \n or not
+ if(sel_multi && cur_end.ch === 0) {
+ repl_end = fenceCharsToInsert + "\n";
+ end_line_sel--;
+ }
+ _replaceSelection(cm, false, [repl_start, repl_end]);
+ cm.setSelection({
+ line: start_line_sel,
+ ch: 0
+ }, {
+ line: end_line_sel,
+ ch: 0
+ });
+ }
+
+ var cm = editor.codemirror,
+ cur_start = cm.getCursor("start"),
+ cur_end = cm.getCursor("end"),
+ tok = cm.getTokenAt({
+ line: cur_start.line,
+ ch: cur_start.ch || 1
+ }), // avoid ch 0 which is a cursor pos but not token
+ line = cm.getLineHandle(cur_start.line),
+ is_code = code_type(cm, cur_start.line, line, tok);
+ var block_start, block_end, lineCount;
+
+ if(is_code === "single") {
+ // similar to some SimpleMDE _toggleBlock logic
+ var start = line.text.slice(0, cur_start.ch).replace("`", ""),
+ end = line.text.slice(cur_start.ch).replace("`", "");
+ cm.replaceRange(start + end, {
+ line: cur_start.line,
+ ch: 0
+ }, {
+ line: cur_start.line,
+ ch: 99999999999999
+ });
+ cur_start.ch--;
+ if(cur_start !== cur_end) {
+ cur_end.ch--;
+ }
+ cm.setSelection(cur_start, cur_end);
+ cm.focus();
+ } else if(is_code === "fenced") {
+ if(cur_start.line !== cur_end.line || cur_start.ch !== cur_end.ch) {
+ // use selection
+
+ // find the fenced line so we know what type it is (tilde, backticks, number of them)
+ for(block_start = cur_start.line; block_start >= 0; block_start--) {
+ line = cm.getLineHandle(block_start);
+ if(fencing_line(line)) {
+ break;
+ }
+ }
+ var fencedTok = cm.getTokenAt({
+ line: block_start,
+ ch: 1
+ });
+ var fence_chars = token_state(fencedTok).fencedChars;
+ var start_text, start_line;
+ var end_text, end_line;
+ // check for selection going up against fenced lines, in which case we don't want to add more fencing
+ if(fencing_line(cm.getLineHandle(cur_start.line))) {
+ start_text = "";
+ start_line = cur_start.line;
+ } else if(fencing_line(cm.getLineHandle(cur_start.line - 1))) {
+ start_text = "";
+ start_line = cur_start.line - 1;
+ } else {
+ start_text = fence_chars + "\n";
+ start_line = cur_start.line;
+ }
+ if(fencing_line(cm.getLineHandle(cur_end.line))) {
+ end_text = "";
+ end_line = cur_end.line;
+ if(cur_end.ch === 0) {
+ end_line += 1;
+ }
+ } else if(cur_end.ch !== 0 && fencing_line(cm.getLineHandle(cur_end.line + 1))) {
+ end_text = "";
+ end_line = cur_end.line + 1;
+ } else {
+ end_text = fence_chars + "\n";
+ end_line = cur_end.line + 1;
+ }
+ if(cur_end.ch === 0) {
+ // full last line selected, putting cursor at beginning of next
+ end_line -= 1;
+ }
+ cm.operation(function() {
+ // end line first, so that line numbers don't change
+ cm.replaceRange(end_text, {
+ line: end_line,
+ ch: 0
+ }, {
+ line: end_line + (end_text ? 0 : 1),
+ ch: 0
+ });
+ cm.replaceRange(start_text, {
+ line: start_line,
+ ch: 0
+ }, {
+ line: start_line + (start_text ? 0 : 1),
+ ch: 0
+ });
+ });
+ cm.setSelection({
+ line: start_line + (start_text ? 1 : 0),
+ ch: 0
+ }, {
+ line: end_line + (start_text ? 1 : -1),
+ ch: 0
+ });
+ cm.focus();
+ } else {
+ // no selection, search for ends of this fenced block
+ var search_from = cur_start.line;
+ if(fencing_line(cm.getLineHandle(cur_start.line))) { // gets a little tricky if cursor is right on a fenced line
+ if(code_type(cm, cur_start.line + 1) === "fenced") {
+ block_start = cur_start.line;
+ search_from = cur_start.line + 1; // for searching for "end"
+ } else {
+ block_end = cur_start.line;
+ search_from = cur_start.line - 1; // for searching for "start"
+ }
+ }
+ if(block_start === undefined) {
+ for(block_start = search_from; block_start >= 0; block_start--) {
+ line = cm.getLineHandle(block_start);
+ if(fencing_line(line)) {
+ break;
+ }
+ }
+ }
+ if(block_end === undefined) {
+ lineCount = cm.lineCount();
+ for(block_end = search_from; block_end < lineCount; block_end++) {
+ line = cm.getLineHandle(block_end);
+ if(fencing_line(line)) {
+ break;
+ }
+ }
+ }
+ cm.operation(function() {
+ cm.replaceRange("", {
+ line: block_start,
+ ch: 0
+ }, {
+ line: block_start + 1,
+ ch: 0
+ });
+ cm.replaceRange("", {
+ line: block_end - 1,
+ ch: 0
+ }, {
+ line: block_end,
+ ch: 0
+ });
+ });
+ cm.focus();
+ }
+ } else if(is_code === "indented") {
+ if(cur_start.line !== cur_end.line || cur_start.ch !== cur_end.ch) {
+ // use selection
+ block_start = cur_start.line;
+ block_end = cur_end.line;
+ if(cur_end.ch === 0) {
+ block_end--;
+ }
+ } else {
+ // no selection, search for ends of this indented block
+ for(block_start = cur_start.line; block_start >= 0; block_start--) {
+ line = cm.getLineHandle(block_start);
+ if(line.text.match(/^\s*$/)) {
+ // empty or all whitespace - keep going
+ continue;
+ } else {
+ if(code_type(cm, block_start, line) !== "indented") {
+ block_start += 1;
+ break;
+ }
+ }
+ }
+ lineCount = cm.lineCount();
+ for(block_end = cur_start.line; block_end < lineCount; block_end++) {
+ line = cm.getLineHandle(block_end);
+ if(line.text.match(/^\s*$/)) {
+ // empty or all whitespace - keep going
+ continue;
+ } else {
+ if(code_type(cm, block_end, line) !== "indented") {
+ block_end -= 1;
+ break;
+ }
+ }
+ }
+ }
+ // if we are going to un-indent based on a selected set of lines, and the next line is indented too, we need to
+ // insert a blank line so that the next line(s) continue to be indented code
+ var next_line = cm.getLineHandle(block_end + 1),
+ next_line_last_tok = next_line && cm.getTokenAt({
+ line: block_end + 1,
+ ch: next_line.text.length - 1
+ }),
+ next_line_indented = next_line_last_tok && token_state(next_line_last_tok).indentedCode;
+ if(next_line_indented) {
+ cm.replaceRange("\n", {
+ line: block_end + 1,
+ ch: 0
+ });
+ }
+
+ for(var i = block_start; i <= block_end; i++) {
+ cm.indentLine(i, "subtract"); // TODO: this doesn't get tracked in the history, so can't be undone :(
+ }
+ cm.focus();
+ } else {
+ // insert code formatting
+ var no_sel_and_starting_of_line = (cur_start.line === cur_end.line && cur_start.ch === cur_end.ch && cur_start.ch === 0);
+ var sel_multi = cur_start.line !== cur_end.line;
+ if(no_sel_and_starting_of_line || sel_multi) {
+ insertFencingAtSelection(cm, cur_start, cur_end, fenceCharsToInsert);
+ } else {
+ _replaceSelection(cm, false, ["`", "`"]);
+ }
+ }
+}
+
+/**
+ * Action for toggling blockquote.
+ */
+function toggleBlockquote(editor) {
+ var cm = editor.codemirror;
+ _toggleLine(cm, "quote");
+}
+
+/**
+ * Action for toggling heading size: normal -> h1 -> h2 -> h3 -> h4 -> h5 -> h6 -> normal
+ */
+function toggleHeadingSmaller(editor) {
+ var cm = editor.codemirror;
+ _toggleHeading(cm, "smaller");
+}
+
+/**
+ * Action for toggling heading size: normal -> h6 -> h5 -> h4 -> h3 -> h2 -> h1 -> normal
+ */
+function toggleHeadingBigger(editor) {
+ var cm = editor.codemirror;
+ _toggleHeading(cm, "bigger");
+}
+
+/**
+ * Action for toggling heading size 1
+ */
+function toggleHeading1(editor) {
+ var cm = editor.codemirror;
+ _toggleHeading(cm, undefined, 1);
+}
+
+/**
+ * Action for toggling heading size 2
+ */
+function toggleHeading2(editor) {
+ var cm = editor.codemirror;
+ _toggleHeading(cm, undefined, 2);
+}
+
+/**
+ * Action for toggling heading size 3
+ */
+function toggleHeading3(editor) {
+ var cm = editor.codemirror;
+ _toggleHeading(cm, undefined, 3);
+}
+
+
+/**
+ * Action for toggling ul.
+ */
+function toggleUnorderedList(editor) {
+ var cm = editor.codemirror;
+ _toggleLine(cm, "unordered-list");
+}
+
+
+/**
+ * Action for toggling ol.
+ */
+function toggleOrderedList(editor) {
+ var cm = editor.codemirror;
+ _toggleLine(cm, "ordered-list");
+}
+
+/**
+ * Action for clean block (remove headline, list, blockquote code, markers)
+ */
+function cleanBlock(editor) {
+ var cm = editor.codemirror;
+ _cleanBlock(cm);
+}
+
+/**
+ * Action for drawing a link.
+ */
+function drawLink(editor) {
+ var cm = editor.codemirror;
+ var stat = getState(cm);
+ var options = editor.options;
+ var url = "http://";
+ if(options.promptURLs) {
+ url = prompt(options.promptTexts.link);
+ if(!url) {
+ return false;
+ }
+ }
+ _replaceSelection(cm, stat.link, options.insertTexts.link, url);
+}
+
+/**
+ * Action for drawing an img.
+ */
+function drawImage(editor) {
+ var cm = editor.codemirror;
+ var stat = getState(cm);
+ var options = editor.options;
+ var url = "http://";
+ if(options.promptURLs) {
+ url = prompt(options.promptTexts.image);
+ if(!url) {
+ return false;
+ }
+ }
+ _replaceSelection(cm, stat.image, options.insertTexts.image, url);
+}
+
+/**
+ * Action for drawing a table.
+ */
+function drawTable(editor) {
+ var cm = editor.codemirror;
+ var stat = getState(cm);
+ var options = editor.options;
+ _replaceSelection(cm, stat.table, options.insertTexts.table);
+}
+
+/**
+ * Action for drawing a horizontal rule.
+ */
+function drawHorizontalRule(editor) {
+ var cm = editor.codemirror;
+ var stat = getState(cm);
+ var options = editor.options;
+ _replaceSelection(cm, stat.image, options.insertTexts.horizontalRule);
+}
+
+
+/**
+ * Undo action.
+ */
+function undo(editor) {
+ var cm = editor.codemirror;
+ cm.undo();
+ cm.focus();
+}
+
+
+/**
+ * Redo action.
+ */
+function redo(editor) {
+ var cm = editor.codemirror;
+ cm.redo();
+ cm.focus();
+}
+
+
+/**
+ * Toggle side by side preview
+ */
+function toggleSideBySide(editor) {
+ var cm = editor.codemirror;
+ var wrapper = cm.getWrapperElement();
+ var preview = wrapper.nextSibling;
+ var toolbarButton = editor.toolbarElements["side-by-side"];
+ var useSideBySideListener = false;
+ if(/editor-preview-active-side/.test(preview.className)) {
+ preview.className = preview.className.replace(
+ /\s*editor-preview-active-side\s*/g, ""
+ );
+ toolbarButton.className = toolbarButton.className.replace(/\s*active\s*/g, "");
+ wrapper.className = wrapper.className.replace(/\s*CodeMirror-sided\s*/g, " ");
+ } else {
+ // When the preview button is clicked for the first time,
+ // give some time for the transition from editor.css to fire and the view to slide from right to left,
+ // instead of just appearing.
+ setTimeout(function() {
+ if(!cm.getOption("fullScreen"))
+ toggleFullScreen(editor);
+ preview.className += " editor-preview-active-side";
+ }, 1);
+ toolbarButton.className += " active";
+ wrapper.className += " CodeMirror-sided";
+ useSideBySideListener = true;
+ }
+
+ // Hide normal preview if active
+ var previewNormal = wrapper.lastChild;
+ if(/editor-preview-active/.test(previewNormal.className)) {
+ previewNormal.className = previewNormal.className.replace(
+ /\s*editor-preview-active\s*/g, ""
+ );
+ var toolbar = editor.toolbarElements.preview;
+ var toolbar_div = wrapper.previousSibling;
+ toolbar.className = toolbar.className.replace(/\s*active\s*/g, "");
+ toolbar_div.className = toolbar_div.className.replace(/\s*disabled-for-preview*/g, "");
+ }
+
+ var sideBySideRenderingFunction = function() {
+ preview.innerHTML = editor.options.previewRender(editor.value(), preview);
+ };
+
+ if(!cm.sideBySideRenderingFunction) {
+ cm.sideBySideRenderingFunction = sideBySideRenderingFunction;
+ }
+
+ if(useSideBySideListener) {
+ preview.innerHTML = editor.options.previewRender(editor.value(), preview);
+ cm.on("update", cm.sideBySideRenderingFunction);
+ } else {
+ cm.off("update", cm.sideBySideRenderingFunction);
+ }
+
+ // Refresh to fix selection being off (#309)
+ cm.refresh();
+}
+
+
+/**
+ * Preview action.
+ */
+function togglePreview(editor) {
+ var cm = editor.codemirror;
+ var wrapper = cm.getWrapperElement();
+ var toolbar_div = wrapper.previousSibling;
+ var toolbar = editor.options.toolbar ? editor.toolbarElements.preview : false;
+ var preview = wrapper.lastChild;
+ if(!preview || !/editor-preview/.test(preview.className)) {
+ preview = document.createElement("div");
+ preview.className = "editor-preview";
+ wrapper.appendChild(preview);
+ }
+ if(/editor-preview-active/.test(preview.className)) {
+ preview.className = preview.className.replace(
+ /\s*editor-preview-active\s*/g, ""
+ );
+ if(toolbar) {
+ toolbar.className = toolbar.className.replace(/\s*active\s*/g, "");
+ toolbar_div.className = toolbar_div.className.replace(/\s*disabled-for-preview*/g, "");
+ }
+ } else {
+ // When the preview button is clicked for the first time,
+ // give some time for the transition from editor.css to fire and the view to slide from right to left,
+ // instead of just appearing.
+ setTimeout(function() {
+ preview.className += " editor-preview-active";
+ }, 1);
+ if(toolbar) {
+ toolbar.className += " active";
+ toolbar_div.className += " disabled-for-preview";
+ }
+ }
+ preview.innerHTML = editor.options.previewRender(editor.value(), preview);
+
+ // Turn off side by side if needed
+ var sidebyside = cm.getWrapperElement().nextSibling;
+ if(/editor-preview-active-side/.test(sidebyside.className))
+ toggleSideBySide(editor);
+}
+
+function _replaceSelection(cm, active, startEnd, url) {
+ if(/editor-preview-active/.test(cm.getWrapperElement().lastChild.className))
+ return;
+
+ var text;
+ var start = startEnd[0];
+ var end = startEnd[1];
+ var startPoint = cm.getCursor("start");
+ var endPoint = cm.getCursor("end");
+ if(url) {
+ end = end.replace("#url#", url);
+ }
+ if(active) {
+ text = cm.getLine(startPoint.line);
+ start = text.slice(0, startPoint.ch);
+ end = text.slice(startPoint.ch);
+ cm.replaceRange(start + end, {
+ line: startPoint.line,
+ ch: 0
+ });
+ } else {
+ text = cm.getSelection();
+ cm.replaceSelection(start + text + end);
+
+ startPoint.ch += start.length;
+ if(startPoint !== endPoint) {
+ endPoint.ch += start.length;
+ }
+ }
+ cm.setSelection(startPoint, endPoint);
+ cm.focus();
+}
+
+
+function _toggleHeading(cm, direction, size) {
+ if(/editor-preview-active/.test(cm.getWrapperElement().lastChild.className))
+ return;
+
+ var startPoint = cm.getCursor("start");
+ var endPoint = cm.getCursor("end");
+ for(var i = startPoint.line; i <= endPoint.line; i++) {
+ (function(i) {
+ var text = cm.getLine(i);
+ var currHeadingLevel = text.search(/[^#]/);
+
+ if(direction !== undefined) {
+ if(currHeadingLevel <= 0) {
+ if(direction == "bigger") {
+ text = "###### " + text;
+ } else {
+ text = "# " + text;
+ }
+ } else if(currHeadingLevel == 6 && direction == "smaller") {
+ text = text.substr(7);
+ } else if(currHeadingLevel == 1 && direction == "bigger") {
+ text = text.substr(2);
+ } else {
+ if(direction == "bigger") {
+ text = text.substr(1);
+ } else {
+ text = "#" + text;
+ }
+ }
+ } else {
+ if(size == 1) {
+ if(currHeadingLevel <= 0) {
+ text = "# " + text;
+ } else if(currHeadingLevel == size) {
+ text = text.substr(currHeadingLevel + 1);
+ } else {
+ text = "# " + text.substr(currHeadingLevel + 1);
+ }
+ } else if(size == 2) {
+ if(currHeadingLevel <= 0) {
+ text = "## " + text;
+ } else if(currHeadingLevel == size) {
+ text = text.substr(currHeadingLevel + 1);
+ } else {
+ text = "## " + text.substr(currHeadingLevel + 1);
+ }
+ } else {
+ if(currHeadingLevel <= 0) {
+ text = "### " + text;
+ } else if(currHeadingLevel == size) {
+ text = text.substr(currHeadingLevel + 1);
+ } else {
+ text = "### " + text.substr(currHeadingLevel + 1);
+ }
+ }
+ }
+
+ cm.replaceRange(text, {
+ line: i,
+ ch: 0
+ }, {
+ line: i,
+ ch: 99999999999999
+ });
+ })(i);
+ }
+ cm.focus();
+}
+
+
+function _toggleLine(cm, name) {
+ if(/editor-preview-active/.test(cm.getWrapperElement().lastChild.className))
+ return;
+
+ var stat = getState(cm);
+ var startPoint = cm.getCursor("start");
+ var endPoint = cm.getCursor("end");
+ var repl = {
+ "quote": /^(\s*)\>\s+/,
+ "unordered-list": /^(\s*)(\*|\-|\+)\s+/,
+ "ordered-list": /^(\s*)\d+\.\s+/
+ };
+ var map = {
+ "quote": "> ",
+ "unordered-list": "* ",
+ "ordered-list": "1. "
+ };
+ for(var i = startPoint.line; i <= endPoint.line; i++) {
+ (function(i) {
+ var text = cm.getLine(i);
+ if(stat[name]) {
+ text = text.replace(repl[name], "$1");
+ } else {
+ text = map[name] + text;
+ }
+ cm.replaceRange(text, {
+ line: i,
+ ch: 0
+ }, {
+ line: i,
+ ch: 99999999999999
+ });
+ })(i);
+ }
+ cm.focus();
+}
+
+function _toggleBlock(editor, type, start_chars, end_chars) {
+ if(/editor-preview-active/.test(editor.codemirror.getWrapperElement().lastChild.className))
+ return;
+
+ end_chars = (typeof end_chars === "undefined") ? start_chars : end_chars;
+ var cm = editor.codemirror;
+ var stat = getState(cm);
+
+ var text;
+ var start = start_chars;
+ var end = end_chars;
+
+ var startPoint = cm.getCursor("start");
+ var endPoint = cm.getCursor("end");
+
+ if(stat[type]) {
+ text = cm.getLine(startPoint.line);
+ start = text.slice(0, startPoint.ch);
+ end = text.slice(startPoint.ch);
+ if(type == "bold") {
+ start = start.replace(/(\*\*|__)(?![\s\S]*(\*\*|__))/, "");
+ end = end.replace(/(\*\*|__)/, "");
+ } else if(type == "italic") {
+ start = start.replace(/(\*|_)(?![\s\S]*(\*|_))/, "");
+ end = end.replace(/(\*|_)/, "");
+ } else if(type == "strikethrough") {
+ start = start.replace(/(\*\*|~~)(?![\s\S]*(\*\*|~~))/, "");
+ end = end.replace(/(\*\*|~~)/, "");
+ }
+ cm.replaceRange(start + end, {
+ line: startPoint.line,
+ ch: 0
+ }, {
+ line: startPoint.line,
+ ch: 99999999999999
+ });
+
+ if(type == "bold" || type == "strikethrough") {
+ startPoint.ch -= 2;
+ if(startPoint !== endPoint) {
+ endPoint.ch -= 2;
+ }
+ } else if(type == "italic") {
+ startPoint.ch -= 1;
+ if(startPoint !== endPoint) {
+ endPoint.ch -= 1;
+ }
+ }
+ } else {
+ text = cm.getSelection();
+ if(type == "bold") {
+ text = text.split("**").join("");
+ text = text.split("__").join("");
+ } else if(type == "italic") {
+ text = text.split("*").join("");
+ text = text.split("_").join("");
+ } else if(type == "strikethrough") {
+ text = text.split("~~").join("");
+ }
+ cm.replaceSelection(start + text + end);
+
+ startPoint.ch += start_chars.length;
+ endPoint.ch = startPoint.ch + text.length;
+ }
+
+ cm.setSelection(startPoint, endPoint);
+ cm.focus();
+}
+
+function _cleanBlock(cm) {
+ if(/editor-preview-active/.test(cm.getWrapperElement().lastChild.className))
+ return;
+
+ var startPoint = cm.getCursor("start");
+ var endPoint = cm.getCursor("end");
+ var text;
+
+ for(var line = startPoint.line; line <= endPoint.line; line++) {
+ text = cm.getLine(line);
+ text = text.replace(/^[ ]*([# ]+|\*|\-|[> ]+|[0-9]+(.|\)))[ ]*/, "");
+
+ cm.replaceRange(text, {
+ line: line,
+ ch: 0
+ }, {
+ line: line,
+ ch: 99999999999999
+ });
+ }
+}
+
+// Merge the properties of one object into another.
+function _mergeProperties(target, source) {
+ for(var property in source) {
+ if(source.hasOwnProperty(property)) {
+ if(source[property] instanceof Array) {
+ target[property] = source[property].concat(target[property] instanceof Array ? target[property] : []);
+ } else if(
+ source[property] !== null &&
+ typeof source[property] === "object" &&
+ source[property].constructor === Object
+ ) {
+ target[property] = _mergeProperties(target[property] || {}, source[property]);
+ } else {
+ target[property] = source[property];
+ }
+ }
+ }
+
+ return target;
+}
+
+// Merge an arbitrary number of objects into one.
+function extend(target) {
+ for(var i = 1; i < arguments.length; i++) {
+ target = _mergeProperties(target, arguments[i]);
+ }
+
+ return target;
+}
+
+/* The right word count in respect for CJK. */
+function wordCount(data) {
+ var pattern = /[a-zA-Z0-9_\u0392-\u03c9\u0410-\u04F9]+|[\u4E00-\u9FFF\u3400-\u4dbf\uf900-\ufaff\u3040-\u309f\uac00-\ud7af]+/g;
+ var m = data.match(pattern);
+ var count = 0;
+ if(m === null) return count;
+ for(var i = 0; i < m.length; i++) {
+ if(m[i].charCodeAt(0) >= 0x4E00) {
+ count += m[i].length;
+ } else {
+ count += 1;
+ }
+ }
+ return count;
+}
+
+var toolbarBuiltInButtons = {
+ "bold": {
+ name: "bold",
+ action: toggleBold,
+ className: "fa fa-bold",
+ title: "Bold",
+ default: true
+ },
+ "italic": {
+ name: "italic",
+ action: toggleItalic,
+ className: "fa fa-italic",
+ title: "Italic",
+ default: true
+ },
+ "strikethrough": {
+ name: "strikethrough",
+ action: toggleStrikethrough,
+ className: "fa fa-strikethrough",
+ title: "Strikethrough"
+ },
+ "heading": {
+ name: "heading",
+ action: toggleHeadingSmaller,
+ className: "fa fa-header",
+ title: "Heading",
+ default: true
+ },
+ "heading-smaller": {
+ name: "heading-smaller",
+ action: toggleHeadingSmaller,
+ className: "fa fa-header fa-header-x fa-header-smaller",
+ title: "Smaller Heading"
+ },
+ "heading-bigger": {
+ name: "heading-bigger",
+ action: toggleHeadingBigger,
+ className: "fa fa-header fa-header-x fa-header-bigger",
+ title: "Bigger Heading"
+ },
+ "heading-1": {
+ name: "heading-1",
+ action: toggleHeading1,
+ className: "fa fa-header fa-header-x fa-header-1",
+ title: "Big Heading"
+ },
+ "heading-2": {
+ name: "heading-2",
+ action: toggleHeading2,
+ className: "fa fa-header fa-header-x fa-header-2",
+ title: "Medium Heading"
+ },
+ "heading-3": {
+ name: "heading-3",
+ action: toggleHeading3,
+ className: "fa fa-header fa-header-x fa-header-3",
+ title: "Small Heading"
+ },
+ "separator-1": {
+ name: "separator-1"
+ },
+ "code": {
+ name: "code",
+ action: toggleCodeBlock,
+ className: "fa fa-code",
+ title: "Code"
+ },
+ "quote": {
+ name: "quote",
+ action: toggleBlockquote,
+ className: "fa fa-quote-left",
+ title: "Quote",
+ default: true
+ },
+ "unordered-list": {
+ name: "unordered-list",
+ action: toggleUnorderedList,
+ className: "fa fa-list-ul",
+ title: "Generic List",
+ default: true
+ },
+ "ordered-list": {
+ name: "ordered-list",
+ action: toggleOrderedList,
+ className: "fa fa-list-ol",
+ title: "Numbered List",
+ default: true
+ },
+ "clean-block": {
+ name: "clean-block",
+ action: cleanBlock,
+ className: "fa fa-eraser fa-clean-block",
+ title: "Clean block"
+ },
+ "separator-2": {
+ name: "separator-2"
+ },
+ "link": {
+ name: "link",
+ action: drawLink,
+ className: "fa fa-link",
+ title: "Create Link",
+ default: true
+ },
+ "image": {
+ name: "image",
+ action: drawImage,
+ className: "fa fa-picture-o",
+ title: "Insert Image",
+ default: true
+ },
+ "table": {
+ name: "table",
+ action: drawTable,
+ className: "fa fa-table",
+ title: "Insert Table"
+ },
+ "horizontal-rule": {
+ name: "horizontal-rule",
+ action: drawHorizontalRule,
+ className: "fa fa-minus",
+ title: "Insert Horizontal Line"
+ },
+ "separator-3": {
+ name: "separator-3"
+ },
+ "preview": {
+ name: "preview",
+ action: togglePreview,
+ className: "fa fa-eye no-disable",
+ title: "Toggle Preview",
+ default: true
+ },
+ "side-by-side": {
+ name: "side-by-side",
+ action: toggleSideBySide,
+ className: "fa fa-columns no-disable no-mobile",
+ title: "Toggle Side by Side",
+ default: true
+ },
+ "fullscreen": {
+ name: "fullscreen",
+ action: toggleFullScreen,
+ className: "fa fa-arrows-alt no-disable no-mobile",
+ title: "Toggle Fullscreen",
+ default: true
+ },
+ "separator-4": {
+ name: "separator-4"
+ },
+ "guide": {
+ name: "guide",
+ action: "https://simplemde.com/markdown-guide",
+ className: "fa fa-question-circle",
+ title: "Markdown Guide",
+ default: true
+ },
+ "separator-5": {
+ name: "separator-5"
+ },
+ "undo": {
+ name: "undo",
+ action: undo,
+ className: "fa fa-undo no-disable",
+ title: "Undo"
+ },
+ "redo": {
+ name: "redo",
+ action: redo,
+ className: "fa fa-repeat no-disable",
+ title: "Redo"
+ }
+};
+
+var insertTexts = {
+ link: ["[", "](#url#)"],
+ image: ["![](", "#url#)"],
+ table: ["", "\n\n| Column 1 | Column 2 | Column 3 |\n| -------- | -------- | -------- |\n| Text | Text | Text |\n\n"],
+ horizontalRule: ["", "\n\n-----\n\n"]
+};
+
+var promptTexts = {
+ link: "URL for the link:",
+ image: "URL of the image:"
+};
+
+var blockStyles = {
+ "bold": "**",
+ "code": "```",
+ "italic": "*"
+};
+
+/**
+ * Interface of SimpleMDE.
+ */
+function SimpleMDE(options) {
+ // Handle options parameter
+ options = options || {};
+
+
+ // Used later to refer to it"s parent
+ options.parent = this;
+
+
+ // Check if Font Awesome needs to be auto downloaded
+ var autoDownloadFA = true;
+
+ if(options.autoDownloadFontAwesome === false) {
+ autoDownloadFA = false;
+ }
+
+ if(options.autoDownloadFontAwesome !== true) {
+ var styleSheets = document.styleSheets;
+ for(var i = 0; i < styleSheets.length; i++) {
+ if(!styleSheets[i].href)
+ continue;
+
+ if(styleSheets[i].href.indexOf("//maxcdn.bootstrapcdn.com/font-awesome/") > -1) {
+ autoDownloadFA = false;
+ }
+ }
+ }
+
+ if(autoDownloadFA) {
+ var link = document.createElement("link");
+ link.rel = "stylesheet";
+ link.href = "https://maxcdn.bootstrapcdn.com/font-awesome/latest/css/font-awesome.min.css";
+ document.getElementsByTagName("head")[0].appendChild(link);
+ }
+
+
+ // Find the textarea to use
+ if(options.element) {
+ this.element = options.element;
+ } else if(options.element === null) {
+ // This means that the element option was specified, but no element was found
+ console.log("SimpleMDE: Error. No element was found.");
+ return;
+ }
+
+
+ // Handle toolbar
+ if(options.toolbar === undefined) {
+ // Initialize
+ options.toolbar = [];
+
+
+ // Loop over the built in buttons, to get the preferred order
+ for(var key in toolbarBuiltInButtons) {
+ if(toolbarBuiltInButtons.hasOwnProperty(key)) {
+ if(key.indexOf("separator-") != -1) {
+ options.toolbar.push("|");
+ }
+
+ if(toolbarBuiltInButtons[key].default === true || (options.showIcons && options.showIcons.constructor === Array && options.showIcons.indexOf(key) != -1)) {
+ options.toolbar.push(key);
+ }
+ }
+ }
+ }
+
+
+ // Handle status bar
+ if(!options.hasOwnProperty("status")) {
+ options.status = ["autosave", "lines", "words", "cursor"];
+ }
+
+
+ // Add default preview rendering function
+ if(!options.previewRender) {
+ options.previewRender = function(plainText) {
+ // Note: "this" refers to the options object
+ return this.parent.markdown(plainText);
+ };
+ }
+
+
+ // Set default options for parsing config
+ options.parsingConfig = extend({
+ highlightFormatting: true // needed for toggleCodeBlock to detect types of code
+ }, options.parsingConfig || {});
+
+
+ // Merging the insertTexts, with the given options
+ options.insertTexts = extend({}, insertTexts, options.insertTexts || {});
+
+
+ // Merging the promptTexts, with the given options
+ options.promptTexts = promptTexts;
+
+
+ // Merging the blockStyles, with the given options
+ options.blockStyles = extend({}, blockStyles, options.blockStyles || {});
+
+
+ // Merging the shortcuts, with the given options
+ options.shortcuts = extend({}, shortcuts, options.shortcuts || {});
+
+
+ // Change unique_id to uniqueId for backwards compatibility
+ if(options.autosave != undefined && options.autosave.unique_id != undefined && options.autosave.unique_id != "")
+ options.autosave.uniqueId = options.autosave.unique_id;
+
+
+ // Update this options
+ this.options = options;
+
+
+ // Auto render
+ this.render();
+
+
+ // The codemirror component is only available after rendering
+ // so, the setter for the initialValue can only run after
+ // the element has been rendered
+ if(options.initialValue && (!this.options.autosave || this.options.autosave.foundSavedValue !== true)) {
+ this.value(options.initialValue);
+ }
+}
+
+/**
+ * Default markdown render.
+ */
+SimpleMDE.prototype.markdown = function(text) {
+ if(marked) {
+ // Initialize
+ var markedOptions = {};
+
+
+ // Update options
+ if(this.options && this.options.renderingConfig && this.options.renderingConfig.singleLineBreaks === false) {
+ markedOptions.breaks = false;
+ } else {
+ markedOptions.breaks = true;
+ }
+
+ if(this.options && this.options.renderingConfig && this.options.renderingConfig.codeSyntaxHighlighting === true && window.hljs) {
+ markedOptions.highlight = function(code) {
+ return window.hljs.highlightAuto(code).value;
+ };
+ }
+
+
+ // Set options
+ marked.setOptions(markedOptions);
+
+
+ // Return
+ return marked(text);
+ }
+};
+
+/**
+ * Render editor to the given element.
+ */
+SimpleMDE.prototype.render = function(el) {
+ if(!el) {
+ el = this.element || document.getElementsByTagName("textarea")[0];
+ }
+
+ if(this._rendered && this._rendered === el) {
+ // Already rendered.
+ return;
+ }
+
+ this.element = el;
+ var options = this.options;
+
+ var self = this;
+ var keyMaps = {};
+
+ for(var key in options.shortcuts) {
+ // null stands for "do not bind this command"
+ if(options.shortcuts[key] !== null && bindings[key] !== null) {
+ (function(key) {
+ keyMaps[fixShortcut(options.shortcuts[key])] = function() {
+ bindings[key](self);
+ };
+ })(key);
+ }
+ }
+
+ keyMaps["Enter"] = "newlineAndIndentContinueMarkdownList";
+ keyMaps["Tab"] = "tabAndIndentMarkdownList";
+ keyMaps["Shift-Tab"] = "shiftTabAndUnindentMarkdownList";
+ keyMaps["Esc"] = function(cm) {
+ if(cm.getOption("fullScreen")) toggleFullScreen(self);
+ };
+
+ document.addEventListener("keydown", function(e) {
+ e = e || window.event;
+
+ if(e.keyCode == 27) {
+ if(self.codemirror.getOption("fullScreen")) toggleFullScreen(self);
+ }
+ }, false);
+
+ var mode, backdrop;
+ if(options.spellChecker !== false) {
+ mode = "spell-checker";
+ backdrop = options.parsingConfig;
+ backdrop.name = "gfm";
+ backdrop.gitHubSpice = false;
+
+ CodeMirrorSpellChecker({
+ codeMirrorInstance: CodeMirror
+ });
+ } else {
+ mode = options.parsingConfig;
+ mode.name = "gfm";
+ mode.gitHubSpice = false;
+ }
+
+ this.codemirror = CodeMirror.fromTextArea(el, {
+ mode: mode,
+ backdrop: backdrop,
+ theme: "paper",
+ tabSize: (options.tabSize != undefined) ? options.tabSize : 2,
+ indentUnit: (options.tabSize != undefined) ? options.tabSize : 2,
+ indentWithTabs: (options.indentWithTabs === false) ? false : true,
+ lineNumbers: false,
+ autofocus: (options.autofocus === true) ? true : false,
+ extraKeys: keyMaps,
+ lineWrapping: (options.lineWrapping === false) ? false : true,
+ allowDropFileTypes: ["text/plain"],
+ placeholder: options.placeholder || el.getAttribute("placeholder") || "",
+ styleSelectedText: (options.styleSelectedText != undefined) ? options.styleSelectedText : true
+ });
+
+ if(options.forceSync === true) {
+ var cm = this.codemirror;
+ cm.on("change", function() {
+ cm.save();
+ });
+ }
+
+ this.gui = {};
+
+ if(options.toolbar !== false) {
+ this.gui.toolbar = this.createToolbar();
+ }
+ if(options.status !== false) {
+ this.gui.statusbar = this.createStatusbar();
+ }
+ if(options.autosave != undefined && options.autosave.enabled === true) {
+ this.autosave();
+ }
+
+ this.gui.sideBySide = this.createSideBySide();
+
+ this._rendered = this.element;
+
+
+ // Fixes CodeMirror bug (#344)
+ var temp_cm = this.codemirror;
+ setTimeout(function() {
+ temp_cm.refresh();
+ }.bind(temp_cm), 0);
+};
+
+// Safari, in Private Browsing Mode, looks like it supports localStorage but all calls to setItem throw QuotaExceededError. We're going to detect this and set a variable accordingly.
+function isLocalStorageAvailable() {
+ if(typeof localStorage === "object") {
+ try {
+ localStorage.setItem("smde_localStorage", 1);
+ localStorage.removeItem("smde_localStorage");
+ } catch(e) {
+ return false;
+ }
+ } else {
+ return false;
+ }
+
+ return true;
+}
+
+SimpleMDE.prototype.autosave = function() {
+ if(isLocalStorageAvailable()) {
+ var simplemde = this;
+
+ if(this.options.autosave.uniqueId == undefined || this.options.autosave.uniqueId == "") {
+ console.log("SimpleMDE: You must set a uniqueId to use the autosave feature");
+ return;
+ }
+
+ if(simplemde.element.form != null && simplemde.element.form != undefined) {
+ simplemde.element.form.addEventListener("submit", function() {
+ localStorage.removeItem("smde_" + simplemde.options.autosave.uniqueId);
+ });
+ }
+
+ if(this.options.autosave.loaded !== true) {
+ if(typeof localStorage.getItem("smde_" + this.options.autosave.uniqueId) == "string" && localStorage.getItem("smde_" + this.options.autosave.uniqueId) != "") {
+ this.codemirror.setValue(localStorage.getItem("smde_" + this.options.autosave.uniqueId));
+ this.options.autosave.foundSavedValue = true;
+ }
+
+ this.options.autosave.loaded = true;
+ }
+
+ localStorage.setItem("smde_" + this.options.autosave.uniqueId, simplemde.value());
+
+ var el = document.getElementById("autosaved");
+ if(el != null && el != undefined && el != "") {
+ var d = new Date();
+ var hh = d.getHours();
+ var m = d.getMinutes();
+ var dd = "am";
+ var h = hh;
+ if(h >= 12) {
+ h = hh - 12;
+ dd = "pm";
+ }
+ if(h == 0) {
+ h = 12;
+ }
+ m = m < 10 ? "0" + m : m;
+
+ el.innerHTML = "Autosaved: " + h + ":" + m + " " + dd;
+ }
+
+ this.autosaveTimeoutId = setTimeout(function() {
+ simplemde.autosave();
+ }, this.options.autosave.delay || 10000);
+ } else {
+ console.log("SimpleMDE: localStorage not available, cannot autosave");
+ }
+};
+
+SimpleMDE.prototype.clearAutosavedValue = function() {
+ if(isLocalStorageAvailable()) {
+ if(this.options.autosave == undefined || this.options.autosave.uniqueId == undefined || this.options.autosave.uniqueId == "") {
+ console.log("SimpleMDE: You must set a uniqueId to clear the autosave value");
+ return;
+ }
+
+ localStorage.removeItem("smde_" + this.options.autosave.uniqueId);
+ } else {
+ console.log("SimpleMDE: localStorage not available, cannot autosave");
+ }
+};
+
+SimpleMDE.prototype.createSideBySide = function() {
+ var cm = this.codemirror;
+ var wrapper = cm.getWrapperElement();
+ var preview = wrapper.nextSibling;
+
+ if(!preview || !/editor-preview-side/.test(preview.className)) {
+ preview = document.createElement("div");
+ preview.className = "editor-preview-side";
+ wrapper.parentNode.insertBefore(preview, wrapper.nextSibling);
+ }
+
+ // Syncs scroll editor -> preview
+ var cScroll = false;
+ var pScroll = false;
+ cm.on("scroll", function(v) {
+ if(cScroll) {
+ cScroll = false;
+ return;
+ }
+ pScroll = true;
+ var height = v.getScrollInfo().height - v.getScrollInfo().clientHeight;
+ var ratio = parseFloat(v.getScrollInfo().top) / height;
+ var move = (preview.scrollHeight - preview.clientHeight) * ratio;
+ preview.scrollTop = move;
+ });
+
+ // Syncs scroll preview -> editor
+ preview.onscroll = function() {
+ if(pScroll) {
+ pScroll = false;
+ return;
+ }
+ cScroll = true;
+ var height = preview.scrollHeight - preview.clientHeight;
+ var ratio = parseFloat(preview.scrollTop) / height;
+ var move = (cm.getScrollInfo().height - cm.getScrollInfo().clientHeight) * ratio;
+ cm.scrollTo(0, move);
+ };
+ return preview;
+};
+
+SimpleMDE.prototype.createToolbar = function(items) {
+ items = items || this.options.toolbar;
+
+ if(!items || items.length === 0) {
+ return;
+ }
+ var i;
+ for(i = 0; i < items.length; i++) {
+ if(toolbarBuiltInButtons[items[i]] != undefined) {
+ items[i] = toolbarBuiltInButtons[items[i]];
+ }
+ }
+
+ var bar = document.createElement("div");
+ bar.className = "editor-toolbar";
+
+ var self = this;
+
+ var toolbarData = {};
+ self.toolbar = items;
+
+ for(i = 0; i < items.length; i++) {
+ if(items[i].name == "guide" && self.options.toolbarGuideIcon === false)
+ continue;
+
+ if(self.options.hideIcons && self.options.hideIcons.indexOf(items[i].name) != -1)
+ continue;
+
+ // Fullscreen does not work well on mobile devices (even tablets)
+ // In the future, hopefully this can be resolved
+ if((items[i].name == "fullscreen" || items[i].name == "side-by-side") && isMobile())
+ continue;
+
+
+ // Don't include trailing separators
+ if(items[i] === "|") {
+ var nonSeparatorIconsFollow = false;
+
+ for(var x = (i + 1); x < items.length; x++) {
+ if(items[x] !== "|" && (!self.options.hideIcons || self.options.hideIcons.indexOf(items[x].name) == -1)) {
+ nonSeparatorIconsFollow = true;
+ }
+ }
+
+ if(!nonSeparatorIconsFollow)
+ continue;
+ }
+
+
+ // Create the icon and append to the toolbar
+ (function(item) {
+ var el;
+ if(item === "|") {
+ el = createSep();
+ } else {
+ el = createIcon(item, self.options.toolbarTips, self.options.shortcuts);
+ }
+
+ // bind events, special for info
+ if(item.action) {
+ if(typeof item.action === "function") {
+ el.onclick = function(e) {
+ e.preventDefault();
+ item.action(self);
+ };
+ } else if(typeof item.action === "string") {
+ el.href = item.action;
+ el.target = "_blank";
+ }
+ }
+
+ toolbarData[item.name || item] = el;
+ bar.appendChild(el);
+ })(items[i]);
+ }
+
+ self.toolbarElements = toolbarData;
+
+ var cm = this.codemirror;
+ cm.on("cursorActivity", function() {
+ var stat = getState(cm);
+
+ for(var key in toolbarData) {
+ (function(key) {
+ var el = toolbarData[key];
+ if(stat[key]) {
+ el.className += " active";
+ } else if(key != "fullscreen" && key != "side-by-side") {
+ el.className = el.className.replace(/\s*active\s*/g, "");
+ }
+ })(key);
+ }
+ });
+
+ var cmWrapper = cm.getWrapperElement();
+ cmWrapper.parentNode.insertBefore(bar, cmWrapper);
+ return bar;
+};
+
+SimpleMDE.prototype.createStatusbar = function(status) {
+ // Initialize
+ status = status || this.options.status;
+ var options = this.options;
+ var cm = this.codemirror;
+
+
+ // Make sure the status variable is valid
+ if(!status || status.length === 0)
+ return;
+
+
+ // Set up the built-in items
+ var items = [];
+ var i, onUpdate, defaultValue;
+
+ for(i = 0; i < status.length; i++) {
+ // Reset some values
+ onUpdate = undefined;
+ defaultValue = undefined;
+
+
+ // Handle if custom or not
+ if(typeof status[i] === "object") {
+ items.push({
+ className: status[i].className,
+ defaultValue: status[i].defaultValue,
+ onUpdate: status[i].onUpdate
+ });
+ } else {
+ var name = status[i];
+
+ if(name === "words") {
+ defaultValue = function(el) {
+ el.innerHTML = wordCount(cm.getValue());
+ };
+ onUpdate = function(el) {
+ el.innerHTML = wordCount(cm.getValue());
+ };
+ } else if(name === "lines") {
+ defaultValue = function(el) {
+ el.innerHTML = cm.lineCount();
+ };
+ onUpdate = function(el) {
+ el.innerHTML = cm.lineCount();
+ };
+ } else if(name === "cursor") {
+ defaultValue = function(el) {
+ el.innerHTML = "0:0";
+ };
+ onUpdate = function(el) {
+ var pos = cm.getCursor();
+ el.innerHTML = pos.line + ":" + pos.ch;
+ };
+ } else if(name === "autosave") {
+ defaultValue = function(el) {
+ if(options.autosave != undefined && options.autosave.enabled === true) {
+ el.setAttribute("id", "autosaved");
+ }
+ };
+ }
+
+ items.push({
+ className: name,
+ defaultValue: defaultValue,
+ onUpdate: onUpdate
+ });
+ }
+ }
+
+
+ // Create element for the status bar
+ var bar = document.createElement("div");
+ bar.className = "editor-statusbar";
+
+
+ // Create a new span for each item
+ for(i = 0; i < items.length; i++) {
+ // Store in temporary variable
+ var item = items[i];
+
+
+ // Create span element
+ var el = document.createElement("span");
+ el.className = item.className;
+
+
+ // Ensure the defaultValue is a function
+ if(typeof item.defaultValue === "function") {
+ item.defaultValue(el);
+ }
+
+
+ // Ensure the onUpdate is a function
+ if(typeof item.onUpdate === "function") {
+ // Create a closure around the span of the current action, then execute the onUpdate handler
+ this.codemirror.on("update", (function(el, item) {
+ return function() {
+ item.onUpdate(el);
+ };
+ }(el, item)));
+ }
+
+
+ // Append the item to the status bar
+ bar.appendChild(el);
+ }
+
+
+ // Insert the status bar into the DOM
+ var cmWrapper = this.codemirror.getWrapperElement();
+ cmWrapper.parentNode.insertBefore(bar, cmWrapper.nextSibling);
+ return bar;
+};
+
+/**
+ * Get or set the text content.
+ */
+SimpleMDE.prototype.value = function(val) {
+ if(val === undefined) {
+ return this.codemirror.getValue();
+ } else {
+ this.codemirror.getDoc().setValue(val);
+ return this;
+ }
+};
+
+
+/**
+ * Bind static methods for exports.
+ */
+SimpleMDE.toggleBold = toggleBold;
+SimpleMDE.toggleItalic = toggleItalic;
+SimpleMDE.toggleStrikethrough = toggleStrikethrough;
+SimpleMDE.toggleBlockquote = toggleBlockquote;
+SimpleMDE.toggleHeadingSmaller = toggleHeadingSmaller;
+SimpleMDE.toggleHeadingBigger = toggleHeadingBigger;
+SimpleMDE.toggleHeading1 = toggleHeading1;
+SimpleMDE.toggleHeading2 = toggleHeading2;
+SimpleMDE.toggleHeading3 = toggleHeading3;
+SimpleMDE.toggleCodeBlock = toggleCodeBlock;
+SimpleMDE.toggleUnorderedList = toggleUnorderedList;
+SimpleMDE.toggleOrderedList = toggleOrderedList;
+SimpleMDE.cleanBlock = cleanBlock;
+SimpleMDE.drawLink = drawLink;
+SimpleMDE.drawImage = drawImage;
+SimpleMDE.drawTable = drawTable;
+SimpleMDE.drawHorizontalRule = drawHorizontalRule;
+SimpleMDE.undo = undo;
+SimpleMDE.redo = redo;
+SimpleMDE.togglePreview = togglePreview;
+SimpleMDE.toggleSideBySide = toggleSideBySide;
+SimpleMDE.toggleFullScreen = toggleFullScreen;
+
+/**
+ * Bind instance methods for exports.
+ */
+SimpleMDE.prototype.toggleBold = function() {
+ toggleBold(this);
+};
+SimpleMDE.prototype.toggleItalic = function() {
+ toggleItalic(this);
+};
+SimpleMDE.prototype.toggleStrikethrough = function() {
+ toggleStrikethrough(this);
+};
+SimpleMDE.prototype.toggleBlockquote = function() {
+ toggleBlockquote(this);
+};
+SimpleMDE.prototype.toggleHeadingSmaller = function() {
+ toggleHeadingSmaller(this);
+};
+SimpleMDE.prototype.toggleHeadingBigger = function() {
+ toggleHeadingBigger(this);
+};
+SimpleMDE.prototype.toggleHeading1 = function() {
+ toggleHeading1(this);
+};
+SimpleMDE.prototype.toggleHeading2 = function() {
+ toggleHeading2(this);
+};
+SimpleMDE.prototype.toggleHeading3 = function() {
+ toggleHeading3(this);
+};
+SimpleMDE.prototype.toggleCodeBlock = function() {
+ toggleCodeBlock(this);
+};
+SimpleMDE.prototype.toggleUnorderedList = function() {
+ toggleUnorderedList(this);
+};
+SimpleMDE.prototype.toggleOrderedList = function() {
+ toggleOrderedList(this);
+};
+SimpleMDE.prototype.cleanBlock = function() {
+ cleanBlock(this);
+};
+SimpleMDE.prototype.drawLink = function() {
+ drawLink(this);
+};
+SimpleMDE.prototype.drawImage = function() {
+ drawImage(this);
+};
+SimpleMDE.prototype.drawTable = function() {
+ drawTable(this);
+};
+SimpleMDE.prototype.drawHorizontalRule = function() {
+ drawHorizontalRule(this);
+};
+SimpleMDE.prototype.undo = function() {
+ undo(this);
+};
+SimpleMDE.prototype.redo = function() {
+ redo(this);
+};
+SimpleMDE.prototype.togglePreview = function() {
+ togglePreview(this);
+};
+SimpleMDE.prototype.toggleSideBySide = function() {
+ toggleSideBySide(this);
+};
+SimpleMDE.prototype.toggleFullScreen = function() {
+ toggleFullScreen(this);
+};
+
+SimpleMDE.prototype.isPreviewActive = function() {
+ var cm = this.codemirror;
+ var wrapper = cm.getWrapperElement();
+ var preview = wrapper.lastChild;
+
+ return /editor-preview-active/.test(preview.className);
+};
+
+SimpleMDE.prototype.isSideBySideActive = function() {
+ var cm = this.codemirror;
+ var wrapper = cm.getWrapperElement();
+ var preview = wrapper.nextSibling;
+
+ return /editor-preview-active-side/.test(preview.className);
+};
+
+SimpleMDE.prototype.isFullscreenActive = function() {
+ var cm = this.codemirror;
+
+ return cm.getOption("fullScreen");
+};
+
+SimpleMDE.prototype.getState = function() {
+ var cm = this.codemirror;
+
+ return getState(cm);
+};
+
+SimpleMDE.prototype.toTextArea = function() {
+ var cm = this.codemirror;
+ var wrapper = cm.getWrapperElement();
+
+ if(wrapper.parentNode) {
+ if(this.gui.toolbar) {
+ wrapper.parentNode.removeChild(this.gui.toolbar);
+ }
+ if(this.gui.statusbar) {
+ wrapper.parentNode.removeChild(this.gui.statusbar);
+ }
+ if(this.gui.sideBySide) {
+ wrapper.parentNode.removeChild(this.gui.sideBySide);
+ }
+ }
+
+ cm.toTextArea();
+
+ if(this.autosaveTimeoutId) {
+ clearTimeout(this.autosaveTimeoutId);
+ this.autosaveTimeoutId = undefined;
+ this.clearAutosavedValue();
+ }
+};
+
+module.exports = SimpleMDE; \ No newline at end of file