...
...sequences * to become one single (unified)
sequence.
*/
sMD = sMD.replace(/<\/pre>(\s*)/g, "\n\n").replace(/<\/code>(\s*)/g, "$1");
this.sHTML = sMD;
return this.sHTML;
};
/**
* convertMDBlocks(sMD, sIndent)
*
* If your text may contain some block markers (ie, double-linefeeds), or headers (either "Atx-style"
* or "Setext-style) that require the insertion of double-linefeed block markers, then call this function.
*
* @this {MarkOut}
* @param {string} sMD
* @param {string} [sIndent]
*/
MarkOut.prototype.convertMDBlocks = function(sMD, sIndent)
{
var sHTML = "";
/*
* Convert all "Atx-style headers" (ie, series of leading hashmarks) to their equivalents.
*
* A slight tweak to standard Markdown: if there are equal numbers of hashmarks on either side of the
* text, we center it.
*/
sMD = sMD.replace(/(^|\n)(#+)\s+(.*?)\2(\n|$)/g, '$1$3 \n\n');
sMD = sMD.replace(/(^|\n)(#+)\s+(.*?)#*(\n|$)/g, "$1$3 \n\n");
sMD = str.replaceArray({"":"h6>", "h#####>":"h5>", "h####>":"h4>", "h###>":"h3>", "h##>":"h2>", "h#>":"h1>"}, sMD);
/*
* Convert all "Setext-style headers" (ie, series of equal-signs or dashes) to their equivalents.
*/
sMD = sMD.replace(/([^\n]+)\n([=-])[=-]*(\n|$)/g, "$1 \n\n");
sMD = str.replaceArray({"h=>":"h1>", "h->":"h2>"}, sMD);
/*
* Auto-generate IDs for headings
*/
var match;
var re = /<(h[0-9])>([^<]*)<\/\1>/g;
while ((match = re.exec(sMD))) {
var sID = this.generateID(match[2]);
if (sID) {
sMD = sMD.replace(match[0], '<' + match[1] + ' id="' + sID + '">' + match[2] + '' + match[1] + '>');
/*
* Since the replacement is guaranteed to be longer than the original, no need to worry about re.lastIndex
*/
}
}
/*
* The preceding replacements used to end with a match on "(\n|$)+", but in cases where there were consecutive
* headings, the first match would "eat" all the linefeeds between them, and so the second heading would not get
* matched. However, a side-effect of not "eating" all those linefeeds is that we can end up with too many of them
* after all the heading replacements are done.
*
* So, even though we already "normalized" all consecutive linefeeds in convertMD(), we have to do it again
* (although this normalization is simpler, as we don't have to worry about any other whitespace).
*/
sMD = sMD.replace(/\n\n+/g, "\n\n");
/*
* Explode the given Markdown sequence into blocks based purely on double-linefeed sequences.
*/
var asBlocks = sMD.split("\n\n");
/*
* Also, for any block that begins with triple-backtick but doesn't end with one, try to find the matching
* end block, and then merge them all into a single block. TODO: This is a hack; fenced blocks need to be
* processed earlier, not in convertMDBlock(), but this is OK for now.
*/
var iBlock = 0;
while (iBlock < asBlocks.length) {
var fCodeBlock = false;
var sBlock = asBlocks[iBlock++];
if (sBlock.substr(0,3) == "```") {
fCodeBlock = true;
var sBlockEnd = sBlock;
while (sBlockEnd.slice(-3) != "```" && iBlock < asBlocks.length) {
sBlockEnd = asBlocks[iBlock++];
sBlock += "\n\n" + sBlockEnd;
}
}
sHTML += this.convertMDBlock(sBlock, sIndent);
if (fCodeBlock) sHTML += this.convertMDBlock("", sIndent);
}
return sHTML;
};
/**
* convertMDBlock(sBlock, sIndent)
*
* @this {MarkOut}
* @param {string} sBlock
* @param {string} [sIndent]
*/
MarkOut.prototype.convertMDBlock = function(sBlock, sIndent)
{
var sHTML = "";
// this.addIndent(sIndent);
// if (this.fDebug) sHTML += this.encodeComment("convertMDBlock", sBlock);
/*
* Look for "quoted" paragraphs that should be wrapped with .
*
* This is a recursive operation, so this code does not need to "fall into" the other conversions.
*/
if (sBlock.match(/^>\s+/)) {
sBlock = sBlock.replace(/(^|\n)>\s+/g, "$1");
sHTML += this.sIndent + "\n" + this.convertMDBlocks(sBlock, sIndent) + this.sIndent + "
\n";
return sHTML;
}
/*
* Look for indented paragraphs that should be converted to blocks.
*
* No other conversions should occur in such a block, so we don't "fall into" the other conversions.
*/
var aMatch;
var re = /^((^|\n)( {4}|\t)([^\n]*))+$/;
if ((aMatch = re.exec(sBlock))) {
var sUndented = aMatch[0].replace(/(^|\n)( {4}|\t)([^\n]*)/g, "$1$3");
sBlock = sBlock.replace(aMatch[0], "" + str.escapeHTML(sUndented.replace(/\t/g, " ")) + "
");
sHTML += this.sIndent + sBlock + "\n";
return sHTML;
}
/*
* Look for GFM ("GitHub Flavored Markdown") "fenced code blocks", which are basically code blocks
* wrapped by "triple-backticks" instead of being indented.
*
* No other conversions should occur in such a block, so we don't "fall into" the other conversions.
*
* TODO: GFM doesn't require fenced code blocks to be preceded/followed by blank lines, so if we want
* to support those as well, it will be up to convertMD() to detect them and parse them into discrete blocks.
*/
re = /^```([^\n]*)\n([\s\S]*?)```$/;
if ((aMatch = re.exec(sBlock))) {
sBlock = sBlock.replace(aMatch[0], "" + str.escapeHTML(aMatch[2]) + "
");
sHTML += this.sIndent + sBlock + "\n";
return sHTML;
}
/*
* Convert any "double-backtick" sequences into sequences, with inner backticks
* treated as literal; we translate those to HTML entity "`" to prevent them from being
* detected as part of a "single-backtick" sequence below.
*
* Note that we also do entity replacement AFTER calling escapeHTML(), our simplified version
* of PHP's htmlspecialchars(), because it isn't smart enough to avoid the "double-encoding"
* problem (ie, translating the leading "&" of an entity into yet another "&" entity).
*/
var sBlockOrig = sBlock;
re = /``(.*?)``/g;
while ((aMatch = re.exec(sBlockOrig))) {
sBlock = sBlock.replace(aMatch[0], "" + str.escapeHTML(aMatch[1]).replace(/`/g, "`") + "");
}
/*
* Convert any remaining "single-backtick" sequences into sequences as well.
*/
sBlockOrig = sBlock;
re = /`(.*?)`/g;
while ((aMatch = re.exec(sBlockOrig))) {
sBlock = sBlock.replace(aMatch[0], "" + str.escapeHTML(aMatch[1]) + "");
}
/*
* As mentioned at the top of convertMD(), the Markdown escape-sequence-to-HTML-entity conversion
* has been moved here, after we've dealt with code blocks and escapeHTML() operations, in an effort
* to avoid HTML entity "double-encoding" issues.
*/
sBlock = str.replaceArray(MarkOut.aHTMLEntities, sBlock);
/*
* Per markdown syntax: "When you do want to insert a
break tag using Markdown,
* you end a line with two or more spaces, then type return."
*/
sBlock = sBlock.replace(/ {2,}\n/g, "
\n" + this.sIndent);
/*
* If the block looks like a list, convertMDList() will convert it; if not, then it will wrap the
* block with paragraph tags -- assuming the block isn't already wrapped with some sort of block markup.
*/
sBlock = this.convertMDList(sBlock, sIndent);
/*
* Process any "image" Markdown links first (since they can be misinterpreted as "normal" links);
* this ordering also allows image links to be wrapped by "normal" Markdown links, if you want to
* turn images into links, as in:
*
* [](http://google.com.au/)
*
* However, we also offer a "link:" extension to the title attribute; as in:
*
* 
*
* which has the same effect but ALSO wraps the image in a special "image link" class and displays the
* "Alt text" as a label underneath the image; and if the block consists entirely of such images, then all
* the images are wrapped in an "image gallery" class.
*/
sBlock = this.convertMDImageLinks(sBlock, sIndent);
/*
* Process any special "machine" Markdown-style links next.
*/
sBlock = this.convertMDMachineLinks(sBlock);
/*
* Now we can finally process "normal" Markdown links.
*/
sBlock = this.convertMDLinks(sBlock);
/*
* Finally, look for all assorted forms of Markdown emphasis (there's no particular reason we do it last, we just do).
*/
sBlock = this.convertMDEmphasis(sBlock);
sHTML += this.sIndent + sBlock + "\n";
// this.subIndent(sIndent);
return sHTML;
};
/**
* convertMDList(sBlock, sIndent)
*
* If the block begins with an unordered list marker, create an unordered list;
* similarly, if the block begins with an ordered list marker, create an ordered list.
*
* Otherwise, wrap the block with paragraph tags in the absence of any other block markup.
*
* HACK: To allow paragraphs to appear within list items, convertMD() found all lines that
* began with an indented list marker and were followed by a series of indented and/or blank
* lines, and replaced all double-linefeeds in that series with "\n\t\n". Hence, this
* function must compensate by replacing "\n\t\n" sequences in any sub-lists with the normal
* "\n\n" and calling convertMDBlocks().
*
* FEATURE: If you use "*" as your unordered list marker (instead of "+" or "-", all of which
* Markdown apparently treats equivalently), I automatically use a list style that omits bullets.
* So, if you REALLY want bullets, use "-" or "+".
*
* @this {MarkOut}
* @param {string} sBlock
* @param {string} sIndent
* @return {string}
*/
MarkOut.prototype.convertMDList = function(sBlock, sIndent)
{
var sIndentPrev = this.addIndent(sIndent);
var aMatch;
var aMatches = [];
var re = /(^|\n) ? ? ?([*+-]|[0-9]+\.)[ \t]+/g;
while ((aMatch = re.exec(sBlock))) {
aMatches.push(aMatch);
}
if (aMatches.length && aMatches[0].index === 0) {
aMatch = aMatches[0];
var sListType = aMatch[2].charAt(0);
var sListStyle = "md-list" + (sListType == '*'? " md-list-none" : (sListType == '-'? " md-list-compact" : ""));
sListType = (sListType >= '0' && sListType <= '9')? "ol" : "ul";
var sList = '<' + sListType + ' class="' + sListStyle + '">\n';
for (var iMatch = 0; iMatch < aMatches.length; iMatch++) {
aMatch = aMatches[iMatch];
var iStart = aMatch.index + aMatch[0].length;
var iStop = iMatch < aMatches.length-1? aMatches[iMatch+1].index : sBlock.length;
var sListItem = sBlock.substr(iStart, iStop - iStart);
/*
* If this list item contains one or more lines indented by 4 or more spaces (or 1 or more tabs)
* then we need to strip them, so that they can be parsed as a sub-list.
*/
re = /((^|\n)( {4}|\t)([^\n]*))+/;
if ((aMatch = re.exec(sListItem))) {
// if (this.fDebug) sList += this.encodeComment("subList", aMatch[0]);
var sSubList = aMatch[0].replace(/(^|\n)( {4}|\t)([^\n]*)/g, "$1$3").replace(/\n\t\n/g, "\n\n");
if (sSubList.charAt(0) == "\n") sSubList = sSubList.substr(1);
sListItem = str.replaceAll(aMatch[0], "\n" + this.sIndent + this.convertMDBlocks(sSubList, sIndent).trim() + "\n" + this.sIndent, sListItem);
} else {
sListItem = this.convertMDLines(sListItem);
}
sList += this.sIndent + "" + sListItem + " \n";
}
sList += sIndentPrev + "" + sListType + ">";
sBlock = sList;
}
else {
/*
* In the absence of any other block markup, wrap the block in paragraph tags.
*
* TODO: The test here is currently for *any* HTML markup; this should probably be tightened up.
*/
if (sBlock.charAt(0) != "<") sBlock = "" + this.convertMDLines(sBlock) + "
";
}
this.subIndent(sIndent);
return sBlock;
};
/**
* convertMDLines(s)
*
* This function is purely cosmetic. The intent is to indent all the lines in multi-line items
* (eg, paragraphs, list items), so that the resulting HTML is a bit more readable. Unfortunately,
* it has some unintended side-effects (for example, it creates unnecessary indentation inside image
* galleries, which are nothing more than paragraphs containing a series of image links), so this
* code is disabled for now.
*
* @this {MarkOut}
* @param {string} s
* @return {string}
*/
MarkOut.prototype.convertMDLines = function(s)
{
return s;
// return s.replace(/\n/g, "\n" + this.sIndent).trim();
};
/**
* convertMDLinks(sBlock)
*
* Aside from basic "inline" Markdown links, we also support named anchors; if the link begins with '!',
* we strip the '!' and use the remainder of the link as the name of the anchor. To reference a named
* anchor from another link, specify a path with '#' and the anchor name appended.
*
* Note that the need for named anchors is somewhat diminished now that I automatically generate IDs for
* all heading tags (eg, ); refer to the generateID() function that's used in convertMDBlocks().
*
* Another extension to Markdown that I've added is detecting empty parentheses alongside a likely URL,
* and automatically converting it to a link; eg:
*
* [http://www.ascii-code.com/]()
*
* Also, if a URL contains any asterisks, we replace them with the current version number from "package.json".
*
* I prefer this solution over GFM's "autolinking" solution, which is too "loosey-goosey" for my taste
* (see https://help.github.com/articles/github-flavored-markdown#url-autolinking).
*
* TODO: Consider adding support for "reference"-style Markdown links.
*
* @this {MarkOut}
* @param {string} sBlock
* @return {string}
*/
MarkOut.prototype.convertMDLinks = function(sBlock)
{
/*
* Before we start replacing Markdown links, see if there are any Liquid-style replacements
* (in case this Markdown file is part of a Jekyll installation) and remove them.
*
* TODO: Any double-brace replacements should use appropriate values from _config.yml or the
* page's Front Matter; however, unless/until we start using Node again to host the public site,
* that's low priority.
*/
sBlock = sBlock.replace(/([^\t]){([{%]).*?\2}/g, "$1");
sBlock = sBlock.replace(/({)([{%])(.*?\2})/g, "$1$2$3
");
var aMatch;
var re = /\[([^\[\]]*)]\((.*?)(?:\s*"(.*?)"\)|\))/g;
while ((aMatch = re.exec(sBlock))) {
var sTag = "a";
var sType = "href";
var sText = aMatch[1];
var sURL = aMatch[2];
var sTitle = (aMatch[3]? ' title="' + aMatch[3] + '"' : '');
if (!sURL) { // if the parentheses are empty and the text (kinda) looks like a URL, use the text as the URL, too
if (sText.match(/^[a-z]+:/) || sText.match(/^\/(.*)\/$/)) {
sURL = sText;
} else if (sText.match(/(^www\.|\.com|\.org|\.net|\.io)/)) {
sURL = "http://" + sText;
} else if (sText.indexOf(' ') < 0 && sText.indexOf('.') > 0) {
sURL = sText; // we assume you're trying to automatically link to a filename
}
}
sURL = sURL.replace(/\*/g, pkg.version);
/*
* Check for my own syntax for defining a named anchor (by using an exclamation point) in Markdown....
*/
if (sURL.charAt(0) == '!') {
sTag = "span"; // using to name an anchor is deprecated
sType = "id"; // using the "name" attribute is deprecated as well
sURL = sURL.substr(1);
} else {
sURL = net.encodeURL(sURL, this.req, this.fDebug);
}
sURL = sURL.replace(/_/g, "%5F"); // this helps prevent emphasis detection in URLs
sBlock = str.replaceAll(aMatch[0], '<' + sTag + ' ' + sType + '="' + sURL + '"' + sTitle + '>' + sText + '' + sTag + '>', sBlock);
}
return sBlock;
};
/**
* convertMDImageLinks(sBlock)
*
* @this {MarkOut}
* @param {string} sBlock
* @param {string} sIndent
* @return {string}
*/
MarkOut.prototype.convertMDImageLinks = function(sBlock, sIndent)
{
/*
* Before we start looking for Markdown-style image links, see if there are any Liquid-style images,
* (in case this Markdown file is part of a Jekyll installation) and convert them to Markdown-style links.
*/
var aMatch;
var reIncludes = /{%\s*include\s+screenshot\.html\s+(.*?)\s*%}/g;
while ((aMatch = reIncludes.exec(sBlock))) {
var option, aOptions = {};
var reOptions = /([^\s]+)=(['"])(.*?)\2/g;
while ((option = reOptions.exec(aMatch[1]))) {
aOptions[option[1]] = option[3];
}
var sReplacement = "![" + aOptions['title'] + "](" + aOptions['src'] + ' "link:' + aOptions['link'] + ':' + aOptions['width'] + ':' + aOptions['height'] + '")';
sBlock = sBlock.replace(aMatch[0], sReplacement);
reIncludes.lastIndex = 0; // reset lastIndex, since we just modified the string that reIncludes is iterating over
}
/*
* Look for image links of the form  and convert them. We do this before processing non-image
* ("normal") links, since the only difference in syntax is the presence of a preceding exclamation point (!).
*
* We also extend the syntax a bit, by allowing the optional title string inside the parentheses to include
* a special "link:" prefix; if that prefix is present, we will wrap the image with the URL following that prefix,
* and then wrap THAT with the special image classes, if any, that we were given at initialization.
*/
var cImageLinks = 0;
var fNoGallery = false;
var sBlockOrig = sBlock;
var re = /!\[(.*?)]\((.*?)(?:\s*"(.*?)"\)|\))/g;
while ((aMatch = re.exec(sBlockOrig))) {
var sImage = '
= 0 && sURL.indexOf("://") > 0 && (this.fDebug || net.hasParm(net.REVEAL_COMMAND, net.REVEAL_PDFS, this.req))) {
sURL = aMatch[2].replace("/thumbs/", "/").replace(" 1.jpeg", ".pdf").replace(".jpg", ".pdf");
}
sURL = net.encodeURL(sURL, this.req, this.fDebug);
if (asParts[iPart] == "nogallery") {
fNoGallery = true;
iPart++;
}
this.addIndent(sIndent);
var sID = this.generateID(aMatch[1]);
sID = (sID? (' id="' + sID + '"') : "");
var sImageLink = this.sIndent + '\n';
this.addIndent(sIndent);
sImageLink += this.sIndent + '\n';
this.addIndent(sIndent);
sImageLink += this.sIndent + (sURL? '' : '') + sImage;
if (asParts[iPart]) {
sImageLink += ' width="' + asParts[iPart++] + '"';
}
if (asParts[iPart]) {
sImageLink += ' height="' + asParts[iPart++] + '"';
}
sImageLink += "/>" + (sURL? "" : "");
this.subIndent(sIndent);
sImageLink += this.sIndent + '\n';
sImageLink += this.sIndent + '' + aMatch[1] + '\n';
this.subIndent(sIndent);
sImageLink += this.sIndent + '';
this.subIndent(sIndent);
sImage = sImageLink;
cImageLinks++;
} else {
sImage += ' title="' + aMatch[3] + '"/>';
}
} else {
sImage += '/>';
}
sBlock = sBlock.replace(aMatch[0], sImage);
}
if (cImageLinks && !fNoGallery) {
sBlock = sBlock.replace(/^([\s\S]*)<\/p>$/g, '
\n$1\n' + this.sIndent + '');
}
return sBlock;
};
/**
* convertMDMachineLinks(sBlock)
*
* Before we call convertMDLinks() to process any normal Markdown-style links, we first look for our own
* special flavor of "machine" Markdown links; ie:
*
* [IBM PC](/devices/pcx86/machine/5150/mda/64kb/ "PCx86:demoPC:stylesheet:version:options:parms")
*
* where a special title attribute triggers generation of an embedded machine rather than a link.
*
* Use "PCx86" or "C1P" to automatically include the latest version of either "pcx86.js" or "c1p.js", followed
* by a colon and the ID you want to use for the embedded . If you need to use the script with the built-in
* Debugger (ie, either "pcx86-dbg.js" or "c1p-dbg.js"), then include "debugger" in the list of comma-delimited
* options, as in:
*
* [IBM PC](/devices/pcx86/machine/5150/mda/64kb/ "PCx86:demoPC:::debugger")
*
* If the link ends with a slash, then it's an implied reference to a "machine.xml".
*
* UPDATE: Since parms containing JSON may also contain colons, machine Markdown links may now use '!' or '|'
* instead of ':' as separators. In fact, whichever separator is used first will be used throughout; eg:
*
* [IBM PC](/devices/pcx86/machine/5150/mda/64kb/ "PCx86!demoPC!stylesheet!version!options!parms")
*
* Granted, there are a number of things we could be smarter about. First, you probably don't care about the
* ID for the ; it's purely a mechanism for telling the script where to embed the machine, so we could
* auto-generate an ID for you, but on the other hand, there might actually be situations where you want to style
* the machine a certain way, or interact with it from another script, so having a known ID can be a good thing.
*
* Second, if we know we're running on the same server as the machine XML file in the link, we could crack
* that file open right now, see if it includes a , and automatically include the appropriate script,
* without requiring the user to specify that. And in fact, that's exactly what the "machine.xsl" stylesheet
* does: it calls componentScripts() with the appropriate script based on the presence of a element
* in the XML file. That's similar to what getMachineXML() in htmlout.js does, when it's loading a "machine.xml".
*
* We don't have the XML file open here, and I don't think it's worth the hit to open it. Besides, the XML
* config file isn't necessarily on the same server (although whenever this script is being used, it very likely is).
*
* TODO: Consider cracking open the XML file anyway, even though the Markdown module is supposed to be non-blocking;
* I'd like to be smarter about defaults (eg, specifying "debugger" when the XML file clearly needs it).
*
* @this {MarkOut}
* @param {string} sBlock
* @return {string}
*/
MarkOut.prototype.convertMDMachineLinks = function(sBlock)
{
var aMatch, sReplacement, machine;
var sMachineType, sMachineID, sMachineXMLFile, sMachineXSLFile, sMachineVersion, sMachineOptions, sMachineParms;
/*
* Before we start looking for Markdown-style machine links, see if there are any Liquid-style machines,
* (in case this Markdown file is part of a Jekyll installation) and convert them to Markdown-style links.
*/
var reIncludes = /(.){%\s*include\s+machine\.html\s+id=(["'])(.*?)\2\s*%}/g;
while ((aMatch = reIncludes.exec(sBlock))) {
if (aMatch[1] == '\t') continue;
sReplacement = "";
sMachineID = aMatch[3];
if (this.aMachineDefs[sMachineID]) {
machine = this.aMachineDefs[sMachineID];
sMachineType = machine['type'] || "PCx86";
sMachineXMLFile = machine['config'] || this.sMachineFile || "machine.xml";
if (sMachineXMLFile.indexOf("debugger") >= 0) machine['debugger'] = "true";
sMachineOptions = ((sMachineType.indexOf("-dbg") > 0 || machine['debugger'] == "true")? "debugger" : "");
if (machine['sticky']) sMachineOptions += (sMachineOptions? "," : "") + "sticky";
sMachineType = sMachineType.replace("-dbg", "");
sMachineXSLFile = machine['template'] || "";
sMachineVersion = ((machine['uncompiled'] == "true")? "uncompiled" : "");
sMachineParms = machine['parms'] || "";
sReplacement = machine['name'] || "Embedded PC";
sReplacement = "[" + sReplacement + "](" + sMachineXMLFile + ' "' + sMachineType + '!' + sMachineID + '!' + sMachineXSLFile + '!!' + sMachineOptions + '!' + sMachineParms + '")';
}
sBlock = str.replace(aMatch[0].substr(1), sReplacement, sBlock);
reIncludes.lastIndex = 0; // reset lastIndex, since we just modified the string that reIncludes is iterating over
}
/*
* Ditto for any Liquid-style machine build links.
*/
sBlock = sBlock.replace(/{%\s*include\s+machine-build\.html\s+id=(["'])(.*?)\1\s*%}/g, '');
/*
* Start looking for Markdown-style machine links now...
*/
var cMatches = 0;
var reMachines = /\[(.*?)]\((.*?)\s*"(PC|C1P|PDP)([^:!|]*)([:!|])(.*?)"\)/gi;
while ((aMatch = reMachines.exec(sBlock))) {
sMachineXMLFile = aMatch[2];
if (sMachineXMLFile.slice(-1) == "/") sMachineXMLFile += "machine.xml";
sMachineType = aMatch[3].toUpperCase() + (aMatch[4] != "js"? aMatch[4] : "");
var sMachineFunc = "embed" + sMachineType;
var aMachineParms = aMatch[6].split(aMatch[5]);
var sMachineMessage = "Waiting for " + sMachineType + " to load";
sMachineID = aMachineParms[0];
sMachineXSLFile = aMachineParms[1] || "";
sMachineVersion = aMachineParms[2] || this.sMachineVersion;
sMachineOptions = aMachineParms[3] || "";
sMachineParms = aMachineParms[4] || "";
var aMachineOptions = sMachineOptions.split(',');
var fDebugger = (aMachineOptions.indexOf("debugger") >= 0);
var fSticky = (aMachineOptions.indexOf("sticky") >= 0);
/*
* TODO: Consider validating the existence of this XML file and generating a more meaningful error if not found
*/
sReplacement = '' + aMatch[1] + '
' + sMachineMessage + '
\n';
/*
* The embedXXX() functions take an XSL file as the 3rd parameter, which defaults to:
*
* "/versions/" + APPCLASS + "/" + APPVERSION + "/components.xsl"
*
* However, when debugging, I'd prefer to use the "development" version of components.xsl, rather than the default
* "production" version.
*/
if (!sMachineXSLFile || sMachineXSLFile.indexOf("components.xsl") >= 0) {
if (this.fDebug) {
if (sMachineType == "C1P") {
sMachineXSLFile = "/modules/c1pjs/templates/components.xsl";
} else {
sMachineXSLFile = "/modules/shared/templates/components.xsl";
}
}
}
/*
* Now that we're providing all of the following machine information to addMachine(), we don't
* need to install the machine embed code here; processMachines() in HTMLOut will take care of that now.
*
sReplacement += this.sIndent + '';
*/
sBlock = sBlock.replace(aMatch[0], sReplacement);
reMachines.lastIndex = 0; // reset lastIndex, since we just modified the string that reMachines is iterating over
cMatches++;
this.addMachine({
'type': sMachineType, // eg, a machine type, such as "PCx86" or "C1P"
'func': sMachineFunc,
'id': sMachineID,
'xml': sMachineXMLFile,
'xsl': sMachineXSLFile,
'version': sMachineVersion,// eg, "1.10", "*" to select the current version, or "uncompiled"; "*" is the default
'debugger': fDebugger, // eg, true or false; false is the default
'sticky': fSticky, // eg, true or false; false is the default
'parms': sMachineParms}
);
}
/*
* Last but not least, see if there are any Liquid-style machine command links that need to be converted.
*/
reIncludes = /([ \t]*){%\s*include\s+machine-command\.html\s+(.*?)\s*%}/g;
var findParm = function(aParms, sParm) {
sParm += '=';
for (var i = 0; i < aParms.length; i++) {
if (aParms[i].indexOf(sParm) == 0) {
return aParms[i].slice(sParm.length+1, -1);
}
}
return "";
};
while ((aMatch = reIncludes.exec(sBlock))) {
if (aMatch[1] == '\t') continue;
var aParms = aMatch[2].match(/[a-z]+=(["']).*?\1/g);
if (!aParms) continue;
sReplacement = "";
sMachineID = findParm(aParms, 'machine');
var sSingle = "false,";
var sControl = findParm(aParms, 'type');
if (sControl == "clickOnce") {
sControl = "";
sSingle = "true,";
}
sControl = sControl || "button";
var sControlType = sControl == 'button'? ' type="button"' : '';
if (this.aMachineDefs[sMachineID]) {
var sCommand = findParm(aParms, 'command');
var sValue = findParm(aParms, 'value');
if (this.aCommandDefs[sCommand]) {
sValue = this.aCommandDefs[sCommand];
sCommand = "script";
} else if (sValue) {
sValue = sValue.replace(/"/g, """);
}
sReplacement = '<' + sControl + sControlType + ' onclick="commandMachine(this,' + sSingle;
sReplacement += "'" + sMachineID + "',";
sReplacement += "'" + findParm(aParms, 'component') + "',";
sReplacement += "'" + sCommand + "',";
sReplacement += "'" + sValue + "')";
sReplacement += '">' + (findParm(aParms, 'label') || 'Try It!') + '' + sControl + '>';
} else {
sReplacement = "";
}
sBlock = sBlock.replace(aMatch[0], sReplacement);
reIncludes.lastIndex = 0; // reset lastIndex, since we just modified the string that reIncludes is iterating over
}
if (cMatches) {
sBlock = sBlock.replace(/^([\s\S]*)<\/p>$/g, "$1");
}
return sBlock;
};
/**
* convertMDEmphasis(sBlock)
*
* We look for sequences like **strength**, __strength__, *emphasis* and _emphasis_;
* we convert the stronger (double-character) forms first, followed by the weaker
* (single-character) forms, since we don't want to misconstrue the former as containing
* the latter.
*
* Also, standard Markdown says that "if you surround an * or _ with spaces, it’ll be
* treated as a literal asterisk or underscore." Well, we don't. You can already escape
* special characters with a backslash to make them literal, so I don't feel like
* complicating the RegExps below to accommodate a syntax I don't use or want to support.
*
* Also, for reasons noted in the code below, we don't support emphasis in the middle
* of words.
*
* @this {MarkOut}
* @param {string} sBlock
* @return {string}
*/
MarkOut.prototype.convertMDEmphasis = function(sBlock)
{
/*
* Standard Markdown allows * or _ in the middle of a word, as in:
*
* un*frigging*believable
*
* but we do not. That's because a Markdown link like:
*
* [modules](/modules/)
*
* would otherwise be misconstrued as containing emphasis (and it doesn't
* matter whether we process emphasis BEFORE or AFTER links -- an HTML link
* poses the same problem as a Markdown link).
*
* To resolve this, I require something non-alphanumeric to both precede AND
* follow the emphasis characters. I would expect that "something" to normally
* be whitespace, but we make it a bit more flexible, so that you can do things
* like place emphasis INSIDE links:
*
* [*modules*](/modules/)
*
* or OUTSIDE links:
*
* *[modules](/modules/)*
*/
if (sBlock.indexOf('*') >= 0 || sBlock.indexOf('_') >= 0) {
sBlock = sBlock.replace(/(^|[^a-z0-9-])([*_])\2([\s\S]*?)\2\2([^a-z0-9-]|$)/gi, "$1$3$4");
sBlock = sBlock.replace(/(^|[^a-z0-9-])([*_])([\s\S]*?)\2([^a-z0-9-]|$)/gi, "$1$3$4");
}
return sBlock;
};
/**
* addIndent(sIndent)
*
* @this {MarkOut}
* @param {string|undefined} sIndent
* @return {string} previous indent
*/
MarkOut.prototype.addIndent = function(sIndent)
{
var sIndentPrev = this.sIndent;
this.sIndent += (sIndent || "");
return sIndentPrev;
};
/**
* subIndent(sIndent)
*
* @this {MarkOut}
* @param {string|undefined} sIndent
*/
MarkOut.prototype.subIndent = function(sIndent)
{
if (sIndent) {
var cch = this.sIndent.length - sIndent.length;
if (cch < 0) {
MarkOut.logError(new Error("indentation underflow"), true);
cch = 0;
}
this.sIndent = this.sIndent.substr(0, cch);
}
};
/**
* encodeComment(sLabel, sText)
*
* This is used purely (at the moment) for debugging purposes, so that we can clearly see
* what our simplistic Markdown parser is parsing at each stage.
*
* @this {MarkOut}
* @param {string} sLabel
* @param {string} sText
* @return {string}
*
MarkOut.prototype.encodeComment = function(sLabel, sText)
{
return '\n';
};
*/
/**
* encodeWhitespace(sText)
*
* This is used purely (at the moment) for debugging purposes, so that we can clearly see
* what our simplistic Markdown parser is parsing at each stage.
*
* @this {MarkOut}
* @param {string} sText
* @return {string}
*
MarkOut.prototype.encodeWhitespace = function(sText)
{
return str.replaceArray({" ":".", "\t":"\\t", "\n":"\\n"}, sText);
};
*/
module.exports = MarkOut;