diff --git a/src/main/java/com/metaweb/gridworks/GridworksServlet.java b/src/main/java/com/metaweb/gridworks/GridworksServlet.java index b51dd4801..d10418b75 100644 --- a/src/main/java/com/metaweb/gridworks/GridworksServlet.java +++ b/src/main/java/com/metaweb/gridworks/GridworksServlet.java @@ -27,6 +27,7 @@ import com.metaweb.gridworks.commands.GetHistoryCommand; import com.metaweb.gridworks.commands.GetProcessesCommand; import com.metaweb.gridworks.commands.GetProjectMetadataCommand; import com.metaweb.gridworks.commands.GetRowsCommand; +import com.metaweb.gridworks.commands.PreviewExpressionCommand; import com.metaweb.gridworks.commands.ReconcileCommand; import com.metaweb.gridworks.commands.UndoRedoCommand; @@ -54,6 +55,8 @@ public class GridworksServlet extends HttpServlet { _commands.put("approve-reconcile", new ApproveReconcileCommand()); _commands.put("approve-new-reconcile", new ApproveNewReconcileCommand()); _commands.put("discard-reconcile", new DiscardReconcileCommand()); + + _commands.put("preview-expression", new PreviewExpressionCommand()); } @Override diff --git a/src/main/java/com/metaweb/gridworks/commands/Command.java b/src/main/java/com/metaweb/gridworks/commands/Command.java index 8d4255417..ef7d4f2d5 100644 --- a/src/main/java/com/metaweb/gridworks/commands/Command.java +++ b/src/main/java/com/metaweb/gridworks/commands/Command.java @@ -15,6 +15,7 @@ import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.apache.commons.lang.NotImplementedException; +import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; import org.json.JSONTokener; @@ -163,6 +164,12 @@ public abstract class Command { return o; } + protected JSONArray jsonStringToArray(String s) throws JSONException { + JSONTokener t = new JSONTokener(s); + JSONArray a = (JSONArray) t.nextValue(); + return a; + } + protected Engine getEngine(HttpServletRequest request, Project project) throws Exception { Engine engine = new Engine(project); String json = request.getParameter("engine"); diff --git a/src/main/java/com/metaweb/gridworks/commands/PreviewExpressionCommand.java b/src/main/java/com/metaweb/gridworks/commands/PreviewExpressionCommand.java new file mode 100644 index 000000000..6410d4514 --- /dev/null +++ b/src/main/java/com/metaweb/gridworks/commands/PreviewExpressionCommand.java @@ -0,0 +1,80 @@ +package com.metaweb.gridworks.commands; + +import java.io.IOException; +import java.util.Properties; + +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.json.JSONArray; +import org.json.JSONWriter; + + +import com.metaweb.gridworks.expr.Evaluable; +import com.metaweb.gridworks.expr.Parser; +import com.metaweb.gridworks.model.Cell; +import com.metaweb.gridworks.model.Project; +import com.metaweb.gridworks.model.Row; + +public class PreviewExpressionCommand extends Command { + + @Override + public void doPost(HttpServletRequest request, HttpServletResponse response) + throws ServletException, IOException { + + try { + Project project = getProject(request); + + int cellIndex = Integer.parseInt(request.getParameter("cellIndex")); + + String expression = request.getParameter("expression"); + Evaluable eval = new Parser(expression).getExpression(); + + String rowIndicesString = request.getParameter("rowIndices"); + if (rowIndicesString == null) { + respond(response, "{ \"code\" : \"error\", \"message\" : \"No row indices specified\" }"); + return; + } + + JSONArray rowIndices = jsonStringToArray(rowIndicesString); + int length = rowIndices.length(); + + JSONWriter writer = new JSONWriter(response.getWriter()); + + writer.object(); + writer.key("code"); writer.value("ok"); + writer.key("results"); writer.array(); + + Properties bindings = new Properties(); + bindings.put("project", project); + for (int i = 0; i < length; i++) { + Object result = null; + + int rowIndex = rowIndices.getInt(i); + if (rowIndex >= 0 && rowIndex < project.rows.size()) { + Row row = project.rows.get(rowIndex); + if (cellIndex < row.cells.size()) { + Cell cell = row.cells.get(cellIndex); + if (cell.value != null) { + bindings.put("cell", cell); + bindings.put("value", cell.value); + + try { + result = eval.evaluate(bindings); + } catch (Exception e) { + // ignore + } + } + } + } + + writer.value(result); + } + writer.endArray(); + writer.endObject(); + } catch (Exception e) { + respondException(response, e); + } + } +} diff --git a/src/main/webapp/project.html b/src/main/webapp/project.html index a9b588ecf..db88ff4c9 100644 --- a/src/main/webapp/project.html +++ b/src/main/webapp/project.html @@ -1 +1 @@ - Gridlock
Loading ...
\ No newline at end of file + Gridlock
Loading ...
\ No newline at end of file diff --git a/src/main/webapp/scripts/project/data-table-view.js b/src/main/webapp/scripts/project/data-table-view.js index cc52ddb0f..c61788b82 100644 --- a/src/main/webapp/scripts/project/data-table-view.js +++ b/src/main/webapp/scripts/project/data-table-view.js @@ -278,12 +278,7 @@ DataTableView.prototype._createMenuForColumnHeader = function(column, index, elm }, { label: "Custom Text Facet ...", - click: function() { - var expression = window.prompt("Enter expression", 'value'); - if (expression != null) { - self._doFilterByExpression(column, expression); - } - } + click: function() { self._doFilterByExpressionPrompt(column); } }, {}, { @@ -489,12 +484,7 @@ DataTableView.prototype._createMenuForColumnHeader = function(column, index, elm {}, { label: "Custom Expression ...", - click: function() { - var expression = window.prompt("Enter expression", 'replace(value, "", "")'); - if (expression != null) { - self._doTextTransform(column, expression); - } - } + click: function() { self._doTextTransformPrompt(column); } } ] }, @@ -536,13 +526,21 @@ DataTableView.prototype._createMenuForColumnHeader = function(column, index, elm ], elmt); }; -DataTableView.prototype._doFilterByExpression = function(column, expression) { - ui.browsingEngine.addFacet( - "list", - { - "name" : column.headerLabel + ": " + expression, - "cellIndex" : column.cellIndex, - "expression" : expression +DataTableView.prototype._doFilterByExpressionPrompt = function(column, expression) { + var self = this; + DataTableView.promptExpressionOnVisibleRows( + column, + "Custom Filter on " + column.headerLabel, + "value", + function(expression) { + ui.browsingEngine.addFacet( + "list", + { + "name" : column.headerLabel + ": " + expression, + "cellIndex" : column.cellIndex, + "expression" : expression + } + ); } ); }; @@ -573,7 +571,50 @@ DataTableView.prototype._doTextTransform = function(column, expression) { this._doPostThenUpdate( "do-text-transform", { cell: column.cellIndex, expression: expression } - ); + ); +}; + +DataTableView.prototype._doTextTransformPrompt = function(column) { + var self = this; + DataTableView.promptExpressionOnVisibleRows( + column, + "Custom Transform on " + column.headerLabel, + "value", + function(expression) { + self._doTextTransform(column, expression); + } + ); +}; + +DataTableView.promptExpressionOnVisibleRows = function(column, title, expression, onDone) { + var rowIndices = []; + var values = []; + + var rows = theProject.rowModel.rows; + for (var r = 0; r < rows.length; r++) { + var row = rows[r]; + + rowIndices.push(row.i); + + var v = null; + if (column.cellIndex < row.cells.length) { + var cell = row.cells[column.cellIndex]; + if (cell != null) { + v = cell.v; + } + } + values.push(v); + } + + var self = this; + new ExpressionPreviewDialog( + title, + column.cellIndex, + rowIndices, + values, + expression, + onDone + ); }; DataTableView.prototype._doDiscardReconResults = function(column) { diff --git a/src/main/webapp/scripts/project/expression-preview-dialog.js b/src/main/webapp/scripts/project/expression-preview-dialog.js new file mode 100644 index 000000000..f84501894 --- /dev/null +++ b/src/main/webapp/scripts/project/expression-preview-dialog.js @@ -0,0 +1,97 @@ +function ExpressionPreviewDialog(title, cellIndex, rowIndices, values, expression, onDone) { + this._cellIndex = cellIndex; + this._rowIndices = rowIndices; + this._values = values; + this._results = null; + + this._expression = expression; + this._onDone = onDone; + + this._timerID = null; + this._createDialog(title); +} + +ExpressionPreviewDialog.prototype._createDialog = function(title) { + var self = this; + var frame = DialogSystem.createDialog(); + frame.width("600px"); + + var header = $('
').addClass("dialog-header").text(title).appendTo(frame); + var body = $('
').addClass("dialog-body").appendTo(frame); + var footer = $('
').addClass("dialog-footer").appendTo(frame); + + $('

').text("Expression:").appendTo(body); + + this._input = $('').width("400px").keypress(function(){ + self._scheduleUpdate(); + }).appendTo($('

').appendTo(body)); + this._preview = $('
').addClass("expression-preview-container").appendTo(body); + + $('').html("  OK  ").click(function() { + DialogSystem.dismissUntil(level - 1); + self._onDone(self._expression); + }).appendTo(footer); + + $('').text("Cancel").click(function() { + DialogSystem.dismissUntil(level - 1); + }).appendTo(footer); + + var level = DialogSystem.showDialog(frame); + + this._input[0].value = this._expression; + this._input[0].focus(); + this._renderPreview(this._expression); +}; + +ExpressionPreviewDialog.prototype._scheduleUpdate = function() { + if (this._timerID != null) { + window.clearTimeout(this._timerID); + } + var self = this; + this._timerID = window.setTimeout(function() { self._update(); }, 300); +}; + +ExpressionPreviewDialog.prototype._update = function() { + var self = this; + var expression = this._expression = $.trim(this._input[0].value); + + $.post( + "/command/preview-expression?" + $.param({ project: theProject.id, expression: expression, cellIndex: this._cellIndex }), + { + rowIndices: JSON.stringify(this._rowIndices) + }, + function(data) { + if (data.code != "error") { + self._results = data.results; + } else { + self._results = null; + } + self._renderPreview(expression); + }, + "json" + ); +}; + +ExpressionPreviewDialog.prototype._renderPreview = function(expression) { + var container = this._preview.empty(); + + var table = $('
').appendTo(container)[0]; + var tr = table.insertRow(0); + $(tr.insertCell(0)).addClass("expression-preview-heading").text("value"); + $(tr.insertCell(1)).addClass("expression-preview-heading").text(expression); + + for (var i = 0; i < this._values.length; i++) { + var tr = table.insertRow(table.rows.length); + + $(tr.insertCell(0)).html(this._values[i]); + if (this._results != null) { + var v = this._results[i]; + if (v != null) { + if ($.isArray(v)) { + v = JSON.stringify(v); + } + $(tr.insertCell(1)).html(v); + } + } + } +}; \ No newline at end of file diff --git a/src/main/webapp/styles/expression-preview-dialog.css b/src/main/webapp/styles/expression-preview-dialog.css new file mode 100644 index 000000000..1b8e93669 --- /dev/null +++ b/src/main/webapp/styles/expression-preview-dialog.css @@ -0,0 +1,17 @@ +.expression-preview-container { + border: 1px solid #ccc; + height: 300px; + overflow: auto; +} + +.expression-preview-container td { + padding: 2px 5px; + border-top: 1px solid #ccc; +} + +td.expression-preview-heading { + border-top: none; + background: #ddd; + font-weight: bold; +} +