MDL-32843 import YUI 3.5.1
[moodle.git] / lib / yui / 3.5.1 / build / slider-base / slider-base-debug.js
blob7983887a98facc89526c07ae743444b94d517142
1 /*
2 YUI 3.5.1 (build 22)
3 Copyright 2012 Yahoo! Inc. All rights reserved.
4 Licensed under the BSD License.
5 http://yuilibrary.com/license/
6 */
7 YUI.add('slider-base', function(Y) {
9 /**
10  * Create a sliding value range input visualized as a draggable thumb on a
11  * background element.
12  * 
13  * @module slider
14  * @submodule slider-base
15  */
17 var INVALID_VALUE = Y.Attribute.INVALID_VALUE;
19 /**
20  * Create a slider to represent an input control capable of representing a
21  * series of intermediate states based on the position of the slider's thumb.
22  * These states are typically aligned to a value algorithm whereby the thumb
23  * position corresponds to a given value. Sliders may be oriented vertically or
24  * horizontally, based on the <code>axis</code> configuration.
25  *
26  * @class SliderBase
27  * @extends Widget
28  * @param config {Object} Configuration object
29  * @constructor
30  */
31 function SliderBase() {
32     SliderBase.superclass.constructor.apply( this, arguments );
35 Y.SliderBase = Y.extend( SliderBase, Y.Widget, {
37     // Y.Slider prototype
39     /**
40      * Construction logic executed during Slider instantiation.
41      *
42      * @method initializer
43      * @protected
44      */
45     initializer : function () {
46         /**
47          * The configured axis, stored for fast lookup since it's a writeOnce
48          * attribute.  This is for use by extension classes.  For
49          * implementation code, use <code>get( &quot;axis&quot; )</code> for
50          * authoritative source.  Never write to this property.
51          *
52          * @property axis
53          * @type {String}
54          * @protected
55          */
56         this.axis = this.get( 'axis' );
58         /**
59          * Cached fast access map for DOM properties and attributes that
60          * pertain to accessing dimensional or positioning information
61          * according to the Slider's axis (e.g. &quot;height&quot; vs.
62          * &quot;width&quot;).  Extension classes should add to this collection
63          * for axis related strings if necessary.
64          *
65          * @property _key
66          * @type {Object}
67          * @protected
68          */
69         this._key = {
70             dim    : ( this.axis === 'y' ) ? 'height' : 'width',
71             minEdge: ( this.axis === 'y' ) ? 'top'    : 'left',
72             maxEdge: ( this.axis === 'y' ) ? 'bottom' : 'right',
73             xyIndex: ( this.axis === 'y' ) ? 1 : 0
74         };
76         /**
77          * Signals that the thumb has moved.  Payload includes the thumb's
78          * pixel offset from the top/left edge of the rail, and if triggered by
79          * dragging the thumb, the <code>drag:drag</code> event.
80          *
81          * @event thumbMove
82          * @param event {Event} The event object for the thumbMove with the
83          *                      following extra properties:
84          *  <dl>
85          *      <dt>offset</dt>
86          *          <dd>Pixel offset from top/left of the slider to the new
87          *          thumb position</dd>
88          *      <dt>ddEvent (deprecated)</dt>
89          *          <dd><code>drag:drag</code> event from the thumb</dd>
90          *      <dt>originEvent</dt>
91          *          <dd><code>drag:drag</code> event from the thumb</dd>
92          *  </dl>
93          */
94         this.publish( 'thumbMove', {
95             defaultFn: this._defThumbMoveFn,
96             queuable : true
97         } );
98     },
100     /**
101      * Create the DOM structure for the Slider.
102      *
103      * @method renderUI
104      * @protected
105      */
106     renderUI : function () {
107         var contentBox = this.get( 'contentBox' );
109         /**
110          * The Node instance of the Slider's rail element.  Do not write to
111          * this property.
112          *
113          * @property rail
114          * @type {Node}
115          */
116         this.rail = this.renderRail();
118         this._uiSetRailLength( this.get( 'length' ) );
120         /**
121          * The Node instance of the Slider's thumb element.  Do not write to
122          * this property.
123          *
124          * @property thumb
125          * @type {Node}
126          */
127         this.thumb = this.renderThumb();
129         this.rail.appendChild( this.thumb );
130         // @TODO: insert( contentBox, 'replace' ) or setContent?
131         contentBox.appendChild( this.rail );
133         // <span class="yui3-slider-x">
134         contentBox.addClass( this.getClassName( this.axis ) );
135     },
137     /**
138      * Creates the Slider rail DOM subtree for insertion into the Slider's
139      * <code>contentBox</code>.  Override this method if you want to provide
140      * the rail element (presumably from existing markup).
141      *
142      * @method renderRail
143      * @return {Node} the rail node subtree
144      */
145     renderRail: function () {
146         var minCapClass = this.getClassName( 'rail', 'cap', this._key.minEdge ),
147             maxCapClass = this.getClassName( 'rail', 'cap', this._key.maxEdge );
149         return Y.Node.create(
150             Y.substitute( this.RAIL_TEMPLATE, {
151                 railClass      : this.getClassName( 'rail' ),
152                 railMinCapClass: minCapClass,
153                 railMaxCapClass: maxCapClass
154             } ) );
155     },
157     /**
158      * Sets the rail length according to the <code>length</code> attribute.
159      *
160      * @method _uiSetRailLength
161      * @param length {String} the length to apply to the rail style
162      * @protected
163      */
164     _uiSetRailLength: function ( length ) {
165         this.rail.setStyle( this._key.dim, length );
166     },
168     /**
169      * Creates the Slider thumb DOM subtree for insertion into the Slider's
170      * rail.  Override this method if you want to provide the thumb element
171      * (presumably from existing markup).
172      *
173      * @method renderThumb
174      * @return {Node} the thumb node subtree
175      */
176     renderThumb: function () {
177         this._initThumbUrl();
179         var imageUrl = this.get( 'thumbUrl' );
181         return Y.Node.create(
182             Y.substitute( this.THUMB_TEMPLATE, {
183                 thumbClass      : this.getClassName( 'thumb' ),
184                 thumbShadowClass: this.getClassName( 'thumb', 'shadow' ),
185                 thumbImageClass : this.getClassName( 'thumb', 'image' ),
186                 thumbShadowUrl  : imageUrl,
187                 thumbImageUrl   : imageUrl,
188                 thumbAriaLabelId: this.getClassName( 'label', Y.guid()) // get unique id for specifying a label for ARIA
189             } ) );
190     },
191     
192     /**
193      * Gives focus to the thumb enabling keyboard access after clicking thumb
194      *
195      * @method _onThumbClick
196      * @protected
197      */
198     _onThumbClick : function(e){
199         this.thumb.focus();
200     },
201     
202     
203     /**
204      * Creates the Y.DD.Drag instance used to handle the thumb movement and
205      * binds Slider interaction to the configured value model.
206      *
207      * @method bindUI
208      * @protected
209      */
210     bindUI : function () {
211     
212         // Begin keyboard listeners ///////////////////////////////
213         var boundingBox = this.get("boundingBox"), //Y.one('body'),
214         // Looking for a key event which will fire continously across browsers while the key is held down.
215         keyEvent = (!Y.UA.opera) ? "down:" : "press:",            
216         // 38, 40 = arrow up/down, 33, 34 = page up/down,  35 , 36 = end/home
217         keyEventSpec = keyEvent + "38,40,33,34,35,36",
218         // 37 , 39 = arrow left/right
219         keyLeftRightSpec = keyEvent + "37,39",
220         // 37 , 39 = arrow left/right + meta (command/apple key) for mac
221         keyLeftRightSpecMeta = keyEvent + "37+meta,39+meta";
223         boundingBox.on("key", this._onDirectionKey, keyEventSpec, this);
224         boundingBox.on("key", this._onLeftRightKey, keyLeftRightSpec, this);
225         boundingBox.on("key", this._onLeftRightKeyMeta, keyLeftRightSpecMeta, this);
226         // End keyboard listeners //////////////////////////////////
228         this.thumb.on('click', this._onThumbClick, this);
230         this._bindThumbDD();
232         this._bindValueLogic();
234         this.after( 'disabledChange', this._afterDisabledChange );
235         this.after( 'lengthChange',   this._afterLengthChange );
236         
237     },
238                       
239     /**
240      * increments Slider value by a minor increment
241      *
242      * @method _incrMinor
243      * @protected
244      */
245     _incrMinor : function(){
246         this.set('value', (this.get('value') + this.get('minorStep')));
247     },
248     
249     /**
250      * decrements Slider value by a minor increment
251      *
252      * @method _decrMinor
253      * @protected
254      */
255     _decrMinor : function(){
256         this.set('value', (this.get('value') - this.get('minorStep')));
257     },
258         
259     /**
260      * increments Slider value by a major increment
261      *
262      * @method _incrMajor
263      * @protected
264      */
265     _incrMajor : function(){
266         this.set('value', (this.get('value') + this.get('majorStep')));
267     },
268     
269     /**
270      * decrements Slider value by a major increment
271      *
272      * @method _decrMajor
273      * @protected
274      */
275     _decrMajor : function(){
276         this.set('value', (this.get('value') - this.get('majorStep')));
277     },
279     /**
280      * sets the Slider value to the min value. 
281      *
282      * @method _setToMin
283      * @protected
284      */
285     _setToMin : function(e){
286         this.set('value', this.get('min'));
287     },
289     /**
290      * sets the Slider value to the max value. 
291      *
292      * @method _setToMax
293      * @protected
294      */
295     _setToMax : function(e){
296         this.set('value', this.get('max'));
297     },
299     /**
300      * sets the Slider's value in response to key events.
301      * Left and right keys are in a separate method 
302      * in case an implementation wants to increment values
303      * but needs left and right arrow keys for other purposes.
304      *
305      * @method _onDirectionKey
306      * @param e {Event} the key event
307      * @protected
308      */
309     _onDirectionKey : function(e) {
310         e.preventDefault();
311         if(this.get('disabled') === false){
312             switch (e.charCode) {
313                 case 38: // up
314                     this._incrMinor();
315                     break;
316                 case 40: // down
317                     this._decrMinor();
318                     break;
319                 case 36: // home
320                     this._setToMin();
321                     break;
322                 case 35: // end
323                     this._setToMax();
324                     break;
325                 case 33: // page up
326                     this._incrMajor();
327                     break;
328                 case 34: // page down
329                     this._decrMajor();
330                     break;
331             }
332         }
333     },
335     /**
336      * sets the Slider's value in response to left or right key events
337      *
338      * @method _onLeftRightKey
339      * @param e {Event} the key event
340      * @protected
341      */
342     _onLeftRightKey : function(e) {
343         e.preventDefault();
344         if(this.get('disabled') === false){
345             switch (e.charCode) {
346                 case 37: // left
347                     this._decrMinor();
348                     break;
349                 case 39: // right
350                     this._incrMinor();
351                     break;
352             }
353         }
354     },
356     /**
357      * sets the Slider's value in response to left or right key events when a meta (mac command/apple) key is also pressed
358      *
359      * @method _onLeftRightKeyMeta
360      * @param e {Event} the key event
361      * @protected
362      */
363     _onLeftRightKeyMeta : function(e) {
364         e.preventDefault();
365         if(this.get('disabled') === false){
366             switch (e.charCode) {
367                 case 37: // left + meta
368                     this._setToMin();
369                     break;
370                 case 39: // right + meta
371                     this._setToMax();
372                     break;
373             }
374         }
375     },
381     /**
382      * Makes the thumb draggable and constrains it to the rail.
383      *
384      * @method _bindThumbDD
385      * @protected
386      */
387     _bindThumbDD: function () {
388         var config = { constrain: this.rail };
389         
390         // { constrain: rail, stickX: true }
391         config[ 'stick' + this.axis.toUpperCase() ] = true;
393         /** 
394          * The DD.Drag instance linked to the thumb node.
395          *
396          * @property _dd
397          * @type {DD.Drag}
398          * @protected
399          */
400         this._dd = new Y.DD.Drag( {
401             node   : this.thumb,
402             bubble : false,
403             on     : {
404                 'drag:start': Y.bind( this._onDragStart, this )
405             },
406             after  : {
407                 'drag:drag': Y.bind( this._afterDrag,    this ),
408                 'drag:end' : Y.bind( this._afterDragEnd, this )
409             }
410         } );
412         // Constrain the thumb to the rail
413         this._dd.plug( Y.Plugin.DDConstrained, config );
414     },
416     /**
417      * Stub implementation.  Override this (presumably in a class extension) to
418      * initialize any value logic that depends on the presence of the Drag
419      * instance.
420      *
421      * @method _bindValueLogic
422      * @protected
423      */
424     _bindValueLogic: function () {},
426     /**
427      * Moves the thumb to pixel offset position along the rail.
428      *
429      * @method _uiMoveThumb
430      * @param offset {Number} the pixel offset to set as left or top style
431      * @param [options] {Object} Details to send with the `thumbMove` event
432      * @protected
433      */
434     _uiMoveThumb: function ( offset, options ) {
435         if ( this.thumb ) {
436             this.thumb.setStyle( this._key.minEdge, offset + 'px' );
438             Y.log("Setting thumb " + this._key.minEdge + " to " + offset + "px","info","slider");
440             options || (options = {});
441             options.offset = offset;
443             this.fire( 'thumbMove', options );
444         }
445     },
447     /**
448      * Dispatches the <code>slideStart</code> event.
449      *
450      * @method _onDragStart
451      * @param e {Event} the <code>drag:start</code> event from the thumb
452      * @protected
453      */
454     _onDragStart: function ( e ) {
455         /**
456          * Signals the beginning of a thumb drag operation.  Payload includes
457          * the thumb's drag:start event.
458          *
459          * @event slideStart
460          * @param event {Event} The event object for the slideStart with the
461          *                      following extra properties:
462          *  <dl>
463          *      <dt>ddEvent (deprecated)</dt>
464          *          <dd><code>drag:start</code> event from the thumb</dd>
465          *      <dt>originEvent</dt>
466          *          <dd><code>drag:start</code> event from the thumb</dd>
467          *  </dl>
468          */
469         this.fire('slideStart', {
470            ddEvent: e, // for backward compatibility
471            originEvent: e
472         });
473     },
475     /**
476      * Dispatches the <code>thumbMove</code> event.
477      *
478      * @method _afterDrag
479      * @param e {Event} the <code>drag:drag</code> event from the thumb
480      * @protected
481      */
482     _afterDrag: function ( e ) {
483         var thumbXY = e.info.xy[ this._key.xyIndex ],
484             railXY  = e.target.con._regionCache[ this._key.minEdge ];
486         Y.log("Thumb position: " + thumbXY + ", Rail position: " + railXY, "info", "slider");
487         this.fire( 'thumbMove', {
488             offset : (thumbXY - railXY),
489             ddEvent: e, // for backward compatibility
490             originEvent: e
491         } );
492     },
494     /**
495      * Dispatches the <code>slideEnd</code> event.
496      *
497      * @method _onDragEnd
498      * @param e {Event} the <code>drag:end</code> event from the thumb
499      * @protected
500      */
501     _afterDragEnd: function ( e ) {
502         /**
503          * Signals the end of a thumb drag operation.  Payload includes
504          * the thumb's drag:end event.
505          *
506          * @event slideEnd
507          * @param event {Event} The event object for the slideEnd with the
508          *                      following extra properties:
509          *  <dl>
510          *      <dt>ddEvent (deprecated)</dt>
511          *          <dd><code>drag:end</code> event from the thumb</dd>
512          *      <dt>originEvent</dt>
513          *          <dd><code>drag:end</code> event from the thumb</dd>
514          *  </dl>
515          */
516         this.fire('slideEnd', {
517             ddEvent: e,
518             originEvent: e
519         });
520     },
522     /**
523      * Locks or unlocks the thumb.
524      *
525      * @method _afterDisabledChange
526      * @param e {Event} The disabledChange event object
527      * @protected
528      */
529     _afterDisabledChange: function ( e ) {
530         this._dd.set( 'lock', e.newVal );
531     },
533     /**
534      * Handles changes to the <code>length</code> attribute.  By default, it
535      * triggers an update to the UI.
536      *
537      * @method _afterLengthChange
538      * @param e {Event} The lengthChange event object
539      * @protected
540      */
541     _afterLengthChange: function ( e ) {
542         if ( this.get( 'rendered' ) ) {
543             this._uiSetRailLength( e.newVal );
545             this.syncUI();
546         }
547     },
549     /**
550      * Synchronizes the DOM state with the attribute settings.
551      *
552      * @method syncUI
553      */
554     syncUI : function () {
555         this._dd.con.resetCache();
557         this._syncThumbPosition();
559         // Forces a reflow of the bounding box to address IE8 inline-block
560         // container not expanding correctly. bug 2527905
561         //this.get('boundingBox').toggleClass('');
562         this.thumb.set('aria-valuemin', this.get('min'));
563         this.thumb.set('aria-valuemax', this.get('max'));
564     },
566     /**
567      * Stub implementation.  Override this (presumably in a class extension) to
568      * ensure the thumb is in the correct position according to the value
569      * alogorithm.
570      * instance.
571      *
572      * @method _syncThumbPosition
573      * @protected
574      */
575     _syncThumbPosition: function () {},
577     /**
578      * Validates the axis is &quot;x&quot; or &quot;y&quot; (case insensitive).
579      * Converts to lower case for storage.
580      *
581      * @method _setAxis
582      * @param v {String} proposed value for the axis attribute
583      * @return {String} lowercased first character of the input string
584      * @protected
585      */
586     _setAxis : function (v) {
587         v = ( v + '' ).toLowerCase();
589         return ( v === 'x' || v === 'y' ) ? v : INVALID_VALUE;
590     },
592     /** 
593      * <p>Ensures the stored length value is a string with a quantity and unit.
594      * Unit will be defaulted to &quot;px&quot; if not included.  Rejects
595      * values less than or equal to 0 and those that don't at least start with
596      * a number.</p>
597      *
598      * <p>Currently only pixel lengths are supported.</p>
599      *
600      * @method _setLength
601      * @param v {String} proposed value for the length attribute
602      * @return {String} the sanitized value
603      * @protected
604      */
605     _setLength: function ( v ) {
606         v = ( v + '' ).toLowerCase();
608         var length = parseFloat( v, 10 ),
609             units  = v.replace( /[\d\.\-]/g, '' ) || this.DEF_UNIT;
611         return length > 0 ? ( length + units ) : INVALID_VALUE;
612     },
614     /**
615      * <p>Defaults the thumbURL attribute according to the current skin, or
616      * &quot;sam&quot; if none can be determined.  Horizontal Sliders will have
617      * their <code>thumbUrl</code> attribute set to</p>
618      * <p><code>&quot;/<em>configured</em>/<em>yu</em>i/<em>builddi</em>r/slider-base/assets/skins/sam/thumb-x.png&quot;</code></p>
619      * <p>And vertical thumbs will get</p>
620      * <p><code>&quot;/<em>configured</em>/<em>yui</em>/<em>builddir</em>/slider-base/assets/skins/sam/thumb-y.png&quot;</code></p>
621      *
622      * @method _initThumbUrl
623      * @protected
624      */
625     _initThumbUrl: function () {
626         if (!this.get('thumbUrl')) {
627             var skin = this.getSkinName() || 'sam',
628                 base = Y.config.base;
630             // Unfortunate hack to avoid requesting image resources from the
631             // combo service.  The combo service does not serve images.
632             if (base.indexOf('http://yui.yahooapis.com/combo') === 0) {
633                 base = 'http://yui.yahooapis.com/' + Y.version + '/build/';
634             }
636             // <img src="/path/to/build/slider-base/assets/skins/sam/thumb-x.png">
637             this.set('thumbUrl', base + 'slider-base/assets/skins/' +
638                                  skin + '/thumb-' + this.axis + '.png');
640         }
641     },
643     /**
644      * Bounding box template that will contain the Slider's DOM subtree.  &lt;span&gt;s are used to support inline-block styling.
645      *
646      * @property BOUNDING_TEMPLATE
647      * @type {String}
648      * @default &lt;span>&lt;/span>
649      */
650     BOUNDING_TEMPLATE : '<span></span>',
652     /**
653      * Content box template that will contain the Slider's rail and thumb.
654      *
655      * @property CONTENT_TEMPLATE
656      * @type {String}
657      * @default &lt;span>&lt;/span>
658      */
659     CONTENT_TEMPLATE  : '<span></span>',
661     /**
662      * Rail template that will contain the end caps and the thumb.
663      * {placeholder}s are used for template substitution at render time.
664      *
665      * @property RAIL_TEMPLATE
666      * @type {String}
667      * @default &lt;span class="{railClass}">&lt;span class="{railMinCapClass}">&lt;/span>&lt;span class="{railMaxCapClass}">&lt;/span>&lt;/span>
668      */
669     RAIL_TEMPLATE     : '<span class="{railClass}">' +
670                             '<span class="{railMinCapClass}"></span>' +
671                             '<span class="{railMaxCapClass}"></span>' +
672                         '</span>',
674     /**
675      * Thumb template that will contain the thumb image and shadow. &lt;img>
676      * tags are used instead of background images to avoid a flicker bug in IE.
677      * {placeholder}s are used for template substitution at render time.
678      *
679      * @property THUMB_TEMPLATE
680      * @type {String}
681      * @default &lt;span class="{thumbClass}" tabindex="-1">&lt;img src="{thumbShadowUrl}" alt="Slider thumb shadow" class="{thumbShadowClass}">&lt;img src="{thumbImageUrl}" alt="Slider thumb" class="{thumbImageClass}">&lt;/span>
682      */
683     THUMB_TEMPLATE    : '<span class="{thumbClass}" aria-labelledby="{thumbAriaLabelId}" aria-valuetext="" aria-valuemax="" aria-valuemin="" aria-valuenow="" role="slider" tabindex="0">' +   // keyboard access jeff     tabindex="-1"
684                             '<img src="{thumbShadowUrl}" ' +
685                                 'alt="Slider thumb shadow" ' +
686                                 'class="{thumbShadowClass}">' +
687                             '<img src="{thumbImageUrl}" ' +
688                                 'alt="Slider thumb" ' +
689                                 'class="{thumbImageClass}">' +
690                         '</span>'
692 }, {
694     // Y.SliderBase static properties
696     /**
697      * The identity of the widget.
698      *
699      * @property NAME
700      * @type String
701      * @default 'sliderBase'
702      * @readOnly
703      * @protected
704      * @static
705      */
706     NAME : 'sliderBase',
708     /**
709      * Static property used to define the default attribute configuration of
710      * the Widget.
711      *
712      * @property ATTRS
713      * @type {Object}
714      * @protected
715      * @static
716      */
717     ATTRS : {
719         /**
720          * Axis upon which the Slider's thumb moves.  &quot;x&quot; for
721          * horizontal, &quot;y&quot; for vertical.
722          *
723          * @attribute axis
724          * @type {String}
725          * @default &quot;x&quot;
726          * @writeOnce
727          */
728         axis : {
729             value     : 'x',
730             writeOnce : true,
731             setter    : '_setAxis',
732             lazyAdd   : false
733         },
735         /**
736          * The length of the rail (exclusive of the end caps if positioned by
737          * CSS).  This corresponds to the movable range of the thumb.
738          *
739          * @attribute length
740          * @type {String | Number} e.g. "200px" or 200
741          * @default 150px
742          */
743         length: {
744             value: '150px',
745             setter: '_setLength'
746         },
748         /**
749          * Path to the thumb image.  This will be used as both the thumb and
750          * shadow as a sprite.  Defaults at render() to thumb-x.png or
751          * thumb-y.png in the skin directory of the current skin.
752          *
753          * @attribute thumbUrl
754          * @type {String}
755          * @default thumb-x.png or thumb-y.png in the sam skin directory of the
756          *          current build path for Slider
757          */
758         thumbUrl: {
759             value: null,
760             validator: Y.Lang.isString
761         }
762     }
767 }, '3.5.1' ,{requires:['widget', 'substitute', 'dd-constrain', 'event-key']});