Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
sagemath
GitHub Repository: sagemath/sagelib
Path: blob/master/sage/misc/html.py
4036 views
1
"""
2
HTML typesetting for the notebook
3
"""
4
5
#*****************************************************************************
6
# Copyright (C) 2008 William Stein <[email protected]>
7
#
8
# Distributed under the terms of the GNU General Public License (GPL)
9
# as published by the Free Software Foundation; either version 2 of
10
# the License, or (at your option) any later version.
11
# http://www.gnu.org/licenses/
12
#*****************************************************************************
13
14
from sage.misc.latex import latex
15
from sage.misc.sage_eval import sage_eval
16
17
def math_parse(s):
18
r"""
19
Turn the HTML-ish string s that can have \$\$ and \$'s in it into
20
pure HTML. See below for a precise definition of what this means.
21
22
INPUT:
23
24
- ``s`` -- a string
25
26
OUTPUT:
27
28
- a string.
29
30
Do the following:
31
32
* Replace all ``\$ text \$``\'s by
33
``<span class='math'> text </span>``
34
* Replace all ``\$\$ text \$\$``\'s by
35
``<div class='math'> text </div>``
36
* Replace all ``\ \$``\'s by ``\$``\'s. Note that in
37
the above two cases nothing is done if the ``\$``
38
is preceeded by a backslash.
39
* Replace all ``\[ text \]``\'s by
40
``<div class='math'> text </div>``
41
42
EXAMPLES::
43
44
sage: sage.misc.html.math_parse('This is $2+2$.')
45
'This is <span class="math">2+2</span>.'
46
sage: sage.misc.html.math_parse('This is $$2+2$$.')
47
'This is <div class="math">2+2</div>.'
48
sage: sage.misc.html.math_parse('This is \\[2+2\\].')
49
'This is <div class="math">2+2</div>.'
50
sage: sage.misc.html.math_parse(r'This is \[2+2\].')
51
'This is <div class="math">2+2</div>.'
52
53
TESTS::
54
55
sage: sage.misc.html.math_parse(r'This \$\$is $2+2$.')
56
'This $$is <span class="math">2+2</span>.'
57
"""
58
# first replace \\[ and \\] by <div class="math"> and </div>, respectively.
59
while True:
60
i = s.find('\\[')
61
if i == -1:
62
break
63
else:
64
s = s[:i] + '<div class="math">' + s[i+2:]
65
j = s.find('\\]')
66
if j == -1: # missing right-hand delimiter, so add one
67
s = s + '</div>'
68
else:
69
s = s[:j] + '</div>' + s[j+2:]
70
71
# Below t always has the "parsed so far" version of s, and s is
72
# just the part of the original input s that hasn't been parsed.
73
t = ''
74
while True:
75
i = s.find('$')
76
if i == -1:
77
# No dollar signs -- definitely done.
78
return t + s
79
elif i > 0 and s[i-1] == '\\':
80
# A dollar sign with a backslash right before it, so
81
# we ignore it by sticking it in the parsed string t
82
# and skip to the next iteration.
83
t += s[:i-1] + '$'
84
s = s[i+1:]
85
continue
86
elif i+1 < len(s) and s[i+1] == '$':
87
# Found a math environment. Double dollar sign so div mode.
88
typ = 'div'
89
else:
90
# Found math environment. Single dollar sign so span mode.
91
typ = 'span'
92
93
# Now find the matching $ sign and form the span or div.
94
j = s[i+2:].find('$')
95
if j == -1:
96
j = len(s)
97
s += '$'
98
if typ == 'div':
99
s += '$$'
100
else:
101
j += i + 2
102
if typ == 'div':
103
txt = s[i+2:j]
104
else:
105
txt = s[i+1:j]
106
t += s[:i] + '<%s class="math">%s</%s>'%(typ,
107
' '.join(txt.splitlines()), typ)
108
s = s[j+1:]
109
if typ == 'div':
110
s = s[1:]
111
return t
112
113
class HTMLExpr(str):
114
r"""
115
A class for HTML expression
116
"""
117
def __repr__(self):
118
return str(self)
119
120
class HTML:
121
def __call__(self, s, globals=None, locals=None):
122
"""
123
Display the given HTML expression in the notebook.
124
125
INPUT:
126
127
- ``s`` -- a string
128
129
OUTPUT:
130
131
- prints a code that embeds HTML in the output.
132
133
By default in the notebook an output cell has two parts, first a plain
134
text preformat part, then second a general HTML part (not pre). If
135
you call html(s) at any point then that adds something that will be
136
displayed in the preformated part in html.
137
138
EXAMPLES::
139
140
sage: html('<a href="http://sagemath.org">sagemath</a>')
141
<html><font color='black'><a href="http://sagemath.org">sagemath</a></font></html>
142
sage: html('<hr>')
143
<html><font color='black'><hr></font></html>
144
"""
145
return HTMLExpr(self.eval(s, globals, locals))
146
147
def eval(self, s, globals=None, locals=None):
148
r"""
149
EXAMPLES::
150
151
sage: html.eval('<hr>')
152
<html><font color='black'><hr></font></html>
153
''
154
"""
155
if globals is None:
156
globals = {}
157
if locals is None:
158
locals = {}
159
s = str(s)
160
s = math_parse(s)
161
t = ''
162
while len(s) > 0:
163
i = s.find('<sage>')
164
if i == -1:
165
t += s
166
break
167
j = s.find('</sage>')
168
if j == -1:
169
t += s
170
break
171
t += s[:i] + '<span class="math">%s</span>'%\
172
latex(sage_eval(s[6+i:j], locals=locals))
173
s = s[j+7:]
174
print "<html><font color='black'>%s</font></html>"%t
175
return ''
176
177
def table(self, x, header = False):
178
r"""
179
Print a nested list as a HTML table. Strings of html
180
will be parsed for math inside dollar and double-dollar signs.
181
2D graphics will be displayed in the cells. Expressions will
182
be latexed.
183
184
185
INPUT:
186
187
- ``x`` -- a list of lists (i.e., a list of table rows)
188
- ``header`` -- a row of headers. If ``True``, then the first
189
row of the table is taken to be the header.
190
191
EXAMPLES::
192
193
sage: html.table([(i, j, i == j) for i in [0..1] for j in [0..1]])
194
<html>
195
<div class="notruncate">
196
<table class="table_form">
197
<tbody>
198
<tr class ="row-a">
199
<td><span class="math">0</span></td>
200
<td><span class="math">0</span></td>
201
<td><span class="math">\mathrm{True}</span></td>
202
</tr>
203
<tr class ="row-b">
204
<td><span class="math">0</span></td>
205
<td><span class="math">1</span></td>
206
<td><span class="math">\mathrm{False}</span></td>
207
</tr>
208
<tr class ="row-a">
209
<td><span class="math">1</span></td>
210
<td><span class="math">0</span></td>
211
<td><span class="math">\mathrm{False}</span></td>
212
</tr>
213
<tr class ="row-b">
214
<td><span class="math">1</span></td>
215
<td><span class="math">1</span></td>
216
<td><span class="math">\mathrm{True}</span></td>
217
</tr>
218
</tbody>
219
</table>
220
</div>
221
</html>
222
223
sage: html.table(["Functions $f(x)$", sin(x), cos(x)], header = True)
224
<html>
225
<div class="notruncate">
226
<table class="table_form">
227
<tbody>
228
<tr>
229
<th>Functions <span class="math">f(x)</span></th>
230
</tr>
231
<tr class ="row-a">
232
<td><span class="math">\sin\left(x\right)</span></td>
233
</tr>
234
<tr class ="row-b">
235
<td><span class="math">\cos\left(x\right)</span></td>
236
</tr>
237
</tbody>
238
</table>
239
</div>
240
</html>
241
242
sage: html.table([(x,n(sin(x), digits=2)) for x in [0..3]], header = ["$x$", "$\sin(x)$"])
243
<html>
244
<div class="notruncate">
245
<table class="table_form">
246
<tbody>
247
<tr>
248
<th><span class="math">x</span></th>
249
<th><span class="math">\sin(x)</span></th>
250
</tr>
251
<tr class ="row-a">
252
<td><span class="math">0</span></td>
253
<td><span class="math">0.00</span></td>
254
</tr>
255
<tr class ="row-b">
256
<td><span class="math">1</span></td>
257
<td><span class="math">0.84</span></td>
258
</tr>
259
<tr class ="row-a">
260
<td><span class="math">2</span></td>
261
<td><span class="math">0.91</span></td>
262
</tr>
263
<tr class ="row-b">
264
<td><span class="math">3</span></td>
265
<td><span class="math">0.14</span></td>
266
</tr>
267
</tbody>
268
</table>
269
</div>
270
</html>
271
272
"""
273
import types
274
from sage.misc.all import latex
275
from itertools import cycle
276
if isinstance(x, types.GeneratorType):
277
x = list(x)
278
if isinstance(x, (list, tuple)):
279
rows = len(x)
280
if rows > 0:
281
# if the table has less then 100 rows, don't truncate the output in the notebook
282
if rows <= 100:
283
print "<html>\n<div class=\"notruncate\">\n<table class=\"table_form\">\n<tbody>"
284
else:
285
print "<html>\n<div class=\"truncate\">\n<table class=\"table_form\">\n<tbody>"
286
287
if header is True:
288
header=x[0]
289
x = list(x[1:])
290
291
if header is not False:
292
print "<tr>"
293
self._table_columns(header, True)
294
print "</tr>"
295
296
for row_class, row in zip(cycle(["row-a", "row-b"]), x):
297
print "<tr class =\"%s\">" % row_class
298
self._table_columns(row, False)
299
print "</tr>"
300
print "</tbody>\n</table>\n</div>\n</html>"
301
302
def _table_columns(self, row, header=False):
303
r"""
304
Print the items of a list as the columns of a HTML table.
305
306
TESTS::
307
308
sage: html._table_columns(["a $x^2$",1, sin(x)])
309
<td>a <span class="math">x^2</span></td>
310
<td><span class="math">1</span></td>
311
<td><span class="math">\sin\left(x\right)</span></td>
312
sage: html._table_columns("a", header=True)
313
<th>a</th>
314
"""
315
column_tag = "<th>%s</th>" if header else "<td>%s</td>"
316
from sage.plot.all import Graphics
317
import types
318
if isinstance(row, types.GeneratorType):
319
row = list(row)
320
elif not isinstance(row, (list, tuple)):
321
row = [row]
322
323
for column in xrange(len(row)):
324
if isinstance(row[column], Graphics):
325
print column_tag % row[column].show(linkmode = True)
326
elif isinstance(row[column], str):
327
print column_tag % math_parse(row[column])
328
else:
329
print column_tag % ('<span class="math">%s</span>' % latex(row[column]))
330
331
def iframe(self, url, height=400, width=800):
332
r"""
333
Put an existing web page into a worksheet.
334
335
INPUT:
336
337
- ``url`` -- a url string, either with or without URI scheme
338
(defaults to "http").
339
- ``height`` -- the number of pixels for the page height.
340
Defaults to 400.
341
- ``width`` -- the number of pixels for the page width.
342
Defaults to 800.
343
344
OUTPUT:
345
346
Opens the url in a worksheet. If the url is a regular web page it
347
will appear in the worksheet. This was originally intended to bring
348
GeoGebra worksheets into Sage, but it can be used for many other
349
purposes.
350
351
EXAMPLES::
352
353
sage: html.iframe("sagemath.org")
354
<html><font color='black'><iframe height="400" width="800"
355
src="http://sagemath.org"></iframe></font></html>
356
sage: html.iframe("http://sagemath.org",30,40)
357
<html><font color='black'><iframe height="30" width="40"
358
src="http://sagemath.org"></iframe></font></html>
359
sage: html.iframe("https://sagemath.org",30)
360
<html><font color='black'><iframe height="30" width="800"
361
src="https://sagemath.org"></iframe></font></html>
362
sage: html.iframe("/home/admin/0/data/filename")
363
<html><font color='black'><iframe height="400" width="800"
364
src="/home/admin/0/data/filename"></iframe></font></html>
365
sage: html.iframe('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAA'
366
... 'AUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBA'
367
... 'AO9TXL0Y4OHwAAAABJRU5ErkJggg=="')
368
<html><font color='black'><iframe height="400" width="800"
369
src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg==""></iframe></font></html>
370
371
AUTHOR:
372
373
- Bruce Cohen (2011-06-14)
374
"""
375
if ":" not in url and not url.startswith('/'):
376
url = "http://" + url
377
string = ( '<iframe height="%d" width="%d" src="%s"></iframe>' %
378
(height, width, url) )
379
return html(string)
380
381
html = HTML()
382
# Ensure that html appear in the sphinx doc as a function
383
# so that the link :func:`html` is correctly set up.
384
html.__doc__ = HTML.__call__.__doc__
385
386