canjs-rails 1.1.2 → 2.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/Gemfile.lock +47 -0
- data/README.md +1 -2
- data/lib/canjs/rails/version.rb +2 -2
- data/vendor/assets/javascripts/can.jquery.js +8568 -3599
- metadata +10 -29
- data/vendor/assets/javascripts/can.construct.proxy.js +0 -61
- data/vendor/assets/javascripts/can.construct.super.js +0 -48
- data/vendor/assets/javascripts/can.control.plugin.js +0 -108
- data/vendor/assets/javascripts/can.control.view.js +0 -94
- data/vendor/assets/javascripts/can.fixture.js +0 -727
- data/vendor/assets/javascripts/can.jquery.min.js +0 -70
- data/vendor/assets/javascripts/can.observe.attributes.js +0 -154
- data/vendor/assets/javascripts/can.observe.backup.js +0 -170
- data/vendor/assets/javascripts/can.observe.delegate.js +0 -206
- data/vendor/assets/javascripts/can.observe.setter.js +0 -59
- data/vendor/assets/javascripts/can.observe.validations.js +0 -199
- data/vendor/assets/javascripts/can.view.modifiers.js +0 -159
- data/vendor/assets/javascripts/can.view.mustache.js +0 -758
- data/vendor/assets/javascripts/download_canjs.sh +0 -20
@@ -1,59 +0,0 @@
|
|
1
|
-
/*
|
2
|
-
* CanJS - 1.1.2 (2012-11-28)
|
3
|
-
* http://canjs.us/
|
4
|
-
* Copyright (c) 2012 Bitovi
|
5
|
-
* Licensed MIT
|
6
|
-
*/
|
7
|
-
(function (can, window, undefined) {
|
8
|
-
// ## can/observe/setter/setter.js
|
9
|
-
can.classize = function (s, join) {
|
10
|
-
// this can be moved out ..
|
11
|
-
// used for getter setter
|
12
|
-
var parts = s.split(can.undHash),
|
13
|
-
i = 0;
|
14
|
-
for (; i < parts.length; i++) {
|
15
|
-
parts[i] = can.capitalize(parts[i]);
|
16
|
-
}
|
17
|
-
|
18
|
-
return parts.join(join || '');
|
19
|
-
}
|
20
|
-
|
21
|
-
var classize = can.classize,
|
22
|
-
proto = can.Observe.prototype,
|
23
|
-
old = proto.__set;
|
24
|
-
|
25
|
-
proto.__set = function (prop, value, current, success, error) {
|
26
|
-
// check if there's a setter
|
27
|
-
var cap = classize(prop),
|
28
|
-
setName = "set" + cap,
|
29
|
-
errorCallback = function (errors) {
|
30
|
-
var stub = error && error.call(self, errors);
|
31
|
-
|
32
|
-
// if 'setter' is on the page it will trigger
|
33
|
-
// the error itself and we dont want to trigger
|
34
|
-
// the event twice. :)
|
35
|
-
if (stub !== false) {
|
36
|
-
can.trigger(self, "error", [prop, errors], true);
|
37
|
-
}
|
38
|
-
|
39
|
-
return false;
|
40
|
-
},
|
41
|
-
self = this;
|
42
|
-
|
43
|
-
// if we have a setter
|
44
|
-
if (this[setName] &&
|
45
|
-
// call the setter, if returned value is undefined,
|
46
|
-
// this means the setter is async so we
|
47
|
-
// do not call update property and return right away
|
48
|
-
(value = this[setName](value, function () {
|
49
|
-
old.call(self, prop, value, current, success, errorCallback)
|
50
|
-
}, errorCallback)) === undefined) {
|
51
|
-
return;
|
52
|
-
}
|
53
|
-
|
54
|
-
old.call(self, prop, value, current, success, errorCallback);
|
55
|
-
|
56
|
-
return this;
|
57
|
-
};
|
58
|
-
|
59
|
-
})(can, this);
|
@@ -1,199 +0,0 @@
|
|
1
|
-
/*
|
2
|
-
* CanJS - 1.1.2 (2012-11-28)
|
3
|
-
* http://canjs.us/
|
4
|
-
* Copyright (c) 2012 Bitovi
|
5
|
-
* Licensed MIT
|
6
|
-
*/
|
7
|
-
(function (can, window, undefined) {
|
8
|
-
// ## can/observe/validations/validations.js
|
9
|
-
//validations object is by property. You can have validations that
|
10
|
-
//span properties, but this way we know which ones to run.
|
11
|
-
// proc should return true if there's an error or the error message
|
12
|
-
var validate = function (attrNames, options, proc) {
|
13
|
-
// normalize argumetns
|
14
|
-
if (!proc) {
|
15
|
-
proc = options;
|
16
|
-
options = {};
|
17
|
-
}
|
18
|
-
|
19
|
-
options = options || {};
|
20
|
-
attrNames = can.makeArray(attrNames)
|
21
|
-
|
22
|
-
// run testIf if it exists
|
23
|
-
if (options.testIf && !options.testIf.call(this)) {
|
24
|
-
return;
|
25
|
-
}
|
26
|
-
|
27
|
-
var self = this;
|
28
|
-
can.each(attrNames, function (attrName) {
|
29
|
-
// Add a test function for each attribute
|
30
|
-
if (!self.validations[attrName]) {
|
31
|
-
self.validations[attrName] = [];
|
32
|
-
}
|
33
|
-
|
34
|
-
self.validations[attrName].push(function (newVal) {
|
35
|
-
// if options has a message return that, otherwise, return the error
|
36
|
-
var res = proc.call(this, newVal, attrName);
|
37
|
-
return res === undefined ? undefined : (options.message || res);
|
38
|
-
})
|
39
|
-
});
|
40
|
-
};
|
41
|
-
|
42
|
-
var old = can.Observe.prototype.__set;
|
43
|
-
can.Observe.prototype.__set = function (prop, value, current, success, error) {
|
44
|
-
var self = this,
|
45
|
-
validations = self.constructor.validations,
|
46
|
-
errorCallback = function (errors) {
|
47
|
-
var stub = error && error.call(self, errors);
|
48
|
-
|
49
|
-
// if 'setter' is on the page it will trigger
|
50
|
-
// the error itself and we dont want to trigger
|
51
|
-
// the event twice. :)
|
52
|
-
if (stub !== false) {
|
53
|
-
can.trigger(self, "error", [prop, errors], true);
|
54
|
-
}
|
55
|
-
|
56
|
-
return false;
|
57
|
-
};
|
58
|
-
|
59
|
-
old.call(self, prop, value, current, success, errorCallback);
|
60
|
-
|
61
|
-
if (validations && validations[prop]) {
|
62
|
-
var errors = self.errors(prop);
|
63
|
-
errors && errorCallback(errors)
|
64
|
-
}
|
65
|
-
|
66
|
-
return this;
|
67
|
-
}
|
68
|
-
|
69
|
-
can.each([can.Observe, can.Model], function (clss) {
|
70
|
-
// in some cases model might not be defined quite yet.
|
71
|
-
if (clss === undefined) {
|
72
|
-
return;
|
73
|
-
}
|
74
|
-
var oldSetup = clss.setup;
|
75
|
-
|
76
|
-
can.extend(clss, {
|
77
|
-
setup: function (superClass) {
|
78
|
-
oldSetup.apply(this, arguments);
|
79
|
-
if (!this.validations || superClass.validations === this.validations) {
|
80
|
-
this.validations = {};
|
81
|
-
}
|
82
|
-
},
|
83
|
-
|
84
|
-
validate: validate,
|
85
|
-
|
86
|
-
|
87
|
-
validationMessages: {
|
88
|
-
format: "is invalid",
|
89
|
-
inclusion: "is not a valid option (perhaps out of range)",
|
90
|
-
lengthShort: "is too short",
|
91
|
-
lengthLong: "is too long",
|
92
|
-
presence: "can't be empty",
|
93
|
-
range: "is out of range"
|
94
|
-
},
|
95
|
-
|
96
|
-
|
97
|
-
validateFormatOf: function (attrNames, regexp, options) {
|
98
|
-
validate.call(this, attrNames, options, function (value) {
|
99
|
-
if ((typeof value != 'undefined' && value != '') && String(value).match(regexp) == null) {
|
100
|
-
return this.constructor.validationMessages.format;
|
101
|
-
}
|
102
|
-
});
|
103
|
-
},
|
104
|
-
|
105
|
-
|
106
|
-
validateInclusionOf: function (attrNames, inArray, options) {
|
107
|
-
validate.call(this, attrNames, options, function (value) {
|
108
|
-
if (typeof value == 'undefined') {
|
109
|
-
return;
|
110
|
-
}
|
111
|
-
|
112
|
-
if (can.grep(inArray, function (elm) {
|
113
|
-
return (elm == value);
|
114
|
-
}).length == 0) {
|
115
|
-
return this.constructor.validationMessages.inclusion;
|
116
|
-
}
|
117
|
-
});
|
118
|
-
},
|
119
|
-
|
120
|
-
|
121
|
-
validateLengthOf: function (attrNames, min, max, options) {
|
122
|
-
validate.call(this, attrNames, options, function (value) {
|
123
|
-
if ((typeof value == 'undefined' && min > 0) || value.length < min) {
|
124
|
-
return this.constructor.validationMessages.lengthShort + " (min=" + min + ")";
|
125
|
-
} else if (typeof value != 'undefined' && value.length > max) {
|
126
|
-
return this.constructor.validationMessages.lengthLong + " (max=" + max + ")";
|
127
|
-
}
|
128
|
-
});
|
129
|
-
},
|
130
|
-
|
131
|
-
|
132
|
-
validatePresenceOf: function (attrNames, options) {
|
133
|
-
validate.call(this, attrNames, options, function (value) {
|
134
|
-
if (typeof value == 'undefined' || value === "" || value === null) {
|
135
|
-
return this.constructor.validationMessages.presence;
|
136
|
-
}
|
137
|
-
});
|
138
|
-
},
|
139
|
-
|
140
|
-
|
141
|
-
validateRangeOf: function (attrNames, low, hi, options) {
|
142
|
-
validate.call(this, attrNames, options, function (value) {
|
143
|
-
if (typeof value != 'undefined' && value < low || value > hi) {
|
144
|
-
return this.constructor.validationMessages.range + " [" + low + "," + hi + "]";
|
145
|
-
}
|
146
|
-
});
|
147
|
-
}
|
148
|
-
});
|
149
|
-
});
|
150
|
-
|
151
|
-
can.extend(can.Observe.prototype, {
|
152
|
-
|
153
|
-
|
154
|
-
errors: function (attrs, newVal) {
|
155
|
-
// convert attrs to an array
|
156
|
-
if (attrs) {
|
157
|
-
attrs = can.isArray(attrs) ? attrs : [attrs];
|
158
|
-
}
|
159
|
-
|
160
|
-
var errors = {},
|
161
|
-
self = this,
|
162
|
-
attr,
|
163
|
-
// helper function that adds error messages to errors object
|
164
|
-
// attr - the name of the attribute
|
165
|
-
// funcs - the validation functions
|
166
|
-
addErrors = function (attr, funcs) {
|
167
|
-
can.each(funcs, function (func) {
|
168
|
-
var res = func.call(self, isTest ? (self.__convert ? self.__convert(attr, newVal) : newVal) : self[attr]);
|
169
|
-
if (res) {
|
170
|
-
if (!errors[attr]) {
|
171
|
-
errors[attr] = [];
|
172
|
-
}
|
173
|
-
errors[attr].push(res);
|
174
|
-
}
|
175
|
-
|
176
|
-
});
|
177
|
-
},
|
178
|
-
validations = this.constructor.validations,
|
179
|
-
isTest = attrs && attrs.length === 1 && arguments.length === 2;
|
180
|
-
|
181
|
-
// go through each attribute or validation and
|
182
|
-
// add any errors
|
183
|
-
can.each(attrs || validations || {}, function (funcs, attr) {
|
184
|
-
// if we are iterating through an array, use funcs
|
185
|
-
// as the attr name
|
186
|
-
if (typeof attr == 'number') {
|
187
|
-
attr = funcs;
|
188
|
-
funcs = validations[attr];
|
189
|
-
}
|
190
|
-
// add errors to the
|
191
|
-
addErrors(attr, funcs || []);
|
192
|
-
});
|
193
|
-
|
194
|
-
// return errors as long as we have one
|
195
|
-
return can.isEmptyObject(errors) ? null : isTest ? errors[attrs[0]] : errors;
|
196
|
-
}
|
197
|
-
});
|
198
|
-
|
199
|
-
})(can, this);
|
@@ -1,159 +0,0 @@
|
|
1
|
-
/*
|
2
|
-
* CanJS - 1.1.2 (2012-11-28)
|
3
|
-
* http://canjs.us/
|
4
|
-
* Copyright (c) 2012 Bitovi
|
5
|
-
* Licensed MIT
|
6
|
-
*/
|
7
|
-
(function (window, $, can, undefined) {
|
8
|
-
// ## can/view/modifiers/modifiers.js
|
9
|
-
//---- ADD jQUERY HELPERS -----
|
10
|
-
//converts jquery functions to use views
|
11
|
-
var convert, modify, isTemplate, isHTML, isDOM, getCallback,
|
12
|
-
// text and val cannot produce an element, so don't run hookups on them
|
13
|
-
noHookup = {
|
14
|
-
'val': true,
|
15
|
-
'text': true
|
16
|
-
};
|
17
|
-
|
18
|
-
convert = function (func_name) {
|
19
|
-
// save the old jQuery helper
|
20
|
-
var old = $.fn[func_name];
|
21
|
-
|
22
|
-
// replace it with our new helper
|
23
|
-
$.fn[func_name] = function () {
|
24
|
-
|
25
|
-
var args = can.makeArray(arguments),
|
26
|
-
callbackNum, callback, self = this,
|
27
|
-
result;
|
28
|
-
|
29
|
-
// if the first arg is a deferred
|
30
|
-
// wait until it finishes, and call
|
31
|
-
// modify with the result
|
32
|
-
if (can.isDeferred(args[0])) {
|
33
|
-
args[0].done(function (res) {
|
34
|
-
modify.call(self, [res], old);
|
35
|
-
})
|
36
|
-
return this;
|
37
|
-
}
|
38
|
-
//check if a template
|
39
|
-
else if (isTemplate(args)) {
|
40
|
-
|
41
|
-
// if we should operate async
|
42
|
-
if ((callbackNum = getCallback(args))) {
|
43
|
-
callback = args[callbackNum];
|
44
|
-
args[callbackNum] = function (result) {
|
45
|
-
modify.call(self, [result], old);
|
46
|
-
callback.call(self, result);
|
47
|
-
};
|
48
|
-
can.view.apply(can.view, args);
|
49
|
-
return this;
|
50
|
-
}
|
51
|
-
// call view with args (there might be deferreds)
|
52
|
-
result = can.view.apply(can.view, args);
|
53
|
-
|
54
|
-
// if we got a string back
|
55
|
-
if (!can.isDeferred(result)) {
|
56
|
-
// we are going to call the old method with that string
|
57
|
-
args = [result];
|
58
|
-
} else {
|
59
|
-
// if there is a deferred, wait until it is done before calling modify
|
60
|
-
result.done(function (res) {
|
61
|
-
modify.call(self, [res], old);
|
62
|
-
})
|
63
|
-
return this;
|
64
|
-
}
|
65
|
-
}
|
66
|
-
return noHookup[func_name] ? old.apply(this, args) : modify.call(this, args, old);
|
67
|
-
};
|
68
|
-
};
|
69
|
-
|
70
|
-
// modifies the content of the element
|
71
|
-
// but also will run any hookup
|
72
|
-
modify = function (args, old) {
|
73
|
-
var res, stub, hooks;
|
74
|
-
|
75
|
-
//check if there are new hookups
|
76
|
-
for (var hasHookups in can.view.hookups) {
|
77
|
-
break;
|
78
|
-
}
|
79
|
-
|
80
|
-
//if there are hookups, turn into a frag
|
81
|
-
// and insert that
|
82
|
-
// by using a frag, the element can be recursively hooked up
|
83
|
-
// before insterion
|
84
|
-
if (hasHookups && args[0] && isHTML(args[0])) {
|
85
|
-
args[0] = can.view.frag(args[0]).childNodes;
|
86
|
-
}
|
87
|
-
|
88
|
-
//then insert into DOM
|
89
|
-
res = old.apply(this, args);
|
90
|
-
|
91
|
-
return res;
|
92
|
-
};
|
93
|
-
|
94
|
-
// returns true or false if the args indicate a template is being used
|
95
|
-
// $('#foo').html('/path/to/template.ejs',{data})
|
96
|
-
// in general, we want to make sure the first arg is a string
|
97
|
-
// and the second arg is data
|
98
|
-
isTemplate = function (args) {
|
99
|
-
// save the second arg type
|
100
|
-
var secArgType = typeof args[1];
|
101
|
-
|
102
|
-
// the first arg is a string
|
103
|
-
return typeof args[0] == "string" &&
|
104
|
-
// the second arg is an object or function
|
105
|
-
(secArgType == 'object' || secArgType == 'function') &&
|
106
|
-
// but it is not a dom element
|
107
|
-
!isDOM(args[1]);
|
108
|
-
};
|
109
|
-
// returns true if the arg is a jQuery object or HTMLElement
|
110
|
-
isDOM = function (arg) {
|
111
|
-
return arg.nodeType || (arg[0] && arg[0].nodeType)
|
112
|
-
};
|
113
|
-
// returns whether the argument is some sort of HTML data
|
114
|
-
isHTML = function (arg) {
|
115
|
-
if (isDOM(arg)) {
|
116
|
-
// if jQuery object or DOM node we're good
|
117
|
-
return true;
|
118
|
-
} else if (typeof arg === "string") {
|
119
|
-
// if string, do a quick sanity check that we're HTML
|
120
|
-
arg = can.trim(arg);
|
121
|
-
return arg.substr(0, 1) === "<" && arg.substr(arg.length - 1, 1) === ">" && arg.length >= 3;
|
122
|
-
} else {
|
123
|
-
// don't know what you are
|
124
|
-
return false;
|
125
|
-
}
|
126
|
-
};
|
127
|
-
|
128
|
-
//returns the callback arg number if there is one (for async view use)
|
129
|
-
getCallback = function (args) {
|
130
|
-
return typeof args[3] === 'function' ? 3 : typeof args[2] === 'function' && 2;
|
131
|
-
};
|
132
|
-
|
133
|
-
|
134
|
-
$.fn.hookup = function () {
|
135
|
-
can.view.frag(this);
|
136
|
-
return this;
|
137
|
-
};
|
138
|
-
|
139
|
-
|
140
|
-
can.each([
|
141
|
-
|
142
|
-
"prepend",
|
143
|
-
|
144
|
-
"append",
|
145
|
-
|
146
|
-
"after",
|
147
|
-
|
148
|
-
"before",
|
149
|
-
|
150
|
-
"text",
|
151
|
-
|
152
|
-
"html",
|
153
|
-
|
154
|
-
"replaceWith", "val"], function (func) {
|
155
|
-
convert(func);
|
156
|
-
});
|
157
|
-
|
158
|
-
|
159
|
-
})(this, jQuery, can);
|
@@ -1,758 +0,0 @@
|
|
1
|
-
/*
|
2
|
-
* CanJS - 1.1.2 (2012-11-28)
|
3
|
-
* http://canjs.us/
|
4
|
-
* Copyright (c) 2012 Bitovi
|
5
|
-
* Licensed MIT
|
6
|
-
*/
|
7
|
-
(function (can, window, undefined) {
|
8
|
-
// ## can/view/mustache/mustache.js
|
9
|
-
|
10
|
-
// # mustache.js
|
11
|
-
// `can.Mustache`: The Mustache templating engine.
|
12
|
-
// See the [Transformation](#section-29) section within *Scanning Helpers* for a detailed explanation
|
13
|
-
// of the runtime render code design. The majority of the Mustache engine implementation
|
14
|
-
// occurs within the *Transformation* scanning helper.
|
15
|
-
// ## Initialization
|
16
|
-
// Define the view extension.
|
17
|
-
can.view.ext = ".mustache";
|
18
|
-
|
19
|
-
// ### Setup internal helper variables and functions.
|
20
|
-
// An alias for the context variable used for tracking a stack of contexts.
|
21
|
-
// This is also used for passing to helper functions to maintain proper context.
|
22
|
-
var CONTEXT = '___c0nt3xt',
|
23
|
-
// An alias for the variable used for the hash object that can be passed
|
24
|
-
// to helpers via `options.hash`.
|
25
|
-
HASH = '___h4sh',
|
26
|
-
// An alias for the function that adds a new context to the context stack.
|
27
|
-
STACK = '___st4ck',
|
28
|
-
// An alias for the most used context stacking call.
|
29
|
-
CONTEXT_STACK = STACK + '(' + CONTEXT + ',this)',
|
30
|
-
|
31
|
-
|
32
|
-
isObserve = function (obj) {
|
33
|
-
return can.isFunction(obj.attr) && obj.constructor && !! obj.constructor.canMakeObserve;
|
34
|
-
},
|
35
|
-
|
36
|
-
|
37
|
-
isArrayLike = function (obj) {
|
38
|
-
return obj && obj.splice && typeof obj.length == 'number';
|
39
|
-
},
|
40
|
-
|
41
|
-
// ## Mustache
|
42
|
-
Mustache = function (options) {
|
43
|
-
// Support calling Mustache without the constructor.
|
44
|
-
// This returns a function that renders the template.
|
45
|
-
if (this.constructor != Mustache) {
|
46
|
-
var mustache = new Mustache(options);
|
47
|
-
return function (data) {
|
48
|
-
return mustache.render(data);
|
49
|
-
};
|
50
|
-
}
|
51
|
-
|
52
|
-
// If we get a `function` directly, it probably is coming from
|
53
|
-
// a `steal`-packaged view.
|
54
|
-
if (typeof options == "function") {
|
55
|
-
this.template = {
|
56
|
-
fn: options
|
57
|
-
};
|
58
|
-
return;
|
59
|
-
}
|
60
|
-
|
61
|
-
// Set options on self.
|
62
|
-
can.extend(this, options);
|
63
|
-
this.template = this.scanner.scan(this.text, this.name);
|
64
|
-
};
|
65
|
-
|
66
|
-
|
67
|
-
// Put Mustache on the `can` object.
|
68
|
-
can.Mustache = window.Mustache = Mustache;
|
69
|
-
|
70
|
-
|
71
|
-
Mustache.prototype.
|
72
|
-
|
73
|
-
render = function (object, extraHelpers) {
|
74
|
-
object = object || {};
|
75
|
-
return this.template.fn.call(object, object, {
|
76
|
-
_data: object
|
77
|
-
});
|
78
|
-
};
|
79
|
-
|
80
|
-
can.extend(Mustache.prototype, {
|
81
|
-
// Share a singleton scanner for parsing templates.
|
82
|
-
scanner: new can.view.Scanner({
|
83
|
-
// A hash of strings for the scanner to inject at certain points.
|
84
|
-
text: {
|
85
|
-
// This is the logic to inject at the beginning of a rendered template.
|
86
|
-
// This includes initializing the `context` stack.
|
87
|
-
start: 'var ' + CONTEXT + ' = []; ' + CONTEXT + '.' + STACK + ' = true;' + 'var ' + STACK + ' = function(context, self) {' + 'var s;' + 'if (arguments.length == 1 && context) {' + 's = !context.' + STACK + ' ? [context] : context;' + '} else {' + 's = context && context.' + STACK + ' ? context.concat([self]) : ' + STACK + '(context).concat([self]);' + '}' + 'return (s.' + STACK + ' = true) && s;' + '};'
|
88
|
-
},
|
89
|
-
|
90
|
-
// An ordered token registry for the scanner.
|
91
|
-
// This needs to be ordered by priority to prevent token parsing errors.
|
92
|
-
// Each token follows the following structure:
|
93
|
-
// [
|
94
|
-
// // Which key in the token map to match.
|
95
|
-
// "tokenMapName",
|
96
|
-
// // A simple token to match, like "{{".
|
97
|
-
// "token",
|
98
|
-
// // Optional. A complex (regexp) token to match that
|
99
|
-
// // overrides the simple token.
|
100
|
-
// "[\\s\\t]*{{",
|
101
|
-
// // Optional. A function that executes advanced
|
102
|
-
// // manipulation of the matched content. This is
|
103
|
-
// // rarely used.
|
104
|
-
// function(content){
|
105
|
-
// return content;
|
106
|
-
// }
|
107
|
-
// ]
|
108
|
-
tokens: [
|
109
|
-
// Return unescaped
|
110
|
-
["returnLeft", "{{{", "{{[{&]"],
|
111
|
-
// Full line comments
|
112
|
-
["commentFull", "{{!}}", "^[\\s\\t]*{{!.+?}}\\n"],
|
113
|
-
// Inline comments
|
114
|
-
["commentLeft", "{{!", "(\\n[\\s\\t]*{{!|{{!)"],
|
115
|
-
// Full line escapes
|
116
|
-
// This is used for detecting lines with only whitespace and an escaped tag
|
117
|
-
["escapeFull", "{{}}", "(^[\\s\\t]*{{[#/^][^}]+?}}\\n|\\n[\\s\\t]*{{[#/^][^}]+?}}\\n|\\n[\\s\\t]*{{[#/^][^}]+?}}$)", function (content) {
|
118
|
-
return {
|
119
|
-
before: /^\n.+?\n$/.test(content) ? '\n' : '',
|
120
|
-
content: content.match(/\{\{(.+?)\}\}/)[1] || ''
|
121
|
-
};
|
122
|
-
}],
|
123
|
-
// Return escaped
|
124
|
-
["escapeLeft", "{{"],
|
125
|
-
// Close return unescaped
|
126
|
-
["returnRight", "}}}"],
|
127
|
-
// Close tag
|
128
|
-
["right", "}}"]],
|
129
|
-
|
130
|
-
// ## Scanning Helpers
|
131
|
-
// This is an array of helpers that transform content that is within escaped tags like `{{token}}`. These helpers are solely for the scanning phase; they are unrelated to Mustache/Handlebars helpers which execute at render time. Each helper has a definition like the following:
|
132
|
-
// {
|
133
|
-
// // The content pattern to match in order to execute.
|
134
|
-
// // Only the first matching helper is executed.
|
135
|
-
// name: /pattern to match/,
|
136
|
-
// // The function to transform the content with.
|
137
|
-
// // @param {String} content The content to transform.
|
138
|
-
// // @param {Object} cmd Scanner helper data.
|
139
|
-
// // {
|
140
|
-
// // insert: "insert command",
|
141
|
-
// // tagName: "div",
|
142
|
-
// // status: 0
|
143
|
-
// // }
|
144
|
-
// fn: function(content, cmd) {
|
145
|
-
// return 'for text injection' ||
|
146
|
-
// { raw: 'to bypass text injection' };
|
147
|
-
// }
|
148
|
-
// }
|
149
|
-
helpers: [
|
150
|
-
// ### Partials
|
151
|
-
// Partials begin with a greater than sign, like {{> box}}.
|
152
|
-
// Partials are rendered at runtime (as opposed to compile time),
|
153
|
-
// so recursive partials are possible. Just avoid infinite loops.
|
154
|
-
// For example, this template and partial:
|
155
|
-
// base.mustache:
|
156
|
-
// <h2>Names</h2>
|
157
|
-
// {{#names}}
|
158
|
-
// {{> user}}
|
159
|
-
// {{/names}}
|
160
|
-
// user.mustache:
|
161
|
-
// <strong>{{name}}</strong>
|
162
|
-
{
|
163
|
-
name: /^>[\s|\w]\w*/,
|
164
|
-
fn: function (content, cmd) {
|
165
|
-
// Get the template name and call back into the render method,
|
166
|
-
// passing the name and the current context.
|
167
|
-
var templateName = can.trim(content.replace(/^>\s?/, ''));
|
168
|
-
return "can.view.render('" + templateName + "', " + CONTEXT_STACK + ".pop())";
|
169
|
-
}
|
170
|
-
},
|
171
|
-
|
172
|
-
// ### Data Hookup
|
173
|
-
// This will attach the data property of `this` to the element
|
174
|
-
// its found on using the first argument as the data attribute
|
175
|
-
// key.
|
176
|
-
// For example:
|
177
|
-
// <li id="nameli" {{ data 'name' }}></li>
|
178
|
-
// then later you can access it like:
|
179
|
-
// can.$('#nameli').data('name');
|
180
|
-
{
|
181
|
-
name: /^\s?data\s/,
|
182
|
-
fn: function (content, cmd) {
|
183
|
-
var attr = content.replace(/(^\s?data\s)|(["'])/g, '');
|
184
|
-
|
185
|
-
// return a function which calls `can.data` on the element
|
186
|
-
// with the attribute name with the current context.
|
187
|
-
return "can.proxy(function(__){can.data(can.$(__),'" + attr + "', this.pop()); }, " + CONTEXT_STACK + ")";
|
188
|
-
}
|
189
|
-
},
|
190
|
-
|
191
|
-
// ### Transformation (default)
|
192
|
-
// This transforms all content to its interpolated equivalent,
|
193
|
-
// including calls to the corresponding helpers as applicable.
|
194
|
-
// This outputs the render code for almost all cases.
|
195
|
-
// #### Definitions
|
196
|
-
// * `context` - This is the object that the current rendering context operates within.
|
197
|
-
// Each nested template adds a new `context` to the context stack.
|
198
|
-
// * `stack` - Mustache supports nested sections,
|
199
|
-
// each of which add their own context to a stack of contexts.
|
200
|
-
// Whenever a token gets interpolated, it will check for a match against the
|
201
|
-
// last context in the stack, then iterate through the rest of the stack checking for matches.
|
202
|
-
// The first match is the one that gets returned.
|
203
|
-
// * `Mustache.txt` - This serializes a collection of logic, optionally contained within a section.
|
204
|
-
// If this is a simple interpolation, only the interpolation lookup will be passed.
|
205
|
-
// If this is a section, then an `options` object populated by the truthy (`options.fn`) and
|
206
|
-
// falsey (`options.inverse`) encapsulated functions will also be passed. This section handling
|
207
|
-
// exists to support the runtime context nesting that Mustache supports.
|
208
|
-
// * `Mustache.get` - This resolves an interpolation reference given a stack of contexts.
|
209
|
-
// * `options` - An object containing methods for executing the inner contents of sections or helpers.
|
210
|
-
// `options.fn` - Contains the inner template logic for a truthy section.
|
211
|
-
// `options.inverse` - Contains the inner template logic for a falsey section.
|
212
|
-
// `options.hash` - Contains the merged hash object argument for custom helpers.
|
213
|
-
// #### Design
|
214
|
-
// This covers the design of the render code that the transformation helper generates.
|
215
|
-
// ##### Pseudocode
|
216
|
-
// A detailed explanation is provided in the following sections, but here is some brief pseudocode
|
217
|
-
// that gives a high level overview of what the generated render code does (with a template similar to
|
218
|
-
// `"{{#a}}{{b.c.d.e.name}}{{/a}}" == "Phil"`).
|
219
|
-
// *Initialize the render code.*
|
220
|
-
// view = []
|
221
|
-
// context = []
|
222
|
-
// stack = fn { context.concat([this]) }
|
223
|
-
// *Render the root section.*
|
224
|
-
// view.push( "string" )
|
225
|
-
// view.push( can.view.txt(
|
226
|
-
// *Render the nested section with `can.Mustache.txt`.*
|
227
|
-
// txt(
|
228
|
-
// *Add the current context to the stack.*
|
229
|
-
// stack(),
|
230
|
-
// *Flag this for truthy section mode.*
|
231
|
-
// "#",
|
232
|
-
// *Interpolate and check the `a` variable for truthyness using the stack with `can.Mustache.get`.*
|
233
|
-
// get( "a", stack() ),
|
234
|
-
// *Include the nested section's inner logic.
|
235
|
-
// The stack argument is usually the parent section's copy of the stack,
|
236
|
-
// but it can be an override context that was passed by a custom helper.
|
237
|
-
// Sections can nest `0..n` times -- **NESTCEPTION**.*
|
238
|
-
// { fn: fn(stack) {
|
239
|
-
// *Render the nested section (everything between the `{{#a}}` and `{{/a}}` tokens).*
|
240
|
-
// view = []
|
241
|
-
// view.push( "string" )
|
242
|
-
// view.push(
|
243
|
-
// *Add the current context to the stack.*
|
244
|
-
// stack(),
|
245
|
-
// *Flag this as interpolation-only mode.*
|
246
|
-
// null,
|
247
|
-
// *Interpolate the `b.c.d.e.name` variable using the stack.*
|
248
|
-
// get( "b.c.d.e.name", stack() ),
|
249
|
-
// )
|
250
|
-
// view.push( "string" )
|
251
|
-
// *Return the result for the nested section.*
|
252
|
-
// return view.join()
|
253
|
-
// }}
|
254
|
-
// )
|
255
|
-
// ))
|
256
|
-
// view.push( "string" )
|
257
|
-
// *Return the result for the root section, which includes all nested sections.*
|
258
|
-
// return view.join()
|
259
|
-
// ##### Initialization
|
260
|
-
// Each rendered template is started with the following initialization code:
|
261
|
-
// var ___v1ew = [];
|
262
|
-
// var ___c0nt3xt = [];
|
263
|
-
// ___c0nt3xt.___st4ck = true;
|
264
|
-
// var ___st4ck = function(context, self) {
|
265
|
-
// var s;
|
266
|
-
// if (arguments.length == 1 && context) {
|
267
|
-
// s = !context.___st4ck ? [context] : context;
|
268
|
-
// } else {
|
269
|
-
// s = context && context.___st4ck
|
270
|
-
// ? context.concat([self])
|
271
|
-
// : ___st4ck(context).concat([self]);
|
272
|
-
// }
|
273
|
-
// return (s.___st4ck = true) && s;
|
274
|
-
// };
|
275
|
-
// The `___v1ew` is the the array used to serialize the view.
|
276
|
-
// The `___c0nt3xt` is a stacking array of contexts that slices and expands with each nested section.
|
277
|
-
// The `___st4ck` function is used to more easily update the context stack in certain situations.
|
278
|
-
// Usually, the stack function simply adds a new context (`self`/`this`) to a context stack.
|
279
|
-
// However, custom helpers will occasionally pass override contexts that need their own context stack.
|
280
|
-
// ##### Sections
|
281
|
-
// Each section, `{{#section}} content {{/section}}`, within a Mustache template generates a section
|
282
|
-
// context in the resulting render code. The template itself is treated like a root section, with the
|
283
|
-
// same execution logic as any others. Each section can have `0..n` nested sections within it.
|
284
|
-
// Here's an example of a template without any descendent sections.
|
285
|
-
// Given the template: `"{{a.b.c.d.e.name}}" == "Phil"`
|
286
|
-
// Would output the following render code:
|
287
|
-
// ___v1ew.push("\"");
|
288
|
-
// ___v1ew.push(can.view.txt(1, '', 0, this, function() {
|
289
|
-
// return can.Mustache.txt(___st4ck(___c0nt3xt, this), null,
|
290
|
-
// can.Mustache.get("a.b.c.d.e.name",
|
291
|
-
// ___st4ck(___c0nt3xt, this))
|
292
|
-
// );
|
293
|
-
// }));
|
294
|
-
// ___v1ew.push("\" == \"Phil\"");
|
295
|
-
// The simple strings will get appended to the view. Any interpolated references (like `{{a.b.c.d.e.name}}`)
|
296
|
-
// will be pushed onto the view via `can.view.txt` in order to support live binding.
|
297
|
-
// The function passed to `can.view.txt` will call `can.Mustache.txt`, which serializes the object data by doing
|
298
|
-
// a context lookup with `can.Mustache.get`.
|
299
|
-
// `can.Mustache.txt`'s first argument is a copy of the context stack with the local context `this` added to it.
|
300
|
-
// This stack will grow larger as sections nest.
|
301
|
-
// The second argument is for the section type. This will be `"#"` for truthy sections, `"^"` for falsey,
|
302
|
-
// or `null` if it is an interpolation instead of a section.
|
303
|
-
// The third argument is the interpolated value retrieved with `can.Mustache.get`, which will perform the
|
304
|
-
// context lookup and return the approriate string or object.
|
305
|
-
// Any additional arguments, if they exist, are used for passing arguments to custom helpers.
|
306
|
-
// For nested sections, the last argument is an `options` object that contains the nested section's logic.
|
307
|
-
// Here's an example of a template with a single nested section.
|
308
|
-
// Given the template: `"{{#a}}{{b.c.d.e.name}}{{/a}}" == "Phil"`
|
309
|
-
// Would output the following render code:
|
310
|
-
// ___v1ew.push("\"");
|
311
|
-
// ___v1ew.push(can.view.txt(0, '', 0, this, function() {
|
312
|
-
// return can.Mustache.txt(___st4ck(___c0nt3xt, this), "#",
|
313
|
-
// can.Mustache.get("a", ___st4ck(___c0nt3xt, this)),
|
314
|
-
// [{
|
315
|
-
// _: function() {
|
316
|
-
// return ___v1ew.join("");
|
317
|
-
// }
|
318
|
-
// }, {
|
319
|
-
// fn: function(___c0nt3xt) {
|
320
|
-
// var ___v1ew = [];
|
321
|
-
// ___v1ew.push(can.view.txt(1, '', 0, this,
|
322
|
-
// function() {
|
323
|
-
// return can.Mustache.txt(
|
324
|
-
// ___st4ck(___c0nt3xt, this),
|
325
|
-
// null,
|
326
|
-
// can.Mustache.get("b.c.d.e.name",
|
327
|
-
// ___st4ck(___c0nt3xt, this))
|
328
|
-
// );
|
329
|
-
// }
|
330
|
-
// ));
|
331
|
-
// return ___v1ew.join("");
|
332
|
-
// }
|
333
|
-
// }]
|
334
|
-
// )
|
335
|
-
// }));
|
336
|
-
// ___v1ew.push("\" == \"Phil\"");
|
337
|
-
// This is specified as a truthy section via the `"#"` argument. The last argument includes an array of helper methods used with `options`.
|
338
|
-
// These act similarly to custom helpers: `options.fn` will be called for truthy sections, `options.inverse` will be called for falsey sections.
|
339
|
-
// The `options._` function only exists as a dummy function to make generating the section nesting easier (a section may have a `fn`, `inverse`,
|
340
|
-
// or both, but there isn't any way to determine that at compilation time).
|
341
|
-
// Within the `fn` function is the section's render context, which in this case will render anything between the `{{#a}}` and `{{/a}}` tokens.
|
342
|
-
// This function has `___c0nt3xt` as an argument because custom helpers can pass their own override contexts. For any case where custom helpers
|
343
|
-
// aren't used, `___c0nt3xt` will be equivalent to the `___st4ck(___c0nt3xt, this)` stack created by its parent section. The `inverse` function
|
344
|
-
// works similarly, except that it is added when `{{^a}}` and `{{else}}` are used. `var ___v1ew = []` is specified in `fn` and `inverse` to
|
345
|
-
// ensure that live binding in nested sections works properly.
|
346
|
-
// All of these nested sections will combine to return a compiled string that functions similar to EJS in its uses of `can.view.txt`.
|
347
|
-
// #### Implementation
|
348
|
-
{
|
349
|
-
name: /^.*$/,
|
350
|
-
fn: function (content, cmd) {
|
351
|
-
var mode = false,
|
352
|
-
result = [];
|
353
|
-
|
354
|
-
// Trim the content so we don't have any trailing whitespace.
|
355
|
-
content = can.trim(content);
|
356
|
-
|
357
|
-
// Determine what the active mode is.
|
358
|
-
// * `#` - Truthy section
|
359
|
-
// * `^` - Falsey section
|
360
|
-
// * `/` - Close the prior section
|
361
|
-
// * `else` - Inverted section (only exists within a truthy/falsey section)
|
362
|
-
if (content.length && (mode = content.match(/^([#^/]|else$)/))) {
|
363
|
-
mode = mode[0];
|
364
|
-
switch (mode) {
|
365
|
-
// Open a new section.
|
366
|
-
case '#':
|
367
|
-
case '^':
|
368
|
-
result.push(cmd.insert + 'can.view.txt(0,\'' + cmd.tagName + '\',' + cmd.status + ',this,function(){ return ');
|
369
|
-
break;
|
370
|
-
// Close the prior section.
|
371
|
-
case '/':
|
372
|
-
return {
|
373
|
-
raw: 'return ___v1ew.join("");}}])}));'
|
374
|
-
};
|
375
|
-
break;
|
376
|
-
}
|
377
|
-
|
378
|
-
// Trim the mode off of the content.
|
379
|
-
content = content.substring(1);
|
380
|
-
}
|
381
|
-
|
382
|
-
// `else` helpers are special and should be skipped since they don't
|
383
|
-
// have any logic aside from kicking off an `inverse` function.
|
384
|
-
if (mode != 'else') {
|
385
|
-
var args = [],
|
386
|
-
i = 0,
|
387
|
-
hashing = false,
|
388
|
-
arg, split, m;
|
389
|
-
|
390
|
-
// Parse the helper arguments.
|
391
|
-
// This needs uses this method instead of a split(/\s/) so that
|
392
|
-
// strings with spaces can be correctly parsed.
|
393
|
-
(can.trim(content) + ' ').replace(/((([^\s]+?=)?('.*?'|".*?"))|.*?)\s/g, function (whole, part) {
|
394
|
-
args.push(part);
|
395
|
-
});
|
396
|
-
|
397
|
-
// Start the content render block.
|
398
|
-
result.push('can.Mustache.txt(' + CONTEXT_STACK + ',' + (mode ? '"' + mode + '"' : 'null') + ',');
|
399
|
-
|
400
|
-
// Iterate through the helper arguments, if there are any.
|
401
|
-
for (; arg = args[i]; i++) {
|
402
|
-
i && result.push(',');
|
403
|
-
|
404
|
-
// Check for special helper arguments (string/number/boolean/hashes).
|
405
|
-
if (i && (m = arg.match(/^(('.*?'|".*?"|[0-9.]+|true|false)|((.+?)=(('.*?'|".*?"|[0-9.]+|true|false)|(.+))))$/))) {
|
406
|
-
// Found a native type like string/number/boolean.
|
407
|
-
if (m[2]) {
|
408
|
-
result.push(m[0]);
|
409
|
-
}
|
410
|
-
// Found a hash object.
|
411
|
-
else {
|
412
|
-
// Open the hash object.
|
413
|
-
if (!hashing) {
|
414
|
-
hashing = true;
|
415
|
-
result.push('{' + HASH + ':{');
|
416
|
-
}
|
417
|
-
|
418
|
-
// Add the key/value.
|
419
|
-
result.push(m[4], ':', m[6] ? m[6] : 'can.Mustache.get("' + m[5].replace(/"/g, '\\"') + '",' + CONTEXT_STACK + ')');
|
420
|
-
|
421
|
-
// Close the hash if this was the last argument.
|
422
|
-
if (i == args.length - 1) {
|
423
|
-
result.push('}}');
|
424
|
-
}
|
425
|
-
}
|
426
|
-
}
|
427
|
-
// Otherwise output a normal interpolation reference.
|
428
|
-
else {
|
429
|
-
result.push('can.Mustache.get("' +
|
430
|
-
// Include the reference name.
|
431
|
-
arg.replace(/"/g, '\\"') + '",' +
|
432
|
-
// Then the stack of context.
|
433
|
-
CONTEXT_STACK +
|
434
|
-
// Flag as a helper method to aid performance,
|
435
|
-
// if it is a known helper (anything with > 0 arguments).
|
436
|
-
(i == 0 && args.length > 1 ? ',true' : '') + ')');
|
437
|
-
}
|
438
|
-
}
|
439
|
-
}
|
440
|
-
|
441
|
-
// Create an option object for sections of code.
|
442
|
-
mode && mode != 'else' && result.push(',[{_:function(){');
|
443
|
-
switch (mode) {
|
444
|
-
// Truthy section
|
445
|
-
case '#':
|
446
|
-
result.push('return ___v1ew.join("");}},{fn:function(' + CONTEXT + '){var ___v1ew = [];');
|
447
|
-
break;
|
448
|
-
// If/else section
|
449
|
-
// Falsey section
|
450
|
-
case 'else':
|
451
|
-
case '^':
|
452
|
-
result.push('return ___v1ew.join("");}},{inverse:function(' + CONTEXT + '){var ___v1ew = [];');
|
453
|
-
break;
|
454
|
-
// Not a section
|
455
|
-
default:
|
456
|
-
result.push(');');
|
457
|
-
break;
|
458
|
-
}
|
459
|
-
|
460
|
-
// Return a raw result if there was a section, otherwise return the default string.
|
461
|
-
result = result.join('');
|
462
|
-
return mode ? {
|
463
|
-
raw: result
|
464
|
-
} : result;
|
465
|
-
}
|
466
|
-
}]
|
467
|
-
})
|
468
|
-
});
|
469
|
-
|
470
|
-
// Add in default scanner helpers first.
|
471
|
-
// We could probably do this differently if we didn't 'break' on every match.
|
472
|
-
var helpers = can.view.Scanner.prototype.helpers;
|
473
|
-
for (var i = 0; i < helpers.length; i++) {
|
474
|
-
Mustache.prototype.scanner.helpers.unshift(helpers[i]);
|
475
|
-
};
|
476
|
-
|
477
|
-
|
478
|
-
Mustache.txt = function (context, mode, name) {
|
479
|
-
// Grab the extra arguments to pass to helpers.
|
480
|
-
var args = Array.prototype.slice.call(arguments, 3),
|
481
|
-
// Create a default `options` object to pass to the helper.
|
482
|
-
options = can.extend.apply(can, [{
|
483
|
-
fn: function () {},
|
484
|
-
inverse: function () {}
|
485
|
-
}].concat(mode ? args.pop() : [])),
|
486
|
-
// An array of arguments to check for truthyness when evaluating sections.
|
487
|
-
validArgs = args.length ? args : [name],
|
488
|
-
// Whether the arguments meet the condition of the section.
|
489
|
-
valid = true,
|
490
|
-
result = [],
|
491
|
-
i, helper;
|
492
|
-
|
493
|
-
// Validate the arguments based on the section mode.
|
494
|
-
if (mode) {
|
495
|
-
for (i = 0; i < validArgs.length; i++) {
|
496
|
-
// Array-like objects are falsey if their length = 0.
|
497
|
-
if (isArrayLike(validArgs[i])) {
|
498
|
-
valid = mode == '#' ? valid && !! validArgs[i].length : mode == '^' ? valid && !validArgs[i].length : valid;
|
499
|
-
}
|
500
|
-
// Otherwise just check if it is truthy or not.
|
501
|
-
else {
|
502
|
-
valid = mode == '#' ? valid && !! validArgs[i] : mode == '^' ? valid && !validArgs[i] : valid;
|
503
|
-
}
|
504
|
-
}
|
505
|
-
}
|
506
|
-
|
507
|
-
// Check for a registered helper or a helper-like function.
|
508
|
-
if (helper = (Mustache.getHelper(name) || (can.isFunction(name) && {
|
509
|
-
fn: name
|
510
|
-
}))) {
|
511
|
-
// Use the most recent context as `this` for the helper.
|
512
|
-
var context = (context[STACK] && context[context.length - 1]) || context,
|
513
|
-
// Update the options with a function/inverse (the inner templates of a section).
|
514
|
-
opts = {
|
515
|
-
fn: can.proxy(options.fn, context),
|
516
|
-
inverse: can.proxy(options.inverse, context)
|
517
|
-
},
|
518
|
-
lastArg = args[args.length - 1];
|
519
|
-
|
520
|
-
// Add the hash to `options` if one exists
|
521
|
-
if (lastArg && lastArg[HASH]) {
|
522
|
-
opts.hash = args.pop()[HASH];
|
523
|
-
}
|
524
|
-
args.push(opts);
|
525
|
-
|
526
|
-
// Call the helper.
|
527
|
-
return helper.fn.apply(context, args) || '';
|
528
|
-
}
|
529
|
-
|
530
|
-
// Otherwise interpolate like normal.
|
531
|
-
if (valid) {
|
532
|
-
switch (mode) {
|
533
|
-
// Truthy section.
|
534
|
-
case '#':
|
535
|
-
// Iterate over arrays
|
536
|
-
if (isArrayLike(name)) {
|
537
|
-
for (i = 0; i < name.length; i++) {
|
538
|
-
result.push(options.fn.call(name[i] || {}, context) || '');
|
539
|
-
}
|
540
|
-
return result.join('');
|
541
|
-
}
|
542
|
-
// Normal case.
|
543
|
-
else {
|
544
|
-
return options.fn.call(name || {}, context) || '';
|
545
|
-
}
|
546
|
-
break;
|
547
|
-
// Falsey section.
|
548
|
-
case '^':
|
549
|
-
return options.inverse.call(name || {}, context) || '';
|
550
|
-
break;
|
551
|
-
default:
|
552
|
-
// Add + '' to convert things like numbers to strings.
|
553
|
-
// This can cause issues if you are trying to
|
554
|
-
// eval on the length but this is the more
|
555
|
-
// common case.
|
556
|
-
return '' + (name !== undefined ? name : '');
|
557
|
-
break;
|
558
|
-
}
|
559
|
-
}
|
560
|
-
|
561
|
-
return '';
|
562
|
-
};
|
563
|
-
|
564
|
-
|
565
|
-
Mustache.get = function (ref, contexts, isHelper) {
|
566
|
-
// Split the reference (like `a.b.c`) into an array of key names.
|
567
|
-
var names = ref.split('.'),
|
568
|
-
// Assume the local object is the last context in the stack.
|
569
|
-
obj = contexts[contexts.length - 1],
|
570
|
-
// Assume the parent context is the second to last context in the stack.
|
571
|
-
context = contexts[contexts.length - 2],
|
572
|
-
lastValue, value, name, i, j;
|
573
|
-
|
574
|
-
// Handle `this` references for list iteration: {{.}} or {{this}}
|
575
|
-
if (/^\.|this$/.test(ref)) {
|
576
|
-
// If context isn't an object, then it was a value passed by a helper so use it as an override.
|
577
|
-
if (!/^object|undefined$/.test(typeof context)) {
|
578
|
-
return context || '';
|
579
|
-
}
|
580
|
-
// Otherwise just return the closest object.
|
581
|
-
else {
|
582
|
-
while (value = contexts.pop()) {
|
583
|
-
if (typeof value !== 'undefined') {
|
584
|
-
return value;
|
585
|
-
}
|
586
|
-
}
|
587
|
-
return '';
|
588
|
-
}
|
589
|
-
}
|
590
|
-
// Handle object resolution (like `a.b.c`).
|
591
|
-
else if (!isHelper) {
|
592
|
-
// Reverse iterate through the contexts (last in, first out).
|
593
|
-
for (i = contexts.length - 1; i >= 0; i--) {
|
594
|
-
// Check the context for the reference
|
595
|
-
value = contexts[i];
|
596
|
-
|
597
|
-
// Make sure the context isn't a failed object before diving into it.
|
598
|
-
if (value !== undefined) {
|
599
|
-
for (j = 0; j < names.length; j++) {
|
600
|
-
// Keep running up the tree while there are matches.
|
601
|
-
if (typeof value[names[j]] != 'undefined') {
|
602
|
-
lastValue = value;
|
603
|
-
value = value[name = names[j]];
|
604
|
-
}
|
605
|
-
// If it's undefined, still match if the parent is an Observe.
|
606
|
-
else if (isObserve(value)) {
|
607
|
-
lastValue = value;
|
608
|
-
name = names[j];
|
609
|
-
break;
|
610
|
-
}
|
611
|
-
else {
|
612
|
-
lastValue = value = undefined;
|
613
|
-
break;
|
614
|
-
}
|
615
|
-
}
|
616
|
-
}
|
617
|
-
|
618
|
-
// Found a matched reference.
|
619
|
-
if (value !== undefined) {
|
620
|
-
// Support functions stored in objects.
|
621
|
-
if (can.isFunction(lastValue[name])) {
|
622
|
-
return lastValue[name]();
|
623
|
-
}
|
624
|
-
// Add support for observes
|
625
|
-
else if (isObserve(lastValue)) {
|
626
|
-
return lastValue.attr(name);
|
627
|
-
}
|
628
|
-
else {
|
629
|
-
// Invoke the length to ensure that Observe.List events fire.
|
630
|
-
isObserve(value) && isArrayLike(value) && value.attr('length');
|
631
|
-
return value;
|
632
|
-
}
|
633
|
-
}
|
634
|
-
}
|
635
|
-
}
|
636
|
-
|
637
|
-
// Support helper-like functions as anonymous helpers
|
638
|
-
if (obj !== undefined && can.isFunction(obj[ref])) {
|
639
|
-
return obj[ref];
|
640
|
-
}
|
641
|
-
// Support helpers without arguments, but only if there wasn't a matching data reference.
|
642
|
-
else if (value = Mustache.getHelper(ref)) {
|
643
|
-
return ref;
|
644
|
-
}
|
645
|
-
|
646
|
-
return '';
|
647
|
-
};
|
648
|
-
|
649
|
-
|
650
|
-
// ## Helpers
|
651
|
-
// Helpers are functions that can be called from within a template.
|
652
|
-
// These helpers differ from the scanner helpers in that they execute
|
653
|
-
// at runtime instead of during compilation.
|
654
|
-
// Custom helpers can be added via `can.Mustache.registerHelper`,
|
655
|
-
// but there are also some built-in helpers included by default.
|
656
|
-
// Most of the built-in helpers are little more than aliases to actions
|
657
|
-
// that the base version of Mustache simply implies based on the
|
658
|
-
// passed in object.
|
659
|
-
// Built-in helpers:
|
660
|
-
// * `data` - `data` is a special helper that is implemented via scanning helpers.
|
661
|
-
// It hooks up the active element to the active data object: `<div {{data "key"}} />`
|
662
|
-
// * `if` - Renders a truthy section: `{{#if var}} render {{/if}}`
|
663
|
-
// * `unless` - Renders a falsey section: `{{#unless var}} render {{/unless}}`
|
664
|
-
// * `each` - Renders an array: `{{#each array}} render {{this}} {{/each}}`
|
665
|
-
// * `with` - Opens a context section: `{{#with var}} render {{/with}}`
|
666
|
-
|
667
|
-
Mustache.registerHelper = function (name, fn) {
|
668
|
-
this._helpers.push({
|
669
|
-
name: name,
|
670
|
-
fn: fn
|
671
|
-
});
|
672
|
-
};
|
673
|
-
|
674
|
-
|
675
|
-
Mustache.getHelper = function (name) {
|
676
|
-
for (var i = 0, helper; helper = this._helpers[i]; i++) {
|
677
|
-
// Find the correct helper
|
678
|
-
if (helper.name == name) {
|
679
|
-
return helper;
|
680
|
-
}
|
681
|
-
}
|
682
|
-
return null;
|
683
|
-
};
|
684
|
-
|
685
|
-
// The built-in Mustache helpers.
|
686
|
-
Mustache._helpers = [
|
687
|
-
// Implements the `if` built-in helper.
|
688
|
-
{
|
689
|
-
name: 'if',
|
690
|
-
fn: function (expr, options) {
|
691
|
-
if ( !! expr) {
|
692
|
-
return options.fn(this);
|
693
|
-
}
|
694
|
-
else {
|
695
|
-
return options.inverse(this);
|
696
|
-
}
|
697
|
-
}
|
698
|
-
},
|
699
|
-
|
700
|
-
// Implements the `unless` built-in helper.
|
701
|
-
{
|
702
|
-
name: 'unless',
|
703
|
-
fn: function (expr, options) {
|
704
|
-
if (!expr) {
|
705
|
-
return options.fn(this);
|
706
|
-
}
|
707
|
-
}
|
708
|
-
},
|
709
|
-
|
710
|
-
// Implements the `each` built-in helper.
|
711
|
-
{
|
712
|
-
name: 'each',
|
713
|
-
fn: function (expr, options) {
|
714
|
-
if ( !! expr && expr.length) {
|
715
|
-
var result = [];
|
716
|
-
for (var i = 0; i < expr.length; i++) {
|
717
|
-
result.push(options.fn(expr[i]));
|
718
|
-
}
|
719
|
-
return result.join('');
|
720
|
-
}
|
721
|
-
}
|
722
|
-
},
|
723
|
-
|
724
|
-
// Implements the `with` built-in helper.
|
725
|
-
{
|
726
|
-
name: 'with',
|
727
|
-
fn: function (expr, options) {
|
728
|
-
if ( !! expr) {
|
729
|
-
return options.fn(expr);
|
730
|
-
}
|
731
|
-
}
|
732
|
-
}];
|
733
|
-
|
734
|
-
// ## Registration
|
735
|
-
// Registers Mustache with can.view.
|
736
|
-
can.view.register({
|
737
|
-
suffix: "mustache",
|
738
|
-
|
739
|
-
contentType: "x-mustache-template",
|
740
|
-
|
741
|
-
// Returns a `function` that renders the view.
|
742
|
-
script: function (id, src) {
|
743
|
-
return "can.Mustache(function(_CONTEXT,_VIEW) { " + new Mustache({
|
744
|
-
text: src,
|
745
|
-
name: id
|
746
|
-
}).template.out + " })";
|
747
|
-
},
|
748
|
-
|
749
|
-
renderer: function (id, text) {
|
750
|
-
return Mustache({
|
751
|
-
text: text,
|
752
|
-
name: id
|
753
|
-
});
|
754
|
-
}
|
755
|
-
});
|
756
|
-
|
757
|
-
|
758
|
-
})(can, this);
|