Browse Source

Adding Flickr and FancyBox

Josh Habdas 4 years ago
parent
commit
a6e6d25afd

+ 2
- 0
.travis.yml View File

@@ -27,3 +27,5 @@ env:
27 27
   - AWS_UPLOAD=true
28 28
   - secure: kMICJKIexxeIEaT0qUv4mTRpNeitiZYGNhXt6xwcki6hVsFNbKYIAddz4SHYNQ5VatC1Uat3Bv0GjJjDKC5rJdZn4b2NBRwwWmIzo+BOy6ejvZxrUPBAkqVNWnDD0Ht/q72buJtc6YVVXEdIdw/8CC5iWTBOgwqtcoE7Wsm7EdE=
29 29
   - secure: ONJOfICPkuk93jMl5402mX2FFmDx79iHIe5LHbs0bAAaBwVKzDe3ZWUI7JgiS9V21EQ8d3FtRnajRSLD/HMiQexjJbrMReRkohasLp4aoppf0Bd21lJDvZgN8lSYkUguYQTVq8vnE7mqeYmo8sx/X0IcEkXeUWgcr+EMVmX5YIw=
30
+  - FLICKR_API_KEY=e34453148031cab75ef047977aaa0f1f
31
+  - secure: hapooKMy5/R511fulVSUKru4DKU54DacH/a5D5sxPXp1vb4rF2Wro+5ycqbSJBH5XJydyf8bDpYiKlWdsCQ3ORWIkSIO0AwrpSiOMRPH8l2hoHn/KxdGQ3eNCQV8qPBw5ebllHhmyYmxRGin9Tp1UIc+BL7HY/lfk91d727H3Lw=

+ 3
- 0
Gemfile View File

@@ -16,6 +16,9 @@ group :development do
16 16
   gem 'liquid', '~> 2.3.0'
17 17
   gem 'directory_watcher', '1.4.1'
18 18
   gem 'aws-s3', '0.6.3'
19
+  gem 'flickraw'
20
+  gem 'builder', '> 2.0.0'
21
+  gem 'persistent_memoize'
19 22
 end
20 23
 
21 24
 gem 'sinatra', '~> 1.4.2'

+ 5
- 0
Gemfile.lock View File

@@ -16,6 +16,7 @@ GEM
16 16
       sass (~> 3.2.19)
17 17
     directory_watcher (1.4.1)
18 18
     fast-stemmer (1.0.2)
19
+    flickraw (0.9.8)
19 20
     fssm (0.2.10)
20 21
     haml (3.1.8)
21 22
     jekyll (0.12.1)
@@ -29,6 +30,7 @@ GEM
29 30
     liquid (2.3.0)
30 31
     maruku (0.7.2)
31 32
     mime-types (2.4.3)
33
+    persistent_memoize (0.0.1)
32 34
     posix-spawn (0.3.11)
33 35
     pygments.rb (0.3.7)
34 36
       posix-spawn (~> 0.3.6)
@@ -58,11 +60,14 @@ PLATFORMS
58 60
 DEPENDENCIES
59 61
   RedCloth (~> 4.2.9)
60 62
   aws-s3 (= 0.6.3)
63
+  builder (> 2.0.0)
61 64
   compass (~> 0.12.2)
62 65
   directory_watcher (= 1.4.1)
66
+  flickraw
63 67
   haml (~> 3.1.7)
64 68
   jekyll (~> 0.12)
65 69
   liquid (~> 2.3.0)
70
+  persistent_memoize
66 71
   pygments.rb (~> 0.3.4)
67 72
   rake (~> 10.2.2)
68 73
   rb-fsevent (~> 0.9)

+ 4
- 4
Rakefile View File

@@ -79,14 +79,14 @@ desc "Combine CSS"
79 79
 task :combine_css do
80 80
   puts "## Combining CSS"
81 81
   styles_dir = "#{source_dir}/stylesheets"
82
-  system "cat #{styles_dir}/screen.css > #{styles_dir}/all.css"
82
+  system "cat #{styles_dir}/screen.css #{styles_dir}/jquery.fancybox.css > #{styles_dir}/all.css"
83 83
 end
84 84
 
85 85
 desc "Combine JS"
86 86
 task :combine_js do
87 87
   puts "## Combining JS"
88 88
   scripts_dir = "#{source_dir}/javascripts"
89
-  system "cat #{scripts_dir}/libs/jquery.min.js #{scripts_dir}/modernizr-2.0.js #{scripts_dir}/octopress.js #{scripts_dir}/github.js > #{scripts_dir}/all.js"
89
+  system "cat #{scripts_dir}/libs/jquery.min.js #{scripts_dir}/modernizr-2.0.js #{scripts_dir}/octopress.js #{scripts_dir}/github.js #{scripts_dir}/jquery.fancybox.js > #{scripts_dir}/all.js"
90 90
 end
91 91
 
92 92
 desc "Combine CSS/JS"
@@ -314,9 +314,9 @@ task :integrate do
314 314
   FileUtils.mv Dir.glob("#{source_dir}/#{stash_dir}/*.*"), "#{source_dir}/#{posts_dir}/"
315 315
 end
316 316
 
317
-desc "Clean out caches: .pygments-cache, .gist-cache, .sass-cache"
317
+desc "Clean out caches: .pygments-cache, .gist-cache, .sass-cache, .flickr-cache"
318 318
 task :clean do
319
-  rm_rf [".pygments-cache/**", ".gist-cache/**", ".sass-cache/**", "source/stylesheets/screen.css"]
319
+  rm_rf [".pygments-cache/**", ".gist-cache/**", ".sass-cache/**", "source/stylesheets/screen.css", ".flickr-cache/**"]
320 320
 end
321 321
 
322 322
 desc "Move sass to sass.old, install sass theme updates, replace sass/custom with sass.old/custom"

+ 5
- 0
_config.yml View File

@@ -107,6 +107,11 @@ google_analytics_tracking_id: UA-6473963-1
107 107
 # Facebook Like
108 108
 facebook_like: false
109 109
 
110
+# Flickr (for Local testing only - DO NOT commit with actual secret)
111
+# flickr:
112
+#   api_key:
113
+#   shared_secret:
114
+
110 115
 # prose.io settings
111 116
 # https://github.com/prose/prose/wiki/Prose-Configuration
112 117
 prose:

+ 580
- 0
plugins/flickr.rb View File

@@ -0,0 +1,580 @@
1
+require "builder"
2
+require "cgi"
3
+require "flickraw"
4
+require "persistent_memoize"
5
+
6
+# CAUTION: This entire plugin is an XSS vector, as we accept HTML from the Flickr API and
7
+# republish it without any transformation or sanitization on our site. If someone can control
8
+# the HTML in titles or descriptions on Flickr they can inject arbitrary HTML into our site.
9
+# The risk is relatively low, since Flickr goes to great lengths to sanitize their inputs. But
10
+# an attacker could exploit some difference between the two sites (encoding, maybe) to do XSS.
11
+
12
+# The author of FlickRaw hasn't made this the default yet, but Flickr requires it :(
13
+FlickRaw.secure = true
14
+
15
+module TagUtil 
16
+  
17
+  # helper for parseMarkup
18
+  def self.appendChar(args, i, c)
19
+    if args[i] == nil 
20
+      args[i] = c
21
+    else 
22
+      args[i] << c
23
+    end
24
+  end
25
+
26
+  # parse a string of arguments separated by spaces
27
+  # arguments may be quoted to capture spaces
28
+  # backslash to escape delimiters or spaces
29
+  # e.g. 
30
+  #   <empty string>     -> []
31
+  #   <spaces>           -> []
32
+  #   foo                -> ["foo"]
33
+  #   foo bar            -> ["foo", "bar"]
34
+  #     foo   bar        -> ["foo", "bar"]
35
+  #   foo 'bar quux'     -> ["foo", "bar quux"]
36
+  #   foo bar\ quux      -> ["foo", "bar quux"]
37
+  #   foo "'bar quux'"   -> ["foo", "'bar quux'"]
38
+  #   foo "\"bar quux\"" -> ["foo", "\"bar quux\""]
39
+  #
40
+  def self.parseMarkup(markup)
41
+    escaped = false
42
+    inQuote = nil
43
+    args = []
44
+    i = 0
45
+    markup.split("").each do |c|
46
+      if escaped 
47
+        self.appendChar(args, i, c)
48
+        escaped = false
49
+      else
50
+        if c == '\\'
51
+          escaped = true 
52
+        else 
53
+          if inQuote != nil
54
+            if c == inQuote
55
+              i += 1
56
+              inQuote = nil
57
+            else
58
+              self.appendChar(args, i, c)
59
+            end
60
+          else
61
+            if c.match(/['"]/) 
62
+              inQuote = c
63
+            elsif c.match(/\s/) 
64
+              if args[i] != nil
65
+                i += 1
66
+              end
67
+            else
68
+              self.appendChar(args, i, c)
69
+            end
70
+          end
71
+        end
72
+      end
73
+    end
74
+
75
+    return args
76
+  end
77
+
78
+end
79
+
80
+
81
+
82
+class FlickrCache
83
+  def self.cacheFile(name)
84
+    cache_folder     = File.expand_path "../.flickr-cache", File.dirname(__FILE__)
85
+    FileUtils.mkdir_p cache_folder
86
+    return "#{cache_folder}/#{name}"
87
+  end
88
+end
89
+
90
+class FlickrApiCached
91
+  def initialize 
92
+    @photos = FlickrApiCachedPrefix.new(:photos)
93
+    @photosets = FlickrApiCachedPrefix.new(:photosets)
94
+  end
95
+
96
+  def photos
97
+    return @photos
98
+  end
99
+
100
+  def photosets
101
+    return @photosets
102
+  end
103
+end
104
+
105
+class FlickrApiCachedPrefix
106
+  include PersistentMemoize
107
+
108
+  def initialize(sym)
109
+    @prefix = flickr.send(sym)
110
+    memoize(:method_missing, FlickrCache.cacheFile("api_#{sym}"))
111
+  end
112
+
113
+  def method_missing(sym, *args, &block)
114
+    @prefix.send sym, *args, &block
115
+  end
116
+
117
+end
118
+
119
+class FlickrPhotoHtml
120
+
121
+  def initialize(id, params)
122
+    @id = id
123
+    @size = params['size'] || 's'
124
+
125
+    case @size
126
+    when 'o'
127
+      @zoom_size = 'o'
128
+    when 'z', 'b'
129
+      @zoom_size = 'b'
130
+    else
131
+      @zoom_size = 'z'
132
+    end
133
+
134
+    if params['title'].nil? or params['title'].empty?
135
+      @title = "Untitled photo"
136
+    else 
137
+      @title = params['title']
138
+    end
139
+
140
+    unless params['class'].nil? or params['class'].empty?
141
+      @klass = params['class']
142
+    end
143
+
144
+    if params['desc'].nil? or params['desc'].empty?
145
+      @desc = ""
146
+    else
147
+      @desc = params['desc']
148
+    end
149
+
150
+    unless params['gallery_id'].nil? or params['gallery_id'].empty?
151
+      @gallery_id = params['gallery_id']
152
+    end
153
+
154
+    unless params['page_url'].nil? or params['page_url'].empty?
155
+      @page_url = params['page_url']
156
+    end
157
+
158
+    unless params['username'].nil? or params['username'].empty?
159
+      @username = params['username']
160
+    end
161
+
162
+    # get the dimensions
163
+    @sizes = flickrCached.photos.getSizes(photo_id: @id)
164
+    @src, @width, @height = FlickrSizes.getSourceAndDimensionsForSize(@sizes, @size)
165
+  end
166
+
167
+  def getAnchorAttrs(dataTitleId)
168
+    zoomUrl, zoomWidth, zoomHeight = FlickrSizes.getSourceAndDimensionsForSize(@sizes, @zoom_size)
169
+    return {
170
+      'href' => zoomUrl,
171
+      'class' => 'fancybox',
172
+      'data-title-id' => dataTitleId,
173
+      'data-media' => 'photo'
174
+    }
175
+  end
176
+
177
+  def cssAttrsToStyle(cssAttrs)
178
+    cssAttrs.map { |(k, v)|
179
+        k.to_s + ': ' + v.to_s + ';'
180
+      }.join " "
181
+  end
182
+
183
+  def icon(x)
184
+    # do nothing
185
+  end
186
+
187
+  def toHtml
188
+    imgAttrs = {src: @src, title: @title}
189
+    imgCssAttrs = {}
190
+
191
+    figureClass = ['flickr-thumbnail']
192
+    unless @klass.nil? or @klass.empty?
193
+      figureClass.push(@klass)
194
+    end
195
+    figureAttrs = { 'class' => figureClass.join(" ") }
196
+    figureCssAttrs = {}
197
+
198
+    # The next bit is tricky - working around various browser bugs, and undesirable behavior.
199
+    # Desiderata
200
+    #   images and their captions should be surrounded by a nice visual offset, white border with  
201
+    #     drop shadow. This is a uniform border around the image if no caption, but surrounds caption if it
202
+    #     exists
203
+    #   large images should uniformly scale in width and height if viewport is narrow.
204
+    #   avoid an annoying webkit bug where many inline-blocks, without explicit height and width, sometimes
205
+    #    shrink to zero size - this happens with big sets
206
+    # 
207
+    # Solution : 
208
+    #   small images: include explicit width and height - avoids layout bug with sets
209
+    #       Also include explicit width for figures for smaller images 
210
+    #   large images: 
211
+    #        Do NOT include explicit width & height for largeish images, because due to other CSS that makes such 
212
+    #        images want to be 100% of the width, they scale the width only. If there is an explicit height then 
213
+    #        it is retained, thus we get a distorted image. Instead, use the inline-block trick here, because we don't care about 
214
+    #        laying out zillions of little images.
215
+    #
216
+    # We also add width to the figure element so it doesn't extend for the entire width, if small.
217
+    # And we make the figure an inline-block if it IS bigger than 450, so it snaps to the size of the image 
218
+    if (not (@width.nil?)) and @width.to_i < 450
219
+      imgCssAttrs['width'] = figureCssAttrs['width'] = @width.to_s + 'px';
220
+      unless @height.nil?
221
+        imgCssAttrs['height'] = @height.to_s + 'px';
222
+      end
223
+    else 
224
+      figureCssAttrs['display'] = 'inline-block'
225
+    end
226
+
227
+    imgAttrs["style"] = self.cssAttrsToStyle(imgCssAttrs)
228
+    figureAttrs["style"] = self.cssAttrsToStyle(figureCssAttrs)
229
+
230
+
231
+    xmlBuffer = ""
232
+    x = Builder::XmlMarkup.new( :target => xmlBuffer )
233
+
234
+  
235
+    dataTitleId = 'flickr-photo-' + @id 
236
+
237
+    anchorAttrs = self.getAnchorAttrs(dataTitleId) 
238
+    if @gallery_id
239
+      anchorAttrs['rel'] = @gallery_id;
240
+    end
241
+    captionAttrs = {
242
+      'id' => dataTitleId
243
+    }
244
+
245
+    x.figure(figureAttrs) { |x| 
246
+      x.a(anchorAttrs) { |x|
247
+        x.img(imgAttrs)
248
+        self.icon(x)
249
+      }
250
+      x.figcaption(captionAttrs) { |x|
251
+        x.h1{ |x|
252
+          x.a('class' => 'flickr-link', 'href' => @page_url) { |x| x << @title }
253
+          if @username
254
+            x << " by "
255
+            x << @username
256
+          end
257
+        }
258
+        x.div({'class' => 'description'}) { |x| 
259
+          x << @desc
260
+        } 
261
+      }
262
+    }
263
+   
264
+    xmlBuffer
265
+  end
266
+end
267
+
268
+
269
+class FlickrVideoPreviewHtml < FlickrPhotoHtml
270
+
271
+  def initialize(id, params)
272
+    super(id, params)
273
+    @secret = params['secret']
274
+    @origWidth = params['origWidth']
275
+    @origHeight = params['origHeight']
276
+    @contentId = 'flickr-video-content-' + @id
277
+    @klass = 'video-preview'
278
+    @zoom_size = 'z'
279
+  end
280
+
281
+  def getAnchorAttrs(dataTitleId)
282
+    return {
283
+      'href' => @page_url,
284
+      'class' => 'fancybox',
285
+      'data-title-id' => dataTitleId,
286
+      'data-media' => 'video',
287
+      'data-content-id' => '#' + @contentId
288
+    }
289
+  end
290
+
291
+  def getZoomLink
292
+    return @page_url
293
+  end
294
+
295
+  def icon(x) 
296
+    x.span( { 
297
+      'class' => 'video-icon', 
298
+    } ) {
299
+      x << '&#x25b6;'
300
+    }
301
+  end
302
+
303
+  def toHtml
304
+    html = ""
305
+    html << super 
306
+    html << "<div style='display:none'><div id='#{@contentId}'>"
307
+    html << FlickrVideoHtml.new(@id, @secret, @zoom_size, @origWidth, @origHeight).toHtml
308
+    html << "</div></div>"          
309
+    html
310
+  end
311
+end
312
+
313
+class FlickrVideoHtml
314
+  @@type="application/x-shockwave-flash" 
315
+  @@playerSwf="http://www.flickr.com/apps/video/stewart.swf?v=109786" 
316
+  @@classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000"
317
+  @@bgcolor="#000000" 
318
+  @@allowfullscreen="true" 
319
+  @@size = '__video__'
320
+
321
+  def initialize(id, photoSecret, size, origWidth, origHeight)
322
+    @id = id
323
+    @photoSecret = photoSecret
324
+    @size = size
325
+    @origWidth = origWidth
326
+    @origHeight = origHeight
327
+
328
+    @sizes = flickrCached.photos.getSizes(photo_id: @id)
329
+    @src, dummy1, dummy2 = FlickrSizes.getSourceAndDimensionsForSize(@sizes, 'site_mp4')
330
+    @poster, @width, @height = FlickrSizes.getSourceAndDimensionsForSize(@sizes, @size)
331
+  end
332
+
333
+  def toHtml
334
+    width, height = FlickrSizes.calculateDimensions(@size, @origWidth, @origHeight)
335
+
336
+    flashvarsHash = {
337
+      intl_lang: 'en-us',
338
+      photo_secret: @photoSecret,
339
+      photo_id: @id
340
+    }
341
+    flashvars = flashvarsHash.map { | (k, v) | 
342
+      CGI.escape(k.to_s) + '=' + CGI.escape(v.to_s)
343
+    }.join "&"
344
+
345
+    xmlBuffer = ""
346
+    x = Builder::XmlMarkup.new( :target => xmlBuffer )
347
+
348
+    x.object( { 'type' => @@type, 
349
+                'width' => width, 
350
+                'height' => height,
351
+                'data' => @@playerSwf,
352
+                'classid' => @@classid } ) { |x|
353
+      x.param( { 'name' => 'flashvars', 'value' => flashvars } )
354
+      x.param( { 'name' => 'movie', 'value' => @@playerSwf } )
355
+      x.param( { 'name' => 'bgcolor', 'value' => @@bgcolor } )
356
+      x.param( { 'name' => 'allowFullScreen', 'value' => @@allowfullscreen } )
357
+      x.embed( { 'type' => @@type, 
358
+                 'src' => @@playerSwf, 
359
+                 'bgcolor' => @@bgcolor,
360
+                 'allowfullscreen' => @@allowfullscreen,
361
+                 'flashvars' => flashvars,
362
+                 'width' => width,
363
+                 'height' => height } )
364
+    }
365
+
366
+    xmlBuffer
367
+  end
368
+end
369
+
370
+
371
+class FlickrSizes 
372
+  @@sizes = [    
373
+    {code: "original_video", label: "Original Video", max: nil },
374
+    {code: "mobile_mp4", label: "Mobile MP4", max: 480 },
375
+    {code: "site_mp4", label: "Site MP4", max: 640 },
376
+    {code: "video_player", label: "Video Player", max: 640 },
377
+    {code: "o", label: "Original", max: nil },
378
+    {code: "b", label: "Large", max: 1024 },
379
+    # {code: "c", label: "Medium 800", max: 75 },  # FlickrRaw doesn't know about this size
380
+    {code: "z", label: "Medium 640", max: 640 },
381
+    {code: "__NONE__", label: "Medium", max: 500 },
382
+    {code: "n", label: "Small 320", max: 320 },
383
+    {code: "m", label: "Small", max: 240 },
384
+    {code: "t", label: "Thumbnail", max: 100 },
385
+    {code: "q", label: "Large Square", max: 150 },
386
+    {code: "s", label: "Square", max: 75 }
387
+  ]
388
+
389
+  def self.sizes
390
+    @@sizes
391
+  end
392
+
393
+  def self.getSourceAndDimensionsForSize(sizes, size)
394
+    # try getting the size we wanted, then try getting ANY size, going from largest to smallest
395
+    sizeCodesToTry = [ size ] + @@sizes.map{ |s| s[:code] }
396
+    sizeInfo = nil
397
+    for code in sizeCodesToTry
398
+      sizeInfo = pickSize(sizes, code)
399
+      if sizeInfo
400
+        break
401
+      end
402
+    end
403
+    if (sizeInfo.nil?)
404
+      raise "could not get a size"
405
+    end
406
+    [ sizeInfo["source"], sizeInfo["width"], sizeInfo["height"] ]
407
+  end
408
+
409
+  def self.getSizeByCode(code) 
410
+    @@sizes.select{ |s| s[:code] == code }[0]
411
+  end
412
+
413
+  def self.pickSize(sizes, desiredSizeCode)
414
+    desiredSizeLabel = self.getSizeByCode(desiredSizeCode)[:label]
415
+    sizes.select{ |item| item["label"] == desiredSizeLabel }[0]
416
+  end
417
+
418
+  def self.calculateDimensions(desiredSizeCode, width, height)
419
+    width = width.to_i
420
+    height = height.to_i
421
+    size = self.getSizeByCode(desiredSizeCode)
422
+    factor = 1
423
+    unless size == nil or size[:max].nil?
424
+      factor = size[:max].to_f / [width, height].max
425
+    end
426
+    return [width, height].map { |dim| (dim * factor).to_i }
427
+  end
428
+end
429
+
430
+class FlickrImageTag < Liquid::Tag
431
+  include PersistentMemoize
432
+
433
+  # options we want:
434
+  # preview size
435
+  # class=right/left/center
436
+  # caption
437
+  # credit (via config)  
438
+  # popup description
439
+  def initialize(tag_name, markup, tokens)
440
+    super
441
+    
442
+    args = TagUtil.parseMarkup(markup)
443
+    @id   = args[0]
444
+    @size = args[1] || 'm'
445
+    @klass = args[2]
446
+    @desc = args[3]
447
+
448
+    unless FlickrSizes.getSizeByCode(@size)
449
+      raise "did not recognize photo size: #{@size}";
450
+    end
451
+    
452
+    memoize(:getHtml, FlickrCache.cacheFile("photo"))
453
+  end
454
+
455
+  def render(context)
456
+    self.getHtml(@id, @size, @klass, @desc)
457
+  end
458
+
459
+  def getHtml(id, size, klass, desc)
460
+    info = flickrCached.photos.getInfo(photo_id: id)
461
+    if desc.nil? or desc.empty?
462
+      desc = info['description']
463
+    end
464
+    
465
+    html = "HTML should go here"
466
+    params = {
467
+        "size" => size,
468
+        "secret" => info['secret'],
469
+        "username" => info['owner']['username'],
470
+        "page_url" => FlickRaw.url_photopage(info),
471
+        "title" => info['title'], 
472
+        "class" => klass, 
473
+        "desc" => desc,
474
+      }
475
+    if info['video']
476
+      # params['origWidth'] = info['video']['width']
477
+      # params['origHeight'] = info['video']['height']
478
+      # html = FlickrVideoHtml.new(@id, params).toHtml
479
+      html = FlickrVideoHtml.new(@id, 
480
+                                 info['secret'], 
481
+                                 @size, 
482
+                                 info['video']['width'],
483
+                                 info['video']['height'] ).toHtml
484
+    else 
485
+      html = FlickrPhotoHtml.new(@id, params).toHtml
486
+    end
487
+
488
+    html
489
+  end
490
+
491
+end
492
+
493
+class FlickrSetTag < Liquid::Tag
494
+  include PersistentMemoize
495
+
496
+  def initialize(tag_name, markup, tokens)
497
+    super
498
+    args = TagUtil.parseMarkup(markup)
499
+    @id = args[0]
500
+    @size = args[1] || 'm'
501
+    @showSetDesc = true
502
+    if (args[2] == 'nodesc')
503
+      @showSetDesc = false 
504
+    end
505
+
506
+    unless FlickrSizes.getSizeByCode(@size)
507
+      raise "did not recognize photo size for sets: #{@size}";
508
+    end
509
+
510
+    memoize(:getHtml, FlickrCache.cacheFile("set"))
511
+  end
512
+
513
+  def render(context)
514
+    getHtml(@id, @size, @showSetDesc)
515
+  end
516
+
517
+  def getHtml(id, size, showSetDesc)
518
+    info = flickrCached.photosets.getInfo(photoset_id: id)
519
+    
520
+    outputHtml = []
521
+
522
+    # assume the title is in the blog post title?
523
+    # titleHtml = '<p>' + info.title + '</p>'
524
+    # outputHtml.push(titleHtml)
525
+    
526
+    if showSetDesc and not info.description.empty?
527
+      outputHtml.push('<p>' + info.description.gsub(/\n/, '<br/>') + '</p>')
528
+    end
529
+
530
+    setPhotosHtml = [];
531
+    # pathalias will give us pretty urls to the photo page
532
+    # note, you have to request 'path_alias' but the returned prop is "pathalias"
533
+    apiExtras = ['url_' + size, 'url_o', 'path_alias', 'media'].join(',');
534
+    response = flickrCached.photosets.getPhotos(photoset_id: id, extras: apiExtras)
535
+    response['photo'].each do |photo|
536
+      params = {
537
+        "size" => size,
538
+        "secret" => photo['secret'],
539
+        "width" => photo["width_" + size],
540
+        "height" => photo["height_" + size],
541
+        "origWidth" => photo["width_o"],
542
+        "origHeight" => photo["height_o"],
543
+        "title" => photo["title"],
544
+        # this doesn't call the api, it constructs the URL from info retrieved
545
+        # Not using FlickRaw.url_photopage() because when user doesn't define pathalias, no owner in photo record,
546
+        # so can't construct URL. (bug in Flickr API?)
547
+        "page_url" => FlickRaw.url_photostream(response) + photo.id,
548
+        "gallery_id" => "flickr-set-" + id
549
+      }
550
+      photoInfoResponse = flickrCached.photos.getInfo(photo_id: photo["id"])
551
+      params["desc"] = photoInfoResponse["description"] 
552
+      params["username"] = photoInfoResponse["owner"]["username"] 
553
+      html = "<!-- thumbail here -->"
554
+      if photo['media'] == 'video'
555
+        html = FlickrVideoPreviewHtml.new(photo["id"], params).toHtml
556
+      else
557
+        html = FlickrPhotoHtml.new(photo["id"], params).toHtml
558
+      end
559
+      setPhotosHtml.push(html)
560
+    end
561
+
562
+    setHtml = '<section class="flickr-set">' + setPhotosHtml.join + '</section>'
563
+    outputHtml.push(setHtml)
564
+
565
+    outputHtml.join
566
+  end
567
+  
568
+
569
+end
570
+
571
+begin
572
+  FlickRaw.api_key        = ENV['FLICKR_API_KEY'] || Jekyll.configuration({})['flickr']['api_key']
573
+  FlickRaw.shared_secret  = ENV['FLICKR_API_SECRET'] || Jekyll.configuration({})['flickr']['shared_secret']
574
+rescue
575
+  $stderr.print("flickr.rb could not be configured. See documentation.\n")
576
+end
577
+
578
+def flickrCached; $flickrCached ||= FlickrApiCached.new end
579
+Liquid::Template.register_tag("flickr_image", FlickrImageTag)
580
+Liquid::Template.register_tag("flickr_set", FlickrSetTag)

+ 18
- 0
sass/_fancybox_fix.scss View File

@@ -0,0 +1,18 @@
1
+/* Fix fancybox style for Octopress.
2
+ * This is taken from the photos_tag.rb Photos Tag plugin for Jekyll by Devin Weaver.
3
+ * (https://gist.github.com/sukima/2631877)
4
+ * If you have both plugins, you should probably either eliminate the 
5
+ * {% fancyboxstylefix %} tag from your markup, or this CSS. Otherwise you might go a
6
+ * bit crazy if you need to modify it and something seems to be overwriting your changes.
7
+ */
8
+.fancybox-wrap { position: fixed !important; }
9
+  .fancybox-opened {
10
+    -webkit-border-radius: 4px !important;
11
+       -moz-border-radius: 4px !important;
12
+            border-radius: 4px !important;
13
+  }
14
+  .fancybox-close, .fancybox-prev span, .fancybox-next span {
15
+    background-color: transparent !important;
16
+    border: 0 !important;
17
+  }
18
+

+ 150
- 0
sass/plugins/_flickr.scss View File

@@ -0,0 +1,150 @@
1
+.flickr-set, .gallery {
2
+  text-align: center;
3
+}
4
+
5
+.flickr-set figure, .gallery li {
6
+  display: inline-block;
7
+  margin-right: 2em;
8
+  margin-bottom: 2em;
9
+  vertical-align: middle;
10
+}
11
+
12
+.gallery li {
13
+  list-style-type: none;
14
+}
15
+
16
+figcaption {
17
+  font-size: smaller;
18
+  line-height: 1.6em;
19
+}
20
+
21
+.illustration figcaption {
22
+  max-width: 40em;
23
+}
24
+
25
+.flickr-thumbnail {
26
+  @extend .flex-content;
27
+  @extend .basic-alignment;
28
+  @include shadow-box;
29
+  @include box-sizing(content-box);
30
+  background: #fff;
31
+}
32
+
33
+.flickr-thumbnail .description {
34
+  display: none;
35
+}
36
+
37
+.flickr-thumbnail h1 {
38
+  font-size: small;
39
+  font-family: $sans;
40
+  text-align: center;
41
+}
42
+
43
+.flickr-set figcaption {
44
+  display: none;
45
+}
46
+
47
+figure.video-preview {
48
+  position: relative;
49
+}
50
+figure.video-preview .video-icon {
51
+  display: block;
52
+  position: absolute;
53
+  width: 2em;
54
+  height: 1.5em;
55
+  bottom: 0.75em;
56
+  left: 0.75em;
57
+  color: white;
58
+  text-align: center;
59
+  background: #000; /* fallback */
60
+  background: rgba(0, 0, 0, 0.5);
61
+  @include border-radius(0.25em);
62
+  border: 1px solid white; /* color fallback */
63
+  border-color: rgba(255, 255, 255, 0.5);
64
+  /* this just gets the triangle visually balanced - would have to change for some other icon */
65
+  padding-left: .2em;
66
+  padding-top: .1em;
67
+  padding-right: .15em;
68
+}
69
+
70
+.noborder figure, .noborder, figure img, figure video, figure .flash-video {
71
+  @include border-radius(0px);
72
+  @include box-shadow(none);
73
+  @include box-sizing(content-box);
74
+  border: none;
75
+}
76
+
77
+/* otherwise, defaults to bottom, but leaves space for the 
78
+   descenders in the otherwise empty anchor */
79
+figure a img, figure a video, figure a .flash-video {
80
+  vertical-align: middle;
81
+}
82
+
83
+/* 
84
+ * When very narrow (mobile sites), center thumbnails 
85
+ * Note that we are using max-width here because the generic 
86
+ * .basic-alignment.left and .basic-alignment.right are not 
87
+ * otherwise tied to a min-width
88
+ */
89
+@media only screen and (max-width: 480px) {
90
+  figure.flickr-thumbnail {
91
+    &.left, &.right {
92
+      float: none;
93
+      display: block;
94
+      margin: 0 auto 1.5em; 
95
+      width: 100%;
96
+    }
97
+  }
98
+}
99
+
100
+/* customization for fancybox */
101
+/* fonts  and layout */
102
+.fancybox-title, .fancybox-title h1, .fancybox-title .description {
103
+  font-family: $sans !important;
104
+  text-align: center;
105
+}
106
+
107
+.fancybox-title {
108
+  max-height: 6em;
109
+  overflow-y: auto;
110
+}
111
+
112
+.flickr-thumbnail h1 {
113
+  font-weight: normal;
114
+}
115
+
116
+.flickr-thumbnail h1, .fancybox-title h1 {
117
+  margin: 0.25em;
118
+  font-size: 1em !important;
119
+}
120
+
121
+.flickr-link {
122
+  padding-left: 18px;
123
+  background: url(data:image/gif;base64,R0lGODlhEAAQAKUgAP8Aev8Ag/8HiwBT3QJX2gRg3ABq3iF94f1EpPpstVuY5P2Cvnaq6vKeyJO56IXI8vu83vLN4dXX1Mfb9f3V5OHi4PHf6Onr6Oru8+/18vz07P34+fH++f388/n+//z++////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////yH5BAEKACAALAAAAAAQABAAAAaBQFBlSCwWhZ+kcrkcMp9Kp2ezpCqpQ0/S+tloP9qNlHFQTMAQBGKRdCoGBYIBQwkA7AnPEFPoxx8LdwGDHEMbfn+BgwECHBISHQwEkwcYFgJ3AAuOEhkZDgoMGBsZEQkIDZ6PFxcarhoXGBkcs7GrrLi5ube6vRe8vrsgj8TFxiBBADs=) center left no-repeat;
124
+}
125
+
126
+
127
+/* always show navigation */
128
+.fancybox-nav {
129
+  width: 60px;       
130
+}
131
+
132
+.fancybox-nav span {
133
+  visibility: visible;
134
+  opacity: 0.20;
135
+}
136
+
137
+.fancybox-nav:hover span {
138
+  opacity: 1;
139
+}
140
+
141
+.fancybox-next {
142
+  right: -100px;
143
+}
144
+
145
+.fancybox-prev {
146
+  left: -100px;
147
+}
148
+
149
+
150
+

+ 1
- 1
sass/screen.scss View File

@@ -8,4 +8,4 @@
8 8
 @import "partials";
9 9
 @import "plugins/**/*";
10 10
 @import "custom/styles";
11
-@import "custom/rve"
11
+@import "custom/rve"

+ 1
- 0
source/_includes/custom/after_footer.html View File

@@ -0,0 +1 @@
1
+{% include custom/fancybox.html %}

+ 31
- 0
source/_includes/custom/fancybox.html View File

@@ -0,0 +1,31 @@
1
+<script>
2
+document.onreadystatechange = (function() {
3
+  if (document.readyState === 'complete') {
4
+    (function($) {
5
+      $(".fancybox[data-content-id]").each(function() {
6
+        this.href = $(this).data('content-id');
7
+      });
8
+      $(".fancybox").fancybox({
9
+        beforeLoad: function() {
10
+          var el,
11
+              id = $(this.element).data('title-id');
12
+          if (id) {
13
+            el = $('#' + id);
14
+            if (el.length) {
15
+              this.title = el.html();
16
+            }
17
+          }
18
+          if ($(this).data('content')) {
19
+            this.content = $(this).data('content');
20
+          }
21
+        },
22
+        helpers: {
23
+          title: {
24
+            type: 'inside'
25
+          }
26
+        }
27
+      });
28
+    }(jQuery));
29
+  }
30
+});
31
+</script>

+ 2
- 0
source/_includes/custom/head.html View File

@@ -1 +1,3 @@
1
+{% include google_analytics.html %}
2
+{% include custom/fancybox.html %}
1 3
 <script async src="//load.sumome.com/" data-sumo-site-id="a008fbcd2524dba5f97f74b7f8c94af6210160bdb8e027ceb9546a10ccf49704"></script>

+ 1
- 2
source/_includes/head.html View File

@@ -28,7 +28,6 @@
28 28
   <link href="//load.sumome.com" rel="dns-prefetch">
29 29
 
30 30
   <style>{% include inline.css %}</style>
31
-  <script async src="{{ root_url }}/javascripts/all.1504131838.js"></script>
31
+  <script async src="{{ root_url }}/javascripts/all.1504231208.js"></script>
32 32
   {% include custom/head.html %}
33
-  {% include google_analytics.html %}
34 33
 </head>

+ 1
- 1
source/_includes/inline.css
File diff suppressed because it is too large
View File


+ 2
- 2
source/_posts/2015-02-15-host-images-on-s3-with-octopress.md View File

@@ -15,13 +15,13 @@ tags:
15 15
   - cloud
16 16
 ---
17 17
 
18
-I was glancing through the Octopress plug-ins list yesterday and noticed something I hadn’t seen before, an [Image tag & uploader for S3](https://github.com/TheAshwanik/aws_s3_imagetag/). Curious to tinker around with it I set-up an account for S3 and integrated it today to decrease my blog header background image size and serve it from the cloud with caching.
18
+Glancing over the Octopress plug-ins list yesterday I noticed something I hadn’t seen before, an [Image tag & uploader for S3](https://github.com/TheAshwanik/aws_s3_imagetag/). Curious to tinker around with it I set-up an account for S3 and integrated it today to decrease my blog header background image size and serve it from the cloud with caching.
19 19
 
20 20
 > Learn how to host images on S3 with Octopress.
21 21
 
22 22
 <!-- more -->
23 23
 
24
-
24
+{% flickr_image 2311159445 z %}
25 25
 
26 26
 ## Create S3 bucket
27 27
 

BIN
source/images/toS3/blank.gif View File


BIN
source/images/toS3/fancybox_loading.gif View File


BIN
source/images/toS3/fancybox_loading@2x.gif View File


BIN
source/images/toS3/fancybox_overlay.png View File


BIN
source/images/toS3/fancybox_sprite.png View File


BIN
source/images/toS3/fancybox_sprite@2x.png View File


+ 2020
- 0
source/javascripts/jquery.fancybox.js
File diff suppressed because it is too large
View File


+ 274
- 0
source/stylesheets/jquery.fancybox.css View File

@@ -0,0 +1,274 @@
1
+/*! fancyBox v2.1.5 fancyapps.com | fancyapps.com/fancybox/#license */
2
+.fancybox-wrap,
3
+.fancybox-skin,
4
+.fancybox-outer,
5
+.fancybox-inner,
6
+.fancybox-image,
7
+.fancybox-wrap iframe,
8
+.fancybox-wrap object,
9
+.fancybox-nav,
10
+.fancybox-nav span,
11
+.fancybox-tmp
12
+{
13
+	padding: 0;
14
+	margin: 0;
15
+	border: 0;
16
+	outline: none;
17
+	vertical-align: top;
18
+}
19
+
20
+.fancybox-wrap {
21
+	position: absolute;
22
+	top: 0;
23
+	left: 0;
24
+	z-index: 8020;
25
+}
26
+
27
+.fancybox-skin {
28
+	position: relative;
29
+	background: #f9f9f9;
30
+	color: #444;
31
+	text-shadow: none;
32
+	-webkit-border-radius: 4px;
33
+	   -moz-border-radius: 4px;
34
+	        border-radius: 4px;
35
+}
36
+
37
+.fancybox-opened {
38
+	z-index: 8030;
39
+}
40
+
41
+.fancybox-opened .fancybox-skin {
42
+	-webkit-box-shadow: 0 10px 25px rgba(0, 0, 0, 0.5);
43
+	   -moz-box-shadow: 0 10px 25px rgba(0, 0, 0, 0.5);
44
+	        box-shadow: 0 10px 25px rgba(0, 0, 0, 0.5);
45
+}
46
+
47
+.fancybox-outer, .fancybox-inner {
48
+	position: relative;
49
+}
50
+
51
+.fancybox-inner {
52
+	overflow: hidden;
53
+}
54
+
55
+.fancybox-type-iframe .fancybox-inner {
56
+	-webkit-overflow-scrolling: touch;
57
+}
58
+
59
+.fancybox-error {
60
+	color: #444;
61
+	font: 14px/20px "Helvetica Neue",Helvetica,Arial,sans-serif;
62
+	margin: 0;
63
+	padding: 15px;
64
+	white-space: nowrap;
65
+}
66
+
67
+.fancybox-image, .fancybox-iframe {
68
+	display: block;
69
+	width: 100%;
70
+	height: 100%;
71
+}
72
+
73
+.fancybox-image {
74
+	max-width: 100%;
75
+	max-height: 100%;
76
+}
77
+
78
+#fancybox-loading, .fancybox-close, .fancybox-prev span, .fancybox-next span {
79
+	background-image: url('//s3.amazonaws.com/images.habdas.org/fancybox_sprite.png');
80
+}
81
+
82
+#fancybox-loading {
83
+	position: fixed;
84
+	top: 50%;
85
+	left: 50%;
86
+	margin-top: -22px;
87
+	margin-left: -22px;
88
+	background-position: 0 -108px;
89
+	opacity: 0.8;
90
+	cursor: pointer;
91
+	z-index: 8060;
92
+}
93
+
94
+#fancybox-loading div {
95
+	width: 44px;
96
+	height: 44px;
97
+	background: url('//s3.amazonaws.com/images.habdas.org/fancybox_loading.gif') center center no-repeat;
98
+}
99
+
100
+.fancybox-close {
101
+	position: absolute;
102
+	top: -18px;
103
+	right: -18px;
104
+	width: 36px;
105
+	height: 36px;
106
+	cursor: pointer;
107
+	z-index: 8040;
108
+}
109
+
110
+.fancybox-nav {
111
+	position: absolute;
112
+	top: 0;
113
+	width: 40%;
114
+	height: 100%;
115
+	cursor: pointer;
116
+	text-decoration: none;
117
+	background: transparent url('//s3.amazonaws.com/images.habdas.org/blank.gif'); /* helps IE */
118
+	-webkit-tap-highlight-color: rgba(0,0,0,0);
119
+	z-index: 8040;
120
+}
121
+
122
+.fancybox-prev {
123
+	left: 0;
124
+}
125
+
126
+.fancybox-next {
127
+	right: 0;
128
+}
129
+
130
+.fancybox-nav span {
131
+	position: absolute;
132
+	top: 50%;
133
+	width: 36px;
134
+	height: 34px;
135
+	margin-top: -18px;
136
+	cursor: pointer;
137
+	z-index: 8040;
138
+	visibility: hidden;
139
+}
140
+
141
+.fancybox-prev span {
142
+	left: 10px;
143
+	background-position: 0 -36px;
144
+}
145
+
146
+.fancybox-next span {
147
+	right: 10px;
148
+	background-position: 0 -72px;
149
+}
150
+
151
+.fancybox-nav:hover span {
152
+	visibility: visible;
153
+}
154
+
155
+.fancybox-tmp {
156
+	position: absolute;
157
+	top: -99999px;
158
+	left: -99999px;
159
+	visibility: hidden;
160
+	max-width: 99999px;
161
+	max-height: 99999px;
162
+	overflow: visible !important;
163
+}
164
+
165
+/* Overlay helper */
166
+
167
+.fancybox-lock {
168
+    overflow: hidden !important;
169
+    width: auto;
170
+}
171
+
172
+.fancybox-lock body {
173
+    overflow: hidden !important;
174
+}
175
+
176
+.fancybox-lock-test {
177
+    overflow-y: hidden !important;
178
+}
179
+
180
+.fancybox-overlay {
181
+	position: absolute;
182
+	top: 0;
183
+	left: 0;
184
+	overflow: hidden;
185
+	display: none;
186
+	z-index: 8010;
187
+	background: url('//s3.amazonaws.com/images.habdas.org/fancybox_overlay.png');
188
+}
189
+
190
+.fancybox-overlay-fixed {
191
+	position: fixed;
192
+	bottom: 0;
193
+	right: 0;
194
+}
195
+
196
+.fancybox-lock .fancybox-overlay {
197
+	overflow: auto;
198
+	overflow-y: scroll;
199
+}
200
+
201
+/* Title helper */
202
+
203
+.fancybox-title {
204
+	visibility: hidden;
205
+	font: normal 13px/20px "Helvetica Neue",Helvetica,Arial,sans-serif;
206
+	position: relative;
207
+	text-shadow: none;
208
+	z-index: 8050;
209
+}
210
+
211
+.fancybox-opened .fancybox-title {
212
+	visibility: visible;
213
+}
214
+
215
+.fancybox-title-float-wrap {
216
+	position: absolute;
217
+	bottom: 0;
218
+	right: 50%;
219
+	margin-bottom: -35px;
220
+	z-index: 8050;
221
+	text-align: center;
222
+}
223
+
224
+.fancybox-title-float-wrap .child {
225
+	display: inline-block;
226
+	margin-right: -100%;
227
+	padding: 2px 20px;
228
+	background: transparent; /* Fallback for web browsers that doesn't support RGBa */
229
+	background: rgba(0, 0, 0, 0.8);
230
+	-webkit-border-radius: 15px;
231
+	   -moz-border-radius: 15px;
232
+	        border-radius: 15px;
233
+	text-shadow: 0 1px 2px #222;
234
+	color: #FFF;
235
+	font-weight: bold;
236
+	line-height: 24px;
237
+	white-space: nowrap;
238
+}
239
+
240
+.fancybox-title-outside-wrap {
241
+	position: relative;
242
+	margin-top: 10px;
243
+	color: #fff;
244
+}
245
+
246
+.fancybox-title-inside-wrap {
247
+	padding-top: 10px;
248
+}
249
+
250
+.fancybox-title-over-wrap {
251
+	position: absolute;
252
+	bottom: 0;
253
+	left: 0;
254
+	color: #fff;
255
+	padding: 10px;
256
+	background: #000;
257
+	background: rgba(0, 0, 0, .8);
258
+}
259
+
260
+/*Retina graphics!*/
261
+@media only screen and (-webkit-min-device-pixel-ratio: 1.5),
262
+	   only screen and (min--moz-device-pixel-ratio: 1.5),
263
+	   only screen and (min-device-pixel-ratio: 1.5){
264
+
265
+	#fancybox-loading, .fancybox-close, .fancybox-prev span, .fancybox-next span {
266
+		background-image: url('//s3.amazonaws.com/images.habdas.org/fancybox_sprite@2x.png');
267
+		background-size: 44px 152px; /*The size of the normal image, half the size of the hi-res image*/
268
+	}
269
+
270
+	#fancybox-loading div {
271
+		background-image: url('//s3.amazonaws.com/images.habdas.org/fancybox_loading@2x.gif');
272
+		background-size: 24px 24px; /*The size of the normal image, half the size of the hi-res image*/
273
+	}
274
+}

Loading…
Cancel
Save