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 +17 -0
- data/README.md +46 -4
- data/app/assets/javascripts/hermitage.js.coffee +242 -182
- data/lib/generators/hermitage/templates/hermitage.rb +2 -1
- data/lib/hermitage/defaults.rb +5 -0
- data/lib/hermitage/version.rb +1 -1
- data/lib/hermitage/view_helpers.rb +27 -11
- data/spec/features/navigation_spec.rb +1 -16
- data/spec/features/render_gallery_spec.rb +1 -1
- data/spec/features/scale_spec.rb +1 -8
- data/spec/features/viewer_customization_spec.rb +152 -0
- data/spec/features/viewer_spec.rb +15 -3
- data/spec/features_helper.rb +42 -0
- data/spec/lib/hermitage/defaults_spec.rb +2 -1
- data/spec/lib/hermitage/view_helpers_spec.rb +6 -0
- metadata +7 -2
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
|
+
[](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:
|
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
|
-
####
|
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:
|
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[:
|
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
|
-
#
|
8
|
-
root.
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
#
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
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
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
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
|
-
#
|
82
|
+
# Hermitage logic
|
60
83
|
#
|
61
84
|
|
62
85
|
# Creates overlay layer, shows it and sets its click handler
|
63
|
-
|
64
|
-
overlay = $(
|
65
|
-
$(
|
66
|
-
|
67
|
-
overlay.css(
|
68
|
-
overlay.css(
|
69
|
-
overlay.css(
|
70
|
-
overlay.css(
|
71
|
-
overlay.css(
|
72
|
-
overlay.css(
|
73
|
-
overlay.css(
|
74
|
-
overlay.css(
|
75
|
-
overlay.css(
|
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
|
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
|
-
|
88
|
-
button = $(
|
89
|
-
$(
|
90
|
-
|
91
|
-
button.css(
|
92
|
-
button.css(
|
93
|
-
button.css(
|
94
|
-
button.css(
|
95
|
-
|
96
|
-
button.css(
|
97
|
-
button.css(
|
98
|
-
button.css(
|
99
|
-
button.css(
|
100
|
-
button.css(
|
101
|
-
button.css(
|
102
|
-
button.css(
|
103
|
-
|
104
|
-
button.css(
|
105
|
-
button.css(
|
106
|
-
button.css(
|
107
|
-
button.css(
|
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
|
-
|
115
|
-
button =
|
116
|
-
button.attr(
|
117
|
-
button.css(
|
118
|
-
button.css(
|
119
|
-
button.css(
|
120
|
-
button.css(
|
121
|
-
button.css(
|
122
|
-
button.css(
|
123
|
-
button.append(
|
124
|
-
|
125
|
-
button.click
|
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
|
-
|
132
|
-
button =
|
133
|
-
button.attr(
|
134
|
-
button.css(
|
135
|
-
button.css(
|
136
|
-
button.css(
|
137
|
-
button.css(
|
138
|
-
button.css(
|
139
|
-
button.css(
|
140
|
-
button.append(
|
141
|
-
|
142
|
-
button.click
|
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
|
-
|
150
|
-
$(
|
151
|
-
$(
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
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
|
-
|
199
|
+
showImage = (index) ->
|
161
200
|
# Create full size image at the center of screen
|
162
|
-
img = $(
|
163
|
-
img.attr(
|
164
|
-
img.attr(
|
165
|
-
img.css(
|
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
|
-
$(
|
208
|
+
$('#hermitage').append(img)
|
169
209
|
|
170
210
|
img.click (event) ->
|
171
|
-
if
|
172
|
-
|
211
|
+
if event.pageX >= $(window).width() / 2
|
212
|
+
showNextImage()
|
173
213
|
else
|
174
|
-
|
214
|
+
showPreviousImage()
|
175
215
|
|
176
216
|
# When image will be loaded set correct size,
|
177
217
|
# center element and show it
|
178
|
-
$(
|
179
|
-
|
180
|
-
|
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
|
185
|
-
if
|
186
|
-
|
187
|
-
|
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
|
-
|
190
|
-
|
229
|
+
maxHeight = hermitage.minimumSize.height
|
230
|
+
maxWidth = maxHeight * (this.width / this.height)
|
191
231
|
|
192
|
-
if
|
193
|
-
scale = Math.min(
|
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
|
-
|
199
|
-
img.fadeIn()
|
200
|
-
|
238
|
+
center(img)
|
239
|
+
img.fadeIn(hermitage.animationDuration)
|
240
|
+
adjustNavigationButtons(img)
|
241
|
+
adjustCloseButton(img)
|
201
242
|
|
202
243
|
# Shows next image
|
203
|
-
|
204
|
-
current = $(
|
205
|
-
if
|
206
|
-
index = images.indexOf(current.attr(
|
207
|
-
|
208
|
-
if
|
209
|
-
|
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
|
-
|
252
|
+
showImage(0)
|
212
253
|
|
213
254
|
# Shows previous image
|
214
|
-
|
215
|
-
current = $(
|
216
|
-
if
|
217
|
-
index = images.indexOf(current.attr(
|
218
|
-
|
219
|
-
if
|
220
|
-
|
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
|
-
|
263
|
+
showImage(hermitage.images.length - 1)
|
223
264
|
|
224
265
|
# Hides current image
|
225
|
-
|
226
|
-
current = $(
|
227
|
-
if
|
228
|
-
current.
|
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
|
233
|
-
|
234
|
-
$(
|
235
|
-
$(
|
236
|
-
$(
|
237
|
-
$(
|
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
|
-
|
242
|
-
|
243
|
-
right = $("#navigation-right")
|
281
|
+
adjustNavigationButtons = (current) ->
|
282
|
+
return unless hermitage.navigationButton.enabled
|
244
283
|
|
245
|
-
|
284
|
+
previous = $('#navigation-left')
|
285
|
+
next = $('#navigation-right')
|
246
286
|
|
247
|
-
|
248
|
-
|
249
|
-
|
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
|
-
|
252
|
-
|
253
|
-
|
304
|
+
# Moves close button to proper position
|
305
|
+
adjustCloseButton = (current) ->
|
306
|
+
return unless hermitage.closeButton.enabled
|
254
307
|
|
255
|
-
|
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
|
-
|
259
|
-
|
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(
|
263
|
-
$(document).on('page:load',
|
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.
|
data/lib/hermitage/defaults.rb
CHANGED
@@ -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 = {}
|
data/lib/hermitage/version.rb
CHANGED
@@ -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
|
-
|
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
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
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
|
-
|
36
|
-
|
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(
|
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
|
data/spec/features/scale_spec.rb
CHANGED
@@ -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
|
-
|
31
|
-
#
|
32
|
-
|
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
|
@@ -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-
|
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
|