premonition 2.0.1 → 4.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 2aaae4cd98bc03518dfe2ff814d0686aab58f5b0370f8bb3f4d3b19eef303007
4
- data.tar.gz: 28c1d9e93692de8c29affe8982accb19327e4d7fcdb32186f4d00679f8a38964
3
+ metadata.gz: c3696b91551d20df8b09f16875aaffa95a1910c5dc5ded8b0706cee7c11f4a9a
4
+ data.tar.gz: 1e8544cacc2c1f7891bb5f1cb0b68ebf97babc923301fa2226f702ac175f6b65
5
5
  SHA512:
6
- metadata.gz: 647c3f5c4b253c9f13d447fdcb25e5aa83aba2ff215eb241c63d3cb1103bf26560b967697a6831f95be030691940cf2c4e599cb87f35a050766e5bb5199c87b7
7
- data.tar.gz: 97837e09726a349eaf4817ebe4acb0cbd60042ca8d663c02b737f170fd5759ada261433da5f7b9691cf312aff8a73a9de4df0b2b6200c5c2820b4c39c9b43012
6
+ metadata.gz: 9da70c269a26ed2b973677ee951512566314327b800f85884c505687572d6de2ca5733fb5917ae97977ff420c692bed4c96fb2631187f4b858d54174c0ae769f
7
+ data.tar.gz: 5fa694730310a0191792c921351bdc3b0de26f995b3c7e5df22b693ddcef91f6b2451705c4efac460cfa6770cae5062421ee4e1a09787bcfd1e31930ad98e4c4
data/README.md CHANGED
@@ -1,113 +1,168 @@
1
1
  # Premonition
2
2
 
3
- **DEMO: https://lazee.github.io/premonition-demo/ ([Source code](https://github.com/lazee/premonition-demo))**
3
+ [Demo site](https://lazee.github.io/premonition-demo/) ([Source code](https://github.com/lazee/premonition-demo))
4
4
 
5
- Premonition is a [Jekyll](https://jekyllrb.com/) extension that makes it possible to add block-styled content to your site in plain Markdown.
5
+ Premonition is a higly customizable [Jekyll](https://jekyllrb.com/) plugin that can convert Markdown block-quotes into beautiful block styled content.
6
6
 
7
- By adding a special header to the first line of a [block quote](https://github.com/adam-p/markdown-here/wiki/Markdown-Cheatsheet#blockquotes),
8
- Premonition will transform it into a markup block of your choice.
7
+ By simply adding a custom header to the first line of a [block quote](https://github.com/adam-p/markdown-here/wiki/Markdown-Cheatsheet#blockquotes), Premonition will transform it into a markup block of your choice.
9
8
 
10
9
  <p align="center">
11
- <img src="https://github.com/lazee/premonition/raw/master/screen.png" height="450"/>
10
+ <img src="https://github.com/lazee/premonition/raw/master/screen.png" height="550"/>
12
11
  </p>
13
12
 
14
13
  ## Features
15
14
 
16
- * Highly customizable (Create your own styles and templates easily)
17
- * Non-intrusive - Content are presented as block-quotes by any other renderer.
18
- * Easy to install
19
- * Comes with a stylesheet (Sass/Css) and templates for rendering typical information boxes.
20
- * Support for both Kramdown and RedCarpet.
15
+ - Highly customizable (Create your own styles and templates easily)
16
+ - Non-intrusive - Its just Markdown!
17
+ - Easy to install
18
+ - Comes with a default stylesheet (Sass/Css) and templates for beautiful messages boxes and citation.
19
+ - Font Awesome 5 support
20
+
21
+ ## Version 4 Highlights
22
+
23
+ - Jekyll [Post Excerpts](https://jekyllrb.com/docs/posts/#post-excerpts) support
24
+ - New install command for the default stylesheet.
25
+ - [Kramdown reference links](https://kramdown.gettalong.org/quickref.html#links-and-images) support
26
+ - Jekyll 4 support (3.7 still supported)
27
+ - Added support for block attributes (See documentation further down)
28
+ - Added new citation block type.
29
+ - Minor fixes to the Premonition stylesheet.
30
+ - Removed the need Font Awesome css in default stylesheet, but
31
+ Font Awesome is still supported.
32
+ - Other bug fixes. See HISTORY.md.
33
+
34
+ See UPGRADE.md for help on how to upgrade from 2.x to 4.0.
21
35
 
22
36
  ## Requirements
23
37
 
24
- * Jekyll 3.7.x or higher
25
- * FontAwesome 4.x (If you are using the default template and styles)
38
+ - Jekyll 3.7.x or higher (We recommend the new Jekyll 4)
26
39
 
27
40
  ## Installation
28
41
 
29
- Add the following line to your `Gemfile`:
42
+ Add the following line to your `Gemfile` inside your Jekyll project folder:
30
43
 
31
44
  ```
32
45
  group :jekyll_plugins do
33
- gem "premonition", "~> 2.0.0"
46
+ gem "premonition", "4.0.0"
34
47
  end
35
48
  ```
36
49
 
37
- Then add Premonition to `plugins` in `_config.yml`:
50
+ Then add the the plugin to your `_config.yml`:
38
51
 
39
52
  ```yaml
40
53
  plugins:
41
- - premonition
54
+ - premonition
42
55
  ```
43
56
 
44
- Finally run `bundle install`
57
+ Now make sure to download the Premonition bundle:
58
+
59
+ ```
60
+ bundle install
61
+ ```
62
+
63
+ ### Installing the default stylesheet
64
+
65
+ Finally, if you want to use the default Premonition styling (You really should!), then you have to install the SASS stylesheet.
66
+ Note: The installer expect you to have installed SASS support like it is described in the Jekyll documentation: https://jekyllrb.com/docs/step-by-step/07-assets/#sass.
67
+
68
+ From your Jekyll project folder, run:
69
+
70
+ ```
71
+ bundle exec jekyll premonition-install
72
+ ```
73
+
74
+ This will add the `premonition.scss` file to your `_sass` folder and ask if you want to import this file into your `assets/main.scss` file.
75
+ Both of these settings (destination folder and main file) can be configured. Run `bundle exec jekyll premonition-install --help` to see how.
76
+
77
+ If you prefer CSS, then download the stylesheet/premonition.css file directly from this repo.
45
78
 
46
79
  ## Usage
47
80
 
48
- Premonition blocks are really just a standard Markdown blockquote where the first line must be on a
49
- special format to activate the transformation.
81
+ A Premonition block is really just a standard Markdown blockquote where the first line of the block must follow a certain syntax.
82
+
83
+ `> [type] "Title" [ attributes... ]`
84
+
85
+ The type must be set to one of the default Premonition block types, or a type
86
+ defined by you in `_config.yml`.
50
87
 
51
- `> [type] "Title"`
88
+ The default types are:
52
89
 
53
- The type can be any letter string. It is used to map a block to its type configuration and/or css.
54
- By default the type will be added as a class to the outer `<div>` of the
55
- generated markup.
90
+ - note
91
+ - info
92
+ - warning
93
+ - error
94
+ - citation
56
95
 
57
- Default types are:
96
+ The _Title_ will normally be the block header. Leave it empty to disable
97
+ the header.
58
98
 
59
- * note
60
- * info
61
- * warning
62
- * error
99
+ _attributes_ are in use by the Citation type, but can be skipped for the other default types. See section about custom types for more info.
63
100
 
64
- The *Title* is the box header. It can be left empty to disable the box header:
101
+ ### Examples
65
102
 
66
- ~~~markdown
67
- > warning ""
103
+ Simple note with no header
104
+
105
+ ```markdown
106
+ > note ""
68
107
  > No headers in here
69
- ~~~
108
+ ```
109
+
110
+ Note
111
+
112
+ ```markdown
113
+ > note "I am a not"
114
+ > The body of the note goes here. Premonition allows you to write any `Markdown` inside the block.
115
+ ```
70
116
 
71
- Example:
117
+ Info
72
118
 
73
- ~~~markdown
119
+ ```markdown
120
+ > info "I am some info"
121
+ > The body of the info box goes here. Premonition allows you to write any `Markdown` inside the block.
122
+ ```
123
+
124
+ Warning
125
+
126
+ ```markdown
74
127
  > warning "I am a warning"
75
- > The body of the warning goes here. Premonition allows you to write any `Markdown` inside the block.
76
- ~~~
128
+ > The body of the warning box goes here. Premonition allows you to write any `Markdown` inside the block.
129
+ ```
77
130
 
78
- Premonition will then convert this into something like:
131
+ Error
79
132
 
80
- ~~~html
81
- <div class="premonition info"><div class="fa fa-check-square"></div><div class="content"><p class="header">Info</p><p>The body of the warning goes here. Premonition also allow you to write Markdown inside the block.</p></div></div>
82
- ~~~
133
+ ```markdown
134
+ > error "I am an error"
135
+ > The body of the error box goes here. Premonition allows you to write any `Markdown` inside the block.
136
+ ```
83
137
 
84
- You can change the markup into anything you like by adding your own template.
138
+ Citation (Note the use of attributes here)
139
+
140
+ ```markdown
141
+ > citations "Mark Twain" [ cite = "mt" ]
142
+ > I will be a beautiful citation quote
143
+ ```
85
144
 
86
145
  ## Configuration
87
146
 
88
- The templates can be customized in two eays. Either by replacing the default template, or by adding a custom template to a type.
147
+ The templates can be customized in two eays. Either by replacing one of the default templates, or by adding a new type from scratch.
89
148
 
90
- All this is done inside your `_config.yml`.
149
+ All this is done inside your `_config.yml`. Look at the [source code for our demo site](https://github.com/lazee/premonition-demo), for live examples on how configuration can be done.
91
150
 
92
151
  ### Templates
93
152
 
94
- Like Jekyll itself, Premonition uses the [Liquid Markup Language](https://github.com/Shopify/liquid) for its templates.
95
-
96
- Five variables are available to the template engine:
153
+ Like Jekyll itself, Premonition uses the [Liquid Markup Language](https://github.com/Shopify/liquid) for templating.
97
154
 
98
- * *header* Boolean that tells you if a title exists and that a header should be added.
99
- * *content* The rendered content for your block.
100
- * *title* The block title.
101
- * *type* The type name (eg: note).
102
- * *meta* This is a hash that can contain any properties you would like to make available to your template. It is configured in `_config.yml`
155
+ Six variables are available to the template engine:
103
156
 
104
- Our default template:
157
+ - _header_ Boolean that tells you if a title exists and that a header should be added.
158
+ - _content_ The rendered content for your block.
159
+ - _title_ The block title.
160
+ - _type_ The type name (eg: note).
161
+ - _meta_ This is a hash that can contain any properties you would like to make available to your template. It is configured in `_config.yml`
162
+ - _attrs_ These are the attributes set in the block header. Like we did in the Citation example above.
105
163
 
106
- ~~~html
107
- <div class="premonition {{type}}">
108
- <div class="fa {{meta.fa-icon}}"></div>
109
- <div class="content">{% if header %}<p class="header">{{title}}</p>{% endif %}{{content}}</div></div>
110
- ~~~
164
+ Take a look at our default template inside `lib/premonition/resources.rb` to
165
+ get an idea of how this is done.
111
166
 
112
167
  #### Overriding the default template
113
168
 
@@ -116,7 +171,7 @@ You can override the default template like this in your `_config.yml`:
116
171
  ```yaml
117
172
  premonition:
118
173
  default:
119
- template: 'Liquid template goes here'
174
+ template: "Liquid template goes here"
120
175
  ```
121
176
 
122
177
  #### Overriding the template for a default type
@@ -126,8 +181,8 @@ If you want to override the template for one of the default types (like note), d
126
181
  ```yaml
127
182
  premonition:
128
183
  types:
129
- - id: note
130
- template: 'Liquid template goes here'
184
+ note:
185
+ template: "Liquid template goes here"
131
186
  ```
132
187
 
133
188
  ### Adding custom types
@@ -137,38 +192,60 @@ of the defaults, or add a new one.
137
192
 
138
193
  For each type you can
139
194
 
140
- * Add a custom template (template)
141
- * Set a default title (default_title)
142
- * Set meta data that can be used inside the template
195
+ - Add a custom template (template)
196
+ - Set a default title (default_title)
197
+ - Set meta data that can be used inside the template
143
198
 
144
199
  Each type must have unique id (lowercase letters).
145
200
 
146
- ~~~yaml
201
+ ```yaml
147
202
  premonition:
148
203
  types:
149
- - id: custombox
204
+ custombox:
150
205
  meta:
151
- fa-icon: fa-exclamation-circle
152
- - id: advanced
153
- template: 'Liquid template goes here'
154
- default_title: 'MY BLOCK'
206
+ my-meta: "By myself"
207
+ advanced:
208
+ template: "Liquid template goes here"
209
+ default_title: "MY BLOCK"
155
210
  meta:
156
- fa-icon: fa-exclamation-triangle
157
- ~~~
211
+ my-meta: "By myself"
212
+ ```
213
+
214
+ ## More on styling
158
215
 
159
- ## Styling
216
+ As described in the Installation section above, it is pretty easy to install the default stylesheet into your project.
217
+ But we recognize that this design probably isn't a perfect fit for everybody. Luckily you can modify it :)
160
218
 
161
- Premonition comes with a stylesheet you can copy into to your project. Either
162
- as a Sass file or as plain css. The [Jekyll Documentation](https://jekyllrb.com/docs/assets/) describes the process in great details.
219
+ Our recommendation is to install the default stylesheet and override it in another SASS file. This way it will be
220
+ easy to upgrade the default Stylesheet later without loosing your changes.
163
221
 
164
- Download the stylesheet from here : https://github.com/lazee/premonition/tree/master/stylesheet
222
+ The [Jekyll Documentation](https://jekyllrb.com/docs/assets/) describes the process of adding your own SASS files in great details.
165
223
 
166
- In order to get the fancy icons, you will have to add [Font Awesome](https://fontawesome.com/) to your html header file.
167
- Be aware that you have to use v4.x of Font Awesome together with our CSS.
224
+ ## Font Awesome support
168
225
 
169
- The easiest way to get startet with Font Awesome is to add this to your html header file:
226
+ Premonition 4.x no longer depends on Font Awesome for its default stylesheet.
227
+ But it is still supported.
170
228
 
171
- ~~~html
172
- <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css"/>
173
- ~~~~
229
+ To add Font Awesome support you should add something like this
230
+ to your head template file:
231
+
232
+ ```
233
+ <link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.12.1/css/all.css">
234
+ ```
235
+
236
+ Feel free to install it any other way you prefer, as long as you follow their
237
+ [license](https://fontawesome.com/license/free).
238
+
239
+ Now you can swith to Font Awesome for any of the default types by adding
240
+ `fa-icon` to a types meta object. Let's say you want to replace the default error box icon with the beautiful `fa-bug` icon from Font Awesome.
241
+
242
+ Then just add this to your `_config.yml`:
243
+
244
+ ```yml
245
+ premonition:
246
+ types:
247
+ error:
248
+ fa-icon: "fa-bug"
249
+ ```
174
250
 
251
+ Simple as that :)
@@ -1,7 +1,14 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'jekyll'
2
- require 'premonition/version'
3
- require 'premonition/resources'
4
- require 'premonition/hook'
4
+ require_relative 'premonition/commands/install_scss.rb'
5
+ require_relative 'premonition/attributes/error'
6
+ require_relative 'premonition/attributes/stacker'
7
+ require_relative 'premonition/attributes/parser'
8
+ require_relative 'premonition/version'
9
+ require_relative 'premonition/processor'
10
+ require_relative 'premonition/resources'
11
+ require_relative 'premonition/hook'
5
12
 
6
13
  module Jekyll
7
14
  module Premonition
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'strscan'
4
+
5
+ module Jekyll
6
+ module Premonition
7
+ module Attributes
8
+ # Public: Custom error for Premonition attributes parser errors.
9
+ class ParserError < StandardError
10
+ # Initialize a new ParserError
11
+ #
12
+ # msg - The error message
13
+ # raw - The raw string send to the parser initially.
14
+ # Used for syntax error output. If nil syntax
15
+ # error output is skipped.
16
+ # pos - The buffer position when error was raised.
17
+ # Used in both error message and syntax error output
18
+ # if raw attribute is set.
19
+ def initialize(msg, raw = nil, pos = 0)
20
+ if raw.nil?
21
+ super(msg)
22
+ else
23
+ super("#{msg} [#{pos}:#{raw.length}]")
24
+ print "Attribute syntax error:\n #{raw}\n"
25
+ pos.times { print ' ' }
26
+ print "^\n"
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,168 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'strscan'
4
+
5
+ module Jekyll
6
+ module Premonition
7
+ module Attributes
8
+ # Public: Premonition block attributes parser.
9
+ #
10
+ # Parses attributes found in block headers like this:
11
+ # > "info" [ foo = abcd, bar = "zot", size = 3 ]
12
+ #
13
+ # The parser itself utilizes the StringScanner in ruby.
14
+ # Each character will be parsed, validated and pushed
15
+ # to a stack (array) according to the rules inside the
16
+ # parser itself.
17
+ #
18
+ # A stack object will be of a certain type:
19
+ #
20
+ # 0 : Outside an attributes block
21
+ # 1 : Inside an attributes block, but between keys or values
22
+ # 2 : Parsing an attribute key
23
+ # 3: Parsing an attribute value
24
+ #
25
+ # Upon parser errors a pretty syntax error will be printed to
26
+ # stdout, showing where the error is.
27
+ class Parser
28
+ # Get parsed attributes as a Hash
29
+ attr_reader :attributes
30
+
31
+ # Initialize a new Parser AND start parsing
32
+ #
33
+ # str - A string containing the attributes block to be parser.
34
+ def initialize(str)
35
+ @raw = str # Keeps th original string for later use
36
+ @buffer = StringScanner.new(str) # Create StringScanner buffer
37
+ @attributes = {} # Initialize the attributes hash
38
+ @stack = [Stacker.new(0)] # Initialize the parser stack with initial "state"
39
+ parse # Start parsing
40
+ end
41
+
42
+ private
43
+
44
+ def parse
45
+ raise error('No attributes block found in given string') unless @raw.match(/^.*\[.*\].*/)
46
+
47
+ until @buffer.eos?
48
+ char = @buffer.getch
49
+ case @stack.last.type
50
+ when 0 # Outside block mode
51
+ push_stacker(1) if char == '['
52
+ when 1 # In between
53
+ parse_in_between(char)
54
+ when 2 # Key
55
+ parse_key(char)
56
+ when 3 # Value
57
+ parse_value(char)
58
+ end
59
+ end
60
+ end
61
+
62
+ def parse_in_between(char)
63
+ return if char == ' ' # Ignoring all spaces before, between and after keys and values.
64
+
65
+ if char == ']'
66
+ pop_attribute_from_stack
67
+ push_stacker(4)
68
+ elsif char == ','
69
+ raise error("Attribute separator ',' not allowed here.") if @attributes.length.zero?
70
+ push_stacker(2)
71
+ elsif char.match(/^[a-zA-z0-9\-_]$/)
72
+ push_stacker(2)
73
+ append_char(char)
74
+ else
75
+ raise error("Illegal character '#{char}' outside key and value")
76
+ end
77
+ end
78
+
79
+ def parse_key(char)
80
+ if char == '='
81
+ push_stacker(3)
82
+ value_mode('plain')
83
+ elsif char.match(/^[a-zA-z0-9\-_]$/)
84
+ append_char(char)
85
+ elsif char == ' '
86
+ m = @buffer.scan(/\s*\=\s*/)
87
+ raise error('Space not allowed inside attribute key') if m.nil?
88
+ push_stacker(3)
89
+ value_mode('plain')
90
+ else
91
+ raise error("Illegal character '#{char}' for attribute key")
92
+ end
93
+ end
94
+
95
+ def parse_value(char)
96
+ case char
97
+ when ']'
98
+ if plain_mode?
99
+ pop_attribute_from_stack
100
+ push_stacker(4)
101
+ end
102
+ when '"'
103
+ pop_attribute_from_stack unless plain_mode?
104
+ raise error('Illegal " found') unless @stack.last.value.nil? || @stack.last.value.empty?
105
+ value_mode('block')
106
+ when '\\'
107
+ raise error('Backslash is not allowed in unquoted values') if plain_mode?
108
+ raise error('Unsupported escaping of character inside string block') unless ['\\', '"'].include?(@buffer.peek(1))
109
+ append_char(@buffer.peek(1))
110
+ @buffer.pos += 1
111
+ when ','
112
+ if plain_mode?
113
+ pop_attribute_from_stack
114
+ push_stacker(1)
115
+ else
116
+ append_char(char)
117
+ end
118
+ when '='
119
+ raise error('Illegal use of equals character in unquoted value') if plain_mode?
120
+ append_char(char)
121
+ when ' '
122
+ if plain_mode?
123
+ raise error('Illegal spacing in unquoted value') if @buffer.check(/\s*[,\]]/).nil?
124
+ pop_attribute_from_stack
125
+ push_stacker(1)
126
+ else
127
+ append_char(char)
128
+ end
129
+ else
130
+ append_char(char)
131
+ end
132
+ end
133
+
134
+ def error(msg)
135
+ ParserError.new(msg, @raw, @buffer.pos)
136
+ end
137
+
138
+ def push_stacker(type)
139
+ @stack << Stacker.new(type)
140
+ end
141
+
142
+ def append_char(char)
143
+ @stack.last.append(char)
144
+ end
145
+
146
+ def pop_attribute_from_stack
147
+ v = @stack.pop
148
+ return if [0, 1, 2].include?(v.type) # Ignore these types from stack
149
+ k = @stack.pop
150
+ raise error("Expected key but got: #{k.value}, #{k.type}") unless k.type == 2
151
+ @attributes[k.value] = v.value
152
+ end
153
+
154
+ def plain_mode?
155
+ @stack.last.meta['mode'] == 'plain'
156
+ end
157
+
158
+ def block_mode?
159
+ @stack.last.meta['mode'] == 'block'
160
+ end
161
+
162
+ def value_mode(str)
163
+ @stack.last.meta['mode'] = str
164
+ end
165
+ end
166
+ end
167
+ end
168
+ end
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'strscan'
4
+
5
+ module Jekyll
6
+ module Premonition
7
+ module Attributes
8
+ # Instances of this class are pushed onto the parser stack upon parsing of block attributes.
9
+ class Stacker
10
+ # Get the string value from the stacker
11
+ attr_reader :value
12
+ # Get the stacker type. 0 = outside block, 1 = in_between, 2 = key, 3 = value
13
+ attr_reader :type
14
+ # Get and set meta attributes for stacker. Used for setting value mode.
15
+ attr_accessor :meta
16
+
17
+ # Initialize a new Stacker
18
+ #
19
+ # type - The stacker type
20
+ def initialize(type)
21
+ @value = nil
22
+ @type = type
23
+ @meta = {}
24
+ end
25
+
26
+ # Append a char to the stacker value
27
+ def append(char)
28
+ @value = @value.nil? ? char : "#{@value}#{char}"
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,65 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'net/http'
4
+
5
+ module Jekyll
6
+ module Premonition
7
+ # Public: Premonition stylesheet installer command
8
+ #
9
+ # Jekyll command that will install the Premonition stylesheet and add it to the needed resource files.
10
+ class InstallScssCommand < Jekyll::Command
11
+ class << self
12
+ def init_with_program(prog)
13
+ prog.command(:"premonition-install") do |c|
14
+ c.syntax 'premonition-install [options]'
15
+ c.description 'Install Premonition SASS stylesheet into your Jekyll site.'
16
+ c.option 'dest', '-d DEST', 'Where premonition.scss should be stored. (defaults to _sass/)'
17
+ c.option 'main', '-m DEST', 'The path to your main SASS file (defaults to assets/main.scss)'
18
+ c.action do |args, opts|
19
+ Jekyll::Premonition::InstallScssCommand.process(args, opts)
20
+ end
21
+ end
22
+ end
23
+
24
+ def process(_args, opts)
25
+ puts '== Installing Premonition Stylesheet and resources =="'
26
+ puts '-- premonition.scss --'
27
+ dest = opts['dest'].nil? || opts['dest'].empty? ? './_sass/' : File.join(opts['dest'], '')
28
+ main = opts['main'].nil? || opts['main'].empty? ? './assets/css/styles.scss' : opts['main']
29
+ raise StandardError, "#{dest} folder does not exist. Create it manually, and run script again." unless Dir.exist?(dest)
30
+ raise StandardError, "#{main} does not exist" unless File.exist?(main)
31
+
32
+ uri = URI('https://raw.githubusercontent.com/lazee/premonition/master/stylesheet/premonition.scss')
33
+ pf = "#{dest}premonition.scss"
34
+
35
+ if File.exist?(pf)
36
+ print "You already have #{pf} installed. Continue? [Y/n]"
37
+ override = gets.chomp
38
+ raise StandardError, 'Aborted. No harm done.' if override == 'n'
39
+ end
40
+
41
+ File.write(pf, Net::HTTP.get(uri))
42
+ puts "Saved stylesheet in #{pf}"
43
+
44
+ if File.readlines(main).grep(/\@import \"premonition\"/).any?
45
+ puts "#{main} already imports the Premonition stylesheet."
46
+ else
47
+ puts 'You must add Premonition to your main SASS file.'
48
+ print "Add '@import \"premonition\";' to end of #{main} now? [Y/n] "
49
+ add = gets.chomp
50
+ if add == 'n'
51
+ puts 'Ok. But then you will need to add it manually for the styling of Premonition to work.'
52
+ else
53
+ f = File.open(main, 'a')
54
+ f.write("\n@import \"premonition\";\n")
55
+ f.close
56
+ puts "Added Premonition stylesheet to #{main}."
57
+ end
58
+ end
59
+ rescue StandardError => e
60
+ puts "ERROR: #{e}"
61
+ end
62
+ end
63
+ end
64
+ end
65
+ end
@@ -1,82 +1,49 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Jekyll
2
4
  module Premonition
5
+ # Registers Premonition hooks in Jekyll.
6
+ #
7
+ # Two hooks are added. One general hook for pages and another special
8
+ # hook for dealing with excerpts within posts.
9
+ #
10
+ # This ladder is really a hack as we scan all Markdown files and insert the
11
+ # excerpt ourselves in the document data. Unfortunately Jekyll prepares
12
+ # the excerpt way to early in the process, preventing us from hooking
13
+ # into it in a prober way. We only support excerpts if the excerpt_separator
14
+ # is explicitly set: https://jekyllrb.com/docs/posts/#post-excerpts
3
15
  class Hook < Generator
4
16
  safe true
5
- priority :low
17
+ priority :high
6
18
 
7
19
  def initialize(p)
8
20
  super(p)
9
21
  end
10
22
 
11
23
  def generate(site)
12
- @resources = Resources.new site.config
13
- Hooks.register [:documents, :pages], :pre_render do |doc|
14
- adder(doc)
15
- end
16
- end
24
+ resources = Resources.new site.config
25
+ processor = Processor.new resources
17
26
 
18
- def adder(doc)
19
- o = []
20
- b = nil
21
- doc.content.each_line do |l|
22
- if blockquote?(l) && empty_block?(b)
23
- if (m = l.match(/^\s*\>\s+([a-z]+)\s+\"(.*)\"$/i))
24
- y, t = m.captures
25
- b = { 'title' => t.strip, 'type' => y.strip.downcase, 'content' => [] }
26
- else
27
- o << l
28
- end
29
- elsif blockquote?(l) && !empty_block?(b)
30
- b['content'] << l.match(/^\s*\>\s?(.*)$/i).captures[0]
31
- else
32
- if !blockquote?(l) && !empty_block?(b)
33
- o << render_block(b)
34
- b = nil
35
- end
36
- o << l
27
+ Hooks.register [:posts], :pre_render do |doc|
28
+ if process?(resources, doc)
29
+ doc.content = processor.adder(doc.content)
30
+ doc.data['excerpt'] = Jekyll::Excerpt.new(doc) if generate_excerpt? doc
37
31
  end
38
32
  end
39
- o << render_block(b) unless empty_block?(b)
40
- doc.content = o.join
41
- end
42
33
 
43
- def blockquote?(l)
44
- l.strip.start_with?('>')
34
+ Hooks.register [:pages], :pre_render do |doc|
35
+ doc.content = processor.adder(doc.content) if process?(resources, doc)
36
+ end
45
37
  end
46
38
 
47
- def empty_block?(b)
48
- b.nil?
49
- end
39
+ private
50
40
 
51
- def render_block(b)
52
- t = create_resource(b)
53
- c = "#{@resources.markdown.convert(b['content'].join("\n"))}\n\n"
54
- template = Liquid::Template.parse(t['template'], error_mode: :strict)
55
- template.render(
56
- {
57
- 'header' => !t['title'].nil?,
58
- 'title' => t['title'],
59
- 'content' => c,
60
- 'type' => b['type'],
61
- 'meta' => t['meta']
62
- },
63
- strict_variables: true
64
- )
41
+ def generate_excerpt?(doc)
42
+ !doc.data['excerpt_separator'].nil? && !doc.data['excerpt_separator'].empty?
65
43
  end
66
44
 
67
- def create_resource(b)
68
- c = {
69
- 'template' => @resources.config['default']['template'],
70
- 'title' => @resources.config['default']['title'],
71
- 'meta' => @resources.config['default']['meta']
72
- }
73
- @resources.config['types'].each do |id, t|
74
- next unless id == b['type']
75
- c['title'] = b['title'].empty? || b['title'].nil? ? t['default_title'] : b['title']
76
- c['template'] = t['template'] unless t['template'].nil?
77
- c['meta'] = c['meta'].merge(t['meta']) unless t['meta'].nil?
78
- end
79
- c
45
+ def process?(resources, doc)
46
+ resources.config['extensions'].include?(File.extname(doc.relative_path)[1..-1])
80
47
  end
81
48
  end
82
49
  end
@@ -0,0 +1,137 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Jekyll
4
+ module Premonition
5
+ # Class that does all of the rendering magic within Premonition.
6
+ class Processor
7
+ def initialize(resources)
8
+ @resources = resources
9
+ end
10
+
11
+ # Called by the registered pre_render hooks.
12
+ #
13
+ # This function takes markdown content as input and converts
14
+ # all the block quotes, with a Premonition header, into html.
15
+ #
16
+ # content - Markdown content
17
+ def adder(content)
18
+ o = []
19
+ references = load_references(content)
20
+ b = nil
21
+ is_code_block = false
22
+ content.each_line do |l|
23
+ is_code_block = !is_code_block if code_block_line?(l)
24
+ if is_code_block
25
+ o << l
26
+ elsif blockquote?(l) && empty_block?(b)
27
+ if (m = l.to_s.match(/^\s*\>\s+([a-z]+)\s+\"(.*)\"\s+(\[.*\])?\s*$/i))
28
+ y, t, attrs = m.captures
29
+ b = { 'title' => t.strip, 'type' => y.strip.downcase, 'content' => [], 'attrs' => attrs }
30
+ else
31
+ o << l
32
+ end
33
+ elsif blockquote?(l) && !empty_block?(b)
34
+ b['content'] << l.to_s.match(/^\s*\>\s?(.*)$/i).captures[0]
35
+ else
36
+ if !blockquote?(l) && !empty_block?(b)
37
+ o << render_block(b, references)
38
+ b = nil
39
+ end
40
+ o << l
41
+ end
42
+ end
43
+ o << render_block(b, references) unless empty_block?(b)
44
+ o.join
45
+ end
46
+
47
+ private
48
+
49
+ # Find all the Kramdown reference name links.
50
+ # https://kramdown.gettalong.org/quickref.html#links-and-images
51
+ # https://github.com/lazee/premonition/issues/10
52
+ def load_references(content)
53
+ refs = ["\n"]
54
+ content.each_line do |l|
55
+ unless l.nil? || l == 0 || (l.is_a? String)
56
+ refs << l if l.to_s.strip!.match(/^\[.*\]:.*\".*\"$/i)
57
+ end
58
+ end
59
+ refs
60
+ end
61
+
62
+ def code_block_line?(line)
63
+ line.strip.start_with?('~~~') || line.strip.start_with?('```')
64
+ end
65
+
66
+ def blockquote?(line)
67
+ line.strip.start_with?('>')
68
+ end
69
+
70
+ def empty_block?(block)
71
+ block.nil?
72
+ end
73
+
74
+ def render_block(block, references)
75
+ t = create_resource(block)
76
+ a = block['content'] + references
77
+ c = "#{@resources.markdown.convert(a.join("\n"))}\n\n"
78
+ attrs = {}
79
+
80
+ unless block['attrs'].nil?
81
+ parser = Jekyll::Premonition::Attributes::Parser.new(block['attrs'])
82
+ attrs = parser.attributes
83
+ end
84
+
85
+ template = Liquid::Template.parse(t['template'], error_mode: :strict)
86
+ template.render(
87
+ {
88
+ 'header' => !t['title'].nil?,
89
+ 'title' => t['title'],
90
+ 'content' => c,
91
+ 'type' => block['type'],
92
+ 'meta' => t['meta'],
93
+ 'attrs' => attrs
94
+ },
95
+ strict_variables: true
96
+ )
97
+ end
98
+
99
+ def error_template
100
+ <<~TEMPLATE
101
+ <div class="premonition error">
102
+ <div class="fa {{meta.pn-icon}}"></div>
103
+ <div class="content">
104
+ <p class="header">PREMONITION ERROR: Invalid box type</p>
105
+ You have specified an invalid box type "{{type}}". You can customize your own box types in `_config.yml`.
106
+ See documentation for more help.
107
+ </div>
108
+ </div>
109
+ TEMPLATE
110
+ end
111
+
112
+ def create_resource(block)
113
+ c = {
114
+ 'template' => @resources.config['default']['template'],
115
+ 'title' => @resources.config['default']['title'],
116
+ 'meta' => @resources.config['default']['meta']
117
+ }
118
+
119
+ unless @resources.config['types'].include? block['type']
120
+ c['title'] = ''
121
+ c['meta'] = { 'pn-icon' => 'pn-error' }
122
+ c['template'] = error_template
123
+ return c
124
+ end
125
+
126
+ @resources.config['types'].each do |id, t|
127
+ next unless id == block['type']
128
+
129
+ c['title'] = block['title'].empty? || block['title'].nil? ? t['default_title'] : block['title']
130
+ c['template'] = t['template'] unless t['template'].nil?
131
+ c['meta'] = c['meta'].merge(t['meta']) unless t['meta'].nil?
132
+ end
133
+ c
134
+ end
135
+ end
136
+ end
137
+ end
@@ -1,14 +1,19 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Jekyll
2
4
  module Premonition
5
+ # Class for loading the Premonition configuration and preparing it for use.
3
6
  class Resources
4
7
  attr_reader :config
5
8
  attr_reader :markdown
6
9
 
7
10
  def initialize(site_config)
8
11
  @config = load site_config
12
+ # Setup a new Markdown renderer.
9
13
  @markdown = Converters::Markdown.new site_config
10
14
  end
11
15
 
16
+ # Load the configuration of Premonition from Jekyll site configuration object.
12
17
  def load(site_config)
13
18
  cfg = default_config
14
19
  p = site_config['premonition'] || {}
@@ -18,33 +23,69 @@ module Jekyll
18
23
  cfg['default']['title'] = df['title'].strip unless df['title'].nil?
19
24
  cfg['default']['meta'] = cfg['default']['meta'].merge(df['meta']) unless df['meta'].nil?
20
25
  load_types p, cfg
26
+ load_extensions p, cfg
21
27
  cfg
22
28
  end
23
29
 
30
+ def default_template
31
+ <<~TEMPLATE
32
+ <div class="premonition {% if meta.style %}{{meta.style}} {% endif %}{{type}}">
33
+ <i class="{% if meta.fa-icon %}fas {{meta.fa-icon}}{% else %}premonition {{meta.pn-icon}}{% endif %}"></i>
34
+ <div class="content">
35
+ {% if header %}<p class="header">{{title}}</p>{% endif %}{{content}}
36
+ </div>
37
+ </div>
38
+ TEMPLATE
39
+ end
40
+
41
+ def citation_template
42
+ <<~TEMPLATE
43
+ <div class="premonition {% if meta.style %}{{meta.style}} {% endif %}{{type}}">
44
+ <i class="{% if meta.fa-icon %}fas {{meta.fa-icon}}{% else %}premonition {{meta.pn-icon}}{% endif %}"></i>
45
+ <blockquote class="content blockquote"{% if attrs.cite %} cite="{{attrs.cite}}"{% endif %}>
46
+ {{content}}
47
+ {% if header %}
48
+ <footer class="blockquote-footer">
49
+ <cite title="{{title}}">{{title}}</cite>
50
+ </footer>
51
+ {% endif %}
52
+ </blockquote>
53
+ </div>
54
+ TEMPLATE
55
+ end
56
+
57
+ # Setup the default configuration and types
24
58
  def default_config
25
59
  {
26
60
  'default' => {
27
- 'template' => '<div class="premonition {{type}}"><div class="fa {{meta.fa-icon}}"></div>'\
28
- '<div class="content">{% if header %}<p class="header">{{title}}</p>{% endif %}{{content}}</div></div>',
29
- 'meta' => { 'fa-icon' => 'fa-check-square' },
61
+ 'template' => default_template,
62
+ 'meta' => { 'pn-icon' => 'pn-square', 'fa-icon' => nil },
30
63
  'title' => nil
31
64
  },
32
65
  'types' => {
33
- 'note' => { 'meta' => { 'fa-icon' => 'fa-check-square' } },
34
- 'info' => { 'meta' => { 'fa-icon' => 'fa-info-circle' } },
35
- 'warning' => { 'meta' => { 'fa-icon' => 'fa-exclamation-circle' } },
36
- 'error' => { 'meta' => { 'fa-icon' => 'fa-exclamation-triangle' } }
37
- }
66
+ 'note' => { 'meta' => { 'pn-icon' => 'pn-note' } },
67
+ 'info' => { 'meta' => { 'pn-icon' => 'pn-info' } },
68
+ 'warning' => { 'meta' => { 'pn-icon' => 'pn-warn' } },
69
+ 'error' => { 'meta' => { 'pn-icon' => 'pn-error' } },
70
+ 'citation' => { 'meta' => { 'pn-icon' => 'pn-quote' }, 'template' => citation_template }
71
+ },
72
+ 'extensions' => %w[
73
+ md
74
+ markdown
75
+ ]
38
76
  }
39
77
  end
40
78
 
79
+ # Basic configuration validation
41
80
  def validate_defaults(df, prem)
42
81
  fail 'meta must be a hash' if !df['meta'].nil? && !df['meta'].is_a?(Hash)
43
82
  fail 'types must be a hash' if !prem['types'].nil? && !prem['types'].is_a?(Hash)
44
83
  end
45
84
 
85
+ # Load extra Premonition types configured/overriden in _config.yml
46
86
  def load_types(p, cfg)
47
87
  return if p['types'].nil?
88
+
48
89
  p['types'].each do |id, obj|
49
90
  t = type_config id, obj
50
91
  cfg['types'][id] = cfg['types'][id].merge(t) unless cfg['types'][id].nil?
@@ -52,6 +93,21 @@ module Jekyll
52
93
  end
53
94
  end
54
95
 
96
+ # Load extra extensions from config.
97
+ #
98
+ # We need this when looking for excerpts
99
+ def load_extensions(p, cfg)
100
+ return if p['extensions'].nil?
101
+ return unless p['extensions'].is_a?(Array)
102
+ return if p['extensions'].empty?
103
+
104
+ cfg['extensions'] = []
105
+ p['extensions'].each do |v|
106
+ cfg['extensions'] << v unless cfg['extensions'].include?(v)
107
+ end
108
+ end
109
+
110
+ # Validate a configured type and return as type hash
55
111
  def type_config(id, t)
56
112
  validate_type(id, t)
57
113
  {
@@ -61,6 +117,7 @@ module Jekyll
61
117
  }
62
118
  end
63
119
 
120
+ # Type validation
64
121
  def validate_type(id, t)
65
122
  fail 'id missing from type' if id.nil?
66
123
  fail "id can only be lowercase letters: #{id}" unless id[/[a-z]+/] == id
@@ -1,5 +1,8 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Jekyll
2
4
  module Premonition
3
- VERSION = '2.0.1'.freeze
5
+ # Make sure to change this manually before a release.
6
+ VERSION = '4.0.1'
4
7
  end
5
8
  end
metadata CHANGED
@@ -1,77 +1,125 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: premonition
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.0.1
4
+ version: 4.0.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jakob Vad Nielsen
8
- autorequire:
8
+ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-02-27 00:00:00.000000000 Z
11
+ date: 2021-01-12 00:00:00.000000000 Z
12
12
  dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: jekyll
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '3.7'
20
+ - - "<"
21
+ - !ruby/object:Gem::Version
22
+ version: '5.0'
23
+ type: :runtime
24
+ prerelease: false
25
+ version_requirements: !ruby/object:Gem::Requirement
26
+ requirements:
27
+ - - ">="
28
+ - !ruby/object:Gem::Version
29
+ version: '3.7'
30
+ - - "<"
31
+ - !ruby/object:Gem::Version
32
+ version: '5.0'
13
33
  - !ruby/object:Gem::Dependency
14
34
  name: bundler
15
35
  requirement: !ruby/object:Gem::Requirement
16
36
  requirements:
17
37
  - - "~>"
18
38
  - !ruby/object:Gem::Version
19
- version: '1.5'
39
+ version: 2.1.4
20
40
  type: :development
21
41
  prerelease: false
22
42
  version_requirements: !ruby/object:Gem::Requirement
23
43
  requirements:
24
44
  - - "~>"
25
45
  - !ruby/object:Gem::Version
26
- version: '1.5'
46
+ version: 2.1.4
27
47
  - !ruby/object:Gem::Dependency
28
- name: jekyll
48
+ name: mocha
49
+ requirement: !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - "~>"
52
+ - !ruby/object:Gem::Version
53
+ version: 1.11.2
54
+ type: :development
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ requirements:
58
+ - - "~>"
59
+ - !ruby/object:Gem::Version
60
+ version: 1.11.2
61
+ - !ruby/object:Gem::Dependency
62
+ name: rake
63
+ requirement: !ruby/object:Gem::Requirement
64
+ requirements:
65
+ - - "~>"
66
+ - !ruby/object:Gem::Version
67
+ version: 13.0.1
68
+ type: :development
69
+ prerelease: false
70
+ version_requirements: !ruby/object:Gem::Requirement
71
+ requirements:
72
+ - - "~>"
73
+ - !ruby/object:Gem::Version
74
+ version: 13.0.1
75
+ - !ruby/object:Gem::Dependency
76
+ name: rubocop
29
77
  requirement: !ruby/object:Gem::Requirement
30
78
  requirements:
31
79
  - - ">="
32
80
  - !ruby/object:Gem::Version
33
- version: '2.0'
81
+ version: 0.68.0
34
82
  - - "<"
35
83
  - !ruby/object:Gem::Version
36
- version: '3.0'
84
+ version: 0.72.0
37
85
  type: :development
38
86
  prerelease: false
39
87
  version_requirements: !ruby/object:Gem::Requirement
40
88
  requirements:
41
89
  - - ">="
42
90
  - !ruby/object:Gem::Version
43
- version: '2.0'
91
+ version: 0.68.0
44
92
  - - "<"
45
93
  - !ruby/object:Gem::Version
46
- version: '3.0'
94
+ version: 0.72.0
47
95
  - !ruby/object:Gem::Dependency
48
- name: mocha
96
+ name: rubocop-jekyll
49
97
  requirement: !ruby/object:Gem::Requirement
50
98
  requirements:
51
99
  - - "~>"
52
100
  - !ruby/object:Gem::Version
53
- version: 1.11.2
101
+ version: 0.10.0
54
102
  type: :development
55
103
  prerelease: false
56
104
  version_requirements: !ruby/object:Gem::Requirement
57
105
  requirements:
58
106
  - - "~>"
59
107
  - !ruby/object:Gem::Version
60
- version: 1.11.2
108
+ version: 0.10.0
61
109
  - !ruby/object:Gem::Dependency
62
- name: rake
110
+ name: test-unit
63
111
  requirement: !ruby/object:Gem::Requirement
64
112
  requirements:
65
113
  - - "~>"
66
114
  - !ruby/object:Gem::Version
67
- version: 13.0.1
115
+ version: 3.3.5
68
116
  type: :development
69
117
  prerelease: false
70
118
  version_requirements: !ruby/object:Gem::Requirement
71
119
  requirements:
72
120
  - - "~>"
73
121
  - !ruby/object:Gem::Version
74
- version: 13.0.1
122
+ version: 3.3.5
75
123
  - !ruby/object:Gem::Dependency
76
124
  name: turn
77
125
  requirement: !ruby/object:Gem::Requirement
@@ -86,7 +134,7 @@ dependencies:
86
134
  - - "~>"
87
135
  - !ruby/object:Gem::Version
88
136
  version: 0.9.7
89
- description:
137
+ description:
90
138
  email:
91
139
  - jakobvadnielsen@gmail.com
92
140
  executables: []
@@ -97,14 +145,19 @@ files:
97
145
  - LICENSE
98
146
  - README.md
99
147
  - lib/premonition.rb
148
+ - lib/premonition/attributes/error.rb
149
+ - lib/premonition/attributes/parser.rb
150
+ - lib/premonition/attributes/stacker.rb
151
+ - lib/premonition/commands/install_scss.rb
100
152
  - lib/premonition/hook.rb
153
+ - lib/premonition/processor.rb
101
154
  - lib/premonition/resources.rb
102
155
  - lib/premonition/version.rb
103
156
  homepage: http://github.com/lazee/premonition
104
157
  licenses:
105
158
  - MIT
106
159
  metadata: {}
107
- post_install_message:
160
+ post_install_message:
108
161
  rdoc_options: []
109
162
  require_paths:
110
163
  - lib
@@ -120,8 +173,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
120
173
  - !ruby/object:Gem::Version
121
174
  version: '0'
122
175
  requirements: []
123
- rubygems_version: 3.0.3
124
- signing_key:
176
+ rubygems_version: 3.1.2
177
+ signing_key:
125
178
  specification_version: 4
126
179
  summary: Jekyll generator that will convert special block quotes into message boxes.
127
180
  test_files: []