merge upstream PageDown changes up to current revision c06c6e0d465e

This commit is contained in:
balpha 2013-02-24 13:40:06 +01:00
parent 430a397cde
commit 22caa7b6a8
2 changed files with 173 additions and 58 deletions

View File

@ -67,7 +67,11 @@ else
if (original === identity) if (original === identity)
this[hookname] = func; this[hookname] = func;
else else
this[hookname] = function (x) { return func(original(x)); } this[hookname] = function (text) {
var args = Array.prototype.slice.call(arguments, 0);
args[0] = original.apply(null, args);
return func.apply(null, args);
};
}, },
set: function (hookname, func) { set: function (hookname, func) {
if (!this[hookname]) if (!this[hookname])
@ -103,9 +107,28 @@ else
Markdown.Converter = function () { Markdown.Converter = function () {
var pluginHooks = this.hooks = new HookCollection(); var pluginHooks = this.hooks = new HookCollection();
pluginHooks.addNoop("plainLinkText"); // given a URL that was encountered by itself (without markup), should return the link text that's to be given to this link
pluginHooks.addNoop("preConversion"); // called with the orignal text as given to makeHtml. The result of this plugin hook is the actual markdown source that will be cooked // given a URL that was encountered by itself (without markup), should return the link text that's to be given to this link
pluginHooks.addNoop("postConversion"); // called with the final cooked HTML code. The result of this plugin hook is the actual output of makeHtml pluginHooks.addNoop("plainLinkText");
// called with the orignal text as given to makeHtml. The result of this plugin hook is the actual markdown source that will be cooked
pluginHooks.addNoop("preConversion");
// called with the text once all normalizations have been completed (tabs to spaces, line endings, etc.), but before any conversions have
pluginHooks.addNoop("postNormalization");
// Called with the text before / after creating block elements like code blocks and lists. Note that this is called recursively
// with inner content, e.g. it's called with the full text, and then only with the content of a blockquote. The inner
// call will receive outdented text.
pluginHooks.addNoop("preBlockGamut");
pluginHooks.addNoop("postBlockGamut");
// called with the text of a single block element before / after the span-level conversions (bold, code spans, etc.) have been made
pluginHooks.addNoop("preSpanGamut");
pluginHooks.addNoop("postSpanGamut");
// called with the final cooked HTML code. The result of this plugin hook is the actual output of makeHtml
pluginHooks.addNoop("postConversion");
// //
// Private state of the converter instance: // Private state of the converter instance:
@ -169,6 +192,8 @@ else
// contorted like /[ \t]*\n+/ . // contorted like /[ \t]*\n+/ .
text = text.replace(/^[ \t]+$/mg, ""); text = text.replace(/^[ \t]+$/mg, "");
text = pluginHooks.postNormalization(text);
// Turn block-level HTML blocks into hash entries // Turn block-level HTML blocks into hash entries
text = _HashHTMLBlocks(text); text = _HashHTMLBlocks(text);
@ -379,11 +404,16 @@ else
return blockText; return blockText;
} }
var blockGamutHookCallback = function (t) { return _RunBlockGamut(t); }
function _RunBlockGamut(text, doNotUnhash) { function _RunBlockGamut(text, doNotUnhash) {
// //
// These are all the transformations that form block-level // These are all the transformations that form block-level
// tags like paragraphs, headers, and list items. // tags like paragraphs, headers, and list items.
// //
text = pluginHooks.preBlockGamut(text, blockGamutHookCallback);
text = _DoHeaders(text); text = _DoHeaders(text);
// Do Horizontal Rules: // Do Horizontal Rules:
@ -396,6 +426,8 @@ else
text = _DoCodeBlocks(text); text = _DoCodeBlocks(text);
text = _DoBlockQuotes(text); text = _DoBlockQuotes(text);
text = pluginHooks.postBlockGamut(text, blockGamutHookCallback);
// We already ran _HashHTMLBlocks() before, in Markdown(), but that // We already ran _HashHTMLBlocks() before, in Markdown(), but that
// was to escape raw HTML in the original Markdown source. This time, // was to escape raw HTML in the original Markdown source. This time,
// we're escaping the markup we've just created, so that we don't wrap // we're escaping the markup we've just created, so that we don't wrap
@ -412,6 +444,8 @@ else
// tags like paragraphs, headers, and list items. // tags like paragraphs, headers, and list items.
// //
text = pluginHooks.preSpanGamut(text);
text = _DoCodeSpans(text); text = _DoCodeSpans(text);
text = _EscapeSpecialCharsWithinTagAttributes(text); text = _EscapeSpecialCharsWithinTagAttributes(text);
text = _EncodeBackslashEscapes(text); text = _EncodeBackslashEscapes(text);
@ -434,6 +468,8 @@ else
// Do hard breaks: // Do hard breaks:
text = text.replace(/ +\n/g, " <br>\n"); text = text.replace(/ +\n/g, " <br>\n");
text = pluginHooks.postSpanGamut(text);
return text; return text;
} }
@ -938,7 +974,7 @@ else
// attacklab: sentinel workarounds for lack of \A and \Z, safari\khtml bug // attacklab: sentinel workarounds for lack of \A and \Z, safari\khtml bug
text += "~0"; text += "~0";
text = text.replace(/(?:\n\n|^)((?:(?:[ ]{4}|\t).*\n+)+)(\n*[ ]{0,3}[^ \t\n]|(?=~0))/g, text = text.replace(/(?:\n\n|^\n?)((?:(?:[ ]{4}|\t).*\n+)+)(\n*[ ]{0,3}[^ \t\n]|(?=~0))/g,
function (wholeMatch, m1, m2) { function (wholeMatch, m1, m2) {
var codeblock = m1; var codeblock = m1;
var nextChar = m2; var nextChar = m2;
@ -1195,21 +1231,50 @@ else
return text; return text;
} }
function handleTrailingParens(wholeMatch, lookbehind, protocol, link) {
if (lookbehind)
return wholeMatch;
if (link.charAt(link.length - 1) !== ")")
return "<" + protocol + link + ">";
var parens = link.match(/[()]/g);
var level = 0;
for (var i = 0; i < parens.length; i++) {
if (parens[i] === "(") {
if (level <= 0)
level = 1;
else
level++;
}
else {
level--;
}
}
var tail = "";
if (level < 0) {
var re = new RegExp("\\){1," + (-level) + "}$");
link = link.replace(re, function (trailingParens) {
tail = trailingParens;
return "";
});
}
return "<" + protocol + link + ">" + tail;
}
function _DoAutoLinks(text) { function _DoAutoLinks(text) {
// note that at this point, all other URL in the text are already hyperlinked as <a href=""></a> // note that at this point, all other URL in the text are already hyperlinked as <a href=""></a>
// *except* for the <http://www.foo.com> case // *except* for the <http://www.foo.com> case
// automatically add < and > around unadorned raw hyperlinks // automatically add < and > around unadorned raw hyperlinks
// must be preceded by space/BOF and followed by non-word/EOF character // must be preceded by a non-word character (and not by =" or <) and followed by non-word/EOF character
text = text.replace(/(^|\s)(https?|ftp)(:\/\/[-A-Z0-9+&@#\/%?=~_|\[\]\(\)!:,\.;]*[-A-Z0-9+&@#\/%=~_|\[\]\)])($|\W)/gi, "$1<$2$3>$4"); // simulating the lookbehind in a consuming way is okay here, since a URL can neither and with a " nor
// with a <, so there is no risk of overlapping matches.
text = text.replace(/(="|<)?\b(https?|ftp)(:\/\/[-A-Z0-9+&@#\/%?=~_|\[\]\(\)!:,\.;]*[-A-Z0-9+&@#\/%=~_|\[\])])(?=$|\W)/gi, handleTrailingParens);
// autolink anything like <http://example.com> // autolink anything like <http://example.com>
var replacer = function (wholematch, m1) { var replacer = function (wholematch, m1) { return "<a href=\"" + m1 + "\">" + pluginHooks.plainLinkText(m1) + "</a>"; }
m1encoded = m1.replace(/\_\_/, '%5F%5F');
return "<a href=\"" + m1encoded + "\">" + pluginHooks.plainLinkText(m1) + "</a>";
}
text = text.replace(/<((https?|ftp):[^'">\s]+)>/gi, replacer); text = text.replace(/<((https?|ftp):[^'">\s]+)>/gi, replacer);
return text; return text;

View File

@ -36,6 +36,43 @@
isOpera: /opera/.test(nav.userAgent.toLowerCase()) isOpera: /opera/.test(nav.userAgent.toLowerCase())
}; };
var defaultsStrings = {
bold: "Strong <strong> Ctrl+B",
boldexample: "strong text",
italic: "Emphasis <em> Ctrl+I",
italicexample: "emphasized text",
link: "Hyperlink <a> Ctrl+L",
linkdescription: "enter link description here",
linkdialog: "<p><b>Insert Hyperlink</b></p><p>http://example.com/ \"optional title\"</p>",
quote: "Blockquote <blockquote> Ctrl+Q",
quoteexample: "Blockquote",
code: "Code Sample <pre><code> Ctrl+K",
codeexample: "enter code here",
image: "Image <img> Ctrl+G",
imagedescription: "enter image description here",
imagedialog: "<p><b>Insert Image</b></p><p>http://example.com/images/diagram.jpg \"optional title\"<br><br>Need <a href='http://www.google.com/search?q=free+image+hosting' target='_blank'>free image hosting?</a></p>",
olist: "Numbered List <ol> Ctrl+O",
ulist: "Bulleted List <ul> Ctrl+U",
litem: "List item",
heading: "Heading <h1>/<h2> Ctrl+H",
headingexample: "Heading",
hr: "Horizontal Rule <hr> Ctrl+R",
undo: "Undo - Ctrl+Z",
redo: "Redo - Ctrl+Y",
redomac: "Redo - Ctrl+Shift+Z",
help: "Markdown Editing Help"
};
// ------------------------------------------------------------------- // -------------------------------------------------------------------
// YOUR CHANGES GO HERE // YOUR CHANGES GO HERE
@ -44,31 +81,45 @@
// this area. // this area.
// ------------------------------------------------------------------- // -------------------------------------------------------------------
// The text that appears on the upper part of the dialog box when
// entering links.
var linkDialogText = "<p><b>Insert Hyperlink</b></p><p>http://example.com/ \"optional title\"</p>";
var imageDialogText = "<p><b>Insert Image</b></p><p>http://example.com/images/diagram.jpg \"optional title\"<br><br>Need <a href='http://www.google.com/search?q=free+image+hosting' target='_blank'>free image hosting?</a></p>";
// The default text that appears in the dialog input box when entering // The default text that appears in the dialog input box when entering
// links. // links.
var imageDefaultText = "http://"; var imageDefaultText = "http://";
var linkDefaultText = "http://"; var linkDefaultText = "http://";
var defaultHelpHoverTitle = "Markdown Editing Help";
// ------------------------------------------------------------------- // -------------------------------------------------------------------
// END OF YOUR CHANGES // END OF YOUR CHANGES
// ------------------------------------------------------------------- // -------------------------------------------------------------------
// help, if given, should have a property "handler", the click handler for the help button, // options, if given, can have the following properties:
// and can have an optional property "title" for the button's tooltip (defaults to "Markdown Editing Help"). // options.helpButton = { handler: yourEventHandler }
// If help isn't given, not help button is created. // options.strings = { italicexample: "slanted text" }
// `yourEventHandler` is the click handler for the help button.
// If `options.helpButton` isn't given, not help button is created.
// `options.strings` can have any or all of the same properties as
// `defaultStrings` above, so you can just override some string displayed
// to the user on a case-by-case basis, or translate all strings to
// a different language.
//
// For backwards compatibility reasons, the `options` argument can also
// be just the `helpButton` object, and `strings.help` can also be set via
// `helpButton.title`. This should be considered legacy.
// //
// The constructed editor object has the methods: // The constructed editor object has the methods:
// - getConverter() returns the markdown converter object that was passed to the constructor // - getConverter() returns the markdown converter object that was passed to the constructor
// - run() actually starts the editor; should be called after all necessary plugins are registered. Calling this more than once is a no-op. // - run() actually starts the editor; should be called after all necessary plugins are registered. Calling this more than once is a no-op.
// - refreshPreview() forces the preview to be updated. This method is only available after run() was called. // - refreshPreview() forces the preview to be updated. This method is only available after run() was called.
Markdown.Editor = function (markdownConverter, idPostfix, help) { Markdown.Editor = function (markdownConverter, idPostfix, options) {
options = options || {};
if (typeof options.handler === "function") { //backwards compatible behavior
options = { helpButton: options };
}
options.strings = options.strings || {};
if (options.helpButton) {
options.strings.help = options.strings.help || options.helpButton.title;
}
var getString = function (identifier) { return options.strings[identifier] || defaultsStrings[identifier]; }
idPostfix = idPostfix || ""; idPostfix = idPostfix || "";
@ -90,7 +141,7 @@
return; // already initialized return; // already initialized
panels = new PanelCollection(idPostfix); panels = new PanelCollection(idPostfix);
var commandManager = new CommandManager(hooks); var commandManager = new CommandManager(hooks, getString);
var previewManager = new PreviewManager(markdownConverter, panels, function () { hooks.onPreviewRefresh(); }); var previewManager = new PreviewManager(markdownConverter, panels, function () { hooks.onPreviewRefresh(); });
var undoManager, uiManager; var undoManager, uiManager;
@ -107,7 +158,7 @@
} }
} }
uiManager = new UIManager(idPostfix, panels, undoManager, previewManager, commandManager, help); uiManager = new UIManager(idPostfix, panels, undoManager, previewManager, commandManager, options.helpButton, getString);
uiManager.setUndoRedoButtonStates(); uiManager.setUndoRedoButtonStates();
var forceRefresh = that.refreshPreview = function () { previewManager.refresh(true); }; var forceRefresh = that.refreshPreview = function () { previewManager.refresh(true); };
@ -536,23 +587,21 @@
var handled = false; var handled = false;
if (event.ctrlKey || event.metaKey) { if ((event.ctrlKey || event.metaKey) && !event.altKey) {
// IE and Opera do not support charCode. // IE and Opera do not support charCode.
var keyCode = event.charCode || event.keyCode; var keyCode = event.charCode || event.keyCode;
var keyCodeChar = String.fromCharCode(keyCode); var keyCodeChar = String.fromCharCode(keyCode);
switch (keyCodeChar) { switch (keyCodeChar.toLowerCase()) {
case "y": case "y":
case "Y":
if (!event.shiftKey) { if (!event.shiftKey) {
undoObj.redo(); undoObj.redo();
handled = true; handled = true;
} }
break; break;
case "Z":
case "z": case "z":
if (!event.shiftKey) { if (!event.shiftKey) {
undoObj.undo(); undoObj.undo();
@ -616,7 +665,7 @@
util.addEvent(panels.input, "keypress", function (event) { util.addEvent(panels.input, "keypress", function (event) {
// keyCode 89: y // keyCode 89: y
// keyCode 90: z // keyCode 90: z
if ((event.ctrlKey || event.metaKey) && (event.keyCode == 89 || event.keyCode == 90)) { if ((event.ctrlKey || event.metaKey) && !event.altKey && (event.keyCode == 89 || event.keyCode == 90)) {
event.preventDefault(); event.preventDefault();
} }
}); });
@ -1226,7 +1275,7 @@
}, 0); }, 0);
}; };
function UIManager(postfix, panels, undoManager, previewManager, commandManager, helpOptions) { function UIManager(postfix, panels, undoManager, previewManager, commandManager, helpOptions, getString) {
var inputBox = panels.input, var inputBox = panels.input,
buttons = {}; // buttons.undo, buttons.link, etc. The actual DOM elements. buttons = {}; // buttons.undo, buttons.link, etc. The actual DOM elements.
@ -1465,33 +1514,33 @@
} }
} }
buttons.bold = makeButton("wmd-bold-button", "Strong <strong> Ctrl+B", bindCommand("doBold")); buttons.bold = makeButton("wmd-bold-button", getString("bold"), bindCommand("doBold"));
buttons.italic = makeButton("wmd-italic-button", "Emphasis <em> Ctrl+I", bindCommand("doItalic")); buttons.italic = makeButton("wmd-italic-button", getString("italic"), bindCommand("doItalic"));
makeSpacer(1); makeSpacer(1);
buttons.link = makeButton("wmd-link-button", "Hyperlink <a> Ctrl+L", bindCommand(function (chunk, postProcessing) { buttons.link = makeButton("wmd-link-button", getString("link"), bindCommand(function (chunk, postProcessing) {
return this.doLinkOrImage(chunk, postProcessing, false); return this.doLinkOrImage(chunk, postProcessing, false);
})); }));
buttons.quote = makeButton("wmd-quote-button", "Blockquote <blockquote> Ctrl+Q",bindCommand("doBlockquote")); buttons.quote = makeButton("wmd-quote-button", getString("quote"), bindCommand("doBlockquote"));
buttons.code = makeButton("wmd-code-button", "Preformatted text <pre><code> Ctrl+K", bindCommand("doCode")); buttons.code = makeButton("wmd-code-button", getString("code"), bindCommand("doCode"));
buttons.image = makeButton("wmd-image-button", "Image <img> Ctrl+G", bindCommand(function (chunk, postProcessing) { buttons.image = makeButton("wmd-image-button", getString("image"), bindCommand(function (chunk, postProcessing) {
return this.doLinkOrImage(chunk, postProcessing, true); return this.doLinkOrImage(chunk, postProcessing, true);
})); }));
makeSpacer(2); makeSpacer(2);
buttons.olist = makeButton("wmd-olist-button", "Numbered List <ol> Ctrl+O", bindCommand(function (chunk, postProcessing) { buttons.olist = makeButton("wmd-olist-button", getString("olist"), bindCommand(function (chunk, postProcessing) {
this.doList(chunk, postProcessing, true); this.doList(chunk, postProcessing, true);
})); }));
buttons.ulist = makeButton("wmd-ulist-button", "Bulleted List <ul> Ctrl+U", bindCommand(function (chunk, postProcessing) { buttons.ulist = makeButton("wmd-ulist-button", getString("ulist"), bindCommand(function (chunk, postProcessing) {
this.doList(chunk, postProcessing, false); this.doList(chunk, postProcessing, false);
})); }));
buttons.heading = makeButton("wmd-heading-button", "Heading <h1>/<h2> Ctrl+H", bindCommand("doHeading")); buttons.heading = makeButton("wmd-heading-button", getString("heading"), bindCommand("doHeading"));
buttons.hr = makeButton("wmd-hr-button", "Horizontal Rule <hr> Ctrl+R", bindCommand("doHorizontalRule")); buttons.hr = makeButton("wmd-hr-button", getString("hr"), bindCommand("doHorizontalRule"));
makeSpacer(3); makeSpacer(3);
buttons.undo = makeButton("wmd-undo-button", "Undo - Ctrl+Z", null); buttons.undo = makeButton("wmd-undo-button", getString("undo"), null);
buttons.undo.execute = function (manager) { if (manager) manager.undo(); }; buttons.undo.execute = function (manager) { if (manager) manager.undo(); };
var redoTitle = /win/.test(nav.platform.toLowerCase()) ? var redoTitle = /win/.test(nav.platform.toLowerCase()) ?
"Redo - Ctrl+Y" : getString("redo") :
"Redo - Ctrl+Shift+Z"; // mac and other non-Windows platforms getString("redomac"); // mac and other non-Windows platforms
buttons.redo = makeButton("wmd-redo-button", redoTitle, null); buttons.redo = makeButton("wmd-redo-button", redoTitle, null);
buttons.redo.execute = function (manager) { if (manager) manager.redo(); }; buttons.redo.execute = function (manager) { if (manager) manager.redo(); };
@ -1504,7 +1553,7 @@
helpButton.id = "wmd-help-button" + postfix; helpButton.id = "wmd-help-button" + postfix;
helpButton.isHelp = true; helpButton.isHelp = true;
helpButton.style.right = "0px"; helpButton.style.right = "0px";
helpButton.title = helpOptions.title || defaultHelpHoverTitle; helpButton.title = getString("help");
helpButton.onclick = helpOptions.handler; helpButton.onclick = helpOptions.handler;
setupButton(helpButton, true); setupButton(helpButton, true);
@ -1526,8 +1575,9 @@
} }
function CommandManager(pluginHooks) { function CommandManager(pluginHooks, getString) {
this.hooks = pluginHooks; this.hooks = pluginHooks;
this.getString = getString;
} }
var commandProto = CommandManager.prototype; var commandProto = CommandManager.prototype;
@ -1557,11 +1607,11 @@
}; };
commandProto.doBold = function (chunk, postProcessing) { commandProto.doBold = function (chunk, postProcessing) {
return this.doBorI(chunk, postProcessing, 2, "strong text"); return this.doBorI(chunk, postProcessing, 2, this.getString("boldexample"));
}; };
commandProto.doItalic = function (chunk, postProcessing) { commandProto.doItalic = function (chunk, postProcessing) {
return this.doBorI(chunk, postProcessing, 1, "emphasized text"); return this.doBorI(chunk, postProcessing, 1, this.getString("italicexample"));
}; };
// chunk: The selected region that will be enclosed with */** // chunk: The selected region that will be enclosed with */**
@ -1697,7 +1747,7 @@
}); });
if (title) { if (title) {
title = title.trim ? title.trim() : title.replace(/^\s*/, "").replace(/\s*$/, ""); title = title.trim ? title.trim() : title.replace(/^\s*/, "").replace(/\s*$/, "");
title = $.trim(title).replace(/"/g, "quot;").replace(/\(/g, "&#40;").replace(/\)/g, "&#41;").replace(/</g, "&lt;").replace(/>/g, "&gt;"); title = title.replace(/"/g, "quot;").replace(/\(/g, "&#40;").replace(/\)/g, "&#41;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
} }
return title ? link + ' "' + title + '"' : link; return title ? link + ' "' + title + '"' : link;
}); });
@ -1764,10 +1814,10 @@
if (!chunk.selection) { if (!chunk.selection) {
if (isImage) { if (isImage) {
chunk.selection = "enter image description here"; chunk.selection = that.getString("imagedescription");
} }
else { else {
chunk.selection = "enter link description here"; chunk.selection = that.getString("linkdescription");
} }
} }
} }
@ -1778,10 +1828,10 @@
if (isImage) { if (isImage) {
if (!this.hooks.insertImageDialog(linkEnteredCallback)) if (!this.hooks.insertImageDialog(linkEnteredCallback))
ui.prompt(imageDialogText, imageDefaultText, linkEnteredCallback); ui.prompt(this.getString("imagedialog"), imageDefaultText, linkEnteredCallback);
} }
else { else {
ui.prompt(linkDialogText, linkDefaultText, linkEnteredCallback); ui.prompt(this.getString("linkdialog"), linkDefaultText, linkEnteredCallback);
} }
return true; return true;
} }
@ -1848,7 +1898,7 @@
}); });
chunk.selection = chunk.selection.replace(/^(\s|>)+$/, ""); chunk.selection = chunk.selection.replace(/^(\s|>)+$/, "");
chunk.selection = chunk.selection || "Blockquote"; chunk.selection = chunk.selection || this.getString("quoteexample");
// The original code uses a regular expression to find out how much of the // The original code uses a regular expression to find out how much of the
// text *directly before* the selection already was a blockquote: // text *directly before* the selection already was a blockquote:
@ -2005,7 +2055,7 @@
if (!chunk.selection) { if (!chunk.selection) {
chunk.startTag = " "; chunk.startTag = " ";
chunk.selection = "enter code here"; chunk.selection = this.getString("codeexample");
} }
else { else {
if (/^[ ]{0,3}\S/m.test(chunk.selection)) { if (/^[ ]{0,3}\S/m.test(chunk.selection)) {
@ -2015,7 +2065,7 @@
chunk.before += " "; chunk.before += " ";
} }
else { else {
chunk.selection = chunk.selection.replace(/^[ ]{4}/gm, ""); chunk.selection = chunk.selection.replace(/^(?:[ ]{4}|[ ]{0,3}\t)/gm, "");
} }
} }
} }
@ -2028,7 +2078,7 @@
if (!chunk.startTag && !chunk.endTag) { if (!chunk.startTag && !chunk.endTag) {
chunk.startTag = chunk.endTag = "`"; chunk.startTag = chunk.endTag = "`";
if (!chunk.selection) { if (!chunk.selection) {
chunk.selection = "enter code here"; chunk.selection = this.getString("codeexample");
} }
} }
else if (chunk.endTag && !chunk.startTag) { else if (chunk.endTag && !chunk.startTag) {
@ -2122,7 +2172,7 @@
}); });
if (!chunk.selection) { if (!chunk.selection) {
chunk.selection = "List item"; chunk.selection = this.getString("litem");
} }
var prefix = getItemPrefix(); var prefix = getItemPrefix();
@ -2154,7 +2204,7 @@
// make a level 2 hash header around some default text. // make a level 2 hash header around some default text.
if (!chunk.selection) { if (!chunk.selection) {
chunk.startTag = "## "; chunk.startTag = "## ";
chunk.selection = "Heading"; chunk.selection = this.getString("headingexample");
chunk.endTag = " ##"; chunk.endTag = " ##";
return; return;
} }