<commit_before>"""
   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

       http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.
"""

import os
import traceback

from .server import runtimeInstances, debug_message, debug_alert


def to_pix(x):
    return str(x) + 'px'


def from_pix(x):
    return int(float(x.replace('px', '')))


def jsonize(d):
    return ';'.join(map(lambda k, v: k + ':' + v + '', d.keys(), d.values()))


class EventManager(object):
    """Manages the event propagation to the listeners functions"""

    def __init__(self):
        self.listeners = {}

    def propagate(self, eventname, params):
        # if for an event there is a listener, it calls the listener passing the parameters
        if eventname not in self.listeners:
            return
        listener = self.listeners[eventname]
        return getattr(listener['instance'], listener['funcname'])(*params)

    def register_listener(self, eventname, instance, funcname):
        """register a listener for a specific event"""
        self.listeners[eventname] = {'instance':instance, 'funcname':funcname}


class Tag(object):
    def __init__(self):
        # the runtime instances are processed every time a requests arrives, searching for the called method
        # if a class instance is not present in the runtimeInstances, it will
        # we not callable
        runtimeInstances.append(self)

        self.renderChildrenList = list()
        self.children = {}
        self.attributes = {}  # properties as class id style

        self.type = 'tag'
        self.attributes['id'] = str(id(self))
        self.attributes['class'] = self.__class__.__name__

    def __setitem__(self, key, value):
        """it is used for fast access to 'self.attributes[]'."""
        self.attributes[key] = value

    @staticmethod
    def _replace_client_specific_values(html, client):
        return html

    def repr(self, client, include_children = True):
        """it is used to automatically represent the object to HTML format
        packs all the attributes, children and so on."""
        classname = self.__class__.__name__

        # concatenating innerHTML. in case of html object we use repr, in case
        # of string we use directly the content
        innerHTML = ''
        for s in self.renderChildrenList:
            if isinstance(s, type('')):
                innerHTML = innerHTML + s
            elif include_children:
                innerHTML = innerHTML + s.repr(client)

        html = '<%s %s>%s</%s>' % (self.type, ' '.join(map(lambda k, v: k + "=\"" + str(
            v) + "\"", self.attributes.keys(), self.attributes.values())), innerHTML, self.type)
        return self._replace_client_specific_values(html, client)

    def append(self, key, value):
        """it allows to add child to this.

        The key can be everything you want, in order to access to the
        specific child in this way 'widget.children[key]'.

        """
        if hasattr(value, 'attributes'):
            value.attributes['parent_widget'] = str(id(self))

        if key in self.children.keys():
            self.renderChildrenList.remove(self.children[key])
        self.renderChildrenList.append(value)

        self.children[key] = value

    def remove(self, child):
        if child in self.children.values():
            #runtimeInstances.pop( runtimeInstances.index( self.children[key] ) )
            self.renderChildrenList.remove(child)
            for k in self.children.keys():
                if str(id(self.children[k])) == str(id(child)):
                    self.children.pop(k)
                    #when the child is removed we stop the iteration
                    #this implies that a child replication should not be allowed
                    break


class Widget(Tag):

    """base class for gui widgets.

    In html, it is a DIV tag    
    the "self.type" attribute specifies the HTML tag representation    
    the "self.attributes[]" attribute specifies the HTML attributes like "style" "class" "id" 
    the "self.style[]" attribute specifies the CSS style content like "font" "color". 
    It will be packet togheter with "self.attributes"

    """
    #constants
    LAYOUT_HORIZONTAL = True
    LAYOUT_VERTICAL = False

    def __init__(self, w=1, h=1, layout_orientation=LAYOUT_HORIZONTAL, widget_spacing=0):
        """w = numeric with
        h = numeric height
        layout_orientation = specifies the "float" css attribute
        widget_spacing = specifies the "margin" css attribute for the children"""
        super(Widget,self).__init__()

        self.style = {}

        self.type = 'div'

        self.layout_orientation = layout_orientation
        self.widget_spacing = widget_spacing

        # some constants for the events
        self.EVENT_ONCLICK = 'onclick'
        self.EVENT_ONDBLCLICK = 'ondblclick'
        self.EVENT_ONMOUSEDOWN = 'onmousedown'
        self.EVENT_ONMOUSEMOVE = 'onmousemove'
        self.EVENT_ONMOUSEOVER = 'onmouseover'
        self.EVENT_ONMOUSEOUT = 'onmouseout'
        self.EVENT_ONMOUSEUP = 'onmouseup'
        self.EVENT_ONKEYDOWN = 'onkeydown'
        self.EVENT_ONKEYPRESS = 'onkeypress'
        self.EVENT_ONKEYUP = 'onkeyup'
        self.EVENT_ONCHANGE = 'onchange'
        self.EVENT_ONFOCUS = 'onfocus'
        self.EVENT_ONBLUR = 'onblur'

        self.EVENT_ONUPDATE = 'onupdate'

        if w > -1:
            self.style['width'] = to_pix(w)
        if h > -1:
            self.style['height'] = to_pix(h)
        self.style['margin'] = '0px auto'

        self.oldRootWidget = None  # used when hiding the widget

        self.eventManager = EventManager()

    def repr(self, client, include_children = True):
        """it is used to automatically represent the widget to HTML format
        packs all the attributes, children and so on."""
        self['style'] = jsonize(self.style)

        return super(Widget,self).repr(client, include_children)

    def append(self, key, value):
        """it allows to add child widgets to this.

        The key can be everything you want, in order to access to the
        specific child in this way 'widget.children[key]'.

        """
        super(Widget,self).append(key, value)

        if hasattr(self.children[key], 'style'):
            spacing = to_pix(self.widget_spacing)
            selfHeight = 0
            selfWidth = 0
            if 'height' in self.style.keys() and 'height' in self.children[key].style.keys():
                selfHeight = from_pix(self.style['height']) - from_pix(self.children[key].style['height'])
            if 'width' in self.style.keys() and 'width' in self.children[key].style.keys():
                selfWidth = from_pix(self.style['width']) - from_pix(self.children[key].style['width'])
            self.children[key].style['margin'] = spacing + " " + to_pix(selfWidth/2)
            
            if self.layout_orientation:
                self.children[key].style['margin'] = to_pix(selfHeight/2) + " " + spacing
                if 'float' in self.children[key].style.keys():
                    if not (self.children[key].style['float'] == 'none'):
                        self.children[key].style['float'] = 'left'
                else:
                    self.children[key].style['float'] = 'left'

    def onfocus(self):
        return self.eventManager.propagate(self.EVENT_ONFOCUS, list())

    def set_on_focus_listener(self, listener, funcname):
        self.attributes[
            self.EVENT_ONFOCUS] = "sendCallback('" + str(id(self)) + "','" + self.EVENT_ONFOCUS + "');"
        #self.attributes[ self.EVENT_ONFOCUS ]=" var id=\'id=\'+'"+str(id(self))+"' ;sendCommand('" + self.BASE_ADDRESS + str(id(self)) + "/" + self.EVENT_ONFOCUS + "',id);"
        self.eventManager.register_listener(
            self.EVENT_ONFOCUS, listener, funcname)

    def onblur(self):
        return self.eventManager.propagate(self.EVENT_ONBLUR, list())

    def set_on_blur_listener(self, listener, funcname):
        self.attributes[
            self.EVENT_ONBLUR] = "sendCallback('" + str(id(self)) + "','" + self.EVENT_ONBLUR + "');"
        self.eventManager.register_listener(
            self.EVENT_ONBLUR, listener, funcname)

    def show(self, baseAppInstance):
        """Allows to show the widget as root window"""
        self.baseAppInstance = baseAppInstance
        # here the widget is set up as root, in server.gui_updater is monitored
        # this change and the new window is send to the browser
        self.oldRootWidget = self.baseAppInstance.client.root
        self.baseAppInstance.client.root = self

    def hide(self):
        """The root window is restored after a show"""
        if hasattr(self,'baseAppInstance'):
            self.baseAppInstance.client.root = self.oldRootWidget

    def onclick(self):
        return self.eventManager.propagate(self.EVENT_ONCLICK, list())

    def set_on_click_listener(self, listener, funcname):
        self.attributes[
            self.EVENT_ONCLICK] = "sendCallback('" + str(id(self)) + "','" + self.EVENT_ONCLICK + "');"
        self.eventManager.register_listener(
            self.EVENT_ONCLICK, listener, funcname)


class Button(Widget):

    """
    button widget: implements the onclick event.
    """

    def __init__(self, w, h, text=''):
        super(Button, self).__init__(w, h)
        self.type = 'button'
        #self.attributes[self.EVENT_ONCLICK] = "var params={};params['x']=1;params['y']=3;sendCallback('" + str(id(self)) + "','" + self.EVENT_ONCLICK + "',params);"
        self.attributes[
            self.EVENT_ONCLICK] = "sendCallback('" + str(id(self)) + "','" + self.EVENT_ONCLICK + "');"
        self.set_text(text)

    def set_text(self, t):
        self.append('text', t)

    def onclick(self):
        debug_message('Button pressed: ', self.children['text'])
        return self.eventManager.propagate(self.EVENT_ONCLICK, list())

    def set_on_click_listener(self, listener, funcname):
        """Register a listener for the click event.

        listener = class instance
        funcname = the name of member function that will be called.
        example:
            bt.set_on_click_listener( listenerClass, "ontest" )

        """
        self.eventManager.register_listener(
            self.EVENT_ONCLICK, listener, funcname)


class TextInput(Widget):

    """multiline text area widget implements the onclick event.
    """

    def __init__(self, w, h, single_line = True):
        super(TextInput, self).__init__(w, h)
        self.type = 'textarea'

        self.attributes[self.EVENT_ONCLICK] = ''
        self.attributes[self.EVENT_ONCHANGE] = "var params={};params['newValue']=document.getElementById('" + str(
            id(self)) + "').value;sendCallbackParam('" + str(id(self)) + "','" + self.EVENT_ONCHANGE + "',params);"

        self.set_text('')

        if single_line:
            self.style['resize'] = 'none'
            self.attributes['rows'] = '1'

    def set_text(self, t):
        """sets the text content."""
        self.append('text', t)

    def get_text(self):
        return self.children['text']

    def set_value(self, t):
        self.set_text(t)

    def get_value(self):
        #facility, same as get_text
        return self.get_text()

    def onchange(self, newValue):
        """returns the new text value."""
        self.set_text(newValue)
        params = list()
        params.append(newValue)
        return self.eventManager.propagate(self.EVENT_ONCHANGE, params)

    def set_on_change_listener(self, listener, funcname):
        """register the listener for the onchange event."""
        self.eventManager.register_listener(
            self.EVENT_ONCHANGE, listener, funcname)

    def onclick(self):
        return self.eventManager.propagate(self.EVENT_ONCLICK, list())

    def set_on_click_listener(self, listener, funcname):
        self.attributes[
            self.EVENT_ONCLICK] = "sendCallback('" + str(id(self)) + "','" + self.EVENT_ONCLICK + "');"
        self.eventManager.register_listener(
            self.EVENT_ONCLICK, listener, funcname)
            
    def onkeydown(self,newValue):
        """returns the new text value."""
        self.set_text(newValue)
        params = list()
        params.append(newValue)
        return self.eventManager.propagate(self.EVENT_ONKEYDOWN, params)
        
    def set_on_key_down_listener(self,listener,funcname):
        self.attributes[
            self.EVENT_ONKEYDOWN] = "var params={};params['newValue']=document.getElementById('" + str(
            id(self)) + "').value;sendCallbackParam('" + str(id(self)) + "','" + self.EVENT_ONKEYDOWN + "',params);"
        self.eventManager.register_listener(
            self.EVENT_ONKEYDOWN, listener, funcname)


class Label(Widget):

    def __init__(self, w, h, text):
        super(Label, self).__init__(w, h)
        self.type = 'p'
        self.append('text', text)

    def set_text(self, t):
        self.append('text', t)

    def get_text(self):
        return self.children['text']
        
    def onclick(self):
        return self.eventManager.propagate(self.EVENT_ONCLICK, list())

    def set_on_click_listener(self, listener, funcname):
        self.attributes[
            self.EVENT_ONCLICK] = "sendCallback('" + str(id(self)) + "','" + self.EVENT_ONCLICK + "');"
        self.eventManager.register_listener(
            self.EVENT_ONCLICK, listener, funcname)


class GenericDialog(Widget):

    """input dialog, it opens a new webpage allows the OK/CANCEL functionality
    implementing the "confirm_value" and "cancel_dialog" events."""

    def __init__(self, width=500, height=160, title='Title', message='Message'):
        self.width = width
        self.height = height
        super(GenericDialog, self).__init__(self.width, self.height, Widget.LAYOUT_VERTICAL, 10)

        self.EVENT_ONCONFIRM = 'confirm_dialog'
        self.EVENT_ONCANCEL = 'cancel_dialog'

        t = Label(self.width - 20, 50, title)
        m = Label(self.width - 20, 30, message)

        self.container = Widget(self.width - 20,0, Widget.LAYOUT_VERTICAL, 0)
        self.conf = Button(50, 30, 'Ok')
        self.cancel = Button(50, 30, 'Cancel')

        t.style['font-size'] = '16px'
        t.style['font-weight'] = 'bold'

        hlay = Widget(self.width - 20, 30)
        hlay.append('1', self.conf)
        hlay.append('2', self.cancel)
        self.conf.style['float'] = 'right'
        self.cancel.style['float'] = 'right'

        self.append('1', t)
        self.append('2', m)
        self.append('3', self.container)
        self.append('4', hlay)

        self.conf.attributes[self.EVENT_ONCLICK] = "sendCallback('" + str(id(self)) + "','" + self.EVENT_ONCONFIRM + "');"
        self.cancel.attributes[self.EVENT_ONCLICK] = "sendCallback('" + str(id(self)) + "','" + self.EVENT_ONCANCEL + "');"

        self.inputs = {}

        self.baseAppInstance = None

    def add_field_with_label(self,key,labelDescription,field):
        fields_spacing = 5
        field_height = from_pix(field.style['height']) + fields_spacing*2
        field_width = from_pix(field.style['width']) + fields_spacing*4
        self.style['height'] = to_pix( from_pix(self.style['height']) + field_height)
        self.container.style['height'] = to_pix(from_pix(self.container.style['height']) + field_height)
        self.inputs[key] = field
        label = Label( (self.width-20)-field_width-1, 30, labelDescription )
        container = Widget(self.width-20, field_height , Widget.LAYOUT_HORIZONTAL, fields_spacing)
        container.append('lbl' + key,label)
        container.append(key,self.inputs[key])
        self.container.append(key,container)
        
    def add_field(self,key,field):
        fields_spacing = 5
        field_height = from_pix(field.style['height']) + fields_spacing*2
        field_width = from_pix(field.style['width']) + fields_spacing*4
        self.style['height'] = to_pix( from_pix(self.style['height']) + field_height)
        self.container.style['height'] = to_pix(from_pix(self.container.style['height']) + field_height)
        self.inputs[key] = field
        container = Widget(self.width-20, field_height , Widget.LAYOUT_HORIZONTAL, fields_spacing)
        container.append(key,self.inputs[key])
        self.container.append(key,container)

    def get_field(self,key):
        return self.inputs[key]

    def confirm_dialog(self):
        """event called pressing on OK button.
        """
        self.hide()
        params = list()
        return self.eventManager.propagate(self.EVENT_ONCONFIRM, params)

    def set_on_confirm_dialog_listener(self, listener, funcname):
        self.eventManager.register_listener(
            self.EVENT_ONCONFIRM, listener, funcname)

    def cancel_dialog(self):
        self.hide()
        return self.eventManager.propagate(self.EVENT_ONCANCEL, list())

    def set_on_cancel_dialog_listener(self, listener, funcname):
        self.eventManager.register_listener(
            self.EVENT_ONCANCEL, listener, funcname)


class InputDialog(GenericDialog):

    """input dialog, it opens a new webpage allows the OK/CANCEL functionality
    implementing the "confirm_value" and "cancel_dialog" events."""

    def __init__(self, width=500, height=160, title='Title', message='Message'):
        super(InputDialog, self).__init__(width, height, title, message)

        self.inputText = TextInput(width - 20, 30)
        self.add_field('textinput',self.inputText)

        self.EVENT_ONCONFIRMVALUE = 'confirm_value'        
        self.set_on_confirm_dialog_listener(self, 'confirm_value')

    def confirm_value(self):
        """event called pressing on OK button.
        propagates the string content of the input field
        """
        self.hide()
        params = list()
        params.append(self.inputText.get_text())
        return self.eventManager.propagate(self.EVENT_ONCONFIRMVALUE, params)

    def set_on_confirm_value_listener(self, listener, funcname):
        self.eventManager.register_listener(
            self.EVENT_ONCONFIRMVALUE, listener, funcname)


class ListView(Widget):

    """list widget it can contain ListItems."""

    def __init__(self, w, h, orientation=Widget.LAYOUT_VERTICAL):
        super(ListView, self).__init__(w, h, orientation)
        self.type = 'ul'
        self.EVENT_ONSELECTION = 'onselection'
        self.selected_item = None
        self.selected_key = None

    def append(self, key, item):
        #if an event listener is already set for the added item, it will not generate a selection event
        if item.attributes[self.EVENT_ONCLICK] == '':
            item.set_on_click_listener(self,self.EVENT_ONSELECTION)
        item.attributes['selected'] = False
        super(self.__class__, self).append(key,item)

    def onselection(self,clicked_item):
        self.selected_key = None
        for k in self.children.keys():
            if self.children[k]==clicked_item:
                self.selected_key = k
                debug_message('ListView - onselection. Selected item key: ',k)
                if self.selected_item != None:
                    self.selected_item['selected'] = False
                self.selected_item = self.children[self.selected_key]
                self.selected_item['selected'] = True
                break
        params = list()
        params.append(self.selected_key)
        return self.eventManager.propagate(self.EVENT_ONSELECTION, params)

    def set_on_selection_listener(self, listener, funcname):
        """The listener will receive the key of the selected item.
        If you add the element from an array, use a numeric incremental key
        """
        self.eventManager.register_listener(
            self.EVENT_ONSELECTION, listener, funcname)
            
    def get_value(self):
        """Returns the value of the selected item or None
        """
        if self.selected_item==None:
            return None
        return self.selected_item.get_value()

    def get_key(self):
        """Returns the key of the selected item or None
        """
        return self.selected_key

    def select_by_key(self, itemKey):
        """
        selects an item by its key
        """
        self.selected_key = None
        self.selected_item = None
        for item in self.children.values():
            item.attributes['selected'] = False

        if itemKey in self.children.keys():
            self.children[itemKey].attributes['selected'] = True
            self.selected_key = itemKey
            self.selected_item = self.children[itemKey]

    def set_value(self, value):
        """
        selects an item by the value of a child
        """
        self.selected_key = None
        self.selected_item = None
        for k in self.children.keys():
            item = self.children[k]
            item.attributes['selected'] = False
            if value==item.get_value():
                self.selected_key = k
                self.selected_item = item
                self.selected_item.attributes['selected'] = True


class ListItem(Widget):

    """item widget for the ListView implements the onclick event.
    """

    def __init__(self, w, h, text):
        super(ListItem, self).__init__(w, h)
        self.type = 'li'

        self.attributes[self.EVENT_ONCLICK] = ''
        self.set_text(text)

    def set_text(self, text):
        self.append('text', text)

    def get_text(self):
        return self.children['text']
        
    def get_value(self):
        return self.get_text()

    def onclick(self):
        params = list()
        params.append(self)
        return self.eventManager.propagate(self.EVENT_ONCLICK, params)

    def set_on_click_listener(self, listener, funcname):
        """WARNING!!! DO NOT USE. If used disables the set_on_selection_listener of the ListView"""
        self.attributes[
            self.EVENT_ONCLICK] = "sendCallback('" + str(id(self)) + "','" + self.EVENT_ONCLICK + "');"
        self.eventManager.register_listener(
            self.EVENT_ONCLICK, listener, funcname)


class DropDown(Widget):

    """combo box widget implements the onchange event.
    """

    def __init__(self, w, h):
        super(DropDown, self).__init__(w, h)
        self.type = 'select'
        self.attributes[self.EVENT_ONCHANGE] = "var params={};params['newValue']=document.getElementById('" + str(
            id(self)) + "').value;sendCallbackParam('" + str(id(self)) + "','" + self.EVENT_ONCHANGE + "',params);"
        self.selected_item = None
        self.selected_key = None
        
    def select_by_key(self, itemKey):
        """
        selects an item by its key
        """
        for item in self.children.values():
            if 'selected' in item.attributes:
                del item.attributes['selected']
        self.children[itemKey].attributes['selected'] = 'selected'
        self.selected_key = itemKey
        self.selected_item = self.children[itemKey]

    def set_value(self, newValue):
        """
        selects the item by the contained text
        """
        self.selected_key = None
        self.selected_item = None
        for k in self.children.keys():
            item = self.children[k]
            if item.attributes['value'] == newValue:
                item.attributes['selected'] = 'selected'
                self.selected_key = k
                self.selected_item = item
            else:
                if 'selected' in item.attributes:
                    del item.attributes['selected']
                    
    def get_value(self):
        """Returns the value of the selected item or None
        """
        if self.selected_item==None:
            return None
        return self.selected_item.get_value()

    def get_key(self):
        """Returns the key of the selected item or None
        """
        return self.selected_key

    def onchange(self, newValue):
        params = list()
        params.append(newValue)
        debug_message('combo box. selected', newValue)
        self.set_value(newValue)
        return self.eventManager.propagate(self.EVENT_ONCHANGE, params)

    def set_on_change_listener(self, listener, funcname):
        self.eventManager.register_listener(
            self.EVENT_ONCHANGE, listener, funcname)


class DropDownItem(Widget):

    """item widget for the DropDown implements the onclick event.
    """

    def __init__(self, w, h, text):
        super(DropDownItem, self).__init__(w, h)
        self.type = 'option'
        self.attributes[self.EVENT_ONCLICK] = ''
        self.set_text(text)

    def onclick(self):
        return self.eventManager.propagate(self.EVENT_ONCLICK, list())

    def set_on_click_listener(self, listener, funcname):
        self.attributes[
            self.EVENT_ONCLICK] = "sendCallback('" + str(id(self)) + "','" + self.EVENT_ONCLICK + "');"
        self.eventManager.register_listener(
            self.EVENT_ONCLICK, listener, funcname)
            
    def set_text(self, text):
        self.attributes['value'] = text
        self.append('text', text)

    def get_text(self):
        return self.attributes['value']

    def set_value(self, text):
        return self.set_text(text)
        
    def get_value(self):
        return self.get_text()



class Image(Widget):

    """image widget."""

    def __init__(self, w, h, filename):
        """filename should be an URL."""
        super(Image, self).__init__(w, h)
        self.type = 'img'
        self.attributes['src'] = filename

    def onclick(self):
        return self.eventManager.propagate(self.EVENT_ONCLICK, list())

    def set_on_click_listener(self, listener, funcname):
        self.attributes[
            self.EVENT_ONCLICK] = "sendCallback('" + str(id(self)) + "','" + self.EVENT_ONCLICK + "');"
        self.eventManager.register_listener(
            self.EVENT_ONCLICK, listener, funcname)
            

class Table(Widget):

    """
    table widget - it will contains TableRow
    """

    def __init__(self, w, h):
        super(Table, self).__init__(w, h)
        self.type = 'table'
        self.style['float'] = 'none'


class TableRow(Widget):

    """
    row widget for the Table - it will contains TableItem
    """

    def __init__(self):
        super(TableRow, self).__init__(-1, -1)
        self.type = 'tr'
        self.style['float'] = 'none'


class TableItem(Widget):

    """item widget for the TableRow."""

    def __init__(self):
        super(TableItem, self).__init__(-1, -1)
        self.type = 'td'
        self.style['float'] = 'none'


class TableTitle(Widget):

    """title widget for the table."""

    def __init__(self, title=''):
        super(TableTitle, self).__init__(-1, -1)
        self.type = 'th'
        self.append('text', title)
        self.style['float'] = 'none'


class Input(Widget):

    def __init__(self, w, h, _type='', defaultValue=''):
        super(Input, self).__init__(w, h)
        self.type = 'input'
        self.attributes['class'] = _type

        self.attributes[self.EVENT_ONCLICK] = ''
        self.attributes[self.EVENT_ONCHANGE] = "var params={};params['newValue']=document.getElementById('" + str(
            id(self)) + "').value;sendCallbackParam('" + str(id(self)) + "','" + self.EVENT_ONCHANGE + "',params);"
        self.attributes['value'] = str(defaultValue)
        self.attributes['type'] = _type

    def set_value(self,value):
        self.attributes['value'] = str(value)

    def get_value(self):
        """returns the new text value."""
        return self.attributes['value']

    def onchange(self, newValue):
        self.attributes['value'] = newValue
        params = list()
        params.append(newValue)
        return self.eventManager.propagate(self.EVENT_ONCHANGE, params)

    def set_on_change_listener(self, listener, funcname):
        """register the listener for the onchange event."""
        self.eventManager.register_listener(
            self.EVENT_ONCHANGE, listener, funcname)


class SpinBox(Input):

    """spin box widget usefull as numeric input field implements the onclick
    event.
    """

    def __init__(self, w, h, defaultValue='100', min=100, max=5000, step=1):
        super(SpinBox, self).__init__(w, h, 'number', defaultValue)
        self.attributes['min'] = str(min)
        self.attributes['max'] = str(max)
        self.attributes['step'] = str(step)


class Slider(Input):
<commit_msg>gui: tidy Input<commit_after>"""
   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

       http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.
"""

import os
import traceback

from .server import runtimeInstances, debug_message, debug_alert


def to_pix(x):
    return str(x) + 'px'


def from_pix(x):
    return int(float(x.replace('px', '')))


def jsonize(d):
    return ';'.join(map(lambda k, v: k + ':' + v + '', d.keys(), d.values()))


class EventManager(object):
    """Manages the event propagation to the listeners functions"""

    def __init__(self):
        self.listeners = {}

    def propagate(self, eventname, params):
        # if for an event there is a listener, it calls the listener passing the parameters
        if eventname not in self.listeners:
            return
        listener = self.listeners[eventname]
        return getattr(listener['instance'], listener['funcname'])(*params)

    def register_listener(self, eventname, instance, funcname):
        """register a listener for a specific event"""
        self.listeners[eventname] = {'instance':instance, 'funcname':funcname}


class Tag(object):
    def __init__(self):
        # the runtime instances are processed every time a requests arrives, searching for the called method
        # if a class instance is not present in the runtimeInstances, it will
        # we not callable
        runtimeInstances.append(self)

        self.renderChildrenList = list()
        self.children = {}
        self.attributes = {}  # properties as class id style

        self.type = 'tag'
        self.attributes['id'] = str(id(self))
        self.attributes['class'] = self.__class__.__name__

    def __setitem__(self, key, value):
        """it is used for fast access to 'self.attributes[]'."""
        self.attributes[key] = value

    @staticmethod
    def _replace_client_specific_values(html, client):
        return html

    def repr(self, client, include_children = True):
        """it is used to automatically represent the object to HTML format
        packs all the attributes, children and so on."""
        classname = self.__class__.__name__

        # concatenating innerHTML. in case of html object we use repr, in case
        # of string we use directly the content
        innerHTML = ''
        for s in self.renderChildrenList:
            if isinstance(s, type('')):
                innerHTML = innerHTML + s
            elif include_children:
                innerHTML = innerHTML + s.repr(client)

        html = '<%s %s>%s</%s>' % (self.type, ' '.join(map(lambda k, v: k + "=\"" + str(
            v) + "\"", self.attributes.keys(), self.attributes.values())), innerHTML, self.type)
        return self._replace_client_specific_values(html, client)

    def append(self, key, value):
        """it allows to add child to this.

        The key can be everything you want, in order to access to the
        specific child in this way 'widget.children[key]'.

        """
        if hasattr(value, 'attributes'):
            value.attributes['parent_widget'] = str(id(self))

        if key in self.children.keys():
            self.renderChildrenList.remove(self.children[key])
        self.renderChildrenList.append(value)

        self.children[key] = value

    def remove(self, child):
        if child in self.children.values():
            #runtimeInstances.pop( runtimeInstances.index( self.children[key] ) )
            self.renderChildrenList.remove(child)
            for k in self.children.keys():
                if str(id(self.children[k])) == str(id(child)):
                    self.children.pop(k)
                    #when the child is removed we stop the iteration
                    #this implies that a child replication should not be allowed
                    break


class Widget(Tag):

    """base class for gui widgets.

    In html, it is a DIV tag    
    the "self.type" attribute specifies the HTML tag representation    
    the "self.attributes[]" attribute specifies the HTML attributes like "style" "class" "id" 
    the "self.style[]" attribute specifies the CSS style content like "font" "color". 
    It will be packet togheter with "self.attributes"

    """
    #constants
    LAYOUT_HORIZONTAL = True
    LAYOUT_VERTICAL = False

    def __init__(self, w=1, h=1, layout_orientation=LAYOUT_HORIZONTAL, widget_spacing=0):
        """w = numeric with
        h = numeric height
        layout_orientation = specifies the "float" css attribute
        widget_spacing = specifies the "margin" css attribute for the children"""
        super(Widget,self).__init__()

        self.style = {}

        self.type = 'div'

        self.layout_orientation = layout_orientation
        self.widget_spacing = widget_spacing

        # some constants for the events
        self.EVENT_ONCLICK = 'onclick'
        self.EVENT_ONDBLCLICK = 'ondblclick'
        self.EVENT_ONMOUSEDOWN = 'onmousedown'
        self.EVENT_ONMOUSEMOVE = 'onmousemove'
        self.EVENT_ONMOUSEOVER = 'onmouseover'
        self.EVENT_ONMOUSEOUT = 'onmouseout'
        self.EVENT_ONMOUSEUP = 'onmouseup'
        self.EVENT_ONKEYDOWN = 'onkeydown'
        self.EVENT_ONKEYPRESS = 'onkeypress'
        self.EVENT_ONKEYUP = 'onkeyup'
        self.EVENT_ONCHANGE = 'onchange'
        self.EVENT_ONFOCUS = 'onfocus'
        self.EVENT_ONBLUR = 'onblur'

        self.EVENT_ONUPDATE = 'onupdate'

        if w > -1:
            self.style['width'] = to_pix(w)
        if h > -1:
            self.style['height'] = to_pix(h)
        self.style['margin'] = '0px auto'

        self.oldRootWidget = None  # used when hiding the widget

        self.eventManager = EventManager()

    def repr(self, client, include_children = True):
        """it is used to automatically represent the widget to HTML format
        packs all the attributes, children and so on."""
        self['style'] = jsonize(self.style)

        return super(Widget,self).repr(client, include_children)

    def append(self, key, value):
        """it allows to add child widgets to this.

        The key can be everything you want, in order to access to the
        specific child in this way 'widget.children[key]'.

        """
        super(Widget,self).append(key, value)

        if hasattr(self.children[key], 'style'):
            spacing = to_pix(self.widget_spacing)
            selfHeight = 0
            selfWidth = 0
            if 'height' in self.style.keys() and 'height' in self.children[key].style.keys():
                selfHeight = from_pix(self.style['height']) - from_pix(self.children[key].style['height'])
            if 'width' in self.style.keys() and 'width' in self.children[key].style.keys():
                selfWidth = from_pix(self.style['width']) - from_pix(self.children[key].style['width'])
            self.children[key].style['margin'] = spacing + " " + to_pix(selfWidth/2)
            
            if self.layout_orientation:
                self.children[key].style['margin'] = to_pix(selfHeight/2) + " " + spacing
                if 'float' in self.children[key].style.keys():
                    if not (self.children[key].style['float'] == 'none'):
                        self.children[key].style['float'] = 'left'
                else:
                    self.children[key].style['float'] = 'left'

    def onfocus(self):
        return self.eventManager.propagate(self.EVENT_ONFOCUS, list())

    def set_on_focus_listener(self, listener, funcname):
        self.attributes[
            self.EVENT_ONFOCUS] = "sendCallback('" + str(id(self)) + "','" + self.EVENT_ONFOCUS + "');"
        #self.attributes[ self.EVENT_ONFOCUS ]=" var id=\'id=\'+'"+str(id(self))+"' ;sendCommand('" + self.BASE_ADDRESS + str(id(self)) + "/" + self.EVENT_ONFOCUS + "',id);"
        self.eventManager.register_listener(
            self.EVENT_ONFOCUS, listener, funcname)

    def onblur(self):
        return self.eventManager.propagate(self.EVENT_ONBLUR, list())

    def set_on_blur_listener(self, listener, funcname):
        self.attributes[
            self.EVENT_ONBLUR] = "sendCallback('" + str(id(self)) + "','" + self.EVENT_ONBLUR + "');"
        self.eventManager.register_listener(
            self.EVENT_ONBLUR, listener, funcname)

    def show(self, baseAppInstance):
        """Allows to show the widget as root window"""
        self.baseAppInstance = baseAppInstance
        # here the widget is set up as root, in server.gui_updater is monitored
        # this change and the new window is send to the browser
        self.oldRootWidget = self.baseAppInstance.client.root
        self.baseAppInstance.client.root = self

    def hide(self):
        """The root window is restored after a show"""
        if hasattr(self,'baseAppInstance'):
            self.baseAppInstance.client.root = self.oldRootWidget

    def onclick(self):
        return self.eventManager.propagate(self.EVENT_ONCLICK, list())

    def set_on_click_listener(self, listener, funcname):
        self.attributes[
            self.EVENT_ONCLICK] = "sendCallback('" + str(id(self)) + "','" + self.EVENT_ONCLICK + "');"
        self.eventManager.register_listener(
            self.EVENT_ONCLICK, listener, funcname)


class Button(Widget):

    """
    button widget: implements the onclick event.
    """

    def __init__(self, w, h, text=''):
        super(Button, self).__init__(w, h)
        self.type = 'button'
        #self.attributes[self.EVENT_ONCLICK] = "var params={};params['x']=1;params['y']=3;sendCallback('" + str(id(self)) + "','" + self.EVENT_ONCLICK + "',params);"
        self.attributes[
            self.EVENT_ONCLICK] = "sendCallback('" + str(id(self)) + "','" + self.EVENT_ONCLICK + "');"
        self.set_text(text)

    def set_text(self, t):
        self.append('text', t)

    def onclick(self):
        debug_message('Button pressed: ', self.children['text'])
        return self.eventManager.propagate(self.EVENT_ONCLICK, list())

    def set_on_click_listener(self, listener, funcname):
        """Register a listener for the click event.

        listener = class instance
        funcname = the name of member function that will be called.
        example:
            bt.set_on_click_listener( listenerClass, "ontest" )

        """
        self.eventManager.register_listener(
            self.EVENT_ONCLICK, listener, funcname)


class TextInput(Widget):

    """multiline text area widget implements the onclick event.
    """

    def __init__(self, w, h, single_line = True):
        super(TextInput, self).__init__(w, h)
        self.type = 'textarea'

        self.attributes[self.EVENT_ONCLICK] = ''
        self.attributes[self.EVENT_ONCHANGE] = "var params={};params['newValue']=document.getElementById('" + str(
            id(self)) + "').value;sendCallbackParam('" + str(id(self)) + "','" + self.EVENT_ONCHANGE + "',params);"

        self.set_text('')

        if single_line:
            self.style['resize'] = 'none'
            self.attributes['rows'] = '1'

    def set_text(self, t):
        """sets the text content."""
        self.append('text', t)

    def get_text(self):
        return self.children['text']

    def set_value(self, t):
        self.set_text(t)

    def get_value(self):
        #facility, same as get_text
        return self.get_text()

    def onchange(self, newValue):
        """returns the new text value."""
        self.set_text(newValue)
        params = list()
        params.append(newValue)
        return self.eventManager.propagate(self.EVENT_ONCHANGE, params)

    def set_on_change_listener(self, listener, funcname):
        """register the listener for the onchange event."""
        self.eventManager.register_listener(
            self.EVENT_ONCHANGE, listener, funcname)

    def onclick(self):
        return self.eventManager.propagate(self.EVENT_ONCLICK, list())

    def set_on_click_listener(self, listener, funcname):
        self.attributes[
            self.EVENT_ONCLICK] = "sendCallback('" + str(id(self)) + "','" + self.EVENT_ONCLICK + "');"
        self.eventManager.register_listener(
            self.EVENT_ONCLICK, listener, funcname)
            
    def onkeydown(self,newValue):
        """returns the new text value."""
        self.set_text(newValue)
        params = list()
        params.append(newValue)
        return self.eventManager.propagate(self.EVENT_ONKEYDOWN, params)
        
    def set_on_key_down_listener(self,listener,funcname):
        self.attributes[
            self.EVENT_ONKEYDOWN] = "var params={};params['newValue']=document.getElementById('" + str(
            id(self)) + "').value;sendCallbackParam('" + str(id(self)) + "','" + self.EVENT_ONKEYDOWN + "',params);"
        self.eventManager.register_listener(
            self.EVENT_ONKEYDOWN, listener, funcname)


class Label(Widget):

    def __init__(self, w, h, text):
        super(Label, self).__init__(w, h)
        self.type = 'p'
        self.append('text', text)

    def set_text(self, t):
        self.append('text', t)

    def get_text(self):
        return self.children['text']
        
    def onclick(self):
        return self.eventManager.propagate(self.EVENT_ONCLICK, list())

    def set_on_click_listener(self, listener, funcname):
        self.attributes[
            self.EVENT_ONCLICK] = "sendCallback('" + str(id(self)) + "','" + self.EVENT_ONCLICK + "');"
        self.eventManager.register_listener(
            self.EVENT_ONCLICK, listener, funcname)


class GenericDialog(Widget):

    """input dialog, it opens a new webpage allows the OK/CANCEL functionality
    implementing the "confirm_value" and "cancel_dialog" events."""

    def __init__(self, width=500, height=160, title='Title', message='Message'):
        self.width = width
        self.height = height
        super(GenericDialog, self).__init__(self.width, self.height, Widget.LAYOUT_VERTICAL, 10)

        self.EVENT_ONCONFIRM = 'confirm_dialog'
        self.EVENT_ONCANCEL = 'cancel_dialog'

        t = Label(self.width - 20, 50, title)
        m = Label(self.width - 20, 30, message)

        self.container = Widget(self.width - 20,0, Widget.LAYOUT_VERTICAL, 0)
        self.conf = Button(50, 30, 'Ok')
        self.cancel = Button(50, 30, 'Cancel')

        t.style['font-size'] = '16px'
        t.style['font-weight'] = 'bold'

        hlay = Widget(self.width - 20, 30)
        hlay.append('1', self.conf)
        hlay.append('2', self.cancel)
        self.conf.style['float'] = 'right'
        self.cancel.style['float'] = 'right'

        self.append('1', t)
        self.append('2', m)
        self.append('3', self.container)
        self.append('4', hlay)

        self.conf.attributes[self.EVENT_ONCLICK] = "sendCallback('" + str(id(self)) + "','" + self.EVENT_ONCONFIRM + "');"
        self.cancel.attributes[self.EVENT_ONCLICK] = "sendCallback('" + str(id(self)) + "','" + self.EVENT_ONCANCEL + "');"

        self.inputs = {}

        self.baseAppInstance = None

    def add_field_with_label(self,key,labelDescription,field):
        fields_spacing = 5
        field_height = from_pix(field.style['height']) + fields_spacing*2
        field_width = from_pix(field.style['width']) + fields_spacing*4
        self.style['height'] = to_pix( from_pix(self.style['height']) + field_height)
        self.container.style['height'] = to_pix(from_pix(self.container.style['height']) + field_height)
        self.inputs[key] = field
        label = Label( (self.width-20)-field_width-1, 30, labelDescription )
        container = Widget(self.width-20, field_height , Widget.LAYOUT_HORIZONTAL, fields_spacing)
        container.append('lbl' + key,label)
        container.append(key,self.inputs[key])
        self.container.append(key,container)
        
    def add_field(self,key,field):
        fields_spacing = 5
        field_height = from_pix(field.style['height']) + fields_spacing*2
        field_width = from_pix(field.style['width']) + fields_spacing*4
        self.style['height'] = to_pix( from_pix(self.style['height']) + field_height)
        self.container.style['height'] = to_pix(from_pix(self.container.style['height']) + field_height)
        self.inputs[key] = field
        container = Widget(self.width-20, field_height , Widget.LAYOUT_HORIZONTAL, fields_spacing)
        container.append(key,self.inputs[key])
        self.container.append(key,container)

    def get_field(self,key):
        return self.inputs[key]

    def confirm_dialog(self):
        """event called pressing on OK button.
        """
        self.hide()
        params = list()
        return self.eventManager.propagate(self.EVENT_ONCONFIRM, params)

    def set_on_confirm_dialog_listener(self, listener, funcname):
        self.eventManager.register_listener(
            self.EVENT_ONCONFIRM, listener, funcname)

    def cancel_dialog(self):
        self.hide()
        return self.eventManager.propagate(self.EVENT_ONCANCEL, list())

    def set_on_cancel_dialog_listener(self, listener, funcname):
        self.eventManager.register_listener(
            self.EVENT_ONCANCEL, listener, funcname)


class InputDialog(GenericDialog):

    """input dialog, it opens a new webpage allows the OK/CANCEL functionality
    implementing the "confirm_value" and "cancel_dialog" events."""

    def __init__(self, width=500, height=160, title='Title', message='Message'):
        super(InputDialog, self).__init__(width, height, title, message)

        self.inputText = TextInput(width - 20, 30)
        self.add_field('textinput',self.inputText)

        self.EVENT_ONCONFIRMVALUE = 'confirm_value'        
        self.set_on_confirm_dialog_listener(self, 'confirm_value')

    def confirm_value(self):
        """event called pressing on OK button.
        propagates the string content of the input field
        """
        self.hide()
        params = list()
        params.append(self.inputText.get_text())
        return self.eventManager.propagate(self.EVENT_ONCONFIRMVALUE, params)

    def set_on_confirm_value_listener(self, listener, funcname):
        self.eventManager.register_listener(
            self.EVENT_ONCONFIRMVALUE, listener, funcname)


class ListView(Widget):

    """list widget it can contain ListItems."""

    def __init__(self, w, h, orientation=Widget.LAYOUT_VERTICAL):
        super(ListView, self).__init__(w, h, orientation)
        self.type = 'ul'
        self.EVENT_ONSELECTION = 'onselection'
        self.selected_item = None
        self.selected_key = None

    def append(self, key, item):
        #if an event listener is already set for the added item, it will not generate a selection event
        if item.attributes[self.EVENT_ONCLICK] == '':
            item.set_on_click_listener(self,self.EVENT_ONSELECTION)
        item.attributes['selected'] = False
        super(self.__class__, self).append(key,item)

    def onselection(self,clicked_item):
        self.selected_key = None
        for k in self.children.keys():
            if self.children[k]==clicked_item:
                self.selected_key = k
                debug_message('ListView - onselection. Selected item key: ',k)
                if self.selected_item != None:
                    self.selected_item['selected'] = False
                self.selected_item = self.children[self.selected_key]
                self.selected_item['selected'] = True
                break
        params = list()
        params.append(self.selected_key)
        return self.eventManager.propagate(self.EVENT_ONSELECTION, params)

    def set_on_selection_listener(self, listener, funcname):
        """The listener will receive the key of the selected item.
        If you add the element from an array, use a numeric incremental key
        """
        self.eventManager.register_listener(
            self.EVENT_ONSELECTION, listener, funcname)
            
    def get_value(self):
        """Returns the value of the selected item or None
        """
        if self.selected_item==None:
            return None
        return self.selected_item.get_value()

    def get_key(self):
        """Returns the key of the selected item or None
        """
        return self.selected_key

    def select_by_key(self, itemKey):
        """
        selects an item by its key
        """
        self.selected_key = None
        self.selected_item = None
        for item in self.children.values():
            item.attributes['selected'] = False

        if itemKey in self.children.keys():
            self.children[itemKey].attributes['selected'] = True
            self.selected_key = itemKey
            self.selected_item = self.children[itemKey]

    def set_value(self, value):
        """
        selects an item by the value of a child
        """
        self.selected_key = None
        self.selected_item = None
        for k in self.children.keys():
            item = self.children[k]
            item.attributes['selected'] = False
            if value==item.get_value():
                self.selected_key = k
                self.selected_item = item
                self.selected_item.attributes['selected'] = True


class ListItem(Widget):

    """item widget for the ListView implements the onclick event.
    """

    def __init__(self, w, h, text):
        super(ListItem, self).__init__(w, h)
        self.type = 'li'

        self.attributes[self.EVENT_ONCLICK] = ''
        self.set_text(text)

    def set_text(self, text):
        self.append('text', text)

    def get_text(self):
        return self.children['text']
        
    def get_value(self):
        return self.get_text()

    def onclick(self):
        params = list()
        params.append(self)
        return self.eventManager.propagate(self.EVENT_ONCLICK, params)

    def set_on_click_listener(self, listener, funcname):
        """WARNING!!! DO NOT USE. If used disables the set_on_selection_listener of the ListView"""
        self.attributes[
            self.EVENT_ONCLICK] = "sendCallback('" + str(id(self)) + "','" + self.EVENT_ONCLICK + "');"
        self.eventManager.register_listener(
            self.EVENT_ONCLICK, listener, funcname)


class DropDown(Widget):

    """combo box widget implements the onchange event.
    """

    def __init__(self, w, h):
        super(DropDown, self).__init__(w, h)
        self.type = 'select'
        self.attributes[self.EVENT_ONCHANGE] = "var params={};params['newValue']=document.getElementById('" + str(
            id(self)) + "').value;sendCallbackParam('" + str(id(self)) + "','" + self.EVENT_ONCHANGE + "',params);"
        self.selected_item = None
        self.selected_key = None
        
    def select_by_key(self, itemKey):
        """
        selects an item by its key
        """
        for item in self.children.values():
            if 'selected' in item.attributes:
                del item.attributes['selected']
        self.children[itemKey].attributes['selected'] = 'selected'
        self.selected_key = itemKey
        self.selected_item = self.children[itemKey]

    def set_value(self, newValue):
        """
        selects the item by the contained text
        """
        self.selected_key = None
        self.selected_item = None
        for k in self.children.keys():
            item = self.children[k]
            if item.attributes['value'] == newValue:
                item.attributes['selected'] = 'selected'
                self.selected_key = k
                self.selected_item = item
            else:
                if 'selected' in item.attributes:
                    del item.attributes['selected']
                    
    def get_value(self):
        """Returns the value of the selected item or None
        """
        if self.selected_item==None:
            return None
        return self.selected_item.get_value()

    def get_key(self):
        """Returns the key of the selected item or None
        """
        return self.selected_key

    def onchange(self, newValue):
        params = list()
        params.append(newValue)
        debug_message('combo box. selected', newValue)
        self.set_value(newValue)
        return self.eventManager.propagate(self.EVENT_ONCHANGE, params)

    def set_on_change_listener(self, listener, funcname):
        self.eventManager.register_listener(
            self.EVENT_ONCHANGE, listener, funcname)


class DropDownItem(Widget):

    """item widget for the DropDown implements the onclick event.
    """

    def __init__(self, w, h, text):
        super(DropDownItem, self).__init__(w, h)
        self.type = 'option'
        self.attributes[self.EVENT_ONCLICK] = ''
        self.set_text(text)

    def onclick(self):
        return self.eventManager.propagate(self.EVENT_ONCLICK, list())

    def set_on_click_listener(self, listener, funcname):
        self.attributes[
            self.EVENT_ONCLICK] = "sendCallback('" + str(id(self)) + "','" + self.EVENT_ONCLICK + "');"
        self.eventManager.register_listener(
            self.EVENT_ONCLICK, listener, funcname)
            
    def set_text(self, text):
        self.attributes['value'] = text
        self.append('text', text)

    def get_text(self):
        return self.attributes['value']

    def set_value(self, text):
        return self.set_text(text)
        
    def get_value(self):
        return self.get_text()



class Image(Widget):

    """image widget."""

    def __init__(self, w, h, filename):
        """filename should be an URL."""
        super(Image, self).__init__(w, h)
        self.type = 'img'
        self.attributes['src'] = filename

    def onclick(self):
        return self.eventManager.propagate(self.EVENT_ONCLICK, list())

    def set_on_click_listener(self, listener, funcname):
        self.attributes[
            self.EVENT_ONCLICK] = "sendCallback('" + str(id(self)) + "','" + self.EVENT_ONCLICK + "');"
        self.eventManager.register_listener(
            self.EVENT_ONCLICK, listener, funcname)
            

class Table(Widget):

    """
    table widget - it will contains TableRow
    """

    def __init__(self, w, h):
        super(Table, self).__init__(w, h)
        self.type = 'table'
        self.style['float'] = 'none'


class TableRow(Widget):

    """
    row widget for the Table - it will contains TableItem
    """

    def __init__(self):
        super(TableRow, self).__init__(-1, -1)
        self.type = 'tr'
        self.style['float'] = 'none'


class TableItem(Widget):

    """item widget for the TableRow."""

    def __init__(self):
        super(TableItem, self).__init__(-1, -1)
        self.type = 'td'
        self.style['float'] = 'none'


class TableTitle(Widget):

    """title widget for the table."""

    def __init__(self, title=''):
        super(TableTitle, self).__init__(-1, -1)
        self.type = 'th'
        self.append('text', title)
        self.style['float'] = 'none'


class Input(Widget):

    def __init__(self, w, h, _type='', defaultValue=''):
        super(Input, self).__init__(w, h)
        self.type = 'input'
        self.attributes['class'] = _type

        self.attributes[self.EVENT_ONCLICK] = ''
        self.attributes[self.EVENT_ONCHANGE] = \
            "var params={};params['newValue']=document.getElementById('%(id)s').value;"\
            "sendCallbackParam('%(id)s','%(evt)s',params);" % {'id':id(self),
                                                               'evt':self.EVENT_ONCHANGE}
        self.attributes['value'] = str(defaultValue)
        self.attributes['type'] = _type

    def set_value(self,value):
        self.attributes['value'] = str(value)

    def get_value(self):
        """returns the new text value."""
        return self.attributes['value']

    def onchange(self, newValue):
        self.attributes['value'] = newValue
        return self.eventManager.propagate(self.EVENT_ONCHANGE, [newValue])

    def set_on_change_listener(self, listener, funcname):
        """register the listener for the onchange event."""
        self.eventManager.register_listener(self.EVENT_ONCHANGE, listener, funcname)


class SpinBox(Input):

    """spin box widget usefull as numeric input field implements the onclick
    event.
    """

    def __init__(self, w, h, defaultValue='100', min=100, max=5000, step=1):
        super(SpinBox, self).__init__(w, h, 'number', defaultValue)
        self.attributes['min'] = str(min)
        self.attributes['max'] = str(max)
        self.attributes['step'] = str(step)


class Slider(Input):

{'fraction_non_alphanumeric': 0.12194530979577708, 'fraction_numerical': 0.003426791277258567, 'mean_word_length': 2.5720202769535114, 'pattern_counts': {'":': 0, '<': 7, '<?xml version=': 0, '>': 11, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 2, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2164432', 'n_tokens_mistral': 17233, 'n_tokens_neox': 16652, 'n_words': 3985}