hermitage 0.0.3 → 0.0.4.1

Sign up to get free protection for your applications and to get access to all the features.
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
  [![Build Status](https://travis-ci.org/ImmaculatePine/hermitage.png?branch=master)](https://travis-ci.org/ImmaculatePine/hermitage)
4
+ [![Code Climate](https://codeclimate.com/github/ImmaculatePine/hermitage.png)](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.configs[:default].merge!({
142
- original: 'image.url(:medium)',
143
- thumbnail: 'image.url(:small)'
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.configs[:pictures] = {
173
- original: 'image_path',
174
- thumbnail: 'image_path(:small)'
175
- }
173
+ Hermitage.configure :pictures do
174
+ original 'image_path'
175
+ thumbnail 'image_path(:small)'
176
+ end
176
177
 
177
- Hermitage.configs[:posts] = {
178
- original: 'attachment',
179
- thumbnail: 'attachment(:tiny)',
180
- list_tag: :div,
181
- item_tag: div,
182
- list_class: 'posts',
183
- item_class: 'post'
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 z-index property
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
- id: 'navigation-right'
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
- id: 'navigation-left'
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
- styles: {}
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
- hermitage.images.push($(this).attr('href'))
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).outerWidth() if height is 0
198
+ height = $(this).outerHeight() if height is 0
186
199
 
187
- param =
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
- if withAnimation
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
- param = { lineHeight: "#{this.outerHeight()}px" }
206
-
207
- if withAnimation
208
- this.animate(param, { duration: hermitage.animationDuration, queue: false })
209
- else
210
- this.css(param)
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
- text = $('<div>')
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(hermitage.image.default.attributes)
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
- hideCurrentImage()
321
- if index < hermitage.images.length - 1
322
- showImage(index + 1)
323
- else
324
- showImage(0)
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
- hideCurrentImage()
333
- if index > 0
334
- showImage(index - 1)
335
- else
336
- showImage(hermitage.images.length - 1)
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
- hideCurrentImage = ->
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
- current.fadeOut hermitage.animationDuration, ->
343
- current.remove()
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
- adjustImage = (withAnimation = false, image = undefined) ->
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
- $('<img />').attr('src', hermitage.images[index]).load ->
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 = hermitage.texts[index]
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
- .fadeIn(hermitage.animationDuration)
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.configs[:default].merge!({
7
- # original: 'file.url',
8
- # thumbnail: 'file.url(:thumbnail)',
9
- # list_tag: :ul,
10
- # item_tag: :li,
11
- # list_class: 'thumbnails',
12
- # item_class: 'span4',
13
- # link_class: 'thumbnail',
14
- # image_class: nil,
15
- # each_slice: nil
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.configs[:images] = {
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
@@ -1,3 +1,3 @@
1
1
  module Hermitage
2
- VERSION = '0.0.3'
2
+ VERSION = '0.0.4.1'
3
3
  end
@@ -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
- # Choose config accoring to class name of objects in passed array
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.configs[:default].merge!({ title: 'description' })
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) { Hermitage.configs[:default] = Hermitage::Defaults.to_hash() }
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').should == ['/assets/0-full.png', '/assets/1-full.png', '/assets/2-full.png']
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
- before(:each) { Hermitage.configs = { default: Hermitage::Defaults.to_hash() } }
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.configs[:dummy_images] = { list_class: 'images-thumbnails' } }
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.configs[:default].merge!({
65
- list_class: 'default-thumbnails',
66
- item_class: 'span3'
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
@@ -23,4 +23,8 @@ RSpec.configure do |config|
23
23
  config.order = 'random'
24
24
  end
25
25
 
26
- Capybara.javascript_driver = :poltergeist
26
+ Capybara.javascript_driver = :poltergeist
27
+
28
+ def reset_configs
29
+ Hermitage.configs = { default: Hermitage::Defaults.to_hash() }
30
+ end
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.3
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-08 00:00:00.000000000 Z
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