/* Copyright (c) 2010, Yahoo! Inc. All rights reserved. Code licensed under the BSD License: http://developer.yahoo.com/yui/license.html version: 3.3.0 build: 3167 */ YUI.add('slider-base', function(Y) { /** * Create a sliding value range input visualized as a draggable thumb on a * background element. * * @module slider * @submodule slider-base */ var INVALID_VALUE = Y.Attribute.INVALID_VALUE; /** * Create a slider to represent an input control capable of representing a * series of intermediate states based on the position of the slider's thumb. * These states are typically aligned to a value algorithm whereby the thumb * position corresponds to a given value. Sliders may be oriented vertically or * horizontally, based on the axis configuration. * * @class SliderBase * @extends Widget * @param config {Object} Configuration object * @constructor */ function SliderBase() { SliderBase.superclass.constructor.apply( this, arguments ); } Y.SliderBase = Y.extend( SliderBase, Y.Widget, { // Y.Slider prototype /** * Construction logic executed during Slider instantiation. * * @method initializer * @protected */ initializer : function () { /** * The configured axis, stored for fast lookup since it's a writeOnce * attribute. This is for use by extension classes. For * implementation code, use get( "axis" ) for * authoritative source. Never write to this property. * * @property axis * @type {String} * @protected */ this.axis = this.get( 'axis' ); /** * Cached fast access map for DOM properties and attributes that * pertain to accessing dimensional or positioning information * according to the Slider's axis (e.g. "height" vs. * "width"). Extension classes should add to this collection * for axis related strings if necessary. * * @property _key * @type {Object} * @protected */ this._key = { dim : ( this.axis === 'y' ) ? 'height' : 'width', minEdge: ( this.axis === 'y' ) ? 'top' : 'left', maxEdge: ( this.axis === 'y' ) ? 'bottom' : 'right', xyIndex: ( this.axis === 'y' ) ? 1 : 0 }; /** * Signals that the thumb has moved. Payload includes the thumb's * pixel offset from the top/left edge of the rail, and if triggered by * dragging the thumb, the drag:drag event. * * @event thumbMove * @param event {Event} The event object for the thumbMove with the * following extra properties: *
*
offset
*
Pixel offset from top/left of the slider to the new * thumb position
*
ddEvent
*
drag:drag event from the thumb
*
*/ this.publish( 'thumbMove', { defaultFn: this._defThumbMoveFn, queuable : true } ); }, /** * Create the DOM structure for the Slider. * * @method renderUI * @protected */ renderUI : function () { var contentBox = this.get( 'contentBox' ); /** * The Node instance of the Slider's rail element. Do not write to * this property. * * @property rail * @type {Node} */ this.rail = this.renderRail(); this._uiSetRailLength( this.get( 'length' ) ); /** * The Node instance of the Slider's thumb element. Do not write to * this property. * * @property thumb * @type {Node} */ this.thumb = this.renderThumb(); this.rail.appendChild( this.thumb ); // @TODO: insert( contentBox, 'replace' ) or setContent? contentBox.appendChild( this.rail ); // contentBox.addClass( this.getClassName( this.axis ) ); }, /** * Creates the Slider rail DOM subtree for insertion into the Slider's * contentBox. Override this method if you want to provide * the rail element (presumably from existing markup). * * @method renderRail * @return {Node} the rail node subtree */ renderRail: function () { var minCapClass = this.getClassName( 'rail', 'cap', this._key.minEdge ), maxCapClass = this.getClassName( 'rail', 'cap', this._key.maxEdge ); return Y.Node.create( Y.substitute( this.RAIL_TEMPLATE, { railClass : this.getClassName( 'rail' ), railMinCapClass: minCapClass, railMaxCapClass: maxCapClass } ) ); }, /** * Sets the rail length according to the length attribute. * * @method _uiSetRailLength * @param length {String} the length to apply to the rail style * @protected */ _uiSetRailLength: function ( length ) { this.rail.setStyle( this._key.dim, length ); }, /** * Creates the Slider thumb DOM subtree for insertion into the Slider's * rail. Override this method if you want to provide the thumb element * (presumably from existing markup). * * @method renderThumb * @return {Node} the thumb node subtree */ renderThumb: function () { this._initThumbUrl(); var imageUrl = this.get( 'thumbUrl' ); return Y.Node.create( Y.substitute( this.THUMB_TEMPLATE, { thumbClass : this.getClassName( 'thumb' ), thumbShadowClass: this.getClassName( 'thumb', 'shadow' ), thumbImageClass : this.getClassName( 'thumb', 'image' ), thumbShadowUrl : imageUrl, thumbImageUrl : imageUrl } ) ); }, /** * Creates the Y.DD.Drag instance used to handle the thumb movement and * binds Slider interaction to the configured value model. * * @method bindUI * @protected */ bindUI : function () { this._bindThumbDD(); this._bindValueLogic(); this.after( 'disabledChange', this._afterDisabledChange ); this.after( 'lengthChange', this._afterLengthChange ); }, /** * Makes the thumb draggable and constrains it to the rail. * * @method _bindThumbDD * @protected */ _bindThumbDD: function () { var config = { constrain: this.rail }; // { constrain: rail, stickX: true } config[ 'stick' + this.axis.toUpperCase() ] = true; /** * The DD.Drag instance linked to the thumb node. * * @property _dd * @type {DD.Drag} * @protected */ this._dd = new Y.DD.Drag( { node : this.thumb, bubble : false, on : { 'drag:start': Y.bind( this._onDragStart, this ) }, after : { 'drag:drag': Y.bind( this._afterDrag, this ), 'drag:end' : Y.bind( this._afterDragEnd, this ) } } ); // Constrain the thumb to the rail this._dd.plug( Y.Plugin.DDConstrained, config ); }, /** * Stub implementation. Override this (presumably in a class extension) to * initialize any value logic that depends on the presence of the Drag * instance. * * @method _bindValueLogic * @protected */ _bindValueLogic: function () {}, /** * Moves the thumb to pixel offset position along the rail. * * @method _uiMoveThumb * @param offset {Number} the pixel offset to set as left or top style * @protected */ _uiMoveThumb: function ( offset ) { if ( this.thumb ) { this.thumb.setStyle( this._key.minEdge, offset + 'px' ); this.fire( 'thumbMove', { offset: offset } ); } }, /** * Dispatches the slideStart event. * * @method _onDragStart * @param e {Event} the drag:start event from the thumb * @protected */ _onDragStart: function ( e ) { /** * Signals the beginning of a thumb drag operation. Payload includes * the thumb's drag:start event. * * @event slideStart * @param event {Event} The event object for the slideStart with the * following extra properties: *
*
ddEvent
*
drag:start event from the thumb
*
*/ this.fire( 'slideStart', { ddEvent: e } ); }, /** * Dispatches the thumbMove event. * * @method _afterDrag * @param e {Event} the drag:drag event from the thumb * @protected */ _afterDrag: function ( e ) { var thumbXY = e.info.xy[ this._key.xyIndex ], railXY = e.target.con._regionCache[ this._key.minEdge ]; this.fire( 'thumbMove', { offset : (thumbXY - railXY), ddEvent: e } ); }, /** * Dispatches the slideEnd event. * * @method _onDragEnd * @param e {Event} the drag:end event from the thumb * @protected */ _afterDragEnd: function ( e ) { /** * Signals the end of a thumb drag operation. Payload includes * the thumb's drag:end event. * * @event slideEnd * @param event {Event} The event object for the slideEnd with the * following extra properties: *
*
ddEvent
*
drag:end event from the thumb
*
*/ this.fire( 'slideEnd', { ddEvent: e } ); }, /** * Locks or unlocks the thumb. * * @method _afterDisabledChange * @param e {Event} The disabledChange event object * @protected */ _afterDisabledChange: function ( e ) { this._dd.set( 'lock', e.newVal ); }, /** * Handles changes to the length attribute. By default, it * triggers an update to the UI. * * @method _afterLengthChange * @param e {Event} The lengthChange event object * @protected */ _afterLengthChange: function ( e ) { if ( this.get( 'rendered' ) ) { this._uiSetRailLength( e.newVal ); this.syncUI(); } }, /** * Synchronizes the DOM state with the attribute settings. * * @method syncUI */ syncUI : function () { this._dd.con.resetCache(); this._syncThumbPosition(); // Forces a reflow of the bounding box to address IE8 inline-block // container not expanding correctly. bug 2527905 //this.get('boundingBox').toggleClass(''); }, /** * Stub implementation. Override this (presumably in a class extension) to * ensure the thumb is in the correct position according to the value * alogorithm. * instance. * * @method _syncThumbPosition * @protected */ _syncThumbPosition: function () {}, /** * Validates the axis is "x" or "y" (case insensitive). * Converts to lower case for storage. * * @method _setAxis * @param v {String} proposed value for the axis attribute * @return {String} lowercased first character of the input string * @protected */ _setAxis : function (v) { v = ( v + '' ).toLowerCase(); return ( v === 'x' || v === 'y' ) ? v : INVALID_VALUE; }, /** *

Ensures the stored length value is a string with a quantity and unit. * Unit will be defaulted to "px" if not included. Rejects * values less than or equal to 0 and those that don't at least start with * a number.

* *

Currently only pixel lengths are supported.

* * @method _setLength * @param v {String} proposed value for the length attribute * @return {String} the sanitized value * @protected */ _setLength: function ( v ) { v = ( v + '' ).toLowerCase(); var length = parseFloat( v, 10 ), units = v.replace( /[\d\.\-]/g, '' ) || this.DEF_UNIT; return length > 0 ? ( length + units ) : INVALID_VALUE; }, /** *

Defaults the thumbURL attribute according to the current skin, or * "sam" if none can be determined. Horizontal Sliders will have * their thumbUrl attribute set to

*

"/configured/yui/builddir/slider/assets/skins/sam/thumb-x.png"

*

And vertical thumbs will get

*

"/configured/yui/builddir/slider/assets/skins/sam/thumb-y.png"

* * @method _initThumbUrl * @protected */ _initThumbUrl: function () { if (!this.get('thumbUrl')) { var skin = this.getSkinName() || 'sam', base = Y.config.base; // Unfortunate hack to avoid requesting image resources from the // combo service. The combo service does not serve images. if (base.indexOf('http://yui.yahooapis.com/combo') === 0) { base = 'http://yui.yahooapis.com/' + Y.version + '/build/'; } // this.set('thumbUrl', base + 'slider/assets/skins/' + skin + '/thumb-' + this.axis + '.png'); } }, /** * Bounding box template that will contain the Slider's DOM subtree. <span>s are used to support inline-block styling. * * @property BOUNDING_TEMPLATE * @type {String} * @default <span></span> */ BOUNDING_TEMPLATE : '', /** * Content box template that will contain the Slider's rail and thumb. * * @property CONTENT_TEMPLATE * @type {String} * @default <span></span> */ CONTENT_TEMPLATE : '', /** * Rail template that will contain the end caps and the thumb. * {placeholder}s are used for template substitution at render time. * * @property RAIL_TEMPLATE * @type {String} * @default <span class="{railClass}"><span class="{railMinCapClass}"></span><span class="{railMaxCapClass}"></span></span> */ RAIL_TEMPLATE : '' + '' + '' + '', /** * Thumb template that will contain the thumb image and shadow. <img> * tags are used instead of background images to avoid a flicker bug in IE. * {placeholder}s are used for template substitution at render time. * * @property THUMB_TEMPLATE * @type {String} * @default <span class="{thumbClass}" tabindex="-1"><img src="{thumbShadowUrl}" alt="Slider thumb shadow" class="{thumbShadowClass}"><img src="{thumbImageUrl}" alt="Slider thumb" class="{thumbImageClass}"></span> */ THUMB_TEMPLATE : '' + '' + '' + '' }, { // Y.SliderBase static properties /** * The identity of the widget. * * @property SliderBase.NAME * @type String * @default 'sliderBase' * @readOnly * @protected * @static */ NAME : 'sliderBase', /** * Static property used to define the default attribute configuration of * the Widget. * * @property SliderBase.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, setter : '_setAxis', lazyAdd : false }, /** * The length of the rail (exclusive of the end caps if positioned by * CSS). This corresponds to the movable range of the thumb. * * @attribute length * @type {String | Number} e.g. "200px" or 200 * @default 150px */ length: { value: '150px', setter: '_setLength' }, /** * Path to the thumb image. This will be used as both the thumb and * shadow as a sprite. Defaults at render() to thumb-x.png or * thumb-y.png in the skin directory of the current skin. * * @attribute thumbUrl * @type {String} * @default thumb-x.png or thumb-y.png in the sam skin directory of the * current build path for Slider */ thumbUrl: { value: null, validator: Y.Lang.isString } } }); }, '3.3.0' ,{requires:['widget', 'substitute', 'dd-constrain']});