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.
@@ -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);