Index: trunk/prototype/modules/calendar/js/calendar.codecs.js
===================================================================
--- trunk/prototype/modules/calendar/js/calendar.codecs.js	(revision 5652)
+++ trunk/prototype/modules/calendar/js/calendar.codecs.js	(revision 5715)
@@ -2,5 +2,5 @@
   
     init: function(){
-		this.moduleName = 'expressoCalendar';
+		this.moduleName = 'expressoCalendar'; 
 		this.me = DataLayer.dispatch( "me" );
 		
@@ -9,5 +9,5 @@
 		this.isDaylightSaving = Timezone.daylightSaving;
 		this.load();      
-    },
+    },             
   
     load: function(){
@@ -564,8 +564,9 @@
 				return returns;
 			};
-						
+								
 		    return {
 			    "class": objEvent["class"],
 			    id: objEvent.id,
+			    repeat: encodeRepeat( objEvent.repeat ),
 			    location: objEvent.location,
 			    category: objEvent.category,
@@ -605,7 +606,5 @@
 							receiveNotification : attend.receiveNotification,
 							alarms : $.map(attend.alarms || [], function( alarm ){
-								//TODO - Atualizar cach cliente em conceito aninhados de camada 2
-								var alarmEvent = DataLayer.get('alarm', alarm)
-								return !!alarmEvent ? alarmEvent: (null);
+								return DataLayer.get('alarm', alarm);
 							})
 						};
@@ -643,4 +642,5 @@
 			    me: !!participantInfo.user ? DataLayer.merge(participantInfo.user, me) : DataLayer.merge(this.isShared ? this.organizer :  me, {acl:  !this.isShared ? 'rowi' : Calendar.signatureOf[objEvent.calendar].permission.aclValues, delegatedFrom: 0, receiveNotification: 1}),
 				delegatedFrom: delegatedFrom,
+				
 				statusParticipants: statusParticipants
 			};
@@ -650,5 +650,85 @@
 });
 
-
+function decodeRepeat ( form ) {
+
+	var patati = {};
+	
+	if( form.repeatId )
+	    patati['id'] = form.repeatId;
+
+	patati['frequency'] = form.frequency;
+
+ 	patati['bymonthday'] = patati['byyearday'] = patati['byday'] = '';
+	
+	patati['interval'] = patati['endTime'] = patati['count'] = patati['startTime'] = 0;
+	
+	if( form.frequency === 'none' )
+	    return( patati );
+	
+	var day = [];
+
+	$("input[type=checkbox][name='repeatweekly[]']:checked").each(function() { 
+		day[ day.length ] = $(this).val();
+	});
+
+	patati['byday'] = day.join(',');
+
+	var formatString = ( typeof form.allDay !== "undefined" && !!form.allDay )? User.preferences.dateFormat+" HH:mm" : User.preferences.dateFormat + " " + User.preferences.hourFormat;
+
+	var date = Date.parseExact( form.startDate + (!!form.allDay ? " 00:00": " "+$.trim(form.startHour)) , formatString ).add( { days: 1 } ).toString('yyyy-MM-dd HH:mm:00');
+	
+	patati['startTime'] = date.toString('yyyy-MM-dd HH:mm:00');
+	
+	if( !patati['byday'] )
+	      switch(form.frequency) {
+	      case 'weekly':      
+		      break;
+	      case 'daily':
+		      break;
+	      case 'monthly':
+		      patati['bymonthday'] = date.getDate();
+		      break;
+	      case 'yearly':
+		      patati['byyearday'] = date.getDayOfYear();
+		      break;
+	      default :
+		      return patati;
+	      }
+
+	if (($(".endRepeat").val() == 'occurrences')) 
+		patati['count'] = $(".occurrencesEnd").val(); 
+	
+	if (($(".endRepeat").val() == 'customDate'))
+		patati['endTime'] = Date.parseExact( $(".customDateEnd").val() + (!!form.allDay ? " 00:00": " "+$.trim(form.endHour)) , formatString ).toString('yyyy-MM-dd HH:mm:00');
+	
+	patati['interval']  = $(".eventInterval").val();
+
+	/**
+	wkst = [ 'MO', 'TU', 'WE', 'TH', 'FR', 'SA', 'SU' ]
+	weekno = number
+	minute = number
+	second = number
+	yearday = number
+	monthday = number
+	setpos = number
+	day = number
+	hour = number
+	interval = number
+	frequency = [ 'monthly', 'daily', 'weekly', 'yearly', 'hourly', 'minutely', 'secondly' ]
+	endTime = milliseconds
+    */	  
+    return( patati );
+      
+}
+
+
+
+function encodeRepeat( repeat ){
+  
+	if( !repeat )
+	    return( false );
+
+	return DataLayer.get( 'repeat', repeat );
+}
 
 DataLayer.codec( "schedulable", "detail", {
@@ -674,5 +754,5 @@
 				  startTime: Date.parseExact(form.startDate + (!!form.allDay ? " 00:00": " "+$.trim(form.startHour)) , formatString ).toString('yyyy-MM-dd HH:mm:00'),
 				  endTime:  Date.parseExact(form.endDate + ( !!form.allDay ? " 00:00": " "+$.trim(form.endHour)), formatString ).toString('yyyy-MM-dd HH:mm:00'),  //+ (!!form.allDay ? 86400000 : 0) ,
-				  allDay: ( !!form.allDay ? 1 : 0 ),
+				  allDay: ( !!form.allDay ? 1 : 0 ),       
 			      id: form.idEvent,
 			      location: form.location,
@@ -685,4 +765,5 @@
 						return {attachment: attachment}
 				  }),
+			      repeat: this.decodeRepeat( form ),
 			      participants: $.map( form.attendee || [], function( attendee, i ){
 
@@ -722,45 +803,61 @@
     },
 
+
     encoder: function( evtObj ){
 	
-	if(!(!!evtObj))
-		return undefined;
-
-	var notArray = false;
-      
-	if( notArray = $.type(evtObj) !== "array" )
-	    evtObj = [ evtObj ];
-
-	var pref = User.preferences;
-
-	var res =  DataLayer.encode('schedulable:preview', evtObj);
-
-	if( !notArray ){
-	    var dates = {};
-
-	    for (var i=0; i < res.length; i++) {
-
-		    var startDate = Date.parseExact( res[i]['startDate'], User.preferences.dateFormat );
-		    var endDate   = Date.parseExact( res[i]['endDate'], User.preferences.dateFormat );
-
-		    var counter   = startDate.clone();
-
-		    while (counter.compareTo(startDate) == 0) {
-
-			    if (!dates[counter.toString(User.preferences.dateFormat)]) 
-					dates[counter.toString(User.preferences.dateFormat)] = { startDate:false, events:[] };
-			    if (!dates[counter.toString(User.preferences.dateFormat)].startDate) 
-					dates[counter.toString(User.preferences.dateFormat)].startDate = counter.toString(User.preferences.dateFormat);
-
-			    dates[counter.toString(User.preferences.dateFormat)].events.push(res[i]);
-			    counter.addDays(-1);
-		    } 
-	    }
-	    res = { events_list: dates,
-				count : res.length
-			};
-	}
-
-	return notArray ? res[0] : res;
+		if(!(!!evtObj))
+			return undefined;
+
+		var notArray = false;
+		  
+		if( notArray = $.type(evtObj) !== "array" )
+			evtObj = [ evtObj ];
+
+		var pref = User.preferences;
+
+		var res =  DataLayer.encode('schedulable:preview', evtObj);
+
+		if( !notArray ){
+			var dates = {};
+
+			for (var i=0; i < res.length; i++) {
+			  
+				var startDate = Date.parseExact( res[i]['startDate'], User.preferences.dateFormat );
+				var endDate   = Date.parseExact( res[i]['endDate'], User.preferences.dateFormat );
+			  
+				var duration = parseInt( endDate.getTime() ) - parseInt( startDate.getTime() );
+		
+				var occurrences = [ startDate.getTime() ];
+				
+				if( res[i].occurrences )
+				{
+				    occurrences = res[i].occurrences;
+				}
+
+				for( var ii = 0; ii < occurrences.length; ii++ )
+				{
+				    var currentDate = new Date( occurrences[ii] );
+				    var counter = currentDate.clone();
+				    
+				    var res2 = $.extend( {}, res[i], {'startDate': currentDate.toString( User.preferences.dateFormat ), 'endDate': new Date( occurrences[ii] + duration ).toString( User.preferences.dateFormat ) } );
+				  
+				    while (counter.compareTo( currentDate ) == 0) {
+
+					    if (!dates[counter.toString(User.preferences.dateFormat)]) 
+						    dates[counter.toString(User.preferences.dateFormat)] = { startDate:false, events:[] };
+					    if (!dates[counter.toString(User.preferences.dateFormat)].startDate) 
+						    dates[counter.toString(User.preferences.dateFormat)].startDate = counter.toString(User.preferences.dateFormat);
+
+					    dates[counter.toString(User.preferences.dateFormat)].events.push(res2);
+					    counter.addDays(-1);
+				    }      
+				}
+			}
+			res = { events_list: dates,
+					count : res.length
+				};
+		}
+
+		return notArray ? res[0] : res;
     }
 
@@ -823,89 +920,6 @@
 	    return( false );
 
-//       if( notArray = $.type(evtObj) !== "array" )
-// 	  evtObj = [ evtObj ];
-// 	alert( evtObj );
 	var filtered = evtObj;
-	
-	// repeat:{
-// 	  frequency: "weekly",
-// 	  endTime: Date.parse("+10 days").getTime(),
-// 	  interval: 3,
-// 	  byday: 3,
-// 	  bysecond: 3,
-// 	  byminute: 3,
-// 	  bymonthday: 3,
-// 	  byyearday: 3,
-// 	  byweekno: 3,
-// 	  bysetpos: 3,
-// 	  wkst: "monday"/*,
-// 	  duration: true*/
-//       }
-
-// 	$.each(evtObj, function( index, evt ){
-
-// 		alert( dump(!!evt.repeat) );
-
-// 		if( evt && !!evt.repeat && (evt.startTime < filter.end || (evt.repeat.endTime || 0) > filter.start ) )
-// 		{
-// // 		    var interval = evt.repeat.interval;
 // 
-// 		    var current = new Date( evt.startTime );
-// 		    var begin = new Date( filter.start );
-// 		    var end = new Date( filter.end );
-// 		    var offset = evt.endTime - evt.startTime;
-// 
-// 		    var ocurrence = [];
-// 
-// 		    while( Date.compare( current, begin ) !== 1 )
-// 			current = current.parse( "+" + evt.repeat.interval + " " + frequency + "s" );
-// 
-// 		    while( current.before( end ) && (ocurrence[ ocurrence.length ] = current) )
-// 			current = current.parse( "+" + evt.repeat.interval + " " + frequency + "s" );
-// 
-// 		    for( var i = 0; i < ocurrences.length; i++ )
-// 			filtered[ filtered.length ] = { URI: evt.URI,
-// 							summary: evt.summary,
-// 							start: new Date( ocurrences[i] ),
-// 							end: new Date( ocurrences[i] + offset ),
-// 							allDay: evt.allDay };
-// 
-// // 		    switch( evt.repeat.frequency )
-// // 		    {
-// // 		      case "yearly":   interval *= 12;
-// // 		      case "monthly":  interval *= 30; //TODO: get the month variant
-// // 		      case "weekly":   interval *= 7;
-// // 		      case "daily":    interval *= 24;
-// // 		      case "hourly":   interval *= 60;
-// // 		      case "minutely": interval *= 60; 
-// // 		      case "secondly": interval *= 1000;
-// // 		    }
-// // 		  
-// // 		    var rest = ( filter.start - evt.startTime ) % interval;
-// // 
-// // 		    var offset = evt.endTime - evt.startTime;
-// // 
-// // 		    if( rest < offset )
-// // 		    {
-// // 			var freq = Math.floor( offset - rest / interval );
-// // 
-// // 			
-// // 
-// // 			for( var i = 0; i < freq; i++ )
-// // 			{
-// // 			     var ocurrence = filter.start + rest + ( interval * i );
-// // 
-// // 			     
-// // 			}
-// // 
-// // 			
-// // 		    }
-// 		}
-
-// 		if( parseInt( evt.endTime ) > filter[1][0][2] || parseInt( evt.startTime ) < filter[1][1][2] )
-// 		    filtered[ filtered.length ] = evt;
-// 
-// 	});
-
 	var grouped = {};
 
@@ -922,5 +936,5 @@
 		var calendar = DataLayer.get('calendar', evt.calendar);
 		
-		var eventEditable = function(idEvent){
+		var eventEditable = function(idEvent, isRecurrence){
 			if(Calendar.signatureOf[calendar.id].isOwner == "1"){
 				var attendee = {};
@@ -928,7 +942,7 @@
 					attendee = DataLayer.get('participant', evt.participants[i]);
 						if(attendee.user == User.me.id)
-							return (attendee.acl.indexOf('w') >= 0 || attendee.acl.indexOf('o') >= 0 ) ? {selectable: true, className: 'fullcalendar-context-menu  event-id-'+idEvent} : {editable: false, selectable: true ,className: 'blocked-event-permision  fullcalendar-not-context-menu event-id-'+idEvent};
+							return (attendee.acl.indexOf('w') >= 0 || attendee.acl.indexOf('o') >= 0 ) ? {selectable: true, className: 'fullcalendar-context-menu  event-id-'+idEvent, editable: !isRecurrence} : { editable: false, selectable: true ,className: 'blocked-event-permision  fullcalendar-not-context-menu event-id-'+idEvent};
 				}
-				return {editable: false, className: 'blocked-event-permision  fullcalendar-not-context-menu event-id-'+idEvent,  selectable: true};
+				return {editable: false, className: 'blocked-event-permision  fullcalendar-not-context-menu event-id-'+idEvent,  selectable: true, disableDragging: isRecurrence };
 			}
 			else{
@@ -943,5 +957,5 @@
 				}				
 				return DataLayer.merge({
-					editable: aclSignature.acl['write'],
+					editable: aclSignature.acl['write'] && !isRecurrence,
 					disableResizing : (((aclSignature.acl['busy'] && !aclSignature.acl['write']) || (!aclSignature.acl['write'] && aclSignature.acl['read'])) ? true : false),
 					disableDragging  : (((aclSignature.acl['busy'] && !aclSignature.acl['write']) || (!aclSignature.acl['write'] && aclSignature.acl['read'])) ? true: false),
@@ -951,10 +965,21 @@
 			
 		}
+		
+		var duration = parseInt( evt.endTime ) - parseInt( evt.startTime ), isRepeat = false;
+		
+		var occurrences = [ evt.startTime ];
+		
+		if( evt.occurrences )
+		{
+		    isRepeat = true;
+		    occurrences = evt.occurrences;
+		}
 		  
-		grouped[ evt.calendar ].push( DataLayer.merge({ id: evt.URI || evt.id,
-						title: Encoder.htmlDecode(evt.summary),
-						start: Timezone.getDateCalendar(new Date(parseInt( evt.startTime )), calendar.timezone,  !!evt.DayLigth ? evt.DayLigth.calendar.startTime : Timezone.daylightSaving),
-						end:   Timezone.getDateCalendar(new Date(parseInt( evt.endTime ) - (!!parseInt(evt.allDay) ? 86400000 : 0)), calendar.timezone, !!evt.DayLigth ? evt.DayLigth.calendar.startTime : Timezone.daylightSaving),
-						allDay: parseInt( evt.allDay ) }, eventEditable(evt.id) ) );
+		for( var i = 0; i < occurrences.length; i++ )
+		    grouped[ evt.calendar ].push( DataLayer.merge({ id: evt.URI || evt.id,
+						    title: Encoder.htmlDecode(evt.summary),
+						    start: Timezone.getDateCalendar(new Date( parseInt( occurrences[i] ) ), calendar.timezone,  !!evt.DayLigth ? evt.DayLigth.calendar.startTime : Timezone.daylightSaving),
+						    end:   Timezone.getDateCalendar(new Date( parseInt( occurrences[i] ) + duration - (!!parseInt(evt.allDay) ? 86400000 : 0)), calendar.timezone, !!evt.DayLigth ? evt.DayLigth.calendar.startTime : Timezone.daylightSaving),
+						    allDay: parseInt( evt.allDay ) }, eventEditable(evt.id, isRepeat ) ) );
 	});
 
Index: trunk/prototype/modules/calendar/js/helpers.js
===================================================================
--- trunk/prototype/modules/calendar/js/helpers.js	(revision 5641)
+++ trunk/prototype/modules/calendar/js/helpers.js	(revision 5715)
@@ -361,5 +361,161 @@
 			
 		}
-		
+
+	    DataLayer.render( 'templates/event_repeat.ejs', {event:objEvent}, function( repeatHtml ){
+
+			UI.dialogs.addEvent.find('#calendar_addevent_details3').html(repeatHtml);
+			$(".date").datepicker();
+		  
+			if(objEvent.repeat) 
+			{
+			     if( objEvent.repeat['id'] )
+			     {
+				$("[name='repeatId']:last").val( objEvent.repeat['id'] );
+			     }
+
+			     if( objEvent.repeat['frequency'] !== 'none' )
+			     {
+				if( objEvent.repeat['startTime'] && objEvent.repeat['startTime'] !== "0" )
+				{
+				      $("[name='startOptions'] [value='customDate']:last").attr( 'selected', 'selected' );
+				      $("[name='start']:last").val(new Date( parseInt(objEvent.repeat['startTime']) ).toString( User.preferences.dateFormat ) );
+				}
+				else
+				{
+				      $("[name='start']:last").val($("[name='startDate']:last").val());     
+				      $("[name='start']:last").readOnly=true;
+				      $("[name='start']:last").datepicker("disable");
+				}
+			      
+				$(".finish_event").removeClass("hidden");
+
+				if(objEvent.repeat['endTime'] && objEvent.repeat['endTime'] !== "0" ) 
+				{
+					//$("[name='occurrences']").addClass("hidden");
+					$(".customDateEnd").removeClass("hidden");
+							$(".endRepeat option[value='customDate']").attr('selected', 'selected')						
+					$(".customDateEnd").val( new Date( parseInt(objEvent.repeat['endTime']) )/*.setTimezoneOffset( Timezone.timezone( objEvent.timezone ) )*/.toString( User.preferences.dateFormat ) );  
+				}
+				else if (objEvent.repeat['count'] && objEvent.repeat['count'] !== "0" ) {
+					$(".endRepeat option[value='occurrences']").attr('selected', 'selected');						
+					$(".occurrencesEnd").removeClass("hidden");
+					$(".occurrencesEnd").val(objEvent.repeat['count']);						
+				}
+			      
+				switch ( objEvent.repeat['frequency'] )
+				{
+				    case "daily":
+					    $(".event-repeat-container:last").find(".repeat-in").find(".interval").html("Dia(s)");
+					    $(".frequency option[value='daily']").attr('selected', 'selected');
+					    break;
+				    case "weekly":
+					    $(".event-repeat-container:last").find(".repeat-in").find(".interval").html("Semana(s)");
+					    $(".frequency option[value='weekly']").attr('selected', 'selected');
+					    objEvent.repeat['interval'] ? $(".event-repeat-container:last").find(".repeat-in").find(".eventInterval").val(objEvent.repeat['interval']) : $(".event-repeat-container:last").find(".repeat-in").find(".eventInterval").val("0");
+					    
+					    $(".event-repeat-weekly").removeClass("hidden");
+					    
+					    var day = [];
+					    
+					    if( objEvent.repeat.byday )
+						day = objEvent.repeat.byday.split(',');
+					    
+					    for(i=0; i<day.length; i++) 
+						    $(".event-repeat-weekly [value='" + day[i] + "']").attr("checked","checked");
+					    
+					    break;
+				    case "monthly":
+					    $(".event-repeat-container:last").find(".repeat-in").find(".interval").html("Mes(s)");
+					    $(".frequency option[value='monthly']").attr('selected', 'selected')
+					    $(".event-repeat-monthly:last").removeClass("hidden").find("input[type=radio][name=repeatmonthyType]").click(function(){
+						    if($("input[type=radio][name=repeatmonthyType]:checked").val() == "1")
+							    $(".event-repeat-weekly:last").removeClass("hidden");
+						    else
+							    $(".event-repeat-weekly:last").addClass("hidden");
+					    });
+					    if($("input[type=radio][name=repeatmonthyType]:checked").val() == "1")
+						    $(".event-repeat-weekly:last").removeClass("hidden");
+					    else
+						    $(".event-repeat-weekly:last").addClass("hidden");
+					    break;
+				    case "yearly":
+					    $(".event-repeat-container:last").find(".repeat-in").find(".interval").html("Ano(s)");
+					    $(".frequency option[value='yearly']").attr('selected', 'selected')
+					    break;	
+				}
+			    }
+			}
+			else {
+				$(".endRepeat option[value='never']").attr('selected', 'selected');
+			}
+
+
+			$(".event-repeat-container:last").find(".repeat-in").find("[name=startOptions]").change(function(){                                       
+
+            if($(this).find("option:selected").val() == "Today"){
+                                $("[name='start']:last").val($("[name='startDate']:last").val());
+                                $("[name='start']:last").readOnly=true;
+                                $("[name='start']:last").datepicker("disable");
+                            }
+                            else{
+                                $("[name='start']:last").readOnly=false;
+                                $("[name='start']:last").datepicker("enable");
+                            }
+                        });
+                        $(".event-repeat-container:last").find(".repeat-in").find("[name=endOptions]").change(function(){                                       
+                            if($(this).find("option:selected").val() == "never"){
+                                $("[name='occurrences']").addClass("hidden");
+                                $("[name='end']:last").addClass("hidden");
+                            }
+                            else if($(this).find("option:selected").val() == "customDate"){
+                                $("[name='occurrences']").addClass("hidden");
+                                $("[name='end']:last").removeClass("hidden");    
+                            }
+                            else{
+                                $("[name='end']:last").addClass("hidden");
+                                $("[name='occurrences']").removeClass("hidden");                                        
+                            }
+                        });
+                        
+			$("[name='frequency']:last").change(function () {
+				$(".frequency-option").addClass("hidden");
+                                if($(this).val() == "none"){
+					$(".repeat-in").addClass("hidden");
+					return;
+				}else{
+                                    $(".repeat-in").removeClass("hidden");
+                                    $("[name='start']:last").val($("[name='startDate']:last").val());
+                                }
+                 
+				 
+				switch($(this).val()){
+                    case "daily":
+                        $(".event-repeat-container:last").find(".repeat-in").find(".interval").html("Dia(s)");
+                        break;
+					case "weekly":
+                        $(".event-repeat-container:last").find(".repeat-in").find(".interval").html("Semana(s)");
+						$(".event-repeat-weekly:last").removeClass("hidden");
+						break;
+					case "monthly":
+                        $(".event-repeat-container:last").find(".repeat-in").find(".interval").html("Mes(s)");
+						$(".event-repeat-monthly:last").removeClass("hidden").find("input[type=radio][name=repeatmonthyType]").click(function(){
+							if($("input[type=radio][name=repeatmonthyType]:checked").val() == "1")
+								$(".event-repeat-weekly:last").removeClass("hidden");
+							else
+								$(".event-repeat-weekly:last").addClass("hidden");
+						});
+                        if($("input[type=radio][name=repeatmonthyType]:checked").val() == "1")
+                            $(".event-repeat-weekly:last").removeClass("hidden");
+						else
+                            $(".event-repeat-weekly:last").addClass("hidden");
+					    break;
+                    default:
+                        $(".event-repeat-container:last").find(".repeat-in").find(".interval").html("Ano(s)");
+                        break;
+				}
+				
+			});
+		});
+
 	    UI.dialogs.addEvent.find('.calendar_addevent_details_lnk_timezone').click(function(e){
 		    $(this).addClass('hidden');
@@ -627,5 +783,5 @@
 					var user = DataLayer.get('user', ["=", "mail", $(this).val()]);
 					if(!!user && user[0].id)
-						attendees[user[0].id].name  = user[0].name;
+						attendees[user[0].id] = { name: user[0].name };
 					
 					/**
