canjs-rails 0.1.0 → 1.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,40 +1,47 @@
1
- (function(can, window, undefined){
2
-
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
3
9
  //---- ADD jQUERY HELPERS -----
4
10
  //converts jquery functions to use views
5
11
  var convert, modify, isTemplate, isHTML, isDOM, getCallback,
6
- // text and val cannot produce an element, so don't run hookups on them
7
- noHookup = {'val':true,'text':true};
12
+ // text and val cannot produce an element, so don't run hookups on them
13
+ noHookup = {
14
+ 'val': true,
15
+ 'text': true
16
+ };
8
17
 
9
- convert = function( func_name ) {
18
+ convert = function (func_name) {
10
19
  // save the old jQuery helper
11
20
  var old = $.fn[func_name];
12
21
 
13
22
  // replace it with our new helper
14
- $.fn[func_name] = function() {
15
-
23
+ $.fn[func_name] = function () {
24
+
16
25
  var args = can.makeArray(arguments),
17
- callbackNum,
18
- callback,
19
- self = this,
26
+ callbackNum, callback, self = this,
20
27
  result;
21
-
28
+
22
29
  // if the first arg is a deferred
23
30
  // wait until it finishes, and call
24
31
  // modify with the result
25
- if ( can.isDeferred(args[0]) ) {
26
- args[0].done(function( res ) {
32
+ if (can.isDeferred(args[0])) {
33
+ args[0].done(function (res) {
27
34
  modify.call(self, [res], old);
28
35
  })
29
36
  return this;
30
37
  }
31
38
  //check if a template
32
- else if ( isTemplate(args) ) {
39
+ else if (isTemplate(args)) {
33
40
 
34
41
  // if we should operate async
35
42
  if ((callbackNum = getCallback(args))) {
36
43
  callback = args[callbackNum];
37
- args[callbackNum] = function( result ) {
44
+ args[callbackNum] = function (result) {
38
45
  modify.call(self, [result], old);
39
46
  callback.call(self, result);
40
47
  };
@@ -43,31 +50,30 @@
43
50
  }
44
51
  // call view with args (there might be deferreds)
45
52
  result = can.view.apply(can.view, args);
46
-
53
+
47
54
  // if we got a string back
48
- if (!can.isDeferred(result) ) {
55
+ if (!can.isDeferred(result)) {
49
56
  // we are going to call the old method with that string
50
57
  args = [result];
51
58
  } else {
52
59
  // if there is a deferred, wait until it is done before calling modify
53
- result.done(function( res ) {
60
+ result.done(function (res) {
54
61
  modify.call(self, [res], old);
55
62
  })
56
63
  return this;
57
64
  }
58
65
  }
59
- return noHookup[func_name] ? old.apply(this,args) :
60
- modify.call(this, args, old);
66
+ return noHookup[func_name] ? old.apply(this, args) : modify.call(this, args, old);
61
67
  };
62
68
  };
63
69
 
64
70
  // modifies the content of the element
65
71
  // but also will run any hookup
66
- modify = function( args, old ) {
72
+ modify = function (args, old) {
67
73
  var res, stub, hooks;
68
74
 
69
75
  //check if there are new hookups
70
- for ( var hasHookups in can.view.hookups ) {
76
+ for (var hasHookups in can.view.hookups) {
71
77
  break;
72
78
  }
73
79
 
@@ -75,10 +81,10 @@
75
81
  // and insert that
76
82
  // by using a frag, the element can be recursively hooked up
77
83
  // before insterion
78
- if ( hasHookups && args[0] && isHTML(args[0]) ) {
79
- args[0] = can.view.frag(args[0])
84
+ if (hasHookups && args[0] && isHTML(args[0])) {
85
+ args[0] = can.view.frag(args[0]).childNodes;
80
86
  }
81
-
87
+
82
88
  //then insert into DOM
83
89
  res = old.apply(this, args);
84
90
 
@@ -89,27 +95,27 @@
89
95
  // $('#foo').html('/path/to/template.ejs',{data})
90
96
  // in general, we want to make sure the first arg is a string
91
97
  // and the second arg is data
92
- isTemplate = function( args ) {
98
+ isTemplate = function (args) {
93
99
  // save the second arg type
94
100
  var secArgType = typeof args[1];
95
-
101
+
96
102
  // the first arg is a string
97
- return typeof args[0] == "string" &&
98
- // the second arg is an object or function
99
- (secArgType == 'object' || secArgType == 'function') &&
100
- // but it is not a dom element
101
- !isDOM(args[1]);
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]);
102
108
  };
103
109
  // returns true if the arg is a jQuery object or HTMLElement
104
- isDOM = function(arg){
110
+ isDOM = function (arg) {
105
111
  return arg.nodeType || (arg[0] && arg[0].nodeType)
106
112
  };
107
113
  // returns whether the argument is some sort of HTML data
108
- isHTML = function( arg ) {
109
- if ( isDOM(arg) ) {
114
+ isHTML = function (arg) {
115
+ if (isDOM(arg)) {
110
116
  // if jQuery object or DOM node we're good
111
117
  return true;
112
- } else if ( typeof arg === "string" ) {
118
+ } else if (typeof arg === "string") {
113
119
  // if string, do a quick sanity check that we're HTML
114
120
  arg = can.trim(arg);
115
121
  return arg.substr(0, 1) === "<" && arg.substr(arg.length - 1, 1) === ">" && arg.length >= 3;
@@ -120,173 +126,34 @@
120
126
  };
121
127
 
122
128
  //returns the callback arg number if there is one (for async view use)
123
- getCallback = function( args ) {
129
+ getCallback = function (args) {
124
130
  return typeof args[3] === 'function' ? 3 : typeof args[2] === 'function' && 2;
125
131
  };
126
132
 
127
- /**
128
- * @add jQuery.fn
129
- * @parent can.View
130
- * Called on a jQuery collection that was rendered with can.View with pending hookups. can.View can render a
131
- * template with hookups, but not actually perform the hookup, because it returns a string without actual DOM
132
- * elements to hook up to. So hookup performs the hookup and clears the pending hookups, preventing errors in
133
- * future templates.
134
- *
135
- * @codestart
136
- * $(can.View('//views/recipes.ejs',recipeData)).hookup()
137
- * @codeend
138
- */
139
- $.fn.hookup = function() {
133
+
134
+ $.fn.hookup = function () {
140
135
  can.view.frag(this);
141
136
  return this;
142
137
  };
143
138
 
144
- /**
145
- * @add jQuery.fn
146
- */
139
+
147
140
  can.each([
148
- /**
149
- * @function jQuery.fn.prepend
150
- * @parent can.view.modifiers
151
- *
152
- * Extending the original [http://api.jquery.com/prepend/ jQuery().prepend()]
153
- * to render [can.view] templates inserted at the beginning of each element in the set of matched elements.
154
- *
155
- * $('#test').prepend('path/to/template.ejs', { name : 'canjs' });
156
- *
157
- * @param {String|Object|Function} content A template filename or the id of a view script tag
158
- * or a DOM element, array of elements, HTML string, or can object.
159
- * @param {Object} [data] The data to render the view with.
160
- * If rendering a view template this parameter always has to be present
161
- * (use the empty object initializer {} for no data).
162
- * @param {Function} [callback] A success callback to load the view asynchronously
163
- *
164
- * @return {jQuery|can.Deferred} The jQuery object or a [can.Deferred] if a deferred has
165
- * been passed in data.
166
- */
141
+
167
142
  "prepend",
168
- /**
169
- * @function jQuery.fn.append
170
- * @parent can.view.modifiers
171
- *
172
- * Extending the original [http://api.jquery.com/append/ jQuery().append()]
173
- * to render [can.view] templates inserted at the end of each element in the set of matched elements.
174
- *
175
- * $('#test').append('path/to/template.ejs', { name : 'canjs' });
176
- *
177
- * @param {String|Object|Function} content A template filename or the id of a view script tag
178
- * or a DOM element, array of elements, HTML string, or can object.
179
- * @param {Object} [data] The data to render the view with.
180
- * If rendering a view template this parameter always has to be present
181
- * (use the empty object initializer {} for no data).
182
- * @param {Function} [callback] A success callback to load the view asynchronously
183
- *
184
- * @return {jQuery|can.Deferred} The jQuery object or a [can.Deferred] if a deferred has
185
- * been passed in data.
186
- */
143
+
187
144
  "append",
188
- /**
189
- * @function jQuery.fn.after
190
- * @parent can.view.modifiers
191
- *
192
- * Extending the original [http://api.jquery.com/after/ jQuery().after()]
193
- * to render [can.view] templates inserted after each element in the set of matched elements.
194
- *
195
- * $('#test').after('path/to/template.ejs', { name : 'canjs' });
196
- *
197
- * @param {String|Object|Function} content A template filename or the id of a view script tag
198
- * or a DOM element, array of elements, HTML string, or can object.
199
- * @param {Object} [data] The data to render the view with.
200
- * If rendering a view template this parameter always has to be present
201
- * (use the empty object initializer {} for no data).
202
- * @param {Function} [callback] A success callback to load the view asynchronously
203
- *
204
- * @return {jQuery|can.Deferred} The jQuery object or a [can.Deferred] if a deferred has
205
- * been passed in data.
206
- */
145
+
207
146
  "after",
208
- /**
209
- * @function jQuery.fn.before
210
- * @parent can.view.modifiers
211
- *
212
- * Extending the original [http://api.jquery.com/before/ jQuery().before()]
213
- * to render [can.view] templates inserted before each element in the set of matched elements.
214
- *
215
- * $('#test').before('path/to/template.ejs', { name : 'canjs' });
216
- *
217
- * @param {String|Object|Function} content A template filename or the id of a view script tag
218
- * or a DOM element, array of elements, HTML string, or can object.
219
- * @param {Object} [data] The data to render the view with.
220
- * If rendering a view template this parameter always has to be present
221
- * (use the empty object initializer {} for no data).
222
- * @param {Function} [callback] A success callback to load the view asynchronously
223
- *
224
- * @return {jQuery|can.Deferred} The jQuery object or a [can.Deferred] if a deferred has
225
- * been passed in data.
226
- */
147
+
227
148
  "before",
228
- /**
229
- * @function jQuery.fn.text
230
- * @parent can.view.modifiers
231
- *
232
- * Extending the original [http://api.jquery.com/text/ jQuery().text()]
233
- * to render [can.View] templates as the content of each matched element.
234
- * Unlike [jQuery.fn.html] jQuery.fn.text also works with XML, escaping the provided
235
- * string as necessary.
236
- *
237
- * $('#test').text('path/to/template.ejs', { name : 'canjs' });
238
- *
239
- * @param {String|Object|Function} content A template filename or the id of a view script tag
240
- * or a DOM element, array of elements, HTML string, or can object.
241
- * @param {Object} [data] The data to render the view with.
242
- * If rendering a view template this parameter always has to be present
243
- * (use the empty object initializer {} for no data).
244
- * @param {Function} [callback] A success callback to load the view asynchronously
245
- *
246
- * @return {jQuery|can.Deferred} The jQuery object or a [can.Deferred] if a deferred has
247
- * been passed in data.
248
- */
149
+
249
150
  "text",
250
- /**
251
- * @function jQuery.fn.html
252
- * @parent can.view.modifiers
253
- *
254
- * Extending the original [http://api.jquery.com/html/ jQuery().html()]
255
- * to render [can.view] templates as the content of each matched element.
256
- *
257
- * $('#test').html('path/to/template.ejs', { name : 'canjs' });
258
- *
259
- * @param {String|Object|Function} content A template filename or the id of a view script tag
260
- * or a DOM element, array of elements, HTML string, or can object.
261
- * @param {Object} [data] The data to render the view with.
262
- * If rendering a view template this parameter always has to be present
263
- * (use the empty object initializer {} for no data).
264
- * @param {Function} [callback] A success callback to load the view asynchronously
265
- *
266
- * @return {jQuery|can.Deferred} The jQuery object or a [can.Deferred] if a deferred has
267
- * been passed in data.
268
- */
151
+
269
152
  "html",
270
- /**
271
- * @function jQuery.fn.replaceWith
272
- * @parent can.view.modifiers
273
- *
274
- * Extending the original [http://api.jquery.com/replaceWith/ jQuery().replaceWith()]
275
- * to render [can.view] templates replacing each element in the set of matched elements.
276
- *
277
- * $('#test').replaceWith('path/to/template.ejs', { name : 'canjs' });
278
- *
279
- * @param {String|Object|Function} content A template filename or the id of a view script tag
280
- * or a DOM element, array of elements, HTML string, or can object.
281
- * @param {Object} [data] The data to render the view with.
282
- * If rendering a view template this parameter always has to be present
283
- * (use the empty object initializer {} for no data).
284
- * @param {Function} [callback] A success callback to load the view asynchronously
285
- *
286
- * @return {jQuery|can.Deferred} The jQuery object or a [can.Deferred] if a deferred has
287
- * been passed in data.
288
- */
289
- "replaceWith", "val"],function(func){
153
+
154
+ "replaceWith", "val"], function (func) {
290
155
  convert(func);
291
156
  });
292
- })(this.can, this )
157
+
158
+
159
+ })(this, jQuery, can);
@@ -0,0 +1,758 @@
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);