Commit d03ac54c authored by Valentin Hervieu's avatar Valentin Hervieu

Apply editorconfig formatting

parent a7c7f0e3
module.exports = function(grunt) {
// Project configuration.
grunt.initConfig({
// Project configuration.
grunt.initConfig({
pkg: grunt.file.readJSON('package.json'),
pkg: grunt.file.readJSON('package.json'),
minBanner: '/*! <%= pkg.name %> - v<%= pkg.version %> - ' +
'(c) <%= pkg.author %>, <%= pkg.repository.url %> - ' +
'<%= grunt.template.today("yyyy-mm-dd") %> */\n',
minBanner: '/*! <%= pkg.name %> - v<%= pkg.version %> - ' +
'(c) <%= pkg.author %>, <%= pkg.repository.url %> - ' +
'<%= grunt.template.today("yyyy-mm-dd") %> */\n',
recess: {
options: {
compile: true
},
recess: {
options: {
compile: true
},
slider: {
src: ['src/rzslider.less'],
dest: 'dist/rzslider.css'
},
slider: {
src: ['src/rzslider.less'],
dest: 'dist/rzslider.css'
},
min: {
options: {
compress: true,
banner: '<%= minBanner %>'
},
src: ['dist/rzslider.css'],
dest: 'dist/rzslider.min.css'
}
min: {
options: {
compress: true,
banner: '<%= minBanner %>'
},
src: ['dist/rzslider.css'],
dest: 'dist/rzslider.min.css'
}
},
uglify: {
options: {
report: 'min',
banner: '<%= minBanner %>'
},
rzslider: {
files: {
'dist/rzslider.min.js': [
'dist/rzslider.js'
]
}
}
},
uglify: {
options: {
report: 'min',
banner: '<%= minBanner %>'
},
rzslider: {
files: {
'dist/rzslider.min.js': [
'dist/rzslider.js'
]
}
}
},
ngtemplates: {
app: {
src: 'src/**.html',
dest: 'temp/templates.js',
options: {
htmlmin: {
collapseBooleanAttributes: true,
collapseWhitespace: true,
removeAttributeQuotes: true,
removeComments: true, // Only if you don't use comment directives!
removeEmptyAttributes: true,
removeRedundantAttributes: true,
removeScriptTypeAttributes: true,
removeStyleLinkTypeAttributes: true
},
module: 'rzModule',
url: function(url) {
return url.replace('src/', '');
},
bootstrap: function(module, script) {
return 'module.run(function($templateCache) {\n' + script + '\n});';
}
}
}
},
ngtemplates: {
app: {
src: 'src/**.html',
dest: 'temp/templates.js',
options: {
htmlmin: {
collapseBooleanAttributes: true,
collapseWhitespace: true,
removeAttributeQuotes: true,
removeComments: true, // Only if you don't use comment directives!
removeEmptyAttributes: true,
removeRedundantAttributes: true,
removeScriptTypeAttributes: true,
removeStyleLinkTypeAttributes: true
},
module: 'rzModule',
url: function(url) {
return url.replace('src/', '');
},
bootstrap: function(module, script) {
return 'module.run(function($templateCache) {\n' + script + '\n});';
}
}
}
},
replace: {
dist: {
options: {
patterns: [
{
match: /\/\*templateReplacement\*\//,
replacement: '<%= grunt.file.read("temp/templates.js") %>'
}
]
},
files: [
{expand: true, flatten: true, src: ['src/rzslider.js'], dest: 'dist/'}
]
}
replace: {
dist: {
options: {
patterns: [{
match: /\/\*templateReplacement\*\//,
replacement: '<%= grunt.file.read("temp/templates.js") %>'
}]
},
files: [{
expand: true,
flatten: true,
src: ['src/rzslider.js'],
dest: 'dist/'
}]
}
},
ngAnnotate: {
options: {
singleQuotes: true,
},
rzslider: {
files: [{
'dist/rzslider.js': 'dist/rzslider.js'
}, {
expand: true,
src: ['dist/rzslider.js']
}
]
}
},
watch: {
all: {
files: ['dist/*', 'demo/*'],
options: {
livereload: true
}
},
js: {
files: ['src/*js', 'src/*.html'],
tasks: ['js']
},
less: {
files: ['src/*.less'],
tasks: ['css']
}
},
serve: {
options: {
port: 9000
}
ngAnnotate: {
options: {
singleQuotes: true,
},
rzslider: {
files: [{
'dist/rzslider.js': 'dist/rzslider.js'
}, {
expand: true,
src: ['dist/rzslider.js']
}]
}
},
watch: {
all: {
files: ['dist/*', 'demo/*'],
options: {
livereload: true
}
},
js: {
files: ['src/*js', 'src/*.html'],
tasks: ['js']
},
less: {
files: ['src/*.less'],
tasks: ['css']
}
},
serve: {
options: {
port: 9000
}
}
});
});
grunt.loadNpmTasks('grunt-contrib-uglify');
grunt.loadNpmTasks('grunt-recess');
grunt.loadNpmTasks('grunt-angular-templates');
grunt.loadNpmTasks('grunt-replace');
grunt.loadNpmTasks('grunt-ng-annotate');
grunt.loadNpmTasks('grunt-contrib-watch');
grunt.loadNpmTasks('grunt-serve');
grunt.loadNpmTasks('grunt-contrib-uglify');
grunt.loadNpmTasks('grunt-recess');
grunt.loadNpmTasks('grunt-angular-templates');
grunt.loadNpmTasks('grunt-replace');
grunt.loadNpmTasks('grunt-ng-annotate');
grunt.loadNpmTasks('grunt-contrib-watch');
grunt.loadNpmTasks('grunt-serve');
grunt.registerTask('default', ['css', 'js']);
grunt.registerTask('default', ['css', 'js']);
grunt.registerTask('css', ['recess']);
grunt.registerTask('js', ['ngtemplates', 'replace', 'ngAnnotate', 'uglify']);
grunt.registerTask('css', ['recess']);
grunt.registerTask('js', ['ngtemplates', 'replace', 'ngAnnotate', 'uglify']);
};
......@@ -31,53 +31,53 @@
'use strict';
var module = angular.module('rzModule', [])
.factory('RzSliderOptions', function() {
var defaultOptions = {
floor: 0,
ceil: null, //defaults to rz-slider-model
step: 1,
precision: 0,
id: null,
translate: null,
stepsArray: null,
draggableRange: false,
showSelectionBar: false,
hideLimitLabels: false,
readOnly: false,
disabled: false,
interval: 350,
showTicks: false,
showTicksValues: false,
ticksValuesTooltip: null,
scale: 1,
onStart: null,
onChange: null,
onEnd: null
};
var globalOptions = {};
var factory = {};
/**
* `options({})` allows global configuration of all sliders in the
* application.
*
* var app = angular.module( 'App', ['rzModule'], function( RzSliderOptions ) {
* // show ticks for all sliders
* RzSliderOptions.options( { showTicks: true } );
* });
*/
factory.options = function(value) {
angular.extend(globalOptions, value);
};
.factory('RzSliderOptions', function() {
var defaultOptions = {
floor: 0,
ceil: null, //defaults to rz-slider-model
step: 1,
precision: 0,
id: null,
translate: null,
stepsArray: null,
draggableRange: false,
showSelectionBar: false,
hideLimitLabels: false,
readOnly: false,
disabled: false,
interval: 350,
showTicks: false,
showTicksValues: false,
ticksValuesTooltip: null,
scale: 1,
onStart: null,
onChange: null,
onEnd: null
};
var globalOptions = {};
var factory = {};
/**
* `options({})` allows global configuration of all sliders in the
* application.
*
* var app = angular.module( 'App', ['rzModule'], function( RzSliderOptions ) {
* // show ticks for all sliders
* RzSliderOptions.options( { showTicks: true } );
* });
*/
factory.options = function(value) {
angular.extend(globalOptions, value);
};
factory.getOptions = function(options) {
return angular.extend({}, defaultOptions, globalOptions, options);
};
factory.getOptions = function(options) {
return angular.extend({}, defaultOptions, globalOptions, options);
};
return factory;
})
return factory;
})
.value('rzThrottle',
.value('rzThrottle',
/**
* rzThrottle
*
......@@ -124,1242 +124,1238 @@
};
})
.factory('RzSlider', ['$timeout', '$document', '$window', '$compile', 'RzSliderOptions', 'rzThrottle', function($timeout, $document, $window, $compile, RzSliderOptions, rzThrottle) {
'use strict';
.factory('RzSlider', ['$timeout', '$document', '$window', '$compile', 'RzSliderOptions', 'rzThrottle', function($timeout, $document, $window, $compile, RzSliderOptions, rzThrottle) {
'use strict';
/**
* Slider
*
* @param {ngScope} scope The AngularJS scope
* @param {Element} sliderElem The slider directive element wrapped in jqLite
* @constructor
*/
var Slider = function(scope, sliderElem) {
/**
* Slider
* The slider's scope
*
* @param {ngScope} scope The AngularJS scope
* @param {Element} sliderElem The slider directive element wrapped in jqLite
* @constructor
* @type {ngScope}
*/
var Slider = function(scope, sliderElem) {
/**
* The slider's scope
*
* @type {ngScope}
*/
this.scope = scope;
this.scope = scope;
/**
* Slider element wrapped in jqLite
*
* @type {jqLite}
*/
this.sliderElem = sliderElem;
/**
* Slider element wrapped in jqLite
*
* @type {jqLite}
*/
this.sliderElem = sliderElem;
/**
* Slider type
*
* @type {boolean} Set to true for range slider
*/
this.range = this.scope.rzSliderModel !== undefined && this.scope.rzSliderHigh !== undefined;
/**
* Slider type
*
* @type {boolean} Set to true for range slider
*/
this.range = this.scope.rzSliderModel !== undefined && this.scope.rzSliderHigh !== undefined;
/**
* Values recorded when first dragging the bar
*
* @type {Object}
*/
this.dragging = {
active: false,
value: 0,
difference: 0,
offset: 0,
lowDist: 0,
highDist: 0
};
/**
* Values recorded when first dragging the bar
*
* @type {Object}
*/
this.dragging = {
active: false,
value: 0,
difference: 0,
offset: 0,
lowDist: 0,
highDist: 0
};
/**
* Half of the width of the slider handles
*
* @type {number}
*/
this.handleHalfWidth = 0;
/**
* 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;
/**
* Maximum left the slider handle can have
*
* @type {number}
*/
this.maxLeft = 0;
/**
* Precision
*
* @type {number}
*/
this.precision = 0;
/**
* Precision
*
* @type {number}
*/
this.precision = 0;
/**
* Step
*
* @type {number}
*/
this.step = 0;
/**
* Step
*
* @type {number}
*/
this.step = 0;
/**
* The name of the handle we are currently tracking
*
* @type {string}
*/
this.tracking = '';
/**
* The name of the handle we are currently tracking
*
* @type {string}
*/
this.tracking = '';
/**
* Minimum value (floor) of the model
*
* @type {number}
*/
this.minValue = 0;
/**
* Minimum value (floor) of the model
*
* @type {number}
*/
this.minValue = 0;
/**
* Maximum value (ceiling) of the model
*
* @type {number}
*/
this.maxValue = 0;
/**
* Maximum value (ceiling) of the model
*
* @type {number}
*/
this.maxValue = 0;
/**
* The delta between min and max value
*
* @type {number}
*/
this.valueRange = 0;
/**
* The delta between min and max value
*
* @type {number}
*/
this.valueRange = 0;
/**
* Set to true if init method already executed
*
* @type {boolean}
*/
this.initHasRun = false;
// Slider DOM elements wrapped in jqLite
this.fullBar = null; // The whole slider bar
this.selBar = null; // Highlight between two handles
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.ticks = null; // The ticks
// Initialize slider
this.init();
};
/**
* Set to true if init method already executed
*
* @type {boolean}
*/
this.initHasRun = false;
// Slider DOM elements wrapped in jqLite
this.fullBar = null; // The whole slider bar
this.selBar = null; // Highlight between two handles
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.ticks = null; // The ticks
// Initialize slider
this.init();
};
// Add instance methods
Slider.prototype = {
// Add instance methods
Slider.prototype = {
/**
* Initialize slider
*
* @returns {undefined}
*/
init: function() {
var thrLow, thrHigh,
calcDimFn = angular.bind(this, this.calcViewDimensions),
self = this;
this.applyOptions();
this.initElemHandles();
this.manageElementsStyle();
this.addAccessibility();
this.manageEventsBindings();
this.setDisabledState();
this.calcViewDimensions();
this.setMinAndMax();
$timeout(function() {
self.updateCeilLab();
self.updateFloorLab();
self.initHandles();
self.bindEvents();
});
// Recalculate slider view dimensions
this.scope.$on('reCalcViewDimensions', calcDimFn);
// Recalculate stuff if view port dimensions have changed
angular.element($window).on('resize', calcDimFn);
this.initHasRun = true;
// Watch for changes to the model
thrLow = rzThrottle(function() {
self.setMinAndMax();
self.updateLowHandle(self.valueToOffset(self.scope.rzSliderModel));
self.updateSelectionBar();
self.updateTicksScale();
if (self.range) {
self.updateCmbLabel();
}
/**
* Initialize slider
*
* @returns {undefined}
*/
init: function() {
var thrLow, thrHigh,
calcDimFn = angular.bind(this, this.calcViewDimensions),
self = this;
this.applyOptions();
this.initElemHandles();
this.manageElementsStyle();
this.addAccessibility();
this.manageEventsBindings();
this.setDisabledState();
this.calcViewDimensions();
this.setMinAndMax();
}, self.options.interval);
thrHigh = rzThrottle(function() {
self.setMinAndMax();
self.updateHighHandle(self.valueToOffset(self.scope.rzSliderHigh));
self.updateSelectionBar();
self.updateTicksScale();
self.updateCmbLabel();
}, self.options.interval);
this.scope.$on('rzSliderForceRender', function() {
self.resetLabelsValue();
thrLow();
if (self.range) {
thrHigh();
}
self.resetSlider();
});
$timeout(function() {
self.updateCeilLab();
self.updateFloorLab();
self.initHandles();
self.bindEvents();
});
// Watchers
this.scope.$watch('rzSliderModel', function(newValue, oldValue) {
if (newValue === oldValue)
return;
thrLow();
});
// Recalculate slider view dimensions
this.scope.$on('reCalcViewDimensions', calcDimFn);
this.scope.$watch('rzSliderHigh', function(newValue, oldValue) {
if (newValue === oldValue)
return;
if (newValue != null)
thrHigh();
if (self.range && newValue == null || !self.range && newValue != null) {
self.applyOptions();
self.resetSlider();
}
});
// Recalculate stuff if view port dimensions have changed
angular.element($window).on('resize', calcDimFn);
this.scope.$watch('rzSliderOptions', function(newValue, oldValue) {
if (newValue === oldValue)
return;
self.applyOptions();
self.resetSlider();
}, true);
this.initHasRun = true;
this.scope.$on('$destroy', function() {
self.unbindEvents();
angular.element($window).off('resize', calcDimFn);
});
},
// Watch for changes to the model
/**
* Read the user options and apply them to the slider model
*/
applyOptions: function() {
this.options = RzSliderOptions.getOptions(this.scope.rzSliderOptions);
thrLow = rzThrottle(function() {
self.setMinAndMax();
self.updateLowHandle(self.valueToOffset(self.scope.rzSliderModel));
self.updateSelectionBar();
self.updateTicksScale();
if (this.options.step <= 0)
this.options.step = 1;
this.range = this.scope.rzSliderModel !== undefined && this.scope.rzSliderHigh !== undefined;
this.options.draggableRange = this.range && this.options.draggableRange;
this.options.showTicks = this.options.showTicks || this.options.showTicksValues;
if (this.options.stepsArray) {
this.options.floor = 0;
this.options.ceil = this.options.stepsArray.length - 1;
this.options.step = 1;
this.customTrFn = function(value) {
return this.options.stepsArray[value];
};
} else if (this.options.translate)
this.customTrFn = this.options.translate;
else
this.customTrFn = function(value) {
return String(value);
};
},
if (self.range) {
self.updateCmbLabel();
}
/**
* Resets slider
*
* @returns {undefined}
*/
resetSlider: function() {
this.manageElementsStyle();
this.setMinAndMax();
this.updateCeilLab();
this.updateFloorLab();
this.unbindEvents();
this.manageEventsBindings();
this.setDisabledState();
this.calcViewDimensions();
},
}, self.options.interval);
/**
* Set the slider children to variables for easy access
*
* Run only once during initialization
*
* @returns {undefined}
*/
initElemHandles: function() {
// Assign all slider elements to object properties for easy access
angular.forEach(this.sliderElem.children(), function(elem, index) {
var jElem = angular.element(elem);
switch (index) {
case 0:
this.fullBar = jElem;
break;
case 1:
this.selBar = jElem;
break;
case 2:
this.minH = jElem;
break;
case 3:
this.maxH = jElem;
break;
case 4:
this.flrLab = jElem;
break;
case 5:
this.ceilLab = jElem;
break;
case 6:
this.minLab = jElem;
break;
case 7:
this.maxLab = jElem;
break;
case 8:
this.cmbLab = jElem;
break;
case 9:
this.ticks = jElem;
break;
}
thrHigh = rzThrottle(function() {
self.setMinAndMax();
self.updateHighHandle(self.valueToOffset(self.scope.rzSliderHigh));
self.updateSelectionBar();
self.updateTicksScale();
self.updateCmbLabel();
}, self.options.interval);
}, this);
this.scope.$on('rzSliderForceRender', function() {
self.resetLabelsValue();
thrLow();
if (self.range) {
thrHigh();
}
self.resetSlider();
});
// Initialize offset cache properties
this.selBar.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;
},
// Watchers
this.scope.$watch('rzSliderModel', function(newValue, oldValue) {
if (newValue === oldValue)
return;
thrLow();
});
/** Update each elements style based on options
*
*/
manageElementsStyle: function() {
if (!this.range)
this.maxH.css('display', 'none');
else
this.maxH.css('display', null);
this.alwaysHide(this.flrLab, this.options.showTicksValues || this.options.hideLimitLabels);
this.alwaysHide(this.ceilLab, this.options.showTicksValues || this.options.hideLimitLabels);
this.alwaysHide(this.minLab, this.options.showTicksValues);
this.alwaysHide(this.maxLab, this.options.showTicksValues || !this.range);
this.alwaysHide(this.cmbLab, this.options.showTicksValues || !this.range);
this.alwaysHide(this.selBar, !this.range && !this.options.showSelectionBar);
if (!this.options.showTicks)
this.ticks.html('');
if (this.options.draggableRange)
this.selBar.addClass('rz-draggable');
else
this.selBar.removeClass('rz-draggable');
},
alwaysHide: function(el, hide) {
el.rzAlwaysHide = hide;
if (hide)
this.hideEl(el);
else
this.showEl(el)
},
this.scope.$watch('rzSliderHigh', function(newValue, oldValue) {
if (newValue === oldValue)
return;
if (newValue != null)
thrHigh();
if (self.range && newValue == null || !self.range && newValue != null) {
self.applyOptions();
self.resetSlider();
}
});
/**
* Manage the events bindings based on readOnly and disabled options
*
* @returns {undefined}
*/
manageEventsBindings: function() {
if (this.options.disabled || this.options.readOnly)
this.unbindEvents();
else if (!this.options.disabled || !this.options.readOnly)
this.bindEvents();
},
this.scope.$watch('rzSliderOptions', function(newValue, oldValue) {
if (newValue === oldValue)
return;
self.applyOptions();
self.resetSlider();
}, true);
/**
* Set the disabled state based on rzSliderDisabled
*
* @returns {undefined}
*/
setDisabledState: function() {
if (this.options.disabled) {
this.sliderElem.attr('disabled', 'disabled');
} else {
this.sliderElem.attr('disabled', null);
}
},
this.scope.$on('$destroy', function() {
self.unbindEvents();
angular.element($window).off('resize', calcDimFn);
});
},
/**
* Reset label values
*
* @return {undefined}
*/
resetLabelsValue: function() {
this.minLab.rzsv = undefined;
this.maxLab.rzsv = undefined;
},
/**
* Read the user options and apply them to the slider model
*/
applyOptions: function() {
this.options = RzSliderOptions.getOptions(this.scope.rzSliderOptions);
if (this.options.step <= 0)
this.options.step = 1;
this.range = this.scope.rzSliderModel !== undefined && this.scope.rzSliderHigh !== undefined;
this.options.draggableRange = this.range && this.options.draggableRange;
this.options.showTicks = this.options.showTicks || this.options.showTicksValues;
if (this.options.stepsArray) {
this.options.floor = 0;
this.options.ceil = this.options.stepsArray.length - 1;
this.options.step = 1;
this.customTrFn = function(value) {
return this.options.stepsArray[value];
};
}
else if (this.options.translate)
this.customTrFn = this.options.translate;
else
this.customTrFn = function(value) {
return String(value);
};
},
/**
* Resets slider
*
* @returns {undefined}
*/
resetSlider: function() {
this.manageElementsStyle();
this.setMinAndMax();
this.updateCeilLab();
this.updateFloorLab();
this.unbindEvents();
this.manageEventsBindings();
this.setDisabledState();
this.calcViewDimensions();
},
/**
* Set the slider children to variables for easy access
*
* Run only once during initialization
*
* @returns {undefined}
*/
initElemHandles: function() {
// Assign all slider elements to object properties for easy access
angular.forEach(this.sliderElem.children(), function(elem, index) {
var jElem = angular.element(elem);
switch (index) {
case 0:
this.fullBar = jElem;
break;
case 1:
this.selBar = jElem;
break;
case 2:
this.minH = jElem;
break;
case 3:
this.maxH = jElem;
break;
case 4:
this.flrLab = jElem;
break;
case 5:
this.ceilLab = jElem;
break;
case 6:
this.minLab = jElem;
break;
case 7:
this.maxLab = jElem;
break;
case 8:
this.cmbLab = jElem;
break;
case 9:
this.ticks = jElem;
break;
}
/**
* 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));
}, this);
// Initialize offset cache properties
this.selBar.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;
},
/** Update each elements style based on options
*
*/
manageElementsStyle: function() {
if (!this.range)
this.maxH.css('display', 'none');
else
this.maxH.css('display', null);
this.alwaysHide(this.flrLab, this.options.showTicksValues || this.options.hideLimitLabels);
this.alwaysHide(this.ceilLab, this.options.showTicksValues || this.options.hideLimitLabels);
this.alwaysHide(this.minLab, this.options.showTicksValues);
this.alwaysHide(this.maxLab, this.options.showTicksValues || !this.range);
this.alwaysHide(this.cmbLab, this.options.showTicksValues || !this.range);
this.alwaysHide(this.selBar, !this.range && !this.options.showSelectionBar);
if (!this.options.showTicks)
this.ticks.html('');
if (this.options.draggableRange)
this.selBar.addClass('rz-draggable');
else
this.selBar.removeClass('rz-draggable');
},
alwaysHide: function(el, hide) {
el.rzAlwaysHide = hide;
if (hide)
this.hideEl(el);
else
this.showEl(el)
},
/**
* Manage the events bindings based on readOnly and disabled options
*
* @returns {undefined}
/*
the order here is important since the selection bar should be
updated after the high handle but before the combined label
*/
manageEventsBindings: function() {
if (this.options.disabled || this.options.readOnly)
this.unbindEvents();
else if (!this.options.disabled || !this.options.readOnly)
this.bindEvents();
},
/**
* Set the disabled state based on rzSliderDisabled
*
* @returns {undefined}
*/
setDisabledState: function() {
if (this.options.disabled) {
this.sliderElem.attr('disabled', 'disabled');
}
else {
this.sliderElem.attr('disabled', null);
}
},
if (this.range)
this.updateHighHandle(this.valueToOffset(this.scope.rzSliderHigh));
this.updateSelectionBar();
if (this.range)
this.updateCmbLabel();
/**
* 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));
/*
the order here is important since the selection bar should be
updated after the high handle but before the combined label
*/
if (this.range)
this.updateHighHandle(this.valueToOffset(this.scope.rzSliderHigh));
this.updateSelectionBar();
if (this.range)
this.updateCmbLabel();
this.updateTicksScale();
},
this.updateTicksScale();
},
/**
* Translate value to human readable format
*
* @param {number|string} value
* @param {jqLite} label
* @param {boolean} [useCustomTr]
* @returns {undefined}
*/
translateFn: function(value, label, useCustomTr) {
useCustomTr = useCustomTr === undefined ? true : useCustomTr;
/**
* Translate value to human readable format
*
* @param {number|string} value
* @param {jqLite} label
* @param {boolean} [useCustomTr]
* @returns {undefined}
*/
translateFn: function(value, label, useCustomTr) {
useCustomTr = useCustomTr === undefined ? true : useCustomTr;
var valStr = String((useCustomTr ? this.customTrFn(value, this.options.id) : value)),
getWidth = false;
var valStr = String((useCustomTr ? this.customTrFn(value, this.options.id) : value)),
getWidth = false;
if (label.rzsv === undefined || label.rzsv.length !== valStr.length || (label.rzsv.length > 0 && label.rzsw === 0)) {
getWidth = true;
label.rzsv = valStr;
}
if (label.rzsv === undefined || label.rzsv.length !== valStr.length || (label.rzsv.length > 0 && label.rzsw === 0)) {
getWidth = true;
label.rzsv = valStr;
}
label.text(valStr);
label.text(valStr);
// Update width only when length of the label have changed
if (getWidth) {
this.getWidth(label);
}
},
// Update width only when length of the label have changed
if (getWidth) {
this.getWidth(label);
}
},
/**
* Set maximum and minimum values for the slider and ensure the model and high
* value match these limits
* @returns {undefined}
*/
setMinAndMax: function() {
/**
* Set maximum and minimum values for the slider and ensure the model and high
* value match these limits
* @returns {undefined}
*/
setMinAndMax: function() {
this.step = +this.options.step;
this.precision = +this.options.precision;
this.step = +this.options.step;
this.precision = +this.options.precision;
this.scope.rzSliderModel = this.roundStep(this.scope.rzSliderModel);
if (this.range)
this.scope.rzSliderHigh = this.roundStep(this.scope.rzSliderHigh);
this.scope.rzSliderModel = this.roundStep(this.scope.rzSliderModel);
if (this.range)
this.scope.rzSliderHigh = this.roundStep(this.scope.rzSliderHigh);
this.minValue = this.roundStep(+this.options.floor);
this.minValue = this.roundStep(+this.options.floor);
if (this.options.ceil)
this.maxValue = this.roundStep(+this.options.ceil);
else
this.maxValue = this.options.ceil = this.range ? this.scope.rzSliderHigh : this.scope.rzSliderModel;
if (this.options.ceil)
this.maxValue = this.roundStep(+this.options.ceil);
else
this.maxValue = this.options.ceil = this.range ? this.scope.rzSliderHigh : this.scope.rzSliderModel;
this.valueRange = this.maxValue - this.minValue;
},
this.valueRange = this.maxValue - this.minValue;
},
/**
* Adds accessibility atributes
*
* Run only once during initialization
*
* @returns {undefined}
*/
addAccessibility: function() {
this.sliderElem.attr("role", "slider");
},
/**
* 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);
/**
* Adds accessibility atributes
*
* Run only once during initialization
*
* @returns {undefined}
*/
addAccessibility: function() {
this.sliderElem.attr("role", "slider");
},
this.handleHalfWidth = handleWidth / 2;
this.barWidth = this.getWidth(this.fullBar);
/**
* 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.maxLeft = this.barWidth - handleWidth;
this.handleHalfWidth = handleWidth / 2;
this.barWidth = this.getWidth(this.fullBar);
this.getWidth(this.sliderElem);
this.sliderElem.rzsl = this.sliderElem[0].getBoundingClientRect().left;
this.maxLeft = this.barWidth - handleWidth;
if (this.initHasRun) {
this.updateFloorLab();
this.updateCeilLab();
this.initHandles();
}
},
this.getWidth(this.sliderElem);
this.sliderElem.rzsl = this.sliderElem[0].getBoundingClientRect().left;
/**
* Update the ticks position
*
* @returns {undefined}
*/
updateTicksScale: function() {
if (!this.options.showTicks) return;
if (!this.step) return; //if step is 0, the following loop will be endless.
var positions = '',
ticksCount = Math.round((this.maxValue - this.minValue) / this.step) + 1;
for (var i = 0; i < ticksCount; i++) {
var value = this.roundStep(this.minValue + i * this.step);
var selectedClass = this.isTickSelected(value) ? 'selected' : '';
positions += '<li class="tick ' + selectedClass + '">';
if (this.options.showTicksValues) {
var tooltip = '';
if (this.options.ticksValuesTooltip) {
tooltip = 'uib-tooltip="' + this.options.ticksValuesTooltip(value) + '"';
}
positions += '<span ' + tooltip + ' class="tick-value">' + this.getDisplayValue(value) + '</span>';
if (this.initHasRun) {
this.updateFloorLab();
this.updateCeilLab();
this.initHandles();
}
},
/**
* Update the ticks position
*
* @returns {undefined}
*/
updateTicksScale: function() {
if (!this.options.showTicks) return;
if (!this.step) return; //if step is 0, the following loop will be endless.
var positions = '',
ticksCount = Math.round((this.maxValue - this.minValue) / this.step) + 1;
for (var i = 0; i < ticksCount; i++) {
var value = this.roundStep(this.minValue + i * this.step);
var selectedClass = this.isTickSelected(value) ? 'selected' : '';
positions += '<li class="tick ' + selectedClass + '">';
if (this.options.showTicksValues) {
var tooltip = '';
if (this.options.ticksValuesTooltip) {
tooltip = 'uib-tooltip="' + this.options.ticksValuesTooltip(value) + '"';
}
positions += '</li>';
}
this.ticks.html(positions);
if (this.options.ticksValuesTooltip)
$compile(this.ticks.contents())(this.scope);
},
isTickSelected: function(value) {
if (!this.range && this.options.showSelectionBar && value <= this.scope.rzSliderModel)
return true;
if (this.range && value >= this.scope.rzSliderModel && value <= this.scope.rzSliderHigh)
return true;
return false;
},
/**
* Update position of the ceiling label
*
* @returns {undefined}
*/
updateCeilLab: function() {
this.translateFn(this.maxValue, 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.minValue, this.flrLab);
this.getWidth(this.flrLab);
},
/**
* Call the onStart callback if defined
*
* @returns {undefined}
*/
callOnStart: function() {
if (this.options.onStart) {
var self = this;
$timeout(function() {
self.options.onStart();
});
positions += '<span ' + tooltip + ' class="tick-value">' + this.getDisplayValue(value) + '</span>';
}
},
positions += '</li>';
}
this.ticks.html(positions);
if (this.options.ticksValuesTooltip)
$compile(this.ticks.contents())(this.scope);
},
isTickSelected: function(value) {
if (!this.range && this.options.showSelectionBar && value <= this.scope.rzSliderModel)
return true;
if (this.range && value >= this.scope.rzSliderModel && value <= this.scope.rzSliderHigh)
return true;
return false;
},
/**
* Call the onChange callback if defined
*
* @returns {undefined}
*/
callOnChange: function() {
if (this.options.onChange) {
var self = this;
$timeout(function() {
self.options.onChange();
});
}
},
/**
* Update position of the ceiling label
*
* @returns {undefined}
*/
updateCeilLab: function() {
this.translateFn(this.maxValue, this.ceilLab);
this.setLeft(this.ceilLab, this.barWidth - this.ceilLab.rzsw);
this.getWidth(this.ceilLab);
},
/**
* Call the onEnd callback if defined
*
* @returns {undefined}
*/
callOnEnd: function() {
if (this.options.onEnd) {
var self = this;
$timeout(function() {
self.options.onEnd();
});
}
},
/**
* Update position of the floor label
*
* @returns {undefined}
*/
updateFloorLab: function() {
this.translateFn(this.minValue, 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);
this.updateSelectionBar();
this.updateTicksScale();
if (this.range) {
this.updateCmbLabel();
}
return;
}
/**
* Call the onStart callback if defined
*
* @returns {undefined}
*/
callOnStart: function() {
if (this.options.onStart) {
var self = this;
$timeout(function() {
self.options.onStart();
});
}
},
if (which === 'rzSliderHigh') {
this.updateHighHandle(newOffset);
this.updateSelectionBar();
this.updateTicksScale();
/**
* Call the onChange callback if defined
*
* @returns {undefined}
*/
callOnChange: function() {
if (this.options.onChange) {
var self = this;
$timeout(function() {
self.options.onChange();
});
}
},
if (this.range) {
this.updateCmbLabel();
}
return;
}
/**
* Call the onEnd callback if defined
*
* @returns {undefined}
*/
callOnEnd: function() {
if (this.options.onEnd) {
var self = this;
$timeout(function() {
self.options.onEnd();
});
}
},
// Update both
/**
* Update slider handles and label positions
*
* @param {string} which
* @param {number} newOffset
*/
updateHandles: function(which, newOffset) {
if (which === 'rzSliderModel') {
this.updateLowHandle(newOffset);
this.updateHighHandle(newOffset);
this.updateSelectionBar();
this.updateTicksScale();
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);
if (this.range) {
this.updateCmbLabel();
}
else {
flHidden = false;
this.showEl(this.flrLab);
return;
}
if (which === 'rzSliderHigh') {
this.updateHighHandle(newOffset);
this.updateSelectionBar();
this.updateTicksScale();
if (this.range) {
this.updateCmbLabel();
}
return;
}
// Update both
this.updateLowHandle(newOffset);
this.updateHighHandle(newOffset);
this.updateSelectionBar();
this.updateTicksScale();
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.minLab.rzsl + this.minLab.rzsw >= this.ceilLab.rzsl - this.handleHalfWidth - 10) {
clHidden = true;
if (this.range) {
if (this.maxLab.rzsl + this.maxLab.rzsw >= this.ceilLab.rzsl - 10) {
this.hideEl(this.ceilLab);
}
else {
clHidden = false;
} else if (!clHidden) {
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);
}
// 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, Math.abs(this.maxH.rzsl - this.minH.rzsl) + this.handleHalfWidth);
this.setLeft(this.selBar, this.range ? this.minH.rzsl + this.handleHalfWidth : 0);
},
/**
* Update combined label position and value
*
* @returns {undefined}
*/
updateCmbLabel: function() {
var lowTr, highTr;
if (this.minLab.rzsl + this.minLab.rzsw + 10 >= this.maxLab.rzsl) {
lowTr = this.getDisplayValue(this.scope.rzSliderModel);
highTr = this.getDisplayValue(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);
}
},
/**
* Update slider selection bar, combined label and range label
*
* @returns {undefined}
*/
updateSelectionBar: function() {
this.setWidth(this.selBar, Math.abs(this.maxH.rzsl - this.minH.rzsl) + this.handleHalfWidth);
this.setLeft(this.selBar, this.range ? this.minH.rzsl + this.handleHalfWidth : 0);
},
/**
* Return the translated value if a translate function is provided else the original value
* @param value
* @returns {*}
*/
getDisplayValue: function(value) {
return this.customTrFn(value, this.options.id);
},
/**
* Round value to step and precision
*
* @param {number} value
* @returns {number}
*/
roundStep: function(value) {
var step = this.step,
remainder = +((value - this.minValue) % step).toFixed(3),
steppedValue = remainder > (step / 2) ? value + step - remainder : value - remainder;
steppedValue = steppedValue.toFixed(this.precision);
return +steppedValue;
},
/**
* Hide element
*
* @param element
* @returns {jqLite} The jqLite wrapped DOM element
*/
hideEl: function(element) {
return element.css({opacity: 0});
},
/**
* Show element
*
* @param element The jqLite wrapped DOM element
* @returns {jqLite} The jqLite
*/
showEl: function(element) {
if (!!element.rzAlwaysHide) {
return element;
}
/**
* Update combined label position and value
*
* @returns {undefined}
*/
updateCmbLabel: function() {
var lowTr, highTr;
if (this.minLab.rzsl + this.minLab.rzsw + 10 >= this.maxLab.rzsl) {
lowTr = this.getDisplayValue(this.scope.rzSliderModel);
highTr = this.getDisplayValue(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);
}
},
return element.css({opacity: 1});
},
/**
* Return the translated value if a translate function is provided else the original value
* @param value
* @returns {*}
*/
getDisplayValue: function(value) {
return this.customTrFn(value, this.options.id);
},
/**
* 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) * this.options.scale;
return elem.rzsw;
},
/**
* Set element width
*
* @param {jqLite} elem The jqLite wrapped DOM element
* @param {number} width
* @returns {number}
*/
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 (this.sanitizeOffsetValue(val) - this.minValue) * this.maxLeft / this.valueRange || 0;
},
/**
* Ensure that the position rendered is within the slider bounds, even if the value is not
*
* @param {number} val
* @returns {number}
*/
sanitizeOffsetValue: function(val) {
return Math.min(Math.max(val, this.minValue), this.maxValue);
},
/**
* Translate offset to model value
*
* @param {number} offset
* @returns {number}
*/
offsetToValue: function(offset) {
return (offset / this.maxLeft) * this.valueRange + this.minValue;
},
/**
* Round value to step and precision
*
* @param {number} value
* @returns {number}
*/
roundStep: function(value) {
var step = this.step,
remainder = +((value - this.minValue) % step).toFixed(3),
steppedValue = remainder > (step / 2) ? value + step - remainder : value - remainder;
// Events
steppedValue = steppedValue.toFixed(this.precision);
return +steppedValue;
},
/**
* Get the X-coordinate of an event
*
* @param {Object} event The event
* @returns {number}
*/
getEventX: function(event) {
/* http://stackoverflow.com/a/12336075/282882 */
//noinspection JSLint
if ('clientX' in event) {
return event.clientX;
}
/**
* Hide element
*
* @param element
* @returns {jqLite} The jqLite wrapped DOM element
*/
hideEl: function(element) {
return element.css({
opacity: 0
});
},
return event.originalEvent === undefined ?
event.touches[0].clientX
: event.originalEvent.touches[0].clientX;
},
/**
* Show element
*
* @param element The jqLite wrapped DOM element
* @returns {jqLite} The jqLite
*/
showEl: function(element) {
if (!!element.rzAlwaysHide) {
return element;
}
/**
* Get the handle closest to an event.
*
* @param event {Event} The event
* @returns {jqLite} The handle closest to the event.
*/
getNearestHandle: function(event) {
if (!this.range) {
return this.minH;
}
var offset = (this.getEventX(event) - this.sliderElem.rzsl - this.handleHalfWidth) * this.options.scale;
return Math.abs(offset - this.minH.rzsl) < Math.abs(offset - this.maxH.rzsl) ? this.minH : this.maxH;
},
/**
* Bind mouse and touch events to slider handles
*
* @returns {undefined}
*/
bindEvents: function() {
if (this.options.readOnly || this.options.disabled) return;
var barTracking, barStart, barMove;
if (this.options.draggableRange) {
barTracking = 'rzSliderDrag';
barStart = this.onDragStart;
barMove = this.onDragMove;
}
else {
barTracking = 'rzSliderModel';
barStart = this.onStart;
barMove = this.onMove;
}
return element.css({
opacity: 1
});
},
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.fullBar.on('mousedown', angular.bind(this, this.onStart, null, null));
this.fullBar.on('mousedown', angular.bind(this, this.onMove, this.fullBar));
this.selBar.on('mousedown', angular.bind(this, barStart, null, barTracking));
this.selBar.on('mousedown', angular.bind(this, barMove, this.selBar));
this.ticks.on('mousedown', angular.bind(this, this.onStart, null, null));
this.ticks.on('mousedown', angular.bind(this, this.onMove, this.ticks));
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'));
}
this.fullBar.on('touchstart', angular.bind(this, this.onStart, null, null));
this.fullBar.on('touchstart', angular.bind(this, this.onMove, this.fullBar));
this.selBar.on('touchstart', angular.bind(this, barStart, null, barTracking));
this.selBar.on('touchstart', angular.bind(this, barMove, this.selBar));
this.ticks.on('touchstart', angular.bind(this, this.onStart, null, null));
this.ticks.on('touchstart', angular.bind(this, this.onMove, this.ticks));
},
/**
* Unbind mouse and touch events to slider handles
*
* @returns {undefined}
*/
unbindEvents: function() {
this.minH.off();
this.maxH.off();
this.fullBar.off();
this.selBar.off();
this.ticks.off();
},
/**
* onStart event handler
*
* @param {?Object} pointer The jqLite wrapped DOM element; if null, the closest handle is used
* @param {?string} ref The name of the handle being changed; if null, the closest handle's value is modified
* @param {Event} event The event
* @returns {undefined}
*/
onStart: function(pointer, ref, event) {
var ehMove, ehEnd,
eventNames = this.getEventNames(event);
/**
* 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;
},
event.stopPropagation();
event.preventDefault();
/**
* 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) * this.options.scale;
return elem.rzsw;
},
if (this.tracking !== '') {
return;
}
/**
* Set element width
*
* @param {jqLite} elem The jqLite wrapped DOM element
* @param {number} width
* @returns {number}
*/
setWidth: function(elem, width) {
elem.rzsw = width;
elem.css({
width: width + 'px'
});
return width;
},
// We have to do this in case the HTML where the sliders are on
// have been animated into view.
this.calcViewDimensions();
/**
* Translate value to pixel offset
*
* @param {number} val
* @returns {number}
*/
valueToOffset: function(val) {
return (this.sanitizeOffsetValue(val) - this.minValue) * this.maxLeft / this.valueRange || 0;
},
if (pointer) {
this.tracking = ref;
}
else {
pointer = this.getNearestHandle(event);
this.tracking = pointer === this.minH ? 'rzSliderModel' : 'rzSliderHigh';
}
/**
* Ensure that the position rendered is within the slider bounds, even if the value is not
*
* @param {number} val
* @returns {number}
*/
sanitizeOffsetValue: function(val) {
return Math.min(Math.max(val, this.minValue), this.maxValue);
},
pointer.addClass('rz-active');
/**
* Translate offset to model value
*
* @param {number} offset
* @returns {number}
*/
offsetToValue: function(offset) {
return (offset / this.maxLeft) * this.valueRange + this.minValue;
},
ehMove = angular.bind(this, this.dragging.active ? this.onDragMove : this.onMove, pointer);
ehEnd = angular.bind(this, this.onEnd, ehMove);
// Events
$document.on(eventNames.moveEvent, ehMove);
$document.one(eventNames.endEvent, ehEnd);
this.callOnStart();
},
/**
* Get the X-coordinate of an event
*
* @param {Object} event The event
* @returns {number}
*/
getEventX: function(event) {
/* http://stackoverflow.com/a/12336075/282882 */
//noinspection JSLint
if ('clientX' in event) {
return event.clientX;
}
/**
* onMove event handler
*
* @param {jqLite} pointer
* @param {Event} event The event
* @returns {undefined}
*/
onMove: function(pointer, event) {
var eventX = this.getEventX(event),
sliderLO, newOffset, newValue;
sliderLO = this.sliderElem.rzsl;
newOffset = (eventX - sliderLO - this.handleHalfWidth) * this.options.scale;
if (newOffset <= 0) {
if (pointer.rzsl === 0)
return;
newValue = this.minValue;
newOffset = 0;
}
else if (newOffset >= this.maxLeft) {
if (pointer.rzsl === this.maxLeft)
return;
newValue = this.maxValue;
newOffset = this.maxLeft;
}
else {
newValue = this.offsetToValue(newOffset);
newValue = this.roundStep(newValue);
newOffset = this.valueToOffset(newValue);
}
this.positionTrackingHandle(newValue, newOffset);
},
/**
* onDragStart event handler
*
* Handles dragging of the middle bar.
*
* @param {Object} pointer The jqLite wrapped DOM element
* @param {string} ref One of the refLow, refHigh values
* @param {Event} event The event
* @returns {undefined}
*/
onDragStart: function(pointer, ref, event) {
var offset = this.getEventX(event) - this.sliderElem.rzsl - this.handleHalfWidth;
this.dragging = {
active: true,
value: this.offsetToValue(offset),
difference: this.scope.rzSliderHigh - this.scope.rzSliderModel,
offset: offset,
lowDist: offset - this.minH.rzsl,
highDist: this.maxH.rzsl - offset
};
this.minH.addClass('rz-active');
this.maxH.addClass('rz-active');
this.onStart(pointer, ref, event);
},
/**
* onDragMove event handler
*
* Handles dragging of the middle bar.
*
* @param {jqLite} pointer
* @param {Event} event The event
* @returns {undefined}
*/
onDragMove: function(pointer, event) {
var newOffset = this.getEventX(event) - this.sliderElem.rzsl - this.handleHalfWidth,
newMinOffset, newMaxOffset,
newMinValue, newMaxValue;
if (newOffset <= this.dragging.lowDist) {
if (pointer.rzsl === this.dragging.lowDist) {
return;
}
newMinValue = this.minValue;
newMinOffset = 0;
newMaxValue = this.minValue + this.dragging.difference;
newMaxOffset = this.valueToOffset(newMaxValue);
}
else if (newOffset >= this.maxLeft - this.dragging.highDist) {
if (pointer.rzsl === this.dragging.highDist) {
return;
}
newMaxValue = this.maxValue;
newMaxOffset = this.maxLeft;
newMinValue = this.maxValue - this.dragging.difference;
newMinOffset = this.valueToOffset(newMinValue);
}
else {
newMinValue = this.offsetToValue(newOffset - this.dragging.lowDist);
newMinValue = this.roundStep(newMinValue);
newMinOffset = this.valueToOffset(newMinValue);
newMaxValue = newMinValue + this.dragging.difference;
newMaxOffset = this.valueToOffset(newMaxValue);
}
return event.originalEvent === undefined ?
event.touches[0].clientX : event.originalEvent.touches[0].clientX;
},
this.positionTrackingBar(newMinValue, newMaxValue, newMinOffset, newMaxOffset);
},
/**
* Get the handle closest to an event.
*
* @param event {Event} The event
* @returns {jqLite} The handle closest to the event.
*/
getNearestHandle: function(event) {
if (!this.range) {
return this.minH;
}
var offset = (this.getEventX(event) - this.sliderElem.rzsl - this.handleHalfWidth) * this.options.scale;
return Math.abs(offset - this.minH.rzsl) < Math.abs(offset - this.maxH.rzsl) ? this.minH : this.maxH;
},
/**
* Set the new value and offset for the entire bar
*
* @param {number} newMinValue the new minimum value
* @param {number} newMaxValue the new maximum value
* @param {number} newMinOffset the new minimum offset
* @param {number} newMaxOffset the new maximum offset
*/
positionTrackingBar: function(newMinValue, newMaxValue, newMinOffset, newMaxOffset) {
this.scope.rzSliderModel = newMinValue;
this.scope.rzSliderHigh = newMaxValue;
this.updateHandles('rzSliderModel', newMinOffset);
this.updateHandles('rzSliderHigh', newMaxOffset);
this.scope.$apply();
this.callOnChange();
},
/**
* Bind mouse and touch events to slider handles
*
* @returns {undefined}
*/
bindEvents: function() {
if (this.options.readOnly || this.options.disabled) return;
var barTracking, barStart, barMove;
if (this.options.draggableRange) {
barTracking = 'rzSliderDrag';
barStart = this.onDragStart;
barMove = this.onDragMove;
} else {
barTracking = 'rzSliderModel';
barStart = this.onStart;
barMove = this.onMove;
}
/**
* Set the new value and offset to the current tracking handle
*
* @param {number} newValue new model value
* @param {number} newOffset new offset value
*/
positionTrackingHandle: function(newValue, newOffset) {
if (this.range) {
/* This is to check if we need to switch the min and max handles*/
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('rz-active');
this.maxH.addClass('rz-active');
/* We need to apply here because we are not sure that we will enter the next block */
this.scope.$apply();
this.callOnChange();
}
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('rz-active');
this.minH.addClass('rz-active');
/* We need to apply here because we are not sure that we will enter the next block */
this.scope.$apply();
this.callOnChange();
}
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.fullBar.on('mousedown', angular.bind(this, this.onStart, null, null));
this.fullBar.on('mousedown', angular.bind(this, this.onMove, this.fullBar));
this.selBar.on('mousedown', angular.bind(this, barStart, null, barTracking));
this.selBar.on('mousedown', angular.bind(this, barMove, this.selBar));
this.ticks.on('mousedown', angular.bind(this, this.onStart, null, null));
this.ticks.on('mousedown', angular.bind(this, this.onMove, this.ticks));
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'));
}
this.fullBar.on('touchstart', angular.bind(this, this.onStart, null, null));
this.fullBar.on('touchstart', angular.bind(this, this.onMove, this.fullBar));
this.selBar.on('touchstart', angular.bind(this, barStart, null, barTracking));
this.selBar.on('touchstart', angular.bind(this, barMove, this.selBar));
this.ticks.on('touchstart', angular.bind(this, this.onStart, null, null));
this.ticks.on('touchstart', angular.bind(this, this.onMove, this.ticks));
},
/**
* Unbind mouse and touch events to slider handles
*
* @returns {undefined}
*/
unbindEvents: function() {
this.minH.off();
this.maxH.off();
this.fullBar.off();
this.selBar.off();
this.ticks.off();
},
/**
* onStart event handler
*
* @param {?Object} pointer The jqLite wrapped DOM element; if null, the closest handle is used
* @param {?string} ref The name of the handle being changed; if null, the closest handle's value is modified
* @param {Event} event The event
* @returns {undefined}
*/
onStart: function(pointer, ref, event) {
var ehMove, ehEnd,
eventNames = this.getEventNames(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();
if (pointer) {
this.tracking = ref;
} else {
pointer = this.getNearestHandle(event);
this.tracking = pointer === this.minH ? 'rzSliderModel' : 'rzSliderHigh';
}
pointer.addClass('rz-active');
ehMove = angular.bind(this, this.dragging.active ? this.onDragMove : this.onMove, pointer);
ehEnd = angular.bind(this, this.onEnd, ehMove);
$document.on(eventNames.moveEvent, ehMove);
$document.one(eventNames.endEvent, ehEnd);
this.callOnStart();
},
/**
* onMove event handler
*
* @param {jqLite} pointer
* @param {Event} event The event
* @returns {undefined}
*/
onMove: function(pointer, event) {
var eventX = this.getEventX(event),
sliderLO, newOffset, newValue;
sliderLO = this.sliderElem.rzsl;
newOffset = (eventX - sliderLO - this.handleHalfWidth) * this.options.scale;
if (newOffset <= 0) {
if (pointer.rzsl === 0)
return;
newValue = this.minValue;
newOffset = 0;
} else if (newOffset >= this.maxLeft) {
if (pointer.rzsl === this.maxLeft)
return;
newValue = this.maxValue;
newOffset = this.maxLeft;
} else {
newValue = this.offsetToValue(newOffset);
newValue = this.roundStep(newValue);
newOffset = this.valueToOffset(newValue);
}
this.positionTrackingHandle(newValue, newOffset);
},
/**
* onDragStart event handler
*
* Handles dragging of the middle bar.
*
* @param {Object} pointer The jqLite wrapped DOM element
* @param {string} ref One of the refLow, refHigh values
* @param {Event} event The event
* @returns {undefined}
*/
onDragStart: function(pointer, ref, event) {
var offset = this.getEventX(event) - this.sliderElem.rzsl - this.handleHalfWidth;
this.dragging = {
active: true,
value: this.offsetToValue(offset),
difference: this.scope.rzSliderHigh - this.scope.rzSliderModel,
offset: offset,
lowDist: offset - this.minH.rzsl,
highDist: this.maxH.rzsl - offset
};
this.minH.addClass('rz-active');
this.maxH.addClass('rz-active');
this.onStart(pointer, ref, event);
},
/**
* onDragMove event handler
*
* Handles dragging of the middle bar.
*
* @param {jqLite} pointer
* @param {Event} event The event
* @returns {undefined}
*/
onDragMove: function(pointer, event) {
var newOffset = this.getEventX(event) - this.sliderElem.rzsl - this.handleHalfWidth,
newMinOffset, newMaxOffset,
newMinValue, newMaxValue;
if (newOffset <= this.dragging.lowDist) {
if (pointer.rzsl === this.dragging.lowDist) {
return;
}
newMinValue = this.minValue;
newMinOffset = 0;
newMaxValue = this.minValue + this.dragging.difference;
newMaxOffset = this.valueToOffset(newMaxValue);
} else if (newOffset >= this.maxLeft - this.dragging.highDist) {
if (pointer.rzsl === this.dragging.highDist) {
return;
}
newMaxValue = this.maxValue;
newMaxOffset = this.maxLeft;
newMinValue = this.maxValue - this.dragging.difference;
newMinOffset = this.valueToOffset(newMinValue);
} else {
newMinValue = this.offsetToValue(newOffset - this.dragging.lowDist);
newMinValue = this.roundStep(newMinValue);
newMinOffset = this.valueToOffset(newMinValue);
newMaxValue = newMinValue + this.dragging.difference;
newMaxOffset = this.valueToOffset(newMaxValue);
}
this.positionTrackingBar(newMinValue, newMaxValue, newMinOffset, newMaxOffset);
},
/**
* Set the new value and offset for the entire bar
*
* @param {number} newMinValue the new minimum value
* @param {number} newMaxValue the new maximum value
* @param {number} newMinOffset the new minimum offset
* @param {number} newMaxOffset the new maximum offset
*/
positionTrackingBar: function(newMinValue, newMaxValue, newMinOffset, newMaxOffset) {
this.scope.rzSliderModel = newMinValue;
this.scope.rzSliderHigh = newMaxValue;
this.updateHandles('rzSliderModel', newMinOffset);
this.updateHandles('rzSliderHigh', newMaxOffset);
this.scope.$apply();
this.callOnChange();
},
if (this.scope[this.tracking] !== newValue) {
this.scope[this.tracking] = newValue;
this.updateHandles(this.tracking, newOffset);
/**
* Set the new value and offset to the current tracking handle
*
* @param {number} newValue new model value
* @param {number} newOffset new offset value
*/
positionTrackingHandle: function(newValue, newOffset) {
if (this.range) {
/* This is to check if we need to switch the min and max handles*/
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('rz-active');
this.maxH.addClass('rz-active');
/* We need to apply here because we are not sure that we will enter the next block */
this.scope.$apply();
this.callOnChange();
} 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('rz-active');
this.minH.addClass('rz-active');
/* We need to apply here because we are not sure that we will enter the next block */
this.scope.$apply();
this.callOnChange();
}
},
/**
* onEnd event handler
*
* @param {Event} event The event
* @param {Function} ehMove The the bound move event handler
* @returns {undefined}
*/
onEnd: function(ehMove, event) {
var moveEventName = this.getEventNames(event).moveEvent;
}
this.minH.removeClass('rz-active');
this.maxH.removeClass('rz-active');
if (this.scope[this.tracking] !== newValue) {
this.scope[this.tracking] = newValue;
this.updateHandles(this.tracking, newOffset);
this.scope.$apply();
this.callOnChange();
}
},
$document.off(moveEventName, ehMove);
/**
* onEnd event handler
*
* @param {Event} event The event
* @param {Function} ehMove The the bound move event handler
* @returns {undefined}
*/
onEnd: function(ehMove, event) {
var moveEventName = this.getEventNames(event).moveEvent;
this.scope.$emit('slideEnded');
this.tracking = '';
this.minH.removeClass('rz-active');
this.maxH.removeClass('rz-active');
this.dragging.active = false;
this.callOnEnd();
},
$document.off(moveEventName, ehMove);
/**
* Get event names for move and event end
*
* @param {Event} event The event
*
* @return {{moveEvent: string, endEvent: string}}
*/
getEventNames: function(event) {
var eventNames = {moveEvent: '', endEvent: ''};
this.scope.$emit('slideEnded');
this.tracking = '';
if (event.touches || (event.originalEvent !== undefined && event.originalEvent.touches)) {
eventNames.moveEvent = 'touchmove';
eventNames.endEvent = 'touchend';
}
else {
eventNames.moveEvent = 'mousemove';
eventNames.endEvent = 'mouseup';
}
this.dragging.active = false;
this.callOnEnd();
},
return eventNames;
/**
* Get event names for move and event end
*
* @param {Event} event The event
*
* @return {{moveEvent: string, endEvent: string}}
*/
getEventNames: function(event) {
var eventNames = {
moveEvent: '',
endEvent: ''
};
if (event.touches || (event.originalEvent !== undefined && event.originalEvent.touches)) {
eventNames.moveEvent = 'touchmove';
eventNames.endEvent = 'touchend';
} else {
eventNames.moveEvent = 'mousemove';
eventNames.endEvent = 'mouseup';
}
};
return Slider;
}])
return eventNames;
}
};
.directive('rzslider', ['RzSlider', function(RzSlider) {
'use strict';
return Slider;
}])
return {
restrict: 'E',
scope: {
rzSliderModel: '=?',
rzSliderHigh: '=?',
rzSliderOptions: '=?',
rzSliderTplUrl: '@'
},
/**
* Return template URL
*
* @param {jqLite} elem
* @param {Object} attrs
* @return {string}
*/
templateUrl: function(elem, attrs) {
//noinspection JSUnresolvedVariable
return attrs.rzSliderTplUrl || 'rzSliderTpl.html';
},
.directive('rzslider', ['RzSlider', function(RzSlider) {
'use strict';
link: function(scope, elem) {
return new RzSlider(scope, elem);
}
};
}]);
return {
restrict: 'E',
scope: {
rzSliderModel: '=?',
rzSliderHigh: '=?',
rzSliderOptions: '=?',
rzSliderTplUrl: '@'
},
// IDE assist
/**
* Return template URL
*
* @param {jqLite} elem
* @param {Object} attrs
* @return {string}
*/
templateUrl: function(elem, attrs) {
//noinspection JSUnresolvedVariable
return attrs.rzSliderTplUrl || 'rzSliderTpl.html';
},
link: function(scope, elem) {
return new RzSlider(scope, elem);
}
};
}]);
// IDE assist
/**
* @name ngScope
......
......@@ -31,53 +31,53 @@
'use strict';
var module = angular.module('rzModule', [])
.factory('RzSliderOptions', function() {
var defaultOptions = {
floor: 0,
ceil: null, //defaults to rz-slider-model
step: 1,
precision: 0,
id: null,
translate: null,
stepsArray: null,
draggableRange: false,
showSelectionBar: false,
hideLimitLabels: false,
readOnly: false,
disabled: false,
interval: 350,
showTicks: false,
showTicksValues: false,
ticksValuesTooltip: null,
scale: 1,
onStart: null,
onChange: null,
onEnd: null
};
var globalOptions = {};
var factory = {};
/**
* `options({})` allows global configuration of all sliders in the
* application.
*
* var app = angular.module( 'App', ['rzModule'], function( RzSliderOptions ) {
* // show ticks for all sliders
* RzSliderOptions.options( { showTicks: true } );
* });
*/
factory.options = function(value) {
angular.extend(globalOptions, value);
};
.factory('RzSliderOptions', function() {
var defaultOptions = {
floor: 0,
ceil: null, //defaults to rz-slider-model
step: 1,
precision: 0,
id: null,
translate: null,
stepsArray: null,
draggableRange: false,
showSelectionBar: false,
hideLimitLabels: false,
readOnly: false,
disabled: false,
interval: 350,
showTicks: false,
showTicksValues: false,
ticksValuesTooltip: null,
scale: 1,
onStart: null,
onChange: null,
onEnd: null
};
var globalOptions = {};
var factory = {};
/**
* `options({})` allows global configuration of all sliders in the
* application.
*
* var app = angular.module( 'App', ['rzModule'], function( RzSliderOptions ) {
* // show ticks for all sliders
* RzSliderOptions.options( { showTicks: true } );
* });
*/
factory.options = function(value) {
angular.extend(globalOptions, value);
};
factory.getOptions = function(options) {
return angular.extend({}, defaultOptions, globalOptions, options);
};
factory.getOptions = function(options) {
return angular.extend({}, defaultOptions, globalOptions, options);
};
return factory;
})
return factory;
})
.value('rzThrottle',
.value('rzThrottle',
/**
* rzThrottle
*
......@@ -124,1242 +124,1238 @@
};
})
.factory('RzSlider', function($timeout, $document, $window, $compile, RzSliderOptions, rzThrottle) {
'use strict';
.factory('RzSlider', function($timeout, $document, $window, $compile, RzSliderOptions, rzThrottle) {
'use strict';
/**
* Slider
*
* @param {ngScope} scope The AngularJS scope
* @param {Element} sliderElem The slider directive element wrapped in jqLite
* @constructor
*/
var Slider = function(scope, sliderElem) {
/**
* Slider
* The slider's scope
*
* @param {ngScope} scope The AngularJS scope
* @param {Element} sliderElem The slider directive element wrapped in jqLite
* @constructor
* @type {ngScope}
*/
var Slider = function(scope, sliderElem) {
/**
* The slider's scope
*
* @type {ngScope}
*/
this.scope = scope;
this.scope = scope;
/**
* Slider element wrapped in jqLite
*
* @type {jqLite}
*/
this.sliderElem = sliderElem;
/**
* Slider element wrapped in jqLite
*
* @type {jqLite}
*/
this.sliderElem = sliderElem;
/**
* Slider type
*
* @type {boolean} Set to true for range slider
*/
this.range = this.scope.rzSliderModel !== undefined && this.scope.rzSliderHigh !== undefined;
/**
* Slider type
*
* @type {boolean} Set to true for range slider
*/
this.range = this.scope.rzSliderModel !== undefined && this.scope.rzSliderHigh !== undefined;
/**
* Values recorded when first dragging the bar
*
* @type {Object}
*/
this.dragging = {
active: false,
value: 0,
difference: 0,
offset: 0,
lowDist: 0,
highDist: 0
};
/**
* Values recorded when first dragging the bar
*
* @type {Object}
*/
this.dragging = {
active: false,
value: 0,
difference: 0,
offset: 0,
lowDist: 0,
highDist: 0
};
/**
* Half of the width of the slider handles
*
* @type {number}
*/
this.handleHalfWidth = 0;
/**
* 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;
/**
* Maximum left the slider handle can have
*
* @type {number}
*/
this.maxLeft = 0;
/**
* Precision
*
* @type {number}
*/
this.precision = 0;
/**
* Precision
*
* @type {number}
*/
this.precision = 0;
/**
* Step
*
* @type {number}
*/
this.step = 0;
/**
* Step
*
* @type {number}
*/
this.step = 0;
/**
* The name of the handle we are currently tracking
*
* @type {string}
*/
this.tracking = '';
/**
* The name of the handle we are currently tracking
*
* @type {string}
*/
this.tracking = '';
/**
* Minimum value (floor) of the model
*
* @type {number}
*/
this.minValue = 0;
/**
* Minimum value (floor) of the model
*
* @type {number}
*/
this.minValue = 0;
/**
* Maximum value (ceiling) of the model
*
* @type {number}
*/
this.maxValue = 0;
/**
* Maximum value (ceiling) of the model
*
* @type {number}
*/
this.maxValue = 0;
/**
* The delta between min and max value
*
* @type {number}
*/
this.valueRange = 0;
/**
* The delta between min and max value
*
* @type {number}
*/
this.valueRange = 0;
/**
* Set to true if init method already executed
*
* @type {boolean}
*/
this.initHasRun = false;
// Slider DOM elements wrapped in jqLite
this.fullBar = null; // The whole slider bar
this.selBar = null; // Highlight between two handles
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.ticks = null; // The ticks
// Initialize slider
this.init();
};
/**
* Set to true if init method already executed
*
* @type {boolean}
*/
this.initHasRun = false;
// Slider DOM elements wrapped in jqLite
this.fullBar = null; // The whole slider bar
this.selBar = null; // Highlight between two handles
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.ticks = null; // The ticks
// Initialize slider
this.init();
};
// Add instance methods
Slider.prototype = {
// Add instance methods
Slider.prototype = {
/**
* Initialize slider
*
* @returns {undefined}
*/
init: function() {
var thrLow, thrHigh,
calcDimFn = angular.bind(this, this.calcViewDimensions),
self = this;
this.applyOptions();
this.initElemHandles();
this.manageElementsStyle();
this.addAccessibility();
this.manageEventsBindings();
this.setDisabledState();
this.calcViewDimensions();
this.setMinAndMax();
$timeout(function() {
self.updateCeilLab();
self.updateFloorLab();
self.initHandles();
self.bindEvents();
});
// Recalculate slider view dimensions
this.scope.$on('reCalcViewDimensions', calcDimFn);
// Recalculate stuff if view port dimensions have changed
angular.element($window).on('resize', calcDimFn);
this.initHasRun = true;
// Watch for changes to the model
thrLow = rzThrottle(function() {
self.setMinAndMax();
self.updateLowHandle(self.valueToOffset(self.scope.rzSliderModel));
self.updateSelectionBar();
self.updateTicksScale();
if (self.range) {
self.updateCmbLabel();
}
/**
* Initialize slider
*
* @returns {undefined}
*/
init: function() {
var thrLow, thrHigh,
calcDimFn = angular.bind(this, this.calcViewDimensions),
self = this;
this.applyOptions();
this.initElemHandles();
this.manageElementsStyle();
this.addAccessibility();
this.manageEventsBindings();
this.setDisabledState();
this.calcViewDimensions();
this.setMinAndMax();
}, self.options.interval);
thrHigh = rzThrottle(function() {
self.setMinAndMax();
self.updateHighHandle(self.valueToOffset(self.scope.rzSliderHigh));
self.updateSelectionBar();
self.updateTicksScale();
self.updateCmbLabel();
}, self.options.interval);
this.scope.$on('rzSliderForceRender', function() {
self.resetLabelsValue();
thrLow();
if (self.range) {
thrHigh();
}
self.resetSlider();
});
$timeout(function() {
self.updateCeilLab();
self.updateFloorLab();
self.initHandles();
self.bindEvents();
});
// Watchers
this.scope.$watch('rzSliderModel', function(newValue, oldValue) {
if (newValue === oldValue)
return;
thrLow();
});
// Recalculate slider view dimensions
this.scope.$on('reCalcViewDimensions', calcDimFn);
this.scope.$watch('rzSliderHigh', function(newValue, oldValue) {
if (newValue === oldValue)
return;
if (newValue != null)
thrHigh();
if (self.range && newValue == null || !self.range && newValue != null) {
self.applyOptions();
self.resetSlider();
}
});
// Recalculate stuff if view port dimensions have changed
angular.element($window).on('resize', calcDimFn);
this.scope.$watch('rzSliderOptions', function(newValue, oldValue) {
if (newValue === oldValue)
return;
self.applyOptions();
self.resetSlider();
}, true);
this.initHasRun = true;
this.scope.$on('$destroy', function() {
self.unbindEvents();
angular.element($window).off('resize', calcDimFn);
});
},
// Watch for changes to the model
/**
* Read the user options and apply them to the slider model
*/
applyOptions: function() {
this.options = RzSliderOptions.getOptions(this.scope.rzSliderOptions);
thrLow = rzThrottle(function() {
self.setMinAndMax();
self.updateLowHandle(self.valueToOffset(self.scope.rzSliderModel));
self.updateSelectionBar();
self.updateTicksScale();
if (this.options.step <= 0)
this.options.step = 1;
this.range = this.scope.rzSliderModel !== undefined && this.scope.rzSliderHigh !== undefined;
this.options.draggableRange = this.range && this.options.draggableRange;
this.options.showTicks = this.options.showTicks || this.options.showTicksValues;
if (this.options.stepsArray) {
this.options.floor = 0;
this.options.ceil = this.options.stepsArray.length - 1;
this.options.step = 1;
this.customTrFn = function(value) {
return this.options.stepsArray[value];
};
} else if (this.options.translate)
this.customTrFn = this.options.translate;
else
this.customTrFn = function(value) {
return String(value);
};
},
if (self.range) {
self.updateCmbLabel();
}
/**
* Resets slider
*
* @returns {undefined}
*/
resetSlider: function() {
this.manageElementsStyle();
this.setMinAndMax();
this.updateCeilLab();
this.updateFloorLab();
this.unbindEvents();
this.manageEventsBindings();
this.setDisabledState();
this.calcViewDimensions();
},
}, self.options.interval);
/**
* Set the slider children to variables for easy access
*
* Run only once during initialization
*
* @returns {undefined}
*/
initElemHandles: function() {
// Assign all slider elements to object properties for easy access
angular.forEach(this.sliderElem.children(), function(elem, index) {
var jElem = angular.element(elem);
switch (index) {
case 0:
this.fullBar = jElem;
break;
case 1:
this.selBar = jElem;
break;
case 2:
this.minH = jElem;
break;
case 3:
this.maxH = jElem;
break;
case 4:
this.flrLab = jElem;
break;
case 5:
this.ceilLab = jElem;
break;
case 6:
this.minLab = jElem;
break;
case 7:
this.maxLab = jElem;
break;
case 8:
this.cmbLab = jElem;
break;
case 9:
this.ticks = jElem;
break;
}
thrHigh = rzThrottle(function() {
self.setMinAndMax();
self.updateHighHandle(self.valueToOffset(self.scope.rzSliderHigh));
self.updateSelectionBar();
self.updateTicksScale();
self.updateCmbLabel();
}, self.options.interval);
}, this);
this.scope.$on('rzSliderForceRender', function() {
self.resetLabelsValue();
thrLow();
if (self.range) {
thrHigh();
}
self.resetSlider();
});
// Initialize offset cache properties
this.selBar.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;
},
// Watchers
this.scope.$watch('rzSliderModel', function(newValue, oldValue) {
if (newValue === oldValue)
return;
thrLow();
});
/** Update each elements style based on options
*
*/
manageElementsStyle: function() {
if (!this.range)
this.maxH.css('display', 'none');
else
this.maxH.css('display', null);
this.alwaysHide(this.flrLab, this.options.showTicksValues || this.options.hideLimitLabels);
this.alwaysHide(this.ceilLab, this.options.showTicksValues || this.options.hideLimitLabels);
this.alwaysHide(this.minLab, this.options.showTicksValues);
this.alwaysHide(this.maxLab, this.options.showTicksValues || !this.range);
this.alwaysHide(this.cmbLab, this.options.showTicksValues || !this.range);
this.alwaysHide(this.selBar, !this.range && !this.options.showSelectionBar);
if (!this.options.showTicks)
this.ticks.html('');
if (this.options.draggableRange)
this.selBar.addClass('rz-draggable');
else
this.selBar.removeClass('rz-draggable');
},
alwaysHide: function(el, hide) {
el.rzAlwaysHide = hide;
if (hide)
this.hideEl(el);
else
this.showEl(el)
},
this.scope.$watch('rzSliderHigh', function(newValue, oldValue) {
if (newValue === oldValue)
return;
if (newValue != null)
thrHigh();
if (self.range && newValue == null || !self.range && newValue != null) {
self.applyOptions();
self.resetSlider();
}
});
/**
* Manage the events bindings based on readOnly and disabled options
*
* @returns {undefined}
*/
manageEventsBindings: function() {
if (this.options.disabled || this.options.readOnly)
this.unbindEvents();
else if (!this.options.disabled || !this.options.readOnly)
this.bindEvents();
},
this.scope.$watch('rzSliderOptions', function(newValue, oldValue) {
if (newValue === oldValue)
return;
self.applyOptions();
self.resetSlider();
}, true);
/**
* Set the disabled state based on rzSliderDisabled
*
* @returns {undefined}
*/
setDisabledState: function() {
if (this.options.disabled) {
this.sliderElem.attr('disabled', 'disabled');
} else {
this.sliderElem.attr('disabled', null);
}
},
this.scope.$on('$destroy', function() {
self.unbindEvents();
angular.element($window).off('resize', calcDimFn);
});
},
/**
* Reset label values
*
* @return {undefined}
*/
resetLabelsValue: function() {
this.minLab.rzsv = undefined;
this.maxLab.rzsv = undefined;
},
/**
* Read the user options and apply them to the slider model
*/
applyOptions: function() {
this.options = RzSliderOptions.getOptions(this.scope.rzSliderOptions);
if (this.options.step <= 0)
this.options.step = 1;
this.range = this.scope.rzSliderModel !== undefined && this.scope.rzSliderHigh !== undefined;
this.options.draggableRange = this.range && this.options.draggableRange;
this.options.showTicks = this.options.showTicks || this.options.showTicksValues;
if (this.options.stepsArray) {
this.options.floor = 0;
this.options.ceil = this.options.stepsArray.length - 1;
this.options.step = 1;
this.customTrFn = function(value) {
return this.options.stepsArray[value];
};
}
else if (this.options.translate)
this.customTrFn = this.options.translate;
else
this.customTrFn = function(value) {
return String(value);
};
},
/**
* Resets slider
*
* @returns {undefined}
*/
resetSlider: function() {
this.manageElementsStyle();
this.setMinAndMax();
this.updateCeilLab();
this.updateFloorLab();
this.unbindEvents();
this.manageEventsBindings();
this.setDisabledState();
this.calcViewDimensions();
},
/**
* Set the slider children to variables for easy access
*
* Run only once during initialization
*
* @returns {undefined}
*/
initElemHandles: function() {
// Assign all slider elements to object properties for easy access
angular.forEach(this.sliderElem.children(), function(elem, index) {
var jElem = angular.element(elem);
switch (index) {
case 0:
this.fullBar = jElem;
break;
case 1:
this.selBar = jElem;
break;
case 2:
this.minH = jElem;
break;
case 3:
this.maxH = jElem;
break;
case 4:
this.flrLab = jElem;
break;
case 5:
this.ceilLab = jElem;
break;
case 6:
this.minLab = jElem;
break;
case 7:
this.maxLab = jElem;
break;
case 8:
this.cmbLab = jElem;
break;
case 9:
this.ticks = jElem;
break;
}
/**
* 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));
}, this);
// Initialize offset cache properties
this.selBar.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;
},
/** Update each elements style based on options
*
*/
manageElementsStyle: function() {
if (!this.range)
this.maxH.css('display', 'none');
else
this.maxH.css('display', null);
this.alwaysHide(this.flrLab, this.options.showTicksValues || this.options.hideLimitLabels);
this.alwaysHide(this.ceilLab, this.options.showTicksValues || this.options.hideLimitLabels);
this.alwaysHide(this.minLab, this.options.showTicksValues);
this.alwaysHide(this.maxLab, this.options.showTicksValues || !this.range);
this.alwaysHide(this.cmbLab, this.options.showTicksValues || !this.range);
this.alwaysHide(this.selBar, !this.range && !this.options.showSelectionBar);
if (!this.options.showTicks)
this.ticks.html('');
if (this.options.draggableRange)
this.selBar.addClass('rz-draggable');
else
this.selBar.removeClass('rz-draggable');
},
alwaysHide: function(el, hide) {
el.rzAlwaysHide = hide;
if (hide)
this.hideEl(el);
else
this.showEl(el)
},
/**
* Manage the events bindings based on readOnly and disabled options
*
* @returns {undefined}
/*
the order here is important since the selection bar should be
updated after the high handle but before the combined label
*/
manageEventsBindings: function() {
if (this.options.disabled || this.options.readOnly)
this.unbindEvents();
else if (!this.options.disabled || !this.options.readOnly)
this.bindEvents();
},
/**
* Set the disabled state based on rzSliderDisabled
*
* @returns {undefined}
*/
setDisabledState: function() {
if (this.options.disabled) {
this.sliderElem.attr('disabled', 'disabled');
}
else {
this.sliderElem.attr('disabled', null);
}
},
if (this.range)
this.updateHighHandle(this.valueToOffset(this.scope.rzSliderHigh));
this.updateSelectionBar();
if (this.range)
this.updateCmbLabel();
/**
* 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));
/*
the order here is important since the selection bar should be
updated after the high handle but before the combined label
*/
if (this.range)
this.updateHighHandle(this.valueToOffset(this.scope.rzSliderHigh));
this.updateSelectionBar();
if (this.range)
this.updateCmbLabel();
this.updateTicksScale();
},
this.updateTicksScale();
},
/**
* Translate value to human readable format
*
* @param {number|string} value
* @param {jqLite} label
* @param {boolean} [useCustomTr]
* @returns {undefined}
*/
translateFn: function(value, label, useCustomTr) {
useCustomTr = useCustomTr === undefined ? true : useCustomTr;
/**
* Translate value to human readable format
*
* @param {number|string} value
* @param {jqLite} label
* @param {boolean} [useCustomTr]
* @returns {undefined}
*/
translateFn: function(value, label, useCustomTr) {
useCustomTr = useCustomTr === undefined ? true : useCustomTr;
var valStr = String((useCustomTr ? this.customTrFn(value, this.options.id) : value)),
getWidth = false;
var valStr = String((useCustomTr ? this.customTrFn(value, this.options.id) : value)),
getWidth = false;
if (label.rzsv === undefined || label.rzsv.length !== valStr.length || (label.rzsv.length > 0 && label.rzsw === 0)) {
getWidth = true;
label.rzsv = valStr;
}
if (label.rzsv === undefined || label.rzsv.length !== valStr.length || (label.rzsv.length > 0 && label.rzsw === 0)) {
getWidth = true;
label.rzsv = valStr;
}
label.text(valStr);
label.text(valStr);
// Update width only when length of the label have changed
if (getWidth) {
this.getWidth(label);
}
},
// Update width only when length of the label have changed
if (getWidth) {
this.getWidth(label);
}
},
/**
* Set maximum and minimum values for the slider and ensure the model and high
* value match these limits
* @returns {undefined}
*/
setMinAndMax: function() {
/**
* Set maximum and minimum values for the slider and ensure the model and high
* value match these limits
* @returns {undefined}
*/
setMinAndMax: function() {
this.step = +this.options.step;
this.precision = +this.options.precision;
this.step = +this.options.step;
this.precision = +this.options.precision;
this.scope.rzSliderModel = this.roundStep(this.scope.rzSliderModel);
if (this.range)
this.scope.rzSliderHigh = this.roundStep(this.scope.rzSliderHigh);
this.scope.rzSliderModel = this.roundStep(this.scope.rzSliderModel);
if (this.range)
this.scope.rzSliderHigh = this.roundStep(this.scope.rzSliderHigh);
this.minValue = this.roundStep(+this.options.floor);
this.minValue = this.roundStep(+this.options.floor);
if (this.options.ceil)
this.maxValue = this.roundStep(+this.options.ceil);
else
this.maxValue = this.options.ceil = this.range ? this.scope.rzSliderHigh : this.scope.rzSliderModel;
if (this.options.ceil)
this.maxValue = this.roundStep(+this.options.ceil);
else
this.maxValue = this.options.ceil = this.range ? this.scope.rzSliderHigh : this.scope.rzSliderModel;
this.valueRange = this.maxValue - this.minValue;
},
this.valueRange = this.maxValue - this.minValue;
},
/**
* Adds accessibility atributes
*
* Run only once during initialization
*
* @returns {undefined}
*/
addAccessibility: function() {
this.sliderElem.attr("role", "slider");
},
/**
* 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);
/**
* Adds accessibility atributes
*
* Run only once during initialization
*
* @returns {undefined}
*/
addAccessibility: function() {
this.sliderElem.attr("role", "slider");
},
this.handleHalfWidth = handleWidth / 2;
this.barWidth = this.getWidth(this.fullBar);
/**
* 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.maxLeft = this.barWidth - handleWidth;
this.handleHalfWidth = handleWidth / 2;
this.barWidth = this.getWidth(this.fullBar);
this.getWidth(this.sliderElem);
this.sliderElem.rzsl = this.sliderElem[0].getBoundingClientRect().left;
this.maxLeft = this.barWidth - handleWidth;
if (this.initHasRun) {
this.updateFloorLab();
this.updateCeilLab();
this.initHandles();
}
},
this.getWidth(this.sliderElem);
this.sliderElem.rzsl = this.sliderElem[0].getBoundingClientRect().left;
/**
* Update the ticks position
*
* @returns {undefined}
*/
updateTicksScale: function() {
if (!this.options.showTicks) return;
if (!this.step) return; //if step is 0, the following loop will be endless.
var positions = '',
ticksCount = Math.round((this.maxValue - this.minValue) / this.step) + 1;
for (var i = 0; i < ticksCount; i++) {
var value = this.roundStep(this.minValue + i * this.step);
var selectedClass = this.isTickSelected(value) ? 'selected' : '';
positions += '<li class="tick ' + selectedClass + '">';
if (this.options.showTicksValues) {
var tooltip = '';
if (this.options.ticksValuesTooltip) {
tooltip = 'uib-tooltip="' + this.options.ticksValuesTooltip(value) + '"';
}
positions += '<span ' + tooltip + ' class="tick-value">' + this.getDisplayValue(value) + '</span>';
if (this.initHasRun) {
this.updateFloorLab();
this.updateCeilLab();
this.initHandles();
}
},
/**
* Update the ticks position
*
* @returns {undefined}
*/
updateTicksScale: function() {
if (!this.options.showTicks) return;
if (!this.step) return; //if step is 0, the following loop will be endless.
var positions = '',
ticksCount = Math.round((this.maxValue - this.minValue) / this.step) + 1;
for (var i = 0; i < ticksCount; i++) {
var value = this.roundStep(this.minValue + i * this.step);
var selectedClass = this.isTickSelected(value) ? 'selected' : '';
positions += '<li class="tick ' + selectedClass + '">';
if (this.options.showTicksValues) {
var tooltip = '';
if (this.options.ticksValuesTooltip) {
tooltip = 'uib-tooltip="' + this.options.ticksValuesTooltip(value) + '"';
}
positions += '</li>';
}
this.ticks.html(positions);
if (this.options.ticksValuesTooltip)
$compile(this.ticks.contents())(this.scope);
},
isTickSelected: function(value) {
if (!this.range && this.options.showSelectionBar && value <= this.scope.rzSliderModel)
return true;
if (this.range && value >= this.scope.rzSliderModel && value <= this.scope.rzSliderHigh)
return true;
return false;
},
/**
* Update position of the ceiling label
*
* @returns {undefined}
*/
updateCeilLab: function() {
this.translateFn(this.maxValue, 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.minValue, this.flrLab);
this.getWidth(this.flrLab);
},
/**
* Call the onStart callback if defined
*
* @returns {undefined}
*/
callOnStart: function() {
if (this.options.onStart) {
var self = this;
$timeout(function() {
self.options.onStart();
});
positions += '<span ' + tooltip + ' class="tick-value">' + this.getDisplayValue(value) + '</span>';
}
},
positions += '</li>';
}
this.ticks.html(positions);
if (this.options.ticksValuesTooltip)
$compile(this.ticks.contents())(this.scope);
},
isTickSelected: function(value) {
if (!this.range && this.options.showSelectionBar && value <= this.scope.rzSliderModel)
return true;
if (this.range && value >= this.scope.rzSliderModel && value <= this.scope.rzSliderHigh)
return true;
return false;
},
/**
* Call the onChange callback if defined
*
* @returns {undefined}
*/
callOnChange: function() {
if (this.options.onChange) {
var self = this;
$timeout(function() {
self.options.onChange();
});
}
},
/**
* Update position of the ceiling label
*
* @returns {undefined}
*/
updateCeilLab: function() {
this.translateFn(this.maxValue, this.ceilLab);
this.setLeft(this.ceilLab, this.barWidth - this.ceilLab.rzsw);
this.getWidth(this.ceilLab);
},
/**
* Call the onEnd callback if defined
*
* @returns {undefined}
*/
callOnEnd: function() {
if (this.options.onEnd) {
var self = this;
$timeout(function() {
self.options.onEnd();
});
}
},
/**
* Update position of the floor label
*
* @returns {undefined}
*/
updateFloorLab: function() {
this.translateFn(this.minValue, 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);
this.updateSelectionBar();
this.updateTicksScale();
if (this.range) {
this.updateCmbLabel();
}
return;
}
/**
* Call the onStart callback if defined
*
* @returns {undefined}
*/
callOnStart: function() {
if (this.options.onStart) {
var self = this;
$timeout(function() {
self.options.onStart();
});
}
},
if (which === 'rzSliderHigh') {
this.updateHighHandle(newOffset);
this.updateSelectionBar();
this.updateTicksScale();
/**
* Call the onChange callback if defined
*
* @returns {undefined}
*/
callOnChange: function() {
if (this.options.onChange) {
var self = this;
$timeout(function() {
self.options.onChange();
});
}
},
if (this.range) {
this.updateCmbLabel();
}
return;
}
/**
* Call the onEnd callback if defined
*
* @returns {undefined}
*/
callOnEnd: function() {
if (this.options.onEnd) {
var self = this;
$timeout(function() {
self.options.onEnd();
});
}
},
// Update both
/**
* Update slider handles and label positions
*
* @param {string} which
* @param {number} newOffset
*/
updateHandles: function(which, newOffset) {
if (which === 'rzSliderModel') {
this.updateLowHandle(newOffset);
this.updateHighHandle(newOffset);
this.updateSelectionBar();
this.updateTicksScale();
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);
if (this.range) {
this.updateCmbLabel();
}
else {
flHidden = false;
this.showEl(this.flrLab);
return;
}
if (which === 'rzSliderHigh') {
this.updateHighHandle(newOffset);
this.updateSelectionBar();
this.updateTicksScale();
if (this.range) {
this.updateCmbLabel();
}
return;
}
// Update both
this.updateLowHandle(newOffset);
this.updateHighHandle(newOffset);
this.updateSelectionBar();
this.updateTicksScale();
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;
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 {
clHidden = false;
} else if (!clHidden) {
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);
}
// 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, Math.abs(this.maxH.rzsl - this.minH.rzsl) + this.handleHalfWidth);
this.setLeft(this.selBar, this.range ? this.minH.rzsl + this.handleHalfWidth : 0);
},
/**
* Update combined label position and value
*
* @returns {undefined}
*/
updateCmbLabel: function() {
var lowTr, highTr;
if (this.minLab.rzsl + this.minLab.rzsw + 10 >= this.maxLab.rzsl) {
lowTr = this.getDisplayValue(this.scope.rzSliderModel);
highTr = this.getDisplayValue(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);
}
},
/**
* Update slider selection bar, combined label and range label
*
* @returns {undefined}
*/
updateSelectionBar: function() {
this.setWidth(this.selBar, Math.abs(this.maxH.rzsl - this.minH.rzsl) + this.handleHalfWidth);
this.setLeft(this.selBar, this.range ? this.minH.rzsl + this.handleHalfWidth : 0);
},
/**
* Return the translated value if a translate function is provided else the original value
* @param value
* @returns {*}
*/
getDisplayValue: function(value) {
return this.customTrFn(value, this.options.id);
},
/**
* Round value to step and precision
*
* @param {number} value
* @returns {number}
*/
roundStep: function(value) {
var step = this.step,
remainder = +((value - this.minValue) % step).toFixed(3),
steppedValue = remainder > (step / 2) ? value + step - remainder : value - remainder;
steppedValue = steppedValue.toFixed(this.precision);
return +steppedValue;
},
/**
* Hide element
*
* @param element
* @returns {jqLite} The jqLite wrapped DOM element
*/
hideEl: function(element) {
return element.css({opacity: 0});
},
/**
* Show element
*
* @param element The jqLite wrapped DOM element
* @returns {jqLite} The jqLite
*/
showEl: function(element) {
if (!!element.rzAlwaysHide) {
return element;
}
/**
* Update combined label position and value
*
* @returns {undefined}
*/
updateCmbLabel: function() {
var lowTr, highTr;
if (this.minLab.rzsl + this.minLab.rzsw + 10 >= this.maxLab.rzsl) {
lowTr = this.getDisplayValue(this.scope.rzSliderModel);
highTr = this.getDisplayValue(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);
}
},
return element.css({opacity: 1});
},
/**
* Return the translated value if a translate function is provided else the original value
* @param value
* @returns {*}
*/
getDisplayValue: function(value) {
return this.customTrFn(value, this.options.id);
},
/**
* 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) * this.options.scale;
return elem.rzsw;
},
/**
* Set element width
*
* @param {jqLite} elem The jqLite wrapped DOM element
* @param {number} width
* @returns {number}
*/
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 (this.sanitizeOffsetValue(val) - this.minValue) * this.maxLeft / this.valueRange || 0;
},
/**
* Ensure that the position rendered is within the slider bounds, even if the value is not
*
* @param {number} val
* @returns {number}
*/
sanitizeOffsetValue: function(val) {
return Math.min(Math.max(val, this.minValue), this.maxValue);
},
/**
* Translate offset to model value
*
* @param {number} offset
* @returns {number}
*/
offsetToValue: function(offset) {
return (offset / this.maxLeft) * this.valueRange + this.minValue;
},
/**
* Round value to step and precision
*
* @param {number} value
* @returns {number}
*/
roundStep: function(value) {
var step = this.step,
remainder = +((value - this.minValue) % step).toFixed(3),
steppedValue = remainder > (step / 2) ? value + step - remainder : value - remainder;
// Events
steppedValue = steppedValue.toFixed(this.precision);
return +steppedValue;
},
/**
* Get the X-coordinate of an event
*
* @param {Object} event The event
* @returns {number}
*/
getEventX: function(event) {
/* http://stackoverflow.com/a/12336075/282882 */
//noinspection JSLint
if ('clientX' in event) {
return event.clientX;
}
/**
* Hide element
*
* @param element
* @returns {jqLite} The jqLite wrapped DOM element
*/
hideEl: function(element) {
return element.css({
opacity: 0
});
},
return event.originalEvent === undefined ?
event.touches[0].clientX
: event.originalEvent.touches[0].clientX;
},
/**
* Show element
*
* @param element The jqLite wrapped DOM element
* @returns {jqLite} The jqLite
*/
showEl: function(element) {
if (!!element.rzAlwaysHide) {
return element;
}
/**
* Get the handle closest to an event.
*
* @param event {Event} The event
* @returns {jqLite} The handle closest to the event.
*/
getNearestHandle: function(event) {
if (!this.range) {
return this.minH;
}
var offset = (this.getEventX(event) - this.sliderElem.rzsl - this.handleHalfWidth) * this.options.scale;
return Math.abs(offset - this.minH.rzsl) < Math.abs(offset - this.maxH.rzsl) ? this.minH : this.maxH;
},
/**
* Bind mouse and touch events to slider handles
*
* @returns {undefined}
*/
bindEvents: function() {
if (this.options.readOnly || this.options.disabled) return;
var barTracking, barStart, barMove;
if (this.options.draggableRange) {
barTracking = 'rzSliderDrag';
barStart = this.onDragStart;
barMove = this.onDragMove;
}
else {
barTracking = 'rzSliderModel';
barStart = this.onStart;
barMove = this.onMove;
}
return element.css({
opacity: 1
});
},
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.fullBar.on('mousedown', angular.bind(this, this.onStart, null, null));
this.fullBar.on('mousedown', angular.bind(this, this.onMove, this.fullBar));
this.selBar.on('mousedown', angular.bind(this, barStart, null, barTracking));
this.selBar.on('mousedown', angular.bind(this, barMove, this.selBar));
this.ticks.on('mousedown', angular.bind(this, this.onStart, null, null));
this.ticks.on('mousedown', angular.bind(this, this.onMove, this.ticks));
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'));
}
this.fullBar.on('touchstart', angular.bind(this, this.onStart, null, null));
this.fullBar.on('touchstart', angular.bind(this, this.onMove, this.fullBar));
this.selBar.on('touchstart', angular.bind(this, barStart, null, barTracking));
this.selBar.on('touchstart', angular.bind(this, barMove, this.selBar));
this.ticks.on('touchstart', angular.bind(this, this.onStart, null, null));
this.ticks.on('touchstart', angular.bind(this, this.onMove, this.ticks));
},
/**
* Unbind mouse and touch events to slider handles
*
* @returns {undefined}
*/
unbindEvents: function() {
this.minH.off();
this.maxH.off();
this.fullBar.off();
this.selBar.off();
this.ticks.off();
},
/**
* onStart event handler
*
* @param {?Object} pointer The jqLite wrapped DOM element; if null, the closest handle is used
* @param {?string} ref The name of the handle being changed; if null, the closest handle's value is modified
* @param {Event} event The event
* @returns {undefined}
*/
onStart: function(pointer, ref, event) {
var ehMove, ehEnd,
eventNames = this.getEventNames(event);
/**
* 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;
},
event.stopPropagation();
event.preventDefault();
/**
* 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) * this.options.scale;
return elem.rzsw;
},
if (this.tracking !== '') {
return;
}
/**
* Set element width
*
* @param {jqLite} elem The jqLite wrapped DOM element
* @param {number} width
* @returns {number}
*/
setWidth: function(elem, width) {
elem.rzsw = width;
elem.css({
width: width + 'px'
});
return width;
},
// We have to do this in case the HTML where the sliders are on
// have been animated into view.
this.calcViewDimensions();
/**
* Translate value to pixel offset
*
* @param {number} val
* @returns {number}
*/
valueToOffset: function(val) {
return (this.sanitizeOffsetValue(val) - this.minValue) * this.maxLeft / this.valueRange || 0;
},
if (pointer) {
this.tracking = ref;
}
else {
pointer = this.getNearestHandle(event);
this.tracking = pointer === this.minH ? 'rzSliderModel' : 'rzSliderHigh';
}
/**
* Ensure that the position rendered is within the slider bounds, even if the value is not
*
* @param {number} val
* @returns {number}
*/
sanitizeOffsetValue: function(val) {
return Math.min(Math.max(val, this.minValue), this.maxValue);
},
pointer.addClass('rz-active');
/**
* Translate offset to model value
*
* @param {number} offset
* @returns {number}
*/
offsetToValue: function(offset) {
return (offset / this.maxLeft) * this.valueRange + this.minValue;
},
ehMove = angular.bind(this, this.dragging.active ? this.onDragMove : this.onMove, pointer);
ehEnd = angular.bind(this, this.onEnd, ehMove);
// Events
$document.on(eventNames.moveEvent, ehMove);
$document.one(eventNames.endEvent, ehEnd);
this.callOnStart();
},
/**
* Get the X-coordinate of an event
*
* @param {Object} event The event
* @returns {number}
*/
getEventX: function(event) {
/* http://stackoverflow.com/a/12336075/282882 */
//noinspection JSLint
if ('clientX' in event) {
return event.clientX;
}
/**
* onMove event handler
*
* @param {jqLite} pointer
* @param {Event} event The event
* @returns {undefined}
*/
onMove: function(pointer, event) {
var eventX = this.getEventX(event),
sliderLO, newOffset, newValue;
sliderLO = this.sliderElem.rzsl;
newOffset = (eventX - sliderLO - this.handleHalfWidth) * this.options.scale;
if (newOffset <= 0) {
if (pointer.rzsl === 0)
return;
newValue = this.minValue;
newOffset = 0;
}
else if (newOffset >= this.maxLeft) {
if (pointer.rzsl === this.maxLeft)
return;
newValue = this.maxValue;
newOffset = this.maxLeft;
}
else {
newValue = this.offsetToValue(newOffset);
newValue = this.roundStep(newValue);
newOffset = this.valueToOffset(newValue);
}
this.positionTrackingHandle(newValue, newOffset);
},
/**
* onDragStart event handler
*
* Handles dragging of the middle bar.
*
* @param {Object} pointer The jqLite wrapped DOM element
* @param {string} ref One of the refLow, refHigh values
* @param {Event} event The event
* @returns {undefined}
*/
onDragStart: function(pointer, ref, event) {
var offset = this.getEventX(event) - this.sliderElem.rzsl - this.handleHalfWidth;
this.dragging = {
active: true,
value: this.offsetToValue(offset),
difference: this.scope.rzSliderHigh - this.scope.rzSliderModel,
offset: offset,
lowDist: offset - this.minH.rzsl,
highDist: this.maxH.rzsl - offset
};
this.minH.addClass('rz-active');
this.maxH.addClass('rz-active');
this.onStart(pointer, ref, event);
},
/**
* onDragMove event handler
*
* Handles dragging of the middle bar.
*
* @param {jqLite} pointer
* @param {Event} event The event
* @returns {undefined}
*/
onDragMove: function(pointer, event) {
var newOffset = this.getEventX(event) - this.sliderElem.rzsl - this.handleHalfWidth,
newMinOffset, newMaxOffset,
newMinValue, newMaxValue;
if (newOffset <= this.dragging.lowDist) {
if (pointer.rzsl === this.dragging.lowDist) {
return;
}
newMinValue = this.minValue;
newMinOffset = 0;
newMaxValue = this.minValue + this.dragging.difference;
newMaxOffset = this.valueToOffset(newMaxValue);
}
else if (newOffset >= this.maxLeft - this.dragging.highDist) {
if (pointer.rzsl === this.dragging.highDist) {
return;
}
newMaxValue = this.maxValue;
newMaxOffset = this.maxLeft;
newMinValue = this.maxValue - this.dragging.difference;
newMinOffset = this.valueToOffset(newMinValue);
}
else {
newMinValue = this.offsetToValue(newOffset - this.dragging.lowDist);
newMinValue = this.roundStep(newMinValue);
newMinOffset = this.valueToOffset(newMinValue);
newMaxValue = newMinValue + this.dragging.difference;
newMaxOffset = this.valueToOffset(newMaxValue);
}
return event.originalEvent === undefined ?
event.touches[0].clientX : event.originalEvent.touches[0].clientX;
},
this.positionTrackingBar(newMinValue, newMaxValue, newMinOffset, newMaxOffset);
},
/**
* Get the handle closest to an event.
*
* @param event {Event} The event
* @returns {jqLite} The handle closest to the event.
*/
getNearestHandle: function(event) {
if (!this.range) {
return this.minH;
}
var offset = (this.getEventX(event) - this.sliderElem.rzsl - this.handleHalfWidth) * this.options.scale;
return Math.abs(offset - this.minH.rzsl) < Math.abs(offset - this.maxH.rzsl) ? this.minH : this.maxH;
},
/**
* Set the new value and offset for the entire bar
*
* @param {number} newMinValue the new minimum value
* @param {number} newMaxValue the new maximum value
* @param {number} newMinOffset the new minimum offset
* @param {number} newMaxOffset the new maximum offset
*/
positionTrackingBar: function(newMinValue, newMaxValue, newMinOffset, newMaxOffset) {
this.scope.rzSliderModel = newMinValue;
this.scope.rzSliderHigh = newMaxValue;
this.updateHandles('rzSliderModel', newMinOffset);
this.updateHandles('rzSliderHigh', newMaxOffset);
this.scope.$apply();
this.callOnChange();
},
/**
* Bind mouse and touch events to slider handles
*
* @returns {undefined}
*/
bindEvents: function() {
if (this.options.readOnly || this.options.disabled) return;
var barTracking, barStart, barMove;
if (this.options.draggableRange) {
barTracking = 'rzSliderDrag';
barStart = this.onDragStart;
barMove = this.onDragMove;
} else {
barTracking = 'rzSliderModel';
barStart = this.onStart;
barMove = this.onMove;
}
/**
* Set the new value and offset to the current tracking handle
*
* @param {number} newValue new model value
* @param {number} newOffset new offset value
*/
positionTrackingHandle: function(newValue, newOffset) {
if (this.range) {
/* This is to check if we need to switch the min and max handles*/
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('rz-active');
this.maxH.addClass('rz-active');
/* We need to apply here because we are not sure that we will enter the next block */
this.scope.$apply();
this.callOnChange();
}
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('rz-active');
this.minH.addClass('rz-active');
/* We need to apply here because we are not sure that we will enter the next block */
this.scope.$apply();
this.callOnChange();
}
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.fullBar.on('mousedown', angular.bind(this, this.onStart, null, null));
this.fullBar.on('mousedown', angular.bind(this, this.onMove, this.fullBar));
this.selBar.on('mousedown', angular.bind(this, barStart, null, barTracking));
this.selBar.on('mousedown', angular.bind(this, barMove, this.selBar));
this.ticks.on('mousedown', angular.bind(this, this.onStart, null, null));
this.ticks.on('mousedown', angular.bind(this, this.onMove, this.ticks));
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'));
}
this.fullBar.on('touchstart', angular.bind(this, this.onStart, null, null));
this.fullBar.on('touchstart', angular.bind(this, this.onMove, this.fullBar));
this.selBar.on('touchstart', angular.bind(this, barStart, null, barTracking));
this.selBar.on('touchstart', angular.bind(this, barMove, this.selBar));
this.ticks.on('touchstart', angular.bind(this, this.onStart, null, null));
this.ticks.on('touchstart', angular.bind(this, this.onMove, this.ticks));
},
/**
* Unbind mouse and touch events to slider handles
*
* @returns {undefined}
*/
unbindEvents: function() {
this.minH.off();
this.maxH.off();
this.fullBar.off();
this.selBar.off();
this.ticks.off();
},
/**
* onStart event handler
*
* @param {?Object} pointer The jqLite wrapped DOM element; if null, the closest handle is used
* @param {?string} ref The name of the handle being changed; if null, the closest handle's value is modified
* @param {Event} event The event
* @returns {undefined}
*/
onStart: function(pointer, ref, event) {
var ehMove, ehEnd,
eventNames = this.getEventNames(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();
if (pointer) {
this.tracking = ref;
} else {
pointer = this.getNearestHandle(event);
this.tracking = pointer === this.minH ? 'rzSliderModel' : 'rzSliderHigh';
}
pointer.addClass('rz-active');
ehMove = angular.bind(this, this.dragging.active ? this.onDragMove : this.onMove, pointer);
ehEnd = angular.bind(this, this.onEnd, ehMove);
$document.on(eventNames.moveEvent, ehMove);
$document.one(eventNames.endEvent, ehEnd);
this.callOnStart();
},
/**
* onMove event handler
*
* @param {jqLite} pointer
* @param {Event} event The event
* @returns {undefined}
*/
onMove: function(pointer, event) {
var eventX = this.getEventX(event),
sliderLO, newOffset, newValue;
sliderLO = this.sliderElem.rzsl;
newOffset = (eventX - sliderLO - this.handleHalfWidth) * this.options.scale;
if (newOffset <= 0) {
if (pointer.rzsl === 0)
return;
newValue = this.minValue;
newOffset = 0;
} else if (newOffset >= this.maxLeft) {
if (pointer.rzsl === this.maxLeft)
return;
newValue = this.maxValue;
newOffset = this.maxLeft;
} else {
newValue = this.offsetToValue(newOffset);
newValue = this.roundStep(newValue);
newOffset = this.valueToOffset(newValue);
}
this.positionTrackingHandle(newValue, newOffset);
},
/**
* onDragStart event handler
*
* Handles dragging of the middle bar.
*
* @param {Object} pointer The jqLite wrapped DOM element
* @param {string} ref One of the refLow, refHigh values
* @param {Event} event The event
* @returns {undefined}
*/
onDragStart: function(pointer, ref, event) {
var offset = this.getEventX(event) - this.sliderElem.rzsl - this.handleHalfWidth;
this.dragging = {
active: true,
value: this.offsetToValue(offset),
difference: this.scope.rzSliderHigh - this.scope.rzSliderModel,
offset: offset,
lowDist: offset - this.minH.rzsl,
highDist: this.maxH.rzsl - offset
};
this.minH.addClass('rz-active');
this.maxH.addClass('rz-active');
this.onStart(pointer, ref, event);
},
/**
* onDragMove event handler
*
* Handles dragging of the middle bar.
*
* @param {jqLite} pointer
* @param {Event} event The event
* @returns {undefined}
*/
onDragMove: function(pointer, event) {
var newOffset = this.getEventX(event) - this.sliderElem.rzsl - this.handleHalfWidth,
newMinOffset, newMaxOffset,
newMinValue, newMaxValue;
if (newOffset <= this.dragging.lowDist) {
if (pointer.rzsl === this.dragging.lowDist) {
return;
}
newMinValue = this.minValue;
newMinOffset = 0;
newMaxValue = this.minValue + this.dragging.difference;
newMaxOffset = this.valueToOffset(newMaxValue);
} else if (newOffset >= this.maxLeft - this.dragging.highDist) {
if (pointer.rzsl === this.dragging.highDist) {
return;
}
newMaxValue = this.maxValue;
newMaxOffset = this.maxLeft;
newMinValue = this.maxValue - this.dragging.difference;
newMinOffset = this.valueToOffset(newMinValue);
} else {
newMinValue = this.offsetToValue(newOffset - this.dragging.lowDist);
newMinValue = this.roundStep(newMinValue);
newMinOffset = this.valueToOffset(newMinValue);
newMaxValue = newMinValue + this.dragging.difference;
newMaxOffset = this.valueToOffset(newMaxValue);
}
this.positionTrackingBar(newMinValue, newMaxValue, newMinOffset, newMaxOffset);
},
/**
* Set the new value and offset for the entire bar
*
* @param {number} newMinValue the new minimum value
* @param {number} newMaxValue the new maximum value
* @param {number} newMinOffset the new minimum offset
* @param {number} newMaxOffset the new maximum offset
*/
positionTrackingBar: function(newMinValue, newMaxValue, newMinOffset, newMaxOffset) {
this.scope.rzSliderModel = newMinValue;
this.scope.rzSliderHigh = newMaxValue;
this.updateHandles('rzSliderModel', newMinOffset);
this.updateHandles('rzSliderHigh', newMaxOffset);
this.scope.$apply();
this.callOnChange();
},
if (this.scope[this.tracking] !== newValue) {
this.scope[this.tracking] = newValue;
this.updateHandles(this.tracking, newOffset);
/**
* Set the new value and offset to the current tracking handle
*
* @param {number} newValue new model value
* @param {number} newOffset new offset value
*/
positionTrackingHandle: function(newValue, newOffset) {
if (this.range) {
/* This is to check if we need to switch the min and max handles*/
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('rz-active');
this.maxH.addClass('rz-active');
/* We need to apply here because we are not sure that we will enter the next block */
this.scope.$apply();
this.callOnChange();
} 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('rz-active');
this.minH.addClass('rz-active');
/* We need to apply here because we are not sure that we will enter the next block */
this.scope.$apply();
this.callOnChange();
}
},
/**
* onEnd event handler
*
* @param {Event} event The event
* @param {Function} ehMove The the bound move event handler
* @returns {undefined}
*/
onEnd: function(ehMove, event) {
var moveEventName = this.getEventNames(event).moveEvent;
}
this.minH.removeClass('rz-active');
this.maxH.removeClass('rz-active');
if (this.scope[this.tracking] !== newValue) {
this.scope[this.tracking] = newValue;
this.updateHandles(this.tracking, newOffset);
this.scope.$apply();
this.callOnChange();
}
},
$document.off(moveEventName, ehMove);
/**
* onEnd event handler
*
* @param {Event} event The event
* @param {Function} ehMove The the bound move event handler
* @returns {undefined}
*/
onEnd: function(ehMove, event) {
var moveEventName = this.getEventNames(event).moveEvent;
this.scope.$emit('slideEnded');
this.tracking = '';
this.minH.removeClass('rz-active');
this.maxH.removeClass('rz-active');
this.dragging.active = false;
this.callOnEnd();
},
$document.off(moveEventName, ehMove);
/**
* Get event names for move and event end
*
* @param {Event} event The event
*
* @return {{moveEvent: string, endEvent: string}}
*/
getEventNames: function(event) {
var eventNames = {moveEvent: '', endEvent: ''};
this.scope.$emit('slideEnded');
this.tracking = '';
if (event.touches || (event.originalEvent !== undefined && event.originalEvent.touches)) {
eventNames.moveEvent = 'touchmove';
eventNames.endEvent = 'touchend';
}
else {
eventNames.moveEvent = 'mousemove';
eventNames.endEvent = 'mouseup';
}
this.dragging.active = false;
this.callOnEnd();
},
return eventNames;
/**
* Get event names for move and event end
*
* @param {Event} event The event
*
* @return {{moveEvent: string, endEvent: string}}
*/
getEventNames: function(event) {
var eventNames = {
moveEvent: '',
endEvent: ''
};
if (event.touches || (event.originalEvent !== undefined && event.originalEvent.touches)) {
eventNames.moveEvent = 'touchmove';
eventNames.endEvent = 'touchend';
} else {
eventNames.moveEvent = 'mousemove';
eventNames.endEvent = 'mouseup';
}
};
return Slider;
})
return eventNames;
}
};
.directive('rzslider', function(RzSlider) {
'use strict';
return Slider;
})
return {
restrict: 'E',
scope: {
rzSliderModel: '=?',
rzSliderHigh: '=?',
rzSliderOptions: '=?',
rzSliderTplUrl: '@'
},
/**
* Return template URL
*
* @param {jqLite} elem
* @param {Object} attrs
* @return {string}
*/
templateUrl: function(elem, attrs) {
//noinspection JSUnresolvedVariable
return attrs.rzSliderTplUrl || 'rzSliderTpl.html';
},
.directive('rzslider', function(RzSlider) {
'use strict';
link: function(scope, elem) {
return new RzSlider(scope, elem);
}
};
});
return {
restrict: 'E',
scope: {
rzSliderModel: '=?',
rzSliderHigh: '=?',
rzSliderOptions: '=?',
rzSliderTplUrl: '@'
},
// IDE assist
/**
* Return template URL
*
* @param {jqLite} elem
* @param {Object} attrs
* @return {string}
*/
templateUrl: function(elem, attrs) {
//noinspection JSUnresolvedVariable
return attrs.rzSliderTplUrl || 'rzSliderTpl.html';
},
link: function(scope, elem) {
return new RzSlider(scope, elem);
}
};
});
// IDE assist
/**
* @name ngScope
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment