html-pipeline-plus 2.10.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,137 @@
1
+ HTML::Pipeline.require_dependency('sanitize', 'SanitizationFilter')
2
+
3
+ module HTML
4
+ class Pipeline
5
+ # HTML filter with sanization routines and whitelists. This module defines
6
+ # what HTML is allowed in user provided content and fixes up issues with
7
+ # unbalanced tags and whatnot.
8
+ #
9
+ # See the Sanitize docs for more information on the underlying library:
10
+ #
11
+ # https://github.com/rgrove/sanitize/#readme
12
+ #
13
+ # Context options:
14
+ # :whitelist - The sanitizer whitelist configuration to use. This
15
+ # can be one of the options constants defined in this
16
+ # class or a custom sanitize options hash.
17
+ # :anchor_schemes - The URL schemes to allow in <a href> attributes. The
18
+ # default set is provided in the ANCHOR_SCHEMES
19
+ # constant in this class. If passed, this overrides any
20
+ # schemes specified in the whitelist configuration.
21
+ #
22
+ # This filter does not write additional information to the context.
23
+ class SanitizationFilter < Filter
24
+ LISTS = Set.new(%w[ul ol].freeze)
25
+ LIST_ITEM = 'li'.freeze
26
+
27
+ # List of table child elements. These must be contained by a <table> element
28
+ # or they are not allowed through. Otherwise they can be used to break out
29
+ # of places we're using tables to contain formatted user content (like pull
30
+ # request review comments).
31
+ TABLE_ITEMS = Set.new(%w[tr td th].freeze)
32
+ TABLE = 'table'.freeze
33
+ TABLE_SECTIONS = Set.new(%w[thead tbody tfoot].freeze)
34
+
35
+ # These schemes are the only ones allowed in <a href> attributes by default.
36
+ ANCHOR_SCHEMES = ['http', 'https', 'mailto', 'xmpp', :relative, 'github-windows', 'github-mac', 'irc', 'ircs'].freeze
37
+
38
+ # The main sanitization whitelist. Only these elements and attributes are
39
+ # allowed through by default.
40
+ WHITELIST = {
41
+ elements: %w[
42
+ h1 h2 h3 h4 h5 h6 h7 h8 br b i strong em a pre code img tt
43
+ div ins del sup sub p ol ul table thead tbody tfoot blockquote
44
+ dl dt dd kbd q samp var hr ruby rt rp li tr td th s strike summary
45
+ details caption figure figcaption
46
+ abbr bdo cite dfn mark small span time wbr
47
+ ].freeze,
48
+ remove_contents: ['script'].freeze,
49
+ attributes: {
50
+ 'a' => ['href'].freeze,
51
+ 'img' => %w[src longdesc].freeze,
52
+ 'div' => %w[itemscope itemtype].freeze,
53
+ 'blockquote' => ['cite'].freeze,
54
+ 'del' => ['cite'].freeze,
55
+ 'ins' => ['cite'].freeze,
56
+ 'q' => ['cite'].freeze,
57
+ all: %w[abbr accept accept-charset
58
+ accesskey action align alt
59
+ aria-describedby aria-hidden aria-label aria-labelledby
60
+ axis border cellpadding cellspacing char
61
+ charoff charset checked
62
+ clear cols colspan color
63
+ compact coords datetime dir
64
+ disabled enctype for frame
65
+ headers height hreflang
66
+ hspace ismap label lang
67
+ maxlength media method
68
+ multiple name nohref noshade
69
+ nowrap open prompt readonly rel rev
70
+ rows rowspan rules scope
71
+ selected shape size span
72
+ start summary tabindex target
73
+ title type usemap valign value
74
+ vspace width itemprop].freeze
75
+ }.freeze,
76
+ protocols: {
77
+ 'a' => { 'href' => ANCHOR_SCHEMES }.freeze,
78
+ 'blockquote' => { 'cite' => ['http', 'https', :relative].freeze },
79
+ 'del' => { 'cite' => ['http', 'https', :relative].freeze },
80
+ 'ins' => { 'cite' => ['http', 'https', :relative].freeze },
81
+ 'q' => { 'cite' => ['http', 'https', :relative].freeze },
82
+ 'img' => {
83
+ 'src' => ['http', 'https', :relative].freeze,
84
+ 'longdesc' => ['http', 'https', :relative].freeze
85
+ }.freeze
86
+ },
87
+ transformers: [
88
+ # Top-level <li> elements are removed because they can break out of
89
+ # containing markup.
90
+ lambda { |env|
91
+ name = env[:node_name]
92
+ node = env[:node]
93
+ if name == LIST_ITEM && node.ancestors.none? { |n| LISTS.include?(n.name) }
94
+ node.replace(node.children)
95
+ end
96
+ },
97
+
98
+ # Table child elements that are not contained by a <table> are removed.
99
+ lambda { |env|
100
+ name = env[:node_name]
101
+ node = env[:node]
102
+ if (TABLE_SECTIONS.include?(name) || TABLE_ITEMS.include?(name)) && node.ancestors.none? { |n| n.name == TABLE }
103
+ node.replace(node.children)
104
+ end
105
+ }
106
+ ].freeze
107
+ }.freeze
108
+
109
+ # A more limited sanitization whitelist. This includes all attributes,
110
+ # protocols, and transformers from WHITELIST but with a more locked down
111
+ # set of allowed elements.
112
+ LIMITED = WHITELIST.merge(
113
+ elements: %w[b i strong em a pre code img ins del sup sub mark abbr p ol ul li]
114
+ )
115
+
116
+ # Strip all HTML tags from the document.
117
+ FULL = { elements: [] }.freeze
118
+
119
+ # Sanitize markup using the Sanitize library.
120
+ def call
121
+ Sanitize.clean_node!(doc, whitelist)
122
+ end
123
+
124
+ # The whitelist to use when sanitizing. This can be passed in the context
125
+ # hash to the filter but defaults to WHITELIST constant value above.
126
+ def whitelist
127
+ whitelist = context[:whitelist] || WHITELIST
128
+ anchor_schemes = context[:anchor_schemes]
129
+ return whitelist unless anchor_schemes
130
+ whitelist = whitelist.dup
131
+ whitelist[:protocols] = (whitelist[:protocols] || {}).dup
132
+ whitelist[:protocols]['a'] = (whitelist[:protocols]['a'] || {}).merge('href' => anchor_schemes)
133
+ whitelist
134
+ end
135
+ end
136
+ end
137
+ end
@@ -0,0 +1,44 @@
1
+ HTML::Pipeline.require_dependency('rouge', 'SyntaxHighlightFilter')
2
+
3
+ module HTML
4
+ class Pipeline
5
+ # HTML Filter that syntax highlights code blocks wrapped
6
+ # in <pre lang="...">.
7
+ class SyntaxHighlightFilter < Filter
8
+ def initialize(*args)
9
+ super(*args)
10
+ @formatter = Rouge::Formatters::HTML.new
11
+ end
12
+
13
+ def call
14
+ doc.search('pre').each do |node|
15
+ default = context[:highlight] && context[:highlight].to_s
16
+ next unless lang = node['lang'] || default
17
+ next unless lexer = lexer_for(lang)
18
+ text = node.inner_text
19
+
20
+ html = highlight_with_timeout_handling(text, lang)
21
+ next if html.nil?
22
+
23
+ node.inner_html = html
24
+ klass = node['class']
25
+ scope = context[:scope] || "highlight-#{lang}"
26
+ klass = [klass, scope].compact.join ' '
27
+
28
+ node['class'] = klass
29
+ end
30
+ doc
31
+ end
32
+
33
+ def highlight_with_timeout_handling(text, lang)
34
+ Rouge.highlight(text, lang, @formatter)
35
+ rescue Timeout::Error => _
36
+ nil
37
+ end
38
+
39
+ def lexer_for(lang)
40
+ Rouge::Lexer.find(lang)
41
+ end
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,14 @@
1
+ module HTML
2
+ class Pipeline
3
+ class TextFilter < Filter
4
+ attr_reader :text
5
+
6
+ def initialize(text, context = nil, result = nil)
7
+ raise TypeError, 'text cannot be HTML' if text.is_a?(DocumentFragment)
8
+ # Ensure that this is always a string
9
+ @text = text.respond_to?(:to_str) ? text.to_str : text.to_s
10
+ super nil, context, result
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,23 @@
1
+ HTML::Pipeline.require_dependency('redcloth', 'RedCloth')
2
+
3
+ module HTML
4
+ class Pipeline
5
+ # HTML Filter that converts Textile text into HTML and converts into a
6
+ # DocumentFragment. This is different from most filters in that it can take a
7
+ # non-HTML as input. It must be used as the first filter in a pipeline.
8
+ #
9
+ # Context options:
10
+ # :autolink => false Disable autolinking URLs
11
+ #
12
+ # This filter does not write any additional information to the context hash.
13
+ #
14
+ # NOTE This filter is provided for really old comments only. It probably
15
+ # shouldn't be used for anything new.
16
+ class TextileFilter < TextFilter
17
+ # Convert Textile to HTML and convert into a DocumentFragment.
18
+ def call
19
+ RedCloth.new(@text).to_html
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,67 @@
1
+ HTML::Pipeline.require_dependency('escape_utils', 'TableOfContentsFilter')
2
+
3
+ module HTML
4
+ class Pipeline
5
+ # HTML filter that adds an 'id' attribute to all headers
6
+ # in a document, so they can be accessed from a table of contents.
7
+ #
8
+ # Generates the Table of Contents, with links to each header.
9
+ #
10
+ # Examples
11
+ #
12
+ # TocPipeline =
13
+ # HTML::Pipeline.new [
14
+ # HTML::Pipeline::TableOfContentsFilter
15
+ # ]
16
+ # # => #<HTML::Pipeline:0x007fc13c4528d8...>
17
+ # orig = %(<h1>Ice cube</h1><p>is not for the pop chart</p>)
18
+ # # => "<h1>Ice cube</h1><p>is not for the pop chart</p>"
19
+ # result = {}
20
+ # # => {}
21
+ # TocPipeline.call(orig, {}, result)
22
+ # # => {:toc=> ...}
23
+ # result[:toc]
24
+ # # => "<ul class=\"section-nav\">\n<li><a href=\"#ice-cube\">...</li><ul>"
25
+ # result[:output].to_s
26
+ # # => "<h1>\n<a id=\"ice-cube\" class=\"anchor\" href=\"#ice-cube\">..."
27
+ class TableOfContentsFilter < Filter
28
+ PUNCTUATION_REGEXP = RUBY_VERSION > '1.9' ? /[^\p{Word}\- ]/u : /[^\w\- ]/
29
+
30
+ # The icon that will be placed next to an anchored rendered markdown header
31
+ def anchor_icon
32
+ context[:anchor_icon] || '<span aria-hidden="true" class="octicon octicon-link"></span>'
33
+ end
34
+
35
+ def call
36
+ result[:toc] = ''
37
+
38
+ headers = Hash.new(0)
39
+ doc.css('h1, h2, h3, h4, h5, h6').each do |node|
40
+ text = node.text
41
+ id = ascii_downcase(text)
42
+ id.gsub!(PUNCTUATION_REGEXP, '') # remove punctuation
43
+ id.tr!(' ', '-') # replace spaces with dash
44
+
45
+ uniq = headers[id] > 0 ? "-#{headers[id]}" : ''
46
+ headers[id] += 1
47
+ if header_content = node.children.first
48
+ result[:toc] << %(<li><a href="##{id}#{uniq}">#{EscapeUtils.escape_html(text)}</a></li>\n)
49
+ header_content.add_previous_sibling(%(<a id="#{id}#{uniq}" class="anchor" href="##{id}#{uniq}" aria-hidden="true">#{anchor_icon}</a>))
50
+ end
51
+ end
52
+ result[:toc] = %(<ul class="section-nav">\n#{result[:toc]}</ul>) unless result[:toc].empty?
53
+ doc
54
+ end
55
+
56
+ if RUBY_VERSION >= '2.4'
57
+ def ascii_downcase(str)
58
+ str.downcase(:ascii)
59
+ end
60
+ else
61
+ def ascii_downcase(str)
62
+ str.downcase
63
+ end
64
+ end
65
+ end
66
+ end
67
+ end
@@ -0,0 +1,5 @@
1
+ module HTML
2
+ class Pipeline
3
+ VERSION = '2.10.1'.freeze
4
+ end
5
+ end
@@ -0,0 +1,207 @@
1
+ require 'nokogiri'
2
+ require 'active_support/xml_mini/nokogiri' # convert Documents to hashes
3
+
4
+ module HTML
5
+ # GitHub HTML processing filters and utilities. This module includes a small
6
+ # framework for defining DOM based content filters and applying them to user
7
+ # provided content.
8
+ #
9
+ # See HTML::Pipeline::Filter for information on building filters.
10
+ #
11
+ # Construct a Pipeline for running multiple HTML filters. A pipeline is created once
12
+ # with one to many filters, and it then can be `call`ed many times over the course
13
+ # of its lifetime with input.
14
+ #
15
+ # filters - Array of Filter objects. Each must respond to call(doc,
16
+ # context) and return the modified DocumentFragment or a
17
+ # String containing HTML markup. Filters are performed in the
18
+ # order provided.
19
+ # default_context - The default context hash. Values specified here will be merged
20
+ # into values from the each individual pipeline run. Can NOT be
21
+ # nil. Default: empty Hash.
22
+ # result_class - The default Class of the result object for individual
23
+ # calls. Default: Hash. Protip: Pass in a Struct to get
24
+ # some semblance of type safety.
25
+ class Pipeline
26
+ autoload :VERSION, 'html/pipeline/version'
27
+ autoload :Filter, 'html/pipeline/filter'
28
+ autoload :AbsoluteSourceFilter, 'html/pipeline/absolute_source_filter'
29
+ autoload :BodyContent, 'html/pipeline/body_content'
30
+ autoload :AutolinkFilter, 'html/pipeline/autolink_filter'
31
+ autoload :CamoFilter, 'html/pipeline/camo_filter'
32
+ autoload :EmailReplyFilter, 'html/pipeline/email_reply_filter'
33
+ autoload :EmojiFilter, 'html/pipeline/emoji_filter'
34
+ autoload :HttpsFilter, 'html/pipeline/https_filter'
35
+ autoload :ImageFilter, 'html/pipeline/image_filter'
36
+ autoload :ImageMaxWidthFilter, 'html/pipeline/image_max_width_filter'
37
+ autoload :MarkdownFilter, 'html/pipeline/markdown_filter'
38
+ autoload :MentionFilter, 'html/pipeline/@mention_filter'
39
+ autoload :PlainTextInputFilter, 'html/pipeline/plain_text_input_filter'
40
+ autoload :SanitizationFilter, 'html/pipeline/sanitization_filter'
41
+ autoload :SyntaxHighlightFilter, 'html/pipeline/syntax_highlight_filter'
42
+ autoload :TextileFilter, 'html/pipeline/textile_filter'
43
+ autoload :TableOfContentsFilter, 'html/pipeline/toc_filter'
44
+ autoload :TextFilter, 'html/pipeline/text_filter'
45
+
46
+ class MissingDependencyError < RuntimeError; end
47
+ def self.require_dependency(name, requirer)
48
+ require name
49
+ rescue LoadError => e
50
+ raise MissingDependencyError,
51
+ "Missing dependency '#{name}' for #{requirer}. See README.md for details.\n#{e.class.name}: #{e}"
52
+ end
53
+
54
+ # Our DOM implementation.
55
+ DocumentFragment = Nokogiri::HTML::DocumentFragment
56
+
57
+ # Parse a String into a DocumentFragment object. When a DocumentFragment is
58
+ # provided, return it verbatim.
59
+ def self.parse(document_or_html)
60
+ document_or_html ||= ''
61
+ if document_or_html.is_a?(String)
62
+ DocumentFragment.parse(document_or_html)
63
+ else
64
+ document_or_html
65
+ end
66
+ end
67
+
68
+ # Public: Returns an Array of Filter objects for this Pipeline.
69
+ attr_reader :filters
70
+
71
+ # Public: Instrumentation service for the pipeline.
72
+ # Set an ActiveSupport::Notifications compatible object to enable.
73
+ attr_accessor :instrumentation_service
74
+
75
+ # Public: String name for this Pipeline. Defaults to Class name.
76
+ attr_writer :instrumentation_name
77
+ def instrumentation_name
78
+ return @instrumentation_name if defined?(@instrumentation_name)
79
+ @instrumentation_name = self.class.name
80
+ end
81
+
82
+ class << self
83
+ # Public: Default instrumentation service for new pipeline objects.
84
+ attr_accessor :default_instrumentation_service
85
+ end
86
+
87
+ def initialize(filters, default_context = {}, result_class = nil)
88
+ raise ArgumentError, 'default_context cannot be nil' if default_context.nil?
89
+ @filters = filters.flatten.freeze
90
+ @default_context = default_context.freeze
91
+ @result_class = result_class || Hash
92
+ @instrumentation_service = self.class.default_instrumentation_service
93
+ end
94
+
95
+ # Apply all filters in the pipeline to the given HTML.
96
+ #
97
+ # html - A String containing HTML or a DocumentFragment object.
98
+ # context - The context hash passed to each filter. See the Filter docs
99
+ # for more info on possible values. This object MUST NOT be modified
100
+ # in place by filters. Use the Result for passing state back.
101
+ # result - The result Hash passed to each filter for modification. This
102
+ # is where Filters store extracted information from the content.
103
+ #
104
+ # Returns the result Hash after being filtered by this Pipeline. Contains an
105
+ # :output key with the DocumentFragment or String HTML markup based on the
106
+ # output of the last filter in the pipeline.
107
+ def call(html, context = {}, result = nil)
108
+ context = @default_context.merge(context)
109
+ context = context.freeze
110
+ result ||= @result_class.new
111
+ payload = default_payload filters: @filters.map(&:name),
112
+ context: context, result: result
113
+ instrument 'call_pipeline.html_pipeline', payload do
114
+ result[:output] =
115
+ @filters.inject(html) do |doc, filter|
116
+ perform_filter(filter, doc, context, result)
117
+ end
118
+ end
119
+ result
120
+ end
121
+
122
+ # Internal: Applies a specific filter to the supplied doc.
123
+ #
124
+ # The filter is instrumented.
125
+ #
126
+ # Returns the result of the filter.
127
+ def perform_filter(filter, doc, context, result)
128
+ payload = default_payload filter: filter.name,
129
+ context: context, result: result
130
+ instrument 'call_filter.html_pipeline', payload do
131
+ filter.call(doc, context, result)
132
+ end
133
+ end
134
+
135
+ # Like call but guarantee the value returned is a DocumentFragment.
136
+ # Pipelines may return a DocumentFragment or a String. Callers that need a
137
+ # DocumentFragment should use this method.
138
+ def to_document(input, context = {}, result = nil)
139
+ result = call(input, context, result)
140
+ HTML::Pipeline.parse(result[:output])
141
+ end
142
+
143
+ # Like call but guarantee the value returned is a string of HTML markup.
144
+ def to_html(input, context = {}, result = nil)
145
+ result = call(input, context, result = nil)
146
+ output = result[:output]
147
+ if output.respond_to?(:to_html)
148
+ output.to_html
149
+ else
150
+ output.to_s
151
+ end
152
+ end
153
+
154
+ # Public: setup instrumentation for this pipeline.
155
+ #
156
+ # Returns nothing.
157
+ def setup_instrumentation(name = nil, service = nil)
158
+ self.instrumentation_name = name
159
+ self.instrumentation_service =
160
+ service || self.class.default_instrumentation_service
161
+ end
162
+
163
+ # Internal: if the `instrumentation_service` object is set, instruments the
164
+ # block, otherwise the block is ran without instrumentation.
165
+ #
166
+ # Returns the result of the provided block.
167
+ def instrument(event, payload = nil)
168
+ payload ||= default_payload
169
+ return yield(payload) unless instrumentation_service
170
+ instrumentation_service.instrument event, payload do |payload|
171
+ yield payload
172
+ end
173
+ end
174
+
175
+ # Internal: Default payload for instrumentation.
176
+ #
177
+ # Accepts a Hash of additional payload data to be merged.
178
+ #
179
+ # Returns a Hash.
180
+ def default_payload(payload = {})
181
+ { pipeline: instrumentation_name }.merge(payload)
182
+ end
183
+ end
184
+ end
185
+
186
+ # XXX nokogiri monkey patches for 1.8
187
+ unless ''.respond_to?(:force_encoding)
188
+ class Nokogiri::XML::Node
189
+ # Work around an issue with utf-8 encoded data being erroneously converted to
190
+ # ... some other shit when replacing text nodes. See 'utf-8 output 2' in
191
+ # user_content_test.rb for details.
192
+ def replace_with_encoding_fix(replacement)
193
+ if replacement.respond_to?(:to_str)
194
+ replacement = document.fragment("<div>#{replacement}</div>").children.first.children
195
+ end
196
+ replace_without_encoding_fix(replacement)
197
+ end
198
+
199
+ alias replace_without_encoding_fix replace
200
+ alias replace replace_with_encoding_fix
201
+
202
+ def swap(replacement)
203
+ replace(replacement)
204
+ self
205
+ end
206
+ end
207
+ end
data/test.txt ADDED
@@ -0,0 +1,13 @@
1
+
2
+ install depend library:
3
+
4
+ gem install appraisal
5
+ gem install email_reply_parser
6
+ gem install RedCloth
7
+ gem install RedCloth --platform x64_mingw32
8
+
9
+ bundle install
10
+
11
+ test:
12
+
13
+ ruby -Ilib:test ./test/test_helper.rb
metadata ADDED
@@ -0,0 +1,115 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: html-pipeline-plus
3
+ version: !ruby/object:Gem::Version
4
+ version: 2.10.1
5
+ platform: ruby
6
+ authors:
7
+ - Ryan Tomayko
8
+ - Jerry Cheung
9
+ - Garen J. Torikian
10
+ - shines77
11
+ autorequire:
12
+ bindir: bin
13
+ cert_chain: []
14
+ date: 2019-02-18 00:00:00.000000000 Z
15
+ dependencies:
16
+ - !ruby/object:Gem::Dependency
17
+ name: activesupport
18
+ requirement: !ruby/object:Gem::Requirement
19
+ requirements:
20
+ - - ">="
21
+ - !ruby/object:Gem::Version
22
+ version: '2'
23
+ type: :runtime
24
+ prerelease: false
25
+ version_requirements: !ruby/object:Gem::Requirement
26
+ requirements:
27
+ - - ">="
28
+ - !ruby/object:Gem::Version
29
+ version: '2'
30
+ - !ruby/object:Gem::Dependency
31
+ name: nokogiri
32
+ requirement: !ruby/object:Gem::Requirement
33
+ requirements:
34
+ - - ">="
35
+ - !ruby/object:Gem::Version
36
+ version: '1.4'
37
+ type: :runtime
38
+ prerelease: false
39
+ version_requirements: !ruby/object:Gem::Requirement
40
+ requirements:
41
+ - - ">="
42
+ - !ruby/object:Gem::Version
43
+ version: '1.4'
44
+ description: GitHub HTML processing filters and utilities
45
+ email:
46
+ - ryan@github.com
47
+ - jerry@github.com
48
+ - gjtorikian@gmail.com
49
+ - gz_shines@msn.com
50
+ executables: []
51
+ extensions: []
52
+ extra_rdoc_files: []
53
+ files:
54
+ - ".gitignore"
55
+ - ".travis.yml"
56
+ - Appraisals
57
+ - CHANGELOG.md
58
+ - CONTRIBUTING.md
59
+ - Gemfile
60
+ - LICENSE
61
+ - README.md
62
+ - Rakefile
63
+ - bin/html-pipeline-plus
64
+ - html-pipeline-plus.gemspec
65
+ - lib/html/pipeline-plus.rb
66
+ - lib/html/pipeline-plus/@mention_filter.rb
67
+ - lib/html/pipeline-plus/absolute_source_filter.rb
68
+ - lib/html/pipeline-plus/autolink_filter.rb
69
+ - lib/html/pipeline-plus/body_content.rb
70
+ - lib/html/pipeline-plus/camo_filter.rb
71
+ - lib/html/pipeline-plus/email_reply_filter.rb
72
+ - lib/html/pipeline-plus/emoji_filter.rb
73
+ - lib/html/pipeline-plus/filter.rb
74
+ - lib/html/pipeline-plus/https_filter.rb
75
+ - lib/html/pipeline-plus/image_filter.rb
76
+ - lib/html/pipeline-plus/image_max_width_filter.rb
77
+ - lib/html/pipeline-plus/markdown_filter.rb
78
+ - lib/html/pipeline-plus/plain_text_input_filter.rb
79
+ - lib/html/pipeline-plus/sanitization_filter.rb
80
+ - lib/html/pipeline-plus/syntax_highlight_filter.rb
81
+ - lib/html/pipeline-plus/text_filter.rb
82
+ - lib/html/pipeline-plus/textile_filter.rb
83
+ - lib/html/pipeline-plus/toc_filter.rb
84
+ - lib/html/pipeline-plus/version.rb
85
+ - test.txt
86
+ homepage: https://github.com/shines77/html-pipeline-plus/
87
+ licenses:
88
+ - MIT
89
+ metadata: {}
90
+ post_install_message: |
91
+ -------------------------------------------------
92
+ Thank you for installing html-pipeline-plus!
93
+ You must bundle Filter gem dependencies.
94
+ See html-pipeline-plus README.md for more details.
95
+ https://github.com/shines77/html-pipeline-plus#dependencies
96
+ -------------------------------------------------
97
+ rdoc_options: []
98
+ require_paths:
99
+ - lib
100
+ required_ruby_version: !ruby/object:Gem::Requirement
101
+ requirements:
102
+ - - ">="
103
+ - !ruby/object:Gem::Version
104
+ version: '0'
105
+ required_rubygems_version: !ruby/object:Gem::Requirement
106
+ requirements:
107
+ - - ">="
108
+ - !ruby/object:Gem::Version
109
+ version: '0'
110
+ requirements: []
111
+ rubygems_version: 3.0.2
112
+ signing_key:
113
+ specification_version: 4
114
+ summary: Helpers for processing content through a chain of filters
115
+ test_files: []