html-pipeline 2.7.1 → 2.7.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.travis.yml +0 -6
- data/Appraisals +8 -8
- data/Gemfile +13 -22
- data/README.md +5 -6
- data/Rakefile +5 -5
- data/bin/html-pipeline +15 -15
- data/html-pipeline.gemspec +13 -13
- data/lib/html/pipeline.rb +15 -15
- data/lib/html/pipeline/@mention_filter.rb +15 -15
- data/lib/html/pipeline/absolute_source_filter.rb +12 -15
- data/lib/html/pipeline/autolink_filter.rb +1 -1
- data/lib/html/pipeline/camo_filter.rb +2 -2
- data/lib/html/pipeline/email_reply_filter.rb +4 -4
- data/lib/html/pipeline/emoji_filter.rb +20 -21
- data/lib/html/pipeline/filter.rb +5 -8
- data/lib/html/pipeline/https_filter.rb +2 -2
- data/lib/html/pipeline/image_filter.rb +1 -1
- data/lib/html/pipeline/image_max_width_filter.rb +4 -6
- data/lib/html/pipeline/markdown_filter.rb +4 -3
- data/lib/html/pipeline/plain_text_input_filter.rb +1 -1
- data/lib/html/pipeline/sanitization_filter.rb +44 -41
- data/lib/html/pipeline/syntax_highlight_filter.rb +15 -11
- data/lib/html/pipeline/text_filter.rb +2 -2
- data/lib/html/pipeline/textile_filter.rb +1 -1
- data/lib/html/pipeline/toc_filter.rb +9 -9
- data/lib/html/pipeline/version.rb +1 -1
- metadata +8 -8
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 947ea1f82d3c13232378bac704544208961431a4
|
4
|
+
data.tar.gz: f72d0af2a8072741e8e2f993b1aa7c12b99c2faa
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 566aaf8bf2f3a012e79e1fcd3028a85f4d26c087b63ba48f03ffea3e626a93150d8ab5b642ecdb3f425df9108eb8963b7bdbc0e6fdb067f7b0987347a019a785
|
7
|
+
data.tar.gz: 9fb3b6c03a92fc82649704700a20af03487200bc2096ece6725920c9c54903215aecc153c61a75732f61da839dfe0a6834b00b4de491ee5b8a584ccc2752bb61
|
data/.travis.yml
CHANGED
@@ -22,8 +22,6 @@ rvm:
|
|
22
22
|
- 2.4.0
|
23
23
|
- 2.3.1
|
24
24
|
- 2.2.5
|
25
|
-
- 2.1
|
26
|
-
- 2.0
|
27
25
|
- ruby-head
|
28
26
|
|
29
27
|
matrix:
|
@@ -31,10 +29,6 @@ matrix:
|
|
31
29
|
allow_failures:
|
32
30
|
- rvm: ruby-head
|
33
31
|
exclude:
|
34
|
-
- gemfile: gemfiles/rails_5.gemfile
|
35
|
-
rvm: 2.1
|
36
|
-
- gemfile: gemfiles/rails_5.gemfile
|
37
|
-
rvm: 2.0
|
38
32
|
- gemfile: gemfiles/rails_4.gemfile
|
39
33
|
rvm: 2.4.0
|
40
34
|
- gemfile: gemfiles/rails_3.gemfile
|
data/Appraisals
CHANGED
@@ -1,13 +1,13 @@
|
|
1
|
-
appraise
|
2
|
-
gem
|
3
|
-
gem
|
1
|
+
appraise 'rails-3' do
|
2
|
+
gem 'rack', '< 2'
|
3
|
+
gem 'rails', '3.2.22.2'
|
4
4
|
end
|
5
5
|
|
6
|
-
appraise
|
7
|
-
gem
|
8
|
-
gem
|
6
|
+
appraise 'rails-4' do
|
7
|
+
gem 'rack', '< 2'
|
8
|
+
gem 'rails', '~> 4.2.6'
|
9
9
|
end
|
10
10
|
|
11
|
-
appraise
|
12
|
-
gem
|
11
|
+
appraise 'rails-5' do
|
12
|
+
gem 'rails', '~> 5.0.0'
|
13
13
|
end
|
data/Gemfile
CHANGED
@@ -1,32 +1,23 @@
|
|
1
|
-
source
|
1
|
+
source 'https://rubygems.org'
|
2
2
|
|
3
3
|
# Specify your gem's dependencies in html-pipeline.gemspec
|
4
4
|
gemspec
|
5
5
|
|
6
6
|
group :development do
|
7
|
-
gem
|
8
|
-
gem
|
9
|
-
gem
|
7
|
+
gem 'appraisal'
|
8
|
+
gem 'bundler'
|
9
|
+
gem 'rake'
|
10
10
|
end
|
11
11
|
|
12
12
|
group :test do
|
13
|
-
gem
|
14
|
-
gem
|
15
|
-
gem
|
16
|
-
gem
|
17
|
-
gem
|
18
|
-
gem
|
19
|
-
gem
|
13
|
+
gem 'commonmarker', '~> 0.14', require: false
|
14
|
+
gem 'email_reply_parser', '~> 0.5', require: false
|
15
|
+
gem 'gemoji', '~> 2.0', require: false
|
16
|
+
gem 'minitest'
|
17
|
+
gem 'RedCloth', '~> 4.2.9', require: false
|
18
|
+
gem 'rinku', '~> 1.7', require: false
|
19
|
+
gem 'sanitize', '~> 2.0', require: false
|
20
20
|
|
21
|
-
|
22
|
-
|
23
|
-
gem "github-linguist", "~> 2.6.2", :require => false
|
24
|
-
else
|
25
|
-
gem "escape_utils", "~> 1.0", :require => false
|
26
|
-
gem "github-linguist", "~> 2.10", :require => false
|
27
|
-
end
|
28
|
-
|
29
|
-
if RUBY_VERSION < "1.9.3"
|
30
|
-
gem "activesupport", ">= 2", "< 4"
|
31
|
-
end
|
21
|
+
gem 'escape_utils', '~> 1.0', require: false
|
22
|
+
gem 'rouge', '~> 3.1', require: false
|
32
23
|
end
|
data/README.md
CHANGED
@@ -82,7 +82,7 @@ Prints:
|
|
82
82
|
</code></pre>
|
83
83
|
```
|
84
84
|
|
85
|
-
To generate CSS for HTML formatted code, use the [
|
85
|
+
To generate CSS for HTML formatted code, use the [Rouge CSS Theme](https://github.com/jneen/rouge#css-theme-options) `#css` method. `rouge` is a dependency of the `SyntaxHighlightFilter`.
|
86
86
|
|
87
87
|
Some filters take an optional **context** and/or **result** hash. These are
|
88
88
|
used to pass around arguments and metadata between filters in a pipeline. For
|
@@ -163,7 +163,7 @@ EmojiPipeline = Pipeline.new [
|
|
163
163
|
* `MarkdownFilter` - convert markdown to html
|
164
164
|
* `PlainTextInputFilter` - html escape text and wrap the result in a div
|
165
165
|
* `SanitizationFilter` - whitelist sanitize user markup
|
166
|
-
* `SyntaxHighlightFilter` -
|
166
|
+
* `SyntaxHighlightFilter` - code syntax highlighter
|
167
167
|
* `TextileFilter` - convert textile to html
|
168
168
|
* `TableOfContentsFilter` - anchor headings with name attributes and generate Table of Contents html unordered list linking headings
|
169
169
|
|
@@ -171,12 +171,12 @@ EmojiPipeline = Pipeline.new [
|
|
171
171
|
|
172
172
|
Filter gem dependencies are not bundled; you must bundle the filter's gem
|
173
173
|
dependencies. The below list details filters with dependencies. For example,
|
174
|
-
`SyntaxHighlightFilter` uses [
|
174
|
+
`SyntaxHighlightFilter` uses [rouge](https://github.com/jneen/rouge)
|
175
175
|
to detect and highlight languages. For example, to use the `SyntaxHighlightFilter`,
|
176
176
|
add the following to your Gemfile:
|
177
177
|
|
178
178
|
```ruby
|
179
|
-
gem '
|
179
|
+
gem 'rouge'
|
180
180
|
```
|
181
181
|
|
182
182
|
* `AutolinkFilter` - `rinku`
|
@@ -185,7 +185,7 @@ gem 'github-linguist'
|
|
185
185
|
* `MarkdownFilter` - `commonmarker`
|
186
186
|
* `PlainTextInputFilter` - `escape_utils`
|
187
187
|
* `SanitizationFilter` - `sanitize`
|
188
|
-
* `SyntaxHighlightFilter` - `
|
188
|
+
* `SyntaxHighlightFilter` - `rouge`
|
189
189
|
* `TextileFilter` - `RedCloth`
|
190
190
|
|
191
191
|
_Note:_ See [Gemfile](/Gemfile) `:test` block for version requirements.
|
@@ -242,7 +242,6 @@ Here are some extensions people have built:
|
|
242
242
|
* [tilt-html-pipeline](https://github.com/bradgessler/tilt-html-pipeline)
|
243
243
|
* [html-pipeline-wiki-link'](https://github.com/lifted-studios/html-pipeline-wiki-link) - WikiMedia-style wiki links
|
244
244
|
* [task_list](https://github.com/github/task_list) - GitHub flavor Markdown Task List
|
245
|
-
* [html-pipeline-rouge_filter](https://github.com/JuanitoFatas/html-pipeline-rouge_filter) - Syntax highlight with [Rouge](https://github.com/jneen/rouge/)
|
246
245
|
* [html-pipeline-nico_link](https://github.com/rutan/html-pipeline-nico_link) - An HTML::Pipeline filter for [niconico](http://www.nicovideo.jp) description links
|
247
246
|
* [html-pipeline-gitlab](https://gitlab.com/gitlab-org/html-pipeline-gitlab) - This gem implements various filters for html-pipeline used by GitLab
|
248
247
|
* [html-pipeline-youtube](https://github.com/st0012/html-pipeline-youtube) - An HTML::Pipeline filter for YouTube links
|
data/Rakefile
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
#!/usr/bin/env rake
|
2
|
-
require
|
3
|
-
require
|
2
|
+
require 'rubygems'
|
3
|
+
require 'bundler/setup'
|
4
4
|
|
5
|
-
require
|
5
|
+
require 'bundler/gem_tasks'
|
6
6
|
require 'rake/testtask'
|
7
7
|
|
8
8
|
Rake::TestTask.new do |t|
|
9
|
-
t.libs <<
|
9
|
+
t.libs << 'test'
|
10
10
|
t.test_files = FileList['test/**/*_test.rb']
|
11
11
|
t.verbose = true
|
12
12
|
end
|
13
13
|
|
14
|
-
task :
|
14
|
+
task default: :test
|
data/bin/html-pipeline
CHANGED
@@ -4,23 +4,23 @@ require 'html/pipeline'
|
|
4
4
|
require 'optparse'
|
5
5
|
|
6
6
|
# Accept "help", too
|
7
|
-
|
7
|
+
.map! { |a| a == 'help' ? '--help' : a }
|
8
8
|
|
9
|
-
|
9
|
+
onParser.new do |opts|
|
10
10
|
opts.banner = <<-HELP.gsub(/^ /, '')
|
11
11
|
Usage: html-pipeline [-h] [-f]
|
12
12
|
html-pipeline [FILTER [FILTER [...]]] < file.md
|
13
|
-
|
13
|
+
cat file.md | html-pipeline [FILTER [FILTER [...]]]
|
14
14
|
HELP
|
15
15
|
|
16
|
-
|
16
|
+
opts.separator 'Options:'
|
17
17
|
|
18
|
-
opts.on(
|
19
|
-
filters = HTML::Pipeline.constants.grep(/\w+Filter$/)
|
20
|
-
|
18
|
+
opts.on('-f', '--filters', 'List the available filters') do
|
19
|
+
filters = HTML::Pipeline.constants.grep(/\w+Filter$/)
|
20
|
+
.map { |f| f.to_s.gsub(/Filter$/, '') }
|
21
21
|
|
22
22
|
# Text filter doesn't work, no call method
|
23
|
-
filters -= [
|
23
|
+
filters -= ['Text']
|
24
24
|
|
25
25
|
abort <<-HELP.gsub(/^ /, '')
|
26
26
|
Available filters:
|
@@ -38,12 +38,12 @@ if ARGV.empty?
|
|
38
38
|
HTML::Pipeline::ImageMaxWidthFilter,
|
39
39
|
HTML::Pipeline::EmojiFilter,
|
40
40
|
HTML::Pipeline::AutolinkFilter,
|
41
|
-
HTML::Pipeline::TableOfContentsFilter
|
41
|
+
HTML::Pipeline::TableOfContentsFilter
|
42
42
|
]
|
43
43
|
|
44
|
-
# Add syntax highlighting if
|
44
|
+
# Add syntax highlighting if rouge is present
|
45
45
|
begin
|
46
|
-
require '
|
46
|
+
require 'rouge'
|
47
47
|
filters << HTML::Pipeline::SyntaxHighlightFilter
|
48
48
|
rescue LoadError
|
49
49
|
end
|
@@ -52,7 +52,7 @@ else
|
|
52
52
|
|
53
53
|
def filter_named(name)
|
54
54
|
case name
|
55
|
-
when
|
55
|
+
when 'Text'
|
56
56
|
raise NameError # Text filter doesn't work, no call method
|
57
57
|
end
|
58
58
|
|
@@ -70,9 +70,9 @@ else
|
|
70
70
|
end
|
71
71
|
|
72
72
|
context = {
|
73
|
-
:
|
74
|
-
:
|
75
|
-
:
|
73
|
+
asset_root: '/assets',
|
74
|
+
base_url: '/',
|
75
|
+
gfm: true
|
76
76
|
}
|
77
77
|
|
78
78
|
puts HTML::Pipeline.new(filters, context).call(ARGF.read)[:output]
|
data/html-pipeline.gemspec
CHANGED
@@ -1,21 +1,21 @@
|
|
1
|
-
|
2
|
-
require File.expand_path(
|
1
|
+
|
2
|
+
require File.expand_path('../lib/html/pipeline/version', __FILE__)
|
3
3
|
|
4
4
|
Gem::Specification.new do |gem|
|
5
|
-
gem.name =
|
5
|
+
gem.name = 'html-pipeline'
|
6
6
|
gem.version = HTML::Pipeline::VERSION
|
7
|
-
gem.license =
|
8
|
-
gem.authors = [
|
9
|
-
gem.email = [
|
10
|
-
gem.description =
|
11
|
-
gem.summary =
|
12
|
-
gem.homepage =
|
7
|
+
gem.license = 'MIT'
|
8
|
+
gem.authors = ['Ryan Tomayko', 'Jerry Cheung']
|
9
|
+
gem.email = ['ryan@github.com', 'jerry@github.com']
|
10
|
+
gem.description = 'GitHub HTML processing filters and utilities'
|
11
|
+
gem.summary = 'Helpers for processing content through a chain of filters'
|
12
|
+
gem.homepage = 'https://github.com/jch/html-pipeline'
|
13
13
|
|
14
|
-
gem.files = `git ls-files -z`.split("\x0").reject { |f| f =~ %r
|
15
|
-
gem.require_paths = [
|
14
|
+
gem.files = `git ls-files -z`.split("\x0").reject { |f| f =~ %r{^(test|gemfiles|script)/} }
|
15
|
+
gem.require_paths = ['lib']
|
16
16
|
|
17
|
-
gem.add_dependency
|
18
|
-
gem.add_dependency
|
17
|
+
gem.add_dependency 'activesupport', '>= 2'
|
18
|
+
gem.add_dependency 'nokogiri', '>= 1.4'
|
19
19
|
|
20
20
|
gem.post_install_message = <<msg
|
21
21
|
-------------------------------------------------
|
data/lib/html/pipeline.rb
CHANGED
@@ -1,5 +1,5 @@
|
|
1
|
-
require
|
2
|
-
require
|
1
|
+
require 'nokogiri'
|
2
|
+
require 'active_support/xml_mini/nokogiri' # convert Documents to hashes
|
3
3
|
|
4
4
|
module HTML
|
5
5
|
# GitHub HTML processing filters and utilities. This module includes a small
|
@@ -43,12 +43,12 @@ module HTML
|
|
43
43
|
autoload :TableOfContentsFilter, 'html/pipeline/toc_filter'
|
44
44
|
autoload :TextFilter, 'html/pipeline/text_filter'
|
45
45
|
|
46
|
-
class MissingDependencyError <
|
46
|
+
class MissingDependencyError < RuntimeError; end
|
47
47
|
def self.require_dependency(name, requirer)
|
48
48
|
require name
|
49
49
|
rescue LoadError => e
|
50
50
|
raise MissingDependencyError,
|
51
|
-
|
51
|
+
"Missing dependency '#{name}' for #{requirer}. See README.md for details.\n#{e.class.name}: #{e}"
|
52
52
|
end
|
53
53
|
|
54
54
|
# Our DOM implementation.
|
@@ -85,7 +85,7 @@ module HTML
|
|
85
85
|
end
|
86
86
|
|
87
87
|
def initialize(filters, default_context = {}, result_class = nil)
|
88
|
-
raise ArgumentError,
|
88
|
+
raise ArgumentError, 'default_context cannot be nil' if default_context.nil?
|
89
89
|
@filters = filters.flatten.freeze
|
90
90
|
@default_context = default_context.freeze
|
91
91
|
@result_class = result_class || Hash
|
@@ -108,9 +108,9 @@ module HTML
|
|
108
108
|
context = @default_context.merge(context)
|
109
109
|
context = context.freeze
|
110
110
|
result ||= @result_class.new
|
111
|
-
payload = default_payload :
|
112
|
-
|
113
|
-
instrument
|
111
|
+
payload = default_payload filters: @filters.map(&:name),
|
112
|
+
context: context, result: result
|
113
|
+
instrument 'call_pipeline.html_pipeline', payload do
|
114
114
|
result[:output] =
|
115
115
|
@filters.inject(html) do |doc, filter|
|
116
116
|
perform_filter(filter, doc, context, result)
|
@@ -125,9 +125,9 @@ module HTML
|
|
125
125
|
#
|
126
126
|
# Returns the result of the filter.
|
127
127
|
def perform_filter(filter, doc, context, result)
|
128
|
-
payload = default_payload :
|
129
|
-
|
130
|
-
instrument
|
128
|
+
payload = default_payload filter: filter.name,
|
129
|
+
context: context, result: result
|
130
|
+
instrument 'call_filter.html_pipeline', payload do
|
131
131
|
filter.call(doc, context, result)
|
132
132
|
end
|
133
133
|
end
|
@@ -178,13 +178,13 @@ module HTML
|
|
178
178
|
#
|
179
179
|
# Returns a Hash.
|
180
180
|
def default_payload(payload = {})
|
181
|
-
{:
|
181
|
+
{ pipeline: instrumentation_name }.merge(payload)
|
182
182
|
end
|
183
183
|
end
|
184
184
|
end
|
185
185
|
|
186
186
|
# XXX nokogiri monkey patches for 1.8
|
187
|
-
|
187
|
+
unless ''.respond_to?(:force_encoding)
|
188
188
|
class Nokogiri::XML::Node
|
189
189
|
# Work around an issue with utf-8 encoded data being erroneously converted to
|
190
190
|
# ... some other shit when replacing text nodes. See 'utf-8 output 2' in
|
@@ -196,8 +196,8 @@ if not ''.respond_to?(:force_encoding)
|
|
196
196
|
replace_without_encoding_fix(replacement)
|
197
197
|
end
|
198
198
|
|
199
|
-
|
200
|
-
|
199
|
+
alias replace_without_encoding_fix replace
|
200
|
+
alias replace replace_with_encoding_fix
|
201
201
|
|
202
202
|
def swap(replacement)
|
203
203
|
replace(replacement)
|
@@ -29,9 +29,9 @@ module HTML
|
|
29
29
|
# the original text.
|
30
30
|
#
|
31
31
|
# Returns a String replaced with the return of the block.
|
32
|
-
def self.mentioned_logins_in(text, username_pattern=UsernamePattern)
|
32
|
+
def self.mentioned_logins_in(text, username_pattern = UsernamePattern)
|
33
33
|
text.gsub MentionPatterns[username_pattern] do |match|
|
34
|
-
login =
|
34
|
+
login = Regexp.last_match(1)
|
35
35
|
yield match, login, MentionLogins.include?(login.downcase)
|
36
36
|
end
|
37
37
|
end
|
@@ -57,22 +57,22 @@ module HTML
|
|
57
57
|
|
58
58
|
# List of username logins that, when mentioned, link to the blog post
|
59
59
|
# about @mentions instead of triggering a real mention.
|
60
|
-
MentionLogins = %w
|
60
|
+
MentionLogins = %w[
|
61
61
|
mention
|
62
62
|
mentions
|
63
63
|
mentioned
|
64
64
|
mentioning
|
65
|
-
|
65
|
+
].freeze
|
66
66
|
|
67
67
|
# Don't look for mentions in text nodes that are children of these elements
|
68
|
-
IGNORE_PARENTS = %w(pre code a style).to_set
|
68
|
+
IGNORE_PARENTS = %w(pre code a style script).to_set
|
69
69
|
|
70
70
|
def call
|
71
71
|
result[:mentioned_usernames] ||= []
|
72
72
|
|
73
73
|
doc.search('.//text()').each do |node|
|
74
74
|
content = node.to_html
|
75
|
-
next
|
75
|
+
next unless content.include?('@')
|
76
76
|
next if has_ancestor?(node, IGNORE_PARENTS)
|
77
77
|
html = mention_link_filter(content, base_url, info_url, username_pattern)
|
78
78
|
next if html == content
|
@@ -103,7 +103,7 @@ module HTML
|
|
103
103
|
#
|
104
104
|
# Returns a string with @mentions replaced with links. All links have a
|
105
105
|
# 'user-mention' class name attached for styling.
|
106
|
-
def mention_link_filter(text,
|
106
|
+
def mention_link_filter(text, _base_url = '/', info_url = nil, username_pattern = UsernamePattern)
|
107
107
|
self.class.mentioned_logins_in(text, username_pattern) do |match, login, is_mentioned|
|
108
108
|
link =
|
109
109
|
if is_mentioned
|
@@ -116,22 +116,22 @@ module HTML
|
|
116
116
|
end
|
117
117
|
end
|
118
118
|
|
119
|
-
def link_to_mention_info(text, info_url=nil)
|
119
|
+
def link_to_mention_info(text, info_url = nil)
|
120
120
|
return "@#{text}" if info_url.nil?
|
121
|
-
"<a href='#{info_url}' class='user-mention'>"
|
122
|
-
|
123
|
-
|
121
|
+
"<a href='#{info_url}' class='user-mention'>" \
|
122
|
+
"@#{text}" \
|
123
|
+
'</a>'
|
124
124
|
end
|
125
125
|
|
126
126
|
def link_to_mentioned_user(login)
|
127
127
|
result[:mentioned_usernames] |= [login]
|
128
128
|
|
129
129
|
url = base_url.dup
|
130
|
-
url <<
|
130
|
+
url << '/' unless url =~ /[\/~]\z/
|
131
131
|
|
132
|
-
"<a href='#{url << login}' class='user-mention'>"
|
133
|
-
|
134
|
-
|
132
|
+
"<a href='#{url << login}' class='user-mention'>" \
|
133
|
+
"@#{login}" \
|
134
|
+
'</a>'
|
135
135
|
end
|
136
136
|
end
|
137
137
|
end
|
@@ -2,7 +2,6 @@ require 'uri'
|
|
2
2
|
|
3
3
|
module HTML
|
4
4
|
class Pipeline
|
5
|
-
|
6
5
|
class AbsoluteSourceFilter < Filter
|
7
6
|
# HTML Filter for replacing relative and root relative image URLs with
|
8
7
|
# fully qualified URLs
|
@@ -18,31 +17,29 @@ module HTML
|
|
18
17
|
# This filter does not write additional information to the context.
|
19
18
|
# This filter would need to be run before CamoFilter.
|
20
19
|
def call
|
21
|
-
doc.search(
|
20
|
+
doc.search('img').each do |element|
|
22
21
|
next if element['src'].nil? || element['src'].empty?
|
23
22
|
src = element['src'].strip
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
end
|
23
|
+
next if src.start_with? 'http'
|
24
|
+
base = if src.start_with? '/'
|
25
|
+
image_base_url
|
26
|
+
else
|
27
|
+
image_subpage_url
|
28
|
+
end
|
29
|
+
element['src'] = URI.join(base, src).to_s
|
32
30
|
end
|
33
31
|
doc
|
34
32
|
end
|
35
|
-
|
33
|
+
|
36
34
|
# Private: the base url you want to use
|
37
35
|
def image_base_url
|
38
|
-
context[:image_base_url]
|
36
|
+
context[:image_base_url] || raise("Missing context :image_base_url for #{self.class.name}")
|
39
37
|
end
|
40
38
|
|
41
39
|
# Private: the relative url you want to use
|
42
40
|
def image_subpage_url
|
43
|
-
context[:image_subpage_url]
|
41
|
+
context[:image_subpage_url] || raise("Missing context :image_subpage_url for #{self.class.name}")
|
44
42
|
end
|
45
|
-
|
46
43
|
end
|
47
44
|
end
|
48
|
-
end
|
45
|
+
end
|
@@ -24,7 +24,7 @@ module HTML
|
|
24
24
|
def call
|
25
25
|
return doc unless asset_proxy_enabled?
|
26
26
|
|
27
|
-
doc.search(
|
27
|
+
doc.search('img').each do |element|
|
28
28
|
original_src = element['src']
|
29
29
|
next unless original_src
|
30
30
|
|
@@ -86,7 +86,7 @@ module HTML
|
|
86
86
|
# Private: helper to hexencode a string. Each byte ends up encoded into
|
87
87
|
# two characters, zero padded value in the range [0-9a-f].
|
88
88
|
def hexencode(str)
|
89
|
-
str.to_enum(:each_byte).map { |byte|
|
89
|
+
str.to_enum(:each_byte).map { |byte| format('%02x', byte) }.join
|
90
90
|
end
|
91
91
|
end
|
92
92
|
end
|
@@ -1,5 +1,5 @@
|
|
1
|
-
HTML::Pipeline.require_dependency(
|
2
|
-
HTML::Pipeline.require_dependency(
|
1
|
+
HTML::Pipeline.require_dependency('escape_utils', 'EmailReplyFilter')
|
2
|
+
HTML::Pipeline.require_dependency('email_reply_parser', 'EmailReplyFilter')
|
3
3
|
|
4
4
|
module HTML
|
5
5
|
class Pipeline
|
@@ -17,9 +17,9 @@ module HTML
|
|
17
17
|
EMAIL_QUOTED_HEADER = %(<div class="email-quoted-reply">).freeze
|
18
18
|
EMAIL_SIGNATURE_HEADER = %(<div class="email-signature-reply">).freeze
|
19
19
|
EMAIL_FRAGMENT_HEADER = %(<div class="email-fragment">).freeze
|
20
|
-
EMAIL_HEADER_END =
|
20
|
+
EMAIL_HEADER_END = '</div>'.freeze
|
21
21
|
EMAIL_REGEX = /[^@\s.][^@\s]*@\[?[a-z0-9.-]+\]?/
|
22
|
-
HIDDEN_EMAIL_PATTERN =
|
22
|
+
HIDDEN_EMAIL_PATTERN = '***@***.***'.freeze
|
23
23
|
|
24
24
|
# Scans an email body to determine which bits are quoted and which should
|
25
25
|
# be hidden. EmailReplyParser is used to split the comment into an Array
|
@@ -1,5 +1,5 @@
|
|
1
|
-
require
|
2
|
-
HTML::Pipeline.require_dependency(
|
1
|
+
require 'cgi'
|
2
|
+
HTML::Pipeline.require_dependency('gemoji', 'EmojiFilter')
|
3
3
|
|
4
4
|
module HTML
|
5
5
|
class Pipeline
|
@@ -11,12 +11,11 @@ module HTML
|
|
11
11
|
# :ignored_ancestor_tags (optional) - Tags to stop the emojification. Node has matched ancestor HTML tags will not be emojified. Default to pre, code, and tt tags. Extra tags please pass in the form of array, e.g., %w(blockquote summary).
|
12
12
|
# :img_attrs (optional) - Attributes for generated img tag. E.g. Pass { "draggble" => true, "height" => nil } to set draggable attribute to "true" and clear height attribute of generated img tag.
|
13
13
|
class EmojiFilter < Filter
|
14
|
-
|
15
|
-
DEFAULT_IGNORED_ANCESTOR_TAGS = %w(pre code tt).freeze
|
14
|
+
DEFAULT_IGNORED_ANCESTOR_TAGS = %w[pre code tt].freeze
|
16
15
|
|
17
16
|
def call
|
18
17
|
doc.search('.//text()').each do |node|
|
19
|
-
content = node.
|
18
|
+
content = node.text
|
20
19
|
next unless content.include?(':')
|
21
20
|
next if has_ancestor?(node, ignored_ancestor_tags)
|
22
21
|
html = emoji_image_filter(content)
|
@@ -38,8 +37,8 @@ module HTML
|
|
38
37
|
#
|
39
38
|
# Returns a String with :emoji: replaced with images.
|
40
39
|
def emoji_image_filter(text)
|
41
|
-
text.gsub(emoji_pattern) do |
|
42
|
-
emoji_image_tag(
|
40
|
+
text.gsub(emoji_pattern) do |_match|
|
41
|
+
emoji_image_tag(Regexp.last_match(1))
|
43
42
|
end
|
44
43
|
end
|
45
44
|
|
@@ -57,9 +56,9 @@ module HTML
|
|
57
56
|
# Returns the context's asset_path or the default path if no context asset_path is given.
|
58
57
|
def asset_path(name)
|
59
58
|
if context[:asset_path]
|
60
|
-
context[:asset_path].gsub(
|
59
|
+
context[:asset_path].gsub(':file_name', emoji_filename(name))
|
61
60
|
else
|
62
|
-
File.join(
|
61
|
+
File.join('emoji', emoji_filename(name))
|
63
62
|
end
|
64
63
|
end
|
65
64
|
|
@@ -67,12 +66,12 @@ module HTML
|
|
67
66
|
|
68
67
|
# Build an emoji image tag
|
69
68
|
def emoji_image_tag(name)
|
70
|
-
require
|
69
|
+
require 'active_support/core_ext/hash/indifferent_access'
|
71
70
|
html_attrs =
|
72
|
-
default_img_attrs(name)
|
73
|
-
|
74
|
-
|
75
|
-
|
71
|
+
default_img_attrs(name)
|
72
|
+
.merge!((context[:img_attrs] || {}).with_indifferent_access)
|
73
|
+
.map { |attr, value| !value.nil? && %(#{attr}="#{value.respond_to?(:call) && value.call(name) || value}") }
|
74
|
+
.reject(&:blank?).join(' '.freeze)
|
76
75
|
|
77
76
|
"<img #{html_attrs}>"
|
78
77
|
end
|
@@ -80,13 +79,13 @@ module HTML
|
|
80
79
|
# Default attributes for img tag
|
81
80
|
def default_img_attrs(name)
|
82
81
|
{
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
82
|
+
'class' => 'emoji'.freeze,
|
83
|
+
'title' => ":#{name}:",
|
84
|
+
'alt' => ":#{name}:",
|
85
|
+
'src' => emoji_url(name).to_s,
|
86
|
+
'height' => '20'.freeze,
|
87
|
+
'width' => '20'.freeze,
|
88
|
+
'align' => 'absmiddle'.freeze
|
90
89
|
}
|
91
90
|
end
|
92
91
|
|
data/lib/html/pipeline/filter.rb
CHANGED
@@ -30,7 +30,7 @@ module HTML
|
|
30
30
|
class InvalidDocumentException < StandardError; end
|
31
31
|
|
32
32
|
def initialize(doc, context = nil, result = nil)
|
33
|
-
if doc.
|
33
|
+
if doc.is_a?(String)
|
34
34
|
@html = doc.to_str
|
35
35
|
@doc = nil
|
36
36
|
else
|
@@ -76,8 +76,7 @@ module HTML
|
|
76
76
|
end
|
77
77
|
|
78
78
|
# Make sure the context has everything we need. Noop: Subclasses can override.
|
79
|
-
def validate
|
80
|
-
end
|
79
|
+
def validate; end
|
81
80
|
|
82
81
|
# The Repository object provided in the context hash, or nil when no
|
83
82
|
# :repository was specified.
|
@@ -116,9 +115,7 @@ module HTML
|
|
116
115
|
# Returns true when the node has a matching ancestor.
|
117
116
|
def has_ancestor?(node, tags)
|
118
117
|
while node = node.parent
|
119
|
-
if tags.include?(node.name.downcase)
|
120
|
-
break true
|
121
|
-
end
|
118
|
+
break true if tags.include?(node.name.downcase)
|
122
119
|
end
|
123
120
|
end
|
124
121
|
|
@@ -134,7 +131,7 @@ module HTML
|
|
134
131
|
# the last filter returns a String.
|
135
132
|
def self.to_document(input, context = nil)
|
136
133
|
html = call(input, context)
|
137
|
-
HTML::Pipeline
|
134
|
+
HTML::Pipeline.parse(html)
|
138
135
|
end
|
139
136
|
|
140
137
|
# Like call but guarantees that a string of HTML markup is returned.
|
@@ -158,7 +155,7 @@ module HTML
|
|
158
155
|
|
159
156
|
if missing.any?
|
160
157
|
raise ArgumentError,
|
161
|
-
|
158
|
+
"Missing context keys for #{self.class.name}: #{missing.map(&:inspect).join ', '}"
|
162
159
|
end
|
163
160
|
end
|
164
161
|
end
|
@@ -7,8 +7,8 @@ module HTML
|
|
7
7
|
# :http_url - The HTTP url to force HTTPS. Falls back to :base_url
|
8
8
|
class HttpsFilter < Filter
|
9
9
|
def call
|
10
|
-
doc.css(%
|
11
|
-
element['href'] = element['href'].sub(/^http:/,'https:')
|
10
|
+
doc.css(%(a[href^="#{http_url}"])).each do |element|
|
11
|
+
element['href'] = element['href'].sub(/^http:/, 'https:')
|
12
12
|
end
|
13
13
|
doc
|
14
14
|
end
|
@@ -17,21 +17,19 @@ module HTML
|
|
17
17
|
# js injection via javascript: urls.
|
18
18
|
next if element['src'].to_s.strip =~ /\Ajavascript/i
|
19
19
|
|
20
|
-
element['style'] =
|
20
|
+
element['style'] = 'max-width:100%;'
|
21
21
|
|
22
|
-
|
23
|
-
link_image element
|
24
|
-
end
|
22
|
+
link_image element unless has_ancestor?(element, %w[a])
|
25
23
|
end
|
26
24
|
|
27
25
|
doc
|
28
26
|
end
|
29
27
|
|
30
28
|
def link_image(element)
|
31
|
-
link = doc.document.create_element('a', :
|
29
|
+
link = doc.document.create_element('a', href: element['src'], target: '_blank')
|
32
30
|
link.add_child(element.dup)
|
33
31
|
element.replace(link)
|
34
32
|
end
|
35
33
|
end
|
36
34
|
end
|
37
|
-
end
|
35
|
+
end
|
@@ -1,4 +1,4 @@
|
|
1
|
-
HTML::Pipeline.require_dependency(
|
1
|
+
HTML::Pipeline.require_dependency('commonmarker', 'MarkdownFilter')
|
2
2
|
|
3
3
|
module HTML
|
4
4
|
class Pipeline
|
@@ -15,7 +15,7 @@ module HTML
|
|
15
15
|
class MarkdownFilter < TextFilter
|
16
16
|
def initialize(text, context = nil, result = nil)
|
17
17
|
super text, context, result
|
18
|
-
@text = @text.
|
18
|
+
@text = @text.delete "\r"
|
19
19
|
end
|
20
20
|
|
21
21
|
# Convert Markdown to HTML using the best available implementation
|
@@ -25,7 +25,8 @@ module HTML
|
|
25
25
|
options << :HARDBREAKS if context[:gfm] != false
|
26
26
|
extensions = context.fetch(
|
27
27
|
:commonmarker_extensions,
|
28
|
-
[
|
28
|
+
%i[table strikethrough tagfilter autolink]
|
29
|
+
)
|
29
30
|
html = CommonMarker.render_html(@text, options, extensions)
|
30
31
|
html.rstrip!
|
31
32
|
html
|
@@ -1,4 +1,4 @@
|
|
1
|
-
HTML::Pipeline.require_dependency(
|
1
|
+
HTML::Pipeline.require_dependency('sanitize', 'SanitizationFilter')
|
2
2
|
|
3
3
|
module HTML
|
4
4
|
class Pipeline
|
@@ -21,16 +21,16 @@ module HTML
|
|
21
21
|
#
|
22
22
|
# This filter does not write additional information to the context.
|
23
23
|
class SanitizationFilter < Filter
|
24
|
-
LISTS = Set.new(%w
|
24
|
+
LISTS = Set.new(%w[ul ol].freeze)
|
25
25
|
LIST_ITEM = 'li'.freeze
|
26
26
|
|
27
27
|
# List of table child elements. These must be contained by a <table> element
|
28
28
|
# or they are not allowed through. Otherwise they can be used to break out
|
29
29
|
# of places we're using tables to contain formatted user content (like pull
|
30
30
|
# request review comments).
|
31
|
-
TABLE_ITEMS = Set.new(%w
|
31
|
+
TABLE_ITEMS = Set.new(%w[tr td th].freeze)
|
32
32
|
TABLE = 'table'.freeze
|
33
|
-
TABLE_SECTIONS = Set.new(%w
|
33
|
+
TABLE_SECTIONS = Set.new(%w[thead tbody tfoot].freeze)
|
34
34
|
|
35
35
|
# These schemes are the only ones allowed in <a href> attributes by default.
|
36
36
|
ANCHOR_SCHEMES = ['http', 'https', 'mailto', :relative, 'github-windows', 'github-mac'].freeze
|
@@ -38,77 +38,80 @@ module HTML
|
|
38
38
|
# The main sanitization whitelist. Only these elements and attributes are
|
39
39
|
# allowed through by default.
|
40
40
|
WHITELIST = {
|
41
|
-
:
|
41
|
+
elements: %w[
|
42
42
|
h1 h2 h3 h4 h5 h6 h7 h8 br b i strong em a pre code img tt
|
43
43
|
div ins del sup sub p ol ul table thead tbody tfoot blockquote
|
44
44
|
dl dt dd kbd q samp var hr ruby rt rp li tr td th s strike summary details
|
45
|
-
|
46
|
-
:
|
47
|
-
:
|
45
|
+
],
|
46
|
+
remove_contents: ['script'],
|
47
|
+
attributes: {
|
48
48
|
'a' => ['href'],
|
49
|
-
'img' => [
|
50
|
-
'div' => [
|
49
|
+
'img' => %w[src longdesc],
|
50
|
+
'div' => %w[itemscope itemtype],
|
51
51
|
'blockquote' => ['cite'],
|
52
52
|
'del' => ['cite'],
|
53
53
|
'ins' => ['cite'],
|
54
54
|
'q' => ['cite'],
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
55
|
+
:all => ['abbr', 'accept', 'accept-charset',
|
56
|
+
'accesskey', 'action', 'align', 'alt', 'axis',
|
57
|
+
'border', 'cellpadding', 'cellspacing', 'char',
|
58
|
+
'charoff', 'charset', 'checked',
|
59
|
+
'clear', 'cols', 'colspan', 'color',
|
60
|
+
'compact', 'coords', 'datetime', 'dir',
|
61
|
+
'disabled', 'enctype', 'for', 'frame',
|
62
|
+
'headers', 'height', 'hreflang',
|
63
|
+
'hspace', 'ismap', 'label', 'lang',
|
64
|
+
'maxlength', 'media', 'method',
|
65
|
+
'multiple', 'name', 'nohref', 'noshade',
|
66
|
+
'nowrap', 'open', 'prompt', 'readonly', 'rel', 'rev',
|
67
|
+
'rows', 'rowspan', 'rules', 'scope',
|
68
|
+
'selected', 'shape', 'size', 'span',
|
69
|
+
'start', 'summary', 'tabindex', 'target',
|
70
|
+
'title', 'type', 'usemap', 'valign', 'value',
|
71
|
+
'vspace', 'width', 'itemprop']
|
72
72
|
},
|
73
|
-
:
|
74
|
-
'a' => {'href' => ANCHOR_SCHEMES},
|
75
|
-
'blockquote' => {'cite' => ['http', 'https', :relative]},
|
76
|
-
'del' => {'cite' => ['http', 'https', :relative]},
|
77
|
-
'ins' => {'cite' => ['http', 'https', :relative]},
|
78
|
-
'q' => {'cite' => ['http', 'https', :relative]},
|
73
|
+
protocols: {
|
74
|
+
'a' => { 'href' => ANCHOR_SCHEMES },
|
75
|
+
'blockquote' => { 'cite' => ['http', 'https', :relative] },
|
76
|
+
'del' => { 'cite' => ['http', 'https', :relative] },
|
77
|
+
'ins' => { 'cite' => ['http', 'https', :relative] },
|
78
|
+
'q' => { 'cite' => ['http', 'https', :relative] },
|
79
79
|
'img' => {
|
80
80
|
'src' => ['http', 'https', :relative],
|
81
81
|
'longdesc' => ['http', 'https', :relative]
|
82
82
|
}
|
83
83
|
},
|
84
|
-
:
|
84
|
+
transformers: [
|
85
85
|
# Top-level <li> elements are removed because they can break out of
|
86
86
|
# containing markup.
|
87
87
|
lambda { |env|
|
88
|
-
name
|
89
|
-
|
88
|
+
name = env[:node_name]
|
89
|
+
node = env[:node]
|
90
|
+
if name == LIST_ITEM && node.ancestors.none? { |n| LISTS.include?(n.name) }
|
90
91
|
node.replace(node.children)
|
91
92
|
end
|
92
93
|
},
|
93
94
|
|
94
95
|
# Table child elements that are not contained by a <table> are removed.
|
95
96
|
lambda { |env|
|
96
|
-
name
|
97
|
-
|
97
|
+
name = env[:node_name]
|
98
|
+
node = env[:node]
|
99
|
+
if (TABLE_SECTIONS.include?(name) || TABLE_ITEMS.include?(name)) && node.ancestors.none? { |n| n.name == TABLE }
|
98
100
|
node.replace(node.children)
|
99
101
|
end
|
100
102
|
}
|
101
103
|
]
|
102
|
-
}
|
104
|
+
}.freeze
|
103
105
|
|
104
106
|
# A more limited sanitization whitelist. This includes all attributes,
|
105
107
|
# protocols, and transformers from WHITELIST but with a more locked down
|
106
108
|
# set of allowed elements.
|
107
109
|
LIMITED = WHITELIST.merge(
|
108
|
-
:
|
110
|
+
elements: %w[b i strong em a pre code img ins del sup sub p ol ul li]
|
111
|
+
)
|
109
112
|
|
110
113
|
# Strip all HTML tags from the document.
|
111
|
-
FULL = { :
|
114
|
+
FULL = { elements: [] }.freeze
|
112
115
|
|
113
116
|
# Sanitize markup using the Sanitize library.
|
114
117
|
def call
|
@@ -1,10 +1,15 @@
|
|
1
|
-
HTML::Pipeline.require_dependency(
|
1
|
+
HTML::Pipeline.require_dependency('rouge', 'SyntaxHighlightFilter')
|
2
2
|
|
3
3
|
module HTML
|
4
4
|
class Pipeline
|
5
5
|
# HTML Filter that syntax highlights code blocks wrapped
|
6
6
|
# in <pre lang="...">.
|
7
7
|
class SyntaxHighlightFilter < Filter
|
8
|
+
def initialize(*args)
|
9
|
+
super(*args)
|
10
|
+
@formatter = Rouge::Formatters::HTML.new
|
11
|
+
end
|
12
|
+
|
8
13
|
def call
|
9
14
|
doc.search('pre').each do |node|
|
10
15
|
default = context[:highlight] && context[:highlight].to_s
|
@@ -12,27 +17,26 @@ module HTML
|
|
12
17
|
next unless lexer = lexer_for(lang)
|
13
18
|
text = node.inner_text
|
14
19
|
|
15
|
-
html = highlight_with_timeout_handling(
|
20
|
+
html = highlight_with_timeout_handling(text, lang)
|
16
21
|
next if html.nil?
|
17
22
|
|
18
|
-
|
19
|
-
|
20
|
-
|
23
|
+
next unless (node = node.replace(html).first)
|
24
|
+
klass = node['class']
|
25
|
+
klass = [klass, "highlight-#{lang}"].compact.join ' '
|
21
26
|
|
22
|
-
|
23
|
-
end
|
27
|
+
node['class'] = klass
|
24
28
|
end
|
25
29
|
doc
|
26
30
|
end
|
27
31
|
|
28
|
-
def highlight_with_timeout_handling(
|
29
|
-
|
30
|
-
rescue Timeout::Error =>
|
32
|
+
def highlight_with_timeout_handling(text, lang)
|
33
|
+
Rouge.highlight(text, lang, @formatter)
|
34
|
+
rescue Timeout::Error => _
|
31
35
|
nil
|
32
36
|
end
|
33
37
|
|
34
38
|
def lexer_for(lang)
|
35
|
-
(
|
39
|
+
Rouge::Lexer.find(lang)
|
36
40
|
end
|
37
41
|
end
|
38
42
|
end
|
@@ -4,11 +4,11 @@ module HTML
|
|
4
4
|
attr_reader :text
|
5
5
|
|
6
6
|
def initialize(text, context = nil, result = nil)
|
7
|
-
raise TypeError,
|
7
|
+
raise TypeError, 'text cannot be HTML' if text.is_a?(DocumentFragment)
|
8
8
|
# Ensure that this is always a string
|
9
9
|
@text = text.respond_to?(:to_str) ? text.to_str : text.to_s
|
10
10
|
super nil, context, result
|
11
11
|
end
|
12
12
|
end
|
13
13
|
end
|
14
|
-
end
|
14
|
+
end
|
@@ -23,35 +23,35 @@ module HTML
|
|
23
23
|
# result[:output].to_s
|
24
24
|
# # => "<h1>\n<a id=\"ice-cube\" class=\"anchor\" href=\"#ice-cube\">..."
|
25
25
|
class TableOfContentsFilter < Filter
|
26
|
-
PUNCTUATION_REGEXP = RUBY_VERSION >
|
26
|
+
PUNCTUATION_REGEXP = RUBY_VERSION > '1.9' ? /[^\p{Word}\- ]/u : /[^\w\- ]/
|
27
27
|
|
28
28
|
# The icon that will be placed next to an anchored rendered markdown header
|
29
29
|
def anchor_icon
|
30
|
-
context[:anchor_icon] ||
|
30
|
+
context[:anchor_icon] || '<span aria-hidden="true" class="octicon octicon-link"></span>'
|
31
31
|
end
|
32
32
|
|
33
33
|
def call
|
34
|
-
result[:toc] =
|
34
|
+
result[:toc] = ''
|
35
35
|
|
36
36
|
headers = Hash.new(0)
|
37
37
|
doc.css('h1, h2, h3, h4, h5, h6').each do |node|
|
38
38
|
text = node.text
|
39
39
|
id = ascii_downcase(text)
|
40
40
|
id.gsub!(PUNCTUATION_REGEXP, '') # remove punctuation
|
41
|
-
id.
|
41
|
+
id.tr!(' ', '-') # replace spaces with dash
|
42
42
|
|
43
|
-
uniq =
|
43
|
+
uniq = headers[id] > 0 ? "-#{headers[id]}" : ''
|
44
44
|
headers[id] += 1
|
45
45
|
if header_content = node.children.first
|
46
|
-
result[:toc] << %
|
47
|
-
header_content.add_previous_sibling(%
|
46
|
+
result[:toc] << %(<li><a href="##{id}#{uniq}">#{text}</a></li>\n)
|
47
|
+
header_content.add_previous_sibling(%(<a id="#{id}#{uniq}" class="anchor" href="##{id}#{uniq}" aria-hidden="true">#{anchor_icon}</a>))
|
48
48
|
end
|
49
49
|
end
|
50
|
-
result[:toc] = %
|
50
|
+
result[:toc] = %(<ul class="section-nav">\n#{result[:toc]}</ul>) unless result[:toc].empty?
|
51
51
|
doc
|
52
52
|
end
|
53
53
|
|
54
|
-
if RUBY_VERSION >=
|
54
|
+
if RUBY_VERSION >= '2.4'
|
55
55
|
def ascii_downcase(str)
|
56
56
|
str.downcase(:ascii)
|
57
57
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: html-pipeline
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.7.
|
4
|
+
version: 2.7.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Ryan Tomayko
|
@@ -9,36 +9,36 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date:
|
12
|
+
date: 2018-04-25 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
|
-
name:
|
15
|
+
name: activesupport
|
16
16
|
requirement: !ruby/object:Gem::Requirement
|
17
17
|
requirements:
|
18
18
|
- - ">="
|
19
19
|
- !ruby/object:Gem::Version
|
20
|
-
version: '
|
20
|
+
version: '2'
|
21
21
|
type: :runtime
|
22
22
|
prerelease: false
|
23
23
|
version_requirements: !ruby/object:Gem::Requirement
|
24
24
|
requirements:
|
25
25
|
- - ">="
|
26
26
|
- !ruby/object:Gem::Version
|
27
|
-
version: '
|
27
|
+
version: '2'
|
28
28
|
- !ruby/object:Gem::Dependency
|
29
|
-
name:
|
29
|
+
name: nokogiri
|
30
30
|
requirement: !ruby/object:Gem::Requirement
|
31
31
|
requirements:
|
32
32
|
- - ">="
|
33
33
|
- !ruby/object:Gem::Version
|
34
|
-
version: '
|
34
|
+
version: '1.4'
|
35
35
|
type: :runtime
|
36
36
|
prerelease: false
|
37
37
|
version_requirements: !ruby/object:Gem::Requirement
|
38
38
|
requirements:
|
39
39
|
- - ">="
|
40
40
|
- !ruby/object:Gem::Version
|
41
|
-
version: '
|
41
|
+
version: '1.4'
|
42
42
|
description: GitHub HTML processing filters and utilities
|
43
43
|
email:
|
44
44
|
- ryan@github.com
|