text: #!/usr/bin/env python
  # -*- coding: utf-8 -*-
  # author: Olivier Noguès
  
  
  import json
  
  from ares.Lib.html import AresHtml
  from ares.Lib.html import AresHtmlSelect
  from ares.Lib import AresImports
  
  
  # External package required
  render_template_string = AresImports.requires(name='flask', reason='URL remappings', install='No need to install', package='render_template_string', raiseExcept=False, sourceScript=__file__)
  
  
  class Panel(AresHtml.Html):
    """ Python wrapper for several items with toggle event
  
    Add Html Component by using + to this object and you will be able to switch between them.
    """
    __pyStyle = ['CssDivNoBorder']
    name, category, callFnc, docCategory = 'Multi Panel', 'Container', 'panel', 'Standard'
    mocks = []
  
    def __init__(self, aresObj, htmlObjs, width, widthUnit, height, heightUnit):
      if htmlObjs is None:
        htmlObjs = self.mocks
      super(Panel, self).__init__(aresObj, [], width=width, widthUnit=widthUnit, height=height, heightUnit=heightUnit)
      self.__htmlOrder, self.__htmlRef, self.htmlMaps = [], {}, {}
      for htmlObj in htmlObjs:
        self.__add__(htmlObj)
      self.css( {"padding": "5px"} )
  
    def __add__(self, htmlObj):
      """ Add an Html Object to this object"""
      htmlObj.inReport = False
      self.__htmlRef[htmlObj.htmlId] = htmlObj
      self.__htmlOrder.append(htmlObj.htmlId)
      self._addToContainerMap(htmlObj)
  
    @property
    def jqId(self):
      """ Refer to the internal select item """
      return "$('#%s_select')" % self.htmlId
  
    def onDocumentLoadVar(self):
      """ Return the variable to store in the global section of the javacript part """
      self.jsVal = "%s_data" % self.htmlId
      self.addGlobalVar(self.jsVal, json.dumps(self.__htmlOrder))
  
    def onDocumentLoadFnc(self):
      """ Pure Javascript onDocumentLoad Function """
      self.addGlobalFnc("%s(htmlObj, data)" % self.__class__.__name__, ''' htmlObj.empty() ;
       data.forEach(function(item){
          htmlObj.append('<option value="' + item + '">' + item + '</option>') ; }) ; ''', 'Javascript Object builder')
  
    def __str__(self):
      """ Return the onload, the HTML object and the javascript events """
      # No string method for a container, it will add its data in the html directly
      # The below part is the string representation
      items = ['<div %s>' % self.strAttr(pyClassNames=self.pyStyle)]
      items.append('<div class="form-group py_csssdivboxmargin"><select class="form-control" id="%s_select"></select></div>' % (self.htmlId))
      for name in self.__htmlOrder:
        obj = self.__htmlRef[name].html()
        items.append('<div style="display:none" id="%s" >%s</div>' % (name, obj))
      items.append('</div>')
  
      # The function to change the display according to the item name selected
      self.aresObj.jsOnLoadFnc.add('''
          $('#%(htmlId)s_select').on('change', function (event){ 
            DisplayItem( $('#%(htmlId)s_select option:checked').val(), %(htmlItems)s ) }); 
        ''' % {'htmlId': self.htmlId, 'htmlItems': json.dumps(self.__htmlOrder)})
  
      # The global function to hide and show components based on a list
      # This is global as some other components might want to use it and there is no need to rewrite it every time
      self.addGlobalFnc('DisplayItem(htmlIdToDisplay, idListToHidde)', '''
          idListToHidde.forEach(function(htmlId) { $('#' + htmlId).hide() ; }); $('#' + htmlIdToDisplay).show() ; ''',
                        'The global function to hide and show components based on a list. This is global as some other components might want to use it and there is no need to rewrite it every time')
      return "".join(items)
  
  
  class PanelSplit(AresHtml.Html):
    """ Python wrapper for a resizable panel
    """
    references = {'Example': 'https://codepen.io/rstrahl/pen/eJZQej'}
    name, category, callFnc, docCategory = 'Panel Split', 'Container', 'panelsplit', 'Advanced'
    mocks = []
  
    def __init__(self, aresObj, width, widthUnit, height, heightUnit, leftObj, rightObj):
      super(PanelSplit, self).__init__(aresObj, None, width=width, widthUnit=widthUnit, height=height, heightUnit=heightUnit)
      self.htmlMaps = {}
      if leftObj is not None:
        self.left(leftObj)
      if rightObj is not None:
        self.right(rightObj)
  
    def left(self, htmlObj):
      """ Add the left component to the panel """
      htmlObj.inReport = False
      self.htmlLeft = htmlObj
      self._addToContainerMap(htmlObj)
  
    def right(self, htmlObj):
      """ Add the right component to the panel """
      htmlObj.inReport = False
      self.htmlRight = htmlObj
      self._addToContainerMap(htmlObj)
  
    def html(self):
      """ Return the onload, the HTML object and the javascript events """
      # Update the HTML element with the values defined in the function call in the report
      if hasattr(self, 'jsUpdateFnc'):
        self.__addUpdtMethod(self.pyToJsData(self.vals))
  
      # No string method for a container, it will add its data in the html directly
      # The below part is the string representation
      self.css( {'display': 'flex', 'flex-direction': 'row', 'border': '1px solid silver', 'overflow': 'hidden', 'xtouch-action': 'none' })
  
      items = ['<div %s>' % self.strAttr(pyClassNames=self.pyStyle)]
      items.append('<div style="flex:0 0 auto;padding:10px;width:300px;width:60%%;white-space:nowrap;" id="%s_left" class="panel-left">%s</div>' % (self.htmlId, self.htmlLeft.html()))
      items.append('<div style="flex:0 0 auto;width:5px;min-height:200px;cursor:col-resize;background:#F8F8F8;" class="splitter">&nbsp;</div>')
      items.append('<div style="flex:1 1 auto;padding:10px;width:100%%;min-width:200px;" id="%s_right" class="panel-right">%s</div>' % (self.htmlId, self.htmlRight.html()))
      items.append('</div>')
      self.aresObj.jsOnLoadFnc.add('''
         $("#%(htmlId)s_left").resizable({ handleSelector: ".splitter", resizeHeight: false });
         $("#%(htmlId)s_right").resizable({ handleSelector: ".splitter-horizontal", resizeWidth: false });
        ''' % {'htmlId': self.htmlId})
  
      return "".join(items)
  
  
  class PanelDisplay(AresHtml.Html):
    __pyStyle = ['CssDivNoBorder']
    name, category, callFnc, docCategory = 'Display Panel', 'Container', 'paneldisplay', 'Standard'
    mocks = []
  
    def __init__(self, aresObj, htmlObjs, title, width, widthUnit, height, heightUnit, showPanel):
      if htmlObjs is None:
        htmlObjs = self.mocks
      super(PanelDisplay, self).__init__(aresObj, [], width=width, widthUnit=widthUnit, height=height, heightUnit=heightUnit)
      self.__htmlOrder, self.__htmlRef, self.htmlMaps, self.title, self.showPanel = [], {}, {}, title, showPanel
      for htmlObj in htmlObjs:
        self.__add__(htmlObj)
      self.css( {"padding": "5px"} )
      self.addGlobalVar("panel_show_%s" % self.htmlId, len(self.vals))
  
    def __add__(self, htmlObj):
      """ Add an Html Object to this object"""
      htmlObj.inReport = False
      self.__htmlRef[htmlObj.htmlId] = htmlObj
      self.__htmlOrder.append(htmlObj.htmlId)
      self._addToContainerMap(htmlObj)
  
    def __str__(self):
      items = ['<div %s>' % self.strAttr(pyClassNames=self.pyStyle)]
      visible, arrow = ("show", 'down') if self.showPanel else ('none', 'up')
      items.append('<div class="form-group" onselectstart="return false" style="color:%s;font-weight:bold;font-size:16px;border-bottom:1px solid black">%s<i onclick="PanelDisplay(this, \'%s\')" style="cursor:pointer;none;none;float:right" class="fas fa-chevron-circle-%s"></i></div>' % (self.getColor('baseColor', 0), self.title, self.htmlId, arrow))
      items.append("<div id='%s_toggle' style='display:%s'>" % (self.htmlId, visible))
      self.addGlobalFnc("PanelDisplay(srcObj, htmlId)", '''
        $('#'+ htmlId + '_toggle').toggle() ; 
        if ( $('#'+ htmlId + '_toggle').css('display') == 'block') { $(srcObj).attr('class', 'fa fa-chevron-circle-down fa-w-16') ; }
        else { $(srcObj).attr('class', 'fa fa-chevron-circle-up fa-w-16') ; }
        ''', 'Javascript function to monitor the display of the sliding panel.')
      for name in self.__htmlOrder:
        obj = self.__htmlRef[name].html()
        items.append('<div id="%s" >%s</div>' % (name, obj))
      items.append("</div>")
      items.append('</div>')
      return "".join(items)
  
  
  class Div(AresHtml.Html):
    """ Python Wrapper for a simple DIV tag
    """
    references = {'Div W3C': 'https://www.w3schools.com/tags/tag_div.asp'}
    # the below methods should not be overriden
    __pyStyle = ['CssDivNoBorder']
    __reqCss, __reqJs = ['bootstrap'], ['jquery']
    name, category, callFnc, docCategory = 'Simple Container', 'Container', 'div', 'Standard'
    mocks = ''
  
    def __init__(self, aresObj, htmlObj, color, size, width, widthUnit, icon, height, heightUnit, editable, align, padding, htmlCode):
      if isinstance(htmlObj, list) and htmlObj:
        for obj in htmlObj:
          if hasattr(obj, 'inReport'):
            obj.inReport = False
      elif htmlObj is not None and hasattr(htmlObj, 'inReport'):
        htmlObj.inReport = False # Has to be defined here otherwise it is set to late
      super(Div, self).__init__(aresObj, htmlObj, code=htmlCode, width=width, widthUnit=widthUnit, height=height, heightUnit=heightUnit)
      size = self.aresObj.pyStyleDfl['fontSize']if size is None else "%spx" % size
      color = color if color is not None else 'black'
      self.icon, self.htmlMaps = icon, {}
      self.css( {"color": color, "font-size": size, 'text-align': align } )
      if padding is not None:
        self.css('padding', '%s' % padding)
      if editable:
        self.addAttr('contenteditable', "true")
        self.css('overflow', 'auto')
  
    def __add__(self, htmlObj):
      """ Add items to a container """
      htmlObj.inReport = False # Has to be defined here otherwise it is set to late
      if not isinstance(self.vals, list):
        self.vals = [self.vals]
      self.vals.append(htmlObj)
      self._addToContainerMap(htmlObj)
      return self
  
    @property
    def jsQueryData(self): return "{ event_val: '', event_code: '%s' }" % self.htmlId
  
    def jsAdd(self, data='data', isPyData=False):
      if isPyData:
        data = json.dumps(data)
      return '''
        %(jqId)s.append("<div style='margin:0;padding:0;white-space:nowrap;padding:0px 5px 0px 5px;'>" + %(data)s + "<i style='margin-left:10px;cursor:pointer' onclick='$(this).parent().remove()' class='far fa-times-circle'></i></div>") ;
        ''' % {'data': data, 'jqId': self.jqId}
  
    def load(self, url, htmlId=None):
      if htmlId is None:
        return "%(jqId)s.load('%(url)s');" % {"jqId": self.jqId, 'url': url, "htmlId": htmlId}
  
      return "%(jqId)s.load('%(url)s #%(htmlId)s');" % {"jqId": self.jqId, 'url': url, "htmlId": htmlId}
  
    def html(self):
      """ Return the HTML display of a split container"""
      self.loadStyle()
      self.jsEvents()
      icon = ''
      if self.icon is not None:
        icon = '<i style="display:inline-block;margin-right:5px;float:left" class="%s"></i>' % self.icon
      if isinstance(self.vals, list):
        if hasattr(self.vals[-1], 'inReport'):
          innerHtml = '\n'.join([val.html() for val in self.vals if val])
        else:
          innerHtml = '\n'.join([val for val in self.vals])
      else:
        if hasattr(self.vals, 'inReport'):
          innerHtml = self.vals.html()
        elif self.vals is not None:
          if hasattr(self.vals, 'decode'):
            innerHtml = self.vals.decode('utf8')
          else:
            innerHtml = self.vals
        else:
          innerHtml = ''
      return '<div %s>%s%s</div>' % (self.strAttr(pyClassNames=self.pyStyle), icon, innerHtml)
  
    def to_word(self, document):
      if isinstance(self.vals, list):
        for val in self.vals:
          if hasattr(val, 'inReport'):
            val.to_word(document)
      else:
        if hasattr(self.vals, 'inReport'):
          self.vals.to_word(document)
  
    @property
    def val(self):
      """ Return the Javascript Value """
      return '$("#%s").html()' % self.htmlId
  
    @property
    def text(self):
      """ Return the Javascript Value """
      return '$("#%s").text()' % self.htmlId
  
    @property
    def innerText(self):
      """ Allows to return text with formatted return carriage which jquery doesn't allow yet"""
      return '''document.getElementById('%s').innerText''' % self.htmlId
  
    def jsContentChildUpdate(self, data=''):
      """
      Allows to dynamically update the child of the div
      TODO: Change the handling of the htmlObj to be stored in a dict with htmlCode
      :return:
      """
  
      return '%s.html(%s)' % (self.vals.jqId, data)
  
  
    def getChild(self, indexObj=0):
      return self.vals[indexObj].jqId
  
  
    def jsUpdate(self, data=''):
      """ """
      return '$("#%s").html(%s)' % (self.htmlId, data)
  
  
  class DivFixed(Div):
  
    name, category, callFnc, docCategory = 'Warning', 'Text', 'fixeddiv', 'Preformatted'
    mocks = ' This is a warning'
  
    def __init__(self, aresObj, text, top, left, right, color, size, width, widthUnit, icon, height, heightUnit, editable, align, backgroundColor, zindex, padding, htmlCode):
      super(DivFixed, self).__init__(aresObj, [], color, size, width, widthUnit, icon, height, heightUnit, editable, align, padding, htmlCode)
      self.css( {'position': 'fixed', 'top': "%spx" % top, ' white-space': 'nowrap'} )
      if zindex is not None:
        self.css('z-index', zindex)
      if text is None:
        text = self.mocks
      if isinstance(text, list):
        for subTxt in text:
          self + self.aresObj.text(subTxt, size=size)
      else:
        self + self.aresObj.text(text, size=size)
      if left is not None:
        self.css('left', "%spx" % left)
      elif right is not None:
        self.css('right', "%spx" % right)
      if backgroundColor is not None:
        self.css('background-color', backgroundColor)
  
  
  class DragDiv(Div):
  
    name, category, callFnc, docCategory = 'Mark', 'Text', 'dragdiv', 'Preformatted'
    mocks = ' This is a mark'
  
    def __init__(self, aresObj, text, top, left, right, color, size, width, widthUnit, icon, height, heightUnit, editable, align, backgroundColor, padding, htmlCode):
      super(DragDiv, self).__init__(aresObj, [], color, size, width, widthUnit, icon, height, heightUnit, editable, align, padding, htmlCode)
      self.css( {'position': 'absolute', 'top': "%spx" % top, 'border-radius': '10px'} )
      self.size = 10 if size is None else size
      if text is None:
        text = self.mocks
      self + self.aresObj.text(text, size=self.size)
      if left is not None:
        self.css('left', "%spx" % left)
      elif right is not None:
        self.css('right', "%spx" % right)
      if backgroundColor is not None:
        self.css('background-color', backgroundColor)
  
    def jsFocus(self):
      return '$("#%s_content").focus()' % self.htmlId
  
    def html(self):
      """ Return the HTML display of a split container"""
      self.loadStyle()
      self.jsEvents()
      icon = ''
      self.aresObj.jsOnLoadFnc.add("%s.draggable()" % self.jqId)
      self.aresObj.jsOnLoadFnc.add("%s.bind('dragstop', function(){  } )" % self.jqId)
  
      if self.icon is not None:
        icon = '<i class="%s"></i>&nbsp;' % self.icon
      val = self.vals.html() if hasattr(self.vals, 'inReport') else self.vals
  
      edit = self.aresObj.edit()
      edit.click([self.jsFocus()])
  
      save = self.aresObj.lock()
      save.click(["%s.draggable( 'disable' );" % self.jqId], ["%s.draggable( 'enable' );" % self.jqId])
      delete = self.aresObj.delete()
      delete.click([self.jsRemove()])
  
      return '''
        <div %(attr)s>%(icon)s
          <div id="%(htmlId)s_content" autocorrect="off" spellcheck="false" contenteditable=true style="float:left;margin-left:5px;margin-right:10px;">%(content)s</div>
          %(options)s  
        </div>
        ''' % {'attr': self.strAttr(pyClassNames=self.pyStyle), 'icon': icon,
               'content': self.vals.html(), 'htmlId': self.htmlId, 'options': "".join([str(delete), str(save),str(edit)])}
  
  
  class Popup(AresHtml.Html):
    """ Python Wrapper for a simple DIV tag
  
    :example
    popup = aresObj.popup(aresObj.title('Youpi'), color="red")
    popup + aresObj.paragraph('Test')
    """
    references = {'Popup W3C': 'https://www.w3schools.com/tags/tag_div.asp'}
    # the below methods should not be overriden
    __reqCss, __reqJs = ['bootstrap'], ['jquery']
    name, category, callFnc, docCategory = 'Popup Container', 'Container', 'popup', 'Advanced'
    dashboards = ['DashBoardPopup']
  
    def __init__(self, aresObj, htmlObj, title, color, size, width, widthUnit, height, heightUnit, withBackground, draggable):
      super(Popup, self).__init__(aresObj, [], width=width, widthUnit=widthUnit, height=height, heightUnit=heightUnit)
      self.inputs, self.height = [], "%s%s" % (height, heightUnit)
      if htmlObj is not None:
        if isinstance(htmlObj, list):
          for obj in htmlObj:
            self.__add__(obj)
        else:
          self.__add__(htmlObj)
      self.size = int(self.aresObj.pyStyleDfl['fontSize'][:-2]) if size is None else size
      color = color if color is not None else 'black'
      self.title, self.withBackground, self.draggable = title, withBackground, draggable
      self.css( {"color": color, "font-size": "%spx" % size} )
      if self.withBackground:
        self.css({'width': '100%', 'position': 'fixed', 'height': '100%', 'background-color': 'rgba(0,0,0,0.4)', 'left': 0, 'top': 0,  'margin': 'auto'})
        self.css({'display': 'none', 'z-index': '10000', 'text-align': 'center', 'padding-top': '50px', 'padding-left': '10%', 'padding-right': '10%'})
        if draggable:
          self.aresObj.jsOnLoadFnc.add("$('#%s table').draggable();" % self.htmlId)
      else:
        self.css({'width': '90%','position': 'absolute', 'border': '1px solid black', 'margin': 0, 'padding': 0, 'display': 'none', 'z-index': '10000'})
        if draggable:
          self.aresObj.jsOnLoadFnc.add("%s.draggable();" % self.jqId)
  
    def __add__(self, htmlObj):
      """ Add items to a container """
      htmlObj.inReport = False # Has to be defined here otherwise it is set too late
      #htmlObj.css( {"height": "100%"} )
      if htmlObj.category in ['Input']:
        self.inputs.append( (htmlObj.jqId, htmlObj.placeholder, htmlObj.val, htmlObj.__class__.__name__) )
      self.vals.append(htmlObj)
      return self
  
    def jsSetInputVals(self, jsData):
      jsFncs = []
      for input in self.inputs:
        jsFncs.append("%s(%s, %s['%s'])" % (input[3], input[0], jsData, input[1]))
      return jsFncs
  
    def jsInputData(self, typeRow='dict'):
      if typeRow == 'dict':
        vals = []
        for input in self.inputs:
          if input[1] is not None:
            vals.append( "'%s': %s" % (input[1], input[2]) )
        return " [ {%s} ]" % ",".join(vals)
  
      return " [ %s ]" % ",".join([input[2] for input in self.inputs])
  
    def submit(self, jsFncs, label='Submit'):
      button = self.aresObj.button(label)
      self.__add__( button )
      button.jsFrg('click', "%s;%s" % (";".join(jsFncs) if isinstance(jsFncs, list) else jsFncs, self.jsHide() ) )
  
    def show(self):
      fncs = []
      for htmlObj in self.vals:
        if getattr(htmlObj, 'dataSrc') is not None and htmlObj.dataSrc['type'] == 'script':
          fncs.append(htmlObj.jsLoadFromSrc(htmlObj.dataSrc.get('jsDataKey')))
        elif getattr(htmlObj, 'dataSrc') is not None and htmlObj.dataSrc['type'] == 'url':
          fncs.append( self.aresObj.jsPost(htmlObj.dataSrc['url']))
      fncs.append("$('#%s').show()" % self.htmlId)
      return ";".join(fncs)
  
    def hide(self): return "$('#%s').hide()" % self.htmlId
  
    def html(self):
      """ Return the HTML display of a split container"""
      self.loadStyle()
      self.jsEvents()
      logo = render_template_string('<img src="{{ url_for(\'static\',filename=\'images/logo/ares_logo_nav_bar.png\') }}" />')
      onmouseOver = 'style="margin:0;padding:0;border-collapse:collapse;background-color:%s;border:1px solid %s;cursor:pointer"' % (self.getColor('blueColor', 12), self.getColor('blueColor', 12)) if self.draggable else 'style="margin:0;padding:0;border-collapse:collapse;background-color:#213B68;border:1px solid #213B68"'
      content = '''
            <table style="border-spacing:0;border-collapse:collapse;margin:0;padding:0;width:100%%;background-color:white;box-shadow:0 0 1px 1px #213B68;-webkit-box-shadow:0 0 1px 1px #213B68;-moz-box-shadow:0 0 1px 1px #213B68;border-radius: 5px ">
              <tr %(onmouseOver)s>
                <td style="vertical-align:top;border-collapse:collapse;text-align:left">%(logo)s</td>
                <td style="vertical-align:middle;border-collapse:collapse;text-align:right;color:white;font-size:%(titleSize)spx">%(title)s <i onclick="$('#%(htmlId)s').hide()" style="margin-left:10px;cursor:pointer" class="fas fa-window-close"></i></td>
              </tr>
              <tr><td colspan=2 style="padding:10px;height:%(height)s">%(objects)s</td></tr>
            </table>''' % {'logo': logo, 'onmouseOver': onmouseOver, 'titleSize': self.size, 'title': self.title, 'htmlId': self.htmlId,
                           'objects': "\n".join([val.html() for val in self.vals]), 'height': self.height}
      self.aresObj.jsOnLoadFnc.add(''' $('#%(htmlId)s').on('click', function (e) { if (e.target == this) {$('#%(htmlId)s').hide() ;} }) ''' % {"htmlId": self.htmlId})
      return '''<div %s>%s</div>''' % (self.strAttr(pyClassNames=self.pyStyle), content)
  
    @property
    def val(self):
      """aresDoc
      :example: myObj.val
      :dsc:
      Property to get the jquery value of the HTML object in a python HTML object.
      This method can be used in any jsFunction to get the value of a component in the browser.
      This method will only be used on the javascript side, so please do not consider it in your algorithm in Python
      """
      return '$("#%s").html()' % self.htmlId
  
    def jsUpdate(self, data=''): return '$("#%s").html(%s)' % (self.htmlId, data)
  
    def to_word(self, document):
      pass
  
  
  class Row(AresHtml.Html):
    """ Python wrapper for a row of HTML items """
    references, __cssCls = {}, []
    name, category, callFnc, docCategory = 'Row', 'Container', 'row', 'Standard'
    mocks = [] # No point to display an empty container
  
    def __init__(self, aresObj, htmlObjs, width, widthUnit, height, heightUnit, aresData, align, valign, colsWith, closable, resizable, titles):
      if aresData is not None:
        # Load the different HTML components from a static list
        # This mode will automatically add the inReport to the new components
        htmlObjs = []
        for component in aresData:
          aresFnc = getattr(aresObj, component['htmlComponent'])
          parameters = dict(component)
          del parameters['htmlComponent']
  
          htmlObjs.append(aresFnc(**parameters))
      self.colsWith = [] if colsWith is None else colsWith
      self.htmlMaps = {}
      super(Row, self).__init__(aresObj, [], width=width, widthUnit=widthUnit, height=height, heightUnit=heightUnit)
      if htmlObjs is None:
        htmlObjs = self.mocks
      for htmlObj in htmlObjs:
        self.__add__(htmlObj)
      self.align, self.valign, self.closable, self.resizable, self.titles = align, valign, closable, resizable, titles
      self.css({"padding": "5px 0 5px 0", "border-collapse": "collapse", 'table-layout': 'fixed' })
  
    def __add__(self, htmlObj):
      """ Add items to a container """
      htmlObj.inReport = False # Has to be defined here otherwise it is set to late
      self.vals.append(htmlObj)
      self._addToContainerMap(htmlObj)
      return self
  
    def get(self, htmlCode):
      """ Return the Html component in the parameter bar """
      return self.__components[htmlCode]
  
    def html(self):
      """ Return the HTML display of a split container"""
      self.loadStyle()
      self.jsEvents()
      items = ['<div style="width:100%%;display:block;"><table %s><tr>' % self.strAttr(pyClassNames=self.pyStyle)]
      widths = {}
      if self.colsWith:
        for i, _ in enumerate(self.vals):
          widths[i] = 'width:%s' % self.colsWith[i]
  
      if self.closable:
        if self.titles:
          for i, htmlObj in enumerate(self.vals):
            onclickEvent = "ResizableRow(this, 'col_%s_%s')" % (self.htmlId, i)  # if self.colsWith else "$(\'#col_%s_%s\').children().toggle()" % ( self.htmlId, i)
            items.append('<th style="text-align:left;padding:5px 0 5px 0;%s"><i onclick="%s" style="cursor:pointer;" class="far fa-minus-square"></i>&nbsp;<p style="font-size:16px;color:%s;display:inline">%s</p></th>' % (widths.get(i, ''), onclickEvent, self.getColor('textColor', 0), self.titles[i].upper()))
        else:
          for i, htmlObj in enumerate(self.vals):
            onclickEvent = "ResizableRow(this, 'col_%s_%s'))" % (self.htmlId, i)  # if self.colsWith else "$(\'#col_%s_%s\').children().toggle()" % ( self.htmlId, i)
            items.append( '<th style="text-align:left;%s"><i onclick="%s" style="cursor:pointer;" class="far fa-minus-square"></i></th>' % (
              widths.get(i, ''), onclickEvent))
        items.append('</tr><tr>')  # $(htmlId).parent().width()
        self.addGlobalFnc("ResizableRow(htmlId, targetId)", '''
           if ( $(htmlId).parent().data('size') == undefined) { $(htmlId).parent().data('size', $(this).parent().width() ) } ; 
           $('#' + targetId).children().toggle(); var styleDisplay = $('#' + targetId).children().css('display') ;
           if ( styleDisplay == 'block') { $(htmlId).parent().width($(htmlId).parent().data('size')) ; }
           else { $(htmlId).parent().width(10) ; } ''')
  
      for i, htmlObj in enumerate(self.vals):
        extraStyle = 'padding:0 0 0 5px' if i != 0 else 'padding:0'
        items.append( '<td id="col_%s_%s" style="font-size:inherit;line-height:inherit;vertical-align:%s;text-align:%s;%s;%s">%s</td>' % (self.htmlId, i, self.valign, self.align, widths.get(i, ''), extraStyle, htmlObj.html()))
      items.append('</tr></table></div>')
      if self.resizable:
        self.aresObj.jsImports.add('datatables-col-resizable')
        self.aresObj.jsOnLoadFnc.add("%s.colResizable({ liveDrag:true });" % self.jqId)
      return "".join(items)
  
  
  class Col(AresHtml.Html):
    """ Python wrapper for a column of HTML elements from Bootstrap
  
    This component is a container and it is used to display multiple Ares components in column.
    You can first add a component in the data list then add the + operator to add more.
  
    Also please have a look at the CSS features to customize your component
    """
    references = {'Col Bootstrap': 'https://getbootstrap.com/docs/4.0/layout/grid/',
                  'FlexBox': 'https://www.alsacreations.com/tuto/lire/1493-css3-flexbox-layout-module.html'}
    name, category, callFnc, docCategory = 'Column', 'Container', 'col', 'Standard'
    mocks = []
  
    def __init__(self, aresObj, htmlObjs, position, width, widthUnit, height, heightUnit, align):
      self.position, self.htmlMaps = position, {}
      super(Col, self).__init__(aresObj, [], width=width, widthUnit=widthUnit, height=height, heightUnit=heightUnit)
      if htmlObjs is not None:
        for htmlObj in htmlObjs:
          self.__add__(htmlObj)
      if align == "center":
        self.css({'margin': 'auto', 'display': 'block'})
      else:
        self.css({'display': 'inline-block'})
  
    def __add__(self, htmlObj):
      """ Add items to a container """
      htmlObj.inReport = False # Has to be defined here otherwise it is set to late
      self.vals.append(htmlObj)
      self._addToContainerMap(htmlObj)
      return self
  
    def content(self):
      self.loadStyle()
      return "".join([htmlObj.html() for htmlObj in self.vals])
  
    def html(self):
      """ Return the HTML display of a split container"""
      self.loadStyle()
      self.jsEvents()
      divStyle = ''
      if self.position == 'bottom':
        self.position = 'center'
        divStyle = ' style="margin:auto"'
      elif self.position == 'middle':
        divStyle = ' style="margin:auto"'
      self.css( {"justify-content": self.position})
      return '<div %s><div%s>%s</div></div>' % (self.strAttr(), divStyle, "".join([htmlObj.html() for htmlObj in self.vals]))
  
    def to_word(self, document):
      for i, htmlObj in enumerate(self.vals):
        htmlObj.to_word(document)
  
    def to_xls(self, workbook, worksheet, cursor):
      for i, htmlObj in enumerate(self.vals):
        try:
          htmlObj.to_xls(workbook, worksheet, cursor)
        except Exception as err:
          cell_format = workbook.add_format({'bold': True, 'font_color': 'red'})
          worksheet.write(cursor['row'], 0, str(err), cell_format)
          cursor['row'] += 2
  
  
  class Grid(AresHtml.Html):
    references, __cssCls = {}, ['container-fluid']
    name, category, callFnc, docCategory = 'Grid', 'Container', 'grid', 'Standard'
    mocks = [] # No point to display an empty container
  
    def __init__(self, aresObj, htmlObjs, width, widthUnit, height, heightUnit, colsDim, colsAlign, noGlutters, align):
      super(Grid, self).__init__(aresObj, [], width=width, widthUnit=widthUnit, height=height, heightUnit=heightUnit)
      self.css( {'overflow-x': 'hidden'} )
      self.rowsStyle, self.colsStyle, self.noGlutters = {}, {}, noGlutters
      if align == 'center':
        self.css({'margin': 'auto'})
      self.colsDim, self.htmlMaps, self.colsAlign  = [], {}, []
      if colsDim is None:
        colsDim = len(htmlObjs) * [1]
      for i, htmlObj in enumerate(htmlObjs):
        self.__add__( (htmlObj, colsDim[i] ) )
      if colsAlign is not None:
        self.colsAlign = colsAlign
  
    def __add__(self, htmlObjWithDim):
      """ Add items to a container """
      if isinstance(htmlObjWithDim, tuple):
        htmlObj, dim = htmlObjWithDim
      else:
        htmlObj, dim = htmlObjWithDim, 1
      self._addToContainerMap(htmlObj)
      htmlObj.inReport = False # Has to be defined here otherwise it is set to late
      self.vals.append(htmlObj)
      self.colsDim.append(dim)
      self.colsAlign.append("left")
      return self
  
    def get(self, id):
      """ Return the Html component in the parameter bar """
      return self.vals[id]
  
    def html(self):
      self.loadStyle()
      self.jsEvents()
      items = ['<div %s>' % self.strAttr(pyClassNames=self.pyStyle)]
      items.append('<div class="row%s">' % (' no-gutters' if self.noGlutters else ''))
      dimRow, rowIndex = 0, 1
      for i, htmlObj in enumerate(self.vals):
        if dimRow == 12:
          items.append('</div><div class="row%s">' % (' no-gutters' if self.noGlutters else ''))
          dimRow = 0
  
        if isinstance(htmlObj, AresHtmlSelect.Select):
          htmlObj.container = "#%s" % self.htmlId # The container should be defined in this case to be visible
        htmlContent = htmlObj.content() if isinstance(htmlObj, Col) else htmlObj.html()
        items.append('<div class="col-md-%s text-%s">%s</div>' % (self.colsDim[i], self.colsAlign[i], htmlContent) )
        dimRow += 1 if self.colsDim[i] == 'auto' else self.colsDim[i]
        rowIndex += 1
        if dimRow > 12:
          raise Exception("BootStrap allow a max of 12 columns per Row")
  
      items.append('</div></div>')
      return "".join(items)
  
    def to_word(self, document):
      for i, htmlObj in enumerate(self.vals):
        try:
          htmlObj.to_word(document)
        except Exception as err:
          from docx.shared import RGBColor
  
          errotTitle = document.add_heading().add_run("Error")
          errotTitle.font.color.rgb = RGBColor(255, 0, 0)
          errotTitle.font.italic = True
          errorParagraph = document.add_paragraph().add_run((str(err)))
          errorParagraph.font.color.rgb = RGBColor(255, 0, 0)
          errorParagraph.font.italic = True
  
    def to_xls(self, workbook, worksheet, cursor):
      for i, htmlObj in enumerate(self.vals):
        try:
          htmlObj.to_xls(workbook, worksheet, cursor)
        except Exception as err:
          cell_format = workbook.add_format({'bold': True, 'font_color': 'red'})
          worksheet.write(cursor['row'], 0, str(err), cell_format)
          cursor['row'] += 2
  
    @staticmethod
    def matchMarkDown(val): return True if val == "[" else None
  
    @classmethod
    def convertMarkDown(cls, val, regExpResult, aresObj=None):
      print(val)
  
    @classmethod
    def jsMarkDown(self, vals):
      return '''
        '''
  
  
  class Tabs(AresHtml.Html):
    """ Python wrapper for a multi Tabs component
    """
    cssCls = ['nav', 'nav-tabs']
    name, category, callFnc, docCategory = 'Tabs', 'Container', 'tabs', 'Advanced'
    __reqCss = ['bootstrap']
    references = {'Tabs Bootstrap': 'https://getbootstrap.com/docs/4.0/components/navs/'}
    __pyStyle = ['CssDivNoBorder']
    htmlObj = None
    mocks = [{'value': 'Main'}, {'value': 'Sensi 1', 'isActive': True}, {'value': 'Sensi 2'},]
    dashboards = ['DashBoardTabs']
  
    def __init__(self, aresObj, htmlObjs, width, widthUnit, height, heightUnit, tabNames, rowsCss, colsCss, closable,
                 selectedTab, htmlCode, alwaysReload, encoding):
      super(Tabs, self).__init__(aresObj, [], htmlCode=htmlCode, width=width, widthUnit=widthUnit, height=height, heightUnit=heightUnit)
      self.rowsStyle, self.colsStyle, self.selectedTab = {}, {}, selectedTab
      self.tabNames, self.htmlMaps = [], {}
      if tabNames is None:
        tabNames = len(htmlObjs) * [1]
      for i, htmlObj in enumerate(htmlObjs):
        tanValue = htmlObj.get('name', tabNames[i])
        if hasattr(tanValue, 'decode'):
          tanValue = tanValue.decode(encoding)
        if isinstance(htmlObj, dict):
          self.__add__((self.aresObj.paragraph(htmlObj['value']), tanValue))
          if htmlObj.get('isActive', False):
            self.selectedTab = tanValue
        else:
          self.__add__((htmlObj, tabNames[i]))
      self.css({'cursor': 'pointer', 'overflow-y': 'hidden', 'overflow-x': 'hidden', 'margin-top': '5px'})
      self.addGlobalVar("tabs_counts_%s" % self.htmlId, len(self.vals))
      for evts in ['click', 'change']:
        # Add the source to the different events
        self.jsFrg(evts, ''' 
          $('#%(htmlId)s li div').css( {'background-color': '%(whiteColor)s', 'color': '%(darkColor)s'} );
          $(this).find('div').css( {'background-color': '%(lightGreyColor)s', 'color': '%(whiteColor)s' } ) ;
          $('div[name="panel_%(htmlId)s"]').hide() ; var tabIndex = $(this).data('index') ; var data = %(jsQueryData)s;
          if ( '%(htmlCode)s' != 'None') { %(breadCrumVar)s['params']['%(htmlCode)s'] = data.event_val } ; 
          ''' % {'jsQueryData': self.jsQueryData, 'htmlId': self.htmlId, 'htmlCode': self.htmlCode,
                 'breadCrumVar': self.aresObj.jsGlobal.breadCrumVar, 'darkColor': self.getColor('greyColor', 10),
                 'lightGreyColor': self.getColor('blueColor', 2), 'whiteColor': self.getColor("greyColor", 0)})
      if self.htmlCode is not None:
        if self.htmlCode in self.aresObj.http:
          self.selectedTab = self.aresObj.http[self.htmlCode]
        if self.selectedTab is not None:
          self.aresObj.jsOnLoadFnc.add("%(breadCrumVar)s['params']['%(htmlCode)s'] = '%(selectedTab)s'" % {'selectedTab': self.selectedTab, 'breadCrumVar': self.aresObj.jsGlobal.breadCrumVar,
                                                                                                           'htmlCode': self.htmlCode} )
      # Check the tab reloading
      self.alwaysReload = alwaysReload
      self.addGlobalVar("reload_%s" % self.htmlId)
  
    def jsRefreshTab(self, jsTabName=None, isPyData=True):
      if jsTabName is not None:
        if isPyData:
          jsTabName = json.dumps(jsTabName)
        return 'delete %(htmlId)s_TAB_LOAD[%(jsTabName)s]' % {'htmlId': self.htmlId, 'jsTabName': jsTabName}
  
      return '%(htmlId)s_TAB_LOAD = {}' % {'htmlId': self.htmlId}
  
    def jsEvents(self):
      if hasattr(self, 'jsFncFrag'):
        for eventKey, fnc in self.jsFncFrag.items():
          if self.htmlCode is not None:
            fnc.insert(0, self.jsAddUrlParam(self.htmlCode, self.val, isPyData=False))
          jsFnc = self.jsLoading(fnc)
          self.aresObj.jsOnLoadEvtsFnc.add("$('#%(htmlId)s').delegate('li.tab_comp', '%(eventKey)s', function(event) { %(jsFnc)s }) ;" % {'htmlId': self.htmlId, 'eventKey': eventKey, 'jsFnc': jsFnc})
  
    def jsShowPanel(self, panelIndex='tabIndex', jsData='data'):
      return " $('#%(htmlId)s_panel_' + %(panelIndex)s ).show() ; " % {'panelIndex': panelIndex, 'htmlId': self.htmlId}
  
    def jsUpdatePanel(self, panelIndex='tabIndex', jsData='data', unLockOthers=False):
      return '''
            if (typeof %(panelIndex)s === 'undefined') {
                var dummy = $('<div/>'); $(dummy).css('color', "%(whiteColor)s");
                $('#%(htmlId)s').children('li').each(function () {
                    if ($(this).find("div").css("background-color") != dummy.css("color")) { 
                        %(panelIndex)s = $(this).data('index') ; }}) } ;
                        
            for (var key in %(jsData)s) {
              if( %(htmlId)s_MAP_COMPONENTS[key] != undefined) {
                var htmlObj;
                if ( %(htmlId)s_MAP_COMPONENTS[key][1].startsWith('$') ) { htmlObj = eval(%(htmlId)s_MAP_COMPONENTS[key][1]) ; }
                else { htmlObj = window[%(htmlId)s_MAP_COMPONENTS[key][1]] ; }
                window[%(htmlId)s_MAP_COMPONENTS[key][0]]( htmlObj, data[key] ) ; } }
            
            if ( %(unLockOthers)s ) { %(htmlId)s_TAB_LOAD = {} ; }
            $('#%(htmlId)s_panel_' + %(panelIndex)s ).show() ; %(panelIndex)s = undefined ;    
        ''' % {'panelIndex': panelIndex, 'htmlId': self.htmlId, 'jsData': jsData, 'whiteColor': self.getColor('greyColor', 0),
               'unLockOthers': json.dumps(unLockOthers)}
  
    @property
    def jqId(self): return "$('#%s li.tab_comp')" % self.htmlId
  
    @property
    def jsQueryData(self): return "{ event_val: $(this).find('div').text(), event_code: '%s'}" % self.htmlId
  
    def jsAddTab(self, tabName):
      return '''
        tabs_counts_%(htmlId)s ++;
        $('#%(htmlId)s').append('<li class="nav-item tab_comp" data-index=tabs_counts_%(htmlId)s><div class="nav-link" style="font-size:%(fontSize)s;font-variant:small-caps;font-weight:bold">%(tabName)s</div></li>') ;
        ''' % {'tabName': tabName, 'htmlId': self.htmlId, 'fontSize': self.aresObj.pyStyleDfl['headerFontSize']}
  
    def jsDelTab(self, tabName):
      return '''
        var pos = -1;
        $('#%(htmlId)s li').each(function( index ) {
           if( $( this ).text() == '%(tabName)s') { pos = index ;} });
        if (pos != -1) { $('#%(htmlId)s li')[pos].remove(); }
        '''  % {'tabName': tabName, 'htmlId': self.htmlId, 'tabName': tabName}
  
    def __add__(self, htmlObjWithDim):
      """ Add items to a container """
      if isinstance(htmlObjWithDim, tuple):
        htmlObj, name = htmlObjWithDim
      else:
        htmlObj, name = htmlObjWithDim, 1
      self._addToContainerMap(htmlObj)
      if hasattr(htmlObj, 'inReport'):
        htmlObj.inReport = False # Has to be defined here otherwise it is set to late
      self.vals.append(htmlObj)
      self.tabNames.append(name)
      return self
  
    def jsLoading(self, jsFnc):
      return " var loading = $('#%(htmlId)s_panel_loading'); loading.show() ; %(jsFnc)s " % {'jsFnc': ";".join(jsFnc), 'htmlId': self.htmlId}
  
    def click(self, jsFnc, tabName=None):
      if tabName is not None:
        self.jsFrg('click', '''
          if ( data['event_val'] == %(tabName)s ) { 
            if( (%(htmlId)s_TAB_LOAD[data['event_val']] == undefined) ||  %(alwaysReload)s ) { 
              %(htmlId)s_TAB_LOAD[data['event_val']] = 1; 
              %(jsFnc)s; $('#%(htmlId)s_panel_' + tabIndex).show(); loading.hide(); }  else { $('#%(htmlId)s_panel_' + tabIndex).show(); loading.hide(); } 
          }; ''' % {'alwaysReload': json.dumps(self.alwaysReload), "tabName": json.dumps(tabName), 'jsFnc': jsFnc, 'htmlId': self.htmlId})
      else:
        self.jsFrg('click', ''' 
         if( ( %(htmlId)s_TAB_LOAD[data['event_val']] == undefined) ||  %(alwaysReload)s ) {
            %(htmlId)s_TAB_LOAD[data['event_val']] = 1; %(jsFnc)s; $('#%(htmlId)s_panel_' + tabIndex).show(); loading.hide()} 
         else { $('#%(htmlId)s_panel_' + tabIndex).show(); loading.hide(); } 
         ''' % {'alwaysReload': json.dumps(self.alwaysReload), 'htmlId': self.htmlId, 'jsFnc': jsFnc})
  
    def html(self):
      """ Return the HTML representation of a Tabular object """
      self.loadStyle()
      if len(self.jsFncFrag['click']) == 1:
        self.click(' ') # Because otherwise the display will not work
      self.jsEvents()
      self.addGlobalVar("%s_MAP_COMPONENTS" % self.htmlId, json.dumps(self.htmlMaps))
      self.addGlobalVar("%s_TAB_LOAD" % self.htmlId, json.dumps( { } ))
      items, tabs = ['<ul %s>' % self.strAttr(pyClassNames=self.pyStyle)], []
      for i, htmlObj in enumerate(self.vals):
        if self.tabNames[i] == self.selectedTab:
          items.append(''' 
              <li id="tab_%(htmlId)s_selected" class="nav-item tab_comp" data-index=%(i)s>
                <div class="nav-link" style="background-color:%(lightGreyColor)s;color:%(whiteColor)s;font-size:%(fontSize)s;font-variant:small-caps;font-weight:bold;margin:0;padding:2px 15px">%(tabNames)s</div>
              </li>''' % {'htmlId': self.htmlId, "i": i, 'tabNames': self.tabNames[i], 'fontSize': self.aresObj.pyStyleDfl['headerFontSize'],
                          'whiteColor': self.getColor('greyColor', 0), 'lightGreyColor': self.getColor('blueColor', 0)})
          self.aresObj.jsOnLoadEvtsFnc.add("$('#tab_%s_selected').trigger('click')" % self.htmlId)
        else:
          items.append('<li class="nav-item tab_comp" style="padding:0" data-index=%s><div class="nav-link" style="margin:0;font-size:%s;font-variant:small-caps;font-weight:bold;padding:2px 15px">%s</div></li>' % (i, self.aresObj.pyStyleDfl['headerFontSize'], self.tabNames[i]))
        tabs.append(htmlObj)
      items.append('</ul>')
      self.aresObj.jsOnLoadFnc.add("$('#%(htmlId)s').delegate('span', 'click', function(event) {event.stopPropagation()})" % {'htmlId': self.htmlId })
      for i, tab in enumerate(tabs):
        if self.tabNames[i] == self.selectedTab:
          items.append('<div id="%(htmlId)s_panel_%(index)s" name="panel_%(htmlId)s" style="text-align:center;border:1px solid %(lightColor)s;padding:10px">%(obj)s</div>' % {'lightColor': self.getColor('blueColor', 0), 'htmlId': self.htmlId, 'index': i, 'obj': tab.html() if hasattr(tab, 'html') else tab})
        else:
          items.append('<div id="%(htmlId)s_panel_%(index)s" name="panel_%(htmlId)s" style="text-align:center;display:none;border:1px solid %(lightColor)s;padding:10px">%(obj)s</div>' % {'lightColor': self.getColor('blueColor', 0), 'htmlId': self.htmlId, 'index': i, 'obj': tab.html() if hasattr(tab, 'html') else tab})
      items.append('<div id="%(htmlId)s_panel_loading" style="text-align:center;display:none;border:1px solid %(lightColor)s;padding:10px;font-size:18px"><i class="fas fa-spinner fa-spin"></i><br />Loading...</div>' % {'lightColor': self.getColor('blueColor', 0), 'htmlId': self.htmlId})
      return "".join(items)
  
  
  class Pills(Tabs):
    """ Python wrapper to the Bootstrap Pills interface """
    cssCls = ['nav', 'nav-pills']
    name, category, callFnc, docCategory = 'Pills', 'Container', 'pills', 'Advanced'
    __reqCss = ['bootstrap']
    references = {'Pills Bootstrap': 'https://getbootstrap.com/docs/4.0/components/navs/'}
    __pyStyle = ['CssDivNoBorder']
  
  
  class IFrame(AresHtml.Html):
    name, category, callFnc, docCategory = 'IFrame', 'Container', 'iframe', 'Advanced'
    __reqCss = ['bootstrap']
    __pyStyle = ['CssDivNoBorder']
  
    def __init__(self, aresObj, url, width, widthUnit, height, heightUnit):
      super(IFrame, self).__init__(aresObj, url, width=width, widthUnit=widthUnit, height=height, heightUnit=heightUnit)
      self.css( {"overflow-x": 'hidden'} )
  
    def __str__(self):
      return "<iframe src='%s' %s frameborder='0' scrolling='no'></iframe>" % (self.vals, self.strAttr(pyClassNames=self.pyStyle))
  
  
  class Dialog(AresHtml.Html):
    name, category, callFnc, docCategory = 'DialogMenu', 'Container', 'dialogs', 'Advanced'
    __reqCss, __reqJs = ['jqueryui', 'datatables', 'datatables-export'], ['jquery', 'datatables', 'datatables-export']
  
    def __init__(self, aresObj, recordSet, width, widthUnit, height, heightUnit):
      super(Dialog, self).__init__(aresObj, recordSet, width=width, widthUnit=widthUnit, height=height, heightUnit=heightUnit)
      self.css( {"border": '2px solid #F4F4F4'} )
  
    def jsAdd(self, title='data.event_val', isPyData=False):
      if isPyData :
        title = json.loads(title)
  
      return ''' 
      var dialogWindow = $("<div>");
      var table = $("table");
      dialogWindow.append(table) ;
      dialogWindow.append('<input type="text">') ;
      var d = dialogWindow.dialog( { modal: false, title: %(title)s, show: 'puff', fluid: true,
          close: function () { $(this).remove() } , appendTo: "#%(htmlId)s", resizable: false,
          buttons: [ { text: "Update", click: function() { $( this ).dialog( "close" ); } } ]
      } ) ;
      d.parent().draggable( {containment: '#%(htmlId)s'} ) ; 
      event.preventDefault();
      ''' % {'title': title, 'htmlId': self.htmlId}
  
    def __str__(self):
      return "<div %s></div>" % self.strAttr()
  
  
  class IconsMenu(AresHtml.Html):
    name, category, callFnc, docCategory = 'Icons Menu', 'Container', 'menu', 'Advanced'
    __reqCss, __reqJs = ['jqueryui', 'datatables', 'datatables-export'], ['jquery', 'datatables', 'datatables-export']
  
    def __init__(self, aresObj, width, widthUnit, height, heightUnit, htmlCode):
      super(IconsMenu, self).__init__(aresObj, None, width=width, widthUnit=widthUnit, height=height, heightUnit=heightUnit, code=htmlCode)
      self._jsActions, self._definedActions = {}, []
      self.css( {"margin": "5px 0"})
      self.addGlobalVar("%s_items" % self.htmlId, "{}")
  
    @property
    def val(self):
      return "%s_items" % self.htmlId
  
    def jsAction(self, action, icon, tooltip, pyCssCls="CssBigIcon", url=None, jsData=None, jsFncs=None, httpCodes=None):
      if not isinstance(jsFncs, list):
        jsFncs = [jsFncs] if jsFncs is not None else []
  
      # Add this to an ajax POST call if an URL is defined
      fnc = self.aresObj.jsPost(url=url, jsData=jsData, jsFnc=jsFncs, httpCodes=httpCodes) if url is not None else ";".join(jsFncs)
      self._jsActions[action] = "<span id='%(htmlId)s_%(action)s' title='%(tooltip)s' class='%(cssStyle)s %(icon)s'></span>" % {
        "icon": icon, "cssStyle": self.addPyCss(pyCssCls), "htmlId": self.htmlId, 'tooltip': tooltip, 'action': action}
      self.aresObj.jsOnLoadFnc.add("$('#%(htmlId)s_%(action)s').on('click', function(event) { %(jsFncs)s; %(htmlId)s_items['%(action)s'] = true; })" % {"htmlId": self.htmlId, "jsFncs": fnc, 'action': action})
      if action not in self._definedActions:
        self._definedActions.append(action)
      return self
  
    def addSelect(self, action, data, width=150):
      options = []
      for d in data:
        options.append("<option>%s</option>" % d)
      self._jsActions[action] = '<select id="inputState" class="form-control" style="width:%spx;display:inline-block;font-size:12px;">%s</select>' % (width, "".join(options))
      self._definedActions.append(action)
      return self
  
    def __str__(self):
      htmlIcons = []
      for action, htmlDef in self._jsActions.items():
        htmlIcons.append(htmlDef)
      return "<div %s>%s</div>" % (self.strAttr(), "".join(htmlIcons))
  