Hide keyboard shortcuts

Hot-keys on this page

r m x p   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

1# -*- coding: utf-8 -*- 

2""" 

3@file 

4@brief Helpers around JSON 

5""" 

6import uuid 

7import os 

8import shutil 

9import urllib.request as liburl 

10import urllib.error as liberror 

11from IPython.display import display_html, display_javascript 

12 

13 

14class UrlNotFoundError(Exception): 

15 """ 

16 Raised when a url does not exist. 

17 """ 

18 

19 def __init__(self, url, code): 

20 Exception.__init__( 

21 self, "Url not found: returned code={0} for '{1}'".format(code, url)) 

22 

23 

24class JavascriptScriptError(ValueError): 

25 """ 

26 Raised when the class does not find what it expects. 

27 """ 

28 pass 

29 

30 

31def check_url(url): 

32 "Checks urls." 

33 try: 

34 liburl.urlopen(url) 

35 return True 

36 except liberror.HTTPError as e: 

37 raise UrlNotFoundError(url, e.code) from e 

38 except liberror.URLError as e: 

39 raise UrlNotFoundError(url, e.reason) from e 

40 except Exception as e: 

41 raise Exception("Issue with url '{0}'".format(url)) from e 

42 

43 

44class RenderJSRaw: 

45 """ 

46 Adds :epkg:`javascript` into a noteboook. 

47 """ 

48 

49 def __init__(self, script, width="100%", height="100%", divid=None, css=None, 

50 libs=None, style=None, only_html=False, div_class=None, check_urls=True, 

51 local=False): 

52 """ 

53 @param script (str) script 

54 @param width (str) width 

55 @param height (str) height 

56 @param style (str) style (added in ``<style>...</style>``) 

57 @param divid (str|None) id of the div 

58 @param css (list) list of css 

59 @param libs (list) list of dependencies 

60 @param only_html (bool) use only function 

61 `display_html <http://ipython.readthedocs.io/en/stable/api/generated/IPython.display.html? 

62 highlight=display_html#IPython.display.display_html>`_ 

63 and not `display_javascript 

64 <http://ipython.readthedocs.io/en/stable/api/generated/IPython.display.html? 

65 highlight=display_html#IPython.display.display_javascript>`_ to add 

66 javascript to the page. 

67 @param div_class (str) class of the section ``div`` which will host the results 

68 of the javascript 

69 @param check_urls (bool) by default, check url exists 

70 @param local (bool|False) use local javascript files 

71 """ 

72 self.script = script 

73 self.uuid = divid if divid else "M" + \ 

74 str(uuid.uuid4()).replace("-", "") 

75 if style is None: 

76 style = '' 

77 if width is not None and 'width' not in style: 

78 style += "width:{0};".format(width) 

79 if height is not None and 'height' not in style: 

80 style += "height:{0};".format(height) 

81 if not style: 

82 style = None 

83 else: 

84 if width is not None and 'width' not in style: 

85 style += "width:{0};".format(width) 

86 if height is not None and 'height' not in style: 

87 style += "height:{0};".format(height) 

88 self.style = style 

89 self.only_html = only_html 

90 self.div_class = div_class 

91 if "__ID__" not in script: 

92 raise JavascriptScriptError( 

93 "The sript does not contain any string __ID__. It is replaced by the ID value in script:\n{0}".format(script)) 

94 self.local = local 

95 self.css, self.libs = self._copy_local(css, libs, local) 

96 if check_urls and not local: 

97 if self.css is not None: 

98 for c in self.css: 

99 check_url(c) 

100 if self.libs is not None: 

101 for lib in self.libs: 

102 if isinstance(lib, dict): 

103 check_url(lib['path']) 

104 else: 

105 check_url(lib) 

106 

107 def _copy_local(self, css, libs, local): 

108 """ 

109 If *self.local*, copies javascript dependencies in the local folder. 

110 

111 @param css list of css 

112 @param libs list of libraries 

113 @param local boolean or new location 

114 @return tuple (css, libs) 

115 """ 

116 if not self.local: 

117 return css, libs 

118 to_copy = [] 

119 if css: 

120 to_copy.extend(css) 

121 if libs: 

122 for js in libs: 

123 if isinstance(js, dict): 

124 to_copy.append(js['path']) 

125 else: 

126 to_copy.append(js) 

127 

128 for js in to_copy: 

129 if not os.path.exists(js): 

130 raise FileNotFoundError("Unable to find '{0}'".format(js)) 

131 dest = local if isinstance(local, str) else os.getcwd() 

132 shutil.copy(js, dest) 

133 

134 if css: 

135 css = [os.path.split(c)[-1] for c in css] 

136 if libs: 

137 def proc(d): 

138 "proc" 

139 if isinstance(d, dict): 

140 d = d.copy() 

141 d['path'] = os.path.split(d['path'])[-1] 

142 return d 

143 else: 

144 return os.path.split(d)[-1] 

145 libs = [proc(c) for c in libs] 

146 return css, libs 

147 

148 def generate_html(self): 

149 """ 

150 Overloads method 

151 `_ipython_display_ <http://ipython.readthedocs.io/en/stable/config/integrating.html?highlight=Integrating%20>`_. 

152 

153 @return `HTML <http://ipython.readthedocs.io/en/stable/api/generated/IPython.display.html#IPython.display.HTML>`_ text, 

154 `Javascript <http://ipython.readthedocs.io/en/stable/api/generated/IPython.display.html#IPython.display.Javascript>`_ text 

155 """ 

156 if self.style: 

157 style = ' style="{0}"'.format(self.style) 

158 else: 

159 style = "" 

160 if self.div_class: 

161 divcl = ' class="{0}"'.format(self.div_class) 

162 else: 

163 divcl = "" 

164 if self.css: 

165 css = "".join( 

166 '<link rel="stylesheet" href="{0}" type="text/css" />'.format(c) for c in self.css) 

167 ht = '<div id="{uuid}-css">{css}<div{divcl} id="{uuid}"{style}></div></div>'.format( 

168 uuid=self.uuid, css=css, style=style, divcl=divcl) 

169 else: 

170 ht = '<div id="{uuid}-cont"><div{divcl} id="{uuid}"{style}></div></div>'.format( 

171 uuid=self.uuid, style=style, divcl=divcl) 

172 

173 script = self.script.replace("__ID__", self.uuid) 

174 if self.libs: 

175 names = [] 

176 paths = [] 

177 shims = {} 

178 args = [] 

179 exports = [] 

180 for lib in self.libs: 

181 if isinstance(lib, dict): 

182 name = lib.get("name", None) 

183 if "path" in lib: 

184 p = lib["path"] 

185 if name is None: 

186 name = ".".join((p.split("/")[-1]).split(".")[:-1]) 

187 path = ".".join(p.split(".")[:-1]) 

188 paths.append((name, path)) 

189 else: 

190 raise KeyError( 

191 "unable to find 'path' in {0}".format(lib)) 

192 names.append(name) 

193 args.append(name) 

194 if "exports" in lib: 

195 if name not in shims: 

196 shims[name] = {} 

197 shims[name]["exports"] = lib["exports"] 

198 if isinstance(lib["exports"], list): 

199 exports.extend(lib["exports"]) 

200 else: 

201 exports.append(lib["exports"]) 

202 if "deps" in lib: 

203 if name not in shims: 

204 shims[name] = {} 

205 shims[name]["deps"] = lib["deps"] 

206 else: 

207 names.append(lib) 

208 if len(names) == 0: 

209 raise ValueError( 

210 "names is empty.\nlibs={0}\npaths={1}\nshims={2}\nexports={3}".format( 

211 self.libs, paths, shims, exports)) 

212 require = ",".join("'{0}'".format(na) for na in names) 

213 

214 config = ["require.config({"] 

215 if len(paths) > 0: 

216 config.append("paths:{") 

217 for name, path in paths: 

218 config.append("'{0}':'{1}',".format(name, path)) 

219 config.append("},") 

220 if len(shims) > 0: 

221 config.append("shim:{") 

222 

223 def vd(d): 

224 "vd" 

225 rows = [] 

226 for k, v in sorted(d.items()): 

227 rows.append("'{0}':{1}".format( 

228 k, v if isinstance(v, list) else "'{0}'".format(v))) 

229 return "{%s}" % ",".join(rows) 

230 for k, v in sorted(shims.items()): 

231 config.append("'{0}':{1},".format(k, vd(v))) 

232 config.append("},") 

233 config.append("});") 

234 if len(config) > 2: 

235 prefix = "\n".join(config) + "\n" 

236 else: 

237 prefix = "" 

238 js = prefix + \ 

239 """\nrequire([%s], function(%s) { %s });\n""" % ( 

240 require, ",".join(args), script) 

241 else: 

242 js = script 

243 if self.only_html: 

244 ht += "\n<script>\n%s\n</script>" % js 

245 return ht, None 

246 return ht, js 

247 

248 

249class RenderJSObj(RenderJSRaw): 

250 """ 

251 Renders JS using :epkg:`javascript`. 

252 """ 

253 

254 def _ipython_display_(self): 

255 """ 

256 overloads method 

257 `_ipython_display_ <http://ipython.readthedocs.io/en/stable/config/integrating.html?highlight=Integrating%20>`_. 

258 """ 

259 ht, js = self.generate_html() 

260 if js is None: 

261 display_html(ht, raw=True) 

262 else: 

263 display_html(ht, raw=True) 

264 display_javascript(js, raw=True) 

265 

266 

267class RenderJS(RenderJSRaw): 

268 """ 

269 Renders :epkg:`javascript`, only outputs :epkg:`HTML`. 

270 """ 

271 

272 def _repr_html_(self): 

273 """ 

274 Overloads method *_repr_html_*. 

275 """ 

276 ht, js = self.generate_html() 

277 if js is not None: 

278 ht += "\n<script>\n{0}\n</script>\n".format(js) 

279 return ht