hermitage 0.0.1 → 0.0.2.1

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.
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