@@ -380,7 +380,18 @@ var FigureModel = widgets.DOMWidgetModel.extend({
380380
381381 // Message tracking
382382 // ----------------
383+ /**
384+ * @type {Number }
385+ * layout_edit_id of the last layout modification operation
386+ * requested by the Python side
387+ */
383388 _last_layout_edit_id : 0 ,
389+
390+ /**
391+ * @type {Number }
392+ * trace_edit_id of the last trace modification operation
393+ * requested by the Python side
394+ */
384395 _last_trace_edit_id : 0
385396 } ) ,
386397
@@ -430,7 +441,11 @@ var FigureModel = widgets.DOMWidgetModel.extend({
430441 return trace_indexes
431442 } ,
432443
433- /** Log changes to the _data trait */
444+ /**
445+ * Log changes to the _data trait
446+ *
447+ * This should only happed on FigureModel initialization
448+ */
434449 do_data : function ( ) {
435450 console . log ( 'Figure Model: do_data' ) ;
436451 var data = this . get ( '_data' ) ;
@@ -440,7 +455,11 @@ var FigureModel = widgets.DOMWidgetModel.extend({
440455 }
441456 } ,
442457
443- /** Log changes to the _layout trait */
458+ /**
459+ * Log changes to the _layout trait
460+ *
461+ * This should only happed on FigureModel initialization
462+ */
444463 do_layout : function ( ) {
445464 console . log ( 'Figure Model: do_layout' ) ;
446465 var layout = this . get ( '_layout' ) ;
@@ -450,10 +469,8 @@ var FigureModel = widgets.DOMWidgetModel.extend({
450469 }
451470 } ,
452471
453-
454-
455472 /**
456- * Perform addTraces operation on the model
473+ * Handle addTraces message and perform operation on the model
457474 */
458475 do_addTraces : function ( ) {
459476 // add trace to plot
@@ -473,7 +490,7 @@ var FigureModel = widgets.DOMWidgetModel.extend({
473490 } ,
474491
475492 /**
476- * Perform the deleteTraces operation on the model
493+ * Handle deleteTraces message and perform operation on the model
477494 */
478495 do_deleteTraces : function ( ) {
479496 // remove traces from plot
@@ -494,7 +511,7 @@ var FigureModel = widgets.DOMWidgetModel.extend({
494511 } ,
495512
496513 /**
497- * Perform moveTraces operation on the model
514+ * Handle moveTraces message and perform operation on the model
498515 */
499516 do_moveTraces : function ( ) {
500517 console . log ( 'Figure Model: do_moveTraces' ) ;
@@ -533,77 +550,69 @@ var FigureModel = widgets.DOMWidgetModel.extend({
533550 }
534551 } ,
535552
553+ /**
554+ * Handle restyle message
555+ */
536556 do_restyle : function ( ) {
537557 console . log ( 'FigureModel: do_restyle' ) ;
538558
539559 /** @type {Py2JsRestyleMsg } */
540560 var msgData = this . get ( '_py2js_restyle' ) ;
541561 if ( msgData !== null ) {
542562 var restyleData = msgData . restyle_data ;
543- var trace_indexes = this . _normalize_trace_indexes ( msgData . restyle_traces ) ;
544- this . _performRestyle ( restyleData , trace_indexes )
563+ var restyleTraces = this . _normalize_trace_indexes ( msgData . restyle_traces ) ;
564+ this . _performRestyle ( restyleData , restyleTraces )
545565 }
546566 } ,
547567
548- _performRestyle : function ( style , trace_indexes ) {
568+ /**
569+ * Perform the restyle operation on the model
570+ * @param {Object } restyleData
571+ * Restyle data as accepted by Plotly.restyle
572+ * @param {Array.<Number> } restyleTraces
573+ * Array of indexes of the traces that the resytle operation applies to
574+ * @private
575+ */
576+ _performRestyle : function ( restyleData , restyleTraces ) {
549577
550- for ( var rawKey in style ) {
551- if ( ! style . hasOwnProperty ( rawKey ) ) { continue }
552- var v = style [ rawKey ] ;
578+ console . log ( [ '_performRestyle' , restyleData ] , this . get ( '_data' ) ) ;
579+ // Loop over the properties of restyleData
580+ for ( var rawKey in restyleData ) {
581+ if ( ! restyleData . hasOwnProperty ( rawKey ) ) { continue }
553582
554- if ( ! Array . isArray ( v ) ) {
555- v = [ v ]
583+ // Extract value for property and normalize into a value list
584+ var val_array = restyleData [ rawKey ] ;
585+ if ( ! Array . isArray ( val_array ) ) {
586+ val_array = [ val_array ]
556587 }
557588
589+ // Convert raw key string (e.g. 'marker.color') into a key path
590+ // array (e.g. ['marker', 'color']
558591 var keyPath = flattenedKeyToObjectPath ( rawKey ) ;
559592
560- for ( var i = 0 ; i < trace_indexes . length ; i ++ ) {
561- var trace_ind = trace_indexes [ i ] ;
562- var valParent = this . get ( '_data' ) [ trace_ind ] ;
593+ // Get key path of the object to which the new value will be
594+ // assigned
595+ var parentKeyPath = keyPath . slice ( 0 , - 1 ) ;
563596
564- for ( var kp = 0 ; kp < keyPath . length - 1 ; kp ++ ) {
565- var keyPathEl = keyPath [ kp ] ;
597+ // Get final propery / index key
598+ var lastKey = keyPath [ keyPath . length - 1 ] ;
566599
567- // Extend val_parent list if needed
568- if ( Array . isArray ( valParent ) ) {
569- if ( typeof keyPathEl === 'number' ) {
570- while ( valParent . length <= keyPathEl ) {
571- valParent . push ( null )
572- }
573- }
574- } else { // object
575- // Initialize child if needed
576- if ( valParent [ keyPathEl ] === undefined ) {
577- if ( typeof keyPath [ kp + 1 ] === 'number' ) {
578- valParent [ keyPathEl ] = [ ]
579- } else {
580- valParent [ keyPathEl ] = { }
581- }
582- }
583- }
584- valParent = valParent [ keyPathEl ] ;
585- }
600+ // Loop over the indexes of the traces being restyled
601+ for ( var i = 0 ; i < restyleTraces . length ; i ++ ) {
602+ var trace_ind = restyleTraces [ i ] ;
586603
587- var lastKey = keyPath [ keyPath . length - 1 ] ;
588- var trace_v = v [ i % v . length ] ;
604+ // valParent is a reference to the array or object that has
605+ // lastKey as an index or property respectively.
606+ var valParent = getOrInitNestedProperty (
607+ this . get ( '_data' ) [ trace_ind ] , parentKeyPath ) ;
589608
590- if ( trace_v === undefined ) {
591- // Nothing to do
592- } else if ( trace_v === null ) {
593- if ( valParent . hasOwnProperty ( lastKey ) ) {
594- delete valParent [ lastKey ] ;
595- }
596- } else {
597- if ( Array . isArray ( valParent ) && typeof lastKey === 'number' ) {
598- while ( valParent . length <= lastKey ) {
599- // Make sure array is long enough to assign into
600- valParent . push ( null )
601- }
602- }
603- valParent [ lastKey ] = trace_v ;
604- }
609+ var single_val = val_array [ i % val_array . length ] ;
610+
611+ updateKeyValHandleDeleteExtend ( valParent , lastKey , single_val )
605612 }
606613 }
614+
615+ console . log ( [ '_performRestyle' , this . get ( '_data' ) ] ) ;
607616 } ,
608617
609618 do_relayout : function ( ) {
@@ -1786,6 +1795,94 @@ function flattenedKeyToObjectPath(rawKey) {
17861795 return keyPath2
17871796}
17881797
1798+ /**
1799+ * Use an array of keys to perform nested indexing into an object or array,
1800+ * initializing the nested layers if needed.
1801+ *
1802+ * Examples:
1803+ * valParent = {foo: {bar: [23]}}
1804+ * getOrInitNestedProperty(valParent, ['foo', 'bar']) -> [23]
1805+ * valParent unchanged
1806+ *
1807+ * valParent = {foo: {bar: [23]}}
1808+ * getOrInitNestedProperty(valParent, ['foo', 'bar', 0]) -> 23
1809+ * valParent unchanged
1810+ *
1811+ * valParent = {foo: {bar: [23]}}
1812+ * getOrInitNestedProperty(valParent, ['foo', 'baz']) -> {}
1813+ * valParent changed to {foo: {bar: [23], baz: {}}}
1814+ *
1815+ * valParent = {foo: {bar: [23]}}
1816+ * getOrInitNestedProperty(valParent, ['foo', 'bar', 3]) -> null
1817+ * valParent changed to {foo: {bar: [23, null, null, null]}}
1818+ *
1819+ * valParent = {foo: {bar: [23]}}
1820+ * getOrInitNestedProperty(valParent, ['foo', 'baz', 1]) -> null
1821+ * valParent changed to {foo: {bar: [23], baz: [null, null]}}
1822+ *
1823+ * @type {{FigureView: any, FigureModel: any} }
1824+ */
1825+ function getOrInitNestedProperty ( valParent , keyPath ) {
1826+ // Loop over the keyPath elements
1827+ for ( var kp = 0 ; kp < keyPath . length ; kp ++ ) {
1828+ var keyPathEl = keyPath [ kp ] ;
1829+
1830+ // Extend valParent array if needed
1831+ if ( Array . isArray ( valParent ) ) {
1832+ if ( typeof keyPathEl === 'number' ) {
1833+ while ( valParent . length <= keyPathEl ) {
1834+ valParent . push ( null )
1835+ }
1836+ }
1837+ } else { // object
1838+ // Initialize child if needed
1839+ if ( valParent [ keyPathEl ] === undefined ) {
1840+ if ( typeof keyPath [ kp + 1 ] === 'number' ) {
1841+ valParent [ keyPathEl ] = [ ]
1842+ } else {
1843+ valParent [ keyPathEl ] = { }
1844+ }
1845+ }
1846+ }
1847+
1848+ // Update valParent
1849+ valParent = valParent [ keyPathEl ] ;
1850+ }
1851+ return valParent ;
1852+ }
1853+
1854+ /**
1855+ * Update the value associated with an index or property for a parent that
1856+ * is either an Object or an Array respectively.
1857+ *
1858+ * @param {Array|Object } valParent
1859+ * Parent object or array
1860+ * @param {String|Number } key
1861+ * Property name or array index of new value
1862+ * @param val
1863+ * New value.
1864+ * - If undefined, do nothing
1865+ * - If null and the index/property is present, then delete it
1866+ * - Otherwise assign the value at the index or property
1867+ */
1868+ function updateKeyValHandleDeleteExtend ( valParent , key , val ) {
1869+ if ( val === undefined ) {
1870+ // Nothing to do
1871+ } else if ( val === null ) {
1872+ if ( valParent . hasOwnProperty ( key ) ) {
1873+ delete valParent [ key ] ;
1874+ }
1875+ } else {
1876+ if ( Array . isArray ( valParent ) && typeof key === 'number' ) {
1877+ while ( valParent . length <= key ) {
1878+ // Make sure array is long enough to assign into
1879+ valParent . push ( null )
1880+ }
1881+ }
1882+ valParent [ key ] = val ;
1883+ }
1884+ }
1885+
17891886module . exports = {
17901887 FigureView : FigureView ,
17911888 FigureModel : FigureModel ,
0 commit comments