Showing posts with label php. Show all posts
Showing posts with label php. Show all posts

1/12/2012

Better PHP Calendar Class

I've dealt with creating monthly calendars in PHP and it has always been a bit of pain. How exactly to show the days in the month in a table without creating ugly and hard to maintain code? I believe this time I was able to find a good solution.

A PHP Class

It's a PHP class that will output the dates of the given month in array. But in order to make the dates fit their place in the week, the class outputs zeros in the place of "empty" days. So the first week of a month starting in Wednesday would be this array:

[0, 0, 1, 2, 3, 4, 5]

This allows you to loop through the returned arrays without worrying where to start the week. If you use a table, you could simply output " " when the date is 0 and output the number (and colored background) when the date is a number. Here is an example in HTML:

Using the output in HTML:


<table>
    <tr><th>Mon</th><th>Tue</th><th>Wed</th><th>Thu</th><th>Fri</th><th>Sat</th><th>Sun</th></tr>
    <?php for($i=1;$i<=$num_weeks;$i++):?>
        <tr>
            <?php $days=$_calendar->days($m[0], $m[1], $i, $num_weeks);
            foreach($days as $day):?>
                <td><?=$day?$day:"&nbsp;"?></td>
            <?php endforeach;?>
        </tr>
    <?php endfor;?>
    </table>


Easy, eh? We just output a table row for every week in the month ($num_weeks is also a method in the class as you'll see below) and for the days with date we output the date (otherwise just blank space).

You can see how it works live in this period calendar.

The PHP Code

Now, here is how it all works. I created a class to hold 3 functions:

num_weeks($month, $year) - to calculate the number of weeks in the given month and year

first_day($month, $year) - to find out which day of the week is the first day of a given month.year

days($month, $year, $week, $num_weeks=0) - this is the main method that returns the array of 7 days. The week starts in Monday.

Here is the full code below and some comments to it:

function num_weeks($month, $year)
    {
        // every month has at least 4 weeks
        $num_weeks=4;
    
        // finding where it starts
        $first_day = $this->first_day($month, $year);  
        
        // if the first week doesn't start on monday 
        // we are sure that the month has at minimum 5 weeks
        if($first_day!=1) $num_weeks++;
        
        // find the "widow" days (i.e. empty cells in the 1st week)
        $widows=$first_day-1;  
        $fw_days=7-$widows;
        if($fw_days==7) $fw_days=0;       
        
        // number of days in the month
        $numdays=date("t",mktime(2, 0, 0, $month, 1, $year));
        
        if( ($numdays - $fw_days) > 28 ) $num_weeks++;
         // that's it!
        return $num_weeks;                  
    }


Then the first_day function:

function first_day($month, $year)
    {
        $first_day= date("w", mktime(2, 0, 0, $month, 1, $year));
        if($first_day==0) $first_day=7; # convert Sunday
        
        return $first_day;
    }


And here is the most important one:
// here $week is the week number when we go in a loop
// (see the html code that I posted earlier to get better idea)
function days($month, $year, $week, $num_weeks=0)
{
        $days=array();

        // this is just to avoid calling num_weeks every time 
        // when you loop through the weeks        
        if($num_weeks==0) $num_weeks=$this->num_weeks($month, $year);
        
        // find which day of the week is 1st of the given month        
        $first_day = $this->first_day($month, $year);
                
        // find widow days (first week)
        $widows=$first_day-1;
        
        // first week days
        $fw_days=7-$widows;
        
        // if $week==1 don't do further calculations
        if($week==1)
        {
            for($i=0;$i<$widows;$i++) $days[]=0;            
            for($i=1;$i<=$fw_days;$i++) $days[]=$i;            
            return $days;
        }
        
        // any other week
        if($week!=$num_weeks)
        {
            $first=$fw_days+(($week-2)*7);
            for($i=$first+1;$i<=$first+7;$i++) $days[]=$i;            
            return $days;
        }
        
        
        # only last week calculations below
        
        // number of days in the month
        $numdays=date("t",mktime(2, 0, 0, $month, 1, $year));
                
        // find orphan days (last week)  
        $orphans=$numdays-$fw_days-(($num_weeks-2)*7);                     
        $empty=7-$orphans;
        for($i=($numdays-$orphans)+1;$i<=$numdays;$i++) $days[]=$i;
        for($i=0;$i<$empty;$i++) $days[]=0;
        return $days;
    }


That's it! Any questions?

5/19/2011

Simple HTML5 Drawing App + Saving The Files With Ajax

This is a simple drawing board app that allows user draw on a canvas, pick several brush sizes and colors and save his image (I'll leave part of the last to you).

You can see how this app works live on the Draw a Robot site. It uses HTML5 canvas and works in all modern browsers except Internet Explorer.

Some important parts of the javascript code are taken from this tutorial. William's app is more impressive. Mine is simpler and aims to explain it in understandable way (William's tutorial is a bit complex). Let's start:

Creating the canvas

<canvas id="drawingCanvas" width="550" height="450" style="border:1pt solid black;margin:auto;cursor:crosshair;clear:both;">
</canvas>


If you don't care about Internet explorer, that's it. We just add some styling to it and make the cursor crossed.

Create the brush sizes and color selectors


<div style="float:left;">Colors:</div> <a href="#" class="colorPicker" onclick="setColor('#FFF');return false;" style="background:#FFF;">&nbsp;</a>
<a class="colorPicker" href="#" onclick="setColor('#000');return false;" style="background:#000;">&nbsp;</a>
<a class="colorPicker" href="#" onclick="setColor('#FF0000');return false;" style="background:#FF0000;">&nbsp;</a>
<a class="colorPicker" href="#" onclick="setColor('#00FF00');return false;" style="background:#00FF00;">&nbsp;</a>
<a class="colorPicker" href="#" onclick="setColor('#0000FF');return false;" style="background:#0000FF;">&nbsp;</a>
<a class="colorPicker" href="#" onclick="setColor('#FFFF00');return false;" style="background:#FFFF00;">&nbsp;</a>
<a class="colorPicker" href="#" onclick="setColor('#00FFFF');return false;" style="background:#00FFFF;">&nbsp;</a>

<div style="clear:both;">&nbsp;</div>

<div style="float:left;">Sizes:</div>
<a href="#" class="colorPicker" onclick="setSize(2);return false;" style="width:2px;height:2px;margin-left:15px;">&nbsp;</a>
<a href="#" class="colorPicker" onclick="setSize(5);return false;" style="width:5px;height:5px;margin-left:15px;">&nbsp;</a>
<a href="#" class="colorPicker" onclick="setSize(10);return false;" style="width:10px;height:10px;margin-left:15px;">&nbsp;</a>
<a href="#" class="colorPicker" onclick="setSize(25);return false;" style="width:25px;height:25px;margin-left:15px;">&nbsp;</a>

<div style="clear:both;">&nbsp;</div>

<p style="clear:both;"><input type="button" value="Clear Canvas" onclick="clearCanvas();">
<input type="button" value="Save My Drawing" onclick="centerElt('saveDrawing',400,300);"></p>


This code adds size selection buttons, color selectiors, and button for saving.

Obviously you can use better CSS to save some code.

Note that Save my drawing users a function that is not published here to keep things simpler. It centers a popup on the screen. You can find or code such function yourself, or simply display the saving form under the canvas without fancy effects. (Let me know in the comments if you need clarification.

The save form

The save form is not important for this tutorial either. You can see the one at the Draw a Robot site, but the form can contain any fields you wish. Maybe image name, description, author name and so on.

The Javascript

After opening a javascript tag, you'll need the following code. I'll input all the explanations to it as javascript comments so you can directly copy the code and use it.

Please note: the javascript below depends on jQuery! If you don't store local copy of jQuery, you need to insert this code in the header of your page:
<script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/1.6.1/jquery.min.js"></script>

Now your javascript follows:

<script type="text/javascript">
/* some code used from http://www.williammalone.com/articles/create-html5-canvas-javascript-drawing-app/ */

/* Some global initializations follow. The first 2 arays will store all mouse positions 
on X and Y, the 3rd one stores the dragged positions. 
The variable paint is a boolean, and then follow the default values 
which we use to start */
var clickX = new Array();
var clickY = new Array();
var clickDrag = new Array();
var paint;
var defaultColor="#000";
var defaultShape="round";
var defaultWidth=5;

// creating the canvas element
var canvas = document.getElementById('drawingCanvas');

if(canvas.getContext) 
{
    // Initaliase a 2-dimensional drawing context
    var context = canvas.getContext('2d');
    
    // set the defaults
    context.strokeStyle = defaultColor;
    context.lineJoin = defaultShape;
    context.lineWidth = defaultWidth;
}

// binding events to the canvas
$('#drawingCanvas').mousedown(function(e){
  var mouseX = e.pageX - this.offsetLeft;
  var mouseY = e.pageY - this.offsetTop;
  
  paint = true; // start painting
  addClick(e.pageX - this.offsetLeft, e.pageY - this.offsetTop);

  // always call redraw
  redraw();
});

$('#drawingCanvas').mousemove(function(e){
  if(paint){
    addClick(e.pageX - this.offsetLeft, e.pageY - this.offsetTop, true);
    redraw();
  }
});

// when mouse is released, stop painting, clear the arrays with dots
$('#drawingCanvas').mouseup(function(e){
  paint = false;
  
  clickX = new Array();
  clickY = new Array();
  clickDrag = new Array();
});

// stop painting when dragged out of the canvas
$('#drawARobot').mouseleave(function(e){
  paint = false;
});

// The function pushes to the three dot arrays
function addClick(x, y, dragging)
{
  clickX.push(x);
  clickY.push(y);
  clickDrag.push(dragging);
}

// this is where actual drawing happens
// we add dots to the canvas
function redraw(){
    
  for(var i=0; i < clickX.length; i++)
  {  
    context.beginPath();
    if(clickDrag[i] && i){
      context.moveTo(clickX[i-1], clickY[i-1]);
     }else{
       context.moveTo(clickX[i]-1, clickY[i]);
     }
     context.lineTo(clickX[i], clickY[i]);
     context.closePath();
     context.stroke();
  }
}

// this is called when "clear canvas" button is pressed
function clearCanvas()
{
    // both these lines are required to clear the canvas properly in all browsers
    context.clearRect(0,0,canvas.width,canvas.height);
    canvas.width = canvas.width;
    
    // we need to flush the arrays too
    clickX = new Array();
    clickY = new Array();
    clickDrag = new Array();
}

/* Two simple functions, they just assign the selected color and size 
to the canvas object properties */ 
function setColor(col)
{
    context.strokeStyle = col;
}

function setSize(px)
{
    context.lineWidth=px;
}

/* Finally this will send your image to the server-side script which will 
save it to the database or where ever you want it saved.
Note that this function should be called when the button in your save 
form is pressed. The variable frm is the form object. 
Basically the HTML will look like this:
<input type="button" value="Save Drawing" onclick="saveDrawing(this.form);">
 */
function saveDrawing(frm)
{       
    // converting the canvas to data URI
    var strImageData = canvas.toDataURL();  
        
    $.ajax({
        url: "", /* You need to enter the URL of your server side script*/
        type: "post",
          /* add the other variables here or serialize the entire form. 
          Image data must be URI encoded */
        data: "save=1&pic="+encodeURIComponent(strImageData), 
        success: function(msg)
        {
           // display some message and/or redirect
        }
    });
}

Your server side script

It really depends on you. In the Draw a Robot site we just save the robots to the database and then display them in a gallery. You can save the image data in a blob field in the DB. Just have in mind that data / URI images are displayed with the following HTML code:

<img src="encoded image data">