Theme:
Processing...
Description & Source Code
  • Description
  • View
    zk_calendar.zul
    calendar_editor.zul
    style.css
    scroll.js
  • Controller
    CalendarController.java
    QueueMessage.java
  • View Model
  • Model
    DemoCalendarData.java
    DemoCalendarEvent.java
    DemoCalendarModel.java

ZK Calendar is an Ajax component readily embeddable into any Java web application. In this demo, it's shown as a standalone, Google Calendar like application; offering functionalities such as filtering, detailed scheduling, multiple time zones, and daily/weekly/monthly view. Properties such as format and style can be easily configured by setting the calendar component's attributes. The main task for the developers is to handle the calendar events and preparing the event data model; similar to how other data components work in ZK.
Full Demo : ZK Calendar Demo
Doc : ZK Calendar Essentials

This feature requires ZK EE. It can also be used independently under commerical or GPL license.
zk_calendar.zul
<zk>
	<style src="/widgets/zk_calendar/zk_calendar/style.css" />
	<window>
		<!-- Calendars Body -->
		<div apply="demo.app.zk_calendar.CalendarController">
			<!-- Control Menu -->
			<hlayout sclass="z-valign-middle">
				<button id="today" label="Today" />
				<button id="prev" image="/widgets/zk_calendar/zk_calendar/img/arrow-180.png"/>
				<button id="next" image="/widgets/zk_calendar/zk_calendar/img/arrow.png"/>
				<separator width="50px" />
				<button id="pageDay" label="Day" width="55px" />
				<button id="pageWeek" label="Week" width="55px"/>
				<button id="pageMonth" label="Month" width="55px"/>
				<separator width="50px" />
				Filter :
				<textbox id="filter"/>
				<button id="applyFilter" label="Apply"/>
				<button id="resetFilter" label="Reset"/>
			</hlayout>
			<separator bar="true" height="20px"/>
		
			<calendars id="calendars" firstDayOfWeek="Sunday" height="600px" 
				timeZone="Main=GMT+0" mold="month"/>
		</div>

		<!-- Create/Update Event Popup -->
		<include src="/widgets/zk_calendar/zk_calendar/calendar_editor.zul" />

		<!-- Server+Client Fusion - Mouse Scroll Handling -->
		<script type="text/javascript"
			src="/js/lib/jquery.mousewheel.min.js" />
		<script type="text/javascript"
			src="/widgets/zk_calendar/zk_calendar/scroll.js" />
	</window>
</zk>
calendar_editor.zul
<div apply="org.zkoss.bind.BindComposer"
	viewModel="@id('vm') @init('demo.app.zk_calendar.CalendarEditorViewModel')"
	validationMessages="@id('vmsgs')">
	<window title="Create Calendar Event" border="normal" width="400px" 
		form="@id('fx') @load(vm.calendarEvent) @save(vm.calendarEvent, before='ok') @validator(vm.dateValidator)"
		allDay="@ref(vm.isAllDay(fx.beginDate,fx.endDate))"
		mode="popup" visible="@load(vm.visible)" position="center,center" >
		<caption>
			Lock this event : <checkbox checked="@bind(fx.locked)" />
			All Day: <checkbox checked="@load(allDay)" disabled="true" />
		</caption>
		<grid hflex='1'>
			<columns>
				<column width="100px" align="right" />
				<column />
			</columns>
			<rows>
				<row>
					BeginDate:
					<div>
						<datebox hflex="true" locale="en" timeZone="GMT+0"
							format="@load(allDay ? 'long' : 'long+medium')" 
							value="@bind(fx.beginDate)" errorMessage="@load(vmsgs.beginDate)" />
					</div>
				</row>
				<row>
					EndDate:
					<div>
						<datebox hflex="true" locale="en" timeZone="GMT+0"
							format="@load(allDay ? 'long' : 'long+medium')"
							value="@bind(fx.endDate)" errorMessage="@load(vmsgs.endDate)"/>
					</div>
				</row>
				<row>
					Color:
					<hlayout sclass="z-valign-middle">
						Border -
						<colorbox value="@bind(fx.headerColor)" height="20px" />
						Content -
						<colorbox value="@bind(fx.contentColor)" height="20px" />
					</hlayout>
	
				</row>
				<row>
					Title:
					<textbox multiline="true" rows="3" width="97%"
						value="@bind(fx.content)" />
				</row>
				<row>
					<cell colspan="2" style="text-align:center;">
						<button label="OK" onClick="@command('ok')" width="80px" />
						<button label="Cancel" onClick="@command('cancel')" width="80px" />
						<button label="Delete" onClick="@command('delete')" width="80px" />
					</cell>
				</row>
			</rows>
		</grid>
	</window>
</div>
style.css
.selectedButton .z-button-cm {
	font-weight: bold;
}

.chooser-popup div.z-popup-cl {
	border: none;
}

.chooser-popup .z-popup-cnt {
	padding: 0;
}

.chooser-popup .z-groupbox {
	-moz-border-radius: 1px 1px 1px 1px;
}
scroll.js
var calenderWidget; //ZK Calendar client side widget
zk.afterMount(function() {
	calenderWidget = zk.Widget.$(jq("$calendars")); // ZK id selector
	//Mouse wheel Handle - change month 
	jq('$calendars').bind(
		'mousewheel',
		function(event, delta) {
			if (calenderWidget.getMold() == "month") { //Only month mold need this 
				zAu.send(new zk.Event(calenderWidget, 'onMoveViewDate',delta > 0 ? 1 : -1));
					return false;
			}
			return true;
		}
	);
});
CalendarController.java
package demo.app.zk_calendar;

import java.util.Calendar;
import java.util.TimeZone;

import org.zkoss.calendar.Calendars;
import org.zkoss.calendar.event.CalendarsEvent;
import org.zkoss.zk.ui.Component;
import org.zkoss.zk.ui.event.Event;
import org.zkoss.zk.ui.select.SelectorComposer;
import org.zkoss.zk.ui.select.annotation.Listen;
import org.zkoss.zk.ui.select.annotation.Wire;
import org.zkoss.zkmax.ui.select.annotation.Subscribe;
import org.zkoss.zul.Textbox;


public class CalendarController extends SelectorComposer<Component> {

	private static final long serialVersionUID = 1L;

	@Wire
	private Calendars calendars;
	@Wire
	private Textbox filter;
	
	private DemoCalendarModel calendarModel;
	
	//the in editing calendar ui event
	private CalendarsEvent calendarsEvent = null;

	@Override
	public void doAfterCompose(Component comp) throws Exception {
		super.doAfterCompose(comp);
		calendarModel = new DemoCalendarModel(new DemoCalendarData().getCalendarEvents());
		calendars.setModel(this.calendarModel);
	}
	
	//control the calendar position
	@Listen("onClick = #today")
	public void gotoToday(){
		TimeZone timeZone = calendars.getDefaultTimeZone();
		calendars.setCurrentDate(Calendar.getInstance(timeZone).getTime());
	}
	@Listen("onClick = #next")
	public void gotoNext(){
		calendars.nextPage();
	}
	@Listen("onClick = #prev")
	public void gotoPrev(){
		calendars.previousPage();
	}
	
	//control page display
	@Listen("onClick = #pageDay")
	public void changeToDay(){
		calendars.setMold("default");
		calendars.setDays(1);
	}
	@Listen("onClick = #pageWeek")
	public void changeToWeek(){
		calendars.setMold("default");
		calendars.setDays(7);
	}
	@Listen("onClick = #pageMonth")
	public void changeToYear(){
		calendars.setMold("month");
	}
	
	//control the filter
	@Listen("onClick = #applyFilter")
	public void applyFilter(){
		calendarModel.setFilterText(filter.getValue());
		calendars.setModel(calendarModel);
	}
	@Listen("onClick = #resetFilter")
	public void resetFilter(){
		filter.setText("");
		calendarModel.setFilterText("");
		calendars.setModel(calendarModel);
	}

	//listen to the calendar-create and edit of a event data
	@Listen("onEventCreate = #calendars; onEventEdit = #calendars")
	public void createEvent(CalendarsEvent event) {
		calendarsEvent = event;
		
		//to display a shadow when editing
		calendarsEvent.stopClearGhost();
		
		DemoCalendarEvent data = (DemoCalendarEvent)event.getCalendarEvent();
		
		if(data == null) {
			data = new DemoCalendarEvent();
			data.setHeaderColor("#3366ff");
			data.setContentColor("#6699ff");
			data.setBeginDate(event.getBeginDate());
			data.setEndDate(event.getEndDate());
		} else {
			data = (DemoCalendarEvent) event.getCalendarEvent();
		}
		//notify the editor
		QueueUtil.lookupQueue().publish(
				new QueueMessage(QueueMessage.Type.EDIT,data));
	}
	
	//listen to the calendar-update of event data, usually send when user drag the event data 
	@Listen("onEventUpdate = #calendars")
	public void updateEvent(CalendarsEvent event) {
		DemoCalendarEvent data = (DemoCalendarEvent) event.getCalendarEvent();
		data.setBeginDate(event.getBeginDate());
		data.setEndDate(event.getEndDate());
		calendarModel.update(data);
	}
	
	//listen to queue message from other controller
	@Subscribe(value = QueueUtil.QUEUE_NAME)
	public void handleQueueMessage(Event event) {
		if(!(event instanceof QueueMessage)) {
			return;
		} 
		QueueMessage message = (QueueMessage)event;
		switch(message.getType()){
		case DELETE:
			calendarModel.remove((DemoCalendarEvent)message.getData());
			//clear the shadow of the event after editing
			calendarsEvent.clearGhost(); 
			calendarsEvent = null;
			break;
		case OK:
			if (calendarModel.indexOf((DemoCalendarEvent)message.getData()) >= 0) {
				calendarModel.update((DemoCalendarEvent)message.getData());
			} else {
				calendarModel.add((DemoCalendarEvent)message.getData());
			}
		case CANCEL:
			//clear the shadow of the event after editing
			calendarsEvent.clearGhost();
			calendarsEvent = null;
			break;
		}
	}	
}
QueueMessage.java
package demo.app.zk_calendar;

import org.zkoss.zk.ui.event.Event;

public class QueueMessage extends Event {
	private static final long serialVersionUID = 1L;

	static public enum Type {
		EDIT, OK, DELETE, CANCEL;
	}
	
	private Type type;
	private Object data;
	
	public QueueMessage(Type type) {
		super("onCalendarMessage");
		this.type = type;
	}
	public QueueMessage(Type type, Object data) {
		this(type);
		this.data = data;
	}

	public Type getType() {
		return type;
	}

	public Object getData() {
		return data;
	}
}
CalendarEditorViewModel.java
package demo.app.zk_calendar;

import java.util.Date;
import java.util.Map;

import org.zkoss.bind.BindUtils;
import org.zkoss.bind.Property;
import org.zkoss.bind.ValidationContext;
import org.zkoss.bind.Validator;
import org.zkoss.bind.annotation.Command;
import org.zkoss.bind.annotation.Init;
import org.zkoss.bind.annotation.NotifyChange;
import org.zkoss.bind.validator.AbstractValidator;
import org.zkoss.zk.ui.event.EventListener;

public class CalendarEditorViewModel {
	
	private DemoCalendarEvent calendarEventData = new DemoCalendarEvent();
	
	private boolean visible = false;

	public DemoCalendarEvent getCalendarEvent() {
		return calendarEventData;
	}

	public boolean isVisible() {
		return visible;
	}

	public void setVisible(boolean visible) {
		this.visible = visible;
	}

	@Init
	public void init() {
		//subscribe a queue, listen to other controller
		QueueUtil.lookupQueue().subscribe(new QueueListener());
	}

	private void startEditing(DemoCalendarEvent calendarEventData) {
		this.calendarEventData = calendarEventData;
		visible = true;
		
		//reload entire view-model data when going to edit
		BindUtils.postNotifyChange(null, null, this, "*");
	}
	

	public boolean isAllDay(Date beginDate,Date endDate) {
		int M_IN_DAY = 1000 * 60 * 60 * 24; 
		boolean allDay = false;
		
		if(beginDate != null && endDate != null) {
			long between = endDate.getTime() - beginDate.getTime();
			allDay = between > M_IN_DAY;
		}
		return allDay;
	}
	
	public Validator getDateValidator(){
		return new AbstractValidator(){
			@Override
			public void validate(ValidationContext ctx) {
				Map<String,Property> formData = ctx.getProperties(ctx.getProperty().getValue());
				Date beginDate = (Date)formData.get("beginDate").getValue();
				Date endDate = (Date)formData.get("endDate").getValue();
				if(beginDate==null){
					addInvalidMessage(ctx, "beginDate","Begin date is empty");
				}
				if(endDate==null){
					addInvalidMessage(ctx, "endDate","End date is empty");
				}
				if(beginDate!=null && endDate!=null && beginDate.compareTo(endDate) >= 0){
					addInvalidMessage(ctx, "endDate","End date is before begin date");
				}
			}
		};
	}

	@Command
	@NotifyChange("visible")
	public void cancel() {
		QueueMessage message = new QueueMessage(QueueMessage.Type.CANCEL);
		QueueUtil.lookupQueue().publish(message);
		this.visible = false;
	}

	@Command
	@NotifyChange("visible")
	public void delete() {
		QueueMessage message = new QueueMessage(QueueMessage.Type.DELETE, calendarEventData);
		QueueUtil.lookupQueue().publish(message);
		this.visible = false;
	}

	@Command
	@NotifyChange("visible")
	public void ok() {
		QueueMessage message = new QueueMessage(QueueMessage.Type.OK, calendarEventData);
		QueueUtil.lookupQueue().publish(message);
		this.visible = false;
	}

	private class QueueListener implements
			EventListener<QueueMessage> {
		@Override
		public void onEvent(QueueMessage message)
				throws Exception {
			if (QueueMessage.Type.EDIT.equals(message.getType())){
				CalendarEditorViewModel.this.startEditing((DemoCalendarEvent)message.getData());
			}
		}
	}
}
DemoCalendarData.java
package demo.app.zk_calendar;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.LinkedList;
import java.util.List;

import org.zkoss.calendar.api.CalendarEvent;


public class DemoCalendarData {

	private List<CalendarEvent> calendarEvents = new LinkedList<CalendarEvent>();
	private final SimpleDateFormat DATA_FORMAT = new SimpleDateFormat("yyyy/MM/dd HH:mm");
	private Calendar cal = Calendar.getInstance();

	public DemoCalendarData() {
		init();
	}

	private void init() {
		int mod = cal.get(Calendar.MONTH) + 1;
		int year = cal.get(Calendar.YEAR);
		String date2 = mod > 9 ? year + "/" + mod + "" : year + "/" + "0" + mod;
		String date1 = --mod > 9 ? year + "/" + mod + "" : year + "/" + "0" + mod;
		++mod;
		String date3 = ++mod > 9 ? year + "/" + mod + "" : year + "/" + "0" + mod;
		// Red Events
		calendarEvents.add(new DemoCalendarEvent(getDate(date1 + "/28 00:00"), getDate(date1 + "/29 00:00"), "#A32929", "#D96666", "ZK Jet Released"));
		calendarEvents.add(new DemoCalendarEvent(getDate(date1 + "/04 02:00"), getDate(date1 + "/05 03:00"), "#A32929", "#D96666", "Experience ZK SpreadSheet Live Demo!"));
		calendarEvents.add(new DemoCalendarEvent(getDate(date2 + "/21 05:00"), getDate(date2 + "/21 07:00"), "#A32929", "#D96666", "New Features of ZK Spreadsheet"));
		calendarEvents.add(new DemoCalendarEvent(getDate(date2 + "/08 00:00"), getDate(date2 + "/09 00:00"), "#A32929", "#D96666", "ZK Spreadsheet Released"));
		// Blue Events
		calendarEvents.add(new DemoCalendarEvent(getDate(date1 + "/29 03:00"), getDate(date2 + "/02 06:00"), "#3467CE", "#668CD9", "ZK Released"));
		calendarEvents.add(new DemoCalendarEvent(getDate(date2 + "/02 10:00"), getDate(date2 + "/02 12:30"), "#3467CE", "#668CD9", "New Feature of ZK "));
		calendarEvents.add(new DemoCalendarEvent(getDate(date2 + "/17 14:00"), getDate(date2 + "/18 16:00"), "#3467CE", "#668CD9", "Case Study - Mecatena"));
		calendarEvents.add(new DemoCalendarEvent(getDate(date3 + "/01 14:30"), getDate(date3 + "/01 17:30"), "#3467CE", "#668CD9", "ZK Unit Testing Project - zunit"));
		// Purple Events
		calendarEvents.add(new DemoCalendarEvent(getDate(date1 + "/29 08:00"), getDate(date2 + "/03 12:00"), "#7A367A", "#B373B3", "ZK Studio released"));
		calendarEvents.add(new DemoCalendarEvent(getDate(date2 + "/07 08:00"), getDate(date2 + "/07 12:00"), "#7A367A", "#B373B3", "Tutorial : Reading from the DB with Netbeans and ZK"));
		calendarEvents.add(new DemoCalendarEvent(getDate(date2 + "/13 11:00"), getDate(date2 + "/13 14:30"), "#7A367A", "#B373B3", "Small talk - ZK Charts"));
		calendarEvents.add(new DemoCalendarEvent(getDate(date2 + "/16 14:00"), getDate(date2 + "/18 16:00"), "#7A367A", "#B373B3", "Style Guide for ZK released !"));
		calendarEvents.add(new DemoCalendarEvent(getDate(date3 + "/02 12:00"), getDate(date3 + "/02 17:00"), "#7A367A", "#B373B3", "Small talk -- Simple Database Access From ZK"));
		// Khaki Events
		calendarEvents.add(new DemoCalendarEvent(getDate(date1 + "/03 00:00"), getDate(date1 + "/04 00:00"), "#88880E", "#BFBF4D", "ZK UK User Group"));
		calendarEvents.add(new DemoCalendarEvent(getDate(date2 + "/13 05:00"), getDate(date2 + "/13 07:00"), "#88880E", "#BFBF4D", "How to Test ZK Application with Selenium"));
		calendarEvents.add(new DemoCalendarEvent(getDate(date2 + "/24 19:30"), getDate(date2 + "/24 20:00"), "#88880E", "#BFBF4D", "ZK Alfresco Talk"));
		calendarEvents.add(new DemoCalendarEvent(getDate(date3 + "/03 00:00"), getDate(date3 + "/04 00:00"), "#88880E", "#BFBF4D", "ZK selected as SourceForge.net Project of the Month"));
		// Green Events
		calendarEvents.add(new DemoCalendarEvent(getDate(date1 + "/28 10:00"), getDate(date1 + "/28 12:30"), "#0D7813", "#4CB052", "ZK Mobile Released"));
		calendarEvents.add(new DemoCalendarEvent(getDate(date2 + "/03 00:00"), getDate(date2 + "/03 05:30"), "#0D7813", "#4CB052", "ZK Gmaps released"));
		calendarEvents.add(new DemoCalendarEvent(getDate(date2 + "/05 20:30"), getDate(date2 + "/06 00:00"), "#0D7813", "#4CB052", "Refresh with Five New ZK Themes!"));
		calendarEvents.add(new DemoCalendarEvent(getDate(date2 + "/23 00:00"), getDate(date2 + "/25 16:30"), "#0D7813", "#4CB052", "ZK Roadmap Announced"));
		calendarEvents.add(new DemoCalendarEvent(getDate(date3 + "/01 08:30"), getDate(date3 + "/01 19:30"), "#0D7813", "#4CB052", "Build Database CRUD Application in 6 Steps"));
	}

	private Date getDate(String dateText) {
		try {
			return DATA_FORMAT.parse(dateText);
		} catch (ParseException e) {
			e.printStackTrace();
		}
		return null;
	}

	public List<CalendarEvent> getCalendarEvents() {
		return calendarEvents;
	}
}
DemoCalendarEvent.java
package demo.app.zk_calendar;

import java.util.Date;

import org.zkoss.calendar.impl.SimpleCalendarEvent;

public class DemoCalendarEvent extends SimpleCalendarEvent {
	private static final long serialVersionUID = 1L;

	public DemoCalendarEvent(Date beginDate, Date endDate, String headerColor, String contentColor, String content) {
		setHeaderColor(headerColor);
		setContentColor(contentColor);
		setContent(content);
		setBeginDate(beginDate);
		setEndDate(endDate);
	}

	public DemoCalendarEvent(Date beginDate, Date endDate, String headerColor, String contentColor, String content,
			String title) {
		setHeaderColor(headerColor);
		setContentColor(contentColor);
		setContent(content);
		setTitle(title);
		setBeginDate(beginDate);
		setEndDate(endDate);
	}

	public DemoCalendarEvent(Date beginDate, Date endDate, String headerColor, String contentColor, String content,
			String title, boolean locked) {
		setHeaderColor(headerColor);
		setContentColor(contentColor);
		setContent(content);
		setTitle(title);
		setBeginDate(beginDate);
		setEndDate(endDate);
		setLocked(locked);
	}
	
	public DemoCalendarEvent() {
		setHeaderColor("#FFFFFF");
		setContentColor("#000000");
	}
}
DemoCalendarModel.java
package demo.app.zk_calendar;

import java.util.Date;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;

import org.zkoss.calendar.api.CalendarEvent;
import org.zkoss.calendar.api.RenderContext;
import org.zkoss.calendar.impl.SimpleCalendarModel;

public class DemoCalendarModel extends SimpleCalendarModel {
	private static final long serialVersionUID = 1L;
	
	private String filterText = "";

	public DemoCalendarModel(List<CalendarEvent> calendarEvents) {
		super(calendarEvents);
	}

	public void setFilterText(String filterText) {
		this.filterText = filterText;
	}

	@Override
	public List<CalendarEvent> get(Date beginDate, Date endDate, RenderContext rc) {
		List<CalendarEvent> list = new LinkedList<CalendarEvent>();
		long begin = beginDate.getTime();
		long end = endDate.getTime();
				
		for (Iterator<?> itr = _list.iterator(); itr.hasNext();) {
			Object obj = itr.next();
			CalendarEvent ce = obj instanceof CalendarEvent ? (CalendarEvent)obj : null;
			
			if(ce == null) break;
			
			long b = ce.getBeginDate().getTime();
			long e = ce.getEndDate().getTime();
			if (e >= begin && b < end && ce.getContent().toLowerCase().contains(filterText.toLowerCase()))
				list.add(ce);
		}
		return list;
	}

}