Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
75 changes: 35 additions & 40 deletions src/firefly/js/Firefly.js
Original file line number Diff line number Diff line change
Expand Up @@ -434,58 +434,53 @@ export const firefly = {
* @param {AppProps} props - application properties
* @param {FireflyOptions} clientAppSpecificOptions - firefly options specific to this client
* @param {Array.<WebApiCommand>} webApiCommands
* @returns {Promise.<boolean>}
* @returns {Promise.<void>}
*/
function bootstrap(props, clientAppSpecificOptions, webApiCommands) {
async function bootstrap(props, clientAppSpecificOptions, webApiCommands) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is a good change. Much cleaner.


if (window?.firefly?.initialized) return Promise.resolve(); // if initialized, don't run it again.
if (window?.firefly?.initialized) return; // if initialized, don't run it again.

set(window, 'firefly.initialized', true);
return new Promise(async (resolve) => {

const processDecor= (process) => (rawAction) => {
getOrCreateWsConn().catch(() => {
if (!getConnectionStatus()?.lost) { // set lost status only when it's not already set, to avoid circular dispatches
dispatchConnectionStatus({lost: true, reason: 'You are no longer connected to the server'});
}
});
process(rawAction);
recordHistory(rawAction);
};

bootstrapRedux( getBootstrapRegistry(), processDecor);
flux.process( {type : APP_LOAD} ); // setup initial store/state
const processDecor= (process) => (rawAction) => {
getOrCreateWsConn().catch(() => {
if (!getConnectionStatus()?.lost) { // set lost status only when it's not already set, to avoid circular dispatches
dispatchConnectionStatus({lost: true, reason: 'You are no longer connected to the server'});
}
});
process(rawAction);
recordHistory(rawAction);
};

let srvAppSpecificOptions={};
try {
srvAppSpecificOptions= await getJsonProperty('FIREFLY_OPTIONS');
}
catch (err) {
logger.error('could not retrieve valid server options');
}
// const appSpecificOptions = mergeObjectOnly(clientAppSpecificOptions, srvAppSpecificOptions);
const appSpecificOptions = mergeAppOptions(clientAppSpecificOptions, srvAppSpecificOptions);
bootstrapRedux( getBootstrapRegistry(), processDecor);
flux.process( {type : APP_LOAD} ); // setup initial store/state

const client= await getOrCreateWsConn(); // establish websocket connection first before doing anything else.
let srvAppSpecificOptions={};
try {
srvAppSpecificOptions= await getJsonProperty('FIREFLY_OPTIONS');
}
catch (err) {
logger.error('could not retrieve valid server options');
}
// const appSpecificOptions = mergeObjectOnly(clientAppSpecificOptions, srvAppSpecificOptions);
const appSpecificOptions = mergeAppOptions(clientAppSpecificOptions, srvAppSpecificOptions);

fireflyInit(props, appSpecificOptions, webApiCommands);
const client= await getOrCreateWsConn(); // establish websocket connection first before doing anything else.

client.addListener(ActionEventHandler);
window.firefly.wsClient = client;
notifyServerAppInit({spaName:`${props.appTitle||''}--${props.template?props.template:'api'}`});
loadAllJobs();
resolve?.();
fireflyInit(props, appSpecificOptions, webApiCommands);

client.addListener(ActionEventHandler);
window.firefly.wsClient = client;
notifyServerAppInit({spaName:`${props.appTitle||''}--${props.template?props.template:'api'}`});

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

since we are cleaning up, this probably should be

void notifyServerAppInit({spaName:`${props.appTitle||''}--${props.template?props.template:'api'}`});

to indicate we don't care about the promise.

loadAllJobs();

}).then(() => {
// when all is done, mark app as 'ready'
defer(() => {
setTimeout(() => {
dispatchUpdateAppData({isReady: true});
},3);
});
initWorkerContext();
// when all is done, mark app as 'ready'
defer(() => {
setTimeout(() => {
dispatchUpdateAppData({isReady: true});
},3);
Comment on lines +478 to +481

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Now that I look tat this code, I wonder why we need a double defer. Probably just a setTimeout(f,5) would be fine but I think that is outside of the scope of this PR.

});
initWorkerContext();
}

/**
Expand Down
2 changes: 1 addition & 1 deletion src/firefly/js/charts/ChartUtil.js
Original file line number Diff line number Diff line change
Expand Up @@ -1195,7 +1195,7 @@ export function formatColExpr({colOrExpr, quoted, colNames}) {
// quote columns, assuming column names are alpha-numeric
expr.getParsedVariables().forEach((v) => {
if (!v.startsWith('"')) {
const re = new RegExp('([^A-Za-z\d_"]|^)(' + v + ')([^A-Za-z\d_"]|$)', 'g');
const re = new RegExp('([^A-Za-z0-9_"]|^)(' + v + ')([^A-Za-z0-9_"]|$)', 'g');

@jaladh-singhal jaladh-singhal Sep 29, 2026 •

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

could have changed to \\d but this is fine as well

while (colOrExpr.match(re)) { // while is needed to handle cases like v*v
colOrExpr = colOrExpr.replace(re, '$1"$2"$3'); // add quotes
}
Expand Down
14 changes: 14 additions & 0 deletions src/firefly/js/charts/__tests__/ChartUtil-test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
/*eslint-env node, jest */
import {formatColExpr} from 'firefly/charts/ChartUtil.js';


describe('ChartUtil', () => {
test('formatColExpr does not treat a digit as the edge of a column name', () => {
const colNames = ['val', 'val2'];
expect(formatColExpr({colOrExpr: 'val + val2', quoted: true, colNames})).toBe('"val"+"val2"');
expect(formatColExpr({colOrExpr: 'val2 + val', quoted: true, colNames})).toBe('"val2"+"val"');

// the digits around e belong to the number 1e5
expect(formatColExpr({colOrExpr: '1e5*e', quoted: true, colNames: ['e']})).toBe('1e5*"e"');
});
});
4 changes: 2 additions & 2 deletions src/firefly/js/charts/ui/CombineChart.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -114,12 +114,12 @@ CombineChart.propTypes = {
* @constructor
*/
export const CombinePinnedCharts = ({viewerId, slotProps}) => {
if (viewerId !== PINNED_CHART_VIEWER_ID) return null;

const {chartIds, selectedChartId} = useStoreConnector(() => ({
chartIds: getViewerItemIds(getMultiViewRoot(), viewerId),
selectedChartId: getActiveViewerItemId(viewerId, true)
}), [viewerId]);

if (viewerId !== PINNED_CHART_VIEWER_ID) return null;
return <CombineChart {...{chartIds, selectedChartId, slotProps}} />;
};

Expand Down
5 changes: 3 additions & 2 deletions src/firefly/js/tables/HpxIndexCntlr.js
Original file line number Diff line number Diff line change
Expand Up @@ -798,7 +798,7 @@ export function getPixelCount(orderData,norder) {

export function getValuesForOrder(orderData,norder) {
if (!orderData) return [];
if (norder<11) return [...orderData[norder]?.tiles[0]?.values()];
if (norder<11) return [...(orderData[norder]?.tiles[0]?.values() ?? [])];
const joinAry= [];
for(let i=0; (i<orderData[norder]?.tiles.length);i++) {
joinAry.push(Array.from(orderData[norder]?.tiles[i].values()));
Expand All @@ -807,7 +807,8 @@ export function getValuesForOrder(orderData,norder) {
}

export function getKeysForOrder(orderData,norder) {
if (norder<11) return [...orderData[norder]?.tiles[0]?.keys()];
if (!orderData) return [];
if (norder<11) return [...(orderData[norder]?.tiles[0]?.keys() ?? [])];
const joinAry= [];
for(let i=0; (i<orderData[norder]?.tiles.length);i++) {
joinAry.push(Array.from(orderData[norder]?.tiles[i].keys()));
Expand Down
20 changes: 20 additions & 0 deletions src/firefly/js/tables/__tests__/HpxIndexCntlr-test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import {getKeysForOrder, getValuesForOrder} from '../HpxIndexCntlr.js';


describe('HpxIndexCntlr', () => {
const orderData = {5: {norder: 5, tiles: [new Map([[1, 'a'], [2, 'b']])]}};

test('getValuesForOrder and getKeysForOrder return the tile entries of an order', () => {
expect(getValuesForOrder(orderData, 5)).toEqual(['a', 'b']);
expect(getKeysForOrder(orderData, 5)).toEqual([1, 2]);
});

test('getValuesForOrder and getKeysForOrder return an empty array for a missing order', () => {
expect(getValuesForOrder(orderData, 1)).toEqual([]);
expect(getKeysForOrder(orderData, 1)).toEqual([]);
});

test('getKeysForOrder returns an empty array without orderData', () => {
expect(getKeysForOrder(undefined, 4)).toEqual([]);
});
});
6 changes: 3 additions & 3 deletions src/firefly/js/util/ObsCoreSRegionParser.js
Original file line number Diff line number Diff line change
Expand Up @@ -200,9 +200,9 @@ export function parseObsCoreRegion(sRegionVal, unit='deg', isCorners=false) {
break;

case regionShape.box.key:
if (sAry.length = height + 1) {
const w = getDim(sAry[width], coordSys, unit, 'arcsec');
const h = getDim(sAry[height], coordSys, unit, 'arcsec');
if (sAry.length === height + 1) {
const w = getDim(sAry[width], coordSys, unit, ShapeDataObj.UnitType.ARCSEC);
const h = getDim(sAry[height], coordSys, unit, ShapeDataObj.UnitType.ARCSEC);
pairCoord = getPairCoord(sAry, [coord1, coord2]);

if (pairCoord.length === 2 && w !== null && h !== null) {
Expand Down
14 changes: 14 additions & 0 deletions src/firefly/js/util/__tests__/Expression-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,20 @@ describe('A test suite for expr/Expression.js', function () {
// expect(expressionGood.getValue()).toBe(2^x);
});

it('nvl2', function () {
const expr = new Expression('nvl2(x, 1, 2)', ['x']);
expect(expr.isValid()).toBeTruthy();
expr.setVariableValue('x', 5);
expect(expr.getValue()).toBe(1);
expr.setVariableValue('x', NaN);
expect(expr.getValue()).toBe(2);

// all-constant arguments are evaluated while parsing
const constExpr = new Expression('nvl2(1, 2, 3)', []);
expect(constExpr.isValid()).toBeTruthy();
expect(constExpr.getValue()).toBe(2);
});

it('invalid expression', function () {
const expressionBad = new Expression('2*sin(x)+y/z', ['x','y']);
expect(expressionBad.isValid()).toBeFalsy();
Expand Down
2 changes: 1 addition & 1 deletion src/firefly/js/util/expr/Expr.js
Original file line number Diff line number Diff line change
Expand Up @@ -230,7 +230,7 @@ class TernaryExpr {
const arg1 = this.rand1.value();
const arg2 = this.rand2.value();
switch (this.rator) {
case NVL2: Number.isFinite(arg0) ? arg1 : arg2;
case NVL2: return Number.isFinite(arg0) ? arg1 : arg2;
default: throw 'BUG: bad rator: '+this.rator;
}
}
Expand Down
2 changes: 1 addition & 1 deletion src/firefly/js/visualize/WebPlot.js
Original file line number Diff line number Diff line change
Expand Up @@ -461,7 +461,7 @@ export const WebPlot= {
const relatedData = cubeCtx ? cubeCtx.relatedData : wpInit.relatedData;
const plotState= PlotState.makePlotStateWithJson(wpInit.plotState,request0, rv0);
if (!request0) request0= plotState.getWebPlotRequest();
const colorTableId= request0?.getInitialColorTable()+'' ?? '0';
const colorTableId= String(request0?.getInitialColorTable() ?? '0');

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is fine but I think request0?.getInitialColorTable() will always return a number and never an undefined or NaN so the the original code is valid.

Also, I don't think ESlint was returning an issue here.


const {processHeader, wlData, wlDataAry, headerAry, header, zeroHeader,
allWCSMap, allWlMap}= getAllHeaderAndWlInfo(cubeCtx,wpInit,plotState);
Expand Down
2 changes: 1 addition & 1 deletion src/firefly/js/visualize/draw/SelectBox.js
Original file line number Diff line number Diff line change
Expand Up @@ -198,7 +198,7 @@ function drawBox(ctx, pt0, pt2, drawParams,renderOptions) {
DrawUtil.drawInnerRecWithHandles(ctx, innerBoxColor, 2, pt0.x, pt0.y, pt2.x, pt2.y, lineWidth);
} else if (selectedShape === SelectedShape.circle.key) {
DrawUtil.drawCircleWithHandles(ctx, innerBoxColor, 2, pt0.x, pt0.y, pt2.x, pt2.y, lineWidth);
} else if (selectedShape === SelectedShape.circle.key) {
} else if (selectedShape === SelectedShape.ellipse.key) {
DrawUtil.drawEclipseWithHandles(ctx, innerBoxColor, 2, pt0.x, pt0.y, pt2.x, pt2.y, lineWidth);

}
Expand Down
Loading