openrat-cms

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README

tiddlywiki.js (8510B)


      1 // CodeMirror, copyright (c) by Marijn Haverbeke and others
      2 // Distributed under an MIT license: http://codemirror.net/LICENSE
      3 
      4 /***
      5     |''Name''|tiddlywiki.js|
      6     |''Description''|Enables TiddlyWikiy syntax highlighting using CodeMirror|
      7     |''Author''|PMario|
      8     |''Version''|0.1.7|
      9     |''Status''|''stable''|
     10     |''Source''|[[GitHub|https://github.com/pmario/CodeMirror2/blob/tw-syntax/mode/tiddlywiki]]|
     11     |''Documentation''|http://codemirror.tiddlyspace.com/|
     12     |''License''|[[MIT License|http://www.opensource.org/licenses/mit-license.php]]|
     13     |''CoreVersion''|2.5.0|
     14     |''Requires''|codemirror.js|
     15     |''Keywords''|syntax highlighting color code mirror codemirror|
     16     ! Info
     17     CoreVersion parameter is needed for TiddlyWiki only!
     18 ***/
     19 
     20 (function(mod) {
     21   if (typeof exports == "object" && typeof module == "object") // CommonJS
     22     mod(require("../../lib/codemirror"));
     23   else if (typeof define == "function" && define.amd) // AMD
     24     define(["../../lib/codemirror"], mod);
     25   else // Plain browser env
     26     mod(CodeMirror);
     27 })(function(CodeMirror) {
     28 "use strict";
     29 
     30 CodeMirror.defineMode("tiddlywiki", function () {
     31   // Tokenizer
     32   var textwords = {};
     33 
     34   var keywords = {
     35     "allTags": true, "closeAll": true, "list": true,
     36     "newJournal": true, "newTiddler": true,
     37     "permaview": true, "saveChanges": true,
     38     "search": true, "slider": true, "tabs": true,
     39     "tag": true, "tagging": true, "tags": true,
     40     "tiddler": true, "timeline": true,
     41     "today": true, "version": true, "option": true,
     42     "with": true, "filter": true
     43   };
     44 
     45   var isSpaceName = /[\w_\-]/i,
     46       reHR = /^\-\-\-\-+$/,                                 // <hr>
     47       reWikiCommentStart = /^\/\*\*\*$/,            // /***
     48       reWikiCommentStop = /^\*\*\*\/$/,             // ***/
     49       reBlockQuote = /^<<<$/,
     50 
     51       reJsCodeStart = /^\/\/\{\{\{$/,                       // //{{{ js block start
     52       reJsCodeStop = /^\/\/\}\}\}$/,                        // //}}} js stop
     53       reXmlCodeStart = /^<!--\{\{\{-->$/,           // xml block start
     54       reXmlCodeStop = /^<!--\}\}\}-->$/,            // xml stop
     55 
     56       reCodeBlockStart = /^\{\{\{$/,                        // {{{ TW text div block start
     57       reCodeBlockStop = /^\}\}\}$/,                 // }}} TW text stop
     58 
     59       reUntilCodeStop = /.*?\}\}\}/;
     60 
     61   function chain(stream, state, f) {
     62     state.tokenize = f;
     63     return f(stream, state);
     64   }
     65 
     66   function tokenBase(stream, state) {
     67     var sol = stream.sol(), ch = stream.peek();
     68 
     69     state.block = false;        // indicates the start of a code block.
     70 
     71     // check start of  blocks
     72     if (sol && /[<\/\*{}\-]/.test(ch)) {
     73       if (stream.match(reCodeBlockStart)) {
     74         state.block = true;
     75         return chain(stream, state, twTokenCode);
     76       }
     77       if (stream.match(reBlockQuote))
     78         return 'quote';
     79       if (stream.match(reWikiCommentStart) || stream.match(reWikiCommentStop))
     80         return 'comment';
     81       if (stream.match(reJsCodeStart) || stream.match(reJsCodeStop) || stream.match(reXmlCodeStart) || stream.match(reXmlCodeStop))
     82         return 'comment';
     83       if (stream.match(reHR))
     84         return 'hr';
     85     }
     86 
     87     stream.next();
     88     if (sol && /[\/\*!#;:>|]/.test(ch)) {
     89       if (ch == "!") { // tw header
     90         stream.skipToEnd();
     91         return "header";
     92       }
     93       if (ch == "*") { // tw list
     94         stream.eatWhile('*');
     95         return "comment";
     96       }
     97       if (ch == "#") { // tw numbered list
     98         stream.eatWhile('#');
     99         return "comment";
    100       }
    101       if (ch == ";") { // definition list, term
    102         stream.eatWhile(';');
    103         return "comment";
    104       }
    105       if (ch == ":") { // definition list, description
    106         stream.eatWhile(':');
    107         return "comment";
    108       }
    109       if (ch == ">") { // single line quote
    110         stream.eatWhile(">");
    111         return "quote";
    112       }
    113       if (ch == '|')
    114         return 'header';
    115     }
    116 
    117     if (ch == '{' && stream.match(/\{\{/))
    118       return chain(stream, state, twTokenCode);
    119 
    120     // rudimentary html:// file:// link matching. TW knows much more ...
    121     if (/[hf]/i.test(ch) &&
    122         /[ti]/i.test(stream.peek()) &&
    123         stream.match(/\b(ttps?|tp|ile):\/\/[\-A-Z0-9+&@#\/%?=~_|$!:,.;]*[A-Z0-9+&@#\/%=~_|$]/i))
    124       return "link";
    125 
    126     // just a little string indicator, don't want to have the whole string covered
    127     if (ch == '"')
    128       return 'string';
    129 
    130     if (ch == '~')    // _no_ CamelCase indicator should be bold
    131       return 'brace';
    132 
    133     if (/[\[\]]/.test(ch) && stream.match(ch)) // check for [[..]]
    134       return 'brace';
    135 
    136     if (ch == "@") {    // check for space link. TODO fix @@...@@ highlighting
    137       stream.eatWhile(isSpaceName);
    138       return "link";
    139     }
    140 
    141     if (/\d/.test(ch)) {        // numbers
    142       stream.eatWhile(/\d/);
    143       return "number";
    144     }
    145 
    146     if (ch == "/") { // tw invisible comment
    147       if (stream.eat("%")) {
    148         return chain(stream, state, twTokenComment);
    149       } else if (stream.eat("/")) { //
    150         return chain(stream, state, twTokenEm);
    151       }
    152     }
    153 
    154     if (ch == "_" && stream.eat("_")) // tw underline
    155         return chain(stream, state, twTokenUnderline);
    156 
    157     // strikethrough and mdash handling
    158     if (ch == "-" && stream.eat("-")) {
    159       // if strikethrough looks ugly, change CSS.
    160       if (stream.peek() != ' ')
    161         return chain(stream, state, twTokenStrike);
    162       // mdash
    163       if (stream.peek() == ' ')
    164         return 'brace';
    165     }
    166 
    167     if (ch == "'" && stream.eat("'")) // tw bold
    168       return chain(stream, state, twTokenStrong);
    169 
    170     if (ch == "<" && stream.eat("<")) // tw macro
    171       return chain(stream, state, twTokenMacro);
    172 
    173     // core macro handling
    174     stream.eatWhile(/[\w\$_]/);
    175     return textwords.propertyIsEnumerable(stream.current()) ? "keyword" : null
    176   }
    177 
    178   // tw invisible comment
    179   function twTokenComment(stream, state) {
    180     var maybeEnd = false, ch;
    181     while (ch = stream.next()) {
    182       if (ch == "/" && maybeEnd) {
    183         state.tokenize = tokenBase;
    184         break;
    185       }
    186       maybeEnd = (ch == "%");
    187     }
    188     return "comment";
    189   }
    190 
    191   // tw strong / bold
    192   function twTokenStrong(stream, state) {
    193     var maybeEnd = false,
    194     ch;
    195     while (ch = stream.next()) {
    196       if (ch == "'" && maybeEnd) {
    197         state.tokenize = tokenBase;
    198         break;
    199       }
    200       maybeEnd = (ch == "'");
    201     }
    202     return "strong";
    203   }
    204 
    205   // tw code
    206   function twTokenCode(stream, state) {
    207     var sb = state.block;
    208 
    209     if (sb && stream.current()) {
    210       return "comment";
    211     }
    212 
    213     if (!sb && stream.match(reUntilCodeStop)) {
    214       state.tokenize = tokenBase;
    215       return "comment";
    216     }
    217 
    218     if (sb && stream.sol() && stream.match(reCodeBlockStop)) {
    219       state.tokenize = tokenBase;
    220       return "comment";
    221     }
    222 
    223     stream.next();
    224     return "comment";
    225   }
    226 
    227   // tw em / italic
    228   function twTokenEm(stream, state) {
    229     var maybeEnd = false,
    230     ch;
    231     while (ch = stream.next()) {
    232       if (ch == "/" && maybeEnd) {
    233         state.tokenize = tokenBase;
    234         break;
    235       }
    236       maybeEnd = (ch == "/");
    237     }
    238     return "em";
    239   }
    240 
    241   // tw underlined text
    242   function twTokenUnderline(stream, state) {
    243     var maybeEnd = false,
    244     ch;
    245     while (ch = stream.next()) {
    246       if (ch == "_" && maybeEnd) {
    247         state.tokenize = tokenBase;
    248         break;
    249       }
    250       maybeEnd = (ch == "_");
    251     }
    252     return "underlined";
    253   }
    254 
    255   // tw strike through text looks ugly
    256   // change CSS if needed
    257   function twTokenStrike(stream, state) {
    258     var maybeEnd = false, ch;
    259 
    260     while (ch = stream.next()) {
    261       if (ch == "-" && maybeEnd) {
    262         state.tokenize = tokenBase;
    263         break;
    264       }
    265       maybeEnd = (ch == "-");
    266     }
    267     return "strikethrough";
    268   }
    269 
    270   // macro
    271   function twTokenMacro(stream, state) {
    272     if (stream.current() == '<<') {
    273       return 'macro';
    274     }
    275 
    276     var ch = stream.next();
    277     if (!ch) {
    278       state.tokenize = tokenBase;
    279       return null;
    280     }
    281     if (ch == ">") {
    282       if (stream.peek() == '>') {
    283         stream.next();
    284         state.tokenize = tokenBase;
    285         return "macro";
    286       }
    287     }
    288 
    289     stream.eatWhile(/[\w\$_]/);
    290     return keywords.propertyIsEnumerable(stream.current()) ? "keyword" : null
    291   }
    292 
    293   // Interface
    294   return {
    295     startState: function () {
    296       return {tokenize: tokenBase};
    297     },
    298 
    299     token: function (stream, state) {
    300       if (stream.eatSpace()) return null;
    301       var style = state.tokenize(stream, state);
    302       return style;
    303     }
    304   };
    305 });
    306 
    307 CodeMirror.defineMIME("text/x-tiddlywiki", "tiddlywiki");
    308 });