jekyll_picture_tag 1.6.0 → 1.7.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (52) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +2 -0
  3. data/.travis.yml +11 -0
  4. data/Gemfile +2 -2
  5. data/Rakefile +28 -0
  6. data/contributing.md +67 -0
  7. data/docs/examples/_config.yml +10 -0
  8. data/{examples → docs/examples}/_data/picture.yml +39 -19
  9. data/docs/examples/post.md +46 -0
  10. data/docs/global_configuration.md +115 -0
  11. data/docs/installation.md +30 -0
  12. data/docs/migration.md +178 -0
  13. data/docs/notes.md +85 -0
  14. data/docs/presets.md +407 -0
  15. data/docs/readme.md +23 -0
  16. data/docs/usage.md +131 -0
  17. data/jekyll-picture-tag.gemspec +3 -12
  18. data/jekyll_picture_tag.gemspec +8 -3
  19. data/lib/jekyll-picture-tag.rb +5 -3
  20. data/lib/jekyll_picture_tag.rb +45 -42
  21. data/lib/jekyll_picture_tag/defaults/global.yml +0 -3
  22. data/lib/jekyll_picture_tag/defaults/presets.yml +1 -0
  23. data/lib/jekyll_picture_tag/generated_image.rb +60 -39
  24. data/lib/jekyll_picture_tag/img_uri.rb +55 -0
  25. data/lib/jekyll_picture_tag/instructions.rb +1 -102
  26. data/lib/jekyll_picture_tag/instructions/configuration.rb +30 -74
  27. data/lib/jekyll_picture_tag/instructions/html_attributes.rb +18 -27
  28. data/lib/jekyll_picture_tag/instructions/preset.rb +14 -3
  29. data/lib/jekyll_picture_tag/instructions/set.rb +61 -0
  30. data/lib/jekyll_picture_tag/instructions/tag_parser.rb +80 -23
  31. data/lib/jekyll_picture_tag/output_formats.rb +1 -1
  32. data/lib/jekyll_picture_tag/output_formats/{basics.rb → basic.rb} +24 -19
  33. data/lib/jekyll_picture_tag/output_formats/data_attributes.rb +2 -2
  34. data/lib/jekyll_picture_tag/output_formats/direct_url.rb +1 -3
  35. data/lib/jekyll_picture_tag/output_formats/img.rb +4 -4
  36. data/lib/jekyll_picture_tag/output_formats/naked_srcset.rb +5 -4
  37. data/lib/jekyll_picture_tag/output_formats/picture.rb +6 -16
  38. data/lib/jekyll_picture_tag/output_formats/readme.md +8 -15
  39. data/lib/jekyll_picture_tag/router.rb +98 -0
  40. data/lib/jekyll_picture_tag/source_image.rb +15 -23
  41. data/lib/jekyll_picture_tag/srcsets.rb +1 -1
  42. data/lib/jekyll_picture_tag/srcsets/{basics.rb → basic.rb} +22 -13
  43. data/lib/jekyll_picture_tag/srcsets/pixel_ratio.rb +6 -11
  44. data/lib/jekyll_picture_tag/srcsets/width.rb +3 -11
  45. data/lib/jekyll_picture_tag/utils.rb +32 -49
  46. data/lib/jekyll_picture_tag/version.rb +1 -1
  47. data/readme.md +70 -70
  48. metadata +97 -16
  49. data/bin/console +0 -14
  50. data/bin/setup +0 -7
  51. data/examples/_config.yml +0 -4
  52. data/examples/post.md +0 -18
@@ -1,21 +1,23 @@
1
1
  module PictureTag
2
2
  # Handles a given source image file and its properties. Provides a speed
3
3
  # advantage by storing expensive file reads and writes in instance variables,
4
- # to be reused by many different source images.
4
+ # to be reused by many different generated images.
5
5
  class SourceImage
6
- attr_reader :name, :shortname, :missing
6
+ attr_reader :name, :shortname, :missing, :media_preset
7
+ include MiniMagick
7
8
 
8
- def initialize(relative_filename)
9
+ def initialize(relative_filename, media_preset = nil)
9
10
  @shortname = relative_filename
10
11
  @name = grab_file relative_filename
11
- end
12
-
13
- def size
14
- @size ||= build_size
12
+ @media_preset = media_preset
15
13
  end
16
14
 
17
15
  def width
18
- size[:width]
16
+ @width ||= if @missing
17
+ 999_999
18
+ else
19
+ image.width
20
+ end
19
21
  end
20
22
 
21
23
  def digest
@@ -39,33 +41,23 @@ module PictureTag
39
41
 
40
42
  private
41
43
 
42
- def build_size
43
- if @missing
44
- width = 999_999
45
- height = 999_999
46
- else
47
- width, height = FastImage.size(@name)
48
- end
49
-
50
- {
51
- width: width,
52
- height: height
53
- }
44
+ def image
45
+ @image ||= Image.open(@name)
54
46
  end
55
47
 
56
48
  # Turn a relative filename into an absolute one, and make sure it exists.
57
49
  def grab_file(source_file)
58
- source_name = File.join(PictureTag.config.source_dir, source_file)
50
+ source_name = File.join(PictureTag.source_dir, source_file)
59
51
 
60
52
  if File.exist? source_name
61
53
  @missing = false
62
54
 
63
- elsif PictureTag.config.continue_on_missing?
55
+ elsif PictureTag.continue_on_missing?
64
56
  @missing = true
65
57
  Utils.warning missing_image_warning(source_name)
66
58
 
67
59
  else
68
- raise missing_image_error(source_name)
60
+ raise ArgumentError, missing_image_error(source_name)
69
61
  end
70
62
 
71
63
  source_name
@@ -1,3 +1,3 @@
1
- require_relative 'srcsets/basics'
1
+ require_relative 'srcsets/basic'
2
2
  require_relative 'srcsets/pixel_ratio'
3
3
  require_relative 'srcsets/width'
@@ -1,3 +1,4 @@
1
+ require 'mime-types'
1
2
  module PictureTag
2
3
  # Handles srcset generation, which also handles file generation.
3
4
  module Srcsets
@@ -7,18 +8,27 @@ module PictureTag
7
8
  # - Return an array of srcset entries.
8
9
  # - Call generate_file for each entry, giving it the desired width in
9
10
  # pixels.
10
- module Basics
11
- require 'fastimage'
12
- require 'mime-types'
13
- attr_reader :media, :source_image
11
+ class Basic
12
+ attr_reader :source_image, :media
14
13
 
15
- def initialize(media:, format:)
16
- @media = media # Associated Media Query, can be nil
14
+ def initialize(source_image, input_format)
15
+ @source_image = source_image
16
+ @input_format = input_format
17
+ @media = source_image.media_preset
18
+ end
17
19
 
18
- # Output format:
19
- @format = Utils.process_format(format, media)
20
+ def format
21
+ # Input format might be 'original', which is handled by the generated
22
+ # image.
23
+ @format ||= files.first.format
24
+ end
20
25
 
21
- @source_image = PictureTag.source_images[@media]
26
+ def files
27
+ @files ||= widths.collect { |w| generate_file(w) }
28
+ end
29
+
30
+ def to_a
31
+ files.collect { |f| build_srcset_entry(f) }
22
32
  end
23
33
 
24
34
  def to_s
@@ -28,7 +38,7 @@ module PictureTag
28
38
  # Allows us to add a type attribute to whichever element contains this
29
39
  # srcset.
30
40
  def mime_type
31
- MIME::Types.type_for(@format).first.to_s
41
+ MIME::Types.type_for(format).first.to_s
32
42
  end
33
43
 
34
44
  # Some srcsets have them, for those that don't return nil.
@@ -53,7 +63,7 @@ module PictureTag
53
63
  private
54
64
 
55
65
  def handle_small_source(targets, image_width)
56
- PictureTag::Utils.warning(
66
+ Utils.warning(
57
67
  " #{@source_image.shortname} is #{image_width}px wide, smaller than" \
58
68
  " at least one size in the set #{targets}. Will not enlarge."
59
69
  )
@@ -69,10 +79,9 @@ module PictureTag
69
79
  GeneratedImage.new(
70
80
  source_file: @source_image,
71
81
  width: width,
72
- format: @format
82
+ format: @input_format
73
83
  )
74
84
  end
75
-
76
85
  end
77
86
  end
78
87
  end
@@ -2,13 +2,7 @@ module PictureTag
2
2
  module Srcsets
3
3
  # Creates a srcset in the "(filename) (pixel_ratio)x" format.
4
4
  # Example: "img.jpg 1x, img2.jpg 1.5x, img3.jpg 2x"
5
- class PixelRatio
6
- include Basics
7
-
8
- def to_a
9
- widths.collect { |w| build_srcset_entry(w) }
10
- end
11
-
5
+ class PixelRatio < Basic
12
6
  private
13
7
 
14
8
  def widths
@@ -19,13 +13,14 @@ module PictureTag
19
13
  check_widths target
20
14
  end
21
15
 
22
- def build_srcset_entry(width)
16
+ def build_srcset_entry(file)
23
17
  # We have to recalculate the pixel ratio after verifying our source
24
18
  # image is large enough.
25
- pixel_ratio = (width.to_f / PictureTag.preset['base_width']).round(2)
26
- file = generate_file(width)
19
+ pixel_ratio = (
20
+ file.width.to_f / PictureTag.preset['base_width']
21
+ ).round(2)
27
22
 
28
- "#{PictureTag.build_url(file.name)} #{pixel_ratio}x"
23
+ "#{file.uri} #{pixel_ratio}x"
29
24
  end
30
25
  end
31
26
  end
@@ -2,13 +2,7 @@ module PictureTag
2
2
  module Srcsets
3
3
  # Creates a srcset in the "(filename) (width)w, (...)" format.
4
4
  # Example: "img.jpg 400w, img2.jpg 600w, img3.jpg 800w"
5
- class Width
6
- include Basics
7
-
8
- def to_a
9
- widths.collect { |w| build_srcset_entry(w) }
10
- end
11
-
5
+ class Width < Basic
12
6
  # Sizes html attribute. Since it's intimately related to srcset, we
13
7
  # generate it at the same time.
14
8
  def sizes
@@ -31,10 +25,8 @@ module PictureTag
31
25
  check_widths PictureTag.widths(@media)
32
26
  end
33
27
 
34
- def build_srcset_entry(width)
35
- file = generate_file(width)
36
-
37
- "#{PictureTag.build_url(file.name)} #{file.width}w"
28
+ def build_srcset_entry(file)
29
+ "#{file.uri} #{file.width}w"
38
30
  end
39
31
 
40
32
  def build_size_entry(media, size)
@@ -2,66 +2,49 @@ module PictureTag
2
2
  # This is a little module to hold logic that doesn't fit other places. If it
3
3
  # starts getting big, refactor.
4
4
  module Utils
5
- # Configure Jekyll to keep our generated files
6
- def self.keep_files
7
- dest_dir = PictureTag.config['picture']['output']
5
+ class << self
6
+ # Configure Jekyll to keep our generated files
7
+ def keep_files
8
+ dest_dir = PictureTag.config['picture']['output']
8
9
 
9
- # Chop a slash off the end, if it's there. Doesn't work otherwise.
10
- dest_dir = dest_dir[0..-2] if dest_dir =~ %r{/\z}
10
+ # Chop a slash off the end, if it's there. Doesn't work otherwise.
11
+ dest_dir = dest_dir[0..-2] if dest_dir =~ %r{/\z}
11
12
 
12
- return if PictureTag.site.config['keep_files'].include?(dest_dir)
13
+ return if PictureTag.site.config['keep_files'].include?(dest_dir)
13
14
 
14
- PictureTag.site.config['keep_files'] << dest_dir
15
- end
16
-
17
- # Print a warning to the console
18
- def self.warning(message)
19
- return if PictureTag.config['picture']['suppress_warnings']
20
-
21
- warn 'Jekyll Picture Tag Warning: '.yellow + message
22
- end
23
-
24
- # Parse a liquid template; allows liquid variables to be included as tag
25
- # params.
26
- def self.liquid_lookup(params)
27
- Liquid::Template.parse(params).render(PictureTag.context)
15
+ PictureTag.site.config['keep_files'] << dest_dir
16
+ end
28
17
 
29
- # This gsub allows people to include template code for javascript
30
- # libraries such as handlebar.js. It adds complication and I'm not sure
31
- # it has much value now, so I'm commenting it out. If someone has a use
32
- # case for it we can add it back in.
33
- # .gsub(/\\\{\\\{|\\\{\\%/, '\{\{' => '{{', '\{\%' => '{%')
34
- end
18
+ # Print a warning to the console
19
+ def warning(message)
20
+ return if PictureTag.config['picture']['suppress_warnings']
35
21
 
36
- # Allows us to use 'original' as a format name.
37
- def self.process_format(format, media)
38
- if format.casecmp('original').zero?
39
- PictureTag.source_images[media].ext
40
- else
41
- format.downcase
22
+ warn 'Jekyll Picture Tag Warning: '.yellow + message
42
23
  end
43
- end
44
24
 
45
- # Used for auto markup configuration and such
46
- def self.count_srcsets
47
- formats = PictureTag.preset['formats'].length
48
- source_images = PictureTag.source_images.length
25
+ # Parse a liquid template; allows liquid variables to be included as tag
26
+ # params.
27
+ def liquid_lookup(params)
28
+ Liquid::Template.parse(params).render(PictureTag.context)
29
+ end
49
30
 
50
- formats * source_images
51
- end
31
+ # Used for auto markup configuration and such
32
+ def count_srcsets
33
+ PictureTag.formats.length * PictureTag.source_images.length
34
+ end
52
35
 
53
- # Returns whether or not the current page is a markdown file.
54
- def self.markdown_page?
55
- page_name = PictureTag.page['name']
56
- page_ext = PictureTag.page['ext']
57
- ext = page_ext ? page_ext : File.extname(page_name)
36
+ # Returns whether or not the current page is a markdown file.
37
+ def markdown_page?
38
+ page_name = PictureTag.page['name']
39
+ page_ext = PictureTag.page['ext']
40
+ ext = page_ext || File.extname(page_name)
58
41
 
59
- ext.casecmp('.md').zero? || ext.casecmp('.markdown').zero?
60
- end
42
+ ext.casecmp('.md').zero? || ext.casecmp('.markdown').zero?
43
+ end
61
44
 
62
- # Returns the widest source image
63
- def self.biggest_source
64
- PictureTag.source_images.values.max_by(&:width)
45
+ def titleize(input)
46
+ input.split('_').map(&:capitalize).join
47
+ end
65
48
  end
66
49
  end
67
50
  end
@@ -1,3 +1,3 @@
1
1
  module PictureTag
2
- VERSION = '1.6.0'.freeze
2
+ VERSION = '1.7.0'.freeze
3
3
  end
data/readme.md CHANGED
@@ -2,67 +2,61 @@
2
2
 
3
3
  **Easy responsive images for Jekyll.**
4
4
 
5
- It's easy to throw an image on a webpage and call it a day. Doing justice to your users by serving it
6
- efficiently on all screen sizes is tedious and tricky. Tedious, tricky things should be automated.
7
-
8
- Jekyll Picture Tag is a liquid tag that adds responsive images to your
9
- [Jekyll](http://jekyllrb.com) static site. It automatically creates resized,
10
- reformatted source images, is fully configurable, implements sensible defaults,
11
- and solves both the art direction and resolution switching problems, with a
12
- little YAML configuration and a simple template tag. It can be configured to
13
- work with JavaScript libraries such as
5
+ It's easy to throw an image on a webpage and call it a day. Doing justice to your users by serving
6
+ it efficiently on all browsers and screen sizes is tedious and tricky. Tedious, tricky things should be
7
+ automated.
8
+
9
+ Jekyll Picture Tag is a liquid tag that adds responsive images to your [Jekyll](http://jekyllrb.com)
10
+ static site. It automatically creates resized, reformatted source images, is fully configurable,
11
+ implements sensible defaults, and solves both the art direction and resolution switching problems,
12
+ with a little YAML configuration and a simple template tag. It offers several different output
13
+ formats, and can be configured to work with JavaScript libraries such as
14
14
  [LazyLoad](https://github.com/verlok/lazyload).
15
15
 
16
+ ## Documentation
17
+
18
+ It's all in the [`docs`](docs/) folder:
19
+
20
+ * [Installation](docs/installation.md)
21
+ * [Usage](docs/usage.md)
22
+ * [Global settings](docs/global_configuration.md)
23
+ * [Presets](docs/presets.md)
24
+ * [Other notes](docs/notes.md)
25
+ * [Contribute](contributing.md)
26
+ * [Release History](#release-history)
27
+ * [License](LICENSE.txt)
28
+
16
29
  ## Why use Jekyll Picture Tag?
17
30
 
18
- **Performance:** The fastest sites are static sites. If we're not using responsive images we're
19
- throwing those performance gains away by serving kilobytes of pixels a user will never see.
31
+ **Performance:** The fastest sites are static sites, but when you plonk a 2mb picture of your dog at
32
+ the top of a blog post you're throwing it all away.
20
33
 
21
34
  **Design:** Your desktop image may not work well on mobile, regardless of its resolution. We often
22
- want to do more than just resize images for different screen sizes.
35
+ want to do more than just resize images for different screen sizes, we want to crop them or use a
36
+ different image entirely.
23
37
 
24
38
  **Developer Sanity:** Image downloading starts before the browser has parsed your CSS and
25
39
  JavaScript; this gets them on the page *fast*, but it leads to some ridiculously verbose markup.
26
- Ultimately, to serve responsive images correctly, we must:
40
+ To serve responsive images correctly, we must:
27
41
 
28
- - Generate, name, and organize the required images (formats \* resolutions, for each source image)
29
- - Inform the browser about the image itself-- format, size, URI, and the screen sizes where it
30
- should be used.
31
- - Inform the browser how large the space for that image on the page will be (which also probably has associated media
32
- queries).
42
+ - Generate, name, and organize the required images (formats \* resolutions \* source images)
43
+ - Inform the browser about the image itself-- format, size, URI, and the screen sizes where it
44
+ should be used.
45
+ - Inform the browser how large the space for that image on the page will be (which also probably
46
+ has associated media queries).
33
47
 
34
- It's a lot. It's tedious and complicated. Jekyll Picture Tag automates it.
48
+ It's a lot. It's tedious and complicated. Jekyll Picture Tag makes it easy.
35
49
 
36
50
  ## Features
37
51
 
38
52
  * Automatic generation of resized, converted image files.
39
- * Automatic generation of complex markup in one of several different formats.
53
+ * Automatic generation of complex markup in several different formats.
40
54
  * No configuration required, extensive configuration available.
41
- * Auto-select between `<picture>` or lone `<img>` as necessary.
42
- * Support for both width based and pixel ratio based srcsets.
43
- * Webp conversion.
44
55
  * `sizes` attribute assistance.
45
- * named media queries so you don't have to remember them.
46
- * Optional `<noscript>` tag with a basic fallback image, so you can lazy load without excluding your
47
- javascript-impaired users.
48
56
  * Optionally, automatically link to the source image. Or manually link to anywhere else, with just a
49
- tag parameter!
57
+ tag parameter!
50
58
 
51
-
52
- # Documentation
53
-
54
- It's mostly in the wiki.
55
-
56
- * [Installation](https://github.com/rbuchberger/jekyll-picture-tag/wiki/Installation)
57
- * [Usage](https://github.com/rbuchberger/jekyll-picture-tag/wiki/Liquid-Tag-Usage)
58
- * [Global settings](https://github.com/rbuchberger/jekyll-picture-tag/wiki/Global-Configuration)
59
- * [Presets](https://github.com/rbuchberger/jekyll-picture-tag/wiki/Writing-Presets)
60
- * [Other notes](https://github.com/rbuchberger/jekyll-picture-tag/wiki/Miscellaneous-notes-and-FAQ)
61
- * [Contribute](#contribute)
62
- * [Release History](#release-history)
63
- * [License](#license)
64
-
65
- # Quick start / Demo
59
+ ## Quick start / Demo
66
60
 
67
61
  **All configuration is optional.** Here's the simplest possible use case:
68
62
 
@@ -74,6 +68,8 @@ group :jekyll_plugins do
74
68
  end
75
69
  ```
76
70
 
71
+ Run `bundle install`
72
+
77
73
  Put this liquid tag somewhere:
78
74
 
79
75
  `{% picture test.jpg %}`
@@ -81,21 +77,32 @@ Put this liquid tag somewhere:
81
77
  Get this in your generated site:
82
78
 
83
79
  ```html
84
- <!-- Line breaks added for readability, the actual markup will not have them. -->
80
+ <!-- Formatted for readability -->
85
81
 
86
82
  <img src="/generated/test-800-195f7d.jpg"
87
- srcset="/generated/test-400-195f7d.jpg 400w,
88
- /generated/test-600-195f7d.jpg 600w,
89
- /generated/test-800-195f7d.jpg 800w,
90
- /generated/test-1000-195f7d.jpg 1000w">
83
+ srcset="
84
+ /generated/test-400-195f7d.jpg 400w,
85
+ /generated/test-600-195f7d.jpg 600w,
86
+ /generated/test-800-195f7d.jpg 800w,
87
+ /generated/test-1000-195f7d.jpg 1000w
88
+ ">
91
89
  ```
92
90
 
93
- ### Here's a more complete example:
91
+ ### Look man, that's cool and all but I just want webp.
94
92
 
95
- With this configuration:
93
+ Create `_data/picture.yml`, add the following:
96
94
 
97
95
  ```yml
96
+ markup_presets:
97
+ default:
98
+ formats: [webp, original]
99
+ ```
100
+
101
+ If you get errors, make sure you have imagemagick installed with a webp delegate.
102
+
103
+ ### Here's a more complex example:
98
104
 
105
+ ```yml
99
106
  # _data/picture.yml
100
107
 
101
108
  media_presets:
@@ -108,7 +115,6 @@ markup_presets:
108
115
  sizes:
109
116
  mobile: 80vw
110
117
  size: 500px
111
-
112
118
  ```
113
119
 
114
120
  Write this:
@@ -164,20 +170,19 @@ for image conversions, so it must be installed on your system. There's a very go
164
170
  already have it. If you want to build webp images, you will need to install a webp delegate for it
165
171
  as well.
166
172
 
167
- # Contribute
168
-
169
- Report bugs and feature proposals in the
170
- [Github issue tracker](https://github.com/robwierzbowski/jekyll-picture-tag/issues).
171
-
172
- Pull requests are encouraged. With a few exceptions, this plugin is written to follow the Rubocop
173
- default settings (except the frozen string literal comment).
174
-
175
- If you add a new setting, it is helpful to add a default value (look under `lib/defaults/`) and
176
- relevant documentation. Don't let that stop you from submitting a pull request, though! Just allow
177
- modifications and I'll take care of it.
178
-
179
- # Release History
180
-
173
+ ## Release History
174
+
175
+ * 1.7.0 Aug 12, 2019
176
+ * Add support for setting generated image quality, either generally or specific to given
177
+ formats.
178
+ * Add support for spaces and other url-encoded characters in filenames
179
+ * Documentation restructure - Moved it out of the wiki, into the `docs` folder.
180
+ * Bugfix: Fallback image width will now be checked against source image width.
181
+ * Bugfix: Minor fix to nomarkdown wrapper output
182
+ * link_source will now target the base source image, rather than finding the biggest one.
183
+ * Remove fastimage dependency, add addressable dependency.
184
+ * Moderately significant refactoring and code cleanup
185
+ * Decent set of tests added
181
186
  * 1.6.0 Jul 2, 2019:
182
187
  * Missing Preset warning respects `data_dir` setting
183
188
  * Add `continue_on_missing` option
@@ -203,14 +208,9 @@ modifications and I'll take care of it.
203
208
  * auto-orient images before stripping metadata
204
209
  * 1.0.2 Jan 18, 2019: Fix ruby version specification
205
210
  * 1.0.1 Jan 13, 2019: Added ruby version checking
206
- * **1.0.0** Nov 27, 2018: Rewrite from the ground up. See [the migration
207
- guide](https://github.com/rbuchberger/jekyll-picture-tag/wiki/Migrating-from-versions-prior-to-1.0).
211
+ * **1.0.0** Nov 27, 2018: Rewrite from the ground up. See the [migration guide](docs/migration.md).
208
212
  * 0.2.2 Aug 2, 2013: Bugfixes
209
213
  * 0.2.1 Jul 17, 2013: Refactor again, add Liquid parsing.
210
214
  * 0.2.0 Jul 14, 2013: Rewrite code base, bring in line with Jekyll Image Tag.
211
215
  * 0.1.1 Jul 5, 2013: Quick round of code improvements.
212
216
  * 0.1.0 Jul 5, 2013: Initial release.
213
-
214
- # License
215
-
216
- [BSD-NEW](http://en.wikipedia.org/wiki/BSD_License)