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:
parent
a123b0840e
commit
fb34d6f507
@ -27,6 +27,7 @@ import com.metaweb.gridworks.commands.GetHistoryCommand;
|
|||||||
import com.metaweb.gridworks.commands.GetProcessesCommand;
|
import com.metaweb.gridworks.commands.GetProcessesCommand;
|
||||||
import com.metaweb.gridworks.commands.GetProjectMetadataCommand;
|
import com.metaweb.gridworks.commands.GetProjectMetadataCommand;
|
||||||
import com.metaweb.gridworks.commands.GetRowsCommand;
|
import com.metaweb.gridworks.commands.GetRowsCommand;
|
||||||
|
import com.metaweb.gridworks.commands.PreviewExpressionCommand;
|
||||||
import com.metaweb.gridworks.commands.ReconcileCommand;
|
import com.metaweb.gridworks.commands.ReconcileCommand;
|
||||||
import com.metaweb.gridworks.commands.UndoRedoCommand;
|
import com.metaweb.gridworks.commands.UndoRedoCommand;
|
||||||
|
|
||||||
@ -54,6 +55,8 @@ public class GridworksServlet extends HttpServlet {
|
|||||||
_commands.put("approve-reconcile", new ApproveReconcileCommand());
|
_commands.put("approve-reconcile", new ApproveReconcileCommand());
|
||||||
_commands.put("approve-new-reconcile", new ApproveNewReconcileCommand());
|
_commands.put("approve-new-reconcile", new ApproveNewReconcileCommand());
|
||||||
_commands.put("discard-reconcile", new DiscardReconcileCommand());
|
_commands.put("discard-reconcile", new DiscardReconcileCommand());
|
||||||
|
|
||||||
|
_commands.put("preview-expression", new PreviewExpressionCommand());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -15,6 +15,7 @@ import javax.servlet.http.HttpServletRequest;
|
|||||||
import javax.servlet.http.HttpServletResponse;
|
import javax.servlet.http.HttpServletResponse;
|
||||||
|
|
||||||
import org.apache.commons.lang.NotImplementedException;
|
import org.apache.commons.lang.NotImplementedException;
|
||||||
|
import org.json.JSONArray;
|
||||||
import org.json.JSONException;
|
import org.json.JSONException;
|
||||||
import org.json.JSONObject;
|
import org.json.JSONObject;
|
||||||
import org.json.JSONTokener;
|
import org.json.JSONTokener;
|
||||||
@ -163,6 +164,12 @@ public abstract class Command {
|
|||||||
return o;
|
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 {
|
protected Engine getEngine(HttpServletRequest request, Project project) throws Exception {
|
||||||
Engine engine = new Engine(project);
|
Engine engine = new Engine(project);
|
||||||
String json = request.getParameter("engine");
|
String json = request.getParameter("engine");
|
||||||
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -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> » </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> » </h1>
</div>
<div id="body">
Loading ...
</div>
</body>
</html>
|
@ -278,12 +278,7 @@ DataTableView.prototype._createMenuForColumnHeader = function(column, index, elm
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: "Custom Text Facet ...",
|
label: "Custom Text Facet ...",
|
||||||
click: function() {
|
click: function() { self._doFilterByExpressionPrompt(column); }
|
||||||
var expression = window.prompt("Enter expression", 'value');
|
|
||||||
if (expression != null) {
|
|
||||||
self._doFilterByExpression(column, expression);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
{},
|
{},
|
||||||
{
|
{
|
||||||
@ -489,12 +484,7 @@ DataTableView.prototype._createMenuForColumnHeader = function(column, index, elm
|
|||||||
{},
|
{},
|
||||||
{
|
{
|
||||||
label: "Custom Expression ...",
|
label: "Custom Expression ...",
|
||||||
click: function() {
|
click: function() { self._doTextTransformPrompt(column); }
|
||||||
var expression = window.prompt("Enter expression", 'replace(value, "", "")');
|
|
||||||
if (expression != null) {
|
|
||||||
self._doTextTransform(column, expression);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
@ -536,13 +526,21 @@ DataTableView.prototype._createMenuForColumnHeader = function(column, index, elm
|
|||||||
], elmt);
|
], elmt);
|
||||||
};
|
};
|
||||||
|
|
||||||
DataTableView.prototype._doFilterByExpression = function(column, expression) {
|
DataTableView.prototype._doFilterByExpressionPrompt = function(column, expression) {
|
||||||
ui.browsingEngine.addFacet(
|
var self = this;
|
||||||
"list",
|
DataTableView.promptExpressionOnVisibleRows(
|
||||||
{
|
column,
|
||||||
"name" : column.headerLabel + ": " + expression,
|
"Custom Filter on " + column.headerLabel,
|
||||||
"cellIndex" : column.cellIndex,
|
"value",
|
||||||
"expression" : expression
|
function(expression) {
|
||||||
|
ui.browsingEngine.addFacet(
|
||||||
|
"list",
|
||||||
|
{
|
||||||
|
"name" : column.headerLabel + ": " + expression,
|
||||||
|
"cellIndex" : column.cellIndex,
|
||||||
|
"expression" : expression
|
||||||
|
}
|
||||||
|
);
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
@ -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) {
|
DataTableView.prototype._doDiscardReconResults = function(column) {
|
||||||
this._doPostThenUpdate(
|
this._doPostThenUpdate(
|
||||||
"discard-reconcile",
|
"discard-reconcile",
|
||||||
|
97
src/main/webapp/scripts/project/expression-preview-dialog.js
Normal file
97
src/main/webapp/scripts/project/expression-preview-dialog.js
Normal 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(" OK ").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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
17
src/main/webapp/styles/expression-preview-dialog.css
Normal file
17
src/main/webapp/styles/expression-preview-dialog.css
Normal 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;
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user