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 com.metaweb.gridlock.commands.Command;
|
||||
import com.metaweb.gridlock.commands.ComputeFacetsCommand;
|
||||
import com.metaweb.gridlock.commands.CreateProjectFromUploadCommand;
|
||||
import com.metaweb.gridlock.commands.DoTextTransformCommand;
|
||||
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-rows", new GetRowsCommand());
|
||||
_commands.put("get-history", new GetHistoryCommand());
|
||||
_commands.put("compute-facets", new ComputeFacetsCommand());
|
||||
_commands.put("undo-redo", new UndoRedoCommand());
|
||||
_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.ListFacet;
|
||||
import com.metaweb.gridlock.browsing.filters.RowFilter;
|
||||
import com.metaweb.gridlock.model.Project;
|
||||
|
||||
public class Engine {
|
||||
@ -29,7 +30,10 @@ public class Engine {
|
||||
ConjunctiveFilteredRows cfr = new ConjunctiveFilteredRows();
|
||||
for (Facet facet : _facets) {
|
||||
if (facet != except) {
|
||||
cfr.add(facet.getRowFilter());
|
||||
RowFilter rowFilter = facet.getRowFilter();
|
||||
if (rowFilter != null) {
|
||||
cfr.add(rowFilter);
|
||||
}
|
||||
}
|
||||
}
|
||||
return cfr;
|
||||
@ -53,7 +57,7 @@ public class Engine {
|
||||
|
||||
for (int i = 0; i < length; i++) {
|
||||
JSONObject fo = a.getJSONObject(i);
|
||||
String type = fo.getString("type");
|
||||
String type = fo.has("type") ? fo.getString("type") : "list";
|
||||
|
||||
Facet facet = null;
|
||||
if ("list".equals(type)) {
|
||||
|
@ -3,6 +3,6 @@ package com.metaweb.gridlock.browsing;
|
||||
import com.metaweb.gridlock.model.Row;
|
||||
|
||||
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
|
||||
public void visit(int rowIndex, Row row) {
|
||||
public boolean visit(int rowIndex, Row row) {
|
||||
if (_cellIndex < row.cells.size()) {
|
||||
Cell cell = row.cells.get(_cellIndex);
|
||||
if (cell != null) {
|
||||
@ -48,5 +48,6 @@ public class CellAccessorNominalRowGrouper implements RowVisitor {
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
@ -22,7 +22,7 @@ public class ExpressionNominalRowGrouper implements RowVisitor {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(int rowIndex, Row row) {
|
||||
public boolean visit(int rowIndex, Row row) {
|
||||
if (_cellIndex < row.cells.size()) {
|
||||
Cell cell = row.cells.get(_cellIndex);
|
||||
if (cell != null) {
|
||||
@ -33,18 +33,30 @@ public class ExpressionNominalRowGrouper implements RowVisitor {
|
||||
|
||||
Object value = _evaluable.evaluate(bindings);
|
||||
if (value != null) {
|
||||
DecoratedValue dValue = new DecoratedValue(value, value.toString());
|
||||
|
||||
if (choices.containsKey(value)) {
|
||||
choices.get(value).count++;
|
||||
if (value.getClass().isArray()) {
|
||||
Object[] a = (Object[]) value;
|
||||
for (Object v : a) {
|
||||
processValue(v);
|
||||
}
|
||||
} else {
|
||||
NominalFacetChoice choice = new NominalFacetChoice(dValue);
|
||||
choice.count = 1;
|
||||
|
||||
choices.put(value, choice);
|
||||
processValue(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
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
|
||||
public RowFilter getRowFilter() {
|
||||
return new ExpressionEqualRowFilter(_eval, _cellIndex, createMatches());
|
||||
return _selection.size() == 0 ? null :
|
||||
new ExpressionEqualRowFilter(_eval, _cellIndex, createMatches());
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -88,12 +89,21 @@ public class ListFacet implements Facet {
|
||||
|
||||
_choices.clear();
|
||||
_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() {
|
||||
Object[] a = new Object[_choices.size()];
|
||||
Object[] a = new Object[_selection.size()];
|
||||
for (int i = 0; i < a.length; i++) {
|
||||
a[i] = _choices.get(i).decoratedValue.value;
|
||||
a[i] = _selection.get(i).decoratedValue.value;
|
||||
}
|
||||
return a;
|
||||
}
|
||||
|
@ -153,14 +153,10 @@ public abstract class Command {
|
||||
}
|
||||
|
||||
protected Engine getEngine(HttpServletRequest request, Project project) throws Exception {
|
||||
Properties properties = new Properties();
|
||||
readFileUpload(request, properties);
|
||||
|
||||
Engine engine = new Engine(project);
|
||||
if (properties.containsKey("engine")) {
|
||||
String json = properties.getProperty("engine");
|
||||
String json = request.getParameter("engine");
|
||||
if (json != null) {
|
||||
JSONObject o = jsonStringToObject(json);
|
||||
|
||||
engine.initializeFromJSON(o);
|
||||
}
|
||||
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
|
||||
public void internalVisit(int rowIndex, Row row) {
|
||||
public boolean internalVisit(int rowIndex, Row row) {
|
||||
try {
|
||||
JSONObject ro = row.getJSON(options);
|
||||
ro.put("i", rowIndex);
|
||||
list.add(ro);
|
||||
} catch (JSONException e) {
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}.init(a, options);
|
||||
|
||||
@ -86,14 +87,18 @@ public class GetRowsCommand extends Command {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(int rowIndex, Row row) {
|
||||
public boolean visit(int rowIndex, Row row) {
|
||||
boolean r = false;
|
||||
|
||||
if (total >= start && total < start + limit) {
|
||||
internalVisit(rowIndex, row);
|
||||
r = internalVisit(rowIndex, row);
|
||||
}
|
||||
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.Token;
|
||||
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.Slice;
|
||||
import com.metaweb.gridlock.expr.functions.Split;
|
||||
import com.metaweb.gridlock.expr.functions.ToLowercase;
|
||||
import com.metaweb.gridlock.expr.functions.ToTitlecase;
|
||||
import com.metaweb.gridlock.expr.functions.ToUppercase;
|
||||
@ -26,7 +28,9 @@ public class Parser {
|
||||
functionTable.put("toTitlecase", new ToTitlecase());
|
||||
functionTable.put("slice", new Slice());
|
||||
functionTable.put("substring", new Slice());
|
||||
functionTable.put("get", new Get());
|
||||
functionTable.put("replace", new Replace());
|
||||
functionTable.put("split", new Split());
|
||||
}
|
||||
|
||||
public Parser(String s) throws Exception {
|
||||
@ -196,7 +200,7 @@ public class Parser {
|
||||
List<Evaluable> args = parseExpressionList("]");
|
||||
args.add(0, eval);
|
||||
|
||||
eval = new FunctionCallExpr(makeArray(args), functionTable.get("slice"));
|
||||
eval = new FunctionCallExpr(makeArray(args), functionTable.get("get"));
|
||||
} else {
|
||||
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;
|
||||
|
||||
import java.lang.reflect.Array;
|
||||
import java.util.Properties;
|
||||
|
||||
import com.metaweb.gridlock.expr.Function;
|
||||
@ -15,14 +14,14 @@ public class Slice implements Function {
|
||||
Object to = args.length == 3 ? args[2] : null;
|
||||
|
||||
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;
|
||||
int start = ((Number) from).intValue();
|
||||
int end = to != null && to instanceof Number ?
|
||||
((Number) to).intValue() : a.length;
|
||||
|
||||
if (start < 0) {
|
||||
start = a.length - start;
|
||||
start = a.length + start;
|
||||
}
|
||||
start = Math.min(a.length, Math.max(0, start));
|
||||
|
||||
@ -40,7 +39,7 @@ public class Slice implements Function {
|
||||
|
||||
int start = ((Number) from).intValue();
|
||||
if (start < 0) {
|
||||
start = s.length() - start;
|
||||
start = s.length() + 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) {
|
||||
this._div = div;
|
||||
this.render();
|
||||
|
||||
this._facets = [];
|
||||
|
||||
this._initializeUI();
|
||||
}
|
||||
|
||||
BrowsingEngine.prototype.render = function() {
|
||||
BrowsingEngine.prototype._initializeUI = function() {
|
||||
var self = this;
|
||||
var container = this._div.empty();
|
||||
};
|
||||
@ -13,7 +13,36 @@ BrowsingEngine.prototype.render = function() {
|
||||
BrowsingEngine.prototype.getJSON = function() {
|
||||
var a = { facets: [] };
|
||||
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;
|
||||
};
|
||||
|
||||
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);
|
||||
$('<span>' +
|
||||
(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.total) + " total rows" +
|
||||
'</span>'
|
||||
@ -30,7 +30,7 @@ DataTableView.prototype.render = function() {
|
||||
$('<span> • </span>').appendTo(pagingControls);
|
||||
var nextPage = $('<a href="javascript:{}">next page »</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); });
|
||||
lastPage.addClass("action").click(function(evt) { self._onClickLastPage(this, evt); });
|
||||
} else {
|
||||
@ -137,7 +137,7 @@ DataTableView.prototype._onClickFirstPage = 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) {
|
||||
@ -172,15 +172,29 @@ DataTableView.prototype._createMenuForColumnHeader = function(column, index, elm
|
||||
submenu: [
|
||||
{
|
||||
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",
|
||||
click: function() {}
|
||||
},
|
||||
{
|
||||
label: "By Regular Expression",
|
||||
click: function() {}
|
||||
label: "By Custom Expression",
|
||||
click: function() {
|
||||
var expression = window.prompt("Enter expression", 'value');
|
||||
if (expression != null) {
|
||||
self._doFilterByExpression(column, expression);
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
label: "By Reconciliation Features",
|
||||
@ -282,6 +296,18 @@ DataTableView.prototype._doTextTransform = function(column, expression) {
|
||||
);
|
||||
};
|
||||
|
||||
DataTableView.prototype.update = function() {
|
||||
this._showRows(theProject.rowModel.start);
|
||||
DataTableView.prototype._doFilterByExpression = function(column, expression) {
|
||||
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) {
|
||||
this._div = div;
|
||||
this._div.mouseover(function() {
|
||||
this.style.height = "300px";
|
||||
}).mouseout(function() {
|
||||
this.style.height = "100px";
|
||||
});
|
||||
|
||||
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;
|
||||
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