Skip to content
This repository
Browse code

Helper functions now have the 'this' pointer as the current view,

hence providing full access to context, e.g. this.data or this.index etc.
The tag helper function 'this' pointer is a tagObject which has this.view
to provide the current view context, and this.props to get named tag parameters, etc.
$.views.helpers/tags/converters each return $.views when used as setters,
so can be chained. Similarly $.templates returns $ when used as a setter.
  • Loading branch information...
commit efdb2cb0a7bed1b54024cd839e3ee2b095d9cf40 1 parent ac145e0
Boris Moore authored March 19, 2012
5  demos/step-by-step/03_no-encoding.html
@@ -25,15 +25,14 @@
25 25
 </ul><br />
26 26
 
27 27
 <script id="movieTemplate" type="text/x-jsrender">
28  
-	{{if true}}<tr>
  28
+	<tr>
29 29
 		<td>{{loc:title}}</td>
30 30
 		<td class="synopsis">{{:synopsis}}</td>
31 31
 		<td class="synopsis">{{>synopsis}}</td>
32 32
 	</tr>
33  
-	{{/if}}
34 33
 </script>
35 34
 
36  
-<table><tbody class="header"><tr><th>title</th><th>No Convert</th><th>HTML Encode</th></tr></tbody>
  35
+<table><tr class="header"><th>title</th><th>No Convert</th><th>HTML Encode</th></tr>
37 36
 	<tbody id="movieList"></tbody>
38 37
 </table>
39 38
 
2  demos/step-by-step/05_for-tag.html
@@ -25,7 +25,7 @@
25 25
 	</tr>
26 26
 </script>
27 27
 
28  
-<table><tbody class="header"><tr><th>Title</th><th>Languages</th></tr></tbody>
  28
+<table><tr class="header"><th>Title</th><th>Languages</th></tr>
29 29
 	<tbody id="movieList"></tbody>
30 30
 </table>
31 31
 
2  demos/step-by-step/06_template-composition.html
@@ -36,7 +36,7 @@
36 36
 	</span>
37 37
 </script>
38 38
 
39  
-<table><tbody class="header"><tr><th>Synopsis</th><th>Fixed Template</th><th>Template specified in data</th></tr></tbody>
  39
+<table><tr class="header"><th>Synopsis</th><th>Fixed Template</th><th>Template specified in data</th></tr>
40 40
 	<tbody id="movieList"></tbody>
41 41
 </table>
42 42
 
4  demos/step-by-step/08_custom-tags.html
@@ -34,6 +34,8 @@
34 34
 });
35 35
 </pre>
36 36
 
  37
+<!--================ Demo ================-->
  38
+
37 39
 <script id="movieTemplate" type="text/x-jsrender">
38 40
 	<tr>
39 41
 		<td>{{:title}}</td>
@@ -51,7 +53,7 @@
51 53
 	<div>{{:name}}</div>
52 54
 </script>
53 55
 
54  
-<table><tbody class="header"><tr><th>Title</th><th>Orginal order</th><th>Reverse order</th></tr></tbody>
  56
+<table><tr class="header"><th>Title</th><th>Orginal order</th><th>Reverse order</th></tr>
55 57
 	<tbody id="movieList"></tbody>
56 58
 </table>
57 59
 
47  demos/step-by-step/09_helper-functions.html
@@ -12,31 +12,21 @@
12 12
 
13 13
 <h3>Helper functions</h3>
14 14
 
15  
-<script id="movieTemplate" type="text/x-jsrender">
16  
-	<tr>
17  
-		<td>{{:title}}</td>
18  
-		<td>
19  
-			{{for languages}}
20  
-				{{:~format(name, "upper")}}{{if ~nextToLast(#view)}} and {{else ~notLast(#view)}}, {{/if}}
21  
-			{{/for}}
22  
-		</td>
23  
-	</tr>
24  
-</script>
25  
-
26 15
 <pre>
27 16
 {{:~format(name, "upper")}}
28 17
 
29  
-{{if ~nextToLast(#view)}}...
  18
+{{if ~nextToLast()}}...{{/if}}
30 19
 
31 20
 $.views.helpers({
32 21
 
33 22
     format: function( val, format ){
34 23
         ...
35  
-		return val.toUpperCase();
36  
-		...
  24
+        return val.toUpperCase();
  25
+        ...
37 26
     },
38 27
 
39  
-    nextToLast: function( view ) {
  28
+    nextToLast: function() {
  29
+        var view = this;
40 30
         return view.index === view.parent.data.length - 2;
41 31
     },
42 32
 
@@ -44,10 +34,27 @@
44 34
 });
45 35
 </pre>
46 36
 
47  
-<table><tbody class="header"><tr><th>Title</th><th>Languages</th></tr></tbody>
  37
+<!--=================== Demo ===================-->
  38
+
  39
+<!------------------ Templates ------------------>
  40
+
  41
+<script id="movieTemplate" type="text/x-jsrender">
  42
+	<tr>
  43
+		<td>{{:~format(title, "upper")}}</td>
  44
+		<td>
  45
+			{{for languages}}
  46
+				{{:name}}{{if ~nextToLast()}} and {{else ~notLast()}}, {{/if}}
  47
+			{{/for}}
  48
+		</td>
  49
+	</tr>
  50
+</script>
  51
+
  52
+<table><tr class="header"><th>Title</th><th>Languages</th></tr></tbody>
48 53
 	<tbody id="movieList"></tbody>
49 54
 </table>
50 55
 
  56
+<!------------------ Script ------------------>
  57
+
51 58
 <script type="text/javascript">
52 59
 	$.views.helpers({
53 60
 
@@ -57,15 +64,17 @@
57 64
 				case "upper":
58 65
 					return val.toUpperCase();
59 66
 				case "lower":
60  
-					return  val.toLowerCase();
  67
+					return val.toLowerCase();
61 68
 			}
62 69
 		},
63 70
 
64  
-		nextToLast: function( view ) {
  71
+		nextToLast: function() {
  72
+			var view = this;
65 73
 			return view.index === view.parent.data.length - 2;
66 74
 		},
67 75
 
68  
-		notLast: function( view ) {
  76
+		notLast: function() {
  77
+			var view = this;
69 78
 			return view.index !== view.parent.data.length - 1;
70 79
 		}
71 80
 	});
16  demos/step-by-step/10_comparison-tests.html
@@ -12,6 +12,14 @@
12 12
 
13 13
 <h3>Helper functions and comparison tests</h3>
14 14
 
  15
+<pre>
  16
+{{if !(languages && languages.length)}}...{{/if}}
  17
+
  18
+{{if languages==null}}...{{/if}}
  19
+</pre>
  20
+
  21
+<!--================ Demo ================-->
  22
+
15 23
 <script id="movieTemplate" type="text/x-jsrender">
16 24
 	<tr>
17 25
 		<td>{{:title}}</td>
@@ -28,13 +36,7 @@
28 36
 	<b>Warning:</b> <em>No alternate languages</em>
29 37
 </script>
30 38
 
31  
-<pre>
32  
-{{if !(languages && languages.length)}}...{{/if}}
33  
-
34  
-{{if languages==null}}...{{/if}}
35  
-</pre>
36  
-
37  
-<table><tbody class="header"><tr><th>Title</th><th>{{if !(languages && languages.length)}}</th><th>{{if a==null}}</th></tr></tbody>
  39
+<table><tr class="header"><th>Title</th><th>{{if !(languages && languages.length)}}</th><th>{{if a==null}}</th></tr>
38 40
 	<tbody id="movieList"></tbody>
39 41
 </table>
40 42
 
54  demos/step-by-step/11_default-values-scenario.html
@@ -22,19 +22,10 @@
22 22
 {{:languages||'Languages unavailable'}}
23 23
 </pre>
24 24
 
25  
-<table><tbody class="header"><tr><th>Title</th><th>{{:path}}</th></tr></tbody>
  25
+<table><tr class="header"><th>Title</th><th>{{:path}}</th></tr>
26 26
 	<tbody id="movieList1"></tbody>
27 27
 </table>
28 28
 
29  
-<script id="movieTemplate1" type="text/x-jsrender">
30  
-	<tr>
31  
-		<td>{{:title}}</td>
32  
-		<td>
33  
-			{{:languages||'Languages unavailable'}}
34  
-		</td>
35  
-	</tr>
36  
-</script>
37  
-
38 29
 <!---------------------- Second Example ---------------------->
39 30
 
40 31
 <div class="subhead">Creating a special custom tag:</div>
@@ -48,19 +39,6 @@
48 39
 });
49 40
 </pre>
50 41
 
51  
-<table><tbody class="header"><tr><th>Title</th><th>{{get path default="..."}}</th></tr></tbody>
52  
-	<tbody id="movieList2"></tbody>
53  
-</table>
54  
-
55  
-<script id="movieTemplate2" type="text/x-jsrender">
56  
-	<tr>
57  
-		<td>{{:title}}</td>
58  
-		<td>
59  
-			{{get languages defaultValue="No languages!"/}}
60  
-		</td>
61  
-	</tr>
62  
-</script>
63  
-
64 42
 <!---------------------- Third Example ---------------------->
65 43
 
66 44
 <div class="subhead">Creating a multi-purpose utility tag:</div>
@@ -74,10 +52,36 @@
74 52
 });
75 53
 </pre>
76 54
 
77  
-<table><tbody class="header"><tr><th>Title</th><th>{{yesNo path yes="..." no="..."}}</th></tr></tbody>
  55
+<table><tr class="header"><th>Title</th><th>{{yesNo path yes="..." no="..."}}</th></tr>
78 56
 	<tbody id="movieList3"></tbody>
79 57
 </table>
80 58
 
  59
+<!--=================== Demo ===================-->
  60
+
  61
+<!------------------ Templates ------------------>
  62
+
  63
+<table><tr class="header"><th>Title</th><th>{{get path default="..."}}</th></tr></tbody>
  64
+	<tbody id="movieList2"></tbody>
  65
+</table>
  66
+
  67
+<script id="movieTemplate1" type="text/x-jsrender">
  68
+	<tr>
  69
+		<td>{{:title}}</td>
  70
+		<td>
  71
+			{{:languages||'Languages unavailable'}}
  72
+		</td>
  73
+	</tr>
  74
+</script>
  75
+
  76
+<script id="movieTemplate2" type="text/x-jsrender">
  77
+	<tr>
  78
+		<td>{{:title}}</td>
  79
+		<td>
  80
+			{{get languages defaultValue="No languages!"/}}
  81
+		</td>
  82
+	</tr>
  83
+</script>
  84
+
81 85
 <script id="movieTemplate3" type="text/x-jsrender">
82 86
 	<tr>
83 87
 		<td>{{:title}}</td>
@@ -88,7 +92,7 @@
88 92
 	</tr>
89 93
 </script>
90 94
 
91  
-<!---------------------- Code ---------------------->
  95
+<!------------------ Script ------------------>
92 96
 
93 97
 <script type="text/javascript">
94 98
 
3  demos/step-by-step/12_layout-templates.html
@@ -30,7 +30,6 @@
30 30
 footer
31 31
 </pre>
32 32
 
33  
-
34 33
 <div class="subhead">Nested layout:</div>
35 34
 <pre>
36 35
 {{for languages layout=true}}
@@ -42,6 +41,8 @@
42 41
 {{/for}}
43 42
 </pre>
44 43
 
  44
+<!--================ Demo ================-->
  45
+
45 46
 <script id="movieTemplate" type="text/x-jsrender">
46 47
 	<tbody class="header">
47 48
 		<tr><th colspan="2">{{:length}} movies available:</th></tr>
4  demos/step-by-step/13_allow-code.html
@@ -27,6 +27,8 @@
27 27
 }}
28 28
 </pre>
29 29
 
  30
+<!--================ Demo ================-->
  31
+
30 32
 <script id="movieTemplate" type="text/x-jsrender">
31 33
 	<tr>
32 34
 		<td>{{:title}}</td>
@@ -46,7 +48,7 @@
46 48
 	</tr>
47 49
 </script>
48 50
 
49  
-<table><tbody class="header"><tr><th>Title</th><th>Languages</th></tr></tbody>
  51
+<table><tr class="header"><th>Title</th><th>Languages</th></tr>
50 52
 	<tbody id="movieList"></tbody>
51 53
 </table>
52 54
 
4  demos/step-by-step/13b_allow-code.html
@@ -26,6 +26,8 @@
26 26
 }}
27 27
 </pre>
28 28
 
  29
+<!--================ Demo ================-->
  30
+
29 31
 <script id="movieTemplate" type="text/x-jsrender">
30 32
 	<tr>
31 33
 		<td>{{:title}}</td>
@@ -45,7 +47,7 @@
45 47
 	</tr>
46 48
 </script>
47 49
 
48  
-<table><tbody class="header"><tr><th>Title</th><th>Languages</th></tr></tbody>
  50
+<table><tr class="header"><th>Title</th><th>Languages</th></tr>
49 51
 	<tbody id="movieList"></tbody>
50 52
 </table>
51 53
 
52  jsrender.js
@@ -6,6 +6,7 @@
6 6
  * Copyright 2012, Boris Moore
7 7
  * Released under the MIT License.
8 8
  */
  9
+// informal pre beta commit counter: 0
9 10
 
10 11
 this.jsviews || this.jQuery && jQuery.views || (function( window, undefined ) {
11 12
 
@@ -60,7 +61,6 @@ var versionNumber = "v1.0pre",
60 61
 		converters: converters,
61 62
 		View: View,
62 63
 		convert: convert,
63  
-		ctx: getContext,
64 64
 		delimiters: setDelimiters,
65 65
 		tag: renderTag
66 66
 	};
@@ -100,13 +100,17 @@ function setDelimiters( openChars, closeChars ) {
100 100
 }
101 101
 
102 102
 //=================
103  
-// jsviews.ctx
  103
+// View.hlp
104 104
 //=================
105 105
 
106  
-function getContext( view, helper ) {
107  
-	var tmplHelpers = view.tmpl.helpers || {};
108  
-	tmplHelpers = (view.ctx[ helper ] ? view.ctx : tmplHelpers[ helper ] ? tmplHelpers : helpers[ helper ] ? helpers : {});
109  
-	return tmplHelpers[ helper ];
  106
+function getHelper( helper ) {
  107
+	// Helper method called as view.hlp() from compiled template, for helper functions or template parameters ~foo
  108
+	var view = this,
  109
+	tmplHelpers = view.tmpl.helpers || {};
  110
+	helper = (view.ctx[ helper ] ? view.ctx : tmplHelpers[ helper ] ? tmplHelpers : helpers[ helper ] ? helpers : {})[ helper ];
  111
+	return typeof helper !== "function" ? helper : function() {
  112
+		return helper.apply(view, arguments);
  113
+	};
110 114
 }
111 115
 
112 116
 //=================
@@ -147,7 +151,7 @@ function renderTag( tag, parentView, converter, content, tagObject ) {
147 151
 
148 152
 	tagObject.isTag = TRUE;
149 153
 	tagObject.converter = converter;
150  
-	tagObject.parent = parentView;
  154
+	tagObject.view = parentView;
151 155
 	tagObject.renderContent = renderContent;
152 156
 	if ( parentView.ctx ) {
153 157
 		extend( tagObject.ctx, parentView.ctx);
@@ -158,7 +162,7 @@ function renderTag( tag, parentView, converter, content, tagObject ) {
158 162
 }
159 163
 
160 164
 //=================
161  
-// View constuctor
  165
+// View constructor
162 166
 //=================
163 167
 
164 168
 function View( context, path, parentView, data, template, index ) {
@@ -173,7 +177,8 @@ function View( context, path, parentView, data, template, index ) {
173 177
 			parent: parentView,
174 178
 			data: data,
175 179
 			ctx: context,
176  
-			views: $.isArray( data ) ? [] : {}
  180
+			views: $.isArray( data ) ? [] : {},
  181
+			hlp: getHelper
177 182
 		};
178 183
 
179 184
 	if ( $.isArray( views ))  {
@@ -192,7 +197,7 @@ function View( context, path, parentView, data, template, index ) {
192 197
 // Registration
193 198
 //=================
194 199
 
195  
-function addToStore( store, name, item, process ) {
  200
+function addToStore( self, store, name, item, process ) {
196 201
 	// Add item to named store such as templates, helpers, converters...
197 202
 	var key, onStore;
198 203
 	if ( name && typeof name === "object" && !name.nodeType ) {
@@ -200,7 +205,7 @@ function addToStore( store, name, item, process ) {
200 205
 		for ( key in name ) {
201 206
 			store( key, name[ key ]);
202 207
 		}
203  
-		return jsv;
  208
+		return self;
204 209
 	}
205 210
 	if ( !name || item === undefined ) {
206 211
 		if ( process ) {
@@ -229,7 +234,7 @@ function templates( name, tmpl ) {
229 234
 
230 235
 	// When registering for {{foo a b c==d e=f}}, tagFn should be a function with the signature:
231 236
 	// function(a,b). The 'this' pointer will be a hash with properties c and e.
232  
-	return addToStore( templates, name, tmpl, compile );
  237
+	return addToStore( this, templates, name, tmpl, compile );
233 238
 }
234 239
 
235 240
 function tags( name, tagFn ) {
@@ -240,7 +245,7 @@ function tags( name, tagFn ) {
240 245
 
241 246
 	// When registering for {{foo a b c==d e=f}}, tagFn should be a function with the signature:
242 247
 	// function(a,b). The 'this' pointer will be a hash with properties c and e.
243  
-	return addToStore( tags, name, tagFn );
  248
+	return addToStore( this, tags, name, tagFn );
244 249
 }
245 250
 
246 251
 function helpers( name, helperFn ) {
@@ -249,7 +254,7 @@ function helpers( name, helperFn ) {
249 254
 	// Getter: Use var helperFn = $.views.helpers( name ) or $.views.helpers[name] or $.views.helpers.name to return the function.
250 255
 	// Remove: Use $.view.helpers( name, null ) to remove a registered helper function from $.view.helpers.
251 256
 	// Within a template, access the helper using the syntax: {{... ~myHelper(...) ...}}.
252  
-	return addToStore( helpers, name, helperFn );
  257
+	return addToStore( this, helpers, name, helperFn );
253 258
 }
254 259
 
255 260
 function converters( name, converterFn ) {
@@ -258,7 +263,7 @@ function converters( name, converterFn ) {
258 263
 	// Getter: Use var converterFn = $.views.converters( name ) or $.views.converters[name] or $.views.converters.name to return the converter function.
259 264
 	// Remove: Use $.view.converters( name, null ) to remove a registered converter from $.view.converters.
260 265
 	// Within a template, access the converter using the syntax: {{myConverter:...}}.
261  
-	return addToStore( converters, name, converterFn );
  266
+	return addToStore( this, converters, name, converterFn );
262 267
 }
263 268
 
264 269
 //=================
@@ -278,7 +283,7 @@ function renderContent( data, context, parentView, path, index ) {
278 283
 		// This is a call from renderTag
279 284
 		tmpl = self.tmpl;
280 285
 		context = context || self.ctx;
281  
-		parentView = parentView || self.parent;
  286
+		parentView = parentView || self.view;
282 287
 		path = path || self.path;
283 288
 		index = index || self.index;
284 289
 		props = self.props;
@@ -478,7 +483,7 @@ function tmplFn( markup, tmpl, bind ) {
478 483
 				tmplFn( markup, nestedTmpl);
479 484
 				nested.push( nestedTmpl );
480 485
 			}
481  
-			hasHelperPath = hasHelperPath || params.indexOf( 'h(view,"' ) > -1;
  486
+			hasHelperPath = hasHelperPath || params.indexOf( 'view.hlp("' ) > -1;
482 487
 			hasViewPath = hasViewPath || params.indexOf( "view" ) > -1;
483 488
 			code += (tag === ":"
484 489
 				? (converter === "html"
@@ -498,7 +503,6 @@ function tmplFn( markup, tmpl, bind ) {
498 503
 		+ (hasTag ? "t=j.tag," : "")
499 504
 		+ (hasConverter ? "c=j.convert," : "")
500 505
 		+ (hasEncoder ? "e=j.converters.html," : "")
501  
-		+ (hasHelperPath ? "h=j.ctx," : "")
502 506
 		+ "ret; try{\n\n"
503 507
 		+ (tmplOptions.debug ? "debugger;" : "")
504 508
 		+ (allowCode ? 'ret=' : 'return ')
@@ -535,10 +539,10 @@ function parseParams( params, bind ) {
535 539
 		//                     val                 object    helper     view      viewProperty pathTokens   leafToken string quot
536 540
 			if ( object ) {
537 541
 				val = (helper
538  
-					? 'h(view,"' + helper + '")'
  542
+					? 'view.hlp("' + helper + '")'
539 543
 					: view
540 544
 						? "view"
541  
-						:"data")
  545
+						: "data")
542 546
 				+ (leafToken
543 547
 					? (viewProperty
544 548
 						? "." + viewProperty
@@ -595,7 +599,7 @@ function parseParams( params, bind ) {
595 599
 												: "")
596 600
 										)
597 601
 										: comma
598  
-											? (fnCall[ parenDepth ] || syntaxError(), ",")
  602
+											? (fnCall[ parenDepth ] || syntaxError(), ",") // We don't allow top-level literal arrays or objects
599 603
 											: (aposed = apos, quoted = quot, '"')
600 604
 			);
601 605
 		}
@@ -753,7 +757,7 @@ if ( jQuery ) {
753 757
 
754 758
 extend = $.extend;
755 759
 
756  
-jsv.topView = { views: {}, tmpl: {}, ctx: jsv.helpers };
  760
+jsv.topView = { views: {}, tmpl: {}, hlp: getHelper, ctx: jsv.helpers };
757 761
 
758 762
 function replacerForHtml( ch ) {
759 763
 	// Original code from Mike Samuel <msamuel@google.com>
@@ -767,7 +771,7 @@ function replacerForHtml( ch ) {
767 771
 tags({
768 772
 	"if": function() {
769 773
 		var ifTag = this,
770  
-			view = ifTag.parent;
  774
+			view = ifTag.view;
771 775
 
772 776
 		view.onElse = function( tagObject, args ) {
773 777
 			var i = 0,
@@ -789,7 +793,7 @@ tags({
789 793
 		return view.onElse( this, arguments );
790 794
 	},
791 795
 	"else": function() {
792  
-		var view = this.parent;
  796
+		var view = this.view;
793 797
 		return view.onElse ? view.onElse( this, arguments ) : "";
794 798
 	},
795 799
 	"for": function() {
2  test/unit/jsrender-tests-no-jquery.js
@@ -437,5 +437,5 @@ test("template encapsulation", function() {
437 437
 	equal( jsviews.render.tmplWithNestedResources({ a: "aVal" }), "aval aValbtrue% (double:aVal&aVal) (override outer double:AVAL|AVAL)", 'Access nested resources from template' );
438 438
 	equal( jsviews.render.useLower({ a: "aVal" }), "", 'Cannot access nested resources from a different template' );
439 439
 	equal( jsviews.render.tmplWithNestedResources({ a: "aVal" }, context), "aval aValbcontextualNot2true% (double:aVal&aVal) (override outer double:contextualUpperAVAL|contextualUpperAVAL)", 'Resources passed in with context override nested resources' );
440  
-	equal( jsviews.templates.tmplWithNestedResources.templates.templateWithDebug.fn.toString().indexOf("debugger;"), 90, 'Can set debug=true on nested template' );
  440
+	equal( jsviews.templates.tmplWithNestedResources.templates.templateWithDebug.fn.toString().indexOf("debugger;"), 82, 'Can set debug=true on nested template' );
441 441
 });

0 notes on commit efdb2cb

Please sign in to comment.
Something went wrong with that request. Please try again.