Ticket #13131: trac_13131-tables.patch

File trac_13131-tables.patch, 29.6 KB (added by jhpalmieri, 5 years ago)
  • doc/en/reference/misc.rst

    # HG changeset patch
    # User J. H. Palmieri <palmieri@math.washington.edu>
    # Date 1352505965 28800
    # Node ID 156d141c09bed201907b97e7836b522931a99a7b
    # Parent  39fe3e5b10aeccaf3b5db746f388bef1fcfbdb5b
    Implement a top-level 'table' function.
    
    diff --git a/doc/en/reference/misc.rst b/doc/en/reference/misc.rst
    a b  
    3333   sage/misc/interpreter
    3434   sage/misc/functional
    3535   sage/misc/html
     36   sage/misc/table
    3637   sage/misc/log
    3738   sage/misc/persist
    3839   sage/misc/unknown
  • sage/misc/all.py

    diff --git a/sage/misc/all.py b/sage/misc/all.py
    a b  
    1818
    1919from html import html
    2020
     21from table import table
     22
    2123from sage_timeit_class import timeit
    2224
    2325from edit_module import edit, set_edit_template
  • sage/misc/html.py

    diff --git a/sage/misc/html.py b/sage/misc/html.py
    a b  
    153153
    154154    def eval(self, s, globals=None, locals=None):
    155155        r"""
     156        Return an html representation for an object ``s``.
     157
     158        If ``s`` has a method ``_html_()``, call that. Otherwise, call
     159        :func:`math_parse` on ``str(s)``, evaluate any variables in
     160        the result, and add some html preamble and postamble.
     161
     162        In any case, *print* the resulting html string. This method
     163        always *returns* an empty string.
     164
    156165        EXAMPLES::
    157166
    158167            sage: html.eval('<hr>')
    159168            <html><font color='black'><hr></font></html>
    160169            ''
    161170        """
     171        if hasattr(s, '_html_'):
     172            s._html_()
     173            return ''
    162174        if globals is None:
    163175            globals = {}
    164176        if locals is None:
     
    227239            </div>
    228240            </html>
    229241
    230             sage: html.table(["Functions $f(x)$", sin(x), cos(x)], header = True)
    231             <html>
    232             <div class="notruncate">
    233             <table class="table_form">
    234             <tbody>
    235             <tr>
    236             <th>Functions <script type="math/tex">f(x)</script></th>
    237             </tr>
    238             <tr class ="row-a">
    239             <td><script type="math/tex">\sin\left(x\right)</script></td>
    240             </tr>
    241             <tr class ="row-b">
    242             <td><script type="math/tex">\cos\left(x\right)</script></td>
    243             </tr>
    244             </tbody>
    245             </table>
    246             </div>
    247             </html>
    248 
    249242            sage: html.table([(x,n(sin(x), digits=2)) for x in [0..3]], header = ["$x$", "$\sin(x)$"])
    250243            <html>
    251244            <div class="notruncate">
     
    277270            </html>
    278271
    279272        """
    280         import types
    281         from sage.misc.all import latex
    282         from itertools import cycle
    283         if isinstance(x, types.GeneratorType):
    284             x = list(x)
    285         if isinstance(x, (list, tuple)):
    286             rows = len(x)
    287             if rows > 0:
    288                 # if the table has less then 100 rows, don't truncate the output in the notebook
    289                 if rows <= 100:
    290                     print "<html>\n<div class=\"notruncate\">\n<table class=\"table_form\">\n<tbody>"
    291                 else:
    292                     print "<html>\n<div class=\"truncate\">\n<table class=\"table_form\">\n<tbody>"
    293                
    294                 if header is True:
    295                     header=x[0]
    296                     x = list(x[1:])
    297 
    298                 if header is not False:
    299                     print "<tr>"
    300                     self._table_columns(header, True)
    301                     print "</tr>"
    302 
    303                 for row_class, row in zip(cycle(["row-a", "row-b"]), x):
    304                     print "<tr class =\"%s\">" % row_class
    305                     self._table_columns(row, False)
    306                     print "</tr>"
    307                 print "</tbody>\n</table>\n</div>\n</html>"
    308 
    309     def _table_columns(self, row, header=False):
    310         r"""
    311         Print the items of a list as the columns of a HTML table.
    312 
    313         TESTS::
    314        
    315             sage: html._table_columns(["a $x^2$",1, sin(x)])
    316             <td>a <script type="math/tex">x^2</script></td>
    317             <td><script type="math/tex">1</script></td>
    318             <td><script type="math/tex">\sin\left(x\right)</script></td>
    319             sage: html._table_columns("a", header=True)
    320             <th>a</th>
    321         """       
    322         column_tag = "<th>%s</th>" if header else "<td>%s</td>"
    323         from sage.plot.all import Graphics
    324         import types
    325         if isinstance(row, types.GeneratorType):
    326             row = list(row)
    327         elif not isinstance(row, (list, tuple)):
    328             row = [row]
    329 
    330         for column in xrange(len(row)):
    331             if isinstance(row[column], Graphics):
    332                 print column_tag % row[column].show(linkmode = True)
    333             elif isinstance(row[column], str):
    334                 print column_tag % math_parse(row[column])
    335             else:
    336                 print column_tag % ('<script type="math/tex">%s</script>' % latex(row[column]))
     273        from table import table
     274        table(x, header_row=header)._html_()
    337275
    338276    def iframe(self, url, height=400, width=800):
    339277        r"""
  • new file sage/misc/table.py

    diff --git a/sage/misc/table.py b/sage/misc/table.py
    new file mode 100644
    - +  
     1r"""
     2Tables
     3
     4Display a rectangular array as a table, either in plain text, LaTeX,
     5or html.  See the documentation for :class:`table` for details and
     6examples.
     7
     8AUTHORS:
     9
     10- John H. Palmieri (2012-11)
     11"""
     12
     13from sage.structure.sage_object import SageObject
     14from sage.misc.cachefunc import cached_method
     15
     16class table(SageObject):
     17    r"""
     18    Display a rectangular array as a table, either in plain text, LaTeX,
     19    or html.
     20
     21    INPUTS:
     22
     23    - ``array`` - a list of lists (or tuple of tuples, etc.),
     24      containing the data to be displayed.
     25    - ``header_row`` (default False) - if True, first row is highlighted.
     26    - ``header_column`` (default False) - if True, first column is highlighted.
     27    - ``frame`` (default False) - if True, put a box around each cell.
     28    - ``align`` (default 'left') - the alignment of each entry: either
     29      'left', 'center', or 'right'
     30   
     31    EXAMPLES::
     32
     33        sage: array = [['a', 'b', 'c'], [100,2,3], [4,5,60]]
     34        sage: table(array)
     35          a     b   c
     36          100   2   3
     37          4     5   60
     38        sage: latex(table(array))
     39        \begin{tabular}{lll}
     40        a & b & c \\
     41        $100$ & $2$ & $3$ \\
     42        $4$ & $5$ & $60$ \\
     43        \end{tabular}
     44
     45    If ``header_row`` is ``True``, then the first row is highlighted. If
     46    ``header_column`` is ``True``, then the first column is
     47    highlighted. If ``frame`` is ``True``, then print a box around every
     48    "cell". ::
     49
     50        sage: table(array, header_row=True)
     51          a     b   c
     52        +-----+---+----+
     53          100   2   3
     54          4     5   60
     55        sage: latex(table(array, header_row=True))
     56        \begin{tabular}{lll}
     57        a & b & c \\ \hline
     58        $100$ & $2$ & $3$ \\
     59        $4$ & $5$ & $60$ \\
     60        \end{tabular}
     61        sage: table(array, frame=True)
     62        +-----+---+----+
     63        | a   | b | c  |
     64        +-----+---+----+
     65        | 100 | 2 | 3  |
     66        +-----+---+----+
     67        | 4   | 5 | 60 |
     68        +-----+---+----+
     69        sage: latex(table(array, frame=True))
     70        \begin{tabular}{|l|l|l|} \hline
     71        a & b & c \\ \hline
     72        $100$ & $2$ & $3$ \\ \hline
     73        $4$ & $5$ & $60$ \\ \hline
     74        \end{tabular}
     75        sage: table(array, header_column=True, frame=True)
     76        +-----++---+----+
     77        | a   || b | c  |
     78        +-----++---+----+
     79        | 100 || 2 | 3  |
     80        +-----++---+----+
     81        | 4   || 5 | 60 |
     82        +-----++---+----+
     83        sage: latex(table(array, header_row=True, frame=True))
     84        \begin{tabular}{|l|l|l|} \hline
     85        a & b & c \\ \hline \hline
     86        $100$ & $2$ & $3$ \\ \hline
     87        $4$ & $5$ & $60$ \\ \hline
     88        \end{tabular}
     89        sage: table(array, header_column=True)
     90          a   | b   c
     91          100 | 2   3
     92          4   | 5   60
     93
     94    The argument ``header_row`` can, instead of being ``True`` or
     95    ``False``, be the contents of the header row, so that ``array``
     96    consists of the data, while ``header_row`` is the header
     97    information.  The same goes for ``header_column``. Passing lists
     98    for both arguments simultaneously is not supported. ::
     99
     100        sage: table([(x,n(sin(x), digits=2)) for x in [0..3]], header_row=["$x$", "$\sin(x)$"], frame=True)
     101        +-----+-----------+
     102        | $x$ | $\sin(x)$ |
     103        +=====+===========+
     104        | 0   | 0.00      |
     105        +-----+-----------+
     106        | 1   | 0.84      |
     107        +-----+-----------+
     108        | 2   | 0.91      |
     109        +-----+-----------+
     110        | 3   | 0.14      |
     111        +-----+-----------+
     112        sage: table([[x for x in [0..3]], [n(sin(x), digits=2) for x in [0..3]]], header_column=['$x$', '$\sin(x)$'], frame=True)
     113        +-----------++------+------+------+------+
     114        | $x$       || 0    | 1    | 2    | 3    |
     115        +-----------++------+------+------+------+
     116        | $\sin(x)$ || 0.00 | 0.84 | 0.91 | 0.14 |
     117        +-----------++------+------+------+------+
     118
     119    In either plain text or LaTeX, entries in tables can be aligned to the
     120    left (default), center, or right::
     121
     122        sage: table(array, align='left')
     123          a     b   c
     124          100   2   3
     125          4     5   60
     126        sage: table(array, align='center')
     127          a    b   c
     128         100   2   3
     129          4    5   60
     130        sage: table(array, align='right', frame=True)
     131        +-----+---+----+
     132        |   a | b |  c |
     133        +-----+---+----+
     134        | 100 | 2 |  3 |
     135        +-----+---+----+
     136        |   4 | 5 | 60 |
     137        +-----+---+----+
     138
     139    To print HTML, use either ``table(...)._html_()`` or ``html(table(...))``::
     140
     141        sage: html(table([["$x$", "$\sin(x)$"]] + [(x,n(sin(x), digits=2)) for x in [0..3]], header_row=True, frame=True))
     142        <html>
     143        <div class="notruncate">
     144        <table border="1" class="table_form">
     145        <tbody>
     146        <tr>
     147        <th><script type="math/tex">x</script></th>
     148        <th><script type="math/tex">\sin(x)</script></th>
     149        </tr>
     150        <tr class ="row-a">
     151        <td><script type="math/tex">0</script></td>
     152        <td><script type="math/tex">0.00</script></td>
     153        </tr>
     154        <tr class ="row-b">
     155        <td><script type="math/tex">1</script></td>
     156        <td><script type="math/tex">0.84</script></td>
     157        </tr>
     158        <tr class ="row-a">
     159        <td><script type="math/tex">2</script></td>
     160        <td><script type="math/tex">0.91</script></td>
     161        </tr>
     162        <tr class ="row-b">
     163        <td><script type="math/tex">3</script></td>
     164        <td><script type="math/tex">0.14</script></td>
     165        </tr>
     166        </tbody>
     167        </table>
     168        </div>
     169        </html>
     170
     171    Note that if ``array`` is just a list or tuple, not nested, then
     172    it is treated as a single row::
     173
     174        sage: table([1,2,3])
     175        1   2   3
     176
     177    TESTS::
     178
     179        sage: TestSuite(table([["$x$", "$\sin(x)$"]] + [(x,n(sin(x), digits=2)) for x in [0..3]], header_row=True, frame=True)).run()
     180    """
     181    def __init__(self, array, header_row=False, header_column=False,
     182                 frame=False, align='left'):
     183        """
     184        EXAMPLES::
     185
     186            sage: table([1,2,3], frame=True)
     187            +---+---+---+
     188            | 1 | 2 | 3 |
     189            +---+---+---+
     190        """
     191        # Set options.
     192        self._options = {}
     193        if header_row is True:
     194            self._options['header_row'] = True
     195        elif header_row:
     196            self._options['header_row'] = True
     197            array = [header_row] + array
     198        else:
     199            self._options['header_row'] = False
     200        if header_column is True:
     201            self._options['header_column'] = True
     202        elif header_column:
     203            self._options['header_column'] = True
     204            array = [(a,) + tuple(x) for (a,x) in zip(header_column, array)]
     205        else:
     206            self._options['header_column'] = False
     207
     208        self._options['frame'] = frame
     209        self._options['align'] = align
     210        # Store array as a tuple.
     211        if not isinstance(array[0], (list, tuple)):
     212            array = (array,)
     213        self._array = tuple(array)
     214
     215    def __eq__(self, other):
     216        """
     217        Two tables are equal if and only if their data arrays and
     218        their options are the same.
     219
     220        EXAMPLES::
     221
     222            sage: array = [['a', 'b', 'c'], [1,plot(sin(x)),3], [4,5,identity_matrix(2)]]
     223            sage: T = table(array, header_row=True)
     224            sage: T2 = table(array, header_row=True)
     225            sage: T is T2
     226            False
     227            sage: T == T2
     228            True
     229            sage: T2.options(frame=True)
     230            sage: T == T2
     231            False
     232        """
     233        return (self._array == other._array and self.options() == other.options())
     234
     235    def options(self, **kwds):
     236        """
     237        With no arguments, return the dictionary of options for this
     238        table. With arguments, modify options.
     239
     240        INPUTS:
     241
     242        - ``header_row`` - if True, first row is highlighted.
     243        - ``header_column`` - if True, first column is highlighted.
     244        - ``frame`` - if True, put a box around each cell.
     245        - ``align`` - the alignment of each entry: either 'left',
     246          'center', or 'right'
     247
     248        EXAMPLES::
     249
     250            sage: T = table([['a', 'b', 'c'], [1,2,3]])
     251            sage: T.options()['align'], T.options()['frame']
     252            ('left', False)
     253            sage: T.options(align='right', frame=True)
     254            sage: T.options()['align'], T.options()['frame']
     255            ('right', True)
     256
     257        Note that when first initializing a table, ``header_row`` or
     258        ``header_column`` can be a list. In this case, during the
     259        initialization process, the header is merged with the rest of
     260        the data, so changing the header option later using
     261        ``table.options(...)`` doesn't affect the contents of the
     262        table, just whether the row or column is highlighed. When
     263        using this :meth:`options` method, no merging of data occurs,
     264        so here ``header_row`` and ``header_column`` should just be
     265        ``True`` or ``False``, not a list. ::
     266
     267            sage: T = table([[1,2,3], [4,5,6]], header_row=['a', 'b', 'c'], frame=True)
     268            sage: T
     269            +---+---+---+
     270            | a | b | c |
     271            +===+===+===+
     272            | 1 | 2 | 3 |
     273            +---+---+---+
     274            | 4 | 5 | 6 |
     275            +---+---+---+
     276            sage: T.options(header_row=False)
     277            sage: T
     278            +---+---+---+
     279            | a | b | c |
     280            +---+---+---+
     281            | 1 | 2 | 3 |
     282            +---+---+---+
     283            | 4 | 5 | 6 |
     284            +---+---+---+
     285
     286        If you do specify a list for ``header_row``, an error is raised::
     287
     288            sage: T.options(header_row=['x', 'y', 'z'])
     289            Traceback (most recent call last):
     290            ...
     291            TypeError: header_row should be either True or False.
     292        """
     293        if kwds:
     294            for option in ['align', 'frame']:
     295                if option in kwds:
     296                    self._options[option] = kwds[option]
     297            for option in ['header_row', 'header_column']:
     298                if option in kwds:
     299                    if not kwds[option]:
     300                        self._options[option] = kwds[option]
     301                    elif kwds[option] is True:
     302                        self._options[option] = kwds[option]
     303                    else:
     304                        raise TypeError("%s should be either True or False." % option)
     305        else:
     306            return self._options
     307
     308    def transpose(self):
     309        """
     310        Return a table which is the transpose of this one:
     311        rows and columns have been interchanged. Several of the
     312        properties of the original table are preserved: whether a
     313        frame is present and any alignment setting. On the other hand,
     314        header rows are converted to header columns, and vice versa.
     315
     316        EXAMPLES::
     317
     318            sage: T = table([[1,2,3], [4,5,6]])
     319            sage: T.transpose()
     320              1   4
     321              2   5
     322              3   6
     323            sage: T = table([[1,2,3], [4,5,6]], header_row=['x', 'y', 'z'], frame=True)
     324            sage: T.transpose()
     325            +---++---+---+
     326            | x || 1 | 4 |
     327            +---++---+---+
     328            | y || 2 | 5 |
     329            +---++---+---+
     330            | z || 3 | 6 |
     331            +---++---+---+
     332        """
     333        return table(zip(*self._array),
     334                     header_row=self._options['header_column'],
     335                     header_column=self._options['header_row'],
     336                     frame=self._options['frame'],
     337                     align=self._options['align'])
     338
     339    @cached_method
     340    def _widths(self):
     341        """
     342        The maximum widths for (the string representation of) each
     343        column. Used by the :meth:`_repr_` method.
     344
     345        EXAMPLES::
     346
     347            sage: table([['a', 'bb', 'ccccc'], [10, -12, 0], [1, 2, 3]])._widths()
     348            (2, 3, 5)
     349        """
     350        nc = len(self._array[0])
     351
     352        widths = [0] * nc
     353        for row in self._array:
     354            w = []
     355            for (idx, x) in zip(range(nc), row):
     356                w.append(max(widths[idx], len(str(x))))
     357            widths = w
     358        return tuple(widths)
     359
     360    def _repr_(self):
     361        """
     362        String representation of a table.
     363
     364        The class docstring has many examples; here is one more.
     365
     366        EXAMPLES::
     367
     368            sage: table([['a', 'bb', 'ccccc'], [10, -12, 0], [1, 2, 3]], align='right') # indirect doctest
     369               a    bb   ccccc
     370              10   -12       0
     371               1     2       3
     372        """
     373        array = self._array
     374        nc = len(array[0])
     375        if len(array) == 0 or nc == 0:
     376            return ""
     377
     378        frame_line = "+" + "+".join("-" * (x+2) for x in self._widths()) + "+\n"
     379
     380        if self._options['header_column'] and self._options['frame']:
     381            frame_line = "+" + frame_line[1:].replace('+', '++', 1)
     382
     383        if self._options['frame']:
     384            s = frame_line
     385        else:
     386            s = ""
     387
     388        if self._options['header_row']:
     389            s += self._str_table_row(array[0], header_row=True)
     390            array = array[1:]
     391
     392        for row in array:
     393            s += self._str_table_row(row, header_row=False)
     394        return s.strip("\n")
     395
     396    def _str_table_row(self, row, header_row=False):
     397        """
     398        String representation of a row of a table. Used by the
     399        :meth:`_repr_` method.
     400
     401        EXAMPLES::
     402
     403            sage: T = table([['a', 'bb', 'ccccc'], [10, -12, 0], [1, 2, 3]], align='right')
     404            sage: T._str_table_row([1,2,3])
     405            '   1     2       3\n'
     406            sage: T._str_table_row([1,2,3], True)
     407            '   1     2       3\n+----+-----+-------+\n'
     408            sage: T.options(header_column=True)
     409            sage: T._str_table_row([1,2,3], True)
     410            '   1 |   2       3\n+----+-----+-------+\n'
     411            sage: T.options(frame=True)
     412            sage: T._str_table_row([1,2,3], False)
     413            '|  1 ||   2 |     3 |\n+----++-----+-------+\n'
     414        """
     415        frame = self._options['frame']
     416        widths = self._widths()
     417        frame_line = "+" + "+".join("-" * (x+2) for x in widths) + "+\n"
     418
     419        align = self._options['align']
     420        if align == 'right':
     421            align_char = '>'
     422        elif align == 'center':
     423            align_char = '^'
     424        else:
     425            align_char = '<'
     426
     427        s = ""
     428        if frame:
     429            s += "| "
     430        else:
     431            s += "  "
     432
     433        if self._options['header_column']:
     434            if frame:
     435                frame_line = "+" + frame_line[1:].replace('+', '++', 1)
     436            s += ("{:" + align_char + str(widths[0]) + "}").format(row[0])
     437            if frame:
     438                s += " || "
     439            else:
     440                s += " | "
     441            row = row[1:]
     442            widths = widths[1:]
     443
     444        for (entry, width) in zip(row, widths):
     445            s += ("{:" + align_char + str(width) + "}").format(entry)
     446            if frame:
     447                s += " | "
     448            else:
     449                s += "   "
     450        s = s.rstrip(' ')
     451        s += "\n"
     452        if frame and header_row:
     453            s += frame_line.replace('-', '=')
     454        elif frame or header_row:
     455            s += frame_line
     456        return s
     457
     458    def _latex_(self):
     459        r"""
     460        LaTeX representation of a table.
     461
     462        If an entry is a Sage object, it is replaced by its LaTeX
     463        representation, delimited by dollar signs (i.e., ``x`` is
     464        replaced by ``$latex(x)$``). If an entry is a string, the
     465        dollar signs are not automatically added, so tables can
     466        include both plain text and mathematics.
     467       
     468        EXAMPLES::
     469
     470            sage: from sage.misc.table import table
     471            sage: a = [[r'$\sin(x)$', '$x$', 'text'], [1,34342,3], [identity_matrix(2),5,6]]
     472            sage: latex(table(a)) # indirect doctest
     473            \begin{tabular}{lll}
     474            $\sin(x)$ & $x$ & text \\
     475            $1$ & $34342$ & $3$ \\
     476            $\left(\begin{array}{rr}
     477            1 & 0 \\
     478            0 & 1
     479            \end{array}\right)$ & $5$ & $6$ \\
     480            \end{tabular}
     481            sage: latex(table(a, frame=True, align='center'))
     482            \begin{tabular}{|c|c|c|} \hline
     483            $\sin(x)$ & $x$ & text \\ \hline
     484            $1$ & $34342$ & $3$ \\ \hline
     485            $\left(\begin{array}{rr}
     486            1 & 0 \\
     487            0 & 1
     488            \end{array}\right)$ & $5$ & $6$ \\ \hline
     489            \end{tabular}
     490        """
     491        from latex import latex, LatexExpr
     492        import types
     493
     494        array = self._array
     495        nc = len(array[0])
     496        if len(array) == 0 or nc == 0:
     497            return ""
     498
     499        align_char = self._options['align'][0]   # 'l', 'c', 'r'
     500        if self._options['frame']:
     501            frame_char = '|'
     502            frame_str = ' \\hline'
     503        else:
     504            frame_char = ''
     505            frame_str = ''
     506        if self._options['header_column']:
     507            head_col_char = '|'
     508        else:
     509            head_col_char = ''
     510        if self._options['header_row']:
     511            head_row_str = ' \\hline'
     512        else:
     513            head_row_str = ''
     514
     515        # table header
     516        s = "\\begin{tabular}{"
     517        s += frame_char + align_char + frame_char + head_col_char
     518        s += frame_char.join([align_char] * (nc-1))
     519        s += frame_char + "}" + frame_str + "\n"
     520        # first row
     521        s += " & ".join(LatexExpr(x) if isinstance(x, (str, LatexExpr))
     522                      else '$' + latex(x).strip() + '$' for x in array[0])
     523        s += " \\\\" + frame_str + head_row_str + "\n"
     524        # other rows
     525        for row in array[1:]:
     526            s += " & ".join(LatexExpr(x) if isinstance(x, (str, LatexExpr))
     527                          else '$' + latex(x).strip() + '$' for x in row)
     528            s += " \\\\" + frame_str + "\n"
     529        s += "\\end{tabular}"
     530        return s
     531
     532    def _html_(self):
     533        r"""
     534        HTML representation of a table.
     535
     536        Strings of html will be parsed for math inside dollar and
     537        double-dollar signs.  2D graphics will be displayed in the
     538        cells.  Expressions will be latexed.
     539
     540        The ``align`` option for tables is ignored in HTML
     541        output. Specifying ``header_column=True`` may not have any
     542        visible effect in the Sage notebook, depending on the version
     543        of the notebook.
     544
     545        EXAMPLES::
     546
     547            sage: T = table([[r'$\sin(x)$', '$x$', 'text'], [1,34342,3], [identity_matrix(2),5,6]])
     548            sage: T._html_()
     549            <html>
     550            <div class="notruncate">
     551            <table  class="table_form">
     552            <tbody>
     553            <tr class ="row-a">
     554            <td><script type="math/tex">\sin(x)</script></td>
     555            <td><script type="math/tex">x</script></td>
     556            <td>text</td>
     557            </tr>
     558            <tr class ="row-b">
     559            <td><script type="math/tex">1</script></td>
     560            <td><script type="math/tex">34342</script></td>
     561            <td><script type="math/tex">3</script></td>
     562            </tr>
     563            <tr class ="row-a">
     564            <td><script type="math/tex">\left(\begin{array}{rr}
     565            1 & 0 \\
     566            0 & 1
     567            \end{array}\right)</script></td>
     568            <td><script type="math/tex">5</script></td>
     569            <td><script type="math/tex">6</script></td>
     570            </tr>
     571            </tbody>
     572            </table>
     573            </div>
     574            </html>
     575
     576        Note that calling ``html(table(...))`` has the same effect as ``table(...)._html_()`::
     577
     578            sage: T = table([["$x$", "$\sin(x)$"]] + [(x,n(sin(x), digits=2)) for x in [0..3]], header_row=True, frame=True)
     579            sage: T
     580            +-----+-----------+
     581            | $x$ | $\sin(x)$ |
     582            +=====+===========+
     583            | 0   | 0.00      |
     584            +-----+-----------+
     585            | 1   | 0.84      |
     586            +-----+-----------+
     587            | 2   | 0.91      |
     588            +-----+-----------+
     589            | 3   | 0.14      |
     590            +-----+-----------+
     591            sage: html(T)
     592            <html>
     593            <div class="notruncate">
     594            <table border="1" class="table_form">
     595            <tbody>
     596            <tr>
     597            <th><script type="math/tex">x</script></th>
     598            <th><script type="math/tex">\sin(x)</script></th>
     599            </tr>
     600            <tr class ="row-a">
     601            <td><script type="math/tex">0</script></td>
     602            <td><script type="math/tex">0.00</script></td>
     603            </tr>
     604            <tr class ="row-b">
     605            <td><script type="math/tex">1</script></td>
     606            <td><script type="math/tex">0.84</script></td>
     607            </tr>
     608            <tr class ="row-a">
     609            <td><script type="math/tex">2</script></td>
     610            <td><script type="math/tex">0.91</script></td>
     611            </tr>
     612            <tr class ="row-b">
     613            <td><script type="math/tex">3</script></td>
     614            <td><script type="math/tex">0.14</script></td>
     615            </tr>
     616            </tbody>
     617            </table>
     618            </div>
     619            </html>
     620        """
     621        import types
     622        from itertools import cycle
     623        array = self._array
     624        header_row = self._options['header_row']
     625        if self._options['frame']:
     626            frame = 'border="1"'
     627        else:
     628            frame = ''
     629
     630        if len(array) > 0:
     631            # If the table has < 100 rows, don't truncate the output in the notebook
     632            if len(array) <= 100:
     633                print "<html>\n<div class=\"notruncate\">\n<table %s class=\"table_form\">\n<tbody>" % frame
     634            else:
     635                print "<html>\n<div class=\"truncate\">\n<table %s class=\"table_form\">\n<tbody>" % frame
     636
     637            # First row:
     638            if header_row:
     639                print "<tr>"
     640                self._html_table_row(array[0], header=header_row)
     641                print "</tr>"
     642                array = array[1:]
     643
     644            # Other rows:
     645            for row_class, row in zip(cycle(["row-a", "row-b"]), array):
     646                print "<tr class =\"%s\">" % row_class
     647                self._html_table_row(row, header=False)
     648                print "</tr>"
     649            print "</tbody>\n</table>\n</div>\n</html>"
     650
     651    def _html_table_row(self, row, header=False):
     652        r"""
     653        Print the items of a list as one row of an HTML table. Used by
     654        the :meth:`_html_` method.
     655
     656        INPUTS:
     657
     658        - ``row`` - a list with the same number of entries as each row
     659          of the table.
     660        - ``header`` (default False) - if True, treat this as a header
     661          row, using ``<th>`` instead of ``<td>``.
     662
     663        Strings get printed verbatim unless they seem to be LaTeX
     664        code, in which case they are enclosed in a ``script`` tag
     665        appropriate for MathJax. Sage objects are printed using their
     666        LaTeX representations.
     667
     668        EXAMPLES::
     669
     670            sage: T = table([['a', 'bb', 'ccccc'], [10, -12, 0], [1, 2, 3]])
     671            sage: T._html_table_row(['a', 2, '$x$'])
     672            <td>a</td>
     673            <td><script type="math/tex">2</script></td>
     674            <td><script type="math/tex">x</script></td>
     675        """       
     676        from sage.plot.all import Graphics
     677        from latex import latex
     678        from html import math_parse
     679        import types
     680
     681        if isinstance(row, types.GeneratorType):
     682            row = list(row)
     683        elif not isinstance(row, (list, tuple)):
     684            row = [row]
     685
     686        column_tag = "<th>%s</th>" if header else "<td>%s</td>"
     687
     688        if self._options['header_column']:
     689            first_column_tag = "<th class=\"ch\">%s</th>" if header else "<td class=\"ch\">%s</td>"
     690        else:
     691            first_column_tag = column_tag
     692
     693        # First entry of row:
     694        entry = row[0]
     695        if isinstance(entry, Graphics):
     696            print first_column_tag % entry.show(linkmode = True)
     697        elif isinstance(entry, str):
     698            print first_column_tag % math_parse(entry)
     699        else:
     700            print first_column_tag % ('<script type="math/tex">%s</script>' % latex(entry))
     701
     702        # Other entries:
     703        for column in xrange(1,len(row)):
     704            if isinstance(row[column], Graphics):
     705                print column_tag % row[column].show(linkmode = True)
     706            elif isinstance(row[column], str):
     707                print column_tag % math_parse(row[column])
     708            else:
     709                print column_tag % ('<script type="math/tex">%s</script>' % latex(row[column]))