1 // ==========================================================================
  2 // Project:   SproutCore - JavaScript Application Framework
  3 // Copyright: ©2006-2011 Strobe Inc. and contributors.
  4 //            Portions ©2008-2011 Apple Inc. All rights reserved.
  5 // License:   Licened under MIT license (see license.js)
  6 // ==========================================================================
  7 
  8 /**
  9   Renders and updates the DOM representation of a slider.
 10 
 11   Parameters
 12   -------------------------
 13   Requires the following parameters:
 14 
 15    - `value` -- a value from 0 to 1.
 16    - `frame` -- containing the frame in which the slider is being drawn.
 17 */
 18 
 19 SC.BaseTheme.sliderRenderDelegate = SC.RenderDelegate.create({
 20 
 21   className: 'slider',
 22 
 23   render: function(dataSource, context) {
 24     this.addSizeClassName(dataSource, context);
 25 
 26     var valueMax = dataSource.get('maximum'),
 27         valueMin = dataSource.get('minimum'),
 28         valueNow = dataSource.get('ariaValue');
 29 
 30     // Add accessibility values.
 31     context.setAttr('aria-valuemax', valueMax);
 32     context.setAttr('aria-valuemin', valueMin);
 33     context.setAttr('aria-valuenow', valueNow);
 34     if(valueMin !== 0 || valueMax !== 100) context.setAttr('aria-valuetext', valueNow);
 35     context.setAttr('aria-orientation', 'horizontal');
 36 
 37     // Begin the track element.
 38     context = context.begin('span').addClass('track');
 39 
 40     // Draw the track's visual elements ("beginning", "middle" and "end").
 41     this.includeSlices(dataSource, context, SC.THREE_SLICE);
 42 
 43     // If desired, draw the step choinks.
 44     if (dataSource.get('markSteps')) {
 45       var stepPositions = dataSource.get('stepPositions');
 46       if (stepPositions) {
 47         var i, len = stepPositions.length;
 48         for (i = 0; i < len; i++) {
 49           context.begin()
 50             .setStyle('left', '%@%'.fmt(stepPositions[i] * 100))
 51             .addClass(['sc-slider-step-mark', 'sc-slider-step-mark-%@'.fmt(i)])
 52             .setClass({
 53               'sc-slider-step-mark-first': i === 0,
 54               'sc-slider-step-mark-last': i === len - 1
 55             }).end();
 56         }
 57       }
 58     }
 59 
 60     // Draw the handle.
 61     context.begin('img')
 62       .setAttr('src', SC.BLANK_IMAGE_URL)
 63       .addClass('sc-handle')
 64       .setStyle('left', '%@%'.fmt(dataSource.get('value')))
 65       .end();
 66 
 67     // End the track element.
 68     context = context.end();
 69 
 70     dataSource.get('renderState')._cachedHandle = null;
 71   },
 72 
 73   update: function(dataSource, jquery) {
 74     this.updateSizeClassName(dataSource, jquery);
 75 
 76     var valueMax = dataSource.get('maximum'),
 77         valueMin = dataSource.get('minimum'),
 78         valueNow = dataSource.get('ariaValue'),
 79         handle = dataSource.get('renderState')._cachedHandle;
 80 
 81     // Snag the handle if we haven't cached it yet.
 82     if (!handle) {
 83       handle = dataSource.get('renderState')._cachedHandle = jquery.find('.sc-handle');
 84     }
 85 
 86     // Update accessibility values.
 87     jquery.attr('aria-valuemax', valueMax);
 88     jquery.attr('aria-valuemin', valueMin);
 89     jquery.attr('aria-valuenow', valueNow);
 90     if(valueMin !== 0 || valueMax !== 100) jquery.attr('aria-valuetext', valueNow);
 91     jquery.attr('aria-orientation', 'horizontal');
 92 
 93     // If the minimum, maximum, step, or markSteps have changed, repoint the choinks.
 94     if (dataSource.didChangeFor('sliderRenderDelegateMinimumMaximumStepMarkSteps', 'minimum', 'maximum', 'step', 'markSteps')) {
 95       var marks = jquery.find('.sc-slider-step-mark'),
 96         markSteps = dataSource.get('markSteps'),
 97         stepPositions;
 98       // Ten years ago we had no marks, no steps and
 99       if (!markSteps || !(stepPositions = dataSource.get('stepPositions'))) {
100         marks.remove();
101       }
102       // Otherwise, reposition them, adding new ones as needed.
103       else {
104         var choinkVal,
105           i, len = stepPositions.length,
106           firstLastClass,
107           choinkTemplate = '<div style="left:%@%" class="sc-slider-step-mark sc-slider-step-mark-%@ %@"></div>',
108           choinkMarkup;
109 
110         for (i = 0; i < len; i++) {
111           if (marks[i]) {
112             marks.eq(i).css('left', '%@%'.fmt(stepPositions[i] * 100)).setClass({
113               'sc-slider-step-mark-first': i === 0,
114               'sc-slider-step-mark-last': i === len - 1
115             });
116           }
117           else {
118             if (i === 0) firstLastClass = 'sc-slider-step-mark-first';
119             else if (i === len - 1) firstLastClass = 'sc-slider-step-mark-last';
120             else firstLastClass = '';
121             choinkMarkup = choinkTemplate.fmt(stepPositions[i] * 100, i, firstLastClass);
122             handle.before(choinkMarkup);
123           }
124         }
125         // Remove any remaining.
126         marks.slice(i).remove();
127       }
128     }
129 
130     // Update the value, if needed.
131     if (dataSource.didChangeFor('sliderRenderDelegateValue', 'value')) {
132       var value = dataSource.get('value');
133       handle.css('left', value + "%");
134     }
135   }
136 });
137