premonition 2.0.1 → 4.0.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 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: []