/* * License information at https://github.com/Caltech-IPAC/firefly/blob/master/License.txt */ //=================================================================================== //----------------------------------------------------------------------------------- // Build the firefly high level api // This file should have has no imports. It should be build the high level api completely from the lowlevel. // There are two reasons for this. // 1. It will be an example of how to use the lowlevel api // 2. We want to make sure the the lowlevel api is only built with the high level. That // way we know that the lowlevel is complete. //----------------------------------------------------------------------------------- //=================================================================================== /** * @public * @desc build highLevelApi using the lowLevelApi as an input */ /** * @param llApi the lowlevel api * @returns {Object} * @ignore * */ export function buildHighLevelApi(llApi) { const current= build(llApi); const deprecated= buildDeprecated(llApi); return Object.assign({}, deprecated, current); } const STANDARD= 'standard'; const ENCAPSULATE= 'encapsulate'; var globalImageViewDefParams= {}; var globalPrefs= {imageDisplayType:STANDARD}; /** * Build the deprecated API * @param llApi * @returns {Object} * @ignore */ function build(llApi) { const commonPart= buildCommon(llApi); const imagePart= buildImagePart(llApi); const chartPart= buildChartPart(llApi); const tablePart= buildTablePart(llApi); return Object.assign({}, commonPart, imagePart,chartPart,tablePart); } /*----------------------------< TABLE PART ----------------------------*/ var divToGrp = (() => { // workaround to support mapping first targetDiv to 'main' var main; return (div) => { if (!main) main = div; return div === main ? 'main' : div; }; })(); function oldApi(llApi, params, options) { const {getBoolean} = llApi.util; const {makeFileRequest} = llApi.util.table; const oldOpts = params.tableOptions && params.tableOptions.split(',').reduce((rval, s) => { const kval = s && s.trim().split('='); rval[kval[0]] = kval[1]; return rval; }, {}); // convert 'key=value' array into {key: value}. options.showFilters = getBoolean(oldOpts, 'show-filter'); options.showTitle = getBoolean(oldOpts, 'show-title'); options.showToolbar = getBoolean(oldOpts, 'show-toolbar'); options.showOptionButton = getBoolean(oldOpts, 'show-options'); options.showPaging = getBoolean(oldOpts, 'show-paging'); options.showSave = getBoolean(oldOpts, 'show-save'); options.showUnits = getBoolean(oldOpts, 'show-units'); // options.?? = getBoolean(oldOpts, 'show-popout'); // options.?? = getBoolean(oldOpts, 'show-table-view'); var {Title, source, alt_source, type, filters, sortInfo, pageSize, startIdx, fixedLength, expandable, rowHeight} = params; var request = makeFileRequest(Title, source, alt_source, {filters, sortInfo, pageSize, startIdx}); options.selectable = type === 'selectable'; options.help_id = 'tables'; options.expandable = !!expandable; options.rowHeight = rowHeight; return request; } function doShowTable(llApi, targetDiv, request, options={}) { const {dispatchTableSearch}= llApi.action; const {renderDOM}= llApi.util; const {TablesContainer}= llApi.ui; if ((typeof targetDiv).match(/string|HTMLDivElement/) === null) { // old api.. need to setup request and options before continue. const params = targetDiv; targetDiv = request; request = oldApi(llApi, params, options); } options = Object.assign({tbl_group: divToGrp(targetDiv)}, options); const contProps = {tbl_group: options.tbl_group}; Object.keys(options).forEach( (k) => { if (options[k] === undefined) { Reflect.deleteProperty(options, k); } }); dispatchTableSearch(request, options); renderDOM(targetDiv, TablesContainer, contProps); } function buildTablePart(llApi) { /** * @global * @public * @typedef {object} TblOptions * @prop {string} tbl_group the group this table belongs to. Defaults to 'main'. * @prop {number} pageSize the starting page size. Will use the request's pageSize if not given. * @prop {boolean} removable true if this table can be removed from view. Defaults to true. * @prop {boolean} backgroundable true if this search can be sent to background. Defaults to false. * @prop {boolean} showUnits defaults to false * @prop {boolean} showTypes defaults to false * @prop {boolean} showFilters defaults to false * @prop {boolean} selectable defaults to true * @prop {boolean} expandable defaults to true * @prop {boolean} showToolbar defaults to true * @prop {boolean} showTitle defaults to true * @prop {boolean} showPaging defaults to true * @prop {boolean} showSave defaults to true * @prop {boolean} showOptionButton defaults to true * @prop {boolean} showFilterButton defaults to true * @prop {boolean} showInfoButton defaults to false * @prop {boolean} border defaults to true * @prop {boolean} help_id link to help if applicable * @prop {function[]} leftButtons an array of functions that returns a button-like component laid out on the left side of this table header. Function will be called with table's state. * @prop {function[]} rightButtons an array of functions that returns a button-like component laid out on the left side of this table header. Function will be called with table's state. */ /** * @param {string|HTMLDivElement} targetDiv to put the table in. * @param {TableRequest} request request object created from * @param {TblOptions} options table options. * @memberof firefly * @public * @example var tblReq = firefly.util.table.makeIrsaCatalogRequest( * 'allwise-500', 'WISE', 'allwise_p3as_psd', * {position: '10.68479;41.26906;EQ_J2000', * SearchMethod: 'Cone', * radius: 300}, * {tbl_id: 'test-tbl', * META_INFO: {defaultChartDef: JSON.stringify({data: [{x: 'tables::w1mpro', y: 'tables::w2mpro', mode: 'markers'}]})} * }); * firefly.showTable('table-1', tblReq, {tbl_group: 'allwise'}); */ // @param {module:firefly.TblOptions} options table options. const showTable= (targetDiv, request, options) => doShowTable(llApi, targetDiv, request, options); return {showTable}; } /*---------------------------- TABLE PART >----------------------------*/ function buildChartPart(llApi) { /** * @summary The general function to plot a Plotly chart. * @param {string|HTMLDivElement} targetDiv - div to put the chart in. * @param {object} parameters * @param {array.object} parameters.data - plotly data array (possibly with firefly extensions) * @param {object} parameters.layout - plotly layout object (possibly with firefly extensions) * @param {boolean} parameters.noChartToolbar - set true for non-interactive chart with no toolbar * @memberof firefly * @public */ const showChart = (targetDiv, parameters) => doShowChart(llApi, targetDiv, parameters); /** * @summary The general plotting function to plot an XY Plot. * @param {string|HTMLDivElement} targetDiv - div to put the chart in. * @param {XYPlotOptions} parameters - object literal with the chart parameters * @memberof firefly * @public * @deprecated * @example firefly.showXYPlot('myDiv', {source: 'mySourceFile', {xCol: 'ra', yCol: 'dec'}) */ const showXYPlot= (targetDiv, parameters) => doShowXYPlot(llApi, targetDiv, parameters); /** * @summary Add XYPlot view of a table. Deprecated: use showXYPlot with tbl_id in @link{XYPlotOptions} instead. * @param {string|HTMLDivElement} targetDiv - div to put the chart in. * @param {XYPlotOptions} parameters - object literal with the chart parameters * @memberof firefly * @deprecated * @example firefly.addXYPlot('myDiv', {tbl_id: <tbl_id>, {xCol: 'ra', yCol: 'dec'}) */ const addXYPlot= (targetDiv, parameters) => doShowXYPlot(llApi, targetDiv, parameters); /** * @summary The general plotting function to plot Histogram. * @param {string|HTMLDivElement} targetDiv - div to put the chart in. * @param {HistogramOptions} parameters - object literal with the chart parameters * @memberof firefly * @public * @deprecated * @example firefly.showHistogram */ const showHistogram= (targetDiv, parameters) => doShowHistogram(llApi, targetDiv, parameters); return {showChart, showXYPlot, addXYPlot, showHistogram}; } function buildCommon(llApi) { /** * @summary Sets the root path for any relative URL. If this method has not been called then relative URLs use the page's root. * @param {string} rootUrlPath * @memberof firefly * @public */ const setRootPath= (rootUrlPath) => llApi.action.dispatchRootUrlPath(rootUrlPath); const setGlobalPref= (pref)=> Object.assign(globalPrefs, pref); return {setRootPath,setGlobalPref}; } function buildImagePart(llApi) { const {RequestType}= llApi.util.image; /** * @summary object for web plot request * @description Below is a list of predefined parameters available for web plot request. * Some parameters control how to get an image, a image can be retrieved from a service, a url, of a file on the server. * Others control the zoom, stretch, and color, title, and default overlays. There are also parameters to pre-process an * image, such as crop, rotate or flip. * @typedef {object} WebPlotParams * @prop {string} Type the request type, see available types at {@link RequestType}. * @prop {string} File file name of a file on the server if Type=='File' * @prop {string} URL url reference to a fits file if Type=='URL' * @prop {string} Service the service type if Type=='SERVICE', see available services at {@link ServiceType} * @prop {string} plotId plot ID * @prop {string} plotGroupId plot group ID * @prop {String} ObjectName object name that can be looked up by NED or Simbad * @prop {string} Resolver the object name resolver to use, options are: NED, Simbad, NedThenSimbad, SimbadThenNed, PTF. * @prop {string} SizeInDeg the radius or side (in degrees) depending of the service type, used with Type=='SERVICE' or 'HiPS' * @prop {string} SurveyKey the survey, used with Type='SERVICE' * @prop {string} SurveyKeyBand the survey band, used with Type=='SERVICE' and Service='WISE', 'TWOMASS' or 'ATLAS' * @prop {string} WorldPt target for service request or HiPS request in serialized version * @prop {string} PlotId plot Id * @prop {string} PlotGroupId plot group id * @prop {string} Title plot title * @prop {string} TitleOptions title options, see available options at {@link TitleOptions} * @prop {string} PreTitle a String to append at the beginning of the title of the plot * @prop {string} PostTitle a String to append at the end of the title of the plot * @prop {string} hipsRootUrl HiPS root url or IVOID, e.g.: ivo://CDS/P/2MASS/J, used with Type='HiPS' * @prop {string} ZoomType zoom type, see {@link ZoomType} * @prop {string} ZoomToWidth the width of the viewable area to determine the zoom level, used with ZoomType.TO_WIDTH_HEIGHT, ZoomType.TO_WIDTH * @prop {string} ZoomToHeight the height of the viewable area to determine the zoom level, used with ZoomType.TO_WIDTH_HEIGHT, ZoomType.TO_HEIGHT * @prop {string} InitZoomLevel initialize zoom level, used with ZoomType.LEVEL * @prop {string} ZoomArcsecPerScreenPix the arcseconds per screen pixel that will be used to determine the zoom level, Used with ZoomType.ARCSEC_PER_SCREEN_PIX * @prop {boolean} RotateNorth plot should come up rotated north * @prop {CoordinateSys} RotateNorthType set to coordinate system for rotate north, eq EQ_J2000 is the default * @prop {boolean} Rotate set to rotate, if true, the angle should also be set * @prop {string} RotationAngle set the angle to rotate to * @prop {string} RotateFromNorth rotate angle from north * @prop {boolean} FlipY set if this image should be flipped on the Y axis * @prop {boolean} FlipX set if this image should be flipped on the X axis * @prop {boolean} PostCrop crop the image before returning it. If rotation is set then the crop will happen post rotation * @prop {boolean} PostCropAndCenter crop and center the image before returning it. Note: SizeInDeg and WorldPt are required * @prop {CoordinateSys} PostCropAndCenterType set to coordinate system for crop and center, eq EQ_J2000 is the default * @prop {string} CropPt1 one corner of the rectangle, in image coordinates, to crop out of the image. * @prop {string} CropPt2 second corner of the rectangle, in image coordinates, to crop out of the image. * @prop {string} CropWorldPt1 one corner of the rectangle, in world coordinates, to crop out of the image. * @prop {string} CropWorldPt2 second corner of the rectangle, in world coordinates, to crop out of the image. * @prop {string} OverlayPosition string of overlay position in world coordinates * @prop {string} ColorTable color table id, value 0 - 21 to represent different predefined color tables * @prop {string} RangeValues a complex string for specify the stretch of this plot. * Use the method firefly.serializeRangeValues() to produce this string * @prop {string} MultiImageIdx number index of image * @prop {string} MultiImageExts image extension list. ex: '3,4,5' for extension 3, 4, 5 * @prop {string} GridOn turn the coordinate grid on after the image is plotted, 'true' or 'false' * @prop {number} thumbnailSize thumbnail size * @prop {boolean} ContinueOnFail for 3 color, if this request fails then keep trying to make a plot with the other request * * @global * @public */ /** * @summary The general plotting function to plot a FITS image. * @param {String|HTMLDivElement} targetDiv to put the image in. * @param {WebPlotParams|WebPlotRequest} request a request object with the plotting parameters * @param {HipsImageConversionSettings} [hipsImageConversion= undefined] if defined, use these parameter to * convert between image and HiPS * @param {boolean} userCanDelete User can delete the image * * @memberof firefly * @public * @example firefly.showImage('myPlot', * {Type: 'SERVICE', * plotId: 'myImage', * plotGroupId: 'myGroup', * Service: 'WISE' * Title: 'Wise' * GridOn: true, * SurveyKey: 'Atlas' * SurveyKeyBand: '2' * WorldPt: '10.68479;41.26906;EQ_J2000', * SizeInDeg: '.12'}); * * */ const showImage= (targetDiv, request, hipsImageConversion, userCanDelete) => showImageInMultiViewer(llApi, targetDiv, request, false, hipsImageConversion, userCanDelete); /** * @summary A convenience plotting function to plot a file on the server or a url. If first looks for the file then * the url is the fallback * @param {string|HTMLDivElement} targetDiv to put the image in. * @param {string} file file on server * @param {string} url url reference to a fits file * @memberof firefly * @public * @ignore * @example firefly.showImageFileOrUrl */ const showImageFileOrUrl= (targetDiv, file,url) => showImageInMultiViewer(llApi, targetDiv, {'File' : file, 'URL' : url, 'Type' : RequestType.TRY_FILE_THEN_URL }, false); /** * @summary set global fallback params for every image plotting call * @param {Object} params a object literal such as any image plot or showImage uses * @memberof firefly * @public * @ignore * @example firefly.setGlobalImageDef */ const setGlobalImageDef= (params) => globalImageViewDefParams= params; /** * * @param {string|HTMLDivElement} div - targetDiv to put the coverage in. * @param {CoverageOptions} options - an object literal containing a list of the coverage options * @memberof firefly * @public * @example firefly.showCoverage('myDiv', {gridOn: true}) */ const showCoverage= (div,options) => initCoverage(llApi,div,options); /** * @summary The plotting function to display a HiPS * @param {String|HTMLDivElement} targetDiv to put the image in. * @param {WebPlotParams|WebPlotRequest} request a request object with Type=='HiPS' used to display a HiPS * @param {HipsImageConversionSettings} [hipsImageConversion=undefined] if defined, use these parameter to * convert between image and HiPS * @param {boolean} userCanDelete User can delete the image * @memberof firefly * @public * @example firefly.showHiPS('hipsDIV1', * { * plotId : 'aHipsID1-1', * WorldPt : '148.892;69.0654;EQ_J2000', * title : 'A HiPS', * hipsRootUrl: 'CDS/P/SDSS9/color' * }, * { * imageRequestRoot: { * Service : 'WISE', * Title : 'Wise', * SurveyKey: '3a', * SurveyKeyBand: '2' * }, * fovDegFallOver: .5 * }; * ); * * */ const showHiPS= (targetDiv, request, hipsImageConversion, userCanDelete) => showImageInMultiViewer(llApi, targetDiv, request, true, hipsImageConversion, userCanDelete); /** * @summary The plotting function to display a HiPS or an image * @param {String|HTMLDivElement} targetDiv to put the image in. * @param {WebPlotParams|WebPlotRequest} hipsRequest a request object used to display a HiPS * @param {WebPlotParams|WebPlotRequest} imageRequest a request object used to display an image * @param {number} fovDegFallOver the field of view size to determine when to move between a HiPS and an image * @param {WebPlotParams|WebPlotRequest} allSkyRequest a request object used to display allsky image. * @param {boolean} plotAllSkyFirst if plot allsky first * * @memberof firefly * @public * @example firefly.showImageOrHiPS('hipsDiv6', * { * plotId: 'aHipsID6', * title : 'A HiPS - 0.2', * hipsRootUrl: 'http://alasky.u-strasbg.fr/AllWISE/RGB-W4-W2-W1', * SizeInDeg:.2 * }, * { * Service: 'WISE', * Title: 'Wise', * SurveyKey: '3a', * SurveyKeyBand: '2', * WorldPt: '148.892;69.0654;EQ_J2000' * }, 0.5 * ); * * */ const showImageOrHiPS = (targetDiv, hipsRequest, imageRequest, fovDegFallOver, allSkyRequest, plotAllSkyFirst) => showImageOrHiPSInMultiViewer(llApi, targetDiv, hipsRequest, imageRequest, fovDegFallOver, allSkyRequest, plotAllSkyFirst); return {showImage, showHiPS, showImageOrHiPS, showImageFileOrUrl, setGlobalImageDef, showCoverage}; } /** * Build the deprecated API * @param llApi * @returns {Object} * @deprecated * @ignore */ function buildDeprecated(llApi) { const dApi= {}; const {RequestType}= llApi.util.image; dApi.makeImageViewer= (plotId) => { llApi.util.debug('makeImageViewer is deprecated, use firefly.showImage() instead'); highlevelImageInit(llApi); const plotSimple= makePlotSimple(llApi,plotId); return { defP: {}, setDefaultParams(params) { this.defP= params; }, plot(request) { plotSimple(Object.assign({},this.defP, request)); }, plotFile(file) { plotSimple(Object.assign({},this.defP,{'File' : file})); }, plotURL(url) { plotSimple(Object.assign({},this.defP,{'URL' : url})); }, plotFileOrURL(file,url) { plotSimple(Object.assign({},this.defP, {'File' : file, 'URL' : url, 'Type' : RequestType.TRY_FILE_THEN_URL })); } }; }; dApi.setGlobalDefaultParams= (params) => globalImageViewDefParams= params; //!!!!!!! Add more Deprecated Api for table, histogram, xyplot here return dApi; } //================================================================ //---------- Private Image functions //================================================================ function makePlotSimple(llApi, plotId) { return (request) => { llApi.util.renderDOM(plotId, llApi.ui.ImageViewer, {plotId}); llApi.action.dispatchPlotImage({plotId, wpRequest:Object.assign({}, globalImageViewDefParams,request)}); }; } function validatePlotRequest(llApi, targetDiv, request) { const {findInvalidWPRKeys,confirmPlotRequest}= llApi.util.image; const {debug, getWsConnId}= llApi.util; const testR= Array.isArray(request) ? request : [request]; testR.forEach( (r) => { const badList= findInvalidWPRKeys(r); if (badList.length) debug(`plot request has the following bad keys: ${badList}`); }); return confirmPlotRequest(request,globalImageViewDefParams,targetDiv,makePlotId(getWsConnId)); } function getPlotIdFromRequest(request) { if (Array.isArray(request)) { const rWithPId = request.find((r) => r.plotId); return rWithPId ? rWithPId.plotId : null; } else { return request.plotId; } } function showImageOrHiPSInMultiViewer(llApi, targetDiv, hipsRequest, imageRequest, fovDegFallOver, allSkyRequest, plotAllSkyFirst) { const {dispatchPlotImageOrHiPS, dispatchAddViewer}= llApi.action; const {IMAGE, NewPlotMode}= llApi.util.image; const {MultiImageViewer, MultiViewStandardToolbar}= llApi.ui; const {renderDOM}= llApi.util; highlevelImageInit(llApi); hipsRequest = validatePlotRequest(llApi, targetDiv, hipsRequest); hipsRequest.Type = 'HiPS'; imageRequest = validatePlotRequest(llApi, targetDiv, imageRequest); dispatchAddViewer(targetDiv, NewPlotMode.create_replace.key, IMAGE); const plotId= getPlotIdFromRequest(hipsRequest) || getPlotIdFromRequest(imageRequest); dispatchPlotImageOrHiPS({plotId, hipsRequest, viewerId: targetDiv, imageRequest, allSkyRequest, plotAllSkyFirst, fovDegFallOver}); renderDOM(targetDiv, MultiImageViewer, {viewerId:targetDiv, canReceiveNewPlots:NewPlotMode.create_replace.key, Toolbar:MultiViewStandardToolbar }); } var firstShowImage= false; var imageRenderType; function showImageInMultiViewer(llApi, targetDiv, request, isHiPS, hipsImageConversion, userCanDelete=true) { const {dispatchPlotImage, dispatchPlotHiPS, dispatchAddViewer, dispatchUpdateCustom}= llApi.action; const {IMAGE, NewPlotMode}= llApi.util.image; const {renderDOM}= llApi.util; const {MultiImageViewer, ApiFullImageDisplay, MultiViewStandardToolbar}= llApi.ui; request = validatePlotRequest(llApi, targetDiv, request); const plotId= getPlotIdFromRequest(request); const viewerId= targetDiv; if (!firstShowImage) { firstShowImage= true; imageRenderType= globalPrefs.imageDisplayType; if (imageRenderType===STANDARD) highlevelImageInit(llApi); } dispatchAddViewer(targetDiv, NewPlotMode.create_replace.key, IMAGE); dispatchUpdateCustom(viewerId, {independentLayout: true}); if (isHiPS) { request.Type= 'HiPS'; if (hipsImageConversion && !hipsImageConversion.hipsRequestRoot) { hipsImageConversion.hipsRequestRoot= request; } dispatchPlotHiPS({plotId, wpRequest:request, viewerId, hipsImageConversion, pvOptions: { userCanDeletePlots: userCanDelete} }); } else { if (hipsImageConversion && !hipsImageConversion.imageRequestRoot) { hipsImageConversion.imageRequestRoot= request; } dispatchPlotImage({plotId, wpRequest:request, viewerId, hipsImageConversion, pvOptions: { userCanDeletePlots: userCanDelete} }); } if (imageRenderType===STANDARD) { renderDOM(targetDiv, MultiImageViewer, {viewerId, canReceiveNewPlots:NewPlotMode.create_replace.key, Toolbar:MultiViewStandardToolbar }); } else { renderDOM(targetDiv, ApiFullImageDisplay, {viewerId, renderTreeId:viewerId, canReceiveNewPlots:NewPlotMode.create_replace.key, Toolbar:MultiViewStandardToolbar }); } } function initCoverage(llApi, targetDiv,options= {}) { const {MultiImageViewer, MultiViewStandardToolbar}= llApi.ui; const {renderDOM,debug}= llApi.util; const {startCoverageWatcher,NewPlotMode}= llApi.util.image; highlevelImageInit(llApi); const {canReceiveNewPlots=NewPlotMode.replace_only.key}= options; renderDOM(targetDiv, MultiImageViewer, {viewerId:targetDiv, canReceiveNewPlots, canDelete:false, Toolbar:MultiViewStandardToolbar }); options= Object.assign({},options, {viewerId:targetDiv}); startCoverageWatcher(options); } var imageInit= false; function highlevelImageInit(llApi) { if (!imageInit) { llApi.action.dispatchApiToolsView(true); llApi.util.image.initAutoReadout(); imageInit= true; } } var plotCnt= 0; function makePlotId(wsConnIdGetter) { return () => { plotCnt++; return `apiPlot-${wsConnIdGetter()}-${plotCnt}`; }; } //================================================================ //---------- Private Table functions //================================================================ //================================================================ //---------- Private XYPlot or Histogram functions //================================================================ function doShowChart(llApi, targetDiv, params={}) { const {dispatchChartAdd}= llApi.action; const {uniqueChartId} = llApi.util.chart; const {renderDOM} = llApi.util; const {ChartsContainer}= llApi.ui; const tbl_group = params.tbl_group; // when tbl_group parameter is set, show a default chart // for an active table in this table group if (!tbl_group) { params = Object.assign({ chartId: uniqueChartId(`${targetDiv}`), viewerId: targetDiv, chartType: 'plot.ly' }, params); dispatchChartAdd(params); } renderDOM(targetDiv, ChartsContainer, { key: `${targetDiv}-plot`, viewerId: targetDiv, tbl_group, addDefaultChart: Boolean(tbl_group), closeable: false, expandedMode: false, noChartToolbar: params.noChartToolbar } ); } function doShowXYPlot(llApi, targetDiv, params={}) { const {dispatchTableFetch, dispatchChartAdd}= llApi.action; const {renderDOM} = llApi.util; const {makeFileRequest} = llApi.util.table; const {ChartsContainer}= llApi.ui; if ((typeof targetDiv).match(/string|HTMLDivElement/) === null) { // old api.. need to change targetDiv and params const oldApiParams = targetDiv; targetDiv = params; params = oldApiParams; } // it is not quite clear how to handle situation when there are multiple tables in a group // for now we are connecting to the currently active table in the group const tblGroup = params.QUERY_ID || params.tbl_group; // QUERY_ID is deprecated, should be removed at some point var tblId = params.tbl_id; // standalone plot, not connected to an existing table if (!tblGroup && !tblId) { const searchRequest = makeFileRequest( params.chartTitle||'', // title params.source, // source null, // alt_source {pageSize: 1} // options ); tblId = searchRequest.tbl_id; dispatchTableFetch(searchRequest); params = Object.assign({}, params, {tbl_id: tblId}); } const help_id = params.help_id; const chartId = targetDiv; dispatchChartAdd({chartId, chartType: 'scatter', help_id, deletable: false, viewerId: targetDiv, params}); renderDOM(targetDiv, ChartsContainer, { key: `${targetDiv}-xyplot`, viewerId: targetDiv, tbl_group: tblGroup, addDefaultChart: Boolean(tblGroup), closeable: false, expandedMode: false } ); } function doShowHistogram(llApi, targetDiv, params={}) { const {dispatchTableFetch, dispatchChartAdd}= llApi.action; const {renderDOM} = llApi.util; const {makeFileRequest} = llApi.util.table; const {ChartsContainer}= llApi.ui; // it is not quite clear how to handle situation when there are multiple tables in a group // for now we are connecting to the currently active table in the group const tblGroup = params.tbl_group; var tblId = params.tbl_id; // standalone plot, not connected to an existing table if (!tblGroup && !tblId) { const searchRequest = makeFileRequest( params.chartTitle||'', // title params.source, // source null, // alt_source {pageSize: 1} // options ); tblId = searchRequest.tbl_id; dispatchTableFetch(searchRequest); params = Object.assign({}, params, {tbl_id: tblId}); } const help_id = params.help_id; const chartId = targetDiv; dispatchChartAdd({chartId, chartType: 'histogram', help_id, deletable: false, viewerId: targetDiv, params}); renderDOM(targetDiv, ChartsContainer, { key: `${targetDiv}-histogram`, viewerId: targetDiv, tbl_group: tblGroup, addDefaultChart: Boolean(tblGroup), closeable: false, expandedMode: false } ); }