+++ /dev/null
-/**
- * Reveal module.
- * @module foundation.reveal
- * @requires foundation.util.keyboard
- * @requires foundation.util.box
- * @requires foundation.util.triggers
- * @requires foundation.util.mediaQuery
- * @requires foundation.util.motion if using animations
- */
-!function(Foundation, $) {
- 'use strict';
-
- /**
- * Creates a new instance of Reveal.
- * @class
- * @param {jQuery} element - jQuery object to use for the modal.
- * @param {Object} options - optional parameters.
- */
-
- function Reveal(element, options) {
- this.$element = element;
- this.options = $.extend({}, Reveal.defaults, this.$element.data(), options);
- this._init();
-
- Foundation.registerPlugin(this, 'Reveal');
- Foundation.Keyboard.register('Reveal', {
- 'ENTER': 'open',
- 'SPACE': 'open',
- 'ESCAPE': 'close',
- 'TAB': 'tab_forward',
- 'SHIFT_TAB': 'tab_backward'
- });
- }
-
- Reveal.defaults = {
- /**
- * Motion-UI class to use for animated elements. If none used, defaults to simple show/hide.
- * @option
- * @example 'slide-in-left'
- */
- animationIn: '',
- /**
- * Motion-UI class to use for animated elements. If none used, defaults to simple show/hide.
- * @option
- * @example 'slide-out-right'
- */
- animationOut: '',
- /**
- * Time, in ms, to delay the opening of a modal after a click if no animation used.
- * @option
- * @example 10
- */
- showDelay: 0,
- /**
- * Time, in ms, to delay the closing of a modal after a click if no animation used.
- * @option
- * @example 10
- */
- hideDelay: 0,
- /**
- * Allows a click on the body/overlay to close the modal.
- * @option
- * @example true
- */
- closeOnClick: true,
- /**
- * Allows the modal to close if the user presses the `ESCAPE` key.
- * @option
- * @example true
- */
- closeOnEsc: true,
- /**
- * If true, allows multiple modals to be displayed at once.
- * @option
- * @example false
- */
- multipleOpened: false,
- /**
- * Distance, in pixels, the modal should push down from the top of the screen.
- * @option
- * @example 100
- */
- vOffset: 100,
- /**
- * Distance, in pixels, the modal should push in from the side of the screen.
- * @option
- * @example 0
- */
- hOffset: 0,
- /**
- * Allows the modal to be fullscreen, completely blocking out the rest of the view. JS checks for this as well.
- * @option
- * @example false
- */
- fullScreen: false,
- /**
- * Percentage of screen height the modal should push up from the bottom of the view.
- * @option
- * @example 10
- */
- btmOffsetPct: 10,
- /**
- * Allows the modal to generate an overlay div, which will cover the view when modal opens.
- * @option
- * @example true
- */
- overlay: true,
- /**
- * Allows the modal to remove and reinject markup on close. Should be true if using video elements w/o using provider's api, otherwise, videos will continue to play in the background.
- * @option
- * @example false
- */
- resetOnClose: false,
- /**
- * Allows the modal to alter the url on open/close, and allows the use of the `back` button to close modals. ALSO, allows a modal to auto-maniacally open on page load IF the hash === the modal's user-set id.
- * @option
- * @example false
- */
- deepLink: false
- };
-
- /**
- * Initializes the modal by adding the overlay and close buttons, (if selected).
- * @private
- */
- Reveal.prototype._init = function(){
- this.id = this.$element.attr('id');
- this.isActive = false;
- this.cached = {mq: Foundation.MediaQuery.current};
- this.isiOS = iPhoneSniff();
- console.log(this.isiOS);
-
- if(this.isiOS){ this.$element.addClass('is-ios'); }
-
- this.$anchor = $('[data-open="' + this.id + '"]').length ? $('[data-open="' + this.id + '"]') : $('[data-toggle="' + this.id + '"]');
-
-
- if(this.$anchor.length){
- var anchorId = this.$anchor[0].id || Foundation.GetYoDigits(6, 'reveal');
-
- this.$anchor.attr({
- 'aria-controls': this.id,
- 'id': anchorId,
- 'aria-haspopup': true,
- 'tabindex': 0
- });
- this.$element.attr({'aria-labelledby': anchorId});
- }
-
- // this.options.fullScreen = this.$element.hasClass('full');
- if(this.options.fullScreen || this.$element.hasClass('full')){
- this.options.fullScreen = true;
- this.options.overlay = false;
- }
- if(this.options.overlay && !this.$overlay){
- this.$overlay = this._makeOverlay(this.id);
- }
-
- this.$element.attr({
- 'role': 'dialog',
- 'aria-hidden': true,
- 'data-yeti-box': this.id,
- 'data-resize': this.id
- });
-
- this._events();
- if(this.options.deepLink && window.location.hash === ( '#' + this.id)){
- $(window).one('load.zf.reveal', this.open.bind(this));
- }
- };
-
- /**
- * Creates an overlay div to display behind the modal.
- * @private
- */
- Reveal.prototype._makeOverlay = function(id){
- var $overlay = $('<div></div>')
- .addClass('reveal-overlay')
- .attr({'tabindex': -1, 'aria-hidden': true})
- .appendTo('body');
- if(this.options.closeOnClick){
- $overlay.attr({
- 'data-close': id
- });
- }
- return $overlay;
- };
-
- /**
- * Adds event handlers for the modal.
- * @private
- */
- Reveal.prototype._events = function(){
- var _this = this;
-
- this.$element.on({
- 'open.zf.trigger': this.open.bind(this),
- 'close.zf.trigger': this.close.bind(this),
- 'toggle.zf.trigger': this.toggle.bind(this),
- 'resizeme.zf.trigger': function(){
- _this.updateVals = true;
- if(_this.isActive){
- _this._setPosition();
- }
- }
- });
-
- if(this.$anchor.length){
- this.$anchor.on('keydown.zf.reveal', function(e){
- if(e.which === 13 || e.which === 32){
- e.stopPropagation();
- e.preventDefault();
- _this.open();
- }
- });
- }
-
- if(this.options.closeOnClick && this.options.overlay){
- this.$overlay.off('.zf.reveal').on('click.zf.reveal', this.close.bind(this));
- }
- if(this.options.deepLink){
- $(window).on('popstate.zf.reveal:' + this.id, this._handleState.bind(this));
- }
- };
- /**
- * Handles modal methods on back/forward button clicks or any other event that triggers popstate.
- * @private
- */
- Reveal.prototype._handleState = function(e){
- if(window.location.hash === ( '#' + this.id) && !this.isActive){ this.open(); }
- else{ this.close(); }
- };
- Reveal.prototype._cacheValues = function(){
- if(this.cached.mq !== Foundation.MediaQuery.current || this.$offsetParent === undefined){
- this.$offsetParent = this.$element.offsetParent();
- this.cached.mq = Foundation.MediaQuery.current;
- }
-
- this.cached.parentOffset = this.$offsetParent.offset();
- this.cached.modalDims = this.$element[0].getBoundingClientRect();
- this.cached.winWidth = window.innerWidth;
- this.cached.vertOffset = window.innerHeight > this.cached.modalDims.height ? this.options.vOffset : 0;
-
- this.updateVals = false;
- return;
- };
-
- /**
- * Sets the position of the modal before opening
- * @param {Function} cb - a callback function to execute when positioning is complete.
- * @private
- */
- Reveal.prototype._setPosition = function(cb){
- if(!this.cached.winWidth || this.updateVals){ this._cacheValues(); }
-
- var x = Math.round((this.cached.winWidth - this.cached.modalDims.width) / 2 - (this.cached.parentOffset.left > 0 ? this.cached.parentOffset.left : 0)),
- y = Math.round(window.pageYOffset - (this.cached.parentOffset.top > 0 ? this.cached.parentOffset.top : 0) + this.cached.vertOffset);
- // this.$element.css('transform', 'translate(' + x + 'px, ' + y + 'px)');
- // this.$element.css({top: y + 'px', left: x + 'px'});
- this.$element.css(this._applyCss(x, y));
-
- if(cb) cb();
- };
- Reveal.prototype._applyCss = (function(x, y){
- var _this = this;
- return (_this.options.animationIn ?
- {top: y + 'px', left: x + 'px'}
- : {transform: 'translate(' + x + 'px, ' + y + 'px)'}
- );
- });
- /**
- * Opens the modal controlled by `this.$anchor`, and closes all others by default.
- * @function
- * @fires Reveal#closeme
- * @fires Reveal#open
- */
- Reveal.prototype.open = function(){
- if(this.options.deepLink){
- var hash = '#' + this.id;
-
- if(window.history.pushState){
- window.history.pushState(null, null, hash);
- }else{
- window.location.hash = hash;
- }
- }
-
- var _this = this;
- this.isActive = true;
- //make element invisible, but remove display: none so we can get size and positioning
- this.$element
- .css({'visibility': 'hidden'})
- .show()
- .scrollTop(0);
-
- this._setPosition(function(){
- _this.$element.hide()
- .css({'visibility': ''});
- if(!_this.options.multipleOpened){
- /**
- * Fires immediately before the modal opens.
- * Closes any other modals that are currently open
- * @event Reveal#closeme
- */
- _this.$element.trigger('closeme.zf.reveal', _this.id);
- }
- if(_this.options.animationIn){
- if(_this.options.overlay){
- Foundation.Motion.animateIn(_this.$overlay, 'fade-in', function(){
- Foundation.Motion.animateIn(_this.$element, _this.options.animationIn, function(){
- _this.focusableElements = Foundation.Keyboard.findFocusable(_this.$element);
- });
- });
- }else{
- Foundation.Motion.animateIn(_this.$element, _this.options.animationIn, function(){
- _this.focusableElements = Foundation.Keyboard.findFocusable(_this.$element);
- });
- }
- }else{
- if(_this.options.overlay){
- _this.$overlay.show(0, function(){
- _this.$element.show(_this.options.showDelay, function(){
- });
- });
- }else{
- _this.$element.show(_this.options.showDelay, function(){
- });
- }
- }
- });
-
-
- // handle accessibility
- this.$element.attr({'aria-hidden': false}).attr('tabindex', -1).focus()
- /**
- * Fires when the modal has successfully opened.
- * @event Reveal#open
- */
- .trigger('open.zf.reveal');
- if(this.isiOS){
- var scrollPos = window.pageYOffset;
- $('html, body').addClass('is-reveal-open').scrollTop(scrollPos);
- // $('body').scrollTop(scrollPos);
- }else{
- $('body').addClass('is-reveal-open');
- }
-
- $('body').attr({'aria-hidden': (this.options.overlay || this.options.fullScreen) ? true : false});
- setTimeout(function(){
- _this._extraHandlers();
- }, 0);
- };
-
- /**
- * Adds extra event handlers for the body and window if necessary.
- * @private
- */
- Reveal.prototype._extraHandlers = function(){
- var _this = this;
- this.focusableElements = Foundation.Keyboard.findFocusable(this.$element);
-
- if(!this.options.overlay && this.options.closeOnClick && !this.options.fullScreen){
- $('body').on('click.zf.reveal', function(e){
- if(e.target === _this.$element[0] || $.contains(_this.$element[0], e.target)){ return; }
- _this.close();
- });
- }
- if(this.options.closeOnEsc){
- $(window).on('keydown.zf.reveal', function(e){
- Foundation.Keyboard.handleKey(e, 'Reveal', {
- close: function() {
- if (_this.options.closeOnEsc) {
- _this.close();
- _this.$anchor.focus();
- }
- }
- });
- if (_this.focusableElements.length === 0) { // no focusable elements inside the modal at all, prevent tabbing in general
- e.preventDefault();
- }
- });
- }
-
- // lock focus within modal while tabbing
- this.$element.on('keydown.zf.reveal', function(e) {
- var $target = $(this);
- // handle keyboard event with keyboard util
- Foundation.Keyboard.handleKey(e, 'Reveal', {
- tab_forward: function() {
- if (_this.$element.find(':focus').is(_this.focusableElements.eq(-1))) { // left modal downwards, setting focus to first element
- _this.focusableElements.eq(0).focus();
- e.preventDefault();
- }
- },
- tab_backward: function() {
- if (_this.$element.find(':focus').is(_this.focusableElements.eq(0)) || _this.$element.is(':focus')) { // left modal upwards, setting focus to last element
- _this.focusableElements.eq(-1).focus();
- e.preventDefault();
- }
- },
- open: function() {
- if (_this.$element.find(':focus').is(_this.$element.find('[data-close]'))) {
- setTimeout(function() { // set focus back to anchor if close button has been activated
- _this.$anchor.focus();
- }, 1);
- } else if ($target.is(_this.focusableElements)) { // dont't trigger if acual element has focus (i.e. inputs, links, ...)
- _this.open();
- }
- },
- close: function() {
- if (_this.options.closeOnEsc) {
- _this.close();
- _this.$anchor.focus();
- }
- }
- });
- });
-
- };
-
- /**
- * Closes the modal.
- * @function
- * @fires Reveal#closed
- */
- Reveal.prototype.close = function(){
- if(!this.isActive || !this.$element.is(':visible')){
- return false;
- }
- var _this = this;
-
- if(this.options.animationOut){
- Foundation.Motion.animateOut(this.$element, this.options.animationOut, function(){
- if(_this.options.overlay){
- Foundation.Motion.animateOut(_this.$overlay, 'fade-out', finishUp);
- }else{ finishUp(); }
- });
- }else{
- this.$element.hide(_this.options.hideDelay, function(){
- if(_this.options.overlay){
- _this.$overlay.hide(0, finishUp);
- }else{ finishUp(); }
- });
- }
- //conditionals to remove extra event listeners added on open
- if(this.options.closeOnEsc){
- $(window).off('keydown.zf.reveal');
- }
- if(!this.options.overlay && this.options.closeOnClick){
- $('body').off('click.zf.reveal');
- }
- this.$element.off('keydown.zf.reveal');
-
- function finishUp(){
- //if the modal changed size, reset it
- // if(_this.changedSize){
- // _this.$element.css({
- // 'height': '',
- // 'width': ''
- // });
- // }
-
- if(_this.isiOS){
- $('html, body').removeClass('is-reveal-open');
- }else{
- $('body').removeClass('is-reveal-open');
- }
-
- $('body').attr({'aria-hidden': false, 'tabindex': ''});
- _this.$element.attr({'aria-hidden': true})
- /**
- * Fires when the modal is done closing.
- * @event Reveal#closed
- */
- .trigger('closed.zf.reveal');
- }
-
-
- /**
- * Resets the modal content
- * This prevents a running video to keep going in the background
- */
- // if(this.options.resetOnClose) {
- // this.$element.html(this.$element.html());
- // }
-
- this.isActive = false;
- if(_this.options.deepLink){
- if(window.history.replaceState){
- window.history.replaceState("", document.title, window.location.pathname);
- }else{
- window.location.hash = '';
- }
- }
- };
- /**
- * Toggles the open/closed state of a modal.
- * @function
- */
- Reveal.prototype.toggle = function(){
- if(this.isActive){
- this.close();
- }else{
- this.open();
- }
- };
-
- /**
- * Destroys an instance of a modal.
- * @function
- */
- Reveal.prototype.destroy = function() {
- if(this.options.overlay){
- this.$overlay.hide().off().remove();
- }
- this.$element.hide().off();
- this.$anchor.off('.zf');
- $(window).off('.zf.reveal:' + this.id);
-
- Foundation.unregisterPlugin(this);
- };
-
- Foundation.plugin(Reveal, 'Reveal');
-
- function iPhoneSniff(){
- return /iP(ad|hone|od).*OS/.test(window.navigator.userAgent);
- }
-
- // Exports for AMD/Browserify
- if (typeof module !== 'undefined' && typeof module.exports !== 'undefined')
- module.exports = Reveal;
- if (typeof define === 'function')
- define(['foundation'], function() {
- return Reveal;
- });
-
-}(Foundation, jQuery);
Reveal.prototype._init = function(){
this.id = this.$element.attr('id');
this.isActive = false;
+ this.cached = {mq: Foundation.MediaQuery.current};
+ this.isiOS = iPhoneSniff();
+ console.log(this.isiOS);
+
+ if(this.isiOS){ this.$element.addClass('is-ios'); }
this.$anchor = $('[data-open="' + this.id + '"]').length ? $('[data-open="' + this.id + '"]') : $('[data-toggle="' + this.id + '"]');
+
if(this.$anchor.length){
var anchorId = this.$anchor[0].id || Foundation.GetYoDigits(6, 'reveal');
this.$element.attr({'aria-labelledby': anchorId});
}
- // this.options.fullScreen = this.$element.hasClass('full');
+
if(this.options.fullScreen || this.$element.hasClass('full')){
this.options.fullScreen = true;
this.options.overlay = false;
'close.zf.trigger': this.close.bind(this),
'toggle.zf.trigger': this.toggle.bind(this),
'resizeme.zf.trigger': function(){
- if(_this.$element.is(':visible')){
- _this._setPosition(function(){});
+ _this.updateVals = true;
+ if(_this.isActive){
+ _this._setPosition();
}
}
});
});
}
-
if(this.options.closeOnClick && this.options.overlay){
this.$overlay.off('.zf.reveal').on('click.zf.reveal', this.close.bind(this));
}
if(window.location.hash === ( '#' + this.id) && !this.isActive){ this.open(); }
else{ this.close(); }
};
+ Reveal.prototype._cacheValues = function(){
+ if(this.cached.mq !== Foundation.MediaQuery.current || this.$offsetParent === undefined){
+ this.$offsetParent = this.$element.offsetParent();
+ this.cached.mq = Foundation.MediaQuery.current;
+ }
+
+ this.cached.parentOffset = this.$offsetParent.offset();
+ this.cached.modalDims = this.$element[0].getBoundingClientRect();
+ this.cached.winWidth = window.innerWidth;
+ this.cached.vertOffset = window.innerHeight > this.cached.modalDims.height ? this.options.vOffset : 0;
+
+ this.updateVals = false;
+ return;
+ };
+
/**
* Sets the position of the modal before opening
* @param {Function} cb - a callback function to execute when positioning is complete.
* @private
*/
Reveal.prototype._setPosition = function(cb){
- var eleDims = Foundation.Box.GetDimensions(this.$element);
- var elePos = this.options.fullScreen ? 'reveal full' : (eleDims.height >= (0.5 * eleDims.windowDims.height)) ? 'reveal' : 'center';
-
- if(elePos === 'reveal full'){
- //set to full height/width
- this.$element
- .offset(Foundation.Box.GetOffsets(this.$element, null, elePos, this.options.vOffset))
- .css({
- 'height': eleDims.windowDims.height,
- 'width': eleDims.windowDims.width
- });
- }else if(!Foundation.MediaQuery.atLeast('medium') || !Foundation.Box.ImNotTouchingYou(this.$element, null, true, false)){
- //if smaller than medium, resize to 100% width minus any custom L/R margin
- this.$element
- .css({
- 'width': eleDims.windowDims.width - (this.options.hOffset * 2)
- })
- .offset(Foundation.Box.GetOffsets(this.$element, null, 'center', this.options.vOffset, this.options.hOffset));
- //flag a boolean so we can reset the size after the element is closed.
- this.changedSize = true;
- }else{
- this.$element
- .css({
- 'max-height': eleDims.windowDims.height - (this.options.vOffset * (this.options.btmOffsetPct / 100 + 1)),
- 'width': ''
- })
- .offset(Foundation.Box.GetOffsets(this.$element, null, elePos, this.options.vOffset));
- //the max height based on a percentage of vertical offset plus vertical offset
- }
+ if(!this.cached.winWidth || this.updateVals){ this._cacheValues(); }
- cb();
- };
+ var x = Math.round((this.cached.winWidth - this.cached.modalDims.width) / 2 - (this.cached.parentOffset.left > 0 ? this.cached.parentOffset.left : 0)),
+ y = Math.round(window.pageYOffset - (this.cached.parentOffset.top > 0 ? this.cached.parentOffset.top : 0) + this.cached.vertOffset);
+
+ this.$element.css(this._applyCss(x, y));
+ if(cb) cb();
+ };
+ Reveal.prototype._applyCss = (function(x, y){
+ var _this = this;
+ return (_this.options.animationIn ?
+ {top: y + 'px', left: x + 'px'}
+ : {transform: 'translate(' + x + 'px, ' + y + 'px)'}
+ );
+ });
/**
* Opens the modal controlled by `this.$anchor`, and closes all others by default.
* @function
* @event Reveal#open
*/
.trigger('open.zf.reveal');
+ if(this.isiOS){
+ var scrollPos = window.pageYOffset;
+ $('html, body').addClass('is-reveal-open').scrollTop(scrollPos);
+ }else{
+ $('body').addClass('is-reveal-open');
+ }
- $('body').addClass('is-reveal-open')
- .attr({'aria-hidden': (this.options.overlay || this.options.fullScreen) ? true : false});
+ $('body').attr({'aria-hidden': (this.options.overlay || this.options.fullScreen) ? true : false});
setTimeout(function(){
_this._extraHandlers();
}, 0);
$('body').off('click.zf.reveal');
}
this.$element.off('keydown.zf.reveal');
+
function finishUp(){
- //if the modal changed size, reset it
- if(_this.changedSize){
- _this.$element.css({
- 'height': '',
- 'width': ''
- });
+ if(_this.isiOS){
+ $('html, body').removeClass('is-reveal-open');
+ }else{
+ $('body').removeClass('is-reveal-open');
}
- $('body').removeClass('is-reveal-open').attr({'aria-hidden': false, 'tabindex': ''});
+
+ $('body').attr({'aria-hidden': false, 'tabindex': ''});
_this.$element.attr({'aria-hidden': true})
/**
* Fires when the modal is done closing.
Foundation.unregisterPlugin(this);
};
- // Foundation.plugin(Reveal, 'Reveal');
+ Foundation.plugin(Reveal, 'Reveal');
+
+ function iPhoneSniff(){
+ return /iP(ad|hone|od).*OS/.test(window.navigator.userAgent);
+ }
// Exports for AMD/Browserify
if (typeof module !== 'undefined' && typeof module.exports !== 'undefined')