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;
+}
+