simple_format 0.0.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.
- checksums.yaml +7 -0
- data/.gitignore +17 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +54 -0
- data/Rakefile +1 -0
- data/config/index.json +4356 -0
- data/lib/simple_format.rb +40 -0
- data/lib/simple_format/auto_link.rb +94 -0
- data/lib/simple_format/emoji.rb +112 -0
- data/lib/simple_format/version.rb +3 -0
- data/simple_format.gemspec +25 -0
- metadata +111 -0
@@ -0,0 +1,40 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
require 'simple_format/version'
|
3
|
+
require 'simple_format/emoji'
|
4
|
+
require 'simple_format/auto_link'
|
5
|
+
require 'sanitize' unless defined?(::Sanitize)
|
6
|
+
|
7
|
+
module SimpleFormat
|
8
|
+
extend self
|
9
|
+
|
10
|
+
def h(string, size=64)
|
11
|
+
return string unless string
|
12
|
+
string = emoji.replace_emoji_with_images(string, size)
|
13
|
+
string = auto_link(string)
|
14
|
+
string = clean(string)
|
15
|
+
end
|
16
|
+
|
17
|
+
def replace_emoji_with_images(string, size=64)
|
18
|
+
emoji.replace_emoji_with_images(string, size)
|
19
|
+
end
|
20
|
+
|
21
|
+
def clean(html, options = {})
|
22
|
+
options = { elements: sanitized_allowed_tags, attributes: { all: sanitized_allowed_attributes } } if options.empty?
|
23
|
+
Sanitize.clean(html, options)
|
24
|
+
end
|
25
|
+
|
26
|
+
def emoji
|
27
|
+
@emoji ||= Emoji.new
|
28
|
+
end
|
29
|
+
|
30
|
+
private
|
31
|
+
|
32
|
+
def sanitized_allowed_tags
|
33
|
+
['a', 'p', 'br', 'img', 'abbr', 'mark', 'm', 'fieldset', 'legend', 'label', 'summary', 'details', 'address', 'map', 'area', 'td', 'tr', 'th', 'thead', 'tbody', 'tfoot', 'caption', 'table', 'h4', 'h5', 'h6', 'hr', 'span', 'em', 'dl', 'dt', 'dd', 'b', 'del', 'ins', 'small', 'pre', 'blockquote', 'strong', 'audio', 'video', 'source', 'ul', 'ol', 'li', 'i', 'embed', 'sub', 'sup']
|
34
|
+
end
|
35
|
+
|
36
|
+
def sanitized_allowed_attributes
|
37
|
+
['shape', 'coords', 'target', 'href', 'muted', 'volume', 'name', 'class', 'title', 'border', 'poster', 'loop', 'autoplay', 'allowfullscreen', 'fullscreen', 'align' , 'quality', 'allowscriptaccess', 'wmode', 'flashvars', 'webkit-playsinline', 'x-webkit-airplay', 'data-original', 'src', 'controls', 'preload', 'type', 'width', 'height', 'size', 'alt']
|
38
|
+
end
|
39
|
+
|
40
|
+
end
|
@@ -0,0 +1,94 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
|
3
|
+
module SimpleFormat
|
4
|
+
extend self
|
5
|
+
|
6
|
+
def auto_link(text, *args, &block) #link = :all, html = {}, &block)
|
7
|
+
return text unless text
|
8
|
+
|
9
|
+
options = {}
|
10
|
+
unless args.empty?
|
11
|
+
options[:link] = args[0] || :all
|
12
|
+
end
|
13
|
+
options.merge!(:link => :all)
|
14
|
+
# 转换回行
|
15
|
+
text = nl2br(text).to_str
|
16
|
+
# 根据类型生成链接
|
17
|
+
case options[:link].to_sym
|
18
|
+
when :all then auto_link_email_addresses(auto_link_urls(text, &block), &block)
|
19
|
+
when :email_addresses then auto_link_email_addresses(text, &block)
|
20
|
+
when :urls then auto_link_urls(text, &block)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
private
|
25
|
+
|
26
|
+
def nl2br(string)
|
27
|
+
if string
|
28
|
+
string.gsub("\n\r","<br />").gsub("\r", "").gsub("\n", "<br />")
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
AUTO_LINK_RE = %r{
|
33
|
+
(?: ((?:ed2k|ftp|http|https|irc|mailto|news|gopher|nntp|telnet|webcal|xmpp|callto|feed|svn|urn|aim|rsync|tag|ssh|sftp|rtsp|afs|file):)// | www\. )
|
34
|
+
[^\s<]+
|
35
|
+
}x
|
36
|
+
|
37
|
+
# regexps for determining context, used high-volume
|
38
|
+
AUTO_LINK_CRE = [/<[^>]+$/, /^[^>]*>/, /<a\b.*?>/i, /<\/a>/i]
|
39
|
+
|
40
|
+
AUTO_EMAIL_RE = /[\w.!#\$%+-]+@[\w-]+(?:\.[\w-]+)+/
|
41
|
+
|
42
|
+
BRACKETS = { ']' => '[', ')' => '(', '}' => '{' }
|
43
|
+
|
44
|
+
WORD_PATTERN = RUBY_VERSION < '1.9' ? '\w' : '\p{Word}'
|
45
|
+
|
46
|
+
# Turns all urls into clickable links. If a block is given, each url
|
47
|
+
# is yielded and the result is used as the link text.
|
48
|
+
def auto_link_urls(text)
|
49
|
+
|
50
|
+
text.gsub(AUTO_LINK_RE) do
|
51
|
+
scheme, href = $1, $&
|
52
|
+
punctuation = []
|
53
|
+
|
54
|
+
if auto_linked?($`, $')
|
55
|
+
# do not change string; URL is already linked
|
56
|
+
href
|
57
|
+
else
|
58
|
+
# don't include trailing punctuation character as part of the URL
|
59
|
+
while href.sub!(/[^#{WORD_PATTERN}\/-]$/, '')
|
60
|
+
punctuation.push $&
|
61
|
+
if opening = BRACKETS[punctuation.last] and href.scan(opening).size > href.scan(punctuation.last).size
|
62
|
+
href << punctuation.pop
|
63
|
+
break
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
link_text = block_given?? yield(href) : href
|
68
|
+
href = 'http://' + href unless scheme
|
69
|
+
|
70
|
+
"<a href='#{href}' target='_blank'>#{link_text}</a>" + punctuation.reverse.join('')
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
# Turns all email addresses into clickable links. If a block is given,
|
76
|
+
# each email is yielded and the result is used as the link text.
|
77
|
+
def auto_link_email_addresses(text)
|
78
|
+
text.gsub(AUTO_EMAIL_RE) do
|
79
|
+
text = $&
|
80
|
+
if auto_linked?($`, $')
|
81
|
+
text
|
82
|
+
else
|
83
|
+
display_text = (block_given?) ? yield(text) : text
|
84
|
+
# mail_to text, display_text
|
85
|
+
"<a href='mailto:#{text}'>#{display_text}</a>"
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
# Detects already linked context or position in the middle of a tag
|
91
|
+
def auto_linked?(left, right)
|
92
|
+
(left =~ AUTO_LINK_CRE[0] and right =~ AUTO_LINK_CRE[1]) or (left.rindex(AUTO_LINK_CRE[2]) and $' !~ AUTO_LINK_CRE[3])
|
93
|
+
end
|
94
|
+
end
|
@@ -0,0 +1,112 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
require 'multi_json' unless defined?(::MultiJson)
|
3
|
+
|
4
|
+
module SimpleFormat
|
5
|
+
class Emoji
|
6
|
+
def initialize(mapping=nil)
|
7
|
+
mapping ||= begin
|
8
|
+
emoji_json = File.read(File.absolute_path(File.dirname(__FILE__) + '/../../config/index.json'))
|
9
|
+
MultiJson.load(emoji_json, symbolize_keys: true)
|
10
|
+
end
|
11
|
+
|
12
|
+
@emoji_by_name = {}
|
13
|
+
@emoji_by_unicode = {}
|
14
|
+
|
15
|
+
mapping.each do |emoji_hash|
|
16
|
+
name = emoji_hash[:name]
|
17
|
+
@emoji_by_name[name] = emoji_hash if name
|
18
|
+
|
19
|
+
unicode = emoji_hash[:unicode]
|
20
|
+
@emoji_by_unicode[unicode] = emoji_hash if unicode
|
21
|
+
end
|
22
|
+
|
23
|
+
@emoji_name_regex = /:([a-z0-9\+\-_]+):/
|
24
|
+
@emoji_unicode_regex = /#{@emoji_by_unicode.keys.join('|')}/
|
25
|
+
end
|
26
|
+
# 主机地址
|
27
|
+
def asset_host
|
28
|
+
@asset_host || '//l.ruby-china.org'
|
29
|
+
end
|
30
|
+
# 设置主机地址
|
31
|
+
def asset_host=(host)
|
32
|
+
@asset_host = host
|
33
|
+
end
|
34
|
+
# 资源路径
|
35
|
+
def asset_path
|
36
|
+
@asset_path || '/assets/emojis/'
|
37
|
+
end
|
38
|
+
# 设置路径
|
39
|
+
def asset_path=(path)
|
40
|
+
@asset_path = path
|
41
|
+
end
|
42
|
+
# 通过(名称、字符)替换表情
|
43
|
+
def replace_emoji_with_images(string, size='64')
|
44
|
+
return string unless string
|
45
|
+
|
46
|
+
html ||= string.dup
|
47
|
+
html = replace_name_with_images(html, size)
|
48
|
+
html = replace_unicode_with_images(html.to_str, size)
|
49
|
+
puts html
|
50
|
+
puts '_'*88 + 'replace_unicode_with_images'
|
51
|
+
return html
|
52
|
+
end
|
53
|
+
# 通过(名称)替换表情
|
54
|
+
def replace_name_with_images(string, size='64')
|
55
|
+
unless string && string.match(names_regex)
|
56
|
+
return string
|
57
|
+
end
|
58
|
+
|
59
|
+
string.to_str.gsub(names_regex) do |match|
|
60
|
+
if names.include?($1)
|
61
|
+
%Q{<img class="emoji" src="#{ image_url_for_name($1) }" width="#{size}" height="#{size}" />}
|
62
|
+
else
|
63
|
+
match
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
# 通过(字符)替换表情
|
68
|
+
def replace_unicode_with_images(string, size='64')
|
69
|
+
unless string && string.match(unicodes_regex)
|
70
|
+
return string
|
71
|
+
end
|
72
|
+
|
73
|
+
html ||= string.dup
|
74
|
+
html.gsub!(unicodes_regex) do |unicode|
|
75
|
+
%Q{<img class="emoji" src="#{ image_url_for_unicode(unicode) }" width="#{size}" height="#{size}" />}
|
76
|
+
end
|
77
|
+
end
|
78
|
+
# 通过(名称)合成图片地址
|
79
|
+
def image_url_for_name(name)
|
80
|
+
"#{asset_host}#{ File.join(asset_path, name) }.png"
|
81
|
+
end
|
82
|
+
# 通过(字符)合成图片地址
|
83
|
+
def image_url_for_unicode(unicode)
|
84
|
+
emoji = find_by_unicode(unicode)
|
85
|
+
image_url_for_name(emoji[:name]) unless emoji.nil?
|
86
|
+
end
|
87
|
+
# 通过(字符)找表情
|
88
|
+
def find_by_unicode(moji)
|
89
|
+
@emoji_by_unicode[moji]
|
90
|
+
end
|
91
|
+
# 通过(名称)表情
|
92
|
+
def find_by_name(name)
|
93
|
+
@emoji_by_name[name]
|
94
|
+
end
|
95
|
+
# 名称列表
|
96
|
+
def names
|
97
|
+
@emoji_by_name.keys
|
98
|
+
end
|
99
|
+
# 字符列表
|
100
|
+
def unicodes
|
101
|
+
@emoji_by_unicode.keys
|
102
|
+
end
|
103
|
+
# 名称匹配表达式
|
104
|
+
def names_regex
|
105
|
+
@emoji_name_regex
|
106
|
+
end
|
107
|
+
# 字符匹配表达式
|
108
|
+
def unicodes_regex
|
109
|
+
@emoji_unicode_regex
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'simple_format/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "simple_format"
|
8
|
+
spec.version = SimpleFormat::VERSION
|
9
|
+
spec.authors = ["Howl王"]
|
10
|
+
spec.email = ["howl.wong@gmail.com"]
|
11
|
+
spec.summary = %q{Returns text transformed into HTML using simple formatting rules.}
|
12
|
+
spec.homepage = "https://github.com/mimosa/simple_format/"
|
13
|
+
spec.license = "MIT"
|
14
|
+
|
15
|
+
spec.files = `git ls-files`.split($/)
|
16
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
17
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
18
|
+
spec.require_paths = ["lib"]
|
19
|
+
|
20
|
+
spec.add_dependency "multi_json"
|
21
|
+
spec.add_dependency "sanitize"
|
22
|
+
|
23
|
+
spec.add_development_dependency "bundler", "~> 1.5"
|
24
|
+
spec.add_development_dependency "rake"
|
25
|
+
end
|
metadata
ADDED
@@ -0,0 +1,111 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: simple_format
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Howl王
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2014-02-28 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: multi_json
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: sanitize
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: bundler
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '1.5'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '1.5'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: rake
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ">="
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0'
|
69
|
+
description:
|
70
|
+
email:
|
71
|
+
- howl.wong@gmail.com
|
72
|
+
executables: []
|
73
|
+
extensions: []
|
74
|
+
extra_rdoc_files: []
|
75
|
+
files:
|
76
|
+
- ".gitignore"
|
77
|
+
- Gemfile
|
78
|
+
- LICENSE.txt
|
79
|
+
- README.md
|
80
|
+
- Rakefile
|
81
|
+
- config/index.json
|
82
|
+
- lib/simple_format.rb
|
83
|
+
- lib/simple_format/auto_link.rb
|
84
|
+
- lib/simple_format/emoji.rb
|
85
|
+
- lib/simple_format/version.rb
|
86
|
+
- simple_format.gemspec
|
87
|
+
homepage: https://github.com/mimosa/simple_format/
|
88
|
+
licenses:
|
89
|
+
- MIT
|
90
|
+
metadata: {}
|
91
|
+
post_install_message:
|
92
|
+
rdoc_options: []
|
93
|
+
require_paths:
|
94
|
+
- lib
|
95
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
96
|
+
requirements:
|
97
|
+
- - ">="
|
98
|
+
- !ruby/object:Gem::Version
|
99
|
+
version: '0'
|
100
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
101
|
+
requirements:
|
102
|
+
- - ">="
|
103
|
+
- !ruby/object:Gem::Version
|
104
|
+
version: '0'
|
105
|
+
requirements: []
|
106
|
+
rubyforge_project:
|
107
|
+
rubygems_version: 2.2.1
|
108
|
+
signing_key:
|
109
|
+
specification_version: 4
|
110
|
+
summary: Returns text transformed into HTML using simple formatting rules.
|
111
|
+
test_files: []
|