tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

xml.js (13282B)


      1 // CodeMirror, copyright (c) by Marijn Haverbeke and others
      2 // Distributed under an MIT license: https://codemirror.net/LICENSE
      3 
      4 (function(mod) {
      5  if (typeof exports == "object" && typeof module == "object") // CommonJS
      6    mod(require("resource://devtools/client/shared/sourceeditor/codemirror/lib/codemirror.js"));
      7  else if (typeof define == "function" && define.amd) // AMD
      8    define(["../../lib/codemirror"], mod);
      9  else // Plain browser env
     10    mod(CodeMirror);
     11 })(function(CodeMirror) {
     12 "use strict";
     13 
     14 var htmlConfig = {
     15  autoSelfClosers: {'area': true, 'base': true, 'br': true, 'col': true, 'command': true,
     16                    'embed': true, 'frame': true, 'hr': true, 'img': true, 'input': true,
     17                    'keygen': true, 'link': true, 'meta': true, 'param': true, 'source': true,
     18                    'track': true, 'wbr': true, 'menuitem': true},
     19  implicitlyClosed: {'dd': true, 'li': true, 'optgroup': true, 'option': true, 'p': true,
     20                     'rp': true, 'rt': true, 'tbody': true, 'td': true, 'tfoot': true,
     21                     'th': true, 'tr': true},
     22  contextGrabbers: {
     23    'dd': {'dd': true, 'dt': true},
     24    'dt': {'dd': true, 'dt': true},
     25    'li': {'li': true},
     26    'option': {'option': true, 'optgroup': true},
     27    'optgroup': {'optgroup': true},
     28    'p': {'address': true, 'article': true, 'aside': true, 'blockquote': true, 'dir': true,
     29          'div': true, 'dl': true, 'fieldset': true, 'footer': true, 'form': true,
     30          'h1': true, 'h2': true, 'h3': true, 'h4': true, 'h5': true, 'h6': true,
     31          'header': true, 'hgroup': true, 'hr': true, 'menu': true, 'nav': true, 'ol': true,
     32          'p': true, 'pre': true, 'section': true, 'table': true, 'ul': true},
     33    'rp': {'rp': true, 'rt': true},
     34    'rt': {'rp': true, 'rt': true},
     35    'tbody': {'tbody': true, 'tfoot': true},
     36    'td': {'td': true, 'th': true},
     37    'tfoot': {'tbody': true},
     38    'th': {'td': true, 'th': true},
     39    'thead': {'tbody': true, 'tfoot': true},
     40    'tr': {'tr': true}
     41  },
     42  doNotIndent: {"pre": true},
     43  allowUnquoted: true,
     44  allowMissing: true,
     45  caseFold: true
     46 }
     47 
     48 var xmlConfig = {
     49  autoSelfClosers: {},
     50  implicitlyClosed: {},
     51  contextGrabbers: {},
     52  doNotIndent: {},
     53  allowUnquoted: false,
     54  allowMissing: false,
     55  allowMissingTagName: false,
     56  caseFold: false
     57 }
     58 
     59 CodeMirror.defineMode("xml", function(editorConf, config_) {
     60  var indentUnit = editorConf.indentUnit
     61  var config = {}
     62  var defaults = config_.htmlMode ? htmlConfig : xmlConfig
     63  for (var prop in defaults) config[prop] = defaults[prop]
     64  for (var prop in config_) config[prop] = config_[prop]
     65 
     66  // Return variables for tokenizers
     67  var type, setStyle;
     68 
     69  function inText(stream, state) {
     70    function chain(parser) {
     71      state.tokenize = parser;
     72      return parser(stream, state);
     73    }
     74 
     75    var ch = stream.next();
     76    if (ch == "<") {
     77      if (stream.eat("!")) {
     78        if (stream.eat("[")) {
     79          if (stream.match("CDATA[")) return chain(inBlock("atom", "]]>"));
     80          else return null;
     81        } else if (stream.match("--")) {
     82          return chain(inBlock("comment", "-->"));
     83        } else if (stream.match("DOCTYPE", true, true)) {
     84          stream.eatWhile(/[\w\._\-]/);
     85          return chain(doctype(1));
     86        } else {
     87          return null;
     88        }
     89      } else if (stream.eat("?")) {
     90        stream.eatWhile(/[\w\._\-]/);
     91        state.tokenize = inBlock("meta", "?>");
     92        return "meta";
     93      } else {
     94        type = stream.eat("/") ? "closeTag" : "openTag";
     95        state.tokenize = inTag;
     96        return "tag bracket";
     97      }
     98    } else if (ch == "&") {
     99      var ok;
    100      if (stream.eat("#")) {
    101        if (stream.eat("x")) {
    102          ok = stream.eatWhile(/[a-fA-F\d]/) && stream.eat(";");
    103        } else {
    104          ok = stream.eatWhile(/[\d]/) && stream.eat(";");
    105        }
    106      } else {
    107        ok = stream.eatWhile(/[\w\.\-:]/) && stream.eat(";");
    108      }
    109      return ok ? "atom" : "error";
    110    } else {
    111      stream.eatWhile(/[^&<]/);
    112      return null;
    113    }
    114  }
    115  inText.isInText = true;
    116 
    117  function inTag(stream, state) {
    118    var ch = stream.next();
    119    if (ch == ">" || (ch == "/" && stream.eat(">"))) {
    120      state.tokenize = inText;
    121      type = ch == ">" ? "endTag" : "selfcloseTag";
    122      return "tag bracket";
    123    } else if (ch == "=") {
    124      type = "equals";
    125      return null;
    126    } else if (ch == "<") {
    127      state.tokenize = inText;
    128      state.state = baseState;
    129      state.tagName = state.tagStart = null;
    130      var next = state.tokenize(stream, state);
    131      return next ? next + " tag error" : "tag error";
    132    } else if (/[\'\"]/.test(ch)) {
    133      state.tokenize = inAttribute(ch);
    134      state.stringStartCol = stream.column();
    135      return state.tokenize(stream, state);
    136    } else {
    137      stream.match(/^[^\s\u00a0=<>\"\']*[^\s\u00a0=<>\"\'\/]/);
    138      return "word";
    139    }
    140  }
    141 
    142  function inAttribute(quote) {
    143    var closure = function(stream, state) {
    144      while (!stream.eol()) {
    145        if (stream.next() == quote) {
    146          state.tokenize = inTag;
    147          break;
    148        }
    149      }
    150      return "string";
    151    };
    152    closure.isInAttribute = true;
    153    return closure;
    154  }
    155 
    156  function inBlock(style, terminator) {
    157    return function(stream, state) {
    158      while (!stream.eol()) {
    159        if (stream.match(terminator)) {
    160          state.tokenize = inText;
    161          break;
    162        }
    163        stream.next();
    164      }
    165      return style;
    166    }
    167  }
    168 
    169  function doctype(depth) {
    170    return function(stream, state) {
    171      var ch;
    172      while ((ch = stream.next()) != null) {
    173        if (ch == "<") {
    174          state.tokenize = doctype(depth + 1);
    175          return state.tokenize(stream, state);
    176        } else if (ch == ">") {
    177          if (depth == 1) {
    178            state.tokenize = inText;
    179            break;
    180          } else {
    181            state.tokenize = doctype(depth - 1);
    182            return state.tokenize(stream, state);
    183          }
    184        }
    185      }
    186      return "meta";
    187    };
    188  }
    189 
    190  function Context(state, tagName, startOfLine) {
    191    this.prev = state.context;
    192    this.tagName = tagName;
    193    this.indent = state.indented;
    194    this.startOfLine = startOfLine;
    195    if (config.doNotIndent.hasOwnProperty(tagName) || (state.context && state.context.noIndent))
    196      this.noIndent = true;
    197  }
    198  function popContext(state) {
    199    if (state.context) state.context = state.context.prev;
    200  }
    201  function maybePopContext(state, nextTagName) {
    202    var parentTagName;
    203    while (true) {
    204      if (!state.context) {
    205        return;
    206      }
    207      parentTagName = state.context.tagName;
    208      if (!config.contextGrabbers.hasOwnProperty(parentTagName) ||
    209          !config.contextGrabbers[parentTagName].hasOwnProperty(nextTagName)) {
    210        return;
    211      }
    212      popContext(state);
    213    }
    214  }
    215 
    216  function baseState(type, stream, state) {
    217    if (type == "openTag") {
    218      state.tagStart = stream.column();
    219      return tagNameState;
    220    } else if (type == "closeTag") {
    221      return closeTagNameState;
    222    } else {
    223      return baseState;
    224    }
    225  }
    226  function tagNameState(type, stream, state) {
    227    if (type == "word") {
    228      state.tagName = stream.current();
    229      setStyle = "tag";
    230      return attrState;
    231    } else if (config.allowMissingTagName && type == "endTag") {
    232      setStyle = "tag bracket";
    233      return attrState(type, stream, state);
    234    } else {
    235      setStyle = "error";
    236      return tagNameState;
    237    }
    238  }
    239  function closeTagNameState(type, stream, state) {
    240    if (type == "word") {
    241      var tagName = stream.current();
    242      if (state.context && state.context.tagName != tagName &&
    243          config.implicitlyClosed.hasOwnProperty(state.context.tagName))
    244        popContext(state);
    245      if ((state.context && state.context.tagName == tagName) || config.matchClosing === false) {
    246        setStyle = "tag";
    247        return closeState;
    248      } else {
    249        setStyle = "tag error";
    250        return closeStateErr;
    251      }
    252    } else if (config.allowMissingTagName && type == "endTag") {
    253      setStyle = "tag bracket";
    254      return closeState(type, stream, state);
    255    } else {
    256      setStyle = "error";
    257      return closeStateErr;
    258    }
    259  }
    260 
    261  function closeState(type, _stream, state) {
    262    if (type != "endTag") {
    263      setStyle = "error";
    264      return closeState;
    265    }
    266    popContext(state);
    267    return baseState;
    268  }
    269  function closeStateErr(type, stream, state) {
    270    setStyle = "error";
    271    return closeState(type, stream, state);
    272  }
    273 
    274  function attrState(type, _stream, state) {
    275    if (type == "word") {
    276      setStyle = "attribute";
    277      return attrEqState;
    278    } else if (type == "endTag" || type == "selfcloseTag") {
    279      var tagName = state.tagName, tagStart = state.tagStart;
    280      state.tagName = state.tagStart = null;
    281      if (type == "selfcloseTag" ||
    282          config.autoSelfClosers.hasOwnProperty(tagName)) {
    283        maybePopContext(state, tagName);
    284      } else {
    285        maybePopContext(state, tagName);
    286        state.context = new Context(state, tagName, tagStart == state.indented);
    287      }
    288      return baseState;
    289    }
    290    setStyle = "error";
    291    return attrState;
    292  }
    293  function attrEqState(type, stream, state) {
    294    if (type == "equals") return attrValueState;
    295    if (!config.allowMissing) setStyle = "error";
    296    return attrState(type, stream, state);
    297  }
    298  function attrValueState(type, stream, state) {
    299    if (type == "string") return attrContinuedState;
    300    if (type == "word" && config.allowUnquoted) {setStyle = "string"; return attrState;}
    301    setStyle = "error";
    302    return attrState(type, stream, state);
    303  }
    304  function attrContinuedState(type, stream, state) {
    305    if (type == "string") return attrContinuedState;
    306    return attrState(type, stream, state);
    307  }
    308 
    309  return {
    310    startState: function(baseIndent) {
    311      var state = {tokenize: inText,
    312                   state: baseState,
    313                   indented: baseIndent || 0,
    314                   tagName: null, tagStart: null,
    315                   context: null}
    316      if (baseIndent != null) state.baseIndent = baseIndent
    317      return state
    318    },
    319 
    320    token: function(stream, state) {
    321      if (!state.tagName && stream.sol())
    322        state.indented = stream.indentation();
    323 
    324      if (stream.eatSpace()) return null;
    325      type = null;
    326      var style = state.tokenize(stream, state);
    327      if ((style || type) && style != "comment") {
    328        setStyle = null;
    329        state.state = state.state(type || style, stream, state);
    330        if (setStyle)
    331          style = setStyle == "error" ? style + " error" : setStyle;
    332      }
    333      return style;
    334    },
    335 
    336    indent: function(state, textAfter, fullLine) {
    337      var context = state.context;
    338      // Indent multi-line strings (e.g. css).
    339      if (state.tokenize.isInAttribute) {
    340        if (state.tagStart == state.indented)
    341          return state.stringStartCol + 1;
    342        else
    343          return state.indented + indentUnit;
    344      }
    345      if (context && context.noIndent) return CodeMirror.Pass;
    346      if (state.tokenize != inTag && state.tokenize != inText)
    347        return fullLine ? fullLine.match(/^(\s*)/)[0].length : 0;
    348      // Indent the starts of attribute names.
    349      if (state.tagName) {
    350        if (config.multilineTagIndentPastTag !== false)
    351          return state.tagStart + state.tagName.length + 2;
    352        else
    353          return state.tagStart + indentUnit * (config.multilineTagIndentFactor || 1);
    354      }
    355      if (config.alignCDATA && /<!\[CDATA\[/.test(textAfter)) return 0;
    356      var tagAfter = textAfter && /^<(\/)?([\w_:\.-]*)/.exec(textAfter);
    357      if (tagAfter && tagAfter[1]) { // Closing tag spotted
    358        while (context) {
    359          if (context.tagName == tagAfter[2]) {
    360            context = context.prev;
    361            break;
    362          } else if (config.implicitlyClosed.hasOwnProperty(context.tagName)) {
    363            context = context.prev;
    364          } else {
    365            break;
    366          }
    367        }
    368      } else if (tagAfter) { // Opening tag spotted
    369        while (context) {
    370          var grabbers = config.contextGrabbers[context.tagName];
    371          if (grabbers && grabbers.hasOwnProperty(tagAfter[2]))
    372            context = context.prev;
    373          else
    374            break;
    375        }
    376      }
    377      while (context && context.prev && !context.startOfLine)
    378        context = context.prev;
    379      if (context) return context.indent + indentUnit;
    380      else return state.baseIndent || 0;
    381    },
    382 
    383    electricInput: /<\/[\s\w:]+>$/,
    384    blockCommentStart: "<!--",
    385    blockCommentEnd: "-->",
    386 
    387    configuration: config.htmlMode ? "html" : "xml",
    388    helperType: config.htmlMode ? "html" : "xml",
    389 
    390    skipAttribute: function(state) {
    391      if (state.state == attrValueState)
    392        state.state = attrState
    393    },
    394 
    395    xmlCurrentTag: function(state) {
    396      return state.tagName ? {name: state.tagName, close: state.type == "closeTag"} : null
    397    },
    398 
    399    xmlCurrentContext: function(state) {
    400      var context = []
    401      for (var cx = state.context; cx; cx = cx.prev)
    402        if (cx.tagName) context.push(cx.tagName)
    403      return context.reverse()
    404    }
    405  };
    406 });
    407 
    408 CodeMirror.defineMIME("text/xml", "xml");
    409 CodeMirror.defineMIME("application/xml", "xml");
    410 if (!CodeMirror.mimeModes.hasOwnProperty("text/html"))
    411  CodeMirror.defineMIME("text/html", {name: "xml", htmlMode: true});
    412 
    413 });