jekyll-target-blank 1.0 → 1.1.0.beta

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
- SHA1:
3
- metadata.gz: 7d8da96186acb1475407698753b3b1fcd72be1d6
4
- data.tar.gz: 98cf97e09c6fbfeb0c3400c7d5603bb433ebed44
2
+ SHA256:
3
+ metadata.gz: ef384a621db70253811f8aa3996b93419b9f61538ea9475bc743bad36c9473cd
4
+ data.tar.gz: e839aa7f4014df3c6ae6b86afb876478ef5867d6618996b38b913a9af6e716c4
5
5
  SHA512:
6
- metadata.gz: 8d1792a7fd4dff808bfb0baeb8b09dbae3a871cb63a21433e82203c0e6cbd1c42d30dee9b3ce3862355c1958047e809caabb50c8cacd02c0a5efcd0fb98f8986
7
- data.tar.gz: 1b53f91c41e53238f8101847240c8e0a08843713318859e8be43c0af3fc995988802e5efbb1547dd9e70204d3b248d255cc2960789649baeb3cb5504a853eee2
6
+ metadata.gz: 4b9a0c884e99230342b2967339fd31e628856c2eb161b5883a1fc8916dc06162afc0bdd203b0191a1b13a7842c1f0725caf598d52f51145daa09a77cf31c8a55
7
+ data.tar.gz: 00dab4bc2b1baad40c54b91628593fd488c530a5ee7db72b8fca247af9a84d7cde2a71d267837c62f8cb6ef76905cf436be06809757a09de598bd7034d500117
data/.gitignore CHANGED
@@ -1,4 +1,5 @@
1
1
  .idea
2
2
  /*.gem
3
3
  Gemfile.lock
4
- dev_notes.md
4
+ dev_notes.md
5
+ .vscode
data/README.md CHANGED
@@ -1,6 +1,8 @@
1
1
  # Jekyll Target Blank
2
2
 
3
- Automatically adds a `target="_blank"` attribute to all __external__ links in Jekyll Content.
3
+ ![Jekyll Target Blank Logo](assets/logo.png "Jekyll Target Blank")
4
+
5
+ Automatically adds a `target="_blank" rel="noopener noreferrer"` attribute to all __external__ links in Jekyll's content plus several other automation features for the external links. [Read more here](https://keith-mifsud.me/projects/jekyll-target-blank)
4
6
 
5
7
  [![Gem Version](https://badge.fury.io/rb/jekyll-target-blank.svg)](https://badge.fury.io/rb/jekyll-target-blank)
6
8
  [![Build Status](https://travis-ci.org/keithmifsud/jekyll-target-blank.svg?branch=master)](https://travis-ci.org/keithmifsud/jekyll-target-blank)
@@ -13,7 +15,7 @@ Add the following to your site's `Gemfile`
13
15
  gem 'jekyll-target-blank'
14
16
  ```
15
17
 
16
- And add the following to your site's `_config.yml`
18
+ and add the following to your site's `_config.yml`
17
19
 
18
20
  ```yml
19
21
  plugins:
@@ -29,15 +31,15 @@ gems:
29
31
 
30
32
  ## Usage
31
33
 
32
- All anchor tags and markdown links pointing to an external host, other than the one listed as `url` in jekyll's `_config.yml` will automatically open in a new tab once the site has been generated.
34
+ By default. all anchor tags and markdown links pointing to an external host, other than the one listed as the `url` in Jekyll's `_config.yml` will automatically be opened in a new browser tab once the site is generated.
33
35
 
34
- This includes pages, posts and collections. __Plain text links are not included__.
36
+ All the links in pages, posts and custom collections are included except for __plain text links.
35
37
 
36
38
  ### Examples
37
39
 
38
40
  #### HTML
39
41
 
40
- The following html anchor tag:
42
+ The following `HTML` anchor tag:
41
43
 
42
44
  ```html
43
45
  <a href="https://google.com">Google</a>
@@ -46,7 +48,7 @@ The following html anchor tag:
46
48
  will be replaced with:
47
49
 
48
50
  ```html
49
- <a href="https://google.com" target="_blank">Google</a>
51
+ <a href="https://google.com" target="_blank" rel="noopener noreferrer">Google</a>
50
52
  ```
51
53
 
52
54
  ..unless your website's URL is google.com 😉
@@ -60,19 +62,109 @@ will be replaced with:
60
62
  will be generated as:
61
63
 
62
64
  ```html
63
- <a href="https://google.com" target="_blank">Google</a>
65
+ <a href="https://google.com" target="_blank" rel="noopener noreferrer">Google</a>
66
+ ```
67
+
68
+ ### Configuration
69
+
70
+ No custom configuration is needed for using this plugin, however, you can override some default behaviours and also make use of some extra features as explained in this section.
71
+
72
+ #### Override the default behaviour
73
+
74
+ You can override the default behaviour and only force external links to open in new browser if they have a CSS class name included with the same value as the one listed in the Jekyll `_config.yml` file.
75
+
76
+ To override this automation, add an entry in your site's `config.yml` file, specifying which CSS class name a link must have for it to be forced to open in a new browser:
77
+
78
+ ```yaml
79
+ target-blank:
80
+ css_class: ext-link
81
+ ```
82
+
83
+ With the above setting, only links containing the `class="ext-link"` attribute will be forced to open in a new browser.
84
+
85
+ #### Automatically add additional CSS Classes
86
+
87
+ You can also automatically add additional CSS classes to qualifying external links. This feature is useful when you want to add CSS styling to external links such as automatically displaying an icon to show the reader that the link will open in a new browser.
88
+
89
+ You can add one or more __space__ separated CSS classes in `_config.yml` like so:
90
+
91
+ ```yaml
92
+ target-blank:
93
+ add_css_classes: css-class-one css-class-two
94
+ ```
95
+
96
+ The above example will add `class="css-class-one css-class-two"` to the generated anchor tag. These CSS class names will be added in addition to any other existing CSS class names of a link.
97
+
98
+ #### Override the default rel attributes
99
+
100
+ For security reasons, `rel="noopener noreferrer"` are added by default to all the processed external links. You can override adding any of the `noopener` and `noreferrer` values with the following entries in your site's `_config.yml` file.
101
+
102
+ __To exclude the `noopener` value:__
103
+
104
+ ```yaml
105
+ target-blank:
106
+ noopener: false
107
+ ```
108
+
109
+ __To exclude the `noreferrer` value:__
110
+
111
+ ```yaml
112
+ target-blank:
113
+ noreferrer: false
114
+ ```
115
+
116
+ __To exclude both `noopner` and `noreferrer` values:__
117
+
118
+ ```yaml
119
+ target-blank:
120
+ noopener: false
121
+ noreferrer: false
122
+ ```
123
+
124
+ #### Adding additional rel attribute values
125
+
126
+ You can add additional `rel=""` attribute values by simply specifying them in your site's `_config.yml` file.
127
+
128
+ ```yaml
129
+ target-blank:
130
+ rel: nofollow
131
+ ```
132
+
133
+ or even more than one extra:
134
+
135
+ ```yaml
136
+ target-blank:
137
+ rel: nofollow
138
+ ```
139
+
140
+ __Note:__
141
+
142
+
143
+ The `rel` setting overrides other default `rel` attribute values. Therefore, (for example), if you exclude the `noopener` value and then add it to the `rel` property, it will still be added. The following `config`:
144
+
145
+ ```yaml
146
+ target-blank:
147
+ noopener: false
148
+ rel: noopener
149
+ ```
150
+
151
+ will output:
152
+
153
+ ```html
154
+ <a href"https://some-external-website.what" target="_blank" rel="noreferrer noopener">Some link</a>
64
155
  ```
65
156
 
157
+
66
158
  ## Support
67
159
 
68
160
  Simply [create an issue](https://github.com/keithmifsud/jekyll-target-blank/issues/new) and I will respond as soon as possible.
69
161
 
70
-
162
+
71
163
  ## Contributing
72
164
 
73
165
  1. [Fork it](https://github.com/keithmifsud/jekyll-target-blank/fork)
74
166
  2. Create your feature branch (`git checkout -b my-new-feature)
75
- 3. Commit your changes (`git commit -am 'Add some feature'`)
167
+ 3. Commit your changes (`git commit -m 'Add some feature'`)
76
168
  4. Push to the branch (git push origin my-new-feature)
77
169
  4. Create a new Pull Request
78
170
 
@@ -85,6 +177,11 @@ rake spec
85
177
  rspec
86
178
  ```
87
179
 
180
+ ## Credits
181
+
182
+ The logo illustration was <a href="http://www.freepik.com">Designed by Freepik</a>. Thank you ❤️
183
+
184
+
88
185
  ## Legal
89
186
 
90
187
  This software is distributed under the [MIT](LICENSE.md) license.
data/assets/logo.png ADDED
Binary file
@@ -6,19 +6,31 @@ require "uri"
6
6
 
7
7
  module Jekyll
8
8
  class TargetBlank
9
- BODY_START_TAG = "<body"
9
+ BODY_START_TAG = "<body"
10
10
  OPENING_BODY_TAG_REGEX = %r!<body(.*)>\s*!
11
11
 
12
12
  class << self
13
13
  # Public: Processes the content and updated the external links
14
- # by adding the target="_blank" attribute.
14
+ # by adding target="_blank" and rel="noopener noreferrer" attributes.
15
15
  #
16
16
  # content - the document or page to be processes.
17
17
  def process(content)
18
- @site_url = content.site.config["url"]
18
+ @site_url = content.site.config["url"]
19
+ @config = content.site.config
20
+ @target_blank_config = class_config
21
+ @requires_specified_css_class = false
22
+ @required_css_class_name = nil
23
+ @should_add_css_classes = false
24
+ @css_classes_to_add = nil
25
+ @should_add_noopener = true
26
+ @should_add_noreferrrer = true
27
+ @should_add_extra_rel_attribute_values = false
28
+ @extra_rel_attribute_values = nil
19
29
 
20
30
  return unless content.output.include?("<a")
21
31
 
32
+ initialise
33
+
22
34
  content.output = if content.output.include? BODY_START_TAG
23
35
  process_html(content)
24
36
  else
@@ -26,21 +38,28 @@ module Jekyll
26
38
  end
27
39
  end
28
40
 
29
- # Public: Determines if the content should be processed.
41
+ # Public: Determines if the document should be processed.
30
42
  #
31
- # doc - the document being processes.
32
- def processable?(doc)
43
+ # doc - the document being processed.
44
+ def document_processable?(doc)
33
45
  (doc.is_a?(Jekyll::Page) || doc.write?) &&
34
46
  doc.output_ext == ".html" || (doc.permalink&.end_with?("/"))
35
47
  end
36
48
 
37
49
  private
38
50
 
51
+ def initialise
52
+ requires_css_class_name
53
+ configure_adding_additional_css_classes
54
+ add_default_rel_attributes?
55
+ add_extra_rel_attributes?
56
+ end
57
+
39
58
  # Private: Processes html content which has a body opening tag.
40
59
  #
41
60
  # content - html to be processes.
42
61
  def process_html(content)
43
- head, opener, tail = content.output.partition(OPENING_BODY_TAG_REGEX)
62
+ head, opener, tail = content.output.partition(OPENING_BODY_TAG_REGEX)
44
63
  body_content, *rest = tail.partition("</body>")
45
64
 
46
65
  processed_markup = process_anchor_tags(body_content)
@@ -49,22 +68,124 @@ module Jekyll
49
68
  end
50
69
 
51
70
  # Private: Processes the anchor tags and adds the target
52
- # attribute if the link is external.
71
+ # attribute if the link is external and depending on the config settings.
53
72
  #
54
73
  # html = the html which includes the anchor tags.
55
74
  def process_anchor_tags(html)
56
75
  content = Nokogiri::HTML::DocumentFragment.parse(html)
57
76
  anchors = content.css("a[href]")
58
77
  anchors.each do |item|
59
- if not_mailto_link?(item["href"]) && external?(item["href"])
60
- item["target"] = "_blank"
78
+ if processable_link?(item)
79
+ add_target_blank_attribute(item)
80
+ add_rel_attributes(item)
81
+ add_css_classes_if_required(item)
61
82
  end
83
+ next
62
84
  end
63
85
  content.to_html
64
86
  end
65
87
 
88
+ # Private: Determines of the link should be processed.
89
+ #
90
+ # link = Nokogiri node.
91
+ def processable_link?(link)
92
+ if not_mailto_link?(link["href"]) && external?(link["href"])
93
+ if @requires_specified_css_class
94
+ return false unless includes_specified_css_class?(link)
95
+ end
96
+ true
97
+ end
98
+ end
99
+
100
+ # Private: Handles adding the target attribute of the config
101
+ # requires a specifies class.
102
+ def requires_css_class_name
103
+ if css_class_name_specified_in_config?
104
+ @requires_specified_css_class = true
105
+ @required_css_class_name = specified_class_name_from_config
106
+ end
107
+ end
108
+
109
+ # Private: Configures any additional CSS classes
110
+ # if needed.
111
+ def configure_adding_additional_css_classes
112
+ if should_add_css_classes?
113
+ @should_add_css_classes = true
114
+ @css_classes_to_add = css_classes_to_add_from_config.to_s
115
+ end
116
+ end
117
+
118
+ # Private: Handles the default rel attribute values
119
+ def add_default_rel_attributes?
120
+ if should_not_include_noopener?
121
+ @should_add_noopener = false
122
+ end
123
+
124
+ if should_not_include_noreferrer?
125
+ @should_add_noreferrrer = false
126
+ end
127
+ end
128
+
129
+ # Private: Sets any extra rel attribute values
130
+ # if required.
131
+ def add_extra_rel_attributes?
132
+ if should_add_extra_rel_attribute_values?
133
+ @should_add_extra_rel_attribute_values = true
134
+ @extra_rel_attribute_values = extra_rel_attribute_values_to_add
135
+ end
136
+ end
137
+
138
+ # Private: adds the cs classes if set in config.
139
+ #
140
+ # link = Nokogiri node.
141
+ def add_css_classes_if_required(link)
142
+ if @should_add_css_classes
143
+ existing_classes = get_existing_css_classes(link)
144
+ existing_classes = " " + existing_classes unless existing_classes.to_s.empty?
145
+ link["class"] = @css_classes_to_add + existing_classes
146
+ end
147
+ end
148
+
149
+ # Private: Adds a target="_blank" to the link.
150
+ #
151
+ # link = Nokogiri node.
152
+ def add_target_blank_attribute(link)
153
+ link["target"] = "_blank"
154
+ end
155
+
156
+ # Private: Adds the rel attribute and values to the link.
157
+ #
158
+ # link = Nokogiri node.
159
+ def add_rel_attributes(link)
160
+ rel = ""
161
+ if @should_add_noopener
162
+ rel = "noopener"
163
+ end
164
+
165
+ if @should_add_noreferrrer
166
+ unless rel.empty?
167
+ rel += " "
168
+ end
169
+ rel += "noreferrer"
170
+ end
171
+
172
+ if @should_add_extra_rel_attribute_values
173
+ unless rel.empty?
174
+ rel += " "
175
+ end
176
+ rel += @extra_rel_attribute_values
177
+ end
178
+
179
+ unless rel.empty?
180
+ link["rel"] = rel
181
+ end
182
+ end
183
+
184
+ # Private: Checks if the link is a mailto url.
185
+ #
186
+ # link - a url.
66
187
  def not_mailto_link?(link)
67
- return true unless link.to_s.start_with?("mailto:")
188
+ true unless link.to_s.start_with?("mailto:")
68
189
  end
69
190
 
70
191
  # Private: Checks if the links points to a host
@@ -76,11 +197,141 @@ module Jekyll
76
197
  URI.parse(link).host != URI.parse(@site_url).host
77
198
  end
78
199
  end
200
+
201
+ # Private: Checks if a css class name is specified in config
202
+ def css_class_name_specified_in_config?
203
+ target_blank_config = @target_blank_config
204
+ case target_blank_config
205
+ when nil, NilClass
206
+ false
207
+ else
208
+ target_blank_config.fetch("css_class", false)
209
+ end
210
+ end
211
+
212
+ # Private: Checks if the link contains the same css class name
213
+ # as specified in config.
214
+ #
215
+ # link - the url under test.
216
+ def includes_specified_css_class?(link)
217
+ link_classes = get_existing_css_classes(link)
218
+ if link_classes
219
+ link_classes = link_classes.split(" ")
220
+ contained = false
221
+ link_classes.each do |name|
222
+ contained = true unless name != @required_css_class_name
223
+ end
224
+ return contained
225
+ end
226
+ false
227
+ end
228
+
229
+ # Private: Gets the the css classes of the link.
230
+ #
231
+ # link - an anchor tag.
232
+ def get_existing_css_classes(link)
233
+ link["class"].to_s
234
+ end
235
+
236
+ # Private: Checks if the link contains the class attribute.
237
+ #
238
+ # link - an anchor tag.
239
+ def link_has_class_attribute?(link)
240
+ link.include?("class=")
241
+ end
242
+
243
+ # Private: Fetches the specified css class name
244
+ # from config.
245
+ def specified_class_name_from_config
246
+ target_blank_config = @target_blank_config
247
+ target_blank_config.fetch("css_class")
248
+ end
249
+
250
+ # Private: Checks if it should add additional CSS classes.
251
+ def should_add_css_classes?
252
+ config = @target_blank_config
253
+ case config
254
+ when nil, NilClass
255
+ false
256
+ else
257
+ config.fetch("add_css_classes", false)
258
+ end
259
+ end
260
+
261
+ # Private: Checks if any addional rel attribute values
262
+ # should be added.
263
+ def should_add_extra_rel_attribute_values?
264
+ config = @target_blank_config
265
+ case config
266
+ when nil, NilClass
267
+ false
268
+ else
269
+ config.fetch("rel", false)
270
+ end
271
+ end
272
+
273
+ # Private: Gets any additional rel attribute values
274
+ # values to add from config.
275
+ def extra_rel_attribute_values_to_add
276
+ config = @target_blank_config
277
+ config.fetch("rel")
278
+ end
279
+
280
+ # Private: Gets the CSS classes to be added to the link from
281
+ # config.
282
+ def css_classes_to_add_from_config
283
+ config = @target_blank_config
284
+ config.fetch("add_css_classes")
285
+ end
286
+
287
+ # Private: Determines if the noopener rel attribute value should be added
288
+ # based on the specified config values.
289
+ #
290
+ # Returns true if noopener is false in config.
291
+ def should_not_include_noopener?
292
+ config = @target_blank_config
293
+ case config
294
+ when nil, NilClass
295
+ false
296
+ else
297
+ noopener = config.fetch("noopener", true)
298
+ if noopener == false
299
+ return true
300
+ else
301
+ return false
302
+ end
303
+ end
304
+ end
305
+
306
+ # Private: Determines if the noreferrer rel attribute value should be added
307
+ # based on the specified config values.
308
+ #
309
+ # Returns true if noreferrer is false in config.
310
+ def should_not_include_noreferrer?
311
+ config = @target_blank_config
312
+ case config
313
+ when nil, NilClass
314
+ false
315
+ else
316
+ noreferrer = config.fetch("noreferrer", true)
317
+ if noreferrer == false
318
+ return true
319
+ else
320
+ return false
321
+ end
322
+ end
323
+ end
324
+
325
+ # Private: Gets the relative config values
326
+ # if they exist.
327
+ def class_config
328
+ @target_blank_config = @config.fetch("target-blank", nil)
329
+ end
79
330
  end
80
331
  end
81
332
  end
82
333
 
83
334
  # Hooks into Jekyll's post_render event.
84
335
  Jekyll::Hooks.register %i[pages documents], :post_render do |doc|
85
- Jekyll::TargetBlank.process(doc) if Jekyll::TargetBlank.processable?(doc)
336
+ Jekyll::TargetBlank.process(doc) if Jekyll::TargetBlank.document_processable?(doc)
86
337
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module JekyllTargetBlank
4
- VERSION = "1.0"
4
+ VERSION = "1.1.0.beta"
5
5
  end
@@ -2,3 +2,5 @@ url: https://keith-mifsud.me
2
2
  collections:
3
3
  docs:
4
4
  output: true
5
+ target-blank:
6
+ add_css_classes: some-class other-some-class another-some-class
@@ -0,0 +1,6 @@
1
+ ---
2
+ layout: default
3
+ title: Post with external html link and random css classes
4
+ ---
5
+
6
+ <a href="https://google.com" class="random-class another-random-class">Link</a>.
@@ -0,0 +1,6 @@
1
+ ---
2
+ layout: default
3
+ title: Post with html link containing the specified css class
4
+ ---
5
+
6
+ <a href="https://google.com" class="ext-link">Link with the css class specified in config</a>.
@@ -0,0 +1,6 @@
1
+ ---
2
+ layout: default
3
+ title: Post with external link containing the specified css class and other css classes
4
+ ---
5
+
6
+ This is <a href="https://not-keith-mifsud.me" class="random-class ext-link another-random-class">a link containing the specified css class and two other random css classes</a>.
@@ -4,13 +4,21 @@ RSpec.describe(Jekyll::TargetBlank) do
4
4
  Jekyll.logger.log_level = :error
5
5
 
6
6
  let(:config_overrides) { {} }
7
+ let(:config_overrides) do
8
+ {
9
+ "url" => "https://keith-mifsud.me",
10
+ "collections" => { "docs" => { "output" => "true" } },
11
+ }
12
+ end
7
13
  let(:configs) do
8
- Jekyll.configuration(config_overrides.merge({
9
- "skip_config_files" => false,
10
- "collections" => { "docs" => { "output" => true } },
11
- "source" => fixtures_dir,
12
- "destination" => fixtures_dir("_site"),
13
- }))
14
+ Jekyll.configuration(config_overrides.merge(
15
+ {
16
+ "skip_config_files" => false,
17
+ "collections" => { "docs" => { "output" => true } },
18
+ "source" => fixtures_dir,
19
+ "destination" => fixtures_dir("_site"),
20
+ }
21
+ ))
14
22
  end
15
23
  let(:target_blank) { described_class }
16
24
  let(:site) { Jekyll::Site.new(configs) }
@@ -32,7 +40,7 @@ RSpec.describe(Jekyll::TargetBlank) do
32
40
  let(:document_with_a_processable_link) { find_by_title(site.collections["docs"].docs, "Document with a processable link") }
33
41
 
34
42
  let(:text_file) { find_by_title(site.collections["docs"].docs, "Text file") }
35
-
43
+ 0
36
44
  let(:post_with_code_block) { find_by_title(posts, "Post with code block") }
37
45
  let(:document_with_liquid_tag) { find_by_title(site.collections["docs"].docs, "Document with liquid tag") }
38
46
 
@@ -40,6 +48,12 @@ RSpec.describe(Jekyll::TargetBlank) do
40
48
 
41
49
  let(:post_with_mailto_link) { find_by_title(posts, "Post with mailto link") }
42
50
 
51
+ let(:post_with_external_html_link_and_random_css_classes) { find_by_title(posts, "Post with external html link and random css classes") }
52
+
53
+ let(:post_with_html_link_containing_the_specified_css_class) { find_by_title(posts, "Post with html link containing the specified css class") }
54
+
55
+ let(:post_with_external_link_containing_the_specified_css_class_and_other_css_classes) { find_by_title(posts, "Post with external link containing the specified css class and other css classes") }
56
+
43
57
  # define common wrappers.
44
58
  def para(content)
45
59
  "<p>#{content}</p>"
@@ -52,82 +66,360 @@ RSpec.describe(Jekyll::TargetBlank) do
52
66
  site.render
53
67
  end
54
68
 
55
- it "should add target attribute to external markdown link" do
56
- expect(post_with_external_markdown_link.output).to include(para('Link to <a href="https://google.com" target="_blank">Google</a>.'))
57
- end
69
+ context "Without entries in config file" do
70
+ let(:config_overrides) do
71
+ { "target-blank" => { "add_css_classes" => false } }
72
+ end
58
73
 
59
- it "should add target attribute to multiple external markdown links" do
60
- expect(post_with_multiple_external_markdown_links.output).to include('<p>This post contains three links. The first link is to <a href="https://google.com" target="_blank">Google</a>, the second link is, well, to <a href="https://keithmifsud.github.io" target="_blank">my website</a> and since <a href="https://github.com" target="_blank">GitHub</a> is so awesome, why not link to them too?</p>')
61
- end
74
+ it "should add target attribute to external markdown link" do
75
+ expect(post_with_external_markdown_link.output).to include(para('Link to <a href="https://google.com" target="_blank" rel="noopener noreferrer">Google</a>.'))
76
+ end
62
77
 
63
- it "should not add target attribute to relative markdown link" do
64
- expect(post_with_relative_markdown_link.output).to include(para('Link to <a href="/contact">contact page</a>.'))
78
+ it "should add target attribute to multiple external markdown links" do
79
+ expect(post_with_multiple_external_markdown_links.output).to include('<p>This post contains three links. The first link is to <a href="https://google.com" target="_blank" rel="noopener noreferrer">Google</a>, the second link is, well, to <a href="https://keithmifsud.github.io" target="_blank" rel="noopener noreferrer">my website</a> and since <a href="https://github.com" target="_blank" rel="noopener noreferrer">GitHub</a> is so awesome, why not link to them too?</p>')
80
+ end
65
81
 
66
- expect(post_with_relative_markdown_link.output).to_not include(para('Link to <a href="/contact" target="_blank">contact page</a>'))
67
- end
82
+ it "should not add target attribute to relative markdown link" do
83
+ expect(post_with_relative_markdown_link.output).to include(para('Link to <a href="/contact">contact page</a>.'))
68
84
 
69
- it "should not add target attribute to absolute internal link" do
70
- expect(post_with_absolute_internal_markdown_link.output).to include('<p>This is an absolute internal <a href="https://keith-mifsud.me/contact">link</a>.</p>
71
- ')
72
- end
85
+ expect(post_with_relative_markdown_link.output).to_not include(para('Link to <a href="/contact" target="_blank" rel="noopener noreferrer">contact page</a>'))
86
+ end
87
+
88
+ it "should not add target attribute to absolute internal link" do
89
+ expect(post_with_absolute_internal_markdown_link.output).to include('<p>This is an absolute internal <a href="https://keith-mifsud.me/contact">link</a>.</p>')
90
+ end
91
+
92
+ it "should correctly handle existing html anchor tag" do
93
+ expect(post_with_html_anchor_tag.output).to include('<p>This is an <a href="https://google.com" target="_blank" rel="noopener noreferrer">anchor tag</a>.</p>')
94
+ end
95
+
96
+ it "should not interfere with plain text link" do
97
+ expect(post_with_plain_text_link.output).to include("<p>This is a plain text link to https://google.com.</p>")
98
+ end
99
+
100
+ it "should process external links in collections" do
101
+ expect(document_with_a_processable_link.output).to include('<p>This is a valid <a href="https://google.com" target="_blank" rel="noopener noreferrer">link</a>.</p>')
102
+ end
103
+
104
+ it "should process external links in pages" do
105
+ expect(site.pages.first.output).to include('<p>This is a valid <a href="https://google.com" target="_blank" rel="noopener noreferrer">link</a>.</p>')
106
+ end
107
+
108
+ it "should not process links in non html files" do
109
+ expect(text_file.output).to eq("Valid [link](https://google.com).")
110
+ end
111
+
112
+ it "should not process link in code block but process link outside of block" do
113
+ expect(post_with_code_block.output).to include('<span class="s1">\'https://google.com\'</span>')
73
114
 
74
- it "should correctly handle existing html anchor tag" do
75
- expect(post_with_html_anchor_tag.output).to include('<p>This is an <a href="https://google.com" target="_blank">anchor tag</a>.</p>
76
- ')
115
+ expect(post_with_code_block.output).not_to include('<span class="s1"><a href="https://google.com" target="_blank" rel="noopener noreferrer">https://google.com</a></span>')
116
+
117
+ expect(post_with_code_block.output).to include('<p>Valid <a href="https://google.com" target="_blank" rel="noopener noreferrer">link</a></p>')
118
+ end
119
+
120
+ it "should not break layouts" do
121
+ expect(site.pages.first.output).to include('<html lang="en-US">')
122
+ expect(site.pages.first.output).to include('<body class="wrap">')
123
+ end
124
+
125
+ it "should not interfere with liquid tags" do
126
+ expect(document_with_liquid_tag.output).to include('<p>This <a href="/docs/document-with-liquid-tag.html">_docs/document-with-liquid-tag.md</a> is a document with a liquid tag.</p>')
127
+ end
128
+
129
+ it "should not interfere with includes" do
130
+ expect(document_with_include.output).to include("<p>This is a document with an include: This is an include.</p>")
131
+ end
132
+
133
+ it "should not break layout content" do
134
+ expect(site.pages.first.output).to include("<div>Layout content started.</div>")
135
+
136
+ expect(site.pages.first.output).to include("<div>Layout content ended.</div>")
137
+ end
138
+
139
+ it "should not duplicate post content" do
140
+ expect(post_with_external_markdown_link.output).to eq(post_with_layout_result)
141
+ end
142
+
143
+ it "should ignore mailto links" do
144
+ expect(post_with_mailto_link.output).to include(para('This is a <a href="mailto:mifsud.k@gmail.com?Subject=Just%20an%20email">mailto link</a>.'))
145
+ end
77
146
  end
78
147
 
79
- it "should not interfere with plain text link" do
80
- expect(post_with_plain_text_link.output).to include("<p>This is a plain text link to https://google.com.</p>
81
- ")
148
+ context "With a specified css class name" do
149
+ let(:target_blank_css_class) { "ext-link" }
150
+ let(:config_overrides) do
151
+ {
152
+ "target-blank" => {
153
+ "css_class" => target_blank_css_class,
154
+ "add_css_classes" => false,
155
+ },
156
+ }
157
+ end
158
+
159
+ it "should not add target attribute to external markdown link that does not have the specified css class" do
160
+ expect(post_with_external_markdown_link.output).to_not include(para('Link to <a href="https://google.com" target="_blank">Google</a>.'))
161
+ end
162
+
163
+ it "should not add target attribute to external markdown link that does not have the specified css class even if it does have other css classes" do
164
+ expect(post_with_external_html_link_and_random_css_classes.output).to include(para('<a href="https://google.com" class="random-class another-random-class">Link</a>.'))
165
+
166
+ expect(post_with_external_html_link_and_random_css_classes.output).to_not include('target="_blank" rel="noopener noreferrer"')
167
+ end
168
+
169
+ it "should add target attribute to an external link containing the specified css class" do
170
+ expect(post_with_html_link_containing_the_specified_css_class.output).to include(para('<a href="https://google.com" class="ext-link" target="_blank" rel="noopener noreferrer">Link with the css class specified in config</a>.'))
171
+ end
172
+
173
+ it "should add target attribute to an external link containing the specified css class even when other css classes are specified" do
174
+ expect(post_with_external_link_containing_the_specified_css_class_and_other_css_classes.output).to include(para('This is <a href="https://not-keith-mifsud.me" class="random-class ext-link another-random-class" target="_blank" rel="noopener noreferrer">a link containing the specified css class and two other random css classes</a>.'))
175
+ end
82
176
  end
83
177
 
84
- it "should process external links in collections" do
85
- expect(document_with_a_processable_link.output).to include('<p>This is a valid <a href="https://google.com" target="_blank">link</a>.</p>
86
- ')
178
+ context "Adds a CSS classes to the links" do
179
+ let(:target_blank_add_css_class) { "some-class" }
180
+ let(:config_overrides) do
181
+ { "target-blank" => { "add_css_classes" => target_blank_add_css_class } }
182
+ end
183
+
184
+ it "should add the CSS class specified in config" do
185
+ expect(post_with_external_markdown_link.output).to include(para('Link to <a href="https://google.com" target="_blank" rel="noopener noreferrer" class="some-class">Google</a>.'))
186
+ end
187
+
188
+ it "should add the CSS class specified in config even when the link already has a CSS class specified" do
189
+ expect(post_with_html_link_containing_the_specified_css_class.output).to include(para('<a href="https://google.com" class="some-class ext-link" target="_blank" rel="noopener noreferrer">Link with the css class specified in config</a>.'))
190
+ end
191
+
192
+ it "should add the CSS class specified in config even when the link has more than CSS classes already included" do
193
+ expect(post_with_external_link_containing_the_specified_css_class_and_other_css_classes.output).to include(para('This is <a href="https://not-keith-mifsud.me" class="some-class random-class ext-link another-random-class" target="_blank" rel="noopener noreferrer">a link containing the specified css class and two other random css classes</a>.'))
194
+ end
87
195
  end
88
196
 
89
- it "should process external links in pages" do
90
- expect(site.pages.first.output).to include('<p>This is a valid <a href="https://google.com" target="_blank">link</a>.</p>')
197
+ context "When more than one CSS classes are specified in config" do
198
+ it "should add the CSS classes specified in config" do
199
+ expect(post_with_external_markdown_link.output).to include(para('Link to <a href="https://google.com" target="_blank" rel="noopener noreferrer" class="some-class other-some-class another-some-class">Google</a>.'))
200
+ end
201
+
202
+ it "should add the CSS classes specified in config even when the link already has a CSS class included" do
203
+ expect(post_with_html_link_containing_the_specified_css_class.output).to include(para('<a href="https://google.com" class="some-class other-some-class another-some-class ext-link" target="_blank" rel="noopener noreferrer">Link with the css class specified in config</a>.'))
204
+ end
205
+
206
+ it "should add the CSS classes specified in config even when the link already has more than one CSS classes included" do
207
+ expect(post_with_external_link_containing_the_specified_css_class_and_other_css_classes.output).to include(para('This is <a href="https://not-keith-mifsud.me" class="some-class other-some-class another-some-class random-class ext-link another-random-class" target="_blank" rel="noopener noreferrer">a link containing the specified css class and two other random css classes</a>.'))
208
+ end
91
209
  end
92
210
 
93
- it "should not process links in non html files" do
94
- expect(text_file.output).to eq("Valid [link](https://google.com).")
211
+ context "When noopener is set to false in config" do
212
+ let(:noopener) { false }
213
+ let(:config_overrides) do
214
+ {
215
+ "target-blank" => {
216
+ "add_css_classes" => false,
217
+ "noopener" => noopener,
218
+ },
219
+ }
220
+ end
221
+
222
+ it "should not add noopener value to the rel attribute" do
223
+ expect(post_with_external_markdown_link.output).to_not include(para('Link to <a href="https://google.com" target="_blank" rel="noopener noreferrer">Google</a>.'))
224
+ end
225
+
226
+ it "should still add noreferrer value to the rel attribute" do
227
+ expect(post_with_external_markdown_link.output).to include(para('Link to <a href="https://google.com" target="_blank" rel="noreferrer">Google</a>.'))
228
+ end
95
229
  end
96
230
 
97
- it "should not process link in code block but process link outside of block" do
98
- expect(post_with_code_block.output).to include("<span class=\"s1\">'https://google.com'</span>")
231
+ context "When noreferrer is set to false in config" do
232
+ let(:noreferrer) { false }
233
+ let(:config_overrides) do
234
+ {
235
+ "target-blank" => {
236
+ "add_css_classes" => false,
237
+ "noreferrer" => noreferrer,
238
+ },
239
+ }
240
+ end
241
+
242
+ it "should not add noreferrer value to the rel attribute" do
243
+ expect(post_with_external_markdown_link.output).to_not include(para('Link to <a href="https://google.com" target="_blank" rel="noopener noreferrer">Google</a>.'))
244
+ end
245
+
246
+ it "should still add noopener value to the rel attribute" do
247
+ expect(post_with_external_markdown_link.output).to include(para('Link to <a href="https://google.com" target="_blank" rel="noopener">Google</a>.'))
248
+ end
249
+ end
99
250
 
100
- expect(post_with_code_block.output).not_to include("<span class=\"s1\"><a href=\"https://google.com\" target=\"_blank\">https://google.com</a></span>")
251
+ context "When both noopener and noreferrer values are set to false in config" do
252
+ let(:noopener) { false }
253
+ let(:noreferrer) { false }
254
+ let(:config_overrides) do
255
+ {
256
+ "target-blank" => {
257
+ "add_css_classes" => false,
258
+ "noopener" => noopener,
259
+ "noreferrer" => noreferrer,
260
+ },
261
+ }
262
+ end
263
+
264
+ it "should not include the rel attribute values" do
265
+ expect(post_with_external_markdown_link.output).to_not include(para('Link to <a href="https://google.com" target="_blank" rel="noopener noreferrer">Google</a>.'))
266
+ end
267
+
268
+ it "should not include the rel attribute noopener value" do
269
+ expect(post_with_external_markdown_link.output).to_not include(para('Link to <a href="https://google.com" target="_blank" rel="noreferrer">Google</a>.'))
270
+ end
271
+
272
+ it "should not include the rel attribute noreferrer value" do
273
+ expect(post_with_external_markdown_link.output).to_not include(para('Link to <a href="https://google.com" target="_blank" rel="noopener">Google</a>.'))
274
+ end
275
+
276
+ it "should not include any rel attributes" do
277
+ expect(post_with_external_markdown_link.output).to include(para('Link to <a href="https://google.com" target="_blank">Google</a>.'))
278
+ end
279
+ end
101
280
 
102
- expect(post_with_code_block.output).to include('<p>Valid <a href="https://google.com" target="_blank">link</a></p>
103
- ')
281
+ context "When one additional rel attribute is added in config" do
282
+ let(:rel_attribute) { "nofollow" }
283
+ let(:config_overrides) do
284
+ {
285
+ "target-blank" => {
286
+ "add_css_classes" => false,
287
+ "rel" => rel_attribute,
288
+ },
289
+ }
290
+ end
291
+
292
+ it "should add the extra rel attribute together with the default ones" do
293
+ expect(post_with_external_markdown_link.output).to include(para('Link to <a href="https://google.com" target="_blank" rel="noopener noreferrer nofollow">Google</a>.'))
294
+ end
104
295
  end
105
296
 
106
- it "should not break layouts" do
107
- expect(site.pages.first.output).to include('<html lang="en-US">')
108
- expect(site.pages.first.output).to include('<body class="wrap">')
297
+ context "When more than one additional rel attributes are added in config" do
298
+ let(:rel_attribute) { "nofollow tag" }
299
+ let(:config_overrides) do
300
+ {
301
+ "target-blank" => {
302
+ "add_css_classes" => false,
303
+ "rel" => rel_attribute,
304
+ },
305
+ }
306
+ end
307
+
308
+ it "should add the extra rel attributes together with the default ones" do
309
+ expect(post_with_external_markdown_link.output).to include(para('Link to <a href="https://google.com" target="_blank" rel="noopener noreferrer nofollow tag">Google</a>.'))
310
+ end
109
311
  end
110
312
 
111
- it "should not interfere with liquid tags" do
112
- expect(document_with_liquid_tag.output).to include('<p>This <a href="/docs/document-with-liquid-tag.html">_docs/document-with-liquid-tag.md</a> is a document with a liquid tag.</p>')
313
+ context "When one extra rel attribute value are set in config and noopener is set to false" do
314
+ let(:rel_attribute) { "nofollow" }
315
+ let(:noopener) { false }
316
+ let(:config_overrides) do
317
+ {
318
+ "target-blank" => {
319
+ "add_css_classes" => false,
320
+ "noopener" => noopener,
321
+ "rel" => rel_attribute,
322
+ },
323
+ }
324
+ end
325
+
326
+ it "should the extra rel attribute value and not add the default noopener value" do
327
+ expect(post_with_external_markdown_link.output).to include(para('Link to <a href="https://google.com" target="_blank" rel="noreferrer nofollow">Google</a>.'))
328
+ end
113
329
  end
114
330
 
115
- it "should not interfere with includes" do
116
- expect(document_with_include.output).to include("<p>This is a document with an include: This is an include.</p>")
331
+ context "When more than one extra rel attribute values are set in config and noopener is set to false" do
332
+ let(:rel_attribute) { "nofollow tag" }
333
+ let(:noopener) { false }
334
+ let(:config_overrides) do
335
+ {
336
+ "target-blank" => {
337
+ "add_css_classes" => false,
338
+ "noopener" => noopener,
339
+ "rel" => rel_attribute,
340
+ },
341
+ }
342
+ end
343
+
344
+ it "should the extra rel attribute values and not add the default noopener value" do
345
+ expect(post_with_external_markdown_link.output).to include(para('Link to <a href="https://google.com" target="_blank" rel="noreferrer nofollow tag">Google</a>.'))
346
+ end
117
347
  end
118
348
 
119
- it "should not break layout content" do
120
- expect(site.pages.first.output).to include("<div>Layout content started.</div>")
349
+ context "When one extra rel attributes is set in config and both noopener and noreferer are set to false" do
350
+ let(:rel_attribute) { "nofollow" }
351
+ let(:noopener) { false }
352
+ let(:noreferrer) { false }
353
+ let(:config_overrides) do
354
+ {
355
+ "target-blank" => {
356
+ "add_css_classes" => false,
357
+ "noopener" => noopener,
358
+ "noreferrer" => noreferrer,
359
+ "rel" => rel_attribute,
360
+ },
361
+ }
362
+ end
363
+
364
+ it "should add the extra rel attribute value and no default ones" do
365
+ expect(post_with_external_markdown_link.output).to include(para('Link to <a href="https://google.com" target="_blank" rel="nofollow">Google</a>.'))
366
+ end
367
+ end
121
368
 
122
- expect(site.pages.first.output).to include("<div>Layout content ended.</div>")
369
+ context "When more than one extra rel attribute values are set in config and both noopener and noreferer are set to false" do
370
+ let(:rel_attribute) { "nofollow tag" }
371
+ let(:noopener) { false }
372
+ let(:noreferrer) { false }
373
+ let(:config_overrides) do
374
+ {
375
+ "target-blank" => {
376
+ "add_css_classes" => false,
377
+ "noopener" => noopener,
378
+ "noreferrer" => noreferrer,
379
+ "rel" => rel_attribute,
380
+ },
381
+ }
382
+ end
383
+
384
+ it "should add the extra rel attribute values and no default ones" do
385
+ expect(post_with_external_markdown_link.output).to include(para('Link to <a href="https://google.com" target="_blank" rel="nofollow tag">Google</a>.'))
386
+ end
123
387
  end
124
388
 
125
- it "should not duplicate post content" do
126
- expect(post_with_external_markdown_link.output).to eq(post_with_layout_result)
389
+ context "When noopener is set to false in config but added to the rel config property" do
390
+ let(:rel_attribute) { "noopener" }
391
+ let(:noopener) { false }
392
+ let(:config_overrides) do
393
+ {
394
+ "target-blank" => {
395
+ "add_css_classes" => false,
396
+ "noopener" => noopener,
397
+ "rel" => rel_attribute,
398
+ },
399
+ }
400
+ end
401
+
402
+ it "should still include the noopener rel attribute value" do
403
+ expect(post_with_external_markdown_link.output).to include(para('Link to <a href="https://google.com" target="_blank" rel="noreferrer noopener">Google</a>.'))
404
+ end
127
405
  end
128
406
 
129
- it "should ignore mailto links" do
130
- expect(post_with_mailto_link.output).to include(para('This is a <a href="mailto:mifsud.k@gmail.com?Subject=Just%20an%20email">mailto link</a>.'))
407
+ context "When noopener is set to false in config but added t0 the rel config property alongside one more extra rel attribute value." do
408
+ let(:rel_attribute) { "noopener nofollow" }
409
+ let(:noopener) { false }
410
+ let(:config_overrides) do
411
+ {
412
+ "target-blank" => {
413
+ "add_css_classes" => false,
414
+ "noopener" => noopener,
415
+ "rel" => rel_attribute,
416
+ },
417
+ }
418
+ end
419
+
420
+ it "should still include the noopener rel attribute value along the extra one" do
421
+ expect(post_with_external_markdown_link.output).to include(para('Link to <a href="https://google.com" target="_blank" rel="noreferrer noopener nofollow">Google</a>.'))
422
+ end
131
423
  end
132
424
 
133
425
  private
@@ -144,7 +436,7 @@ RSpec.describe(Jekyll::TargetBlank) do
144
436
  </head>
145
437
  <body class="wrap">
146
438
  <div>Layout content started.</div>
147
- <p>Link to <a href="https://google.com" target="_blank">Google</a>.</p>
439
+ <p>Link to <a href="https://google.com" target="_blank" rel="noopener noreferrer">Google</a>.</p>
148
440
 
149
441
  <div>Layout content ended.</div>
150
442
  </body>
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: jekyll-target-blank
3
3
  version: !ruby/object:Gem::Version
4
- version: '1.0'
4
+ version: 1.1.0.beta
5
5
  platform: ruby
6
6
  authors:
7
7
  - Keith Mifsud
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2018-05-30 00:00:00.000000000 Z
11
+ date: 2018-08-01 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: jekyll
@@ -110,6 +110,7 @@ files:
110
110
  - LICENSE.md
111
111
  - README.md
112
112
  - Rakefile
113
+ - assets/logo.png
113
114
  - jekyll-target-blank.gemspec
114
115
  - lib/jekyll-target-blank.rb
115
116
  - lib/jekyll-target-blank/version.rb
@@ -131,6 +132,9 @@ files:
131
132
  - spec/fixtures/_posts/2018-05-23-post-with-absolute-internal-markdown-link.md
132
133
  - spec/fixtures/_posts/2018-05-24-post-with-code-block.md
133
134
  - spec/fixtures/_posts/2018-05-30-post-with-mailto-link.md
135
+ - spec/fixtures/_posts/2018-07-02-post-with-external-html-link-and-random-css-classes.md
136
+ - spec/fixtures/_posts/2018-07-02-post-with-html-link-containing-the-specified-css-class.md
137
+ - spec/fixtures/_posts/2018-07-05-post-with-external-link-containing-the-specified-css-class-and-other-css-classes.md
134
138
  - spec/fixtures/index.md
135
139
  - spec/jekyll-target_spec.rb
136
140
  - spec/spec_helper.rb
@@ -149,12 +153,12 @@ required_ruby_version: !ruby/object:Gem::Requirement
149
153
  version: 2.3.0
150
154
  required_rubygems_version: !ruby/object:Gem::Requirement
151
155
  requirements:
152
- - - ">="
156
+ - - ">"
153
157
  - !ruby/object:Gem::Version
154
- version: '0'
158
+ version: 1.3.1
155
159
  requirements: []
156
160
  rubyforge_project:
157
- rubygems_version: 2.6.14.1
161
+ rubygems_version: 2.7.7
158
162
  signing_key:
159
163
  specification_version: 4
160
164
  summary: Target Blank automatically changes the external links to open in a new browser.