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:   Licensed under MIT license (see license.js)
  6 // ==========================================================================
  7 sc_require("theme");
  8 SC.LegacyTheme.PROGRESS_ANIMATED_BACKGROUND_MATRIX = [];
  9 SC.LegacyTheme.PROGRESS_OFFSET_RANGE = 24;
 10 
 11 /**
 12   @class
 13   Renders and updates DOM representations of progress bars.
 14 
 15   Parameters
 16   --------------------------
 17   Expects these properties on the data source:
 18 
 19   - isIndeterminate
 20   - isRunning
 21   - isEnabled
 22   - value (from 0 to 1)
 23 
 24   There are a few other properties supported for backwards-compatibility
 25   with certain ProgressView implementations; these ProgressViews should
 26   be updated to match the new API. These properties will trigger deprecation
 27   warnings.
 28 
 29   Theme Constants
 30   -------------------------------------
 31   Note that, unlike render delegate parameters, which are mostly standardized,
 32   the theme constants can vary by the theme and the theme's method of rendering
 33   the control.
 34 
 35   - PROGRESS_ANIMATED_BACKGROUND_MATRIX: Set to the matrix used for
 36     background image position for animation.
 37     [1st image y-location, offset, total number of images]
 38 
 39   - PROGRESS_OFFSET_RANGE: The value of the progress inner offset range.
 40     Should be the same as width of image. Default it to 24.
 41 
 42 */
 43 SC.LegacyTheme.progressRenderDelegate = SC.RenderDelegate.create({
 44   className: 'progress',
 45 
 46   render: function(dataSource, context) {
 47     this.addSizeClassName(dataSource, context);
 48 
 49     var theme    = dataSource.get('theme'),
 50         valueMax = dataSource.get('maximum'),
 51         valueMin = dataSource.get('minimum'),
 52         valueNow = dataSource.get('ariaValue');
 53 
 54     var inner, animatedBackground, value = dataSource.get('value') * 100,
 55         cssString, backPosition,
 56         isIndeterminate = dataSource.get('isIndeterminate'),
 57         isRunning = dataSource.get('isRunning'),
 58         isEnabled = dataSource.get('isEnabled'),
 59         offsetRange = theme.PROGRESS_OFFSET_RANGE,
 60         offset = (isIndeterminate && isRunning) ?
 61                 (Math.floor(Date.now()/75)%offsetRange-offsetRange) : 0;
 62 
 63     //addressing accessibility
 64     context.setAttr('aria-valuemax', valueMax);
 65     context.setAttr('aria-valuemin', valueMin);
 66     context.setAttr('aria-valuenow', valueNow);
 67     context.setAttr('aria-valuetext', valueNow);
 68 
 69     // offsetRange from dataSource only supported for backwards-compatibility
 70     if (dataSource.get('offsetRange')) {
 71       if (!this._hasGivenOffsetRangeDeprecationWarning) {
 72         console.warn(
 73           "The 'offsetRange' property for progressRenderDelegate is deprecated. " +
 74           "Please override the value on your theme, instead, by setting " +
 75           "its PROGRESS_OFFSET_RANGE property."
 76         );
 77       }
 78       this._hasGivenOffsetRangeDeprecationWarning = YES;
 79 
 80       offsetRange = dataSource.get('offsetRange');
 81     }
 82 
 83     var classNames = {
 84       'sc-indeterminate': isIndeterminate,
 85       'sc-empty': (value <= 0),
 86       'sc-complete': (value >= 100)
 87     };
 88 
 89     // compute value for setting the width of the inner progress
 90     if (!isEnabled) {
 91       value = "0%" ;
 92     } else if (isIndeterminate) {
 93       value = "120%";
 94     } else {
 95       value = value + "%";
 96     }
 97 
 98     var classString = this._createClassNameString(classNames);
 99     context.push('<div class="sc-inner ', classString, '" style="width: ',
100                   value, ';left: ', offset, 'px;">',
101                   '<div class="sc-inner-head">','</div>',
102                   '<div class="sc-inner-tail"></div></div>',
103                   '<div class="sc-outer-head"></div>',
104                   '<div class="sc-outer-tail"></div>');
105   },
106 
107   update: function(dataSource, $) {
108     this.updateSizeClassName(dataSource, $);
109 
110     var theme    = dataSource.get('theme'),
111         valueMax = dataSource.get('maximum'),
112         valueMin = dataSource.get('minimum'),
113         valueNow = dataSource.get('ariaValue');
114 
115     // make accessible
116     $.attr('aria-valuemax', valueMax);
117     $.attr('aria-valuemin', valueMin);
118     $.attr('aria-valuenow', valueNow);
119     $.attr('aria-valuetext', valueNow);
120 
121     var inner, value, cssString, backPosition,
122         animatedBackground = theme.PROGRESS_ANIMATED_BACKGROUND_MATRIX,
123         isIndeterminate = dataSource.get('isIndeterminate'),
124         isRunning = dataSource.get('isRunning'),
125         isEnabled = dataSource.get('isEnabled'),
126         offsetRange = dataSource.get('offsetRange'),
127         offset = (isIndeterminate && isRunning) ?
128                 (Math.floor(Date.now()/75)%offsetRange-offsetRange) : 0;
129 
130     // compute value for setting the width of the inner progress
131     if (!isEnabled) {
132       value = "0%" ;
133     } else if (isIndeterminate) {
134       value = "120%";
135     } else {
136       value = (dataSource.get('value') * 100) + "%";
137     }
138 
139     var classNames = {
140       'sc-indeterminate': isIndeterminate,
141       'sc-empty': (value <= 0),
142       'sc-complete': (value >= 100)
143     };
144 
145     $.setClass(classNames);
146     inner = $.find('.sc-inner');
147 
148     // animatedBackground from dataSource only supported for backwards-compatibility
149     if (dataSource.get('animatedBackgroundMatrix')) {
150       if (!this._hasGivenAnimatedBackgroundDeprecationWarning) {
151         console.warn(
152           "The 'animatedBackgroundMatrix' property for progressRenderDelegate " +
153           "is deprecated. Please override the value on your theme by setting " +
154           "its PROGRESS_ANIMATED_BACKGROUND_MATRIX property."
155         );
156       }
157 
158       this._hasGivenAnimatedBackgroundDeprecationWarning = YES;
159 
160       animatedBackground = dataSource.get('animatedBackgroundMatrix');
161     }
162 
163     if (!animatedBackground) {
164       animatedBackground = theme.PROGRESS_ANIMATED_BACKGROUND_MATRIX;
165     }
166 
167     cssString = "width: "+value+"; ";
168     cssString = cssString + "left: "+offset+"px; ";
169     if (animatedBackground.length === 3 ) {
170       inner.css('backgroundPosition', '0px -'+
171               (animatedBackground[0] +
172               animatedBackground[1]*this._currentBackground)+'px');
173       if(this._currentBackground===animatedBackground[2]-1
174          || this._currentBackground===0){
175         this._nextBackground *= -1;
176       }
177       this._currentBackground += this._nextBackground;
178 
179       cssString = cssString + "backgroundPosition: "+backPosition+"px; ";
180       //Instead of using css() set attr for faster perf.
181       inner.attr('style', cssString);
182     }else{
183       inner.attr('style', cssString);
184     }
185   },
186 
187 
188   _createClassNameString: function(classNames) {
189     var classNameArray = [], key;
190     for(key in classNames) {
191       if(!classNames.hasOwnProperty(key)) continue;
192       if(classNames[key]) classNameArray.push(key);
193     }
194     return classNameArray.join(" ");
195   }
196 });
197