/**
* Copyright 2014 55 Minutes (http://www.55minutes.com)
*
* 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 fiftyfive.wicket.css;
import java.util.Arrays;
import java.util.LinkedHashSet;
import java.util.Set;
import org.apache.wicket.Component;
import org.apache.wicket.behavior.Behavior;
import org.apache.wicket.markup.ComponentTag;
import org.apache.wicket.markup.parser.XmlTag.TagType;
import org.apache.wicket.util.string.Strings;
/**
* A behavior simliar to {@link org.apache.wicket.AttributeModifier AttributeModifier} but
* specially tuned for modifying the HTML {@code class} attribute. When this behavior is bound
* to a component it does the following:
* <ul>
* <li>Always adds the {@code class} attribute, even if it did not already exist.</li>
* <li>Ensures that duplicate CSS classes will not be emitted in the attribute.</li>
* <li>Each class will be separated by a space character.</li>
* </ul>
* Note that this class is abstract. Subclasses must implement the
* {@link #modifyClasses modifyClasses()} to specify what classes are added (or subtracted!)
* from the {@code class} attribute.
*
* @since 2.0.4
* @see fiftyfive.wicket.util.Shortcuts#toggledCssClass(String,org.apache.wicket.model.IModel)
*/
public abstract class CssClassModifier extends Behavior
{
/**
* Implemented by subclasses to specify what CSS classes should be added or subtracted
* to the HTML for this component.
*
* @param component The component whose HTML element is being rendered.
*
* @param cssClasses A set containing all the CSS classes that were declared in the markup
* for this component, if any. If values are added to this set, they will
* be emitted in the {@code class} attribute of the element when the
* component renders, with a space separating each value. Values may also
* be removed in order to prevent them from being emitted.
*/
protected abstract void modifyClasses(Component component, Set<String> cssClasses);
/**
* Parse any existing classes declared in the markup for this component and then delegate
* to {@link #modifyClasses modifyClasses()}. Set resulting set of classes on the component
* tag to render its {@code class} attribute with the desired values.
*/
@Override
public void onComponentTag(Component component, ComponentTag tag)
{
if(tag.getType() != TagType.CLOSE)
{
Set<String> values = new LinkedHashSet<String>();
String existing = tag.getAttribute("class");
if(existing != null)
{
values.addAll(Arrays.asList(existing.split("\\s+")));
}
modifyClasses(component, values);
tag.put("class", Strings.join(" ", values.toArray(new String[0])));
}
}
}