PageSourceSearch

https://www.toile-libre.org/js/learn.js?1790845865

js toile-libre.org collected 2026-10-01 09:14:55 UTC 16,771 bytes, 496 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
28function setMenuHeight() {
29    $('#sidebar .highlightable').height($('#sidebar').innerHeight() - $('#header-wrapper').height() - 40);
30    $('#sidebar .highlightable').perfectScrollbar('update');
31}
32
33function fallbackMessage(action) {
34    var actionMsg = '';
35    var actionKey = (action === 'cut' ? 'X' : 'C');
36
37    if (/iPhone|iPad/i.test(navigator.userAgent)) {
38        actionMsg = 'No support :(';
39    }
40    else if (/Mac/i.test(navigator.userAgent)) {
41        actionMsg = 'Press ⌘-' + actionKey + ' to ' + action;
42    }
43    else {
44        actionMsg = 'Press Ctrl-' + actionKey + ' to ' + action;
45    }
46
47    return actionMsg;
48}
49
50function switchTab(tabGroup, tabId) {
51    allTabItems = jQuery("[data-tab-group='"+tabGroup+"']");
52    targetTabItems = jQuery("[data-tab-group='"+tabGroup+"'][data-tab-item='"+tabId+"']");
53
54    // if event is undefined then switchTab was called from restoreTabSelection
55    // so it's not a button event and we don't need to safe the selction or
56    // prevent page jump
57    var isButtonEvent = event != undefined;
58
59    if(isButtonEvent){
60      // save button position relative to viewport
61      var yposButton = event.target.getBoundingClientRect().top;
62    }
63
64    allTabItems.removeClass("active");
65    targetTabItems.addClass("active");
66
67    if(isButtonEvent){
68      // reset screen to the same position relative to clicked button to prevent page jump
69      var yposButtonDiff = event.target.getBoundingClientRect().top - yposButton;
70      window.scrollTo(window.scrollX, window.scrollY+yposButtonDiff);
71
72      // Store the selection to make it persistent
73      if(window.localStorage){
74          var selectionsJSON = window.localStorage.getItem("tabSelections");
75          if(selectionsJSON){
76            var tabSelections = JSON.parse(selectionsJSON);
77          }else{
78            var tabSelections = {};
79          }
80          tabSelections[tabGroup] = tabId;
81          window.localStorage.setItem("tabSelections", JSON.stringify(tabSelections));
82      }
83    }
84}
85
86function restoreTabSelections() {
87    if(window.localStorage){
88        var selectionsJSON = window.localStorage.getItem("tabSelections");
89        if(selectionsJSON){
90          var tabSelections = JSON.parse(selectionsJSON);
91        }else{
92          var tabSelections = {};
93        }
94        Object.keys(tabSelections).forEach(function(tabGroup) {
95          var tabItem = tabSelections[tabGroup];
96          switchTab(tabGroup, tabItem);
97        });
98    }
99}
100
101// for the window resize
102$(window).resize(function() {
103    setMenuHeight();
104});
105
106// debouncing function from John Hann
107// http://unscriptable.com/index.php/2009/03/20/debouncing-javascript-methods/
108(function($, sr) {
109
110    var debounce = function(func, threshold, execAsap) {
111        var timeout;
112
113        return function debounced() {
114            var obj = this, args = arguments;
115
116            function delayed() {
117                if (!execAsap)
118                    func.apply(obj, args);
119                timeout = null;
120            };
121
122            if (timeout)
123                clearTimeout(timeout);
124            else if (execAsap)
125                func.apply(obj, args);
126
127            timeout = setTimeout(delayed, threshold || 100);
128        };
129    }
130    // smartresize
131    jQuery.fn[sr] = function(fn) { return fn ? this.bind('resize', debounce(fn)) : this.trigger(sr); };
132
133})(jQuery, 'smartresize');
134
135
136jQuery(document).ready(function() {
137    restoreTabSelections();
138
139    jQuery('#sidebar .category-icon').on('click', function() {
140        $( this ).toggleClass("fa-angle-down fa-angle-right") ;
141        $( this ).parent().parent().children('ul').toggle() ;
142        return false;
143    });
144
145    var sidebarStatus = searchStatus = 'open';
146    $('#sidebar .highlightable').perfectScrollbar();
147    setMenuHeight();
148
149    jQuery('#overlay').on('click', function() {
150        jQuery(document.body).toggleClass('sidebar-hidden');
151        sidebarStatus = (jQuery(document.body).hasClass('sidebar-hidden') ? 'closed' : 'open');
152
153        return false;
154    });
155
156    jQuery('[data-sidebar-toggle]').on('click', function() {
157        jQuery(document.body).toggleClass('sidebar-hidden');
158        sidebarStatus = (jQuery(document.body).hasClass('sidebar-hidden') ? 'closed' : 'open');
159
160        return false;
161    });
162    jQuery('[data-clear-history-toggle]').on('click', function() {
163        sessionStorage.clear();
164        location.reload();
165        return false;
166    });
167    jQuery('[data-search-toggle]').on('click', function() {
168        if (sidebarStatus == 'closed') {
169            jQuery('[data-sidebar-toggle]').trigger('click');
170            jQuery(document.body).removeClass('searchbox-hidden');
171            searchStatus = 'open';
172
173            return false;
174        }
175
176        jQuery(document.body).toggleClass('searchbox-hidden');
177        searchStatus = (jQuery(document.body).hasClass('searchbox-hidden') ? 'closed' : 'open');
178
179        return false;
180    });
181
182    var ajax;
183    jQuery('[data-search-input]').on('input', function() {
184        var input = jQuery(this),
185            value = input.val(),
186            items = jQuery('[data-nav-id]');
187        items.removeClass('search-match');
188        if (!value.length) {
189            $('ul.topics').removeClass('searched');
190            items.css('display', 'block');
191            sessionStorage.removeItem('search-value');
192            $(".highlightable").unhighlight({ element: 'mark' })
193            return;
194        }
195
196        sessionStorage.setItem('search-value', value);
197        $(".highlightable").unhighlight({ element: 'mark' }).highlight(value, { element: 'mark' });
198
199        if (ajax && ajax.abort) ajax.abort();
200
201        jQuery('[data-search-clear]').on('click', function() {
202            jQuery('[data-search-input]').val('').trigger('input');
203            sessionStorage.removeItem('search-input');
204            $(".highlightable").unhighlight({ element: 'mark' })
205        });
206    });
207
208    $.expr[":"].contains = $.expr.createPseudo(function(arg) {
209        return function( elem ) {
210            return $(elem).text().toUpperCase().indexOf(arg.toUpperCase()) >= 0;
211        };
212    });
213
214    if (sessionStorage.getItem('search-value')) {
215        var searchValue = sessionStorage.getItem('search-value')
216        $(document.body).removeClass('searchbox-hidden');
217        $('[data-search-input]').val(searchValue);
218        $('[data-search-input]').trigger('input');
219        var searchedElem = $('#body-inner').find(':contains(' + searchValue + ')').get(0);
220        if (searchedElem) {
221            searchedElem.scrollIntoView(true);
222            var scrolledY = window.scrollY;
223            if(scrolledY){
224                window.scroll(0, scrolledY - 125);
225            }
226        }
227    }
228
229    // clipboard
230    var clipInit = false;
231    $('code').each(function() {
232        var code = $(this),
233            text = code.text();
234
235        if (text.length > 5) {
236            if (!clipInit) {
237                var text, clip = new ClipboardJS('.copy-to-clipboard', {
238                    text: function(trigger) {
239                        text = $(trigger).prev('code').text();
240                        return text.replace(/^\$\s/gm, '');
241                    }
242                });
243
244                var inPre;
245                clip.on('success', function(e) {
246                    e.clearSelection();
247                    inPre = $(e.trigger).parent().prop('tagName') == 'PRE';
248                    $(e.trigger).attr('aria-label', 'Copied to clipboard!').addClass('tooltipped tooltipped-' + (inPre ? 'w' : 's'));
249                });
250
251                clip.on('error', function(e) {
252                    inPre = $(e.trigger).parent().prop('tagName') == 'PRE';
253                    $(e.trigger).attr('aria-label', fallbackMessage(e.action)).addClass('tooltipped tooltipped-' + (inPre ? 'w' : 's'));
254                    $(document).one('copy', function(){
255                        $(e.trigger).attr('aria-label', 'Copied to clipboard!').addClass('tooltipped tooltipped-' + (inPre ? 'w' : 's'));
256                    });
257                });
258
259                clipInit = true;
260            }
261
262            code.after('<span class="copy-to-clipboard" title="Copy to clipboard" />');
263            code.next('.copy-to-clipboard').on('mouseleave', function() {
264                $(this).attr('aria-label', null).removeClass('tooltipped tooltipped-s tooltipped-w');
265            });
266        }
267    });
268
269    // allow keyboard control for prev/next links
270    jQuery(function() {
271        jQuery('.nav-prev').click(function(){
272            location.href = jQuery(this).attr('href');
273        });
274        jQuery('.nav-next').click(function() {
275            location.href = jQuery(this).attr('href');
276        });
277    });
278
279    jQuery('input, textarea').keydown(function (e) {
280         //  left and right arrow keys
281         if (e.which == '37' || e.which == '39') {
282             e.stopPropagation();
283         }
284     });
285
286    jQuery(document).keydown(function(e) {
287      // prev links - left arrow key
288      if(e.which == '37') {
289        jQuery('.nav.nav-prev').click();
290      }
291
292      // next links - right arrow key
293      if(e.which == '39') {
294        jQuery('.nav.nav-next').click();
295      }
296    });
297
298    $('#top-bar a:not(:has(img)):not(.btn)').addClass('highlight');
299    $('#body-inner a:not(:has(img)):not(.btn):not(a[rel="footnote"])').addClass('highlight');
300
301    var touchsupport = ('ontouchstart' in window) || (navigator.maxTouchPoints > 0) || (navigator.msMaxTouchPoints > 0)
302    if (!touchsupport){ // browser doesn't support touch
303        $('#toc-menu').hover(function() {
304            $('.progress').stop(true, false, true).fadeToggle(100);
305        });
306
307        $('.progress').hover(function() {
308            $('.progress').stop(true, false, true).fadeToggle(100);
309        });
310    }
311    if (touchsupport){ // browser does support touch
312        $('#toc-menu').click(function() {
313            $('.progress').stop(true, false, true).fadeToggle(100);
314        });
315        $('.progress').click(function() {
316            $('.progress').stop(true, false, true).fadeToggle(100);
317        });
318    }
319
320    /**
321    * Fix anchor scrolling that hides behind top nav bar
322    * Courtesy of https://stackoverflow.com/a/13067009/28106
323    *
324    * We could use pure css for this if only heading anchors were
325    * involved, but this works for any anchor, including footnotes
326    **/
327    (function (document, history, location) {
328        var HISTORY_SUPPORT = !!(history && history.pushState);
329
330        var anchorScrolls = {
331            ANCHOR_REGEX: /^#[^ ]+$/,
332            OFFSET_HEIGHT_PX: 50,
333
334            /**
335             * Establish events, and fix initial scroll position if a hash is provided.
336             */
337            init: function () {
338                this.scrollToCurrent();
339                $(window).on('hashchange', $.proxy(this, 'scrollToCurrent'));
340                $('body').on('click', 'a', $.proxy(this, 'delegateAnchors'));
341            },
342
343            /**
344             * Return the offset amount to deduct from the normal scroll position.
345             * Modify as appropriate to allow for dynamic calculations
346             */
347            getFixedOffset: function () {
348                return this.OFFSET_HEIGHT_PX;
349            },
350
351            /**
352             * If the provided href is an anchor which resolves to an element on the
353             * page, scroll to it.
354             * @param  {String} href
355             * @return {Boolean} - Was the href an anchor.
356             */
357            scrollIfAnchor: function (href, pushToHistory) {
358                var match, anchorOffset;
359
360                if (!this.ANCHOR_REGEX.test(href)) {
361                    return false;
362                }
363
364                match = document.getElementById(href.slice(1));
365
366                if (match) {
367                    anchorOffset = $(match).offset().top - this.getFixedOffset();
368                    $('html, body').animate({ scrollTop: anchorOffset });
369
370                    // Add the state to history as-per normal anchor links
371                    if (HISTORY_SUPPORT && pushToHistory) {
372                        history.pushState({}, document.title, location.pathname + href);
373                    }
374                }
375
376                return !!match;
377            },
378
379            /**
380             * Attempt to scroll to the current location's hash.
381             */
382            scrollToCurrent: function (e) {
383                if (this.scrollIfAnchor(window.location.hash) && e) {
384                    e.preventDefault();
385                }
386            },
387
388            /**
389             * If the click event's target was an anchor, fix the scroll position.
390             */
391            delegateAnchors: function (e) {
392                var elem = e.target;
393
394                if (this.scrollIfAnchor(elem.getAttribute('href'), true)) {
395                    e.preventDefault();
396                }
397            }
398        };
399
400        $(document).ready($.proxy(anchorScrolls, 'init'));
401    })(window.document, window.history, window.location);
402
403});
404
405jQuery(window).on('load', function() {
406    // store this page in session
407    sessionStorage.setItem(jQuery('body').data('url'), 1);
408
409    // loop through the sessionStorage and see if something should be marked as visited
410    for (var url in sessionStorage) {
411        if (sessionStorage.getItem(url) == 1) jQuery('[data-nav-id="' + url + '"]').addClass('visited');
412    }
413
414
415    $(".highlightable").highlight(sessionStorage.getItem('search-value'), { element: 'mark' });
416});
417
418$(function() {
419    $('a[rel="lightbox"]').featherlight({
420        root: 'section#body'
421    });
422});
423
424jQuery.extend({
425    highlight: function(node, re, nodeName, className) {
426        if (node.nodeType === 3) {
427            var match = node.data.match(re);
428            if (match) {
429                var highlight = document.createElement(nodeName || 'span');
430                highlight.className = className || 'highlight';
431                var wordNode = node.splitText(match.index);
432                wordNode.splitText(match[0].length);
433                var wordClone = wordNode.cloneNode(true);
434                highlight.appendChild(wordClone);
435                wordNode.parentNode.replaceChild(highlight, wordNode);
436                return 1; //skip added node in parent
437            }
438        } else if ((node.nodeType === 1 && node.childNodes) && // only element nodes that have children
439            !/(script|style)/i.test(node.tagName) && // ignore script and style nodes
440            !(node.tagName === nodeName.toUpperCase() && node.className === className)) { // skip if already highlighted
441            for (var i = 0; i < node.childNodes.length; i++) {
442                i += jQuery.highlight(node.childNodes[i], re, nodeName, className);
443            }
444        }
445        return 0;
446    }
447});
448
449jQuery.fn.unhighlight = function(options) {
450    var settings = {
451        className: 'highlight',
452        element: 'span'
453    };
454    jQuery.extend(settings, options);
455
456    return this.find(settings.element + "." + settings.className).each(function() {
457        var parent = this.parentNode;
458        parent.replaceChild(this.firstChild, this);
459        parent.normalize();
460    }).end();
461};
462
463jQuery.fn.highlight = function(words, options) {
464    var settings = {
465        className: 'highlight',
466        element: 'span',
467        caseSensitive: false,
468        wordsOnly: false
469    };
470    jQuery.extend(settings, options);
471
472    if (!words) { return; }
473
474    if (words.constructor === String) {
475        words = [words];
476    }
477    words = jQuery.grep(words, function(word, i) {
478        return word != '';
479    });
480    words = jQuery.map(words, function(word, i) {
481        return word.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, "\\$&");
482    });
483    if (words.length == 0) { return this; }
484    ;
485
486    var flag = settings.caseSensitive ? "" : "i";
487    var pattern = "(" + words.join("|") + ")";
488    if (settings.wordsOnly) {
489        pattern = "\\b" + pattern + "\\b";
490    }
491    var re = new RegExp(pattern, flag);
492
493    return this.each(function() {
494        jQuery.highlight(this, re, settings.element, settings.className);
495    });
496};

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.