diff --git a/pom.xml b/pom.xml index 3e26d967fe22b8f35df82ee90622c400f1afd4c4..44fee9cc8de59b2e47cdc75085f2359a07bdc092 100644 --- a/pom.xml +++ b/pom.xml @@ -33,6 +33,12 @@ 7.0 provided + + jstl + jstl + 1.2 + + org.postgresql postgresql diff --git a/src/main/java/core/web/LoginControler.java b/src/main/java/core/web/LoginControler.java new file mode 100644 index 0000000000000000000000000000000000000000..5d1dfedefe73928ff8926e87426544d830f879b6 --- /dev/null +++ b/src/main/java/core/web/LoginControler.java @@ -0,0 +1,74 @@ +package core.web; + +import java.io.IOException; +import java.util.regex.Pattern; +import javax.servlet.ServletException; +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +/** + * @author dominique huguenin (dominique.huguenin@rpn.ch) + */ +public class LoginControler extends HttpServlet { + + public static final String LOGIN_PATTERN = "/login.html"; + public static final String LOGOUT_PATTERN = "/logout.html"; + public static final String PAGE_ACCUEIL = "/index.html"; + private final Pattern logoutPattern; + private final Pattern loginPattern; + + public LoginControler() { + this.loginPattern = Pattern.compile(LOGIN_PATTERN); + this.logoutPattern = Pattern.compile(LOGOUT_PATTERN); + + } + + protected void processRequest(final HttpServletRequest request, + final HttpServletResponse response) + throws ServletException, IOException { + + if (this.loginPattern.matcher(request.getServletPath()).matches()) { + boolean authentifie = request.authenticate(response); + if (authentifie) { + response.sendRedirect(request.getContextPath() + PAGE_ACCUEIL); + } + } + + if (this.logoutPattern.matcher(request.getServletPath()).matches()) { + request.logout(); + response.sendRedirect(request.getContextPath() + PAGE_ACCUEIL); + } + } + + /** + * Handles the HTTP GET method. + * + * @param request servlet request + * @param response servlet response + * @throws ServletException if a servlet-specific error occurs + * @throws IOException if an I/O error occurs + */ + @Override + protected void doGet(final HttpServletRequest request, + final HttpServletResponse response) + throws ServletException, IOException { + processRequest(request, response); + } + + /** + * Handles the HTTP POST method. + * + * @param request servlet request + * @param response servlet response + * @throws ServletException if a servlet-specific error occurs + * @throws IOException if an I/O error occurs + */ + @Override + protected void doPost(final HttpServletRequest request, + final HttpServletResponse response) + throws ServletException, IOException { + processRequest(request, response); + } + +} diff --git a/src/main/java/core/web/RequestUtils.java b/src/main/java/core/web/RequestUtils.java new file mode 100644 index 0000000000000000000000000000000000000000..2308a91a2e3a568409674b6a3076cb934d9c7862 --- /dev/null +++ b/src/main/java/core/web/RequestUtils.java @@ -0,0 +1,156 @@ +package core.web; + +import java.util.HashMap; +import java.util.Map; +import java.util.logging.Level; +import java.util.logging.Logger; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import javax.servlet.http.HttpServletRequest; + +/** + * + * @author dominique huguenin (dominique.huguenin@rpn.ch) + */ +public final class RequestUtils { + + private RequestUtils() { + } + + public static final String HEADER_ACCEPT = "Accept"; + public static final String HEADER_LOCATION = "Location"; + + public static final String MEDIA_TYPE_APPLICATION_JSON = "application/json"; + public static final String MEDIA_TYPE_APPLICATION_XML = "application/xml"; + public static final String MEDIA_TYPE_TEXT_PLAIN = "text/plain"; + public static final String ENCODAGE_UTF8 = "UTF-8"; + + public static final String VERSION_PARAM = "version"; + + public static final String ACTION_PARAMETRE = "action"; + public static final String ACTION_PATTERN_REGEX + = "([^\\]]*)(?:\\[([^=]*)=([^\\]]*)\\])?"; + public static final int ACTION_PATTERN_GROUPE_KEY = 2; + public static final int ACTION_PATTERN_GROUPE_VALUE = 3; + + private static final Logger LOG + = Logger.getLogger(RequestUtils.class.getName()); + + public static Long extractVersion(final HttpServletRequest req) { + return RequestUtils.extractLongParametre(req, VERSION_PARAM); + } + + public static String extractAcceptHeader(final HttpServletRequest req) { + return extractParametre(req, HEADER_ACCEPT); + } + + private static String extractParametre( + final HttpServletRequest req, + final String param) { + String value = req.getParameter(param); + if (value == null) { + value = ""; + } + return value; + } + + public static String extractId( + final HttpServletRequest req, + final Pattern idRegex) { + String id = null; + + Matcher matcher = idRegex.matcher(req.getRequestURL()); + if (matcher.matches()) { + id = matcher.group(1); + + } + return id; + } + + public static String extractActionParameter( + final HttpServletRequest request) { + String actionStr = request.getParameter(ACTION_PARAMETRE); + Pattern actionPattern = Pattern.compile(ACTION_PATTERN_REGEX); + if (actionStr != null) { + Matcher matcher = actionPattern.matcher(actionStr); + if (matcher.matches()) { + actionStr = matcher.group(1); + } + } + return actionStr; + } + + /** + * + * @param request une requète + * @return les attributs de l'action + */ + public static Map extractActionAttribut( + final HttpServletRequest request) { + Map map = new HashMap<>(); + Pattern actionPattern = Pattern.compile(ACTION_PATTERN_REGEX); + String actionStr = request.getParameter(ACTION_PARAMETRE); + String key; + String value; + if (actionStr != null) { + Matcher matcher = actionPattern.matcher(actionStr); + if (matcher.matches()) { + key = matcher.group(ACTION_PATTERN_GROUPE_KEY); + value = matcher.group(ACTION_PATTERN_GROUPE_VALUE); + map.put(key, value); + } + } + return map; + } + + public static Long extractLongParametre( + final HttpServletRequest request, + final String param) { + String valeurStr = request.getParameter(param); + Long valeur = null; + try { + if (!(valeurStr == null || "".equals(valeurStr))) { + valeur = Long.valueOf(valeurStr); + } + } catch (NumberFormatException ex) { + LOG.log(Level.WARNING, + String.format("%s, %s", ex.getMessage(), valeurStr)); + } + return valeur; + } + + public static Integer extractIntegerParametre( + final HttpServletRequest request, + final String param) { + String valeurStr = request.getParameter(param); + Integer valeur = null; + try { + if (!(valeurStr == null || "".equals(valeurStr))) { + valeur = Integer.valueOf(valeurStr); + } + + } catch (NumberFormatException ex) { + LOG.log(Level.WARNING, + String.format("%s, %s", ex.getMessage(), valeurStr)); + } + return valeur; + } + + public static Double extractDoubleParametre( + final HttpServletRequest request, + final String param) { + Double valeur = null; + String valeurStr = request.getParameter(param); + try { + if (!(valeurStr == null || "".equals(valeurStr))) { + valeur = Double.valueOf(valeurStr); + } + } catch (NumberFormatException ex) { + LOG.log(Level.WARNING, + String.format("%s, %s", ex.getMessage(), valeurStr)); + } + + return valeur; + } + +} diff --git a/src/main/java/core/web/routing/Action.java b/src/main/java/core/web/routing/Action.java new file mode 100644 index 0000000000000000000000000000000000000000..a483c4f6bed394615beef8b43fd80d0345fb18dd --- /dev/null +++ b/src/main/java/core/web/routing/Action.java @@ -0,0 +1,61 @@ +package core.web.routing; + +import java.io.IOException; +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +/** + * + * @author dominique huguenin (dominique.huguenin at rpn.ch) + */ +public interface Action { + + void execute(HttpServletRequest request, + HttpServletResponse response) + throws ServletException, IOException; + + /** + * Action permettant la redirection vers l'url saisi dans le constructeur. + */ + class Redirect implements Action { + + private final String url; + + public Redirect(final String url) { + this.url = url; + } + + @Override + public void execute( + final HttpServletRequest request, + final HttpServletResponse response) + throws ServletException, IOException { + response.sendRedirect(this.url); + } + } + + /** + * Action permettant de transférer la requète vers le path saisi dans le + * constructeur. + */ + class Forward implements Action { + + private final String path; + + public Forward(final String path) { + this.path = path; + } + + @Override + public void execute( + final HttpServletRequest request, + final HttpServletResponse response) + throws ServletException, IOException { + + request.getRequestDispatcher(path) + .forward(request, response); + } + } + +} diff --git a/src/main/java/core/web/routing/Routage.java b/src/main/java/core/web/routing/Routage.java new file mode 100644 index 0000000000000000000000000000000000000000..78e39868603749e76587ea6c6b36a8e615f9dd05 --- /dev/null +++ b/src/main/java/core/web/routing/Routage.java @@ -0,0 +1,69 @@ +package core.web.routing; + +import core.web.RequestUtils; +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.util.HashMap; +import java.util.Map; +import javax.servlet.ServletException; +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +/** + * + * @author dominique huguenin (dominique.huguenin at rpn.ch) + */ +public abstract class Routage extends HttpServlet { + + private Map actions; + + private Action actionNull; + + public Routage() { + actions = new HashMap<>(); + + } + + public Map getActions() { + return actions; + } + + public Action getActionNull() { + return actionNull; + } + + public void setActionNull(final Action actionNull) { + this.actionNull = actionNull; + } + + protected void processRequest( + final HttpServletRequest request, + final HttpServletResponse response) + throws ServletException, IOException { + request.setCharacterEncoding(StandardCharsets.UTF_8.name()); + String actionStr = RequestUtils.extractActionParameter(request); + Action action = actions.get(actionStr); + if (action == null) { + action = this.actionNull; + } + action.execute(request, response); + } + + @Override + protected void doGet( + final HttpServletRequest request, + final HttpServletResponse response) + throws ServletException, IOException { + processRequest(request, response); + } + + @Override + protected void doPost( + final HttpServletRequest request, + final HttpServletResponse response) + throws ServletException, IOException { + processRequest(request, response); + } + +} diff --git a/src/main/java/personne/routing/ActionPage.java b/src/main/java/personne/routing/ActionPage.java new file mode 100644 index 0000000000000000000000000000000000000000..045ee2eb3a6e8fa0c31431d1239a43a65633a602 --- /dev/null +++ b/src/main/java/personne/routing/ActionPage.java @@ -0,0 +1,17 @@ +package personne.routing; + +/** + * + * @author dominique huguenin (dominique.huguenin at rpn.ch) + */ +public enum ActionPage { + QUITTER, + FILTRER, + VISUALISER, + MODIFIER, + VALIDER_MODIFICATION, + CREER, + VALIDER_CREATION, + SUPPRIMER, + VALIDER_SUPPRESSION, +} diff --git a/src/main/java/personne/routing/DatasourceRoutage.java b/src/main/java/personne/routing/DatasourceRoutage.java new file mode 100644 index 0000000000000000000000000000000000000000..b9f96c970b4870d921c85b6b616312e1ec294281 --- /dev/null +++ b/src/main/java/personne/routing/DatasourceRoutage.java @@ -0,0 +1,102 @@ +package personne.routing; + +import core.web.routing.Action; +import core.web.routing.Routage; +import java.io.IOException; +import java.util.logging.Level; +import java.util.logging.Logger; +import javax.naming.InitialContext; +import javax.naming.NamingException; +import javax.servlet.ServletException; +import javax.servlet.annotation.WebServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import javax.sql.DataSource; +import personne.datasource.MapperManager; +import personne.datasource.PersistenceException; +import personne.datasource.db.DbMapperManagerImpl; + +/** + * + * @author dominique huguenin (dominique.huguenin at rpn.ch) + */ +@WebServlet(name = "DatasourceRoutage", + urlPatterns = {"/datasource.html"}) +public class DatasourceRoutage extends Routage { + + public static final String PAGE_JSP_DATABASE = "/WEB-INF/database.jsp"; + + public static final String MSG_ERREUR_SUPPRESSION_DATASOURCE = "La suppression n'a pas étre effectuée!"; + public static final String MSG_SUCCES_SUPPRESSION_DATASOURCE = "Les objets ont été supprimés!"; + public static final String MSG_ERREUR_ECHEC_CREATION = "La création n'a pas étre effectuée!"; + public static final String MSG_SUCCES_CREATION_DATASOURCE = "Les objets ont été créés!"; + + public static final String MESSAGE_SUCCES_ATTR + = "messageSucces"; + public static final String MESSAGE_ERREUR_ATTR + = "messageErreur"; + private static final Logger LOG = Logger.getLogger(DatasourceRoutage.class.getName()); + private MapperManager mapperManager; + + @Override + public void init() throws ServletException { + try { + InitialContext initCtx; + initCtx = new InitialContext(); + DataSource datasource = (DataSource) initCtx + .lookup("java:/comp/env/jdbc/db"); + + mapperManager = new DbMapperManagerImpl(datasource); + + this.getActions().put(ActionPage.VALIDER_CREATION.name(), + new Action.Forward(PAGE_JSP_DATABASE) { + @Override + public void execute( + final HttpServletRequest request, + final HttpServletResponse response) + throws ServletException, IOException { + try { + mapperManager.getDatabaseSetup().createTables(); + + request.setAttribute(MESSAGE_SUCCES_ATTR, + MSG_SUCCES_CREATION_DATASOURCE); + + } catch (PersistenceException ex) { + LOG.log(Level.SEVERE, null, ex); + request.setAttribute(MESSAGE_ERREUR_ATTR, + MSG_ERREUR_ECHEC_CREATION); + } + + super.execute(request, response); + } + }); + + this.getActions().put(ActionPage.VALIDER_SUPPRESSION.name(), + new Action.Forward(PAGE_JSP_DATABASE) { + @Override + public void execute( + final HttpServletRequest request, + final HttpServletResponse response) + throws ServletException, IOException { + try { + mapperManager.getDatabaseSetup().dropTables(); + + request.setAttribute(MESSAGE_SUCCES_ATTR, + MSG_SUCCES_SUPPRESSION_DATASOURCE); + + } catch (PersistenceException ex) { + LOG.log(Level.SEVERE, null, ex); + request.setAttribute(MESSAGE_ERREUR_ATTR, + MSG_ERREUR_SUPPRESSION_DATASOURCE); + } + + super.execute(request, response); + } + }); + } catch (NamingException ex) { + Logger.getLogger(DatasourceRoutage.class.getName()).log(Level.SEVERE, null, ex); + throw new ServletException(ex); + } + } + +} diff --git a/src/main/java/personne/routing/DemoDataRoutage.java b/src/main/java/personne/routing/DemoDataRoutage.java new file mode 100644 index 0000000000000000000000000000000000000000..847ab12fac4877330adfd288ed33f5918fbeb602 --- /dev/null +++ b/src/main/java/personne/routing/DemoDataRoutage.java @@ -0,0 +1,78 @@ +package personne.routing; + +import core.web.routing.Action; +import core.web.routing.Routage; +import java.io.IOException; +import java.util.logging.Level; +import java.util.logging.Logger; +import javax.naming.InitialContext; +import javax.naming.NamingException; +import javax.servlet.ServletException; +import javax.servlet.annotation.WebServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import javax.sql.DataSource; +import personne.datasource.MapperManager; +import personne.datasource.PersistenceException; +import personne.datasource.db.DbMapperManagerImpl; + +/** + * + * @author dominique huguenin (dominique.huguenin at rpn.ch) + */ +@WebServlet(name = "DemoDataRoutage", + urlPatterns = {"/datasource/demodata.html"}) +public class DemoDataRoutage extends Routage { + + public static final String PAGE_JSP_DATABASE = "/WEB-INF/database.jsp"; + + public static final String MSG_SUCCES_CREATION_DATASOURCE = "Les objets ont été créés!"; + public static final String MSG_ECHEC_CREATION_DATASOURCE = "Les objets n'ont pas été créés!"; + + public static final String MESSAGE_SUCCES_ATTR + = "messageSucces"; + public static final String MESSAGE_ERREUR_ATTR + = "messageErreur"; + private static final Logger LOG = Logger.getLogger(DemoDataRoutage.class.getName()); + private MapperManager mapperManager; + + @Override + public void init() throws ServletException { + + try { + InitialContext initCtx; + initCtx = new InitialContext(); + DataSource datasource = (DataSource) initCtx + .lookup("java:/comp/env/jdbc/db"); + + mapperManager = new DbMapperManagerImpl(datasource); + + this.getActions().put(ActionPage.VALIDER_CREATION.name(), + new Action.Forward(PAGE_JSP_DATABASE) { + @Override + public void execute( + final HttpServletRequest request, + final HttpServletResponse response) + throws ServletException, IOException { + try { + mapperManager.getDatabaseSetup().insertData(); + + request.setAttribute(MESSAGE_SUCCES_ATTR, + MSG_SUCCES_CREATION_DATASOURCE); + + } catch (PersistenceException ex) { + LOG.log(Level.SEVERE, null, ex); + request.setAttribute(MESSAGE_ERREUR_ATTR, + MSG_ECHEC_CREATION_DATASOURCE); + } + + super.execute(request, response); + } + }); + } catch (NamingException ex) { + Logger.getLogger(DemoDataRoutage.class.getName()).log(Level.SEVERE, null, ex); + throw new ServletException(ex); + } + } + +} diff --git a/src/main/java/personne/routing/EtatPage.java b/src/main/java/personne/routing/EtatPage.java new file mode 100644 index 0000000000000000000000000000000000000000..3d7728af6d03ba2206d515084882d6fd6bb1ce4e --- /dev/null +++ b/src/main/java/personne/routing/EtatPage.java @@ -0,0 +1,13 @@ +package personne.routing; + +/** + * + * @author dominique huguenin (dominique.huguenin at rpn.ch) + */ +public enum EtatPage { + LISTE, + VISUALISATION, + MODIFICATION, + CREATION, + SUPPRESSION +} diff --git a/src/main/java/personne/routing/PersonneRoutage.java b/src/main/java/personne/routing/PersonneRoutage.java new file mode 100644 index 0000000000000000000000000000000000000000..e47660b31397aa8bede62d4ae983a39421d7205e --- /dev/null +++ b/src/main/java/personne/routing/PersonneRoutage.java @@ -0,0 +1,266 @@ +package personne.routing; + +import core.web.RequestUtils; +import core.web.routing.Action; +import core.web.routing.Routage; +import java.io.IOException; +import java.util.logging.Level; +import java.util.logging.Logger; +import java.util.regex.Pattern; +import javax.naming.InitialContext; +import javax.naming.NamingException; +import javax.servlet.ServletException; +import javax.servlet.annotation.WebServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import javax.sql.DataSource; +import personne.datasource.PersistenceException; +import personne.datasource.db.DbMapperManagerImpl; +import personne.domain.Personne; + +/** + * + * @author dominique huguenin ( dominique.huguenin AT rpn.ch) + */ +@WebServlet(name = "PersonneRoutage", + urlPatterns = {"/personnes/*"}) +public class PersonneRoutage extends Routage { + + private final Pattern idPatternRegex; + private static final Logger LOG + = Logger.getLogger(PersonneRoutage.class.getName()); + private DbMapperManagerImpl mapperManager; + + public PersonneRoutage() { + idPatternRegex = Pattern.compile(PersonneUtils.ID_PATTERN_REGEX); + + } + + //CHECKSTYLE.OFF: MethodLength + @Override + public void init() throws ServletException { + try { + InitialContext initCtx; + initCtx = new InitialContext(); + DataSource datasource = (DataSource) initCtx + .lookup("java:/comp/env/jdbc/db"); + + mapperManager = new DbMapperManagerImpl(datasource); + + this.getActions().put(ActionPage.VISUALISER.name(), + new Action.Forward( + PersonneUtils.PAGE_JSP_DETAIL + ) { + @Override + public void execute( + final HttpServletRequest request, + final HttpServletResponse response) + throws ServletException, IOException { + + try { + String uuid = RequestUtils.extractId(request, + idPatternRegex); + + Personne detail = mapperManager.getPersonneMapper() + .retrieveByUUID(uuid); + + if (detail != null) { + request.getSession().setAttribute( + PersonneUtils.JSP_ATTRIBUT_ETAT_PAGE, + EtatPage.VISUALISATION); + request.setAttribute(PersonneUtils.JSP_ATTRIBUT_DETAIL, + detail); + + super.execute(request, response); + } else { + response.sendError( + HttpServletResponse.SC_NOT_FOUND); + } + + } catch (PersistenceException ex) { + LOG.log(Level.SEVERE, null, ex); + response.sendError( + HttpServletResponse.SC_INTERNAL_SERVER_ERROR, + ex.toString()); + + } + } + }); + + this.getActions().put(ActionPage.MODIFIER.name(), + new Action.Forward( + PersonneUtils.PAGE_JSP_DETAIL + ) { + @Override + public void execute( + final HttpServletRequest request, + final HttpServletResponse response) + throws ServletException, IOException { + + try { + String uuid = RequestUtils.extractId(request, + idPatternRegex); + + Personne detail = mapperManager.getPersonneMapper() + .retrieveByUUID(uuid); + + if (detail != null) { + request.getSession().setAttribute( + PersonneUtils.JSP_ATTRIBUT_ETAT_PAGE, + EtatPage.MODIFICATION); + request.setAttribute(PersonneUtils.JSP_ATTRIBUT_DETAIL, + detail); + + super.execute(request, response); + } else { + response.sendError( + HttpServletResponse.SC_NOT_FOUND); + } + + } catch (PersistenceException ex) { + LOG.log(Level.SEVERE, null, ex); + response.sendError( + HttpServletResponse.SC_INTERNAL_SERVER_ERROR, + ex.toString()); + + } + } + }); + + this.getActions().put(ActionPage.VALIDER_MODIFICATION.name(), + new Action() { + @Override + public void execute( + final HttpServletRequest request, + final HttpServletResponse response) + throws ServletException, IOException { + + try { + final Personne entite = PersonneUtils.lireFormulaire(request); + mapperManager.getPersonneMapper().update(entite); + + response.sendRedirect(String.format( + PersonneUtils.TEMPLATE_URL_DETAIL, + request.getContextPath(), + entite.getUUID())); + } catch (PersistenceException ex) { + LOG.log(Level.SEVERE, null, ex); + response.sendError( + HttpServletResponse.SC_INTERNAL_SERVER_ERROR, + ex.toString()); + } + } + }); + + this.getActions().put(ActionPage.SUPPRIMER.name(), + new Action.Forward( + PersonneUtils.PAGE_JSP_DETAIL + ) { + @Override + public void execute( + final HttpServletRequest request, + final HttpServletResponse response) + throws ServletException, IOException { + + try { + String uuid = RequestUtils.extractId(request, + idPatternRegex); + + Personne detail = mapperManager.getPersonneMapper() + .retrieveByUUID(uuid); + + if (detail != null) { + + request.getSession().setAttribute( + PersonneUtils.JSP_ATTRIBUT_ETAT_PAGE, + EtatPage.SUPPRESSION); + request.setAttribute(PersonneUtils.JSP_ATTRIBUT_DETAIL, + detail); + + super.execute(request, response); + } else { + response.sendError( + HttpServletResponse.SC_NOT_FOUND); + } + + } catch (PersistenceException ex) { + LOG.log(Level.SEVERE, null, ex); + response.sendError( + HttpServletResponse.SC_INTERNAL_SERVER_ERROR, + ex.toString()); + + } + } + }); + + this.getActions().put(ActionPage.VALIDER_SUPPRESSION.name(), + new Action.Redirect( + String.format(PersonneUtils.TEMPLATE_URL_LISTE, + this.getServletContext().getContextPath()) + ) { + @Override + public void execute( + final HttpServletRequest request, + final HttpServletResponse response) + throws ServletException, IOException { + + try { + final Personne entite = PersonneUtils.lireFormulaire(request); + mapperManager.getPersonneMapper().delete(entite); + + super.execute(request, response); + + } catch (PersistenceException ex) { + LOG.log(Level.SEVERE, null, ex); + response.sendError( + HttpServletResponse.SC_INTERNAL_SERVER_ERROR, + ex.toString()); + } + + } + }); + + this.getActions().put(ActionPage.QUITTER.name(), + new Action() { + @Override + public void execute( + final HttpServletRequest request, + final HttpServletResponse response) + throws ServletException, IOException { + + EtatPage etatPage = (EtatPage) request.getSession() + .getAttribute(PersonneUtils.JSP_ATTRIBUT_ETAT_PAGE); + + switch (etatPage) { + case SUPPRESSION: + case MODIFICATION: + String uuid = RequestUtils.extractId(request, + idPatternRegex); + + response.sendRedirect(String.format( + PersonneUtils.TEMPLATE_URL_DETAIL, + request.getContextPath(), + uuid)); + break; + + default: + response.sendRedirect(String.format( + PersonneUtils.TEMPLATE_URL_LISTE, + request.getContextPath())); + break; + + } + + } + }); + + this.setActionNull(this.getActions().get(ActionPage.VISUALISER.name())); + } catch (NamingException ex) { + Logger.getLogger(PersonneRoutage.class.getName()).log(Level.SEVERE, null, ex); + throw new ServletException(ex); + } + + } + //CHECKSTYLE.ON: MethodLength + +} diff --git a/src/main/java/personne/routing/PersonneUtils.java b/src/main/java/personne/routing/PersonneUtils.java new file mode 100644 index 0000000000000000000000000000000000000000..8195d7528dbee3a8d90e11d8b1a25ad3c33f79db --- /dev/null +++ b/src/main/java/personne/routing/PersonneUtils.java @@ -0,0 +1,48 @@ +package personne.routing; + +import javax.servlet.http.HttpServletRequest; +import personne.domain.Personne; +import personne.domain.PersonneBase; + +/** + * + * @author dominique huguenin ( dominique.huguenin AT rpn.ch) + */ +public final class PersonneUtils { + + private PersonneUtils() { + } + + public static final String ELEMENT = "personnes"; + public static final String ID_PATTERN_REGEX = ".*/" + ELEMENT + "/([\\w-]*).html"; + + public static final String PAGE_JSP_LISTE = "/WEB-INF/" + ELEMENT + "/liste.jsp"; + public static final String PAGE_JSP_DETAIL = "/WEB-INF/" + ELEMENT + "/detail.jsp"; + public static final String TEMPLATE_URL_LISTE = "%s/" + ELEMENT + ".html"; + public static final String TEMPLATE_URL_DETAIL = "%s/" + ELEMENT + "/%s.html"; + + public static final String JSP_ATTRIBUT_ETAT_PAGE = "etatPage"; + public static final String JSP_ATTRIBUT_LISTE = "liste"; + public static final String JSP_ATTRIBUT_FILTRE = "filtre"; + public static final String JSP_ATTRIBUT_DETAIL = "detail"; + + public static final String JSP_ATTRIBUT_UUID = "uuid"; + public static final String JSP_ATTRIBUT_NOM = "nom"; + public static final String JSP_ATTRIBUT_PRENOM = "prénom"; + + public static Personne lireFormulaire(final HttpServletRequest request) { + String uuid = request.getParameter(JSP_ATTRIBUT_UUID); + if ("".equals(uuid)) { + uuid = null; + } + + String nom = request.getParameter(JSP_ATTRIBUT_NOM); + String prenom = request.getParameter(JSP_ATTRIBUT_PRENOM); + + return PersonneBase.builder() + .uuid(uuid) + .nom(nom) + .prenom(prenom) + .build(); + } +} diff --git a/src/main/java/personne/routing/PersonnesRoutage.java b/src/main/java/personne/routing/PersonnesRoutage.java new file mode 100644 index 0000000000000000000000000000000000000000..219ae2543d896249a2138e6cebd98c3f4dd48ca6 --- /dev/null +++ b/src/main/java/personne/routing/PersonnesRoutage.java @@ -0,0 +1,136 @@ +package personne.routing; + +import core.web.routing.Action; +import core.web.routing.Routage; +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; +import java.util.logging.Level; +import java.util.logging.Logger; +import javax.naming.InitialContext; +import javax.naming.NamingException; +import javax.servlet.ServletException; +import javax.servlet.annotation.WebServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import javax.sql.DataSource; +import personne.datasource.PersistenceException; +import personne.datasource.db.DbMapperManagerImpl; +import personne.domain.Personne; + +/** + * + * @author dominique huguenin ( dominique.huguenin AT rpn.ch) + */ +@WebServlet(name = "PersonnesRoutage", + urlPatterns = {"/personnes.html"}) + +public class PersonnesRoutage extends Routage { + + private static final Logger LOG + = Logger.getLogger(PersonnesRoutage.class.getName()); + private DbMapperManagerImpl mapperManager; + + @Override + public void init() throws ServletException { + + try { + InitialContext initCtx; + initCtx = new InitialContext(); + DataSource datasource = (DataSource) initCtx + .lookup("java:/comp/env/jdbc/db"); + + mapperManager = new DbMapperManagerImpl(datasource); + + this.getActions().put(ActionPage.FILTRER.name(), + new Action.Forward(PersonneUtils.PAGE_JSP_LISTE) { + @Override + public void execute( + final HttpServletRequest request, + final HttpServletResponse response) + throws ServletException, IOException { + + try { + List liste = new ArrayList<>(); + final String filtre = request.getParameter( + PersonneUtils.JSP_ATTRIBUT_FILTRE); + if (filtre != null) { + liste = mapperManager.getPersonneMapper() + .retrieve(filtre); + } + request.setAttribute(PersonneUtils.JSP_ATTRIBUT_LISTE, liste); + request.getSession().setAttribute( + PersonneUtils.JSP_ATTRIBUT_ETAT_PAGE, + EtatPage.LISTE); + + super.execute(request, response); + } catch (java.util.regex.PatternSyntaxException ex) { + response.sendError( + HttpServletResponse.SC_BAD_REQUEST, + ex.getMessage()); + + } catch (PersistenceException ex) { + LOG.log(Level.SEVERE, null, ex); + response.sendError( + HttpServletResponse.SC_INTERNAL_SERVER_ERROR, + ex.toString()); + } + + } + }); + + this.getActions().put(ActionPage.CREER.name(), + new Action.Forward(PersonneUtils.PAGE_JSP_DETAIL) { + @Override + public void execute( + final HttpServletRequest request, + final HttpServletResponse response) + throws ServletException, IOException { + + request.getSession().setAttribute( + PersonneUtils.JSP_ATTRIBUT_ETAT_PAGE, + EtatPage.CREATION); + super.execute(request, response); + } + }); + + this.getActions().put(ActionPage.VALIDER_CREATION.name(), + new Action() { + @Override + public void execute( + final HttpServletRequest request, + final HttpServletResponse response) + throws ServletException, IOException { + + try { + final Personne entite = PersonneUtils.lireFormulaire(request); + + Personne nouvelleEntite + = mapperManager.getPersonneMapper().create(entite); + + response.sendRedirect(String.format( + PersonneUtils.TEMPLATE_URL_DETAIL, + request.getContextPath(), + nouvelleEntite.getUUID())); + } catch (PersistenceException ex) { + LOG.log(Level.SEVERE, null, ex); + response.sendError( + HttpServletResponse.SC_INTERNAL_SERVER_ERROR, + ex.toString()); + } + } + }); + + this.getActions().put(ActionPage.QUITTER.name(), + new Action.Redirect(String.format(PersonneUtils.TEMPLATE_URL_LISTE, + this.getServletContext().getContextPath()))); + + this.setActionNull(this.getActions().get(ActionPage.FILTRER.name())); + } catch (NamingException ex) { + Logger.getLogger(PersonnesRoutage.class.getName()).log(Level.SEVERE, null, ex); + throw new ServletException(ex); + + } + } + +} diff --git a/src/main/webapp/META-INF/context.xml b/src/main/webapp/META-INF/context.xml index dec974ede50cb81b2f531bffd9610e18dc30dbe4..7bbcca8b61d9123968077b5d5ee554d6304b3670 100644 --- a/src/main/webapp/META-INF/context.xml +++ b/src/main/webapp/META-INF/context.xml @@ -1,2 +1,11 @@ - + + + + diff --git a/src/main/webapp/WEB-INF/accueil.jsp b/src/main/webapp/WEB-INF/accueil.jsp new file mode 100644 index 0000000000000000000000000000000000000000..9916f27d04e8c5b0671ec19ad730203f2bb9ae05 --- /dev/null +++ b/src/main/webapp/WEB-INF/accueil.jsp @@ -0,0 +1,14 @@ +<%@page contentType="text/html" pageEncoding="UTF-8"%><%-- +--%><%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %><%-- +--%> + + <%@include file="./jspf/entete.jspf" %> + + <%@include file="./jspf/navbar.jspf" %> +
+
+

Personnes - Gestion des personnes

+
+
+ + diff --git a/src/main/webapp/WEB-INF/database.jsp b/src/main/webapp/WEB-INF/database.jsp new file mode 100644 index 0000000000000000000000000000000000000000..21d56b9fdc1e9ea30bb7ceecf215d569c7526a00 --- /dev/null +++ b/src/main/webapp/WEB-INF/database.jsp @@ -0,0 +1,55 @@ +<%@page contentType="text/html" pageEncoding="UTF-8"%><%-- +--%><%@page import="personne.routing.ActionPage"%><%-- +--%><%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%><%-- +--%> + + + <%@include file="./jspf/entete-meta.jspf" %> + + + + + + <%@include file="./jspf/navbar.jspf" %> +
+
+

Administration de la source de données

+
+ + +

+ OK! + + + +

+
+ +

+ OUPS! + + + Veuillez avertir l'administrateur de l'application, merci. + +

+
+
+
+

Source de données

+

Ces actions permettent de créer/supprimer les objets de la source de données.

+

 Créer  +  Supprimer

+
+
+

Jeu de démonstration

+

Cette action permettent d'ajouter des données de démonstration dans la source de données.

+

 Créer

+
+
+ +
+ + diff --git a/src/main/webapp/WEB-INF/erreurException.jsp b/src/main/webapp/WEB-INF/erreurException.jsp new file mode 100644 index 0000000000000000000000000000000000000000..ffaf2c837dfe986622eb2fb398d47127f4d2e55c --- /dev/null +++ b/src/main/webapp/WEB-INF/erreurException.jsp @@ -0,0 +1,20 @@ +<%@page isErrorPage="true" contentType="text/html" pageEncoding="UTF-8"%><%-- +--%><%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%><%-- +--%><%@page import="java.util.*, java.io.*"%><%-- +--%> + + <%@include file="./jspf/entete.jspf" %> + + <%@include file="./jspf/navbar.jspf" %> +
+
+

Erreur

+

+ +
+

+ +
<%exception.printStackTrace(new PrintWriter(out));%>
+
+ + diff --git a/src/main/webapp/WEB-INF/erreurHTTP.jsp b/src/main/webapp/WEB-INF/erreurHTTP.jsp new file mode 100644 index 0000000000000000000000000000000000000000..f324597ad9e8ba5ddfd3ab4ef7e940c91c41ff9d --- /dev/null +++ b/src/main/webapp/WEB-INF/erreurHTTP.jsp @@ -0,0 +1,22 @@ +<%@page isErrorPage="true" contentType="text/html" pageEncoding="UTF-8"%><%-- +--%><%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%><%-- +--%><%@page import="java.util.*, java.io.*"%><%-- +--%> + + <%@include file="./jspf/entete.jspf" %> + + <%@include file="./jspf/navbar.jspf" %> +
+
+

Erreur + +

+

+ +

+
+
+ + diff --git a/src/main/webapp/WEB-INF/jspf/entete-meta.jspf b/src/main/webapp/WEB-INF/jspf/entete-meta.jspf new file mode 100644 index 0000000000000000000000000000000000000000..9b1aeb0caa54b820419ff51bd7ce4ab2610f83c9 --- /dev/null +++ b/src/main/webapp/WEB-INF/jspf/entete-meta.jspf @@ -0,0 +1,11 @@ +<%@ page pageEncoding="UTF-8" %><%-- +--%><%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> + + Recettes - maquette + + + + + + + diff --git a/src/main/webapp/WEB-INF/jspf/entete.jspf b/src/main/webapp/WEB-INF/jspf/entete.jspf new file mode 100644 index 0000000000000000000000000000000000000000..4eb7c769d02dfd50f1556e7973bd269080220a25 --- /dev/null +++ b/src/main/webapp/WEB-INF/jspf/entete.jspf @@ -0,0 +1,7 @@ +<%@ page pageEncoding="UTF-8" %><%-- +--%><%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> + + <%@include file="./entete-meta.jspf" %> + Personnes - Gestion des personnes + + diff --git a/src/main/webapp/WEB-INF/jspf/identifiant.jspf b/src/main/webapp/WEB-INF/jspf/identifiant.jspf new file mode 100644 index 0000000000000000000000000000000000000000..fb9da8e54b012d240f4c9034bceaf0e22f8bac6a --- /dev/null +++ b/src/main/webapp/WEB-INF/jspf/identifiant.jspf @@ -0,0 +1,5 @@ +<%@ page pageEncoding="UTF-8" %><%-- +--%><%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> + +${detail.UUID} + diff --git a/src/main/webapp/WEB-INF/jspf/navbar.jspf b/src/main/webapp/WEB-INF/jspf/navbar.jspf new file mode 100644 index 0000000000000000000000000000000000000000..e97875496527b40ae7eb55df04b226b2baf43e7d --- /dev/null +++ b/src/main/webapp/WEB-INF/jspf/navbar.jspf @@ -0,0 +1,23 @@ +<%@ page pageEncoding="UTF-8" %><%-- +--%><%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> + + diff --git a/src/main/webapp/WEB-INF/personnes/detail.jsp b/src/main/webapp/WEB-INF/personnes/detail.jsp new file mode 100644 index 0000000000000000000000000000000000000000..af184ab1f44093004b801b9024fd62404f083266 --- /dev/null +++ b/src/main/webapp/WEB-INF/personnes/detail.jsp @@ -0,0 +1,90 @@ +<%@page contentType="text/html" pageEncoding="UTF-8"%><%-- +--%><%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %><%-- + +--%><%@page import="personne.routing.ActionPage"%><%-- +--%><%@page import="personne.routing.EtatPage"%><%-- +--%><%@page import="personne.routing.PersonneUtils"%><%-- +--%> + + <%@include file="../jspf/entete.jspf" %> + + <%@include file="../jspf/navbar.jspf" %> +
+
+

Personnes

+
+ <%@include file="./filtre.jspf" %> +
+

Détail

+
+ + + <%@include file="../jspf/identifiant.jspf" %> + + + + + readonly="" + + placeholder="nom" required="" + title="saisir le nom de la personne!"/> + + + readonly="" + + placeholder="prénom" + title="saisir le prénom de la personne!"/> +
+
+
+ + + diff --git a/src/main/webapp/WEB-INF/personnes/filtre.jspf b/src/main/webapp/WEB-INF/personnes/filtre.jspf new file mode 100644 index 0000000000000000000000000000000000000000..3cfc6fbbc5d2e692dfb7340245940abe2ca61730 --- /dev/null +++ b/src/main/webapp/WEB-INF/personnes/filtre.jspf @@ -0,0 +1,10 @@ +<%@ page pageEncoding="UTF-8" %><%-- +--%><%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> + diff --git a/src/main/webapp/WEB-INF/personnes/liste.jsp b/src/main/webapp/WEB-INF/personnes/liste.jsp new file mode 100644 index 0000000000000000000000000000000000000000..7e2ab84868b4d7d1aa1863809de5ded3d4e13906 --- /dev/null +++ b/src/main/webapp/WEB-INF/personnes/liste.jsp @@ -0,0 +1,41 @@ +<%@page contentType="text/html" pageEncoding="UTF-8"%><%-- +--%><%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %><%-- + +--%> + + <%@include file="../jspf/entete.jspf" %> + + <%@include file="../jspf/navbar.jspf" %> +
+
+

Personnes

+
+ <%@include file="./filtre.jspf" %> +
+

Liste

+
+ +
+ +
+ + + +
+
+ +
+ + diff --git a/src/main/webapp/WEB-INF/web.xml b/src/main/webapp/WEB-INF/web.xml new file mode 100644 index 0000000000000000000000000000000000000000..456dcfd5ca9e45b2a47b8c2ea0db74f169dc1e46 --- /dev/null +++ b/src/main/webapp/WEB-INF/web.xml @@ -0,0 +1,43 @@ + + + + Accueil + /WEB-INF/accueil.jsp + + + Administration-datasource + /WEB-INF/database.jsp + + + Accueil + /accueil.html + /index.html + + + Administration-datasource + /administration/datasource.html + + + + 30 + + + + + 400 + /WEB-INF/erreurHTTP.jsp + + + 404 + /WEB-INF/erreurHTTP.jsp + + + 500 + /WEB-INF/erreurHTTP.jsp + + + java.lang.Exception + /WEB-INF/erreurException.jsp + + + diff --git a/src/main/webapp/css/main.css b/src/main/webapp/css/main.css new file mode 100644 index 0000000000000000000000000000000000000000..67f74a04fd468f629fa190e0d14df9d48687635b --- /dev/null +++ b/src/main/webapp/css/main.css @@ -0,0 +1,177 @@ +body { + font: normal 100% sans-serif; + margin: 1rem; +} + +.FenetreModale { + /* visibility: hidden; */ + position: fixed; + left: 0; + top: 0; + width: 100%; + height: 100%; + z-index: 200; + background-color: whitesmoke; + opacity:0.9; +} + +/*.FenetreModale div*/ +.FenetreModale-dialog { + max-width: 60%; /* 300px; */ + max-height: 40%; /*300px; */ + margin: 5% auto; + background-color: white; + border:1px solid #000; + padding:1%; + overflow : auto; +} + +:required { + border-color: red; +} + +body > nav > ul { + display: flex; + flex-wrap: wrap; + list-style: none; + padding: 0; +} + + +body > nav > ul > li { + padding-right: .5rem; +} + +body > nav > ul > li:nth-child(n+2):before { + content: "|"; + padding-right: .5rem; +} + +.item-list { + margin-top: 1rem; + display: flex; + flex-wrap: wrap; + flex-direction: row; + +} + +.item-list > * { + border: 1px solid #ccc; + padding: .5rem; + flex-basis: 25rem; +} + +.item-list > article > header > * { + margin: 0; +} + +small { + font-size: .5rem; +} + + +.form-recherche { + display: flex; + flex-wrap: wrap; +} + +.form-recherche input { + flex-grow: 2; +} + +.form-recherche * { + margin-top: 0.5rem; + +} + +.grid-form { + display: grid; + grid-template-columns: [first] 1fr [control] 1fr 1fr 2fr 1fr [end]; + row-gap: .5em; + column-gap: .5em; +} + +.grid-form > nav, .grid-form > small { + grid-column: first / end; + grid-row: auto; + +} + +.grid-form > label { + grid-column: first; + grid-row: auto; +} + +.grid-form > input, +.grid-form > span, +.grid-form > textarea { + grid-column: control /end; + grid-row: auto; +} + + +.grid-form span { + display: flex; + flex-wrap: wrap; + align-items: center +} + +.grid-form span > *{ + margin-right: 0.5em +} + +.grid-form span > input{ + flex-grow: 1; +} + +.grid-form > section { + grid-column: first / end; + grid-row: auto; + + display: grid; + grid-template-columns: subgrid; + row-gap: .5em; + column-gap: .5em; + +} + +.grid-form > section > .new_row{ + grid-column: first; + grid-row: auto; +} + +.grid-form > section >h3{ + grid-column: first / end; + grid-row: auto; +} + +@media( max-width : 1200px){ + .grid-form > section { + display: grid; + grid-template-columns: [first] 1fr 1fr [end]; + } + +} + +@media( max-width : 500px){ + .grid-form { + display: grid; + grid-template-columns: [first control] 1fr [end]; + } + + .grid-form > section { + grid-template-columns: subgrid; + } + +} + +@media print { + nav, + aside, + h2 + form, + a { + display: none; + + } + +} \ No newline at end of file diff --git a/src/main/webapp/css/normalize.css b/src/main/webapp/css/normalize.css new file mode 100644 index 0000000000000000000000000000000000000000..da84267f594ab8a85870e6c56b32071b6f76bc0b --- /dev/null +++ b/src/main/webapp/css/normalize.css @@ -0,0 +1,431 @@ +/* Document + * ========================================================================== */ + +/** + * 1. Correct the line height in all browsers. + * 2. Prevent adjustments of font size after orientation changes in + * IE on Windows Phone and in iOS. + */ + +html { + line-height: 1.15; /* 1 */ + -ms-text-size-adjust: 100%; /* 2 */ + -webkit-text-size-adjust: 100%; /* 2 */ +} + +/* Sections + * ========================================================================== */ + +/** + * Correct the font size and margin on `h1` elements within `section` and + * `article` contexts in Chrome, Edge, Firefox, and Safari. + */ + +h1 { + font-size: 2em; + margin: 0.67em 0; +} + +/* Grouping content + * ========================================================================== */ + +/** + * Remove the margin on nested lists in Chrome, Edge, IE, and Safari. + */ + +dl dl, +dl ol, +dl ul, +ol dl, +ul dl { + margin: 0; +} + +/** + * Remove the margin on nested lists in Edge 18- and IE. + */ + +ol ol, +ol ul, +ul ol, +ul ul { + margin: 0; +} + +/** + * 1. Add the correct box sizing in Firefox. + * 2. Correct the inheritance of border color in Firefox. + * 3. Show the overflow in Edge 18- and IE. + */ + +hr { + box-sizing: content-box; /* 1 */ + color: inherit; /* 2 */ + height: 0; /* 1 */ + overflow: visible; /* 3 */ +} + +/** + * Add the correct display in IE. + */ + +main { + display: block; +} + +/** + * 1. Correct the inheritance and scaling of font size in all browsers. + * 2. Correct the odd `em` font sizing in all browsers. + */ + +pre { + font-family: monospace, monospace; /* 1 */ + font-size: 1em; /* 2 */ +} + +/* Text-level semantics + * ========================================================================== */ + +/** + * Remove the gray background on active links in IE 10. + */ + +a { + background-color: transparent; +} + +/** + * Add the correct text decoration in Edge 18-, IE, and Safari. + */ + +abbr[title] { + text-decoration: underline; + text-decoration: underline dotted; +} + +/** + * Add the correct font weight in Chrome, Edge, and Safari. + */ + +b, +strong { + font-weight: bolder; +} + +/** + * 1. Correct the inheritance and scaling of font size in all browsers. + * 2. Correct the odd `em` font sizing in all browsers. + */ + +code, +kbd, +samp { + font-family: monospace, monospace; /* 1 */ + font-size: 1em; /* 2 */ +} + +/** + * Add the correct font size in all browsers. + */ + +small { + font-size: 80%; +} + +/* Embedded content + * ========================================================================== */ + +/** + * Add the correct display in IE 9-. + */ + +audio, +video { + display: inline-block; +} + +/** + * Add the correct display in iOS 4-7. + */ + +audio:not([controls]) { + display: none; + height: 0; +} + +/** + * Remove the border on images within links in IE 10-. + */ + +img { + border-style: none; +} + +/** + * Hide the overflow in IE. + */ + +svg:not(:root) { + overflow: hidden; +} + +/* Tabular data + * ========================================================================== */ + +/** + * 1. Correct table border color inheritance in all Chrome, Edge, and Safari. + * 2. Remove text indentation from table contents in Chrome, Edge, and Safari. + */ + +table { + border-color: inherit; /* 1 */ + text-indent: 0; /* 2 */ +} + +/* Forms + * ========================================================================== */ + +/** + * Remove the margin on controls in Safari. + */ + +button, +input, +select { + margin: 0; +} + +/** + * 1. Show the overflow in IE. + * 2. Remove the inheritance of text transform in Edge 18-, Firefox, and IE. + */ + +button { + overflow: visible; /* 1 */ + text-transform: none; /* 2 */ +} + +/** + * Correct the inability to style buttons in iOS and Safari. + */ + +button, +[type="button"], +[type="reset"], +[type="submit"] { + -webkit-appearance: button; +} + +/** + * Correct the padding in Firefox. + */ + +fieldset { + padding: 0.35em 0.75em 0.625em; +} + +/** + * Show the overflow in Edge 18- and IE. + */ + +input { + overflow: visible; +} + +/** + * 1. Correct the text wrapping in Edge 18- and IE. + * 2. Correct the color inheritance from `fieldset` elements in IE. + */ + +legend { + box-sizing: border-box; /* 1 */ + color: inherit; /* 2 */ + display: table; /* 1 */ + max-width: 100%; /* 1 */ + white-space: normal; /* 1 */ +} + +/** + * 1. Add the correct display in Edge 18- and IE. + * 2. Add the correct vertical alignment in Chrome, Edge, and Firefox. + */ + +progress { + display: inline-block; /* 1 */ + vertical-align: baseline; /* 2 */ +} + +/** + * Remove the inheritance of text transform in Firefox. + */ + +select { + text-transform: none; +} + +/** + * 1. Remove the margin in Firefox and Safari. + * 2. Remove the default vertical scrollbar in IE. + */ + +textarea { + margin: 0; /* 1 */ + overflow: auto; /* 2 */ +} + +/** + * 1. Add the correct box sizing in IE 10-. + * 2. Remove the padding in IE 10-. + */ + +[type="checkbox"], +[type="radio"] { + box-sizing: border-box; /* 1 */ + padding: 0; /* 2 */ +} + +/** + * 1. Correct the odd appearance in Chrome, Edge, and Safari. + * 2. Correct the outline style in Safari. + */ + +[type="search"] { + -webkit-appearance: textfield; /* 1 */ + outline-offset: -2px; /* 2 */ +} + +/** + * Correct the cursor style of increment and decrement buttons in Safari. + */ + +::-webkit-inner-spin-button, +::-webkit-outer-spin-button { + height: auto; +} + +/** + * Correct the text style of placeholders in Chrome, Edge, and Safari. + */ + +::-webkit-input-placeholder { + color: inherit; + opacity: 0.54; +} + +/** + * Remove the inner padding in Chrome, Edge, and Safari on macOS. + */ + +::-webkit-search-decoration { + -webkit-appearance: none; +} + +/** + * 1. Correct the inability to style upload buttons in iOS and Safari. + * 2. Change font properties to `inherit` in Safari. + */ + +::-webkit-file-upload-button { + -webkit-appearance: button; /* 1 */ + font: inherit; /* 2 */ +} + +/** + * Remove the inner border and padding of focus outlines in Firefox. + */ + +::-moz-focus-inner { + border-style: none; + padding: 0; +} + +/** + * Restore the focus outline styles unset by the previous rule in Firefox. + */ + +:-moz-focusring { + outline: 1px dotted ButtonText; +} + +/** + * Remove the additional :invalid styles in Firefox. + */ + +:-moz-ui-invalid { + box-shadow: none; +} + +/* Interactive + * ========================================================================== */ + +/* + * Add the correct display in Edge 18- and IE. + */ + +details { + display: block; +} + +/* + * Add the correct styles in Edge 18-, IE, and Safari. + */ + +dialog { + background-color: white; + border: solid; + color: black; + display: block; + height: -moz-fit-content; + height: -webkit-fit-content; + height: fit-content; + left: 0; + margin: auto; + padding: 1em; + position: absolute; + right: 0; + width: -moz-fit-content; + width: -webkit-fit-content; + width: fit-content; +} + +dialog:not([open]) { + display: none; +} + +/* + * Add the correct display in all browsers. + */ + +summary { + display: list-item; +} + +/* Scripting + * ========================================================================== */ + +/** + * Add the correct display in IE 9-. + */ + +canvas { + display: inline-block; +} + +/** + * Add the correct display in IE. + */ + +template { + display: none; +} + +/* User interaction + * ========================================================================== */ + +/** + * Add the correct display in IE 10-. + */ + +[hidden] { + display: none; +}