Subversion-Projekte lars-tiefland.cienc

Revision

Details | Letzte Änderung | Log anzeigen | RSS feed

Revision Autor Zeilennr. Zeile
5 lars 1
// Ion.RangeSlider
2
// version 2.0.13 Build: 335
3
// © Denis Ineshin, 2015
4
// https://github.com/IonDen
5
//
6
// Project page:    http://ionden.com/a/plugins/ion.rangeSlider/en.html
7
// GitHub page:     https://github.com/IonDen/ion.rangeSlider
8
//
9
// Released under MIT licence:
10
// http://ionden.com/a/plugins/licence-en.html
11
// =====================================================================================================================
12
 
13
;(function ($, document, window, navigator, undefined) {
14
    "use strict";
15
 
16
    // =================================================================================================================
17
    // Service
18
 
19
    var plugin_count = 0;
20
 
21
    // IE8 fix
22
    var is_old_ie = (function () {
23
        var n = navigator.userAgent,
24
            r = /msie\s\d+/i,
25
            v;
26
        if (n.search(r) > 0) {
27
            v = r.exec(n).toString();
28
            v = v.split(" ")[1];
29
            if (v < 9) {
30
                $("html").addClass("lt-ie9");
31
                return true;
32
            }
33
        }
34
        return false;
35
    } ());
36
    if (!Function.prototype.bind) {
37
        Function.prototype.bind = function bind(that) {
38
 
39
            var target = this;
40
            var slice = [].slice;
41
 
42
            if (typeof target != "function") {
43
                throw new TypeError();
44
            }
45
 
46
            var args = slice.call(arguments, 1),
47
                bound = function () {
48
 
49
                    if (this instanceof bound) {
50
 
51
                        var F = function(){};
52
                        F.prototype = target.prototype;
53
                        var self = new F();
54
 
55
                        var result = target.apply(
56
                            self,
57
                            args.concat(slice.call(arguments))
58
                        );
59
                        if (Object(result) === result) {
60
                            return result;
61
                        }
62
                        return self;
63
 
64
                    } else {
65
 
66
                        return target.apply(
67
                            that,
68
                            args.concat(slice.call(arguments))
69
                        );
70
 
71
                    }
72
 
73
                };
74
 
75
            return bound;
76
        };
77
    }
78
    if (!Array.prototype.indexOf) {
79
        Array.prototype.indexOf = function(searchElement, fromIndex) {
80
            var k;
81
            if (this == null) {
82
                throw new TypeError('"this" is null or not defined');
83
            }
84
            var O = Object(this);
85
            var len = O.length >>> 0;
86
            if (len === 0) {
87
                return -1;
88
            }
89
            var n = +fromIndex || 0;
90
            if (Math.abs(n) === Infinity) {
91
                n = 0;
92
            }
93
            if (n >= len) {
94
                return -1;
95
            }
96
            k = Math.max(n >= 0 ? n : len - Math.abs(n), 0);
97
            while (k < len) {
98
                if (k in O && O[k] === searchElement) {
99
                    return k;
100
                }
101
                k++;
102
            }
103
            return -1;
104
        };
105
    }
106
 
107
 
108
 
109
    // =================================================================================================================
110
    // Template
111
 
112
    var base_html =
113
        '<span class="irs">' +
114
        '<span class="irs-line" tabindex="-1"><span class="irs-line-left"></span><span class="irs-line-mid"></span><span class="irs-line-right"></span></span>' +
115
        '<span class="irs-min">0</span><span class="irs-max">1</span>' +
116
        '<span class="irs-from">0</span><span class="irs-to">0</span><span class="irs-single">0</span>' +
117
        '</span>' +
118
        '<span class="irs-grid"></span>' +
119
        '<span class="irs-bar"></span>';
120
 
121
    var single_html =
122
        '<span class="irs-bar-edge"></span>' +
123
        '<span class="irs-shadow shadow-single"></span>' +
124
        '<span class="irs-slider single"></span>';
125
 
126
    var double_html =
127
        '<span class="irs-shadow shadow-from"></span>' +
128
        '<span class="irs-shadow shadow-to"></span>' +
129
        '<span class="irs-slider from"></span>' +
130
        '<span class="irs-slider to"></span>';
131
 
132
    var disable_html =
133
        '<span class="irs-disable-mask"></span>';
134
 
135
 
136
 
137
    // =================================================================================================================
138
    // Core
139
 
140
    /**
141
     * Main plugin constructor
142
     *
143
     * @param input {object}
144
     * @param options {object}
145
     * @param plugin_count {number}
146
     * @constructor
147
     */
148
    var IonRangeSlider = function (input, options, plugin_count) {
149
        this.VERSION = "2.0.13";
150
        this.input = input;
151
        this.plugin_count = plugin_count;
152
        this.current_plugin = 0;
153
        this.calc_count = 0;
154
        this.update_tm = 0;
155
        this.old_from = 0;
156
        this.old_to = 0;
157
        this.raf_id = null;
158
        this.dragging = false;
159
        this.force_redraw = false;
160
        this.is_key = false;
161
        this.is_update = false;
162
        this.is_start = true;
163
        this.is_finish = false;
164
        this.is_active = false;
165
        this.is_resize = false;
166
        this.is_click = false;
167
 
168
        this.$cache = {
169
            win: $(window),
170
            body: $(document.body),
171
            input: $(input),
172
            cont: null,
173
            rs: null,
174
            min: null,
175
            max: null,
176
            from: null,
177
            to: null,
178
            single: null,
179
            bar: null,
180
            line: null,
181
            s_single: null,
182
            s_from: null,
183
            s_to: null,
184
            shad_single: null,
185
            shad_from: null,
186
            shad_to: null,
187
            edge: null,
188
            grid: null,
189
            grid_labels: []
190
        };
191
 
192
        // get config data attributes
193
        var $inp = this.$cache.input;
194
        var data = {
195
            type: $inp.data("type"),
196
 
197
            min: $inp.data("min"),
198
            max: $inp.data("max"),
199
            from: $inp.data("from"),
200
            to: $inp.data("to"),
201
            step: $inp.data("step"),
202
 
203
            min_interval: $inp.data("minInterval"),
204
            max_interval: $inp.data("maxInterval"),
205
            drag_interval: $inp.data("dragInterval"),
206
 
207
            values: $inp.data("values"),
208
 
209
            from_fixed: $inp.data("fromFixed"),
210
            from_min: $inp.data("fromMin"),
211
            from_max: $inp.data("fromMax"),
212
            from_shadow: $inp.data("fromShadow"),
213
 
214
            to_fixed: $inp.data("toFixed"),
215
            to_min: $inp.data("toMin"),
216
            to_max: $inp.data("toMax"),
217
            to_shadow: $inp.data("toShadow"),
218
 
219
            prettify_enabled: $inp.data("prettifyEnabled"),
220
            prettify_separator: $inp.data("prettifySeparator"),
221
 
222
            force_edges: $inp.data("forceEdges"),
223
 
224
            keyboard: $inp.data("keyboard"),
225
            keyboard_step: $inp.data("keyboardStep"),
226
 
227
            grid: $inp.data("grid"),
228
            grid_margin: $inp.data("gridMargin"),
229
            grid_num: $inp.data("gridNum"),
230
            grid_snap: $inp.data("gridSnap"),
231
 
232
            hide_min_max: $inp.data("hideMinMax"),
233
            hide_from_to: $inp.data("hideFromTo"),
234
 
235
            prefix: $inp.data("prefix"),
236
            postfix: $inp.data("postfix"),
237
            max_postfix: $inp.data("maxPostfix"),
238
            decorate_both: $inp.data("decorateBoth"),
239
            values_separator: $inp.data("valuesSeparator"),
240
 
241
            disable: $inp.data("disable")
242
        };
243
        data.values = data.values && data.values.split(",");
244
 
245
        // get from and to out of input
246
        var val = $inp.prop("value");
247
        if (val) {
248
            val = val.split(";");
249
 
250
            if (val[0] && val[0] == +val[0]) {
251
                val[0] = +val[0];
252
            }
253
            if (val[1] && val[1] == +val[1]) {
254
                val[1] = +val[1];
255
            }
256
 
257
            if (options && options.values && options.values.length) {
258
                data.from = val[0] && options.values.indexOf(val[0]);
259
                data.to = val[1] && options.values.indexOf(val[1]);
260
            } else {
261
                data.from = val[0] && +val[0];
262
                data.to = val[1] && +val[1];
263
            }
264
        }
265
 
266
        // JS config has a priority
267
        options = $.extend(data, options);
268
 
269
        // get config from options
270
        this.options = $.extend({
271
            type: "single",
272
 
273
            min: 10,
274
            max: 100,
275
            from: null,
276
            to: null,
277
            step: 1,
278
 
279
            min_interval: 0,
280
            max_interval: 0,
281
            drag_interval: false,
282
 
283
            values: [],
284
            p_values: [],
285
 
286
            from_fixed: false,
287
            from_min: null,
288
            from_max: null,
289
            from_shadow: false,
290
 
291
            to_fixed: false,
292
            to_min: null,
293
            to_max: null,
294
            to_shadow: false,
295
 
296
            prettify_enabled: true,
297
            prettify_separator: " ",
298
            prettify: null,
299
 
300
            force_edges: false,
301
 
302
            keyboard: false,
303
            keyboard_step: 5,
304
 
305
            grid: false,
306
            grid_margin: true,
307
            grid_num: 4,
308
            grid_snap: false,
309
 
310
            hide_min_max: false,
311
            hide_from_to: false,
312
 
313
            prefix: "",
314
            postfix: "",
315
            max_postfix: "",
316
            decorate_both: true,
317
            values_separator: " — ",
318
 
319
            disable: false,
320
 
321
            onStart: null,
322
            onChange: null,
323
            onFinish: null,
324
            onUpdate: null
325
        }, options);
326
 
327
        this.validate();
328
 
329
        this.result = {
330
            input: this.$cache.input,
331
            slider: null,
332
 
333
            min: this.options.min,
334
            max: this.options.max,
335
 
336
            from: this.options.from,
337
            from_percent: 0,
338
            from_value: null,
339
 
340
            to: this.options.to,
341
            to_percent: 0,
342
            to_value: null
343
        };
344
 
345
        this.coords = {
346
            // left
347
            x_gap: 0,
348
            x_pointer: 0,
349
 
350
            // width
351
            w_rs: 0,
352
            w_rs_old: 0,
353
            w_handle: 0,
354
 
355
            // percents
356
            p_gap: 0,
357
            p_gap_left: 0,
358
            p_gap_right: 0,
359
            p_step: 0,
360
            p_pointer: 0,
361
            p_handle: 0,
362
            p_single: 0,
363
            p_single_real: 0,
364
            p_from: 0,
365
            p_from_real: 0,
366
            p_to: 0,
367
            p_to_real: 0,
368
            p_bar_x: 0,
369
            p_bar_w: 0,
370
 
371
            // grid
372
            grid_gap: 0,
373
            big_num: 0,
374
            big: [],
375
            big_w: [],
376
            big_p: [],
377
            big_x: []
378
        };
379
 
380
        this.labels = {
381
            // width
382
            w_min: 0,
383
            w_max: 0,
384
            w_from: 0,
385
            w_to: 0,
386
            w_single: 0,
387
 
388
            // percents
389
            p_min: 0,
390
            p_max: 0,
391
            p_from: 0,
392
            p_from_left: 0,
393
            p_to: 0,
394
            p_to_left: 0,
395
            p_single: 0,
396
            p_single_left: 0
397
        };
398
 
399
        this.init();
400
    };
401
 
402
    IonRangeSlider.prototype = {
403
        init: function (is_update) {
404
            this.coords.p_step = this.options.step / ((this.options.max - this.options.min) / 100);
405
            this.target = "base";
406
 
407
            this.toggleInput();
408
            this.append();
409
            this.setMinMax();
410
 
411
            if (is_update) {
412
                this.force_redraw = true;
413
                this.calc(true);
414
 
415
                // callbacks called
416
                this.callOnUpdate();
417
            } else {
418
                this.force_redraw = true;
419
                this.calc(true);
420
 
421
                // callbacks called
422
                this.callOnStart();
423
            }
424
 
425
            this.updateScene();
426
        },
427
 
428
        append: function () {
429
            var container_html = '<span class="irs js-irs-' + this.plugin_count + '"></span>';
430
            this.$cache.input.before(container_html);
431
            this.$cache.input.prop("readonly", true);
432
            this.$cache.cont = this.$cache.input.prev();
433
            this.result.slider = this.$cache.cont;
434
 
435
            this.$cache.cont.html(base_html);
436
            this.$cache.rs = this.$cache.cont.find(".irs");
437
            this.$cache.min = this.$cache.cont.find(".irs-min");
438
            this.$cache.max = this.$cache.cont.find(".irs-max");
439
            this.$cache.from = this.$cache.cont.find(".irs-from");
440
            this.$cache.to = this.$cache.cont.find(".irs-to");
441
            this.$cache.single = this.$cache.cont.find(".irs-single");
442
            this.$cache.bar = this.$cache.cont.find(".irs-bar");
443
            this.$cache.line = this.$cache.cont.find(".irs-line");
444
            this.$cache.grid = this.$cache.cont.find(".irs-grid");
445
 
446
            if (this.options.type === "single") {
447
                this.$cache.cont.append(single_html);
448
                this.$cache.edge = this.$cache.cont.find(".irs-bar-edge");
449
                this.$cache.s_single = this.$cache.cont.find(".single");
450
                this.$cache.from[0].style.visibility = "hidden";
451
                this.$cache.to[0].style.visibility = "hidden";
452
                this.$cache.shad_single = this.$cache.cont.find(".shadow-single");
453
            } else {
454
                this.$cache.cont.append(double_html);
455
                this.$cache.s_from = this.$cache.cont.find(".from");
456
                this.$cache.s_to = this.$cache.cont.find(".to");
457
                this.$cache.shad_from = this.$cache.cont.find(".shadow-from");
458
                this.$cache.shad_to = this.$cache.cont.find(".shadow-to");
459
 
460
                this.setTopHandler();
461
            }
462
 
463
            if (this.options.hide_from_to) {
464
                this.$cache.from[0].style.display = "none";
465
                this.$cache.to[0].style.display = "none";
466
                this.$cache.single[0].style.display = "none";
467
            }
468
 
469
            this.appendGrid();
470
 
471
            if (this.options.disable) {
472
                this.appendDisableMask();
473
                this.$cache.input[0].disabled = true;
474
            } else {
475
                this.$cache.cont.removeClass("irs-disabled");
476
                this.$cache.input[0].disabled = false;
477
                this.bindEvents();
478
            }
479
        },
480
 
481
        setTopHandler: function () {
482
            var min = this.options.min,
483
                max = this.options.max,
484
                from = this.options.from,
485
                to = this.options.to;
486
 
487
            if (from > min && to === max) {
488
                this.$cache.s_from.addClass("type_last");
489
            } else if (to < max) {
490
                this.$cache.s_to.addClass("type_last");
491
            }
492
        },
493
 
494
        appendDisableMask: function () {
495
            this.$cache.cont.append(disable_html);
496
            this.$cache.cont.addClass("irs-disabled");
497
        },
498
 
499
        remove: function () {
500
            this.$cache.cont.remove();
501
            this.$cache.cont = null;
502
 
503
            this.$cache.line.off("keydown.irs_" + this.plugin_count);
504
 
505
            this.$cache.body.off("touchmove.irs_" + this.plugin_count);
506
            this.$cache.body.off("mousemove.irs_" + this.plugin_count);
507
 
508
            this.$cache.win.off("touchend.irs_" + this.plugin_count);
509
            this.$cache.win.off("mouseup.irs_" + this.plugin_count);
510
 
511
            if (is_old_ie) {
512
                this.$cache.body.off("mouseup.irs_" + this.plugin_count);
513
                this.$cache.body.off("mouseleave.irs_" + this.plugin_count);
514
            }
515
 
516
            this.$cache.grid_labels = [];
517
            this.coords.big = [];
518
            this.coords.big_w = [];
519
            this.coords.big_p = [];
520
            this.coords.big_x = [];
521
 
522
            cancelAnimationFrame(this.raf_id);
523
        },
524
 
525
        bindEvents: function () {
526
            this.$cache.body.on("touchmove.irs_" + this.plugin_count, this.pointerMove.bind(this));
527
            this.$cache.body.on("mousemove.irs_" + this.plugin_count, this.pointerMove.bind(this));
528
 
529
            this.$cache.win.on("touchend.irs_" + this.plugin_count, this.pointerUp.bind(this));
530
            this.$cache.win.on("mouseup.irs_" + this.plugin_count, this.pointerUp.bind(this));
531
 
532
            this.$cache.line.on("touchstart.irs_" + this.plugin_count, this.pointerClick.bind(this, "click"));
533
            this.$cache.line.on("mousedown.irs_" + this.plugin_count, this.pointerClick.bind(this, "click"));
534
 
535
            if (this.options.drag_interval && this.options.type === "double") {
536
                this.$cache.bar.on("touchstart.irs_" + this.plugin_count, this.pointerDown.bind(this, "both"));
537
                this.$cache.bar.on("mousedown.irs_" + this.plugin_count, this.pointerDown.bind(this, "both"));
538
            } else {
539
                this.$cache.bar.on("touchstart.irs_" + this.plugin_count, this.pointerClick.bind(this, "click"));
540
                this.$cache.bar.on("mousedown.irs_" + this.plugin_count, this.pointerClick.bind(this, "click"));
541
            }
542
 
543
            if (this.options.type === "single") {
544
                this.$cache.single.on("touchstart.irs_" + this.plugin_count, this.pointerDown.bind(this, "single"));
545
                this.$cache.s_single.on("touchstart.irs_" + this.plugin_count, this.pointerDown.bind(this, "single"));
546
                this.$cache.shad_single.on("touchstart.irs_" + this.plugin_count, this.pointerClick.bind(this, "click"));
547
 
548
                this.$cache.single.on("mousedown.irs_" + this.plugin_count, this.pointerDown.bind(this, "single"));
549
                this.$cache.s_single.on("mousedown.irs_" + this.plugin_count, this.pointerDown.bind(this, "single"));
550
                this.$cache.edge.on("mousedown.irs_" + this.plugin_count, this.pointerClick.bind(this, "click"));
551
                this.$cache.shad_single.on("mousedown.irs_" + this.plugin_count, this.pointerClick.bind(this, "click"));
552
            } else {
553
                this.$cache.single.on("touchstart.irs_" + this.plugin_count, this.pointerDown.bind(this, "from"));
554
                this.$cache.single.on("mousedown.irs_" + this.plugin_count, this.pointerDown.bind(this, "from"));
555
 
556
                this.$cache.from.on("touchstart.irs_" + this.plugin_count, this.pointerDown.bind(this, "from"));
557
                this.$cache.s_from.on("touchstart.irs_" + this.plugin_count, this.pointerDown.bind(this, "from"));
558
                this.$cache.to.on("touchstart.irs_" + this.plugin_count, this.pointerDown.bind(this, "to"));
559
                this.$cache.s_to.on("touchstart.irs_" + this.plugin_count, this.pointerDown.bind(this, "to"));
560
                this.$cache.shad_from.on("touchstart.irs_" + this.plugin_count, this.pointerClick.bind(this, "click"));
561
                this.$cache.shad_to.on("touchstart.irs_" + this.plugin_count, this.pointerClick.bind(this, "click"));
562
 
563
                this.$cache.from.on("mousedown.irs_" + this.plugin_count, this.pointerDown.bind(this, "from"));
564
                this.$cache.s_from.on("mousedown.irs_" + this.plugin_count, this.pointerDown.bind(this, "from"));
565
                this.$cache.to.on("mousedown.irs_" + this.plugin_count, this.pointerDown.bind(this, "to"));
566
                this.$cache.s_to.on("mousedown.irs_" + this.plugin_count, this.pointerDown.bind(this, "to"));
567
                this.$cache.shad_from.on("mousedown.irs_" + this.plugin_count, this.pointerClick.bind(this, "click"));
568
                this.$cache.shad_to.on("mousedown.irs_" + this.plugin_count, this.pointerClick.bind(this, "click"));
569
            }
570
 
571
            if (this.options.keyboard) {
572
                this.$cache.line.on("keydown.irs_" + this.plugin_count, this.key.bind(this, "keyboard"));
573
            }
574
 
575
            if (is_old_ie) {
576
                this.$cache.body.on("mouseup.irs_" + this.plugin_count, this.pointerUp.bind(this));
577
                this.$cache.body.on("mouseleave.irs_" + this.plugin_count, this.pointerUp.bind(this));
578
            }
579
        },
580
 
581
        pointerMove: function (e) {
582
            if (!this.dragging) {
583
                return;
584
            }
585
 
586
            var x = e.pageX || e.originalEvent.touches && e.originalEvent.touches[0].pageX;
587
            this.coords.x_pointer = x - this.coords.x_gap;
588
 
589
            this.calc();
590
        },
591
 
592
        pointerUp: function (e) {
593
            if (this.current_plugin !== this.plugin_count) {
594
                return;
595
            }
596
 
597
            if (this.is_active) {
598
                this.is_active = false;
599
            } else {
600
                return;
601
            }
602
 
603
            // callbacks call
604
            if ($.contains(this.$cache.cont[0], e.target) || this.dragging) {
605
                this.is_finish = true;
606
                this.callOnFinish();
607
            }
608
 
609
            this.$cache.cont.find(".state_hover").removeClass("state_hover");
610
 
611
            this.force_redraw = true;
612
            this.dragging = false;
613
 
614
            if (is_old_ie) {
615
                $("*").prop("unselectable", false);
616
            }
617
 
618
            this.updateScene();
619
        },
620
 
621
        changeLevel: function (target) {
622
            switch (target) {
623
                case "single":
624
                    this.coords.p_gap = this.toFixed(this.coords.p_pointer - this.coords.p_single);
625
                    break;
626
                case "from":
627
                    this.coords.p_gap = this.toFixed(this.coords.p_pointer - this.coords.p_from);
628
                    this.$cache.s_from.addClass("state_hover");
629
                    this.$cache.s_from.addClass("type_last");
630
                    this.$cache.s_to.removeClass("type_last");
631
                    break;
632
                case "to":
633
                    this.coords.p_gap = this.toFixed(this.coords.p_pointer - this.coords.p_to);
634
                    this.$cache.s_to.addClass("state_hover");
635
                    this.$cache.s_to.addClass("type_last");
636
                    this.$cache.s_from.removeClass("type_last");
637
                    break;
638
                case "both":
639
                    this.coords.p_gap_left = this.toFixed(this.coords.p_pointer - this.coords.p_from);
640
                    this.coords.p_gap_right = this.toFixed(this.coords.p_to - this.coords.p_pointer);
641
                    this.$cache.s_to.removeClass("type_last");
642
                    this.$cache.s_from.removeClass("type_last");
643
                    break;
644
            }
645
        },
646
 
647
        pointerDown: function (target, e) {
648
            e.preventDefault();
649
            var x = e.pageX || e.originalEvent.touches && e.originalEvent.touches[0].pageX;
650
            if (e.button === 2) {
651
                return;
652
            }
653
 
654
            this.current_plugin = this.plugin_count;
655
            this.target = target;
656
 
657
            this.is_active = true;
658
            this.dragging = true;
659
 
660
            this.coords.x_gap = this.$cache.rs.offset().left;
661
            this.coords.x_pointer = x - this.coords.x_gap;
662
 
663
            this.calcPointer();
664
            this.changeLevel(target);
665
 
666
            if (is_old_ie) {
667
                $("*").prop("unselectable", true);
668
            }
669
 
670
            this.$cache.line.trigger("focus");
671
 
672
            this.updateScene();
673
        },
674
 
675
        pointerClick: function (target, e) {
676
            e.preventDefault();
677
            var x = e.pageX || e.originalEvent.touches && e.originalEvent.touches[0].pageX;
678
            if (e.button === 2) {
679
                return;
680
            }
681
 
682
            this.current_plugin = this.plugin_count;
683
            this.target = target;
684
 
685
            this.is_click = true;
686
            this.coords.x_gap = this.$cache.rs.offset().left;
687
            this.coords.x_pointer = +(x - this.coords.x_gap).toFixed();
688
 
689
            this.force_redraw = true;
690
            this.calc();
691
 
692
            this.$cache.line.trigger("focus");
693
        },
694
 
695
        key: function (target, e) {
696
            if (this.current_plugin !== this.plugin_count || e.altKey || e.ctrlKey || e.shiftKey || e.metaKey) {
697
                return;
698
            }
699
 
700
            switch (e.which) {
701
                case 83: // W
702
                case 65: // A
703
                case 40: // DOWN
704
                case 37: // LEFT
705
                    e.preventDefault();
706
                    this.moveByKey(false);
707
                    break;
708
 
709
                case 87: // S
710
                case 68: // D
711
                case 38: // UP
712
                case 39: // RIGHT
713
                    e.preventDefault();
714
                    this.moveByKey(true);
715
                    break;
716
            }
717
 
718
            return true;
719
        },
720
 
721
        // Move by key. Beta
722
        // TODO: refactor than have plenty of time
723
        moveByKey: function (right) {
724
            var p = this.coords.p_pointer;
725
 
726
            if (right) {
727
                p += this.options.keyboard_step;
728
            } else {
729
                p -= this.options.keyboard_step;
730
            }
731
 
732
            this.coords.x_pointer = this.toFixed(this.coords.w_rs / 100 * p);
733
            this.is_key = true;
734
            this.calc();
735
        },
736
 
737
        setMinMax: function () {
738
            if (!this.options) {
739
                return;
740
            }
741
 
742
            if (this.options.hide_min_max) {
743
                this.$cache.min[0].style.display = "none";
744
                this.$cache.max[0].style.display = "none";
745
                return;
746
            }
747
 
748
            if (this.options.values.length) {
749
                this.$cache.min.html(this.decorate(this.options.p_values[this.options.min]));
750
                this.$cache.max.html(this.decorate(this.options.p_values[this.options.max]));
751
            } else {
752
                this.$cache.min.html(this.decorate(this._prettify(this.options.min), this.options.min));
753
                this.$cache.max.html(this.decorate(this._prettify(this.options.max), this.options.max));
754
            }
755
 
756
            this.labels.w_min = this.$cache.min.outerWidth(false);
757
            this.labels.w_max = this.$cache.max.outerWidth(false);
758
        },
759
 
760
 
761
 
762
        // =============================================================================================================
763
        // Calculations
764
 
765
        calc: function (update) {
766
            if (!this.options) {
767
                return;
768
            }
769
 
770
            this.calc_count++;
771
 
772
            if (this.calc_count === 10 || update) {
773
                this.calc_count = 0;
774
                this.coords.w_rs = this.$cache.rs.outerWidth(false);
775
                if (this.options.type === "single") {
776
                    this.coords.w_handle = this.$cache.s_single.outerWidth(false);
777
                } else {
778
                    this.coords.w_handle = this.$cache.s_from.outerWidth(false);
779
                }
780
            }
781
 
782
            if (!this.coords.w_rs) {
783
                return;
784
            }
785
 
786
            this.calcPointer();
787
 
788
            this.coords.p_handle = this.toFixed(this.coords.w_handle / this.coords.w_rs * 100);
789
            var real_width = 100 - this.coords.p_handle,
790
                real_x = this.toFixed(this.coords.p_pointer - this.coords.p_gap);
791
 
792
            if (this.target === "click") {
793
                this.coords.p_gap = this.coords.p_handle / 2;
794
                real_x = this.toFixed(this.coords.p_pointer - this.coords.p_gap);
795
                this.target = this.chooseHandle(real_x);
796
            }
797
 
798
            if (real_x < 0) {
799
                real_x = 0;
800
            } else if (real_x > real_width) {
801
                real_x = real_width;
802
            }
803
 
804
            switch (this.target) {
805
                case "base":
806
                    var w = (this.options.max - this.options.min) / 100,
807
                        f = (this.result.from - this.options.min) / w,
808
                        t = (this.result.to - this.options.min) / w;
809
 
810
                    this.coords.p_single_real = this.toFixed(f);
811
                    this.coords.p_from_real = this.toFixed(f);
812
                    this.coords.p_to_real = this.toFixed(t);
813
 
814
                    this.coords.p_single_real = this.checkDiapason(this.coords.p_single_real, this.options.from_min, this.options.from_max);
815
                    this.coords.p_from_real = this.checkDiapason(this.coords.p_from_real, this.options.from_min, this.options.from_max);
816
                    this.coords.p_to_real = this.checkDiapason(this.coords.p_to_real, this.options.to_min, this.options.to_max);
817
 
818
                    this.coords.p_single = this.toFixed(f - (this.coords.p_handle / 100 * f));
819
                    this.coords.p_from = this.toFixed(f - (this.coords.p_handle / 100 * f));
820
                    this.coords.p_to = this.toFixed(t - (this.coords.p_handle / 100 * t));
821
 
822
                    this.target = null;
823
 
824
                    break;
825
 
826
                case "single":
827
                    if (this.options.from_fixed) {
828
                        break;
829
                    }
830
 
831
                    this.coords.p_single_real = this.calcWithStep(real_x / real_width * 100);
832
                    this.coords.p_single_real = this.checkDiapason(this.coords.p_single_real, this.options.from_min, this.options.from_max);
833
                    this.coords.p_single = this.toFixed(this.coords.p_single_real / 100 * real_width);
834
 
835
                    break;
836
 
837
                case "from":
838
                    if (this.options.from_fixed) {
839
                        break;
840
                    }
841
 
842
                    this.coords.p_from_real = this.calcWithStep(real_x / real_width * 100);
843
                    if (this.coords.p_from_real > this.coords.p_to_real) {
844
                        this.coords.p_from_real = this.coords.p_to_real;
845
                    }
846
                    this.coords.p_from_real = this.checkDiapason(this.coords.p_from_real, this.options.from_min, this.options.from_max);
847
                    this.coords.p_from_real = this.checkMinInterval(this.coords.p_from_real, this.coords.p_to_real, "from");
848
                    this.coords.p_from_real = this.checkMaxInterval(this.coords.p_from_real, this.coords.p_to_real, "from");
849
                    this.coords.p_from = this.toFixed(this.coords.p_from_real / 100 * real_width);
850
 
851
                    break;
852
 
853
                case "to":
854
                    if (this.options.to_fixed) {
855
                        break;
856
                    }
857
 
858
                    this.coords.p_to_real = this.calcWithStep(real_x / real_width * 100);
859
                    if (this.coords.p_to_real < this.coords.p_from_real) {
860
                        this.coords.p_to_real = this.coords.p_from_real;
861
                    }
862
                    this.coords.p_to_real = this.checkDiapason(this.coords.p_to_real, this.options.to_min, this.options.to_max);
863
                    this.coords.p_to_real = this.checkMinInterval(this.coords.p_to_real, this.coords.p_from_real, "to");
864
                    this.coords.p_to_real = this.checkMaxInterval(this.coords.p_to_real, this.coords.p_from_real, "to");
865
                    this.coords.p_to = this.toFixed(this.coords.p_to_real / 100 * real_width);
866
 
867
                    break;
868
 
869
                case "both":
870
                    if (this.options.from_fixed || this.options.to_fixed) {
871
                        break;
872
                    }
873
 
874
                    real_x = this.toFixed(real_x + (this.coords.p_handle * 0.1));
875
 
876
                    this.coords.p_from_real = this.calcWithStep((real_x - this.coords.p_gap_left) / real_width * 100);
877
                    this.coords.p_from_real = this.checkDiapason(this.coords.p_from_real, this.options.from_min, this.options.from_max);
878
                    this.coords.p_from_real = this.checkMinInterval(this.coords.p_from_real, this.coords.p_to_real, "from");
879
                    this.coords.p_from = this.toFixed(this.coords.p_from_real / 100 * real_width);
880
 
881
                    this.coords.p_to_real = this.calcWithStep((real_x + this.coords.p_gap_right) / real_width * 100);
882
                    this.coords.p_to_real = this.checkDiapason(this.coords.p_to_real, this.options.to_min, this.options.to_max);
883
                    this.coords.p_to_real = this.checkMinInterval(this.coords.p_to_real, this.coords.p_from_real, "to");
884
                    this.coords.p_to = this.toFixed(this.coords.p_to_real / 100 * real_width);
885
 
886
                    break;
887
            }
888
 
889
            if (this.options.type === "single") {
890
                this.coords.p_bar_x = (this.coords.p_handle / 2);
891
                this.coords.p_bar_w = this.coords.p_single;
892
 
893
                this.result.from_percent = this.coords.p_single_real;
894
                this.result.from = this.calcReal(this.coords.p_single_real);
895
                if (this.options.values.length) {
896
                    this.result.from_value = this.options.values[this.result.from];
897
                }
898
            } else {
899
                this.coords.p_bar_x = this.toFixed(this.coords.p_from + (this.coords.p_handle / 2));
900
                this.coords.p_bar_w = this.toFixed(this.coords.p_to - this.coords.p_from);
901
 
902
                this.result.from_percent = this.coords.p_from_real;
903
                this.result.from = this.calcReal(this.coords.p_from_real);
904
                this.result.to_percent = this.coords.p_to_real;
905
                this.result.to = this.calcReal(this.coords.p_to_real);
906
                if (this.options.values.length) {
907
                    this.result.from_value = this.options.values[this.result.from];
908
                    this.result.to_value = this.options.values[this.result.to];
909
                }
910
            }
911
 
912
            this.calcMinMax();
913
            this.calcLabels();
914
        },
915
 
916
        calcPointer: function () {
917
            if (!this.coords.w_rs) {
918
                this.coords.p_pointer = 0;
919
                return;
920
            }
921
 
922
            if (this.coords.x_pointer < 0 || isNaN(this.coords.x_pointer)  ) {
923
                this.coords.x_pointer = 0;
924
            } else if (this.coords.x_pointer > this.coords.w_rs) {
925
                this.coords.x_pointer = this.coords.w_rs;
926
            }
927
 
928
            this.coords.p_pointer = this.toFixed(this.coords.x_pointer / this.coords.w_rs * 100);
929
        },
930
 
931
        chooseHandle: function (real_x) {
932
            if (this.options.type === "single") {
933
                return "single";
934
            } else {
935
                var m_point = this.coords.p_from_real + ((this.coords.p_to_real - this.coords.p_from_real) / 2);
936
                if (real_x >= m_point) {
937
                    return this.options.to_fixed ? "from" : "to";
938
                } else {
939
                    return this.options.from_fixed ? "to" : "from";
940
                }
941
            }
942
        },
943
 
944
        calcMinMax: function () {
945
            if (!this.coords.w_rs) {
946
                return;
947
            }
948
 
949
            this.labels.p_min = this.labels.w_min / this.coords.w_rs * 100;
950
            this.labels.p_max = this.labels.w_max / this.coords.w_rs * 100;
951
        },
952
 
953
        calcLabels: function () {
954
            if (!this.coords.w_rs || this.options.hide_from_to) {
955
                return;
956
            }
957
 
958
            if (this.options.type === "single") {
959
 
960
                this.labels.w_single = this.$cache.single.outerWidth(false);
961
                this.labels.p_single = this.labels.w_single / this.coords.w_rs * 100;
962
                this.labels.p_single_left = this.coords.p_single + (this.coords.p_handle / 2) - (this.labels.p_single / 2);
963
                this.labels.p_single_left = this.checkEdges(this.labels.p_single_left, this.labels.p_single);
964
 
965
            } else {
966
 
967
                this.labels.w_from = this.$cache.from.outerWidth(false);
968
                this.labels.p_from = this.labels.w_from / this.coords.w_rs * 100;
969
                this.labels.p_from_left = this.coords.p_from + (this.coords.p_handle / 2) - (this.labels.p_from / 2);
970
                this.labels.p_from_left = this.toFixed(this.labels.p_from_left);
971
                this.labels.p_from_left = this.checkEdges(this.labels.p_from_left, this.labels.p_from);
972
 
973
                this.labels.w_to = this.$cache.to.outerWidth(false);
974
                this.labels.p_to = this.labels.w_to / this.coords.w_rs * 100;
975
                this.labels.p_to_left = this.coords.p_to + (this.coords.p_handle / 2) - (this.labels.p_to / 2);
976
                this.labels.p_to_left = this.toFixed(this.labels.p_to_left);
977
                this.labels.p_to_left = this.checkEdges(this.labels.p_to_left, this.labels.p_to);
978
 
979
                this.labels.w_single = this.$cache.single.outerWidth(false);
980
                this.labels.p_single = this.labels.w_single / this.coords.w_rs * 100;
981
                this.labels.p_single_left = ((this.labels.p_from_left + this.labels.p_to_left + this.labels.p_to) / 2) - (this.labels.p_single / 2);
982
                this.labels.p_single_left = this.toFixed(this.labels.p_single_left);
983
                this.labels.p_single_left = this.checkEdges(this.labels.p_single_left, this.labels.p_single);
984
 
985
            }
986
        },
987
 
988
 
989
 
990
        // =============================================================================================================
991
        // Drawings
992
 
993
        updateScene: function () {
994
            if (this.raf_id) {
995
                cancelAnimationFrame(this.raf_id);
996
                this.raf_id = null;
997
            }
998
 
999
            clearTimeout(this.update_tm);
1000
            this.update_tm = null;
1001
 
1002
            if (!this.options) {
1003
                return;
1004
            }
1005
 
1006
            this.drawHandles();
1007
 
1008
            if (this.is_active) {
1009
                this.raf_id = requestAnimationFrame(this.updateScene.bind(this));
1010
            } else {
1011
                this.update_tm = setTimeout(this.updateScene.bind(this), 300);
1012
            }
1013
        },
1014
 
1015
        drawHandles: function () {
1016
            this.coords.w_rs = this.$cache.rs.outerWidth(false);
1017
 
1018
            if (!this.coords.w_rs) {
1019
                return;
1020
            }
1021
 
1022
            if (this.coords.w_rs !== this.coords.w_rs_old) {
1023
                this.target = "base";
1024
                this.is_resize = true;
1025
            }
1026
 
1027
            if (this.coords.w_rs !== this.coords.w_rs_old || this.force_redraw) {
1028
                this.setMinMax();
1029
                this.calc(true);
1030
                this.drawLabels();
1031
                if (this.options.grid) {
1032
                    this.calcGridMargin();
1033
                    this.calcGridLabels();
1034
                }
1035
                this.force_redraw = true;
1036
                this.coords.w_rs_old = this.coords.w_rs;
1037
                this.drawShadow();
1038
            }
1039
 
1040
            if (!this.coords.w_rs) {
1041
                return;
1042
            }
1043
 
1044
            if (!this.dragging && !this.force_redraw && !this.is_key) {
1045
                return;
1046
            }
1047
 
1048
            if (this.old_from !== this.result.from || this.old_to !== this.result.to || this.force_redraw || this.is_key) {
1049
 
1050
                this.drawLabels();
1051
 
1052
                this.$cache.bar[0].style.left = this.coords.p_bar_x + "%";
1053
                this.$cache.bar[0].style.width = this.coords.p_bar_w + "%";
1054
 
1055
                if (this.options.type === "single") {
1056
                    this.$cache.s_single[0].style.left = this.coords.p_single + "%";
1057
 
1058
                    this.$cache.single[0].style.left = this.labels.p_single_left + "%";
1059
 
1060
                    if (this.options.values.length) {
1061
                        this.$cache.input.prop("value", this.result.from_value);
1062
                        this.$cache.input.data("from", this.result.from_value);
1063
                    } else {
1064
                        this.$cache.input.prop("value", this.result.from);
1065
                        this.$cache.input.data("from", this.result.from);
1066
                    }
1067
                } else {
1068
                    this.$cache.s_from[0].style.left = this.coords.p_from + "%";
1069
                    this.$cache.s_to[0].style.left = this.coords.p_to + "%";
1070
 
1071
                    if (this.old_from !== this.result.from || this.force_redraw) {
1072
                        this.$cache.from[0].style.left = this.labels.p_from_left + "%";
1073
                    }
1074
                    if (this.old_to !== this.result.to || this.force_redraw) {
1075
                        this.$cache.to[0].style.left = this.labels.p_to_left + "%";
1076
                    }
1077
 
1078
                    this.$cache.single[0].style.left = this.labels.p_single_left + "%";
1079
 
1080
                    if (this.options.values.length) {
1081
                        this.$cache.input.prop("value", this.result.from_value + ";" + this.result.to_value);
1082
                        this.$cache.input.data("from", this.result.from_value);
1083
                        this.$cache.input.data("to", this.result.to_value);
1084
                    } else {
1085
                        this.$cache.input.prop("value", this.result.from + ";" + this.result.to);
1086
                        this.$cache.input.data("from", this.result.from);
1087
                        this.$cache.input.data("to", this.result.to);
1088
                    }
1089
                }
1090
 
1091
                if ((this.old_from !== this.result.from || this.old_to !== this.result.to) && !this.is_start) {
1092
                    this.$cache.input.trigger("change");
1093
                }
1094
 
1095
                this.old_from = this.result.from;
1096
                this.old_to = this.result.to;
1097
 
1098
                // callbacks call
1099
                if (!this.is_resize && !this.is_update && !this.is_start && !this.is_finish) {
1100
                    this.callOnChange();
1101
                }
1102
                if (this.is_key || this.is_click) {
1103
                    this.callOnFinish();
1104
                }
1105
 
1106
                this.is_update = false;
1107
                this.is_resize = false;
1108
                this.is_finish = false;
1109
            }
1110
 
1111
            this.is_start = false;
1112
            this.is_key = false;
1113
            this.is_click = false;
1114
            this.force_redraw = false;
1115
        },
1116
 
1117
        // callbacks
1118
        callOnStart: function () {
1119
            if (this.options.onStart && typeof this.options.onStart === "function") {
1120
                this.options.onStart(this.result);
1121
            }
1122
        },
1123
        callOnChange: function () {
1124
            if (this.options.onChange && typeof this.options.onChange === "function") {
1125
                this.options.onChange(this.result);
1126
            }
1127
        },
1128
        callOnFinish: function () {
1129
            if (this.options.onFinish && typeof this.options.onFinish === "function") {
1130
                this.options.onFinish(this.result);
1131
            }
1132
        },
1133
        callOnUpdate: function () {
1134
            if (this.options.onUpdate && typeof this.options.onUpdate === "function") {
1135
                this.options.onUpdate(this.result);
1136
            }
1137
        },
1138
 
1139
        drawLabels: function () {
1140
            if (!this.options) {
1141
                return;
1142
            }
1143
 
1144
            var values_num = this.options.values.length,
1145
                p_values = this.options.p_values,
1146
                text_single,
1147
                text_from,
1148
                text_to;
1149
 
1150
            if (this.options.hide_from_to) {
1151
                return;
1152
            }
1153
 
1154
            if (this.options.type === "single") {
1155
 
1156
                if (values_num) {
1157
                    text_single = this.decorate(p_values[this.result.from]);
1158
                    this.$cache.single.html(text_single);
1159
                } else {
1160
                    text_single = this.decorate(this._prettify(this.result.from), this.result.from);
1161
                    this.$cache.single.html(text_single);
1162
                }
1163
 
1164
                this.calcLabels();
1165
 
1166
                if (this.labels.p_single_left < this.labels.p_min + 1) {
1167
                    this.$cache.min[0].style.visibility = "hidden";
1168
                } else {
1169
                    this.$cache.min[0].style.visibility = "visible";
1170
                }
1171
 
1172
                if (this.labels.p_single_left + this.labels.p_single > 100 - this.labels.p_max - 1) {
1173
                    this.$cache.max[0].style.visibility = "hidden";
1174
                } else {
1175
                    this.$cache.max[0].style.visibility = "visible";
1176
                }
1177
 
1178
            } else {
1179
 
1180
                if (values_num) {
1181
 
1182
                    if (this.options.decorate_both) {
1183
                        text_single = this.decorate(p_values[this.result.from]);
1184
                        text_single += this.options.values_separator;
1185
                        text_single += this.decorate(p_values[this.result.to]);
1186
                    } else {
1187
                        text_single = this.decorate(p_values[this.result.from] + this.options.values_separator + p_values[this.result.to]);
1188
                    }
1189
                    text_from = this.decorate(p_values[this.result.from]);
1190
                    text_to = this.decorate(p_values[this.result.to]);
1191
 
1192
                    this.$cache.single.html(text_single);
1193
                    this.$cache.from.html(text_from);
1194
                    this.$cache.to.html(text_to);
1195
 
1196
                } else {
1197
 
1198
                    if (this.options.decorate_both) {
1199
                        text_single = this.decorate(this._prettify(this.result.from), this.result.from);
1200
                        text_single += this.options.values_separator;
1201
                        text_single += this.decorate(this._prettify(this.result.to), this.result.to);
1202
                    } else {
1203
                        text_single = this.decorate(this._prettify(this.result.from) + this.options.values_separator + this._prettify(this.result.to), this.result.to);
1204
                    }
1205
                    text_from = this.decorate(this._prettify(this.result.from), this.result.from);
1206
                    text_to = this.decorate(this._prettify(this.result.to), this.result.to);
1207
 
1208
                    this.$cache.single.html(text_single);
1209
                    this.$cache.from.html(text_from);
1210
                    this.$cache.to.html(text_to);
1211
 
1212
                }
1213
 
1214
                this.calcLabels();
1215
 
1216
                var min = Math.min(this.labels.p_single_left, this.labels.p_from_left),
1217
                    single_left = this.labels.p_single_left + this.labels.p_single,
1218
                    to_left = this.labels.p_to_left + this.labels.p_to,
1219
                    max = Math.max(single_left, to_left);
1220
 
1221
                if (this.labels.p_from_left + this.labels.p_from >= this.labels.p_to_left) {
1222
                    this.$cache.from[0].style.visibility = "hidden";
1223
                    this.$cache.to[0].style.visibility = "hidden";
1224
                    this.$cache.single[0].style.visibility = "visible";
1225
 
1226
                    if (this.result.from === this.result.to) {
1227
                        this.$cache.from[0].style.visibility = "visible";
1228
                        this.$cache.single[0].style.visibility = "hidden";
1229
                        max = to_left;
1230
                    } else {
1231
                        this.$cache.from[0].style.visibility = "hidden";
1232
                        this.$cache.single[0].style.visibility = "visible";
1233
                        max = Math.max(single_left, to_left);
1234
                    }
1235
                } else {
1236
                    this.$cache.from[0].style.visibility = "visible";
1237
                    this.$cache.to[0].style.visibility = "visible";
1238
                    this.$cache.single[0].style.visibility = "hidden";
1239
                }
1240
 
1241
                if (min < this.labels.p_min + 1) {
1242
                    this.$cache.min[0].style.visibility = "hidden";
1243
                } else {
1244
                    this.$cache.min[0].style.visibility = "visible";
1245
                }
1246
 
1247
                if (max > 100 - this.labels.p_max - 1) {
1248
                    this.$cache.max[0].style.visibility = "hidden";
1249
                } else {
1250
                    this.$cache.max[0].style.visibility = "visible";
1251
                }
1252
 
1253
            }
1254
        },
1255
 
1256
        drawShadow: function () {
1257
            var o = this.options,
1258
                c = this.$cache,
1259
 
1260
                is_from_min = typeof o.from_min === "number" && !isNaN(o.from_min),
1261
                is_from_max = typeof o.from_max === "number" && !isNaN(o.from_max),
1262
                is_to_min = typeof o.to_min === "number" && !isNaN(o.to_min),
1263
                is_to_max = typeof o.to_max === "number" && !isNaN(o.to_max),
1264
 
1265
                from_min,
1266
                from_max,
1267
                to_min,
1268
                to_max;
1269
 
1270
            if (o.type === "single") {
1271
                if (o.from_shadow && (is_from_min || is_from_max)) {
1272
                    from_min = this.calcPercent(is_from_min ? o.from_min : o.min);
1273
                    from_max = this.calcPercent(is_from_max ? o.from_max : o.max) - from_min;
1274
                    from_min = this.toFixed(from_min - (this.coords.p_handle / 100 * from_min));
1275
                    from_max = this.toFixed(from_max - (this.coords.p_handle / 100 * from_max));
1276
                    from_min = from_min + (this.coords.p_handle / 2);
1277
 
1278
                    c.shad_single[0].style.display = "block";
1279
                    c.shad_single[0].style.left = from_min + "%";
1280
                    c.shad_single[0].style.width = from_max + "%";
1281
                } else {
1282
                    c.shad_single[0].style.display = "none";
1283
                }
1284
            } else {
1285
                if (o.from_shadow && (is_from_min || is_from_max)) {
1286
                    from_min = this.calcPercent(is_from_min ? o.from_min : o.min);
1287
                    from_max = this.calcPercent(is_from_max ? o.from_max : o.max) - from_min;
1288
                    from_min = this.toFixed(from_min - (this.coords.p_handle / 100 * from_min));
1289
                    from_max = this.toFixed(from_max - (this.coords.p_handle / 100 * from_max));
1290
                    from_min = from_min + (this.coords.p_handle / 2);
1291
 
1292
                    c.shad_from[0].style.display = "block";
1293
                    c.shad_from[0].style.left = from_min + "%";
1294
                    c.shad_from[0].style.width = from_max + "%";
1295
                } else {
1296
                    c.shad_from[0].style.display = "none";
1297
                }
1298
 
1299
                if (o.to_shadow && (is_to_min || is_to_max)) {
1300
                    to_min = this.calcPercent(is_to_min ? o.to_min : o.min);
1301
                    to_max = this.calcPercent(is_to_max ? o.to_max : o.max) - to_min;
1302
                    to_min = this.toFixed(to_min - (this.coords.p_handle / 100 * to_min));
1303
                    to_max = this.toFixed(to_max - (this.coords.p_handle / 100 * to_max));
1304
                    to_min = to_min + (this.coords.p_handle / 2);
1305
 
1306
                    c.shad_to[0].style.display = "block";
1307
                    c.shad_to[0].style.left = to_min + "%";
1308
                    c.shad_to[0].style.width = to_max + "%";
1309
                } else {
1310
                    c.shad_to[0].style.display = "none";
1311
                }
1312
            }
1313
        },
1314
 
1315
 
1316
 
1317
        // =============================================================================================================
1318
        // Service methods
1319
 
1320
        toggleInput: function () {
1321
            this.$cache.input.toggleClass("irs-hidden-input");
1322
        },
1323
 
1324
        calcPercent: function (num) {
1325
            var w = (this.options.max - this.options.min) / 100,
1326
                percent = (num - this.options.min) / w;
1327
 
1328
            return this.toFixed(percent);
1329
        },
1330
 
1331
        calcReal: function (percent) {
1332
            var min = this.options.min,
1333
                max = this.options.max,
1334
                min_decimals = min.toString().split(".")[1],
1335
                max_decimals = max.toString().split(".")[1],
1336
                min_length, max_length,
1337
                avg_decimals = 0,
1338
                abs = 0;
1339
 
1340
            if (percent === 0) {
1341
                return this.options.min;
1342
            }
1343
            if (percent === 100) {
1344
                return this.options.max;
1345
            }
1346
 
1347
 
1348
            if (min_decimals) {
1349
                min_length = min_decimals.length;
1350
                avg_decimals = min_length;
1351
            }
1352
            if (max_decimals) {
1353
                max_length = max_decimals.length;
1354
                avg_decimals = max_length;
1355
            }
1356
            if (min_length && max_length) {
1357
                avg_decimals = (min_length >= max_length) ? min_length : max_length;
1358
            }
1359
 
1360
            if (min < 0) {
1361
                abs = Math.abs(min);
1362
                min = +(min + abs).toFixed(avg_decimals);
1363
                max = +(max + abs).toFixed(avg_decimals);
1364
            }
1365
 
1366
            var number = ((max - min) / 100 * percent) + min,
1367
                string = this.options.step.toString().split(".")[1],
1368
                result;
1369
 
1370
            if (string) {
1371
                number = +number.toFixed(string.length);
1372
            } else {
1373
                number = number / this.options.step;
1374
                number = number * this.options.step;
1375
 
1376
                number = +number.toFixed(0);
1377
            }
1378
 
1379
            if (abs) {
1380
                number -= abs;
1381
            }
1382
 
1383
            if (string) {
1384
                result = +number.toFixed(string.length);
1385
            } else {
1386
                result = this.toFixed(number);
1387
            }
1388
 
1389
            if (result < this.options.min) {
1390
                result = this.options.min;
1391
            } else if (result > this.options.max) {
1392
                result = this.options.max;
1393
            }
1394
 
1395
            return result;
1396
        },
1397
 
1398
        calcWithStep: function (percent) {
1399
            var rounded = Math.round(percent / this.coords.p_step) * this.coords.p_step;
1400
 
1401
            if (rounded > 100) {
1402
                rounded = 100;
1403
            }
1404
            if (percent === 100) {
1405
                rounded = 100;
1406
            }
1407
 
1408
            return this.toFixed(rounded);
1409
        },
1410
 
1411
        checkMinInterval: function (p_current, p_next, type) {
1412
            var o = this.options,
1413
                current,
1414
                next;
1415
 
1416
            if (!o.min_interval) {
1417
                return p_current;
1418
            }
1419
 
1420
            current = this.calcReal(p_current);
1421
            next = this.calcReal(p_next);
1422
 
1423
            if (type === "from") {
1424
 
1425
                if (next - current < o.min_interval) {
1426
                    current = next - o.min_interval;
1427
                }
1428
 
1429
            } else {
1430
 
1431
                if (current - next < o.min_interval) {
1432
                    current = next + o.min_interval;
1433
                }
1434
 
1435
            }
1436
 
1437
            return this.calcPercent(current);
1438
        },
1439
 
1440
        checkMaxInterval: function (p_current, p_next, type) {
1441
            var o = this.options,
1442
                current,
1443
                next;
1444
 
1445
            if (!o.max_interval) {
1446
                return p_current;
1447
            }
1448
 
1449
            current = this.calcReal(p_current);
1450
            next = this.calcReal(p_next);
1451
 
1452
            if (type === "from") {
1453
 
1454
                if (next - current > o.max_interval) {
1455
                    current = next - o.max_interval;
1456
                }
1457
 
1458
            } else {
1459
 
1460
                if (current - next > o.max_interval) {
1461
                    current = next + o.max_interval;
1462
                }
1463
 
1464
            }
1465
 
1466
            return this.calcPercent(current);
1467
        },
1468
 
1469
        checkDiapason: function (p_num, min, max) {
1470
            var num = this.calcReal(p_num),
1471
                o = this.options;
1472
 
1473
            if (typeof min !== "number") {
1474
                min = o.min;
1475
            }
1476
 
1477
            if (typeof max !== "number") {
1478
                max = o.max;
1479
            }
1480
 
1481
            if (num < min) {
1482
                num = min;
1483
            }
1484
 
1485
            if (num > max) {
1486
                num = max;
1487
            }
1488
 
1489
            return this.calcPercent(num);
1490
        },
1491
 
1492
        toFixed: function (num) {
1493
            num = num.toFixed(9);
1494
            return +num;
1495
        },
1496
 
1497
        _prettify: function (num) {
1498
            if (!this.options.prettify_enabled) {
1499
                return num;
1500
            }
1501
 
1502
            if (this.options.prettify && typeof this.options.prettify === "function") {
1503
                return this.options.prettify(num);
1504
            } else {
1505
                return this.prettify(num);
1506
            }
1507
        },
1508
 
1509
        prettify: function (num) {
1510
            var n = num.toString();
1511
            return n.replace(/(\d{1,3}(?=(?:\d\d\d)+(?!\d)))/g, "$1" + this.options.prettify_separator);
1512
        },
1513
 
1514
        checkEdges: function (left, width) {
1515
            if (!this.options.force_edges) {
1516
                return this.toFixed(left);
1517
            }
1518
 
1519
            if (left < 0) {
1520
                left = 0;
1521
            } else if (left > 100 - width) {
1522
                left = 100 - width;
1523
            }
1524
 
1525
            return this.toFixed(left);
1526
        },
1527
 
1528
        validate: function () {
1529
            var o = this.options,
1530
                r = this.result,
1531
                v = o.values,
1532
                vl = v.length,
1533
                value,
1534
                i;
1535
 
1536
            if (typeof o.min === "string") o.min = +o.min;
1537
            if (typeof o.max === "string") o.max = +o.max;
1538
            if (typeof o.from === "string") o.from = +o.from;
1539
            if (typeof o.to === "string") o.to = +o.to;
1540
            if (typeof o.step === "string") o.step = +o.step;
1541
 
1542
            if (typeof o.from_min === "string") o.from_min = +o.from_min;
1543
            if (typeof o.from_max === "string") o.from_max = +o.from_max;
1544
            if (typeof o.to_min === "string") o.to_min = +o.to_min;
1545
            if (typeof o.to_max === "string") o.to_max = +o.to_max;
1546
 
1547
            if (typeof o.keyboard_step === "string") o.keyboard_step = +o.keyboard_step;
1548
            if (typeof o.grid_num === "string") o.grid_num = +o.grid_num;
1549
 
1550
            if (o.max <= o.min) {
1551
                if (o.min) {
1552
                    o.max = o.min * 2;
1553
                } else {
1554
                    o.max = o.min + 1;
1555
                }
1556
                o.step = 1;
1557
            }
1558
 
1559
            if (vl) {
1560
                o.p_values = [];
1561
                o.min = 0;
1562
                o.max = vl - 1;
1563
                o.step = 1;
1564
                o.grid_num = o.max;
1565
                o.grid_snap = true;
1566
 
1567
 
1568
                for (i = 0; i < vl; i++) {
1569
                    value = +v[i];
1570
 
1571
                    if (!isNaN(value)) {
1572
                        v[i] = value;
1573
                        value = this._prettify(value);
1574
                    } else {
1575
                        value = v[i];
1576
                    }
1577
 
1578
                    o.p_values.push(value);
1579
                }
1580
            }
1581
 
1582
            if (typeof o.from !== "number" || isNaN(o.from)) {
1583
                o.from = o.min;
1584
            }
1585
 
1586
            if (typeof o.to !== "number" || isNaN(o.from)) {
1587
                o.to = o.max;
1588
            }
1589
 
1590
            if (o.type === "single") {
1591
 
1592
                if (o.from < o.min) {
1593
                    o.from = o.min;
1594
                }
1595
 
1596
                if (o.from > o.max) {
1597
                    o.from = o.max;
1598
                }
1599
 
1600
            } else {
1601
 
1602
                if (o.from < o.min || o.from > o.max) {
1603
                    o.from = o.min;
1604
                }
1605
                if (o.to > o.max || o.to < o.min) {
1606
                    o.to = o.max;
1607
                }
1608
                if (o.from > o.to) {
1609
                    o.from = o.to;
1610
                }
1611
 
1612
            }
1613
 
1614
            if (typeof o.step !== "number" || isNaN(o.step) || !o.step || o.step < 0) {
1615
                o.step = 1;
1616
            }
1617
 
1618
            if (typeof o.keyboard_step !== "number" || isNaN(o.keyboard_step) || !o.keyboard_step || o.keyboard_step < 0) {
1619
                o.keyboard_step = 5;
1620
            }
1621
 
1622
            if (typeof o.from_min === "number" && o.from < o.from_min) {
1623
                o.from = o.from_min;
1624
            }
1625
 
1626
            if (typeof o.from_max === "number" && o.from > o.from_max) {
1627
                o.from = o.from_max;
1628
            }
1629
 
1630
            if (typeof o.to_min === "number" && o.to < o.to_min) {
1631
                o.to = o.to_min;
1632
            }
1633
 
1634
            if (typeof o.to_max === "number" && o.from > o.to_max) {
1635
                o.to = o.to_max;
1636
            }
1637
 
1638
            if (r) {
1639
                if (r.min !== o.min) {
1640
                    r.min = o.min;
1641
                }
1642
 
1643
                if (r.max !== o.max) {
1644
                    r.max = o.max;
1645
                }
1646
 
1647
                if (r.from < r.min || r.from > r.max) {
1648
                    r.from = o.from;
1649
                }
1650
 
1651
                if (r.to < r.min || r.to > r.max) {
1652
                    r.to = o.to;
1653
                }
1654
            }
1655
 
1656
            if (typeof o.min_interval !== "number" || isNaN(o.min_interval) || !o.min_interval || o.min_interval < 0) {
1657
                o.min_interval = 0;
1658
            }
1659
 
1660
            if (typeof o.max_interval !== "number" || isNaN(o.max_interval) || !o.max_interval || o.max_interval < 0) {
1661
                o.max_interval = 0;
1662
            }
1663
 
1664
            if (o.min_interval && o.min_interval > o.max - o.min) {
1665
                o.min_interval = o.max - o.min;
1666
            }
1667
 
1668
            if (o.max_interval && o.max_interval > o.max - o.min) {
1669
                o.max_interval = o.max - o.min;
1670
            }
1671
        },
1672
 
1673
        decorate: function (num, original) {
1674
            var decorated = "",
1675
                o = this.options;
1676
 
1677
            if (o.prefix) {
1678
                decorated += o.prefix;
1679
            }
1680
 
1681
            decorated += num;
1682
 
1683
            if (o.max_postfix) {
1684
                if (o.values.length && num === o.p_values[o.max]) {
1685
                    decorated += o.max_postfix;
1686
                    if (o.postfix) {
1687
                        decorated += " ";
1688
                    }
1689
                } else if (original === o.max) {
1690
                    decorated += o.max_postfix;
1691
                    if (o.postfix) {
1692
                        decorated += " ";
1693
                    }
1694
                }
1695
            }
1696
 
1697
            if (o.postfix) {
1698
                decorated += o.postfix;
1699
            }
1700
 
1701
            return decorated;
1702
        },
1703
 
1704
        updateFrom: function () {
1705
            this.result.from = this.options.from;
1706
            this.result.from_percent = this.calcPercent(this.result.from);
1707
            if (this.options.values) {
1708
                this.result.from_value = this.options.values[this.result.from];
1709
            }
1710
        },
1711
 
1712
        updateTo: function () {
1713
            this.result.to = this.options.to;
1714
            this.result.to_percent = this.calcPercent(this.result.to);
1715
            if (this.options.values) {
1716
                this.result.to_value = this.options.values[this.result.to];
1717
            }
1718
        },
1719
 
1720
        updateResult: function () {
1721
            this.result.min = this.options.min;
1722
            this.result.max = this.options.max;
1723
            this.updateFrom();
1724
            this.updateTo();
1725
        },
1726
 
1727
 
1728
        // =============================================================================================================
1729
        // Grid
1730
 
1731
        appendGrid: function () {
1732
            if (!this.options.grid) {
1733
                return;
1734
            }
1735
 
1736
            var o = this.options,
1737
                i, z,
1738
 
1739
                total = o.max - o.min,
1740
                big_num = o.grid_num,
1741
                big_p = 0,
1742
                big_w = 0,
1743
 
1744
                small_max = 4,
1745
                local_small_max,
1746
                small_p,
1747
                small_w = 0,
1748
 
1749
                result,
1750
                html = '';
1751
 
1752
 
1753
 
1754
            this.calcGridMargin();
1755
 
1756
            if (o.grid_snap) {
1757
                big_num = total / o.step;
1758
                big_p = this.toFixed(o.step / (total / 100));
1759
            } else {
1760
                big_p = this.toFixed(100 / big_num);
1761
            }
1762
 
1763
            if (big_num > 4) {
1764
                small_max = 3;
1765
            }
1766
            if (big_num > 7) {
1767
                small_max = 2;
1768
            }
1769
            if (big_num > 14) {
1770
                small_max = 1;
1771
            }
1772
            if (big_num > 28) {
1773
                small_max = 0;
1774
            }
1775
 
1776
            for (i = 0; i < big_num + 1; i++) {
1777
                local_small_max = small_max;
1778
 
1779
                big_w = this.toFixed(big_p * i);
1780
 
1781
                if (big_w > 100) {
1782
                    big_w = 100;
1783
 
1784
                    local_small_max -= 2;
1785
                    if (local_small_max < 0) {
1786
                        local_small_max = 0;
1787
                    }
1788
                }
1789
                this.coords.big[i] = big_w;
1790
 
1791
                small_p = (big_w - (big_p * (i - 1))) / (local_small_max + 1);
1792
 
1793
                for (z = 1; z <= local_small_max; z++) {
1794
                    if (big_w === 0) {
1795
                        break;
1796
                    }
1797
 
1798
                    small_w = this.toFixed(big_w - (small_p * z));
1799
 
1800
                    html += '<span class="irs-grid-pol small" style="left: ' + small_w + '%"></span>';
1801
                }
1802
 
1803
                html += '<span class="irs-grid-pol" style="left: ' + big_w + '%"></span>';
1804
 
1805
                result = this.calcReal(big_w);
1806
                if (o.values.length) {
1807
                    result = o.p_values[result];
1808
                } else {
1809
                    result = this._prettify(result);
1810
                }
1811
 
1812
                html += '<span class="irs-grid-text js-grid-text-' + i + '" style="left: ' + big_w + '%">' + result + '</span>';
1813
            }
1814
            this.coords.big_num = Math.ceil(big_num + 1);
1815
 
1816
 
1817
 
1818
            this.$cache.cont.addClass("irs-with-grid");
1819
            this.$cache.grid.html(html);
1820
            this.cacheGridLabels();
1821
        },
1822
 
1823
        cacheGridLabels: function () {
1824
            var $label, i,
1825
                num = this.coords.big_num;
1826
 
1827
            for (i = 0; i < num; i++) {
1828
                $label = this.$cache.grid.find(".js-grid-text-" + i);
1829
                this.$cache.grid_labels.push($label);
1830
            }
1831
 
1832
            this.calcGridLabels();
1833
        },
1834
 
1835
        calcGridLabels: function () {
1836
            var i, label, start = [], finish = [],
1837
                num = this.coords.big_num;
1838
 
1839
            for (i = 0; i < num; i++) {
1840
                this.coords.big_w[i] = this.$cache.grid_labels[i].outerWidth(false);
1841
                this.coords.big_p[i] = this.toFixed(this.coords.big_w[i] / this.coords.w_rs * 100);
1842
                this.coords.big_x[i] = this.toFixed(this.coords.big_p[i] / 2);
1843
 
1844
                start[i] = this.toFixed(this.coords.big[i] - this.coords.big_x[i]);
1845
                finish[i] = this.toFixed(start[i] + this.coords.big_p[i]);
1846
            }
1847
 
1848
            if (this.options.force_edges) {
1849
                if (start[0] < -this.coords.grid_gap) {
1850
                    start[0] = -this.coords.grid_gap;
1851
                    finish[0] = this.toFixed(start[0] + this.coords.big_p[0]);
1852
 
1853
                    this.coords.big_x[0] = this.coords.grid_gap;
1854
                }
1855
 
1856
                if (finish[num - 1] > 100 + this.coords.grid_gap) {
1857
                    finish[num - 1] = 100 + this.coords.grid_gap;
1858
                    start[num - 1] = this.toFixed(finish[num - 1] - this.coords.big_p[num - 1]);
1859
 
1860
                    this.coords.big_x[num - 1] = this.toFixed(this.coords.big_p[num - 1] - this.coords.grid_gap);
1861
                }
1862
            }
1863
 
1864
            this.calcGridCollision(2, start, finish);
1865
            this.calcGridCollision(4, start, finish);
1866
 
1867
            for (i = 0; i < num; i++) {
1868
                label = this.$cache.grid_labels[i][0];
1869
                label.style.marginLeft = -this.coords.big_x[i] + "%";
1870
            }
1871
        },
1872
 
1873
        // Collisions Calc Beta
1874
        // TODO: Refactor then have plenty of time
1875
        calcGridCollision: function (step, start, finish) {
1876
            var i, next_i, label,
1877
                num = this.coords.big_num;
1878
 
1879
            for (i = 0; i < num; i += step) {
1880
                next_i = i + (step / 2);
1881
                if (next_i >= num) {
1882
                    break;
1883
                }
1884
 
1885
                label = this.$cache.grid_labels[next_i][0];
1886
 
1887
                if (finish[i] <= start[next_i]) {
1888
                    label.style.visibility = "visible";
1889
                } else {
1890
                    label.style.visibility = "hidden";
1891
                }
1892
            }
1893
        },
1894
 
1895
        calcGridMargin: function () {
1896
            if (!this.options.grid_margin) {
1897
                return;
1898
            }
1899
 
1900
            this.coords.w_rs = this.$cache.rs.outerWidth(false);
1901
            if (!this.coords.w_rs) {
1902
                return;
1903
            }
1904
 
1905
            if (this.options.type === "single") {
1906
                this.coords.w_handle = this.$cache.s_single.outerWidth(false);
1907
            } else {
1908
                this.coords.w_handle = this.$cache.s_from.outerWidth(false);
1909
            }
1910
            this.coords.p_handle = this.toFixed(this.coords.w_handle  / this.coords.w_rs * 100);
1911
            this.coords.grid_gap = this.toFixed((this.coords.p_handle / 2) - 0.1);
1912
 
1913
            this.$cache.grid[0].style.width = this.toFixed(100 - this.coords.p_handle) + "%";
1914
            this.$cache.grid[0].style.left = this.coords.grid_gap + "%";
1915
        },
1916
 
1917
 
1918
 
1919
        // =============================================================================================================
1920
        // Public methods
1921
 
1922
        update: function (options) {
1923
            if (!this.input) {
1924
                return;
1925
            }
1926
 
1927
            this.is_update = true;
1928
 
1929
            this.options.from = this.result.from;
1930
            this.options.to = this.result.to;
1931
 
1932
            this.options = $.extend(this.options, options);
1933
            this.validate();
1934
            this.updateResult(options);
1935
 
1936
            this.toggleInput();
1937
            this.remove();
1938
            this.init(true);
1939
        },
1940
 
1941
        reset: function () {
1942
            if (!this.input) {
1943
                return;
1944
            }
1945
 
1946
            this.updateResult();
1947
            this.update();
1948
        },
1949
 
1950
        destroy: function () {
1951
            if (!this.input) {
1952
                return;
1953
            }
1954
 
1955
            this.toggleInput();
1956
            this.$cache.input.prop("readonly", false);
1957
            $.data(this.input, "ionRangeSlider", null);
1958
 
1959
            this.remove();
1960
            this.input = null;
1961
            this.options = null;
1962
        }
1963
    };
1964
 
1965
    $.fn.ionRangeSlider = function (options) {
1966
        return this.each(function() {
1967
            if (!$.data(this, "ionRangeSlider")) {
1968
                $.data(this, "ionRangeSlider", new IonRangeSlider(this, options, plugin_count++));
1969
            }
1970
        });
1971
    };
1972
 
1973
 
1974
 
1975
    // =================================================================================================================
1976
    // http://paulirish.com/2011/requestanimationframe-for-smart-animating/
1977
    // http://my.opera.com/emoller/blog/2011/12/20/requestanimationframe-for-smart-er-animating
1978
 
1979
    // requestAnimationFrame polyfill by Erik Möller. fixes from Paul Irish and Tino Zijdel
1980
 
1981
    // MIT license
1982
 
1983
    (function() {
1984
        var lastTime = 0;
1985
        var vendors = ['ms', 'moz', 'webkit', 'o'];
1986
        for(var x = 0; x < vendors.length && !window.requestAnimationFrame; ++x) {
1987
            window.requestAnimationFrame = window[vendors[x]+'RequestAnimationFrame'];
1988
            window.cancelAnimationFrame = window[vendors[x]+'CancelAnimationFrame']
1989
                || window[vendors[x]+'CancelRequestAnimationFrame'];
1990
        }
1991
 
1992
        if (!window.requestAnimationFrame)
1993
            window.requestAnimationFrame = function(callback, element) {
1994
                var currTime = new Date().getTime();
1995
                var timeToCall = Math.max(0, 16 - (currTime - lastTime));
1996
                var id = window.setTimeout(function() { callback(currTime + timeToCall); },
1997
                    timeToCall);
1998
                lastTime = currTime + timeToCall;
1999
                return id;
2000
            };
2001
 
2002
        if (!window.cancelAnimationFrame)
2003
            window.cancelAnimationFrame = function(id) {
2004
                clearTimeout(id);
2005
            };
2006
    }());
2007
 
2008
} (jQuery, document, window, navigator));