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

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107

108

109

110

111

112

113

114

115

116

117

118

119

120

121

122

123

124

125

126

127

128

129

130

131

132

133

134

135

136

137

138

139

140

141

142

143

144

145

146

147

148

149

150

151

152

153

154

155

156

157

158

159

160

161

162

163

164

165

166

167

168

169

170

171

172

173

174

175

176

177

178

179

180

181

182

183

184

185

186

187

188

189

190

191

192

193

194

195

196

197

198

199

200

201

202

203

204

205

206

207

208

209

210

211

212

213

214

215

216

217

218

219

220

221

222

223

224

225

226

227

228

229

230

231

232

233

234

235

236

237

238

239

240

241

242

243

244

245

246

247

248

249

250

251

252

253

254

255

256

257

258

259

260

261

262

263

264

265

266

267

268

269

270

271

272

273

274

275

276

277

278

279

280

281

282

283

284

285

286

287

288

289

290

291

292

293

294

295

296

297

298

299

300

301

302

303

304

305

""" 

HTML Generator for JSmol 

 

This is all an evil iframe hack to get JSmol to display 3-d graphics 

while separating JSmol's j2s machinery from your actual web page. 

 

There are some caveats for how to load JSmol, in particular it cannot 

just load its code from a ``file://`` uri. To use a html file 

generated by this module, you need 

 

* A web server, 

 

* The JSmol directory tree must be served by your web server, 

 

* The output of :meth:`JSMolHtml.inner_html` or 

:meth:`JSMolHtml.outer_html` must be served by the same web server. 

 

See https://github.com/phetsims/molecule-polarity/issues/6 for a 

discussion of loading JSMol. 

""" 

 

import os 

import zipfile 

from six import StringIO 

 

from sage.env import SAGE_LOCAL 

from sage.structure.sage_object import SageObject 

from sage.misc.cachefunc import cached_method 

 

 

INNER_HTML_TEMPLATE = \ 

""" 

<html> 

<head> 

<style> 

* {{ 

margin: 0; 

padding: 0; 

overflow: hidden; 

}} 

body, html {{  

height: 100%; 

width: 100%; 

}} 

</style> 

<script type="text/javascript" src="{path_to_jsmol}/JSmol.min.js"></script> 

</head> 

<body> 

<script type="text/javascript"> 

var script = {script}; 

var Info = {{ 

width: '{width}', 

height: '{height}', 

debug: false, 

disableInitialConsole: true, // very slow when used with inline mesh 

color: '#3131ff', 

addSelectionOptions: false, 

use: 'HTML5', 

j2sPath: '{path_to_jsmol}/j2s', 

script: script, 

}}; 

var jmolApplet0 = Jmol.getApplet('jmolApplet0', Info); 

</script> 

</body> 

</html> 

""" 

 

 

IFRAME_TEMPLATE = \ 

""" 

<iframe srcdoc="{escaped_inner_html}"  

width="{width}" 

height="{height}" 

style="border: 0;"> 

</iframe> 

""" 

 

 

OUTER_HTML_TEMPLATE = \ 

""" 

<html> 

<head> 

<title>JSmol 3D Scene</title> 

</head> 

</body> 

{iframe} 

</body> 

</html> 

""".format(iframe=IFRAME_TEMPLATE) 

 

 

class JSMolHtml(SageObject): 

 

def __init__(self, jmol, path_to_jsmol=None, width='100%', height='100%'): 

""" 

INPUT: 

 

- ``jmol`` -- 3-d graphics or 

:class:`sage.repl.rich_output.output_graphics3d.OutputSceneJmol` 

instance. The 3-d scene to show. 

 

- ``path_to_jsmol`` -- string (optional, default is 

``'/nbextensions/jsmol'``). The path (relative or absolute) 

where ``JSmol.min.js`` is served on the web server.  

 

- ``width`` -- integer or string (optional, default: 

``'100%'``). The width of the JSmol applet using CSS 

dimensions. 

 

- ``height`` -- integer or string (optional, default: 

``'100%'``). The height of the JSmol applet using CSS 

dimensions. 

 

EXAMPLES:: 

 

sage: from sage.repl.display.jsmol_iframe import JSMolHtml 

sage: JSMolHtml(sphere(), width=500, height=300) 

JSmol Window 500x300 

""" 

from sage.repl.rich_output.output_graphics3d import OutputSceneJmol 

if not isinstance(jmol, OutputSceneJmol): 

jmol = jmol._rich_repr_jmol() 

self._jmol = jmol 

self._zip = zipfile.ZipFile(StringIO(self._jmol.scene_zip.get())) 

if path_to_jsmol is None: 

self._path = os.path.join('/', 'nbextensions', 'jsmol') 

else: 

self._path = path_to_jsmol 

self._width = width 

self._height = height 

 

@cached_method 

def script(self): 

r""" 

Return the JMol script file. 

 

This method extracts the Jmol script from the Jmol spt file (a 

zip archive) and inlines meshes. 

 

OUTPUT: 

 

String. 

 

EXAMPLES:: 

 

sage: from sage.repl.display.jsmol_iframe import JSMolHtml 

sage: from sage.repl.rich_output.output_graphics3d import OutputSceneJmol 

sage: jsmol = JSMolHtml(OutputSceneJmol.example(), width=500, height=300) 

sage: jsmol.script() 

'data "model list"\n10\nempt...aliasdisplay on;\n' 

""" 

script = [] 

with self._zip.open('SCRIPT') as SCRIPT: 

for line in SCRIPT: 

if line.startswith('pmesh'): 

command, obj, meshfile = line.split(' ', 3) 

assert command == 'pmesh' 

if meshfile not in ['dots\n', 'mesh\n']: 

assert meshfile.startswith('"') and meshfile.endswith('"\n') 

meshfile = meshfile[1:-2] # strip quotes 

script += [ 

'pmesh {0} inline "'.format(obj), 

self._zip.open(meshfile).read(), 

'"\n' 

] 

else: 

script += [line] 

else: 

script += [line] 

return ''.join(script) 

 

def js_script(self): 

r""" 

The :meth:`script` as Javascript string. 

 

Since the many shortcomings of Javascript include multi-line 

strings, this actually returns Javascript code to reassemble 

the script from a list of strings. 

 

OUTPUT: 

 

String. Javascript code that evaluates to :meth:`script` as 

Javascript string. 

 

EXAMPLES:: 

 

sage: from sage.repl.display.jsmol_iframe import JSMolHtml 

sage: from sage.repl.rich_output.output_graphics3d import OutputSceneJmol 

sage: jsmol = JSMolHtml(OutputSceneJmol.example(), width=500, height=300) 

sage: print(jsmol.js_script()) 

[ 

'data "model list"', 

... 

'isosurface fullylit; pmesh o* fullylit; set antialiasdisplay on;', 

].join('\n'); 

""" 

script = [r"["] 

for line in self.script().splitlines(): 

script += [r" '{0}',".format(line)] 

script += [r"].join('\n');"] 

return '\n'.join(script) 

 

def _repr_(self): 

""" 

Return as string representation 

 

OUTPUT: 

 

String. 

 

EXAMPLES:: 

 

sage: from sage.repl.display.jsmol_iframe import JSMolHtml 

sage: from sage.repl.rich_output.output_graphics3d import OutputSceneJmol 

sage: JSMolHtml(OutputSceneJmol.example(), width=500, height=300)._repr_() 

'JSmol Window 500x300' 

""" 

return 'JSmol Window {0}x{1}'.format(self._width, self._height) 

 

def inner_html(self): 

""" 

Return a HTML document containing a JSmol applet 

 

EXAMPLES:: 

 

sage: from sage.repl.display.jsmol_iframe import JSMolHtml 

sage: from sage.repl.rich_output.output_graphics3d import OutputSceneJmol 

sage: jmol = JSMolHtml(OutputSceneJmol.example(), width=500, height=300) 

sage: print(jmol.inner_html()) 

<html> 

<head> 

<style> 

* { 

margin: 0; 

padding: 0; 

... 

</html> 

""" 

return INNER_HTML_TEMPLATE.format( 

script=self.js_script(), 

width=self._width, 

height=self._height, 

path_to_jsmol=self._path, 

) 

 

def iframe(self): 

""" 

Return HTML iframe 

 

OUTPUT: 

 

String. 

 

EXAMPLES:: 

 

sage: from sage.repl.display.jsmol_iframe import JSMolHtml 

sage: from sage.repl.rich_output.output_graphics3d import OutputSceneJmol 

sage: jmol = JSMolHtml(OutputSceneJmol.example()) 

sage: print(jmol.iframe()) 

<iframe srcdoc=" 

... 

</iframe> 

""" 

escaped_inner_html = self.inner_html().replace('"', '&quot;') 

iframe = IFRAME_TEMPLATE.format( 

script=self.js_script(), 

width=self._width, 

height=self._height, 

escaped_inner_html=escaped_inner_html, 

) 

return iframe 

 

def outer_html(self): 

""" 

Return a HTML document containing an iframe with a JSmol applet 

 

OUTPUT: 

 

String 

 

EXAMPLES:: 

 

sage: from sage.repl.display.jsmol_iframe import JSMolHtml 

sage: from sage.repl.rich_output.output_graphics3d import OutputSceneJmol 

sage: jmol = JSMolHtml(OutputSceneJmol.example(), width=500, height=300) 

sage: print(jmol.outer_html()) 

<html> 

<head> 

<title>JSmol 3D Scene</title> 

</head> 

</body> 

<BLANKLINE> 

<iframe srcdoc=" 

... 

</html> 

""" 

escaped_inner_html = self.inner_html().replace('"', '&quot;') 

outer = OUTER_HTML_TEMPLATE.format( 

script=self.js_script(), 

width=self._width, 

height=self._height, 

escaped_inner_html=escaped_inner_html, 

) 

return outer