name + " ~~" + ""value=\"" + (hoursBooked[6] ? hoursBooked[6].hours : "0") + "\" onBlur=\"timekeeper.saveTimesheetItem('" + project.id + "', 6, this.value);\"\">~~" + ""value=\"" + (hoursBooked[5] ? hoursBooked[5].hours : "0") + "\" onBlur=\"timekeeper.saveTimesheetItem('" + project.id + "', 5, this.value);\"\">~~" + ...SNIP... projectHours ], cellFuncs, { escapeHtml : false, rowCreator : function(inOptions) { var tr = document.createElement("tr"); tr.setAttribute("class", "cssTableRow"); tr.setAttribute("valign", "middle"); CHAPTER 9 n TIMEKEEPER: DWR EVEN MAKES PROJECT MANAGEMENT FUN! 498 return tr; } } ); } It starts with cellFuncs, same as always. Next is a check to see whether the current project is one the user is a project administrator for or is assigned to. Those are the only cases where a user should be able to book time to a project (an administrator, we assume, doesn??™t automatically work on projects; he or she would have to be assigned like any other user would). Once that??™s determined, it??™s on with the work. That work begins with getting the number of hours booked to this project for this period and this user, and also calculating the total hours for this project in this period.