Faceted browsing is starting to work.
git-svn-id: http://google-refine.googlecode.com/svn/trunk@13 7d457c2a-affb-35e4-300a-418c747d4874
This commit is contained in:
parent
dce2ec71aa
commit
e24d40c3da
@ -15,6 +15,7 @@ import org.json.JSONObject;
|
|||||||
import org.json.JSONTokener;
|
import org.json.JSONTokener;
|
||||||
|
|
||||||
import com.metaweb.gridlock.commands.Command;
|
import com.metaweb.gridlock.commands.Command;
|
||||||
|
import com.metaweb.gridlock.commands.ComputeFacetsCommand;
|
||||||
import com.metaweb.gridlock.commands.CreateProjectFromUploadCommand;
|
import com.metaweb.gridlock.commands.CreateProjectFromUploadCommand;
|
||||||
import com.metaweb.gridlock.commands.DoTextTransformCommand;
|
import com.metaweb.gridlock.commands.DoTextTransformCommand;
|
||||||
import com.metaweb.gridlock.commands.GetColumnModelCommand;
|
import com.metaweb.gridlock.commands.GetColumnModelCommand;
|
||||||
@ -34,6 +35,7 @@ public class GridlockServlet extends HttpServlet {
|
|||||||
_commands.put("get-column-model", new GetColumnModelCommand());
|
_commands.put("get-column-model", new GetColumnModelCommand());
|
||||||
_commands.put("get-rows", new GetRowsCommand());
|
_commands.put("get-rows", new GetRowsCommand());
|
||||||
_commands.put("get-history", new GetHistoryCommand());
|
_commands.put("get-history", new GetHistoryCommand());
|
||||||
|
_commands.put("compute-facets", new ComputeFacetsCommand());
|
||||||
_commands.put("undo-redo", new UndoRedoCommand());
|
_commands.put("undo-redo", new UndoRedoCommand());
|
||||||
_commands.put("do-text-transform", new DoTextTransformCommand());
|
_commands.put("do-text-transform", new DoTextTransformCommand());
|
||||||
}
|
}
|
||||||
|
@ -11,6 +11,7 @@ import org.json.JSONObject;
|
|||||||
|
|
||||||
import com.metaweb.gridlock.browsing.facets.Facet;
|
import com.metaweb.gridlock.browsing.facets.Facet;
|
||||||
import com.metaweb.gridlock.browsing.facets.ListFacet;
|
import com.metaweb.gridlock.browsing.facets.ListFacet;
|
||||||
|
import com.metaweb.gridlock.browsing.filters.RowFilter;
|
||||||
import com.metaweb.gridlock.model.Project;
|
import com.metaweb.gridlock.model.Project;
|
||||||
|
|
||||||
public class Engine {
|
public class Engine {
|
||||||
@ -29,7 +30,10 @@ public class Engine {
|
|||||||
ConjunctiveFilteredRows cfr = new ConjunctiveFilteredRows();
|
ConjunctiveFilteredRows cfr = new ConjunctiveFilteredRows();
|
||||||
for (Facet facet : _facets) {
|
for (Facet facet : _facets) {
|
||||||
if (facet != except) {
|
if (facet != except) {
|
||||||
cfr.add(facet.getRowFilter());
|
RowFilter rowFilter = facet.getRowFilter();
|
||||||
|
if (rowFilter != null) {
|
||||||
|
cfr.add(rowFilter);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return cfr;
|
return cfr;
|
||||||
@ -53,7 +57,7 @@ public class Engine {
|
|||||||
|
|
||||||
for (int i = 0; i < length; i++) {
|
for (int i = 0; i < length; i++) {
|
||||||
JSONObject fo = a.getJSONObject(i);
|
JSONObject fo = a.getJSONObject(i);
|
||||||
String type = fo.getString("type");
|
String type = fo.has("type") ? fo.getString("type") : "list";
|
||||||
|
|
||||||
Facet facet = null;
|
Facet facet = null;
|
||||||
if ("list".equals(type)) {
|
if ("list".equals(type)) {
|
||||||
|
@ -3,6 +3,6 @@ package com.metaweb.gridlock.browsing;
|
|||||||
import com.metaweb.gridlock.model.Row;
|
import com.metaweb.gridlock.model.Row;
|
||||||
|
|
||||||
public interface RowVisitor {
|
public interface RowVisitor {
|
||||||
public void visit(int rowIndex, Row row);
|
public boolean visit(int rowIndex, Row row);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -21,7 +21,7 @@ public class CellAccessorNominalRowGrouper implements RowVisitor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void visit(int rowIndex, Row row) {
|
public boolean visit(int rowIndex, Row row) {
|
||||||
if (_cellIndex < row.cells.size()) {
|
if (_cellIndex < row.cells.size()) {
|
||||||
Cell cell = row.cells.get(_cellIndex);
|
Cell cell = row.cells.get(_cellIndex);
|
||||||
if (cell != null) {
|
if (cell != null) {
|
||||||
@ -48,5 +48,6 @@ public class CellAccessorNominalRowGrouper implements RowVisitor {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -22,7 +22,7 @@ public class ExpressionNominalRowGrouper implements RowVisitor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void visit(int rowIndex, Row row) {
|
public boolean visit(int rowIndex, Row row) {
|
||||||
if (_cellIndex < row.cells.size()) {
|
if (_cellIndex < row.cells.size()) {
|
||||||
Cell cell = row.cells.get(_cellIndex);
|
Cell cell = row.cells.get(_cellIndex);
|
||||||
if (cell != null) {
|
if (cell != null) {
|
||||||
@ -33,18 +33,30 @@ public class ExpressionNominalRowGrouper implements RowVisitor {
|
|||||||
|
|
||||||
Object value = _evaluable.evaluate(bindings);
|
Object value = _evaluable.evaluate(bindings);
|
||||||
if (value != null) {
|
if (value != null) {
|
||||||
DecoratedValue dValue = new DecoratedValue(value, value.toString());
|
if (value.getClass().isArray()) {
|
||||||
|
Object[] a = (Object[]) value;
|
||||||
if (choices.containsKey(value)) {
|
for (Object v : a) {
|
||||||
choices.get(value).count++;
|
processValue(v);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
NominalFacetChoice choice = new NominalFacetChoice(dValue);
|
processValue(value);
|
||||||
choice.count = 1;
|
|
||||||
|
|
||||||
choices.put(value, choice);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void processValue(Object value) {
|
||||||
|
DecoratedValue dValue = new DecoratedValue(value, value.toString());
|
||||||
|
|
||||||
|
if (choices.containsKey(value)) {
|
||||||
|
choices.get(value).count++;
|
||||||
|
} else {
|
||||||
|
NominalFacetChoice choice = new NominalFacetChoice(dValue);
|
||||||
|
choice.count = 1;
|
||||||
|
|
||||||
|
choices.put(value, choice);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -76,7 +76,8 @@ public class ListFacet implements Facet {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public RowFilter getRowFilter() {
|
public RowFilter getRowFilter() {
|
||||||
return new ExpressionEqualRowFilter(_eval, _cellIndex, createMatches());
|
return _selection.size() == 0 ? null :
|
||||||
|
new ExpressionEqualRowFilter(_eval, _cellIndex, createMatches());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -88,12 +89,21 @@ public class ListFacet implements Facet {
|
|||||||
|
|
||||||
_choices.clear();
|
_choices.clear();
|
||||||
_choices.addAll(grouper.choices.values());
|
_choices.addAll(grouper.choices.values());
|
||||||
|
|
||||||
|
for (NominalFacetChoice choice : _selection) {
|
||||||
|
if (grouper.choices.containsKey(choice.decoratedValue.value)) {
|
||||||
|
grouper.choices.get(choice.decoratedValue.value).selected = true;
|
||||||
|
} else {
|
||||||
|
choice.count = 0;
|
||||||
|
_choices.add(choice);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected Object[] createMatches() {
|
protected Object[] createMatches() {
|
||||||
Object[] a = new Object[_choices.size()];
|
Object[] a = new Object[_selection.size()];
|
||||||
for (int i = 0; i < a.length; i++) {
|
for (int i = 0; i < a.length; i++) {
|
||||||
a[i] = _choices.get(i).decoratedValue.value;
|
a[i] = _selection.get(i).decoratedValue.value;
|
||||||
}
|
}
|
||||||
return a;
|
return a;
|
||||||
}
|
}
|
||||||
|
@ -153,14 +153,10 @@ public abstract class Command {
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected Engine getEngine(HttpServletRequest request, Project project) throws Exception {
|
protected Engine getEngine(HttpServletRequest request, Project project) throws Exception {
|
||||||
Properties properties = new Properties();
|
|
||||||
readFileUpload(request, properties);
|
|
||||||
|
|
||||||
Engine engine = new Engine(project);
|
Engine engine = new Engine(project);
|
||||||
if (properties.containsKey("engine")) {
|
String json = request.getParameter("engine");
|
||||||
String json = properties.getProperty("engine");
|
if (json != null) {
|
||||||
JSONObject o = jsonStringToObject(json);
|
JSONObject o = jsonStringToObject(json);
|
||||||
|
|
||||||
engine.initializeFromJSON(o);
|
engine.initializeFromJSON(o);
|
||||||
}
|
}
|
||||||
return engine;
|
return engine;
|
||||||
|
@ -0,0 +1,30 @@
|
|||||||
|
package com.metaweb.gridlock.commands;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.Properties;
|
||||||
|
|
||||||
|
import javax.servlet.ServletException;
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
import javax.servlet.http.HttpServletResponse;
|
||||||
|
|
||||||
|
import com.metaweb.gridlock.browsing.Engine;
|
||||||
|
import com.metaweb.gridlock.model.Project;
|
||||||
|
|
||||||
|
public class ComputeFacetsCommand extends Command {
|
||||||
|
@Override
|
||||||
|
public void doPost(HttpServletRequest request, HttpServletResponse response)
|
||||||
|
throws ServletException, IOException {
|
||||||
|
|
||||||
|
try {
|
||||||
|
Project project = getProject(request);
|
||||||
|
Engine engine = getEngine(request, project);
|
||||||
|
|
||||||
|
engine.computeFacets();
|
||||||
|
|
||||||
|
Properties options = new Properties();
|
||||||
|
respondJSON(response, engine.getJSON(options));
|
||||||
|
} catch (Exception e) {
|
||||||
|
respondException(response, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -47,13 +47,14 @@ public class GetRowsCommand extends Command {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void internalVisit(int rowIndex, Row row) {
|
public boolean internalVisit(int rowIndex, Row row) {
|
||||||
try {
|
try {
|
||||||
JSONObject ro = row.getJSON(options);
|
JSONObject ro = row.getJSON(options);
|
||||||
ro.put("i", rowIndex);
|
ro.put("i", rowIndex);
|
||||||
list.add(ro);
|
list.add(ro);
|
||||||
} catch (JSONException e) {
|
} catch (JSONException e) {
|
||||||
}
|
}
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
}.init(a, options);
|
}.init(a, options);
|
||||||
|
|
||||||
@ -86,14 +87,18 @@ public class GetRowsCommand extends Command {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void visit(int rowIndex, Row row) {
|
public boolean visit(int rowIndex, Row row) {
|
||||||
|
boolean r = false;
|
||||||
|
|
||||||
if (total >= start && total < start + limit) {
|
if (total >= start && total < start + limit) {
|
||||||
internalVisit(rowIndex, row);
|
r = internalVisit(rowIndex, row);
|
||||||
}
|
}
|
||||||
total++;
|
total++;
|
||||||
|
return r;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void internalVisit(int rowIndex, Row row) {
|
protected boolean internalVisit(int rowIndex, Row row) {
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -8,8 +8,10 @@ import java.util.Map;
|
|||||||
import com.metaweb.gridlock.expr.Scanner.NumberToken;
|
import com.metaweb.gridlock.expr.Scanner.NumberToken;
|
||||||
import com.metaweb.gridlock.expr.Scanner.Token;
|
import com.metaweb.gridlock.expr.Scanner.Token;
|
||||||
import com.metaweb.gridlock.expr.Scanner.TokenType;
|
import com.metaweb.gridlock.expr.Scanner.TokenType;
|
||||||
|
import com.metaweb.gridlock.expr.functions.Get;
|
||||||
import com.metaweb.gridlock.expr.functions.Replace;
|
import com.metaweb.gridlock.expr.functions.Replace;
|
||||||
import com.metaweb.gridlock.expr.functions.Slice;
|
import com.metaweb.gridlock.expr.functions.Slice;
|
||||||
|
import com.metaweb.gridlock.expr.functions.Split;
|
||||||
import com.metaweb.gridlock.expr.functions.ToLowercase;
|
import com.metaweb.gridlock.expr.functions.ToLowercase;
|
||||||
import com.metaweb.gridlock.expr.functions.ToTitlecase;
|
import com.metaweb.gridlock.expr.functions.ToTitlecase;
|
||||||
import com.metaweb.gridlock.expr.functions.ToUppercase;
|
import com.metaweb.gridlock.expr.functions.ToUppercase;
|
||||||
@ -26,7 +28,9 @@ public class Parser {
|
|||||||
functionTable.put("toTitlecase", new ToTitlecase());
|
functionTable.put("toTitlecase", new ToTitlecase());
|
||||||
functionTable.put("slice", new Slice());
|
functionTable.put("slice", new Slice());
|
||||||
functionTable.put("substring", new Slice());
|
functionTable.put("substring", new Slice());
|
||||||
|
functionTable.put("get", new Get());
|
||||||
functionTable.put("replace", new Replace());
|
functionTable.put("replace", new Replace());
|
||||||
|
functionTable.put("split", new Split());
|
||||||
}
|
}
|
||||||
|
|
||||||
public Parser(String s) throws Exception {
|
public Parser(String s) throws Exception {
|
||||||
@ -196,7 +200,7 @@ public class Parser {
|
|||||||
List<Evaluable> args = parseExpressionList("]");
|
List<Evaluable> args = parseExpressionList("]");
|
||||||
args.add(0, eval);
|
args.add(0, eval);
|
||||||
|
|
||||||
eval = new FunctionCallExpr(makeArray(args), functionTable.get("slice"));
|
eval = new FunctionCallExpr(makeArray(args), functionTable.get("get"));
|
||||||
} else {
|
} else {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
67
src/main/java/com/metaweb/gridlock/expr/functions/Get.java
Normal file
67
src/main/java/com/metaweb/gridlock/expr/functions/Get.java
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
package com.metaweb.gridlock.expr.functions;
|
||||||
|
|
||||||
|
import java.util.Properties;
|
||||||
|
|
||||||
|
import com.metaweb.gridlock.expr.Function;
|
||||||
|
|
||||||
|
public class Get implements Function {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object call(Properties bindings, Object[] args) {
|
||||||
|
if (args.length > 1 && args.length <= 3) {
|
||||||
|
Object v = args[0];
|
||||||
|
Object from = args[1];
|
||||||
|
Object to = args.length == 3 ? args[2] : null;
|
||||||
|
|
||||||
|
if (v != null && from != null && from instanceof Number && (to == null || to instanceof Number)) {
|
||||||
|
if (v.getClass().isArray()) {
|
||||||
|
Object[] a = (Object[]) v;
|
||||||
|
int start = ((Number) from).intValue();
|
||||||
|
if (start < 0) {
|
||||||
|
start = a.length + start;
|
||||||
|
}
|
||||||
|
start = Math.min(a.length, Math.max(0, start));
|
||||||
|
|
||||||
|
if (to == null) {
|
||||||
|
return a[start];
|
||||||
|
} else {
|
||||||
|
int end = to != null && to instanceof Number ?
|
||||||
|
((Number) to).intValue() : a.length;
|
||||||
|
|
||||||
|
if (end < 0) {
|
||||||
|
end = a.length - end;
|
||||||
|
}
|
||||||
|
end = Math.min(a.length, Math.max(start, end));
|
||||||
|
|
||||||
|
Object[] a2 = new Object[end - start];
|
||||||
|
System.arraycopy(a, start, a2, 0, end - start);
|
||||||
|
|
||||||
|
return a2;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
String s = (v instanceof String ? (String) v : v.toString());
|
||||||
|
|
||||||
|
int start = ((Number) from).intValue();
|
||||||
|
if (start < 0) {
|
||||||
|
start = s.length() + start;
|
||||||
|
}
|
||||||
|
start = Math.min(s.length(), Math.max(0, start));
|
||||||
|
|
||||||
|
if (to != null && to instanceof Number) {
|
||||||
|
int end = ((Number) to).intValue();
|
||||||
|
if (end < 0) {
|
||||||
|
end = s.length() - end;
|
||||||
|
}
|
||||||
|
end = Math.min(s.length(), Math.max(start, end));
|
||||||
|
|
||||||
|
return s.substring(start, end);
|
||||||
|
} else {
|
||||||
|
return s.substring(start, start + 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -1,6 +1,5 @@
|
|||||||
package com.metaweb.gridlock.expr.functions;
|
package com.metaweb.gridlock.expr.functions;
|
||||||
|
|
||||||
import java.lang.reflect.Array;
|
|
||||||
import java.util.Properties;
|
import java.util.Properties;
|
||||||
|
|
||||||
import com.metaweb.gridlock.expr.Function;
|
import com.metaweb.gridlock.expr.Function;
|
||||||
@ -15,14 +14,14 @@ public class Slice implements Function {
|
|||||||
Object to = args.length == 3 ? args[2] : null;
|
Object to = args.length == 3 ? args[2] : null;
|
||||||
|
|
||||||
if (v != null && from != null && from instanceof Number && (to == null || to instanceof Number)) {
|
if (v != null && from != null && from instanceof Number && (to == null || to instanceof Number)) {
|
||||||
if (v instanceof Array) {
|
if (v.getClass().isArray()) {
|
||||||
Object[] a = (Object[]) v;
|
Object[] a = (Object[]) v;
|
||||||
int start = ((Number) from).intValue();
|
int start = ((Number) from).intValue();
|
||||||
int end = to != null && to instanceof Number ?
|
int end = to != null && to instanceof Number ?
|
||||||
((Number) to).intValue() : a.length;
|
((Number) to).intValue() : a.length;
|
||||||
|
|
||||||
if (start < 0) {
|
if (start < 0) {
|
||||||
start = a.length - start;
|
start = a.length + start;
|
||||||
}
|
}
|
||||||
start = Math.min(a.length, Math.max(0, start));
|
start = Math.min(a.length, Math.max(0, start));
|
||||||
|
|
||||||
@ -40,7 +39,7 @@ public class Slice implements Function {
|
|||||||
|
|
||||||
int start = ((Number) from).intValue();
|
int start = ((Number) from).intValue();
|
||||||
if (start < 0) {
|
if (start < 0) {
|
||||||
start = s.length() - start;
|
start = s.length() + start;
|
||||||
}
|
}
|
||||||
start = Math.min(s.length(), Math.max(0, start));
|
start = Math.min(s.length(), Math.max(0, start));
|
||||||
|
|
||||||
|
21
src/main/java/com/metaweb/gridlock/expr/functions/Split.java
Normal file
21
src/main/java/com/metaweb/gridlock/expr/functions/Split.java
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
package com.metaweb.gridlock.expr.functions;
|
||||||
|
|
||||||
|
import java.util.Properties;
|
||||||
|
|
||||||
|
import com.metaweb.gridlock.expr.Function;
|
||||||
|
|
||||||
|
public class Split implements Function {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object call(Properties bindings, Object[] args) {
|
||||||
|
if (args.length == 2) {
|
||||||
|
Object v = args[0];
|
||||||
|
Object split = args[1];
|
||||||
|
if (v != null && split != null && split instanceof String) {
|
||||||
|
return (v instanceof String ? (String) v : v.toString()).split((String) split);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -1 +1 @@
|
|||||||
<html>
<head>
<title>Gridlock</title>
<link rel="stylesheet" href="/styles/common.css" />
<link rel="stylesheet" href="/styles/project.css" />
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.4/jquery.min.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/project.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>
</head>
<body>
<div id="header">
<h1 id="title">Gridlock</h1>
</div>
<div id="body">
Loading ...
</div>
</body>
</html>
|
<html>
<head>
<title>Gridlock</title>
<link rel="stylesheet" href="/styles/common.css" />
<link rel="stylesheet" href="/styles/project.css" />
<link rel="stylesheet" href="/styles/history.css" />
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.4/jquery.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/project.js"></script>
<script type="text/javascript" src="scripts/project/list-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>
</head>
<body>
<div id="header">
<h1 id="title">Gridlock</h1>
</div>
<div id="body">
Loading ...
</div>
</body>
</html>
|
@ -1,11 +1,11 @@
|
|||||||
function BrowsingEngine(div) {
|
function BrowsingEngine(div) {
|
||||||
this._div = div;
|
this._div = div;
|
||||||
this.render();
|
|
||||||
|
|
||||||
this._facets = [];
|
this._facets = [];
|
||||||
|
|
||||||
|
this._initializeUI();
|
||||||
}
|
}
|
||||||
|
|
||||||
BrowsingEngine.prototype.render = function() {
|
BrowsingEngine.prototype._initializeUI = function() {
|
||||||
var self = this;
|
var self = this;
|
||||||
var container = this._div.empty();
|
var container = this._div.empty();
|
||||||
};
|
};
|
||||||
@ -13,7 +13,36 @@ BrowsingEngine.prototype.render = function() {
|
|||||||
BrowsingEngine.prototype.getJSON = function() {
|
BrowsingEngine.prototype.getJSON = function() {
|
||||||
var a = { facets: [] };
|
var a = { facets: [] };
|
||||||
for (var i = 0; i < this._facets.length; i++) {
|
for (var i = 0; i < this._facets.length; i++) {
|
||||||
a.facets.push(this._facets[i].getJSON());
|
a.facets.push(this._facets[i].facet.getJSON());
|
||||||
}
|
}
|
||||||
return a;
|
return a;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
BrowsingEngine.prototype.addFacet = function(type, config) {
|
||||||
|
var div = $('<div></div>').addClass("facet-container").appendTo(this._div);
|
||||||
|
var facet;
|
||||||
|
switch (type) {
|
||||||
|
default:
|
||||||
|
facet = new ListFacet(div, config, {});
|
||||||
|
}
|
||||||
|
|
||||||
|
this._facets.push({ elmt: div, facet: facet });
|
||||||
|
this.update();
|
||||||
|
};
|
||||||
|
|
||||||
|
BrowsingEngine.prototype.update = function() {
|
||||||
|
var self = this;
|
||||||
|
|
||||||
|
$.post(
|
||||||
|
"/command/compute-facets?" + $.param({ project: theProject.id }),
|
||||||
|
{ engine: JSON.stringify(ui.browsingEngine.getJSON()) },
|
||||||
|
function(data) {
|
||||||
|
var facetData = data.facets;
|
||||||
|
|
||||||
|
for (var i = 0; i < facetData.length; i++) {
|
||||||
|
self._facets[i].facet.updateState(facetData[i]);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"json"
|
||||||
|
);
|
||||||
|
};
|
||||||
|
@ -11,7 +11,7 @@ DataTableView.prototype.render = function() {
|
|||||||
var divSummary = $('<div></div>').addClass("viewPanel-summary").appendTo(container);
|
var divSummary = $('<div></div>').addClass("viewPanel-summary").appendTo(container);
|
||||||
$('<span>' +
|
$('<span>' +
|
||||||
(theProject.rowModel.start + 1) + " to " +
|
(theProject.rowModel.start + 1) + " to " +
|
||||||
(theProject.rowModel.start + theProject.rowModel.limit) + " of " +
|
Math.min(theProject.rowModel.filtered, theProject.rowModel.start + theProject.rowModel.limit) + " of " +
|
||||||
(theProject.rowModel.filtered) + " filtered rows, " +
|
(theProject.rowModel.filtered) + " filtered rows, " +
|
||||||
(theProject.rowModel.total) + " total rows" +
|
(theProject.rowModel.total) + " total rows" +
|
||||||
'</span>'
|
'</span>'
|
||||||
@ -30,7 +30,7 @@ DataTableView.prototype.render = function() {
|
|||||||
$('<span> • </span>').appendTo(pagingControls);
|
$('<span> • </span>').appendTo(pagingControls);
|
||||||
var nextPage = $('<a href="javascript:{}">next page »</a>').appendTo(pagingControls);
|
var nextPage = $('<a href="javascript:{}">next page »</a>').appendTo(pagingControls);
|
||||||
var lastPage = $('<a href="javascript:{}">last »</a>').appendTo(pagingControls);
|
var lastPage = $('<a href="javascript:{}">last »</a>').appendTo(pagingControls);
|
||||||
if (theProject.rowModel.start + theProject.rowModel.limit < theProject.rowModel.total) {
|
if (theProject.rowModel.start + theProject.rowModel.limit < theProject.rowModel.filtered) {
|
||||||
nextPage.addClass("action").click(function(evt) { self._onClickNextPage(this, evt); });
|
nextPage.addClass("action").click(function(evt) { self._onClickNextPage(this, evt); });
|
||||||
lastPage.addClass("action").click(function(evt) { self._onClickLastPage(this, evt); });
|
lastPage.addClass("action").click(function(evt) { self._onClickLastPage(this, evt); });
|
||||||
} else {
|
} else {
|
||||||
@ -137,7 +137,7 @@ DataTableView.prototype._onClickFirstPage = function(elmt, evt) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
DataTableView.prototype._onClickLastPage = function(elmt, evt) {
|
DataTableView.prototype._onClickLastPage = function(elmt, evt) {
|
||||||
this._showRows(Math.floor(theProject.rowModel.total / this._pageSize) * this._pageSize);
|
this._showRows(Math.floor(theProject.rowModel.filtered / this._pageSize) * this._pageSize);
|
||||||
};
|
};
|
||||||
|
|
||||||
DataTableView.prototype._createMenuForAllColumns = function(elmt) {
|
DataTableView.prototype._createMenuForAllColumns = function(elmt) {
|
||||||
@ -172,15 +172,29 @@ DataTableView.prototype._createMenuForColumnHeader = function(column, index, elm
|
|||||||
submenu: [
|
submenu: [
|
||||||
{
|
{
|
||||||
label: "By Nominal Choices",
|
label: "By Nominal Choices",
|
||||||
click: function() {}
|
click: function() {
|
||||||
|
ui.browsingEngine.addFacet(
|
||||||
|
"list",
|
||||||
|
{
|
||||||
|
"name" : column.headerLabel,
|
||||||
|
"cellIndex" : column.cellIndex,
|
||||||
|
"expression" : "value"
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: "By Simple Text Search",
|
label: "By Simple Text Search",
|
||||||
click: function() {}
|
click: function() {}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: "By Regular Expression",
|
label: "By Custom Expression",
|
||||||
click: function() {}
|
click: function() {
|
||||||
|
var expression = window.prompt("Enter expression", 'value');
|
||||||
|
if (expression != null) {
|
||||||
|
self._doFilterByExpression(column, expression);
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: "By Reconciliation Features",
|
label: "By Reconciliation Features",
|
||||||
@ -282,6 +296,18 @@ DataTableView.prototype._doTextTransform = function(column, expression) {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
DataTableView.prototype.update = function() {
|
DataTableView.prototype._doFilterByExpression = function(column, expression) {
|
||||||
this._showRows(theProject.rowModel.start);
|
ui.browsingEngine.addFacet(
|
||||||
|
"list",
|
||||||
|
{
|
||||||
|
"name" : column.headerLabel + ": " + expression,
|
||||||
|
"cellIndex" : column.cellIndex,
|
||||||
|
"expression" : expression
|
||||||
|
}
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
DataTableView.prototype.update = function(reset) {
|
||||||
|
this._showRows(reset ? 0 : theProject.rowModel.start);
|
||||||
|
};
|
||||||
|
|
||||||
|
@ -1,5 +1,11 @@
|
|||||||
function HistoryWidget(div) {
|
function HistoryWidget(div) {
|
||||||
this._div = div;
|
this._div = div;
|
||||||
|
this._div.mouseover(function() {
|
||||||
|
this.style.height = "300px";
|
||||||
|
}).mouseout(function() {
|
||||||
|
this.style.height = "100px";
|
||||||
|
});
|
||||||
|
|
||||||
this.update();
|
this.update();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
142
src/main/webapp/scripts/project/list-facet.js
Normal file
142
src/main/webapp/scripts/project/list-facet.js
Normal file
@ -0,0 +1,142 @@
|
|||||||
|
function ListFacet(div, config, options) {
|
||||||
|
this._div = div;
|
||||||
|
this._config = config;
|
||||||
|
this._selection = [];
|
||||||
|
this._data = null;
|
||||||
|
|
||||||
|
this.render();
|
||||||
|
}
|
||||||
|
|
||||||
|
ListFacet.prototype.getJSON = function() {
|
||||||
|
var o = cloneDeep(this._config);
|
||||||
|
o.type = "list";
|
||||||
|
o.selection = [];
|
||||||
|
for (var i = 0; i < this._selection.length; i++) {
|
||||||
|
var choice = cloneDeep(this._selection[i]);
|
||||||
|
choice.s = true;
|
||||||
|
o.selection.push(choice);
|
||||||
|
}
|
||||||
|
return o;
|
||||||
|
};
|
||||||
|
|
||||||
|
ListFacet.prototype.updateState = function(data) {
|
||||||
|
this._data = data;
|
||||||
|
|
||||||
|
var selection = [];
|
||||||
|
var choices = data.choices;
|
||||||
|
for (var i = 0; i < choices.length; i++) {
|
||||||
|
var choice = choices[i];
|
||||||
|
if (choice.s) {
|
||||||
|
selection.push(choice);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this._selection = selection;
|
||||||
|
|
||||||
|
this.render();
|
||||||
|
};
|
||||||
|
|
||||||
|
ListFacet.prototype.render = function() {
|
||||||
|
var self = this;
|
||||||
|
|
||||||
|
var scrollTop = 0;
|
||||||
|
try {
|
||||||
|
scrollTop = this._div[0].childNodes[1].scrollTop;
|
||||||
|
} catch (e) {
|
||||||
|
}
|
||||||
|
var container = this._div.empty();
|
||||||
|
|
||||||
|
var headerDiv = $('<div></div>').addClass("facet-title").appendTo(container);
|
||||||
|
$('<span></span>').text(this._config.name).appendTo(headerDiv);
|
||||||
|
|
||||||
|
var bodyDiv = $('<div></div>').addClass("facet-body").appendTo(container);
|
||||||
|
if (this._data == null) {
|
||||||
|
bodyDiv.html("Loading...");
|
||||||
|
} else {
|
||||||
|
var selectionCount = this._selection.length;
|
||||||
|
if (selectionCount > 0) {
|
||||||
|
var reset = function() {
|
||||||
|
self._reset();
|
||||||
|
};
|
||||||
|
$('<a href="javascript:{}"></a>').addClass("facet-choice-link").text("reset").click(reset).prependTo(headerDiv);
|
||||||
|
}
|
||||||
|
|
||||||
|
var renderChoice = function(choice) {
|
||||||
|
var label = choice.v.l;
|
||||||
|
var count = choice.c;
|
||||||
|
|
||||||
|
var choiceDiv = $('<div></div>').addClass("facet-choice").appendTo(bodyDiv);
|
||||||
|
if (choice.s) {
|
||||||
|
choiceDiv.addClass("facet-choice-selected");
|
||||||
|
}
|
||||||
|
|
||||||
|
var a = $('<a href="javascript:{}"></a>').addClass("facet-choice-label").text(label).appendTo(choiceDiv);
|
||||||
|
$('<span></span>').addClass("facet-choice-count").text(count).appendTo(choiceDiv);
|
||||||
|
|
||||||
|
var select = function() {
|
||||||
|
self._select(choice, false);
|
||||||
|
};
|
||||||
|
var selectOnly = function() {
|
||||||
|
self._select(choice, true);
|
||||||
|
};
|
||||||
|
var deselect = function() {
|
||||||
|
self._deselect(choice);
|
||||||
|
};
|
||||||
|
|
||||||
|
if (choice.s) { // selected
|
||||||
|
if (selectionCount > 1) {
|
||||||
|
// select only
|
||||||
|
a.click(selectOnly);
|
||||||
|
} else {
|
||||||
|
// deselect
|
||||||
|
a.click(deselect);
|
||||||
|
}
|
||||||
|
|
||||||
|
// remove link
|
||||||
|
$('<a href="javascript:{}"></a>').addClass("facet-choice-link").text("remove").click(deselect).prependTo(choiceDiv);
|
||||||
|
} else if (selectionCount > 0) {
|
||||||
|
a.click(selectOnly);
|
||||||
|
|
||||||
|
// include link
|
||||||
|
$('<a href="javascript:{}"></a>').addClass("facet-choice-link").text("include").click(select).appendTo(choiceDiv);
|
||||||
|
} else {
|
||||||
|
a.click(select);
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
var choices = this._data.choices;
|
||||||
|
for (var i = 0; i < choices.length; i++) {
|
||||||
|
renderChoice(choices[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
bodyDiv[0].scrollTop = scrollTop;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
ListFacet.prototype._select = function(choice, only) {
|
||||||
|
if (only) {
|
||||||
|
this._selection = [];
|
||||||
|
}
|
||||||
|
this._selection.push(choice);
|
||||||
|
this._updateRest();
|
||||||
|
};
|
||||||
|
|
||||||
|
ListFacet.prototype._deselect = function(choice) {
|
||||||
|
for (var i = this._selection.length - 1; i >= 0; i--) {
|
||||||
|
if (this._selection[i] == choice) {
|
||||||
|
this._selection.splice(i, 1);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this._updateRest();
|
||||||
|
};
|
||||||
|
|
||||||
|
ListFacet.prototype._reset = function() {
|
||||||
|
this._selection = [];
|
||||||
|
this._updateRest();
|
||||||
|
};
|
||||||
|
|
||||||
|
ListFacet.prototype._updateRest = function() {
|
||||||
|
ui.browsingEngine.update();
|
||||||
|
ui.dataTableView.update(true);
|
||||||
|
};
|
23
src/main/webapp/scripts/util/misc.js
Normal file
23
src/main/webapp/scripts/util/misc.js
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
function cloneDeep(o) {
|
||||||
|
if (o === undefined || o === null) {
|
||||||
|
return o;
|
||||||
|
} else if (o instanceof Function) {
|
||||||
|
return o;
|
||||||
|
} else if (o instanceof Array) {
|
||||||
|
var a = [];
|
||||||
|
for (var i = 0; i < o.length; i++) {
|
||||||
|
a.push(cloneDeep(o[i]));
|
||||||
|
}
|
||||||
|
return a;
|
||||||
|
} else if (o instanceof Object) {
|
||||||
|
var a = {};
|
||||||
|
for (var n in o) {
|
||||||
|
if (o.hasOwnProperty(n)) {
|
||||||
|
a[n] = cloneDeep(o[n]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return a;
|
||||||
|
} else {
|
||||||
|
return o;
|
||||||
|
}
|
||||||
|
}
|
59
src/main/webapp/styles/browsing.css
Normal file
59
src/main/webapp/styles/browsing.css
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
.browsing-panel {
|
||||||
|
}
|
||||||
|
|
||||||
|
.facet-container {
|
||||||
|
clear: both;
|
||||||
|
margin-top: 1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.facet-title {
|
||||||
|
padding: 5px;
|
||||||
|
background: #eee;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
.facet-body {
|
||||||
|
border: 1px solid #ccc;
|
||||||
|
padding: 1px;
|
||||||
|
height: 20em;
|
||||||
|
overflow: auto;
|
||||||
|
}
|
||||||
|
.facet-choice {
|
||||||
|
padding: 2px 5px;
|
||||||
|
clear: both;
|
||||||
|
}
|
||||||
|
.facet-choice-selected .facet-choice-label {
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
a.facet-choice-label {
|
||||||
|
margin-right: 0.5em;
|
||||||
|
text-decoration: none;
|
||||||
|
color: #004;
|
||||||
|
}
|
||||||
|
a.facet-choice-label:hover {
|
||||||
|
text-decoration: underline;
|
||||||
|
color: #66f;
|
||||||
|
}
|
||||||
|
|
||||||
|
.facet-choice-count {
|
||||||
|
color: #aaa;
|
||||||
|
}
|
||||||
|
.facet-choice-count:before {
|
||||||
|
content: "(";
|
||||||
|
color: #aaa;
|
||||||
|
}
|
||||||
|
.facet-choice-count:after {
|
||||||
|
content: ")";
|
||||||
|
color: #aaa;
|
||||||
|
}
|
||||||
|
|
||||||
|
a.facet-choice-link {
|
||||||
|
font-size: 80%;
|
||||||
|
float: right;
|
||||||
|
text-decoration: none;
|
||||||
|
color: #aac;
|
||||||
|
}
|
||||||
|
a.facet-choice-link:hover {
|
||||||
|
text-decoration: underline;
|
||||||
|
color: #88a;
|
||||||
|
}
|
35
src/main/webapp/styles/history.css
Normal file
35
src/main/webapp/styles/history.css
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
.history-panel {
|
||||||
|
position: absolute;
|
||||||
|
top: -1px;
|
||||||
|
right: 20px;
|
||||||
|
width: 200px;
|
||||||
|
padding: 2px;
|
||||||
|
background: #fffee0;
|
||||||
|
border: 1px solid #ccc;
|
||||||
|
height: 100px;
|
||||||
|
overflow: auto;
|
||||||
|
}
|
||||||
|
.history-panel h3 {
|
||||||
|
margin: 0;
|
||||||
|
padding: 3px;
|
||||||
|
background: #fee;
|
||||||
|
font-size: 100%;
|
||||||
|
}
|
||||||
|
.history-past {
|
||||||
|
padding-bottom: 3px;
|
||||||
|
border-bottom: 2px solid #aaa;
|
||||||
|
}
|
||||||
|
.history-future {
|
||||||
|
padding-top: 3px;
|
||||||
|
}
|
||||||
|
a.history-entry {
|
||||||
|
display: block;
|
||||||
|
padding: 3px 5px;
|
||||||
|
border-bottom: 1px solid #eee;
|
||||||
|
text-decoration: none;
|
||||||
|
color: black;
|
||||||
|
}
|
||||||
|
a.history-entry:hover {
|
||||||
|
background: #eee;
|
||||||
|
color: #a88;
|
||||||
|
}
|
@ -33,38 +33,3 @@ img.column-header-menu {
|
|||||||
text-align: center;
|
text-align: center;
|
||||||
margin: 1em 0;
|
margin: 1em 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.history-panel {
|
|
||||||
position: absolute;
|
|
||||||
top: -1px;
|
|
||||||
right: 20px;
|
|
||||||
width: 200px;
|
|
||||||
padding: 2px;
|
|
||||||
background: white;
|
|
||||||
border: 1px solid #eee;
|
|
||||||
height: 200px;
|
|
||||||
overflow: auto;
|
|
||||||
}
|
|
||||||
.history-panel h3 {
|
|
||||||
margin: 0;
|
|
||||||
padding: 5px;
|
|
||||||
background: #fee;
|
|
||||||
}
|
|
||||||
.history-past {
|
|
||||||
padding-bottom: 3px;
|
|
||||||
border-bottom: 2px solid #aaa;
|
|
||||||
}
|
|
||||||
.history-future {
|
|
||||||
padding-top: 3px;
|
|
||||||
}
|
|
||||||
a.history-entry {
|
|
||||||
display: block;
|
|
||||||
padding: 3px 5px;
|
|
||||||
border-bottom: 1px solid #eee;
|
|
||||||
text-decoration: none;
|
|
||||||
color: black;
|
|
||||||
}
|
|
||||||
a.history-entry:hover {
|
|
||||||
background: #eee;
|
|
||||||
color: #a88;
|
|
||||||
}
|
|
||||||
|
Loading…
Reference in New Issue
Block a user