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

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 

9 

10 

11class sharenet_node(nodes.Structural, nodes.Element): 

12 

13 """ 

14 Defines *sharenet* node. 

15 """ 

16 pass 

17 

18 

19class ShareNetDirective(Directive): 

20 """ 

21 Adds buttons to share a page. It can be done by 

22 adding:: 

23 

24 .. sharenet:: 

25 :facebook: 1 

26 :linkedin: 2 

27 :twitter: 3 

28 :head: False 

29 

30 Which gives: 

31 

32 .. sharenet:: 

33 :facebook: 1 

34 :linkedin: 2 

35 :twitter: 3 

36 :head: False 

37 

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 

55 

56 def run(self): 

57 """ 

58 Runs the directve. 

59 

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) 

70 

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] 

77 

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 

85 

86 

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`):: 

92 

93 :sharenet:`facebook-linkedin-twitter-20-body` 

94 

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. 

98 

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] 

131 

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 

137 

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], [] 

143 

144 

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 

153 

154 

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. 

161 

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: 

169 

170 script = """ 

171 <script> 

172 function share_url(share) { 

173 var url = share + encodeURIComponent(window.location.href); 

174 window.location.href = url; 

175 } 

176 

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; 

183 

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(" ", "") 

195 

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&amp;title=&amp;summary=&amp;source=&amp;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>""" 

202 

203 rows = [] 

204 for key in networks: 

205 text = mapping[key] 

206 ld = link.format(urls[key], text, size) 

207 rows.append(ld) 

208 

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) 

215 

216 

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. 

223 

224 It does only html for the time being. 

225 """ 

226 pass # pragma: no cover 

227 

228 

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}`") 

239 

240 

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 

249 

250 

251def setup(app): 

252 """ 

253 setup for ``sharenet`` (sphinx) 

254 """ 

255 if hasattr(app, "add_mapping"): 

256 app.add_mapping('sharenet', sharenet_node) 

257 

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)) 

266 

267 app.add_directive('sharenet', ShareNetDirective) 

268 app.add_role('sharenet', sharenet_role) 

269 return {'version': sphinx.__display_version__, 'parallel_read_safe': True}