if (typeof UWA == "undefined") var UWA = {};
if (typeof UWA.Scripts == "undefined") UWA.Scripts = [];
UWA.Scripts['feedreader']=UWA.script=function(widget){
var FeedReader={};FeedReader.data={};FeedReader.getData=function(){var feedUrl=widget.getValue('feedUrl');if(!feedUrl||feedUrl==''){widget.body.setHTML('');widget.body.className+=' uwa-module-feedreader';var message=widget.createElement('div',{'class':'no-content'});message.setText(_("Please specify a feed URL."));widget.body.appendChild(message);return;}
var url='/json/feed?feedUrl='+escape(feedUrl);UWA.Data.getJson(url,FeedReader.displayContent);}
FeedReader.displayContent=function(data){if(data){FeedReader.data=data;}else{data=FeedReader.data;}
var title=widget.getValue('title');if(title){widget.setTitle(title);}else{if(data.title){widget.setTitle(data.title);}else{widget.setTitle(_("Feed Reader"));}}
widget.body.setHTML('');widget.body.className+=' uwa-module-feedreader';if(!data.items){var message=widget.createElement('div',{'class':'no-content'});message.setText(_("No items in the feed."));widget.body.appendChild(message);}else{var items=widget.createElement('ul',{'class':'items'});var count=widget.getValue('count');var hidethumbs=widget.getValue('hidethumbs');for(var i=0;i<data.items.length&&i<count;i++){var item=data.items[i];var node=widget.createElement('li');if(item.thumbnail&&item.thumbnail.url&&hidethumbs!='true'){var media=widget.createElement('div',{'class':'media'});var mediaUrl=widget.createElement('a',{'href':item.link});var mediaImg=widget.createElement('img',{'src':item.thumbnail.url});mediaUrl.appendChild(mediaImg);media.appendChild(mediaUrl);node.appendChild(media);}
var header=widget.createElement('h3');var title=widget.createElement('a',{'href':item.link,'title':item.title});title.setText(item.title);header.appendChild(title);node.appendChild(header);var content=widget.createElement('div',{'class':'lead'});content.setText(item.contentShort);node.appendChild(content);var clear=widget.createElement('div',{'class':'clear'});node.appendChild(clear);items.appendChild(node);}
widget.body.appendChild(items);}
if(data.htmlUrl){var footer=widget.createElement('div',{'class':'footer'});var footerLink=widget.createElement('a',{'href':data.htmlUrl,'target':'_blank'});footerLink.setText(_("visit this site"));var arrow=widget.createElement('span');footerLink.appendChild(arrow);footer.appendChild(footerLink);widget.body.appendChild(footer);}
widget.callback("onUpdateBody");}
widget.onLoad=function(){FeedReader.getData();};
widget.setMetas({"apiVersion":"1.0","autoRefresh":"20","author":"Andr\u00e1s B\u00e1rth\u00e1zi, NowPublic","description":"Found a site that has content relevant to your story? Add the URL for the site's RSS feed, choose how many posts to display, then click save.","debugMode":"false"});
widget.setPreferences([{"type":"text","name":"title","label":"Name Widget","defaultValue":""},{"type":"text","name":"feedUrl","label":"URL","defaultValue":""},{"type":"range","name":"count","label":"Number of items","defaultValue":"10","step":"1","min":"1","max":"20"},{"type":"boolean","name":"hidethumbs","label":"Hide thumbnails","defaultValue":"false"}]);
return widget;
}
if (typeof UWA == "undefined") var UWA = {};
if (typeof UWA.Scripts == "undefined") UWA.Scripts = [];
UWA.Scripts['headlines']=UWA.script=function(widget){
var Headlines={};Headlines.data={};Headlines.getData=function(){var filter=widget.getValue('filter')
var url='/json/headlines?order='+widget.getValue('order')+'&filter='+filter;if(filter=='tag'){url+='&tag='+widget.getValue('tag');}
if(filter=='member'){url+='&member='+widget.getValue('member');}
if(widget.getValue('hidetop')=='true'){url+='&hidetop=true';}
if(widget.getValue('feature')=='true'){url+='&feature=true';}
if(widget.getValue('showauthor')=='true'){url+='&showauthor=true';}
UWA.Data.getJson(url,Headlines.displayContent);}
Headlines.displayContent=function(data){if(data){Headlines.data=data;}else{data=Headlines.data;}
var title=widget.getValue('title');if(title){widget.setTitle(title);}else{widget.setTitle(_('Headlines'));}
widget.body.setHTML('');widget.body.className+=' uwa-module-headlines';var nodes=widget.createElement('ul',{'class':'nodes'});var count=widget.getValue('count');for(var i=0;i<data.nodes.length&&i<count;i++){var item=data.nodes[i];var node=widget.createElement('li');if(item.media){var nodeLink=widget.createElement('a',{'href':item.url,'title':item.title,'class':'media'});var nodeImg=widget.createElement('img',{'src':item.media.thumb,'alt':item.title,'width':80,'height':60});nodeLink.appendChild(nodeImg);node.appendChild(nodeLink);}
var header=widget.createElement('h3');var title=widget.createElement('a',{'href':item.url,'title':item.title});title.setText(item.title);header.appendChild(title);node.appendChild(header);if(widget.getValue('showauthor')=='true'){var meta=widget.createElement('div',{'class':'meta'});meta.setHTML(_("Created by")+' '+item.author_url+' | <a href="'+item.url+'#comments">'+_('add comment')+'</a>')
node.appendChild(meta);}
var clear=widget.createElement('div',{'class':'clear'});node.appendChild(clear);nodes.appendChild(node);}
widget.body.appendChild(nodes);widget.callback("onUpdateBody");}
widget.onLoad=function(){Headlines.getData();};
widget.setMetas({"apiVersion":"1.0","autoRefresh":"20","author":"Andr\u00e1s B\u00e1rth\u00e1zi, NowPublic","description":"NowPublic Headlines module","debugMode":"false"});
widget.setPreferences([{"type":"text","name":"title","label":"Name Widget","defaultValue":""},{"type":"list","name":"order","label":"Show","defaultValue":"recent","options":[{"label":"Most recent","value":"recent"},{"label":"Most recommended","value":"votes"},{"label":"Most viewed","value":"popgs"}]},{"type":"boolean","name":"feature","label":"Show media on first story","defaultValue":"false"},{"type":"boolean","name":"hidetop","label":"Hide top story","defaultValue":"false"},{"type":"boolean","name":"showauthor","label":"Show author info","defaultValue":"true"},{"type":"range","name":"count","label":"Number of items","defaultValue":"10","step":"1","min":"1","max":"20"},{"type":"list","name":"filter","label":"Filter","defaultValue":"all","options":[{"label":"Show all content","value":"all"},{"label":"Choose a tag (eg. politics)","value":"tag","show":"tag"},{"label":"Choose a member's content","value":"member","show":"member"}]},{"type":"text","name":"tag","label":"Tag","defaultValue":"","autocomplete":"\/np_tags\/autocomplete"},{"type":"text","name":"member","label":"Member name","defaultValue":"","autocomplete":"\/user\/autocomplete"}]);
return widget;
}
if (typeof UWA == "undefined") var UWA = {};
if (typeof UWA.Scripts == "undefined") UWA.Scripts = [];
UWA.Scripts['comments']=UWA.script=function(widget){
var Comments={};Comments.data={};Comments.getData=function(){var order=widget.getValue('order')
var url='/json/comments?order='+order+'&content=this&_ptid='+widget.getValue('_ptid');UWA.Data.getJson(url,Comments.displayContent);}
Comments.displayContent=function(data){if(data){Comments.data=data;}else{data=Comments.data;}
var title=widget.getValue('title');if(title){widget.setTitle(title);}else{widget.setTitle(_('Comments'));}
widget.body.setHTML('');widget.body.className+=' uwa-module-comments';if(!data.comments){var message=widget.createElement('div',{'class':'no-content'});message.setText(_("There are no comments yet on this story."));widget.body.appendChild(message);}else{var comments=widget.createElement('ul',{'class':'comments'});var count=widget.getValue('count');for(var i=0;i<data.comments.length&&i<count;i++){var item=data.comments[i];var node=widget.createElement('li');var comment=widget.createElement('blockquote',{'class':'comment'});var commentText=widget.createElement('a',{'href':item.url,'title':item.comment});commentText.setText(item.comment);comment.appendChild(commentText);node.appendChild(comment);var member=widget.createElement('div',{'class':'member'});member.setHTML(item.author_picture);node.appendChild(member);var clear=widget.createElement('div',{'class':'clear'});node.appendChild(clear);comments.appendChild(node);}}
widget.body.appendChild(comments);widget.callback("onUpdateBody");}
widget.onLoad=function(){Comments.getData();};
widget.setMetas({"apiVersion":"1.0","autoRefresh":"20","author":"Andr\u00e1s B\u00e1rth\u00e1zi, NowPublic","description":"Using the comment widget you can feature the most recent or most recommended comment in the body of your story.","debugMode":"false"});
widget.setPreferences([{"type":"text","name":"title","label":"Name Widget","defaultValue":""},{"type":"list","name":"order","label":"Show","defaultValue":"recent","options":[{"label":"Most recent","value":"recent"},{"label":"Most recommended","value":"votes"}]},{"type":"range","name":"count","label":"Number of items","defaultValue":"5","step":"1","min":"1","max":"5"}]);
return widget;
}
if (typeof UWA == "undefined") var UWA = {};
if (typeof UWA.Scripts == "undefined") UWA.Scripts = [];
UWA.Scripts['quote']=UWA.script=function(widget){
var Quote={};Quote.displayContent=function(){var title=widget.getValue('title');if(title){widget.setTitle(title);}else{widget.setTitle(_('Quote'));}
widget.body.setHTML('');widget.body.className+=' uwa-module-quote';var source=widget.getValue('source')||'';if(source&&source.match(/^https?:\/\//)&&source.length==7){source='';}
if(source.length&&!source.match(/^https?:\/\//)){source='http://'+source;}
var hcitation=widget.createElement('div',{'class':'hcitation'});var blockquote=widget.createElement('blockquote',{'class':'bq-style-01'});if(source){blockquote.cite=source;}
var blockquoteText=widget.createElement('div');blockquoteText.setHTML(widget.getValue('quote'));blockquote.appendChild(blockquoteText);hcitation.appendChild(blockquote);var cite=widget.createElement('cite',{'class':'author'});cite.setText(widget.getValue('author'));hcitation.appendChild(cite);widget.body.appendChild(hcitation);if(source){var footer=widget.createElement('div',{'class':'footer'});footer.setText(_("Source:")+' ');var footerLink=widget.createElement('a',{'href':source});footerLink.setText(source);footer.appendChild(footerLink);widget.body.appendChild(footer);}
widget.callback("onUpdateBody");}
widget.onLoad=function(){Quote.displayContent();};
widget.setMetas({"apiVersion":"1.0","autoRefresh":"20","author":"Andr\u00e1s B\u00e1rth\u00e1zi, NowPublic","description":"Use this widget to add a quote from anywhere on the web. Just paste in the quote contents, the author's name and the url of the web page you found the quote on.","debugMode":"false"});
widget.setPreferences([{"type":"text","name":"title","label":"Name Widget","defaultValue":""},{"type":"textarea","name":"quote","label":"Quote","defaultValue":""},{"type":"text","name":"author","label":"Author","defaultValue":""},{"type":"text","name":"source","label":"Source URL","defaultValue":"http:\/\/"}]);
return widget;
}
if (typeof UWA == "undefined") var UWA = {};
if (typeof UWA.Scripts == "undefined") UWA.Scripts = [];
UWA.Scripts['qik']=UWA.script=function(widget){
var Qik={};Qik.displayContent=function(){var title=widget.getValue('title');if(title){widget.setTitle(title);}else{widget.setTitle(_("Qik Video"));}
widget.body.setHTML('');widget.body.className+=' uwa-module-qik';var permalink=widget.getValue('permalink')||'';var matches=permalink.match(/^\s*(http?:\/\/qik\.com\/video\/\d+)\s*$/);if(matches){var url='http://qik.com/api/oembed.json?url='+encodeURIComponent(matches[1]);UWA.Data.getJson(url,Qik.displayData);}
else{Qik.displayData();}};Qik.displayData=function(data){if(data){embedHTML=data.html;}else{embedHTML='';}
var description=(widget.getValue('description')||'').substring(0,80);var embedDiv=widget.createElement('div',{'class':'embed'});var descDiv=widget.createElement('div',{'class':'description'});descDiv.setHTML(description);var prefWidth=300;var prefHeight=225;if(widget.getValue('_align')=='center'){prefWidth=620;prefHeight=466;}
embedDiv.setHTML(embedHTML.replace(/width="\d+"/g,'width="'+prefWidth+'"').replace(/height="\d+"/g,'height="'+prefHeight+'"'));if(data){var info=widget.createElement('div',{'class':'info'});var author=widget.createElement('div',{'class':'author'});author.appendText('by ');var authorLink=widget.createElement('a',{'href':data.author_url.replace(/^\s+|\s+$/g,''),'target':'_blank'});authorLink.appendText(data.author_name);author.appendChild(authorLink);info.appendChild(author);var titleLink=widget.createElement('a',{'href':widget.getValue('permalink').replace(/^\s+|\s+$/g,''),'class':'title','target':'_blank'});titleLink.appendText(data.title);info.appendChild(titleLink);embedDiv.appendChild(info);}
widget.body.appendChild(embedDiv);widget.body.appendChild(descDiv);widget.callback("onUpdateBody");};widget.onLoad=function(){Qik.displayContent();};
widget.setMetas({"apiVersion":"1.0","autoRefresh":"20","author":"Peter Galiba, NowPublic","description":"Use this widget to add a Qik video from qik.com. Just paste in the permalink, and add some description to it.","debugMode":"false"});
widget.setPreferences([{"type":"text","name":"title","label":"Name Widget","defaultValue":"Qik Video"},{"type":"textarea","name":"description","label":"Description (80 characters max)","defaultValue":""},{"type":"text","name":"permalink","label":"Qik permalink","defaultValue":""}]);
return widget;
}
if (typeof UWA == "undefined") var UWA = {};
if (typeof UWA.Scripts == "undefined") UWA.Scripts = [];
UWA.Scripts['thumbnails']=UWA.script=function(widget){
var Thumbnails={};Thumbnails.data={};Thumbnails.getData=function(){var content=widget.getValue('content')||'this';var order=widget.getValue('order');var url='/json/media?content='+content+'&order='+order+'&type=footage';if(content=='this'){url+='&_ptid='+widget.getValue('_ptid');}
if(content=='other'){url+='&node='+widget.getValue('node');}
UWA.Data.getJson(url,Thumbnails.displayContent);}
Thumbnails.displayContent=function(data){if(data){Thumbnails.data=data;}else{data=Thumbnails.data;}
var title=widget.getValue('title');if(title){widget.setTitle(title);}else{widget.setTitle(_('Photos and Videos'));}
widget.body.setHTML('');widget.body.className+=' uwa-module-thumbnails';var images;var count=widget.getValue("count")*(widget.getValue('_align')=='center'?6:3);var col=0;if(!!data.medias.length){images=widget.createElement('ul',{'class':'images'});for(var i=0;i<data.medias.length&&i<count;i++){var item=data.medias[i];var node=widget.createElement('li',{'class':'col'+col});var nodeLink=widget.createElement('a',{'href':item.path,'title':item.title});var nodeImg=widget.createElement('img',{'src':item.thumb,'alt':item.title,'width':96,'height':72});nodeLink.appendChild(nodeImg);node.appendChild(nodeLink);images.appendChild(node);col++;var align=widget.getValue('_align');if(align=='center'){if(col==6){col=0;}}else{if(col==3){col=0;}}}}
else{images=widget.createElement('div',{'class':'no-content'});images.setText(_("No media found."));}
widget.body.appendChild(images);}
widget.onLoad=function(){Thumbnails.getData();};
widget.setMetas({"apiVersion":"1.0","autoRefresh":"20","author":"Andr\u00e1s B\u00e1rth\u00e1zi, NowPublic","description":"Use this widget to show all of the media that has been added to your story. You can organize the the files by most recent, most viewed or most recommended. If you'd like to order the widgets use the manage footage tab to drag and drop your files into this widget.","debugMode":"false"});
widget.setPreferences([{"type":"text","name":"title","label":"Name Widget","defaultValue":""},{"type":"list","name":"content","label":"Content","defaultValue":"this","options":[{"label":"This story","value":"this"},{"label":"Other NowPublic story","value":"other","show":"node"}]},{"type":"list","name":"order","label":"Show","defaultValue":"recent","options":[{"label":"Most recent","value":"recent"},{"label":"Most recommended","value":"votes"},{"label":"Most viewed","value":"popgs"}]},{"type":"text","name":"node","label":"Other NowPublic story URL","defaultValue":""},{"type":"range","name":"count","label":"Number of rows","defaultValue":"2","step":"1","min":"1","max":"3"}]);
return widget;
}
if (typeof UWA == "undefined") var UWA = {};
if (typeof UWA.Scripts == "undefined") UWA.Scripts = [];
UWA.Scripts['slideshow']=UWA.script=function(widget){
var Slideshow={};Slideshow.info={currentImage:0,sliderItems:3,mouseover:false,mousetimeout:null,timeout:null,widget:null,interval:7};if(widget.getValue('_align')=='center'){Slideshow.info.sliderItems=6;}
Slideshow.data={};Slideshow.getData=function(){var content=widget.getValue('content')||'this';var order=widget.getValue('order');var url='/json/media?order='+order+'&type=photo&content='+content;if(content=='this'){url+='&_ptid='+widget.getValue('_ptid');}
if(content=='other'){url+='&node='+widget.getValue('node');}
UWA.Data.getJson(url,Slideshow.displayContent);};Slideshow.displayContent=function(data){if(data){Slideshow.data=data;}else{data=Slideshow.data;}
var title=widget.getValue('title');if(title){widget.setTitle(title);}else{widget.setTitle(_('Photos'));}
widget.body.setHTML('');widget.body.className+=' uwa-module-slideshow';var first=data.medias[0];if(!first){var message=widget.createElement('div',{'class':'no-content'});message.setText(_("No photos found."));widget.body.appendChild(message);return;}
var mediaContainer=widget.createElement('div',{'class':'media'});Slideshow.mediaSpan=widget.createElement('span',{'class':'media'});Slideshow.mediaImg=widget.createElement('img',{'src':Drupal.settings.media_url+first.url,'alt':first.title,'title':first.title,'style':'visibility: hidden;'});Slideshow.mediaImg.onload=Slideshow.mediaImgLoad;Slideshow.mediaSpan.appendChild(Slideshow.mediaImg);mediaContainer.appendChild(Slideshow.mediaSpan);Slideshow.mediaInfoDiv=widget.createElement('div',{'class':'info'});Slideshow.mediaInfoTitle=widget.createElement('h2');Slideshow.mediaInfoTitle.setText(first.title);Slideshow.mediaInfoDesc=widget.createElement('p',{'class':'info'});Slideshow.mediaInfoDesc.setText('');Slideshow.mediaInfoDiv.appendChild(Slideshow.mediaInfoTitle);Slideshow.mediaInfoDiv.appendChild(Slideshow.mediaInfoDesc);mediaContainer.appendChild(Slideshow.mediaInfoDiv);Slideshow.mediaMore=widget.createElement('p',{'class':'more'});Slideshow.mediaMoreLink=widget.createElement('a',{'href':first.path});Slideshow.mediaMoreLink.setText(_("see larger image")+' ');var mediaMoreLinkImg=widget.createElement('img',{'src':'/sites/all/themes/nova/default/images/1px.gif','class':'arrow-01-r4'});Slideshow.mediaMoreLink.appendChild(mediaMoreLinkImg);Slideshow.mediaMore.appendChild(Slideshow.mediaMoreLink);mediaContainer.appendChild(Slideshow.mediaMore);Slideshow.mediaNext=widget.createElement('a',{'class':'next','href':'#right'});var mediaNextImg=widget.createElement('img',{'src':'/sites/all/themes/nova/default/images/1px.gif','class':'arrow-03-r5'});mediaNextImg.onclick=function(){Slideshow.showImage(Slideshow.info.currentImage+1);return false;};Slideshow.mediaNext.appendChild(mediaNextImg);mediaContainer.appendChild(Slideshow.mediaNext);Slideshow.mediaPrev=widget.createElement('a',{'class':'prev','href':'#left'});var mediaPrevImg=widget.createElement('img',{'src':'/sites/all/themes/nova/default/images/1px.gif','class':'arrow-03-l5'});mediaPrevImg.onclick=function(){Slideshow.showImage(Slideshow.info.currentImage-1);return false;};Slideshow.mediaPrev.appendChild(mediaPrevImg);mediaContainer.appendChild(Slideshow.mediaPrev);widget.body.appendChild(mediaContainer);if(widget.getValue('thumbs')!='true'&&data.medias.length>1){var footageContainer=widget.createElement('div',{'class':'content-footage'});var footageList=widget.createElement('ul',{'class':'list-footage','style':'width:'+(data.medias.length*100+(widget.getValue('_align')=='center'?24:20))+'px'});for(var i=0;i<data.medias.length;i++){var item=data.medias[i];var node=widget.createElement('li');var nodeLink=widget.createElement('a',{'href':item.path,'title':item.title,'no':i});nodeLink.onclick=Slideshow.showImage;var nodeImg=widget.createElement('img',{'src':item.thumb,'alt':item.title,'width':80,'height':60});nodeLink.appendChild(nodeImg);node.appendChild(nodeLink);footageList.appendChild(node);}
footageContainer.appendChild(footageList);widget.body.appendChild(footageContainer);if(data.medias.length>Slideshow.info.sliderItems){var sliderContainer=widget.createElement('div',{'class':'sliderContainer'});var sliderLeft=widget.createElement('img',{'href':'/sites/all/themes/nova/default/images/1px.gif','class':'arrow-01-l3'});sliderContainer.appendChild(sliderLeft);var sliderRight=widget.createElement('img',{'href':'/sites/all/themes/nova/default/images/1px.gif','class':'arrow-01-r3'});sliderContainer.appendChild(sliderRight);var sliderHandler=widget.createElement('div',{'class':'slider-handle'});var sliderDiv=widget.createElement('div',{'class':'slider'});sliderDiv.appendChild(sliderHandler);sliderContainer.appendChild(sliderDiv);widget.body.appendChild(sliderContainer);var slider=$(sliderDiv).slider({handle:'.slider-handle',steps:data.medias.length-Slideshow.info.sliderItems,max:data.medias.length-Slideshow.info.sliderItems,slide:function(ev,ui){footageList.style.left=(-ui.value*100)+'px';}});sliderLeft.onclick=sliderRight.onclick=function(){slider.slider("moveTo",slider.slider("value",0)+($(this).hasClass("arrow-01-r3")?1:-1));this.blur();return false;};}}
widget.callback("onUpdateBody");Slideshow.setSlideshowTimeout();mediaContainer.onmouseover=function(){if(Slideshow.info.mousetimeout){window.clearTimeout(Slideshow.info.mousetimeout);}
if(Slideshow.info.mouseover){return;}
Slideshow.info.mouseover=true;Slideshow.stopSlideshow();$(Slideshow.mediaInfoDiv).slideDown(300);$(Slideshow.mediaMore).slideDown(300);$(Slideshow.mediaNext).fadeIn(300);$(Slideshow.mediaPrev).fadeIn(300);};mediaContainer.onmouseout=function(){if(Slideshow.info.mousetimeout){window.clearTimeout(Slideshow.info.mousetimeout);}
Slideshow.info.mouseover=false;Slideshow.info.mousetimeout=window.setTimeout(function(){Slideshow.setSlideshowTimeout();$(Slideshow.mediaInfoDiv).slideUp(300);$(Slideshow.mediaMore).slideUp(300);$(Slideshow.mediaNext).fadeOut(300);$(Slideshow.mediaPrev).fadeOut(300);},300);};};Slideshow.mediaImgLoad=function(){var w=this.width;if(this.parentNode&&this.width>this.parentNode.offsetWidth){w=this.parentNode.offsetWidth;this.style.width=w+'px';}
var h=this.height;var r=w/h;if(this.parentNode&&this.height>this.parentNode.offsetHeight){h=this.parentNode.offsetHeight;w=Math.round(r*h);this.style.height=h+'px';this.style.width=w+'px';}
this.style.margin='0';var m;if(this.parentNode&&w<this.parentNode.offsetWidth){m=Math.round((this.parentNode.offsetWidth-w)/2);this.style.marginLeft=m+'px';this.style.marginRight=m+'px';}
if(this.parentNode&&h<this.parentNode.offsetHeight){m=Math.round((this.parentNode.offsetHeight-h)/2);this.style.marginTop=m+'px';}
this.style.visibility='visible';};Slideshow.showImage=function(item){if(typeof item!='number'){item=this.getAttribute('no')*1;}
if(item>=Slideshow.data.medias.length){item=0;}
if(item<0){item=Slideshow.data.medias.length-1;}
Slideshow.info.currentImage=item;var media=Slideshow.data.medias[item];Slideshow.mediaSpan.innerHTML='';Slideshow.mediaImg=widget.createElement('img',{'src':Drupal.settings.media_url+media.url,'alt':media.title,'title':media.title,'style':'visibility: hidden;'});Slideshow.mediaImg.onload=Slideshow.mediaImgLoad;Slideshow.mediaSpan.appendChild(Slideshow.mediaImg);Slideshow.mediaInfoTitle.setText(media.title);Slideshow.mediaMoreLink.href=media.path;return false;};Slideshow.doSlideshow=function(){if(Slideshow.info.mouseover){return;}
var item=Slideshow.info.currentImage+1;if(item>=Slideshow.data.medias.length){item=0;}
Slideshow.info.currentImage=item;if(Slideshow.info.timeout){window.clearTimeout(Slideshow.info.timeout);}
Slideshow.showImage(item);Slideshow.mediaImg.onload=function(){Slideshow.setSlideshowTimeout();Slideshow.mediaImgLoad.call(Slideshow.mediaImg);};};Slideshow.stopSlideshow=function(){if(Slideshow.info.timeout){window.clearTimeout(Slideshow.info.timeout);}};Slideshow.setSlideshowTimeout=function(){if(widget.getValue('advance')=='true'&&Slideshow.data.medias.length>1&&Slideshow.info.interval>0){Slideshow.info.timeout=window.setTimeout(function(){Slideshow.doSlideshow();},Slideshow.info.interval*1000);}};widget.onLoad=function(){Slideshow.info.interval=widget.getValue('interval');Slideshow.getData();};
widget.setMetas({"apiVersion":"1.0","autoRefresh":"20","author":"Andr\u00e1s B\u00e1rth\u00e1zi, NowPublic","description":"Use this widget to show one or more photo files. By default any photos added to the story will appear here. You can choose to have the videos scroll through automatically and set the speed of the scroll using the slider.","debugMode":"false"});
widget.setPreferences([{"type":"text","name":"title","label":"Name Widget","defaultValue":""},{"type":"list","name":"order","label":"Show","defaultValue":"recent","options":[{"label":"Most recent","value":"recent"},{"label":"Most recommended","value":"votes"},{"label":"Most viewed","value":"popgs"},{"label":"Custom","value":"footage_weight"}]},{"type":"boolean","name":"advance","label":"Auto advance","defaultValue":"false"},{"type":"range","name":"interval","label":"Advance speed (seconds)","defaultValue":"7","step":"1","min":"1","max":"15"},{"type":"boolean","name":"thumbs","label":"Hide thumbnails","defaultValue":"false"}]);
return widget;
}
if (typeof UWA == "undefined") var UWA = {};
if (typeof UWA.Scripts == "undefined") UWA.Scripts = [];
UWA.Scripts['playlist']=UWA.script=function(widget){
var Playlist={};Playlist.info={currentVideo:0,sliderItems:3,mouseover:false,mousetimeout:null,timeout:null,widget:null,interval:7};if(widget.getValue('_align')=='center'){Playlist.info.sliderItems=6;}
Playlist.data={};Playlist.getData=function(){var content=widget.getValue('content')||'this';var order=widget.getValue('order');var align=widget.getValue('_align');var url='/json/media?order='+order+'&type=video,audio&content='+content+'&_align='+widget.getValue('_align');if(content=='this'){url+='&_ptid='+widget.getValue('_ptid');}
if(content=='other'){url+='&node='+widget.getValue('node');}
UWA.Data.getJson(url,Playlist.displayContent);};Playlist.displayContent=function(data){if(data){Playlist.data=data;}else{data=Playlist.data;}
var title=widget.getValue('title');if(title){widget.setTitle(title);}else{widget.setTitle(_('Videos'));}
widget.body.setHTML('');widget.body.className+=' uwa-module-playlist';var first=data.medias[0];if(!first){var message=widget.createElement('div',{'class':'no-content'});message.setText(_("No videos found."));widget.body.appendChild(message);return;}
var mediaContainer=widget.createElement('div',{'class':'media'});Playlist.mediaInfoDiv=widget.createElement('div',{'class':'info'});Playlist.mediaInfoTitle=widget.createElement('h2');Playlist.mediaInfoTitle.setText(first.title);Playlist.mediaMorePara=widget.createElement('p',{'class':'more'});Playlist.mediaMoreLink=widget.createElement('a',{'href':first.path});Playlist.mediaMoreLink.setText(_("see larger video")+' ');Playlist.mediaMoreLink.appendChild(widget.createElement('img',{'src':'/sites/all/themes/nova/default/images/1px.gif','class':'arrow-01-r4'}));Playlist.mediaMorePara.appendChild(Playlist.mediaMoreLink);Playlist.mediaInfoDesc=widget.createElement('p',{'class':'info'});Playlist.mediaInfoDesc.setHTML(_('sourced by ')+first.author_url);Playlist.mediaInfoDiv.appendChild(Playlist.mediaInfoTitle);Playlist.mediaInfoDiv.appendChild(Playlist.mediaMorePara);Playlist.mediaInfoDiv.appendChild(Playlist.mediaInfoDesc);mediaContainer.appendChild(Playlist.mediaInfoDiv);Playlist.mediaContent=widget.createElement('div',{'class':'media'});Playlist.mediaContent.setHTML(Playlist.parseYTVideo(first.output));mediaContainer.appendChild(Playlist.mediaContent);widget.body.appendChild(mediaContainer);if(widget.getValue('thumbs')!='true'&&data.medias.length>1){var footageContainer=widget.createElement('div',{'class':'content-footage'});var footageList=widget.createElement('ul',{'class':'list-footage','style':'width:'+(data.medias.length*100)+'px'});for(var i=0;i<data.medias.length;i++){var item=data.medias[i];var node=widget.createElement('li');var nodeLink=widget.createElement('a',{'href':item.path,'title':item.title,'no':i});nodeLink.onclick=Playlist.showVideo;var nodeImg=widget.createElement('img',{'src':item.thumb,'alt':item.title,'width':80,'height':60});nodeLink.appendChild(nodeImg);node.appendChild(nodeLink);footageList.appendChild(node);}
footageContainer.appendChild(footageList);widget.body.appendChild(footageContainer);if(data.medias.length>Playlist.info.sliderItems){var sliderContainer=widget.createElement('div',{'class':'sliderContainer'});var sliderLeft=widget.createElement('img',{'href':'/sites/all/themes/nova/default/images/1px.gif','class':'arrow-01-l3'});sliderContainer.appendChild(sliderLeft);var sliderRight=widget.createElement('img',{'href':'/sites/all/themes/nova/default/images/1px.gif','class':'arrow-01-r3'});sliderContainer.appendChild(sliderRight);var sliderHandler=widget.createElement('div',{'class':'slider-handle'});var sliderDiv=widget.createElement('div',{'class':'slider'});sliderDiv.appendChild(sliderHandler);sliderContainer.appendChild(sliderDiv);widget.body.appendChild(sliderContainer);var slider=$(sliderDiv).slider({handle:'.slider-handle',steps:data.medias.length-Playlist.info.sliderItems,max:data.medias.length-Playlist.info.sliderItems,slide:function(ev,ui){footageList.style.left=(-ui.value*100)+'px';}});sliderLeft.onclick=sliderRight.onclick=function(){slider.slider("moveTo",slider.slider("value",0)+($(this).hasClass("arrow-01-r3")?1:-1));this.blur();return false;};}}
widget.callback("onUpdateBody");Playlist.setPlaylistTimeout();};Playlist.parseYTVideo=function(output){delete Playlist.ytPlayer;var matches=output.match(/http:\/\/(?:www.)?youtube\.com\/v\/[^"]+/);if(matches){var width=output.match(/\s+width="(\d+)"/,output);var height=output.match(/\s+height="(\d+)"/,output);width=width?width.pop():300;height=height?height.pop():225;var param='&enablejsapi=1&playerapiid=ytplayer'+widget.getValue('_ptid');output='<object width="'+width+'" height="'+height+'" class="ytplayer"';output+=' type="application/x-shockwave-flash" data="'+matches[0]+param+'">';output+='<param name="allowScriptAccess" value="always" />';output+='<param name="movie" value="'+matches[0]+'" />';output+='<embed width="'+width+'" height="'+height+'" src="'+matches[0]+'" type="application/x-shockwave-flash" />';output+='</object>';}
return output;};onYouTubePlayerReady=function(playerId){Playlist.ytPlayer=Playlist.mediaContent.getElementsByClassName('ytplayer')[0];Playlist.ytPlayer.addEventListener("onStateChange","onYTPlayerStateChange");};onYTPlayerStateChange=function(newState){switch(newState){case 1:case 2:case 3:Playlist.stopPlaylist();break;default:break;}};Playlist.showVideo=function(item){if(typeof item!='number'){item=this.getAttribute('no')*1;}
if(item>=Playlist.data.medias.length){item=0;}
if(item<0){item=Playlist.data.medias.length-1;}
Playlist.info.currentVideo=item;var media=Playlist.data.medias[item];Playlist.mediaContent.setHTML(Playlist.parseYTVideo(media.output));Playlist.mediaInfoTitle.setText(media.title);Playlist.mediaInfoDesc.setHTML(_('uploaded by ')+media.author_url);Playlist.mediaMoreLink.href=media.path;return false;};Playlist.doPlaylist=function(){if(Playlist.info.mouseover){return;}
var item=Playlist.info.currentVideo+1;if(item>=Playlist.data.medias.length){item=0;}
Playlist.info.currentVideo=item;if(Playlist.info.timeout){window.clearTimeout(Playlist.info.timeout);}
Playlist.showVideo(item);Playlist.setPlaylistTimeout();};Playlist.stopPlaylist=function(){if(Playlist.info.timeout){window.clearTimeout(Playlist.info.timeout);}};Playlist.setPlaylistTimeout=function(){if(widget.getValue('advance')=='true'&&Playlist.data.medias.length>1&&Playlist.info.interval>0){Playlist.info.timeout=window.setTimeout(function(){Playlist.doPlaylist();},Playlist.info.interval*1000);}};widget.onLoad=function(){Playlist.info.interval=widget.getValue('interval');Playlist.getData();widget.app=Playlist;};
widget.setMetas({"apiVersion":"1.0","autoRefresh":"20","author":"Andr\u00e1s B\u00e1rth\u00e1zi, NowPublic","description":"Use this widget to show one or more video files. By default any videos added to the story will appear here. You can choose to have the videos scroll through automatically and set the speed of the scroll using the slider.","debugMode":"false"});
widget.setPreferences([{"type":"text","name":"title","label":"Name Widget","defaultValue":""},{"type":"list","name":"order","label":"Show","defaultValue":"recent","options":[{"label":"Most recent","value":"recent"},{"label":"Most recommended","value":"votes"},{"label":"Most viewed","value":"popgs"},{"label":"Custom","value":"footage_weight"}]},{"type":"boolean","name":"advance","label":"Auto advance","defaultValue":"false"},{"type":"range","name":"interval","label":"Advance speed (seconds)","defaultValue":"7","step":"1","min":"1","max":"15"},{"type":"boolean","name":"thumbs","label":"Hide thumbnails","defaultValue":"false"}]);
return widget;
}
if (typeof UWA == "undefined") var UWA = {};
if (typeof UWA.Scripts == "undefined") UWA.Scripts = [];
UWA.Scripts['topstory']=UWA.script=function(widget){
var Thumbnails={};Thumbnails.data={};Thumbnails.displayContent=function(){var title=widget.getValue('title');if(title){widget.setTitle(title);}
var content='<ul class="list-03 floatbox">'+'<li class="first"><a href="http://www.redesign.ab.d2.nowpublic.com/world/spongebob-bands-new-song"><img height="72" alt="Spongebob bands new song" src="http://media.nowpublic.com/images/themes/npv5/video_thumbnail.gif"/></a></li>'+'<li><a href="http://www.redesign.ab.d2.nowpublic.com/world/other-world-story-0"><img height="72" alt="other world story" src="http://media.redesign.ab.d2.nowpublic.com/images/39/a/39a0d7eb58bed195ef88eabb6fb61f01.jpg"/></a></li>'+'<li class="last"><a href="http://www.redesign.ab.d2.nowpublic.com/world/unique-name-47fdf59ca7d91-1"><img height="72" alt="unique_name_47fdf59ca7d91" src="http://media.redesign.ab.d2.nowpublic.com/images/53/e/53ef0c2c00d0a3c57a49b6b75c1c1086.jpg"/></a></li>'+'<li class="first"><a href="http://www.redesign.ab.d2.nowpublic.com/world/unique-name-47fdf59ca7d91-0"><img height="72" alt="unique_name_47fdf59ca7d91" src="http://media.redesign.ab.d2.nowpublic.com/images/cd/3/cd30e01bcd38aad5328b3c648ce18264.jpg"/></a></li>'+'<li class=""><a href="http://www.redesign.ab.d2.nowpublic.com/world/unique-name-47fdf59ca7d91"><img height="72" alt="unique_name_47fdf59ca7d91" src="http://media.nowpublic.com/images/themes/npv5/video_thumbnail.gif"/></a></li>'+'<li class="last"><a href="http://www.redesign.ab.d2.nowpublic.com/world/store-diff"><img height="72" alt="store_diff" src="http://media.nowpublic.com/images/themes/npv5/video_thumbnail.gif"/></a></li>'+'<li class="first"><a href="http://www.redesign.ab.d2.nowpublic.com/world/mystic-buffing-2"><img height="72" alt="mystic buffing" src="http://media.redesign.ab.d2.nowpublic.com/fscache/_vi_gh9T5hkQg-M_0.jpg"/></a></li>'+'<li class=""><a href="http://www.redesign.ab.d2.nowpublic.com/world/front-5-30"><img height="72" alt="front 5" src="http://media.redesign.ab.d2.nowpublic.com/images/4a/a/4aa3ec60868d0878fbfdaf000ba02ad7.jpg"/></a></li>'+'<li class="last"><a href="http://www.redesign.ab.d2.nowpublic.com/world/front-5-29"><img height="72" alt="front 5" src="http://media.redesign.ab.d2.nowpublic.com/images/ef/8/ef8f44b4c4ac93f7f6dd0b1a6ed4f9fc.jpg"/></a></li>'+'</ul>';widget.body.setHTML(content);widget.callback("onUpdateBody");}
widget.onLoad=function(){Thumbnails.displayContent();};
widget.setMetas({"apiVersion":"1.0","autoRefresh":"20","author":"Andr\u00e1s B\u00e1rth\u00e1zi, NowPublic","description":"NowPublic Thumbnails module","debugMode":"false"});
widget.setPreferences([{"type":"text","name":"title","label":"Widget title","defaultValue":""}]);
return widget;
}
if (typeof UWA == "undefined") var UWA = {};
if (typeof UWA.Scripts == "undefined") UWA.Scripts = [];
UWA.Scripts['scan']=UWA.script=function(widget){
var Scan={};Scan.displayContent=function(){var title=widget.getValue('title');if(title){widget.setTitle(title);}else{widget.setTitle(_('Scan'));}
widget.body.setHTML('');widget.body.className+=' uwa-module-scan';var page_id=widget.getValue('page_id')||'';if(page_id&&!page_id.match(/^\d+$/)){page_id='';}
if(page_id){var script=widget.createElement('script',{'src':'http://www.scan.nowpublic.com/widget/276/bundle.js#'+page_id,'type':'text/javascript'});widget.body.appendChild(script);}
widget.callback("onUpdateBody");}
widget.onLoad=function(){Scan.displayContent();};
widget.setMetas({"apiVersion":"1.0","autoRefresh":"20","author":"Galiba P\u00e9ter, NowPublic","description":"Use this widget to add a Scan widget to your page. Just paste in scan page id.","debugMode":"false"});
widget.setPreferences([{"type":"text","name":"page_id","label":"Page Id","defaultValue":""}]);
return widget;
}
if (typeof UWA == "undefined") var UWA = {};
if (typeof UWA.Scripts == "undefined") UWA.Scripts = [];
UWA.Scripts['poll']=UWA.script=function(widget){
var Poll={};Poll.data={};Poll.getData=function(){var node=widget.getValue('node');if(!node||node===''){widget.body.setHTML('');widget.body.className+=' uwa-module-poll';var message=widget.createElement('div',{'class':'no-content'});message.setText(_("Please specify a Poll URL."));widget.body.appendChild(message);return;}
var url='/json/poll?node='+node+'&type=poll';UWA.Data.getJson(url,Poll.displayContent);};Poll.displayContent=function(data){if(data){Poll.data=data;}else{data=Poll.data;}
var title=widget.getValue('title');if(title){widget.setTitle(title);}
else if(data.title){widget.setTitle(data.title);}
else{widget.setTitle(_("Poll"));}
widget.body.setHTML('');widget.body.className+=' uwa-module-poll';var choices=widget.createElement('ul',{'class':'list-bars'});var question=widget.createElement('div',{'class':'question'});var formDiv=widget.createElement('div',{'class':'form-01'});if(data){Poll.domain='';var matches=data.post_url.match(/^[a-z]{3,6}:\/\/[^\/]+/);if(matches){Poll.domain=matches[0];}
var form=widget.createElement('form',{'action':data.post_url,'method':'post','accept-charset':"UTF-8"});question.setHTML(data.body);var itemType=data.max_choices>1?'checkbox':'radio';var formHolder=widget.createElement('div',{'class':data.max_choices>1?'form-checkboxes':'form-radios'});var votes=parseInt(data.votes);for(var i=0;i<data.choices.length;i++){var item=data.choices[i];var choice=widget.createElement('li');title=widget.createElement('h3',{'class':'title'});title.setText(item.label);var percentValue=votes?item.votes*100/votes:0;var n_percentValue=new Number(percentValue);if(n_percentValue.toFixed){percentValue=n_percentValue.toFixed(2);}
else{percentValue=Math.round(percentValue*100)/100;}
var bar=widget.createElement('div',{'class':'bar'});var percent=widget.createElement('div',{'class':'percent'});percent.appendText(percentValue+'%');var topCorner=widget.createElement('span',{'class':'corner-top'});topCorner.appendChild(widget.createElement('span'));var barFill=widget.createElement('div',{'class':'bar-fill','style':'width: '+percentValue+'%','title':percentValue+'%'});var bottomCorner=widget.createElement('span',{'class':'corner-bottom'});bottomCorner.appendChild(widget.createElement('span'));bar.appendChild(percent);bar.appendChild(topCorner);bar.appendChild(barFill);bar.appendChild(bottomCorner);choice.appendChild(title);choice.appendChild(bar);choices.appendChild(choice);var formItem=widget.createElement('div',{'class':'form-item'});var label=widget.createElement('label',{'class':'option'});var input=widget.createElement('input',{'type':itemType,'class':'form-'+itemType,'value':item.cid,'name':'choices[]'});label.appendChild(input);label.appendText(' '+item.label);formItem.appendChild(label);formHolder.appendChild(formItem);}
form.appendChild(formHolder);form.appendChild(widget.createElement('input',{'class':'form-submit button-01-s','type':'submit','value':_("Vote"),'name':'op'}));formDiv.appendChild(form);var footer=widget.createElement('p',{'class':'more'});var footerLink=widget.createElement('a',{'href':Poll.domain+'/node/'+data.nid+'#vote'});footerLink.appendText(_("Vote"));footerLink.appendChild(widget.createElement('span'));footerLink.onclick=Poll.footerClick;footer.appendChild(footerLink);}
widget.body.appendChild(question);widget.body.appendChild(choices);widget.body.appendChild(formDiv);widget.body.appendChild(footer);Poll.form=UWA.$element(widget.body.getElementsByTagName('form')[0]);Poll.choices=choices;Poll.form.onsubmit=Poll.submitVote;Poll.form.hide();widget.callback("onUpdateBody");};Poll.footerClick=function(){var url=Poll.domain+'/node/'+Poll.data.nid;if(this.href.match(/#vote$/)){Poll.form.show();Poll.choices.hide();this.href=url+'#results';this.setText(_("Results"));}
else{Poll.form.hide();Poll.choices.show();this.href=url+'#vote';this.setText(_("Vote"));}
this.appendChild(widget.createElement('span'));var messages=widget.body.getElementsByClassName('messages');for(var i=0;i<messages.length;i++){messages[i].remove();}
return false;};Poll.submitVote=function(){var inputs=Poll.form.getElementsByTagName('input');var choices="";for(var i=0;i<inputs.length;i++){var input=inputs[i];var type=input.getAttribute('type');if(input.checked&&(type=='checkbox'||type=='radio')){choices+=input.name+'='+encodeURIComponent(input.value)+'&';}
input.setAttribute('disabled','disabled');}
choices+='ajax=1';UWA.Data.request(Poll.form.action,{method:Poll.form.method,type:'json',parameters:choices,onComplete:Poll.onSubmitOk});var messages=widget.body.getElementsByClassName('messages');for(var i=0;i<messages.length;i++){messages[i].remove();}
return false;};Poll.onSubmitOk=function(response){if(response.error){Poll.form.getParent().insertBefore(Poll.formatMessage(response.error,'error'),Poll.form);}
if(response.errors){Poll.form.getParent().insertBefore(Poll.formatMessage(new Array(response.errors),'error'),Poll.form);}
if(response.status){Poll.form.getParent().insertBefore(Poll.formatMessage(response.status,'status'),Poll.form);}
var inputs=Poll.form.getElementsByTagName('input');for(var i=0;i<inputs.length;i++){inputs[i].removeAttribute('disabled');}};Poll.formatMessage=function(data,type){var messageHolder=widget.createElement('div',{'class':'messages '+type});var messages=widget.createElement('ul');for(var i=0;i<data.length;i++){var item=widget.createElement('li');item.setHTML(data[i]);messages.appendChild(item);}
messageHolder.appendChild(messages);return messageHolder;};widget.onLoad=function(){Poll.getData();};
widget.setMetas({"apiVersion":"1.0","autoRefresh":"20","author":"P\u00e9ter Galiba, NowPublic","description":"Use this widget to display a Poll from NowPublic. Just paste in the URL of the Poll.","debugMode":"false"});
widget.setPreferences([{"type":"text","name":"node","label":"Poll URL","defaultValue":""}]);
return widget;
}
/* NV Globals */

if (typeof window.NV_HOST == "undefined") {
  NV_HOST = 'www.netvibes.com';
}
if (typeof window.NV_MODULES == "undefined") {
  NV_MODULES = 'nvmodules.netvibes.com';
}
if (typeof window.NV_AVATARS == "undefined") {
  NV_AVATARS = 'avatars.netvibes.com';
}
if (typeof window.NV_STATIC == "undefined") {
  NV_STATIC = 'http://' + NV_HOST;
}
if (typeof window.NV_PATH == "undefined") {
  NV_PATH = 'http://' + NV_HOST + '/';
}

if (typeof UWA == "undefined") var UWA = {};
if (typeof UWA.Widgets == "undefined") UWA.Widgets = {};
if (typeof UWA.Scripts == "undefined") UWA.Scripts = {};
if (typeof UWA.Controls == "undefined") UWA.Controls = {};
if (typeof UWA.Services == "undefined") UWA.Services = {};
if (typeof UWA.Templates == "undefined") UWA.Templates = {};

UWA.version = '1.2';

// Compatibility - To be removed
if (typeof Netvibes == "undefined") var Netvibes = {};
if (typeof Netvibes.UI == "undefined") Netvibes.UI = {};
Netvibes.UI._idIncrement = 0;
UWA.Controls = Netvibes.UI;
if (Netvibes.DLA) UWA.Controls.SearchForm = Netvibes.DLA.SearchForm;

if (typeof _ == "undefined") {
  _ = function(s) {
    return s
  };
}

/*
Script: Core

UWA Runtime Core.

Credits:
  Partially based on MooTools, My Object Oriented Javascript Tools.
  Copyright (c) 2006-2007 Valerio Proietti, <http://mad4milk.net>, MIT Style License.
*/

/*
License:
  Copyright (c) 2005-2008 Netvibes (http://www.netvibes.org/).

  This file is part of Netvibes Widget Platform.

  Netvibes Widget Platform is free software: you can redistribute it and/or modify
  it under the terms of the GNU Lesser General Public License as published by
  the Free Software Foundation, either version 3 of the License, or
  (at your option) any later version.

  Netvibes Widget Platform is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  GNU Lesser General Public License for more details.

  You should have received a copy of the GNU Lesser General Public License
  along with Netvibes Widget Platform.  If not, see <http://www.gnu.org/licenses/>.
*/

/* Method: extend 

Copies all the properties from the second passed object to the first passed Object.

See also:
  <http://docs.mootools.net/Core/Core.js#$extend>

Example:
  TODO
*/
UWA.extend = function(original, extended) {
    /*
    for (var property in arguments[1]) arguments[0][property] = arguments[1][property];
    return arguments[0];
    */
    for (var key in (extended || {})) original[key] = extended[key];
    return original;
}

/* Method: merge 

Copies the properties from the second passed object to the first passed Object if it not exists already.

*/
UWA.merge = function() {
    for (var property in arguments[1]) {
      if(typeof arguments[0][property] == "undefined")
        arguments[0][property] = arguments[1][property];
    }
    return arguments[0];
}

/* Method: log 

Log a message to a console, if one available.

Example:
  > widget.log("Widget is loading ...")
*/
UWA.log = function(message) {
    if (window.console && typeof(console.log) == "function") console.log(message); // firebug, safari
    else if (window.opera && typeof(opera.postError) == "function") opera.postError(message);
    // else if (window.widget) window.alert(message); // dashboard
    // else window.alert(message); // IE
}

// jQuery.noConflict();

UWA.Class = Class = {
  create: function() {
    return function() {
      this.initialize.apply(this, arguments);
    }
  }
}

Function.prototype.bind = function(object) {
  var __method = this;
  return function() {
    return __method.apply(object, arguments);
  }
}

Function.prototype.bindAsEventListener = function(object) {
  var __method = this;
  return function(event) {
    return __method.apply(object, [event || window.event]);
  }
}

document.getElementsByClassName = function(className) {
  return jQuery("." + className)
}

UWA.Ajax = {
  Request: function(url, request) {
    if(request.parameters) request.data = request.parameters;
    if(request.postBody) request.data = request.postBody;
    jQuery.ajax( { type: request.method, url: url, success: request.onComplete, data: request.data } )
  },
  onCompleteXML: function(arg, callback, context) {
    callback(arg[0]);
  },
  onCompleteText: function(arg, callback, context) {
    callback(arg[0]);
  },
  onCompleteJson: function(arg, callback, context) {
    try {
      eval("var j = " + arg[0]);
      callback(j, context);
    } catch(e) {
      UWA.log(e);
    }
  }
}

UWA.Form = {
  getElements: function(form) {
    return jQuery(':input', form)
  }
}

UWA.$element = UWA.extendElement = function(el){
  if (el) {
    if (!el.isUwaExtended) {
      UWA.extend(el, UWA.Element);
      el.isUwaExtended = true;
    } 
    return jQuery(el)[0];
  }
}

// Element builder
UWA.createElement = function(tagName, options){
  var el = UWA.extendElement( document.createElement(tagName) ); 
  if (typeof options == 'string') {
    UWA.log('widget.createElement : elName as 2nd argument is deprecated');
    this.elements[options] = el;
  } else if (typeof options == "object") {
    for (var name in options) { 
     var option = options[name]; 
     switch(name) { 
       case 'styles': 
         el.setStyle(option); 
         break;
       case 'attributes':
         el.setAttributes(option);
         break;
       case 'id':
         el.id = option;
         break;
       case 'class':
         el.className = option;
         break;
       case 'events':
         el.addEvents(option);
         break; 
       default:
         el.setAttribute(name, option); 
     } 
   } 
 } 
 return el;
}

if (typeof UWA.Element == "undefined") UWA.Element = {};

// if (typeof UWA.Element.methods == "undefined") UWA.Element.methods = {};

UWA.extend(UWA.Element, {
  
  hide: function() {
   return this.setStyle('display', 'none');
  },
  show: function() {
    return this.setStyle('display', '');
  },
  toggle: function() {
    if(this.style.display == 'none') {
      this.style.display = '';
    } else {
      this.style.display = 'none';
    }
  },
  remove: function() {
    return this.parentNode.removeChild(this);
  },
  setStyle: function(arg1, arg2) {
    if (typeof arg1 == 'string' && typeof arg2 != 'undefined') { // key->value syntax
      jQuery(this).css(arg1, arg2);
    } else { // object syntax
      jQuery(this).css(arg1);
    }
    return this;
  },
  getDimensions: function() {
    return { 'width' : jQuery(this).width(), 'height' : jQuery(this).height() }
  },
  hasClassName: function(className) {
   return jQuery.className.has(this, className);
  },
  addClassName: function(className) {
    jQuery.className.add(this, className);
    return this;
  },
  removeClassName: function(className) {
    jQuery.className.remove(this, className);
    return this;
  },
  getElementsByClassName: function(className) {
    return jQuery("." + className, this)
  }
});

// Compatibility with prototype style (TabView)

if (typeof Element == "undefined") var Element = {};

Element.addClassName = function(el, className) {
  return jQuery.className.add(el, className);
}
Element.removeClassName = function(el, className) {
  return jQuery.className.remove(el, className);
}
Element.show = function(el) {
  return el.style.display == '';
}
Element.hide = function(el) {
  return el.style.display == 'none';
}
/*
Script: String

Extensions to the native JavaScript String class.

Credits:
  Partially based on MooTools, My Object Oriented Javascript Tools.
  Copyright (c) 2006-2007 Valerio Proietti, <http://mad4milk.net>, MIT Style License.
  Partially based on Prototype JavaScript framework, version 1.6.0 (c) 2005-2007 Sam Stephenson.
  Prototype is freely distributable under the terms of an MIT-style license.
  For details, see the Prototype web site: http://www.prototypejs.org/
*/

/*
License:
  Copyright (c) 2005-2008 Netvibes (http://www.netvibes.org/).

  This file is part of Netvibes Widget Platform.

  Netvibes Widget Platform is free software: you can redistribute it and/or modify
  it under the terms of the GNU Lesser General Public License as published by
  the Free Software Foundation, either version 3 of the License, or
  (at your option) any later version.

  Netvibes Widget Platform is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  GNU Lesser General Public License for more details.

  You should have received a copy of the GNU Lesser General Public License
  along with Netvibes Widget Platform.  If not, see <http://www.gnu.org/licenses/>.
*/

UWA.merge(String.prototype, {

  /* Method: stripTags 

  Strips a string of any HTML tag.

  Original documentation:
    <http://www.prototypejs.org/api/string/stripTags>

  */
  stripTags: function() {
    return this.replace(/<\/?[^>]+>/gi, '');
  },

  /* Method: truncate 

  Truncates a string to the given length and appends a suffix to it (indicating that it is only an excerpt).

  Original documentation:
    <http://www.prototypejs.org/api/string/truncate>

  Notes:
    needed for backward compatibility with a third-party UWA implementation

  */
  truncate: function(length, truncation) {
    length = length || 30;
    truncation = truncation === undefined ? '...' : truncation;
    return this.length > length ?
      this.slice(0, length - truncation.length) + truncation : String(this);
  },

  /* Method: escapeRegExp 

  Returns string with escaped regular expression characters

  Original documentation:
    <http://docs.mootools.net/Native/String.js#String.escapeRegExp>

  Notes:
    needed for compatibility with Vibes native widget

  */
  escapeRegExp: function() {
    return this.replace(/([.*+?^${}()|[\]\/\\])/g, '\\$1');
  },

  /* Method: trim 

  Trims the leading and trailing spaces off a string.

  Original documentation:
    <http://docs.mootools.net/Native/String.js#String.trim>

  Notes:
    needed for compatibility with a third-party UWA implementation

  */
  trim: function(){
    return this.replace(/^\s+|\s+$/g, '');
  },

  /* Method: isEmail

  Not documented

  Notes:
    needed for compatibility with a third-party UWA implementation

  */
  isEmail: function() {
    var regexp = /^([a-zA-Z0-9_.\-+])+\@(([a-zA-Z0-9\-])+\.)+([a-zA-Z0-9]{2,4})+$/;
    return regexp.test(this);
  },

  /* Method: s

  Not documented
    
  Notes:
    needed for compatibility with various Netvibes native widget

  */
  s: function() {
    var str = this;
    if (arguments.length < 1) return str;
    var re = /([^%]*)%s(.*)/;
    var a = [], numSubstitutions = -1;
    while (a = re.exec(str)) {
      var leftpart = a[1], rightPart = a[2];
      if (++numSubstitutions >= arguments.length) {
        break;
      }
      str = leftpart + arguments[numSubstitutions] + rightPart;
    }
    return str;
  },
  
  /* Method: format

  Not documented
    
  Notes:
    needed for compatibility with various Netvibes native widget (new format)

  */
  format : function() {
    var args = arguments;
    return this.replace(/\{(\d+)\}/g, function(m, i){
      return args[i];
    });
  },
    
  /* Method: parseRelativeTime

  Not documented

  Notes:
    needed for Timeline Control, from /js/App/Core/String.js

  */
  parseRelativeTime: function(raw, offset) {
    if (typeof offset != 'number') offset = 0;

    var matches = (raw && raw.match(/^(\d\d\d\d)\-(\d\d)\-(\d\d) (\d\d):(\d\d):(\d\d)$/));
    if (!matches) return false;

    var date = new Date(matches[1], matches[2] - 1, matches[3], matches[4], matches[5], matches[6]);
    var relative_to = new Date();
    var delta = parseInt((relative_to.getTime() - date.getTime()) / 1000); // Compute the diff
    delta = delta + (relative_to.getTimezoneOffset() * 60 + 3600 * offset); // Add timezone offset (+1 because we are not in GMT)
    if (delta < 60) {
      return _("less than a minute ago");
    } else if(delta < 120) {
      return _("about a minute ago");
    } else if(delta < (45*60)) {
      return _("{0} minutes ago").format( Math.round(delta/60) );
    } else if(delta < (90*60)) {
      return _("about an hour ago");
    } else if(delta < (24*60*60)) {
      return _("about {0} hours ago").format( Math.round(delta/3600) );
    } else if(delta < (48*60*60)) {
      return _("yesterday");
    } else {
      return _("{0} days ago").format( Math.round(delta/86400) );
    }
  },

  /* Method: contains
  
  Not documented

  Notes:
    used in el.hasClassName implementation in Element.js
    
  */
  contains: function(string, separator) {
    return (separator) ? (separator + this + separator).indexOf(separator + string + separator) > -1 : this.indexOf(string) > -1;
  },

  /* Method: camelCase
  
  Not documented

  Notes:
    used in el.setStyle implementation in Element.js
    
  */
  camelCase: function() {
    return this.replace(/-\D/g, function(match) {
      return match.charAt(1).toUpperCase();
    });
  },

  /* Method: makeClickable
  
  Not documented

  Notes:
    needed for compatibility with various Netvibes native widget
    
  */
  makeClickable: function() {
    var lines = this.split("<br>");
    for(var z=0; z<lines.length; z++){
      var tmp = lines[z].split(" ");
      for(var i=0; i<tmp.length; i++){
        if(tmp[i].indexOf("www.")!=-1 && tmp[i].indexOf("http://")==-1 && tmp[i].indexOf("https://")==-1){
          tmp[i] = "<a href='http://"+tmp[i]+"' target='_blank'>"+tmp[i]+"</a>";
        } else if(tmp[i].indexOf("http://")!=-1 || tmp[i].indexOf("ftp://")!=-1 || tmp[i].indexOf("https://")!=-1){
          tmp[i] = "<a href='"+tmp[i]+"' target='_blank'>"+tmp[i]+"</a>";
        } else if(tmp[i].indexOf("@")>0){
          tmp[i] = "<a href='mailto:"+tmp[i]+"' target='_blank'>"+tmp[i]+"</a>";
        }
        // remove trailing '.', '?', '!', ':' characters
        tmp[i]  = tmp[i] .replace(/<a href='(.*?)[\.\!\?\:]' target='_blank'>(.*?)([\.\!\?\:])<\/a>/g, '<a href="$1" target="_blank">$2</a>$3');
      }
      lines[z] = tmp.join(" ");
    }
    return lines.join("<br>");
  },

/**
 * @private Not Documented.
 * Note: needed for compatibility with various Netvibes native widget.
 *
 * @return {String} The converted string.
 */
  unescapeHTML: function() {
    var div = document.createElement('div');
    div.innerHTML = this.stripTags();
    return div.childNodes[0] ? div.childNodes[0].nodeValue : '';
  }
    
});

// Timeline control use String.parseRelativeTime(string)
String.parseRelativeTime = String.prototype.parseRelativeTime;

/*
Script: Array

Extensions to the native JavaScript Array class.

Credits:
  Partially based on MooTools, My Object Oriented Javascript Tools.
  Copyright (c) 2006-2007 Valerio Proietti, <http://mad4milk.net>, MIT Style License.
  Partially based on Prototype JavaScript framework, version 1.6.0 (c) 2005-2007 Sam Stephenson.
  Prototype is freely distributable under the terms of an MIT-style license.
  For details, see the Prototype web site: http://www.prototypejs.org/
*/

/*
License:
  Copyright (c) 2005-2008 Netvibes (http://www.netvibes.org/).

  This file is part of Netvibes Widget Platform.

  Netvibes Widget Platform is free software: you can redistribute it and/or modify
  it under the terms of the GNU Lesser General Public License as published by
  the Free Software Foundation, either version 3 of the License, or
  (at your option) any later version.

  Netvibes Widget Platform is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  GNU Lesser General Public License for more details.

  You should have received a copy of the GNU Lesser General Public License
  along with Netvibes Widget Platform.  If not, see <http://www.gnu.org/licenses/>.
*/

UWA.merge(Array.prototype, {

  /* Method: forEach 

  Executes a provided function once per array element.

  Notes:
    Javascript 1.6 method

  See also:
    <http://developer.mozilla.org/en/docs/Core_JavaScript_1.5_Reference:Global_Objects:Array:forEach>
  */
  forEach: function(fn, bind){
    for (var i = 0, j = this.length; i < j; i++) fn.call(bind, this[i], i, this);
  },

  /* Method: filter 

  Creates a new array with all elements that pass the test implemented by the provided function.

  Notes:
    Javascript 1.6 method

  See also:
    <http://developer.mozilla.org/en/docs/Core_JavaScript_1.5_Reference:Global_Objects:Array:filter>
  */
  filter: function(fn, bind){
    var results = [];
    for (var i = 0, j = this.length; i < j; i++){
      if (fn.call(bind, this[i], i, this)) results.push(this[i]);
    }
    return results;
  },

  /* Method: map 

  Creates a new array with the results of calling a provided function on every element in this array.

  Notes:
    Javascript 1.6 method

  See also:
    <http://developer.mozilla.org/en/docs/Core_JavaScript_1.5_Reference:Global_Objects:Array:map>
  */
  map: function(fn, bind){
    var results = [];
    for (var i = 0, j = this.length; i < j; i++) results[i] = fn.call(bind, this[i], i, this);
    return results;
  },

  /* Method: every 

  Tests whether all elements in the array pass the test implemented by the provided function.

  Notes:
    Javascript 1.6 method

  See also:
    <http://developer.mozilla.org/en/docs/Core_JavaScript_1.5_Reference:Global_Objects:Array:every>
  */
  every: function(fn, bind){
    for (var i = 0, j = this.length; i < j; i++){
      if (!fn.call(bind, this[i], i, this)) return false;
    }
    return true;
  },

  /* Method: some 

  Tests whether some element in the array passes the test implemented by the provided function.

  Notes:
    Javascript 1.6 method

  See also:
    <http://developer.mozilla.org/en/docs/Core_JavaScript_1.5_Reference:Global_Objects:Array:some>
  */
  some: function(fn, bind){
    for (var i = 0, j = this.length; i < j; i++){
      if (fn.call(bind, this[i], i, this)) return true;
    }
    return false;
  },

  /* Method: indexOf 

  Returns the first index at which a given element can be found in the array, or -1 if it is not present.

  Notes:
    Javascript 1.6 method

  See also:
    <http://developer.mozilla.org/en/docs/Core_JavaScript_1.5_Reference:Global_Objects:Array:indexOf>
  */
  indexOf: function(item, from){
    var len = this.length;
    for (var i = (from < 0) ? Math.max(0, len + from) : from || 0; i < len; i++){
      if (this[i] === item) return i;
    }
    return -1;
  }

});

UWA.merge(Array.prototype, {

  /* Method: normalize

  Not documented

  Notes:
   - needed for compatibility with a third-party UWA implementation

  */
  normalize: function(sum) {
    var x = 0;
    var ratio = sum / this.inject(0, function(a, n) { return a + n; } );
    for (var i = 0; i < this.length - 1; i++) x += (this[i] *= ratio);
    this[this.length - 1] = sum - x;
  },

  /* Method: equals

  Test wether the array equals to the one passed as parameter.

  Notes:
   - needed for compatibility with a third-party UWA implementation

  */
  equals: function(compare) {
    if (!compare) {
      return false;
    }
    var len = this.length;
    if (len != compare.length) {
      return false;
    }
    for (var i = 0; i < len; i++) {
      if (this[i] != compare[i]) {
        return false;
      }
    }
    return true;
  },

  /* Method: detect

  Not documented

  Notes:
   - needed for compatibility with the TabView Control
   - needed for compatibility with the multiplefeeds native widget

  */
  detect: function(iterator) {
    var result;
    this.each(function(value, index) {
      if (iterator(value, index)) {
        result = value;
        return result;
      }
    });
    return result;
  }

});

if (typeof Array.prototype.each != "function") {
  Array.prototype.each = Array.prototype.forEach;
}

/*
Script: Element

Document Object Model extensions.

Credits:
  Partially based on MooTools, My Object Oriented Javascript Tools.
  Copyright (c) 2006-2007 Valerio Proietti, <http://mad4milk.net>, MIT Style License.
  Partially based on Prototype JavaScript framework, version 1.6.0 (c) 2005-2007 Sam Stephenson.
  Prototype is freely distributable under the terms of an MIT-style license.
  For details, see the Prototype web site: http://www.prototypejs.org/
*/

/*
License:
  Copyright (c) 2005-2008 Netvibes (http://www.netvibes.org/).

  This file is part of Netvibes Widget Platform.

  Netvibes Widget Platform is free software: you can redistribute it and/or modify
  it under the terms of the GNU Lesser General Public License as published by
  the Free Software Foundation, either version 3 of the License, or
  (at your option) any later version.

  Netvibes Widget Platform is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  GNU Lesser General Public License for more details.

  You should have received a copy of the GNU Lesser General Public License
  along with Netvibes Widget Platform.  If not, see <http://www.gnu.org/licenses/>.
*/

if (typeof UWA.Element == "undefined") UWA.Element = {};

UWA.merge(UWA.Element, {
  
  /* Group: Content manipulation methods */
  
  /* Method: addContent
  
  Status:
    Documented in UWA 1.0 - to be deprecated
  
  */
  addContent: function(content) {
    if (typeof content == 'string') {
      // UWA.log("addContent should be soon deprecated. Use alternative syntaxes.");
      var node = document.createElement("div");
      node.innerHTML = content;
      return this.appendChild(node);
    }
    return this.appendChild(content);
  },
  
  /* Method: setText
  
  Sets the inner text of the Element.
  
  From MooTools
  
  Status:
    Documented in UWA 1.0
  
  */
  setText: function(text) {
    this[(typeof this.innerText != 'undefined') ? 'innerText' : 'textContent'] = text;
    return this;
  },
  
  /* Method: appendText
  
  Adds a new text node at the end of the element's existing content
  
  Status:
    Documented in UWA 1.0
  
  */
  appendText: function(text) {
    var node = document.createTextNode(text);
    return this.appendChild(node);
  },
  
  /* Method: setHTML
  
  Sets the innerHTML of the Element.
  
  In MooTools
  
  Status:
    Documented in UWA 1.0
  
  */
  setHTML: function(html) {
    this.innerHTML = html;
    return this;
  },

  setContent: function(content) {
    if (typeof content == 'string') {
      this.setHTML(content);
    } else if (typeof content == 'object') {
      this.innerHTML = '';
      this.appendChild(content);
    }
    return this;
  },
  
  /* Group: Class manipulation methods */
  
  /* Method: hasClassName
  
  Checks whether element has the given CSS className.
  
  - From Prototype, code derived from MooTools
    
  Status:
    Documented in UWA 1.0
  
  */
  
  hasClassName: function(className) {
    return this.className.contains(className, ' ');
  },
  
  /* Method: addClassName
  
  Adds a CSS class to element.
   
  - From Prototype, code derived from MooTools
    
  Status:
    Documented in UWA 1.0
  
  */
  
  addClassName: function(className) {
    if (!this.hasClassName(className)) this.className = (this.className + ' ' + className);
    return this;
  },
  
  /* Method: removeClassName
  
  Removes element's CSS className and returns element.
  
  - From Prototype, code derived from MooTools
  
  Status:
    Documented in UWA 1.0
  
  */
  
  removeClassName: function(className) {
    this.className = this.className.replace(new RegExp('(^|\\s)' + className + '(?:\\s|$)'), '$1');
    return this;
  },
  
  /* Group: DOM manipulation methods */
  
  
  /* Method: getParent
  
  return a reference to the element's parent node
  
  - In Mootools
  
  Status:
    Documented in UWA 1.0
  
  */
  getParent: function() {
    return UWA.$element(this.parentNode);
  },
  
  /* Method: getChildren
  
  return a collection of the element's child nodes
  
  - In Mootools
  
  Status:
    Documented in UWA 1.0
  
  */
  getChildren: function() {
    return this.childNodes;
  },
  
  /* Method: empty
  
  Empty an element of all its children.
  
  - From MooTools
  
  Status:
    Documented in UWA 1.0
  
  */
  
  empty: function() {
    this.innerHTML = '';
    return this;
  },
  
  /* Method: hide
  
  Hides and returns element.
  
  - From Prototype
  
  Status:
    Documented in UWA 1.0
  
  */
  
  hide: function() {
   return this.setStyle('display', 'none');
  },
  
  /* Method: show
  
  Displays and returns element.
  
  - From Prototype
  
  Status:
    Documented in UWA 1.0
  
  */
  
  show: function() {
    return this.setStyle('display', '');
  },
  
  /* Method: toggle
  
  Toggles the visibility of element.
  
  - From Prototype
  
  Status:
    Documented in UWA 1.0 - to deprecate
  
  */
  
  toggle: function() {
    this.style.display == 'none' ? this.setStyle('display', '') : this.setStyle('display', 'none');
    return this;
  },
  
  /* Method: remove
  
  Completely removes element from the document and returns it.
  
  - From Prototype, code from MooTools (dispose)
  
  Status:
    Documented in UWA 1.0
  
  */
  
  remove: function() {
    return this.parentNode.removeChild(this);
  },
  
  /* Method: getDimensions
  
  Finds the computed width and height of element and returns them as key/value pairs of an object.
  
  - From Prototype
  
  Status:
    Documented in UWA 1.0
  
  */
  
  getDimensions: function() {
    return { width: this.offsetWidth, height: this.offsetHeight };
  },
  
  /* Method: setStyle
  
  Modifies element's CSS style properties. Styles are passed as either a hash or a name/value pair.
  
  Status:
    Documented in UWA 1.0
  
  */
  
  setStyle: function(style) {
    if (typeof style == 'string') {
      style = style.camelCase();
      this.style[style] = arguments[1];
    } else if (typeof style == 'object') {
      return this.setStyles(style);
    }
    return this;
  },
  
  setStyles: function(styles) {
    var elementStyle = this.style;
    for (var property in styles) {
      if (property == 'opacity') {
        this.setOpacity(styles[property]);
      } else {
        elementStyle[(property == 'float' || property == 'cssFloat') ?
          (elementStyle.styleFloat === undefined ? 'cssFloat' : 'styleFloat') :
            property] = styles[property];
        }
    }
    return this;
  },
  
  setOpacity: function(value) {
    this.style.opacity = (value == 1 || value === '') ? '' :
      (value < 0.00001) ? 0 : value;
    return this;
  },
  
  /* Method: inject
  
  Insert the Element inside the passed element
  
  - From MooTools
  - also in Prototype : insert
  
  Status:
    Introduced in UWA 1.2 (ginger)
  
  */
  
  inject: function(el, where) {
    if (typeof where != 'undefined') {
      UWA.log('warning: el.inject. 2nd argument not supported. ' + where);
    }
    return el.appendChild(this);
  },
  
  /* Group: Events manipulation methods */
  
  addListener: function(type, fn) {
    if (this.addEventListener) this.addEventListener(type, fn, false);
    else this.attachEvent('on' + type, fn);
    return this;
  },
  
  removeListener: function(type, fn){
    if (this.removeEventListener) this.removeEventListener(type, fn, false);
    else this.detachEvent('on' + type, fn);
    return this;
  }

});


if (window.HTMLElement) {
  UWA.merge(window.HTMLElement.prototype, UWA.Element);
}

/*
Class: Data

The Data class provides abstract methods to access external resources using Ajax (XMLHttpRequest) requests.

Credits:
  Partially based on MooTools, My Object Oriented Javascript Tools.
  Copyright (c) 2006-2007 Valerio Proietti, <http://mad4milk.net>, MIT Style License.
  Partially based on Prototype JavaScript framework, version 1.6.0 (c) 2005-2007 Sam Stephenson.
  Prototype is freely distributable under the terms of an MIT-style license.
  For details, see the Prototype web site: http://www.prototypejs.org/
*/

/*
License:
  Copyright (c) 2005-2008 Netvibes (http://www.netvibes.org/).

  This file is part of Netvibes Widget Platform.

  Netvibes Widget Platform is free software: you can redistribute it and/or modify
  it under the terms of the GNU Lesser General Public License as published by
  the Free Software Foundation, either version 3 of the License, or
  (at your option) any later version.

  Netvibes Widget Platform is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  GNU Lesser General Public License for more details.

  You should have received a copy of the GNU Lesser General Public License
  along with Netvibes Widget Platform.  If not, see <http://www.gnu.org/licenses/>.
*/

if (typeof UWA.proxies == "undefined") {
  
  UWA.proxies = {
    'api'  : NV_PATH + 'proxy/api2Proxy.php',
    'rss'  : NV_PATH + 'proxy/xmlProxy.php',
    'ajax' : NV_PATH + 'proxy/ajaxProxy.php',
    'feed' : NV_PATH + 'proxy/feedProxy.php',
    'xml'  : NV_PATH + 'data/xml/'
  }
  
}


if (typeof UWA.Json == "undefined") UWA.Json = {};

UWA.Json.request = function(url, request) {
  
  var varname = 'json';
  
  if (request.context && request.context[0]) varname += request.context[0];
  else varname += Math.round(1000*1000*Math.random());

  eval(varname + '= false');

  url += '&object=' + varname ;

  var script = document.createElement('script');
  script.setAttribute('type', 'text/javascript');
  script.src = url;
  var head = document.getElementsByTagName('head')[0];
  var insert = head.appendChild(script);

  if (typeof request.onComplete == "undefined") UWA.log('no callback set');

  var callback = request.onComplete;

  var myCallback = function(c){ return function(j) { callback(j, c) } }(request.context);

  var interval = setInterval( ( function() {
    eval('var json = ' + varname);
    if (json) {
      try {
        myCallback(json);
      } catch(e) {
        UWA.log(e);
      }
      insert.parentNode.removeChild(insert);
      clearInterval(interval);
    }
  } ).bind(this), 100);

}


UWA.Data = {
  
  useJsonRequest: false,

  /* Section: methods */

  /* Method: getFeed

  Gets the content of a feed, in a JSON format.

  Parameters:
    * String url: the URL of the feed data source.
    * Function callback: the callback method that will be triggered when the request is succesful. 
      This method *must have one parameter* to receive the feed (JSON format) returned by the request.

  Returns:
    * Nothing, but if the request is successful, the callback method is fired and receives the feed as parameter.
  
  Example:
    (start code)
    UWA.Data.getFeed('http://feeds.feedburner.com/NetvibesDevBlog', myModule.display);
    myModule.display = function(aFeed) {
      // your display code 
    }
    (end code)

  Notes:
    In this example, the callback method is named "display", and is used to display the feed content, which is contained in the aFeed variable.
  */
  getFeed: function(url, callback) {
    if(UWA.Feeds && UWA.Feeds[url]) {
      callback(UWA.Feeds[url]);
      setTimeout(function() { UWA.Feeds[url] = null }, 15000);
      return;
    }
    if (typeof UWA.feedCallbackType == "undefined") UWA.feedCallbackType = "json";
    return this.request(url, { method : 'GET', proxy: 'feed', type: UWA.feedCallbackType, onComplete: callback } );
  },


  /* Method: getXml

  This method is used to get the content of an external XML data source. 
  It can be used to retrieve the content of a feed in XML format.

  Parameters:
    * String url: the URL of the XML data source,
    * Function callback: the callback method that will be fired when the request is succesful. 
    This method *must have one parameter* to receive the XML content returned by the request.

  Returns:
    * Nothing, but if the request is successful, the callback method is fired and receives the XML content as parameter.
  
  Example:
    (start code)
    UWA.Data.getXml('http://example.com/content.xml', myModule.parse);
    myModule.parse = function(xml) {
      // your parsing code
    }
  (end)

  Notes:
    In this example, the callback method is named "parse", and is used to parse the XML tree, which is contained in the "xml" variable.
  */
  getXml: function(url, callback) {
    return this.request(url, { method : 'GET', type: 'xml', onComplete: callback } );
  },
  

  /* Method: getText

  This method is used to get the content of an external data source. 
  It can be used to retrieve any kind of content, as long as it is made of text.

  Parameters:
    * String url: the URL of the data source,
    * Function callback: the callback method that will be fired when the request is succesful. 
    This method *must have one parameter* to receive the text content returned by the request.

  Returns:
    * Nothing, but if the request is successful, the callback method is fired and receives the XML content as parameter.
  
  Example:
    (start code)
    UWA.Data.getText('http://example.com/content.txt', myModule.parse);
    myModule.parse = function(text) {
      // your parsing code
    }

  Notes:
    In this example, the callback method is named "parse", and is used to display the feed content, which is contained in the "text" variable.
  (end)
  */
  getText: function(url, callback) {
    return this.request(url, { method : 'GET', type: 'text', onComplete: callback } );
  },
  

  /* Method: getJson

  This method is used to get the content of an external JSON data source. 
  It can be used to retrieve any kind of JSON data.

  Parameters:
    * String url: the URL of the data source,
    * Function callback: the callback method that will be fired when the request is succesful. 
    This method *must have one parameter* to receive the JSON content returned by the request.

  Returns:
    * Nothing, but if the request is successful, the callback method is fired and receives the JSON content as parameter.
  
  Example:
    (start code)
    UWA.Data.getJson('http://example.com/json.php', myModule.parse);
    myModule.parse = function(text) {
      // your parsing code
    }
  (end)
  */
  getJson: function(url, callback) {
    return this.request(url, { method : 'GET', type: 'json', onComplete: callback } );
  },


  /* Method: getModule

  This method is used to get the content of an external widget, in XML format.

  Parameters:
    * String url: the URL of the feed data source,
    * Function callback: the callback method that will be fired when the request is succesful. 
  This method *must have one parameter* to receive the module content returned by the request.

  Returns:
    * Nothing, but if the request is successful, the callback method is fired and receives the widget's content as parameter.
  */  
  getModule: function(url, callback, id) {
    return this.request(url, { method : 'GET', type: 'xml', proxy: 'api', onComplete: callback } );
  },


  /* Method: request

  This method is used to get the content of an external data source. 
  It can be used to retrieve or set any kind of data: text-based, XML, JSON or a feed.
  The other Ajax methods (getText(), getXml(), getJson(), getFeed()) are all shortcut methods to specific uses of request().
  This method is also the only way to perform HTTP POST request, as well as authenticated requests

  Parameters:
    * String url: the URL of the data source.
    * Object request: a JavaScript object containing setting/value pairs. 
      This object can take a handful of settings, the only required one being 'onComplete', 
      because you need to always set a callback method that will receive the Ajax response.
      That method *must have one parameter* to receive the JSON content returned by the request.

  Sample request object:
     > { method : 'get', proxy: 'ajax', type: 'xml', onComplete: callback }

  Available methods:
      (code)
      ^ Setting       ^ Options                    ^ Default option 
        method          get, post (in lowercase!)    post
        proxy           ajax, feed                   ajax
        type            json, xml, text, html        text
        cache           seconds of server caching)   undefined
        onComplete      (choose your own method)     undefined
        parameters      (your POST parameters)       undefined
        postBody        (in case you need to set it) undefined
        authentication  (the auth object. See doc)   undefined
      (end)
      
  Returns:
    * Nothing, but if the request is successful, the callback method is fired and receives the content as parameter.
  
  Example:
    (start code)
    UWA.Data.request(
    'http://example.org/api.php', 
    { 
    method: 'get', 
    proxy: 'ajax', 
    type: 'xml', 
    cache: 3600,
    onComplete: myModule.parse 
    });
    myModule.parse = function(response) {
      // your parsing code
    }
    (end)
  */
  request: function(url, request) {
    
    if (typeof request == 'undefined') request = {};
    
    if (typeof request.method == 'undefined') request.method = 'GET';
    
    // Always set this header
    if (typeof request.headers  == 'undefined') request.headers = {};
    
    request.headers['X-Requested-Method'] = request.method;
    
    if (request.method == 'DELETE' || request.method == 'PUT') {
      // Support for Opera and Safari
      request.method = 'POST';
    }
    
    if (typeof request.proxy == 'undefined') {
      if (typeof request.authentication == 'object' || location.hostname == '' ||
            (url.substr(0, 4) == "http" && url.indexOf("http://" + location.hostname) == -1) ) {
                request.proxy = 'ajax';
      }
    }
    
    if (typeof request.type == 'undefined') request.type = 'text';
    
    if (UWA.proxies[request.proxy]) {
      url = UWA.proxies[request.proxy] + '?url=' + encodeURIComponent(url);
      if (request.proxy == "feed" && request.shortFeed != false) url += "&rss=1";
    } else if (request.proxy) {
      UWA.log('no proxy URL set for ' + request.proxy);
    }
    
    var auth = request.authentication;
    
    if (typeof auth == 'object') {
      if (auth.type) url += '&auth=' + auth.type;
      if (auth.gp) url += '&gp=' + auth.gp;
      if (auth.moduleId) url += '&moduleId=' + auth.moduleId;
      if (auth.username) url += '&username=' + encodeURIComponent(auth.username);
      if (auth.password) url += '&password=' + encodeURIComponent(auth.password);
    }
      
    if (request.type && request.proxy) {
      url += '&type=' + request.type;
    }
    
    if (typeof request.cache != 'undefined') {
      url += '&cache=' + request.cache;
    }
    
    if(UWA.Client.Engine.ie) {
        url += '&rnd='+ Math.random();
    }
    
    var callbacks = {
      'xml'  : 'onCompleteXML',
      'feed' : 'onCompleteFeed',
      'json' : 'onCompleteJson',
      'text' : 'onCompleteText',
      'html' : 'onCompleteText'
    }
    
    switch(request.type) {
      
      case 'xml':
        var callback = request.onComplete;
        request.onComplete = function() {
          UWA.Ajax.onCompleteXML(arguments, callback);
        }
        return UWA.Ajax.Request(url, request);
        
      default:
        // disable JSON request if no proxy defined
        if(typeof request.proxy == 'undefined' || request.proxy == null) {
          this.useJsonRequest = false;
        }
        if (this.useJsonRequest && typeof request.authentication == "undefined") {
          return UWA.Json.request(url, request);
        } else {
          var callback = request.onComplete;
          var context = request.context;
          if (typeof UWA.Ajax[callbacks[request.type]] == 'undefined') request.type = 'text';
          request.onComplete = function() {
            UWA.Ajax[callbacks[request.type]](arguments, callback, context);
          }
          return UWA.Ajax.Request(url, request);
        }
        
    }
    
  }

};

/*
Class: Environment

The Environment Base class provide base functions to run an UWA Widget Execution Environnement.
It is an abstract class, and must be extended to be useful.
The object must be filled with DOM elements (at least body), in the "<onInit>" or "<onRegisterModule>" callbacks for example.
*/

/*
License:
  Copyright (c) 2005-2008 Netvibes (http://www.netvibes.org/).

  This file is part of Netvibes Widget Platform.

  Netvibes Widget Platform is free software: you can redistribute it and/or modify
  it under the terms of the GNU Lesser General Public License as published by
  the Free Software Foundation, either version 3 of the License, or
  (at your option) any later version.

  Netvibes Widget Platform is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  GNU Lesser General Public License for more details.

  You should have received a copy of the GNU Lesser General Public License
  along with Netvibes Widget Platform.  If not, see <http://www.gnu.org/licenses/>.
*/

UWA.Environment = function() {

  /* Property: widget

  *Widget*: current widget registered in this environnement.
  */
  this.module = null;
  this.widget = this.module;

  /* Property: html

  *Object*: HTML Dom elements related to this environnement
  
  Common Elements are 'title', 'edit', 'body' & 'icon'
  */
  this.html = {};

  /* Property: loaded

  *Boolean*: flag to know if the Environnement is loaded.

  Environment is likely ready once it is instanciated and the DOM is ready to be manipulated.
  */
  this.loaded = false;

  /* Property: registered

  *Boolean*: flag to know if a Widget is registered.

  A widget is not registered until the Environment is *loaded*
  */
  this.registered = false;

  /* internal or advanced use only */
  this.callbacks = {};

  /* 'maybe' deprecated - where is it used ? */
  this.query = '';

  /* internal or advanced use only */
  this.data = {};

  /* Property: debugMode

  *Boolean*: activates or desactivates the debug mode for the widget. 

  The default value is TRUE. When TRUE, messages written with <log> method will appear in the console.
  */
  this.debugMode = false;

  /* Property: periodicals

  *Object*: Stores environment's periodical events. 

  The object is initially empty. It is filled by the <setPeriodical> method.
  */
  this.periodicals = {};

  /* Property: delays

  *Object*: Stores environment's delayed events. 

  The object is initially empty. It is filled by the <setDelayed> method.
  */
  this.delays = {};

  /* Property: height

  *Integer*: The current height of the widget in pixel.

  Notes:
    Experimental. Only used in some environments.
  */
  this.height = 200;

  if(this.initialize) this.initialize();

  this.setPeriodical('init', this.init, 100, true);

}

UWA.Environment.prototype = {
  
  /* Method: init

  Initialize the Environment, when DOM is ready

  Parameters:
  * None.
  */ 
  init: function() {
    if (document.body) {
      this.callback('onInit');
      this.clearPeriodical('init');
      this.log('Environnement loaded');
      this.loaded = true;
      return true;
    }
    return false;
  },

  /* Method: getModule

  Returns the widget (= module) currently registered in the Environment. 
  If no widget is registered, the Environment creates one and registers it.

  Parameters:
  * None.

  Returns:
  * Widget: the (maybe newly created) registered widget.
  */ 
  getModule: function() {
    if (this.module) {
      var module = this.module;
    } else {
      var module = new UWA.Module();
      this.registerModule(module);
    }
    return module;
  },

  /* Method: registerModule

  Registers a Widget (module) into the execution Environment. Once done, fire the *onRegisterModule* callback.

  Parameters:
  * Widget : the widget to register in the Environment.

  Returns:
  * Nothing.
  */ 
  registerModule: function(module) {
    this.module = module;
    this.widget = this.module;
    module.environment = this;
    this.setPeriodical('register', function() {
      if (this.loaded) {
        this.callback('onRegisterModule');
        this.registered = true;
        this.log('Module registered');
        this.clearPeriodical('register');
      }
    }, 100, true);
  },

  /* Method: launchModule

  Launch the registered widget by fire the widget.launch method.
  
  If needed, wait until the environment is fully loaded and a widget registered.

  Parameters:
  * None.
  */
  launchModule: function() {
    this.setPeriodical('launch', function() {
      if (this.loaded && this.module && this.registered) {
          this.log('Launching module');
          this.clearPeriodical('launch');
          this.module.launch();
          if (typeof this.module.onLoadComplete != "function") {
            this.callback('onLoadComplete');
          }
      }
    }, 100, true);
  },

  /* deprecated - internal or advanced use only */
  setCallback: function(name, fn) {
    this.callbacks[name] = fn;
  },

  /* Method: callback

  Fire Environment.key then the callback method associated with the given callback name (key).
  Returns false if no callback method is associated with the given key.

  Parameters:
    * String name: the callback name (e.g. "onUpdateTitle");
    * Object args: one optional argument 
    * Object: an object to bind the callback to

  Returns:
    * Nothing, but calls the method associated with the given callback name (key) 
  */
  callback: function(name, args, bind) {
    if (typeof bind == 'undefined') bind = this;
    try {
      if (this[name]) return this[name].apply(bind, [args]);
      if (this.callbacks[name]) return this.callbacks[name].apply(bind, [args]);
    } catch(e) {
      this.log(e);
    }
    return false;
  },

  /* Method: setPeriodical 

  Register a function as periodical event.

  The function will automatically be binded to the current environment object.

  Parameters:
    * String name: the name of the event
    * Function fn: the function to register
    * Integer delay: the execution delay in milliseconds
    * Boolean force: If true, fire the function for the time right now.

  Notes:
    internal or advanced use only

  */
  setPeriodical: function(name, fn, delay, force) {
    this.clearPeriodical(name);
    fn = fn.bind(this);
    this.periodicals[name] = setInterval(fn, delay);
    if (force) fn();
  },

  /* Method: clearPeriodical 

  Unregister a periodical event previously registered with <setPeriodical>

  Parameters:
    * String name: the name of the event

  Notes:
    internal or advanced use only

  */
  clearPeriodical: function(name) {
    if (this.periodicals[name]) { clearInterval(this.periodicals[name]) }
  },

  /* Method: setDelayed 

  Registers a function as delayed event.

  If 'bind' is not defined, the function will automatically be bound to the current environment object.

  Parameters:
    * String name: the name of the event
    * Function fn: the function to register
    * Integer delay: the delay in milliseconds
    * Object bind: A javascript object to bind the function to.

  Notes:
    internal or advanced use only

  */
  setDelayed: function(name, fn, delay, bind) {
    this.clearDelayed(name);
    if(typeof bind == "undefined" || bind === true) fn = fn.bind(this);
    this.delays[name] = setTimeout(fn, delay);
  },

  /* Method: clearDelayed 

  Unregister a delayed event previously registered with <setDelayed>

  Parameters:
    * String name: the name of the event

  Notes:
    internal or advanced use only

  */
  clearDelayed: function(name) {
    if (this.delays[name]) { clearTimeout(this.delays[name]) }
  },

  /* Method: log
  
  Logs environment's messages in the console, if one exists and if the <debugMode> is true.
  It is using <UWA.log> which usually works with Firebug, Safari and Opera.

  Parameters:
    * String message: the message to display in the console.

  Example:
    > widget.log("Environment is loading");
  */
  log: function(string) {
    if (this.debugMode) UWA.log(string);
  }

}

/*
Class: Widget

The Widget class provides abstract methods to create and manipulate UWA widgets.
The Widget object is typically instanciated as the *widget* var in a widget execution scope.
*/

/*
License:
  Copyright (c) 2005-2008 Netvibes (http://www.netvibes.org/).

  This file is part of Netvibes Widget Platform.

  Netvibes Widget Platform is free software: you can redistribute it and/or modify
  it under the terms of the GNU Lesser General Public License as published by
  the Free Software Foundation, either version 3 of the License, or
  (at your option) any later version.

  Netvibes Widget Platform is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  GNU Lesser General Public License for more details.

  You should have received a copy of the GNU Lesser General Public License
  along with Netvibes Widget Platform.  If not, see <http://www.gnu.org/licenses/>.
*/

UWA.Widget = function() {
    
  /* Section: Properties */

  /* Property: id
    *String* - Unique identifier of the widget.
    The value depends on the execution environment: the Environment registration handler sets this property.
  */
  this.id = '';

  /*
  Property: environment
    *Object* - Reference to the execution environnement.
    The Environment registration handler sets this property. Instance of the Environment class.
  */
  this.environment = null;

  /*
  Property: title
    *String* - widget's title.
    The title of the widget. It is set by the <setTitle> method.
  */
  this.title = '';

  /*
  Property: body
    *Object* - widget's body.
    The main HTML element of the widget.
    Value is null until the <widget> is fully registered in the Environment.
    Should not be used before <launch> or <onLoad> are fired.
  */
  this.body = null;

  /*
  Property: data
    *Object* - Stores widget's data.
    This property can be modified by the <setValue> method, and accessed by the <getValue> method.
  */
  this.data = {};

  /* deprecated - internal or advanced use only */
  this.callbacks = {};

  /*
  Property: preferences
    *Array*: Stores widget's preferences. 
    The array is initially empty. It is initialised by the <setPreferences> method.
  */
  this.preferences = [];

  /*
  Property: metas
    *Object* - Stores widget's metas. 
    The object is initially empty. It is initialised by the <setMetas> method.
  */
  this.metas = {};

  /*
  Property: debugMode
    *Boolean* - activates or desactivates the debug mode for the widget. 
    The default value is TRUE. When TRUE, messages written with <log> method will appear in the console.
  */
  this.debugMode = false;

  /*
  Property: periodicals
    *Object* - Stores widget's periodical events. 
    The object is initially empty. It is filled by the <setPeriodical> method.
  */
  this.periodicals = {};

  /*
  Property: searchResultCount
    *Integer* - the search result count when the widget is onSearch.
    This property is set by the <setSearchResultCount> method.
  */
  this.searchResultCount = 0;

  /*
  Property: unreadCount
    *Integer* - the count of unread items in the widget.
    The unread count is set by the <setUnreadCount> method.
  */
  this.unreadCount = 0;

  /* deprecated - internal or advanced use only */
  this.prefsForm = null;

  /* deprecated - internal or advanced use only */
  this.elements = {};

  /* deprecated */
  this.inline = false;

  /* internal or advanced use only */
  this.apiVersion = '1.2';

  /*
  Property: lang
    *String* - The preferred language as defined by the Environment.
  */
  this.lang = 'en_US';

  /*
  Property: locale
    *String* - The preferred locale as defined by the Environment.
  */
  this.locale = 'us';

  /*
  Property: dir
    *String* - The preferred direction as defined by the Environment.
  */
  this.dir = 'ltr';

  /* internal or advanced use only */
  this.isNew = false;

  /*
  Property: readOnly
    *Boolean* - Default to false. True if the widget is currently read only for the viewer.
  */
  this.readOnly = false;

  /* new - internal or advanced use only */
  this.theme = null;

  if(this.initialize) this.initialize();

}

UWA.Widget.prototype = {
  
  /* Section: Methods */
  
  /* Group: Content management */
  
  /*
  Method: setTitle
  
  Sets the title of the Widget.
  
  Parameters:
    * String title: The title of the widget. Can contain HTML code.
    * String extended: An extra string. Internal use only.
  
  Example:
    > widget.setTitle('Netvibes Blog');
    or
    > widget.setTitle('<a href="http://blog.netvibes.com/">Netvibes Blog</a>');
  
  Notes:
    Implementation can differ between environments.
  */
  setTitle: function(title, extended) {
    this.title = title;
    if (this.elements['title']) {
      if (extended) {
         extended = ' ' + extended + '';
       } else {
         extended = '';
       }
      this.elements['title'].setHTML(title + extended);
    }
    if (this.environment && this.environment.setTitle) this.environment.setTitle(title);
  },
  
  /*
  Method: getTitle
  
  Get the title of the Widget.
  
  Parameters:
   * Nothing

   Returns:
   * String : the title of the widget. HTML tags are stripped.
  */
  getTitle: function() {
    if (this.environment && this.environment.getTitle) return this.environment.getTitle();
    return this.title.stripTags(); // stripTags = prototype.js
  },
  
  /*
  Method: setBody
  
  Sets the body of the Widget. Erases the previous body.
  
  Use the String setContent function.
  
  Parameters:
    * Object content: The body of the widget.
  
  Returns:
    * Nothing, but fire the "onUpdateBody" callback.
    
  Example:
    > var div = widget.createElement('div');
    > div.addClassName('container');
    > div.setHTML("<p>Hello World</p>");
    > widget.setBody(div);
    or
    > widget.setBody("<p>Hello World</p>");
  */
  setBody: function(content) {
    this.body.setContent(content);
    this.callback('onUpdateBody');
  },
  
  /*
  Method: addBody
  
  Adds contents to the existing body of the Widget.
  
  Use the String addContent function.
  
  Parameters:
    * Object content: The content to add in the body of the widget.
  
  Returns:
    * Nothing, but calls the methods associated with the "onUpdateBody" callback. 
  
  Example:
    > var div = widget.createElement('div');
    > div.addClassName('footer');
    > div.setText("Powered by Netvibes UWA.");
    > widget.addBody(div);
    or
    > widget.addBody("<p>Powered by Netvibes UWA.</p>");
  */
  addBody: function(content) {
    this.body.addContent(content);
    this.callback('onUpdateBody');
  },

  /*
  Method: setIcon
  
  Sets the icon for the Widget.
  
  Parameters:
    * String url: the url of the icon. The URL should include the protocol (http://)
    * Boolean search: If true, try to autodiscover the icon for the given url. Internal use only.
  
  Returns:
    * Nothing.
  
  Example:
    > widget.setIcon("http://www.netvibes.com/favicon.ico");
  */
  setIcon: function(url, search) {
    if (this.environment.setIcon) {
      this.environment.setIcon(url, search);
    } else {
      if (this.elements['icon']) {
        url = 'http://' + NV_HOST + '/proxy/favIcon.php?url=' + encodeURIComponent(url);
        this.elements['icon'].setHTML('<img width="16" height="16" src="' + url + '" />');
      }
    }
  },
  
  /* deprecated */
  setElementId: function(element, id) {
    UWA.log('widget.setElementId is deprecated');
    this.$(element).setAttribute('id', 'm_' + this.id + '_' + id);
  },

  /*
  Method: createElement
  
  Creates a new element according to the provided "tagName".
   
   - if options is not defined, works like document.createElement(tagName)
   - if options is defined, works like JS frameworks DOM builders (mootools/prototype) - new Element(tagName, options)
  
  Parameters:
    * String tagName: the HTML tag name of the element to create.
    * Object options: will be set on the newly-created element using Element#setAttributes.
  
  Returns:
    * Element: The created element.
  
  Example:
    > var div = widget.createElement('div');
    or
    > var input = widget.createElement('input', {'type': 'submit', 'value': "Update");
  */
  createElement: function(tagName, options) {
    if (typeof options == 'string') {
      UWA.log('widget.createElement : elName as 2nd argument is deprecated');
      options = {}; // createElement options NEED an objet
    }
    return UWA.createElement(tagName, options);
  },
  
  /* deprecated */
  $: function(el) {
    UWA.log('widget.$ is deprecated');
    if (typeof el == 'string' && this.elements[el]) {
      el = this.elements[el];
    }
    return UWA.$element(el);
  },
  
  /*
  
  Group: Preferences management
  
  This methods are mostly for internal use or advanced scripting.
  Behavior can differ between differents execution environments.
  
  */
  
  /*
  Method: initPreferences
  
  Initializes preferences of the widget. The method gets values from the environnement.
  If values do not exist in the environment, it sets them to their default values.
  This method is likely internaly fired by the <launch> method of the Widget.
  
  Parameters:
    * None.
  */
  initPreferences: function() {
    for (var i = 0; i < this.preferences.length; i++) {
      var pref = this.preferences[i];
      if (typeof pref.name == "undefined") { // no name = preference ignored
         continue; 
      }
      if (pref.defaultvalue) {
        pref.defaultValue = pref.defaultvalue; // fix after xml parsing
      } 
      this.data[pref.name] = this.getValue(pref.name) || pref.defaultValue || null;
    }
  },
  
  /*
  Method: getPreference
  
  Get a preference with its name.
  
  Parameters:
    * String name : the name of the preference
  
  Returns:
    * Object : a preference in its JSON serialization
  
  Example:
    If you have this preference defined in XML
    > <preference name="limit" type="range" label="Number of items to display" defaultValue="5" step="1" min="1" max="25" />
    You can get its javascript representation with the following code
    > widget.getPreference("limit")
  */
  getPreference: function(name) {
    for(var i = 0; i < this.preferences.length; i++) {
      if(this.preferences[i].name == name) return this.preferences[i];
    }
    return null;
  },
  
  /*
  Method: setPreferences
  
  Sets preferences of the widget. Replaces previous preferences.
  
  Parameters:
    * Array schema: an Array of preferences in their JSON serialization
  
  Returns:
    * Nothing.
  
  Example:
    > widget.setPreferences([
    >  {"name":"paging","type":"boolean","label":"Enable pagination","defaultValue":"false"},
    >  {"name":"offset","type":"hidden","defaultValue":"0"}
    > ]);
  */
  setPreferences: function(schema) {
    if(typeof schema == 'object') this.preferences = schema;
    this.callback('onUpdatePreferences');
  },
  
  /*
  Method: mergePreferences
  
  Add preferences to the widget if preferences of the same name are not already defined.
  
  Parameters:
    * Array schema: an Array of preferences in their JSON serialization
    
  Returns:
    * Nothing.
  */
  mergePreferences: function(prefs) {
    for (var i = 0; i < prefs.length; i++) {
      if (this.getPreference(prefs[i].name) == null) this.addPreference(prefs[i]);
    }
  },
  
  /*
  Method: addPreference
  
  Adds a single preference to the existing preferences of the widget.
  
  Parameters:
    * Object : a preference in its JSON serialization
  */
  addPreference: function(preference) {
    this.preferences.push(preference);
  },
  
  /* internal use only - not documented */
  setPreferencesXML: function(prefs) {
    this.preferences = []; // this.setPreferences( [] ); // empty preferences array
    for(var i = 0; i < prefs.length; i++) {
      var preference = {};
      for(var j = 0; j < prefs[i].attributes.length; j++) {
        var name = prefs[i].attributes[j]['nodeName'];
        var value = prefs[i].attributes[j]['nodeValue'];
        preference[name] = value;
      }
      if (preference.type == 'list') {
        var options = prefs[i].getElementsByTagName("option");
        preference.options = [];
        for(var j = 0; j < options.length; j++) {
          var option = {};
          if ( options[j].attributes[0]['value'] ) option[options[j].attributes[0]['name']] = options[j].attributes[0]['value'];
          if ( options[j].attributes[1]['value'] ) option[options[j].attributes[1]['name']] = options[j].attributes[1]['value'];
          preference.options.push(option)
        }
      }
      this.addPreference(preference);
    }
    this.callback('onUpdatePreferences');
  },
  
  /* to document */
  onEdit: function() {
    if (this.prefsForm) {
      var form = this.prefsForm;
    } else {
      var prefsForm = new UWA.Controls.PrefsForm( { module: this } );
      var form = prefsForm.getContent();
    }
    this.elements['edit'].setContent(form);
    var infos = this.getInfos();
    if(infos) this.elements['edit'].addContent(infos);
    // Fire "ShowEdit" notification with HTMLDivElement "edit" as argument
    this.callback('onShowEdit', this.elements['edit']);
    this.elements['edit'].show();
    if(this.elements['editLink']) this.elements['editLink'].setHTML( _("Close Edit") );
  },
  
  /* internal or advanced use only - not documented */
  getInfos: function() {
    if(this.metas['author']) {
      if(this.metas['website']) {
        var content = 'Widget by <strong><a href="' + this.metas['website'] + '" rel="author">' + this.metas['author'] + '</a></strong>';
      } else {
        var content = 'Widget by <strong>' + this.metas['author'] + '</strong>';
      }
      if(this.metas['version']) {
        content += ' - version <strong>' + this.metas['version'] + '</strong>';
      }
      return this.createElement('p').setStyle({'padding': '10px', 'textAlign': 'right'}).setHTML(content);
    }
    return false;
  },
  
  /* to document */
  endEdit: function() {
    this.elements['body'].show();
    this.elements['edit'].hide();
    if (this.elements['editLink']) {
      this.elements['editLink'].show().setHTML( _("Edit") );
    } 
    if (this.onRefresh) {
      this.onRefresh();
    } else if (this.onLoad) {
      this.onLoad();
    }
    this.callback('onHideEdit');
  },
  
  /* Group: Data storage */
  
  /*
  Method: getValue
  
  Gets the value of the given preference.
  
  Parameters:
    * String - name: the name of the preference we want the value of.
  
  Returns:
    * String : the current value of the preference
    
  Example:
    > var url = widget.getValue("feedUrl");
  */
  getValue: function(name) {
    if (typeof this.data[name] != "undefined") {
      return this.data[name];
    } 
    if (this.environment && this.environment.getData) {
      var value = this.environment.getData(name);
      if (value == 'null') {
        value = null;
      } 
      this.data[name] = value;
      return value;
    }
    return null;
  },
  
  /*
  Method: getInt
  
  Gets the Integer value of the given preference.
  
  It is particularly advised to use getInt when a preference is of type range.
  
  Parameters:
    * String name: the name of the preference we want the value of.
  
  Returns:
    * Number : the current value of the preference, converted as integer.
  */
  getInt: function(name) {
    var value = this.getValue(name);
    if (value == 'true' || value == true) {
      value = 1;
    }
    value = parseInt(value, 10);
    return isNaN(value) ? 0 : value;
  },
  
  /*
  Method: getBool
  
  Gets the Boolean value of the given preference.
  
  It is particularly advised to use getBool when a preference is of type boolean.
  
  Parameters:
    * String name: the name of the preference we want the value of.
  
  Returns:
    * Boolean : the current value of the preference, converted as boolean.
  */
  getBool: function(name) {
    return this.getInt(name) ? true : false;
  },
  
  /*
  Method: setValue
  
  Sets the value of the given preference.
  
  Parameters:
    * String name: the name of the preference we want to set.
    * String value: the value of the preference
  
  Returns:
    * Object: the value of the preference we set.
  
  Example:
    > widget.setValue("nbItems", "5");
  */
  setValue: function(name, value) {
    if (this.data[name] == value) {
      return value;
    } 
    this.data[name] = value;
    var pref = this.getPreference(name);
    if (this.environment && this.environment.setData) {
      this.environment.setData(name, value);
    } 
    return value;
  },
  
  /* new - to document */
  deleteValue: function(name) {
    delete this.data[name];
    if (this.environment && this.environment.deleteData) {
      return this.environment.deleteData(name);
    }
  },
  
  /* internal or advanced use only - not documented */
  saveValues: function(callback) {
    if (this.environment && this.environment.saveDatas && this.readOnly == false) {
      this.environment.saveDatas(callback);
    } else {
      callback();
    }
  },
  
  /* Group: Others */
  
  /*
  Method: log
  
  Logs widget's messages in the console, if one exists and if the "<debugMode>" is true.
  It is using <UWA.log> which usually works with Firebug, Safari and Opera.
  
  Parameters:
    * String message: the message to display in the console.
  
  Example:
    > widget.log("Widget is loading");
  */
  log: function(message) {
    if (this.debugMode === true) UWA.log(message);
  },
  
  /*
  Method: setPeriodical 
  
  Register a function as periodical event.
  
  The function will automatically be binded to the current widget object.
  
  Parameters:
    * String name: the name of the event
    * Function fn: the function to register
    * Integer delay: the execution delay in milliseconds
    * Boolean force: If true, fire the function for the time right now.
    
  Notes:
    internal or advanced use only
    
  */
  setPeriodical: function(name, fn, delay, force) {
    this.clearPeriodical(name);
    this.periodicals[name] = setInterval(fn.bind(this), delay);
    if (force) fn();
  },
  
  /*
  Method: clearPeriodical 
  
  Unregister a periodical event previously registered with <setPeriodical>
  
  Parameters:
    * String name: the name of the event
    
  Notes:
    internal or advanced use only
    
  */
  clearPeriodical: function(name) {
    if (this.periodicals[name]) { clearInterval(this.periodicals[name]) }
  },
  
  /*
  Method: callback
  
  Executes the callback method associated with the given callback name (key). 
  Returns false if no callback method is associated with the given key.
  
  Parameters:
    * String name: the callback name (e.g. "onUpdateTitle");
    * Object args: one optional argument 
    * Object: an object to bind the callback to
  
  Returns:
    * Nothing, but calls the method associated with the given callback name (key) 
  */
  callback: function(name, args, bind) {
    if (typeof bind == 'undefined') bind = this;
    try {
      if (this[name]) this[name].apply(bind, [args]);
      if (this.callbacks[name]) this.callbacks[name].apply(bind, [args]);
    } catch(e) {
      UWA.log(e);
    }

    if(this.environment && this.environment.callback) this.environment.callback(name); 

  },
  
  /* deprecated - internal or advanced use only */
  setCallback: function(name, fn) {
    this.callbacks[name] = fn;
  },
  
  /*
  Method: setMetas 
  
  Set the metas of the widget.
  
  Parameters:
    * Object metas: metas in a key:value form 
    
  Notes:
    internal or advanced use only
    
  */
  setMetas: function(metas) {
    this.metas = metas;
    if(this.metas.debugMode) this.setDebugMode(this.metas.debugMode);
    if(this.metas.autoRefresh) this.setAutoRefresh(this.metas.autoRefresh);
  },
  
  /* to document */
  setDebugMode: function(mode) {
    if (mode === true || mode == 'true') this.debugMode = true; else this.debugMode = false;
  },
  
  /* deprecated */
  setInline: function(mode) {
    UWA.log('widget.setInline is deprecated');
    if (mode) this.inline = true; else this.inline = false;    
  },
  
  /*
  Method: setAutoRefresh
  
  Sets the auto-refresh interval for the widget. 
  The widget must have a "onRefresh" method to work properly.
  
  Parameters:
    * Integer - delay: the refresh delay, in *minutes*.
  
  Returns:
    * Nothing.
  
  Example:
    > widget.setAutoRefresh(20); // Set the auto-refresh interval to 20 minutes
  */
  setAutoRefresh: function(delay) {
    var rndUpdateTime = Math.round(10*1000*Math.random());
    delay = parseInt(delay);
    if (this.onRefresh && delay && delay > 0) {
      delay = delay * 1000 * 60; // from minutes to milliseconds
      this.setPeriodical('autoRefresh', this.onRefresh, delay + rndUpdateTime);
    }
  },
  
  /* internal use only - not documented */
  setMetasXML: function(metas) {
    var metasArray = [];
    for(var i = 0; i < metas.length; i++) {
      if(metas[i].name) var name = metas[i].name;
      else var name = metas[i].attributes[0]['nodeValue'];
      if(metas[i].content) var value = metas[i].content;
      else var value = metas[i].attributes[1]['nodeValue'];
      if(value == 'false') value = false; else if(value == 'true') value = true; // booleanise
      metasArray[name] = value;
    }
    this.setMetas(metasArray);
  },
  
  /*
  Method: setStyle 
  
  Set the stylesheet of the widget with the given CSS rules.
  
  Notes:
    Internal or advanced use only
  */
  setStyle: function(style) {
    if (typeof style == 'string') {
      UWA.Utils.setCss(this.id, style);
    }
  },
  
  /* deprecated */
  setCSS: function(css) {
    UWA.log('widget.setCSS is deprecated. Use widget.setStyle instead.');
    UWA.Utils.setCss(this.id, css);
  },
  
  /* experimental - internal use only - not documented */
  setTemplate: function(module) {
    UWA.log('setTemplate:' + module.name);
    var tpl = module.name;
    var klass = new UWA.Templates[tpl](this);
    klass.createFromJSON(module);
  },
  
  /* experimental - internal use only - not documented */
  setFeeds: function(feeds) {
    if (typeof UWA.Feeds == "undefined") UWA.Feeds = {};
    for (key in feeds) UWA.Feeds[key] = feeds[key];
  },
  
  /*
  Method: setSearchResultCount
  
  Sets the search result count.
  
  Parameters:
    * Integer - count: the count of results for the current search terms.
  
  Returns:
    * Nothing, but updates the title with the result count, if greater or equal to zero.
  */
  setSearchResultCount: function(count) {
    this.searchResultCount = count;
    if (this.environment.setSearchResultCount) this.environment.setSearchResultCount(count);
  },
  
  /*
  Method: setUnreadCount 
  
  Sets the count of unread items.
  
  Parameters:
    * Integer - count: the count of unread items.
  
  Returns:
    * Nothing, but updates the title with the unread count, if greater or equal to zero.
  */  
  setUnreadCount: function(count) {
    this.unreadCount = count;
    if (this.environment && this.environment.setUnreadCount) this.environment.setUnreadCount(count);
  },
  
  /*
  Method: openURL
  
  Open an URL. Behavior differ between execution environments.
    - open the page in an iframe on the same screen
    - open the page in a new window/tab
    - open the page in a new browser window (desktop widgets)
  
  Parameters:
    * String url: the url to open in a new window
  */
  openURL: function(url) {
    if (this.environment && this.environment.openURL) this.environment.openURL(url); 
    else window.open(url);
  },
  
  /* experimental - to be documented */
  getHistory: function() {
    if (this.environment && this.environment.getHistory) return this.environment.getHistory();
    else return this.getValue('history');
  },
  setHistory: function(history) {
    if (this.environment && this.environment.setHistory) this.environment.setHistory(history);
    else this.setValue('history', history);
  },
  saveHistory: function() {
    if (this.environment && this.environment.saveHistory) this.environment.saveHistory();
  },
  addStar: function(data) {
    if (this.environment && this.environment.addStar) this.environment.addStar(data);
  },
  
  /*
  Method: launch 
  
  Launch the widget : call <initPreferences> then fire widget.onLoad.
  
  Notes:
    Internal or advanced use only
  */
  launch: function() {
    this.initPreferences();
    this.callback('onLoad');
  }
  
};

// old name
UWA.Module = UWA.Widget;

/*
Library: UWA Utils
*/

/*
License:
  Copyright (c) 2005-2008 Netvibes (http://www.netvibes.org/).

  This file is part of Netvibes Widget Platform.

  Netvibes Widget Platform is free software: you can redistribute it and/or modify
  it under the terms of the GNU Lesser General Public License as published by
  the Free Software Foundation, either version 3 of the License, or
  (at your option) any later version.

  Netvibes Widget Platform is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  GNU Lesser General Public License for more details.

  You should have received a copy of the GNU Lesser General Public License
  along with Netvibes Widget Platform.  If not, see <http://www.gnu.org/licenses/>.
*/

UWA.Utils = {
  
  /* only used in /modules/uwa/uwa2.js */
  buildUrl: function (moduleUrl, linkHref) {
        var first_split = moduleUrl.split("://");
        var scheme =  first_split[0]
        var without_resource = first_split[1];
        var second_split = without_resource.split("/");
        var domain = second_split[0];
        var path = '';
        for (i=1;i<second_split.length-1;i++) {
            path += '/' + second_split[i];
        }
        if (linkHref.split("://").length > 1) { // complete
            return false;
        } else if (linkHref.substring(0, 1) == '/') { // absolute
            return scheme +'://' + domain + linkHref;
        } else { // relative
            return scheme +'://' + domain + path + '/' + linkHref;
        }
    },

  setTooltip: function(element, text, width) {
    if (window.App && App.toolTip)
      new App.toolTip(element, text, width, "left");
    return false;
  },
  
  /* used in /js/UWA/Widget.js & /modules/uwa/uwa2.js */
  setCss: function(id, content, namespace) {
    
    if (typeof namespace == 'undefined') {
      var namespace = ( id && id != '' ? '#m_' + id : '');
    }
    
    var cssId = 'css_' + id;
    
    if (!$(cssId)) {
      var css = document.createElement("style");
      css.setAttribute('id', cssId);
      css.setAttribute('type','text/css');
      var head = document.getElementsByTagName('head').item(0);
      head.appendChild(css);
    } 
    
    content = "\n" + content + "\n"; // fix a problem with the final regexp
    content = content.replace(/,/g, ",\n"); // fix a problem with the final regexp
    content = content.replace(/#moduleContent/g, ''); // remove namespace (Netvibes - not documented)
    content = content.replace(/#container/g, ''); // remove namespace (WZD)
    content = content.replace(/\n\s*([a-zA-z0-9\.\-, :#]*)\s*([{|,])/g, "\n" + namespace + " $1$2");
    
    if ($(cssId).styleSheet){ // IE
      $(cssId).styleSheet.cssText = content;
    } else { // w3c
      $(cssId).appendChild( document.createTextNode(content) );
    }
    
  }
  
}

/*
Script: Client

Credits:
  Partially based on MooTools, My Object Oriented Javascript Tools.
  Copyright (c) 2006-2007 Valerio Proietti, <http://mad4milk.net>, MIT Style License.
    
Class: Client
  Some browser properties are attached to the Client object for browser detection.

Engine:
  UWA.Client.Engine.ie - is set to true if the current browser is internet explorer (any)
  UWA.Client.Engine.ie6 - is set to true if the current browser is internet explorer 6
  UWA.Client.Engine.ie7 - is set to true if the current browser is internet explorer 7
  UWA.Client.Engine.gecko - is set to true if the current browser is Mozilla/Gecko
  UWA.Client.Engine.webkit - is set to true if the current browser is Safari/Konqueror
  UWA.Client.Engine.webkit419 - is set to true if the current browser is Safari2 / webkit till version 419
  UWA.Client.Engine.webkit420 - is set to true if the current browser is Safari3 (Webkit SVN Build) / webkit over version 419
  UWA.Client.Engine.opera - is set to true if the current browser is opera
  UWA.Client.Engine.name - is set to the name of the engine

Platform:
  UWA.Client.Platform.mac - is set to true if the platform is mac
  UWA.Client.Platform.windows - is set to true if the platform is windows
  UWA.Client.Platform.linux - is set to true if the platform is linux
  UWA.Client.Platform.other - is set to true if the platform is neither mac, windows or linux
  UWA.Client.Platform.name - is set to the name of the platform

Note:
  Engine detection is entirely object-based.
*/

/*
License:
  Copyright (c) 2005-2008 Netvibes (http://www.netvibes.org/).

  This file is part of Netvibes Widget Platform.

  Netvibes Widget Platform is free software: you can redistribute it and/or modify
  it under the terms of the GNU Lesser General Public License as published by
  the Free Software Foundation, either version 3 of the License, or
  (at your option) any later version.

  Netvibes Widget Platform is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  GNU Lesser General Public License for more details.

  You should have received a copy of the GNU Lesser General Public License
  along with Netvibes Widget Platform.  If not, see <http://www.gnu.org/licenses/>.
*/

UWA.Client = {Engine: {'name': 'unknown', 'version': ''}, Platform: {}, 'features': {}};

//features
UWA.Client.features.xhr = !!(window.XMLHttpRequest);
UWA.Client.features.xpath = !!(document.evaluate);

//engine
if (window.opera) UWA.Client.Engine.name = 'opera';
else if (window.ActiveXObject) UWA.Client.Engine = {'name': 'ie', 'version': (UWA.Client.features.xhr) ? 7 : 6};
else if (!navigator.taintEnabled) UWA.Client.Engine = {'name': 'webkit', 'version': (UWA.Client.features.xpath) ? 420 : 419};
else if (document.getBoxObjectFor != null) UWA.Client.Engine.name = 'gecko';
UWA.Client.Engine[UWA.Client.Engine.name] = UWA.Client.Engine[UWA.Client.Engine.name + UWA.Client.Engine.version] = true;

//platform
var platform = navigator.platform.match(/(mac)|(win)|(linux)|(nix)/i) || ['Other'];
UWA.Client.Platform.name = platform[0].toLowerCase();
UWA.Client.Platform[UWA.Client.Platform.name] = true;

// Retro compatibility with old Browser object
if (typeof Browser == "undefined") var Browser = {};
if(UWA.Client.Engine.ie) Browser.isIE = true; else Browser.isIE = false;
if(UWA.Client.Engine.opera) Browser.isOpera = true; else Browser.isOpera = false;


//
// The NowPublic environment - based on other environments
//

/* -------------------------------------------------------- */

/* UWA Container */

if (typeof UWA == "undefined") var UWA = {};
if (typeof UWA.Container == "undefined") UWA.Container = {};

// Widget environments, indexed by the widget id

UWA.Container.environments = {};

// Booting the widgets, the UWA.Widgets array should be prepared

UWA.Container.start = function() {
  for(var i = 0, l = UWA.Widgets.length; i < l; i++) {
    this.registerWidget(UWA.Widgets[i]);
  }
}

// Register a widget: setup the widget environment and the widget object

UWA.Container.registerWidget = function(options) {
  var environment = new UWA.Environment();
  environment.debugMode = false;
  // NowPublic extension, show preferences on widget load
  environment.loadPrefs = typeof options.loadPrefs != 'undefined' ? options.loadPrefs : false;
  environment.id = options.id;
  environment.data = options.data;
  environment.setContainer(options.chromeId);
  environment.loadModule = function() {
    var module = this.getModule();
    UWA.Scripts[options.skeletonId](module);
    this.setDelayed('launchModule', this.launchModule, 1);
  };
  UWA.Container.environments[options.id] = environment;
  environment.loadModule();
}

/* -------------------------------------------------------- */

/* Extending the UWA Environment */

UWA.extend(UWA.Environment.prototype, {

  // initializing the widget container, the DOM of the widget
  setContainer: function(el) {
    el = $('#'+el).get(0);
    this.html = {
      'chrome':     el,
      'header':     $('.moduleHeader', el).get(0),
      'title':      $('.title', el).get(0),
      'edit':       $('.editContent', el).get(0),
      'body':       $('.moduleContent', el).get(0),
      'helpLink':   $('<img class="icon icon-01-help" title="Help" src="/html/images/1px.gif" />'),
      'editLink':   $('<img class="icon icon-01-expand" title="Edit" src="/html/images/1px.gif" />'),
      'removeLink': $('<img class="icon icon-01-close" title="Close" src="/html/images/1px.gif" />')
    };
    // display the edit and close items only if we're not readOnly mode
    if (Drupal && Drupal.settings && Drupal.settings.uwa && !Drupal.settings.uwa.readOnly) {
      $(this.html.edit).before(this.html['help'] = $('<div class="helpContent"></div>'));
      this.html['helpLink'][0].style.display = 'none';
      $('.corner-top', this.html.header).after(this.html.helpLink).after(this.html.editLink).after(this.html.removeLink);
    }
  },

  // initializing some module elements by extending the DOM elements by the UWA methods
  // it happens before adding the actual widget JS code to the module object, so no widget specific info available
  onRegisterModule: function() {
    for (var key in this.html) {
      this.module.elements[key] = UWA.extendElement(this.html[key]);
    }
    this.module.id = this.id;
    this.module.body = this.module.elements['body']; // shortcut
    this.module.readOnly = true;
    if (Drupal && Drupal.settings && Drupal.settings.uwa) {
      this.module.readOnly = Drupal.settings.uwa.readOnly;
    }
    this.module.endEdit = function() {
      this.environment.hidePrefs();
      if (this.onRefresh) {
        this.onRefresh();
      } else if (this.onLoad) {
        this.onLoad();
      }
      this.callback('onHideEdit');
    }
  },

  // TODO: describing what is it about
  onLoadComplete: function() {
    var environment = this;
  	this.helpMode = false;
    $(this.html['helpLink']).click(function() {
      environment.callback('toggleHelp');
      return false;
    });
    $(this.html['editLink']).click(function() {
      environment.callback('togglePrefs');
      return false;
    });
    $(this.html['removeLink']).click(function() {
      environment.callback('removeModule');
      return false;
    });
    if (this.html['help']) {
      $(this.html['help']).text(this.module.metas.description);
    }
  	this.prefsMode = false;
    if (this.loadPrefs) {
      this.showPrefs();
    }
  },

  // Retrieving the widget settings
  getData: function(name) {
    return this.data[name];
  },
  
  // Storing the widget settings -- saving only if the values are changed
  setData: function(name, value) {
    if (this.data[name] != value) {
      this.data[name] = value;
      // TODO: no reason for delay if we don't save it to the server
      this.setDelayed('saveData', this.saveData, 1000);
    }
  },
   
  // Handling the save event -- TODO: it should be quite different based on the actual widget use
  saveData: function(callback) {
    var data = {};
    var wnum = 0; while (wnum < UWA.Widgets.length && UWA.Widgets[wnum].id != this.id) { wnum++; }
    for (var i in this.data) {
      if (i !== '' && i.substr(0,1) != '_' && typeof this.data[i] == 'string')
        UWA.Widgets[wnum].data[i] = this.data[i];
        data[i] = this.data[i];
    }
    var state = jQuery.toJSON(UWA.Widgets);
    $('#edit-uwa-settings').val(state);
    // the on-the-fly save, if it's not a new widget
    if (!this.id.match(/^tmp/)) {
      $.post('/uwa/save/'+this.id, data, function(response) {
        // we don't process the response
      }, 'text');
    }
  },

  // The Edit switch for hiding and displaying the widget preferences
  togglePrefs: function() {
    if (this.prefsMode) {
      this.hidePrefs();
    } else {
      this.showPrefs();
    }
  },

  // Hiding the widget preferences
  hidePrefs: function() {
    this.prefsMode = false;
    this.html['edit'].setStyle('display', 'none');
    this.html['helpLink'].setStyle('display', 'none');
    this.toggleHelp(false);
  },

  // Showing the widget preferences
  showPrefs: function() {
    this.prefsMode = true;
    var editContent = this.html['edit'];
    editContent.innerHTML = '';
    // TODO: the form should be updated even if it's ready, as changing the preference values is possible by setData, not just using the form
    // if (!this.form) {
      // We're using the PrefsForm UWA Control, it's not the general one, but a NowPublic specific
      this.prefsForm = new Netvibes.UI.PrefsForm( { module: this.module, displayButton: true } );
      this.form = editContent.appendChild(this.prefsForm.getContent());
    // }
    editContent.setStyle('display', 'block');
    this.html['helpLink'].setStyle('display', '');
    this.toggleHelp(false);
  },

  // The Edit switch for hiding and displaying the widget preferences
  toggleHelp: function(newmode) {
    if (typeof newmode == 'undefined') {
      newmode = !this.helpMode;
    }
    if (newmode) {
      this.showHelp();
    } else {
      this.hideHelp();
    }
  },

  // Hiding the widget help
  hideHelp: function() {
    this.helpMode = false;
    this.html['help'].setStyle('display', 'none');
  },

  // Showing the widget help
  showHelp: function() {
    this.helpMode = true;
    this.html['help'].setStyle('display', 'block');
  },

  // Deletes the widget
  removeModule: function() {
    // From the UWA.Widgets list
    var wnum = 0;
    while (wnum < UWA.Widgets.length && UWA.Widgets[wnum].id != this.id) { wnum++; }
    UWA.Widgets[wnum].deleted = true; // old behaviour was: UWA.Widgets.splice(wnum,1);

    // If available, remove the widget from the story page Drag'n'Drop registry (TODO: a callback would be better)
    if (NP && NP.story && NP.story.widgetDnD && NP.story.widgetDnD.removeWidget) {
      NP.story.widgetDnD.removeWidget(this.html['chrome']);
    }

    // Save the state to the settings textarea
    var state = jQuery.toJSON(UWA.Widgets);
    var settings = $('#uwa-settings').get(0);
    if (!settings) {
      var settings = document.createElement('textarea');
      settings.id = 'uwa-settings';
      document.body.appendChild(settings);
    }
    settings.value = state;

    // And from the DOM
    $(this.html['chrome']).remove();
  }
   
});

/* -------------------------------------------------------- */

/* UWA Frame Communication Handler -- for widgets displayed in frames */

// TODO: handling setValue and other events

UWA.merge(document, {'addListener': UWA.Element.addListener});
UWA.merge(window, {'addListener': UWA.Element.addListener});

var rt = {}

UWA.MessageHandler = function(message) {
  // UWA.log('Received message ' + message.action);
  var id = message.id;
  switch (message.action) {
    case 'resizeHeight':
      var frame = document.getElementById('frame-' + id);
      if (frame) {
        if (message.value > 100 || message.value > frame.getAttribute('height') ) var delay = 100;
        else var delay = 1500;
        if (rt[id]) clearTimeout(rt[id]);
        rt[id] = setTimeout(function() { frame.setAttribute('height', message.value) }, delay);
      }
      break;
    case 'setTitle':
      var title = $('#widget-' + id + xxxxxx);
      var title = chrome.getElementsByClassName("title").get(0);
      if (title) {
        title = UWA.extendElement(title);
        title.setHTML(message.value);
      }
      break;
    case 'setValue':
    case 'deleteValue':
    case 'audioPlayer':
    case 'addStar':
    case 'setIcon':
    case 'setUnreadCount':
    case 'setSearchResultCount':
    default:
      UWA.log(message.action + ': not implemented - ' + message.name + ':' + message.value);
      break;
  }
};

// Post Message Handler
document.addListener("message", function(e) {
  UWA.MessageHandler(UWA.Json.decode(e.data));
});
window.addListener("message", function(e) {
  UWA.MessageHandler(UWA.Json.decode(e.data));
});
/*
License:
  Copyright (c) 2005-2008 Netvibes (http://www.netvibes.org/).

  This file is part of Netvibes Widget Platform.

  Netvibes Widget Platform is free software: you can redistribute it and/or modify
  it under the terms of the GNU Lesser General Public License as published by
  the Free Software Foundation, either version 3 of the License, or
  (at your option) any later version.

  Netvibes Widget Platform is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  GNU Lesser General Public License for more details.

  You should have received a copy of the GNU Lesser General Public License
  along with Netvibes Widget Platform.  If not, see <http://www.gnu.org/licenses/>.
*/

UWA.Controls.PrefsForm = function(options) {

    /* Property: module

    *Module*: the module using the current Preference form.
    */
    this.widget = options.module;

    /* Property: displayButton

    *Type*: ..
    */
    this.displayButton = options.displayButton;
}

UWA.Controls.PrefsForm.prototype.controls = {}

UWA.Controls.PrefsForm.prototype.controls['default'] = function(pref) {
  var input = this.widget.createElement('input', {
    'id'    : 'm_' + this.widget.id + '_' + pref.name,
    'class' : 'text',
    'type'  : 'text',
    'name'  : pref.name,
    'value' : this.widget.getValue(pref.name) || ''
  });
  if (pref.autocomplete) {
    if (!UWA.ACDBs) {
      UWA.ACDBs = [];
    }
    if (!UWA.ACDBs[pref.autocomplete]) {
      UWA.ACDBs[pref.autocomplete] = new Drupal.ACDB(pref.autocomplete);
    }
    new Drupal.jsAC(input, UWA.ACDBs[pref.autocomplete]);
    input.className+=' form-autocomplete';
    input.setAttribute('autocomplete', 'off');
  }
  return input;
}

UWA.Controls.PrefsForm.prototype.controls['boolean'] = function(pref) {
  var input = this.widget.createElement('input', {
    'id'   : 'm_' + this.widget.id + '_' + pref.name,
    'class': 'checkbox',
    'type' : 'checkbox',
    'name' : pref.name
  });
  if (this.widget.getBool(pref.name) === true) {
    input.setAttribute('checked', 'checked');
    input.defaultChecked = true; // for IE
  }
  if (pref.onchange) {
    input.onclick = ( function(e) {
      var sender = (e.target || e.srcElement);
      if(sender.checked == true) {
        this.widget.setValue(pref.name, 'true');
      } else {
        this.widget.setValue(pref.name, 'false');
      }
      this.widget.callback(pref.onchange);
    } ).bindAsEventListener(this);
  }
  return input;
}

UWA.Controls.PrefsForm.prototype.controls['password'] = function(pref) {
  var input = this.widget.createElement('input', {
    'id'    : 'm_' + this.widget.id + '_' + 'pass',
    'class' : 'text',
    'type'  : 'password',
    'name'  : 'pass',
    'value' : ''
  });
  return input;
}

UWA.Controls.PrefsForm.prototype.controls['textarea'] = function(pref) {
  var textarea = this.widget.createElement('textarea', {
    'id'   : 'm_' + this.widget.id + '_' + pref.name,
    'class' : 'textarea',
    'name' : pref.name
  }).setText( this.widget.getValue(pref.name) || '' );
  return textarea;
}

UWA.Controls.PrefsForm.prototype.controls['range'] = function(pref) {   
  var select = this.widget.createElement('select', {
    'id'   : 'm_' + this.widget.id + '_' + pref.name,
    'class': 'select',
    'name' : pref.name
  });
  if (parseInt(pref.step) > 0) {
    for (var i = parseInt(pref.min); i <= parseInt(pref.max); i += parseInt(pref.step)) {
      var option = this.widget.createElement('option', { 'value': i }).setText("" + i);
      if (this.widget.getValue(pref.name) == i) {
        option.setAttribute('selected', 'selected');
      } 
      select.appendChild(option);
    }
  }
  if (pref.onchange) {
    select.onchange = ( function(e) {
      var sender = (e.target || e.srcElement);
      this.widget.setValue(pref.name, sender.value)
      this.widget.callback(pref.onchange);
    } ).bindAsEventListener(this);
  }
  return select;
}

UWA.Controls.PrefsForm.prototype.controls['list'] = function(pref) {   
  var select = this.widget.createElement('select', {
    'id'   :  'm_' + this.widget.id + '_' + pref.name,
    'class': 'select',
    'name' :  pref.name
  });
  var onselect = false;
  var onselects = {};
  for (var i = 0; i < pref.options.length; i++) {
    var option = pref.options[i];
    option.label = option.label || option.value;
    var optionElement = this.widget.createElement('option').setText(option.label).inject(select);
    optionElement.value = option.value;
    if (this.widget.getValue(pref.name) == option.value) {
      optionElement.setAttribute('selected', 'selected');
    }
    if (option.show) {
      onselect = true;
      onselects[option.value] = option.show;
    }
  }
  if (pref.onchange || onselect) {
    select.onchange = ( function(e) {
      var sender = (e.target || e.srcElement);
      this.widget.setValue(pref.name, sender.value);
      for (onselect in onselects) {
        $('#m_' + this.widget.id + '_' + onselects[onselect], sender.parentNode.parentNode.parentNode)[0]
          .parentNode.parentNode.style.display = (onselect==sender.value?'block':'none');
      }
      if (pref.onchange) {
        this.widget.callback(pref.onchange);
      }
    } ).bindAsEventListener(this);
  }
  return select;
}

/* Method: build

Gets the content of the preference form, for display.

Parameters:
* None.

Returns:
* String: the HTML version of the preference form.
*/
UWA.Controls.PrefsForm.prototype.build = function() {
    
  var widget = this.widget;

  var form = this.form = widget.createElement("form");

  var controls = [];
  for (var i = 0; i < widget.preferences.length; i++) {
    var pref = widget.preferences[i];
    if (pref.type == 'hidden') {
      continue;
    }
    var item = widget.createElement("div").addClassName('item').inject(form);
    var tlabel = _( (pref.label || pref.name) + ':' );
    var label = widget.createElement("label", {
      'for': widget.id + '_' + pref.name 
    }).setText(tlabel).inject(item);
    if (typeof this.controls[pref.type] == 'undefined') {
      pref.type = 'default';
    }
    var control = this.controls[pref.type].bind(this)(pref);
    widget.createElement("div").setContent(control).inject(item);
    controls.push(control);
  }
  // ugly hack for list preference's show support
  for (var i = 0; i < controls.length; i++) {
    if (controls[i].nodeName == 'SELECT' && controls[i].onchange) {
      controls[i].onchange.call(this, {'target': controls[i]});
    }
  }

  if (typeof this.displayButton == 'undefined' || this.displayButton === true) {
    var item = widget.createElement("div").addClassName('submit').inject(form);
    var button = widget.createElement('input', {
      'type'  : 'button',
      'value' :  _("Ok")
    });
    button.addClassName('buttonClean').inject(item);

    button.onclick = ( function() {
      var callback = ( function() { this.callback('endEdit') } ).bind(this.widget);
      this.saveValues();
      // force to saveValues immediatly and add a callback when it's done
      this.widget.saveValues(callback);
      return false;
    } ).bindAsEventListener(this);

  }
  form.onsubmit = function(){ return false; }

  return form;

}

UWA.Controls.PrefsForm.prototype.getContent = UWA.Controls.PrefsForm.prototype.build;

UWA.Controls.PrefsForm.prototype.saveValues = function(formElement) {
    
  var widget = this.widget;
  var form = formElement || this.form
  var formElements = UWA.Form.getElements(form);

  for (var j = 0; j < formElements.length; j++) {
    var el = formElements[j];
    switch (el.type) {
      case 'submit':
        break;
      case 'password':
        if (el.value != '') {
          widget.setValue(el.name, el.value)
        }
        break;
      case 'checkbox':
        if (el.checked) {
          widget.setValue(el.name, 'true');
        } else {
          widget.setValue(el.name, 'false');
        }
        break;  
      case 'radio':
        if (el.checked) {
          widget.setValue(el.name, el.value);
        }
        break;
      default :
       widget.setValue(el.name, el.value);
      break;
    }
  }

}

