prism-textile.js 7.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276
  1. (function (Prism) {
  2. // We don't allow for pipes inside parentheses
  3. // to not break table pattern |(. foo |). bar |
  4. var modifierRegex = /\([^|()\n]+\)|\[[^\]\n]+\]|\{[^}\n]+\}/.source;
  5. // Opening and closing parentheses which are not a modifier
  6. // This pattern is necessary to prevent exponential backtracking
  7. var parenthesesRegex = /\)|\((?![^|()\n]+\))/.source;
  8. /**
  9. * @param {string} source
  10. * @param {string} [flags]
  11. */
  12. function withModifier(source, flags) {
  13. return RegExp(
  14. source
  15. .replace(/<MOD>/g, function () { return '(?:' + modifierRegex + ')'; })
  16. .replace(/<PAR>/g, function () { return '(?:' + parenthesesRegex + ')'; }),
  17. flags || '');
  18. }
  19. var modifierTokens = {
  20. 'css': {
  21. pattern: /\{[^}]+\}/,
  22. inside: {
  23. rest: Prism.languages.css
  24. }
  25. },
  26. 'class-id': {
  27. pattern: /(\()[^)]+(?=\))/,
  28. lookbehind: true,
  29. alias: 'attr-value'
  30. },
  31. 'lang': {
  32. pattern: /(\[)[^\]]+(?=\])/,
  33. lookbehind: true,
  34. alias: 'attr-value'
  35. },
  36. // Anything else is punctuation (the first pattern is for row/col spans inside tables)
  37. 'punctuation': /[\\\/]\d+|\S/
  38. };
  39. var textile = Prism.languages.textile = Prism.languages.extend('markup', {
  40. 'phrase': {
  41. pattern: /(^|\r|\n)\S[\s\S]*?(?=$|\r?\n\r?\n|\r\r)/,
  42. lookbehind: true,
  43. inside: {
  44. // h1. Header 1
  45. 'block-tag': {
  46. pattern: withModifier(/^[a-z]\w*(?:<MOD>|<PAR>|[<>=])*\./.source),
  47. inside: {
  48. 'modifier': {
  49. pattern: withModifier(/(^[a-z]\w*)(?:<MOD>|<PAR>|[<>=])+(?=\.)/.source),
  50. lookbehind: true,
  51. inside: modifierTokens
  52. },
  53. 'tag': /^[a-z]\w*/,
  54. 'punctuation': /\.$/
  55. }
  56. },
  57. // # List item
  58. // * List item
  59. 'list': {
  60. pattern: withModifier(/^[*#]+<MOD>*\s+.+/.source, 'm'),
  61. inside: {
  62. 'modifier': {
  63. pattern: withModifier(/(^[*#]+)<MOD>+/.source),
  64. lookbehind: true,
  65. inside: modifierTokens
  66. },
  67. 'punctuation': /^[*#]+/
  68. }
  69. },
  70. // | cell | cell | cell |
  71. 'table': {
  72. // Modifiers can be applied to the row: {color:red}.|1|2|3|
  73. // or the cell: |{color:red}.1|2|3|
  74. pattern: withModifier(/^(?:(?:<MOD>|<PAR>|[<>=^~])+\.\s*)?(?:\|(?:(?:<MOD>|<PAR>|[<>=^~_]|[\\/]\d+)+\.)?[^|]*)+\|/.source, 'm'),
  75. inside: {
  76. 'modifier': {
  77. // Modifiers for rows after the first one are
  78. // preceded by a pipe and a line feed
  79. pattern: withModifier(/(^|\|(?:\r?\n|\r)?)(?:<MOD>|<PAR>|[<>=^~_]|[\\/]\d+)+(?=\.)/.source),
  80. lookbehind: true,
  81. inside: modifierTokens
  82. },
  83. 'punctuation': /\||^\./
  84. }
  85. },
  86. 'inline': {
  87. pattern: withModifier(/(^|[^a-zA-Z\d])(\*\*|__|\?\?|[*_%@+\-^~])<MOD>*.+?\2(?![a-zA-Z\d])/.source),
  88. lookbehind: true,
  89. inside: {
  90. // Note: superscripts and subscripts are not handled specifically
  91. // *bold*, **bold**
  92. 'bold': {
  93. pattern: withModifier(/(^(\*\*?)<MOD>*).+?(?=\2)/.source),
  94. lookbehind: true
  95. },
  96. // _italic_, __italic__
  97. 'italic': {
  98. pattern: withModifier(/(^(__?)<MOD>*).+?(?=\2)/.source),
  99. lookbehind: true
  100. },
  101. // ??cite??
  102. 'cite': {
  103. pattern: withModifier(/(^\?\?<MOD>*).+?(?=\?\?)/.source),
  104. lookbehind: true,
  105. alias: 'string'
  106. },
  107. // @code@
  108. 'code': {
  109. pattern: withModifier(/(^@<MOD>*).+?(?=@)/.source),
  110. lookbehind: true,
  111. alias: 'keyword'
  112. },
  113. // +inserted+
  114. 'inserted': {
  115. pattern: withModifier(/(^\+<MOD>*).+?(?=\+)/.source),
  116. lookbehind: true
  117. },
  118. // -deleted-
  119. 'deleted': {
  120. pattern: withModifier(/(^-<MOD>*).+?(?=-)/.source),
  121. lookbehind: true
  122. },
  123. // %span%
  124. 'span': {
  125. pattern: withModifier(/(^%<MOD>*).+?(?=%)/.source),
  126. lookbehind: true
  127. },
  128. 'modifier': {
  129. pattern: withModifier(/(^\*\*|__|\?\?|[*_%@+\-^~])<MOD>+/.source),
  130. lookbehind: true,
  131. inside: modifierTokens
  132. },
  133. 'punctuation': /[*_%?@+\-^~]+/
  134. }
  135. },
  136. // [alias]http://example.com
  137. 'link-ref': {
  138. pattern: /^\[[^\]]+\]\S+$/m,
  139. inside: {
  140. 'string': {
  141. pattern: /(\[)[^\]]+(?=\])/,
  142. lookbehind: true
  143. },
  144. 'url': {
  145. pattern: /(\])\S+$/,
  146. lookbehind: true
  147. },
  148. 'punctuation': /[\[\]]/
  149. }
  150. },
  151. // "text":http://example.com
  152. // "text":link-ref
  153. 'link': {
  154. pattern: withModifier(/"<MOD>*[^"]+":.+?(?=[^\w/]?(?:\s|$))/.source),
  155. inside: {
  156. 'text': {
  157. pattern: withModifier(/(^"<MOD>*)[^"]+(?=")/.source),
  158. lookbehind: true
  159. },
  160. 'modifier': {
  161. pattern: withModifier(/(^")<MOD>+/.source),
  162. lookbehind: true,
  163. inside: modifierTokens
  164. },
  165. 'url': {
  166. pattern: /(:).+/,
  167. lookbehind: true
  168. },
  169. 'punctuation': /[":]/
  170. }
  171. },
  172. // !image.jpg!
  173. // !image.jpg(Title)!:http://example.com
  174. 'image': {
  175. pattern: withModifier(/!(?:<MOD>|<PAR>|[<>=])*[^!\s()]+(?:\([^)]+\))?!(?::.+?(?=[^\w/]?(?:\s|$)))?/.source),
  176. inside: {
  177. 'source': {
  178. pattern: withModifier(/(^!(?:<MOD>|<PAR>|[<>=])*)[^!\s()]+(?:\([^)]+\))?(?=!)/.source),
  179. lookbehind: true,
  180. alias: 'url'
  181. },
  182. 'modifier': {
  183. pattern: withModifier(/(^!)(?:<MOD>|<PAR>|[<>=])+/.source),
  184. lookbehind: true,
  185. inside: modifierTokens
  186. },
  187. 'url': {
  188. pattern: /(:).+/,
  189. lookbehind: true
  190. },
  191. 'punctuation': /[!:]/
  192. }
  193. },
  194. // Footnote[1]
  195. 'footnote': {
  196. pattern: /\b\[\d+\]/,
  197. alias: 'comment',
  198. inside: {
  199. 'punctuation': /\[|\]/
  200. }
  201. },
  202. // CSS(Cascading Style Sheet)
  203. 'acronym': {
  204. pattern: /\b[A-Z\d]+\([^)]+\)/,
  205. inside: {
  206. 'comment': {
  207. pattern: /(\()[^)]+(?=\))/,
  208. lookbehind: true
  209. },
  210. 'punctuation': /[()]/
  211. }
  212. },
  213. // Prism(C)
  214. 'mark': {
  215. pattern: /\b\((?:TM|R|C)\)/,
  216. alias: 'comment',
  217. inside: {
  218. 'punctuation': /[()]/
  219. }
  220. }
  221. }
  222. }
  223. });
  224. var phraseInside = textile['phrase'].inside;
  225. var nestedPatterns = {
  226. 'inline': phraseInside['inline'],
  227. 'link': phraseInside['link'],
  228. 'image': phraseInside['image'],
  229. 'footnote': phraseInside['footnote'],
  230. 'acronym': phraseInside['acronym'],
  231. 'mark': phraseInside['mark']
  232. };
  233. // Only allow alpha-numeric HTML tags, not XML tags
  234. textile.tag.pattern = /<\/?(?!\d)[a-z0-9]+(?:\s+[^\s>\/=]+(?:=(?:("|')(?:\\[\s\S]|(?!\1)[^\\])*\1|[^\s'">=]+))?)*\s*\/?>/i;
  235. // Allow some nesting
  236. var phraseInlineInside = phraseInside['inline'].inside;
  237. phraseInlineInside['bold'].inside = nestedPatterns;
  238. phraseInlineInside['italic'].inside = nestedPatterns;
  239. phraseInlineInside['inserted'].inside = nestedPatterns;
  240. phraseInlineInside['deleted'].inside = nestedPatterns;
  241. phraseInlineInside['span'].inside = nestedPatterns;
  242. // Allow some styles inside table cells
  243. var phraseTableInside = phraseInside['table'].inside;
  244. phraseTableInside['inline'] = nestedPatterns['inline'];
  245. phraseTableInside['link'] = nestedPatterns['link'];
  246. phraseTableInside['image'] = nestedPatterns['image'];
  247. phraseTableInside['footnote'] = nestedPatterns['footnote'];
  248. phraseTableInside['acronym'] = nestedPatterns['acronym'];
  249. phraseTableInside['mark'] = nestedPatterns['mark'];
  250. }(Prism));