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 +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
|
+
[![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:
|
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
|