1var throttle = function(func, wait, options) { 2 var context, args, result; 3 var timeout = null; 4 var previous = 0; 5 if (!options) options = {}; 6 var later = function() { 7 previous = options.leading === false ? 0 : Date.now(); 8 timeout = null; 9 result = func.apply(context, args); 10 if (!timeout) context = args = null; 11 }; 12 return function() { 13 var now = Date.now(); 14 if (!previous && options.leading === false) previous = now; 15 var remaining = wait - (now - previous); 16 context = this; 17 args = arguments; 18 if (remaining <= 0 || remaining > wait) { 19 if (timeout) { 20 clearTimeout(timeout); 21 timeout = null; 22 } 23 previous = now; 24 result = func.apply(context, args); 25 if (!timeout) context = args = null; 26 } else if (!timeout && options.trailing !== false) { 27 timeout = setTimeout(later, remaining); 28 } 29 return result; 30 }; 31}; 32 33function getPosition(element) { 34 var xPosition = 0; 35 var yPosition = 0; 36 37 while(element) { 38 xPosition += (element.offsetLeft - element.scrollLeft + element.clientLeft); 39 yPosition += (element.offsetTop - element.scrollTop + element.clientTop); 40 element = element.offsetParent; 41 } 42 43 return { x: xPosition, y: yPosition }; 44} 45 46var ScrollSpy = function() { 47 var instance = { 48 /** 49 * Objet représentant toutes les configurations possibles sur ScrollSpy 50 */ 51 _options: { 52 /** 53 * anchors: liste des zones à repérer dans le menu de sauts. 54 * Peut être : 55 * - une chaîne de caractère (sélecteur) 56 * - un tableau de Nodes 57 * - une fonction qui retourne une NodeList 58 */ 59 anchors: '[data-scrollspy-landmark]', 60 /** 61 * Fonction appelée pour récupérer le titre de la zone. 62 * @param anchor zone ancrée à repérer 63 * @returns {string} Titre de la zone 64 */ 65 anchorGetTitle: function(anchor) { 66 return anchor.querySelector('[data-scrollspy-landmark-title]').innerHTML; 67 }, 68 /** 69 * Décalage du scroll à appliquer lors des calculs (utile en cas d'éléments fixes) 70 */ 71 yFix: 0, 72 /** 73 * Fonction appelée au moment de scroller dans la page. Cette fonction peut-être surchargée pour animer le 74 * scroll par exemple. 75 * @param y Coordonnée du saut. 76 */ 77 jumpTo: function(y) { 78 document.documentElement.scrollTop = y; 79 }, 80 /** 81 * Méthode appelée quand on entre dans une zone. Cette méthode sert notamment à activer les classes 82 * adéquates du menu de sauts entre autres. 83 * @param anchor Ancre concernée. 84 */ 85 onEnter: function(anchor) { 86 anchor.classList.add('active'); 87 this._chaptersElements.forEach(function(handler) { 88 handler.querySelector('[data-scrollspy-jump="' + anchor.getAttribute('id') + '"]').classList.add('active'); 89 }); 90 }, 91 /** 92 * Méthode appelée quand on sort d'une zone. Cette méthode sert notamment à désactiver les classes 93 * adéquantes du menu de sauts entre autres. 94 * @param anchor Ancre concernée. 95 */ 96 onLeave: function(anchor) { 97 anchor.classList.remove('active'); 98 this._chaptersElements.forEach(function(handler) { 99 handler.querySelector('[data-scrollspy-jump="' + anchor.getAttribute('id') + '"]').classList.remove('active'); 100 }); 101 }, 102 /** 103 * Méthode appelée une fois que le ScrollSpy est initialisé. Par défaut, cette méthode crée un menu de sauts 104 * dans une div repérée par l'attribut "data-scrollspy-chapters".
105 * @param anchors Liste des zones ancrées. 106 */ 107 created: function(anchors) { 108 var self = this; 109 var ul = document.createElement('ol'); 110 [].forEach.call(document.querySelectorAll('[data-scrollspy-chapters]'), function($element) { 111 anchors.forEach(function(anchor) { 112 var id = anchor.getAttribute('id'); 113 var a = document.createElement('a'); 114 a.innerHTML = self._options.anchorGetTitle(anchor); 115 a.setAttribute('href', '#' + id); 116 a.setAttribute('data-scrollspy-jump', id); 117 a.addEventListener('click', function(event) { 118 event.preventDefault(); 119 var y = getPosition(anchor).y - self._options.yFix; 120 self._options.jumpTo.call(self, y); 121 return false; 122 }); 123 var li = document.createElement('li'); 124 li.appendChild(a); 125 ul.appendChild(li); 126 }); 127 $element.appendChild(ul); 128 self.addChaptersElement($element); 129 }); 130 } 131 }, 132 _chaptersElements: [], 133 _computedAnchors: [], 134 _onscroll: function(event) { 135 var self = this; 136 this._computedAnchors.forEach(function(anchor) { 137 var rect = anchor.getBoundingClientRect(); 138 if (Math.floor(rect.top) <= self._options.yFix && Math.floor(rect.bottom) > self._options.yFix) { 139 if (!anchor._isActive) { 140 anchor._isActive = true; 141 self._options.onEnter.call(self, anchor); 142 } 143 } else { 144 if (anchor._isActive) { 145 anchor._isActive = false; 146 self._options.onLeave.call(self, anchor); 147 } 148 } 149 }); 150 }, 151 _extend: function(options) { 152 for (var key in options) { 153 if (options.hasOwnProperty(key)) { 154 this._options[key] = options[key]; 155 } 156 } 157 }, 158 _binds: function() { 159 var self = this; 160 var callback = throttle(function (event) { 161 self._onscroll.call(self, event); 162 }, 100, { trailing: true }); 163 window.addEventListener('scroll', callback); 164 window.addEventListener('resize', callback); 165 window.addEventListener('load', callback); 166 }, 167 _create: function() { 168 if (typeof this._options.anchors === typeof '') { 169 this._computedAnchors = document.documentElement.querySelectorAll(this._options.anchors); 170 } else if (typeof this._options.anchors === typeof [] || this._options.anchors instanceof NodeList) { 171 this._computedAnchors = this._options.anchors; 172 } else if (typeof this._options.anchors === typeof Function) { 173 this._computedAnchors = this._options.anchors(document.documentElement); 174 } 175 this._computedAnchors = [].map.call(this._computedAnchors, function(anchor) { 176 anchor._isActive = false; 177 return anchor; 178 }); 179 }, 180 addChaptersElement: function(handler) { 181 this._chaptersElements.push(handler); 182 }, 183 init: function(options) { 184 this._extend(options); 185 this._create(); 186 this._binds(); 187 if (typeof this._options.created === typeof Function) { 188 this._options.created.call(this, this._computedAnchors); 189 } 190 } 191 }; 192 instance.init.apply(instance, arguments); 193 return instance; 194};
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.