Compare commits

...

133 Commits

Author SHA1 Message Date
jeffreytse
8ca6d144a4 release: v0.9.3 2020-10-07 18:46:06 +08:00
jeffreytse
7847cac8c4 fix: inline code corrupted in table (#27) 2020-10-07 18:41:45 +08:00
jeffreytse
6973d6ab7e docs: update README.md 2020-10-03 09:22:31 +08:00
jeffreytse
ac287926a9 release: v0.9.2 2020-09-18 12:35:17 +08:00
jeffreytse
4195a431be docs: update README.md 2020-09-16 18:40:38 +08:00
jeffreytse
e1a5bf7356 feat: support svg object for diagrams (#23,#24,#25) 2020-09-16 18:38:26 +08:00
jeffreytse
b29469b7b2 feat: add images related functions 2020-09-16 18:33:47 +08:00
jeffreytse
7d86dcbe03 docs: update README.md 2020-08-29 20:11:00 +08:00
jeffreytse
793e6591a3 chore: revise .travis.yml format 2020-08-29 20:08:23 +08:00
Dirk van Oosterbosch
9b283f62e5
docs: revise the link to the PlantUML website (#22) 2020-08-28 18:12:27 +08:00
jeffreytse
ccd9c971c1 chore: init rspec 2020-08-16 17:30:23 +08:00
jeffreytse
c15932fd45 chore: update gemspec summary 2020-08-10 16:29:48 +08:00
jeffreytse
d0e4f6d9aa release: v0.9.1 2020-08-10 16:27:34 +08:00
jeffreytse
643072fdf3 docs: update README.md 2020-08-10 16:24:28 +08:00
jeffreytse
92ee45f8b2 feat: support spotify and soundcloud 2020-08-10 11:52:51 +08:00
jeffreytse
c596868051 refactor: rename video-processor to media-processor 2020-08-09 19:06:09 +08:00
jeffreytse
de62d61a6a feat: support rendering audio link 2020-08-09 19:04:29 +08:00
jeffreytse
3852a48aba docs: update README.md 2020-08-01 11:31:24 +08:00
jeffreytse
9b7ed3a951 release: v0.9.0 2020-08-01 11:30:59 +08:00
jeffreytse
2fa6177694 feat: support mathjax v3 2020-08-01 11:22:42 +08:00
jeffreytse
c98006df64 fix: liquid filter of percent form broken (#21) 2020-07-31 21:18:16 +08:00
jeffreytse
c59b4e67f5 release: v0.8.7 2020-07-30 12:29:30 +08:00
jeffreytse
58b9d5e784 fix: wrong ext matched if path including dots 2020-07-30 12:25:48 +08:00
jeffreytse
d094789ad4 fix: link and image in same row not working (#20) 2020-07-28 11:59:32 +08:00
jeffreytse
f8f6ffe26e docs: update README.md 2020-07-26 01:05:18 +08:00
jeffreytse
44ec868188 release: v0.8.6 2020-07-26 00:47:40 +08:00
jeffreytse
2e99bcfb84 docs: update README.md 2020-07-26 00:43:56 +08:00
jeffreytse
04ff451208 feat: support table cell attribute list (#19) 2020-07-25 20:54:31 +08:00
jeffreytse
a85ec310cd fix: html inside table cell not working 2020-07-25 14:09:27 +08:00
jeffreytse
c916d10c6b release: v0.8.5 2020-07-21 10:30:25 +08:00
jeffreytse
430b992521 fix: error with permalink file extension (#18) 2020-07-21 10:23:25 +08:00
jeffreytse
e1f1aa9006 release: v0.8.4 2020-07-16 13:54:47 +08:00
jeffreytse
6d3bd946f2 fix: liquid filter tag broken issue 2020-07-16 13:05:04 +08:00
jeffreytse
8e932a4d32 fix: correct style of table cell 2020-07-16 13:00:35 +08:00
jeffreytse
006983a62e fix: emoji img css class and block display issue 2020-07-09 23:07:13 +08:00
jeffreytse
59d47ecbc8 release: v0.8.3 2020-07-04 11:39:18 +08:00
jeffreytse
4c07bdcd8d docs: update README.md 2020-07-04 11:36:18 +08:00
jeffreytse
a3a1cc2b88 fix: math expression within pipline broken (#14) 2020-07-03 13:00:32 +08:00
jeffreytse
68cc44e2bd release: v0.8.2 2020-07-02 15:48:46 +08:00
jeffreytse
53a203ff66 fix: math expression with pipeline not working 2020-07-02 15:45:09 +08:00
jeffreytse
40103470cd perf: match markdown table more exactly 2020-07-02 15:42:54 +08:00
jeffreytse
bf641a67f6 fix: table in code or pre html tag not skipped 2020-07-01 23:46:56 +08:00
jeffreytse
7b31d51d88 refactor: better maintainability 2020-06-28 12:28:40 +08:00
jeffreytse
686e24b634 release: v0.8.1 2020-06-25 12:29:05 +08:00
jeffreytse
a1810890e5 docs: update README.md 2020-06-24 14:46:29 +08:00
jeffreytse
137a0bafbf fix: css id and class with video id not working 2020-06-24 14:29:38 +08:00
jeffreytse
21e4435a74 docs: update README.md 2020-06-23 09:56:43 +08:00
jeffreytse
724e20e1a9 release: v0.8.0 2020-06-22 23:10:16 +08:00
jeffreytse
afce6b2191 chore: update gemspec summary 2020-06-22 23:07:44 +08:00
jeffreytse
ed231ba8c2 chore: change default syntax of code block 2020-06-22 23:03:49 +08:00
jeffreytse
a6842ffa27 docs: update README.md 2020-06-22 22:54:43 +08:00
jeffreytse
e1dbca31a0 refactor: revise pre-fetch mode of plantuml 2020-06-22 18:44:45 +08:00
jeffreytse
7e495e9468 refactor: revise pre-fetch mode of mermaid 2020-06-22 18:43:40 +08:00
jeffreytse
a3cc560007 feat: add mermaid-processor to default config 2020-06-22 18:07:11 +08:00
jeffreytse
425f90cdaf feat: add mermaid processor 2020-06-22 18:06:50 +08:00
jeffreytse
5f3f23dcb9 feat: add flexible config for emoji processor 2020-06-22 18:05:30 +08:00
jeffreytse
d553212c84 feat: add flexible config for video processor 2020-06-22 18:05:03 +08:00
jeffreytse
7b6cf5d5db feat: add flexible config for plantuml processor 2020-06-22 18:04:06 +08:00
jeffreytse
71563cceb6 release: v0.7.3 2020-06-22 12:31:53 +08:00
jeffreytse
a30d7e46f0 fix: load site config broke in group plugins (#12) 2020-06-22 12:16:02 +08:00
jeffreytse
b3b7d1fb15 release: v0.7.2 2020-06-21 22:34:51 +08:00
jeffreytse
5b1b41889c fix: processor default config not working 2020-06-21 22:31:59 +08:00
jeffreytse
36a58e6ab8 chore: remove unused yaml requirement 2020-06-21 18:00:19 +08:00
jeffreytse
bfcd31a493 release: v0.7.1 2020-06-19 02:04:20 +08:00
jeffreytse
ed78592816 fix: specify config files not working (#11) 2020-06-19 01:58:55 +08:00
jeffreytse
6f5a0b6912 docs: update README.md 2020-06-15 13:42:14 +08:00
jeffreytse
7b11b0a854 release: v0.7.0 2020-06-15 01:14:36 +08:00
jeffreytse
b47c942d0a docs: update README.md 2020-06-15 01:13:47 +08:00
jeffreytse
512b9a89ed fix: empty element and regex matching issue 2020-06-14 23:17:15 +08:00
jeffreytse
afd203b121 feat: add element-processor to default config 2020-06-14 22:48:36 +08:00
jeffreytse
4c26f25f7c feat: add element processor 2020-06-14 22:46:39 +08:00
jeffreytse
0d82237ad8 fix: merging default config issue 2020-06-14 22:41:57 +08:00
jeffreytse
f175be2b49 release: v0.6.3 2020-06-14 03:54:20 +08:00
jeffreytse
c1187eb633 fix: escape code block issue (#10) 2020-06-14 03:51:20 +08:00
jeffreytse
098ed91509 perfect: improve plantuml code block compatibility 2020-06-14 03:49:17 +08:00
jeffreytse
6a12fa7c95 feat: add default max width of video 2020-06-14 01:58:54 +08:00
jeffreytse
3fb0fa35d1 docs: update README.md 2020-06-13 21:21:42 +08:00
jeffreytse
4b9291d137 release: v0.6.2 2020-06-13 18:16:59 +08:00
jeffreytse
c1033d3b97 chore: update gemspec summary 2020-06-13 18:15:05 +08:00
jeffreytse
bb29d05f9b feat: support normal videos 2020-06-13 18:12:45 +08:00
jeffreytse
f5e26a0549 chore: revise emoji img tag 2020-06-13 18:02:11 +08:00
jeffreytse
72dfb0fbec perfect: refine config granularity 2020-06-13 16:06:05 +08:00
jeffreytse
9e63239cb0 feat: better video attributes 2020-06-13 15:36:45 +08:00
jeffreytse
959311a89b fix: collections are not treated (#8) 2020-06-13 15:08:19 +08:00
jeffreytse
9f41aa9f65 release: v0.6.1 2020-06-10 22:36:03 +08:00
jeffreytse
c553ccf07f fix: the cell content rendering is corrupt (#9) 2020-06-10 22:07:12 +08:00
jeffreytse
b60dc56a21 docs: update README.md 2020-05-26 14:12:03 +08:00
jeffreytse
cf8171465a release: v0.6.0 2020-05-26 13:44:48 +08:00
jeffreytse
71e58aaa54 feat: add config for processor 2020-05-26 13:38:48 +08:00
jeffreytse
68f69a9ffe feat: use processors by config 2020-05-26 13:38:32 +08:00
jeffreytse
627aeb4ff4 feat: add config core 2020-05-26 13:37:25 +08:00
jeffreytse
76f00f37dc docs: update README.md 2020-05-25 21:00:30 +08:00
jeffreytse
496ffb9294 release: v0.5.4 2020-05-21 15:26:26 +08:00
jeffreytse
9da37027fe feat: add class name for html tag 2020-05-21 15:19:33 +08:00
jeffreytse
deb98c7ed5 refactor: perfect emoji processor 2020-05-21 15:18:48 +08:00
jeffreytse
22f485381e fix: html entities issue 2020-05-21 15:16:29 +08:00
jeffreytse
64b06a6d24 fix: colspan feature is not working (#6) 2020-05-21 12:41:16 +08:00
jeffreytse
5c928bf781 ci: update travis.yml 2020-05-20 03:57:51 +08:00
jeffreytse
c563485ef2 release: v0.5.3 2020-05-20 03:47:01 +08:00
jeffreytse
5c3a2b2ce8 fix: the array method params issue 2020-05-20 03:44:58 +08:00
jeffreytse
5e3fe1639f release: v0.5.2 2020-05-19 19:38:11 +08:00
jeffreytse
6c5fa22898 perf: render plantuml on server side 2020-05-19 19:33:17 +08:00
jeffreytse
fe53c63d50 docs: update README.md 2020-05-19 12:17:01 +08:00
jeffreytse
76a96c3a4e release: v0.5.1 2020-05-18 23:47:28 +08:00
jeffreytse
006db3bbcd feat: styling terminal info output 2020-05-18 23:44:49 +08:00
jeffreytse
3feff20e20 chore: correct variable name 2020-05-18 12:46:35 +08:00
jeffreytse
24b9800405 fix: table cell colspan issue 2020-05-18 03:04:40 +08:00
jeffreytse
768cd1b73e release: v0.5.0 2020-05-17 20:04:00 +08:00
jeffreytse
c79f3fee55 feat: show unicode emoji when image error 2020-05-17 02:31:27 +08:00
jeffreytse
fd5c0c5a71 docs: update README.md 2020-05-16 20:30:27 +08:00
jeffreytse
4072a420e7 feat: add emoji processor for emoji markup 2020-05-16 19:51:06 +08:00
jeffreytse
0b8603048b perfect: logger and processor core 2020-05-16 18:10:14 +08:00
jeffreytse
d8ecaf1b25 docs: update README.md 2020-05-16 15:33:07 +08:00
jeffreytse
0605f213e4 chore: update FUNDING.yml 2020-05-16 15:09:48 +08:00
jeffreytse
5c075b4b6c docs: update README.md 2020-05-16 14:07:45 +08:00
jeffreytse
8ee3977b40 chore: update FUNDING.yml 2020-05-16 13:45:12 +08:00
jeffreytse
74d4a86219 docs: update README.md 2020-05-16 12:01:00 +08:00
jeffreytse
e7400fdb04 feat: support video autoplay in query string 2020-05-16 11:58:12 +08:00
jeffreytse
68cd5108db fix: interactions between colspan and rowspan (#5) 2020-05-15 23:58:44 +08:00
jeffreytse
12338ad541
chore: add github FUNDING.yml 2020-05-15 22:29:45 +08:00
jeffreytse
6c603374c8 chore: update jekyll-spaceship logo 2020-05-15 22:14:28 +08:00
jeffreytse
be170568de docs: update README.md 2020-05-15 22:08:42 +08:00
jeffreytse
7b13e57324 feat: add jekyll-spaceship logo 2020-05-15 20:39:14 +08:00
jeffreytse
791afd0ab9 chore: revise the table colspan code 2020-05-15 18:01:56 +08:00
jeffreytse
6f5add9f8f release: v0.4.4 2020-05-15 02:23:57 +08:00
Will Fletcher
c5644ecba5
fix: multiple colspans within same row (#4)
* Fix multiple colspans within same row

This fixes a problem whereby each spanning cell in | spancell1 || spancell2 || cell | spancell3 || would get progressively larger instead of all spanning their intended number of cells.

Resolved by maintaining a count per cell as well as a row-wide count for removing trailing cells.

* refactor: revise the colspan counting code

Co-authored-by: jeffreytse <jeffreytse.mail@gmail.com>
2020-05-15 02:12:09 +08:00
jeffreytse
5f98b542ba docs: update README.md 2020-05-14 18:41:59 +08:00
jeffreytse
4e2b64bbca release: v0.4.3 2020-05-14 10:39:19 +08:00
jeffreytse
045efd3de1 fix: text in paranthesis become interpreted (#3) 2020-05-14 10:35:59 +08:00
jeffreytse
cf97ad8d1d chore: revise table processor 2020-05-01 14:47:57 +08:00
jeffreytse
f961be49e8 feat: add codeclimate.yml 2020-05-01 14:44:37 +08:00
jeffreytse
7624339b1d refactor: improve processor core 2020-05-01 11:48:23 +08:00
jeffreytse
dd679fd4f7 ci: add configration for gem deployment 2020-04-29 17:32:15 +08:00
26 changed files with 1876 additions and 542 deletions

38
.codeclimate.yml Normal file
View File

@ -0,0 +1,38 @@
version: "2"
checks:
argument-count:
enabled: true
config:
threshold: 5
file-lines:
enabled: true
config:
threshold: 300
method-complexity:
enabled: true
config:
threshold: 15
method-count:
enabled: true
config:
threshold: 50
method-lines:
enabled: true
config:
threshold: 30
plugins:
fixme:
enabled: false
exclude_patterns:
- "*.*"
- ".*"
- Gemfile
- LICENSE.txt
- Rakefile
- rake/
- script/
- spec/
- test/

12
.github/FUNDING.yml vendored Normal file
View File

@ -0,0 +1,12 @@
# These are supported funding model platforms
github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
patreon: jeffreytse
open_collective: # Replace with a single Open Collective username
ko_fi: jeffreytse
tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
liberapay: jeffreytse
issuehunt: # Replace with a single IssueHunt username
otechie: # Replace with a single Otechie username
custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']

3
.rspec Normal file
View File

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

View File

@ -1,7 +1,7 @@
language: ruby
cache: bundler
rvm:
- &latest_ruby 2.7
- 2.7
- 2.3
env:
global:
@ -10,10 +10,10 @@ env:
- JEKYLL_VERSION="~> 3.8"
matrix:
include:
- rvm: *latest_ruby
env: JEKYLL_VERSION="~> 3.8.6"
- rvm: *latest_ruby
env: JEKYLL_VERSION=">= 4.0.0"
- rvm: 2.7
env: JEKYLL_VERSION="~> 3.8.6"
- rvm: 2.7
env: JEKYLL_VERSION=">= 4.0.0"
before_install:
- gem update --system
- gem install bundler
@ -21,3 +21,14 @@ before_script: bundle update
script: script/cibuild
notifications:
email: false
deploy:
provider: rubygems
api_key:
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
on:
rvm: 2.3
tags: true
repo: jeffreytse/jekyll-spaceship
edge: true
cleanup: false

524
README.md
View File

@ -1,16 +1,99 @@
# jekyll-spaceship
<h1 align="center">
<a href="https://github.com/jeffreytse/jekyll-spaceship">
<img alt="spaceship →~ jekyll" src="https://raw.githubusercontent.com/jeffreytse/jekyll-spaceship/master/logos/jekyll-spaceship-logo.png" width="400">
</a>
<br> 🚀 Jekyll Spaceship 🚀 <br>
</h1>
[![Build Status](https://travis-ci.org/jeffreytse/jekyll-spaceship.svg?branch=master)](https://travis-ci.org/jeffreytse/jekyll-spaceship)
[![Gem Version](https://badge.fury.io/rb/jekyll-spaceship.svg)](http://badge.fury.io/rb/jekyll-spaceship)
[![Code Climate](https://codeclimate.com/github/jeffreytse/jekyll-spaceship/badges/gpa.svg)](https://codeclimate.com/github/jeffreytse/jekyll-spaceship)
[![Test Coverage](https://api.codeclimate.com/v1/badges/cd56b207f327603662a1/test_coverage)](https://codeclimate.com/github/jeffreytse/jekyll-spaceship/test_coverage)
<h4 align="center">
<a href="https://jekyllrb.org" target="_blank"><code>Jekyll</code></a> plugin for Astronauts.
</h4>
<p align="center">
<a href="https://travis-ci.org/jeffreytse/jekyll-spaceship">
<img src="https://travis-ci.org/jeffreytse/jekyll-spaceship.svg?branch=master"
alt="CI Status" />
</a>
<a href="http://badge.fury.io/rb/jekyll-spaceship">
<img src="https://badge.fury.io/rb/jekyll-spaceship.svg"
alt="Gem Version" />
</a>
<a href="https://codeclimate.com/github/jeffreytse/jekyll-spaceship">
<img src="https://codeclimate.com/github/jeffreytse/jekyll-spaceship/badges/gpa.svg"
alt="Code Climate" />
</a>
<a href="https://codeclimate.com/github/jeffreytse/jekyll-spaceship/test_coverage">
<img src="https://api.codeclimate.com/v1/badges/cd56b207f327603662a1/test_coverage"
alt="Test Coverage" />
</a>
<a href="https://github.com/jeffreytse/jekyll-spaceship/blob/master/LICENSE.txt">
<img src="https://img.shields.io/badge/License-MIT-brightgreen.svg"
alt="License" />
</a>
<a href="http://badge.fury.io/rb/jekyll-spaceship">
<img src="https://img.shields.io/gem/dt/jekyll-spaceship"
alt="Download Data" />
</a>
</p>
<p align="center">
<a href="https://liberapay.com/jeffreytse">
<img src="http://img.shields.io/liberapay/goal/jeffreytse.svg?logo=liberapay"
alt="Donate (Liberapay)" />
</a>
<a href="https://patreon.com/jeffreytse">
<img src="https://img.shields.io/badge/support-patreon-F96854.svg?style=flat-square"
alt="Donate (Patreon)" />
</a>
<a href="https://ko-fi.com/jeffreytse">
<img height="20" src="https://www.ko-fi.com/img/githubbutton_sm.svg"
alt="Donate (Ko-fi)" />
</a>
</p>
<div align="center">
<h4>
<a href="#installation">Install</a> |
<a href="#configuration">Config</a> |
<a href="#usage">Usage</a> |
<a href="#credits">Credits</a> |
<a href="#license">License</a>
</h4>
</div>
<div align="center">
<sub>Built with ❤︎ by
<a href="https://jeffreytse.net">jeffreytse</a> and
<a href="https://github.com/jeffreytse/jekyll-spaceship/graphs/contributors">contributors </a>
</div>
<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.
<p align="center">
<img alt="Jekyll Spaceship Demo" src="https://user-images.githubusercontent.com/9413601/82250463-15451780-997e-11ea-8809-f411586d9508.gif" />
</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!_
A Jekyll plugin to provide powerful supports for table, mathjax, plantuml, youtube, etc.
## Table of Contents
- [Requirements](#requirements)
- [Installation](#installation)
- [Configuration](#configuration)
- [Usage](#usage)
- [1. Table Usage](#1-table-usage)
- [1.1 Rowspan and Colspan](#rowspan-and-colspan)
@ -18,15 +101,26 @@ A Jekyll plugin to provide powerful supports for table, mathjax, plantuml, youtu
- [1.3 Headerless](#headerless)
- [1.4 Cell Alignment](#cell-alignment)
- [1.5 Cell Markdown](#cell-markdown)
- [1.6 Cell Inline Attributes](#cell-inline-attributes)
- [2. MathJax Usage](#2-mathjax-usage)
- [2.1 Performance Optimization](#21-performance-optimization)
- [2.2 How to use?](#22-how-to-use)
- [3. PlantUML Usage](#3-plantuml-usage)
- [4. Video Usage](#4-video-usage)
- [4.1 Youtube Usage](#youtube-usage)
- [4.2 Vimeo Usage](#vimeo-usage)
- [4.3 DailyMotion Usage](#dailymotion-usage)
- [5. Hybrid HTML with Markdown](#5-hybrid-html-with-markdown)
- [6. Markdown Polyfill](#6-markdown-polyfill)
- [6.1 Escape Ordered List](#escape-ordered-list)
- [4. Mermaid Usage](#4-mermaid-usage)
- [5. Media Usage](#5-media-usage)
- [5.1 Youtube Usage](#youtube-usage)
- [5.2 Vimeo Usage](#vimeo-usage)
- [5.3 DailyMotion Usage](#dailymotion-usage)
- [5.4 Spotify Usage](#spotify-usage)
- [5.5 SoundCloud Usage](#soundcloud-usage)
- [5.6 General Video Usage](#general-video-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)
- [Contributing](#contributing)
- [License](#license)
@ -43,13 +137,81 @@ Add jekyll-spaceship plugin in your site's `Gemfile`, and run `bundle install`.
gem 'jekyll-spaceship'
```
Add jekyll-spaceship to the `gems:` section in your site's `_config.yml`.
Add jekyll-spaceship to the `plugins:` section in your site's `_config.yml`.
```yml
plugins:
- 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.
### 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
### 1. Table Usage
@ -72,13 +234,13 @@ This feature is contributed by [pmccloghrylaing](https://github.com/pmccloghryla
```markdown
| Stage | Direct Products | ATP Yields |
| -----------------: | --------------: | ---------: |
| Glycolysis | 2 ATP | |
| ^^ | 2 NADH | 3--5 ATP |
| Glycolysis | 2 ATP ||
| ^^ | 2 NADH | 3--5 ATP |
| Pyruvaye oxidation | 2 NADH | 5 ATP |
| Citric acid cycle | 2 ATP ||
| ^^ | 6 NADH | 15 ATP |
| ^^ | 2 FADH | 3 ATP |
| 30--32 ATP |||
| ^^ | 6 NADH | 15 ATP |
| ^^ | 2 FADH | 3 ATP |
| 30--32 ATP |||
```
Code above would be parsed as:
@ -279,7 +441,7 @@ Table cell can be set alignment separately.
```markdown
| : Fruits \|\| Food : |||
| :--------- | :-------- | :-------- |
| Apple | : Apple :| Apple \
| Apple | : Apple : | Apple \
| Banana | Banana | Banana \
| Orange | Orange | Orange |
| : Rowspan is 4 : || How's it? |
@ -380,11 +542,66 @@ Rowspan is 4
</tbody>
</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
[MathJax](http://www.mathjax.org/) is an open-source JavaScript display engine for LaTeX, MathML, and AsciiMath notation that works in all modern browsers.
**Some of the main features of MathJax include:**
Some of the main features of MathJax include:
- High-quality display of LaTeX, MathML, and AsciiMath notation in HTML pages
- Supported in most browsers with no plug-ins, extra fonts, or special
@ -394,6 +611,12 @@ Rowspan is 4
advanced functionality
- Powerful API for integration with other web applications
#### 2.1 Performance optimization
At building stage, the MathJax engine script will be added by automatically checking whether there is a math expression in the page, this feature can help you improve the page performance on loading speed.
#### 2.2 How to use?
Put your math expression within \$...\$
```markdown
@ -408,22 +631,26 @@ $ 2^{\frac{n-1}{3}} $
$ \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
[PlantUML](http://plantuml.sourceforge.net/) is a component that allows to quickly write:
[PlantUML](https://plantuml.com) is a component that allows to quickly write:
- sequence diagram,
- use case diagram,
- class diagram,
- activity diagram,
- component diagram,
- state diagram
- state diagram,
- object diagram
There are two ways to create a diagram in your Jekyll blog page:
````markdown
```plantuml
````
```plantuml!
Bob -> Alice : hello world
```
````
@ -436,24 +663,76 @@ Bob -> Alice : hello
@enduml
```
### 4. Video Usage
Code above would be parsed as:
How often did you find yourself googling "**How to embed a video in markdown?**"
![PlantUML Diagram](https://user-images.githubusercontent.com/9413601/82813883-9180b300-9ec8-11ea-8778-f450e0056938.png)
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.
### 4. Mermaid Usage
**For now, these video links parsing are provided:**
[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.
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
- Vimeo
- 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 in your Jekyll blog page:
There are two ways to embed a video/audio in your Jekyll blog page:
Inline-style:
```markdown
![]({video-link})
![]({media-link})
```
Reference-style:
@ -461,27 +740,23 @@ Reference-style:
```markdown
![][{reference}]
[{reference}]: {video-link}
[{reference}]: {media-link}
```
For configuring video attributes (e.g, width, height), just adding query string to
For configuring media attributes (e.g, width, height), just adding query string to
the link as below:
```markdown
![](https://www.youtube.com/watch?v=Ptk_1Dc2iPY?width=800&height=500)
```
```markdown
![](https://www.dailymotion.com/video/x7tfyq3?width=100%&height=400)
![](https://www.dailymotion.com/video/x7tfyq3?width=100%&height=400&autoplay=1)
```
#### Youtube Usage
```markdown
![](https://www.youtube.com/watch?v=Ptk_1Dc2iPY)
```
```markdown
![](//www.youtube.com/watch?v=Ptk_1Dc2iPY?width=800&height=500)
```
@ -489,9 +764,7 @@ the link as below:
```markdown
![](https://vimeo.com/263856289)
```
```markdown
![](https://vimeo.com/263856289?width=500&height=320)
```
@ -499,13 +772,47 @@ the link as below:
```markdown
![](https://www.dailymotion.com/video/x7tfyq3)
```
```markdown
![](https://dai.ly/x7tgcev?width=100%&height=400)
```
### 5. Hybrid HTML with Markdown
#### Spotify Usage
```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.
@ -519,7 +826,7 @@ It's easy to write markdown inside HTML:
| : Fruits \|\| Food : |||
| :--------- | :-------- | :-------- |
| Apple | : Apple :| Apple \
| Apple | : Apple : | Apple \
| Banana | Banana | Banana \
| Orange | Orange | Orange |
| : Rowspan is 4 : || How's it? |
@ -539,7 +846,7 @@ Bob -> Alice : hello
</script>
```
### 6. Markdown Polyfill
### 7. Markdown Polyfill
It allows us to polyfill features for extending markdown syntax.
@ -547,7 +854,7 @@ It allows us to polyfill features for extending markdown syntax.
- Escape ordered list
#### Escape Ordered List
#### 7.1 Escape Ordered List
A backslash at begin to escape the ordered list.
@ -581,11 +888,136 @@ Escaped:
10. List item Cafe.
```
### 8. Emoji Usage
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).
In any page or post, use emoji as you would normally, e.g.
```
I give this plugin two :+1:!
```
**Code above would be parsed as:**
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
- [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.
- [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.
- [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

View File

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

View File

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

View File

@ -0,0 +1,61 @@
# 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

@ -1,21 +1,36 @@
# frozen_string_literal: true
require 'jekyll-spaceship/version'
require 'rainbow/refinement'
using Rainbow
module Jekyll::Spaceship
class Logger
def initialize(namespace)
@namespace = namespace
end
def self.display_info
self.log "Jekyll-Spaceship #{Jekyll::Spaceship::VERSION}"
self.log 'A Jekyll plugin to provide powerful supports.'
self.log 'https://github.com/jeffreytse/jekyll-spaceship'
self.log "🚀 Jekyll-Spaceship #{Jekyll::Spaceship::VERSION}"
self.log '🎉 A Jekyll plugin to provide powerful supports.'
self.log '👉 ' + 'https://github.com/jeffreytse/jekyll-spaceship'.underline
end
def self.log(content)
self.output 'Jekyll Spaceship', content
self.output 'Jekyll Spaceship', content.bright
end
def self.output(title, content)
puts "#{title.rjust(18)}: #{content}"
end
def log(content)
if @namespace.nil?
self.class.log content
else
self.class.log "[#{@namespace}] #{content}"
end
end
end
end

View File

@ -0,0 +1,119 @@
# frozen_string_literal: true
require 'nokogiri'
require __dir__ + '/type'
module Jekyll::Spaceship
class Manager
@@_hooks = {}
@@_processors = []
def self.add(processor)
# register for listening event
processor.registers.each do |_register|
container = _register.first
events = _register.last.uniq
events = events.select do |event|
next true if event.match(/^post/)
next events.index(event.to_s.gsub(/^pre/, 'post').to_sym).nil?
end
events.each do |event|
self.hook container, event
end
end
@@_processors.push(processor)
@@_processors = @@_processors.sort { |a, b| b.priority <=> a.priority }
end
def self.hook(container, event, &block)
return if not is_hooked? container, event
handler = ->(page) {
self.dispatch page, container, event
block.call if block
}
if event.to_s.start_with?('after')
Jekyll::Hooks.register container, event do |page|
handler.call page
end
elsif event.to_s.start_with?('post')
Jekyll::Hooks.register container, event do |page|
handler.call page
end
# auto add pre-event
self.hook container, event.to_s.sub('post', 'pre').to_sym
elsif event.to_s.start_with?('pre')
Jekyll::Hooks.register container, event do |page|
handler.call page
end
end
end
def self.is_hooked?(container, event)
hook_name = "#{container}_#{event}".to_sym
return false if @@_hooks.has_key? hook_name
@@_hooks[hook_name] = true
end
def self.dispatch(page, container, event)
@@_processors.each do |processor|
processor.dispatch page, container, event
end
if event.to_s.start_with?('post') and Type.html? output_ext(page)
self.dispatch_html_block(page)
end
@@_processors.each do |processor|
processor.on_handled if processor.handled
end
end
def self.ext(page)
ext = page.path.match(/\.[^.]+$/)
ext.to_s.rstrip
end
def self.output_ext(page)
page.url_placeholders[:output_ext]
end
def self.converter(page, name)
page.site.converters.each do |converter|
class_name = converter.class.to_s.downcase
return converter if class_name.end_with?(name.downcase)
end
end
def self.dispatch_html_block(page)
doc = Nokogiri::HTML(page.output)
doc.css('script').each do |node|
type = Type.html_block_type node['type']
content = node.content
next if type.nil?
# dispatch to on_handle_html_block
@@_processors.each do |processor|
next unless processor.process?
content = processor.on_handle_html_block content, type
# dispatch to type handlers
method = "on_handle_#{type}"
next unless processor.respond_to? method
content = processor.pre_exclude content
content = processor.send method, content
content = processor.after_exclude content
end
cvter = self.converter page, type
content = cvter.convert content unless cvter.nil?
# dispatch to on_handle_html
@@_processors.each do |processor|
next unless processor.process?
content = processor.on_handle_html content
end
node.replace Nokogiri::HTML.fragment content
end
page.output = Processor.escape_html doc.to_html
end
end
end

View File

@ -1,325 +1,236 @@
# frozen_string_literal: true
require 'nokogiri'
module Jekyll::Spaceship
class Processor
@@_hooks = {}
@@_registers = []
@@_processers = []
@@_exclusions = []
@@_priority = nil
attr_accessor :priority
attr_accessor :page
attr_accessor :handled
DEFAULT_PRIORITY = 20
PRIORITY_MAP = {
:low => 10,
:normal => 20,
:high => 30,
:lowest => 0,
:low => 10,
:normal => 20,
:high => 30,
:highest => 40,
}.freeze
HTML_EXTENSIONS = %w(
.html
.xhtml
.htm
).freeze
@@_registers = []
@@_exclusions = []
@@_priority = nil
CSS_EXTENSIONS = %w(
.css
.scss
).freeze
attr_reader :page
attr_reader :logger
attr_reader :config
attr_reader :priority
attr_reader :registers
attr_reader :exclusions
attr_accessor :handled
MD_EXTENSIONS = %w(
.md
.markdown
).freeze
def name
self.class.name.split('::').last
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()
self.initialize_priority
self.initialize_register
self.initialize_exclusions
@logger = Logger.new(self.name)
@config = Config.store(self.filename, self.class.config)
@handled_files = {}
end
def initialize_priority
@@_priority = DEFAULT_PRIORITY if @@_priority.nil?
@priority = @@_priority
unless @priority.nil? or @priority.is_a? Numeric
@priority = PRIORITY_MAP[@priority.to_sym]
end
@priority = DEFAULT_PRIORITY if @priority.nil?
@@_priority = nil
@@_processers.push(self)
@@_processers = @@_processers.sort { |a, b| b.priority <=> a.priority }
end
def initialize_register
if @@_registers.size.zero?
self.class.register :pages, :pre_render, :post_render
self.class.register :posts, :pre_render, :post_render
end
@@_registers.each do |_register|
container = _register.first
events = _register.last.uniq
events = events.select do |event|
next true if event.match(/^post/)
next !events.any?(event.to_s.gsub(/^pre/, 'post').to_sym)
end
events.each do |event|
self.class.hook container, event
end
self.class.register :documents, :pre_render, :post_render
end
@registers = Array.new @@_registers
@@_registers.clear
end
def initialize_exclusions
if @@_exclusions.size.zero?
self.class.exclude :code, :block_quotes
self.class.exclude :code, :math, :liquid_filter
end
@_exclusions = @@_exclusions.uniq
@exclusions = @@_exclusions.uniq
@@_exclusions.clear
end
def self.priority(value)
value = value.to_sym
if PRIORITY_MAP.has_key? value
@@_priority = PRIORITY_MAP[value]
elsif value.nil?
@@_priority = DEFAULT_PRIORITY
else
@@_priority = value
end
@@_priority = value.to_sym
end
def self.register(container, *events)
@@_registers << [container, events]
end
def self.hook_register(container, event)
hook_name = "#{container}_#{event}".to_sym
return false if @@_hooks.has_key? hook_name
@@_hooks[hook_name] = true
end
def self.hook(container, event, &block)
return if not hook_register container, event
# define dispatch proc
dispatch = ->(instance) {
@@_processers.each do |processor|
processor.page = instance
processor.handled = false
next if not processor.process?
processor.dispatch container, event
end
if event.to_s.start_with?('post') and self.html? output_ext(instance)
self.handle_html_block(instance)
end
@@_processers.each do |processor|
processor.page = instance
if processor.handled
processor.on_handled
end
end
block.call if block
}
if event.to_s.start_with?('after')
Jekyll::Hooks.register container, event do |instance|
dispatch.call instance
end
elsif event.to_s.start_with?('post')
Jekyll::Hooks.register container, event do |instance|
dispatch.call instance
end
# auto add pre-event
register container, event.to_s.sub('post', 'pre').to_sym
elsif event.to_s.start_with?('pre')
Jekyll::Hooks.register container, event do |instance|
dispatch.call instance
end
end
end
def dispatch(container, event)
method = "on_#{container}_#{event}"
self.send method, @page if self.respond_to? method
if event.to_s.start_with?('pre')
if markdown? ext
method = 'on_handle_markdown'
else
method = ''
end
if self.respond_to? method
@page.content = self.pre_exclude @page.content
@page.content = self.send method, @page.content
@page.content = self.after_exclude @page.content
end
else
if html? output_ext
method = 'on_handle_html'
elsif css? output_ext
method = 'on_handle_css'
else
method = ''
end
if self.respond_to? method
@page.output = self.send method, @page.output
end
end
end
def self.exclude(*types)
@@_exclusions = types
end
def html?(_ext)
self.class.html? _ext
end
def css?(_ext)
self.class.css? _ext
end
def markdown?(_ext)
self.class.markdown? _ext
end
def self.html?(_ext)
HTML_EXTENSIONS.include?(_ext)
end
def self.css?(_ext)
CSS_EXTENSIONS.include?(_ext)
end
def self.markdown?(_ext)
MD_EXTENSIONS.include?(_ext)
end
def converter(name)
self.class.converter(@page, name)
end
def self.converter(instance, name)
instance.site.converters.each do |converter|
class_name = converter.class.to_s.downcase
return converter if class_name.end_with?(name.downcase)
end
end
def ext
self.class.ext @page
end
def output_ext
self.class.output_ext @page
end
def self.ext(instance)
instance.data['ext']
end
def self.output_ext(instance)
instance.url_placeholders[:output_ext]
def self.config
end
def process?
html?(output_ext) or markdown?(ext)
Type.html?(output_ext) or Type.markdown?(ext)
end
def pre_exclude(content)
@_exclusion_store = []
@_exclusions.each do |type|
regex = nil
if type == :code
regex = /(`{3}\s*(\w*)((?:.|\n)*?)`{3})/
def ext
Manager.ext @page
end
def output_ext
Manager.output_ext @page
end
def converter(name)
Manager.converter @page, name
end
def dispatch(page, container, event)
@page = page
@handled = false
return unless self.process?
method = "on_#{container}_#{event}"
self.send method, @page if self.respond_to? method
method = ''
if event.to_s.start_with?('pre')
if Type.markdown? ext
method = 'on_handle_markdown'
end
next if regex.nil?
content.scan(regex) do |match_data|
match = match_data[0]
id = @_exclusion_store.size
content = content.gsub(match, "[//]: JEKYLL_EXCLUDE_##{id}")
@_exclusion_store.push match
if self.respond_to? method
@page.content = self.pre_exclude @page.content
@page.content = self.send method, @page.content
@page.content = self.post_exclude @page.content
end
else
if Type.html? output_ext
method = 'on_handle_html'
elsif Type.css? output_ext
method = 'on_handle_css'
end
if self.respond_to? method
@page.output = self.send method, @page.output
if Type.html? output_ext
@page.output = self.class.escape_html(@page.output)
end
end
end
end
def on_handle_html_block(content, type)
# default handle method
content
end
def after_exclude(content)
while @_exclusion_store.size > 0
match = @_exclusion_store.pop
id = @_exclusion_store.size
content = content.gsub("[//]: JEKYLL_EXCLUDE_##{id}", match)
end
@_exclusion_store = []
def on_handle_html(content)
# default handle method
content
end
def on_handled
processor = self.class.name.split('::').last
file = page.path.gsub(/.*_posts\//, '')
Logger.log "[#{processor}] #{file}"
source = page.site.source
file = page.path.sub(/^#{source}\//, '')
return if @handled_files.has_key? file
@handled_files[file] = true
logger.log file
end
def self.handle_html_block(instance)
doc = Nokogiri::HTML(instance.output)
doc.css('script').each do |node|
blk_type = node['type']
blk_content = node.content
cvter = nil
method = ''
block_method = 'on_handle_html_block'
case blk_type
when 'text/markdown'
method = 'on_handle_markdown'
cvter = self.converter(instance, 'markdown')
def exclusion_regexs()
regexs = []
@exclusions.each do |type|
regex = nil
if type == :code
regex = /(((?<!\\)`+)\s*(\w*)((?:.|\n)*?)\2)/
elsif type == :math
regex = /(((?<!\\)\${1,2})[^\n]*?\1)/
elsif type == :liquid_filter
regex = /((?<!\\)((\{\{[^\n]*?\}\})|(\{%[^\n]*?%\})))/
end
@@_processers.each do |processor|
processor.page = instance
next if not processor.process?
# dispatch to on_handle_html_block
if processor.respond_to? block_method
blk_content = processor.send block_method blk_content, blk_type
end
# dispatch to other handlers
if processor.respond_to? method
blk_content = processor.pre_exclude blk_content
blk_content = processor.send method, blk_content
blk_content = processor.after_exclude blk_content
end
end
if not cvter.nil?
blk_content = cvter.convert blk_content
end
next if method == ''
method = 'on_handle_html'
@@_processers.each do |processor|
processor.page = instance
next if not processor.process?
if processor.respond_to? method
blk_content = processor.send method, blk_content
end
end
node.replace Nokogiri::HTML.fragment(blk_content)
regexs.push regex unless regex.nil?
end
regexs
end
instance.output = doc.to_html
def pre_exclude(content, regexs = self.exclusion_regexs())
@exclusion_store = []
regexs.each do |regex|
content.scan(regex) do |match_data|
match = match_data[0]
id = @exclusion_store.size
content = content.sub(match, "<!JEKYLL@#{object_id}@#{id}>")
@exclusion_store.push match
end
end
content
end
def post_exclude(content)
while @exclusion_store.size > 0
match = @exclusion_store.pop
id = @exclusion_store.size
content = content.sub("<!JEKYLL@#{object_id}@#{id}>", match)
end
@exclusion_store = []
content
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

View File

@ -24,21 +24,17 @@ module Jekyll::Spaceship
filename = File.basename(path, '.rb')
next if filename.gsub(/-/, '').downcase != name
Logger.log "use #{filename}"
Logger.log "🗂 use #{filename}"
require path
constants = Jekyll::Spaceship.constants.select do |c|
c.downcase.to_s == name
end
next if constants.first.nil?
_class = Jekyll::Spaceship.const_get(constants.first)
next unless _class.is_a? Class
_class.new
Manager.add _class.new
end
end
end

View File

@ -0,0 +1,41 @@
# frozen_string_literal: true
module Jekyll::Spaceship
class Type
HTML_EXTENSIONS = %w(
.html
.xhtml
.htm
).freeze
CSS_EXTENSIONS = %w(
.css
.scss
).freeze
MD_EXTENSIONS = %w(
.md
.markdown
).freeze
HTML_BLOCK_TYPE_MAP = {
'text/markdown' => 'markdown',
}.freeze
def self.html?(_ext)
HTML_EXTENSIONS.include?(_ext)
end
def self.css?(_ext)
CSS_EXTENSIONS.include?(_ext)
end
def self.markdown?(_ext)
MD_EXTENSIONS.include?(_ext)
end
def self.html_block_type(type)
HTML_BLOCK_TYPE_MAP[type]
end
end
end

View File

@ -0,0 +1,170 @@
# 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

@ -0,0 +1,46 @@
# frozen_string_literal: true
require 'net/http'
require 'json'
require 'gemoji'
module Jekyll::Spaceship
class EmojiProcessor < Processor
def self.config
{
'css' => {
'class' => 'emoji'
},
'src' => 'https://github.githubassets.com/images/icons/emoji/'
}
end
def on_handle_html(content)
# handle emoji markup
content.scan(/:([\w\d+-]+):/) do |match|
emoji = Emoji.find_by_alias match[0]
next if emoji.nil?
self.handled = true
# escape plus sign
emoji_name = emoji.name.gsub('+', '\\\+')
css_class = self.config['css']['class']
content = content.gsub(
/(?<!\=")\s*:#{emoji_name}:\s*(?!"\s)/,
"<img class=\"#{css_class}\""\
" title=\":#{emoji.name}:\""\
" alt=\":#{emoji.name}:\""\
" raw=\"#{emoji.raw}\""\
" src=\"#{config['src']}#{emoji.image_filename}\""\
" style=\"vertical-align: middle; display: inline;"\
" max-width: 1em; visibility: hidden;\""\
" onload=\"this.style.visibility='visible'\""\
" onerror=\"this.replaceWith(this.getAttribute('raw'))\">"\
"</img>"
)
end
content
end
end
end

View File

@ -4,8 +4,21 @@ require "nokogiri"
module Jekyll::Spaceship
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?
return true if html?(output_ext)
return true if Type.html?(output_ext)
end
def on_handle_html(content)
@ -18,17 +31,25 @@ module Jekyll::Spaceship
self.handled = true
params = "config=TeX-AMS-MML_HTMLorMML"
src = "//cdn.mathjax.org/mathjax/latest/MathJax.js?#{params}"
config = "MathJax.Hub.Config({tex2jax: {inlineMath: [['$','$'], ['\\(','\\)']]}});"
head.add_child("<script src=\"#{src}\">#{config}</script>")
# add mathjax config
cfg = config['config'].to_json
head.add_child("<script>MathJax=#{cfg}</script>")
# add mathjax dependencies
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
end
def has_mathjax_expression?(doc)
doc.css('*').each do |node|
if node.content.match(/\$.+\$/)
if node.content.match(/(?<!\\)\$.+(?<!\\)\$/)
return true
end
if node.content.match(/(?<!\\)\\\(.+(?<!\\)\\\)/)
return true
end
end

View File

@ -0,0 +1,234 @@
# 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

@ -0,0 +1,102 @@
# 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,25 +1,50 @@
# frozen_string_literal: true
require "net/http"
require "base64"
module Jekyll::Spaceship
class PlantUMLProcessor < Processor
class PlantumlProcessor < Processor
exclude :none
def on_handle_markdown(content)
# match default plantuml block and code block
pattern = Regexp.union(
/(\\?@startuml((?:.|\n)*?)@enduml)/,
/(`{3}\s*plantuml((?:.|\n)*?)`{3})/
)
def self.config
{
'mode' => 'default',
'syntax' => {
'code' => 'plantuml!',
'custom' => ['@startuml', '@enduml']
},
'css' => {
'class' => 'plantuml'
},
'src' => 'http://www.plantuml.com/plantuml/svg/'
}
end
def on_handle_markdown(content)
# match custom plantuml 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_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|
match = match.select { |m| not m.nil? }
block = match[0]
code = match[1]
# skip escape default plantuml block
if block.match(/(^\\@startuml|\\@enduml$)/)
next
end
code = match[2]
self.handled = true
@ -28,25 +53,41 @@ module Jekyll::Spaceship
handle_plantuml(code)
)
end
# handle escape default plantuml block
content.gsub(/\\(@startuml|@enduml)/, '\1')
content
end
def handle_plantuml(code)
# wrap plantuml code
uml = "@startuml#{code}@enduml"
code = "@startuml#{code}@enduml".encode('UTF-8')
url = self.get_url(code)
dir = File.dirname(__FILE__)
jar = dir + "/../utils/plantuml/plantuml.jar"
echo = "echo -e \"#{uml.gsub('"', '\"')}\""
plantuml = "java -jar \"#{jar}\" -pipe 2>/dev/null"
# exec plantuml.jar and output base64 data
base64 = `#{echo} | #{plantuml} | base64`
# 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
"<img src=\"data:image/png;base64, #{base64}\">"
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
if src.include?('{hexcode}')
code = '~h' + code.unpack("H*").first
return src.gsub('{hexcode}', code)
else
raise "No supported src ! #{src}"
end
end
end
end

View File

@ -5,18 +5,23 @@ require "nokogiri"
module Jekyll::Spaceship
class TableProcessor < Processor
ATTR_LIST_PATTERN = /((?<!\\)\{:(?:([A-Za-z]\S*):)?(.*?)(?<!\\)\})/
ATTR_LIST_REFS = {}
def on_handle_markdown(content)
# pre-handle reference-style links
references = {}
content.scan(/(\[(.*)\]:\s*(.*))/) do |match_data|
content.scan(/\n\s*(\[(.*)\]:\s*(\S+(\s+".*?")?))/) do |match_data|
ref_name = match_data[1]
ref_value = match_data[2]
references[ref_name] = ref_value
end
if references.size > 0
content.scan(/.*(?<!\\)\|.*/) do |result|
content.scan(/[^\n]*(?<!\\)\|[^\n]*/) do |result|
references.each do |key, val|
replace = result.gsub(/\[(.*)\]\s*\[#{key}\]/, "[\1](#{val})")
replace = result.gsub(
/\[([^\n\]]*?)\]\s*\[#{key}\]/,
"[\1](#{val})")
next if result == replace
content = content.gsub(result, replace)
end
@ -24,21 +29,37 @@ module Jekyll::Spaceship
end
# pre-handle row-span
content = content.gsub(/(?<!\\)(\|.*\\\s*)\|\s*\n/, "\\1\n")
content = content.gsub(/(?<!\\)(\|[^\n]*\\\s*)\|\s*\n/, "\\1\n")
# escape | and :
content = content.gsub(/\|(?=\|)/, '\\|')
.gsub(/\\:(?=.*?(?<!\\)\|)/, '\\\\\\\\:')
.gsub(/((?<!\\)\|.*?)(\\:)/, '\1\\\\\\\\:')
.gsub(/\\:(?=[^\n]*?(?<!\\)\|)/, '\\\\\\\\:')
.gsub(/((?<!\\)\|[^\n]*?)(\\:)/, '\1\\\\\\\\:')
# escape * and _ and $ etc.
content.scan(/.*(?<!\\)\|.*/) do |result|
content.scan(/[^\n]*(?<!\\)\|[^\n]*/) do |result|
# skip for math expression within pipeline
next unless result
.gsub(/((?<!\\)\${1,2})[^\n]*?\1/, '')
.match(/(?<!\\)\|/)
replace = result.gsub(
/(?<!(?<!\\)\\)(\*|\$|\[|\(|\"|_)/,
'\\\\\\\\\1')
/(?<!(?<!\\)\\)(\*|\$|\[|\(|\"|_)/, '\\\\\\\\\1')
next if result == replace
content = content.gsub(result, replace)
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
end
@ -46,6 +67,43 @@ module Jekyll::Spaceship
# use nokogiri to parse html content
doc = Nokogiri::HTML(content)
data = self.table_scope_data
# handle each table
doc.css('table').each do |table|
next if table.ancestors('code, pre').size > 0
rows = table.css('tr')
data.table = table
data.rows = rows
data.reset.call :table
rows.each do |row|
cells = row.css('th, td')
data.row = row
data.cells = cells
data.reset.call :row
cells.each do |cell|
data.cell = cell
handle_colspan(data)
handle_multi_rows(data)
handle_text_align(data)
handle_rowspan(data)
handle_attr_list(data)
end
end
rows.each do |row|
cells = row.css('th, td')
cells.each do |cell|
data.cell = cell
handle_format(data)
end
end
self.handled = true
end
doc.to_html
end
def table_scope_data
data = OpenStruct.new(_: OpenStruct.new)
data.reset = ->(scope, namespace = nil) {
data._.marshal_dump.each do |key, val|
@ -63,78 +121,48 @@ module Jekyll::Spaceship
end
data._[namespace]
}
# handle each table
doc.css('table').each do |table|
rows = table.css('tr')
data.table = table
data.rows = rows
data.reset.call :table
rows.each do |row|
cells = row.css('th, td')
data.row = row
data.cells = cells
data.reset.call :row
cells.each do |cell|
data.cell = cell
handle_colspan(data)
handle_multi_rows(data)
handle_text_align(data)
handle_rowspan(data)
end
end
rows.each do |row|
cells = row.css('th, td')
cells.each do |cell|
data.cell = cell
handle_format(data)
end
end
self.handled = true
end
doc.to_html
data
end
def handle_colspan(data)
scope = data.scope.call __method__
scope_table = scope.table
scope_row = scope.row
cells = data.cells
cell = data.cell
if scope_table.row != data.row
scope_table.row = data.row
scope_row.colspan = 0
if scope.table.row != data.row
scope.table.row = data.row
scope.row.colspan = 0
end
# handle colspan
result = cell.content.match(/(\s*\|)+$/)
if cell == cells.last and scope_row.colspan > 0
range = (cells.count - scope_row.colspan)...cells.count
for i in range do
cells[i].remove
if cell == cells.last and scope.row.colspan > 0
cells.count.downto(cells.count - scope.row.colspan + 1) do |i|
c = cells[i - 1]
return unless c.get_attribute('colspan').nil?
c.remove
end
end
if result
result = result[0]
scope_row.colspan += result.scan(/\|/).count
cell.content = cell.content.gsub(/(\s*\|)+$/, '')
cell.set_attribute('colspan', scope_row.colspan + 1)
end
result = cell.content.match(/(\|)+$/)
return if result.nil?
cell.content = cell.content.gsub(/(\|)+$/, '')
result = result[0]
colspan = result.scan(/\|/).count
scope.row.colspan += colspan
cell.set_attribute('colspan', colspan + 1)
end
def handle_multi_rows(data)
scope = data.scope.call __method__
scope_table = scope.table
cells = data.cells
row = data.row
cell = data.cell
if scope_table.table != data.table
scope_table.table = data.table
scope_table.multi_row_cells = nil
scope_table.multi_row_start = false
if scope.table.table != data.table
scope.table.table = data.table
scope.table.multi_row_cells = nil
scope.table.multi_row_start = false
end
# handle multi-rows
@ -143,53 +171,49 @@ module Jekyll::Spaceship
match = cell.content.match(/(?<!\\)\\\s*$/)
if match
cell.content = cell.content.gsub(/(?<!\\)\\\s*$/, '')
if not scope_table.multi_row_start
scope_table.multi_row_cells = cells
scope_table.multi_row_start = true
if not scope.table.multi_row_start
scope.table.multi_row_cells = cells
scope.table.multi_row_start = true
end
end
if scope_table.multi_row_cells != cells and scope_table.multi_row_start
for i in 0...scope_table.multi_row_cells.count do
multi_row_cell = scope_table.multi_row_cells[i]
multi_row_cell.content += " \n#{cells[i].content}"
if scope.table.multi_row_cells != cells and scope.table.multi_row_start
for i in 0...scope.table.multi_row_cells.count do
multi_row_cell = scope.table.multi_row_cells[i]
multi_row_cell.inner_html += "\n<br>\n#{cells[i].inner_html}"
end
row.remove
end
scope_table.multi_row_start = false if not match
scope.table.multi_row_start = false if not match
end
def handle_rowspan(data)
scope = data.scope.call __method__
scope_table = scope.table
scope_row = scope.row
cell = data.cell
cells = data.cells
if scope_table.table != data.table
scope_table.table = data.table
scope_table.span_row_cells = []
if scope.table.table != data.table
scope.table.table = data.table
scope.table.span_row_cells = []
end
if scope_row.row != data.row
scope_row.row = data.row
scope_row.col_index = 0
if scope.row.row != data.row
scope.row.row = data.row
scope.row.col_index = 0
end
# handle rowspan
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}/)
cell.content = cell.content.gsub(/^\s*\^{2}/, '')
span_cell.content += " \n#{cell.content}"
span_cell.inner_html += "\n<br>\n#{cell.inner_html}"
rowspan = span_cell.get_attribute('rowspan') || 1
rowspan = rowspan.to_i + 1
span_cell.set_attribute('rowspan', "#{rowspan}")
cell.remove
else
scope_table.span_row_cells[scope_row.col_index] = cell
scope.table.span_row_cells[scope.row.col_index] = cell
end
scope_row.col_index += 1
scope.row.col_index += [cell.get_attribute('colspan').to_i, 1].max
end
def handle_text_align(data)
@ -206,12 +230,12 @@ module Jekyll::Spaceship
align += 2
end
# handle escape colon
cell.content = cell.content.gsub(/\\:/, ':')
# handle text align
return if align == 0
# handle escape colon
cell.content = cell.content.gsub(/\\:/, ':')
style = cell.get_attribute('style')
if align == 1
align = 'text-align: left'
@ -230,13 +254,69 @@ module Jekyll::Spaceship
cell.set_attribute('style', style)
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)
cell = data.cell
cvter = self.converter('markdown')
return if cvter.nil?
content = cell.content.gsub(/(?<!\\)\|/, '\\|')
content = cvter.convert(content.strip)
cell.inner_html = Nokogiri::HTML.fragment(content)
content = cell.inner_html
content = self.pre_exclude(content, [/(\<code.*\>.*\<\/code\>)/])
.gsub(/(?<!\\)\|/, '\\|')
.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

View File

@ -1,99 +0,0 @@
# frozen_string_literal: true
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]
width = url.match(/(?<=width=)(\S*?)(?=&|$)/)
height = url.match(/(?<=height=)(\S*?)(?=&|$)/)
data[:width] = 600 if data[:width].nil?
data[:height] = 400 if data[:height].nil?
style = "max-width: 100%" if width.nil?
width = data[:width] if width.nil?
height = data[:height] if height.nil?
url = "#{iframe_url}#{id}"
html = "<iframe \
src=\"#{url}\" \
title=\"#{title}\" \
width=\"#{width}\" \
height=\"#{height}\" \
style=\"#{style}\" \
frameborder=\"0\" \
allowfullscreen=\"\">\
</iframe>"
content = content.gsub(match_data[0], html)
self.handled = true
end
content
end
end
end

View File

View File

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 59 KiB

100
spec/spec_helper.rb Normal file
View File

@ -0,0 +1,100 @@
# 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