Merge branch '2.0'
[GitHub/WoltLab/WCF.git] / wcfsetup / install / files / js / 3rdParty / codemirror / mode / smarty / smarty.js
1 /**
2 * Smarty 2 and 3 mode.
3 */
4
5 (function(mod) {
6 if (typeof exports == "object" && typeof module == "object") // CommonJS
7 mod(require("../../lib/codemirror"));
8 else if (typeof define == "function" && define.amd) // AMD
9 define(["../../lib/codemirror"], mod);
10 else // Plain browser env
11 mod(CodeMirror);
12 })(function(CodeMirror) {
13 "use strict";
14
15 CodeMirror.defineMode("smarty", function(config) {
16 "use strict";
17
18 // our default settings; check to see if they're overridden
19 var settings = {
20 rightDelimiter: '}',
21 leftDelimiter: '{',
22 smartyVersion: 2 // for backward compatibility
23 };
24 if (config.hasOwnProperty("leftDelimiter")) {
25 settings.leftDelimiter = config.leftDelimiter;
26 }
27 if (config.hasOwnProperty("rightDelimiter")) {
28 settings.rightDelimiter = config.rightDelimiter;
29 }
30 if (config.hasOwnProperty("smartyVersion") && config.smartyVersion === 3) {
31 settings.smartyVersion = 3;
32 }
33
34 var keyFunctions = ["debug", "extends", "function", "include", "literal"];
35 var last;
36 var regs = {
37 operatorChars: /[+\-*&%=<>!?]/,
38 validIdentifier: /[a-zA-Z0-9_]/,
39 stringChar: /['"]/
40 };
41
42 var helpers = {
43 cont: function(style, lastType) {
44 last = lastType;
45 return style;
46 },
47 chain: function(stream, state, parser) {
48 state.tokenize = parser;
49 return parser(stream, state);
50 }
51 };
52
53
54 // our various parsers
55 var parsers = {
56
57 // the main tokenizer
58 tokenizer: function(stream, state) {
59 if (stream.match(settings.leftDelimiter, true)) {
60 if (stream.eat("*")) {
61 return helpers.chain(stream, state, parsers.inBlock("comment", "*" + settings.rightDelimiter));
62 } else {
63 // Smarty 3 allows { and } surrounded by whitespace to NOT slip into Smarty mode
64 state.depth++;
65 var isEol = stream.eol();
66 var isFollowedByWhitespace = /\s/.test(stream.peek());
67 if (settings.smartyVersion === 3 && settings.leftDelimiter === "{" && (isEol || isFollowedByWhitespace)) {
68 state.depth--;
69 return null;
70 } else {
71 state.tokenize = parsers.smarty;
72 last = "startTag";
73 return "tag";
74 }
75 }
76 } else {
77 stream.next();
78 return null;
79 }
80 },
81
82 // parsing Smarty content
83 smarty: function(stream, state) {
84 if (stream.match(settings.rightDelimiter, true)) {
85 if (settings.smartyVersion === 3) {
86 state.depth--;
87 if (state.depth <= 0) {
88 state.tokenize = parsers.tokenizer;
89 }
90 } else {
91 state.tokenize = parsers.tokenizer;
92 }
93 return helpers.cont("tag", null);
94 }
95
96 if (stream.match(settings.leftDelimiter, true)) {
97 state.depth++;
98 return helpers.cont("tag", "startTag");
99 }
100
101 var ch = stream.next();
102 if (ch == "$") {
103 stream.eatWhile(regs.validIdentifier);
104 return helpers.cont("variable-2", "variable");
105 } else if (ch == "|") {
106 return helpers.cont("operator", "pipe");
107 } else if (ch == ".") {
108 return helpers.cont("operator", "property");
109 } else if (regs.stringChar.test(ch)) {
110 state.tokenize = parsers.inAttribute(ch);
111 return helpers.cont("string", "string");
112 } else if (regs.operatorChars.test(ch)) {
113 stream.eatWhile(regs.operatorChars);
114 return helpers.cont("operator", "operator");
115 } else if (ch == "[" || ch == "]") {
116 return helpers.cont("bracket", "bracket");
117 } else if (ch == "(" || ch == ")") {
118 return helpers.cont("bracket", "operator");
119 } else if (/\d/.test(ch)) {
120 stream.eatWhile(/\d/);
121 return helpers.cont("number", "number");
122 } else {
123
124 if (state.last == "variable") {
125 if (ch == "@") {
126 stream.eatWhile(regs.validIdentifier);
127 return helpers.cont("property", "property");
128 } else if (ch == "|") {
129 stream.eatWhile(regs.validIdentifier);
130 return helpers.cont("qualifier", "modifier");
131 }
132 } else if (state.last == "pipe") {
133 stream.eatWhile(regs.validIdentifier);
134 return helpers.cont("qualifier", "modifier");
135 } else if (state.last == "whitespace") {
136 stream.eatWhile(regs.validIdentifier);
137 return helpers.cont("attribute", "modifier");
138 } if (state.last == "property") {
139 stream.eatWhile(regs.validIdentifier);
140 return helpers.cont("property", null);
141 } else if (/\s/.test(ch)) {
142 last = "whitespace";
143 return null;
144 }
145
146 var str = "";
147 if (ch != "/") {
148 str += ch;
149 }
150 var c = null;
151 while (c = stream.eat(regs.validIdentifier)) {
152 str += c;
153 }
154 for (var i=0, j=keyFunctions.length; i<j; i++) {
155 if (keyFunctions[i] == str) {
156 return helpers.cont("keyword", "keyword");
157 }
158 }
159 if (/\s/.test(ch)) {
160 return null;
161 }
162 return helpers.cont("tag", "tag");
163 }
164 },
165
166 inAttribute: function(quote) {
167 return function(stream, state) {
168 var prevChar = null;
169 var currChar = null;
170 while (!stream.eol()) {
171 currChar = stream.peek();
172 if (stream.next() == quote && prevChar !== '\\') {
173 state.tokenize = parsers.smarty;
174 break;
175 }
176 prevChar = currChar;
177 }
178 return "string";
179 };
180 },
181
182 inBlock: function(style, terminator) {
183 return function(stream, state) {
184 while (!stream.eol()) {
185 if (stream.match(terminator)) {
186 state.tokenize = parsers.tokenizer;
187 break;
188 }
189 stream.next();
190 }
191 return style;
192 };
193 }
194 };
195
196
197 // the public API for CodeMirror
198 return {
199 startState: function() {
200 return {
201 tokenize: parsers.tokenizer,
202 mode: "smarty",
203 last: null,
204 depth: 0
205 };
206 },
207 token: function(stream, state) {
208 var style = state.tokenize(stream, state);
209 state.last = last;
210 return style;
211 },
212 electricChars: ""
213 };
214 });
215
216 CodeMirror.defineMIME("text/x-smarty", "smarty");
217
218 });