2 * Copyright 2005-2010 the original author or authors.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
16 package org.wamblee.wicket.jquery;
18 import java.util.Arrays;
20 import org.apache.wicket.Component;
21 import org.apache.wicket.Page;
22 import org.apache.wicket.behavior.IBehavior;
23 import org.apache.wicket.markup.html.IHeaderResponse;
24 import org.wamblee.wicket.behavior.CompositeBehavior;
26 import flexjson.JSONSerializer;
29 * Abstract JQuery hehavior class that makes it easy to write jQuery behaviors:
31 * <li>Creating a ready function which will be invoked for the component</li>
32 * <li>Checking whether or not the behavior may be attached to a page using
33 * {@link #isPageAllowed()}. By default, the behavior may be attached to
34 * pages. Behaviors that should not be allowed to be attached to pages should
35 * override this method.</li>
36 * <li>Ensuring that the markup id of the component is output</li>
37 * <li>Creating a call to an intialization function from the ready handler using
38 * the component id</li>
41 * The ready function will be invoked as part of a ready handler and will invoke
42 * a function with two arguments. The first is the selector of the component and
43 * the second is a configuration object. In case the behavior is attached to a
44 * component, a selector is used based on the unique markup id. When used on a
45 * page, the selector matches with the "body" of the page.
47 * The second parameter is obtained through a call to
48 * {@link #getConfigurationJavascript()}.
51 * @author Erik Brakkee
55 public class AbstractJQueryBehavior<ConfigType> extends CompositeBehavior {
57 private static final String DEFAULT_NAMESPACE = "org.wamblee";
58 private static JSONSerializer DEFAULT_JSON_SERIALIZER = new JSONSerializer();
60 private Component component;
61 private String function;
64 * Constructs the behavior.
67 * Function to be invoked from the ready handler. This function
68 * is invoked with a CSS selector that identifies the component.
70 * Behaviors to add in addition to the basic JQuery stuff.
72 public AbstractJQueryBehavior(String aFunction, IBehavior... aBehaviors) {
73 super(getBehaviors(aBehaviors));
78 * Determines whether the behavior is allowed to be attached toa page.
82 protected boolean isPageAllowed() {
86 private static IBehavior[] getBehaviors(IBehavior[] aBehaviors) {
87 IBehavior[] behaviors = new IBehavior[aBehaviors.length + 1];
88 behaviors[0] = new JQueryHeaderContributor();
89 for (int i = 0; i < aBehaviors.length; i++) {
90 behaviors[i + 1] = aBehaviors[i];
96 public void bind(Component aComponent) {
97 if (component != null) {
98 throw new IllegalStateException(
99 "this kind of handler cannot be attached to " +
100 "multiple components; it is already attached to component " +
101 component + ", but component " + aComponent +
102 " wants to be attached too");
104 if (!isPageAllowed() && aComponent instanceof Page) {
105 throw new IllegalStateException(
106 "This behavior cannot be applied to a page: " + aComponent);
108 component = aComponent;
109 super.bind(aComponent);
110 if (!(aComponent instanceof Page)) {
111 aComponent.setOutputMarkupId(true);
116 public void renderHead(IHeaderResponse aResponse) {
117 super.renderHead(aResponse);
118 String jsString = createReadyFunction();
119 aResponse.renderJavascript(jsString, null);
123 * Creates a jQuery ready handler that invokes a given javascript function
124 * with the id of a component and with a second parameter of parameters to
125 * pass additional information to the function.
128 * Javascript function to invoke, the name of this function must
129 * include the namespace as it is called from a global scope.
131 * Component to invoke the id for.
134 String createReadyFunction() {
135 if (!(component instanceof Page) && !component.getOutputMarkupId()) {
136 throw new IllegalStateException(
139 " does not have its markup id set so this ready handler will not have any effect");
141 StringBuffer js = new StringBuffer();
142 js.append("jQuery(function(){");
143 js.append(function + "(");
144 String selector = "body";
145 if (!(component instanceof Page)) {
146 selector = "#" + component.getMarkupId();
148 js.append("\"" + selector + "\"");
149 String config = getConfigurationJavascript();
150 if (config != null) {
156 String jsString = js.toString();
161 * Returns a javascript object that is passed as second argument to the
162 * ready function. This method uses {@link #getConfigurationObject()} to
163 * obtain the configuration object to use which is then serialized to
164 * javascript using {@link JSONSerializer}. Subclasses can override the
165 * default JSONSerializer by implementing {@link #getCustomSerializer()}.
167 * Subclasses should override this method to perform custom serialization.
169 * @return Configuration object in javascript.
171 protected String getConfigurationJavascript() {
172 Object config = getConfigurationObject();
173 return getActualSerializer().serialize(config);
177 * Gets the actual serializer to use. It uses {@link #getCustomSerializer()}
178 * to check if there is a custom serializer available.
180 * @return Serializer.
182 private JSONSerializer getActualSerializer() {
183 JSONSerializer serializer = getCustomSerializer();
184 if (serializer != null) {
187 return DEFAULT_JSON_SERIALIZER;
191 * Returns the serializer to use. Implementations can override this method
192 * to perform custom initialization of the serializer knowing the type of
193 * configuration object to use.
195 * @return Custom serializer to use.
197 protected JSONSerializer getCustomSerializer() {
202 * Gets the configuration object to use. This is transformed to JSON using
205 * @return Configuration object.
207 protected ConfigType getConfigurationObject() {