1- /*! JsObservable v1.0.2 : http://jsviews.com/#jsobservable */
1+ /*! JsObservable v1.0.3 : http://jsviews.com/#jsobservable */
22/*
33 * Subcomponent of JsViews
44 * Data change events for data-linking
@@ -44,7 +44,7 @@ if (!$ || !$.fn) {
4444 throw "JsObservable requires jQuery" ; // We require jQuery
4545}
4646
47- var versionNumber = "v1.0.2 " ,
47+ var versionNumber = "v1.0.3 " ,
4848 _ocp = "_ocp" , // Observable contextual parameter
4949 $observe , $observable ,
5050
@@ -101,6 +101,7 @@ if (!$.observe) {
101101 observeInnerCbKey = 1 ,
102102 $data = $ . data ,
103103 remove = { } , // flag for removeProperty
104+ asyncBatch = [ ] ,
104105
105106 //========================== Top-level functions ==========================
106107
@@ -247,13 +248,12 @@ if (!$.observe) {
247248
248249 function filterAndObserveAll ( obj , prop , unobs , nestedArray ) {
249250 var newObject , newParentObs ;
250- if ( prop !== $expando && ( newObject = $observable . _fltr ( newAllPath , obj [ prop ] , nextParentObs , filter ) ) ) {
251+ if ( ( + prop === prop || prop !== $expando ) && ( newObject = $observable . _fltr ( newAllPath , obj [ prop ] , nextParentObs , filter ) ) ) {
251252 newParentObs = nextParentObs . slice ( ) ;
252253 if ( nestedArray && updatedTgt && newParentObs [ 0 ] !== updatedTgt ) {
253254 newParentObs . unshift ( updatedTgt ) ; // For array change events when observing an array which is not the root, need to add updated array to parentObs
254255 }
255256 observeAll ( namespace , newObject , cb , filter || ( nestedArray ? undefined : 0 ) , newParentObs , newAllPath , unobs , objMap ) ;
256- // If nested array, need to observe the array too - so set filter to undefined
257257 }
258258 }
259259
@@ -345,6 +345,19 @@ if (!$.observe) {
345345 $unobserve = function ( ) {
346346 [ ] . push . call ( arguments , true ) ; // Add true as additional final argument
347347 return $observe . apply ( undefined , arguments ) ;
348+ } ,
349+
350+ batchTrigger = function ( async ) {
351+ var event ,
352+ batch = this . slice ( ) ;
353+ this . length = 0 ;
354+ this . _go = 0 ;
355+ while ( event = batch . shift ( ) ) {
356+ if ( ! event . skip ) {
357+ event [ 0 ] . _trigger ( event [ 1 ] , event [ 2 ] , true ) ;
358+ }
359+ }
360+ this . paths = { } ;
348361 } ;
349362
350363 $observe = function ( ) {
@@ -367,7 +380,7 @@ if (!$.observe) {
367380 }
368381
369382 function observeOnOff ( cb , object , fullPath , namespace , pathStr , isArrayBinding , off ) {
370- var j , evData ,
383+ var j , evData , dataOb ,
371384 boundObOrArr = wrapArray ( object ) ,
372385 prntObs = parentObs ,
373386 allPth = allPath ;
@@ -422,18 +435,17 @@ if (!$.observe) {
422435 } ;
423436 }
424437 $ ( boundObOrArr ) . on ( namespace , null , evData , onDataChange ) ;
425-
426438 if ( cbBindings ) {
427439 // Add object to cbBindings
428- cbBindings [ $data ( object ) . obId || $data ( object , " obId" , observeObjKey ++ ) ] = object ;
440+ cbBindings [ ( dataOb = $data ( object ) ) . obId || ( dataOb . obId = observeObjKey ++ ) ] = object ;
429441 }
430442 }
431443 }
432444
433445 function bindArray ( cb , arr , unbind , isArray , relPath ) {
434446 if ( allowArray ) {
435447 // allowArray is 1 if this is a call to observe that does not come from observeAndBind (tag binding), or is from a 'depends' path,
436- // so we allow arrayChange binding. Otherwise allowArray is zero.
448+ // or for a tag with tag.onArrayChange = true - so we allow arrayChange binding. Otherwise allowArray is zero.
437449 var object ,
438450 prevAllPath = allPath ;
439451
@@ -510,7 +522,7 @@ if (!$.observe) {
510522 // remove previous observeAll wrapped callback, if inner callback was the same;
511523 }
512524
513- var arrIndex , skip , dep , obArr , prt ,
525+ var arrIndex , skip , dep , obArr , prt , fnProp , isGet ,
514526 obj = object ;
515527 if ( object && object . _cxp ) {
516528 return observeObjectPaths ( object [ 0 ] , [ object [ 1 ] ] , callback , contextCb ) ;
@@ -521,6 +533,10 @@ if (!$.observe) {
521533 if ( prop === "" ) {
522534 continue ;
523535 }
536+ if ( prop . slice ( - 2 ) === "()" ) {
537+ prop = prop . slice ( 0 , - 2 ) ;
538+ isGet = true ;
539+ }
524540 if ( ( prts . length < depth + 1 ) && ! obj . nodeType ) {
525541 // Add observer for each token in path starting at depth, and on to the leaf
526542 if ( ! unobserve && ( events = $ . _data ( obj ) . events ) ) {
@@ -533,6 +549,7 @@ if (!$.observe) {
533549 && data . ns === initialNs
534550 && data . cb . _cId === callback . _cId
535551 && data . cb . _inId === callback . _inId
552+ && ! data . _arOk === ! allowArray
536553 && ( data . prop === prop || data . prop === "*" || data . prop === "**" ) ) {
537554 if ( prt = prts . join ( "." ) ) {
538555 data . paths . push ( prt ) ; // We will skip this binding, but if it is not a leaf binding,
@@ -596,7 +613,8 @@ if (!$.observe) {
596613 }
597614 }
598615 if ( $isFunction ( prop ) ) {
599- if ( dep = prop . depends ) {
616+ fnProp = prop ;
617+ if ( dep = fnProp . depends ) {
600618 // This is a computed observable. We will observe any declared dependencies.
601619 if ( obj . _vw && obj . _ocp ) {
602620 // Observable contextual parameter, so context was ocp object. Now move context to view.data for dependencies
@@ -609,7 +627,17 @@ if (!$.observe) {
609627 }
610628 observeObjects ( concat . apply ( [ ] , [ [ obj ] , dependsPaths ( dep , obj , callback ) ] ) ) ;
611629 }
612- break ;
630+
631+ if ( isGet ) {
632+ if ( ! prts [ 0 ] ) {
633+ bindArray ( callback , fnProp . call ( obj ) , unobserve ) ;
634+ break ;
635+ }
636+ prop = fnProp . call ( obj ) ;
637+ if ( ! prop ) {
638+ break ;
639+ }
640+ }
613641 }
614642 obj = prop ;
615643 }
@@ -721,6 +749,8 @@ if (!$.observe) {
721749 }
722750 }
723751
752+ //END OF FUNCTIONS
753+
724754 var ns = observeStr ,
725755 paths = this != 1 // Using != for IE<10 bug- see jsviews/issues/237
726756 ? concat . apply ( [ ] , arguments ) // Flatten the arguments - this is a 'recursive call' with params using the 'wrapped array'
@@ -729,7 +759,6 @@ if (!$.observe) {
729759 lastArg = paths . pop ( ) || false ,
730760 m = paths . length ;
731761
732- //END OF FUNCTIONS
733762 if ( lastArg + "" === lastArg ) { // If last arg is a string then this observe call is part of an observeAll call,
734763 allPath = lastArg ; // and the last three args are the parentObs array, the filter, and the allPath string.
735764 parentObs = paths . pop ( ) ;
@@ -792,9 +821,10 @@ if (!$.observe) {
792821 }
793822
794823 var initialNs ,
795- allowArray = this == 1 ? 0 : 1 , // If this == 1, this is a call from observeAndBind - doing binding of datalink expressions. We don't bind
796- // arrayChange events in this scenario. Instead, {^{for}} and similar do specific arrayChange binding to the tagCtx.args[0] value, in onAfterLink.
797- // Note deliberately using this == 1, rather than this === 1 because of IE<10 bug- see jsviews/issues/237
824+ allowArray = this == 1 ? 0 : 1 , // If this == 1, this is a call from observeAndBind (doing binding of datalink expressions),
825+ // and tag.onArrayChange is not set to true. We don't bind arrayChange events in this scenario. Instead, {^{for}} and similar
826+ // do specific arrayChange binding to the tagCtx.args[0] value, in onAfterLink.
827+ // Note deliberately using this == 1, rather than this === 1 because of IE<10 bug - see jsviews/issues/237
798828 paths = slice . call ( arguments ) ,
799829 pth = paths [ 0 ] ;
800830
@@ -805,14 +835,42 @@ if (!$.observe) {
805835 return innerObserve . apply ( 1 , paths ) ;
806836 } ;
807837
808- $observable = function ( ns , data ) {
809- if ( arguments . length === 1 ) {
838+ asyncBatch . wait = function ( ) {
839+ var batch = this ;
840+ batch . _go = 1 ;
841+ setTimeout ( function ( ) {
842+ batch . trigger ( true ) ;
843+ batch . _go = 0 ;
844+ batch . paths = { } ;
845+ } ) ;
846+ } ;
847+
848+ $observable = function ( ns , data , delay ) {
849+ if ( ns + "" !== ns ) {
850+ delay = data ;
810851 data = ns ;
811852 ns = "" ;
812853 }
813- return $isArray ( data )
854+ delay = delay === undefined ? $subSettingsAdvanced . asyncObserve : delay ;
855+ var observable = $isArray ( data )
814856 ? new ArrayObservable ( ns , data )
815857 : new ObjectObservable ( ns , data ) ;
858+ if ( delay ) {
859+ if ( delay === true ) {
860+ observable . async = true ;
861+ delay = asyncBatch ;
862+ }
863+ if ( ! delay . trigger ) {
864+ if ( $isArray ( delay ) ) {
865+ delay . trigger = batchTrigger ;
866+ delay . paths = { } ;
867+ } else {
868+ delay = undefined ;
869+ }
870+ }
871+ observable . _batch = delay ;
872+ }
873+ return observable ;
816874 } ;
817875
818876 //========================== Initialize ==========================
@@ -848,10 +906,11 @@ if (!$.observe) {
848906
849907 setProperty : function ( path , value , nonStrict ) {
850908 path = path || "" ;
851- var key , pair , parts ,
909+ var key , pair , parts , tempBatch ,
852910 multi = path + "" !== path , // Hash of paths
853911 self = this ,
854- object = self . _data ;
912+ object = self . _data ,
913+ batch = self . _batch ;
855914
856915 if ( object ) {
857916 if ( multi ) {
@@ -865,10 +924,18 @@ if (!$.observe) {
865924 self . setProperty ( pair . name , pair . value , nonStrict === undefined || nonStrict ) ; //If nonStrict not specified, default to true;
866925 }
867926 } else {
868- // Object representation where property name is path and property value is value.
869- for ( key in path ) {
927+ if ( ! batch ) {
928+ self . _batch = tempBatch = [ ] ;
929+ tempBatch . trigger = batchTrigger ;
930+ tempBatch . paths = { } ;
931+ }
932+ for ( key in path ) { // Object representation where property name is path and property value is value.
870933 self . setProperty ( key , path [ key ] , nonStrict ) ;
871934 }
935+ if ( tempBatch ) {
936+ self . _batch . trigger ( ) ;
937+ self . _batch = undefined ;
938+ }
872939 }
873940 } else if ( path !== $expando ) {
874941 // Simple single property case.
@@ -931,8 +998,24 @@ if (!$.observe) {
931998 }
932999 } ,
9331000
934- _trigger : function ( target , eventArgs ) {
935- $ ( target ) . triggerHandler ( propertyChangeStr + ( this . _ns ? "." + / ^ \S + / . exec ( this . _ns ) [ 0 ] : "" ) , eventArgs ) ; // If white-space separated namespaces, use first one only
1001+ _trigger : function ( target , eventArgs , force ) {
1002+ var key , batch , previous ,
1003+ self = this ;
1004+ if ( $ . hasData ( target ) ) {
1005+ if ( ! force && ( batch = self . _batch ) ) {
1006+ if ( self . async && ! batch . _go ) {
1007+ batch . wait ( ) ;
1008+ }
1009+ batch . push ( [ self , target , eventArgs ] ) ;
1010+ key = $data ( target ) . obId + eventArgs . path ;
1011+ if ( previous = batch . paths [ key ] ) {
1012+ batch [ previous - 1 ] . skip = 1 ;
1013+ }
1014+ batch . paths [ key ] = batch . length ;
1015+ } else {
1016+ $ ( target ) . triggerHandler ( propertyChangeStr + ( this . _ns ? "." + / ^ \S + / . exec ( this . _ns ) [ 0 ] : "" ) , eventArgs ) ; // If white-space separated namespaces, use first one only
1017+ }
1018+ }
9361019 }
9371020 } ;
9381021
@@ -1082,17 +1165,28 @@ if (!$.observe) {
10821165 return self ;
10831166 } ,
10841167
1085- _trigger : function ( eventArgs , oldLength ) {
1086- var self = this ,
1087- _data = self . _data ,
1088- length = _data . length ,
1089- $_data = $ ( [ _data ] ) ;
1090- if ( self . _srt ) {
1091- eventArgs . refresh = true ; // We are sorting during refresh
1092- } else if ( length !== oldLength ) { // We have finished sort operations during refresh
1093- $_data . triggerHandler ( propertyChangeStr , { change : "set" , path : "length" , value : length , oldValue : oldLength } ) ;
1168+ _trigger : function ( eventArgs , oldLength , force ) {
1169+ var length , _data , batch ,
1170+ self = this ;
1171+ if ( $ . hasData ( _data = self . _data ) ) {
1172+ if ( ! force && ( batch = self . _batch ) ) {
1173+ eventArgs . _dly = true ; // Delayed event (async or batch change)
1174+ batch . push ( [ self , eventArgs , oldLength ] ) ;
1175+ if ( self . async && ! batch . _go ) {
1176+ batch . wait ( ) ;
1177+ }
1178+ } else {
1179+ length = _data . length ;
1180+ _data = $ ( [ _data ] ) ;
1181+
1182+ if ( self . _srt ) {
1183+ eventArgs . refresh = true ; // We are sorting during refresh
1184+ } else if ( length !== oldLength ) { // We have finished sort operations during refresh
1185+ _data . triggerHandler ( propertyChangeStr , { change : "set" , path : "length" , value : length , oldValue : oldLength } ) ;
1186+ }
1187+ _data . triggerHandler ( arrayChangeStr + ( self . _ns ? "." + / ^ \S + / . exec ( self . _ns ) [ 0 ] : "" ) , eventArgs ) ; // If white-space separated namespaces, use first one only
1188+ }
10941189 }
1095- $_data . triggerHandler ( arrayChangeStr + ( self . _ns ? "." + / ^ \S + / . exec ( self . _ns ) [ 0 ] : "" ) , eventArgs ) ; // If white-space separated namespaces, use first one only
10961190 }
10971191 } ;
10981192
@@ -1243,8 +1337,9 @@ if (!$.observe) {
12431337 } ;
12441338
12451339 $sub . advSet = function ( ) { // refresh advanced settings
1340+ $subSettingsAdvanced = $subSettings . advanced ;
12461341 $sub . _gccb = this . _gccb ; // getContextCallback method
1247- global . _jsv = $subSettings . advanced . _jsv
1342+ global . _jsv = $subSettingsAdvanced . _jsv
12481343 ? { // create global _jsv, for accessing views, etc
12491344 cbBindings : cbBindingsStore
12501345 }
@@ -1253,6 +1348,10 @@ if (!$.observe) {
12531348 $sub . _dp = dependsPaths ;
12541349 $sub . _gck = getCbKey ;
12551350 $sub . _obs = $observe ;
1351+ $subSettingsAdvanced = $subSettings . advanced = $subSettingsAdvanced || {
1352+ useViews : false ,
1353+ _jsv : false // For global access to JsViews store
1354+ } ;
12561355}
12571356
12581357return $ ;
0 commit comments