[ Index ] |
PHP Cross Reference of Joomla 4.2.2 documentation |
[Summary view] [Print] [Text view]
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 });
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
Generated: Wed Sep 7 05:41:13 2022 | Chilli.vc Blog - For Webmaster,Blog-Writer,System Admin and Domainer |