1030
|
1 /*
|
|
2 * jQuery.range - A tiny, easily styleable range selector
|
|
3 * Tom Moor, http://tommoor.com
|
|
4 * Copyright (c) 2011 Tom Moor
|
|
5 * MIT Licensed
|
|
6 * @version 1.0
|
|
7 */
|
|
8
|
|
9 (function($){
|
|
10
|
|
11 var TinyRange = function(){
|
|
12 // locals
|
|
13 var options;
|
|
14 var $input;
|
|
15 var $rail;
|
|
16 var $handle;
|
|
17 var $handle2;
|
|
18 var $selection;
|
|
19 var $dragging;
|
|
20 var $original;
|
|
21 var jump;
|
|
22 var size;
|
|
23 var defaults = {
|
|
24 orientation: 'horizontal', // todo
|
|
25 range: false,
|
|
26 values: false,
|
|
27 snap: false,
|
|
28 change: null,
|
|
29 blur: null
|
|
30 };
|
|
31
|
|
32 var jumpHandle = function(ev) {
|
|
33 ev.pageX = ev.pageX - $input.offset().left;
|
|
34
|
|
35 // get closest handle
|
|
36 var x1 = $handle.position().left;
|
|
37 var dist = ev.pageX - x1;
|
|
38
|
|
39 if($handle2){
|
|
40 var x2 = $handle2.position().left;
|
|
41 var dist2 = ev.pageX - x2;
|
|
42 }
|
|
43
|
|
44 // move towards click
|
|
45 if(!$handle2 || Math.abs(dist) < Math.abs(dist2) ){
|
|
46 if(dist > 0) moveHandle($handle, valueToPx(jump)+x1);
|
|
47 if(dist < 0) moveHandle($handle, -valueToPx(jump)+x1);
|
|
48 } else {
|
|
49 if(dist2 > 0) moveHandle($handle2, valueToPx(jump)+x2);
|
|
50 if(dist2 < 0) moveHandle($handle2, -valueToPx(jump)+x2);
|
|
51 }
|
|
52 }
|
|
53
|
|
54 var moveHandle = function($h, p, update){
|
|
55
|
|
56 var boundR = $input.width()-size;
|
|
57 var boundL = 0;
|
|
58
|
|
59 if(options.range){
|
|
60 if($h[0] === $handle[0]){
|
|
61 boundR = $handle2.position().left;
|
|
62 } else {
|
|
63 boundL = $handle.position().left;
|
|
64 }
|
|
65 }
|
|
66
|
|
67 if(p >= boundR){
|
|
68 p = boundR;
|
|
69 } else if(p <= boundL){
|
|
70 p = boundL;
|
|
71 }
|
|
72
|
|
73 if(options.snap && p !== boundR){
|
|
74 var snapPx = valueToPx(options.snap);
|
|
75 p = Math.round(p/snapPx) * snapPx;
|
|
76 }
|
|
77
|
|
78 $h.css({'left': p, 'position': 'absolute'});
|
|
79 if(options.range) updateSelection();
|
|
80 if(update !== false) updateValues();
|
|
81 }
|
|
82
|
|
83 var dragStart = function(ev){
|
|
84 ev.stopPropagation();
|
|
85 ev.preventDefault();
|
|
86
|
|
87 $dragging = $(this);
|
|
88 };
|
|
89
|
|
90 var drag = function(ev){
|
|
91
|
|
92 if($dragging){
|
|
93 ev.preventDefault();
|
|
94 var pos = ev.pageX - $input.offset().left;
|
|
95
|
|
96 moveHandle($dragging, pos);
|
|
97 }
|
|
98 };
|
|
99
|
|
100 var updateSelection = function(){
|
|
101
|
|
102 var p = $handle.position().left;
|
|
103 var w = $handle2.position().left-p;
|
|
104 $selection.css({
|
|
105 'left': p,
|
|
106 'width': w,
|
|
107 'position': 'absolute'
|
|
108 });
|
|
109 };
|
|
110
|
|
111 var dragEnd = function(ev){
|
|
112 if($dragging){
|
|
113 $dragging = null;
|
|
114 if (options.blur == null) {
|
|
115 // send original blur event
|
|
116 $original.blur();
|
|
117 } else {
|
|
118 options.blur(options.values);
|
|
119 }
|
|
120 }
|
|
121 };
|
|
122
|
|
123 var updateValues = function(){
|
|
124
|
|
125 var prev;
|
|
126 if(options.range){
|
|
127
|
|
128 prev = options.values.slice(); // clone
|
|
129 options.values[0] = pxToValue($handle);
|
|
130 options.values[1] = pxToValue($handle2);
|
|
131
|
|
132 // set value on original element
|
|
133 $original.val(options.values[0] +','+options.values[1]);
|
|
134 } else {
|
|
135
|
|
136 prev = options.values;
|
|
137 options.values = pxToValue($handle);
|
|
138
|
|
139 // set value on original element
|
|
140 $original.val(options.values);
|
|
141 }
|
|
142
|
|
143 if(options.values !== prev) {
|
|
144 if (options.change == null) {
|
|
145 // trigger original change event
|
|
146 $original.change();
|
|
147 } else {
|
|
148 options.change(options.values);
|
|
149 }
|
|
150 }
|
|
151 };
|
|
152
|
|
153 var updateHandles = function(){
|
|
154
|
1040
|
155 if (options.values != null) {
|
1030
|
156 if (options.range){
|
|
157 moveHandle($handle2, valueToPx(options.values[1]), false);
|
|
158 moveHandle($handle, valueToPx(options.values[0]), false);
|
|
159 } else {
|
|
160 moveHandle($handle, valueToPx(options.values), false);
|
|
161 }
|
|
162 }
|
|
163
|
|
164 updateValues();
|
|
165 };
|
|
166
|
|
167 var pxToValue = function($h){
|
|
168 var w = $input.width()-size;
|
|
169 var p = $h.position().left;
|
|
170 var v = (p/(w/(options.max-options.min)))+options.min;
|
|
171
|
|
172 if(options.snap) return Math.floor(v/options.snap) * options.snap;
|
|
173
|
|
174 return Math.round(v);
|
|
175 };
|
|
176
|
|
177 var valueToPx = function(val){
|
|
178 var w = $input.width();
|
1040
|
179 var valspan = options.max - options.min;
|
|
180 var valpos = val - options.min;
|
|
181 var v = valpos * w / valspan;
|
1030
|
182
|
|
183 return v;
|
|
184 };
|
|
185
|
|
186 var bound = function(input){
|
|
187 return Math.max(Math.min(input, options.max), options.min);
|
|
188 };
|
|
189
|
|
190 var methods = {
|
|
191 init : function(o){
|
|
192
|
|
193 // element already replaced
|
|
194 if($(this).data('TinyRange')) return this;
|
|
195
|
|
196 // options
|
|
197 defaults.min = parseFloat($(this).attr('min'));
|
|
198 defaults.max = parseFloat($(this).attr('max'));
|
|
199 defaults.snap = parseFloat($(this).attr('step'));
|
|
200
|
|
201 // options passed into plugin override input attributes
|
|
202 options = $.extend(defaults, o);
|
|
203
|
|
204 if(options.values){
|
|
205 //
|
|
206 } else if(options.range){
|
|
207 options.values = [0, options.max];
|
|
208 } else {
|
|
209 options.values = parseFloat($(this).attr('value'));
|
|
210 }
|
|
211
|
|
212 // how far do handles jump on click, default to step value
|
|
213 jump = options.snap ? options.snap : options.max/10;
|
|
214
|
|
215 // create dom elements
|
|
216 $input = $('<div/>', {'class': 'range-input'}).mousedown(jumpHandle);
|
|
217 $rail = $('<div/>', {'class': 'range-rail'}).appendTo($input);
|
|
218 if(options.range) $selection = $('<div/>', {'class': 'range-selection'}).appendTo($input);
|
|
219 $handle = $('<a/>', {'class': 'range-handle'}).appendTo($input).mousedown(dragStart);
|
|
220 if(options.range) $handle2 = $handle.clone(true).appendTo($input);
|
|
221
|
|
222 // replace dom element
|
|
223 $(this).after($input);
|
|
224 $(this).hide();
|
|
225 $original = $(this);
|
|
226
|
|
227 // attach events
|
|
228 $(document).bind('mouseup', dragEnd);
|
|
229 $(document).bind('mousemove', drag);
|
|
230
|
|
231 // position handles
|
|
232 size = $handle.width();
|
|
233 updateHandles();
|
|
234
|
|
235 return this;
|
|
236 },
|
|
237 set: function(input){
|
|
238
|
|
239 if(typeof input === 'string'){
|
|
240 options.values = bound(input);
|
|
241 } else if(typeof input === 'object' && input.length === 2){
|
|
242 options.values[0] = bound(input[0]);
|
|
243 options.values[1] = bound(input[1]);
|
|
244 }
|
|
245
|
|
246 updateHandles();
|
|
247 },
|
|
248 destroy : function(){
|
|
249
|
|
250 $input.remove();
|
|
251 $(this).show().data('TinyRange', false);
|
|
252 $(document).unbind('mouseup', dragEnd);
|
|
253 $(document).unbind('mousemove', drag);
|
|
254
|
|
255 return this;
|
|
256 }
|
|
257 };
|
|
258
|
|
259 return methods;
|
|
260 };
|
|
261
|
|
262 $.fn.range = function(method) {
|
|
263
|
|
264 // so that arguments are accessible within each closure
|
|
265 var args = arguments;
|
|
266
|
|
267 return this.each(function(){
|
|
268 var state = $(this).data('TinyRange');
|
|
269
|
|
270 // Method calling logic
|
|
271 if (state && state[method] ) {
|
|
272 state[ method ].apply( this, Array.prototype.slice.call( args, 1 ));
|
|
273 } else if ( typeof method === 'object' || ! method ) {
|
|
274
|
|
275 // create new tinyrange
|
|
276 var tr = (new TinyRange(this));
|
|
277 tr.init.apply( this, args );
|
|
278
|
|
279 // save state in jquery data
|
|
280 $(this).data('TinyRange', tr);
|
|
281
|
|
282 } else {
|
|
283 $.error( 'Method ' + method + ' does not exist on jQuery.range' );
|
|
284 }
|
|
285 });
|
|
286 };
|
|
287 })(jQuery);
|