# -*- coding: utf-8 -*
#############################################################################
#       Copyright (C) 2008 William Stein <wstein@gmail.com>
#  Distributed under the terms of the GNU General Public License (GPL)
#  The full text of the GPL is available at:
#                  http://www.gnu.org/licenses/
#############################################################################

r"""
Interact Functions in the Notebook

This module implements an :func:`interact` function decorator for the Sage
notebook.

AUTHORS:

- William Stein (2008-03-02): version 1.0 at Sage/Enthought Days
  8 in Texas

- Jason Grout (2008-03): discussion and first few prototypes

- Jason Grout (2008-05): :class:`input_grid` control
"""

"""
 ** PLANNING **

NOTES:

   * There is no testing of pickling anywhere in this file.  This is
     because there is no reason one would ever pickle anything in this
     file, since everything is associated with particular state
     information of a notebook.

BUGS:

   [x] have default values set from the get go
   [x] spacing around sliders; also need to have labels  
   [x] when re-evaluate input, make sure to clear output so cell-interact-id div is gone.
   [x] two interacts in one cell -- what to do?
   [x] draw initial state
   [x] make interact canvas resizable
   [x] if you  use a interact control after restarting, doesn't work.   Need to reset it.  How?
   [x] display html parts of output as html
   [x] default slider pos doesn't work, eg. def _(q1=(-1,(-3,3)), q2=(1,(-3,3))):
   [x] change from interact to interact everywhere. 
   [x] edit/save breaks interact mode 
          * picking up images that shouldn't.
          * controls completely stop working. 
   [x] problems with html/pre/text formatting, e.g., in TEXT mode and in interact cells
   [x] tab completion in interact broken formatting
   [x] error exception reporting broken
   [x] replace special %interact by something very obfuscated to keep from having
       really weird mistakes that are hard for people to debug.
   [x] cell order corruption
   [x] cross-platform testing (good enough -- it's jquery)
   [x] can't enter "foo" in input_box now because of how strings are
       passed back and forth using single quotes.
   [x] possible issue with page title being undefined; don't know why
       or if that is connected with interactives
   [x] autorunning interact cells on load is being injected into the
       i/o pexpect stream way too early.
   [x] what do published worksheets do??    

VERSION 1:

   [X] get sliders to work; values after move slider
   [x] default values
   [x] NO -- autoswitch to 1-cell mode:
           put                  slide_mode(); jump_to_slide(%s);   in wrap_in_outside_frame
        but feels all wrong.     
   [x] completely get rid of left clicking to switch wrap mode for
           interact objects: always in word wrap mode or hide.
   [x] shortcut ('label', v)        
   [x] test saving and loading whole notebook to a file
   [x] collection of about 20 good examples of how to use interact (doctested)
   [x] interact(f) should also work; i.e., no need to use decorators -- done; won't be advertised, but
       at least fixing this improves code quality.
   [x] obfuscate ?START and ?END much more.
   [x] type checked input box
   [x] line up all the control in a single table so all labels and all
       controls exactly match up
   [x] button bar
   [x] drop down menu
   [x] checkbox
   [x] color selector
   [x] something to avoid server flood, e.g., any %interact request removes all other
       such requests from the queue in worksheet.py

   DOCS:

   [x] 100% documentation and doctest coverage
   [ ] put the docs for this in the reference manual
   [ ] put summary doc in notebook help page
   
VERSION 2:

   [ ] vertical scroll bars (maybe very easy via jsquery)
   [ ] small version of color selector
   [ ] button -- block of code gets run when it is clicked
   [ ] when click a button in a button bar it is highlighted and
       other buttons are not (via some sort of javascript)
   [ ] much more fine control over style of all controls
   [ ] safe/secure evaluation mode
   [ ] slider is too narrow -- need to expand to window width?
   [ ] fix the flicker resize during update (hard???)
   [ ] make option for text input that correctly gives something of
       the same type as the default input.
   [ ] matrix input control (method of matrix space) -- a spreadsheet like thing
   [ ] a 2-D slider:
          u = ((xmin,xmax),(ymin,ymax))  2-D slider   -- NOT IMPLEMENTED
   [ ] locator in a 2-D graphic
   [ ] tab_view -- represents an object in which clicking the tab
                   with label lbl[i] displays expr[i]
   [ ] controls: make them easy to customize as below --
          location -- where to put the slider (?)   
          align -- left, right, top, texttop, middle, absmiddle, baseline, bottom, absbottom
          background -- the color of the background for the cell
          frame -- draw a frame around
          disabled -- disables the input element when it first loads
                      so that the user can not write text in it, or
                      select it.
          editable -- bool
          font_size -- integer
          maxlength -- the maximum number of characters allowed in a text field.
          name -- defines a unique name for the input element
          size -- the size of the input element
          type -- button, checkbox, file, password, radio, slider, text, setter_bar

VERSION 3:
   [ ] protocol for objects to have their own interact function; make
       it so for any object obj in sage, one can do
             {{{
             interact(obj)
             }}}
       and get something useful as a result. 
   [ ] flot -- some pretty but very simple javascript only graphics  (maybe don't do... unclear)
   [ ] zorn -- similar (maybe don't do... unclear)
   [ ] color sliders (?):
          u = color(...)         a color slider
          http://interface.eyecon.ro/demos/slider_colorpicker.html
          http://jqueryfordesigners.com/demo/slider-gallery.html
   [ ] tag_cell('foo') -- makes it so one can refer to the current cell
       from elsewhere using the tag foo instead of the cell id number
       This involves doing something with SAGE_CELL_ID and the cell_id() method.
   [ ] framed -- put a frame around an object
"""

# Standard system libraries
import inspect
import math
import types
import collections
from base64 import standard_b64decode

# Sage libraries
from sagenb.misc.misc import sage_eval, Color, is_Matrix
from sage.arith.srange import srange
from sage.misc.cachefunc import cached_method

# SAGE_CELL_ID is a module scope variable that is always set equal to
# the current cell id (of the executing cell).  Code that sets this is
# inserted by the notebook server each time a worksheet cell is
# evaluated.
SAGE_CELL_ID = 0

# Prefixed to cell input, signals an interact update.
INTERACT_UPDATE_PREFIX = '%__sage_interact__'

# If this special message appears in an interact cell's output, it
# should trigger an automatic re-evaluation of the ambient cell.
INTERACT_RESTART = '__SAGE_INTERACT_RESTART__'

# Place-holder markers/fields, replaced in cell.py and
# notebook_lib.js.
INTERACT_START = '<?__SAGE__START>'
INTERACT_TEXT = '<?__SAGE__TEXT>'
INTERACT_HTML = '<?__SAGE__HTML>'
INTERACT_END = '<?__SAGE__END>'


# Dictionary that stores the state of all active interact cells. 
state = {}

def reset_state():
    """
    Reset the :func:`interact` state of this sage process.

    EXAMPLES::

        sage: sagenb.notebook.interact.state  # random output
        {1: {'function': <function g at 0x72aaab0>, 'variables': {'m': 3, 'n': 5}, 'adapt': {1: <bound method Slider._adaptor of Slider Interact Control: n [1--|1|---10].>, 2: <bound method Slider._adaptor of Slider Interact Control: m [1--|1|---10].>}}}
        sage: from sagenb.notebook.interact import reset_state
        sage: reset_state()
        sage: sagenb.notebook.interact.state
        {}
    """
    global state
    state = {}

_k = 0
def new_adapt_number():
    """
    Return an integer, always counting up, and starting with 0.  This
    is used for saving the adapt methods for controls.  An adapt
    method is just a function that coerces data into some object,
    e.g., makes sure the control always produces int's.

    OUTPUT:

    - an integer

    EXAMPLES::

        sage: sagenb.notebook.interact.new_adapt_number()   # random output -- depends on when called
        1
    """
    global _k
    _k += 1
    return _k
    

def html(s):
    """
    Print the input string ``s`` in a form that tells the notebook to
    display it in the HTML portion of the output.  This function has
    no return value.

    INPUT:

    - ``s`` - a string

    EXAMPLES::

        sage: sagenb.notebook.interact.html('hello')
        <html>hello</html>
    """
    print("<html>%s</html>" % s)

def html_slider(id, values, callback, steps, default=0, margin=0):
    """
    Return the HTML representation of a jQuery slider.

    INPUT:

    - ``id`` - a string; the DOM ID of the slider (better be unique)

    - ``values`` - a string; 'null' or JavaScript string containing
      array of values on slider

    - ``callback`` - a string; JavaScript that is executed whenever
      the slider is done moving

    - ``steps`` - an integer; number of steps from minimum to maximum
      value

    - ``default`` - an integer (default: 0); the default position of
      the slider

    - ``margin`` - an integer (default: 0); size of margin to insert
      around the slider

    OUTPUT:

    - a string - HTML format

    EXAMPLES:

    We create a jQuery HTML slider.  If you do the following in the
    notebook you should obtain a slider that when moved pops up a
    window showing its current position::

        sage: from sagenb.notebook.interact import html_slider, html
        sage: html(html_slider('slider-007', 'null', 'alert(position)', steps=5, default=2, margin=5))
        <html>...slider...</html>
    """
    val_html = ''
    if values != 'null':
        val_html = """
            <td>
              <font color="black" id="%s-lbl"></font>
            </td>""" % id

    s = """
        <table>
          <tr>
            <td>
              <div id="%s" style="margin:%spx; margin-left: 1.0em; margin-right: 1.0em; width: 15.0em;"></div>
            </td>
            %s
          </tr>
        </table>""" % (id, int(margin), val_html)

    # We now generate javascript that gets run after the above div
    # gets inserted. This happens because of the setTimeout function
    # below which gets passed an anonymous function.
    s += """<script>
    (function() {
        var values = %(values)s;
        setTimeout(function() {
            $('#%(id)s').slider({
                step: 1,
                min: 0,
                max: %(maxvalue)s,
                value: %(startvalue)s,
                change: function (e, ui) {
                    var position = ui.value;
                    if (values != null) {
                        $('#%(id)s-lbl').text(values[position]);
                        %(callback)s;
                    }
                },
                slide: function (e, ui) {
                    if (values != null) {
                        $('#%(id)s-lbl').text(values[ui.value]);
                    }
                }
            });
            if (values != null) {
                $('#%(id)s-lbl').text(values[$('#%(id)s').slider('value')]);
            }
        }, 1);
    })();
    </script>""" % {'values': values, 'id': id, 'maxvalue': steps - 1,
                    'startvalue': default, 'callback': callback}

    # change 'change' to 'slide' and it changes the slider every time it moves;
    # needs much more work to actually work, since server gets flooded by
    # requests.

    return s

def html_rangeslider(id, values, callback, steps, default_l=0, default_r=1, 
                     margin=0):
    """
    Return the HTML representation of a jQuery range slider.

    INPUT:

    - ``id`` - a string; the DOM ID of the slider (better be unique)

    - ``values`` - a string; 'null' or JavaScript string containing
      array of values on slider

    - ``callback`` - a string; JavaScript that is executed whenever
      the slider is done moving

    - ``steps`` - an integer; number of steps from minimum to maximum
      value

    - ``default_l`` - an integer (default: 0); the default position of
      the left edge of the slider

    - ``default_r`` - an integer (default: 1); the default position of
      the right edge of the slider

    - ``margin`` - an integer (default: 0); size of margin to insert
      around the slider

    OUTPUT:

    - a string - HTML format

    EXAMPLES:

    We create a jQuery range slider. If you do the following in the
    notebook you should obtain a slider that when moved pops up a
    window showing its current position::

        sage: from sagenb.notebook.interact import html_rangeslider, html
        sage: html(html_rangeslider('slider-007', 'null', 'alert(pos[0]+", "+pos[1])', steps=5, default_l=2, default_r=3, margin=5))
        <html>...slider...range...</html>
    """
    val_html = ''
    if values != 'null':
        val_html = """
          <tr>
            <td>
              <font color="black" id="%s-lbl"></font>
            </td>
          </tr>""" % id

    s = """
        <table>
          <tr>
            <td>
              <div id="%s" style="margin:%spx; margin-left: 1.0em; margin-right: 1.0em; width: 20.0em;"></div>
            </td>
          </tr>
          %s
        </table>
        """ % (id, int(margin), val_html)


    # We now generate javascript that gets run after the above div
    # gets inserted. This happens because of the setTimeout function
    # below which gets passed an anonymous function.
    s += """<script>
         (function() {
             var values = %s, pos = [%s, %s], sel = '#%s', updatePos;
             updatePos = function() {
                 pos[0] = $(sel).slider('values', 0);
                 pos[1] = $(sel).slider('values', 1);
                 if (values != null) {
                     $(sel + '-lbl').text('(' + values[pos[0]] + ', ' +
                                          values[pos[1]] + ')');
                 }
             };
             setTimeout(function() {
                 $(sel).slider({
                     range: true,
                     step: 1,
                     min: 0,
                     max: %s,
                     values: [%s, %s],
                     change: function (e, ui) {
                         updatePos();
                         %s;
                     },
                     slide: updatePos
                 });
                 updatePos();
             }, 1);
         })();
         </script>""" % (values, default_l, default_r, id, steps - 1,
                         default_l, default_r, callback)

    # Note: Change 'change' to 'slide' and it changes the slider every
    # time it moves; needs much more work to actually work, since
    # server gets flooded by requests.
    
    return s


def html_color_selector(id, change, input_change, default='000000', 
                        widget='colorpicker', hide_box=False):
    """
    Return HTML representation of a jQuery color selector.

    INPUT:

    - ``id`` - an integer or string; an identifier (e.g., cell ID) for
      this selector

    - ``change`` - a string; JavaScript code to execute when the color
      selector changes.

    - ``default`` - a string (default: ``'000000'``); default color as
      a 6-character HTML hexadecimal string.

    - ``widget`` - a string (default: 'colorpicker'); the color
      selector widget to use; choices are 'colorpicker', 'farbtastic' and
      'jpicker' (currently broken)

    - ``hide_box`` - a boolean (default: False); whether to hide the
      input box associated with the color selector widget

    OUTPUT:

    - a string - HTML that creates the slider.

    EXAMPLES::

        sage: sagenb.notebook.interact.html_color_selector(0, 'alert("changed")', '', default='0afcac')
        '...<table>...colorpicker...'
        sage: sagenb.notebook.interact.html_color_selector(99, 'console.log(color);', '', default='fedcba', widget='farbtastic', hide_box=True)
        '...<table>...farbtastic...'
    """
    input_style = ''
    if hide_box:
        input_style = 'display: none;'

    if widget == 'farbtastic':
        # HTML
        s = """
            <table>
              <tr>
                <td>
                  <div id="%s-picker"></div>
                </td>
                <td>
                  <input type="text"
                         id="%s"
                         name="color"
                         onchange="%s; $(this).css({backgroundColor: this.value, color: ''}); $.farbtastic('#%s-picker').setColor(this.value);"
                         value="%s"
                         style="%s"/>
                </td>
              </tr>
            </table>""" % (id, id, input_change, id, default, input_style)

        # We now generate javascript that gets run after the above div
        # gets inserted. This happens because of the setTimeout
        # function below which gets passed an anonymous function.
        # Figuring out the below took understanding jQuery much
        # better, and took me surprisingly long, especially the part
        # involving linkTo which sets the callback.
        s += """<script>
             setTimeout(function () {
                 var input = $('#%s'), picker = $.farbtastic('#%s-picker');
                 picker.linkTo('#%s').linkTo(function (color) {
                     if (input.val() !== color) {
                         input.val(color);
                         input.css({
                             backgroundColor: color,
                             color: picker.hsl[2] > 0.5 ? '#000000' : '#ffffff'
                         });
                         %s;
                     }
                 })
             }, 1);
             </script>""" % (id, id, id, change)

    elif widget == 'colorpicker':
        # HTML
        select = 'background: url(/javascript/jquery/plugins/colorpicker/images/select.png)'
        style1 = 'position: relative; width: 36px; height: 36px; %s;' % select
        style2 = 'position: absolute; top: 3px; left: 3px; width: 30px; height: 30px; %s center; background-color: %s' % (select, default)

        s = """
            <table>
              <tr>
                <td>
                  <input type="text"
                         id="%s"
                         name="color"
                         onchange="%s; $(this).css({backgroundColor: this.value}); $('#%s-picker').ColorPickerSetColor(this.value.slice(1)); $('#%s-picker div').css({backgroundColor: this.value});"
                         value="%s"
                         style="%s" />
                </td>
                <td>
                  <div id="%s-picker" style="%s">
                    <div style="%s"></div>
                  </div>
                </td>
              </tr>
            </table>""" % (id, input_change, id, id, default, input_style,
                           id, style1, style2)

        # JS
        s += """<script>
             setTimeout(function () {
                 var def = '%s'.slice(1), div = $('#%s-picker div'),
                     input = $('#%s'), picker = $('#%s-picker');
                 input.css({
                     backgroundColor: '%s',
                     // Should be good enough:
                     color: (parseInt(def.slice(0, 2), 16) + parseInt(def.slice(2, 4), 16) + parseInt(def.slice(4, 6), 16)) / 3 > 127 ? '#000000' : '#ffffff'
                 });
                 picker.ColorPicker({
                     color : '%s',
                     onShow : function (pkr) {
                         $(pkr).css({zIndex: '10'}).show();
                         return false;
                     },
                     onChange : function (hsb, hex, rgb) {
                         color = '#' + hex;
                         if (input.val() !== color) {
                             input.val(color);
                             input.css({
                                 backgroundColor: color,
                                 color: hsb.b > 50 ? '#000000' : '#ffffff'
                             });
                             div.css({backgroundColor: color});
                             %s;
                         }
                     },
                     onSubmit : function (hsb, hex, rgb, el) {
                         $(el).ColorPickerHide();
                     }
                 });
             }, 1);
             </script>""" % (default, id, id, id, default, default, change)

    elif widget == 'jpicker':
        # HTML.
        s = """
            <table>
              <tr>
                <td>
                  <input type="text"
                         id="%s"
                         name="color"
                         onchange="%s; $(this).css({backgroundColor: this.value}); $('.jPicker_HexText', $('#%s-picker').next()).val(this.value.slice(1)).trigger('keyup');"
                         value="%s"
                         style="%s" />
                </td>
                <td>
                  <div id="%s-picker"></div>
                </td>
              </tr>
            </table>
            """ % (id, input_change, id, default, input_style, id)

        # JS.  Note: jPicker uses, e.g., 'abcdef' instead of '#abcdef'
        # for bound input fields.  To maintain consistency with the
        # Farbtastic and ColorPicker implementations above, we do not
        # simply call $('#%s').jPicker({...}).
        s += """<script>
             setTimeout(function () {
                 var def = '%s'.slice(1), input = $('#%s'),
                     picker = $('#%s-picker');
                 input.css({
                     backgroundColor: '%s',
                     // Should be good enough:
                     color: (parseInt(def.slice(0, 2), 16) + parseInt(def.slice(2, 4), 16) + parseInt(def.slice(4, 6), 16)) / 3 > 127 ? '#000000' : '#ffffff'
                 });
                 picker.jPicker(
                     // Settings.
                     {
                         window: {
                             expandable: true,
                             position: { x: 20, y: 20 },
                             title: 'Select a color'
                         },
                         color: {
                             active: '%s'
                         },
                         images: {
                             clientPath: '/javascript/jquery/plugins/jpicker/images/'
                         }
                     },
                     // commitCallback
                     function (color) {},
                     // liveCallback
                     function (color_arg) {
                         color = '#' + color_arg.hex;
                         if (input.val() !== color) {
                             input.val(color);
                             input.css({
                                 backgroundColor: color,
                                 color: color_arg.v > 50 ? '#000000' : '#ffffff'
                             });
                             %s;
                         }
                     },
                     // cancelCallback
                     function (color) {}
                 );
             }, 1);
             </script>""" % (default, id, id, default, default, change)

    return s


class InteractElement(object):
    def label(self):
        """
        Returns an empty label for this element. This should be
        overridden for subclasses that need a label.

        OUTPUT:

        - a string

        EXAMPLES::

            sage: from sagenb.notebook.interact import UpdateButton, InteractElement
            sage: b = UpdateButton(1, 'autoupdate')
            sage: isinstance(b, InteractElement)
            True
            sage: b.label()
            ''
        """
        return ""

    def set_canvas(self, canvas):
        """
        Sets the :class:`InteractCanvas` on which this element
        appears.  This method is primarily called in the constructor
        for :class:`InteractCanvas`.

        EXAMPLES::

            sage: from sagenb.notebook.interact import InputBox, InteractCanvas
            sage: B = InputBox('x',2)
            sage: canvas1 = InteractCanvas([B], 3)
            sage: canvas2 = InteractCanvas([B], 3)
            sage: B.canvas() is canvas2
            True
            sage: B.set_canvas(canvas1)
            sage: B.canvas() is canvas1
            True
        """
        self._canvas = canvas

    def canvas(self):
        """
        Returns the :class:`InteractCanvas` associated to this
        element.  If no canvas has been set (via the
        :meth:`set_canvas` method), then raise a ValueError.

        EXAMPLES::

            sage: from sagenb.notebook.interact import InputBox, InteractCanvas
            sage: B = InputBox('x',2)
            sage: canvas1 = InteractCanvas([B], 3)
            sage: canvas2 = InteractCanvas([B], 3)
            sage: B.canvas() is canvas2
            True
        """
        if hasattr(self, '_canvas'):
            return self._canvas
        else:
            raise ValueError("this element does not have a canvas associated with it")


class InteractControl(InteractElement):
    def __init__(self, var, default_value, label=None):
        """
        Abstract base class for :func:`interact` controls.  These are
        controls that are used in a specific :func:`interact`.  They
        have internal state information about the specific function
        being interacted, etc.
        
        INPUT:

        - ``var`` - a string; name of variable that this control
          interacts

        - ``default_value`` - the default value of the variable
          corresponding to this control.

        - ``label`` - a string (default: None); label for this
          control; if None then defaults to ``var``.

        EXAMPLES::

            sage: from sagenb.notebook.interact import InteractControl
            sage: InteractControl('x', default_value=5)
            A InteractControl (abstract base class)
        """
        self.__var = var
        self.__cell_id = SAGE_CELL_ID
        self.__default_value = default_value
        self.__adapt_number = new_adapt_number()
        if label is None:
            self.__label = var
        else:
            self.__label = label

        InteractElement.__init__(self)

    def __repr__(self):
        """
        String representation of :func:`interact` control.

        OUTPUT:

        - a string

        EXAMPLES::

            sage: from sagenb.notebook.interact import InteractControl
            sage: InteractControl('x', default_value=5).__repr__()
            'A InteractControl (abstract base class)'
        """
        return "A InteractControl (abstract base class)"

    def value_js(self):
        """
        JavaScript that when evaluated gives the current value of this
        control.  This should be redefined in a derived class.

        OUTPUT:

        - a string - defaults to 'NULL' - this should be redefined.

        EXAMPLES::

            sage: sagenb.notebook.interact.InteractControl('x', default_value=5).value_js()
            'NULL'
        """
        return 'NULL'

    def label(self):
        """
        Return the text label of this :func:`interact` control.

        OUTPUT:

        - a string

        EXAMPLES::

            sage: from sagenb.notebook.interact import InteractControl
            sage: InteractControl('x', default_value=5, label='the x value').label()
            'the x value'
        """
        return self.__label

    def default_value(self):
        """
        Return the default value of the variable corresponding to this
        :func:`interact` control.

        OUTPUT:

        - an object

        EXAMPLES::

            sage: from sagenb.notebook.interact import InteractControl
            sage: InteractControl('x', 19/3).default_value()
            19/3
        """
        return self.__default_value

    def html_escaped_default_value(self):
        """
        Returns the HTML escaped default value of the variable
        corresponding to this :func:`interact` control.  Note that any
        HTML that uses quotes around this should use double quotes and
        not single quotes.

        OUTPUT:

        - a string

        EXAMPLES::

            sage: from sagenb.notebook.interact import InteractControl
            sage: InteractControl('x', '"cool"').html_escaped_default_value()
            '&quot;cool&quot;'
            sage: InteractControl('x',"'cool'").html_escaped_default_value()
            "'cool'"
            sage: x = var('x')
            sage: InteractControl('x', x^2).html_escaped_default_value()
            'x^2'
        """
        s = self.default_value()
        if not isinstance(s, str):
            s = repr(s)
        import cgi
        return cgi.escape(s, quote=True)

    def adapt_number(self):
        """
        Return integer index into adapt dictionary of function that is
        called to adapt the values of this control to Python.

        OUTPUT:

        - an integer

        EXAMPLES::

            sage: from sagenb.notebook.interact import InteractControl
            sage: InteractControl('x', 19/3).adapt_number()       # random -- depends on call order
            2
        """
        return self.__adapt_number

    def _adaptor(self, value, globs):
        """
        Adapt a user input, which is a string, to be an element selected
        by this control.

        INPUT:

        - ``value`` - the string the user typed in

        - ``globs`` - a string:object dictionary; the globals
          interpreter variables, e.g., :func:`globals`, which is
          useful for evaluating value.

        OUTPUT:

        - an object

        EXAMPLES::

            sage: sagenb.notebook.interact.InteractControl('x', 1)._adaptor('2/3', globals())
            2/3
        """
        return sage_eval(value, globs)

    def interact(self, *args):
        r"""
        Return a string that when evaluated in JavaScript calls the
        JavaScript :func:`interact` function with appropriate inputs for
        this control.

        This method will check to see if there is a canvas attached to
        this control and whether or not controls should automatically
        update the output when their values change.  If no canvas is
        associated with this control, then the control will
        automatically update.

        OUTPUT:

        - a string - that is meant to be evaluated in JavaScript

        EXAMPLES::

            sage: sagenb.notebook.interact.InteractControl('x', 1).interact()
            "...interact...x...1..."
        """
        # We have to do a try/except block here since the control may
        # not have a canvas associated with it.
        try:
            auto_update = self.canvas().is_auto_update()
        except ValueError:
            auto_update = True

        recompute = 0

        if auto_update:
            recompute = 1

        update = "{variable: '%s', adapt_number: %s, value: encode64(%s)}" % (
            self.var(), self.adapt_number(), self.value_js(*args))

        return "interact(%r, %s, %s)" % (self.cell_id(), update, recompute)
   
    def var(self):
        """
        Return the name of the variable that this control interacts.

        OUTPUT:

        - a string - name of a variable as a string.

        EXAMPLES::

            sage: sagenb.notebook.interact.InteractControl('theta', 1).var()
            'theta'
        """
        return self.__var

    def cell_id(self):
        """
        Return the ID of the cell that contains this :func:`interact` control.

        OUTPUT:

        - an integer or a string

        EXAMPLES:

        The output below should equal the ID of the current cell::

            sage: sagenb.notebook.interact.InteractControl('theta', 1).cell_id()
            0
        """
        return self.__cell_id


class InputBox(InteractControl):
    def __init__(self, var, default_value, label=None, type=None, width=80, height = 1, **kwargs):
        """
        An input box :func:`interact` control.

        INPUT:

        - ``var`` - a string; name of variable that this control
          interacts

        - ``default_value`` - the default value of the variable
          corresponding to this control

        - ``label`` - a string (default: None); label for this
          control

        - ``type`` - a type (default: None); the type of this control,
          e.g., the type 'bool'

        - ``height`` - an integer (default: 1); the number of rows.  
          If greater than 1 a value won't be returned until something
          outside the textarea is clicked.

        - ``width`` - an integer (default: 80); the character width of
          this control

        - ``kwargs`` - a dictionary; additional keyword options

        EXAMPLES::

            sage: sagenb.notebook.interact.InputBox('theta', 1, 'theta')
            An InputBox interactive control with theta=1 and label 'theta'
            sage: sagenb.notebook.interact.InputBox('theta', 1, 'theta', int)
            An InputBox interactive control with theta=1 and label 'theta'
        """
        InteractControl.__init__(self, var, default_value, label)
        self.__type = type
        self.__width = width
        self.__height = height
        self._kwargs = kwargs

    def __repr__(self):
        """
        String representation of an :class:`InputBox` interactive control.

        OUTPUT:

        - a string

        EXAMPLES::

            sage: sagenb.notebook.interact.InputBox('theta', 1).__repr__()
            "An InputBox interactive control with theta=1 and label 'theta'"
        """
        return 'An InputBox interactive control with %s=%r and label %r' % (
            self.var(), self.default_value(), self.label())

    def _adaptor(self, value, globs):
        """
        Adapt a user input, which is the text they enter, to be an
        element selected by this control.

        INPUT:

        - ``value`` - text entered by user

        - ``globs`` - a string:object dictionary; the :func:`globals`
          interpreter variables (not used here).

        OUTPUT:

        - an object

        EXAMPLES::

            sage: sagenb.notebook.interact.InputBox('theta', Color('red'), type=Color)._adaptor('#aaaaaa',globals())
            RGB color (0.66..., 0.66..., 0.66...)
        """
        if self.__type is None:
            return sage_eval(value, globs)
        elif self.__type is str:
            return value
        elif self.__type is Color:
            try:
                return Color(value)
            except ValueError:
                try:
                    return Color('#' + value)
                except ValueError:
                    print("Invalid color '%s', using default Color()" % value)
                    return Color()
        else:
            return self.__type(sage_eval(value, globs))

    def value_js(self):
        """
        Return JavaScript string that will give the value of this
        control element.

        OUTPUT:

        - a string - JavaScript

        EXAMPLES::

            sage: sagenb.notebook.interact.InputBox('theta', 1).value_js()
            'this.value'
        """
        if self.__type is bool:
            return 'this.checked'
        else:
            return 'this.value'

    def render(self):
        r"""
        Render this control as a string.

        OUTPUT:

        - a string - HTML format

        EXAMPLES::

            sage: sagenb.notebook.interact.InputBox('theta', 1).render()
            '...input...value="1"...theta...'
        """
        if self.__type is bool:
            return """<input type="checkbox" %s width=200px onchange="%s"></input>"""%(
                'checked' if self.default_value() else '',  self.interact())
        elif self.__type is str and self.__height ==1:
            return """<input type="text" value="%s" size=%s onchange="%s"></input>"""%(
                self.html_escaped_default_value(), self.__width, self.interact())
        elif self.__type is str and self.__height > 1:
            textval = self.html_escaped_default_value()
            return """<textarea type="text" value="%s" rows="%s" cols="%s" onchange="%s">%s</textarea>"""%(
                textval, self.__height, self.__width, self.interact(), textval)
        else:
            return """<input type="text" value="%s" size=%s onchange="%s"></input>"""%(
                self.html_escaped_default_value(), self.__width,  self.interact())

class ColorInput(InputBox):
    def value_js(self, n):
        """
        Return JavaScript that evaluates to value of this control.
        If ``n`` is 0, return code for evaluation by the actual color
        control.  If ``n`` is 1, return code for the text area that
        displays the current color.

        INPUT:

        - ``n`` - integer, either 0 or 1.

        OUTPUT:

        - a string

        EXAMPLES::

            sage: C = sagenb.notebook.interact.ColorInput('c', Color('red'))
            sage: C.value_js(0)
            'color'
            sage: C.value_js(1)
            'this.value'
        """
        if n == 0:
            return 'color'
        else:
            return 'this.value'

    def render(self):
        """
        Render this color input box to HTML.

        OUTPUT:

        - a string - HTML format

        EXAMPLES::

            sage: sagenb.notebook.interact.ColorInput('c', Color('red')).render()
            '...table...color...'
        """
        return html_color_selector('color-selector-%s-%s' % (self.var(),
                                                             self.cell_id()),
                                   change=self.interact(0),
                                   input_change=self.interact(1),
                                   default=self.default_value().html_color(),
                                   **self._kwargs)


class InputGrid(InteractControl):
    def __init__(self, var, rows, columns, default_value=None, label=None, 
                 to_value=lambda x: x, width=4):
        """
        A grid interact control.

        INPUT:

        - ``var`` - an object; the variable

        - ``rows`` - an integer; the number of rows

        - ``columns`` - an integer; the number of columns

        - ``default_value`` - an object; if this is a scalar, it is
          put in every cell; if it is a list, it is filled into the
          cells row by row; if it is a nested list, then it is filled
          into the cells according to the nesting structure.

        - ``label`` - a string; the label for the control

        - ``to_value`` - a function applied to the nested list from
          user input when assigning the variable

        - ``width`` - an integer; the width of the input boxes

        EXAMPLES::

            sage: sagenb.notebook.interact.InputGrid('M', 2,2, default_value = 0, label='M')
            A 2 x 2 InputGrid interactive control with M=[[0, 0], [0, 0]] and label 'M'
            sage: sagenb.notebook.interact.InputGrid('M', 2,2, default_value = [[1,2],[3,4]], label='M')
            A 2 x 2 InputGrid interactive control with M=[[1, 2], [3, 4]] and label 'M'
            sage: sagenb.notebook.interact.InputGrid('M', 2,2, default_value = [[1,2],[3,4]], label='M', to_value=MatrixSpace(ZZ,2,2))
            A 2 x 2 InputGrid interactive control with M=[1 2]
            [3 4] and label 'M'
            sage: sagenb.notebook.interact.InputGrid('M', 1, 3, default_value=[1,2,3], to_value=lambda x: vector(flatten(x)))
            A 1 x 3 InputGrid interactive control with M=(1, 2, 3) and label 'M'
        """
        self.__rows = rows
        self.__columns = columns
        self.__to_value = to_value
        self.__width = width

        if type(default_value) != list:
            default_value = [[default_value for _ in range(columns)] for _ in range(rows)]
        elif not all(type(elt) == list for elt in default_value):
            default_value = [[default_value[i * columns + j] for j in xrange(columns)] for i in xrange(rows)]

        self.__default_value_grid = default_value

        InteractControl.__init__(self, var, self.__to_value(default_value), label)

    def __repr__(self):
        """
        String representation of an :class:`InputGrid` interactive control.

        OUTPUT:

        - a string

        EXAMPLES::

            sage: sagenb.notebook.interact.InputGrid('M', 2,2).__repr__()
            "A 2 x 2 InputGrid interactive control with M=[[None, None], [None, None]] and label 'M'"
        """
        
        return 'A %r x %r InputGrid interactive control with %s=%r and label %r' % ( 
            self.__rows, self.__columns, self.var(),  self.default_value(), 
            self.label())

    
    def _adaptor(self, value, globs):
        """
        Adapt a user input, which is the text they enter, to be an
        element selected by this control.

        INPUT:

        - ``value`` - text entered by user

        - ``globs`` - a string:object dictionary; the :func:`globals`
          interpreter variables (not used here).

        OUTPUT:

        - an object

        EXAMPLES::

            sage: sagenb.notebook.interact.InputGrid('M', 1,3, default_value=[[1,2,3]], to_value=lambda x: vector(flatten(x)))._adaptor("[[4,5,6]]", globals())
            (4, 5, 6)
        """
        return self.__to_value(sage_eval(value, globs))

    def value_js(self):
        """
        Return JavaScript string that will give the value of this
        control element.

        OUTPUT:

        - string - JavaScript

        EXAMPLES::

            sage: sagenb.notebook.interact.InputGrid('M', 2,2).value_js()
            "...jQuery...table...map...val...join..."
        """
        # Basically, given an input element in a table, it constructs
        # a python string representation of a list of lists from the
        # rows in the table.
        return """
        '[[' +
            jQuery(this).parents('table').eq(0).find('tr').map(function() {
                return jQuery(this).find('input').map(function() {
                    return jQuery(this).val();
                }).get().join(',');
            }).get().join('],[') +
        ']]' """

    def render(self):
        """
        Render this control as a string.

        OUTPUT:

        - string - HTML format

        EXAMPLES::

            sage: sagenb.notebook.interact.InputGrid('M', 1,2).render()
            '...table...input...M...'
        """
        s = "<table>"

        for i in range(self.__rows):
            s += "  <tr>"
            for j in range(self.__columns):
                s += '    <td>\n'
                s += '      <input type="text" value="%r" size="%s" onchange="%s" />\n' % (self.__default_value_grid[i][j], self.__width, self.interact())
                s += '    </td>\n'
            s += '  </tr>\n'
        s += '</table>\n'

        return s
   

class Selector(InteractControl):
    def __init__(self, var, values, label=None, default=0,
                 nrows=None, ncols=None, width=None, buttons=False):
        """
        A drop down menu or a button bar that when pressed sets a
        variable to a given value.

        INPUT:

        - ``var`` - a string; variable name

        - ``values`` - a list; button values

        - ``label`` - a string (default: None); label off to the left
          for this button group

        - ``default`` - an integer (default: 0); position of default
          value in values list.

        - ``nrows`` - an integer (default: None); number of rows

        - ``ncols`` - an integer (default: None); number of columns

        - ``width`` - an integer (default: None); width of all the
          buttons

        - ``buttons`` - a bool (default: False); if True use buttons
            instead of dropdown

        EXAMPLES::

            sage: sagenb.notebook.interact.Selector('x', [1..5], 'alpha', default=2)
            Selector with 5 options for variable 'x'
            sage: sagenb.notebook.interact.Selector('x', [1..4], 'alpha', default=2, nrows=2, ncols=2, width=10, buttons=True)
            Selector with 4 options for variable 'x'
        """
        if (len(values) > 0 and isinstance(values[0], tuple) and 
            len(values[0]) == 2):
            vals = [z[0] for z in values]
            lbls = [repr(z[1]) if z[1] is not None else None for z in values]
        else:
            vals = values
            lbls = [None] * len(vals)

        default = int(default)
        if default < 0 or default >= len(vals):
            default = 0

        InteractControl.__init__(self, var, vals[default], label)
        
        self.__default = default
        self.__buttons = buttons
        self.__values = vals
        self.__labels = lbls
        if nrows is None:
            if ncols is not None:
                nrows = len(values) // ncols
                if ncols * nrows < len(values):
                    nrows += 1
            else:
                nrows = 1 # temporary
        else:
            nrows = int(nrows)
            if nrows <= 0:
                nrows = 1
        if ncols is None:
            ncols = len(values) // nrows
            if ncols * nrows < len(values):
                ncols += 1
                
        self.__nrows = nrows
        self.__ncols = ncols

        if width is not None:
            self.__width = "width:%sex;" % width
        else:
            self.__width = ''

        self.__selected = 'background-color:orange;'

    def __repr__(self):
        """
        String representation of a :class:`Selector` interactive control.

        OUTPUT:

        - a string

        EXAMPLES::

            sage: sagenb.notebook.interact.Selector('x', [1..5]).__repr__()
            "Selector with 5 options for variable 'x'"
        """
        return "Selector with %s options for variable '%s'" % (
            len(self.__values), self.var())

    def _adaptor(self, value, globs):
        """
        Adapt value of button or menu selection.  The button value is
        just an integer, and this function adapts it to be the value
        that we associate with that button.

        INPUT:

        - ``value` - an integer; value sent in via JavaScript

        - ``globs`` - a string:object dictionary; the :func:`globals`
          interpreter variables (not used here).

        OUTPUT:

        - an object

        EXAMPLES::

            sage: S = sagenb.notebook.interact.Selector('x', ['first',x^3+5])
            sage: S._adaptor(0,globals())
            'first'
            sage: S._adaptor(1,globals())
            x^3 + 5
        """
        return self.__values[int(value)]

    def use_buttons(self):
        """
        Whether or not to use buttons instead of a drop down menu for
        this select list.

        OUTPUT:

        - a bool

        EXAMPLES::

            sage: sagenb.notebook.interact.Selector('x', [1..5]).use_buttons()
            False
            sage: sagenb.notebook.interact.Selector('x', [1..5], buttons=True).use_buttons()
            True
        """
        return self.__buttons

    def value_js(self):
        """
        Return JavaScript string that will give the value of this
        control element.

        OUTPUT:

        - a string - JavaScript

        EXAMPLES::

            sage: sagenb.notebook.interact.Selector('x', [1..5]).value_js()
            'this.options[this.selectedIndex].value'
            sage: sagenb.notebook.interact.Selector('x', [1..5], buttons=True).value_js()
            'this.value'
        """
        if self.use_buttons():
            return 'this.value'
        else:
            # Now we have to use a option selector.
            return 'this.options[this.selectedIndex].value'

    def render(self):
        """
        Render this control as a string.

        OUTPUT:

        - a string - HTML format

        EXAMPLES::

            sage: sagenb.notebook.interact.Selector('x', [1..5]).render()
            '...select...x...'
            sage: sagenb.notebook.interact.Selector('x', [1..5], buttons=True).render()
            '...table...button...x...'
        """
        width = self.__width
        vals = self.__values
        lbls = self.__labels
        default = self.__default
        label = self.label()
        use_buttons = self.use_buttons()
        event = self.interact()
        if use_buttons:
            #On selected buttons, border is set to inset, on
            #unselected boxes - outset. This usually is default
            #rendering.
            if len(vals) > 1:
                    event = """$('BUTTON', this.parentNode).css('border-style', 'outset'); $(this).css('border-style', 'inset'); %s""" % event
            s = """<table style="border:1px solid #dfdfdf; background-color:#efefef;">"""
        else:
            s = """<select onchange="%s;">""" % event
        i = 0
        for r in range(self.__nrows):
            if use_buttons:
                s += '\n<tr><td>'
            for c in range(self.__ncols):
                if i >= len(vals):
                    i += 1
                    continue
                style = width
                #if i == default:
                #    style += self.__selected
                if lbls[i] is None:
                    if isinstance(vals[i], str):
                        lbl = vals[i]
                    else:
                        lbl = repr(vals[i])
                else:
                    lbl = lbls[i]
                if use_buttons:
                    s += """<button style="%s%s" value="%s" onclick="%s">%s</button>\n""" % ('border-style:inset;' if i == default and len(vals) > 1 else 'border-style:outset;', style, i, event, lbl)
                else:
                    s += '<option value="%s" %s>%s</option>\n' % (i, 'selected' if i == default else '', lbl)
                i += 1
            if use_buttons:
                s += '</td></tr>'
        if use_buttons:
            s += '</table>'
        else:
            s += '</select>'
        return s

    
class SliderGeneric(InteractControl):
    def __init__(self, var, values, default_value, label=None, 
                 display_value=True):
        """
        An abstract slider :func:`interact` control that takes on the
        given list of values.

        INPUT:

        - ``var`` - a string; name of variable being interacted

        - ``values`` - a list; a list of the values that the slider
            will take on

        - ``default_value`` - an object; default value of the slider.

        - ``label`` - a string; alternative label to the left of the
          slider, instead of the variable.

          - ``display_value`` - a bool; whether to display the current
            value on the slider

        EXAMPLES::

            sage: sagenb.notebook.interact.SliderGeneric('x', [1..5], 2, 'alpha')
            Abstract Slider Interact Control: alpha [1--|2|---5]
        """
        InteractControl.__init__(self, var, default_value, label=label)
        self.__values = values
        self.__display_value = display_value

    def __repr__(self):
        """
        Return string representation of this slider control.

        OUTPUT:

        - a string

        EXAMPLES::

            sage: sagenb.notebook.interact.SliderGeneric('x', [1..5], 2, 'alpha').__repr__()
            'Abstract Slider Interact Control: alpha [1--|2|---5]'
        """
        return "Abstract Slider Interact Control: %s [%s--|%s|---%s]" % (
            self.label(), self.__values[0],
            self.default_value(), self.__values[-1])

    def values(self):
        """
        Return list of values the slider acts on.

        OUTPUT:

        - a list

        EXAMPLES::

            sagenb.notebook.interact.Slider('x', [1..5], 2, 'alpha').values()
            [1, 2, 3, 4, 5]
        """
        return self.__values

    def display_value(self):
        """
        Returns whether to display the value on the slider.

        OUTPUT:

        - a bool

        EXAMPLES::

            sagenb.notebook.interact.Slider('x', [1..5], 2, 'alpha').display_value()
            True
        """
        return self.__display_value

    def values_js(self):
        """
        Returns JavaScript array representation of values or 'null' if
        display_value=False

        OUTPUT:

        - a string

        EXAMPLES::

            sage: sagenb.notebook.interact.Slider('x', [1..5], 2, 'alpha').values_js()
            '["1","2","3","4","5"]'
            sage: sagenb.notebook.interact.Slider('x', [1..5], 2, 'alpha', False).values_js()
            'null'
            sage: sagenb.notebook.interact.Slider('x', [pi..2*pi], 2, 'alpha').values_js()
            '["pi","pi + 1","pi + 2","pi + 3"]'
        """
        if self.__display_value == False:
            return "null"
        s = "["
        for i in self.__values:
            ie = repr(i).replace("\\", "\\\\").replace("\"", "\\\"").replace("'", "\\'")
            s += "\"%s\"," % ie
        s = s[:-1] + ']'
        return s


class Slider(SliderGeneric):
    def __init__(self, var, values, default_position, label=None, 
                 display_value=True):
        """
        A slider :func:`interact` control that takes on the given list of
        values.

        INPUT:

        - ``var`` - a string; name of variable being interacted

        - ``values`` - a list; a list of the values that the slider
          will take on

        - ``default_position`` - an integer; default location that the
          slider is set to.

        - ``label`` - a string; alternative label to the left of the
          slider, instead of the variable.

          - ``display_value`` - a bool, whether to display the current
            value right of the slider

        EXAMPLES::

            sage: sagenb.notebook.interact.Slider('x', [1..5], 2, 'alpha')
            Slider Interact Control: alpha [1--|3|---5]
        """
        SliderGeneric.__init__(self, var, values, values[default_position], 
                               label=label, display_value=display_value)
        self.__default_position = default_position

    def __repr__(self):
        """
        Return string representation of this slider control.

        OUTPUT:

        - a string

        EXAMPLES::

            sage: sagenb.notebook.interact.Slider('x', [1..5], 2, 'alpha').__repr__()
            'Slider Interact Control: alpha [1--|3|---5]'
        """
        return "Slider Interact Control: %s [%s--|%s|---%s]" % (
            self.label(), self.values()[0],
            self.default_value(), self.values()[-1])

    def default_position(self):
        """
        Return the default position (as an integer) of the slider.

        OUTPUT:

        - an integer

        EXAMPLES::

            sage: sagenb.notebook.interact.Slider('x', [1..5], 2, 'alpha').default_position()
            2
        """
        return self.__default_position

    def value_js(self):
        """
        Return JavaScript string that will give the value of this
        control element.

        OUTPUT:

        - a string - JavaScript

        EXAMPLES::

            sage: sagenb.notebook.interact.Slider('x', [1..5], 2, 'alpha').value_js()
            'position'
        """
        return "position"

    def _adaptor(self, position, globs):
        """
        Adapt a user input, which is the slider position, to be an
        element selected by this control.

        INPUT:

        - ``position`` - an object; position of the slider

        - ``globs`` - a string:object dictionary; the :func:`globals`
          interpreter variables (not used here).

        OUTPUT:

        - an object

        EXAMPLES::

            sage: sagenb.notebook.interact.Slider('x', [1..5], 2, 'alpha')._adaptor(2,globals())
            3
        """
        v = self.values()
        # We have to cast to int, since it comes back as a float that
        # is too big.
        return v[int(position)]

    def render(self):
        """
        Render this control as an HTML string.

        OUTPUT:

        - a string - HTML format

        EXAMPLES::

            sage: sagenb.notebook.interact.Slider('x', [1..5], 2, 'alpha').render()
            '...table...slider...["1","2","3","4","5"]...'

            sage: sagenb.notebook.interact.Slider('x', [1..5], 2, 'alpha', display_value=False).render()
            '...table...slider...null...'
        """
        
        return html_slider('slider-%s-%s'%(self.var(), self.cell_id()),
                           self.values_js(), self.interact(),
                           steps=len(self.values()),
                           default=self.default_position())


class RangeSlider(SliderGeneric):
    def __init__(self, var, values, default_position, label=None, 
                 display_value=True):
        """
        A range slider :func:`interact` control that takes on the given
        list of values.

        INPUT:

        - ``var`` - a string; name of variable being interacted

        - ``values`` - a list; a list of the values that the slider
          will take on

        - ``default_position`` - an integer 2-tuple; default location
          that the slider is set to.

        - ``label`` - a string; alternative label to the left of the
          slider, instead of the variable.

        - ``display_value`` - a bool, whether to display the current
          value below the slider

        EXAMPLES::

            sage: sagenb.notebook.interact.RangeSlider('x', [1..5], (2,3), 'alpha')
            Range Slider Interact Control: alpha [1--|3==4|---5]
        """
        SliderGeneric.__init__(self, var, values, (values[default_position[0]], values[default_position[1]]), label=label, display_value=display_value)
        self.__default_position = default_position

    def __repr__(self):
        """
        Return string representation of this slider control.

        OUTPUT:

        - a string

        EXAMPLES::

            sage: sagenb.notebook.interact.RangeSlider('x', [1..5], (2,3), 'alpha').__repr__()
            'Range Slider Interact Control: alpha [1--|3==4|---5]'
        """
        return "Range Slider Interact Control: %s [%s--|%s==%s|---%s]" % (
            self.label(), self.values()[0],
            self.default_value()[0], self.default_value()[1], self.values()[-1])

    def default_position(self):
        """
        Return the default position (as an integer) of the slider.

        OUTPUT:

        - an integer 2-tuple

        EXAMPLES::

            sage: sagenb.notebook.interact.RangeSlider('x', [1..5], (2,3), 'alpha').default_position()
            (2, 3)
        """
        return self.__default_position

    def value_js(self):
        """
        Return JavaScript string that will give the value of this
        control element.

        OUTPUT:

        - a string - JavaScript

        EXAMPLES::

            sage: sagenb.notebook.interact.RangeSlider('x', [1..5], (2,3), 'alpha').value_js()
            "pos[0]+' '+pos[1]"
        """
        return "pos[0]+' '+pos[1]"

    def _adaptor(self, position, globs):
        """
        Adapt a user input, which is the slider position, to be an
        element selected by this control.

        INPUT:

        - ``position`` - an object; position of the slider

        - ``globs` - a string:object dictionary; the :func:`globals`
          interpreter variables (not used here).

        OUTPUT:

        - an object

        EXAMPLES::

            sage: sagenb.notebook.interact.RangeSlider('x', [1..5], (2,3), 'alpha')._adaptor("2 3",globals())
            (3, 4)
        """
        v = self.values()
        s = position.split(' ')
        # Use of int() here matches it's use in Slider._adaptor
        return (v[int(s[0])], v[int(s[1])])

    def render(self):
        """
        Render this control as an HTML string.

        OUTPUT:

        - string - HTML format

        EXAMPLES::

            sage: sagenb.notebook.interact.RangeSlider('x', [1..5], (2,3), 'alpha').render()
            '...table...slider...["1","2","3","4","5"]...range...'

            sage: sagenb.notebook.interact.RangeSlider('x', [1..5], (2,3), 'alpha', display_value=False).render()
            '...table...slider...null...range...
        """
        
        return html_rangeslider('slider-%s-%s'%(self.var(), self.cell_id()),
                                self.values_js(), self.interact(), 
                                steps=len(self.values()),
                                default_l=self.default_position()[0], 
                                default_r=self.default_position()[1])


class TextControl(InteractControl):
    def __init__(self, var, data):
        """
        A text field :func:`interact` control

        INPUT:

        - ``data`` - a string; the HTML value of the text field

        EXAMPLES::

            sage: sagenb.notebook.interact.TextControl('x', 'something')
            Text Interact Control: something
        """
        InteractControl.__init__(self, var, data, label='')
        self.__data = data

    def __repr__(self):
        """
        Return string representation of this control.

        OUTPUT:

        - a string

        EXAMPLES::

            sage: sagenb.notebook.interact.TextControl('x', 'something').__repr__()
            'Text Interact Control: something'
        """
        return 'Text Interact Control: %s' % self.default_value()

    def render(self):
        """
        Render this control as an HTML string.

        OUTPUT:

        - a string - HTML format

        EXAMPLES::

            sage: sagenb.notebook.interact.TextControl('x', 'something').render()
            '...div...something...'
        """
        return '<div style="color:black; padding-bottom:5px">%s</div>' % self.default_value()


class InteractCanvas(object):
    def __init__(self, controls, id, layout=None, width=None, **options):
        """
        Base class for :func:`interact` canvases. This is where all
        the controls along with the output of the interacted function
        are laid out and rendered.

        INPUT:

        - ``controls`` - a list of :class:`InteractControl` instances.

        - ``id`` - an integer or a string; the ID of the cell that
          contains this InteractCanvas.

        - ``layout`` - a dictionary with keys
          'top','bottom','left','right' and values lists of rows of
          control variable names.  If a dictionary is not passed in,
          then the value of layout is set to the 'top' value.  If
          ``None``, then all control names are put on separate rows in
          the 'top' value.

        - ``width`` - the width of the interact control

        - ``options`` - any additional keyword arguments (for example,
          auto_update=False)

        EXAMPLES::

            sage: B = sagenb.notebook.interact.InputBox('x',2)
            sage: sagenb.notebook.interact.InteractCanvas([B], 3)
            Interactive canvas in cell 3 with 1 controls
        """
        for control in controls:
            control.set_canvas(self)

        self.__controls = controls
        self.__cell_id = id
        self.__width = width
        self.__options = options

        if layout is None:
            layout = [[c.var()] for c in self.__controls]
        if not isinstance(layout, dict):
            layout={'top': layout}
        self.__layout = layout


    def __repr__(self):
        """
        Print representation of an interactive canvas.

        OUTPUT:

        - a string

        EXAMPLES::

            sage: B = sagenb.notebook.interact.InputBox('x',2)
            sage: sagenb.notebook.interact.InteractCanvas([B], 3).__repr__()
            'Interactive canvas in cell 3 with 1 controls'
        """
        return "Interactive canvas in cell %s with %s controls" % (
            self.__cell_id, len(self.__controls))

    def cell_id(self):
        """
        Return the ID of the cell that contains this :func:`interact` control.

        OUTPUT:

        - an integer or a string

        EXAMPLES:

        The output below should equal the ID of the current cell::

            sage: B = sagenb.notebook.interact.InputBox('x',2)
            sage: C = sagenb.notebook.interact.InteractCanvas([B], 3); C
            Interactive canvas in cell 3 with 1 controls
            sage: C.cell_id()
            3
        """
        return self.__cell_id

    def is_auto_update(self):
        r"""
        Returns True if any change of the values for the controls on
        this canvas should cause an update.  If ``auto_update=False`` was
        not specified in the constructor for this canvas, then this
        will default to True.

        OUTPUT:

        - a bool

        EXAMPLES::

            sage: B = sagenb.notebook.interact.InputBox('x',2)
            sage: canvas = sagenb.notebook.interact.InteractCanvas([B], 3)
            sage: canvas.is_auto_update()
            True
            sage: canvas = sagenb.notebook.interact.InteractCanvas([B], 3, auto_update=False)
            sage: canvas.is_auto_update()
            False
        """
        return self.__options.get('auto_update', True)

    def controls(self):
        """
        Return a list of controls in this canvas.

        OUTPUT:

        - list of controls

        .. note::

            Returns a reference to a mutable list.

        EXAMPLES::

            sage: B = sagenb.notebook.interact.InputBox('x',2)
            sage: sagenb.notebook.interact.InteractCanvas([B], 3).controls()
            [An InputBox interactive control with x=2 and label 'x']
        """
        return self.__controls
    
    def render_output(self):
        """
        Render in text (HTML) form the output portion of the
        :func:`interact` canvas.

        The output contains two special tags, <?TEXT> and <?HTML>,
        which get replaced at runtime by the text and HTML parts
        of the output of running the function.

        OUTPUT:

        - a string - HTML format

        EXAMPLES::

            sage: B = sagenb.notebook.interact.InputBox('x',2)
            sage: sagenb.notebook.interact.InteractCanvas([B], 3).render_output()
            '...div...interact...3...'
        """
        return """
        <div id="cell-interact-{0}">{1}
          <table border=0 bgcolor="white" width=100%>
            <tr>
              <td bgcolor="white" align=left valign=top>
                <pre>{2}</pre>
              </td>
            </tr>
            <tr>
              <td align=left valign=top>{3}</td>
            </tr>
          </table>{4}
        </div>""".format(self.cell_id(), INTERACT_START, INTERACT_TEXT,
                         INTERACT_HTML, INTERACT_END)

    def render_controls(self, side='top'):
        """
        Render in text (HTML) form all the input controls.

        OUTPUT:

        - a string - HTML format

        EXAMPLES::

            sage: B = sagenb.notebook.interact.InputBox('x',2)
            sage: sagenb.notebook.interact.InteractCanvas([B], 3).render_controls()
            '...table...x...input...2...'
        """
        if side not in self.__layout:
            return ''

        tbl_body = ''
        controls = dict([c.var(), c] for c in self.__controls)
        for row in self.__layout[side]:
            tbl_body += '<tr>'
            for c_name in row:
                c = controls[c_name]
                if c.label() == '':
                    tbl_body += '<td colspan=2>{0}</td>\n'.format(c.render())
                else:
                    tbl_body += '<td align=right><font color="black">{0}&nbsp;</font></td><td>{1}</td>\n'.format(c.label(), c.render())
                    
            tbl_body += '</tr>'

        return '<table>' + tbl_body + '</table>'

    def wrap_in_outside_frame(self, inside):
        """
        Return the entire HTML for the interactive canvas, obtained by
        wrapping all the inside HTML of the canvas in a div and a
        table.

        INPUT:

        - ``inside`` - a string; HTML

        OUTPUT:

        - a string - HTML format
            
        EXAMPLES::

            sage: B = sagenb.notebook.interact.InputBox('x',2)
            sage: sagenb.notebook.interact.InteractCanvas([B], 3).wrap_in_outside_frame('<!--inside-->')
            '...notruncate...div...interact...table...inside...'
        """
        return """<!--notruncate-->
        <div padding=6 id="div-interact-%s">
          <table width=800px height=20px bgcolor="#c5c5c5" cellpadding=15>
            <tr>
              <td bgcolor="#f9f9f9" valign=top align=left>%s</td>
            </tr>
          </table>
        </div>""" % (self.cell_id(), inside)

    # The following could be used to make the interact frame resizable
    # and/or draggable.  Neither effect is as cool as it sounds!

    #    <script>
    #        setTimeout(function() {
    #            $('#div-interact-%s').resizable();
    #            $('#div-interact-%s').draggable();
    #        }, 1);
    #    </script>


    def render(self):
        """
        Render in text (HTML) the entire :func:`interact` canvas.

        OUTPUT:

        - string - HTML format

        EXAMPLES::

            sage: B = sagenb.notebook.interact.InputBox('x',2)
            sage: sagenb.notebook.interact.InteractCanvas([B], 3).render()
            '...notruncate...div...interact...table...x...'
        """
        html_controls={}
        for side in ('top','left','right','bottom'):
            html_controls[side] = self.render_controls(side=side)

        s = """
            <table>
              <tr><td colspan=3>{top}</td></tr>
              <tr><td>{left}</td><td style='width: 100%;'>{output}</td><td>{right}</td></tr>
              <tr><td colspan=3>{bottom}</td></tr>
            </table>""".format(output=self.render_output(), **html_controls)
        
        s = self.wrap_in_outside_frame(s)
        return s

class JavascriptCodeButton(InteractElement):
    def __init__(self, label, code):
        """
        This :func:`interact` element displays a button which when clicked
        executes JavaScript code in the notebook.

        EXAMPLES::

            sage: b = sagenb.notebook.interact.JavascriptCodeButton('Push me', 'alert("2")')
        """
        self.__label = label
        self.__code = code
        InteractElement.__init__(self)

    def render(self):
        r"""
        Returns the HTML to display this button.

        OUTPUT:

        - a string - HTML format

        EXAMPLES::

            sage: b = sagenb.notebook.interact.JavascriptCodeButton('Push me', 'alert("2")')
            sage: b.render()
            '...input...button...Push me...alert("2")...'
        """
        return """<input type="button" value="%s" onclick="%s">\n""" % (
            self.__label, self.__code)

class UpdateButton(JavascriptCodeButton):
    def __init__(self, cell_id, var):
        r"""
        Creates an :func:`interact` button element.  A click on the
        button triggers recomputation of the cell with the current
        values of the variables.

        INPUT:

        - ``cell_id`` - an integer or string; the ambient cell's ID

        - ``var``` - a variable which is used in the layout

        EXAMPLES::

            sage: b = sagenb.notebook.interact.UpdateButton(0, 'auto_update')
            sage: b.render()
            '...input...button...Update...0...'
        """
        s = "interact(%r, {}, 1)" % cell_id
        JavascriptCodeButton.__init__(self, "Update", s)

        self.__var = var

    def var(self):
        """
        Return the name of the variable that this control interacts.

        OUTPUT:

        - a string - name of a variable as a string.

        EXAMPLES::

            sage: sagenb.notebook.interact.UpdateButton(1, 'auto_update').var()
            'auto_update'
        """
        return self.__var

        
from sage.misc.decorators import decorator_defaults

@decorator_defaults
def interact(f, layout=None, width='800px'):
    r"""
    Use interact as a decorator to create interactive Sage notebook
    cells with sliders, text boxes, radio buttons, check boxes, and
    color selectors.  Simply put ``@interact`` on the line before a
    function definition in a cell by itself, and choose appropriate
    defaults for the variable names to determine the types of
    controls (see tables below).

    INPUT:

    - ``f`` - a Python function

    - ``layout`` (optional) - a dictionary with keys 'top', 'bottom', 'left', 'right' and values lists of rows of control variable names.  Controls are laid out according to this pattern.  If ``layout`` is not a dictionary, it is assumed to be the 'top' value.  If ``layout`` is None, then all controls are assigned separate rows in the ``top`` value.

    EXAMPLES:

    In each example below we use a single underscore for the function
    name.  You can use *any* name you want; it does not have to
    be an underscore.

    We create an interact control with two inputs, a text input for
    the variable ``a`` and a ``y`` slider that runs through the range of
    integers from `0` to `19`.

    ::

        sage: @interact
        ....: def _(a=5, y=(0..20)): print(a + y)
        ....:
        <html>...

    ::

        sage: @interact(layout=[['a','b'],['d']])
        ....: def _(a=x^2, b=(0..20), c=100, d=x+1): print(a+b+c+d)
        ....:
        <html>...

    ::

        sage: @interact(layout={'top': [['a', 'b']], 'left': [['c']], 'bottom': [['d']]})
        ....: def _(a=x^2, b=(0..20), c=100, d=x+1): print(a+b+c+d)
        ....:
        <html>...
    

    Draw a plot interacting with the "continuous" variable ``a``.  By
    default continuous variables have exactly 50 possibilities.

    ::

        sage: @interact
        ....: def _(a=(0,2)):
        ....:     show(plot(sin(x*(1+a*x)), (x,0,6)), figsize=4)
        <html>...

    Interact a variable in steps of 1 (we also use an unnamed
    function)::

        sage: @interact
        ....: def _(n=(10,100,1)):
        ....:     show(factor(x^n - 1))
        <html>...

    Interact two variables::

        sage: @interact
        ....: def _(a=(1,4), b=(0,10)):
        ....:     show(plot(sin(a*x+b), (x,0,6)), figsize=3)
        <html>...

    Place a block of text among the controls::

        sage: @interact
        ....: def _(t1=text_control("Factors an integer."), n="1"):
        ....:     print(factor(Integer(n)))
        <html>...

    If your the time to evaluate your function takes awhile, you may
    not want to have it reevaluated every time the inputs change.  In
    order to prevent this, you can add a keyword ``auto_update=False`` to
    your function to prevent it from updating whenever the values are
    changed.  This will cause a button labeled 'Update' to appear
    which you can click on to re-evaluate your function.

    ::

        sage: @interact
        ....: def _(n=(10,100,1), auto_update=False):
        ....:     show(factor(x^n - 1))
        <html>...

    DEFAULTS:

    Defaults for the variables of the input function determine
    interactive controls.  The standard controls are ``input_box``,
    ``slider``, ``range_slider``, ``checkbox``, ``selector``,
    ``input_grid``, and ``color_selector``.  There is also a text
    control (see the defaults below).


    * ``u = input_box(default=None, label=None, type=None)`` -
      input box with given ``default``; use ``type=str`` to get
      input as an arbitrary string


    * ``u = slider(vmin, vmax=None, step_size=1, default=None,
      label=None)`` - slider with given list of possible values;
      ``vmin`` can be a list


    * ``u = range_slider(vmin, vmax=None, step_size=1,
      default=None, label=None)`` - range slider with given list
      of possible values; ``vmin`` can be a list


    * ``u = checkbox(default=True, label=None)`` - a checkbox


    * ``u = selector(values, label=None, nrows=None, ncols=None,
      buttons=False)`` - a dropdown menu or buttons (get buttons
      if ``nrows``, ``ncols``, or ``buttons`` is set, otherwise a
      dropdown menu)


    * ``u = input_grid(nrows, ncols, default=None, label=None,
      to_value=lambda x:x, width=4)`` - an editable grid of
      objects (a matrix or array)

    * ``u = color_selector(default=(0,0,1), label=None,
      widget='colorpicker', hide_box=False)`` - a color selector with a
      possibly hidden input box; the ``widget`` can also be ``'farbtastic'``
      or ``'jpicker'`` (currently not working properly)

    * ``u = text_control(value='')`` - a block of text

    You can also create a color selector by setting the default value for
    an ``input_box`` to ``Color(...)``.

    There are also some convenient defaults that allow you to make
    controls automatically without having to explicitly specify them.
    E.g., you can make ``x`` a continuous slider of values between ``u``
    and ``v`` by just writing ``x=(u,v)`` in the argument list of
    your function.  These are all just convenient shortcuts for
    creating the controls listed above.

    * ``u`` - blank input_box field

    * ``u = element`` - input_box with ``default=element``, if
      element not below.

    * ``u = (umin,umax)`` - continuous slider (really `100` steps)

    * ``u = (umin,umax,du)`` - slider with step size ``du``

    * ``u = list`` - buttons if ``len(list)`` at most `5`;
      otherwise, drop down

    * ``u = iterator`` (e.g. a generator) - a slider (up to `10000`
      steps)

    * ``u = bool`` - a checkbox

    * ``u = Color('blue')`` - a color selector; returns ``Color``
      object

    * ``u = (default, v)`` - ``v`` as above, with given
      ``default`` value

    * ``u = (label, v)`` - ``v`` as above, with given ``label``
      (a string)

    * ``u = matrix`` - an ``input_grid`` with ``to_value`` set to
      ``matrix.parent()`` and default values given by the matrix

    .. note::

        Suppose you would like to make an interactive with a default
        RGB color of ``(1,0,0)``, so the function would have signature
        ``f(color=(1,0,0))``.  Unfortunately, the above shortcuts
        reinterpret the ``(1,0,0)`` as a discrete slider with step
        size 0 between 1 and 0.  Instead you should do the
        following::

            sage: @interact
            ....: def _(v = input_box((1,0,0))):
            ....:       show(plot(sin,color=v))
            <html>...

        An alternative::

            sage: @interact
            ....: def _(c = color_selector((1, 0, 0))):
            ....:       show(plot(sin, color = c))
            <html>...

    MORE EXAMPLES:

    We give an input box that allows one to enter completely arbitrary
    strings::

        sage: @interact
        ....: def _(a=input_box('sage', label="Enter your name", type=str)):
        ....:        print("Hello there %s"%a.capitalize())
        <html>...

    The scope of variables that you control via :func:`interact` are local
    to the scope of the function being interacted with. However, by
    using the ``global`` Python keyword, you can still modify global
    variables as follows::

        sage: xyz = 10
        sage: @interact
        ....: def _(a=('xyz',5)):
        ....:       global xyz
        ....:       xyz = a
        <html>...

    If you enter the above you obtain an :func:`interact` canvas.
    Entering values in the box changes the global variable ``xyz``.
    Here's a example with several controls::

        sage: @interact
        ....: def _(title=["A Plot Demo", "Something silly", "something tricky"], a=input_box(sin(x*sin(x*sin(x))), 'function'),
        ....:     clr = Color('red'), thickness=[1..30], zoom=(1,0.95,..,0.1), plot_points=(200..2000)):
        ....:     html('<h1 align=center>%s</h1>'%title)
        ....:     print(plot_points)
        ....:     show(plot(a, -zoom*pi,zoom*pi, color=clr, thickness=thickness, plot_points=plot_points))
        <html>...

    For a more compact color control, use an empty label and
    hide the input box::

        sage: @interact
        ....: def _(color=color_selector((1,0,1), label='', hide_box=True)):
        ....:     show(plot(x/(8/7+sin(x)), (x,-50,50), fill=True, fillcolor=color))
        <html>...

    We give defaults and name the variables::

        sage: @interact
        ....: def _(a=('first', (1,4)), b=(0,10)):
        ....:     show(plot(sin(a*x+sin(b*x)), (x,0,6)), figsize=3)
        <html>...

    Another example involving labels, defaults, and the slider
    command::

        sage: @interact
        ....: def _(a = slider(1, 4, default=2, label='Multiplier'),
        ....:     b = slider(0, 10, default=0, label='Phase Variable')):
        ....:     show(plot(sin(a*x+b), (x,0,6)), figsize=4)
        <html>...

    An example where the range slider control is useful::

        sage: @interact
        ....: def _(b = range_slider(-20, 20, 1, default=(-19,3), label='Range')):
        ....:     plot(sin(x)/x, b[0], b[1]).show(xmin=b[0],xmax=b[1])
        <html>...

    An example using checkboxes, obtained by making the default values
    bools::

        sage: @interact
        ....: def _(axes=('Show axes', True), square=False):
        ....:     show(plot(sin, -5,5), axes=axes, aspect_ratio = (1 if square else None))
        <html>...

    An example generating a random walk that uses a checkbox control
    to determine whether points are placed at each step::

        sage: @interact
        ....: def foo(pts = checkbox(True, "points"), n = (50,(10..100))):
        ....:     s = 0; v = [(0,0)]
        ....:     for i in range(n):
        ....:          s += random() - 0.5
        ....:          v.append((i, s))
        ....:     L = line(v, rgbcolor='#4a8de2')
        ....:     if pts: L += points(v, pointsize=20, rgbcolor='black')
        ....:     show(L)
        <html>...

    You can rotate and zoom into 3-D graphics while interacting with a
    variable::

        sage: @interact
        ....: def _(a=(0,1)):
        ....:     x,y = var('x,y')
        ....:     show(plot3d(sin(x*cos(y*a)), (x,0,5), (y,0,5)), figsize=4)
        <html>...

    A random polygon::

        sage: pts = [(random(), random()) for _ in xrange(20)]
        sage: @interact
        ....: def _(n = (4..len(pts)), c=Color('purple') ):
        ....:     G = points(pts[:n],pointsize=60) + polygon(pts[:n], rgbcolor=c)
        ....:     show(G, figsize=5, xmin=0, ymin=0)
        <html>...

    Two "sinks" displayed simultaneously via a contour plot and a 3-D
    interactive plot::

        sage: @interact
        ....: def _(q1=(-1,(-3,3)), q2=(-2,(-3,3))):
        ....:     x,y = var('x,y')
        ....:     f = q1/sqrt((x+1)^2 + y^2) + q2/sqrt((x-1)^2+(y+0.5)^2)
        ....:     C = contour_plot(f, (-2,2), (-2,2), plot_points=30, contours=15, cmap='cool')
        ....:     show(C, figsize=3, aspect_ratio=1)
        ....:     show(plot3d(f, (x,-2,2), (y,-2,2)), figsize=4)
        <html>...

    This is similar to above, but you can select the color map from a
    dropdown menu::

        sage: @interact
        ....: def _(q1=(-1,(-3,3)), q2=(-2,(-3,3)),
        ....:     cmap=['autumn', 'bone', 'cool', 'copper', 'gray', 'hot', 'hsv',
        ....:           'jet', 'pink', 'prism', 'spring', 'summer', 'winter']):
        ....:     x,y = var('x,y')
        ....:     f = q1/sqrt((x+1)^2 + y^2) + q2/sqrt((x-1)^2+(y+0.5)^2)
        ....:     C = contour_plot(f, (x,-2,2), (y,-2,2), plot_points=30, contours=15, cmap=cmap)
        ....:     show(C, figsize=3, aspect_ratio=1)
        <html>...

    A quadratic roots etch-a-sketch::

        sage: v = []
        sage: html('<h2>Quadratic Root Etch-a-sketch</h2>')
        <html>...<h2>Quadratic Root Etch-a-sketch</h2>...</html>
        sage: @interact
        ....: def _(a=[-10..10], b=[-10..10], c=[-10..10]):
        ....:     f = a*x^2 + b*x + c == 0; show(f)
        ....:     soln = solve(a*x^2 + b*x + c == 0, x)[0].rhs()
        ....:     show(soln)
        ....:     P = tuple(CDF(soln))
        ....:     v.append(P)
        ....:     show(line(v, rgbcolor='purple') + point(P, pointsize=200))
        <html>...

    In the following example, we only generate data for a given ``n``
    once, so that as one varies ``p`` the data does not randomly change.
    We do this by simply caching the results for each ``n`` in a
    dictionary.::

        sage: data = {}
        sage: @interact
        ....: def _(n=(500,(100,5000,1)), p=(1,(0.1,10))):
        ....:     n = int(n)
        ....:     if n not in data:
        ....:         data[n] = [(random(), random()) for _ in xrange(n)]
        ....:     show(points([(x^p,y^p) for x,y in data[n]], rgbcolor='black'), xmin=0, ymin=0, axes=False)
        <html>...

    A conchoid::

        sage: @interact
        ....: def _(k=(1.2,(1.1,2)), k_2=(1.2,(1.1,2)), a=(1.5,(1.1,2))):
        ....:     u, v = var('u,v')
        ....:     f = (k^u*(1+cos(v))*cos(u), k^u*(1+cos(v))*sin(u), k^u*sin(v)-a*k_2^u)
        ....:     show(parametric_plot3d(f, (u,0,6*pi), (v,0,2*pi), plot_points=[40,40], texture=(0,0.5,0)))
        <html>...

    An input grid::

        sage: @interact
        ....: def _(A=matrix(QQ,3,3,range(9)), v=matrix(QQ,3,1,range(3))):
        ....:     try:
        ....:         x = A\v
        ....:         html('$$%s %s = %s$$'%(latex(A), latex(x), latex(v)))
        ....:     except:
        ....:         html('There is no solution to $$%s x=%s$$'%(latex(A), latex(v)))
        <html>...
    """
    if not isinstance(f, types.FunctionType) and callable(f):
        f = f.__call__
    (args, varargs, varkw, defaults) = inspect.getargspec(f)

    if defaults is None:
        defaults = []

    n = len(args) - len(defaults)
    controls = [automatic_control(defaults[i - n] if i >= n else None).render(arg)
                for i, arg in enumerate(args)]

    variables = {}
    adapt = {}
    state[SAGE_CELL_ID] = {'variables': variables, 'adapt': adapt}

    for control in controls:
        variables[control.var()] = control.default_value()
        adapt[control.adapt_number()] = control._adaptor

    # Replace the auto_update checkbox with a button that will cause
    # the cell to recompute itself.
    # TODO: Should auto_update=True yield a checkbox?
    auto_update = variables.get('auto_update', True)
    if auto_update is False:
        i = args.index('auto_update')
        controls[i] = UpdateButton(SAGE_CELL_ID, 'auto_update')

    C = InteractCanvas(controls, SAGE_CELL_ID, auto_update=auto_update, layout=layout, 
                       width=width)
    html(C.render())

    def _():
        z = f(*[variables[arg] for arg in args])
        if z: 
            print(z)

    state[SAGE_CELL_ID]['function'] = _

    return f


######################################################
# Actual control objects that the user passes in
######################################################
class control:
    def __init__(self, label=None):
        """
        An interactive control object used with the :func:`interact` command.
        This is the abstract base class.

        INPUTS:

        - ``label`` - a string

        EXAMPLES::

            sage: sagenb.notebook.interact.control('a control')
            Interative control 'a control' (abstract base class)
        """
        self.__label = label

    def __repr__(self):
        """
        Return string representation of this control.  (It just
        mentions the label and that this is an abstract base class.)

        OUTPUT:

        - a string

        EXAMPLES::

            sage: sagenb.notebook.interact.control('a control').__repr__()
            "Interative control 'a control' (abstract base class)"
        """
        return "Interative control '%s' (abstract base class)" % self.__label

    def label(self):
        """
        Return the label of this control.

        OUTPUT:

        - a string

        EXAMPLES::

            sage: sagenb.notebook.interact.control('a control').label()
            'a control'
            sage: selector([1,2,7], 'alpha').label()
            'alpha'
        """
        return self.__label

    def set_label(self, label):
        """
        Set the label of this control.

        INPUT:

        - ``label`` - a string

        EXAMPLES::

            sage: C = sagenb.notebook.interact.control('a control')
            sage: C.set_label('sage'); C
            Interative control 'sage' (abstract base class)
        """
        self.__label = label

class input_box(control):
    def __init__(self, default=None, label=None, type=None, width=80, height=1, **kwargs):
        r"""
        An input box interactive control.  Use this in conjunction
        with the :func:`interact` command.

        INPUT:

        - ``default`` - an object; the default put in this input box

        - ``label`` - a string; the label rendered to the left of the
          box.

        - ``type`` - a type; coerce inputs to this; this doesn't
          have to be an actual type, since anything callable will do.

        - ``height`` - an integer (default: 1); the number of rows.  
          If greater than 1 a value won't be returned until something
          outside the textarea is clicked.

        - ``width`` - an integer; width of text box in characters

        - ``kwargs`` - a dictionary; additional keyword options

        EXAMPLES::

            sage: input_box("2+2", 'expression')
            Interact input box labeled 'expression' with default value '2+2'
            sage: input_box('sage', label="Enter your name", type=str)
            Interact input box labeled 'Enter your name' with default value 'sage'
            sage: input_box('Multiline\nInput',label='Click to change value',type=str,height=5)
            Interact input box labeled 'Click to change value' with default value 'Multiline\nInput'
        """
        self.__default = default
        self.__type = type
        control.__init__(self, label)
        self.__width = width
        self.__height = height
        self.__kwargs = kwargs

    def __repr__(self):
        """
        Return print representation of this input box.

        OUTPUT:

        - a string

        EXAMPLES::

            sage: input_box("2+2", 'expression').__repr__()
            "Interact input box labeled 'expression' with default value '2+2'"
        """
        return "Interact input box labeled %r with default value %r"%(
            self.label(), self.__default)

    def default(self):
        """
        Return the default value of this input box.

        OUTPUT:

        - an object

        EXAMPLES::

            sage: input_box('2+2', 'Expression').default()
            '2+2'
            sage: input_box(x^2 + 1, 'Expression').default()
            x^2 + 1
            sage: checkbox(True, "Points").default()
            True
        """
        return self.__default

    def type(self):
        """
        Return the type that elements of this input box are coerced to
        or None if they are not coerced (they have whatever type
        they evaluate to).

        OUTPUT:

        - a type

        EXAMPLES::

            sage: input_box("2+2", 'expression', type=int).type()
            <type 'int'>
            sage: input_box("2+2", 'expression').type() is None
            True
        """
        return self.__type

    def render(self, var):
        r"""
        Return rendering of this input box as an :class:`InputBox` to be
        used for an :func:`interact` canvas.  Basically this specializes
        this input to be used for a specific function and variable.

        INPUT:

        - ``var`` - a string (variable; one of the variable names
          input to ``f``)

        OUTPUT:

        - an :class:`InputBox` instance

        EXAMPLES::

            sage: input_box("2+2", 'Exp').render('x')
            An InputBox interactive control with x='2+2' and label 'Exp'
        """
        if self.__type is Color:
            return ColorInput(var, default_value=self.__default, 
                              label=self.label(), type=self.__type, 
                              **self.__kwargs)
        else:
            return InputBox(var, default_value=self.__default, 
                            height=self.__height,
                            label=self.label(), type=self.__type, 
                            width=self.__width, **self.__kwargs)


class color_selector(input_box):
    def __init__(self, default=(0, 0, 1), label=None,
                 widget='colorpicker', hide_box=False):
        r"""
        A color selector (also called a color chooser, picker, or
        tool) interactive control.  Use this with the :func:`interact`
        command.

        INPUT:

        - ``default`` - an instance of or valid constructor argument
          to :class:`Color` (default: (0,0,1)); the selector's default
          color; a string argument must be a valid color name (e.g.,
          'red') or HTML hex color (e.g., '#abcdef')

        - ``label`` - a string (default: None); the label rendered to
          the left of the selector.

        - ``widget`` - a string (default: 'colorpicker'); the color
          selector widget to use; choices are 'colorpicker', 'farbtastic',
          and 'jpicker' (currently broken)

        - ``hide_box`` - a boolean (default: False); whether to hide
          the input box associated with the color selector widget

        EXAMPLES::

            sage: color_selector()
            Interact color selector labeled None, with default RGB color (0.0, 0.0, 1.0), widget 'colorpicker', and visible input box
            sage: color_selector((0.5, 0.5, 1.0), widget='jpicker')
            Interact color selector labeled None, with default RGB color (0.5, 0.5, 1.0), widget 'jpicker', and visible input box
            sage: color_selector(default = Color(0, 0.5, 0.25))
            Interact color selector labeled None, with default RGB color (0.0, 0.5, 0.25), widget 'colorpicker', and visible input box
            sage: color_selector('purple', widget = 'colorpicker')
            Interact color selector labeled None, with default RGB color (0.50..., 0.0, 0.50...), widget 'colorpicker', and visible input box
            sage: color_selector('crayon', widget = 'colorpicker')
            Traceback (most recent call last):
            ...
            ValueError: unknown color 'crayon'
            sage: color_selector('#abcdef', label='height', widget='jpicker')
            Interact color selector labeled 'height', with default RGB color (0.6..., 0.8..., 0.9...), widget 'jpicker', and visible input box
            sage: color_selector('abcdef', label='height', widget='jpicker')
            Traceback (most recent call last):
            ...
            ValueError: unknown color 'abcdef'
        """
        input_box.__init__(self, default=Color(default), label=label,
                           type=Color, widget=widget, hide_box=hide_box)
        self.__widget = widget
        self.__hide_box = hide_box

    def __repr__(self):
        """
        Return the print representation of this color selector.

        OUTPUT:

        - a string

        EXAMPLES::

            sage: color_selector(Color('red'), 'line color').__repr__()
            "Interact color selector labeled 'line color', with default RGB color (1.0, 0.0, 0.0), widget 'colorpicker', and visible input box"
            sage: color_selector(Color((1,0,1)), 'circle color', widget='jpicker', hide_box=True).__repr__()
            "Interact color selector labeled 'circle color', with default RGB color (1.0, 0.0, 1.0), widget 'jpicker', and hidden input box"
        """
        s = "Interact color selector labeled %r, with default %r, widget %r, and " % (self.label(), self.default(), self.widget())
        if self.hide_box():
            return s + "hidden input box"
        else:
            return s + "visible input box"

    def widget(self):
        """
        Return the name of the HTML widget for this color selector.

        OUTPUT:

        - a string; the widget's name

        EXAMPLES::

            sage: color_selector().widget()
            'colorpicker'
            sage: color_selector('#abcdef', hide_box=True, widget='farbtastic').widget()
            'farbtastic'
            sage: color_selector(widget='jpicker').widget()
            'jpicker'
            sage: color_selector(default=Color(0,0.5,0.25)).widget()
            'colorpicker'
        """
        return self.__widget

    def hide_box(self):
        """
        Return whether to hide the input box associated with this color
        selector.

        OUTPUT:

        - a boolean

        EXAMPLES::

            sage: color_selector().hide_box()
            False
            sage: color_selector('green', hide_box=True, widget='jpicker').hide_box()
            True
            sage: color_selector((0.75,0.5,0.25)).hide_box()
            False
        """
        return self.__hide_box


class input_grid(control):
    def __init__(self, nrows, ncols, default=None, label=None, 
                 to_value=lambda x: x, width=4):
        r"""
        An input grid interactive control.  Use this in conjunction
        with the :func:`interact` command.

        INPUT:

        - ``nrows`` - an integer

        - ``ncols`` - an integer

        - ``default`` - an object; the default put in this input box

        - ``label`` - a string; the label rendered to the left of the
          box.

        - ``to_value`` - a list; the grid output (list of rows) is
          sent through this function.  This may reformat the data or
          coerce the type.

        - ``width`` - an integer; size of each input box in characters

        NOTEBOOK EXAMPLE::

            @interact
            def _(m = input_grid(2,2, default = [[1,7],[3,4]],
                                 label='M=', to_value=matrix),
                  v = input_grid(2,1, default=[1,2],
                                 label='v=', to_value=matrix)):
                try:
                    x = m\v
                    html('$$%s %s = %s$$'%(latex(m), latex(x), latex(v)))
                except:
                    html('There is no solution to $$%s x=%s$$'%(latex(m), latex(v)))

        EXAMPLES::

            sage: input_grid(2,2, default = 0, label='M')
            Interact 2 x 2 input grid control labeled M with default value 0
            sage: input_grid(2,2, default = [[1,2],[3,4]], label='M')
            Interact 2 x 2 input grid control labeled M with default value [[1, 2], [3, 4]]
            sage: input_grid(2,2, default = [[1,2],[3,4]], label='M', to_value=MatrixSpace(ZZ,2,2))
            Interact 2 x 2 input grid control labeled M with default value [[1, 2], [3, 4]]
            sage: input_grid(1, 3, default=[[1,2,3]], to_value=lambda x: vector(flatten(x)))
            Interact 1 x 3 input grid control labeled None with default value [[1, 2, 3]]
        """
        self.__default = default
        self.__rows = nrows
        self.__columns = ncols
        self.__to_value = to_value
        self.__width = width
        control.__init__(self, label)

    def __repr__(self):
        """
        Return print representation of this input box.

        OUTPUT:

        - a string

        EXAMPLES::

            sage: input_grid(2,2, label='M').__repr__() 
            'Interact 2 x 2 input grid control labeled M with default value None'
        """
        return 'Interact %r x %r input grid control labeled %s with default value %s'%( 
            self.__rows, self.__columns, self.label(),  self.default())
            

    def default(self):
        """
        Return the default value of this input grid.

        OUTPUT:

        - an object

        EXAMPLES::

            sage: input_grid(2,2, default=1).default()
            1
        """
        return self.__default

    def render(self, var):
        r"""
        Return rendering of this input grid as an :class:`InputGrid` to be
        used for an :func:`interact` canvas.  Basically this specializes
        this input to be used for a specific function and variable.

        INPUT:

        - ``var`` - a string (variable; one of the variable names
          input to ``f``)

        OUTPUT:

        - an :class:`InputGrid` instance.

        EXAMPLES::

            sage: input_grid(2,2).render('x')
            A 2 x 2 InputGrid interactive control with x=[[None, None], [None, None]] and label 'x'
            
        """
        return InputGrid(var, rows=self.__rows, columns=self.__columns, 
                         default_value=self.__default, label=self.label(),
                         to_value=self.__to_value, width=self.__width)



class checkbox(input_box):
    def __init__(self, default=True, label=None):
        """
        A checkbox interactive control.  Use this in conjunction with
        the :func:`interact` command.

        INPUT:

        - ``default`` - a bool (default: True); whether box should be
          checked or not

        - ``label`` - a string (default: None) text label rendered to
          the left of the box

        EXAMPLES::

            sage: checkbox(False, "Points")
            Interact checkbox labeled 'Points' with default value False
            sage: checkbox(True, "Points")
            Interact checkbox labeled 'Points' with default value True
            sage: checkbox(True)
            Interact checkbox labeled None with default value True
            sage: checkbox()
            Interact checkbox labeled None with default value True
        """
        input_box.__init__(self, bool(default), label=label, type=bool)

    def __repr__(self):
        """
        Print representation of this checkbox.

        OUTPUT:

        - a string

        EXAMPLES::

            sage: checkbox(True, "Points").__repr__()
            "Interact checkbox labeled 'Points' with default value True"
        """
        return "Interact checkbox labeled %r with default value %r"% ( 
            self.label(), self.default())
    
class slider_generic(control):
    def __init__(self, vmin, vmax=None, step_size=None, label=None, 
                 display_value=True):
        control.__init__(self, label=label)
        self.__display_value = display_value
        self.__vmin = vmin
        self.__vmax = vmax
        self.__step_size = step_size

    @cached_method
    def values(self):
        """
        Returns list of values that this slider takes on, in order.
        
        OUTPUT:
        
        - a list
        
        .. note:: This is a reference to a mutable list.
        
        EXAMPLES::
        
            sage: sagenb.notebook.interact.slider(1,10,1/2).values()
            [1, 3/2, 2, 5/2, 3, 7/2, 4, 9/2, 5, 11/2, 6, 13/2, 7, 15/2, 8, 17/2, 9, 19/2, 10]
        """
        if isinstance(self.__vmin, list):
            vals = self.__vmin
        else:
            if self.__vmax is None:
                self.__vmax = self.__vmin
                self.__vmin = 0
                
            # Compute step size; vmin and vmax are both defined here
            # 500 is the length of the slider (in px)
            if self.__step_size is None:
                self.__step_size = (self.__vmax-self.__vmin) / 499.0
            elif self.__step_size <= 0:
                raise ValueError("invalid negative step size -- step size must be positive")

            #Compute list of values
            num_steps = int(math.ceil((self.__vmax-self.__vmin)/float(self.__step_size)))
            if num_steps <= 1:
                vals = [self.__vmin, self.__vmax]
            else:
                vals = srange(self.__vmin, self.__vmax, self.__step_size, include_endpoint=True)
                if vals[-1] != self.__vmax:
                    try:
                        if vals[-1] > self.__vmax:
                            vals[-1] = self.__vmax
                        else:
                            vals.append(self.__vmax)
                    except (ValueError, TypeError):
                        pass
                
        # Is the list of values is small (len < =50), use the whole
        # list.  Otherwise, use part of the list.
        if len(vals) == 0:
            return [0]
        elif(len(vals) <= 500):
            return vals
        else:
            vlen = (len(vals)-1) / 499.0
            return [vals[(int)(i*vlen)] for i in range(500)]

    def display_value(self):
        """
        Returns whether to display the value on the slider.

        OUTPUT:

        - a bool

        EXAMPLES::

            sagenb.notebook.interact.slider_generic(1,10,1/2).display_value()
            True
        """
        return self.__display_value

    
class slider(slider_generic):
    def __init__(self, vmin, vmax=None, step_size=None, default=None, 
                 label=None, display_value=True):
        r"""
        An interactive slider control, which can be used in conjunction
        with the :func:`interact` command.

        INPUT:

        - ``vmin`` - an object

        - ``vmax`` - an object (default: None); if None then ``vmin``
          must be a list, and the slider then varies over elements of
          the list.

        - ``step_size`` - an integer (default: 1)

        - ``default`` - an object (default: None); default value is
          "closest" in ``vmin`` or range to this default.

        - ``label`` - a string

        - ``display_value`` - a bool, whether to display the current
          value to the right of the slider

        EXAMPLES:

        We specify both ``vmin`` and ``vmax``.  We make the default
        `3`, but since `3` isn't one of `3/17`-th spaced values
        between `2` and `5`, `52/17` is instead chosen as the
        default (it is closest)::

            sage: slider(2, 5, 3/17, 3, 'alpha')
            Slider: alpha [2--|52/17|---5]

        Here we give a list::

            sage: slider([1..10], None, None, 3, 'alpha')
            Slider: alpha [1--|3|---10]

        The elements of the list can be anything::

            sage: slider([1, 'x', 'abc', 2/3], None, None, 'x', 'alpha')
            Slider: alpha [1--|x|---2/3]            
        """
        slider_generic.__init__(self, vmin, vmax, step_size, label, 
                                display_value)
        self.__default = default

    def __repr__(self):
        """
        Return string representation of this slider.

        OUTPUT:

        - a string

        EXAMPLES::

            sage: slider(2, 5, 1/5, 3, 'alpha').__repr__()
            'Slider: alpha [2--|3|---5]'
        """
        return "Slider: %s [%s--|%s|---%s]" % (
            self.label(), self.values()[0],
            self.values()[self.default_index()], self.values()[-1])

    @cached_method
    def default_index(self):
        """
        Return default index into the list of values.

        OUTPUT:

        - an integer

        EXAMPLES::

            sage: slider(2, 5, 1/2, 3, 'alpha').default_index()
            2
        """
        # determine the best choice of index into the list of values
        # for the user-selected default
        if self.__default is None:
            return 0
        else:
            try:
                i = self.values().index(self.__default)
            except ValueError:
                # here no index matches - which is best?
                try:
                    v = [(abs(self.__default - self.values()[j]), j) for j in range(len(self.values()))]
                    m = min(v)
                    i = m[1]
                except TypeError: # abs not defined on everything, so give up
                    i = 0
            return i

    def render(self, var):
        """
        Render the :func:`interact` control for the given function and
        variable.

        INPUT:

         - ``var`` - a string; variable name

        OUTPUT:

        - a :class:`Slider` instance

        EXAMPLES::

            sage: S = slider(0, 10, 1, default=3, label='theta'); S
            Slider: theta [0--|3|---10]
            sage: S.render('x')
            Slider Interact Control: theta [0--|3|---10]
            sage: slider(2, 5, 2/7, 3, 'alpha').render('x')
            Slider Interact Control: alpha [2--|20/7|---5]
        """
        return Slider(var, self.values(), self.default_index(),
                      label=self.label(), display_value=self.display_value())


class range_slider(slider_generic):
    def __init__(self, vmin, vmax=None, step_size=None, default=None, label=None, 
                 display_value=True):
        r"""
        An interactive range slider control, which can be used in conjunction
        with the :func:`interact` command.

        INPUT:

        - ``vmin`` - an object

        - ``vmax`` - object or None; if None then ``vmin`` must be a
          list, and the slider then varies over elements of the list.

        - ``step_size`` - integer (default: 1)

        - ``default`` - a 2-tuple of objects (default: None); default
          range is "closest" in ``vmin`` or range to this default.

        - ``label`` - a string

        - ``display_value`` - a bool, whether to display the current
          value below the slider

        EXAMPLES:

        We specify both ``vmin`` and ``vmax``.  We make the default
        `(3,4)` but since neither is one of `3/17`-th spaced
        values between `2` and `5`, the closest values: `52/17`
        and `67/17`, are instead chosen as the default::

            sage: range_slider(2, 5, 3/17, (3,4), 'alpha')
            Range Slider: alpha [2--|52/17==67/17|---5]

        Here we give a list::

            sage: range_slider([1..10], None, None, (3,7), 'alpha')
            Range Slider: alpha [1--|3==7|---10]
        """
        slider_generic.__init__(self, vmin, vmax, step_size, label, 
                                display_value)
        self.__default = default

    def __repr__(self):
        """
        Return string representation of this slider.

        OUTPUT:

        - a string

        EXAMPLES::

            sage: range_slider(2, 5, 1/5, (3,4), 'alpha').__repr__()
            'Range Slider: alpha [2--|3==4|---5]'
        """
        return "Range Slider: %s [%s--|%s==%s|---%s]" % (
            self.label(), self.values()[0],
            self.values()[self.default_index()[0]], 
            self.values()[self.default_index()[1]], self.values()[-1])
    
    @cached_method
    def default_index(self):
        """
        Return default index into the list of values.

        OUTPUT:

        - an integer 2-tuple

        EXAMPLES::

            sage: range_slider(2, 5, 1/2, (3,4), 'alpha').default_index()
            (2, 4)
        """
        # Determine the best choice of index into the list of values
        # for the user-selected default.
        if self.__default is None:
            return (0,1)
        elif not isinstance(self.__default, tuple) or len(self.__default) != 2:
            raise TypeError("default value must be None or a 2-tuple.")
        else:
            dlist = []
            for i in [0, 1]:
                try:
                    d = self.values().index(self.__default[i])
                except ValueError:
                    # Here no index matches -- which is best?
                    try:
                        v = [(abs(self.__default[i] - self.values()[j]), j) for j in range(len(self.values()))]
                        m = min(v)
                        d = m[1]
                    except TypeError:
                        # abs not defined on everything, so give up
                        d = 0
                dlist.append(d)
            return (dlist[0], dlist[1])

    def render(self, var):
        """
        Render the :func:`interact` control for the given function and
        variable.

        INPUT:

        - ``var`` - string; variable name

        OUTPUT:

        - a :class:`RangeSlider` instance

        EXAMPLES::

            sage: S = range_slider(0, 10, 1, default=(3,7), label='theta'); S
            Range Slider: theta [0--|3==7|---10]
            sage: S.render('x')
            Range Slider Interact Control: theta [0--|3==7|---10]
            sage: range_slider(2, 5, 2/7, (3,4), 'alpha').render('x')
            Range Slider Interact Control: alpha [2--|20/7==4|---5]
        """
        return RangeSlider(var, self.values(), self.default_index(),
                           label=self.label(),
                           display_value=self.display_value())

        
class selector(control):
    def __init__(self, values, label=None, default=None,
                 nrows=None, ncols=None, width=None, buttons=False):
        r"""
        A drop down menu or a button bar that when pressed sets a
        variable to a given value.  Use this in conjunction with the
        :func:`interact` command.

        We use the same command to create either a drop down menu or
        selector bar of buttons, since conceptually the two controls
        do exactly the same thing - they only look different.  If
        either ``nrows`` or ``ncols`` is given, then you get a buttons
        instead of a drop down menu.

        INPUT:

        - ``values`` - [val0, val1, val2, ...] or [(val0, lbl0),
          (val1,lbl1), ...] where all labels must be given or given as
          None.

        - ``label`` - a string (default: None); if given, this label
          is placed to the left of the entire button group

        - ``default`` - an object (default: 0); default value in values
          list

        - ``nrows`` - an integer (default: None); if given determines
          the number of rows of buttons; if given buttons option below
          is set to True

        - ``ncols`` - an integer (default: None); if given determines
          the number of columns of buttons; if given buttons option
          below is set to True

        - ``width`` - an integer (default: None); if given, all
          buttons are the same width, equal to this in HTML ex
          units's.

        - ``buttons`` - a bool (default: False); if True, use buttons

        EXAMPLES::

            sage: selector([1..5])
            Drop down menu with 5 options
            sage: selector([1,2,7], default=2)
            Drop down menu with 3 options
            sage: selector([1,2,7], nrows=2)
            Button bar with 3 buttons
            sage: selector([1,2,7], ncols=2)
            Button bar with 3 buttons
            sage: selector([1,2,7], width=10)
            Drop down menu with 3 options
            sage: selector([1,2,7], buttons=True)
            Button bar with 3 buttons

        We create an :func:`interact` that involves computing charpolys of
        matrices over various rings::

            sage: @interact
            ....: def _(R=selector([ZZ,QQ,GF(17),RDF,RR]), n=(1..10)):
            ....:      M = random_matrix(R, n)
            ....:      show(M)
            ....:      show(matrix_plot(M,cmap='Oranges'))
            ....:      f = M.charpoly()
            ....:      print(f)
            <html>...

        Here we create a drop-down::

            sage: @interact
            ....: def _(a=selector([(2,'second'), (3,'third')])):
            ....:       print(a)
            <html>...
        """
        if nrows is not None or ncols is not None:
            buttons = True
        if default is None:
            default = 0
        else:
            try:
                default = values.index(default)
            except IndexError:
                default = 0
        self.__values = values
        self.__nrows = nrows
        self.__ncols = ncols
        self.__width = width
        self.__default = default
        self.__buttons = buttons
        control.__init__(self, label)

    def __repr__(self):
        """
        Return print representation of this button.

        OUTPUT:

        - a string

        EXAMPLES::

            sage: selector([1,2,7], default=2).__repr__()
            'Drop down menu with 3 options'
        """
        if self.__buttons:
            return "Button bar with %s buttons" % len(self.__values)
        else:
            return "Drop down menu with %s options" % len(self.__values)

    def values(self):
        """
        Return the list of values or (val, lbl) pairs that this
        selector can take on.

        OUTPUT:

        - a list

        EXAMPLES::

            sage: selector([1..5]).values()
            [1, 2, 3, 4, 5]
            sage: selector([(5,'fifth'), (8,'eight')]).values()
            [(5, 'fifth'), (8, 'eight')]
        """
        return self.__values

    def default(self):
        """
        Return the default choice for this control.

        OUTPUT:

        - an integer, with 0 corresponding to the first choice.

        EXAMPLES::

            sage: selector([1,2,7], default=2).default()
            1
        """
        return self.__default

    def render(self, var):
        r"""
        Return rendering of this button as a :class:`Selector`
        instance to be used for an :func:`interact` canvas.

        INPUT:

        - ``var`` - a string (variable; one of the variable names
          input to ``f``)

        OUTPUT:

        - a :class:`Selector` instance

        EXAMPLES::

            sage: selector([1..5]).render('alpha')
            Selector with 5 options for variable 'alpha'
        """
        return Selector(var, values=self.__values, label=self.label(),
                        default=self.__default, nrows=self.__nrows, 
                        ncols=self.__ncols, width=self.__width,
                        buttons=self.__buttons)


class text_control(control):
    def __init__(self, value=''):
        """
        Text that can be inserted among other :func:`interact` controls.

        INPUT:

        - ``value`` - HTML for the control

        EXAMPLES::

            sage: text_control('something')
            Text field: something
        """
        self.__default = value
        control.__init__(self, '')

    def __repr__(self):
        """
        Return print representation of this control.

        OUTPUT:

        - a string

        EXAMPLES::

            sage: text_control('something')
            Text field: something
        """
        return "Text field: %s" % self.__default

    def render(self, var):
        """
        Return rendering of the text field
        
        INPUT:

        - ``var`` - a string (variable; one of the variable names
          input to ``f``)

        OUTPUT:

        - a :class:`TextControl` instance
        """
        return TextControl(var, self.__default)


def automatic_control(default):
    """
    Automagically determine the type of control from the default
    value of the variable.

    INPUT:

    - ``default`` - the default value for ``v`` given by the function;
      see the documentation to :func:`interact` for details.

    OUTPUT:

    - an :func:`interact` control

    EXAMPLES::

        sage: sagenb.notebook.interact.automatic_control('')
        Interact input box labeled None with default value ''
        sage: sagenb.notebook.interact.automatic_control(15)
        Interact input box labeled None with default value 15
        sage: sagenb.notebook.interact.automatic_control(('start', 15))
        Interact input box labeled 'start' with default value 15
        sage: sagenb.notebook.interact.automatic_control((1,250))
        Slider: None [1.0--|1.0|---250.0]
        sage: sagenb.notebook.interact.automatic_control(('alpha', (1,250)))
        Slider: alpha [1.0--|1.0|---250.0]
        sage: sagenb.notebook.interact.automatic_control((2,(0,250)))
        Slider: None [0.0--|2.00400801603|---250.0]
        sage: sagenb.notebook.interact.automatic_control(('alpha label', (2,(0,250))))
        Slider: alpha label [0.0--|2.00400801603|---250.0]
        sage: sagenb.notebook.interact.automatic_control((2, ('alpha label',(0,250))))
        Slider: alpha label [0.0--|2.00400801603|---250.0]
        sage: C = sagenb.notebook.interact.automatic_control((1,52, 5)); C
        Slider: None [1--|1|---52]
        sage: C.values()
        [1, 6, 11, 16, 21, 26, 31, 36, 41, 46, 51, 52]
        sage: sagenb.notebook.interact.automatic_control((17, (1,100,5)))
        Slider: None [1--|16|---100]
        sage: sagenb.notebook.interact.automatic_control([1..4])
        Button bar with 4 buttons
        sage: sagenb.notebook.interact.automatic_control([1..100])
        Drop down menu with 100 options
        sage: sagenb.notebook.interact.automatic_control((1..100))
        Slider: None [1--|1|---100]
        sage: sagenb.notebook.interact.automatic_control((5, (1..100)))
        Slider: None [1--|5|---100]
        sage: sagenb.notebook.interact.automatic_control(matrix(2,2))
        Interact 2 x 2 input grid control labeled None with default value [0, 0, 0, 0]
    """
    label = None
    default_value = None

    for _ in range(2):
        if isinstance(default, tuple) and len(default) == 2 and isinstance(default[0], str):
            label, default = default
        if isinstance(default, tuple) and len(default) == 2 and isinstance(default[1], (tuple, list, collections.Iterator)):
            default_value, default = default

    if isinstance(default, control):
        C = default
        if label:
            C.set_label(label)
    elif isinstance(default, str):
        C = input_box(default, label=label, type=str)
    elif isinstance(default, bool):
        C = input_box(default, label=label, type=bool)
    elif isinstance(default, list):
        C = selector(default, default=default_value, label=label, buttons=len(default) <= 5)
    elif isinstance(default, collections.Iterator):
        C = slider(list_of_first_n(default, 10000), default=default_value, label=label)
    elif isinstance(default, Color):
        C = input_box(default, label=label, type=Color)
    elif isinstance(default, tuple):
        if len(default) == 2:
            C = slider(default[0], default[1], default=default_value, label=label)
        elif len(default) == 3:
            C = slider(default[0], default[1], default[2], default=default_value, label=label)
        else:
            C = slider(list(default), default=default_value, label=label)
    elif is_Matrix(default):
        C = input_grid(default.nrows(), default.ncols(), default=default.list(), to_value=default.parent(), label=label)
    else:
        C = input_box(default, label=label)

    return C

def list_of_first_n(v, n):
    """
    Given an iterator v, return first n elements it produces as a list.

    INPUT:

    - ``v`` - an iterator

    - ``n`` - an integer

    OUTPUT:

    - a list

    EXAMPLES::

        sage: from itertools import takewhile
        sage: p100 = takewhile(lambda x: x < 100, Primes())
        sage: sagenb.notebook.interact.list_of_first_n(p100, 10)
        [2, 3, 5, 7, 11, 13, 17, 19, 23, 29]
        sage: sagenb.notebook.interact.list_of_first_n((1..5), 10)
        [1, 2, 3, 4, 5]
        sage: sagenb.notebook.interact.list_of_first_n(QQ, 10)
        [0, 1, -1, 1/2, -1/2, 2, -2, 1/3, -1/3, 3]
    """
    if not hasattr(v, 'next'):
        v = v.__iter__()
    w = []
    while n > 0:
        try:
            w.append(v.next())
        except StopIteration:
            return w
        n -= 1
    return w


def update(cell_id, var, adapt, value, globs):
    r"""
    Called when updating the positions of an interactive control.
    Note that this just causes the values of the variables to be
    updated; it does not reevaluate the function with the new values.

    INPUT:

    - ``cell_id`` - an integer or string; the ID of an
      :func:`interact` cell

    - ``var`` - an object; a variable associated to that cell

    - ``adapt`` - in integer; the number of the adapt function

    - ``value`` - an object; new value of the control

    - ``globs`` - global variables.

    EXAMPLES:

    The following outputs __SAGE_INTERACT_RESTART__ to indicate that
    not all the state of the :func:`interact` canvas has been set up yet
    (this setup happens when JavaScript calls certain functions)::

        sage: sagenb.notebook.interact.update(0, 'a', 0, '5', globals())
        __SAGE_INTERACT_RESTART__
    """
    # We cast the id to an integer, if it's an integer.
    try:
        cell_id = int(cell_id)
    except ValueError:
        pass

    try:
        S = state[cell_id]
        # Look up the function that adapts inputs to have the right
        # type
        adapt_function = S["adapt"][adapt]
        # Apply that function and save the result in the appropriate
        # variables dictionary.
        S["variables"][var] = adapt_function(value, globs)
    except KeyError:
        # If you change this, make sure to change notebook_lib.js as
        # well.
        print(INTERACT_RESTART)

def recompute(cell_id):
    """
    Evaluates the :func:`interact` function associated to the cell
    ``cell_id``. This typically gets called after a call to
    :func:`update`.

    INPUT:

    - ``cell_id`` - a string or an integer; the ID of an
      :func:`interact` cell

    EXAMPLES:

    The following outputs __SAGE_INTERACT_RESTART__ to indicate that
    not all the state of the :func:`interact` canvas has been set up yet
    (this setup happens when JavaScript calls certain functions)::

        sage: sagenb.notebook.interact.recompute(10)
        __SAGE_INTERACT_RESTART__

    """
    # We cast the id to an integer, if it's an integer.
    try:
        cell_id = int(cell_id)
    except ValueError:
        pass

    try:
        S = state[cell_id]
        # Finally call the interactive function, which will use the
        # above variables.
        S['function']()
    except KeyError:
        # If you change this, make sure to change notebook_lib.js as
        # well.
        print(INTERACT_RESTART)
        
