PageSourceSearch

https://sibagu.com/responsive-nav.js

js sibagu.com collected 2026-10-02 11:23:08 UTC 11,951 bytes, 416 lines download raw bytes

1/*! responsive-nav.js v1.0.15
2 * https://github.com/viljamis/responsive-nav.js
3 * http://responsive-nav.com
4 *
5 * Copyright (c) 2013 @viljamis
6 * Available under the MIT license
7 */
8
9/* jshint strict:false, forin:false, noarg:true, noempty:true, eqeqeq:true,
10boss:true, bitwise:true, browser:true, devel:true, indent:2 */
11/* exported responsiveNav */
12
13var responsiveNav = (function (window, document) {
14
15  var computed = !!window.getComputedStyle;
16
17  // getComputedStyle polyfill
18  if (!window.getComputedStyle) {
19    window.getComputedStyle = function(el) {
20      this.el = el;
21      this.getPropertyValue = function(prop) {
22        var re = /(\-([a-z]){1})/g;
23        if (prop === "float") {
24          prop = "styleFloat";
25        }
26        if (re.test(prop)) {
27          prop = prop.replace(re, function () {
28            return arguments[2].toUpperCase();
29          });
30        }
31        return el.currentStyle[prop] ? el.currentStyle[prop] : null;
32      };
33      return this;
34    };
35  }
36
37  var nav,
38    opts,
39    navToggle,
40    styleElement = document.createElement("style"),
41    navOpen = false,
42
43    // fn arg can be an object or a function, thanks to handleEvent
44    // read more at: http://www.thecssninja.com/javascript/handleevent
45    addEvent = function (el, evt, fn, bubble) {
46      if ("addEventListener" in el) {
47        // BBOS6 doesn't support handleEvent, catch and polyfill
48        try {
49          el.addEventListener(evt, fn, bubble);
50        } catch (e) {
51          if (typeof fn === "object" && fn.handleEvent) {
52            el.addEventListener(evt, function (e) {
53              // Bind fn as this and set first arg as event object
54              fn.handleEvent.call(fn, e);
55            }, bubble);
56          } else {
57            throw e;
58          }
59        }
60      } else if ("attachEvent" in el) {
61        // check if the callback is an object and contains handleEvent
62        if (typeof fn === "object" && fn.handleEvent) {
63          el.attachEvent("on" + evt, function () {
64            // Bind fn as this
65            fn.handleEvent.call(fn);
66          });
67        } else {
68          el.attachEvent("on" + evt, fn);
69        }
70      }
71    },
72
73    removeEvent = function (el, evt, fn, bubble) {
74      if ("removeEventListener" in el) {
75        try {
76          el.removeEventListener(evt, fn, bubble);
77        } catch (e) {
78          if (typeof fn === "object" && fn.handleEvent) {
79            el.removeEventListener(evt, function (e) {
80              fn.handleEvent.call(fn, e);
81            }, bubble);
82          } else {
83            throw e;
84          }
85        }
86      } else if ("detachEvent" in el) {
87        if (typeof fn === "object" && fn.handleEvent) {
88          el.detachEvent("on" + evt, function () {
89            fn.handleEvent.call(fn);
90          });
91        } else {
92          el.detachEvent("on" + evt, fn);
93        }
94      }
95    },
96
97    getChildren = function (e) {
98      if (e.children.length < 1) {
99        throw new Error("The Nav container has no containing elements");
100      }
101      // Store all children in array
102      var children = [];
103      // Loop through children and store in array if child != TextNode
104      for (var i = 0; i < e.children.length; i++) {
105        if (e.children[i].nodeType === 1) {
106          children.push(e.children[i]);
107        }
108      }
109      return children;
110    },
111
112    setAttributes = function (el, attrs) {
113      for (var key in attrs) {
114        el.setAttribute(key, attrs[key]);
115      }
116    },
117
118    addClass = function (el, cls) {
119      el.className += " " + cls;
120      el.className = el.className.replace(/(^\s*)|(\s*$)/g,"");
121    },
122
123    removeClass = function (el, cls) {
124      var reg = new RegExp("(\\s|^)" + cls + "(\\s|$)");
125      el.className = el.className.replace(reg, " ").replace(/(^\s*)|(\s*$)/g,"");
126    },
127
128    ResponsiveNav = function (el, options) {
129      var i;
130
131      // Default options
132      this.options = {
133        animate: true,        // Boolean: Use CSS3 transitions, true or false
134        transition: 400,      // Integer: Speed of the transition, in milliseconds
135        label: "Site navigation",        // String: Label for the navigation toggle
136        insert: "before",      // String: Insert the toggle before or after the navigation
137        customToggle: "",     // Selector: Specify the ID of a custom toggle
138        openPos: "relative",  // String: Position of the opened nav, relative or static
139        jsClass: "js",        // String: 'JS enabled' class which is added to <html> el
140        init: function(){},   // Function: Init callback
141        open: function(){},   // Function: Open callback
142        close: function(){}   // Function: Close callback
143      };
144
145      // User defined options
146      for (i in options) {
147        this.options[i] = options[i];
148      }
149
150      // Adds "js" class for <html>
151      addClass(document.documentElement, this.options.jsClass);
152
153      // Wrapper
154      this.wrapperEl = el.replace("#", "");
155      if (document.getElementById(this.wrapperEl)) {
156        this.wrapper = document.getElementById(this.wrapperEl);
157      } else {
158        // If el doesn't exists, stop here.
159        throw new Error("The nav element you are trying to select doesn't exist");
160      }
161
162      // Inner wrapper
163      this.wrapper.inner = getChildren(this.wrapper);
164
165      // For minification
166      opts = this.options;
167      nav = this.wrapper;
168
169      // Init
170      this._init(this);
171    };
172
173  ResponsiveNav.prototype = {
174    // Public methods
175    destroy: function () {
176      this._removeStyles();
177      removeClass(nav, "closed");
178      removeClass(nav, "opened");
179      nav.removeAttribute("style");
180      nav.removeAttribute("aria-hidden");
181      nav = null;
182      _instance = null;
183
184      removeEvent(window, "load", this, false);
185      removeEvent(window, "resize", this, false);
186      removeEvent(navToggle, "mousedown", this, false);
187      removeEvent(navToggle, "touchstart", this, false);
188      removeEvent(navToggle, "touchend", this, false);
189      removeEvent(navToggle, "keyup", this, false);
190      removeEvent(navToggle, "click", this, false);
191
192      if (!opts.customToggle) {
193        navToggle.parentNode.removeChild(navToggle);
194      } else {
195        navToggle.removeAttribute("aria-hidden");
196      }
197    },
198
199    toggle: function () {
200      if (!navOpen) {
201        removeClass(nav, "closed");
202        addClass(nav, "opened");
203        nav.style.position = opts.openPos;
204        setAttributes(nav, {"aria-hidden": "false"});
205
206        navOpen = true;
207        opts.open();
208      } else {
209        removeClass(nav, "opened");
210        addClass(nav, "closed");
211        setAttributes(nav, {"aria-hidden": "true"});
212
213        if (opts.animate) {
214          setTimeout(function () {
215            nav.style.position = "absolute";
216          }, opts.transition + 10);
217        } else {
218          nav.style.position = "absolute";
219        }
220
221        navOpen = false;
222        opts.close();
223      }
224    },
225
226    handleEvent: function (e) {
227      var evt = e || window.event;
228
229      switch (evt.type) {
230      case "mousedown":
231        this._onmousedown(evt);
232        break;
233      case "touchstart":
234        this._ontouchstart(evt);
235        break;
236      case "touchend":
237        this._ontouchend(evt);
238        break;
239      case "keyup":
240        this._onkeyup(evt);
241        break;
242      case "click":
243        this._onclick(evt);
244        break;
245      case "load":
246        this._transitions(evt);
247        this._resize(evt);
248        break;
249      case "resize":
250        this._resize(evt);
251        break;
252      }
253    },
254
255    // Private methods
256    _init: function () {
257      addClass(nav, "closed");
258      this._createToggle();
259
260      addEvent(window, "load", this, false);
261      addEvent(window, "resize", this, false);
262      addEvent(navToggle, "mousedown", this, false);
263      addEvent(navToggle, "touchstart", this, false);
264      addEvent(navToggle, "touchend", this, false);
265      addEvent(navToggle, "keyup", this, false);
266      addEvent(navToggle, "click", this, false);
267    },
268
269    _createStyles: function () {
270      if (!styleElement.parentNode) {
271        document.getElementsByTagName("head")[0].appendChild(styleElement);
272      }
273    },
274
275    _removeStyles: function () {
276      if (styleElement.parentNode) {
277        styleElement.parentNode.removeChild(styleElement);
278      }
279    },
280
281    _createToggle: function () {
282      if (!opts.customToggle) {
283        var toggle = document.createElement("a");
284        toggle.innerHTML = opts.label;
285        setAttributes(toggle, {
286          "href": "#",
287          "id": "nav-toggle"
288        });
289
290        if (opts.insert === "after") {
291          nav.parentNode.insertBefore(toggle, nav.nextSibling);
292        } else {
293          nav.parentNode.insertBefore(toggle, nav);
294        }
295
296        navToggle = document.getElementById("nav-toggle");
297      } else {
298        var toggleEl = opts.customToggle.replace("#", "");
299
300        if (document.getElementById(toggleEl)) {
301          navToggle = document.getElementById(toggleEl);
302        } else {
303          throw new Error("The custom nav toggle you are trying to select doesn't exist");
304        }
305      }
306    },
307
308    _preventDefault: function(e) {
309      if (e.preventDefault) {
310        e.preventDefault();
311        e.stopPropagation();
312      } else {
313        e.returnValue = false;
314      }
315    },
316
317    _onmousedown: function (e) {
318      var evt = e || window.event;
319      // If the user isn't right clicking:
320      if (!(evt.which === 3 || evt.button === 2)) {
321        this._preventDefault(e);
322        this.toggle(e);
323      }
324    },
325
326    _ontouchstart: function (e) {
327      // Touchstart event fires before
328      // the mousedown and can wipe it
329      navToggle.onmousedown = null;
330      this._preventDefault(e);
331      this.toggle(e);
332    },
333
334    _ontouchend: function () {
335      // Prevents ghost click from happening on some Android browsers
336      var that = this;
337      nav.addEventListener("click", that._preventDefault, true);
338      setTimeout(function () {
339        nav.removeEventListener("click", that._preventDefault, true);
340      }, opts.transition);
341    },
342
343    _onkeyup: function (e) {
344      var evt = e || window.event;
345      if (evt.keyCode === 13) {
346        this.toggle(e);
347      }
348    },
349
350    _onclick: function (e) {
351      // For older browsers (looking at IE)
352      this._preventDefault(e);
353    },
354
355    _transitions: function () {
356      if (opts.animate) {
357        var objStyle = nav.style,
358          transition = "max-height " + opts.transition + "ms";
359
360        objStyle.WebkitTransition = transition;
361        objStyle.MozTransition = transition;
362        objStyle.OTransition = transition;
363        objStyle.transition = transition;
364      }
365    },
366
367    _calcHeight: function () {
368      var savedHeight = 0;
369      for (var i = 0; i < nav.inner.length; i++) {
370        savedHeight += nav.inner[i].offsetHeight;
371      }
372
373      var innerStyles = "#" + this.wrapperEl + ".opened{max-height:" + savedHeight + "px}";
374
375      // Hide from old IE
376      if (computed) {
377        styleElement.innerHTML = innerStyles;
378        innerStyles = "";
379      }
380    },
381
382    _resize: function () {
383      if (window.getComputedStyle(navToggle, null).getPropertyValue("display") !== "none") {
384        setAttributes(navToggle, {"aria-hidden": "false"});
385
386        // If the navigation is hidden
387        if (nav.className.match(/(^|\s)closed(\s|$)/)) {
388          setAttributes(nav, {"aria-hidden": "true"});
389          nav.style.position = "absolute";
390        }
391
392        this._createStyles();
393        this._calcHeight();
394      } else {
395        setAttributes(navToggle, {"aria-hidden": "true"});
396        setAttributes(nav, {"aria-hidden": "false"});
397        nav.style.position = opts.openPos;
398        this._removeStyles();
399      }
400
401      // Init callback
402      opts.init();
403    }
404
405  };
406
407  var _instance;
408  function rn (el, options) {
409    if (!_instance) {
410      _instance = new ResponsiveNav(el, options);
411    }
412    return _instance;
413  }
414
415  return rn;
416})(window, document);

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.