jekyll-spaceship 0.8.7 → 0.9.4
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.
- checksums.yaml +4 -4
- data/.rspec +3 -0
- data/.travis.yml +6 -6
- data/README.md +64 -26
- data/jekyll-spaceship.gemspec +1 -1
- data/lib/jekyll-spaceship/cores/config.rb +1 -1
- data/lib/jekyll-spaceship/cores/processor.rb +46 -6
- data/lib/jekyll-spaceship/processors/mathjax-processor.rb +15 -4
- data/lib/jekyll-spaceship/processors/media-processor.rb +225 -0
- data/lib/jekyll-spaceship/processors/mermaid-processor.rb +6 -20
- data/lib/jekyll-spaceship/processors/plantuml-processor.rb +8 -22
- data/lib/jekyll-spaceship/processors/table-processor.rb +2 -0
- data/lib/jekyll-spaceship/version.rb +1 -1
- metadata +6 -4
- data/lib/jekyll-spaceship/processors/video-processor.rb +0 -139
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: cbba69bcf95b309c36da702cce9670212f7a32985e2522f13b06fdc0058e0b60
|
4
|
+
data.tar.gz: 679ee30cad6abfe6c6fb271e3816bff2e16f931b94492d0e0223f4dba11263f4
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 207137e6fedec9521c2cde3a3215cf7f1ccbc81f6ce0385c385a1c48d31a1ba7d03b3c532e0cbde80ce2f8d33e79a770d299d64cc436acddfc03ed651a2e9fc4
|
7
|
+
data.tar.gz: 1c78e2e73e99f7c3e007e3cbcea683b1d4a4db5771737530865be9262d42adf5617e6d7b9cb9931b4947937fdc64d927bf38967121b2dc98d1107d5bec01cace
|
data/.rspec
ADDED
data/.travis.yml
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
language: ruby
|
2
2
|
cache: bundler
|
3
3
|
rvm:
|
4
|
-
- 2.7
|
5
|
-
- 2.3
|
4
|
+
- 2.7
|
5
|
+
- 2.3
|
6
6
|
env:
|
7
7
|
global:
|
8
|
-
|
8
|
+
- NOKOGIRI_USE_SYSTEM_LIBRARIES=true
|
9
9
|
matrix:
|
10
|
-
|
10
|
+
- JEKYLL_VERSION="~> 3.8"
|
11
11
|
matrix:
|
12
12
|
include:
|
13
13
|
- rvm: 2.7
|
@@ -15,8 +15,8 @@ matrix:
|
|
15
15
|
- rvm: 2.7
|
16
16
|
env: JEKYLL_VERSION=">= 4.0.0"
|
17
17
|
before_install:
|
18
|
-
- gem update --system
|
19
|
-
- gem install bundler
|
18
|
+
- gem update --system
|
19
|
+
- gem install bundler
|
20
20
|
before_script: bundle update
|
21
21
|
script: script/cibuild
|
22
22
|
notifications:
|
data/README.md
CHANGED
@@ -107,11 +107,14 @@ Spaceship is a minimalistic, powerful and extremely customizable [Jekyll](https:
|
|
107
107
|
- [2.2 How to use?](#22-how-to-use)
|
108
108
|
- [3. PlantUML Usage](#3-plantuml-usage)
|
109
109
|
- [4. Mermaid Usage](#4-mermaid-usage)
|
110
|
-
- [5.
|
110
|
+
- [5. Media Usage](#5-media-usage)
|
111
111
|
- [5.1 Youtube Usage](#youtube-usage)
|
112
112
|
- [5.2 Vimeo Usage](#vimeo-usage)
|
113
113
|
- [5.3 DailyMotion Usage](#dailymotion-usage)
|
114
|
-
- [5.4
|
114
|
+
- [5.4 Spotify Usage](#spotify-usage)
|
115
|
+
- [5.5 SoundCloud Usage](#soundcloud-usage)
|
116
|
+
- [5.6 General Video Usage](#general-video-usage)
|
117
|
+
- [5.7 General Audio Usage](#general-audio-usage)
|
115
118
|
- [6. Hybrid HTML with Markdown](#6-hybrid-html-with-markdown)
|
116
119
|
- [7. Markdown Polyfill](#7-markdown-polyfill)
|
117
120
|
- [7.1 Escape Ordered List](#71-escape-ordered-list)
|
@@ -143,7 +146,7 @@ plugins:
|
|
143
146
|
|
144
147
|
**💡 Tip:** Note that GitHub Pages runs in `safe` mode and only allows [a set of whitelisted plugins](https://pages.github.com/versions/). To use the gem in GitHub Pages, you need to build locally or use CI (e.g. [travis](https://travis-ci.org/), [github workflow](https://help.github.com/en/actions/configuring-and-managing-workflows/configuring-a-workflow)) and deploy to your `gh-pages` branch.
|
145
148
|
|
146
|
-
### Additions
|
149
|
+
### Additions for Unlimited GitHub Pages
|
147
150
|
|
148
151
|
* Here is a GitHub Action named [jekyll-deploy-action](https://github.com/jeffreytse/jekyll-deploy-action) for Jekyll site deployment conveniently. 👍
|
149
152
|
* Here is a [Jekyll site](https://github.com/jeffreytse/jekyll-jeffreytse-blog) using Travis to build and deploy to GitHub Pages for your references.
|
@@ -162,16 +165,20 @@ jekyll-spaceship:
|
|
162
165
|
- plantuml-processor
|
163
166
|
- mermaid-processor
|
164
167
|
- polyfill-processor
|
165
|
-
-
|
168
|
+
- media-processor
|
166
169
|
- emoji-processor
|
167
170
|
- element-processor
|
168
171
|
mathjax-processor:
|
169
|
-
src:
|
172
|
+
src:
|
173
|
+
- https://polyfill.io/v3/polyfill.min.js?features=es6
|
174
|
+
- https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-mml-chtml.js
|
170
175
|
config:
|
171
|
-
|
176
|
+
tex:
|
172
177
|
inlineMath:
|
173
178
|
- ['$','$']
|
174
179
|
- ['\(','\)']
|
180
|
+
svg:
|
181
|
+
fontCache: 'global'
|
175
182
|
plantuml-processor:
|
176
183
|
mode: default # mode value 'pre-fetch' for fetching image at building stage
|
177
184
|
css:
|
@@ -179,7 +186,7 @@ jekyll-spaceship:
|
|
179
186
|
syntax:
|
180
187
|
code: 'plantuml!'
|
181
188
|
custom: ['@startuml', '@enduml']
|
182
|
-
src: http://www.plantuml.com/plantuml/
|
189
|
+
src: http://www.plantuml.com/plantuml/svg/
|
183
190
|
mermaid-processor:
|
184
191
|
mode: default # mode value 'pre-fetch' for fetching image at building stage
|
185
192
|
css:
|
@@ -190,14 +197,14 @@ jekyll-spaceship:
|
|
190
197
|
config:
|
191
198
|
theme: default
|
192
199
|
src: https://mermaid.ink/svg/
|
193
|
-
|
200
|
+
media-processor:
|
194
201
|
default:
|
195
|
-
id: '
|
196
|
-
class: '
|
202
|
+
id: 'media-{id}'
|
203
|
+
class: 'media'
|
197
204
|
width: '100%'
|
198
205
|
height: 350
|
199
|
-
|
200
|
-
style: 'max-width: 600px'
|
206
|
+
frameborder: 0
|
207
|
+
style: 'max-width: 600px; outline: none;'
|
201
208
|
allow: 'encrypted-media; picture-in-picture'
|
202
209
|
emoji-processor:
|
203
210
|
css:
|
@@ -630,7 +637,7 @@ Code above would be parsed as:
|
|
630
637
|
|
631
638
|
### 3. PlantUML Usage
|
632
639
|
|
633
|
-
[PlantUML](
|
640
|
+
[PlantUML](https://plantuml.com) is a component that allows to quickly write:
|
634
641
|
|
635
642
|
- sequence diagram,
|
636
643
|
- use case diagram,
|
@@ -701,25 +708,31 @@ Code above would be parsed as:
|
|
701
708
|
|
702
709
|

|
703
710
|
|
704
|
-
### 5.
|
711
|
+
### 5. Media Usage
|
705
712
|
|
706
|
-
How often did you find yourself googling "**How to embed a video in markdown?**"
|
713
|
+
How often did you find yourself googling "**How to embed a video/audio in markdown?**"
|
707
714
|
|
708
|
-
While its not possible to embed a video in markdown, the best and easiest
|
715
|
+
While its not possible to embed a video/audio in markdown, the best and easiest
|
716
|
+
way is to extract a frame from the video/audio. To add videos/audios to your
|
717
|
+
markdown files easier I developped this tool for you, and it will parse the
|
718
|
+
video/audio link inside the image block automatically.
|
709
719
|
|
710
|
-
**For now, these
|
720
|
+
**For now, these media links parsing are provided:**
|
711
721
|
|
712
722
|
- Youtube
|
713
723
|
- Vimeo
|
714
724
|
- DailyMotion
|
715
|
-
-
|
725
|
+
- Spotify
|
726
|
+
- SoundCloud
|
727
|
+
- General Video ( mp4 | avi | ogg | ogv | webm | 3gp | flv | mov ... )
|
728
|
+
- General Audio ( mp3 | wav | ogg | mid | midi | aac | wma ... )
|
716
729
|
|
717
|
-
There are two ways to embed a video in your Jekyll blog page:
|
730
|
+
There are two ways to embed a video/audio in your Jekyll blog page:
|
718
731
|
|
719
732
|
Inline-style:
|
720
733
|
|
721
734
|
```markdown
|
722
|
-

|
723
736
|
```
|
724
737
|
|
725
738
|
Reference-style:
|
@@ -727,10 +740,10 @@ Reference-style:
|
|
727
740
|
```markdown
|
728
741
|
![][{reference}]
|
729
742
|
|
730
|
-
[{reference}]: {
|
743
|
+
[{reference}]: {media-link}
|
731
744
|
```
|
732
745
|
|
733
|
-
For configuring
|
746
|
+
For configuring media attributes (e.g, width, height), just adding query string to
|
734
747
|
the link as below:
|
735
748
|
|
736
749
|
```markdown
|
@@ -763,6 +776,22 @@ the link as below:
|
|
763
776
|

|
764
777
|
```
|
765
778
|
|
779
|
+
#### Spotify Usage
|
780
|
+
|
781
|
+
```markdown
|
782
|
+

|
783
|
+
```
|
784
|
+
|
785
|
+
<image width="600" src="https://user-images.githubusercontent.com/9413601/89762618-5d11b000-db23-11ea-81db-35cc3682b234.png">
|
786
|
+
|
787
|
+
#### SoundCloud Usage
|
788
|
+
|
789
|
+
```markdown
|
790
|
+

|
791
|
+
```
|
792
|
+
|
793
|
+
<image width="600" src="https://user-images.githubusercontent.com/9413601/89762969-1c666680-db24-11ea-97e3-4340f7fac7ac.png">
|
794
|
+
|
766
795
|
#### General Video Usage
|
767
796
|
|
768
797
|
```markdown
|
@@ -773,6 +802,15 @@ the link as below:
|
|
773
802
|

|
774
803
|
```
|
775
804
|
|
805
|
+
#### General Audio Usage
|
806
|
+
|
807
|
+
```markdown
|
808
|
+

|
809
|
+
|
810
|
+

|
811
|
+
```
|
812
|
+
|
813
|
+
<image width="300" src="https://user-images.githubusercontent.com/9413601/89762143-68181080-db22-11ea-8467-e8b2a8a96ae5.png">
|
776
814
|
|
777
815
|
### 6. Hybrid HTML with Markdown
|
778
816
|
|
@@ -928,7 +966,7 @@ Automatically adds a `target="_blank" rel="noopener noreferrer"` attribute to al
|
|
928
966
|
jekyll-spaceship:
|
929
967
|
element-processor:
|
930
968
|
css:
|
931
|
-
- a: #
|
969
|
+
- a: # Replace all `a` tags
|
932
970
|
props:
|
933
971
|
class: ['(^.*$)', '\0 ext-link'] # Add `ext-link` to class by regex pattern
|
934
972
|
target: _blank # Replace `target` value to `_blank`
|
@@ -944,9 +982,9 @@ Automatically adds `loading="lazy"` to `img` and `iframe` tags to natively load
|
|
944
982
|
jekyll-spaceship:
|
945
983
|
element-processor:
|
946
984
|
css:
|
947
|
-
- a: #
|
985
|
+
- a: # Replace all `a` tags
|
948
986
|
props: #
|
949
|
-
loading: lazy # Replace `
|
987
|
+
loading: lazy # Replace `loading` value to `lazy`
|
950
988
|
```
|
951
989
|
|
952
990
|
In case you want to prevent loading some images/iframes lazily, add
|
@@ -959,7 +997,7 @@ See the following examples to prevent lazy loading.
|
|
959
997
|
jekyll-spaceship:
|
960
998
|
element-processor:
|
961
999
|
css:
|
962
|
-
- a: #
|
1000
|
+
- a: # Replace all `a` tags
|
963
1001
|
props: #
|
964
1002
|
loading: eager # Replace `loading` value to `eager`
|
965
1003
|
```
|
data/jekyll-spaceship.gemspec
CHANGED
@@ -9,7 +9,7 @@ Gem::Specification.new do |spec|
|
|
9
9
|
spec.version = Jekyll::Spaceship::VERSION
|
10
10
|
spec.authors = ["jeffreytse"]
|
11
11
|
spec.email = ["jeffreytse.mail@gmail.com"]
|
12
|
-
spec.summary = "A Jekyll plugin to provide powerful supports for table, mathjax, plantuml, mermaid, emoji, video, youtube, vimeo, dailymotion, etc."
|
12
|
+
spec.summary = "A Jekyll plugin to provide powerful supports for table, mathjax, plantuml, mermaid, emoji, video, audio, youtube, vimeo, dailymotion, spotify, soundcloud, etc."
|
13
13
|
spec.homepage = "https://github.com/jeffreytse/jekyll-spaceship"
|
14
14
|
spec.license = "MIT"
|
15
15
|
|
@@ -116,7 +116,7 @@ module Jekyll::Spaceship
|
|
116
116
|
if self.respond_to? method
|
117
117
|
@page.content = self.pre_exclude @page.content
|
118
118
|
@page.content = self.send method, @page.content
|
119
|
-
@page.content = self.
|
119
|
+
@page.content = self.post_exclude @page.content
|
120
120
|
end
|
121
121
|
else
|
122
122
|
if Type.html? output_ext
|
@@ -151,8 +151,8 @@ module Jekyll::Spaceship
|
|
151
151
|
logger.log file
|
152
152
|
end
|
153
153
|
|
154
|
-
def
|
155
|
-
|
154
|
+
def exclusion_regexs()
|
155
|
+
regexs = []
|
156
156
|
@exclusions.each do |type|
|
157
157
|
regex = nil
|
158
158
|
if type == :code
|
@@ -160,9 +160,16 @@ module Jekyll::Spaceship
|
|
160
160
|
elsif type == :math
|
161
161
|
regex = /(((?<!\\)\${1,2})[^\n]*?\1)/
|
162
162
|
elsif type == :liquid_filter
|
163
|
-
regex = /((?<!\\)\{\{[^\n]*?\}\})/
|
163
|
+
regex = /((?<!\\)((\{\{[^\n]*?\}\})|(\{%[^\n]*?%\})))/
|
164
164
|
end
|
165
|
-
|
165
|
+
regexs.push regex unless regex.nil?
|
166
|
+
end
|
167
|
+
regexs
|
168
|
+
end
|
169
|
+
|
170
|
+
def pre_exclude(content, regexs = self.exclusion_regexs())
|
171
|
+
@exclusion_store = []
|
172
|
+
regexs.each do |regex|
|
166
173
|
content.scan(regex) do |match_data|
|
167
174
|
match = match_data[0]
|
168
175
|
id = @exclusion_store.size
|
@@ -173,7 +180,7 @@ module Jekyll::Spaceship
|
|
173
180
|
content
|
174
181
|
end
|
175
182
|
|
176
|
-
def
|
183
|
+
def post_exclude(content)
|
177
184
|
while @exclusion_store.size > 0
|
178
185
|
match = @exclusion_store.pop
|
179
186
|
id = @exclusion_store.size
|
@@ -192,5 +199,38 @@ module Jekyll::Spaceship
|
|
192
199
|
end
|
193
200
|
content
|
194
201
|
end
|
202
|
+
|
203
|
+
def self.fetch_img_data(url)
|
204
|
+
begin
|
205
|
+
res = Net::HTTP.get_response URI(url)
|
206
|
+
raise res.body unless res.is_a?(Net::HTTPSuccess)
|
207
|
+
content_type = res.header['Content-Type']
|
208
|
+
raise 'Unknown content type!' if content_type.nil?
|
209
|
+
content_body = res.body.force_encoding('UTF-8')
|
210
|
+
return {
|
211
|
+
'type' => content_type,
|
212
|
+
'body' => content_body
|
213
|
+
}
|
214
|
+
rescue StandardError => msg
|
215
|
+
logger.log msg
|
216
|
+
end
|
217
|
+
end
|
218
|
+
|
219
|
+
def self.make_img_tag(data)
|
220
|
+
css_class = data['class']
|
221
|
+
type = data['type']
|
222
|
+
body = data['body']
|
223
|
+
if type == 'url'
|
224
|
+
"<img class=\"#{css_class}\" src=\"#{body}\">"
|
225
|
+
elsif type.include?('svg')
|
226
|
+
body.gsub(/\<\?xml.*?\?>/, '')
|
227
|
+
.gsub(/<!--[^\0]*?-->/, '')
|
228
|
+
.sub(/<svg /, "<svg class=\"#{css_class}\" ")
|
229
|
+
else
|
230
|
+
body = Base64.encode64(body)
|
231
|
+
body = "data:#{type};base64, #{body}"
|
232
|
+
"<img class=\"#{css_class}\" src=\"#{body}\">"
|
233
|
+
end
|
234
|
+
end
|
195
235
|
end
|
196
236
|
end
|
@@ -6,9 +6,13 @@ module Jekyll::Spaceship
|
|
6
6
|
class MathjaxProcessor < Processor
|
7
7
|
def self.config
|
8
8
|
{
|
9
|
-
'src' =>
|
9
|
+
'src' => [
|
10
|
+
'https://polyfill.io/v3/polyfill.min.js?features=es6',
|
11
|
+
'https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-mml-chtml.js',
|
12
|
+
],
|
10
13
|
'config' => {
|
11
|
-
'
|
14
|
+
'tex' => { 'inlineMath' => [['$','$'], ['\\(','\\)']] },
|
15
|
+
'svg': { 'fontCache': 'global' }
|
12
16
|
}
|
13
17
|
}
|
14
18
|
end
|
@@ -27,8 +31,15 @@ module Jekyll::Spaceship
|
|
27
31
|
|
28
32
|
self.handled = true
|
29
33
|
|
30
|
-
|
31
|
-
|
34
|
+
# add mathjax config
|
35
|
+
cfg = config['config'].to_json
|
36
|
+
head.add_child("<script>MathJax=#{cfg}</script>")
|
37
|
+
|
38
|
+
# add mathjax dependencies
|
39
|
+
config['src'] = [config['src']] if config['src'].is_a? String
|
40
|
+
config['src'].each do |src|
|
41
|
+
head.add_child("<script src=\"#{src}\"></script>")
|
42
|
+
end
|
32
43
|
|
33
44
|
doc.to_html
|
34
45
|
end
|
@@ -0,0 +1,225 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'uri'
|
4
|
+
require "nokogiri"
|
5
|
+
|
6
|
+
module Jekyll::Spaceship
|
7
|
+
class MediaProcessor < Processor
|
8
|
+
def self.config
|
9
|
+
{
|
10
|
+
'default' => {
|
11
|
+
'id' => 'media-{id}',
|
12
|
+
'class' => 'media',
|
13
|
+
'width' => '100%',
|
14
|
+
'height' => 350,
|
15
|
+
'frameborder' => 0,
|
16
|
+
'style' => 'max-width: 600px;outline: none',
|
17
|
+
'allow' => 'encrypted-media; picture-in-picture'
|
18
|
+
}
|
19
|
+
}
|
20
|
+
end
|
21
|
+
|
22
|
+
def on_handle_html(content)
|
23
|
+
# use nokogiri to parse html content
|
24
|
+
doc = Nokogiri::HTML(content)
|
25
|
+
# handle each img tag
|
26
|
+
doc.css('img').each do |element|
|
27
|
+
handle_normal_audio(element)
|
28
|
+
handle_normal_video(element)
|
29
|
+
handle_youtube(element)
|
30
|
+
handle_vimeo(element)
|
31
|
+
handle_dailymotion(element)
|
32
|
+
handle_spotify(element)
|
33
|
+
handle_soundcloud(element)
|
34
|
+
end
|
35
|
+
doc.to_html
|
36
|
+
end
|
37
|
+
|
38
|
+
# Examples:
|
39
|
+
# 
|
40
|
+
# 
|
41
|
+
def handle_normal_audio(element)
|
42
|
+
handle_media(element, {
|
43
|
+
media_type: 'audio',
|
44
|
+
host: '(https?:\\/\\/)?.*\\/',
|
45
|
+
id: '(.+?\\.(mp3|wav|ogg|mid|midi|aac|wma))',
|
46
|
+
})
|
47
|
+
end
|
48
|
+
|
49
|
+
|
50
|
+
# Examples:
|
51
|
+
# 
|
52
|
+
# 
|
53
|
+
# 
|
54
|
+
def handle_normal_video(element)
|
55
|
+
handle_media(element, {
|
56
|
+
media_type: 'iframe',
|
57
|
+
host: '(https?:\\/\\/)?.*\\/',
|
58
|
+
id: '(.+?\\.(avi|mp4|webm|ogg|ogv|flv|mkv|mov|wmv|3gp|rmvb|asf))'
|
59
|
+
})
|
60
|
+
end
|
61
|
+
|
62
|
+
# Examples:
|
63
|
+
# 
|
64
|
+
# 
|
65
|
+
# 
|
66
|
+
def handle_youtube(element)
|
67
|
+
handle_media(element, {
|
68
|
+
media_type: 'iframe',
|
69
|
+
host: '(https?:)?\\/\\/.*youtu.*',
|
70
|
+
id: '(?<=\\?v\\=|embed\\/|\\.be\\/)([a-zA-Z0-9\\_\\-]+)',
|
71
|
+
base_url: "https://www.youtube.com/embed/"
|
72
|
+
})
|
73
|
+
end
|
74
|
+
|
75
|
+
# Examples:
|
76
|
+
# 
|
77
|
+
# 
|
78
|
+
def handle_vimeo(element)
|
79
|
+
handle_media(element, {
|
80
|
+
media_type: 'iframe',
|
81
|
+
host: '(https?:)?\\/\\/vimeo\\.com\\/',
|
82
|
+
id: '([0-9]+)',
|
83
|
+
base_url: "https://player.vimeo.com/video/"
|
84
|
+
})
|
85
|
+
end
|
86
|
+
|
87
|
+
# Examples:
|
88
|
+
# 
|
89
|
+
# 
|
90
|
+
def handle_dailymotion(element)
|
91
|
+
handle_media(element, {
|
92
|
+
media_type: 'iframe',
|
93
|
+
host: '(https?:)?\\/\\/.*dai.?ly.*',
|
94
|
+
id: '(?<=video\\/|\\/)([a-zA-Z0-9\\_\\-]+)',
|
95
|
+
base_url: "https://www.dailymotion.com/embed/video/"
|
96
|
+
})
|
97
|
+
end
|
98
|
+
|
99
|
+
# Examples:
|
100
|
+
# 
|
101
|
+
# 
|
102
|
+
def handle_spotify(element)
|
103
|
+
handle_media(element, {
|
104
|
+
media_type: 'iframe',
|
105
|
+
host: '(https?:)?\\/\\/open\\.spotify\\.com\\/track\\/',
|
106
|
+
id: '(?<=track\\/)([a-zA-Z0-9\\_\\-]+)',
|
107
|
+
base_url: "https://open.spotify.com/embed/track/",
|
108
|
+
height: 80
|
109
|
+
})
|
110
|
+
end
|
111
|
+
|
112
|
+
# Examples:
|
113
|
+
# 
|
114
|
+
def handle_soundcloud(element)
|
115
|
+
handle_media(element, {
|
116
|
+
media_type: 'iframe',
|
117
|
+
id_from: 'html',
|
118
|
+
host: '(https?:)?\\/\\/soundcloud\\.com\\/.+\\/[^\\?]+',
|
119
|
+
id: '(?<=soundcloud:\\/\\/sounds:)([0-9]+)',
|
120
|
+
base_url: "https://w.soundcloud.com/player/?url="\
|
121
|
+
"https%3A//api.soundcloud.com/tracks/",
|
122
|
+
height: 125,
|
123
|
+
})
|
124
|
+
end
|
125
|
+
|
126
|
+
def handle_media(element, data)
|
127
|
+
host = data[:host]
|
128
|
+
src = element.get_attribute('src')
|
129
|
+
title = element.get_attribute('title')
|
130
|
+
id = data[:id_from] === 'html' ? '()' : data[:id]
|
131
|
+
match_data = src.match(/#{host}#{id}\S*/)
|
132
|
+
return if match_data.nil?
|
133
|
+
|
134
|
+
media_type = data[:media_type]
|
135
|
+
base_url = data[:base_url]
|
136
|
+
id = data[:id_from] === 'html' \
|
137
|
+
? get_id_from_html(src, data[:id]) \
|
138
|
+
: match_data[2]
|
139
|
+
qs = src.match(/(?<=\?)(\S*?)$/)
|
140
|
+
qs = Hash[URI.decode_www_form(qs.to_s)].reject do |k, v|
|
141
|
+
next true if v == id or v == ''
|
142
|
+
end
|
143
|
+
|
144
|
+
cfg = self.config['default'].clone
|
145
|
+
cfg['id'] = qs['id'] || cfg['id']
|
146
|
+
cfg['class'] = qs['class'] || cfg['class']
|
147
|
+
cfg['style'] = qs['style'] || cfg['style']
|
148
|
+
cfg['id'] = cfg['id'].gsub('{id}', id)
|
149
|
+
cfg['class'] = cfg['class'].gsub('{id}', id)
|
150
|
+
|
151
|
+
cfg['src'] = URI(base_url ? "#{base_url}#{id}" : src).tap do |v|
|
152
|
+
v.query = URI.encode_www_form(qs) if qs.size > 0
|
153
|
+
end
|
154
|
+
|
155
|
+
case media_type
|
156
|
+
when 'audio'
|
157
|
+
cfg['autoplay'] = qs['autoplay'] || data[:autoplay] || cfg['autoplay']
|
158
|
+
cfg['loop'] = qs['loop'] || data[:loop] || cfg['loop']
|
159
|
+
cfg['style'] += ';display: none;' if qs['hidden']
|
160
|
+
handle_audio(element, { cfg: cfg })
|
161
|
+
when 'iframe'
|
162
|
+
cfg['title'] = title
|
163
|
+
cfg['width'] = qs['width'] || data[:width] || cfg['width']
|
164
|
+
cfg['height'] = qs['height'] || data[:height] || cfg['height']
|
165
|
+
cfg['frameborder'] = qs['frameborder'] || cfg['frameborder']
|
166
|
+
cfg['allow'] ||= cfg['allow']
|
167
|
+
handle_iframe(element, { cfg: cfg })
|
168
|
+
end
|
169
|
+
self.handled = true
|
170
|
+
end
|
171
|
+
|
172
|
+
def handle_audio(element, data)
|
173
|
+
cfg = data[:cfg]
|
174
|
+
html = "<audio"\
|
175
|
+
" id=\"#{cfg['id']}\""\
|
176
|
+
" class=\"#{cfg['class']}\""\
|
177
|
+
" #{cfg['autoplay'] ? 'autoplay' : ''}"\
|
178
|
+
" #{cfg['loop'] ? 'loop' : ''}"\
|
179
|
+
" src=\"#{cfg['src']}\""\
|
180
|
+
" style=\"#{cfg['style']}\""\
|
181
|
+
" controls>" \
|
182
|
+
"<p> Your browser doesn't support HTML5 audio."\
|
183
|
+
" Here is a <a href=\"#{cfg['src']}\">link to download the audio</a>"\
|
184
|
+
"instead. </p>"\
|
185
|
+
"</audio>"
|
186
|
+
doc = Nokogiri::XML(html)
|
187
|
+
element.replace(doc.children.first)
|
188
|
+
end
|
189
|
+
|
190
|
+
def handle_iframe(element, data)
|
191
|
+
cfg = data[:cfg]
|
192
|
+
html = "<iframe"\
|
193
|
+
" id=\"#{cfg['id']}\""\
|
194
|
+
" class=\"#{cfg['class']}\""\
|
195
|
+
" src=\"#{cfg['src']}\""\
|
196
|
+
" title=\"#{cfg['title']}\""\
|
197
|
+
" width=\"#{cfg['width']}\""\
|
198
|
+
" height=\"#{cfg['height']}\""\
|
199
|
+
" style=\"#{cfg['style']}\""\
|
200
|
+
" allow=\"#{cfg['allow']}\""\
|
201
|
+
" frameborder=\"#{cfg['frameborder']}\""\
|
202
|
+
" allowfullscreen>"\
|
203
|
+
"</iframe>"
|
204
|
+
doc = Nokogiri::XML(html)
|
205
|
+
element.replace(doc.children.first)
|
206
|
+
end
|
207
|
+
|
208
|
+
def get_id_from_html(url, pattern)
|
209
|
+
id = ''
|
210
|
+
begin
|
211
|
+
url = 'https:' + url if url.start_with? '//'
|
212
|
+
res = Net::HTTP.get_response URI(url)
|
213
|
+
raise res.body unless res.is_a?(Net::HTTPSuccess)
|
214
|
+
res.body.match pattern do |match_data|
|
215
|
+
id = match_data[0]
|
216
|
+
break
|
217
|
+
end
|
218
|
+
rescue StandardError => msg
|
219
|
+
data = url
|
220
|
+
logger.log msg
|
221
|
+
end
|
222
|
+
id
|
223
|
+
end
|
224
|
+
end
|
225
|
+
end
|
@@ -62,18 +62,20 @@ module Jekyll::Spaceship
|
|
62
62
|
def handle_mermaid(code)
|
63
63
|
# encode to UTF-8
|
64
64
|
code = code.encode('UTF-8')
|
65
|
-
|
66
65
|
url = get_url(code)
|
67
66
|
|
68
67
|
# render mode
|
69
68
|
case self.config['mode']
|
70
69
|
when 'pre-fetch'
|
71
|
-
|
70
|
+
data = self.class.fetch_img_data(url)
|
71
|
+
end
|
72
|
+
if data.nil?
|
73
|
+
data = { 'type' => 'url', 'body' => url }
|
72
74
|
end
|
73
75
|
|
74
76
|
# return img tag
|
75
|
-
|
76
|
-
|
77
|
+
data['class'] = self.config['css']['class']
|
78
|
+
self.class.make_img_tag(data)
|
77
79
|
end
|
78
80
|
|
79
81
|
def get_url(code)
|
@@ -96,21 +98,5 @@ module Jekyll::Spaceship
|
|
96
98
|
raise "No supported src ! #{src}"
|
97
99
|
end
|
98
100
|
end
|
99
|
-
|
100
|
-
def get_mermaid_img_data(url)
|
101
|
-
data = ''
|
102
|
-
begin
|
103
|
-
res = Net::HTTP.get_response URI(url)
|
104
|
-
raise res.body unless res.is_a?(Net::HTTPSuccess)
|
105
|
-
data = Base64.encode64(res.body)
|
106
|
-
content_type = res.header['Content-Type']
|
107
|
-
raise 'Unknown content type!' if content_type.nil?
|
108
|
-
data = "data:#{content_type};base64, #{data}"
|
109
|
-
rescue StandardError => msg
|
110
|
-
data = url
|
111
|
-
logger.log msg
|
112
|
-
end
|
113
|
-
data
|
114
|
-
end
|
115
101
|
end
|
116
102
|
end
|
@@ -17,7 +17,7 @@ module Jekyll::Spaceship
|
|
17
17
|
'css' => {
|
18
18
|
'class' => 'plantuml'
|
19
19
|
},
|
20
|
-
'src' => 'http://www.plantuml.com/plantuml/
|
20
|
+
'src' => 'http://www.plantuml.com/plantuml/svg/'
|
21
21
|
}
|
22
22
|
end
|
23
23
|
|
@@ -59,18 +59,20 @@ module Jekyll::Spaceship
|
|
59
59
|
def handle_plantuml(code)
|
60
60
|
# wrap plantuml code
|
61
61
|
code = "@startuml#{code}@enduml".encode('UTF-8')
|
62
|
-
|
63
|
-
url = get_url(code)
|
62
|
+
url = self.get_url(code)
|
64
63
|
|
65
64
|
# render mode
|
66
65
|
case self.config['mode']
|
67
66
|
when 'pre-fetch'
|
68
|
-
|
67
|
+
data = self.class.fetch_img_data(url)
|
68
|
+
end
|
69
|
+
if data.nil?
|
70
|
+
data = { 'type' => 'url', 'body' => url }
|
69
71
|
end
|
70
72
|
|
71
73
|
# return img tag
|
72
|
-
|
73
|
-
|
74
|
+
data['class'] = self.config['css']['class']
|
75
|
+
self.class.make_img_tag(data)
|
74
76
|
end
|
75
77
|
|
76
78
|
def get_url(code)
|
@@ -87,21 +89,5 @@ module Jekyll::Spaceship
|
|
87
89
|
raise "No supported src ! #{src}"
|
88
90
|
end
|
89
91
|
end
|
90
|
-
|
91
|
-
def get_plantuml_img_data(url)
|
92
|
-
data = ''
|
93
|
-
begin
|
94
|
-
res = Net::HTTP.get_response URI(url)
|
95
|
-
raise res.body unless res.is_a?(Net::HTTPSuccess)
|
96
|
-
data = Base64.encode64(res.body)
|
97
|
-
content_type = res.header['Content-Type']
|
98
|
-
raise 'Unknown content type!' if content_type.nil?
|
99
|
-
data = "data:#{content_type};base64, #{data}"
|
100
|
-
rescue StandardError => msg
|
101
|
-
data = url
|
102
|
-
logger.log msg
|
103
|
-
end
|
104
|
-
data
|
105
|
-
end
|
106
92
|
end
|
107
93
|
end
|
@@ -305,10 +305,12 @@ module Jekyll::Spaceship
|
|
305
305
|
cvter = self.converter('markdown')
|
306
306
|
return if cvter.nil?
|
307
307
|
content = cell.inner_html
|
308
|
+
content = self.pre_exclude(content, [/(\<code.*\>.*\<\/code\>)/])
|
308
309
|
.gsub(/(?<!\\)\|/, '\\|')
|
309
310
|
.gsub(/^\s+|\s+$/, '')
|
310
311
|
.gsub(/</, '<')
|
311
312
|
.gsub(/>/, '>')
|
313
|
+
content = self.post_exclude(content)
|
312
314
|
content = cvter.convert(content)
|
313
315
|
content = Nokogiri::HTML.fragment(content)
|
314
316
|
if content.children.first&.name == 'p'
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: jekyll-spaceship
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.9.4
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- jeffreytse
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2020-
|
11
|
+
date: 2020-10-12 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: jekyll
|
@@ -124,6 +124,7 @@ files:
|
|
124
124
|
- ".codeclimate.yml"
|
125
125
|
- ".github/FUNDING.yml"
|
126
126
|
- ".gitignore"
|
127
|
+
- ".rspec"
|
127
128
|
- ".travis.yml"
|
128
129
|
- Gemfile
|
129
130
|
- LICENSE.txt
|
@@ -140,11 +141,11 @@ files:
|
|
140
141
|
- lib/jekyll-spaceship/processors/element-processor.rb
|
141
142
|
- lib/jekyll-spaceship/processors/emoji-processor.rb
|
142
143
|
- lib/jekyll-spaceship/processors/mathjax-processor.rb
|
144
|
+
- lib/jekyll-spaceship/processors/media-processor.rb
|
143
145
|
- lib/jekyll-spaceship/processors/mermaid-processor.rb
|
144
146
|
- lib/jekyll-spaceship/processors/plantuml-processor.rb
|
145
147
|
- lib/jekyll-spaceship/processors/polyfill-processor.rb
|
146
148
|
- lib/jekyll-spaceship/processors/table-processor.rb
|
147
|
-
- lib/jekyll-spaceship/processors/video-processor.rb
|
148
149
|
- lib/jekyll-spaceship/utils/.keep
|
149
150
|
- lib/jekyll-spaceship/version.rb
|
150
151
|
- logos/jekyll-spaceship-logo.png
|
@@ -173,5 +174,6 @@ rubygems_version: 3.0.8
|
|
173
174
|
signing_key:
|
174
175
|
specification_version: 4
|
175
176
|
summary: A Jekyll plugin to provide powerful supports for table, mathjax, plantuml,
|
176
|
-
mermaid, emoji, video, youtube, vimeo, dailymotion,
|
177
|
+
mermaid, emoji, video, audio, youtube, vimeo, dailymotion, spotify, soundcloud,
|
178
|
+
etc.
|
177
179
|
test_files: []
|
@@ -1,139 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require 'uri'
|
4
|
-
|
5
|
-
module Jekyll::Spaceship
|
6
|
-
class VideoProcessor < Processor
|
7
|
-
def self.config
|
8
|
-
{
|
9
|
-
'default' => {
|
10
|
-
'id' => 'video-{id}',
|
11
|
-
'class' => 'video',
|
12
|
-
'width' => '100%',
|
13
|
-
'height' => 350,
|
14
|
-
'border' => 0,
|
15
|
-
'style' => 'max-width: 600px',
|
16
|
-
'allow' => 'encrypted-media; picture-in-picture',
|
17
|
-
}
|
18
|
-
}
|
19
|
-
end
|
20
|
-
|
21
|
-
def on_handle_markdown(content)
|
22
|
-
content = handle_normal_video(content)
|
23
|
-
content = handle_youtube(content)
|
24
|
-
content = handle_vimeo(content)
|
25
|
-
content = handle_dailymotion(content)
|
26
|
-
end
|
27
|
-
|
28
|
-
# Examples:
|
29
|
-
# 
|
30
|
-
# 
|
31
|
-
# 
|
32
|
-
def handle_normal_video(content)
|
33
|
-
handle_video(content, {
|
34
|
-
host: '(https?:)?\\/\\/.*\\/',
|
35
|
-
id: '(.+?\\.(avi|mp4|webm|ogg|ogv|flv|mkv|mov|wmv|3gp|rmvb|asf))',
|
36
|
-
})
|
37
|
-
end
|
38
|
-
|
39
|
-
# Examples:
|
40
|
-
# 
|
41
|
-
# 
|
42
|
-
# 
|
43
|
-
def handle_youtube(content)
|
44
|
-
handle_video(content, {
|
45
|
-
host: '(https?:)?\\/\\/.*youtu.*',
|
46
|
-
id: '(?<=\\?v\\=|embed\\/|\\.be\\/)([a-zA-Z0-9\\_\\-]+)',
|
47
|
-
iframe_url: "https://www.youtube.com/embed/"
|
48
|
-
})
|
49
|
-
end
|
50
|
-
|
51
|
-
# Examples:
|
52
|
-
# 
|
53
|
-
# 
|
54
|
-
def handle_vimeo(content)
|
55
|
-
handle_video(content, {
|
56
|
-
host: '(https?:)?\\/\\/vimeo\\.com\\/',
|
57
|
-
id: '([0-9]+)',
|
58
|
-
iframe_url: "https://player.vimeo.com/video/"
|
59
|
-
})
|
60
|
-
end
|
61
|
-
|
62
|
-
# Examples:
|
63
|
-
# 
|
64
|
-
# 
|
65
|
-
def handle_dailymotion(content)
|
66
|
-
handle_video(content, {
|
67
|
-
host: '(https?:)?\\/\\/.*dai.?ly.*',
|
68
|
-
id: '(?<=video\\/|\\/)([a-zA-Z0-9\\_\\-]+)',
|
69
|
-
iframe_url: "https://www.dailymotion.com/embed/video/"
|
70
|
-
})
|
71
|
-
end
|
72
|
-
|
73
|
-
def handle_video(content, data)
|
74
|
-
host = data[:host]
|
75
|
-
return content if content.sub(/#{host}/, '').nil?
|
76
|
-
|
77
|
-
iframe_url = data[:iframe_url]
|
78
|
-
id = data[:id]
|
79
|
-
url = "(#{host}#{id}\\S*)"
|
80
|
-
title = '("(.*)".*){0,1}'
|
81
|
-
|
82
|
-
# pre-handle reference-style links
|
83
|
-
regex = /(\[(.*)\]:\s*(#{url}\s*#{title}))/
|
84
|
-
content.scan regex do |match_data|
|
85
|
-
match = match_data[0]
|
86
|
-
ref_name = match_data[1]
|
87
|
-
ref_value = match_data[2]
|
88
|
-
content = content.gsub(match, '')
|
89
|
-
.gsub(/\!\[(.*)\]\s*\[#{ref_name}\]/,
|
90
|
-
"")
|
91
|
-
end
|
92
|
-
|
93
|
-
# handle inline-style links
|
94
|
-
regex = /(\!\[(.*)\]\(.*#{url}\s*#{title}\))/
|
95
|
-
content.scan regex do |match_data|
|
96
|
-
url = match_data[2]
|
97
|
-
id = match_data[4]
|
98
|
-
title = match_data[6]
|
99
|
-
qs = url.match(/(?<=\?)(\S*?)$/)
|
100
|
-
qs = Hash[URI.decode_www_form(qs.to_s)].reject do |k, v|
|
101
|
-
next true if v == id or v == ''
|
102
|
-
end
|
103
|
-
|
104
|
-
default = self.config['default']
|
105
|
-
css_id = qs['id'] || default['id']
|
106
|
-
css_class = qs['class'] || default['class']
|
107
|
-
width = qs['width'] || data[:width] || default['width']
|
108
|
-
height = qs['height'] || data[:height] || default['height']
|
109
|
-
frameborder = qs['frameborder'] || default['border']
|
110
|
-
style = qs['style'] || default['style']
|
111
|
-
allow = qs['allow'] || default['allow']
|
112
|
-
|
113
|
-
css_id = css_id.gsub('{id}', id)
|
114
|
-
css_class = css_class.gsub('{id}', id)
|
115
|
-
|
116
|
-
url = URI(iframe_url ? "#{iframe_url}#{id}" : url).tap do |v|
|
117
|
-
v.query = URI.encode_www_form(qs) if qs.size > 0
|
118
|
-
end
|
119
|
-
|
120
|
-
html = "<iframe"\
|
121
|
-
" id=\"#{css_id}\""\
|
122
|
-
" class=\"#{css_class}\""\
|
123
|
-
" src=\"#{url}\""\
|
124
|
-
" title=\"#{title}\""\
|
125
|
-
" width=\"#{width}\""\
|
126
|
-
" height=\"#{height}\""\
|
127
|
-
" style=\"#{style}\""\
|
128
|
-
" allow=\"#{allow}\""\
|
129
|
-
" frameborder=\"#{frameborder}\""\
|
130
|
-
" allowfullscreen>" \
|
131
|
-
"</iframe>"
|
132
|
-
|
133
|
-
content = content.gsub(match_data[0], html)
|
134
|
-
self.handled = true
|
135
|
-
end
|
136
|
-
content
|
137
|
-
end
|
138
|
-
end
|
139
|
-
end
|