{"version":3,"sources":["jsdelivr-header.js","/npm/comicgen@1.9.8/src/comicgen.js"],"names":[],"mappings":"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","file":"/npm/comicgen@1.9.8/src/comicgen.js","sourceRoot":"","sourcesContent":["/**\n * Minified by jsDelivr using Terser v5.39.0.\n * Original file: /npm/comicgen@1.9.8/src/comicgen.js\n *\n * Do NOT use SRI with dynamically generated files! More information: https://www.jsdelivr.com/using-sri-with-dynamic-files\n */\n","// comicserver() takes a configuration object and returns a fully-rendered SVG\nconst path = require(\"path\");\nconst _ = require(\"lodash\");\nconst cheerio = require(\"cheerio\");\nconst mustache = require(\"mustache\");\nconst roughjs = require(\"roughjs\");\nconst interpolate = require(\"d3-interpolate-path\");\nconst colorInterpolate = require(\"color-interpolate\");\nconst interpolateNumber = require(\"./interpolate\").number;\nconst interpolateString = require(\"./interpolate\").string;\nconst get_config = require(\"./getconfig\").get_config;\nconst speechbubble = require(\"./speechbubble\");\n\n// TODO: Allow users to override root in options\nlet root = path.join(__dirname, \"..\", \"svg\");\n\nfunction comicgen(fs) {\n  function comic(template, _replacements) {\n    // comic() or comic('') returns just an empty string.\n    if (!template) return \"\";\n    // If we got a string, treat it as a HTML template. Render all <comic> instances\n    else if (typeof template == \"string\") {\n      // Replace all <comic> instances with the underlying SVG via comic({...})\n      let $ = cheerio.load(template, { xmlMode: true });\n      $(\"comic\").each(function (index, el) {\n        $(el).replaceWith(comic(el.attribs, _replacements));\n      });\n\n      // Replace interpolation elements\n      $(\"interpolate\").each(function (index, el) {\n        let $source = $(comic({ name: el.attribs[\"from\"] }, _replacements));\n        let $target = $(comic({ name: el.attribs[\"to\"] }, _replacements));\n        // TODO: we get all interpolation parameters from index.svg. But they're similar to index.json's replacements.\n        // Let's move index.json's replacements into index.svg as <param> elements.\n        $(\"param\", el).each(function (index, param) {\n          let $param = $(param);\n          let selector = $param.attr(\"selector\");\n          let key = $param.attr(\"value\");\n          let method = $param.attr(\"method\");\n          let value = (_replacements[key] || { value: 0 }).value;\n          let attrs = ($param.attr(\"attr\") || \"fill,stroke,d\").split(\",\");\n          attrs.forEach((attr) => {\n            let sourceVal = $target.find(selector).attr(attr);\n            let targetVal = $source.find(selector).attr(attr);\n            if (!sourceVal || !targetVal) return;\n            let interpolator = () => sourceVal;\n            if (method == \"path\") interpolator = interpolate.interpolatePath(targetVal, sourceVal);\n            else if (method == \"color\") interpolator = colorInterpolate([targetVal, sourceVal]);\n            else if (method == \"number\") interpolator = interpolateNumber(targetVal, sourceVal);\n            else interpolator = interpolateString(targetVal, sourceVal);\n            $(selector, $source).attr(attr, interpolator(value));\n          });\n        });\n        $(el).replaceWith($source);\n      });\n      // Replace attributes of the SVG, e.g. change fill colors.\n      // _replacements is like {name: {selector, attr, value}}.\n      // In the SVG, we find all matching selectors and set attr=value\n      // Note: Prefer case-insensitive selectors - https://caniuse.com/css-case-insensitive\n      // {selector: 'path[fill=\"#ccc\"]'} is case-sensitive. Won't ma\\tch #CCC.\n      // {selector: 'path[fill=\"#ccc\" i]'} is case-INsensitive. It will also match #CCC. Use this.\n      _.each(_replacements, (replace) => {\n        if (replace.value && replace.selector) {\n          let $el = $(replace.selector);\n          replace.attr.split(/[, ]+/g).forEach((attr) => {\n            if ($el.is(`[${attr}]`)) $el.attr(attr, replace.value);\n          });\n        }\n      });\n      return $.html().trim();\n    }\n\n    // Else, if we get a dict of options...\n    else if (typeof template == \"object\") {\n      // comic({}) return an empty string\n      if (!template.name) return \"\";\n      let svg_path = path.join(root, template.name);\n      let stat;\n      try {\n        stat = fs.lstatSync(svg_path);\n      } catch (e) {\n        throw new ComicError(`Unknown character ${template.name}`, {\n          name: template.name,\n        });\n      }\n      // If it’s a directory, read the `index.svg`. Else read the file intself\n      if (stat.isDirectory()) svg_path = path.join(svg_path, \"index.svg\");\n      // Merge all index.json files in every directory from root to svg_path to get the config\n      const config = get_config(svg_path, root, fs);\n      // Render the SVG as a template\n      const attrs = _.merge(\n        {\n          speechbubble: () => (text, render) => speechbubble({ ...attrs, text: render(text) }),\n        },\n        config.defaults,\n        template,\n        comic.functions,\n      );\n      let width = +attrs.width || config.defaults.width;\n      let height = +attrs.height || config.defaults.height;\n      const comic_width_half = width / 2;\n      const comic_height_half = height / 2;\n      const mirror_transform = attrs.mirror ? `translate(${width},0) scale(-1, 1)` : \"\";\n      const aspect = config.aspect || \"xMidYMin slice\";\n      // Characters with a default width & height are drawn to that viewBox.\n      // Others (like speechbubbles) are drawn dynamically based on width/height, and have no viewBox\n      const viewBox =\n        config.defaults.width && config.defaults.height\n          ? `viewBox=\"0 0 ${config.defaults.width} ${config.defaults.height}\"`\n          : \"\";\n      // If there's a ?box=<thickness>, add a panel around the strip.\n      //    ?boxcolor= defines the color. Default: black\n      //    ?boxgap= defines the box padding. Default: max(box, jitter)\n      let box = \"\";\n      if (attrs.box) {\n        const gen = roughjs.generator({\n          options: {\n            seed: 1,\n            bowing: 2,\n            roughness: Math.max(2, Math.min(attrs.box / 2, 5)),\n          },\n        });\n        let padding = +attrs.boxgap || +attrs.box;\n        let path = gen.opsToPath(gen.rectangle(padding / 2, padding / 2, width - padding, height - padding).sets[0]);\n        box = `<path d=\"${path}\" fill=\"none\" stroke=\"${attrs.boxcolor || \"black\"}\" stroke-width=\"${attrs.box}\"></path>`;\n      }\n      // TODO: check if overflow is working\n      const overflow = attrs.box ? \"\" : 'style=\"overflow:visible\"';\n      let svg = get_template(svg_path);\n      svg = `\n  <svg xmlns=\"http://www.w3.org/2000/svg\" width=\"${width}\" height=\"${height}\" ${overflow}>\n    ${box}\n    <svg ${viewBox} preserveAspectRatio=\"${aspect}\" width=\"${config.defaults.width || \"100%\"}\" height=\"${\n      config.defaults.height || \"100%\"\n    }\" ${overflow}>\n      <g transform=\"${mirror_transform} translate(${comic_width_half},${comic_height_half}) scale(${\n        attrs.scale\n      }) translate(-${comic_width_half},-${comic_height_half}) translate(${attrs.x},${attrs.y})\">\n        ${render(svg, attrs)}\n      </g>\n    </svg>\n  </svg>`;\n      // If the template contains a <comic> object, recursively replace it with comic().\n      // Get the replacement parameters from the template object, or the parent replacements object.\n      const _new_replacements = _.mapValues(config.replace, (v, k) =>\n        _.merge(v, { value: template[k] || _replacements?.[k]?.value }),\n      );\n      return comic(svg, _new_replacements);\n    } else throw new Error(\"TODO\");\n  }\n\n  // These functions can be used by mustache in templates\n  comic.functions = {\n    roughrect: function () {\n      return function (shape, render) {\n        let [x, y, w, h, r, t] = shape.split(/[\\s,]+/);\n        r = +render(r);\n        t = +render(t);\n        const gap = r + t;\n        const gen = roughjs.generator({ options: { roughnesss: r } });\n        return gen.opsToPath(gen.rectangle(+x + gap, +y + gap, +w - 2 * gap, +h - 2 * gap).sets[0]);\n      };\n    },\n  };\n\n  // Return the SVG at svg_path. If there is an <import \"../path/file.svg\">, insert that file here.\n  // This is done recursively. That makes this function hard to cache -- since it may involve\n  // loading multiple files.\n  function get_template(svg_path) {\n    let svg = fs.readFileSync(svg_path, { encoding: \"utf-8\" });\n    return svg.replace(/<\\?import\\s+(.*?)\\?>/, function (match, import_path) {\n      return get_template(path.join(svg_path, \"..\", import_path.replace(/^[\"']|[\"']$/g, \"\")));\n    });\n  }\n\n  function render(svg, attrs) {\n    return mustache.render(svg, attrs);\n  }\n\n  return comic;\n}\n\nclass ComicError extends Error {\n  constructor(message, options) {\n    super(message);\n    this.options = options;\n  }\n}\n\n// Usage:\n// const comicgen = require('comicgen')(require('fs'))\n// const comicgen = require('comicgen')(require('fsjson'))\nmodule.exports = comicgen;\n"]}