Skip to content

Commit 53577c1

Browse files
committed
[jsroot] dev 24/09/2025 with unzipJSON support
1. Introduce unzipJSON for processing zip strings 2. Better detect div layout when creating canvas svg element 3. Fix minor latex problem 4. Detect default histogram in TGraph to correctly set ranges then
1 parent 0871044 commit 53577c1

File tree

11 files changed

+154
-44
lines changed

11 files changed

+154
-44
lines changed

js/build/jsroot.js

Lines changed: 75 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ const version_id = 'dev',
1212

1313
/** @summary version date
1414
* @desc Release date in format day/month/year like '14/04/2022' */
15-
version_date = '18/09/2025',
15+
version_date = '24/09/2025',
1616

1717
/** @summary version id and date
1818
* @desc Produced by concatenation of {@link version_id} and {@link version_date}
@@ -1567,7 +1567,8 @@ function createHistogram(typename, nbinsx, nbinsy, nbinsz) {
15671567
* @desc Title may include axes titles, provided with ';' symbol like "Title;x;y;z" */
15681568

15691569
function setHistogramTitle(histo, title) {
1570-
if (!histo) return;
1570+
if (!histo || !isStr(title))
1571+
return;
15711572
if (title.indexOf(';') < 0)
15721573
histo.fTitle = title;
15731574
else {
@@ -9231,6 +9232,14 @@ class BasePainter {
92319232

92329233
rect.changed = false;
92339234

9235+
if (!rect.width && !rect.height && !main.empty() && main.attr('style')) {
9236+
const ws = main.style('width'), hs = main.style('height');
9237+
if (isStr(ws) && isStr(hs) && ws.match(/^\d+px$/) && hs.match(/^\d+px$/)) {
9238+
rect.width = parseInt(ws.slice(0, ws.length-2));
9239+
rect.height = parseInt(hs.slice(0, hs.length-2));
9240+
}
9241+
}
9242+
92349243
if (old_h && old_w && (old_h > 0) && (old_w > 0)) {
92359244
if ((old_h !== rect.height) || (old_w !== rect.width))
92369245
rect.changed = (check_level > 1) || (rect.width / old_w < 0.99) || (rect.width / old_w > 1.01) || (rect.height / old_h < 0.99) || (rect.height / old_h > 1.01);
@@ -10490,23 +10499,24 @@ function parseLatex(node, arg, label, curr) {
1049010499

1049110500
const extractLowUp = name => {
1049210501
const res = {};
10502+
1049310503
if (name) {
1049410504
label = '{' + label;
1049510505
res[name] = extractSubLabel(name === 'low' ? '_' : '^');
1049610506
if (res[name] === -1) return false;
1049710507
}
1049810508

1049910509
while (label) {
10500-
if (label[0] === '_') {
10510+
if ((label[0] === '_') && !res.low) {
1050110511
label = label.slice(1);
10502-
res.low = !res.low ? extractSubLabel('_') : -1;
10512+
res.low = extractSubLabel('_');
1050310513
if (res.low === -1) {
10504-
console.log(`error with ${found.name} low limit`);
10514+
console.log(`error with ${found.name} low limit ${label}`);
1050510515
return false;
1050610516
}
10507-
} else if (label[0] === '^') {
10517+
} else if ((label[0] === '^') && !res.up) {
1050810518
label = label.slice(1);
10509-
res.up = !res.up ? extractSubLabel('^') : -1;
10519+
res.up = extractSubLabel('^');
1051010520
if (res.up === -1) {
1051110521
console.log(`error with ${found.name} upper limit ${label}`);
1051210522
return false;
@@ -86869,7 +86879,7 @@ class TPadPainter extends ObjectPainter {
8686986879
this.createAttFill({ attr: this.#pad });
8687086880

8687186881
if ((rect.width <= lmt) || (rect.height <= lmt)) {
86872-
if (this.hasSnapId()) {
86882+
if (!this.hasSnapId()) {
8687386883
svg.style('display', 'none');
8687486884
console.warn(`Hide canvas while geometry too small w=${rect.width} h=${rect.height}`);
8687586885
}
@@ -102976,6 +102986,24 @@ let TGraphPainter$1 = class TGraphPainter extends ObjectPainter {
102976102986
/** @summary Return histogram object used for axis drawings */
102977102987
getHistogram() { return this.getObject()?.fHistogram; }
102978102988

102989+
/** @summary Return true if histogram not present or has dummy ranges (for requested axis) */
102990+
isDummyHistogram(check_axis) {
102991+
const histo = this.getHistogram();
102992+
if (!histo)
102993+
return true;
102994+
102995+
let is_normal = false;
102996+
if (check_axis !== 'y')
102997+
is_normal ||= (histo.fXaxis.fXmin !== 0.0011) || (histo.fXaxis.fXmax !== 1.1);
102998+
102999+
if (check_axis !== 'x') {
103000+
is_normal ||= (histo.fYaxis.fXmin !== 0.0011) || (histo.fYaxis.fXmax !== 1.1) ||
103001+
(histo.fMinimum !== 0.0011) || (histo.fMaximum !== 1.1);
103002+
}
103003+
103004+
return !is_normal;
103005+
}
103006+
102979103007
/** @summary Set histogram object to graph */
102980103008
setHistogram(histo) {
102981103009
const obj = this.getObject();
@@ -103293,7 +103321,7 @@ let TGraphPainter$1 = class TGraphPainter extends ObjectPainter {
103293103321
histo.fBits |= kNoStats;
103294103322
this.#own_histogram = true;
103295103323
this.setHistogram(histo);
103296-
} else if ((histo.fMaximum !== kNoZoom) && (histo.fMinimum !== kNoZoom)) {
103324+
} else if ((histo.fMaximum !== kNoZoom) && (histo.fMinimum !== kNoZoom) && !this.isDummyHistogram('y')) {
103297103325
minimum = histo.fMinimum;
103298103326
maximum = histo.fMaximum;
103299103327
}
@@ -104156,7 +104184,7 @@ let TGraphPainter$1 = class TGraphPainter extends ObjectPainter {
104156104184
* @desc if arg specified changes or toggles editable flag */
104157104185
testEditable(arg) {
104158104186
const obj = this.getGraph();
104159-
if (!obj)
104187+
if (!isFunc(obj?.TestBit))
104160104188
return false;
104161104189
if ((arg === 'toggle') || (arg !== undefined))
104162104190
obj.SetBit(kNotEditable, !arg);
@@ -104572,8 +104600,9 @@ let TGraphPainter$1 = class TGraphPainter extends ObjectPainter {
104572104600
/** @summary Draw axis histogram
104573104601
* @private */
104574104602
async drawAxisHisto() {
104575-
const need_histo = !this.getHistogram(),
104576-
histo = this.createHistogram(need_histo, need_histo);
104603+
const set_x = this.isDummyHistogram('x'),
104604+
set_y = this.isDummyHistogram('y'),
104605+
histo = this.createHistogram(set_x, set_y);
104577104606
return TH1Painter$2.draw(this.getDrawDom(), histo, this.getOptions().Axis);
104578104607
}
104579104608

@@ -104687,7 +104716,10 @@ class RTreeMapTooltip {
104687104716
}
104688104717

104689104718
cleanup() {
104690-
if (this.tooltip !== null) document.body.removeChild(this.tooltip);
104719+
if (this.tooltip !== null) {
104720+
document.body.removeChild(this.tooltip);
104721+
this.tooltip = null;
104722+
}
104691104723
}
104692104724

104693104725
createTooltip()
@@ -104726,7 +104758,7 @@ class RTreeMapTooltip {
104726104758

104727104759
hideTooltip()
104728104760
{
104729-
if (this.tooltip)
104761+
if (this.tooltip)
104730104762
this.tooltip.style.opacity = '0';
104731104763
}
104732104764

@@ -104738,13 +104770,13 @@ class RTreeMapTooltip {
104738104770
content += `<i>${(isLeaf ? 'Column' : 'Field')}</i><br>`;
104739104771
content += `Size: ${this.painter.getDataStr(node.fSize)}<br>`;
104740104772

104741-
if (isLeaf && node.fType !== undefined)
104773+
if (isLeaf && node.fType !== undefined)
104742104774
content += `Type: ${node.fType}<br>`;
104743-
104744104775

104745-
if (!isLeaf)
104776+
104777+
if (!isLeaf)
104746104778
content += `Children: ${node.fNChildren}<br>`;
104747-
104779+
104748104780

104749104781
const obj = this.painter.getObject();
104750104782
if (obj.fNodes && obj.fNodes.length > 0) {
@@ -122531,6 +122563,27 @@ function openFile(arg, opts) {
122531122563
return file._open();
122532122564
}
122533122565

122566+
/** @summary Unzip JSON string
122567+
* @desc Should be used for buffer produced with TBufferJSON::zipJSON() method
122568+
* @param tgtsize - original length of json string
122569+
* @param src - string with data returned by TBufferJSON::zipJSON
122570+
* @return {Promise} with unzipped string */
122571+
122572+
async function unzipJSON(tgtsize, src) {
122573+
const bindata = atob_func(src),
122574+
buf = new ArrayBuffer(bindata.length),
122575+
bufView = new DataView(buf);
122576+
for (let i = 0; i < bindata.length; i++)
122577+
bufView.setUint8(i, bindata.charCodeAt(i));
122578+
122579+
return R__unzip(bufView, tgtsize).then(resView => {
122580+
let resstr = '';
122581+
for (let i = 0; i < tgtsize; i++)
122582+
resstr += String.fromCharCode(resView.getUint8(i));
122583+
return resstr;
122584+
});
122585+
}
122586+
122534122587
// special way to assign methods when streaming objects
122535122588
addClassMethods(clTNamed, CustomStreamers[clTNamed]);
122536122589
addClassMethods(clTObjString, CustomStreamers[clTObjString]);
@@ -173683,8 +173736,9 @@ class TScatterPainter extends TGraphPainter$1 {
173683173736
/** @summary Draw axis histogram
173684173737
* @private */
173685173738
async drawAxisHisto() {
173686-
const need_histo = !this.getHistogram(),
173687-
histo = this.createHistogram(need_histo, need_histo);
173739+
const set_x = this.isDummyHistogram('x'),
173740+
set_y = this.isDummyHistogram('y'),
173741+
histo = this.createHistogram(set_x, set_y);
173688173742
return TH2Painter$2.draw(this.getDrawDom(), histo, this.getOptions().Axis + ';IGNORE_PALETTE');
173689173743
}
173690173744

@@ -184295,6 +184349,7 @@ exports.svgToImage = svgToImage;
184295184349
exports.toJSON = toJSON;
184296184350
exports.treeDraw = treeDraw;
184297184351
exports.treeProcess = treeProcess;
184352+
exports.unzipJSON = unzipJSON;
184298184353
exports.urlClassPrefix = urlClassPrefix;
184299184354
exports.version = version;
184300184355
exports.version_date = version_date;

js/changes.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
# JSROOT changelog
22

3-
43
## Changes in dev
54
1. RNtuple support, thanks to Kriti Mahajan (https://github.com/Krmjn09)
65
1. Implement RTreeMapPainter to display RNTuple structure, thanks to Patryk Pilichowski (https://github.com/magnustymoteus)
@@ -11,6 +10,7 @@
1110
1. Support dark mode when store images
1211
1. With 'Shift' key pressed whole graph is moved by dragging action
1312
1. Support `Xall` and `Yall` as projections width #340
13+
1. Implement `unzipJSON()` function for data embeding in jupyter
1414
1. Support reading TBranch from old ROOT files with custom streamers
1515
1. Upgrade three.js r174 -> r180
1616
1. Upgrade lil-gui.mjs 0.19.2 -> 0.20.0
@@ -25,6 +25,7 @@
2525
1. Fix - TTree::Draw with strings
2626
1. Fix - first color in palette drawing #365
2727
1. Fix - toggle vertical/horizontal palette via context menu
28+
1. Fix - latex parsing error of `#delta_{0}_suffix` string
2829

2930

3031
## Changes in 7.9.1

js/modules/base/BasePainter.mjs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -646,6 +646,14 @@ class BasePainter {
646646

647647
rect.changed = false;
648648

649+
if (!rect.width && !rect.height && !main.empty() && main.attr('style')) {
650+
const ws = main.style('width'), hs = main.style('height');
651+
if (isStr(ws) && isStr(hs) && ws.match(/^\d+px$/) && hs.match(/^\d+px$/)) {
652+
rect.width = parseInt(ws.slice(0, ws.length-2));
653+
rect.height = parseInt(hs.slice(0, hs.length-2));
654+
}
655+
}
656+
649657
if (old_h && old_w && (old_h > 0) && (old_w > 0)) {
650658
if ((old_h !== rect.height) || (old_w !== rect.width))
651659
rect.changed = (check_level > 1) || (rect.width / old_w < 0.99) || (rect.width / old_w > 1.01) || (rect.height / old_h < 0.99) || (rect.height / old_h > 1.01);

js/modules/base/latex.mjs

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -705,23 +705,24 @@ function parseLatex(node, arg, label, curr) {
705705

706706
const extractLowUp = name => {
707707
const res = {};
708+
708709
if (name) {
709710
label = '{' + label;
710711
res[name] = extractSubLabel(name === 'low' ? '_' : '^');
711712
if (res[name] === -1) return false;
712713
}
713714

714715
while (label) {
715-
if (label[0] === '_') {
716+
if ((label[0] === '_') && !res.low) {
716717
label = label.slice(1);
717-
res.low = !res.low ? extractSubLabel('_') : -1;
718+
res.low = extractSubLabel('_');
718719
if (res.low === -1) {
719-
console.log(`error with ${found.name} low limit`);
720+
console.log(`error with ${found.name} low limit ${label}`);
720721
return false;
721722
}
722-
} else if (label[0] === '^') {
723+
} else if ((label[0] === '^') && !res.up) {
723724
label = label.slice(1);
724-
res.up = !res.up ? extractSubLabel('^') : -1;
725+
res.up = extractSubLabel('^');
725726
if (res.up === -1) {
726727
console.log(`error with ${found.name} upper limit ${label}`);
727728
return false;

js/modules/core.mjs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ const version_id = 'dev',
44

55
/** @summary version date
66
* @desc Release date in format day/month/year like '14/04/2022' */
7-
version_date = '18/09/2025',
7+
version_date = '24/09/2025',
88

99
/** @summary version id and date
1010
* @desc Produced by concatenation of {@link version_id} and {@link version_date}
@@ -1559,7 +1559,8 @@ function createHistogram(typename, nbinsx, nbinsy, nbinsz) {
15591559
* @desc Title may include axes titles, provided with ';' symbol like "Title;x;y;z" */
15601560

15611561
function setHistogramTitle(histo, title) {
1562-
if (!histo) return;
1562+
if (!histo || !isStr(title))
1563+
return;
15631564
if (title.indexOf(';') < 0)
15641565
histo.fTitle = title;
15651566
else {

js/modules/draw/RTreeMapTooltip.mjs

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,10 @@ class RTreeMapTooltip {
1212
}
1313

1414
cleanup() {
15-
if (this.tooltip !== null) document.body.removeChild(this.tooltip);
15+
if (this.tooltip !== null) {
16+
document.body.removeChild(this.tooltip);
17+
this.tooltip = null;
18+
}
1619
}
1720

1821
createTooltip()
@@ -51,7 +54,7 @@ class RTreeMapTooltip {
5154

5255
hideTooltip()
5356
{
54-
if (this.tooltip)
57+
if (this.tooltip)
5558
this.tooltip.style.opacity = '0';
5659
}
5760

@@ -63,13 +66,13 @@ class RTreeMapTooltip {
6366
content += `<i>${(isLeaf ? 'Column' : 'Field')}</i><br>`;
6467
content += `Size: ${this.painter.getDataStr(node.fSize)}<br>`;
6568

66-
if (isLeaf && node.fType !== undefined)
69+
if (isLeaf && node.fType !== undefined)
6770
content += `Type: ${node.fType}<br>`;
68-
6971

70-
if (!isLeaf)
72+
73+
if (!isLeaf)
7174
content += `Children: ${node.fNChildren}<br>`;
72-
75+
7376

7477
const obj = this.painter.getObject();
7578
if (obj.fNodes && obj.fNodes.length > 0) {

js/modules/gpad/TPadPainter.mjs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -823,7 +823,7 @@ class TPadPainter extends ObjectPainter {
823823
this.createAttFill({ attr: this.#pad });
824824

825825
if ((rect.width <= lmt) || (rect.height <= lmt)) {
826-
if (this.hasSnapId()) {
826+
if (!this.hasSnapId()) {
827827
svg.style('display', 'none');
828828
console.warn(`Hide canvas while geometry too small w=${rect.width} h=${rect.height}`);
829829
}

0 commit comments

Comments
 (0)