12
12
var Lib = require ( '../lib' ) ;
13
13
var isPlainObject = Lib . isPlainObject ;
14
14
var PlotSchema = require ( './plot_schema' ) ;
15
+ var Plots = require ( '../plots/plots' ) ;
15
16
var plotAttributes = require ( '../plots/attributes' ) ;
16
17
var Template = require ( './plot_template' ) ;
18
+ var dfltConfig = require ( './plot_config' ) ;
17
19
18
20
/**
19
21
* Plotly.makeTemplate: create a template off an existing figure to reuse
@@ -29,8 +31,13 @@ var Template = require('./plot_template');
29
31
* `layout.template` in another figure.
30
32
*/
31
33
module . exports = function makeTemplate ( figure ) {
34
+ figure = Lib . extendDeep ( { _context : dfltConfig } , figure ) ;
35
+ Plots . supplyDefaults ( figure ) ;
32
36
var data = figure . data || [ ] ;
33
37
var layout = figure . layout || { } ;
38
+ // copy over a few items to help follow the schema
39
+ layout . _basePlotModules = figure . _fullLayout . _basePlotModules ;
40
+ layout . _modules = figure . _fullLayout . _modules ;
34
41
35
42
var template = {
36
43
data : { } ,
@@ -75,6 +82,7 @@ module.exports = function makeTemplate(figure) {
75
82
* since valid options can be context-dependent. It could be solved with
76
83
* a *list* of values, but that would be huge complexity for little gain.
77
84
*/
85
+ delete template . layout . template ;
78
86
var oldTemplate = layout . template ;
79
87
if ( isPlainObject ( oldTemplate ) ) {
80
88
var oldLayoutTemplate = oldTemplate . layout ;
@@ -93,9 +101,9 @@ module.exports = function makeTemplate(figure) {
93
101
typeLen = typeTemplates . length ;
94
102
oldTypeLen = oldTypeTemplates . length ;
95
103
for ( i = 0 ; i < typeLen ; i ++ ) {
96
- mergeTemplates ( oldTypeTemplates [ i % typeLen ] , typeTemplates [ i ] ) ;
104
+ mergeTemplates ( oldTypeTemplates [ i % oldTypeLen ] , typeTemplates [ i ] ) ;
97
105
}
98
- for ( ; i < oldTypeLen ; i ++ ) {
106
+ for ( i = typeLen ; i < oldTypeLen ; i ++ ) {
99
107
typeTemplates . push ( Lib . extendDeep ( { } , oldTypeTemplates [ i ] ) ) ;
100
108
}
101
109
}
@@ -121,68 +129,120 @@ function mergeTemplates(oldTemplate, newTemplate) {
121
129
var oldKeys = Object . keys ( oldTemplate ) . sort ( ) ;
122
130
var i , j ;
123
131
132
+ function mergeOne ( oldVal , newVal , key ) {
133
+ if ( isPlainObject ( newVal ) && isPlainObject ( oldVal ) ) {
134
+ mergeTemplates ( oldVal , newVal ) ;
135
+ }
136
+ else if ( Array . isArray ( newVal ) && Array . isArray ( oldVal ) ) {
137
+ // Note: omitted `inclusionAttr` from arrayTemplater here,
138
+ // it's irrelevant as we only want the resulting `_template`.
139
+ var templater = Template . arrayTemplater ( { _template : oldTemplate } , key ) ;
140
+ for ( j = 0 ; j < newVal . length ; j ++ ) {
141
+ var item = newVal [ j ] ;
142
+ var oldItem = templater . newItem ( item ) . _template ;
143
+ if ( oldItem ) mergeTemplates ( oldItem , item ) ;
144
+ }
145
+ var defaultItems = templater . defaultItems ( ) ;
146
+ for ( j = 0 ; j < defaultItems . length ; j ++ ) newVal . push ( defaultItems [ j ] . _template ) ;
147
+
148
+ // templateitemname only applies to receiving plots
149
+ for ( j = 0 ; j < newVal . length ; j ++ ) delete newVal [ j ] . templateitemname ;
150
+ }
151
+ }
152
+
124
153
for ( i = 0 ; i < oldKeys . length ; i ++ ) {
125
154
var key = oldKeys [ i ] ;
126
155
var oldVal = oldTemplate [ key ] ;
127
156
if ( key in newTemplate ) {
128
- var newVal = newTemplate [ key ] ;
129
- if ( isPlainObject ( newVal ) && isPlainObject ( oldVal ) ) {
130
- mergeTemplates ( oldVal , newVal ) ;
131
- }
132
- else if ( Array . isArray ( newVal ) && Array . isArray ( oldVal ) ) {
133
- // Note: omitted `inclusionAttr` from arrayTemplater here,
134
- // it's irrelevant as we only want the resulting `_template`.
135
- var templater = Template . arrayTemplater ( { _template : oldTemplate } , key ) ;
136
- for ( j = 0 ; j < newVal . length ; j ++ ) {
137
- var item = newVal [ j ] ;
138
- var oldItem = templater . newItem ( item ) . _template ;
139
- if ( oldItem ) mergeTemplates ( oldItem , item ) ;
157
+ mergeOne ( oldVal , newTemplate [ key ] , key ) ;
158
+ }
159
+ else newTemplate [ key ] = oldVal ;
160
+
161
+ // if this is a base key from the old template (eg xaxis), look for
162
+ // extended keys (eg xaxis2) in the new template to merge into
163
+ if ( getBaseKey ( key ) === key ) {
164
+ for ( var key2 in newTemplate ) {
165
+ var baseKey2 = getBaseKey ( key2 ) ;
166
+ if ( key2 !== baseKey2 && baseKey2 === key && ! ( key2 in oldTemplate ) ) {
167
+ mergeOne ( oldVal , newTemplate [ key2 ] , key ) ;
140
168
}
141
- var defaultItems = templater . defaultItems ( ) ;
142
- for ( j = 0 ; j < defaultItems . length ; j ++ ) newVal . push ( defaultItems [ j ] ) ;
143
169
}
144
170
}
145
- else newTemplate [ key ] = oldVal ;
146
171
}
147
172
}
148
173
149
- function walkStyleKeys ( parent , templateOut , getAttributeInfo , path ) {
174
+ function getBaseKey ( key ) {
175
+ return key . replace ( / [ 0 - 9 ] + $ / , '' ) ;
176
+ }
177
+
178
+ function walkStyleKeys ( parent , templateOut , getAttributeInfo , path , basePath ) {
179
+ var pathAttr = basePath && getAttributeInfo ( basePath ) ;
150
180
for ( var key in parent ) {
151
181
var child = parent [ key ] ;
152
182
var nextPath = getNextPath ( parent , key , path ) ;
153
- var attr = getAttributeInfo ( nextPath ) ;
183
+ var nextBasePath = getNextPath ( parent , key , basePath ) ;
184
+ var attr = getAttributeInfo ( nextBasePath ) ;
185
+ if ( ! attr ) {
186
+ var baseKey = getBaseKey ( key ) ;
187
+ if ( baseKey !== key ) {
188
+ nextBasePath = getNextPath ( parent , baseKey , basePath ) ;
189
+ attr = getAttributeInfo ( nextBasePath ) ;
190
+ }
191
+ }
192
+
193
+ // we'll get an attr if path starts with a valid part, then has an
194
+ // invalid ending. Make sure we got all the way to the end.
195
+ if ( pathAttr && ( pathAttr === attr ) ) continue ;
154
196
155
- if ( ! attr ||
197
+ if ( ! attr || attr . _noTemplating ||
156
198
attr . valType === 'data_array' ||
157
199
( attr . arrayOk && Array . isArray ( child ) )
158
200
) {
159
201
continue ;
160
202
}
161
203
162
204
if ( ! attr . valType && isPlainObject ( child ) ) {
163
- walkStyleKeys ( child , templateOut , getAttributeInfo , nextPath ) ;
205
+ walkStyleKeys ( child , templateOut , getAttributeInfo , nextPath , nextBasePath ) ;
164
206
}
165
207
else if ( attr . _isLinkedToArray && Array . isArray ( child ) ) {
166
208
var dfltDone = false ;
167
209
var namedIndex = 0 ;
210
+ var usedNames = { } ;
168
211
for ( var i = 0 ; i < child . length ; i ++ ) {
169
212
var item = child [ i ] ;
170
213
if ( isPlainObject ( item ) ) {
171
- if ( item . name ) {
172
- walkStyleKeys ( item , templateOut , getAttributeInfo ,
173
- getNextPath ( child , namedIndex , nextPath ) ) ;
174
- namedIndex ++ ;
214
+ var name = item . name ;
215
+ if ( name ) {
216
+ if ( ! usedNames [ name ] ) {
217
+ // named array items: allow all attributes except data arrays
218
+ walkStyleKeys ( item , templateOut , getAttributeInfo ,
219
+ getNextPath ( child , namedIndex , nextPath ) ,
220
+ getNextPath ( child , namedIndex , nextBasePath ) ) ;
221
+ namedIndex ++ ;
222
+ usedNames [ name ] = 1 ;
223
+ }
175
224
}
176
225
else if ( ! dfltDone ) {
177
226
var dfltKey = Template . arrayDefaultKey ( key ) ;
178
- walkStyleKeys ( item , templateOut , getAttributeInfo ,
179
- getNextPath ( parent , dfltKey , path ) ) ;
227
+ var dfltPath = getNextPath ( parent , dfltKey , path ) ;
228
+
229
+ // getAttributeInfo will fail if we try to use dfltKey directly.
230
+ // Instead put this item into the next array element, then
231
+ // pull it out and move it to dfltKey.
232
+ var pathInArray = getNextPath ( child , namedIndex , nextPath ) ;
233
+ walkStyleKeys ( item , templateOut , getAttributeInfo , pathInArray ,
234
+ getNextPath ( child , namedIndex , nextBasePath ) ) ;
235
+ var itemPropInArray = Lib . nestedProperty ( templateOut , pathInArray ) ;
236
+ var dfltProp = Lib . nestedProperty ( templateOut , dfltPath ) ;
237
+ dfltProp . set ( itemPropInArray . get ( ) ) ;
238
+ itemPropInArray . set ( null ) ;
239
+
180
240
dfltDone = true ;
181
241
}
182
242
}
183
243
}
184
244
}
185
- else if ( attr . role === 'style' ) {
245
+ else {
186
246
var templateProp = Lib . nestedProperty ( templateOut , nextPath ) ;
187
247
templateProp . set ( child ) ;
188
248
}
0 commit comments