AJAX calendar

Small, short code snippets that other people may find useful. Do you have a good regex that you would like to share? Share it! Even better, the code can be commented on, and improved.

Moderator: General Moderators

User avatar
pickle
Briney Mod
Posts: 6445
Joined: Mon Jan 19, 2004 6:11 pm
Location: 53.01N x 112.48W
Contact:

AJAX calendar

Post by pickle »

Hi all,

I just finished making MY version of a date picker calendar. This one uses XMLHTTP (AJAX) to get PHP to generate the calendar. The visual part itself is thrown into the page, so there are no pop-ups. This uses DOM scripting and xmlhttp, so it's not backwards compatible with Netscape 2. Sorry.

Changing the appearance isn't ideally simple, but I didn't want to require modifications to a site's CSS file in order to change the appearance. So, everything's done with <style> tags and onMouseOver and onMouseOut. To change the appearance, just modify the $styles array at the top of the file.

$styles['current']['day'] defines the styles that are applied to the current month's weekdays,
$styles['current']['weekend'] defines the styles that are applied to the current month's weekend days
$styles['other']['day'] defines the styles that are applied to previous & last month's weekdays,
$styles['other']['weekend'] defines the styles that are applied to previous & last month's weekends days.
$styles['nav'] defines the styles applied to the 'next' and 'previous' links
$styles['date'] defines the styles applied to the date output in the header
$styles['th'] defines the styles applied to the whole header
$styles['td'] defines the styles applied to the calendar days
$styles['a'] defines the styles applied to the links in each day

'js' defines the string javascript will use to change this property
'css' defines the string that will be used in the <style> tags
'value' defines the initial and onMouseOut value of this property
'mouseovervalue' defines the value of this property defined onmouseover


Library

Code: Select all

<?PHP
//Released under the GPL license


//--------------------
// CONFIG

//set the styles of the calendar
$styles['current']['day'] = array(array('js'=>'backgroundColor',
					'css'=>'background-color',
					'mouseovervalue'=>'#FFFFFF',
					'value'=>'#d7e9ff'));
$styles['current']['weekend'] = array(array('js'=>'backgroundColor',
					    'css'=>'background-color',
					    'mouseovervalue'=>'#FFFFFF',
					    'value'=>'#a7cbf5'));
$styles['other']['day'] = array(array('js'=>'backgroundColor',
				      'css'=>'background-color',
				      'mouseovervalue'=>'#FFFFFF',
				      'value'=>'#afc3d9'));
$styles['other']['weekend'] = array(array('js'=>'backgroundColor',
					  'css'=>'background-color',
					  'mouseovervalue'=>'#FFFFFF',
					  'value'=>'#d9e0e7'));
$styles['nav'] = array(array('js'=>'backgroundColor',
			     'css'=>'background-color',
			     'mouseovervalue'=>'#FFFFFF',
			     'value'=>'#5995de'),
		       array('js'=>'color',
			     'css'=>'color',
			     'mouseovervalue'=>'#333333',
			     'value'=>'#FFFFFF'),
		       array('js'=>'display',
			     'css'=>'display',
			     'mouseovervalue'=>'block',
			     'value'=>'block'),
		       array('js'=>'textDecoration',
			     'css'=>'text-decoration',
			     'mouseovervalue'=>'none',
			     'value'=>'none'));
$styles['th'] = 'background-color:#5995de;color:#fff;font-weight:bold;';
$styles['date'] = 'text-align:center;';
$styles['td'] = 'text-align:center;';
$styles['a'] = 'display:block;height:100%;text-decoration:none;padding:3px;';


//set the date format to return - use PHP's date() syntax
$date_format = 'd/m/Y';



// /CONFIG
// You shouldn't need to go beyond this point
//--------------------


if($_POST['mode'] == 'PHP')
{
  actAsPHP($styles,$date_format);
}
else
{
  actAsJavascript();
}



function actAsPHP($styles,$date_format)
{
  $id = $_POST['id'];


  if(isset($_POST['curr_stamp']))
  {
    $direction = ($_POST['direction'] == 'subtract') ? '-' : '+';
    $now = strtotime($direction.'1 month',$_POST['curr_stamp']);
  }
  else
  {
    $now = time();
  }

  $first_of_month = mktime(0,0,0,date('m',$now),1,date('y',$now));

 //-------------------
 //build the day array

 //get the Monday of the week that has the 1st of the month
 $first_day_stamp = $first_of_month;
 while(date('w',$first_day_stamp) > 0)
 {
   $first_day_stamp = strtotime('-1 day',$first_day_stamp);
 }

 //insert all days from last month
 $curr_stamp = $first_day_stamp;
 while($curr_stamp < $first_of_month)
 {
   $days[1][] = $curr_stamp;
   $curr_stamp = strtotime('+1 day',$curr_stamp);
 }

 //insert all days from this month
 $week = 1;
 while(date('n',$curr_stamp) == date('n',$first_of_month))
 {
   $days[$week][] = $curr_stamp;
   $week = (date('w',$curr_stamp)) == 6 ? $week + 1 : $week;
   $curr_stamp = strtotime('+1 day',$curr_stamp);
 }

 //insert all the days from next month
 while(date('w',$curr_stamp) > 0)
 {
   $days[$week][] = $curr_stamp;
   $curr_stamp = strtotime('+1 day',$curr_stamp);
 }

 //-----------------
 //display the table

 //open the table & display the navigation

 foreach($styles['nav'] as $property)
 {
   $style .= $property['css'].':'.$property['value'].';';
   $mouseOver .= 'this.style.'.$property['js']."='".$property['mouseovervalue']."';";
   $mouseOut .= 'this.style.'.$property['js']."='".$property['value']."';";
 }

 $monthyear = date('M \'y',$first_of_month);
 echo <<<TABLE
	<table cellspacing = '1' style = '$styles[table]'>
	  <tr>
	    <th style = '$styles[th]' colspan = '2'>
	      <a href = "javascript:void(0);" style = "$style" onmouseover = "$mouseOver" onmouseout = "$mouseOut" onClick = "loadDate('$id','$first_of_month','subtract');">
		&laquo;
	      </a>
	    </th>
	    <th colspan = '3' style = '$styles[th] $styles[date]'>
	      $monthyear
	    </th>
	    <th style = '$styles[th]' colspan = '2'>
	      <a href = "javascript:void(0);" style = "$style" onmouseover = "$mouseOver" onmouseout = "$mouseOut" onClick = "loadDate('$id','$first_of_month','add');">
		&raquo;
	      </a>
	    </th>
	  </tr>
TABLE;
 


 foreach($days as $week)
 {
   echo '<tr>';
   foreach($week as $day)
   {
     //setup how the day should look
     if(date('n',$day) != date('n',$first_of_month))
       if(date('w',$day) == 0 || date('w',$day) == 6)
	 $style_properties = $styles['other']['weekend'];
       else
	 $style_properties = $styles['other']['day'];
     else
       if(date('w',$day) == 0 || date('w',$day) == 6)
	 $style_properties = $styles['current']['weekend'];
       else
	 $style_properties = $styles['current']['day'];

     unset($style);
     unset($mouseOver);
     unset($mouseOut);

     foreach($style_properties as $property)
     {
       $style .= $property['css'].':'.$property['value'].';';
       $mouseOver .= 'this.style.'.$property['js']." = '".$property['mouseovervalue']."';";
       $mouseOut .= 'this.style.'.$property['js']." = '".$property['value']."';";
     }

     $day_of_month = date('j',$day);
     $click_val = date($date_format,$day);
     echo <<<CELL
	    <td style = '$styles[td]'>
	      <a href = "javascript:void(0);" 
		 style = '$styles[a] $style' 
		 onMouseOver = "$mouseOver" 
		 onMouseOut = "$mouseOut" 
		 onClick = "document.getElementById('$id').value = '$click_val';document.getElementById('$id').style.display = 'block';document.getElementById('$id').focus();divNode = document.getElementById('calendar');divNode.parentNode.removeChild(divNode);">$day_of_month
</a>
	    </td>
CELL;
   }
   echo '</tr>';
 }
 echo '</table>';
}//function: actAsPHP()

function actAsJavascript()
{
  $uri = 'http://'.$_SERVER['SERVER_NAME'].$_SERVER['REQUEST_URI'];
  header('Content-type: text/javascript;');
  
  echo <<<JAVASCRIPT
    function loadDate(pFieldID)
    {
      try
      {
	if(window.XMLHttpRequest)
	{
	  reqsend = new XMLHttpRequest();
	}
	else
	{
	  reqsend = new ActiveXObject("Microsoft.XMLHTTP");
	}
      }
      catch(e)
      {
	alert('Your browser does not support XMLHTTP.');
      }

      try
      {
	reqsend.open("POST",'$uri');
	reqsend.setRequestHeader("Content-Type","application/x-www-form-urlencoded");

	post = "mode=PHP&id=" + pFieldID;

	//'arguments' is a JS construct that represents
	//all the arguments sent to a function
	if(arguments[1])
	{
	  post = post + '&curr_stamp=' + arguments[1] + '&direction=' + arguments[2];
	}
	reqsend.setRequestHeader("Content-Length",post.length);
	reqsend.send(post);
	reqsend.onreadystatechange = function()
	{
	  if(reqsend.readyState == 4 && reqsend.status == 200)
	  {
	    var responseText = reqsend.responseText;

	    //create the new div
	    //it's not inserted into the document yet
	    calendarDiv = document.createElement('div');
	    calendarDiv.setAttribute('id','calendar');

	    //remove the div if it exists
	    //this avoids duplication of the calendar in the document
	    if(document.getElementById('calendar'))
	    {
	      calendarDiv = document.getElementById('calendar');
	      calendarDiv.parentNode.removeChild(calendarDiv);
	    }
  
	    //insert the new div into the document
	    pField = document.getElementById(pFieldID);
	    pField.parentNode.appendChild(calendarDiv);

	    //hide the text field it's replacing
	    document.getElementById(pFieldID).style.display = 'none';

	    //fill the new div
	    document.getElementById('calendar').innerHTML = responseText;
	  }
	};
      }
      catch(e)
      {
	alert(e);
      }
    }
JAVASCRIPT;
}
?>

Example usage

Code: Select all

<html>
  <head>
    <script src = "calendar.php"></script>
  </head>
  <body>
    <input type = "text" id = "test2" onclick = "loadDate('test2');" value = 'Click me!'>
  </body>
</html>
You can see this in use here: http://www.nderson.ca/misc/calendar/test.php
Last edited by pickle on Thu May 18, 2006 12:46 pm, edited 1 time in total.
Real programmers don't comment their code. If it was hard to write, it should be hard to understand.
jmut
Forum Regular
Posts: 945
Joined: Tue Jul 05, 2005 3:54 am
Location: Sofia, Bulgaria
Contact:

Post by jmut »

I cant seem to get this working. Trying the url you provided.
When I click it returns.
Using Firefox 1.5.0.1


[Exception... "'Permission denied to set property XULElement.selectedIndex' when calling method: [nsIAutoCompletePopup::selectedIndex]" nsresult: "0x8057001e (NS_ERROR_XPC_JS_THREW_STRING)" location: "JS frame :: http://www.nderson.ca/misc/calendar/test.php :: anonymous :: line 0" data: no]test.php (line 0)
[Exception... "'Permission denied to set property XULElement.selectedIndex' when calling method: [nsIAutoCompletePopup::selectedIndex]" nsresult: "0x8057001e (NS_ERROR_XPC_JS_THREW_STRING)" location: "JS frame :: http://www.nderson.ca/misc/calendar/test.php :: anonymous :: line 0" data: no]
User avatar
Chris Corbyn
Breakbeat Nuttzer
Posts: 13098
Joined: Wed Mar 24, 2004 7:57 am
Location: Melbourne, Australia

Post by Chris Corbyn »

jmut wrote:I cant seem to get this working. Trying the url you provided.
When I click it returns.
Using Firefox 1.5.0.1


[Exception... "'Permission denied to set property XULElement.selectedIndex' when calling method: [nsIAutoCompletePopup::selectedIndex]" nsresult: "0x8057001e (NS_ERROR_XPC_JS_THREW_STRING)" location: "JS frame :: http://www.nderson.ca/misc/calendar/test.php :: anonymous :: line 0" data: no]test.php (line 0)
[Exception... "'Permission denied to set property XULElement.selectedIndex' when calling method: [nsIAutoCompletePopup::selectedIndex]" nsresult: "0x8057001e (NS_ERROR_XPC_JS_THREW_STRING)" location: "JS frame :: http://www.nderson.ca/misc/calendar/test.php :: anonymous :: line 0" data: no]
Using FF 1.5.0.3 (Linux) Works nicely for me :)
User avatar
Maugrim_The_Reaper
DevNet Master
Posts: 2704
Joined: Tue Nov 02, 2004 5:43 am
Location: Ireland

Post by Maugrim_The_Reaper »

Firefox 1.5.0.3 for Windows likewise works.
jmut
Forum Regular
Posts: 945
Joined: Tue Jul 05, 2005 3:54 am
Location: Sofia, Bulgaria
Contact:

Post by jmut »

:)
guess it is time for an upgrade.
User avatar
pickle
Briney Mod
Posts: 6445
Joined: Mon Jan 19, 2004 6:11 pm
Location: 53.01N x 112.48W
Contact:

Post by pickle »

I'm not doubting you get that error, but nonetheless it makes no sense. That error seems like it should pop up when there's a problem with a drop-down list - dunno why it's popping up here.
Real programmers don't comment their code. If it was hard to write, it should be hard to understand.
User avatar
tasteslikepurple
Forum Commoner
Posts: 46
Joined: Thu Jan 26, 2006 3:38 am
Location: Bath, UK

Post by tasteslikepurple »

works on Firefox 1.0.7, Opera 8.52, Safari 2.0.3 on the mac but (not surprisingly!) Internet Explorer 5.2 doesn't support XMLHTTP (gotta love microsoft and their half hearted attempt to created a less than half decent browser for the mac).

good stuff! keep up the good work
User avatar
Chris Corbyn
Breakbeat Nuttzer
Posts: 13098
Joined: Wed Mar 24, 2004 7:57 am
Location: Melbourne, Australia

Post by Chris Corbyn »

tasteslikepurple wrote:works on Firefox 1.0.7, Opera 8.52, Safari 2.0.3 on the mac but (not surprisingly!) Internet Explorer 5.2 doesn't support XMLHTTP (gotta love microsoft and their half hearted attempt to created a less than half decent browser for the mac).

good stuff! keep up the good work
IE does support XMLHTTP they just decided to dilly-dally around using XMLHttpRequest and invent their own. Now I have to make a point here though in Microsoft's favour. Microsoft invented XMLHTTP.

In IE you use an ActiveX control called XMLHTTP. It's pretty easy to roll your own function to return an XMLHttpRequest object or an activeX object depending upon what's avilable ;)

EDIT | I'm terribly sorry I totally mis-read the whole thing about the Mac and version 5.2 of IE :oops:
User avatar
Burrito
Spockulator
Posts: 4715
Joined: Wed Feb 04, 2004 8:15 pm
Location: Eden, Utah

Post by Burrito »

nice work pickle.
User avatar
pickle
Briney Mod
Posts: 6445
Joined: Mon Jan 19, 2004 6:11 pm
Location: 53.01N x 112.48W
Contact:

Post by pickle »

Thanks everyone!
Real programmers don't comment their code. If it was hard to write, it should be hard to understand.
Roja
Tutorials Group
Posts: 2692
Joined: Sun Jan 04, 2004 10:30 pm

Post by Roja »

What license? BSDL? Free-to-use? GPL?
User avatar
pickle
Briney Mod
Posts: 6445
Joined: Mon Jan 19, 2004 6:11 pm
Location: 53.01N x 112.48W
Contact:

Post by pickle »

Yep.

Um... GPL I guess
(updated the code to say as much)
Real programmers don't comment their code. If it was hard to write, it should be hard to understand.
User avatar
tasteslikepurple
Forum Commoner
Posts: 46
Joined: Thu Jan 26, 2006 3:38 am
Location: Bath, UK

Post by tasteslikepurple »

d11wtq wrote:
tasteslikepurple wrote:works on Firefox 1.0.7, Opera 8.52, Safari 2.0.3 on the mac but (not surprisingly!) Internet Explorer 5.2 doesn't support XMLHTTP (gotta love microsoft and their half hearted attempt to created a less than half decent browser for the mac).

good stuff! keep up the good work
IE does support XMLHTTP they just decided to dilly-dally around using XMLHttpRequest and invent their own. Now I have to make a point here though in Microsoft's favour. Microsoft invented XMLHTTP.

In IE you use an ActiveX control called XMLHTTP. It's pretty easy to roll your own function to return an XMLHttpRequest object or an activeX object depending upon what's avilable ;)

EDIT | I'm terribly sorry I totally mis-read the whole thing about the Mac and version 5.2 of IE :oops:
easily done :)
Moogle
Forum Newbie
Posts: 1
Joined: Tue Sep 26, 2006 5:18 am
Location: Paris, France

Post by Moogle »

Nice code but I made 2 modifications :

- The calendar doesn't work before 1970. To correct this, change :

Code: Select all

$first_of_month = mktime(0,0,0,date('m',$now),1,date('y',$now));
into

Code: Select all

$first_of_month = mktime(0,0,0,date('m',$now),1,date('Y',$now));
Yeah, a simple case change for just one can change everything :)

- It would be great to show the calendar with another month than the current one. To do this, I changed the beginning of the actAsPHP function :

Code: Select all

function actAsPHP($styles,$date_format)
{
  $id = $_POST['id'];

  if(isset($_POST['curr_stamp']))
  {
  	if (isset($_POST['direction'])) {
	    $direction = ($_POST['direction'] == 'subtract') ? '-' : '+';
    	$now = strtotime($direction.'1 month',$_POST['curr_stamp']);
	} else {
		$now = $_POST['curr_stamp'];
	}
  }
  else
  {
    $now = time();
  }
[...]
and the Javascript part :

Code: Select all

[...]
        //'arguments' is a JS construct that represents
        //all the arguments sent to a function
        if(arguments[1])
        {
          post = post + '&curr_stamp=' + arguments[1];
		  
		  if (arguments[2]) {
		  	post = post + '&direction=' + arguments[2];
		  }
        }
[...]
Now you can call loadDate with the wanted timestamp as a second argument.
User avatar
pickle
Briney Mod
Posts: 6445
Joined: Mon Jan 19, 2004 6:11 pm
Location: 53.01N x 112.48W
Contact:

Post by pickle »

Nice additions. I'll add them to the code.
Real programmers don't comment their code. If it was hard to write, it should be hard to understand.
Post Reply