Made our own slider widget to use in conjunction with our histogram widget.

git-svn-id: http://google-refine.googlecode.com/svn/trunk@503 7d457c2a-affb-35e4-300a-418c747d4874
This commit is contained in:
David Huynh 2010-04-19 23:27:57 +00:00
parent b9bbff325e
commit d1b0de95de
12 changed files with 344 additions and 254 deletions

Binary file not shown.

View File

@ -23,7 +23,6 @@ public class RangeFacet implements Facet {
protected String _name; // name of facet
protected String _expression; // expression to compute numeric value(s) per row
protected String _columnName; // column to base expression on, if any
protected String _mode; // "range", MIN, MAX
protected double _from; // the numeric selection
protected double _to;
@ -76,7 +75,6 @@ public class RangeFacet implements Facet {
writer.key("name"); writer.value(_name);
writer.key("expression"); writer.value(_expression);
writer.key("columnName"); writer.value(_columnName);
writer.key("mode"); writer.value(_mode);
if (_errorMessage != null) {
writer.key("error"); writer.value(_errorMessage);
@ -98,14 +96,8 @@ public class RangeFacet implements Facet {
}
writer.endArray();
if (MIN.equals(_mode)) {
writer.key(FROM); writer.value(_from);
} else if (MAX.equals(_mode)) {
writer.key(TO); writer.value(_to);
} else {
writer.key(FROM); writer.value(_from);
writer.key(TO); writer.value(_to);
}
writer.key(FROM); writer.value(_from);
writer.key(TO); writer.value(_to);
}
writer.key("baseNumericCount"); writer.value(_baseNumericCount);
@ -143,24 +135,9 @@ public class RangeFacet implements Facet {
_errorMessage = e.getMessage();
}
_mode = o.getString("mode");
if (MIN.equals(_mode)) {
if (o.has(FROM)) {
_from = o.getDouble(FROM);
_selected = true;
}
} else if (MAX.equals(_mode)) {
if (o.has(TO)) {
_to = o.getDouble(TO);
_selected = true;
}
} else {
if (o.has(FROM) && o.has(TO)) {
_from = o.getDouble(FROM);
_to = o.getDouble(TO);
_selected = true;
}
}
_from = o.getDouble(FROM);
_to = o.getDouble(TO);
_selected = true;
_selectNumeric = JSONUtilities.getBoolean(o, "selectNumeric", true);
_selectNonNumeric = JSONUtilities.getBoolean(o, "selectNonNumeric", true);
@ -174,31 +151,13 @@ public class RangeFacet implements Facet {
public RowFilter getRowFilter() {
if (_eval != null && _errorMessage == null && _selected) {
if (MIN.equals(_mode)) {
return new ExpressionNumberComparisonRowFilter(
_eval, _columnName, _cellIndex, _selectNumeric, _selectNonNumeric, _selectBlank, _selectError) {
protected boolean checkValue(double d) {
return d >= _from;
};
return new ExpressionNumberComparisonRowFilter(
_eval, _columnName, _cellIndex, _selectNumeric, _selectNonNumeric, _selectBlank, _selectError) {
protected boolean checkValue(double d) {
return d >= _from && d < _to;
};
} else if (MAX.equals(_mode)) {
return new ExpressionNumberComparisonRowFilter(
_eval, _columnName, _cellIndex, _selectNumeric, _selectNonNumeric, _selectBlank, _selectError) {
protected boolean checkValue(double d) {
return d < _to;
};
};
} else {
return new ExpressionNumberComparisonRowFilter(
_eval, _columnName, _cellIndex, _selectNumeric, _selectNonNumeric, _selectBlank, _selectError) {
protected boolean checkValue(double d) {
return d >= _from && d < _to;
};
};
}
};
} else {
return null;
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 750 B

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 718 B

After

Width:  |  Height:  |  Size: 2.9 KiB

File diff suppressed because one or more lines are too long

View File

@ -476,31 +476,28 @@ ClusteringDialog.Facet = function(dialog, title, property, elmt, clusters) {
elmt.addClass("clustering-dialog-facet");
var html = $(
'<div class="clustering-dialog-facet-header">' + title + '</div>' +
'<div class="clustering-dialog-facet-histogram" bind="histogramContainer"></div>' +
'<div class="clustering-dialog-facet-slider" bind="slider"></div>' +
'<div class="clustering-dialog-facet-slider" bind="sliderWidgetDiv">' +
'<div class="clustering-dialog-facet-histogram" bind="histogramContainer"></div>' +
'</div>' +
'<div class="clustering-dialog-facet-selection" bind="selectionContainer"></div>'
).appendTo(elmt);
this._elmts = DOM.bind(html);
this._histogram = new HistogramWidget(this._elmts.histogramContainer, { binColors: [ "#ccccff", "#6666ff" ] });
this._elmts.slider.slider({
min: this._min,
max: this._max,
values: [ this._from, this._to ],
slide: function(evt, ui) {
self._from = ui.values[0];
self._to = ui.values[1];
self._setRangeIndicators();
},
stop: function(evt, ui) {
self._from = ui.values[0];
self._to = ui.values[1];
self._setRangeIndicators();
self._dialog._updateAll();
}
this._sliderWidget = new SliderWidget(this._elmts.sliderWidgetDiv);
this._elmts.sliderWidgetDiv.bind("slide", function(evt, data) {
self._from = data.from;
self._to = data.to;
self._setRangeIndicators();
}).bind("stop", function(evt, data) {
self._from = data.from;
self._to = data.to;
self._setRangeIndicators();
self._dialog._updateAll();
});
this._setRangeIndicators();
}
};
@ -531,19 +528,23 @@ ClusteringDialog.Facet.prototype.update = function(clusters) {
var bins = this._computeDistribution(clusters);
this._sliderWidget.update(
this._min,
this._max,
this._step,
this._from,
this._to
);
this._histogram.update(
this._min,
this._max,
this._step,
[ this._baseBins, bins ],
this._from,
this._to
[ this._baseBins, bins ]
);
};
ClusteringDialog.Facet.prototype._setRangeIndicators = function() {
this._histogram.highlight(this._from, this._to);
this._elmts.selectionContainer.text(this._from + " to " + this._to);
this._elmts.selectionContainer.html(this._from + " &mdash; " + this._to);
};
ClusteringDialog.Facet.prototype._computeDistribution = function(clusters) {

View File

@ -26,21 +26,16 @@ function RangeFacet(div, config, options) {
}
RangeFacet.prototype.reset = function() {
switch (this._config.mode) {
case "min":
this._from = this._config.min;
this._sliderDiv.slider("value", this._from);
break;
case "max":
this._to = this._config.max;
this._sliderDiv.slider("value", this._to);
break;
default:
this._from = this._config.min;
this._to = this._config.max;
this._sliderDiv.slider("values", 0, this._from);
this._sliderDiv.slider("values", 1, this._to);
}
this._from = this._config.min;
this._to = this._config.max;
this._sliderWidget.update(
this._config.min,
this._config.max,
this._config.step,
this._from,
this._to
);
this._selectNumeric = true;
this._selectNonNumeric = true;
this._selectBlank = true;
@ -69,7 +64,6 @@ RangeFacet.prototype.getJSON = function() {
var o = {
type: "range",
name: this._config.name,
mode: this._config.mode,
expression: this._config.expression,
columnName: this._config.columnName,
selectNumeric: this._selectNumeric,
@ -78,15 +72,11 @@ RangeFacet.prototype.getJSON = function() {
selectError: this._selectError
};
if (this._config.mode == "min" || this._config.mode == "range") {
if (this._from !== null) {
o.from = this._from;
}
if (this._from !== null) {
o.from = this._from;
}
if (this._config.mode == "max" || this._config.mode == "range") {
if (this._to !== null) {
o.to = this._to;
}
if (this._to !== null) {
o.to = this._to;
}
return o;
@ -97,92 +87,59 @@ RangeFacet.prototype.hasSelection = function() {
return true;
}
switch (this._config.mode) {
case "min":
return this._from !== null && (!this._initializedUI || this._from > this._config.min);
case "max":
return this._to !== null && (!this._initializedUI || this._to < this._config.max);
default:
return (this._from !== null && (!this._initializedUI || this._from > this._config.min)) ||
(this._to !== null && (!this._initializedUI || this._to < this._config.max));
}
return (this._from !== null && (!this._initializedUI || this._from > this._config.min)) ||
(this._to !== null && (!this._initializedUI || this._to < this._config.max));
};
RangeFacet.prototype._initializeUI = function() {
var self = this;
var container = this._div.empty().show();
this._div
.empty()
.show()
.html(
'<div class="facet-title" bind="headerDiv">' +
'<img src="images/close.png" class="facet-choice-link" title="Remove this facet" bind="removeButton" />' +
'<a href="javascript:{}" class="facet-choice-link" bind="resetButton">reset</a>' +
'<span bind="facetTitle"></span>' +
'</div>' +
'<div class="facet-range-body">' +
'<div class="facet-range-message" bind="messageDiv">Loading...</div>' +
'<div class="facet-range-slider" bind="sliderWidgetDiv">' +
'<div class="facet-range-histogram" bind="histogramDiv"></div>' +
'</div>' +
'<div class="facet-range-status" bind="statusDiv"></div>' +
'<div class="facet-range-other-choices" bind="otherChoicesDiv"></div>' +
'</div>'
);
this._elmts = DOM.bind(this._div);
var headerDiv = $('<div></div>').addClass("facet-title").appendTo(container);
$('<span></span>').text(this._config.name).appendTo(headerDiv);
var resetButton = $('<a href="javascript:{}"></a>').addClass("facet-choice-link").text("reset").click(function() {
this._elmts.facetTitle.text(this._config.name);
this._elmts.resetButton.click(function() {
self.reset();
self._updateRest();
}).prependTo(headerDiv);
var removeButton = $('<img>')
.attr("src", "images/close.png")
.attr("title", "Remove this facet")
.addClass("facet-choice-link")
.click(function() {
});
this._elmts.removeButton.click(function() {
self._remove();
}).prependTo(headerDiv);
});
var bodyDiv = $('<div></div>').addClass("facet-range-body").appendTo(container);
this._histogram = new HistogramWidget(this._elmts.histogramDiv, { binColors: [ "#ccccff", "#6666ff" ] });
this._sliderWidget = new SliderWidget(this._elmts.sliderWidgetDiv);
this._messageDiv = $('<div>').text("Loading...").addClass("facet-range-message").appendTo(bodyDiv);
this._histogramDiv = $('<div>').addClass("facet-range-histogram").appendTo(bodyDiv);
this._sliderDiv = $('<div>').addClass("facet-range-slider").appendTo(bodyDiv);
this._statusDiv = $('<div>').addClass("facet-range-status").appendTo(bodyDiv);
this._otherChoicesDiv = $('<div>').addClass("facet-range-other-choices").appendTo(bodyDiv);
this._histogram = new HistogramWidget(this._histogramDiv, { binColors: [ "#ccccff", "#6666ff" ] });
var onSlide = function(event, ui) {
switch (self._config.mode) {
case "min":
self._from = ui.value;
break;
case "max":
self._to = ui.value;
break;
default:
self._from = ui.values[0];
self._to = ui.values[1];
}
this._elmts.sliderWidgetDiv.bind("slide", function(evt, data) {
self._from = data.from;
self._to = data.to;
self._setRangeIndicators();
};
var onStop = function() {
}).bind("stop", function(evt, data) {
self._from = data.from;
self._to = data.to;
self._selectNumeric = true;
self._updateRest();
};
var sliderConfig = {
min: this._config.min,
max: this._config.max,
stop: onStop,
slide: onSlide
};
switch (this._config.mode) {
case "min":
sliderConfig.range = "max";
sliderConfig.value = this._config.min;
break;
case "max":
sliderConfig.range = "min";
sliderConfig.value = this._config.max;
break;
default:
sliderConfig.range = true;
sliderConfig.values = [ this._config.min, this._config.max ];
}
this._sliderDiv.slider(sliderConfig);
});
};
RangeFacet.prototype._renderOtherChoices = function() {
var self = this;
var container = this._otherChoicesDiv.empty();
var container = this._elmts.otherChoicesDiv.empty();
if (this._baseNonNumericCount === 0 && this._baseBlankCount === 0 && this._baseErrorCount === 0) {
return;
@ -274,20 +231,7 @@ RangeFacet.prototype._renderOtherChoices = function() {
};
RangeFacet.prototype._setRangeIndicators = function() {
var text;
switch (this._config.mode) {
case "min":
text = "At least " + this._from;
break;
case "max":
text = "At most " + this._to;
break;
default:
text = this._from + " to " + this._to;
}
this._statusDiv.text(text);
this._histogram.highlight(this._from, this._to);
this._elmts.statusDiv.html(this._from + " &mdash; " + this._to);
};
RangeFacet.prototype.updateState = function(data) {
@ -340,31 +284,32 @@ RangeFacet.prototype.render = function() {
}
if (this._error) {
this._messageDiv.text(this._errorMessage).show();
this._sliderDiv.hide();
this._histogramDiv.hide();
this._statusDiv.hide();
this._otherChoicesDiv.hide();
this._elmts.messageDiv.text(this._errorMessage).show();
this._elmts.sliderWidgetDiv.hide();
this._elmts.histogramDiv.hide();
this._elmts.statusDiv.hide();
this._elmts.otherChoicesDiv.hide();
return;
}
this._messageDiv.hide();
this._sliderDiv.show();
this._histogramDiv.show();
this._statusDiv.show();
this._otherChoicesDiv.show();
this._sliderDiv.slider("option", "min", this._config.min);
this._sliderDiv.slider("option", "max", this._config.max);
this._sliderDiv.slider("option", "step", this._config.step);
this._elmts.messageDiv.hide();
this._elmts.sliderWidgetDiv.show();
this._elmts.histogramDiv.show();
this._elmts.statusDiv.show();
this._elmts.otherChoicesDiv.show();
this._sliderWidget.update(
this._config.min,
this._config.max,
this._config.step,
this._from,
this._to
);
this._histogram.update(
this._config.min,
this._config.max,
this._config.step,
[ this._baseBins, this._bins ],
this._from,
this._to
[ this._baseBins, this._bins ]
);
this._setRangeIndicators();

View File

@ -4,21 +4,18 @@ function HistogramWidget(elmt, options) {
this._range = null;
this._binMatrix = null;
this._highlight = null;
this._initializeUI();
}
HistogramWidget.prototype.highlight = function(from, to) {
this._highlight = { from: from, to: to };
this._update();
};
HistogramWidget.prototype.update = function(min, max, step, binMatrix, from, to) {
if (typeof min == "undefined" || typeof binMatrix == "undefined" || binMatrix.length === 0 || binMatrix[0].length === 0) {
HistogramWidget.prototype.update = function(min, max, step, binMatrix) {
if (typeof min == "undefined" ||
typeof binMatrix == "undefined" ||
binMatrix.length === 0 ||
binMatrix[0].length === 0) {
this._range = null;
this._binMatrix = null;
this._highlight = null;
this._elmt.hide();
} else {
@ -33,21 +30,12 @@ HistogramWidget.prototype.update = function(min, max, step, binMatrix, from, to)
}
}
if (typeof from != "undefined" && typeof to != "undefined") {
this._highlight = { from: from, to: to };
}
this._update();
}
};
HistogramWidget.prototype._update = function() {
if (this._binMatrix !== null) {
if (this._highlight !== null) {
this._highlight.from = Math.max(this._highlight.from, this._range.min);
this._highlight.to = Math.min(this._highlight.to, this._range.max);
}
this._elmt.show();
this._resize();
this._render();
@ -67,8 +55,11 @@ HistogramWidget.prototype._initializeUI = function() {
};
HistogramWidget.prototype._resize = function() {
this._elmts.canvas.attr("height", "height" in this._options ? this._options.height : 50);
var height = "height" in this._options ? this._options.height : 50;
this._elmts.canvas.attr("height", height);
this._elmts.canvas.attr("width", this._elmts.canvas.width());
this._elmt.height(height);
};
HistogramWidget.prototype._render = function() {
@ -132,29 +123,5 @@ HistogramWidget.prototype._render = function() {
);
}
/*
* Draw highlight
*/
if (this._highlight !== null) {
ctx.fillStyle = "rgba(192,192,192, 0.5)";
ctx.globalCompositeOperation = "source-over";
if (this._highlight.from > this._range.min) {
ctx.fillRect(
0,
0,
(this._highlight.from - this._range.min) * stepScale,
canvas.height
);
}
if (this._highlight.to < this._range.max) {
ctx.fillRect(
(this._highlight.to - this._range.min) * stepScale,
0,
canvas.width - (this._highlight.to - this._range.min) * stepScale,
canvas.height
);
}
}
ctx.restore();
};

View File

@ -0,0 +1,180 @@
function SliderWidget(elmt, options) {
this._elmt = elmt;
this._options = options || {};
this._range = {
min: 0,
max: 1,
step: 1,
from: 0,
to: 0
};
this._drag = null;
this._initializeUI();
this._update();
}
SliderWidget.prototype.update = function(min, max, step, from, to) {
if (step <= 0) {
step = 1;
}
max = Math.max(max, min + step);
from = Math.max(min, from);
to = Math.min(max, to);
this._range = {
min: min,
max: max,
step: step,
from: from,
to: to
};
this._update();
}
SliderWidget.prototype._initializeUI = function() {
this._elmt.addClass("slider-widget");
this._leftTintedRect = $("<div>").addClass("slider-widget-tint left").appendTo(this._elmt);
this._rightTintedRect = $("<div>").addClass("slider-widget-tint right").appendTo(this._elmt);
this._highlightRect = $("<div>").addClass("slider-widget-highlight slider-widget-draggable").attr("part", "highlight").appendTo(this._elmt);
this._leftBracket = $("<div>").addClass("slider-widget-bracket slider-widget-draggable left").attr("part", "left").appendTo(this._elmt);
this._rightBracket = $("<div>").addClass("slider-widget-bracket slider-widget-draggable right").attr("part", "right").appendTo(this._elmt);
var self = this;
this._elmt.find(".slider-widget-draggable")
.mousedown(function(evt) {
return self._onMouseDown(evt, this.getAttribute("part"));
});
this._highlightRect.dblclick(function(evt) {
if (self._range.from > self._range.min || self._range.to < self._range.max) {
self._range.from = self._range.min;
self._range.to = self._range.max;
self._update();
self._trigger("stop");
}
});
this._elmt
.mousemove(function(evt) {
return self._onMouseMove(evt);
})
.mouseup(function(evt) {
return self._onMouseUp(evt);
});
};
SliderWidget.prototype._onMouseDown = function(evt, part) {
if (this._drag) {
return;
}
this._drag = {
sureDrag: false
};
if ("highlight" == part) {
this._drag.elmt = this._highlightRect;
this._drag.value = this._range.from;
} else if ("left" == part) {
this._drag.elmt = this._leftBracket;
} else if ("right" == part) {
this._drag.elmt = this._rightBracket;
}
this._drag.what = part;
this._drag.from = this._range.from;
this._drag.to = this._range.to;
this._drag.down = {
x: evt.pageX,
y: evt.pageY
};
};
SliderWidget.prototype._onMouseUp = function(evt) {
if (!(this._drag)) {
return;
}
if (this._drag.sureDrag) {
this._update();
this._trigger("stop");
}
this._drag = null;
};
SliderWidget.prototype._trigger = function(eventName) {
this._elmt.trigger(eventName, [{ from: this._range.from, to: this._range.to }]);
};
SliderWidget.prototype._onMouseMove = function(evt) {
if (!(this._drag)) {
return;
}
var drag = this._drag;
var range = this._range;
var offset = this._elmt.offset();
var xDiff = evt.pageX - drag.down.x;
var yDiff = evt.pageX - drag.down.y;
if (Math.abs(xDiff) >= 2) {
drag.sureDrag = true;
}
var pixelWidth = this._elmt.width();
var scale = pixelWidth / (range.max - range.min);
var vDiff = xDiff / scale;
var adjustFrom = function() {
range.from = drag.from + Math.floor(vDiff / range.step) * range.step;
range.from = Math.max(Math.min(range.from, range.max), range.min);
};
var adjustTo = function() {
range.to = drag.to + Math.floor(vDiff / range.step) * range.step;
range.to = Math.max(Math.min(range.to, range.max), range.min);
};
if (drag.what == "left") {
adjustFrom();
range.to = Math.min(Math.max(range.to, range.from + range.step), range.max);
} else if (drag.what == "right") {
adjustTo();
range.from = Math.max(Math.min(range.from, range.to - range.step), range.min);
} else {
adjustFrom();
adjustTo();
}
this._update();
this._trigger("slide");
evt.preventDefault();
return false;
};
SliderWidget.prototype._update = function() {
var range = this._range;
var pixelWidth = this._elmt.width();
var scale = pixelWidth / (range.max - range.min);
var valueToPixel = function(x) {
return (x - range.min) * scale;
};
var fromPixel = Math.floor(valueToPixel(range.from));
var toPixel = Math.floor(valueToPixel(range.to));
if (range.from == range.min && range.to == range.max) {
this._leftTintedRect.hide();
this._rightTintedRect.hide();
} else {
this._leftTintedRect.show().width(fromPixel);
this._rightTintedRect.show().width(pixelWidth - toPixel);
}
this._highlightRect.css("left", (fromPixel - 1) + "px").width(toPixel - fromPixel);
this._leftBracket.css("left", fromPixel + "px");
this._rightBracket.css("left", toPixel + "px");
};

View File

@ -64,11 +64,10 @@ table.clustering-dialog-entry-table a:hover {
font-weight: bold;
}
.clustering-dialog-facet-histogram {
margin: 10px;
overflow: hidden;
}
.clustering-dialog-facet-slider {
margin: 5px 10px;
margin: 5px;
}
.clustering-dialog-facet-selection {
text-align: center;

View File

@ -143,21 +143,15 @@ img.facet-choice-link {
color: #f88;
}
.facet-range-histogram {
margin: 10px 0px;
overflow: hidden;
}
.facet-range-slider.ui-corner-all {
border-bottom-left-radius: 0px 0px;
border-bottom-right-radius: 0px 0px;
border-top-left-radius: 0px 0px;
border-top-right-radius: 0px 0px;
.facet-range-slider {
margin: 0px;
}
.facet-range-status {
margin: 10px 0;
margin: 5px 0;
text-align: center;
color: #aaa;
}
.facet-text-body {

View File

@ -0,0 +1,45 @@
.slider-widget {
position: relative;
overflow: visible;
margin-left: 12px;
margin-right: 12px;
}
.slider-widget-tint {
position: absolute;
top: 0px;
height: 100%;
background: black;
opacity: 0.4;
display: none;
}
.slider-widget-tint.left {
left: 0px;
}
.slider-widget-tint.right {
right: 0px;
}
.slider-widget-highlight {
position: absolute;
padding: 1px 0px;
border: 1px solid #faa;
top: -2px;
height: 100%;
cursor: move;
}
.slider-widget-bracket {
position: absolute;
width: 12px;
top: 0px;
height: 100%;
}
.slider-widget-bracket.left {
background: url(../../images/slider-left-bracket.png) no-repeat center right;
margin-left: -14px;
cursor: e-resize;
}
.slider-widget-bracket.right {
background: url(../../images/slider-right-bracket.png) no-repeat center left;
margin-left: 2px;
cursor: w-resize;
}