motion-markdown-it-plugins 8.4.1 → 8.4.2

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,161 @@
1
+ module MotionMarkdownItPlugins
2
+ module EmojiPlugin
3
+ module Data
4
+ module Light
5
+ EMOJIES_DEF_LIGHT = {
6
+ "grinning" => "😀",
7
+ "smiley" => "😃",
8
+ "smile" => "😄",
9
+ "grin" => "😁",
10
+ "laughing" => "😆",
11
+ "satisfied" => "😆",
12
+ "sweat_smile" => "😅",
13
+ "joy" => "😂",
14
+ "blush" => "😊",
15
+ "innocent" => "😇",
16
+ "wink" => "😉",
17
+ "relieved" => "😌",
18
+ "heart_eyes" => "😍",
19
+ "kissing_heart" => "😘",
20
+ "kissing" => "😗",
21
+ "kissing_smiling_eyes" => "😙",
22
+ "kissing_closed_eyes" => "😚",
23
+ "yum" => "😋",
24
+ "stuck_out_tongue_winking_eye" => "😜",
25
+ "stuck_out_tongue_closed_eyes" => "😝",
26
+ "stuck_out_tongue" => "😛",
27
+ "sunglasses" => "😎",
28
+ "smirk" => "😏",
29
+ "unamused" => "😒",
30
+ "disappointed" => "😞",
31
+ "pensive" => "😔",
32
+ "worried" => "😟",
33
+ "confused" => "😕",
34
+ "persevere" => "😣",
35
+ "confounded" => "😖",
36
+ "tired_face" => "😫",
37
+ "weary" => "😩",
38
+ "angry" => "😠",
39
+ "rage" => "😡",
40
+ "pout" => "😡",
41
+ "no_mouth" => "😶",
42
+ "neutral_face" => "😐",
43
+ "expressionless" => "😑",
44
+ "hushed" => "😯",
45
+ "frowning" => "😦",
46
+ "anguished" => "😧",
47
+ "open_mouth" => "😮",
48
+ "astonished" => "😲",
49
+ "dizzy_face" => "😵",
50
+ "flushed" => "😳",
51
+ "scream" => "😱",
52
+ "fearful" => "😨",
53
+ "cold_sweat" => "😰",
54
+ "cry" => "😢",
55
+ "disappointed_relieved" => "😥",
56
+ "sob" => "😭",
57
+ "sweat" => "😓",
58
+ "sleepy" => "😪",
59
+ "sleeping" => "😴",
60
+ "mask" => "😷",
61
+ "smiling_imp" => "😈",
62
+ "smiley_cat" => "😺",
63
+ "smile_cat" => "😸",
64
+ "joy_cat" => "😹",
65
+ "heart_eyes_cat" => "😻",
66
+ "smirk_cat" => "😼",
67
+ "kissing_cat" => "😽",
68
+ "scream_cat" => "🙀",
69
+ "crying_cat_face" => "😿",
70
+ "pouting_cat" => "😾",
71
+ "fist_raised" => "✊",
72
+ "fist" => "✊",
73
+ "v" => "✌️",
74
+ "point_up" => "☝️",
75
+ "hand" => "✋",
76
+ "raised_hand" => "✋",
77
+ "cat" => "🐱",
78
+ "mouse" => "🐭",
79
+ "cow" => "🐮",
80
+ "monkey_face" => "🐵",
81
+ "star" => "⭐️",
82
+ "sparkles" => "✨",
83
+ "zap" => "⚡️",
84
+ "sunny" => "☀️",
85
+ "cloud" => "☁️",
86
+ "snowflake" => "❄️",
87
+ "umbrella" => "☔️",
88
+ "coffee" => "☕️",
89
+ "airplane" => "✈️",
90
+ "anchor" => "⚓️",
91
+ "watch" => "⌚️",
92
+ "phone" => "☎️",
93
+ "telephone" => "☎️",
94
+ "hourglass" => "⌛️",
95
+ "email" => "✉️",
96
+ "envelope" => "✉️",
97
+ "scissors" => "✂️",
98
+ "black_nib" => "✒️",
99
+ "pencil2" => "✏️",
100
+ "heart" => "❤️",
101
+ "aries" => "♈️",
102
+ "taurus" => "♉️",
103
+ "gemini" => "♊️",
104
+ "cancer" => "♋️",
105
+ "leo" => "♌️",
106
+ "virgo" => "♍️",
107
+ "libra" => "♎️",
108
+ "scorpius" => "♏️",
109
+ "sagittarius" => "♐️",
110
+ "capricorn" => "♑️",
111
+ "aquarius" => "♒️",
112
+ "pisces" => "♓️",
113
+ "eight_pointed_black_star" => "✴️",
114
+ "x" => "❌",
115
+ "hotsprings" => "♨️",
116
+ "exclamation" => "❗️",
117
+ "heavy_exclamation_mark" => "❗️",
118
+ "grey_exclamation" => "❕",
119
+ "question" => "❓",
120
+ "grey_question" => "❔",
121
+ "bangbang" => "‼️",
122
+ "interrobang" => "⁉️",
123
+ "part_alternation_mark" => "〽️",
124
+ "warning" => "⚠️",
125
+ "recycle" => "♻️",
126
+ "white_check_mark" => "✅",
127
+ "sparkle" => "❇️",
128
+ "eight_spoked_asterisk" => "✳️",
129
+ "negative_squared_cross_mark" => "❎",
130
+ "m" => "Ⓜ️",
131
+ "wheelchair" => "♿️",
132
+ "information_source" => "ℹ️",
133
+ "heavy_plus_sign" => "➕",
134
+ "heavy_minus_sign" => "➖",
135
+ "heavy_division_sign" => "➗",
136
+ "heavy_multiplication_x" => "✖️",
137
+ "tm" => "™️",
138
+ "copyright" => "©️",
139
+ "registered" => "®️",
140
+ "wavy_dash" => "〰️",
141
+ "curly_loop" => "➰",
142
+ "loop" => "➿",
143
+ "heavy_check_mark" => "✔️",
144
+ "ballot_box_with_check" => "☑️",
145
+ "white_circle" => "⚪️",
146
+ "black_circle" => "⚫️",
147
+ "black_small_square" => "▪️",
148
+ "white_small_square" => "▫️",
149
+ "black_medium_small_square" => "◾️",
150
+ "white_medium_small_square" => "◽️",
151
+ "black_medium_square" => "◼️",
152
+ "white_medium_square" => "◻️",
153
+ "black_large_square" => "⬛️",
154
+ "white_large_square" => "⬜️",
155
+ "black_joker" => "🃏",
156
+ "mahjong" => "🀄️"
157
+ }.freeze
158
+ end
159
+ end
160
+ end
161
+ end
@@ -0,0 +1,36 @@
1
+ module MotionMarkdownItPlugins
2
+ module EmojiPlugin
3
+ module Data
4
+ module Shortcuts
5
+ EMOJIES_DEF_SHORTCUTS = {
6
+ 'angry' => [ '>:(', '>:-(' ],
7
+ 'blush' => [ ':")', ':-")' ],
8
+ 'broken_heart' => [ '</3', '<\\3' ],
9
+ # :\ and :-\ not used because of conflict with markdown escaping
10
+ 'confused' => [ ':/', ':-/' ], # twemoji shows question
11
+ 'cry' => [ ":'(", ":'-(", ':,(', ':,-(' ],
12
+ 'frowning' => [ ':(', ':-(' ],
13
+ 'heart' => [ '<3' ],
14
+ 'imp' => [ ']:(', ']:-(' ],
15
+ 'innocent' => [ 'o:)', 'O:)', 'o:-)', 'O:-)', '0:)', '0:-)' ],
16
+ 'joy' => [ ":')", ":'-)", ':,)', ':,-)', ":'D", ":'-D", ':,D', ':,-D' ],
17
+ 'kissing' => [ ':*', ':-*' ],
18
+ 'laughing' => [ 'x-)', 'X-)' ],
19
+ 'neutral_face' => [ ':|', ':-|' ],
20
+ 'open_mouth' => [ ':o', ':-o', ':O', ':-O' ],
21
+ 'rage' => [ ':@', ':-@' ],
22
+ 'smile' => [ ':D', ':-D' ],
23
+ 'smiley' => [ ':)', ':-)' ],
24
+ 'smiling_imp' => [ ']:)', ']:-)' ],
25
+ 'sob' => [ ":,'(", ":,'-(", ';(', ';-(' ],
26
+ 'stuck_out_tongue' => [ ':P', ':-P' ],
27
+ 'sunglasses' => [ '8-)', 'B-)' ],
28
+ 'sweat' => [ ',:(', ',:-(' ],
29
+ 'sweat_smile' => [ ',:)', ',:-)' ],
30
+ 'unamused' => [ ':s', ':-S', ':z', ':-Z', ':$', ':-$' ],
31
+ 'wink' => [ ';)', ';-)' ]
32
+ }.freeze
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,72 @@
1
+ module MotionMarkdownItPlugins
2
+ module EmojiPlugin
3
+ module NormalizeOpts
4
+ #------------------------------------------------------------------------------
5
+ def quoteRE(str)
6
+ str.gsub(/([.?*+^$\[\]\\(){}|-])/, '\\\\\1')
7
+ end
8
+
9
+ #------------------------------------------------------------------------------
10
+ def normalize_opts(options)
11
+ emojies = stringify_keys(options[:defs])
12
+ shortcuts = stringify_keys(options[:shortcuts])
13
+
14
+ # Filter emojies by whitelist, if needed
15
+ if options[:enabled].length > 0
16
+ emojies = emojies.keys.reduce({}) do |acc, key|
17
+ if options[:enabled].include?(key)
18
+ acc[key] = emojies[key]
19
+ end
20
+
21
+ acc
22
+ end
23
+ end
24
+
25
+ # Flatten shortcuts to simple object: { alias: emoji_name }
26
+ shortcuts = shortcuts.keys.reduce({}) do |acc, key|
27
+ # Skip aliases for filtered emojies, to reduce regexp
28
+ next acc if !emojies[key]
29
+
30
+ if shortcuts[key].is_a?(Array)
31
+ shortcuts[key].each do |alias_value|
32
+ acc[alias_value] = key
33
+ end
34
+ next acc
35
+ end
36
+
37
+ acc[shortcuts[key]] = key
38
+ acc
39
+ end
40
+
41
+ # Compile regexp
42
+ names = emojies.keys
43
+ .map { |name| ':' + name + ':' }
44
+ .concat(shortcuts.keys)
45
+ .sort
46
+ .reverse
47
+ .map { |name| quoteRE(name) }
48
+ .join('|')
49
+
50
+ scanRE = Regexp.new(names)
51
+ replaceRE = Regexp.new(names)
52
+
53
+ return {
54
+ defs: emojies,
55
+ shortcuts: shortcuts,
56
+ scanRE: scanRE,
57
+ replaceRE: replaceRE
58
+ }
59
+ end
60
+
61
+ private
62
+
63
+ # convert keys from symbols into strings
64
+ #------------------------------------------------------------------------------
65
+ def stringify_keys(symbol_hash)
66
+ result = {}
67
+ symbol_hash.each_key { |key| result[key.to_s] = symbol_hash[key] }
68
+ result
69
+ end
70
+ end
71
+ end
72
+ end
@@ -0,0 +1,9 @@
1
+ module MotionMarkdownItPlugins
2
+ module EmojiPlugin
3
+ module Render
4
+ def emoji_html(tokens, idx) #, options, env
5
+ return tokens[idx].content
6
+ end
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,101 @@
1
+ # Emojies & shortcuts replacement logic.
2
+ #
3
+ # Note: In theory, it could be faster to parse :smile: in inline chain and
4
+ # leave only shortcuts here. But, who care...
5
+ #------------------------------------------------------------------------------
6
+ module MotionMarkdownItPlugins
7
+ module EmojiPlugin
8
+ module Replace
9
+ Z_RE_SRC = UCMicro::Categories::Z::REGEX.source
10
+ P_RE_SRC = UCMicro::Categories::P::REGEX.source
11
+ CC_RE_SRC = UCMicro::Categories::Cc::REGEX.source
12
+ ZPCC_RE = Regexp.new([Z_RE_SRC, P_RE_SRC, CC_RE_SRC].join('|'))
13
+
14
+ #------------------------------------------------------------------------------
15
+ def create_rule(md, emojies, shortcuts, scanRE, replaceRE)
16
+ @emojies = emojies
17
+ @shortcuts = shortcuts
18
+ @scanRE = scanRE
19
+ @replaceRE = replaceRE
20
+
21
+ return lambda { |state| emoji_replace(state) }
22
+ end
23
+
24
+ #------------------------------------------------------------------------------
25
+ def splitTextToken(text, level, token_class)
26
+ last_pos = 0
27
+ nodes = []
28
+
29
+ text.gsub(@replaceRE) do |match|
30
+ match_data = Regexp.last_match
31
+ offset = match_data.offset(0)[0]
32
+ src = match_data.string
33
+
34
+ # Validate emoji name
35
+ if @shortcuts.include?(match)
36
+ # replace shortcut with full name
37
+ emoji_name = @shortcuts[match]
38
+
39
+ # Don't allow letters before any shortcut (as in no ":/" in http://)
40
+ next if offset > 0 && !(ZPCC_RE =~ src[offset - 1])
41
+
42
+ # Don't allow letters after any shortcut
43
+ next if (offset + match.length < src.length) && !(ZPCC_RE =~ src[offset + match.length])
44
+ else
45
+ emoji_name = match.slice(1...-1)
46
+ end
47
+
48
+ # Add new tokens to pending list
49
+ if offset > last_pos
50
+ token = token_class.new('text', '', 0)
51
+ token.content = text.slice(last_pos...offset)
52
+ nodes.push(token)
53
+ end
54
+
55
+ token = token_class.new('emoji', '', 0)
56
+ token.markup = emoji_name
57
+ token.content = @emojies[emoji_name]
58
+ nodes.push(token)
59
+
60
+ last_pos = offset + match.length
61
+ end
62
+
63
+ if last_pos < text.length
64
+ token = token_class.new('text', '', 0)
65
+ token.content = text.slice(last_pos..-1)
66
+ nodes.push(token)
67
+ end
68
+
69
+ return nodes
70
+ end
71
+
72
+ #------------------------------------------------------------------------------
73
+ def emoji_replace(state)
74
+ blockTokens = state.tokens
75
+ autolinkLevel = 0
76
+
77
+ 0.upto(blockTokens.length - 1) do |j|
78
+ next if blockTokens[j].type != 'inline'
79
+ tokens = blockTokens[j].children
80
+
81
+ # We scan from the end, to keep position when new tags added.
82
+ # Use reversed logic in links start/end match
83
+ (tokens.length - 1).downto(0) do |i|
84
+ token = tokens[i]
85
+
86
+ if token.type == 'link_open' || token.type == 'link_close'
87
+ autolinkLevel -= token.nesting if token.info == 'auto'
88
+ end
89
+
90
+ if token.type == 'text' && autolinkLevel == 0 && @scanRE =~ token.content
91
+ # replace current node
92
+ blockTokens[j].children = tokens = arrayReplaceAt(
93
+ tokens, i, splitTextToken(token.content, token.level, MarkdownIt::Token)
94
+ )
95
+ end
96
+ end
97
+ end
98
+ end
99
+ end
100
+ end
101
+ end
@@ -1,5 +1,5 @@
1
1
  module MotionMarkdownItPlugins
2
2
 
3
- VERSION = '8.4.1'
3
+ VERSION = '8.4.2'
4
4
 
5
5
  end
@@ -0,0 +1,115 @@
1
+ fixture_dir = fixture_path('emoji/fixtures')
2
+
3
+ #------------------------------------------------------------------------------
4
+ describe 'markdown-it-emoji' do
5
+ describe 'default' do
6
+ md = MarkdownIt::Parser.new
7
+ md.use(MotionMarkdownItPlugins::Emoji)
8
+
9
+ generate(File.join(fixture_dir, 'default/emoji.txt'), md)
10
+ generate(File.join(fixture_dir, 'default/aliases.txt'), md)
11
+ generate(File.join(fixture_dir, 'full.txt'), md)
12
+ end
13
+
14
+ describe 'options' do
15
+ md = MarkdownIt::Parser.new
16
+ md.use(MotionMarkdownItPlugins::Emoji, {
17
+ defs: {
18
+ one: '!!!one!!!',
19
+ fifty: '!!50!!'
20
+ },
21
+ shortcuts: {
22
+ fifty: [ ':50', '|50' ],
23
+ one: ':uno'
24
+ }
25
+ })
26
+
27
+ generate(File.join(fixture_dir, 'options.txt'), md)
28
+ end
29
+
30
+ describe 'whitelist' do
31
+ md = MarkdownIt::Parser.new
32
+ md.use(MotionMarkdownItPlugins::Emoji, { enabled: [ 'smile', 'grin' ] })
33
+
34
+ generate(File.join(fixture_dir, 'whitelist.txt'), md)
35
+ end
36
+
37
+ describe 'autolinks' do
38
+ md = MarkdownIt::Parser.new({ linkify: true })
39
+ md.use(MotionMarkdownItPlugins::Emoji)
40
+
41
+ generate(File.join(fixture_dir, 'autolinks.txt'), md)
42
+ end
43
+ end
44
+
45
+ describe 'markdown-it-emoji-light' do
46
+ describe 'default' do
47
+ md = MarkdownIt::Parser.new
48
+ md.use(MotionMarkdownItPlugins::EmojiLight)
49
+
50
+ generate(File.join(fixture_dir, 'default/emoji.txt'), md)
51
+ generate(File.join(fixture_dir, 'default/aliases.txt'), md)
52
+ generate(File.join(fixture_dir, 'light.txt'), md)
53
+ end
54
+
55
+ describe 'options' do
56
+ md = MarkdownIt::Parser.new
57
+ md.use(MotionMarkdownItPlugins::EmojiLight, {
58
+ defs: {
59
+ one: '!!!one!!!',
60
+ fifty: '!!50!!'
61
+ },
62
+ shortcuts: {
63
+ fifty: [ ':50', '|50' ],
64
+ one: ':uno'
65
+ }
66
+ })
67
+
68
+ generate(File.join(fixture_dir, 'options.txt'), md)
69
+ end
70
+
71
+ describe 'whitelist' do
72
+ md = MarkdownIt::Parser.new
73
+ md.use(MotionMarkdownItPlugins::EmojiLight, { enabled: [ 'smile', 'grin' ] })
74
+
75
+ generate(File.join(fixture_dir, 'whitelist.txt'), md)
76
+ end
77
+
78
+ describe 'autolinks' do
79
+ md = MarkdownIt::Parser.new({ linkify: true })
80
+ md.use(MotionMarkdownItPlugins::EmojiLight)
81
+
82
+ generate(File.join(fixture_dir, 'autolinks.txt'), md)
83
+ end
84
+ end
85
+
86
+ describe 'integrity' do
87
+ it 'all shortcuts should exist' do
88
+ MotionMarkdownItPlugins::EmojiPlugin::Data::Shortcuts::EMOJIES_DEF_SHORTCUTS.keys.each do |name|
89
+ expect(MotionMarkdownItPlugins::EmojiPlugin::Data::Full::EMOJIES_DEF_FULL[name]).not_to eq nil
90
+ end
91
+ end
92
+
93
+ it 'no chars with "uXXXX" names allowed' do
94
+ MotionMarkdownItPlugins::EmojiPlugin::Data::Full::EMOJIES_DEF_FULL.keys.each do |name|
95
+ expect(/^u[0-9a-b]{4,}$/ =~ name).to eq nil
96
+ end
97
+ end
98
+
99
+ it 'all light chars should exist' do
100
+
101
+ visible = File.read(File.join(fixture_dir, 'visible.txt'))
102
+
103
+ available = MotionMarkdownItPlugins::EmojiPlugin::Data::Light::EMOJIES_DEF_LIGHT.keys.map do |k|
104
+ MotionMarkdownItPlugins::EmojiPlugin::Data::Light::EMOJIES_DEF_LIGHT[k].gsub(/\uFE0F/, '')
105
+ end
106
+
107
+ missed = ''
108
+
109
+ visible.each_char do |ch|
110
+ missed << ch unless available.include?(ch)
111
+ end
112
+
113
+ expect(missed.length).to eq 0
114
+ end
115
+ end