sprockets-jsrender 0.1.2 → 0.1.3

Sign up to get free protection for your applications and to get access to all the features.
data/README CHANGED
@@ -3,20 +3,22 @@ Sprockets jsRender/jsViews
3
3
 
4
4
  This gem adds jsRender/jsViews templates as tilt templates for Sprockets 2 in Rails 3.1.
5
5
 
6
- Thanks
7
- ======
8
- Inpired by sprockets-jquery-tmpl by Ryan Dy (https://github.com/rdy/sprockets-jquery-tmpl)
9
- jsrender and jsviews are created bu Boris Moore (https://github.com/BorisMoore/jsrender)
10
-
11
6
  Installing
12
7
  ==========
13
8
 
14
9
  1. Add the gem to bundler or install: `gem install sprockets-jsrender`
15
10
  2. Add to your app/assets/javascripts/application.js the following lines
16
11
  //= require jsrender
17
- //= require jquery.observable
18
- //= require jquery.views
12
+ //= require jquery.observable //(optional)
13
+ //= require jquery.views //(optional)
14
+
15
+ Any files in assets/javascripts/jsrender will be automatically added to list of templates using the path from that directory - i.e. assets/javascripts/jsrender/index.jsr will be mapped to $.render ["index"]( obj )
16
+
17
+
18
+ Acknowledgments
19
+ ===============
20
+ Inspired by sprockets-jquery-tmpl by Ryan Dy (https://github.com/rdy/sprockets-jquery-tmpl)
21
+ jsrender and jsviews are created bu Boris Moore (https://github.com/BorisMoore/jsrender)
19
22
 
20
- Any files in assets/javascripts/jsrender will be automatically added to list of templates using the path from that directory (i.e. assets/javascripts/jsrender/examples/index.jsr will be mapped to $.render("examples/index") )
21
23
 
22
24
  Copyright (c) 2012 Enrico Rubboli, released under the MIT license
@@ -7,7 +7,7 @@
7
7
  * Copyright 2012, Boris Moore
8
8
  * Released under the MIT License.
9
9
  */
10
- // informal pre beta commit counter: 2
10
+ // informal pre beta commit counter: 3
11
11
 
12
12
  this.jQuery && jQuery.link || (function( global, undefined ) {
13
13
  // global is the this object, which is window when running in the usual browser environment.
@@ -419,113 +419,115 @@ function linkViews( node, parent, nextNode, depth, data, context, prevNode, inde
419
419
  context = context || {};
420
420
  node = prevNode || node;
421
421
 
422
- if ( !prevNode && node.nodeType === 1 ) {
423
- if ( viewDepth++ === 0 ) {
424
- // Add top-level element nodes to view.nodes
425
- currentView.nodes.push( node );
426
- }
427
- if ( linkMarkup = node.getAttribute( jsv.linkAttr ) ) {
428
- linkIndex = currentView._lnk++;
429
- // Compiled linkFn expressions are stored in the tmpl.links array of the template
430
- links = currentView.links || currentView.tmpl.links;
431
- if ( !(link = links[ linkIndex ] )) {
432
- link = links [ linkIndex ] = {};
433
- if ( linkMarkup.charAt(linkMarkup.length-1) !== "}" ) {
434
- // Simplified syntax is used: data-link="expression"
435
- // Convert to data-link="{:expression}", or for inputs, data-link="{:expression:}" for (default) two-way binding
436
- linkMarkup = delimOpen1 + ":" + linkMarkup + ($.nodeName( node, "input" ) ? ":" : "") + delimClose0;
437
- }
438
- while( tokens = rTag.exec( linkMarkup )) { // TODO require } to be followed by whitespace or $, and remove the \}(!\}) option.
439
- // Iterate over the data-link expressions, for different target attrs, e.g. <input data-link="{:firstName:} title{:~description(firstName, lastName)}"
440
- // tokens: [all, attr, tag, converter, colon, html, code, linkedParams]
441
- attr = tokens[ 1 ];
442
- expression = tokens[ 2 ];
443
- if ( tokens[ 5 ]) {
444
- // Only for {:} link"
445
- if ( !attr && (convertBack = /^.*:([\w$]*)$/.exec( tokens[ 8 ] ))) {
446
- // two-way binding
447
- convertBack = convertBack[ 1 ];
448
- if ( cbLength = convertBack.length ) {
449
- // There is a convertBack function
450
- expression = tokens[ 2 ].slice( 0, -cbLength - 1 ) + delimClose0; // Remove the convertBack string from expression.
422
+ if ( node ) {
423
+ if ( !prevNode && node.nodeType === 1 ) {
424
+ if ( viewDepth++ === 0 ) {
425
+ // Add top-level element nodes to view.nodes
426
+ currentView.nodes.push( node );
427
+ }
428
+ if ( linkMarkup = node.getAttribute( jsv.linkAttr ) ) {
429
+ linkIndex = currentView._lnk++;
430
+ // Compiled linkFn expressions are stored in the tmpl.links array of the template
431
+ links = currentView.links || currentView.tmpl.links;
432
+ if ( !(link = links[ linkIndex ] )) {
433
+ link = links [ linkIndex ] = {};
434
+ if ( linkMarkup.charAt(linkMarkup.length-1) !== "}" ) {
435
+ // Simplified syntax is used: data-link="expression"
436
+ // Convert to data-link="{:expression}", or for inputs, data-link="{:expression:}" for (default) two-way binding
437
+ linkMarkup = delimOpen1 + ":" + linkMarkup + ($.nodeName( node, "input" ) ? ":" : "") + delimClose0;
438
+ }
439
+ while( tokens = rTag.exec( linkMarkup )) { // TODO require } to be followed by whitespace or $, and remove the \}(!\}) option.
440
+ // Iterate over the data-link expressions, for different target attrs, e.g. <input data-link="{:firstName:} title{:~description(firstName, lastName)}"
441
+ // tokens: [all, attr, tag, converter, colon, html, code, linkedParams]
442
+ attr = tokens[ 1 ];
443
+ expression = tokens[ 2 ];
444
+ if ( tokens[ 5 ]) {
445
+ // Only for {:} link"
446
+ if ( !attr && (convertBack = /^.*:([\w$]*)$/.exec( tokens[ 8 ] ))) {
447
+ // two-way binding
448
+ convertBack = convertBack[ 1 ];
449
+ if ( cbLength = convertBack.length ) {
450
+ // There is a convertBack function
451
+ expression = tokens[ 2 ].slice( 0, -cbLength - 1 ) + delimClose0; // Remove the convertBack string from expression.
452
+ }
453
+ }
454
+ if ( convertBack === null ) {
455
+ convertBack = undefined;
451
456
  }
452
457
  }
453
- if ( convertBack === null ) {
454
- convertBack = undefined;
458
+ // Compile the linkFn expression which evaluates and binds a data-link expression
459
+ // TODO - optimize for the case of simple data path with no conversion, helpers, etc.:
460
+ // i.e. data-link="a.b.c". Avoid creating new instances of Function every time. Can use a default function for all of these...
461
+ link[ attr ] = jsv.tmplFn( delimOpen0 + expression + delimClose1, undefined, TRUE );
462
+ if ( !attr && convertBack !== undefined ) {
463
+ link[ attr ].to = convertBack;
455
464
  }
456
465
  }
457
- // Compile the linkFn expression which evaluates and binds a data-link expression
458
- // TODO - optimize for the case of simple data path with no conversion, helpers, etc.:
459
- // i.e. data-link="a.b.c". Avoid creating new instances of Function every time. Can use a default function for all of these...
460
- link[ attr ] = jsv.tmplFn( delimOpen0 + expression + delimClose1, undefined, TRUE );
461
- if ( !attr && convertBack !== undefined ) {
462
- link[ attr ].to = convertBack;
463
- }
464
466
  }
467
+ for ( attr in link ) {
468
+ bindDataLinkTarget(
469
+ currentView.data || data, //source
470
+ node, //target
471
+ attr, //attr
472
+ link[ attr ], //compiled link markup expression
473
+ currentView //view
474
+ );
475
+ }
476
+ // TODO - Add one-way-to-source support
477
+ // if ( linkMarkup.lastIndexOf( "toSrc{", 0 ) === 0 ) {
478
+ // linkMarkup = "{toSrc " + linkMarkup.slice(6);
479
+ // }
465
480
  }
466
- for ( attr in link ) {
467
- bindDataLinkTarget(
468
- currentView.data|| data, //source
469
- node, //target
470
- attr, //attr
471
- link[ attr ], //compiled link markup expression
472
- currentView //view
473
- );
474
- }
475
- // TODO - Add one-way-to-source support
476
- // if ( linkMarkup.lastIndexOf( "toSrc{", 0 ) === 0 ) {
477
- // linkMarkup = "{toSrc " + linkMarkup.slice(6);
478
- // }
481
+ node = node.firstChild;
482
+ } else {
483
+ node = node.nextSibling;
479
484
  }
480
- node = node.firstChild;
481
- } else {
482
- node = node.nextSibling;
483
- }
484
485
 
485
- while ( node && node !== nextNode ) {
486
- if ( node.nodeType === 1 ) {
487
- linkViews( node, currentView, nextNode, viewDepth, data, context );
488
- } else if ( node.nodeType === 8 && (tokens = rTmplOrItemComment.exec( node.nodeValue ))) {
489
- // tokens: [ all, slash, 'item', 'tmpl', path, index, tmplParam ]
490
- parentNode = node.parentNode;
491
- if ( tokens[ 1 ]) {
492
- // <!--/item--> or <!--/tmpl-->
493
- currentView.nextNode = node;
494
- if ( currentView.ctx.onAfterCreate ) {
495
- currentView.ctx.onAfterCreate.call( currentView, currentView );
496
- }
497
- if ( tokens[ 2 ]) {
498
- // An item close tag: <!--/item-->
499
- currentView = parent;
500
- } else {
501
- // A tmpl close tag: <!--/tmpl-->
502
- return node;
503
- }
504
- } else {
505
- // <!--item--> or <!--tmpl-->
506
- parentElViews = parentElViews || jsViewsData( parentNode, viewStr, TRUE );
507
- if ( tokens[ 2 ]) {
508
- // An item open tag: <!--item-->
509
- parentElViews.push(
510
- currentView = linkedView( currentView.views[ index ] )
511
- );
512
- index++;
513
- currentView.prevNode = node;
486
+ while ( node && node !== nextNode ) {
487
+ if ( node.nodeType === 1 ) {
488
+ linkViews( node, currentView, nextNode, viewDepth, data, context );
489
+ } else if ( node.nodeType === 8 && (tokens = rTmplOrItemComment.exec( node.nodeValue ))) {
490
+ // tokens: [ all, slash, 'item', 'tmpl', path, index, tmplParam ]
491
+ parentNode = node.parentNode;
492
+ if ( tokens[ 1 ]) {
493
+ // <!--/item--> or <!--/tmpl-->
494
+ currentView.nextNode = node;
495
+ if ( currentView.ctx.onAfterCreate ) {
496
+ currentView.ctx.onAfterCreate.call( currentView, currentView );
497
+ }
498
+ if ( tokens[ 2 ]) {
499
+ // An item close tag: <!--/item-->
500
+ currentView = parent;
501
+ } else {
502
+ // A tmpl close tag: <!--/tmpl-->
503
+ return node;
504
+ }
514
505
  } else {
515
- // A tmpl open tag: <!--tmpl(path) name-->
516
- parentElViews.push(
517
- view = linkedView( currentView.views[ tokens[ 5 ]] )
518
- );
519
- view.prevNode = node;
520
- // Jump to the nextNode of the tmpl view
521
- node = linkViews( node, view, nextNode, 0, undefined, undefined, undefined, 0 );
506
+ // <!--item--> or <!--tmpl-->
507
+ parentElViews = parentElViews || jsViewsData( parentNode, viewStr, TRUE );
508
+ if ( tokens[ 2 ]) {
509
+ // An item open tag: <!--item-->
510
+ parentElViews.push(
511
+ currentView = linkedView( currentView.views[ index ] )
512
+ );
513
+ index++;
514
+ currentView.prevNode = node;
515
+ } else {
516
+ // A tmpl open tag: <!--tmpl(path) name-->
517
+ parentElViews.push(
518
+ view = linkedView( currentView.views[ tokens[ 5 ]] )
519
+ );
520
+ view.prevNode = node;
521
+ // Jump to the nextNode of the tmpl view
522
+ node = linkViews( node, view, nextNode, 0, undefined, undefined, undefined, 0 );
523
+ }
522
524
  }
525
+ } else if ( viewDepth === 0 ) {
526
+ // Add top-level non-element nodes to view.nodes
527
+ currentView.nodes.push( node );
523
528
  }
524
- } else if ( viewDepth === 0 ) {
525
- // Add top-level non-element nodes to view.nodes
526
- currentView.nodes.push( node );
529
+ node = node.nextSibling;
527
530
  }
528
- node = node.nextSibling;
529
531
  }
530
532
  }
531
533
 
@@ -6,7 +6,7 @@
6
6
  * Copyright 2012, Boris Moore
7
7
  * Released under the MIT License.
8
8
  */
9
- // informal pre beta commit counter: 3
9
+ // informal pre beta commit counter: 6
10
10
 
11
11
  this.jsviews || this.jQuery && jQuery.views || (function( window, undefined ) {
12
12
 
@@ -81,7 +81,7 @@ function setDelimiters( openChars, closeChars ) {
81
81
  // Build regex with new delimiters
82
82
  jsv.rTag = rTag // make rTag available to JsViews (or other components) for parsing binding expressions
83
83
  = secondOpenChar
84
- // tag (followed by / space or }) or colon or html or code
84
+ // tag (followed by / space or }) or colon or html or code
85
85
  + "(?:(?:(\\w+(?=[\\/\\s" + firstCloseChar + "]))|(?:(\\w+)?(:)|(>)|(\\*)))"
86
86
  // params
87
87
  + "\\s*((?:[^" + firstCloseChar + "]|" + firstCloseChar + "(?!" + secondCloseChar + "))*?)"
@@ -356,8 +356,8 @@ function renderContent( data, context, parentView, path, index ) {
356
356
  // Generate a reusable function that will serve to render a template against data
357
357
  // (Compile AST then build template function)
358
358
 
359
- function syntaxError() {
360
- throw "Syntax error";
359
+ function syntaxError( message, e ) {
360
+ throw (e ? (e.name + ': "' + e.message + '"') : "Syntax error") + (message ? (" \n" + message) : "");
361
361
  }
362
362
 
363
363
  function tmplFn( markup, tmpl, bind ) {
@@ -466,10 +466,10 @@ function tmplFn( markup, tmpl, bind ) {
466
466
  for ( i = 0; i < l; i++ ) {
467
467
  // AST nodes: [ tagName, converter, params, content, hash, contentMarkup ]
468
468
  node = astTop[ i ];
469
- if ( node[ 0 ] === "*" ) {
470
- code = code.slice( 0, i ? -1 : -3 ) + ";" + node[ 1 ] + (i + 1 < l ? "ret+=" : "");
471
- } else if ( "" + node === node ) { // type string
469
+ if ( "" + node === node ) { // type string
472
470
  code += '"' + node + '"+';
471
+ } else if ( node[ 0 ] === "*" ) {
472
+ code = code.slice( 0, i ? -1 : -3 ) + ";" + node[ 1 ] + (i + 1 < l ? "ret+=" : "");
473
473
  } else {
474
474
  tag = node[ 0 ];
475
475
  converter = node[ 1 ];
@@ -498,7 +498,7 @@ function tmplFn( markup, tmpl, bind ) {
498
498
  + ")+";
499
499
  }
500
500
  }
501
- code = new Function( "data, view, j, b, u", fnDeclStr
501
+ code = fnDeclStr
502
502
  + (getsValue ? "v," : "")
503
503
  + (hasTag ? "t=j.tag," : "")
504
504
  + (hasConverter ? "c=j.convert," : "")
@@ -508,8 +508,13 @@ function tmplFn( markup, tmpl, bind ) {
508
508
  + (allowCode ? 'ret=' : 'return ')
509
509
  + code.slice( 0, -1 ) + ";\n\n"
510
510
  + (allowCode ? "return ret;" : "")
511
- + "}catch(e){return j.err(e);}"
512
- );
511
+ + "}catch(e){return j.err(e);}";
512
+
513
+ try {
514
+ code = new Function( "data, view, j, b, u", code );
515
+ } catch(e) {
516
+ syntaxError( "Error in compiled template code:\n" + code, e );
517
+ }
513
518
 
514
519
  // Include only the var references that are needed in the code
515
520
  if ( tmpl ) {
@@ -626,8 +631,16 @@ function compile( name, tmpl, parent, options ) {
626
631
  // Return the template object, if already compiled, or the markup string
627
632
 
628
633
  if ( ("" + value === value) || value.nodeType > 0 ) {
629
- // If selector is valid and returns at least one element, get first element
630
- elem = value.nodeType > 0 ? value : !rTmplString.test( value ) && jQuery && jQuery( value )[0];
634
+ try {
635
+ elem = value.nodeType > 0
636
+ ? value
637
+ : !rTmplString.test( value )
638
+ // If value is a string and does not contain HTML or tag content, then test as selector
639
+ && jQuery && jQuery( value )[0];
640
+ // If selector is valid and returns at least one element, get first element
641
+ // If invalid, jQuery will throw. We will stay with the original string.
642
+ } catch(e) {}
643
+
631
644
  if ( elem && elem.type ) {
632
645
  // It is a script element
633
646
  // Create a name for data linking if none provided
@@ -1,5 +1,5 @@
1
1
  module Sprockets
2
2
  module JSRender
3
- VERSION = '0.1.2'
3
+ VERSION = '0.1.3'
4
4
  end
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: sprockets-jsrender
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.2
4
+ version: 0.1.3
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-04-06 00:00:00.000000000 Z
12
+ date: 2012-04-28 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: actionpack