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

	/**
	 * The dependencies map is built automatically with gulp.
	 *
	 * @type {Object<string, string | string[]>}
	 */
	var lang_dependencies = /*dependencies_placeholder[*/{
		"javascript": "clike",
		"actionscript": "javascript",
		"apex": [
			"clike",
			"sql"
		],
		"arduino": "cpp",
		"aspnet": [
			"markup",
			"csharp"
		],
		"birb": "clike",
		"bison": "c",
		"c": "clike",
		"csharp": "clike",
		"cpp": "c",
		"coffeescript": "javascript",
		"crystal": "ruby",
		"css-extras": "css",
		"d": "clike",
		"dart": "clike",
		"django": "markup-templating",
		"ejs": [
			"javascript",
			"markup-templating"
		],
		"etlua": [
			"lua",
			"markup-templating"
		],
		"erb": [
			"ruby",
			"markup-templating"
		],
		"fsharp": "clike",
		"firestore-security-rules": "clike",
		"flow": "javascript",
		"ftl": "markup-templating",
		"gml": "clike",
		"glsl": "c",
		"go": "clike",
		"groovy": "clike",
		"haml": "ruby",
		"handlebars": "markup-templating",
		"haxe": "clike",
		"hlsl": "c",
		"java": "clike",
		"javadoc": [
			"markup",
			"java",
			"javadoclike"
		],
		"jolie": "clike",
		"jsdoc": [
			"javascript",
			"javadoclike",
			"typescript"
		],
		"js-extras": "javascript",
		"json5": "json",
		"jsonp": "json",
		"js-templates": "javascript",
		"kotlin": "clike",
		"latte": [
			"clike",
			"markup-templating",
			"php"
		],
		"less": "css",
		"lilypond": "scheme",
		"markdown": "markup",
		"markup-templating": "markup",
		"mongodb": "javascript",
		"n4js": "javascript",
		"nginx": "clike",
		"objectivec": "c",
		"opencl": "c",
		"parser": "markup",
		"php": "markup-templating",
		"phpdoc": [
			"php",
			"javadoclike"
		],
		"php-extras": "php",
		"plsql": "sql",
		"processing": "clike",
		"protobuf": "clike",
		"pug": [
			"markup",
			"javascript"
		],
		"purebasic": "clike",
		"purescript": "haskell",
		"qml": "javascript",
		"qore": "clike",
		"racket": "scheme",
		"jsx": [
			"markup",
			"javascript"
		],
		"tsx": [
			"jsx",
			"typescript"
		],
		"reason": "clike",
		"ruby": "clike",
		"sass": "css",
		"scss": "css",
		"scala": "java",
		"shell-session": "bash",
		"smarty": "markup-templating",
		"solidity": "clike",
		"soy": "markup-templating",
		"sparql": "turtle",
		"sqf": "clike",
		"swift": "clike",
		"t4-cs": [
			"t4-templating",
			"csharp"
		],
		"t4-vb": [
			"t4-templating",
			"vbnet"
		],
		"tap": "yaml",
		"tt2": [
			"clike",
			"markup-templating"
		],
		"textile": "markup",
		"twig": "markup",
		"typescript": "javascript",
		"vala": "clike",
		"vbnet": "basic",
		"velocity": "markup",
		"wiki": "markup",
		"xeora": "markup",
		"xml-doc": "markup",
		"xquery": "markup"
	}/*]*/;

	var lang_aliases = /*aliases_placeholder[*/{
		"html": "markup",
		"xml": "markup",
		"svg": "markup",
		"mathml": "markup",
		"ssml": "markup",
		"atom": "markup",
		"rss": "markup",
		"js": "javascript",
		"g4": "antlr4",
		"adoc": "asciidoc",
		"shell": "bash",
		"shortcode": "bbcode",
		"rbnf": "bnf",
		"oscript": "bsl",
		"cs": "csharp",
		"dotnet": "csharp",
		"coffee": "coffeescript",
		"conc": "concurnas",
		"jinja2": "django",
		"dns-zone": "dns-zone-file",
		"dockerfile": "docker",
		"eta": "ejs",
		"xlsx": "excel-formula",
		"xls": "excel-formula",
		"gamemakerlanguage": "gml",
		"hs": "haskell",
		"gitignore": "ignore",
		"hgignore": "ignore",
		"npmignore": "ignore",
		"webmanifest": "json",
		"kt": "kotlin",
		"kts": "kotlin",
		"tex": "latex",
		"context": "latex",
		"ly": "lilypond",
		"emacs": "lisp",
		"elisp": "lisp",
		"emacs-lisp": "lisp",
		"md": "markdown",
		"moon": "moonscript",
		"n4jsd": "n4js",
		"nani": "naniscript",
		"objc": "objectivec",
		"objectpascal": "pascal",
		"px": "pcaxis",
		"pcode": "peoplecode",
		"pq": "powerquery",
		"mscript": "powerquery",
		"pbfasm": "purebasic",
		"purs": "purescript",
		"py": "python",
		"rkt": "racket",
		"rpy": "renpy",
		"robot": "robotframework",
		"rb": "ruby",
		"sh-session": "shell-session",
		"shellsession": "shell-session",
		"smlnj": "sml",
		"sol": "solidity",
		"sln": "solution-file",
		"rq": "sparql",
		"t4": "t4-cs",
		"trig": "turtle",
		"ts": "typescript",
		"tsconfig": "typoscript",
		"uscript": "unrealscript",
		"uc": "unrealscript",
		"vb": "visual-basic",
		"vba": "visual-basic",
		"xeoracube": "xeora",
		"yml": "yaml"
	}/*]*/;

	/**
	 * @typedef LangDataItem
	 * @property {{ success?: () => void, error?: () => void }[]} callbacks
	 * @property {boolean} [error]
	 * @property {boolean} [loading]
	 */
	/** @type {Object<string, LangDataItem>} */
	var lang_data = {};

	var ignored_language = 'none';
	var languages_path = 'components/';

	var script = Prism.util.currentScript();
	if (script) {
		var autoloaderFile = /\bplugins\/autoloader\/prism-autoloader\.(?:min\.)?js(?:\?[^\r\n/]*)?$/i;
		var prismFile = /(^|\/)[\w-]+\.(?:min\.)?js(?:\?[^\r\n/]*)?$/i;

		var autoloaderPath = script.getAttribute('data-autoloader-path');
		if (autoloaderPath != null) {
			// data-autoloader-path is set, so just use it
			languages_path = autoloaderPath.trim().replace(/\/?$/, '/');
		} else {
			var src = script.src;
			if (autoloaderFile.test(src)) {
				// the script is the original autoloader script in the usual Prism project structure
				languages_path = src.replace(autoloaderFile, 'components/');
			} else if (prismFile.test(src)) {
				// the script is part of a bundle like a custom prism.js from the download page
				languages_path = src.replace(prismFile, '$1components/');
			}
		}
	}

	var config = Prism.plugins.autoloader = {
		languages_path: languages_path,
		use_minified: true,
		loadLanguages: loadLanguages
	};


	/**
	 * Lazily loads an external script.
	 *
	 * @param {string} src
	 * @param {() => void} [success]
	 * @param {() => void} [error]
	 */
	function addScript(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 all additional dependencies of the given element defined by the `data-dependencies` attribute.
	 *
	 * @param {Element} element
	 * @returns {string[]}
	 */
	function getDependencies(element) {
		var deps = (element.getAttribute('data-dependencies') || '').trim();
		if (!deps) {
			var parent = element.parentElement;
			if (parent && parent.tagName.toLowerCase() === 'pre') {
				deps = (parent.getAttribute('data-dependencies') || '').trim();
			}
		}
		return deps ? deps.split(/\s*,\s*/g) : [];
	}

	/**
	 * Returns whether the given language is currently loaded.
	 *
	 * @param {string} lang
	 * @returns {boolean}
	 */
	function isLoaded(lang) {
		if (lang.indexOf('!') >= 0) {
			// forced reload
			return false;
		}

		lang = lang_aliases[lang] || lang; // resolve alias

		if (lang in Prism.languages) {
			// the given language is already loaded
			return true;
		}

		// this will catch extensions like CSS extras that don't add a grammar to Prism.languages
		var data = lang_data[lang];
		return data && !data.error && data.loading === false;
	}

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

	/**
	 * Loads all given grammars concurrently.
	 *
	 * @param {string[]|string} languages
	 * @param {(languages: string[]) => void} [success]
	 * @param {(language: string) => void} [error] This callback will be invoked on the first language to fail.
	 */
	function loadLanguages(languages, success, error) {
		if (typeof languages === 'string') {
			languages = [languages];
		}

		var total = languages.length;
		var completed = 0;
		var failed = false;

		if (total === 0) {
			if (success) {
				setTimeout(success, 0);
			}
			return;
		}

		function successCallback() {
			if (failed) {
				return;
			}
			completed++;
			if (completed === total) {
				success && success(languages);
			}
		}

		languages.forEach(function (lang) {
			loadLanguage(lang, successCallback, function () {
				if (failed) {
					return;
				}
				failed = true;
				error && error(lang);
			});
		});
	}

	/**
	 * Loads a grammar with its dependencies.
	 *
	 * @param {string} lang
	 * @param {() => void} [success]
	 * @param {() => void} [error]
	 */
	function loadLanguage(lang, success, error) {
		var force = lang.indexOf('!') >= 0;

		lang = lang.replace('!', '');
		lang = lang_aliases[lang] || lang;

		function load() {
			var data = lang_data[lang];
			if (!data) {
				data = lang_data[lang] = {
					callbacks: []
				};
			}
			data.callbacks.push({
				success: success,
				error: error
			});

			if (!force && isLoaded(lang)) {
				// the language is already loaded and we aren't forced to reload
				languageCallback(lang, 'success');
			} else if (!force && data.error) {
				// the language failed to load before and we don't reload
				languageCallback(lang, 'error');
			} else if (force || !data.loading) {
				// the language isn't currently loading and/or we are forced to reload
				data.loading = true;
				data.error = false;

				addScript(getLanguagePath(lang), function () {
					data.loading = false;
					languageCallback(lang, 'success');

				}, function () {
					data.loading = false;
					data.error = true;
					languageCallback(lang, 'error');
				});
			}
		};

		var dependencies = lang_dependencies[lang];
		if (dependencies && dependencies.length) {
			loadLanguages(dependencies, load, error);
		} else {
			load();
		}
	}

	/**
	 * Runs all callbacks of the given type for the given language.
	 *
	 * @param {string} lang
	 * @param {"success" | "error"} type
	 */
	function languageCallback(lang, type) {
		if (lang_data[lang]) {
			var callbacks = lang_data[lang].callbacks;
			for (var i = 0, l = callbacks.length; i < l; i++) {
				var callback = callbacks[i][type];
				if (callback) {
					setTimeout(callback, 0);
				}
			}
			callbacks.length = 0;
		}
	}

	Prism.hooks.add('complete', function (env) {
		var element = env.element;
		var language = env.language;
		if (!element || !language || language === ignored_language) {
			return;
		}

		var deps = getDependencies(element);
		if (/^diff-./i.test(language)) {
			// the "diff-xxxx" format is used by the Diff Highlight plugin
			deps.push('diff');
			deps.push(language.substr('diff-'.length));
		} else {
			deps.push(language);
		}

		if (!deps.every(isLoaded)) {
			// the language or some dependencies aren't loaded
			loadLanguages(deps, function () {
				Prism.highlightElement(element);
			});
		}
	});

}());
