/* * Copyright 2005-2010 the original author or authors. * * 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 org.wamblee.wicket.jquery; import java.util.Arrays; import org.apache.wicket.Component; import org.apache.wicket.Page; import org.apache.wicket.behavior.IBehavior; import org.apache.wicket.markup.html.IHeaderResponse; import org.wamblee.wicket.behavior.CompositeBehavior; import flexjson.JSONSerializer; /** * Abstract JQuery hehavior class that makes it easy to write jQuery behaviors: * * * The ready function will be invoked as part of a ready handler and will invoke * a function with two arguments. The first is the selector of the component and * the second is a configuration object. In case the behavior is attached to a * component, a selector is used based on the unique markup id. When used on a * page, the selector matches with the "body" of the page. *

* The second parameter is obtained through a call to * {@link #getConfigurationJavascript()}. * * * @author Erik Brakkee * * */ public class AbstractJQueryBehavior extends CompositeBehavior { private static final String DEFAULT_NAMESPACE = "org.wamblee"; private static JSONSerializer DEFAULT_JSON_SERIALIZER = new JSONSerializer(); private Component component; private String function; /** * Constructs the behavior. * * @param aFunction * Function to be invoked from the ready handler. This function * is invoked with a CSS selector that identifies the component. * @param aBehaviors * Behaviors to add in addition to the basic JQuery stuff. */ public AbstractJQueryBehavior(String aFunction, IBehavior... aBehaviors) { super(getBehaviors(aBehaviors)); function = aFunction; } /** * Determines whether the behavior is allowed to be attached toa page. * * @return True. */ protected boolean isPageAllowed() { return true; } private static IBehavior[] getBehaviors(IBehavior[] aBehaviors) { IBehavior[] behaviors = new IBehavior[aBehaviors.length + 1]; behaviors[0] = new JQueryHeaderContributor(); for (int i = 0; i < aBehaviors.length; i++) { behaviors[i + 1] = aBehaviors[i]; } return behaviors; } @Override public void bind(Component aComponent) { if (component != null) { throw new IllegalStateException( "this kind of handler cannot be attached to " + "multiple components; it is already attached to component " + component + ", but component " + aComponent + " wants to be attached too"); } if (!isPageAllowed() && aComponent instanceof Page) { throw new IllegalStateException( "This behavior cannot be applied to a page: " + aComponent); } component = aComponent; super.bind(aComponent); if (!(aComponent instanceof Page)) { aComponent.setOutputMarkupId(true); } } @Override public void renderHead(IHeaderResponse aResponse) { super.renderHead(aResponse); String jsString = createReadyFunction(); aResponse.renderJavascript(jsString, null); } /** * Creates a jQuery ready handler that invokes a given javascript function * with the id of a component and with a second parameter of parameters to * pass additional information to the function. * * @param aFunction * Javascript function to invoke, the name of this function must * include the namespace as it is called from a global scope. * @param aComponent * Component to invoke the id for. * @return */ String createReadyFunction() { if (!(component instanceof Page) && !component.getOutputMarkupId()) { throw new IllegalStateException( "The component " + component + " does not have its markup id set so this ready handler will not have any effect"); } StringBuffer js = new StringBuffer(); js.append("jQuery(function(){"); js.append(function + "("); String selector = "body"; if (!(component instanceof Page)) { selector = "#" + component.getMarkupId(); } js.append("\"" + selector + "\""); String config = getConfigurationJavascript(); if (config != null) { js.append(","); js.append(config); } js.append(");"); js.append("});"); String jsString = js.toString(); return jsString; } /** * Returns a javascript object that is passed as second argument to the * ready function. This method uses {@link #getConfigurationObject()} to * obtain the configuration object to use which is then serialized to * javascript using {@link JSONSerializer}. Subclasses can override the * default JSONSerializer by implementing {@link #getCustomSerializer()}. *

* Subclasses should override this method to perform custom serialization. * * @return Configuration object in javascript. */ protected String getConfigurationJavascript() { Object config = getConfigurationObject(); return getActualSerializer().serialize(config); } /** * Gets the actual serializer to use. It uses {@link #getCustomSerializer()} * to check if there is a custom serializer available. * * @return Serializer. */ private JSONSerializer getActualSerializer() { JSONSerializer serializer = getCustomSerializer(); if (serializer != null) { return serializer; } return DEFAULT_JSON_SERIALIZER; } /** * Returns the serializer to use. Implementations can override this method * to perform custom initialization of the serializer knowing the type of * configuration object to use. * * @return Custom serializer to use. */ protected JSONSerializer getCustomSerializer() { return null; } /** * Gets the configuration object to use. This is transformed to JSON using * the serializer. * * @return Configuration object. */ protected ConfigType getConfigurationObject() { return null; } }