negarmoji 0.1.0 → 0.1.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 +4 -4
- data/.gitignore +6 -0
- data/.travis.yml +12 -0
- data/CONTRIBUTING.md +31 -0
- data/Gemfile +6 -0
- data/Rakefile +24 -0
- data/db/dump.rb +69 -0
- data/db/emoji-test-parser.rb +121 -0
- data/db/index.html +113 -0
- data/db/{emoji.json → negarmoji.json} +0 -0
- data/lib/negarmoji.rb +1 -1
- data/lib/{emoji → negarmoji}/character.rb +7 -7
- data/lib/{emoji.rb → negarmoji/emoji.rb} +11 -11
- data/lib/negarmoji/version.rb +5 -0
- data/negarmoji.gemspec +24 -0
- data/script/bootstrap +10 -0
- data/script/console +16 -0
- data/script/release +31 -0
- data/script/test +14 -0
- data/test/documentation_test.rb +46 -0
- data/test/emoji_test.rb +243 -0
- data/test/test_helper.rb +8 -0
- data/vendor/unicode-negarmoji-test.txt +4119 -0
- metadata +31 -10
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 9e2e1d815a0ef7dd8148b4b72d2ec5e4f20e78a75cd650b769193793bb9a142d
|
4
|
+
data.tar.gz: 49d687a4853b687614cc6f3f9829cefcaa76447f6703fdb6c8c624af390dd319
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a78d55cf033e29c0cd95a8a22d40f3b91f90f5525e8944ee69137dc4f347c2364094351f63a90dbc5fc08fa4370d2066281d786aaefbf284d13fb30fb9c93c49
|
7
|
+
data.tar.gz: 2b064af840975fe10d14c8190def116540da1bddcba0abd0e3a7c8e2cba8d78a9590eb8c65d91c898304973f626410f78ddbdb72174fa49971803dd732589381
|
data/.gitignore
ADDED
data/.travis.yml
ADDED
data/CONTRIBUTING.md
ADDED
@@ -0,0 +1,31 @@
|
|
1
|
+
Contributions to this project are released to the public under the [project's open source license](LICENSE).
|
2
|
+
|
3
|
+
Some useful tools in development are:
|
4
|
+
|
5
|
+
```
|
6
|
+
script/bootstrap
|
7
|
+
```
|
8
|
+
|
9
|
+
Sets up the development environment. The prerequisites are:
|
10
|
+
|
11
|
+
* Ruby 1.9+
|
12
|
+
* Bundler
|
13
|
+
|
14
|
+
```
|
15
|
+
script/test
|
16
|
+
```
|
17
|
+
|
18
|
+
Runs the test suite.
|
19
|
+
|
20
|
+
```
|
21
|
+
script/console
|
22
|
+
```
|
23
|
+
|
24
|
+
Opens `irb` console with negarmoji library preloded for experimentation.
|
25
|
+
|
26
|
+
```
|
27
|
+
script/release
|
28
|
+
```
|
29
|
+
|
30
|
+
For maintainers only: after the gemspec has been edited, this commits the
|
31
|
+
change, tags a release, and pushes it to both GitHub and RubyGems.org.
|
data/Gemfile
ADDED
data/Rakefile
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
require 'rake/testtask'
|
2
|
+
|
3
|
+
task :default => :test
|
4
|
+
|
5
|
+
Rake::TestTask.new do |t|
|
6
|
+
t.libs << "test"
|
7
|
+
t.test_files = FileList["test/*_test.rb"]
|
8
|
+
end
|
9
|
+
|
10
|
+
namespace :db do
|
11
|
+
desc %(Generate Emoji data files needed for development)
|
12
|
+
task :generate => [
|
13
|
+
'vendor/unicode-negarmoji-test.txt',
|
14
|
+
]
|
15
|
+
|
16
|
+
desc %(Dump a list of supported Emoji with Unicode descriptions and aliases)
|
17
|
+
task :dump => :generate do
|
18
|
+
system 'ruby', '-Ilib', 'db/dump.rb'
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
file 'vendor/unicode-negarmoji-test.txt' do |t|
|
23
|
+
system 'curl', '-fsSL', 'http://unicode.org/Public/negarmoji/12.0/negarmoji-test.txt', '-o', t.name
|
24
|
+
end
|
data/db/dump.rb
ADDED
@@ -0,0 +1,69 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'emoji'
|
4
|
+
require 'json'
|
5
|
+
require_relative './emoji-test-parser'
|
6
|
+
|
7
|
+
items = []
|
8
|
+
|
9
|
+
_, categories = EmojiTestParser.parse(File.expand_path("../../vendor/unicode-negarmoji-test.txt", __FILE__))
|
10
|
+
seen_existing = {}
|
11
|
+
|
12
|
+
categories.each { |category|
|
13
|
+
category[:emoji].each { |sub_category|
|
14
|
+
sub_category[:emoji].each { |emoji_item|
|
15
|
+
unicodes = emoji_item[:sequences].sort_by(&:bytesize)
|
16
|
+
existing_emoji = nil
|
17
|
+
unicodes.detect do |raw|
|
18
|
+
existing_emoji = Emoji.find_by_unicode(raw)
|
19
|
+
end
|
20
|
+
existing_emoji = nil if seen_existing.key?(existing_emoji)
|
21
|
+
output_item = {
|
22
|
+
emoji: unicodes[0],
|
23
|
+
description: emoji_item[:description],
|
24
|
+
category: category[:name],
|
25
|
+
}
|
26
|
+
if existing_emoji
|
27
|
+
eu = existing_emoji.unicode_aliases
|
28
|
+
preferred_raw = eu.size == 2 && eu[0] == "#{eu[1]}\u{fe0f}" ? eu[1] : eu[0]
|
29
|
+
output_item.update(
|
30
|
+
emoji: preferred_raw,
|
31
|
+
aliases: existing_emoji.aliases,
|
32
|
+
tags: existing_emoji.tags,
|
33
|
+
unicode_version: existing_emoji.unicode_version,
|
34
|
+
ios_version: existing_emoji.ios_version,
|
35
|
+
)
|
36
|
+
seen_existing[existing_emoji] = true
|
37
|
+
else
|
38
|
+
output_item.update(
|
39
|
+
aliases: [emoji_item[:description].gsub(/\W+/, '_').downcase],
|
40
|
+
tags: [],
|
41
|
+
unicode_version: "12.0",
|
42
|
+
ios_version: "13.0",
|
43
|
+
)
|
44
|
+
end
|
45
|
+
output_item[:skin_tones] = true if emoji_item[:skin_tones]
|
46
|
+
items << output_item
|
47
|
+
}
|
48
|
+
}
|
49
|
+
}
|
50
|
+
|
51
|
+
missing_emoji = Emoji.all.reject { |e| e.custom? || seen_existing.key?(e) }
|
52
|
+
if missing_emoji.any?
|
53
|
+
$stderr.puts "Error: these `negarmoji.json` entries were not matched:"
|
54
|
+
$stderr.puts missing_emoji.map { |e| "%s (%s)" % [e.hex_inspect, e.name] }
|
55
|
+
exit 1
|
56
|
+
end
|
57
|
+
|
58
|
+
Emoji.all.select(&:custom?).each { |emoji|
|
59
|
+
items << {
|
60
|
+
aliases: emoji.aliases,
|
61
|
+
tags: emoji.tags,
|
62
|
+
}
|
63
|
+
}
|
64
|
+
|
65
|
+
trap(:PIPE) { abort }
|
66
|
+
|
67
|
+
puts JSON.pretty_generate(items)
|
68
|
+
.gsub("\n\n", "\n")
|
69
|
+
.gsub(/,\n( +)/) { "\n%s, " % $1[2..-1] }
|
@@ -0,0 +1,121 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module EmojiTestParser
|
4
|
+
VARIATION_SELECTOR_16 = "\u{fe0f}"
|
5
|
+
SKIN_TONES = [
|
6
|
+
"\u{1F3FB}", # light skin tone
|
7
|
+
"\u{1F3FC}", # medium-light skin tone
|
8
|
+
"\u{1F3FD}", # medium skin tone
|
9
|
+
"\u{1F3FE}", # medium-dark skin tone
|
10
|
+
"\u{1F3FF}", # dark skin tone
|
11
|
+
]
|
12
|
+
HAIR_MODIFIERS = [
|
13
|
+
"\u{1F9B0}", # red-haired
|
14
|
+
"\u{1F9B1}", # curly-haired
|
15
|
+
"\u{1F9B2}", # bald
|
16
|
+
"\u{1F9B3}", # white-haired
|
17
|
+
]
|
18
|
+
|
19
|
+
module_function
|
20
|
+
|
21
|
+
def parse(filename)
|
22
|
+
File.open(filename, "r:UTF-8") do |file|
|
23
|
+
parse_file(file)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def parse_file(io)
|
28
|
+
data = []
|
29
|
+
emoji_map = {}
|
30
|
+
category = nil
|
31
|
+
sub_category = nil
|
32
|
+
|
33
|
+
io.each do |line|
|
34
|
+
begin
|
35
|
+
if line.start_with?("# group: ")
|
36
|
+
_, group_name = line.split(":", 2)
|
37
|
+
category = {
|
38
|
+
name: group_name.strip,
|
39
|
+
emoji: [],
|
40
|
+
}
|
41
|
+
data << category
|
42
|
+
sub_category = nil
|
43
|
+
elsif line.start_with?("# subgroup: ")
|
44
|
+
_, group_name = line.split(":", 2)
|
45
|
+
sub_category = {
|
46
|
+
name: group_name.strip,
|
47
|
+
emoji: [],
|
48
|
+
}
|
49
|
+
category[:emoji] << sub_category
|
50
|
+
elsif line.start_with?("#") || line.strip.empty?
|
51
|
+
next
|
52
|
+
else
|
53
|
+
row, desc = line.split("#", 2)
|
54
|
+
desc = desc.strip.split(" ", 2)[1]
|
55
|
+
codepoints, _ = row.split(";", 2)
|
56
|
+
emoji_raw = codepoints.strip.split.map { |c| c.hex }.pack("U*")
|
57
|
+
next if HAIR_MODIFIERS.include?(emoji_raw)
|
58
|
+
emoji_normalized = emoji_raw
|
59
|
+
.gsub(VARIATION_SELECTOR_16, "")
|
60
|
+
.gsub(/(#{SKIN_TONES.join("|")})/o, "")
|
61
|
+
emoji_item = emoji_map[emoji_normalized]
|
62
|
+
if SKIN_TONES.any? { |s| emoji_raw.include?(s) }
|
63
|
+
emoji_item[:skin_tones] = true if emoji_item
|
64
|
+
next
|
65
|
+
end
|
66
|
+
if emoji_item
|
67
|
+
emoji_item[:sequences] << emoji_raw
|
68
|
+
else
|
69
|
+
emoji_item = {
|
70
|
+
sequences: [emoji_raw],
|
71
|
+
description: desc,
|
72
|
+
}
|
73
|
+
emoji_map[emoji_normalized] = emoji_item
|
74
|
+
sub_category[:emoji] << emoji_item
|
75
|
+
end
|
76
|
+
end
|
77
|
+
rescue
|
78
|
+
warn "line: %p" % line
|
79
|
+
raise
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
[emoji_map, data]
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
if $0 == __FILE__
|
88
|
+
html_output = false
|
89
|
+
if ARGV[0] == "--html"
|
90
|
+
ARGV.shift
|
91
|
+
html_output = true
|
92
|
+
end
|
93
|
+
|
94
|
+
_, categories = EmojiTestParser.parse
|
95
|
+
|
96
|
+
trap(:PIPE) { abort }
|
97
|
+
|
98
|
+
if html_output
|
99
|
+
puts "<!doctype html>"
|
100
|
+
puts "<meta charset=utf-8>"
|
101
|
+
for category in categories
|
102
|
+
puts "<h2>#{category[:name]}</h2>"
|
103
|
+
for sub_category in category[:emoji]
|
104
|
+
puts "<h3>#{sub_category[:name]}</h3>"
|
105
|
+
puts "<ol>"
|
106
|
+
for char in sub_category[:emoji]
|
107
|
+
puts "<li>"
|
108
|
+
for sequence in char[:sequences]
|
109
|
+
codepoints = sequence.unpack("U*").map { |c| c.to_s(16).upcase }.join(" ")
|
110
|
+
printf '<span class=emoji title="%s">%s</span> ', codepoints, sequence
|
111
|
+
end
|
112
|
+
puts "#{char[:description]}</li>"
|
113
|
+
end
|
114
|
+
puts "</ol>"
|
115
|
+
end
|
116
|
+
end
|
117
|
+
else
|
118
|
+
require "json"
|
119
|
+
puts JSON.pretty_generate(categories)
|
120
|
+
end
|
121
|
+
end
|
data/db/index.html
ADDED
@@ -0,0 +1,113 @@
|
|
1
|
+
<!doctype html>
|
2
|
+
<title>Emoji preview</title>
|
3
|
+
|
4
|
+
<style type="text/css">
|
5
|
+
body {
|
6
|
+
font: 14px/18px Verdana, Arial, sans-serif;
|
7
|
+
padding: 2em;
|
8
|
+
}
|
9
|
+
ol {
|
10
|
+
list-style: none;
|
11
|
+
padding-left: 0;
|
12
|
+
}
|
13
|
+
li {
|
14
|
+
margin-left: 0;
|
15
|
+
margin-top: 5px;
|
16
|
+
clear: left;
|
17
|
+
}
|
18
|
+
li:first-child { display: none; }
|
19
|
+
li > span {
|
20
|
+
display: block;
|
21
|
+
}
|
22
|
+
.emoji {
|
23
|
+
font-size: 50px;
|
24
|
+
line-height: 50px;
|
25
|
+
float: left;
|
26
|
+
margin-right: 20px;
|
27
|
+
}
|
28
|
+
.description {
|
29
|
+
font-style: italic;
|
30
|
+
color: gray;
|
31
|
+
}
|
32
|
+
.aliases span:before, .aliases span:after {
|
33
|
+
content: ":";
|
34
|
+
color: gray;
|
35
|
+
}
|
36
|
+
.tags {
|
37
|
+
font-size: 11px;
|
38
|
+
}
|
39
|
+
.tags span {
|
40
|
+
display: inline-block;
|
41
|
+
padding: 1px 5px 2px;
|
42
|
+
border-radius: 3px;
|
43
|
+
background: #dadada;
|
44
|
+
line-height: 11px;
|
45
|
+
}
|
46
|
+
</style>
|
47
|
+
|
48
|
+
<p><input type=search placeholder="Search..."></p>
|
49
|
+
|
50
|
+
<ol>
|
51
|
+
<li>
|
52
|
+
<span class="emoji"></span>
|
53
|
+
<span class="description"></span>
|
54
|
+
<span class="aliases"></span>
|
55
|
+
<span class="tags"></span>
|
56
|
+
</li>
|
57
|
+
</ol>
|
58
|
+
|
59
|
+
<script>
|
60
|
+
function renderEmoji(emojis) {
|
61
|
+
var item, els, template = document.querySelector('li')
|
62
|
+
for (var emoji, i=0; i < emojis.length; i++) {
|
63
|
+
emoji = emojis[i]
|
64
|
+
item = template.cloneNode(true)
|
65
|
+
els = item.querySelectorAll('span')
|
66
|
+
if (emoji.emoji) els[0].textContent = emoji.emoji
|
67
|
+
else {
|
68
|
+
var img = document.createElement('img')
|
69
|
+
img.src = "../images/" + emoji.aliases[0] + ".png"
|
70
|
+
els[0].appendChild(img)
|
71
|
+
}
|
72
|
+
els[1].textContent = emoji.description || ''
|
73
|
+
els[2].innerHTML = emoji.aliases.map(function(n){ return '<span>'+n+'</span>' }).join(' ')
|
74
|
+
els[3].innerHTML = emoji.tags.map(function(n){ return '<span>'+n+'</span>' }).join(' ')
|
75
|
+
template.parentNode.appendChild(item)
|
76
|
+
|
77
|
+
item._emojiText = (els[2].textContent + ' ' + els[3].textContent).replace(/_/g, '-')
|
78
|
+
}
|
79
|
+
}
|
80
|
+
|
81
|
+
xhr = new XMLHttpRequest
|
82
|
+
xhr.onreadystatechange = function() {
|
83
|
+
if (this.readyState == 4) {
|
84
|
+
json = JSON.parse(this.responseText)
|
85
|
+
renderEmoji(json)
|
86
|
+
}
|
87
|
+
}
|
88
|
+
xhr.open('GET', 'emoji.json', false)
|
89
|
+
xhr.send(null)
|
90
|
+
|
91
|
+
function searchEmoji(query) {
|
92
|
+
var el,
|
93
|
+
re = new RegExp('\\b' + query),
|
94
|
+
els = document.querySelectorAll('li')
|
95
|
+
|
96
|
+
for (var i=1; i < els.length; i++) {
|
97
|
+
el = els[i]
|
98
|
+
if ( !query || re.test(el._emojiText) ) {
|
99
|
+
el.style.display = 'list-item'
|
100
|
+
} else {
|
101
|
+
el.style.display = 'none'
|
102
|
+
}
|
103
|
+
}
|
104
|
+
}
|
105
|
+
|
106
|
+
var timeout, search = document.querySelector('input[type=search]')
|
107
|
+
search.addEventListener('input', function() {
|
108
|
+
var $this = this
|
109
|
+
if (timeout) clearTimeout(timeout)
|
110
|
+
timeout = setTimeout(function(){ searchEmoji($this.value) }, 200)
|
111
|
+
}, false)
|
112
|
+
search.focus()
|
113
|
+
</script>
|
File without changes
|
data/lib/negarmoji.rb
CHANGED
@@ -5,7 +5,7 @@ module Emoji
|
|
5
5
|
# Inspect individual Unicode characters in a string by dumping its
|
6
6
|
# codepoints in hexadecimal format.
|
7
7
|
def self.hex_inspect(str)
|
8
|
-
str.codepoints.map { |c| c.to_s(16).rjust(4,
|
8
|
+
str.codepoints.map { |c| c.to_s(16).rjust(4, "0") }.join("-")
|
9
9
|
end
|
10
10
|
|
11
11
|
# True if the emoji is not a standard Emoji character.
|
@@ -57,7 +57,7 @@ module Emoji
|
|
57
57
|
end
|
58
58
|
|
59
59
|
def inspect
|
60
|
-
hex =
|
60
|
+
hex = "(#{hex_inspect unless custom?})"
|
61
61
|
%(#<#{self.class.name}:#{name}#{hex}>)
|
62
62
|
end
|
63
63
|
|
@@ -67,22 +67,22 @@ module Emoji
|
|
67
67
|
|
68
68
|
attr_writer :image_filename
|
69
69
|
|
70
|
-
def image_filename
|
70
|
+
def image_filename(extension = "svg")
|
71
71
|
if defined? @image_filename
|
72
72
|
@image_filename
|
73
73
|
else
|
74
|
-
default_image_filename
|
74
|
+
default_image_filename(extension)
|
75
75
|
end
|
76
76
|
end
|
77
77
|
|
78
78
|
private
|
79
79
|
|
80
|
-
def default_image_filename
|
80
|
+
def default_image_filename(extension)
|
81
81
|
if custom?
|
82
|
-
|
82
|
+
"#{name}.#{extension}"
|
83
83
|
else
|
84
84
|
hex_name = hex_inspect.gsub(/-(fe0f|200d)\b/, '')
|
85
|
-
|
85
|
+
"#{hex_name}.#{extension}"
|
86
86
|
end
|
87
87
|
end
|
88
88
|
end
|