/extras/t2tformatterplugin.tiddly

http://txt2tags.googlecode.com/ · Unknown · 952 lines · 888 code · 64 blank · 0 comment · 0 complexity · b9a2656348a8e54e7ea5cd5796347f74 MD5 · raw file

  1. /***
  2. |''Name:''|T2tFormatterPlugin|
  3. |''Description:''|Allows Tiddlers to use [[txt2tags|http://txt2tags.org/markup.html]] formatting|
  4. |''Author:''|David Young (david (dot) a (dot) young (at) gmail (dot) com)|
  5. |''Credit:''|Copied and modified from Martin Budden's http://www.martinswiki.com/#ExampleFormatterPlugin |
  6. |''CodeRepository:''|NA |
  7. |''Version:''|0.4|
  8. |''Status:''|Rough Draft|
  9. |''Date:''|Jul 5, 2011|
  10. |''Comments:''|Please make comments at http://groups.google.co.uk/group/TiddlyWikiDev |
  11. |''License:''|NA |
  12. |''~CoreVersion:''|2.6.1|
  13. ! Installation Instructions
  14. Apply the following tags to this tiddler:
  15. * systemConfig
  16. * excludeLists
  17. * excludeSearch
  18. You can make a tiddler with the tag ''t2t'' and the Txt2Tags markup will be used instead of TiddlyWiki markup. You can eliminate the need to manually tag by modifying your ''Create New Tiddler'' macro in your [[MainMenu]] to set the //wikiformat// field to ''t2t'':
  19. {{{
  20. <<newTiddler
  21. label: "New t2t"
  22. title: "New t2t tiddler"
  23. fields: "wikiformat:t2t"
  24. prompt: "Create a new tiddler using the t2t formatter">>
  25. }}}
  26. You should also get into [[AdvancedOptions]] or //backstage -> Tweaks -> chkInsertTabs// because that lets you hit the tab button in the tiddly text field. Tab will still move your cursor around the page, from title input to tiddly text input, etc. It only effects the text field. Vital for t2t blockquote syntax (prepending with tabs).
  27. !Features/ Progress
  28. | ''Markup'' | ''Feature'' | ''Status'' | ''Version'' | ''Comment'' |
  29. | several | \n vs. \r in input | ''WAITING'' | NA |Applied to tagged and raw, but I am uncertain if this is OK.<br>Emailed twdev mailing list. |
  30. | ''FONT/INLINE'' |||||
  31. |{{{**}}}''bold''{{{**}}} | All | ''OK'' | 0.1 |Hasn't been heavily tested on any platform. |
  32. |{{{//}}}//italic//{{{//}}} | All | ''OK'' | 0.1 |Hasn't been heavily tested on any platform. |
  33. |{{{__}}}__under__{{{__}}} | All | ''OK'' | 0.1 |Hasn't been heavily tested on any platform. |
  34. |{{{--}}}--strike--{{{--}}} | All | ''OK'' | 0.1 |Hasn't been heavily tested on any platform.|
  35. |{{{^^}}}^^super^^{{{^^}}} | All | ''OK'' | 0.1 |Hasn't been heavily tested on any platform.<br>Not Official Markup.|
  36. |{{{,,}}}~~sub~~{{{,,}}} | All | ''OK'' | 0.1 |Hasn't been heavily tested on any platform.<br>Not Official Markup.|
  37. |{{{``monospace``}}} | All | ''OK'' | 0.1 |Hasn't been heavily tested on any platform.|
  38. |{{{''}}}tagged/html{{{''}}} | All | ''OK'' | 0.1 |Hasn't been heavily tested on any platform.|
  39. |{{{""}}}raw{{{""}}} | All | ''OK'' | 0.1 |Hasn't been heavily tested on any platform.<br>Overrides WikiWords and Markup.|
  40. | ''SINGLE-LINE'' |||||
  41. |{{{``` monospace}}} | All | ''OK'' | 0.1 |Hasn't been heavily tested on any platform. |
  42. |{{{'''}}} tagged/html | All | ''OK'' | 0.1 |Hasn't been heavily tested on any platform. |
  43. |{{{"""}}} raw | All | ''OK'' | 0.1 |Hasn't been heavily tested on any platform. |
  44. |{{{%}}}comments | ALL | ''OK'' | 0.1 |Hasn't been heavily tested on any platform. |
  45. | ''BLOCKS'' |||||
  46. |{{{```}}}<br>{{{monospace}}}<br>{{{```}}} | All | ''OK'' | 0.1 |Hasn't been heavily tested on any platform. |
  47. |{{{'''}}}<br>tagged/html<br>{{{'''}}} | All | ''OK'' | 0.1 |Hasn't been heavily tested on any platform. |
  48. |{{{"""}}}<br>raw<br>{{{"""}}} | All | ''OK'' | 0.1 |Hasn't been heavily tested on any platform. |
  49. |{{{%%%}}}<br>comments<br>{{{%%%}}} | All | ''OK'' | 0.1 |Hasn't been heavily tested on any platform |
  50. |\t<html>&nbsp;&nbsp;&nbsp;&nbsp;</html>block<br>\t<html>&nbsp;&nbsp;&nbsp;&nbsp;</html>quotes | All | ''OK'' | 0.1 |Hasn't been heavily tested on any platform.<br>Tiddlywiki interprets all markup inside the block; unlike txt2tags (only some markup). |
  51. | ''HEADERS'' |||||
  52. |{{{=}}}un-numbered{{{=}}} | ALL | ''OK'' | 0.1 |Hasn't been heavily tested on any platform. |
  53. |{{{+}}}numbered{{{+}}} | HEADING | ''OK'' | 0.1 |Hasn't been heavily tested on any platform. |
  54. |{{{+}}}numbered{{{+}}} | NUMBERING | ''OK'' | 0.1 |Hasn't been heavily tested on any platform.<br>Unknown if array length modification or array[6] reassignment is faster on a per-iteration basis. |
  55. | ''MISC'' |||||
  56. |horizontal rule<br>{{{--------------------}}} | RULE | ''OK'' | 0.1 |Hasn't been heavily tested on any platform. |
  57. |{{{====================}}}<br>{{{____________________}}}| THICKNESS | ''??'' | 0.1 |The alternative rules are interpreted and classes //heavy// or //light// are assigned; but my theme doesn't use them so I can't tell.<br>Maybe tiddlywiki doesn't use those css classes at all?<br>If so, delete this functionality. |
  58. |Paragraphs | ALL | ''CAN'T FIX'' | 0.1 |Multiple newlines are concatenated into {{{<p><\p>}}}.<br>Visually they appear correctly (line spacing, etc), but it isn't a real paragraph.<br>If you try to make {{{<p>}}} elements wrap the text, then they are nested improperly if you look at the html.<br>This nesting lead to a stack busting on tiddlers if you have around 200 paragraphs or more.<br>This resulted in content truncation, so {{{<p><\p>}}} it shall be.<br>Historical bug in tiddlywiki's core, so I can't do much about it.<br>This is certainly a kludge, maybe {{{<br><br>}}} would be better, but that seems equally kludgy and all block-type syntax must be careful. |
  59. | ''LISTS'' |||||
  60. |{{{- }}}unordered | ALL | ''OK'' | 0.1 |t2t allows complex lists (paragraphs inside list items).<br>Can't figure out a good regex to match "X blank lines" that works with paragraph.<br>Interim solution: terminate list with empty item. |
  61. |{{{+ }}}ordered | ALL | ''OK'' | 0.1 |t2t allows complex lists (paragraphs inside list items).<br>Can't figure out a good regex to match "X blank lines" that works with paragraph.<br>Interim solution: terminate list with empty item.<br>INTERFERES WITH NUMBERED HEADINGS. |
  62. |{{{: }}}word<br><html>&nbsp;&nbsp;&nbsp;&nbsp;</html>definitions | ALL | ''TIDDLY'' | NA |Difficult to pull off since "definition" part of t2t uses whitespaces.<br>Interim solution: use tiddlywiki ; word \n : definition format. |
  63. | ''COMPLEX'' |||||
  64. |~WikiWords | ALL | ''TIDDLY'' | 0.1 |Self-updating link to core formatter.<br>Not Official Markup. |
  65. |{{{<<macros>>}}} | ALL | ''TIDDLY'' | 0.1 |Self-updating link to core formatter.<br>By changing the header syntax, the {{{<<tiddler##section>>}}} section transclusion macro won't work.<br>Not Official Markup. |
  66. |http url recognition | ALL | ''TIDDLY'' | 0.1 |Self-updating link to core formatter.<br>Not Official Markup. |
  67. |email recognition | ALL | ''NA'' | NA |Is this at all worth it? t2t will mask or convert to mailto...<br>don't take this lightly- it wrecked the works last time. |
  68. |{{{[}}}links{{{]}}} | ALL | ''OK'' | 0.1 |Links to internal tiddlers (as long as their title has no spaces), links to external websites |
  69. |{{{[}}}named links{{{]}}} | ALL | ''OK'' | 0.1 |Links to internal tiddlers (as long as their title has no spaces), links to external websites |
  70. |'image-related' | ALL | ''OK'' | 0.1 |an image or image link supports whitespace-alignment.<br>If the {{{[}}} starts at the newline, the image will float to the left (equivalent to the {{{<}}} markup in tiddly images).<br>If the {{{]}}} ends the line, the image will float to the right (equivalent to the {{{>}}} markup in tiddly images).<br>If the image is both the start and end of line, it is neither left nor right.<br>No centering image support yet. |
  71. |{{{[}}}images{{{]}}} | ALL | ''OK'' | 0.1 |Supports .png .gif .jpg .jpeg, just grep for IMAGE-EXTENSION-REGEX in this tiddler to expand the list.<br>Works for local and external image files |
  72. |{{{[[image] links]}}} | ALL | ''OK'' | 0.1 |A twist here is that the link can have spaces in the name, but I don't recommend trying that. |
  73. |{{{|}}}tables{{{|}}} | ALL | ''TIDDLY'' | 0.1 |Tiddlywiki tables are supported. Self-updating link to core formatter. |
  74. ***/
  75. //{{{
  76. //REGEXES found in txt2tags.py:1920 (getRegexes)
  77. // Ensure that the plugin is only installed once.
  78. if(!version.extensions.T2tFormatterPlugin) {
  79. version.extensions.T2tFormatterPlugin = {installed:true};
  80. //Should test this backwards until I find the revision that is actually useless
  81. if(version.major < 2 || (version.major == 2 && version.minor < 6))
  82. {alertAndThrow('T2tFormatterPlugin requires TiddlyWiki 2.6 or later.');}
  83. t2tFormatter = {}; // 'namespace' for local functions
  84. t2tDebug = function(out,str)
  85. {
  86. createTiddlyText(out,str.replace(/\n/mg,'\\n').replace(/\r/mg,'RR'));
  87. createTiddlyElement(out,'br');
  88. };
  89. /*
  90. wikify = function(source,output,highlightRegExp,tiddler)
  91. {
  92. if(source && source !== '') {
  93. var w = new Wikifier(source,getParser(tiddler),highlightRegExp,tiddler);
  94. w.output = tiddler ? createTiddlyElement(output,'p') : output;
  95. w.subWikifyUnterm(w.output);
  96. }
  97. };
  98. */
  99. // COPY from EnclosedTextHelper Tiddlywiki v2.6.1 line 2923
  100. // If the lookaheadRegexp fails, use the string in "restore:"
  101. // define the element "restore:" as the static string equal to your
  102. // "match:"'s result.
  103. //
  104. // If your markup rules are more complex/less dynamic (like ==headers==);
  105. // then you must copy this and make up your own combo of parenthesis.
  106. config.formatterHelpers.nonDestructiveEnclosedTextHelper = function(w)
  107. {
  108. this.lookaheadRegExp.lastIndex = w.matchStart;
  109. var lookaheadMatch = this.lookaheadRegExp.exec(w.source);
  110. if(lookaheadMatch && lookaheadMatch.index == w.matchStart) {
  111. //Everything that uses this function is a one-liner syntax
  112. /*
  113. var text = lookaheadMatch[1];
  114. if(config.browser.isIE)
  115. text = text.replace(/\n/g,"\r");
  116. createTiddlyElement(w.output,this.element,null,null,text);
  117. */
  118. //createTiddlyElement(w.output,this.element,null,null,lookaheadMatch[1]);
  119. w.subWikifyTerm(createTiddlyElement(w.output,this.element),this.termRegExp);
  120. w.nextMatch = lookaheadMatch.index + lookaheadMatch[0].length;
  121. } else {
  122. w.output.appendChild(document.createTextNode(w.matchText));
  123. }
  124. };
  125. /*
  126. // Like nonDestructiveEnclosedTextHelper, only you can define what is
  127. // restored with a regular expression instead of a static string.
  128. // This is helpful for when the "match" is more dynamic. Just define
  129. // element "restoreRegExp:" and the first parenthesis in it will be used
  130. // to restore the consumed "match" text.
  131. // NOTE: this comes at the cost of ANOTHER grepping of your input!
  132. config.formatterHelpers.nonDestructiveEnclosedTextHelperRegEx = function(w)
  133. {
  134. this.lookaheadRegExp.lastIndex = w.matchStart;
  135. var lookaheadMatch = this.lookaheadRegExp.exec(w.source);
  136. if(lookaheadMatch && lookaheadMatch.index == w.matchStart) {
  137. var text = lookaheadMatch[1];
  138. if(config.browser.isIE)
  139. text = text.replace(/\n/g,"\r");
  140. createTiddlyElement(w.output,this.element,null,null,text);
  141. w.nextMatch = lookaheadMatch.index + lookaheadMatch[0].length;
  142. } else {
  143. this.restoreRegExp.lastIndex = w.matchStart;
  144. var restoreMatch = this.restoreRegExp.exec(w.source);
  145. if (restoreMatch && restoreMatch.index == w.matchStart) {
  146. w.output.appendChild(document.createTextNode(restoreMatch[1]));
  147. } else {
  148. //shouldn't be possible!
  149. //Only if you don't define restoreRegExp: properly
  150. w.output.appendChild(document.createTextNode("### SYNTAX ERROR ###"));
  151. }
  152. }
  153. };
  154. */
  155. /*
  156. config.formatterHelpers.setAttributesFromParams = function(e,p)
  157. {
  158. var re = /\s*(.*?)=(?:(?:"(.*?)")|(?:'(.*?)')|((?:\w|%|#)*))/mg;
  159. var match = re.exec(p);
  160. while(match) {
  161. var s = match[1].unDash();
  162. if(s=='bgcolor') {
  163. s = 'backgroundColor';
  164. }
  165. try {
  166. if(match[2]) {
  167. e.setAttribute(s,match[2]);
  168. } else if(match[3]) {
  169. e.setAttribute(s,match[3]);
  170. } else {
  171. e.setAttribute(s,match[4]);
  172. }
  173. }
  174. catch(ex) {}
  175. match = re.exec(p);
  176. }
  177. };
  178. */
  179. config.t2tFormatters = [
  180. //SPEED: COMBINE ALL BEAUTIFIERS LIKE TIDDLY DOES ALREADY
  181. // bold uses syntax **bold** no whitespace in the bookends.
  182. {
  183. name: 't2tBold',
  184. match: '\\*\\*',
  185. element: 'strong',
  186. lookaheadRegExp: /\*\*([^\s](|.*?[^\s])\**)\*\*/mg,
  187. termRegExp: /(\*\*)/mg,
  188. handler: config.formatterHelpers.nonDestructiveEnclosedTextHelper
  189. },
  190. // italic uses //italic//
  191. {
  192. name: 't2tItalic',
  193. match: '//',
  194. element: 'em',
  195. lookaheadRegExp: /\/\/([^\s](|.*?[^\s])[\/]*)(\/\/)/mg,
  196. termRegExp: /(\/\/)/mg,
  197. handler: config.formatterHelpers.nonDestructiveEnclosedTextHelper
  198. },
  199. // Horizontal rules can be made up of 20 or more - characters on a line with
  200. // whitespace on the ends. Alternatively 20 or more _ characters is accepted.
  201. // 20 or more = characters also work, but that makes a "thicker" horizontal rule
  202. // see t2tHeavyRule.
  203. {
  204. name: 't2tRule',
  205. match: '^[ \\t]*[-_]{20,}[ \\t]*$',
  206. handler: function(w)
  207. {
  208. createTiddlyElement(w.output,'hr',null,'light');
  209. }
  210. },
  211. // Default tiddler theme doesn't differentiate between heavy and light.
  212. // TODO: Try some other themes otherwise roll this into t2tRule.
  213. {
  214. name: 't2tHeavyRule',
  215. match: '^[ \\t]*={20,}[ \\t]*$',
  216. handler: function(w)
  217. {
  218. createTiddlyElement(w.output,'hr',null,'heavy');
  219. }
  220. },
  221. // underline uses __underline__
  222. {
  223. name: 't2tUnderline',
  224. match: '__',
  225. element: 'ins',
  226. lookaheadRegExp: /__([^\s](|.*?[^\s])_*)__/mg,
  227. termRegExp: /(__)/mg,
  228. handler: config.formatterHelpers.nonDestructiveEnclosedTextHelper
  229. },
  230. // strikethrough uses --strike--
  231. {
  232. name: 't2tStrike',
  233. match: '--',
  234. element: 'del',
  235. lookaheadRegExp: /--([^\s](|.*?[^\s])-*)--/mg,
  236. termRegExp: /(--)/mg,
  237. handler: config.formatterHelpers.nonDestructiveEnclosedTextHelper
  238. },
  239. /*
  240. // superscript uses ^^super^^
  241. // this is unofficial
  242. {
  243. name: 't2tSuperscript',
  244. match: '\\^\\^',
  245. element: 'sup',
  246. lookaheadRegExp: /\^\^([^\s](|.*?[^\s])\^*)\^\^/mg,
  247. handler: config.formatterHelpers.nonDestructiveEnclosedTextHelper
  248. },
  249. // subscript uses ,,sub,,
  250. // this is unofficial
  251. {
  252. name: 't2tSubscript',
  253. match: ',,',
  254. element: 'sub',
  255. lookaheadRegExp: /,,([^\s](|.*?[^\s]),*),,/mg,
  256. handler: config.formatterHelpers.nonDestructiveEnclosedTextHelper
  257. },
  258. */
  259. // Monospace uses different variants of backticks
  260. // blocks are bordered with lines containing only 3 backticks
  261. // ```
  262. {
  263. name: 't2tMonospaceBlock',
  264. match: '^```[ \\t]*$',
  265. lookaheadRegExp: /^```[ \t]*(?:\n?((?:.|\n)*?\n)```[ \t]*$)|((?:.|\n)*$)/gm,
  266. handler: function(w) {
  267. this.lookaheadRegExp.lastIndex = w.matchStart;
  268. var lookaheadMatch = this.lookaheadRegExp.exec(w.source);
  269. if(lookaheadMatch && lookaheadMatch.index == w.matchStart) {
  270. if (lookaheadMatch[1]) {
  271. var text = lookaheadMatch[1];
  272. } else {
  273. var text = lookaheadMatch[2].substr(w.matchLength+1);
  274. }
  275. if(config.browser.isIE)
  276. text = text.replace(/\n/g,"\r");
  277. createTiddlyElement(w.output,"pre",null,null,text);
  278. w.nextMatch = this.lookaheadRegExp.lastIndex;
  279. } else w.output.appendChild(document.createTextNode(w.matchText));
  280. }
  281. },
  282. // lines prefixed with 3 backticks are preformatted blocks as well
  283. // one block per line! putting backtic-prefixed lines consecutively will
  284. // result in 2 preformatted blocks!
  285. //SPEED: this can be like tiddly's termregexp syntax like a "Heading"
  286. {
  287. name: 't2tMonospaceLine',
  288. match: '^``` ',
  289. lookaheadRegExp: /^``` ((?:.)*?)$/mg,
  290. element: 'pre',
  291. handler: config.formatterHelpers.enclosedTextHelper
  292. },
  293. // inline monospaced text ``like this``.
  294. // terminates at the first newline without matching backtics.
  295. // this version uses code instead of pre.
  296. {
  297. name: 't2tMonospace',
  298. match: '``',
  299. element: 'code',
  300. lookaheadRegExp: /``([^\s](|.*?[^\s])`*)``/mg,
  301. termRegExp: /(``)/mg,
  302. handler: config.formatterHelpers.nonDestructiveEnclosedTextHelper
  303. },
  304. //t2tCommentblock
  305. {
  306. name: 't2tCommentBlock',
  307. match: '^%%%[ \\t]*\\n',
  308. //emptyblock but no unclosed trailer
  309. //lookaheadRegExp: /^%%%[ \t]*((?:.|\n)*?)\n%%%[ \t]*(\n|$)/mg,
  310. // match next %%% line OR match to EOF
  311. lookaheadRegExp: /^%%%[ \t]*(?:(?:[.|\n]*?)\n%%%[ \t]*$)|(?:[.\n]*$)/gm,
  312. handler: function(w)
  313. {
  314. this.lookaheadRegExp.lastIndex = w.matchStart;
  315. var lookaheadMatch = this.lookaheadRegExp.exec(w.source);
  316. if(lookaheadMatch && lookaheadMatch.index == w.matchStart) {
  317. w.nextMatch = this.lookaheadRegExp.lastIndex;
  318. } else w.output.appendChild(document.createTextNode(w.matchText));
  319. }
  320. },
  321. //SPEED: this can be like tiddly's termregexp syntax like a "Heading"
  322. {
  323. name: 't2tCommentLine',
  324. match: '^%' ,
  325. lookaheadRegExp: /^%.*?$/mg,
  326. handler: function(w)
  327. {
  328. this.lookaheadRegExp.lastIndex = w.matchStart;
  329. var lookaheadMatch = this.lookaheadRegExp.exec(w.source);
  330. if(lookaheadMatch && lookaheadMatch.index == w.matchStart) {
  331. w.nextMatch = this.lookaheadRegExp.lastIndex;
  332. } else w.output.appendChild(document.createTextNode(w.matchText));
  333. }
  334. },
  335. // nonstandard br insertion
  336. // I like tex's \\
  337. {
  338. name: 't2tLineBreak',
  339. match: '\\\\\\\\',
  340. handler: function(w)
  341. {
  342. createTiddlyElement(w.output,'br');
  343. }
  344. },
  345. //TODO: tagged text doesn't get the \n \r IE replacement... test in IE
  346. // t2t tagged text is "passthrough" so what you type is passed on to
  347. // the html interpreter. t2t formatting isn't converted into html.
  348. // Raw html is interpreted by your browser.
  349. // Tagged uses various forms of single-quotes.
  350. // multi-line blocks are bordered by lines containing only '''
  351. {
  352. name: 't2tTaggedBlock',
  353. match: '^\'\'\'[ \\t]*$',
  354. lookaheadRegExp: /^'''[ \t]*(?:((?:.|\n)*?\n)'''[ \t]*$)|((?:.|\n)*$)/gm,
  355. handler: function(w)
  356. {
  357. this.lookaheadRegExp.lastIndex = w.matchStart;
  358. var lookaheadMatch = this.lookaheadRegExp.exec(w.source);
  359. if(lookaheadMatch && lookaheadMatch.index == w.matchStart) {
  360. if (lookaheadMatch[1]) {
  361. var text = lookaheadMatch[1];
  362. } else {
  363. var text = lookaheadMatch[2].substr(w.matchLength+1);
  364. }
  365. if(config.browser.isIE)
  366. text = text.replace(/\n/g,"\r");
  367. createTiddlyElement(w.output,"span").innerHTML = text;
  368. w.nextMatch = this.lookaheadRegExp.lastIndex;
  369. } else w.output.appendChild(document.createTextNode(w.matchText));
  370. }
  371. },
  372. // Single lines prepended with ''' will be considered "tagged"
  373. // Unlike monospace, tagged isn't really a "block" so having multiple
  374. // pre-pended lines won't look any different than one multi-line block.
  375. //SPEED: this can be like tiddly's termregexp syntax like a "Heading"
  376. {
  377. name: 't2tTaggedLine',
  378. match: '^\'\'\' ',
  379. lookaheadRegExp: /^''' ((?:.)*?)$/mg,
  380. handler: function(w)
  381. {
  382. this.lookaheadRegExp.lastIndex = w.matchStart;
  383. var lookaheadMatch = this.lookaheadRegExp.exec(w.source);
  384. if(lookaheadMatch && lookaheadMatch.index == w.matchStart) {
  385. createTiddlyElement(w.output,"span").innerHTML = lookaheadMatch[1];
  386. w.nextMatch = this.lookaheadRegExp.lastIndex;
  387. } else w.output.appendChild(document.createTextNode(w.matchText));
  388. }
  389. },
  390. // inline tagged text is wrapped ''in 2 single quotes''. This type ends at
  391. // the newline if there is no matching ''.
  392. {
  393. name: 't2tTagged',
  394. match: '\'\'',
  395. lookaheadRegExp: /''([^\s](|.*?[^\s])'*)''/mg,
  396. handler: function(w)
  397. {
  398. this.lookaheadRegExp.lastIndex = w.matchStart;
  399. var lookaheadMatch = this.lookaheadRegExp.exec(w.source);
  400. if(lookaheadMatch && lookaheadMatch.index == w.matchStart) {
  401. createTiddlyElement(w.output,"span").innerHTML = lookaheadMatch[1];
  402. w.nextMatch = this.lookaheadRegExp.lastIndex;
  403. } else w.output.appendChild(document.createTextNode(w.matchText));
  404. }
  405. },
  406. //TODO: RAW text doesn't get the \n \r IE replacement... test in IE
  407. // t2t Raw text ignores t2t formatting (any wiki formatting); but characters
  408. // are still 'escaped' out to be displayed by the brower.
  409. // e.g. ""**bold**"" isn't made bold; you see the astrices; while lines using
  410. // html tags will convert angle brackets to < >
  411. // This is synonymous with nowiki
  412. // Blocks of raw text are bordered with blank lines of 3 double quotes.
  413. {
  414. name: 't2tRawBlock',
  415. match: '^\\"\\"\\"[ \\t]*$',
  416. lookaheadRegExp: /^"""[ \t]*(?:((?:.|\n)*?\n)"""[ \t]*$)|((?:.|\n)*$)/gm,
  417. handler: function(w)
  418. {
  419. this.lookaheadRegExp.lastIndex = w.matchStart;
  420. var lookaheadMatch = this.lookaheadRegExp.exec(w.source);
  421. if(lookaheadMatch && lookaheadMatch.index == w.matchStart) {
  422. if (lookaheadMatch[1]) {
  423. var text = lookaheadMatch[1];
  424. } else {
  425. var text = lookaheadMatch[2].substr(w.matchLength+1);
  426. }
  427. //rawblock may not work with this properly?
  428. if(config.browser.isIE)
  429. text = text.replace(/\n/g,"\r");
  430. createTiddlyElement(w.output,"span",null,null,text);
  431. w.nextMatch = this.lookaheadRegExp.lastIndex;
  432. } else w.output.appendChild(document.createTextNode(w.matchText));
  433. }
  434. },
  435. // Any line prepended with 3 double quotes is interpreted as raw.
  436. // Unlike monospace, raw isn't really a "block" so consecutive pre-pended lines
  437. // will have no difference with a multi-line block.
  438. //SPEED: this can be like tiddly's termregexp syntax like a "Heading"
  439. {
  440. name: 't2tRawLine',
  441. match: '^\\"\\"\\" ',
  442. lookaheadRegExp: /^""" ((?:.)*?)$/mg,
  443. handler: function(w)
  444. {
  445. this.lookaheadRegExp.lastIndex = w.matchStart;
  446. var lookaheadMatch = this.lookaheadRegExp.exec(w.source);
  447. if(lookaheadMatch && lookaheadMatch.index == w.matchStart) {
  448. createTiddlyElement(w.output,'span',null,null,lookaheadMatch[1]);
  449. w.nextMatch = this.lookaheadRegExp.lastIndex;
  450. } else w.output.appendChild(document.createTextNode(w.matchText));
  451. }
  452. },
  453. // The in-line format uses 2 double quotes ""raw"".
  454. // The end of the line terminates raw if there is no matching "".
  455. {
  456. name: 't2tRaw',
  457. match: '\\"\\"',
  458. lookaheadRegExp: /""([^\s](|.*?[^\s])"*)""/mg,
  459. handler: function(w)
  460. {
  461. this.lookaheadRegExp.lastIndex = w.matchStart;
  462. var lookaheadMatch = this.lookaheadRegExp.exec(w.source);
  463. if(lookaheadMatch && lookaheadMatch.index == w.matchStart) {
  464. createTiddlyElement(w.output,'span',null,null,lookaheadMatch[1]);
  465. w.nextMatch = this.lookaheadRegExp.lastIndex;
  466. } else w.output.appendChild(document.createTextNode(w.matchText));
  467. }
  468. },
  469. // Section headings are bookended by matching sets of = signs.
  470. // It must all be on one line
  471. //SPEED: simplify match
  472. {
  473. name: 't2tHeading',
  474. //no whitespace at the start
  475. //match: '^ *={1,6}[^=\n]',
  476. //lookaheadRegExp: /^ *(={1,6})([^=\n][^\n]*[^=\n])\1[ \t]*$/mg,
  477. match: '^={1,6}[^ =\n]',
  478. lookaheadRegExp: /^(={1,6})([^ =\n][^\n]*[^=\n])\1[ \t]*$/mg,
  479. handler: function(w)
  480. {
  481. this.lookaheadRegExp.lastIndex = w.matchStart;
  482. var lookaheadMatch = this.lookaheadRegExp.exec(w.source);
  483. if(lookaheadMatch && lookaheadMatch.index == w.matchStart) {
  484. //headings in t2t don't allow markup inside, so we DO NOT use the subwikifyterm here.
  485. createTiddlyElement(w.output,'h'+lookaheadMatch[1].length,null,null,lookaheadMatch[2]);
  486. w.nextMatch = lookaheadMatch.index + lookaheadMatch[0].length;
  487. } else w.output.appendChild(document.createTextNode(w.matchText));
  488. }
  489. },
  490. /*
  491. //This breaks everything. T2T converts emails into mailto:s or it masks them:
  492. // dave@gmail.com --> <dave (AT) gmail dOT com>
  493. {
  494. name: 't2tEmailHiding',
  495. match: '[\\w]+(\\.[\\w\\-\\+_]+)*\\@[\\w\\-\\+_]+\\.[\\w\\-\\+_]+(\\.[\\w\\-\\+_]+)*',
  496. lookaheadRegExp: /([\w]+(?:\.[\w\-\+_]+)*)\@([\w\-\+_]+\.[\w\-\+_]+(?:\.[\w\-\+_]+)*)/mg,
  497. handler: function(w)
  498. {
  499. this.lookaheadRegExp.lastIndex = w.matchStart;
  500. var lookaheadMatch = this.lookaheadRegExp.exec(w.source);
  501. if(lookaheadMatch && lookaheadMatch.index == w.matchStart) {
  502. var text = '<' + lookaheadMatch[1] + " (AT) " + lookaheadMatch[2] + '>';
  503. text = text.replace(/\./g," DOT ");
  504. createTiddlyElement(w.output,"span",null,null,text);
  505. w.nextMatch = this.lookaheadRegExp.lastIndex;
  506. }
  507. }
  508. },
  509. */
  510. // push-pop version keeps the array sized to whatever it should be. This is
  511. // probably faster than the array reassignment (below this function) especially
  512. // in the simplest and more likely use case: only using top-level headings.
  513. // In practice, though, 10k numbered headings takes forever in BOTH cases.
  514. // HTML doesn't have an inherent number scheme for header tags. Just treat
  515. // them as unnumbered for now.
  516. // SPEED: simplify match. see what termregexp can do.
  517. {
  518. name: 't2tNumberedHeading',
  519. //no whitespace at the start.
  520. //match: '^ *\\+{1,6}[^\\+\\n]',
  521. //lookaheadRegExp: /^ *(\+{1,6})([^\+\n][^\n]*[^\+\n])\1[ \t]*$/mg,
  522. match: '^\\+{1,6}[^ \\+\\n]',
  523. lookaheadRegExp: /^(\+{1,6})([^ \+\n][^\n]*[^\+\n])\1[ \t]*$/mg,
  524. handler: function(w)
  525. {
  526. if (! w.numheading) {
  527. w.numheading = [0,0];
  528. }
  529. this.lookaheadRegExp.lastIndex = w.matchStart;
  530. var lookaheadMatch = this.lookaheadRegExp.exec(w.source);
  531. if(lookaheadMatch && lookaheadMatch.index == w.matchStart) {
  532. var text = lookaheadMatch[2];
  533. // since length=1 == arrayindex 0
  534. var headingindex = lookaheadMatch[1].length - 1;
  535. // since length1 means we want 2 items in the array
  536. var targetlength = headingindex + 2;
  537. //add or remove values until numheading.length = lookaheadMatch[1].length
  538. //push 0's first to add
  539. for (var i = w.numheading.length; i < targetlength; i++)
  540. w.numheading.push(0);
  541. //removing is eashy, you can just change length and it truncates array (garbage later)
  542. w.numheading.length = targetlength;
  543. //increment this heading
  544. w.numheading[headingindex]++;
  545. //reset the next level down to 0
  546. w.numheading[headingindex + 1] = 0;
  547. text = " " + text;
  548. for (headingindex; headingindex >= 0; headingindex--) {
  549. text = w.numheading[headingindex] + "." + text;
  550. }
  551. createTiddlyElement(w.output,'h'+lookaheadMatch[1].length,null,null,text);
  552. w.nextMatch = lookaheadMatch.index + lookaheadMatch[0].length;
  553. } else w.output.appendChild(document.createTextNode(w.matchText));
  554. }
  555. },
  556. // t2t concatenates newlines to make paragraphs.
  557. // WARNING: tiddly doesn't support this very well; the appearance is
  558. // OK but the dynamic HTML has oddly-nested paragraphs.
  559. {
  560. name: 't2tParagraph',
  561. match: '\\n{2,}',
  562. handler: function(w)
  563. {
  564. //do you like stack overflows in your html engine?
  565. //try making 200 paragraphs with this.
  566. //createTiddlyElement(w.output,'p');
  567. //Looks ok-ish, but now you have manual BR's mixed in
  568. //with the implied whitespace by an <h1> or <quote>.
  569. //createTiddlyElement(w.output,'br');
  570. //createTiddlyElement(w.output,'br');
  571. //ohh this is so wrong to do, but it works pretty well.
  572. //no stack explosions, and it looks visually correct.
  573. createTiddlyElement(w.output,"span").innerHTML = "<p></p>";
  574. }
  575. },
  576. {
  577. name: 't2tLinkImg',
  578. match: '(?:^\\[)|(?:\\[)',
  579. // ([^\n\r]*?) matches 2 separate entities: [foo] and [soft]
  580. // ([^\n\r]*) matches entity: [(foo] and [soft)]
  581. // use *? or include [] us unallowed chaaracters
  582. lookaheadRegExp: /(?:(^\[)|(?:\[))(?:(\[([^\[\]\n\r\f]+)\] ?([^\n\r\[\]]*))|([^\s\[\]]+)|(([^\[\]\n\r\f]+) ([^\[\]\s]+)))(?:(\]$)|(?:\]))/mg,
  583. handler: function(w)
  584. {
  585. this.lookaheadRegExp.lastIndex = w.matchStart;
  586. var lookaheadMatch = this.lookaheadRegExp.exec(w.source);
  587. if(lookaheadMatch && lookaheadMatch.index == w.matchStart)
  588. {
  589. //first and last group match for alignment to be used for images.
  590. if (lookaheadMatch[1] && (! lookaheadMatch[9]))
  591. {
  592. var imagealign = "left";
  593. }
  594. else if ( (! lookaheadMatch[1]) && lookaheadMatch[9])
  595. {
  596. var imagealign = "right";
  597. }
  598. //alignment discovered, now see what type of bracket link.
  599. // [[image] link]
  600. //2 = imglink; 3 img 4 link
  601. if (lookaheadMatch[2])
  602. {
  603. var e = w.output;
  604. // 4 may not exist, it could just be an image formatted [[image]]
  605. if (lookaheadMatch[4]) {
  606. var link = lookaheadMatch[4];
  607. e = config.formatterHelpers.isExternalLink(link) ?
  608. createExternalLink(w.output,link) :
  609. createTiddlyLink(w.output,link,false,null,w.isStatic,w.tiddler);
  610. addClass(e,"imageLink");
  611. }
  612. var img = createTiddlyElement(e,"img");
  613. if (imagealign) img.align = imagealign;
  614. img.src = lookaheadMatch[3];
  615. img.title = lookaheadMatch[4];
  616. img.setAttribute("alt",lookaheadMatch[4]);
  617. w.nextMatch = this.lookaheadRegExp.lastIndex;
  618. /*
  619. if (align)
  620. {
  621. alert( align+"-ed image\nfile: '"+lookaheadMatch[3]+"'\nurl: '"+lookaheadMatch[4]+"'");
  622. } else {
  623. alert( "unaligned image\nfile: '"+lookaheadMatch[3]+"'\nurl: '"+lookaheadMatch[4]+"'");
  624. }
  625. */
  626. }
  627. // [link] or [image]
  628. //5 = url or img
  629. else if (lookaheadMatch[5])
  630. {
  631. // IMAGE-EXTENSION-REGEX
  632. if (/\.((gif)|(je?pg)|(png)|(bmp)|(tif))$/i.exec(lookaheadMatch[5]))
  633. {
  634. //alert("image extension found");
  635. var e = w.output;
  636. var img = createTiddlyElement(e,"img");
  637. if (imagealign) img.align = imagealign;
  638. img.src = lookaheadMatch[5];
  639. img.title = lookaheadMatch[5];
  640. img.setAttribute("alt",lookaheadMatch[5]);
  641. w.nextMatch = this.lookaheadRegExp.lastIndex;
  642. /*
  643. if (align)
  644. {
  645. alert( align+"-ed image\nfile: '"+lookaheadMatch[5]+"'");
  646. } else {
  647. alert( "unaligned image\nfile: '"+lookaheadMatch[5]+"'");
  648. }
  649. */
  650. }
  651. else
  652. {
  653. //alert("no image extension found");
  654. //obviously this isn't an image by the extension, so it is just a link.
  655. var link = lookaheadMatch[5];
  656. var e = (config.formatterHelpers.isExternalLink(link)) ?
  657. createExternalLink(w.output,link) :
  658. createTiddlyLink(w.output,link,false,null,w.isStatic,w.tiddler);
  659. createTiddlyText(e,link);
  660. w.nextMatch = this.lookaheadRegExp.lastIndex;
  661. //alert("Simple link to url: '"+lookaheadMatch[5]+"'");
  662. }
  663. }
  664. // [some name text linkWithNoSpaces] the existence of a space is the key!
  665. //6 named link 7 = name 8 = link
  666. else if (lookaheadMatch[6])
  667. {
  668. var text = lookaheadMatch[7];
  669. var link = lookaheadMatch[8];
  670. var e = (config.formatterHelpers.isExternalLink(link)) ?
  671. createExternalLink(w.output,link) :
  672. createTiddlyLink(w.output,link,false,null,w.isStatic,w.tiddler);
  673. createTiddlyText(e,text);
  674. w.nextMatch = this.lookaheadRegExp.lastIndex;
  675. //alert( "name: '"+lookaheadMatch[8]+"'\nurl: '"+lookaheadMatch[9]+"'");
  676. } else w.output.appendChild(document.createTextNode(w.matchText));
  677. } else w.output.appendChild(document.createTextNode(w.matchText));
  678. }
  679. },
  680. // COPY from tiddlywiki code
  681. {
  682. name: "t2tList",
  683. match: "^(?:[ ]*[\\+-;:] )",
  684. //lookaheadRegExp: /^(?:[ ]*(?:(-)|(\+)|(:)|( )) )/mg,
  685. lookaheadRegExp: /^(?:[ ]*(?:(-)|(\+)|(;)|(:)) )/mg,
  686. //http://stackoverflow.com/questions/5531899/regex-do-not-include-a-substring-within-a-group/5531972#5531972
  687. // V~do include this if it matched
  688. termRegExp: /(\n(?:(?=[ ]*[\+;:-] )|[ ]*[\+;:-]))/mg,
  689. // ^~lookahead for this but don't include it
  690. handler: function(w)
  691. {
  692. //if (! w.lengths) {
  693. w.lengths = [0];
  694. //}
  695. var stack = [w.output];
  696. var currLevel = 0, currType = null;
  697. var listLevel, listType, itemType, baseType;
  698. w.nextMatch = w.matchStart;
  699. this.lookaheadRegExp.lastIndex = w.nextMatch;
  700. var lookaheadMatch = this.lookaheadRegExp.exec(w.source);
  701. while(lookaheadMatch && lookaheadMatch.index == w.nextMatch) {
  702. if(lookaheadMatch[1]) {
  703. listType = "ul";
  704. itemType = "li";
  705. } else if(lookaheadMatch[2]) {
  706. listType = "ol";
  707. itemType = "li";
  708. } else if(lookaheadMatch[3]) {
  709. listType = "dl";
  710. itemType = "dt";
  711. } else if(lookaheadMatch[4]) {
  712. listType = "dl";
  713. itemType = "dd";
  714. }
  715. if(!baseType)
  716. baseType = listType;
  717. //fill levelstack
  718. var l = lookaheadMatch[0].length
  719. while ( w.lengths[w.lengths.length-1] != l ) {
  720. if ( l > w.lengths[w.lengths.length-1] ) {
  721. w.lengths.push(l);
  722. break;
  723. } else {
  724. w.lengths.pop();
  725. }
  726. }
  727. listLevel = w.lengths.length-1;
  728. w.nextMatch += l;
  729. var t;
  730. if(listLevel > currLevel) {
  731. for(t=currLevel; t<listLevel; t++) {
  732. var target = (currLevel == 0) ? stack[stack.length-1] : stack[stack.length-1].lastChild;
  733. stack.push(createTiddlyElement(target,listType));
  734. }
  735. } else if(listType!=baseType && listLevel==1) {
  736. w.nextMatch -= lookaheadMatch[0].length;
  737. return;
  738. } else if(listLevel < currLevel) {
  739. for(t=currLevel; t>listLevel; t--)
  740. stack.pop();
  741. } else if(listLevel == currLevel && listType != currType) {
  742. stack.pop();
  743. stack.push(createTiddlyElement(stack[stack.length-1].lastChild,listType));
  744. }
  745. currLevel = listLevel;
  746. currType = listType;
  747. var e = createTiddlyElement(stack[stack.length-1],itemType);
  748. w.subWikifyTerm(e,this.termRegExp);
  749. this.lookaheadRegExp.lastIndex = w.nextMatch;
  750. lookaheadMatch = this.lookaheadRegExp.exec(w.source);
  751. }
  752. }
  753. },
  754. /*
  755. //array reassignment version. I think that the up-to-6 assignments per iteration
  756. //could have worse performance that resizing the array.
  757. //see above for array re-sizing.
  758. // HTML doesn't have an inherent number scheme for header tags. Just treat
  759. // them as unnumbered for now.
  760. {
  761. name: 't2tNumberedHeading',
  762. match: '^ *\\+{1,6}[^\\+\n]',
  763. lookaheadRegExp: / *(\+{1,6})([^\+\n][^\n]*[^\+\n])\1[ \t]*$/mg,
  764. handler: function(w)
  765. {
  766. if (! w.numheading) {
  767. w.numheading = [0,0,0,0,0,0];
  768. }
  769. this.lookaheadRegExp.lastIndex = w.matchStart;
  770. var lookaheadMatch = this.lookaheadRegExp.exec(w.source);
  771. if(lookaheadMatch && lookaheadMatch.index == w.matchStart) {
  772. var text = lookaheadMatch[2];
  773. var headingindex = lookaheadMatch[1].length - 1;
  774. w.numheading[headingindex]++;
  775. //reset the next levels down to 0
  776. for(var i = headingindex + 1; i<=5; i++){
  777. w.numheading[i] = 0;
  778. }
  779. text = " " + text;
  780. for (headingindex; headingindex >= 0; headingindex--) {
  781. text = w.numheading[headingindex] + "." + text;
  782. }
  783. createTiddlyElement(w.output,'h'+lookaheadMatch[1].length,null,null,text);
  784. w.nextMatch = lookaheadMatch.index + lookaheadMatch[0].length;
  785. } else {
  786. w.output.appendChild(document.createTextNode(w.matchText));
  787. }
  788. }
  789. },
  790. */
  791. // t2t prepends TABs on lines that form blockquotes.
  792. // Consecutive lines of the same depth of tabs are in the same block.
  793. // This is just like tiddly markup, only they use angle brackets.
  794. // Thus, I am ripping this code from a 2.6.1 formatters.
  795. {
  796. name: 't2tQuoteByLine',
  797. match: '\\t+',
  798. lookaheadRegExp: /^\t+/mg,
  799. termRegExp: /(\n)/mg,
  800. element: 'blockquote',
  801. handler: function(w)
  802. {
  803. var stack = [w.output];
  804. var currLevel = 0;
  805. var newLevel = w.matchLength;
  806. var t;
  807. do {
  808. if(newLevel > currLevel) {
  809. for(t=currLevel; t<newLevel; t++)
  810. stack.push(createTiddlyElement(stack[stack.length-1],this.element));
  811. } else if(newLevel < currLevel) {
  812. for(t=currLevel; t>newLevel; t--)
  813. stack.pop();
  814. }
  815. currLevel = newLevel;
  816. w.subWikifyTerm(stack[stack.length-1],this.termRegExp);
  817. //originally they insert linebreaks... not in t2t
  818. //createTiddlyElement(stack[stack.length-1],'br');
  819. //replace the newline that was consumed
  820. if(config.browser.isIE) {
  821. var e = document.createTextNode('\r');
  822. } else {
  823. var e = document.createTextNode('\n');
  824. }
  825. stack[stack.length-1].appendChild(e);
  826. this.lookaheadRegExp.lastIndex = w.nextMatch;
  827. var lookaheadMatch = this.lookaheadRegExp.exec(w.source);
  828. var matched = lookaheadMatch && lookaheadMatch.index == w.nextMatch;
  829. if(matched) {
  830. newLevel = lookaheadMatch[0].length;
  831. w.nextMatch += lookaheadMatch[0].length;
  832. }
  833. } while(matched);
  834. }
  835. },
  836. /*
  837. //COPY from tiddlywiki config.formatters
  838. {
  839. name: "macro",
  840. match: "<<",
  841. ////don't start new macro << HERE
  842. //lookaheadRegExp: /<<([^>\s(?:<<)]+)(?:\s*)((?:[^>]|(?:>(?!>)))*)>>/mg,
  843. lookaheadRegExp: /<<([^>\s]+)(?:\s*)((?:[^>]|(?:>(?!>)))*)>>/mg,
  844. handler: function(w)
  845. {
  846. this.lookaheadRegExp.lastIndex = w.matchStart;
  847. var lookaheadMatch = this.lookaheadRegExp.exec(w.source);
  848. if(lookaheadMatch && lookaheadMatch.index == w.matchStart && lookaheadMatch[1]) {
  849. w.nextMatch = this.lookaheadRegExp.lastIndex;
  850. invokeMacro(w.output,lookaheadMatch[1],lookaheadMatch[2],w,w.tiddler);
  851. //with this modification for nondestructiveness.
  852. } else {
  853. w.output.appendChild(document.createTextNode(w.matchText));
  854. }
  855. }
  856. },
  857. */
  858. ];
  859. t2tReuse = function(name)
  860. {
  861. var i;
  862. for (i = 0; i < config.formatters.length; i++) {
  863. if (name == config.formatters[i].name)
  864. return config.formatters[i];
  865. }
  866. return null;
  867. };
  868. //attach reused formatters from standard config.formatters.
  869. config.t2tFormatters.push(t2tReuse("macro"));
  870. config.t2tFormatters.push(t2tReuse("wikiLink"));
  871. config.t2tFormatters.push(t2tReuse("urlLink"));
  872. config.t2tFormatters.push(t2tReuse("htmlEntitiesEncoding"));
  873. //config.t2tFormatters.push(t2tReuse("mdash"));
  874. config.t2tFormatters.push(t2tReuse("table"));
  875. /*
  876. var text = "t2tFormatters:\n";
  877. for (var i=0; i < config.t2tFormatters.length; i++)
  878. text = text + config.t2tFormatters[i].name + "\n";
  879. alert(text);
  880. */
  881. config.parsers.t2tFormatter = new Formatter(config.t2tFormatters);
  882. config.parsers.t2tFormatter.format = 't2t';
  883. config.parsers.t2tFormatter.formatTag = 't2t';
  884. } // end of 'install only once'
  885. //}}}
  886. /***
  887. vim: ft=javascript
  888. ***/