Statistics
| Branch: | Tag: | Revision:

root / snf-cyclades-app / synnefo / ui / new_ui / ui / vendor / foundation / foundation.clearing.js @ faad3c72

History | View | Annotate | Download (13.4 kB)

1
/*jslint unparam: true, browser: true, indent: 2 */
2

    
3
;(function ($, window, document, undefined) {
4
  'use strict';
5

    
6
  Foundation.libs.clearing = {
7
    name : 'clearing',
8

    
9
    version : '4.0.0',
10

    
11
    settings : {
12
      templates : {
13
        viewing : '<a href="#" class="clearing-close">&times;</a>' +
14
          '<div class="visible-img" style="display: none"><img src="//:0">' +
15
          '<p class="clearing-caption"></p><a href="#" class="clearing-main-left"><span></span></a>' +
16
          '<a href="#" class="clearing-main-right"><span></span></a></div>'
17
      },
18

    
19
      // comma delimited list of selectors that, on click, will close clearing,
20
      // add 'div.clearing-blackout, div.visible-img' to close on background click
21
      close_selectors : '.clearing-close',
22

    
23
      // event initializers and locks
24
      init : false,
25
      locked : false
26
    },
27

    
28
    init : function (scope, method, options) {
29
      this.scope = this.scope || scope;
30
      Foundation.inherit(this, 'set_data get_data remove_data throttle');
31

    
32
      if (typeof method === 'object') {
33
        options = $.extend(true, this.settings, method);
34
      }
35

    
36
      if (typeof method != 'string') {
37
        $(this.scope).find('ul[data-clearing]').each(function () {
38
          var self = Foundation.libs.clearing,
39
              $el = $(this),
40
              options = options || {},
41
              settings = self.get_data($el);
42

    
43
          if (!settings) {
44
            options.$parent = $el.parent();
45

    
46
            self.set_data($el, $.extend(true, self.settings, options));
47

    
48
            self.assemble($el.find('li'));
49

    
50
            if (!self.settings.init) {
51
              self.events().swipe_events();
52
            }
53
          }
54
        });
55

    
56
        return this.settings.init;
57
      } else {
58
        // fire method
59
        return this[method].call(this, options);
60
      }
61
    },
62

    
63
    // event binding and initial setup
64

    
65
    events : function () {
66
      var self = this;
67

    
68
      $(this.scope)
69
        .on('click.fndtn.clearing', 'ul[data-clearing] li',
70
          function (e, current, target) {
71
            var current = current || $(this),
72
                target = target || current,
73
                settings = self.get_data(current.parent());
74

    
75
            e.preventDefault();
76
            if (!settings) self.init();
77

    
78
            // set current and target to the clicked li if not otherwise defined.
79
            self.open($(e.target), current, target);
80
            self.update_paddles(target);
81
          })
82

    
83
        .on('click.fndtn.clearing', '.clearing-main-right',
84
          function (e) { this.nav(e, 'next') }.bind(this))
85
        .on('click.fndtn.clearing', '.clearing-main-left',
86
          function (e) { this.nav(e, 'prev') }.bind(this))
87
        .on('click.fndtn.clearing', this.settings.close_selectors,
88
          function (e) { Foundation.libs.clearing.close(e, this) })
89
        .on('keydown.fndtn.clearing',
90
          function (e) { this.keydown(e) }.bind(this));
91

    
92
      $(window).on('resize.fndtn.clearing',
93
        function (e) { this.resize() }.bind(this));
94

    
95
      this.settings.init = true;
96
      return this;
97
    },
98

    
99
    swipe_events : function () {
100
      var self = this;
101

    
102
      $(this.scope)
103
        .on('touchstart.fndtn.clearing', '.visible-img', function(e) {
104
          if (!e.touches) { e = e.originalEvent; }
105
          var data = {
106
                start_page_x: e.touches[0].pageX,
107
                start_page_y: e.touches[0].pageY,
108
                start_time: (new Date()).getTime(),
109
                delta_x: 0,
110
                is_scrolling: undefined
111
              };
112

    
113
          $(this).data('swipe-transition', data);
114
          e.stopPropagation();
115
        })
116
        .on('touchmove.fndtn.clearing', '.visible-img', function(e) {
117
          if (!e.touches) { e = e.originalEvent; }
118
          // Ignore pinch/zoom events
119
          if(e.touches.length > 1 || e.scale && e.scale !== 1) return;
120

    
121
          var data = $(this).data('swipe-transition');
122

    
123
          if (typeof data === 'undefined') {
124
            data = {};
125
          }
126

    
127
          data.delta_x = e.touches[0].pageX - data.start_page_x;
128

    
129
          if ( typeof data.is_scrolling === 'undefined') {
130
            data.is_scrolling = !!( data.is_scrolling || Math.abs(data.delta_x) < Math.abs(e.touches[0].pageY - data.start_page_y) );
131
          }
132

    
133
          if (!data.is_scrolling && !data.active) {
134
            e.preventDefault();
135
            var direction = (data.delta_x < 0) ? 'next' : 'prev';
136
            data.active = true;
137
            self.nav(e, direction);
138
          }
139
        })
140
        .on('touchend.fndtn.clearing', '.visible-img', function(e) {
141
          $(this).data('swipe-transition', {});
142
          e.stopPropagation();
143
        });
144
    },
145

    
146
    assemble : function ($li) {
147
      var $el = $li.parent(),
148
          settings = this.get_data($el),
149
          grid = $el.detach(),
150
          data = {
151
            grid: '<div class="carousel">' + this.outerHTML(grid[0]) + '</div>',
152
            viewing: settings.templates.viewing
153
          },
154
          wrapper = '<div class="clearing-assembled"><div>' + data.viewing +
155
            data.grid + '</div></div>';
156

    
157
      return settings.$parent.append(wrapper);
158
    },
159

    
160
    // event callbacks
161

    
162
    open : function ($image, current, target) {
163
      var root = target.closest('.clearing-assembled'),
164
          container = root.find('div').first(),
165
          visible_image = container.find('.visible-img'),
166
          image = visible_image.find('img').not($image);
167

    
168
      if (!this.locked()) {
169
        // set the image to the selected thumbnail
170
        image.attr('src', this.load($image));
171

    
172
        this.loaded(image, function () {
173
          // toggle the gallery
174
          root.addClass('clearing-blackout');
175
          container.addClass('clearing-container');
176
          visible_image.show();
177
          this.fix_height(target)
178
            .caption(visible_image.find('.clearing-caption'), $image)
179
            .center(image)
180
            .shift(current, target, function () {
181
              target.siblings().removeClass('visible');
182
              target.addClass('visible');
183
            });
184
        }.bind(this));
185
      }
186
    },
187

    
188
    close : function (e, el) {
189
      e.preventDefault();
190

    
191
      var root = (function (target) {
192
            if (/blackout/.test(target.selector)) {
193
              return target;
194
            } else {
195
              return target.closest('.clearing-blackout');
196
            }
197
          }($(el))), container, visible_image;
198

    
199
      if (el === e.target && root) {
200
        container = root.find('div').first(),
201
        visible_image = container.find('.visible-img');
202
        this.settings.prev_index = 0;
203
        root.find('ul[data-clearing]')
204
          .attr('style', '').closest('.clearing-blackout')
205
          .removeClass('clearing-blackout');
206
        container.removeClass('clearing-container');
207
        visible_image.hide();
208
      }
209

    
210
      return false;
211
    },
212

    
213
    keydown : function (e) {
214
      var clearing = $('.clearing-blackout').find('ul[data-clearing]');
215

    
216
      if (e.which === 39) this.go(clearing, 'next');
217
      if (e.which === 37) this.go(clearing, 'prev');
218
      if (e.which === 27) $('a.clearing-close').trigger('click');
219
    },
220

    
221
    nav : function (e, direction) {
222
      var clearing = $('.clearing-blackout').find('ul[data-clearing]');
223

    
224
      e.preventDefault();
225
      this.go(clearing, direction);
226
    },
227

    
228
    resize : function () {
229
      var image = $('.clearing-blackout .visible-img').find('img');
230

    
231
      if (image.length) {
232
        this.center(image);
233
      }
234
    },
235

    
236
    // visual adjustments
237
    fix_height : function (target) {
238
      var lis = target.parent().children(),
239
          self = this;
240

    
241
      lis.each(function () {
242
          var li = $(this),
243
              image = li.find('img');
244

    
245
          if (li.height() > self.outerHeight(image)) {
246
            li.addClass('fix-height');
247
          }
248
        })
249
        .closest('ul')
250
        .width(lis.length * 100 + '%');
251

    
252
      return this;
253
    },
254

    
255
    update_paddles : function (target) {
256
      var visible_image = target
257
        .closest('.carousel')
258
        .siblings('.visible-img');
259

    
260
      if (target.next().length) {
261
        visible_image
262
          .find('.clearing-main-right')
263
          .removeClass('disabled');
264
      } else {
265
        visible_image
266
          .find('.clearing-main-right')
267
          .addClass('disabled');
268
      }
269

    
270
      if (target.prev().length) {
271
        visible_image
272
          .find('.clearing-main-left')
273
          .removeClass('disabled');
274
      } else {
275
        visible_image
276
          .find('.clearing-main-left')
277
          .addClass('disabled');
278
      }
279
    },
280

    
281
    center : function (target) {
282
      target.css({
283
        marginLeft : -(this.outerWidth(target) / 2),
284
        marginTop : -(this.outerHeight(target) / 2)
285
      });
286
      return this;
287
    },
288

    
289
    // image loading and preloading
290

    
291
    load : function ($image) {
292
      var href = $image.parent().attr('href');
293

    
294
      this.preload($image);
295

    
296
      if (href) return href;
297
      return $image.attr('src');
298
    },
299

    
300
    preload : function ($image) {
301
      this
302
        .img($image.closest('li').next())
303
        .img($image.closest('li').prev());
304
    },
305

    
306
    loaded : function (image, callback) {
307
      // based on jquery.imageready.js
308
      // @weblinc, @jsantell, (c) 2012
309

    
310
      function loaded () {
311
        callback();
312
      }
313

    
314
      function bindLoad () {
315
        this.one('load', loaded);
316

    
317
        if (/MSIE (\d+\.\d+);/.test(navigator.userAgent)) {
318
          var src = this.attr( 'src' ),
319
              param = src.match( /\?/ ) ? '&' : '?';
320

    
321
          param += 'random=' + (new Date()).getTime();
322
          this.attr('src', src + param);
323
        }
324
      }
325

    
326
      if (!image.attr('src')) {
327
        loaded();
328
        return;
329
      }
330

    
331
      if (this.complete || this.readyState === 4) {
332
        loaded();
333
      } else {
334
        bindLoad.call(image);
335
      }
336
    },
337

    
338
    img : function (img) {
339
      if (img.length) {
340
        var new_img = new Image(),
341
            new_a = img.find('a');
342

    
343
        if (new_a.length) {
344
          new_img.src = new_a.attr('href');
345
        } else {
346
          new_img.src = img.find('img').attr('src');
347
        }
348
      }
349
      return this;
350
    },
351

    
352
    // image caption
353

    
354
    caption : function (container, $image) {
355
      var caption = $image.data('caption');
356

    
357
      if (caption) {
358
        container
359
          .text(caption)
360
          .show();
361
      } else {
362
        container
363
          .text('')
364
          .hide();
365
      }
366
      return this;
367
    },
368

    
369
    // directional methods
370

    
371
    go : function ($ul, direction) {
372
      var current = $ul.find('.visible'),
373
          target = current[direction]();
374

    
375
      if (target.length) {
376
        target
377
          .find('img')
378
          .trigger('click', [current, target]);
379
      }
380
    },
381

    
382
    shift : function (current, target, callback) {
383
      var clearing = target.parent(),
384
          old_index = this.settings.prev_index || target.index(),
385
          direction = this.direction(clearing, current, target),
386
          left = parseInt(clearing.css('left'), 10),
387
          width = this.outerWidth(target),
388
          skip_shift;
389

    
390
      // we use jQuery animate instead of CSS transitions because we
391
      // need a callback to unlock the next animation
392
      if (target.index() !== old_index && !/skip/.test(direction)){
393
        if (/left/.test(direction)) {
394
          this.lock();
395
          clearing.animate({left : left + width}, 300, this.unlock());
396
        } else if (/right/.test(direction)) {
397
          this.lock();
398
          clearing.animate({left : left - width}, 300, this.unlock());
399
        }
400
      } else if (/skip/.test(direction)) {
401
        // the target image is not adjacent to the current image, so
402
        // do we scroll right or not
403
        skip_shift = target.index() - this.settings.up_count;
404
        this.lock();
405

    
406
        if (skip_shift > 0) {
407
          clearing.animate({left : -(skip_shift * width)}, 300, this.unlock());
408
        } else {
409
          clearing.animate({left : 0}, 300, this.unlock());
410
        }
411
      }
412

    
413
      callback();
414
    },
415

    
416
    direction : function ($el, current, target) {
417
      var lis = $el.find('li'),
418
          li_width = this.outerWidth(lis) + (this.outerWidth(lis) / 4),
419
          up_count = Math.floor(this.outerWidth($('.clearing-container')) / li_width) - 1,
420
          target_index = lis.index(target),
421
          response;
422

    
423
      this.settings.up_count = up_count;
424

    
425
      if (this.adjacent(this.settings.prev_index, target_index)) {
426
        if ((target_index > up_count)
427
          && target_index > this.settings.prev_index) {
428
          response = 'right';
429
        } else if ((target_index > up_count - 1)
430
          && target_index <= this.settings.prev_index) {
431
          response = 'left';
432
        } else {
433
          response = false;
434
        }
435
      } else {
436
        response = 'skip';
437
      }
438

    
439
      this.settings.prev_index = target_index;
440

    
441
      return response;
442
    },
443

    
444
    adjacent : function (current_index, target_index) {
445
      for (var i = target_index + 1; i >= target_index - 1; i--) {
446
        if (i === current_index) return true;
447
      }
448
      return false;
449
    },
450

    
451
    // lock management
452

    
453
    lock : function () {
454
      this.settings.locked = true;
455
    },
456

    
457
    unlock : function () {
458
      this.settings.locked = false;
459
    },
460

    
461
    locked : function () {
462
      return this.settings.locked;
463
    },
464

    
465
    // plugin management/browser quirks
466

    
467
    outerHTML : function (el) {
468
      // support FireFox < 11
469
      return el.outerHTML || new XMLSerializer().serializeToString(el);
470
    },
471

    
472
    off : function () {
473
      $(this.scope).off('.fndtn.clearing');
474
      $(window).off('.fndtn.clearing');
475
      this.remove_data(); // empty settings cache
476
      this.settings.init = false;
477
    }
478
  };
479

    
480
}(Foundation.zj, this, this.document));