/** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ var App = require('app'); App.BaseUnitConvertMixin = Em.Mixin.create({ /** * Which type of units should be used e.g. size, time. * Conversion dimensions will be used according this type. * For more info regarding dimension table @see convertMapTable property. * * This property can be used in mixed object to specify which dimension table map will * be used for conversion. If type is not specified we try to find correct table by input unit type. * * Note: You should specify dimension for `percentage` conversion with specified property unit type. * * @property currentDimensionType * @type {String} */ currentDimensionType: null, units: ['b', 'kb', 'mb', 'gb', 'tb', 'pb'], /** * Labels related to units. Specify desired display names here that not much actual unit name. */ unitLabelMap: { percent: '%', int: '', float: '' }, convertMapTable: Em.Object.create({ size: { b: 1024, kb: 1024, mb: 1024, gb: 1024, tb: 1024, pb: 1024 }, time: { milliseconds: 1, seconds: 1000, minutes: 60, hours: 60, days: 24 }, /** * Percent dimension type should be specified directly through `currentDimensionType` property. * For example: * 'percent.percent_int' if widget `unit-name` is "percent" and config property `type` is "int" * 'percent.percent_float' if widget `unit-name` is "percent" and config property `type` is "float" */ percent: { percent_int: { int: 1, percent: 1 }, percent_float: { float: 1, percent: 0.01 } } }), /** * Convert value between different unit types. Conversion works for following directions: * single unit -> single unit e.g. from "s" -> "s". * single unit -> multi unit e.g. from "ms" -> "days,hours" * multi unit -> single unit e.g. from "days,hours" -> "ms" * For single -> single unit conversion returned value will be as is or an array of objects if * isObjectOutput flag passed. * If any passed unit is multi dimensions like "days,hours" array of objects will return always. * For example: * * convertValue(1024, 'kb', 'kb') // returns 1024 * convertValue(1024, 'kb', 'kb', true) // returns [{ type: 'kb', value: 1024 }] * convertValue(60000, 'ms', 'hours,minutes') // returns [{type: 'hours', value: 0 }, {type: 'minutes', value: 1}] * * For more examples see unit tests. * * @method convertValue * @param {String|Number|Object[]} value - value to convert * @param {String|String[]} fromUnit - specified value unit type(s) e.g. "mb" * Form multi dimensional format pass units separated with "," or list of unit types * e.g. "gb,mb", ['gb', 'mb'] * @param {Boolean} [isObjectOutput=false] - 'returned' value should be object this option useful for widgets where its * value should be an Object * @param {String|String[]} toUnit - desired unit(s) to convert specified value. Same format as for `fromUnit` * @returns {Number|Object[]} returns single value or array of objects according to multi unit format */ convertValue: function(value, fromUnit, toUnit, isObjectOutput) { var self = this; var valueIsArray = Em.isArray(value); if (!valueIsArray) { value = +value; } // if desired unit not passed or fromUnit and toUnit are the same // just return the value if ((fromUnit == toUnit || !toUnit) && !isObjectOutput) { return value; } if (isNaN(value) && !valueIsArray) { return null; } // convert passed toUnit string to array of units by ',' // for example "mb,kb" to ['mb','kb'] if (!Em.isArray(toUnit)) { toUnit = toUnit.split(','); } // process multi unit format if (toUnit.length > 1 || isObjectOutput) { // returned array has following structure // .value - value according to unit // .type - unit name return toUnit.map(function(unit) { var convertedValue = Math.floor(self._convertToSingleValue(value, fromUnit, unit)); value -= self._convertToSingleValue(convertedValue, unit, fromUnit); return { value: convertedValue, type: unit }; }); } // for single unit format just return single value else { if (!valueIsArray) { return this._convertToSingleValue(value, fromUnit, toUnit[0]); } else { return value.map(function(item) { return self._convertToSingleValue(item.value, item.type, toUnit[0]); }).reduce(Em.sum, 0); } } }, /** * Get dimension table map. `currentDimensionType` will be used if specified or * detected by property unit type. * * @method _converterGetUnitTable * @private * @param {String|String[]} unit - unit type * @return {Object} */ _converterGetUnitTable: function(unit) { var unitType; if (this.get('currentDimensionType')) { unitType = this.get('currentDimensionType'); } else { unitType = Em.keys(this.get('convertMapTable')).filter(function(item) { return Em.keys(this.get('convertMapTable.' + item)).contains(Em.isArray(unit) ? unit[0].toLowerCase() : unit.toLowerCase()); }, this)[0]; } return this.get('convertMapTable.' + unitType); }, /** * @method _convertToSingleValue * @private * @param {Number} value - value to convert * @param {String} fromUnit - unit type for current value * @param {String} toUnit - desired unit type * @return {Number} */ _convertToSingleValue: function(value, fromUnit, toUnit) { var convertTable = this._converterGetUnitTable(fromUnit); var units = Em.keys(convertTable); var fromUnitIndex = units.indexOf(fromUnit.toLowerCase()); var toUnitIndex = units.indexOf(toUnit.toLowerCase()); var isInt = function(val) { return parseInt(val) === val; }; Em.assert("Invalid value unit type " + fromUnit, fromUnitIndex > -1); Em.assert("Invalid desired unit type " + toUnit, toUnitIndex > -1); if (fromUnitIndex == toUnitIndex) { return value; } var range = [fromUnitIndex + 1, toUnitIndex + 1]; var processedUnits = Array.prototype.slice.apply(units, range[0] < range[1] ? range : range.slice().reverse()); var factor = processedUnits.map(function(unit) { return Em.get(convertTable, unit); }, this).reduce(function(p,c) { return p*c; }); if (range[0] < range[1]) { value /= factor; } else { value *= factor; } return isInt(value) ? value : parseFloat(value.toFixed(3)); } });