PageSourceSearch

https://docs.gethue.com/js/learn.js

js gethue.com collected 2026-09-24 08:58:05 UTC 24,024 bytes, 717 lines download raw bytes

1// Scrollbar Width function
2function getScrollBarWidth() {
3    var inner = document.createElement('p');
4    inner.style.width = "100%";
5    inner.style.height = "200px";
6
7    var outer = document.createElement('div');
8    outer.style.position = "absolute";
9    outer.style.top = "0px";
10    outer.style.left = "0px";
11    outer.style.visibility = "hidden";
12    outer.style.width = "200px";
13    outer.style.height = "150px";
14    outer.style.overflow = "hidden";
15    outer.appendChild(inner);
16
17    document.body.appendChild(outer);
18    var w1 = inner.offsetWidth;
19    outer.style.overflow = 'scroll';
20    var w2 = inner.offsetWidth;
21    if (w1 == w2) w2 = outer.clientWidth;
22
23    document.body.removeChild(outer);
24
25    return (w1 - w2);
26};
27
28var topics = {};
29var hOP = topics.hasOwnProperty;
30
31var huePubSub = {
32    subscribe: function(topic, listener, app) {
33        if (!hOP.call(topics, topic)) {
34            topics[topic] = [];
35        }
36
37        var index =
38          topics[topic].push({
39              listener: listener,
40              app: app,
41              status: 'running'
42          }) - 1;
43
44        return {
45            remove: function() {
46                delete topics[topic][index];
47            }
48        };
49    },
50    removeAll: function(topic) {
51        topics[topic] = [];
52    },
53    subscribeOnce: function(topic, listener, app) {
54        var ephemeral = this.subscribe(
55          topic,
56          function() {
57              listener.apply(listener, arguments);
58              ephemeral.remove();
59          },
60          app
61        );
62    },
63    publish: function(topic, info) {
64        if (!hOP.call(topics, topic)) {
65            return;
66        }
67
68        topics[topic].forEach(item => {
69            if (item.status === 'running') {
70                item.listener(info);
71            }
72        });
73    },
74    getTopics: function() {
75        return topics;
76    },
77    pauseAppSubscribers: function(app) {
78        if (app) {
79            Object.keys(topics).forEach(topicName => {
80                topics[topicName].forEach(topic => {
81                    if (
82                      typeof topic.app !== 'undefined' &&
83                      topic.app !== null &&
84                      (topic.app === app || topic.app.split('-')[0] === app)
85                    ) {
86                        topic.status = 'paused';
87                    }
88                });
89            });
90        }
91    },
92    resumeAppSubscribers: function(app) {
93        if (app) {
94            Object.keys(topics).forEach(topicName => {
95                topics[topicName].forEach(topic => {
96                    if (
97                      typeof topic.app !== 'undefined' &&
98                      topic.app !== null &&
99                      (topic.app === app || topic.app.split('-')[0] === app)
100                    ) {
101                        topic.status = 'running';
102                    }
103                });
104            });
105        }
106    },
107    clearAppSubscribers: function(app) {
108        if (app) {
109            Object.keys(topics).forEach(topicName => {
110                topics[topicName] = topics[topicName].filter(obj => {
111                    return obj.app !== app;
112                });
113            });
114        }
115    }
116};
117
118(function ($, window, document, undefined) {
119
120    var pluginName = "jHueScrollUp",
121      defaults = {
122          threshold: 100, // it displays it after 100 px of scroll
123          scrollLeft: false
124      };
125
126    function Plugin(element, options) {
127        this.element = element;
128        this.options = $.extend({}, defaults, options);
129        this._defaults = defaults;
130        this._name = pluginName;
131        if ($(element).attr('jHueScrollified') !== 'true') {
132            this.setupScrollUp();
133        }
134        if (this.options.scrollLeft) {
135            $(element).jHueScrollLeft(this.options.threshold);
136        }
137    }
138
139    Plugin.prototype.setOptions = function (options) {
140        this.options = $.extend({}, defaults, options);
141    };
142
143    Plugin.prototype.setupScrollUp = function () {
144        var _this = this,
145          link = null;
146
147        if ($("#jHueScrollUpAnchor").length > 0) { // just one scroll up per page
148            link = $("#jHueScrollUpAnchor");
149            $(document).off("click", "#jHueScrollUpAnchor");
150        } else {
151            link = $("<a/>").attr("id", "jHueScrollUpAnchor")
152              .hide()
153              .addClass("hueAnchor hueAnchorScroller")
154              .attr("href", "javascript:void(0)")
155              .html("<i class='fa fa-fw fa-chevron-up'></i>")
156              .appendTo('#body-inner');
157        }
158
159        if ($(window).scrollTop() > _this.options.threshold) {
160            link.show();
161        }
162        $(_this.element).attr("jHueScrollified", "true");
163
164        if ($(_this.element).is("body")) {
165            setScrollBehavior($(window), $("body, html"));
166        } else {
167            setScrollBehavior($(_this.element), $(_this.element));
168        }
169
170        huePubSub.subscribe('reposition.scroll.anchor.up', function(){
171            $('#jHueScrollUpAnchor').css('right', '20px');
172            if (!$(_this.element).is('body') && $(_this.element).is(':visible')) {
173                var adjustRight = $(window).width() - ($(_this.element).width() + $(_this.element).offset().left);
174                if (adjustRight > 0) {
175                    $('#jHueScrollUpAnchor').css('right', adjustRight + 'px');
176                }
177            }
178        });
179
180
181        function setScrollBehavior(scrolled, scrollable) {
182            scrolled.scroll(function () {
183                if (scrolled.scrollTop() > _this.options.threshold) {
184                    if (link.is(":hidden")) {
185                        huePubSub.publish('reposition.scroll.anchor.up');
186                        link.fadeIn(200, function(){
187                            huePubSub.publish('reposition.scroll.anchor.up');
188                        });
189                    }
190                    if ($(_this.element).data("lastScrollTop") == null || $(_this.element).data("lastScrollTop") < scrolled.scrollTop()) {
191                        $("#jHueScrollUpAnchor").data("caller", scrollable);
192                    }
193                    $(_this.element).data("lastScrollTop", scrolled.scrollTop());
194                } else {
195                    checkForAllScrolls();
196                }
197            });
198            window.setTimeout(function() {
199                huePubSub.publish('reposition.scroll.anchor.up');
200            }, 0);
201        }
202
203        function checkForAllScrolls() {
204            var _allOk = true;
205            $(document).find("[jHueScrollified='true']").each(function (cnt, item) {
206                if ($(item).is("body")) {
207                    if ($(window).scrollTop() > _this.options.threshold) {
208                        _allOk = false;
209                        $("#jHueScrollUpAnchor").data("caller", $("body, html"));
210                    }
211                } else if ($(item).scrollTop() > _this.options.threshold) {
212                    _allOk = false;
213                    $("#jHueScrollUpAnchor").data("caller", $(item));
214                }
215            });
216            if (_allOk) {
217                link.fadeOut(200);
218                $("#jHueScrollUpAnchor").data("caller", null);
219            }
220        }
221
222        $(document).on("click", "#jHueScrollUpAnchor", function (event) {
223            if ($("#jHueScrollUpAnchor").data("caller") != null) {
224                $("html, body").animate({ scrollTop: 0 }, 200);
225                if ($(document).find("[jHueScrollified='true']").not($("#jHueScrollUpAnchor").data("caller")).is("body") && $(window).scrollTop() > _this.options.threshold) {
226                    $("#jHueScrollUpAnchor").data("caller", $("body, html"));
227                } else {
228                    checkForAllScrolls();
229                }
230            }
231            return false;
232        });
233    };
234
235    $.fn[pluginName] = function (options) {
236        return this.each(function () {
237            $.data(this, 'plugin_' + pluginName, new Plugin(this, options));
238        });
239    };
240
241    $[pluginName] = function (options) {
242        new Plugin($("body"), options);
243    };
244
245})(jQuery, window, document);
246
247$('body').jHueScrollUp();
248
249function setMenuHeight() {
250    $('#sidebar .highlightable').height($('#sidebar').innerHeight() - $('#header-wrapper').height() - 40);
251    $('#sidebar .highlightable').perfectScrollbar('update');
252}
253
254function fallbackMessage(action) {
255    var actionMsg = '';
256    var actionKey = (action === 'cut' ? 'X' : 'C');
257
258    if (/iPhone|iPad/i.test(navigator.userAgent)) {
259        actionMsg = 'No support :(';
260    }
261    else if (/Mac/i.test(navigator.userAgent)) {
262        actionMsg = 'Press ⌘-' + actionKey + ' to ' + action;
263    }
264    else {
265        actionMsg = 'Press Ctrl-' + actionKey + ' to ' + action;
266    }
267
268    return actionMsg;
269}
270
271// for the window resize
272$(window).resize(function() {
273    setMenuHeight();
274});
275
276// debouncing function from John Hann
277// http://unscriptable.com/index.php/2009/03/20/debouncing-javascript-methods/
278(function($, sr) {
279
280    var debounce = function(func, threshold, execAsap) {
281        var timeout;
282
283        return function debounced() {
284            var obj = this, args = arguments;
285
286            function delayed() {
287                if (!execAsap)
288                    func.apply(obj, args);
289                timeout = null;
290            };
291
292            if (timeout)
293                clearTimeout(timeout);
294            else if (execAsap)
295                func.apply(obj, args);
296
297            timeout = setTimeout(delayed, threshold || 100);
298        };
299    }
300    // smartresize
301    jQuery.fn[sr] = function(fn) { return fn ? this.bind('resize', debounce(fn)) : this.trigger(sr); };
302
303})(jQuery, 'smartresize');
304
305
306jQuery(document).ready(function() {
307    jQuery('#sidebar .category-icon').on('click', function() {
308        $( this ).toggleClass("fa-angle-down fa-angle-right") ;
309        $( this ).parent().parent().children('ul').toggle() ;
310        return false;
311    });
312
313    var sidebarStatus = searchStatus = 'open';
314    $('#sidebar .highlightable').perfectScrollbar();
315    setMenuHeight();
316
317    jQuery('#overlay').on('click', function() {
318        jQuery(document.body).toggleClass('sidebar-hidden');
319        sidebarStatus = (jQuery(document.body).hasClass('sidebar-hidden') ? 'closed' : 'open');
320
321        return false;
322    });
323
324    jQuery('[data-sidebar-toggle]').on('click', function() {
325        jQuery(document.body).toggleClass('sidebar-hidden');
326        sidebarStatus = (jQuery(document.body).hasClass('sidebar-hidden') ? 'closed' : 'open');
327
328        return false;
329    });
330    jQuery('[data-clear-history-toggle]').on('click', function() {
331        sessionStorage.clear();
332        location.reload();
333        return false;
334    });
335    jQuery('[data-search-toggle]').on('click', function() {
336        if (sidebarStatus == 'closed') {
337            jQuery('[data-sidebar-toggle]').trigger('click');
338            jQuery(document.body).removeClass('searchbox-hidden');
339            searchStatus = 'open';
340
341            return false;
342        }
343
344        jQuery(document.body).toggleClass('searchbox-hidden');
345        searchStatus = (jQuery(document.body).hasClass('searchbox-hidden') ? 'closed' : 'open');
346
347        return false;
348    });
349
350    var ajax;
351    jQuery('[data-search-input]').on('input', function() {
352        var input = jQuery(this),
353            value = input.val(),
354            items = jQuery('[data-nav-id]');
355        items.removeClass('search-match');
356        if (!value.length) {
357            $('ul.topics').removeClass('searched');
358            items.css('display', 'block');
359            sessionStorage.removeItem('search-value');
360            $(".highlightable").unhighlight({ element: 'mark' })
361            return;
362        }
363
364        sessionStorage.setItem('search-value', value);
365        $(".highlightable").unhighlight({ element: 'mark' }).highlight(value, { element: 'mark' });
366
367        if (ajax && ajax.abort) ajax.abort();
368
369        jQuery('[data-search-clear]').on('click', function() {
370            jQuery('[data-search-input]').val('').trigger('input');
371            sessionStorage.removeItem('search-input');
372            $(".highlightable").unhighlight({ element: 'mark' })
373        });
374    });
375
376    $.expr[":"].contains = $.expr.createPseudo(function(arg) {
377        return function( elem ) {
378            return $(elem).text().toUpperCase().indexOf(arg.toUpperCase()) >= 0;
379        };
380    });
381
382    if (sessionStorage.getItem('search-value')) {
383        var searchValue = sessionStorage.getItem('search-value')
384        $(document.body).removeClass('searchbox-hidden');
385        $('[data-search-input]').val(searchValue);
386        $('[data-search-input]').trigger('input');
387        var searchedElem = $('#body-inner').find(':contains(' + searchValue + ')').get(0);
388        if (searchedElem) {
389            searchedElem.scrollIntoView(true);
390            var scrolledY = window.scrollY;
391            if(scrolledY){
392                window.scroll(0, scrolledY - 125);
393            }
394        }
395    }
396
397    // clipboard
398    var clipInit = false;
399    $('code').each(function() {
400        var code = $(this),
401            text = code.text();
402
403        if (text.length > 5) {
404            if (!clipInit) {
405                var text, clip = new Clipboard('.copy-to-clipboard', {
406                    text: function(trigger) {
407                        text = $(trigger).prev('code').text();
408                        return text.replace(/^\$\s/gm, '');
409                    }
410                });
411
412                var inPre;
413                clip.on('success', function(e) {
414                    e.clearSelection();
415                    inPre = $(e.trigger).parent().prop('tagName') == 'PRE';
416                    $(e.trigger).attr('aria-label', 'Copied to clipboard!').addClass('tooltipped tooltipped-' + (inPre ? 'w' : 's'));
417                });
418
419                clip.on('error', function(e) {
420                    inPre = $(e.trigger).parent().prop('tagName') == 'PRE';
421                    $(e.trigger).attr('aria-label', fallbackMessage(e.action)).addClass('tooltipped tooltipped-' + (inPre ? 'w' : 's'));
422                    $(document).one('copy', function(){
423                        $(e.trigger).attr('aria-label', 'Copied to clipboard!').addClass('tooltipped tooltipped-' + (inPre ? 'w' : 's'));
424                    });
425                });
426
427                clipInit = true;
428            }
429
430            code.after('<span class="copy-to-clipboard" title="Copy to clipboard" />');
431            code.next('.copy-to-clipboard').on('mouseleave', function() {
432                $(this).attr('aria-label', null).removeClass('tooltipped tooltipped-s tooltipped-w');
433            });
434        }
435    });
436
437    // allow keyboard control for prev/next links
438    jQuery(function() {
439        jQuery('.nav-prev').click(function(){
440            location.href = jQuery(this).attr('href');
441        });
442        jQuery('.nav-next').click(function() {
443            location.href = jQuery(this).attr('href');
444        });
445    });
446
447    jQuery('input, textarea').keydown(function (e) {
448         //  left and right arrow keys
449         if (e.which == '37' || e.which == '39') {
450             e.stopPropagation();
451         }
452     });
453    
454    jQuery(document).keydown(function(e) {
455      // prev links - left arrow key
456      if(e.which == '37') {
457        jQuery('.nav.nav-prev').click();
458      }
459
460      // next links - right arrow key
461      if(e.which == '39') {
462        jQuery('.nav.nav-next').click();
463      }
464    });
465
466    $('#top-bar a:not(:has(img)):not(.btn)').addClass('highlight');
467    $('#body-inner a:not(:has(img)):not(.btn):not(a[rel="footnote"])').addClass('highlight');
468
469    var touchsupport = ('ontouchstart' in window) || (navigator.maxTouchPoints > 0) || (navigator.msMaxTouchPoints > 0)
470    if (!touchsupport){ // browser doesn't support touch
471        $('#toc-menu').hover(function() {
472            $('.progress').stop(true, false, true).fadeToggle(100);
473        });
474
475        $('.progress').hover(function() {
476            $('.progress').stop(true, false, true).fadeToggle(100);
477        });
478    }
479    if (touchsupport){ // browser does support touch
480        $('#toc-menu').click(function() {
481            $('.progress').stop(true, false, true).fadeToggle(100);
482        });
483        $('.progress').click(function() {
484            $('.progress').stop(true, false, true).fadeToggle(100);
485        });
486    }
487
488    /** 
489    * Fix anchor scrolling that hides behind top nav bar
490    * Courtesy of https://stackoverflow.com/a/13067009/28106
491    *
492    * We could use pure css for this if only heading anchors were
493    * involved, but this works for any anchor, including footnotes
494    **/
495    (function (document, history, location) {
496        var HISTORY_SUPPORT = !!(history && history.pushState);
497
498        var anchorScrolls = {
499            ANCHOR_REGEX: /^#[^ ]+$/,
500            OFFSET_HEIGHT_PX: 50,
501
502            /**
503             * Establish events, and fix initial scroll position if a hash is provided.
504             */
505            init: function () {
506                this.scrollToCurrent();
507                $(window).on('hashchange', $.proxy(this, 'scrollToCurrent'));
508                $('body').on('click', 'a', $.proxy(this, 'delegateAnchors'));
509            },
510
511            /**
512             * Return the offset amount to deduct from the normal scroll position.
513             * Modify as appropriate to allow for dynamic calculations
514             */
515            getFixedOffset: function () {
516                return this.OFFSET_HEIGHT_PX;
517            },
518
519            /**
520             * If the provided href is an anchor which resolves to an element on the
521             * page, scroll to it.
522             * @param  {String} href
523             * @return {Boolean} - Was the href an anchor.
524             */
525            scrollIfAnchor: function (href, pushToHistory) {
526                var match, anchorOffset;
527
528                if (!this.ANCHOR_REGEX.test(href)) {
529                    return false;
530                }
531
532                match = document.getElementById(href.slice(1));
533
534                if (match) {
535                    anchorOffset = $(match).offset().top - this.getFixedOffset();
536                    $('html, body').animate({ scrollTop: anchorOffset });
537
538                    // Add the state to history as-per normal anchor links
539                    if (HISTORY_SUPPORT && pushToHistory) {
540                        history.pushState({}, document.title, location.pathname + href);
541                    }
542                }
543
544                return !!match;
545            },
546
547            /**
548             * Attempt to scroll to the current location's hash.
549             */
550            scrollToCurrent: function (e) {
551                if (this.scrollIfAnchor(window.location.hash) && e) {
552                    e.preventDefault();
553                }
554            },
555
556            /**
557             * If the click event's target was an anchor, fix the scroll position.
558             */
559            delegateAnchors: function (e) {
560                var elem = e.target;
561
562                if (this.scrollIfAnchor(elem.getAttribute('href'), true)) {
563                    e.preventDefault();
564                }
565            }
566        };
567
568        $(document).ready($.proxy(anchorScrolls, 'init'));
569    })(window.document, window.history, window.location);
570    
571});
572
573jQuery(window).on('load', function() {
574
575    function adjustForScrollbar() {
576        if ((parseInt(jQuery('#body-inner').height()) + 83) >= jQuery('#body').height()) {
577            jQuery('.nav.nav-next').css({ 'margin-right': getScrollBarWidth() });
578        } else {
579            jQuery('.nav.nav-next').css({ 'margin-right': 0 });
580        }
581    }
582
583    // adjust sidebar for scrollbar
584    adjustForScrollbar();
585
586    jQuery(window).smartresize(function() {
587        adjustForScrollbar();
588    });
589
590    // store this page in session
591    sessionStorage.setItem(jQuery('body').data('url'), 1);
592
593    // loop through the sessionStorage and see if something should be marked as visited
594    for (var url in sessionStorage) {
595        if (sessionStorage.getItem(url) == 1) jQuery('[data-nav-id="' + url + '"]').addClass('visited');
596    }
597
598
599    $(".highlightable").highlight(sessionStorage.getItem('search-value'), { element: 'mark' });
600});
601
602$(function() {
603    $('a[rel="lightbox"]').featherlight({
604        root: 'section#body'
605    });
606});
607
608jQuery.extend({
609    highlight: function(node, re, nodeName, className) {
610        if (node.nodeType === 3) {
611            var match = node.data.match(re);
612            if (match) {
613                var highlight = document.createElement(nodeName || 'span');
614                highlight.className = className || 'highlight';
615                var wordNode = node.splitText(match.index);
616                wordNode.splitText(match[0].length);
617                var wordClone = wordNode.cloneNode(true);
618                highlight.appendChild(wordClone);
619                wordNode.parentNode.replaceChild(highlight, wordNode);
620                return 1; //skip added node in parent
621            }
622        } else if ((node.nodeType === 1 && node.childNodes) && // only element nodes that have children
623            !/(script|style)/i.test(node.tagName) && // ignore script and style nodes
624            !(node.tagName === nodeName.toUpperCase() && node.className === className)) { // skip if already highlighted
625            for (var i = 0; i < node.childNodes.length; i++) {
626                i += jQuery.highlight(node.childNodes[i], re, nodeName, className);
627            }
628        }
629        return 0;
630    }
631});
632
633jQuery.fn.unhighlight = function(options) {
634    var settings = {
635        className: 'highlight',
636        element: 'span'
637    };
638    jQuery.extend(settings, options);
639
640    return this.find(settings.element + "." + settings.className).each(function() {
641        var parent = this.parentNode;
642        parent.replaceChild(this.firstChild, this);
643        parent.normalize();
644    }).end();
645};
646
647jQuery.fn.highlight = function(words, options) {
648    var settings = {
649        className: 'highlight',
650        element: 'span',
651        caseSensitive: false,
652        wordsOnly: false
653    };
654    jQuery.extend(settings, options);
655
656    if (!words) { return; }
657
658    if (words.constructor === String) {
659        words = [words];
660    }
661    words = jQuery.grep(words, function(word, i) {
662        return word != '';
663    });
664    words = jQuery.map(words, function(word, i) {
665        return word.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, "\\$&");
666    });
667    if (words.length == 0) { return this; }
668    ;
669
670    var flag = settings.caseSensitive ? "" : "i";
671    var pattern = "(" + words.join("|") + ")";
672    if (settings.wordsOnly) {
673        pattern = "\\b" + pattern + "\\b";
674    }
675    var re = new RegExp(pattern, flag);
676
677    return this.each(function() {
678        jQuery.highlight(this, re, settings.element, settings.className);
679    });
680};
681
682$(window).on('DOMContentLoaded load resize scroll', function () {
683  function isElementInViewport (el) {
684
685    var rect     = el.getBoundingClientRect(),
686        vWidth   = window.innerWidth || doc.documentElement.clientWidth,
687        vHeight  = window.innerHeight || doc.documentElement.clientHeight,
688        efp      = function (x, y) { return document.elementFromPoint(x, y) };
689
690    // Return false if it's not in the viewport
691    if (rect.right < 0 || rect.bottom < 0
692            || rect.left > vWidth || rect.top > vHeight)
693        return false;
694
695    // Return true if any of its four corners are visible
696    return (
697          el.contains(efp(rect.left,  rect.top))
698      ||  el.contains(efp(rect.right, rect.top))
699      ||  el.contains(efp(rect.right, rect.bottom))
700      ||  el.contains(efp(rect.left,  rect.bottom))
701    );
702  }
703  var visible = 0;
704  $('#body-inner h1, #body-inner h2, #body-inner h3, #body-inner h4, #body-inner h5, #body-inner h6').each(function (i,e) {
705    if(isElementInViewport(e)) {
706      visible = $(e).attr('id');
707      if (visible) {
708        return false;
709      }
710    }
711  });
712
713  if(visible){
714    $('.toc ul li a').removeClass('active');
715    $('.toc ul li a[href="#'+visible+'"]').addClass('active');
716  }
717});

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.