1'use strict'; 2 3var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); 4 5function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } 6 7!function ($) { 8 9 /** 10 * OffCanvas module. 11 * @module foundation.offcanvas 12 * @requires foundation.util.keyboard 13 * @requires foundation.util.mediaQuery 14 * @requires foundation.util.triggers 15 * @requires foundation.util.motion 16 */ 17 18 var OffCanvas = function () { 19 /** 20 * Creates a new instance of an off-canvas wrapper. 21 * @class 22 * @fires OffCanvas#init 23 * @param {Object} element - jQuery object to initialize. 24 * @param {Object} options - Overrides to the default plugin settings. 25 */ 26 function OffCanvas(element, options) { 27 _classCallCheck(this, OffCanvas); 28 29 this.$element = element; 30 this.options = $.extend({}, OffCanvas.defaults, this.$element.data(), options); 31 this.$lastTrigger = $(); 32 this.$triggers = $(); 33 34 this._init(); 35 this._events(); 36 37 Foundation.registerPlugin(this, 'OffCanvas'); 38 Foundation.Keyboard.register('OffCanvas', { 39 'ESCAPE': 'close' 40 }); 41 } 42 43 /** 44 * Initializes the off-canvas wrapper by adding the exit overlay (if needed). 45 * @function 46 * @private 47 */ 48 49 50 _createClass(OffCanvas, [{ 51 key: '_init', 52 value: function _init() { 53 var id = this.$element.attr('id'); 54 55 this.$element.attr('aria-hidden', 'true'); 56 57 this.$element.addClass('is-transition-' + this.options.transition); 58 59 // Find triggers that affect this element and add aria-expanded to them 60 this.$triggers = $(document).find('[data-open="' + id + '"], [data-close="' + id + '"], [data-toggle="' + id + '"]').attr('aria-expanded', 'false').attr('aria-controls', id); 61 62 // Add an overlay over the content if necessary 63 if (this.options.contentOverlay === true) { 64 var overlay = document.createElement('div'); 65 var overlayPosition = $(this.$element).css("position") === 'fixed' ? 'is-overlay-fixed' : 'is-overlay-absolute'; 66 overlay.setAttribute('class', 'js-off-canvas-overlay ' + overlayPosition); 67 this.$overlay = $(overlay); 68 if (overlayPosition === 'is-overlay-fixed') { 69 $('body').append(this.$overlay); 70 } else { 71 this.$element.siblings('[data-off-canvas-content]').append(this.$overlay); 72 } 73 } 74 75 this.options.isRevealed = this.options.isRevealed || new RegExp(this.options.revealClass, 'g').test(this.$element[0].className); 76 77 if (this.options.isRevealed === true) { 78 this.options.revealOn = this.options.revealOn || this.$element[0].className.match(/(reveal-for-medium|reveal-for-large)/g)[0].split('-')[2]; 79 this._setMQChecker(); 80 } 81 if (!this.options.transitionTime === true) { 82 this.options.transitionTime = parseFloat(window.getComputedStyle($('[data-off-canvas]')[0]).transitionDuration) * 1000; 83 } 84 } 85 86 /** 87 * Adds event handlers to the off-canvas wrapper and the exit overlay. 88 * @function 89 * @private 90 */ 91 92 }, { 93 key: '_events', 94 value: function _events() { 95 this.$element.off('.zf.trigger .zf.offcanvas').on({ 96 'open.zf.trigger': this.open.bind(this), 97 'close.zf.trigger': this.close.bind(this), 98 'toggle.zf.trigger': this.toggle.bind(this), 99 'keydown.zf.offcanvas': this._handleKeyboard.bind(this) 100 }); 101 102 if (this.options.closeOnClick === true) { 103 var $target = this.options.contentOverlay ? this.$overlay : $('[data-off-canvas-content]'); 104 $target.on({ 'click.zf.offcanvas': this.close.bind(this) }); 105 } 106 } 107 108 /** 109 * Applies event listener for elements that will reveal at certain breakpoints. 110 * @private 111 */ 112 113 }, {
114 key: '_setMQChecker', 115 value: function _setMQChecker() { 116 var _this = this; 117 118 $(window).on('changed.zf.mediaquery', function () { 119 if (Foundation.MediaQuery.atLeast(_this.options.revealOn)) { 120 _this.reveal(true); 121 } else { 122 _this.reveal(false); 123 } 124 }).one('load.zf.offcanvas', function () { 125 if (Foundation.MediaQuery.atLeast(_this.options.revealOn)) { 126 _this.reveal(true); 127 } 128 }); 129 } 130 131 /** 132 * Handles the revealing/hiding the off-canvas at breakpoints, not the same as open. 133 * @param {Boolean} isRevealed - true if element should be revealed. 134 * @function 135 */ 136 137 }, { 138 key: 'reveal', 139 value: function reveal(isRevealed) { 140 var $closer = this.$element.find('[data-close]'); 141 if (isRevealed) { 142 this.close(); 143 this.isRevealed = true; 144 this.$element.attr('aria-hidden', 'false'); 145 this.$element.off('open.zf.trigger toggle.zf.trigger'); 146 if ($closer.length) { 147 $closer.hide(); 148 } 149 } else { 150 this.isRevealed = false; 151 this.$element.attr('aria-hidden', 'true'); 152 this.$element.off('open.zf.trigger toggle.zf.trigger').on({ 153 'open.zf.trigger': this.open.bind(this), 154 'toggle.zf.trigger': this.toggle.bind(this) 155 }); 156 if ($closer.length) { 157 $closer.show(); 158 } 159 } 160 } 161 162 /** 163 * Stops scrolling of the body when offcanvas is open on mobile Safari and other troublesome browsers. 164 * @private 165 */ 166 167 }, { 168 key: '_stopScrolling', 169 value: function _stopScrolling(event) { 170 return false; 171 } 172 173 /** 174 * Opens the off-canvas menu. 175 * @function 176 * @param {Object} event - Event object passed from listener. 177 * @param {jQuery} trigger - element that triggered the off-canvas to open. 178 * @fires OffCanvas#opened 179 */ 180 181 }, { 182 key: 'open', 183 value: function open(event, trigger) { 184 if (this.$element.hasClass('is-open') || this.isRevealed) { 185 return; 186 } 187 var _this = this; 188 189 if (trigger) { 190 this.$lastTrigger = trigger; 191 } 192 193 if (this.options.forceTo === 'top') { 194 window.scrollTo(0, 0); 195 } else if (this.options.forceTo === 'bottom') { 196 window.scrollTo(0, document.body.scrollHeight); 197 } 198 199 /** 200 * Fires when the off-canvas menu opens. 201 * @event OffCanvas#opened 202 */ 203 _this.$element.addClass('is-open'); 204 205 this.$triggers.attr('aria-expanded', 'true'); 206 this.$element.attr('aria-hidden', 'false').trigger('opened.zf.offcanvas'); 207 208 // If `contentScroll` is set to false, add class and disable scrolling on touch devices. 209 if (this.options.contentScroll === false) { 210 $('body').addClass('is-off-canvas-open').on('touchmove', this._stopScrolling); 211 } 212 213 if (this.options.contentOverlay === true) { 214 this.$overlay.addClass('is-visible'); 215 } 216 217 if (this.options.closeOnClick === true && this.options.contentOverlay === true) { 218 this.$overlay.addClass('is-closable'); 219 } 220 221 if (this.options.autoFocus === true) { 222 this.$element.one(Foundation.transitionend(this.$element), function () { 223 var canvasFocus = _this.$element.find('[data-autofocus]'); 224 if (canvasFocus.length) { 225 canvasFocus.eq(0).focus(); 226 } else { 227 _this.$element.find('a, button').eq(0).focus(); 228 } 229 }); 230 } 231 232 if (this.options.trapFocus === true) { 233 this.$element.siblings('[data-off-canvas-content]').attr('tabindex', '-1'); 234 Foundation.Keyboard.trapFocus(this.$element); 235 } 236 } 237 238 /** 239 * Closes the off-canvas menu. 240 * @function 241 * @param {Function} cb - optional cb to fire after closure. 242 * @fires OffCanvas#closed 243 */ 244 245 }, {
246 key: 'close', 247 value: function close(cb) { 248 if (!this.$element.hasClass('is-open') || this.isRevealed) { 249 return; 250 } 251 252 var _this = this; 253 254 _this.$element.removeClass('is-open'); 255 256 this.$element.attr('aria-hidden', 'true') 257 /** 258 * Fires when the off-canvas menu opens. 259 * @event OffCanvas#closed 260 */ 261 .trigger('closed.zf.offcanvas'); 262 263 // If `contentScroll` is set to false, remove class and re-enable scrolling on touch devices. 264 if (this.options.contentScroll === false) { 265 $('body').removeClass('is-off-canvas-open').off('touchmove', this._stopScrolling); 266 } 267 268 if (this.options.contentOverlay === true) { 269 this.$overlay.removeClass('is-visible'); 270 } 271 272 if (this.options.closeOnClick === true && this.options.contentOverlay === true) { 273 this.$overlay.removeClass('is-closable'); 274 } 275 276 this.$triggers.attr('aria-expanded', 'false'); 277 278 if (this.options.trapFocus === true) { 279 this.$element.siblings('[data-off-canvas-content]').removeAttr('tabindex'); 280 Foundation.Keyboard.releaseFocus(this.$element); 281 } 282 } 283 284 /** 285 * Toggles the off-canvas menu open or closed. 286 * @function 287 * @param {Object} event - Event object passed from listener. 288 * @param {jQuery} trigger - element that triggered the off-canvas to open. 289 */ 290 291 }, { 292 key: 'toggle', 293 value: function toggle(event, trigger) { 294 if (this.$element.hasClass('is-open')) { 295 this.close(event, trigger); 296 } else { 297 this.open(event, trigger); 298 } 299 } 300 301 /** 302 * Handles keyboard input when detected. When the escape key is pressed, the off-canvas menu closes, and focus is restored to the element that opened the menu. 303 * @function 304 * @private 305 */ 306 307 }, { 308 key: '_handleKeyboard', 309 value: function _handleKeyboard(e) { 310 var _this2 = this; 311 312 Foundation.Keyboard.handleKey(e, 'OffCanvas', { 313 close: function close() { 314 _this2.close(); 315 _this2.$lastTrigger.focus(); 316 return true; 317 }, 318 handled: function handled() { 319 e.stopPropagation(); 320 e.preventDefault(); 321 } 322 }); 323 } 324 325 /** 326 * Destroys the offcanvas plugin. 327 * @function 328 */ 329 330 }, { 331 key: 'destroy', 332 value: function destroy() { 333 this.close(); 334 this.$element.off('.zf.trigger .zf.offcanvas'); 335 this.$overlay.off('.zf.offcanvas'); 336 337 Foundation.unregisterPlugin(this); 338 } 339 }]); 340 341 return OffCanvas; 342 }(); 343 344 OffCanvas.defaults = { 345 /** 346 * Allow the user to click outside of the menu to close it. 347 * @option 348 * @type {boolean} 349 * @default true 350 */ 351 closeOnClick: true, 352 353 /** 354 * Adds an overlay on top of `[data-off-canvas-content]`. 355 * @option 356 * @type {boolean} 357 * @default true 358 */ 359 contentOverlay: true, 360 361 /** 362 * Enable/disable scrolling of the main content when an off canvas panel is open. 363 * @option 364 * @type {boolean} 365 * @default true 366 */ 367 contentScroll: true, 368 369 /** 370 * Amount of time in ms the open and close transition requires. If none selected, pulls from body style. 371 * @option 372 * @type {number} 373 * @default 0 374 */ 375 transitionTime: 0, 376 377 /** 378 * Type of transition for the offcanvas menu. Options are 'push', 'detached' or 'slide'. 379 * @option 380 * @type {string} 381 * @default push 382 */ 383 transition: 'push', 384 385 /** 386 * Force the page to scroll to top or bottom on open. 387 * @option 388 * @type {?string} 389 * @default null 390 */ 391 forceTo: null, 392 393 /** 394 * Allow the offcanvas to remain open for certain breakpoints. 395 * @option 396 * @type {boolean} 397 * @default false 398 */ 399 isRevealed: false, 400 401 /** 402 * Breakpoint at which to reveal. JS will use a RegExp to target standard classes, if changing classnames, pass your class with the `revealClass` option. 403 * @option 404 * @type {?string} 405 * @default null 406 */ 407 revealOn: null, 408 409 /** 410 * Force focus to the offcanvas on open. If true, will focus the opening trigger on close. 411 * @option 412 * @type {boolean} 413 * @default true 414 */ 415 autoFocus: true, 416 417 /** 418 * Class used to force an offcanvas to remain open. Foundation defaults for this are `reveal-for-large` & `reveal-for-medium`. 419 * @option 420 * @type {string} 421 * @default reveal-for- 422 * @todo improve the regex testing for this. 423 */ 424 revealClass: 'reveal-for-', 425 426 /** 427 * Triggers optional focus trapping when opening an offcanvas. Sets tabindex of [data-off-canvas-content] to -1 for accessibility purposes. 428 * @option 429 * @type {boolean} 430 * @default false 431 */ 432 trapFocus: false 433 }; 434 435 // Window exports 436 Foundation.plugin(OffCanvas, 'OffCanvas'); 437}(jQuery);
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.