diff --git a/README.md b/README.md
index 656fedc..fb84d8f 100644
--- a/README.md
+++ b/README.md
@@ -111,6 +111,7 @@ close_aria_label|`string`|`Close (Press escape to close)`||String for close butt
width|`integer`|null||Set the desired width of the modal.
height|`integer`|null||Set the desired height of the modal.
gallery_active_class|`string`|`gallery_active_item`||Active class applied to the currently active image or image slide in a gallery
+loop_gallery|`boolean`|`false`|`true` `false`|Set to true to allow the gallery to loop between the last and first image slides.
outer_controls|`boolean`|`false`|`true` `false`|Set to true to put the next/prev controls outside the Modaal wrapper, at the edges of the browser window.
confirm_button_text|`string`|`Confirm`||Text on the confirm button.
confirm_cancel_button_text|`string`|`Cancel`||Text on the confirm modal cancel button.
diff --git a/dist/js/modaal.js b/dist/js/modaal.js
index 646cbc2..bbffcda 100644
--- a/dist/js/modaal.js
+++ b/dist/js/modaal.js
@@ -65,6 +65,7 @@
=== Gallery Options & Events ===
gallery_active_class (string) : Active class applied to the currently active image or image slide in a gallery 'gallery_active_item'
+ loop_gallery (boolean) : Set to true to allow the gallery to loop between the last and first image slides.
outer_controls (boolean) : Set to true to put the next/prev controls outside the Modaal wrapper, at the edges of the browser window.
before_image_change (function) : Callback function executed before the image slide changes in a gallery modal. Default function( current_item, incoming_item )
after_image_change (function) : Callback function executed after the image slide changes in a gallery modal. Default function ( current_item )
@@ -378,7 +379,7 @@
}
// add the guts of the content
- build_markup += '
';
+ build_markup += '
';
// If it's inline type, we want to clone content instead of dropping it straight in
if (self.options.type == 'inline') {
@@ -573,6 +574,7 @@
create_image : function() {
var self = this;
var content;
+ var loop_gallery = self.options.loop_gallery;
var modaal_image_markup = '';
var gallery_total;
@@ -721,10 +723,10 @@
self.build_modal(content);
// setup next & prev buttons
- if ( $('.modaal-gallery-item.is_active').is('.gallery-item-0') ) {
+ if ( !loop_gallery && $('.modaal-gallery-item.is_active').is('.gallery-item-0') ) {
$('.modaal-gallery-prev').hide();
}
- if ( $('.modaal-gallery-item.is_active').is('.gallery-item-' + gallery_total) ) {
+ if ( !loop_gallery && $('.modaal-gallery-item.is_active').is('.gallery-item-' + gallery_total) ) {
$('.modaal-gallery-next').hide();
}
},
@@ -736,6 +738,7 @@
var this_gallery = $('#' + self.scope.id);
var this_gallery_item = this_gallery.find('.modaal-gallery-item');
var this_gallery_total = this_gallery_item.length - 1;
+ var loop_gallery = self.options.loop_gallery;
// if single item, don't proceed
if ( this_gallery_total == 0 ) {
@@ -752,16 +755,36 @@
// CB: Before image change
var current_item = this_gallery.find( '.modaal-gallery-item.' + self.private_options.active_class ),
- incoming_item = ( direction == 'next' ? current_item.next( '.modaal-gallery-item' ) : current_item.prev( '.modaal-gallery-item' ) );
- self.options.before_image_change.call(self, current_item, incoming_item);
-
- // stop change if at start of end
- if ( direction == 'prev' && this_gallery.find('.gallery-item-0').hasClass('is_active') ) {
- return false;
- } else if ( direction == 'next' && this_gallery.find('.gallery-item-' + this_gallery_total).hasClass('is_active') ) {
- return false;
+ incoming_item;
+
+ if ( direction == 'prev') {
+ // If it's the first item
+ if (this_gallery.find('.gallery-item-0').hasClass('is_active')) {
+ // If looping is set then move to the last slide
+ if (loop_gallery) {
+ incoming_item = current_item.parent().find('> div:last-child');
+ } else {
+ return false;
+ }
+ } else {
+ incoming_item = current_item.prev( '.modaal-gallery-item' );
+ }
+ } else if ( direction == 'next') {
+ // If it's the last item
+ if (this_gallery.find('.gallery-item-' + this_gallery_total).hasClass('is_active')) {
+ // If looping is set then move to the first slide
+ if (loop_gallery) {
+ incoming_item = current_item.parent().find('> div:first-child');
+ } else {
+ return false;
+ }
+ } else {
+ incoming_item = current_item.next( '.modaal-gallery-item' );
+ }
}
+ self.options.before_image_change.call(self, current_item, incoming_item);
+
// lock dimensions
current_item.stop().animate({
@@ -835,7 +858,7 @@
this_gallery.find('.modaal-gallery-item.' + self.private_options.active_class + '').attr('tabindex', '0').focus();
// hide/show next/prev
- if ( this_gallery.find('.modaal-gallery-item.' + self.private_options.active_class).is('.gallery-item-0') ) {
+ if ( !loop_gallery && this_gallery.find('.modaal-gallery-item.' + self.private_options.active_class).is('.gallery-item-0') ) {
prev_btn.stop().animate({
opacity: 0
}, 150, function(){
@@ -849,7 +872,7 @@
opacity: 1
}, 150);
}
- if ( this_gallery.find('.modaal-gallery-item.' + self.private_options.active_class).is('.gallery-item-' + this_gallery_total) ) {
+ if ( !loop_gallery && this_gallery.find('.modaal-gallery-item.' + self.private_options.active_class).is('.gallery-item-' + this_gallery_total) ) {
next_btn.stop().animate({
opacity: 0
}, 150, function(){
@@ -940,6 +963,15 @@
// now set the focus
focusTarget.attr('tabindex', '0').focus();
+
+ // iOS Fix for VoiceOver.
+ // Unfortunately this timeout appears to be the only consistent workaround for shifting focus in VoiceOver for conten that shows and hides.
+ // Wrapped in User Agent check to restrict it's application to only iphone, ipad and ipod
+ if ( self.is_ios() ) {
+ setTimeout(function() {
+ focusTarget.find('[role="document"] >:first-child').focus();
+ }, 1000);
+ }
// Run after_open
if (animation_type !== 'none') {
@@ -1001,6 +1033,17 @@
if (self.lastFocus != null) {
self.lastFocus.focus();
}
+
+ // iOS Fix for VoiceOver.
+ // Unfortunately this timeout appears to be the only consistent workaround for shifting focus in VoiceOver for conten that shows and hides.
+ // Wrapped in User Agent check to restrict it's application to only iphone, ipad and ipod
+ if ( self.is_ios() ) {
+ setTimeout(function() {
+ if (self.lastFocus != null) {
+ self.lastFocus.focus();
+ }
+ }, 1000);
+ }
},
// Overlay control (accepts action for show or hide)
@@ -1049,6 +1092,17 @@
// ----------------------------------------------------------------
is_touch : function() {
return 'ontouchstart' in window || navigator.maxTouchPoints;
+ },
+
+ // Check if is iOS - this is necessary for focus handling on iOS VoiceOver where a delay is required to shift focus successfully.
+ // ----------------------------------------------------------------
+ is_ios : function() {
+ var ua = navigator.userAgent;
+ if ( ua.match(/iPhone/i) || ua.match(/iPad/i) || ua.match(/iPod/i) ) {
+ return true;
+ } else {
+ return false;
+ }
}
};
@@ -1135,6 +1189,7 @@
//Gallery Modal
gallery_active_class: 'gallery_active_item',
+ loop_gallery: false,
outer_controls: false,
before_image_change: function( current_item, incoming_item ) {},
after_image_change: function( current_item ) {},
@@ -1301,6 +1356,12 @@
options.gallery_active_class = self.attr('data-modaal-gallery-active-class');
}
+ // option: loop_gallery
+ if ( self.attr('data-modaal-loop-gallery') ) {
+ inline_options = true;
+ options.loop_gallery = self.attr('data-modaal-loop-gallery');
+ }
+
// option: loading_content
if ( self.attr('data-modaal-loading-content') ) {
inline_options = true;
diff --git a/dist/js/modaal.min.js b/dist/js/modaal.min.js
index 4a56051..e7e84b3 100644
--- a/dist/js/modaal.min.js
+++ b/dist/js/modaal.min.js
@@ -3,4 +3,4 @@
by Humaan, for all humans.
http://humaan.com
*/
-!function(a){function t(a){var t={},o=!1;a.attr("data-modaal-type")&&(o=!0,t.type=a.attr("data-modaal-type")),a.attr("data-modaal-content-source")&&(o=!0,t.content_source=a.attr("data-modaal-content-source")),a.attr("data-modaal-animation")&&(o=!0,t.animation=a.attr("data-modaal-animation")),a.attr("data-modaal-animation-speed")&&(o=!0,t.animation_speed=a.attr("data-modaal-animation-speed")),a.attr("data-modaal-after-callback-delay")&&(o=!0,t.after_callback_delay=a.attr("data-modaal-after-callback-delay")),a.attr("data-modaal-is-locked")&&(o=!0,t.is_locked="true"===a.attr("data-modaal-is-locked")),a.attr("data-modaal-hide-close")&&(o=!0,t.hide_close="true"===a.attr("data-modaal-hide-close")),a.attr("data-modaal-background")&&(o=!0,t.background=a.attr("data-modaal-background")),a.attr("data-modaal-overlay-opacity")&&(o=!0,t.overlay_opacity=a.attr("data-modaal-overlay-opacity")),a.attr("data-modaal-overlay-close")&&(o=!0,t.overlay_close="false"!==a.attr("data-modaal-overlay-close")),a.attr("data-modaal-accessible-title")&&(o=!0,t.accessible_title=a.attr("data-modaal-accessible-title")),a.attr("data-modaal-start-open")&&(o=!0,t.start_open="true"===a.attr("data-modaal-start-open")),a.attr("data-modaal-fullscreen")&&(o=!0,t.fullscreen="true"===a.attr("data-modaal-fullscreen")),a.attr("data-modaal-custom-class")&&(o=!0,t.custom_class=a.attr("data-modaal-custom-class")),a.attr("data-modaal-close-text")&&(o=!0,t.close_text=a.attr("data-modaal-close-text")),a.attr("data-modaal-close-aria-label")&&(o=!0,t.close_aria_label=a.attr("data-modaal-close-aria-label")),a.attr("data-modaal-background-scroll")&&(o=!0,t.background_scroll="true"===a.attr("data-modaal-background-scroll")),a.attr("data-modaal-width")&&(o=!0,t.width=parseInt(a.attr("data-modaal-width"))),a.attr("data-modaal-height")&&(o=!0,t.height=parseInt(a.attr("data-modaal-height"))),a.attr("data-modaal-confirm-button-text")&&(o=!0,t.confirm_button_text=a.attr("data-modaal-confirm-button-text")),a.attr("data-modaal-confirm-cancel-button-text")&&(o=!0,t.confirm_cancel_button_text=a.attr("data-modaal-confirm-cancel-button-text")),a.attr("data-modaal-confirm-title")&&(o=!0,t.confirm_title=a.attr("data-modaal-confirm-title")),a.attr("data-modaal-confirm-content")&&(o=!0,t.confirm_content=a.attr("data-modaal-confirm-content")),a.attr("data-modaal-gallery-active-class")&&(o=!0,t.gallery_active_class=a.attr("data-modaal-gallery-active-class")),a.attr("data-modaal-loading-content")&&(o=!0,t.loading_content=a.attr("data-modaal-loading-content")),a.attr("data-modaal-loading-class")&&(o=!0,t.loading_class=a.attr("data-modaal-loading-class")),a.attr("data-modaal-ajax-error-class")&&(o=!0,t.ajax_error_class=a.attr("data-modaal-ajax-error-class")),a.attr("data-modaal-instagram-id")&&(o=!0,t.instagram_id=a.attr("data-modaal-instagram-id")),o&&a.modaal(t)}var o={init:function(t,o){var e=this;if(e.dom=a("body"),e.$elem=a(o),e.options=a.extend({},a.fn.modaal.options,e.$elem.data(),t),e.xhr=null,e.scope={is_open:!1,id:"modaal_"+(new Date).getTime()+Math.random().toString(16).substring(2),source:e.options.content_source?e.options.content_source:e.$elem.attr("href")},e.$elem.attr("data-modaal-scope",e.scope.id),e.private_options={active_class:"is_active"},e.lastFocus=null,e.options.is_locked||"confirm"==e.options.type||e.options.hide_close?e.scope.close_btn="":e.scope.close_btn='","none"===e.options.animation&&(e.options.animation_speed=0,e.options.after_callback_delay=0),a(o).on("click.Modaal",function(a){a.preventDefault(),e.create_modaal(e,a)}),!0===e.options.outer_controls)var i="outer";else var i="inner";e.scope.prev_btn='',e.scope.next_btn='',!0===e.options.start_open&&e.create_modaal(e)},create_modaal:function(a,t){var o,a=this;if(a.lastFocus=a.$elem,!1!==a.options.should_open&&("function"!=typeof a.options.should_open||!1!==a.options.should_open())){switch(a.options.before_open.call(a,t),a.options.type){case"inline":a.create_basic();break;case"ajax":o=a.options.source(a.$elem,a.scope.source),a.fetch_ajax(o);break;case"confirm":a.options.is_locked=!0,a.create_confirm();break;case"image":a.create_image();break;case"iframe":o=a.options.source(a.$elem,a.scope.source),a.create_iframe(o);break;case"video":a.create_video(a.scope.source);break;case"instagram":a.create_instagram()}a.watch_events()}},watch_events:function(){var t=this;t.dom.off("click.Modaal keyup.Modaal keydown.Modaal"),t.dom.on("keydown.Modaal",function(o){var e=o.keyCode,i=o.target;9==e&&t.scope.is_open&&(a.contains(document.getElementById(t.scope.id),i)||a("#"+t.scope.id).find('*[tabindex="0"]').focus())}),t.dom.on("keyup.Modaal",function(o){var e=o.keyCode,i=o.target;return o.shiftKey&&9==o.keyCode&&t.scope.is_open&&(a.contains(document.getElementById(t.scope.id),i)||a("#"+t.scope.id).find(".modaal-close").focus()),!t.options.is_locked&&27==e&&t.scope.is_open?!a(document.activeElement).is("input:not(:checkbox):not(:radio)")&&void t.modaal_close():"image"==t.options.type?(37==e&&t.scope.is_open&&!a("#"+t.scope.id+" .modaal-gallery-prev").hasClass("is_hidden")&&t.gallery_update("prev"),void(39==e&&t.scope.is_open&&!a("#"+t.scope.id+" .modaal-gallery-next").hasClass("is_hidden")&&t.gallery_update("next"))):void 0}),t.dom.on("click.Modaal",function(o){var e=a(o.target);if(!t.options.is_locked&&(t.options.overlay_close&&(e.is(".modaal-inner-wrapper")||"video"==t.options.type&&e.is(".modaal-video-wrap"))||e.is(".modaal-close")||e.closest(".modaal-close").length))return void t.modaal_close();if(e.is(".modaal-confirm-btn"))return e.is(".modaal-ok")&&t.options.confirm_callback.call(t,t.lastFocus),e.is(".modaal-cancel")&&t.options.confirm_cancel_callback.call(t,t.lastFocus),void t.modaal_close();if(e.is(".modaal-gallery-control")){if(e.hasClass("is_hidden"))return;return e.is(".modaal-gallery-prev")&&t.gallery_update("prev"),void(e.is(".modaal-gallery-next")&&t.gallery_update("next"))}})},build_modal:function(t){var o=this,e="";"instagram"==o.options.type&&(e=" modaal-instagram");var i,l="video"==o.options.type?"modaal-video-wrap":"modaal-content";switch(o.options.animation){case"fade":i=" modaal-start_fade";break;case"slide-down":i=" modaal-start_slidedown";break;default:i=" modaal-start_none"}var n="";o.options.fullscreen&&(n=" modaal-fullscreen"),""===o.options.custom_class&&void 0===o.options.custom_class||(o.options.custom_class=" "+o.options.custom_class);var s="";o.options.width&&o.options.height&&"number"==typeof o.options.width&&"number"==typeof o.options.height?s=' style="max-width:'+o.options.width+"px;height:"+o.options.height+'px;overflow:auto;"':o.options.width&&"number"==typeof o.options.width?s=' style="max-width:'+o.options.width+'px;"':o.options.height&&"number"==typeof o.options.height&&(s=' style="height:'+o.options.height+'px;overflow:auto;"'),("image"==o.options.type||"video"==o.options.type||"instagram"==o.options.type||o.options.fullscreen)&&(s="");var d="";o.is_touch()&&(d=' style="cursor:pointer;"');var r='
",a("#"+o.scope.id+"_overlay").length<1&&o.dom.append(r),"inline"==o.options.type&&t.appendTo("#"+o.scope.id+" .modaal-content-container"),o.modaal_overlay("show")},create_basic:function(){var t=this,o=a(t.scope.source),e="";o.length?(e=o.contents().detach(),o.empty()):e="Content could not be loaded. Please check the source and try again.",t.build_modal(e)},create_instagram:function(){var t=this,o=t.options.instagram_id,e="",i="Instagram photo couldn't be loaded, please check the embed code and try again.";if(t.build_modal('
"),o.xhr=a.ajax(t,{success:function(t){var e=a("#"+o.scope.id).find(".modaal-content-container");e.length>0&&(e.removeClass(o.options.loading_class),e.html(t),o.options.ajax_success.call(o,e))},error:function(t){if("abort"!=t.statusText){var e=a("#"+o.scope.id+" .modaal-content-container");e.length>0&&(e.removeClass(o.options.loading_class).addClass(o.options.ajax_error_class),e.html("Content could not be loaded. Please check the source and try again."))}}})},create_confirm:function(){var a,t=this;a='
",a("#"+o.scope.id+"_overlay").length<1&&o.dom.append(r),"inline"==o.options.type&&t.appendTo("#"+o.scope.id+" .modaal-content-container"),o.modaal_overlay("show")},create_basic:function(){var t=this,o=a(t.scope.source),e="";o.length?(e=o.contents().detach(),o.empty()):e="Content could not be loaded. Please check the source and try again.",t.build_modal(e)},create_instagram:function(){var t=this,o=t.options.instagram_id,e="",i="Instagram photo couldn't be loaded, please check the embed code and try again.";if(t.build_modal('
"),o.xhr=a.ajax(t,{success:function(t){var e=a("#"+o.scope.id).find(".modaal-content-container");e.length>0&&(e.removeClass(o.options.loading_class),e.html(t),o.options.ajax_success.call(o,e))},error:function(t){if("abort"!=t.statusText){var e=a("#"+o.scope.id+" .modaal-content-container");e.length>0&&(e.removeClass(o.options.loading_class).addClass(o.options.ajax_error_class),e.html("Content could not be loaded. Please check the source and try again."))}}})},create_confirm:function(){var a,t=this;a='