[a-zA-Z0-9-]+(,[a-zA-Z0-9-]+)*)?)?)?;'
160 | # ^---------------------^^ ^ ^--------------^ ^ ^ ^ ^--------------^ ^ ^ ^
161 | # | +-------------------------------------+ | +------------------------------------------+ | |
162 | # | +----------------------------------------------+ |
163 | # +------------------------------------------------------------------------------------------+
164 | # This is the full regex we use. Only reason we have pieces above is to easily change the prefix to something custom
165 | icon_regex = ''.join([icon_regex_start, prefix, icon_regex_end])
166 |
167 | # Register the global one
168 | self.add_inline(md, 'iconfonts', IconFontsPattern, icon_regex, config)
169 |
170 | # Register each of the pairings
171 | for _prefix, _base in config['prefix_base_pairs'].items():
172 |
173 | _prefix_base = _prefix if _prefix[-1] != '-' else _prefix[:-1]
174 |
175 | icon_regex = ''.join([icon_regex_start, _prefix, icon_regex_end])
176 |
177 | self.add_inline(
178 | md, 'iconfonts_{}'.format(_prefix_base),
179 | IconFontsPattern, icon_regex,
180 | {'prefix': _prefix, 'base': _base})
181 |
182 |
183 | class IconFontsPattern(markdown.inlinepatterns.Pattern):
184 | def __init__(self, pattern, md, config):
185 | # Pass the patterna and markdown instance
186 | super(IconFontsPattern, self).__init__(pattern, md)
187 |
188 | self.config = config
189 |
190 | """ Return a element with the necessary classes"""
191 | def handleMatch(self, match):
192 |
193 | # The dictionary keys come from named capture groups in the regex
194 | match_dict = match.groupdict()
195 |
196 | # Create the element
197 | el = markdown.util.etree.Element("i")
198 |
199 | base = self.config['base']
200 | prefix = self.config['prefix']
201 |
202 | icon_class_name = match_dict.get("name")
203 |
204 | # Mods are modifier classes. The syntax in the markdown is:
205 | # "&icon-namehere:2x;" and with multiple "&icon-spinner:2x,spin;"
206 | mod_classes_string = ""
207 | if match_dict.get("mod"):
208 | # Make a string with each modifier like: "fa-2x fa-spin"
209 | mod_classes_string = ' '.join('{}{}'.format(prefix, c) for c in match_dict.get("mod").split(",") if c)
210 |
211 | # User mods are modifier classes that shouldn't be prefixed with
212 | # prefix. The syntax in the markdown is:
213 | # "&icon-namehere::red;" and with multiple "&icon-spinner::red,bold;"
214 | user_mod_classes_string = ""
215 | if match_dict.get("user_mod"):
216 | # Make a string with each modifier like "red bold"
217 | user_mod_classes_string = ' '.join(uc for uc in match_dict.get("user_mod").split(",") if uc)
218 |
219 | if prefix != '':
220 | icon_class = '{}{}'.format(prefix, icon_class_name)
221 | else:
222 | icon_class = icon_class_name
223 |
224 | # Add the icon classes to the element
225 | classes = '{} {} {} {}'.format(base, icon_class, mod_classes_string, user_mod_classes_string)
226 |
227 | # Clean up classes
228 | classes = classes.strip()
229 | classes = re.sub(r'\s{2,}', ' ', classes)
230 |
231 | el.set('class', classes)
232 | # This is for accessibility and text-to-speech browsers so they don't try to read it
233 | el.set('aria-hidden', 'true')
234 | return el
235 |
236 |
237 | # http://pythonhosted.org/Markdown/extensions/api.html#makeextension
238 | def makeExtension(*args, **kwargs):
239 | return IconFontsExtension(*args, **kwargs)
240 |
--------------------------------------------------------------------------------
/setup.cfg:
--------------------------------------------------------------------------------
1 | [metadata]
2 | description-file = README.md
3 |
--------------------------------------------------------------------------------
/setup.py:
--------------------------------------------------------------------------------
1 | from distutils.core import setup
2 |
3 | setup(
4 | name='markdown-icons',
5 | description='Easily display icon fonts in markdown.',
6 | py_modules=['iconfonts'],
7 | install_requires=['markdown', 'six'],
8 | author='Eric Eastwoord',
9 | author_email='contact@ericeastwood.com',
10 | url='https://github.com/MadLittleMods/markdown-icons',
11 | keywords='markdown, icons, fontawesome, bootstrap',
12 | classifiers=[
13 | 'Development Status :: 3 - Alpha',
14 | 'Environment :: Web Environment',
15 | 'Intended Audience :: Developers',
16 | 'License :: Other/Proprietary License',
17 | 'Operating System :: OS Independent',
18 | 'Programming Language :: Python :: 2',
19 | 'Programming Language :: Python :: 3',
20 | ],
21 | )
22 |
--------------------------------------------------------------------------------
/tests/unit-tests.py:
--------------------------------------------------------------------------------
1 | import unittest
2 |
3 | import os
4 | import markdown
5 |
6 |
7 | class TestMDI(unittest.TestCase):
8 | def test_vanilla(self):
9 | text = 'I love &icon-html5; and &icon-css3;.'
10 | expected = 'I love and .
'
11 |
12 | md = markdown.Markdown(extensions=['iconfonts'])
13 | converted_text = md.convert(text)
14 |
15 | self.assertEqual(converted_text, expected)
16 |
17 | def test_vanilla_mod(self):
18 | text = 'I also love &icon-spinner:spin; &icon-spinner:2x,spin;\n&icon-spinner:large,spin; Sorry we have to load...'
19 | expected = 'I also love \n Sorry we have to load...
'
20 |
21 | md = markdown.Markdown(extensions=['iconfonts'])
22 | converted_text = md.convert(text)
23 |
24 | self.assertEqual(converted_text, expected)
25 |
26 | def test_vanilla_user_mod(self):
27 | text = 'I also love &icon-spinner:spin:red; &icon-spinner:2x,spin:bold;\n&icon-spinner:large,spin; Sorry we have to load...'
28 | expected = 'I also love \n Sorry we have to load...
'
29 |
30 | md = markdown.Markdown(extensions=['iconfonts'])
31 | converted_text = md.convert(text)
32 |
33 | self.assertEqual(converted_text, expected)
34 |
35 | def test_prefix_base_pairs_setting(self):
36 | text = 'I also love &fa-spinner:2x,spin:red; &glyphicon-remove::bold;\n&fa-spinner:large,spin; Sorry we have to load...'
37 | expected = 'I also love \n Sorry we have to load...
'
38 |
39 | md = markdown.Markdown(
40 | extensions=['iconfonts'],
41 | extension_configs={
42 | 'iconfonts': {
43 | 'prefix': 'icon-',
44 | 'base': 'icon',
45 | 'prefix_base_pairs': {
46 | 'fa-': 'fa',
47 | 'glyphicon-': 'glyphicon',
48 | }
49 | }
50 | })
51 | converted_text = md.convert(text)
52 |
53 | self.assertEqual(converted_text, expected)
54 |
55 | def test_prefix_base_pairs_setting_with_normal_prefix_and_base_settings(self):
56 | text = 'I also love &fa-spinner:2x,spin:red; &glyphicon-remove::bold;\n&icon-spinner:large,spin; Sorry we have to load...'
57 | expected = 'I also love \n Sorry we have to load...
'
58 |
59 | md = markdown.Markdown(
60 | extensions=['iconfonts'],
61 | extension_configs={
62 | 'iconfonts': {
63 | 'prefix': 'icon-',
64 | 'base': 'icon',
65 | 'prefix_base_pairs': {
66 | 'fa-': 'fa',
67 | 'glyphicon-': 'glyphicon',
68 | }
69 | }
70 | })
71 | converted_text = md.convert(text)
72 |
73 | self.assertEqual(converted_text, expected)
74 |
75 | def test_custom_prefix(self):
76 | text = 'I love &mypref-html5; and &mypref-css3;.'
77 | expected = 'I love and .
'
78 |
79 | md = markdown.Markdown(extensions=['iconfonts(prefix=mypref-)'])
80 | converted_text = md.convert(text)
81 |
82 | self.assertEqual(converted_text, expected)
83 |
84 | def test_custom_prefix_mod(self):
85 | text = 'I also love &mypref-spinner:spin; &mypref-spinner:2x,spin;\n&mypref-spinner:large,spin; Sorry we have to load...'
86 | expected = 'I also love \n Sorry we have to load...
'
87 |
88 | md = markdown.Markdown(extensions=['iconfonts(prefix=mypref-)'])
89 | converted_text = md.convert(text)
90 |
91 | self.assertEqual(converted_text, expected)
92 |
93 | def test_base(self):
94 | text = 'I love &icon-html5; and &icon-css3;.'
95 | expected = 'I love and .
'
96 |
97 | md = markdown.Markdown(extensions=['iconfonts(base=icon)'])
98 | converted_text = md.convert(text)
99 |
100 | self.assertEqual(converted_text, expected)
101 |
102 | def test_complex(self):
103 | text = 'I love &fa-html5; and &fa-css3;.'
104 | expected = 'I love and .
'
105 |
106 | md = markdown.Markdown(extensions=['iconfonts(prefix=fa-, base=fa)'])
107 | converted_text = md.convert(text)
108 |
109 | self.assertEqual(converted_text, expected)
110 |
111 |
112 | """
113 | # Save to file
114 | BASE_DIR = os.path.realpath(os.path.dirname(__file__))
115 | with open(os.path.join(BASE_DIR, 'output.txt'), "w") as text_file:
116 | text_file.write(str(converted_text))
117 | """
118 |
119 |
120 | if __name__ == '__main__':
121 | unittest.main()
122 |
--------------------------------------------------------------------------------