Commit 087948b4 authored by Valentin Hervieu's avatar Valentin Hervieu

Merge pull request #191 from rzajac/keyboard-support

Add keyboard support [WIP]
parents 06c08223 99829daa
node_modules/
.idea/
bower_components/
temp/
\ No newline at end of file
temp/
tests/coverage/
module.exports = function(grunt) {
module.exports = function (grunt) {
// Project configuration.
grunt.initConfig({
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',
'(c) <%= pkg.author %>, <%= pkg.repository.url %> - ' +
'<%= grunt.template.today("yyyy-mm-dd") %> */\n',
recess: {
options: {
......@@ -58,10 +58,10 @@ module.exports = function(grunt) {
removeStyleLinkTypeAttributes: true
},
module: 'rzModule',
url: function(url) {
url: function (url) {
return url.replace('src/', '');
},
bootstrap: function(module, script) {
bootstrap: function (module, script) {
return 'module.run(function($templateCache) {\n' + script + '\n});';
}
}
......@@ -118,8 +118,13 @@ module.exports = function(grunt) {
options: {
port: 9000
}
},
karma: {
unit: {
configFile: 'karma.conf.js',
singleRun: true
}
}
});
grunt.loadNpmTasks('grunt-contrib-uglify');
......@@ -129,8 +134,10 @@ module.exports = function(grunt) {
grunt.loadNpmTasks('grunt-ng-annotate');
grunt.loadNpmTasks('grunt-contrib-watch');
grunt.loadNpmTasks('grunt-serve');
grunt.loadNpmTasks('grunt-karma');
grunt.registerTask('default', ['css', 'js']);
grunt.registerTask('test', ['karma']);
grunt.registerTask('css', ['recess']);
grunt.registerTask('js', ['ngtemplates', 'replace', 'ngAnnotate', 'uglify']);
......
......@@ -8,7 +8,10 @@
"Jussi Saarivirta <jusasi@gmail.com>"
],
"description": "AngularJS slider directive with no external dependencies. Mobile friendly!",
"main": ["dist/rzslider.js", "dist/rzslider.css"],
"main": [
"dist/rzslider.js",
"dist/rzslider.css"
],
"keywords": [
"angularjs",
"slider"
......
......@@ -51,6 +51,7 @@
ticksValuesTooltip: null,
vertical: false,
selectionBarColor: null,
keyboardSupport: true,
scale: 1,
onStart: null,
onChange: null,
......@@ -281,7 +282,6 @@
this.initElemHandles();
this.manageElementsStyle();
this.addAccessibility();
this.manageEventsBindings();
this.setDisabledState();
this.calcViewDimensions();
this.setMinAndMax();
......@@ -290,7 +290,7 @@
self.updateCeilLab();
self.updateFloorLab();
self.initHandles();
self.bindEvents();
self.manageEventsBindings();
});
// Recalculate slider view dimensions
......@@ -311,6 +311,7 @@
self.updateLowHandle(self.valueToOffset(self.scope.rzSliderModel));
self.updateSelectionBar();
self.updateTicksScale();
self.updateAriaAttributes();
if (self.range) {
self.updateCmbLabel();
......@@ -324,6 +325,7 @@
self.updateSelectionBar();
self.updateTicksScale();
self.updateCmbLabel();
self.updateAriaAttributes();
}, self.options.interval);
this.scope.$on('rzSliderForceRender', function() {
......@@ -615,14 +617,47 @@
},
/**
* Adds accessibility atributes
* Adds accessibility attributes
*
* Run only once during initialization
*
* @returns {undefined}
*/
addAccessibility: function() {
this.sliderElem.attr("role", "slider");
this.minH.attr('role', 'slider');
this.updateAriaAttributes();
if (this.options.keyboardSupport)
this.minH.attr('tabindex', '0');
if (this.options.vertical)
this.minH.attr('aria-orientation', 'vertical');
if (this.range) {
this.maxH.attr('role', 'slider');
if (this.options.keyboardSupport)
this.maxH.attr('tabindex', '0');
if (this.options.vertical)
this.maxH.attr('aria-orientation', 'vertical');
}
},
/**
* Updates aria attributes according to current values
*/
updateAriaAttributes: function() {
this.minH.attr({
'aria-valuenow': this.scope.rzSliderModel,
'aria-valuetext': this.customTrFn(this.scope.rzSliderModel),
'aria-valuemin': this.minValue,
'aria-valuemax': this.maxValue
});
if (this.range) {
this.maxH.attr({
'aria-valuenow': this.scope.rzSliderHigh,
'aria-valuetext': this.customTrFn(this.scope.rzSliderHigh),
'aria-valuemin': this.minValue,
'aria-valuemax': this.maxValue
});
}
},
/**
......@@ -1014,16 +1049,16 @@
* @returns {number}
*/
valueToOffset: function(val) {
return (this.sanitizeOffsetValue(val) - this.minValue) * this.maxPos / this.valueRange || 0;
return (this.sanitizeValue(val) - this.minValue) * this.maxPos / this.valueRange || 0;
},
/**
* Ensure that the position rendered is within the slider bounds, even if the value is not
* Returns a value that is within slider range
*
* @param {number} val
* @returns {number}
*/
sanitizeOffsetValue: function(val) {
sanitizeValue: function(val) {
return Math.min(Math.max(val, this.minValue), this.maxValue);
},
......@@ -1086,6 +1121,16 @@
return Math.abs(offset - this.minH.rzsp) < Math.abs(offset - this.maxH.rzsp) ? this.minH : this.maxH;
},
/**
* Wrapper function to focus an angular element
*
* @param el {AngularElement} the element to focus
*/
focusElement: function(el) {
var DOM_ELEMENT = 0;
el[DOM_ELEMENT].focus();
},
/**
* Bind mouse and touch events to slider handles
*
......@@ -1126,6 +1171,13 @@
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));
if (this.options.keyboardSupport) {
this.minH.on('focus', angular.bind(this, this.onPointerFocus, this.minH, 'rzSliderModel'));
if (this.range) {
this.maxH.on('focus', angular.bind(this, this.onPointerFocus, this.maxH, 'rzSliderHigh'));
}
}
},
/**
......@@ -1156,10 +1208,6 @@
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();
......@@ -1173,6 +1221,9 @@
pointer.addClass('rz-active');
if (this.options.keyboardSupport)
this.focusElement(pointer);
ehMove = angular.bind(this, this.dragging.active ? this.onDragMove : this.onMove, pointer);
ehEnd = angular.bind(this, this.onEnd, ehMove);
......@@ -1210,6 +1261,74 @@
this.positionTrackingHandle(newValue, newOffset);
},
/**
* 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;
if (!this.options.keyboardSupport) {
this.minH.removeClass('rz-active');
this.maxH.removeClass('rz-active');
this.tracking = '';
}
this.dragging.active = false;
$document.off(moveEventName, ehMove);
this.scope.$emit('slideEnded');
this.callOnEnd();
},
onPointerFocus: function(pointer, ref) {
this.tracking = ref;
pointer.one('blur', angular.bind(this, this.onPointerBlur, pointer));
pointer.on('keydown', angular.bind(this, this.onKeyboardEvent));
pointer.addClass('rz-active');
},
onPointerBlur: function(pointer) {
pointer.off('keydown');
this.tracking = '';
pointer.removeClass('rz-active');
},
onKeyboardEvent: function(event) {
var currentValue = this.scope[this.tracking],
keyCode = event.keyCode || event.which,
keys = {
38: 'UP',
40: 'DOWN',
37: 'LEFT',
39: 'RIGHT',
33: 'PAGEUP',
34: 'PAGEDOWN',
36: 'HOME',
35: 'END'
},
actions = {
UP: currentValue + this.step,
DOWN: currentValue - this.step,
LEFT: currentValue - this.step,
RIGHT: currentValue + this.step,
PAGEUP: currentValue + this.valueRange / 10,
PAGEDOWN: currentValue - this.valueRange / 10,
HOME: this.minValue,
END: this.maxValue
},
key = keys[keyCode],
action = actions[key];
if (action == null || this.tracking === '') return;
event.preventDefault();
var newValue = this.roundStep(this.sanitizeValue(action)),
newOffset = this.valueToOffset(newValue);
this.positionTrackingHandle(newValue, newOffset);
},
/**
* onDragStart event handler
*
......@@ -1302,22 +1421,31 @@
*/
positionTrackingHandle: function(newValue, newOffset) {
var valueChanged = false;
var switched = false;
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) {
switched = true;
this.scope[this.tracking] = this.scope.rzSliderHigh;
this.updateHandles(this.tracking, this.maxH.rzsp);
this.updateAriaAttributes();
this.tracking = 'rzSliderHigh';
this.minH.removeClass('rz-active');
this.maxH.addClass('rz-active');
if (this.options.keyboardSupport)
this.focusElement(this.maxH);
valueChanged = true;
} else if (this.tracking === 'rzSliderHigh' && newValue <= this.scope.rzSliderModel) {
switched = true;
this.scope[this.tracking] = this.scope.rzSliderModel;
this.updateHandles(this.tracking, this.minH.rzsp);
this.updateAriaAttributes();
this.tracking = 'rzSliderModel';
this.maxH.removeClass('rz-active');
this.minH.addClass('rz-active');
if (this.options.keyboardSupport)
this.focusElement(this.minH);
valueChanged = true;
}
}
......@@ -1325,6 +1453,7 @@
if (this.scope[this.tracking] !== newValue) {
this.scope[this.tracking] = newValue;
this.updateHandles(this.tracking, newOffset);
this.updateAriaAttributes();
valueChanged = true;
}
......@@ -1332,28 +1461,7 @@
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');
$document.off(moveEventName, ehMove);
this.scope.$emit('slideEnded');
this.tracking = '';
this.dragging.active = false;
this.callOnEnd();
return switched;
},
/**
......
/*! angularjs-slider - v2.2.0 - (c) Rafal Zajac <rzajac@gmail.com>, Valentin Hervieu <valentin@hervieu.me>, Jussi Saarivirta <jusasi@gmail.com>, Angelin Sirbu <angelin.sirbu@gmail.com>, https://github.com/rzajac/angularjs-slider.git - 2015-12-17 */
/*! angularjs-slider - v2.2.0 - (c) Rafal Zajac <rzajac@gmail.com>, Valentin Hervieu <valentin@hervieu.me>, Jussi Saarivirta <jusasi@gmail.com>, Angelin Sirbu <angelin.sirbu@gmail.com>, https://github.com/rzajac/angularjs-slider.git - 2015-12-18 */
rzslider{position:relative;display:inline-block;width:100%;height:4px;margin:35px 0 15px 0;vertical-align:middle;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}rzslider[disabled]{cursor:not-allowed}rzslider[disabled] .rz-pointer{cursor:not-allowed;background-color:#d8e0f3}rzslider span{position:absolute;display:inline-block;white-space:nowrap}rzslider .rz-base{width:100%;height:100%;padding:0}rzslider .rz-bar-wrapper{left:0;z-index:1;width:100%;height:32px;padding-top:16px;margin-top:-16px;box-sizing:border-box}rzslider .rz-bar-wrapper.rz-draggable{cursor:move}rzslider .rz-bar{left:0;z-index:1;width:100%;height:4px;background:#d8e0f3;-webkit-border-radius:2px;-moz-border-radius:2px;border-radius:2px}rzslider .rz-bar.rz-selection{z-index:2;background:#0db9f0;-webkit-border-radius:2px;-moz-border-radius:2px;border-radius:2px}rzslider .rz-pointer{top:-14px;z-index:3;width:32px;height:32px;cursor:pointer;background-color:#0db9f0;-webkit-border-radius:16px;-moz-border-radius:16px;border-radius:16px}rzslider .rz-pointer:after{position:absolute;top:12px;left:12px;width:8px;height:8px;background:#fff;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;content:''}rzslider .rz-pointer:hover:after{background-color:#fff}rzslider .rz-pointer.rz-active:after{background-color:#451aff}rzslider .rz-bubble{bottom:16px;padding:1px 3px;color:#55637d;cursor:default}rzslider .rz-bubble.rz-selection{top:16px}rzslider .rz-bubble.rz-limit{color:#55637d}rzslider .rz-ticks{position:absolute;top:-3px;left:0;z-index:1;display:-webkit-flex;display:-ms-flexbox;display:flex;width:100%;padding:0 11px;margin:0;list-style:none;box-sizing:border-box;-webkit-justify-content:space-between;-ms-flex-pack:justify;justify-content:space-between}rzslider .rz-ticks .tick{width:10px;height:10px;text-align:center;cursor:pointer;background:#d8e0f3;border-radius:50%}rzslider .rz-ticks .tick.selected{background:#0db9f0}rzslider .rz-ticks .tick .tick-value{position:absolute;top:-30px;transform:translate(-50%,0)}rzslider.vertical{position:relative;width:4px;height:100%;padding:0;margin:0 20px;vertical-align:baseline}rzslider.vertical .rz-base{width:100%;height:100%;padding:0}rzslider.vertical .rz-bar-wrapper{top:auto;left:0;width:32px;height:100%;padding:0 0 0 16px;margin:0 0 0 -16px}rzslider.vertical .rz-bar{bottom:0;left:auto;width:4px;height:100%}rzslider.vertical .rz-pointer{top:auto;bottom:0;left:-14px!important}rzslider.vertical .rz-bubble{bottom:0;left:16px!important;margin-left:3px}rzslider.vertical .rz-bubble.rz-selection{top:auto;left:16px!important}rzslider.vertical .rz-ticks{top:0;left:-3px;z-index:1;width:auto;height:100%;padding:11px 0;-webkit-flex-direction:column-reverse;-ms-flex-direction:column-reverse;flex-direction:column-reverse}rzslider.vertical .rz-ticks .tick{vertical-align:middle}rzslider.vertical .rz-ticks .tick .tick-value{top:auto;right:-30px;transform:translate(0,-28%)}
\ No newline at end of file
This diff is collapsed.
'use strict';
module.exports = function (config) {
config.set({
// base path, that will be used to resolve files and exclude
basePath: '',
// testing framework to use (jasmine/mocha/qunit/...)
frameworks: ['mocha', 'chai', 'chai-things', 'chai-as-promised'],
reporters: ['progress', 'coverage'],
// list of files / patterns to load in the browser
files: [
'bower_components/angular/angular.js',
'node_modules/angular-mocks/angular-mocks.js',
'src/*.js',
'tests/spec/*.js',
'src/*.html'
],
// list of files / patterns to exclude
exclude: [],
// preprocess matching files before serving them to the browser
preprocessors: {
"src/*.js": ['coverage'],
"src/*Tpl.html": 'ng-html2js'
},
ngHtml2JsPreprocessor: {
stripPrefix: 'src/',
moduleName: 'appTemplates'
},
// web server port
port: 9998,
// level of logging
// possible values: LOG_DISABLE || LOG_ERROR || LOG_WARN || LOG_INFO || LOG_DEBUG
logLevel: config.LOG_INFO,
// enable / disable watching file and executing tests whenever any file changes
autoWatch: false,
coverageReporter: {
type: 'html',
dir: 'tests/coverage'
},
// Start these browsers, currently available:
// - Chrome
// - ChromeCanary
// - Firefox
// - Opera
// - Safari (only Mac)
// - PhantomJS
// - IE (only Windows)
browsers: ['PhantomJS'],
// Continuous Integration mode
// if true, it capture browsers, run tests and exit
singleRun: false
})
;
}
;
......@@ -12,15 +12,29 @@
"slider"
],
"devDependencies": {
"angular-mocks": "^1.4.8",
"chai": "^3.4.1",
"chai-as-promised": "^5.1.0",
"chai-things": "^0.2.0",
"grunt": "~0.4.2",
"grunt-angular-templates": "^0.5.7",
"grunt-contrib-mincss": "~0.3.2",
"grunt-contrib-uglify": "~0.2.2",
"grunt-contrib-watch": "^0.6.1",
"grunt-karma": "^0.12.1",
"grunt-ng-annotate": "^1.0.1",
"grunt-recess": "~0.4.0",
"grunt-replace": "^0.11.0",
"grunt-contrib-watch": "^0.6.1",
"grunt-serve": "^0.1.6",
"karma": "^0.13.15",
"karma-chai-plugins": "^0.6.1",
"karma-chrome-launcher": "^0.2.2",
"karma-coverage": "^0.5.3",
"karma-mocha": "^0.2.1",
"karma-ng-html2js-preprocessor": "^0.2.0",
"karma-phantomjs-launcher": "^0.2.1",
"mocha": "^2.3.4",
"phantomjs": "^1.9.19",
"recess": "~1.1.9"
},
"author": "Rafal Zajac <rzajac@gmail.com>, Valentin Hervieu <valentin@hervieu.me>, Jussi Saarivirta <jusasi@gmail.com>, Angelin Sirbu <angelin.sirbu@gmail.com>",
......
......@@ -51,6 +51,7 @@
ticksValuesTooltip: null,
vertical: false,
selectionBarColor: null,
keyboardSupport: true,
scale: 1,
onStart: null,
onChange: null,
......@@ -281,7 +282,6 @@
this.initElemHandles();
this.manageElementsStyle();
this.addAccessibility();
this.manageEventsBindings();
this.setDisabledState();
this.calcViewDimensions();
this.setMinAndMax();
......@@ -290,7 +290,7 @@
self.updateCeilLab();
self.updateFloorLab();
self.initHandles();
self.bindEvents();
self.manageEventsBindings();
});
// Recalculate slider view dimensions
......@@ -311,6 +311,7 @@
self.updateLowHandle(self.valueToOffset(self.scope.rzSliderModel));
self.updateSelectionBar();
self.updateTicksScale();
self.updateAriaAttributes();
if (self.range) {
self.updateCmbLabel();
......@@ -324,6 +325,7 @@
self.updateSelectionBar();
self.updateTicksScale();
self.updateCmbLabel();
self.updateAriaAttributes();
}, self.options.interval);
this.scope.$on('rzSliderForceRender', function() {
......@@ -615,14 +617,47 @@
},
/**
* Adds accessibility atributes
* Adds accessibility attributes
*
* Run only once during initialization
*
* @returns {undefined}
*/
addAccessibility: function() {
this.sliderElem.attr("role", "slider");
this.minH.attr('role', 'slider');
this.updateAriaAttributes();
if (this.options.keyboardSupport)
this.minH.attr('tabindex', '0');
if (this.options.vertical)
this.minH.attr('aria-orientation', 'vertical');
if (this.range) {
this.maxH.attr('role', 'slider');
if (this.options.keyboardSupport)
this.maxH.attr('tabindex', '0');
if (this.options.vertical)
this.maxH.attr('aria-orientation', 'vertical');
}
},
/**
* Updates aria attributes according to current values
*/
updateAriaAttributes: function() {
this.minH.attr({
'aria-valuenow': this.scope.rzSliderModel,
'aria-valuetext': this.customTrFn(this.scope.rzSliderModel),
'aria-valuemin': this.minValue,
'aria-valuemax': this.maxValue
});
if (this.range) {
this.maxH.attr({
'aria-valuenow': this.scope.rzSliderHigh,
'aria-valuetext': this.customTrFn(this.scope.rzSliderHigh),
'aria-valuemin': this.minValue,
'aria-valuemax': this.maxValue
});
}
},
/**
......@@ -1014,16 +1049,16 @@
* @returns {number}
*/
valueToOffset: function(val) {
return (this.sanitizeOffsetValue(val) - this.minValue) * this.maxPos / this.valueRange || 0;
return (this.sanitizeValue(val) - this.minValue) * this.maxPos / this.valueRange || 0;
},
/**
* Ensure that the position rendered is within the slider bounds, even if the value is not
* Returns a value that is within slider range
*
* @param {number} val
* @returns {number}
*/
sanitizeOffsetValue: function(val) {
sanitizeValue: function(val) {
return Math.min(Math.max(val, this.minValue), this.maxValue);
},
......@@ -1086,6 +1121,16 @@
return Math.abs(offset - this.minH.rzsp) < Math.abs(offset - this.maxH.rzsp) ? this.minH : this.maxH;
},
/**
* Wrapper function to focus an angular element
*
* @param el {AngularElement} the element to focus
*/
focusElement: function(el) {
var DOM_ELEMENT = 0;
el[DOM_ELEMENT].focus();
},
/**
* Bind mouse and touch events to slider handles
*
......@@ -1126,6 +1171,13 @@
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));
if (this.options.keyboardSupport) {
this.minH.on('focus', angular.bind(this, this.onPointerFocus, this.minH, 'rzSliderModel'));
if (this.range) {
this.maxH.on('focus', angular.bind(this, this.onPointerFocus, this.maxH, 'rzSliderHigh'));
}
}
},
/**
......@@ -1156,10 +1208,6 @@
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();
......@@ -1173,6 +1221,9 @@
pointer.addClass('rz-active');
if (this.options.keyboardSupport)
this.focusElement(pointer);
ehMove = angular.bind(this, this.dragging.active ? this.onDragMove : this.onMove, pointer);
ehEnd = angular.bind(this, this.onEnd, ehMove);
......@@ -1210,6 +1261,74 @@
this.positionTrackingHandle(newValue, newOffset);
},
/**
* 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;
if (!this.options.keyboardSupport) {
this.minH.removeClass('rz-active');
this.maxH.removeClass('rz-active');
this.tracking = '';
}
this.dragging.active = false;
$document.off(moveEventName, ehMove);
this.scope.$emit('slideEnded');
this.callOnEnd();
},
onPointerFocus: function(pointer, ref) {
this.tracking = ref;
pointer.one('blur', angular.bind(this, this.onPointerBlur, pointer));
pointer.on('keydown', angular.bind(this, this.onKeyboardEvent));
pointer.addClass('rz-active');
},
onPointerBlur: function(pointer) {
pointer.off('keydown');
this.tracking = '';
pointer.removeClass('rz-active');
},
onKeyboardEvent: function(event) {
var currentValue = this.scope[this.tracking],
keyCode = event.keyCode || event.which,
keys = {
38: 'UP',
40: 'DOWN',
37: 'LEFT',
39: 'RIGHT',
33: 'PAGEUP',
34: 'PAGEDOWN',
36: 'HOME',
35: 'END'
},
actions = {
UP: currentValue + this.step,
DOWN: currentValue - this.step,
LEFT: currentValue - this.step,
RIGHT: currentValue + this.step,
PAGEUP: currentValue + this.valueRange / 10,
PAGEDOWN: currentValue - this.valueRange / 10,
HOME: this.minValue,
END: this.maxValue
},
key = keys[keyCode],
action = actions[key];
if (action == null || this.tracking === '') return;
event.preventDefault();
var newValue = this.roundStep(this.sanitizeValue(action)),
newOffset = this.valueToOffset(newValue);
this.positionTrackingHandle(newValue, newOffset);
},
/**
* onDragStart event handler
*
......@@ -1302,22 +1421,31 @@
*/
positionTrackingHandle: function(newValue, newOffset) {
var valueChanged = false;
var switched = false;
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) {
switched = true;
this.scope[this.tracking] = this.scope.rzSliderHigh;
this.updateHandles(this.tracking, this.maxH.rzsp);
this.updateAriaAttributes();
this.tracking = 'rzSliderHigh';
this.minH.removeClass('rz-active');
this.maxH.addClass('rz-active');
if (this.options.keyboardSupport)
this.focusElement(this.maxH);
valueChanged = true;
} else if (this.tracking === 'rzSliderHigh' && newValue <= this.scope.rzSliderModel) {
switched = true;
this.scope[this.tracking] = this.scope.rzSliderModel;
this.updateHandles(this.tracking, this.minH.rzsp);
this.updateAriaAttributes();
this.tracking = 'rzSliderModel';
this.maxH.removeClass('rz-active');
this.minH.addClass('rz-active');
if (this.options.keyboardSupport)
this.focusElement(this.minH);
valueChanged = true;
}
}
......@@ -1325,6 +1453,7 @@
if (this.scope[this.tracking] !== newValue) {
this.scope[this.tracking] = newValue;
this.updateHandles(this.tracking, newOffset);
this.updateAriaAttributes();
valueChanged = true;
}
......@@ -1332,28 +1461,7 @@
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');
$document.off(moveEventName, ehMove);
this.scope.$emit('slideEnded');
this.tracking = '';
this.dragging.active = false;
this.callOnEnd();
return switched;
},
/**
......
'use strict';
describe('rzslider api', function () {
var RzSlider;
var $rootScope;
var scope;
var $compile;
var element;
var $httpBackend;
beforeEach(module('rzModule'));
beforeEach(module('appTemplates'));
beforeEach(inject(function (_RzSlider_, _$rootScope_, _$compile_, _$httpBackend_) {
RzSlider = _RzSlider_;
$rootScope = _$rootScope_;
$compile = _$compile_;
$httpBackend = _$httpBackend_;
}));
beforeEach(function () {
scope = $rootScope.$new();
scope.minSlider = {value: 10};
scope.rzSliderModel = scope.minSlider.value;
scope.options = {
floor: 0,
ceil: 1000, //defaults to rz-slider-model
step: 100
};
compileHtml();
});
it('should exist compiled', function () {
expect(element.find('span')).to.have.length(11);
});
it('should trigger a left arrow respecting step values and not go below 0', function (done) {
var service = new RzSlider(scope, element);
service.step = 100;
var event = pressLeftArrow();
service.onPointerFocus(element, 'rzSliderModel', event);
service.onKeyboardEvent(event);
expect(scope.rzSliderModel).to.equal(0);
done();
});
function pressLeftArrow() {
var evt = document.createEvent('CustomEvent'); // MUST be 'CustomEvent'
evt.initCustomEvent('keydown', false, false, null);
evt.which = 37;
return evt;
}
function compileHtml() {
element = $compile("<rzslider rz-slider-model='minSlider.value' rz-slider-options='options'></rzslider>")(scope);
scope.$apply();
}
});
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