PageRenderTime 52ms CodeModel.GetById 23ms RepoModel.GetById 0ms app.codeStats 1ms

/assets/creole.js

https://bitbucket.org/saibotd/bitbeaker/
JavaScript | 371 lines | 318 code | 26 blank | 27 comment | 44 complexity | 83969bd38c1e7967e58d4402ab3d6c8d MD5 | raw file
  1. /*
  2. * JavaScript Creole 1.0 Wiki Markup Parser
  3. * $Id$
  4. *
  5. * Copyright (c) 2009 Ivan Fomichev
  6. *
  7. * Portions Copyright (c) 2007 Chris Purcell
  8. *
  9. * Permission is hereby granted, free of charge, to any person obtaining a
  10. * copy of this software and associated documentation files (the "Software"),
  11. * to deal in the Software without restriction, including without limitation
  12. * the rights to use, copy, modify, merge, publish, distribute, sublicense,
  13. * and/or sell copies of the Software, and to permit persons to whom the
  14. * Software is furnished to do so, subject to the following conditions:
  15. *
  16. * The above copyright notice and this permission notice shall be included
  17. * in all copies or substantial portions of the Software.
  18. *
  19. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  20. * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  21. * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
  22. * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  23. * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
  24. * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
  25. * DEALINGS IN THE SOFTWARE.
  26. */
  27. if (!Parse) { var Parse = {}; }
  28. if (!Parse.Simple) { Parse.Simple = {}; }
  29. Parse.Simple.Base = function(grammar, options) {
  30. if (!arguments.length) { return; }
  31. this.grammar = grammar;
  32. this.grammar.root = new this.ruleConstructor(this.grammar.root);
  33. this.options = options;
  34. };
  35. Parse.Simple.Base.prototype = {
  36. ruleConstructor: null,
  37. grammar: null,
  38. options: null,
  39. parse: function(node, data, options) {
  40. if (options) {
  41. for (i in this.options) {
  42. if (typeof options[i] == 'undefined') { options[i] = this.options[i]; }
  43. }
  44. }
  45. else {
  46. options = this.options;
  47. }
  48. data = data.replace(/\r\n?/g, '\n');
  49. this.grammar.root.apply(node, data, options);
  50. if (options && options.forIE) { node.innerHTML = node.innerHTML.replace(/\r?\n/g, '\r\n'); }
  51. }
  52. };
  53. Parse.Simple.Base.prototype.constructor = Parse.Simple.Base;
  54. Parse.Simple.Base.Rule = function(params) {
  55. if (!arguments.length) { return; }
  56. for (var p in params) { this[p] = params[p]; }
  57. if (!this.children) { this.children = []; }
  58. };
  59. Parse.Simple.Base.prototype.ruleConstructor = Parse.Simple.Base.Rule;
  60. Parse.Simple.Base.Rule.prototype = {
  61. regex: null,
  62. capture: null,
  63. replaceRegex: null,
  64. replaceString: null,
  65. tag: null,
  66. attrs: null,
  67. children: null,
  68. match: function(data, options) {
  69. return data.match(this.regex);
  70. },
  71. build: function(node, r, options) {
  72. var data;
  73. if (this.capture !== null) {
  74. data = r[this.capture];
  75. }
  76. var target;
  77. if (this.tag) {
  78. target = document.createElement(this.tag);
  79. node.appendChild(target);
  80. }
  81. else { target = node; }
  82. if (data) {
  83. if (this.replaceRegex) {
  84. data = data.replace(this.replaceRegex, this.replaceString);
  85. }
  86. this.apply(target, data, options);
  87. }
  88. if (this.attrs) {
  89. for (var i in this.attrs) {
  90. target.setAttribute(i, this.attrs[i]);
  91. if (options && options.forIE && i == 'class') { target.className = this.attrs[i]; }
  92. }
  93. }
  94. return this;
  95. },
  96. apply: function(node, data, options) {
  97. var tail = '' + data;
  98. var matches = [];
  99. if (!this.fallback.apply) {
  100. this.fallback = new this.constructor(this.fallback);
  101. }
  102. while (true) {
  103. var best = false;
  104. var rule = false;
  105. for (var i = 0; i < this.children.length; i++) {
  106. if (typeof matches[i] == 'undefined') {
  107. if (!this.children[i].match) {
  108. this.children[i] = new this.constructor(this.children[i]);
  109. }
  110. matches[i] = this.children[i].match(tail, options);
  111. }
  112. if (matches[i] && (!best || best.index > matches[i].index)) {
  113. best = matches[i];
  114. rule = this.children[i];
  115. if (best.index == 0) { break; }
  116. }
  117. }
  118. var pos = best ? best.index : tail.length;
  119. if (pos > 0) {
  120. this.fallback.apply(node, tail.substring(0, pos), options);
  121. }
  122. if (!best) { break; }
  123. if (!rule.build) { rule = new this.constructor(rule); }
  124. rule.build(node, best, options);
  125. var chopped = best.index + best[0].length;
  126. tail = tail.substring(chopped);
  127. for (var i = 0; i < this.children.length; i++) {
  128. if (matches[i]) {
  129. if (matches[i].index >= chopped) {
  130. matches[i].index -= chopped;
  131. }
  132. else {
  133. matches[i] = void 0;
  134. }
  135. }
  136. }
  137. }
  138. return this;
  139. },
  140. fallback: {
  141. apply: function(node, data, options) {
  142. if (options && options.forIE) {
  143. // workaround for bad IE
  144. data = data.replace(/\n/g, ' \r');
  145. }
  146. node.appendChild(document.createTextNode(data));
  147. }
  148. }
  149. };
  150. Parse.Simple.Base.Rule.prototype.constructor = Parse.Simple.Base.Rule;
  151. Parse.Simple.Creole = function(options) {
  152. var rx = {};
  153. rx.link = '[^\\]|~\\n]*(?:(?:\\](?!\\])|~.)[^\\]|~\\n]*)*';
  154. rx.linkText = '[^\\]~\\n]*(?:(?:\\](?!\\])|~.)[^\\]~\\n]*)*';
  155. rx.uriPrefix = '\\b(?:(?:https?|ftp)://|mailto:)';
  156. rx.uri = rx.uriPrefix + rx.link;
  157. rx.rawUri = rx.uriPrefix + '\\S*[^\\s!"\',.:;?]';
  158. rx.interwikiPrefix = '[\\w.]+:';
  159. rx.interwikiLink = rx.interwikiPrefix + rx.link;
  160. rx.img = '\\{\\{((?!\\{)[^|}\\n]*(?:}(?!})[^|}\\n]*)*)' +
  161. (options && options.strict ? '' : '(?:') +
  162. '\\|([^}~\\n]*((}(?!})|~.)[^}~\\n]*)*)' +
  163. (options && options.strict ? '' : ')?') +
  164. '}}';
  165. var formatLink = function(link, format) {
  166. if (format instanceof Function) {
  167. return format(link);
  168. }
  169. format = format instanceof Array ? format : [ format ];
  170. if (typeof format[1] == 'undefined') { format[1] = ''; }
  171. return format[0] + link + format[1];
  172. };
  173. var g = {
  174. hr: { tag: 'hr', regex: /(^|\n)\s*----\s*(\n|$)/ },
  175. br: { tag: 'br', regex: /\\\\/ },
  176. preBlock: { tag: 'pre', capture: 2,
  177. regex: /(^|\n)\{\{\{\n((.*\n)*?)\}\}\}(\n|$)/,
  178. replaceRegex: /^ ([ \t]*\}\}\})/gm,
  179. replaceString: '$1' },
  180. tt: { tag: 'tt',
  181. regex: /\{\{\{(.*?\}\}\}+)/, capture: 1,
  182. replaceRegex: /\}\}\}$/, replaceString: '' },
  183. ulist: { tag: 'ul', capture: 0,
  184. regex: /(^|\n)([ \t]*\*[^*#].*(\n|$)([ \t]*[^\s*#].*(\n|$))*([ \t]*[*#]{2}.*(\n|$))*)+/ },
  185. olist: { tag: 'ol', capture: 0,
  186. regex: /(^|\n)([ \t]*#[^*#].*(\n|$)([ \t]*[^\s*#].*(\n|$))*([ \t]*[*#]{2}.*(\n|$))*)+/ },
  187. li: { tag: 'li', capture: 0,
  188. regex: /[ \t]*([*#]).+(\n[ \t]*[^*#\s].*)*(\n[ \t]*\1[*#].+)*/,
  189. replaceRegex: /(^|\n)[ \t]*[*#]/g, replaceString: '$1' },
  190. table: { tag: 'table', capture: 0,
  191. regex: /(^|\n)(\|.*?[ \t]*(\n|$))+/ },
  192. tr: { tag: 'tr', capture: 2, regex: /(^|\n)(\|.*?)\|?[ \t]*(\n|$)/ },
  193. th: { tag: 'th', regex: /\|+=([^|]*)/, capture: 1 },
  194. td: { tag: 'td', capture: 1,
  195. regex: '\\|+([^|~\\[{]*((~(.|(?=\\n)|$)|' +
  196. '\\[\\[' + rx.link + '(\\|' + rx.linkText + ')?\\]\\]' +
  197. (options && options.strict ? '' : '|' + rx.img) +
  198. '|[\\[{])[^|~]*)*)' },
  199. singleLine: { regex: /.+/, capture: 0 },
  200. paragraph: { tag: 'p', capture: 0,
  201. regex: /(^|\n)([ \t]*\S.*(\n|$))+/ },
  202. text: { capture: 0, regex: /(^|\n)([ \t]*[^\s].*(\n|$))+/ },
  203. strong: { tag: 'strong', capture: 1,
  204. regex: /\*\*([^*~]*((\*(?!\*)|~(.|(?=\n)|$))[^*~]*)*)(\*\*|\n|$)/ },
  205. em: { tag: 'em', capture: 1,
  206. regex: '\\/\\/(((?!' + rx.uriPrefix + ')[^\\/~])*' +
  207. '((' + rx.rawUri + '|\\/(?!\\/)|~(.|(?=\\n)|$))' +
  208. '((?!' + rx.uriPrefix + ')[^\\/~])*)*)(\\/\\/|\\n|$)' },
  209. img: { regex: rx.img,
  210. build: function(node, r, options) {
  211. var img = document.createElement('img');
  212. img.src = r[1];
  213. img.alt = r[2] === undefined
  214. ? (options && options.defaultImageText ? options.defaultImageText : '')
  215. : r[2].replace(/~(.)/g, '$1');
  216. node.appendChild(img);
  217. } },
  218. namedUri: { regex: '\\[\\[(' + rx.uri + ')\\|(' + rx.linkText + ')\\]\\]',
  219. build: function(node, r, options) {
  220. var link = document.createElement('a');
  221. link.href = r[1];
  222. if (options && options.isPlainUri) {
  223. link.appendChild(document.createTextNode(r[2]));
  224. }
  225. else {
  226. this.apply(link, r[2], options);
  227. }
  228. node.appendChild(link);
  229. } },
  230. namedLink: { regex: '\\[\\[(' + rx.link + ')\\|(' + rx.linkText + ')\\]\\]',
  231. build: function(node, r, options) {
  232. var link = document.createElement('a');
  233. link.href = options && options.linkFormat
  234. ? formatLink(r[1].replace(/~(.)/g, '$1'), options.linkFormat)
  235. : r[1].replace(/~(.)/g, '$1');
  236. this.apply(link, r[2], options);
  237. node.appendChild(link);
  238. } },
  239. unnamedUri: { regex: '\\[\\[(' + rx.uri + ')\\]\\]',
  240. build: 'dummy' },
  241. unnamedLink: { regex: '\\[\\[(' + rx.link + ')\\]\\]',
  242. build: 'dummy' },
  243. unnamedInterwikiLink: { regex: '\\[\\[(' + rx.interwikiLink + ')\\]\\]',
  244. build: 'dummy' },
  245. rawUri: { regex: '(' + rx.rawUri + ')',
  246. build: 'dummy' },
  247. escapedSequence: { regex: '~(' + rx.rawUri + '|.)', capture: 1,
  248. tag: 'span', attrs: { 'class': 'escaped' } },
  249. escapedSymbol: { regex: /~(.)/, capture: 1,
  250. tag: 'span', attrs: { 'class': 'escaped' } }
  251. };
  252. g.unnamedUri.build = g.rawUri.build = function(node, r, options) {
  253. if (!options) { options = {}; }
  254. options.isPlainUri = true;
  255. g.namedUri.build.call(this, node, Array(r[0], r[1], r[1]), options);
  256. };
  257. g.unnamedLink.build = function(node, r, options) {
  258. g.namedLink.build.call(this, node, Array(r[0], r[1], r[1]), options);
  259. };
  260. g.namedInterwikiLink = { regex: '\\[\\[(' + rx.interwikiLink + ')\\|(' + rx.linkText + ')\\]\\]',
  261. build: function(node, r, options) {
  262. var link = document.createElement('a');
  263. var m, f;
  264. if (options && options.interwiki) {
  265. m = r[1].match(/(.*?):(.*)/);
  266. f = options.interwiki[m[1]];
  267. }
  268. if (typeof f == 'undefined') {
  269. if (!g.namedLink.apply) {
  270. g.namedLink = new this.constructor(g.namedLink);
  271. }
  272. return g.namedLink.build.call(g.namedLink, node, r, options);
  273. }
  274. link.href = formatLink(m[2].replace(/~(.)/g, '$1'), f);
  275. this.apply(link, r[2], options);
  276. node.appendChild(link);
  277. }
  278. };
  279. g.unnamedInterwikiLink.build = function(node, r, options) {
  280. g.namedInterwikiLink.build.call(this, node, Array(r[0], r[1], r[1]), options);
  281. };
  282. g.namedUri.children = g.unnamedUri.children = g.rawUri.children =
  283. g.namedLink.children = g.unnamedLink.children =
  284. g.namedInterwikiLink.children = g.unnamedInterwikiLink.children =
  285. [ g.escapedSymbol, g.img ];
  286. for (var i = 1; i <= 6; i++) {
  287. g['h' + i] = { tag: 'h' + i, capture: 2,
  288. regex: '(^|\\n)[ \\t]*={' + i + '}(?!=)[ \\t]*' +
  289. '([^~]*?(~(.|(?=\\n)|$))*)[ \\t]*=*\\s*(\\n|$)'
  290. };
  291. }
  292. g.ulist.children = g.olist.children = [ g.li ];
  293. g.li.children = [ g.ulist, g.olist ];
  294. g.li.fallback = g.text;
  295. g.table.children = [ g.tr ];
  296. g.tr.children = [ g.th, g.td ];
  297. g.td.children = [ g.singleLine ];
  298. g.th.children = [ g.singleLine ];
  299. g.h1.children = g.h2.children = g.h3.children =
  300. g.h4.children = g.h5.children = g.h6.children =
  301. g.singleLine.children = g.paragraph.children =
  302. g.text.children = g.strong.children = g.em.children =
  303. [ g.escapedSequence, g.strong, g.em, g.br, g.rawUri,
  304. g.namedUri, g.namedInterwikiLink, g.namedLink,
  305. g.unnamedUri, g.unnamedInterwikiLink, g.unnamedLink,
  306. g.tt, g.img ];
  307. g.root = {
  308. children: [ g.h1, g.h2, g.h3, g.h4, g.h5, g.h6,
  309. g.hr, g.ulist, g.olist, g.preBlock, g.table ],
  310. fallback: { children: [ g.paragraph ] }
  311. };
  312. Parse.Simple.Base.call(this, g, options);
  313. };
  314. Parse.Simple.Creole.prototype = new Parse.Simple.Base();
  315. Parse.Simple.Creole.prototype.constructor = Parse.Simple.Creole;