Skip to content

Commit 5a53199

Browse files
author
Laszlo Moczo
committed
Optional prompt windows to Link and Image
1 parent eb568dd commit 5a53199

File tree

5 files changed

+611
-42
lines changed

5 files changed

+611
-42
lines changed

README.md

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -94,9 +94,6 @@ simplemde.value("This text will appear in the editor");
9494
- **placeholder**: Custom placeholder that should be displayed
9595
- **previewRender**: Custom function for parsing the plaintext Markdown and returning HTML. Used when user previews.
9696
- **promptURLs**: If set to `true`, a prompt window come if you insert link or image. Defaults to `false`.
97-
- **promptTexts**: An object of prompt text.
98-
-- image (Default: `The URL of image:`)
99-
-- link (Default: `URL for link:`)
10097
- **renderingConfig**: Adjust settings for parsing the Markdown during previewing (not editing).
10198
- **singleLineBreaks**: If set to `false`, disable parsing GFM single line breaks. Defaults to `true`.
10299
- **codeSyntaxHighlighting**: If set to `true`, will highlight using [highlight.js](https://github.com/isagalaev/highlight.js). Defaults to `false`. To use this feature you must include highlight.js on your page. For example, include the script and the CSS files like:<br>`<script src="https://cdn.jsdelivr.net/highlight.js/latest/highlight.min.js"></script>`<br>`<link rel="stylesheet" href="https://cdn.jsdelivr.net/highlight.js/latest/styles/github.min.css">`
@@ -149,6 +146,7 @@ var simplemde = new SimpleMDE({
149146

150147
return "Loading...";
151148
},
149+
promptURLs: true, // Show a prompt window to insert URL for link or image
152150
renderingConfig: {
153151
singleLineBreaks: false,
154152
codeSyntaxHighlighting: true,

debug/simplemde.debug.js

Lines changed: 296 additions & 8 deletions
Large diffs are not rendered by default.

debug/simplemde.js

Lines changed: 295 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -13139,7 +13139,286 @@ function toggleStrikethrough(editor) {
1313913139
* Action for toggling code block.
1314013140
*/
1314113141
function toggleCodeBlock(editor) {
13142-
_toggleBlock(editor, "code", "```\r\n", "\r\n```");
13142+
var fenceCharsToInsert = editor.options.blockStyles.code;
13143+
13144+
function fencing_line(line) {
13145+
/* return true, if this is a ``` or ~~~ line */
13146+
if(typeof line !== "object") {
13147+
throw "fencing_line() takes a 'line' object (not a line number, or line text). Got: " + typeof line + ": " + line;
13148+
}
13149+
return line.styles && line.styles[2] && line.styles[2].indexOf("formatting-code-block") !== -1;
13150+
}
13151+
13152+
function token_state(token) {
13153+
// base goes an extra level deep when mode backdrops are used, e.g. spellchecker on
13154+
return token.state.base.base || token.state.base;
13155+
}
13156+
13157+
function code_type(cm, line_num, line, firstTok, lastTok) {
13158+
/*
13159+
* Return "single", "indented", "fenced" or false
13160+
*
13161+
* cm and line_num are required. Others are optional for efficiency
13162+
* To check in the middle of a line, pass in firstTok yourself.
13163+
*/
13164+
line = line || cm.getLineHandle(line_num);
13165+
firstTok = firstTok || cm.getTokenAt({
13166+
line: line_num,
13167+
ch: 1
13168+
});
13169+
lastTok = lastTok || (!!line.text && cm.getTokenAt({
13170+
line: line_num,
13171+
ch: line.text.length - 1
13172+
}));
13173+
var types = firstTok.type ? firstTok.type.split(" ") : [];
13174+
if(lastTok && token_state(lastTok).indentedCode) {
13175+
// have to check last char, since first chars of first line aren"t marked as indented
13176+
return "indented";
13177+
} else if(types.indexOf("comment") === -1) {
13178+
// has to be after "indented" check, since first chars of first indented line aren"t marked as such
13179+
return false;
13180+
} else if(token_state(firstTok).fencedChars || token_state(lastTok).fencedChars || fencing_line(line)) {
13181+
return "fenced";
13182+
} else {
13183+
return "single";
13184+
}
13185+
}
13186+
13187+
function insertFencingAtSelection(cm, cur_start, cur_end, fenceCharsToInsert) {
13188+
var start_line_sel = cur_start.line + 1,
13189+
end_line_sel = cur_end.line + 1,
13190+
sel_multi = cur_start.line !== cur_end.line,
13191+
repl_start = fenceCharsToInsert + "\n",
13192+
repl_end = "\n" + fenceCharsToInsert;
13193+
if(sel_multi) {
13194+
end_line_sel++;
13195+
}
13196+
// handle last char including \n or not
13197+
if(sel_multi && cur_end.ch === 0) {
13198+
repl_end = fenceCharsToInsert + "\n";
13199+
end_line_sel--;
13200+
}
13201+
_replaceSelection(cm, false, [repl_start, repl_end]);
13202+
cm.setSelection({
13203+
line: start_line_sel,
13204+
ch: 0
13205+
}, {
13206+
line: end_line_sel,
13207+
ch: 0
13208+
});
13209+
}
13210+
13211+
var cm = editor.codemirror,
13212+
cur_start = cm.getCursor("start"),
13213+
cur_end = cm.getCursor("end"),
13214+
tok = cm.getTokenAt({
13215+
line: cur_start.line,
13216+
ch: cur_start.ch || 1
13217+
}), // avoid ch 0 which is a cursor pos but not token
13218+
line = cm.getLineHandle(cur_start.line),
13219+
is_code = code_type(cm, cur_start.line, line, tok);
13220+
var block_start, block_end, lineCount;
13221+
13222+
if(is_code === "single") {
13223+
// similar to some SimpleMDE _toggleBlock logic
13224+
var start = line.text.slice(0, cur_start.ch).replace("`", ""),
13225+
end = line.text.slice(cur_start.ch).replace("`", "");
13226+
cm.replaceRange(start + end, {
13227+
line: cur_start.line,
13228+
ch: 0
13229+
}, {
13230+
line: cur_start.line,
13231+
ch: 99999999999999
13232+
});
13233+
cur_start.ch--;
13234+
if(cur_start !== cur_end) {
13235+
cur_end.ch--;
13236+
}
13237+
cm.setSelection(cur_start, cur_end);
13238+
cm.focus();
13239+
} else if(is_code === "fenced") {
13240+
if(cur_start.line !== cur_end.line || cur_start.ch !== cur_end.ch) {
13241+
// use selection
13242+
13243+
// find the fenced line so we know what type it is (tilde, backticks, number of them)
13244+
for(block_start = cur_start.line; block_start >= 0; block_start--) {
13245+
line = cm.getLineHandle(block_start);
13246+
if(fencing_line(line)) {
13247+
break;
13248+
}
13249+
}
13250+
var fencedTok = cm.getTokenAt({
13251+
line: block_start,
13252+
ch: 1
13253+
});
13254+
var fence_chars = token_state(fencedTok).fencedChars;
13255+
var start_text, start_line;
13256+
var end_text, end_line;
13257+
// check for selection going up against fenced lines, in which case we don't want to add more fencing
13258+
if(fencing_line(cm.getLineHandle(cur_start.line))) {
13259+
start_text = "";
13260+
start_line = cur_start.line;
13261+
} else if(fencing_line(cm.getLineHandle(cur_start.line - 1))) {
13262+
start_text = "";
13263+
start_line = cur_start.line - 1;
13264+
} else {
13265+
start_text = fence_chars + "\n";
13266+
start_line = cur_start.line;
13267+
}
13268+
if(fencing_line(cm.getLineHandle(cur_end.line))) {
13269+
end_text = "";
13270+
end_line = cur_end.line;
13271+
if(cur_end.ch === 0) {
13272+
end_line += 1;
13273+
}
13274+
} else if(cur_end.ch !== 0 && fencing_line(cm.getLineHandle(cur_end.line + 1))) {
13275+
end_text = "";
13276+
end_line = cur_end.line + 1;
13277+
} else {
13278+
end_text = fence_chars + "\n";
13279+
end_line = cur_end.line + 1;
13280+
}
13281+
if(cur_end.ch === 0) {
13282+
// full last line selected, putting cursor at beginning of next
13283+
end_line -= 1;
13284+
}
13285+
cm.operation(function() {
13286+
// end line first, so that line numbers don't change
13287+
cm.replaceRange(end_text, {
13288+
line: end_line,
13289+
ch: 0
13290+
}, {
13291+
line: end_line + (end_text ? 0 : 1),
13292+
ch: 0
13293+
});
13294+
cm.replaceRange(start_text, {
13295+
line: start_line,
13296+
ch: 0
13297+
}, {
13298+
line: start_line + (start_text ? 0 : 1),
13299+
ch: 0
13300+
});
13301+
});
13302+
cm.setSelection({
13303+
line: start_line + (start_text ? 1 : 0),
13304+
ch: 0
13305+
}, {
13306+
line: end_line + (start_text ? 1 : -1),
13307+
ch: 0
13308+
});
13309+
cm.focus();
13310+
} else {
13311+
// no selection, search for ends of this fenced block
13312+
var search_from = cur_start.line;
13313+
if(fencing_line(cm.getLineHandle(cur_start.line))) { // gets a little tricky if cursor is right on a fenced line
13314+
if(code_type(cm, cur_start.line + 1) === "fenced") {
13315+
block_start = cur_start.line;
13316+
search_from = cur_start.line + 1; // for searching for "end"
13317+
} else {
13318+
block_end = cur_start.line;
13319+
search_from = cur_start.line - 1; // for searching for "start"
13320+
}
13321+
}
13322+
if(block_start === undefined) {
13323+
for(block_start = search_from; block_start >= 0; block_start--) {
13324+
line = cm.getLineHandle(block_start);
13325+
if(fencing_line(line)) {
13326+
break;
13327+
}
13328+
}
13329+
}
13330+
if(block_end === undefined) {
13331+
lineCount = cm.lineCount();
13332+
for(block_end = search_from; block_end < lineCount; block_end++) {
13333+
line = cm.getLineHandle(block_end);
13334+
if(fencing_line(line)) {
13335+
break;
13336+
}
13337+
}
13338+
}
13339+
cm.operation(function() {
13340+
cm.replaceRange("", {
13341+
line: block_start,
13342+
ch: 0
13343+
}, {
13344+
line: block_start + 1,
13345+
ch: 0
13346+
});
13347+
cm.replaceRange("", {
13348+
line: block_end - 1,
13349+
ch: 0
13350+
}, {
13351+
line: block_end,
13352+
ch: 0
13353+
});
13354+
});
13355+
cm.focus();
13356+
}
13357+
} else if(is_code === "indented") {
13358+
if(cur_start.line !== cur_end.line || cur_start.ch !== cur_end.ch) {
13359+
// use selection
13360+
block_start = cur_start.line;
13361+
block_end = cur_end.line;
13362+
if(cur_end.ch === 0) {
13363+
block_end--;
13364+
}
13365+
} else {
13366+
// no selection, search for ends of this indented block
13367+
for(block_start = cur_start.line; block_start >= 0; block_start--) {
13368+
line = cm.getLineHandle(block_start);
13369+
if(line.text.match(/^\s*$/)) {
13370+
// empty or all whitespace - keep going
13371+
continue;
13372+
} else {
13373+
if(code_type(cm, block_start, line) !== "indented") {
13374+
block_start += 1;
13375+
break;
13376+
}
13377+
}
13378+
}
13379+
lineCount = cm.lineCount();
13380+
for(block_end = cur_start.line; block_end < lineCount; block_end++) {
13381+
line = cm.getLineHandle(block_end);
13382+
if(line.text.match(/^\s*$/)) {
13383+
// empty or all whitespace - keep going
13384+
continue;
13385+
} else {
13386+
if(code_type(cm, block_end, line) !== "indented") {
13387+
block_end -= 1;
13388+
break;
13389+
}
13390+
}
13391+
}
13392+
}
13393+
// if we are going to un-indent based on a selected set of lines, and the next line is indented too, we need to
13394+
// insert a blank line so that the next line(s) continue to be indented code
13395+
var next_line = cm.getLineHandle(block_end + 1),
13396+
next_line_last_tok = next_line && cm.getTokenAt({
13397+
line: block_end + 1,
13398+
ch: next_line.text.length - 1
13399+
}),
13400+
next_line_indented = next_line_last_tok && token_state(next_line_last_tok).indentedCode;
13401+
if(next_line_indented) {
13402+
cm.replaceRange("\n", {
13403+
line: block_end + 1,
13404+
ch: 0
13405+
});
13406+
}
13407+
13408+
for(var i = block_start; i <= block_end; i++) {
13409+
cm.indentLine(i, "subtract"); // TODO: this doesn't get tracked in the history, so can't be undone :(
13410+
}
13411+
cm.focus();
13412+
} else {
13413+
// insert code formatting
13414+
var no_sel_and_starting_of_line = (cur_start.line === cur_end.line && cur_start.ch === cur_end.ch && cur_start.ch === 0);
13415+
var sel_multi = cur_start.line !== cur_end.line;
13416+
if(no_sel_and_starting_of_line || sel_multi) {
13417+
insertFencingAtSelection(cm, cur_start, cur_end, fenceCharsToInsert);
13418+
} else {
13419+
_replaceSelection(cm, false, ["`", "`"]);
13420+
}
13421+
}
1314313422
}
1314413423

1314513424
/**
@@ -13225,7 +13504,10 @@ function drawLink(editor) {
1322513504
var options = editor.options;
1322613505
var url = "http://";
1322713506
if(options.promptURLs) {
13228-
url = prompt(options.promptTexts.link) || "http://";
13507+
url = prompt(options.promptTexts.link);
13508+
if(!url) {
13509+
return false;
13510+
}
1322913511
}
1323013512
_replaceSelection(cm, stat.link, options.insertTexts.link, url);
1323113513
}
@@ -13239,7 +13521,10 @@ function drawImage(editor) {
1323913521
var options = editor.options;
1324013522
var url = "http://";
1324113523
if(options.promptURLs) {
13242-
url = prompt(options.promptTexts.image) || "http://";
13524+
url = prompt(options.promptTexts.image);
13525+
if(!url) {
13526+
return false;
13527+
}
1324313528
}
1324413529
_replaceSelection(cm, stat.image, options.insertTexts.image, url);
1324513530
}
@@ -13845,12 +14130,13 @@ var insertTexts = {
1384514130
};
1384614131

1384714132
var promptTexts = {
13848-
link: "URL for link:",
13849-
image: "The URL of image:"
14133+
link: "URL for the link:",
14134+
image: "URL of the image:"
1385014135
};
1385114136

1385214137
var blockStyles = {
1385314138
"bold": "**",
14139+
"code": "```",
1385414140
"italic": "*"
1385514141
};
1385614142

@@ -13940,15 +14226,17 @@ function SimpleMDE(options) {
1394014226

1394114227

1394214228
// Set default options for parsing config
13943-
options.parsingConfig = options.parsingConfig || {};
14229+
options.parsingConfig = extend({
14230+
highlightFormatting: true // needed for toggleCodeBlock to detect types of code
14231+
}, options.parsingConfig || {});
1394414232

1394514233

1394614234
// Merging the insertTexts, with the given options
1394714235
options.insertTexts = extend({}, insertTexts, options.insertTexts || {});
1394814236

1394914237

1395014238
// Merging the promptTexts, with the given options
13951-
options.promptTexts = extend({}, promptTexts, options.promptTexts || {});
14239+
options.promptTexts = promptTexts;
1395214240

1395314241

1395414242
// Merging the blockStyles, with the given options

dist/simplemde.min.js

Lines changed: 8 additions & 19 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)