PageSourceSearch

https://lazi.vn/system/cms/themes/mytheme/js/jquery.scrollbar.js

js lazi.vn collected 2026-09-25 09:21:23 UTC 26,292 bytes, 673 lines download raw bytes

1/**
2 * jQuery CSS Customizable Scrollbar
3 *
4 * Copyright 2014, Yuriy Khabarov
5 * Dual licensed under the MIT or GPL Version 2 licenses.
6 *
7 * If you found bug, please contact me via email <[email protected]>
8 *
9 * @author Yuriy Khabarov aka Gromo
10 * @version 0.2.1
11 * @url https://github.com/gromo/jquery.scrollbar/
12 *
13 * TODO:
14 *
15 */
16;
17(function($, doc, win){
18
19    // Init flags & variables
20    var browser = {
21        "mobile": /Android|webOS|iPhone|iPad|iPod|BlackBerry/i.test(win.navigator.userAgent),
22        "webkit": win.WebKitPoint ? true : false,
23
24        "log": function(data, toString){
25            var output = data;
26            if(toString && typeof data != "string"){
27                output = [];
28                $.each(data, function(i, v){
29                    output.push('"' + i + '": ' + v);
30                });
31                output = output.join(", ");
32            }
33            if(win.console && win.console.log){
34                win.console.log(output);
35            }
36        },
37        "scroll": null
38    };
39    var debug = false;
40    var lmb = 1, px = "px";
41    var scrolls = [];
42
43
44    var defaults = {
45        "autoScrollSize": true,     // automatically calculate scrollsize
46        "autoUpdate": true,         // update scrollbar if content/container size changed
47        "disableBodyScroll": false, // disable body scroll if mouse over container
48        "duration": 200,            // scroll animate duration in ms
49        "ignoreMobile": true,       // ignore mobile devices
50        "scrollStep": 30,           // scroll step for scrollbar arrows
51        "showArrows": false,        // add class to show arrows
52        "stepScrolling": true,      // when scrolling to scrollbar mousedown position
53        "type":"simple",            // [advanced|simple] scrollbar html type
54
55        "scrollx": null,            // horizontal scroll element
56        "scrolly": null,            // vertical scroll element
57
58        "onDestroy": null,          // callback function on destroy,
59        "onInit": null,             // callback function on first initialization
60        "onUpdate": null            // callback function on init/resize (before scrollbar size calculation)
61    };
62
63
64    var customScrollbar = function(container, options){
65
66        if(!browser.scroll){
67            debug && browser.log("Init jQuery Scrollbar v0.2.1");
68            browser.scroll = getBrowserScrollSize();
69        }
70
71        this.container = container;
72        this.options = $.extend({}, defaults);
73        this.scrollTo = null;
74        this.scrollx = {};
75        this.scrolly = {};
76
77        this.init(options);
78    };
79
80    customScrollbar.prototype = {
81
82        destroy: function(){
83
84            if(!this.wrapper){
85                return;
86            }
87
88            // init variables
89            var scrollLeft = this.container.scrollLeft();
90            var scrollTop  = this.container.scrollTop();
91
92            this.container.insertBefore(this.wrapper).css({
93                "height":"",
94                "margin":""
95            })
96            .removeClass("scroll-content")
97            .removeClass("scroll-scrollx_visible")
98            .removeClass("scroll-scrolly_visible")
99            .off(".scrollbar")
100            .scrollLeft(scrollLeft)
101            .scrollTop(scrollTop);
102
103            this.scrollx.scrollbar.removeClass("scroll-scrollx_visible").find("div").andSelf().off(".scrollbar");
104            this.scrolly.scrollbar.removeClass("scroll-scrolly_visible").find("div").andSelf().off(".scrollbar");
105
106            this.wrapper.remove();
107
108            $(doc).add("body").off(".scrollbar");
109
110            if($.isFunction(this.options.onDestroy))
111                this.options.onDestroy.apply(this, [this.container]);
112        },
113
114
115
116        getScrollbar: function(d){
117
118            var scrollbar = this.options["scroll" + d];
119            var html = {
120                "advanced":
121                '<div class="scroll-arrow scroll-arrow_less"></div>' +
122                '<div class="scroll-arrow scroll-arrow_more"></div>' +
123                '<div class="scroll-element_outer">' +
124                '    <div class="scroll-element_size"></div>' + // required! used for scrollbar size calculation !
125                '    <div class="scroll-element_inner-wrapper">' +
126                '        <div class="scroll-element_inner scroll-element_track">'  + // used for handling scrollbar click
127                '            <div class="scroll-element_inner-bottom"></div>' +
128                '        </div>' +
129                '    </div>' +
130                '    <div class="scroll-bar">' +
131                '        <div class="scroll-bar_body">' +
132                '            <div class="scroll-bar_body-inner"></div>' +
133                '        </div>' +
134                '        <div class="scroll-bar_bottom"></div>' +
135                '        <div class="scroll-bar_center"></div>' +
136                '    </div>' +
137                '</div>',
138
139                "simple":
140                '<div class="scroll-element_outer">
140' +
141            '    <div class="scroll-element_size"></div>'  + // required! used for scrollbar size calculation !
142            '    <div class="scroll-element_track"></div>' + // used for handling scrollbar click
143            '    <div class="scroll-bar"></div>' +
144            '</div>'
145            };
146            var type = html[this.options.type] ? this.options.type : "advanced";
147
148            if(scrollbar){
149                if(typeof(scrollbar) == "string"){
150                    scrollbar = $(scrollbar).appendTo(this.wrapper);
151                } else {
152                    scrollbar = $(scrollbar);
153                }
154            } else {
155                scrollbar = $("<div>").addClass("scroll-element").html(html[type]).appendTo(this.wrapper);
156            }
157
158            if(this.options.showArrows){
159                scrollbar.addClass("scroll-element_arrows_visible");
160            }
161
162            return scrollbar.addClass("scroll-" + d);
163        },
164
165
166
167        init: function(options){
168
169            // init variables
170            var S = this;
171
172            var c = this.container;
173            var o = $.extend(this.options, options);
174            var s = {
175                "x": this.scrollx,
176                "y": this.scrolly
177            };
178            var w = this.wrapper;
179
180            var initScroll = {
181                "scrollLeft": c.scrollLeft(),
182                "scrollTop": c.scrollTop()
183            };
184
185            // ignore mobile
186            if(browser.mobile && o.ignoreMobile){
187                return false;
188            }
189
190            // init scroll container
191            if(!w){
192                this.wrapper = w = c.wrap($("<div>").css({
193                    "position": (c.css("position") == "absolute") ? "absolute" : "relative"
194                }).addClass("scroll-wrapper").addClass(c.attr("class"))).parent();
195
196                c.addClass("scroll-content").css({
197                    "height":"",
198                    "margin-bottom": browser.scroll.height * -1 + px,
199                    "margin-right":  browser.scroll.width  * -1 + px
200                }).on("scroll.scrollbar", function(){
201                    s.x.isVisible && s.x.scroller.css("left", c.scrollLeft() * s.x.kx + px);
202                    s.y.isVisible && s.y.scroller.css("top",  c.scrollTop()  * s.y.kx + px);
203                });
204
205                /* prevent native scrollbars to be visible on #anchor click */
206                w.on("scroll", function(e){
207                    w.scrollTop(0).scrollLeft(0);
208                    e.preventDefault();
209                    return false;
210                });
211
212                if(o.disableBodyScroll){
213                    var handleMouseScroll = function(event){
214                        isVerticalScroll(event) ?
215                        s.y.isVisible && s.y.mousewheel(event) :
216                        s.x.isVisible && s.x.mousewheel(event);
217                    };
218                    w.on({
219                        "MozMousePixelScroll.scrollbar": handleMouseScroll,
220                        "mousewheel.scrollbar": handleMouseScroll
221                    });
222
223                    if(browser.mobile){
224                        w.on("touchstart.scrollbar", function(event){
225                            var touch = event.originalEvent.touches && event.originalEvent.touches[0] || event;
226                            var originalTouch = {
227                                "pageX": touch.pageX,
228                                "pageY": touch.pageY
229                            };
230                            var originalScroll = {
231                                "left": c.scrollLeft(),
232                                "top": c.scrollTop()
233                            };
234                            $(doc).on({
235                                "touchmove.scrollbar": function(event){
236                                    var touch = event.originalEvent.targetTouches && event.originalEvent.targetTouches[0] || event;
237                                    c.scrollLeft(originalScroll.left + originalTouch.pageX - touch.pageX);
238                                    c.scrollTop(originalScroll.top + originalTouch.pageY - touch.pageY);
239                                    event.preventDefault();
240                                },
241                                "touchend.scrollbar": function(){
242                                    $(doc).off(".scrollbar");
243                                }
244                            });
245                        });
246                    }
247                }
248                if($.isFunction(o.onInit))
249                    o.onInit.apply(this, [c]);
250            } else {
251                c.css({
252                    "height":""
253                });
254            }
255
256            // init scrollbars & recalculate sizes
257            $.each(s, function(d, scrollx){
258
259                var scrollCallback = null;
260                var scrollForward = 1;
261                var scrollOffset = (d == "x") ? "scrollLeft" : "scrollTop";
262                var scrollStep = o.scrollStep;
263                var scrollTo = function(){
264                    var currentOffset = c[scrollOffset]();
265                    c[scrollOffset](currentOffset + scrollStep);
266                    if(scrollForward == 1 && (currentOffset + scrollStep) >= scrollToValue)
267                        currentOffset = c[scrollOffset]();
268                    if(scrollForward == -1 && (currentOffset + scrollStep) <= scrollToValue)
269                        currentOffset = c[scrollOffset]();
270                    if(c[scrollOffset]() == currentOffset && scrollCallback){
271                        scrollCallback();
272                    }
273                }
274                var scrollToValue = 0;
275
276                if(!scrollx.scrollbar){
277
278                    scrollx.scrollbar = S.getScrollbar(d);
279                    scrollx.scroller = scrollx.scrollbar.find(".scroll-bar");
280
281                    scrollx.mousewheel = function(event){
282
283                        if(!scrollx.isVisible || (d == 'x' && isVerticalScroll(event))){
284                            return true;
285                        }
286                        if(d == 'y' && !isVerticalScroll(event)){
287                            s.x.mousewheel(event);
288                            return true;
289                        }
290
291                        var delta = event.originalEvent.wheelDelta * -1 || event.originalEvent.detail;
292                        var maxScrollValue = scrollx.size - scrollx.visible - scrollx.offset;
293
294                        if(!((scrollToValue <= 0 && delta < 0) || (scrollToValue >= maxScrollValue && delta > 0))){
295                            scrollToValue = scrollToValue + delta;
296                            if(scrollToValue < 0)
297                                scrollToValue = 0;
298                            if(scrollToValue > maxScrollValue)
299                                scrollToValue = maxScrollValue;
300
301                            S.scrollTo = S.scrollTo || {};
302                            S.scrollTo[scrollOffset] = scrollToValue;
303                            setTimeout(function(){
304                                if(S.scrollTo){
305                                    c.stop().animate(S.scrollTo, 240, 'linear', function(){
306                                        scrollToValue = c[scrollOffset]();
307                                    });
308                                    S.scrollTo = null;
309                                }
310                            }, 1);
311                        }
312
313                        event.preventDefault();
314                        return false;
315                    };
316
317                    scrollx.scrollbar.on({
318                        "MozMousePixelScroll.scrollbar": scrollx.mousewheel,
319                        "mousewheel.scrollbar": scrollx.mousewheel,
320                        "mouseenter.scrollbar": function(){
321                            scrollToValue = c[scrollOffset]();
322                        }
323                    });
324
325                    // handle arrows & scroll inner mousedown event
326                    scrollx.scrollbar.find(".scroll-arrow, .scroll-element_track")
327                    .on("mousedown.scrollbar", function(event){
328
329                        if(event.which != lmb)
330                            return true;
331
332                        scrollForward = 1;
333
334                        var data = {
335                            "eventOffset": event[(d == "x") ? "pageX" : "pageY"],
336                            "maxScrollValue": scrollx.size - scrollx.visible - scrollx.offset,
337                            "scrollbarOffset": scrollx.scroller.offset()[(d == "x") ? "left" : "top"],
338                            "scrollbarSize": scrollx.scroller[(d == "x") ? "outerWidth" : "outerHeight"]()
339                        };
340                        var timeout = 0, timer = 0;
341
342                        if($(this).hasClass('scroll-arrow')){
343                            scrollForward = $(this).hasClass("scroll-arrow_more") ? 1 : -1;
344                            scrollStep = o.scrollStep * scrollForward;
345                            scrollToValue = scrollForward > 0 ? data.maxScrollValue : 0;
346                        } else {
347                            scrollForward = (data.eventOffset > (data.scrollbarOffset + data.scrollbarSize) ? 1
348                                : (data.eventOffset < data.scrollbarOffset ? -1 : 0));
349                            scrollStep = Math.round(scrollx.visible * 0.75) * scrollForward;
350                            scrollToValue = (data.eventOffset - data.scrollbarOffset -
351                                (o.stepScrolling ? (scrollForward == 1 ? data.scrollbarSize : 0)
352                                    : Math.round(data.scrollbarSize / 2)));
353                            scrollToValue = c[scrollOffset]() + (scrollToValue / scrollx.kx);
354                        }
355
356                        S.scrollTo = S.scrollTo || {};
357                        S.scrollTo[scrollOffset] = o.stepScrolling ? c[scrollOffset]() + scrollStep : scrollToValue;
358
359                        if(o.stepScrolling){
360                            scrollCallback = function(){
361                                scrollToValue = c[scrollOffset]();
362                                clearInterval(timer);
363                                clearTimeout(timeout);
364                                timeout = 0;
365                                timer = 0;
366                            };
367                            timeout = setTimeout(function(){
368                                timer = setInterval(scrollTo, 40);
369                            }, o.duration + 100);
370                        }
371
372                        setTimeout(function(){
373                            if(S.scrollTo){
374                                c.animate(S.scrollTo, o.duration);
375                                S.scrollTo = null;
376                            }
377                        }, 1);
378
379                        return handleMouseDown(scrollCallback, event);
380                    });
381
382                    // handle scrollbar drag'n'drop
383                    scrollx.scroller.on("mousedown.scrollbar", function(event){
384
385                        if(event.which != lmb)
386                            return true;
387
388                        var eventPosition = event[(d == "x")? "pageX" : "pageY"];
389                        var initOffset = c[scrollOffset]();
390
391                        scrollx.scrollbar.addClass("scroll-draggable");
392
393                        $(doc).on("mousemove.scrollbar", function(event){
394                            var diff = parseInt((event[(d == "x")? "pageX" : "pageY"] - eventPosition) / scrollx.kx, 10);
395                            c[scrollOffset](initOffset + diff);
396                        });
397
398                        return handleMouseDown(function(){
399                            scrollx.scrollbar.removeClass("scroll-draggable");
400                            scrollToValue = c[scrollOffset]();
401                        }, event);
402                    });
403                }
404            });
405
406            // remove classes & reset applied styles
407            $.each(s, function(d, scrollx){
408                var scrollClass = "scroll-scroll" + d + "_visible";
409                var scrolly = (d == "x") ? s.y : s.x;
410
411                scrollx.scrollbar.removeClass(scrollClass);
412                scrolly.scrollbar.removeClass(scrollClass);
413                c.removeClass(scrollClass);
414            });
415
416            // calculate init sizes
417            $.each(s, function(d, scrollx){
418                $.extend(scrollx, (d == "x") ? {
419                    "offset": parseInt(c.css("left"), 10) || 0,
420                    "size": c.prop("scrollWidth"),
421                    "visible": w.width()
422                } : {
423                    "offset": parseInt(c.css("top"), 10) || 0,
424                    "size": c.prop("scrollHeight"),
425                    "visible": w.height()
426                });
427            });
428
429
430            var updateScroll = function(d, scrollx){
431
432                var scrollClass = "scroll-scroll" + d + "_visible";
433                var scrolly = (d == "x") ? s.y : s.x;
434                var offset = parseInt(c.css((d == "x") ? "left" : "top"), 10) || 0;
435
436                var AreaSize = scrollx.size;
437                var AreaVisible = scrollx.visible + offset;
438
439                scrollx.isVisible = (AreaSize - AreaVisible) > 1; // bug in IE9/11 with 1px diff
440                if(scrollx.isVisible){
441                    scrollx.scrollbar.addClass(scrollClass);
442                    scrolly.scrollbar.addClass(scrollClass);
443                    c.addClass(scrollClass);
444                } else {
445                    scrollx.scrollbar.removeClass(scrollClass);
446                    scrolly.scrollbar.removeClass(scrollClass);
447                    c.removeClass(scrollClass);
448                }
449
450                if(d == "y" && (scrollx.isVisible || scrollx.size < scrollx.visible)){
451                    c.css("height", (AreaVisible + browser.scroll.height) + px);
452                }
453
454                if(s.x.size != c.prop("scrollWidth")
455                    || s.y.size != c.prop("scrollHeight")
456                    || s.x.visible != w.width()
457                    || s.y.visible != w.height()
458                    || s.x.offset  != (parseInt(c.css("left"), 10) || 0)
459                    || s.y.offset  != (parseInt(c.css("top"), 10) || 0)
460                    ){
461                    $.each(s, function(d, scrollx){
462                        $.extend(scrollx, (d == "x") ? {
463                            "offset": parseInt(c.css("left"), 10) || 0,
464                            "size": c.prop("scrollWidth"),
465                            "visible": w.width()
466                        } : {
467                            "offset": parseInt(c.css("top"), 10) || 0,
468                            "size": c.prop("scrollHeight"),
469                            "visible": w.height()
470                        });
471                    });
472                    updateScroll(d == "x" ? "y" : "x", scrolly);
473                }
474            };
475            $.each(s, updateScroll);
476
477            if($.isFunction(o.onUpdate))
478                o.onUpdate.apply(this, [c]);
479
480            // calculate scroll size
481            $.each(s, function(d, scrollx){
482
483                var cssOffset = (d == "x") ? "left" : "top";
484                var cssFullSize = (d == "x") ? "outerWidth" : "outerHeight";
485                var cssSize = (d == "x") ? "width" : "height";
486                var offset = parseInt(c.css(cssOffset), 10) || 0;
487
488                var AreaSize = scrollx.size;
489                var AreaVisible = scrollx.visible + offset;
490
491                var scrollSize = scrollx.scrollbar.find(".scroll-element_size");
492                scrollSize = scrollSize[cssFullSize]() + (parseInt(scrollSize.css(cssOffset), 10) || 0);
493
494                if(o.autoScrollSize){
495                    scrollx.scrollbarSize = parseInt(scrollSize * AreaVisible / AreaSize, 10);
496                    scrollx.scroller.css(cssSize, scrollx.scrollbarSize + px);
497                }
498
499                scrollx.scrollbarSize = scrollx.scroller[cssFullSize]();
500                scrollx.kx = ((scrollSize - scrollx.scrollbarSize) / (AreaSize - AreaVisible)) || 1;
501            });
502
503            c.scrollLeft(initScroll.scrollLeft).scrollTop(initScroll.scrollTop).trigger("scroll");
504        }
505    };
506
507    /*
508     * Extend jQuery as plugin
509     *
510     * @param {object|string} options or command to execute
511     * @param {object|array} args additional arguments as array []
512     */
513    $.fn.scrollbar = function(options, args){
514
515        var toReturn = this;
516
517        if(options == "get")
518            toReturn = null;
519
520        this.each(function() {
521
522            var container = $(this);
523
524            if(container.hasClass("scroll-wrapper")
525                || container.get(0).nodeName == "body"){
526                return true;
527            }
528
529            var instance = container.data("scrollbar");
530            if(instance){
531                if(options === "get"){
532                    toReturn = instance;
533                    return false;
534                }
535
536                options = (typeof options == "string" && instance[options]) ? options : "init";
537                instance[options].apply(instance, $.isArray(args) ? args : []);
538
539                if(options == "destroy"){
540                    container.removeData("scrollbar");
541                    while($.inArray(instance, scrolls) >= 0)
542                        scrolls.splice($.inArray(instance, scrolls), 1);
543                }
544            } else {
545                if(typeof options != "string"){
546                    instance = new customScrollbar(container, options);
547                    container.data("scrollbar", instance);
548                    scrolls.push(instance);
549                }
550            }
551            return true;
552        });
553
554        return toReturn;
555    };
556
557    /**
558     * Connect default options to global object
559     */
560    $.fn.scrollbar.options = defaults;
561
562    /**
563     * Check if scroll content/container size is changed
564     */
565    var timerCounter = 0;
566    var timer = setInterval(function(){
567        var i, c, o, s, w, x, y;
568        for(i=0; i<scrolls.length; i++){
569            s = scrolls[i];
570            c = s.container;
571            o = s.options;
572            w = s.wrapper;
573            x = s.scrollx;
574            y = s.scrolly;
575            if(o.autoUpdate && w && w.is(":visible") &&
576                (c.prop("scrollWidth") != x.size
577                    || c.prop("scrollHeight") != y.size
578                    || w.width()  != x.visible
579                    || w.height() != y.visible
580                    )){
581                s.init();
582
583                if(debug){
584                    browser.log({
585                        "scrollHeight":  c.prop("scrollHeight") + ":" + s.scrolly.size,
586                        "scrollWidth":   c.prop("scrollWidth") + ":" + s.scrollx.size,
587                        "visibleHeight": w.height() + ":" + s.scrolly.visible,
588                        "visibleWidth":  w.width() + ":" + s.scrollx.visible
589                    }, true);
590
591                    if(++timerCounter > 10){
592                        browser.log("Scroll updates exceed 10");
593                        clearInterval(timer);
594                    }
595                }
596            }
597        }
598    }, 300);
599
600
601    /* ADDITIONAL FUNCTIONS */
602    function getBrowserScrollSize(){
603
604        if(browser.webkit){
605            return {
606                "height": 0,
607                "width": 0
608            };
609        }
610
611        var css = {
612            "border":  "none",
613            "height":  "100px",
614            "margin":  "0",
615            "padding": "0",
616            "width":   "100px"
617        };
618
619        var inner = $("<div>").css($.extend({}, css));
620        var outer = $("<div>").css($.extend({
621            "background": "#F00",
622            "left":       "-200px",
623            "overflow":   "scroll",
624            "position":   "absolute",
625            "top":        "-200px"
626        }, css)).append(inner).appendTo("body")
627        .scrollLeft(inner.width())
628        .scrollTop(inner.height());
629
630        var scrollSize = {
631            "height": (outer.offset().top - inner.offset().top) || 0,
632            "width": (outer.offset().left - inner.offset().left) || 0
633        };
634
635        outer.remove();
636        return scrollSize;
637    }
638
639    function handleMouseDown(callback, event){
640        $(doc).on({
641            "blur.scrollbar": function(){
642                $(doc).add('body').off('.scrollbar');
643                callback && callback();
644            },
645            "dragstart.scrollbar": function(event){
646                event.preventDefault();
647                return false;
648            },
649            "mouseup.scrollbar": function(){
650                $(doc).add('body').off('.scrollbar');
651                callback && callback();
652            }
653        });
654        $("body").on({
655            "selectstart.scrollbar": function(event){
656                event.preventDefault();
657                return false;
658            }
659        });
660        event && event.preventDefault();
661        return false;
662    }
663
664    function isVerticalScroll(event){
665        var e = event.originalEvent;
666        if (e.axis && e.axis === e.HORIZONTAL_AXIS)
667            return false;
668        if (e.wheelDeltaX)
669            return false;
670        return true;
671    }
672
673})(jQuery, document, window);

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.