hermitage 0.0.3 → 0.0.4.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 +10 -0
- data/README.md +20 -17
- data/app/assets/javascripts/hermitage.js.coffee +157 -105
- data/lib/generators/hermitage/templates/hermitage.rb +15 -14
- data/lib/hermitage/configurator.rb +31 -0
- data/lib/hermitage/rails_render_core.rb +58 -0
- data/lib/hermitage/version.rb +1 -1
- data/lib/hermitage/view_helpers.rb +3 -35
- data/lib/hermitage.rb +5 -0
- data/spec/features/bottom_panel_spec.rb +2 -2
- data/spec/features/preload_neighbours_spec.rb +76 -0
- data/spec/features/render_gallery_spec.rb +8 -1
- data/spec/lib/hermitage/configurator_spec.rb +42 -0
- data/spec/lib/hermitage/view_helpers_spec.rb +6 -6
- data/spec/spec_helper.rb +5 -1
- metadata +8 -2
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,13 @@
|
|
1
|
+
### 0.0.4.1 ###
|
2
|
+
|
3
|
+
* RailsRenderCore is not a child of ActionView::Base anymore
|
4
|
+
|
5
|
+
### 0.0.4 ###
|
6
|
+
|
7
|
+
* Ability to preload neighbour images
|
8
|
+
* Slide effect added and is used by default
|
9
|
+
* Nice DSL syntax added for configuration
|
10
|
+
|
1
11
|
### 0.0.3 ###
|
2
12
|
|
3
13
|
* *Attention*! In this version some options were renamed: attribute_full_size -> original, attribute_thumbnail -> thumbnail.
|
data/README.md
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
# Hermitage
|
2
2
|
|
3
3
|
[](https://travis-ci.org/ImmaculatePine/hermitage)
|
4
|
+
[](https://codeclimate.com/github/ImmaculatePine/hermitage)
|
4
5
|
|
5
6
|
Ruby library for generation of image galleries (thumbnails and original images viewer).
|
6
7
|
|
@@ -130,7 +131,7 @@ When you call `render_gallery_for` method Hermitage looks for config with name f
|
|
130
131
|
In the example above Hermitage tries to find :images config because first argument of `render_gallery_for` method was array of Image instances.
|
131
132
|
If there is no proper config :default config is used.
|
132
133
|
|
133
|
-
Hermitage configs are described in config/initializers/hermitage.rb file.
|
134
|
+
Hermitage configs are described in config/initializers/hermitage.rb file. For configuration you can use DSL syntax described below.
|
134
135
|
|
135
136
|
#### Overwriting Defaults
|
136
137
|
|
@@ -138,10 +139,10 @@ You can overwrite :default config. These changes will be applied to all the gall
|
|
138
139
|
|
139
140
|
Uncoment the following lines in config/initializers/hermitage.rb file and make some changes here:
|
140
141
|
|
141
|
-
Hermitage.
|
142
|
-
original
|
143
|
-
thumbnail
|
144
|
-
|
142
|
+
Hermitage.configure :default do
|
143
|
+
original 'image.url(:medium)'
|
144
|
+
thumbnail 'image.url(:small)'
|
145
|
+
end
|
145
146
|
|
146
147
|
Now Hermitage will use `image.url` method with :medium or :small argument to get images for the gallery.
|
147
148
|
|
@@ -169,19 +170,19 @@ Then your config/initializers/hermitage.rb could looks like this:
|
|
169
170
|
|
170
171
|
# Some rules for :default config if needed...
|
171
172
|
|
172
|
-
Hermitage.
|
173
|
-
original
|
174
|
-
thumbnail
|
175
|
-
|
173
|
+
Hermitage.configure :pictures do
|
174
|
+
original 'image_path'
|
175
|
+
thumbnail 'image_path(:small)'
|
176
|
+
end
|
176
177
|
|
177
|
-
Hermitage.
|
178
|
-
original
|
179
|
-
thumbnail
|
180
|
-
list_tag
|
181
|
-
item_tag:
|
182
|
-
list_class
|
183
|
-
item_class
|
184
|
-
|
178
|
+
Hermitage.configure :posts do
|
179
|
+
original 'attachment'
|
180
|
+
thumbnail 'attachment(:tiny)'
|
181
|
+
list_tag :div
|
182
|
+
item_tag :div
|
183
|
+
list_class 'posts'
|
184
|
+
item_class 'post'
|
185
|
+
end
|
185
186
|
|
186
187
|
Now when you write `render_gallery_for @pictures` or `render_gallery_for @posts` Hermitage will automatically choose the proper config.
|
187
188
|
|
@@ -206,6 +207,8 @@ In the example above the darkening will be disabled and both navigation buttons
|
|
206
207
|
You can customize the following parameters:
|
207
208
|
|
208
209
|
* `looped` - set it to false if after the last image the first should not be shown
|
210
|
+
* `preloadNeighbours` - determines if next and previous images should be preloaded
|
211
|
+
* `slideshowEffect` - how to show/hide images (correct values are 'slide' and 'fade'). Default value is 'slide'
|
209
212
|
* `darkening.opacity` - opacity of darkening layer (0 if it should be disabled)
|
210
213
|
* `darkening.styles` - any custom CSS for darkening layer
|
211
214
|
* `navigationButtons.enabled` - are there navigation buttons
|
@@ -7,8 +7,10 @@ root = exports ? this
|
|
7
7
|
# Hermitage options
|
8
8
|
root.hermitage =
|
9
9
|
looped: true
|
10
|
+
preloadNeighbours: true
|
11
|
+
slideshowEffect: 'slide' # or 'fade'
|
10
12
|
|
11
|
-
# Image viewer
|
13
|
+
# Image viewer properties
|
12
14
|
default:
|
13
15
|
styles:
|
14
16
|
zIndex: 10
|
@@ -21,8 +23,7 @@ root.hermitage =
|
|
21
23
|
# Darkening properties
|
22
24
|
darkening:
|
23
25
|
default:
|
24
|
-
attributes:
|
25
|
-
id: 'overlay'
|
26
|
+
attributes: { id: 'overlay' }
|
26
27
|
styles:
|
27
28
|
position: 'absolute'
|
28
29
|
top: 0
|
@@ -30,13 +31,14 @@ root.hermitage =
|
|
30
31
|
width: '100%'
|
31
32
|
height: '100%'
|
32
33
|
backgroundColor: '#000'
|
33
|
-
|
34
|
-
opacity: 0.85 # 0 if you don't want darkening
|
35
34
|
styles: {}
|
35
|
+
opacity: 0.85 # 0 if you don't want darkening
|
36
|
+
|
36
37
|
|
37
38
|
# Navigation buttons' properties
|
38
39
|
navigationButtons:
|
39
40
|
default:
|
41
|
+
attributes: {}
|
40
42
|
styles:
|
41
43
|
position: 'absolute'
|
42
44
|
width: '50px'
|
@@ -54,27 +56,22 @@ root.hermitage =
|
|
54
56
|
|
55
57
|
next:
|
56
58
|
default:
|
57
|
-
attributes:
|
58
|
-
|
59
|
-
styles:
|
60
|
-
right: 0
|
59
|
+
attributes: { id: 'navigation-right' }
|
60
|
+
styles: { right: 0 }
|
61
61
|
styles: {}
|
62
62
|
text: '▶'
|
63
63
|
|
64
64
|
previous:
|
65
65
|
default:
|
66
|
-
attributes:
|
67
|
-
|
68
|
-
styles:
|
69
|
-
left: 0
|
66
|
+
attributes: { id: 'navigation-left' }
|
67
|
+
styles: { left: 0 }
|
70
68
|
styles: {}
|
71
69
|
text: '◀'
|
72
70
|
|
73
71
|
# Close button properties
|
74
72
|
closeButton:
|
75
73
|
default:
|
76
|
-
attributes:
|
77
|
-
id: 'close-button'
|
74
|
+
attributes: { id: 'close-button' }
|
78
75
|
styles:
|
79
76
|
position: 'absolute'
|
80
77
|
top: 0
|
@@ -84,16 +81,15 @@ root.hermitage =
|
|
84
81
|
fontFamily: 'Tahoma,Arial,Helvetica,sans-serif'
|
85
82
|
whiteSpace: 'nowrap'
|
86
83
|
cursor: 'pointer'
|
87
|
-
|
84
|
+
styles: {}
|
88
85
|
enabled: true
|
89
86
|
text: '×'
|
90
|
-
|
87
|
+
|
91
88
|
|
92
89
|
# Current image properties
|
93
90
|
image:
|
94
91
|
default:
|
95
|
-
attributes:
|
96
|
-
class: 'current'
|
92
|
+
attributes: { class: 'current' }
|
97
93
|
styles:
|
98
94
|
cursor: 'pointer'
|
99
95
|
maxWidth: 'none' # Fix the conflict with Twitter Bootstrap
|
@@ -102,8 +98,7 @@ root.hermitage =
|
|
102
98
|
# Bottom panel (for text or anything else)
|
103
99
|
bottomPanel:
|
104
100
|
default:
|
105
|
-
attributes:
|
106
|
-
class: 'bottom-panel'
|
101
|
+
attributes: { class: 'bottom-panel' }
|
107
102
|
styles:
|
108
103
|
position: 'absolute'
|
109
104
|
bottom: 0
|
@@ -112,8 +107,7 @@ root.hermitage =
|
|
112
107
|
|
113
108
|
text:
|
114
109
|
default:
|
115
|
-
attributes:
|
116
|
-
class: 'text'
|
110
|
+
attributes: { class: 'text' }
|
117
111
|
styles:
|
118
112
|
width: '100%'
|
119
113
|
height: '100%'
|
@@ -132,9 +126,6 @@ root.hermitage =
|
|
132
126
|
# Array of images of current gallery
|
133
127
|
images: []
|
134
128
|
|
135
|
-
# Array of texts for current gallery
|
136
|
-
texts: []
|
137
|
-
|
138
129
|
# Timeout before adjustig elements after window resize
|
139
130
|
resizeTimeout: 100
|
140
131
|
|
@@ -153,12 +144,10 @@ root.hermitage =
|
|
153
144
|
|
154
145
|
# Clear old images and texts array
|
155
146
|
hermitage.images.length = 0
|
156
|
-
hermitage.texts.length = 0
|
157
147
|
|
158
148
|
# Create new images and texts array
|
159
149
|
$.each $('a[rel="hermitage"]'), ->
|
160
|
-
|
161
|
-
hermitage.texts.push($(this).attr('title'))
|
150
|
+
addImage($(this).attr('href'), $(this).attr('title'))
|
162
151
|
|
163
152
|
# Set on click handlers to all elements that
|
164
153
|
# have 'hermitage' rel attribute
|
@@ -173,54 +162,89 @@ root.hermitage =
|
|
173
162
|
-> adjustImage(true),
|
174
163
|
hermitage.resizeTimeout
|
175
164
|
|
165
|
+
#
|
166
|
+
# Working with images array
|
167
|
+
#
|
168
|
+
addImage = (source, text) ->
|
169
|
+
image =
|
170
|
+
source: source
|
171
|
+
text: text
|
172
|
+
loaded: false
|
173
|
+
hermitage.images.push(image)
|
174
|
+
|
175
|
+
indexOfImage = (image) ->
|
176
|
+
source = if $(image).prop('tagName') is 'IMG' then $(image).attr('src') else $(image).attr('href')
|
177
|
+
imageObject = (img for img in hermitage.images when img.source is source)[0]
|
178
|
+
hermitage.images.indexOf(imageObject)
|
179
|
+
|
180
|
+
imageAt = (index) -> hermitage.images[index]
|
181
|
+
sourceFor = (index) -> imageAt(index).source
|
182
|
+
textFor = (index) -> imageAt(index).text
|
183
|
+
|
176
184
|
#
|
177
185
|
# Helpers
|
178
186
|
#
|
187
|
+
$.fn.applyStyles = (params, withAnimation, complete = undefined) ->
|
188
|
+
if withAnimation
|
189
|
+
this.animate(params, { duration: hermitage.animationDuration, queue: false, complete: complete })
|
190
|
+
else
|
191
|
+
this.css(params)
|
179
192
|
|
180
193
|
# Place element at the center of screen
|
181
194
|
$.fn.center = (withAnimation = false, width = 0, height = 0, offsetX = 0, offsetY = 0) ->
|
182
195
|
this.css('position', 'absolute')
|
183
196
|
|
184
197
|
width = $(this).outerWidth() if width is 0
|
185
|
-
height = $(this).
|
198
|
+
height = $(this).outerHeight() if height is 0
|
186
199
|
|
187
|
-
|
200
|
+
params =
|
188
201
|
top: "#{Math.max(0, ($(window).height() - height) / 2 + offsetY)}px"
|
189
202
|
left: "#{Math.max(0, ($(window).width() - width) / 2 + offsetX)}px"
|
190
203
|
|
191
|
-
|
192
|
-
this.animate(param, { duration: hermitage.animationDuration, queue: false })
|
193
|
-
else
|
194
|
-
this.css(param)
|
204
|
+
this.applyStyles(params, withAnimation)
|
195
205
|
|
196
206
|
$.fn.setSize = (width, height, withAnimation = false) ->
|
197
207
|
params = { width: width, height: height}
|
198
|
-
|
199
|
-
if withAnimation
|
200
|
-
this.animate(params, { duration: hermitage.animationDuration, queue: false })
|
201
|
-
else
|
202
|
-
this.css(params)
|
208
|
+
this.applyStyles(params, withAnimation)
|
203
209
|
|
204
210
|
$.fn.maximizeLineHeight = (withAnimation = false) ->
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
+
params = { lineHeight: "#{this.outerHeight()}px" }
|
212
|
+
this.applyStyles(params, withAnimation)
|
213
|
+
|
214
|
+
$.fn.showFromRight = (width = 0, height = 0, offsetX = 0, offsetY = 0) ->
|
215
|
+
this.css(left: "#{$(window).width()}px")
|
216
|
+
this.show()
|
217
|
+
this.center(true, width, height, offsetX, offsetY)
|
218
|
+
|
219
|
+
$.fn.showFromLeft = (width = 0, height = 0, offsetX = 0, offsetY = 0) ->
|
220
|
+
this.css(left: "#{- $(this).outerWidth()}px")
|
221
|
+
this.show()
|
222
|
+
this.center(true, width, height, offsetX, offsetY)
|
223
|
+
|
224
|
+
$.fn.hideToRight = (complete = undefined) ->
|
225
|
+
params = { left: "#{$(window).width()}px" }
|
226
|
+
this.applyStyles(params, true, complete)
|
227
|
+
|
228
|
+
$.fn.hideToLeft = (complete = undefined) ->
|
229
|
+
params = { left: "#{- $(this).outerWidth()}px" }
|
230
|
+
this.applyStyles(params, true, complete)
|
211
231
|
|
212
232
|
#
|
213
233
|
# Hermitage logic
|
214
234
|
#
|
215
235
|
|
236
|
+
# Creates base element with attributes and styles from params
|
237
|
+
createElement = (tag, params) ->
|
238
|
+
tag
|
239
|
+
.attr(params.default.attributes)
|
240
|
+
.css(params.default.styles)
|
241
|
+
.css(params.styles)
|
242
|
+
|
216
243
|
# Creates darkening overlay, shows it and sets its click handler
|
217
244
|
createDarkening = ->
|
218
|
-
$('<div>')
|
219
|
-
.attr(hermitage.darkening.default.attributes)
|
220
|
-
.css(hermitage.darkening.default.styles)
|
245
|
+
createElement($('<div>'), hermitage.darkening)
|
221
246
|
.css('opacity', hermitage.darkening.opacity)
|
222
247
|
.css('filter', "alpha(opacity='#{hermitage.darkening.opacity * 100}')")
|
223
|
-
.css(hermitage.darkening.styles)
|
224
248
|
.appendTo($('#hermitage'))
|
225
249
|
.hide()
|
226
250
|
.fadeIn()
|
@@ -228,60 +252,42 @@ createDarkening = ->
|
|
228
252
|
|
229
253
|
# Creates base navigation button and returns it
|
230
254
|
createNavigationButton = ->
|
231
|
-
$('<div>')
|
255
|
+
createElement($('<div>'), hermitage.navigationButtons)
|
232
256
|
.appendTo($('#hermitage'))
|
233
257
|
.hide()
|
234
|
-
.css(hermitage.navigationButtons.default.styles)
|
235
258
|
.maximizeLineHeight()
|
236
|
-
.css(hermitage.navigationButtons.styles)
|
237
259
|
|
238
260
|
# Creates right navigation button and returns it
|
239
261
|
createRightNavigationButton = ->
|
240
|
-
createNavigationButton()
|
241
|
-
.attr(hermitage.navigationButtons.next.default.attributes)
|
242
|
-
.css(hermitage.navigationButtons.next.default.styles)
|
243
|
-
.css(hermitage.navigationButtons.next.styles)
|
262
|
+
createElement(createNavigationButton(), hermitage.navigationButtons.next)
|
244
263
|
.text(hermitage.navigationButtons.next.text)
|
245
264
|
.click(showNextImage)
|
246
265
|
|
247
266
|
# Create left navigation button and returns it
|
248
267
|
createLeftNavigationButton = ->
|
249
|
-
createNavigationButton()
|
250
|
-
.attr(hermitage.navigationButtons.previous.default.attributes)
|
251
|
-
.css(hermitage.navigationButtons.previous.default.styles)
|
252
|
-
.css(hermitage.navigationButtons.previous.styles)
|
268
|
+
createElement(createNavigationButton(), hermitage.navigationButtons.previous)
|
253
269
|
.text(hermitage.navigationButtons.previous.text)
|
254
270
|
.click(showPreviousImage)
|
255
271
|
|
256
272
|
# Creates close button
|
257
273
|
createCloseButton = ->
|
258
|
-
$('<div>')
|
274
|
+
createElement($('<div>'), hermitage.closeButton)
|
259
275
|
.appendTo($('#hermitage'))
|
260
276
|
.hide()
|
261
|
-
.attr(hermitage.closeButton.default.attributes)
|
262
|
-
.css(hermitage.closeButton.default.styles)
|
263
277
|
.text(hermitage.closeButton.text)
|
264
|
-
.css(hermitage.closeButton.styles)
|
265
278
|
.click(closeGallery)
|
266
279
|
|
267
280
|
createBotomPanel = ->
|
268
|
-
bottomPanel = $('<div>')
|
281
|
+
bottomPanel = createElement($('<div>'), hermitage.bottomPanel)
|
269
282
|
.appendTo($('#hermitage'))
|
270
283
|
.hide()
|
271
|
-
.attr(hermitage.bottomPanel.default.attributes)
|
272
|
-
.css(hermitage.bottomPanel.default.styles)
|
273
|
-
.css(hermitage.bottomPanel.styles)
|
274
284
|
|
275
|
-
|
285
|
+
createElement($('<div>'), hermitage.bottomPanel.text)
|
276
286
|
.appendTo(bottomPanel)
|
277
|
-
.attr(hermitage.bottomPanel.text.default.attributes)
|
278
|
-
.css(hermitage.bottomPanel.text.default.styles)
|
279
|
-
.css(hermitage.bottomPanel.text.styles)
|
280
287
|
|
281
288
|
# Shows original image of the chosen one
|
282
289
|
openGallery = (image) ->
|
283
290
|
$('#hermitage')
|
284
|
-
.css('z-index', hermitage.zIndex)
|
285
291
|
.empty()
|
286
292
|
.show()
|
287
293
|
|
@@ -294,12 +300,9 @@ openGallery = (image) ->
|
|
294
300
|
showImage(indexOfImage(image))
|
295
301
|
|
296
302
|
# Shows image with specified index from images array
|
297
|
-
showImage = (index) ->
|
298
|
-
img = $('<img />')
|
299
|
-
.attr(
|
300
|
-
.css(hermitage.image.default.styles)
|
301
|
-
.attr('src', hermitage.images[index])
|
302
|
-
.css(hermitage.image.styles)
|
303
|
+
showImage = (index, direction = undefined) ->
|
304
|
+
img = createElement($('<img />'), hermitage.image)
|
305
|
+
.attr('src', sourceFor(index))
|
303
306
|
.hide()
|
304
307
|
.appendTo($('#hermitage'))
|
305
308
|
|
@@ -309,7 +312,8 @@ showImage = (index) ->
|
|
309
312
|
else
|
310
313
|
showPreviousImage()
|
311
314
|
|
312
|
-
adjustImage(false, img)
|
315
|
+
adjustImage(false, img, direction)
|
316
|
+
preloadNeighboursFor(index)
|
313
317
|
|
314
318
|
# Shows next image
|
315
319
|
showNextImage = ->
|
@@ -317,11 +321,12 @@ showNextImage = ->
|
|
317
321
|
if current.length is 1
|
318
322
|
index = indexOfImage(current)
|
319
323
|
return unless canShowNextAfter(index)
|
320
|
-
|
321
|
-
|
322
|
-
|
323
|
-
|
324
|
-
|
324
|
+
|
325
|
+
direction = if hermitage.slideshowEffect is 'slide' then 'left' else undefined
|
326
|
+
hideCurrentImage(direction)
|
327
|
+
|
328
|
+
direction = if hermitage.slideshowEffect is 'slide' then 'right' else undefined
|
329
|
+
showImage nextIndexAfter(index), direction
|
325
330
|
|
326
331
|
# Shows previous image
|
327
332
|
showPreviousImage = ->
|
@@ -329,18 +334,30 @@ showPreviousImage = ->
|
|
329
334
|
if current.length is 1
|
330
335
|
index = indexOfImage(current)
|
331
336
|
return unless canShowPreviousBefore(index)
|
332
|
-
|
333
|
-
if
|
334
|
-
|
335
|
-
|
336
|
-
|
337
|
+
|
338
|
+
direction = if hermitage.slideshowEffect is 'slide' then 'right' else undefined
|
339
|
+
hideCurrentImage(direction)
|
340
|
+
|
341
|
+
direction = if hermitage.slideshowEffect is 'slide' then 'left' else undefined
|
342
|
+
showImage previousIndexBefore(index), direction
|
337
343
|
|
338
344
|
# Hides current image
|
339
|
-
|
345
|
+
# Arguments:
|
346
|
+
# * direction - how to hide the image. It can apply options:
|
347
|
+
# * undefined - just fade out
|
348
|
+
# * 'right' - move to the right bound of screen
|
349
|
+
# * 'left' - move to the left bound of screen
|
350
|
+
hideCurrentImage = (direction = undefined) ->
|
340
351
|
current = $('img.current')
|
341
352
|
if current.length is 1
|
342
|
-
|
343
|
-
|
353
|
+
complete = -> current.remove()
|
354
|
+
|
355
|
+
if direction is 'right'
|
356
|
+
current.hideToRight(complete)
|
357
|
+
else if direction is 'left'
|
358
|
+
current.hideToLeft(complete)
|
359
|
+
else
|
360
|
+
current.fadeOut hermitage.animationDuration, complete
|
344
361
|
|
345
362
|
# Starts fade out animation and clears Hermitage at the end of animation
|
346
363
|
closeGallery = ->
|
@@ -355,7 +372,11 @@ closeGallery = ->
|
|
355
372
|
# Attributes:
|
356
373
|
# * `withAnimation` - boolean value determines if adjusting should be animated
|
357
374
|
# * `image` - currently opened image. It is optional argument and can be evaluated by the method itself.
|
358
|
-
|
375
|
+
# * direction - how to show the image. It can apply options:
|
376
|
+
# * undefined - just fade in
|
377
|
+
# * 'right' - move from the right bound of screen
|
378
|
+
# * 'left' - move from the left bound of screen
|
379
|
+
adjustImage = (withAnimation = false, image = undefined, direction = undefined) ->
|
359
380
|
|
360
381
|
if image is undefined
|
361
382
|
image = $('#hermitage img.current')
|
@@ -364,15 +385,16 @@ adjustImage = (withAnimation = false, image = undefined) ->
|
|
364
385
|
index = indexOfImage(image)
|
365
386
|
|
366
387
|
# Wait until source image is loaded
|
367
|
-
|
368
|
-
|
388
|
+
loadImage sourceFor(index), ->
|
389
|
+
imageAt(index).loaded = true
|
390
|
+
|
369
391
|
# Offset for image position
|
370
392
|
offsetY = 0
|
371
393
|
|
372
394
|
maxWidth = $(window).width() - $('#navigation-left').outerWidth() - $('#navigation-right').outerWidth()
|
373
395
|
maxHeight = $(window).height()
|
374
396
|
|
375
|
-
text =
|
397
|
+
text = textFor(index)
|
376
398
|
|
377
399
|
if text
|
378
400
|
offsetY = - $('#hermitage .bottom-panel').outerHeight() / 2
|
@@ -396,7 +418,13 @@ adjustImage = (withAnimation = false, image = undefined) ->
|
|
396
418
|
image
|
397
419
|
.setSize(this.width * scale, this.height * scale, withAnimation)
|
398
420
|
.center(withAnimation, this.width * scale, this.height * scale, 0, offsetY)
|
399
|
-
|
421
|
+
|
422
|
+
if direction is 'right'
|
423
|
+
image.showFromRight(this.width * scale, this.height * scale, 0, offsetY)
|
424
|
+
else if direction is 'left'
|
425
|
+
image.showFromLeft(this.width * scale, this.height * scale, 0, offsetY)
|
426
|
+
else
|
427
|
+
image.fadeIn(hermitage.animationDuration)
|
400
428
|
|
401
429
|
adjustNavigationButtons(withAnimation, image)
|
402
430
|
adjustCloseButton(withAnimation, image)
|
@@ -414,9 +442,7 @@ adjustNavigationButtons = (withAnimation, current) ->
|
|
414
442
|
next.maximizeLineHeight(withAnimation)
|
415
443
|
|
416
444
|
# Show or hide buttons
|
417
|
-
|
418
445
|
currentIndex = indexOfImage(current)
|
419
|
-
|
420
446
|
duration = hermitage.animationDuration
|
421
447
|
|
422
448
|
if canShowPreviousBefore(currentIndex)
|
@@ -452,10 +478,6 @@ adjustBottomPanel = (withAnimation) ->
|
|
452
478
|
|
453
479
|
panel.fadeIn(hermitage.animationDuration)
|
454
480
|
|
455
|
-
indexOfImage = (image) ->
|
456
|
-
href = if $(image).prop('tagName') is 'IMG' then $(image).attr('src') else $(image).attr('href')
|
457
|
-
hermitage.images.indexOf(href)
|
458
|
-
|
459
481
|
canShowNextAfter = (index) ->
|
460
482
|
if index < hermitage.images.length - 1
|
461
483
|
true
|
@@ -468,6 +490,36 @@ canShowPreviousBefore = (index) ->
|
|
468
490
|
else
|
469
491
|
hermitage.looped
|
470
492
|
|
493
|
+
preloadNeighboursFor = (index) ->
|
494
|
+
return unless hermitage.preloadNeighbours
|
495
|
+
|
496
|
+
nextIndex = nextIndexAfter(index)
|
497
|
+
previousIndex = previousIndexBefore(index)
|
498
|
+
|
499
|
+
if canShowNextAfter(index) and not imageAt(nextIndex).loaded
|
500
|
+
loadImage sourceFor(nextIndex), ->
|
501
|
+
imageAt(nextIndex).loaded = true
|
502
|
+
|
503
|
+
if canShowPreviousBefore(index) and not imageAt(previousIndex).loaded
|
504
|
+
loadImage sourceFor(previousIndex), ->
|
505
|
+
imageAt(previousIndex).loaded = true
|
506
|
+
|
507
|
+
loadImage = (source, complete) ->
|
508
|
+
$('<img />').attr('src', source).load(complete)
|
509
|
+
|
510
|
+
nextIndexAfter = (index) ->
|
511
|
+
if index < hermitage.images.length - 1
|
512
|
+
index + 1
|
513
|
+
else
|
514
|
+
0
|
515
|
+
|
516
|
+
previousIndexBefore = (index) ->
|
517
|
+
if index > 0
|
518
|
+
index - 1
|
519
|
+
else
|
520
|
+
hermitage.images.length - 1
|
521
|
+
|
522
|
+
|
471
523
|
# Initialize gallery on page load
|
472
524
|
$(document).ready(hermitage.init)
|
473
525
|
$(document).on('page:load', hermitage.init)
|
@@ -3,17 +3,18 @@
|
|
3
3
|
# Default config is used as base options hash for every gallery.
|
4
4
|
# You can configure any of its options and they will be applied for every rendering.
|
5
5
|
#
|
6
|
-
# Hermitage.
|
7
|
-
# original
|
8
|
-
# thumbnail
|
9
|
-
#
|
10
|
-
#
|
11
|
-
#
|
12
|
-
#
|
13
|
-
#
|
14
|
-
#
|
15
|
-
#
|
16
|
-
#
|
6
|
+
# Hermitage.configure :default do
|
7
|
+
# original 'file.url'
|
8
|
+
# thumbnail 'file.url(:thumbnail)'
|
9
|
+
# title nil
|
10
|
+
# list_tag :ul
|
11
|
+
# item_tag :li
|
12
|
+
# list_class 'thumbnails'
|
13
|
+
# item_class 'span4'
|
14
|
+
# link_class 'thumbnail'
|
15
|
+
# image_class nil
|
16
|
+
# each_slice nil
|
17
|
+
# end
|
17
18
|
|
18
19
|
# Also you can create your own configs that will be merged with default config to overwrite default options.
|
19
20
|
#
|
@@ -25,6 +26,6 @@
|
|
25
26
|
#
|
26
27
|
# All available options are listed in default config above.
|
27
28
|
#
|
28
|
-
# Hermitage.
|
29
|
-
|
30
|
-
#
|
29
|
+
# Hermitage.configure :images do
|
30
|
+
#
|
31
|
+
# end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
module Hermitage
|
2
|
+
|
3
|
+
class Configurator
|
4
|
+
|
5
|
+
Defaults.constants.each do |c|
|
6
|
+
define_method c.downcase do |value|
|
7
|
+
@config.merge!({ c.downcase.to_sym => value })
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
def respond_to?(method_name)
|
12
|
+
Defaults.constants.include? method_name.upcase.to_sym || super
|
13
|
+
end
|
14
|
+
|
15
|
+
def initialize(config_name, &block)
|
16
|
+
Hermitage.configs[config_name] ||= {}
|
17
|
+
@config = Hermitage.configs[config_name]
|
18
|
+
self.instance_eval(&block) if block_given?
|
19
|
+
end
|
20
|
+
|
21
|
+
# Returns full options hash for specified objects and options.
|
22
|
+
# It chooses config accoring to the class name of objects in passed array
|
23
|
+
# and merges default options with the chosen config and with passed options hash.
|
24
|
+
def self.options_for(objects, options = {})
|
25
|
+
config_name = objects.first.class.to_s.pluralize.underscore.to_sym if defined?(Rails) && !objects.empty?
|
26
|
+
config = Hermitage.configs[config_name] || Hermitage.configs[:default]
|
27
|
+
Hermitage.configs[:default].merge(config).merge(options)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
module Hermitage
|
2
|
+
|
3
|
+
# This class performs all the rendering logic for Rails apps
|
4
|
+
class RailsRenderCore
|
5
|
+
|
6
|
+
def initialize(objects, options = {})
|
7
|
+
@objects = objects
|
8
|
+
@options = Configurator.options_for(objects, options)
|
9
|
+
@template = ActionView::Base.new
|
10
|
+
end
|
11
|
+
|
12
|
+
# Renders gallery markup
|
13
|
+
def render
|
14
|
+
# Initialize the resulting tag
|
15
|
+
tag = ''
|
16
|
+
|
17
|
+
# Slice objects into separate lists
|
18
|
+
lists = slice_objects
|
19
|
+
|
20
|
+
# Render each list into `tag` variable
|
21
|
+
lists.each do |list|
|
22
|
+
items = list.collect { |item| render_link_for(item) }
|
23
|
+
tag << render_content_tag_for(items)
|
24
|
+
end
|
25
|
+
|
26
|
+
tag.html_safe
|
27
|
+
end
|
28
|
+
|
29
|
+
private
|
30
|
+
|
31
|
+
# Slices objects into separate lists if it's necessary
|
32
|
+
def slice_objects
|
33
|
+
unless @options[:each_slice]
|
34
|
+
[@objects]
|
35
|
+
else
|
36
|
+
@objects.each_slice(@options[:each_slice]).to_a
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
# Renders link to the specific image in a gallery
|
41
|
+
def render_link_for(item)
|
42
|
+
original_path = eval("item.#{@options[:original]}")
|
43
|
+
thumbnail_path = eval("item.#{@options[:thumbnail]}")
|
44
|
+
title = @options[:title] ? eval("item.#{@options[:title]}") : nil
|
45
|
+
image = @template.image_tag(thumbnail_path, class: @options[:image_class])
|
46
|
+
@template.link_to(image, original_path, rel: 'hermitage', class: @options[:link_class], title: title)
|
47
|
+
end
|
48
|
+
|
49
|
+
# Renders items into content tag
|
50
|
+
def render_content_tag_for(items)
|
51
|
+
@template.content_tag(@options[:list_tag], class: @options[:list_class]) do
|
52
|
+
items.collect { |item| @template.concat(@template.content_tag(@options[:item_tag], item, class: @options[:item_class])) }
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
end
|
57
|
+
|
58
|
+
end
|
data/lib/hermitage/version.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
require 'hermitage/rails_render_core' if defined? Rails
|
2
|
+
|
1
3
|
module Hermitage
|
2
4
|
|
3
5
|
module ViewHelpers
|
@@ -17,41 +19,7 @@ module Hermitage
|
|
17
19
|
# Config names are formed by the class name of the first element in array.
|
18
20
|
#
|
19
21
|
def render_gallery_for(objects, options = {})
|
20
|
-
|
21
|
-
config_name = objects.first.class.to_s.pluralize.underscore.to_sym if defined?(Rails) && !objects.empty?
|
22
|
-
config = Hermitage.configs.include?(config_name) ? config_name : :default
|
23
|
-
|
24
|
-
# Merge default options with the chosen config and with passed options
|
25
|
-
options = Hermitage.configs[:default].merge(Hermitage.configs[config]).merge(options)
|
26
|
-
|
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
|
32
|
-
end
|
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
|
-
original_path = eval("item.#{options[:original]}")
|
42
|
-
thumbnail_path = eval("item.#{options[:thumbnail]}")
|
43
|
-
title = options[:title] ? eval("item.#{options[:title]}") : nil
|
44
|
-
image = image_tag(thumbnail_path, class: options[:image_class])
|
45
|
-
link_to(image, original_path, rel: 'hermitage', class: options[:link_class], title: title)
|
46
|
-
end
|
47
|
-
|
48
|
-
# Convert these items into content tag string
|
49
|
-
tag << content_tag(options[:list_tag], class: options[:list_class]) do
|
50
|
-
items.collect { |item| concat(content_tag(options[:item_tag], item, class: options[:item_class])) }
|
51
|
-
end
|
52
|
-
end
|
53
|
-
|
54
|
-
tag.html_safe
|
22
|
+
RailsRenderCore.new(objects, options).render if defined? Rails
|
55
23
|
end
|
56
24
|
|
57
25
|
end
|
data/lib/hermitage.rb
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
require 'hermitage/version'
|
2
2
|
require 'hermitage/defaults'
|
3
|
+
require 'hermitage/configurator'
|
3
4
|
require 'hermitage/railtie' if defined? Rails
|
4
5
|
require 'hermitage/engine' if defined? Rails
|
5
6
|
|
@@ -10,4 +11,8 @@ module Hermitage
|
|
10
11
|
# Hash of configs presets
|
11
12
|
self.configs = { default: Hermitage::Defaults.to_hash() }
|
12
13
|
|
14
|
+
def self.configure(config_name, &block)
|
15
|
+
configurator = Configurator.new(config_name, &block)
|
16
|
+
end
|
17
|
+
|
13
18
|
end
|
@@ -3,14 +3,14 @@ require 'features_helper'
|
|
3
3
|
|
4
4
|
describe 'bottom_panel', type: :feature, js: true do
|
5
5
|
before(:each) do
|
6
|
-
Hermitage.
|
6
|
+
Hermitage.configure :default do title 'description' end
|
7
7
|
visit images_path
|
8
8
|
page.first('a[rel="hermitage"]').click
|
9
9
|
page.should have_css('img.current')
|
10
10
|
end
|
11
11
|
|
12
12
|
# I don't want any influence of this config to another tests
|
13
|
-
after(:each) {
|
13
|
+
after(:each) { reset_configs }
|
14
14
|
|
15
15
|
it 'has bottom panel' do
|
16
16
|
page.should have_css('#hermitage .bottom-panel')
|
@@ -0,0 +1,76 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe 'preload_neighbours', type: :feature, js: true do
|
4
|
+
|
5
|
+
def clickAtImage(index)
|
6
|
+
page.first("a[href='/assets/#{index}-full.png']").click
|
7
|
+
sleep(1) # We want wait some time while animations and images preloading
|
8
|
+
end
|
9
|
+
|
10
|
+
def images
|
11
|
+
evaluate_script('hermitage.images')
|
12
|
+
end
|
13
|
+
|
14
|
+
shared_examples 'preloader' do
|
15
|
+
it 'preloads proper images' do
|
16
|
+
loaded = images().collect { |image| image['loaded'] }
|
17
|
+
loaded.should == expected
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
before(:each) { visit images_path }
|
22
|
+
|
23
|
+
context 'preload enabled' do
|
24
|
+
context 'loop enabled' do
|
25
|
+
let(:expected) { Array.new(3, true) }
|
26
|
+
|
27
|
+
context '1st image' do
|
28
|
+
before(:each) { clickAtImage(0) }
|
29
|
+
it_behaves_like 'preloader'
|
30
|
+
end
|
31
|
+
|
32
|
+
context 'middle image' do
|
33
|
+
before(:each) { clickAtImage(1) }
|
34
|
+
it_behaves_like 'preloader'
|
35
|
+
end
|
36
|
+
|
37
|
+
context 'last image' do
|
38
|
+
before(:each) { clickAtImage(2) }
|
39
|
+
it_behaves_like 'preloader'
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
context 'loop disabled' do
|
44
|
+
before(:each) { evaluate_script('hermitage.looped = false') }
|
45
|
+
|
46
|
+
context '1st image' do
|
47
|
+
before(:each) { clickAtImage(0) }
|
48
|
+
let(:expected) { [true, true, false] }
|
49
|
+
it_behaves_like 'preloader'
|
50
|
+
end
|
51
|
+
|
52
|
+
context 'middle image' do
|
53
|
+
before(:each) { clickAtImage(1) }
|
54
|
+
let(:expected) { [true, true, true] }
|
55
|
+
it_behaves_like 'preloader'
|
56
|
+
end
|
57
|
+
|
58
|
+
context 'last image' do
|
59
|
+
before(:each) { clickAtImage(2) }
|
60
|
+
let(:expected) { [false, true, true] }
|
61
|
+
it_behaves_like 'preloader'
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
context 'preload disabled' do
|
67
|
+
before(:each) do
|
68
|
+
evaluate_script('hermitage.preloadNeighbours = false')
|
69
|
+
clickAtImage(1)
|
70
|
+
end
|
71
|
+
|
72
|
+
let(:expected) { [false, true, false] }
|
73
|
+
it_behaves_like 'preloader'
|
74
|
+
end
|
75
|
+
|
76
|
+
end
|
@@ -13,7 +13,14 @@ describe 'render gallery', type: :feature, js: true do
|
|
13
13
|
end
|
14
14
|
|
15
15
|
it 'fills images array' do
|
16
|
-
evaluate_script('hermitage.images')
|
16
|
+
images = evaluate_script('hermitage.images')
|
17
|
+
expected = Array.new(3) do |i|
|
18
|
+
{
|
19
|
+
'source' => "/assets/#{i}-full.png",
|
20
|
+
'loaded' => false
|
21
|
+
}
|
22
|
+
end
|
23
|
+
images.should == expected
|
17
24
|
end
|
18
25
|
|
19
26
|
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Hermitage::Configurator do
|
4
|
+
after(:each) { reset_configs }
|
5
|
+
|
6
|
+
describe '.new' do
|
7
|
+
it 'can create new config' do
|
8
|
+
Hermitage::Configurator.new(:new_config)
|
9
|
+
Hermitage.configs.keys.should == [:default, :new_config]
|
10
|
+
end
|
11
|
+
|
12
|
+
it 'can change existing config' do
|
13
|
+
Hermitage::Configurator.new(:default) do
|
14
|
+
title 'description'
|
15
|
+
end
|
16
|
+
|
17
|
+
Hermitage.configs[:default][:title].should == 'description'
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
describe '#respond_to?' do
|
22
|
+
it 'responds to methods corresponing to options' do
|
23
|
+
configurator = Hermitage::Configurator.new(:new_config)
|
24
|
+
Hermitage::Defaults.constants.each do |c|
|
25
|
+
configurator.should respond_to(c.downcase.to_sym)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
describe '.options_for' do
|
31
|
+
it 'merges default config with objects config and with options hash' do
|
32
|
+
Hermitage.configure :images do
|
33
|
+
title 'image_title'
|
34
|
+
list_class 'image-list'
|
35
|
+
end
|
36
|
+
Image = Class.new
|
37
|
+
objects = [Image.new]
|
38
|
+
result = Hermitage::Configurator.options_for(objects, { title: 'title' })
|
39
|
+
result.should == Hermitage::Defaults.to_hash.merge({ list_class: 'image-list', title: 'title' })
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -4,7 +4,7 @@ require 'dummy/app/models/dummy'
|
|
4
4
|
describe Hermitage::ViewHelpers, type: :helper do
|
5
5
|
|
6
6
|
# Reset Hermitage configs because they should not be shared among specs
|
7
|
-
|
7
|
+
after(:each) { reset_configs }
|
8
8
|
|
9
9
|
let(:template) { ActionView::Base.new }
|
10
10
|
|
@@ -52,7 +52,7 @@ describe Hermitage::ViewHelpers, type: :helper do
|
|
52
52
|
|
53
53
|
context 'with configs' do
|
54
54
|
|
55
|
-
before(:each) { Hermitage.
|
55
|
+
before(:each) { Hermitage.configure :dummy_images do list_class 'images-thumbnails' end }
|
56
56
|
|
57
57
|
let(:expected) { '<ul class="images-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><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>' }
|
58
58
|
subject { template.render_gallery_for images }
|
@@ -61,10 +61,10 @@ describe Hermitage::ViewHelpers, type: :helper do
|
|
61
61
|
context 'with overwritten defaults' do
|
62
62
|
|
63
63
|
before(:each) do
|
64
|
-
Hermitage.
|
65
|
-
list_class
|
66
|
-
item_class
|
67
|
-
|
64
|
+
Hermitage.configure :default do
|
65
|
+
list_class 'default-thumbnails'
|
66
|
+
item_class 'span3'
|
67
|
+
end
|
68
68
|
end
|
69
69
|
|
70
70
|
let(:expected) { '<ul class="images-thumbnails"><li class="span3"><a class="thumbnail" href="/assets/0-full.png" rel="hermitage"><img alt="0 thumbnail" src="/assets/0-thumbnail.png" /></a></li><li class="span3"><a class="thumbnail" href="/assets/1-full.png" rel="hermitage"><img alt="1 thumbnail" src="/assets/1-thumbnail.png" /></a></li></ul>' }
|
data/spec/spec_helper.rb
CHANGED
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.
|
4
|
+
version: 0.0.4.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-11 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: rails
|
@@ -209,8 +209,10 @@ files:
|
|
209
209
|
- lib/generators/hermitage/install_generator.rb
|
210
210
|
- lib/generators/hermitage/templates/hermitage.rb
|
211
211
|
- lib/hermitage.rb
|
212
|
+
- lib/hermitage/configurator.rb
|
212
213
|
- lib/hermitage/defaults.rb
|
213
214
|
- lib/hermitage/engine.rb
|
215
|
+
- lib/hermitage/rails_render_core.rb
|
214
216
|
- lib/hermitage/railtie.rb
|
215
217
|
- lib/hermitage/version.rb
|
216
218
|
- lib/hermitage/view_helpers.rb
|
@@ -266,6 +268,7 @@ files:
|
|
266
268
|
- spec/features/bottom_panel_spec.rb
|
267
269
|
- spec/features/engine_spec.rb
|
268
270
|
- spec/features/navigation_spec.rb
|
271
|
+
- spec/features/preload_neighbours_spec.rb
|
269
272
|
- spec/features/render_gallery_spec.rb
|
270
273
|
- spec/features/resize_spec.rb
|
271
274
|
- spec/features/scale_spec.rb
|
@@ -273,6 +276,7 @@ files:
|
|
273
276
|
- spec/features/viewer_spec.rb
|
274
277
|
- spec/features_helper.rb
|
275
278
|
- spec/generators/install_spec.rb
|
279
|
+
- spec/lib/hermitage/configurator_spec.rb
|
276
280
|
- spec/lib/hermitage/defaults_spec.rb
|
277
281
|
- spec/lib/hermitage/railtie_spec.rb
|
278
282
|
- spec/lib/hermitage/view_helpers_spec.rb
|
@@ -357,6 +361,7 @@ test_files:
|
|
357
361
|
- spec/features/bottom_panel_spec.rb
|
358
362
|
- spec/features/engine_spec.rb
|
359
363
|
- spec/features/navigation_spec.rb
|
364
|
+
- spec/features/preload_neighbours_spec.rb
|
360
365
|
- spec/features/render_gallery_spec.rb
|
361
366
|
- spec/features/resize_spec.rb
|
362
367
|
- spec/features/scale_spec.rb
|
@@ -364,6 +369,7 @@ test_files:
|
|
364
369
|
- spec/features/viewer_spec.rb
|
365
370
|
- spec/features_helper.rb
|
366
371
|
- spec/generators/install_spec.rb
|
372
|
+
- spec/lib/hermitage/configurator_spec.rb
|
367
373
|
- spec/lib/hermitage/defaults_spec.rb
|
368
374
|
- spec/lib/hermitage/railtie_spec.rb
|
369
375
|
- spec/lib/hermitage/view_helpers_spec.rb
|