[ Index ]

PHP Cross Reference of Joomla 4.2.2 documentation

title

Body

[close]

/media/vendor/codemirror/mode/soy/ -> soy.js (source)

   1  // CodeMirror, copyright (c) by Marijn Haverbeke and others
   2  // Distributed under an MIT license: https://codemirror.net/5/LICENSE
   3  
   4  (function(mod) {
   5    if (typeof exports == "object" && typeof module == "object") // CommonJS
   6      mod(require("../../lib/codemirror"), require("../htmlmixed/htmlmixed"));
   7    else if (typeof define == "function" && define.amd) // AMD
   8      define(["../../lib/codemirror", "../htmlmixed/htmlmixed"], mod);
   9    else // Plain browser env
  10      mod(CodeMirror);
  11  })(function(CodeMirror) {
  12    "use strict";
  13  
  14    var paramData = { noEndTag: true, soyState: "param-def" };
  15    var tags = {
  16      "alias": { noEndTag: true },
  17      "delpackage": { noEndTag: true },
  18      "namespace": { noEndTag: true, soyState: "namespace-def" },
  19      "@attribute": paramData,
  20      "@attribute?": paramData,
  21      "@param": paramData,
  22      "@param?": paramData,
  23      "@inject": paramData,
  24      "@inject?": paramData,
  25      "@state": paramData,
  26      "template": { soyState: "templ-def", variableScope: true},
  27      "extern": {soyState: "param-def"},
  28      "export": {soyState: "export"},
  29      "literal": { },
  30      "msg": {},
  31      "fallbackmsg": { noEndTag: true, reduceIndent: true},
  32      "select": {},
  33      "plural": {},
  34      "let": { soyState: "var-def" },
  35      "if": {},
  36      "javaimpl": {},
  37      "jsimpl": {},
  38      "elseif": { noEndTag: true, reduceIndent: true},
  39      "else": { noEndTag: true, reduceIndent: true},
  40      "switch": {},
  41      "case": { noEndTag: true, reduceIndent: true},
  42      "default": { noEndTag: true, reduceIndent: true},
  43      "foreach": { variableScope: true, soyState: "for-loop" },
  44      "ifempty": { noEndTag: true, reduceIndent: true},
  45      "for": { variableScope: true, soyState: "for-loop" },
  46      "call": { soyState: "templ-ref" },
  47      "param": { soyState: "param-ref"},
  48      "print": { noEndTag: true },
  49      "deltemplate": { soyState: "templ-def", variableScope: true},
  50      "delcall": { soyState: "templ-ref" },
  51      "log": {},
  52      "element": { variableScope: true },
  53      "velog": {},
  54      "const": { soyState: "const-def"},
  55    };
  56  
  57    var indentingTags = Object.keys(tags).filter(function(tag) {
  58      return !tags[tag].noEndTag || tags[tag].reduceIndent;
  59    });
  60  
  61    CodeMirror.defineMode("soy", function(config) {
  62      var textMode = CodeMirror.getMode(config, "text/plain");
  63      var modes = {
  64        html: CodeMirror.getMode(config, {name: "text/html", multilineTagIndentFactor: 2, multilineTagIndentPastTag: false, allowMissingTagName: true}),
  65        attributes: textMode,
  66        text: textMode,
  67        uri: textMode,
  68        trusted_resource_uri: textMode,
  69        css: CodeMirror.getMode(config, "text/css"),
  70        js: CodeMirror.getMode(config, {name: "text/javascript", statementIndent: 2 * config.indentUnit})
  71      };
  72  
  73      function last(array) {
  74        return array[array.length - 1];
  75      }
  76  
  77      function tokenUntil(stream, state, untilRegExp) {
  78        if (stream.sol()) {
  79          for (var indent = 0; indent < state.indent; indent++) {
  80            if (!stream.eat(/\s/)) break;
  81          }
  82          if (indent) return null;
  83        }
  84        var oldString = stream.string;
  85        var match = untilRegExp.exec(oldString.substr(stream.pos));
  86        if (match) {
  87          // We don't use backUp because it backs up just the position, not the state.
  88          // This uses an undocumented API.
  89          stream.string = oldString.substr(0, stream.pos + match.index);
  90        }
  91        var result = stream.hideFirstChars(state.indent, function() {
  92          var localState = last(state.localStates);
  93          return localState.mode.token(stream, localState.state);
  94        });
  95        stream.string = oldString;
  96        return result;
  97      }
  98  
  99      function contains(list, element) {
 100        while (list) {
 101          if (list.element === element) return true;
 102          list = list.next;
 103        }
 104        return false;
 105      }
 106  
 107      function prepend(list, element) {
 108        return {
 109          element: element,
 110          next: list
 111        };
 112      }
 113  
 114      function popcontext(state) {
 115        if (!state.context) return;
 116        if (state.context.scope) {
 117          state.variables = state.context.scope;
 118        }
 119        state.context = state.context.previousContext;
 120      }
 121  
 122      // Reference a variable `name` in `list`.
 123      // Let `loose` be truthy to ignore missing identifiers.
 124      function ref(list, name, loose) {
 125        return contains(list, name) ? "variable-2" : (loose ? "variable" : "variable-2 error");
 126      }
 127  
 128      // Data for an open soy tag.
 129      function Context(previousContext, tag, scope) {
 130        this.previousContext = previousContext;
 131        this.tag = tag;
 132        this.kind = null;
 133        this.scope = scope;
 134      }
 135  
 136      function expression(stream, state) {
 137        var match;
 138        if (stream.match(/[[]/)) {
 139          state.soyState.push("list-literal");
 140          state.context = new Context(state.context, "list-literal", state.variables);
 141          state.lookupVariables = false;
 142          return null;
 143        } else if (stream.match(/\bmap(?=\()/)) {
 144          state.soyState.push("map-literal");
 145          return "keyword";
 146        } else if (stream.match(/\brecord(?=\()/)) {
 147          state.soyState.push("record-literal");
 148          return "keyword";
 149        } else if (stream.match(/([\w]+)(?=\()/)) {
 150          return "variable callee";
 151        } else if (match = stream.match(/^["']/)) {
 152          state.soyState.push("string");
 153          state.quoteKind = match[0];
 154          return "string";
 155        } else if (stream.match(/^[(]/)) {
 156          state.soyState.push("open-parentheses");
 157          return null;
 158        } else if (stream.match(/(null|true|false)(?!\w)/) ||
 159            stream.match(/0x([0-9a-fA-F]{2,})/) ||
 160            stream.match(/-?([0-9]*[.])?[0-9]+(e[0-9]*)?/)) {
 161          return "atom";
 162        } else if (stream.match(/(\||[+\-*\/%]|[=!]=|\?:|[<>]=?)/)) {
 163          // Tokenize filter, binary, null propagator, and equality operators.
 164          return "operator";
 165        } else if (match = stream.match(/^\$([\w]+)/)) {
 166          return ref(state.variables, match[1], !state.lookupVariables);
 167        } else if (match = stream.match(/^\w+/)) {
 168          return /^(?:as|and|or|not|in|if)$/.test(match[0]) ? "keyword" : null;
 169        }
 170  
 171        stream.next();
 172        return null;
 173      }
 174  
 175      return {
 176        startState: function() {
 177          return {
 178            soyState: [],
 179            variables: prepend(null, 'ij'),
 180            scopes: null,
 181            indent: 0,
 182            quoteKind: null,
 183            context: null,
 184            lookupVariables: true, // Is unknown variables considered an error
 185            localStates: [{
 186              mode: modes.html,
 187              state: CodeMirror.startState(modes.html)
 188            }]
 189          };
 190        },
 191  
 192        copyState: function(state) {
 193          return {
 194            tag: state.tag, // Last seen Soy tag.
 195            soyState: state.soyState.concat([]),
 196            variables: state.variables,
 197            context: state.context,
 198            indent: state.indent, // Indentation of the following line.
 199            quoteKind: state.quoteKind,
 200            lookupVariables: state.lookupVariables,
 201            localStates: state.localStates.map(function(localState) {
 202              return {
 203                mode: localState.mode,
 204                state: CodeMirror.copyState(localState.mode, localState.state)
 205              };
 206            })
 207          };
 208        },
 209  
 210        token: function(stream, state) {
 211          var match;
 212  
 213          switch (last(state.soyState)) {
 214            case "comment":
 215              if (stream.match(/^.*?\*\//)) {
 216                state.soyState.pop();
 217              } else {
 218                stream.skipToEnd();
 219              }
 220              if (!state.context || !state.context.scope) {
 221                var paramRe = /@param\??\s+(\S+)/g;
 222                var current = stream.current();
 223                for (var match; (match = paramRe.exec(current)); ) {
 224                  state.variables = prepend(state.variables, match[1]);
 225                }
 226              }
 227              return "comment";
 228  
 229            case "string":
 230              var match = stream.match(/^.*?(["']|\\[\s\S])/);
 231              if (!match) {
 232                stream.skipToEnd();
 233              } else if (match[1] == state.quoteKind) {
 234                state.quoteKind = null;
 235                state.soyState.pop();
 236              }
 237              return "string";
 238          }
 239  
 240          if (!state.soyState.length || last(state.soyState) != "literal") {
 241            if (stream.match(/^\/\*/)) {
 242              state.soyState.push("comment");
 243              return "comment";
 244            } else if (stream.match(stream.sol() ? /^\s*\/\/.*/ : /^\s+\/\/.*/)) {
 245              return "comment";
 246            }
 247          }
 248  
 249          switch (last(state.soyState)) {
 250            case "templ-def":
 251              if (match = stream.match(/^\.?([\w]+(?!\.[\w]+)*)/)) {
 252                state.soyState.pop();
 253                return "def";
 254              }
 255              stream.next();
 256              return null;
 257  
 258            case "templ-ref":
 259              if (match = stream.match(/(\.?[a-zA-Z_][a-zA-Z_0-9]+)+/)) {
 260                state.soyState.pop();
 261                // If the first character is '.', it can only be a local template.
 262                if (match[0][0] == '.') {
 263                  return "variable-2"
 264                }
 265                // Otherwise
 266                return "variable";
 267              }
 268              if (match = stream.match(/^\$([\w]+)/)) {
 269                state.soyState.pop();
 270                return ref(state.variables, match[1], !state.lookupVariables);
 271              }
 272  
 273              stream.next();
 274              return null;
 275  
 276            case "namespace-def":
 277              if (match = stream.match(/^\.?([\w\.]+)/)) {
 278                state.soyState.pop();
 279                return "variable";
 280              }
 281              stream.next();
 282              return null;
 283  
 284            case "param-def":
 285              if (match = stream.match(/^\*/)) {
 286                state.soyState.pop();
 287                state.soyState.push("param-type");
 288                return "type";
 289              }
 290              if (match = stream.match(/^\w+/)) {
 291                state.variables = prepend(state.variables, match[0]);
 292                state.soyState.pop();
 293                state.soyState.push("param-type");
 294                return "def";
 295              }
 296              stream.next();
 297              return null;
 298  
 299            case "param-ref":
 300              if (match = stream.match(/^\w+/)) {
 301                state.soyState.pop();
 302                return "property";
 303              }
 304              stream.next();
 305              return null;
 306  
 307            case "open-parentheses":
 308              if (stream.match(/[)]/)) {
 309                state.soyState.pop();
 310                return null;
 311              }
 312              return expression(stream, state);
 313  
 314            case "param-type":
 315              var peekChar = stream.peek();
 316              if ("}]=>,".indexOf(peekChar) != -1) {
 317                state.soyState.pop();
 318                return null;
 319              } else if (peekChar == "[") {
 320                state.soyState.push('param-type-record');
 321                return null;
 322              } else if (peekChar == "(") {
 323                state.soyState.push('param-type-template');
 324                return null;
 325              } else if (peekChar == "<") {
 326                state.soyState.push('param-type-parameter');
 327                return null;
 328              } else if (match = stream.match(/^([\w]+|[?])/)) {
 329                return "type";
 330              }
 331              stream.next();
 332              return null;
 333  
 334            case "param-type-record":
 335              var peekChar = stream.peek();
 336              if (peekChar == "]") {
 337                state.soyState.pop();
 338                return null;
 339              }
 340              if (stream.match(/^\w+/)) {
 341                state.soyState.push('param-type');
 342                return "property";
 343              }
 344              stream.next();
 345              return null;
 346  
 347            case "param-type-parameter":
 348              if (stream.match(/^[>]/)) {
 349                state.soyState.pop();
 350                return null;
 351              }
 352              if (stream.match(/^[<,]/)) {
 353                state.soyState.push('param-type');
 354                return null;
 355              }
 356              stream.next();
 357              return null;
 358  
 359            case "param-type-template":
 360              if (stream.match(/[>]/)) {
 361                state.soyState.pop();
 362                state.soyState.push('param-type');
 363                return null;
 364              }
 365              if (stream.match(/^\w+/)) {
 366                state.soyState.push('param-type');
 367                return "def";
 368              }
 369              stream.next();
 370              return null;
 371  
 372            case "var-def":
 373              if (match = stream.match(/^\$([\w]+)/)) {
 374                state.variables = prepend(state.variables, match[1]);
 375                state.soyState.pop();
 376                return "def";
 377              }
 378              stream.next();
 379              return null;
 380  
 381            case "for-loop":
 382              if (stream.match(/\bin\b/)) {
 383                state.soyState.pop();
 384                return "keyword";
 385              }
 386              if (stream.peek() == "$") {
 387                state.soyState.push('var-def');
 388                return null;
 389              }
 390              stream.next();
 391              return null;
 392  
 393            case "record-literal":
 394              if (stream.match(/^[)]/)) {
 395                state.soyState.pop();
 396                return null;
 397              }
 398              if (stream.match(/[(,]/)) {
 399                state.soyState.push("map-value")
 400                state.soyState.push("record-key")
 401                return null;
 402              }
 403              stream.next()
 404              return null;
 405  
 406            case "map-literal":
 407              if (stream.match(/^[)]/)) {
 408                state.soyState.pop();
 409                return null;
 410              }
 411              if (stream.match(/[(,]/)) {
 412                state.soyState.push("map-value")
 413                state.soyState.push("map-value")
 414                return null;
 415              }
 416              stream.next()
 417              return null;
 418  
 419            case "list-literal":
 420              if (stream.match(']')) {
 421                state.soyState.pop();
 422                state.lookupVariables = true;
 423                popcontext(state);
 424                return null;
 425              }
 426              if (stream.match(/\bfor\b/)) {
 427                state.lookupVariables = true;
 428                state.soyState.push('for-loop');
 429                return "keyword";
 430              }
 431              return expression(stream, state);
 432  
 433            case "record-key":
 434              if (stream.match(/[\w]+/)) {
 435                return "property";
 436              }
 437              if (stream.match(/^[:]/)) {
 438                state.soyState.pop();
 439                return null;
 440              }
 441              stream.next();
 442              return null;
 443  
 444            case "map-value":
 445              if (stream.peek() == ")" || stream.peek() == "," || stream.match(/^[:)]/)) {
 446                state.soyState.pop();
 447                return null;
 448              }
 449              return expression(stream, state);
 450  
 451            case "import":
 452              if (stream.eat(";")) {
 453                state.soyState.pop();
 454                state.indent -= 2 * config.indentUnit;
 455                return null;
 456              }
 457              if (stream.match(/\w+(?=\s+as\b)/)) {
 458                return "variable";
 459              }
 460              if (match = stream.match(/\w+/)) {
 461                return /\b(from|as)\b/.test(match[0]) ? "keyword" : "def";
 462              }
 463              if (match = stream.match(/^["']/)) {
 464                state.soyState.push("string");
 465                state.quoteKind = match[0];
 466                return "string";
 467              }
 468              stream.next();
 469              return null;
 470  
 471            case "tag":
 472              var endTag;
 473              var tagName;
 474              if (state.tag === undefined) {
 475                endTag = true;
 476                tagName = '';
 477              } else {
 478                endTag = state.tag[0] == "/";
 479                tagName = endTag ? state.tag.substring(1) : state.tag;
 480              }
 481              var tag = tags[tagName];
 482              if (stream.match(/^\/?}/)) {
 483                var selfClosed = stream.current() == "/}";
 484                if (selfClosed && !endTag) {
 485                  popcontext(state);
 486                }
 487                if (state.tag == "/template" || state.tag == "/deltemplate") {
 488                  state.variables = prepend(null, 'ij');
 489                  state.indent = 0;
 490                } else {
 491                  state.indent -= config.indentUnit *
 492                      (selfClosed || indentingTags.indexOf(state.tag) == -1 ? 2 : 1);
 493                }
 494                state.soyState.pop();
 495                return "keyword";
 496              } else if (stream.match(/^([\w?]+)(?==)/)) {
 497                if (state.context && state.context.tag == tagName && stream.current() == "kind" && (match = stream.match(/^="([^"]+)/, false))) {
 498                  var kind = match[1];
 499                  state.context.kind = kind;
 500                  var mode = modes[kind] || modes.html;
 501                  var localState = last(state.localStates);
 502                  if (localState.mode.indent) {
 503                    state.indent += localState.mode.indent(localState.state, "", "");
 504                  }
 505                  state.localStates.push({
 506                    mode: mode,
 507                    state: CodeMirror.startState(mode)
 508                  });
 509                }
 510                return "attribute";
 511              }
 512              return expression(stream, state);
 513  
 514            case "template-call-expression":
 515              if (stream.match(/^([\w-?]+)(?==)/)) {
 516                return "attribute";
 517              } else if (stream.eat('>')) {
 518                state.soyState.pop();
 519                return "keyword";
 520              } else if (stream.eat('/>')) {
 521                state.soyState.pop();
 522                return "keyword";
 523              }
 524              return expression(stream, state);
 525            case "literal":
 526              if (stream.match('{/literal}', false)) {
 527                state.soyState.pop();
 528                return this.token(stream, state);
 529              }
 530              return tokenUntil(stream, state, /\{\/literal}/);
 531            case "export":
 532              if (match = stream.match(/\w+/)) {
 533                state.soyState.pop();
 534                if (match == "const") {
 535                  state.soyState.push("const-def")
 536                  return "keyword";
 537                } else if (match == "extern") {
 538                  state.soyState.push("param-def")
 539                  return "keyword";
 540                }
 541              } else {
 542                stream.next();
 543              }
 544              return null;
 545            case "const-def":
 546              if (stream.match(/^\w+/)) {
 547                state.soyState.pop();
 548                return "def";
 549              }
 550              stream.next();
 551              return null;
 552          }
 553  
 554          if (stream.match('{literal}')) {
 555            state.indent += config.indentUnit;
 556            state.soyState.push("literal");
 557            state.context = new Context(state.context, "literal", state.variables);
 558            return "keyword";
 559  
 560          // A tag-keyword must be followed by whitespace, comment or a closing tag.
 561          } else if (match = stream.match(/^\{([/@\\]?\w+\??)(?=$|[\s}]|\/[/*])/)) {
 562            var prevTag = state.tag;
 563            state.tag = match[1];
 564            var endTag = state.tag[0] == "/";
 565            var indentingTag = !!tags[state.tag];
 566            var tagName = endTag ? state.tag.substring(1) : state.tag;
 567            var tag = tags[tagName];
 568            if (state.tag != "/switch")
 569              state.indent += ((endTag || tag && tag.reduceIndent) && prevTag != "switch" ? 1 : 2) * config.indentUnit;
 570  
 571            state.soyState.push("tag");
 572            var tagError = false;
 573            if (tag) {
 574              if (!endTag) {
 575                if (tag.soyState) state.soyState.push(tag.soyState);
 576              }
 577              // If a new tag, open a new context.
 578              if (!tag.noEndTag && (indentingTag || !endTag)) {
 579                state.context = new Context(state.context, state.tag, tag.variableScope ? state.variables : null);
 580              // Otherwise close the current context.
 581              } else if (endTag) {
 582                var isBalancedForExtern = tagName == 'extern' && (state.context && state.context.tag == 'export');
 583                if (!state.context || ((state.context.tag != tagName) && !isBalancedForExtern)) {
 584                  tagError = true;
 585                } else if (state.context) {
 586                  if (state.context.kind) {
 587                    state.localStates.pop();
 588                    var localState = last(state.localStates);
 589                    if (localState.mode.indent) {
 590                      state.indent -= localState.mode.indent(localState.state, "", "");
 591                    }
 592                  }
 593                  popcontext(state);
 594                }
 595              }
 596            } else if (endTag) {
 597              // Assume all tags with a closing tag are defined in the config.
 598              tagError = true;
 599            }
 600            return (tagError ? "error " : "") + "keyword";
 601  
 602          // Not a tag-keyword; it's an implicit print tag.
 603          } else if (stream.eat('{')) {
 604            state.tag = "print";
 605            state.indent += 2 * config.indentUnit;
 606            state.soyState.push("tag");
 607            return "keyword";
 608          } else if (!state.context && stream.sol() && stream.match(/import\b/)) {
 609            state.soyState.push("import");
 610            state.indent += 2 * config.indentUnit;
 611            return "keyword";
 612          } else if (match = stream.match('<{')) {
 613            state.soyState.push("template-call-expression");
 614            state.indent += 2 * config.indentUnit;
 615            state.soyState.push("tag");
 616            return "keyword";
 617          } else if (match = stream.match('</>')) {
 618            state.indent -= 1 * config.indentUnit;
 619            return "keyword";
 620          }
 621  
 622          return tokenUntil(stream, state, /\{|\s+\/\/|\/\*/);
 623        },
 624  
 625        indent: function(state, textAfter, line) {
 626          var indent = state.indent, top = last(state.soyState);
 627          if (top == "comment") return CodeMirror.Pass;
 628  
 629          if (top == "literal") {
 630            if (/^\{\/literal}/.test(textAfter)) indent -= config.indentUnit;
 631          } else {
 632            if (/^\s*\{\/(template|deltemplate)\b/.test(textAfter)) return 0;
 633            if (/^\{(\/|(fallbackmsg|elseif|else|ifempty)\b)/.test(textAfter)) indent -= config.indentUnit;
 634            if (state.tag != "switch" && /^\{(case|default)\b/.test(textAfter)) indent -= config.indentUnit;
 635            if (/^\{\/switch\b/.test(textAfter)) indent -= config.indentUnit;
 636          }
 637          var localState = last(state.localStates);
 638          if (indent && localState.mode.indent) {
 639            indent += localState.mode.indent(localState.state, textAfter, line);
 640          }
 641          return indent;
 642        },
 643  
 644        innerMode: function(state) {
 645          if (state.soyState.length && last(state.soyState) != "literal") return null;
 646          else return last(state.localStates);
 647        },
 648  
 649        electricInput: /^\s*\{(\/|\/template|\/deltemplate|\/switch|fallbackmsg|elseif|else|case|default|ifempty|\/literal\})$/,
 650        lineComment: "//",
 651        blockCommentStart: "/*",
 652        blockCommentEnd: "*/",
 653        blockCommentContinue: " * ",
 654        useInnerComments: false,
 655        fold: "indent"
 656      };
 657    }, "htmlmixed");
 658  
 659    CodeMirror.registerHelper("wordChars", "soy", /[\w$]/);
 660  
 661    CodeMirror.registerHelper("hintWords", "soy", Object.keys(tags).concat(
 662        ["css", "debugger"]));
 663  
 664    CodeMirror.defineMIME("text/x-soy", "soy");
 665  });


Generated: Wed Sep 7 05:41:13 2022 Chilli.vc Blog - For Webmaster,Blog-Writer,System Admin and Domainer