You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
516 lines
14 KiB
516 lines
14 KiB
/*jslint unparam: true, browser: true, indent: 2 */ |
|
|
|
;(function ($, window, document, undefined) { |
|
'use strict'; |
|
|
|
Foundation.libs.clearing = { |
|
name : 'clearing', |
|
|
|
version: '4.3.1', |
|
|
|
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().attr('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));
|
|
|