Coverage for pyquickhelper/sphinxext/sphinx_sharenet_extension.py: 99%
88 statements
« prev ^ index » next coverage.py v7.2.7, created at 2023-06-03 02:21 +0200
« prev ^ index » next coverage.py v7.2.7, created at 2023-06-03 02:21 +0200
1# -*- coding: utf-8 -*-
2"""
3@file
4@brief Defines a sphinx extension to add button to share a page
5"""
6import sphinx
7from docutils import nodes
8from docutils.parsers.rst import Directive, directives
11class sharenet_node(nodes.Structural, nodes.Element):
13 """
14 Defines *sharenet* node.
15 """
16 pass
19class ShareNetDirective(Directive):
20 """
21 Adds buttons to share a page. It can be done by
22 adding::
24 .. sharenet::
25 :facebook: 1
26 :linkedin: 2
27 :twitter: 3
28 :head: False
30 Which gives:
32 .. sharenet::
33 :facebook: 1
34 :linkedin: 2
35 :twitter: 3
36 :head: False
38 The integer indicates the order in which they need to be displayed.
39 It is optional. The option ``:head: False`` specifies the javascript
40 part is added to the html body and not the header.
41 The header can be overwritten by other custom commands.
42 """
43 available_networks = {'facebook', 'linkedin', 'twitter'}
44 required_arguments = 0
45 optional_arguments = 0
46 final_argument_whitespace = True
47 option_spec = {'facebook': directives.unchanged,
48 'linkedin': directives.unchanged,
49 'twitter': directives.unchanged,
50 'size': directives.unchanged,
51 'head': directives.unchanged,
52 }
53 has_content = True
54 sharenet_class = sharenet_node
56 def run(self):
57 """
58 Runs the directve.
60 @return a list of nodes
61 """
62 def options_to_int(s):
63 "local function"
64 if s is None:
65 return -1
66 elif len(s) == 0:
67 return 1e6
68 else:
69 return int(s)
71 # list of networks
72 bool_set = (True, 1, "True", "1", "true")
73 p = [(options_to_int(self.options.get(net, None)), net)
74 for net in ShareNetDirective.available_networks]
75 p.sort()
76 p = [a[1] for a in p if a[0] >= 0]
78 # build node
79 node = self.__class__.sharenet_class(networks=p,
80 size=self.options.get('size', 20),
81 inhead=self.options.get('head', True) in bool_set)
82 node['classes'] += ["sharenet"]
83 ns = [node]
84 return ns
87def sharenet_role(role, rawtext, text, lineno, inliner,
88 options=None, content=None):
89 """
90 Defines custom roles *sharenet*. The following instructions defines
91 buttons of size 20 (:sharenet:`facebook-linkedin-twitter-20-body`)::
93 :sharenet:`facebook-linkedin-twitter-20-body`
95 The last term indicates the javascript part will be included in the
96 HTML body instead of the head as this part might be tweaked by other
97 custom commands.
99 :param role: The role name used in the document.
100 :param rawtext: The entire markup snippet, with role.
101 :param text: The text marked with the role.
102 :param lineno: The line number where rawtext appears in the input.
103 :param inliner: The inliner instance that called us.
104 :param options: Directive options for customization.
105 :param content: The directive content for customization.
106 """
107 if options is None:
108 options = {}
109 if content is None:
110 content = []
111 spl = [a.strip().lower() for a in text.split('-')]
112 networks = []
113 size = 20
114 inhead = True
115 for v in spl:
116 if v in ShareNetDirective.available_networks:
117 networks.append(v)
118 elif v == "body":
119 inhead = False
120 elif v == "head":
121 inhead = True
122 else:
123 try:
124 i = int(v)
125 size = i
126 except ValueError: # pragma: no cover
127 msg = inliner.reporter.error(
128 f"unable to interpret {v} from {rawtext}", line=lineno)
129 prb = inliner.problematic(rawtext, rawtext, msg)
130 return [prb], [msg]
132 if len(networks) == 0:
133 msg = inliner.reporter.error( # pragma: no cover
134 f"no specified network from {rawtext}", line=lineno)
135 prb = inliner.problematic(rawtext, rawtext, msg) # pragma: no cover
136 return [prb], [msg] # pragma: no cover
138 # ref = inliner.document.settings.rfc_base_url + inliner.rfc_url % rfcnum
139 # set_classes(options)
140 node = sharenet_node(networks=networks, size=size, inhead=inhead)
141 node['classes'] += ["sharenet"]
142 return [node], []
145def visit_sharenet_node(self, node):
146 """
147 what to do when visiting a node sharenet
148 the function should have different behaviour,
149 depending on the format, or the setup should
150 specify a different function for each.
151 """
152 pass
155def depart_sharenet_node_html(self, node):
156 """
157 what to do when leaving a node sharenet
158 the function should have different behaviour,
159 depending on the format, or the setup should
160 specify a different function for each.
162 It does only html for the time being.
163 """
164 if node.hasattr("networks"):
165 networks = node["networks"]
166 size = node["size"]
167 inhead = node["inhead"]
168 if len(networks) > 0:
170 script = """
171 <script>
172 function share_url(share) {
173 var url = share + encodeURIComponent(window.location.href);
174 window.location.href = url;
175 }
177 function share_icon(divid, text) {
178 var canvas = document.getElementById(divid);
179 var context = canvas.getContext('2d');
180 var centerX = canvas.width / 2;
181 var centerY = canvas.height / 2;
182 var radius = centerX;
184 context.beginPath();
185 context.arc(centerX, centerY, radius, 0, 2 * Math.PI, false);
186 context.fillStyle = '#444444';
187 context.fill();
188 context.font = '' + (centerX*4/3) + 'pt Calibri';
189 context.textAlign = 'center';
190 context.fillStyle = '#FFFFFF';
191 context.fillText(text, centerX, centerY+centerY*16/30);
192 }
193 </script>
194 """.replace(" ", "")
196 mapping = dict(facebook="f", linkedin="in", twitter="t")
197 urls = dict(facebook="https://www.facebook.com/sharer/sharer.php?u=",
198 twitter="https://twitter.com/home?status=",
199 linkedin="https://www.linkedin.com/shareArticle?mini=true&title=&summary=&source=&url=")
200 link = """<a href="#" onclick="share_url('{0}');return false;"><canvas height="{2}" id="canvas-{1}" width="{2}"/>""" + \
201 """</a><script>share_icon('canvas-{1}', '{1}');</script>"""
203 rows = []
204 for key in networks:
205 text = mapping[key]
206 ld = link.format(urls[key], text, size)
207 rows.append(ld)
209 if len(rows) > 0:
210 if inhead:
211 self.head.append(script)
212 else:
213 self.body.append(script)
214 self.body.extend(rows)
217def depart_sharenet_node(self, node):
218 """
219 what to do when leaving a node sharenet
220 the function should have different behaviour,
221 depending on the format, or the setup should
222 specify a different function for each.
224 It does only html for the time being.
225 """
226 pass # pragma: no cover
229def visit_sharenet_node_rst(self, node):
230 """
231 what to do when visiting a node sharenet
232 the function should have different behaviour,
233 depending on the format, or the setup should
234 specify a different function for each.
235 """
236 inside = "-".join(node['networks'])
237 inside = f"{inside}-{node['size']}-{'head' if node['inhead'] else 'body'}"
238 self.add_text(f":sharenet:`{inside}`")
241def depart_sharenet_node_rst(self, node):
242 """
243 what to do when leaving a node sharenet
244 the function should have different behaviour,
245 depending on the format, or the setup should
246 specify a different function for each.
247 """
248 pass
251def setup(app):
252 """
253 setup for ``sharenet`` (sphinx)
254 """
255 if hasattr(app, "add_mapping"):
256 app.add_mapping('sharenet', sharenet_node)
258 app.add_node(sharenet_node,
259 html=(visit_sharenet_node, depart_sharenet_node_html),
260 epub=(visit_sharenet_node, depart_sharenet_node_html),
261 elatex=(visit_sharenet_node, depart_sharenet_node),
262 latex=(visit_sharenet_node, depart_sharenet_node),
263 rst=(visit_sharenet_node_rst, depart_sharenet_node_rst),
264 md=(visit_sharenet_node_rst, depart_sharenet_node_rst),
265 text=(visit_sharenet_node, depart_sharenet_node))
267 app.add_directive('sharenet', ShareNetDirective)
268 app.add_role('sharenet', sharenet_role)
269 return {'version': sphinx.__display_version__, 'parallel_read_safe': True}