Compare commits

..

No commits in common. "master" and "v0.5.2" have entirely different histories.

18 changed files with 278 additions and 1343 deletions

3
.rspec
View File

@ -1,3 +0,0 @@
--color
--format progress
--require spec_helper

View File

@ -1,13 +1,13 @@
language: ruby language: ruby
cache: bundler cache: bundler
rvm: rvm:
- 2.7 - 2.7
- 2.3 - 2.3
env: env:
global: global:
- NOKOGIRI_USE_SYSTEM_LIBRARIES=true - NOKOGIRI_USE_SYSTEM_LIBRARIES=true
matrix: matrix:
- JEKYLL_VERSION="~> 3.8" - JEKYLL_VERSION="~> 3.8"
matrix: matrix:
include: include:
- rvm: 2.7 - rvm: 2.7
@ -15,8 +15,8 @@ matrix:
- rvm: 2.7 - rvm: 2.7
env: JEKYLL_VERSION=">= 4.0.0" env: JEKYLL_VERSION=">= 4.0.0"
before_install: before_install:
- gem update --system - gem update --system
- gem install bundler - gem install bundler
before_script: bundle update before_script: bundle update
script: script/cibuild script: script/cibuild
notifications: notifications:
@ -27,8 +27,6 @@ deploy:
secure: AqjHemFJsIxFuudjWzYbUGBpA5wpAB9Ate6oDZMGMnpaoSD2VgfkBDklyFfEh5uuHrh+rU6U9MPv4wknXVIV7gTTY18ATgzciLa+JCVjhAaSoPGSkORT15/kKpVp4IXIoEytj5T9D+8Wc8czd/B1+lMGGu7n7d7gMvg0HsjOLzJtAOYPrRUTaTXDLeIA+rPnsI1IzbvfHzcZvuD70XkBJWN9kiu0djlI6o51XQNumWMJrFAD/NDD3h3tZ0kkI0TalAYbWVRkZ/ZeABKAod3IRAXGt4L2MM2eYqE5KaXb/GE5wISib3I/iTCjjwrNlM+wM9a+mnOkC+elaCJm1LENqP5Ocy9wbOLYmC8i1VpPDXm2bskbj32oy1wf5zeQUf6bnPB+wDmwgCirYb7z2jQlV4BzRRkDCTftfNTa8FIi03kf+i7phjHuj18j/JC3Ww1ApRq71JAuqEnUY0wBaaN3M5abrJsYOxCRnYVPcBn/w8gfkXuhv9xvDG2OgwqIjDiECPjmbeK8Apo9kgKSrfjBQ43q62Ore6SuVCS+PZOOxVnHmfphfT4xc1atyVeMLkSvnWOa/sWTwgOSXqt1TYAEEhb734AsfXeRMzxU/LN1Y4nZ0otuEv+HVHA/XeHLA1Skq9vHhtZaORhJ58Jmwv5oMwA8KC/wPrn0gGtSBPe8zxw= secure: AqjHemFJsIxFuudjWzYbUGBpA5wpAB9Ate6oDZMGMnpaoSD2VgfkBDklyFfEh5uuHrh+rU6U9MPv4wknXVIV7gTTY18ATgzciLa+JCVjhAaSoPGSkORT15/kKpVp4IXIoEytj5T9D+8Wc8czd/B1+lMGGu7n7d7gMvg0HsjOLzJtAOYPrRUTaTXDLeIA+rPnsI1IzbvfHzcZvuD70XkBJWN9kiu0djlI6o51XQNumWMJrFAD/NDD3h3tZ0kkI0TalAYbWVRkZ/ZeABKAod3IRAXGt4L2MM2eYqE5KaXb/GE5wISib3I/iTCjjwrNlM+wM9a+mnOkC+elaCJm1LENqP5Ocy9wbOLYmC8i1VpPDXm2bskbj32oy1wf5zeQUf6bnPB+wDmwgCirYb7z2jQlV4BzRRkDCTftfNTa8FIi03kf+i7phjHuj18j/JC3Ww1ApRq71JAuqEnUY0wBaaN3M5abrJsYOxCRnYVPcBn/w8gfkXuhv9xvDG2OgwqIjDiECPjmbeK8Apo9kgKSrfjBQ43q62Ore6SuVCS+PZOOxVnHmfphfT4xc1atyVeMLkSvnWOa/sWTwgOSXqt1TYAEEhb734AsfXeRMzxU/LN1Y4nZ0otuEv+HVHA/XeHLA1Skq9vHhtZaORhJ58Jmwv5oMwA8KC/wPrn0gGtSBPe8zxw=
gem: jekyll-spaceship gem: jekyll-spaceship
on: on:
rvm: 2.3
tags: true tags: true
repo: jeffreytse/jekyll-spaceship repo: jeffreytse/jekyll-spaceship
edge: true
cleanup: false cleanup: false

411
README.md
View File

@ -64,8 +64,8 @@
<div align="center"> <div align="center">
<h4> <h4>
<a href="#requirements">Requirements</a> |
<a href="#installation">Install</a> | <a href="#installation">Install</a> |
<a href="#configuration">Config</a> |
<a href="#usage">Usage</a> | <a href="#usage">Usage</a> |
<a href="#credits">Credits</a> | <a href="#credits">Credits</a> |
<a href="#license">License</a> <a href="#license">License</a>
@ -80,10 +80,10 @@
<br> <br>
Spaceship is a minimalistic, powerful and extremely customizable [Jekyll](https://jekyllrb.com/) plugin. It combines everything you may need for convenient work, without unnecessary complications, like a real spaceship. A Jekyll plugin to provide powerful supports for table, mathjax, plantuml, emoji, youtube, vimeo, dailymotion, etc.
<p align="center"> <p align="center">
<img alt="Jekyll Spaceship Demo" src="https://user-images.githubusercontent.com/9413601/82250463-15451780-997e-11ea-8809-f411586d9508.gif" /> <img alt="Jekyll Spaceship Demo" src="https://user-images.githubusercontent.com/9413601/82250463-15451780-997e-11ea-8809-f411586d9508.gif" alt="Donate (Ko-fi)" />
</p> </p>
**💡 Tip:** I hope you enjoy using this plugin. If you like this project, **a little star** for it is your way make a clear statement: **My work is valued**. I would appreciate your support! _Thank you!_ **💡 Tip:** I hope you enjoy using this plugin. If you like this project, **a little star** for it is your way make a clear statement: **My work is valued**. I would appreciate your support! _Thank you!_
@ -93,7 +93,6 @@ Spaceship is a minimalistic, powerful and extremely customizable [Jekyll](https:
- [Requirements](#requirements) - [Requirements](#requirements)
- [Installation](#installation) - [Installation](#installation)
- [Configuration](#configuration)
- [Usage](#usage) - [Usage](#usage)
- [1. Table Usage](#1-table-usage) - [1. Table Usage](#1-table-usage)
- [1.1 Rowspan and Colspan](#rowspan-and-colspan) - [1.1 Rowspan and Colspan](#rowspan-and-colspan)
@ -101,26 +100,18 @@ Spaceship is a minimalistic, powerful and extremely customizable [Jekyll](https:
- [1.3 Headerless](#headerless) - [1.3 Headerless](#headerless)
- [1.4 Cell Alignment](#cell-alignment) - [1.4 Cell Alignment](#cell-alignment)
- [1.5 Cell Markdown](#cell-markdown) - [1.5 Cell Markdown](#cell-markdown)
- [1.6 Cell Inline Attributes](#cell-inline-attributes)
- [2. MathJax Usage](#2-mathjax-usage) - [2. MathJax Usage](#2-mathjax-usage)
- [2.1 Performance Optimization](#21-performance-optimization) - [2.1 Performance Optimization](#21-performance-optimization)
- [2.2 How to use?](#22-how-to-use) - [2.2 How to use?](#22-how-to-use)
- [3. PlantUML Usage](#3-plantuml-usage) - [3. PlantUML Usage](#3-plantuml-usage)
- [4. Mermaid Usage](#4-mermaid-usage) - [4. Video Usage](#4-video-usage)
- [5. Media Usage](#5-media-usage) - [4.1 Youtube Usage](#youtube-usage)
- [5.1 Youtube Usage](#youtube-usage) - [4.2 Vimeo Usage](#vimeo-usage)
- [5.2 Vimeo Usage](#vimeo-usage) - [4.3 DailyMotion Usage](#dailymotion-usage)
- [5.3 DailyMotion Usage](#dailymotion-usage) - [5. Hybrid HTML with Markdown](#5-hybrid-html-with-markdown)
- [5.4 Spotify Usage](#spotify-usage) - [6. Markdown Polyfill](#6-markdown-polyfill)
- [5.5 SoundCloud Usage](#soundcloud-usage) - [6.1 Escape Ordered List](#escape-ordered-list)
- [5.6 General Video Usage](#general-video-usage) - [7. Emoji Usage](#7-emoji-usage)
- [5.7 General Audio Usage](#general-audio-usage)
- [6. Hybrid HTML with Markdown](#6-hybrid-html-with-markdown)
- [7. Markdown Polyfill](#7-markdown-polyfill)
- [7.1 Escape Ordered List](#71-escape-ordered-list)
- [8. Emoji Usage](#8-emoji-usage)
- [8.1 Emoji Customizing](#81-emoji-customizing)
- [9. Modifying Element Usage](#9-modifying-element-usage)
- [Credits](#credits) - [Credits](#credits)
- [Contributing](#contributing) - [Contributing](#contributing)
- [License](#license) - [License](#license)
@ -137,80 +128,14 @@ Add jekyll-spaceship plugin in your site's `Gemfile`, and run `bundle install`.
gem 'jekyll-spaceship' gem 'jekyll-spaceship'
``` ```
Add jekyll-spaceship to the `plugins:` section in your site's `_config.yml`. Add jekyll-spaceship to the `gems:` section in your site's `_config.yml`.
```yml ```yml
plugins: plugins:
- jekyll-spaceship - jekyll-spaceship
``` ```
**💡 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. **💡 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. [Click here for more information.](https://jekyllrb.com/docs/continuous-integration/github-actions/)
### Additions for Unlimited GitHub Pages
* Here is a GitHub Action named [jekyll-deploy-action](https://github.com/jeffreytse/jekyll-deploy-action) for Jekyll site deployment conveniently. 👍
* Here is a [Jekyll site](https://github.com/jeffreytse/jekyll-jeffreytse-blog) using Travis to build and deploy to GitHub Pages for your references.
## Configuration
This plugin runs with the following configuration options by default. Alternative settings for these options can be explicitly specified in the configuration file `_config.yml`.
```yml
# Where things are
jekyll-spaceship:
# default enabled processors
processors:
- table-processor
- mathjax-processor
- plantuml-processor
- mermaid-processor
- polyfill-processor
- media-processor
- emoji-processor
- element-processor
mathjax-processor:
src:
- https://polyfill.io/v3/polyfill.min.js?features=es6
- https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-mml-chtml.js
config:
tex:
inlineMath:
- ['$','$']
- ['\(','\)']
svg:
fontCache: 'global'
plantuml-processor:
mode: default # mode value 'pre-fetch' for fetching image at building stage
css:
class: plantuml
syntax:
code: 'plantuml!'
custom: ['@startuml', '@enduml']
src: http://www.plantuml.com/plantuml/svg/
mermaid-processor:
mode: default # mode value 'pre-fetch' for fetching image at building stage
css:
class: mermaid
syntax:
code: 'mermaid!'
custom: ['@startmermaid', '@endmermaid']
config:
theme: default
src: https://mermaid.ink/svg/
media-processor:
default:
id: 'media-{id}'
class: 'media'
width: '100%'
height: 350
frameborder: 0
style: 'max-width: 600px; outline: none;'
allow: 'encrypted-media; picture-in-picture'
emoji-processor:
css:
class: emoji
src: https://github.githubassets.com/images/icons/emoji/
```
## Usage ## Usage
@ -234,13 +159,13 @@ This feature is contributed by [pmccloghrylaing](https://github.com/pmccloghryla
```markdown ```markdown
| Stage | Direct Products | ATP Yields | | Stage | Direct Products | ATP Yields |
| -----------------: | --------------: | ---------: | | -----------------: | --------------: | ---------: |
| Glycolysis | 2 ATP || | Glycolysis | 2 ATP | |
| ^^ | 2 NADH | 3--5 ATP | | ^^ | 2 NADH | 3--5 ATP |
| Pyruvaye oxidation | 2 NADH | 5 ATP | | Pyruvaye oxidation | 2 NADH | 5 ATP |
| Citric acid cycle | 2 ATP || | Citric acid cycle | 2 ATP ||
| ^^ | 6 NADH | 15 ATP | | ^^ | 6 NADH | 15 ATP |
| ^^ | 2 FADH | 3 ATP | | ^^ | 2 FADH | 3 ATP |
| 30--32 ATP ||| | 30--32 ATP |||
``` ```
Code above would be parsed as: Code above would be parsed as:
@ -441,7 +366,7 @@ Table cell can be set alignment separately.
```markdown ```markdown
| : Fruits \|\| Food : ||| | : Fruits \|\| Food : |||
| :--------- | :-------- | :-------- | | :--------- | :-------- | :-------- |
| Apple | : Apple : | Apple \ | Apple | : Apple :| Apple \
| Banana | Banana | Banana \ | Banana | Banana | Banana \
| Orange | Orange | Orange | | Orange | Orange | Orange |
| : Rowspan is 4 : || How's it? | | : Rowspan is 4 : || How's it? |
@ -542,61 +467,6 @@ Rowspan is 4
</tbody> </tbody>
</table> </table>
#### Cell Inline Attributes
This feature is very useful for custom cell such as using inline style. (e.g., background, color, font)
The idea and syntax comes from the [Maruku](http://maruku.rubyforge.org/) package.
[](https://kramdown.gettalong.org/syntax.html#block-ials)
Following are some examples of attributes definitions (ALDs) and afterwards comes the syntax explanation:
```markdown
{:ref-name: #id .cls1 .cls2}
{:second: ref-name #id-of-other title="hallo you"}
{:other: ref-name second}
```
An ALD line has the following structure:
- a left brace, optionally preceded by up to three spaces,
- followed by a colon, the id and another colon,
- followed by attribute definitions (allowed characters are backslash-escaped closing braces or any character except a not escaped closing brace),
- followed by a closing brace and optional spaces until the end of the line.
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.
An inline attribute list (IAL) is used to attach attributes to another element.
Here are some examples for span IALs:
```markdown
{: #id .cls1 .cls2} <!-- #id <=> id="id", .cls1 .cls2 <=> class="cls1 cls2" -->
{: ref-name title="hallo you"}
{: ref-name class='.cls3' .cls4}
```
Here is an example for custom table cell with IAL:
```markdown
{:color-style: style="background: black;"}
{:color-style: style="color: white;"}
{:text-style: style="font-weight: 800; text-decoration: underline;"}
|: Here's an Inline Attribute Lists example :||||
| ------- | ------------------ | -------------------- | ------------------ |
|: :|: <div style="color: red;"> &lt; Normal HTML Block > </div> :|||
| ^^ | Red {: .cls style="background: orange" } |||
| ^^ IALs | Green {: #id style="background: green; color: white" } |||
| ^^ | Blue {: style="background: blue; color: white" } |||
| ^^ | Black {: color-style text-style } |||
```
Code above would be parsed as:
<img width="580px" src="https://user-images.githubusercontent.com/9413601/88461592-738afb00-ced7-11ea-9aac-3179023742b0.png" alt="IALs">
Additionally, [here](https://kramdown.gettalong.org/syntax.html#block-ials) you can learn more details about IALs.
### 2. MathJax Usage ### 2. MathJax Usage
[MathJax](http://www.mathjax.org/) is an open-source JavaScript display engine for LaTeX, MathML, and AsciiMath notation that works in all modern browsers. [MathJax](http://www.mathjax.org/) is an open-source JavaScript display engine for LaTeX, MathML, and AsciiMath notation that works in all modern browsers.
@ -631,26 +501,22 @@ $ 2^{\frac{n-1}{3}} $
$ \int\_a^b f(x)\,dx. $ $ \int\_a^b f(x)\,dx. $
``` ```
Code above would be parsed as:
<image alt="MathJax Expression" height="180" src="https://user-images.githubusercontent.com/9413601/82814245-5a5ed180-9ec9-11ea-9d5b-fba303c627ac.png"></image>
### 3. PlantUML Usage ### 3. PlantUML Usage
[PlantUML](https://plantuml.com) is a component that allows to quickly write: [PlantUML](http://plantuml.sourceforge.net/) is a component that allows to quickly write:
- sequence diagram, - sequence diagram,
- use case diagram, - use case diagram,
- class diagram, - class diagram,
- activity diagram, - activity diagram,
- component diagram, - component diagram,
- state diagram, - state diagram
- object diagram - object diagram
There are two ways to create a diagram in your Jekyll blog page: There are two ways to create a diagram in your Jekyll blog page:
```` ````markdown
```plantuml! ```plantuml
Bob -> Alice : hello world Bob -> Alice : hello world
``` ```
```` ````
@ -663,76 +529,24 @@ Bob -> Alice : hello
@enduml @enduml
``` ```
Code above would be parsed as: ### 4. Video Usage
![PlantUML Diagram](https://user-images.githubusercontent.com/9413601/82813883-9180b300-9ec8-11ea-8778-f450e0056938.png) How often did you find yourself googling "**How to embed a video in markdown?**"
### 4. Mermaid Usage 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.
[Mermaid](https://mermaid-js.github.io/) is a Javascript based diagramming and charting tool. It generates diagrams flowcharts and more, using markdown-inspired text for ease and speed. **For now, these video links parsing are provided:**
It allows to quickly write:
- flow chart,
- pie chart,
- sequence diagram,
- class diagram,
- state diagram,
- entity relationship diagram,
- user journey,
- gantt
There are two ways to create a diagram in your Jekyll blog page:
````
```mermaid!
pie title Pets adopted by volunteers
"Dogs" : 386
"Cats" : 85
"Rats" : 35
```
````
or
```markdown
@startmermaid
pie title Pets adopted by volunteers
"Dogs" : 386
"Cats" : 85
"Rats" : 35
@endmermaid
```
Code above would be parsed as:
![Mermaid Diagram](https://user-images.githubusercontent.com/9413601/85282355-2e317300-b4be-11ea-9c30-8f9d61540d14.png)
### 5. Media Usage
How often did you find yourself googling "**How to embed a video/audio in markdown?**"
While its not possible to embed a video/audio in markdown, the best and easiest
way is to extract a frame from the video/audio. To add videos/audios to your
markdown files easier I developped this tool for you, and it will parse the
video/audio link inside the image block automatically.
**For now, these media links parsing are provided:**
- Youtube - Youtube
- Vimeo - Vimeo
- DailyMotion - DailyMotion
- Spotify
- SoundCloud
- General Video ( mp4 | avi | ogg | ogv | webm | 3gp | flv | mov ... )
- General Audio ( mp3 | wav | ogg | mid | midi | aac | wma ... )
There are two ways to embed a video/audio in your Jekyll blog page: There are two ways to embed a video in your Jekyll blog page:
Inline-style: Inline-style:
```markdown ```markdown
![]({media-link}) ![]({video-link})
``` ```
Reference-style: Reference-style:
@ -740,15 +554,17 @@ Reference-style:
```markdown ```markdown
![][{reference}] ![][{reference}]
[{reference}]: {media-link} [{reference}]: {video-link}
``` ```
For configuring media attributes (e.g, width, height), just adding query string to For configuring video attributes (e.g, width, height), just adding query string to
the link as below: the link as below:
```markdown ```markdown
![](https://www.youtube.com/watch?v=Ptk_1Dc2iPY?width=800&height=500) ![](https://www.youtube.com/watch?v=Ptk_1Dc2iPY?width=800&height=500)
```
```markdown
![](https://www.dailymotion.com/video/x7tfyq3?width=100%&height=400&autoplay=1) ![](https://www.dailymotion.com/video/x7tfyq3?width=100%&height=400&autoplay=1)
``` ```
@ -756,7 +572,9 @@ the link as below:
```markdown ```markdown
![](https://www.youtube.com/watch?v=Ptk_1Dc2iPY) ![](https://www.youtube.com/watch?v=Ptk_1Dc2iPY)
```
```markdown
![](//www.youtube.com/watch?v=Ptk_1Dc2iPY?width=800&height=500) ![](//www.youtube.com/watch?v=Ptk_1Dc2iPY?width=800&height=500)
``` ```
@ -764,7 +582,9 @@ the link as below:
```markdown ```markdown
![](https://vimeo.com/263856289) ![](https://vimeo.com/263856289)
```
```markdown
![](https://vimeo.com/263856289?width=500&height=320) ![](https://vimeo.com/263856289?width=500&height=320)
``` ```
@ -772,47 +592,13 @@ the link as below:
```markdown ```markdown
![](https://www.dailymotion.com/video/x7tfyq3) ![](https://www.dailymotion.com/video/x7tfyq3)
```
```markdown
![](https://dai.ly/x7tgcev?width=100%&height=400) ![](https://dai.ly/x7tgcev?width=100%&height=400)
``` ```
#### Spotify Usage ### 5. Hybrid HTML with Markdown
```markdown
![](http://open.spotify.com/track/4Dg5moVCTqxAb7Wr8Dq2T5)
```
<image width="600" src="https://user-images.githubusercontent.com/9413601/89762618-5d11b000-db23-11ea-81db-35cc3682b234.png">
#### SoundCloud Usage
```markdown
![](https://soundcloud.com/aviciiofficial/preview-avicii-vs-lenny)
```
<image width="600" src="https://user-images.githubusercontent.com/9413601/89762969-1c666680-db24-11ea-97e3-4340f7fac7ac.png">
#### General Video Usage
```markdown
![](//www.html5rocks.com/en/tutorials/video/basics/devstories.webm)
![](//techslides.com/demos/sample-videos/small.ogv?allow=autoplay)
![](//techslides.com/demos/sample-videos/small.mp4?width=400)
```
#### General Audio Usage
```markdown
![](//www.soundhelix.com/examples/mp3/SoundHelix-Song-1.mp3)
![](//www.soundhelix.com/examples/mp3/SoundHelix-Song-1.mp3?autoplay=1&loop=1)
```
<image width="300" src="https://user-images.githubusercontent.com/9413601/89762143-68181080-db22-11ea-8467-e8b2a8a96ae5.png">
### 6. Hybrid HTML with Markdown
As markdown is not only a lightweight markup language with plain-text-formatting syntax, but also an easy-to-read and easy-to-write plain text format, so writing a hybrid HTML with markdown is an awesome choice. As markdown is not only a lightweight markup language with plain-text-formatting syntax, but also an easy-to-read and easy-to-write plain text format, so writing a hybrid HTML with markdown is an awesome choice.
@ -826,7 +612,7 @@ It's easy to write markdown inside HTML:
| : Fruits \|\| Food : ||| | : Fruits \|\| Food : |||
| :--------- | :-------- | :-------- | | :--------- | :-------- | :-------- |
| Apple | : Apple : | Apple \ | Apple | : Apple :| Apple \
| Banana | Banana | Banana \ | Banana | Banana | Banana \
| Orange | Orange | Orange | | Orange | Orange | Orange |
| : Rowspan is 4 : || How's it? | | : Rowspan is 4 : || How's it? |
@ -846,7 +632,7 @@ Bob -> Alice : hello
</script> </script>
``` ```
### 7. Markdown Polyfill ### 6. Markdown Polyfill
It allows us to polyfill features for extending markdown syntax. It allows us to polyfill features for extending markdown syntax.
@ -854,7 +640,7 @@ It allows us to polyfill features for extending markdown syntax.
- Escape ordered list - Escape ordered list
#### 7.1 Escape Ordered List #### Escape Ordered List
A backslash at begin to escape the ordered list. A backslash at begin to escape the ordered list.
@ -888,7 +674,7 @@ Escaped:
10. List item Cafe. 10. List item Cafe.
``` ```
### 8. Emoji Usage ### 7. Emoji Usage
GitHub-flavored emoji images and names would allow emojifying content such as: it's raining :cat:s and :dog:s! GitHub-flavored emoji images and names would allow emojifying content such as: it's raining :cat:s and :dog:s!
Noted that emoji images are served from the GitHub.com CDN, with a base URL of [https://github.githubassets.com](https://github.githubassets.com), which results in emoji image URLs like [https://github.githubassets.com/images/icons/emoji/unicode/1f604.png](https://github.githubassets.com/images/icons/emoji/unicode/1f604.png). Noted that emoji images are served from the GitHub.com CDN, with a base URL of [https://github.githubassets.com](https://github.githubassets.com), which results in emoji image URLs like [https://github.githubassets.com/images/icons/emoji/unicode/1f604.png](https://github.githubassets.com/images/icons/emoji/unicode/1f604.png).
@ -903,121 +689,12 @@ I give this plugin two :+1:!
I give this plugin two :+1:! I give this plugin two :+1:!
#### 8.1 Emoji Customizing
If you'd like to serve emoji images locally, or use a custom emoji source, you can specify so in your `_config.yml` file:
```yml
jekyll-spaceship:
emoji-processor:
src: "/assets/images/emoji"
```
See the [Gemoji](https://github.com/github/gemoji) documentation for generating image files.
### 9. Modifying Element Usage
It allows us to modify elements via `CSS3 selectors`. Through it you can easily
modify the attributes of an element tag, replace the children nodes and so on,
it's very flexible, but here is example usage for modifying a document:
```yml
# Here is a comprehensive example
jekyll-spaceship:
element-processor:
css:
- a: '<h1>Test</h1>' # Replace all `a` tags (String Style)
- ['a.link1', 'a.link2']: # Replace all `a.link1`, `a.link2` tags (Hash Style)
name: img # Replace element tag name
props: # Replace element properties
title: Good image # Add a title attribute
src: ['(^.*$)', '\0?a=123'] # Add query string to src attribute by regex pattern
style: # Add style attribute (Hash Style)
color: red
font-size: '1.2em'
children: # Add children to the element
- # First empty for adding after the last child node
- "<span>Google</span>" # First child node (String Style)
- # Middle empty for wrapping the children nodes
- name: span # Second child node (Hash Style)
props:
prop1: "1" # Custom property1
prop2: "2" # Custom property2
prop3: "3" # Custom property3
children: # Add nested chidren nodes
- "<span>Jekyll</span>" # First child node (String Style)
- name: span # Second child node (Hash Style)
props: # Add attributes to child node (Hash Style)
prop1: "a"
prop2: "b"
prop3: "c"
children: "<b>Yap!</b>" # Add children nodes (String Style)
- # Last empty for adding before the first child node
- a.link: '<a href="//t.com">Link</a>' # Replace all `a.link` tags (String Style)
- 'h1#title': # Replace `h1#title` tags (Hash Style)
children: I'm a title! # Replace inner html to new text
```
#### Example 1
Automatically adds a `target="_blank" rel="noopener noreferrer"` attribute to all external links in Jekyll's content.
```yml
jekyll-spaceship:
element-processor:
css:
- a: # Replace all `a` tags
props:
class: ['(^.*$)', '\0 ext-link'] # Add `ext-link` to class by regex pattern
target: _blank # Replace `target` value to `_blank`
rel: noopener noreferrer # Replace `rel` value to `noopener noreferrer`
```
#### Example 2
Automatically adds `loading="lazy"` to `img` and `iframe` tags to natively load lazily.
[Browser support](https://caniuse.com/#feat=loading-lazy-attr) is growing. If a browser does not support the `loading` attribute, it will load the resource just like it would normally.
```yml
jekyll-spaceship:
element-processor:
css:
- a: # Replace all `a` tags
props: #
loading: lazy # Replace `loading` value to `lazy`
```
In case you want to prevent loading some images/iframes lazily, add
`loading="eager"` to their tags. This might be useful to prevent flickering of
images during navigation (e.g. the site's logo).
See the following examples to prevent lazy loading.
```yml
jekyll-spaceship:
element-processor:
css:
- a: # Replace all `a` tags
props: #
loading: eager # Replace `loading` value to `eager`
```
There are three options when using this method to lazy load images. Here are the supported values for the loading attribute:
- auto: Default lazy-loading behavior of the browser, which is the same as not including the attribute.
- lazy: Defer loading of the resource until it reaches a calculated distance from the viewport.
- eager: Load the resource immediately, regardless of where its located on the page.
## Credits ## Credits
- [Jekyll](https://github.com/jekyll/jekyll) - A blog-aware static site generator in Ruby. - [Jekyll](https://github.com/jekyll/jekyll) - A blog-aware static site generator in Ruby.
- [MultiMarkdown](https://fletcher.github.io/MultiMarkdown-6) - Lightweight markup processor to produce HTML, LaTeX, and more. - [MultiMarkdown](https://fletcher.github.io/MultiMarkdown-6) - Lightweight markup processor to produce HTML, LaTeX, and more.
- [markdown-it-multimd-table](https://github.com/RedBug312/markdown-it-multimd-table) - Multimarkdown table syntax plugin for markdown-it markdown parser. - [markdown-it-multimd-table](https://github.com/RedBug312/markdown-it-multimd-table) - Multimarkdown table syntax plugin for markdown-it markdown parser.
- [jmoji](https://github.com/jekyll/jemoji) - GitHub-flavored emoji plugin for Jekyll. - [jmoji](https://github.com/jekyll/jemoji) - GitHub-flavored emoji plugin for Jekyll.
- [jekyll-target-blank](https://github.com/keithmifsud/jekyll-target-blank) - Automatically opens external links in a new browser for Jekyll Pages, Posts and Docs.
- [jekyll-loading-lazy](https://github.com/gildesmarais/jekyll-loading-lazy) - Automatically adds loading="lazy" to img and iframe tags to natively load lazily.
- [mermaid](https://github.com/mermaid-js/mermaid) - Generation of diagram and flowchart from text in a similar manner as markdown.
## Contributing ## Contributing

View File

@ -9,7 +9,7 @@ Gem::Specification.new do |spec|
spec.version = Jekyll::Spaceship::VERSION spec.version = Jekyll::Spaceship::VERSION
spec.authors = ["jeffreytse"] spec.authors = ["jeffreytse"]
spec.email = ["jeffreytse.mail@gmail.com"] spec.email = ["jeffreytse.mail@gmail.com"]
spec.summary = "A Jekyll plugin to provide powerful supports for table, mathjax, plantuml, mermaid, emoji, video, audio, youtube, vimeo, dailymotion, spotify, soundcloud, etc." spec.summary = "A Jekyll plugin to provide powerful supports for table, mathjax, plantuml, emoji, youtube, vimeo, dailymotion, etc."
spec.homepage = "https://github.com/jeffreytse/jekyll-spaceship" spec.homepage = "https://github.com/jeffreytse/jekyll-spaceship"
spec.license = "MIT" spec.license = "MIT"
@ -20,7 +20,6 @@ Gem::Specification.new do |spec|
spec.add_dependency "jekyll", ">= 3.6", "< 5.0" spec.add_dependency "jekyll", ">= 3.6", "< 5.0"
spec.add_dependency "nokogiri", "~> 1.6" spec.add_dependency "nokogiri", "~> 1.6"
spec.add_dependency "gemoji", "~> 3.0"
spec.add_dependency "rainbow", "~> 3.0" spec.add_dependency "rainbow", "~> 3.0"
spec.add_development_dependency "bundler" spec.add_development_dependency "bundler"

View File

@ -1,12 +1,16 @@
# frozen_string_literal: true # frozen_string_literal: true
require 'jekyll-spaceship/cores/logger' require 'jekyll-spaceship/cores/logger'
require 'jekyll-spaceship/cores/config'
require 'jekyll-spaceship/cores/manager' require 'jekyll-spaceship/cores/manager'
require 'jekyll-spaceship/cores/processor' require 'jekyll-spaceship/cores/processor'
require 'jekyll-spaceship/cores/register' require 'jekyll-spaceship/cores/register'
module Jekyll::Spaceship module Jekyll::Spaceship
Logger.display_info Logger.display_info
Config.load_config Register.use 'table-processor'
Register.use 'mathjax-processor'
Register.use 'plantuml-processor'
Register.use 'polyfill-processor'
Register.use 'video-processor'
Register.use 'emoji-processor'
end end

View File

@ -1,61 +0,0 @@
# frozen_string_literal: true
module Jekyll::Spaceship
class Config
CONFIG_NAME = 'jekyll-spaceship'
DEFAULT_CONFIG = {
'processors' => [
'table-processor',
'mathjax-processor',
'plantuml-processor',
'mermaid-processor',
'polyfill-processor',
'media-processor',
'emoji-processor',
'element-processor'
]
}
@@store = {}
def self.deep_merge(first, second)
merger = proc do |_, f, s|
if Hash === f && Hash === s
f.merge(s, &merger)
elsif Array === f && Array === s
s || f
else
[:undefined, nil, :nil].include?(s) ? f : s
end
end
first.merge(second.to_h, &merger)
end
def self.store(section, default)
return @@store[section] if default.nil?
@@store[section] = deep_merge(default, @@store[section])
end
def self.load(config = {})
config = deep_merge(
{ CONFIG_NAME => DEFAULT_CONFIG },
config
)[CONFIG_NAME]
@@store = config
self.use_processors(config)
end
def self.use_processors(config)
config['processors'].each do |processor|
Register.use processor
end
end
def self.load_config
# post load site config for `group :jekyll_plugin`
Jekyll::Hooks.register :site, :after_init do |site|
self.load(site.config)
end
end
end
end

View File

@ -15,7 +15,7 @@ module Jekyll::Spaceship
events = _register.last.uniq events = _register.last.uniq
events = events.select do |event| events = events.select do |event|
next true if event.match(/^post/) next true if event.match(/^post/)
next events.index(event.to_s.gsub(/^pre/, 'post').to_sym).nil? next !events.any?(event.to_s.gsub(/^pre/, 'post').to_sym)
end end
events.each do |event| events.each do |event|
self.hook container, event self.hook container, event
@ -69,8 +69,7 @@ module Jekyll::Spaceship
end end
def self.ext(page) def self.ext(page)
ext = page.path.match(/\.[^.]+$/) page.data['ext']
ext.to_s.rstrip
end end
def self.output_ext(page) def self.output_ext(page)
@ -113,7 +112,7 @@ module Jekyll::Spaceship
end end
node.replace Nokogiri::HTML.fragment content node.replace Nokogiri::HTML.fragment content
end end
page.output = Processor.escape_html doc.to_html page.output = doc.to_html
end end
end end
end end

View File

@ -18,7 +18,6 @@ module Jekyll::Spaceship
attr_reader :page attr_reader :page
attr_reader :logger attr_reader :logger
attr_reader :config
attr_reader :priority attr_reader :priority
attr_reader :registers attr_reader :registers
attr_reader :exclusions attr_reader :exclusions
@ -28,21 +27,11 @@ module Jekyll::Spaceship
self.class.name.split('::').last self.class.name.split('::').last
end end
def filename
self.name
.gsub(/([A-Z]+)([A-Z][a-z])/,'\1-\2')
.gsub(/([a-z\d])([A-Z])/,'\1-\2')
.tr("_", "-")
.downcase
end
def initialize() def initialize()
self.initialize_priority self.initialize_priority
self.initialize_register self.initialize_register
self.initialize_exclusions self.initialize_exclusions
@logger = Logger.new(self.name) @logger = Logger.new(self.name)
@config = Config.store(self.filename, self.class.config)
@handled_files = {}
end end
def initialize_priority def initialize_priority
@ -57,7 +46,7 @@ module Jekyll::Spaceship
def initialize_register def initialize_register
if @@_registers.size.zero? if @@_registers.size.zero?
self.class.register :pages, :pre_render, :post_render self.class.register :pages, :pre_render, :post_render
self.class.register :documents, :pre_render, :post_render self.class.register :posts, :pre_render, :post_render
end end
@registers = Array.new @@_registers @registers = Array.new @@_registers
@@_registers.clear @@_registers.clear
@ -65,7 +54,7 @@ module Jekyll::Spaceship
def initialize_exclusions def initialize_exclusions
if @@_exclusions.size.zero? if @@_exclusions.size.zero?
self.class.exclude :code, :math, :liquid_filter self.class.exclude :code, :block_quotes
end end
@exclusions = @@_exclusions.uniq @exclusions = @@_exclusions.uniq
@@_exclusions.clear @@_exclusions.clear
@ -83,9 +72,6 @@ module Jekyll::Spaceship
@@_exclusions = types @@_exclusions = types
end end
def self.config
end
def process? def process?
Type.html?(output_ext) or Type.markdown?(ext) Type.html?(output_ext) or Type.markdown?(ext)
end end
@ -116,19 +102,16 @@ module Jekyll::Spaceship
if self.respond_to? method if self.respond_to? method
@page.content = self.pre_exclude @page.content @page.content = self.pre_exclude @page.content
@page.content = self.send method, @page.content @page.content = self.send method, @page.content
@page.content = self.post_exclude @page.content @page.content = self.after_exclude @page.content
end end
else else
if Type.html? output_ext if Type.html? output_ext
method = 'on_handle_html' method = 'on_handle_html'
elsif Type.css? output_ext elsif css? output_ext
method = 'on_handle_css' method = 'on_handle_css'
end end
if self.respond_to? method if self.respond_to? method
@page.output = self.send method, @page.output @page.output = self.send method, @page.output
if Type.html? output_ext
@page.output = self.class.escape_html(@page.output)
end
end end
end end
end end
@ -144,93 +127,36 @@ module Jekyll::Spaceship
end end
def on_handled def on_handled
source = page.site.source file = page.path.gsub(/.*_posts\//, '')
file = page.path.sub(/^#{source}\//, '')
return if @handled_files.has_key? file
@handled_files[file] = true
logger.log file logger.log file
end end
def exclusion_regexs() def pre_exclude(content)
regexs = [] @exclusion_store = []
@exclusions.each do |type| @exclusions.each do |type|
regex = nil regex = nil
if type == :code if type == :code
regex = /(((?<!\\)`+)\s*(\w*)((?:.|\n)*?)\2)/ regex = /(`{3}\s*(\w*)((?:.|\n)*?)`{3})/
elsif type == :math
regex = /(((?<!\\)\${1,2})[^\n]*?\1)/
elsif type == :liquid_filter
regex = /((?<!\\)((\{\{[^\n]*?\}\})|(\{%[^\n]*?%\})))/
end end
regexs.push regex unless regex.nil? next if regex.nil?
end
regexs
end
def pre_exclude(content, regexs = self.exclusion_regexs())
@exclusion_store = []
regexs.each do |regex|
content.scan(regex) do |match_data| content.scan(regex) do |match_data|
match = match_data[0] match = match_data[0]
id = @exclusion_store.size id = @exclusion_store.size
content = content.sub(match, "<!JEKYLL@#{object_id}@#{id}>") content = content.gsub(match, "[//]: JEKYLL_EXCLUDE_##{id}")
@exclusion_store.push match @exclusion_store.push match
end end
end end
content content
end end
def post_exclude(content) def after_exclude(content)
while @exclusion_store.size > 0 while @exclusion_store.size > 0
match = @exclusion_store.pop match = @exclusion_store.pop
id = @exclusion_store.size id = @exclusion_store.size
content = content.sub("<!JEKYLL@#{object_id}@#{id}>", match) content = content.gsub("[//]: JEKYLL_EXCLUDE_##{id}", match)
end end
@exclusion_store = [] @exclusion_store = []
content content
end end
def self.escape_html(content)
# escape link
content.scan(/((https?:)?\/\/\S+\?[a-zA-Z0-9%\-_=\.&;]+)/) do |result|
result = result[0]
link = result.gsub('&amp;', '&')
content = content.gsub(result, link)
end
content
end
def self.fetch_img_data(url)
begin
res = Net::HTTP.get_response URI(url)
raise res.body unless res.is_a?(Net::HTTPSuccess)
content_type = res.header['Content-Type']
raise 'Unknown content type!' if content_type.nil?
content_body = res.body.force_encoding('UTF-8')
return {
'type' => content_type,
'body' => content_body
}
rescue StandardError => msg
logger.log msg
end
end
def self.make_img_tag(data)
css_class = data['class']
type = data['type']
body = data['body']
if type == 'url'
"<img class=\"#{css_class}\" src=\"#{body}\">"
elsif type.include?('svg')
body.gsub(/\<\?xml.*?\?>/, '')
.gsub(/<!--[^\0]*?-->/, '')
.sub(/<svg /, "<svg class=\"#{css_class}\" ")
else
body = Base64.encode64(body)
body = "data:#{type};base64, #{body}"
"<img class=\"#{css_class}\" src=\"#{body}\">"
end
end
end end
end end

View File

@ -1,170 +0,0 @@
# frozen_string_literal: true
require 'nokogiri'
module Jekyll::Spaceship
class ElementProcessor < Processor
priority :lowest
def self.config
{ 'css' => [] }
end
def on_handle_html(content)
return content if config['css'].size.zero?
# use nokogiri to parse html content
doc = Nokogiri::HTML(content)
# handle each css pattern
config['css'].each do |data|
data.each do |key, val|
key = [key] if key.kind_of? String
key.each do |pattern|
nodes = doc.css(pattern)
nodes.each do |element|
handle_css_pattern({
:doc => doc,
:element => element,
:data => val
})
end
self.handled = true
end
end
end
doc.to_html
end
def handle_css_pattern(data)
doc = data[:doc]
element = data[:element]
data = data[:data]
if data.kind_of? String
element.replace Nokogiri::HTML.fragment(data)
elsif data.kind_of? Hash
handle_hash_element({
:doc => doc,
:element => element,
:data => data,
})
end
end
def create_children(data)
doc = data[:doc]
data = data[:data]
root = Nokogiri::HTML.fragment("")
data = [data] unless data.kind_of? Array
data.each do |child_data|
node = self.create_element({
:doc => doc,
:data => child_data
})
next if node.nil?
unless child_data['children'].nil?
node.children = self.create_children({
:doc => doc,
:data => child_data['children']
})
end
root.add_child node
end
root.children
end
def handle_hash_element(data)
doc = data[:doc]
element = data[:element]
data = data[:data]
# set name
element.name = data['name'] unless data['name'].nil?
# set props
data['props']&.each do |prop, val|
next element.remove_attribute if val.nil?
if val.kind_of? Array
next if val.size != 2
v = element[prop]
v = '' if v.nil?
val = v.sub(/#{val[0]}/, val[1])
elsif val.kind_of? Hash
result = []
val.each { |k, v| result.push "#{k}: #{v}" }
val = result.join(";")
end
element.set_attribute(prop, val)
end
# processing children
return unless data.has_key?('children')
return element.inner_html = "" if data['children'].nil?
children = self.create_children({
:doc => doc,
:data => data['children']
})
handle_element_placement({
:data => data,
:element => element,
:children => children,
})
end
def create_element(data)
doc = data[:doc]
data = data[:data]
return if data.nil?
return Nokogiri::HTML.fragment(data) if data.kind_of? String
return if data['name'].nil?
# create node
node = doc.create_element(data['name'])
# set props
data['props']&.each do |prop, val|
if val.kind_of? Hash
result = []
val.each { |k, v| result.push "#{k}: #{v}" }
val = result.join(";")
end
node.set_attribute(prop, val)
end
node
end
def handle_element_placement(data)
element = data[:element]
children = data[:children]
data = data[:data]
# replace whole inner html
unless data['children'].kind_of? Array
return element.inner_html = children
end
if element.children.size.zero?
return element.inner_html = children
end
index = data['children'].index(nil)
rindex = data['children'].rindex { |item| !item.nil? }
if index.nil?
element.inner_html = children
elsif index == 0 # insert to the end of children
element.children.last.after(children)
elsif index == rindex + 1 # insert to the begin of children
element.children.first.before children
else # wrap the children
element.children.first.before children[0..index]
element.children.last.after children[index..children.size]
end
end
end
end

View File

@ -1,46 +1,65 @@
# frozen_string_literal: true # frozen_string_literal: true
require 'net/http' require 'net/http'
require 'uri'
require 'json' require 'json'
require 'gemoji'
module Jekyll::Spaceship module Jekyll::Spaceship
class EmojiProcessor < Processor class EmojiProcessor < Processor
def self.config EMOJI_MARKUP_HOST = 'https://api.github.com/emojis'
{ EMOJI_MARKUP_DATA = {}
'css' => {
'class' => 'emoji' def initialize
}, super()
'src' => 'https://github.githubassets.com/images/icons/emoji/' self.initialize_emoji_data
} end
def initialize_emoji_data
EMOJI_MARKUP_DATA.update get_emoji_markup_data
end end
def on_handle_html(content) def on_handle_html(content)
return content if EMOJI_MARKUP_DATA.size.zero?
# handle emoji markup # handle emoji markup
content.scan(/:([\w\d+-]+):/) do |match| content.scan(/:([\w+-]+):/) do |match_data|
emoji = Emoji.find_by_alias match[0] emoji_markup = match_data[0]
next if emoji.nil? emoji_image = EMOJI_MARKUP_DATA[emoji_markup]
next if emoji_image.nil?
self.handled = true self.handled = true
# escape plus sign # convert hex string to unicode
emoji_name = emoji.name.gsub('+', '\\\+') unicode = emoji_image.match(/unicode\/([\d\w]+)/)
css_class = self.config['css']['class'] if unicode[1]
unicode = "0x#{unicode[1]}".to_i(16)
alt = [unicode].pack('U*')
end
alt = emoji_markup if alt.nil?
content = content.gsub( content = content.gsub(
/(?<!\=")\s*:#{emoji_name}:\s*(?!"\s)/, ":#{emoji_markup}:",
"<img class=\"#{css_class}\""\ "<image class=\"emoji\" \
" title=\":#{emoji.name}:\""\ title=\"#{emoji_markup}\" \
" alt=\":#{emoji.name}:\""\ alt=\"#{alt}\" \
" raw=\"#{emoji.raw}\""\ src=\"#{emoji_image}\" \
" src=\"#{config['src']}#{emoji.image_filename}\""\ style=\"vertical-align: middle; \
" style=\"vertical-align: middle; display: inline;"\ max-width: 1em; visibility: hidden;\" \
" max-width: 1em; visibility: hidden;\""\ onload=\"this.style.visibility='visible'\" \
" onload=\"this.style.visibility='visible'\""\ onerror=\"this.replaceWith(this.alt)\"> \
" onerror=\"this.replaceWith(this.getAttribute('raw'))\">"\ </image>"
"</img>"
) )
end end
content content
end end
def get_emoji_markup_data
data = {}
begin
source = Net::HTTP.get URI(EMOJI_MARKUP_HOST)
data = JSON.parse(source)
rescue StandardError => msg
logger.log msg
end
data
end
end end
end end

View File

@ -4,19 +4,6 @@ require "nokogiri"
module Jekyll::Spaceship module Jekyll::Spaceship
class MathjaxProcessor < Processor class MathjaxProcessor < Processor
def self.config
{
'src' => [
'https://polyfill.io/v3/polyfill.min.js?features=es6',
'https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-mml-chtml.js',
],
'config' => {
'tex' => { 'inlineMath' => [['$','$'], ['\\(','\\)']] },
'svg': { 'fontCache': 'global' }
}
}
end
def process? def process?
return true if Type.html?(output_ext) return true if Type.html?(output_ext)
end end
@ -31,15 +18,13 @@ module Jekyll::Spaceship
self.handled = true self.handled = true
# add mathjax config params = "config=TeX-AMS-MML_HTMLorMML"
cfg = config['config'].to_json src = "//cdn.mathjax.org/mathjax/latest/MathJax.js?#{params}"
head.add_child("<script>MathJax=#{cfg}</script>") config = "MathJax.Hub.Config({ \
tex2jax: { inlineMath: [['$','$'], ['\\\\(','\\\\)']] } \
});"
# add mathjax dependencies head.add_child("<script src=\"#{src}\">#{config}</script>")
config['src'] = [config['src']] if config['src'].is_a? String
config['src'].each do |src|
head.add_child("<script src=\"#{src}\"></script>")
end
doc.to_html doc.to_html
end end

View File

@ -1,234 +0,0 @@
# frozen_string_literal: true
require 'uri'
module Jekyll::Spaceship
class MediaProcessor < Processor
def self.config
{
'default' => {
'id' => 'media-{id}',
'class' => 'media',
'width' => '100%',
'height' => 350,
'frameborder' => 0,
'style' => 'max-width: 600px;outline: none',
'allow' => 'encrypted-media; picture-in-picture'
}
}
end
def on_handle_markdown(content)
content = handle_normal_audio(content)
content = handle_normal_video(content)
content = handle_youtube(content)
content = handle_vimeo(content)
content = handle_dailymotion(content)
content = handle_spotify(content)
content = handle_soundcloud(content)
end
# Examples:
# ![audio](//www.soundhelix.com/examples/mp3/SoundHelix-Song-1.mp3)
# ![audio](//www.expample.com/examples/t-rex-roar.mp3?autoplay=true&loop=true)
def handle_normal_audio(content)
handle_media(content, {
media_type: 'audio',
host: '(https?:)?\\/\\/.*\\/',
id: '(.+?\\.(mp3|wav|ogg|mid|midi|aac|wma))',
})
end
# Examples:
# ![video](//www.html5rocks.com/en/tutorials/video/basics/devstories.webm)
# ![video](//techslides.com/demos/sample-videos/small.ogv?allow=autoplay)
# ![video](//techslides.com/demos/sample-videos/small.mp4?width=400)
def handle_normal_video(content)
handle_media(content, {
media_type: 'iframe',
host: '(https?:)?\\/\\/.*\\/',
id: '(.+?\\.(avi|mp4|webm|ogg|ogv|flv|mkv|mov|wmv|3gp|rmvb|asf))'
})
end
# Examples:
# ![youtube](https://www.youtube.com/watch?v=XA2WjJbmmoM "title")
# ![youtube](http://www.youtube.com/embed/w-m_yZCLF5Q)
# ![youtube](//youtu.be/mEP3YXaSww8?height=100%&width=400)
def handle_youtube(content)
handle_media(content, {
media_type: 'iframe',
host: '(https?:)?\\/\\/.*youtu.*',
id: '(?<=\\?v\\=|embed\\/|\\.be\\/)([a-zA-Z0-9\\_\\-]+)',
base_url: "https://www.youtube.com/embed/"
})
end
# Examples:
# ![vimeo](https://vimeo.com/263856289)
# ![vimeo](https://vimeo.com/263856289?height=100%&width=400)
def handle_vimeo(content)
handle_media(content, {
media_type: 'iframe',
host: '(https?:)?\\/\\/vimeo\\.com\\/',
id: '([0-9]+)',
base_url: "https://player.vimeo.com/video/"
})
end
# Examples:
# ![dailymotion](https://www.dailymotion.com/video/x7tgcev)
# ![dailymotion](https://dai.ly/x7tgcev?height=100%&width=400)
def handle_dailymotion(content)
handle_media(content, {
media_type: 'iframe',
host: '(https?:)?\\/\\/.*dai.?ly.*',
id: '(?<=video\\/|\\/)([a-zA-Z0-9\\_\\-]+)',
base_url: "https://www.dailymotion.com/embed/video/"
})
end
# Examples:
# ![spotify](//open.spotify.com/track/4Dg5moVCTqxAb7Wr8Dq2T5)
# ![spotify](//open.spotify.com/track/37mEkAaqCE7FXMvnlVA8pp?width=400)
def handle_spotify(content)
handle_media(content, {
media_type: 'iframe',
host: '(https?:)?\\/\\/open\\.spotify\\.com\\/track\\/',
id: '(?<=track\\/)([a-zA-Z0-9\\_\\-]+)',
base_url: "https://open.spotify.com/embed/track/",
height: 80
})
end
# Examples:
# ![soundcloud](//soundcloud.com/aviciiofficial/preview-avicii-vs-lenny)
def handle_soundcloud(content)
handle_media(content, {
media_type: 'iframe',
id_from: 'html',
host: '(https?:)?\\/\\/soundcloud\\.com\\/.+\\/[^\\?]+',
id: '(?<=soundcloud:\\/\\/sounds:)([0-9]+)',
base_url: "https://w.soundcloud.com/player/?url="\
"https%3A//api.soundcloud.com/tracks/",
height: 125,
})
end
def handle_media(content, data)
host = data[:host]
return content if content.sub(/#{host}/, '').nil?
media_type = data[:media_type]
base_url = data[:base_url]
id = data[:id_from] === 'html' ? '()' : data[:id]
url = "(#{host}#{id}\\S*)"
title = '("(.*)".*){0,1}'
# pre-handle reference-style links
regex = /(\[(.*)\]:\s*(#{url}\s*#{title}))/
content.scan regex do |match_data|
match = match_data[0]
ref_name = match_data[1]
ref_value = match_data[2]
content = content.gsub(match, '')
.gsub(/\!\[(.*)\]\s*\[#{ref_name}\]/,
"![\1](#{ref_value})")
end
# handle inline-style links
regex = /(\!\[(.*)\]\(.*#{url}\s*#{title}\))/
content.scan regex do |match_data|
url = match_data[2]
id = data[:id_from] === 'html' \
? get_id_from_html(url, data[:id]) \
: match_data[4]
title = match_data[6]
qs = url.match(/(?<=\?)(\S*?)$/)
qs = Hash[URI.decode_www_form(qs.to_s)].reject do |k, v|
next true if v == id or v == ''
end
cfg = self.config['default'].clone
cfg['id'] = qs['id'] || cfg['id']
cfg['class'] = qs['class'] || cfg['class']
cfg['style'] = qs['style'] || cfg['style']
cfg['id'] = cfg['id'].gsub('{id}', id)
cfg['class'] = cfg['class'].gsub('{id}', id)
cfg['src'] = URI(base_url ? "#{base_url}#{id}" : url).tap do |v|
v.query = URI.encode_www_form(qs) if qs.size > 0
end
case media_type
when 'audio'
cfg['autoplay'] = qs['autoplay'] || data[:autoplay] || cfg['autoplay']
cfg['loop'] = qs['loop'] || data[:loop] || cfg['loop']
cfg['style'] += ';display: none;' if qs['hidden']
content = handle_audio(content, { target: match_data[0], cfg: cfg })
when 'iframe'
cfg['title'] = title
cfg['width'] = qs['width'] || data[:width] || cfg['width']
cfg['height'] = qs['height'] || data[:height] || cfg['height']
cfg['frameborder'] = qs['frameborder'] || cfg['frameborder']
cfg['allow'] ||= cfg['allow']
content = handle_iframe(content, { target: match_data[0], cfg: cfg })
end
self.handled = true
end
content
end
def handle_audio(content, data)
cfg = data[:cfg]
html = "<audio"\
" id=\"#{cfg['id']}\""\
" class=\"#{cfg['class']}\""\
" #{cfg['autoplay'] ? 'autoplay' : ''}"\
" #{cfg['loop'] ? 'loop' : ''}"\
" src=\"#{cfg['src']}\""\
" style=\"#{cfg['style']}\""\
" controls>" \
"<p> Your browser doesn't support HTML5 audio."\
" Here is a <a href=\"#{cfg['src']}\">link to download the audio</a>"\
"instead. </p>"\
"</audio>"
content.gsub(data[:target], html)
end
def handle_iframe(content, data)
cfg = data[:cfg]
html = "<iframe"\
" id=\"#{cfg['id']}\""\
" class=\"#{cfg['class']}\""\
" src=\"#{cfg['src']}\""\
" title=\"#{cfg['title']}\""\
" width=\"#{cfg['width']}\""\
" height=\"#{cfg['height']}\""\
" style=\"#{cfg['style']}\""\
" allow=\"#{cfg['allow']}\""\
" frameborder=\"#{cfg['frameborder']}\""\
" allowfullscreen>"\
"</iframe>"
content.gsub(data[:target], html)
end
def get_id_from_html(url, pattern)
id = ''
begin
url = 'https:' + url if url.start_with? '//'
res = Net::HTTP.get_response URI(url)
raise res.body unless res.is_a?(Net::HTTPSuccess)
res.body.match pattern do |match_data|
id = match_data[0]
break
end
rescue StandardError => msg
data = url
logger.log msg
end
id
end
end
end

View File

@ -1,102 +0,0 @@
# frozen_string_literal: true
require "net/http"
require "base64"
module Jekyll::Spaceship
class MermaidProcessor < Processor
exclude :none
def self.config
{
'mode' => 'default',
'syntax' => {
'code' => 'mermaid!',
'custom' => ['@startmermaid', '@endmermaid']
},
'css' => {
'class' => 'mermaid'
},
'config': {
'theme' => 'default'
},
'src' => 'https://mermaid.ink/svg/'
}
end
def on_handle_markdown(content)
# match custom mermaid block and code block
syntax = self.config['syntax']
code_name = syntax['code']
custom = syntax['custom'][-2, 2]
patterns = [
/((`{3,})\s*#{code_name}((?:.|\n)*?)\2)/,
/((?<!\\)(#{custom[0]})((?:.|\n)*?)(?<!\\)(#{custom[1]}))/
]
patterns.each do |pattern|
content = handle_mermaid_block(pattern, content)
end
# handle escape custom mermaid block
content.gsub(/\\(#{custom[0]}|#{custom[1]})/, '\1')
end
def handle_mermaid_block(pattern, content)
content.scan pattern do |match|
match = match.select { |m| not m.nil? }
block = match[0]
code = match[2]
self.handled = true
content = content.gsub(
block,
handle_mermaid(code)
)
end
content
end
def handle_mermaid(code)
# encode to UTF-8
code = code.encode('UTF-8')
url = get_url(code)
# render mode
case self.config['mode']
when 'pre-fetch'
data = self.class.fetch_img_data(url)
end
if data.nil?
data = { 'type' => 'url', 'body' => url }
end
# return img tag
data['class'] = self.config['css']['class']
self.class.make_img_tag(data)
end
def get_url(code)
src = self.config['src']
# wrap code
code = {
'code' => code.gsub(/^\s*|\s*$/, ''),
'mermaid' => config['config']
}.to_json
# set default method
src += '{code}' if src.match(/\{.*\}/).nil?
# encode to base64 string
if src.include?('{code}')
code = Base64.urlsafe_encode64(code, padding: false)
return src.gsub('{code}', code)
else
raise "No supported src ! #{src}"
end
end
end
end

View File

@ -1,50 +1,29 @@
# frozen_string_literal: true # frozen_string_literal: true
require "net/http"
require "base64" require "base64"
module Jekyll::Spaceship module Jekyll::Spaceship
class PlantumlProcessor < Processor class PlantUMLProcessor < Processor
exclude :none exclude :none
def self.config PLANT_UML_HOST = 'http://www.plantuml.com/plantuml/png/'
{
'mode' => 'default',
'syntax' => {
'code' => 'plantuml!',
'custom' => ['@startuml', '@enduml']
},
'css' => {
'class' => 'plantuml'
},
'src' => 'http://www.plantuml.com/plantuml/svg/'
}
end
def on_handle_markdown(content) def on_handle_markdown(content)
# match custom plantuml block and code block # match default plantuml block and code block
syntax = self.config['syntax'] pattern = Regexp.union(
code_name = syntax['code'] /(\\?@startuml((?:.|\n)*?)@enduml)/,
custom = syntax['custom'][-2, 2] /(`{3}\s*plantuml((?:.|\n)*?)`{3})/
)
patterns = [
/((`{3,})\s*#{code_name}((?:.|\n)*?)\2)/,
/((?<!\\)(#{custom[0]})((?:.|\n)*?)(?<!\\)(#{custom[1]}))/
]
patterns.each do |pattern|
content = handle_plantuml_block(pattern, content)
end
# handle escape custom plantuml block
content.gsub(/\\(#{custom[0]}|#{custom[1]})/, '\1')
end
def handle_plantuml_block(pattern, content)
content.scan pattern do |match| content.scan pattern do |match|
match = match.select { |m| not m.nil? } match = match.select { |m| not m.nil? }
block = match[0] block = match[0]
code = match[2] code = match[1]
# skip escape default plantuml block
if block.match(/(^\\@startuml|\\@enduml$)/)
next
end
self.handled = true self.handled = true
@ -53,41 +32,35 @@ module Jekyll::Spaceship
handle_plantuml(code) handle_plantuml(code)
) )
end end
content
# handle escape default plantuml block
content.gsub(/\\(@startuml|@enduml)/, '\1')
end end
def handle_plantuml(code) def handle_plantuml(code)
# wrap plantuml code # wrap plantuml code
code = "@startuml#{code}@enduml".encode('UTF-8') code = "@startuml#{code}@enduml".encode('UTF-8')
url = self.get_url(code)
# render mode
case self.config['mode']
when 'pre-fetch'
data = self.class.fetch_img_data(url)
end
if data.nil?
data = { 'type' => 'url', 'body' => url }
end
# return img tag
data['class'] = self.config['css']['class']
self.class.make_img_tag(data)
end
def get_url(code)
src = self.config['src']
# set default method
src += '{hexcode}' if src.match(/\{.*\}/).nil?
# encode to hex string # encode to hex string
if src.include?('{hexcode}') code = '~h' + code.unpack("H*").first
code = '~h' + code.unpack("H*").first data = self.get_plantuml_img_data(code)
return src.gsub('{hexcode}', code)
else # return img tag
raise "No supported src ! #{src}" "<img src=\"#{data}\">"
end
def get_plantuml_img_data(code)
data = ''
url = "#{PLANT_UML_HOST}#{code}"
begin
data = Net::HTTP.get URI(url)
data = Base64.encode64(data)
data = "data:image/png;base64, #{data}"
rescue StandardError => msg
data = url
logger.log msg
end end
data
end end
end end
end end

View File

@ -5,23 +5,18 @@ require "nokogiri"
module Jekyll::Spaceship module Jekyll::Spaceship
class TableProcessor < Processor class TableProcessor < Processor
ATTR_LIST_PATTERN = /((?<!\\)\{:(?:([A-Za-z]\S*):)?(.*?)(?<!\\)\})/
ATTR_LIST_REFS = {}
def on_handle_markdown(content) def on_handle_markdown(content)
# pre-handle reference-style links # pre-handle reference-style links
references = {} references = {}
content.scan(/\n\s*(\[(.*)\]:\s*(\S+(\s+".*?")?))/) do |match_data| content.scan(/(\[(.*)\]:\s*(.*))/) do |match_data|
ref_name = match_data[1] ref_name = match_data[1]
ref_value = match_data[2] ref_value = match_data[2]
references[ref_name] = ref_value references[ref_name] = ref_value
end end
if references.size > 0 if references.size > 0
content.scan(/[^\n]*(?<!\\)\|[^\n]*/) do |result| content.scan(/.*(?<!\\)\|.*/) do |result|
references.each do |key, val| references.each do |key, val|
replace = result.gsub( replace = result.gsub(/\[(.*)\]\s*\[#{key}\]/, "[\1](#{val})")
/\[([^\n\]]*?)\]\s*\[#{key}\]/,
"[\1](#{val})")
next if result == replace next if result == replace
content = content.gsub(result, replace) content = content.gsub(result, replace)
end end
@ -29,37 +24,21 @@ module Jekyll::Spaceship
end end
# pre-handle row-span # pre-handle row-span
content = content.gsub(/(?<!\\)(\|[^\n]*\\\s*)\|\s*\n/, "\\1\n") content = content.gsub(/(?<!\\)(\|.*\\\s*)\|\s*\n/, "\\1\n")
# escape | and : # escape | and :
content = content.gsub(/\|(?=\|)/, '\\|') content = content.gsub(/\|(?=\|)/, '\\|')
.gsub(/\\:(?=[^\n]*?(?<!\\)\|)/, '\\\\\\\\:') .gsub(/\\:(?=.*?(?<!\\)\|)/, '\\\\\\\\:')
.gsub(/((?<!\\)\|[^\n]*?)(\\:)/, '\1\\\\\\\\:') .gsub(/((?<!\\)\|.*?)(\\:)/, '\1\\\\\\\\:')
# escape * and _ and $ etc. # escape * and _ and $ etc.
content.scan(/[^\n]*(?<!\\)\|[^\n]*/) do |result| content.scan(/.*(?<!\\)\|.*/) do |result|
# skip for math expression within pipeline
next unless result
.gsub(/((?<!\\)\${1,2})[^\n]*?\1/, '')
.match(/(?<!\\)\|/)
replace = result.gsub( replace = result.gsub(
/(?<!(?<!\\)\\)(\*|\$|\[|\(|\"|_)/, '\\\\\\\\\1') /(?<!(?<!\\)\\)(\*|\$|\[|\(|\"|_)/,
'\\\\\\\\\1')
next if result == replace next if result == replace
content = content.gsub(result, replace) content = content.gsub(result, replace)
end end
# pre-handle attribute list (AL)
ATTR_LIST_REFS.clear()
content.scan(ATTR_LIST_PATTERN) do |result|
ref = result[1]
list = result[2]
next if ref.nil?
if ATTR_LIST_REFS.has_key? ref
ATTR_LIST_REFS[ref] += list
else
ATTR_LIST_REFS[ref] = list
end
end
content content
end end
@ -71,7 +50,6 @@ module Jekyll::Spaceship
# handle each table # handle each table
doc.css('table').each do |table| doc.css('table').each do |table|
next if table.ancestors('code, pre').size > 0
rows = table.css('tr') rows = table.css('tr')
data.table = table data.table = table
data.rows = rows data.rows = rows
@ -87,7 +65,6 @@ module Jekyll::Spaceship
handle_multi_rows(data) handle_multi_rows(data)
handle_text_align(data) handle_text_align(data)
handle_rowspan(data) handle_rowspan(data)
handle_attr_list(data)
end end
end end
rows.each do |row| rows.each do |row|
@ -180,7 +157,7 @@ module Jekyll::Spaceship
if scope.table.multi_row_cells != cells and scope.table.multi_row_start if scope.table.multi_row_cells != cells and scope.table.multi_row_start
for i in 0...scope.table.multi_row_cells.count do for i in 0...scope.table.multi_row_cells.count do
multi_row_cell = scope.table.multi_row_cells[i] multi_row_cell = scope.table.multi_row_cells[i]
multi_row_cell.inner_html += "\n<br>\n#{cells[i].inner_html}" multi_row_cell.content += " \n#{cells[i].content}"
end end
row.remove row.remove
end end
@ -205,7 +182,7 @@ module Jekyll::Spaceship
span_cell = scope.table.span_row_cells[scope.row.col_index] span_cell = scope.table.span_row_cells[scope.row.col_index]
if span_cell and cell.content.match(/^\s*\^{2}/) if span_cell and cell.content.match(/^\s*\^{2}/)
cell.content = cell.content.gsub(/^\s*\^{2}/, '') cell.content = cell.content.gsub(/^\s*\^{2}/, '')
span_cell.inner_html += "\n<br>\n#{cell.inner_html}" span_cell.content += " \n#{cell.content}"
rowspan = span_cell.get_attribute('rowspan') || 1 rowspan = span_cell.get_attribute('rowspan') || 1
rowspan = rowspan.to_i + 1 rowspan = rowspan.to_i + 1
span_cell.set_attribute('rowspan', "#{rowspan}") span_cell.set_attribute('rowspan', "#{rowspan}")
@ -230,12 +207,11 @@ module Jekyll::Spaceship
align += 2 align += 2
end end
# handle text align
return if align == 0
# handle escape colon # handle escape colon
cell.content = cell.content.gsub(/\\:/, ':') cell.content = cell.content.gsub(/\\:/, ':')
# handle text align
return if align == 0
style = cell.get_attribute('style') style = cell.get_attribute('style')
if align == 1 if align == 1
align = 'text-align: left' align = 'text-align: left'
@ -254,69 +230,13 @@ module Jekyll::Spaceship
cell.set_attribute('style', style) cell.set_attribute('style', style)
end end
# Examples:
# {:ref-name: .cls1 title="hello" }
# {: #id ref-name data="world" }
# {: #id title="hello" }
# {: .cls style="color: #333" }
def handle_attr_list(data)
cell = data.cell
content = cell.inner_html
# inline attribute list(IAL) handler
ial_handler = ->(list) do
list.scan(/(\S+)=("|')(.*?)\2|(\S+)/) do |attr|
key = attr[0]
val = attr[2]
single = attr[3]
if !key.nil?
val = (cell.get_attribute(key) || '') + val
cell.set_attribute(key, val)
elsif !single.nil?
if single.start_with? '#'
key = 'id'
val = single[1..-1]
elsif single.start_with? '.'
key = 'class'
val = cell.get_attribute(key) || ''
val += (val.size.zero? ? '' : ' ') + single[1..-1]
elsif ATTR_LIST_REFS.has_key? single
ial_handler.call ATTR_LIST_REFS[single]
end
unless key.nil?
cell.set_attribute(key, val)
end
end
end
end
# handle attribute list
content.scan(ATTR_LIST_PATTERN) do |result|
ref = result[1]
list = result[2]
# handle inline attribute list
ial_handler.call list if ref.nil?
# remove attr_list
content = content.sub(result[0], '')
end
cell.inner_html = content
end
def handle_format(data) def handle_format(data)
cell = data.cell cell = data.cell
cvter = self.converter('markdown') cvter = self.converter('markdown')
return if cvter.nil? return if cvter.nil?
content = cell.inner_html content = cell.content.gsub(/(?<!\\)\|/, '\\|')
content = self.pre_exclude(content, [/(\<code.*\>.*\<\/code\>)/]) content = cvter.convert(content.strip)
.gsub(/(?<!\\)\|/, '\\|') cell.inner_html = Nokogiri::HTML.fragment(content)
.gsub(/^\s+|\s+$/, '')
.gsub(/&lt;/, '<')
.gsub(/&gt;/, '>')
content = self.post_exclude(content)
content = cvter.convert(content)
content = Nokogiri::HTML.fragment(content)
if content.children.first&.name == 'p'
content = content.children
end
cell.inner_html = content.inner_html
end end
end end
end end

View File

@ -0,0 +1,105 @@
# frozen_string_literal: true
require 'uri'
module Jekyll::Spaceship
class VideoProcessor < Processor
def on_handle_markdown(content)
content = handle_youtube(content)
content = handle_vimeo(content)
content = handle_dailymotion(content)
end
# Examples:
# ![youtube](https://www.youtube.com/watch?v=XA2WjJbmmoM "title")
# ![youtube](http://www.youtube.com/embed/w-m_yZCLF5Q)
# ![youtube](//youtu.be/mEP3YXaSww8?height=100%&width=400)
def handle_youtube(content)
handle_video(content, {
host: '(https?:)?\\/\\/.*youtu.*',
id: '(?<=\\?v\\=|embed\\/|\\.be\\/)([a-zA-Z0-9\\_\\-]+)',
iframe_url: "https://www.youtube.com/embed/"
})
end
# Examples:
# ![vimeo](https://vimeo.com/263856289)
# ![vimeo](https://vimeo.com/263856289?height=100%&width=400)
def handle_vimeo(content)
handle_video(content, {
host: '(https?:)?\\/\\/vimeo\\.com\\/',
id: '([0-9]+)',
iframe_url: "https://player.vimeo.com/video/",
width: 600,
height: 350
})
end
# Examples:
# ![dailymotion](https://www.dailymotion.com/video/x7tgcev)
# ![dailymotion](https://dai.ly/x7tgcev?height=100%&width=400)
def handle_dailymotion(content)
handle_video(content, {
host: '(https?:)?\\/\\/.*dai.?ly.*',
id: '(?<=video\\/|\\/)([a-zA-Z0-9\\_\\-]+)',
iframe_url: "https://www.dailymotion.com/embed/video/"
})
end
def handle_video(content, data)
host = data[:host]
return content if content.sub(/#{host}/, '').nil?
iframe_url = data[:iframe_url]
id = data[:id]
url = "(#{host}#{id}\\S*)"
title = '("(.*)".*){0,1}'
# pre-handle reference-style links
regex = /(\[(.*)\]:\s*(#{url}\s*#{title}))/
content.scan regex do |match_data|
match = match_data[0]
ref_name = match_data[1]
ref_value = match_data[2]
content = content.gsub(match, '')
.gsub(/\!\[(.*)\]\s*\[#{ref_name}\]/,
"![\1](#{ref_value})")
end
# handle inline-style links
regex = /(\!\[(.*)\]\(.*#{url}\s*#{title}\))/
content.scan regex do |match_data|
url = match_data[2]
id = match_data[4]
title = match_data[6]
qs = url.match(/(?<=\?)(\S*?)$/)
qs = Hash[URI.decode_www_form(qs.to_s)].reject do |k, v|
next true if v == id or v == ''
end
width = qs['width'] || data[:width] || 600
height = qs['height'] || data[:height] || 400
style = "max-width: 100%" if width.nil?
url = URI("#{iframe_url}#{id}").tap do |v|
v.query = URI.encode_www_form(qs) if qs.size > 0
end
html = "<iframe \
src=\"#{url}\" \
title=\"#{title}\" \
width=\"#{width}\" \
height=\"#{height}\" \
style=\"#{style}\" \
allow=\"autoplay; encrypted-media\" \
frameborder=\"0\" \
allowfullscreen=\"\">\
</iframe>"
content = content.gsub(match_data[0], html)
self.handled = true
end
content
end
end
end

View File

@ -2,6 +2,6 @@
module Jekyll module Jekyll
module Spaceship module Spaceship
VERSION = "0.9.3" VERSION = "0.5.2"
end end
end end

View File

@ -1,100 +0,0 @@
# This file was generated by the `rspec --init` command. Conventionally, all
# specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`.
# The generated `.rspec` file contains `--require spec_helper` which will cause
# this file to always be loaded, without a need to explicitly require it in any
# files.
#
# Given that it is always loaded, you are encouraged to keep this file as
# light-weight as possible. Requiring heavyweight dependencies from this file
# will add to the boot time of your test suite on EVERY test run, even for an
# individual file that may not need all of that loaded. Instead, consider making
# a separate helper file that requires the additional dependencies and performs
# the additional setup, and require it from the spec files that actually need
# it.
#
# See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration
RSpec.configure do |config|
# rspec-expectations config goes here. You can use an alternate
# assertion/expectation library such as wrong or the stdlib/minitest
# assertions if you prefer.
config.expect_with :rspec do |expectations|
# This option will default to `true` in RSpec 4. It makes the `description`
# and `failure_message` of custom matchers include text for helper methods
# defined using `chain`, e.g.:
# be_bigger_than(2).and_smaller_than(4).description
# # => "be bigger than 2 and smaller than 4"
# ...rather than:
# # => "be bigger than 2"
expectations.include_chain_clauses_in_custom_matcher_descriptions = true
end
# rspec-mocks config goes here. You can use an alternate test double
# library (such as bogus or mocha) by changing the `mock_with` option here.
config.mock_with :rspec do |mocks|
# Prevents you from mocking or stubbing a method that does not exist on
# a real object. This is generally recommended, and will default to
# `true` in RSpec 4.
mocks.verify_partial_doubles = true
end
# This option will default to `:apply_to_host_groups` in RSpec 4 (and will
# have no way to turn it off -- the option exists only for backwards
# compatibility in RSpec 3). It causes shared context metadata to be
# inherited by the metadata hash of host groups and examples, rather than
# triggering implicit auto-inclusion in groups with matching metadata.
config.shared_context_metadata_behavior = :apply_to_host_groups
# The settings below are suggested to provide a good initial experience
# with RSpec, but feel free to customize to your heart's content.
=begin
# This allows you to limit a spec run to individual examples or groups
# you care about by tagging them with `:focus` metadata. When nothing
# is tagged with `:focus`, all examples get run. RSpec also provides
# aliases for `it`, `describe`, and `context` that include `:focus`
# metadata: `fit`, `fdescribe` and `fcontext`, respectively.
config.filter_run_when_matching :focus
# Allows RSpec to persist some state between runs in order to support
# the `--only-failures` and `--next-failure` CLI options. We recommend
# you configure your source control system to ignore this file.
config.example_status_persistence_file_path = "spec/examples.txt"
# Limits the available syntax to the non-monkey patched syntax that is
# recommended. For more details, see:
# - http://rspec.info/blog/2012/06/rspecs-new-expectation-syntax/
# - http://www.teaisaweso.me/blog/2013/05/27/rspecs-new-message-expectation-syntax/
# - http://rspec.info/blog/2014/05/notable-changes-in-rspec-3/#zero-monkey-patching-mode
config.disable_monkey_patching!
# This setting enables warnings. It's recommended, but in some cases may
# be too noisy due to issues in dependencies.
config.warnings = true
# Many RSpec users commonly either run the entire suite or an individual
# file, and it's useful to allow more verbose output when running an
# individual spec file.
if config.files_to_run.one?
# Use the documentation formatter for detailed output,
# unless a formatter has already been configured
# (e.g. via a command-line flag).
config.default_formatter = "doc"
end
# Print the 10 slowest examples and example groups at the
# end of the spec run, to help surface which specs are running
# particularly slow.
config.profile_examples = 10
# Run specs in random order to surface order dependencies. If you find an
# order dependency and want to debug it, you can fix the order by providing
# the seed, which is printed after each run.
# --seed 1234
config.order = :random
# Seed global randomization in this process using the `--seed` CLI option.
# Setting this allows you to use `--seed` to deterministically reproduce
# test failures related to randomization by passing the same `--seed` value
# as the one that triggered the failure.
Kernel.srand config.seed
=end
end