dew/src/main/resources/org/apidesign/bck2brwsr/dew/js/codemirror/mode/xml.js
author Jaroslav Tulach <jaroslav.tulach@apidesign.org>
Wed, 23 Jan 2013 13:18:46 +0100
branchdew
changeset 544 08ffdc3938e7
parent 460 launcher/src/main/resources/org/apidesign/bck2brwsr/dew/js/codemirror/mode/xml.js@c0f1788183dd
permissions -rw-r--r--
Moving Development Environment for Web to own project
     1 CodeMirror.defineMode("xml", function(config, parserConfig) {
     2   var indentUnit = config.indentUnit;
     3   var Kludges = parserConfig.htmlMode ? {
     4     autoSelfClosers: {'area': true, 'base': true, 'br': true, 'col': true, 'command': true,
     5                       'embed': true, 'frame': true, 'hr': true, 'img': true, 'input': true,
     6                       'keygen': true, 'link': true, 'meta': true, 'param': true, 'source': true,
     7                       'track': true, 'wbr': true},
     8     implicitlyClosed: {'dd': true, 'li': true, 'optgroup': true, 'option': true, 'p': true,
     9                        'rp': true, 'rt': true, 'tbody': true, 'td': true, 'tfoot': true,
    10                        'th': true, 'tr': true},
    11     contextGrabbers: {
    12       'dd': {'dd': true, 'dt': true},
    13       'dt': {'dd': true, 'dt': true},
    14       'li': {'li': true},
    15       'option': {'option': true, 'optgroup': true},
    16       'optgroup': {'optgroup': true},
    17       'p': {'address': true, 'article': true, 'aside': true, 'blockquote': true, 'dir': true,
    18             'div': true, 'dl': true, 'fieldset': true, 'footer': true, 'form': true,
    19             'h1': true, 'h2': true, 'h3': true, 'h4': true, 'h5': true, 'h6': true,
    20             'header': true, 'hgroup': true, 'hr': true, 'menu': true, 'nav': true, 'ol': true,
    21             'p': true, 'pre': true, 'section': true, 'table': true, 'ul': true},
    22       'rp': {'rp': true, 'rt': true},
    23       'rt': {'rp': true, 'rt': true},
    24       'tbody': {'tbody': true, 'tfoot': true},
    25       'td': {'td': true, 'th': true},
    26       'tfoot': {'tbody': true},
    27       'th': {'td': true, 'th': true},
    28       'thead': {'tbody': true, 'tfoot': true},
    29       'tr': {'tr': true}
    30     },
    31     doNotIndent: {"pre": true},
    32     allowUnquoted: true,
    33     allowMissing: true
    34   } : {
    35     autoSelfClosers: {},
    36     implicitlyClosed: {},
    37     contextGrabbers: {},
    38     doNotIndent: {},
    39     allowUnquoted: false,
    40     allowMissing: false
    41   };
    42   var alignCDATA = parserConfig.alignCDATA;
    43 
    44   // Return variables for tokenizers
    45   var tagName, type;
    46 
    47   function inText(stream, state) {
    48     function chain(parser) {
    49       state.tokenize = parser;
    50       return parser(stream, state);
    51     }
    52 
    53     var ch = stream.next();
    54     if (ch == "<") {
    55       if (stream.eat("!")) {
    56         if (stream.eat("[")) {
    57           if (stream.match("CDATA[")) return chain(inBlock("atom", "]]>"));
    58           else return null;
    59         }
    60         else if (stream.match("--")) return chain(inBlock("comment", "-->"));
    61         else if (stream.match("DOCTYPE", true, true)) {
    62           stream.eatWhile(/[\w\._\-]/);
    63           return chain(doctype(1));
    64         }
    65         else return null;
    66       }
    67       else if (stream.eat("?")) {
    68         stream.eatWhile(/[\w\._\-]/);
    69         state.tokenize = inBlock("meta", "?>");
    70         return "meta";
    71       }
    72       else {
    73         var isClose = stream.eat("/");
    74         tagName = "";
    75         var c;
    76         while ((c = stream.eat(/[^\s\u00a0=<>\"\'\/?]/))) tagName += c;
    77         if (!tagName) return "error";
    78         type = isClose ? "closeTag" : "openTag";
    79         state.tokenize = inTag;
    80         return "tag";
    81       }
    82     }
    83     else if (ch == "&") {
    84       var ok;
    85       if (stream.eat("#")) {
    86         if (stream.eat("x")) {
    87           ok = stream.eatWhile(/[a-fA-F\d]/) && stream.eat(";");          
    88         } else {
    89           ok = stream.eatWhile(/[\d]/) && stream.eat(";");
    90         }
    91       } else {
    92         ok = stream.eatWhile(/[\w\.\-:]/) && stream.eat(";");
    93       }
    94       return ok ? "atom" : "error";
    95     }
    96     else {
    97       stream.eatWhile(/[^&<]/);
    98       return null;
    99     }
   100   }
   101 
   102   function inTag(stream, state) {
   103     var ch = stream.next();
   104     if (ch == ">" || (ch == "/" && stream.eat(">"))) {
   105       state.tokenize = inText;
   106       type = ch == ">" ? "endTag" : "selfcloseTag";
   107       return "tag";
   108     }
   109     else if (ch == "=") {
   110       type = "equals";
   111       return null;
   112     }
   113     else if (/[\'\"]/.test(ch)) {
   114       state.tokenize = inAttribute(ch);
   115       return state.tokenize(stream, state);
   116     }
   117     else {
   118       stream.eatWhile(/[^\s\u00a0=<>\"\']/);
   119       return "word";
   120     }
   121   }
   122 
   123   function inAttribute(quote) {
   124     return function(stream, state) {
   125       while (!stream.eol()) {
   126         if (stream.next() == quote) {
   127           state.tokenize = inTag;
   128           break;
   129         }
   130       }
   131       return "string";
   132     };
   133   }
   134 
   135   function inBlock(style, terminator) {
   136     return function(stream, state) {
   137       while (!stream.eol()) {
   138         if (stream.match(terminator)) {
   139           state.tokenize = inText;
   140           break;
   141         }
   142         stream.next();
   143       }
   144       return style;
   145     };
   146   }
   147   function doctype(depth) {
   148     return function(stream, state) {
   149       var ch;
   150       while ((ch = stream.next()) != null) {
   151         if (ch == "<") {
   152           state.tokenize = doctype(depth + 1);
   153           return state.tokenize(stream, state);
   154         } else if (ch == ">") {
   155           if (depth == 1) {
   156             state.tokenize = inText;
   157             break;
   158           } else {
   159             state.tokenize = doctype(depth - 1);
   160             return state.tokenize(stream, state);
   161           }
   162         }
   163       }
   164       return "meta";
   165     };
   166   }
   167 
   168   var curState, setStyle;
   169   function pass() {
   170     for (var i = arguments.length - 1; i >= 0; i--) curState.cc.push(arguments[i]);
   171   }
   172   function cont() {
   173     pass.apply(null, arguments);
   174     return true;
   175   }
   176 
   177   function pushContext(tagName, startOfLine) {
   178     var noIndent = Kludges.doNotIndent.hasOwnProperty(tagName) || (curState.context && curState.context.noIndent);
   179     curState.context = {
   180       prev: curState.context,
   181       tagName: tagName,
   182       indent: curState.indented,
   183       startOfLine: startOfLine,
   184       noIndent: noIndent
   185     };
   186   }
   187   function popContext() {
   188     if (curState.context) curState.context = curState.context.prev;
   189   }
   190 
   191   function element(type) {
   192     if (type == "openTag") {
   193       curState.tagName = tagName;
   194       return cont(attributes, endtag(curState.startOfLine));
   195     } else if (type == "closeTag") {
   196       var err = false;
   197       if (curState.context) {
   198         if (curState.context.tagName != tagName) {
   199           if (Kludges.implicitlyClosed.hasOwnProperty(curState.context.tagName.toLowerCase())) {
   200             popContext();
   201           }
   202           err = !curState.context || curState.context.tagName != tagName;
   203         }
   204       } else {
   205         err = true;
   206       }
   207       if (err) setStyle = "error";
   208       return cont(endclosetag(err));
   209     }
   210     return cont();
   211   }
   212   function endtag(startOfLine) {
   213     return function(type) {
   214       var tagName = curState.tagName;
   215       curState.tagName = null;
   216       if (type == "selfcloseTag" ||
   217           (type == "endTag" && Kludges.autoSelfClosers.hasOwnProperty(tagName.toLowerCase()))) {
   218         maybePopContext(tagName.toLowerCase());
   219         return cont();
   220       }
   221       if (type == "endTag") {
   222         maybePopContext(tagName.toLowerCase());
   223         pushContext(tagName, startOfLine);
   224         return cont();
   225       }
   226       return cont();
   227     };
   228   }
   229   function endclosetag(err) {
   230     return function(type) {
   231       if (err) setStyle = "error";
   232       if (type == "endTag") { popContext(); return cont(); }
   233       setStyle = "error";
   234       return cont(arguments.callee);
   235     };
   236   }
   237   function maybePopContext(nextTagName) {
   238     var parentTagName;
   239     while (true) {
   240       if (!curState.context) {
   241         return;
   242       }
   243       parentTagName = curState.context.tagName.toLowerCase();
   244       if (!Kludges.contextGrabbers.hasOwnProperty(parentTagName) ||
   245           !Kludges.contextGrabbers[parentTagName].hasOwnProperty(nextTagName)) {
   246         return;
   247       }
   248       popContext();
   249     }
   250   }
   251 
   252   function attributes(type) {
   253     if (type == "word") {setStyle = "attribute"; return cont(attribute, attributes);}
   254     if (type == "endTag" || type == "selfcloseTag") return pass();
   255     setStyle = "error";
   256     return cont(attributes);
   257   }
   258   function attribute(type) {
   259     if (type == "equals") return cont(attvalue, attributes);
   260     if (!Kludges.allowMissing) setStyle = "error";
   261     else if (type == "word") setStyle = "attribute";
   262     return (type == "endTag" || type == "selfcloseTag") ? pass() : cont();
   263   }
   264   function attvalue(type) {
   265     if (type == "string") return cont(attvaluemaybe);
   266     if (type == "word" && Kludges.allowUnquoted) {setStyle = "string"; return cont();}
   267     setStyle = "error";
   268     return (type == "endTag" || type == "selfCloseTag") ? pass() : cont();
   269   }
   270   function attvaluemaybe(type) {
   271     if (type == "string") return cont(attvaluemaybe);
   272     else return pass();
   273   }
   274 
   275   return {
   276     startState: function() {
   277       return {tokenize: inText, cc: [], indented: 0, startOfLine: true, tagName: null, context: null};
   278     },
   279 
   280     token: function(stream, state) {
   281       if (stream.sol()) {
   282         state.startOfLine = true;
   283         state.indented = stream.indentation();
   284       }
   285       if (stream.eatSpace()) return null;
   286 
   287       setStyle = type = tagName = null;
   288       var style = state.tokenize(stream, state);
   289       state.type = type;
   290       if ((style || type) && style != "comment") {
   291         curState = state;
   292         while (true) {
   293           var comb = state.cc.pop() || element;
   294           if (comb(type || style)) break;
   295         }
   296       }
   297       state.startOfLine = false;
   298       return setStyle || style;
   299     },
   300 
   301     indent: function(state, textAfter, fullLine) {
   302       var context = state.context;
   303       if ((state.tokenize != inTag && state.tokenize != inText) ||
   304           context && context.noIndent)
   305         return fullLine ? fullLine.match(/^(\s*)/)[0].length : 0;
   306       if (alignCDATA && /<!\[CDATA\[/.test(textAfter)) return 0;
   307       if (context && /^<\//.test(textAfter))
   308         context = context.prev;
   309       while (context && !context.startOfLine)
   310         context = context.prev;
   311       if (context) return context.indent + indentUnit;
   312       else return 0;
   313     },
   314 
   315     electricChars: "/",
   316 
   317     configuration: parserConfig.htmlMode ? "html" : "xml"
   318   };
   319 });
   320 
   321 CodeMirror.defineMIME("text/xml", "xml");
   322 CodeMirror.defineMIME("application/xml", "xml");
   323 if (!CodeMirror.mimeModes.hasOwnProperty("text/html"))
   324   CodeMirror.defineMIME("text/html", {name: "xml", htmlMode: true});