mirror of
				https://github.com/zadam/trilium.git
				synced 2025-11-03 21:19:01 +01:00 
			
		
		
		
	
		
			
				
	
	
		
			470 lines
		
	
	
		
			14 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			470 lines
		
	
	
		
			14 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
// CodeMirror, copyright (c) by Marijn Haverbeke and others
 | 
						|
// Distributed under an MIT license: https://codemirror.net/LICENSE
 | 
						|
 | 
						|
(function(mod) {
 | 
						|
  if (typeof exports == "object" && typeof module == "object") { // CommonJS
 | 
						|
    mod(require("../../lib/codemirror"));
 | 
						|
  } else if (typeof define == "function" && define.amd) { // AMD
 | 
						|
    define(["../../lib/codemirror"], mod);
 | 
						|
  } else { // Plain browser env
 | 
						|
    mod(CodeMirror);
 | 
						|
  }
 | 
						|
})(function(CodeMirror) {
 | 
						|
  "use strict";
 | 
						|
 | 
						|
  var TOKEN_STYLES = {
 | 
						|
    addition: "positive",
 | 
						|
    attributes: "attribute",
 | 
						|
    bold: "strong",
 | 
						|
    cite: "keyword",
 | 
						|
    code: "atom",
 | 
						|
    definitionList: "number",
 | 
						|
    deletion: "negative",
 | 
						|
    div: "punctuation",
 | 
						|
    em: "em",
 | 
						|
    footnote: "variable",
 | 
						|
    footCite: "qualifier",
 | 
						|
    header: "header",
 | 
						|
    html: "comment",
 | 
						|
    image: "string",
 | 
						|
    italic: "em",
 | 
						|
    link: "link",
 | 
						|
    linkDefinition: "link",
 | 
						|
    list1: "variable-2",
 | 
						|
    list2: "variable-3",
 | 
						|
    list3: "keyword",
 | 
						|
    notextile: "string-2",
 | 
						|
    pre: "operator",
 | 
						|
    p: "property",
 | 
						|
    quote: "bracket",
 | 
						|
    span: "quote",
 | 
						|
    specialChar: "tag",
 | 
						|
    strong: "strong",
 | 
						|
    sub: "builtin",
 | 
						|
    sup: "builtin",
 | 
						|
    table: "variable-3",
 | 
						|
    tableHeading: "operator"
 | 
						|
  };
 | 
						|
 | 
						|
  function startNewLine(stream, state) {
 | 
						|
    state.mode = Modes.newLayout;
 | 
						|
    state.tableHeading = false;
 | 
						|
 | 
						|
    if (state.layoutType === "definitionList" && state.spanningLayout &&
 | 
						|
        stream.match(RE("definitionListEnd"), false))
 | 
						|
      state.spanningLayout = false;
 | 
						|
  }
 | 
						|
 | 
						|
  function handlePhraseModifier(stream, state, ch) {
 | 
						|
    if (ch === "_") {
 | 
						|
      if (stream.eat("_"))
 | 
						|
        return togglePhraseModifier(stream, state, "italic", /__/, 2);
 | 
						|
      else
 | 
						|
        return togglePhraseModifier(stream, state, "em", /_/, 1);
 | 
						|
    }
 | 
						|
 | 
						|
    if (ch === "*") {
 | 
						|
      if (stream.eat("*")) {
 | 
						|
        return togglePhraseModifier(stream, state, "bold", /\*\*/, 2);
 | 
						|
      }
 | 
						|
      return togglePhraseModifier(stream, state, "strong", /\*/, 1);
 | 
						|
    }
 | 
						|
 | 
						|
    if (ch === "[") {
 | 
						|
      if (stream.match(/\d+\]/)) state.footCite = true;
 | 
						|
      return tokenStyles(state);
 | 
						|
    }
 | 
						|
 | 
						|
    if (ch === "(") {
 | 
						|
      var spec = stream.match(/^(r|tm|c)\)/);
 | 
						|
      if (spec)
 | 
						|
        return tokenStylesWith(state, TOKEN_STYLES.specialChar);
 | 
						|
    }
 | 
						|
 | 
						|
    if (ch === "<" && stream.match(/(\w+)[^>]+>[^<]+<\/\1>/))
 | 
						|
      return tokenStylesWith(state, TOKEN_STYLES.html);
 | 
						|
 | 
						|
    if (ch === "?" && stream.eat("?"))
 | 
						|
      return togglePhraseModifier(stream, state, "cite", /\?\?/, 2);
 | 
						|
 | 
						|
    if (ch === "=" && stream.eat("="))
 | 
						|
      return togglePhraseModifier(stream, state, "notextile", /==/, 2);
 | 
						|
 | 
						|
    if (ch === "-" && !stream.eat("-"))
 | 
						|
      return togglePhraseModifier(stream, state, "deletion", /-/, 1);
 | 
						|
 | 
						|
    if (ch === "+")
 | 
						|
      return togglePhraseModifier(stream, state, "addition", /\+/, 1);
 | 
						|
 | 
						|
    if (ch === "~")
 | 
						|
      return togglePhraseModifier(stream, state, "sub", /~/, 1);
 | 
						|
 | 
						|
    if (ch === "^")
 | 
						|
      return togglePhraseModifier(stream, state, "sup", /\^/, 1);
 | 
						|
 | 
						|
    if (ch === "%")
 | 
						|
      return togglePhraseModifier(stream, state, "span", /%/, 1);
 | 
						|
 | 
						|
    if (ch === "@")
 | 
						|
      return togglePhraseModifier(stream, state, "code", /@/, 1);
 | 
						|
 | 
						|
    if (ch === "!") {
 | 
						|
      var type = togglePhraseModifier(stream, state, "image", /(?:\([^\)]+\))?!/, 1);
 | 
						|
      stream.match(/^:\S+/); // optional Url portion
 | 
						|
      return type;
 | 
						|
    }
 | 
						|
    return tokenStyles(state);
 | 
						|
  }
 | 
						|
 | 
						|
  function togglePhraseModifier(stream, state, phraseModifier, closeRE, openSize) {
 | 
						|
    var charBefore = stream.pos > openSize ? stream.string.charAt(stream.pos - openSize - 1) : null;
 | 
						|
    var charAfter = stream.peek();
 | 
						|
    if (state[phraseModifier]) {
 | 
						|
      if ((!charAfter || /\W/.test(charAfter)) && charBefore && /\S/.test(charBefore)) {
 | 
						|
        var type = tokenStyles(state);
 | 
						|
        state[phraseModifier] = false;
 | 
						|
        return type;
 | 
						|
      }
 | 
						|
    } else if ((!charBefore || /\W/.test(charBefore)) && charAfter && /\S/.test(charAfter) &&
 | 
						|
               stream.match(new RegExp("^.*\\S" + closeRE.source + "(?:\\W|$)"), false)) {
 | 
						|
      state[phraseModifier] = true;
 | 
						|
      state.mode = Modes.attributes;
 | 
						|
    }
 | 
						|
    return tokenStyles(state);
 | 
						|
  };
 | 
						|
 | 
						|
  function tokenStyles(state) {
 | 
						|
    var disabled = textileDisabled(state);
 | 
						|
    if (disabled) return disabled;
 | 
						|
 | 
						|
    var styles = [];
 | 
						|
    if (state.layoutType) styles.push(TOKEN_STYLES[state.layoutType]);
 | 
						|
 | 
						|
    styles = styles.concat(activeStyles(
 | 
						|
      state, "addition", "bold", "cite", "code", "deletion", "em", "footCite",
 | 
						|
      "image", "italic", "link", "span", "strong", "sub", "sup", "table", "tableHeading"));
 | 
						|
 | 
						|
    if (state.layoutType === "header")
 | 
						|
      styles.push(TOKEN_STYLES.header + "-" + state.header);
 | 
						|
 | 
						|
    return styles.length ? styles.join(" ") : null;
 | 
						|
  }
 | 
						|
 | 
						|
  function textileDisabled(state) {
 | 
						|
    var type = state.layoutType;
 | 
						|
 | 
						|
    switch(type) {
 | 
						|
    case "notextile":
 | 
						|
    case "code":
 | 
						|
    case "pre":
 | 
						|
      return TOKEN_STYLES[type];
 | 
						|
    default:
 | 
						|
      if (state.notextile)
 | 
						|
        return TOKEN_STYLES.notextile + (type ? (" " + TOKEN_STYLES[type]) : "");
 | 
						|
      return null;
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  function tokenStylesWith(state, extraStyles) {
 | 
						|
    var disabled = textileDisabled(state);
 | 
						|
    if (disabled) return disabled;
 | 
						|
 | 
						|
    var type = tokenStyles(state);
 | 
						|
    if (extraStyles)
 | 
						|
      return type ? (type + " " + extraStyles) : extraStyles;
 | 
						|
    else
 | 
						|
      return type;
 | 
						|
  }
 | 
						|
 | 
						|
  function activeStyles(state) {
 | 
						|
    var styles = [];
 | 
						|
    for (var i = 1; i < arguments.length; ++i) {
 | 
						|
      if (state[arguments[i]])
 | 
						|
        styles.push(TOKEN_STYLES[arguments[i]]);
 | 
						|
    }
 | 
						|
    return styles;
 | 
						|
  }
 | 
						|
 | 
						|
  function blankLine(state) {
 | 
						|
    var spanningLayout = state.spanningLayout, type = state.layoutType;
 | 
						|
 | 
						|
    for (var key in state) if (state.hasOwnProperty(key))
 | 
						|
      delete state[key];
 | 
						|
 | 
						|
    state.mode = Modes.newLayout;
 | 
						|
    if (spanningLayout) {
 | 
						|
      state.layoutType = type;
 | 
						|
      state.spanningLayout = true;
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  var REs = {
 | 
						|
    cache: {},
 | 
						|
    single: {
 | 
						|
      bc: "bc",
 | 
						|
      bq: "bq",
 | 
						|
      definitionList: /- .*?:=+/,
 | 
						|
      definitionListEnd: /.*=:\s*$/,
 | 
						|
      div: "div",
 | 
						|
      drawTable: /\|.*\|/,
 | 
						|
      foot: /fn\d+/,
 | 
						|
      header: /h[1-6]/,
 | 
						|
      html: /\s*<(?:\/)?(\w+)(?:[^>]+)?>(?:[^<]+<\/\1>)?/,
 | 
						|
      link: /[^"]+":\S/,
 | 
						|
      linkDefinition: /\[[^\s\]]+\]\S+/,
 | 
						|
      list: /(?:#+|\*+)/,
 | 
						|
      notextile: "notextile",
 | 
						|
      para: "p",
 | 
						|
      pre: "pre",
 | 
						|
      table: "table",
 | 
						|
      tableCellAttributes: /[\/\\]\d+/,
 | 
						|
      tableHeading: /\|_\./,
 | 
						|
      tableText: /[^"_\*\[\(\?\+~\^%@|-]+/,
 | 
						|
      text: /[^!"_=\*\[\(<\?\+~\^%@-]+/
 | 
						|
    },
 | 
						|
    attributes: {
 | 
						|
      align: /(?:<>|<|>|=)/,
 | 
						|
      selector: /\([^\(][^\)]+\)/,
 | 
						|
      lang: /\[[^\[\]]+\]/,
 | 
						|
      pad: /(?:\(+|\)+){1,2}/,
 | 
						|
      css: /\{[^\}]+\}/
 | 
						|
    },
 | 
						|
    createRe: function(name) {
 | 
						|
      switch (name) {
 | 
						|
      case "drawTable":
 | 
						|
        return REs.makeRe("^", REs.single.drawTable, "$");
 | 
						|
      case "html":
 | 
						|
        return REs.makeRe("^", REs.single.html, "(?:", REs.single.html, ")*", "$");
 | 
						|
      case "linkDefinition":
 | 
						|
        return REs.makeRe("^", REs.single.linkDefinition, "$");
 | 
						|
      case "listLayout":
 | 
						|
        return REs.makeRe("^", REs.single.list, RE("allAttributes"), "*\\s+");
 | 
						|
      case "tableCellAttributes":
 | 
						|
        return REs.makeRe("^", REs.choiceRe(REs.single.tableCellAttributes,
 | 
						|
                                            RE("allAttributes")), "+\\.");
 | 
						|
      case "type":
 | 
						|
        return REs.makeRe("^", RE("allTypes"));
 | 
						|
      case "typeLayout":
 | 
						|
        return REs.makeRe("^", RE("allTypes"), RE("allAttributes"),
 | 
						|
                          "*\\.\\.?", "(\\s+|$)");
 | 
						|
      case "attributes":
 | 
						|
        return REs.makeRe("^", RE("allAttributes"), "+");
 | 
						|
 | 
						|
      case "allTypes":
 | 
						|
        return REs.choiceRe(REs.single.div, REs.single.foot,
 | 
						|
                            REs.single.header, REs.single.bc, REs.single.bq,
 | 
						|
                            REs.single.notextile, REs.single.pre, REs.single.table,
 | 
						|
                            REs.single.para);
 | 
						|
 | 
						|
      case "allAttributes":
 | 
						|
        return REs.choiceRe(REs.attributes.selector, REs.attributes.css,
 | 
						|
                            REs.attributes.lang, REs.attributes.align, REs.attributes.pad);
 | 
						|
 | 
						|
      default:
 | 
						|
        return REs.makeRe("^", REs.single[name]);
 | 
						|
      }
 | 
						|
    },
 | 
						|
    makeRe: function() {
 | 
						|
      var pattern = "";
 | 
						|
      for (var i = 0; i < arguments.length; ++i) {
 | 
						|
        var arg = arguments[i];
 | 
						|
        pattern += (typeof arg === "string") ? arg : arg.source;
 | 
						|
      }
 | 
						|
      return new RegExp(pattern);
 | 
						|
    },
 | 
						|
    choiceRe: function() {
 | 
						|
      var parts = [arguments[0]];
 | 
						|
      for (var i = 1; i < arguments.length; ++i) {
 | 
						|
        parts[i * 2 - 1] = "|";
 | 
						|
        parts[i * 2] = arguments[i];
 | 
						|
      }
 | 
						|
 | 
						|
      parts.unshift("(?:");
 | 
						|
      parts.push(")");
 | 
						|
      return REs.makeRe.apply(null, parts);
 | 
						|
    }
 | 
						|
  };
 | 
						|
 | 
						|
  function RE(name) {
 | 
						|
    return (REs.cache[name] || (REs.cache[name] = REs.createRe(name)));
 | 
						|
  }
 | 
						|
 | 
						|
  var Modes = {
 | 
						|
    newLayout: function(stream, state) {
 | 
						|
      if (stream.match(RE("typeLayout"), false)) {
 | 
						|
        state.spanningLayout = false;
 | 
						|
        return (state.mode = Modes.blockType)(stream, state);
 | 
						|
      }
 | 
						|
      var newMode;
 | 
						|
      if (!textileDisabled(state)) {
 | 
						|
        if (stream.match(RE("listLayout"), false))
 | 
						|
          newMode = Modes.list;
 | 
						|
        else if (stream.match(RE("drawTable"), false))
 | 
						|
          newMode = Modes.table;
 | 
						|
        else if (stream.match(RE("linkDefinition"), false))
 | 
						|
          newMode = Modes.linkDefinition;
 | 
						|
        else if (stream.match(RE("definitionList")))
 | 
						|
          newMode = Modes.definitionList;
 | 
						|
        else if (stream.match(RE("html"), false))
 | 
						|
          newMode = Modes.html;
 | 
						|
      }
 | 
						|
      return (state.mode = (newMode || Modes.text))(stream, state);
 | 
						|
    },
 | 
						|
 | 
						|
    blockType: function(stream, state) {
 | 
						|
      var match, type;
 | 
						|
      state.layoutType = null;
 | 
						|
 | 
						|
      if (match = stream.match(RE("type")))
 | 
						|
        type = match[0];
 | 
						|
      else
 | 
						|
        return (state.mode = Modes.text)(stream, state);
 | 
						|
 | 
						|
      if (match = type.match(RE("header"))) {
 | 
						|
        state.layoutType = "header";
 | 
						|
        state.header = parseInt(match[0][1]);
 | 
						|
      } else if (type.match(RE("bq"))) {
 | 
						|
        state.layoutType = "quote";
 | 
						|
      } else if (type.match(RE("bc"))) {
 | 
						|
        state.layoutType = "code";
 | 
						|
      } else if (type.match(RE("foot"))) {
 | 
						|
        state.layoutType = "footnote";
 | 
						|
      } else if (type.match(RE("notextile"))) {
 | 
						|
        state.layoutType = "notextile";
 | 
						|
      } else if (type.match(RE("pre"))) {
 | 
						|
        state.layoutType = "pre";
 | 
						|
      } else if (type.match(RE("div"))) {
 | 
						|
        state.layoutType = "div";
 | 
						|
      } else if (type.match(RE("table"))) {
 | 
						|
        state.layoutType = "table";
 | 
						|
      }
 | 
						|
 | 
						|
      state.mode = Modes.attributes;
 | 
						|
      return tokenStyles(state);
 | 
						|
    },
 | 
						|
 | 
						|
    text: function(stream, state) {
 | 
						|
      if (stream.match(RE("text"))) return tokenStyles(state);
 | 
						|
 | 
						|
      var ch = stream.next();
 | 
						|
      if (ch === '"')
 | 
						|
        return (state.mode = Modes.link)(stream, state);
 | 
						|
      return handlePhraseModifier(stream, state, ch);
 | 
						|
    },
 | 
						|
 | 
						|
    attributes: function(stream, state) {
 | 
						|
      state.mode = Modes.layoutLength;
 | 
						|
 | 
						|
      if (stream.match(RE("attributes")))
 | 
						|
        return tokenStylesWith(state, TOKEN_STYLES.attributes);
 | 
						|
      else
 | 
						|
        return tokenStyles(state);
 | 
						|
    },
 | 
						|
 | 
						|
    layoutLength: function(stream, state) {
 | 
						|
      if (stream.eat(".") && stream.eat("."))
 | 
						|
        state.spanningLayout = true;
 | 
						|
 | 
						|
      state.mode = Modes.text;
 | 
						|
      return tokenStyles(state);
 | 
						|
    },
 | 
						|
 | 
						|
    list: function(stream, state) {
 | 
						|
      var match = stream.match(RE("list"));
 | 
						|
      state.listDepth = match[0].length;
 | 
						|
      var listMod = (state.listDepth - 1) % 3;
 | 
						|
      if (!listMod)
 | 
						|
        state.layoutType = "list1";
 | 
						|
      else if (listMod === 1)
 | 
						|
        state.layoutType = "list2";
 | 
						|
      else
 | 
						|
        state.layoutType = "list3";
 | 
						|
 | 
						|
      state.mode = Modes.attributes;
 | 
						|
      return tokenStyles(state);
 | 
						|
    },
 | 
						|
 | 
						|
    link: function(stream, state) {
 | 
						|
      state.mode = Modes.text;
 | 
						|
      if (stream.match(RE("link"))) {
 | 
						|
        stream.match(/\S+/);
 | 
						|
        return tokenStylesWith(state, TOKEN_STYLES.link);
 | 
						|
      }
 | 
						|
      return tokenStyles(state);
 | 
						|
    },
 | 
						|
 | 
						|
    linkDefinition: function(stream, state) {
 | 
						|
      stream.skipToEnd();
 | 
						|
      return tokenStylesWith(state, TOKEN_STYLES.linkDefinition);
 | 
						|
    },
 | 
						|
 | 
						|
    definitionList: function(stream, state) {
 | 
						|
      stream.match(RE("definitionList"));
 | 
						|
 | 
						|
      state.layoutType = "definitionList";
 | 
						|
 | 
						|
      if (stream.match(/\s*$/))
 | 
						|
        state.spanningLayout = true;
 | 
						|
      else
 | 
						|
        state.mode = Modes.attributes;
 | 
						|
 | 
						|
      return tokenStyles(state);
 | 
						|
    },
 | 
						|
 | 
						|
    html: function(stream, state) {
 | 
						|
      stream.skipToEnd();
 | 
						|
      return tokenStylesWith(state, TOKEN_STYLES.html);
 | 
						|
    },
 | 
						|
 | 
						|
    table: function(stream, state) {
 | 
						|
      state.layoutType = "table";
 | 
						|
      return (state.mode = Modes.tableCell)(stream, state);
 | 
						|
    },
 | 
						|
 | 
						|
    tableCell: function(stream, state) {
 | 
						|
      if (stream.match(RE("tableHeading")))
 | 
						|
        state.tableHeading = true;
 | 
						|
      else
 | 
						|
        stream.eat("|");
 | 
						|
 | 
						|
      state.mode = Modes.tableCellAttributes;
 | 
						|
      return tokenStyles(state);
 | 
						|
    },
 | 
						|
 | 
						|
    tableCellAttributes: function(stream, state) {
 | 
						|
      state.mode = Modes.tableText;
 | 
						|
 | 
						|
      if (stream.match(RE("tableCellAttributes")))
 | 
						|
        return tokenStylesWith(state, TOKEN_STYLES.attributes);
 | 
						|
      else
 | 
						|
        return tokenStyles(state);
 | 
						|
    },
 | 
						|
 | 
						|
    tableText: function(stream, state) {
 | 
						|
      if (stream.match(RE("tableText")))
 | 
						|
        return tokenStyles(state);
 | 
						|
 | 
						|
      if (stream.peek() === "|") { // end of cell
 | 
						|
        state.mode = Modes.tableCell;
 | 
						|
        return tokenStyles(state);
 | 
						|
      }
 | 
						|
      return handlePhraseModifier(stream, state, stream.next());
 | 
						|
    }
 | 
						|
  };
 | 
						|
 | 
						|
  CodeMirror.defineMode("textile", function() {
 | 
						|
    return {
 | 
						|
      startState: function() {
 | 
						|
        return { mode: Modes.newLayout };
 | 
						|
      },
 | 
						|
      token: function(stream, state) {
 | 
						|
        if (stream.sol()) startNewLine(stream, state);
 | 
						|
        return state.mode(stream, state);
 | 
						|
      },
 | 
						|
      blankLine: blankLine
 | 
						|
    };
 | 
						|
  });
 | 
						|
 | 
						|
  CodeMirror.defineMIME("text/x-textile", "textile");
 | 
						|
});
 |