Skip to content

Commit 4c06438

Browse files
Jerry Cheungrtomayko
authored andcommitted
initial extraction of github-html from .com
1 parent ff7e570 commit 4c06438

File tree

13 files changed

+236
-199
lines changed

13 files changed

+236
-199
lines changed

.gitignore

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
*.gem
2+
*.rbc
3+
.bundle
4+
.config
5+
.yardoc
6+
Gemfile.lock
7+
InstalledFiles
8+
_yardoc
9+
coverage
10+
doc/
11+
lib/bundler/man
12+
pkg
13+
rdoc
14+
spec/reports
15+
test/tmp
16+
test/version_tmp
17+
tmp

Gemfile

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
source 'https://rubygems.org'
2+
3+
# Specify your gem's dependencies in github-html.gemspec
4+
gemspec
5+
6+
group :development, :test do
7+
gem 'rake'
8+
gem 'emoji', :git => 'git@github.com:github/emoji.git'
9+
end

LICENSE

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
Copyright (c) 2012 Jerry Cheung
2+
3+
MIT License
4+
5+
Permission is hereby granted, free of charge, to any person obtaining
6+
a copy of this software and associated documentation files (the
7+
"Software"), to deal in the Software without restriction, including
8+
without limitation the rights to use, copy, modify, merge, publish,
9+
distribute, sublicense, and/or sell copies of the Software, and to
10+
permit persons to whom the Software is furnished to do so, subject to
11+
the following conditions:
12+
13+
The above copyright notice and this permission notice shall be
14+
included in all copies or substantial portions of the Software.
15+
16+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

README.md

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
# GitHub::HTML
2+
3+
GitHub HTML processing filters and utilities. This module includes a small
4+
framework for defining DOM based content filters and applying them to user
5+
provided content.
6+
7+
## Installation
8+
9+
Add this line to your application's Gemfile:
10+
11+
gem 'github-html'
12+
13+
And then execute:
14+
15+
$ bundle
16+
17+
Or install it yourself as:
18+
19+
$ gem install github-html
20+
21+
## Usage
22+
23+
TODO: Write usage instructions here
24+
25+
## Contributing
26+
27+
1. Fork it
28+
2. Create your feature branch (`git checkout -b my-new-feature`)
29+
3. Commit your changes (`git commit -am 'Added some feature'`)
30+
4. Push to the branch (`git push origin my-new-feature`)
31+
5. Create new Pull Request
32+
33+
34+
## TODO
35+
36+
* emoji gem is private, can't add to gemspec. specify manually for now
37+
* syntax highlighter filter requires other github specific things
38+
* autolink_filter depends on Github.enterprise?
39+
* test whether nokogiri monkey patch is still necessary

Rakefile

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
#!/usr/bin/env rake
2+
require "bundler/gem_tasks"
3+
require 'rake/testtask'
4+
5+
Rake::TestTask.new do |t|
6+
t.libs << "test"
7+
t.test_files = FileList['test/*_test.rb']
8+
t.verbose = true
9+
end
10+
11+
task :default => :test

github-html.gemspec

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
# -*- encoding: utf-8 -*-
2+
require File.expand_path('../lib/github/html/version', __FILE__)
3+
4+
Gem::Specification.new do |gem|
5+
gem.authors = ["Jerry Cheung"]
6+
gem.email = ["jch@whatcodecraves.com"]
7+
gem.description = %q{TODO: Write a gem description}
8+
gem.summary = %q{TODO: Write a gem summary}
9+
gem.homepage = ""
10+
11+
gem.files = `git ls-files`.split($\)
12+
gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
13+
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
14+
gem.name = "github-html"
15+
gem.require_paths = ["lib"]
16+
gem.version = GitHub::HTML::VERSION
17+
18+
gem.add_dependency 'nokogiri', '~> 1.4'
19+
gem.add_dependency 'github-markdown', '~> 0.5'
20+
gem.add_dependency 'sanitize', '~> 2.0'
21+
gem.add_dependency 'github-linguist', '~> 2.1'
22+
gem.add_dependency 'rinku', '~> 1.7'
23+
gem.add_dependency 'escape_utils', '~> 0.2'
24+
gem.add_dependency 'activesupport'
25+
end

lib/github/html.rb

Lines changed: 24 additions & 198 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
require 'nokogiri'
1+
require "nokogiri"
22

33
module GitHub
44
# GitHub HTML processing filters and utilities. This module includes a small
@@ -7,32 +7,31 @@ module GitHub
77
#
88
# See GitHub::HTML::Filter for information on building filters.
99
module HTML
10-
# Our DOM implementation.
11-
DocumentFragment = Nokogiri::HTML::DocumentFragment
12-
13-
# A Struct for results passed back from the Pipelines
14-
# This allows us to have some explicit-ness around the types of things that
15-
# pipelines add to the repsonse.
16-
#
17-
# Members of the Result:
18-
# output - the DocumentFragment or String result of the Pipeline
19-
# mentioned_users - see GitHub::HTML::MentionFilter
20-
# mentioned_teams - see GitHub::HTML::TeamMentionFilter
21-
# commits - see GitHub::HTML::CommitMentionFilter
22-
# commits_count - see GitHub::HTML::CommitMentionFilter
23-
# issues - see GitHub::HTML::IssueMentionFilter
24-
class Result < Struct.new(:output,
25-
:mentioned_users,
26-
:mentioned_teams,
27-
:commits, :commits_count,
28-
:issues
29-
)
10+
extend self
3011

31-
def to_s
32-
output.to_s
33-
end
12+
autoload :Version, 'github/html/version'
13+
autoload :Pipeline, 'github/html/pipeline'
14+
autoload :Filter, 'github/html/filter'
15+
autoload :BodyContent, 'github/html/body_content'
16+
autoload :AutolinkFilter, 'github/html/autolink_filter'
17+
autoload :CamoFilter, 'github/html/camo_filter'
18+
autoload :CommitMentionFilter, 'github/html/commit_mention_filter'
19+
autoload :EmailReplyFilter, 'github/html/email_reply_filter'
20+
autoload :EmojiFilter, 'github/html/emoji_filter'
21+
autoload :HttpsFilter, 'github/html/https_filter'
22+
autoload :ImageMaxWidthFilter, 'github/html/image_max_width_filter'
23+
autoload :IssueMentionFilter, 'github/html/issue_mention_filter'
24+
autoload :MarkdownFilter, 'github/html/markdown_filter'
25+
autoload :MentionFilter, 'github/html/@mention_filter'
26+
autoload :TeamMentionFilter, 'github/html/team_mention_filter'
27+
autoload :PlainTextInputFilter, 'github/html/plain_text_input_filter'
28+
autoload :SanitizationFilter, 'github/html/sanitization_filter'
29+
autoload :SyntaxHighlightFilter, 'github/html/syntax_highlight_filter'
30+
autoload :TextileFilter, 'github/html/textile_filter'
31+
autoload :TableOfContentsFilter, 'github/html/toc_filter'
3432

35-
end
33+
# Our DOM implementation.
34+
DocumentFragment = Nokogiri::HTML::DocumentFragment
3635

3736
# Parse a String into a DocumentFragment object. When a DocumentFragment is
3837
# provided, return it verbatim.
@@ -44,179 +43,6 @@ def self.parse(document_or_html)
4443
document_or_html
4544
end
4645
end
47-
48-
require 'github/html/body_content'
49-
50-
# Filter implementations
51-
require 'github/html/filter'
52-
require 'github/html/autolink_filter'
53-
require 'github/html/camo_filter'
54-
require 'github/html/commit_mention_filter'
55-
require 'github/html/email_reply_filter'
56-
require 'github/html/emoji_filter'
57-
require 'github/html/https_filter'
58-
require 'github/html/image_max_width_filter'
59-
require 'github/html/issue_mention_filter'
60-
require 'github/html/markdown_filter'
61-
require 'github/html/@mention_filter'
62-
require 'github/html/team_mention_filter'
63-
require 'github/html/plain_text_input_filter'
64-
require 'github/html/sanitization_filter'
65-
require 'github/html/syntax_highlight_filter'
66-
require 'github/html/textile_filter'
67-
require 'github/html/toc_filter'
68-
69-
# Contruct a pipeline for running multiple HTML filters.
70-
#
71-
# filters - Array of Filter objects. Each must respond to call(doc,
72-
# context) and return the modified DocumentFragment or a
73-
# String containing HTML markup. Filters are performed in the
74-
# order provided.
75-
# context - The default context hash. Values specified here MUST NOT be
76-
# overridden by individual pipeline runs.
77-
# result_class - The default Class of the result object for individual
78-
# calls. Default: Hash. Protip: Pass in a Struct to get
79-
# some semblence of type safety.
80-
class Pipeline
81-
# Public: Returns an Array of Filter objects for this Pipeline.
82-
attr_reader :filters
83-
84-
def initialize(filters, context = nil, result_class = nil)
85-
@filters = filters.flatten.freeze
86-
@context = context || {}
87-
@result_class = result_class || Hash
88-
end
89-
90-
# Apply all filters in the pipeline to the given HTML.
91-
#
92-
# html - A String containing HTML or a DocumentFragment object.
93-
# context - The context hash passed to each filter. See the Filter docs
94-
# for more info on possible values. This object MUST NOT be modified
95-
# in place by filters. Use the Result for passing state back.
96-
# result - The result Hash passed to each filter for modification. This
97-
# is where Filters store extracted information from the content.
98-
#
99-
# Returns the result Hash after being filtered by this Pipeline. Contains an
100-
# :output key with the DocumentFragment or String HTML markup based on the
101-
# output of the last filter in the pipeline.
102-
def call(html, context = nil, result = nil)
103-
if context
104-
@context.each { |k, v| context[k] = v if !context.key?(k) }
105-
else
106-
context = @context.dup
107-
end
108-
context.freeze
109-
result ||= @result_class.new
110-
result[:output] = @filters.inject(html) { |doc, filter| filter.call(doc, context, result) }
111-
result
112-
end
113-
114-
# Like call but guarantee the value returned is a DocumentFragment.
115-
# Pipelines may return a DocumentFragment or a String. Callers that need a
116-
# DocumentFragment should use this method.
117-
def to_document(input, context = nil, result = nil)
118-
result = call(input, context, result)
119-
GitHub::HTML.parse(result[:output])
120-
end
121-
122-
# Like call but guarantee the value returned is a string of HTML markup.
123-
def to_html(input, context = nil, result = nil)
124-
result = call(input, context, result = nil)
125-
output = result[:output]
126-
if output.respond_to?(:to_html)
127-
output.to_html
128-
else
129-
output.to_s
130-
end
131-
end
132-
end
133-
134-
# Pipeline providing sanitization and image hijacking but no mention
135-
# related features.
136-
SimplePipeline = Pipeline.new [
137-
SanitizationFilter,
138-
TableOfContentsFilter, # add 'name' anchors to all headers
139-
CamoFilter,
140-
ImageMaxWidthFilter,
141-
SyntaxHighlightFilter,
142-
EmojiFilter,
143-
AutolinkFilter # Perform an autolinking pass, for those GitHub::Markup
144-
# languages that don't have built-in autolinking
145-
], nil, Result
146-
147-
# Pipeline used for most types of user provided content like comments
148-
# and issue bodies. Performs sanitization, image hijacking, and various
149-
# mention links.
150-
MarkdownPipeline = Pipeline.new [
151-
MarkdownFilter,
152-
SanitizationFilter,
153-
CamoFilter,
154-
ImageMaxWidthFilter,
155-
HttpsFilter,
156-
MentionFilter,
157-
TeamMentionFilter,
158-
IssueMentionFilter,
159-
CommitMentionFilter,
160-
EmojiFilter,
161-
SyntaxHighlightFilter
162-
], {:gfm => true}, Result
163-
164-
# Same as MarkdownPipeline, but without GFM. Used for Pull Requests
165-
# created from Commits.
166-
NonGFMMarkdownPipeline = Pipeline.new(MarkdownPipeline.filters,
167-
{:gfm => false}, Result)
168-
169-
# Pipeline used to Render the Markdown content in pages2
170-
# autogenerated websites
171-
PagesPipeline = Pipeline.new [
172-
MarkdownFilter,
173-
SanitizationFilter,
174-
MentionFilter,
175-
TeamMentionFilter,
176-
EmojiFilter,
177-
SyntaxHighlightFilter
178-
], {:base_url => GitHub.url, :gfm => false}, Result
179-
180-
# Pipeline used for commit messages. This one is kind of weird because
181-
# commit messages are treated as preformatted plain text.
182-
CommitMessagePipeline = Pipeline.new [
183-
PlainTextInputFilter,
184-
MentionFilter,
185-
TeamMentionFilter,
186-
CommitMentionFilter,
187-
IssueMentionFilter,
188-
EmojiFilter,
189-
AutolinkFilter
190-
], nil, Result
191-
192-
# Pipeline used for very large commit messages that take too long to
193-
# generate with a fully featured pipeline.
194-
LongCommitMessagePipeline = Pipeline.new [PlainTextInputFilter]
195-
196-
# Pipeline used for email replies.
197-
EmailPipeline = Pipeline.new [
198-
EmailReplyFilter,
199-
MentionFilter,
200-
TeamMentionFilter,
201-
IssueMentionFilter,
202-
CommitMentionFilter,
203-
EmojiFilter,
204-
AutolinkFilter
205-
], nil, Result
206-
207-
# Used to post-process user content for HTML email clients.
208-
HtmlEmailPipeline = Pipeline.new [
209-
ImageMaxWidthFilter
210-
], nil, Result
211-
212-
# Pipeline used for really old comments and maybe other textile content
213-
# I guess.
214-
TextilePipeline = Pipeline.new [
215-
TextileFilter,
216-
SanitizationFilter
217-
], {:whitelist => SanitizationFilter::LIMITED}, Result
218-
219-
extend self
22046
end
22147
end
22248

lib/github/html/email_reply_filter.rb

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
require 'escape_utils'
2+
13
module GitHub::HTML
24
# HTML Filter that converts email reply text into an HTML DocumentFragment.
35
# It must be used as the first filter in a pipeline.

lib/github/html/filter.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -160,7 +160,7 @@ class TextFilter < Filter
160160
def initialize(text, context = nil, result = nil)
161161
raise TypeError, "text cannot be HTML" if text.is_a?(DocumentFragment)
162162
# Ensure that this is always a string
163-
@text = text.try(:to_str) || text.to_s
163+
@text = text.try(:to_str) || text.to_s
164164
super nil, context, result
165165
end
166166
end

0 commit comments

Comments
 (0)