PageRenderTime 60ms CodeModel.GetById 18ms RepoModel.GetById 1ms app.codeStats 0ms

/libs/snuownd.js

https://github.com/creesch/reddit-moderator-toolbox
JavaScript | 3835 lines | 3128 code | 204 blank | 503 comment | 416 complexity | 8f54cc06d352d2d5ec12191843ae3072 MD5 | raw file
Possible License(s): Apache-2.0

Large files files are truncated, but you can click here to view the full file

  1. /**
  2. @preserve snuownd.js - javascript port of reddit's "snudown" markdown parser
  3. https://github.com/gamefreak/snuownd
  4. */
  5. /**
  6. * @license Copyright (c) 2009, Natacha Porté
  7. * Copyright (c) 2011, Vicent Marti
  8. * Copyright (c) 2012, Scott McClaugherty
  9. *
  10. * Permission to use, copy, modify, and distribute this software for any
  11. * purpose with or without fee is hereby granted, provided that the above
  12. * copyright notice and this permission notice appear in all copies.
  13. *
  14. * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
  15. * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
  16. * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
  17. * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
  18. * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
  19. * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  20. * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  21. */
  22. // up to date with commit b6baacb79996cec794a20d3abcae51adec5cc3cd
  23. /**
  24. @module SnuOwnd
  25. */
  26. (function(exports){
  27. function _isspace(c) {return c == ' ' || c == '\n';}
  28. function isspace(c) {return /[\x09-\x0d ]/.test(c);}
  29. function isalnum(c) { return /[A-Za-z0-9]/.test(c); }
  30. function isalpha(c) { return /[A-Za-z]/.test(c); }
  31. function ispunct(c) {return /[\x20-\x2f\x3a-\x40\x5b-\x60\x7b-\x7f]/.test(c); }
  32. function urlHexCode(number) {
  33. var hex_str = '0123456789ABCDEF';
  34. return '%'+hex_str[(number&0xf0)>>4]+hex_str[(number&0x0f)>>0];
  35. }
  36. function escapeUTF8Char(char) {
  37. var code = char.charCodeAt(0);
  38. if (code < 0x80) {
  39. return urlHexCode(code);
  40. } else if((code > 0x7f) && (code < 0x0800)) {
  41. var seq = urlHexCode(code >> 6 & 0xff | 0xc0);
  42. seq += urlHexCode(code >> 0 & 0x3f | 0x80);
  43. return seq;
  44. } else {
  45. var seq = urlHexCode(code >> 12 & 0xff | 0xe0);
  46. seq += urlHexCode(code >> 6 & 0x3f | 0x80);
  47. seq += urlHexCode(code >> 0 & 0x3f | 0x80);
  48. return seq;
  49. }
  50. }
  51. function find_block_tag (str) {
  52. var wordList = [
  53. 'p', 'dl', 'div', 'math',
  54. 'table', 'ul', 'del', 'form',
  55. 'blockquote', 'figure', 'ol', 'fieldset',
  56. 'h1', 'h6', 'pre', 'script',
  57. 'h5', 'noscript', 'style', 'iframe',
  58. 'h4', 'ins', 'h3', 'h2'
  59. ];
  60. if (wordList.indexOf(str.toLowerCase()) != -1) {
  61. return str.toLowerCase();
  62. }
  63. return '';
  64. }
  65. function sdhtml_is_tag(tag_data, tagname) {
  66. var i;
  67. var closed = 0;
  68. var tag_size = tag_data.length;
  69. if (tag_size < 3 || tag_data[0] != '<') return HTML_TAG_NONE;
  70. i = 1;
  71. if (tag_data[i] == '/') {
  72. closed = 1;
  73. i++;
  74. }
  75. var tagname_c = 0;
  76. for (; i < tag_size; ++i, ++tagname_c) {
  77. if (tagname_c >= tagname.length) break;
  78. if (tag_data[i] != tagname[tagname_c]) return HTML_TAG_NONE;
  79. }
  80. if (i == tag_size) return HTML_TAG_NONE;
  81. if (isspace(tag_data[i]) || tag_data[i] == '>')
  82. return closed ? HTML_TAG_CLOSE : HTML_TAG_OPEN;
  83. return HTML_TAG_NONE;
  84. }
  85. function unscape_text(out, src) {
  86. var i = 0, org;
  87. while (i < src.s.length) {
  88. org = i;
  89. while (i < src.s.length && src.s[i] != '\\') i++;
  90. if (i > org) out.s += src.s.slice(org, i);
  91. if (i + 1 >= src.s.length) break;
  92. out.s += src.s[i + 1];
  93. i += 2;
  94. }
  95. }
  96. /**
  97. * According to the OWASP rules:
  98. *
  99. * & --> &amp;
  100. * < --> &lt;
  101. * > --> &gt;
  102. * " --> &quot;
  103. * ' --> &#x27; &apos; is not recommended
  104. * / --> &#x2F; forward slash is included as it helps end an HTML entity
  105. *
  106. */
  107. var HTML_ESCAPE_TABLE = [
  108. 7, 7, 7, 7, 7, 7, 7, 7, 7, 0, 0, 7, 7, 0, 7, 7,
  109. 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
  110. 0, 0, 1, 0, 0, 0, 2, 3, 0, 0, 0, 0, 0, 0, 0, 4,
  111. 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 0, 6, 0,
  112. 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  113. 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  114. 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  115. 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  116. 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  117. 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  118. 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  119. 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  120. 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  121. 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  122. 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  123. 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
  124. ];
  125. var HTML_ESCAPES = ["", "&quot;", "&amp;", "&#39;", "&#47;", "&lt;", "&gt;", "" /* throw out control characters */ ];
  126. function escape_html(out, src, secure) {
  127. var i = 0, org, esc = 0;
  128. while (i < src.length) {
  129. org = i;
  130. while (i < src.length && !(esc = HTML_ESCAPE_TABLE[src.charCodeAt(i)]))
  131. i++;
  132. if (i > org) out.s += src.slice(org, i);
  133. /* escaping */
  134. if (i >= src.length) break;
  135. /* The forward slash is only escaped in secure mode */
  136. if (src[i] == '/' && !secure) {
  137. out.s += '/';
  138. } else if (HTML_ESCAPE_TABLE[src.charCodeAt(i)] == 7) {
  139. /* skip control characters */
  140. } else {
  141. out.s += HTML_ESCAPES[esc];
  142. }
  143. i++;
  144. }
  145. }
  146. /*
  147. * The following characters will not be escaped:
  148. *
  149. * -_.+!*'(),%#@?=;:/,+&$ alphanum
  150. *
  151. * Note that this character set is the addition of:
  152. *
  153. * - The characters which are safe to be in an URL
  154. * - The characters which are *not* safe to be in
  155. * an URL because they are RESERVED characters.
  156. *
  157. * We asume (lazily) that any RESERVED char that
  158. * appears inside an URL is actually meant to
  159. * have its native function (i.e. as an URL
  160. * component/separator) and hence needs no escaping.
  161. *
  162. * There are two exceptions: the chacters & (amp)
  163. * and ' (single quote) do not appear in the table.
  164. * They are meant to appear in the URL as components,
  165. * yet they require special HTML-entity escaping
  166. * to generate valid HTML markup.
  167. *
  168. * All other characters will be escaped to %XX.
  169. *
  170. */
  171. var HREF_SAFE = [
  172. 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 2, 2, 0, 2, 2,
  173. 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
  174. 0, 1, 0, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1,
  175. 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1,
  176. 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
  177. 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1,
  178. 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
  179. 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0,
  180. 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  181. 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  182. 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  183. 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  184. 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  185. 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  186. 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  187. 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
  188. ];
  189. function escape_href(out, src) {
  190. var i = 0, org;
  191. while (i < src.length) {
  192. org = i;
  193. while (i < src.length && HREF_SAFE[src.charCodeAt(i)] != 0) i++;
  194. if (i > org) out.s += src.slice(org, i);
  195. /* escaping */
  196. if (i >= src.length) break;
  197. /* throw out control characters */
  198. if (HREF_SAFE[src.charCodeAt(i)] == 2) {
  199. i++;
  200. continue;
  201. }
  202. switch (src[i]) {
  203. /* amp appears all the time in URLs, but needs
  204. * HTML-entity escaping to be inside an href */
  205. case '&':
  206. out.s += '&amp;';
  207. break;
  208. /* the single quote is a valid URL character
  209. * according to the standard; it needs HTML
  210. * entity escaping too */
  211. case '\'':
  212. out.s += '&#x27;';
  213. break;
  214. /* the space can be escaped to %20 or a plus
  215. * sign. we're going with the generic escape
  216. * for now. the plus thing is more commonly seen
  217. * when building GET strings */
  218. /*
  219. //This was disabled
  220. case ' ':
  221. out.s += '+'
  222. break;
  223. //*/
  224. /* every other character goes with a %XX escaping */
  225. default:
  226. out.s += escapeUTF8Char(src[i]);
  227. /*
  228. var cc = src.charCodeAt(i);
  229. hex_str[1] = hex_chars[(cc >> 4) & 0xF];
  230. hex_str[2] = hex_chars[cc & 0xF];
  231. out.s += hex_str.join('');
  232. */
  233. }
  234. i++;
  235. }
  236. }
  237. // function autolink_delim(uint8_t *data, size_t link_end, size_t offset, size_t size)
  238. function autolink_delim(data, link_end) {
  239. var cclose, copen = 0;
  240. var i;
  241. for (i = 0; i < link_end; ++i)
  242. if (data[i] == '<') {
  243. link_end = i;
  244. break;
  245. }
  246. while (link_end > 0) {
  247. if ('?!.,'.indexOf(data[link_end - 1]) != -1) link_end--;
  248. else if (data[link_end - 1] == ';') {
  249. var new_end = link_end - 2;
  250. while (new_end > 0 && isalpha(data[new_end])) new_end--;
  251. if (new_end < link_end - 2 && data[new_end] == '&')
  252. link_end = new_end;
  253. else link_end--;
  254. }
  255. else break;
  256. }
  257. if (link_end == 0) return 0;
  258. cclose = data[link_end - 1];
  259. switch (cclose) {
  260. case '"': copen = '"'; break;
  261. case '\'': copen = '\''; break;
  262. case ')': copen = '('; break;
  263. case ']': copen = '['; break;
  264. case '}': copen = '{'; break;
  265. }
  266. if (copen != 0) {
  267. var closing = 0;
  268. var opening = 0;
  269. var j = 0;
  270. /* Try to close the final punctuation sign in this same line;
  271. * if we managed to close it outside of the URL, that means that it's
  272. * not part of the URL. If it closes inside the URL, that means it
  273. * is part of the URL.
  274. *
  275. * Examples:
  276. *
  277. * foo http://www.pokemon.com/Pikachu_(Electric) bar
  278. * => http://www.pokemon.com/Pikachu_(Electric)
  279. *
  280. * foo (http://www.pokemon.com/Pikachu_(Electric)) bar
  281. * => http://www.pokemon.com/Pikachu_(Electric)
  282. *
  283. * foo http://www.pokemon.com/Pikachu_(Electric)) bar
  284. * => http://www.pokemon.com/Pikachu_(Electric))
  285. *
  286. * (foo http://www.pokemon.com/Pikachu_(Electric)) bar
  287. * => foo http://www.pokemon.com/Pikachu_(Electric)
  288. */
  289. while (j < link_end) {
  290. if (data[j] == copen) opening++;
  291. else if (data[j] == cclose) closing++;
  292. j++;
  293. }
  294. if (closing != opening) link_end--;
  295. }
  296. return link_end;
  297. }
  298. function check_domain(data, allow_short) {
  299. var i, np = 0;
  300. if (!isalnum(data[0])) return 0;
  301. for (i = 1; i < data.length - 1; ++i) {
  302. if (data[i] == '.') np++;
  303. else if (!isalnum(data[i]) && data[i] != '-') break;
  304. }
  305. /* a valid domain needs to have at least a dot.
  306. * that's as far as we get */
  307. if (allow_short) {
  308. /* We don't need a valid domain in the strict sence (with
  309. * at least one dot; so just make sure it's composed of valid
  310. * domain characters and return the length of the valid
  311. * sequence. */
  312. return i;
  313. } else {
  314. return np ? i : 0;
  315. }
  316. }
  317. function sd_autolink_issafe(link) {
  318. var valid_uris = [
  319. "http://", "https://", "ftp://", "mailto://",
  320. "/", "git://", "steam://", "irc://", "news://", "mumble://",
  321. "ssh://", "ircs://", "#"];
  322. var i;
  323. for (i = 0; i < valid_uris.length; ++i) {
  324. var len = valid_uris[i].length;
  325. if (link.length > len &&
  326. link.toLowerCase().indexOf(valid_uris[i]) == 0 &&
  327. /[A-Za-z0-9#\/?]/.test(link[len]))
  328. return 1;
  329. }
  330. return 0;
  331. }
  332. function sd_autolink__url(rewind_p, link, data_, offset, size, flags) {
  333. var data = data_.slice(offset);
  334. var link_end, rewind = 0, domain_len;
  335. if (size < 4 || data_[offset+1] != '/' || data_[offset+2] != '/') return 0;
  336. while (rewind < offset && isalpha(data_[offset-rewind - 1])) rewind++;
  337. if (!sd_autolink_issafe(data_.substr(offset-rewind, size+rewind))) return 0;
  338. link_end = "://".length;
  339. domain_len = check_domain(data.slice(link_end), flags & SD_AUTOLINK_SHORT_DOMAINS);
  340. if (domain_len == 0) return 0;
  341. link_end += domain_len;
  342. while (link_end < size && !isspace(data_[offset+link_end])) link_end++;
  343. link_end = autolink_delim(data, link_end);
  344. if (link_end == 0) return 0;
  345. //TODO
  346. link.s += data_.substr(offset-rewind, link_end+rewind);
  347. rewind_p.p = rewind;
  348. return link_end;
  349. }
  350. function sd_autolink__subreddit(rewind_p, link, data_, offset, size) {
  351. var data = data_.slice(offset);
  352. var link_end;
  353. var allMinus = false;
  354. if (size < 3) return 0;
  355. /* make sure this / is part of /r/ */
  356. if (data.indexOf('/r/') != 0) return 0;
  357. link_end = "/r/".length;
  358. if (data.substr(link_end-1, 4).toLowerCase() == "all-") {
  359. allMinus = true;
  360. }
  361. do {
  362. var start = link_end;
  363. var max_length = 24;
  364. /* special case: /r/reddit.com (the only subreddit with a '.') */
  365. if ( size >= link_end+10 && data.substr(link_end, 10).toLowerCase() == 'reddit.com') {
  366. link_end += 10;
  367. max_length = 10;
  368. } else {
  369. /* If not the special case make sure it starts with (t:)?[A-Za-z0-9] */
  370. /* support autolinking to timereddits, /r/t:when (1 April 2012) */
  371. if ( size > link_end+2 && data.substr(link_end, 2) == 't:')
  372. link_end += 2; /* Jump over the 't:' */
  373. /* the first character of a subreddit name must be a letter or digit */
  374. if (!isalnum(data[link_end]))
  375. return 0;
  376. link_end += 1;
  377. }
  378. /* consume valid characters ([A-Za-z0-9_]) until we run out */
  379. while (link_end < size && (isalnum(data[link_end]) ||
  380. data[link_end] == '_'))
  381. link_end++;
  382. /* valid subreddit names are between 3 and 21 characters, with
  383. * some subreddits having 2-character names. Don't bother with
  384. * autolinking for anything outside this length range.
  385. * (chksrname function in reddit/.../validator.py) */
  386. if ( link_end-start < 2 || link_end-start > max_length )
  387. return 0;
  388. /* If we are linking to a multireddit, continue */
  389. } while ( link_end < size && (data[link_end] == '+' || (allMinus && data[link_end] == '-')) && link_end++ );
  390. if (link_end < size && data[link_end] == '/') {
  391. while (link_end < size && (isalnum(data[link_end]) ||
  392. data[link_end] == '_' ||
  393. data[link_end] == '/' ||
  394. data[link_end] == '-')) {
  395. link_end++;
  396. }
  397. }
  398. /* make the link */
  399. link.s += data.slice(0, link_end);
  400. rewind_p.p = 0;
  401. return link_end;
  402. }
  403. function sd_autolink__username(rewind_p, link, data_, offset, size) {
  404. var data = data_.slice(offset);
  405. var link_end;
  406. if (size < 6) return 0;
  407. /* make sure this / is part of /u/ */
  408. if (data.indexOf('/u/') != 0) return 0;
  409. /* the first letter of a username must... well, be valid, we don't care otherwise */
  410. link_end = "/u/".length;
  411. if (!isalnum(data[link_end]) && data[link_end] != '_' && data[link_end] != '-')
  412. return 0;
  413. link_end += 1;
  414. /* consume valid characters ([A-Za-z0-9_-/]) until we run out */
  415. while (link_end < size && (isalnum(data[link_end]) ||
  416. data[link_end] == '_' ||
  417. data[link_end] == '/' ||
  418. data[link_end] == '-'))
  419. link_end++;
  420. /* make the link */
  421. link.s += data.slice(0, link_end);
  422. rewind_p.p = 0;
  423. return link_end;
  424. }
  425. function sd_autolink__email(rewind_p, link, data_, offset, size, flags) {
  426. var data = data_.slice(offset);
  427. var link_end, rewind;
  428. var nb = 0, np = 0;
  429. for (rewind = 0; rewind < offset; ++rewind) {
  430. var c = data_[offset-rewind - 1];
  431. if (isalnum(c)) continue;
  432. if (".+-_".indexOf(c) != -1) continue;
  433. break;
  434. }
  435. if (rewind == 0) return 0;
  436. for (link_end = 0; link_end < size; ++link_end) {
  437. var c = data_[offset+link_end];
  438. if (isalnum(c)) continue;
  439. if (c == '@') nb++;
  440. else if (c == '.' && link_end < size - 1) np++;
  441. else if (c != '-' && c != '_') break;
  442. }
  443. if (link_end < 2 || nb != 1 || np == 0) return 0;
  444. //TODO
  445. link_end = autolink_delim(data, link_end);
  446. if (link_end == 0) return 0;
  447. // link.s += data_.slice(offset - rewind, link_end + rewind
  448. link.s += data_.substr(offset - rewind, link_end + rewind);
  449. rewind_p.p = rewind;
  450. return link_end;
  451. }
  452. function sd_autolink__www(rewind_p, link, data_, offset, size, flags) {
  453. var data = data_.slice(offset);
  454. var link_end;
  455. if (offset > 0 && !ispunct(data_[offset-1]) && !isspace(data_[offset-1]))
  456. return 0;
  457. // if (size < 4 || memcmp(data, "www.", strlen("www.")) != 0)
  458. if (size < 4 || (data.slice(0,4) != 'www.')) return 0;
  459. link_end = check_domain(data, 0);
  460. if (link_end == 0)
  461. return 0;
  462. while (link_end < size && !isspace(data[link_end])) link_end++;
  463. link_end = autolink_delim(data, link_end);
  464. if (link_end == 0) return 0;
  465. link.s += data.slice(0, link_end);
  466. rewind_p.p = 0;
  467. return link_end;
  468. }
  469. /**
  470. Initialize a Callbacks object.
  471. @constructor
  472. @param {Object.<string, ?function>} callbacks A set of callbacks to use as the methods on this object.
  473. */
  474. function Callbacks(callbacks) {
  475. if (callbacks) {
  476. for (var name in callbacks) {
  477. if (name in this) this[name] = callbacks[name];
  478. }
  479. }
  480. }
  481. Callbacks.prototype = {
  482. /**
  483. Renders a code block.
  484. Syntax highlighting specific to lanugage may be performed here.
  485. @method
  486. @param {Buffer} out The output string buffer to append to.
  487. @param {Buffer} text The input text.
  488. @param {Buffer} language The name of the code langage.
  489. @param {?Object} context A renderer specific context object.
  490. */
  491. blockcode: null,
  492. /**
  493. Renders a blockquote.
  494. @method
  495. @param {Buffer} out The output string buffer to append to.
  496. @param {Buffer} text The input text.
  497. @param {?Object} context A renderer specific context object.
  498. */
  499. blockquote: null,
  500. /**
  501. Renders a block of HTML code.
  502. @method
  503. @param {Buffer} out The output string buffer to append to.
  504. @param {Buffer} text The input text.
  505. @param {?Object} context A renderer specific context object.
  506. */
  507. blockhtml: null,
  508. /**
  509. Renders a header.
  510. @method
  511. @param {Buffer} out The output string buffer to append to.
  512. @param {Buffer} text The input text.
  513. @param {Number} level The header level.
  514. @param {?Object} context A renderer specific context object.
  515. */
  516. header: null,
  517. /**
  518. Renders a horizontal rule.
  519. @method
  520. @param {Buffer} out The output string buffer to append to.
  521. @param {?Object} context A renderer specific context object.
  522. */
  523. hrule: null,
  524. /**
  525. Renders a list.
  526. <p>
  527. This method handles the list wrapper, which in terms of HTML would be &lt;ol&gt; or &lt;ul&gt;.
  528. This method is not responsible for handling list elements, all such processing should
  529. already have occured on text pased to the method . All that it is intended
  530. to do is to wrap the text parameter in anything needed.
  531. </p>
  532. @example
  533. out.s += "&lt;ul&gt;" + text.s + "&lt;/ul&gt;"
  534. @method
  535. @param {Buffer} out The output string buffer to append to.
  536. @param {Buffer} text The input that goes inside the list.
  537. @param {Number} flags A bitfield holding a portion of the render state. The only bit that this should be concerned with is MKD_LIST_ORDERED
  538. @param {?Object} context A renderer specific context object.
  539. */
  540. list: null,
  541. /**
  542. Renders a list.
  543. <p>
  544. Wraps the text in a list element.
  545. </p>
  546. @example
  547. out.s += "&lt;li&gt;" + text.s + "&lt;/li&gt;"
  548. @method
  549. @param {Buffer} out The output string buffer to append to.
  550. @param {Buffer} text The contents of the list element.
  551. @param {Number} flags A bitfield holding a portion of the render state. The only bit that this should be concerned with is MKD_LI_BLOCK.
  552. @param {?Object} context A renderer specific context object.
  553. */
  554. listitem: null,
  555. /**
  556. Renders a paragraph.
  557. @example
  558. out.s += "&lt;p&gt;" + text.s + "&lt;/p&gt;";
  559. @method
  560. @param {Buffer} out The output string buffer to append to.
  561. @param {Buffer} text The input text.
  562. @param {?Object} context A renderer specific context object.
  563. */
  564. paragraph: null,
  565. /**
  566. Renders a table.
  567. @example
  568. out.s += "<table><thead>";
  569. out.s += header.s;
  570. out.s += "</thead><tbody>";
  571. out.s += body.s;
  572. out.s += "</tbody></table>";
  573. @method
  574. @param {Buffer} out The output string buffer to append to.
  575. @param {Buffer} head The table header.
  576. @param {Buffer} body The table body.
  577. @param {?Object} context A renderer specific context object.
  578. */
  579. table: null,
  580. /**
  581. Renders a table row.
  582. @example
  583. out.s += "&lt;tr&gt;" + text.s + "&lt;/tr&gt;";
  584. @method
  585. @param {Buffer} out The output string buffer to append to.
  586. @param {Buffer} text The input text.
  587. @param {?Object} context A renderer specific context object.
  588. */
  589. table_row: null,
  590. /**
  591. Renders a table cell.
  592. @example
  593. out.s += "&lt;td&gt;" + text.s + "&lt;/td&gt;";
  594. @method
  595. @param {Buffer} out The output string buffer to append to.
  596. @param {Buffer} text The input text.
  597. @param {Number} flags A bit filed indicating a portion of the output state. Relevant bits are: MKD_TABLE_HEADER, MKD_TABLE_ALIGN_CENTER. MKD_TABLE_ALIGN_L, and MKD_TABLE_ALIGN_R.
  598. @param {?Object} context A renderer specific context object.
  599. */
  600. table_cell: null,
  601. /**
  602. Renders a link that was autodetected.
  603. @example
  604. out.s += "&lt;a href=\""+ text.s + "\"&gt;" + text.s + "&lt;/a&gt;";
  605. @method
  606. @param {Buffer} out The output string buffer to append to.
  607. @param {Buffer} text The address being linked to.
  608. @param {Number} type Equal to MKDA_NORMAL or MKDA_EMAIL
  609. @param {?Object} context A renderer specific context object.
  610. @returns {Boolean} Whether or not the tag was rendered.
  611. */
  612. autolink: null,
  613. /**
  614. Renders inline code.
  615. @method
  616. @param {Buffer} out The output string buffer to append to.
  617. @param {Buffer} text The text being wrapped.
  618. @param {?Object} context A renderer specific context object.
  619. @returns {Boolean} Whether or not the tag was rendered.
  620. */
  621. codespan: null,
  622. /**
  623. Renders text with double emphasis. Default is equivalent to the HTML &lt;strong&gt; tag.
  624. @method
  625. @param {Buffer} out The output string buffer to append to.
  626. @param {Buffer} text The text being wrapped.
  627. @param {?Object} context A renderer specific context object.
  628. @returns {Boolean} Whether or not the tag was rendered.
  629. */
  630. double_emphasis: null,
  631. /**
  632. Renders text with single emphasis. Default is equivalent to the HTML &lt;em&gt; tag.
  633. @method
  634. @param {Buffer} out The output string buffer to append to.
  635. @param {Buffer} text The text being wrapped.
  636. @param {?Object} context A renderer specific context object.
  637. @returns {Boolean} Whether or not the tag was rendered.
  638. */
  639. emphasis: null,
  640. /**
  641. Renders an image.
  642. @example
  643. out.s = "&lt;img src=\"" + link.s + "\" title=\"" + title.s + "\" alt=\"" + alt.s + "\"/&gt;";"
  644. @method
  645. @param {Buffer} out The output string buffer to append to.
  646. @param {Buffer} link The address of the image.
  647. @param {Buffer} title Title text for the image
  648. @param {Buffer} alt Alt text for the image
  649. @param {?Object} context A renderer specific context object.
  650. @returns {Boolean} Whether or not the tag was rendered.
  651. */
  652. image: null,
  653. /**
  654. Renders line break.
  655. @example
  656. out.s += "&lt;br/&gt;";
  657. @method
  658. @param {Buffer} out The output string buffer to append to.
  659. @param {?Object} context A renderer specific context object.
  660. @returns {Boolean} Whether or not the tag was rendered.
  661. */
  662. linebreak: null,
  663. /**
  664. Renders a link.
  665. @example
  666. out.s = "&lt;a href=\"" + link.s + "\" title=\"" + title.s + "\"&gt;" + content.s + "&lt;/a&gt;";
  667. @method
  668. @param {Buffer} out The output string buffer to append to.
  669. @param {Buffer} link The link address.
  670. @param {Buffer} title Title text for the link.
  671. @param {Buffer} content Link text.
  672. @param {?Object} context A renderer specific context object.
  673. @returns {Boolean} Whether or not the tag was rendered.
  674. */
  675. link: null,
  676. /**
  677. Copies and potentially escapes some HTML.
  678. @method
  679. @param {Buffer} out The output string buffer to append to.
  680. @param {Buffer} text The input text.
  681. @param {?Object} context A renderer specific context object.
  682. @returns {Boolean} Whether or not the tag was rendered.
  683. */
  684. raw_html_tag: null,
  685. /**
  686. Renders text with triple emphasis. Default is equivalent to both the &lt;em&gt; and &lt;strong&gt; HTML tags.
  687. @method
  688. @param {Buffer} out The output string buffer to append to.
  689. @param {Buffer} text The text being wrapped.
  690. @param {?Object} context A renderer specific context object.
  691. @returns {Boolean} Whether or not the tag was rendered.
  692. */
  693. triple_emphasis: null,
  694. /**
  695. Renders text crossd out.
  696. @method
  697. @param {Buffer} out The output string buffer to append to.
  698. @param {Buffer} text The text being wrapped.
  699. @param {?Object} context A renderer specific context object.
  700. @returns {Boolean} Whether or not the tag was rendered.
  701. */
  702. strikethrough: null,
  703. /**
  704. Renders text as superscript.
  705. @method
  706. @param {Buffer} out The output string buffer to append to.
  707. @param {Buffer} text The text being wrapped.
  708. @param {?Object} context A renderer specific context object.
  709. @returns {Boolean} Whether or not the tag was rendered.
  710. */
  711. superscript: null,
  712. /**
  713. Escapes an HTML entity.
  714. @method
  715. @param {Buffer} out The output string buffer to append to.
  716. @param {Buffer} text The text being wrapped.
  717. @param {?Object} context A renderer specific context object.
  718. */
  719. entity: null,
  720. /**
  721. Renders plain text.
  722. @method
  723. @param {Buffer} out The output string buffer to append to.
  724. @param {Buffer} text The text being rendered.
  725. @param {?Object} context A renderer specific context object.
  726. */
  727. normal_text: null,
  728. /**
  729. Creates opening boilerplate for a table of contents.
  730. @method
  731. @param {Buffer} out The output string buffer to append to.
  732. @param {?Object} context A renderer specific context object.
  733. */
  734. doc_header: null,
  735. /**
  736. Creates closing boilerplate for a table of contents.
  737. @method
  738. @param {Buffer} out The output string buffer to append to.
  739. @param {?Object} context A renderer specific context object.
  740. */
  741. doc_footer: null
  742. };
  743. /**
  744. A renderer object
  745. @constructor
  746. @param {Callbacks} callbacks The callbacks object to use for the renderer.
  747. @param {?Callbacks} context Renderer specific context information.
  748. */
  749. function Renderer(callbacks, context) {
  750. this.callbacks = callbacks;
  751. this.context = context;
  752. }
  753. /**
  754. Instantiates a custom Renderer object.
  755. @param {Callbacks} callbacks The callbacks object to use for the renderer.
  756. @param {?Callbacks} context Renderer specific context information.
  757. @returns {Renderer}
  758. */
  759. function createCustomRenderer(callbacks, context) {
  760. return new Renderer(callbacks, context)
  761. }
  762. exports.createCustomRenderer = createCustomRenderer;
  763. function defaultRenderState() {
  764. return {
  765. nofollow: 0,
  766. target: null,
  767. tocData: {
  768. headerCount: 0,
  769. currentLevel: 0,
  770. levelOffset: 0
  771. },
  772. toc_id_prefix: null,
  773. html_element_whitelist: DEFAULT_HTML_ELEMENT_WHITELIST,
  774. html_attr_whitelist: DEFAULT_HTML_ATTR_WHITELIST,
  775. flags: 0,
  776. //(flags != undefined?flags:HTML_SKIP_HTML | HTML_SKIP_IMAGES | HTML_SAFELINK | HTML_ESCAPE | HTML_USE_XHTML),
  777. /* extra callbacks */
  778. // void (*link_attributes)(struct buf *ob, const struct buf *url, void *self);
  779. link_attributes: function link_attributes(out, url, options) {
  780. if (options.nofollow) out.s += ' rel="nofollow"';
  781. if (options.target != null) {
  782. out.s += ' target="' + options.target + '"';
  783. }
  784. }
  785. };
  786. }
  787. exports.defaultRenderState = defaultRenderState;
  788. /**
  789. Produces a renderer object that will match Reddit's output.
  790. @param {?Number=} flags A bitfield containing flags specific to the reddit HTML renderer. Passing undefined, null, or null value will produce reddit exact output.
  791. @returns {Renderer} A renderer object that will match Reddit's output.
  792. */
  793. function getRedditRenderer(flags) {
  794. var state =defaultRenderState();
  795. if (flags == null) {
  796. state.flags = DEFAULT_BODY_FLAGS;
  797. } else {
  798. state.flags = flags;
  799. }
  800. var renderer = new Renderer(getRedditCallbacks() , state);
  801. if (renderer.context.flags & HTML_SKIP_IMAGES)
  802. renderer.callbacks.image = null;
  803. if (renderer.context.flags & HTML_SKIP_LINKS) {
  804. renderer.callbacks.link = null;
  805. renderer.callbacks.autolink = null;
  806. }
  807. if (renderer.context.flags & HTML_SKIP_HTML || renderer.context.flags & HTML_ESCAPE)
  808. renderer.callbacks.blockhtml = null;
  809. return renderer;
  810. }
  811. exports.getRedditRenderer = getRedditRenderer;
  812. /**
  813. Produces a renderer object that will match Reddit's for a table of contents.
  814. @returns {Renderer} A renderer object that will match Reddit's output.
  815. */
  816. function getTocRenderer() {
  817. var state = defaultRenderState();
  818. state.flags = HTML_TOC | HTML_SKIP_HTML;
  819. var renderer = new Renderer(getTocCallbacks(), state);
  820. return renderer;
  821. }
  822. exports.getTocRenderer = getTocRenderer;
  823. /**
  824. Create a Callbacks object with the given callback table.
  825. @param {Object.<string, function>} callbacks A table of callbacks to place int a callbacks object.
  826. @returns {Callbacks} A callbacks object holding the provided callbacks.
  827. */
  828. function createCustomCallbacks(callbacks) {
  829. return new Callbacks(callbacks);
  830. }
  831. exports.createCustomCallbacks = createCustomCallbacks;
  832. /**
  833. Produce a callbacks object that matches Reddit's output.
  834. @returns {Callbacks} A callbacks object that matches Reddit's output.
  835. */
  836. function getRedditCallbacks(){
  837. return new Callbacks({
  838. blockcode: cb_blockcode,
  839. blockquote: cb_blockquote,
  840. blockhtml: cb_blockhtml,
  841. header: cb_header,
  842. hrule: cb_hrule,
  843. list: cb_list,
  844. listitem: cb_listitem,
  845. paragraph: cb_paragraph,
  846. table: cb_table,
  847. table_row: cb_table_row,
  848. table_cell: cb_table_cell,
  849. autolink: cb_autolink,
  850. codespan: cb_codespan,
  851. double_emphasis: cb_double_emphasis,
  852. emphasis: cb_emphasis,
  853. image: cb_image,
  854. linebreak: cb_linebreak,
  855. link: cb_link,
  856. raw_html_tag: cb_raw_html_tag,
  857. triple_emphasis: cb_triple_emphasis,
  858. strikethrough: cb_strikethrough,
  859. superscript: cb_superscript,
  860. entity: null,
  861. normal_text: cb_normal_text,
  862. doc_header: null,
  863. doc_footer: cb_reset_toc
  864. });
  865. }
  866. exports.getRedditCallbacks = getRedditCallbacks;
  867. /**
  868. Produce a callbacks object for rendering a table of contents.
  869. @returns {Callbacks} A callbacks object for rendering a table of contents.
  870. */
  871. function getTocCallbacks() {
  872. return new Callbacks({
  873. blockcode: null,
  874. blockquote: null,
  875. blockhtml: null,
  876. header: cb_toc_header,
  877. hrule: null,
  878. list: null,
  879. listitem: null,
  880. paragraph: null,
  881. table: null,
  882. table_row: null,
  883. table_cell: null,
  884. autolink: null,
  885. codespan: cb_codespan,
  886. double_emphasis: cb_double_emphasis,
  887. emphasis: cb_emphasis,
  888. image: null,
  889. linebreak: null,
  890. link: cb_toc_link,
  891. raw_html_tag: null,
  892. triple_emphasis: cb_triple_emphasis,
  893. strikethrough: cb_strikethrough,
  894. superscript: cb_superscript,
  895. entity: null,
  896. normal_text: null,
  897. doc_header: null,
  898. doc_footer: cb_toc_finalize
  899. });
  900. }
  901. exports.getTocCallbacks = getTocCallbacks;
  902. /* block level callbacks - NULL skips the block */
  903. // void (*blockcode)(struct buf *ob, const struct buf *text, const struct buf *lang, void *opaque);
  904. function cb_blockcode(out, text, lang, options) {
  905. if (out.s.length) out.s += '\n';
  906. if (lang && lang.s.length) {
  907. var i, cls;
  908. out.s += '<pre><code class="';
  909. for (i = 0, cls = 0; i < lang.s.length; ++i, ++cls) {
  910. while (i < lang.s.length && isspace(lang.s[i]))
  911. i++;
  912. if (i < lang.s.length) {
  913. var org = i;
  914. while (i < lang.s.length && !isspace(lang.s[i])) i++;
  915. if (lang.s[org] == '.') org++;
  916. if (cls) out.s += ' ';
  917. escape_html(out, lang.s.slice(org, i), false);
  918. }
  919. }
  920. out.s += '">';
  921. } else
  922. out.s += '<pre><code>';
  923. if (text) escape_html(out, text.s, false);
  924. out.s += '</code></pre>\n';
  925. }
  926. // void (*blockquote)(struct buf *ob, const struct buf *text, void *opaque);
  927. function cb_blockquote(out, text, options) {
  928. if (out.s.length) out.s += '\n';
  929. out.s += '<blockquote>\n';
  930. if (text) out.s += text.s;
  931. out.s += '</blockquote>\n';
  932. }
  933. // void (*blockhtml)(struct buf *ob,const struct buf *text, void *opaque);
  934. function cb_blockhtml(out, text, options) {
  935. var org, sz;
  936. if (!text) return;
  937. sz = text.s.length;
  938. while (sz > 0 && text.s[sz - 1] == '\n') sz--;
  939. org = 0;
  940. while (org < sz && text.s[org] == '\n') org++;
  941. if (org >= sz) return;
  942. if (out.s.length) out.s += '\n';
  943. out.s += text.s.slice(org, sz);
  944. out.s += '\n';
  945. }
  946. // header(Buffer out, Buffer text, int level, void *opaque);
  947. function cb_header(out, text, level, options) {
  948. if (out.s.length) out.s += '\n';
  949. if (options.flags & HTML_TOC) {
  950. out.s += '<h' + (+level) + ' id="';
  951. if (options.toc_id_prefix) out.s += options.toc_id_prefix;
  952. out.s += 'toc_' + (options.tocData.headerCount++) + '">';
  953. } else {
  954. out.s += '<h' + (+level) + '>';
  955. }
  956. if (text) out.s += text.s;
  957. out.s += '</h' + (+level) + '>\n';
  958. }
  959. // void (*hrule)(struct buf *ob, void *opaque);
  960. function cb_hrule(out, options) {
  961. if (out.s.length) out.s += '\n';
  962. out.s += (options.flags & HTML_USE_XHTML) ? '<hr/>\n' : '<hr>\n';
  963. }
  964. // void (*list)(struct buf *ob, const struct buf *text, int flags, void *opaque);
  965. function cb_list(out, text, flags, options) {
  966. if (out.s.length) out.s += '\n';
  967. out.s += (flags&MKD_LIST_ORDERED?'<ol>\n':'<ul>\n');
  968. if (text) out.s += text.s;
  969. out.s += (flags&MKD_LIST_ORDERED?'</ol>\n':'</ul>\n');
  970. }
  971. // void (*listitem)(struct buf *ob, const struct buf *text, int flags, void *opaque);
  972. function cb_listitem(out, text, flags, options) {
  973. out.s += '<li>';
  974. if (text) {
  975. var size = text.s.length;
  976. while (size && text.s[size - 1] == '\n') size--;
  977. out.s += text.s.slice(0, size);
  978. }
  979. out.s += '</li>\n';
  980. }
  981. // void (*paragraph)(struct buf *ob, const struct buf *text, void *opaque);
  982. function cb_paragraph(out, text, options) {
  983. var i = 0;
  984. if (out.s.length) out.s += '\n';
  985. if (!text || !text.s.length) return;
  986. while (i < text.s.length && isspace(text.s[i])) i++;
  987. if (i == text.s.length) return;
  988. out.s += '<p>';
  989. if (options.flags & HTML_HARD_WRAP) {
  990. var org;
  991. while (i < text.s.length) {
  992. org = i;
  993. while (i < text.s.length && text.data[i] != '\n')
  994. i++;
  995. if (i > org) out.s += text.s.slice(org, i);
  996. /*
  997. * do not insert a line break if this newline
  998. * is the last character on the paragraph
  999. */
  1000. if (i >= text.s.length - 1) break;
  1001. cb_linebreak(out, options);
  1002. i++;
  1003. }
  1004. } else {
  1005. out.s += text.s.slice(i);
  1006. }
  1007. out.s += '</p>\n';
  1008. }
  1009. // void (*table)(struct buf *ob, const struct buf *header, const struct buf *body, void *opaque);
  1010. function cb_table(out, header, body, options) {
  1011. if (out.s.length) out.s += '\n';
  1012. out.s += '<table><thead>\n';
  1013. if (header) out.s += header.s;
  1014. out.s += '</thead><tbody>\n';
  1015. if (body) out.s += body.s;
  1016. out.s += '</tbody></table>\n';
  1017. }
  1018. // void (*table_row)(struct buf *ob, const struct buf *text, void *opaque);
  1019. function cb_table_row(out, text, options) {
  1020. out.s += '<tr>\n';
  1021. if (text) out.s += text.s;
  1022. out.s += '</tr>\n';
  1023. }
  1024. // void (*table_cell)(struct buf *ob, const struct buf *text, int flags, void *opaque);
  1025. function cb_table_cell(out, text, flags, options) {
  1026. if (flags & MKD_TABLE_HEADER) {
  1027. out.s += '<th';
  1028. } else {
  1029. out.s += '<td';
  1030. }
  1031. switch (flags & MKD_TABLE_ALIGNMASK) {
  1032. case MKD_TABLE_ALIGN_CENTER:
  1033. out.s += ' align="center">';
  1034. break;
  1035. case MKD_TABLE_ALIGN_L:
  1036. out.s += ' align="left">';
  1037. break;
  1038. case MKD_TABLE_ALIGN_R:
  1039. out.s += ' align="right">';
  1040. break;
  1041. default:
  1042. out.s += '>';
  1043. }
  1044. if (text) out.s += text.s;
  1045. if (flags & MKD_TABLE_HEADER) {
  1046. out.s += '</th>\n';
  1047. } else {
  1048. out.s += '</td>\n';
  1049. }
  1050. }
  1051. /* span level callbacks - NULL or return 0 prints the span verbatim */
  1052. // int (*autolink)(struct buf *ob, const struct buf *link, enum mkd_autolink type, void *opaque);
  1053. function cb_autolink(out, link, type, options) {
  1054. var offset = 0;
  1055. if (!link || !link.s.length) return 0;
  1056. if ((options.flags & HTML_SAFELINK) != 0 &&
  1057. !sd_autolink_issafe(link.s) && type != MKDA_EMAIL)
  1058. return 0;
  1059. out.s += '<a href="';
  1060. if (type == MKDA_EMAIL) out.s += 'mailto:';
  1061. escape_href(out, link.s.slice(offset));
  1062. if (options.link_attributes) {
  1063. out.s += '"';
  1064. options.link_attributes(out, link, options);
  1065. out.s += '>';
  1066. } else {
  1067. out.s += '">';
  1068. }
  1069. /*
  1070. * Pretty printing: if we get an email address as
  1071. * an actual URI, e.g. `mailto:foo@bar.com`, we don't
  1072. * want to print the `mailto:` prefix
  1073. */
  1074. if (link.s.indexOf('mailto:')==0) {
  1075. escape_html(out, link.s.slice(7), false);
  1076. } else {
  1077. escape_html(out, link.s, false);
  1078. }
  1079. out.s += '</a>';
  1080. return 1;
  1081. }
  1082. // int (*codespan)(struct buf *ob, const struct buf *text, void *opaque);
  1083. function cb_codespan(out, text, options) {
  1084. out.s += '<code>';
  1085. if (text) escape_html(out, text.s, false);
  1086. out.s += '</code>';
  1087. return 1;
  1088. }
  1089. // int (*double_emphasis)(struct buf *ob, const struct buf *text, void *opaque);
  1090. function cb_double_emphasis(out, text, options) {
  1091. if (!text || !text.s.length) return 0;
  1092. out.s += '<strong>' + text.s + '</strong>';
  1093. return 1;
  1094. }
  1095. // int (*emphasis)(struct buf *ob, const struct buf *text, void *opaque);
  1096. function cb_emphasis(out, text, options) {
  1097. if (!text || !text.s.length) return 0;
  1098. out.s += '<em>' + text.s + '</em>';
  1099. return 1;
  1100. }
  1101. // int (*image)(struct buf *ob, const struct buf *link, const struct buf *title, const struct buf *alt, void *opaque);
  1102. function cb_image(out, link, title, alt, options) {
  1103. if (!link || !link.s.length) return 0;
  1104. out.s += '<img src="';
  1105. escape_href(out, link.s);
  1106. out.s += '" alt="';
  1107. if (alt && alt.s.length) escape_html(out, alt.s, false);
  1108. if (title && title.s.length) {
  1109. out.s += '" title="';
  1110. escape_html(out, title.s, false);
  1111. }
  1112. out.s += (options.flags&HTML_USE_XHTML?'"/>':'">');
  1113. return 1;
  1114. }
  1115. // int (*linebreak)(struct buf *ob, void *opaque);
  1116. function cb_linebreak(out, options) {
  1117. out.s += (options.flags&HTML_USE_XHTML?'<br/>\n':'<br>\n');
  1118. return 1;
  1119. }
  1120. // int (*link)(struct buf *ob, const struct buf *link, const struct buf *title, const struct buf *content, void *opaque);
  1121. function cb_link(out, link, title, content, options) {
  1122. if (link != null && (options.flags & HTML_SAFELINK) != 0 && !sd_autolink_issafe(link.s)) return 0;
  1123. out.s += '<a href="';
  1124. if (link && link.s.length) escape_href(out, link.s);
  1125. if (title && title.s.length) {
  1126. out.s += '" title="';
  1127. escape_html(out, title.s, false);
  1128. }
  1129. if (options.link_attributes) {
  1130. out.s += '"';
  1131. options.link_attributes(out, link, options);
  1132. out.s += '>';
  1133. } else {
  1134. out.s += '">';
  1135. }
  1136. if (content && content.s.length) out.s += content.s;
  1137. out.s += '</a>';
  1138. return 1;
  1139. }
  1140. // rndr_html_tag(struct buf *ob, const struct buf *text, void *opaque, char* tagname, char** whitelist, int tagtype)
  1141. //NOT A CALLBACK!
  1142. // rndr_html_tag(struct buf *ob, const struct buf *text, void *opaque, char* tagname, char** whitelist, int tagtype)
  1143. //NOT A CALLBACK!
  1144. function rndr_html_tag(out, text, options, tagname, whitelist, tagtype) {
  1145. var i, x, z, in_str = 0, seen_equals = 0, done = 0, done_attr = 0, reset = 0;
  1146. var attr;
  1147. var value;
  1148. var c;
  1149. out.s += '<';
  1150. if(tagtype == HTML_TAG_CLOSE) {
  1151. out.s += '/';
  1152. out.s += tagname;
  1153. out.s += '>';
  1154. return;
  1155. }
  1156. out.s += tagname;
  1157. i = 1 + tagname.length;
  1158. attr = new Buffer();
  1159. value = new Buffer();
  1160. var self_closed = false;
  1161. for(; i < text.s.length && !done; i++) {
  1162. c = text.s[i];
  1163. done = 0;
  1164. reset = 0;
  1165. done_attr = 0;
  1166. switch(c) {
  1167. case '/':
  1168. if(!seen_equals) {
  1169. self_closed = 1;
  1170. } else if(in_str) {
  1171. value.s += '/';
  1172. } else {
  1173. reset = 1;
  1174. }
  1175. break;
  1176. case '>':
  1177. done = 1;
  1178. break;
  1179. case '\'':
  1180. case '"':
  1181. self_closed = 0;
  1182. if(!seen_equals) {
  1183. reset = 1;
  1184. } else if(!in_str) {
  1185. in_str = c;
  1186. } else if(in_str == c) {
  1187. in_str = 0;
  1188. done_attr = 1;
  1189. } else {
  1190. value.s += c;
  1191. }
  1192. break;
  1193. case ' ':
  1194. if (in_str) {
  1195. value.s += ' ';
  1196. } else {
  1197. reset = 1;
  1198. }
  1199. break;
  1200. case '=':
  1201. self_closed = 0;
  1202. if(seen_equals) {
  1203. reset = 1;
  1204. break;
  1205. }
  1206. seen_equals = 1;
  1207. break;
  1208. default:
  1209. self_closed = 0;
  1210. if(seen_equals && in_str || !seen_equals) {
  1211. if(seen_equals)
  1212. value.s += c;
  1213. else
  1214. attr.s += c;
  1215. }
  1216. break;
  1217. }
  1218. if(done_attr) {
  1219. var valid = 0;
  1220. for(z = 0; z < whitelist.length; z++) {
  1221. if(whitelist[z].length != attr.s.length) {
  1222. continue;
  1223. }
  1224. for(x = 0; x < attr.s.length; x++) {
  1225. if(whitelist[z][x].toLowerCase() != attr.s[x].toLowerCase()) {
  1226. break;
  1227. }
  1228. }
  1229. if(x == attr.s.length) {
  1230. valid = 1;
  1231. break;
  1232. }
  1233. }
  1234. if(valid && value.s.length && attr.s.length) {
  1235. out.s += ' ';
  1236. escape_html(out, attr.s, true);
  1237. out.s += "=\"";
  1238. escape_html(out, value.s, true);
  1239. out.s += '"';
  1240. }
  1241. reset = 1;
  1242. }
  1243. if(reset) {
  1244. seen_equals = 0;
  1245. in_str = 0;
  1246. attr.s = '';
  1247. value.s = '';
  1248. }
  1249. }
  1250. // bufrelease(attr);
  1251. if(self_closed)
  1252. out.s += ' /';
  1253. out.s += '>';
  1254. }
  1255. // int (*raw_html_tag)(struct buf *ob, const struct buf *tag, void *opaque);
  1256. function cb_raw_html_tag(out, text, options) {
  1257. var whitelist = options.html_element_whitelist;
  1258. /* Items on the whitelist ignore all other flags and just output */
  1259. if (((options.flags & HTML_ALLOW_ELEMENT_WHITELIST) != 0) && whitelist) {
  1260. for(var i = 0; whitelist[i]; i++) {
  1261. var tagtype = sd

Large files files are truncated, but you can click here to view the full file