First reconstruction
parent
3662859798
commit
ca6b87f016
@ -0,0 +1,607 @@
|
|||||||
|
/**
|
||||||
|
* Created by WittBulter on 2017/1/17.
|
||||||
|
*/
|
||||||
|
import Base from './base'
|
||||||
|
|
||||||
|
export default class Action {
|
||||||
|
constructor (){
|
||||||
|
this.saved_overflow = ''
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Action for toggling bold.
|
||||||
|
*/
|
||||||
|
static toggleBold (editor){
|
||||||
|
Base.toggleBlock(editor, "bold", editor.options.blockStyles.bold);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Action for toggling italic.
|
||||||
|
*/
|
||||||
|
static toggleItalic (editor){
|
||||||
|
Base.toggleBlock(editor, "italic", editor.options.blockStyles.italic);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Action for drawing a link.
|
||||||
|
*/
|
||||||
|
static drawLink (editor){
|
||||||
|
const cm = editor.codemirror;
|
||||||
|
const stat = getState(cm);
|
||||||
|
const options = editor.options;
|
||||||
|
let url = "http://";
|
||||||
|
if(options.promptURLs) {
|
||||||
|
url = prompt(options.promptTexts.link);
|
||||||
|
if(!url) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Base.replaceSelection(cm, stat.link, options.insertTexts.link, url);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Action for toggling heading size: normal -> h1 -> h2 -> h3 -> h4 -> h5 -> h6 -> normal
|
||||||
|
*/
|
||||||
|
static toggleHeadingSmaller (editor){
|
||||||
|
const cm = editor.codemirror;
|
||||||
|
Base.toggleHeading(cm, "smaller");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Action for toggling heading size: normal -> h6 -> h5 -> h4 -> h3 -> h2 -> h1 -> normal
|
||||||
|
*/
|
||||||
|
static toggleHeadingBigger (editor){
|
||||||
|
const cm = editor.codemirror;
|
||||||
|
Base.toggleHeading(cm, "bigger");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Action for drawing an img.
|
||||||
|
*/
|
||||||
|
static drawImage (editor){
|
||||||
|
const cm = editor.codemirror;
|
||||||
|
const stat = Base.getState(cm);
|
||||||
|
const options = editor.options;
|
||||||
|
let url = "http://";
|
||||||
|
if(options.promptURLs) {
|
||||||
|
url = prompt(options.promptTexts.image);
|
||||||
|
if(!url) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Base.replaceSelection(cm, stat.image, options.insertTexts.image, url);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Action for toggling blockquote.
|
||||||
|
*/
|
||||||
|
static toggleBlockquote (editor){
|
||||||
|
const cm = editor.codemirror;
|
||||||
|
Base.toggleLine(cm, "quote");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Action for toggling ol.
|
||||||
|
*/
|
||||||
|
static toggleOrderedList (editor){
|
||||||
|
const cm = editor.codemirror;
|
||||||
|
Base.toggleLine(cm, "ordered-list");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Action for toggling ul.
|
||||||
|
*/
|
||||||
|
static toggleUnorderedList (editor){
|
||||||
|
const cm = editor.codemirror;
|
||||||
|
Base.toggleLine(cm, "unordered-list");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Action for toggling code block.
|
||||||
|
*/
|
||||||
|
static toggleCodeBlock (editor){
|
||||||
|
let fenceCharsToInsert = editor.options.blockStyles.code;
|
||||||
|
|
||||||
|
const 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
const 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
const code_type = (cm, line_num, line = cm.getLineHandle(line_num), 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.
|
||||||
|
*/
|
||||||
|
firstTok = firstTok || cm.getTokenAt({
|
||||||
|
line: line_num,
|
||||||
|
ch: 1
|
||||||
|
});
|
||||||
|
lastTok = lastTok || (!!line.text && cm.getTokenAt({
|
||||||
|
line: line_num,
|
||||||
|
ch: line.text.length - 1
|
||||||
|
}));
|
||||||
|
let 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";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const insertFencingAtSelection = (cm, cur_start, cur_end, fenceCharsToInsert) => {
|
||||||
|
let 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--;
|
||||||
|
}
|
||||||
|
Base.replaceSelection(cm, false, [repl_start, repl_end]);
|
||||||
|
cm.setSelection({
|
||||||
|
line: start_line_sel,
|
||||||
|
ch: 0
|
||||||
|
}, {
|
||||||
|
line: end_line_sel,
|
||||||
|
ch: 0
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
let 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);
|
||||||
|
let block_start, block_end, lineCount;
|
||||||
|
|
||||||
|
if(is_code === "single") {
|
||||||
|
// similar to some SimpleMDE _toggleBlock logic
|
||||||
|
let 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let fencedTok = cm.getTokenAt({
|
||||||
|
line: block_start,
|
||||||
|
ch: 1
|
||||||
|
});
|
||||||
|
let fence_chars = token_state(fencedTok).fencedChars;
|
||||||
|
let start_text, start_line;
|
||||||
|
let 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
|
||||||
|
let 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
|
||||||
|
let 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
|
||||||
|
let no_sel_and_starting_of_line = (cur_start.line === cur_end.line && cur_start.ch === cur_end.ch && cur_start.ch === 0);
|
||||||
|
let 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 {
|
||||||
|
Base.replaceSelection(cm, false, ["`", "`"]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Toggle full screen of the editor.
|
||||||
|
*/
|
||||||
|
static toggleFullScreen (editor){
|
||||||
|
// Set fullscreen
|
||||||
|
let cm = editor.codemirror;
|
||||||
|
cm.setOption("fullScreen", !cm.getOption("fullScreen"));
|
||||||
|
|
||||||
|
|
||||||
|
// Prevent scrolling on body during fullscreen active
|
||||||
|
if(cm.getOption("fullScreen")) {
|
||||||
|
this.saved_overflow = document.body.style.overflow;
|
||||||
|
document.body.style.overflow = "hidden";
|
||||||
|
} else {
|
||||||
|
document.body.style.overflow = this.saved_overflow;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Update toolbar class
|
||||||
|
let 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
|
||||||
|
let 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
|
||||||
|
const sidebyside = cm.getWrapperElement().nextSibling;
|
||||||
|
if(/editor-preview-active-side/.test(sidebyside.className))
|
||||||
|
this.toggleSideBySide(editor);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Toggle side by side preview
|
||||||
|
*/
|
||||||
|
static toggleSideBySide (editor){
|
||||||
|
let cm = editor.codemirror;
|
||||||
|
let wrapper = cm.getWrapperElement();
|
||||||
|
let preview = wrapper.nextSibling;
|
||||||
|
let toolbarButton = editor.toolbarElements["side-by-side"];
|
||||||
|
let 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"))
|
||||||
|
this.toggleFullScreen(editor);
|
||||||
|
preview.className += " editor-preview-active-side";
|
||||||
|
}, 1);
|
||||||
|
toolbarButton.className += " active";
|
||||||
|
wrapper.className += " CodeMirror-sided";
|
||||||
|
useSideBySideListener = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Hide normal preview if active
|
||||||
|
let previewNormal = wrapper.lastChild;
|
||||||
|
if(/editor-preview-active/.test(previewNormal.className)) {
|
||||||
|
previewNormal.className = previewNormal.className.replace(
|
||||||
|
/\s*editor-preview-active\s*/g, ""
|
||||||
|
);
|
||||||
|
let toolbar = editor.toolbarElements.preview;
|
||||||
|
let 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, "");
|
||||||
|
}
|
||||||
|
|
||||||
|
const 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.
|
||||||
|
*/
|
||||||
|
static togglePreview (editor){
|
||||||
|
let cm = editor.codemirror;
|
||||||
|
let wrapper = cm.getWrapperElement();
|
||||||
|
let toolbar_div = wrapper.previousSibling;
|
||||||
|
let toolbar = editor.options.toolbar ? editor.toolbarElements.preview : false;
|
||||||
|
let 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
|
||||||
|
const sidebyside = cm.getWrapperElement().nextSibling;
|
||||||
|
if(/editor-preview-active-side/.test(sidebyside.className))
|
||||||
|
this.toggleSideBySide(editor);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Action for toggling strikethrough.
|
||||||
|
*/
|
||||||
|
static toggleStrikethrough (editor){
|
||||||
|
Base.toggleBlock(editor, "strikethrough", "~~");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Action for toggling heading size 1, 2, 3
|
||||||
|
*/
|
||||||
|
static toggleHeading1 (editor){
|
||||||
|
const cm = editor.codemirror;
|
||||||
|
Base.toggleHeading(cm, undefined, 1);
|
||||||
|
}
|
||||||
|
static toggleHeading2 (editor){
|
||||||
|
const cm = editor.codemirror;
|
||||||
|
Base.toggleHeading(cm, undefined, 2);
|
||||||
|
}
|
||||||
|
static toggleHeading3 (editor){
|
||||||
|
const cm = editor.codemirror;
|
||||||
|
Base.toggleHeading(cm, undefined, 3);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Action for clean block (remove headline, list, blockquote code, markers)
|
||||||
|
*/
|
||||||
|
static cleanBlock (editor){
|
||||||
|
const cm = editor.codemirror;
|
||||||
|
Base.cleanBlock(cm);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Action for drawing a table.
|
||||||
|
*/
|
||||||
|
static drawTable (editor){
|
||||||
|
const cm = editor.codemirror;
|
||||||
|
const stat = Base.getState(cm);
|
||||||
|
const options = editor.options;
|
||||||
|
Base.replaceSelection(cm, stat.table, options.insertTexts.table);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Action for drawing a horizontal rule.
|
||||||
|
*/
|
||||||
|
static drawHorizontalRule (editor){
|
||||||
|
const cm = editor.codemirror;
|
||||||
|
const stat = Base.getState(cm);
|
||||||
|
const options = editor.options;
|
||||||
|
Base.replaceSelection(cm, stat.image, options.insertTexts.horizontalRule);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Undo action.
|
||||||
|
*/
|
||||||
|
static undo (editor){
|
||||||
|
const cm = editor.codemirror;
|
||||||
|
cm.undo();
|
||||||
|
cm.focus();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Redo action.
|
||||||
|
*/
|
||||||
|
static redo (editor){
|
||||||
|
const cm = editor.codemirror;
|
||||||
|
cm.redo();
|
||||||
|
cm.focus();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,276 @@
|
|||||||
|
/**
|
||||||
|
* Created by WittBulter on 2017/1/17.
|
||||||
|
*/
|
||||||
|
|
||||||
|
export default new class Base {
|
||||||
|
constructor (){}
|
||||||
|
|
||||||
|
getState (cm, pos = cm.getCursor("start")){
|
||||||
|
let stat = cm.getTokenAt(pos);
|
||||||
|
if(!stat.type) return {};
|
||||||
|
const types = stat.type.split(" ");
|
||||||
|
|
||||||
|
let ret = {},
|
||||||
|
data, text;
|
||||||
|
for(let 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
let cm = editor.codemirror;
|
||||||
|
let stat = this.getState(cm);
|
||||||
|
|
||||||
|
let text;
|
||||||
|
let start = start_chars;
|
||||||
|
let end = end_chars;
|
||||||
|
|
||||||
|
let startPoint = cm.getCursor("start");
|
||||||
|
let 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();
|
||||||
|
}
|
||||||
|
|
||||||
|
replaceSelection (cm, active, startEnd, url){
|
||||||
|
if(/editor-preview-active/.test(cm.getWrapperElement().lastChild.className))
|
||||||
|
return;
|
||||||
|
|
||||||
|
let text;
|
||||||
|
let start = startEnd[0];
|
||||||
|
let end = startEnd[1];
|
||||||
|
let startPoint = cm.getCursor("start");
|
||||||
|
let 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();
|
||||||
|
}
|
||||||
|
|
||||||
|
toggleHeading (cm, direction, size){
|
||||||
|
if(/editor-preview-active/.test(cm.getWrapperElement().lastChild.className))
|
||||||
|
return;
|
||||||
|
|
||||||
|
const startPoint = cm.getCursor("start");
|
||||||
|
const endPoint = cm.getCursor("end");
|
||||||
|
for(let i = startPoint.line; i <= endPoint.line; i++) {
|
||||||
|
(function(i) {
|
||||||
|
let text = cm.getLine(i);
|
||||||
|
let 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();
|
||||||
|
}
|
||||||
|
|
||||||
|
toggleLine (cm, name){
|
||||||
|
if(/editor-preview-active/.test(cm.getWrapperElement().lastChild.className)) return;
|
||||||
|
|
||||||
|
let stat = this.getState(cm);
|
||||||
|
const startPoint = cm.getCursor("start");
|
||||||
|
const endPoint = cm.getCursor("end");
|
||||||
|
const repl = {
|
||||||
|
"quote": /^(\s*)\>\s+/,
|
||||||
|
"unordered-list": /^(\s*)(\*|\-|\+)\s+/,
|
||||||
|
"ordered-list": /^(\s*)\d+\.\s+/
|
||||||
|
};
|
||||||
|
const map = {
|
||||||
|
"quote": "> ",
|
||||||
|
"unordered-list": "* ",
|
||||||
|
"ordered-list": "1. "
|
||||||
|
};
|
||||||
|
for(let i = startPoint.line; i <= endPoint.line; i++) {
|
||||||
|
(function(i) {
|
||||||
|
let 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();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
cleanBlock (cm){
|
||||||
|
if(/editor-preview-active/.test(cm.getWrapperElement().lastChild.className))
|
||||||
|
return;
|
||||||
|
|
||||||
|
const startPoint = cm.getCursor("start");
|
||||||
|
const endPoint = cm.getCursor("end");
|
||||||
|
let text;
|
||||||
|
|
||||||
|
for(let 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
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,238 @@
|
|||||||
|
/**
|
||||||
|
* Created by WittBulter on 2017/1/17.
|
||||||
|
*/
|
||||||
|
import Action from './action'
|
||||||
|
|
||||||
|
export const bindings = {
|
||||||
|
"toggleBold": Action.toggleBold,
|
||||||
|
"toggleItalic": Action.toggleItalic,
|
||||||
|
"drawLink": Action.drawLink,
|
||||||
|
"toggleHeadingSmaller": Action.toggleHeadingSmaller,
|
||||||
|
"toggleHeadingBigger": Action.toggleHeadingBigger,
|
||||||
|
"drawImage": Action.drawImage,
|
||||||
|
"toggleBlockquote": Action.toggleBlockquote,
|
||||||
|
"toggleOrderedList": Action.toggleOrderedList,
|
||||||
|
"toggleUnorderedList": Action.toggleUnorderedList,
|
||||||
|
"toggleCodeBlock": Action.toggleCodeBlock,
|
||||||
|
"togglePreview": Action.togglePreview,
|
||||||
|
"toggleStrikethrough": Action.toggleStrikethrough,
|
||||||
|
"toggleHeading1": Action.toggleHeading1,
|
||||||
|
"toggleHeading2": Action.toggleHeading2,
|
||||||
|
"toggleHeading3": Action.toggleHeading3,
|
||||||
|
"cleanBlock": Action.cleanBlock,
|
||||||
|
"drawTable": Action.drawTable,
|
||||||
|
"drawHorizontalRule": Action.drawHorizontalRule,
|
||||||
|
"undo": Action.undo,
|
||||||
|
"redo": Action.redo,
|
||||||
|
"toggleSideBySide": Action.toggleSideBySide,
|
||||||
|
"toggleFullScreen": Action.toggleFullScreen
|
||||||
|
};
|
||||||
|
|
||||||
|
export const 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"
|
||||||
|
};
|
||||||
|
|
||||||
|
export const toolbarBuiltInButtons = {
|
||||||
|
"bold": {
|
||||||
|
name: "bold",
|
||||||
|
action: Action.toggleBold,
|
||||||
|
className: "fa fa-bold",
|
||||||
|
title: "Bold",
|
||||||
|
default: true
|
||||||
|
},
|
||||||
|
"italic": {
|
||||||
|
name: "italic",
|
||||||
|
action: Action.toggleItalic,
|
||||||
|
className: "fa fa-italic",
|
||||||
|
title: "Italic",
|
||||||
|
default: true
|
||||||
|
},
|
||||||
|
"strikethrough": {
|
||||||
|
name: "strikethrough",
|
||||||
|
action: Action.toggleStrikethrough,
|
||||||
|
className: "fa fa-strikethrough",
|
||||||
|
title: "Strikethrough"
|
||||||
|
},
|
||||||
|
"heading": {
|
||||||
|
name: "heading",
|
||||||
|
action: Action.toggleHeadingSmaller,
|
||||||
|
className: "fa fa-header",
|
||||||
|
title: "Heading",
|
||||||
|
default: true
|
||||||
|
},
|
||||||
|
"heading-smaller": {
|
||||||
|
name: "heading-smaller",
|
||||||
|
action: Action.toggleHeadingSmaller,
|
||||||
|
className: "fa fa-header fa-header-x fa-header-smaller",
|
||||||
|
title: "Smaller Heading"
|
||||||
|
},
|
||||||
|
"heading-bigger": {
|
||||||
|
name: "heading-bigger",
|
||||||
|
action: Action.toggleHeadingBigger,
|
||||||
|
className: "fa fa-header fa-header-x fa-header-bigger",
|
||||||
|
title: "Bigger Heading"
|
||||||
|
},
|
||||||
|
"heading-1": {
|
||||||
|
name: "heading-1",
|
||||||
|
action: Action.toggleHeading1,
|
||||||
|
className: "fa fa-header fa-header-x fa-header-1",
|
||||||
|
title: "Big Heading"
|
||||||
|
},
|
||||||
|
"heading-2": {
|
||||||
|
name: "heading-2",
|
||||||
|
action: Action.toggleHeading2,
|
||||||
|
className: "fa fa-header fa-header-x fa-header-2",
|
||||||
|
title: "Medium Heading"
|
||||||
|
},
|
||||||
|
"heading-3": {
|
||||||
|
name: "heading-3",
|
||||||
|
action: Action.toggleHeading3,
|
||||||
|
className: "fa fa-header fa-header-x fa-header-3",
|
||||||
|
title: "Small Heading"
|
||||||
|
},
|
||||||
|
"separator-1": {
|
||||||
|
name: "separator-1"
|
||||||
|
},
|
||||||
|
"code": {
|
||||||
|
name: "code",
|
||||||
|
action: Action.toggleCodeBlock,
|
||||||
|
className: "fa fa-code",
|
||||||
|
title: "Code"
|
||||||
|
},
|
||||||
|
"quote": {
|
||||||
|
name: "quote",
|
||||||
|
action: Action.toggleBlockquote,
|
||||||
|
className: "fa fa-quote-left",
|
||||||
|
title: "Quote",
|
||||||
|
default: true
|
||||||
|
},
|
||||||
|
"unordered-list": {
|
||||||
|
name: "unordered-list",
|
||||||
|
action: Action.toggleUnorderedList,
|
||||||
|
className: "fa fa-list-ul",
|
||||||
|
title: "Generic List",
|
||||||
|
default: true
|
||||||
|
},
|
||||||
|
"ordered-list": {
|
||||||
|
name: "ordered-list",
|
||||||
|
action: Action.toggleOrderedList,
|
||||||
|
className: "fa fa-list-ol",
|
||||||
|
title: "Numbered List",
|
||||||
|
default: true
|
||||||
|
},
|
||||||
|
"clean-block": {
|
||||||
|
name: "clean-block",
|
||||||
|
action: Action.cleanBlock,
|
||||||
|
className: "fa fa-eraser fa-clean-block",
|
||||||
|
title: "Clean block"
|
||||||
|
},
|
||||||
|
"separator-2": {
|
||||||
|
name: "separator-2"
|
||||||
|
},
|
||||||
|
"link": {
|
||||||
|
name: "link",
|
||||||
|
action: Action.drawLink,
|
||||||
|
className: "fa fa-link",
|
||||||
|
title: "Create Link",
|
||||||
|
default: true
|
||||||
|
},
|
||||||
|
"image": {
|
||||||
|
name: "image",
|
||||||
|
action: Action.drawImage,
|
||||||
|
className: "fa fa-picture-o",
|
||||||
|
title: "Insert Image",
|
||||||
|
default: true
|
||||||
|
},
|
||||||
|
"table": {
|
||||||
|
name: "table",
|
||||||
|
action: Action.drawTable,
|
||||||
|
className: "fa fa-table",
|
||||||
|
title: "Insert Table"
|
||||||
|
},
|
||||||
|
"horizontal-rule": {
|
||||||
|
name: "horizontal-rule",
|
||||||
|
action: Action.drawHorizontalRule,
|
||||||
|
className: "fa fa-minus",
|
||||||
|
title: "Insert Horizontal Line"
|
||||||
|
},
|
||||||
|
"separator-3": {
|
||||||
|
name: "separator-3"
|
||||||
|
},
|
||||||
|
"preview": {
|
||||||
|
name: "preview",
|
||||||
|
action: Action.togglePreview,
|
||||||
|
className: "fa fa-eye no-disable",
|
||||||
|
title: "Toggle Preview",
|
||||||
|
default: true
|
||||||
|
},
|
||||||
|
"side-by-side": {
|
||||||
|
name: "side-by-side",
|
||||||
|
action: Action.toggleSideBySide,
|
||||||
|
className: "fa fa-columns no-disable no-mobile",
|
||||||
|
title: "Toggle Side by Side",
|
||||||
|
default: true
|
||||||
|
},
|
||||||
|
"fullscreen": {
|
||||||
|
name: "fullscreen",
|
||||||
|
action: 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: Action.undo,
|
||||||
|
className: "fa fa-undo no-disable",
|
||||||
|
title: "Undo"
|
||||||
|
},
|
||||||
|
"redo": {
|
||||||
|
name: "redo",
|
||||||
|
action: Action.redo,
|
||||||
|
className: "fa fa-repeat no-disable",
|
||||||
|
title: "Redo"
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export const blockStyles = {
|
||||||
|
"bold": "**",
|
||||||
|
"code": "```",
|
||||||
|
"italic": "*"
|
||||||
|
};
|
||||||
|
|
||||||
|
export const 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"]
|
||||||
|
};
|
||||||
|
|
||||||
|
export const promptTexts = {
|
||||||
|
link: "URL for the link:",
|
||||||
|
image: "URL of the image:"
|
||||||
|
};
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,69 @@
|
|||||||
|
/**
|
||||||
|
* Created by WittBulter on 2017/1/17.
|
||||||
|
*/
|
||||||
|
import {bindings} from './metadata'
|
||||||
|
|
||||||
|
export default new class Utils {
|
||||||
|
constructor (){}
|
||||||
|
|
||||||
|
isMac (){
|
||||||
|
return navigator.platform.includes('Mac')
|
||||||
|
}
|
||||||
|
|
||||||
|
isMobile (){
|
||||||
|
let 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* The right word count in respect for CJK. */
|
||||||
|
wordCount (data){
|
||||||
|
const pattern = /[a-zA-Z0-9_\u0392-\u03c9\u0410-\u04F9]+|[\u4E00-\u9FFF\u3400-\u4dbf\uf900-\ufaff\u3040-\u309f\uac00-\ud7af]+/g;
|
||||||
|
const m = data.match(pattern);
|
||||||
|
let count = 0;
|
||||||
|
if(m === null) return count;
|
||||||
|
for(let i = 0; i < m.length; i++) {
|
||||||
|
if(m[i].charCodeAt(0) >= 0x4E00) {
|
||||||
|
count += m[i].length;
|
||||||
|
} else {
|
||||||
|
count += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
getBindingName (f){
|
||||||
|
for(let key of bindings) {
|
||||||
|
if(bindings[key] === f) {
|
||||||
|
return key;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fix shortcut. Mac use Command, others use Ctrl.
|
||||||
|
*/
|
||||||
|
fixShortcut (name){
|
||||||
|
name = this.isMac() ? name.replace("Ctrl", "Cmd") : name.replace("Cmd", "Ctrl")
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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.
|
||||||
|
isLocalStorageAvailable (){
|
||||||
|
if(typeof localStorage === "object") {
|
||||||
|
try {
|
||||||
|
localStorage.setItem("smde_localStorage", 1);
|
||||||
|
localStorage.removeItem("smde_localStorage");
|
||||||
|
} catch(e) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue