Commit e26f3227 authored by Rafal Zajac's avatar Rafal Zajac

Initial commit

parents
node_modules/
.idea/
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',
recess: {
options: {
compile: true
},
slider: {
src: ['rzslider.less'],
dest: 'dist/rzslider.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': [
'rzslider.js'
]
}
}
}
});
grunt.loadNpmTasks('grunt-contrib-uglify');
grunt.loadNpmTasks('grunt-recess');
grunt.registerTask('default', ['css', 'js']);
grunt.registerTask('css', ['recess']);
grunt.registerTask('js', ['uglify']);
};
The MIT License (MIT)
Copyright (c) 2013 Rafal Zajac <rzajac@gmail.com>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
## AngularJS slider directive with no external dependencies
Slider directive implementation for AngularJS, without any dependencies.
## Example
```html
<div>
<rzslider
rz-slider-floor="priceSlider.floor"
rz-slider-ceil="priceSlider.ceil"
rz-slider-low="priceSlider.min"
rz-slider-high="priceSlider.max"
rz-slider-step="5"></rzslider>
</div>
```
## License
Licensed under the MIT license
/**
* Angular JS slider directive
*
* (c) Rafal Zajac <rzajac@gmail.com>
* http://github.com/rzajac/angularjs-slider
*
* Licensed under the MIT license
*/
rzslider {
position: relative;
display: inline-block;
width: 100%;
height: 2px;
margin: 30px 0 15px 0;
vertical-align: middle;
}
rzslider span {
position: absolute;
display: inline-block;
white-space: nowrap;
}
rzslider span.base {
width: 100%;
height: 100%;
padding: 0;
}
rzslider span.bar {
z-index: 0;
width: 100%;
height: 100%;
background: #fff;
}
rzslider span.bar.selection {
z-index: 1;
width: 0;
background: #67b700;
}
rzslider span.pointer {
top: -15px;
z-index: 2;
width: 32px;
height: 32px;
cursor: pointer;
background-color: #fff;
-webkit-border-radius: 16px;
-moz-border-radius: 16px;
border-radius: 16px;
}
rzslider span.pointer:after {
position: absolute;
top: 12px;
left: 12px;
width: 8px;
height: 8px;
background: #71818e;
-webkit-border-radius: 4px;
-moz-border-radius: 4px;
border-radius: 4px;
content: '';
}
rzslider span.pointer:hover:after {
background-color: #67b700;
}
rzslider span.pointer.active:after {
background-color: #67b700;
}
rzslider span.bubble {
top: -32px;
padding: 1px 3px 1px 3px;
color: #67b700;
cursor: default;
}
rzslider span.bubble.selection {
top: 15px;
}
rzslider span.bubble.limit {
/*color: #808080;*/
}
\ No newline at end of file
/*! angularjs-slider - v0.0.1 - (c) Rafal Zajac <rzajac@gmail.com>, https://github.com/rzajac/angularjs-slider.git - 2013-12-10 */
rzslider{position:relative;display:inline-block;width:100%;height:2px;margin:30px 0 15px 0;vertical-align:middle}rzslider span{position:absolute;display:inline-block;white-space:nowrap}rzslider span.base{width:100%;height:100%;padding:0}rzslider span.bar{z-index:0;width:100%;height:100%;background:#fff}rzslider span.bar.selection{z-index:1;width:0;background:#67b700}rzslider span.pointer{top:-15px;z-index:2;width:32px;height:32px;cursor:pointer;background-color:#fff;-webkit-border-radius:16px;-moz-border-radius:16px;border-radius:16px}rzslider span.pointer:after{position:absolute;top:12px;left:12px;width:8px;height:8px;background:#71818e;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;content:''}rzslider span.pointer:hover:after{background-color:#67b700}rzslider span.pointer.active:after{background-color:#67b700}rzslider span.bubble{top:-32px;padding:1px 3px 1px 3px;color:#67b700;cursor:default}rzslider span.bubble.selection{top:15px}
\ No newline at end of file
/*! angularjs-slider - v0.0.1 - (c) Rafal Zajac <rzajac@gmail.com>, https://github.com/rzajac/angularjs-slider.git - 2013-12-10 */
angular.module("rzModule",[]).factory("Slider",["$timeout","$document",function(a,b){var c=function(a,b,c){this.scope=a,this.attributes=c,this.element=b,this.range=void 0===c.rzSliderModel&&void 0!==c.rzSliderLow&&void 0!==c.rzSliderHigh,this.refLow=this.range?"rzSliderLow":"rzSliderModel",this.refHigh="rzSliderHigh",this.barWidth=0,this.ptrHalfWidth=0,this.minOffset=0,this.maxOffset=0,this.minValue=0,this.maxValue=0,this.valueRange=0,this.offsetRange=0,this.precision=0,this.step=0,this.tracking="",this.fullBar=null,this.selBar=null,this.minPtr=null,this.maxPtr=null,this.selBub=null,this.flrBub=null,this.ceilBub=null,this.lowBub=null,this.highBub=null,this.cmbBub=null,this.init()};return c.prototype={init:function(){var b=this;void 0===this.attributes.rzSliderTranslate&&(this.scope.rzSliderTranslate=function(a){return a}),this.setMinAndMax(),this.valueRange=this.maxValue-this.minValue,this.precision=void 0===this.scope.rzSliderPrecision?0:+this.scope.rzSliderPrecision,this.step=void 0===this.scope.rzSliderStep?1:+this.scope.rzSliderStep,this.cacheElemHandles(),this.calcViewDimensions(),a(function(){b.setPointers(),b.adjustLabels(),b.bindToInputEvents()}),angular.element(window).on("resize",angular.bind(this,this.calcViewDimensions)),this.scope.$watch(this.refLow,function(){b.setPointers(),b.adjustLabels()}),this.range&&this.scope.$watch(this.refHigh,function(){b.setPointers(),b.adjustLabels()}),void 0!==this.scope.rzSliderFloor&&this.scope.$watch("rzSliderFloor",function(){b.setMinAndMax(),b.setPointers(),b.adjustLabels()}),void 0!==this.scope.rzSliderCeil&&this.scope.$watch("rzSliderCeil",function(){b.setMinAndMax(),b.setPointers(),b.adjustLabels()})},setMinAndMax:function(){this.minValue=void 0===this.scope.rzSliderFloor?0:+this.scope.rzSliderFloor,this.maxValue=void 0===this.scope.rzSliderCeil?this.range?this.scope[this.refHigh]:this.scope[this.refLow]:+this.scope.rzSliderCeil},cacheElemHandles:function(){angular.forEach(this.element.children(),function(a,b){var c=angular.element(a);switch(b){case 0:this.fullBar=c;break;case 1:this.selBar=c;break;case 2:this.minPtr=c;break;case 3:this.maxPtr=c;break;case 4:this.selBub=c;break;case 5:this.flrBub=c;break;case 6:this.ceilBub=c;break;case 7:this.lowBub=c;break;case 8:this.highBub=c;break;case 9:this.cmbBub=c}},this)},calcViewDimensions:function(){var a=this.offsetWidth(this.minPtr);this.ptrHalfWidth=a/2,this.barWidth=this.offsetWidth(this.fullBar),this.minOffset=0,this.maxOffset=this.barWidth-a,this.offsetRange=this.maxOffset-this.minOffset},setPointers:function(){var a,b,c,d,e,f;this.setLeft(this.ceilBub,this.barWidth-this.offsetWidth(this.ceilBub)),b=this.percentValue(this.scope[this.refLow]),c=this.setLeft(this.minPtr,this.percentToOffset(b)),this.setLeft(this.lowBub,c-this.halfOffsetWidth(this.lowBub)+this.ptrHalfWidth),this.range&&(a=this.percentValue(this.scope[this.refHigh]),d=this.setLeft(this.maxPtr,this.percentToOffset(a)),this.setLeft(this.highBub,d-this.halfOffsetWidth(this.highBub)+this.ptrHalfWidth),e=this.setLeft(this.selBar,c+this.ptrHalfWidth),f=this.percentToOffset(a-b),this.selBar.css({width:f+"px"}),this.setLeft(this.cmbBub,e+f/2-this.halfOffsetWidth(this.cmbBub)+1),this.setLeft(this.selBub,e+f/2-this.halfOffsetWidth(this.selBub)+1),this.scope.rzSliderDiff=this.roundStep(this.scope[this.refHigh]-this.scope[this.refLow]))},adjustLabels:function(){var a=this.highBub;this.fitToBar(this.lowBub),this.range&&(this.fitToBar(this.highBub),this.fitToBar(this.selBub),this.gap(this.lowBub,this.highBub)<10?(this.hideEl(this.lowBub),this.hideEl(this.highBub),this.fitToBar(this.cmbBub),this.showEl(this.cmbBub),a=this.cmbBub):(this.showEl(this.lowBub),this.showEl(this.highBub),this.hideEl(this.cmbBub),a=this.highBub)),this.gap(this.flrBub,this.lowBub)<5?this.hideEl(this.flrBub):this.range?this.gap(this.flrBub,a)<5?this.hideEl(this.flrBub):this.showEl(this.flrBub):this.showEl(this.flrBub),this.gap(this.lowBub,this.ceilBub)<5?this.hideEl(this.ceilBub):this.range?this.gap(a,this.ceilBub)<5?this.hideEl(this.ceilBub):this.showEl(this.ceilBub):this.showEl(this.ceilBub)},roundStep:function(a){var b=this.step,c=Math.pow(10,this.precision),d=(a-this.minValue)%b,e=d>b/2?a+b-d:a-d;return+(e*c/c).toFixed(this.precision)},hideEl:function(a){return a.css({opacity:0})},showEl:function(a){return a.css({opacity:1})},offsetLeft:function(a){return a[0].offsetLeft},offsetWidth:function(a){return a[0].offsetWidth},halfOffsetWidth:function(a){return a[0].offsetWidth/2},setLeft:function(a,b){return a.css({left:b+"px"}),b},fitToBar:function(a){this.setLeft(a,Math.min(Math.max(0,this.offsetLeft(a)),this.barWidth-this.offsetWidth(a)))},gap:function(a,b){return this.offsetLeft(b)-this.offsetLeft(a)-this.offsetWidth(a)},percentValue:function(a){return(a-this.minValue)/this.valueRange*100},percentOffset:function(a){return(a-this.minOffset)/this.offsetRange*100},percentToOffset:function(a){return a*this.offsetRange/100},bindToInputEvents:function(){this.minPtr.on("mousedown",angular.bind(this,this.onStart,this.minPtr,this.refLow)),this.range&&this.maxPtr.on("mousedown",angular.bind(this,this.onStart,this.maxPtr,this.refHigh)),this.minPtr.on("touchstart",angular.bind(this,this.onStart,this.minPtr,this.refLow)),this.range&&this.maxPtr.on("touchstart",angular.bind(this,this.onStart,this.maxPtr,this.refHigh))},onStart:function(a,c,d){""===this.tracking&&(this.tracking=c,a.addClass("active"),d.stopPropagation(),d.preventDefault(),d.touches?(b.on("touchmove",angular.bind(this,this.onMove)),b.on("touchend",angular.bind(this,this.onEnd,a))):(b.on("mousemove",angular.bind(this,this.onMove)),b.on("mouseup",angular.bind(this,this.onEnd,a))))},onMove:function(a){var b,c,d,e=a.clientX||a.touches[0].clientX;b=e-this.element[0].getBoundingClientRect().left-this.ptrHalfWidth,b=Math.max(Math.min(b,this.maxOffset),this.minOffset),c=this.percentOffset(b),d=this.minValue+this.valueRange*c/100,this.range&&(this.tracking===this.refLow&&d>=this.scope[this.refHigh]?(this.tracking=this.refHigh,this.minPtr.removeClass("active"),this.maxPtr.addClass("active")):d<=this.scope[this.refLow]&&(this.tracking=this.refLow,this.maxPtr.removeClass("active"),this.minPtr.addClass("active"))),this.scope[this.tracking]=this.roundStep(d),this.setPointers(),this.adjustLabels(),this.scope.$apply()},onEnd:function(a,c){a.removeClass("active"),c.touches?(b.unbind("touchmove"),b.unbind("touchend")):(b.unbind("mousemove"),b.unbind("mouseup")),this.tracking=""}},c}]).directive("rzslider",["Slider",function(a){return{restrict:"E",scope:{rzSliderFloor:"=?",rzSliderCeil:"=?",rzSliderStep:"@",rzSliderPrecision:"@",rzSliderModel:"=?",rzSliderLow:"=?",rzSliderHigh:"=?",rzSliderTranslate:"&"},template:'<span class="bar"></span><span class="bar selection"></span><span class="pointer"></span><span class="pointer"></span><span class="bubble selection"></span><span class="bubble limit" ng-bind="rzSliderTranslate(rzSliderFloor)"></span><span class="bubble limit" ng-bind="rzSliderTranslate(rzSliderCeil)" class="bubble limit"></span><span class="bubble"></span><span class="bubble"></span><span class="bubble"></span>',compile:function(b,c){var d=b.children(),e=void 0===c.rzSliderModel&&void 0!==c.rzSliderLow&&void 0!==c.rzSliderHigh,f=e?"rzSliderLow":"rzSliderModel",g="rzSliderHigh";return c.rzSliderTranslate&&c.$set("rzSliderTranslate",""+c.rzSliderTranslate+"(value)"),angular.element(d[4]).attr("ng-bind","rzSliderTranslate(rzSliderDiff)"),angular.element(d[7]).attr("ng-bind","rzSliderTranslate("+f+")"),angular.element(d[8]).attr("ng-bind","rzSliderTranslate("+g+")"),angular.element(d[9]).attr("ng-bind-html","rzSliderTranslate("+f+') + " - " + rzSliderTranslate('+g+")"),{post:function(b,c,d){return console.log(b),new a(b,c,d)}}}}}]);
\ No newline at end of file
{
"name": "angularjs-slider",
"version": "0.0.1",
"description": "Slider directive for AngularJS. No dependencies.",
"main": "rzslider.js",
"repository": {
"type": "git",
"url": "https://github.com/rzajac/angularjs-slider.git"
},
"keywords": [
"angular",
"slider"
],
"devDependencies": {
"grunt": "~0.4.2",
"grunt-contrib-uglify": "~0.2.2",
"grunt-contrib-mincss": "~0.3.2",
"grunt-recess": "~0.4.0",
"recess": "~1.1.9"
},
"author": "Rafal Zajac <rzajac@gmail.com>",
"license": "MIT",
"readmeFilename": "README.md"
}
/**
* Angular JS slider directive
*
* (c) Rafal Zajac <rzajac@gmail.com>
* http://github.com/rzajac/angularjs-slider
*
* Licensed under the MIT license
*/
/* global angular: false */
angular.module('rzModule', [])
.factory('Slider', ['$timeout', '$document', function($timeout, $document)
{
/**
* Slider
*
* @param {*} scope The AngularJS scope
* @param {Element} element The slider directive element wrapped in jqLite
* @param {*} attributes The slider directive attributes
* @constructor
*/
var Slider = function(scope, element, attributes)
{
/**
* The slider's scope
*
* @type {*}
*/
this.scope = scope;
/**
* The slider attributes
*
* @type {*}
*/
this.attributes = attributes;
/**
* Slider element wrapped in jqLite
*
* @type {Element}
*/
this.element = element;
/**
* Slider type
*
* @type {string}
*/
this.range = (attributes.rzSliderModel === undefined) && ((attributes.rzSliderLow !== undefined) && (attributes.rzSliderHigh !== undefined));
/**
* Name of the low value
*
* @type {string}
*/
this.refLow = this.range ? 'rzSliderLow' : 'rzSliderModel';
/**
* Name of the high value
*
* @type {string}
*/
this.refHigh = 'rzSliderHigh';
/**
* The total width of slider bar
*
* @type {number}
*/
this.barWidth = 0;
/**
* Half of the width of the slider handles
*
* @type {number}
*/
this.ptrHalfWidth = 0;
/**
* Minimum left offset the slider handle can have
*
* @type {number}
*/
this.minOffset = 0;
/**
* Maximum left offset the slider handle can have
*
* @type {number}
*/
this.maxOffset = 0;
/**
* Minimum value (floor)
*
* @type {number}
*/
this.minValue = 0;
/**
* Maximum value (ceiling)
*
* @type {number}
*/
this.maxValue = 0;
/**
* The delta between min and max value
*
* @type {number}
*/
this.valueRange = 0;
/**
* The delta between min and max left offset
*
* @type {number}
*/
this.offsetRange = 0;
/**
* Precision
*
* @type {number}
*/
this.precision = 0;
/**
* Step
*
* @type {number}
*/
this.step = 0;
/**
* The name of the handle we are currently tracking
*
* @type {string}
*/
this.tracking = '';
// Slider DOM elements
this.fullBar = null; // The whole slider bar
this.selBar = null; // Highlight between two handles
this.minPtr = null; // Left slider handle
this.maxPtr = null; // Right slider handle
this.selBub = null; // Range bubble
this.flrBub = null; // Floor label
this.ceilBub = null; // Ceiling label
this.lowBub = null; // Label above the low value
this.highBub = null; // Label above the high value
this.cmbBub = null; // Combined label
// Initialize slider
this.init();
};
// Add instance methods
Slider.prototype = {
/**
* Initialize slider
*/
init: function()
{
var self = this;
// Provide default translate function if needed
if (this.attributes.rzSliderTranslate === undefined)
{
this.scope.rzSliderTranslate = function (value)
{
return value;
};
}
this.setMinAndMax();
this.valueRange = this.maxValue - this.minValue;
this.precision = this.scope.rzSliderPrecision === undefined ? 0 : +this.scope.rzSliderPrecision;
this.step = this.scope.rzSliderStep === undefined ? 1 : +this.scope.rzSliderStep;
this.cacheElemHandles();
this.calcViewDimensions();
$timeout(function()
{
self.setPointers();
self.adjustLabels();
self.bindToInputEvents();
});
// Recalculate stuff if view port dimensions have changed
angular.element(window).on('resize', angular.bind(this, this.calcViewDimensions));
// Watch for changes to the model
this.scope.$watch(this.refLow, function()
{
self.setPointers();
self.adjustLabels();
});
if(this.range)
{
// We have to watch it only for range slider
this.scope.$watch(this.refHigh, function()
{
self.setPointers();
self.adjustLabels();
});
}
// We do not have to watch it if it was not defined
if(this.scope.rzSliderFloor !== undefined)
{
this.scope.$watch('rzSliderFloor', function()
{
self.setMinAndMax();
self.setPointers();
self.adjustLabels();
});
}
// We do not have to watch it if it was not defined
if(this.scope.rzSliderCeil !== undefined)
{
this.scope.$watch('rzSliderCeil', function()
{
self.setMinAndMax();
self.setPointers();
self.adjustLabels();
});
}
},
/**
* Set maximum and minimum values for the slider
*/
setMinAndMax: function()
{
this.minValue = this.scope.rzSliderFloor === undefined ? 0 : +this.scope.rzSliderFloor;
if(this.scope.rzSliderCeil === undefined)
{
this.maxValue = this.range ? this.scope[this.refHigh] : this.scope[this.refLow];
}
else
{
this.maxValue = +this.scope.rzSliderCeil;
}
},
/**
* Set the slider children to variables for easy access
*/
cacheElemHandles: function()
{
angular.forEach(this.element.children(), function(elem, index)
{
var _elem = angular.element(elem);
switch(index)
{
case 0:
this.fullBar = _elem;
break;
case 1:
this.selBar = _elem;
break;
case 2:
this.minPtr = _elem;
break;
case 3:
this.maxPtr = _elem;
break;
case 4:
this.selBub = _elem;
break;
case 5:
this.flrBub = _elem;
break;
case 6:
this.ceilBub = _elem;
break;
case 7:
this.lowBub = _elem;
break;
case 8:
this.highBub = _elem;
break;
case 9:
this.cmbBub = _elem;
break;
}
}, this);
},
/**
* Calculate dimensions that are dependent on window size
*/
calcViewDimensions: function ()
{
var pointerWidth = this.offsetWidth(this.minPtr);
this.ptrHalfWidth = pointerWidth / 2;
this.barWidth = this.offsetWidth(this.fullBar);
this.minOffset = 0;
this.maxOffset = this.barWidth - pointerWidth;
this.offsetRange = this.maxOffset - this.minOffset;
},
/**
* Set positions of slider handles, labels and selection bar
*/
setPointers: function()
{
var newHighValue, newLowValue, minPtrOL, maxPtrOL, selBarOL, selBarWidth;
this.setLeft(this.ceilBub, this.barWidth - this.offsetWidth(this.ceilBub));
newLowValue = this.percentValue(this.scope[this.refLow]);
// Set low value slider handle position
minPtrOL = this.setLeft(this.minPtr, this.percentToOffset(newLowValue));
// Set low value label position
this.setLeft(this.lowBub, minPtrOL - this.halfOffsetWidth(this.lowBub) + this.ptrHalfWidth);
if (this.range)
{
newHighValue = this.percentValue(this.scope[this.refHigh]);
// Set high value slider handle position
maxPtrOL = this.setLeft(this.maxPtr, this.percentToOffset(newHighValue));
// Set high value slider handle label position
this.setLeft(this.highBub, maxPtrOL - (this.halfOffsetWidth(this.highBub)) + this.ptrHalfWidth);
// Set selection bar position
selBarOL = this.setLeft(this.selBar, minPtrOL + this.ptrHalfWidth);
selBarWidth = this.percentToOffset(newHighValue - newLowValue);
this.selBar.css({width: selBarWidth + 'px'});
// Set combined label position
this.setLeft(this.cmbBub, selBarOL + selBarWidth / 2 - this.halfOffsetWidth(this.cmbBub) + 1);
// Set range label position
this.setLeft(this.selBub, selBarOL + selBarWidth / 2 - this.halfOffsetWidth(this.selBub) + 1);
this.scope.rzSliderDiff = this.roundStep(this.scope[this.refHigh] - this.scope[this.refLow]);
}
},
/**
* Adjust label positions
*/
adjustLabels: function ()
{
var bubToAdjust = this.highBub;
this.fitToBar(this.lowBub);
if (this.range)
{
this.fitToBar(this.highBub);
this.fitToBar(this.selBub);
if (this.gap(this.lowBub, this.highBub) < 10)
{
this.hideEl(this.lowBub);
this.hideEl(this.highBub);
this.fitToBar(this.cmbBub);
this.showEl(this.cmbBub);
bubToAdjust = this.cmbBub;
}
else
{
this.showEl(this.lowBub);
this.showEl(this.highBub);
this.hideEl(this.cmbBub);
bubToAdjust = this.highBub;
}
}
if (this.gap(this.flrBub, this.lowBub) < 5)
{
this.hideEl(this.flrBub);
}
else
{
if (this.range)
{
if (this.gap(this.flrBub, bubToAdjust) < 5)
{
this.hideEl(this.flrBub);
}
else
{
this.showEl(this.flrBub);
}
}
else
{
this.showEl(this.flrBub);
}
}
if (this.gap(this.lowBub, this.ceilBub) < 5)
{
this.hideEl(this.ceilBub);
}
else
{
if (this.range)
{
if (this.gap(bubToAdjust, this.ceilBub) < 5)
{
this.hideEl(this.ceilBub);
}
else
{
this.showEl(this.ceilBub);
}
}
else
{
this.showEl(this.ceilBub);
}
}
},
/**
* Round value to step and precision
*
* @param {number} value
* @returns {number}
*/
roundStep: function(value)
{
var step = this.step,
decimals = Math.pow(10, this.precision),
remainder = (value - this.minValue) % step,
steppedValue = remainder > (step / 2) ? value + step - remainder : value - remainder;
return +(steppedValue * decimals / decimals).toFixed(this.precision);
},
/**
* Hide element
*
* @param element
* @returns {jQlite} The jqLite
*/
hideEl: function (element)
{
return element.css({opacity: 0});
},
/**
* Show element
*
* @param element
* @returns {jQlite} The jqLite
*/
showEl: function (element)
{
return element.css({opacity: 1});
},
/**
* Get element's offsetLeft
*
* @param element The jqLite wrapped DOM element
* @returns {number}
*/
offsetLeft: function (element)
{
return element[0].offsetLeft;
},
/**
* Get element's offsetWidth
*
* @param element The jqLite wrapped DOM element
* @returns {number}
*/
offsetWidth: function (element)
{
return element[0].offsetWidth;
},
/**
* Get element's offsetWidth / 2
*
* @param element The jqLite wrapped DOM element
* @returns {number}
*/
halfOffsetWidth: function (element)
{
return element[0].offsetWidth / 2;
},
/**
* Set element left offset
*
* @param element The jqLite wrapped DOM element
* @param {number} left
* @returns {number}
*/
setLeft: function (element, left)
{
element.css({left: left + 'px'});
return left;
},
/**
* Fit element into slider bar
*
* @param element The jqLite wrapped DOM element
*/
fitToBar: function (element)
{
this.setLeft(element, Math.min(Math.max(0, this.offsetLeft(element)), this.barWidth - this.offsetWidth(element)));
},
/**
* Get gap in pixels between two elements accounting for elements widths
*
* @param element1 The jqLite wrapped DOM element
* @param element2 The jqLite wrapped DOM element
* @returns {number}
*/
gap: function (element1, element2)
{
return this.offsetLeft(element2) - this.offsetLeft(element1) - this.offsetWidth(element1);
},
/**
* Translate value to percent (between min and max value)
*
* @param value
* @returns {number}
*/
percentValue: function (value)
{
return ((value - this.minValue) / this.valueRange) * 100;
},
/**
* Translate left offset in pixels to percentages
*
* @param {number} offset The left offset
* @returns {number}
*/
percentOffset: function (offset)
{
return ((offset - this.minOffset) / this.offsetRange) * 100;
},
/**
* Translate left offset in percentages to pixels
*
* @param {number} percent The value in percentages
* @returns {number} The left offset
*/
percentToOffset: function (percent)
{
return percent * this.offsetRange / 100;
},
// Events
/**
* Bind mouse and touch events to slider handles
*/
bindToInputEvents: function()
{
this.minPtr.on('mousedown', angular.bind(this, this.onStart, this.minPtr, this.refLow));
if(this.range) { this.maxPtr.on('mousedown', angular.bind(this, this.onStart, this.maxPtr, this.refHigh)) }
this.minPtr.on('touchstart', angular.bind(this, this.onStart, this.minPtr, this.refLow));
if(this.range) { this.maxPtr.on('touchstart', angular.bind(this, this.onStart, this.maxPtr, this.refHigh)) }
},
/**
* onStart event handler
*
* @param {Object} pointer The jqLite wrapped DOM element
* @param {string} ref One of the refLow, refHigh
* @param {Event} event The event
*/
onStart: function (pointer, ref, event)
{
if(this.tracking !== '') { return }
this.tracking = ref;
pointer.addClass('active');
event.stopPropagation();
event.preventDefault();
if(event.touches)
{
$document.on('touchmove', angular.bind(this, this.onMove));
$document.on('touchend', angular.bind(this, this.onEnd, pointer));
}
else
{
$document.on('mousemove', angular.bind(this, this.onMove));
$document.on('mouseup', angular.bind(this, this.onEnd, pointer));
}
},
/**
* onMove event handler
*
* @param {Event} event The event
*/
onMove: function (event)
{
var eventX = event.clientX || event.touches[0].clientX,
newOffset, newPercent, newValue;
newOffset = eventX - this.element[0].getBoundingClientRect().left - this.ptrHalfWidth;
newOffset = Math.max(Math.min(newOffset, this.maxOffset), this.minOffset);
newPercent = this.percentOffset(newOffset);
newValue = this.minValue + (this.valueRange * newPercent / 100.0);
if (this.range)
{
if (this.tracking === this.refLow && newValue >= this.scope[this.refHigh])
{
this.tracking = this.refHigh;
this.minPtr.removeClass('active');
this.maxPtr.addClass('active');
}
else if(newValue <= this.scope[this.refLow])
{
this.tracking = this.refLow;
this.maxPtr.removeClass('active');
this.minPtr.addClass('active');
}
}
this.scope[this.tracking] = this.roundStep(newValue);
this.setPointers();
this.adjustLabels();
this.scope.$apply();
},
/**
* onEnd event handler
*
* @param {Object} pointer The jqLite wrapped DOM element
* @param {Event} event The event
*/
onEnd: function(pointer, event)
{
pointer.removeClass('active');
if(event.touches)
{
$document.unbind('touchmove');
$document.unbind('touchend');
}
else
{
$document.unbind('mousemove');
$document.unbind('mouseup');
}
this.tracking = '';
}
};
return Slider;
}])
.directive('rzslider', ['Slider', function(Slider)
{
return {
restrict: 'E',
scope: {
rzSliderFloor: '=?',
rzSliderCeil: '=?',
rzSliderStep: '@',
rzSliderPrecision: '@',
rzSliderModel: '=?',
rzSliderLow: '=?',
rzSliderHigh: '=?',
rzSliderTranslate: '&'
},
template: '<span class="bar"></span>' + // 0 The slider bar
'<span class="bar selection"></span>' + // 1 Highlight between two handles
'<span class="pointer"></span>' + // 2 Left slider handle
'<span class="pointer"></span>' + // 3 Right slider handle
'<span class="bubble selection"></span>' + // 4 Range label
'<span class="bubble limit" ng-bind="rzSliderTranslate(rzSliderFloor)"></span>' + // 5 Floor label
'<span class="bubble limit" ng-bind="rzSliderTranslate(rzSliderCeil)" class="bubble limit"></span>' + // 6 Ceiling label
'<span class="bubble"></span>' + // 7 Label above left slider handle
'<span class="bubble"></span>' + // 8 Label above right slider handle
'<span class="bubble"></span>', // 9 Range label when the slider handles are close ex. 15 - 17
compile: function (element, attributes)
{
var
// The slider child elements
children = element.children(),
range = (attributes.rzSliderModel === undefined) && ((attributes.rzSliderLow !== undefined) && (attributes.rzSliderHigh !== undefined)),
refLow = range ? 'rzSliderLow' : 'rzSliderModel',
refHigh = 'rzSliderHigh';
// Set attribute value to function call
if (attributes.rzSliderTranslate)
{
attributes.$set('rzSliderTranslate', '' + attributes.rzSliderTranslate + '(value)');
}
// Range label
angular.element(children[4]).attr('ng-bind', 'rzSliderTranslate(rzSliderDiff)');
// Label above low slider
angular.element(children[7]).attr('ng-bind', 'rzSliderTranslate(' + refLow + ')');
// Label above high slider
angular.element(children[8]).attr('ng-bind', 'rzSliderTranslate(' + refHigh + ')');
// Combined label for low and high
angular.element(children[9]).attr('ng-bind-html', 'rzSliderTranslate(' + refLow + ') + " - " + rzSliderTranslate(' + refHigh + ')');
return {
post: function(scope, elem, attr)
{
console.log(scope); // TODO: remove this!
return new Slider(scope, elem, attr);
}
};
}
};
}]);
/**
* Angular JS slider directive
*
* (c) Rafal Zajac <rzajac@gmail.com>
* http://github.com/rzajac/angularjs-slider
*
* Licensed under the MIT license
*/
.rounded(@radius: 2px) {
-webkit-border-radius: @radius;
-moz-border-radius: @radius;
border-radius: @radius;
}
rzslider {
display: inline-block;
position: relative;
height: 2px;
width: 100%;
margin: 30px 0 15px 0;
vertical-align: middle;
}
rzslider span {
white-space: nowrap;
position: absolute;
display: inline-block;
}
rzslider span.base {
width: 100%;
height: 100%;
padding: 0;
}
rzslider span.bar {
width: 100%;
height: 100%;
z-index: 0;
background: #fff;
}
rzslider span.bar.selection {
width: 0%;
z-index: 1;
background: #67b700;
}
rzslider span.pointer {
cursor: pointer;
width: 32px;
height: 32px;
top: -15px;
background-color: #fff;
z-index: 2;
.rounded(16px);
}
rzslider span.pointer:after {
content: '';
width: 8px;
height: 8px;
position: absolute;
top: 12px;
left: 12px;
.rounded(4px);
background: #71818e;
}
rzslider span.pointer:hover:after {
background-color: #67b700;
}
rzslider span.pointer.active:after {
background-color: #67b700;
}
rzslider span.bubble {
cursor: default;
top: -32px;
padding: 1px 3px 1px 3px;
color: #67b700;
}
rzslider span.bubble.selection {
top: 15px;
}
rzslider span.bubble.limit {
/*color: #808080;*/
}
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