Merge pull request #65 from NextStepWebs/development

Toolbar icon improvements, Fullscreen improvements, Bug fixes
pull/69/head
Wes Cossick 9 years ago
commit 1d69c335c4

@ -1,10 +1,13 @@
# SimpleMDE - Markdown Editor
A drop-in JavaScript textarea replacement for writing beautiful and understandable markdown. The WYSIWYG-esque editor allows users to modify the markdown with toolbar buttons and shortcuts. WYSIWYG editors that produce HTML are often complex and buggy. Markdown solves this problem in many ways, but is less visually clear while editing. SimpleMDE has been designed to bridge this gap for non-technical users who are less familiar with or just learning markdown syntax.
A drop-in JavaScript textarea replacement for writing beautiful and understandable markdown. The WYSIWYG-esque editor allows users who may be less experienced with Markdown to use familiar toolbar buttons and shortcuts. In addition, the the syntax is rendered while editing to clearly show the expected result. Headings are larger, emphasized words are italicized, links are underlined, etc. SimpleMDE is one of the first editors to feature both built-in autosaving and spell checking.
[Demo](http://nextstepwebs.github.io/simplemde-markdown-editor)
[**Demo**](http://nextstepwebs.github.io/simplemde-markdown-editor)
[![Preview](http://i.imgur.com/b9hFHFT.png)](http://nextstepwebs.github.io/simplemde-markdown-editor)
## Why not a WYSIWYG editor or pure Markdown?
WYSIWYG editors that produce HTML are often complex and buggy. Markdown solves this problem in many ways, plus Markdown can be rendered natively on more platforms than HTML. However, Markdown is not a syntax that an average user will be familiar with, nor is it visually clear while editing. In otherwords, for an unfamiliar user, the syntax they write will make little sense until they click the preview button. SimpleMDE has been designed to bridge this gap for non-technical users who are less familiar with or just learning Markdown syntax.
## Quick start
SimpleMDE is available on [jsDelivr](http://www.jsdelivr.com/#!simplemde). Font Awesome is available on MaxCDN. *Please note, jsDelivr may take a few days to update to the latest release.*
@ -92,12 +95,15 @@ var simplemde = new SimpleMDE({
#### Toolbar icons
Below are the available toolbar icons, which can be reorganized however you like. "Name" is the name of the icon, referenced in the JS. "Action" is either a function or a URL to open. "Class" is the class given to the icon. "Tooltip" is the small tooltip that appears via the `title=""` attribute. The `Ctrl` and `Alt` in the title tags will be changed automatically to their Mac equivalents when needed. Additionally, you can add a separator between any icons by adding `"|"` to the toolbar array.
Below are the built-in toolbar icons, which can be reorganized however you like. "Name" is the name of the icon, referenced in the JS. "Action" is either a function or a URL to open. "Class" is the class given to the icon. "Tooltip" is the small tooltip that appears via the `title=""` attribute. The `Ctrl` and `Alt` in the title tags will be changed automatically to their Mac equivalents when needed. Additionally, you can add a separator between any icons by adding `"|"` to the toolbar array.
Name | Action | Class | Tooltip
:--- | :----- | :---- | :------
bold | toggleBold | fa fa-bold | Bold (Ctrl+B)
italic | toggleItalic | fa fa-italic | Italic (Ctrl+I)
heading | toggleHeadingSmaller | fa fa-header | Heading (Ctrl+H)
heading-smaller | toggleHeadingSmaller | fa fa-header | Smaller Heading (Ctrl+H)
heading-bigger | toggleHeadingBigger | fa fa-lg fa-header | Bigger Heading (Shift+Ctrl+H)
code | toggleCodeBlock | fa fa-code | Code (Ctrl+Alt+C)
quote | toggleBlockquote | fa fa-quote-left | Quote (Ctrl+')
unordered-list | toggleUnorderedList | fa fa-list-ul | Generic List (Ctrl+L)
@ -112,6 +118,12 @@ guide | [This link](http://nextstepwebs.github.io/simplemde-markdown-editor/mark
Customize the toolbar using the `toolbar` option like:
```JavaScript
// Customize only the order of existing buttons
var simplemde = new SimpleMDE({
toolbar: ["bold", "italic", "heading", "|", "quote"],
});
// Customize all information and/or add your own icons
var simplemde = new SimpleMDE({
toolbar: [{
name: "bold",
@ -156,7 +168,7 @@ simplemde.codemirror.on("change", function(){
## How it works
SimpleMDE is an improvement of [lepture's Editor project](https://github.com/lepture/editor) and includes a great many number of changes. It is bundled with [CodeMirror](https://github.com/codemirror/codemirror) and depends on [Font Awesome](http://fortawesome.github.io/Font-Awesome/).
CodeMirror is the backbone of the project and parses much of the markdown syntax as it's being written. This allows us to add styles to the markdown that's being written. Additionally, a toolbar and status bar have been added to the top and bottom, respectively. Previews are rendered by [Marked](https://github.com/chjj/marked).
CodeMirror is the backbone of the project and parses much of the Markdown syntax as it's being written. This allows us to add styles to the Markdown that's being written. Additionally, a toolbar and status bar have been added to the top and bottom, respectively. Previews are rendered by [Marked](https://github.com/chjj/marked).
## What's changed?
As mentioned earlier, SimpleMDE is an improvement of [lepture's Editor project](https://github.com/lepture/editor). So you might be wondering, what's changed? Quite a bit actually. Here's some notable changes:

File diff suppressed because one or more lines are too long

@ -1,6 +1,6 @@
{
"name": "simplemde",
"version": "1.5.1",
"version": "1.6.0",
"description": "A simple, beautiful, and embeddable JavaScript markdown editor. Features autosaving and spell checking.",
"keywords": ["embeddable", "markdown", "editor", "javascript", "wysiwyg"],
"homepage": "https://github.com/NextStepWebs/simplemde-markdown-editor",
@ -14,7 +14,7 @@
"bugs": {
"url": "https://github.com/NextStepWebs/simplemde-markdown-editor/issues"
},
"dependencies": {
"devDependencies": {
"gulp": "*",
"gulp-minify-css": "*",
"gulp-uglify": "*",

@ -59,6 +59,10 @@
.editor-toolbar.fullscreen {
width: 100%;
height: 50px;
overflow-x: auto;
overflow-y: hidden;
white-space: nowrap;
background: #fff;
border: 0;
position: fixed;

@ -2405,8 +2405,8 @@
doc.iter(doc.frontier, Math.min(doc.first + doc.size, cm.display.viewTo + 500), function(line) {
if (doc.frontier >= cm.display.viewFrom) { // Visible
var oldStyles = line.styles;
var highlighted = highlightLine(cm, line, state, true);
var oldStyles = line.styles, tooLong = line.text.length > cm.options.maxHighlightLength;
var highlighted = highlightLine(cm, line, tooLong ? copyState(doc.mode, state) : state, true);
line.styles = highlighted.styles;
var oldCls = line.styleClasses, newCls = highlighted.classes;
if (newCls) line.styleClasses = newCls;
@ -2415,9 +2415,10 @@
oldCls != newCls && (!oldCls || !newCls || oldCls.bgClass != newCls.bgClass || oldCls.textClass != newCls.textClass);
for (var i = 0; !ischange && i < oldStyles.length; ++i) ischange = oldStyles[i] != line.styles[i];
if (ischange) changedLines.push(doc.frontier);
line.stateAfter = copyState(doc.mode, state);
line.stateAfter = tooLong ? state : copyState(doc.mode, state);
} else {
processLine(cm, line.text, state);
if (line.text.length <= cm.options.maxHighlightLength)
processLine(cm, line.text, state);
line.stateAfter = doc.frontier % 5 == 0 ? copyState(doc.mode, state) : null;
}
++doc.frontier;
@ -3664,7 +3665,8 @@
setSelection(doc, normalizeSelection(ranges.concat([ourRange]), ourIndex),
{scroll: false, origin: "*mouse"});
} else if (ranges.length > 1 && ranges[ourIndex].empty() && type == "single" && !e.shiftKey) {
setSelection(doc, normalizeSelection(ranges.slice(0, ourIndex).concat(ranges.slice(ourIndex + 1)), 0));
setSelection(doc, normalizeSelection(ranges.slice(0, ourIndex).concat(ranges.slice(ourIndex + 1)), 0),
{scroll: false, origin: "*mouse"});
startSel = doc.sel;
} else {
replaceOneSelection(doc, ourIndex, ourRange, sel_mouse);
@ -6813,7 +6815,9 @@
function getLineStyles(cm, line, updateFrontier) {
if (!line.styles || line.styles[0] != cm.state.modeGen) {
var result = highlightLine(cm, line, line.stateAfter = getStateBefore(cm, lineNo(line)));
var state = getStateBefore(cm, lineNo(line));
var result = highlightLine(cm, line, line.text.length > cm.options.maxHighlightLength ? copyState(cm.doc.mode, state) : state);
line.stateAfter = state;
line.styles = result.styles;
if (result.classes) line.styleClasses = result.classes;
else if (line.styleClasses) line.styleClasses = null;
@ -6830,7 +6834,7 @@
var stream = new StringStream(text, cm.options.tabSize);
stream.start = stream.pos = startAt || 0;
if (text == "") callBlankLine(mode, state);
while (!stream.eol() && stream.pos <= cm.options.maxHighlightLength) {
while (!stream.eol()) {
readToken(mode, stream, state);
stream.start = stream.pos;
}
@ -6948,7 +6952,7 @@
txt.setAttribute("cm-text", "\t");
builder.col += tabWidth;
} else if (m[0] == "\r" || m[0] == "\n") {
var txt = content.appendChild(elt("span", m[0] == "\r" ? "␍" : "␤", "cm-invalidchar"));
var txt = content.appendChild(elt("span", m[0] == "\r" ? "\u240d" : "\u2424", "cm-invalidchar"));
txt.setAttribute("cm-text", m[0]);
builder.col += 1;
} else {
@ -8207,7 +8211,7 @@
// The inverse of countColumn -- find the offset that corresponds to
// a particular column.
function findColumn(string, goal, tabSize) {
var findColumn = CodeMirror.findColumn = function(string, goal, tabSize) {
for (var pos = 0, col = 0;;) {
var nextTab = string.indexOf("\t", pos);
if (nextTab == -1) nextTab = string.length;
@ -8812,7 +8816,7 @@
// THE END
CodeMirror.version = "5.5.1";
CodeMirror.version = "5.6.1";
return CodeMirror;
});

@ -1,6 +1,5 @@
// NOTE: This has been modified from the original version to add fullscreen class to the status bar too
// 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
@ -27,7 +26,6 @@
wrap.style.height = "auto";
wrap.className += " CodeMirror-fullscreen";
document.documentElement.style.overflow = "hidden";
wrap.previousSibling.className += " fullscreen";
cm.refresh();
}
@ -38,7 +36,6 @@
var info = cm.state.fullScreenRestore;
wrap.style.width = info.width; wrap.style.height = info.height;
window.scrollTo(info.scrollLeft, info.scrollTop);
wrap.previousSibling.className = wrap.previousSibling.className.replace(/\s*fullscreen\b/, "");
cm.refresh();
}
});

@ -4,6 +4,8 @@ var shortcuts = {
'Cmd-B': toggleBold,
'Cmd-I': toggleItalic,
'Cmd-K': drawLink,
'Cmd-H': toggleHeadingSmaller,
'Shift-Cmd-H': toggleHeadingBigger,
'Cmd-Alt-I': drawImage,
"Cmd-'": toggleBlockquote,
'Cmd-Alt-L': toggleOrderedList,
@ -94,9 +96,22 @@ function getState(cm, pos) {
* Toggle full screen of the editor.
*/
function toggleFullScreen(editor) {
// Set fullscreen
var cm = editor.codemirror;
cm.setOption("fullScreen", !cm.getOption("fullScreen"));
// 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)) {
@ -137,6 +152,21 @@ function toggleBlockquote(editor) {
_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');
}
/**
@ -269,6 +299,44 @@ function _replaceSelection(cm, active, start, end) {
}
function _toggleHeading(cm, direction) {
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 (currHeadingLevel <= 0) {
if (direction == 'bigger') {
text = '###### ' + text;
} else {
text = '# ' + text;
}
} else if ((currHeadingLevel == 6 && direction == 'smaller') || (currHeadingLevel == 1 && direction == 'bigger')) {
text = text.substr(7);
} else {
if (direction == 'bigger') {
text = text.substr(1);
} else {
text = '#' + text;
}
}
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;
@ -384,61 +452,100 @@ function wordCount(data) {
}
var toolbar = [{
var toolbarBuiltInButtons = {
"bold": {
name: "bold",
action: toggleBold,
className: "fa fa-bold",
title: "Bold (Ctrl+B)",
}, {
},
"italic": {
name: "italic",
action: toggleItalic,
className: "fa fa-italic",
title: "Italic (Ctrl+I)",
},
"|", {
"heading": {
name: "heading",
action: toggleHeadingSmaller,
className: "fa fa-header",
title: "Heading (Ctrl+H)",
},
"heading-smaller": {
name: "heading-smaller",
action: toggleHeadingSmaller,
className: "fa fa-header",
title: "Smaller Heading (Ctrl+H)",
},
"heading-bigger": {
name: "heading-bigger",
action: toggleHeadingBigger,
className: "fa fa-lg fa-header",
title: "Bigger Heading (Shift+Ctrl+H)",
},
"code": {
name: "code",
action: toggleCodeBlock,
className: "fa fa-code",
title: "Code (Ctrl+Alt+C)",
},
"quote": {
name: "quote",
action: toggleBlockquote,
className: "fa fa-quote-left",
title: "Quote (Ctrl+')",
}, {
},
"unordered-list": {
name: "unordered-list",
action: toggleUnorderedList,
className: "fa fa-list-ul",
title: "Generic List (Ctrl+L)",
}, {
},
"ordered-list": {
name: "ordered-list",
action: toggleOrderedList,
className: "fa fa-list-ol",
title: "Numbered List (Ctrl+Alt+L)",
},
"|", {
"link": {
name: "link",
action: drawLink,
className: "fa fa-link",
title: "Create Link (Ctrl+K)",
}, {
name: "quote",
},
"image": {
name: "image",
action: drawImage,
className: "fa fa-picture-o",
title: "Insert Image (Ctrl+Alt+I)",
},
"|", {
"horizontal-rule": {
name: "horizontal-rule",
action: drawHorizontalRule,
className: "fa fa-minus",
title: "Insert Horizontal Line",
},
"preview": {
name: "preview",
action: togglePreview,
className: "fa fa-eye",
title: "Toggle Preview (Ctrl+P)",
}, {
},
"fullscreen": {
name: "fullscreen",
action: toggleFullScreen,
className: "fa fa-arrows-alt",
title: "Toggle Fullscreen (F11)",
}, {
},
"guide": {
name: "guide",
action: "http://nextstepwebs.github.io/simplemde-markdown-editor/markdown-guide",
className: "fa fa-question-circle",
title: "Markdown Guide",
}
];
};
var toolbar = ["bold", "italic", "heading", "|", "quote", "unordered-list", "ordered-list", "|", "link", "image", "|", "preview", "fullscreen", "guide"];
/**
* Interface of SimpleMDE.
@ -612,6 +719,12 @@ SimpleMDE.prototype.createToolbar = function(items) {
if(!items || items.length === 0) {
return;
}
for(var 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';
@ -736,6 +849,8 @@ SimpleMDE.prototype.value = function(val) {
SimpleMDE.toggleBold = toggleBold;
SimpleMDE.toggleItalic = toggleItalic;
SimpleMDE.toggleBlockquote = toggleBlockquote;
SimpleMDE.toggleHeadingSmaller = toggleHeadingSmaller;
SimpleMDE.toggleHeadingBigger = toggleHeadingBigger;
SimpleMDE.toggleCodeBlock = toggleCodeBlock;
SimpleMDE.toggleUnorderedList = toggleUnorderedList;
SimpleMDE.toggleOrderedList = toggleOrderedList;
@ -759,6 +874,12 @@ SimpleMDE.prototype.toggleItalic = function() {
SimpleMDE.prototype.toggleBlockquote = function() {
toggleBlockquote(this);
};
SimpleMDE.prototype.toggleHeadingSmaller = function() {
toggleHeadingSmaller(this);
};
SimpleMDE.prototype.toggleHeadingBigger = function() {
toggleHeadingBigger(this);
};
SimpleMDE.prototype.toggleCodeBlock = function() {
toggleCodeBlock(this);
};
@ -788,4 +909,4 @@ SimpleMDE.prototype.togglePreview = function() {
};
SimpleMDE.prototype.toggleFullScreen = function() {
toggleFullScreen(this);
};
};

@ -1,43 +1,52 @@
// Initialize data globally to reduce memory consumption
var num_loaded = 0;
var aff_loading = false;
var dic_loading = false;
var aff_data = "";
var dic_data = "";
var typo;
CodeMirror.defineMode("spell-checker", function(config, parserConfig) {
// Initialize data
var num_loaded = 0;
var aff_data = "";
var dic_data = "";
var typo;
// Load AFF/DIC data
var xhr_aff = new XMLHttpRequest();
xhr_aff.open("GET", "https://cdn.jsdelivr.net/codemirror.spell-checker/latest/en_US.aff", true);
xhr_aff.onload = function (e) {
if (xhr_aff.readyState === 4 && xhr_aff.status === 200) {
aff_data = xhr_aff.responseText;
num_loaded++;
if(num_loaded == 2){
typo = new Typo("en_US", aff_data, dic_data, {
platform: 'any'
});
if(!aff_loading){
aff_loading = true;
var xhr_aff = new XMLHttpRequest();
xhr_aff.open("GET", "https://cdn.jsdelivr.net/codemirror.spell-checker/latest/en_US.aff", true);
xhr_aff.onload = function (e) {
if (xhr_aff.readyState === 4 && xhr_aff.status === 200) {
aff_data = xhr_aff.responseText;
num_loaded++;
if(num_loaded == 2){
typo = new Typo("en_US", aff_data, dic_data, {
platform: 'any'
});
}
}
}
};
xhr_aff.send(null);
};
xhr_aff.send(null);
}
var xhr_dic = new XMLHttpRequest();
xhr_dic.open("GET", "https://cdn.jsdelivr.net/codemirror.spell-checker/latest/en_US.dic", true);
xhr_dic.onload = function (e) {
if (xhr_dic.readyState === 4 && xhr_dic.status === 200) {
dic_data = xhr_dic.responseText;
num_loaded++;
if(num_loaded == 2){
typo = new Typo("en_US", aff_data, dic_data, {
platform: 'any'
});
if(!dic_loading){
dic_loading = true;
var xhr_dic = new XMLHttpRequest();
xhr_dic.open("GET", "https://cdn.jsdelivr.net/codemirror.spell-checker/latest/en_US.dic", true);
xhr_dic.onload = function (e) {
if (xhr_dic.readyState === 4 && xhr_dic.status === 200) {
dic_data = xhr_dic.responseText;
num_loaded++;
if(num_loaded == 2){
typo = new Typo("en_US", aff_data, dic_data, {
platform: 'any'
});
}
}
}
};
xhr_dic.send(null);
};
xhr_dic.send(null);
}
// Define what separates a word

Loading…
Cancel
Save