Tek-Tips is the largest IT community on the Internet today!

Members share and learn making Tek-Tips Forums the best source of peer-reviewed technical information on the Internet!

  • Congratulations SkipVought on being selected by the Tek-Tips community for having the most helpful posts in the forums last week. Way to Go!

jQuery fullCalndar Alignment and Holidays

Status
Not open for further replies.

PCHomepage

Programmer
Feb 24, 2009
609
1
16
US
Since I'm not sure if this is a JavaScript or CSS question, I am posting here because it is a JavaScript plug-in. On fullCalendar, I need to move the day numbers to the upper left when they seem to default to the upper right but so far I've not had any luck. I found numerous postings through Google that suggest various CSS but they either do nothing or they bunch up all the dates to the left on top of one another. I would also like the day row to have its own border.

Also, I need the calendar to automatically show certain holidays but not necessarily all of them. Is this possible?

Here is my current code, which is using PHP to get calendar events from the database:

Code:
<script>
	$(document).ready(function() {
		$('#calendar').fullCalendar({
		weekMode: 'variable',
			header: {
				right: 'prev,next',
				left: 'title'
			},
			firstDay: 1, // Starts week on Monday
			handleWindowResize: true,
			selectable: false,
			selectHelper: true,
			select: function(start, end) {
				var title = prompt('Event Title:');
				var eventData;
				if (title) {
					eventData = {
						title: title,
						start: start,
						end: end
					};
					$('#calendar').fullCalendar('renderEvent', eventData, true); // stick? = true
				}
				$('#calendar').fullCalendar('unselect');
			},
			editable: true,			
			eventLimit: true, // allow "more" link when too many events
			
			
			eventRender: function(event, element, view) {
                  element.bind('click', function()  {
                         var day = ($.fullCalendar.formatDate( event.start, 'dd' ));
                         var month = ($.fullCalendar.formatDate( event.start, 'MM' ));
                         var year = ($.fullCalendar.formatDate( event.start, 'yyyy' ));
                          alert(year+'-'+month+'-'+day);
                         });
                   },
			
			
			events:
				[
					<?php
					$n = 0;
					for ($i=0;$i<=count($rowCat)-1;$i++) :
						$EntryID = $rowCat[$i]["ID"];
						$StartTime = strtotime(trim($rowCat[$i]["Start"]));
						$EndTime = strtotime(trim($rowCat[$i]["End"]));
						$DisplayName = trim($rowCat[$i]["StaffName"]);
						echo "{\ntitle: '$DisplayName',\n";
						if (!$rowCat[$i]["FirstName"] && !$rowCat[$i]["LastName"]) :
							$FormattedStart = date("Y-m-d", $StartTime);
							$FormattedEnd = ($EndTime > $StartTime) ? date('Y-m-d', strtotime('+1 day', $EndTime)): date("Y-m-d", $EndTime);
							echo "start: '$FormattedStart',\n";
							echo "end: '$FormattedEnd'";
							if (isset($_SESSION['AccessLevel']) && $_SESSION['AccessLevel'] > 1) :
								echo ",\n";
								echo "url: '/administration/events_admin.php?ID=$EntryID',";
							endif;
							echo "className: 'specialevent'\n";
							echo "}\n";
						else :
							$FormattedStart = date("Y-m-d\TH:i:s", $StartTime);
							$FormattedEnd = date("Y-m-d\TH:i:s", $EndTime);
							echo "start: '$FormattedStart',\n";
							echo "end: '$FormattedEnd'";
							if (isset($_SESSION['AccessLevel']) && $_SESSION['AccessLevel'] > 1) :
								echo ",\n";
								echo "url: '/administration/calendar_admin.php?ID=$EntryID',";
							endif;
							echo "className: 'calendarevent'\n";
							echo "}\n";
						endif;
						if(++$n !== $numRows) :
							echo ",\n\n";
						endif;
					endfor;
					?>
				],
				timeFormat: 'hh:mmt',
		});
		<?php
		if (isset($_COOKIE['CalendarDate'])) :
			$datePieces = explode('-', $_SESSION['CalendarDate']);
			$DateYear = $datePieces[0];
			$DateMonth = $datePieces[1];?>
			//$('#calendar').fullCalendar('gotoDate', '<?=$DateYear?>-<?=$DateMonth?>-01');
		<?php endif;?>
		});
</script>
 
i would add holiday as events and then specify that event as non-editable in the event object. you could also do so via a callback.

i think i'd also simplify the php output for the events and just push a json object rather than building the object manually.

i'd probably not bind the click event within with eventRenderer. is there a good reason why you are doing that rather than using the built in method (dayClick)? from memory, the jQuery method 'bind' is now deprecated too - use the on() method.

if you want to distinguish different classes of event (i.e. holiday from a booking) then you can use eventRenderer to do so.

Code:
eventRenderer: function (ev, elem, v){
  if(ev.description.toLowerCase.indexOf('holiday') >= 0){
    //event is a holiday
    elem.css({'background-color': "#08088A", color: "#EFFBFB"});
  }
},

 
for the day number issue, the easiest solution is to change the .fc-day-number text alignment in the css file.

or if you want to do this programmatically, adding this code to the calendar options should do the trick (not tested)
Code:
viewRender: function(view, element){
  $('.fc-day-number').css('text-align', 'left');
},
windowResize: function(view){
 $('.fc-day-number').css('text-align', 'left');
},

as above, this code is not tested and I am not familiar with fullCalendar. this is just from five minutes with the docs and a quick view of the underlying source code of a rendered calendar.
 
I am not sure what you mean by day row. but if you mean that you want a line under the day number then just apply a bottom border to the .fc-day-number class; either programmatically (beware of the frequent refreshes though) or in the css directly.

you also wanted to 'hide' days that were from a previous month. you can do this a number of ways. the easiest would be to prevent the event rendering for those days. to get rid of the dayNumber you can adjust the css for the fc-other-month class. again, either programmatically or in the css directly.
Code:
viewRender: function(){$('.fc-other-month').css('color','white');});


 
Doing it programmatically, the day is now to the left (!) but I was unable to hide the events of other months from showing.

For day "row", I meant that the day is on a line by itself and I want a horizontal line under that line to separate the date from the events. It's not too important but I am trying to emulate an existing hard-copy calendar in layout.

Also, is there some way to get whatever the current year and month is and save it as a PHP session? I have a separate printable version but it always reverts back to the current month so I need a way to query only the events from whichever month was being viewed. If there is a way, this would also solve the part of the events showing from other months because, getting the events from MySQL, it could just query the current month.
 
By 'the day is now to the left' does this mean that you have achieved what you want?

For the bottom border my preference is that you edit the CSS directly. If you insist on doing it programmatically add the CSS command inside the viewRender event.

For a printable version it is almost certain my better to use Pdf. Considee using WebKit and html2pdf to create the PSF. This supports JavaScript abs ime gives the most faithful rendition of a webpage.
 
Yes, thank you, the day is now to the left and I don't care if it's CSS or done programmatically but either way, I've not been able to locate any references to the line creation below the date.

On the print version, whatever I use I need to remove the page's header and footer first which is why I was using a second printable page. It does want to print oddly with dates with fewer entries scrunched down and fuller dates very large. In fact, some are a bit too large so any printable version would need the font size reduced a bit.

However, you might be right that using html2pdf would be better. I don't know how well fullCalendar can handle it, nor even how to do it since I've never needed to print to PDF.
 
fullCalendar will never know of html2pdf. the way it works is to launch an internal browser with full js capability and render the page internally.

if you want bits to be non-printing then simply put a style sheet in and add a @media print declaration with visibility set to hidden.

when you say that you have not been able to locate any reference to the line below the date, are you saying that the solution I offered does not work for you? It does work for me. this for example:

Code:
viewRender: function(view, element){
  $('.fc-day-number').css({'text-align':'left', borderBottom: "thin solid black"});
  $('.fc-other-month').css('color','white'); 
},
windowResize: function(view){
  $('.fc-day-number').css({'text-align':'left', borderBottom: "thin solid black"});
  $('.fc-other-month').css('color','white');
},
 
Scrolling through the earlier posts, I do not see this code so this is the first I've seen it. I'll try it in the morning so thank you for that. The other code you sent for suppressing days from other months does not work but I was away for most of the day and haven't researched it further so see why.
 
i had a spare ten minutes so created a dummy version. originally as a jsFiddle but i realised you don't get full functionality with that. so here's the script in raw. it is entirely self contained: just copy it to a file and point your browser at it (apart from the ajax elements - see later).

Code:
<!DOCTYPE HTML>
	<head>
		<title>Calendar test</title>
		 <meta charset="utf-8">
		<link rel="stylesheet" href="//code.jquery.com/ui/1.11.2/themes/smoothness/jquery-ui.css">
		<link rel="stylesheet" href="[URL unfurl="true"]http://fullcalendar.io/css/base.css?2.1.1">[/URL]
		<link rel="stylesheet" href="[URL unfurl="true"]http://fullcalendar.io/js/fullcalendar-2.1.1/fullcalendar.css">[/URL]
		<style>
.holiday{background-color: red}
.normalEvent{background-color:green}
#dialog .formRow{clear:left; overflow-y:hidden; }
#dialog .formRow .label {float:left; width: 5em;}
#dialog .formRow .formData {float:left: width: 12em;}
#dialog .formRow .formData input{width: 10em;}
#dialog, #dialog select, #dialog input{
	font-family: "Calibri";
	font-size: 0.8em;
}
#dialog{
	width: 20em;
}
h1{
	color:white;
	margin-bottom: 1em;
	background-color: #444;
}
		</style>
		<script src="//code.jquery.com/jquery-1.10.2.js"></script>
		<script src="//code.jquery.com/ui/1.11.2/jquery-ui.js"></script>
		<script src="[URL unfurl="true"]http://fullcalendar.io//js/fullcalendar-2.1.1/lib/moment.min.js"></script>[/URL]
		<script src="[URL unfurl="true"]http://fullcalendar.io//js/fullcalendar-2.1.1/fullcalendar.min.js"></script>[/URL]
		<script>
var curEvent;
$(document).ready(function(){
	var ajaxServer = '[URL unfurl="true"]http://rathercurious.net/fullCalendarAjaxServer.php';[/URL]
	//extend the prototype
	$.fn.addOption = function(optText, optValue){
        var option = new Option(optText, optValue);
        return this.append(option);
	}
	var opts = {
        select: function(start, end) {
			var el = $('#dialog');
			var s = fTime(start.hour(), start.minutes());
			var e = fTime(end.hour(), end.minutes());
			var d = fDate(start);
			if(s == '00:00') s = "09:30";
			if(e == '00:00') e = "17:30";
            $('#startTime').val(s);
            $('#endTime').val(e);
            $('#eventDate').val(d);
			$('#id').val(' ');
			$('#eventTitle').val('');
            el.dialog('open');
		},
        selectHelper: true,
        weekMode: 'variable',
		header: {
				right: 'prev,next,month,basicWeek,basicDay',
				left: 'title'
			},
		firstDay: 1, // Starts week on Monday
		handleWindowResize: true,
		selectable: true,
		editable: true,			
		eventLimit: true,
        timeFormat: 'hh:mmt',
        viewRender: function(view, element){
            $('.fc-day-number').css({
                    'text-align':'left', 
                    borderBottom: "thin solid black"});
            $('.fc-other-month').css('color','white'); 
        },
        windowResize: function(view){
            $('.fc-day-number').css({
                   'text-align':'left', 
                    borderBottom: "thin solid black"});
            $('.fc-other-month').css('color','white');
        },
		eventClick: function(event, jsEvent, view){
			$('#eventDate').val(fDate(event.start));
			$('#startTime').val(fTime(event.start.hour(), event.start.minutes()));
			$('#endTime').val(fTime(event.end.hour(), event.end.minutes()));
			$('#id').val(event.id);
			$('#eventTitle').val(event.title);
			$('#editable').val(event.editable);
			$('#dialog').dialog('open');
		},
		eventDrop: function(event, delta, revertFunc, jsEvent, ui, view){
			if(!event.start.hasTime()){
				revertFunc();
				return;
			}
			var eventData = {	id: event.id,
						start: event.start.format(),
						end: event.end.format(),
						title: event.title,
						allDay: event.allDay,
						editable: event.editable,
						action: 'updateEventDate'
					};
			$.ajax({
				url: ajaxServer,
				type: 'POST',
				dataType: 'json',
				success: function(data){
					if(data.result != 'ok'){
						revertFunc();
					}
				},
				data: eventData
			});
		},
        events: [
            {
                id: 1,
                title: 'default holiday',
                allDay: false,
                start: '2014-11-06T09:00-08:00',
                end:'2014-11-06T18:00-08:00',
                className:'holiday',
                editable: false
            },
        {
                id: 2,
                title: 'default event',
                allDay: false,
                start: '2014-11-07T09:00-08:00',
                end:'2014-11-07T18:00-08:00',
                className:'normalEvent',
                editable: true
        }
            ]
};	//end of options
    $('#calendar').fullCalendar(opts);
    for(var i = 8; i<=17; i++){
        $('#startTime').addOption(fTime(i,0), fTime(i,0));
        $('#startTime').addOption(fTime(i,30), fTime(i,30));
    }
    for (var i = 17; i<=24; i++){
        $('#endTime').addOption(fTime(i,0), fTime(i,0));
        $('#endTime').addOption(fTime(i,30), fTime(i,30));
    }
    function fTime(h, m){
        h = h==24 ? 0 : h;
        h = h < 10 ? '0' + h: h;
        m = m <10 ? '0' + m : m;
        return h + ':' + m;
    }
    function fDate(i){
        var d = i.date() < 10 ? '0' + i.date() : i.date();
        var m = i.month() < 9 ? '0' + (i.month +1) : i.month() + 1;
        return i.year() + '-' + m + '-' + d;
     }
     
    $('#eventSave').on('click', function(e){
        e.preventDefault();
        var eventData = {
            start: $('#eventDate').val() + 'T' + $('#startTime').val(),
            end: $('#eventDate').val() + 'T' + $('#endTime').val(),
            title: $('#eventTitle').val(),
			id:$('#id').val(),
            allDay: false,
			action: 'saveEvent',
			editable: $('#editable').val()
       };
	   $.ajax({
	   		url: 'ajaxServer',
			type: 'POST',
			dataType: 'json',
			success: function(data){
				$('#calendar').fullCalendar('removeEvents', data.id);
				$('#calendar').fullCalendar('renderEvent', data);
				$('#dialog').dialog('close');
			},
			data: eventData
	   });
    });
	$('#dialog').dialog({ autoOpen: false, closeOnEscape: false, buttons: [], modal: true});
	
});
		</script>
		
	</head>
	<body>

<h1>Don's Calendar Fiddle</h1>
<div id="calendar"></div>
<div id="dialog">
    <form>
        <div class="formRow">
            <div class="label">Event Title</div>
            <div class="formData"><input name="eventTitle" id="eventTitle" /></div>
        </div>
        <div class="formRow">
            <div class="label">Start Time:</div>
            <div class="formData"><select id="startTime"></select></div>
        </div>
        <div class="formRow">
            <div class="label">End Time:</div>
            <div class="formData"><select id="endTime"></select></div>
        </div>
        <div class="formRow">
            <div class="label">&nbsp;</div>
            <div class="formData"><input type="button" id="eventSave" value="Save" />
			<input id="eventDate" type="hidden"/>
			<input id="editable" type="hidden"/>
			<input id="id" type="hidden" name="id" />
			</div>
        </div>
    </form>
</div>
	</body>

the above uses ajax to send the updated event data to a server. here is a test php script to use for receiving and handling the data. you can easily change it to provide for proper saving and error management.

Code:
<?php
class base{
	public function load($data){
		foreach($this->fields as $field):
			if(isset($data[$field])):
				$this->$field = $data[$field];
			else:
				$this->$field = '';
			endif;
		endforeach;
	}
} 
class event extends base{
	public $fields = array('id', 'title', 'start', 'end', 'allDay', 'url', 'className', 'editable');
	public function save(){
		//save the incoming information	
		if(empty($this->id)):
			$this->id = rand(1,1000000);
		endif;
		if(strpos( strtolower($this->title), 'holiday') !== FALSE):
			$this->className = 'holiday';
		else:
			$this->className = 'normalEvent';
		endif;
		$this->editable = empty($this->editable) ? false: true;
		$this->allDay = empty($this->allDay) ? false : true;
		//if save was ok
		return true;
	}
	public function getObject(){
		date_default_timezone_set('America/Los_Angeles');
		$array = array(); 
		foreach($this->fields as $field):
			if($field == 'start' || $field == 'end'):
				$array[$field] = date("Y-m-d\TH:i:sO",strtotime($this->$field));
			else:
				$array[$field] = $this->$field;
			endif;
		endforeach;
		return json_encode($array);
	}
	
}
if(isset($_POST['action'])):
	switch ($_POST['action']):
		case 'saveEvent'://save the event
		//send it back with an ID
		$e = new event;
		$e->load($_POST);
		$e->save();
		echo $e->getObject();
		die;
		break;
		case 'updateEventDate':
			$e=new event;
			$e->load($_POST);
			$result = $e->save();
			echo json_encode(array('result'=> ($result) ? 'ok' : 'error'));
		die;
	endswitch;
endif;		
?>
 
i should add that the code above supports editing of the events, drag and drop of events and also creation of events by clicking on a day.

inevitably there are already a few bugs I have spotted. problem with only spending limited time on it. please use this version instead for testing

Code:
<!DOCTYPE HTML>
	<head>
		<title>Calendar test</title>
		 <meta charset="utf-8">
		<link rel="stylesheet" href="//code.jquery.com/ui/1.11.2/themes/smoothness/jquery-ui.css">
		<link rel="stylesheet" href="[URL unfurl="true"]http://fullcalendar.io//css/base.css?2.1.1">[/URL]
		<link rel="stylesheet" href="[URL unfurl="true"]http://fullcalendar.io//js/fullcalendar-2.1.1/fullcalendar.css">[/URL]
		<style>
.holiday{background-color: red}
.normalEvent{background-color:green}
#dialog .formRow{clear:left; overflow-y:hidden; }
#dialog .formRow .label {float:left; width: 5em;}
#dialog .formRow .formData {float:left: width: 12em;}
#dialog .formRow .formData input{width: 10em;}
#dialog, #dialog select, #dialog input{
	font-family: "Calibri";
	font-size: 0.8em;
}
#dialog{
	width: 20em;
}
h1{
	color:white;
	margin-bottom: 1em;
	background-color: #444;
}
		</style>
		<script src="//code.jquery.com/jquery-1.10.2.js"></script>
		<script src="//code.jquery.com/ui/1.11.2/jquery-ui.js"></script>
		<script src="[URL unfurl="true"]http://fullcalendar.io//js/fullcalendar-2.1.1/lib/moment.min.js"></script>[/URL]
		<script src="[URL unfurl="true"]http://fullcalendar.io//js/fullcalendar-2.1.1/fullcalendar.min.js"></script>[/URL]
		<script>
var curEvent;
$(document).ready(function(){
	var ajaxServer = 'ajaxServer.php';
	//extend the prototype
	$.fn.addOption = function(optText, optValue){
        var option = new Option(optText, optValue);
        return this.append(option);
	}
	var opts = {
        select: function(start, end) {
			var el = $('#dialog');
			var s = fTime(start.hour(), start.minutes());
			var e = fTime(end.hour(), end.minutes());
			var d = fDate(start);
			if(s == '00:00') s = "09:30";
			if(e == '00:00') e = "17:30";
            $('#startTime').val(s);
            $('#endTime').val(e);
            $('#eventDate').val(d);
			$('#id').val(' ');
			$('#eventTitle').val('');
            el.dialog('open');
		},
        selectHelper: true,
        weekMode: 'variable',
		header: {
				right: 'prev,next,month,basicWeek,basicDay',
				left: 'title'
			},
		firstDay: 1, // Starts week on Monday
		handleWindowResize: true,
		selectable: true,
		editable: true,			
		eventLimit: true,
        timeFormat: 'hh:mmt',
        viewRender: function(view, element){
            $('.fc-day-number').css({
                    'text-align':'left', 
                    borderBottom: "thin solid black"});
            $('.fc-other-month').css('color','white'); 
        },
        windowResize: function(view){
            $('.fc-day-number').css({
                   'text-align':'left', 
                    borderBottom: "thin solid black"});
            $('.fc-other-month').css('color','white');
        },
		eventClick: function(event, jsEvent, view){
			$('#eventDate').val(fDate(event.start));
			$('#startTime').val(fTime(event.start.hour(), event.start.minutes()));
			$('#endTime').val(fTime(event.end.hour(), event.end.minutes()));
			$('#id').val(event.id);
			$('#eventTitle').val(event.title);
			$('#editable').val(event.editable);
			$('#dialog').dialog('open');
		},
		eventDrop: function(event, delta, revertFunc, jsEvent, ui, view){
			if(!event.start.hasTime()){
				revertFunc();
				return;
			}
			var eventData = {	id: event.id,
						start: event.start.format(),
						end: event.end.format(),
						title: event.title,
						allDay: event.allDay,
						editable: event.editable,
						action: 'updateEventDate'
					};
			$.ajax({
				url: ajaxServer,
				type: 'POST',
				dataType: 'json',
				success: function(data){
					if(data.result != 'ok'){
						revertFunc();
					}
				},
				data: eventData
			});
		},
        events: [
            {
                id: 1,
                title: 'default holiday',
                allDay: false,
                start: '2014-11-06T09:00-08:00',
                end:'2014-11-06T18:00-08:00',
                className:'holiday',
                editable: false
            },
        {
                id: 2,
                title: 'default event',
                allDay: false,
                start: '2014-11-07T09:00-08:00',
                end:'2014-11-07T18:00-08:00',
                className:'normalEvent',
                editable: true
        }
            ]
};	//end of options
    $('#calendar').fullCalendar(opts);
    for(var i = 8; i<=17; i++){
        $('#startTime').addOption(fTime(i,0), fTime(i,0));
        $('#startTime').addOption(fTime(i,30), fTime(i,30));
    }
    for (var i = 17; i<=24; i++){
        $('#endTime').addOption(fTime(i,0), fTime(i,0));
        $('#endTime').addOption(fTime(i,30), fTime(i,30));
    }
    function fTime(h, m){
        h = h==24 ? 0 : h;
        h = h < 10 ? '0' + h: h;
        m = m <10 ? '0' + m : m;
        return h + ':' + m;
    }
    function fDate(i){
        var d = i.date() < 10 ? '0' + i.date() : i.date();
        var m = i.month() < 9 ? '0' + (i.month +1) : i.month() + 1;
        return i.year() + '-' + m + '-' + d;
     }
    $('#eventSave').closest('form').on('submit', function(e){
		e.preventDefault();
		$('#eventSave').trigger('click');
		
	});
    $('#eventSave').on('click', function(e){
        e.preventDefault();
        var eventData = {
            start: $('#eventDate').val() + 'T' + $('#startTime').val(),
            end: $('#eventDate').val() + 'T' + $('#endTime').val(),
            title: $('#eventTitle').val(),
			id:$('#id').val(),
            allDay: false,
			action: 'saveEvent',
			editable: $('#editable').val()
       };
	   $.ajax({
	   		url: ajaxServer,
			type: 'POST',
			dataType: 'json',
			success: function(data){
				$('#calendar').fullCalendar('removeEvents', data.id);
				$('#calendar').fullCalendar('renderEvent', data);
				$('#dialog').dialog('close');
			},
			data: eventData
	   });
    });
	$('#dialog').dialog({ autoOpen: false, closeOnEscape: false, buttons: [], modal: true});
	
});
		</script>
		
	</head>
	<body>

<h1>Don's Calendar Fiddle</h1>
<div id="calendar"></div>
<div id="dialog">
    <form>
        <div class="formRow">
            <div class="label">Event Title</div>
            <div class="formData"><input name="eventTitle" id="eventTitle" /></div>
        </div>
        <div class="formRow">
            <div class="label">Start Time:</div>
            <div class="formData"><select id="startTime"></select></div>
        </div>
        <div class="formRow">
            <div class="label">End Time:</div>
            <div class="formData"><select id="endTime"></select></div>
        </div>
        <div class="formRow">
            <div class="label">&nbsp;</div>
            <div class="formData"><input type="button" id="eventSave" value="Save" />
			<input id="eventDate" type="hidden"/>
			<input id="editable" type="hidden"/>
			<input id="id" type="hidden" name="id" />
			</div>
        </div>
    </form>
</div>
	</body>

Code:
<?php
class base{
	public function load($data){
		foreach($this->fields as $field):
			if(isset($data[$field])):
				$this->$field = $data[$field];
			else:
				$this->$field = '';
			endif;
		endforeach;
		$this->editable = $this->editable == 'false' ? false : true;
		$this->allDay = $this->allDay == 'false' ? false : true;
	}
} 
class event extends base{
	public $fields = array('id', 'title', 'start', 'end', 'allDay', 'url', 'className', 'editable');
	public function save(){
		//save the incoming information	
		if(empty($this->id)):
			$this->id = rand(1,1000000);
		endif;
		if(strpos( strtolower($this->title), 'holiday') !== FALSE):
			$this->className = 'holiday';
		else:
			$this->className = 'normalEvent';
		endif;
		$this->editable = empty(trim($this->editable)) ? false: true;
		$this->allDay = empty(trim($this->allDay)) ? false : true;
		//if save was ok
		return true;
	}
	public function getObject(){
		date_default_timezone_set('America/Los_Angeles');
		$array = array(); 
		foreach($this->fields as $field):
			if($field == 'start' || $field == 'end'):
				$array[$field] = date("Y-m-d\TH:i:sO",strtotime($this->$field));
			else:
				$array[$field] = $this->$field;
			endif;
		endforeach;
		return json_encode($array);
	}
	
}
if(isset($_POST['action'])):
	switch ($_POST['action']):
		case 'saveEvent'://save the event
		//send it back with an ID
		$e = new event;
		$e->load($_POST);
		$e->save();
		echo $e->getObject();
		die;
		break;
		case 'updateEventDate':
			$e=new event;
			$e->load($_POST);
			$result = $e->save();
			echo json_encode(array('result'=> ($result) ? 'ok' : 'error'));
		die;
	endswitch;
endif;		
?>
 
Thank you for all that. It was a bit of an overkill answer for a simple question but I really appreciate it and am always happy to try new things.

It seems to work but I don't see how to tie it in to my existing database which is required. As it is, it seems to be free-standing without any relation to the rest of the site. The calendar administration form it still not working but I do need to be able to use it.

I noticed you did not use <html> or </html> tags. Are they obsolete now?
 
As an update, I have it tied back into my database but, of course, it still wants to open its own Ajax form momentarily before mine opens but that's easy to correct. It is also still showing events from the previous/next month but I've not attempted to fix that yet. The boxes around the days are also gone and, again, I've not tried to correct it yet. There is too much spacing above and below the calendar that I yet need to adjust.

Print version: I also added styles to make the header and footer not print so now the print version is much better but it does not expand longer entries so some do not print. The font size is fine for display but probably too large for printing onto a single sheet as needed.

External: I moved the styles into an external sheet. Before adding the embedded PHP I tried moving the JavaScript to an external file but it would not work so I moved it back but I think it must be embedded anyway to work in combination with the PHP. Here is what I have:

Code:
<?php
include_once $_SERVER ['DOCUMENT_ROOT'] . "/functions/common.php";
accessRedirect(1);

$query = "SELECT c.ID, StaffID, Start, End, FirstName, LastName, 
		  CONCAT_WS(' ', IF(DisplayName IS NULL,FirstName,DisplayName), LEFT(LastName,1))
		  AS StaffName
  		  FROM calendar c
		  LEFT JOIN staff s ON StaffID = s.ID 
		  ORDER BY Start, DisplayName, FirstName, LastName";
$rowCat = DBConnect($query, "Multiple");
$numRows = DBConnect($query, "Count");

?><!DOCTYPE HTML>
	<head>
		<title>J. Lohr Staff Calendar</title>
		<meta charset="utf-8">
		<?php include("../includes/pageheader.php"); ?>
		<link rel="stylesheet" href="//code.jquery.com/ui/1.11.2/themes/smoothness/jquery-ui.css">
		<link rel="stylesheet" href="[URL unfurl="true"]http://fullcalendar.io//css/base.css?2.1.1">[/URL]
		<link rel="stylesheet" href="[URL unfurl="true"]http://fullcalendar.io//js/fullcalendar-2.1.1/fullcalendar.css">[/URL]
		<link rel="stylesheet" href="/style/calendar.css">
		<script src="//code.jquery.com/jquery-1.10.2.js"></script>
		<script src="//code.jquery.com/ui/1.11.2/jquery-ui.js"></script>
		<script src="[URL unfurl="true"]http://fullcalendar.io//js/fullcalendar-2.1.1/lib/moment.min.js"></script>[/URL]
		<script src="[URL unfurl="true"]http://fullcalendar.io//js/fullcalendar-2.1.1/fullcalendar.min.js"></script>[/URL]
		<script>
			var curEvent;
			$(document).ready(function(){
				var ajaxServer = 'ajaxServer.php';
				//extend the prototype
				$.fn.addOption = function(optText, optValue){
					var option = new Option(optText, optValue);
					return this.append(option);
				}
				var opts = {
					select: function(start, end) {
						var el = $('#dialog');
						var s = fTime(start.hour(), start.minutes());
						var e = fTime(end.hour(), end.minutes());
						var d = fDate(start);
						if(s == '00:00') s = "09:30";
						if(e == '00:00') e = "17:30";
						$('#startTime').val(s);
						$('#endTime').val(e);
						$('#eventDate').val(d);
						$('#id').val(' ');
						$('#eventTitle').val('');
						el.dialog('open');
					},
					selectHelper: true,
					weekMode: 'variable',
					header: {
							right: 'prev,next,month,basicWeek,basicDay',
							left: 'title'
						},
					firstDay: 1, // Starts week on Monday
					handleWindowResize: true,
					selectable: true,
					editable: true,			
					eventLimit: true,
					timeFormat: 'hh:mmt',
					viewRender: function(view, element){
						$('.fc-day-number').css({
								'text-align':'left', 
								borderBottom: "thin solid black"});
						$('.fc-other-month').css('color','white'); 
					},
					windowResize: function(view){
						$('.fc-day-number').css({
							   'text-align':'left', 
								borderBottom: "thin solid black"});
						$('.fc-other-month').css('color','white');
					},
					eventClick: function(event, jsEvent, view){
						$('#eventDate').val(fDate(event.start));
						$('#startTime').val(fTime(event.start.hour(), event.start.minutes()));
						$('#endTime').val(fTime(event.end.hour(), event.end.minutes()));
						$('#id').val(event.id);
						$('#eventTitle').val(event.title);
						$('#editable').val(event.editable);
						$('#dialog').dialog('open');
					},
					eventDrop: function(event, delta, revertFunc, jsEvent, ui, view){
						if(!event.start.hasTime()){
							revertFunc();
							return;
						}
						var eventData = {	id: event.id,
									start: event.start.format(),
									end: event.end.format(),
									title: event.title,
									allDay: event.allDay,
									editable: event.editable,
									action: 'updateEventDate'
								};
						$.ajax({
							url: ajaxServer,
							type: 'POST',
							dataType: 'json',
							success: function(data){
								if(data.result != 'ok'){
									revertFunc();
								}
							},
							data: eventData
						});
					},
					events: [
					<?php
					$n = 0;
					for ($i=0;$i<=count($rowCat)-1;$i++) :
						$EntryID = $rowCat[$i]["ID"];
						$StartTime = strtotime(trim($rowCat[$i]["Start"]));
						$EndTime = strtotime(trim($rowCat[$i]["End"]));
						$DisplayName = trim($rowCat[$i]["StaffName"]);
						echo "{\nid: $EntryID,\n";
						echo "title: '$DisplayName',\n";
						echo "allDay: false,\n";
						if (!$rowCat[$i]["FirstName"] && !$rowCat[$i]["LastName"]) :
							$FormattedStart = date("Y-m-d", $StartTime);
							$FormattedEnd = ($EndTime > $StartTime) ? date('Y-m-d', strtotime('+1 day', $EndTime)): date("Y-m-d", $EndTime);
							echo "start: '$FormattedStart',\n";
							echo "end: '$FormattedEnd'";
							if (isset($_SESSION['AccessLevel']) && $_SESSION['AccessLevel'] > 1) :
								echo ",\n";
								echo "url: '/administration/events_admin.php?ID=$EntryID',";
							endif;
							echo "className:'holiday',\n";
							//echo "className: 'specialevent',\n";
							echo "editable: false\n";
							echo "}\n";
						else :
							$FormattedStart = date("Y-m-d\TH:i:s", $StartTime);
							$FormattedEnd = date("Y-m-d\TH:i:s", $EndTime);
							echo "start: '$FormattedStart',\n";
							echo "end: '$FormattedEnd'";
							if (isset($_SESSION['AccessLevel']) && $_SESSION['AccessLevel'] > 1) :
								echo ",\n";
								echo "url: '/administration/calendar_admin.php?ID=$EntryID',";
							endif;
							echo "className: 'normalEvent',\n";
							//echo "className: 'calendarevent',\n,";
							echo "editable: true\n";
							echo "}\n";
						endif;
						if(++$n !== $numRows) :
							echo ",\n\n";
						endif;
					endfor;
					?>					
					]
			};	//end of options
				$('#calendar').fullCalendar(opts);
				for(var i = 8; i<=17; i++){
					$('#startTime').addOption(fTime(i,0), fTime(i,0));
					$('#startTime').addOption(fTime(i,30), fTime(i,30));
				}
				for (var i = 17; i<=24; i++){
					$('#endTime').addOption(fTime(i,0), fTime(i,0));
					$('#endTime').addOption(fTime(i,30), fTime(i,30));
				}
				function fTime(h, m){
					h = h==24 ? 0 : h;
					h = h < 10 ? '0' + h: h;
					m = m <10 ? '0' + m : m;
					return h + ':' + m;
				}
				function fDate(i){
					var d = i.date() < 10 ? '0' + i.date() : i.date();
					var m = i.month() < 9 ? '0' + (i.month +1) : i.month() + 1;
					return i.year() + '-' + m + '-' + d;
				 }
				$('#eventSave').closest('form').on('submit', function(e){
					e.preventDefault();
					$('#eventSave').trigger('click');
					
				});
				$('#eventSave').on('click', function(e){
					e.preventDefault();
					var eventData = {
						start: $('#eventDate').val() + 'T' + $('#startTime').val(),
						end: $('#eventDate').val() + 'T' + $('#endTime').val(),
						title: $('#eventTitle').val(),
						id:$('#id').val(),
						allDay: false,
						action: 'saveEvent',
						editable: $('#editable').val()
				   };
				   $.ajax({
						url: ajaxServer,
						type: 'POST',
						dataType: 'json',
						success: function(data){
							$('#calendar').fullCalendar('removeEvents', data.id);
							$('#calendar').fullCalendar('renderEvent', data);
							$('#dialog').dialog('close');
						},
						data: eventData
				   });
				});
				$('#dialog').dialog({ autoOpen: false, closeOnEscape: false, buttons: [], modal: true});
				
			});
		</script>
	</head>
	<body>

<?php include("../includes/header.php"); ?>

<div id="calendar"></div>
<div id="dialog">
    <form>
        <div class="formRow">
            <div class="label">Event Title</div>
            <div class="formData"><input name="eventTitle" id="eventTitle" /></div>
        </div>
        <div class="formRow">
            <div class="label">Start Time:</div>
            <div class="formData"><select id="startTime"></select></div>
        </div>
        <div class="formRow">
            <div class="label">End Time:</div>
            <div class="formData"><select id="endTime"></select></div>
        </div>
        <div class="formRow">
            <div class="label"> </div>
            <div class="formData"><input type="button" id="eventSave" value="Save" />
			<input id="eventDate" type="hidden"/>
			<input id="editable" type="hidden"/>
			<input id="id" type="hidden" name="id" />
			</div>
        </div>
    </form>
</div>

<?php include("../includes/footer.php"); ?>

</body>
 
ideally you would deliver events to the script via ajax or json. but otherwise you can just deliver via the options api as you are doing.

it still wants to open its own Ajax form momentarily before mine opens
i don't understand that. if you are worried about the momentary flash of unstyled content (as it is proverbially known) then just add a css rule to set the #dialog to display: none

It is also still showing events from the previous/next month but I've not attempted to fix that yet
the code deals with this. if it is not working then there is something in whatever you are doing that is overriding the style rules.

The boxes around the days are also gone and, again, I've not tried to correct it yet
sorry but I don't understand. it sounds as if you are not loading one of the required style sheets. but you are at liberty to style the calendar precisely as you want.

it does not expand longer entries so some do not print.

typically a calendar view is not the best for print. you may be better off printing a succession of agenda/weeks.


the <html> tags are not deprecated but are inferred in most browser support of html5. it is better to include them.

I think it must be embedded anyway to work in combination with the PHP.
there is no requirement for the javascript to be embedded. it will work equally well in a separate file.

 
Most of the things you quoted and referred to were simply my comments by way of updating the status and each indicated that I hadn't worked on the issue yet. I wasn't saying there was any problem, only that that's the way they are now.

I do, however, need to print the entire monthly calendar and all events must show and that is something I'm not sure, yet, how to fix. This is a staff scheduling calendar sent to each person before the end of the previous month so that they can plan accordingly.

On this:

Quote: It is also still showing events from the previous/next month but I've not attempted to fix that yet

the code deals with this. if it is not working then there is something in whatever you are doing that is overriding the style rules.

I'm not sure what I may have done to make previous/next month entries still show since the formatting from the database entries is identical to the hard-coded sample events that were already there. Other than to add the database links, I've not yet changed anything in the JavaScript or CSS.

I've never used json or Ajax so it these are capable of writing to the database when things are dragged, that would be great. However, per our offline work that you and I have been discussing, the PHP form still needs to be functional in order to populate dates quickly.
 
I'm not sure what I may have done to make previous/next month entries still show

we may be talking at cross purposes. i thought you were referring to the day numbers (over which you have no control). You may be referring to the event entries themselves (over which you do have control).

If so, then this would be non-standard for a month view calendar (as would suppressing the day numbers). But if you want a solution, here are several:

1. programmatically.
a. have all events delivered as a javascript object outside of the calendar constructor.
b. on viewRender callback refresh all the events and then remove them with a callback filter that checks the date.

or
b. on viewRender callback, remove all the events and then iterate through the javascript object testing the dates and adding them back in using renderEvent.

or
a. have all the events delivered as a javascript array of objects of the calendar constructor and store them in a structured format such as [year][month][day][{events}].
b. on viewRender callback remove all the events and then iterate through the object at the right point, derived from the knowledge of the current year and month of the view.

2. client-server (preferred)
a. set the source of the events to a json server
b. whenever the view changes, programmatically remove all the events from the calendar and then refetch from the server.
c. have the server only deliver the events that are relevant to the month in question (or week/day in question).


obviously the client-server is the best method and probably the easiest to implement by far.

I've never used json or Ajax

json is javascript object notation. you will have used it countless times; or at least overtime you use javascript.

ie.
Code:
var obj = {fruit: ['apples','oranges'], vegetables:['carrots','onions']};
the above is an object with each item of the object holding an array.

in php an associative array is equivalent to a javascript object. a numeric array is equivalent to a javascript array. you can use json_encode to get php to collapse an array for output as an object. eg.
Code:
<?php
$array = array('fruit'=>array('apples','oranges'), 'vegetables'=>array('carrots','onions'));
echo json_encode($array);
//output
{"fruit":["apples","oranges"],"vegetables":["carrots","onions"]}

just as it is very useful to hold structured data inside arrays within php, json is very useful for communicating structured data between systems. and particularly non-identical systems like javascript and php. so most ajax servers are designed to take POST input and return json output. jQuery automatically parses the response from the server and expands it into a proper object that you can use just like any other object/array in javascript.

ajax is simply a useful term for addressing the XHR object within javascript. this allows the browser to send post or get data to a web server and receive a response without refreshing the page. there is nothing more to it than that (other than that you can make the interaction asynchronous or synchronous where as a full page request is always synchronous). so from the perspective of your php you just need to decide: 1. what to do with incoming data; and 2; whether to output a full page or just a tiny amount of json encoded data. I prefer making these decisions based on entry point. so anything addressing ajaxServer.php will only ever get json data back and never html. anything addressing index.php will only ever get the full monty. both ajaxServer.php and index.php are always just bootstraps in my code - after that everything is handled by the same dispatchers and routines so there is no code/effort duplication.


I do, however, need to print the entire monthly calendar and all events must show and that is something I'm not sure, yet, how to fix.
set eventLimit to false in the constructor.
 
we may be talking at cross purposes. i thought you were referring to the day numbers (over which you have no control). You may be referring to the event entries themselves (over which you do have control).

Yes, Think we were. Non-standard or not, I want events for only the current month to show and I know it probably needs to be done in the query which is why I asked how the calendar's programming can create a session value with the date. With that, I can modify the query to pull up only events for the current month.

json is javascript object notation. you will have used it countless times; or at least overtime you use javascript.

As I've always been a back-end programmer, I've virtually never needed to use JavaScript except for an occasional onclick(), onchange() and that sort of basic thing. I am only just now starting to use it more with these odd projects I've been finding myself doing.

I do, however, need to print the entire monthly calendar and all events must show and that is something I'm not sure, yet, how to fix.

set eventLimit to false in the constructor.

The problem with setting eventLimit to false is that it misshapes the calendar in viewing mode. I had wanted it to show with the "More" links as it has been but to print showing all of them. No worries, though, as eventLimit: false is a good workaround.
 
but you can do that with just print. if you use htmltopdf you can point the url to be printed at a php script that outputs eventLimit: false.

how the calendar's programming can create a session value with the date
there is no way for a javascript to create a session variable. one is client side, the other server side.
but using ajax the script can request events between certain dates, very easily. and then the server can return only the events to show.

i have not tested this at all, and it is the work of only a few minutes and limited exposure to the fullCalendar API, but I believe that this approach will work. your server side script will receive the criteria in the POST vars and needs to rreturn properly formatted events as a numeric array of associative arrays, all json_encoded like this
Code:
echo json_encode(array('events'=>array( .... events ...)));
die;

Code:
 viewRender: function(view, element){
			switch(view.name){
				case 'month':
					if(view.start.month() != view.end.month()){
						var mD = view.end.month() - view.start.month();
						if(mD == 2){
							var y = view.start.year();
							if(view.start.month() == 11){
								var s = y+1 + '-01-01';
								var e = y+1 + '-01-31';
							} else {
								var s = y + '-' + (view.start.month() + 2) + '-01';
								var _t = new date(y, view.start.month() + 2, 0);
								var e = _t.getFullYear() + '-' + (_t.getMonth() + 1) + '-' + _t.getDate();	
							}
						} else if(mD == 1){
							if(view.start.date() == 1){
								var s = view.start.year() + '-' + (view.start.month() + 1) + '-01';
								var _t = new date(view.start.year(), view.start.month() + 2, 0);
								var e = _t.getFullYear() + '-' + (_t.getMonth() + 1) + '-' + _t.getDate();
							}
						}
					} else {
						var s = view.start.year() + '-' + (view.start.month() + 1) + '-01';
						var _t = new Date( view.start.year(), view.start.month() + 2,0);
						var e = _t.getFullYear() + '-' + (_t.getMonth() + 1) + '-' + _t.getDate();    
					}
				break;
				
				case 'basicDay':
				case 'agendaDay':
					var s = view.start.year() + '-' + (view.start.month() + 1 ) + '-' + view.start.date();
					var e = s;	
					break;
				case 'agendaWeek':
				case 'basicWeek':
					var s = view.start.year() + '-' + (view.start.month() + 1) + '-' + view.start.date();
					var e = view.end.year() + '-' + (1 + view.end.month() ) + '-' + view.end.date();;	
					break;
			}
			$.ajax({
				url: ajaxServer,
				type:'POST',
				dataType:'json',
				data:{
					action: 'getEvents',
					start: s,
					end: e
				},
				success: function(data){
					$('#calendar').fullCalendar('removeEvents');
					$('#calendar').fullCalendar('addEventSource', data.events);
				}
			})
			
            $('.fc-day-number').css({
                    'text-align':'left', 
                    borderBottom: "thin solid black"});
            $('.fc-other-month').css('color','white'); 
        },
 
how the calendar's programming can create a session value with the date

there is no way for a javascript to create a session variable. one is client side, the other server side.
but using ajax the script can request events between certain dates, very easily. and then the server can return only the events to show.

Yes, I know that but, as I've already embedded PHP into JavaScript, is there no way to do it to fetch the date? Better still, do the calendar's forward or back arrows submit anything that can be grabbed for the purpose? I mean, it appears that that's what you're saying but when I replace the viewRender I already had with that above, I get no calendar at all, and the update/insert form shows all the time so I'm not really sure what to do with it.
 
There is probably a syntax error in the js.
 
Status
Not open for further replies.

Part and Inventory Search

Sponsor

Back
Top