com.smartclient.debug.public.sc.client.widgets.Slider.js Maven / Gradle / Ivy
Show all versions of smartgwt Show documentation
/*
* Isomorphic SmartClient
* Version SC_SNAPSHOT-2011-08-08 (2011-08-08)
* Copyright(c) 1998 and beyond Isomorphic Software, Inc. All rights reserved.
* "SmartClient" is a trademark of Isomorphic Software, Inc.
*
* [email protected]
*
* http://smartclient.com/license
*/
/*----------> isc.Slider.js <----------*/
// The Slider class was developed as an instructional/documentation example of
// creating a new widget class, covering a broad range of ISC client-side
// framework concepts and conventions.
// Questions: [email protected]
//---------- Description ----------\\
//> @class Slider
// The Slider class implements a GUI slider widget allowing the user to select a numeric
// value from within a range by dragging a visual indicator up and down a track.
//
// The slider will generate events as the user interacts with it and changes its value.
// If slider.sliderTarget is specified, moving the slider thumb generates a custom
// event named 'sliderMove', sent to the sliderTarget.
// If a sliderMove
handler stringMethod is defined on the target, it will be
// fired when the slider is moved. The second parameter (available via the variable name
// eventInfo
if the handler is a string) is a pointer back to the slider.
//
// The slider will also fire a valueChanged()
method whenever its value is
// changed. This can be observed or overridden on the Slider instance to perform some action.
//
// @treeLocation Client Reference/Control
// @visibility external
// @example slider
//<
//---------- Create the class ----------\\
isc.ClassFactory.defineClass("Slider", isc.Canvas);
//---------- Define static properties ----------\\
isc.Slider.addClassProperties({
// isc.Slider.DOWN down state for the slider thumb
DOWN:"down",
// isc.Slider.UP up (enabled) state for the slider thumb
UP:"",
// isc.Slider.EVENTNAME name of event sent to sliderTarget when thumb moved
EVENTNAME:"sliderMove"
});
//---------- Define instance properties ----------\\
isc.Slider.addProperties({
//> @attr slider.title (String : "Set Value" : [IRW])
// Optional display title for the slider.
// @see attr:showTitle
// @visibility external
//<
title:"Set Value",
//> @attr slider.length (integer : 200 : [IRW])
// Used to set slider height if vertical, slider width if horizontal.
// Applied to the slider track, not necessarily the entire widget.
// Overridden by an explicit width/height specification for the widget.
// @visibility external
//<
length:200,
//> @attr slider.vertical (boolean : true : [IRW])
// Indicates whether this is a vertical or horizontal slider.
// @visibility external
// @example slider
//<
vertical:true,
//> @attr slider.thumbThickWidth (integer : 23 : [IRW])
// The dimension of the thumb perpendicular to the slider track.
// @visibility external
//<
thumbThickWidth:23,
//> @attr slider.thumbThinWidth (integer : 17 : [IRW])
// The dimension of the thumb parallel to the slider track.
// @visibility external
//<
thumbThinWidth:17,
//> @attr slider.trackWidth (integer : 7 : [IRW])
// The thickness of the track. This is the width, for a vertical slider, or the height, for
// a horizontal slider.
// @visibility external
//<
trackWidth:7,
//> @attr slider.hThumbStyle (CSSStyleName : null : IR)
// Optional CSS style for the thumb for a horizontally oriented slider.
//
// Will have the suffix "down" added when the mouse is down on the thumb, and "Disabled"
// added when the slider is disabled.
//
// @visibility external
//<
//> @attr slider.vThumbStyle (CSSStyleName : null : IR)
// Optional CSS style for the thumb for a vertically oriented slider. See
// +link{hThumbStyle} for state suffixes.
// @visibility external
//<
//> @attr slider.hTrackStyle (CSSStyleName : null : IR)
// Optional CSS style for the track for a horizontally oriented slider.
//
// Will have the suffix "Disabled" added when the slider is disabled.
//
// @visibility external
//<
//> @attr slider.vTrackStyle (CSSStyleName : null : IR)
// Optional CSS style for the track for a vertically oriented slider.
//
// Will have the suffix "Disabled" added when the slider is disabled.
// @visibility external
//<
// skinImgDir subdirectory for slider skin images
skinImgDir:"images/Slider/",
//> @attr slider.thumbSrc (String : "thumb.gif" : [IRW])
// The base filename for the slider thumb images.
// The filenames for the thumb icons are assembled from this base filename and the state of the
// thumb, as follows:
// Assume the thumbSrc is set to {baseName}.{extension}
// The full set of images to be displayed is:
// For horizontal sliders:
//
// h{baseName}.{extension}
: default enabled appearance.
// h{baseName}_down.{extension}
: appearance when the slider is enabled and the
// thumb is clicked.
// h{baseName}_Disabled.{extension}
: appearance when the slider is disabled.
//
// For vertical sliders:
//
// v{baseName}.{extension}
: default enabled appearance.
// v{baseName}_down.{extension}
: appearance when the slider is enabled and the
// thumb is clicked.
// v{baseName}_Disabled.{extension}
: appearance when the slider is disabled.
//
// @visibility external
//<
thumbSrc:"thumb.gif",
//> @attr slider.trackSrc (String : "track.gif" : [IRW])
// The base filename for the slider track images.
// The filenames for the track icons are assembled from this base filename and the state of the
// slider, as follows:
// Assume the trackSrc is set to {baseName}.{extension}
// The full set of images to be displayed is:
// For horizontal sliders:
//
// h{baseName}_start.{extension}
: start (left edge) of the track for a slider
// that is enabled.
// h{baseName}_stretch.{extension}
: the track for an enabled slider; this may
// be centered, tiled, or stretched.
// h{baseName}_end.{extension}
: end (right edge) of the track for a slider
// that is enabled.
// h{baseName}_Disabled_start.{extension}
: start (left edge) of the track for a slider
// that is disabled.
// h{baseName}_Disabled_stretch.{extension}
: the track for a disabled slider; this
// may be centered, tiled, or stretched.
// h{baseName}_Disabled_end.{extension}
: end (right edge) of the track for a slider
// that is disabled.
//
// For vertical sliders:
//
// v{baseName}_start.{extension}
: start (bottom edge) of the track for a slider
// that is enabled.
// v{baseName}_stretch.{extension}
: the track for an enabled slider; this may
// be centered, tiled, or stretched.
// v{baseName}_end.{extension}
: end (top edge) of the track for a slider
// that is enabled.
// v{baseName}_Disabled_start.{extension}
: start (bottom edge) of the track for a slider
// that is disabled.
// v{baseName}_Disabled_stretch.{extension}
: the track for a disabled slider; this
// may be centered, tiled, or stretched.
// v{baseName}_end.{extension}
: end (top edge) of the track for a slider
// that is disabled.
//
// @see attr:trackImageType
// @visibility external
//<
trackSrc:"track.gif",
//> @attr slider.trackCapSize (integer : 6 : [IRW])
// The height of vertical slider start and end images, or width of horizontal slider start and
// end images.
// @visibility external
//<
trackCapSize:6,
//> @attr slider.trackImageType (ImageStyle : "stretch" : [IRW])
// The imageType setting for the slider track.
// @see type:ImageStyle
// @see attr:stretchImg.imageType
// @visibility external
//<
trackImageType:isc.Img.STRETCH,
//> @attr slider.showTitle (boolean : true : [IRW])
// Indicates whether the slider's title should be displayed. The default position for this label
// is to the left of a horizontal slider, or above a vertical slider.
// @see attr:title
// @visibility external
//<
showTitle:true,
//> @attr slider.showRange (boolean : true : [IRW])
// Indicates whether labels for the min and max values of the slider should be displayed. The
// default positions for these labels are below the start/end of a horizontal slider, or to the
// right of the start/end of a vertical slider.
// @see attr:minValueLabel
// @see attr:maxValueLabel
// @visibility external
//<
showRange:true,
//> @attr slider.showValue (boolean : true : [IRW])
// Indicates whether a label for the value of the slider should be displayed. The
// default position for this label is to the right of a horizontal slider, or below a vertical
// slider.
// @see attr:value
// @visibility external
//<
showValue:true,
//> @attr slider.labelWidth (integer : 50 : [IRW])
// The width of the labels used to display the minimum, maximum and current values of the
// slider.
// @visibility external
//<
labelWidth:50,
//> @attr slider.labelHeight (integer : 20 : [IRW])
// The height of the labels used to display the minimum, maximum and current values of the
// slider.
// @visibility external
//<
labelHeight:20,
//> @attr slider.labelSpacing (integer : 5 : [IRW])
// The space around the labels used to display the minimum, maximum and current values of the
// slider.
// @visibility external
//<
labelSpacing:5,
titleStyle:"sliderTitle",
rangeStyle:"sliderRange",
valueStyle:"sliderValue",
//XXX need to create and use these CSS styles
//XXX need mechanism for overriding default layouts
//> @attr slider.value (float : 1 : [IRW])
// The slider value. This value should lie between the minValue and maxValue and increases as
// the thumb is moved up (for a vertical slider) or right (for a horizontal slider) unless
// flipValues is set to true.
// @see attr:minValue
// @see attr:maxValue
// @see attr:flipValues
// @see attr:showValue
// @visibility external
//<
value:1,
//> @attr slider.minValue (float : 1 : [IRW])
// The minimum slider value. The slider value is equal to minValue when the thumb is at the
// bottom or left of the slider (unless flipValues is true, in which case the minimum value
// is at the top/right of the slider)
// @see attr:slider.flipValues
// @visibility external
// @example slider
//<
minValue:1,
//> @attr slider.minValueLabel (String : null : [IRW])
// The text displayed in the label for the minimum value of the slider. If left as null, then
// slider.minValue will be displayed.
// @see attr:showRange
// @see attr:minValue
// @visibility external
//<
//> @attr slider.maxValue (float : 100 : [IRW])
// The maximum slider value. The slider value is equal to maxValue when the thumb is at the
// top or right of the slider (unless flipValues is true, in which case the maximum value
// is at the bottom/left of the slider)
// @see attr:slider.flipValues
// @visibility external
// @example slider
//<
maxValue:100,
//> @attr slider.maxValueLabel (String : null : [IRW])
// The text displayed in the label for the maximum value of the slider. If left as null, then
// slider.maxValue will be displayed.
// @see attr:showRange
// @see attr:maxValue
// @visibility external
//<
//> @attr slider.numValues (integer : null : [IRW])
// The number of discrete values represented by slider. If specified, the range of valid
// values (between minValue
and maxValue
) will be divided into
// this many steps. As the thumb is moved along the track it will only select these values
// and appear to jump between the steps.
// @visibility external
// @example slider
//<
//> @attr slider.roundValues (boolean : true : [IRW])
// Specifies whether the slider value should be rounded to the nearest integer. If set to
// false, values will be rounded to a fixed number of decimal places controlled by
// +link{roundPrecision}.
//
// @visibility external
//<
roundValues:true,
//> @attr slider.roundPrecision (integer : 1 : [IRW])
// If +link{slider.roundValues} is false, the slider value will be rounded to this number of
// decimal places. If set to null the value will not be rounded
// @visibility external
//<
roundPrecision:1,
//> @attr slider.flipValues (boolean : false : [IRW])
// Specifies whether the value range of the slider should be flipped so that values increase as
// the thumb is moved down (for a vertical slider) or to the left (for a horizontal slider).
// @visibility external
//<
flipValues:false,
//> @attr slider.sliderTarget (Canvas : null : [IRW])
// The target widget for the sliderMove
event generated when the slider thumb
// is moved.
// @visibility external
//<
//> @attr slider.canFocus (boolean : true : [IRW])
// Indicates whether keyboard manipulation of the slider is allowed.
// @visibility external
//<
canFocus:true,
//> @attr slider.stepPercent (float : 5 : [IRW])
// The percentage of the total slider that constitutes one discrete step. The slider will move
// one step when the appropriate arrow key is pressed.
// @visibility external
//<
stepPercent:5,
//> @attr slider.animateThumb (boolean : true : [IRW])
// Should the thumb be animated to its new position when the value is changed programmatically,
// or by clicking in the slider track.
// @visibility animation
// @group animation
//<
//animateThumb:false,
//> @attr slider.animateThumbTime (integer : 250 : [IRW])
// Duration of thumb animation, in milliseconds.
// @visibility animation
// @group animation
//<
animateThumbTime:250,
//> @attr slider.animateThumbInit (boolean : false : [IRW])
// If thumb animation is enabled, should the thumb be animated to its initial value?
// @visibility animation
// @group animation
//<
//animateThumbInit:false,
// undocumented for now; possibly make this internal
animateThumbAcceleration:"slowStartandEnd",
valueChangedOnDrag:true, // default false may be more appropriate, but has backcompat problems
valueChangedOnRelease:true, // can set this to false to exactly match the pre-5.5 behavior
valueChangedOnClick:true // actually on mouseUp, but that is too confusable with thumb release
});
//!>Deferred
//---------- Define instance methods ----------\\
isc.Slider.addMethods({
//------ initWidget()
// Extends superclass initWidget() to set slider dimensions, create the track and thumb child
// widgets, and initialize the slider's target, value, and enabled state.
initWidget : function () {
this.Super("initWidget", arguments);
// If passed a minValue that's greater than a max value, swap them.
// If they are equal just leave them for now - we'll always return that value.
if (!(this.minValue <= this.maxValue)) {
this.logWarn("Slider specified with minValue:"+ this.minValue
+ ", greater than maxValue:"+ this.maxValue
+ " - reversing max and min value.");
var minValue = this.minValue;
this.minValue = this.maxValue;
this.maxValue = minValue;
}
// Enforce rounding precision on min/max values.
if (this.minValue != null) this.minValue = this._getRoundedValue(this.minValue);
if (this.maxValue != null) this.maxValue = this._getRoundedValue(this.maxValue);
this.setUpSize();
// create track and thumb
this._createTrackLayout();
// create title, range, value labels if specified
if (this.showTitle) this._titleLabel = this.addChild(this._createTitleLabel());
if (this.showRange) {
this._minLabel = this.addChild(this._createRangeLabel("min"));
this._maxLabel = this.addChild(this._createRangeLabel("max"));
}
if (this.showValue) {
this._valueLabel = this._thumb.addPeer(this._createValueLabel());
this._valueLabel.sendToBack();
// Ensure the valueLabel is drawn at the correct position.
this._updateValueLabel();
}
// If an event is sent with a null target, the event handling system determines the
// target based on the last mouse event. We definitely don't want that, so make this
// slider the target if no target has been specified.
this.setValue(this.value, !(this.animateThumbInit==true));
},
// setUpSize() - sets up width/height/length (track length)
// If width / height is explicitly specified, determine length from this
// Otherwise determine width/height based on specified length
setUpSize : function () {
var specifiedWidth = this._userWidth,
specifiedHeight = this._userHeight;
// If the user didn't specify a width / height, default them based on which components are
// being shown.
if (this.vertical) {
if (specifiedWidth == null) {
var width = Math.max(this.thumbThickWidth, this.trackWidth);
// value shows on one side of the slider, range (min/max labels) show on the
// other side
if (this.showValue) width += this.labelWidth + this.labelSpacing;
if (this.showRange) width += this.labelWidth + this.labelSpacing;
// Note: titleLabel width is derived from the width of the slider so no need to account
// for it here
//>DEBUG
this.logInfo("defaulting width to " + width + "px");
// this.thumbThinWidth)) {
height += (this.labelHeight-this.thumbThinWidth);
}
//>DEBUG
this.logInfo("no specified height on vertical Slider - defaulting to:" + height +
" based on slider.length of " + this.length);
// this.thumbThinWidth)) {
this.length -= (this.labelHeight-this.thumbThinWidth);
}
//>DEBUG
this.logInfo("setting slider track length to:"+ this.length
+ ", based on specified height");
// DEBUG
this.logInfo("defaulting height to " + height + "px");
// this.thumbThinWidth)) {
width += (this.labelWidth - this.thumbThinWidth);
}
//>DEBUG
this.logInfo("no specified width on horizontal Slider - defaulting to:" + width +
" based on slider.length of " + this.length);
// this.thumbThinWidth)) {
// We use a small label width for the horizontal valueLabel and
// allow the content to overflow if necessary
this.length -= (this.hValueLabelWidth - this.thumbThinWidth);
}
//>DEBUG
this.logInfo("setting slider track length to:"+ this.length
+ ", based on specified width");
// 1) {
this._stepSize = this._usableLength/(this.numValues-1);
}
},
// Override resizeBy to resize the track.
// setWidth / setHeight / setRect et al. fall through to this method
resizeBy : function (deltaX, deltaY) {
this.Super("resizeBy", arguments);
if (!this._track) return;
var vertical = this.vertical;
if ((vertical && deltaY != 0) || (!vertical && deltaX != 0)) {
// Update length / usable length for caculations...
this.length += vertical ? deltaY : deltaX;
this._usableLength = this.length-this.thumbThinWidth;
// resize the track
if (vertical) this._track.resizeBy(0, deltaY)
else this._track.resizeBy(deltaX, 0);
// re-calculate stepSize if numValues is defined
if (this.numValues && this.numValues > 1) {
this._stepSize = this._usableLength/(this.numValues-1);
}
// fire setValue to update the thumb.
this.setValue(this.value, true, true); // no animation, no logical value change
// Also move the max (or min) marker
if (this.showRange) {
if (this.vertical) {
var endMarker = this.flipValues ? this._maxLabel : this._minLabel;
endMarker.moveBy(0, deltaY);
} else {
var endMarker = this.flipValues ? this._minLabel : this._maxLabel;
endMarker.moveBy(deltaX, 0);
}
}
}
},
//------ _createRangeLabel(minOrMax)
// Creates, initializes, and returns a new Label widget to be the slider's mix or max value
// label. minOrMax must be the string "min" or "max".
_createRangeLabel : function (minOrMax) {
var labelLeft, labelTop, labelAlign, labelValign,
// Should the label be at the start (top / left) or end (bottom/right) of the slider?
atStartPosition = (this.vertical ? minOrMax == "max" : minOrMax == "min");
if (this.flipValues) atStartPosition = !atStartPosition;
// For vertical sliders, range labels appear to the right of the slider track
// for horizontal sliders, they appear below the slider track.
if (this.vertical) {
labelLeft = Math.max(this.thumbThickWidth, this.trackWidth) + this.labelSpacing +
(this.showValue ? this.labelWidth + this.labelSpacing : 0);
labelAlign = isc.Canvas.LEFT;
if (atStartPosition) {
labelTop = (this.showTitle ? this.labelHeight + this.labelSpacing : 0);
labelValign = isc.Canvas.TOP;
} else {
labelTop = (this.showTitle ? this.labelHeight + this.labelSpacing: 0)
+ (this.length - this.labelHeight);
labelValign = isc.Canvas.BOTTOM;
}
} else { // this.horizontal
labelTop = Math.max(this.thumbThickWidth, this.trackWidth) + this.labelSpacing +
(this.showValue ? this.labelHeight + this.labelSpacing : 0);
labelValign = isc.Canvas.TOP;
if (atStartPosition) {
labelLeft = (this.showTitle ? this.labelWidth + this.labelSpacing : 0);
labelAlign = isc.Canvas.LEFT;
} else {
labelLeft = (this.showTitle ? this.labelWidth + this.labelSpacing : 0)
+ this.length - this.labelWidth;
labelAlign = isc.Canvas.RIGHT;
}
}
return isc.Label.create({
ID:this.getID()+"_"+minOrMax+"Label",
autoDraw:false,
left:labelLeft,
top:labelTop,
width:this.labelWidth,
height:this.labelHeight,
wrap:false,
align:labelAlign,
valign:labelValign,
className:this.rangeStyle,
contents:(minOrMax == "min" ?
(this.minValueLabel ? this.minValueLabel : this.minValue) :
(this.maxValueLabel ? this.maxValueLabel : this.maxValue) )
});
},
//------ _createTitleLabel()
// Creates, initializes, and returns a new Label widget to be the slider's title label.
_createTitleLabel : function () {
// Title label will always float at 0,0 within the slider.
var labelAlign = (this.vertical ? isc.Canvas.CENTER : isc.Canvas.RIGHT);
return isc.Label.create({
ID:this.getID()+"_titleLabel",
autoDraw:false,
left:0,
top:0,
width:(this.vertical ? this.getWidth() : this.labelWidth),
height:(this.vertical ? this.labelHeight : this.getHeight()),
align:labelAlign,
className:this.titleStyle,
contents:this.title
});
},
//------ _createValueLabel()
// Creates, initializes, and returns a new Label widget to be the slider's dynamic value label.
hValueLabelWidth:5,
_createValueLabel : function () {
var labelLeft, labelTop, labelWidth, labelAlign, labelValign;
if (this.vertical) {
labelLeft = this._thumb.getLeft() - this.labelWidth - this.labelSpacing;
// align the center of the label with the center of the thumb
labelTop = this._thumb.getTop()
+ parseInt(this._thumb.getHeight()/2 - this.labelHeight/2);
labelAlign = isc.Canvas.RIGHT;
labelValign = isc.Canvas.CENTER;
labelWidth = this.labelWidth;
} else {
labelLeft = this._thumb.getLeft()
+ parseInt(this._thumb.getWidth()/2 - this.labelWidth/2);
labelTop = this._thumb.getTop() - this.labelHeight - this.labelSpacing;
labelAlign = isc.Canvas.CENTER;
labelValign = isc.Canvas.BOTTOM;
// Specify a small size for the label, and allow it's content to
// overflow.
labelWidth = this.hValueLabelWidth;
}
var label = isc.Label.create({
ID:this.getID()+"_valueLabel",
autoDraw:false,
left:labelLeft,
top:labelTop,
width:labelWidth,
height:this.labelHeight,
wrap:false,
align:labelAlign,
className:this.valueStyle,
contents:this.value,
mouseUp:function () { return false; },
moveWithMaster:false, // We'll explicitly handle moving the valueLabel
observes:[{source:this, message:"valueChanged", action:"this._updateValueLabel();"}]
});
if (!this.vertical) {
isc.addMethods(label, {
// Override draw() to reposition the label after drawing.
// we have to do this as we don't know the drawn size of the label until it has been
// drawn in the DOM, and the desired position depends on the drawn size.
draw : function () {
var prevVis = this.visibility
// avoid a flash by drawing with visibility hidden initially
this.hide();
this.Super("draw", arguments);
this.parentElement._updateValueLabel();
this.setVisibility(this.prevVis);
}
});
};
return label;
},
//_createTrackLayout()
// Internal function fired once at init time to create the track and thumb for the slider
_createTrackLayout : function () {
// Determine the rect for the trackLayout. We will center the thumb and track along the
// long axis of this rect.
var layoutRect = this._getTrackLayoutPos(),
trackLeft, trackTop,
trackWidth = (this.vertical ? this.trackWidth : this.length),
trackHeight = (this.vertical ? this.length : this.trackWidth),
thumbLeft, thumbTop,
thumbWidth = (this.vertical ? this.thumbThickWidth : this.thumbThinWidth),
thumbHeight = (this.vertical ? this.thumbThinWidth : this.thumbThickWidth)
;
var thumbThicker = this.thumbThickWidth > this.trackWidth;
if (thumbThicker) {
if (this.vertical) {
thumbLeft = layoutRect[0];
trackLeft = thumbLeft + parseInt(this.thumbThickWidth/2 - this.trackWidth/2);
trackTop = layoutRect[1];
// Doesn't really matter where we put the thumb vertically - it'll be shifted via
// 'setValue()'
thumbTop = layoutRect[1];
} else {
thumbTop = layoutRect[1];
trackTop = thumbTop + parseInt(this.thumbThickWidth/2 - this.trackWidth/2);
trackLeft = layoutRect[0];
thumbLeft = layoutRect[0];
}
// track is thicker than the thumb
} else {
if (this.vertical) {
trackLeft = layoutRect[0];
thumbLeft = trackLeft + parseInt(this.trackWidth/2 - this.thumbThinWidth/2);
trackTop = layoutRect[1];
thumbTop = layoutRect[1];
} else {
trackTop = layoutRect[1];
thumbTop = trackTop + parseInt(this.trackWidth/2 - this.thumbThinWidth/2);
trackLeft = layoutRect[0];
thumbLeft = layoutRect[0];
}
}
//>DEBUG
this.logDebug("calculated coords for track:"+ [trackLeft, trackTop, trackWidth, trackHeight]);
this.logDebug("calculated coords for thumb:"+ [thumbLeft, thumbTop, thumbWidth, thumbHeight]);
// this.thumbThinWidth))
top += Math.round((this.labelHeight - this.thumbThinWidth)/2);
if (this.horizontal && (this.labelWidth > this.thumbThinWidth))
left += Math.round((this.labelWidth - this.thumbThinWidth)/2);
}
return [left, top];
},
//------ _createTrack()
// Creates, initializes, and returns a new StretchImg widget to be the slider's track.
trackConstructor:"StretchImg",
_createTrack : function (top, left, width, height) {
return this.createAutoChild("track", {
left:left,
top:top,
width:width,
height:height,
vertical:this.vertical,
// image-based appearance: StretchImg props
capSize:this.trackCapSize,
src:"[SKIN]" + (this.vertical ? "v" : "h") + this.trackSrc,
skinImgDir:this.skinImgDir,
imageType:this.trackImageType,
// allows a Label to be used with pure CSS styling
styleName:this[(this.vertical ? "v" : "h") + "TrackStyle"],
overflow:"hidden",
showDisabled:true,
// allow the thumb and the track to have focus, but set exclude them from the tab order
// this allows for bubbling of keypress events after the user has clicked on the thumb or
// track of the slider
canFocus:true,
tabIndex:-1,
cacheImageSizes: false
//backgroundColor:"#666666" // in case images aren't available
});
},
//------ _createThumb()
// Creates, initializes, and returns a new Img widget to be the slider's thumb.
thumbConstructor:"Img",
_createThumb : function (top, left, width, height) {
var vPrefix
return this.createAutoChild("thumb", {
left:left,
top:top,
width:width,
height:height,
// image-based appearance: Img props
src:"[SKIN]" + (this.vertical ? "v" : "h") + this.thumbSrc,
skinImgDir:this.skinImgDir,
// allows a Label to be used with pure CSS styling
overflow:"hidden",
showDisabled:true,
styleName:this[(this.vertical ? "v" : "h") + "ThumbStyle"],
canDrag:true,
dragAppearance:isc.EventHandler.NONE,
cursor:isc.Canvas.HAND,
dragMove:function () { this.parentElement._thumbMove(); return false },
// We want the thumb to move with the track, but NOT resize with it.
_resizeWithMaster:false,
dragStart:function(){
var EH = isc.EventHandler;
EH.dragOffsetX = -1 * (this.getPageLeft() - EH.mouseDownEvent.x);
EH.dragOffsetY = -1 * (this.getPageTop() - EH.mouseDownEvent.y);
this.parentElement._thumbIsDragging = true;
return EH.STOP_BUBBLING;
},
dragStop:function(){
this.parentElement._thumbIsDragging = false;
this.setState(isc.Slider.UP);
if (this.parentElement.valueChangedOnRelease) {
this.parentElement.valueChanged(this.parentElement.value);
}
return false;
},
mouseDown:function () { this.setState(isc.Slider.DOWN) },
mouseUp:function () { this.setState(isc.Slider.UP); return false },
// allow the thumb and the track to have focus, but set exclude them from the tab order
// this allows for bubbling of keypress events after the user has clicked on the thumb or
// track of the slider
canFocus:true,
tabIndex:-1
//backgroundColor:"#AAAAAA" // in case images aren't available
});
},
//------ _thumbMove()
// Called by the dragMove handler for the slider thumb (this._thumb). Calculates
// the new thumb position, and if the position has changed: moves the thumb widget,
// calculates the new slider value (this.value) and sends the 'sliderMove' event
// to the target (this.sliderTarget).
// The 'fromClick' parameter indicates whether this movement is called from a click
// (eg elsewhere on the track) instead of a drag, in which case we might animate the thumb.
_thumbMove : function (fromClick) {
var thumbPosition, rawValue;
if (this.vertical) {
var trackTop = this._track.getTop(),
trackEnd = this._usableLength + trackTop;
// determine the desired position on the track
thumbPosition = isc.EventHandler.getY() - isc.EventHandler.dragOffsetY - this.getPageTop();
thumbPosition = Math.max(trackTop, Math.min(trackEnd, thumbPosition));
// for values calculations we want positions relative to trackTop
var thumbOffset = thumbPosition - trackTop;
if (this.numValues) {
// do not round thumbOffset yet, since it is used to calculate the raw value below
thumbOffset = Math.round(thumbOffset/this._stepSize) * this._stepSize;
thumbPosition = Math.round(thumbOffset) + trackTop;
}
if (thumbPosition == this._thumb.getTop()) return; // no thumb movement
//>DEBUG
this.logDebug("drag-moving thumb to:"+ thumbPosition)
// DEBUG
this.logDebug("drag-moving thumb to:"+ thumbPosition)
// DEBUG
this.logDebug("slider value from drag-move:"+ this.value);
// this.getWidth()) {
desiredLeft = this.getWidth() - width;
//this.logWarn("width:" + width + ", would overflow so clamping:" + desiredLeft);
}
if (desiredLeft < 0) desiredLeft = 0;
label.setLeft(desiredLeft);
}
},
mouseUp : function() {
isc.EventHandler.dragOffsetX = isc.EventHandler.dragOffsetY =
Math.floor(this.thumbThinWidth/2);
if (this.valueChangedOnClick) this._thumbMove(true);
},
//------ setValue(newValue)
//> @method slider.setValue() ([])
// Sets the slider value to newValue and moves the slider thumb to the appropriate position for this
// value. Sends the 'sliderMove' event to the sliderTarget.
//
// @param newValue (float) the new value
// @param noAnimation (boolean) do not animate the slider thumb to the new value
// @visibility external
//<
setValue : function (newValue, noAnimation, noValueChange) {
var rawValue, thumbOffset;
if (!isc.isA.Number(newValue)) return;
// Ensure minValue<=newValue<=maxValue.
newValue = Math.max(this.minValue, (Math.min(newValue, this.maxValue)));
// Set value, rounding if specified.
newValue = this._getRoundedValue(newValue);
this.value = newValue;
// Calculate rawValue and resulting thumbOffset.
if (this.minValue == this.maxValue) rawValue = 1;
else rawValue = (this.value-this.minValue)/(this.maxValue-this.minValue);
thumbOffset = rawValue * this._usableLength;
// Set the thumb position.
var thumbPosition;
if (this.vertical) {
thumbPosition = this._track.getTop() +
parseInt(this.flipValues ? thumbOffset : this._usableLength - thumbOffset);
if (this.animateThumb && !noAnimation) {
this._thumbAnimation = this._thumb.animateMove(this._thumb.getLeft(), thumbPosition,
null, this.animateThumbTime, this.animateThumbAcceleration);
} else {
this._thumb.setTop(thumbPosition);
}
} else {
thumbPosition = this._track.getLeft() +
parseInt(this.flipValues ? this._usableLength - thumbOffset : thumbOffset);
if (this.animateThumb && !noAnimation) {
this._thumbAnimation = this._thumb.animateMove(thumbPosition, this._thumb.getTop(),
null, this.animateThumbTime, this.animateThumbAcceleration);
} else {
this._thumb.setLeft(thumbPosition);
}
}
if (!noValueChange) this.valueChanged(this.value); // observable method
if (this.sliderTarget) isc.EventHandler.handleEvent(this.sliderTarget, isc.Slider.EVENTNAME, this);
},
//------ getValue()
//> @method slider.getValue() ([])
// Returns the current slider value.
//
// @return (float) current slider value
// @visibility external
//<
getValue : function () {
return this.value;
},
//------ valueChanged()
//> @method slider.valueChanged() ([A])
// This method is called when the slider value changes. This occurs when the setValue method is
// called, or when the slider is moved. Observe this method to be notified when the slider value
// changes.
//
// @param value (number) the new value
//
// @see method:class.observe
// @visibility external
// @example slider
//<
valueChanged : function (value) {
},
//> @method slider.valueIsChanging() ([A])
// Call this method in your +link{slider.valueChanged()} handler to determine whether the
// value change is due to an ongoing drag interaction (true) or due to a thumb-release,
// mouse click, keypress, or programmatic event (false). You may choose to execute temporary or
// partial updates while the slider thumb is dragged, and final updates or persistence of the value
// in response to the other events.
//
// @return (boolean) true if user is still dragging the slider thumb, false otherwise
//
// @visibility external
//<
valueIsChanging : function () {
return (this._thumbIsDragging == true);
},
// HandleKeyPress:
// If Home, End, or the arrow keys are pressed while this slider has focus, move the slider
// appropriately.
// 20050912: Thumb animation is explicitly disabled by setting the noAnimation parameter of
// setValue(), because the thumb jumps around when one of the arrow keys is held down. Not worth
// tracking down, since the effect is already pretty close to an animation in this case.
handleKeyPress : function (event, eventInfo) {
var keyName = event.keyName;
// Note: if this.flipValues is true, vertical sliders will start at the top, and increase
// toward the bottom, horizontal sliders will start at the right and increase towards the
// left
// "Home" will move the slider all the way to the min value (may be either end depending on
// flipValues)
if (keyName == "Home") {
this.setValue(this.minValue, true);
return false;
}
// "End" will move the slider all the way to the max value
if (keyName == "End") {
this.setValue(this.maxValue, true);
return false;
}
// If an arrow key was pressed, move the slider one step in the direction indicated
// Calculate one step from this.stepPercent:
var change = (this.maxValue - this.minValue) * this.stepPercent / 100;
// if roundValues is enabled, ensure we always move (a change < 1 could be rounded to no
// change)
if (this.roundValues && change < 1) change = 1;
if (this.vertical) {
if ((this.flipValues && keyName == "Arrow_Up") ||
(!this.flipValues && keyName == "Arrow_Down"))
{
this.setValue(this.getValue() - change, true);
return false;
} else if ( (this.flipValues && keyName == "Arrow_Down") ||
(!this.flipValues && keyName == "Arrow_Up"))
{
this.setValue(this.getValue() + change, true);
return false
}
} else {
if ((this.flipValues && keyName == "Arrow_Left") ||
(!this.flipValues && keyName == "Arrow_Right"))
{
this.setValue(this.getValue() + change, true)
return false;
} else if ( (this.flipValues && keyName == "Arrow_Right") ||
(!this.flipValues && keyName == "Arrow_Left"))
{
this.setValue(this.getValue() - change, true)
return false;
}
}
if (this.keyPress) {
this.convertToMethod("keyPress");
return this.keyPress(event, eventInfo);
}
},
// override setCanFocus to set the canFocus property on the track and the thumb as well
setCanFocus : function (canFocus) {
this.Super("canFocus", arguments);
if (this._thumb != null) this._thumb.setCanFocus(canFocus);
if (this._track != null) this._track.setCanFocus(canFocus);
},
//> @method slider.setMinValue() ([])
// Sets the +link{slider.minValue, minimum value} of the slider
//
// @param newValue (float) the new minimum value
// @visibility external
//<
setMinValue : function (newValue) {
newValue = this._getRoundedValue(newValue);
this.minValue = newValue;
if (this._minLabel) this._minLabel.setContents(newValue);
this.setValue(this.minValue);
},
//> @method slider.setMaxValue() ([])
// Sets the +link{slider.maxValue, maximum value} of the slider
//
// @param newValue (float) the new maximum value
// @visibility external
//<
setMaxValue : function (newValue) {
// If we're rounding, round the min/max value as well
newValue = this._getRoundedValue(newValue);
this.maxValue = newValue;
if (this._maxLabel) this._maxLabel.setContents(newValue);
this.setValue(this.minValue);
},
//> @method slider.setNumValues() ([])
// Sets the +link{slider.numValues, number of values} for the slider
//
// @param newNumValues (float) the new number of values
// @visibility external
//<
setNumValues : function (newNumValues) {
this.numValues = newNumValues;
this._stepSize = this._usableLength/(this.numValues-1);
this.setValue(this.minValue);
},
//> @method slider.setTitle()
// Sets the +link{title} of the slider
//
// @param newTitle (string) new title for the slider
// @visibility external
//<
setTitle : function (newTitle) {
this._titleLabel.setContents(newTitle);
},
//> method slider.setLength()
// Sets the +link{length} of the slider
//
// @param newLength (number) the new length to set the slider to
// @visibility external
//<
setLength : function (newLength) {
this.length = newLength;
this.setUpSize();
},
_refreshChildren : function () {
this._titleLabel.destroy();
this._track.destroy();
this._thumb.destroy();
this._valueLabel.destroy();
this._minLabel.destroy();
this._maxLabel.destroy();
this.initWidget();
},
//> @method slider.setVertical()
// Sets the +link{vertical} property of the slider
//
// @param isVertical (boolean) is the slider vertical
// @visibility external
//<
setVertical : function (isVertical) {
this.vertical = isVertical;
this._refreshChildren();
},
//> @method slider.setThumbThickWidth()
// Sets the +link{thumbThickWidth} property of the slider
//
// @param newWidth (number) new thumbThickWidth
// @visibility external
//<
setThumbThickWidth : function (newWidth) {
this.thumbThickWidth = newWidth;
this._refreshChildren();
},
//> @method slider.setThumbThinWidth()
// Sets the +link{thumbThinWidth} property of the slider
//
// @param newWidth (number) new thumbThinWidth
// @visibility external
//<
setThumbThinWidth : function (newWidth) {
this.thumbThinWidth = newWidth;
this._refreshChildren();
},
//> @method slider.setTrackWidth()
// Sets the +link{trackWidth} property of the slider
//
// @param newWidth (number) new trackWidth
// @visibility external
//<
setTrackWidth : function (newWidth) {
this.trackWidth = newWidth;
this._refreshChildren();
},
//> @method slider.setThumbSrc()
// Sets the +link{thumbSrc} property of the slider
//
// @param newSrc (string) new thumbSrc
// @visibility external
//<
setThumbSrc : function (newSrc) {
this.thumbSrc = newSrc;
this._refreshChildren();
},
//> @method slider.setTrackSrc()
// Sets the +link{trackSrc} property of the slider
//
// @param newSrc (string) new trackSrc
// @visibility external
//<
setTrackSrc : function (newSrc) {
this.trackSrc = newSrc;
this._refreshChildren();
},
//> @method slider.setTrackCapSize()
// Sets the +link{trackCapSize} property of the slider
//
// @param newSize (number) new trackCapSize
// @visibility external
//<
setTrackCapSize : function (newSize) {
this.trackCapSize = newSize;
this._refreshChildren();
},
//> @method slider.setTrackImageType()
// Sets the +link{trackImageType} property of the slider
//
// @param newType (string) new trackImageType
// @visibility external
//<
setTrackImageType : function (newType) {
this.trackImageType = newType;
this._refreshChildren();
},
//> @method slider.setShowTitle()
// Sets the +link{showTitle} property of the slider
//
// @param showTitle (boolean) show the slider title?
// @visibility external
//<
setShowTitle : function (showTitle) {
this.showTitle = showTitle;
this._refreshChildren();
},
//> @method slider.setShowRange()
// Sets the +link{showRange} property of the slider
//
// @param showRange (boolean) show the slider range?
// @visibility external
//<
setShowRange : function (showRange) {
this.showRange = showRange;
this._refreshChildren();
},
//> @method slider.setShowValue()
// Sets the +link{showValue} property of the slider
//
// @param showValue (boolean) show the slider value?
// @visibility external
//<
setShowValue : function (showValue) {
this.showValue = showValue;
this._refreshChildren();
},
//> @method slider.setLabelWidth()
// Sets the +link{labelWidth} property of the slider
//
// @param labelWidth (number) new label width
// @visibility external
//<
setLabelWidth : function (labelWidth) {
this.labelWidth = labelWidth;
this._refreshChildren();
},
//> @method slider.setLabelHeight()
// Sets the +link{labelHeight} property of the slider
//
// @param newHeight (number) new label height
// @visibility external
//<
setLabelHeight : function (newHeight) {
this.labelHeight = newHeight;
this._refreshChildren();
},
//> @method slider.setLabelSpacing()
// Sets the +link{labelSpacing} property of the slider
//
// @param labelWidth (number) new label spacing
// @visibility external
//<
setLabelSpacing : function (newSpacing) {
this.labelSpacing = newSpacing;
this._refreshChildren();
},
//> @method slider.setMaxValueLabel()
// Sets the +link{maxValueLabel} property of the slider
//
// @param labelText (string) new label text
// @visibility external
//<
setMaxValueLabel : function (labelText) {
this._maxLabel.setContents(labelText);
},
//> @method slider.setRoundValues()
// Sets the +link{roundValues} property of the slider
//
// @param roundValues (boolean) round slider values?
// @visibility external
//<
setRoundValues : function (roundValues) {
this.roundValues = roundValues;
this._refreshChildren();
},
//> @method slider.setRoundPrecision()
// Sets the +link{roundPrecision} property of the slider
//
// @param roundPrecision (number) new round precision
// @visibility external
//<
setRoundPrecision : function (roundPrecision) {
this.roundPrecision = roundPrecision;
this._refreshChildren();
},
//> @method slider.setFlipValues()
// Sets the +link{flipValues} property of the slider
//
// @param flipValues (boolean) flip slider values?
// @visibility external
//<
setFlipValues : function (flipValues) {
this.flipValues = flipValues;
this._refreshChildren();
},
//> @method slider.setStepPercent()
// Sets the +link{stepPercent} property of the slider
//
// @param stepPercent (number) new slider step percent
// @visibility external
//<
setStepPercent : function (stepPercent) {
this.stepPercent = stepPercent;
this._refreshChildren();
}
});
isc.Slider.registerStringMethods({
valueChanged : "value"
})
//!