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