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.