From 128b0008eef797050cf5146fb1dd69505c4439d4 Mon Sep 17 00:00:00 2001 From: Stefan Parviainen Date: Mon, 5 Jan 2015 18:30:12 +0100 Subject: Replace jslider with jRange --- library/jRange/jquery.range.js | 297 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 297 insertions(+) create mode 100644 library/jRange/jquery.range.js (limited to 'library/jRange/jquery.range.js') diff --git a/library/jRange/jquery.range.js b/library/jRange/jquery.range.js new file mode 100644 index 000000000..978b3e7ba --- /dev/null +++ b/library/jRange/jquery.range.js @@ -0,0 +1,297 @@ +/*jshint multistr:true, curly: false */ +/*global jQuery:false, define: false */ +/** + * jRange - Awesome range control + * + * Written by + * ---------- + * Nitin Hayaran (nitinhayaran@gmail.com) + * + * Licensed under the MIT (MIT-LICENSE.txt). + * + * @author Nitin Hayaran + * @version 0.1-RELEASE + * + * Dependencies + * ------------ + * jQuery (http://jquery.com) + * + **/ + ; + (function($, window, document, undefined) { + 'use strict'; + + var jRange = function(){ + return this.init.apply(this, arguments); + }; + jRange.prototype = { + defaults : { + onstatechange : function(){}, + isRange : false, + showLabels : true, + showScale : true, + step : 1, + format: '%s', + theme : 'theme-green', + width : 300 + }, + template : '
\ +
\ +
\ +
123456
\ +
456789
\ +
\ +
\ +
\ +
', + init : function(node, options){ + this.options = $.extend({}, this.defaults, options); + this.inputNode = $(node); + this.options.value = this.inputNode.val() || (this.options.isRange ? this.options.from+','+this.options.from : this.options.from); + this.domNode = $(this.template); + this.domNode.addClass(this.options.theme); + this.inputNode.after(this.domNode); + this.domNode.on('change', this.onChange); + this.pointers = $('.pointer', this.domNode); + this.lowPointer = this.pointers.first(); + this.highPointer = this.pointers.last(); + this.labels = $('.pointer-label', this.domNode); + this.lowLabel = this.labels.first(); + this.highLabel = this.labels.last(); + this.scale = $('.scale', this.domNode); + this.bar = $('.selected-bar', this.domNode); + this.clickableBar = this.domNode.find('.clickable-dummy'); + this.interval = this.options.to - this.options.from; + this.render(); + }, + render: function(){ + // Check if inputNode is visible, and have some width, so that we can set slider width accordingly. + if( this.inputNode.width() === 0 && !this.options.width ){ + console.log('jRange : no width found, returning'); + return; + }else{ + this.domNode.width( this.options.width || this.inputNode.width() ); + this.inputNode.hide(); + } + + if(this.isSingle()){ + this.lowPointer.hide(); + this.lowLabel.hide(); + } + if(!this.options.showLabels){ + this.labels.hide(); + } + this.attachEvents(); + if(this.options.showScale){ + this.renderScale(); + } + this.setValue(this.options.value); + }, + isSingle: function(){ + if(typeof(this.options.value) === 'number'){ + return true; + } + return (this.options.value.indexOf(',') !== -1 || this.options.isRange) ? + false : true; + }, + attachEvents: function(){ + this.clickableBar.click($.proxy(this.barClicked, this)); + this.pointers.mousedown($.proxy(this.onDragStart, this)); + this.pointers.bind('dragstart', function(event) { event.preventDefault(); }); + }, + onDragStart: function(e){ + if(e.which !== 1){return;} + e.stopPropagation(); e.preventDefault(); + var pointer = $(e.target); + pointer.addClass('focused'); + this[(pointer.hasClass('low')?'low':'high') + 'Label'].addClass('focused'); + $(document).on('mousemove.slider', $.proxy(this.onDrag, this, pointer)); + $(document).on('mouseup.slider', $.proxy(this.onDragEnd, this)); + }, + onDrag: function(pointer, e){ + e.stopPropagation(); e.preventDefault(); + var position = e.clientX - this.domNode.offset().left; + this.domNode.trigger('change', [this, pointer, position]); + }, + onDragEnd: function(){ + this.pointers.removeClass('focused'); + this.labels.removeClass('focused'); + $(document).off('.slider'); + $(document).off('.slider'); + }, + barClicked: function(e){ + var x = e.pageX - this.clickableBar.offset().left; + if(this.isSingle()) + this.setPosition(this.pointers.last(), x, true, true); + else{ + var pointer = Math.abs(parseInt(this.pointers.first().css('left'), 10) - x + this.pointers.first().width() / 2) < Math.abs(parseInt(this.pointers.last().css('left'), 10) - x + this.pointers.first().width() / 2) ? + this.pointers.first() : this.pointers.last(); + this.setPosition(pointer, x, true, true); + } + }, + onChange: function(e, self, pointer, position){ + var min, max; + if(self.isSingle()){ + min = 0; + max = self.domNode.width(); + }else{ + min = pointer.hasClass('high')? self.lowPointer.position().left + self.lowPointer.width() / 2 : 0; + max = pointer.hasClass('low') ? self.highPointer.position().left + self.highPointer.width() / 2 : self.domNode.width(); + } + var value = Math.min(Math.max(position, min), max); + self.setPosition(pointer, value, true); + }, + setPosition: function(pointer, position, isPx, animate){ + var leftPos, + lowPos = this.lowPointer.position().left, + highPos = this.highPointer.position().left, + circleWidth = this.highPointer.width() / 2; + if(!isPx){ + position = this.prcToPx(position); + } + if(pointer[0] === this.highPointer[0]){ + highPos = Math.round(position - circleWidth); + }else{ + lowPos = Math.round(position - circleWidth); + } + pointer[animate?'animate':'css']({'left': Math.round(position - circleWidth)}); + if(this.isSingle()){ + leftPos = 0; + }else{ + leftPos = lowPos + circleWidth; + } + this.bar[animate?'animate':'css']({ + 'width' : Math.round(highPos + circleWidth - leftPos), + 'left' : leftPos + }); + this.showPointerValue(pointer, position, animate); + }, + // will be called from outside + setValue: function(value){ + var values = value.toString().split(','); + this.options.value = value; + var prc = this.valuesToPrc( values.length === 2 ? values : [0, values[0]] ); + if(this.isSingle()){ + this.setPosition(this.highPointer, prc[1]); + }else{ + this.setPosition(this.lowPointer, prc[0]); + this.setPosition(this.highPointer, prc[1]); + } + }, + renderScale: function(){ + var s = this.options.scale || [this.options.from, this.options.to]; + var prc = Math.round((100 / (s.length - 1)) * 10) / 10; + var str = ''; + for(var i = 0; i < s.length ; i++ ){ + str += '' + (s[i] != '|' ? '' + s[i] + '' : '') + ''; + } + this.scale.html(str); + + $('ins', this.scale).each(function () { + $(this).css({ + marginLeft: -$(this).outerWidth() / 2 + }); + }); + }, + getBarWidth: function(){ + var values = this.options.value.split(','); + if(values.length > 1){ + return parseInt(values[1], 10) - parseInt(values[0], 10); + }else{ + return parseInt(values[0], 10); + } + }, + showPointerValue: function(pointer, position, animate){ + var label = $('.pointer-label', this.domNode)[pointer.hasClass('low')?'first':'last'](); + var text; + var value = this.positionToValue(position); + if($.isFunction(this.options.format)){ + var type = this.isSingle() ? undefined : (pointer.hasClass('low') ? 'low':'high'); + text = this.options.format(value, type); + }else{ + text = this.options.format.replace('%s', value); + } + + var width = label.html(text).width(), + left = position - width / 2; + left = Math.min(Math.max(left, 0), this.options.width - width); + label[animate?'animate':'css']({left: left}); + this.setInputValue(pointer, value); + }, + valuesToPrc: function(values){ + var lowPrc = ( ( values[0] - this.options.from ) * 100 / this.interval ), + highPrc = ( ( values[1] - this.options.from ) * 100 / this.interval ); + return [lowPrc, highPrc]; + }, + prcToPx: function(prc){ + return (this.domNode.width() * prc) / 100; + }, + positionToValue: function(pos){ + var value = (pos / this.domNode.width()) * this.interval; + value = value + this.options.from; + return Math.round(value / this.options.step) * this.options.step; + }, + setInputValue: function(pointer, v){ + // if(!isChanged) return; + if(this.isSingle()){ + this.options.value = v.toString(); + }else{ + var values = this.options.value.split(','); + if(pointer.hasClass('low')){ + this.options.value = v + ',' + values[1]; + }else{ + this.options.value = values[0] + ',' + v; + } + } + if( this.inputNode.val() !== this.options.value ){ + this.inputNode.val(this.options.value); + this.options.onstatechange.call(this, this.options.value); + } + }, + getValue: function(){ + return this.options.value; + } + }; + + /*$.jRange = function (node, options) { + var jNode = $(node); + if(!jNode.data('jrange')){ + jNode.data('jrange', new jRange(node, options)); + } + return jNode.data('jrange'); + }; + + $.fn.jRange = function (options) { + return this.each(function(){ + $.jRange(this, options); + }); + };*/ + + var pluginName = 'jRange'; + // A really lightweight plugin wrapper around the constructor, + // preventing against multiple instantiations + $.fn[pluginName] = function(option) { + var args = arguments, + result; + + this.each(function() { + var $this = $(this), + data = $.data(this, 'plugin_' + pluginName), + options = typeof option === 'object' && option; + if (!data) { + $this.data('plugin_' + pluginName, (data = new jRange(this, options))); + } + // if first argument is a string, call silimarly named function + // this gives flexibility to call functions of the plugin e.g. + // - $('.dial').plugin('destroy'); + // - $('.dial').plugin('render', $('.new-child')); + if (typeof option === 'string') { + result = data[option].apply(data, Array.prototype.slice.call(args, 1)); + } + }); + + // To enable plugin returns values + return result || this; + }; + +})(jQuery, window, document); \ No newline at end of file -- cgit v1.2.3