500 lines
14 KiB
JavaScript
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);
|
|
}
|
|
});
|