jekyll-spaceship 0.8.5 → 0.9.2

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 5310871597e6232b96116549a1ea824054fe68572ced4bb2a14238bb3b7098fd
4
- data.tar.gz: 41461f5eb94fda09064bdd21126d0b505c84232bec5a51a309b5954ffa5ba2b6
3
+ metadata.gz: 604e55f37a3597c851c56fd267f316eea0a180adcb8613c0908efa9f5ea87869
4
+ data.tar.gz: a32c960d432a348eb583d2e5d114f66564de56e2726b680765166bce86adbd6f
5
5
  SHA512:
6
- metadata.gz: 9a081bcfb8bcdaed430eeb456cae89172e55de4654ae81412fdf2749b8cb793f2c9cb702b829d1c80aa48db86f8bd5b445bde91cd1d07fdd57d717861e561642
7
- data.tar.gz: e98d3a810ba88e430f9cddfcfafc4e6f7d5cd77b4b91ed9ad2b073b02332af915059b195fbace35f02dddc88415466405eefc11b0e4c9485633f77d5fc1b0415
6
+ metadata.gz: 4f8383046d4a550ddacd859e14699b90b7a92d3759ebddf34430a8cbe421bfe1334f9a80186b80942aaff96a5802415ff7d01b78ec70939719aa483686238778
7
+ data.tar.gz: c9c98d831503df109e943d49030c7a6b7d0a37cd0d1eb0dba6fe39f555ee05ceaf5550d8cbcab10ec8160eb7bdc559db9ca8bcd6a7ec3ddefc813b97ecb7a585
data/.rspec ADDED
@@ -0,0 +1,3 @@
1
+ --color
2
+ --format progress
3
+ --require spec_helper
@@ -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
- - NOKOGIRI_USE_SYSTEM_LIBRARIES=true
8
+ - NOKOGIRI_USE_SYSTEM_LIBRARIES=true
9
9
  matrix:
10
- - JEKYLL_VERSION="~> 3.8"
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
@@ -101,16 +101,20 @@ Spaceship is a minimalistic, powerful and extremely customizable [Jekyll](https:
101
101
  - [1.3 Headerless](#headerless)
102
102
  - [1.4 Cell Alignment](#cell-alignment)
103
103
  - [1.5 Cell Markdown](#cell-markdown)
104
+ - [1.6 Cell Inline Attributes](#cell-inline-attributes)
104
105
  - [2. MathJax Usage](#2-mathjax-usage)
105
106
  - [2.1 Performance Optimization](#21-performance-optimization)
106
107
  - [2.2 How to use?](#22-how-to-use)
107
108
  - [3. PlantUML Usage](#3-plantuml-usage)
108
109
  - [4. Mermaid Usage](#4-mermaid-usage)
109
- - [5. Video Usage](#5-video-usage)
110
+ - [5. Media Usage](#5-media-usage)
110
111
  - [5.1 Youtube Usage](#youtube-usage)
111
112
  - [5.2 Vimeo Usage](#vimeo-usage)
112
113
  - [5.3 DailyMotion Usage](#dailymotion-usage)
113
- - [5.4 General Video Usage](#general-video-usage)
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)
114
118
  - [6. Hybrid HTML with Markdown](#6-hybrid-html-with-markdown)
115
119
  - [7. Markdown Polyfill](#7-markdown-polyfill)
116
120
  - [7.1 Escape Ordered List](#71-escape-ordered-list)
@@ -142,7 +146,7 @@ plugins:
142
146
 
143
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.
144
148
 
145
- ### Additions
149
+ ### Additions for Unlimited GitHub Pages
146
150
 
147
151
  * Here is a GitHub Action named [jekyll-deploy-action](https://github.com/jeffreytse/jekyll-deploy-action) for Jekyll site deployment conveniently. 👍
148
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.
@@ -161,16 +165,20 @@ jekyll-spaceship:
161
165
  - plantuml-processor
162
166
  - mermaid-processor
163
167
  - polyfill-processor
164
- - video-processor
168
+ - media-processor
165
169
  - emoji-processor
166
170
  - element-processor
167
171
  mathjax-processor:
168
- src: //cdn.mathjax.org/mathjax/latest/MathJax.js?config=TeX-AMS-MML_HTMLorMML
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
169
175
  config:
170
- tex2jax:
176
+ tex:
171
177
  inlineMath:
172
178
  - ['$','$']
173
179
  - ['\(','\)']
180
+ svg:
181
+ fontCache: 'global'
174
182
  plantuml-processor:
175
183
  mode: default # mode value 'pre-fetch' for fetching image at building stage
176
184
  css:
@@ -178,7 +186,7 @@ jekyll-spaceship:
178
186
  syntax:
179
187
  code: 'plantuml!'
180
188
  custom: ['@startuml', '@enduml']
181
- src: http://www.plantuml.com/plantuml/png/
189
+ src: http://www.plantuml.com/plantuml/svg/
182
190
  mermaid-processor:
183
191
  mode: default # mode value 'pre-fetch' for fetching image at building stage
184
192
  css:
@@ -189,14 +197,14 @@ jekyll-spaceship:
189
197
  config:
190
198
  theme: default
191
199
  src: https://mermaid.ink/svg/
192
- video-processor:
200
+ media-processor:
193
201
  default:
194
- id: 'video-{id}'
195
- class: 'video'
202
+ id: 'media-{id}'
203
+ class: 'media'
196
204
  width: '100%'
197
205
  height: 350
198
- border: 0
199
- style: 'max-width: 600px'
206
+ frameborder: 0
207
+ style: 'max-width: 600px; outline: none;'
200
208
  allow: 'encrypted-media; picture-in-picture'
201
209
  emoji-processor:
202
210
  css:
@@ -534,6 +542,61 @@ Rowspan is 4
534
542
  </tbody>
535
543
  </table>
536
544
 
545
+ #### Cell Inline Attributes
546
+
547
+ This feature is very useful for custom cell such as using inline style. (e.g., background, color, font)
548
+ The idea and syntax comes from the [Maruku](http://maruku.rubyforge.org/) package.
549
+
550
+ [](https://kramdown.gettalong.org/syntax.html#block-ials)
551
+
552
+ Following are some examples of attributes definitions (ALDs) and afterwards comes the syntax explanation:
553
+
554
+ ```markdown
555
+ {:ref-name: #id .cls1 .cls2}
556
+ {:second: ref-name #id-of-other title="hallo you"}
557
+ {:other: ref-name second}
558
+ ```
559
+
560
+ An ALD line has the following structure:
561
+
562
+ - a left brace, optionally preceded by up to three spaces,
563
+ - followed by a colon, the id and another colon,
564
+ - followed by attribute definitions (allowed characters are backslash-escaped closing braces or any character except a not escaped closing brace),
565
+ - followed by a closing brace and optional spaces until the end of the line.
566
+
567
+ If there is more than one ALD with the same reference name, the attribute definitions of all the ALDs are processed like they are defined in one ALD.
568
+
569
+ An inline attribute list (IAL) is used to attach attributes to another element.
570
+ Here are some examples for span IALs:
571
+
572
+ ```markdown
573
+ {: #id .cls1 .cls2} <!-- #id <=> id="id", .cls1 .cls2 <=> class="cls1 cls2" -->
574
+ {: ref-name title="hallo you"}
575
+ {: ref-name class='.cls3' .cls4}
576
+ ```
577
+
578
+ Here is an example for custom table cell with IAL:
579
+
580
+ ```markdown
581
+ {:color-style: style="background: black;"}
582
+ {:color-style: style="color: white;"}
583
+ {:text-style: style="font-weight: 800; text-decoration: underline;"}
584
+
585
+ |: Here's an Inline Attribute Lists example :||||
586
+ | ------- | ------------------ | -------------------- | ------------------ |
587
+ |: :|: <div style="color: red;"> &lt; Normal HTML Block > </div> :|||
588
+ | ^^ | Red {: .cls style="background: orange" } |||
589
+ | ^^ IALs | Green {: #id style="background: green; color: white" } |||
590
+ | ^^ | Blue {: style="background: blue; color: white" } |||
591
+ | ^^ | Black {: color-style text-style } |||
592
+ ```
593
+
594
+ Code above would be parsed as:
595
+
596
+ <img width="580px" src="https://user-images.githubusercontent.com/9413601/88461592-738afb00-ced7-11ea-9aac-3179023742b0.png" alt="IALs">
597
+
598
+ Additionally, [here](https://kramdown.gettalong.org/syntax.html#block-ials) you can learn more details about IALs.
599
+
537
600
  ### 2. MathJax Usage
538
601
 
539
602
  [MathJax](http://www.mathjax.org/) is an open-source JavaScript display engine for LaTeX, MathML, and AsciiMath notation that works in all modern browsers.
@@ -574,7 +637,7 @@ Code above would be parsed as:
574
637
 
575
638
  ### 3. PlantUML Usage
576
639
 
577
- [PlantUML](http://plantuml.sourceforge.net/) is a component that allows to quickly write:
640
+ [PlantUML](https://plantuml.com) is a component that allows to quickly write:
578
641
 
579
642
  - sequence diagram,
580
643
  - use case diagram,
@@ -645,25 +708,31 @@ Code above would be parsed as:
645
708
 
646
709
  ![Mermaid Diagram](https://user-images.githubusercontent.com/9413601/85282355-2e317300-b4be-11ea-9c30-8f9d61540d14.png)
647
710
 
648
- ### 5. Video Usage
711
+ ### 5. Media Usage
649
712
 
650
- 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?**"
651
714
 
652
- While its not possible to embed a video in markdown, the best and easiest way is to extract a frame from the video. To add videos to your markdown files easier I developped this tool for you, and it will parse the video link inside the image block automatically.
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.
653
719
 
654
- **For now, these video links parsing are provided:**
720
+ **For now, these media links parsing are provided:**
655
721
 
656
722
  - Youtube
657
723
  - Vimeo
658
724
  - DailyMotion
659
- - General Video ( mp4 | avi | webm | ogg | ogv | 3gp | flv | mov ... )
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 ... )
660
729
 
661
- 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:
662
731
 
663
732
  Inline-style:
664
733
 
665
734
  ```markdown
666
- ![]({video-link})
735
+ ![]({media-link})
667
736
  ```
668
737
 
669
738
  Reference-style:
@@ -671,10 +740,10 @@ Reference-style:
671
740
  ```markdown
672
741
  ![][{reference}]
673
742
 
674
- [{reference}]: {video-link}
743
+ [{reference}]: {media-link}
675
744
  ```
676
745
 
677
- For configuring video attributes (e.g, width, height), just adding query string to
746
+ For configuring media attributes (e.g, width, height), just adding query string to
678
747
  the link as below:
679
748
 
680
749
  ```markdown
@@ -707,6 +776,22 @@ the link as below:
707
776
  ![](https://dai.ly/x7tgcev?width=100%&height=400)
708
777
  ```
709
778
 
779
+ #### Spotify Usage
780
+
781
+ ```markdown
782
+ ![](http://open.spotify.com/track/4Dg5moVCTqxAb7Wr8Dq2T5)
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
+ ![](https://soundcloud.com/aviciiofficial/preview-avicii-vs-lenny)
791
+ ```
792
+
793
+ <image width="600" src="https://user-images.githubusercontent.com/9413601/89762969-1c666680-db24-11ea-97e3-4340f7fac7ac.png">
794
+
710
795
  #### General Video Usage
711
796
 
712
797
  ```markdown
@@ -717,6 +802,15 @@ the link as below:
717
802
  ![](//techslides.com/demos/sample-videos/small.mp4?width=400)
718
803
  ```
719
804
 
805
+ #### General Audio Usage
806
+
807
+ ```markdown
808
+ ![](//www.soundhelix.com/examples/mp3/SoundHelix-Song-1.mp3)
809
+
810
+ ![](//www.soundhelix.com/examples/mp3/SoundHelix-Song-1.mp3?autoplay=1&loop=1)
811
+ ```
812
+
813
+ <image width="300" src="https://user-images.githubusercontent.com/9413601/89762143-68181080-db22-11ea-8467-e8b2a8a96ae5.png">
720
814
 
721
815
  ### 6. Hybrid HTML with Markdown
722
816
 
@@ -890,7 +984,7 @@ jekyll-spaceship:
890
984
  css:
891
985
  - a: # Replce all `a` tags
892
986
  props: #
893
- loading: lazy # Replace `lading` value to `lazy`
987
+ loading: lazy # Replace `loading` value to `lazy`
894
988
  ```
895
989
 
896
990
  In case you want to prevent loading some images/iframes lazily, add
@@ -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
 
@@ -10,7 +10,7 @@ module Jekyll::Spaceship
10
10
  'plantuml-processor',
11
11
  'mermaid-processor',
12
12
  'polyfill-processor',
13
- 'video-processor',
13
+ 'media-processor',
14
14
  'emoji-processor',
15
15
  'element-processor'
16
16
  ]
@@ -69,7 +69,7 @@ module Jekyll::Spaceship
69
69
  end
70
70
 
71
71
  def self.ext(page)
72
- ext = page.path.match(/\.\S+$/)
72
+ ext = page.path.match(/\.[^.]+$/)
73
73
  ext.to_s.rstrip
74
74
  end
75
75
 
@@ -160,7 +160,7 @@ 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
  next if regex.nil?
166
166
  content.scan(regex) do |match_data|
@@ -192,5 +192,38 @@ module Jekyll::Spaceship
192
192
  end
193
193
  content
194
194
  end
195
+
196
+ def self.fetch_img_data(url)
197
+ begin
198
+ res = Net::HTTP.get_response URI(url)
199
+ raise res.body unless res.is_a?(Net::HTTPSuccess)
200
+ content_type = res.header['Content-Type']
201
+ raise 'Unknown content type!' if content_type.nil?
202
+ content_body = res.body.force_encoding('UTF-8')
203
+ return {
204
+ 'type' => content_type,
205
+ 'body' => content_body
206
+ }
207
+ rescue StandardError => msg
208
+ logger.log msg
209
+ end
210
+ end
211
+
212
+ def self.make_img_tag(data)
213
+ css_class = data['class']
214
+ type = data['type']
215
+ body = data['body']
216
+ if type == 'url'
217
+ "<img class=\"#{css_class}\" src=\"#{body}\">"
218
+ elsif type.include?('svg')
219
+ body.gsub(/\<\?xml.*?\?>/, '')
220
+ .gsub(/<!--[^\0]*?-->/, '')
221
+ .sub(/<svg /, "<svg class=\"#{css_class}\" ")
222
+ else
223
+ body = Base64.encode64(body)
224
+ body = "data:#{type};base64, #{body}"
225
+ "<img class=\"#{css_class}\" src=\"#{body}\">"
226
+ end
227
+ end
195
228
  end
196
229
  end
@@ -6,9 +6,13 @@ module Jekyll::Spaceship
6
6
  class MathjaxProcessor < Processor
7
7
  def self.config
8
8
  {
9
- 'src' => '//cdn.mathjax.org/mathjax/latest/MathJax.js?config=TeX-AMS-MML_HTMLorMML',
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
- 'tex2jax' => { 'inlineMath' => [['$','$'], ['\\(','\\)']] }
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
- cfg = "MathJax.Hub.Config(#{config['config'].to_json});"
31
- head.add_child("<script src=\"#{config['src']}\">#{cfg}</script>")
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,234 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'uri'
4
+
5
+ module Jekyll::Spaceship
6
+ class MediaProcessor < Processor
7
+ def self.config
8
+ {
9
+ 'default' => {
10
+ 'id' => 'media-{id}',
11
+ 'class' => 'media',
12
+ 'width' => '100%',
13
+ 'height' => 350,
14
+ 'frameborder' => 0,
15
+ 'style' => 'max-width: 600px;outline: none',
16
+ 'allow' => 'encrypted-media; picture-in-picture'
17
+ }
18
+ }
19
+ end
20
+
21
+ def on_handle_markdown(content)
22
+ content = handle_normal_audio(content)
23
+ content = handle_normal_video(content)
24
+ content = handle_youtube(content)
25
+ content = handle_vimeo(content)
26
+ content = handle_dailymotion(content)
27
+ content = handle_spotify(content)
28
+ content = handle_soundcloud(content)
29
+ end
30
+
31
+ # Examples:
32
+ # ![audio](//www.soundhelix.com/examples/mp3/SoundHelix-Song-1.mp3)
33
+ # ![audio](//www.expample.com/examples/t-rex-roar.mp3?autoplay=true&loop=true)
34
+ def handle_normal_audio(content)
35
+ handle_media(content, {
36
+ media_type: 'audio',
37
+ host: '(https?:)?\\/\\/.*\\/',
38
+ id: '(.+?\\.(mp3|wav|ogg|mid|midi|aac|wma))',
39
+ })
40
+ end
41
+
42
+
43
+ # Examples:
44
+ # ![video](//www.html5rocks.com/en/tutorials/video/basics/devstories.webm)
45
+ # ![video](//techslides.com/demos/sample-videos/small.ogv?allow=autoplay)
46
+ # ![video](//techslides.com/demos/sample-videos/small.mp4?width=400)
47
+ def handle_normal_video(content)
48
+ handle_media(content, {
49
+ media_type: 'iframe',
50
+ host: '(https?:)?\\/\\/.*\\/',
51
+ id: '(.+?\\.(avi|mp4|webm|ogg|ogv|flv|mkv|mov|wmv|3gp|rmvb|asf))'
52
+ })
53
+ end
54
+
55
+ # Examples:
56
+ # ![youtube](https://www.youtube.com/watch?v=XA2WjJbmmoM "title")
57
+ # ![youtube](http://www.youtube.com/embed/w-m_yZCLF5Q)
58
+ # ![youtube](//youtu.be/mEP3YXaSww8?height=100%&width=400)
59
+ def handle_youtube(content)
60
+ handle_media(content, {
61
+ media_type: 'iframe',
62
+ host: '(https?:)?\\/\\/.*youtu.*',
63
+ id: '(?<=\\?v\\=|embed\\/|\\.be\\/)([a-zA-Z0-9\\_\\-]+)',
64
+ base_url: "https://www.youtube.com/embed/"
65
+ })
66
+ end
67
+
68
+ # Examples:
69
+ # ![vimeo](https://vimeo.com/263856289)
70
+ # ![vimeo](https://vimeo.com/263856289?height=100%&width=400)
71
+ def handle_vimeo(content)
72
+ handle_media(content, {
73
+ media_type: 'iframe',
74
+ host: '(https?:)?\\/\\/vimeo\\.com\\/',
75
+ id: '([0-9]+)',
76
+ base_url: "https://player.vimeo.com/video/"
77
+ })
78
+ end
79
+
80
+ # Examples:
81
+ # ![dailymotion](https://www.dailymotion.com/video/x7tgcev)
82
+ # ![dailymotion](https://dai.ly/x7tgcev?height=100%&width=400)
83
+ def handle_dailymotion(content)
84
+ handle_media(content, {
85
+ media_type: 'iframe',
86
+ host: '(https?:)?\\/\\/.*dai.?ly.*',
87
+ id: '(?<=video\\/|\\/)([a-zA-Z0-9\\_\\-]+)',
88
+ base_url: "https://www.dailymotion.com/embed/video/"
89
+ })
90
+ end
91
+
92
+ # Examples:
93
+ # ![spotify](//open.spotify.com/track/4Dg5moVCTqxAb7Wr8Dq2T5)
94
+ # ![spotify](//open.spotify.com/track/37mEkAaqCE7FXMvnlVA8pp?width=400)
95
+ def handle_spotify(content)
96
+ handle_media(content, {
97
+ media_type: 'iframe',
98
+ host: '(https?:)?\\/\\/open\\.spotify\\.com\\/track\\/',
99
+ id: '(?<=track\\/)([a-zA-Z0-9\\_\\-]+)',
100
+ base_url: "https://open.spotify.com/embed/track/",
101
+ height: 80
102
+ })
103
+ end
104
+
105
+ # Examples:
106
+ # ![soundcloud](//soundcloud.com/aviciiofficial/preview-avicii-vs-lenny)
107
+ def handle_soundcloud(content)
108
+ handle_media(content, {
109
+ media_type: 'iframe',
110
+ id_from: 'html',
111
+ host: '(https?:)?\\/\\/soundcloud\\.com\\/.+\\/[^\\?]+',
112
+ id: '(?<=soundcloud:\\/\\/sounds:)([0-9]+)',
113
+ base_url: "https://w.soundcloud.com/player/?url="\
114
+ "https%3A//api.soundcloud.com/tracks/",
115
+ height: 125,
116
+ })
117
+ end
118
+
119
+ def handle_media(content, data)
120
+ host = data[:host]
121
+ return content if content.sub(/#{host}/, '').nil?
122
+
123
+ media_type = data[:media_type]
124
+ base_url = data[:base_url]
125
+ id = data[:id_from] === 'html' ? '()' : data[:id]
126
+ url = "(#{host}#{id}\\S*)"
127
+ title = '("(.*)".*){0,1}'
128
+
129
+ # pre-handle reference-style links
130
+ regex = /(\[(.*)\]:\s*(#{url}\s*#{title}))/
131
+ content.scan regex do |match_data|
132
+ match = match_data[0]
133
+ ref_name = match_data[1]
134
+ ref_value = match_data[2]
135
+ content = content.gsub(match, '')
136
+ .gsub(/\!\[(.*)\]\s*\[#{ref_name}\]/,
137
+ "![\1](#{ref_value})")
138
+ end
139
+
140
+ # handle inline-style links
141
+ regex = /(\!\[(.*)\]\(.*#{url}\s*#{title}\))/
142
+ content.scan regex do |match_data|
143
+ url = match_data[2]
144
+ id = data[:id_from] === 'html' \
145
+ ? get_id_from_html(url, data[:id]) \
146
+ : match_data[4]
147
+ title = match_data[6]
148
+ qs = url.match(/(?<=\?)(\S*?)$/)
149
+ qs = Hash[URI.decode_www_form(qs.to_s)].reject do |k, v|
150
+ next true if v == id or v == ''
151
+ end
152
+
153
+ cfg = self.config['default'].clone
154
+ cfg['id'] = qs['id'] || cfg['id']
155
+ cfg['class'] = qs['class'] || cfg['class']
156
+ cfg['style'] = qs['style'] || cfg['style']
157
+ cfg['id'] = cfg['id'].gsub('{id}', id)
158
+ cfg['class'] = cfg['class'].gsub('{id}', id)
159
+
160
+ cfg['src'] = URI(base_url ? "#{base_url}#{id}" : url).tap do |v|
161
+ v.query = URI.encode_www_form(qs) if qs.size > 0
162
+ end
163
+
164
+ case media_type
165
+ when 'audio'
166
+ cfg['autoplay'] = qs['autoplay'] || data[:autoplay] || cfg['autoplay']
167
+ cfg['loop'] = qs['loop'] || data[:loop] || cfg['loop']
168
+ cfg['style'] += ';display: none;' if qs['hidden']
169
+ content = handle_audio(content, { target: match_data[0], cfg: cfg })
170
+ when 'iframe'
171
+ cfg['title'] = title
172
+ cfg['width'] = qs['width'] || data[:width] || cfg['width']
173
+ cfg['height'] = qs['height'] || data[:height] || cfg['height']
174
+ cfg['frameborder'] = qs['frameborder'] || cfg['frameborder']
175
+ cfg['allow'] ||= cfg['allow']
176
+ content = handle_iframe(content, { target: match_data[0], cfg: cfg })
177
+ end
178
+ self.handled = true
179
+ end
180
+ content
181
+ end
182
+
183
+ def handle_audio(content, data)
184
+ cfg = data[:cfg]
185
+ html = "<audio"\
186
+ " id=\"#{cfg['id']}\""\
187
+ " class=\"#{cfg['class']}\""\
188
+ " #{cfg['autoplay'] ? 'autoplay' : ''}"\
189
+ " #{cfg['loop'] ? 'loop' : ''}"\
190
+ " src=\"#{cfg['src']}\""\
191
+ " style=\"#{cfg['style']}\""\
192
+ " controls>" \
193
+ "<p> Your browser doesn't support HTML5 audio."\
194
+ " Here is a <a href=\"#{cfg['src']}\">link to download the audio</a>"\
195
+ "instead. </p>"\
196
+ "</audio>"
197
+ content.gsub(data[:target], html)
198
+ end
199
+
200
+ def handle_iframe(content, data)
201
+ cfg = data[:cfg]
202
+ html = "<iframe"\
203
+ " id=\"#{cfg['id']}\""\
204
+ " class=\"#{cfg['class']}\""\
205
+ " src=\"#{cfg['src']}\""\
206
+ " title=\"#{cfg['title']}\""\
207
+ " width=\"#{cfg['width']}\""\
208
+ " height=\"#{cfg['height']}\""\
209
+ " style=\"#{cfg['style']}\""\
210
+ " allow=\"#{cfg['allow']}\""\
211
+ " frameborder=\"#{cfg['frameborder']}\""\
212
+ " allowfullscreen>"\
213
+ "</iframe>"
214
+ content.gsub(data[:target], html)
215
+ end
216
+
217
+ def get_id_from_html(url, pattern)
218
+ id = ''
219
+ begin
220
+ url = 'https:' + url if url.start_with? '//'
221
+ res = Net::HTTP.get_response URI(url)
222
+ raise res.body unless res.is_a?(Net::HTTPSuccess)
223
+ res.body.match pattern do |match_data|
224
+ id = match_data[0]
225
+ break
226
+ end
227
+ rescue StandardError => msg
228
+ data = url
229
+ logger.log msg
230
+ end
231
+ id
232
+ end
233
+ end
234
+ 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
- url = self.get_mermaid_img_data(url)
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
- css_class = self.config['css']['class']
76
- "<img class=\"#{css_class}\" src=\"#{url}\">"
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/png/'
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
- url = self.get_plantuml_img_data(url)
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
- css_class = self.config['css']['class']
73
- "<img class=\"#{css_class}\" src=\"#{url}\">"
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
@@ -5,6 +5,9 @@ require "nokogiri"
5
5
 
6
6
  module Jekyll::Spaceship
7
7
  class TableProcessor < Processor
8
+ ATTR_LIST_PATTERN = /((?<!\\)\{:(?:([A-Za-z]\S*):)?(.*?)(?<!\\)\})/
9
+ ATTR_LIST_REFS = {}
10
+
8
11
  def on_handle_markdown(content)
9
12
  # pre-handle reference-style links
10
13
  references = {}
@@ -16,7 +19,9 @@ module Jekyll::Spaceship
16
19
  if references.size > 0
17
20
  content.scan(/[^\n]*(?<!\\)\|[^\n]*/) do |result|
18
21
  references.each do |key, val|
19
- replace = result.gsub(/\[([^\n]*)\]\s*\[#{key}\]/, "[\1](#{val})")
22
+ replace = result.gsub(
23
+ /\[([^\n\]]*?)\]\s*\[#{key}\]/,
24
+ "[\1](#{val})")
20
25
  next if result == replace
21
26
  content = content.gsub(result, replace)
22
27
  end
@@ -42,6 +47,19 @@ module Jekyll::Spaceship
42
47
  next if result == replace
43
48
  content = content.gsub(result, replace)
44
49
  end
50
+
51
+ # pre-handle attribute list (AL)
52
+ ATTR_LIST_REFS.clear()
53
+ content.scan(ATTR_LIST_PATTERN) do |result|
54
+ ref = result[1]
55
+ list = result[2]
56
+ next if ref.nil?
57
+ if ATTR_LIST_REFS.has_key? ref
58
+ ATTR_LIST_REFS[ref] += list
59
+ else
60
+ ATTR_LIST_REFS[ref] = list
61
+ end
62
+ end
45
63
  content
46
64
  end
47
65
 
@@ -69,6 +87,7 @@ module Jekyll::Spaceship
69
87
  handle_multi_rows(data)
70
88
  handle_text_align(data)
71
89
  handle_rowspan(data)
90
+ handle_attr_list(data)
72
91
  end
73
92
  end
74
93
  rows.each do |row|
@@ -161,7 +180,7 @@ module Jekyll::Spaceship
161
180
  if scope.table.multi_row_cells != cells and scope.table.multi_row_start
162
181
  for i in 0...scope.table.multi_row_cells.count do
163
182
  multi_row_cell = scope.table.multi_row_cells[i]
164
- multi_row_cell.inner_html += "<br>#{cells[i].inner_html}"
183
+ multi_row_cell.inner_html += "\n<br>\n#{cells[i].inner_html}"
165
184
  end
166
185
  row.remove
167
186
  end
@@ -186,7 +205,7 @@ module Jekyll::Spaceship
186
205
  span_cell = scope.table.span_row_cells[scope.row.col_index]
187
206
  if span_cell and cell.content.match(/^\s*\^{2}/)
188
207
  cell.content = cell.content.gsub(/^\s*\^{2}/, '')
189
- span_cell.inner_html += "<br>#{cell.inner_html}"
208
+ span_cell.inner_html += "\n<br>\n#{cell.inner_html}"
190
209
  rowspan = span_cell.get_attribute('rowspan') || 1
191
210
  rowspan = rowspan.to_i + 1
192
211
  span_cell.set_attribute('rowspan', "#{rowspan}")
@@ -235,6 +254,52 @@ module Jekyll::Spaceship
235
254
  cell.set_attribute('style', style)
236
255
  end
237
256
 
257
+ # Examples:
258
+ # {:ref-name: .cls1 title="hello" }
259
+ # {: #id ref-name data="world" }
260
+ # {: #id title="hello" }
261
+ # {: .cls style="color: #333" }
262
+ def handle_attr_list(data)
263
+ cell = data.cell
264
+ content = cell.inner_html
265
+ # inline attribute list(IAL) handler
266
+ ial_handler = ->(list) do
267
+ list.scan(/(\S+)=("|')(.*?)\2|(\S+)/) do |attr|
268
+ key = attr[0]
269
+ val = attr[2]
270
+ single = attr[3]
271
+ if !key.nil?
272
+ val = (cell.get_attribute(key) || '') + val
273
+ cell.set_attribute(key, val)
274
+ elsif !single.nil?
275
+ if single.start_with? '#'
276
+ key = 'id'
277
+ val = single[1..-1]
278
+ elsif single.start_with? '.'
279
+ key = 'class'
280
+ val = cell.get_attribute(key) || ''
281
+ val += (val.size.zero? ? '' : ' ') + single[1..-1]
282
+ elsif ATTR_LIST_REFS.has_key? single
283
+ ial_handler.call ATTR_LIST_REFS[single]
284
+ end
285
+ unless key.nil?
286
+ cell.set_attribute(key, val)
287
+ end
288
+ end
289
+ end
290
+ end
291
+ # handle attribute list
292
+ content.scan(ATTR_LIST_PATTERN) do |result|
293
+ ref = result[1]
294
+ list = result[2]
295
+ # handle inline attribute list
296
+ ial_handler.call list if ref.nil?
297
+ # remove attr_list
298
+ content = content.sub(result[0], '')
299
+ end
300
+ cell.inner_html = content
301
+ end
302
+
238
303
  def handle_format(data)
239
304
  cell = data.cell
240
305
  cvter = self.converter('markdown')
@@ -242,6 +307,8 @@ module Jekyll::Spaceship
242
307
  content = cell.inner_html
243
308
  .gsub(/(?<!\\)\|/, '\\|')
244
309
  .gsub(/^\s+|\s+$/, '')
310
+ .gsub(/&lt;/, '<')
311
+ .gsub(/&gt;/, '>')
245
312
  content = cvter.convert(content)
246
313
  content = Nokogiri::HTML.fragment(content)
247
314
  if content.children.first&.name == 'p'
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Jekyll
4
4
  module Spaceship
5
- VERSION = "0.8.5"
5
+ VERSION = "0.9.2"
6
6
  end
7
7
  end
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.8.5
4
+ version: 0.9.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - jeffreytse
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-07-21 00:00:00.000000000 Z
11
+ date: 2020-09-18 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
@@ -169,10 +170,10 @@ required_rubygems_version: !ruby/object:Gem::Requirement
169
170
  - !ruby/object:Gem::Version
170
171
  version: '0'
171
172
  requirements: []
172
- rubyforge_project:
173
- rubygems_version: 2.7.7
173
+ rubygems_version: 3.0.8
174
174
  signing_key:
175
175
  specification_version: 4
176
176
  summary: A Jekyll plugin to provide powerful supports for table, mathjax, plantuml,
177
- mermaid, emoji, video, youtube, vimeo, dailymotion, etc.
177
+ mermaid, emoji, video, audio, youtube, vimeo, dailymotion, spotify, soundcloud,
178
+ etc.
178
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
- # ![video](//www.html5rocks.com/en/tutorials/video/basics/devstories.webm)
30
- # ![video](//techslides.com/demos/sample-videos/small.ogv?allow=autoplay)
31
- # ![video](//techslides.com/demos/sample-videos/small.mp4?width=400)
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
- # ![youtube](https://www.youtube.com/watch?v=XA2WjJbmmoM "title")
41
- # ![youtube](http://www.youtube.com/embed/w-m_yZCLF5Q)
42
- # ![youtube](//youtu.be/mEP3YXaSww8?height=100%&width=400)
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
- # ![vimeo](https://vimeo.com/263856289)
53
- # ![vimeo](https://vimeo.com/263856289?height=100%&width=400)
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
- # ![dailymotion](https://www.dailymotion.com/video/x7tgcev)
64
- # ![dailymotion](https://dai.ly/x7tgcev?height=100%&width=400)
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
- "![\1](#{ref_value})")
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