jekyll_pig 0.0.0

Sign up to get free protection for your applications and to get access to all the features.
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: []