diff --git a/build.gradle b/build.gradle index 1086268cd6..56c9944868 100644 --- a/build.gradle +++ b/build.gradle @@ -12,3 +12,7 @@ subprojects { apply from: "$fireflyPath/buildScript/utils.gincl" +task purge << { + println('Removes all firefly build directories.') + delete "${fireflyPath}/build", "${fireflyPath}/jars/build", "${fireflyPath}/node_modules" +} diff --git a/buildScript/utils.gincl b/buildScript/utils.gincl index 3a9cf1c2ca..2a1f130a00 100644 --- a/buildScript/utils.gincl +++ b/buildScript/utils.gincl @@ -85,7 +85,3 @@ def execCmd(remoteHost, ignoreFailure, cmdLine) { } -task purge << { - println('Removes all build directories.') - delete "${fireflyPath}/build", "${fireflyPath}/jars/build", "${fireflyPath}/node_modules" -} diff --git a/buildScript/webpack.config.js b/buildScript/webpack.config.js index 336e3f9a4d..705db9f5f6 100644 --- a/buildScript/webpack.config.js +++ b/buildScript/webpack.config.js @@ -10,10 +10,24 @@ import fs from 'fs'; var exclude_dirs = [/node_modules/, /java/, /python/, /config/, /test/]; -function makeWebpackConfig(config) { +/** + * A helper function to create the webpack config object to be sent to webpack module bundler. + * @param {Object} config configuration parameters used to create the webpack config object. + * @param {string} config.src source directory + * @param {string} config.firefly_root Firefly's build root + * @param {string} [config.firefly_dir] Firefly's JS source directory + * @param {Object} [config.alias] additional alias + * @param {boolean=true} [config.use_loader] generate a loader to load compiled JS script(s). Defautls to true + * @param {string} [config.project] project name + * @param {string} [config.filename] name of the generated JS script. + * @returns {Object} a webpack config object. + */ +export default function makeWebpackConfig(config) { // setting defaults - config.firefly_dir = config.firefly_dir || config.src; + config.src = config.src || __dirname; + config.firefly_root = config.firefly_root || path.resolve(config.src, '../..'); + config.firefly_dir = config.firefly_dir || path.resolve(config.firefly_root, 'src/firefly'); config.project = config.project || path.resolve(config.src, '../../'); var def_config = { @@ -229,11 +243,6 @@ function makeWebpackConfig(config) { - -export default makeWebpackConfig; - - - function firefly_loader(loadScript, outpath, debug=true) { return function () { this.plugin('done', function (stats) { @@ -249,7 +258,7 @@ function firefly_loader(loadScript, outpath, debug=true) { }`; } var content = fs.readFileSync(loadScript); - content += `\nloadScript('/${cxt_name}/${cxt_name}-${hash}.js'${callback});`; + content += `\nloadScript('/${cxt_name}/firefly-${hash}.js'${callback});`; var loader = path.join(outpath, 'firefly_loader.js'); fs.writeFileSync(loader, content); }); diff --git a/src/fftools/config/web.xml b/src/fftools/config/web.xml index a0361a3eb6..6e702d28a1 100644 --- a/src/fftools/config/web.xml +++ b/src/fftools/config/web.xml @@ -190,7 +190,7 @@ - edu.caltech.ipac.firefly.server.servlets.SearchServices + edu.caltech.ipac.firefly.server.servlets.JsonSearchServices FireFly SearchService diff --git a/src/fftools/webpack.config.js b/src/fftools/webpack.config.js index e12c522f8d..d8d6d46186 100644 --- a/src/fftools/webpack.config.js +++ b/src/fftools/webpack.config.js @@ -4,15 +4,7 @@ require('babel/register'); var path = require('path'); var firefly_root = path.resolve(__dirname, '../..'); +var name = 'fftools'; +var entry = {fflib: path.resolve(firefly_root, 'src/firefly/js/fireflyJSLib.js')}; -var config = { - name : 'fftools', - src : __dirname, - use_loader: false, - entry : { - fflib: path.resolve(firefly_root, 'src/firefly/js/fireflyJSLib.js') - }, - firefly_dir : path.resolve(firefly_root, 'src/firefly') -}; - -module.exports = require(firefly_root + '/buildScript/webpack.config.js')(config); +module.exports = require(firefly_root + '/buildScript/webpack.config.js')({firefly_root, name, entry, user_loader: false}); diff --git a/src/firefly/config/web.xml b/src/firefly/config/web.xml index f9df237ad6..e73529e6bf 100644 --- a/src/firefly/config/web.xml +++ b/src/firefly/config/web.xml @@ -134,7 +134,7 @@ - edu.caltech.ipac.firefly.server.servlets.SearchServices + edu.caltech.ipac.firefly.server.servlets.JsonSearchServices FireFly SearchService diff --git a/src/firefly/html/css/global.css b/src/firefly/html/css/global.css index 25d27e636e..ffcee53270 100644 --- a/src/firefly/html/css/global.css +++ b/src/firefly/html/css/global.css @@ -78,19 +78,20 @@ order: 3; } -.button-std { - height: 23px; +.button { padding: 0 3px; margin-right: 3px; } -.button-hl { - font-weight: bold; +.button.std { height: 23px; - padding: 0 3px; } -.button-std:hover, .button-hl:hover { +.button.hl { + font-weight: bold; +} + +.button:hover { cursor: pointer; } diff --git a/src/firefly/html/firefly.html b/src/firefly/html/firefly.html index 9b07c24401..ae7cc51b3c 100644 --- a/src/firefly/html/firefly.html +++ b/src/firefly/html/firefly.html @@ -15,7 +15,7 @@ window.firefly = {app: {}}; - +
diff --git a/src/firefly/java/edu/caltech/ipac/firefly/data/table/TableMeta.java b/src/firefly/java/edu/caltech/ipac/firefly/data/table/TableMeta.java index 831e2b5800..76d80be5f1 100644 --- a/src/firefly/java/edu/caltech/ipac/firefly/data/table/TableMeta.java +++ b/src/firefly/java/edu/caltech/ipac/firefly/data/table/TableMeta.java @@ -202,6 +202,9 @@ public void setAttribute(String key, Number value) { public void setAttribute(String key, String value) { attributes.put(key, value); } + public void removeAttribute(String key) { + attributes.remove(key); + } public void setWorldPtAttribute(String key, WorldPt pt) { diff --git a/src/firefly/java/edu/caltech/ipac/firefly/server/query/JsonDataProcessor.java b/src/firefly/java/edu/caltech/ipac/firefly/server/query/JsonDataProcessor.java new file mode 100644 index 0000000000..9481024ea0 --- /dev/null +++ b/src/firefly/java/edu/caltech/ipac/firefly/server/query/JsonDataProcessor.java @@ -0,0 +1,98 @@ +/* + * License information at https://github.com/Caltech-IPAC/firefly/blob/master/License.txt + */ +package edu.caltech.ipac.firefly.server.query; + +import edu.caltech.ipac.firefly.data.*; +import edu.caltech.ipac.firefly.data.table.TableMeta; +import edu.caltech.ipac.firefly.server.util.Logger; +import edu.caltech.ipac.util.*; +import org.json.simple.JSONArray; +import org.json.simple.JSONObject; + +import java.io.*; +import java.util.*; + + +/** + * Subclasses of this processor return JSONObject. This helper class provides SearchProcessor's + * supported features and helper functions. + * + * @author loi + * @version $Id: IpacTablePartProcessor.java,v 1.33 2012/10/23 18:37:22 loi Exp $ + */ +abstract public class JsonDataProcessor implements SearchProcessor { + + public static final boolean useWorkspace = AppProperties.getBooleanProperty("useWorkspace", false); + + public static final Logger.LoggerImpl SEARCH_LOGGER = Logger.getLogger(Logger.SEARCH_LOGGER); + public static final Logger.LoggerImpl LOGGER = Logger.getLogger(); + + public ServerRequest inspectRequest(ServerRequest request) { return request; }; + + public void prepareTableMeta(TableMeta defaults, List columns, ServerRequest request) {} + + public QueryDescResolver getDescResolver() { return null;} + + public void onComplete(ServerRequest request, String results) throws DataAccessException {} + + public void writeData(OutputStream out, ServerRequest request) throws DataAccessException {} + + public boolean doCache() { return false; } + + public boolean doLogging() { return false; } + + + public String getUniqueID(ServerRequest request) { + String uid = request.getRequestId() + "-"; + + // parameters to get original data (before filter, sort, etc.) + List srvParams = new ArrayList<>(); + for (Param p : request.getParams()) { + srvParams.add(p); + } + + // sort by parameter name + Collections.sort(srvParams, (p1, p2) -> p1.getName().compareTo(p2.getName())); + for (Param p : srvParams) { + uid += "|" + p.toString(); + } + return uid; + } + + abstract public String getData(ServerRequest request) throws DataAccessException; + + + protected JSONArray toJsonArray(List values) { + JSONArray jAry = new JSONArray(); + for (Object v : values) { + if (v instanceof List) { + + } else if(v instanceof Map) { + jAry.add(toJsonObject((Map) v)); + } else { + jAry.add(v); + } + } + return jAry; + } + + protected JSONObject toJsonObject(Map values) { + + JSONObject jObj = new JSONObject(); + for (Object k : values.keySet()) { + String name = String.valueOf(k); + Object v = values.get(k); + if (v instanceof List) { + jObj.put(name, toJsonArray((List) v)); + } else if(v instanceof Map) { + jObj.put(name, toJsonObject((Map) v)); + } else { + jObj.put(name, v); + } + } + return jObj; + } + +} + diff --git a/src/firefly/java/edu/caltech/ipac/firefly/server/query/SearchManager.java b/src/firefly/java/edu/caltech/ipac/firefly/server/query/SearchManager.java index 4d15ba3d70..e5d15b21f0 100644 --- a/src/firefly/java/edu/caltech/ipac/firefly/server/query/SearchManager.java +++ b/src/firefly/java/edu/caltech/ipac/firefly/server/query/SearchManager.java @@ -7,12 +7,7 @@ import edu.caltech.ipac.firefly.core.RPCException; import edu.caltech.ipac.firefly.core.background.BackgroundState; import edu.caltech.ipac.firefly.core.background.BackgroundStatus; -import edu.caltech.ipac.firefly.data.DownloadRequest; -import edu.caltech.ipac.firefly.data.FileStatus; -import edu.caltech.ipac.firefly.data.Request; -import edu.caltech.ipac.firefly.data.ServerParams; -import edu.caltech.ipac.firefly.data.ServerRequest; -import edu.caltech.ipac.firefly.data.TableServerRequest; +import edu.caltech.ipac.firefly.data.*; import edu.caltech.ipac.firefly.data.table.RawDataSet; import edu.caltech.ipac.firefly.data.table.TableMeta; import edu.caltech.ipac.firefly.server.ServerContext; @@ -23,11 +18,12 @@ import edu.caltech.ipac.firefly.server.util.QueryUtil; import edu.caltech.ipac.firefly.server.util.ipactable.*; import edu.caltech.ipac.util.Assert; +import edu.caltech.ipac.util.DataGroup; +import edu.caltech.ipac.util.IpacTableUtil; import org.json.simple.JSONObject; import org.json.simple.JSONValue; import org.json.simple.parser.JSONParser; import org.json.simple.parser.ParseException; -import edu.caltech.ipac.util.IpacTableUtil; import java.io.File; import java.io.IOException; @@ -43,59 +39,38 @@ public class SearchManager { public static final Logger.LoggerImpl LOGGER = Logger.getLogger(); - +//==================================================================== +// RPC.. returning RawDataSet +//==================================================================== public RawDataSet getRawDataSet(TableServerRequest request) throws DataAccessException { - SearchProcessor processor = getProcessor(request.getRequestId()); - ServerRequest req = processor.inspectRequest(request); - if (req != null) { - DataGroupPart dgp = null; - try { - dgp = (DataGroupPart) processor.getData(req); - RawDataSet ds = QueryUtil.getRawDataSet(dgp); - DataGroupPart.State status = dgp.getTableDef().getStatus(); - ds.getMeta().setIsLoaded(!status.equals(DataGroupPart.State.INPROGRESS)); - - processor.prepareTableMeta(ds.getMeta(), - Collections.unmodifiableList(dgp.getTableDef().getCols()), - req); - - return ds; - } catch (Exception ex) { - String source = dgp != null && dgp.getTableDef() != null ? dgp.getTableDef().getSource() : "unknown"; - String errMsg = ex.getClass().getSimpleName() + ":" + ex.getMessage() + " from:" + source ; - LOGGER.error(ex, errMsg); - throw new DataAccessException(errMsg, ex); - } - } else { - throw new DataAccessException("Request fail inspection. Operation aborted."); - } + DataGroupPart dgp = getDataGroup(request); + RawDataSet ds = QueryUtil.getRawDataSet(dgp); + // merge TableDef info into ds.meta + dgp.getTableDef().setMetaTo(ds.getMeta()); + return ds; } - public String getJsonString(TableServerRequest request) throws DataAccessException { +//==================================================================== +// JSON.. top level handler, return JSON string. +//==================================================================== + public String handleJsonRequest(TableServerRequest request) throws DataAccessException { SearchProcessor processor = getProcessor(request.getRequestId()); - ServerRequest req = processor.inspectRequest(request); - if (req != null) { - DataGroupPart dgp = null; - try { - dgp = (DataGroupPart) processor.getData(req); - TableMeta meta = new TableMeta(); - DataGroupPart.State status = dgp.getTableDef().getStatus(); - meta.setIsLoaded(!status.equals(DataGroupPart.State.INPROGRESS)); - - processor.prepareTableMeta(meta, - Collections.unmodifiableList(dgp.getTableDef().getCols()), - req); - JSONObject json = JsonTableUtil.toJsonTableModel(dgp, meta, request); - return json.toJSONString(); + if (processor instanceof IpacTablePartProcessor) { + return jsonTablePartRequest(request); + } else if (processor instanceof JsonDataProcessor) { + return (String)processor.getData(request); + } + throw new DataAccessException("Unable to resolve a search processor for this request. Operation aborted:" + request.getRequestId()); + } - } catch (Exception ex) { - String source = dgp != null && dgp.getTableDef() != null ? dgp.getTableDef().getSource() : "unknown"; - String errMsg = ex.getClass().getSimpleName() + ":" + ex.getMessage() + " from:" + source ; - LOGGER.error(ex, errMsg); - throw new DataAccessException(errMsg, ex); - } - } else { - throw new DataAccessException("Request fail inspection. Operation aborted."); + private String jsonTablePartRequest(TableServerRequest request) throws DataAccessException { + try { + DataGroupPart dgp = getDataGroup(request); + JSONObject json = JsonTableUtil.toJsonTableModel(dgp, request); + return json.toJSONString(); + } catch (IOException ex) { + LOGGER.error(ex); + throw new DataAccessException("Fail convert data to JSON.", ex); } } @@ -121,13 +96,33 @@ public String getJSONData(ServerRequest request) throws DataAccessException { } } +//==================================================================== +// search related funtions... +//==================================================================== public DataGroupPart getDataGroup(TableServerRequest request) throws DataAccessException { SearchProcessor processor = getProcessor(request.getRequestId()); + DataGroupPart dgp = null; ServerRequest req = processor.inspectRequest(request); if (req != null) { - DataGroupPart dgp = (DataGroupPart) processor.getData(req); - return dgp; + try { + dgp = (DataGroupPart) processor.getData(req); + TableMeta meta = new TableMeta(); + DataGroupPart.State status = dgp.getTableDef().getStatus(); + meta.setIsLoaded(!status.equals(DataGroupPart.State.INPROGRESS)); + + processor.prepareTableMeta(meta, + Collections.unmodifiableList(dgp.getTableDef().getCols()), + req); + // merge meta info with TableDef info + dgp.getTableDef().getMetaFrom(meta); + return dgp; + } catch (Exception ex) { + String source = dgp != null && dgp.getTableDef() != null ? dgp.getTableDef().getSource() : "unknown"; + String errMsg = ex.getClass().getSimpleName() + ":" + ex.getMessage() + " from:" + source; + LOGGER.error(ex, errMsg); + throw new DataAccessException(errMsg, ex); + } } else { throw new DataAccessException("Request fail inspection. Operation aborted."); } diff --git a/src/firefly/java/edu/caltech/ipac/firefly/server/query/IpacTableFromSource.java b/src/firefly/java/edu/caltech/ipac/firefly/server/query/tables/IpacTableFromSource.java similarity index 97% rename from src/firefly/java/edu/caltech/ipac/firefly/server/query/IpacTableFromSource.java rename to src/firefly/java/edu/caltech/ipac/firefly/server/query/tables/IpacTableFromSource.java index f5e70b33cf..7e76a3e538 100644 --- a/src/firefly/java/edu/caltech/ipac/firefly/server/query/IpacTableFromSource.java +++ b/src/firefly/java/edu/caltech/ipac/firefly/server/query/tables/IpacTableFromSource.java @@ -1,7 +1,7 @@ /* * License information at https://github.com/Caltech-IPAC/firefly/blob/master/License.txt */ -package edu.caltech.ipac.firefly.server.query; +package edu.caltech.ipac.firefly.server.query.tables; import edu.caltech.ipac.firefly.data.Param; import edu.caltech.ipac.firefly.data.ServerParams; @@ -10,6 +10,7 @@ import edu.caltech.ipac.firefly.data.table.TableMeta; import edu.caltech.ipac.firefly.server.ServerContext; import edu.caltech.ipac.firefly.server.packagedata.FileInfo; +import edu.caltech.ipac.firefly.server.query.*; import edu.caltech.ipac.util.DataType; import edu.caltech.ipac.util.FileUtil; import edu.caltech.ipac.util.StringUtils; diff --git a/src/firefly/java/edu/caltech/ipac/firefly/server/query/tables/TableServices.java b/src/firefly/java/edu/caltech/ipac/firefly/server/query/tables/TableServices.java new file mode 100644 index 0000000000..bca1efeacf --- /dev/null +++ b/src/firefly/java/edu/caltech/ipac/firefly/server/query/tables/TableServices.java @@ -0,0 +1,38 @@ +package edu.caltech.ipac.firefly.server.query.tables; + +import edu.caltech.ipac.firefly.core.RPCException; +import edu.caltech.ipac.firefly.data.ServerRequest; +import edu.caltech.ipac.firefly.server.ServerContext; +import edu.caltech.ipac.firefly.server.query.*; +import edu.caltech.ipac.util.StringUtils; +import org.json.simple.JSONObject; + +import java.io.File; +import java.io.IOException; +import java.util.List; + +public class TableServices { + + + @SearchProcessorImpl(id = "Table__SelectedValues", params ={ + @ParamDoc(name = "filePath", desc = "The path of the file on the server."), + @ParamDoc(name = "selectedRows", desc = "Selected row indices separated by comma."), + @ParamDoc(name = "columnName", desc = "The name of the column to get the data from."), + }) + static public class SelectedValues extends JsonDataProcessor { + public String getData(ServerRequest request) throws DataAccessException { + String filePath = request.getParam("filePath"); + try { + String selRows = request.getParam("selectedRows"); + String columnName = request.getParam("columnName"); + List rows = StringUtils.convertToListInteger(selRows, ","); + List values = new SearchManager().getDataFileValues(ServerContext.convertToFile(filePath), rows, columnName); + return toJsonArray(values).toJSONString(); + } catch (IOException e) { + throw new DataAccessException("Fail to retrieve data for file:" + filePath); + } + } + + } + +} diff --git a/src/firefly/java/edu/caltech/ipac/firefly/server/servlets/SearchServices.java b/src/firefly/java/edu/caltech/ipac/firefly/server/servlets/JsonSearchServices.java similarity index 89% rename from src/firefly/java/edu/caltech/ipac/firefly/server/servlets/SearchServices.java rename to src/firefly/java/edu/caltech/ipac/firefly/server/servlets/JsonSearchServices.java index 669ed3021a..676ac0b265 100644 --- a/src/firefly/java/edu/caltech/ipac/firefly/server/servlets/SearchServices.java +++ b/src/firefly/java/edu/caltech/ipac/firefly/server/servlets/JsonSearchServices.java @@ -4,7 +4,6 @@ package edu.caltech.ipac.firefly.server.servlets; import edu.caltech.ipac.firefly.core.EndUserException; -import edu.caltech.ipac.firefly.core.RPCException; import edu.caltech.ipac.firefly.data.TableServerRequest; import edu.caltech.ipac.firefly.server.query.SearchManager; import edu.caltech.ipac.firefly.server.util.QueryUtil; @@ -19,13 +18,13 @@ */ @MultipartConfig -public class SearchServices extends BaseHttpServlet { +public class JsonSearchServices extends BaseHttpServlet { @Override protected void processRequest(HttpServletRequest req, HttpServletResponse res) throws Exception { TableServerRequest tsr = QueryUtil.convertToServerRequest(req); try { - String jsonStr = new SearchManager().getJsonString(tsr); + String jsonStr = new SearchManager().handleJsonRequest(tsr); res.setContentType("application/json"); FileUtil.writeStringToStream(jsonStr, res.getOutputStream()); diff --git a/src/firefly/java/edu/caltech/ipac/firefly/server/util/QueryUtil.java b/src/firefly/java/edu/caltech/ipac/firefly/server/util/QueryUtil.java index abf445217a..d71e65bffa 100644 --- a/src/firefly/java/edu/caltech/ipac/firefly/server/util/QueryUtil.java +++ b/src/firefly/java/edu/caltech/ipac/firefly/server/util/QueryUtil.java @@ -302,7 +302,7 @@ public static DataGroupPart convertToDataGroupPart(DataGroup dg, int startIdx, i DataGroup page = dg.subset(startIdx, startIdx+pageSize); page.setRowIdxOffset(startIdx); TableDef tableDef = new TableDef(); - tableDef.setSource("unknown"); + tableDef.addAttributes(dg.getKeywords().toArray(new DataGroup.Attribute[0])); tableDef.setStatus(DataGroupPart.State.COMPLETED); tableDef.setCols(Arrays.asList(page.getDataDefinitions())); diff --git a/src/firefly/java/edu/caltech/ipac/firefly/server/util/ipactable/JsonTableUtil.java b/src/firefly/java/edu/caltech/ipac/firefly/server/util/ipactable/JsonTableUtil.java index 9392a5ae91..575206938e 100644 --- a/src/firefly/java/edu/caltech/ipac/firefly/server/util/ipactable/JsonTableUtil.java +++ b/src/firefly/java/edu/caltech/ipac/firefly/server/util/ipactable/JsonTableUtil.java @@ -30,43 +30,29 @@ public class JsonTableUtil { * convert data to JSON TableModel. Use toJSONString() to turn it into a String. * * @param page - * @param meta * @param request * @return * @throws IOException */ - public static JSONObject toJsonTableModel(DataGroupPart page, TableMeta meta, TableServerRequest request) throws IOException { + public static JSONObject toJsonTableModel(DataGroupPart page, TableServerRequest request) throws IOException { - if (meta == null) { - meta = new TableMeta(); - } - if (page.getTableDef()!=null && page.getTableDef().getAttributes() != null) { - if (StringUtils.isEmpty(meta.getSource())) { - meta.setSource(page.getTableDef().getSource()); - } - // merge tableDef's attributes with tablemeta's. - for (DataGroup.Attribute attr : page.getTableDef().getAttributes()) { - // meta override tabledef's. - if (!StringUtils.isEmpty(attr.getValue()) && !attr.isComment() && !meta.contains(attr.getKey())) { - meta.setAttribute(attr.getKey(), attr.getValue()); - } - } - } + TableDef meta = page.getTableDef(); if (request != null && request.getMeta() != null) { for (String key : request.getMeta().keySet()) { meta.setAttribute(key, request.getMeta(key)); } } - String tbl_id = meta.getAttribute(TableServerRequest.TBL_ID); JSONObject tableModel = new JSONObject(); - tableModel.put("tbl_id", tbl_id); + if (meta.contains(TableServerRequest.TBL_ID)) { + tableModel.put("tbl_id", meta.getAttribute(TableServerRequest.TBL_ID).getValue()); + } tableModel.put("title", page.getData().getTitle()); tableModel.put("type", guessType(meta)); tableModel.put("totalRows", page.getRowCount()); if (page.getData() != null ) { - tableModel.put("tableData", toJsonTableData(page.getData(), page.getTableDef(), meta)); + tableModel.put("tableData", toJsonTableData(page.getData(), page.getTableDef())); } @@ -117,7 +103,7 @@ public static JSONObject toJsonTableRequest(TableServerRequest req) { * @param tableDef * @return */ - public static JSONObject toJsonTableData(DataGroup data, TableDef tableDef, TableMeta meta) { + public static JSONObject toJsonTableData(DataGroup data, TableDef tableDef) { List> tableData = new ArrayList>(); for (int i = 0; i < data.size(); i++) { List row = new ArrayList(); @@ -129,7 +115,7 @@ public static JSONObject toJsonTableData(DataGroup data, TableDef tableDef, Tabl JSONObject tdata = new JSONObject(); - tdata.put("columns", toJsonTableColumn(tableDef!=null?tableDef.getCols().toArray(new DataType[0]):data.getDataDefinitions() , meta)); + tdata.put("columns", toJsonTableColumn(data, tableDef)); tdata.put("data", tableData); return tdata; } @@ -140,19 +126,11 @@ public static JSONObject toJsonTableData(DataGroup data, TableDef tableDef, Tabl * @param meta * @return */ - public static JSONObject toJsonTableMeta(TableMeta meta) { + public static JSONObject toJsonTableMeta(TableDef meta) { JSONObject tmeta = new JSONObject(); - tmeta.put("source", meta.getSource()); - tmeta.put("fileSize", meta.getFileSize()); - if (meta.getRelatedCols() != null) { - tmeta.put("relatedCols", StringUtils.toString(meta.getRelatedCols(), ",")); - } - if (meta.getGroupByCols() != null) { - tmeta.put("groupByCols", StringUtils.toString(meta.getGroupByCols(), ",")); - } - if (meta.getAttributes() != null) { - for (String key : meta.getAttributes().keySet()) { - tmeta.put(key, meta.getAttribute(key)); + for (DataGroup.Attribute att : meta.getAttributes()) { + if (!att.isComment()) { + tmeta.put(att.getKey(), att.getValue()); } } return tmeta; @@ -162,7 +140,9 @@ public static JSONObject toJsonTableMeta(TableMeta meta) { // //==================================================================== - private static List toJsonTableColumn(DataType[] dataTypes, TableMeta meta) { + private static List toJsonTableColumn(DataGroup dataGroup, TableDef meta) { + + DataType[] dataTypes = meta.getCols().size() > 0 ? meta.getCols().toArray(new DataType[0]) : dataGroup.getDataDefinitions(); ArrayList cols = new ArrayList(); for (DataType dt :dataTypes) { @@ -182,53 +162,65 @@ private static List toJsonTableColumn(DataType[] dataTypes, TableMet } // modify column's attributes based on meta - String label = meta.getAttribute( makeAttribKey(LABEL_TAG, cname) ); + String label = getColAttr(meta, LABEL_TAG, cname); if (!StringUtils.isEmpty(label)) { c.put("label", label); } - String desc = meta.getAttribute( makeAttribKey(DESC_TAG, cname) ); + String desc = getColAttr(meta, DESC_TAG, cname); if (!StringUtils.isEmpty(desc)) { c.put("desc", desc); } - String visibility = meta.getAttribute( makeAttribKey(VISI_TAG, cname) ); + String visibility = getColAttr(meta, VISI_TAG, cname); if (!StringUtils.isEmpty(visibility)) { c.put("visibility", visibility); } - String width = meta.getAttribute( makeAttribKey(WIDTH_TAG, cname) ); + String width = getColAttr(meta, WIDTH_TAG, cname); if (!StringUtils.isEmpty(width)) { c.put("width", width); } - String prefWidth = meta.getAttribute( makeAttribKey(PREF_WIDTH_TAG, cname) ); + String prefWidth = getColAttr(meta, PREF_WIDTH_TAG, cname); if (!StringUtils.isEmpty(prefWidth)) { c.put("prefWidth", prefWidth); } - String sortable = meta.getAttribute( makeAttribKey(SORTABLE_TAG, cname) ); + String sortable = getColAttr(meta, SORTABLE_TAG, cname); if (!StringUtils.isEmpty(sortable)) { c.put("sortable", sortable); } - String units = meta.getAttribute( makeAttribKey(UNIT_TAG, cname) ); + String units = getColAttr(meta, UNIT_TAG, cname); if (!StringUtils.isEmpty(units)) { c.put("units", units); } - String items = meta.getAttribute( makeAttribKey(ITEMS_TAG, cname) ); + String items = getColAttr(meta, ITEMS_TAG, cname); if (!StringUtils.isEmpty(items)) { c.put("items", items); } - String sortBy = meta.getAttribute( makeAttribKey(SORT_BY_TAG, cname) ); + String sortBy = getColAttr(meta, SORT_BY_TAG, cname); if (!StringUtils.isEmpty(sortBy)) { - c.put("sortBy", sortBy); + c.put("sortByCols", sortBy); } cols.add(c); } + for (DataGroup.Attribute att : meta.getAttributes()) { + // clean up all of the column's attributes since we already set it to the columns + if (att.getKey().startsWith("col.")) { + meta.removeAttribute(att.getKey()); + } + } return cols; } + + private static String getColAttr(TableDef meta, String tag, String cname) { + DataGroup.Attribute att = meta.getAttribute(makeAttribKey(tag, cname)); + return (att == null) ? "" : att.getValue(); + } + /** * get the type of data this table contains based on its meta information * * @param meta * @return */ - private static Object guessType(TableMeta meta) { + private static Object guessType(TableDef meta) { return "table"; } @@ -236,12 +228,11 @@ private static Object guessType(TableMeta meta) { //============================= //LZ DM-4494 - public static JSONObject toJsonTableModelMap(Map dataMap, Map metaMap, TableServerRequest request) throws IOException { + public static JSONObject toJsonTableModelMap(Map dataMap, TableServerRequest request) throws IOException { JSONObject jsoObj = new JSONObject(); for (Object key : dataMap.keySet()) { - TableMeta meta = metaMap.get(key); DataGroupPart dp = QueryUtil.convertToDataGroupPart(dataMap.get(key), 0, Integer.MAX_VALUE); - JSONObject aJsonTable = JsonTableUtil.toJsonTableModel(dp, meta, request); + JSONObject aJsonTable = JsonTableUtil.toJsonTableModel(dp, request); jsoObj.put(key, aJsonTable); diff --git a/src/firefly/java/edu/caltech/ipac/firefly/server/util/ipactable/TableDef.java b/src/firefly/java/edu/caltech/ipac/firefly/server/util/ipactable/TableDef.java index b201b1589d..873a056cd5 100644 --- a/src/firefly/java/edu/caltech/ipac/firefly/server/util/ipactable/TableDef.java +++ b/src/firefly/java/edu/caltech/ipac/firefly/server/util/ipactable/TableDef.java @@ -1,10 +1,13 @@ package edu.caltech.ipac.firefly.server.util.ipactable; +import edu.caltech.ipac.firefly.data.table.TableMeta; import edu.caltech.ipac.util.DataGroup; import edu.caltech.ipac.util.DataType; import edu.caltech.ipac.util.StringUtils; import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; import java.util.List; /** @@ -15,18 +18,16 @@ */ public class TableDef { private List cols = new ArrayList(); - private ArrayList attributes = new ArrayList(); + private HashMap attributes = new HashMap<>(); private int lineWidth; private int rowCount; - private int colCount; private int rowStartOffset; - private String sourceFile; private int lineSepLength; public void addAttributes(DataGroup.Attribute... attributes) { if (attributes != null) { for(DataGroup.Attribute a : attributes) { - this.attributes.add(a); + this.attributes.put(a.getKey(), a); } } } @@ -36,20 +37,18 @@ public List getCols() { return cols; } - public void addCols(DataType col) { - cols.add(col); - } - public List getAttributes() { - return attributes; + return new ArrayList<>(attributes.values()); } + public boolean contains(String name) { return attributes.containsKey(name);}; + public void setAttribute(String name, String value) { - DataGroup.Attribute att = getAttribute(name); - if (att != null) { - attributes.remove(att); - } - attributes.add(new DataGroup.Attribute(name, value)); + attributes.put(name, new DataGroup.Attribute(name, value)); + } + + public void removeAttribute(String name) { + attributes.remove(name); } public void setStatus(DataGroupPart.State status) { @@ -57,10 +56,7 @@ public void setStatus(DataGroupPart.State status) { } DataGroup.Attribute getAttribute(String key) { - for (DataGroup.Attribute at : attributes) { - if (at.getKey().equals(key)) return at; - } - return null; + return attributes.get(key); } public void ensureStatus() { @@ -109,11 +105,52 @@ public void setRowStartOffset(int rowStartOffset) { } public String getSource() { - return sourceFile; + DataGroup.Attribute source = getAttribute("source"); + return source != null ? source.getValue() : null; } public void setSource(String sourceFile) { - this.sourceFile = sourceFile; + setAttribute("source", sourceFile); + } + + public void setMetaTo(TableMeta meta) { + if (meta == null) return; + if (contains("groupByCols")) { + meta.setGroupByCols(StringUtils.asList(getAttribute("groupByCols").getValue(), ",")); + } + if (contains("relatedCols")) { + meta.setRelatedCols(StringUtils.asList(getAttribute("relatedCols").getValue(), ",")); + } + if (contains("fileSize")) { + meta.setFileSize(Long.parseLong(getAttribute("fileSize").getValue())); + } + if (getSource() != null) { + meta.setSource(getSource()); + } + meta.setIsLoaded(Boolean.parseBoolean(getAttribute("isFullyLoaded").getValue())); + for (String key : meta.getAttributes().keySet()) { + setAttribute(key, meta.getAttribute(key)); + } + } + + public void getMetaFrom(TableMeta meta) { + if (meta == null) return; + if (meta.getGroupByCols().size() > 0) { + setAttribute("groupByCols", StringUtils.toString(meta.getGroupByCols())); + } + if (meta.getRelatedCols().size() > 0) { + setAttribute("relatedCols", StringUtils.toString(meta.getRelatedCols())); + } + if (meta.getFileSize() > 0) { + setAttribute("fileSize", String.valueOf( meta.getFileSize()) ); + } + if (meta.getSource() != null) { + setSource(meta.getSource()); + } + setAttribute("isFullyLoaded", String.valueOf(meta.isLoaded())); + for (String key : meta.getAttributes().keySet()) { + setAttribute(key, meta.getAttribute(key)); + } } } /* diff --git a/src/firefly/java/edu/caltech/ipac/firefly/server/visualize/VisServerCommands.java b/src/firefly/java/edu/caltech/ipac/firefly/server/visualize/VisServerCommands.java index 4e57ec6c53..715ae2e8e8 100644 --- a/src/firefly/java/edu/caltech/ipac/firefly/server/visualize/VisServerCommands.java +++ b/src/firefly/java/edu/caltech/ipac/firefly/server/visualize/VisServerCommands.java @@ -300,13 +300,11 @@ public String doCommand(Map paramMap) throws IllegalArgumentEx //TableServerRequest req=TableServerRequest.parse(sp.getRequired(ServerParams.FITS_HEADER)); PlotState state= sp.getState(); - Object[] dataInfo = VisServerOps.getFitsHeader(state, tableID); - HashMap dataGroupMap= (HashMap ) dataInfo[0]; - HashMap metaMap = ( HashMap ) dataInfo[1]; + HashMap dataGroupMap = VisServerOps.getFitsHeader(state, tableID); TableServerRequest treq = new TableServerRequest("fitsHeaderTale"); treq.setPageSize(Integer.MAX_VALUE); - return JsonTableUtil.toJsonTableModelMap(dataGroupMap, metaMap, treq).toJSONString(); + return JsonTableUtil.toJsonTableModelMap(dataGroupMap, treq).toJSONString(); } diff --git a/src/firefly/java/edu/caltech/ipac/firefly/server/visualize/VisServerOps.java b/src/firefly/java/edu/caltech/ipac/firefly/server/visualize/VisServerOps.java index 5724fe1b96..acb715aec7 100644 --- a/src/firefly/java/edu/caltech/ipac/firefly/server/visualize/VisServerOps.java +++ b/src/firefly/java/edu/caltech/ipac/firefly/server/visualize/VisServerOps.java @@ -7,12 +7,12 @@ import edu.caltech.ipac.firefly.data.DataEntry; import edu.caltech.ipac.firefly.data.TableServerRequest; import edu.caltech.ipac.firefly.data.table.RawDataSet; -import edu.caltech.ipac.firefly.data.table.TableMeta; import edu.caltech.ipac.firefly.server.Counters; import edu.caltech.ipac.firefly.server.ServerContext; import edu.caltech.ipac.firefly.server.cache.UserCache; import edu.caltech.ipac.firefly.server.util.Logger; import edu.caltech.ipac.firefly.server.util.QueryUtil; +import edu.caltech.ipac.firefly.server.util.ipactable.TableDef; import edu.caltech.ipac.firefly.server.util.multipart.UploadFileInfo; import edu.caltech.ipac.firefly.visualize.Band; import edu.caltech.ipac.firefly.visualize.ClientFitsHeader; @@ -47,11 +47,7 @@ import edu.caltech.ipac.visualize.draw.Metric; import edu.caltech.ipac.visualize.draw.Metrics; import edu.caltech.ipac.visualize.plot.*; -import nom.tam.fits.BasicHDU; -import nom.tam.fits.Fits; -import nom.tam.fits.FitsException; -import nom.tam.fits.Header; -import nom.tam.fits.HeaderCard; +import nom.tam.fits.*; import nom.tam.util.Cursor; import java.awt.*; @@ -876,12 +872,12 @@ public static DataGroup getFitsHeaders(Header headers, String name) { DataType comment = new DataType("Comments", String.class); DataType keyword = new DataType("Keyword", String.class); DataType value = new DataType("Value", String.class); - comment.getFormatInfo().setWidth(80); - value.getFormatInfo().setWidth(80); - keyword.getFormatInfo().setWidth(68); - DataType[] types = new DataType[]{ - new DataType("#", Integer.class), - keyword, value, comment}; + DataType num = new DataType("#", Integer.class); + comment.getFormatInfo().setWidth(30); + value.getFormatInfo().setWidth(10); + keyword.getFormatInfo().setWidth(10); + num.getFormatInfo().setWidth(3); + DataType[] types = new DataType[]{num, keyword, value, comment}; DataGroup dg = new DataGroup("Headers - " + name, types); int i = 0; @@ -946,10 +942,11 @@ public static WebPlotResult getFitsHeaderInfo(PlotState state) { * DM-4494 * * @param state - * @return + * @param f + *@param tableID @return */ - public static Map getFitsHeaderExtend(PlotState state) throws FitsException { + private static Map getFitsHeaderExtend(PlotState state, File f, String tableID) throws FitsException { HashMap dataMap = new HashMap(); ActiveCallCtx ctx = null; @@ -960,9 +957,11 @@ public static Map getFitsHeaderExtend(PlotState state) throws FitsException { for (Band band : state.getBands()) { FitsRead fr = plot.getHistogramOps(band, ctx.getFitsReadGroup()).getFitsRead(); DataGroup dg = getFitsHeaders(fr.getHeader(), plot.getPlotDesc()); + dg.addAttribute("source", ServerContext.replaceWithPrefix(f)); + dg.addAttribute("fileSize", String.valueOf(f.length())); + dg.addAttribute(TableServerRequest.TBL_ID, tableID + '-' + band.name()); dataMap.put(band.name(), dg); - } return dataMap; @@ -981,12 +980,10 @@ public static Map getFitsHeaderExtend(PlotState state) throws FitsException { * @return */ - public static Object[] getFitsHeader(PlotState state, String tableID) throws FitsException { + public static HashMap getFitsHeader(PlotState state, String tableID) throws FitsException { HashMap dataMap = new HashMap(); - - HashMap metaMap = new HashMap(); try { for (Band band : state.getBands()) { File f = PlotStateUtil.getWorkingFitsFile(state, band); @@ -995,25 +992,21 @@ public static Object[] getFitsHeader(PlotState state, String tableID) throws Fi TableServerRequest request = new TableServerRequest("fitsHeaderTale"); request.setTblId(tableID); - TableMeta meta = new TableMeta("fitsHeader"); - meta.setFileSize(f.length()); - meta.setAttribute(TableServerRequest.TBL_ID, tableID + '-' + band.name()); - - metaMap.put(band.name(), meta); BasicHDU hdu[] = fits.read(); Header header = hdu[0].getHeader(); if (header.containsKey("EXTEND") && header.getBooleanValue("EXTEND")) { - dataMap = (HashMap) getFitsHeaderExtend(state); + dataMap = (HashMap) getFitsHeaderExtend(state, f, tableID); } else { DataGroup dg = getFitsHeaders(header, "fits data"); - + dg.addAttribute("source", ServerContext.replaceWithPrefix(f)); + dg.addAttribute("fileSize", String.valueOf(f.length())); + dg.addAttribute(TableServerRequest.TBL_ID, tableID + '-' + band.name()); dataMap.put(band.name(), dg); } } ////LZcounters.incrementVis("Fits header"); - Object[] mapObj = {dataMap, metaMap}; - return mapObj; + return dataMap; } catch (Exception e ) { diff --git a/src/firefly/js/core/AppDataCntlr.js b/src/firefly/js/core/AppDataCntlr.js index 9545740c64..3e5345b217 100644 --- a/src/firefly/js/core/AppDataCntlr.js +++ b/src/firefly/js/core/AppDataCntlr.js @@ -3,12 +3,13 @@ */ import {take} from 'redux-saga/effects'; +import shallowequal from 'shallowequal'; import {get} from 'lodash'; import {flux} from '../Firefly.js'; import {dispatchAddSaga} from '../core/MasterSaga.js'; import BrowserCache from '../util/BrowserCache.js'; -import menuRenderer from './reducers/MenuReducer.js'; +import {menuReducer} from './reducers/MenuReducer.js'; import Point, {isValidPoint} from '../visualize/Point.js'; import {getModuleName} from '../util/WebUtil.js'; @@ -186,21 +187,14 @@ function initPreferences() { export function reducer(state=getInitState(), action={}) { - if (action.type && !action.type.startsWith(APP_DATA_PATH)) return state; + var nstate = appDataReducer(state, action); + nstate.menu = menuReducer(nstate.menu, action); - var newState = appDataReducer(state, action); - - var menu = menuRenderer.reducer(newState.menu, action); - - return mergeAll(state, newState, {menu}); -} - -function mergeAll(orig, newval, updates) { - - var hasChanged = orig !== newval; - hasChanged = hasChanged || Object.keys(updates).reduce( (prev, next) => prev || orig[next] != updates[next], false); - - return hasChanged ? Object.assign({}, newval, updates) : orig; + if (shallowequal(state, nstate)) { + return state; + } else { + return nstate; + } } function appDataReducer(state, action={}) { @@ -310,7 +304,7 @@ export function dispatchOnAppReady(callback) { /*---------------------------- EXPORTED FUNTIONS -----------------------------*/ export function isAppReady() { return get(flux.getState(), [APP_DATA_PATH, 'isReady']) && - get(flux.getState(), [APP_DATA_PATH, 'gwtLoaded']); + (get(window, 'firefly.noGWT') || get(flux.getState(), [APP_DATA_PATH, 'gwtLoaded'])); } export function getMenu() { diff --git a/src/firefly/js/core/FireflyViewer.js b/src/firefly/js/core/FireflyViewer.js index 958bb33740..a88b4847d7 100644 --- a/src/firefly/js/core/FireflyViewer.js +++ b/src/firefly/js/core/FireflyViewer.js @@ -32,6 +32,7 @@ import {dispatchAddSaga} from '../core/MasterSaga.js'; *
  • menu: menu is an array of menu items {label, action, icon, desc, type}. Leave type blank for dropdown. If type='COMMAND', it will fire the action without triggering dropdown.
  • *
  • appTitle: The title of the FireflyViewer. It will appears at top left of the banner. Defaults to 'Firefly'.
  • *
  • appIcon: A url string to the icon to appear on the banner.
  • + *
  • footer: A react elements to place on the footer when the menu drop down.
  • *
  • searchPanels: An array of additional react elements which are mapped to a menu item's action.
  • *
  • views: The type of result view. Choices are 'images', 'tables', and 'xyPlots'. They can be combined with ' | ', i.e. 'images | tables'
  • * @@ -80,7 +81,7 @@ export class FireflyViewer extends Component { render() { var {isReady, menu={}, appTitle, appIcon, altAppIcon, dropDown, - searchPanels, views} = this.state; + searchPanels, views, footer, style} = this.state; const {visible, view} = dropDown || {}; const searches = getDropDownNames(); @@ -88,11 +89,12 @@ export class FireflyViewer extends Component { return (
    ); } else { return ( -
    +
    @@ -117,8 +119,10 @@ FireflyViewer.propTypes = { appTitle: PropTypes.string, appIcon: PropTypes.string, altAppIcon: PropTypes.string, + footer: PropTypes.element, searchPanels: PropTypes.arrayOf(PropTypes.element), - views: PropTypes.string // combination of LO_VIEW separated by ' | '. ie. 'images | tables'. + views: PropTypes.string, // combination of LO_VIEW separated by ' | '. ie. 'images | tables'. + style: PropTypes.object }; FireflyViewer.defaultProps = { diff --git a/src/firefly/js/core/LayoutCntlr.js b/src/firefly/js/core/LayoutCntlr.js index 95a04234d5..cd9d4952a6 100644 --- a/src/firefly/js/core/LayoutCntlr.js +++ b/src/firefly/js/core/LayoutCntlr.js @@ -12,8 +12,8 @@ import ImagePlotCntlr from '../visualize/ImagePlotCntlr.js'; import {smartMerge} from '../tables/TableUtil.js'; import {getDropDownNames} from '../ui/Menu.jsx'; import {isMetaDataTable, isCatalogTable} from '../metaConvert/converterUtils.js'; -import {META_VIEWER_ID, FITS_VIEWER_ID, COVERAGE_VIEWER_ID} from '../visualize/ui/TriViewImageSection.jsx'; -import {ADD_IMAGES, REPLACE_IMAGES, REMOVE_IMAGES, getViewerPlotIds, getMultiViewRoot} from '../visualize/MultiViewCntlr.js'; +import {META_VIEWER_ID, FITS_VIEWER_ID} from '../visualize/ui/TriViewImageSection.jsx'; +import {REPLACE_IMAGES, getViewerPlotIds, getMultiViewRoot} from '../visualize/MultiViewCntlr.js'; export const LAYOUT_PATH = 'layout'; @@ -29,6 +29,15 @@ export const SET_LAYOUT_MODE = `${LAYOUT_PATH}.setLayoutMode`; export const SHOW_DROPDOWN = `${LAYOUT_PATH}.showDropDown`; +export function showDropDownCreator(action) { + var {visible=true, view} = action.payload; + if (visible && !view) { + view = get(flux.getState(), 'layout.dropDown.view') || getDropDownNames()[0]; + } + action.payload = {visible, view}; + return action; +} + /*---------------------------- Reducers ----------------------------*/ export function reducer(state={}, action={}) { @@ -43,7 +52,6 @@ export function reducer(state={}, action={}) { case SHOW_DROPDOWN : const {visible = true} = action.payload; - view = view || get(state, 'dropDown.view') || getDropDownNames()[0]; return smartMerge(state, {dropDown: {visible, view}}); default: @@ -125,10 +133,10 @@ export function* layoutManager({title, views='tables | images | xyPlots'}) { while (true) { const action = yield take([ - ImagePlotCntlr.PLOT_IMAGE, ImagePlotCntlr.DELETE_PLOT_VIEW, REPLACE_IMAGES, - TBL_RESULTS_ADDED, TABLE_REMOVE, TABLE_NEW, - SHOW_DROPDOWN, SET_LAYOUT_MODE - ]); + REPLACE_IMAGES, ImagePlotCntlr.PLOT_IMAGE, ImagePlotCntlr.DELETE_PLOT_VIEW, ImagePlotCntlr.PLOT_IMAGE_FAIL, + TBL_RESULTS_ADDED, TABLE_REMOVE, TABLE_NEW, + SHOW_DROPDOWN, SET_LAYOUT_MODE + ]); var {hasImages, hasTables, hasXyPlots, mode, ...others} = getLayouInfo(); // eslint-disable-next-line diff --git a/src/firefly/js/core/ReduxFlux.js b/src/firefly/js/core/ReduxFlux.js index 6ac705e109..517e2d4092 100644 --- a/src/firefly/js/core/ReduxFlux.js +++ b/src/firefly/js/core/ReduxFlux.js @@ -8,9 +8,9 @@ import thunkMiddleware from 'redux-thunk'; import loggerMiddleware from 'redux-logger'; import { createStore, applyMiddleware, combineReducers } from 'redux'; import * as AppDataCntlr from './AppDataCntlr.js'; +import * as LayoutCntlr from './LayoutCntlr.js'; import {recordHistory} from './History.js'; -import {LAYOUT_PATH, reducer as layoutReducer} from './LayoutCntlr.js'; -import FieldGroupCntlr, {valueChangeActionCreator,multiValueChangeActionCreator} +import FieldGroupCntlr, {valueChangeActionCreator,multiValueChangeActionCreator} from '../fieldGroup/FieldGroupCntlr.js'; import * as MouseReadoutCntlr from '../visualize/MouseReadoutCntlr.js'; import ImagePlotCntlr, {IMAGE_PLOT_KEY, @@ -85,7 +85,7 @@ const drawLayerFactory= DrawLayerFactory.makeFactory(ActiveTarget,SelectArea,Dis */ const reducers = { [AppDataCntlr.APP_DATA_PATH]: AppDataCntlr.reducer, - [LAYOUT_PATH]: layoutReducer, + [LayoutCntlr.LAYOUT_PATH]: LayoutCntlr.reducer, [FieldGroupCntlr.FIELD_GROUP_KEY]: FieldGroupCntlr.reducer, [IMAGE_PLOT_KEY]: ImagePlotCntlr.reducer, [ExternalAccessCntlr.EXTERNAL_ACCESS_KEY]: ExternalAccessCntlr.reducer, @@ -106,6 +106,7 @@ let redux = null; // pre-map a set of action => creator prior to boostraping. actionCreators.set(AppDataCntlr.APP_LOAD, AppDataCntlr.loadAppData); actionCreators.set(AppDataCntlr.HELP_LOAD, AppDataCntlr.onlineHelpLoad); +actionCreators.set(LayoutCntlr.SHOW_DROPDOWN, LayoutCntlr.showDropDownCreator); actionCreators.set(FieldGroupCntlr.VALUE_CHANGE, FieldGroupCntlr.valueChangeActionCreator); actionCreators.set(FieldGroupCntlr.MULTI_VALUE_CHANGE, multiValueChangeActionCreator); actionCreators.set(ExternalAccessCntlr.EXTENSION_ACTIVATE, ExternalAccessCntlr.extensionActivateActionCreator); diff --git a/src/firefly/js/core/reducers/MenuReducer.js b/src/firefly/js/core/reducers/MenuReducer.js index e64ce92555..59cb69b380 100644 --- a/src/firefly/js/core/reducers/MenuReducer.js +++ b/src/firefly/js/core/reducers/MenuReducer.js @@ -2,14 +2,18 @@ * License information at https://github.com/Caltech-IPAC/firefly/blob/master/License.txt */ +import {SHOW_DROPDOWN} from '../LayoutCntlr.js'; -function reducer(state={}, action={}) { +export function menuReducer(state={}, action={}) { - const menuAction = state.menuItems && state.menuItems.find( (el) => el && el.action === action.type); - const selected = menuAction ? action.type : ''; - return Object.assign({}, state, {selected}); + switch (action.type) { + case SHOW_DROPDOWN : + const {visible, view=''} = action.payload; + const selected = visible ? view : ''; + return Object.assign({}, state, {selected}); + + default: + return state; + } } -export default { - reducer -}; \ No newline at end of file diff --git a/src/firefly/js/tables/FilterInfo.js b/src/firefly/js/tables/FilterInfo.js index 4c8a4dc932..829fb4e448 100644 --- a/src/firefly/js/tables/FilterInfo.js +++ b/src/firefly/js/tables/FilterInfo.js @@ -6,9 +6,6 @@ /* * License information at https://github.com/Caltech-IPAC/firefly/blob/master/License.txt */ -import {isEmpty, merge} from 'lodash'; - - const op_regex = new RegExp('(<|>|>=|<=|=|!=|like|in)'); const cond_regex = new RegExp('^' + op_regex.source + '\\s+(.+)', 'i'); const filter_regex = new RegExp('(\\S+)\\s+' + op_regex.source + '\\s+(.+)', 'i'); @@ -25,11 +22,21 @@ export const FILTER_TTIPS = 'Valid values are one of (=, >, <, !=, >=, <=, LIKE) * col: ['op expression'] * } * use parse and serialize to object to string and vice-versa + * in this context: + * filter is column_name = conditions + * condition is operator + value(s) + * multiple conditions are separated by comma. + * multiple filters are separated by semicolon. */ export class FilterInfo { constructor() { } + /** + * parse the given filterString into a FilterInfo + * @param filterString + * @returns {FilterInfo} + */ static parse(filterString) { var filterInfo = new FilterInfo(); if (filterString) { @@ -41,15 +48,41 @@ export class FilterInfo { return filterInfo; } + /** + * given a list of conditions separated by semicolon, + * transform them into valid conditions if they are not already so. + * @param conditions + * @returns {string} + */ + static autoCorrect(conditions) { + if (conditions) { + const parts = conditions.split(';').map( (v) => { + const opVal = v.replace(/\(|\)| /g, '').split(op_regex); + var [op, val] = opVal.length > 1 ? [ opVal[1], opVal[2] ] : [ 'like', opVal[0] ]; // defualt to 'like' if no operators found + val = op.toLowerCase() === 'in' ? `(${val})` : val; // add parentheses when 'in' is used. + return `${op} ${val}`; + }); + return parts.join(';'); + } else { + return conditions; + } + } + + /** + * validate the conditions + * @param conditions + * @returns {boolean} + */ static isValid(conditions) { - return isEmpty(conditions) || conditions.split(';').reduce( (rval, v) => { + return !conditions || conditions.split(';').reduce( (rval, v) => { return rval && cond_regex.test(v.trim()); }, true); } static validator(conditions) { + conditions = FilterInfo.autoCorrect(conditions); const valid = FilterInfo.isValid(conditions); - return {valid, message: FILTER_TTIPS}; + return {valid, value: conditions, message: FILTER_TTIPS}; } serialize() { @@ -63,26 +96,36 @@ export class FilterInfo { }, ''); } + /** + * add additional conditions to the given column. + * @param colName + */ addFilter(colName, conditions) { - this[colName] = isEmpty(this[colName]) ? conditions : `${this[colName]}; ${conditions}`; + this[colName] = !this[colName] ? conditions : `${this[colName]}; ${conditions}`; } setFilter(colName, conditions) { Reflect.deleteProperty(this, colName); - if (!isEmpty(conditions)) { - conditions && conditions.split(';').forEach( (v) => { + if (conditions) { + conditions.split(';').forEach( (v) => { const parts = v.trim().match(cond_regex); if (parts.length > 2) this.addFilter(colName, `${parts[1]} ${parts[2]}`); }); } } + /** + * returns the string value of this columns filter info. + * multiple filters are separated by comma. + * @param colName + * @returns {string} + */ getFilter(colName) { return this[colName] && this[colName].toString(); } isEqual(colName, value) { const oldVal = this.getFilter(colName); - return (isEmpty(oldVal) && isEmpty(value)) || oldVal === value; + return (!oldVal && !value) || oldVal === value; } } diff --git a/src/firefly/js/tables/SelectInfo.js b/src/firefly/js/tables/SelectInfo.js index 8d96882cb4..4fdff991d5 100644 --- a/src/firefly/js/tables/SelectInfo.js +++ b/src/firefly/js/tables/SelectInfo.js @@ -109,10 +109,10 @@ export class SelectInfo { /** * Destructing of the SelectInfo's data. - * @param selectAll boolean. Indicates selectAll mode. defaults to false. - * @param exceptions Set. A set of exceptions based on selectAll mode. - * @param rowCount int. Total number of rows. defaults to zero. - * @param offset int. All indices passed into this class's funtion will be offsetted by this given value. + * @param {number=0} rowCount IMPORTANT!!. Total number of rows in the table. defaults to zero. + * @param {boolean} [selectAll] boolean. Indicates selectAll mode. defaults to false. + * @param {Set} [exceptions] A set of exceptions based on selectAll mode. + * @param {number} [offset] All indices passed into this class's funtion will be offsetted by this given value. * @returns {SelectInfo} */ static newInstance({selectAll=false, exceptions=(new Set()), rowCount=0}, offset) { diff --git a/src/firefly/js/tables/SortInfo.js b/src/firefly/js/tables/SortInfo.js index 6900cb761a..23d1da8b44 100644 --- a/src/firefly/js/tables/SortInfo.js +++ b/src/firefly/js/tables/SortInfo.js @@ -1,12 +1,6 @@ -/** - * Created by loi on 1/15/16. - */ - - /* * License information at https://github.com/Caltech-IPAC/firefly/blob/master/License.txt */ -const sortInfo_regex = new RegExp('\\s*(\\S+),(\\S+)'); export const SORT_ASC = 'ASC'; export const SORT_DESC = 'DESC'; export const UNSORTED = ''; @@ -28,7 +22,7 @@ export const UNSORTED = ''; * @returns {*} */ getDirection(colName) { - if (this.sortColumns.includes(colName)) { + if (this.sortColumns[0] === colName) { return this.direction; } else { return UNSORTED; @@ -41,7 +35,8 @@ export const UNSORTED = ''; * @returns {string} */ toggle(colName) { - const dir = this.getDirection(colName); + const name = colName.split(',')[0].trim(); + const dir = this.getDirection(name); const direction = dir === UNSORTED ? SORT_ASC : dir === SORT_ASC ? SORT_DESC : UNSORTED; const sortColumns = UNSORTED ? [] : [colName]; @@ -54,10 +49,10 @@ export const UNSORTED = ''; static parse(sortInfo) { if (sortInfo) { - const parts = sortInfo.trim().match(sortInfo_regex); + const parts = sortInfo.split(',').map((s) => s.trim()); if (parts) { - const direction = parts[1] && parts[1].toUpperCase(); - const sortColumns = parts[2] && parts[2].split(','); + const direction = parts[0] && parts[0].toUpperCase(); + const sortColumns = parts[1] && parts.slice(1); return new SortInfo(direction, sortColumns); } } else { diff --git a/src/firefly/js/tables/TableConnector.js b/src/firefly/js/tables/TableConnector.js index cd75658f99..e4a5b76fa5 100644 --- a/src/firefly/js/tables/TableConnector.js +++ b/src/firefly/js/tables/TableConnector.js @@ -2,11 +2,13 @@ * License information at https://github.com/Caltech-IPAC/firefly/blob/master/License.txt */ -import {isEmpty, omitBy, isUndefined, cloneDeep} from 'lodash'; +import {isEmpty, omitBy, isUndefined, cloneDeep, get} from 'lodash'; import {flux} from '../Firefly.js'; import * as TblCntlr from './TablesCntlr.js'; import * as TblUtil from './TableUtil.js'; import {SelectInfo} from './SelectInfo.js'; +import {FilterInfo} from './FilterInfo.js'; +import {fetchUrl} from '../util/WebUtil.js'; export class TableConnector { @@ -31,13 +33,36 @@ export class TableConnector { var {tableModel, request} = TblUtil.getTblInfoById(this.tbl_id); if (this.origTableModel) { // not implemented yet - flux.process({type: TblCntlr.TABLE_REPLACE, payload: tableModel}); } else { request = Object.assign({}, request, {filters: filterIntoString}); TblCntlr.dispatchTableFetch(request); } } + /** + * filter on the selected rows + * @param {number[]} selected array of selected row indices. + */ + onFilterSelected(selected) { + var {tableModel, request} = TblUtil.getTblInfoById(this.tbl_id); + if (this.origTableModel) { + // not implemented yet + } else { + const filterInfoCls = FilterInfo.parse(request.filters); + const filePath = get(tableModel, 'tableMeta.source'); + if (filePath) { + getRowIdFor(filePath, selected).then( (selectedRowIdAry) => { + const value = selectedRowIdAry.reduce((rv, val, idx) => { + return rv + (idx ? ',':'') + val; + }, 'IN (') + ')'; + filterInfoCls.addFilter('ROWID', value); + request = Object.assign({}, request, {filters: filterInfoCls.serialize()}); + TblCntlr.dispatchTableFetch(request); + }); + } + } + } + onPageSizeChange(nPageSize) { nPageSize = Number.parseInt(nPageSize); const {pageSize, highlightedRow} = TblUtil.getTblInfoById(this.tbl_id); @@ -105,3 +130,15 @@ export class TableConnector { return new TableConnector(tbl_id, tbl_ui_id, tableModel); } } + + +function getRowIdFor(filePath, selected) { + const params = {id: 'Table__SelectedValues', columnName: 'ROWID', filePath, selectedRows: String(selected)}; + + return fetchUrl(TblUtil.SEARCH_SRV_PATH, {method: 'post', params}).then( (response) => { + return response.json().then( (selectedRowIdAry) => { + return selectedRowIdAry; + }); + }); + +} \ No newline at end of file diff --git a/src/firefly/js/tables/TableUtil.js b/src/firefly/js/tables/TableUtil.js index fcc16eeaa5..f493ea4012 100644 --- a/src/firefly/js/tables/TableUtil.js +++ b/src/firefly/js/tables/TableUtil.js @@ -9,8 +9,8 @@ import {flux} from '../Firefly.js'; import {fetchUrl, encodeServerUrl, encodeParams} from '../util/WebUtil.js'; import {getRootPath, getRootURL} from '../util/BrowserUtil.js'; +export const SEARCH_SRV_PATH = getRootPath() + 'search/json'; const SAVE_TABLE_URL = getRootURL() + 'servlet/SaveAsIpacTable'; -const SRV_PATH = getRootPath() + 'search/json'; const INT_MAX = Math.pow(2,31) - 1; /*----------------------------< creator functions ----------------------------*/ @@ -139,7 +139,7 @@ export function doFetchTable(tableRequest, hlRowIdx) { params.META_INFO = encodeParams(params.META_INFO); } - return fetchUrl(SRV_PATH, {method: 'post', params}).then( (response) => { + return fetchUrl(SEARCH_SRV_PATH, {method: 'post', params}).then( (response) => { return response.json().then( (tableModel) => { const startIdx = get(tableModel, 'request.startIdx', 0); if (startIdx > 0) { diff --git a/src/firefly/js/tables/TablesCntlr.js b/src/firefly/js/tables/TablesCntlr.js index c32eacaa30..31adf9e4e0 100644 --- a/src/firefly/js/tables/TablesCntlr.js +++ b/src/firefly/js/tables/TablesCntlr.js @@ -21,17 +21,16 @@ export const UI_PREFIX = 'tableUi'; /*---------------------------- ACTIONS -----------------------------*/ export const TABLE_SEARCH = `${DATA_PREFIX}.search`; -export const TABLE_FETCH = `${DATA_PREFIX}.fetch`; export const TABLE_NEW = `${DATA_PREFIX}.new`; export const TABLE_NEW_LOADED = `${DATA_PREFIX}.newLoaded`; -export const TABLE_UPDATE = `${DATA_PREFIX}.update`; -export const TABLE_REPLACE = `${DATA_PREFIX}.replace`; - export const TABLE_SORT = `${DATA_PREFIX}.sort`; -export const TABLE_FILTER = `${DATA_PREFIX}.filter`; export const TABLE_REMOVE = `${DATA_PREFIX}.remove`; + +export const TABLE_FETCH = `${DATA_PREFIX}.fetch`; export const TABLE_SELECT = `${DATA_PREFIX}.select`; export const TABLE_HIGHLIGHT = `${DATA_PREFIX}.highlight`; +export const TABLE_UPDATE = `${DATA_PREFIX}.update`; +export const TABLE_REPLACE = `${DATA_PREFIX}.replace`; export const TBL_RESULTS_ADDED = `${RESULTS_PREFIX}.added`; export const TBL_RESULTS_UPDATE = `${RESULTS_PREFIX}.update`; @@ -86,16 +85,13 @@ export function tableFetch(action) { return (dispatch) => { if (!action.err) { var {request, hlRowIdx} = action.payload; - var actionType, {tbl_id} = request; - switch (action.type) { - case (TABLE_SORT) : - actionType = TABLE_REPLACE; - break; - - case (TABLE_FETCH) : + const {tbl_id} = request; + var actionType = action.type; + if (action.type === TABLE_FETCH) { actionType = TABLE_NEW; dispatchAddSaga(doOnTblLoaded, {tbl_id, callback:dispatchTableLoaded}); } + request.startIdx = 0; dispatch({type: TABLE_REPLACE, payload: {tbl_id, isFetching: true}}); TblUtil.doFetchTable(request, hlRowIdx).then ( (tableModel) => { diff --git a/src/firefly/js/tables/reducer/TableDataReducer.js b/src/firefly/js/tables/reducer/TableDataReducer.js index ed7d496df7..c01266d9a6 100644 --- a/src/firefly/js/tables/reducer/TableDataReducer.js +++ b/src/firefly/js/tables/reducer/TableDataReducer.js @@ -30,9 +30,11 @@ export function dataReducer(state={data:{}}, action={}) { case (Cntlr.TABLE_UPDATE) : return TblUtil.smartMerge(root, {[tbl_id] : action.payload}); - case (Cntlr.TABLE_NEW) : + case (Cntlr.TABLE_NEW) : + case (Cntlr.TABLE_SORT) : case (Cntlr.TABLE_REPLACE) : - const nTable = Object.assign({isFetching:false, selectInfo: SelectInfo.newInstance({}).data},action.payload); + const rowCount = action.payload.totalRows || get(action, 'payload.tableData.data.length', 0); + const nTable = Object.assign({isFetching:false, selectInfo: SelectInfo.newInstance({rowCount}).data},action.payload); return updateSet(root, [tbl_id], nTable); case (Cntlr.TABLE_REMOVE) : diff --git a/src/firefly/js/tables/reducer/TableUiReducer.js b/src/firefly/js/tables/reducer/TableUiReducer.js index df4cb23c7e..68f6a2a9a4 100644 --- a/src/firefly/js/tables/reducer/TableUiReducer.js +++ b/src/firefly/js/tables/reducer/TableUiReducer.js @@ -24,11 +24,12 @@ export function uiReducer(state={ui:{}}, action={}) { const {options} = action.payload || {}; return Object.assign(root, {[tbl_ui_id]:{tbl_ui_id, tbl_id, ...options}}); - case (Cntlr.TABLE_NEW) : - case (Cntlr.TABLE_UPDATE) : - case (Cntlr.TABLE_REPLACE): - case (Cntlr.TABLE_SELECT) : - case (Cntlr.TABLE_NEW_LOADED) : + case (Cntlr.TABLE_NEW) : + case (Cntlr.TABLE_SORT) : + case (Cntlr.TABLE_UPDATE) : + case (Cntlr.TABLE_REPLACE) : + case (Cntlr.TABLE_SELECT) : + case (Cntlr.TABLE_NEW_LOADED) : case (Cntlr.TABLE_HIGHLIGHT) : // state is in-progress(fresh) data.. use it to reduce ui state. return uiStateReducer(root, get(state, ['data', tbl_id])); diff --git a/src/firefly/js/tables/ui/BasicTableView.jsx b/src/firefly/js/tables/ui/BasicTableView.jsx index 6a5417e2d1..d9694b8133 100644 --- a/src/firefly/js/tables/ui/BasicTableView.jsx +++ b/src/firefly/js/tables/ui/BasicTableView.jsx @@ -46,7 +46,12 @@ export class BasicTableView extends React.Component { this.onColumnResizeEndCallback = this.onColumnResizeEndCallback.bind(this); this.rowClassName = this.rowClassName.bind(this); - this.onKeyDown = this.onKeyDown.bind(this); + this.onKeyDown = this.onKeyDown.bind(this); + this.onRowSelect = this.onRowSelect.bind(this); + this.onSelectAll = this.onSelectAll.bind(this); + this.onSort = this.onSort.bind(this); + this.onFilter = this.onFilter.bind(this); + this.onFilterSelected = this.onFilterSelected.bind(this); } onColumnResizeEndCallback(newColumnWidth, columnKey) { @@ -90,36 +95,57 @@ export class BasicTableView extends React.Component { e.preventDefault && e.preventDefault(); } } + onFilterSelected() { + const {callbacks, selectInfoCls} = this.props; + if (callbacks.onFilterSelected) { + const selected = [...selectInfoCls.getSelected()]; + callbacks.onFilterSelected(selected); + } + } + + onFilter({fieldKey, valid, value}) { + const {callbacks, filterInfo} = this.props; + if (callbacks.onFilter) { + const filterInfoCls = FilterInfo.parse(filterInfo); + if (valid && !filterInfoCls.isEqual(fieldKey, value)) { + filterInfoCls.setFilter(fieldKey, value); + callbacks.onFilter(filterInfoCls.serialize()); + } + } + }; + + onSort(cname) { + const {callbacks, sortInfo} = this.props; + if (callbacks.onSort) { + const sortInfoCls = SortInfo.parse(sortInfo); + callbacks.onSort(sortInfoCls.toggle(cname)); + } + }; + + onSelectAll(checked) { + const {callbacks} = this.props; + callbacks.onSelectAll && callbacks.onSelectAll(checked); + } + + onRowSelect(checked, rowIndex) { + const {callbacks} = this.props; + callbacks.onRowSelect && callbacks.onRowSelect(checked, rowIndex); + } render() { - const {columns, data, hlRowIdx, showUnits, showFilters, filterInfo, - sortInfo, callbacks, textView, rowHeight, showMask} = this.props; + const {columns, data, hlRowIdx, showUnits, showFilters, filterInfo, renderers, + selectable, selectInfoCls, sortInfo, callbacks, textView, rowHeight, showMask} = this.props; const {widthPx, heightPx, columnWidths} = this.state; + const {onSort, onFilter, onRowSelect, onSelectAll, onFilterSelected} = this; if (isEmpty(columns)) return (
    ); - const filterInfoCls = FilterInfo.parse(filterInfo); - const sortInfoCls = SortInfo.parse(sortInfo); - - const onRowSelect = (checked, rowIndex) => callbacks.onRowSelect && callbacks.onRowSelect(checked, rowIndex); - const onSelectAll = (checked) => callbacks.onSelectAll && callbacks.onSelectAll(checked); - const onSort = (cname) => { - if (callbacks.onSort) { - callbacks.onSort(sortInfoCls.toggle(cname)); - } - }; - - const onFilter = ({fieldKey, valid, value}) => { - if (callbacks.onFilter) { - if (valid && !filterInfoCls.isEqual(fieldKey, value)) { - filterInfoCls.setFilter(fieldKey, value); - callbacks.onFilter(filterInfoCls.serialize()); - } - } - }; - - const colProps = pick(this.props, ['columns', 'data', 'selectable', 'selectInfoCls', 'callbacks', 'renderers']); - Object.assign(colProps, {columnWidths, filterInfoCls, sortInfoCls, showUnits, showFilters}, {onSort, onFilter, onRowSelect, onSelectAll}); + // const filterInfoCls = FilterInfo.parse(filterInfo); + // const sortInfoCls = SortInfo.parse(sortInfo); + // + const makeColumnsProps = {columns, data, selectable, selectInfoCls, renderers, + columnWidths, filterInfo, sortInfo, showUnits, showFilters, + onSort, onFilter, onRowSelect, onSelectAll, onFilterSelected}; const headerHeight = 22 + (showUnits && 12) + (showFilters && 20); return ( @@ -137,7 +163,7 @@ export class BasicTableView extends React.Component { scrollToRow={hlRowIdx} width={widthPx} height={heightPx}> - { makeColumns(colProps) } + { makeColumns(makeColumnsProps) } } {showMask &&
    } @@ -187,6 +213,9 @@ BasicTableView.defaultProps = { currentPage: -1 }; + +// components here on down are private. not all props are defined. +/* eslint-disable react/prop-types */ const TextView = ({columns, data, showUnits, widthPx, heightPx}) => { const text = tableToText(columns, data, showUnits); return ( @@ -198,13 +227,13 @@ const TextView = ({columns, data, showUnits, widthPx, heightPx}) => { ); }; -function makeColWidth(columns, data, showUnits) { - return !columns ? {} : columns.reduce((widths, col, cidx) => { +function makeColWidth(columns, showUnits) { + return !columns ? {} : columns.reduce((widths, col) => { const label = col.name; var nchar = col.prefWidth; const unitLength = showUnits ? get(col, 'units.length', 0) : 0; if (!nchar) { - nchar = Math.max(label.length+2, unitLength+2, get(data, `0.${cidx}.length`, 0)); // 2 is for padding and sort symbol + nchar = Math.max(label.length+2, unitLength+2, get(col,'width', 0)); // 2 is for padding and sort symbol } widths[col.name] = nchar * 8; return widths; @@ -212,7 +241,7 @@ function makeColWidth(columns, data, showUnits) { } function makeColumns ({columns, columnWidths, data, selectable, showUnits, showFilters, renderers, - selectInfoCls, filterInfoCls, sortInfoCls, onRowSelect, onSelectAll, onSort, onFilter}) { + selectInfoCls, filterInfo, sortInfo, onRowSelect, onSelectAll, onSort, onFilter, onFilterSelected}) { if (!columns) return false; var colsEl = columns.map((col, idx) => { @@ -224,7 +253,7 @@ function makeColumns ({columns, columnWidths, data, selectable, showUnits, showF } + header={} cell={} fixed={false} width={columnWidths[col.name]} @@ -234,15 +263,16 @@ function makeColumns ({columns, columnWidths, data, selectable, showUnits, showF ); }); if (selectable) { - var cbox = } + header={} cell={} fixed={true} width={25} allowCellsRecycling={true} - />; + />); colsEl.splice(0, 0, cbox); } return colsEl; diff --git a/src/firefly/js/tables/ui/TablePanelOptions.jsx b/src/firefly/js/tables/ui/TablePanelOptions.jsx index da22eca61a..42a4e83508 100644 --- a/src/firefly/js/tables/ui/TablePanelOptions.jsx +++ b/src/firefly/js/tables/ui/TablePanelOptions.jsx @@ -88,7 +88,7 @@ function prepareOptionData(columns, colSortDir) { var cols = [{name: 'Column', visibility: 'show', prefWidth: 20}]; const sortInfo = SortInfo.newInstance(colSortDir, 'Column').serialize(); - var selectInfoCls = SelectInfo.newInstance({}); + var selectInfoCls = SelectInfo.newInstance({rowCount: data.length}); selectInfoCls.data.rowCount = data.length; columns.forEach( (v, idx) => { selectInfoCls.setRowSelect(idx, get(v, 'visibility', 'show') === 'show'); diff --git a/src/firefly/js/tables/ui/TableRenderer.js b/src/firefly/js/tables/ui/TableRenderer.js index ea802b0b75..8d18c423ec 100644 --- a/src/firefly/js/tables/ui/TableRenderer.js +++ b/src/firefly/js/tables/ui/TableRenderer.js @@ -1,52 +1,73 @@ -/** - * Created by loi on 3/17/16. +/* + * License information at https://github.com/Caltech-IPAC/firefly/blob/master/License.txt */ import React from 'react'; import FixedDataTable from 'fixed-data-table'; -import {set, get, omit, isEqual} from 'lodash'; +import sCompare from 'react-addons-shallow-compare'; +import {set, get, isEqual, pick} from 'lodash'; import {FilterInfo, FILTER_TTIPS} from '../FilterInfo.js'; +import {SortInfo} from '../SortInfo.js'; import {InputField} from '../../ui/InputField.jsx'; import {SORT_ASC, UNSORTED} from '../SortInfo'; +import {toBoolean} from '../../util/WebUtil.js'; import ASC_ICO from 'html/images/sort_asc.gif'; import DESC_ICO from 'html/images/sort_desc.gif'; -// import {deepDiff} from '../../util/WebUtil.js'; +import FILTER_SELECTED_ICO from 'html/images/icons-2014/16x16_Filter.png'; const {Cell} = FixedDataTable; +// the components here are small and used by table only. not all props are defined. +/* eslint-disable react/prop-types */ +/*---------------------------- COLUMN HEADER RENDERERS ----------------------------*/ +function Label({sortable, title, name, sortByCols, sortInfoCls, onSort}) { + const sortDir = sortInfoCls.getDirection(name); + sortByCols = sortByCols || name; -const SortSymbol = ({sortDir}) => { + if (toBoolean(sortable, true)) { + return ( +
    onSort(sortByCols)} >{title || name} + { sortDir!==UNSORTED && } +
    + ); + } else { + return
    {title || name}
    ; + } +} + +function SortSymbol({sortDir}) { return ; }; -/*---------------------------- COLUMN HEADER RENDERERS ----------------------------*/ - export class HeaderCell extends React.Component { constructor(props) { super(props); } shouldComponentUpdate(nProps, nState) { - const excludes = ['onSort', 'onFilter']; - return !isEqual(omit(nProps, excludes), omit(this.props, excludes)); + return sCompare(this, nProps, nState); } + // componentDidUpdate(prevProps, prevState) { + // deepDiff({props: prevProps, state: prevState}, + // {props: this.props, state: this.state}, + // this.constructor.name); + // } + // render() { - // eslint-disable-next-line - const {col, showUnits, showFilters, filterInfoCls, sortInfoCls, onSort, onFilter} = this.props; + const {col, showUnits, showFilters, filterInfo, sortInfo, onSort, onFilter} = this.props; const cname = col.name; const cdesc = col.desc || col.title || cname; - const sortDir = sortInfoCls.getDirection(cname); const style = {width: '100%', boxSizing: 'border-box'}; + const filterInfoCls = FilterInfo.parse(filterInfo); + const sortInfoCls = SortInfo.parse(sortInfo); return (
    -
    onSort(cname)} >{col.title || cname} - { sortDir!==UNSORTED && } -
    +