prism-core.js 34 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185
  1. /// <reference lib="WebWorker"/>
  2. var _self = (typeof window !== 'undefined')
  3. ? window // if in browser
  4. : (
  5. (typeof WorkerGlobalScope !== 'undefined' && self instanceof WorkerGlobalScope)
  6. ? self // if in worker
  7. : {} // if in node js
  8. );
  9. /**
  10. * Prism: Lightweight, robust, elegant syntax highlighting
  11. *
  12. * @license MIT <https://opensource.org/licenses/MIT>
  13. * @author Lea Verou <https://lea.verou.me>
  14. * @namespace
  15. * @public
  16. */
  17. var Prism = (function (_self){
  18. // Private helper vars
  19. var lang = /\blang(?:uage)?-([\w-]+)\b/i;
  20. var uniqueId = 0;
  21. var _ = {
  22. /**
  23. * By default, Prism will attempt to highlight all code elements (by calling {@link Prism.highlightAll}) on the
  24. * current page after the page finished loading. This might be a problem if e.g. you wanted to asynchronously load
  25. * additional languages or plugins yourself.
  26. *
  27. * By setting this value to `true`, Prism will not automatically highlight all code elements on the page.
  28. *
  29. * You obviously have to change this value before the automatic highlighting started. To do this, you can add an
  30. * empty Prism object into the global scope before loading the Prism script like this:
  31. *
  32. * ```js
  33. * window.Prism = window.Prism || {};
  34. * Prism.manual = true;
  35. * // add a new <script> to load Prism's script
  36. * ```
  37. *
  38. * @default false
  39. * @type {boolean}
  40. * @memberof Prism
  41. * @public
  42. */
  43. manual: _self.Prism && _self.Prism.manual,
  44. disableWorkerMessageHandler: _self.Prism && _self.Prism.disableWorkerMessageHandler,
  45. /**
  46. * A namespace for utility methods.
  47. *
  48. * All function in this namespace that are not explicitly marked as _public_ are for __internal use only__ and may
  49. * change or disappear at any time.
  50. *
  51. * @namespace
  52. * @memberof Prism
  53. */
  54. util: {
  55. encode: function encode(tokens) {
  56. if (tokens instanceof Token) {
  57. return new Token(tokens.type, encode(tokens.content), tokens.alias);
  58. } else if (Array.isArray(tokens)) {
  59. return tokens.map(encode);
  60. } else {
  61. return tokens.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/\u00a0/g, ' ');
  62. }
  63. },
  64. /**
  65. * Returns the name of the type of the given value.
  66. *
  67. * @param {any} o
  68. * @returns {string}
  69. * @example
  70. * type(null) === 'Null'
  71. * type(undefined) === 'Undefined'
  72. * type(123) === 'Number'
  73. * type('foo') === 'String'
  74. * type(true) === 'Boolean'
  75. * type([1, 2]) === 'Array'
  76. * type({}) === 'Object'
  77. * type(String) === 'Function'
  78. * type(/abc+/) === 'RegExp'
  79. */
  80. type: function (o) {
  81. return Object.prototype.toString.call(o).slice(8, -1);
  82. },
  83. /**
  84. * Returns a unique number for the given object. Later calls will still return the same number.
  85. *
  86. * @param {Object} obj
  87. * @returns {number}
  88. */
  89. objId: function (obj) {
  90. if (!obj['__id']) {
  91. Object.defineProperty(obj, '__id', { value: ++uniqueId });
  92. }
  93. return obj['__id'];
  94. },
  95. /**
  96. * Creates a deep clone of the given object.
  97. *
  98. * The main intended use of this function is to clone language definitions.
  99. *
  100. * @param {T} o
  101. * @param {Record<number, any>} [visited]
  102. * @returns {T}
  103. * @template T
  104. */
  105. clone: function deepClone(o, visited) {
  106. visited = visited || {};
  107. var clone, id;
  108. switch (_.util.type(o)) {
  109. case 'Object':
  110. id = _.util.objId(o);
  111. if (visited[id]) {
  112. return visited[id];
  113. }
  114. clone = /** @type {Record<string, any>} */ ({});
  115. visited[id] = clone;
  116. for (var key in o) {
  117. if (o.hasOwnProperty(key)) {
  118. clone[key] = deepClone(o[key], visited);
  119. }
  120. }
  121. return /** @type {any} */ (clone);
  122. case 'Array':
  123. id = _.util.objId(o);
  124. if (visited[id]) {
  125. return visited[id];
  126. }
  127. clone = [];
  128. visited[id] = clone;
  129. (/** @type {Array} */(/** @type {any} */(o))).forEach(function (v, i) {
  130. clone[i] = deepClone(v, visited);
  131. });
  132. return /** @type {any} */ (clone);
  133. default:
  134. return o;
  135. }
  136. },
  137. /**
  138. * Returns the Prism language of the given element set by a `language-xxxx` or `lang-xxxx` class.
  139. *
  140. * If no language is set for the element or the element is `null` or `undefined`, `none` will be returned.
  141. *
  142. * @param {Element} element
  143. * @returns {string}
  144. */
  145. getLanguage: function (element) {
  146. while (element && !lang.test(element.className)) {
  147. element = element.parentElement;
  148. }
  149. if (element) {
  150. return (element.className.match(lang) || [, 'none'])[1].toLowerCase();
  151. }
  152. return 'none';
  153. },
  154. /**
  155. * Returns the script element that is currently executing.
  156. *
  157. * This does __not__ work for line script element.
  158. *
  159. * @returns {HTMLScriptElement | null}
  160. */
  161. currentScript: function () {
  162. if (typeof document === 'undefined') {
  163. return null;
  164. }
  165. if ('currentScript' in document && 1 < 2 /* hack to trip TS' flow analysis */) {
  166. return /** @type {any} */ (document.currentScript);
  167. }
  168. // IE11 workaround
  169. // we'll get the src of the current script by parsing IE11's error stack trace
  170. // this will not work for inline scripts
  171. try {
  172. throw new Error();
  173. } catch (err) {
  174. // Get file src url from stack. Specifically works with the format of stack traces in IE.
  175. // A stack will look like this:
  176. //
  177. // Error
  178. // at _.util.currentScript (http://localhost/components/prism-core.js:119:5)
  179. // at Global code (http://localhost/components/prism-core.js:606:1)
  180. var src = (/at [^(\r\n]*\((.*):.+:.+\)$/i.exec(err.stack) || [])[1];
  181. if (src) {
  182. var scripts = document.getElementsByTagName('script');
  183. for (var i in scripts) {
  184. if (scripts[i].src == src) {
  185. return scripts[i];
  186. }
  187. }
  188. }
  189. return null;
  190. }
  191. },
  192. /**
  193. * Returns whether a given class is active for `element`.
  194. *
  195. * The class can be activated if `element` or one of its ancestors has the given class and it can be deactivated
  196. * if `element` or one of its ancestors has the negated version of the given class. The _negated version_ of the
  197. * given class is just the given class with a `no-` prefix.
  198. *
  199. * Whether the class is active is determined by the closest ancestor of `element` (where `element` itself is
  200. * closest ancestor) that has the given class or the negated version of it. If neither `element` nor any of its
  201. * ancestors have the given class or the negated version of it, then the default activation will be returned.
  202. *
  203. * In the paradoxical situation where the closest ancestor contains __both__ the given class and the negated
  204. * version of it, the class is considered active.
  205. *
  206. * @param {Element} element
  207. * @param {string} className
  208. * @param {boolean} [defaultActivation=false]
  209. * @returns {boolean}
  210. */
  211. isActive: function (element, className, defaultActivation) {
  212. var no = 'no-' + className;
  213. while (element) {
  214. var classList = element.classList;
  215. if (classList.contains(className)) {
  216. return true;
  217. }
  218. if (classList.contains(no)) {
  219. return false;
  220. }
  221. element = element.parentElement;
  222. }
  223. return !!defaultActivation;
  224. }
  225. },
  226. /**
  227. * This namespace contains all currently loaded languages and the some helper functions to create and modify languages.
  228. *
  229. * @namespace
  230. * @memberof Prism
  231. * @public
  232. */
  233. languages: {
  234. /**
  235. * Creates a deep copy of the language with the given id and appends the given tokens.
  236. *
  237. * If a token in `redef` also appears in the copied language, then the existing token in the copied language
  238. * will be overwritten at its original position.
  239. *
  240. * ## Best practices
  241. *
  242. * Since the position of overwriting tokens (token in `redef` that overwrite tokens in the copied language)
  243. * doesn't matter, they can technically be in any order. However, this can be confusing to others that trying to
  244. * understand the language definition because, normally, the order of tokens matters in Prism grammars.
  245. *
  246. * Therefore, it is encouraged to order overwriting tokens according to the positions of the overwritten tokens.
  247. * Furthermore, all non-overwriting tokens should be placed after the overwriting ones.
  248. *
  249. * @param {string} id The id of the language to extend. This has to be a key in `Prism.languages`.
  250. * @param {Grammar} redef The new tokens to append.
  251. * @returns {Grammar} The new language created.
  252. * @public
  253. * @example
  254. * Prism.languages['css-with-colors'] = Prism.languages.extend('css', {
  255. * // Prism.languages.css already has a 'comment' token, so this token will overwrite CSS' 'comment' token
  256. * // at its original position
  257. * 'comment': { ... },
  258. * // CSS doesn't have a 'color' token, so this token will be appended
  259. * 'color': /\b(?:red|green|blue)\b/
  260. * });
  261. */
  262. extend: function (id, redef) {
  263. var lang = _.util.clone(_.languages[id]);
  264. for (var key in redef) {
  265. lang[key] = redef[key];
  266. }
  267. return lang;
  268. },
  269. /**
  270. * Inserts tokens _before_ another token in a language definition or any other grammar.
  271. *
  272. * ## Usage
  273. *
  274. * This helper method makes it easy to modify existing languages. For example, the CSS language definition
  275. * not only defines CSS highlighting for CSS documents, but also needs to define highlighting for CSS embedded
  276. * in HTML through `<style>` elements. To do this, it needs to modify `Prism.languages.markup` and add the
  277. * appropriate tokens. However, `Prism.languages.markup` is a regular JavaScript object literal, so if you do
  278. * this:
  279. *
  280. * ```js
  281. * Prism.languages.markup.style = {
  282. * // token
  283. * };
  284. * ```
  285. *
  286. * then the `style` token will be added (and processed) at the end. `insertBefore` allows you to insert tokens
  287. * before existing tokens. For the CSS example above, you would use it like this:
  288. *
  289. * ```js
  290. * Prism.languages.insertBefore('markup', 'cdata', {
  291. * 'style': {
  292. * // token
  293. * }
  294. * });
  295. * ```
  296. *
  297. * ## Special cases
  298. *
  299. * If the grammars of `inside` and `insert` have tokens with the same name, the tokens in `inside`'s grammar
  300. * will be ignored.
  301. *
  302. * This behavior can be used to insert tokens after `before`:
  303. *
  304. * ```js
  305. * Prism.languages.insertBefore('markup', 'comment', {
  306. * 'comment': Prism.languages.markup.comment,
  307. * // tokens after 'comment'
  308. * });
  309. * ```
  310. *
  311. * ## Limitations
  312. *
  313. * The main problem `insertBefore` has to solve is iteration order. Since ES2015, the iteration order for object
  314. * properties is guaranteed to be the insertion order (except for integer keys) but some browsers behave
  315. * differently when keys are deleted and re-inserted. So `insertBefore` can't be implemented by temporarily
  316. * deleting properties which is necessary to insert at arbitrary positions.
  317. *
  318. * To solve this problem, `insertBefore` doesn't actually insert the given tokens into the target object.
  319. * Instead, it will create a new object and replace all references to the target object with the new one. This
  320. * can be done without temporarily deleting properties, so the iteration order is well-defined.
  321. *
  322. * However, only references that can be reached from `Prism.languages` or `insert` will be replaced. I.e. if
  323. * you hold the target object in a variable, then the value of the variable will not change.
  324. *
  325. * ```js
  326. * var oldMarkup = Prism.languages.markup;
  327. * var newMarkup = Prism.languages.insertBefore('markup', 'comment', { ... });
  328. *
  329. * assert(oldMarkup !== Prism.languages.markup);
  330. * assert(newMarkup === Prism.languages.markup);
  331. * ```
  332. *
  333. * @param {string} inside The property of `root` (e.g. a language id in `Prism.languages`) that contains the
  334. * object to be modified.
  335. * @param {string} before The key to insert before.
  336. * @param {Grammar} insert An object containing the key-value pairs to be inserted.
  337. * @param {Object<string, any>} [root] The object containing `inside`, i.e. the object that contains the
  338. * object to be modified.
  339. *
  340. * Defaults to `Prism.languages`.
  341. * @returns {Grammar} The new grammar object.
  342. * @public
  343. */
  344. insertBefore: function (inside, before, insert, root) {
  345. root = root || /** @type {any} */ (_.languages);
  346. var grammar = root[inside];
  347. /** @type {Grammar} */
  348. var ret = {};
  349. for (var token in grammar) {
  350. if (grammar.hasOwnProperty(token)) {
  351. if (token == before) {
  352. for (var newToken in insert) {
  353. if (insert.hasOwnProperty(newToken)) {
  354. ret[newToken] = insert[newToken];
  355. }
  356. }
  357. }
  358. // Do not insert token which also occur in insert. See #1525
  359. if (!insert.hasOwnProperty(token)) {
  360. ret[token] = grammar[token];
  361. }
  362. }
  363. }
  364. var old = root[inside];
  365. root[inside] = ret;
  366. // Update references in other language definitions
  367. _.languages.DFS(_.languages, function(key, value) {
  368. if (value === old && key != inside) {
  369. this[key] = ret;
  370. }
  371. });
  372. return ret;
  373. },
  374. // Traverse a language definition with Depth First Search
  375. DFS: function DFS(o, callback, type, visited) {
  376. visited = visited || {};
  377. var objId = _.util.objId;
  378. for (var i in o) {
  379. if (o.hasOwnProperty(i)) {
  380. callback.call(o, i, o[i], type || i);
  381. var property = o[i],
  382. propertyType = _.util.type(property);
  383. if (propertyType === 'Object' && !visited[objId(property)]) {
  384. visited[objId(property)] = true;
  385. DFS(property, callback, null, visited);
  386. }
  387. else if (propertyType === 'Array' && !visited[objId(property)]) {
  388. visited[objId(property)] = true;
  389. DFS(property, callback, i, visited);
  390. }
  391. }
  392. }
  393. }
  394. },
  395. plugins: {},
  396. /**
  397. * This is the most high-level function in Prism’s API.
  398. * It fetches all the elements that have a `.language-xxxx` class and then calls {@link Prism.highlightElement} on
  399. * each one of them.
  400. *
  401. * This is equivalent to `Prism.highlightAllUnder(document, async, callback)`.
  402. *
  403. * @param {boolean} [async=false] Same as in {@link Prism.highlightAllUnder}.
  404. * @param {HighlightCallback} [callback] Same as in {@link Prism.highlightAllUnder}.
  405. * @memberof Prism
  406. * @public
  407. */
  408. highlightAll: function(async, callback) {
  409. _.highlightAllUnder(document, async, callback);
  410. },
  411. /**
  412. * Fetches all the descendants of `container` that have a `.language-xxxx` class and then calls
  413. * {@link Prism.highlightElement} on each one of them.
  414. *
  415. * The following hooks will be run:
  416. * 1. `before-highlightall`
  417. * 2. `before-all-elements-highlight`
  418. * 3. All hooks of {@link Prism.highlightElement} for each element.
  419. *
  420. * @param {ParentNode} container The root element, whose descendants that have a `.language-xxxx` class will be highlighted.
  421. * @param {boolean} [async=false] Whether each element is to be highlighted asynchronously using Web Workers.
  422. * @param {HighlightCallback} [callback] An optional callback to be invoked on each element after its highlighting is done.
  423. * @memberof Prism
  424. * @public
  425. */
  426. highlightAllUnder: function(container, async, callback) {
  427. var env = {
  428. callback: callback,
  429. container: container,
  430. selector: 'code[class*="language-"], [class*="language-"] code, code[class*="lang-"], [class*="lang-"] code'
  431. };
  432. _.hooks.run('before-highlightall', env);
  433. env.elements = Array.prototype.slice.apply(env.container.querySelectorAll(env.selector));
  434. _.hooks.run('before-all-elements-highlight', env);
  435. for (var i = 0, element; element = env.elements[i++];) {
  436. _.highlightElement(element, async === true, env.callback);
  437. }
  438. },
  439. /**
  440. * Highlights the code inside a single element.
  441. *
  442. * The following hooks will be run:
  443. * 1. `before-sanity-check`
  444. * 2. `before-highlight`
  445. * 3. All hooks of {@link Prism.highlight}. These hooks will be run by an asynchronous worker if `async` is `true`.
  446. * 4. `before-insert`
  447. * 5. `after-highlight`
  448. * 6. `complete`
  449. *
  450. * Some the above hooks will be skipped if the element doesn't contain any text or there is no grammar loaded for
  451. * the element's language.
  452. *
  453. * @param {Element} element The element containing the code.
  454. * It must have a class of `language-xxxx` to be processed, where `xxxx` is a valid language identifier.
  455. * @param {boolean} [async=false] Whether the element is to be highlighted asynchronously using Web Workers
  456. * to improve performance and avoid blocking the UI when highlighting very large chunks of code. This option is
  457. * [disabled by default](https://prismjs.com/faq.html#why-is-asynchronous-highlighting-disabled-by-default).
  458. *
  459. * Note: All language definitions required to highlight the code must be included in the main `prism.js` file for
  460. * asynchronous highlighting to work. You can build your own bundle on the
  461. * [Download page](https://prismjs.com/download.html).
  462. * @param {HighlightCallback} [callback] An optional callback to be invoked after the highlighting is done.
  463. * Mostly useful when `async` is `true`, since in that case, the highlighting is done asynchronously.
  464. * @memberof Prism
  465. * @public
  466. */
  467. highlightElement: function(element, async, callback) {
  468. // Find language
  469. var language = _.util.getLanguage(element);
  470. var grammar = _.languages[language];
  471. // Set language on the element, if not present
  472. element.className = element.className.replace(lang, '').replace(/\s+/g, ' ') + ' language-' + language;
  473. // Set language on the parent, for styling
  474. var parent = element.parentElement;
  475. if (parent && parent.nodeName.toLowerCase() === 'pre') {
  476. parent.className = parent.className.replace(lang, '').replace(/\s+/g, ' ') + ' language-' + language;
  477. }
  478. var code = element.textContent;
  479. var env = {
  480. element: element,
  481. language: language,
  482. grammar: grammar,
  483. code: code
  484. };
  485. function insertHighlightedCode(highlightedCode) {
  486. env.highlightedCode = highlightedCode;
  487. _.hooks.run('before-insert', env);
  488. env.element.innerHTML = env.highlightedCode;
  489. _.hooks.run('after-highlight', env);
  490. _.hooks.run('complete', env);
  491. callback && callback.call(env.element);
  492. }
  493. _.hooks.run('before-sanity-check', env);
  494. if (!env.code) {
  495. _.hooks.run('complete', env);
  496. callback && callback.call(env.element);
  497. return;
  498. }
  499. _.hooks.run('before-highlight', env);
  500. if (!env.grammar) {
  501. insertHighlightedCode(_.util.encode(env.code));
  502. return;
  503. }
  504. if (async && _self.Worker) {
  505. var worker = new Worker(_.filename);
  506. worker.onmessage = function(evt) {
  507. insertHighlightedCode(evt.data);
  508. };
  509. worker.postMessage(JSON.stringify({
  510. language: env.language,
  511. code: env.code,
  512. immediateClose: true
  513. }));
  514. }
  515. else {
  516. insertHighlightedCode(_.highlight(env.code, env.grammar, env.language));
  517. }
  518. },
  519. /**
  520. * Low-level function, only use if you know what you’re doing. It accepts a string of text as input
  521. * and the language definitions to use, and returns a string with the HTML produced.
  522. *
  523. * The following hooks will be run:
  524. * 1. `before-tokenize`
  525. * 2. `after-tokenize`
  526. * 3. `wrap`: On each {@link Token}.
  527. *
  528. * @param {string} text A string with the code to be highlighted.
  529. * @param {Grammar} grammar An object containing the tokens to use.
  530. *
  531. * Usually a language definition like `Prism.languages.markup`.
  532. * @param {string} language The name of the language definition passed to `grammar`.
  533. * @returns {string} The highlighted HTML.
  534. * @memberof Prism
  535. * @public
  536. * @example
  537. * Prism.highlight('var foo = true;', Prism.languages.javascript, 'javascript');
  538. */
  539. highlight: function (text, grammar, language) {
  540. var env = {
  541. code: text,
  542. grammar: grammar,
  543. language: language
  544. };
  545. _.hooks.run('before-tokenize', env);
  546. env.tokens = _.tokenize(env.code, env.grammar);
  547. _.hooks.run('after-tokenize', env);
  548. return Token.stringify(_.util.encode(env.tokens), env.language);
  549. },
  550. /**
  551. * This is the heart of Prism, and the most low-level function you can use. It accepts a string of text as input
  552. * and the language definitions to use, and returns an array with the tokenized code.
  553. *
  554. * When the language definition includes nested tokens, the function is called recursively on each of these tokens.
  555. *
  556. * This method could be useful in other contexts as well, as a very crude parser.
  557. *
  558. * @param {string} text A string with the code to be highlighted.
  559. * @param {Grammar} grammar An object containing the tokens to use.
  560. *
  561. * Usually a language definition like `Prism.languages.markup`.
  562. * @returns {TokenStream} An array of strings and tokens, a token stream.
  563. * @memberof Prism
  564. * @public
  565. * @example
  566. * let code = `var foo = 0;`;
  567. * let tokens = Prism.tokenize(code, Prism.languages.javascript);
  568. * tokens.forEach(token => {
  569. * if (token instanceof Prism.Token && token.type === 'number') {
  570. * console.log(`Found numeric literal: ${token.content}`);
  571. * }
  572. * });
  573. */
  574. tokenize: function(text, grammar) {
  575. var rest = grammar.rest;
  576. if (rest) {
  577. for (var token in rest) {
  578. grammar[token] = rest[token];
  579. }
  580. delete grammar.rest;
  581. }
  582. var tokenList = new LinkedList();
  583. addAfter(tokenList, tokenList.head, text);
  584. matchGrammar(text, tokenList, grammar, tokenList.head, 0);
  585. return toArray(tokenList);
  586. },
  587. /**
  588. * @namespace
  589. * @memberof Prism
  590. * @public
  591. */
  592. hooks: {
  593. all: {},
  594. /**
  595. * Adds the given callback to the list of callbacks for the given hook.
  596. *
  597. * The callback will be invoked when the hook it is registered for is run.
  598. * Hooks are usually directly run by a highlight function but you can also run hooks yourself.
  599. *
  600. * One callback function can be registered to multiple hooks and the same hook multiple times.
  601. *
  602. * @param {string} name The name of the hook.
  603. * @param {HookCallback} callback The callback function which is given environment variables.
  604. * @public
  605. */
  606. add: function (name, callback) {
  607. var hooks = _.hooks.all;
  608. hooks[name] = hooks[name] || [];
  609. hooks[name].push(callback);
  610. },
  611. /**
  612. * Runs a hook invoking all registered callbacks with the given environment variables.
  613. *
  614. * Callbacks will be invoked synchronously and in the order in which they were registered.
  615. *
  616. * @param {string} name The name of the hook.
  617. * @param {Object<string, any>} env The environment variables of the hook passed to all callbacks registered.
  618. * @public
  619. */
  620. run: function (name, env) {
  621. var callbacks = _.hooks.all[name];
  622. if (!callbacks || !callbacks.length) {
  623. return;
  624. }
  625. for (var i=0, callback; callback = callbacks[i++];) {
  626. callback(env);
  627. }
  628. }
  629. },
  630. Token: Token
  631. };
  632. _self.Prism = _;
  633. // Typescript note:
  634. // The following can be used to import the Token type in JSDoc:
  635. //
  636. // @typedef {InstanceType<import("./prism-core")["Token"]>} Token
  637. /**
  638. * Creates a new token.
  639. *
  640. * @param {string} type See {@link Token#type type}
  641. * @param {string | TokenStream} content See {@link Token#content content}
  642. * @param {string|string[]} [alias] The alias(es) of the token.
  643. * @param {string} [matchedStr=""] A copy of the full string this token was created from.
  644. * @class
  645. * @global
  646. * @public
  647. */
  648. function Token(type, content, alias, matchedStr) {
  649. /**
  650. * The type of the token.
  651. *
  652. * This is usually the key of a pattern in a {@link Grammar}.
  653. *
  654. * @type {string}
  655. * @see GrammarToken
  656. * @public
  657. */
  658. this.type = type;
  659. /**
  660. * The strings or tokens contained by this token.
  661. *
  662. * This will be a token stream if the pattern matched also defined an `inside` grammar.
  663. *
  664. * @type {string | TokenStream}
  665. * @public
  666. */
  667. this.content = content;
  668. /**
  669. * The alias(es) of the token.
  670. *
  671. * @type {string|string[]}
  672. * @see GrammarToken
  673. * @public
  674. */
  675. this.alias = alias;
  676. // Copy of the full string this token was created from
  677. this.length = (matchedStr || '').length | 0;
  678. }
  679. /**
  680. * A token stream is an array of strings and {@link Token Token} objects.
  681. *
  682. * Token streams have to fulfill a few properties that are assumed by most functions (mostly internal ones) that process
  683. * them.
  684. *
  685. * 1. No adjacent strings.
  686. * 2. No empty strings.
  687. *
  688. * The only exception here is the token stream that only contains the empty string and nothing else.
  689. *
  690. * @typedef {Array<string | Token>} TokenStream
  691. * @global
  692. * @public
  693. */
  694. /**
  695. * Converts the given token or token stream to an HTML representation.
  696. *
  697. * The following hooks will be run:
  698. * 1. `wrap`: On each {@link Token}.
  699. *
  700. * @param {string | Token | TokenStream} o The token or token stream to be converted.
  701. * @param {string} language The name of current language.
  702. * @returns {string} The HTML representation of the token or token stream.
  703. * @memberof Token
  704. * @static
  705. */
  706. Token.stringify = function stringify(o, language) {
  707. if (typeof o == 'string') {
  708. return o;
  709. }
  710. if (Array.isArray(o)) {
  711. var s = '';
  712. o.forEach(function (e) {
  713. s += stringify(e, language);
  714. });
  715. return s;
  716. }
  717. var env = {
  718. type: o.type,
  719. content: stringify(o.content, language),
  720. tag: 'span',
  721. classes: ['token', o.type],
  722. attributes: {},
  723. language: language
  724. };
  725. var aliases = o.alias;
  726. if (aliases) {
  727. if (Array.isArray(aliases)) {
  728. Array.prototype.push.apply(env.classes, aliases);
  729. } else {
  730. env.classes.push(aliases);
  731. }
  732. }
  733. _.hooks.run('wrap', env);
  734. var attributes = '';
  735. for (var name in env.attributes) {
  736. attributes += ' ' + name + '="' + (env.attributes[name] || '').replace(/"/g, '&quot;') + '"';
  737. }
  738. return '<' + env.tag + ' class="' + env.classes.join(' ') + '"' + attributes + '>' + env.content + '</' + env.tag + '>';
  739. };
  740. /**
  741. * @param {string} text
  742. * @param {LinkedList<string | Token>} tokenList
  743. * @param {any} grammar
  744. * @param {LinkedListNode<string | Token>} startNode
  745. * @param {number} startPos
  746. * @param {RematchOptions} [rematch]
  747. * @returns {void}
  748. * @private
  749. *
  750. * @typedef RematchOptions
  751. * @property {string} cause
  752. * @property {number} reach
  753. */
  754. function matchGrammar(text, tokenList, grammar, startNode, startPos, rematch) {
  755. for (var token in grammar) {
  756. if (!grammar.hasOwnProperty(token) || !grammar[token]) {
  757. continue;
  758. }
  759. var patterns = grammar[token];
  760. patterns = Array.isArray(patterns) ? patterns : [patterns];
  761. for (var j = 0; j < patterns.length; ++j) {
  762. if (rematch && rematch.cause == token + ',' + j) {
  763. return;
  764. }
  765. var patternObj = patterns[j],
  766. inside = patternObj.inside,
  767. lookbehind = !!patternObj.lookbehind,
  768. greedy = !!patternObj.greedy,
  769. lookbehindLength = 0,
  770. alias = patternObj.alias;
  771. if (greedy && !patternObj.pattern.global) {
  772. // Without the global flag, lastIndex won't work
  773. var flags = patternObj.pattern.toString().match(/[imsuy]*$/)[0];
  774. patternObj.pattern = RegExp(patternObj.pattern.source, flags + 'g');
  775. }
  776. /** @type {RegExp} */
  777. var pattern = patternObj.pattern || patternObj;
  778. for ( // iterate the token list and keep track of the current token/string position
  779. var currentNode = startNode.next, pos = startPos;
  780. currentNode !== tokenList.tail;
  781. pos += currentNode.value.length, currentNode = currentNode.next
  782. ) {
  783. if (rematch && pos >= rematch.reach) {
  784. break;
  785. }
  786. var str = currentNode.value;
  787. if (tokenList.length > text.length) {
  788. // Something went terribly wrong, ABORT, ABORT!
  789. return;
  790. }
  791. if (str instanceof Token) {
  792. continue;
  793. }
  794. var removeCount = 1; // this is the to parameter of removeBetween
  795. if (greedy && currentNode != tokenList.tail.prev) {
  796. pattern.lastIndex = pos;
  797. var match = pattern.exec(text);
  798. if (!match) {
  799. break;
  800. }
  801. var from = match.index + (lookbehind && match[1] ? match[1].length : 0);
  802. var to = match.index + match[0].length;
  803. var p = pos;
  804. // find the node that contains the match
  805. p += currentNode.value.length;
  806. while (from >= p) {
  807. currentNode = currentNode.next;
  808. p += currentNode.value.length;
  809. }
  810. // adjust pos (and p)
  811. p -= currentNode.value.length;
  812. pos = p;
  813. // the current node is a Token, then the match starts inside another Token, which is invalid
  814. if (currentNode.value instanceof Token) {
  815. continue;
  816. }
  817. // find the last node which is affected by this match
  818. for (
  819. var k = currentNode;
  820. k !== tokenList.tail && (p < to || typeof k.value === 'string');
  821. k = k.next
  822. ) {
  823. removeCount++;
  824. p += k.value.length;
  825. }
  826. removeCount--;
  827. // replace with the new match
  828. str = text.slice(pos, p);
  829. match.index -= pos;
  830. } else {
  831. pattern.lastIndex = 0;
  832. var match = pattern.exec(str);
  833. }
  834. if (!match) {
  835. continue;
  836. }
  837. if (lookbehind) {
  838. lookbehindLength = match[1] ? match[1].length : 0;
  839. }
  840. var from = match.index + lookbehindLength,
  841. matchStr = match[0].slice(lookbehindLength),
  842. to = from + matchStr.length,
  843. before = str.slice(0, from),
  844. after = str.slice(to);
  845. var reach = pos + str.length;
  846. if (rematch && reach > rematch.reach) {
  847. rematch.reach = reach;
  848. }
  849. var removeFrom = currentNode.prev;
  850. if (before) {
  851. removeFrom = addAfter(tokenList, removeFrom, before);
  852. pos += before.length;
  853. }
  854. removeRange(tokenList, removeFrom, removeCount);
  855. var wrapped = new Token(token, inside ? _.tokenize(matchStr, inside) : matchStr, alias, matchStr);
  856. currentNode = addAfter(tokenList, removeFrom, wrapped);
  857. if (after) {
  858. addAfter(tokenList, currentNode, after);
  859. }
  860. if (removeCount > 1) {
  861. // at least one Token object was removed, so we have to do some rematching
  862. // this can only happen if the current pattern is greedy
  863. matchGrammar(text, tokenList, grammar, currentNode.prev, pos, {
  864. cause: token + ',' + j,
  865. reach: reach
  866. });
  867. }
  868. }
  869. }
  870. }
  871. }
  872. /**
  873. * @typedef LinkedListNode
  874. * @property {T} value
  875. * @property {LinkedListNode<T> | null} prev The previous node.
  876. * @property {LinkedListNode<T> | null} next The next node.
  877. * @template T
  878. * @private
  879. */
  880. /**
  881. * @template T
  882. * @private
  883. */
  884. function LinkedList() {
  885. /** @type {LinkedListNode<T>} */
  886. var head = { value: null, prev: null, next: null };
  887. /** @type {LinkedListNode<T>} */
  888. var tail = { value: null, prev: head, next: null };
  889. head.next = tail;
  890. /** @type {LinkedListNode<T>} */
  891. this.head = head;
  892. /** @type {LinkedListNode<T>} */
  893. this.tail = tail;
  894. this.length = 0;
  895. }
  896. /**
  897. * Adds a new node with the given value to the list.
  898. * @param {LinkedList<T>} list
  899. * @param {LinkedListNode<T>} node
  900. * @param {T} value
  901. * @returns {LinkedListNode<T>} The added node.
  902. * @template T
  903. */
  904. function addAfter(list, node, value) {
  905. // assumes that node != list.tail && values.length >= 0
  906. var next = node.next;
  907. var newNode = { value: value, prev: node, next: next };
  908. node.next = newNode;
  909. next.prev = newNode;
  910. list.length++;
  911. return newNode;
  912. }
  913. /**
  914. * Removes `count` nodes after the given node. The given node will not be removed.
  915. * @param {LinkedList<T>} list
  916. * @param {LinkedListNode<T>} node
  917. * @param {number} count
  918. * @template T
  919. */
  920. function removeRange(list, node, count) {
  921. var next = node.next;
  922. for (var i = 0; i < count && next !== list.tail; i++) {
  923. next = next.next;
  924. }
  925. node.next = next;
  926. next.prev = node;
  927. list.length -= i;
  928. }
  929. /**
  930. * @param {LinkedList<T>} list
  931. * @returns {T[]}
  932. * @template T
  933. */
  934. function toArray(list) {
  935. var array = [];
  936. var node = list.head.next;
  937. while (node !== list.tail) {
  938. array.push(node.value);
  939. node = node.next;
  940. }
  941. return array;
  942. }
  943. if (!_self.document) {
  944. if (!_self.addEventListener) {
  945. // in Node.js
  946. return _;
  947. }
  948. if (!_.disableWorkerMessageHandler) {
  949. // In worker
  950. _self.addEventListener('message', function (evt) {
  951. var message = JSON.parse(evt.data),
  952. lang = message.language,
  953. code = message.code,
  954. immediateClose = message.immediateClose;
  955. _self.postMessage(_.highlight(code, _.languages[lang], lang));
  956. if (immediateClose) {
  957. _self.close();
  958. }
  959. }, false);
  960. }
  961. return _;
  962. }
  963. // Get current script and highlight
  964. var script = _.util.currentScript();
  965. if (script) {
  966. _.filename = script.src;
  967. if (script.hasAttribute('data-manual')) {
  968. _.manual = true;
  969. }
  970. }
  971. function highlightAutomaticallyCallback() {
  972. if (!_.manual) {
  973. _.highlightAll();
  974. }
  975. }
  976. if (!_.manual) {
  977. // If the document state is "loading", then we'll use DOMContentLoaded.
  978. // If the document state is "interactive" and the prism.js script is deferred, then we'll also use the
  979. // DOMContentLoaded event because there might be some plugins or languages which have also been deferred and they
  980. // might take longer one animation frame to execute which can create a race condition where only some plugins have
  981. // been loaded when Prism.highlightAll() is executed, depending on how fast resources are loaded.
  982. // See https://github.com/PrismJS/prism/issues/2102
  983. var readyState = document.readyState;
  984. if (readyState === 'loading' || readyState === 'interactive' && script && script.defer) {
  985. document.addEventListener('DOMContentLoaded', highlightAutomaticallyCallback);
  986. } else {
  987. if (window.requestAnimationFrame) {
  988. window.requestAnimationFrame(highlightAutomaticallyCallback);
  989. } else {
  990. window.setTimeout(highlightAutomaticallyCallback, 16);
  991. }
  992. }
  993. }
  994. return _;
  995. })(_self);
  996. if (typeof module !== 'undefined' && module.exports) {
  997. module.exports = Prism;
  998. }
  999. // hack for components to work correctly in node.js
  1000. if (typeof global !== 'undefined') {
  1001. global.Prism = Prism;
  1002. }
  1003. // some additional documentation/types
  1004. /**
  1005. * The expansion of a simple `RegExp` literal to support additional properties.
  1006. *
  1007. * @typedef GrammarToken
  1008. * @property {RegExp} pattern The regular expression of the token.
  1009. * @property {boolean} [lookbehind=false] If `true`, then the first capturing group of `pattern` will (effectively)
  1010. * behave as a lookbehind group meaning that the captured text will not be part of the matched text of the new token.
  1011. * @property {boolean} [greedy=false] Whether the token is greedy.
  1012. * @property {string|string[]} [alias] An optional alias or list of aliases.
  1013. * @property {Grammar} [inside] The nested grammar of this token.
  1014. *
  1015. * The `inside` grammar will be used to tokenize the text value of each token of this kind.
  1016. *
  1017. * This can be used to make nested and even recursive language definitions.
  1018. *
  1019. * Note: This can cause infinite recursion. Be careful when you embed different languages or even the same language into
  1020. * each another.
  1021. * @global
  1022. * @public
  1023. */
  1024. /**
  1025. * @typedef Grammar
  1026. * @type {Object<string, RegExp | GrammarToken | Array<RegExp | GrammarToken>>}
  1027. * @property {Grammar} [rest] An optional grammar object that will be appended to this grammar.
  1028. * @global
  1029. * @public
  1030. */
  1031. /**
  1032. * A function which will invoked after an element was successfully highlighted.
  1033. *
  1034. * @callback HighlightCallback
  1035. * @param {Element} element The element successfully highlighted.
  1036. * @returns {void}
  1037. * @global
  1038. * @public
  1039. */
  1040. /**
  1041. * @callback HookCallback
  1042. * @param {Object<string, any>} env The environment variables of the hook.
  1043. * @returns {void}
  1044. * @global
  1045. * @public
  1046. */