12
12
var Plotly = require ( '../plotly' ) ;
13
13
var Lib = require ( '../lib' ) ;
14
14
15
+ var attrPrefixRegex = / ^ ( d a t a | l a y o u t ) ( \[ ( - ? [ 0 - 9 ] * ) \] ) ? \. ( .* ) $ / ;
16
+
17
+ /*
18
+ * This function checks to see if a set of bindings is compatible
19
+ * with automatic two-way binding. The criteria right now are that
20
+ *
21
+ * 1. multiple traces may be affected
22
+ * 2. only one property may be affected
23
+ */
24
+ exports . bindingsAreConsistent = function ( currentBindings , newBindings ) {
25
+ // If they're not both arrays of equal length, return false:
26
+ if ( ! newBindings || ! currentBindings || currentBindings . length !== newBindings . length ) {
27
+ return false ;
28
+ }
29
+
30
+ var n = currentBindings . length ;
31
+
32
+ for ( var i = 0 ; i < n ; i ++ ) {
33
+ // This is not the most efficient check, but the pathological case where there
34
+ // are an excessive number of bindings should be rare, and at any rate we really
35
+ // try to bail out early at every opportunity.
36
+ if ( currentBindings . indexOf ( newBindings [ i ] ) === - 1 ) {
37
+ return false ;
38
+ }
39
+ }
40
+
41
+ return true ;
42
+ } ;
43
+
44
+ exports . evaluateAPICommandBinding = function ( gd , attrName ) {
45
+ var match = attrName . match ( attrPrefixRegex ) ;
46
+
47
+ if ( ! match ) {
48
+ return null ;
49
+ }
50
+
51
+ var group = match [ 1 ] ;
52
+ var propStr = match [ 4 ] ;
53
+ var container ;
54
+
55
+ switch ( group ) {
56
+ case 'data' :
57
+ container = gd . _fullData [ parseInt ( match [ 3 ] ) ] ;
58
+ break ;
59
+ case 'layout' :
60
+ container = gd . _fullLayout ;
61
+ break ;
62
+ default :
63
+ return null ;
64
+ }
65
+
66
+ return Lib . nestedProperty ( container , propStr ) . get ( ) ;
67
+ } ;
68
+
15
69
exports . executeAPICommand = function ( gd , method , args ) {
16
70
var apiMethod = Plotly [ method ] ;
17
71
@@ -44,7 +98,7 @@ exports.computeAPICommandBindings = function(gd, method, args) {
44
98
// This case could be analyzed more in-depth, but for a start,
45
99
// we'll assume that the only relevant modification an animation
46
100
// makes that's meaningfully tracked is the frame:
47
- bindings = [ 'layout. _currentFrame' ] ;
101
+ bindings = [ { type : 'layout' , prop : ' _currentFrame'} ] ;
48
102
break ;
49
103
default :
50
104
// We'll elect to fail-non-fatal since this is a correct
@@ -68,14 +122,14 @@ function computeLayoutBindings(gd, args) {
68
122
}
69
123
70
124
crawl ( aobj , function ( path ) {
71
- bindings . push ( 'layout' + path ) ;
72
- } ) ;
125
+ bindings . push ( { type : 'layout' , prop : path } ) ;
126
+ } , '' , 0 ) ;
73
127
74
128
return bindings ;
75
129
}
76
130
77
131
function computeDataBindings ( gd , args ) {
78
- var i , traces , astr , val , aobj ;
132
+ var traces , astr , val , aobj ;
79
133
var bindings = [ ] ;
80
134
81
135
// Logic copied from Plotly.restyle:
@@ -97,41 +151,45 @@ function computeDataBindings(gd, args) {
97
151
}
98
152
99
153
if ( traces === undefined ) {
100
- traces = [ ] ;
101
- for ( i = 0 ; i < gd . data . length ; i ++ ) {
102
- traces . push ( i ) ;
103
- }
154
+ // Explicitly assign this to null instead of undefined:
155
+ traces = null ;
104
156
}
105
157
106
158
crawl ( aobj , function ( path , attrName , attr ) {
107
- var nAttr ;
159
+ var thisTraces ;
108
160
if ( Array . isArray ( attr ) ) {
109
- nAttr = Math . min ( attr . length , traces . length ) ;
161
+ var nAttr = Math . min ( attr . length , gd . data . length ) ;
162
+ if ( traces ) {
163
+ nAttr = Math . min ( nAttr , traces . length ) ;
164
+ }
165
+ thisTraces = [ ] ;
166
+ for ( var j = 0 ; j < nAttr ; j ++ ) {
167
+ thisTraces [ j ] = traces ? traces [ j ] : j ;
168
+ }
110
169
} else {
111
- nAttr = traces . length ;
112
- }
113
- for ( var j = 0 ; j < nAttr ; j ++ ) {
114
- bindings . push ( 'data[' + traces [ j ] + ']' + path ) ;
170
+ thisTraces = traces ? traces . slice ( 0 ) : null ;
115
171
}
116
- } ) ;
172
+
173
+ bindings . push ( {
174
+ type : 'data' ,
175
+ prop : path ,
176
+ traces : thisTraces
177
+ } ) ;
178
+ } , '' , 0 ) ;
117
179
118
180
return bindings ;
119
181
}
120
182
121
- function crawl ( attrs , callback , path ) {
122
- if ( path === undefined ) {
123
- path = '' ;
124
- }
125
-
183
+ function crawl ( attrs , callback , path , depth ) {
126
184
Object . keys ( attrs ) . forEach ( function ( attrName ) {
127
185
var attr = attrs [ attrName ] ;
128
186
129
187
if ( attrName [ 0 ] === '_' ) return ;
130
188
131
- var thisPath = path + '.' + attrName ;
189
+ var thisPath = path + ( depth > 0 ? '.' : '' ) + attrName ;
132
190
133
191
if ( Lib . isPlainObject ( attr ) ) {
134
- crawl ( attr , callback , thisPath ) ;
192
+ crawl ( attr , callback , thisPath , depth + 1 ) ;
135
193
} else {
136
194
// Only execute the callback on leaf nodes:
137
195
callback ( thisPath , attrName , attr ) ;
0 commit comments