| /*jslint unparam: true, browser: true, indent: 2 */ |
| |
| ;(function ($, window, document, undefined) { |
| 'use strict'; |
| |
| Foundation.libs.clearing = { |
| name : 'clearing', |
| |
| version: '4.3.2', |
| |
| settings : { |
| templates : { |
| viewing : '<a href="#" class="clearing-close">×</a>' + |
| '<div class="visible-img" style="display: none"><img src="//:0">' + |
| '<p class="clearing-caption"></p><a href="#" class="clearing-main-prev"><span></span></a>' + |
| '<a href="#" class="clearing-main-next"><span></span></a></div>' |
| }, |
| |
| // comma delimited list of selectors that, on click, will close clearing, |
| // add 'div.clearing-blackout, div.visible-img' to close on background click |
| close_selectors : '.clearing-close', |
| |
| // event initializers and locks |
| init : false, |
| locked : false |
| }, |
| |
| init : function (scope, method, options) { |
| var self = this; |
| Foundation.inherit(this, 'set_data get_data remove_data throttle data_options'); |
| |
| if (typeof method === 'object') { |
| options = $.extend(true, this.settings, method); |
| } |
| |
| if (typeof method !== 'string') { |
| $(this.scope).find('ul[data-clearing]').each(function () { |
| var $el = $(this), |
| options = options || {}, |
| lis = $el.find('li'), |
| settings = self.get_data($el); |
| |
| if (!settings && lis.length > 0) { |
| options.$parent = $el.parent(); |
| |
| self.set_data($el, $.extend({}, self.settings, options, self.data_options($el))); |
| |
| self.assemble($el.find('li')); |
| |
| if (!self.settings.init) { |
| self.events().swipe_events(); |
| } |
| } |
| }); |
| |
| return this.settings.init; |
| } else { |
| // fire method |
| return this[method].call(this, options); |
| } |
| }, |
| |
| // event binding and initial setup |
| |
| events : function () { |
| var self = this; |
| |
| $(this.scope) |
| .on('click.fndtn.clearing', 'ul[data-clearing] li', |
| function (e, current, target) { |
| var current = current || $(this), |
| target = target || current, |
| next = current.next('li'), |
| settings = self.get_data(current.parent()), |
| image = $(e.target); |
| |
| e.preventDefault(); |
| if (!settings) self.init(); |
| |
| // if clearing is open and the current image is |
| // clicked, go to the next image in sequence |
| if (target.hasClass('visible') && |
| current[0] === target[0] && |
| next.length > 0 && self.is_open(current)) { |
| target = next; |
| image = target.find('img'); |
| } |
| |
| // set current and target to the clicked li if not otherwise defined. |
| self.open(image, current, target); |
| self.update_paddles(target); |
| }) |
| |
| .on('click.fndtn.clearing', '.clearing-main-next', |
| function (e) { this.nav(e, 'next') }.bind(this)) |
| .on('click.fndtn.clearing', '.clearing-main-prev', |
| function (e) { this.nav(e, 'prev') }.bind(this)) |
| .on('click.fndtn.clearing', this.settings.close_selectors, |
| function (e) { Foundation.libs.clearing.close(e, this) }) |
| .on('keydown.fndtn.clearing', |
| function (e) { this.keydown(e) }.bind(this)); |
| |
| $(window).on('resize.fndtn.clearing', |
| function () { this.resize() }.bind(this)); |
| |
| this.settings.init = true; |
| return this; |
| }, |
| |
| swipe_events : function () { |
| var self = this; |
| |
| $(this.scope) |
| .on('touchstart.fndtn.clearing', '.visible-img', function(e) { |
| if (!e.touches) { e = e.originalEvent; } |
| var data = { |
| start_page_x: e.touches[0].pageX, |
| start_page_y: e.touches[0].pageY, |
| start_time: (new Date()).getTime(), |
| delta_x: 0, |
| is_scrolling: undefined |
| }; |
| |
| $(this).data('swipe-transition', data); |
| e.stopPropagation(); |
| }) |
| .on('touchmove.fndtn.clearing', '.visible-img', function(e) { |
| if (!e.touches) { e = e.originalEvent; } |
| // Ignore pinch/zoom events |
| if(e.touches.length > 1 || e.scale && e.scale !== 1) return; |
| |
| var data = $(this).data('swipe-transition'); |
| |
| if (typeof data === 'undefined') { |
| data = {}; |
| } |
| |
| data.delta_x = e.touches[0].pageX - data.start_page_x; |
| |
| if ( typeof data.is_scrolling === 'undefined') { |
| data.is_scrolling = !!( data.is_scrolling || Math.abs(data.delta_x) < Math.abs(e.touches[0].pageY - data.start_page_y) ); |
| } |
| |
| if (!data.is_scrolling && !data.active) { |
| e.preventDefault(); |
| var direction = (data.delta_x < 0) ? 'next' : 'prev'; |
| data.active = true; |
| self.nav(e, direction); |
| } |
| }) |
| .on('touchend.fndtn.clearing', '.visible-img', function(e) { |
| $(this).data('swipe-transition', {}); |
| e.stopPropagation(); |
| }); |
| }, |
| |
| assemble : function ($li) { |
| var $el = $li.parent(); |
| $el.after('<div id="foundationClearingHolder"></div>'); |
| |
| var holder = $('#foundationClearingHolder'), |
| settings = this.get_data($el), |
| grid = $el.detach(), |
| data = { |
| grid: '<div class="carousel">' + this.outerHTML(grid[0]) + '</div>', |
| viewing: settings.templates.viewing |
| }, |
| wrapper = '<div class="clearing-assembled"><div>' + data.viewing + |
| data.grid + '</div></div>'; |
| |
| return holder.after(wrapper).remove(); |
| }, |
| |
| // event callbacks |
| |
| open : function ($image, current, target) { |
| var root = target.closest('.clearing-assembled'), |
| container = root.find('div').first(), |
| visible_image = container.find('.visible-img'), |
| image = visible_image.find('img').not($image); |
| |
| if (!this.locked()) { |
| // set the image to the selected thumbnail |
| image |
| .attr('src', this.load($image)) |
| .css('visibility', 'hidden'); |
| |
| this.loaded(image, function () { |
| image.css('visibility', 'visible'); |
| // toggle the gallery |
| root.addClass('clearing-blackout'); |
| container.addClass('clearing-container'); |
| visible_image.show(); |
| this.fix_height(target) |
| .caption(visible_image.find('.clearing-caption'), $image) |
| .center(image) |
| .shift(current, target, function () { |
| target.siblings().removeClass('visible'); |
| target.addClass('visible'); |
| }); |
| }.bind(this)); |
| } |
| }, |
| |
| close : function (e, el) { |
| e.preventDefault(); |
| |
| var root = (function (target) { |
| if (/blackout/.test(target.selector)) { |
| return target; |
| } else { |
| return target.closest('.clearing-blackout'); |
| } |
| }($(el))), container, visible_image; |
| |
| if (el === e.target && root) { |
| container = root.find('div').first(); |
| visible_image = container.find('.visible-img'); |
| this.settings.prev_index = 0; |
| root.find('ul[data-clearing]') |
| .attr('style', '').closest('.clearing-blackout') |
| .removeClass('clearing-blackout'); |
| container.removeClass('clearing-container'); |
| visible_image.hide(); |
| } |
| |
| return false; |
| }, |
| |
| is_open : function (current) { |
| return current.parent().prop('style').length > 0; |
| }, |
| |
| keydown : function (e) { |
| var clearing = $('.clearing-blackout').find('ul[data-clearing]'); |
| |
| if (e.which === 39) this.go(clearing, 'next'); |
| if (e.which === 37) this.go(clearing, 'prev'); |
| if (e.which === 27) $('a.clearing-close').trigger('click'); |
| }, |
| |
| nav : function (e, direction) { |
| var clearing = $('.clearing-blackout').find('ul[data-clearing]'); |
| |
| e.preventDefault(); |
| this.go(clearing, direction); |
| }, |
| |
| resize : function () { |
| var image = $('.clearing-blackout .visible-img').find('img'); |
| |
| if (image.length) { |
| this.center(image); |
| } |
| }, |
| |
| // visual adjustments |
| fix_height : function (target) { |
| var lis = target.parent().children(), |
| self = this; |
| |
| lis.each(function () { |
| var li = $(this), |
| image = li.find('img'); |
| |
| if (li.height() > self.outerHeight(image)) { |
| li.addClass('fix-height'); |
| } |
| }) |
| .closest('ul') |
| .width(lis.length * 100 + '%'); |
| |
| return this; |
| }, |
| |
| update_paddles : function (target) { |
| var visible_image = target |
| .closest('.carousel') |
| .siblings('.visible-img'); |
| |
| if (target.next().length > 0) { |
| visible_image |
| .find('.clearing-main-next') |
| .removeClass('disabled'); |
| } else { |
| visible_image |
| .find('.clearing-main-next') |
| .addClass('disabled'); |
| } |
| |
| if (target.prev().length > 0) { |
| visible_image |
| .find('.clearing-main-prev') |
| .removeClass('disabled'); |
| } else { |
| visible_image |
| .find('.clearing-main-prev') |
| .addClass('disabled'); |
| } |
| }, |
| |
| center : function (target) { |
| if (!this.rtl) { |
| target.css({ |
| marginLeft : -(this.outerWidth(target) / 2), |
| marginTop : -(this.outerHeight(target) / 2) |
| }); |
| } else { |
| target.css({ |
| marginRight : -(this.outerWidth(target) / 2), |
| marginTop : -(this.outerHeight(target) / 2) |
| }); |
| } |
| return this; |
| }, |
| |
| // image loading and preloading |
| |
| load : function ($image) { |
| if ($image[0].nodeName === "A") { |
| var href = $image.attr('href'); |
| } else { |
| var href = $image.parent().attr('href'); |
| } |
| |
| this.preload($image); |
| |
| if (href) return href; |
| return $image.attr('src'); |
| }, |
| |
| preload : function ($image) { |
| this |
| .img($image.closest('li').next()) |
| .img($image.closest('li').prev()); |
| }, |
| |
| loaded : function (image, callback) { |
| // based on jquery.imageready.js |
| // @weblinc, @jsantell, (c) 2012 |
| |
| function loaded () { |
| callback(); |
| } |
| |
| function bindLoad () { |
| this.one('load', loaded); |
| |
| if (/MSIE (\d+\.\d+);/.test(navigator.userAgent)) { |
| var src = this.attr( 'src' ), |
| param = src.match( /\?/ ) ? '&' : '?'; |
| |
| param += 'random=' + (new Date()).getTime(); |
| this.attr('src', src + param); |
| } |
| } |
| |
| if (!image.attr('src')) { |
| loaded(); |
| return; |
| } |
| |
| if (image[0].complete || image[0].readyState === 4) { |
| loaded(); |
| } else { |
| bindLoad.call(image); |
| } |
| }, |
| |
| img : function (img) { |
| if (img.length) { |
| var new_img = new Image(), |
| new_a = img.find('a'); |
| |
| if (new_a.length) { |
| new_img.src = new_a.attr('href'); |
| } else { |
| new_img.src = img.find('img').attr('src'); |
| } |
| } |
| return this; |
| }, |
| |
| // image caption |
| |
| caption : function (container, $image) { |
| var caption = $image.data('caption'); |
| |
| if (caption) { |
| container |
| .html(caption) |
| .show(); |
| } else { |
| container |
| .text('') |
| .hide(); |
| } |
| return this; |
| }, |
| |
| // directional methods |
| |
| go : function ($ul, direction) { |
| var current = $ul.find('.visible'), |
| target = current[direction](); |
| |
| if (target.length) { |
| target |
| .find('img') |
| .trigger('click', [current, target]); |
| } |
| }, |
| |
| shift : function (current, target, callback) { |
| var clearing = target.parent(), |
| old_index = this.settings.prev_index || target.index(), |
| direction = this.direction(clearing, current, target), |
| left = parseInt(clearing.css('left'), 10), |
| width = this.outerWidth(target), |
| skip_shift; |
| |
| // we use jQuery animate instead of CSS transitions because we |
| // need a callback to unlock the next animation |
| if (target.index() !== old_index && !/skip/.test(direction)){ |
| if (/left/.test(direction)) { |
| this.lock(); |
| clearing.animate({left : left + width}, 300, this.unlock()); |
| } else if (/right/.test(direction)) { |
| this.lock(); |
| clearing.animate({left : left - width}, 300, this.unlock()); |
| } |
| } else if (/skip/.test(direction)) { |
| // the target image is not adjacent to the current image, so |
| // do we scroll right or not |
| skip_shift = target.index() - this.settings.up_count; |
| this.lock(); |
| |
| if (skip_shift > 0) { |
| clearing.animate({left : -(skip_shift * width)}, 300, this.unlock()); |
| } else { |
| clearing.animate({left : 0}, 300, this.unlock()); |
| } |
| } |
| |
| callback(); |
| }, |
| |
| direction : function ($el, current, target) { |
| var lis = $el.find('li'), |
| li_width = this.outerWidth(lis) + (this.outerWidth(lis) / 4), |
| up_count = Math.floor(this.outerWidth($('.clearing-container')) / li_width) - 1, |
| target_index = lis.index(target), |
| response; |
| |
| this.settings.up_count = up_count; |
| |
| if (this.adjacent(this.settings.prev_index, target_index)) { |
| if ((target_index > up_count) |
| && target_index > this.settings.prev_index) { |
| response = 'right'; |
| } else if ((target_index > up_count - 1) |
| && target_index <= this.settings.prev_index) { |
| response = 'left'; |
| } else { |
| response = false; |
| } |
| } else { |
| response = 'skip'; |
| } |
| |
| this.settings.prev_index = target_index; |
| |
| return response; |
| }, |
| |
| adjacent : function (current_index, target_index) { |
| for (var i = target_index + 1; i >= target_index - 1; i--) { |
| if (i === current_index) return true; |
| } |
| return false; |
| }, |
| |
| // lock management |
| |
| lock : function () { |
| this.settings.locked = true; |
| }, |
| |
| unlock : function () { |
| this.settings.locked = false; |
| }, |
| |
| locked : function () { |
| return this.settings.locked; |
| }, |
| |
| // plugin management/browser quirks |
| |
| outerHTML : function (el) { |
| // support FireFox < 11 |
| return el.outerHTML || new XMLSerializer().serializeToString(el); |
| }, |
| |
| off : function () { |
| $(this.scope).off('.fndtn.clearing'); |
| $(window).off('.fndtn.clearing'); |
| this.remove_data(); // empty settings cache |
| this.settings.init = false; |
| }, |
| |
| reflow : function () { |
| this.init(); |
| } |
| }; |
| |
| }(Foundation.zj, this, this.document)); |