vitro/webapp/web/src/widget/html/Slider.js

500 lines
14 KiB
JavaScript

/*
Copyright (c) 2004-2006, The Dojo Foundation
All Rights Reserved.
Licensed under the Academic Free License version 2.1 or above OR the
modified BSD license. For more information on Dojo licensing, see:
http://dojotoolkit.org/community/licensing.shtml
*/
/**
* Slider Widget.
*
* The slider widget comes in three forms:
* 1. Base Slider widget which supports movement in x and y dimensions
* 2. Vertical Slider (SliderVertical) widget which supports movement
* only in the y dimension.
* 3. Horizontal Slider (SliderHorizontal) widget which supports movement
* only in the x dimension.
*
* The key objects in the widget are:
* - a container div which displays a bar in the background (Slider object)
* - a handle inside the container div, which represents the value
* (sliderHandle DOM node)
* - the object which moves the handle (handleMove is of type
* SliderDragMoveSource)
*
* The values for the slider are calculated by grouping pixels together,
* based on the number of values to be represented by the slider.
* The number of pixels in a group is called the valueSize
* e.g. if slider is 150 pixels long, and is representing the values
* 0,1,...10 then pixels are grouped into lots of 15 (valueSize), where:
* value 0 maps to pixels 0 - 7
* 1 8 - 22
* 2 23 - 37 etc.
* The accuracy of the slider is limited to the number of pixels
* (i.e tiles > pixels will result in the slider not being able to
* represent some values).
*
* Technical Notes:
* - 3 widgets exist because the framework caches the template in
* dojo.widget.fillFromTemplateCache (which ignores the changed URI)
*
* Todo:
* - Setting of initial value currently doesn't work, because the one or
* more of the offsets, constraints or containing block are not set and
* are required to set the valueSize is not set before fillInTemplate
* is called.
* - Issues with dragging handle when page has been scrolled
* -
*
* References (aka sources of inspiration):
* - http://dojotoolkit.org/docs/fast_widget_authoring.html
* - http://dojotoolkit.org/docs/dojo_event_system.html
*
* @author Marcel Linnenfelser (m.linnen@synflag.de)
* @author Mathew Pole (mathew.pole@ebor.com)
*
* $Id: $
*/
// tell the package system what functionality is provided in this module (file)
// (note that the package system works on modules, not the classes)
dojo.provide("dojo.widget.html.Slider");
// load dependencies
dojo.require("dojo.event.*");
dojo.require("dojo.dnd.*");
// dojo.dnd.* doesn't include this package, because it's not in __package__.js
dojo.require("dojo.dnd.HtmlDragMove");
dojo.require("dojo.widget.*");
dojo.require("dojo.style");
/**
* Define the two dimensional slider widget class.
*/
dojo.widget.defineWidget (
"dojo.widget.html.Slider",
dojo.widget.HtmlWidget,
{
// over-ride some defaults
isContainer: false,
widgetType: "Slider",
// useful properties (specified as attributes in the html tag)
// number of values to be represented by slider in the horizontal direction
valuesX: 10,
// number of values to be represented by slider in the vertical direction
valuesY: 10,
// can values be changed on the x (horizontal) axis?
isEnableX: true,
// can values be changed on the y (vertical) axis?
isEnableY: true,
// value size (pixels) in the x dimension
valueSizeX: 0.0,
// value size (pixels) in the y dimension
valueSizeY: 0.0,
// initial value in the x dimension
initialValueX: 0,
// initial value in the y dimension
initialValueY: 0,
// do we allow the user to click on the slider to set the position?
// (note: dojo's infrastructor will convert attribute to a boolean)
clickSelect: true,
// should the handle snap to the grid or remain where it was dragged to?
// (note: dojo's infrastructor will convert attribute to a boolean)
snapToGrid: false,
// should the value change while you are dragging, or just after drag finishes?
activeDrag: false,
templateCssPath: dojo.uri.dojoUri ("src/widget/templates/HtmlSlider.css"),
templatePath: dojo.uri.dojoUri ("src/widget/templates/HtmlSlider.html"),
// our DOM nodes
sliderHandle: null,
// private attributes
// This is set to true when a drag is started, so that it is not confused
// with a click
isDragInProgress: false,
// This function is called when the template is loaded
fillInTemplate: function ()
{
// dojo.debug ("fillInTemplate - className = " + this.domNode.className);
// setup drag-n-drop for the sliderHandle
this.handleMove = new dojo.widget.html.SliderDragMoveSource (this.sliderHandle);
this.handleMove.setParent (this);
dojo.event.connect(this.handleMove, "onDragMove", this, "onDragMove");
dojo.event.connect(this.handleMove, "onDragEnd", this, "onDragEnd");
dojo.event.connect(this.handleMove, "onClick", this, "onClick");
// keep the slider handle inside it's parent container
this.handleMove.constrainToContainer = true;
if (this.clickSelect) {
dojo.event.connect (this.domNode, "onclick", this, "setPosition");
}
if (this.isEnableX && this.initialValueX > 0) {
alert("setting x to " + this.initialValueX);
this.setValueX (this.initialValueX);
}
if (this.isEnableY && this.initialValueY > 0) {
this.setValueY (this.initialValueY);
}
},
// Move the handle (in the x dimension) to the specified value
setValueX: function (value) {
if (0.0 == this.valueSizeX) {
this.valueSizeX = this.handleMove.calcValueSizeX ();
}
if (value > this.valuesX) {
value = this.valuesX;
}
else if (value < 0) {
value = 0;
}
//dojo.debug ("value = " + value, ", valueSizeX = " + this.valueSizeX);
this.handleMove.domNode.style.left = (value * this.valueSizeX) + "px";
},
// Get the number of the value that matches the position of the handle
getValueX: function () {
if (0.0 == this.valueSizeX) {
this.valueSizeX = this.handleMove.calcValueSizeX ();
}
return Math.round (dojo.style.getPixelValue (this.handleMove.domNode, "left") / this.valueSizeX);
},
// set the slider to a particular value
setValueY: function (value) {
if (0.0 == this.valueSizeY) {
this.valueSizeY = this.handleMove.calcValueSizeY ();
}
if (value > this.valuesY) {
value = this.valuesY;
}
else if (value < 0) {
value = 0;
}
this.handleMove.domNode.style.top = (value * this.valueSizeY) + "px";
},
// Get the number of the value that the matches the position of the handle
getValueY: function () {
if (0.0 == this.valueSizeY) {
this.valueSizeY = this.handleMove.calcValueSizeY ();
}
return Math.round (dojo.style.getPixelValue (this.handleMove.domNode, "top") / this.valueSizeY);
},
// set the position of the handle
setPosition: function (e) {
//dojo.debug ("Slider#setPosition - e.clientX = " + e.clientX
// + ", e.clientY = " + e.clientY);
if (this.isDragInProgress) {
this.isDragInProgress = false;
}
var offset = dojo.html.getScrollOffset();
var parent = dojo.style.getAbsolutePosition(this.domNode, true);
if (this.isEnableX) {
var x = offset.x + e.clientX - parent.x;
if (x > this.domNode.offsetWidth) {
x = this.domNode.offsetWidth;
}
if (this.snapToGrid && x > 0) {
if (0.0 == this.valueSizeX) {
this.valueSizeX = this.handleMove.calcValueSizeX ();
}
x = this.valueSizeX * (Math.round (x / this.valueSizeX));
}
this.handleMove.domNode.style.left = x + "px";
}
if (this.isEnableY) {
var y = offset.y + e.clientY - parent.y;
if (y > this.domNode.offsetHeight) {
y = this.domNode.offsetHeight;
}
if (this.snapToGrid && y > 0) {
if (0.0 == this.valueSizeY) {
this.valueSizeY = this.handleMove.calcValueSizeY ();
}
y = this.valueSizeY * (Math.round (y / this.valueSizeY));
}
this.handleMove.domNode.style.top = y + "px";
}
},
onDragMove: function(){
this.onValueChanged(this.getValueX(), this.getValueY());
},
onClick: function(){
this.onValueChanged(this.getValueX(), this.getValueY());
},
onValueChanged: function(x, y){
}
}
);
/* ------------------------------------------------------------------------- */
/**
* Define the horizontal slider widget class.
*/
dojo.widget.defineWidget (
"dojo.widget.html.SliderHorizontal",
dojo.widget.html.Slider,
{
widgetType: "SliderHorizontal",
value: 0,
isEnableY: false,
templatePath: dojo.uri.dojoUri ("src/widget/templates/HtmlSliderHorizontal.html"),
postMixInProperties: function(){
this.initialValue = this.value;
},
// wrapper for getValueX
getValue: function () {
return this.getValueX ();
},
// wrapper for setValueX
setValue: function (value) {
this.setValueX (value);
this.onValueChanged(value);
},
onDragMove: function(){
if(this.activeDrag){
this.onValueChanged(this.getValue());
}
},
onDragEnd: function(){
if(!this.activeDrag){
this.onValueChanged(this.getValue());
}
},
onClick: function(){
this.onValueChanged(this.getValue());
},
onValueChanged: function(value){
this.value=value;
}
}
);
/* ------------------------------------------------------------------------- */
/**
* Define the vertical slider widget class.
*/
dojo.widget.defineWidget (
"dojo.widget.html.SliderVertical",
dojo.widget.html.Slider,
{
widgetType: "SliderVertical",
value: 0,
isEnableX: false,
templatePath: dojo.uri.dojoUri ("src/widget/templates/HtmlSliderVertical.html"),
postMixInProperties: function(){
this.initialValueY = this.value;
},
// wrapper for getValueY
getValue: function () {
return this.getValueY ();
},
// wrapper for setValueY
setValue: function (value) {
this.setValueY (value);
},
onDragMove: function(){
if(this.activeDrag){
this.onValueChanged(this.getValue());
}
},
onDragEnd: function(){
if(!this.activeDrag){
this.onValueChanged(this.getValue());
}
},
onClick: function(){
this.onValueChanged(this.getValue());
},
onValueChanged: function(value){
this.value=value;
}
}
);
/* ------------------------------------------------------------------------- */
/**
* This class extends the HtmlDragMoveSource class to provide
* features for the slider handle.
*/
dojo.declare (
"dojo.widget.html.SliderDragMoveSource",
dojo.dnd.HtmlDragMoveSource,
{
isDragInProgress: false,
slider: null,
/** Setup the handle for drag
* Extends dojo.dnd.HtmlDragMoveSource by creating a SliderDragMoveSource */
onDragStart: function (e) {
this.isDragInProgress = true;
this.constrainToContainer = true;
var dragObj = this.createDragMoveObject ();
var constraints = null;
dojo.event.connect (dragObj, "onDragMove", this, "onDragMove");
return dragObj;
},
onDragMove: function (e) {
// placeholder to enable event connection
},
createDragMoveObject: function () {
//dojo.debug ("SliderDragMoveSource#createDragMoveObject - " + this.slider);
var dragObj = new dojo.widget.html.SliderDragMoveObject (this.dragObject, this.type);
dragObj.slider = this.slider;
// this code copied from dojo.dnd.HtmlDragSource#onDragStart
if (this.dragClass) {
dragObj.dragClass = this.dragClass;
}
if (this.constrainToContainer) {
dragObj.constrainTo(this.constrainingContainer || this.domNode.parentNode);
}
return dragObj;
},
setParent: function (slider) {
this.slider = slider;
},
calcValueSizeX: function () {
var dragObj = this.createDragMoveObject ();
dragObj.containingBlockPosition = dragObj.domNode.offsetParent ?
dojo.style.getAbsolutePosition(dragObj.domNode.offsetParent) : {x:0, y:0};
var constraints = dragObj.getConstraints ();
return (constraints.maxX - constraints.minX) / this.slider.valuesX;
},
calcValueSizeY: function () {
var dragObj = this.createDragMoveObject ();
dragObj.containingBlockPosition = dragObj.domNode.offsetParent ?
dojo.style.getAbsolutePosition(dragObj.domNode.offsetParent) : {x:0, y:0};
var constraints = dragObj.getConstraints ();
return (constraints.maxY - constraints.minY) / this.slider.valuesY;
}
});
/* ------------------------------------------------------------------------- */
/**
* This class extends the HtmlDragMoveObject class to provide
* features for the slider handle.
*/
dojo.declare (
"dojo.widget.html.SliderDragMoveObject",
dojo.dnd.HtmlDragMoveObject,
{
// reference to dojo.widget.html.Slider
slider: null,
/** Moves the node to follow the mouse.
* Extends functon HtmlDragObject by adding functionality to snap handle
* to a discrete value */
onDragMove: function (e) {
if (this.slider.isEnableX && 0.0 == this.slider.valueSizeX) {
this.slider.valueSizeX = (this.constraints.maxX - this.constraints.minX) / this.slider.valuesX;
}
if (this.slider.isEnableY && 0.0 == this.slider.valueSizeY) {
this.slider.valueSizeY = (this.constraints.maxY - this.constraints.minY) / this.slider.valuesY;
}
this.updateDragOffset ();
var x = this.dragOffset.x + e.pageX;
var y = this.dragOffset.y + e.pageY;
if (this.constrainToContainer) {
if (x < this.constraints.minX) { x = this.constraints.minX; }
if (y < this.constraints.minY) { y = this.constraints.minY; }
if (x > this.constraints.maxX) { x = this.constraints.maxX; }
if (y > this.constraints.maxY) { y = this.constraints.maxY; }
}
if (this.slider.isEnableX) {
var selectedValue = 0;
if (x > 0) {
selectedValue = Math.round (x / this.slider.valueSizeX);
}
// dojo.debug ("x = " + x + ", valueSize = " + valueSize
// + ", selectedValue = " + selectedValue);
x = (selectedValue * this.slider.valueSizeX);
}
if (this.slider.isEnableY) {
var selectedValue = 0;
if (y > 0) {
selectedValue = Math.round (y / this.slider.valueSizeY);
}
y = (selectedValue * this.slider.valueSizeY);
}
this.setAbsolutePosition (x, y);
}
});