1
- /*! jquery.views.js v0.9.80 (Beta): http://jsviews.com/ */
1
+ /*! jquery.views.js v0.9.81 (Beta): http://jsviews.com/ */
2
2
/*
3
3
* Interactive data-driven views using JsRender templates.
4
4
* Subcomponent of JsViews
@@ -44,7 +44,7 @@ var setGlobals = $ === false; // Only set globals if script block in browser (no
44
44
jsr = jsr || setGlobals && global . jsrender ;
45
45
$ = $ || global . jQuery ;
46
46
47
- var versionNumber = "v0.9.80 " ,
47
+ var versionNumber = "v0.9.81 " ,
48
48
requiresStr = "JsViews requires " ;
49
49
50
50
if ( ! $ || ! $ . fn ) {
@@ -166,7 +166,7 @@ function elemChangeHandler(ev, params, sourceValue) {
166
166
// The binding has a 'to' field, which is of the form [[targetObject, toPath], cvtBack]
167
167
linkCtx = binding . linkCtx ;
168
168
view = linkCtx . view ;
169
- tag = linkCtx . tag ;
169
+ tag = linkCtx . tag || view . tag ;
170
170
$source = $ ( source ) ;
171
171
onBeforeChange = view . hlp ( onBeforeChangeStr ) ; // TODO Can we optimize this and other instances of same?
172
172
onAfterChange = view . hlp ( onAfterChangeStr ) ; // TODO Can we optimize this and other instances of same
@@ -298,10 +298,7 @@ function propertyChangeHandler(ev, eventArgs, linkFn) {
298
298
// onUpdate returned false, or attr === "none", or this is an update coming from the tag's own change event
299
299
// - so don't refresh the tag: we just use the new tagCtxs merged from the sourceValue,
300
300
// (which may optionally have been modifed in onUpdate()...) and then bind, and we are done
301
- if ( attr === HTML && tag . onBeforeLink ) {
302
- tag . onBeforeLink ( ) ;
303
- }
304
- callAfterLink ( tag ) ;
301
+ callAfterLink ( tag , ev , eventArgs ) ;
305
302
observeAndBind ( linkCtx , source , target ) ;
306
303
view . linkCtx = oldLinkCtx ;
307
304
return ;
@@ -310,6 +307,10 @@ function propertyChangeHandler(ev, eventArgs, linkFn) {
310
307
return ;
311
308
}
312
309
310
+ if ( tag . onUnbind ) {
311
+ tag . onUnbind ( tag . tagCtx , linkCtx , tag . ctx , ev , eventArgs ) ;
312
+ }
313
+
313
314
sourceValue = tag . tagName === ":" // Call convertVal if it is a {{cvt:...}} - otherwise call renderTag
314
315
? $sub . _cnvt ( tag . cvt , view , sourceValue [ 0 ] )
315
316
: $sub . _tag ( tag , view , view . tmpl , sourceValue , true , onError ) ;
@@ -335,7 +336,7 @@ function propertyChangeHandler(ev, eventArgs, linkFn) {
335
336
336
337
if ( tag ) {
337
338
tag . _er = hasError ;
338
- callAfterLink ( tag , eventArgs ) ;
339
+ callAfterLink ( tag , ev , eventArgs ) ;
339
340
}
340
341
}
341
342
@@ -410,14 +411,12 @@ function updateContent(sourceValue, linkCtx, attr, tag) {
410
411
411
412
if ( tag ) {
412
413
// Initialize the tag with element references
414
+ tag . _ . unlinked = true ; // Set to unlinked, so initialization is triggered after re-rendering, e.g. for setting linkedElem, and calling onBind
413
415
tag . parentElem = tag . parentElem || ( linkCtx . expr || tag . _elCnt ) ? target : targetParent ;
414
416
prevNode = tag . _prv ;
415
417
nextNode = tag . _nxt ;
416
418
}
417
419
if ( ! renders ) {
418
- if ( attr === HTML && tag && tag . onBeforeLink ) {
419
- tag . onBeforeLink ( ) ;
420
- }
421
420
return ;
422
421
}
423
422
@@ -530,9 +529,6 @@ function updateContent(sourceValue, linkCtx, attr, tag) {
530
529
// Remove HTML nodes
531
530
$ ( nodesToRemove ) . remove ( ) ; // Note if !tag._elCnt removing the nodesToRemove will process and dispose view and tag bindings contained within the updated tag control
532
531
533
- if ( tag && tag . onBeforeLink ) {
534
- tag . onBeforeLink ( ) ;
535
- }
536
532
// Insert and link new content
537
533
promise = view . link ( view . data , target , prevNode , nextNode , sourceValue , tag && { tag : tag . _tgId , lazyLink : tag . tagCtx . props . lazyLink } ) ;
538
534
} else {
@@ -541,9 +537,6 @@ function updateContent(sourceValue, linkCtx, attr, tag) {
541
537
if ( renders ) {
542
538
$target . empty ( ) ;
543
539
}
544
- if ( tag && tag . onBeforeLink ) {
545
- tag . onBeforeLink ( ) ;
546
- }
547
540
if ( renders ) {
548
541
promise = view . link ( source , target , prevNode , nextNode , sourceValue , tag && { tag : tag . _tgId } ) ;
549
542
}
@@ -800,7 +793,7 @@ function observeAndBind(linkCtx, source, target) { //TODO? linkFnArgs) {;
800
793
801
794
if ( tag ) {
802
795
// Use the 'depends' paths set on linkCtx.tag - which may have been set on declaration
803
- // or in events: init, render, onBeforeLink, onAfterLink etc.
796
+ // or in events: init, render, onAfterLink etc.
804
797
depends = tag . depends || depends ;
805
798
depends = $isFunction ( depends ) ? tag . depends ( tag ) : depends ;
806
799
linkedElem = tag . linkedElem ;
@@ -854,20 +847,13 @@ function observeAndBind(linkCtx, source, target) { //TODO? linkFnArgs) {;
854
847
bindTo ( binding , tag , linkedElem && linkedElem [ 0 ] || target , cvtBk ) ;
855
848
}
856
849
if ( tag ) {
857
- if ( tag . onAfterBind ) {
858
- tag . onAfterBind ( binding ) ;
859
- }
860
850
if ( ! tag . flow && ! tag . _ . inline ) {
861
851
target . setAttribute ( jsvAttrStr , ( target . getAttribute ( jsvAttrStr ) || "" ) + "#" + bindId + "^/" + bindId + "^" ) ;
862
852
tag . _tgId = "" + bindId ;
863
853
}
864
854
}
865
855
}
866
856
if ( linkedElem && linkedElem [ 0 ] ) {
867
- if ( tag . _ . radio ) {
868
- linkedElem = linkedElem . find ( RADIOINPUT ) ;
869
- }
870
-
871
857
l = linkedElem . length ;
872
858
while ( l -- ) {
873
859
linkedElem [ l ] . _jsvBnd = linkedElem [ l ] . _jsvBnd || ( target . _jsvBnd + "+" ) ;
@@ -1421,9 +1407,6 @@ function viewLink(outerData, parentNode, prevNode, nextNode, html, refresh, cont
1421
1407
tag . _prv = elem ;
1422
1408
}
1423
1409
tag . _elCnt = linkInfo . elCnt ;
1424
- if ( tag . onBeforeLink ) {
1425
- tag . onBeforeLink ( ) ;
1426
- }
1427
1410
// We data-link depth-first ("on the way in"), which is better for perf - and allows setting parent tags etc.
1428
1411
view = tag . tagCtx . view ;
1429
1412
addDataBinding ( undefined , tag . _prv , view , linkInfo . id ) ;
@@ -1634,11 +1617,11 @@ function addDataBinding(linkMarkup, node, currentView, boundTagId, isLink, data,
1634
1617
attr = tokens [ 1 ] ;
1635
1618
tagExpr = tokens [ 3 ] ;
1636
1619
while ( linkExpressions [ 0 ] && linkExpressions [ 0 ] [ 4 ] === "else" ) { // If this is {someTag...} and is followed by an {else...} add to tagExpr
1637
- tagExpr += "}{" + linkExpressions . shift ( ) [ 3 ] ;
1620
+ tagExpr += delimCloseChar1 + delimOpenChar0 + linkExpressions . shift ( ) [ 3 ] ;
1638
1621
hasElse = true ;
1639
1622
}
1640
1623
if ( hasElse ) { // If an {else} has been added, need also to add closing {{/someTag}}
1641
- tagExpr += "}{{ /" + tokens [ 4 ] + "}" ;
1624
+ tagExpr += delimCloseChar1 + delimOpenChar0 + delimOpenChar1 + " /" + tokens [ 4 ] + delimCloseChar0 ;
1642
1625
}
1643
1626
linkCtx = {
1644
1627
type : isLink ? "top" : "link" ,
@@ -1792,54 +1775,60 @@ function normalizeLinkTag(linkMarkup, twoway) {
1792
1775
// Methods for views and tags
1793
1776
//===========================
1794
1777
1795
- function callAfterLink ( tag , eventArgs ) {
1796
- var $linkedElem , linkedElem , radioButtons , val , l , linkedTag , oldTrig , newTrig ,
1778
+ function callAfterLink ( tag , ev , eventArgs ) {
1779
+ var $linkedElem , linkedElem , radioButtons , val , l , linkedTag , oldTrig , newTrig , tagProps , propsExpr , linkedElemView , prop , propDef ,
1797
1780
tagCtx = tag . tagCtx ,
1798
1781
view = tagCtx . view ,
1799
1782
props = tagCtx . props ,
1800
1783
linkCtx = tag . linkCtx ;
1801
1784
1785
+ if ( tag . _ . unlinked ) { // First call to onAfterLink, or first call after onUpdate: updateContent. Initialize and call onBind and set properties
1786
+ if ( tag . linkedElement !== undefined ) {
1787
+ // linkedElement: - selector for identifying linked element in template/rendered content
1788
+ tag . linkedElem = tag . _ . inline ? tag . contents ( true , tag . linkedElement || "*" ) . first ( ) : $ ( linkCtx . elem ) ;
1789
+ }
1790
+ if ( tag . onBind ) {
1791
+ tag . onBind ( tagCtx , linkCtx , tag . ctx , ev , eventArgs ) ;
1792
+ }
1793
+ }
1794
+
1802
1795
if ( tag . onAfterLink ) {
1803
- tag . onAfterLink ( tagCtx , linkCtx , eventArgs ) ;
1796
+ tag . onAfterLink ( tagCtx , linkCtx , tag . ctx , ev , eventArgs ) ;
1804
1797
}
1798
+
1805
1799
tag . _ . unlinked = undefined ;
1806
1800
$linkedElem = tag . targetTag ? tag . targetTag . linkedElem : tag . linkedElem ;
1807
- if ( ! tag . noVal && ( linkedElem = $linkedElem && $linkedElem [ 0 ] ) ) {
1808
- if ( radioButtons = tag . _ . radio ) {
1809
- $linkedElem = $linkedElem . find ( RADIOINPUT ) ;
1810
- }
1811
- if ( radioButtons || ! tag . _ . chging ) {
1812
- val = tag . cvtArgs ( ) [ 0 ] ;
1801
+ if ( linkedElem = $linkedElem && $linkedElem [ 0 ] ) {
1802
+ if ( ! tag . noVal ) {
1803
+ if ( ! tag . _ . chging ) {
1804
+ val = tag . cvtArgs ( ) [ 0 ] ;
1813
1805
1814
- if ( radioButtons || linkedElem !== linkCtx . elem ) {
1815
- l = $linkedElem . length ;
1816
- while ( l -- ) {
1817
- linkedElem = $linkedElem [ l ] ;
1818
- linkedTag = linkedElem . _jsvLkEl ;
1819
- if ( tag . _ . inline && ( ! linkedTag || linkedTag !== tag && linkedTag . targetTag !== tag ) ) {
1820
- // For data-linked tags, identify the linkedElem with the tag, for "to" binding
1821
- // (For data-linked elements, if not yet bound, we identify later when the linkCtx.elem is bound)
1822
- linkedElem . _jsvLkEl = tag ;
1823
- bindTo ( bindingStore [ tag . _tgId ] , tag , linkedElem ) ;
1824
- linkedElem . _jsvBnd = "&" + tag . _tgId + "+" ; // Add a "+" for cloned binding - so removing
1825
- // elems with cloned bindings will not remove the 'parent' binding from the bindingStore.
1826
- }
1827
- if ( radioButtons ) {
1828
- // For radio button, set to if val === value. For others set val() to val, below
1829
- linkedElem [ CHECKED ] = val === linkedElem . value ;
1806
+ if ( linkedElem !== linkCtx . elem ) {
1807
+ l = $linkedElem . length ;
1808
+ while ( l -- ) {
1809
+ linkedElem = $linkedElem [ l ] ;
1810
+ linkedTag = linkedElem . _jsvLkEl ;
1811
+ if ( tag . _ . inline && ( ! linkedTag || linkedTag !== tag && linkedTag . targetTag !== tag ) ) {
1812
+ // For data-linked tags, identify the linkedElem with the tag, for "to" binding
1813
+ // (For data-linked elements, if not yet bound, we identify later when the linkCtx.elem is bound)
1814
+ linkedElem . _jsvLkEl = tag ;
1815
+ bindTo ( bindingStore [ tag . _tgId ] , tag , linkedElem ) ;
1816
+ linkedElem . _jsvBnd = "&" + tag . _tgId + "+" ; // Add a "+" for cloned binding - so removing
1817
+ // elems with cloned bindings will not remove the 'parent' binding from the bindingStore.
1818
+ }
1830
1819
}
1820
+ linkCtx . _val = val ;
1831
1821
}
1832
- linkCtx . _val = val ;
1833
- }
1834
- if ( val !== undefined ) {
1835
- if ( ! radioButtons && linkedElem . value !== undefined ) {
1836
- if ( linkedElem . type === CHECKBOX ) {
1837
- linkedElem [ CHECKED ] = val && val !== "false" ;
1838
- } else {
1839
- $linkedElem . val ( val ) ;
1822
+ if ( val !== undefined ) {
1823
+ if ( linkedElem . value !== undefined ) {
1824
+ if ( linkedElem . type === CHECKBOX ) {
1825
+ linkedElem [ CHECKED ] = val && val !== "false" ;
1826
+ } else if ( linkedElem . type === "text" ) {
1827
+ linkedElem . value = val ;
1828
+ }
1829
+ } else if ( linkedElem . contentEditable === TRUE ) {
1830
+ linkedElem . innerHTML = val ;
1840
1831
}
1841
- } else if ( linkedElem . contentEditable === TRUE ) {
1842
- linkedElem . innerHTML = val ;
1843
1832
}
1844
1833
}
1845
1834
}
@@ -1851,7 +1840,14 @@ function callAfterLink(tag, eventArgs) {
1851
1840
$linkedElem . width ( props . width ) ;
1852
1841
}
1853
1842
}
1843
+ if ( props . title !== undefined ) {
1844
+ $linkedElem . attr ( "title" , props . title ) ;
1845
+ }
1854
1846
if ( props [ "class" ] ) {
1847
+ // This code supports dynamic binding to class - where it adds the class if absent, and removes/adds if a previous value is present
1848
+ if ( eventArgs && $linkedElem . hasClass ( eventArgs . oldValue ) ) {
1849
+ $linkedElem . removeClass ( eventArgs . oldValue ) ;
1850
+ }
1855
1851
$linkedElem . addClass ( props [ "class" ] ) ;
1856
1852
}
1857
1853
if ( props . id ) {
@@ -1887,6 +1883,8 @@ function bindTo(binding, tag, linkedElem, cvtBk) {
1887
1883
source = linkCtx . data ,
1888
1884
paths = linkCtx . fn . paths ;
1889
1885
1886
+ tag = tag || linkedElem . _jsvLkEl ;
1887
+
1890
1888
if ( binding && paths ) {
1891
1889
oldTrig = linkedElem . _jsvTr || false ;
1892
1890
if ( tag ) {
@@ -2064,6 +2062,9 @@ function removeViewBinding(bindId, linkedElemTag, elem) {
2064
2062
}
2065
2063
$linkedElem = tag . linkedElem ;
2066
2064
2065
+ if ( tag . onUnbind ) {
2066
+ tag . onUnbind ( tag . tagCtx , linkCtx , tag . ctx , true ) ;
2067
+ }
2067
2068
if ( tag . onDispose ) {
2068
2069
tag . onDispose ( ) ;
2069
2070
}
@@ -2615,6 +2616,7 @@ $converters.merge = function(val) {
2615
2616
2616
2617
$tags ( "on" , {
2617
2618
attr : NONE ,
2619
+ noVal : true , // This tag control does not bind to arg[0] - no default binding to current #data context
2618
2620
init : function ( tagCtx ) {
2619
2621
var content ,
2620
2622
tag = this ,
@@ -2636,7 +2638,7 @@ $tags("on", {
2636
2638
} ,
2637
2639
render : function ( ) {
2638
2640
var tagCtx = this . tagCtx ;
2639
- return tagCtx . render ( tagCtx . view , true ) ; // no arg, so renders against parentView.data
2641
+ return this . _ . inline && tagCtx . render ( tagCtx . view , true ) ; // no arg, so renders against parentView.data
2640
2642
} ,
2641
2643
onAfterLink : function ( tagCtx , linkCtx ) {
2642
2644
var handler , params , find , activeElem ,
@@ -2659,17 +2661,18 @@ $tags("on", {
2659
2661
? ( tag . _sel = args [ 1 ] || "*" , tag . parentElem )
2660
2662
// If inline, attach to child elements of tag parent element (filtered by selector argument if provided.
2661
2663
// (In handler we'll filter out events from sibling elements preceding or following tag.)
2664
+ // This allows us to use the delegated pattern where the attached event works even for added elements satisfying the selector
2662
2665
: linkCtx . elem ) ;
2663
2666
2664
2667
if ( ! contextOb ) {
2665
2668
// Get the path for the preceding object (context object) of handler (which is the last arg), compile function
2666
2669
// to return that context object, and run compiled function against data
2667
2670
contextOb = / ^ ( .* ) [ \. ^ ] [ \w $ ] + $ / . exec ( tagCtx . params . args . slice ( - params . length - 1 ) [ 0 ] ) ;
2668
- contextOb = contextOb && $sub . tmplFn ( "{ :" + contextOb [ 1 ] + "}" , view . tmpl , true ) ( linkCtx . data , view ) ;
2671
+ contextOb = contextOb && $sub . tmplFn ( delimOpenChar1 + " :" + contextOb [ 1 ] + delimCloseChar0 , view . tmpl , true ) ( linkCtx . data , view ) ;
2669
2672
}
2670
2673
2671
2674
if ( tag . _evs ) {
2672
- tag . onDispose ( ) ;
2675
+ tag . onUnbind ( ) ;
2673
2676
}
2674
2677
2675
2678
activeElem . on (
@@ -2706,7 +2709,7 @@ $tags("on", {
2706
2709
onUpdate : function ( ) {
2707
2710
return false ;
2708
2711
} ,
2709
- onDispose : function ( ) {
2712
+ onUnbind : function ( ) {
2710
2713
var self = this ;
2711
2714
if ( self . activeElem ) {
2712
2715
self . activeElem . off ( self . _evs , self . _sel , self . _hlr ) ;
@@ -2717,11 +2720,11 @@ $tags("on", {
2717
2720
} ) ;
2718
2721
2719
2722
$extend ( $tags [ "for" ] , {
2720
- //onUpdate: function(ev, eventArgs, tagCtxs ) {
2723
+ //onUpdate: function(ev, eventArgs, newTagCtxs ) {
2721
2724
//Consider adding filtering for perf optimization. However the below prevents update on some scenarios which _should_ update - namely when there is another array on which for also depends.
2722
2725
//var i, l, tci, prevArg;
2723
2726
//for (tci = 0; (prevArg = this.tagCtxs[tci]) && prevArg.args.length; tci++) {
2724
- // if (prevArg.args[0] !== tagCtxs [tci].args[0]) {
2727
+ // if (prevArg.args[0] !== newTagCtxs [tci].args[0]) {
2725
2728
// return true;
2726
2729
// }
2727
2730
//}
@@ -2795,10 +2798,10 @@ $extend($tags["for"], {
2795
2798
} ) ;
2796
2799
2797
2800
$extend ( $tags [ "if" ] , {
2798
- onUpdate : function ( ev , eventArgs , tagCtxs ) {
2801
+ onUpdate : function ( ev , eventArgs , newTagCtxs ) {
2799
2802
var tci , prevArg , different ;
2800
2803
for ( tci = 0 ; ( prevArg = this . tagCtxs [ tci ] ) ; tci ++ ) {
2801
- different = prevArg . props . tmpl !== tagCtxs [ tci ] . props . tmpl || prevArg . args . length && ! ( prevArg = prevArg . args [ 0 ] ) !== ! tagCtxs [ tci ] . args [ 0 ] ;
2804
+ different = prevArg . props . tmpl !== newTagCtxs [ tci ] . props . tmpl || prevArg . args . length && ! ( prevArg = prevArg . args [ 0 ] ) !== ! newTagCtxs [ tci ] . args [ 0 ] ;
2802
2805
if ( ( ! this . convert && ! ! prevArg ) || different ) {
2803
2806
return different ;
2804
2807
// If there is not a change of template, and there is no converter, and newArg and prevArg are both truthy, return false to cancel update.
@@ -2810,7 +2813,7 @@ $extend($tags["if"], {
2810
2813
// Boolean value of all args are unchanged (falsey), so return false to cancel update
2811
2814
return false ;
2812
2815
} ,
2813
- onAfterLink : function ( tagCtx , linkCtx , eventArgs ) {
2816
+ onAfterLink : function ( tagCtx , linkCtx , ctx , ev , eventArgs ) {
2814
2817
if ( eventArgs ) {
2815
2818
this . domChange ( tagCtx , linkCtx , eventArgs ) ;
2816
2819
}
@@ -3050,7 +3053,7 @@ $views.getCtx = function(param) { // Return value of ctx.foo, including for comp
3050
3053
3051
3054
$sub . _cp = function ( paramVal , params , view ) { // Get compiled contextual parameters (or properties) ~foo=expr.
3052
3055
if ( view . linked ) { // In JsViews, returns [view, linkFn] where linkFn is compiled function for expression
3053
- params = "{ :" + params + "}" ;
3056
+ params = delimOpenChar1 + " :" + params + delimCloseChar0 ;
3054
3057
var tmpl = view . tmpl ,
3055
3058
links = topView . tmpl . links , // Use topView links, as for compiled top-level linking expressions. To do - should this ever get disposed?
3056
3059
linkFn = links [ params ] ;
0 commit comments