| 
									
										
										
										
											2018-01-21 10:33:32 -05:00
										 |  |  | // CodeMirror, copyright (c) by Marijn Haverbeke and others
 | 
					
						
							| 
									
										
										
										
											2022-10-15 12:22:09 +02:00
										 |  |  | // Distributed under an MIT license: https://codemirror.net/5/LICENSE
 | 
					
						
							| 
									
										
										
										
											2018-01-21 10:33:32 -05:00
										 |  |  | 
 | 
					
						
							|  |  |  | /** | 
					
						
							|  |  |  |  * Smarty 2 and 3 mode. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | (function(mod) { | 
					
						
							|  |  |  |   if (typeof exports == "object" && typeof module == "object") // CommonJS
 | 
					
						
							|  |  |  |     mod(require("../../lib/codemirror")); | 
					
						
							|  |  |  |   else if (typeof define == "function" && define.amd) // AMD
 | 
					
						
							|  |  |  |     define(["../../lib/codemirror"], mod); | 
					
						
							|  |  |  |   else // Plain browser env
 | 
					
						
							|  |  |  |     mod(CodeMirror); | 
					
						
							|  |  |  | })(function(CodeMirror) { | 
					
						
							|  |  |  |   "use strict"; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   CodeMirror.defineMode("smarty", function(config, parserConf) { | 
					
						
							|  |  |  |     var rightDelimiter = parserConf.rightDelimiter || "}"; | 
					
						
							|  |  |  |     var leftDelimiter = parserConf.leftDelimiter || "{"; | 
					
						
							|  |  |  |     var version = parserConf.version || 2; | 
					
						
							|  |  |  |     var baseMode = CodeMirror.getMode(config, parserConf.baseMode || "null"); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     var keyFunctions = ["debug", "extends", "function", "include", "literal"]; | 
					
						
							|  |  |  |     var regs = { | 
					
						
							|  |  |  |       operatorChars: /[+\-*&%=<>!?]/, | 
					
						
							|  |  |  |       validIdentifier: /[a-zA-Z0-9_]/, | 
					
						
							|  |  |  |       stringChar: /['"]/ | 
					
						
							|  |  |  |     }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     var last; | 
					
						
							|  |  |  |     function cont(style, lastType) { | 
					
						
							|  |  |  |       last = lastType; | 
					
						
							|  |  |  |       return style; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     function chain(stream, state, parser) { | 
					
						
							|  |  |  |       state.tokenize = parser; | 
					
						
							|  |  |  |       return parser(stream, state); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // Smarty 3 allows { and } surrounded by whitespace to NOT slip into Smarty mode
 | 
					
						
							|  |  |  |     function doesNotCount(stream, pos) { | 
					
						
							|  |  |  |       if (pos == null) pos = stream.pos; | 
					
						
							|  |  |  |       return version === 3 && leftDelimiter == "{" && | 
					
						
							|  |  |  |         (pos == stream.string.length || /\s/.test(stream.string.charAt(pos))); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     function tokenTop(stream, state) { | 
					
						
							|  |  |  |       var string = stream.string; | 
					
						
							|  |  |  |       for (var scan = stream.pos;;) { | 
					
						
							|  |  |  |         var nextMatch = string.indexOf(leftDelimiter, scan); | 
					
						
							|  |  |  |         scan = nextMatch + leftDelimiter.length; | 
					
						
							|  |  |  |         if (nextMatch == -1 || !doesNotCount(stream, nextMatch + leftDelimiter.length)) break; | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |       if (nextMatch == stream.pos) { | 
					
						
							|  |  |  |         stream.match(leftDelimiter); | 
					
						
							|  |  |  |         if (stream.eat("*")) { | 
					
						
							|  |  |  |           return chain(stream, state, tokenBlock("comment", "*" + rightDelimiter)); | 
					
						
							|  |  |  |         } else { | 
					
						
							|  |  |  |           state.depth++; | 
					
						
							|  |  |  |           state.tokenize = tokenSmarty; | 
					
						
							|  |  |  |           last = "startTag"; | 
					
						
							|  |  |  |           return "tag"; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       if (nextMatch > -1) stream.string = string.slice(0, nextMatch); | 
					
						
							|  |  |  |       var token = baseMode.token(stream, state.base); | 
					
						
							|  |  |  |       if (nextMatch > -1) stream.string = string; | 
					
						
							|  |  |  |       return token; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // parsing Smarty content
 | 
					
						
							|  |  |  |     function tokenSmarty(stream, state) { | 
					
						
							|  |  |  |       if (stream.match(rightDelimiter, true)) { | 
					
						
							|  |  |  |         if (version === 3) { | 
					
						
							|  |  |  |           state.depth--; | 
					
						
							|  |  |  |           if (state.depth <= 0) { | 
					
						
							|  |  |  |             state.tokenize = tokenTop; | 
					
						
							|  |  |  |           } | 
					
						
							|  |  |  |         } else { | 
					
						
							|  |  |  |           state.tokenize = tokenTop; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         return cont("tag", null); | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       if (stream.match(leftDelimiter, true)) { | 
					
						
							|  |  |  |         state.depth++; | 
					
						
							|  |  |  |         return cont("tag", "startTag"); | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       var ch = stream.next(); | 
					
						
							|  |  |  |       if (ch == "$") { | 
					
						
							|  |  |  |         stream.eatWhile(regs.validIdentifier); | 
					
						
							|  |  |  |         return cont("variable-2", "variable"); | 
					
						
							|  |  |  |       } else if (ch == "|") { | 
					
						
							|  |  |  |         return cont("operator", "pipe"); | 
					
						
							|  |  |  |       } else if (ch == ".") { | 
					
						
							|  |  |  |         return cont("operator", "property"); | 
					
						
							|  |  |  |       } else if (regs.stringChar.test(ch)) { | 
					
						
							|  |  |  |         state.tokenize = tokenAttribute(ch); | 
					
						
							|  |  |  |         return cont("string", "string"); | 
					
						
							|  |  |  |       } else if (regs.operatorChars.test(ch)) { | 
					
						
							|  |  |  |         stream.eatWhile(regs.operatorChars); | 
					
						
							|  |  |  |         return cont("operator", "operator"); | 
					
						
							|  |  |  |       } else if (ch == "[" || ch == "]") { | 
					
						
							|  |  |  |         return cont("bracket", "bracket"); | 
					
						
							|  |  |  |       } else if (ch == "(" || ch == ")") { | 
					
						
							|  |  |  |         return cont("bracket", "operator"); | 
					
						
							|  |  |  |       } else if (/\d/.test(ch)) { | 
					
						
							|  |  |  |         stream.eatWhile(/\d/); | 
					
						
							|  |  |  |         return cont("number", "number"); | 
					
						
							|  |  |  |       } else { | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         if (state.last == "variable") { | 
					
						
							|  |  |  |           if (ch == "@") { | 
					
						
							|  |  |  |             stream.eatWhile(regs.validIdentifier); | 
					
						
							|  |  |  |             return cont("property", "property"); | 
					
						
							|  |  |  |           } else if (ch == "|") { | 
					
						
							|  |  |  |             stream.eatWhile(regs.validIdentifier); | 
					
						
							|  |  |  |             return cont("qualifier", "modifier"); | 
					
						
							|  |  |  |           } | 
					
						
							|  |  |  |         } else if (state.last == "pipe") { | 
					
						
							|  |  |  |           stream.eatWhile(regs.validIdentifier); | 
					
						
							|  |  |  |           return cont("qualifier", "modifier"); | 
					
						
							|  |  |  |         } else if (state.last == "whitespace") { | 
					
						
							|  |  |  |           stream.eatWhile(regs.validIdentifier); | 
					
						
							|  |  |  |           return cont("attribute", "modifier"); | 
					
						
							|  |  |  |         } if (state.last == "property") { | 
					
						
							|  |  |  |           stream.eatWhile(regs.validIdentifier); | 
					
						
							|  |  |  |           return cont("property", null); | 
					
						
							|  |  |  |         } else if (/\s/.test(ch)) { | 
					
						
							|  |  |  |           last = "whitespace"; | 
					
						
							|  |  |  |           return null; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         var str = ""; | 
					
						
							|  |  |  |         if (ch != "/") { | 
					
						
							|  |  |  |           str += ch; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         var c = null; | 
					
						
							|  |  |  |         while (c = stream.eat(regs.validIdentifier)) { | 
					
						
							|  |  |  |           str += c; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         for (var i=0, j=keyFunctions.length; i<j; i++) { | 
					
						
							|  |  |  |           if (keyFunctions[i] == str) { | 
					
						
							|  |  |  |             return cont("keyword", "keyword"); | 
					
						
							|  |  |  |           } | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         if (/\s/.test(ch)) { | 
					
						
							|  |  |  |           return null; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         return cont("tag", "tag"); | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     function tokenAttribute(quote) { | 
					
						
							|  |  |  |       return function(stream, state) { | 
					
						
							|  |  |  |         var prevChar = null; | 
					
						
							|  |  |  |         var currChar = null; | 
					
						
							|  |  |  |         while (!stream.eol()) { | 
					
						
							|  |  |  |           currChar = stream.peek(); | 
					
						
							|  |  |  |           if (stream.next() == quote && prevChar !== '\\') { | 
					
						
							|  |  |  |             state.tokenize = tokenSmarty; | 
					
						
							|  |  |  |             break; | 
					
						
							|  |  |  |           } | 
					
						
							|  |  |  |           prevChar = currChar; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         return "string"; | 
					
						
							|  |  |  |       }; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     function tokenBlock(style, terminator) { | 
					
						
							|  |  |  |       return function(stream, state) { | 
					
						
							|  |  |  |         while (!stream.eol()) { | 
					
						
							|  |  |  |           if (stream.match(terminator)) { | 
					
						
							|  |  |  |             state.tokenize = tokenTop; | 
					
						
							|  |  |  |             break; | 
					
						
							|  |  |  |           } | 
					
						
							|  |  |  |           stream.next(); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         return style; | 
					
						
							|  |  |  |       }; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     return { | 
					
						
							|  |  |  |       startState: function() { | 
					
						
							|  |  |  |         return { | 
					
						
							|  |  |  |           base: CodeMirror.startState(baseMode), | 
					
						
							|  |  |  |           tokenize: tokenTop, | 
					
						
							|  |  |  |           last: null, | 
					
						
							|  |  |  |           depth: 0 | 
					
						
							|  |  |  |         }; | 
					
						
							|  |  |  |       }, | 
					
						
							|  |  |  |       copyState: function(state) { | 
					
						
							|  |  |  |         return { | 
					
						
							|  |  |  |           base: CodeMirror.copyState(baseMode, state.base), | 
					
						
							|  |  |  |           tokenize: state.tokenize, | 
					
						
							|  |  |  |           last: state.last, | 
					
						
							|  |  |  |           depth: state.depth | 
					
						
							|  |  |  |         }; | 
					
						
							|  |  |  |       }, | 
					
						
							|  |  |  |       innerMode: function(state) { | 
					
						
							|  |  |  |         if (state.tokenize == tokenTop) | 
					
						
							|  |  |  |           return {mode: baseMode, state: state.base}; | 
					
						
							|  |  |  |       }, | 
					
						
							|  |  |  |       token: function(stream, state) { | 
					
						
							|  |  |  |         var style = state.tokenize(stream, state); | 
					
						
							|  |  |  |         state.last = last; | 
					
						
							|  |  |  |         return style; | 
					
						
							|  |  |  |       }, | 
					
						
							| 
									
										
										
										
											2019-06-02 09:59:07 +02:00
										 |  |  |       indent: function(state, text, line) { | 
					
						
							| 
									
										
										
										
											2018-01-21 10:33:32 -05:00
										 |  |  |         if (state.tokenize == tokenTop && baseMode.indent) | 
					
						
							| 
									
										
										
										
											2019-06-02 09:59:07 +02:00
										 |  |  |           return baseMode.indent(state.base, text, line); | 
					
						
							| 
									
										
										
										
											2018-01-21 10:33:32 -05:00
										 |  |  |         else | 
					
						
							|  |  |  |           return CodeMirror.Pass; | 
					
						
							|  |  |  |       }, | 
					
						
							|  |  |  |       blockCommentStart: leftDelimiter + "*", | 
					
						
							|  |  |  |       blockCommentEnd: "*" + rightDelimiter | 
					
						
							|  |  |  |     }; | 
					
						
							|  |  |  |   }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   CodeMirror.defineMIME("text/x-smarty", "smarty"); | 
					
						
							|  |  |  | }); |