bootstrap-addons-rails 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (32) hide show
  1. data/.document +5 -0
  2. data/.rspec +1 -0
  3. data/Gemfile +13 -0
  4. data/Gemfile.lock +109 -0
  5. data/LICENSE.txt +20 -0
  6. data/README.md +54 -0
  7. data/Rakefile +49 -0
  8. data/VERSION +1 -0
  9. data/bootstrap-addons-rails.gemspec +85 -0
  10. data/lib/bootstrap-addons-rails.rb +6 -0
  11. data/spec/spec_helper.rb +6 -0
  12. data/vendor/assets/colorpicker/css/colorpicker.css +127 -0
  13. data/vendor/assets/colorpicker/img/alpha.png +0 -0
  14. data/vendor/assets/colorpicker/img/hue.png +0 -0
  15. data/vendor/assets/colorpicker/img/saturation.png +0 -0
  16. data/vendor/assets/colorpicker/js/bootstrap-colorpicker.js +520 -0
  17. data/vendor/assets/colorpicker/less/colorpicker.less +89 -0
  18. data/vendor/assets/images/bootstrap/colorpicker/alpha.png +0 -0
  19. data/vendor/assets/images/bootstrap/colorpicker/hue.png +0 -0
  20. data/vendor/assets/images/bootstrap/colorpicker/saturation.png +0 -0
  21. data/vendor/assets/images/bootstrap/image-gallery/loading.gif +0 -0
  22. data/vendor/assets/javascripts/bootstrap/colorpicker.js +520 -0
  23. data/vendor/assets/javascripts/bootstrap/datepicker.js +401 -0
  24. data/vendor/assets/javascripts/bootstrap/image-gallery.js +387 -0
  25. data/vendor/assets/javascripts/bootstrap/image-gallery.min.js +1 -0
  26. data/vendor/assets/less/bootstrap/colorpicker.less +89 -0
  27. data/vendor/assets/less/bootstrap/datepicker.less +122 -0
  28. data/vendor/assets/stylesheets/bootstrap/colorpicker.css +127 -0
  29. data/vendor/assets/stylesheets/bootstrap/datepicker.css +156 -0
  30. data/vendor/assets/stylesheets/bootstrap/image-gallery.css +141 -0
  31. data/vendor/assets/stylesheets/bootstrap/image-gallery.min.css +21 -0
  32. metadata +178 -0
@@ -0,0 +1,401 @@
1
+ /* =========================================================
2
+ * bootstrap-datepicker.js
3
+ * http://www.eyecon.ro/bootstrap-datepicker
4
+ * =========================================================
5
+ * Copyright 2012 Stefan Petre
6
+ *
7
+ * Licensed under the Apache License, Version 2.0 (the "License");
8
+ * you may not use this file except in compliance with the License.
9
+ * You may obtain a copy of the License at
10
+ *
11
+ * http://www.apache.org/licenses/LICENSE-2.0
12
+ *
13
+ * Unless required by applicable law or agreed to in writing, software
14
+ * distributed under the License is distributed on an "AS IS" BASIS,
15
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16
+ * See the License for the specific language governing permissions and
17
+ * limitations under the License.
18
+ * ========================================================= */
19
+
20
+ !function( $ ) {
21
+
22
+ // Picker object
23
+
24
+ var Datepicker = function(element, options){
25
+ this.element = $(element);
26
+ this.format = DPGlobal.parseFormat(options.format||this.element.data('date-format')||'mm/dd/yyyy');
27
+ this.picker = $(DPGlobal.template)
28
+ .appendTo('body')
29
+ .on({
30
+ click: $.proxy(this.click, this),
31
+ mousedown: $.proxy(this.mousedown, this)
32
+ });
33
+ this.isInput = this.element.is('input');
34
+ this.component = this.element.is('.date') ? this.element.find('.add-on') : false;
35
+
36
+ if (this.isInput) {
37
+ this.element.on({
38
+ focus: $.proxy(this.show, this),
39
+ blur: $.proxy(this.hide, this),
40
+ keyup: $.proxy(this.update, this)
41
+ });
42
+ } else {
43
+ if (this.component){
44
+ this.component.on('click', $.proxy(this.show, this));
45
+ } else {
46
+ this.element.on('click', $.proxy(this.show, this));
47
+ }
48
+ }
49
+
50
+ this.viewMode = 0;
51
+ this.weekStart = options.weekStart||this.element.data('date-weekstart')||0;
52
+ this.weekEnd = this.weekStart == 0 ? 6 : this.weekStart - 1;
53
+ this.fillDow();
54
+ this.fillMonths();
55
+ this.update();
56
+ this.showMode();
57
+ };
58
+
59
+ Datepicker.prototype = {
60
+ constructor: Datepicker,
61
+
62
+ show: function(e) {
63
+ this.picker.show();
64
+ this.height = this.component ? this.component.outerHeight() : this.element.outerHeight();
65
+ this.place();
66
+ $(window).on('resize', $.proxy(this.place, this));
67
+ if (e ) {
68
+ e.stopPropagation();
69
+ e.preventDefault();
70
+ }
71
+ if (!this.isInput) {
72
+ $(document).on('mousedown', $.proxy(this.hide, this));
73
+ }
74
+ this.element.trigger({
75
+ type: 'show',
76
+ date: this.date
77
+ });
78
+ },
79
+
80
+ hide: function(){
81
+ this.picker.hide();
82
+ $(window).off('resize', this.place);
83
+ this.viewMode = 0;
84
+ this.showMode();
85
+ if (!this.isInput) {
86
+ $(document).off('mousedown', this.hide);
87
+ }
88
+ this.setValue();
89
+ this.element.trigger({
90
+ type: 'hide',
91
+ date: this.date
92
+ });
93
+ },
94
+
95
+ setValue: function() {
96
+ var formated = DPGlobal.formatDate(this.date, this.format);
97
+ if (!this.isInput) {
98
+ if (this.component){
99
+ this.element.find('input').prop('value', formated);
100
+ }
101
+ this.element.data('date', formated);
102
+ } else {
103
+ this.element.prop('value', formated);
104
+ }
105
+ },
106
+
107
+ place: function(){
108
+ var offset = this.component ? this.component.offset() : this.element.offset();
109
+ this.picker.css({
110
+ top: offset.top + this.height,
111
+ left: offset.left
112
+ });
113
+ },
114
+
115
+ update: function(){
116
+ this.date = DPGlobal.parseDate(
117
+ this.isInput ? this.element.prop('value') : this.element.data('date'),
118
+ this.format
119
+ );
120
+ this.viewDate = new Date(this.date);
121
+ this.fill();
122
+ },
123
+
124
+ fillDow: function(){
125
+ var dowCnt = this.weekStart;
126
+ var html = '<tr>';
127
+ while (dowCnt < this.weekStart + 7) {
128
+ html += '<th class="dow">'+DPGlobal.dates.daysMin[(dowCnt++)%7]+'</th>';
129
+ }
130
+ html += '</tr>';
131
+ this.picker.find('.datepicker-days thead').append(html);
132
+ },
133
+
134
+ fillMonths: function(){
135
+ var html = '';
136
+ var i = 0
137
+ while (i < 12) {
138
+ html += '<span class="month">'+DPGlobal.dates.monthsShort[i++]+'</span>';
139
+ }
140
+ this.picker.find('.datepicker-months td').append(html);
141
+ },
142
+
143
+ fill: function() {
144
+ var d = new Date(this.viewDate),
145
+ year = d.getFullYear(),
146
+ month = d.getMonth(),
147
+ currentDate = this.date.valueOf();
148
+ this.picker.find('.datepicker-days th:eq(1)')
149
+ .text(DPGlobal.dates.months[month]+' '+year);
150
+ var prevMonth = new Date(year, month-1, 28,0,0,0,0),
151
+ day = DPGlobal.getDaysInMonth(prevMonth.getFullYear(), prevMonth.getMonth());
152
+ prevMonth.setDate(day);
153
+ prevMonth.setDate(day - (prevMonth.getDay() - this.weekStart + 7)%7);
154
+ var nextMonth = new Date(prevMonth);
155
+ nextMonth.setDate(nextMonth.getDate() + 42);
156
+ nextMonth = nextMonth.valueOf();
157
+ html = [];
158
+ var clsName;
159
+ while(prevMonth.valueOf() < nextMonth) {
160
+ if (prevMonth.getDay() == this.weekStart) {
161
+ html.push('<tr>');
162
+ }
163
+ clsName = '';
164
+ if (prevMonth.getMonth() < month) {
165
+ clsName += ' old';
166
+ } else if (prevMonth.getMonth() > month) {
167
+ clsName += ' new';
168
+ }
169
+ if (prevMonth.valueOf() == currentDate) {
170
+ clsName += ' active';
171
+ }
172
+ html.push('<td class="day'+clsName+'">'+prevMonth.getDate() + '</td>');
173
+ if (prevMonth.getDay() == this.weekEnd) {
174
+ html.push('</tr>');
175
+ }
176
+ prevMonth.setDate(prevMonth.getDate()+1);
177
+ }
178
+ this.picker.find('.datepicker-days tbody').empty().append(html.join(''));
179
+ var currentYear = this.date.getFullYear();
180
+
181
+ var months = this.picker.find('.datepicker-months')
182
+ .find('th:eq(1)')
183
+ .text(year)
184
+ .end()
185
+ .find('span').removeClass('active');
186
+ if (currentYear == year) {
187
+ months.eq(this.date.getMonth()).addClass('active');
188
+ }
189
+
190
+ html = '';
191
+ year = parseInt(year/10, 10) * 10;
192
+ var yearCont = this.picker.find('.datepicker-years')
193
+ .find('th:eq(1)')
194
+ .text(year + '-' + (year + 9))
195
+ .end()
196
+ .find('td');
197
+ year -= 1;
198
+ for (var i = -1; i < 11; i++) {
199
+ html += '<span class="year'+(i == -1 || i == 10 ? ' old' : '')+(currentYear == year ? ' active' : '')+'">'+year+'</span>';
200
+ year += 1;
201
+ }
202
+ yearCont.html(html);
203
+ },
204
+
205
+ click: function(e) {
206
+ e.stopPropagation();
207
+ e.preventDefault();
208
+ var target = $(e.target).closest('span, td, th');
209
+ if (target.length == 1) {
210
+ switch(target[0].nodeName.toLowerCase()) {
211
+ case 'th':
212
+ switch(target[0].className) {
213
+ case 'switch':
214
+ this.showMode(1);
215
+ break;
216
+ case 'prev':
217
+ case 'next':
218
+ this.viewDate['set'+DPGlobal.modes[this.viewMode].navFnc].call(
219
+ this.viewDate,
220
+ this.viewDate['get'+DPGlobal.modes[this.viewMode].navFnc].call(this.viewDate) +
221
+ DPGlobal.modes[this.viewMode].navStep * (target[0].className == 'prev' ? -1 : 1)
222
+ );
223
+ this.fill();
224
+ break;
225
+ }
226
+ break;
227
+ case 'span':
228
+ if (target.is('.month')) {
229
+ var month = target.parent().find('span').index(target);
230
+ this.viewDate.setMonth(month);
231
+ } else {
232
+ var year = parseInt(target.text(), 10)||0;
233
+ this.viewDate.setFullYear(year);
234
+ }
235
+ this.showMode(-1);
236
+ this.fill();
237
+ break;
238
+ case 'td':
239
+ if (target.is('.day')){
240
+ var day = parseInt(target.text(), 10)||1;
241
+ var month = this.viewDate.getMonth();
242
+ if (target.is('.old')) {
243
+ month -= 1;
244
+ } else if (target.is('.new')) {
245
+ month += 1;
246
+ }
247
+ var year = this.viewDate.getFullYear();
248
+ this.date = new Date(year, month, day,0,0,0,0);
249
+ this.viewDate = new Date(year, month, day,0,0,0,0);
250
+ this.fill();
251
+ this.setValue();
252
+ this.element.trigger({
253
+ type: 'changeDate',
254
+ date: this.date
255
+ });
256
+ }
257
+ break;
258
+ }
259
+ }
260
+ },
261
+
262
+ mousedown: function(e){
263
+ e.stopPropagation();
264
+ e.preventDefault();
265
+ },
266
+
267
+ showMode: function(dir) {
268
+ if (dir) {
269
+ this.viewMode = Math.max(0, Math.min(2, this.viewMode + dir));
270
+ }
271
+ this.picker.find('>div').hide().filter('.datepicker-'+DPGlobal.modes[this.viewMode].clsName).show();
272
+ }
273
+ };
274
+
275
+ $.fn.datepicker = function ( option ) {
276
+ return this.each(function () {
277
+ var $this = $(this),
278
+ data = $this.data('datepicker'),
279
+ options = typeof option == 'object' && option;
280
+ if (!data) {
281
+ $this.data('datepicker', (data = new Datepicker(this, $.extend({}, $.fn.datepicker.defaults,options))));
282
+ }
283
+ if (typeof option == 'string') data[option]();
284
+ });
285
+ };
286
+
287
+ $.fn.datepicker.defaults = {
288
+ };
289
+ $.fn.datepicker.Constructor = Datepicker;
290
+
291
+ var DPGlobal = {
292
+ modes: [
293
+ {
294
+ clsName: 'days',
295
+ navFnc: 'Month',
296
+ navStep: 1
297
+ },
298
+ {
299
+ clsName: 'months',
300
+ navFnc: 'FullYear',
301
+ navStep: 1
302
+ },
303
+ {
304
+ clsName: 'years',
305
+ navFnc: 'FullYear',
306
+ navStep: 10
307
+ }],
308
+ dates:{
309
+ days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
310
+ daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
311
+ daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
312
+ months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
313
+ monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
314
+ },
315
+ isLeapYear: function (year) {
316
+ return (((year % 4 === 0) && (year % 100 !== 0)) || (year % 400 === 0))
317
+ },
318
+ getDaysInMonth: function (year, month) {
319
+ return [31, (DPGlobal.isLeapYear(year) ? 29 : 28), 31, 30, 31, 30, 31, 31, 30, 31, 30, 31][month]
320
+ },
321
+ parseFormat: function(format){
322
+ var separator = format.match(/[.\/-].*?/),
323
+ parts = format.split(/\W+/);
324
+ if (!separator || !parts || parts.length == 0){
325
+ throw new Error("Invalid date format.");
326
+ }
327
+ return {separator: separator, parts: parts};
328
+ },
329
+ parseDate: function(date, format) {
330
+ var parts = date.split(format.separator),
331
+ date = new Date(1970, 1, 1, 0, 0, 0),
332
+ val;
333
+ if (parts.length == format.parts.length) {
334
+ for (var i=0, cnt = format.parts.length; i < cnt; i++) {
335
+ val = parseInt(parts[i], 10)||1;
336
+ switch(format.parts[i]) {
337
+ case 'dd':
338
+ case 'd':
339
+ date.setDate(val);
340
+ break;
341
+ case 'mm':
342
+ case 'm':
343
+ date.setMonth(val - 1);
344
+ break;
345
+ case 'yy':
346
+ date.setFullYear(2000 + val);
347
+ break;
348
+ case 'yyyy':
349
+ date.setFullYear(val);
350
+ break;
351
+ }
352
+ }
353
+ }
354
+ return date;
355
+ },
356
+ formatDate: function(date, format){
357
+ var val = {
358
+ d: date.getDate(),
359
+ m: date.getMonth() + 1,
360
+ yy: date.getFullYear().toString().substring(2),
361
+ yyyy: date.getFullYear()
362
+ };
363
+ val.dd = (val.d < 10 ? '0' : '') + val.d;
364
+ val.mm = (val.m < 10 ? '0' : '') + val.m;
365
+ var date = [];
366
+ for (var i=0, cnt = format.parts.length; i < cnt; i++) {
367
+ date.push(val[format.parts[i]]);
368
+ }
369
+ return date.join(format.separator);
370
+ },
371
+ headTemplate: '<thead>'+
372
+ '<tr>'+
373
+ '<th class="prev"><i class="icon-arrow-left"/></th>'+
374
+ '<th colspan="5" class="switch"></th>'+
375
+ '<th class="next"><i class="icon-arrow-right"/></th>'+
376
+ '</tr>'+
377
+ '</thead>',
378
+ contTemplate: '<tbody><tr><td colspan="7"></td></tr></tbody>'
379
+ };
380
+ DPGlobal.template = '<div class="datepicker dropdown-menu">'+
381
+ '<div class="datepicker-days">'+
382
+ '<table class=" table-condensed">'+
383
+ DPGlobal.headTemplate+
384
+ '<tbody></tbody>'+
385
+ '</table>'+
386
+ '</div>'+
387
+ '<div class="datepicker-months">'+
388
+ '<table class="table-condensed">'+
389
+ DPGlobal.headTemplate+
390
+ DPGlobal.contTemplate+
391
+ '</table>'+
392
+ '</div>'+
393
+ '<div class="datepicker-years">'+
394
+ '<table class="table-condensed">'+
395
+ DPGlobal.headTemplate+
396
+ DPGlobal.contTemplate+
397
+ '</table>'+
398
+ '</div>'+
399
+ '</div>';
400
+
401
+ }( window.jQuery )
@@ -0,0 +1,387 @@
1
+ /*
2
+ * Bootstrap Image Gallery 2.8
3
+ * https://github.com/blueimp/Bootstrap-Image-Gallery
4
+ *
5
+ * Copyright 2011, Sebastian Tschan
6
+ * https://blueimp.net
7
+ *
8
+ * Licensed under the MIT license:
9
+ * http://www.opensource.org/licenses/MIT
10
+ */
11
+
12
+ /*jslint nomen: true, regexp: true */
13
+ /*global define, window, document, jQuery */
14
+
15
+ (function (factory) {
16
+ 'use strict';
17
+ if (typeof define === 'function' && define.amd) {
18
+ // Register as an anonymous AMD module:
19
+ define([
20
+ 'jquery',
21
+ 'load-image',
22
+ 'bootstrap'
23
+ ], factory);
24
+ } else {
25
+ // Browser globals:
26
+ factory(
27
+ window.jQuery,
28
+ window.loadImage
29
+ );
30
+ }
31
+ }(function ($, loadImage) {
32
+ 'use strict';
33
+ // Bootstrap Image Gallery is an extension to the Modal dialog of Twitter's
34
+ // Bootstrap toolkit, to ease navigation between a set of gallery images.
35
+ // It features transition effects, fullscreen mode and slideshow functionality.
36
+ $.extend($.fn.modal.defaults, {
37
+ // Delegate to search gallery links from, can be anything that
38
+ // is accepted as parameter for $():
39
+ delegate: document,
40
+ // Selector for gallery links:
41
+ selector: null,
42
+ // The filter for the selected gallery links (e.g. set to ":odd" to
43
+ // filter out label and thumbnail linking twice to the same image):
44
+ filter: '*',
45
+ // The index of the first gallery image to show:
46
+ index: 0,
47
+ // The href of the first gallery image to show (overrides index):
48
+ href: null,
49
+ // The range of images around the current one to preload:
50
+ preloadRange: 2,
51
+ // Offset of image width to viewport width:
52
+ offsetWidth: 100,
53
+ // Offset of image height to viewport height:
54
+ offsetHeight: 200,
55
+ // Set to true to display images as canvas elements:
56
+ canvas: false,
57
+ // Shows the next image after the given time in ms (0 = disabled):
58
+ slideshow: 0,
59
+ // Defines the image division for previous/next clicks:
60
+ imageClickDivision: 0.5
61
+ });
62
+ var originalShow = $.fn.modal.Constructor.prototype.show,
63
+ originalHide = $.fn.modal.Constructor.prototype.hide;
64
+ $.extend($.fn.modal.Constructor.prototype, {
65
+ initLinks: function () {
66
+ var $this = this,
67
+ options = this.options,
68
+ selector = options.selector ||
69
+ 'a[data-target=' + options.target + ']';
70
+ this.$links = $(options.delegate).find(selector)
71
+ .filter(options.filter).each(function (index) {
72
+ if ($this.getUrl(this) === options.href) {
73
+ options.index = index;
74
+ }
75
+ });
76
+ if (!this.$links[options.index]) {
77
+ options.index = 0;
78
+ }
79
+ },
80
+ getUrl: function (element) {
81
+ return element.href || $(element).data('href');
82
+ },
83
+ startSlideShow: function () {
84
+ var $this = this;
85
+ if (this.options.slideshow) {
86
+ this._slideShow = window.setTimeout(
87
+ function () {
88
+ $this.next();
89
+ },
90
+ this.options.slideshow
91
+ );
92
+ }
93
+ },
94
+ stopSlideShow: function () {
95
+ window.clearTimeout(this._slideShow);
96
+ },
97
+ toggleSlideShow: function () {
98
+ var node = this.$element.find('.modal-slideshow');
99
+ if (this.options.slideshow) {
100
+ this.options.slideshow = 0;
101
+ this.stopSlideShow();
102
+ } else {
103
+ this.options.slideshow = node.data('slideshow') || 5000;
104
+ this.startSlideShow();
105
+ }
106
+ node.find('i').toggleClass('icon-play icon-pause');
107
+ },
108
+ preloadImages: function () {
109
+ var options = this.options,
110
+ range = options.index + options.preloadRange + 1,
111
+ link,
112
+ i;
113
+ for (i = options.index - options.preloadRange; i < range; i += 1) {
114
+ link = this.$links[i];
115
+ if (link && i !== options.index) {
116
+ $('<img>').prop('src', this.getUrl(link));
117
+ }
118
+ }
119
+ },
120
+ loadImage: function () {
121
+ var $this = this,
122
+ modal = this.$element,
123
+ index = this.options.index,
124
+ url = this.getUrl(this.$links[index]),
125
+ oldImg;
126
+ this.abortLoad();
127
+ this.stopSlideShow();
128
+ modal.trigger('beforeLoad');
129
+ // The timeout prevents displaying a loading status,
130
+ // if the image has already been loaded:
131
+ this._loadingTimeout = window.setTimeout(function () {
132
+ modal.addClass('modal-loading');
133
+ }, 100);
134
+ oldImg = modal.find('.modal-image').children().removeClass('in');
135
+ // The timeout allows transition effects to finish:
136
+ window.setTimeout(function () {
137
+ oldImg.remove();
138
+ }, 3000);
139
+ modal.find('.modal-title').text(this.$links[index].title);
140
+ modal.find('.modal-download').prop(
141
+ 'href',
142
+ url
143
+ );
144
+ this._loadingImage = loadImage(
145
+ url,
146
+ function (img) {
147
+ $this.img = img;
148
+ window.clearTimeout($this._loadingTimeout);
149
+ modal.removeClass('modal-loading');
150
+ modal.trigger('load');
151
+ $this.showImage(img);
152
+ $this.startSlideShow();
153
+ },
154
+ this._loadImageOptions
155
+ );
156
+ this.preloadImages();
157
+ },
158
+ showImage: function (img) {
159
+ var modal = this.$element,
160
+ transition = $.support.transition && modal.hasClass('fade'),
161
+ method = transition ? modal.animate : modal.css,
162
+ modalImage = modal.find('.modal-image'),
163
+ clone,
164
+ forceReflow;
165
+ modalImage.css({
166
+ width: img.width,
167
+ height: img.height
168
+ });
169
+ modal.find('.modal-title').css({ width: Math.max(img.width, 380) });
170
+ if ($(window).width() > 480) {
171
+ if (transition) {
172
+ clone = modal.clone().hide().appendTo(document.body);
173
+ }
174
+ method.call(modal.stop(), {
175
+ 'margin-top': -((clone || modal).outerHeight() / 2),
176
+ 'margin-left': -((clone || modal).outerWidth() / 2)
177
+ });
178
+ if (clone) {
179
+ clone.remove();
180
+ }
181
+ }
182
+ modalImage.append(img);
183
+ forceReflow = img.offsetWidth;
184
+ modal.trigger('display');
185
+ if (transition) {
186
+ if (modal.is(':visible')) {
187
+ $(img).on(
188
+ $.support.transition.end,
189
+ function (e) {
190
+ // Make sure we don't respond to other transitions events
191
+ // in the container element, e.g. from button elements:
192
+ if (e.target === img) {
193
+ $(img).off($.support.transition.end);
194
+ modal.trigger('displayed');
195
+ }
196
+ }
197
+ ).addClass('in');
198
+ } else {
199
+ $(img).addClass('in');
200
+ modal.one('shown', function () {
201
+ modal.trigger('displayed');
202
+ });
203
+ }
204
+ } else {
205
+ $(img).addClass('in');
206
+ modal.trigger('displayed');
207
+ }
208
+ },
209
+ abortLoad: function () {
210
+ if (this._loadingImage) {
211
+ this._loadingImage.onload = this._loadingImage.onerror = null;
212
+ }
213
+ window.clearTimeout(this._loadingTimeout);
214
+ },
215
+ prev: function () {
216
+ var options = this.options;
217
+ options.index -= 1;
218
+ if (options.index < 0) {
219
+ options.index = this.$links.length - 1;
220
+ }
221
+ this.loadImage();
222
+ },
223
+ next: function () {
224
+ var options = this.options;
225
+ options.index += 1;
226
+ if (options.index > this.$links.length - 1) {
227
+ options.index = 0;
228
+ }
229
+ this.loadImage();
230
+ },
231
+ keyHandler: function (e) {
232
+ switch (e.which) {
233
+ case 37: // left
234
+ case 38: // up
235
+ e.preventDefault();
236
+ this.prev();
237
+ break;
238
+ case 39: // right
239
+ case 40: // down
240
+ e.preventDefault();
241
+ this.next();
242
+ break;
243
+ }
244
+ },
245
+ wheelHandler: function (e) {
246
+ e.preventDefault();
247
+ e = e.originalEvent;
248
+ this._wheelCounter = this._wheelCounter || 0;
249
+ this._wheelCounter += (e.wheelDelta || e.detail || 0);
250
+ if ((e.wheelDelta && this._wheelCounter >= 120) ||
251
+ (!e.wheelDelta && this._wheelCounter < 0)) {
252
+ this.prev();
253
+ this._wheelCounter = 0;
254
+ } else if ((e.wheelDelta && this._wheelCounter <= -120) ||
255
+ (!e.wheelDelta && this._wheelCounter > 0)) {
256
+ this.next();
257
+ this._wheelCounter = 0;
258
+ }
259
+ },
260
+ initGalleryEvents: function () {
261
+ var $this = this,
262
+ modal = this.$element;
263
+ modal.find('.modal-image').on('click.modal-gallery', function (e) {
264
+ var modalImage = $(this);
265
+ if ($this.$links.length === 1) {
266
+ $this.hide();
267
+ } else {
268
+ if ((e.pageX - modalImage.offset().left) / modalImage.width() <
269
+ $this.options.imageClickDivision) {
270
+ $this.prev(e);
271
+ } else {
272
+ $this.next(e);
273
+ }
274
+ }
275
+ });
276
+ modal.find('.modal-prev').on('click.modal-gallery', function (e) {
277
+ $this.prev(e);
278
+ });
279
+ modal.find('.modal-next').on('click.modal-gallery', function (e) {
280
+ $this.next(e);
281
+ });
282
+ modal.find('.modal-slideshow').on('click.modal-gallery', function (e) {
283
+ $this.toggleSlideShow(e);
284
+ });
285
+ $(document)
286
+ .on('keydown.modal-gallery', function (e) {
287
+ $this.keyHandler(e);
288
+ })
289
+ .on(
290
+ 'mousewheel.modal-gallery, DOMMouseScroll.modal-gallery',
291
+ function (e) {
292
+ $this.wheelHandler(e);
293
+ }
294
+ );
295
+ },
296
+ destroyGalleryEvents: function () {
297
+ var modal = this.$element;
298
+ this.abortLoad();
299
+ this.stopSlideShow();
300
+ modal.find('.modal-image, .modal-prev, .modal-next, .modal-slideshow')
301
+ .off('click.modal-gallery');
302
+ $(document)
303
+ .off('keydown.modal-gallery')
304
+ .off('mousewheel.modal-gallery, DOMMouseScroll.modal-gallery');
305
+ },
306
+ show: function () {
307
+ if (!this.isShown && this.$element.hasClass('modal-gallery')) {
308
+ var modal = this.$element,
309
+ options = this.options,
310
+ windowWidth = $(window).width(),
311
+ windowHeight = $(window).height();
312
+ if (modal.hasClass('modal-fullscreen')) {
313
+ this._loadImageOptions = {
314
+ maxWidth: windowWidth,
315
+ maxHeight: windowHeight,
316
+ canvas: options.canvas
317
+ };
318
+ if (modal.hasClass('modal-fullscreen-stretch')) {
319
+ this._loadImageOptions.minWidth = windowWidth;
320
+ this._loadImageOptions.minHeight = windowHeight;
321
+ }
322
+ } else {
323
+ this._loadImageOptions = {
324
+ maxWidth: windowWidth - options.offsetWidth,
325
+ maxHeight: windowHeight - options.offsetHeight,
326
+ canvas: options.canvas
327
+ };
328
+ }
329
+ if (windowWidth > 480) {
330
+ modal.css({
331
+ 'margin-top': -(modal.outerHeight() / 2),
332
+ 'margin-left': -(modal.outerWidth() / 2)
333
+ });
334
+ }
335
+ this.initGalleryEvents();
336
+ this.initLinks();
337
+ if (this.$links.length) {
338
+ modal.find('.modal-slideshow, .modal-prev, .modal-next')
339
+ .toggle(this.$links.length !== 1);
340
+ modal.toggleClass(
341
+ 'modal-single',
342
+ this.$links.length === 1
343
+ );
344
+ this.loadImage();
345
+ }
346
+ }
347
+ originalShow.apply(this, arguments);
348
+ },
349
+ hide: function () {
350
+ if (this.isShown && this.$element.hasClass('modal-gallery')) {
351
+ this.options.delegate = document;
352
+ this.options.href = null;
353
+ this.destroyGalleryEvents();
354
+ }
355
+ originalHide.apply(this, arguments);
356
+ }
357
+ });
358
+ $(function () {
359
+ $(document.body).on(
360
+ 'click.modal-gallery.data-api',
361
+ '[data-toggle="modal-gallery"]',
362
+ function (e) {
363
+ var $this = $(this),
364
+ options = $this.data(),
365
+ modal = $(options.target),
366
+ data = modal.data('modal'),
367
+ link;
368
+ if (!data) {
369
+ options = $.extend(modal.data(), options);
370
+ }
371
+ if (!options.selector) {
372
+ options.selector = 'a[rel=gallery]';
373
+ }
374
+ link = $(e.target).closest(options.selector);
375
+ if (link.length && modal.length) {
376
+ e.preventDefault();
377
+ options.href = link.prop('href') || link.data('href');
378
+ options.delegate = link[0] !== this ? this : document;
379
+ if (data) {
380
+ $.extend(data.options, options);
381
+ }
382
+ modal.modal(options);
383
+ }
384
+ }
385
+ );
386
+ });
387
+ }));