jekyll-octopod 0.4.0 → 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (134) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +5 -1
  3. data/assets/_config.yml.sample +47 -0
  4. data/assets/_includes/disqus_count.html +13 -0
  5. data/assets/_includes/disqus_thread.html +13 -0
  6. data/assets/_includes/flattr_loader.html +14 -0
  7. data/assets/_includes/post.html +18 -0
  8. data/assets/_includes/post_header.html +7 -0
  9. data/assets/_includes/post_line.html +3 -0
  10. data/assets/_includes/sidebar.html +100 -0
  11. data/assets/_includes/tweet_us.html +3 -0
  12. data/assets/_layouts/default.html +87 -0
  13. data/assets/_layouts/feed.xml +66 -0
  14. data/assets/_layouts/page.html +15 -0
  15. data/assets/_layouts/player_index.html +40 -0
  16. data/assets/_layouts/post.html +10 -0
  17. data/assets/_plugins/flattr_filters.rb +108 -0
  18. data/assets/_plugins/octopod_filters.rb +399 -0
  19. data/assets/_plugins/podcast_player_page_generator.rb +35 -0
  20. data/assets/_plugins/site.rb +16 -0
  21. data/assets/_plugins/static_file.rb +30 -0
  22. data/assets/_posts/2016-03-22-episode0.md +26 -0
  23. data/assets/apple-touch-icon-precomposed.png +0 -0
  24. data/assets/css/bootflat.css +2557 -0
  25. data/assets/css/bootstrap.min.css +5 -0
  26. data/assets/css/custom_styles.css +7 -0
  27. data/assets/css/font-awesome.min.css +4 -0
  28. data/assets/episodes.m4a.rss +5 -0
  29. data/assets/episodes.mp3.rss +5 -0
  30. data/assets/episodes.ogg.rss +5 -0
  31. data/assets/episodes/episode0.m4a +0 -0
  32. data/assets/episodes/episode0.mp3 +0 -0
  33. data/assets/episodes/episode0.ogg +0 -0
  34. data/assets/favicon.ico +0 -0
  35. data/assets/fonts/FontAwesome.otf +0 -0
  36. data/assets/fonts/fontawesome-webfont.eot +0 -0
  37. data/assets/fonts/fontawesome-webfont.svg +655 -0
  38. data/assets/fonts/fontawesome-webfont.ttf +0 -0
  39. data/assets/fonts/fontawesome-webfont.woff +0 -0
  40. data/assets/fonts/fontawesome-webfont.woff2 +0 -0
  41. data/assets/general_feed.xml +31 -0
  42. data/assets/img/favicon.ico +0 -0
  43. data/assets/img/glyphicons-halflings-white.png +0 -0
  44. data/assets/img/glyphicons-halflings.png +0 -0
  45. data/assets/img/logo-360x360.png +0 -0
  46. data/assets/img/logo-itunes.jpg +0 -0
  47. data/assets/img/logo.jpg +0 -0
  48. data/assets/img/logo/android-icon-144x144.png +0 -0
  49. data/assets/img/logo/android-icon-192x192.png +0 -0
  50. data/assets/img/logo/android-icon-36x36.png +0 -0
  51. data/assets/img/logo/android-icon-48x48.png +0 -0
  52. data/assets/img/logo/android-icon-72x72.png +0 -0
  53. data/assets/img/logo/android-icon-96x96.png +0 -0
  54. data/assets/img/logo/apple-icon-114x114.png +0 -0
  55. data/assets/img/logo/apple-icon-120x120.png +0 -0
  56. data/assets/img/logo/apple-icon-144x144.png +0 -0
  57. data/assets/img/logo/apple-icon-152x152.png +0 -0
  58. data/assets/img/logo/apple-icon-180x180.png +0 -0
  59. data/assets/img/logo/apple-icon-57x57.png +0 -0
  60. data/assets/img/logo/apple-icon-60x60.png +0 -0
  61. data/assets/img/logo/apple-icon-72x72.png +0 -0
  62. data/assets/img/logo/apple-icon-76x76.png +0 -0
  63. data/assets/img/logo/apple-icon-precomposed.png +0 -0
  64. data/assets/img/logo/apple-icon.png +0 -0
  65. data/assets/img/logo/browserconfig.xml +2 -0
  66. data/assets/img/logo/favicon-16x16.png +0 -0
  67. data/assets/img/logo/favicon-32x32.png +0 -0
  68. data/assets/img/logo/favicon-96x96.png +0 -0
  69. data/assets/img/logo/favicon.ico +0 -0
  70. data/assets/img/logo/manifest.json +41 -0
  71. data/assets/img/logo/ms-icon-144x144.png +0 -0
  72. data/assets/img/logo/ms-icon-150x150.png +0 -0
  73. data/assets/img/logo/ms-icon-310x310.png +0 -0
  74. data/assets/img/logo/ms-icon-70x70.png +0 -0
  75. data/assets/index.html +14 -0
  76. data/assets/inprint.md +19 -0
  77. data/assets/js/bootstrap.min.js +7 -0
  78. data/assets/js/custom.js +9 -0
  79. data/assets/js/icheck.min.js +11 -0
  80. data/assets/js/jquery.fs.selecter.min.js +9 -0
  81. data/assets/js/jquery.fs.stepper.min.js +9 -0
  82. data/assets/podlove-web-player/bin/flashmediaelement.swf +0 -0
  83. data/assets/podlove-web-player/bin/silverlightmediaelement.xap +0 -0
  84. data/assets/podlove-web-player/css/pwp-blue-orange.css +3526 -0
  85. data/assets/podlove-web-player/css/pwp-blue-orange.min.css +1 -0
  86. data/assets/podlove-web-player/css/pwp-creamy.css +3526 -0
  87. data/assets/podlove-web-player/css/pwp-creamy.min.css +1 -0
  88. data/assets/podlove-web-player/css/pwp-dark-blaze.css +3526 -0
  89. data/assets/podlove-web-player/css/pwp-dark-blaze.min.css +1 -0
  90. data/assets/podlove-web-player/css/pwp-dark-gray.css +3526 -0
  91. data/assets/podlove-web-player/css/pwp-dark-gray.min.css +1 -0
  92. data/assets/podlove-web-player/css/pwp-dark-green.css +3526 -0
  93. data/assets/podlove-web-player/css/pwp-dark-green.min.css +1 -0
  94. data/assets/podlove-web-player/css/pwp-dark.css +3526 -0
  95. data/assets/podlove-web-player/css/pwp-dark.min.css +1 -0
  96. data/assets/podlove-web-player/css/pwp-jungle-green.css +3526 -0
  97. data/assets/podlove-web-player/css/pwp-jungle-green.min.css +1 -0
  98. data/assets/podlove-web-player/css/pwp-light-gray.css +3526 -0
  99. data/assets/podlove-web-player/css/pwp-light-gray.min.css +1 -0
  100. data/assets/podlove-web-player/css/pwp-light.css +3526 -0
  101. data/assets/podlove-web-player/css/pwp-light.min.css +1 -0
  102. data/assets/podlove-web-player/css/pwp-midnightblue.css +3526 -0
  103. data/assets/podlove-web-player/css/pwp-midnightblue.min.css +1 -0
  104. data/assets/podlove-web-player/css/pwp-silver-blaze.css +3526 -0
  105. data/assets/podlove-web-player/css/pwp-silver-blaze.min.css +1 -0
  106. data/assets/podlove-web-player/css/pwp-silver.css +3526 -0
  107. data/assets/podlove-web-player/css/pwp-silver.min.css +1 -0
  108. data/assets/podlove-web-player/css/vendor/progress-polyfill.css +47 -0
  109. data/assets/podlove-web-player/css/vendor/style.css +72 -0
  110. data/assets/podlove-web-player/font/podlove.eot +0 -0
  111. data/assets/podlove-web-player/font/podlove.svg +103 -0
  112. data/assets/podlove-web-player/font/podlove.ttf +0 -0
  113. data/assets/podlove-web-player/font/podlove.woff +0 -0
  114. data/assets/podlove-web-player/img/arrow-down-black.png +0 -0
  115. data/assets/podlove-web-player/img/arrow-down-white.png +0 -0
  116. data/assets/podlove-web-player/img/download-overlay.png +0 -0
  117. data/assets/podlove-web-player/img/icon-podlove-subscribe-600.png +0 -0
  118. data/assets/podlove-web-player/js/podlove-web-moderator.js +1829 -0
  119. data/assets/podlove-web-player/js/podlove-web-moderator.min.js +1 -0
  120. data/assets/podlove-web-player/js/podlove-web-player.js +6058 -0
  121. data/assets/podlove-web-player/js/podlove-web-player.min.js +3 -0
  122. data/assets/podlove-web-player/js/vendor/html5shiv-printshiv.js +524 -0
  123. data/assets/podlove-web-player/js/vendor/html5shiv-printshiv.min.js +4 -0
  124. data/assets/podlove-web-player/js/vendor/html5shiv.js +326 -0
  125. data/assets/podlove-web-player/js/vendor/html5shiv.min.js +4 -0
  126. data/assets/podlove-web-player/js/vendor/jquery.js +10351 -0
  127. data/assets/podlove-web-player/js/vendor/jquery.min.js +6 -0
  128. data/assets/podlove-web-player/js/vendor/jquery.min.map +1 -0
  129. data/assets/podlove-web-player/js/vendor/progress-polyfill.js +224 -0
  130. data/assets/podlove-web-player/js/vendor/progress-polyfill.min.js +5 -0
  131. data/assets/podlove-web-player/js/vendor/tests.js +83 -0
  132. data/assets/podlove-web-player/js/vendor/user-interaction.js +44 -0
  133. data/lib/jekyll/octopod/version.rb +1 -1
  134. metadata +132 -2
@@ -0,0 +1,10 @@
1
+ ---
2
+ layout: default
3
+ ---
4
+ {% for post in site.posts %}
5
+ {% if post.url == page.url %}
6
+ {% include post.html %}
7
+ {% else %}
8
+ {% include post_line.html %}
9
+ {% endif %}
10
+ {% endfor %}
@@ -0,0 +1,108 @@
1
+ require 'erb'
2
+
3
+ module Jekyll
4
+ module FlattrFilters
5
+
6
+ # Generates the query string part for the flattr load.js from the
7
+ # configurations in _config.yml
8
+ #
9
+ # {{ site | flattr_loader_options }}
10
+ def flattr_loader_options(site)
11
+ return if site['flattr_uid'].nil?
12
+ keys = %w[mode https popout uid button language category]
13
+ options = flattr_options(site, nil, keys).delete_if { |_, v| v.to_s.empty? }
14
+
15
+ options.map { |k, v| "#{k}=#{ERB::Util.url_encode(v)}" }.join('&')
16
+ end
17
+
18
+ # Returns a flattr button
19
+ #
20
+ # {{ site | flattr_button:page }}
21
+ def flattr_button(site, page = nil)
22
+ return if site['flattr_uid'].nil?
23
+
24
+ keys = %w[url title description uid popout button category language tags]
25
+ options = flattr_options(site, page, keys)
26
+
27
+ button = '<a class="FlattrButton" style="display:none;" '
28
+ button << %Q{title="#{options.delete('title')}" href="#{options.delete('url')}" }
29
+ button << options.map { |k, v|
30
+ %Q{data-flattr-#{k}="#{v}"} unless k == 'description'
31
+ }.join(' ')
32
+ button << ">\n\n#{options['description'].gsub(/<\/?[^>]*>/, "")}\n</a>"
33
+ end
34
+
35
+ # Returns a RSS payment link.
36
+ #
37
+ # {{ site | flattr_rss:post }}
38
+ def flattr_rss(site, page = nil)
39
+ return if site['flattr_uid'].nil?
40
+ link = '<atom:link rel="payment" href="https://flattr.com/submit/auto?'
41
+ link << %Q{#{flattr_feed_options(site, page)}" type="text/html" />}
42
+ end
43
+
44
+ # Returns a ATOM payment link.
45
+ #
46
+ # {{ site | flattr_atom:post }}
47
+ def flattr_atom(site, page = nil)
48
+ return if site['flattr_uid'].nil?
49
+ link = '<link rel="payment" href="https://flattr.com/submit/auto?'
50
+ link << %Q{#{flattr_feed_options(site, page)}" type="text/html" />}
51
+ end
52
+
53
+ # Removes all leading "flattr_" from the keys of the given hash.
54
+ #
55
+ # flattrize({ 'octopod' => 'awesome', 'flattr_uid' => 'pattex' })
56
+ # => { "octopod" => "awesome", "uid" => "pattex" }
57
+ def flattrize(hsh)
58
+ config = {}
59
+ hsh.to_hash.each { |k, v|
60
+ if new_key = k.to_s.match(/\Aflattr_(.*)\z/)
61
+ config[new_key[1]] = v
62
+ else
63
+ config[k] = v
64
+ end
65
+ }
66
+
67
+ config
68
+ end
69
+
70
+ def flattr_options(site, page, keys)
71
+ page = {} if page.nil?
72
+ site = flattrize(site)
73
+ page = flattrize(page)
74
+ options = {}
75
+
76
+ keys.each { |k|
77
+ case k
78
+ when 'https'
79
+ options[k] = 1
80
+ when 'url'
81
+ options[k] = "#{site['url']}#{page['url']}"
82
+ when 'description'
83
+ options[k] = page['content'] || site['description'] || site['title']
84
+ when 'category'
85
+ options[k] = page['category'] || site['category'] || 'audio'
86
+ when 'language'
87
+ options[k] = page['language'] || site['language'] || 'en_GB'
88
+ when 'tags'
89
+ options[k] = page['tags'].join(', ') if page['tags']
90
+ else
91
+ options[k] = page[k] || site[k]
92
+ end
93
+ }
94
+
95
+ options
96
+ end
97
+
98
+ def flattr_feed_options(site, page)
99
+ keys = %w[url uid]
100
+ options = flattr_options(site, page, keys).map { |k, v|
101
+ "#{k == 'uid' ? 'user_id' : k}=#{ERB::Util.url_encode(v)}"
102
+ }.join('&amp;')
103
+ end
104
+
105
+ end
106
+ end
107
+
108
+ Liquid::Template.register_filter(Jekyll::FlattrFilters)
@@ -0,0 +1,399 @@
1
+ #require 'erb'
2
+ require 'uri'
3
+ require 'digest/sha1'
4
+
5
+ module Jekyll
6
+ module OctopodFilters
7
+ JSON_ENTITIES = { '&' => '\u0026', '>' => '\u003E', '<' => '\u003C', "'" => '\u0027' }
8
+
9
+ # Escapes some text for CDATA
10
+ def cdata_escape(input)
11
+ input.gsub(/<!\[CDATA\[/, '&lt;![CDATA[').gsub(/\]\]>/, ']]&gt;')
12
+ end
13
+
14
+ # Escapes HTML entities in JSON strings.
15
+ # More or less a copy of the equivalent method in Active Support.
16
+ # https://github.com/rails/rails/tree/master/activesupport
17
+ def j(str)
18
+ str.to_s.gsub(/[&"><']/) { |e| JSON_ENTITIES[e] }
19
+ end
20
+
21
+ # Replaces relative urls with full urls
22
+ #
23
+ # {{ "about.html" | expand_urls }} => "/about.html"
24
+ # {{ "about.html" | expand_urls:site.url }} => "http://example.com/about.html"
25
+ def expand_urls(input, url='')
26
+ url ||= '/'
27
+ input.gsub /(\s+(href|src)\s*=\s*["|']{1})(\/[^\"'>]*)/ do
28
+ $1+url+$3
29
+ end
30
+ end
31
+
32
+ # Removes scripts tag and audio tags in multiline moderator
33
+ #
34
+ # {{ page.content | remove_script_and_audio }}
35
+ def remove_script_and_audio(input)
36
+ input.gsub(/<audio.*audio>/m, '').gsub(/<script.*script>/m, '')
37
+ end
38
+
39
+ def http_only(input)
40
+ input.gsub(/https/,"http")
41
+ end
42
+
43
+ # Formats a Time to be RSS compatible like "Wed, 15 Jun 2005 19:00:00 GMT"
44
+ #
45
+ # {{ site.time | time_to_rssschema }}
46
+ def time_to_rssschema(time)
47
+ time.strftime("%a, %d %b %Y %H:%M:%S %z")
48
+ end
49
+
50
+ # Returns the first argument if it's not nil or empty otherwise it returns
51
+ # the second one.
52
+ #
53
+ # {{ post.author | otherwise:site.author }}
54
+ def otherwise(first, second)
55
+ first = first.to_s
56
+ first.empty? ? second : first
57
+ end
58
+
59
+ # Returns the value of a given hash. Is no key as second parameter given, it
60
+ # trys first "mp3", than "m4a" and than it will return a more or less random
61
+ # value.
62
+ #
63
+ # {{ post.audio | audio:"m4a" }} => "my-episode.m4a"
64
+ def audio(hsh, key = nil)
65
+ if key.nil?
66
+ hsh['mp3'] ? hsh['mp3'] : hsh['m4a'] ? hsh['m4a'] : hsh.values.first
67
+ else
68
+ hsh[key]
69
+ end
70
+ end
71
+
72
+ # Returns the MIME-Type of a given file format.
73
+ #
74
+ # {{ "m4a" | mime_type }} => "audio/mp4a-latm"
75
+ def mime_type(format)
76
+ types = {
77
+ 'mp3' => 'mpeg',
78
+ 'm4a' => 'mp4a-latm',
79
+ 'ogg' => 'ogg; codecs=vorbis',
80
+ 'opus' => 'ogg; codecs=opus'
81
+ }
82
+
83
+ "audio/#{types[format]}"
84
+ end
85
+
86
+ # Returns the size of a given file in bytes. If there is just a filename
87
+ # without a path, this method assumes that the file is an episode audio file
88
+ # which lives in /episodes.
89
+ #
90
+ # {{ "example.m4a" | file_size }} => 4242
91
+ def file_size(path, rel = nil)
92
+ return 0 if path.nil?
93
+ path = path =~ /\// ? path : File.join('episodes', path)
94
+ path = rel + path if rel
95
+ File.size(path)
96
+ end
97
+
98
+ # Returns a slug based on the id of a given page.
99
+ #
100
+ # {{ page | slug }} => '2012_10_02_octopod'
101
+ def slug(page)
102
+ page['id'][1..-1].gsub('/', '_')
103
+ end
104
+
105
+ # Splits a chapter, like it is written to the post YAML front matter into
106
+ # the components 'start' which refers to a single point in time relative to
107
+ # the beginning of the media file nad 'title' which defines the text to be
108
+ # the title of the chapter.
109
+ #
110
+ # {{ '00:00:00.000 Welcome to Octopod!' | split_chapter }}
111
+ # => { 'start' => '00:00:00.000', 'title' => 'Welcome to Octopod!' }
112
+ #
113
+ # {{ '00:00:00.000 Welcome to Octopod!' | split_chapter:'title' }}
114
+ # => 'Welcome to Octopod!'
115
+ #
116
+ # {{ '00:00:00.000 Welcome to Octopod!' | split_chapter:'start' }}
117
+ # => '00:00:00.000'
118
+ def split_chapter(chapter_str, attribute = nil)
119
+ attributes = chapter_str.split(/ /, 2)
120
+ return nil unless attributes.first.match(/\A(\d|:|\.)+\z/)
121
+
122
+ if attribute.nil?
123
+ { 'start' => attributes.first, 'title' => attributes.last }
124
+ else
125
+ attribute == 'start' ? attributes.first : attributes.last
126
+ end
127
+ end
128
+
129
+ # Returns an <audio>-tag for a given page with <source>-tags in it for every
130
+ # audio file in the page's YAML front matter.
131
+ #
132
+ # {{ page | audio_tag:site }}
133
+ def audio_tag(page, site)
134
+ out = "<audio>" + page['audio'].map { |format, filename|
135
+ %Q{<source src="#{site['url']}/episodes/#{ERB::Util.url_encode(filename)}" type="#{mime_type(format)}"></source>}
136
+ }.join("\n") + "\n</audio>\n"
137
+ end
138
+
139
+
140
+ # Returns the web player iframe for the episode of a given page.
141
+ #
142
+ # {{ page | web_player_moderator:site }}
143
+ def web_player_moderator(page, site)
144
+ return if page['audio'].nil?
145
+ out = %Q{<div class="podlove-player-wrapper">}
146
+ out = out + %Q{ <audio data-podlove-web-player-source="/players/#{page['slug']}/index.html">\n}
147
+ out = out + " <source src='episodes/#{page['audio']['mp3']}' type='audio/mp3'>\n"
148
+ out = out + " </audio>\n"
149
+ out = out + "</div>\n"
150
+ out = out + "<script>$('audio').podlovewebplayer();</script>\n"
151
+ end
152
+
153
+
154
+ # Returns the web player for the episode of a given page for the iframe mentioned in the filter above.
155
+ #
156
+ # {{ page | web_player:site }}
157
+ def web_player(page, site)
158
+ return if page['audio'].nil?
159
+
160
+ options = {
161
+ alwaysShowHours: 'true',
162
+ startVolume: '0.8',
163
+ width: 'auto',
164
+ summaryVisible: 'true',
165
+ timecontrolsVisible: 'true',
166
+ chaptersVisible: 'true',
167
+ sharebuttonsVisible: 'true'
168
+ }
169
+
170
+ simple_keys = %w[]
171
+
172
+ if sitehash = site.posts.first.site.config.dup
173
+ sitehash.delete('title')
174
+ sitehash.delete('subtitle')
175
+ options = options.merge(sitehash)
176
+ end
177
+ options = options.merge(page)
178
+ out = audio_tag(page, sitehash)
179
+ end
180
+
181
+ # Returns the script tag initializing the web player for the episode of a given page.
182
+ #
183
+ # {{ page | web_player_script_tag:site }}
184
+ def web_player_script_tag(page, site)
185
+ return if page['audio'].nil?
186
+
187
+ options = {
188
+ alwaysShowHours: 'true',
189
+ startVolume: '0.8',
190
+ width: 'auto',
191
+ summaryVisible: 'true',
192
+ timecontrolsVisible: 'true',
193
+ chaptersVisible: 'true',
194
+ sharebuttonsVisible: 'true'
195
+ }
196
+
197
+ simple_keys = %w[]
198
+
199
+ if sitehash = site.posts.first.site.config.dup
200
+ sitehash.delete('title')
201
+ sitehash.delete('subtitle')
202
+ options = options.merge(sitehash)
203
+ end
204
+ options = options.merge(page)
205
+
206
+ out = "<script>\n$('audio').podlovewebplayer("
207
+ out << { poster: sitehash['url'] + (options['episode_cover'] || '/img/logo-360x360.png'),
208
+ subtitle: options['subtitle'],
209
+ title: options['title'],
210
+ alwaysShowHours: options['alwaysShowHours'],
211
+ startVolume: options['startVolume'],
212
+ width: options['width'],
213
+ summaryVisible: options['summaryVisible'],
214
+ timecontrolsVisible: options['timecontrolsVisible'],
215
+ chaptersVisible: options['chaptersVisible'],
216
+ sharebuttonsVisible: options['sharebuttonsVisible'],
217
+ show: { title: site['title'],
218
+ subtitle: site['subtitle'],
219
+ summary: site['description'],
220
+ poster: sitehash['url'] + '/img/logo-360x360.png',
221
+ url: sitehash['url'] },
222
+ chapters: options['chapters'].map {|chapter| split_chapter(chapter)},
223
+ downloads: [
224
+ { assetTitle: options['title'],
225
+ size: file_size(page['audio']['mp3']),
226
+ downloadUrl: sitehash["url"] + "/episodes/" + page['audio']['mp3'] },
227
+ ],
228
+ summary: options['summary'],
229
+ duration: string_of_duration(options['duration']),
230
+ permalink: sitehash['url'] + page['url'],
231
+ activeTab: "chapters"
232
+ }.to_json
233
+ out << ")\n</script>"
234
+ end
235
+
236
+ # Gets a number of seconds and returns an human readable duration string of
237
+ # it.
238
+ #
239
+ # {{ 1252251 | string_of_duration }} => "00:03:13"
240
+ def string_of_duration(duration)
241
+ seconds = duration.to_i
242
+ minutes = seconds / 60
243
+ hours = minutes / 60
244
+
245
+ "#{"%02d" % hours}:#{"%02d" % (minutes % 60)}:#{"%02d" % (seconds % 60)}"
246
+ end
247
+
248
+ # Gets a number of bytes and returns an human readable string of it.
249
+ #
250
+ # {{ 1252251 | string_of_size }} => "1.19M"
251
+ def string_of_size(bytes)
252
+ bytes = bytes.to_i.to_f
253
+ out = '0'
254
+ return out if bytes == 0.0
255
+
256
+ jedec = %w[b K M G]
257
+ [3, 2, 1, 0].each { |i|
258
+ if bytes > 1024 ** i
259
+ out = "%.1f#{jedec[i]}" % (bytes / 1024 ** i)
260
+ break
261
+ end
262
+ }
263
+
264
+ return out
265
+ end
266
+
267
+ # Returns the host a given url
268
+ #
269
+ # {{ 'https://github.com/pattex/octopod' | host_from_url }} => "github.com"
270
+ def host_from_url(url)
271
+ URI.parse(url).host
272
+ end
273
+
274
+ # Generates the config for disqus integration
275
+ # If a page object is given, it generates the config variables only for this
276
+ # page. Otherwise it generate only the global config variables.
277
+ #
278
+ # {{ site | disqus_config }}
279
+ # {{ site | disqus_config:page }}
280
+ def disqus_config(site, page = nil)
281
+ if page
282
+ disqus_vars = {
283
+ 'disqus_identifier' => page['url'],
284
+ 'disqus_url' => "#{site['url']}#{page['url']}",
285
+ 'disqus_category_id' => page['disqus_category_id'] || site['disqus_category_id'],
286
+ 'disqus_title' => j(page['title'] || site['site'])
287
+ }
288
+ else
289
+ disqus_vars = {
290
+ 'disqus_developer' => site['disqus_developer'],
291
+ 'disqus_shortname' => site['disqus_shortname']
292
+ }
293
+ end
294
+
295
+ disqus_vars.delete_if { |_, v| v.nil? }
296
+ disqus_vars.map { |k, v| "var #{k} = '#{v}';" }.compact.join("\n")
297
+ end
298
+
299
+ # Returns the hex-encoded hash value of a given string. The optional
300
+ # second argument defines the length of the returned string.
301
+ #
302
+ # {{ "Octopod" | sha1 }} => "8b20a59c"
303
+ # {{ "Octopod" | sha1:23 }} => "8b20a59c8e2dcb5e1f845ba"
304
+ def sha1(str, lenght = 8)
305
+ sha1 = Digest::SHA1.hexdigest(str)
306
+ sha1[0, lenght.to_i]
307
+ end
308
+
309
+ # Returns a, ready to use, navigation list of all pages that have
310
+ # <tt>navigation</tt> set in their YAML front matter. The list is sorted by
311
+ # the value of <tt>navigation</tt>.
312
+ #
313
+ # {{ site | navigation_list:page }}
314
+ def navigation_list(site, page)
315
+ pages = site['pages'].select { |p|
316
+ p.data['navigation'] && p.data['title']
317
+ }.sort_by { |p| p.data['navigation'] }
318
+
319
+ list = ['<ul class="nav navbar-nav">']
320
+ list << pages.map { |p|
321
+ active = (p.url == page['url']) || (page.has_key?('next') && File.join(p.dir, p.basename) == '/index')
322
+ navigation_list_item(File.join(site['url'], p.url), p.data['title'], active)
323
+ }
324
+ list << ['</ul>']
325
+
326
+ list.join("\n")
327
+ end
328
+
329
+ def navigation_list_item(url, title, active = false)
330
+ a_class = active ? ' class="active"' : ''
331
+ %Q{<li#{a_class}><a #{a_class} href="#{url}">#{title}</a></li>}
332
+ end
333
+
334
+ # Returns an array of all episode feeds named by the convetion
335
+ # 'episodes.<episode_file_format>.rss' within the root directory. Also it
336
+ # contains all additional feeds specified by 'additional_feeds' in the
337
+ # '_config.yml'. If an 'episode_file_format' or key of 'additional_feeds'
338
+ # equals the optional parameter 'except', it will be skipped.
339
+ #
340
+ # episode_feeds(site, except = nil) =>
341
+ # [
342
+ # ["m4a Episode RSS-Feed", "/episodes.m4a.rss"],
343
+ # ["mp3 Episode RSS-Feed", "/episodes.mp3.rss"],
344
+ # ["Torrent Feed m4a", "http://bitlove.org/octopod/octopod_m4a/feed"],
345
+ # ["Torrent Feed mp3", "http://bitlove.org/octopod/octopod_mp3/feed"]
346
+ # ]
347
+ def episode_feeds(site, except = nil)
348
+ feeds = []
349
+
350
+ if site['episode_feed_formats']
351
+ site['episode_feed_formats'].map { |f|
352
+ feeds << ["#{f} Episode RSS-Feed", "#{site['url']}/episodes.#{f}.rss"] unless f == except
353
+ }
354
+ end
355
+ if site['additional_feeds']
356
+ site['additional_feeds'].each { |k, v|
357
+ feeds << [k.gsub('_', ' '), v] unless k == except
358
+ }
359
+ end
360
+
361
+ feeds
362
+ end
363
+
364
+ # Returns HTML links to all episode feeds named by the convetion
365
+ # 'episodes.<episode_file_format>.rss' within the root directory. Also it
366
+ # returns all additional feeds specified by 'additional_feeds' in the
367
+ # '_config.yml'. If an 'episode_file_format' or key of 'additional_feeds'
368
+ # equals the optional parameter 'except', it will be skipped.
369
+ #
370
+ # {{ site | episode_feeds_html:'m4a' }} =>
371
+ # <link rel="alternate" type="application/rss+xml" title="mp3 Episode RSS-Feed" href="/episodes.mp3.rss" />
372
+ # <link rel="alternate" type="application/rss+xml" title="Torrent Feed m4a" href="http://bitlove.org/octopod/octopod_m4a/feed" />
373
+ # <link rel="alternate" type="application/rss+xml" title="Torrent Feed mp3" href="http://bitlove.org/octopod/octopod_mp3/feed" />
374
+ def episode_feeds_html(site, except = nil)
375
+ episode_feeds(site, except).map { |f|
376
+ %Q{<link rel="alternate" type="application/rss+xml" title="#{f.first || f.last}" href="#{f.last}" />}
377
+ }.join("\n")
378
+ end
379
+
380
+ # Returns RSS-XML links to all episode feeds named by the convetion
381
+ # 'episodes.<episode_file_format>.rss' within the root directory. Also it
382
+ # returns all additional feeds specified by 'additional_feeds' in the
383
+ # '_config.yml'. If an 'episode_file_format' or key of 'additional_feeds'
384
+ # equals the optional parameter 'except', it will be skipped.
385
+ #
386
+ # {{ site | episode_feeds_rss:'m4a' }} =>
387
+ # <atom:link rel="alternate" href="/episodes.mp3.rss" type="application/rss+xml" title="mp3 Episode RSS-Feed"/>
388
+ # <atom:link rel="alternate" href="http://bitlove.org/octopod/octopod_m4a/feed" type="application/rss+xml" title="Torrent Feed m4a"/>
389
+ # <atom:link rel="alternate" href="http://bitlove.org/octopod/octopod_mp3/feed" type="application/rss+xml" title="Torrent Feed mp3"/>
390
+ def episode_feeds_rss(site, except = nil)
391
+ episode_feeds(site, except).map { |f|
392
+ %Q{<atom:link rel="alternate" href="#{f.last}" type="application/rss+xml" title="#{f.first || f.last}"/>}
393
+ }.join("\n")
394
+ end
395
+
396
+ end
397
+ end
398
+
399
+ Liquid::Template.register_filter(Jekyll::OctopodFilters)