|
1 /* |
|
2 Copyright (c) 2009, Yahoo! Inc. All rights reserved. |
|
3 Code licensed under the BSD License: |
|
4 http://developer.yahoo.net/yui/license.txt |
|
5 version: 3.0.0 |
|
6 build: 1549 |
|
7 */ |
|
8 YUI.add('slider', function(Y) { |
|
9 |
|
10 /** |
|
11 * Create a sliding value range input visualized as a draggable thumb on a |
|
12 * background element. |
|
13 * |
|
14 * @module slider |
|
15 */ |
|
16 |
|
17 var SLIDER = 'slider', |
|
18 RAIL = 'rail', |
|
19 THUMB = 'thumb', |
|
20 VALUE = 'value', |
|
21 MIN = 'min', |
|
22 MAX = 'max', |
|
23 MIN_GUTTER = 'minGutter', |
|
24 MAX_GUTTER = 'maxGutter', |
|
25 THUMB_IMAGE = 'thumbImage', |
|
26 RAIL_SIZE = 'railSize', |
|
27 CONTENT_BOX = 'contentBox', |
|
28 |
|
29 SLIDE_START = 'slideStart', |
|
30 SLIDE_END = 'slideEnd', |
|
31 |
|
32 THUMB_DRAG = 'thumbDrag', |
|
33 SYNC = 'sync', |
|
34 POSITION_THUMB = 'positionThumb', |
|
35 RENDERED = 'rendered', |
|
36 DISABLED = 'disabled', |
|
37 DISABLED_CHANGE = 'disabledChange', |
|
38 |
|
39 DOT = '.', |
|
40 PX = 'px', |
|
41 WIDTH = 'width', |
|
42 HEIGHT = 'height', |
|
43 COMPLETE = 'complete', |
|
44 |
|
45 L = Y.Lang, |
|
46 isBoolean= L.isBoolean, |
|
47 isString = L.isString, |
|
48 isNumber = L.isNumber, |
|
49 |
|
50 getCN = Y.ClassNameManager.getClassName, |
|
51 |
|
52 IMAGE = 'image', |
|
53 C_RAIL = getCN(SLIDER,RAIL), |
|
54 C_THUMB = getCN(SLIDER,THUMB), |
|
55 C_THUMB_IMAGE = getCN(SLIDER,THUMB,IMAGE), |
|
56 C_IMAGE_ERROR = getCN(SLIDER,IMAGE,'error'), |
|
57 |
|
58 M = Math, |
|
59 max = M.max, |
|
60 round = M.round, |
|
61 floor = M.floor; |
|
62 |
|
63 /** |
|
64 * Create a slider to represent an integer value between a given minimum and |
|
65 * maximum. Sliders may be aligned vertically or horizontally, based on the |
|
66 * <code>axis</code> configuration. |
|
67 * |
|
68 * @class Slider |
|
69 * @extends Widget |
|
70 * @param config {Object} Configuration object |
|
71 * @constructor |
|
72 */ |
|
73 function Slider() { |
|
74 Slider.superclass.constructor.apply(this,arguments); |
|
75 } |
|
76 |
|
77 Y.mix(Slider, { |
|
78 |
|
79 /** |
|
80 * The identity of the widget. |
|
81 * |
|
82 * @property Slider.NAME |
|
83 * @type String |
|
84 * @static |
|
85 */ |
|
86 NAME : SLIDER, |
|
87 |
|
88 /** |
|
89 * Object property names used for respective X and Y axis Sliders (e.g. |
|
90 * "left" vs. "top" for placing the thumb according to |
|
91 * its representative value). |
|
92 * |
|
93 * @property Slider._AXIS_KEYS |
|
94 * @type Object |
|
95 * @protected |
|
96 * @static |
|
97 */ |
|
98 _AXIS_KEYS : { |
|
99 x : { |
|
100 dim : WIDTH, |
|
101 offAxisDim : HEIGHT, |
|
102 eventPageAxis : 'pageX', |
|
103 ddStick : 'stickX', |
|
104 xyIndex : 0 |
|
105 }, |
|
106 y : { |
|
107 dim : HEIGHT, |
|
108 offAxisDim : WIDTH, |
|
109 eventPageAxis : 'pageY', |
|
110 ddStick : 'stickY', |
|
111 xyIndex : 1 |
|
112 } |
|
113 }, |
|
114 |
|
115 /** |
|
116 * Static Object hash used to capture existing markup for progressive |
|
117 * enhancement. Keys correspond to config attribute names and values |
|
118 * are selectors used to inspect the contentBox for an existing node |
|
119 * structure. |
|
120 * |
|
121 * @property Slider.HTML_PARSER |
|
122 * @type Object |
|
123 * @protected |
|
124 * @static |
|
125 */ |
|
126 HTML_PARSER : { |
|
127 rail : DOT + C_RAIL, |
|
128 thumb : DOT + C_THUMB, |
|
129 thumbImage : DOT + C_THUMB_IMAGE |
|
130 }, |
|
131 |
|
132 /** |
|
133 * Static property used to define the default attribute configuration of |
|
134 * the Widget. |
|
135 * |
|
136 * @property Slider.ATTRS |
|
137 * @type Object |
|
138 * @protected |
|
139 * @static |
|
140 */ |
|
141 ATTRS : { |
|
142 |
|
143 /** |
|
144 * Axis upon which the Slider's thumb moves. "x" for |
|
145 * horizontal, "y" for vertical. |
|
146 * |
|
147 * @attribute axis |
|
148 * @type String |
|
149 * @default "x" |
|
150 * @writeOnce |
|
151 */ |
|
152 axis : { |
|
153 value : 'x', |
|
154 writeOnce : true, |
|
155 validator : function (v) { |
|
156 return this._validateNewAxis(v); |
|
157 }, |
|
158 setter : function (v) { |
|
159 return this._setAxisFn(v); |
|
160 } |
|
161 }, |
|
162 |
|
163 /** |
|
164 * Value associated with the left or top most position of the thumb on |
|
165 * the rail. |
|
166 * |
|
167 * @attribute min |
|
168 * @type Number |
|
169 * @default 0 |
|
170 */ |
|
171 min : { |
|
172 value : 0, |
|
173 validator : function (v) { |
|
174 return this._validateNewMin(v); |
|
175 } |
|
176 }, |
|
177 |
|
178 /** |
|
179 * Value associated with the right or bottom most position of the thumb |
|
180 * on the rail. |
|
181 * |
|
182 * @attribute max |
|
183 * @type Number |
|
184 * @default 100 |
|
185 */ |
|
186 max : { |
|
187 value : 100, |
|
188 validator : function (v) { |
|
189 return this._validateNewMax(v); |
|
190 } |
|
191 }, |
|
192 |
|
193 /** |
|
194 * The current value of the Slider. This value is interpretted into a |
|
195 * position for the thumb along the Slider's rail. |
|
196 * |
|
197 * @attribute value |
|
198 * @type Number |
|
199 * @default 0 |
|
200 */ |
|
201 value : { |
|
202 value : 0, |
|
203 validator : function (v) { |
|
204 return this._validateNewValue(v); |
|
205 } |
|
206 }, |
|
207 |
|
208 /** |
|
209 * The Node representing the Slider's rail, usually visualized as a |
|
210 * bar of some sort using a background image, along which the thumb |
|
211 * moves. This Node contains the thumb Node. |
|
212 * |
|
213 * @attribute rail |
|
214 * @type Node |
|
215 * @default null |
|
216 */ |
|
217 rail : { |
|
218 value : null, |
|
219 validator : function (v) { |
|
220 return this._validateNewRail(v); |
|
221 }, |
|
222 setter : function (v) { |
|
223 return this._setRailFn(v); |
|
224 } |
|
225 }, |
|
226 |
|
227 /** |
|
228 * <p>The Node representing the Slider's thumb, usually visualized as a |
|
229 * pointer using a contained image Node (see thumbImage). The current |
|
230 * value of the Slider is calculated from the centerpoint of this |
|
231 * Node in relation to the rail Node. If provided, the thumbImage |
|
232 * Node is contained within this Node.</p> |
|
233 * |
|
234 * <p>If no thumbImage is provided and the Node passed as the thumb is |
|
235 * an <code>img</code> element, the assigned Node will be allocated to |
|
236 * the thumbImage and the thumb container defaulted.</p> |
|
237 * |
|
238 * @attribute thumb |
|
239 * @type Node |
|
240 * @default null |
|
241 */ |
|
242 thumb : { |
|
243 value : null, |
|
244 validator : function (v) { |
|
245 return this._validateNewThumb(v); |
|
246 }, |
|
247 setter : function (v) { |
|
248 return this._setThumbFn(v); |
|
249 } |
|
250 }, |
|
251 |
|
252 /** |
|
253 * <p>The Node representing the image element to use for the Slider's |
|
254 * thumb.</p> |
|
255 * |
|
256 * <p>Alternately, an image URL can be passed and an <code>img</code> |
|
257 * Node will be generated accordingly.</p> |
|
258 * |
|
259 * <p>If no thumbImage is provided and the Node passed as the thumb is |
|
260 * an <code>img</code> element, the assigned Node will be allocated to |
|
261 * the thumbImage and the thumb container defaulted.</p> |
|
262 * |
|
263 * <p>If thumbImage is provided but its URL resolves to a 404, a default |
|
264 * style will be applied to maintain basic functionality.</p> |
|
265 * |
|
266 * @attribute thumbImage |
|
267 * @type Node|String |
|
268 * @default null |
|
269 */ |
|
270 thumbImage : { |
|
271 value : null, |
|
272 validator : function (v) { |
|
273 return this._validateNewThumbImage(v); |
|
274 }, |
|
275 setter : function (v) { |
|
276 return this._setThumbImageFn(v); |
|
277 } |
|
278 }, |
|
279 |
|
280 /** |
|
281 * <p>The width or height of the rail element representing the physical |
|
282 * space along which the thumb can move. CSS size values (e.g. '30em') |
|
283 * accepted but converted to pixels during render.</p> |
|
284 * |
|
285 * <p>Alternately, but not recommended, this attribute can be left |
|
286 * unassigned in favor of specifying height or width.</p> |
|
287 * |
|
288 * @attribute railSize |
|
289 * @type String |
|
290 * @default '0' |
|
291 */ |
|
292 railSize : { |
|
293 value : '0', |
|
294 validator : function (v) { |
|
295 return this._validateNewRailSize(v); |
|
296 } |
|
297 }, |
|
298 |
|
299 /** |
|
300 * Boolean indicating whether clicking and dragging on the rail will |
|
301 * trigger thumb movement. |
|
302 * |
|
303 * @attribute railEnabled |
|
304 * @type Boolean |
|
305 * @default true |
|
306 */ |
|
307 railEnabled : { |
|
308 value : true, |
|
309 validator : isBoolean |
|
310 }, |
|
311 |
|
312 /** |
|
313 * Like CSS padding, the distance in pixels from the inner top or left |
|
314 * edge of the rail node within which the thumb can travel. Negative |
|
315 * values allow the edge of the thumb to escape the rail node |
|
316 * boundaries. |
|
317 * |
|
318 * @attribute minGutter |
|
319 * @type Number |
|
320 * @default 0 |
|
321 */ |
|
322 minGutter : { |
|
323 value : 0, |
|
324 validator : isNumber |
|
325 }, |
|
326 |
|
327 /** |
|
328 * Like CSS padding, the distance in pixels from the inner bottom or |
|
329 * right edge of the rail node within which the thumb can travel. |
|
330 * Negative values allow the edge of the thumb to escape the rail node |
|
331 * boundaries. |
|
332 * |
|
333 * @attribute maxGutter |
|
334 * @type Number |
|
335 * @default 0 |
|
336 */ |
|
337 maxGutter : { |
|
338 value : 0, |
|
339 validator : isNumber |
|
340 } |
|
341 } |
|
342 }); |
|
343 |
|
344 Y.extend(Slider, Y.Widget, { |
|
345 |
|
346 /** |
|
347 * Collection of object property names from the appropriate hash set in |
|
348 * Slider._AXIS_KEYS. |
|
349 * |
|
350 * @property _key |
|
351 * @type Object |
|
352 * @protected |
|
353 */ |
|
354 _key : null, |
|
355 |
|
356 /** |
|
357 * Factor used to translate positional coordinates (e.g. left or top) to |
|
358 * the Slider's value. |
|
359 * |
|
360 * @property _factor |
|
361 * @type Number |
|
362 * @protected |
|
363 */ |
|
364 _factor : 1, |
|
365 |
|
366 /** |
|
367 * Pixel dimension of the rail Node's width for X axis Sliders or height |
|
368 * for Y axis Sliders. Used with _factor to calculate positional |
|
369 * coordinates for the thumb. |
|
370 * |
|
371 * @property _railSize |
|
372 * @type Number |
|
373 * @protected |
|
374 */ |
|
375 _railSize : null, |
|
376 |
|
377 /** |
|
378 * Pixel dimension of the thumb Node's width for X axis Sliders or height |
|
379 * for Y axis Sliders. Used with _factor to calculate positional |
|
380 * coordinates for the thumb. |
|
381 * |
|
382 * @property _thumbSize |
|
383 * @type Number |
|
384 * @protected |
|
385 */ |
|
386 _thumbSize : null, |
|
387 |
|
388 /** |
|
389 * Pixel offset of the point in the thumb element from its top/left edge |
|
390 * to where the value calculation should take place. By default, this is |
|
391 * calculated to half the width of the thumb, causing the value to be |
|
392 * marked from the center of the thumb. |
|
393 * |
|
394 * @property _thumbOffset |
|
395 * @type Number |
|
396 * @protected |
|
397 */ |
|
398 _thumbOffset : 0, |
|
399 |
|
400 /** |
|
401 * Object returned from temporary subscription to disabledChange event to |
|
402 * defer setting the disabled state while Slider is loading the thumb |
|
403 * image. |
|
404 * |
|
405 * @property _stall |
|
406 * @type Object |
|
407 * @protected |
|
408 */ |
|
409 _stall : false, |
|
410 |
|
411 /** |
|
412 * Deferred value for the disabled attribute when stalled (see _stall |
|
413 * property). |
|
414 * |
|
415 * @property _disabled |
|
416 * @type Boolean |
|
417 * @protected |
|
418 */ |
|
419 _disabled : false, |
|
420 |
|
421 /** |
|
422 * Construction logic executed durint Slider instantiation. Subscribes to |
|
423 * after events for min, max, and railSize. Publishes custom events |
|
424 * including slideStart and slideEnd. |
|
425 * |
|
426 * @method initializer |
|
427 * @protected |
|
428 */ |
|
429 initializer : function () { |
|
430 this._key = Slider._AXIS_KEYS[this.get('axis')]; |
|
431 |
|
432 this.after('minChange', this._afterMinChange); |
|
433 this.after('maxChange', this._afterMaxChange); |
|
434 |
|
435 this.after('railSizeChange', this._afterRailSizeChange); |
|
436 |
|
437 /** |
|
438 * Signals the beginning of a thumb drag operation. Payload includes |
|
439 * the DD.Drag instance's drag:start event under key ddEvent. |
|
440 * |
|
441 * @event slideStart |
|
442 * @param event {Event.Facade} An Event Facade object with the following attribute specific properties added: |
|
443 * <dl> |
|
444 * <dt>ddEvent</dt> |
|
445 * <dd><code>drag:start</code> event from the managed DD.Drag instance</dd> |
|
446 * </dl> |
|
447 */ |
|
448 this.publish(SLIDE_START); |
|
449 |
|
450 /** |
|
451 * Signals the end of a thumb drag operation. Payload includes |
|
452 * the DD.Drag instance's drag:end event under key ddEvent. |
|
453 * |
|
454 * @event slideEnd |
|
455 * @param event {Event.Facade} An Event Facade object with the following attribute specific properties added: |
|
456 * <dl> |
|
457 * <dt>ddEvent</dt> |
|
458 * <dd><code>drag:end</code> event from the managed DD.Drag instance</dd> |
|
459 * </dl> |
|
460 */ |
|
461 this.publish(SLIDE_END); |
|
462 |
|
463 /** |
|
464 * Communicates a request to synchronize the Slider UI with the |
|
465 * attribute state. Links the sync request with the default sync |
|
466 * logic in _defSyncFn. |
|
467 * |
|
468 * @event sync |
|
469 * @param event {Event.Facade} Event Facade object |
|
470 * @preventable _defSyncFn |
|
471 */ |
|
472 this.publish(SYNC, { defaultFn: this._defSyncFn }); |
|
473 |
|
474 /** |
|
475 * Signals a request to reposition the thumb in response to API methods. |
|
476 * Triggers the thumb placement logic in _defPositionThumbFn. |
|
477 * |
|
478 * @event positionThumb |
|
479 * @param event {Event.Facade} An Event Facade object with the following attribute specific properties added: |
|
480 * <dl> |
|
481 * <dt>changeEv</dt> |
|
482 * <dd><code>valueChange</code> event fired in response to the change in the value attribute</dd> |
|
483 * </dl> |
|
484 * @preventable _defPositionThumbFn |
|
485 */ |
|
486 this.publish(POSITION_THUMB, { defaultFn: this._defPositionThumbFn }); |
|
487 }, |
|
488 |
|
489 /** |
|
490 * Create the DOM structure for the Slider. |
|
491 * |
|
492 * @method renderUI |
|
493 * @protected |
|
494 */ |
|
495 renderUI : function () { |
|
496 this._initRail(); |
|
497 this._initThumb(); |
|
498 }, |
|
499 |
|
500 /** |
|
501 * Creates the rail element if not provided and not discovered via |
|
502 * HTML_PARSER. |
|
503 * |
|
504 * @method _initRail |
|
505 * @protected |
|
506 */ |
|
507 _initRail : function () { |
|
508 var cb = this.get(CONTENT_BOX), |
|
509 rail = this.get(RAIL); |
|
510 |
|
511 // Create rail if necessary. Make sure it's in the contentBox |
|
512 if (!rail) { |
|
513 rail = cb.appendChild( |
|
514 Y.Node.create('<div class="'+C_RAIL+'"></div>')); |
|
515 |
|
516 this.set(RAIL,rail); |
|
517 } else if (!cb.contains(rail)) { |
|
518 cb.appendChild(rail); |
|
519 } |
|
520 |
|
521 rail.addClass(C_RAIL); |
|
522 rail.addClass(this.getClassName(RAIL,this.get('axis'))); |
|
523 }, |
|
524 |
|
525 /** |
|
526 * <p>Creates the thumb element (not image) if not provided and not |
|
527 * discovered via HTML_PARSER. If the thumb is an <code>img</code> element |
|
528 * but no thumbImage configured or discovered, reassigns the thumb element |
|
529 * to the thumbImage and defaults the thumb element as a div.</p> |
|
530 * |
|
531 * <p>Makes sure the thumb is a child of the rail element and calls |
|
532 * _initThumbImage if thumbImage is provided.</p> |
|
533 * |
|
534 * @method _initThumb |
|
535 * @protected |
|
536 */ |
|
537 _initThumb : function () { |
|
538 var rail = this.get(RAIL), |
|
539 thumb = this.get(THUMB); |
|
540 |
|
541 // Passed an img element as the thumb |
|
542 if (thumb && !this.get(THUMB_IMAGE) && |
|
543 thumb.get('nodeName').toLowerCase() === 'img') { |
|
544 this.set(THUMB_IMAGE, thumb); |
|
545 this.set(THUMB,null); |
|
546 thumb = null; |
|
547 } |
|
548 |
|
549 if (!thumb) { |
|
550 thumb = Y.Node.create( |
|
551 '<div class="'+C_THUMB+'"></div>'); |
|
552 |
|
553 this.set(THUMB,thumb); |
|
554 } |
|
555 |
|
556 thumb.addClass(C_THUMB); |
|
557 |
|
558 if (!rail.contains(thumb)) { |
|
559 rail.appendChild(thumb); |
|
560 } |
|
561 |
|
562 if (this.get(THUMB_IMAGE)) { |
|
563 this._initThumbImage(); |
|
564 } |
|
565 }, |
|
566 |
|
567 /** |
|
568 * Ensures the thumbImage is a child of the thumb element. |
|
569 * |
|
570 * @method _initThumbImage |
|
571 * @protected |
|
572 */ |
|
573 _initThumbImage : function () { |
|
574 var thumb = this.get(THUMB), |
|
575 img = this.get(THUMB_IMAGE); |
|
576 |
|
577 if (img) { |
|
578 img.replaceClass(C_THUMB,C_THUMB_IMAGE); |
|
579 |
|
580 if (!thumb.contains(img)) { |
|
581 thumb.appendChild(img); |
|
582 } |
|
583 } |
|
584 }, |
|
585 |
|
586 /** |
|
587 * Creates the Y.DD instance used to handle the thumb movement and binds |
|
588 * Slider interaction to the configured value model. |
|
589 * |
|
590 * @method bindUI |
|
591 * @protected |
|
592 */ |
|
593 bindUI : function () { |
|
594 /** |
|
595 * Bridges user interaction with the thumb to the value attribute. |
|
596 * |
|
597 * @event thumbDrag |
|
598 * @param event {Event.Facade} An Event Facade object with the following attribute specific properties added: |
|
599 * <dl> |
|
600 * <dt>ddEvent</dt> |
|
601 * <dd><code>drag:drag</code> event from the managed DD.Drag instance</dd> |
|
602 * </dl> |
|
603 * @preventable _defThumbDragFn |
|
604 */ |
|
605 this.publish(THUMB_DRAG, {defaultFn: this._defThumbDragFn}); |
|
606 |
|
607 this._bindThumbDD(); |
|
608 |
|
609 this.after('valueChange', this._afterValueChange); |
|
610 this.after('thumbImageChange', this._afterThumbImageChange); |
|
611 this.after(DISABLED_CHANGE, this._afterDisabledChange); |
|
612 }, |
|
613 |
|
614 /** |
|
615 * Creates the Y.DD instance used to handle the thumb interaction. |
|
616 * |
|
617 * @method _bindThumbDD |
|
618 * @protected |
|
619 */ |
|
620 _bindThumbDD : function () { |
|
621 var ddConf = { |
|
622 node : this.get(THUMB), |
|
623 bubble : false |
|
624 }, |
|
625 conConf = { |
|
626 constrain2node : this.get(RAIL) |
|
627 }; |
|
628 |
|
629 conConf[this._key.ddStick] = true; |
|
630 |
|
631 this._dd = new Y.DD.Drag(ddConf).plug(Y.Plugin.DDConstrained, conConf); |
|
632 this._dd.on('drag:start', Y.bind(this._onDDStartDrag, this)); |
|
633 this._dd.on('drag:drag', Y.bind(this._onDDDrag, this)); |
|
634 this._dd.on('drag:end', Y.bind(this._onDDEndDrag, this)); |
|
635 |
|
636 this._initRailDD(); |
|
637 }, |
|
638 |
|
639 /** |
|
640 * Subscribes to the rail Node's mousedown event to actuate the thumb when |
|
641 * backgroundEnabled is true. |
|
642 * |
|
643 * @method _initRailDD |
|
644 * @protected |
|
645 */ |
|
646 _initRailDD : function () { |
|
647 this.get(RAIL).on('mousedown',Y.bind(this._handleRailMouseDown,this)); |
|
648 }, |
|
649 |
|
650 /** |
|
651 * If the Slider is not disabled and railEnabled is true, moves the thumb |
|
652 * to the mousedown position and hands control over to DD. |
|
653 * |
|
654 * @method _handleRailMouseDown |
|
655 * @param e {Event} Mousedown event facade |
|
656 * @protected |
|
657 */ |
|
658 _handleRailMouseDown : function (e) { |
|
659 if (this.get('railEnabled') && !this.get(DISABLED)) { |
|
660 var dd = this._dd, |
|
661 xyIndex = this._key.xyIndex, |
|
662 xy; |
|
663 |
|
664 if (dd.get('primaryButtonOnly') && e.button > 1) { |
|
665 Y.log('Mousedown was not produced by the primary button', |
|
666 'warn', 'dd-drag'); |
|
667 return false; |
|
668 } |
|
669 |
|
670 dd._dragThreshMet = true; |
|
671 |
|
672 dd._fixIEMouseDown(); |
|
673 e.halt(); |
|
674 |
|
675 Y.DD.DDM.activeDrag = dd; |
|
676 |
|
677 // Adjust registered starting position by half the thumb's x/y |
|
678 xy = dd.get('dragNode').getXY(); |
|
679 xy[xyIndex] += this._thumbOffset; |
|
680 |
|
681 dd._setStartPosition(xy); |
|
682 dd.set('activeHandle',dd.get('dragNode')); |
|
683 |
|
684 dd.start(); |
|
685 dd._alignNode([e.pageX,e.pageY]); |
|
686 } |
|
687 }, |
|
688 |
|
689 /** |
|
690 * Synchronizes the DOM state with the attribute settings (most notably |
|
691 * railSize and value). If thumbImage is provided and is still loading, |
|
692 * sync is delayed until it is complete, since the image's dimensions are |
|
693 * taken into consideration for calculations. |
|
694 * |
|
695 * @method syncUI |
|
696 */ |
|
697 syncUI : function () { |
|
698 this.get(CONTENT_BOX).removeClass(C_IMAGE_ERROR); |
|
699 |
|
700 var img = this.get(THUMB_IMAGE); |
|
701 |
|
702 if (this._isImageLoading(img)) { |
|
703 Y.log('Thumb image loading. Scheduling sync.','info','slider'); |
|
704 // Schedule the sync for when the image loads/errors |
|
705 this._scheduleSync(); |
|
706 } else { |
|
707 Y.log('No thumb image, or image already loaded. Syncing immediately.','info','slider'); |
|
708 this._ready(img,!this._isImageLoaded(img)); |
|
709 } |
|
710 }, |
|
711 |
|
712 /** |
|
713 * Binds to the load and error event on the thumbImage to sync the DOM |
|
714 * state with the attribute settings when the image resource is resolved. |
|
715 * The Slider is disabled while it waits. |
|
716 * |
|
717 * @method _scheduleSync |
|
718 * @protected |
|
719 */ |
|
720 _scheduleSync : function () { |
|
721 var img, handler; |
|
722 |
|
723 if (!this._stall) { |
|
724 // disable the control until the image is loaded |
|
725 this._disabled = this.get(DISABLED); |
|
726 this.set(DISABLED,true); |
|
727 this._stall = this.on(DISABLED_CHANGE,this._stallDisabledChange); |
|
728 |
|
729 img = this.get(THUMB_IMAGE); |
|
730 handler = Y.bind(this._imageLoaded,this,img); |
|
731 img.on('load', handler); |
|
732 img.on('error',handler); |
|
733 } |
|
734 }, |
|
735 |
|
736 /** |
|
737 * Method subscribed to the disabledChange event when thumbImage is being |
|
738 * loaded. Prevents manually enabling the Slider until the thumbImage |
|
739 * resource is resolved. Intended value is stored during load and set upon |
|
740 * completion. |
|
741 * |
|
742 * @method _stallDisabledChange |
|
743 * @param e {Event} Change event for the disabled attribute |
|
744 * @protected |
|
745 */ |
|
746 _stallDisabledChange : function (e) { |
|
747 this._disabled = e.newVal; |
|
748 e.preventDefault(); |
|
749 }, |
|
750 |
|
751 /** |
|
752 * Event handler assigned to the thumbImage's load and error event if it |
|
753 * was not loaded prior to instantiation. Restores the disabled value. |
|
754 * |
|
755 * @method _imageLoaded |
|
756 * @param img {Node} The thumbImage Node |
|
757 * @param e {Event} load or error event fired by the thumbImage |
|
758 * @protected |
|
759 */ |
|
760 _imageLoaded : function (img,e) { |
|
761 var error = (e.type.toLowerCase().indexOf('error') > -1); |
|
762 |
|
763 // Need to execute inside a setTimeout because IE doesn't report |
|
764 // img.complete === true until after the img.onload handler |
|
765 // @TODO: readyState reports correctly in onload. Lose this wrapper |
|
766 // and use that in _isImageLoaded. |
|
767 Y.later(0, this, function () { |
|
768 if (this._stall) { |
|
769 this._stall.detach(); |
|
770 } |
|
771 |
|
772 Y.log('Thumb image '+e.type+'ed. Syncing','info','slider'); |
|
773 |
|
774 this._stall = false; |
|
775 |
|
776 this._ready(img,error); |
|
777 |
|
778 this.set(DISABLED,this._disabled); |
|
779 }); |
|
780 }, |
|
781 |
|
782 /** |
|
783 * Applies a class to the content box if the thumbImage failed to resolve, |
|
784 * the fires the internal sync event triggering a sync between UI and |
|
785 * state. |
|
786 * |
|
787 * @method _ready |
|
788 * @param img {Node} the thumbImage Node |
|
789 * @param error {Boolean} Indicates an error while loading the thumbImage |
|
790 * @protected |
|
791 */ |
|
792 _ready : function (img,error) { |
|
793 var method = error ? 'addClass' : 'removeClass'; |
|
794 |
|
795 // If the thumb image url results in 404, assign a class to provide |
|
796 // default thumb dimensions/UI |
|
797 this.get(CONTENT_BOX)[method](C_IMAGE_ERROR); |
|
798 |
|
799 this.fire(SYNC); |
|
800 }, |
|
801 |
|
802 /** |
|
803 * The default synchronization behavior, updating the Slider's DOM state to |
|
804 * match the current attribute values. |
|
805 * |
|
806 * @method _defSyncFn |
|
807 * @param e {Event} Internal sync event |
|
808 * @protected |
|
809 */ |
|
810 _defSyncFn : function (e) { |
|
811 this._uiSetThumbSize(); |
|
812 |
|
813 this._setThumbOffset(); |
|
814 |
|
815 this._uiSetRailSize(); |
|
816 |
|
817 this._setRailOffsetXY(); |
|
818 |
|
819 this._setDDGutter(); |
|
820 |
|
821 this._resetDDCacheRegion(); |
|
822 |
|
823 this._setFactor(); |
|
824 |
|
825 var val = this.get(VALUE); |
|
826 |
|
827 this.fire(POSITION_THUMB, { |
|
828 value : val, |
|
829 offset : this._convertValueToOffset(val) |
|
830 }); |
|
831 |
|
832 // Forces a reflow of the bounding box to address IE8 inline-block |
|
833 // container not expanding correctly. bug 2527905 |
|
834 this.get('boundingBox').toggleClass(''); |
|
835 }, |
|
836 |
|
837 /** |
|
838 * Captures the thumb's pixel height or width (depending on the Slider's |
|
839 * axis) for use in positioning calculations. |
|
840 * |
|
841 * @method _uiSetThumbSize |
|
842 * @protected |
|
843 */ |
|
844 _uiSetThumbSize : function () { |
|
845 var thumb = this.get(THUMB), |
|
846 dim = this._key.dim, |
|
847 img = this.get(THUMB_IMAGE), |
|
848 size; |
|
849 |
|
850 // offsetWidth fails in hidden containers |
|
851 size = parseInt(thumb.getComputedStyle(dim),10); |
|
852 |
|
853 Y.log('thumb '+dim+': '+size+'px','info','slider'); |
|
854 |
|
855 if (img && this._isImageLoaded(img)) { |
|
856 Y.log('using thumbImage '+dim+' ('+img.get(dim)+') for _thumbSize','info','slider'); |
|
857 |
|
858 size = img.get(dim); |
|
859 } |
|
860 |
|
861 this._thumbSize = size; |
|
862 }, |
|
863 |
|
864 /** |
|
865 * Establishes the point in the thumb that should align to the rail |
|
866 * position representing the calculated value. |
|
867 * |
|
868 * @method _setThumbOffset |
|
869 * @protected |
|
870 */ |
|
871 _setThumbOffset : function () { |
|
872 this._thumbOffset = floor(this._thumbSize / 2); |
|
873 Y.log('_thumbOffset calculated to '+this._thumbOffset+'px','info','slider'); |
|
874 }, |
|
875 |
|
876 /** |
|
877 * Stores the rail Node's pixel height or width, depending on the Slider's |
|
878 * axis, for use in calculating thumb position from the value. |
|
879 * |
|
880 * @method _uiSetRailSize |
|
881 * @protected |
|
882 */ |
|
883 _uiSetRailSize : function () { |
|
884 var rail = this.get(RAIL), |
|
885 thumb = this.get(THUMB), |
|
886 img = this.get(THUMB_IMAGE), |
|
887 dim = this._key.dim, |
|
888 size = this.get(RAIL_SIZE), |
|
889 setxy = false; |
|
890 |
|
891 if (parseInt(size,10)) { |
|
892 Y.log('railSize provided: '+size,'info','slider'); |
|
893 |
|
894 // Convert to pixels |
|
895 rail.setStyle(dim,size); |
|
896 size = parseInt(rail.getComputedStyle(dim),10); |
|
897 |
|
898 Y.log('pixel '+dim+' of railSize: '+size+'px', 'info', 'slider'); |
|
899 } else { |
|
900 Y.log('defaulting railSize from max of computed style and configured '+dim+' attribute value', 'info', 'slider'); |
|
901 // Default from height or width (axis respective), or dims assigned |
|
902 // via css to the rail or thumb, whichever is largest. |
|
903 // Dear implementers, please use railSize, not height/width to |
|
904 // set the rail dims |
|
905 size = this.get(dim); |
|
906 if (parseInt(size,10)) { |
|
907 setxy = true; |
|
908 rail.setStyle(dim,size); |
|
909 size = parseInt(rail.getComputedStyle(dim),10); |
|
910 } |
|
911 size = max( |
|
912 size|0, |
|
913 parseInt(thumb.getComputedStyle(dim),10), |
|
914 parseInt(rail.getComputedStyle(dim),10)); |
|
915 |
|
916 Y.log('pixel '+dim+' of rail: '+size+'px', 'info', 'slider'); |
|
917 |
|
918 if (img && this._isImageLoaded(img)) { |
|
919 Y.log('using max of thumbImage '+dim+' ('+img.get(dim)+' and '+size+' for railSize', 'info', 'slider'); |
|
920 |
|
921 size = max(img.get(dim),size); |
|
922 } |
|
923 } |
|
924 |
|
925 rail.setStyle(dim, size + PX); |
|
926 |
|
927 this._railSize = size; |
|
928 |
|
929 // handle the (not recommended) fallback case of setting rail size via |
|
930 // widget height/width params. This is the only case that sets the |
|
931 // off-axis rail dim in the code. |
|
932 if (setxy) { |
|
933 dim = this._key.offAxisDim; |
|
934 size = this.get(dim); |
|
935 if (size) { |
|
936 rail.set(dim,size); |
|
937 } |
|
938 } |
|
939 }, |
|
940 |
|
941 /** |
|
942 * Store the current XY position of the rail Node on the page. For use in |
|
943 * calculating thumb position from value. |
|
944 * |
|
945 * @method _setRailOffsetXY |
|
946 * @protected |
|
947 */ |
|
948 _setRailOffsetXY : function () { |
|
949 this._offsetXY = this.get(RAIL).getXY()[this._key.xyIndex] + |
|
950 this.get(MIN_GUTTER); |
|
951 }, |
|
952 |
|
953 /** |
|
954 * Passes the gutter attribute value to the DDConstrain gutter attribute. |
|
955 * |
|
956 * @method _setDDGutter |
|
957 * @protected |
|
958 */ |
|
959 _setDDGutter : function () { |
|
960 var gutter = this._key.xyIndex ? |
|
961 this.get(MIN_GUTTER) + " 0 " + this.get(MAX_GUTTER) : |
|
962 "0 " + this.get(MAX_GUTTER) + " 0 " + this.get(MIN_GUTTER); |
|
963 |
|
964 Y.log('setting DDConstrain gutter "'+gutter+'"','info','slider'); |
|
965 |
|
966 this._dd.con.set('gutter', gutter); |
|
967 }, |
|
968 |
|
969 /** |
|
970 * Resets the cached region inside the DD constrain instance to support |
|
971 * repositioning the Slider after instantiation. |
|
972 * |
|
973 * @method _resetDDCacheRegion |
|
974 * @protected |
|
975 */ |
|
976 _resetDDCacheRegion : function () { |
|
977 // Workaround for ticket #2527964 |
|
978 this._dd.con._cacheRegion(); |
|
979 }, |
|
980 |
|
981 /** |
|
982 * Calculates the multiplier used to translate the value into a thumb |
|
983 * position. |
|
984 * |
|
985 * @method _setFactor |
|
986 * @protected |
|
987 */ |
|
988 _setFactor : function () { |
|
989 var range = this._railSize - this._thumbSize - |
|
990 this.get(MIN_GUTTER) - this.get(MAX_GUTTER); |
|
991 |
|
992 this._factor = this._railSize ? |
|
993 (this.get(MAX) - this.get(MIN)) / range : |
|
994 1; |
|
995 |
|
996 Y.log('_factor set to '+this._factor,'info','slider'); |
|
997 }, |
|
998 |
|
999 /** |
|
1000 * Convenience method for accessing the current value of the Slider. |
|
1001 * Equivalent to <code>slider.get("value")</code>. |
|
1002 * |
|
1003 * @method getValue |
|
1004 * @return {Number} the value |
|
1005 */ |
|
1006 getValue : function () { |
|
1007 return this.get(VALUE); |
|
1008 }, |
|
1009 |
|
1010 /** |
|
1011 * Convenience method for updating the current value of the Slider. |
|
1012 * Equivalent to <code>slider.set("value",val)</code>. |
|
1013 * |
|
1014 * @method setValue |
|
1015 * @param val {Number} the new value |
|
1016 */ |
|
1017 setValue : function (val) { |
|
1018 this.set(VALUE,val); |
|
1019 }, |
|
1020 |
|
1021 /** |
|
1022 * Validator applied to new values for the axis attribute. Only |
|
1023 * "x" and "y" are permitted. |
|
1024 * |
|
1025 * @method _validateNewAxis |
|
1026 * @param v {String} proposed value for the axis attribute |
|
1027 * @return Boolean |
|
1028 * @protected |
|
1029 */ |
|
1030 _validateNewAxis : function (v) { |
|
1031 return isString(v) && 'xXyY'.indexOf(v.charAt(0)) > -1; |
|
1032 }, |
|
1033 |
|
1034 /** |
|
1035 * Validator applied to the min attribute. |
|
1036 * |
|
1037 * @method _validateNewMin |
|
1038 * @param v {MIXED} proposed value for the min attribute |
|
1039 * @return Boolean |
|
1040 * @protected |
|
1041 */ |
|
1042 _validateNewMin : function (v) { |
|
1043 return isNumber(v); |
|
1044 }, |
|
1045 |
|
1046 /** |
|
1047 * Validator applied to the max attribute. |
|
1048 * |
|
1049 * @method _validateNewMax |
|
1050 * @param v {MIXED} proposed value for the max attribute |
|
1051 * @return Boolean |
|
1052 * @protected |
|
1053 */ |
|
1054 _validateNewMax : function (v) { |
|
1055 return isNumber(v); |
|
1056 }, |
|
1057 |
|
1058 /** |
|
1059 * Validator applied to the value attribute. |
|
1060 * |
|
1061 * @method _validateNewValue |
|
1062 * @param v {MIXED} proposed value for the value attribute |
|
1063 * @return Boolean |
|
1064 * @protected |
|
1065 */ |
|
1066 _validateNewValue : function (v) { |
|
1067 var min = this.get(MIN), |
|
1068 max = this.get(MAX); |
|
1069 |
|
1070 return isNumber(v) && |
|
1071 (min < max ? (v >= min && v <= max) : (v >= max && v <= min)); |
|
1072 }, |
|
1073 |
|
1074 /** |
|
1075 * Validator applied to the rail attribute. Rejects all values after the |
|
1076 * Slider has been rendered. |
|
1077 * |
|
1078 * @method _validateNewRail |
|
1079 * @param v {MIXED} proposed value for the rail attribute |
|
1080 * @return Boolean |
|
1081 * @protected |
|
1082 */ |
|
1083 _validateNewRail : function (v) { |
|
1084 return !this.get(RENDERED) || v; |
|
1085 }, |
|
1086 |
|
1087 /** |
|
1088 * Validator applied to the thumb attribute. Rejects all values after the |
|
1089 * Slider has been rendered. |
|
1090 * |
|
1091 * @method _validateNewThumb |
|
1092 * @param v {MIXED} proposed value for the thumb attribute |
|
1093 * @return Boolean |
|
1094 * @protected |
|
1095 */ |
|
1096 _validateNewThumb : function (v) { |
|
1097 return !this.get(RENDERED) || v; |
|
1098 }, |
|
1099 |
|
1100 /** |
|
1101 * Validator applied to the thumbImage attribute. Rejects all values after |
|
1102 * the Slider has been rendered. |
|
1103 * |
|
1104 * @method _validateNewThumbImage |
|
1105 * @param v {MIXED} proposed value for the thumbImage attribute |
|
1106 * @return Boolean |
|
1107 * @protected |
|
1108 */ |
|
1109 _validateNewThumbImage : function (v) { |
|
1110 return !this.get(RENDERED) || v; |
|
1111 }, |
|
1112 |
|
1113 /** |
|
1114 * Validator applied to the railSize attribute. Only strings of css size |
|
1115 * values (e.g. '200px') are allowed. |
|
1116 * |
|
1117 * @method _validateNewRailSize |
|
1118 * @param v {String} proposed value for the railSize attribute |
|
1119 * @return Boolean |
|
1120 * @protected |
|
1121 */ |
|
1122 _validateNewRailSize : function (v) { |
|
1123 return isString(v) && |
|
1124 (v === '0' || /^\d+(?:p[xtc]|%|e[mx]|in|[mc]m)$/.test(v)); |
|
1125 }, |
|
1126 |
|
1127 /** |
|
1128 * Setter applied to the input when updating the axis attribute. |
|
1129 * |
|
1130 * @method _setAxisFn |
|
1131 * @param v {String} proposed value for the axis attribute |
|
1132 * @return {String} lowercased first character of the input string |
|
1133 * @protected |
|
1134 */ |
|
1135 _setAxisFn : function (v) { |
|
1136 return v.charAt(0).toLowerCase(); |
|
1137 }, |
|
1138 |
|
1139 /** |
|
1140 * Setter applied to the input when updating the rail attribute. Input can |
|
1141 * be a Node, raw HTMLElement, or a selector string to locate it. |
|
1142 * |
|
1143 * @method _setRailFn |
|
1144 * @param v {Node|String|HTMLElement} The rail element Node or selector |
|
1145 * @return {Node} The Node if found. Otherwise null. |
|
1146 * @protected |
|
1147 */ |
|
1148 _setRailFn : function (v) { |
|
1149 return Y.get(v) || null; |
|
1150 }, |
|
1151 |
|
1152 /** |
|
1153 * Setter applied to the input when updating the thumb attribute. Input can |
|
1154 * be a Node, raw HTMLElement, or a selector string to locate it. |
|
1155 * |
|
1156 * @method _setThumbFn |
|
1157 * @param v {Node|String|HTMLElement} The thumb element Node or selector |
|
1158 * @return {Node} The Node if found. Otherwise null. |
|
1159 * @protected |
|
1160 */ |
|
1161 _setThumbFn : function (v) { |
|
1162 return Y.get(v) || null; |
|
1163 }, |
|
1164 |
|
1165 /** |
|
1166 * Setter applied to the input when updating the thumbImage attribute. |
|
1167 * Input can be a Node, raw HTMLElement, selector string to locate it, or |
|
1168 * the URL for an image resource. |
|
1169 * |
|
1170 * String input will be treated as a selector. If no element is found using |
|
1171 * the selector, an <code>img</code> Node will be created with the string |
|
1172 * used as the <code>src</code> attribute. |
|
1173 * |
|
1174 * @method _setThumbImageFn |
|
1175 * @param v {Node|String|HTMLElement} The thumbImage element Node, selector, |
|
1176 * or image URL |
|
1177 * @return {Node} The Node if found or created. Otherwise null. |
|
1178 * @protected |
|
1179 */ |
|
1180 _setThumbImageFn : function (v) { |
|
1181 return v ? Y.get(v) || |
|
1182 Y.Node.create('<img src="'+v+'" alt="Slider thumb">') : |
|
1183 null; |
|
1184 }, |
|
1185 |
|
1186 |
|
1187 /** |
|
1188 * Caches the current page position of the rail element and fires the |
|
1189 * slideStart event in response to the DD's drag:start. |
|
1190 * |
|
1191 * @method _onDDStartDrag |
|
1192 * @param e {Event} the DD instance's drag:start custom event |
|
1193 * @protected |
|
1194 */ |
|
1195 _onDDStartDrag : function (e) { |
|
1196 Y.log('slide start','info','slider'); |
|
1197 this._setRailOffsetXY(); |
|
1198 this.fire(SLIDE_START,{ ddEvent: e }); |
|
1199 }, |
|
1200 |
|
1201 /** |
|
1202 * Fires the thumbDrag event to queue Slider value update. |
|
1203 * |
|
1204 * @method _onDDDrag |
|
1205 * @param e {Event} the DD instance's drag:drag custom event |
|
1206 * @protected |
|
1207 */ |
|
1208 _onDDDrag : function (e) { |
|
1209 Y.log('thumb drag','info','slider'); |
|
1210 this.fire(THUMB_DRAG, { ddEvent: e }); |
|
1211 }, |
|
1212 |
|
1213 /** |
|
1214 * The default value update behavior in response to Slider thumb |
|
1215 * interaction. Calculates the value using stored offsets, the _factor |
|
1216 * multiplier and the min value. |
|
1217 * |
|
1218 * @method _defThumbDragFn |
|
1219 * @param e {Event} the internal thumbDrag event |
|
1220 * @protected |
|
1221 */ |
|
1222 _defThumbDragFn : function (e) { |
|
1223 var before = this.get(VALUE), |
|
1224 val = e.ddEvent[this._key.eventPageAxis] - this._offsetXY; |
|
1225 |
|
1226 Y.log('setting value from thumb drag: before('+before+') raw('+val+') factored('+this._convertOffsetToValue(val)+')', 'info','slider'); |
|
1227 |
|
1228 val = this._convertOffsetToValue(val); |
|
1229 |
|
1230 if (before !== val) { |
|
1231 this.set(VALUE, val, { ddEvent: e.ddEvent }); |
|
1232 } |
|
1233 }, |
|
1234 |
|
1235 /** |
|
1236 * Fires the slideEnd event. |
|
1237 * |
|
1238 * @method _onDDEndDrag |
|
1239 * @param e {Event} the DD instance's drag:end custom event |
|
1240 * @protected |
|
1241 */ |
|
1242 _onDDEndDrag : function (e) { |
|
1243 Y.log('slide end','info','slider'); |
|
1244 this.fire(SLIDE_END,{ ddEvent: e }); |
|
1245 }, |
|
1246 |
|
1247 |
|
1248 |
|
1249 |
|
1250 /** |
|
1251 * Calls _uiPositionThumb with the value of the custom event's |
|
1252 * "offset" property. |
|
1253 * |
|
1254 * @method _defPositionThumbFn |
|
1255 * @param e {Event} the positionThumb custom event |
|
1256 * @protected |
|
1257 */ |
|
1258 _defPositionThumbFn : function (e) { |
|
1259 Y.log('setting thumb offset ('+e.offset+') from value attribute update ('+e.value+')', 'info', 'slider'); |
|
1260 |
|
1261 this._uiPositionThumb(e.offset); |
|
1262 }, |
|
1263 |
|
1264 /** |
|
1265 * Places the thumb at a particular X or Y location based on the configured |
|
1266 * axis. |
|
1267 * |
|
1268 * @method _uiPositionThumb |
|
1269 * @param xy {Number} the desired left or top pixel position of the thumb |
|
1270 * in relation to the rail Node. |
|
1271 * @protected |
|
1272 */ |
|
1273 _uiPositionThumb : function (xy) { |
|
1274 var dd = this._dd, |
|
1275 thumb = dd.get('dragNode'), |
|
1276 hidden = thumb.ancestor(this._isDisplayNone); |
|
1277 |
|
1278 if (!hidden) { |
|
1279 dd._setStartPosition(dd.get('dragNode').getXY()); |
|
1280 |
|
1281 // stickX/stickY config on DD instance will negate off-axis move |
|
1282 dd._alignNode([xy,xy],true); |
|
1283 } |
|
1284 }, |
|
1285 |
|
1286 /** |
|
1287 * Helper function to search up the ancestor axis looking for a node with |
|
1288 * style display: none. This is passed as a function to node.ancestor(..) |
|
1289 * to test if a given node is in the displayed DOM and can get accurate |
|
1290 * positioning information. |
|
1291 * |
|
1292 * @method _isDisplayNone |
|
1293 * @param el {Node} ancestor node as the function walks up the parent axis |
|
1294 * @return {Boolean} true if the node is styled with display: none |
|
1295 * @protected |
|
1296 */ |
|
1297 _isDisplayNone : function (node) { |
|
1298 return node.getComputedStyle('display') === 'none'; |
|
1299 }, |
|
1300 |
|
1301 /** |
|
1302 * Fires the internal positionThumb event in response to a change in the |
|
1303 * value attribute. |
|
1304 * |
|
1305 * @method _afterValueChange |
|
1306 * @param e {Event} valueChange custom event |
|
1307 * @protected |
|
1308 */ |
|
1309 _afterValueChange : function (e) { |
|
1310 if (!e.ddEvent) { |
|
1311 var xy = this._convertValueToOffset(e.newVal); |
|
1312 |
|
1313 Y.log('firing positionThumb to position thumb', 'info', 'slider'); |
|
1314 |
|
1315 this.fire(POSITION_THUMB,{ value: e.newVal, offset: xy }); |
|
1316 } |
|
1317 }, |
|
1318 |
|
1319 /** |
|
1320 * Converts a value to a pixel offset for the thumb position on the rail. |
|
1321 * |
|
1322 * @method _convertValueToOffset |
|
1323 * @param v {Number} value between the Slider's min and max |
|
1324 * @protected |
|
1325 */ |
|
1326 _convertValueToOffset : function (v) { |
|
1327 return round((v - this.get(MIN)) / this._factor) + this._offsetXY; |
|
1328 }, |
|
1329 |
|
1330 /** |
|
1331 * Converts a pixel offset of the thumb on the rail to a value. |
|
1332 * |
|
1333 * @method _convertOffsetToValue |
|
1334 * @param v {Number} pixel offset of the thumb on the rail |
|
1335 * @protected |
|
1336 */ |
|
1337 _convertOffsetToValue : function (v) { |
|
1338 return round(this.get(MIN) + (v * this._factor)); |
|
1339 }, |
|
1340 |
|
1341 /** |
|
1342 * Replaces the thumb Node in response to a change in the thumb attribute. |
|
1343 * This only has effect after the Slider is rendered. |
|
1344 * |
|
1345 * @method _afterThumbChange |
|
1346 * @param e {Event} thumbChange custom event |
|
1347 * @protected |
|
1348 */ |
|
1349 _afterThumbChange : function (e) { |
|
1350 var thumb; |
|
1351 |
|
1352 if (this.get(RENDERED)) { |
|
1353 if (e.prevValue) { |
|
1354 e.prevValue.get('parentNode').removeChild(e.prevValue); |
|
1355 } |
|
1356 |
|
1357 this._initThumb(); |
|
1358 |
|
1359 thumb = this.get(THUMB); |
|
1360 this._dd.set('node',thumb); |
|
1361 this._dd.set('dragNode',thumb); |
|
1362 |
|
1363 this.syncUI(); |
|
1364 } |
|
1365 }, |
|
1366 |
|
1367 /** |
|
1368 * Sets or replaces the thumb's contained <code>img</code> Node with the |
|
1369 * new Node in response to a change in the thumbImage attribute. This only |
|
1370 * has effect after the Slider is rendered. |
|
1371 * |
|
1372 * @method _afterThumbImageChange |
|
1373 * @param e {Event} thumbImageChange custom event |
|
1374 * @protected |
|
1375 */ |
|
1376 _afterThumbImageChange : function (e) { |
|
1377 if (this.get(RENDERED)) { |
|
1378 if (e.prevValue) { |
|
1379 e.prevValue.get('parentNode').removeChild(e.prevValue); |
|
1380 } |
|
1381 |
|
1382 this._initThumbImage(); |
|
1383 |
|
1384 this.syncUI(); |
|
1385 } |
|
1386 }, |
|
1387 |
|
1388 /** |
|
1389 * Updates the Slider UI in response to change in the min attribute. |
|
1390 * |
|
1391 * @method _afterMinChange |
|
1392 * @param e {Event} minChange custom event |
|
1393 * @protected |
|
1394 */ |
|
1395 _afterMinChange : function (e) { |
|
1396 this._refresh(e); |
|
1397 }, |
|
1398 |
|
1399 /** |
|
1400 * Updates the Slider UI in response to change in the max attribute. |
|
1401 * |
|
1402 * @method _afterMaxChange |
|
1403 * @param e {Event} maxChange custom event |
|
1404 * @protected |
|
1405 */ |
|
1406 _afterMaxChange : function (e) { |
|
1407 this._refresh(e); |
|
1408 }, |
|
1409 |
|
1410 /** |
|
1411 * Updates the Slider UI in response to change in the railSize attribute. |
|
1412 * |
|
1413 * @method _afterRailSizeChange |
|
1414 * @param e {Event} railSizeChange custom event |
|
1415 * @protected |
|
1416 */ |
|
1417 _afterRailSizeChange : function (e) { |
|
1418 this._refresh(e); |
|
1419 }, |
|
1420 |
|
1421 /** |
|
1422 * Locks or unlocks the DD instance in response to a change in the disabled |
|
1423 * attribute. |
|
1424 * |
|
1425 * @method _afterDisabledChange |
|
1426 * @param e {Event} disabledChange custom event |
|
1427 * @protected |
|
1428 */ |
|
1429 _afterDisabledChange : function (e) { |
|
1430 if (this._dd) { |
|
1431 this._dd.set('lock',e.newVal); |
|
1432 } |
|
1433 }, |
|
1434 |
|
1435 /** |
|
1436 * Common handler to call syncUI in response to change events that occurred |
|
1437 * after the Slider is rendered. |
|
1438 * |
|
1439 * @method _refresh |
|
1440 * @param e {Event} An attribute change event |
|
1441 * @protected |
|
1442 */ |
|
1443 _refresh : function (e) { |
|
1444 if (e.newVal !== e.prevVal && this.get(RENDERED)) { |
|
1445 this.syncUI(); |
|
1446 } |
|
1447 }, |
|
1448 |
|
1449 /** |
|
1450 * Used to determine if there is a current or pending request for the |
|
1451 * thumbImage resource. |
|
1452 * |
|
1453 * @method _isImageLoading |
|
1454 * @param img {Node} <code>img</code> Node |
|
1455 * @return Boolean |
|
1456 * @protected |
|
1457 */ |
|
1458 _isImageLoading : function (img) { |
|
1459 return img && !img.get(COMPLETE); |
|
1460 }, |
|
1461 |
|
1462 /** |
|
1463 * Used to determine if the image resource loaded successfully or there was |
|
1464 * an error. |
|
1465 * |
|
1466 * NOTES: |
|
1467 * <ul> |
|
1468 * <li>img load error fired xbrowser for image resources not yet resolved</li> |
|
1469 * <li>img.complete reports false in IE for images not yet loaded as well as images that failed to load</li> |
|
1470 * <li>img.complete true && img.naturalWidth == 0 in FF and Safari indicate image failed to load</li> |
|
1471 * <li>img.complete && img.width == 0 in Opera indicates image failed to load</li> |
|
1472 * </ul> |
|
1473 * |
|
1474 * @method _isImageLoaded |
|
1475 * @param img {Node} <code>img</code> Node |
|
1476 * @return Boolean |
|
1477 * @protected |
|
1478 */ |
|
1479 _isImageLoaded : function (img) { |
|
1480 if (img) { |
|
1481 var w = img.get('naturalWidth'); |
|
1482 return img.get(COMPLETE) && (!isNumber(w) ? img.get(WIDTH) : w); |
|
1483 } |
|
1484 |
|
1485 return true; |
|
1486 } |
|
1487 |
|
1488 }); |
|
1489 |
|
1490 Y.Slider = Slider; |
|
1491 |
|
1492 |
|
1493 }, '3.0.0' ,{requires:['widget','dd-constrain']}); |