-
Notifications
You must be signed in to change notification settings - Fork 15
/
expressions.js
126 lines (117 loc) · 3.62 KB
/
expressions.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
//// Example framework-specific classes ////
// Expression classes should be implemented specific to the containing
// framework's data model and expression semantics. They are created when
// templates are instantiated, so any source string parsing that can be done
// once should be performed by the Expression constructor. This is an example
// set of expresions, but a framework can have an arbitrary number of
// expression types.
//
// The required interface methods are:
// Expression(source)
// Expression::toString()
// Expression::get(context)
// Expression::truthy(context)
if (typeof require === 'function') {
var serializeObject = require('serialize-object');
}
module.exports = {
Expression: Expression,
ElseExpression: ElseExpression,
Context: Context,
ContextMeta: ContextMeta
};
function Expression(source) {
this.source = source;
}
Expression.prototype.toString = function() {
return this.source;
};
Expression.prototype.get = function(context) {
return ((this.source == null)
? context.data
: context._get(this.source)
);
};
Expression.prototype.truthy = function(context) {
return templateTruthy(this.get(context));
};
Expression.prototype.module = 'expressions';
Expression.prototype.type = 'Expression';
Expression.prototype.serialize = function() {
return serializeObject.instance(this, this.source);
};
function ElseExpression() {}
ElseExpression.prototype = new Expression();
ElseExpression.prototype.truthy = function() {
return true;
};
ElseExpression.prototype.type = 'ElseExpression';
function templateTruthy(value) {
return (Array.isArray(value)) ? value.length > 0 : !!value;
}
// Context classes should be implemented specific to the containing framework's
// data model and expression semantics. Context objects are created at render
// and binding update time, so they should be fast and minimally complex.
// Framework specific work, such as parsing template language specific syntax,
// should be done in Expression object instantiation whenever possible.
//
// The required interface methods are:
// Context::addBinding(binding)
// Context::removeBinding(binding)
// Context::child(expression)
// Context::eachChild(index)
function Context(meta, data, parent) {
this.meta = meta;
this.data = data;
this.parent = parent;
}
Context.prototype = new Expression();
Context.prototype.addBinding = function(binding) {
this.meta.addBinding(binding);
};
Context.prototype.removeBinding = function(binding) {
this.meta.removeBinding(binding);
};
Context.prototype.removeNode = function(node) {
this.meta.removeNode(node);
};
Context.prototype.child = function(expression) {
var data = expression.get(this);
return new Context(this.meta, data, this);
};
Context.prototype.eachChild = function(expression, index) {
var data = expression.get(this)[index];
return new Context(this.meta, data, this);
};
Context.prototype._get = function(property) {
return (this.data && this.data.hasOwnProperty(property)) ?
this.data[property] :
this.parent && this.parent._get(property);
};
Context.prototype.pause = function() {
this.meta.pauseCount++;
};
Context.prototype.unpause = function() {
if (--this.meta.pauseCount) return;
this.flush();
};
Context.prototype.flush = function() {
var pending = this.meta.pending;
var len = pending.length;
if (!len) return;
this.meta.pending = [];
for (var i = 0; i < len; i++) {
pending[i]();
}
};
Context.prototype.queue = function(cb) {
this.meta.pending.push(cb);
};
function noop() {}
function ContextMeta() {
this.addBinding = noop;
this.removeBinding = noop;
this.removeNode = noop;
this.pauseCount = 0;
this.pending = [];
};