2 (function ($, window
, document
, undefined) {
5 Foundation
.libs
.clearing
= {
12 viewing
: '<a href="#" class="clearing-close">×</a>' +
13 '<div class="visible-img" style="display: none"><div class="clearing-touch-label"></div><img src="data:image/gif;base64,R0lGODlhAQABAAD/ACwAAAAAAQABAAACADs%3D" alt="" />' +
14 '<p class="clearing-caption"></p><a href="#" class="clearing-main-prev"><span></span></a>' +
15 '<a href="#" class="clearing-main-next"><span></span></a></div>'
18 // comma delimited list of selectors that, on click, will close clearing,
19 // add 'div.clearing-blackout, div.visible-img' to close on background click
20 close_selectors
: '.clearing-close, div.clearing-blackout',
22 // Default to the entire li element.
25 // Image will be skipped in carousel.
30 // event initializers and locks
35 init: function (scope
, method
, options
) {
37 Foundation
.inherit(this, 'throttle image_loaded');
39 this.bindings(method
, options
);
41 if (self
.S(this.scope
).is('[' + this.attr_name() + ']')) {
42 this.assemble(self
.S('li', this.scope
));
44 self
.S('[' + this.attr_name() + ']', this.scope
).each(function () {
45 self
.assemble(self
.S('li', this));
50 events: function (scope
) {
53 $scroll_container
= $('.scroll-container');
55 if ($scroll_container
.length
> 0) {
56 this.scope
= $scroll_container
;
61 .on('click.fndtn.clearing', 'ul[' + this.attr_name() + '] li ' + this.settings
.open_selectors
,
62 function (e
, current
, target
) {
63 var current
= current
|| S(this),
64 target
= target
|| current
,
65 next
= current
.next('li'),
66 settings
= current
.closest('[' + self
.attr_name() + ']').data(self
.attr_name(true) + '-init'),
73 settings
= current
.closest('[' + self
.attr_name() + ']').data(self
.attr_name(true) + '-init');
76 // if clearing is open and the current image is
77 // clicked, go to the next image in sequence
78 if (target
.hasClass('visible') &&
79 current
[0] === target
[0] &&
80 next
.length
> 0 && self
.is_open(current
)) {
82 image
= S('img', target
);
85 // set current and target to the clicked li if not otherwise defined.
86 self
.open(image
, current
, target
);
87 self
.update_paddles(target
);
90 .on('click.fndtn.clearing', '.clearing-main-next',
94 .on('click.fndtn.clearing', '.clearing-main-prev',
98 .on('click.fndtn.clearing', this.settings
.close_selectors
,
100 Foundation
.libs
.clearing
.close(e
, this)
103 $(document
).on('keydown.fndtn.clearing',
108 S(window
).off('.clearing').on('resize.fndtn.clearing',
113 this.swipe_events(scope
);
116 swipe_events: function (scope
) {
121 .on('touchstart.fndtn.clearing', '.visible-img', function (e
) {
126 start_page_x
: e
.touches
[0].pageX
,
127 start_page_y
: e
.touches
[0].pageY
,
128 start_time
: (new Date()).getTime(),
130 is_scrolling
: undefined
133 S(this).data('swipe-transition', data
);
136 .on('touchmove.fndtn.clearing', '.visible-img', function (e
) {
140 // Ignore pinch/zoom events
141 if (e
.touches
.length
> 1 || e
.scale
&& e
.scale
!== 1) return;
143 var data
= S(this).data('swipe-transition');
145 if (typeof data
=== 'undefined') {
149 data
.delta_x
= e
.touches
[0].pageX
- data
.start_page_x
;
151 if (Foundation
.rtl
) {
152 data
.delta_x
= -data
.delta_x
;
155 if (typeof data
.is_scrolling
=== 'undefined') {
156 data
.is_scrolling
= !!( data
.is_scrolling
|| Math
.abs(data
.delta_x
) < Math
.abs(e
.touches
[0].pageY
- data
.start_page_y
) );
159 if (!data
.is_scrolling
&& !data
.active
) {
161 var direction
= (data
.delta_x
< 0) ? 'next' : 'prev';
163 self
.nav(e
, direction
);
166 .on('touchend.fndtn.clearing', '.visible-img', function (e
) {
167 S(this).data('swipe-transition', {});
172 assemble: function ($li
) {
173 var $el
= $li
.parent();
175 if ($el
.parent().hasClass('carousel')) {
179 $el
.after('<div id="foundationClearingHolder"></div>');
181 var grid
= $el
.detach(),
184 if (grid
[0] == null) {
187 grid_outerHTML
= grid
[0].outerHTML
;
190 var holder
= this.S('#foundationClearingHolder'),
191 settings
= $el
.data(this.attr_name(true) + '-init'),
193 grid
: '<div class="carousel">' + grid_outerHTML
+ '</div>',
194 viewing
: settings
.templates
.viewing
196 wrapper
= '<div class="clearing-assembled"><div>' + data
.viewing
+
197 data
.grid
+ '</div></div>',
198 touch_label
= this.settings
.touch_label
;
200 if (Modernizr
.touch
) {
201 wrapper
= $(wrapper
).find('.clearing-touch-label').html(touch_label
).end();
204 holder
.after(wrapper
).remove();
207 open: function ($image
, current
, target
) {
209 body
= $(document
.body
),
210 root
= target
.closest('.clearing-assembled'),
211 container
= self
.S('div', root
).first(),
212 visible_image
= self
.S('.visible-img', container
),
213 image
= self
.S('img', visible_image
).not($image
),
214 label
= self
.S('.clearing-touch-label', container
),
217 // Event to disable scrolling on touch devices when Clearing is activated
218 $('body').on('touchmove', function (e
) {
222 image
.error(function () {
226 function startLoad() {
227 setTimeout(function () {
228 this.image_loaded(image
, function () {
229 if (image
.outerWidth() === 1 && !error
) {
230 startLoad
.call(this);
232 cb
.call(this, image
);
239 var $image
= $(image
);
240 $image
.css('visibility', 'visible');
241 // toggle the gallery
242 body
.css('overflow', 'hidden');
243 root
.addClass('clearing-blackout');
244 container
.addClass('clearing-container');
245 visible_image
.show();
246 this.fix_height(target
)
247 .caption(self
.S('.clearing-caption', visible_image
), self
.S('img', target
))
248 .center_and_label(image
, label
)
249 .shift(current
, target
, function () {
250 target
.closest('li').siblings().removeClass('visible');
251 target
.closest('li').addClass('visible');
253 visible_image
.trigger('opened.fndtn.clearing')
256 if (!this.locked()) {
257 visible_image
.trigger('open.fndtn.clearing');
258 // set the image to the selected thumbnail
260 .attr('src', this.load($image
))
261 .css('visibility', 'hidden');
263 startLoad
.call(this);
267 close: function (e
, el
) {
270 var root
= (function (target
) {
271 if (/blackout/.test(target
.selector
)) {
274 return target
.closest('.clearing-blackout');
277 body
= $(document
.body
), container
, visible_image
;
279 if (el
=== e
.target
&& root
) {
280 body
.css('overflow', '');
281 container
= $('div', root
).first();
282 visible_image
= $('.visible-img', container
);
283 visible_image
.trigger('close.fndtn.clearing');
284 this.settings
.prev_index
= 0;
285 $('ul[' + this.attr_name() + ']', root
)
286 .attr('style', '').closest('.clearing-blackout')
287 .removeClass('clearing-blackout');
288 container
.removeClass('clearing-container');
289 visible_image
.hide();
290 visible_image
.trigger('closed.fndtn.clearing');
293 // Event to re-enable scrolling on touch devices
294 $('body').off('touchmove');
299 is_open: function (current
) {
300 return current
.parent().prop('style').length
> 0;
303 keydown: function (e
) {
304 var clearing
= $('.clearing-blackout ul[' + this.attr_name() + ']'),
305 NEXT_KEY
= this.rtl
? 37 : 39,
306 PREV_KEY
= this.rtl
? 39 : 37,
309 if (e
.which
=== NEXT_KEY
) this.go(clearing
, 'next');
310 if (e
.which
=== PREV_KEY
) this.go(clearing
, 'prev');
311 if (e
.which
=== ESC_KEY
) this.S('a.clearing-close').trigger('click').trigger('click.fndtn.clearing');
314 nav: function (e
, direction
) {
315 var clearing
= $('ul[' + this.attr_name() + ']', '.clearing-blackout');
318 this.go(clearing
, direction
);
321 resize: function () {
322 var image
= $('img', '.clearing-blackout .visible-img'),
323 label
= $('.clearing-touch-label', '.clearing-blackout');
326 this.center_and_label(image
, label
);
327 image
.trigger('resized.fndtn.clearing')
331 // visual adjustments
332 fix_height: function (target
) {
333 var lis
= target
.parent().children(),
336 lis
.each(function () {
337 var li
= self
.S(this),
338 image
= li
.find('img');
340 if (li
.height() > image
.outerHeight()) {
341 li
.addClass('fix-height');
345 .width(lis
.length
* 100 + '%');
350 update_paddles: function (target
) {
351 target
= target
.closest('li');
352 var visible_image
= target
353 .closest('.carousel')
354 .siblings('.visible-img');
356 if (target
.next().length
> 0) {
357 this.S('.clearing-main-next', visible_image
).removeClass('disabled');
359 this.S('.clearing-main-next', visible_image
).addClass('disabled');
362 if (target
.prev().length
> 0) {
363 this.S('.clearing-main-prev', visible_image
).removeClass('disabled');
365 this.S('.clearing-main-prev', visible_image
).addClass('disabled');
369 center_and_label: function (target
, label
) {
372 marginLeft
: -(target
.outerWidth() / 2),
373 marginTop
: -(target
.outerHeight() / 2)
376 if (label
.length
> 0) {
378 marginLeft
: -(label
.outerWidth() / 2),
379 marginTop
: -(target
.outerHeight() / 2) - label
.outerHeight() - 10
384 marginRight
: -(target
.outerWidth() / 2),
385 marginTop
: -(target
.outerHeight() / 2),
390 if (label
.length
> 0) {
392 marginRight
: -(label
.outerWidth() / 2),
393 marginTop
: -(target
.outerHeight() / 2) - label
.outerHeight() - 10,
402 // image loading and preloading
404 load: function ($image
) {
407 if ($image
[0].nodeName
=== 'A') {
408 href
= $image
.attr('href');
410 href
= $image
.closest('a').attr('href');
413 this.preload($image
);
415 if (href
) return href
;
416 return $image
.attr('src');
419 preload: function ($image
) {
421 .img($image
.closest('li').next())
422 .img($image
.closest('li').prev());
425 img: function (img
) {
427 var new_img
= new Image(),
428 new_a
= this.S('a', img
);
431 new_img
.src
= new_a
.attr('href');
433 new_img
.src
= this.S('img', img
).attr('src');
441 caption: function (container
, $image
) {
442 var caption
= $image
.attr('data-caption');
456 // directional methods
458 go: function ($ul
, direction
) {
459 var current
= this.S('.visible', $ul
),
460 target
= current
[direction
]();
462 // Check for skip selector.
463 if (this.settings
.skip_selector
&& target
.find(this.settings
.skip_selector
).length
!= 0) {
464 target
= target
[direction
]();
468 this.S('img', target
)
469 .trigger('click', [current
, target
]).trigger('click.fndtn.clearing', [current
, target
])
470 .trigger('change.fndtn.clearing');
474 shift: function (current
, target
, callback
) {
475 var clearing
= target
.parent(),
476 old_index
= this.settings
.prev_index
|| target
.index(),
477 direction
= this.direction(clearing
, current
, target
),
478 dir
= this.rtl
? 'right' : 'left',
479 left
= parseInt(clearing
.css('left'), 10),
480 width
= target
.outerWidth(),
485 // we use jQuery animate instead of CSS transitions because we
486 // need a callback to unlock the next animation
487 // needs support for RTL **
488 if (target
.index() !== old_index
&& !/skip/.test(direction
)) {
489 if (/left/.test(direction
)) {
491 dir_obj
[dir
] = left
+ width
;
492 clearing
.animate(dir_obj
, 300, this.unlock());
493 } else if (/right/.test(direction
)) {
495 dir_obj
[dir
] = left
- width
;
496 clearing
.animate(dir_obj
, 300, this.unlock());
498 } else if (/skip/.test(direction
)) {
499 // the target image is not adjacent to the current image, so
500 // do we scroll right or not
501 skip_shift
= target
.index() - this.settings
.up_count
;
504 if (skip_shift
> 0) {
505 dir_obj
[dir
] = -(skip_shift
* width
);
506 clearing
.animate(dir_obj
, 300, this.unlock());
509 clearing
.animate(dir_obj
, 300, this.unlock());
516 direction: function ($el
, current
, target
) {
517 var lis
= this.S('li', $el
),
518 li_width
= lis
.outerWidth() + (lis
.outerWidth() / 4),
519 up_count
= Math
.floor(this.S('.clearing-container').outerWidth() / li_width
) - 1,
520 target_index
= lis
.index(target
),
523 this.settings
.up_count
= up_count
;
525 if (this.adjacent(this.settings
.prev_index
, target_index
)) {
526 if ((target_index
> up_count
) && target_index
> this.settings
.prev_index
) {
528 } else if ((target_index
> up_count
- 1) && target_index
<= this.settings
.prev_index
) {
537 this.settings
.prev_index
= target_index
;
542 adjacent: function (current_index
, target_index
) {
543 for (var i
= target_index
+ 1; i
>= target_index
- 1; i
--) {
544 if (i
=== current_index
) return true;
552 this.settings
.locked
= true;
555 unlock: function () {
556 this.settings
.locked
= false;
559 locked: function () {
560 return this.settings
.locked
;
564 this.S(this.scope
).off('.fndtn.clearing');
565 this.S(window
).off('.fndtn.clearing');
568 reflow: function () {
573 }(jQuery
, window
, window
.document
));