UNPKG

dijit

Version:

Dijit provides a complete collection of user interface controls based on Dojo, giving you the power to create web applications that are highly optimized for usability, performance, internationalization, accessibility, but above all deliver an incredible u

391 lines (343 loc) 13.1 kB
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; });