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 +10 -8
- data/app/assets/javascripts/jquery.views.js +98 -96
- data/app/assets/javascripts/jsrender.js +25 -12
- data/lib/sprockets/jsrender/version.rb +1 -1
- metadata +2 -2
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:
|
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 (
|
423
|
-
if (
|
424
|
-
|
425
|
-
|
426
|
-
|
427
|
-
|
428
|
-
|
429
|
-
|
430
|
-
|
431
|
-
|
432
|
-
link = links
|
433
|
-
|
434
|
-
|
435
|
-
|
436
|
-
|
437
|
-
|
438
|
-
|
439
|
-
//
|
440
|
-
|
441
|
-
|
442
|
-
|
443
|
-
|
444
|
-
|
445
|
-
|
446
|
-
|
447
|
-
|
448
|
-
|
449
|
-
|
450
|
-
|
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
|
-
|
454
|
-
|
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
|
-
|
467
|
-
|
468
|
-
|
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
|
-
|
486
|
-
|
487
|
-
|
488
|
-
|
489
|
-
|
490
|
-
|
491
|
-
|
492
|
-
|
493
|
-
|
494
|
-
|
495
|
-
|
496
|
-
|
497
|
-
|
498
|
-
|
499
|
-
|
500
|
-
|
501
|
-
|
502
|
-
|
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
|
-
//
|
516
|
-
parentElViews
|
517
|
-
|
518
|
-
|
519
|
-
|
520
|
-
|
521
|
-
|
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
|
-
|
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:
|
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
|
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 (
|
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 =
|
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
|
-
|
630
|
-
|
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
|
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.
|
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-
|
12
|
+
date: 2012-04-28 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: actionpack
|