'use strict';

function _interopDefault (ex) { return (ex && (typeof ex === 'object') && 'default' in ex) ? ex['default'] : ex; }

var fs = require('fs');
var path = require('path');
var resolvePathname = _interopDefault(require('resolve-pathname'));
var fetch = _interopDefault(require('node-fetch'));
var debug = _interopDefault(require('debug'));
var DOMPurify = _interopDefault(require('dompurify'));
var marked = _interopDefault(require('marked'));
var Prism = _interopDefault(require('prismjs'));
require('prismjs/components/prism-markup-templating');
var stripIndent = _interopDefault(require('strip-indent'));

function __async(g){return new Promise(function(s,j){function c(a,x){try{var r=g[x?"throw":"next"](a);}catch(e){j(e);return}r.done?s(r.value):Promise.resolve(r.value).then(c,d);}function d(e){c(e,1);}c();})}

/**
 * Create a cached version of a pure function.
 * @param {*} fn The function call to be cached
 * @void
 */

function cached(fn) {
  var cache = Object.create(null);
  return function(str) {
    var key = isPrimitive(str) ? str : JSON.stringify(str);
    var hit = cache[key];
    return hit || (cache[key] = fn(str));
  };
}

var hasOwn = Object.prototype.hasOwnProperty;

/**
 * Simple Object.assign polyfill
 * @param {Object} to The object to be merged with
 * @returns {Object} The merged object
 */
var merge =
  Object.assign ||
  function(to) {
    var arguments$1 = arguments;

    for (var i = 1; i < arguments.length; i++) {
      var from = Object(arguments$1[i]);

      for (var key in from) {
        if (hasOwn.call(from, key)) {
          to[key] = from[key];
        }
      }
    }

    return to;
  };

/**
 * Check if value is primitive
 * @param {*} value Checks if a value is primitive
 * @returns {Boolean} Result of the check
 */
function isPrimitive(value) {
  return typeof value === 'string' || typeof value === 'number';
}

/**
 * Performs no operation.
 * @void
 */
function noop() {}

/**
 * Check if value is function
 * @param {*} obj Any javascript object
 * @returns {Boolean} True if the passed-in value is a function
 */
function isFn(obj) {
  return typeof obj === 'function';
}

var decode = decodeURIComponent;
var encode = encodeURIComponent;

function parseQuery(query) {
  var res = {};

  query = query.trim().replace(/^(\?|#|&)/, '');

  if (!query) {
    return res;
  }

  // Simple parse
  query.split('&').forEach(function(param) {
    var parts = param.replace(/\+/g, ' ').split('=');

    res[parts[0]] = parts[1] && decode(parts[1]);
  });

  return res;
}

function stringifyQuery(obj, ignores) {
  if ( ignores === void 0 ) ignores = [];

  var qs = [];

  for (var key in obj) {
    if (ignores.indexOf(key) > -1) {
      continue;
    }

    qs.push(
      obj[key]
        ? ((encode(key)) + "=" + (encode(obj[key]))).toLowerCase()
        : encode(key)
    );
  }

  return qs.length ? ("?" + (qs.join('&'))) : '';
}

var isAbsolutePath = cached(function (path) {
  return /(:|(\/{2}))/g.test(path);
});

var getParentPath = cached(function (path) {
  if (/\/$/g.test(path)) {
    return path;
  }

  var matchingParts = path.match(/(\S*\/)[^/]+$/);
  return matchingParts ? matchingParts[1] : '';
});

var cleanPath = cached(function (path) {
  return path.replace(/^\/+/, '/').replace(/([^:])\/{2,}/g, '$1/');
});

var resolvePath = cached(function (path) {
  var segments = path.replace(/^\//, '').split('/');
  var resolved = [];
  for (var i = 0, len = segments.length; i < len; i++) {
    var segment = segments[i];
    if (segment === '..') {
      resolved.pop();
    } else if (segment !== '.') {
      resolved.push(segment);
    }
  }

  return '/' + resolved.join('/');
});

/**
 * Normalises the URI path to handle the case where Docsify is
 * hosted off explicit files, i.e. /index.html. This function
 * eliminates any path segments that contain `#` fragments.
 *
 * This is used to map browser URIs to markdown file sources.
 *
 * For example:
 *
 * http://example.org/base/index.html#/blah
 *
 * would be mapped to:
 *
 * http://example.org/base/blah.md.
 *
 * See here for more information:
 *
 * https://github.com/docsifyjs/docsify/pull/1372
 *
 * @param {string} path The URI path to normalise
 * @return {string} { path, query }
 */

function normaliseFragment(path) {
  return path
    .split('/')
    .filter(function (p) { return p.indexOf('#') === -1; })
    .join('/');
}

function getPath() {
  var args = [], len = arguments.length;
  while ( len-- ) args[ len ] = arguments[ len ];

  return cleanPath(args.map(normaliseFragment).join('/'));
}

var replaceSlug = cached(function (path) {
  return path.replace('#', '?id=');
});

var cached$1 = {};

function getAlias(path, alias, last) {
  var match = Object.keys(alias).filter(function (key) {
    var re = cached$1[key] || (cached$1[key] = new RegExp(("^" + key + "$")));
    return re.test(path) && path !== last;
  })[0];

  return match
    ? getAlias(path.replace(cached$1[match], alias[match]), alias, path)
    : path;
}

function getFileName(path, ext) {
  return new RegExp(("\\.(" + (ext.replace(/^\./, '')) + "|html)$"), 'g').test(path)
    ? path
    : /\/$/g.test(path)
    ? (path + "README" + ext)
    : ("" + path + ext);
}

var History = function History(config) {
  this.config = config;
};

History.prototype.getBasePath = function getBasePath () {
  return this.config.basePath;
};

History.prototype.getFile = function getFile (path, isRelative) {
    if ( path === void 0 ) path = this.getCurrentPath();

  var ref = this;
    var config = ref.config;
  var base = this.getBasePath();
  var ext = typeof config.ext === 'string' ? config.ext : '.md';

  path = config.alias ? getAlias(path, config.alias) : path;
  path = getFileName(path, ext);
  path = path === ("/README" + ext) ? config.homepage || path : path;
  path = isAbsolutePath(path) ? path : getPath(base, path);

  if (isRelative) {
    path = path.replace(new RegExp(("^" + base)), '');
  }

  return path;
};

History.prototype.onchange = function onchange (cb) {
    if ( cb === void 0 ) cb = noop;

  cb();
};

History.prototype.getCurrentPath = function getCurrentPath () {};

History.prototype.normalize = function normalize () {};

History.prototype.parse = function parse () {};

History.prototype.toURL = function toURL (path, params, currentRoute) {
  var local = currentRoute && path[0] === '#';
  var route = this.parse(replaceSlug(path));

  route.query = merge({}, route.query, params);
  path = route.path + stringifyQuery(route.query);
  path = path.replace(/\.md(\?)|\.md$/, '$1');

  if (local) {
    var idIndex = currentRoute.indexOf('?');
    path =
      (idIndex > 0 ? currentRoute.substring(0, idIndex) : currentRoute) +
      path;
  }

  if (this.config.relativePath && path.indexOf('/') !== 0) {
    var currentDir = currentRoute.substring(
      0,
      currentRoute.lastIndexOf('/') + 1
    );
    return cleanPath(resolvePath(currentDir + path));
  }

  return cleanPath('/' + path);
};

var AbstractHistory = /*@__PURE__*/(function (History) {
  function AbstractHistory(config) {
    History.call(this, config);
    this.mode = 'abstract';
  }

  if ( History ) AbstractHistory.__proto__ = History;
  AbstractHistory.prototype = Object.create( History && History.prototype );
  AbstractHistory.prototype.constructor = AbstractHistory;

  AbstractHistory.prototype.parse = function parse (path) {
    var query = '';

    var queryIndex = path.indexOf('?');
    if (queryIndex >= 0) {
      query = path.slice(queryIndex + 1);
      path = path.slice(0, queryIndex);
    }

    return {
      path: path,
      file: this.getFile(path),
      query: parseQuery(query),
    };
  };

  return AbstractHistory;
}(History));

/**
 * Render github corner
 * @param  {Object} data URL for the View Source on Github link
 * @param {String} cornerExternalLinkTarge value of the target attribute of the link
 * @return {String} SVG element as string
 */
function corner(data, cornerExternalLinkTarge) {
  if (!data) {
    return '';
  }

  if (!/\/\//.test(data)) {
    data = 'https://github.com/' + data;
  }

  data = data.replace(/^git\+/, '');
  // Double check
  cornerExternalLinkTarge = cornerExternalLinkTarge || '_blank';

  return (
    "<a href=\"" + data + "\" target=\"" + cornerExternalLinkTarge + "\" class=\"github-corner\" aria-label=\"View source on Github\">" +
    '<svg viewBox="0 0 250 250" aria-hidden="true">' +
    '<path d="M0,0 L115,115 L130,115 L142,142 L250,250 L250,0 Z"></path>' +
    '<path d="M128.3,109.0 C113.8,99.7 119.0,89.6 119.0,89.6 C122.0,82.7 120.5,78.6 120.5,78.6 C119.2,72.0 123.4,76.3 123.4,76.3 C127.3,80.9 125.5,87.3 125.5,87.3 C122.9,97.6 130.6,101.9 134.4,103.2" fill="currentColor" style="transform-origin: 130px 106px;" class="octo-arm"></path>' +
    '<path d="M115.0,115.0 C114.9,115.1 118.7,116.5 119.8,115.4 L133.7,101.6 C136.9,99.2 139.9,98.4 142.2,98.6 C133.8,88.0 127.5,74.4 143.8,58.0 C148.5,53.4 154.0,51.2 159.7,51.0 C160.3,49.4 163.2,43.6 171.4,40.1 C171.4,40.1 176.1,42.5 178.8,56.2 C183.1,58.6 187.2,61.8 190.9,65.4 C194.5,69.0 197.7,73.2 200.1,77.6 C213.8,80.2 216.3,84.9 216.3,84.9 C212.7,93.1 206.9,96.0 205.4,96.6 C205.1,102.4 203.0,107.8 198.3,112.5 C181.9,128.9 168.3,122.5 157.7,114.1 C157.9,116.9 156.7,120.9 152.7,124.9 L141.0,136.5 C139.8,137.7 141.6,141.9 141.8,141.8 Z" fill="currentColor" class="octo-body"></path>' +
    '</svg>' +
    '</a>'
  );
}

/**
 * Renders main content
 * @param {Object} config Configuration object
 * @returns {String} HTML of the main content
 */
function main(config) {
  var name = config.name ? config.name : '';

  var aside =
    '<button class="sidebar-toggle" aria-label="Menu">' +
    '<div class="sidebar-toggle-button">' +
    '<span></span><span></span><span></span>' +
    '</div>' +
    '</button>' +
    '<aside class="sidebar">' +
    (config.name
      ? ("<h1 class=\"app-name\"><a class=\"app-name-link\" data-nosearch>" + (config.logo ? ("<img alt=\"" + name + "\" src=" + (config.logo) + ">") : name) + "</a></h1>")
      : '') +
    '<div class="sidebar-nav"><!--sidebar--></div>' +
    '</aside>';
  return (
    "<main>" + aside +
    '<section class="content">' +
    '<article class="markdown-section" id="main"><!--main--></article>' +
    '</section>' +
    '</main>'
  );
}

/**
 * Cover Page
 * @returns {String} Cover page
 */
function cover() {
  var SL = ', 100%, 85%';
  var bgc =
    'linear-gradient(to left bottom, ' +
    "hsl(" + (Math.floor(Math.random() * 255) + SL) + ") 0%," +
    "hsl(" + (Math.floor(Math.random() * 255) + SL) + ") 100%)";

  return (
    "<section class=\"cover show\" style=\"background: " + bgc + "\">" +
    '<div class="mask"></div>' +
    '<div class="cover-main"><!--cover--></div>' +
    '</section>'
  );
}

/**
 * Render tree
 * @param  {Array} toc Array of TOC section links
 * @param  {String} tpl TPL list
 * @return {String} Rendered tree
 */
function tree(toc, tpl) {
  if ( tpl === void 0 ) tpl = '<ul class="app-sub-sidebar">{inner}</ul>';

  if (!toc || !toc.length) {
    return '';
  }

  var innerHTML = '';
  toc.forEach(function (node) {
    var title = node.title.replace(/(<([^>]+)>)/g, '');
    innerHTML += "<li><a class=\"section-link\" href=\"" + (node.slug) + "\" title=\"" + title + "\">" + (node.title) + "</a></li>";
    if (node.children) {
      innerHTML += tree(node.children, tpl);
    }
  });
  return tpl.replace('{inner}', innerHTML);
}

function helper(className, content) {
  return ("<p class=\"" + className + "\">" + (content.slice(5).trim()) + "</p>");
}

/**
 * Gen toc tree
 * @link https://github.com/killercup/grock/blob/5280ae63e16c5739e9233d9009bc235ed7d79a50/styles/solarized/assets/js/behavior.coffee#L54-L81
 * @param  {Array} toc List of TOC elements
 * @param  {Number} maxLevel Deep level
 * @return {Array} Headlines
 */
function genTree(toc, maxLevel) {
  var headlines = [];
  var last = {};

  toc.forEach(function (headline) {
    var level = headline.level || 1;
    var len = level - 1;

    if (level > maxLevel) {
      return;
    }

    if (last[len]) {
      last[len].children = (last[len].children || []).concat(headline);
    } else {
      headlines.push(headline);
    }

    last[level] = headline;
  });

  return headlines;
}

var cache = {};
var re = /[\u2000-\u206F\u2E00-\u2E7F\\'!"#$%&()*+,./:;<=>?@[\]^`{|}~]/g;

function lower(string) {
  return string.toLowerCase();
}

function slugify(str) {
  if (typeof str !== 'string') {
    return '';
  }

  var slug = str
    .trim()
    .replace(/[A-Z]+/g, lower)
    .replace(/<[^>]+>/g, '')
    .replace(re, '')
    .replace(/\s/g, '-')
    .replace(/-+/g, '-')
    .replace(/^(\d)/, '_$1');
  var count = cache[slug];

  count = hasOwn.call(cache, slug) ? count + 1 : 0;
  cache[slug] = count;

  if (count) {
    slug = slug + '-' + count;
  }

  return slug;
}

slugify.clear = function() {
  cache = {};
};

function replace(m, $1) {
  return (
    '<img class="emoji" src="https://github.githubassets.com/images/icons/emoji/' +
    $1 +
    '.png" alt="' +
    $1 +
    '" />'
  );
}

function emojify(text) {
  return text
    .replace(/:\+1:/g, ':thumbsup:')
    .replace(/:-1:/g, ':thumbsdown:')
    .replace(/<(pre|template|code)[^>]*?>[\s\S]+?<\/(pre|template|code)>/g, function (m) { return m.replace(/:/g, '__colon__'); }
    )
    .replace(/:(\w+?):/gi,  replace)
    .replace(/__colon__/g, ':');
}

/**
 * Converts a colon formatted string to a object with properties.
 *
 * This is process a provided string and look for any tokens in the format
 * of `:name[=value]` and then convert it to a object and return.
 * An example of this is ':include :type=code :fragment=demo' is taken and
 * then converted to:
 *
 * ```
 * {
 *  include: '',
 *  type: 'code',
 *  fragment: 'demo'
 * }
 * ```
 *
 * @param {string}   str   The string to parse.
 *
 * @return {object}  The original string and parsed object, { str, config }.
 */
function getAndRemoveConfig(str) {
  if ( str === void 0 ) str = '';

  var config = {};

  if (str) {
    str = str
      .replace(/^'/, '')
      .replace(/'$/, '')
      .replace(/(?:^|\s):([\w-]+:?)=?([\w-%]+)?/g, function (m, key, value) {
        if (key.indexOf(':') === -1) {
          config[key] = (value && value.replace(/&quot;/g, '')) || true;
          return '';
        }

        return m;
      })
      .trim();
  }

  return { str: str, config: config };
}

/**
 * Remove the <a> tag from sidebar when the header with link, details see issue 1069
 * @param {string}   str   The string to deal with.
 *
 * @return {string}   str   The string after delete the <a> element.
 */
function removeAtag(str) {
  if ( str === void 0 ) str = '';

  return str.replace(/(<\/?a.*?>)/gi, '');
}

var imageCompiler = function (ref) {
    var renderer = ref.renderer;
    var contentBase = ref.contentBase;
    var router = ref.router;

    return (renderer.image = function (href, title, text) {
    var url = href;
    var attrs = [];

    var ref = getAndRemoveConfig(title);
    var str = ref.str;
    var config = ref.config;
    title = str;

    if (config['no-zoom']) {
      attrs.push('data-no-zoom');
    }

    if (title) {
      attrs.push(("title=\"" + title + "\""));
    }

    if (config.size) {
      var ref$1 = config.size.split('x');
      var width = ref$1[0];
      var height = ref$1[1];
      if (height) {
        attrs.push(("width=\"" + width + "\" height=\"" + height + "\""));
      } else {
        attrs.push(("width=\"" + width + "\""));
      }
    }

    if (config.class) {
      attrs.push(("class=\"" + (config.class) + "\""));
    }

    if (config.id) {
      attrs.push(("id=\"" + (config.id) + "\""));
    }

    if (!isAbsolutePath(href)) {
      url = getPath(contentBase, getParentPath(router.getCurrentPath()), href);
    }

    if (attrs.length > 0) {
      return ("<img src=\"" + url + "\" data-origin=\"" + href + "\" alt=\"" + text + "\" " + (attrs.join(
        ' '
      )) + " />");
    }

    return ("<img src=\"" + url + "\" data-origin=\"" + href + "\" alt=\"" + text + "\"" + attrs + ">");
  });
};

var highlightCodeCompiler = function (ref) {
    var renderer = ref.renderer;

    return (renderer.code = function(code, lang) {
    if ( lang === void 0 ) lang = 'markup';

    var langOrMarkup = Prism.languages[lang] || Prism.languages.markup;
    var text = Prism.highlight(
      code.replace(/@DOCSIFY_QM@/g, '`'),
      langOrMarkup,
      lang
    );

    return ("<pre v-pre data-lang=\"" + lang + "\"><code class=\"lang-" + lang + "\">" + text + "</code></pre>");
  });
};

var paragraphCompiler = function (ref) {
    var renderer = ref.renderer;

    return (renderer.paragraph = function (text) {
    var result;
    if (/^!&gt;/.test(text)) {
      result = helper('tip', text);
    } else if (/^\?&gt;/.test(text)) {
      result = helper('warn', text);
    } else {
      result = "<p>" + text + "</p>";
    }

    return result;
  });
};

var taskListCompiler = function (ref) {
    var renderer = ref.renderer;

    return (renderer.list = function (body, ordered, start) {
    var isTaskList = /<li class="task-list-item">/.test(
      body.split('class="task-list"')[0]
    );
    var isStartReq = start && start > 1;
    var tag = ordered ? 'ol' : 'ul';
    var tagAttrs = [
      isTaskList ? 'class="task-list"' : '',
      isStartReq ? ("start=\"" + start + "\"") : '' ]
      .join(' ')
      .trim();

    return ("<" + tag + " " + tagAttrs + ">" + body + "</" + tag + ">");
  });
};

var taskListItemCompiler = function (ref) {
    var renderer = ref.renderer;

    return (renderer.listitem = function (text) {
    var isTaskItem = /^(<input.*type="checkbox"[^>]*>)/.test(text);
    var html = isTaskItem
      ? ("<li class=\"task-list-item\"><label>" + text + "</label></li>")
      : ("<li>" + text + "</li>");

    return html;
  });
};

var linkCompiler = function (ref) {
    var renderer = ref.renderer;
    var router = ref.router;
    var linkTarget = ref.linkTarget;
    var linkRel = ref.linkRel;
    var compilerClass = ref.compilerClass;

    return (renderer.link = function (href, title, text) {
    if ( title === void 0 ) title = '';

    var attrs = [];
    var ref = getAndRemoveConfig(title);
    var str = ref.str;
    var config = ref.config;
    linkTarget = config.target || linkTarget;
    linkRel =
      linkTarget === '_blank'
        ? compilerClass.config.externalLinkRel || 'noopener'
        : '';
    title = str;

    if (
      !isAbsolutePath(href) &&
      !compilerClass._matchNotCompileLink(href) &&
      !config.ignore
    ) {
      if (href === compilerClass.config.homepage) {
        href = 'README';
      }

      href = router.toURL(href, null, router.getCurrentPath());
    } else {
      if (!isAbsolutePath(href) && href.slice(0, 2) === './') {
        href =
          document.URL.replace(/\/(?!.*\/).*/, '/').replace('#/./', '') + href;
      }
      attrs.push(href.indexOf('mailto:') === 0 ? '' : ("target=\"" + linkTarget + "\""));
      attrs.push(
        href.indexOf('mailto:') === 0
          ? ''
          : linkRel !== ''
          ? (" rel=\"" + linkRel + "\"")
          : ''
      );
    }

    // special case to check crossorigin urls
    if (
      config.crossorgin &&
      linkTarget === '_self' &&
      compilerClass.config.routerMode === 'history'
    ) {
      if (compilerClass.config.crossOriginLinks.indexOf(href) === -1) {
        compilerClass.config.crossOriginLinks.push(href);
      }
    }

    if (config.disabled) {
      attrs.push('disabled');
      href = 'javascript:void(0)';
    }

    if (config.class) {
      attrs.push(("class=\"" + (config.class) + "\""));
    }

    if (config.id) {
      attrs.push(("id=\"" + (config.id) + "\""));
    }

    if (title) {
      attrs.push(("title=\"" + title + "\""));
    }

    return ("<a href=\"" + href + "\" " + (attrs.join(' ')) + ">" + text + "</a>");
  });
};

var cachedLinks = {};

var compileMedia = {
  markdown: function markdown(url) {
    return {
      url: url,
    };
  },
  mermaid: function mermaid(url) {
    return {
      url: url,
    };
  },
  iframe: function iframe(url, title) {
    return {
      html: ("<iframe src=\"" + url + "\" " + (title ||
        'width=100% height=400') + "></iframe>"),
    };
  },
  video: function video(url, title) {
    return {
      html: ("<video src=\"" + url + "\" " + (title || 'controls') + ">Not Support</video>"),
    };
  },
  audio: function audio(url, title) {
    return {
      html: ("<audio src=\"" + url + "\" " + (title || 'controls') + ">Not Support</audio>"),
    };
  },
  code: function code(url, title) {
    var lang = url.match(/\.(\w+)$/);

    lang = title || (lang && lang[1]);
    if (lang === 'md') {
      lang = 'markdown';
    }

    return {
      url: url,
      lang: lang,
    };
  },
};

var Compiler = function Compiler(config, router) {
  var this$1 = this;

  this.config = config;
  this.router = router;
  this.cacheTree = {};
  this.toc = [];
  this.cacheTOC = {};
  this.linkTarget = config.externalLinkTarget || '_blank';
  this.linkRel =
    this.linkTarget === '_blank' ? config.externalLinkRel || 'noopener' : '';
  this.contentBase = router.getBasePath();

  var renderer = this._initRenderer();
  this.heading = renderer.heading;
  var compile;
  var mdConf = config.markdown || {};

  if (isFn(mdConf)) {
    compile = mdConf(marked, renderer);
  } else {
    marked.setOptions(
      merge(mdConf, {
        renderer: merge(renderer, mdConf.renderer),
      })
    );
    compile = marked;
  }

  this._marked = compile;
  this.compile = function (text) {
    var isCached = true;
    // eslint-disable-next-line no-unused-vars
    var result = cached(function (_) {
      isCached = false;
      var html = '';

      if (!text) {
        return text;
      }

      if (isPrimitive(text)) {
        html = compile(text);
      } else {
        html = compile.parser(text);
      }

      html = config.noEmoji ? html : emojify(html);
      slugify.clear();

      return html;
    })(text);

    var curFileName = this$1.router.parse().file;

    if (isCached) {
      this$1.toc = this$1.cacheTOC[curFileName];
    } else {
      this$1.cacheTOC[curFileName] = [].concat( this$1.toc );
    }

    return result;
  };
};

/**
 * Pulls content from file and renders inline on the page as a embedded item.
 *
 * This allows you to embed different file types on the returned
 * page.
 * The basic format is:
 * ```
 * [filename](_media/example.md ':include')
 * ```
 *
 * @param {string} href The href to the file to embed in the page.
 * @param {string} titleTitle of the link used to make the embed.
 *
 * @return {type} Return value description.
 */
Compiler.prototype.compileEmbed = function compileEmbed (href, title) {
  var ref = getAndRemoveConfig(title);
    var str = ref.str;
    var config = ref.config;
  var embed;
  title = str;

  if (config.include) {
    if (!isAbsolutePath(href)) {
      href = getPath(
         '' ,
        getParentPath(this.router.getCurrentPath()),
        href
      );
    }

    var media;
    if (config.type && (media = compileMedia[config.type])) {
      embed = media.call(this, href, title);
      embed.type = config.type;
    } else {
      var type = 'code';
      if (/\.(md|markdown)/.test(href)) {
        type = 'markdown';
      } else if (/\.mmd/.test(href)) {
        type = 'mermaid';
      } else if (/\.html?/.test(href)) {
        type = 'iframe';
      } else if (/\.(mp4|ogg)/.test(href)) {
        type = 'video';
      } else if (/\.mp3/.test(href)) {
        type = 'audio';
      }

      embed = compileMedia[type].call(this, href, title);
      embed.type = type;
    }

    embed.fragment = config.fragment;

    return embed;
  }
};

Compiler.prototype._matchNotCompileLink = function _matchNotCompileLink (link) {
  var links = this.config.noCompileLinks || [];

  for (var i = 0; i < links.length; i++) {
    var n = links[i];
    var re = cachedLinks[n] || (cachedLinks[n] = new RegExp(("^" + n + "$")));

    if (re.test(link)) {
      return link;
    }
  }
};

Compiler.prototype._initRenderer = function _initRenderer () {
  var renderer = new marked.Renderer();
  var ref = this;
    var linkTarget = ref.linkTarget;
    var linkRel = ref.linkRel;
    var router = ref.router;
    var contentBase = ref.contentBase;
  var _self = this;
  var origin = {};

  /**
   * Render anchor tag
   * @link https://github.com/markedjs/marked#overriding-renderer-methods
   * @param {String} text Text content
   * @param {Number} level Type of heading (h<level> tag)
   * @returns {String} Heading element
   */
  origin.heading = renderer.heading = function(text, level) {
    var ref = getAndRemoveConfig(text);
      var str = ref.str;
      var config = ref.config;
    var nextToc = { level: level, title: removeAtag(str) };

    if (/<!-- {docsify-ignore} -->/g.test(str)) {
      str = str.replace('<!-- {docsify-ignore} -->', '');
      nextToc.title = removeAtag(str);
      nextToc.ignoreSubHeading = true;
    }

    if (/{docsify-ignore}/g.test(str)) {
      str = str.replace('{docsify-ignore}', '');
      nextToc.title = removeAtag(str);
      nextToc.ignoreSubHeading = true;
    }

    if (/<!-- {docsify-ignore-all} -->/g.test(str)) {
      str = str.replace('<!-- {docsify-ignore-all} -->', '');
      nextToc.title = removeAtag(str);
      nextToc.ignoreAllSubs = true;
    }

    if (/{docsify-ignore-all}/g.test(str)) {
      str = str.replace('{docsify-ignore-all}', '');
      nextToc.title = removeAtag(str);
      nextToc.ignoreAllSubs = true;
    }

    var slug = slugify(config.id || str);
    var url = router.toURL(router.getCurrentPath(), { id: slug });
    nextToc.slug = url;
    _self.toc.push(nextToc);

    return ("<h" + level + " id=\"" + slug + "\"><a href=\"" + url + "\" data-id=\"" + slug + "\" class=\"anchor\"><span>" + str + "</span></a></h" + level + ">");
  };

  origin.code = highlightCodeCompiler({ renderer: renderer });
  origin.link = linkCompiler({
    renderer: renderer,
    router: router,
    linkTarget: linkTarget,
    linkRel: linkRel,
    compilerClass: _self,
  });
  origin.paragraph = paragraphCompiler({ renderer: renderer });
  origin.image = imageCompiler({ renderer: renderer, contentBase: contentBase, router: router });
  origin.list = taskListCompiler({ renderer: renderer });
  origin.listitem = taskListItemCompiler({ renderer: renderer });

  renderer.origin = origin;

  return renderer;
};

/**
 * Compile sidebar
 * @param {String} text Text content
 * @param {Number} level Type of heading (h<level> tag)
 * @returns {String} Sidebar element
 */
Compiler.prototype.sidebar = function sidebar (text, level) {
  var ref = this;
    var toc = ref.toc;
  var currentPath = this.router.getCurrentPath();
  var html = '';

  if (text) {
    html = this.compile(text);
  } else {
    for (var i = 0; i < toc.length; i++) {
      if (toc[i].ignoreSubHeading) {
        var deletedHeaderLevel = toc[i].level;
        toc.splice(i, 1);
        // Remove headers who are under current header
        for (
          var j = i;
          j < toc.length && deletedHeaderLevel < toc[j].level;
          j++
        ) {
          toc.splice(j, 1) && j-- && i++;
        }

        i--;
      }
    }

    var tree$1 = this.cacheTree[currentPath] || genTree(toc, level);
    html = tree(tree$1, '<ul>{inner}</ul>');
    this.cacheTree[currentPath] = tree$1;
  }

  return html;
};

/**
 * Compile sub sidebar
 * @param {Number} level Type of heading (h<level> tag)
 * @returns {String} Sub-sidebar element
 */
Compiler.prototype.subSidebar = function subSidebar (level) {
  if (!level) {
    this.toc = [];
    return;
  }

  var currentPath = this.router.getCurrentPath();
  var ref = this;
    var cacheTree = ref.cacheTree;
    var toc = ref.toc;

  toc[0] && toc[0].ignoreAllSubs && toc.splice(0);
  toc[0] && toc[0].level === 1 && toc.shift();

  for (var i = 0; i < toc.length; i++) {
    toc[i].ignoreSubHeading && toc.splice(i, 1) && i--;
  }

  var tree$1 = cacheTree[currentPath] || genTree(toc, level);

  cacheTree[currentPath] = tree$1;
  this.toc = [];
  return tree(tree$1);
};

Compiler.prototype.header = function header (text, level) {
  return this.heading(text, level);
};

Compiler.prototype.article = function article (text) {
  return this.compile(text);
};

/**
 * Compile cover page
 * @param {Text} text Text content
 * @returns {String} Cover page
 */
Compiler.prototype.cover = function cover (text) {
  var cacheToc = this.toc.slice();
  var html = this.compile(text);

  this.toc = cacheToc.slice();

  return html;
};

var cached$2 = {};

function walkFetchEmbed(ref, cb) {
  var embedTokens = ref.embedTokens;
  var compile = ref.compile;
  var fetch = ref.fetch;

  var token;
  var step = 0;
  var count = 1;

  if (!embedTokens.length) {
    return cb({});
  }

  while ((token = embedTokens[step++])) {
    // eslint-disable-next-line no-shadow
    var next = (function(token) {
      return function (text) {
        var embedToken;
        if (text) {
          if (token.embed.type === 'markdown') {
            var path = token.embed.url.split('/');
            path.pop();
            path = path.join('/');
            // Resolves relative links to absolute
            text = text.replace(/\[([^[\]]+)\]\(([^)]+)\)/g, function (x) {
              var linkBeginIndex = x.indexOf('(');
              if (x.slice(linkBeginIndex, linkBeginIndex + 2) === '(.') {
                return (
                  x.substring(0, linkBeginIndex) +
                  "(" + (window.location.protocol) + "//" + (window.location.host) + path + "/" +
                  x.substring(linkBeginIndex + 1, x.length - 1) +
                  ')'
                );
              }
              return x;
            });

            // This may contain YAML front matter and will need to be stripped.
            var frontMatterInstalled =
              ($docsify.frontMatter || {}).installed || false;
            if (frontMatterInstalled === true) {
              text = $docsify.frontMatter.parseMarkdown(text);
            }

            embedToken = compile.lexer(text);
          } else if (token.embed.type === 'code') {
            if (token.embed.fragment) {
              var fragment = token.embed.fragment;
              var pattern = new RegExp(
                ("(?:###|\\/\\/\\/)\\s*\\[" + fragment + "\\]([\\s\\S]*)(?:###|\\/\\/\\/)\\s*\\[" + fragment + "\\]")
              );
              text = stripIndent((text.match(pattern) || [])[1] || '').trim();
            }

            embedToken = compile.lexer(
              '```' +
                token.embed.lang +
                '\n' +
                text.replace(/`/g, '@DOCSIFY_QM@') +
                '\n```\n'
            );
          } else if (token.embed.type === 'mermaid') {
            embedToken = [
              { type: 'html', text: ("<div class=\"mermaid\">\n" + text + "\n</div>") } ];
            embedToken.links = {};
          } else {
            embedToken = [{ type: 'html', text: text }];
            embedToken.links = {};
          }
        }

        cb({ token: token, embedToken: embedToken });
        if (++count >= step) {
          cb({});
        }
      };
    })(token);

    if (token.embed.url) {
      {
        fetch(token.embed.url).then(next);
      }
    } else {
      next(token.embed.html);
    }
  }
}

function prerenderEmbed(ref, done) {
  var compiler = ref.compiler;
  var raw = ref.raw; if ( raw === void 0 ) raw = '';
  var fetch = ref.fetch;

  var hit = cached$2[raw];
  if (hit) {
    var copy = hit.slice();
    copy.links = hit.links;
    return done(copy);
  }

  var compile = compiler._marked;
  var tokens = compile.lexer(raw);
  var embedTokens = [];
  var linkRE = compile.Lexer.rules.inline.link;
  var links = tokens.links;

  tokens.forEach(function (token, index) {
    if (token.type === 'paragraph') {
      token.text = token.text.replace(
        new RegExp(linkRE.source, 'g'),
        function (src, filename, href, title) {
          var embed = compiler.compileEmbed(href, title);

          if (embed) {
            embedTokens.push({
              index: index,
              embed: embed,
            });
          }

          return src;
        }
      );
    }
  });

  // keep track of which tokens have been embedded so far
  // so that we know where to insert the embedded tokens as they
  // are returned
  var moves = [];
  walkFetchEmbed({ compile: compile, embedTokens: embedTokens, fetch: fetch }, function (ref) {
    var embedToken = ref.embedToken;
    var token = ref.token;

    if (token) {
      // iterate through the array of previously inserted tokens
      // to determine where the current embedded tokens should be inserted
      var index = token.index;
      moves.forEach(function (pos) {
        if (index > pos.start) {
          index += pos.length;
        }
      });

      merge(links, embedToken.links);

      tokens = tokens
        .slice(0, index)
        .concat(embedToken, tokens.slice(index + 1));
      moves.push({ start: index, length: embedToken.length - 1 });
    } else {
      cached$2[raw] = tokens.concat();
      tokens.links = cached$2[raw].links = links;
      done(tokens);
    }
  });
}

function cwd() {
  var args = [], len = arguments.length;
  while ( len-- ) args[ len ] = arguments[ len ];

  return path.resolve.apply(void 0, [ process.cwd() ].concat( args ));
}

function isExternal(url) {
  var match = url.match(
    /^([^:/?#]+:)?(?:\/\/([^/?#]*))?([^?#]+)?(\?[^#]*)?(#.*)?/
  );
  if (
    typeof match[1] === 'string' &&
    match[1].length > 0 &&
    match[1].toLowerCase() !== location.protocol
  ) {
    return true;
  }
  if (
    typeof match[2] === 'string' &&
    match[2].length > 0 &&
    match[2].replace(
      new RegExp(
        ':(' + { 'http:': 80, 'https:': 443 }[location.protocol] + ')?$'
      ),
      ''
    ) !== location.host
  ) {
    return true;
  }
  return false;
}

function mainTpl(config) {
  var html = "<nav class=\"app-nav" + (config.repo ? '' : ' no-badge') + "\"><!--navbar--></nav>";

  if (config.repo) {
    html += corner(config.repo);
  }

  if (config.coverpage) {
    html += cover();
  }

  html += main(config);

  return html;
}

var Renderer = function Renderer(ref) {
  var this$1 = this;
  var template = ref.template;
  var config = ref.config;
  var cache = ref.cache;

  this.html = template;
  this.config = config = Object.assign({}, config, {
    routerMode: 'history',
  });
  this.cache = cache;

  this.router = new AbstractHistory(config);
  this.compiler = new Compiler(config, this.router);

  this.router.getCurrentPath = function () { return this$1.url; };
  this._renderHtml(
    'inject-config',
    ("<script>window.$docsify = " + (JSON.stringify(config)) + "</script>")
  );
  this._renderHtml('inject-app', mainTpl(config));

  this.template = this.html;
};

Renderer.prototype._getPath = function _getPath (url) {
  var file = this.router.getFile(url);

  return isAbsolutePath(file) ? file : cwd(("./" + file));
};

Renderer.prototype.renderToString = function renderToString (url) {return __async(function*(){
  this.url = url = this.router.parse(url).path;
  this.isRemoteUrl = isExternal(this.url);
  var ref = this.config;
    var loadSidebar = ref.loadSidebar;
    var loadNavbar = ref.loadNavbar;
    var coverpage = ref.coverpage;

  var mainFile = this._getPath(url);
  this._renderHtml('main', yield this._render(mainFile, 'main'));

  if (loadSidebar) {
    var name = loadSidebar === true ? '_sidebar.md' : loadSidebar;
    var sidebarFile = this._getPath(path.resolve(url, ("./" + name)));
    this._renderHtml('sidebar', yield this._render(sidebarFile, 'sidebar'));
  }

  if (loadNavbar) {
    var name$1 = loadNavbar === true ? '_navbar.md' : loadNavbar;
    var navbarFile = this._getPath(path.resolve(url, ("./" + name$1)));
    this._renderHtml('navbar', yield this._render(navbarFile, 'navbar'));
  }

  if (coverpage) {
    var path$1 = null;
    if (typeof coverpage === 'string') {
      if (url === '/') {
        path$1 = coverpage;
      }
    } else if (Array.isArray(coverpage)) {
      path$1 = coverpage.indexOf(url) > -1 && '_coverpage.md';
    } else {
      var cover = coverpage[url];
      path$1 = cover === true ? '_coverpage.md' : cover;
    }

    var coverFile = this._getPath(path.resolve(url, ("./" + path$1)));

    this._renderHtml('cover', yield this._render(coverFile), 'cover');
  }

  var html = this.isRemoteUrl
    ? DOMPurify.sanitize(this.html, { ADD_TAGS: ['script'] })
    : this.html;
  this.html = this.template;
  return html;
}.call(this))};

Renderer.prototype._renderHtml = function _renderHtml (match, content) {
  this.html = this.html.replace(new RegExp(("<!--" + match + "-->"), 'g'), content);

  return this.html;
};

Renderer.prototype._render = function _render (path, type) {return __async(function*(){
    var this$1 = this;

  var html = yield this._loadFile(path);
  var ref = this.config;
    var subMaxLevel = ref.subMaxLevel;
    var maxLevel = ref.maxLevel;
  var tokens;

  switch (type) {
    case 'sidebar':
      html =
        this.compiler.sidebar(html, maxLevel) +
        "<script>window.__SUB_SIDEBAR__ = " + (JSON.stringify(
          this.compiler.subSidebar(subMaxLevel)
        )) + "</script>";
      break;
    case 'cover':
      html = this.compiler.cover(html);
      break;
    case 'main':
      tokens = yield new Promise(function (r) {
        prerenderEmbed(
          {
            fetch: function (url) { return this$1._loadFile(this$1._getPath(url)); },
            compiler: this$1.compiler,
            raw: html,
          },
          r
        );
      });
      html = this.compiler.compile(tokens);
      break;
    case 'navbar':
    case 'article':
    default:
      html = this.compiler.compile(html);
      break;
  }

  return html;
}.call(this))};

Renderer.prototype._loadFile = function _loadFile (filePath) {return __async(function*(){
  debug('docsify')(("load > " + filePath));
  var content;
  try {
    if (isAbsolutePath(filePath)) {
      var res = yield fetch(filePath);
      if (!res.ok) {
        throw Error();
      }

      content = yield res.text();
      this.lock = 0;
    } else {
      content = yield fs.readFileSync(filePath, 'utf8');
      this.lock = 0;
    }

    return content;
  } catch (e) {
    this.lock = this.lock || 0;
    if (++this.lock > 10) {
      this.lock = 0;
      return;
    }

    var fileName = path.basename(filePath);
    var result = yield this._loadFile(
      resolvePathname(("../" + fileName), filePath)
    );

    return result;
  }
}.call(this))};

Renderer.version = '4.12.1';

module.exports = Renderer;
