(function() {
  if (
    typeof self === 'undefined' ||
    !self.Prism ||
    !self.document ||
    !document.createElement
  ) {
    return;
  }

  // The dependencies map is built automatically with gulp
  var lang_dependencies = /*languages_placeholder[*/ {
    javascript: 'clike',
    actionscript: 'javascript',
    arduino: 'cpp',
    aspnet: 'markup',
    bison: 'c',
    c: 'clike',
    csharp: 'clike',
    cpp: 'c',
    coffeescript: 'javascript',
    crystal: 'ruby',
    'css-extras': 'css',
    d: 'clike',
    dart: 'clike',
    django: 'markup',
    fsharp: 'clike',
    flow: 'javascript',
    glsl: 'clike',
    go: 'clike',
    groovy: 'clike',
    haml: 'ruby',
    handlebars: 'markup',
    haxe: 'clike',
    java: 'clike',
    jolie: 'clike',
    kotlin: 'clike',
    less: 'css',
    markdown: 'markup',
    n4js: 'javascript',
    nginx: 'clike',
    objectivec: 'c',
    opencl: 'cpp',
    parser: 'markup',
    php: 'clike',
    'php-extras': 'php',
    processing: 'clike',
    protobuf: 'clike',
    pug: 'javascript',
    qore: 'clike',
    jsx: ['markup', 'javascript'],
    tsx: ['jsx', 'typescript'],
    reason: 'clike',
    ruby: 'clike',
    sass: 'css',
    scss: 'css',
    scala: 'java',
    smarty: 'markup',
    swift: 'clike',
    textile: 'markup',
    twig: 'markup',
    typescript: 'javascript',
    vbnet: 'basic',
    wiki: 'markup',
    xeora: 'markup',
  } /*]*/;

  var lang_data = {};

  var ignored_language = 'none';

  var script = document.getElementsByTagName('script');
  script = script[script.length - 1];
  var languages_path = 'components/';
  if (script.hasAttribute('data-autoloader-path')) {
    var path = script.getAttribute('data-autoloader-path').trim();
    if (path.length > 0 && !/^[a-z]+:\/\//i.test(script.src)) {
      languages_path = path.replace(/\/?$/, '/');
    }
  } else if (/[\w-]+\.js$/.test(script.src)) {
    languages_path = script.src.replace(/[\w-]+\.js$/, 'components/');
  }
  var config = (Prism.plugins.autoloader = {
    languages_path: languages_path,
    use_minified: true,
  });

  /**
   * Lazy loads an external script
   * @param {string} src
   * @param {function=} success
   * @param {function=} error
   */
  var script = function(src, success, error) {
    var s = document.createElement('script');
    s.src = src;
    s.async = true;
    s.onload = function() {
      document.body.removeChild(s);
      success && success();
    };
    s.onerror = function() {
      document.body.removeChild(s);
      error && error();
    };
    document.body.appendChild(s);
  };

  /**
   * Returns the path to a grammar, using the language_path and use_minified config keys.
   * @param {string} lang
   * @returns {string}
   */
  var getLanguagePath = function(lang) {
    return (
      config.languages_path +
      'prism-' +
      lang +
      (config.use_minified ? '.min' : '') +
      '.js'
    );
  };

  /**
   * Tries to load a grammar and
   * highlight again the given element once loaded.
   * @param {string} lang
   * @param {HTMLElement} elt
   */
  var registerElement = function(lang, elt) {
    var data = lang_data[lang];
    if (!data) {
      data = lang_data[lang] = {};
    }

    // Look for additional dependencies defined on the <code> or <pre> tags
    var deps = elt.getAttribute('data-dependencies');
    if (
      !deps &&
      elt.parentNode &&
      elt.parentNode.tagName.toLowerCase() === 'pre'
    ) {
      deps = elt.parentNode.getAttribute('data-dependencies');
    }

    if (deps) {
      deps = deps.split(/\s*,\s*/g);
    } else {
      deps = [];
    }

    loadLanguages(deps, function() {
      loadLanguage(lang, function() {
        Prism.highlightElement(elt);
      });
    });
  };

  /**
   * Sequentially loads an array of grammars.
   * @param {string[]|string} langs
   * @param {function=} success
   * @param {function=} error
   */
  var loadLanguages = function(langs, success, error) {
    if (typeof langs === 'string') {
      langs = [langs];
    }
    var i = 0;
    var l = langs.length;
    var f = function() {
      if (i < l) {
        loadLanguage(
          langs[i],
          function() {
            i++;
            f();
          },
          function() {
            error && error(langs[i]);
          }
        );
      } else if (i === l) {
        success && success(langs);
      }
    };
    f();
  };

  /**
   * Load a grammar with its dependencies
   * @param {string} lang
   * @param {function=} success
   * @param {function=} error
   */
  var loadLanguage = function(lang, success, error) {
    var load = function() {
      var force = false;
      // Do we want to force reload the grammar?
      if (lang.indexOf('!') >= 0) {
        force = true;
        lang = lang.replace('!', '');
      }

      var data = lang_data[lang];
      if (!data) {
        data = lang_data[lang] = {};
      }
      if (success) {
        if (!data.success_callbacks) {
          data.success_callbacks = [];
        }
        data.success_callbacks.push(success);
      }
      if (error) {
        if (!data.error_callbacks) {
          data.error_callbacks = [];
        }
        data.error_callbacks.push(error);
      }

      if (!force && Prism.languages[lang]) {
        languageSuccess(lang);
      } else if (!force && data.error) {
        languageError(lang);
      } else if (force || !data.loading) {
        data.loading = true;
        var src = getLanguagePath(lang);
        script(
          src,
          function() {
            data.loading = false;
            languageSuccess(lang);
          },
          function() {
            data.loading = false;
            data.error = true;
            languageError(lang);
          }
        );
      }
    };
    var dependencies = lang_dependencies[lang];
    if (dependencies && dependencies.length) {
      loadLanguages(dependencies, load);
    } else {
      load();
    }
  };

  /**
   * Runs all success callbacks for this language.
   * @param {string} lang
   */
  var languageSuccess = function(lang) {
    if (
      lang_data[lang] &&
      lang_data[lang].success_callbacks &&
      lang_data[lang].success_callbacks.length
    ) {
      lang_data[lang].success_callbacks.forEach(function(f) {
        f(lang);
      });
    }
  };

  /**
   * Runs all error callbacks for this language.
   * @param {string} lang
   */
  var languageError = function(lang) {
    if (
      lang_data[lang] &&
      lang_data[lang].error_callbacks &&
      lang_data[lang].error_callbacks.length
    ) {
      lang_data[lang].error_callbacks.forEach(function(f) {
        f(lang);
      });
    }
  };

  Prism.hooks.add('complete', function(env) {
    if (env.element && env.language && !env.grammar) {
      if (env.language !== ignored_language) {
        registerElement(env.language, env.element);
      }
    }
  });
})();
