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