Implemented preview expression dialog.

git-svn-id: http://google-refine.googlecode.com/svn/trunk@31 7d457c2a-affb-35e4-300a-418c747d4874
This commit is contained in:
David Huynh 2010-02-03 09:27:53 +00:00
parent a123b0840e
commit fb34d6f507
7 changed files with 266 additions and 21 deletions

View File

@ -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

View File

@ -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");

View File

@ -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);
}
}
}

View File

@ -1 +1 @@
<html> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8"> <title>Gridlock</title> <link type="text/css" rel="stylesheet" href="http://freebaselibs.com/static/suggest/1.0.3/suggest.min.css" /> <link type="text/css" rel="stylesheet" href="externals/jquery-ui/css/ui-lightness/jquery-ui-1.7.2.custom.css" /> <link rel="stylesheet" href="/styles/common.css" /> <link rel="stylesheet" href="/styles/project.css" /> <link rel="stylesheet" href="/styles/history.css" /> <link rel="stylesheet" href="/styles/browsing.css" /> <link rel="stylesheet" href="/styles/process.css" /> <script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.4/jquery.min.js"></script> <script type="text/javascript" src="http://freebaselibs.com/static/suggest/1.0.3/suggest.min.js"></script> <script type="text/javascript" src="externals/jquery-ui/jquery-ui-1.7.2.custom.min.js"></script> <script type="text/javascript" src="scripts/util/misc.js"></script> <script type="text/javascript" src="scripts/util/url.js"></script> <script type="text/javascript" src="scripts/util/string.js"></script> <script type="text/javascript" src="scripts/util/ajax.js"></script> <script type="text/javascript" src="scripts/util/menu.js"></script> <script type="text/javascript" src="scripts/util/dialog.js"></script> <script type="text/javascript" src="scripts/project.js"></script> <script type="text/javascript" src="scripts/project/list-facet.js"></script> <script type="text/javascript" src="scripts/project/range-facet.js"></script> <script type="text/javascript" src="scripts/project/text-search-facet.js"></script> <script type="text/javascript" src="scripts/project/browsing-engine.js"></script> <script type="text/javascript" src="scripts/project/data-table-view.js"></script> <script type="text/javascript" src="scripts/project/history-widget.js"></script> <script type="text/javascript" src="scripts/project/process-widget.js"></script> <script type="text/javascript" src="scripts/project/recon-dialog.js"></script> </head> <body> <div id="header"> <h1 id="title"><a href="./index.html">Gridworks</a> &raquo; </h1> </div> <div id="body"> Loading ... </div> </body> </html>
<html> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8"> <title>Gridlock</title> <link type="text/css" rel="stylesheet" href="http://freebaselibs.com/static/suggest/1.0.3/suggest.min.css" /> <link type="text/css" rel="stylesheet" href="externals/jquery-ui/css/ui-lightness/jquery-ui-1.7.2.custom.css" /> <link rel="stylesheet" href="/styles/common.css" /> <link rel="stylesheet" href="/styles/project.css" /> <link rel="stylesheet" href="/styles/history.css" /> <link rel="stylesheet" href="/styles/browsing.css" /> <link rel="stylesheet" href="/styles/process.css" /> <link rel="stylesheet" href="/styles/expression-preview-dialog.css" /> <script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.4/jquery.min.js"></script> <script type="text/javascript" src="http://freebaselibs.com/static/suggest/1.0.3/suggest.min.js"></script> <script type="text/javascript" src="externals/jquery-ui/jquery-ui-1.7.2.custom.min.js"></script> <script type="text/javascript" src="scripts/util/misc.js"></script> <script type="text/javascript" src="scripts/util/url.js"></script> <script type="text/javascript" src="scripts/util/string.js"></script> <script type="text/javascript" src="scripts/util/ajax.js"></script> <script type="text/javascript" src="scripts/util/menu.js"></script> <script type="text/javascript" src="scripts/util/dialog.js"></script> <script type="text/javascript" src="scripts/project.js"></script> <script type="text/javascript" src="scripts/project/list-facet.js"></script> <script type="text/javascript" src="scripts/project/range-facet.js"></script> <script type="text/javascript" src="scripts/project/text-search-facet.js"></script> <script type="text/javascript" src="scripts/project/browsing-engine.js"></script> <script type="text/javascript" src="scripts/project/data-table-view.js"></script> <script type="text/javascript" src="scripts/project/history-widget.js"></script> <script type="text/javascript" src="scripts/project/process-widget.js"></script> <script type="text/javascript" src="scripts/project/recon-dialog.js"></script> <script type="text/javascript" src="scripts/project/expression-preview-dialog.js"></script> </head> <body> <div id="header"> <h1 id="title"><a href="./index.html">Gridworks</a> &raquo; </h1> </div> <div id="body"> Loading ... </div> </body> </html>

View File

@ -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,7 +526,13 @@ DataTableView.prototype._createMenuForColumnHeader = function(column, index, elm
], elmt);
};
DataTableView.prototype._doFilterByExpression = function(column, 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",
{
@ -545,6 +541,8 @@ DataTableView.prototype._doFilterByExpression = function(column, expression) {
"expression" : expression
}
);
}
);
};
DataTableView.prototype._createUpdateFunction = function() {
@ -576,6 +574,49 @@ DataTableView.prototype._doTextTransform = function(column, 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) {
this._doPostThenUpdate(
"discard-reconcile",

View File

@ -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 = $('<div></div>').addClass("dialog-header").text(title).appendTo(frame);
var body = $('<div></div>').addClass("dialog-body").appendTo(frame);
var footer = $('<div></div>').addClass("dialog-footer").appendTo(frame);
$('<p></p>').text("Expression:").appendTo(body);
this._input = $('<input />').width("400px").keypress(function(){
self._scheduleUpdate();
}).appendTo($('<p></p>').appendTo(body));
this._preview = $('<div></div>').addClass("expression-preview-container").appendTo(body);
$('<button></button>').html("&nbsp;&nbsp;OK&nbsp;&nbsp;").click(function() {
DialogSystem.dismissUntil(level - 1);
self._onDone(self._expression);
}).appendTo(footer);
$('<button></button>').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 = $('<table></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);
}
}
}
};

View File

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