/*
* Copyright 2005 Wicket Stuff
*
* 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 com.googlecode.wicketwebbeans.datepicker;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Locale;
import java.util.Map;
import org.apache.wicket.ResourceReference;
import org.apache.wicket.markup.ComponentTag;
import org.apache.wicket.markup.MarkupStream;
import org.apache.wicket.markup.html.WebComponent;
import org.apache.wicket.markup.html.panel.Panel;
import org.apache.wicket.markup.html.resources.CompressedResourceReference;
import org.apache.wicket.markup.html.resources.JavaScriptReference;
import org.apache.wicket.markup.html.resources.StyleSheetReference;
import org.apache.wicket.model.Model;
import org.apache.wicket.util.convert.converters.DateConverter;
import org.apache.wicket.util.string.AppendingStringBuffer;
/**
* Datepicker component. It has two modes: A {@link PopupDatePicker popup} and a
* {@link FlatDatePicker flat} mode.
* <p>
* Customize the looks, localization etc of the datepicker by providing a custom
* {@link wicket.extensions.markup.html.datepicker.DatePickerSettings} object.
* </p>
* <p>
* This component is based on Dynarch's JSCalendar component, which can be found
* at <a href="http://www.dynarch.com/">the Dynarch site</a>.
* </p>
*
* @see wicket.extensions.markup.html.datepicker.DatePickerSettings
* @author Frank Bille Jensen
* @author Eelco Hillenius
* @author Mihai Bazon (creator of the JSCalendar component)
*/
public abstract class DatePicker extends Panel
{
/**
* Outputs the Javascript initialization code.
*/
private final class InitScript extends WebComponent
{
private static final long serialVersionUID = 1L;
/**
* Construct.
*
* @param id
* component id
*/
public InitScript(String id)
{
super(id);
}
/**
* @see wicket.Component#onComponentTagBody(wicket.markup.MarkupStream,
* wicket.markup.ComponentTag)
*/
@Override
public void onComponentTagBody(final MarkupStream markupStream,
final ComponentTag openTag)
{
replaceComponentTagBody(markupStream, openTag, getInitScript());
}
}
protected abstract class CallbackScript extends WebComponent
{
private static final long serialVersionUID = 1L;
private ISelectCallback selectCallback;
/**
* Construct.
*
* @param id
* Component id.
* @param selectCallback
*/
public CallbackScript(String id, ISelectCallback selectCallback)
{
super(id);
this.selectCallback = selectCallback;
if (selectCallback != null)
{
selectCallback.bind(this);
}
}
public abstract String getCallbackFunctionName();
@Override
public boolean isVisible()
{
return selectCallback != null;
}
@Override
public void onComponentTagBody(MarkupStream markupStream, ComponentTag openTag)
{
if (selectCallback != null)
{
AppendingStringBuffer b = new AppendingStringBuffer("\nfunction ");
String functionName = getCallbackFunctionName();
b.append(functionName).append("(calendar){\n");
b.append("\tvar dateParam = calendar.date.getFullYear();\n");
b.append("\tdateParam += '-';\n");
b.append("\tdateParam += calendar.date.getMonth()+1;\n");
b.append("\tdateParam += '-';\n");
b.append("\tdateParam += calendar.date.getDate();\n");
b.append("\tdateParam += '-';\n");
b.append("\tdateParam += calendar.date.getHours();\n");
b.append("\tdateParam += '-';\n");
b.append("\tdateParam += calendar.date.getMinutes();\n");
CharSequence handleCallback = selectCallback.handleCallback();
b.append("\t").append(handleCallback).append("\n");
b.append("}\n");
replaceComponentTagBody(markupStream, openTag, b);
}
}
}
private static final long serialVersionUID = 1L;
private static final ResourceReference JAVASCRIPT = new CompressedResourceReference(
DatePicker.class, "calendar.js");
private static final ResourceReference JAVASCRIPT_SETUP = new CompressedResourceReference(
DatePicker.class, "calendar-setup.js");
/** settings for the javascript datepicker component. */
private final DatePickerSettings settings;
private DateConverter dateConverter;
private CallbackScript onSelect;
private CallbackScript onClose;
private CallbackScript onUpdate;
private Date defaultDate;;
public DatePicker(final String id, final DatePickerSettings settings)
{
this(id, settings, null);
}
public DatePicker(final String id, final DatePickerSettings settings, Date defaultDate)
{
super(id);
if (settings == null)
{
throw new IllegalArgumentException(
"Settings must be non null when using this constructor");
}
this.settings = settings;
this.defaultDate = defaultDate;
add(new InitScript("script"));
add(new JavaScriptReference("calendarMain", JAVASCRIPT));
add(new JavaScriptReference("calendarSetup", JAVASCRIPT_SETUP));
add(new JavaScriptReference("calendarLanguage", new Model<ResourceReference>()
{
private static final long serialVersionUID = 1L;
@Override
public ResourceReference getObject()
{
return settings.getLanguage(DatePicker.this.getLocale());
}
}));
add(new StyleSheetReference("calendarStyle", settings.getStyle()));
// Add callbacks
add(onSelect = new CallbackScript("onSelect", settings.getOnSelect())
{
private static final long serialVersionUID = 1L;
public String getCallbackFunctionName()
{
return DatePicker.this.getMarkupId() + "_onselect";
}
});
add(onClose = new CallbackScript("onClose", settings.getOnClose())
{
private static final long serialVersionUID = 1L;
public String getCallbackFunctionName()
{
return DatePicker.this.getMarkupId() + "_onclose";
}
});
add(onUpdate = new CallbackScript("onUpdate", settings.getOnUpdate())
{
private static final long serialVersionUID = 1L;
public String getCallbackFunctionName()
{
return DatePicker.this.getMarkupId() + "_onupdate";
}
});
}
/**
* Sets the date converter to use for generating the javascript format
* string. If this is not set or set to null the default DateConverter will
* be used.
*
* @param dateConverter
*/
public void setDateConverter(DateConverter dateConverter)
{
this.dateConverter = dateConverter;
}
/**
* Gets the initilization javascript.
*
* @return the initilization javascript
*/
private CharSequence getInitScript()
{
Map<String, String> additionalSettings = new HashMap<String, String>();
appendSettings(additionalSettings);
AppendingStringBuffer b = new AppendingStringBuffer();
if (defaultDate != null)
{
Calendar calendar = Calendar.getInstance();
calendar.setTime(defaultDate);
b.append("\nvar defaultDate = new Date();");
b.append("\ndefaultDate.setFullYear(").append(calendar.get(Calendar.YEAR)).append(");");
b.append("\ndefaultDate.setDate(").append(calendar.get(Calendar.DAY_OF_MONTH)).append(
");");
b.append("\ndefaultDate.setMonth(").append(calendar.get(Calendar.MONTH)).append(");");
b.append("\ndefaultDate.setHours(").append(calendar.get(Calendar.HOUR_OF_DAY)).append(
");");
b.append("\ndefaultDate.setMinutes(").append(calendar.get(Calendar.MINUTE)).append(
");\n");
}
b.append("\nCalendar.setup(\n{");
// Append specific settings
for (Iterator iter = additionalSettings.keySet().iterator(); iter.hasNext();)
{
String option = (String)iter.next();
b.append("\n\t\t").append(option).append(" : ").append(additionalSettings.get(option))
.append(",");
}
// Callbacks
if (settings.getOnClose() != null)
{
b.append("\n\t\tonClose : ").append(onClose.getCallbackFunctionName()).append(",");
}
if (settings.getOnSelect() != null)
{
b.append("\n\t\tonSelect : ").append(onSelect.getCallbackFunctionName()).append(",");
}
if (settings.getOnUpdate() != null)
{
b.append("\n\t\tonUpdate : ").append(onUpdate.getCallbackFunctionName()).append(",");
}
if (defaultDate != null)
{
b.append("\n\t\tdate : defaultDate,");
}
String pattern = null;
if (dateConverter == null)
{
dateConverter = getDateConverter();
}
DateFormat df = dateConverter.getDateFormat(getDatePickerLocale());
if (df instanceof SimpleDateFormat)
{
pattern = ((SimpleDateFormat)df).toPattern();
}
b.append(settings.toScript(getDatePickerLocale(), pattern));
int last = b.length() - 1;
if (',' == b.charAt(last))
{
b.deleteCharAt(last);
}
b.append("\n});");
return b;
}
protected abstract void appendSettings(Map<String, String> settings);
protected DateConverter getDateConverter()
{
if (dateConverter == null)
{
dateConverter = new DateConverter();
}
return dateConverter;
}
protected Locale getDatePickerLocale()
{
return getLocale();
}
}