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