"""html widgetsthose are in cubicweb since we need to know available widgets at schemaserialization time:organization: Logilab:copyright: 2001-2010 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.:contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses"""frommathimportfloorimportrandomfromlogilab.mtconverterimportxml_escapefromcubicweb.utilsimportUStringIOfromcubicweb.uilibimporttoggle_action,htmlescapefromcubicweb.webimportjsonize# XXX HTMLWidgets should have access to req (for datadir / static urls,# i18n strings, etc.)classHTMLWidget(object):def_initialize_stream(self,w=None):ifw:self.w=welse:self._stream=UStringIO()self.w=self._stream.writedef_render(self):raiseNotImplementedErrordefrender(self,w=None):self._initialize_stream(w)self._render()ifwisNone:returnself._stream.getvalue()defis_empty(self):returnFalseclassBoxWidget(HTMLWidget):def__init__(self,title,id,items=None,_class="boxFrame",islist=True,shadow=True,escape=True):self.title=titleself.id=idself.items=itemsor[]self._class=_classself.islist=islistself.shadow=shadowself.escape=escapedef__len__(self):returnlen(self.items)defis_empty(self):returnlen(self)==0defappend(self,item):self.items.append(item)defextend(self,items):self.items.extend(items)title_class='boxTitle'main_div_class='boxContent'listing_class='boxListing'defbox_begin_content(self):self.w(u'<div class="%s">\n'%self.main_div_class)ifself.islist:self.w(u'<ul class="%s">'%self.listing_class)defbox_end_content(self):ifself.islist:self.w(u'</ul>\n')self.w(u'</div>\n')ifself.shadow:self.w(u'<div class="shadow"> </div>')def_render(self):ifself.id:self.w(u'<div class="%s" id="%s">'%(self._class,self.id))else:self.w(u'<div class="%s">'%self._class)ifself.title:ifself.escape:title='<span>%s</span>'%xml_escape(self.title)else:title='<span>%s</span>'%self.titleself.w(u'<div class="%s">%s</div>'%(self.title_class,title))ifself.items:self.box_begin_content()foriteminself.items:item.render(self.w)self.box_end_content()self.w(u'</div>')classSideBoxWidget(BoxWidget):"""default CubicWeb's sidebox widget"""title_class=u'sideBoxTitle'main_div_class=u'sideBoxBody'listing_class=''def__init__(self,title,id=None):super(SideBoxWidget,self).__init__(title,id=id,_class='sideBox',shadow=False)classMenuWidget(BoxWidget):main_div_class='menuContent'listing_class='menuListing'defbox_end_content(self):ifself.islist:self.w(u'</ul>\n')self.w(u'</div>\n')classRawBoxItem(HTMLWidget):"""a simpe box item displaying raw data"""def__init__(self,label,liclass=None):self.label=labelself.liclass=liclassdef_start_li(self):ifself.liclassisNone:returnu'<li>'else:returnu'<li class="%s">'%self.liclassreturnself.labeldef_render(self):self.w(u'%s%s</li>'%(self._start_li(),self.label))classBoxMenu(RawBoxItem):"""a menu in a box"""link_class='boxMenu'def__init__(self,label,items=None,isitem=True,liclass=None,ident=None,link_class=None):super(BoxMenu,self).__init__(label,liclass)self.items=itemsor[]self.isitem=isitemself.ident=identoru'boxmenu_%s'%label.replace(' ','_').replace("'",'')iflink_class:self.link_class=link_classdefappend(self,item):self.items.append(item)def_begin_menu(self,ident):self.w(u'<ul id="%s" class="hidden">'%ident)def_end_menu(self):self.w(u'</ul>')def_render(self):ifself.isitem:self.w(self._start_li())ident=self.identself.w(u'<a href="%s" class="%s">%s</a>'%(toggle_action(ident),self.link_class,self.label))self._begin_menu(ident)foriteminself.items:item.render(self.w)self._end_menu()ifself.isitem:self.w(u'</li>')classPopupBoxMenu(BoxMenu):"""like BoxMenu but uses div and specific css class in order to behave like a popup menu """link_class='popupMenu'def_begin_menu(self,ident):self.w(u'<div class="popupWrapper"><div id="%s" class="hidden popup"><ul>'%ident)def_end_menu(self):self.w(u'</ul></div></div>')classBoxField(HTMLWidget):"""couples label / value meant to be displayed in a box"""def__init__(self,label,value):self.label=labelself.value=valuedef_render(self):self.w(u'<li><div><span class="label">%s</span> 'u'<span class="value">%s</span></div></li>'%(self.label,self.value))classBoxSeparator(HTMLWidget):"""a menu separator"""def_render(self):self.w(u'</ul><hr class="boxSeparator"/><ul>')classBoxLink(HTMLWidget):"""a link in a box"""def__init__(self,href,label,_class='',title='',ident='',escape=False):self.href=hrefifescape:self.label=xml_escape(label)else:self.label=labelself._class=_classor''self.title=titleself.ident=identdef_render(self):link=u'<a href="%s" title="%s">%s</a>'%(xml_escape(self.href),xml_escape(self.title),self.label)ifself.ident:self.w(u'<li id="%s" class="%s">%s</li>\n'%(self.ident,self._class,link))else:self.w(u'<li class="%s">%s</li>\n'%(self._class,link))classBoxHtml(HTMLWidget):"""a form in a box"""def__init__(self,rawhtml):self.rawhtml=rawhtmldef_render(self):self.w(self.rawhtml)classTableColumn(object):def__init__(self,name,rset_sortcol):""" :param name: the column's name :param rset_sortcol: the model's column used to sort this column view """self.name=nameself.cellrenderers=[]self.rset_sortcol=rset_sortcolself.cell_attrs={}defappend_renderer(self,cellvid,colindex):# XXX (adim) : why do we need colindex here ?self.cellrenderers.append((cellvid,colindex))defadd_attr(self,attr,value):self.cell_attrs[attr]=valueclassSimpleTableModel(object):""" uses a list of lists as a storage backend NB: the model expectes the cellvid passed to TableColumn.append_renderer to be a callable accepting a single argument and returning a unicode object """def__init__(self,rows):self._rows=rowsdefget_rows(self):returnself._rowsdefrender_cell(self,cellvid,rowindex,colindex,w):value=self._rows[rowindex][colindex]w(cellvid(value))@htmlescape@jsonizedefsortvalue(self,rowindex,colindex):value=self._rows[rowindex][colindex]ifvalueisNone:returnu''elifisinstance(value,int):returnu'%09d'%valueelse:returnunicode(value)classTableWidget(HTMLWidget):""" Display data in a Table with sortable column. When using remember to include the required css and js with: self._cw.add_js('jquery.tablesorter.js') self._cw.add_css(('cubicweb.tablesorter.css', 'cubicweb.tableview.css')) """highlight="onmouseover=\"addElementClass(this, 'highlighted');\" " \"onmouseout=\"removeElementClass(this, 'highlighted');\""def__init__(self,model):self.model=modelself.columns=[]defappend_column(self,column):""" :type column: TableColumn """self.columns.append(column)def_render(self):self.w(u'<table class="listing">')self.w(u'<thead>')self.w(u'<tr class="header">')forcolumninself.columns:attrs=('%s="%s"'%(name,value)forname,valueincolumn.cell_attrs.iteritems())self.w(u'<th %s>%s</th>'%(' '.join(attrs),column.name))self.w(u'</tr>')self.w(u'</thead><tbody>')forrowindexinxrange(len(self.model.get_rows())):klass=(rowindex%2==1)and'odd'or'even'self.w(u'<tr class="%s" %s>'%(klass,self.highlight))forcolumn,sortvalueinself.itercols(rowindex):attrs=dict(column.cell_attrs)attrs["cubicweb:sortvalue"]='json:'+sortvalueattrs=('%s="%s"'%(name,value)forname,valueinattrs.iteritems())self.w(u'<td %s>'%(' '.join(attrs)))forcellvid,colindexincolumn.cellrenderers:self.model.render_cell(cellvid,rowindex,colindex,w=self.w)self.w(u'</td>')self.w(u'</tr>')self.w(u'</tbody>')self.w(u'</table>')defitercols(self,rowindex):forcolumninself.columns:yieldcolumn,self.model.sortvalue(rowindex,column.rset_sortcol)