1212var Lib = require ( '../lib' ) ;
1313var isPlainObject = Lib . isPlainObject ;
1414var PlotSchema = require ( './plot_schema' ) ;
15+ var Plots = require ( '../plots/plots' ) ;
1516var plotAttributes = require ( '../plots/attributes' ) ;
1617var Template = require ( './plot_template' ) ;
18+ var dfltConfig = require ( './plot_config' ) ;
1719
1820/**
1921 * Plotly.makeTemplate: create a template off an existing figure to reuse
@@ -29,8 +31,13 @@ var Template = require('./plot_template');
2931 * `layout.template` in another figure.
3032 */
3133module . exports = function makeTemplate ( figure ) {
34+ figure = Lib . extendDeep ( { _context : dfltConfig } , figure ) ;
35+ Plots . supplyDefaults ( figure ) ;
3236 var data = figure . data || [ ] ;
3337 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 ;
3441
3542 var template = {
3643 data : { } ,
@@ -75,6 +82,7 @@ module.exports = function makeTemplate(figure) {
7582 * since valid options can be context-dependent. It could be solved with
7683 * a *list* of values, but that would be huge complexity for little gain.
7784 */
85+ delete template . layout . template ;
7886 var oldTemplate = layout . template ;
7987 if ( isPlainObject ( oldTemplate ) ) {
8088 var oldLayoutTemplate = oldTemplate . layout ;
@@ -93,9 +101,9 @@ module.exports = function makeTemplate(figure) {
93101 typeLen = typeTemplates . length ;
94102 oldTypeLen = oldTypeTemplates . length ;
95103 for ( i = 0 ; i < typeLen ; i ++ ) {
96- mergeTemplates ( oldTypeTemplates [ i % typeLen ] , typeTemplates [ i ] ) ;
104+ mergeTemplates ( oldTypeTemplates [ i % oldTypeLen ] , typeTemplates [ i ] ) ;
97105 }
98- for ( ; i < oldTypeLen ; i ++ ) {
106+ for ( i = typeLen ; i < oldTypeLen ; i ++ ) {
99107 typeTemplates . push ( Lib . extendDeep ( { } , oldTypeTemplates [ i ] ) ) ;
100108 }
101109 }
@@ -121,68 +129,120 @@ function mergeTemplates(oldTemplate, newTemplate) {
121129 var oldKeys = Object . keys ( oldTemplate ) . sort ( ) ;
122130 var i , j ;
123131
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+
124153 for ( i = 0 ; i < oldKeys . length ; i ++ ) {
125154 var key = oldKeys [ i ] ;
126155 var oldVal = oldTemplate [ key ] ;
127156 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 ) ;
140168 }
141- var defaultItems = templater . defaultItems ( ) ;
142- for ( j = 0 ; j < defaultItems . length ; j ++ ) newVal . push ( defaultItems [ j ] ) ;
143169 }
144170 }
145- else newTemplate [ key ] = oldVal ;
146171 }
147172}
148173
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 ) ;
150180 for ( var key in parent ) {
151181 var child = parent [ key ] ;
152182 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 ;
154196
155- if ( ! attr ||
197+ if ( ! attr || attr . _noTemplating ||
156198 attr . valType === 'data_array' ||
157199 ( attr . arrayOk && Array . isArray ( child ) )
158200 ) {
159201 continue ;
160202 }
161203
162204 if ( ! attr . valType && isPlainObject ( child ) ) {
163- walkStyleKeys ( child , templateOut , getAttributeInfo , nextPath ) ;
205+ walkStyleKeys ( child , templateOut , getAttributeInfo , nextPath , nextBasePath ) ;
164206 }
165207 else if ( attr . _isLinkedToArray && Array . isArray ( child ) ) {
166208 var dfltDone = false ;
167209 var namedIndex = 0 ;
210+ var usedNames = { } ;
168211 for ( var i = 0 ; i < child . length ; i ++ ) {
169212 var item = child [ i ] ;
170213 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+ }
175224 }
176225 else if ( ! dfltDone ) {
177226 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+
180240 dfltDone = true ;
181241 }
182242 }
183243 }
184244 }
185- else if ( attr . role === 'style' ) {
245+ else {
186246 var templateProp = Lib . nestedProperty ( templateOut , nextPath ) ;
187247 templateProp . set ( child ) ;
188248 }
0 commit comments