PageSourceSearch

https://www.mofed.gov.et/static/assets/js/ion.rangeSlider.js

js mofed.gov.et collected 2026-09-24 12:06:58 UTC 84,561 bytes, 2,450 lines download raw bytes

1// Ion.RangeSlider
2// version 2.2.0 Build: 380
3// © Denis Ineshin, 2017
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(factory) {
14    if (typeof define === "function" && define.amd) {
15        define(["jquery"], function (jQuery) {
16            return factory(jQuery, document, window, navigator);
17        });
18    } else if (typeof exports === "object") {
19        factory(require("jquery"), document, window, navigator);
20    } else {
21        factory(jQuery, document, window, navigator);
22    }
23} (function ($, document, window, navigator, undefined) {
24    "use strict";
25
26    // =================================================================================================================
27    // Service
28
29    var plugin_count = 0;
30
31    // IE8 fix
32    var is_old_ie = (function () {
33        var n = navigator.userAgent,
34            r = /msie\s\d+/i,
35            v;
36        if (n.search(r) > 0) {
37            v = r.exec(n).toString();
38            v = v.split(" ")[1];
39            if (v < 9) {
40                $("html").addClass("lt-ie9");
41                return true;
42            }
43        }
44        return false;
45    } ());
46    if (!Function.prototype.bind) {
47        Function.prototype.bind = function bind(that) {
48
49            var target = this;
50            var slice = [].slice;
51
52            if (typeof target != "function") {
53                throw new TypeError();
54            }
55
56            var args = slice.call(arguments, 1),
57                bound = function () {
58
59                    if (this instanceof bound) {
60
61                        var F = function(){};
62                        F.prototype = target.prototype;
63                        var self = new F();
64
65                        var result = target.apply(
66                            self,
67                            args.concat(slice.call(arguments))
68                        );
69                        if (Object(result) === result) {
70                            return result;
71                        }
72                        return self;
73
74                    } else {
75
76                        return target.apply(
77                            that,
78                            args.concat(slice.call(arguments))
79                        );
80
81                    }
82
83                };
84
85            return bound;
86        };
87    }
88    if (!Array.prototype.indexOf) {
89        Array.prototype.indexOf = function(searchElement, fromIndex) {
90            var k;
91            if (this == null) {
92                throw new TypeError('"this" is null or not defined');
93            }
94            var O = Object(this);
95            var len = O.length >>> 0;
96            if (len === 0) {
97                return -1;
98            }
99            var n = +fromIndex || 0;
100            if (Math.abs(n) === Infinity) {
101                n = 0;
102            }
103            if (n >= len) {
104                return -1;
105            }
106            k = Math.max(n >= 0 ? n : len - Math.abs(n), 0);
107            while (k < len) {
108                if (k in O && O[k] === searchElement) {
109                    return k;
110                }
111                k++;
112            }
113            return -1;
114        };
115    }
116
117
118
119    // =================================================================================================================
120    // Template
121
122    var base_html =
123        '<span class="irs">' +
124        '<span class="irs-line" tabindex="0"><span class="irs-line-left"></span><span class="irs-line-mid"></span><span class="irs-line-right"></span></span>' +
125        '<span class="irs-min">0</span><span class="irs-max">1</span>' +
126        '<span class="irs-from">0</span><span class="irs-to">0</span><span class="irs-single">0</span>' +
127        '</span>' +
128        '<span class="irs-grid"></span>' +
129        '<span class="irs-bar"></span>';
130
131    var single_html =
132        '<span class="irs-bar-edge"></span>' +
133        '<span class="irs-shadow shadow-single"></span>' +
134        '<span class="irs-slider single"></span>';
135
136    var double_html =
137        '<span class="irs-shadow shadow-from"></span>' +
138        '<span class="irs-shadow shadow-to"></span>' +
139        '<span class="irs-slider from"></span>' +
140        '<span class="irs-slider to"></span>';
141
142    var disable_html =
143        '<span class="irs-disable-mask"></span>';
144
145
146
147    // =================================================================================================================
148    // Core
149
150    /**
151     * Main plugin constructor
152     *
153     * @param input {Object} link to base input element
154     * @param options {Object} slider config
155     * @param plugin_count {Number}
156     * @constructor
157     */
158    var IonRangeSlider = function (input, options, plugin_count) {
159        this.VERSION = "2.2.0";
160        this.input = input;
161        this.plugin_count = plugin_count;
162        this.current_plugin = 0;
163        this.calc_count = 0;
164        this.update_tm = 0;
165        this.old_from = 0;
166        this.old_to = 0;
167        this.old_min_interval = null;
168        this.raf_id = null;
169        this.dragging = false;
170        this.force_redraw = false;
171        this.no_diapason = false;
172        this.has_tab_index = true;
173        this.is_key = false;
174        this.is_update = false;
175        this.is_start = true;
176        this.is_finish = false;
177        this.is_active = false;
178        this.is_resize = false;
179        this.is_click = false;
180
181        options = options || {};
182
183        // cache for links to all DOM elements
184        this.$cache = {
185            win: $(window),
186            body: $(document.body),
187            input: $(input),
188            cont: null,
189            rs: null,
190            min: null,
191            max: null,
192            from: null,
193            to: null,
194            single: null,
195            bar: null,
196            line: null,
197            s_single: null,
198            s_from: null,
199            s_to: null,
200            shad_single: null,
201            shad_from: null,
202            shad_to: null,
203            edge: null,
204            grid: null,
205            grid_labels: []
206        };
207
208        // storage for measure variables
209        this.coords = {
210            // left
211            x_gap: 0,
212            x_pointer: 0,
213
214            // width
215            w_rs: 0,
216            w_rs_old: 0,
217            w_handle: 0,
218
219            // percents
220            p_gap: 0,
221            p_gap_left: 0,
222            p_gap_right: 0,
223            p_step: 0,
224            p_pointer: 0,
225            p_handle: 0,
226            p_single_fake: 0,
227            p_single_real: 0,
228            p_from_fake: 0,
229            p_from_real: 0,
230            p_to_fake: 0,
231            p_to_real: 0,
232            p_bar_x: 0,
233            p_bar_w: 0,
234
235            // grid
236            grid_gap: 0,
237            big_num: 0,
238            big: [],
239            big_w: [],
240            big_p: [],
241            big_x: []
242        };
243
244        // storage for labels measure variables
245        this.labels = {
246            // width
247            w_min: 0,
248            w_max: 0,
249            w_from: 0,
250            w_to: 0,
251            w_single: 0,
252
253            // percents
254            p_min: 0,
255            p_max: 0,
256            p_from_fake: 0,
257            p_from_left: 0,
258            p_to_fake: 0,
259            p_to_left: 0,
260            p_single_fake: 0,
261            p_single_left: 0
262        };
263
264
265
266        /**
267         * get and validate config
268         */
269        var $inp = this.$cache.input,
270            val = $inp.prop("value"),
271            config, config_from_data, prop;
272
273        // default config
274        config = {
275            type: "single",
276
277            min: 10,
278            max: 100,
279            from: null,
280            to: null,
281            step: 1,
282
283            min_interval: 0,
284            max_interval: 0,
285            drag_interval: false,
286
287            values: [],
288            p_values: [],
289
290            from_fixed: false,
291            from_min: null,
292            from_max: null,
293            from_shadow: false,
294
295            to_fixed: false,
296            to_min: null,
297            to_max: null,
298            to_shadow: false,
299
300            prettify_enabled: true,
301            prettify_separator: " ",
302            prettify: null,
303
304            force_edges: false,
305
306            keyboard: true,
307
308            grid: false,
309            grid_margin: true,
310            grid_num: 4,
311            grid_snap: false,
312
313            hide_min_max: false,
314            hide_from_to: false,
315
316            prefix: "",
317            postfix: "",
318            max_postfix: "",
319            decorate_both: true,
320            values_separator: " — ",
321
322            input_values_separator: ";",
323
324            disable: false,
325            block: false,
326
327            extra_classes: "",
328
329            scope: null,
330            onStart: null,
331            onChange: null,
332            onFinish: null,
333            onUpdate: null
334        };
335
336
337        // check if base element is input
338        if ($inp[0].nodeName !== "INPUT") {
339            console && console.warn && console.warn("Base element should be <input>!", $inp[0]);
340        }
341
342
343        // config from data-attributes extends js config
344        config_from_data = {
345            type: $inp.data("type"),
346
347            min: $inp.data("min"),
348            max: $inp.data("max"),
349            from: $inp.data("from"),
350            to: $inp.data("to"),
351            step: $inp.data("step"),
352
353            min_interval: $inp.data("minInterval"),
354            max_interval: $inp.data("maxInterval"),
355            drag_interval: $inp.data("dragInterval"),
356
357            values: $inp.data("values"),
358
359            from_fixed: $inp.data("fromFixed"),
360            from_min: $inp.data("fromMin"),
361            from_max: $inp.data("fromMax"),
362            from_shadow: $inp.data("fromShadow"),
363
364            to_fixed: $inp.data("toFixed"),
365            to_min: $inp.data("toMin"),
366            to_max: $inp.data("toMax"),
367            to_shadow: $inp.data("toShadow"),
368
369            prettify_enabled: $inp.data("prettifyEnabled"),
370            prettify_separator: $inp.data("prettifySeparator"),
371
372            force_edges: $inp.data("forceEdges"),
373
374            keyboard: $inp.data("keyboard"),
375
376            grid: $inp.data("grid"),
377            grid_margin: $inp.data("gridMargin"),
378            grid_num: $inp.data("gridNum"),
379            grid_snap: $inp.data("gridSnap"),
380
381            hide_min_max: $inp.data("hideMinMax"),
382            hide_from_to: $inp.data("hideFromTo"),
383
384            prefix: $inp.data("prefix"),
385            postfix: $inp.data("postfix"),
386            max_postfix: $inp.data("maxPostfix"),
387            decorate_both: $inp.data("decorateBoth"),
388            values_separator: $inp.data("valuesSeparator"),
389
390            input_values_separator: $inp.data("inputValuesSeparator"),
391
392            disable: $inp.data("disable"),
393            block: $inp.data("block"),
394
395            extra_classes: $inp.data("extraClasses"),
396        };
397        config_from_data.values = config_from_data.values && config_from_data.values.split(",");
398
399        for (prop in config_from_data) {
400            if (config_from_data.hasOwnProperty(prop)) {
401                if (config_from_data[prop] === undefined || config_from_data[prop] === "") {
402                    delete config_from_data[prop];
403                }
404            }
405        }
406
407
408        // input value extends default config
409        if (val !== undefined && val !== "") {
410            val = val.split(config_from_data.input_values_separator || options.input_values_separator || ";");
411
412            if (val[0] && val[0] == +val[0]) {
413                val[0] = +val[0];
414            }
415            if (val[1] && val[1] == +val[1]) {
416                val[1] = +val[1];
417            }
418
419            if (options && options.values && options.values.length) {
420                config.from = val[0] && options.values.indexOf(val[0]);
421                config.to = val[1] && options.values.indexOf(val[1]);
422            } else {
423                config.from = val[0] && +val[0];
424                config.to = val[1] && +val[1];
425            }
426        }
427
428
429
430        // js config extends default config
431        $.extend(config, options);
432
433
434        // data config extends config
435        $.extend(config, config_from_data);
436        this.options = config;
437
438
439
440        // validate config, to be sure that all data types are correct
441        this.update_check = {};
442        this.validate();
443
444
445
446        // default result object, returned to callbacks
447        this.result = {
448            input: this.$cache.input,
449            slider: null,
450
451            min: this.options.min,
452            max: this.options.max,
453
454            from: this.options.from,
455            from_percent: 0,
456            from_value: null,
457
458            to: this.options.to,
459            to_percent: 0,
460            to_value: null
461        };
462
463
464
465        this.init();
466    };
467
468    IonRangeSlider.prototype = {
469
470        /**
471         * Starts or updates the plugin instance
472         *
473         * @param [is_update] {boolean}
474         */
475        init: function (is_update) {
476            this.no_diapason = false;
477            this.coords.p_step = this.convertToPercent(this.options.step, true);
478
479            this.target = "base";
480
481            this.toggleInput();
482            this.append();
483            this.setMinMax();
484
485            if (is_update) {
486                this.force_redraw = true;
487                this.calc(true);
488
489                // callbacks called
490                this.callOnUpdate();
491            } else {
492                this.force_redraw = true;
493                this.calc(true);
494
495                // callbacks called
496                this.callOnStart();
497            }
498
499            this.updateScene();
500        },
501
502        /**
503         * Appends slider template to a DOM
504         */
505        append: function () {
506            var container_html = '<span class="irs js-irs-' + this.plugin_count + ' ' + this.options.extra_classes + '"></span>';
507            this.$cache.input.before(container_html);
508            this.$cache.input.prop("readonly", true);
509            this.$cache.cont = this.$cache.input.prev();
510            this.result.slider = this.$cache.cont;
511
512            this.$cache.cont.html(base_html);
513            this.$cache.rs = this.$cache.cont.find(".irs");
514            this.$cache.min = this.$cache.cont.find(".irs-min");
515            this.$cache.max = this.$cache.cont.find(".irs-max");
516            this.$cache.from = this.$cache.cont.find(".irs-from");
517            this.$cache.to = this.$cache.cont.find(".irs-to");
518            this.$cache.single = this.$cache.cont.find(".irs-single");
519            this.$cache.bar = this.$cache.cont.find(".irs-bar");
520            this.$cache.line = this.$cache.cont.find(".irs-line");
521            this.$cache.grid = this.$cache.cont.find(".irs-grid");
522
523            if (this.options.type === "single") {
524                this.$cache.cont.append(single_html);
525                this.$cache.edge = this.$cache.cont.find(".irs-bar-edge");
526                this.$cache.s_single = this.$cache.cont.find(".single");
527                this.$cache.from[0].style.visibility = "hidden";
528                this.$cache.to[0].style.visibility = "hidden";
529                this.$cache.shad_single = this.$cache.cont.find(".shadow-single");
530            } else {
531                this.$cache.cont.append(double_html);
532                this.$cache.s_from = this.$cache.cont.find(".from");
533                this.$cache.s_to = this.$cache.cont.find(".to");
534                this.$cache.shad_from = this.$cache.cont.find(".shadow-from");
535                this.$cache.shad_to = this.$cache.cont.find(".shadow-to");
536
537                this.setTopHandler();
538            }
539
540            if (this.options.hide_from_to) {
541                this.$cache.from[0].style.display = "none";
542                this.$cache.to[0].style.display = "none";
543                this.$cache.single[0].style.display = "none";
544            }
545
546            this.appendGrid();
547
548            if (this.options.disable) {
549                this.appendDisableMask();
550                this.$cache.input[0].disabled = true;
551            } else {
552                this.$cache.input[0].disabled = false;
553                this.removeDisableMask();
554                this.bindEvents();
555            }
556
557            // block only if not disabled
558            if (!this.options.disable) {
559                if (this.options.block) {
560                    this.appendDisableMask();
561                } else {
562                    this.removeDisableMask();
563                }
564            }
565
566            if (this.options.drag_interval) {
567                this.$cache.bar[0].style.cursor = "ew-resize";
568            }
569        },
570
571        /**
572         * Determine which handler has a priority
573         * works only for double slider type
574         */
575        setTopHandler: function () {
576            var min = this.options.min,
577                max = this.options.max,
578                from = this.options.from,
579                to = this.options.to;
580
581            if (from > min && to === max) {
582                this.$cache.s_from.addClass("type_last");
583            } else if (to < max) {
584                this.$cache.s_to.addClass("type_last");
585            }
586        },
587
588        /**
589         * Determine which handles was clicked last
590         * and which handler should have hover effect
591         *
592         * @param target {String}
593         */
594        changeLevel: function (target) {
595            switch (target) {
596                case "single":
597                    this.coords.p_gap = this.toFixed(this.coords.p_pointer - this.coords.p_single_fake);
598                    this.$cache.s_single.addClass("state_hover");
599                    break;
600                case "from":
601                    this.coords.p_gap = this.toFixed(this.coords.p_pointer - this.coords.p_from_fake);
602                    this.$cache.s_from.addClass("state_hover");
603                    this.$cache.s_from.addClass("type_last");
604                    this.$cache.s_to.removeClass("type_last");
605                    break;
606                case "to":
607                    this.coords.p_gap = this.toFixed(this.coords.p_pointer - this.coords.p_to_fake);
608                    this.$cache.s_to.addClass("state_hover");
609                    this.$cache.s_to.addClass("type_last");
610                    this.$cache.s_from.removeClass("type_last");
611                    break;
612                case "both":
613                    this.coords.p_gap_left = this.toFixed(this.coords.p_pointer - this.coords.p_from_fake);
614                    this.coords.p_gap_right = this.toFixed(this.coords.p_to_fake - this.coords.p_pointer);
615                    this.$cache.s_to.removeClass("type_last");
616                    this.$cache.s_from.removeClass("type_last");
617                    break;
618            }
619        },
620
621        /**
622         * Then slider is disabled
623         * appends extra layer with opacity
624         */
625        appendDisableMask: function () {
626            this.$cache.cont.append(disable_html);
627            this.$cache.cont.addClass("irs-disabled");
628        },
629
630        /**
631         * Then slider is not disabled
632         * remove disable mask
633         */
634        removeDisableMask: function () {
635            this.$cache.cont.remove(".irs-disable-mask");
636            this.$cache.cont.removeClass("irs-disabled");
637        },
638
639        /**
640         * Remove slider instance
641         * and unbind all events
642         */
643        remove: function () {
644            this.$cache.cont.remove();
645            this.$cache.cont = null;
646
647            this.$cache.line.off("keydown.irs_" + this.plugin_count);
648
649            this.$cache.body.off("touchmove.irs_" + this.plugin_count);
650            this.$cache.body.off("mousemove.irs_" + this.plugin_count);
651
652            this.$cache.win.off("touchend.irs_" + this.plugin_count);
653            this.$cache.win.off("mouseup.irs_" + this.plugin_count);
654
655            if (is_old_ie) {
656                this.$cache.body.off("mouseup.irs_" + this.plugin_count);
657                this.$cache.body.off("mouseleave.irs_" + this.plugin_count);
658            }
659
660            this.$cache.grid_labels = [];
661            this.coords.big = [];
662            this.coords.big_w = [];
663            this.coords.big_p = [];
664            this.coords.big_x = [];
665
666            cancelAnimationFrame(this.raf_id);
667        },
668
669        /**
670         * bind all slider events
671         */
672        bindEvents: function () {
673            if (this.no_diapason) {
674                return;
675            }
676
677            this.$cache.body.on("touchmove.irs_" + this.plugin_count, this.pointerMove.bind(this));
678            this.$cache.body.on("mousemove.irs_" + this.plugin_count, this.pointerMove.bind(this));
679
680            this.$cache.win.on("touchend.irs_" + this.plugin_count, this.pointerUp.bind(this));
681            this.$cache.win.on("mouseup.irs_" + this.plugin_count, this.pointerUp.bind(this));
682
683            this.$cache.line.on("touchstart.irs_" + this.plugin_count, this.pointerClick.bind(this, "click"));
684            this.$cache.line.on("mousedown.irs_" + this.plugin_count, this.pointerClick.bind(this, "click"));
685
686            this.$cache.line.on("focus.irs_" + this.plugin_count, this.pointerFocus.bind(this));
687
688            if (this.options.drag_interval && this.options.type === "double") {
689                this.$cache.bar.on("touchstart.irs_" + this.plugin_count, this.pointerDown.bind(this, "both"));
690                this.$cache.bar.on("mousedown.irs_" + this.plugin_count, this.pointerDown.bind(this, "both"));
691            } else {
692                this.$cache.bar.on("touchstart.irs_" + this.plugin_count, this.pointerClick.bind(this, "click"));
693                this.$cache.bar.on("mousedown.irs_" + this.plugin_count, this.pointerClick.bind(this, "click"));
694            }
695
696            if (this.options.type === "single") {
697                this.$cache.single.on("touchstart.irs_" + this.plugin_count, this.pointerDown.bind(this, "single"));
698                this.$cache.s_single.on("touchstart.irs_" + this.plugin_count, this.pointerDown.bind(this, "single"));
699                this.$cache.shad_single.on("touchstart.irs_" + this.plugin_count, this.pointerClick.bind(this, "click"));
700
701                this.$cache.single.on("mousedown.irs_" + this.plugin_count, this.pointerDown.bind(this, "single"));
702                this.$cache.s_single.on("mousedown.irs_" + this.plugin_count, this.pointerDown.bind(this, "single"));
703                this.$cache.edge.on("mousedown.irs_" + this.plugin_count, this.pointerClick.bind(this, "click"));
704                this.$cache.shad_single.on("mousedown.irs_" + this.plugin_count, this.pointerClick.bind(this, "click"));
705            } else {
706                this.$cache.single.on("touchstart.irs_" + this.plugin_count, this.pointerDown.bind(this, null));
707                this.$cache.single.on("mousedown.irs_" + this.plugin_count, this.pointerDown.bind(this, null));
708
709                this.$cache.from.on("touchstart.irs_" + this.plugin_count, this.pointerDown.bind(this, "from"));
710                this.$cache.s_from.on("touchstart.irs_" + this.plugin_count, this.pointerDown.bind(this, "from"));
711                this.$cache.to.on("touchstart.irs_" + this.plugin_count, this.pointerDown.bind(this, "to"));
712                this.$cache.s_to.on("touchstart.irs_" + this.plugin_count, this.pointerDown.bind(this, "to"));
713                this.$cache.shad_from.on("touchstart.irs_" + this.plugin_count, this.pointerClick.bind(this, "click"));
714                this.$cache.shad_to.on("touchstart.irs_" + this.plugin_count, this.pointerClick.bind(this, "click"));
715
716                this.$cache.from.on("mousedown.irs_" + this.plugin_count, this.pointerDown.bind(this, "from"));
717                this.$cache.s_from.on("mousedown.irs_" + this.plugin_count, this.pointerDown.bind(this, "from"));
718                this.$cache.to.on("mousedown.irs_" + this.plugin_count, this.pointerDown.bind(this, "to"));
719                this.$cache.s_to.on("mousedown.irs_" + this.plugin_count, this.pointerDown.bind(this, "to"));
720                this.$cache.shad_from.on("mousedown.irs_" + this.plugin_count, this.pointerClick.bind(this, "click"));
721                this.$cache.shad_to.on("mousedown.irs_" + this.plugin_count, this.pointerClick.bind(this, "click"));
722            }
723
724            if (this.options.keyboard) {
725                this.$cache.line.on("keydown.irs_" + this.plugin_count, this.key.bind(this, "keyboard"));
726            }
727
728            if (is_old_ie) {
729                this.$cache.body.on("mouseup.irs_" + this.plugin_count, this.pointerUp.bind(this));
730                this.$cache.body.on("mouseleave.irs_" + this.plugin_count, this.pointerUp.bind(this));
731            }
732        },
733
734        /**
735         * Focus with tabIndex
736         *
737         * @param e {Object} event object
738         */
739        pointerFocus: function (e) {
740            if (!this.target) {
741                var x;
742                var $handle;
743
744                if (this.options.type === "single") {
745                    $handle = this.$cache.single;
746                } else {
747                    $handle = this.$cache.from;
748                }
749
750                x = $handle.offset().left;
751                x += ($handle.width() / 2) - 1;
752
753                this.pointerClick("single", {preventDefault: function () {}, pageX: x});
754            }
755        },
756
757        /**
758         * Mousemove or touchmove
759         * only for handlers
760         *
761         * @param e {Object} event object
762         */
763        pointerMove: function (e) {
764            if (!this.dragging) {
765                return;
766            }
767
768            var x = e.pageX || e.originalEvent.touches && e.originalEvent.touches[0].pageX;
769            this.coords.x_pointer = x - this.coords.x_gap;
770
771            this.calc();
772        },
773
774        /**
775         * Mouseup or touchend
776         * only for handlers
777         *
778         * @param e {Object} event object
779         */
780        pointerUp: function (e) {
781            if (this.current_plugin !== this.plugin_count) {
782                return;
783            }
784
785            if (this.is_active) {
786                this.is_active = false;
787            } else {
788                return;
789            }
790
791            this.$cache.cont.find(".state_hover").removeClass("state_hover");
792
793            this.force_redraw = true;
794
795            if (is_old_ie) {
796                $("*").prop("unselectable", false);
797            }
798
799            this.updateScene();
800            this.restoreOriginalMinInterval();
801
802            // callbacks call
803            if ($.contains(this.$cache.cont[0], e.target) || this.dragging) {
804                this.callOnFinish();
805            }
806            
807            this.dragging = false;
808        },
809
810        /**
811         * Mousedown or touchstart
812         * only for handlers
813         *
814         * @param target {String|null}
815         * @param e {Object} event object
816         */
817        pointerDown: function (target, e) {
818            e.preventDefault();
819            var x = e.pageX || e.originalEvent.touches && e.originalEvent.touches[0].pageX;
820            if (e.button === 2) {
821                return;
822            }
823
824            if (target === "both") {
825                this.setTempMinInterval();
826            }
827
828            if (!target) {
829                target = this.target || "from";
830            }
831
832            this.current_plugin = this.plugin_count;
833            this.target = target;
834
835            this.is_active = true;
836            this.dragging = true;
837
838            this.coords.x_gap = this.$cache.rs.offset().left;
839            this.coords.x_pointer = x - this.coords.x_gap;
840
841            this.calcPointerPercent();
842            this.changeLevel(target);
843
844            if (is_old_ie) {
845                $("*").prop("unselectable", true);
846            }
847
848            this.$cache.line.trigger("focus");
849
850            this.updateScene();
851        },
852
853        /**
854         * Mousedown or touchstart
855         * for other slider elements, like diapason line
856         *
857         * @param target {String}
858         * @param e {Object} event object
859         */
860        pointerClick: function (target, e) {
861            e.preventDefault();
862            var x = e.pageX || e.originalEvent.touches && e.originalEvent.touches[0].pageX;
863            if (e.button === 2) {
864                return;
865            }
866
867            this.current_plugin = this.plugin_count;
868            this.target = target;
869
870            this.is_click = true;
871            this.coords.x_gap = this.$cache.rs.offset().left;
872            this.coords.x_pointer = +(x - this.coords.x_gap).toFixed();
873
874            this.force_redraw = true;
875            this.calc();
876
877            this.$cache.line.trigger("focus");
878        },
879
880        /**
881         * Keyborard controls for focused slider
882         *
883         * @param target {String}
884         * @param e {Object} event object
885         * @returns {boolean|undefined}
886         */
887        key: function (target, e) {
888            if (this.current_plugin !== this.plugin_count || e.altKey || e.ctrlKey || e.shiftKey || e.metaKey) {
889                return;
890            }
891
892            switch (e.which) {
893                case 83: // W
894                case 65: // A
895                case 40: // DOWN
896                case 37: // LEFT
897                    e.preventDefault();
898                    this.moveByKey(false);
899                    break;
900
901                case 87: // S
902                case 68: // D
903                case 38: // UP
904                case 39: // RIGHT
905                    e.preventDefault();
906                    this.moveByKey(true);
907                    break;
908            }
909
910            return true;
911        },
912
913        /**
914         * Move by key
915         *
916         * @param right {boolean} direction to move
917         */
918        moveByKey: function (right) {
919            var p = this.coords.p_pointer;
920            var p_step = (this.options.max - this.options.min) / 100;
921            p_step = this.options.step / p_step;
922
923            if (right) {
924                p += p_step;
925            } else {
926                p -= p_step;
927            }
928
929            this.coords.x_pointer = this.toFixed(this.coords.w_rs / 100 * p);
930            this.is_key = true;
931            this.calc();
932        },
933
934        /**
935         * Set visibility and content
936         * of Min and Max labels
937         */
938        setMinMax: function () {
939            if (!this.options) {
940                return;
941            }
942
943            if (this.options.hide_min_max) {
944                this.$cache.min[0].style.display = "none";
945                this.$cache.max[0].style.display = "none";
946                return;
947            }
948
949            if (this.options.values.length) {
950                this.$cache.min.html(this.decorate(this.options.p_values[this.options.min]));
951                this.$cache.max.html(this.decorate(this.options.p_values[this.options.max]));
952            } else {
953                var min_pretty = this._prettify(this.options.min);
954                var max_pretty = this._prettify(this.options.max);
955
956                this.result.min_pretty = min_pretty;
957                this.result.max_pretty = max_pretty;
958
959                this.$cache.min.html(this.decorate(min_pretty, this.options.min));
960                this.$cache.max.html(this.decorate(max_pretty, this.options.max));
961            }
962
963            this.labels.w_min = this.$cache.min.outerWidth(false);
964            this.labels.w_max = this.$cache.max.outerWidth(false);
965        },
966
967        /**
968         * Then dragging interval, prevent interval collapsing
969         * using min_interval option
970         */
971        setTempMinInterval: function () {
972            var interval = this.result.to - this.result.from;
973
974            if (this.old_min_interval === null) {
975                this.old_min_interval = this.options.min_interval;
976            }
977
978            this.options.min_interval = interval;
979        },
980
981        /**
982         * Restore min_interval option to original
983         */
984        restoreOriginalMinInterval: function () {
985            if (this.old_min_interval !== null) {
986                this.options.min_interval = this.old_min_interval;
987                this.old_min_interval = null;
988            }
989        },
990
991
992
993        // =============================================================================================================
994        // Calculations
995
996        /**
997         * All calculations and measures start here
998         *
999         * @param update {boolean=}
1000         */
1001        calc: function (update) {
1002            if (!this.options) {
1003                return;
1004            }
1005
1006            this.calc_count++;
1007
1008            if (this.calc_count === 10 || update) {
1009                this.calc_count = 0;
1010                this.coords.w_rs = this.$cache.rs.outerWidth(false);
1011
1012                this.calcHandlePercent();
1013            }
1014
1015            if (!this.coords.w_rs) {
1016                return;
1017            }
1018
1019            this.calcPointerPercent();
1020            var handle_x = this.getHandleX();
1021
1022
1023            if (this.target === "both") {
1024                this.coords.p_gap = 0;
1025                handle_x = this.getHandleX();
1026            }
1027
1028            if (this.target === "click") {
1029                this.coords.p_gap = this.coords.p_handle / 2;
1030                handle_x = this.getHandleX();
1031
1032                if (this.options.drag_interval) {
1033                    this.target = "both_one";
1034                } else {
1035                    this.target = this.chooseHandle(handle_x);
1036                }
1037            }
1038
1039            switch (this.target) {
1040                case "base":
1041                    var w = (this.options.max - this.options.min) / 100,
1042                        f = (this.result.from - this.options.min) / w,
1043                        t = (this.result.to - this.options.min) / w;
1044
1045                    this.coords.p_single_real = this.toFixed(f);
1046                    this.coords.p_from_real = this.toFixed(f);
1047                    this.coords.p_to_real = this.toFixed(t);
1048
1049                    this.coords.p_single_real = this.checkDiapason(this.coords.p_single_real, this.options.from_min, this.options.from_max);
1050                    this.coords.p_from_real = this.checkDiapason(this.coords.p_from_real, this.options.from_min, this.options.from_max);
1051                    this.coords.p_to_real = this.checkDiapason(this.coords.p_to_real, this.options.to_min, this.options.to_max);
1052
1053                    this.coords.p_single_fake = this.convertToFakePercent(this.coords.p_single_real);
1054                    this.coords.p_from_fake = this.convertToFakePercent(this.coords.p_from_real);
1055                    this.coords.p_to_fake = this.convertToFakePercent(this.coords.p_to_real);
1056
1057                    this.target = null;
1058
1059                    break;
1060
1061                case "single":
1062                    if (this.options.from_fixed) {
1063                        break;
1064                    }
1065
1066                    this.coords.p_single_real = this.convertToRealPercent(handle_x);
1067                    this.coords.p_single_real = this.calcWithStep(this.coords.p_single_real);
1068                    this.coords.p_single_real = this.checkDiapason(this.coords.p_single_real, this.options.from_min, this.options.from_max);
1069
1070                    this.coords.p_single_fake = this.convertToFakePercent(this.coords.p_single_real);
1071
1072                    break;
1073
1074                case "from":
1075                    if (this.options.from_fixed) {
1076                        break;
1077                    }
1078
1079                    this.coords.p_from_real = this.convertToRealPercent(handle_x);
1080                    this.coords.p_from_real = this.calcWithStep(this.coords.p_from_real);
1081                    if (this.coords.p_from_real > this.coords.p_to_real) {
1082                        this.coords.p_from_real = this.coords.p_to_real;
1083                    }
1084                    this.coords.p_from_real = this.checkDiapason(this.coords.p_from_real, this.options.from_min, this.options.from_max);
1085                    this.coords.p_from_real = this.checkMinInterval(this.coords.p_from_real, this.coords.p_to_real, "from");
1086                    this.coords.p_from_real = this.checkMaxInterval(this.coords.p_from_real, this.coords.p_to_real, "from");
1087
1088                    this.coords.p_from_fake = this.convertToFakePercent(this.coords.p_from_real);
1089
1090                    break;
1091
1092                case "to":
1093                    if (this.options.to_fixed) {
1094                        break;
1095                    }
1096
1097                    this.coords.p_to_real = this.convertToRealPercent(handle_x);
1098                    this.coords.p_to_real = this.calcWithStep(this.coords.p_to_real);
1099                    if (this.coords.p_to_real < this.coords.p_from_real) {
1100                        this.coords.p_to_real = this.coords.p_from_real;
1101                    }
1102                    this.coords.p_to_real = this.checkDiapason(this.coords.p_to_real, this.options.to_min, this.options.to_max);
1103                    this.coords.p_to_real = this.checkMinInterval(this.coords.p_to_real, this.coords.p_from_real, "to");
1104                    this.coords.p_to_real = this.checkMaxInterval(this.coords.p_to_real, this.coords.p_from_real, "to");
1105
1106                    this.coords.p_to_fake = this.convertToFakePercent(this.coords.p_to_real);
1107
1108                    break;
1109
1110                case "both":
1111                    if (this.options.from_fixed || this.options.to_fixed) {
1112                        break;
1113                    }
1114
1115                    handle_x = this.toFixed(handle_x + (this.coords.p_handle * 0.001));
1116
1117                    this.coords.p_from_real = this.convertToRealPercent(handle_x) - this.coords.p_gap_left;
1118                    this.coords.p_from_real = this.calcWithStep(this.coords.p_from_real);
1119                    this.coords.p_from_real = this.checkDiapason(this.coords.p_from_real, this.options.from_min, this.options.from_max);
1120                    this.coords.p_from_real = this.checkMinInterval(this.coords.p_from_real, this.coords.p_to_real, "from");
1121                    this.coords.p_from_fake = this.convertToFakePercent(this.coords.p_from_real);
1122
1123                    this.coords.p_to_real = this.convertToRealPercent(handle_x) + this.coords.p_gap_right;
1124                    this.coords.p_to_real = this.calcWithStep(this.coords.p_to_real);
1125                    this.coords.p_to_real = this.checkDiapason(this.coords.p_to_real, this.options.to_min, this.options.to_max);
1126                    this.coords.p_to_real = this.checkMinInterval(this.coords.p_to_real, this.coords.p_from_real, "to");
1127                    this.coords.p_to_fake = this.convertToFakePercent(this.coords.p_to_real);
1128
1129                    break;
1130
1131                case "both_one":
1132                    if (this.options.from_fixed || this.options.to_fixed) {
1133                        break;
1134                    }
1135
1136                    var real_x = this.convertToRealPercent(handle_x),
1137                        from = this.result.from_percent,
1138                        to = this.result.to_percent,
1139                        full = to - from,
1140                        half = full / 2,
1141                        new_from = real_x - half,
1142                        new_to = real_x + half;
1143
1144                    if (new_from < 0) {
1145                        new_from = 0;
1146                        new_to = new_from + full;
1147                    }
1148
1149                    if (new_to > 100) {
1150                        new_to = 100;
1151                        new_from = new_to - full;
1152                    }
1153
1154                    this.coords.p_from_real = this.calcWithStep(new_from);
1155                    this.coords.p_from_real = this.checkDiapason(this.coords.p_from_real, this.options.from_min, this.options.from_max);
1156                    this.coords.p_from_fake = this.convertToFakePercent(this.coords.p_from_real);
1157
1158                    this.coords.p_to_real = this.calcWithStep(new_to);
1159                    this.coords.p_to_real = this.checkDiapason(this.coords.p_to_real, this.options.to_min, this.options.to_max);
1160                    this.coords.p_to_fake = this.convertToFakePercent(this.coords.p_to_real);
1161
1162                    break;
1163            }
1164
1165            if (this.options.type === "single") {
1166                this.coords.p_bar_x = (this.coords.p_handle / 2);
1167                this.coords.p_bar_w = this.coords.p_single_fake;
1168
1169                this.result.from_percent = this.coords.p_single_real;
1170                this.result.from = this.convertToValue(this.coords.p_single_real);
1171                this.result.from_pretty = this._prettify(this.result.from);
1172
1173                if (this.options.values.length) {
1174                    this.result.from_value = this.options.values[this.result.from];
1175                }
1176            } else {
1177                this.coords.p_bar_x = this.toFixed(this.coords.p_from_fake + (this.coords.p_handle / 2));
1178                this.coords.p_bar_w = this.toFixed(this.coords.p_to_fake - this.coords.p_from_fake);
1179
1180                this.result.from_percent = this.coords.p_from_real;
1181                this.result.from = this.convertToValue(this.coords.p_from_real);
1182                this.result.from_pretty = this._prettify(this.result.from);
1183                this.result.to_percent = this.coords.p_to_real;
1184                this.result.to = this.convertToValue(this.coords.p_to_real);
1185                this.result.to_pretty = this._prettify(this.result.to);
1186
1187                if (this.options.values.length) {
1188                    this.result.from_value = this.options.values[this.result.from];
1189                    this.result.to_value = this.options.values[this.result.to];
1190                }
1191            }
1192
1193            this.calcMinMax();
1194            this.calcLabels();
1195        },
1196
1197
1198        /**
1199         * calculates pointer X in percent
1200         */
1201        calcPointerPercent: function () {
1202            if (!this.coords.w_rs) {
1203                this.coords.p_pointer = 0;
1204                return;
1205            }
1206
1207            if (this.coords.x_pointer < 0 || isNaN(this.coords.x_pointer)  ) {
1208                this.coords.x_pointer = 0;
1209            } else if (this.coords.x_pointer > this.coords.w_rs) {
1210                this.coords.x_pointer = this.coords.w_rs;
1211            }
1212
1213            this.coords.p_pointer = this.toFixed(this.coords.x_pointer / this.coords.w_rs * 100);
1214        },
1215
1216        convertToRealPercent: function (fake) {
1217            var full = 100 - this.coords.p_handle;
1218            return fake / full * 100;
1219        },
1220
1221        convertToFakePercent: function (real) {
1222            var full = 100 - this.coords.p_handle;
1223            return real / 100 * full;
1224        },
1225
1226        getHandleX: function () {
1227            var max = 100 - this.coords.p_handle,
1228                x = this.toFixed(this.coords.p_pointer - this.coords.p_gap);
1229
1230            if (x < 0) {
1231                x = 0;
1232            } else if (x > max) {
1233                x = max;
1234            }
1235
1236            return x;
1237        },
1238
1239        calcHandlePercent: function () {
1240            if (this.options.type === "single") {
1241                this.coords.w_handle = this.$cache.s_single.outerWidth(false);
1242            } else {
1243                this.coords.w_handle = this.$cache.s_from.outerWidth(false);
1244            }
1245
1246            this.coords.p_handle = this.toFixed(this.coords.w_handle / this.coords.w_rs * 100);
1247        },
1248
1249        /**
1250         * Find closest handle to pointer click
1251         *
1252         * @param real_x {Number}
1253         * @returns {String}
1254         */
1255        chooseHandle: function (real_x) {
1256            if (this.options.type === "single") {
1257                return "single";
1258            } else {
1259                var m_point = this.coords.p_from_real + ((this.coords.p_to_real - this.coords.p_from_real) / 2);
1260                if (real_x >= m_point) {
1261                    return this.options.to_fixed ? "from" : "to";
1262                } else {
1263                    return this.options.from_fixed ? "to" : "from";
1264                }
1265            }
1266        },
1267
1268        /**
1269         * Measure Min and Max labels width in percent
1270         */
1271        calcMinMax: function () {
1272            if (!this.coords.w_rs) {
1273                return;
1274            }
1275
1276            this.labels.p_min = this.labels.w_min / this.coords.w_rs * 100;
1277            this.labels.p_max = this.labels.w_max / this.coords.w_rs * 100;
1278        },
1279
1280        /**
1281         * Measure labels width and X in percent
1282         */
1283        calcLabels: function () {
1284            if (!this.coords.w_rs || this.options.hide_from_to) {
1285                return;
1286            }
1287
1288            if (this.options.type === "single") {
1289
1290                this.labels.w_single = this.$cache.single.outerWidth(false);
1291                this.labels.p_single_fake = this.labels.w_single / this.coords.w_rs * 100;
1292                this.labels.p_single_left = this.coords.p_single_fake + (this.coords.p_handle / 2) - (this.labels.p_single_fake / 2);
1293                this.labels.p_single_left = this.checkEdges(this.labels.p_single_left, this.labels.p_single_fake);
1294
1295            } else {
1296
1297                this.labels.w_from = this.$cache.from.outerWidth(false);
1298                this.labels.p_from_fake = this.labels.w_from / this.coords.w_rs * 100;
1299                this.labels.p_from_left = this.coords.p_from_fake + (this.coords.p_handle / 2) - (this.labels.p_from_fake / 2);
1300                this.labels.p_from_left = this.toFixed(this.labels.p_from_left);
1301                this.labels.p_from_left = this.checkEdges(this.labels.p_from_left, this.labels.p_from_fake);
1302
1303                this.labels.w_to = this.$cache.to.outerWidth(false);
1304                this.labels.p_to_fake = this.labels.w_to / this.coords.w_rs * 100;
1305                this.labels.p_to_left = this.coords.p_to_fake + (this.coords.p_handle / 2) - (this.labels.p_to_fake / 2);
1306                this.labels.p_to_left = this.toFixed(this.labels.p_to_left);
1307                this.labels.p_to_left = this.checkEdges(this.labels.p_to_left, this.labels.p_to_fake);
1308
1309                this.labels.w_single = this.$cache.single.outerWidth(false);
1310                this.labels.p_single_fake = this.labels.w_single / this.coords.w_rs * 100;
1311                this.labels.p_single_left = ((this.labels.p_from_left + this.labels.p_to_left + this.labels.p_to_fake) / 2) - (this.labels.p_single_fake / 2);
1312                this.labels.p_single_left = this.toFixed(this.labels.p_single_left);
1313                this.labels.p_single_left = this.checkEdges(this.labels.p_single_left, this.labels.p_single_fake);
1314
1315            }
1316        },
1317
1318
1319
1320        // =============================================================================================================
1321        // Drawings
1322
1323        /**
1324         * Main function called in request animation frame
1325         * to update everything
1326         */
1327        updateScene: function () {
1328            if (this.raf_id) {
1329                cancelAnimationFrame(this.raf_id);
1330                this.raf_id = null;
1331            }
1332
1333            clearTimeout(this.update_tm);
1334            this.update_tm = null;
1335
1336            if (!this.options) {
1337                return;
1338            }
1339
1340            this.drawHandles();
1341
1342            if (this.is_active) {
1343                this.raf_id = requestAnimationFrame(this.updateScene.bind(this));
1344            } else {
1345                this.update_tm = setTimeout(this.updateScene.bind(this), 300);
1346            }
1347        },
1348
1349        /**
1350         * Draw handles
1351         */
1352        drawHandles: function () {
1353            this.coords.w_rs = this.$cache.rs.outerWidth(false);
1354
1355            if (!this.coords.w_rs) {
1356                return;
1357            }
1358
1359            if (this.coords.w_rs !== this.coords.w_rs_old) {
1360                this.target = "base";
1361                this.is_resize = true;
1362            }
1363
1364            if (this.coords.w_rs !== this.coords.w_rs_old || this.force_redraw) {
1365                this.setMinMax();
1366                this.calc(true);
1367                this.drawLabels();
1368                if (this.options.grid) {
1369                    this.calcGridMargin();
1370                    this.calcGridLabels();
1371                }
1372                this.force_redraw = true;
1373                this.coords.w_rs_old = this.coords.w_rs;
1374                this.drawShadow();
1375            }
1376
1377            if (!this.coords.w_rs) {
1378                return;
1379            }
1380
1381            if (!this.dragging && !this.force_redraw && !this.is_key) {
1382                return;
1383            }
1384
1385            if (this.old_from !== this.result.from || this.old_to !== this.result.to || this.force_redraw || this.is_key) {
1386
1387                this.drawLabels();
1388
1389                this.$cache.bar[0].style.left = this.coords.p_bar_x + "%";
1390                this.$cache.bar[0].style.width = this.coords.p_bar_w + "%";
1391
1392                if (this.options.type === "single") {
1393                    this.$cache.s_single[0].style.left = this.coords.p_single_fake + "%";
1394
1395                    this.$cache.single[0].style.left = this.labels.p_single_left + "%";
1396                } else {
1397                    this.$cache.s_from[0].style.left = this.coords.p_from_fake + "%";
1398                    this.$cache.s_to[0].style.left = this.coords.p_to_fake + "%";
1399
1400                    if (this.old_from !== this.result.from || this.force_redraw) {
1401                        this.$cache.from[0].style.left = this.labels.p_from_left + "%";
1402                    }
1403                    if (this.old_to !== this.result.to || this.force_redraw) {
1404                        this.$cache.to[0].style.left = this.labels.p_to_left + "%";
1405                    }
1406
1407                    this.$cache.single[0].style.left = this.labels.p_single_left + "%";
1408                }
1409
1410                this.writeToInput();
1411
1412                if ((this.old_from !== this.result.from || this.old_to !== this.result.to) && !this.is_start) {
1413                    this.$cache.input.trigger("change");
1414                    this.$cache.input.trigger("input");
1415                }
1416
1417                this.old_from = this.result.from;
1418                this.old_to = this.result.to;
1419
1420                // callbacks call
1421                if (!this.is_resize && !this.is_update && !this.is_start && !this.is_finish) {
1422                    this.callOnChange();
1423                }
1424                if (this.is_key || this.is_click) {
1425                    this.is_key = false;
1426                    this.is_click = false;
1427                    this.callOnFinish();
1428                }
1429
1430                this.is_update = false;
1431                this.is_resize = false;
1432                this.is_finish = false;
1433            }
1434
1435            this.is_start = false;
1436            this.is_key = false;
1437            this.is_click = false;
1438            this.force_redraw = false;
1439        },
1440
1441        /**
1442         * Draw labels
1443         * measure labels collisions
1444         * collapse close labels
1445         */
1446        drawLabels: function () {
1447            if (!this.options) {
1448                return;
1449            }
1450
1451            var values_num = this.options.values.length;
1452            var p_values = this.options.p_values;
1453            var text_single;
1454            var text_from;
1455            var text_to;
1456            var from_pretty;
1457            var to_pretty;
1458
1459            if (this.options.hide_from_to) {
1460                return;
1461            }
1462
1463            if (this.options.type === "single") {
1464
1465                if (values_num) {
1466                    text_single = this.decorate(p_values[this.result.from]);
1467                    this.$cache.single.html(text_single);
1468                } else {
1469                    from_pretty = this._prettify(this.result.from);
1470
1471                    text_single = this.decorate(from_pretty, this.result.from);
1472                    this.$cache.single.html(text_single);
1473                }
1474
1475                this.calcLabels();
1476
1477                if (this.labels.p_single_left < this.labels.p_min + 1) {
1478                    this.$cache.min[0].style.visibility = "hidden";
1479                } else {
1480                    this.$cache.min[0].style.visibility = "visible";
1481                }
1482
1483                if (this.labels.p_single_left + this.labels.p_single_fake > 100 - this.labels.p_max - 1) {
1484                    this.$cache.max[0].style.visibility = "hidden";
1485                } else {
1486                    this.$cache.max[0].style.visibility = "visible";
1487                }
1488
1489            } else {
1490
1491                if (values_num) {
1492
1493                    if (this.options.decorate_both) {
1494                        text_single = this.decorate(p_values[this.result.from]);
1495                        text_single += this.options.values_separator;
1496                        text_single += this.decorate(p_values[this.result.to]);
1497                    } else {
1498                        text_single = this.decorate(p_values[this.result.from] + this.options.values_separator + p_values[this.result.to]);
1499                    }
1500                    text_from = this.decorate(p_values[this.result.from]);
1501                    text_to = this.decorate(p_values[this.result.to]);
1502
1503                    this.$cache.single.html(text_single);
1504                    this.$cache.from.html(text_from);
1505                    this.$cache.to.html(text_to);
1506
1507                } else {
1508                    from_pretty = this._prettify(this.result.from);
1509                    to_pretty = this._prettify(this.result.to);
1510
1511                    if (this.options.decorate_both) {
1512                        text_single = this.decorate(from_pretty, this.result.from);
1513                        text_single += this.options.values_separator;
1514                        text_single += this.decorate(to_pretty, this.result.to);
1515                    } else {
1516                        text_single = this.decorate(from_pretty + this.options.values_separator + to_pretty, this.result.to);
1517                    }
1518                    text_from = this.decorate(from_pretty, this.result.from);
1519                    text_to = this.decorate(to_pretty, this.result.to);
1520
1521                    this.$cache.single.html(text_single);
1522                    this.$cache.from.html(text_from);
1523                    this.$cache.to.html(text_to);
1524
1525                }
1526
1527                this.calcLabels();
1528
1529                var min = Math.min(this.labels.p_single_left, this.labels.p_from_left),
1530                    single_left = this.labels.p_single_left + this.labels.p_single_fake,
1531                    to_left = this.labels.p_to_left + this.labels.p_to_fake,
1532                    max = Math.max(single_left, to_left);
1533
1534                if (this.labels.p_from_left + this.labels.p_from_fake >= this.labels.p_to_left) {
1535                    this.$cache.from[0].style.visibility = "hidden";
1536                    this.$cache.to[0].style.visibility = "hidden";
1537                    this.$cache.single[0].style.visibility = "visible";
1538
1539                    if (this.result.from === this.result.to) {
1540                        if (this.target === "from") {
1541                            this.$cache.from[0].style.visibility = "visible";
1542                        } else if (this.target === "to") {
1543                            this.$cache.to[0].style.visibility = "visible";
1544                        } else if (!this.target) {
1545                            this.$cache.from[0].style.visibility = "visible";
1546                        }
1547                        this.$cache.single[0].style.visibility = "hidden";
1548                        max = to_left;
1549                    } else {
1550                        this.$cache.from[0].style.visibility = "hidden";
1551                        this.$cache.to[0].style.visibility = "hidden";
1552                        this.$cache.single[0].style.visibility = "visible";
1553                        max = Math.max(single_left, to_left);
1554                    }
1555                } else {
1556                    this.$cache.from[0].style.visibility = "visible";
1557                    this.$cache.to[0].style.visibility = "visible";
1558                    this.$cache.single[0].style.visibility = "hidden";
1559                }
1560
1561                if (min < this.labels.p_min + 1) {
1562                    this.$cache.min[0].style.visibility = "hidden";
1563                } else {
1564                    this.$cache.min[0].style.visibility = "visible";
1565                }
1566
1567                if (max > 100 - this.labels.p_max - 1) {
1568                    this.$cache.max[0].style.visibility = "hidden";
1569                } else {
1570                    this.$cache.max[0].style.visibility = "visible";
1571                }
1572
1573            }
1574        },
1575
1576        /**
1577         * Draw shadow intervals
1578         */
1579        drawShadow: function () {
1580            var o = this.options,
1581                c = this.$cache,
1582
1583                is_from_min = typeof o.from_min === "number" && !isNaN(o.from_min),
1584                is_from_max = typeof o.from_max === "number" && !isNaN(o.from_max),
1585                is_to_min = typeof o.to_min === "number" && !isNaN(o.to_min),
1586                is_to_max = typeof o.to_max === "number" && !isNaN(o.to_max),
1587
1588                from_min,
1589                from_max,
1590                to_min,
1591                to_max;
1592
1593            if (o.type === "single") {
1594                if (o.from_shadow && (is_from_min || is_from_max)) {
1595                    from_min = this.convertToPercent(is_from_min ? o.from_min : o.min);
1596                    from_max = this.convertToPercent(is_from_max ? o.from_max : o.max) - from_min;
1597                    from_min = this.toFixed(from_min - (this.coords.p_handle / 100 * from_min));
1598                    from_max = this.toFixed(from_max - (this.coords.p_handle / 100 * from_max));
1599                    from_min = from_min + (this.coords.p_handle / 2);
1600
1601                    c.shad_single[0].style.display = "block";
1602                    c.shad_single[0].style.left = from_min + "%";
1603                    c.shad_single[0].style.width = from_max + "%";
1604                } else {
1605                    c.shad_single[0].style.display = "none";
1606                }
1607            } else {
1608                if (o.from_shadow && (is_from_min || is_from_max)) {
1609                    from_min = this.convertToPercent(is_from_min ? o.from_min : o.min);
1610                    from_max = this.convertToPercent(is_from_max ? o.from_max : o.max) - from_min;
1611                    from_min = this.toFixed(from_min - (this.coords.p_handle / 100 * from_min));
1612                    from_max = this.toFixed(from_max - (this.coords.p_handle / 100 * from_max));
1613                    from_min = from_min + (this.coords.p_handle / 2);
1614
1615                    c.shad_from[0].style.display = "block";
1616                    c.shad_from[0].style.left = from_min + "%";
1617                    c.shad_from[0].style.width = from_max + "%";
1618                } else {
1619                    c.shad_from[0].style.display = "none";
1620                }
1621
1622                if (o.to_shadow && (is_to_min || is_to_max)) {
1623                    to_min = this.convertToPercent(is_to_min ? o.to_min : o.min);
1624                    to_max = this.convertToPercent(is_to_max ? o.to_max : o.max) - to_min;
1625                    to_min = this.toFixed(to_min - (this.coords.p_handle / 100 * to_min));
1626                    to_max = this.toFixed(to_max - (this.coords.p_handle / 100 * to_max));
1627                    to_min = to_min + (this.coords.p_handle / 2);
1628
1629                    c.shad_to[0].style.display = "block";
1630                    c.shad_to[0].style.left = to_min + "%";
1631                    c.shad_to[0].style.width = to_max + "%";
1632                } else {
1633                    c.shad_to[0].style.display = "none";
1634                }
1635            }
1636        },
1637
1638
1639
1640        /**
1641         * Write values to input element
1642         */
1643        writeToInput: function () {
1644            if (this.options.type === "single") {
1645                if (this.options.values.length) {
1646                    this.$cache.input.prop("value", this.result.from_value);
1647                } else {
1648                    this.$cache.input.prop("value", this.result.from);
1649                }
1650                this.$cache.input.data("from", this.result.from);
1651            } else {
1652                if (this.options.values.length) {
1653                    this.$cache.input.prop("value", this.result.from_value + this.options.input_values_separator + this.result.to_value);
1654                } else {
1655                    this.$cache.input.prop("value", this.result.from + this.options.input_values_separator + this.result.to);
1656                }
1657                this.$cache.input.data("from", this.result.from);
1658                this.$cache.input.data("to", this.result.to);
1659            }
1660        },
1661
1662
1663
1664        // =============================================================================================================
1665        // Callbacks
1666
1667        callOnStart: function () {
1668            this.writeToInput();
1669
1670            if (this.options.onStart && typeof this.options.onStart === "function") {
1671                if (this.options.scope) {
1672                    this.options.onStart.call(this.options.scope, this.result);
1673                } else {
1674                    this.options.onStart(this.result);
1675                }
1676            }
1677        },
1678        callOnChange: function () {
1679            this.writeToInput();
1680
1681            if (this.options.onChange && typeof this.options.onChange === "function") {
1682                if (this.options.scope) {
1683                    this.options.onChange.call(this.options.scope, this.result);
1684                } else {
1685                    this.options.onChange(this.result);
1686                }
1687            }
1688        },
1689        callOnFinish: function () {
1690            this.writeToInput();
1691
1692            if (this.options.onFinish && typeof this.options.onFinish === "function") {
1693                if (this.options.scope) {
1694                    this.options.onFinish.call(this.options.scope, this.result);
1695                } else {
1696                    this.options.onFinish(this.result);
1697                }
1698            }
1699        },
1700        callOnUpdate: function () {
1701            this.writeToInput();
1702
1703            if (this.options.onUpdate && typeof this.options.onUpdate === "function") {
1704                if (this.options.scope) {
1705                    this.options.onUpdate.call(this.options.scope, this.result);
1706                } else {
1707                    this.options.onUpdate(this.result);
1708                }
1709            }
1710        },
1711
1712
1713
1714
1715        // =============================================================================================================
1716        // Service methods
1717
1718        toggleInput: function () {
1719            this.$cache.input.toggleClass("irs-hidden-input");
1720
1721            if (this.has_tab_index) {
1722                this.$cache.input.prop("tabindex", -1);
1723            } else {
1724                this.$cache.input.removeProp("tabindex");
1725            }
1726
1727            this.has_tab_index = !this.has_tab_index;
1728        },
1729
1730        /**
1731         * Convert real value to percent
1732         *
1733         * @param value {Number} X in real
1734         * @param no_min {boolean=} don't use min value
1735         * @returns {Number} X in percent
1736         */
1737        convertToPercent: function (value, no_min) {
1738            var diapason = this.options.max - this.options.min,
1739                one_percent = diapason / 100,
1740                val, percent;
1741
1742            if (!diapason) {
1743                this.no_diapason = true;
1744                return 0;
1745            }
1746
1747            if (no_min) {
1748                val = value;
1749            } else {
1750                val = value - this.options.min;
1751            }
1752
1753            percent = val / one_percent;
1754
1755            return this.toFixed(percent);
1756        },
1757
1758        /**
1759         * Convert percent to real values
1760         *
1761         * @param percent {Number} X in percent
1762         * @returns {Number} X in real
1763         */
1764        convertToValue: function (percent) {
1765            var min = this.options.min,
1766                max = this.options.max,
1767                min_decimals = min.toString().split(".")[1],
1768                max_decimals = max.toString().split(".")[1],
1769                min_length, max_length,
1770                avg_decimals = 0,
1771                abs = 0;
1772
1773            if (percent === 0) {
1774                return this.options.min;
1775            }
1776            if (percent === 100) {
1777                return this.options.max;
1778            }
1779
1780
1781            if (min_decimals) {
1782                min_length = min_decimals.length;
1783                avg_decimals = min_length;
1784            }
1785            if (max_decimals) {
1786                max_length = max_decimals.length;
1787                avg_decimals = max_length;
1788            }
1789            if (min_length && max_length) {
1790                avg_decimals = (min_length >= max_length) ? min_length : max_length;
1791            }
1792
1793            if (min < 0) {
1794                abs = Math.abs(min);
1795                min = +(min + abs).toFixed(avg_decimals);
1796                max = +(max + abs).toFixed(avg_decimals);
1797            }
1798
1799            var number = ((max - min) / 100 * percent) + min,
1800                string = this.options.step.toString().split(".")[1],
1801                result;
1802
1803            if (string) {
1804                number = +number.toFixed(string.length);
1805            } else {
1806                number = number / this.options.step;
1807                number = number * this.options.step;
1808
1809                number = +number.toFixed(0);
1810            }
1811
1812            if (abs) {
1813                number -= abs;
1814            }
1815
1816            if (string) {
1817                result = +number.toFixed(string.length);
1818            } else {
1819                result = this.toFixed(number);
1820            }
1821
1822            if (result < this.options.min) {
1823                result = this.options.min;
1824            } else if (result > this.options.max) {
1825                result = this.options.max;
1826            }
1827
1828            return result;
1829        },
1830
1831        /**
1832         * Round percent value with step
1833         *
1834         * @param percent {Number}
1835         * @returns percent {Number} rounded
1836         */
1837        calcWithStep: function (percent) {
1838            var rounded = Math.round(percent / this.coords.p_step) * this.coords.p_step;
1839
1840            if (rounded > 100) {
1841                rounded = 100;
1842            }
1843            if (percent === 100) {
1844                rounded = 100;
1845            }
1846
1847            return this.toFixed(rounded);
1848        },
1849
1850        checkMinInterval: function (p_current, p_next, type) {
1851            var o = this.options,
1852                current,
1853                next;
1854
1855            if (!o.min_interval) {
1856                return p_current;
1857            }
1858
1859            current = this.convertToValue(p_current);
1860            next = this.convertToValue(p_next);
1861
1862            if (type === "from") {
1863
1864                if (next - current < o.min_interval) {
1865                    current = next - o.min_interval;
1866                }
1867
1868            } else {
1869
1870                if (current - next < o.min_interval) {
1871                    current = next + o.min_interval;
1872                }
1873
1874            }
1875
1876            return this.convertToPercent(current);
1877        },
1878
1879        checkMaxInterval: function (p_current, p_next, type) {
1880            var o = this.options,
1881                current,
1882                next;
1883
1884            if (!o.max_interval) {
1885                return p_current;
1886            }
1887
1888            current = this.convertToValue(p_current);
1889            next = this.convertToValue(p_next);
1890
1891            if (type === "from") {
1892
1893                if (next - current > o.max_interval) {
1894                    current = next - o.max_interval;
1895                }
1896
1897            } else {
1898
1899                if (current - next > o.max_interval) {
1900                    current = next + o.max_interval;
1901                }
1902
1903            }
1904
1905            return this.convertToPercent(current);
1906        },
1907
1908        checkDiapason: function (p_num, min, max) {
1909            var num = this.convertToValue(p_num),
1910                o = this.options;
1911
1912            if (typeof min !== "number") {
1913                min = o.min;
1914            }
1915
1916            if (typeof max !== "number") {
1917                max = o.max;
1918            }
1919
1920            if (num < min) {
1921                num = min;
1922            }
1923
1924            if (num > max) {
1925                num = max;
1926            }
1927
1928            return this.convertToPercent(num);
1929        },
1930
1931        toFixed: function (num) {
1932            num = num.toFixed(20);
1933            return +num;
1934        },
1935
1936        _prettify: function (num) {
1937            if (!this.options.prettify_enabled) {
1938                return num;
1939            }
1940
1941            if (this.options.prettify && typeof this.options.prettify === "function") {
1942                return this.options.prettify(num);
1943            } else {
1944                return this.prettify(num);
1945            }
1946        },
1947
1948        prettify: function (num) {
1949            var n = num.toString();
1950            return n.replace(/(\d{1,3}(?=(?:\d\d\d)+(?!\d)))/g, "$1" + this.options.prettify_separator);
1951        },
1952
1953        checkEdges: function (left, width) {
1954            if (!this.options.force_edges) {
1955                return this.toFixed(left);
1956            }
1957
1958            if (left < 0) {
1959                left = 0;
1960            } else if (left > 100 - width) {
1961                left = 100 - width;
1962            }
1963
1964            return this.toFixed(left);
1965        },
1966
1967        validate: function () {
1968            var o = this.options,
1969                r = this.result,
1970                v = o.values,
1971                vl = v.length,
1972                value,
1973                i;
1974
1975            if (typeof o.min === "string") o.min = +o.min;
1976            if (typeof o.max === "string") o.max = +o.max;
1977            if (typeof o.from === "string") o.from = +o.from;
1978            if (typeof o.to === "string") o.to = +o.to;
1979            if (typeof o.step === "string") o.step = +o.step;
1980
1981            if (typeof o.from_min === "string") o.from_min = +o.from_min;
1982            if (typeof o.from_max === "string") o.from_max = +o.from_max;
1983            if (typeof o.to_min === "string") o.to_min = +o.to_min;
1984            if (typeof o.to_max === "string") o.to_max = +o.to_max;
1985
1986            if (typeof o.grid_num === "string") o.grid_num = +o.grid_num;
1987
1988            if (o.max < o.min) {
1989                o.max = o.min;
1990            }
1991
1992            if (vl) {
1993                o.p_values = [];
1994                o.min = 0;
1995                o.max = vl - 1;
1996                o.step = 1;
1997                o.grid_num = o.max;
1998                o.grid_snap = true;
1999
2000                for (i = 0; i < vl; i++) {
2001                    value = +v[i];
2002
2003                    if (!isNaN(value)) {
2004                        v[i] = value;
2005                        value = this._prettify(value);
2006                    } else {
2007                        value = v[i];
2008                    }
2009
2010                    o.p_values.push(value);
2011                }
2012            }
2013
2014            if (typeof o.from !== "number" || isNaN(o.from)) {
2015                o.from = o.min;
2016            }
2017
2018            if (typeof o.to !== "number" || isNaN(o.to)) {
2019                o.to = o.max;
2020            }
2021
2022            if (o.type === "single") {
2023
2024                if (o.from < o.min) o.from = o.min;
2025                if (o.from > o.max) o.from = o.max;
2026
2027            } else {
2028
2029                if (o.from < o.min) o.from = o.min;
2030                if (o.from > o.max) o.from = o.max;
2031
2032                if (o.to < o.min) o.to = o.min;
2033                if (o.to > o.max) o.to = o.max;
2034
2035                if (this.update_check.from) {
2036
2037                    if (this.update_check.from !== o.from) {
2038                        if (o.from > o.to) o.from = o.to;
2039                    }
2040                    if (this.update_check.to !== o.to) {
2041                        if (o.to < o.from) o.to = o.from;
2042                    }
2043
2044                }
2045
2046                if (o.from > o.to) o.from = o.to;
2047                if (o.to < o.from) o.to = o.from;
2048
2049            }
2050
2051            if (typeof o.step !== "number" || isNaN(o.step) || !o.step || o.step < 0) {
2052                o.step = 1;
2053            }
2054
2055            if (typeof o.from_min === "number" && o.from < o.from_min) {
2056                o.from = o.from_min;
2057            }
2058
2059            if (typeof o.from_max === "number" && o.from > o.from_max) {
2060                o.from = o.from_max;
2061            }
2062
2063            if (typeof o.to_min === "number" && o.to < o.to_min) {
2064                o.to = o.to_min;
2065            }
2066
2067            if (typeof o.to_max === "number" && o.from > o.to_max) {
2068                o.to = o.to_max;
2069            }
2070
2071            if (r) {
2072                if (r.min !== o.min) {
2073                    r.min = o.min;
2074                }
2075
2076                if (r.max !== o.max) {
2077                    r.max = o.max;
2078                }
2079
2080                if (r.from < r.min || r.from > r.max) {
2081                    r.from = o.from;
2082                }
2083
2084                if (r.to < r.min || r.to > r.max) {
2085                    r.to = o.to;
2086                }
2087            }
2088
2089            if (typeof o.min_interval !== "number" || isNaN(o.min_interval) || !o.min_interval || o.min_interval < 0) {
2090                o.min_interval = 0;
2091            }
2092
2093            if (typeof o.max_interval !== "number" || isNaN(o.max_interval) || !o.max_interval || o.max_interval < 0) {
2094                o.max_interval = 0;
2095            }
2096
2097            if (o.min_interval && o.min_interval > o.max - o.min) {
2098                o.min_interval = o.max - o.min;
2099            }
2100
2101            if (o.max_interval && o.max_interval > o.max - o.min) {
2102                o.max_interval = o.max - o.min;
2103            }
2104        },
2105
2106        decorate: function (num, original) {
2107            var decorated = "",
2108                o = this.options;
2109
2110            if (o.prefix) {
2111                decorated += o.prefix;
2112            }
2113
2114            decorated += num;
2115
2116            if (o.max_postfix) {
2117                if (o.values.length && num === o.p_values[o.max]) {
2118                    decorated += o.max_postfix;
2119                    if (o.postfix) {
2120                        decorated += " ";
2121                    }
2122                } else if (original === o.max) {
2123                    decorated += o.max_postfix;
2124                    if (o.postfix) {
2125                        decorated += " ";
2126                    }
2127                }
2128            }
2129
2130            if (o.postfix) {
2131                decorated += o.postfix;
2132            }
2133
2134            return decorated;
2135        },
2136
2137        updateFrom: function () {
2138            this.result.from = this.options.from;
2139            this.result.from_percent = this.convertToPercent(this.result.from);
2140            this.result.from_pretty = this._prettify(this.result.from);
2141            if (this.options.values) {
2142                this.result.from_value = this.options.values[this.result.from];
2143            }
2144        },
2145
2146        updateTo: function () {
2147            this.result.to = this.options.to;
2148            this.result.to_percent = this.convertToPercent(this.result.to);
2149            this.result.to_pretty = this._prettify(this.result.to);
2150            if (this.options.values) {
2151                this.result.to_value = this.options.values[this.result.to];
2152            }
2153        },
2154
2155        updateResult: function () {
2156            this.result.min = this.options.min;
2157            this.result.max = this.options.max;
2158            this.updateFrom();
2159            this.updateTo();
2160        },
2161
2162
2163        // =============================================================================================================
2164        // Grid
2165
2166        appendGrid: function () {
2167            if (!this.options.grid) {
2168                return;
2169            }
2170
2171            var o = this.options,
2172                i, z,
2173
2174                total = o.max - o.min,
2175                big_num = o.grid_num,
2176                big_p = 0,
2177                big_w = 0,
2178
2179                small_max = 4,
2180                local_small_max,
2181                small_p,
2182                small_w = 0,
2183
2184                result,
2185                html = '';
2186
2187
2188
2189            this.calcGridMargin();
2190
2191            if (o.grid_snap) {
2192
2193                if (total > 50) {
2194                    big_num = 50 / o.step;
2195                    big_p = this.toFixed(o.step / 0.5);
2196                } else {
2197                    big_num = total / o.step;
2198                    big_p = this.toFixed(o.step / (total / 100));
2199                }
2200
2201            } else {
2202                big_p = this.toFixed(100 / big_num);
2203            }
2204
2205            if (big_num > 4) {
2206                small_max = 3;
2207            }
2208            if (big_num > 7) {
2209                small_max = 2;
2210            }
2211            if (big_num > 14) {
2212                small_max = 1;
2213            }
2214            if (big_num > 28) {
2215                small_max = 0;
2216            }
2217
2218            for (i = 0; i < big_num + 1; i++) {
2219                local_small_max = small_max;
2220
2221                big_w = this.toFixed(big_p * i);
2222
2223                if (big_w > 100) {
2224                    big_w = 100;
2225                }
2226                this.coords.big[i] = big_w;
2227
2228                small_p = (big_w - (big_p * (i - 1))) / (local_small_max + 1);
2229
2230                for (z = 1; z <= local_small_max; z++) {
2231                    if (big_w === 0) {
2232                        break;
2233                    }
2234
2235                    small_w = this.toFixed(big_w - (small_p * z));
2236
2237                    html += '<span class="irs-grid-pol small" style="left: ' + small_w + '%"></span>';
2238                }
2239
2240                html += '<span class="irs-grid-pol" style="left: ' + big_w + '%"></span>';
2241
2242                result = this.convertToValue(big_w);
2243                if (o.values.length) {
2244                    result = o.p_values[result];
2245                } else {
2246                    result = this._prettify(result);
2247                }
2248
2249                html += '<span class="irs-grid-text js-grid-text-' + i + '" style="left: ' + big_w + '%">' + result + '</span>';
2250            }
2251            this.coords.big_num = Math.ceil(big_num + 1);
2252
2253
2254
2255            this.$cache.cont.addClass("irs-with-grid");
2256            this.$cache.grid.html(html);
2257            this.cacheGridLabels();
2258        },
2259
2260        cacheGridLabels: function () {
2261            var $label, i,
2262                num = this.coords.big_num;
2263
2264            for (i = 0; i < num; i++) {
2265                $label = this.$cache.grid.find(".js-grid-text-" + i);
2266                this.$cache.grid_labels.push($label);
2267            }
2268
2269            this.calcGridLabels();
2270        },
2271
2272        calcGridLabels: function () {
2273            var i, label, start = [], finish = [],
2274                num = this.coords.big_num;
2275
2276            for (i = 0; i < num; i++) {
2277                this.coords.big_w[i] = this.$cache.grid_labels[i].outerWidth(false);
2278                this.coords.big_p[i] = this.toFixed(this.coords.big_w[i] / this.coords.w_rs * 100);
2279                this.coords.big_x[i] = this.toFixed(this.coords.big_p[i] / 2);
2280
2281                start[i] = this.toFixed(this.coords.big[i] - this.coords.big_x[i]);
2282                finish[i] = this.toFixed(start[i] + this.coords.big_p[i]);
2283            }
2284
2285            if (this.options.force_edges) {
2286                if (start[0] < -this.coords.grid_gap) {
2287                    start[0] = -this.coords.grid_gap;
2288                    finish[0] = this.toFixed(start[0] + this.coords.big_p[0]);
2289
2290                    this.coords.big_x[0] = this.coords.grid_gap;
2291                }
2292
2293                if (finish[num - 1] > 100 + this.coords.grid_gap) {
2294                    finish[num - 1] = 100 + this.coords.grid_gap;
2295                    start[num - 1] = this.toFixed(finish[num - 1] - this.coords.big_p[num - 1]);
2296
2297                    this.coords.big_x[num - 1] = this.toFixed(this.coords.big_p[num - 1] - this.coords.grid_gap);
2298                }
2299            }
2300
2301            this.calcGridCollision(2, start, finish);
2302            this.calcGridCollision(4, start, finish);
2303
2304            for (i = 0; i < num; i++) {
2305                label = this.$cache.grid_labels[i][0];
2306
2307                if (this.coords.big_x[i] !== Number.POSITIVE_INFINITY) {
2308                    label.style.marginLeft = -this.coords.big_x[i] + "%";
2309                }
2310            }
2311        },
2312
2313        // Collisions Calc Beta
2314        // TODO: Refactor then have plenty of time
2315        calcGridCollision: function (step, start, finish) {
2316            var i, next_i, label,
2317                num = this.coords.big_num;
2318
2319            for (i = 0; i < num; i += step) {
2320                next_i = i + (step / 2);
2321                if (next_i >= num) {
2322                    break;
2323                }
2324
2325                label = this.$cache.grid_labels[next_i][0];
2326
2327                if (finish[i] <= start[next_i]) {
2328                    label.style.visibility = "visible";
2329                } else {
2330                    label.style.visibility = "hidden";
2331                }
2332            }
2333        },
2334
2335        calcGridMargin: function () {
2336            if (!this.options.grid_margin) {
2337                return;
2338            }
2339
2340            this.coords.w_rs = this.$cache.rs.outerWidth(false);
2341            if (!this.coords.w_rs) {
2342                return;
2343            }
2344
2345            if (this.options.type === "single") {
2346                this.coords.w_handle = this.$cache.s_single.outerWidth(false);
2347            } else {
2348                this.coords.w_handle = this.$cache.s_from.outerWidth(false);
2349            }
2350            this.coords.p_handle = this.toFixed(this.coords.w_handle  / this.coords.w_rs * 100);
2351            this.coords.grid_gap = this.toFixed((this.coords.p_handle / 2) - 0.1);
2352
2353            this.$cache.grid[0].style.width = this.toFixed(100 - this.coords.p_handle) + "%";
2354            this.$cache.grid[0].style.left = this.coords.grid_gap + "%";
2355        },
2356
2357
2358
2359        // =============================================================================================================
2360        // Public methods
2361
2362        update: function (options) {
2363            if (!this.input) {
2364                return;
2365            }
2366
2367            this.is_update = true;
2368
2369            this.options.from = this.result.from;
2370            this.options.to = this.result.to;
2371            this.update_check.from = this.result.from;
2372            this.update_check.to = this.result.to;
2373
2374            this.options = $.extend(this.options, options);
2375            this.validate();
2376            this.updateResult(options);
2377
2378            this.toggleInput();
2379            this.remove();
2380            this.init(true);
2381        },
2382
2383        reset: function () {
2384            if (!this.input) {
2385                return;
2386            }
2387
2388            this.updateResult();
2389            this.update();
2390        },
2391
2392        destroy: function () {
2393            if (!this.input) {
2394                return;
2395            }
2396
2397            this.toggleInput();
2398            this.$cache.input.prop("readonly", false);
2399            $.data(this.input, "ionRangeSlider", null);
2400
2401            this.remove();
2402            this.input = null;
2403            this.options = null;
2404        }
2405    };
2406
2407    $.fn.ionRangeSlider = function (options) {
2408        return this.each(function() {
2409            if (!$.data(this, "ionRangeSlider")) {
2410                $.data(this, "ionRangeSlider", new IonRangeSlider(this, options, plugin_count++));
2411            }
2412        });
2413    };
2414
2415
2416
2417    // =================================================================================================================
2418    // http://paulirish.com/2011/requestanimationframe-for-smart-animating/
2419    // http://my.opera.com/emoller/blog/2011/12/20/requestanimationframe-for-smart-er-animating
2420
2421    // requestAnimationFrame polyfill by Erik Möller. fixes from Paul Irish and Tino Zijdel
2422
2423    // MIT license
2424
2425    (function() {
2426        var lastTime = 0;
2427        var vendors = ['ms', 'moz', 'webkit', 'o'];
2428        for(var x = 0; x < vendors.length && !window.requestAnimationFrame; ++x) {
2429            window.requestAnimationFrame = window[vendors[x]+'RequestAnimationFrame'];
2430            window.cancelAnimationFrame = window[vendors[x]+'CancelAnimationFrame']
2431                || window[vendors[x]+'CancelRequestAnimationFrame'];
2432        }
2433
2434        if (!window.requestAnimationFrame)
2435            window.requestAnimationFrame = function(callback, element) {
2436                var currTime = new Date().getTime();
2437                var timeToCall = Math.max(0, 16 - (currTime - lastTime));
2438                var id = window.setTimeout(function() { callback(currTime + timeToCall); },
2439                    timeToCall);
2440                lastTime = currTime + timeToCall;
2441                return id;
2442            };
2443
2444        if (!window.cancelAnimationFrame)
2445            window.cancelAnimationFrame = function(id) {
2446                clearTimeout(id);
2447            };
2448    }());
2449
2450}));

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.