base_unit_convert_mixin.js 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211
  1. /**
  2. * Licensed to the Apache Software Foundation (ASF) under one
  3. * or more contributor license agreements. See the NOTICE file
  4. * distributed with this work for additional information
  5. * regarding copyright ownership. The ASF licenses this file
  6. * to you under the Apache License, Version 2.0 (the
  7. * "License"); you may not use this file except in compliance
  8. * with the License. You may obtain a copy of the License at
  9. *
  10. * http://www.apache.org/licenses/LICENSE-2.0
  11. *
  12. * Unless required by applicable law or agreed to in writing, software
  13. * distributed under the License is distributed on an "AS IS" BASIS,
  14. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  15. * See the License for the specific language governing permissions and
  16. * limitations under the License.
  17. */
  18. var App = require('app');
  19. App.BaseUnitConvertMixin = Em.Mixin.create({
  20. /**
  21. * Which type of units should be used e.g. size, time.
  22. * Conversion dimensions will be used according this type.
  23. * For more info regarding dimension table @see convertMapTable property.
  24. *
  25. * This property can be used in mixed object to specify which dimension table map will
  26. * be used for conversion. If type is not specified we try to find correct table by input unit type.
  27. *
  28. * Note: You should specify dimension for `percentage` conversion with specified property unit type.
  29. *
  30. * @property currentDimensionType
  31. * @type {String}
  32. */
  33. currentDimensionType: null,
  34. units: ['b', 'kb', 'mb', 'gb', 'tb', 'pb'],
  35. /**
  36. * Labels related to units. Specify desired display names here that not much actual unit name.
  37. */
  38. unitLabelMap: {
  39. percent: '%',
  40. int: '',
  41. float: ''
  42. },
  43. convertMapTable: Em.Object.create({
  44. size: {
  45. b: 1024,
  46. kb: 1024,
  47. mb: 1024,
  48. gb: 1024,
  49. tb: 1024,
  50. pb: 1024
  51. },
  52. time: {
  53. milliseconds: 1,
  54. seconds: 1000,
  55. minutes: 60,
  56. hours: 60,
  57. days: 24
  58. },
  59. /**
  60. * Percent dimension type should be specified directly through `currentDimensionType` property.
  61. * For example:
  62. * 'percent.percent_int' if widget `unit-name` is "percent" and config property `type` is "int"
  63. * 'percent.percent_float' if widget `unit-name` is "percent" and config property `type` is "float"
  64. */
  65. percent: {
  66. percent_int: {
  67. int: 1,
  68. percent: 1
  69. },
  70. percent_float: {
  71. float: 1,
  72. percent: 0.01
  73. }
  74. }
  75. }),
  76. /**
  77. * Convert value between different unit types. Conversion works for following directions:
  78. * single unit -> single unit e.g. from "s" -> "s".
  79. * single unit -> multi unit e.g. from "ms" -> "days,hours"
  80. * multi unit -> single unit e.g. from "days,hours" -> "ms"
  81. * For single -> single unit conversion returned value will be as is or an array of objects if
  82. * isObjectOutput flag passed.
  83. * If any passed unit is multi dimensions like "days,hours" array of objects will return always.
  84. * For example:
  85. * <code>
  86. * convertValue(1024, 'kb', 'kb') // returns 1024
  87. * convertValue(1024, 'kb', 'kb', true) // returns [{ type: 'kb', value: 1024 }]
  88. * convertValue(60000, 'ms', 'hours,minutes') // returns [{type: 'hours', value: 0 }, {type: 'minutes', value: 1}]
  89. * </code>
  90. * For more examples see unit tests.
  91. *
  92. * @method convertValue
  93. * @param {String|Number|Object[]} value - value to convert
  94. * @param {String|String[]} fromUnit - specified value unit type(s) e.g. "mb"
  95. * Form multi dimensional format pass units separated with "," or list of unit types
  96. * e.g. "gb,mb", ['gb', 'mb']
  97. * @param {Boolean} [isObjectOutput=false] - 'returned' value should be object this option useful for widgets where its
  98. * value should be an Object
  99. * @param {String|String[]} toUnit - desired unit(s) to convert specified value. Same format as for `fromUnit`
  100. * @returns {Number|Object[]} returns single value or array of objects according to multi unit format
  101. */
  102. convertValue: function(value, fromUnit, toUnit, isObjectOutput) {
  103. var self = this;
  104. var valueIsArray = Em.isArray(value);
  105. if (!valueIsArray) {
  106. value = +value;
  107. }
  108. // if desired unit not passed or fromUnit and toUnit are the same
  109. // just return the value
  110. if ((fromUnit == toUnit || !toUnit) && !isObjectOutput) {
  111. return value;
  112. }
  113. if (isNaN(value) && !valueIsArray) {
  114. return null;
  115. }
  116. // convert passed toUnit string to array of units by ','
  117. // for example "mb,kb" to ['mb','kb']
  118. if (!Em.isArray(toUnit)) {
  119. toUnit = toUnit.split(',');
  120. }
  121. // process multi unit format
  122. if (toUnit.length > 1 || isObjectOutput) {
  123. // returned array has following structure
  124. // .value - value according to unit
  125. // .type - unit name
  126. return toUnit.map(function(unit) {
  127. var convertedValue = Math.floor(self._convertToSingleValue(value, fromUnit, unit));
  128. value -= self._convertToSingleValue(convertedValue, unit, fromUnit);
  129. return {
  130. value: convertedValue,
  131. type: unit
  132. };
  133. });
  134. }
  135. // for single unit format just return single value
  136. else {
  137. if (!valueIsArray) {
  138. return this._convertToSingleValue(value, fromUnit, toUnit[0]);
  139. }
  140. else {
  141. return value.map(function(item) {
  142. return self._convertToSingleValue(item.value, item.type, toUnit[0]);
  143. }).reduce(Em.sum);
  144. }
  145. }
  146. },
  147. /**
  148. * Get dimension table map. `currentDimensionType` will be used if specified or
  149. * detected by property unit type.
  150. *
  151. * @method _converterGetUnitTable
  152. * @private
  153. * @param {String|String[]} unit - unit type
  154. * @return {Object}
  155. */
  156. _converterGetUnitTable: function(unit) {
  157. var unitType;
  158. if (this.get('currentDimensionType')) {
  159. unitType = this.get('currentDimensionType');
  160. }
  161. else {
  162. unitType = Em.keys(this.get('convertMapTable')).filter(function(item) {
  163. return Em.keys(this.get('convertMapTable.' + item)).contains(Em.isArray(unit) ? unit[0].toLowerCase() : unit.toLowerCase());
  164. }, this)[0];
  165. }
  166. return this.get('convertMapTable.' + unitType);
  167. },
  168. /**
  169. * @method _convertToSingleValue
  170. * @private
  171. * @param {Number} value - value to convert
  172. * @param {String} fromUnit - unit type for current value
  173. * @param {String} toUnit - desired unit type
  174. * @return {Number}
  175. */
  176. _convertToSingleValue: function(value, fromUnit, toUnit) {
  177. var convertTable = this._converterGetUnitTable(fromUnit);
  178. var units = Em.keys(convertTable);
  179. var fromUnitIndex = units.indexOf(fromUnit.toLowerCase());
  180. var toUnitIndex = units.indexOf(toUnit.toLowerCase());
  181. var isInt = function(val) {
  182. return parseInt(val) === val;
  183. };
  184. Em.assert("Invalid value unit type " + fromUnit, fromUnitIndex > -1);
  185. Em.assert("Invalid desired unit type " + toUnit, toUnitIndex > -1);
  186. if (fromUnitIndex == toUnitIndex) {
  187. return value;
  188. }
  189. var range = [fromUnitIndex + 1, toUnitIndex + 1];
  190. var processedUnits = Array.prototype.slice.apply(units, range[0] < range[1] ? range : range.slice().reverse());
  191. var factor = processedUnits.map(function(unit) {
  192. return Em.get(convertTable, unit);
  193. }, this).reduce(function(p,c) { return p*c; });
  194. if (range[0] < range[1]) {
  195. value /= factor;
  196. }
  197. else {
  198. value *= factor;
  199. }
  200. return isInt(value) ? value : parseFloat(value.toFixed(3));
  201. }
  202. });