/* * :organization: Logilab * :copyright: 2003-2008 LOGILAB S.A. (Paris, FRANCE), all rights reserved. * :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr * * */// widget namespaceWidgets={};/* this function takes a DOM node defining a widget and * instantiates / builds the appropriate widget class */functionbuildWidget(wdgnode){varwdgclass=Widgets[wdgnode.getAttribute('cubicweb:wdgtype')];if(wdgclass){varwdg=newwdgclass(wdgnode);}}/* This function is called on load and is in charge to build * JS widgets according to DOM nodes found in the page */functionbuildWidgets(root){root=root||document;jQuery(root).find('.widget').each(function(){if(this.getAttribute('cubicweb:loadtype')=='auto'){buildWidget(this);}});}// we need to differenciate cases where initFacetBoxEvents is called// with one argument or without any argument. If we use `initFacetBoxEvents`// as the direct callback on the jQuery.ready event, jQuery will pass some argument// of his, so we use this small anonymous function instead.jQuery(document).ready(function(){buildWidgets();});Widgets.SuggestField=defclass('SuggestField',null,{__init__:function(node,options){varmulti=node.getAttribute('cubicweb:multi')||"no";options=options||{};options.multiple=(multi=="yes")?true:false;vardataurl=node.getAttribute('cubicweb:dataurl');varmethod=postJSON;if(options.method=='get'){method=function(url,data,callback){// We can't rely on jQuery.getJSON because the server// might set the Content-Type's response header to 'text/plain'jQuery.get(url,data,function(response){callback(evalJSON(response));});};}varself=this;// closuremethod(dataurl,null,function(data){// in case we received a list of couple, we assume that the first// element is the real value to be sent, and the second one is the// value to be displayedif(data.length&&data[0].length==2){options.formatItem=function(row){returnrow[1];};self.hideRealValue(node);self.setCurrentValue(node,data);}jQuery(node).autocomplete(data,options);});},hideRealValue:function(node){varhidden=INPUT({'type':"hidden",'name':node.name,'value':node.value});node.parentNode.appendChild(hidden);// remove 'name' attribute from visible input so that it is not submitted// and set correct value in the corresponding hidden fieldjQuery(node).removeAttr('name').bind('result',function(_,row,_){hidden.value=row[0];});},setCurrentValue:function(node,data){// called when the data is loaded to reset the correct displayed// value in the visible input field (typically replacing an eid// by a displayable value)varcurvalue=node.value;if(!node.value){return;}for(vari=0,length=data.length;i<length;i++){varrow=data[i];if(row[0]==curvalue){node.value=row[1];return;}}}});Widgets.StaticFileSuggestField=defclass('StaticSuggestField',[Widgets.SuggestField],{__init__:function(node){Widgets.SuggestField.__init__(this,node,{method:'get'});}});Widgets.RestrictedSuggestField=defclass('RestrictedSuggestField',[Widgets.SuggestField],{__init__:function(node){Widgets.SuggestField.__init__(this,node,{mustMatch:true});}});/* * suggestform displays a suggest field and associated validate / cancel buttons * constructor's argumemts are the same that BaseSuggestField widget */Widgets.SuggestForm=defclass("SuggestForm",null,{__init__:function(inputid,initfunc,varargs,validatefunc,options){this.validatefunc=validatefunc||noop;this.sgfield=newWidgets.BaseSuggestField(inputid,initfunc,varargs,options);this.oklabel=options.oklabel||'ok';this.cancellabel=options.cancellabel||'cancel';bindMethods(this);connect(this.sgfield,'validate',this,this.entryValidated);},show:function(parentnode){varsgnode=this.sgfield.builddom();varbuttons=DIV({'class':"sgformbuttons"},[A({'href':"javascript: noop();",'onclick':this.onValidateClicked},this.oklabel),' / ',A({'href':"javascript: noop();",'onclick':this.destroy},escapeHTML(this.cancellabel))]);varformnode=DIV({'class':"sgform"},[sgnode,buttons]);appendChildNodes(parentnode,formnode);this.sgfield.textinput.focus();this.formnode=formnode;returnformnode;},destroy:function(){signal(this,'destroy');this.sgfield.destroy();removeElement(this.formnode);},onValidateClicked:function(){this.validatefunc(this,this.sgfield.taglist());},/* just an indirection to pass the form instead of the sgfield as first parameter */entryValidated:function(sgfield,taglist){this.validatefunc(this,taglist);}});/* called when the use clicks on a tree node * - if the node has a `cubicweb:loadurl` attribute, replace the content of the node * by the url's content. * - else, there's nothing to do, let the jquery plugin handle it. */functiontoggleTree(event){varlinode=jQuery(this);varurl=linode.attr('cubicweb:loadurl');linode.find('ul.placeholder').remove();if(url){linode.loadxhtml(url,{callback:function(domnode){linode.removeAttr('cubicweb:loadurl');jQuery(domnode).treeview({toggle:toggleTree,prerendered:true});returnnull;}},'post','append');}}Widgets.TreeView=defclass("TreeView",null,{__init__:function(wdgnode){jQuery(wdgnode).treeview({toggle:toggleTree,prerendered:true});}});/* widget based on SIMILE's timeline widget * http://code.google.com/p/simile-widgets/ * * Beware not to mess with SIMILE's Timeline JS namepsace ! */Widgets.TimelineWidget=defclass("TimelineWidget",null,{__init__:function(wdgnode){vartldiv=DIV({id:"tl",style:'height: 200px; border: 1px solid #ccc;'});wdgnode.appendChild(tldiv);vartlunit=wdgnode.getAttribute('cubicweb:tlunit')||'YEAR';vareventSource=newTimeline.DefaultEventSource();varbandData={eventPainter:Timeline.CubicWebEventPainter,eventSource:eventSource,width:"100%",intervalUnit:Timeline.DateTime[tlunit.toUpperCase()],intervalPixels:100};varbandInfos=[Timeline.createBandInfo(bandData)];vartl=Timeline.create(tldiv,bandInfos);varloadurl=wdgnode.getAttribute('cubicweb:loadurl');Timeline.loadJSON(loadurl,function(json,url){eventSource.loadJSON(json,url);});}});Widgets.TemplateTextField=defclass("TemplateTextField",null,{__init__:function(wdgnode){this.variables=getNodeAttribute(wdgnode,'cubicweb:variables').split(',');this.options={'name':wdgnode.getAttribute('cubicweb:inputid'),'rows':wdgnode.getAttribute('cubicweb:rows')||40,'cols':wdgnode.getAttribute('cubicweb:cols')||80};// this.variableRegexp = /%\((\w+)\)s/;this.errorField=DIV({'class':"errorMessage"});this.textField=TEXTAREA(this.options);jQuery(this.textField).bind('keyup',{'self':this},this.highlightInvalidVariables);jQuery('#substitutions').prepend(this.errorField);jQuery('#substitutions .errorMessage').hide();wdgnode.appendChild(this.textField);},/* signal callbacks */highlightInvalidVariables:function(event){varself=event.data.self;vartext=self.textField.value;varunknownVariables=[];varit=0;vargroup=null;varvariableRegexp=/%\((\w+)\)s/g;// emulates rgx.findAll()while(group=variableRegexp.exec(text)){if(!self.variables.contains(group[1])){unknownVariables.push(group[1]);}it++;if(it>5){break;}}varerrText='';if(unknownVariables.length){errText="Detected invalid variables : "+", ".join(unknownVariables);jQuery('#substitutions .errorMessage').show();}else{jQuery('#substitutions .errorMessage').hide();}self.errorField.innerHTML=errText;}});/* * ComboBox with a textinput : allows to add a new value */Widgets.AddComboBox=defclass('AddComboBox',null,{__init__:function(wdgnode){jQuery("#add_newopt").click(function(){varnew_val=jQuery("#newopt").val();if(!new_val){returnfalse;}name=wdgnode.getAttribute('name').split(':');this.rel=name[0];this.eid_to=name[1];this.etype_to=wdgnode.getAttribute('cubicweb:etype_to');this.etype_from=wdgnode.getAttribute('cubicweb:etype_from');vard=asyncRemoteExec('add_and_link_new_entity',this.etype_to,this.rel,this.eid_to,this.etype_from,'new_val');d.addCallback(function(eid){jQuery(wdgnode).find("option[selected]").removeAttr("selected");varnew_option=OPTION({'value':eid,'selected':'selected'},value=new_val);wdgnode.appendChild(new_option);});d.addErrback(function(xxx){log('xxx =',xxx);});});}});CubicWeb.provide('widgets.js');