557 lines
16 KiB
JavaScript
557 lines
16 KiB
JavaScript
|
;(function ($, window, document, undefined) {
|
||
|
'use strict';
|
||
|
|
||
|
Foundation.libs.clearing = {
|
||
|
name : 'clearing',
|
||
|
|
||
|
version : '5.5.1',
|
||
|
|
||
|
settings : {
|
||
|
templates : {
|
||
|
viewing : '<a href="#" class="clearing-close">×</a>' +
|
||
|
'<div class="visible-img" style="display: none"><div class="clearing-touch-label"></div><img src="data:image/gif;base64,R0lGODlhAQABAAD/ACwAAAAAAQABAAACADs%3D" alt="" />' +
|
||
|
'<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, div.clearing-blackout',
|
||
|
|
||
|
// Default to the entire li element.
|
||
|
open_selectors : '',
|
||
|
|
||
|
// Image will be skipped in carousel.
|
||
|
skip_selector : '',
|
||
|
|
||
|
touch_label : '',
|
||
|
|
||
|
// event initializers and locks
|
||
|
init : false,
|
||
|
locked : false
|
||
|
},
|
||
|
|
||
|
init : function (scope, method, options) {
|
||
|
var self = this;
|
||
|
Foundation.inherit(this, 'throttle image_loaded');
|
||
|
|
||
|
this.bindings(method, options);
|
||
|
|
||
|
if (self.S(this.scope).is('[' + this.attr_name() + ']')) {
|
||
|
this.assemble(self.S('li', this.scope));
|
||
|
} else {
|
||
|
self.S('[' + this.attr_name() + ']', this.scope).each(function () {
|
||
|
self.assemble(self.S('li', this));
|
||
|
});
|
||
|
}
|
||
|
},
|
||
|
|
||
|
events : function (scope) {
|
||
|
var self = this,
|
||
|
S = self.S,
|
||
|
$scroll_container = $('.scroll-container');
|
||
|
|
||
|
if ($scroll_container.length > 0) {
|
||
|
this.scope = $scroll_container;
|
||
|
}
|
||
|
|
||
|
S(this.scope)
|
||
|
.off('.clearing')
|
||
|
.on('click.fndtn.clearing', 'ul[' + this.attr_name() + '] li ' + this.settings.open_selectors,
|
||
|
function (e, current, target) {
|
||
|
var current = current || S(this),
|
||
|
target = target || current,
|
||
|
next = current.next('li'),
|
||
|
settings = current.closest('[' + self.attr_name() + ']').data(self.attr_name(true) + '-init'),
|
||
|
image = S(e.target);
|
||
|
|
||
|
e.preventDefault();
|
||
|
|
||
|
if (!settings) {
|
||
|
self.init();
|
||
|
settings = current.closest('[' + self.attr_name() + ']').data(self.attr_name(true) + '-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 = S('img', target);
|
||
|
}
|
||
|
|
||
|
// 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) { self.nav(e, 'next') })
|
||
|
.on('click.fndtn.clearing', '.clearing-main-prev',
|
||
|
function (e) { self.nav(e, 'prev') })
|
||
|
.on('click.fndtn.clearing', this.settings.close_selectors,
|
||
|
function (e) { Foundation.libs.clearing.close(e, this) });
|
||
|
|
||
|
$(document).on('keydown.fndtn.clearing',
|
||
|
function (e) { self.keydown(e) });
|
||
|
|
||
|
S(window).off('.clearing').on('resize.fndtn.clearing',
|
||
|
function () { self.resize() });
|
||
|
|
||
|
this.swipe_events(scope);
|
||
|
},
|
||
|
|
||
|
swipe_events : function (scope) {
|
||
|
var self = this,
|
||
|
S = self.S;
|
||
|
|
||
|
S(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
|
||
|
};
|
||
|
|
||
|
S(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 = S(this).data('swipe-transition');
|
||
|
|
||
|
if (typeof data === 'undefined') {
|
||
|
data = {};
|
||
|
}
|
||
|
|
||
|
data.delta_x = e.touches[0].pageX - data.start_page_x;
|
||
|
|
||
|
if (Foundation.rtl) {
|
||
|
data.delta_x = -data.delta_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) {
|
||
|
S(this).data('swipe-transition', {});
|
||
|
e.stopPropagation();
|
||
|
});
|
||
|
},
|
||
|
|
||
|
assemble : function ($li) {
|
||
|
var $el = $li.parent();
|
||
|
|
||
|
if ($el.parent().hasClass('carousel')) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
$el.after('<div id="foundationClearingHolder"></div>');
|
||
|
|
||
|
var grid = $el.detach(),
|
||
|
grid_outerHTML = '';
|
||
|
|
||
|
if (grid[0] == null) {
|
||
|
return;
|
||
|
} else {
|
||
|
grid_outerHTML = grid[0].outerHTML;
|
||
|
}
|
||
|
|
||
|
var holder = this.S('#foundationClearingHolder'),
|
||
|
settings = $el.data(this.attr_name(true) + '-init'),
|
||
|
data = {
|
||
|
grid : '<div class="carousel">' + grid_outerHTML + '</div>',
|
||
|
viewing : settings.templates.viewing
|
||
|
},
|
||
|
wrapper = '<div class="clearing-assembled"><div>' + data.viewing +
|
||
|
data.grid + '</div></div>',
|
||
|
touch_label = this.settings.touch_label;
|
||
|
|
||
|
if (Modernizr.touch) {
|
||
|
wrapper = $(wrapper).find('.clearing-touch-label').html(touch_label).end();
|
||
|
}
|
||
|
|
||
|
holder.after(wrapper).remove();
|
||
|
},
|
||
|
|
||
|
open : function ($image, current, target) {
|
||
|
var self = this,
|
||
|
body = $(document.body),
|
||
|
root = target.closest('.clearing-assembled'),
|
||
|
container = self.S('div', root).first(),
|
||
|
visible_image = self.S('.visible-img', container),
|
||
|
image = self.S('img', visible_image).not($image),
|
||
|
label = self.S('.clearing-touch-label', container),
|
||
|
error = false;
|
||
|
|
||
|
// Event to disable scrolling on touch devices when Clearing is activated
|
||
|
$('body').on('touchmove', function (e) {
|
||
|
e.preventDefault();
|
||
|
});
|
||
|
|
||
|
image.error(function () {
|
||
|
error = true;
|
||
|
});
|
||
|
|
||
|
function startLoad() {
|
||
|
setTimeout(function () {
|
||
|
this.image_loaded(image, function () {
|
||
|
if (image.outerWidth() === 1 && !error) {
|
||
|
startLoad.call(this);
|
||
|
} else {
|
||
|
cb.call(this, image);
|
||
|
}
|
||
|
}.bind(this));
|
||
|
}.bind(this), 100);
|
||
|
}
|
||
|
|
||
|
function cb (image) {
|
||
|
var $image = $(image);
|
||
|
$image.css('visibility', 'visible');
|
||
|
// toggle the gallery
|
||
|
body.css('overflow', 'hidden');
|
||
|
root.addClass('clearing-blackout');
|
||
|
container.addClass('clearing-container');
|
||
|
visible_image.show();
|
||
|
this.fix_height(target)
|
||
|
.caption(self.S('.clearing-caption', visible_image), self.S('img', target))
|
||
|
.center_and_label(image, label)
|
||
|
.shift(current, target, function () {
|
||
|
target.closest('li').siblings().removeClass('visible');
|
||
|
target.closest('li').addClass('visible');
|
||
|
});
|
||
|
visible_image.trigger('opened.fndtn.clearing')
|
||
|
}
|
||
|
|
||
|
if (!this.locked()) {
|
||
|
visible_image.trigger('open.fndtn.clearing');
|
||
|
// set the image to the selected thumbnail
|
||
|
image
|
||
|
.attr('src', this.load($image))
|
||
|
.css('visibility', 'hidden');
|
||
|
|
||
|
startLoad.call(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))),
|
||
|
body = $(document.body), container, visible_image;
|
||
|
|
||
|
if (el === e.target && root) {
|
||
|
body.css('overflow', '');
|
||
|
container = $('div', root).first();
|
||
|
visible_image = $('.visible-img', container);
|
||
|
visible_image.trigger('close.fndtn.clearing');
|
||
|
this.settings.prev_index = 0;
|
||
|
$('ul[' + this.attr_name() + ']', root)
|
||
|
.attr('style', '').closest('.clearing-blackout')
|
||
|
.removeClass('clearing-blackout');
|
||
|
container.removeClass('clearing-container');
|
||
|
visible_image.hide();
|
||
|
visible_image.trigger('closed.fndtn.clearing');
|
||
|
}
|
||
|
|
||
|
// Event to re-enable scrolling on touch devices
|
||
|
$('body').off('touchmove');
|
||
|
|
||
|
return false;
|
||
|
},
|
||
|
|
||
|
is_open : function (current) {
|
||
|
return current.parent().prop('style').length > 0;
|
||
|
},
|
||
|
|
||
|
keydown : function (e) {
|
||
|
var clearing = $('.clearing-blackout ul[' + this.attr_name() + ']'),
|
||
|
NEXT_KEY = this.rtl ? 37 : 39,
|
||
|
PREV_KEY = this.rtl ? 39 : 37,
|
||
|
ESC_KEY = 27;
|
||
|
|
||
|
if (e.which === NEXT_KEY) {
|
||
|
this.go(clearing, 'next');
|
||
|
}
|
||
|
if (e.which === PREV_KEY) {
|
||
|
this.go(clearing, 'prev');
|
||
|
}
|
||
|
if (e.which === ESC_KEY) {
|
||
|
this.S('a.clearing-close').trigger('click').trigger('click.fndtn.clearing');
|
||
|
}
|
||
|
},
|
||
|
|
||
|
nav : function (e, direction) {
|
||
|
var clearing = $('ul[' + this.attr_name() + ']', '.clearing-blackout');
|
||
|
|
||
|
e.preventDefault();
|
||
|
this.go(clearing, direction);
|
||
|
},
|
||
|
|
||
|
resize : function () {
|
||
|
var image = $('img', '.clearing-blackout .visible-img'),
|
||
|
label = $('.clearing-touch-label', '.clearing-blackout');
|
||
|
|
||
|
if (image.length) {
|
||
|
this.center_and_label(image, label);
|
||
|
image.trigger('resized.fndtn.clearing')
|
||
|
}
|
||
|
},
|
||
|
|
||
|
// visual adjustments
|
||
|
fix_height : function (target) {
|
||
|
var lis = target.parent().children(),
|
||
|
self = this;
|
||
|
|
||
|
lis.each(function () {
|
||
|
var li = self.S(this),
|
||
|
image = li.find('img');
|
||
|
|
||
|
if (li.height() > image.outerHeight()) {
|
||
|
li.addClass('fix-height');
|
||
|
}
|
||
|
})
|
||
|
.closest('ul')
|
||
|
.width(lis.length * 100 + '%');
|
||
|
|
||
|
return this;
|
||
|
},
|
||
|
|
||
|
update_paddles : function (target) {
|
||
|
target = target.closest('li');
|
||
|
var visible_image = target
|
||
|
.closest('.carousel')
|
||
|
.siblings('.visible-img');
|
||
|
|
||
|
if (target.next().length > 0) {
|
||
|
this.S('.clearing-main-next', visible_image).removeClass('disabled');
|
||
|
} else {
|
||
|
this.S('.clearing-main-next', visible_image).addClass('disabled');
|
||
|
}
|
||
|
|
||
|
if (target.prev().length > 0) {
|
||
|
this.S('.clearing-main-prev', visible_image).removeClass('disabled');
|
||
|
} else {
|
||
|
this.S('.clearing-main-prev', visible_image).addClass('disabled');
|
||
|
}
|
||
|
},
|
||
|
|
||
|
center_and_label : function (target, label) {
|
||
|
if (!this.rtl && label.length > 0) {
|
||
|
label.css({
|
||
|
marginLeft : -(label.outerWidth() / 2),
|
||
|
marginTop : -(target.outerHeight() / 2)-label.outerHeight()-10
|
||
|
});
|
||
|
} else {
|
||
|
label.css({
|
||
|
marginRight : -(label.outerWidth() / 2),
|
||
|
marginTop : -(target.outerHeight() / 2)-label.outerHeight()-10,
|
||
|
left: 'auto',
|
||
|
right: '50%'
|
||
|
});
|
||
|
}
|
||
|
return this;
|
||
|
},
|
||
|
|
||
|
// image loading and preloading
|
||
|
|
||
|
load : function ($image) {
|
||
|
var href;
|
||
|
|
||
|
if ($image[0].nodeName === 'A') {
|
||
|
href = $image.attr('href');
|
||
|
} else {
|
||
|
href = $image.closest('a').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());
|
||
|
},
|
||
|
|
||
|
img : function (img) {
|
||
|
if (img.length) {
|
||
|
var new_img = new Image(),
|
||
|
new_a = this.S('a', img);
|
||
|
|
||
|
if (new_a.length) {
|
||
|
new_img.src = new_a.attr('href');
|
||
|
} else {
|
||
|
new_img.src = this.S('img', img).attr('src');
|
||
|
}
|
||
|
}
|
||
|
return this;
|
||
|
},
|
||
|
|
||
|
// image caption
|
||
|
|
||
|
caption : function (container, $image) {
|
||
|
var caption = $image.attr('data-caption');
|
||
|
|
||
|
if (caption) {
|
||
|
container
|
||
|
.html(caption)
|
||
|
.show();
|
||
|
} else {
|
||
|
container
|
||
|
.text('')
|
||
|
.hide();
|
||
|
}
|
||
|
return this;
|
||
|
},
|
||
|
|
||
|
// directional methods
|
||
|
|
||
|
go : function ($ul, direction) {
|
||
|
var current = this.S('.visible', $ul),
|
||
|
target = current[direction]();
|
||
|
|
||
|
// Check for skip selector.
|
||
|
if (this.settings.skip_selector && target.find(this.settings.skip_selector).length != 0) {
|
||
|
target = target[direction]();
|
||
|
}
|
||
|
|
||
|
if (target.length) {
|
||
|
this.S('img', target)
|
||
|
.trigger('click', [current, target]).trigger('click.fndtn.clearing', [current, target])
|
||
|
.trigger('change.fndtn.clearing');
|
||
|
}
|
||
|
},
|
||
|
|
||
|
shift : function (current, target, callback) {
|
||
|
var clearing = target.parent(),
|
||
|
old_index = this.settings.prev_index || target.index(),
|
||
|
direction = this.direction(clearing, current, target),
|
||
|
dir = this.rtl ? 'right' : 'left',
|
||
|
left = parseInt(clearing.css('left'), 10),
|
||
|
width = target.outerWidth(),
|
||
|
skip_shift;
|
||
|
|
||
|
var dir_obj = {};
|
||
|
|
||
|
// we use jQuery animate instead of CSS transitions because we
|
||
|
// need a callback to unlock the next animation
|
||
|
// needs support for RTL **
|
||
|
if (target.index() !== old_index && !/skip/.test(direction)) {
|
||
|
if (/left/.test(direction)) {
|
||
|
this.lock();
|
||
|
dir_obj[dir] = left + width;
|
||
|
clearing.animate(dir_obj, 300, this.unlock());
|
||
|
} else if (/right/.test(direction)) {
|
||
|
this.lock();
|
||
|
dir_obj[dir] = left - width;
|
||
|
clearing.animate(dir_obj, 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) {
|
||
|
dir_obj[dir] = -(skip_shift * width);
|
||
|
clearing.animate(dir_obj, 300, this.unlock());
|
||
|
} else {
|
||
|
dir_obj[dir] = 0;
|
||
|
clearing.animate(dir_obj, 300, this.unlock());
|
||
|
}
|
||
|
}
|
||
|
|
||
|
callback();
|
||
|
},
|
||
|
|
||
|
direction : function ($el, current, target) {
|
||
|
var lis = this.S('li', $el),
|
||
|
li_width = lis.outerWidth() + (lis.outerWidth() / 4),
|
||
|
up_count = Math.floor(this.S('.clearing-container').outerWidth() / 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;
|
||
|
},
|
||
|
|
||
|
off : function () {
|
||
|
this.S(this.scope).off('.fndtn.clearing');
|
||
|
this.S(window).off('.fndtn.clearing');
|
||
|
},
|
||
|
|
||
|
reflow : function () {
|
||
|
this.init();
|
||
|
}
|
||
|
};
|
||
|
|
||
|
}(jQuery, window, window.document));
|