mirror of
https://github.com/zulip/zulip.git
synced 2026-07-15 21:03:26 +08:00
This was "npm update handlebars" followed by copying runtime.js into the static directory and restoring the copyright header. (imported from commit 69d30cbfcb3b776cdfdcffa17a87704540eab76a)
227 lines
6.2 KiB
JavaScript
227 lines
6.2 KiB
JavaScript
import Exception from "../exception";
|
|
|
|
function LocationInfo(locInfo){
|
|
locInfo = locInfo || {};
|
|
this.firstLine = locInfo.first_line;
|
|
this.firstColumn = locInfo.first_column;
|
|
this.lastColumn = locInfo.last_column;
|
|
this.lastLine = locInfo.last_line;
|
|
}
|
|
|
|
var AST = {
|
|
ProgramNode: function(statements, inverseStrip, inverse, locInfo) {
|
|
var inverseLocationInfo, firstInverseNode;
|
|
if (arguments.length === 3) {
|
|
locInfo = inverse;
|
|
inverse = null;
|
|
} else if (arguments.length === 2) {
|
|
locInfo = inverseStrip;
|
|
inverseStrip = null;
|
|
}
|
|
|
|
LocationInfo.call(this, locInfo);
|
|
this.type = "program";
|
|
this.statements = statements;
|
|
this.strip = {};
|
|
|
|
if(inverse) {
|
|
firstInverseNode = inverse[0];
|
|
if (firstInverseNode) {
|
|
inverseLocationInfo = {
|
|
first_line: firstInverseNode.firstLine,
|
|
last_line: firstInverseNode.lastLine,
|
|
last_column: firstInverseNode.lastColumn,
|
|
first_column: firstInverseNode.firstColumn
|
|
};
|
|
this.inverse = new AST.ProgramNode(inverse, inverseStrip, inverseLocationInfo);
|
|
} else {
|
|
this.inverse = new AST.ProgramNode(inverse, inverseStrip);
|
|
}
|
|
this.strip.right = inverseStrip.left;
|
|
} else if (inverseStrip) {
|
|
this.strip.left = inverseStrip.right;
|
|
}
|
|
},
|
|
|
|
MustacheNode: function(rawParams, hash, open, strip, locInfo) {
|
|
LocationInfo.call(this, locInfo);
|
|
this.type = "mustache";
|
|
this.strip = strip;
|
|
|
|
// Open may be a string parsed from the parser or a passed boolean flag
|
|
if (open != null && open.charAt) {
|
|
// Must use charAt to support IE pre-10
|
|
var escapeFlag = open.charAt(3) || open.charAt(2);
|
|
this.escaped = escapeFlag !== '{' && escapeFlag !== '&';
|
|
} else {
|
|
this.escaped = !!open;
|
|
}
|
|
|
|
if (rawParams instanceof AST.SexprNode) {
|
|
this.sexpr = rawParams;
|
|
} else {
|
|
// Support old AST API
|
|
this.sexpr = new AST.SexprNode(rawParams, hash);
|
|
}
|
|
|
|
this.sexpr.isRoot = true;
|
|
|
|
// Support old AST API that stored this info in MustacheNode
|
|
this.id = this.sexpr.id;
|
|
this.params = this.sexpr.params;
|
|
this.hash = this.sexpr.hash;
|
|
this.eligibleHelper = this.sexpr.eligibleHelper;
|
|
this.isHelper = this.sexpr.isHelper;
|
|
},
|
|
|
|
SexprNode: function(rawParams, hash, locInfo) {
|
|
LocationInfo.call(this, locInfo);
|
|
|
|
this.type = "sexpr";
|
|
this.hash = hash;
|
|
|
|
var id = this.id = rawParams[0];
|
|
var params = this.params = rawParams.slice(1);
|
|
|
|
// a mustache is an eligible helper if:
|
|
// * its id is simple (a single part, not `this` or `..`)
|
|
var eligibleHelper = this.eligibleHelper = id.isSimple;
|
|
|
|
// a mustache is definitely a helper if:
|
|
// * it is an eligible helper, and
|
|
// * it has at least one parameter or hash segment
|
|
this.isHelper = eligibleHelper && (params.length || hash);
|
|
|
|
// if a mustache is an eligible helper but not a definite
|
|
// helper, it is ambiguous, and will be resolved in a later
|
|
// pass or at runtime.
|
|
},
|
|
|
|
PartialNode: function(partialName, context, strip, locInfo) {
|
|
LocationInfo.call(this, locInfo);
|
|
this.type = "partial";
|
|
this.partialName = partialName;
|
|
this.context = context;
|
|
this.strip = strip;
|
|
},
|
|
|
|
BlockNode: function(mustache, program, inverse, close, locInfo) {
|
|
LocationInfo.call(this, locInfo);
|
|
|
|
if(mustache.sexpr.id.original !== close.path.original) {
|
|
throw new Exception(mustache.sexpr.id.original + " doesn't match " + close.path.original, this);
|
|
}
|
|
|
|
this.type = 'block';
|
|
this.mustache = mustache;
|
|
this.program = program;
|
|
this.inverse = inverse;
|
|
|
|
this.strip = {
|
|
left: mustache.strip.left,
|
|
right: close.strip.right
|
|
};
|
|
|
|
(program || inverse).strip.left = mustache.strip.right;
|
|
(inverse || program).strip.right = close.strip.left;
|
|
|
|
if (inverse && !program) {
|
|
this.isInverse = true;
|
|
}
|
|
},
|
|
|
|
ContentNode: function(string, locInfo) {
|
|
LocationInfo.call(this, locInfo);
|
|
this.type = "content";
|
|
this.string = string;
|
|
},
|
|
|
|
HashNode: function(pairs, locInfo) {
|
|
LocationInfo.call(this, locInfo);
|
|
this.type = "hash";
|
|
this.pairs = pairs;
|
|
},
|
|
|
|
IdNode: function(parts, locInfo) {
|
|
LocationInfo.call(this, locInfo);
|
|
this.type = "ID";
|
|
|
|
var original = "",
|
|
dig = [],
|
|
depth = 0;
|
|
|
|
for(var i=0,l=parts.length; i<l; i++) {
|
|
var part = parts[i].part;
|
|
original += (parts[i].separator || '') + part;
|
|
|
|
if (part === ".." || part === "." || part === "this") {
|
|
if (dig.length > 0) {
|
|
throw new Exception("Invalid path: " + original, this);
|
|
} else if (part === "..") {
|
|
depth++;
|
|
} else {
|
|
this.isScoped = true;
|
|
}
|
|
} else {
|
|
dig.push(part);
|
|
}
|
|
}
|
|
|
|
this.original = original;
|
|
this.parts = dig;
|
|
this.string = dig.join('.');
|
|
this.depth = depth;
|
|
|
|
// an ID is simple if it only has one part, and that part is not
|
|
// `..` or `this`.
|
|
this.isSimple = parts.length === 1 && !this.isScoped && depth === 0;
|
|
|
|
this.stringModeValue = this.string;
|
|
},
|
|
|
|
PartialNameNode: function(name, locInfo) {
|
|
LocationInfo.call(this, locInfo);
|
|
this.type = "PARTIAL_NAME";
|
|
this.name = name.original;
|
|
},
|
|
|
|
DataNode: function(id, locInfo) {
|
|
LocationInfo.call(this, locInfo);
|
|
this.type = "DATA";
|
|
this.id = id;
|
|
},
|
|
|
|
StringNode: function(string, locInfo) {
|
|
LocationInfo.call(this, locInfo);
|
|
this.type = "STRING";
|
|
this.original =
|
|
this.string =
|
|
this.stringModeValue = string;
|
|
},
|
|
|
|
IntegerNode: function(integer, locInfo) {
|
|
LocationInfo.call(this, locInfo);
|
|
this.type = "INTEGER";
|
|
this.original =
|
|
this.integer = integer;
|
|
this.stringModeValue = Number(integer);
|
|
},
|
|
|
|
BooleanNode: function(bool, locInfo) {
|
|
LocationInfo.call(this, locInfo);
|
|
this.type = "BOOLEAN";
|
|
this.bool = bool;
|
|
this.stringModeValue = bool === "true";
|
|
},
|
|
|
|
CommentNode: function(comment, locInfo) {
|
|
LocationInfo.call(this, locInfo);
|
|
this.type = "comment";
|
|
this.comment = comment;
|
|
}
|
|
};
|
|
|
|
// Must be exported as an object rather than the root of the module as the jison lexer
|
|
// most modify the object to operate properly.
|
|
export default AST;
|