/*
* Copyright 2007-2008 WorldWide Conferencing, LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions
* and limitations under the License.
*/
package net.liftweb.widgets.calendars;
import _root_.scala.xml._
import _root_.java.util.{Calendar, Locale}
import _root_.java.util.Calendar._
import _root_.java.text.SimpleDateFormat
import _root_.net.liftweb.util.Helpers._
import _root_.net.liftweb.util.{Box, Full, Empty}
import _root_.net.liftweb.http.{LiftRules}
import _root_.net.liftweb.http.js._
import _root_.net.liftweb.http.js.jquery._
import _root_.net.liftweb.http.SHtml._
import JsCmds._
import JE._
import JqJsCmds._
import JqJE._
object CalendarMonthView {
/**
* Call this function typically in boot
*/
def init() {
import _root_.net.liftweb.http.ResourceServer
ResourceServer.allow({
case "calendars" :: tail => true
case "common" :: _ => true
})
}
def apply(when: Calendar,
calendars: Seq[CalendarItem],
itemClick: Box[AnonFunc],
dayClick: Box[AnonFunc],
weekClick: Box[AnonFunc]) = new CalendarMonthView(when).render(calendars, itemClick, dayClick, weekClick)
def apply(when: Calendar,
meta: MonthViewMeta,
calendars: Seq[CalendarItem],
itemClick: Box[AnonFunc],
dayClick: Box[AnonFunc],
weekClick: Box[AnonFunc]) = new CalendarMonthView(when, meta) render(calendars, itemClick, dayClick, weekClick)
}
/**
* CalendarMonthView renders a month view representation of a collection of CalendarItem
* <br>
* Usage example - assume CalendarView is a typical LiftWeb snippet
* <pre>
* class CalendarView {
*
* def render(html: Group) : NodeSeq = {
* val c = Calendar getInstance;
* c.set(MONTH, 4)
* bind("cal", html,
* "widget" --> CalendarMonthView(c, makeCals, itemClick, dayClick, weekClick)
* )
* }
*
* import JE._
* import JsCmds._
*
* def itemClick = Full(AnonFunc("elem, param", JsRaw("alert(param + ' - ' + elem.nodeName)")))
* def dayClick = Full(AnonFunc("elem, param", JsRaw("alert(param + ' - ' + elem.nodeName)")))
* def weekClick = Full(AnonFunc("elem, param", JsRaw("alert(param + ' - ' + elem.nodeName)")))
*
*
* private def makeCals = {
* val c1 = Calendar getInstance
* val c2 = Calendar getInstance;
* val c3 = Calendar getInstance;
*
* c2.set(DAY_OF_MONTH, 3)
* c3.set(DAY_OF_MONTH, 1)
* c3.set(MONTH, 4)
*
* val item1 = CalendarItem(...)
* val item2 = CalendarItem(...)
* val item3 = CalendarItem(...)
*
* item1 :: item2 :: item3 :: Nil
* }
* }
*
* </pre>
*
* @param when - the Calendar object describing the month that needs tobe rendered
*
*/
class CalendarMonthView(val when: Calendar, val meta: MonthViewMeta) {
def this(when: Calendar) = this(when, MonthViewMeta(MONDAY, Locale getDefault))
/**
* Returns the markup for rendering the calendar month view
*
* @param calendars - the calendar items than need to be rendered
* @param itemClick - Ajax function to be called when a calendar item was clicked.
* It takes two parameters: elem the node that was clicked and
* param the identifier if this CalendarItem
* @param dayClick - Ajax function to be called when a day number(cell header) item was clicked
* It takes two parameters: elem the node that was clicked and
* param the date of the clicked day in MM/dd/yyyy format
* @param weekClick - Ajax function to be called when a day number(cell header) item was clicked
* It takes two parameters: elem the node that was clicked and
* the week number
* @return NodeSeq - the markup to be rendered
*/
def render(calendars: Seq[CalendarItem],
itemClick: Box[AnonFunc],
dayClick: Box[AnonFunc],
weekClick: Box[AnonFunc]): NodeSeq = {
def makeCells(calendar: Calendar): NodeSeq = {
def predicate (current: Calendar, c: CalendarItem) = {
// Adjust the precision
current.set(MILLISECOND, c.start.get(MILLISECOND))
current.set(SECOND, c.start.get(SECOND))
current.set(MINUTE, c.start.get(MINUTE))
current.set(HOUR_OF_DAY, c.start.get(HOUR_OF_DAY))
c end match {
case Full(end) => {
val crt = current getTimeInMillis;
(crt >= c.start.getTimeInMillis) && (crt <= end.getTimeInMillis)
}
case _ => current.get(DAY_OF_MONTH) >= c.start.get(DAY_OF_MONTH) && current.get(MONTH) >= c.start.get(MONTH)
}
}
val thisMonth = when get(MONTH)
val cal = calendar.clone().asInstanceOf[Calendar]
val today = Calendar getInstance (meta locale)
(0 to 5) map (row => <tr><td wk={cal get(WEEK_OF_YEAR) toString}
class="cellWeek"
onclick={JsFunc("weekClick", JsRaw("this"), Jq(JsRaw("this")) >> JqGetAttr("wk")).toJsCmd}>
{cal get(WEEK_OF_YEAR)}</td>{(0 to 6) map (col =>
try{
<td>{
val day = cal.get(DAY_OF_MONTH)
val month = cal.get(MONTH)
val isToday = today.get(DAY_OF_MONTH) == cal.get(DAY_OF_MONTH) && (month == today.get(MONTH))
val div = <div>{
calendars filter (c => predicate(cal, c)) map (c => {
val r = <div class="calendarItem"><a href="#">{
<span>{timeFormatter format(c.start.getTime)} {c.subject openOr "..."}</span>
}</a></div> %
("id" -> c.id) %
("onclick" -> JsFunc("itemClick", JsRaw("this"), Jq(JsRaw("this")) >> JqGetAttr("id")).toJsCmd)
c.description map (desc => r % (("title" -> desc))) openOr r
}
)
}</div>
val (head, cell) = isToday match {
case true => ("cellHeadToday", "cellBodyToday")
case _ => (month != thisMonth) match {
case true => ("cellHeadOtherMonth", "cellBodyOtherMonth")
case _ => ("cellHead", "cellBody")
}
}
Group(<div>{day}</div> %
("class" -> head) ::
div % ("class" -> cell) :: Nil)
}</td> % ("date" -> (dateFormatter format(cal getTime))) %
("onclick" -> JsFunc("dayClick", JsRaw("this"), (Jq(JsRaw("this")) >> JqGetAttr("date"))).toJsCmd)
} finally {
cal add(DAY_OF_MONTH, 1)
}
)
}</tr>)
}
def makeHead(headCal: Calendar) = <tr><td></td>{
(0 to 6) map(x => <td width="14%">{
try{
meta.weekDaysFormatter format(headCal getTime)
} finally {
headCal add(DAY_OF_MONTH, 1)
}
}</td>)
}</tr>
val cal = when.clone().asInstanceOf[Calendar]
cal set(DAY_OF_MONTH, 1)
val delta = cal.get(DAY_OF_WEEK) - meta.firstDayOfWeek
cal add(DAY_OF_MONTH, if (delta < 0) -delta-7 else -delta)
val headCal = cal.clone().asInstanceOf[Calendar]
val init = JsRaw("""
jQuery(document).ready(function() {
jQuery('.calendarItem').click(function(e){
e.stopPropagation();
});
jQuery('.calendarItem').tooltip({
track: true,
delay: 0,
showURL: false
});
})
""") &
JsCrVar("itemClick", itemClick openOr JsRaw("function(param){}")) &
JsCrVar("dayClick", dayClick openOr JsRaw("function(param){}")) &
JsCrVar("weekClick", weekClick openOr JsRaw("function(param){}"))
<head>
<link rel="stylesheet" href={"/" + LiftRules.resourceServerPath + "/calendars/monthview/style.css"} type="text/css"/>
<script type="text/javascript" src={"/" + LiftRules.resourceServerPath + "/common/jquery.dimensions.js"}></script>
<script type="text/javascript" src={"/" + LiftRules.resourceServerPath + "/common/jquery.bgiframe.js"}></script>
<script type="text/javascript" src={"/" + LiftRules.resourceServerPath + "/common/jquery.tooltip.js"}></script>
<script type="text/javascript" charset="utf-8">{Unparsed(init toJsCmd)}</script>
</head>
<div class="monthView">{
<table width="100%" cellspacing="1" cellpadding="0" style="table-layout: fixed;" class="topHead">
{makeHead(headCal)}
{makeCells(cal)}
</table>
}</div>
}
}