var app = angular.module('plunker', ['rzModule']);
app.controller('sliderController', function($scope) {
$scope.init = function() {
$scope.componentDetails = $scope.formatSliderMetadata($scope.validValues, $scope.translate,$scope.incrSpeed,$scope.decrSpeed)
};
$scope.formatSliderMetadata = function(validValues, translate,incrFun,decrFun) {
var codeArray = [];
var valueArray = [];
for (var i = 0; i < validValues.length; i++) {
codeArray[i] = validValues[i].code;
valueArray[i] = validValues[i].decode;
}
// formatted slider attruibutes
var formattedSliderValidValues =
{
floor: codeArray[0],
ceil: codeArray[codeArray.length-1],
value: 0,
translate : translate,
incrSpeed : incrFun,
decrSpeed : decrFun,
codeArray : codeArray,
stepArray : codeArray,
valueArray : valueArray,
selection : true,
displayScale : true,
typedValue: codeArray[0]
};
return formattedSliderValidValues;
};
$scope.$on('slideEnded', function () {
console.log("slideEnded Event Fired : " +$scope.componentDetails.stepArray[$scope.componentDetails.value]);
});
/**
* function executed whenever the bandwidthCIR value is increased through associated text-field
* It takes slider to next available step
*/
$scope.incrSpeed=function(){
for(var i=0; i< $scope.componentDetails.stepArray.length; i++){
if(parseInt($scope.componentDetails.stepArray[i]) > parseInt($scope.componentDetails.typedValue)){
$scope.componentDetails.typedValue = $scope.componentDetails.stepArray[i];
$scope.componentDetails.value = i;
break;
}
}
};
/**
* function executed whenever the bandwidthCIR value is decreased through associated text-field
* It takes slider to previous available step
*/
$scope.decrSpeed=function(){
for(var i=$scope.componentDetails.stepArray.length-1; i>=0; i--){
if(parseInt($scope.componentDetails.stepArray[i]) < parseInt($scope.componentDetails.typedValue)){
$scope.componentDetails.typedValue = $scope.componentDetails.stepArray[i];
$scope.componentDetails.value = i;
break;
}
}
};
/**
* function executed whenever the bandwidthCIR slider value is changed
*/
$scope.translate = function(value) {
$scope.componentDetails.typedValue = $scope.componentDetails.stepArray[value];
return $scope.componentDetails.valueArray[value];
};
$scope.validValues = [{
"code": "2",
"decode": "2 Mbps"
}, {
"code": "4",
"decode": "4 Mbps"
}, {
"code": "5",
"decode": "5 Mbps"
}, {
"code": "8",
"decode": "8 Mbps"
}, {
"code": "10",
"decode": "10 Mbps"
}, {
"code": "20",
"decode": "20 Mbps"
}, {
"code": "50",
"decode": "50 Mbps"
}, {
"code": "100",
"decode": "100 Mbps"
}, {
"code": "150",
"decode": "150 Mbps"
}, {
"code": "250",
"decode": "250 Mbps"
}, {
"code": "500",
"decode": "500 Mbps"
}, {
"code": "600",
"decode": "600 Mbps"
}, {
"code": "1000",
"decode": "1 Gbps"
}];
});
<!DOCTYPE html>
<html ng-app="plunker">
<head>
<meta charset="utf-8" />
<title>AngularJS Plunker</title>
<script>
document.write('<base href="' + document.location + '" />');
</script>
<link rel="stylesheet" href="style.css" />
<script data-require="angular.js@1.2.x" src="https://code.angularjs.org/1.2.20/angular.js" data-semver="1.2.20"></script>
<script src="rzslider.js"></script>
<script src="app.js"></script>
</head>
<body>
<div class="general-config" ng-controller="sliderController" ng-init="init()">
<rzslider class="rzslider-evc-size"
id="componentDetails.id" rz-slider-floor="componentDetails.floor"
rz-slider-ceil="componentDetails.ceil"
rz-slider-model="componentDetails.value"
rz-slider-step-array="componentDetails.stepArray"
rz-slider-translate="componentDetails.translate"
rz-slider-selection="componentDetails.selection"
rz-slider-display-scale="componentDetails.displayScale"> </rzslider>
<span class="sliderinputbox">
<input num-only
ng-keypress="onSliderEnter($event)" readonly="readonly"
class="slidervaluebox disablepointer1" id="inputTrueBandwidth" min="0"
max="{{componentDetails.stepArray[componentDetails.ceil]}}"
ng-blur="validateSliderRange();" type="text"
ng-model="componentDetails.typedValue"/>
<span class="slidervalueincrdecr">
<span ng-click="componentDetails.incrSpeed()"
ng-class="(componentDetails.typedValue!=componentDetails.stepArray[componentDetails.ceil]) ? 'sliderTextIncrementer' : 'sliderTextIncrementerDisabled'"></span>
<span ng-click="componentDetails.decrSpeed()"
ng-class="(componentDetails.typedValue!=componentDetails.stepArray[componentDetails.floor]) ? 'sliderTextDecrementer' : 'sliderTextDecrementerDisabled'"></span>
</span>
</span>
</div>
</body>
</html>
/* Put your css in here */
/**
* Angular JS slider directive
*
* (c) Rafal Zajac <rzajac@gmail.com>
* http://github.com/rzajac/angularjs-slider
*
* Licensed under the MIT license
*/
rzslider {
position: relative;
display: inline-block;
width: 80%;
height: 2px;
margin: 30px 0 15px 0;
vertical-align: middle;
}
rzslider span {
position: absolute;
display: inline-block;
white-space: nowrap;
}
rzslider span.base {
width: 100%;
height: 100%;
padding: 0;
}
rzslider span.bar {
z-index: 1;
width: 100%;
height: 100%;
border: 1px solid #D7D7D7!important;
border-radius: 4px;
background: #D7D7D7!important;
}
.wrapper {
width: 97%;
}
rzslider span.bar.selection {
z-index: 1;
width: 0;
background: #1575A9!important;
border: 1px solid #1575A9!important;
}
rzslider span.pointer {
background: #0579B4;
top: -10px;
z-index: 2;
width: 22px;
height: 22px;
cursor: pointer;
-webkit-border-radius: 16px;
-moz-border-radius: 16px;
border-radius: 16px;
}
/*
rzslider span.pointer:after {
position: absolute;
top: 12px;
left: 12px;
padding: 5px;
background: #71818e;
-webkit-border-radius: 4px;
-moz-border-radius: 4px;
border-radius: 4px;
content: '';
background: #0579B4;
}*/
rzslider span.pointer:hover:after {
background: #0579B4;
}
rzslider span.bubble {
top: 18px;
padding: 1px 3px 1px 3px;
color: #808080;
cursor: default;
font-size: 12px;
}
rzslider span.bubble.selection {
top: 15px;
}
rzslider span.bubble.limit {
/*color: #808080;*/
}
.DisableSlider .EnableSliderToggle{
background:#BBBBBB!important;
border: 1px solid #BBBBBB!important;
}
.EnableSlider .EnableSliderToggle {
background:#1575A9;
border: 1px solid #1575A9;
}
.DisableSlider .selection {
background:#BBBBBB!important;
border: 1px solid #BBBBBB!important;
}
.DisableSlider .Disabletooltip {
display:none;
}
.EnableSlider .Disabletooltip{
display:block;
}
.DisableSlider .DisableCOS {
display:none;
}
.EnableSlider .DisableCOS {
display:block;
}
.disablepointer2 { pointer-events:none;}
.disablepointer3 { border-bottom: 8px solid grey; pointer-events:none; }
.disablepointer4 {border-top: 8px solid grey; }
.scaleWrap ul {
overflow: hidden;
float: left;
margin-left: -31px;
margin-top: 12px;
width: 97.5%;
list-style: none;
}
/* rz-slider styling */
.rzslide-addport {
width:100px;
float:right;
margin-top:20px;
margin-right: 5px;
}
.inputslide-port {
float:right;
width:60px;
padding:5px 24px 5px 0px;
border:1px solid grey;
border-radius:5px;
text-align:center;
}
.slidearrow {
position: absolute;
right: 0;
height: 23px;
border-left: 1px solid grey;
width: 17%;
top: 2px;
padding: 2px 3px;
}
.rzslide-addevc {
width:100px;
float:right;
margin-top:20px;
margin-right: 5px;
}
.inputslide-evc {
float:right;
width:60px;
padding:5px 24px 5px 0px;
border:1px solid grey;
border-radius:5px;
text-align:center;
}
.slidearrow-evc {
position: absolute;
right: 0;
height: 23px;
border-left: 1px solid grey;
width: 17%;top: 2px;
padding: 2px 3px;
}
.rzslider-evc-size {
width: 80%;
}
.scaleWrap ul li{
float:left;
}
.scaleWrap ul li:last-child{
width: 1% !important;
}
.sliderinputbox {
width:100px;
float:right;
margin-top:20px;
margin-right: 5px;
}
.slidervaluebox {
float:right;
width:60px;
padding:5px 24px 5px 0px;
border:1px solid grey;
border-radius:5px;
text-align:center;
position:relative;
}
.slidervalueincrdecr {
position: absolute;
right: 13px;
height: 23px;
border-left: 1px solid grey;
width: 2%;
top: 28px;
padding: 2px 3px;
}
.sliderTextIncrementer{
width: 0;
height: 0;
border-left: 7px solid transparent;
border-right: 7px solid transparent;
border-bottom: 8px solid #0579B4;
border-radius: 5px;
position: absolute;
top: 6px;
right: 4px;
}
.sliderTextIncrementerDisabled{
width: 0;
height: 0;
border-left: 7px solid transparent;
border-right: 7px solid transparent;
border-bottom: 8px solid grey;
border-radius: 5px;
position: absolute;
top: 6px;
right: 4px;
}
.sliderTextDecrementer{
width: 0;
height: 0;
border-left: 7px solid transparent;
border-right: 7px solid transparent;
border-top: 8px solid #0579B4;
border-radius: 5px;
position: absolute;
top: 16px;
right: 4px;
}
.sliderTextDecrementerDisabled{
width: 0;
height: 0;
border-left: 7px solid transparent;
border-right: 7px solid transparent;
border-top: 8px solid grey;
border-radius: 5px;
position: absolute;
top: 16px;
right: 4px;
}
/**
* Angular JS slider directive
*
* (c) Rafal Zajac <rzajac@gmail.com>
* http://github.com/rzajac/angularjs-slider
*
* Version: v0.1.3
*
* Licensed under the MIT license
*/
/* global angular: false */
angular.module('rzModule', [])
.value('throttle',
/**
* throttle
*
* Taken from underscore project
*
* @param {Function} func
* @param {number} wait
* @param {ThrottleOptions} options
* @returns {Function}
*/
function throttle(func, wait, options) {
var getTime = (Date.now || function() {
return new Date().getTime();
});
var context, args, result;
var timeout = null;
var previous = 0;
options || (options = {});
var later = function() {
previous = options.leading === false ? 0 : getTime();
timeout = null;
result = func.apply(context, args);
context = args = null;
};
return function() {
var now = getTime();
if (!previous && options.leading === false) previous = now;
var remaining = wait - (now - previous);
context = this;
args = arguments;
if (remaining <= 0) {
clearTimeout(timeout);
timeout = null;
previous = now;
result = func.apply(context, args);
context = args = null;
} else if (!timeout && options.trailing !== false) {
timeout = setTimeout(later, remaining);
}
return result;
}
})
.factory('Slider', ['$timeout', '$document','$compile', 'throttle', function($timeout, $document,$compile, throttle)
{
/**
* Slider
*
* @param {ngScope} scope The AngularJS scope
* @param {Element} sliderElem The slider directive element wrapped in jqLite
* @param {*} attributes The slider directive attributes
* @constructor
*/
var Slider = function(scope, sliderElem, attributes)
{
/**
* The slider's scope
*
* @type {ngScope}
*/
this.scope = scope;
/**
* The slider attributes
*
* @type {*}
*/
this.attributes = attributes;
/**
* Slider element wrapped in jqLite
*
* @type {jqLite}
*/
this.sliderElem = sliderElem;
/**
* Slider type
*
* @type {string}
*/
this.range = attributes.rzSliderHigh !== undefined && attributes.rzSliderModel !== undefined;
/**
* Half of the width of the slider handles
*
* @type {number}
*/
this.handleHalfWidth = 0;
/**
* Maximum left the slider handle can have
*
* @type {number}
*/
this.maxLeft = 0;
/**
* Precision
*
* @type {number}
*/
this.precision = 0;
/**
* Step
*
* @type {number}
*/
this.step = 0;
/**
* Step Array
*
* @type {[]}
*/
this.stepArray = [];
/**
* The name of the handle we are currently tracking
*
* @type {string}
*/
this.tracking = '';
/**
* Minimum value (floor) of the model
*
* @type {number}
*/
this.minValue = 0;
/**
* Maximum value (ceiling) of the model
*
* @type {number}
*/
this.maxValue = 0;
/**
* The delta between min and max value
*
* @type {number}
*/
this.valueRange = 0;
/**
* Set to true if init method already executed
*
* @type {boolean}
*/
this.initRun = false;
/**
* for custom tooltip, slider fix
*
* @type {boolean}
*/
this.hasPopOver = attributes.rzHasPopover !== undefined;
/**
* Text to be displayed inside popover
*
* @type {boolean}
*/
this.sliderPopoverText = (attributes.popoverText !== undefined)?attributes.popoverText:"Unable to retrive message";
/**
* Slider Selection colored value
*/
this.isSelection = false;
/**
* Slider scale display flag
*/
this.displayScale = false;
/**
* Custom translate function
*
* @type {function}
*/
this.customTrFn = null;
// Slider DOM elements wrapped in jqLite
this.fullBar = null; // The whole slider bar
this.selBar = null; // Highlight between two handles
this.singleSel = null; // Custom added for single selection
this.minH = null; // Left slider handle
this.maxH = null; // Right slider handle
this.flrLab = null; // Floor label
this.ceilLab = null; // Ceiling label
this.minLab = null; // Label above the low value
this.maxLab = null; // Label above the high value
this.cmbLab = null; // Combined label
this.dispScale = null; //Custom added for display Scale
// Initialize slider
this.init();
};
// Add instance methods
Slider.prototype = {
/**
* Initialize slider
*
* @returns {undefined}
*/
init: function()
{
var self = this;
if(this.scope.rzSliderTranslate)
{
this.customTrFn = this.scope.rzSliderTranslate();
}
this.isSelection = this.scope.rzSliderSelection === undefined ? true : this.scope.rzSliderSelection;
this.displayScale = this.scope.rzSliderDisplayScale === undefined ? false : this.scope.rzSliderDisplayScale;
this.initElemHandles();
this.calcViewDimensions();
this.precision = this.scope.rzSliderPrecision === undefined ? 0 : +this.scope.rzSliderPrecision;
this.step = this.scope.rzSliderStep === undefined ? 1 : +this.scope.rzSliderStep;
if(this.scope.rzSliderStepArray){
this.stepArray = this.scope.rzSliderStepArray;
this.step = 1;
this.scope.rzSliderFloor = 0;
this.scope.rzSliderCeil = this.stepArray.length-1;
}
this.setMinAndMax();
$timeout(function()
{
self.updateCeilLab();
self.updateFloorLab();
self.initHandles();
self.bindEvents();
});
// Recalculate slider view dimensions
this.scope.$on('reCalcViewDimensions', angular.bind(this, this.calcViewDimensions));
// Recalculate stuff if view port dimensions have changed
angular.element(window).on('resize', angular.bind(this, this.calcViewDimensions));
this.initRun = true;
// Watch for changes to the model
var thrLow = throttle(function()
{
self.setMinAndMax();
self.updateLowHandle(self.valueToOffset(self.scope.rzSliderModel));
if(self.range)
{
self.updateSelectionBar();
self.updateCmbLabel();
}
if(self.isSelection)
{
self.updateSingleSelectionBar();
}
}, 350, { leading: false });
var thrHigh = throttle(function()
{
self.setMinAndMax();
self.updateHighHandle(self.valueToOffset(self.scope.rzSliderHigh));
self.updateSelectionBar();
self.updateCmbLabel();
}, 350, { leading: false });
this.scope.$on('rzSliderForceRender', function()
{
self.resetLabelsValue();
thrLow();
thrHigh();
self.resetSlider();
});
// Watchers
this.scope.$watch('rzSliderModel', function(newValue, oldValue)
{
if(newValue === oldValue) return;
thrLow();
});
this.scope.$watch('rzSliderHigh', function(newValue, oldValue)
{
if(newValue === oldValue) return;
thrHigh();
});
this.scope.$watch('rzSliderFloor', function(newValue, oldValue)
{
if(newValue === oldValue) return;
self.resetSlider();
});
this.scope.$watch('rzSliderCeil', function(newValue, oldValue)
{
if(newValue === oldValue) return;
self.resetSlider();
});
},
/**
* Resets slider
*
* @returns {undefined}
*/
resetSlider: function()
{
this.setMinAndMax();
this.calcViewDimensions();
this.updateCeilLab();
this.updateFloorLab();
},
/**
* Reset label values
*
* @return {undefined}
*/
resetLabelsValue: function()
{
this.minLab.rzsv = undefined;
this.maxLab.rzsv = undefined;
},
/**
* Initialize slider handles positions and labels
*
* Run only once during initialization and every time view port changes size
*
* @returns {undefined}
*/
initHandles: function()
{
this.updateLowHandle(this.valueToOffset(this.scope.rzSliderModel));
if(this.range)
{
this.updateHighHandle(this.valueToOffset(this.scope.rzSliderHigh));
this.updateSelectionBar();
this.updateCmbLabel();
}
if(this.isSelection)
{
this.updateSingleSelectionBar();
}
},
/**
* Translate value to human readable format
*
* @param {number|string} value
* @param {jqLite} label
* @param {bool?} useCustomTr
* @returns {undefined}
*/
translateFn: function(value, label, useCustomTr)
{
useCustomTr = useCustomTr === undefined ? true : useCustomTr;
var valStr = this.customTrFn && useCustomTr ? '' + this.customTrFn(value) : '' + value,
getWidth = false;
if(label.rzsv === undefined || label.rzsv.length != valStr.length)
{
getWidth = true;
label.rzsv = valStr;
}
label.text(valStr);
// Update width only when length of the label have changed
if(getWidth) { this.getWidth(label); }
},
/**
* Set maximum and minimum values for the slider
*
* @returns {undefined}
*/
setMinAndMax: function()
{
if(this.scope.rzSliderFloor)
{
this.minValue = +this.scope.rzSliderFloor;
}
else
{
this.minValue = this.scope.rzSliderFloor = 0;
}
if(this.scope.rzSliderCeil)
{
this.maxValue = +this.scope.rzSliderCeil;
}
else
{
this.scope.rzSliderCeil = this.maxValue = this.range ? this.scope.rzSliderHigh : this.scope.rzSliderModel;
}
this.valueRange = this.maxValue - this.minValue;
},
/**
* Set the slider children to variables for easy access
*
* Run only once during initialization
*
* @returns {undefined}
*/
initElemHandles: function()
{
angular.forEach(this.sliderElem.children(), function(elem, index)
{
var _elem = angular.element(elem);
switch(index)
{
case 0: this.fullBar = _elem; break;
case 1: this.selBar = _elem; break;
case 2: this.singleSel = _elem; break;
case 3: this.minH = _elem; break;
case 4: this.maxH = _elem; break;
case 5: this.flrLab = _elem; break;
case 6: this.ceilLab = _elem; break;
case 7: this.minLab = _elem; break;
case 8: this.maxLab = _elem; break;
case 9: this.cmbLab = _elem; break;
case 10: this.dispScale = _elem; break;
}
}, this);
// Initialize offsets
this.fullBar.rzsl = 0;
this.selBar.rzsl = 0;
this.singleSel.rzsl = 0;
this.minH.rzsl = 0;
this.maxH.rzsl = 0;
this.flrLab.rzsl = 0;
this.ceilLab.rzsl = 0;
this.minLab.rzsl = 0;
this.maxLab.rzsl = 0;
this.cmbLab.rzsl = 0;
this.dispScale.rzsl = 0;
// Remove stuff not needed in single slider
if( ! this.range)
{
this.cmbLab.remove();
this.maxLab.remove();
this.maxH.remove();
this.selBar.remove();
}
if( ! this.isSelection)
{
this.singleSel.remove();
}
if( ! this.displayScale)
{
this.dispScale.remove();
}
},
/**
* Calculate dimensions that are dependent on view port size
*
* Run once during initialization and every time view port changes size.
*
* @returns {undefined}
*/
calcViewDimensions: function ()
{
var handleWidth = this.getWidth(this.minH);
this.handleHalfWidth = handleWidth / 2;
this.barWidth = this.getWidth(this.fullBar);
this.maxLeft = this.barWidth - handleWidth;
this.getWidth(this.sliderElem);
this.sliderElem.rzsl = this.sliderElem[0].getBoundingClientRect().left;
if(this.initRun)
{
this.updateCeilLab();
this.initHandles();
}
},
/**
* Update position of the ceiling label
*
* @returns {undefined}
*/
updateCeilLab: function()
{
this.translateFn(this.scope.rzSliderCeil, this.ceilLab);
this.setLeft(this.ceilLab, this.barWidth - this.ceilLab.rzsw);
this.getWidth(this.ceilLab);
},
/**
* Update position of the floor label
*
* @returns {undefined}
*/
updateFloorLab: function()
{
this.translateFn(this.scope.rzSliderFloor, this.flrLab);
this.getWidth(this.flrLab);
},
/**
* Update slider handles and label positions
*
* @param {string} which
* @param {number} newOffset
*/
updateHandles: function(which, newOffset)
{
if(which === 'rzSliderModel')
{
this.updateLowHandle(newOffset);
if(this.range)
{
this.updateSelectionBar();
this.updateCmbLabel();
}
if(this.isSelection)
{
this.updateSingleSelectionBar();
}
return;
}
if(which === 'rzSliderHigh')
{
this.updateHighHandle(newOffset);
if(this.range)
{
this.updateSelectionBar();
this.updateCmbLabel();
}
if(this.isSelection)
{
this.updateSingleSelectionBar();
}
return;
}
// Update both
this.updateLowHandle(newOffset);
this.updateHighHandle(newOffset);
this.updateSelectionBar();
this.updateSingleSelectionBar();
this.updateCmbLabel();
},
/**
* Update low slider handle position and label
*
* @param {number} newOffset
* @returns {undefined}
*/
updateLowHandle: function(newOffset)
{
this.setLeft(this.minH, newOffset);
this.translateFn(this.scope.rzSliderModel, this.minLab);
this.setLeft(this.minLab, newOffset - this.minLab.rzsw / 2 + this.handleHalfWidth);
this.shFloorCeil();
},
/**
* Update high slider handle position and label
*
* @param {number} newOffset
* @returns {undefined}
*/
updateHighHandle: function(newOffset)
{
this.setLeft(this.maxH, newOffset);
this.translateFn(this.scope.rzSliderHigh, this.maxLab);
this.setLeft(this.maxLab, newOffset - this.maxLab.rzsw / 2 + this.handleHalfWidth);
this.shFloorCeil();
},
/**
* Show / hide floor / ceiling label
*
* @returns {undefined}
*/
shFloorCeil: function()
{
var flHidden = false, clHidden = false;
if(this.minLab.rzsl <= this.flrLab.rzsl + this.flrLab.rzsw + 5)
{
flHidden = true;
this.hideEl(this.flrLab);
}
else
{
flHidden = false;
this.showEl(this.flrLab);
}
if(this.minLab.rzsl + this.minLab.rzsw >= this.ceilLab.rzsl - this.handleHalfWidth - 10)
{
clHidden = true;
this.hideEl(this.ceilLab);
}
else
{
clHidden = false;
this.showEl(this.ceilLab);
}
if(this.range)
{
if(this.maxLab.rzsl + this.maxLab.rzsw >= this.ceilLab.rzsl - 10)
{
this.hideEl(this.ceilLab);
}
else if( ! clHidden)
{
this.showEl(this.ceilLab);
}
// Hide or show floor label
if(this.maxLab.rzsl <= this.flrLab.rzsl + this.flrLab.rzsw + this.handleHalfWidth)
{
this.hideEl(this.flrLab);
}
else if( ! flHidden)
{
this.showEl(this.flrLab);
}
}
},
/**
* Update slider selection bar, combined label and range label
*
* @returns {undefined}
*/
updateSelectionBar: function()
{
this.setWidth(this.selBar, this.maxH.rzsl - this.minH.rzsl);
this.setLeft(this.selBar, this.minH.rzsl + this.handleHalfWidth);
},
/**
* Update slider selection bar for single handle
*
* @returns {undefined}
*/
updateSingleSelectionBar: function()
{
this.setWidth(this.singleSel, this.minH.rzsl);
},
/**
* Update combined label position and value
*
* @returns {undefined}
*/
updateCmbLabel: function()
{
var lowTr, highTr;
if(this.minLab.rzsl + this.minLab.rzsw + 10 >= this.maxLab.rzsl)
{
if(this.customTrFn)
{
lowTr = this.customTrFn(this.scope.rzSliderModel);
highTr = this.customTrFn(this.scope.rzSliderHigh);
}
else
{
lowTr = this.scope.rzSliderModel;
highTr = this.scope.rzSliderHigh;
}
this.translateFn(lowTr + ' - ' + highTr, this.cmbLab, false);
this.setLeft(this.cmbLab, this.selBar.rzsl + this.selBar.rzsw / 2 - this.cmbLab.rzsw / 2);
this.hideEl(this.minLab);
this.hideEl(this.maxLab);
this.showEl(this.cmbLab);
}
else
{
this.showEl(this.maxLab);
this.showEl(this.minLab);
this.hideEl(this.cmbLab);
}
},
/**
* Round value to step and precision
*
* @param {number} value
* @returns {number}
*/
roundStep: function(value)
{
var step = this.step,
remainder = (value - this.minValue) % step,
steppedValue = remainder > (step / 2) ? value + step - remainder : value - remainder;
return +(steppedValue).toFixed(this.precision);
},
/**
* Hide element
*
* @param element
* @returns {jqLite} The jqLite wrapped DOM element
*/
hideEl: function (element)
{
return element.css({opacity: 1});
},
/**
* Show element
*
* @param element The jqLite wrapped DOM element
* @returns {jqLite} The jqLite
*/
showEl: function (element)
{
return element.css({opacity: 1});
},
/**
* Set element left offset
*
* @param {jqLite} elem The jqLite wrapped DOM element
* @param {number} left
* @returns {number}
*/
setLeft: function (elem, left)
{
elem.rzsl = left;
elem.css({left: left + 'px'});
return left;
},
/**
* Get element width
*
* @param {jqLite} elem The jqLite wrapped DOM element
* @returns {number}
*/
getWidth: function(elem)
{
var val = elem[0].getBoundingClientRect();
elem.rzsw = val.right - val.left;
return elem.rzsw;
},
/**
* Set element width
*
* @param {jqLite} elem The jqLite wrapped DOM element
* @param {number} width
* @returns {*}
*/
setWidth: function(elem, width)
{
elem.rzsw = width;
elem.css({width: width + 'px'});
return width;
},
/**
* Translate value to pixel offset
*
* @param {number} val
* @returns {number}
*/
valueToOffset: function(val)
{
return (val - this.minValue) * this.maxLeft / this.valueRange;
},
/**
* Translate offset to model value
*
* @param {number} offset
* @returns {number}
*/
offsetToValue: function(offset)
{
return (offset / this.maxLeft) * this.valueRange + this.minValue;
},
// Events
/**
* Bind mouse and touch events to slider handles
*
* @returns {undefined}
*/
bindEvents: function()
{
this.minH.on('mousedown', angular.bind(this, this.onStart, this.minH, 'rzSliderModel'));
if(this.range) { this.maxH.on('mousedown', angular.bind(this, this.onStart, this.maxH, 'rzSliderHigh')) }
this.minH.on('touchstart', angular.bind(this, this.onStart, this.minH, 'rzSliderModel'));
if(this.range) { this.maxH.on('touchstart', angular.bind(this, this.onStart, this.maxH, 'rzSliderHigh')) }
if(this.hasPopOver) {
angular.element(".pointer:first").attr({"popover":"","item":"slider","trigger":"hover"}).attr("slider-popover-text","{{" + this.sliderPopoverText + "}}");
$compile(angular.element(".pointer:first"))(this.scope.$parent);
}
},
/**
* onStart event handler
*
* @param {Object} pointer The jqLite wrapped DOM element
* @param {string} ref One of the refLow, refHigh values
* @param {Event} event The event
* @returns {undefined}
*/
onStart: function (pointer, ref, event)
{
event.stopPropagation();
event.preventDefault();
if(this.tracking !== '') { return }
// We have to do this in case the HTML where the sliders are on
// have been animated into view.
this.calcViewDimensions();
this.tracking = ref;
pointer.addClass('active');
if(this.hasPopOver === false) // This means slider is not disabled
{
if(event.touches || (typeof(event.originalEvent) != 'undefined' && event.originalEvent.touches))
{
$document.on('touchmove', angular.bind(this, this.onMove, pointer));
$document.on('touchend', angular.bind(this, this.onEnd));
}
else
{
$document.on('mousemove', angular.bind(this, this.onMove, pointer));
$document.on('mouseup', angular.bind(this, this.onEnd));
}
}
},
/**
* onMove event handler
*
* @param {jqLite} pointer
* @param {Event} event The event
* @returns {undefined}
*/
onMove: function (pointer, event)
{
var eventX = event.clientX || (typeof(event.originalEvent) != 'undefined' ? event.originalEvent.touches[0].clientX : event.touches[0].clientX),
sliderLO = this.sliderElem.rzsl,
newOffset = eventX - sliderLO - this.handleHalfWidth,
newValue;
if(newOffset <= 0)
{
if(pointer.rzsl !== 0)
{
this.scope[this.tracking] = this.minValue;
this.updateHandles(this.tracking, 0);
this.scope.$apply();
}
return;
}
if(newOffset >= this.maxLeft)
{
if(pointer.rzsl !== this.maxLeft)
{
this.scope[this.tracking] = this.maxValue;
this.updateHandles(this.tracking, this.maxLeft);
this.scope.$apply();
}
return;
}
newValue = this.offsetToValue(newOffset);
newValue = this.roundStep(newValue);
if (this.range)
{
if (this.tracking === 'rzSliderModel' && newValue >= this.scope.rzSliderHigh)
{
this.scope[this.tracking] = this.scope.rzSliderHigh;
this.updateHandles(this.tracking, this.maxH.rzsl);
this.tracking = 'rzSliderHigh';
this.minH.removeClass('active');
this.maxH.addClass('active');
}
else if(this.tracking === 'rzSliderHigh' && newValue <= this.scope.rzSliderModel)
{
this.scope[this.tracking] = this.scope.rzSliderModel;
this.updateHandles(this.tracking, this.minH.rzsl);
this.tracking = 'rzSliderModel';
this.maxH.removeClass('active');
this.minH.addClass('active');
}
}
if(this.scope[this.tracking] !== newValue)
{
this.scope[this.tracking] = newValue;
this.updateHandles(this.tracking, this.valueToOffset(newValue));
this.scope.$apply();
}
},
/**
* onEnd event handler
*
* @param {Event} event The event
* @returns {undefined}
*/
onEnd: function(event)
{
this.minH.removeClass('active');
this.maxH.removeClass('active');
if(event.touches || (typeof(event.originalEvent) != 'undefined' && event.originalEvent.touches))
{
$document.unbind('touchmove');
$document.unbind('touchend');
}
else
{
$document.unbind('mousemove');
$document.unbind('mouseup');
}
this.scope.$emit('slideEnded');
this.tracking = '';
}
};
return Slider;
}])
.directive('rzslider', ['Slider', function(Slider)
{
return {
restrict: 'EA',
scope: {
rzSliderFloor: '=?',
rzSliderCeil: '=?',
rzSliderStep: '@',
rzSliderStepArray: '=',
rzSliderPrecision: '@',
rzSliderModel: '=?',
rzSliderHigh: '=?',
rzSliderTranslate: '&',
rzSliderSelection: '=',
rzSliderDisplayScale: '='
},
template: '<span class="bar EnableSliderToggle"></span>' + // 0 The slider bar
'<span class="bar selection"></span>' + // 1 Highlight between two handles
'<span class="bar selection"></span>' + // 2 Highlight for single bar
'<span class="pointer EnableSliderToggle"></span>' + // 3 Left slider handle
'<span class="pointer EnableSliderToggle"></span>' + // 4 Right slider handle
'<span class="bubble limit"></span>' + // 5 Floor label
'<span class="bubble limit"></span>' + // 6 Ceiling label
'<span class="bubble Disabletooltip bubble_slider_tooltip"></span>' + // 7 Label above left slider handle
'<span class="bubble Disabletooltip bubble_slider_tooltip"></span>' + // 8 Label above right slider handle
'<span class="bubble Disabletooltip bubble_slider_tooltip"></span>' + // 9 Range label when the slider handles are close ex. 15 - 17
'<div class="scaleWrap"><ul><li ng-repeat="step in rzSliderStepArray" ng-attr-style="width:{{99 / (rzSliderStepArray.length-1)}}%"><b>|</b></li></ul></div>',
link: function(scope, elem, attr)
{
return new Slider(scope, elem, attr);
}
};
}]);
// IDE assist
/**
* @name ngScope
*
* @property {number} rzSliderModel
* @property {number} rzSliderHigh
* @property {number} rzSliderCeil
*/
/**
* @name jqLite
*
* @property {number|undefined} rzsl
* @property {number|undefined} rzsw
* @property {string|undefined} rzsv
* @property {Function} css
* @property {Function} text
*/
/**
* @name Event
* @property {Array} touches
*/
/**
* @name ThrottleOptions
*
* @property {bool} leading
* @property {bool} trailing
*/