'use strict'; /** * Licensed Materials - Property of IBM * IBM Cognos Products: BI Cloud (C) Copyright IBM Corp. 2014, 2017 * US Government Users Restricted Rights - Use, duplication or disclosure restricted by GSA ADP Schedule Contract with IBM Corp. */ define(['jquery', 'underscore', '../lib/@waca/core-client/js/core-client/ui/core/View', 'text!./templates/Slider.template', '../lib/@waca/core-client/js/core-client/i18n/Formatter', '../nls/StringResources', 'bootstrap-slider'], function ($, _, BaseView, Template, formatter, resources) { var View = BaseView.extend({ templateString: Template, leftTooltip: null, leftTooltipArrow: null, leftTooltipInput: null, rightTooltip: null, rightTooltipArrow: null, rightTooltipInput: null, tooltipPadding: 5, events: { 'change .tooltipHandle input': 'onInputChange', 'keyup .tooltipHandle input': 'onKeyUp', 'keydown .tooltipHandle input': 'onKeyDown', 'keypress .tooltipHandle input': 'onKeyPress', 'mousedown .tooltipHandle input': 'onMouseDownTooltip', 'focus .tooltipHandle input': 'onInputFocus', 'blur .tooltipHandle input': 'onInputBlur', 'keydown div.slider-handle.round': 'onHandleKeyDown' }, init: function init(options) { View.inherited('init', this, arguments); // Enabled by default. _.defaults(options, { sliderId: 'slider', min: 0, max: 10, precision: 20, value: 0, inverted: false, enabled: true, minimized: false, indeterminate: false, showMinMax: true, valueFormatter: this.valueFormatter, prettyValue: this.prettyValue, tooltipTemplate: '', style: 'simple' }); // If there is only one option, display the slider as disabled so it is clear to the user if (options.min === options.max) { options.enabled = false; } if (options.indeterminate) { // The slide track needs to be fully visible for indeterminate mode. options.value = options.max; options.enabled = false; options.minimized = true; } if (options.minimized) { options.showMinMax = false; } this.options = options; }, valueFormatter: function valueFormatter(value) { return value; }, _getKeyCode: function _getKeyCode(e) { return e.keyCode || e.which; }, prettyValue: function prettyValue(value) { return value; }, setFocus: function setFocus() { this.$('.slider-handle').first().focus(); }, render: function render() { var _this = this; var callResolve = true; var isRange = this.options.value instanceof Array; this.$el.empty(); var sHtml = this.dotTemplate({ min: this.options.min, max: this.options.max, value: this.options.value, showMinMax: this.options.showMinMax, isRange: isRange, minimized: this.options.minimized }); this.$el.attr('role', 'application'); this.$el.attr('aria-label', resources.get('sliderRegionLabel')); this.$el.html(sHtml); var sliderContainer = this.$el.find('.slider-container'); sliderContainer.addClass(this.options.style); var sliderElement = sliderContainer.find('.slider-control'); sliderElement.attr('id', this.options.sliderId); var slider = sliderElement.bootstrapSlider({ tooltip: 'hide', value: this.options.value, min: this.options.min, max: this.options.max, step: this.options.step, precision: this.options.precision }); this.slider = slider; // Remove the bootstrap-slider's tooltips. We are using our own. this.$el.find('.tooltip').remove(); var handles; var result = void 0; if (!this.options.minimized) { slider.on('slide', this.onSlide.bind(this)).on('slideStart', this.onSlideStart.bind(this)).on('slideStop', this.onSlideStop.bind(this)); var tooltips = this.$el.find('.tooltipHandle'); var tooltipInner = tooltips.find('.tooltip-inner'); tooltips.css({ 'position': 'absolute' }); tooltips.hide(); tooltips.find('.tooltipHandle .tooltip-arrow').css({ 'left': '0', 'right': '0', 'padding': '0', 'margin-left': 'auto', 'margin-right': 'auto', 'width': '0', 'height': '0' }); this.sliderSelection = this.$el.find('.slider-selection'); this.sliderTrack = this.$el.find('.slider-track'); handles = this.$el.find('.slider-handle'); if (isRange && sliderContainer.hasClass('dual')) { this.leftLinkLineTop = this.$el.find('.lineSegment.top .linkLine.left'); this.rightLinkLineTop = this.$el.find('.lineSegment.top .linkLine.right'); this.leftLinkLineBottom = this.$el.find('.lineSegment.bottom .linkLine.left'); this.rightLinkLineBottom = this.$el.find('.lineSegment.bottom .linkLine.right'); var handleArrow = $('
'); var handleBox = $(''); handleArrow.appendTo(handles); handleBox.appendTo(handles); this.rightHandle = handles.eq(1); this.leftTooltip = tooltips.eq(0); this.rightTooltip = tooltips.eq(1); this.leftTooltipInner = tooltipInner.eq(0); this.rightTooltipInner = tooltipInner.eq(1); } else { this.sliderTrack.append(''); this.rightHandle = handles; this.rightTooltip = tooltips; this.rightTooltipArrow = this.rightTooltip.find('.tooltip-arrow'); this.rightTooltipInner = tooltipInner; } this.updateTooltips(); this.updateFilterRangeInfo(); callResolve = false; result = new Promise(function (resolve, reject) { try { setTimeout(function () { _this.layout(); if (_this.leftTooltip) { _this.leftTooltip.fadeIn(); } _this.rightTooltip.fadeIn(); resolve(); }, 100); } catch (error) { reject(error); } }); } else { sliderContainer.addClass('minimized'); if (this.options.indeterminate) { sliderContainer.addClass('indeterminate'); } result = Promise.resolve(); } if (this.options.inverted) { this.invert(); } this.setEnabled(this.options.enabled); this.setSliderAriaLabel(handles); // TODO: Refactor so that we don't instantiate multiple promises unecessarily (see above). if (callResolve) { result = Promise.resolve(); } return result; }, setSliderAriaLabel: function setSliderAriaLabel($handles) { if ($handles && $handles.length > 0) { var percentValue = $handles.get(0).style.left; if (percentValue) { $handles.attr('role', 'slider'); $handles.attr('aria-valuenow', resources.get('a11ySliderHandleLabel', { 'sliderValue': this.options.prettyValue(this.getValue()) })); } } }, getValue: function getValue() { return this.options.value; }, getMin: function getMin() { return this.options.min; }, getMax: function getMax() { return this.options.max; }, isInverted: function isInverted() { return this.inverted; }, invert: function invert() { this.inverted = !this.inverted; this.$el.find('.slider-container').toggleClass('invert'); if (this.options.invertedTooltipTemplate) { this.updateTooltips(); this.updateFilterRangeInfo(); this.layout(); } }, isCleared: function isCleared() { if (this._applyFilter) { return false; } var value = this.getValue(); if (value instanceof Array) { if (this.inverted) { // An inverted range slider is considered to not be cleared. return false; } return value[0] === this.getMin() && value[1] === this.getMax(); } else if (this.inverted) { return value === this.getMax(); } return value === this.getMin(); }, clear: function clear() { if (this.isInverted()) { this.invert(); } var min = this.getMin(); var max = this.getMax(); var value; if (this.getValue() instanceof Array) { value = [min, max]; } else if (this.inverted) { value = min; } else { value = max; } this.setValue(value); this.updateFilterRangeInfo(); }, remove: function remove() { if (this.slider) { this.slider.bootstrapSlider('destroy'); this.slider.remove(); } this.slider = null; View.inherited('remove', this, arguments); }, updateValue: function updateValue() { if (this.slider) { // Update the model. this.options.value = this.slider.bootstrapSlider('getValue'); } this.setSliderAriaLabel(this.$el.find('.slider-handle')); }, updateFilterRangeInfo: function updateFilterRangeInfo() { var titleInfo = this.$el.parents().find('.titleInfo .rangeInfo'); if (this.options.value instanceof Array && (this.options.min !== this.options.value[0] || this.options.max !== this.options.value[1])) { if (this.leftTooltipInner) { var left = this.leftTooltipInner.text(); var right = this.rightTooltipInner.text(); var prettyValue = this.options.prettyValue(this.options.value); if (this.inverted) { titleInfo.text(left + prettyValue[0] + ' ' + right + prettyValue[1]); } else { titleInfo.text(prettyValue[0] + ' - ' + prettyValue[1]); } } } else { titleInfo.text(''); } }, /** * Set the value of this slider. * Use setUiOnly parameter to set the UI state without notifying * listeners of value change. */ setValue: function setValue(value, preventNotify) { this.options.value = value; if (this.slider) { this.slider.bootstrapSlider('setValue', value); this.layout(); } this.triggerOnChange(); if (!preventNotify) { this.triggerSlideStop(); } }, setStep: function setStep(step) { this.options.step = step; if (this.slider) { this.slider.bootstrapSlider('setAttribute', 'step', step); } }, setMin: function setMin(min) { this.options.min = min; if (this.slider) { this.slider.bootstrapSlider('setAttribute', 'min', min); } }, setMax: function setMax(max) { this.options.max = max; if (this.slider) { this.slider.bootstrapSlider('setAttribute', 'max', max); } }, setEnabled: function setEnabled(enable) { if (enable) { this.slider.bootstrapSlider('enable'); this.$el.find('.slider-container').removeClass('disabled'); } else { this.slider.bootstrapSlider('disable'); this.$el.find('.slider-container').addClass('disabled'); } }, onKeyDown: function onKeyDown(oEvent) { if (oEvent.keyCode === 13) { //Enter oEvent.preventDefault(); this.onInputChange(oEvent); } }, onKeyUp: function onKeyUp() {}, onKeyPress: function onKeyPress() { this.layoutTooltips(); }, onMouseDownTooltip: function onMouseDownTooltip(oEvent) { var selector = $(oEvent.target); if (!selector.is(':focus')) { oEvent.preventDefault(); oEvent.stopPropagation(); oEvent.target.select(); } }, onInputFocus: function onInputFocus(ev) { $(ev.target).parent().addClass('focus'); ev.preventDefault(); ev.stopPropagation(); }, onInputBlur: function onInputBlur(ev) { $(ev.target).parent().removeClass('focus'); }, calculateTooltipsWidth: function calculateTooltipsWidth() { if (this.leftTooltipInput) { this.leftTooltipWidth = this.calculateTooltipWidth(this.leftTooltipInput); } this.rightTooltipWidth = this.calculateTooltipWidth(this.rightTooltipInput); }, calculateTooltipWidth: function calculateTooltipWidth(input) { var val = input.val(); var width = 20 + (val.length + 1) * 5; var maxWidth = Math.floor((this.sliderTrack.width() - this.tooltipPadding) / 1.3); if (width > maxWidth) { width = maxWidth; } return width; }, updateTooltipsWidth: function updateTooltipsWidth() { if (this.leftTooltipInput) { this.updateTooltipWidth(this.leftTooltip, this.leftTooltipWidth); } this.updateTooltipWidth(this.rightTooltip, this.rightTooltipWidth); }, updateTooltipWidth: function updateTooltipWidth(input, width) { input.attr('aria-label', input.attr('value')); input.css({ 'width': width + 'px' }); }, onInputChange: function onInputChange(oEvent) { var $target = $(oEvent.currentTarget); var rval = 0; var lval = 0; if ($target.data('input') === 'left') { lval = formatter.parseDecimal($target.val()); rval = this.rightTooltipInput.data('value') ? this.rightTooltipInput.data('value') : this.rightTooltipInput.val(); } else if ($target.data('input') === 'right') { if (this.leftTooltipInput) { lval = this.leftTooltipInput.data('value') ? this.leftTooltipInput.data('value') : this.leftTooltipInput.val(); } rval = formatter.parseDecimal($target.val()); } var value; var prettyValue; if (this.options.value instanceof Array) { value = this.validate(lval, rval); value = this.options.valueFormatter(value); if (lval !== value[0] || rval !== value[1]) { prettyValue = this.options.prettyValue(value); this.leftTooltipInput.data('value', value[0]); this.leftTooltipInput.val(prettyValue[0]); this.rightTooltipInput.data('value', value[1]); this.rightTooltipInput.val(prettyValue[1]); } } else { value = this.validateMax(rval); value = this.options.valueFormatter(value); if (rval !== value) { prettyValue = this.options.prettyValue(value); this.rightTooltipInput.data('value', value); this.rightTooltipInput.val(prettyValue); } } // When input value is smaller than min or greater than max, need to apply filter after input. // Set a flag "_applyFilter" to make isClear function to return false so the filter will be applied. if (value[0] < this.options.value[0]) { this._applyFilter = true; this.setMin(value[0]); } if (value[1] > this.options.value[1]) { this._applyFilter = true; this.setMax(value[1]); } this.setValue(value); this.updateFilterRangeInfo(); }, updateTooltips: function updateTooltips() { if (this.options.tooltipTemplate instanceof Array) { var leftTemplate; var rightTemplate; if (this.inverted && this.options.invertedTooltipTemplate) { leftTemplate = this.options.invertedTooltipTemplate[0]; rightTemplate = this.options.invertedTooltipTemplate[1]; } else { leftTemplate = this.options.tooltipTemplate[0]; rightTemplate = this.options.tooltipTemplate[1]; } this.leftTooltipInner.html($(leftTemplate)); this.rightTooltipInner.html($(rightTemplate)); } else { this.rightTooltipInner.html($(this.options.tooltipTemplate)); } this.updateInputs(); }, updateInputs: function updateInputs() { this.rightTooltipInput = this.rightTooltip.find('input'); this.rightTooltipInput.data('input', 'right'); if (this.leftTooltip) { this.leftTooltipInput = this.leftTooltip.find('input'); this.leftTooltipInput.data('input', 'left'); } }, layoutValues: function layoutValues() { // Update the model. this.updateValue(); var value = this.options.valueFormatter(this.getValue()); var prettyValue = this.options.prettyValue(value); if (this.leftTooltip) { this.leftTooltipInput.data('value', value[0]); this.leftTooltipInput.val(prettyValue[0]); this.rightTooltipInput.data('value', value[1]); this.rightTooltipInput.val(prettyValue[1]); } else { this.rightTooltipInput.data('value', value); this.rightTooltipInput.val(prettyValue); } }, layout: function layout() { this.layoutValues(); this.layoutTooltips(); }, layoutTooltips: function layoutTooltips() { this.calculateTooltipsWidth(); this.calculateRightTooltip(); if (this.leftTooltip) { this.calculateLeftTooltip(); // Only calculate possible collisions if there are two handles. this.calculateCollisions(); // Adjust this.paintLines(); this.layoutTooltip(this.leftTooltip, this.leftTooltipCenter, this.leftTooltipMarginLeft); } this.layoutTooltip(this.rightTooltip, this.rightTooltipCenter, this.rightTooltipMarginLeft); if (this.rightTooltipArrow) { this.layoutTooltipArrow(this.rightTooltipArrow, this.rightTooltipArrowLeft); } this.updateTooltipsWidth(); }, layoutTooltip: function layoutTooltip(tooltip, tooltipLeft, tooltipMarginLeft) { tooltip.css({ 'left': tooltipLeft, 'margin-left': tooltipMarginLeft }); }, layoutTooltipArrow: function layoutTooltipArrow(arrow, left) { arrow.css({ 'left': left }); }, paintLines: function paintLines() { this.leftLinkLineTop.css({ 'left': Math.min(this.leftHandleCenter, this.leftTooltipCenter), 'width': this.leftHandleCenter - this.leftTooltipCenter }); this.rightLinkLineTop.css({ 'left': this.rightHandleCenter, 'width': this.rightTooltipCenter - this.rightHandleCenter }); this.leftLinkLineBottom.css({ 'left': this.leftHandleCenter }); this.rightLinkLineBottom.css({ 'left': this.rightHandleCenter }); }, calculateLeftTooltip: function calculateLeftTooltip() { var tooltipWidth = this.leftTooltipWidth; var sliderPosition = Math.floor(this.sliderSelection.position().left); // Cache the handle position. this.leftHandleCenter = sliderPosition; var tooltipLeft = sliderPosition; tooltipLeft = this.checkLeftBoundaryForLeftTooltip(tooltipLeft, tooltipWidth); tooltipLeft = this.checkRightBoundary(tooltipLeft, tooltipWidth); var marginLeft = Math.floor(-(tooltipWidth / 2)); this.leftTooltipCenter = tooltipLeft; this.leftTooltipMarginLeft = marginLeft; this.leftTooltipWidth = tooltipWidth; }, calculateRightTooltip: function calculateRightTooltip() { var tooltipWidth = this.rightTooltipWidth; var sliderPosition = Math.floor(this.rightHandle.position().left); // Cache the handle position. this.rightHandleCenter = sliderPosition; var tooltipLeft = sliderPosition; tooltipLeft = this.checkLeftBoundaryForRightTooltip(tooltipLeft, tooltipWidth); tooltipLeft = this.checkRightBoundary(tooltipLeft, tooltipWidth); var marginLeft = Math.floor(-tooltipWidth / 2); this.rightTooltipCenter = tooltipLeft; this.rightTooltipMarginLeft = marginLeft; if (this.rightTooltipArrow) { this.rightTooltipArrowLeft = (sliderPosition - tooltipLeft) * 2; } this.rightTooltipWidth = tooltipWidth; }, calculateCollisions: function calculateCollisions() { var padding = this.tooltipPadding; var leftTooltipRight = this.leftTooltipCenter + this.leftTooltipWidth / 2; var rightTooltipLeft = this.rightTooltipCenter - this.rightTooltipWidth / 2; var difference = rightTooltipLeft - leftTooltipRight - padding; if (difference < 0) { var halfDifference = Math.floor(difference / 2); this.leftTooltipCenter += halfDifference; this.rightTooltipCenter -= halfDifference; if (this.leftTooltipCenter <= this.tooltipMinLeftForLeftTooltip) { this.leftTooltipCenter = this.tooltipMinLeftForLeftTooltip; this.rightTooltipCenter = this.leftTooltipCenter + this.leftTooltipWidth / 2 + padding + this.rightTooltipWidth / 2; } else { var rightTooltipRight = this.rightTooltipCenter + this.rightTooltipWidth / 2; var trackRight = this.sliderTrack.position().left + this.sliderTrack.width() + 15; difference = rightTooltipRight - trackRight; if (difference > 0) { this.rightTooltipCenter = trackRight - this.rightTooltipWidth / 2; this.leftTooltipCenter = this.rightTooltipCenter - this.rightTooltipWidth / 2 - padding - this.leftTooltipWidth / 2; } } this.rightTooltipCenter = Math.floor(this.rightTooltipCenter); this.leftTooltipCenter = Math.floor(this.leftTooltipCenter); } }, checkLeftBoundaryForLeftTooltip: function checkLeftBoundaryForLeftTooltip(position, tooltipWidth) { var trackLeft = this.sliderTrack.position().left; var minLeft = trackLeft + tooltipWidth / 2 - 15; // Cache the min left. this.tooltipMinLeftForLeftTooltip = minLeft; if (position < minLeft) { position = minLeft; } return position; }, checkLeftBoundaryForRightTooltip: function checkLeftBoundaryForRightTooltip(position, tooltipWidth) { var trackLeft = this.sliderTrack.position().left; var minLeft = trackLeft + tooltipWidth / 2 - 15; if (position < minLeft) { position = minLeft; } return position; }, checkRightBoundary: function checkRightBoundary(position, tooltipWidth) { var trackRight = this.sliderTrack.position().left + this.sliderTrack.width(); var maxRight = trackRight - tooltipWidth / 2 + 15; // Cache the max left. this.tooltipMaxRight = maxRight; if (position > maxRight) { position = maxRight; } return position; }, onSlide: function onSlide() { this.layout(); this.triggerOnChange(); }, onSlideStart: function onSlideStart() { this.sliding = true; }, onSlideStop: function onSlideStop() { this.sliding = false; // Update the model. this.updateValue(); this.updateFilterRangeInfo(); this.layout(); this.triggerOnChange(); this.triggerSlideStop(); }, triggerSlideStop: function triggerSlideStop() { this.trigger('action:slidestop', {}); }, triggerOnChange: function triggerOnChange() { this.trigger('action:change', {}); }, validate: function validate(lval, rval) { lval = this.validateMin(lval); if (rval !== undefined) { rval = this.validateMax(rval); // The left value should always be less than the right value. if (rval < lval) { var tempVal = rval; // Swap the values. rval = lval; lval = tempVal; } return [lval, rval]; } return lval; }, validateMin: function validateMin(val) { if (isNaN(val)) { val = this.options.min; } return this.validateBoundary(val); }, validateMax: function validateMax(val) { if (isNaN(val)) { val = this.options.max; } return this.validateBoundary(val); }, validateBoundary: function validateBoundary(val) { if (!this.options.noMinMaxRestriction) { // Check min bounds. var minVal = this.options.min; if (val <= minVal) { return minVal; } // Check max bounds. var maxVal = this.options.max; if (val >= maxVal) { return maxVal; } } else if (typeof val !== 'number') { val = Number(val); } return val; } }); return View; }); //# sourceMappingURL=Slider.js.map