blob: 7ea1b3220624a373a59a99675700c10a16d2003f [file] [log] [blame]
/**
* 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');
const {isValidFloat} = require('utils/validator');
/**
* Thresholds manager for sliders with single value
* Usage:
* <pre>
* SingleHandler.create({
* thresholdMin: 12,
* minValue: 10,
* maxValue: 100
* });
* </pre>
*
* @class SingleHandler
*/
const SingleHandler = Em.Object.extend({
/**
* @type {number}
*/
thresholdMin: null,
/**
* @type {number}
* @default 0
*/
minValue: 0,
/**
* @type {number}
*/
maxValue: null,
/**
* Is <code>thresholdMin</code> invalid
* true - invalid
* false - valid
*
* @type {boolean}
*/
thresholdMinError: Em.computed.bool('thresholdMinErrorMessage'),
/**
* Alias for <code>thresholdMinError</code>
*
* @type {boolean}
*/
hasErrors: Em.computed.alias('thresholdMinError'),
/**
* Error message for <code>thresholdMin</code>
* <ul>
* <li>Value is not a number</li>
* <li>Value is out of range <code>(minValue - maxValue)</code></li>
* </ul>
* Empty message means that <code>thresholdMin</code> has valid value
*
* @type {string}
*/
thresholdMinErrorMessage: function () {
var thresholdMin = this.get('thresholdMin');
var maxValue = this.get('maxValue');
var minValue = this.get('minValue');
if (!isValidFloat(thresholdMin) || thresholdMin > maxValue || thresholdMin < minValue) {
return Em.I18n.t('dashboard.widgets.error.invalid').format(minValue, maxValue);
}
return '';
}.property('thresholdMin', 'maxValue'),
/**
* Formatted threshold value
*
* @type {number[]}
*/
preparedThresholds: function () {
return [parseFloat(this.get('thresholdMin'))];
}.property('thresholdMin'),
/**
* Force set new values for threshold
*
* @param {number[]} newValues
*/
updateThresholds(newValues) {
this.set('thresholdMin', newValues[0]);
}
});
/**
* Thresholds manager for sliders with double values
* Usage:
* <pre>
* SingleHandler.create({
* thresholdMin: 12,
* thresholdMax: 40,
* minValue: 10,
* maxValue: 100
* });
* </pre>
*
* @class DoubleHandlers
*/
const DoubleHandlers = SingleHandler.extend({
/**
* @type {number}
*/
thresholdMax: null,
/**
* Is <code>thresholdMax</code> invalid
* true - invalid
* false - valid
*
* @type {boolean}
*/
thresholdMaxError: Em.computed.bool('thresholdMaxErrorMessage'),
/**
* Is some threshold invalid
* true - some one is invalid
* false - thresholds are valid
*
* @type {boolean}
*/
hasErrors: Em.computed.or('thresholdMinError', 'thresholdMaxError'),
/**
* Error message for <code>thresholdMin</code>
* <ul>
* <li>Value is not a number</li>
* <li>Value is out of range <code>(minValue - maxValue)</code></li>
* <li><code>thresholdMin</code>-value greater than <code>thresholdMax</code>-value</li>
* </ul>
* Empty message means that <code>thresholdMin</code> has valid value
*
* @type {string}
*/
thresholdMinErrorMessage: function () {
var thresholdMin = this.get('thresholdMin');
var thresholdMax = this.get('thresholdMax');
var maxValue = this.get('maxValue');
var minValue = this.get('minValue');
if (!isValidFloat(thresholdMin) || thresholdMin > maxValue || thresholdMin < minValue) {
return Em.I18n.t('dashboard.widgets.error.invalid').format(minValue, maxValue);
}
if (this.get('thresholdMaxError') === false && thresholdMax <= thresholdMin) {
return Em.I18n.t('dashboard.widgets.error.smaller');
}
return '';
}.property('thresholdMin', 'thresholdMax'),
/**
* Error message for <code>thresholdMax</code>
* <ul>
* <li>Value is not a number</li>
* <li>Value is out of range <code>(minValue - maxValue)</code></li>
* </ul>
* Empty message means that <code>thresholdMax</code> has valid value
*
* @type {string}
*/
thresholdMaxErrorMessage: function () {
var thresholdMax = this.get('thresholdMax');
var maxValue = this.get('maxValue');
var minValue = this.get('minValue');
if (!isValidFloat(thresholdMax) || thresholdMax > maxValue || thresholdMax < minValue) {
return Em.I18n.t('dashboard.widgets.error.invalid').format(minValue, maxValue);
}
return '';
}.property('thresholdMax'),
/**
* Threshold values ready to save
*
* @type {number[]}
*/
preparedThresholds: function () {
return [parseFloat(this.get('thresholdMin')), parseFloat(this.get('thresholdMax'))];
}.property('thresholdMin', 'thresholdMax'),
/**
* Force set new values for threshold
*
* @param {number[]} newValues
*/
updateThresholds(newValues) {
this.set('thresholdMin', newValues[0]);
this.set('thresholdMax', newValues[1]);
}
});
/**
* Common body-view for popup with sliders
*
* @class EditDashboardWidgetPopupBody
*/
const EditDashboardWidgetPopupBody = Em.View.extend({
templateName: require('templates/main/dashboard/edit_widget_popup')
});
/**
* Popup with slider to edit dashboard widget
* Usage:
* <pre>
* App.EditDashboardWidgetPopup.show({
* widgetView: this,
* sliderHandlersManager: App.EditDashboardWidgetPopup.DoubleHandlers.create({
* maxValue: 100,
* thresholdMin: this.get('thresholdMin'),
* thresholdMax: this.get('thresholdMax')
* })
* });
* </pre>
*
* <code>widgetView</code> should be set to view with widget.
* Usually you will use <code>App.EditDashboardWidgetPopup</code> inside of some <code>App.DashboardWidgetView</code> instance,
* so <code>widgetView</code> may be set to <code>this</code>
* <code>sliderHandlersManager</code> should be set to some of the <code>App.EditDashboardWidgetPopup.SingleHandler</code>
* or <code>App.EditDashboardWidgetPopup.DoubleHandler</code>
*
* You can't use <code>App.EditDashboardWidgetPopup</code> without setting this two properties!
*
* @class App.EditDashboardWidgetPopup
*/
App.EditDashboardWidgetPopup = App.ModalPopup.extend({
header: Em.I18n.t('dashboard.widgets.popupHeader'),
classNames: ['modal-edit-widget'],
modalDialogClasses: ['modal-lg'],
primary: Em.I18n.t('common.apply'),
disablePrimary: Em.computed.alias('sliderHandlersManager.hasErrors'),
/**
* Can't be null or undefined
*
* @type {SingleHandler|DoubleHandlers}
*/
sliderHandlersManager: null,
/**
* Widget view
* Can't be not a view. Used to save Thresholds. Normally it's an instance of <code>App.DashboardWidgetView</code>
*
* @type {Em.View}
*/
widgetView: null,
/**
* Determines if slider is enabled for slide
* true - don't enabled
* false - enabled
*
* Determines if slider handlers should be updated with Threshold values
* true - don't update
* false - update
*
* Used as option <code>disabled</code> for $.ui.slider
*
* @type {boolean}
*/
sliderDisabled: false,
/**
* Slider "ticks"
* Used as option <code>values</code> for $.ui.slider
*
* @type {number[]}
*/
sliderHandlers: function () {
return this.get('sliderHandlersManager.preparedThresholds');
}.property('sliderHandlersManager.preparedThresholds.[]'),
/**
* Colors used for slider ranges
* @type {string[]}
*/
sliderColors: [App.healthStatusRed, App.healthStatusOrange, App.healthStatusGreen],
/**
* Maximum value for slider
* Used as option <code>max</code> for $.ui.slider
*
* @type {number}
*/
sliderMaxValue: Em.computed.alias('sliderHandlersManager.maxValue'),
/**
* Minimum value for slider
* Used as option <code>min</code> for $.ui.slider
*
* @type {number}
*/
sliderMinValue: 0,
/**
* Check how many handlers has slider
* true - 2 handlers
* false - 1 handler
*
* Used as option <code>range</code> for $.ui.slider
*
* @type {boolean}
*/
sliderIsRange: true,
bodyClass: EditDashboardWidgetPopupBody,
init() {
Em.assert('`widgetView` should be valid view', this.get('widgetView.isView'));
Em.assert('`sliderHandlersManager` should be set', !!this.get('sliderHandlersManager'));
return this._super(...arguments);
},
/**
* Save new threshold value on popup-close (means Primary click)
* Use <code>widgetView</code> to get <code>widgetsView</code> and save new values
* Current widget is updated too (without redrawing)
*/
saveThreshold () {
let preparedThresholds = this.get('sliderHandlersManager.preparedThresholds');
this.get('widgetView').saveWidgetThresholds(preparedThresholds);
},
/**
* Update slider values when new threshold values are provided
* Don't do anything if some value is invalid or slider is disabled
*
* @private
*/
_updateSliderValues: function() {
var sliderHandlersManager = this.get('sliderHandlersManager');
if (!sliderHandlersManager.get('hasErrors') && !this.get('sliderDisabled')) {
$('#slider-range').slider('values', sliderHandlersManager.get('preparedThresholds'));
}
}.observes('sliderDisabled', 'sliderHandlersManager.preparedThresholds.[]', 'sliderHandlersManager.hasErrors'),
onPrimary () {
let sliderHandlersManager = this.get('sliderHandlersManager');
if (!sliderHandlersManager.get('hasErrors')) {
this.saveThreshold();
this.hide();
}
},
/**
* Create slider in the popup when it's opened
*/
createSlider() {
var self = this;
let sliderHandlersManager = this.get('sliderHandlersManager');
var handlers = this.get('sliderHandlers');
$('#slider-range').slider({
range: this.get('sliderIsRange'),
min: this.get('sliderMinValue'),
max: this.get('sliderMaxValue'),
disabled: this.get('sliderDisabled'),
values: handlers,
create: function () {
self.updateSliderColors(handlers);
},
slide: function (event, ui) {
self.updateSliderColors(ui.values);
sliderHandlersManager.updateThresholds(ui.values);
},
change: function (event, ui) {
self.updateSliderColors(ui.values);
}
});
},
didInsertElement: function () {
this._super();
this.createSlider();
},
/**
* Update colors on slider using <code>sliderColors</code> theme when user interacts with it
*
* @param {number[]} handlers
*/
updateSliderColors(handlers) {
let gradient = this._getGradientStr(handlers);
$('#slider-range')
.css('background-image', '-webkit-' + gradient)
.css('background-image', '-ms-' + gradient)
.css('background-image', '-moz-' + gradient)
.find('.ui-widget-header').css({
'background-color': App.healthStatusOrange,
'background-image': 'none'
});
},
/**
* @param {number[]} handlers
* @returns {string}
* @private
*/
_getGradientStr(handlers) {
let maxValue = this.get('sliderMaxValue');
let colors = this.get('sliderColors');
let gradient = colors[0] + ', ' + handlers.map((handler, i) => {
return `${colors[i]} ${handlers[i] * 100 / maxValue}%, ${colors[i + 1]} ${handlers[i] * 100 / maxValue}%,`;
}).join('') + colors[colors.length - 1];
return `linear-gradient(left,${gradient})`;
}
});
App.EditDashboardWidgetPopup.reopenClass({
SingleHandler,
DoubleHandlers,
EditDashboardWidgetPopupBody
});