hermitage 0.0.1 → 0.0.2.1

Sign up to get free protection for your applications and to get access to all the features.
data/CHANGELOG.md ADDED
@@ -0,0 +1,17 @@
1
+ ### 0.0.2.1 ###
2
+
3
+ * Fixed conflict with Twitter Bootstrap in Firefox and Opera.
4
+
5
+ ### 0.0.2 ###
6
+
7
+ * Added ability to split the gallery into several parts by `each_slice` option
8
+ * Added ability to customize image viewer appearance
9
+ * Close button added to the image viewer
10
+ * Fixed bug when image was incorrectly positioned because of scroll
11
+ * Removed raising exception when the first argument in `render_gallery_for` method is not Array
12
+ * Fixed the way of the image path evaluation
13
+ * CoffeeScript code refactoring
14
+
15
+ ### 0.0.1 ###
16
+
17
+ The first release.
data/README.md CHANGED
@@ -1,5 +1,7 @@
1
1
  # Hermitage
2
2
 
3
+ [![Build Status](https://travis-ci.org/ImmaculatePine/hermitage.png?branch=master)](https://travis-ci.org/ImmaculatePine/hermitage)
4
+
3
5
  Ruby library for generation of image galleries (thumbnails and full size images viewer).
4
6
 
5
7
  ## Requirements
@@ -38,7 +40,7 @@ The example from Quick Start section works well when you are using Paperclip gem
38
40
 
39
41
  class Image < ActiveRecord::Base
40
42
  attr_accessible :file
41
- has_attached_file :file, styles: { thumbnail: "100x100>" }
43
+ has_attached_file :file, styles: { thumbnail: '100x100>' }
42
44
  end
43
45
 
44
46
  Then
@@ -94,6 +96,17 @@ will render the following markup:
94
96
  </p>
95
97
  </div>
96
98
 
99
+ #### Slicing
100
+
101
+ If you are using Twitter Bootstrap framework and your gallery is inside `.row-fluid` block the markup above will not look awesome.
102
+ Or maybe you have any other reasons to split the gallery into several separate galleries.
103
+ Then pass `each_slice` options to `render_gallery_for` method:
104
+
105
+ @images = Array.new(5, Image.new) # weird, but it's just an example
106
+ render_gallery_for @images, each_slice: 3
107
+
108
+ This code will render 2 `ul` tags with 3 and 2 items in each, respectively. Nevertheless they both will be available in navigation flow when you open the image viewer.
109
+
97
110
  ### Configuration
98
111
 
99
112
  It is more handy to use configs to customize Hermitage behavior.
@@ -104,7 +117,7 @@ If there is no proper config :default config is used.
104
117
 
105
118
  Hermitage configs are described in config/initializers/hermitage.rb file.
106
119
 
107
- #### Overwrite Defaults
120
+ #### Overwriting Defaults
108
121
 
109
122
  You can overwrite :default config. These changes will be applied to all the galleries in your application.
110
123
 
@@ -133,7 +146,7 @@ and
133
146
 
134
147
  class Post < ActiveRecord::Base
135
148
  attr_accessible :attachment
136
- has_attached_file :attachment, styles: { tiny: "200x200>" }
149
+ has_attached_file :attachment, styles: { tiny: '200x200>' }
137
150
  end
138
151
 
139
152
  Suppose that pictures should be rendered with Twitter Bootstrap style, but posts should be wrapped by simple blocks.
@@ -146,7 +159,7 @@ Then your config/initializers/hermitage.rb could looks like this:
146
159
  attribute_thumbnail: 'image_path(:small)'
147
160
  }
148
161
 
149
- Hermitage.configs[:pictures] = {
162
+ Hermitage.configs[:posts] = {
150
163
  attribute_full_size: 'attachment',
151
164
  attribute_thumbnail: 'attachment(:tiny)',
152
165
  list_tag: :div,
@@ -166,6 +179,35 @@ So, Hermitage looks for parameters with the following priority:
166
179
  * Then it overwrites some of them by custom config's parameters if they were specified;
167
180
  * Finally it overwrites both of them by the values from options hash passed to `render_gallery_for` method (if there are such values, of course).
168
181
 
182
+ ### Viewer Customization
183
+
184
+ You can customize appearance of Hermitage image viewer. All you need is to add to any of your .js or .coffee files lines like this:
185
+
186
+ hermitage.darkening.opacity = 0
187
+ hermitage.navigationButton.color = '#faeedd'
188
+
189
+ In the example above the darkening will be disabled and navigation buttons will change their color.
190
+
191
+ You can customize the following parameters:
192
+
193
+ * `zIndex` - image viewer's z-index property
194
+ * `darkening.opacity` - opacity of darkening layer (0 if it should be disabled)
195
+ * `darkening.color` - color of darkening layer
196
+ * `navigationButton.enabled` - are there navigation buttons
197
+ * `navigationButton.color` - color of navigation buttons
198
+ * `navigationButton.width` - width of navigation buttons, px
199
+ * `navigationButton.borderRadius` - border radius of outer corners of navigation buttons, px
200
+ * `navigationButton.margin` - distance between navigation buttons and the image, px
201
+ * `closeButton.enabled` - is there close button
202
+ * `closeButton.text` - close button's text
203
+ * `closeButton.color` - close button's color
204
+ * `closeButton.fontSize` - close button's font size
205
+ * `windowPadding.x` - minimum distance between window borders and the image by x axis, px
206
+ * `windowPadding.y` - minimum distance between window borders and the image by y axis, px
207
+ * `minimumSize.width` - minimum width of scaled image, px
208
+ * `minimumSize.height` - minimum height of scaled image, px
209
+ * `animationDuration` - duration of UI animations, ms
210
+
169
211
  ## Contributing
170
212
 
171
213
  1. Fork it
@@ -4,260 +4,320 @@ root = exports ? this
4
4
  # Data
5
5
  #
6
6
 
7
- # Array of images of current gallery
8
- root.images = []
9
-
10
- # Distance between navigation buttons and image
11
- root.navigation_button_margin = 10
12
-
13
- # Color of navigation button's border and symbols
14
- root.navigation_button_color = "#777"
15
-
16
- # Minimum distance between window borders and image
17
- root.window_padding_x = 50
18
- root.window_padding_y = 50
19
-
20
- # Minimum size of scaled image
21
- root.minimum_scaled_width = 100
22
- root.minimum_scaled_height = 100
23
-
24
- # Initializes the gallery on this page
25
- root.init_hermitage = ->
26
- # Create simple gallery layer if it doesn't exist
27
- if ($("#hermitage").length == 0)
28
- hermitage = $("<div>", {id: "hermitage"})
29
- $("body").append(hermitage)
30
- hermitage.css("z-index", 10)
31
- hermitage.hide()
32
-
33
- # Clear old images array
34
- images.length = 0
35
-
36
- # Create new images array
37
- $.each $('a[rel="hermitage"]'), ->
38
- images.push($(this).attr('href'))
39
-
40
- # Set on click handlers to all elements that
41
- # have 'hermitage' rel attribute
42
- $('a[rel="hermitage"]').click (event) ->
43
- open_gallery(this)
44
- event.preventDefault()
7
+ # Hermitage options
8
+ root.hermitage =
9
+ # Image viewer z-index property
10
+ zIndex: 10
11
+
12
+ # Darkening properties
13
+ darkening:
14
+ opacity: 0.75 # 0 if you don't want darkening
15
+ color: '#000'
16
+
17
+ # Navigation buttons' properties
18
+ navigationButton:
19
+ enabled: true
20
+ color: '#777'
21
+ width: 50 # px
22
+ borderRadius: 7 # px
23
+ margin: 10 # Distance between navigation buttons and image, px
24
+
25
+ # Close button properties
26
+ closeButton:
27
+ enabled: true
28
+ text: '×'
29
+ color: '#FFF'
30
+ fontSize: 30 # px
31
+
32
+ # Minimum distance between window borders and image, px
33
+ windowPadding:
34
+ x: 50
35
+ y: 50
36
+
37
+ # Minimum size of scaled image, px
38
+ minimumSize:
39
+ width: 100
40
+ height: 100
41
+
42
+ # Duration of all animations, ms
43
+ animationDuration: 400
44
+
45
+ # Array of images of current gallery
46
+ images: []
47
+
48
+ # Initializes the gallery on this page
49
+ init: ->
50
+ # Create Hermitage layer if it doesn't exist
51
+ if $('#hermitage').length is 0
52
+ layer = $('<div>', {id: 'hermitage'})
53
+ layer.css('position', 'fixed')
54
+ layer.hide()
55
+ $('body').append(layer)
56
+
57
+ # Clear old images array
58
+ hermitage.images.length = 0
59
+
60
+ # Create new images array
61
+ $.each $('a[rel="hermitage"]'), ->
62
+ hermitage.images.push($(this).attr('href'))
63
+
64
+ # Set on click handlers to all elements that
65
+ # have 'hermitage' rel attribute
66
+ $('a[rel="hermitage"]').click (event) ->
67
+ openGallery(this)
68
+ event.preventDefault()
45
69
 
46
70
  #
47
71
  # Helpers
48
72
  #
49
73
 
50
74
  # Place element at the center of screen
51
- jQuery.fn.center = () ->
52
- this.css("position", "fixed")
53
- this.css("top", Math.max(0, (($(window).height() - $(this).outerHeight()) / 2) + $(window).scrollTop()) + "px")
54
- this.css("left", Math.max(0, (($(window).width() - $(this).outerWidth()) / 2) + $(window).scrollLeft()) + "px")
55
- this
56
-
75
+ center = (element) ->
76
+ element.css('position', 'fixed')
77
+ element.css('top', "#{Math.max(0, ($(window).height() - $(element).outerHeight()) / 2)}px")
78
+ element.css('left', "#{Math.max(0, ($(window).width() - $(element).outerWidth()) / 2)}px")
79
+ element
57
80
 
58
81
  #
59
- # Simple gallery logic
82
+ # Hermitage logic
60
83
  #
61
84
 
62
85
  # Creates overlay layer, shows it and sets its click handler
63
- create_overlay = () ->
64
- overlay = $("<div>", {id: "overlay"})
65
- $("#hermitage").append(overlay)
66
-
67
- overlay.css("position", "fixed")
68
- overlay.css("top", "0")
69
- overlay.css("left", "0")
70
- overlay.css("background", "#000")
71
- overlay.css("display", "block")
72
- overlay.css("opacity", "0.75")
73
- overlay.css("filter", "alpha(opacity=75)")
74
- overlay.css("width", "100%")
75
- overlay.css("height", "100%")
86
+ createOverlay = ->
87
+ overlay = $('<div>', {id: 'overlay'})
88
+ $('#hermitage').append(overlay)
89
+
90
+ overlay.css('position', 'fixed')
91
+ overlay.css('top', '0')
92
+ overlay.css('left', '0')
93
+ overlay.css('background', hermitage.darkening.color)
94
+ overlay.css('display', 'block')
95
+ overlay.css('opacity', hermitage.darkening.opacity)
96
+ overlay.css('filter', "alpha(opacity='#{hermitage.darkening.opacity * 100}')")
97
+ overlay.css('width', '100%')
98
+ overlay.css('height', '100%')
76
99
 
77
100
  overlay.hide()
78
101
  overlay.fadeIn()
79
102
 
80
- overlay.click (event) ->
81
- close_gallery()
103
+ overlay.click(closeGallery)
82
104
 
83
105
  overlay
84
106
 
85
-
86
107
  # Creates base navigation button and returns it
87
- create_navigation_button = () ->
88
- button = $("<div>")
89
- $("#hermitage").append(button)
90
-
91
- button.css("position", "fixed")
92
- button.css("width", "50px")
93
- button.css("display", "block")
94
- button.css("cursor", "pointer")
95
-
96
- button.css("border-width", "1px")
97
- button.css("border-style", "solid")
98
- button.css("border-color", navigation_button_color)
99
- button.css("display", "block")
100
- button.css("border-radius", "7px")
101
- button.css("-webkit-border-radius", "7px")
102
- button.css("-moz-border-radius", "7px")
103
-
104
- button.css("color", navigation_button_color)
105
- button.css("text-align", "center")
106
- button.css("vertical-align", "middle")
107
- button.css("font", "30px Tahoma,Arial,Helvetica,sans-serif")
108
+ createNavigationButton = ->
109
+ button = $('<div>')
110
+ $('#hermitage').append(button)
111
+
112
+ button.css('position', 'fixed')
113
+ button.css('width', "#{hermitage.navigationButton.width}px")
114
+ button.css('display', 'block')
115
+ button.css('cursor', 'pointer')
116
+
117
+ button.css('border-width', '1px')
118
+ button.css('border-style', 'solid')
119
+ button.css('border-color', hermitage.navigationButton.color)
120
+ button.css('display', 'block')
121
+ button.css('border-radius', "#{hermitage.navigationButton.borderRadius}px")
122
+ button.css('-webkit-border-radius', "#{hermitage.navigationButton.borderRadius}px")
123
+ button.css('-moz-border-radius', "#{hermitage.navigationButton.borderRadius}px")
124
+
125
+ button.css('color', hermitage.navigationButton.color)
126
+ button.css('text-align', 'center')
127
+ button.css('vertical-align', 'middle')
128
+ button.css('font', '30px Tahoma,Arial,Helvetica,sans-serif')
108
129
 
109
130
  button.hide()
110
131
 
111
132
  button
112
133
 
113
134
  # Creates right navigation button and returns it
114
- create_right_navigation_button = () ->
115
- button = create_navigation_button()
116
- button.attr("id", "navigation-right")
117
- button.css("border-top-left-radius", "0")
118
- button.css("-webkit-border-top-left-radius", "0")
119
- button.css("-moz-border-top-left-radius", "0")
120
- button.css("border-bottom-left-radius", "0")
121
- button.css("-webkit-border-bottom-left-radius", "0")
122
- button.css("-moz-border-bottom-left-radius", "0")
123
- button.append(">")
124
-
125
- button.click (event) ->
126
- show_next_image()
135
+ createRightNavigationButton = ->
136
+ button = createNavigationButton()
137
+ button.attr('id', 'navigation-right')
138
+ button.css('border-top-left-radius', '0')
139
+ button.css('-webkit-border-top-left-radius', '0')
140
+ button.css('-moz-border-top-left-radius', '0')
141
+ button.css('border-bottom-left-radius', '0')
142
+ button.css('-webkit-border-bottom-left-radius', '0')
143
+ button.css('-moz-border-bottom-left-radius', '0')
144
+ button.append('>')
145
+
146
+ button.click(showNextImage)
127
147
 
128
148
  button
129
149
 
130
150
  # Create left navigation button and returns it
131
- create_left_navigation_button = () ->
132
- button = create_navigation_button()
133
- button.attr("id", "navigation-left")
134
- button.css("border-top-right-radius", "0")
135
- button.css("-webkit-border-top-right-radius", "0")
136
- button.css("-moz-border-top-right-radius", "0")
137
- button.css("border-bottom-right-radius", "0")
138
- button.css("-webkit-border-bottom-right-radius", "0")
139
- button.css("-moz-border-bottom-right-radius", "0")
140
- button.append("<")
141
-
142
- button.click (event) ->
143
- show_previous_image()
151
+ createLeftNavigationButton = ->
152
+ button = createNavigationButton()
153
+ button.attr('id', 'navigation-left')
154
+ button.css('border-top-right-radius', '0')
155
+ button.css('-webkit-border-top-right-radius', '0')
156
+ button.css('-moz-border-top-right-radius', '0')
157
+ button.css('border-bottom-right-radius', '0')
158
+ button.css('-webkit-border-bottom-right-radius', '0')
159
+ button.css('-moz-border-bottom-right-radius', '0')
160
+ button.append('<')
161
+
162
+ button.click(showPreviousImage)
144
163
 
145
164
  button
146
165
 
166
+ # Creates close button
167
+ createCloseButton = ->
168
+ button = $('<div>', {id: 'close-button'})
169
+ $('#hermitage').append(button)
170
+
171
+ button.hide()
172
+
173
+ button.text(hermitage.closeButton.text)
174
+ button.css('position', 'fixed')
175
+ button.css('color', hermitage.closeButton.color)
176
+ button.css('font-size', "#{hermitage.closeButton.fontSize}px")
177
+ button.css('white-space', 'nowrap')
178
+ button.css('cursor', 'pointer')
179
+
180
+ button.click(closeGallery)
181
+
182
+ button
147
183
 
148
184
  # Shows full size image of the chosen one
149
- open_gallery = (image) ->
150
- $("#hermitage").empty()
151
- $("#hermitage").show()
152
- create_overlay()
153
- create_right_navigation_button()
154
- create_left_navigation_button()
155
-
156
- show_image(images.indexOf($(image).attr("href")))
185
+ openGallery = (image) ->
186
+ $('#hermitage').css('z-index', hermitage.zIndex)
187
+ $('#hermitage').empty()
188
+ $('#hermitage').show()
189
+
190
+ createOverlay()
191
+ createRightNavigationButton()
192
+ createLeftNavigationButton()
193
+ createCloseButton()
194
+
195
+ showImage(hermitage.images.indexOf($(image).attr('href')))
157
196
 
158
197
 
159
198
  # Shows image with specified index from images array
160
- show_image = (index) ->
199
+ showImage = (index) ->
161
200
  # Create full size image at the center of screen
162
- img = $("<img />")
163
- img.attr("src", images[index])
164
- img.attr("class", "current")
165
- img.css("cursor", "pointer")
201
+ img = $('<img />')
202
+ img.attr('src', hermitage.images[index])
203
+ img.attr('class', 'current')
204
+ img.css('cursor', 'pointer')
205
+ img.css('max-width', 'none') # fix the conflict with Twitter Bootstrap
166
206
  img.hide()
167
207
 
168
- $("#hermitage").append(img)
208
+ $('#hermitage').append(img)
169
209
 
170
210
  img.click (event) ->
171
- if (event.pageX >= $(window).width() / 2)
172
- show_next_image()
211
+ if event.pageX >= $(window).width() / 2
212
+ showNextImage()
173
213
  else
174
- show_previous_image()
214
+ showPreviousImage()
175
215
 
176
216
  # When image will be loaded set correct size,
177
217
  # center element and show it
178
- $("<img />").attr("src", images[index]).load ->
179
- max_width = $(window).width() - (window_padding_x + $("#navigation-left").outerWidth() + navigation_button_margin) * 2
180
- max_height = $(window).height() - window_padding_y * 2
218
+ $('<img />').attr('src', hermitage.images[index]).load ->
219
+ maxWidth = $(window).width() - (hermitage.windowPadding.x + $('#navigation-left').outerWidth() + hermitage.navigationButton.margin) * 2
220
+ maxHeight = $(window).height() - hermitage.windowPadding.y * 2
181
221
 
182
222
  scale = 1.0
183
223
 
184
- if (max_width <= minimum_scaled_width || max_height <= minimum_scaled_height)
185
- if (max_width < max_height)
186
- max_width = minimum_scaled_width
187
- max_height = max_width * (this.height / this.width)
224
+ if maxWidth <= hermitage.minimumSize.width or maxHeight <= hermitage.minimumSize.height
225
+ if maxWidth < maxHeight
226
+ maxWidth = hermitage.minimumSize.width
227
+ maxHeight = maxWidth * (this.height / this.width)
188
228
  else
189
- max_height = minimum_scaled_height
190
- max_width = max_height * (this.width / this.height)
229
+ maxHeight = hermitage.minimumSize.height
230
+ maxWidth = maxHeight * (this.width / this.height)
191
231
 
192
- if (this.width > max_width || this.height > max_height)
193
- scale = Math.min(max_width / this.width, max_height / this.height)
232
+ if this.width > maxWidth or this.height > maxHeight
233
+ scale = Math.min(maxWidth / this.width, maxHeight / this.height)
194
234
 
195
235
  img.width(this.width * scale)
196
236
  img.height(this.height * scale)
197
237
 
198
- img.center()
199
- img.fadeIn()
200
- adjust_navigation_buttons()
238
+ center(img)
239
+ img.fadeIn(hermitage.animationDuration)
240
+ adjustNavigationButtons(img)
241
+ adjustCloseButton(img)
201
242
 
202
243
  # Shows next image
203
- show_next_image = ->
204
- current = $("img.current")
205
- if (current.length == 1)
206
- index = images.indexOf(current.attr("src"))
207
- hide_current_image()
208
- if (index < images.length - 1)
209
- show_image(index + 1)
244
+ showNextImage = ->
245
+ current = $('img.current')
246
+ if current.length is 1
247
+ index = hermitage.images.indexOf(current.attr('src'))
248
+ hideCurrentImage()
249
+ if index < hermitage.images.length - 1
250
+ showImage(index + 1)
210
251
  else
211
- show_image(0)
252
+ showImage(0)
212
253
 
213
254
  # Shows previous image
214
- show_previous_image = ->
215
- current = $("img.current")
216
- if (current.length == 1)
217
- index = images.indexOf(current.attr("src"))
218
- hide_current_image()
219
- if (index > 0)
220
- show_image(index - 1)
255
+ showPreviousImage = ->
256
+ current = $('img.current')
257
+ if current.length is 1
258
+ index = hermitage.images.indexOf(current.attr('src'))
259
+ hideCurrentImage()
260
+ if index > 0
261
+ showImage(index - 1)
221
262
  else
222
- show_image(images.length - 1)
263
+ showImage(hermitage.images.length - 1)
223
264
 
224
265
  # Hides current image
225
- hide_current_image = ->
226
- current = $("img.current")
227
- if (current.length == 1)
228
- current.attr("class", "")
229
- current.fadeOut 400, ->
266
+ hideCurrentImage = ->
267
+ current = $('img.current')
268
+ if current.length is 1
269
+ current.fadeOut hermitage.animationDuration, ->
230
270
  current.remove()
231
271
 
232
- # Starts fade out animation and clears simple gallery at the end of animation
233
- close_gallery = () ->
234
- $("#hermitage :not(#overlay)").fadeOut()
235
- $("#overlay").fadeOut 400, ->
236
- $("#hermitage").hide()
237
- $("#hermitage").empty()
272
+ # Starts fade out animation and clears Hermitage at the end of animation
273
+ closeGallery = ->
274
+ $('#hermitage :not(#overlay)').fadeOut()
275
+ $('#overlay').fadeOut hermitage.animationDuration, ->
276
+ $('#hermitage').hide()
277
+ $('#hermitage').empty()
238
278
 
239
279
 
240
280
  # Moves navigation buttons to proper positions
241
- adjust_navigation_buttons = () ->
242
- left = $("#navigation-left")
243
- right = $("#navigation-right")
281
+ adjustNavigationButtons = (current) ->
282
+ return unless hermitage.navigationButton.enabled
244
283
 
245
- current = $(".current")
284
+ previous = $('#navigation-left')
285
+ next = $('#navigation-right')
246
286
 
247
- left_new_height = current.outerHeight() + "px"
248
- left_new_left = (current.position().left - left.outerWidth() - navigation_button_margin) + "px"
249
- left_new_top = current.position().top + "px"
287
+ newPrevious =
288
+ top: current.position().top
289
+ left: current.position().left - previous.outerWidth() - hermitage.navigationButton.margin
290
+ height: current.outerHeight()
291
+
292
+ newNext =
293
+ top: current.position().top
294
+ left: current.position().left + current.outerWidth() + hermitage.navigationButton.margin
295
+ height: current.outerHeight()
296
+
297
+ move = (button, dimensions) ->
298
+ button.animate({ height: "#{dimensions.height}px", 'line-height': "#{dimensions.height}px", left: "#{dimensions.left}px", top: "#{dimensions.top}px"}, hermitage.animationDuration)
299
+ button.fadeIn(hermitage.animationDuration) if button.css('display') is 'none'
300
+
301
+ move previous, newPrevious
302
+ move next, newNext
250
303
 
251
- right_new_height = current.outerHeight() + "px"
252
- right_new_left = (current.position().left + current.outerWidth() + navigation_button_margin) + "px"
253
- right_new_top = current.position().top + "px"
304
+ # Moves close button to proper position
305
+ adjustCloseButton = (current) ->
306
+ return unless hermitage.closeButton.enabled
254
307
 
255
- left.animate({ height: left_new_height, 'line-height': left_new_height, left: left_new_left, top: left_new_top}, 400)
256
- right.animate({ height: right_new_height, 'line-height': right_new_height, left: right_new_left, top: right_new_top}, 400)
308
+ button = $('#close-button')
257
309
 
258
- left.fadeIn() if (left.css("display") == "none")
259
- right.fadeIn() if (right.css("display") == "none")
310
+ top = current.position().top - button.outerHeight()
311
+ left = current.position().left + current.outerWidth() - button.outerWidth()
312
+
313
+ if button.css('display') is 'none'
314
+ button.css('top', top)
315
+ button.css('left', left)
316
+ button.fadeIn(hermitage.animationDuration)
317
+
318
+ button.animate({ top: "#{top}px", left: "#{left}px" }, hermitage.animationDuration)
319
+
260
320
 
261
321
  # Initialize gallery on page load
262
- $(document).ready(init_hermitage)
263
- $(document).on('page:load', init_hermitage)
322
+ $(document).ready(hermitage.init)
323
+ $(document).on('page:load', hermitage.init)
@@ -11,7 +11,8 @@
11
11
  # list_class: 'thumbnails',
12
12
  # item_class: 'span4',
13
13
  # link_class: 'thumbnail',
14
- # image_class: nil
14
+ # image_class: nil,
15
+ # each_slice: nil
15
16
  # })
16
17
 
17
18
  # Also you can create your own configs that will be merged with default config to overwrite default options.
@@ -19,6 +19,11 @@ module Hermitage
19
19
  LINK_CLASS = 'thumbnail'
20
20
  IMAGE_CLASS = nil
21
21
 
22
+ # Slices each N images into the separate gallery.
23
+ # It is helpful e.g. when using Twitter Bootstrap framework
24
+ # and your gallery is inside `.row-fluid` block.
25
+ EACH_SLICE = nil
26
+
22
27
  # Returns hash of default options
23
28
  def self.to_hash
24
29
  hash = {}
@@ -1,3 +1,3 @@
1
1
  module Hermitage
2
- VERSION = '0.0.1'
2
+ VERSION = '0.0.2.1'
3
3
  end
@@ -17,24 +17,40 @@ module Hermitage
17
17
  # Config names are formed by the class name of the first element in array.
18
18
  #
19
19
  def render_gallery_for(objects, options = {})
20
- raise(ArgumentError, 'First argument in render_gallery_for method can be string, symbol or array only.') unless objects.is_a? Array
21
-
20
+ # Choose config accoring to class name of objects in passed array
22
21
  config_name = objects.first.class.to_s.pluralize.underscore.to_sym if defined?(Rails) && !objects.empty?
23
22
  config = Hermitage.configs.include?(config_name) ? config_name : :default
24
23
 
24
+ # Merge default options with the chosen config and with passed options
25
25
  options = Hermitage.configs[:default].merge(Hermitage.configs[config]).merge(options)
26
26
 
27
- items = []
28
- objects.each do |object|
29
- full_image_path = object.instance_eval(options[:attribute_full_size])
30
- thumbnail_image_path = object.instance_eval(options[:attribute_thumbnail])
31
- image = image_tag(thumbnail_image_path, class: options[:image_class])
32
- items << link_to(image, full_image_path, rel: 'hermitage', class: options[:link_class])
27
+ # Create array of all list tags
28
+ lists = unless options[:each_slice]
29
+ [objects]
30
+ else
31
+ objects.each_slice(options[:each_slice]).to_a
33
32
  end
34
-
35
- content_tag options[:list_tag], class: options[:list_class] do
36
- items.collect { |item| concat(content_tag(options[:item_tag], item, class: options[:item_class])) }
33
+
34
+ # The resulting tag
35
+ tag = ''
36
+
37
+ # Render each list into `tag` variable
38
+ lists.each do |list|
39
+ # Array of items in current list
40
+ items = list.collect do |item|
41
+ full_image_path = eval("item.#{options[:attribute_full_size]}")
42
+ thumbnail_image_path = eval("item.#{options[:attribute_thumbnail]}")
43
+ image = image_tag(thumbnail_image_path, class: options[:image_class])
44
+ link_to(image, full_image_path, rel: 'hermitage', class: options[:link_class])
45
+ end
46
+
47
+ # Convert these items into content tag string
48
+ tag << content_tag(options[:list_tag], class: options[:list_class]) do
49
+ items.collect { |item| concat(content_tag(options[:item_tag], item, class: options[:item_class])) }
50
+ end
37
51
  end
52
+
53
+ tag.html_safe
38
54
  end
39
55
 
40
56
  end
@@ -1,23 +1,8 @@
1
1
  require 'spec_helper'
2
+ require 'features_helper'
2
3
 
3
4
  describe 'navigation', type: :feature, js: true do
4
5
 
5
- # Workaround for clicking exactly on the specified position of the element
6
- def click_at(selector, offset_x, offset_y)
7
- x = evaluate_script("$('#{selector}').position().left + $('#{selector}').outerWidth() * #{offset_x}")
8
- y = evaluate_script("$('#{selector}').position().top + $('#{selector}').outerHeight() * #{offset_y}")
9
- page.driver.click(x, y)
10
- end
11
-
12
- def click_at_left(selector)
13
- click_at(selector, 0.25, 0.5)
14
- end
15
-
16
- def click_at_right(selector)
17
- click_at(selector, 0.75, 0.5)
18
- end
19
-
20
-
21
6
  shared_examples 'navigation to next' do
22
7
  before(:each) { click_action.call() }
23
8
 
@@ -13,7 +13,7 @@ describe 'render gallery', type: :feature, js: true do
13
13
  end
14
14
 
15
15
  it 'fills images array' do
16
- evaluate_script("images").should == ['/assets/0-full.png', '/assets/1-full.png', '/assets/2-full.png']
16
+ evaluate_script('hermitage.images').should == ['/assets/0-full.png', '/assets/1-full.png', '/assets/2-full.png']
17
17
  end
18
18
 
19
19
  end
@@ -1,15 +1,8 @@
1
1
  require 'spec_helper'
2
+ require 'features_helper'
2
3
 
3
4
  describe 'scale', type: :feature, js: true do
4
5
 
5
- def width(selector)
6
- evaluate_script("$('#{selector}').outerWidth()")
7
- end
8
-
9
- def height(selector)
10
- evaluate_script("$('#{selector}').outerHeight()")
11
- end
12
-
13
6
  before(:each) do
14
7
  visit images_path
15
8
  page.driver.resize(window_width, window_height)
@@ -0,0 +1,152 @@
1
+ require 'spec_helper'
2
+ require 'features_helper'
3
+
4
+ describe 'viewer_customization', type: :feature, js: true do
5
+
6
+ let(:before_click) { nil }
7
+
8
+ before(:each) do
9
+ visit images_path
10
+ evaluate_script(js)
11
+ before_click.call() if before_click
12
+ page.first('a[rel=hermitage]').click
13
+ page.should have_css('div#hermitage img.current')
14
+ # There will be sleep(1) in tests where we should wait until fade in animation is ended
15
+ end
16
+
17
+ context 'zIndex' do
18
+ let(:js) { 'hermitage.zIndex = 5'}
19
+ it { css('#hermitage', 'z-index').should == '5' }
20
+ end
21
+
22
+ context 'darkening.opacity' do
23
+ let(:js) { 'hermitage.darkening.opacity = 0.5'}
24
+ before(:each) { sleep(1) }
25
+ it { css('#overlay', 'opacity').should == '0.5' }
26
+ end
27
+
28
+ context 'darkening.color' do
29
+ let(:js) { 'hermitage.darkening.color = "#FAFAFA"' }
30
+ it { css('#overlay', 'background-color').should == 'rgb(250, 250, 250)' }
31
+ end
32
+
33
+ context 'navigationButton.enabled' do
34
+ let(:js) { 'hermitage.navigationButton.enabled = false' }
35
+ it { should_not have_css('#navigation-left') }
36
+ it { should_not have_css('#navigation-right') }
37
+ end
38
+
39
+ context 'navigationButton.color' do
40
+ let(:js) { 'hermitage.navigationButton.color = "#000"'}
41
+
42
+ shared_examples 'navigation button' do
43
+ it 'sets border color' do
44
+ css(selector, 'border-top-color').should == 'rgb(0, 0, 0)'
45
+ css(selector, 'border-right-color').should == 'rgb(0, 0, 0)'
46
+ css(selector, 'border-bottom-color').should == 'rgb(0, 0, 0)'
47
+ css(selector, 'border-left-color').should == 'rgb(0, 0, 0)'
48
+ end
49
+ end
50
+
51
+ context 'left' do
52
+ let(:selector) { '#navigation-left' }
53
+ it_behaves_like 'navigation button'
54
+ end
55
+
56
+ context 'right' do
57
+ let(:selector) { '#navigation-right' }
58
+ it_behaves_like 'navigation button'
59
+ end
60
+ end
61
+
62
+ context 'navigationButton.width' do
63
+ let(:js) { 'hermitage.navigationButton.width = 100' }
64
+ it { css('#navigation-left', 'width').should == '100px' }
65
+ it { css('#navigation-right', 'width').should == '100px' }
66
+ end
67
+
68
+ context 'navigationButton.borderRadius' do
69
+ let(:js) { 'hermitage.navigationButton.borderRadius = 5' }
70
+
71
+ it 'sets border radiuses for left button' do
72
+ css('#navigation-left', 'border-top-left-radius').should == '5px'
73
+ css('#navigation-left', 'border-bottom-left-radius').should == '5px'
74
+ css('#navigation-left', 'border-top-right-radius').should == '0px'
75
+ css('#navigation-left', 'border-bottom-right-radius').should == '0px'
76
+ end
77
+
78
+ it 'sets border radiuses for right button' do
79
+ css('#navigation-right', 'border-top-left-radius').should == '0px'
80
+ css('#navigation-right', 'border-bottom-left-radius').should == '0px'
81
+ css('#navigation-right', 'border-top-right-radius').should == '5px'
82
+ css('#navigation-right', 'border-bottom-right-radius').should == '5px'
83
+ end
84
+ end
85
+
86
+ context 'navigationButton.margin' do
87
+ let(:js) { 'hermitage.navigationButton.margin = 30' }
88
+ before(:each) { sleep(1) }
89
+ it { css('#navigation-left', 'left').should == "#{left('.current') - 30 - width('#navigation-left')}px" }
90
+ it { css('#navigation-right', 'left').should == "#{left('.current') + width('.current') + 30}px" }
91
+ end
92
+
93
+ context 'closeButton.enabled' do
94
+ let(:js) { 'hermitage.closeButton.enabled = false' }
95
+ it { should_not have_css('#close-button') }
96
+ end
97
+
98
+ context 'closeButton.text' do
99
+ let(:js) { 'hermitage.closeButton.text = "Close"' }
100
+ it { text('#close-button').should == 'Close' }
101
+ end
102
+
103
+ context 'closeButton.color' do
104
+ let(:js) { 'hermitage.closeButton.color = "#777"' }
105
+ it { css('#close-button', 'color').should == 'rgb(119, 119, 119)' }
106
+ end
107
+
108
+ context 'closeButton.fontSize' do
109
+ let(:js) { 'hermitage.closeButton.fontSize = 10' }
110
+ it { css('#close-button', 'font-size').should == '10px' }
111
+ end
112
+
113
+ context 'windowPadding.x' do
114
+ let(:js) { 'hermitage.windowPadding.x = 100'}
115
+ let(:before_click) { Proc.new{ page.driver.resize(500, 1000) } }
116
+
117
+ it 'scales the image' do
118
+ width('.current').should == 176
119
+ height('.current').should == 176
120
+ end
121
+ end
122
+
123
+ context 'windowPadding.y' do
124
+ let(:js) { 'hermitage.windowPadding.y = 100'}
125
+ let(:before_click) { Proc.new{ page.driver.resize(1000, 400) } }
126
+
127
+ it 'scales the image' do
128
+ width('.current').should == 200
129
+ height('.current').should == 200
130
+ end
131
+ end
132
+
133
+ shared_examples 'image scaled to the minimum allowed size' do
134
+ it 'scales the image to the minimum allowed size' do
135
+ width('.current').should == 200
136
+ height('.current').should == 200
137
+ end
138
+ end
139
+
140
+ context 'minimumSize.width' do
141
+ let(:js) { 'hermitage.minimumSize.width = 200'}
142
+ let(:before_click) { Proc.new{ page.driver.resize(300, 1000) } }
143
+ it_behaves_like 'image scaled to the minimum allowed size'
144
+ end
145
+
146
+ context 'minimumSize.width' do
147
+ let(:js) { 'hermitage.minimumSize.height = 200'}
148
+ let(:before_click) { Proc.new{ page.driver.resize(1000, 250) } }
149
+ it_behaves_like 'image scaled to the minimum allowed size'
150
+ end
151
+
152
+ end
@@ -27,14 +27,26 @@ describe 'viewer', type: :feature, js: true do
27
27
  page.should have_css("div#hermitage div#navigation-right")
28
28
  end
29
29
 
30
- describe 'visitor clicks on shadow in the viewer' do
31
- # Workaround for clicking at the shadow div
32
- before(:each) { evaluate_script "$('#overlay').click()" }
30
+ it 'has close button' do
31
+ page.should have_css('div#hermitage div#close-button')
32
+ end
33
33
 
34
+ shared_examples 'close button' do
34
35
  it 'hides hermitage layer and removes everything from it' do
35
36
  page.should_not have_css('div#hermitage')
36
37
  page.find('div#hermitage', visible: false).all('*').length.should == 0
37
38
  end
38
39
  end
39
40
 
41
+ describe 'click on the close button' do
42
+ before(:each) { page.find('#close-button').click }
43
+ it_behaves_like 'close button'
44
+ end
45
+
46
+ describe 'click on shadow in the viewer' do
47
+ # Workaround for clicking at the shadow div
48
+ before(:each) { evaluate_script "$('#overlay').click()" }
49
+ it_behaves_like 'close button'
50
+ end
51
+
40
52
  end
@@ -0,0 +1,42 @@
1
+ #
2
+ # There are helper methods for features specs
3
+ #
4
+
5
+ def css(selector, css)
6
+ evaluate_script("$('#{selector}').css('#{css}')")
7
+ end
8
+
9
+ def width(selector)
10
+ evaluate_script("$('#{selector}').outerWidth()")
11
+ end
12
+
13
+ def height(selector)
14
+ evaluate_script("$('#{selector}').outerHeight()")
15
+ end
16
+
17
+ def top(selector)
18
+ evaluate_script("$('#{selector}').position().top")
19
+ end
20
+
21
+ def left(selector)
22
+ evaluate_script("$('#{selector}').position().left")
23
+ end
24
+
25
+ # Workaround for clicking exactly on the specified position of the element
26
+ def click_at(selector, offset_x, offset_y)
27
+ x = left(selector) + width(selector) * offset_x
28
+ y = top(selector) + height(selector) * offset_y
29
+ page.driver.click(x, y)
30
+ end
31
+
32
+ def click_at_left(selector)
33
+ click_at(selector, 0.25, 0.5)
34
+ end
35
+
36
+ def click_at_right(selector)
37
+ click_at(selector, 0.75, 0.5)
38
+ end
39
+
40
+ def text(selector)
41
+ evaluate_script("$('#{selector}').text()")
42
+ end
@@ -11,7 +11,8 @@ describe Hermitage::Defaults do
11
11
  list_class: 'thumbnails',
12
12
  item_class: 'span4',
13
13
  link_class: 'thumbnail',
14
- image_class: nil
14
+ image_class: nil,
15
+ each_slice: nil
15
16
  }
16
17
  end
17
18
  end
@@ -36,6 +36,12 @@ describe Hermitage::ViewHelpers, type: :helper do
36
36
  let(:expected) { '<ul><li><a class="thumb link" href="/assets/0-full.png" rel="hermitage"><img alt="0 thumbnail" class="thumb" src="/assets/0-thumbnail.png" /></a></li><li><a class="thumb link" href="/assets/1-full.png" rel="hermitage"><img alt="1 thumbnail" class="thumb" src="/assets/1-thumbnail.png" /></a></li></ul>' }
37
37
  it { should == expected }
38
38
  end
39
+
40
+ context 'each_slice' do
41
+ subject { template.render_gallery_for images, each_slice: 1 }
42
+ let(:expected) { '<ul class="thumbnails"><li class="span4"><a class="thumbnail" href="/assets/0-full.png" rel="hermitage"><img alt="0 thumbnail" src="/assets/0-thumbnail.png" /></a></li></ul><ul class="thumbnails"><li class="span4"><a class="thumbnail" href="/assets/1-full.png" rel="hermitage"><img alt="1 thumbnail" src="/assets/1-thumbnail.png" /></a></li></ul>' }
43
+ it { should == expected }
44
+ end
39
45
  end
40
46
 
41
47
  context 'with configs' do
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: hermitage
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1
4
+ version: 0.0.2.1
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2013-09-04 00:00:00.000000000 Z
12
+ date: 2013-09-05 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: rails
@@ -198,6 +198,7 @@ files:
198
198
  - .rspec
199
199
  - .rvmrc
200
200
  - .travis.yml
201
+ - CHANGELOG.md
201
202
  - Gemfile
202
203
  - LICENSE.txt
203
204
  - README.md
@@ -266,7 +267,9 @@ files:
266
267
  - spec/features/navigation_spec.rb
267
268
  - spec/features/render_gallery_spec.rb
268
269
  - spec/features/scale_spec.rb
270
+ - spec/features/viewer_customization_spec.rb
269
271
  - spec/features/viewer_spec.rb
272
+ - spec/features_helper.rb
270
273
  - spec/generators/install_spec.rb
271
274
  - spec/lib/hermitage/defaults_spec.rb
272
275
  - spec/lib/hermitage/railtie_spec.rb
@@ -353,7 +356,9 @@ test_files:
353
356
  - spec/features/navigation_spec.rb
354
357
  - spec/features/render_gallery_spec.rb
355
358
  - spec/features/scale_spec.rb
359
+ - spec/features/viewer_customization_spec.rb
356
360
  - spec/features/viewer_spec.rb
361
+ - spec/features_helper.rb
357
362
  - spec/generators/install_spec.rb
358
363
  - spec/lib/hermitage/defaults_spec.rb
359
364
  - spec/lib/hermitage/railtie_spec.rb