From 4fdf53819d0837188adf047c0798c5460ce42ba3 Mon Sep 17 00:00:00 2001 From: Jeff Parsons Date: Wed, 16 Dec 2015 15:51:05 -0800 Subject: [PATCH] Somehow failed to pick up markout.js from the gh-pages branch --- _config.yml | 2 +- modules/markout/lib/markout.js | 174 ++++++++++++++++++++++++--------- 2 files changed, 127 insertions(+), 49 deletions(-) diff --git a/_config.yml b/_config.yml index 3c23fe4c8..08f9b9f62 100644 --- a/_config.yml +++ b/_config.yml @@ -14,7 +14,7 @@ github_username: jeffpar # Build settings -exclude: ["logs", "node_modules", "**/static", "src", "tmp", "videos", "web"] +exclude: ["index.html", "logs", "node_modules", "**/static", "src", "tmp", "videos", "web"] markdown: kramdown kramdown: diff --git a/modules/markout/lib/markout.js b/modules/markout/lib/markout.js index 4894497db..3ff26b99e 100644 --- a/modules/markout/lib/markout.js +++ b/modules/markout/lib/markout.js @@ -110,8 +110,9 @@ function MarkOut(sMD, sIndent, req, aParms, fDebug) } this.fDebug = fDebug; this.sHTML = null; - this.aIDs = []; // this keeps tracks of auto-generated ID attributes for page elements, to insure uniqueness - this.aMachines = []; // this keeps tracks of embedded machines on the page + this.aIDs = []; // this keeps track of auto-generated ID attributes for page elements, to insure uniqueness + this.aMachines = []; // this keeps track of embedded machines on the page + this.aMachineDefs = {}; // this keeps track of any machine definitions at the top of the file (as part of any Jekyll "Front Matter") } /* @@ -355,14 +356,40 @@ MarkOut.prototype.convertMD = function(sIndent) if (sMD.substr(0, 3) == "---") { aMatch = sMD.match(/^---([\s\S]*?)---[ \t]*\n*/); if (aMatch) { + /* + * Remove the Front Matter. + */ sMD = sMD.replace(aMatch[0], ""); + /* + * Extract machine definitions, if any, from the Front Matter. + */ + var aMachineDefs = aMatch[1].match(/\nmachines:([\s\S]*?)\n([^\s]|$)/); + if (aMachineDefs) { + var asMachines = aMachineDefs[1].split(/\n[ \t]+-\s*/); + for (var iMachine = 0; iMachine < asMachines.length; iMachine++) { + if (!asMachines[iMachine]) continue; + var id = null; + var aOptions, machine = {}; + var reOption = /([^\s]+):\s*([^\n]*)/g; + while (aOptions = reOption.exec(asMachines[iMachine])) { + if (!id && aOptions[1] == "id") { + id = aOptions[2]; + } + else { + machine[aOptions[1]] = aOptions[2]; + } + } + if (id) this.aMachineDefs[id] = machine; + } + } /* * If a "title" element existed in the Front Matter, then you can enable this code to * auto-generate a top-level heading with the same value. * * aMatch = aMatch[1].match(/title:\s*(.*?)\s*?\n/); * if (aMatch) sMD = aMatch[1] + "\n---\n\n" + sMD; - */ + */ + } } @@ -752,6 +779,15 @@ MarkOut.prototype.convertMDLines = function(s) */ 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: These replacements should use appropriate values from _config.yml; however, unless/until + * we start using Node again to host the public site, that's low priority. + */ + sBlock = sBlock.replace(/\{([\{%]).*?\1}/, ""); + var aMatch; var re = /\[([^\[\]]*)]\((.*?)(?:\s*"(.*?)"\)|\))/g; while ((aMatch = re.exec(sBlock))) { @@ -792,6 +828,24 @@ MarkOut.prototype.convertMDLinks = function(sBlock) */ 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 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 (!). @@ -800,7 +854,6 @@ MarkOut.prototype.convertMDImageLinks = function(sBlock, sIndent) * 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 aMatch; var cImageLinks = 0; var fNoGallery = false; var sBlockOrig = sBlock; @@ -890,76 +943,101 @@ MarkOut.prototype.convertMDImageLinks = function(sBlock, sIndent) /** * 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/pc/machine/5150/mda/64kb/ "PCjs:demoPC:stylesheet:version:options:state") + * + * where a special title attribute triggers generation of an embedded machine rather than a link. + * + * Use "PCjs" or "C1Pjs" to automatically include the latest version of either "pc.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 "pc-dbg.js" or "c1p-dbg.js"), then include "debugger" in the list of comma-delimited + * options, as in: + * + * [IBM PC](/devices/pc/machine/5150/mda/64kb/ "PCjs:demoPC:::debugger") + * + * If the link ends with a slash, then it's an implied reference to a "machine.xml". + * + * 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; + var sMachine, sMachineID, sMachineXMLFile, sMachineXSLFile, sMachineVersion, sMachineOptions, sMachineState; + /* - * 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/pc/machine/5150/mda/64kb/ "PCjs:demoPC:stylesheet:version:options:state") - * - * where a special title attribute triggers generation of an embedded machine rather than - * a link. Use "PCjs" or "C1Pjs" to automatically include the latest version of either - * "pc.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 "pc-dbg.js" or - * "c1p-dbg.js"), then include "debugger" in the list of comma-delimited options, as in: - * - * [IBM PC](/devices/pc/machine/5150/mda/64kb/ "PCjs:demoPC:::debugger") - * - * If the link ends with a slash, then it's an implied reference to a "machine.xml". - * - * 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). + * 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 machine\.html\s+id="(.*?)"\s*%}/g; + + while ((aMatch = reIncludes.exec(sBlock))) { + sMachineID = aMatch[1]; + sReplacement = "[Embedded PC]"; + if (this.aMachineDefs[sMachineID]) { + var machine = this.aMachineDefs[sMachineID]; + sMachine = machine['type'] || "pc"; + sMachineOptions = (sMachine.indexOf("-dbg") > 0? "debugger" : ""); + sMachine = sMachine.replace("-dbg", "").toUpperCase(); + sMachineXMLFile = machine['config'] || "machine.xml"; + sMachineXSLFile = machine['template'] || ""; + sMachineVersion = (machine['uncompiled'] && machine['uncompiled'] == "true"? "uncompiled" : ""); + sMachineState = machine['state'] || ""; + sReplacement += "(" + sMachineXMLFile + ' "' + sMachine + 'js:' + sMachineID + ':' + sMachineXSLFile + '::' + sMachineOptions + ':' + sMachineState + '")'; + } + sBlock = sBlock.replace(aMatch[0], sReplacement); + reIncludes.lastIndex = 0; // reset lastIndex, since we just modified the string that reIncludes is iterating over + } + + /* + * Start looking for Markdown-style machine links now... */ - var aMatch; var cMatches = 0; var reMachines = /\[(.*?)]\((.*?)\s*"(PC|C1P)js:(.*?)"\)/gi; while ((aMatch = reMachines.exec(sBlock))) { - var sMachineXMLFile = aMatch[2]; + sMachineXMLFile = aMatch[2]; if (sMachineXMLFile.slice(-1) == "/") sMachineXMLFile += "machine.xml"; - var sMachine = aMatch[3].toUpperCase(); + sMachine = aMatch[3].toUpperCase(); var sMachineFunc = "embed" + sMachine; var sMachineClass = sMachine.toLowerCase(); var aMachineParms = aMatch[4].split(':'); var sMachineMessage = "Waiting for " + sMachine + "js to load"; - var sMachineID = aMachineParms[0]; - var sMachineXSLFile = aMachineParms[1] || ""; - var sMachineVersion = aMachineParms[2] || this.sMachineVersion; - var sMachineOptions = aMachineParms[3] || ""; - var sMachineState = aMachineParms[4] || ""; + sMachineID = aMachineParms[0]; + sMachineXSLFile = aMachineParms[1] || ""; + sMachineVersion = aMachineParms[2] || this.sMachineVersion; + sMachineOptions = aMachineParms[3] || ""; + sMachineState = aMachineParms[4] || ""; var aMachineOptions = sMachineOptions.split(','); var fDebugger = (aMachineOptions.indexOf("debugger") >= 0); /* * TODO: Consider validating the existence of this XML file and generating a more meaningful error if not found */ - var sReplacement = '

' + aMatch[1] + '

' + sMachineMessage + '

\n'; + sReplacement = '

' + aMatch[1] + '

' + sMachineMessage + '

\n'; /* * The embedPC()/embedC1P functions take an XSL file as the 3rd parameter, which defaults to: