jekyll_pig 0.0.0

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.
Files changed (3) hide show
  1. checksums.yaml +7 -0
  2. data/lib/jekyll_pig.rb +269 -0
  3. metadata +59 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 2fde6bb62e619909125906add217fe087f2e24668c340797dca8f3ce15787147
4
+ data.tar.gz: 7fe81995ba545a52cfceb8354a9930a553327ad5eb1c16a336aeba2268ff891c
5
+ SHA512:
6
+ metadata.gz: 7e67b67216c2c160f1f5eaec07240ec83d2e5c24022b5c0e47b3a8f84732293c66541ed095cf75457ca231fe46a3cae53c3ede2b579e47e984f01c5f36599109
7
+ data.tar.gz: c4e4d1c647920672b58109288c62d43c2f90dff97aa4adc7a4fc271a77b713e4f55c3eb29d84a40692bf0808d75933503b9dd61006bd701b6bfd9999908e3ea6
data/lib/jekyll_pig.rb ADDED
@@ -0,0 +1,269 @@
1
+
2
+ #install ImageMagick
3
+ #add gem "mini_magick" to your site's Gemfile
4
+ #run gem install mini_magick
5
+ #add <site source>/gallery folder with images
6
+ #use {% include gallery.html %} anywhere you want the gallery to appear
7
+ #add gallery.rb to your site's _plugins folder
8
+ #run jekyll build
9
+
10
+ require 'fileutils'
11
+ require 'json'
12
+ require 'mini_magick'
13
+
14
+ module JekyllPig
15
+
16
+ class SourceGallery
17
+ def initialize(path, name)
18
+ @path = path
19
+ @name = name
20
+ end
21
+ def to_s
22
+ "gallery #{@name} at #{@path}"
23
+ end
24
+ def path
25
+ @path
26
+ end
27
+ def name
28
+ @name
29
+ end
30
+ end
31
+
32
+ class JekyllPig < Jekyll::Generator
33
+
34
+ @@image_cache = {}
35
+
36
+ @@pig_min_js = '!function(t){"use strict";var i,e,s=(e=!(i=[]),{add:function(t){i.length||window.addEventListener("resize",n),i.push(t)},disable:function(){window.removeEventListener("resize",n)},reEnable:function(){window.addEventListener("resize",n)}});function n(){e||(e=!0,window.requestAnimationFrame?window.requestAnimationFrame(o):setTimeout(o,66))}function o(){i.forEach(function(t){t()}),e=!1}function a(t,i){return this.inRAF=!1,this.isTransitioning=!1,this.minAspectRatioRequiresTransition=!1,this.minAspectRatio=null,this.latestYOffset=0,this.lastWindowWidth=window.innerWidth,this.scrollDirection="down",this.visibleImages=[],this.settings={containerId:"pig",scroller:window,classPrefix:"pig",figureTagName:"figure",spaceBetweenImages:8,transitionSpeed:500,primaryImageBufferHeight:1e3,secondaryImageBufferHeight:300,thumbnailSize:20,urlForSize:function(t,i){return"/img/"+i+"/"+t},onClickHandler:function(t){},getMinAspectRatio:function(t){return t<=640?2:t<=1280?4:t<=1920?5:6},getImageSize:function(t){return t<=640?100:t<=1920?250:500}},function(t,i){for(var e in i)i.hasOwnProperty(e)&&(t[e]=i[e])}(this.settings,i||{}),this.container=document.getElementById(this.settings.containerId),this.container||console.error("Could not find element with ID "+this.settings.containerId),this.scroller=this.settings.scroller,this.images=this._parseImageData(t),function(t,i,e){var s="#"+t+" { position: relative;}."+i+"-figure { background-color: #D5D5D5; overflow: hidden; left: 0; position: absolute; top: 0; margin: 0;}."+i+"-figure img { left: 0; position: absolute; top: 0; height: 100%; width: 100%; opacity: 0; transition: "+e/1e3+"s ease opacity; -webkit-transition: "+e/1e3+"s ease opacity;}."+i+"-figure img."+i+"-thumbnail { -webkit-filter: blur(30px); filter: blur(30px); left: auto; position: relative; width: auto;}."+i+"-figure img."+i+"-loaded { opacity: 1;}",n=document.head||document.getElementsByTagName("head")[0],o=document.createElement("style");o.type="text/css",o.styleSheet?o.styleSheet.cssText=s:o.appendChild(document.createTextNode(s)),n.appendChild(o)}(this.settings.containerId,this.settings.classPrefix,this.settings.transitionSpeed),this}function r(t,i,e){return this.existsOnPage=!1,this.aspectRatio=t.aspectRatio,this.filename=t.filename,this.index=i,this.pig=e,this.classNames={figure:e.settings.classPrefix+"-figure",thumbnail:e.settings.classPrefix+"-thumbnail",loaded:e.settings.classPrefix+"-loaded"},this}a.prototype._getTransitionTimeout=function(){return 1.5*this.settings.transitionSpeed},a.prototype._getTransitionString=function(){return this.isTransitioning?this.settings.transitionSpeed/1e3+"s transform ease":"none"},a.prototype._recomputeMinAspectRatio=function(){var t=this.minAspectRatio;this.minAspectRatio=this.settings.getMinAspectRatio(this.lastWindowWidth),null!==t&&t!==this.minAspectRatio?this.minAspectRatioRequiresTransition=!0:this.minAspectRatioRequiresTransition=!1},a.prototype._parseImageData=function(t){var s=[];return t.forEach(function(t,i){var e=new r(t,i,this);s.push(e)}.bind(this)),s},a.prototype._computeLayout=function(){var s=parseInt(this.container.clientWidth),n=[],o=0,a=0,r=0;this._recomputeMinAspectRatio(),!this.isTransitioning&&this.minAspectRatioRequiresTransition&&(this.isTransitioning=!0,setTimeout(function(){this.isTransitioning=!1},this._getTransitionTimeout()));var h=this._getTransitionString();[].forEach.call(this.images,function(t,i){if(r+=parseFloat(t.aspectRatio),n.push(t),r>=this.minAspectRatio||i+1===this.images.length){r=Math.max(r,this.minAspectRatio);var e=(s-this.settings.spaceBetweenImages*(n.length-1))/r;n.forEach(function(t){var i=e*t.aspectRatio;t.style={width:parseInt(i),height:parseInt(e),translateX:o,translateY:a,transition:h},o+=i+this.settings.spaceBetweenImages}.bind(this)),n=[],r=0,a+=parseInt(e)+this.settings.spaceBetweenImages,o=0}}.bind(this)),this.totalHeight=a-this.settings.spaceBetweenImages},a.prototype._doLayout=function(){this.container.style.height=this.totalHeight+"px";var t="up"===this.scrollDirection?this.settings.primaryImageBufferHeight:this.settings.secondaryImageBufferHeight,i="down"===this.scrollDirection?this.settings.secondaryImageBufferHeight:this.settings.primaryImageBufferHeight,e=function(t){for(var i=0;isNaN(t.offsetTop)||(i+=t.offsetTop),t=t.offsetParent;);return i}(this.container),s=this.scroller===window?window.innerHeight:this.scroller.offsetHeight,n=this.latestYOffset-e-t,o=this.latestYOffset-e+s+i;this.images.forEach(function(t){t.style.translateY+t.style.height<n||t.style.translateY>o?t.hide():t.load()}.bind(this))},a.prototype._getOnScroll=function(){var i=this;return function(){var t=i.scroller===window?window.pageYOffset:i.scroller.scrollTop;i.previousYOffset=i.latestYOffset||t,i.latestYOffset=t,i.scrollDirection=i.latestYOffset>i.previousYOffset?"down":"up",i.inRAF||(i.inRAF=!0,window.requestAnimationFrame(function(){i._doLayout(),i.inRAF=!1}))}},a.prototype.enable=function(){return this.onScroll=this._getOnScroll(),this.scroller.addEventListener("scroll",this.onScroll),this.onScroll(),this._computeLayout(),this._doLayout(),s.add(function(){this.lastWindowWidth=this.scroller===window?window.innerWidth:this.scroller.offsetWidth,this._computeLayout(),this._doLayout()}.bind(this)),this},a.prototype.disable=function(){return this.scroller.removeEventListener("scroll",this.onScroll),s.disable(),this},r.prototype.load=function(){this.existsOnPage=!0,this._updateStyles(),this.pig.container.appendChild(this.getElement()),setTimeout(function(){this.existsOnPage&&(this.thumbnail||(this.thumbnail=new Image,this.thumbnail.src=this.pig.settings.urlForSize(this.filename,this.pig.settings.thumbnailSize),this.thumbnail.className=this.classNames.thumbnail,this.thumbnail.onload=function(){this.thumbnail&&(this.thumbnail.className+=" "+this.classNames.loaded)}.bind(this),this.getElement().appendChild(this.thumbnail)),this.fullImage||(this.fullImage=new Image,this.fullImage.src=this.pig.settings.urlForSize(this.filename,this.pig.settings.getImageSize(this.pig.lastWindowWidth)),this.fullImage.onload=function(){this.fullImage&&(this.fullImage.className+=" "+this.classNames.loaded)}.bind(this),this.getElement().appendChild(this.fullImage)))}.bind(this),100)},r.prototype.hide=function(){this.getElement()&&(this.thumbnail&&(this.thumbnail.src="",this.getElement().removeChild(this.thumbnail),delete this.thumbnail),this.fullImage&&(this.fullImage.src="",this.getElement().removeChild(this.fullImage),delete this.fullImage)),this.existsOnPage&&this.pig.container.removeChild(this.getElement()),this.existsOnPage=!1},r.prototype.getElement=function(){return this.element||(this.element=document.createElement(this.pig.settings.figureTagName),this.element.className=this.classNames.figure,this.element.addEventListener("click",function(){this.pig.settings.onClickHandler(this.filename)}.bind(this)),this._updateStyles()),this.element},r.prototype._updateStyles=function(){this.getElement().style.transition=this.style.transition,this.getElement().style.width=this.style.width+"px",this.getElement().style.height=this.style.height+"px",this.getElement().style.transform="translate3d("+this.style.translateX+"px,"+this.style.translateY+"px, 0)"},"function"==typeof define&&define.amd?define([],function(){return a}):"undefined"!=typeof module&&module.exports?module.exports=a:t.Pig=a}("undefined"!=typeof window?window:this);'
37
+
38
+ def full_size_html(gallery_name, name, date, prev_url, next_url)
39
+ "---\n" \
40
+ "layout: post\n" \
41
+ "title: #{name}\n" \
42
+ "date: #{date.strftime("%Y-%m-%d %H:%M:%S")}\n" \
43
+ "permalink: /assets/html/#{gallery_name}/#{name}.html\n" \
44
+ "exclude: true\n" \
45
+ "---\n" \
46
+ "<div><a href=\"#{prev_url}\" style=\"display:inline;\">prev</a><a href=\"#{next_url}\" style=\"display:inline; float:right\">next</a></div>\n" \
47
+ "<img src=\"{{site.baseurl}}/assets/img/#{gallery_name}/1024/#{name}\"/>\n"
48
+ end
49
+
50
+ def gallery_html(id, image_data)
51
+ "<div id='#{id}_pig'></div>\n" \
52
+ "<script src='{{site.baseurl}}/assets/js/pig.min.js'></script>\n" \
53
+ "<script>\n" \
54
+ "var #{id}_pig = new Pig(\n" \
55
+ " #{image_data.to_json()},\n" \
56
+ " {\n" \
57
+ " containerId: '#{id}_pig',\n" \
58
+ " classPrefix: '#{id}_pig',\n" \
59
+ " urlForSize: function(filename, size) {\n" \
60
+ " return '{{site.baseurl}}/assets/img/#{id}/' + size + '/' + filename;\n" \
61
+ " },\n" \
62
+ " onClickHandler: function(filename) {\n" \
63
+ " window.location.href = '{{site.baseurl}}/assets/html/#{id}/' + filename + '.html';\n" \
64
+ " }\n" \
65
+ " }\n" \
66
+ ").enable();\n" \
67
+ "</script>"
68
+ end
69
+
70
+ def image_html_url(gallery_name, image_name)
71
+ "/assets/html/#{gallery_name}/#{image_name}.html"
72
+ end
73
+
74
+ #read the image data from the _includes folder
75
+ def get_image_data(gallery_name)
76
+ image_data = []
77
+ #read image_data if existing
78
+ if File.exists?(File.join(@data_path, "#{gallery_name}.json"))
79
+ File.open(File.join(@data_path, "#{gallery_name}.json"), 'r') { |file|
80
+ #get array of image data (drop 'var imageData = ' and ';')
81
+ image_data = JSON.parse(file.read)
82
+ }
83
+ end
84
+ image_data
85
+ end
86
+
87
+ #read images that require processing from gallery
88
+ def get_images(gallery_path)
89
+ patterns = ['*.jpg', '*.jpeg', '*.png'].map { |ext| File.join(gallery_path, ext) }
90
+ Dir.glob(patterns).map { |path| File.basename(path) }
91
+ end
92
+
93
+ def get_image(gallery_path, image_name)
94
+ image = @@image_cache[File.join(gallery_path, image_name)]
95
+ if image == nil
96
+ image = MiniMagick::Image.open(File.join(gallery_path, image_name))
97
+ @@image_cache[File.join(gallery_path, image_name)] = image
98
+ end
99
+ image
100
+ end
101
+
102
+ def get_image_date(gallery_path, image_name)
103
+ image_date = nil
104
+ begin
105
+ image = get_image(gallery_path, image_name)
106
+ exif_date = image.exif['DateTimeOriginal']
107
+ if exif_date == nil
108
+ #no exif date, try to get from file name
109
+ image_date = Time.strptime(image_name, "%Y-%m-%d")
110
+ else
111
+ #try to get the image date from exif
112
+ image_date = Time.strptime(exif_date, "%Y:%m:%d %H:%M:%S")
113
+ end
114
+ rescue
115
+ #get the date from file if possible
116
+ image_date = File.mtime(File.join(gallery_path, image_name))
117
+ end
118
+ image_date
119
+ end
120
+
121
+ def get_previous_url(image_data, gallery_name, image_name)
122
+ index = image_data.index { |data| data['filename'] == image_name }
123
+ index = index - 1
124
+ if index < 0
125
+ index = image_data.length - 1
126
+ end
127
+ image_html_url(gallery_name, image_data[index]['filename'])
128
+ end
129
+
130
+ def get_next_url(image_data, gallery_name, image_name)
131
+ index = image_data.index { |data| data['filename'] == image_name }
132
+ index = index + 1
133
+ if index >= image_data.length
134
+ index = 0
135
+ end
136
+ image_html_url(gallery_name, image_data[index]['filename'])
137
+ end
138
+
139
+ #create thumbnails and fullsize image assets, and create full size html page for a given image
140
+ def process_image(image_data, gallery_id, gallery_path, image_name)
141
+ #puts "jekyll-pig: processing " << image_name
142
+ #create thumbs
143
+ [1024, 500, 250, 100, 20].each { |size|
144
+ size_out_path = File.join(@img_path, gallery_id, size.to_s)
145
+ resized_img_path = File.join(size_out_path, image_name)
146
+ if not File.exists? resized_img_path
147
+ image = get_image(gallery_path, image_name)
148
+ image.resize("x" + size.to_s)
149
+ FileUtils.mkdir_p size_out_path unless File.exists? size_out_path
150
+ image.write(resized_img_path)
151
+ end
152
+ }
153
+ full_size_html_path = File.join(@html_path, gallery_id, image_name + ".html")
154
+ #create full size html if it doesn't exist
155
+ if not File.exists? full_size_html_path
156
+ #get image date
157
+ image_date = get_image_date(gallery_path, image_name)
158
+ #create full size html text
159
+ full_size_html = full_size_html(gallery_id, image_name, image_date,
160
+ get_previous_url(image_data, gallery_id, image_name),
161
+ get_next_url(image_data, gallery_id, image_name))
162
+ File.open(full_size_html_path, 'w') { |file|
163
+ file.write(full_size_html)
164
+ }
165
+ end
166
+ end
167
+
168
+ def get_paths
169
+ @assets_path = File.join(@site.source, "assets")
170
+ @js_path = File.join(@assets_path, "js")
171
+ @data_path = File.join(@site.source, "_data")
172
+ @img_path = File.join(@assets_path, "img")
173
+ @html_path = File.join(@assets_path, "html")
174
+ @includes_path = File.join(@site.source, "_includes")
175
+ end
176
+
177
+ def get_galleries
178
+ galleries = []
179
+ config_galleries = Jekyll.configuration({})['galleries']
180
+ if config_galleries != nil
181
+ config_galleries.each do |gallery|
182
+ full_path = File.join(@site.source, gallery['path'])
183
+ if File.directory?(full_path)
184
+ galleries << SourceGallery.new(full_path, gallery['name'])
185
+ end
186
+ end
187
+ else
188
+ default_gallery_path = File.join(@site.source, 'gallery')
189
+ if File.directory?(default_gallery_path)
190
+ galleries << SourceGallery.new(default_gallery_path, 'gallery')
191
+ end
192
+ end
193
+ galleries
194
+ end
195
+
196
+ def make_output_paths
197
+ FileUtils.mkdir_p @assets_path unless File.exists? @assets_path
198
+ FileUtils.mkdir_p @js_path unless File.exists? @js_path
199
+ FileUtils.mkdir_p @img_path unless File.exists? @img_path
200
+ FileUtils.mkdir_p @html_path unless File.exists? @html_path
201
+ FileUtils.mkdir_p @includes_path unless File.exists? @includes_path
202
+ end
203
+
204
+ def augment_image_data(gallery, image_data, images)
205
+ images.each do |image_name|
206
+ #append data to image_data array if it's not already there
207
+ if not image_data.any? { |data| data['filename'] == image_name }
208
+ #get image date
209
+ image_date = get_image_date(gallery.path, image_name)
210
+ image = get_image(gallery.path, image_name)
211
+ image_data <<
212
+ {
213
+ 'datetime' => image_date.to_s,
214
+ 'filename' => image_name,
215
+ 'aspectRatio' => image.width.to_f / image.height
216
+ }
217
+ end
218
+ end
219
+ end
220
+
221
+ def generate(site)
222
+ @site = site
223
+ get_paths()
224
+ make_output_paths()
225
+ galleries = get_galleries()
226
+ galleries.each do |gallery|
227
+
228
+ #make gallery specific html and image output paths
229
+ html_output_path = File.join(@html_path, gallery.name)
230
+ FileUtils.mkdir_p html_output_path unless File.exists? html_output_path
231
+ img_output_path = File.join(@img_path, gallery.name)
232
+ FileUtils.mkdir_p img_output_path unless File.exists? img_output_path
233
+
234
+ #write pig.min.js to js path
235
+ if not File.exists? File.join(@js_path, 'pig.min.js')
236
+ File.open(File.join(@js_path, 'pig.min.js'), 'w') { |file| file.write(@@pig_min_js) }
237
+ end
238
+
239
+ #get image data from _data
240
+ image_data = get_image_data(gallery.name)
241
+
242
+ #get images from gallery
243
+ images = get_images(gallery.path)
244
+
245
+ #add any additional images to image_data
246
+ augment_image_data(gallery, image_data, images)
247
+
248
+ #sort image data
249
+ image_data = image_data.sort_by { |data| data['datetime'] }
250
+
251
+ #process images
252
+ images.each do |image_name|
253
+ #create thumbs, full size, and html assets for each image
254
+ process_image(image_data, gallery.name, gallery.path, image_name)
255
+ end
256
+
257
+ #write image_data
258
+ File.open(File.join(@data_path, "#{gallery.name}.json"), 'w') { |file|
259
+ file.write(image_data.to_json)
260
+ }
261
+
262
+ #save this gallery's includable content
263
+ File.open(File.join(@includes_path, "#{gallery.name}.html"), 'w') { |file|
264
+ file.write(gallery_html(gallery.name, image_data))
265
+ }
266
+ end
267
+ end
268
+ end
269
+ end
metadata ADDED
@@ -0,0 +1,59 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: jekyll_pig
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.0
5
+ platform: ruby
6
+ authors:
7
+ - Colin Holzman
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2020-03-04 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: mini_magick
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '4.10'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '4.10'
27
+ description: Uses ImageMagick and pig.js to create progressive image galleries for
28
+ Jekyll pages
29
+ email: me@colinholzman.xyz
30
+ executables: []
31
+ extensions: []
32
+ extra_rdoc_files: []
33
+ files:
34
+ - lib/jekyll_pig.rb
35
+ homepage: https://github.com/clnhlzmn/jekyll-pig
36
+ licenses:
37
+ - MIT
38
+ metadata: {}
39
+ post_install_message:
40
+ rdoc_options: []
41
+ require_paths:
42
+ - lib
43
+ required_ruby_version: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ required_rubygems_version: !ruby/object:Gem::Requirement
49
+ requirements:
50
+ - - ">="
51
+ - !ruby/object:Gem::Version
52
+ version: '0'
53
+ requirements: []
54
+ rubyforge_project:
55
+ rubygems_version: 2.7.6
56
+ signing_key:
57
+ specification_version: 4
58
+ summary: Jekyll image gallery generator
59
+ test_files: []