Tuesday, August 5, 2008

Code Review Vol. 1 - Calendar Class in ActionScript 3.0


I'd like to do switch gears for a moment and focus on some code. I'll name these sections, for lack of a better term, Code Review and I'll number them in volumes for ease of reference. The template that I'll follow will be as follows: whole numbers are the full source code, dot releases will be a walk through of the code. So for example this is Code Review Vol. 1 and I will follow up this post with a code walk through that I will title Code Review Vol. 1.01

Here's something interesting that I made recently in ActionScript 3.0. It's a calendar that uses the system date as the data source, the Model, and assembles a collection of Days in correct order, back filling and front-filling empty days. In the final implementation of this I have it being interfaced with a Controller that tells it to switch from month, week, or day View and passes it the month or date to render. For the below code I only show the month renderer, because the week renderer is just a subset of the month anyway.

I won't post the full implementation, because it has tween effects, events, and business logic that, if included, would only serve to obfuscate the actual intention of the code review. Also I de-normalized, if you will, re-coupled a bit for readability and succinctness. In total, I pared down a 300 line abstract class to an actual class that is less than 100 lines. Also I reference a Day class which I'm not including right now, but for the purposes of this example it can be thought of as a class that just extends the Sprite class, draws a rectangle and adds a TextField control to itself when it's drawn.


import flash.display.*;
import net.com.calendar.Day;

public class AbstractCalendar extends Sprite
{
private static var _dayIDSequence:Number = 0;

private var _dayHeight:Number = 50;
private var _dayWidth:Number = 50;
private var _startX:Number = 10;
private var _startY:Number = 10;

private const WEEKDAYCOUNT:Number = 7;

private var _bgColor:uint = 0xCCCCCC;
private var _borderColor:uint = 0x666666;
private var _borderSize:uint = 1;

public function AbstractCalendar(month:Number)
{}

private function createDay(today:Date,name:String, display:String, dayNum:Number):Day
{
var tempDay:Day = new Day()
tempDay.name = getNewDayID();
tempDay.setDate(today)
tempDay.setName(name);
tempDay.setDisplay(display);
tempDay.setDayNumber(dayNum)
tempDay.drawDay(_bgColor, _borderSize,_borderColor, _dayWidth, _dayHeight);
tempDay.originalStackIndex = Number(tempDay.name)
return tempDay
}

private function getNewDayID():String
{
return (_dayIDSequence++).toString();
}

public function drawMonth(tempMonth:Number,tempDay:Day, backDay:Day):void{
var currentDay:Number = 1
var dayStepper:Number = 0
var tempDate:Date = new Date(tempMonth + "/"+currentDay+"/" + new Date().getFullYear());
var tempMonthChecker:Number = tempDate.getMonth()
//check for current month to step through each day in the month
while(tempDate.getMonth() + 1 == tempMonth){

tempDate = new Date(tempMonth + "/"+currentDay+"/" + new Date().getFullYear());
tempMonthChecker = tempDate.getMonth()

// check for day num, at each zero
// reset startX back to 0
if(tempDate.getDay() == 0){ _startX = 10}

//figure out the day number for the first day only
if(tempDate.getDay() > 0 && currentDay == 1 )
{
for(var x:Number = 0;x < tempDate.getDay(); x++)
{
//backfill previous days
backDay = createDay(tempDate,"","",x)
backDay.x = _startX
backDay.y = _startY
_startX += _dayWidth
addChild(backDay);
dayStepper++
}
}

if(tempDate.getMonth() + 1 == tempMonth){
tempDay = createDay(tempDate, "",currentDay.toString(),tempDate.getDay())
tempDay.x = _startX
tempDay.y = _startY
_startX += _dayWidth
addChild(tempDay);
dayStepper++

// increment startY by the _dayHeight
if(dayStepper % WEEKDAYCOUNT == 0){_startY += _dayHeight}

currentDay++
}else{
for(var y:Number = tempDate.getDay(); y < WEEKDAYCOUNT;y++)
{
//front fill the next months days
backDay = createDay(tempDate,"","",x)
backDay.x = _startX
backDay.y = _startY
_startX += _dayWidth
addChild(backDay);
}
}
}
}
}

No comments: