/* Copyright (c) 2009, Yahoo! Inc. All rights reserved. Code licensed under the BSD License: http://developer.yahoo.net/yui/license.txt version: 3.0.0 build: 1549 */ YUI.add('slider', function(Y) { /** * Create a sliding value range input visualized as a draggable thumb on a * background element. * * @module slider */ var SLIDER = 'slider', RAIL = 'rail', THUMB = 'thumb', VALUE = 'value', MIN = 'min', MAX = 'max', MIN_GUTTER = 'minGutter', MAX_GUTTER = 'maxGutter', THUMB_IMAGE = 'thumbImage', RAIL_SIZE = 'railSize', CONTENT_BOX = 'contentBox', SLIDE_START = 'slideStart', SLIDE_END = 'slideEnd', THUMB_DRAG = 'thumbDrag', SYNC = 'sync', POSITION_THUMB = 'positionThumb', RENDERED = 'rendered', DISABLED = 'disabled', DISABLED_CHANGE = 'disabledChange', DOT = '.', PX = 'px', WIDTH = 'width', HEIGHT = 'height', COMPLETE = 'complete', L = Y.Lang, isBoolean= L.isBoolean, isString = L.isString, isNumber = L.isNumber, getCN = Y.ClassNameManager.getClassName, IMAGE = 'image', C_RAIL = getCN(SLIDER,RAIL), C_THUMB = getCN(SLIDER,THUMB), C_THUMB_IMAGE = getCN(SLIDER,THUMB,IMAGE), C_IMAGE_ERROR = getCN(SLIDER,IMAGE,'error'), M = Math, max = M.max, round = M.round, floor = M.floor; /** * Create a slider to represent an integer value between a given minimum and * maximum. Sliders may be aligned vertically or horizontally, based on the * axis configuration. * * @class Slider * @extends Widget * @param config {Object} Configuration object * @constructor */ function Slider() { Slider.superclass.constructor.apply(this,arguments); } Y.mix(Slider, { /** * The identity of the widget. * * @property Slider.NAME * @type String * @static */ NAME : SLIDER, /** * Object property names used for respective X and Y axis Sliders (e.g. * "left" vs. "top" for placing the thumb according to * its representative value). * * @property Slider._AXIS_KEYS * @type Object * @protected * @static */ _AXIS_KEYS : { x : { dim : WIDTH, offAxisDim : HEIGHT, eventPageAxis : 'pageX', ddStick : 'stickX', xyIndex : 0 }, y : { dim : HEIGHT, offAxisDim : WIDTH, eventPageAxis : 'pageY', ddStick : 'stickY', xyIndex : 1 } }, /** * Static Object hash used to capture existing markup for progressive * enhancement. Keys correspond to config attribute names and values * are selectors used to inspect the contentBox for an existing node * structure. * * @property Slider.HTML_PARSER * @type Object * @protected * @static */ HTML_PARSER : { rail : DOT + C_RAIL, thumb : DOT + C_THUMB, thumbImage : DOT + C_THUMB_IMAGE }, /** * Static property used to define the default attribute configuration of * the Widget. * * @property Slider.ATTRS * @type Object * @protected * @static */ ATTRS : { /** * Axis upon which the Slider's thumb moves. "x" for * horizontal, "y" for vertical. * * @attribute axis * @type String * @default "x" * @writeOnce */ axis : { value : 'x', writeOnce : true, validator : function (v) { return this._validateNewAxis(v); }, setter : function (v) { return this._setAxisFn(v); } }, /** * Value associated with the left or top most position of the thumb on * the rail. * * @attribute min * @type Number * @default 0 */ min : { value : 0, validator : function (v) { return this._validateNewMin(v); } }, /** * Value associated with the right or bottom most position of the thumb * on the rail. * * @attribute max * @type Number * @default 100 */ max : { value : 100, validator : function (v) { return this._validateNewMax(v); } }, /** * The current value of the Slider. This value is interpretted into a * position for the thumb along the Slider's rail. * * @attribute value * @type Number * @default 0 */ value : { value : 0, validator : function (v) { return this._validateNewValue(v); } }, /** * The Node representing the Slider's rail, usually visualized as a * bar of some sort using a background image, along which the thumb * moves. This Node contains the thumb Node. * * @attribute rail * @type Node * @default null */ rail : { value : null, validator : function (v) { return this._validateNewRail(v); }, setter : function (v) { return this._setRailFn(v); } }, /** *

The Node representing the Slider's thumb, usually visualized as a * pointer using a contained image Node (see thumbImage). The current * value of the Slider is calculated from the centerpoint of this * Node in relation to the rail Node. If provided, the thumbImage * Node is contained within this Node.

* *

If no thumbImage is provided and the Node passed as the thumb is * an img element, the assigned Node will be allocated to * the thumbImage and the thumb container defaulted.

* * @attribute thumb * @type Node * @default null */ thumb : { value : null, validator : function (v) { return this._validateNewThumb(v); }, setter : function (v) { return this._setThumbFn(v); } }, /** *

The Node representing the image element to use for the Slider's * thumb.

* *

Alternately, an image URL can be passed and an img * Node will be generated accordingly.

* *

If no thumbImage is provided and the Node passed as the thumb is * an img element, the assigned Node will be allocated to * the thumbImage and the thumb container defaulted.

* *

If thumbImage is provided but its URL resolves to a 404, a default * style will be applied to maintain basic functionality.

* * @attribute thumbImage * @type Node|String * @default null */ thumbImage : { value : null, validator : function (v) { return this._validateNewThumbImage(v); }, setter : function (v) { return this._setThumbImageFn(v); } }, /** *

The width or height of the rail element representing the physical * space along which the thumb can move. CSS size values (e.g. '30em') * accepted but converted to pixels during render.

* *

Alternately, but not recommended, this attribute can be left * unassigned in favor of specifying height or width.

* * @attribute railSize * @type String * @default '0' */ railSize : { value : '0', validator : function (v) { return this._validateNewRailSize(v); } }, /** * Boolean indicating whether clicking and dragging on the rail will * trigger thumb movement. * * @attribute railEnabled * @type Boolean * @default true */ railEnabled : { value : true, validator : isBoolean }, /** * Like CSS padding, the distance in pixels from the inner top or left * edge of the rail node within which the thumb can travel. Negative * values allow the edge of the thumb to escape the rail node * boundaries. * * @attribute minGutter * @type Number * @default 0 */ minGutter : { value : 0, validator : isNumber }, /** * Like CSS padding, the distance in pixels from the inner bottom or * right edge of the rail node within which the thumb can travel. * Negative values allow the edge of the thumb to escape the rail node * boundaries. * * @attribute maxGutter * @type Number * @default 0 */ maxGutter : { value : 0, validator : isNumber } } }); Y.extend(Slider, Y.Widget, { /** * Collection of object property names from the appropriate hash set in * Slider._AXIS_KEYS. * * @property _key * @type Object * @protected */ _key : null, /** * Factor used to translate positional coordinates (e.g. left or top) to * the Slider's value. * * @property _factor * @type Number * @protected */ _factor : 1, /** * Pixel dimension of the rail Node's width for X axis Sliders or height * for Y axis Sliders. Used with _factor to calculate positional * coordinates for the thumb. * * @property _railSize * @type Number * @protected */ _railSize : null, /** * Pixel dimension of the thumb Node's width for X axis Sliders or height * for Y axis Sliders. Used with _factor to calculate positional * coordinates for the thumb. * * @property _thumbSize * @type Number * @protected */ _thumbSize : null, /** * Pixel offset of the point in the thumb element from its top/left edge * to where the value calculation should take place. By default, this is * calculated to half the width of the thumb, causing the value to be * marked from the center of the thumb. * * @property _thumbOffset * @type Number * @protected */ _thumbOffset : 0, /** * Object returned from temporary subscription to disabledChange event to * defer setting the disabled state while Slider is loading the thumb * image. * * @property _stall * @type Object * @protected */ _stall : false, /** * Deferred value for the disabled attribute when stalled (see _stall * property). * * @property _disabled * @type Boolean * @protected */ _disabled : false, /** * Construction logic executed durint Slider instantiation. Subscribes to * after events for min, max, and railSize. Publishes custom events * including slideStart and slideEnd. * * @method initializer * @protected */ initializer : function () { this._key = Slider._AXIS_KEYS[this.get('axis')]; this.after('minChange', this._afterMinChange); this.after('maxChange', this._afterMaxChange); this.after('railSizeChange', this._afterRailSizeChange); /** * Signals the beginning of a thumb drag operation. Payload includes * the DD.Drag instance's drag:start event under key ddEvent. * * @event slideStart * @param event {Event.Facade} An Event Facade object with the following attribute specific properties added: *
*
ddEvent
*
drag:start event from the managed DD.Drag instance
*
*/ this.publish(SLIDE_START); /** * Signals the end of a thumb drag operation. Payload includes * the DD.Drag instance's drag:end event under key ddEvent. * * @event slideEnd * @param event {Event.Facade} An Event Facade object with the following attribute specific properties added: *
*
ddEvent
*
drag:end event from the managed DD.Drag instance
*
*/ this.publish(SLIDE_END); /** * Communicates a request to synchronize the Slider UI with the * attribute state. Links the sync request with the default sync * logic in _defSyncFn. * * @event sync * @param event {Event.Facade} Event Facade object * @preventable _defSyncFn */ this.publish(SYNC, { defaultFn: this._defSyncFn }); /** * Signals a request to reposition the thumb in response to API methods. * Triggers the thumb placement logic in _defPositionThumbFn. * * @event positionThumb * @param event {Event.Facade} An Event Facade object with the following attribute specific properties added: *
*
changeEv
*
valueChange event fired in response to the change in the value attribute
*
* @preventable _defPositionThumbFn */ this.publish(POSITION_THUMB, { defaultFn: this._defPositionThumbFn }); }, /** * Create the DOM structure for the Slider. * * @method renderUI * @protected */ renderUI : function () { this._initRail(); this._initThumb(); }, /** * Creates the rail element if not provided and not discovered via * HTML_PARSER. * * @method _initRail * @protected */ _initRail : function () { var cb = this.get(CONTENT_BOX), rail = this.get(RAIL); // Create rail if necessary. Make sure it's in the contentBox if (!rail) { rail = cb.appendChild( Y.Node.create('
')); this.set(RAIL,rail); } else if (!cb.contains(rail)) { cb.appendChild(rail); } rail.addClass(C_RAIL); rail.addClass(this.getClassName(RAIL,this.get('axis'))); }, /** *

Creates the thumb element (not image) if not provided and not * discovered via HTML_PARSER. If the thumb is an img element * but no thumbImage configured or discovered, reassigns the thumb element * to the thumbImage and defaults the thumb element as a div.

* *

Makes sure the thumb is a child of the rail element and calls * _initThumbImage if thumbImage is provided.

* * @method _initThumb * @protected */ _initThumb : function () { var rail = this.get(RAIL), thumb = this.get(THUMB); // Passed an img element as the thumb if (thumb && !this.get(THUMB_IMAGE) && thumb.get('nodeName').toLowerCase() === 'img') { this.set(THUMB_IMAGE, thumb); this.set(THUMB,null); thumb = null; } if (!thumb) { thumb = Y.Node.create( '
'); this.set(THUMB,thumb); } thumb.addClass(C_THUMB); if (!rail.contains(thumb)) { rail.appendChild(thumb); } if (this.get(THUMB_IMAGE)) { this._initThumbImage(); } }, /** * Ensures the thumbImage is a child of the thumb element. * * @method _initThumbImage * @protected */ _initThumbImage : function () { var thumb = this.get(THUMB), img = this.get(THUMB_IMAGE); if (img) { img.replaceClass(C_THUMB,C_THUMB_IMAGE); if (!thumb.contains(img)) { thumb.appendChild(img); } } }, /** * Creates the Y.DD instance used to handle the thumb movement and binds * Slider interaction to the configured value model. * * @method bindUI * @protected */ bindUI : function () { /** * Bridges user interaction with the thumb to the value attribute. * * @event thumbDrag * @param event {Event.Facade} An Event Facade object with the following attribute specific properties added: *
*
ddEvent
*
drag:drag event from the managed DD.Drag instance
*
* @preventable _defThumbDragFn */ this.publish(THUMB_DRAG, {defaultFn: this._defThumbDragFn}); this._bindThumbDD(); this.after('valueChange', this._afterValueChange); this.after('thumbImageChange', this._afterThumbImageChange); this.after(DISABLED_CHANGE, this._afterDisabledChange); }, /** * Creates the Y.DD instance used to handle the thumb interaction. * * @method _bindThumbDD * @protected */ _bindThumbDD : function () { var ddConf = { node : this.get(THUMB), bubble : false }, conConf = { constrain2node : this.get(RAIL) }; conConf[this._key.ddStick] = true; this._dd = new Y.DD.Drag(ddConf).plug(Y.Plugin.DDConstrained, conConf); this._dd.on('drag:start', Y.bind(this._onDDStartDrag, this)); this._dd.on('drag:drag', Y.bind(this._onDDDrag, this)); this._dd.on('drag:end', Y.bind(this._onDDEndDrag, this)); this._initRailDD(); }, /** * Subscribes to the rail Node's mousedown event to actuate the thumb when * backgroundEnabled is true. * * @method _initRailDD * @protected */ _initRailDD : function () { this.get(RAIL).on('mousedown',Y.bind(this._handleRailMouseDown,this)); }, /** * If the Slider is not disabled and railEnabled is true, moves the thumb * to the mousedown position and hands control over to DD. * * @method _handleRailMouseDown * @param e {Event} Mousedown event facade * @protected */ _handleRailMouseDown : function (e) { if (this.get('railEnabled') && !this.get(DISABLED)) { var dd = this._dd, xyIndex = this._key.xyIndex, xy; if (dd.get('primaryButtonOnly') && e.button > 1) { return false; } dd._dragThreshMet = true; dd._fixIEMouseDown(); e.halt(); Y.DD.DDM.activeDrag = dd; // Adjust registered starting position by half the thumb's x/y xy = dd.get('dragNode').getXY(); xy[xyIndex] += this._thumbOffset; dd._setStartPosition(xy); dd.set('activeHandle',dd.get('dragNode')); dd.start(); dd._alignNode([e.pageX,e.pageY]); } }, /** * Synchronizes the DOM state with the attribute settings (most notably * railSize and value). If thumbImage is provided and is still loading, * sync is delayed until it is complete, since the image's dimensions are * taken into consideration for calculations. * * @method syncUI */ syncUI : function () { this.get(CONTENT_BOX).removeClass(C_IMAGE_ERROR); var img = this.get(THUMB_IMAGE); if (this._isImageLoading(img)) { // Schedule the sync for when the image loads/errors this._scheduleSync(); } else { this._ready(img,!this._isImageLoaded(img)); } }, /** * Binds to the load and error event on the thumbImage to sync the DOM * state with the attribute settings when the image resource is resolved. * The Slider is disabled while it waits. * * @method _scheduleSync * @protected */ _scheduleSync : function () { var img, handler; if (!this._stall) { // disable the control until the image is loaded this._disabled = this.get(DISABLED); this.set(DISABLED,true); this._stall = this.on(DISABLED_CHANGE,this._stallDisabledChange); img = this.get(THUMB_IMAGE); handler = Y.bind(this._imageLoaded,this,img); img.on('load', handler); img.on('error',handler); } }, /** * Method subscribed to the disabledChange event when thumbImage is being * loaded. Prevents manually enabling the Slider until the thumbImage * resource is resolved. Intended value is stored during load and set upon * completion. * * @method _stallDisabledChange * @param e {Event} Change event for the disabled attribute * @protected */ _stallDisabledChange : function (e) { this._disabled = e.newVal; e.preventDefault(); }, /** * Event handler assigned to the thumbImage's load and error event if it * was not loaded prior to instantiation. Restores the disabled value. * * @method _imageLoaded * @param img {Node} The thumbImage Node * @param e {Event} load or error event fired by the thumbImage * @protected */ _imageLoaded : function (img,e) { var error = (e.type.toLowerCase().indexOf('error') > -1); // Need to execute inside a setTimeout because IE doesn't report // img.complete === true until after the img.onload handler // @TODO: readyState reports correctly in onload. Lose this wrapper // and use that in _isImageLoaded. Y.later(0, this, function () { if (this._stall) { this._stall.detach(); } this._stall = false; this._ready(img,error); this.set(DISABLED,this._disabled); }); }, /** * Applies a class to the content box if the thumbImage failed to resolve, * the fires the internal sync event triggering a sync between UI and * state. * * @method _ready * @param img {Node} the thumbImage Node * @param error {Boolean} Indicates an error while loading the thumbImage * @protected */ _ready : function (img,error) { var method = error ? 'addClass' : 'removeClass'; // If the thumb image url results in 404, assign a class to provide // default thumb dimensions/UI this.get(CONTENT_BOX)[method](C_IMAGE_ERROR); this.fire(SYNC); }, /** * The default synchronization behavior, updating the Slider's DOM state to * match the current attribute values. * * @method _defSyncFn * @param e {Event} Internal sync event * @protected */ _defSyncFn : function (e) { this._uiSetThumbSize(); this._setThumbOffset(); this._uiSetRailSize(); this._setRailOffsetXY(); this._setDDGutter(); this._resetDDCacheRegion(); this._setFactor(); var val = this.get(VALUE); this.fire(POSITION_THUMB, { value : val, offset : this._convertValueToOffset(val) }); // Forces a reflow of the bounding box to address IE8 inline-block // container not expanding correctly. bug 2527905 this.get('boundingBox').toggleClass(''); }, /** * Captures the thumb's pixel height or width (depending on the Slider's * axis) for use in positioning calculations. * * @method _uiSetThumbSize * @protected */ _uiSetThumbSize : function () { var thumb = this.get(THUMB), dim = this._key.dim, img = this.get(THUMB_IMAGE), size; // offsetWidth fails in hidden containers size = parseInt(thumb.getComputedStyle(dim),10); if (img && this._isImageLoaded(img)) { size = img.get(dim); } this._thumbSize = size; }, /** * Establishes the point in the thumb that should align to the rail * position representing the calculated value. * * @method _setThumbOffset * @protected */ _setThumbOffset : function () { this._thumbOffset = floor(this._thumbSize / 2); }, /** * Stores the rail Node's pixel height or width, depending on the Slider's * axis, for use in calculating thumb position from the value. * * @method _uiSetRailSize * @protected */ _uiSetRailSize : function () { var rail = this.get(RAIL), thumb = this.get(THUMB), img = this.get(THUMB_IMAGE), dim = this._key.dim, size = this.get(RAIL_SIZE), setxy = false; if (parseInt(size,10)) { // Convert to pixels rail.setStyle(dim,size); size = parseInt(rail.getComputedStyle(dim),10); } else { // Default from height or width (axis respective), or dims assigned // via css to the rail or thumb, whichever is largest. // Dear implementers, please use railSize, not height/width to // set the rail dims size = this.get(dim); if (parseInt(size,10)) { setxy = true; rail.setStyle(dim,size); size = parseInt(rail.getComputedStyle(dim),10); } size = max( size|0, parseInt(thumb.getComputedStyle(dim),10), parseInt(rail.getComputedStyle(dim),10)); if (img && this._isImageLoaded(img)) { size = max(img.get(dim),size); } } rail.setStyle(dim, size + PX); this._railSize = size; // handle the (not recommended) fallback case of setting rail size via // widget height/width params. This is the only case that sets the // off-axis rail dim in the code. if (setxy) { dim = this._key.offAxisDim; size = this.get(dim); if (size) { rail.set(dim,size); } } }, /** * Store the current XY position of the rail Node on the page. For use in * calculating thumb position from value. * * @method _setRailOffsetXY * @protected */ _setRailOffsetXY : function () { this._offsetXY = this.get(RAIL).getXY()[this._key.xyIndex] + this.get(MIN_GUTTER); }, /** * Passes the gutter attribute value to the DDConstrain gutter attribute. * * @method _setDDGutter * @protected */ _setDDGutter : function () { var gutter = this._key.xyIndex ? this.get(MIN_GUTTER) + " 0 " + this.get(MAX_GUTTER) : "0 " + this.get(MAX_GUTTER) + " 0 " + this.get(MIN_GUTTER); this._dd.con.set('gutter', gutter); }, /** * Resets the cached region inside the DD constrain instance to support * repositioning the Slider after instantiation. * * @method _resetDDCacheRegion * @protected */ _resetDDCacheRegion : function () { // Workaround for ticket #2527964 this._dd.con._cacheRegion(); }, /** * Calculates the multiplier used to translate the value into a thumb * position. * * @method _setFactor * @protected */ _setFactor : function () { var range = this._railSize - this._thumbSize - this.get(MIN_GUTTER) - this.get(MAX_GUTTER); this._factor = this._railSize ? (this.get(MAX) - this.get(MIN)) / range : 1; }, /** * Convenience method for accessing the current value of the Slider. * Equivalent to slider.get("value"). * * @method getValue * @return {Number} the value */ getValue : function () { return this.get(VALUE); }, /** * Convenience method for updating the current value of the Slider. * Equivalent to slider.set("value",val). * * @method setValue * @param val {Number} the new value */ setValue : function (val) { this.set(VALUE,val); }, /** * Validator applied to new values for the axis attribute. Only * "x" and "y" are permitted. * * @method _validateNewAxis * @param v {String} proposed value for the axis attribute * @return Boolean * @protected */ _validateNewAxis : function (v) { return isString(v) && 'xXyY'.indexOf(v.charAt(0)) > -1; }, /** * Validator applied to the min attribute. * * @method _validateNewMin * @param v {MIXED} proposed value for the min attribute * @return Boolean * @protected */ _validateNewMin : function (v) { return isNumber(v); }, /** * Validator applied to the max attribute. * * @method _validateNewMax * @param v {MIXED} proposed value for the max attribute * @return Boolean * @protected */ _validateNewMax : function (v) { return isNumber(v); }, /** * Validator applied to the value attribute. * * @method _validateNewValue * @param v {MIXED} proposed value for the value attribute * @return Boolean * @protected */ _validateNewValue : function (v) { var min = this.get(MIN), max = this.get(MAX); return isNumber(v) && (min < max ? (v >= min && v <= max) : (v >= max && v <= min)); }, /** * Validator applied to the rail attribute. Rejects all values after the * Slider has been rendered. * * @method _validateNewRail * @param v {MIXED} proposed value for the rail attribute * @return Boolean * @protected */ _validateNewRail : function (v) { return !this.get(RENDERED) || v; }, /** * Validator applied to the thumb attribute. Rejects all values after the * Slider has been rendered. * * @method _validateNewThumb * @param v {MIXED} proposed value for the thumb attribute * @return Boolean * @protected */ _validateNewThumb : function (v) { return !this.get(RENDERED) || v; }, /** * Validator applied to the thumbImage attribute. Rejects all values after * the Slider has been rendered. * * @method _validateNewThumbImage * @param v {MIXED} proposed value for the thumbImage attribute * @return Boolean * @protected */ _validateNewThumbImage : function (v) { return !this.get(RENDERED) || v; }, /** * Validator applied to the railSize attribute. Only strings of css size * values (e.g. '200px') are allowed. * * @method _validateNewRailSize * @param v {String} proposed value for the railSize attribute * @return Boolean * @protected */ _validateNewRailSize : function (v) { return isString(v) && (v === '0' || /^\d+(?:p[xtc]|%|e[mx]|in|[mc]m)$/.test(v)); }, /** * Setter applied to the input when updating the axis attribute. * * @method _setAxisFn * @param v {String} proposed value for the axis attribute * @return {String} lowercased first character of the input string * @protected */ _setAxisFn : function (v) { return v.charAt(0).toLowerCase(); }, /** * Setter applied to the input when updating the rail attribute. Input can * be a Node, raw HTMLElement, or a selector string to locate it. * * @method _setRailFn * @param v {Node|String|HTMLElement} The rail element Node or selector * @return {Node} The Node if found. Otherwise null. * @protected */ _setRailFn : function (v) { return Y.get(v) || null; }, /** * Setter applied to the input when updating the thumb attribute. Input can * be a Node, raw HTMLElement, or a selector string to locate it. * * @method _setThumbFn * @param v {Node|String|HTMLElement} The thumb element Node or selector * @return {Node} The Node if found. Otherwise null. * @protected */ _setThumbFn : function (v) { return Y.get(v) || null; }, /** * Setter applied to the input when updating the thumbImage attribute. * Input can be a Node, raw HTMLElement, selector string to locate it, or * the URL for an image resource. * * String input will be treated as a selector. If no element is found using * the selector, an img Node will be created with the string * used as the src attribute. * * @method _setThumbImageFn * @param v {Node|String|HTMLElement} The thumbImage element Node, selector, * or image URL * @return {Node} The Node if found or created. Otherwise null. * @protected */ _setThumbImageFn : function (v) { return v ? Y.get(v) || Y.Node.create('Slider thumb') : null; }, /** * Caches the current page position of the rail element and fires the * slideStart event in response to the DD's drag:start. * * @method _onDDStartDrag * @param e {Event} the DD instance's drag:start custom event * @protected */ _onDDStartDrag : function (e) { this._setRailOffsetXY(); this.fire(SLIDE_START,{ ddEvent: e }); }, /** * Fires the thumbDrag event to queue Slider value update. * * @method _onDDDrag * @param e {Event} the DD instance's drag:drag custom event * @protected */ _onDDDrag : function (e) { this.fire(THUMB_DRAG, { ddEvent: e }); }, /** * The default value update behavior in response to Slider thumb * interaction. Calculates the value using stored offsets, the _factor * multiplier and the min value. * * @method _defThumbDragFn * @param e {Event} the internal thumbDrag event * @protected */ _defThumbDragFn : function (e) { var before = this.get(VALUE), val = e.ddEvent[this._key.eventPageAxis] - this._offsetXY; val = this._convertOffsetToValue(val); if (before !== val) { this.set(VALUE, val, { ddEvent: e.ddEvent }); } }, /** * Fires the slideEnd event. * * @method _onDDEndDrag * @param e {Event} the DD instance's drag:end custom event * @protected */ _onDDEndDrag : function (e) { this.fire(SLIDE_END,{ ddEvent: e }); }, /** * Calls _uiPositionThumb with the value of the custom event's * "offset" property. * * @method _defPositionThumbFn * @param e {Event} the positionThumb custom event * @protected */ _defPositionThumbFn : function (e) { this._uiPositionThumb(e.offset); }, /** * Places the thumb at a particular X or Y location based on the configured * axis. * * @method _uiPositionThumb * @param xy {Number} the desired left or top pixel position of the thumb * in relation to the rail Node. * @protected */ _uiPositionThumb : function (xy) { var dd = this._dd, thumb = dd.get('dragNode'), hidden = thumb.ancestor(this._isDisplayNone); if (!hidden) { dd._setStartPosition(dd.get('dragNode').getXY()); // stickX/stickY config on DD instance will negate off-axis move dd._alignNode([xy,xy],true); } }, /** * Helper function to search up the ancestor axis looking for a node with * style display: none. This is passed as a function to node.ancestor(..) * to test if a given node is in the displayed DOM and can get accurate * positioning information. * * @method _isDisplayNone * @param el {Node} ancestor node as the function walks up the parent axis * @return {Boolean} true if the node is styled with display: none * @protected */ _isDisplayNone : function (node) { return node.getComputedStyle('display') === 'none'; }, /** * Fires the internal positionThumb event in response to a change in the * value attribute. * * @method _afterValueChange * @param e {Event} valueChange custom event * @protected */ _afterValueChange : function (e) { if (!e.ddEvent) { var xy = this._convertValueToOffset(e.newVal); this.fire(POSITION_THUMB,{ value: e.newVal, offset: xy }); } }, /** * Converts a value to a pixel offset for the thumb position on the rail. * * @method _convertValueToOffset * @param v {Number} value between the Slider's min and max * @protected */ _convertValueToOffset : function (v) { return round((v - this.get(MIN)) / this._factor) + this._offsetXY; }, /** * Converts a pixel offset of the thumb on the rail to a value. * * @method _convertOffsetToValue * @param v {Number} pixel offset of the thumb on the rail * @protected */ _convertOffsetToValue : function (v) { return round(this.get(MIN) + (v * this._factor)); }, /** * Replaces the thumb Node in response to a change in the thumb attribute. * This only has effect after the Slider is rendered. * * @method _afterThumbChange * @param e {Event} thumbChange custom event * @protected */ _afterThumbChange : function (e) { var thumb; if (this.get(RENDERED)) { if (e.prevValue) { e.prevValue.get('parentNode').removeChild(e.prevValue); } this._initThumb(); thumb = this.get(THUMB); this._dd.set('node',thumb); this._dd.set('dragNode',thumb); this.syncUI(); } }, /** * Sets or replaces the thumb's contained img Node with the * new Node in response to a change in the thumbImage attribute. This only * has effect after the Slider is rendered. * * @method _afterThumbImageChange * @param e {Event} thumbImageChange custom event * @protected */ _afterThumbImageChange : function (e) { if (this.get(RENDERED)) { if (e.prevValue) { e.prevValue.get('parentNode').removeChild(e.prevValue); } this._initThumbImage(); this.syncUI(); } }, /** * Updates the Slider UI in response to change in the min attribute. * * @method _afterMinChange * @param e {Event} minChange custom event * @protected */ _afterMinChange : function (e) { this._refresh(e); }, /** * Updates the Slider UI in response to change in the max attribute. * * @method _afterMaxChange * @param e {Event} maxChange custom event * @protected */ _afterMaxChange : function (e) { this._refresh(e); }, /** * Updates the Slider UI in response to change in the railSize attribute. * * @method _afterRailSizeChange * @param e {Event} railSizeChange custom event * @protected */ _afterRailSizeChange : function (e) { this._refresh(e); }, /** * Locks or unlocks the DD instance in response to a change in the disabled * attribute. * * @method _afterDisabledChange * @param e {Event} disabledChange custom event * @protected */ _afterDisabledChange : function (e) { if (this._dd) { this._dd.set('lock',e.newVal); } }, /** * Common handler to call syncUI in response to change events that occurred * after the Slider is rendered. * * @method _refresh * @param e {Event} An attribute change event * @protected */ _refresh : function (e) { if (e.newVal !== e.prevVal && this.get(RENDERED)) { this.syncUI(); } }, /** * Used to determine if there is a current or pending request for the * thumbImage resource. * * @method _isImageLoading * @param img {Node} img Node * @return Boolean * @protected */ _isImageLoading : function (img) { return img && !img.get(COMPLETE); }, /** * Used to determine if the image resource loaded successfully or there was * an error. * * NOTES: * * * @method _isImageLoaded * @param img {Node} img Node * @return Boolean * @protected */ _isImageLoaded : function (img) { if (img) { var w = img.get('naturalWidth'); return img.get(COMPLETE) && (!isNumber(w) ? img.get(WIDTH) : w); } return true; } }); Y.Slider = Slider; }, '3.0.0' ,{requires:['widget','dd-constrain']});