Skip to content

Commit 5351d23

Browse files
author
Jon M. Mease
committed
Refactor _performRestyle to pull out utility functions.
1 parent dfb52c4 commit 5351d23

File tree

1 file changed

+153
-56
lines changed

1 file changed

+153
-56
lines changed

js/src/Figure.js

Lines changed: 153 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -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+
17891886
module.exports = {
17901887
FigureView : FigureView,
17911888
FigureModel: FigureModel,

0 commit comments

Comments
 (0)