You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
390 lines
13 KiB
390 lines
13 KiB
define([ |
|
"dojo/_base/array", // array.forEach |
|
"dojo/_base/declare", // declare |
|
"dojo/dnd/move", |
|
"dojo/_base/fx", // fx.animateProperty |
|
"dojo/dom-geometry", // domGeometry.position |
|
"dojo/dom-style", // domStyle.getComputedStyle |
|
"dojo/keys", // keys.DOWN_ARROW keys.END keys.HOME keys.LEFT_ARROW keys.PAGE_DOWN keys.PAGE_UP keys.RIGHT_ARROW keys.UP_ARROW |
|
"dojo/_base/lang", // lang.hitch |
|
"dojo/sniff", // has("ie") has("mozilla") |
|
"dojo/dnd/Moveable", // Moveable |
|
"dojo/dnd/Mover", // Mover Mover.prototype.destroy.apply |
|
"dojo/query", // query |
|
"dojo/mouse", // mouse.wheel |
|
"dojo/on", |
|
"../_base/manager", // defaultDuration |
|
"../focus", // focus.focus() |
|
"../typematic", |
|
"./Button", |
|
"./_FormValueWidget", |
|
"../_Container", |
|
"dojo/text!./templates/HorizontalSlider.html" |
|
], function(array, declare, move, fx, domGeometry, domStyle, keys, lang, has, Moveable, Mover, query, mouse, on, |
|
manager, focus, typematic, Button, _FormValueWidget, _Container, template){ |
|
|
|
// module: |
|
// dijit/form/HorizontalSlider |
|
|
|
var _SliderMover = declare("dijit.form._SliderMover", Mover, { |
|
onMouseMove: function(e){ |
|
var widget = this.widget; |
|
var abspos = widget._abspos; |
|
if(!abspos){ |
|
abspos = widget._abspos = domGeometry.position(widget.sliderBarContainer, true); |
|
widget._setPixelValue_ = lang.hitch(widget, "_setPixelValue"); |
|
widget._isReversed_ = widget._isReversed(); |
|
} |
|
var pixelValue = e[widget._mousePixelCoord] - abspos[widget._startingPixelCoord]; |
|
widget._setPixelValue_(widget._isReversed_ ? (abspos[widget._pixelCount] - pixelValue) : pixelValue, abspos[widget._pixelCount], false); |
|
}, |
|
|
|
destroy: function(e){ |
|
Mover.prototype.destroy.apply(this, arguments); |
|
var widget = this.widget; |
|
widget._abspos = null; |
|
widget._setValueAttr(widget.value, true); |
|
} |
|
}); |
|
|
|
var HorizontalSlider = declare("dijit.form.HorizontalSlider", [_FormValueWidget, _Container], { |
|
// summary: |
|
// A form widget that allows one to select a value with a horizontally draggable handle |
|
|
|
templateString: template, |
|
|
|
// Overrides FormValueWidget.value to indicate numeric value |
|
value: 0, |
|
|
|
// showButtons: [const] Boolean |
|
// Show increment/decrement buttons at the ends of the slider? |
|
showButtons: true, |
|
|
|
// minimum: [const] Integer |
|
// The minimum value the slider can be set to. |
|
minimum: 0, |
|
|
|
// maximum: [const] Integer |
|
// The maximum value the slider can be set to. |
|
maximum: 100, |
|
|
|
// discreteValues: Integer |
|
// If specified, indicates that the slider handle has only 'discreteValues' possible positions, |
|
// and that after dragging the handle, it will snap to the nearest possible position. |
|
// Thus, the slider has only 'discreteValues' possible values. |
|
// |
|
// For example, if minimum=10, maxiumum=30, and discreteValues=3, then the slider handle has |
|
// three possible positions, representing values 10, 20, or 30. |
|
// |
|
// If discreteValues is not specified or if it's value is higher than the number of pixels |
|
// in the slider bar, then the slider handle can be moved freely, and the slider's value will be |
|
// computed/reported based on pixel position (in this case it will likely be fractional, |
|
// such as 123.456789). |
|
discreteValues: Infinity, |
|
|
|
// pageIncrement: Integer |
|
// If discreteValues is also specified, this indicates the amount of clicks (ie, snap positions) |
|
// that the slider handle is moved via pageup/pagedown keys. |
|
// If discreteValues is not specified, it indicates the number of pixels. |
|
pageIncrement: 2, |
|
|
|
// clickSelect: Boolean |
|
// If clicking the slider bar changes the value or not |
|
clickSelect: true, |
|
|
|
// slideDuration: Number |
|
// The time in ms to take to animate the slider handle from 0% to 100%, |
|
// when clicking the slider bar to make the handle move. |
|
slideDuration: manager.defaultDuration, |
|
|
|
// Map widget attributes to DOMNode attributes. |
|
_setIdAttr: "", // Override _FormWidget which sends id to focusNode |
|
_setNameAttr: "valueNode", // Override default behavior to send to focusNode |
|
|
|
baseClass: "dijitSlider", |
|
|
|
// Apply CSS classes to up/down arrows and handle per mouse state |
|
cssStateNodes: { |
|
incrementButton: "dijitSliderIncrementButton", |
|
decrementButton: "dijitSliderDecrementButton", |
|
focusNode: "dijitSliderThumb" |
|
}, |
|
|
|
_mousePixelCoord: "pageX", |
|
_pixelCount: "w", |
|
_startingPixelCoord: "x", |
|
_handleOffsetCoord: "left", |
|
_progressPixelSize: "width", |
|
|
|
_onKeyUp: function(/*Event*/ e){ |
|
if(this.disabled || this.readOnly || e.altKey || e.ctrlKey || e.metaKey){ |
|
return; |
|
} |
|
this._setValueAttr(this.value, true); |
|
}, |
|
|
|
_onKeyDown: function(/*Event*/ e){ |
|
if(this.disabled || this.readOnly || e.altKey || e.ctrlKey || e.metaKey){ |
|
return; |
|
} |
|
switch(e.keyCode){ |
|
case keys.HOME: |
|
this._setValueAttr(this.minimum, false); |
|
break; |
|
case keys.END: |
|
this._setValueAttr(this.maximum, false); |
|
break; |
|
// this._descending === false: if ascending vertical (min on top) |
|
// (this._descending || this.isLeftToRight()): if left-to-right horizontal or descending vertical |
|
case ((this._descending || this.isLeftToRight()) ? keys.RIGHT_ARROW : keys.LEFT_ARROW): |
|
case (this._descending === false ? keys.DOWN_ARROW : keys.UP_ARROW): |
|
case (this._descending === false ? keys.PAGE_DOWN : keys.PAGE_UP): |
|
this.increment(e); |
|
break; |
|
case ((this._descending || this.isLeftToRight()) ? keys.LEFT_ARROW : keys.RIGHT_ARROW): |
|
case (this._descending === false ? keys.UP_ARROW : keys.DOWN_ARROW): |
|
case (this._descending === false ? keys.PAGE_UP : keys.PAGE_DOWN): |
|
this.decrement(e); |
|
break; |
|
default: |
|
return; |
|
} |
|
e.stopPropagation(); |
|
e.preventDefault(); |
|
}, |
|
|
|
_onHandleClick: function(e){ |
|
if(this.disabled || this.readOnly){ |
|
return; |
|
} |
|
if(!has("ie")){ |
|
// make sure you get focus when dragging the handle |
|
// (but don't do on IE because it causes a flicker on mouse up (due to blur then focus) |
|
focus.focus(this.sliderHandle); |
|
} |
|
e.stopPropagation(); |
|
e.preventDefault(); |
|
}, |
|
|
|
_isReversed: function(){ |
|
// summary: |
|
// Returns true if direction is from right to left |
|
// tags: |
|
// protected extension |
|
return !this.isLeftToRight(); |
|
}, |
|
|
|
_onBarClick: function(e){ |
|
if(this.disabled || this.readOnly || !this.clickSelect){ |
|
return; |
|
} |
|
focus.focus(this.sliderHandle); |
|
e.stopPropagation(); |
|
e.preventDefault(); |
|
var abspos = domGeometry.position(this.sliderBarContainer, true); |
|
var pixelValue = e[this._mousePixelCoord] - abspos[this._startingPixelCoord]; |
|
this._setPixelValue(this._isReversed() ? (abspos[this._pixelCount] - pixelValue) : pixelValue, abspos[this._pixelCount], true); |
|
this._movable.onMouseDown(e); |
|
}, |
|
|
|
_setPixelValue: function(/*Number*/ pixelValue, /*Number*/ maxPixels, /*Boolean?*/ priorityChange){ |
|
if(this.disabled || this.readOnly){ |
|
return; |
|
} |
|
var count = this.discreteValues; |
|
if(count <= 1 || count == Infinity){ |
|
count = maxPixels; |
|
} |
|
count--; |
|
var pixelsPerValue = maxPixels / count; |
|
var wholeIncrements = Math.round(pixelValue / pixelsPerValue); |
|
this._setValueAttr(Math.max(Math.min((this.maximum - this.minimum) * wholeIncrements / count + this.minimum, this.maximum), this.minimum), priorityChange); |
|
}, |
|
|
|
_setValueAttr: function(/*Number*/ value, /*Boolean?*/ priorityChange){ |
|
// summary: |
|
// Hook so set('value', value) works. |
|
this._set("value", value); |
|
this.valueNode.value = value; |
|
this.focusNode.setAttribute("aria-valuenow", value); |
|
this.inherited(arguments); |
|
var percent = this.maximum > this.minimum ? ((value - this.minimum) / (this.maximum - this.minimum)) : 0; |
|
var progressBar = (this._descending === false) ? this.remainingBar : this.progressBar; |
|
var remainingBar = (this._descending === false) ? this.progressBar : this.remainingBar; |
|
if(this._inProgressAnim && this._inProgressAnim.status != "stopped"){ |
|
this._inProgressAnim.stop(true); |
|
} |
|
if(priorityChange && this.slideDuration > 0 && progressBar.style[this._progressPixelSize]){ |
|
// animate the slider |
|
var _this = this; |
|
var props = {}; |
|
var start = parseFloat(progressBar.style[this._progressPixelSize]); |
|
var duration = this.slideDuration * (percent - start / 100); |
|
if(duration == 0){ |
|
return; |
|
} |
|
if(duration < 0){ |
|
duration = 0 - duration; |
|
} |
|
props[this._progressPixelSize] = { start: start, end: percent * 100, units: "%" }; |
|
this._inProgressAnim = fx.animateProperty({ node: progressBar, duration: duration, |
|
onAnimate: function(v){ |
|
remainingBar.style[_this._progressPixelSize] = (100 - parseFloat(v[_this._progressPixelSize])) + "%"; |
|
}, |
|
onEnd: function(){ |
|
delete _this._inProgressAnim; |
|
}, |
|
properties: props |
|
}); |
|
this._inProgressAnim.play(); |
|
}else{ |
|
progressBar.style[this._progressPixelSize] = (percent * 100) + "%"; |
|
remainingBar.style[this._progressPixelSize] = ((1 - percent) * 100) + "%"; |
|
} |
|
}, |
|
|
|
_bumpValue: function(signedChange, /*Boolean?*/ priorityChange){ |
|
if(this.disabled || this.readOnly || (this.maximum <= this.minimum)){ |
|
return; |
|
} |
|
var s = domStyle.getComputedStyle(this.sliderBarContainer); |
|
var c = domGeometry.getContentBox(this.sliderBarContainer, s); |
|
var count = this.discreteValues; |
|
if(count <= 1 || count == Infinity){ |
|
count = c[this._pixelCount]; |
|
} |
|
count--; |
|
// the division is imprecise so the expression has to be rounded to avoid long floating numbers |
|
var value = Math.round((this.value - this.minimum) * count / (this.maximum - this.minimum)) + signedChange; |
|
if(value < 0){ |
|
value = 0; |
|
} |
|
if(value > count){ |
|
value = count; |
|
} |
|
value = value * (this.maximum - this.minimum) / count + this.minimum; |
|
this._setValueAttr(value, priorityChange); |
|
}, |
|
|
|
_onClkBumper: function(val){ |
|
if(this.disabled || this.readOnly || !this.clickSelect){ |
|
return; |
|
} |
|
this._setValueAttr(val, true); |
|
}, |
|
|
|
_onClkIncBumper: function(){ |
|
this._onClkBumper(this._descending === false ? this.minimum : this.maximum); |
|
}, |
|
|
|
_onClkDecBumper: function(){ |
|
this._onClkBumper(this._descending === false ? this.maximum : this.minimum); |
|
}, |
|
|
|
decrement: function(/*Event*/ e){ |
|
// summary: |
|
// Decrement slider |
|
// tags: |
|
// private |
|
this._bumpValue(e.keyCode == keys.PAGE_DOWN ? -this.pageIncrement : -1); |
|
}, |
|
|
|
increment: function(/*Event*/ e){ |
|
// summary: |
|
// Increment slider |
|
// tags: |
|
// private |
|
this._bumpValue(e.keyCode == keys.PAGE_UP ? this.pageIncrement : 1); |
|
}, |
|
|
|
_mouseWheeled: function(/*Event*/ evt){ |
|
// summary: |
|
// Event handler for mousewheel where supported |
|
|
|
if(!this.focused){ |
|
// If use is scrolling over page and we happen to get the mouse wheel event, just ignore it. |
|
return; |
|
} |
|
|
|
evt.stopPropagation(); |
|
evt.preventDefault(); |
|
this._bumpValue(evt.wheelDelta < 0 ? -1 : 1, true); // negative scroll acts like a decrement |
|
}, |
|
|
|
startup: function(){ |
|
if(this._started){ |
|
return; |
|
} |
|
|
|
array.forEach(this.getChildren(), function(child){ |
|
if(this[child.container] != this.containerNode){ |
|
this[child.container].appendChild(child.domNode); |
|
} |
|
}, this); |
|
|
|
this.inherited(arguments); |
|
}, |
|
|
|
_typematicCallback: function(/*Number*/ count, /*Object*/ button, /*Event*/ e){ |
|
if(count == -1){ |
|
this._setValueAttr(this.value, true); |
|
}else{ |
|
this[(button == (this._descending ? this.incrementButton : this.decrementButton)) ? "decrement" : "increment"](e); |
|
} |
|
}, |
|
|
|
buildRendering: function(){ |
|
this.inherited(arguments); |
|
if(this.showButtons){ |
|
this.incrementButton.style.display = ""; |
|
this.decrementButton.style.display = ""; |
|
} |
|
|
|
// find any associated label element and add to slider focusnode. |
|
var label = query('label[for="' + this.id + '"]'); |
|
if(label.length){ |
|
if(!label[0].id){ |
|
label[0].id = this.id + "_label"; |
|
} |
|
this.focusNode.setAttribute("aria-labelledby", label[0].id); |
|
} |
|
|
|
this.focusNode.setAttribute("aria-valuemin", this.minimum); |
|
this.focusNode.setAttribute("aria-valuemax", this.maximum); |
|
}, |
|
|
|
postCreate: function(){ |
|
this.inherited(arguments); |
|
|
|
if(this.showButtons){ |
|
this.own( |
|
typematic.addMouseListener(this.decrementButton, this, "_typematicCallback", 25, 500), |
|
typematic.addMouseListener(this.incrementButton, this, "_typematicCallback", 25, 500) |
|
); |
|
} |
|
this.own( |
|
on(this.domNode, mouse.wheel, lang.hitch(this, "_mouseWheeled")) |
|
); |
|
|
|
// define a custom constructor for a SliderMover that points back to me |
|
var mover = declare(_SliderMover, { |
|
widget: this |
|
}); |
|
this._movable = new Moveable(this.sliderHandle, {mover: mover}); |
|
|
|
this._layoutHackIE7(); |
|
}, |
|
|
|
destroy: function(){ |
|
this._movable.destroy(); |
|
if(this._inProgressAnim && this._inProgressAnim.status != "stopped"){ |
|
this._inProgressAnim.stop(true); |
|
} |
|
this.inherited(arguments); |
|
} |
|
}); |
|
|
|
HorizontalSlider._Mover = _SliderMover; // for monkey patching |
|
|
|
return HorizontalSlider; |
|
});
|
|
|