001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: */
017: package org.apache.wicket.extensions.yui.calendar;
018:
019: import java.text.DateFormat;
020: import java.text.SimpleDateFormat;
021: import java.util.Iterator;
022: import java.util.Map;
023: import java.util.Properties;
024: import java.util.Map.Entry;
025:
026: import org.apache.wicket.behavior.HeaderContributor;
027: import org.apache.wicket.behavior.StringHeaderContributor;
028: import org.apache.wicket.extensions.yui.YuiLib;
029: import org.apache.wicket.markup.html.WebComponent;
030: import org.apache.wicket.model.LoadableDetachableModel;
031: import org.apache.wicket.util.string.JavascriptUtils;
032:
033: /**
034: * Abstract calendar component based on the YUI (Yahoo User Interface library)
035: * javascript widget.
036: * <p>
037: * Although this component by itself is fully functional, it doesn't do much
038: * other than just displaying the calendar. Hence, this class is abstract.
039: * </p>
040: * <p>
041: * An easy way to build upon this component is to override
042: * {@link #appendToInit(String, String, String, StringBuffer)} and add event
043: * handlers etc. in the YUI widget's initialization function.
044: * </p>
045: * See <a href="http://developer.yahoo.com/yui/calendar/">YUI's calendar
046: * documentation</a> for more info.
047: *
048: * @author eelcohillenius
049: *
050: * @see CalendarPopup
051: */
052: // TODO provide localization strings (base them on the messages of
053: // JsDatePicker?)
054: public abstract class AbstractCalendar extends WebComponent {
055: /**
056: * Format to be used when configuring YUI calendar. Can be used when using
057: * the "selected" property.
058: */
059: public static final DateFormat FORMAT_DATE = new SimpleDateFormat(
060: "MM/dd/yyyy");
061:
062: /**
063: * For specifying which page (month/year) to show in the calendar, use this
064: * format for the date. This is to be used together with the property
065: * "pagedate"
066: */
067: public static final DateFormat FORMAT_PAGEDATE = new SimpleDateFormat(
068: "MM/yyyy");
069: private static final long serialVersionUID = 1L;
070:
071: /**
072: * Construct. Contributes packaged dependencies.
073: *
074: * @param id
075: * The component id
076: */
077: public AbstractCalendar(String id) {
078: this (id, true);
079: }
080:
081: /**
082: * Construct.
083: *
084: * @param id
085: * The component id
086: * @param contributeDependencies
087: * Whether to contribute the packaged dependencies. Pass false in
088: * case you want to include the dependencies manually in your own
089: * page, e.g. when you want to keep them in your web application
090: * dir. To contribute yourself (in case you want to pass false),
091: * your page header should look like:
092: *
093: * <pre>
094: * <script type="text/javascript" src="yahoo.js"></script>
095: * <script type="text/javascript" src="dom.js"></script>
096: * <script type="text/javascript" src="event.js"></script>
097: * <script type="text/javascript" src="calendar.js"></script>
098: * <link rel="stylesheet" type="text/css" href="calendar.css" />
099: * </pre>
100: */
101: public AbstractCalendar(String id, boolean contributeDependencies) {
102:
103: super (id);
104: setOutputMarkupId(true);
105: if (contributeDependencies) {
106: contributeDependencies();
107: }
108:
109: add(new StringHeaderContributor(new LoadableDetachableModel() {
110:
111: private static final long serialVersionUID = 1L;
112:
113: protected Object load() {
114:
115: // not pretty to look at, but cheaper than using a template
116: String markupId = AbstractCalendar.this .getMarkupId();
117: String javascriptId = getJavascriptId();
118: String javascriptWidgetId = getJavascriptWidgetId();
119: StringBuffer b = new StringBuffer();
120: b.append(JavascriptUtils.SCRIPT_OPEN_TAG);
121: // initialize wicket namespace and register the init function
122: // for the YUI widget
123: b.append("YAHOO.namespace(\"wicket\");\nfunction init");
124: b.append(javascriptId);
125: b.append("() {\n");
126:
127: // instantiate the calendar object
128: b.append(" ");
129: b.append(javascriptWidgetId);
130: b.append(" = new YAHOO.widget.Calendar(\"");
131: b.append(javascriptId);
132: b.append("\",\"");
133: b.append(markupId);
134:
135: Properties p = new Properties();
136: configureWidgetProperties(p);
137: b.append("\", { ");
138: for (Iterator i = p.entrySet().iterator(); i.hasNext();) {
139: Entry entry = (Entry) i.next();
140: b.append(entry.getKey());
141: Object value = entry.getValue();
142: if (value instanceof CharSequence) {
143: b.append(":\"");
144: b.append(value);
145: b.append("\"");
146: } else if (value instanceof CharSequence[]) {
147: b.append(":[");
148: CharSequence[] valueArray = (CharSequence[]) value;
149: for (int j = 0; j < valueArray.length; j++) {
150: CharSequence tmpValue = valueArray[j];
151: b.append("\"");
152: b.append(tmpValue);
153: b.append("\"");
154: if (j < valueArray.length - 1) {
155: b.append(",");
156: }
157: }
158: b.append("]");
159: } else {
160: b.append(":");
161: b.append(value);
162: }
163: // TODO handle arrays
164: if (i.hasNext()) {
165: b.append(",");
166: }
167: }
168:
169: b.append(" });\n");
170:
171: // append the javascript we want for our init function; call
172: // this in an overridable method so that clients can add their
173: // stuff without needing a big ass API
174: appendToInit(markupId, javascriptId,
175: javascriptWidgetId, b);
176:
177: // trigger rendering
178: b.append(" ");
179: b.append(javascriptWidgetId);
180: b.append(".render();\n");
181:
182: b.append("}\n");
183: // register the function for execution when the page is loaded
184: b
185: .append("YAHOO.util.Event.addListener(window, \"load\", init");
186: b.append(javascriptId);
187: b.append(");");
188: b.append(JavascriptUtils.SCRIPT_CLOSE_TAG);
189: return b;
190: }
191: }));
192: }
193:
194: /**
195: * Gets the id of the javascript widget. Note that this is the
196: * non-namespaced id, so depending on what you want to do with it, you may
197: * need to prepend 'YAHOO.wicket.' to it. Or you can call
198: * {@link #getJavascriptWidgetId()}.
199: *
200: * @return The javascript id
201: * @see #getJavascriptWidgetId()
202: */
203: public final String getJavascriptId() {
204: return getMarkupId() + "Js";
205: }
206:
207: /**
208: * The name spaced id of the widget.
209: *
210: * @return The widget id
211: * @see #getJavascriptId()
212: */
213: public final String getJavascriptWidgetId() {
214: return "YAHOO.wicket." + getJavascriptId();
215: }
216:
217: /**
218: * add header contributions for packaged resources.
219: */
220: private void contributeDependencies() {
221: add(HeaderContributor.forJavaScript(YuiLib.class, "yahoo.js"));
222: add(HeaderContributor.forJavaScript(YuiLib.class, "event.js"));
223: add(HeaderContributor.forJavaScript(YuiLib.class, "dom.js"));
224: add(HeaderContributor.forJavaScript(AbstractCalendar.class,
225: "calendar.js"));
226: add(HeaderContributor.forCss(AbstractCalendar.class,
227: "assets/calendar.css"));
228: }
229:
230: /**
231: * Append javascript to the initialization function for the YUI widget. Can
232: * be used by subclasses to conveniently extend configuration without having
233: * to write a separate contribution.
234: *
235: * @param markupId
236: * The markup id of the calendar component
237: * @param javascriptId
238: * the non-name spaced javascript id of the widget
239: * @param javascriptWidgetId
240: * the name space id of the widget
241: * @param b
242: * the buffer to append the script to
243: */
244: protected void appendToInit(String markupId, String javascriptId,
245: String javascriptWidgetId, StringBuffer b) {
246: }
247:
248: /**
249: * Gives overriding classes the option of adding (or even changing/
250: * removing) configuration properties for the javascript widget. See <a
251: * href="http://developer.yahoo.com/yui/calendar/">the widget's
252: * documentation</a> for the available options. If you want to override/
253: * remove properties, you obviously should call
254: * {@link super#setWidgetProperties(Properties)} first.
255: *
256: * @param widgetProperties
257: * the current widget properties
258: */
259: protected void configureWidgetProperties(Map widgetProperties) {
260: }
261: }
|