github.com/elliott5/community@v0.14.1-0.20160709191136-823126fb026a/app/public/codemirror/mode/textile/textile.js (about)

     1  // CodeMirror, copyright (c) by Marijn Haverbeke and others
     2  // Distributed under an MIT license: http://codemirror.net/LICENSE
     3  
     4  (function(mod) {
     5    if (typeof exports == "object" && typeof module == "object") { // CommonJS
     6      mod(require("../../lib/codemirror"));
     7    } else if (typeof define == "function" && define.amd) { // AMD
     8      define(["../../lib/codemirror"], mod);
     9    } else { // Plain browser env
    10      mod(CodeMirror);
    11    }
    12  })(function(CodeMirror) {
    13    "use strict";
    14  
    15    var TOKEN_STYLES = {
    16      addition: "positive",
    17      attributes: "attribute",
    18      bold: "strong",
    19      cite: "keyword",
    20      code: "atom",
    21      definitionList: "number",
    22      deletion: "negative",
    23      div: "punctuation",
    24      em: "em",
    25      footnote: "variable",
    26      footCite: "qualifier",
    27      header: "header",
    28      html: "comment",
    29      image: "string",
    30      italic: "em",
    31      link: "link",
    32      linkDefinition: "link",
    33      list1: "variable-2",
    34      list2: "variable-3",
    35      list3: "keyword",
    36      notextile: "string-2",
    37      pre: "operator",
    38      p: "property",
    39      quote: "bracket",
    40      span: "quote",
    41      specialChar: "tag",
    42      strong: "strong",
    43      sub: "builtin",
    44      sup: "builtin",
    45      table: "variable-3",
    46      tableHeading: "operator"
    47    };
    48  
    49    function startNewLine(stream, state) {
    50      state.mode = Modes.newLayout;
    51      state.tableHeading = false;
    52  
    53      if (state.layoutType === "definitionList" && state.spanningLayout &&
    54          stream.match(RE("definitionListEnd"), false))
    55        state.spanningLayout = false;
    56    }
    57  
    58    function handlePhraseModifier(stream, state, ch) {
    59      if (ch === "_") {
    60        if (stream.eat("_"))
    61          return togglePhraseModifier(stream, state, "italic", /__/, 2);
    62        else
    63          return togglePhraseModifier(stream, state, "em", /_/, 1);
    64      }
    65  
    66      if (ch === "*") {
    67        if (stream.eat("*")) {
    68          return togglePhraseModifier(stream, state, "bold", /\*\*/, 2);
    69        }
    70        return togglePhraseModifier(stream, state, "strong", /\*/, 1);
    71      }
    72  
    73      if (ch === "[") {
    74        if (stream.match(/\d+\]/)) state.footCite = true;
    75        return tokenStyles(state);
    76      }
    77  
    78      if (ch === "(") {
    79        var spec = stream.match(/^(r|tm|c)\)/);
    80        if (spec)
    81          return tokenStylesWith(state, TOKEN_STYLES.specialChar);
    82      }
    83  
    84      if (ch === "<" && stream.match(/(\w+)[^>]+>[^<]+<\/\1>/))
    85        return tokenStylesWith(state, TOKEN_STYLES.html);
    86  
    87      if (ch === "?" && stream.eat("?"))
    88        return togglePhraseModifier(stream, state, "cite", /\?\?/, 2);
    89  
    90      if (ch === "=" && stream.eat("="))
    91        return togglePhraseModifier(stream, state, "notextile", /==/, 2);
    92  
    93      if (ch === "-" && !stream.eat("-"))
    94        return togglePhraseModifier(stream, state, "deletion", /-/, 1);
    95  
    96      if (ch === "+")
    97        return togglePhraseModifier(stream, state, "addition", /\+/, 1);
    98  
    99      if (ch === "~")
   100        return togglePhraseModifier(stream, state, "sub", /~/, 1);
   101  
   102      if (ch === "^")
   103        return togglePhraseModifier(stream, state, "sup", /\^/, 1);
   104  
   105      if (ch === "%")
   106        return togglePhraseModifier(stream, state, "span", /%/, 1);
   107  
   108      if (ch === "@")
   109        return togglePhraseModifier(stream, state, "code", /@/, 1);
   110  
   111      if (ch === "!") {
   112        var type = togglePhraseModifier(stream, state, "image", /(?:\([^\)]+\))?!/, 1);
   113        stream.match(/^:\S+/); // optional Url portion
   114        return type;
   115      }
   116      return tokenStyles(state);
   117    }
   118  
   119    function togglePhraseModifier(stream, state, phraseModifier, closeRE, openSize) {
   120      var charBefore = stream.pos > openSize ? stream.string.charAt(stream.pos - openSize - 1) : null;
   121      var charAfter = stream.peek();
   122      if (state[phraseModifier]) {
   123        if ((!charAfter || /\W/.test(charAfter)) && charBefore && /\S/.test(charBefore)) {
   124          var type = tokenStyles(state);
   125          state[phraseModifier] = false;
   126          return type;
   127        }
   128      } else if ((!charBefore || /\W/.test(charBefore)) && charAfter && /\S/.test(charAfter) &&
   129                 stream.match(new RegExp("^.*\\S" + closeRE.source + "(?:\\W|$)"), false)) {
   130        state[phraseModifier] = true;
   131        state.mode = Modes.attributes;
   132      }
   133      return tokenStyles(state);
   134    };
   135  
   136    function tokenStyles(state) {
   137      var disabled = textileDisabled(state);
   138      if (disabled) return disabled;
   139  
   140      var styles = [];
   141      if (state.layoutType) styles.push(TOKEN_STYLES[state.layoutType]);
   142  
   143      styles = styles.concat(activeStyles(
   144        state, "addition", "bold", "cite", "code", "deletion", "em", "footCite",
   145        "image", "italic", "link", "span", "strong", "sub", "sup", "table", "tableHeading"));
   146  
   147      if (state.layoutType === "header")
   148        styles.push(TOKEN_STYLES.header + "-" + state.header);
   149  
   150      return styles.length ? styles.join(" ") : null;
   151    }
   152  
   153    function textileDisabled(state) {
   154      var type = state.layoutType;
   155  
   156      switch(type) {
   157      case "notextile":
   158      case "code":
   159      case "pre":
   160        return TOKEN_STYLES[type];
   161      default:
   162        if (state.notextile)
   163          return TOKEN_STYLES.notextile + (type ? (" " + TOKEN_STYLES[type]) : "");
   164        return null;
   165      }
   166    }
   167  
   168    function tokenStylesWith(state, extraStyles) {
   169      var disabled = textileDisabled(state);
   170      if (disabled) return disabled;
   171  
   172      var type = tokenStyles(state);
   173      if (extraStyles)
   174        return type ? (type + " " + extraStyles) : extraStyles;
   175      else
   176        return type;
   177    }
   178  
   179    function activeStyles(state) {
   180      var styles = [];
   181      for (var i = 1; i < arguments.length; ++i) {
   182        if (state[arguments[i]])
   183          styles.push(TOKEN_STYLES[arguments[i]]);
   184      }
   185      return styles;
   186    }
   187  
   188    function blankLine(state) {
   189      var spanningLayout = state.spanningLayout, type = state.layoutType;
   190  
   191      for (var key in state) if (state.hasOwnProperty(key))
   192        delete state[key];
   193  
   194      state.mode = Modes.newLayout;
   195      if (spanningLayout) {
   196        state.layoutType = type;
   197        state.spanningLayout = true;
   198      }
   199    }
   200  
   201    var REs = {
   202      cache: {},
   203      single: {
   204        bc: "bc",
   205        bq: "bq",
   206        definitionList: /- [^(?::=)]+:=+/,
   207        definitionListEnd: /.*=:\s*$/,
   208        div: "div",
   209        drawTable: /\|.*\|/,
   210        foot: /fn\d+/,
   211        header: /h[1-6]/,
   212        html: /\s*<(?:\/)?(\w+)(?:[^>]+)?>(?:[^<]+<\/\1>)?/,
   213        link: /[^"]+":\S/,
   214        linkDefinition: /\[[^\s\]]+\]\S+/,
   215        list: /(?:#+|\*+)/,
   216        notextile: "notextile",
   217        para: "p",
   218        pre: "pre",
   219        table: "table",
   220        tableCellAttributes: /[\/\\]\d+/,
   221        tableHeading: /\|_\./,
   222        tableText: /[^"_\*\[\(\?\+~\^%@|-]+/,
   223        text: /[^!"_=\*\[\(<\?\+~\^%@-]+/
   224      },
   225      attributes: {
   226        align: /(?:<>|<|>|=)/,
   227        selector: /\([^\(][^\)]+\)/,
   228        lang: /\[[^\[\]]+\]/,
   229        pad: /(?:\(+|\)+){1,2}/,
   230        css: /\{[^\}]+\}/
   231      },
   232      createRe: function(name) {
   233        switch (name) {
   234        case "drawTable":
   235          return REs.makeRe("^", REs.single.drawTable, "$");
   236        case "html":
   237          return REs.makeRe("^", REs.single.html, "(?:", REs.single.html, ")*", "$");
   238        case "linkDefinition":
   239          return REs.makeRe("^", REs.single.linkDefinition, "$");
   240        case "listLayout":
   241          return REs.makeRe("^", REs.single.list, RE("allAttributes"), "*\\s+");
   242        case "tableCellAttributes":
   243          return REs.makeRe("^", REs.choiceRe(REs.single.tableCellAttributes,
   244                                              RE("allAttributes")), "+\\.");
   245        case "type":
   246          return REs.makeRe("^", RE("allTypes"));
   247        case "typeLayout":
   248          return REs.makeRe("^", RE("allTypes"), RE("allAttributes"),
   249                            "*\\.\\.?", "(\\s+|$)");
   250        case "attributes":
   251          return REs.makeRe("^", RE("allAttributes"), "+");
   252  
   253        case "allTypes":
   254          return REs.choiceRe(REs.single.div, REs.single.foot,
   255                              REs.single.header, REs.single.bc, REs.single.bq,
   256                              REs.single.notextile, REs.single.pre, REs.single.table,
   257                              REs.single.para);
   258  
   259        case "allAttributes":
   260          return REs.choiceRe(REs.attributes.selector, REs.attributes.css,
   261                              REs.attributes.lang, REs.attributes.align, REs.attributes.pad);
   262  
   263        default:
   264          return REs.makeRe("^", REs.single[name]);
   265        }
   266      },
   267      makeRe: function() {
   268        var pattern = "";
   269        for (var i = 0; i < arguments.length; ++i) {
   270          var arg = arguments[i];
   271          pattern += (typeof arg === "string") ? arg : arg.source;
   272        }
   273        return new RegExp(pattern);
   274      },
   275      choiceRe: function() {
   276        var parts = [arguments[0]];
   277        for (var i = 1; i < arguments.length; ++i) {
   278          parts[i * 2 - 1] = "|";
   279          parts[i * 2] = arguments[i];
   280        }
   281  
   282        parts.unshift("(?:");
   283        parts.push(")");
   284        return REs.makeRe.apply(null, parts);
   285      }
   286    };
   287  
   288    function RE(name) {
   289      return (REs.cache[name] || (REs.cache[name] = REs.createRe(name)));
   290    }
   291  
   292    var Modes = {
   293      newLayout: function(stream, state) {
   294        if (stream.match(RE("typeLayout"), false)) {
   295          state.spanningLayout = false;
   296          return (state.mode = Modes.blockType)(stream, state);
   297        }
   298        var newMode;
   299        if (!textileDisabled(state)) {
   300          if (stream.match(RE("listLayout"), false))
   301            newMode = Modes.list;
   302          else if (stream.match(RE("drawTable"), false))
   303            newMode = Modes.table;
   304          else if (stream.match(RE("linkDefinition"), false))
   305            newMode = Modes.linkDefinition;
   306          else if (stream.match(RE("definitionList")))
   307            newMode = Modes.definitionList;
   308          else if (stream.match(RE("html"), false))
   309            newMode = Modes.html;
   310        }
   311        return (state.mode = (newMode || Modes.text))(stream, state);
   312      },
   313  
   314      blockType: function(stream, state) {
   315        var match, type;
   316        state.layoutType = null;
   317  
   318        if (match = stream.match(RE("type")))
   319          type = match[0];
   320        else
   321          return (state.mode = Modes.text)(stream, state);
   322  
   323        if (match = type.match(RE("header"))) {
   324          state.layoutType = "header";
   325          state.header = parseInt(match[0][1]);
   326        } else if (type.match(RE("bq"))) {
   327          state.layoutType = "quote";
   328        } else if (type.match(RE("bc"))) {
   329          state.layoutType = "code";
   330        } else if (type.match(RE("foot"))) {
   331          state.layoutType = "footnote";
   332        } else if (type.match(RE("notextile"))) {
   333          state.layoutType = "notextile";
   334        } else if (type.match(RE("pre"))) {
   335          state.layoutType = "pre";
   336        } else if (type.match(RE("div"))) {
   337          state.layoutType = "div";
   338        } else if (type.match(RE("table"))) {
   339          state.layoutType = "table";
   340        }
   341  
   342        state.mode = Modes.attributes;
   343        return tokenStyles(state);
   344      },
   345  
   346      text: function(stream, state) {
   347        if (stream.match(RE("text"))) return tokenStyles(state);
   348  
   349        var ch = stream.next();
   350        if (ch === '"')
   351          return (state.mode = Modes.link)(stream, state);
   352        return handlePhraseModifier(stream, state, ch);
   353      },
   354  
   355      attributes: function(stream, state) {
   356        state.mode = Modes.layoutLength;
   357  
   358        if (stream.match(RE("attributes")))
   359          return tokenStylesWith(state, TOKEN_STYLES.attributes);
   360        else
   361          return tokenStyles(state);
   362      },
   363  
   364      layoutLength: function(stream, state) {
   365        if (stream.eat(".") && stream.eat("."))
   366          state.spanningLayout = true;
   367  
   368        state.mode = Modes.text;
   369        return tokenStyles(state);
   370      },
   371  
   372      list: function(stream, state) {
   373        var match = stream.match(RE("list"));
   374        state.listDepth = match[0].length;
   375        var listMod = (state.listDepth - 1) % 3;
   376        if (!listMod)
   377          state.layoutType = "list1";
   378        else if (listMod === 1)
   379          state.layoutType = "list2";
   380        else
   381          state.layoutType = "list3";
   382  
   383        state.mode = Modes.attributes;
   384        return tokenStyles(state);
   385      },
   386  
   387      link: function(stream, state) {
   388        state.mode = Modes.text;
   389        if (stream.match(RE("link"))) {
   390          stream.match(/\S+/);
   391          return tokenStylesWith(state, TOKEN_STYLES.link);
   392        }
   393        return tokenStyles(state);
   394      },
   395  
   396      linkDefinition: function(stream, state) {
   397        stream.skipToEnd();
   398        return tokenStylesWith(state, TOKEN_STYLES.linkDefinition);
   399      },
   400  
   401      definitionList: function(stream, state) {
   402        stream.match(RE("definitionList"));
   403  
   404        state.layoutType = "definitionList";
   405  
   406        if (stream.match(/\s*$/))
   407          state.spanningLayout = true;
   408        else
   409          state.mode = Modes.attributes;
   410  
   411        return tokenStyles(state);
   412      },
   413  
   414      html: function(stream, state) {
   415        stream.skipToEnd();
   416        return tokenStylesWith(state, TOKEN_STYLES.html);
   417      },
   418  
   419      table: function(stream, state) {
   420        state.layoutType = "table";
   421        return (state.mode = Modes.tableCell)(stream, state);
   422      },
   423  
   424      tableCell: function(stream, state) {
   425        if (stream.match(RE("tableHeading")))
   426          state.tableHeading = true;
   427        else
   428          stream.eat("|");
   429  
   430        state.mode = Modes.tableCellAttributes;
   431        return tokenStyles(state);
   432      },
   433  
   434      tableCellAttributes: function(stream, state) {
   435        state.mode = Modes.tableText;
   436  
   437        if (stream.match(RE("tableCellAttributes")))
   438          return tokenStylesWith(state, TOKEN_STYLES.attributes);
   439        else
   440          return tokenStyles(state);
   441      },
   442  
   443      tableText: function(stream, state) {
   444        if (stream.match(RE("tableText")))
   445          return tokenStyles(state);
   446  
   447        if (stream.peek() === "|") { // end of cell
   448          state.mode = Modes.tableCell;
   449          return tokenStyles(state);
   450        }
   451        return handlePhraseModifier(stream, state, stream.next());
   452      }
   453    };
   454  
   455    CodeMirror.defineMode("textile", function() {
   456      return {
   457        startState: function() {
   458          return { mode: Modes.newLayout };
   459        },
   460        token: function(stream, state) {
   461          if (stream.sol()) startNewLine(stream, state);
   462          return state.mode(stream, state);
   463        },
   464        blankLine: blankLine
   465      };
   466    });
   467  
   468    CodeMirror.defineMIME("text/x-textile", "textile");
   469  });