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 */
017package org.apache.wicket.markup.html;
018
019import org.apache.wicket.Component;
020import org.apache.wicket.application.IComponentInstantiationListener;
021import org.apache.wicket.behavior.Behavior;
022import org.apache.wicket.markup.ComponentTag;
023import org.apache.wicket.markup.html.form.AbstractTextComponent;
024import org.apache.wicket.markup.html.form.Button;
025import org.apache.wicket.markup.html.form.FormComponent;
026import org.apache.wicket.model.IModel;
027import org.apache.wicket.validation.IValidator;
028import org.apache.wicket.validation.ValidatorAdapter;
029import org.apache.wicket.validation.validator.PatternValidator;
030
031/**
032 * Behavior which renders HTML5 attributes.
033 * 
034 * @see #onInput(AbstractTextComponent, ComponentTag)
035 * @see #onButton(Button, ComponentTag)
036 */
037public class HTML5Attributes extends Behavior
038{
039        private static final long serialVersionUID = 1L;
040
041        @Override
042        public void onComponentTag(Component component, ComponentTag tag)
043        {
044                if (component instanceof AbstractTextComponent)
045                {
046                        onInput((AbstractTextComponent<?>)component, tag);
047                }
048                else if (component instanceof Button)
049                {
050                        onButton((Button)component, tag);
051                }
052        }
053
054        /**
055         * Writes HTML5 attributes for {@link AbstractTextComponent} inputs:
056         * 
057         * <ul>
058         * <li>{@code required} if component {@link AbstractTextComponent#isRequired()}</li>
059         * <li>{@code placeholder} for {@link AbstractTextComponent#getLabel()}</li>
060         * <li>{@code pattern} for {@link AbstractTextComponent}s with a {@link PatternValidator}</li>
061         * <ul>
062         * 
063         * @param input
064         *            input component
065         * @param tag
066         *            component tag
067         */
068        protected void onInput(AbstractTextComponent<?> input, ComponentTag tag)
069        {
070                if (input.isRequired())
071                {
072                        tag.put("required", "required");
073                }
074
075                IModel<String> label = input.getLabel();
076                if (label != null && label.getObject() != null)
077                {
078                        tag.put("placeholder", label.getObject());
079                }
080
081                for (IValidator<?> validator : input.getValidators())
082                {
083                        while (validator instanceof ValidatorAdapter)
084                        {
085                                validator = ((ValidatorAdapter<?>)validator).getValidator();
086                        }
087
088                        if (validator instanceof PatternValidator)
089                        {
090                                tag.put("pattern", ((PatternValidator)validator).getPattern().toString());
091                        }
092                }
093        }
094
095        /**
096         * Writes HTML5 attributes for {@link Button}s:
097         * 
098         * <ul>
099         * <li>{@code formnovalidate} if {@link Button#getDefaultFormProcessing()} returns {@code false}
100         * </li>
101         * <ul>
102         * 
103         * @param button
104         *            button component
105         * @param tag
106         *            component tag
107         */
108        protected void onButton(Button button, ComponentTag tag)
109        {
110                if (!button.getDefaultFormProcessing())
111                {
112                        tag.put("formnovalidate", "formnovalidate");
113                }
114        }
115
116        /**
117         * A listener to instantiations of {@link FormComponent}s to add HTML5 attributes.
118         */
119        public static class InstantiationListener implements IComponentInstantiationListener
120        {
121                /**
122                 * Adds {@link HTML5Attributes} to all {@link FormComponent}s.
123                 */
124                @Override
125                public void onInstantiation(Component component)
126                {
127                        if (component instanceof FormComponent)
128                        {
129                                component.add(new HTML5Attributes());
130                        }
131                }
132        }
133}