mirror of
https://github.com/spring-projects/spring-boot.git
synced 2024-07-05 00:56:58 +08:00
[bs-118], [bs-119]: add UI builder features
* MessageSource created automatically (location spring.messages.basename:messages) * Thymeleaf configured automatically to look for templates in classpath:/templates * Added static resource handlers for classpath:/static and classpath:/ [Fixes #49832165] [bs-118] Support for thymeleaf templates
This commit is contained in:
parent
ceea71fc38
commit
53078c320e
1
.gitignore
vendored
1
.gitignore
vendored
@ -10,3 +10,4 @@
|
||||
bin
|
||||
build
|
||||
target
|
||||
.springBeans
|
||||
|
15
pom.xml
15
pom.xml
@ -309,6 +309,11 @@
|
||||
<artifactId>javax.servlet-api</artifactId>
|
||||
<version>3.0.1</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>javax.servlet</groupId>
|
||||
<artifactId>jstl</artifactId>
|
||||
<version>1.2</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.hibernate.javax.persistence</groupId>
|
||||
<artifactId>hibernate-jpa-2.0-api</artifactId>
|
||||
@ -525,6 +530,16 @@
|
||||
<artifactId>snakeyaml</artifactId>
|
||||
<version>1.12</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.thymeleaf</groupId>
|
||||
<artifactId>thymeleaf-spring3</artifactId>
|
||||
<version>2.0.16</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>nz.net.ultraq.web.thymeleaf</groupId>
|
||||
<artifactId>thymeleaf-layout-dialect</artifactId>
|
||||
<version>1.0.6</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</dependencyManagement>
|
||||
<dependencies>
|
||||
|
@ -1,4 +1,2 @@
|
||||
org.springframework.bootstrap.context.annotation.EnableAutoConfiguration=\
|
||||
org.springframework.bootstrap.actuate.autoconfigure.ActuatorAutoConfiguration
|
||||
org.springframework.context.ApplicationContextInitializer=\
|
||||
org.springframework.bootstrap.logging.LoggingInitializer
|
||||
|
@ -3,10 +3,10 @@
|
||||
<parent>
|
||||
<artifactId>spring-bootstrap-samples</artifactId>
|
||||
<groupId>org.springframework.bootstrap</groupId>
|
||||
<version>0.0.1-SNAPSHOT</version>
|
||||
<version>0.5.0.BUILD-SNAPSHOT</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<artifactId>spring-bootstrap-service-sample</artifactId>
|
||||
<artifactId>spring-bootstrap-integration-sample</artifactId>
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
@ -14,77 +14,6 @@
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
<profiles>
|
||||
<profile>
|
||||
<id>tomcat</id>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.apache.tomcat.embed</groupId>
|
||||
<artifactId>tomcat-embed-core</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.tomcat.embed</groupId>
|
||||
<artifactId>tomcat-embed-logging-juli</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</profile>
|
||||
<profile>
|
||||
<id>jetty</id>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.eclipse.jetty</groupId>
|
||||
<artifactId>jetty-webapp</artifactId>
|
||||
<version>8.1.9.v20130131</version>
|
||||
<scope>compile</scope>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<artifactId>javax.servlet</artifactId>
|
||||
<groupId>org.eclipse.jetty.orbit</groupId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.eclipse.jetty</groupId>
|
||||
<artifactId>jetty-jsp</artifactId>
|
||||
<version>8.1.9.v20130131</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</profile>
|
||||
<profile>
|
||||
<id>juli</id>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>slf4j-jdk14</artifactId>
|
||||
<version>1.7.2</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</profile>
|
||||
<profile>
|
||||
<id>log4j</id>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>slf4j-log4j12</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>log4j</groupId>
|
||||
<artifactId>log4j</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</profile>
|
||||
<profile>
|
||||
<id>logback</id>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>ch.qos.logback</groupId>
|
||||
<artifactId>logback-classic</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</profile>
|
||||
</profiles>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>junit</groupId>
|
||||
@ -128,8 +57,7 @@
|
||||
</dependency>
|
||||
</dependencies>
|
||||
<properties>
|
||||
<main.basedir>${project.basedir}/../..</main.basedir>
|
||||
<start-class>org.springframework.bootstrap.sample.service.ServiceBootstrapApplication</start-class>
|
||||
<start-class>org.springframework.bootstrap.sample.service.IntegrationBootstrapApplication</start-class>
|
||||
</properties>
|
||||
</project>
|
||||
|
||||
|
@ -41,7 +41,7 @@ public class TradBootstrapApplicationTests {
|
||||
.run(TradBootstrapApplication.class);
|
||||
}
|
||||
});
|
||||
context = future.get(10, TimeUnit.SECONDS);
|
||||
context = future.get(30, TimeUnit.SECONDS);
|
||||
}
|
||||
|
||||
@AfterClass
|
||||
|
64
spring-bootstrap-samples/spring-bootstrap-ui-sample/pom.xml
Normal file
64
spring-bootstrap-samples/spring-bootstrap-ui-sample/pom.xml
Normal file
@ -0,0 +1,64 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
<groupId>org.springframework.bootstrap</groupId>
|
||||
<artifactId>spring-bootstrap-samples</artifactId>
|
||||
<version>0.5.0.BUILD-SNAPSHOT</version>
|
||||
</parent>
|
||||
<artifactId>spring-bootstrap-ui-sample</artifactId>
|
||||
<packaging>war</packaging>
|
||||
<properties>
|
||||
<m2eclipse.wtp.contextRoot>/</m2eclipse.wtp.contextRoot>
|
||||
<start-class>org.springframework.bootstrap.sample.ui.UiBootstrapApplication</start-class>
|
||||
</properties>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>${project.groupId}</groupId>
|
||||
<artifactId>spring-bootstrap</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-webmvc</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.thymeleaf</groupId>
|
||||
<artifactId>thymeleaf-spring3</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>nz.net.ultraq.web.thymeleaf</groupId>
|
||||
<artifactId>thymeleaf-layout-dialect</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.tomcat.embed</groupId>
|
||||
<artifactId>tomcat-embed-core</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.tomcat.embed</groupId>
|
||||
<artifactId>tomcat-embed-logging-juli</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.hibernate</groupId>
|
||||
<artifactId>hibernate-validator</artifactId>
|
||||
<version>4.3.1.Final</version>
|
||||
<!-- FIXME: make this optional -->
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>slf4j-jdk14</artifactId>
|
||||
<scope>runtime</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<artifactId>maven-dependency-plugin</artifactId>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<artifactId>maven-assembly-plugin</artifactId>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</project>
|
@ -0,0 +1,52 @@
|
||||
/*
|
||||
* Copyright 2012-2013 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.springframework.bootstrap.sample.ui;
|
||||
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ConcurrentMap;
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
|
||||
/**
|
||||
* @author Dave Syer
|
||||
*
|
||||
*/
|
||||
public class InMemoryMessageRespository implements MessageRepository {
|
||||
|
||||
private static AtomicLong counter = new AtomicLong();
|
||||
private ConcurrentMap<Long, Message> messages = new ConcurrentHashMap<Long, Message>();
|
||||
|
||||
@Override
|
||||
public Iterable<Message> findAll() {
|
||||
return this.messages.values();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Message save(Message message) {
|
||||
Long id = message.getId();
|
||||
if (id == null) {
|
||||
id = counter.incrementAndGet();
|
||||
message.setId(id);
|
||||
}
|
||||
this.messages.put(id, message);
|
||||
return message;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Message findMessage(Long id) {
|
||||
return this.messages.get(id);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,67 @@
|
||||
/*
|
||||
* Copyright 2012 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.springframework.bootstrap.sample.ui;
|
||||
|
||||
import java.util.Calendar;
|
||||
|
||||
import org.hibernate.validator.constraints.NotEmpty;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Rob Winch
|
||||
*
|
||||
*/
|
||||
public class Message {
|
||||
|
||||
private Long id;
|
||||
|
||||
@NotEmpty(message = "Message is required.")
|
||||
private String text;
|
||||
|
||||
@NotEmpty(message = "Summary is required.")
|
||||
private String summary;
|
||||
|
||||
private Calendar created = Calendar.getInstance();
|
||||
|
||||
public Long getId() {
|
||||
return this.id;
|
||||
}
|
||||
|
||||
public void setId(Long id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public Calendar getCreated() {
|
||||
return this.created;
|
||||
}
|
||||
|
||||
public void setCreated(Calendar created) {
|
||||
this.created = created;
|
||||
}
|
||||
|
||||
public String getText() {
|
||||
return this.text;
|
||||
}
|
||||
|
||||
public void setText(String text) {
|
||||
this.text = text;
|
||||
}
|
||||
|
||||
public String getSummary() {
|
||||
return this.summary;
|
||||
}
|
||||
|
||||
public void setSummary(String summary) {
|
||||
this.summary = summary;
|
||||
}
|
||||
}
|
@ -0,0 +1,28 @@
|
||||
/*
|
||||
* Copyright 2012 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.springframework.bootstrap.sample.ui;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Rob Winch
|
||||
*
|
||||
*/
|
||||
public interface MessageRepository {
|
||||
|
||||
Iterable<Message> findAll();
|
||||
|
||||
Message save(Message message);
|
||||
|
||||
Message findMessage(Long id);
|
||||
|
||||
}
|
@ -0,0 +1,34 @@
|
||||
package org.springframework.bootstrap.sample.ui;
|
||||
|
||||
import org.springframework.bootstrap.SpringApplication;
|
||||
import org.springframework.bootstrap.context.annotation.EnableAutoConfiguration;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.ComponentScan;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.core.convert.converter.Converter;
|
||||
|
||||
@Configuration
|
||||
@EnableAutoConfiguration
|
||||
@ComponentScan
|
||||
public class UiBootstrapApplication {
|
||||
|
||||
@Bean
|
||||
public MessageRepository messageRepository() {
|
||||
return new InMemoryMessageRespository();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public Converter<String, Message> messageConverter() {
|
||||
return new Converter<String, Message>() {
|
||||
@Override
|
||||
public Message convert(String id) {
|
||||
return messageRepository().findMessage(Long.valueOf(id));
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
SpringApplication.run(UiBootstrapApplication.class, args);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,82 @@
|
||||
/*
|
||||
* Copyright 2012 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.springframework.bootstrap.sample.ui.mvc;
|
||||
|
||||
import javax.validation.Valid;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.bootstrap.sample.ui.Message;
|
||||
import org.springframework.bootstrap.sample.ui.MessageRepository;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.validation.BindingResult;
|
||||
import org.springframework.web.bind.annotation.ModelAttribute;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMethod;
|
||||
import org.springframework.web.servlet.ModelAndView;
|
||||
import org.springframework.web.servlet.mvc.support.RedirectAttributes;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Rob Winch
|
||||
*
|
||||
*/
|
||||
@Controller
|
||||
@RequestMapping("/")
|
||||
public class MessageController {
|
||||
private MessageRepository messageRepository;
|
||||
|
||||
@Autowired
|
||||
public MessageController(MessageRepository messageRepository) {
|
||||
this.messageRepository = messageRepository;
|
||||
}
|
||||
|
||||
@RequestMapping
|
||||
public ModelAndView list() {
|
||||
Iterable<Message> messages = messageRepository.findAll();
|
||||
return new ModelAndView("messages/list", "messages", messages);
|
||||
}
|
||||
|
||||
@RequestMapping("{id}")
|
||||
public ModelAndView view(@PathVariable("id")
|
||||
Message message) {
|
||||
return new ModelAndView("messages/view", "message", message);
|
||||
}
|
||||
|
||||
@RequestMapping(params = "form", method = RequestMethod.GET)
|
||||
public String createForm(@ModelAttribute
|
||||
Message message) {
|
||||
return "messages/form";
|
||||
}
|
||||
|
||||
@RequestMapping(method = RequestMethod.POST)
|
||||
public ModelAndView create(@Valid
|
||||
Message message, BindingResult result, RedirectAttributes redirect) {
|
||||
if (result.hasErrors()) {
|
||||
return new ModelAndView("messages/form", "formErrors",
|
||||
result.getAllErrors());
|
||||
}
|
||||
message = messageRepository.save(message);
|
||||
redirect.addFlashAttribute("globalMessage",
|
||||
"Successfully created a new message");
|
||||
return new ModelAndView("redirect:/{message.id}",
|
||||
"message.id", message.getId());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
11
spring-bootstrap-samples/spring-bootstrap-ui-sample/src/main/resources/css/bootstrap.min.css
vendored
Normal file
11
spring-bootstrap-samples/spring-bootstrap-ui-sample/src/main/resources/css/bootstrap.min.css
vendored
Normal file
File diff suppressed because one or more lines are too long
9404
spring-bootstrap-samples/spring-bootstrap-ui-sample/src/main/resources/js/jquery-1.7.2.js
vendored
Normal file
9404
spring-bootstrap-samples/spring-bootstrap-ui-sample/src/main/resources/js/jquery-1.7.2.js
vendored
Normal file
File diff suppressed because it is too large
Load Diff
1250
spring-bootstrap-samples/spring-bootstrap-ui-sample/src/main/resources/js/jquery.validate.js
vendored
Normal file
1250
spring-bootstrap-samples/spring-bootstrap-ui-sample/src/main/resources/js/jquery.validate.js
vendored
Normal file
@ -0,0 +1,1250 @@
|
||||
/**
|
||||
* jQuery Validation Plugin @VERSION
|
||||
*
|
||||
* http://bassistance.de/jquery-plugins/jquery-plugin-validation/
|
||||
* http://docs.jquery.com/Plugins/Validation
|
||||
*
|
||||
* Copyright (c) 2012 Jörn Zaefferer
|
||||
*
|
||||
* Dual licensed under the MIT and GPL licenses:
|
||||
* http://www.opensource.org/licenses/mit-license.php
|
||||
* http://www.gnu.org/licenses/gpl.html
|
||||
*/
|
||||
|
||||
(function($) {
|
||||
|
||||
$.extend($.fn, {
|
||||
// http://docs.jquery.com/Plugins/Validation/validate
|
||||
validate: function( options ) {
|
||||
|
||||
// if nothing is selected, return nothing; can't chain anyway
|
||||
if (!this.length) {
|
||||
if (options && options.debug && window.console) {
|
||||
console.warn( "nothing selected, can't validate, returning nothing" );
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// check if a validator for this form was already created
|
||||
var validator = $.data(this[0], 'validator');
|
||||
if ( validator ) {
|
||||
return validator;
|
||||
}
|
||||
|
||||
// Add novalidate tag if HTML5.
|
||||
this.attr('novalidate', 'novalidate');
|
||||
|
||||
validator = new $.validator( options, this[0] );
|
||||
$.data(this[0], 'validator', validator);
|
||||
|
||||
if ( validator.settings.onsubmit ) {
|
||||
|
||||
this.validateDelegate( ":submit", "click", function(ev) {
|
||||
if ( validator.settings.submitHandler ) {
|
||||
validator.submitButton = ev.target;
|
||||
}
|
||||
// allow suppressing validation by adding a cancel class to the submit button
|
||||
if ( $(ev.target).hasClass('cancel') ) {
|
||||
validator.cancelSubmit = true;
|
||||
}
|
||||
});
|
||||
|
||||
// validate the form on submit
|
||||
this.submit( function( event ) {
|
||||
if ( validator.settings.debug ) {
|
||||
// prevent form submit to be able to see console output
|
||||
event.preventDefault();
|
||||
}
|
||||
function handle() {
|
||||
var hidden;
|
||||
if ( validator.settings.submitHandler ) {
|
||||
if (validator.submitButton) {
|
||||
// insert a hidden input as a replacement for the missing submit button
|
||||
hidden = $("<input type='hidden'/>").attr("name", validator.submitButton.name).val(validator.submitButton.value).appendTo(validator.currentForm);
|
||||
}
|
||||
validator.settings.submitHandler.call( validator, validator.currentForm, event );
|
||||
if (validator.submitButton) {
|
||||
// and clean up afterwards; thanks to no-block-scope, hidden can be referenced
|
||||
hidden.remove();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// prevent submit for invalid forms or custom submit handlers
|
||||
if ( validator.cancelSubmit ) {
|
||||
validator.cancelSubmit = false;
|
||||
return handle();
|
||||
}
|
||||
if ( validator.form() ) {
|
||||
if ( validator.pendingRequest ) {
|
||||
validator.formSubmitted = true;
|
||||
return false;
|
||||
}
|
||||
return handle();
|
||||
} else {
|
||||
validator.focusInvalid();
|
||||
return false;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return validator;
|
||||
},
|
||||
// http://docs.jquery.com/Plugins/Validation/valid
|
||||
valid: function() {
|
||||
if ( $(this[0]).is('form')) {
|
||||
return this.validate().form();
|
||||
} else {
|
||||
var valid = true;
|
||||
var validator = $(this[0].form).validate();
|
||||
this.each(function() {
|
||||
valid &= validator.element(this);
|
||||
});
|
||||
return valid;
|
||||
}
|
||||
},
|
||||
// attributes: space seperated list of attributes to retrieve and remove
|
||||
removeAttrs: function(attributes) {
|
||||
var result = {},
|
||||
$element = this;
|
||||
$.each(attributes.split(/\s/), function(index, value) {
|
||||
result[value] = $element.attr(value);
|
||||
$element.removeAttr(value);
|
||||
});
|
||||
return result;
|
||||
},
|
||||
// http://docs.jquery.com/Plugins/Validation/rules
|
||||
rules: function(command, argument) {
|
||||
var element = this[0];
|
||||
|
||||
if (command) {
|
||||
var settings = $.data(element.form, 'validator').settings;
|
||||
var staticRules = settings.rules;
|
||||
var existingRules = $.validator.staticRules(element);
|
||||
switch(command) {
|
||||
case "add":
|
||||
$.extend(existingRules, $.validator.normalizeRule(argument));
|
||||
staticRules[element.name] = existingRules;
|
||||
if (argument.messages) {
|
||||
settings.messages[element.name] = $.extend( settings.messages[element.name], argument.messages );
|
||||
}
|
||||
break;
|
||||
case "remove":
|
||||
if (!argument) {
|
||||
delete staticRules[element.name];
|
||||
return existingRules;
|
||||
}
|
||||
var filtered = {};
|
||||
$.each(argument.split(/\s/), function(index, method) {
|
||||
filtered[method] = existingRules[method];
|
||||
delete existingRules[method];
|
||||
});
|
||||
return filtered;
|
||||
}
|
||||
}
|
||||
|
||||
var data = $.validator.normalizeRules(
|
||||
$.extend(
|
||||
{},
|
||||
$.validator.metadataRules(element),
|
||||
$.validator.classRules(element),
|
||||
$.validator.attributeRules(element),
|
||||
$.validator.staticRules(element)
|
||||
), element);
|
||||
|
||||
// make sure required is at front
|
||||
if (data.required) {
|
||||
var param = data.required;
|
||||
delete data.required;
|
||||
data = $.extend({required: param}, data);
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
});
|
||||
|
||||
// Custom selectors
|
||||
$.extend($.expr[":"], {
|
||||
// http://docs.jquery.com/Plugins/Validation/blank
|
||||
blank: function(a) {return !$.trim("" + a.value);},
|
||||
// http://docs.jquery.com/Plugins/Validation/filled
|
||||
filled: function(a) {return !!$.trim("" + a.value);},
|
||||
// http://docs.jquery.com/Plugins/Validation/unchecked
|
||||
unchecked: function(a) {return !a.checked;}
|
||||
});
|
||||
|
||||
// constructor for validator
|
||||
$.validator = function( options, form ) {
|
||||
this.settings = $.extend( true, {}, $.validator.defaults, options );
|
||||
this.currentForm = form;
|
||||
this.init();
|
||||
};
|
||||
|
||||
$.validator.format = function(source, params) {
|
||||
if ( arguments.length === 1 ) {
|
||||
return function() {
|
||||
var args = $.makeArray(arguments);
|
||||
args.unshift(source);
|
||||
return $.validator.format.apply( this, args );
|
||||
};
|
||||
}
|
||||
if ( arguments.length > 2 && params.constructor !== Array ) {
|
||||
params = $.makeArray(arguments).slice(1);
|
||||
}
|
||||
if ( params.constructor !== Array ) {
|
||||
params = [ params ];
|
||||
}
|
||||
$.each(params, function(i, n) {
|
||||
source = source.replace(new RegExp("\\{" + i + "\\}", "g"), n);
|
||||
});
|
||||
return source;
|
||||
};
|
||||
|
||||
$.extend($.validator, {
|
||||
|
||||
defaults: {
|
||||
messages: {},
|
||||
groups: {},
|
||||
rules: {},
|
||||
errorClass: "error",
|
||||
validClass: "valid",
|
||||
errorElement: "label",
|
||||
focusInvalid: true,
|
||||
errorContainer: $( [] ),
|
||||
errorLabelContainer: $( [] ),
|
||||
onsubmit: true,
|
||||
ignore: ":hidden",
|
||||
ignoreTitle: false,
|
||||
onfocusin: function(element, event) {
|
||||
this.lastActive = element;
|
||||
|
||||
// hide error label and remove error class on focus if enabled
|
||||
if ( this.settings.focusCleanup && !this.blockFocusCleanup ) {
|
||||
if ( this.settings.unhighlight ) {
|
||||
this.settings.unhighlight.call( this, element, this.settings.errorClass, this.settings.validClass );
|
||||
}
|
||||
this.addWrapper(this.errorsFor(element)).hide();
|
||||
}
|
||||
},
|
||||
onfocusout: function(element, event) {
|
||||
if ( !this.checkable(element) && (element.name in this.submitted || !this.optional(element)) ) {
|
||||
this.element(element);
|
||||
}
|
||||
},
|
||||
onkeyup: function(element, event) {
|
||||
if ( element.name in this.submitted || element === this.lastElement ) {
|
||||
this.element(element);
|
||||
}
|
||||
},
|
||||
onclick: function(element, event) {
|
||||
// click on selects, radiobuttons and checkboxes
|
||||
if ( element.name in this.submitted ) {
|
||||
this.element(element);
|
||||
}
|
||||
// or option elements, check parent select in that case
|
||||
else if (element.parentNode.name in this.submitted) {
|
||||
this.element(element.parentNode);
|
||||
}
|
||||
},
|
||||
highlight: function(element, errorClass, validClass) {
|
||||
if (element.type === 'radio') {
|
||||
this.findByName(element.name).addClass(errorClass).removeClass(validClass);
|
||||
} else {
|
||||
$(element).addClass(errorClass).removeClass(validClass);
|
||||
}
|
||||
},
|
||||
unhighlight: function(element, errorClass, validClass) {
|
||||
if (element.type === 'radio') {
|
||||
this.findByName(element.name).removeClass(errorClass).addClass(validClass);
|
||||
} else {
|
||||
$(element).removeClass(errorClass).addClass(validClass);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
// http://docs.jquery.com/Plugins/Validation/Validator/setDefaults
|
||||
setDefaults: function(settings) {
|
||||
$.extend( $.validator.defaults, settings );
|
||||
},
|
||||
|
||||
messages: {
|
||||
required: "This field is required.",
|
||||
remote: "Please fix this field.",
|
||||
email: "Please enter a valid email address.",
|
||||
url: "Please enter a valid URL.",
|
||||
date: "Please enter a valid date.",
|
||||
dateISO: "Please enter a valid date (ISO).",
|
||||
number: "Please enter a valid number.",
|
||||
digits: "Please enter only digits.",
|
||||
creditcard: "Please enter a valid credit card number.",
|
||||
equalTo: "Please enter the same value again.",
|
||||
accept: "Please enter a value with a valid extension.",
|
||||
maxlength: $.validator.format("Please enter no more than {0} characters."),
|
||||
minlength: $.validator.format("Please enter at least {0} characters."),
|
||||
rangelength: $.validator.format("Please enter a value between {0} and {1} characters long."),
|
||||
range: $.validator.format("Please enter a value between {0} and {1}."),
|
||||
max: $.validator.format("Please enter a value less than or equal to {0}."),
|
||||
min: $.validator.format("Please enter a value greater than or equal to {0}.")
|
||||
},
|
||||
|
||||
autoCreateRanges: false,
|
||||
|
||||
prototype: {
|
||||
|
||||
init: function() {
|
||||
this.labelContainer = $(this.settings.errorLabelContainer);
|
||||
this.errorContext = this.labelContainer.length && this.labelContainer || $(this.currentForm);
|
||||
this.containers = $(this.settings.errorContainer).add( this.settings.errorLabelContainer );
|
||||
this.submitted = {};
|
||||
this.valueCache = {};
|
||||
this.pendingRequest = 0;
|
||||
this.pending = {};
|
||||
this.invalid = {};
|
||||
this.reset();
|
||||
|
||||
var groups = (this.groups = {});
|
||||
$.each(this.settings.groups, function(key, value) {
|
||||
$.each(value.split(/\s/), function(index, name) {
|
||||
groups[name] = key;
|
||||
});
|
||||
});
|
||||
var rules = this.settings.rules;
|
||||
$.each(rules, function(key, value) {
|
||||
rules[key] = $.validator.normalizeRule(value);
|
||||
});
|
||||
|
||||
function delegate(event) {
|
||||
var validator = $.data(this[0].form, "validator"),
|
||||
eventType = "on" + event.type.replace(/^validate/, "");
|
||||
if (validator.settings[eventType]) {
|
||||
validator.settings[eventType].call(validator, this[0], event);
|
||||
}
|
||||
}
|
||||
$(this.currentForm)
|
||||
.validateDelegate("[type='text'], [type='password'], [type='file'], select, textarea, " +
|
||||
"[type='number'], [type='search'] ,[type='tel'], [type='url'], " +
|
||||
"[type='email'], [type='datetime'], [type='date'], [type='month'], " +
|
||||
"[type='week'], [type='time'], [type='datetime-local'], " +
|
||||
"[type='range'], [type='color'] ",
|
||||
"focusin focusout keyup", delegate)
|
||||
.validateDelegate("[type='radio'], [type='checkbox'], select, option", "click", delegate);
|
||||
|
||||
if (this.settings.invalidHandler) {
|
||||
$(this.currentForm).bind("invalid-form.validate", this.settings.invalidHandler);
|
||||
}
|
||||
},
|
||||
|
||||
// http://docs.jquery.com/Plugins/Validation/Validator/form
|
||||
form: function() {
|
||||
this.checkForm();
|
||||
$.extend(this.submitted, this.errorMap);
|
||||
this.invalid = $.extend({}, this.errorMap);
|
||||
if (!this.valid()) {
|
||||
$(this.currentForm).triggerHandler("invalid-form", [this]);
|
||||
}
|
||||
this.showErrors();
|
||||
return this.valid();
|
||||
},
|
||||
|
||||
checkForm: function() {
|
||||
this.prepareForm();
|
||||
for ( var i = 0, elements = (this.currentElements = this.elements()); elements[i]; i++ ) {
|
||||
this.check( elements[i] );
|
||||
}
|
||||
return this.valid();
|
||||
},
|
||||
|
||||
// http://docs.jquery.com/Plugins/Validation/Validator/element
|
||||
element: function( element ) {
|
||||
element = this.validationTargetFor( this.clean( element ) );
|
||||
this.lastElement = element;
|
||||
this.prepareElement( element );
|
||||
this.currentElements = $(element);
|
||||
var result = this.check( element ) !== false;
|
||||
if (result) {
|
||||
delete this.invalid[element.name];
|
||||
} else {
|
||||
this.invalid[element.name] = true;
|
||||
}
|
||||
if ( !this.numberOfInvalids() ) {
|
||||
// Hide error containers on last error
|
||||
this.toHide = this.toHide.add( this.containers );
|
||||
}
|
||||
this.showErrors();
|
||||
return result;
|
||||
},
|
||||
|
||||
// http://docs.jquery.com/Plugins/Validation/Validator/showErrors
|
||||
showErrors: function(errors) {
|
||||
if(errors) {
|
||||
// add items to error list and map
|
||||
$.extend( this.errorMap, errors );
|
||||
this.errorList = [];
|
||||
for ( var name in errors ) {
|
||||
this.errorList.push({
|
||||
message: errors[name],
|
||||
element: this.findByName(name)[0]
|
||||
});
|
||||
}
|
||||
// remove items from success list
|
||||
this.successList = $.grep( this.successList, function(element) {
|
||||
return !(element.name in errors);
|
||||
});
|
||||
}
|
||||
if (this.settings.showErrors) {
|
||||
this.settings.showErrors.call( this, this.errorMap, this.errorList );
|
||||
} else {
|
||||
this.defaultShowErrors();
|
||||
}
|
||||
},
|
||||
|
||||
// http://docs.jquery.com/Plugins/Validation/Validator/resetForm
|
||||
resetForm: function() {
|
||||
if ( $.fn.resetForm ) {
|
||||
$( this.currentForm ).resetForm();
|
||||
}
|
||||
this.submitted = {};
|
||||
this.lastElement = null;
|
||||
this.prepareForm();
|
||||
this.hideErrors();
|
||||
this.elements().removeClass( this.settings.errorClass );
|
||||
},
|
||||
|
||||
numberOfInvalids: function() {
|
||||
return this.objectLength(this.invalid);
|
||||
},
|
||||
|
||||
objectLength: function( obj ) {
|
||||
var count = 0;
|
||||
for ( var i in obj ) {
|
||||
count++;
|
||||
}
|
||||
return count;
|
||||
},
|
||||
|
||||
hideErrors: function() {
|
||||
this.addWrapper( this.toHide ).hide();
|
||||
},
|
||||
|
||||
valid: function() {
|
||||
return this.size() === 0;
|
||||
},
|
||||
|
||||
size: function() {
|
||||
return this.errorList.length;
|
||||
},
|
||||
|
||||
focusInvalid: function() {
|
||||
if( this.settings.focusInvalid ) {
|
||||
try {
|
||||
$(this.findLastActive() || this.errorList.length && this.errorList[0].element || [])
|
||||
.filter(":visible")
|
||||
.focus()
|
||||
// manually trigger focusin event; without it, focusin handler isn't called, findLastActive won't have anything to find
|
||||
.trigger("focusin");
|
||||
} catch(e) {
|
||||
// ignore IE throwing errors when focusing hidden elements
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
findLastActive: function() {
|
||||
var lastActive = this.lastActive;
|
||||
return lastActive && $.grep(this.errorList, function(n) {
|
||||
return n.element.name === lastActive.name;
|
||||
}).length === 1 && lastActive;
|
||||
},
|
||||
|
||||
elements: function() {
|
||||
var validator = this,
|
||||
rulesCache = {};
|
||||
|
||||
// select all valid inputs inside the form (no submit or reset buttons)
|
||||
return $(this.currentForm)
|
||||
.find("input, select, textarea")
|
||||
.not(":submit, :reset, :image, [disabled]")
|
||||
.not( this.settings.ignore )
|
||||
.filter(function() {
|
||||
if ( !this.name && validator.settings.debug && window.console ) {
|
||||
console.error( "%o has no name assigned", this);
|
||||
}
|
||||
|
||||
// select only the first element for each name, and only those with rules specified
|
||||
if ( this.name in rulesCache || !validator.objectLength($(this).rules()) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
rulesCache[this.name] = true;
|
||||
return true;
|
||||
});
|
||||
},
|
||||
|
||||
clean: function( selector ) {
|
||||
return $( selector )[0];
|
||||
},
|
||||
|
||||
errors: function() {
|
||||
var errorClass = this.settings.errorClass.replace(' ', '.');
|
||||
return $( this.settings.errorElement + "." + errorClass, this.errorContext );
|
||||
},
|
||||
|
||||
reset: function() {
|
||||
this.successList = [];
|
||||
this.errorList = [];
|
||||
this.errorMap = {};
|
||||
this.toShow = $([]);
|
||||
this.toHide = $([]);
|
||||
this.currentElements = $([]);
|
||||
},
|
||||
|
||||
prepareForm: function() {
|
||||
this.reset();
|
||||
this.toHide = this.errors().add( this.containers );
|
||||
},
|
||||
|
||||
prepareElement: function( element ) {
|
||||
this.reset();
|
||||
this.toHide = this.errorsFor(element);
|
||||
},
|
||||
|
||||
elementValue: function( element ) {
|
||||
var val = $(element).val();
|
||||
if( typeof val === 'string' ) {
|
||||
return val.replace(/\r/g, "");
|
||||
}
|
||||
return val;
|
||||
},
|
||||
|
||||
check: function( element ) {
|
||||
element = this.validationTargetFor( this.clean( element ) );
|
||||
|
||||
var rules = $(element).rules();
|
||||
var dependencyMismatch = false;
|
||||
var val = this.elementValue(element);
|
||||
var result;
|
||||
|
||||
for (var method in rules ) {
|
||||
var rule = { method: method, parameters: rules[method] };
|
||||
try {
|
||||
|
||||
result = $.validator.methods[method].call( this, val, element, rule.parameters );
|
||||
|
||||
// if a method indicates that the field is optional and therefore valid,
|
||||
// don't mark it as valid when there are no other rules
|
||||
if ( result === "dependency-mismatch" ) {
|
||||
dependencyMismatch = true;
|
||||
continue;
|
||||
}
|
||||
dependencyMismatch = false;
|
||||
|
||||
if ( result === "pending" ) {
|
||||
this.toHide = this.toHide.not( this.errorsFor(element) );
|
||||
return;
|
||||
}
|
||||
|
||||
if( !result ) {
|
||||
this.formatAndAdd( element, rule );
|
||||
return false;
|
||||
}
|
||||
} catch(e) {
|
||||
if ( this.settings.debug && window.console ) {
|
||||
console.log("exception occured when checking element " + element.id + ", check the '" + rule.method + "' method", e);
|
||||
}
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
if (dependencyMismatch) {
|
||||
return;
|
||||
}
|
||||
if ( this.objectLength(rules) ) {
|
||||
this.successList.push(element);
|
||||
}
|
||||
return true;
|
||||
},
|
||||
|
||||
// return the custom message for the given element and validation method
|
||||
// specified in the element's "messages" metadata
|
||||
customMetaMessage: function(element, method) {
|
||||
if (!$.metadata) {
|
||||
return;
|
||||
}
|
||||
var meta = this.settings.meta ? $(element).metadata()[this.settings.meta] : $(element).metadata();
|
||||
return meta && meta.messages && meta.messages[method];
|
||||
},
|
||||
|
||||
// return the custom message for the given element name and validation method
|
||||
customMessage: function( name, method ) {
|
||||
var m = this.settings.messages[name];
|
||||
return m && (m.constructor === String ? m : m[method]);
|
||||
},
|
||||
|
||||
// return the first defined argument, allowing empty strings
|
||||
findDefined: function() {
|
||||
for(var i = 0; i < arguments.length; i++) {
|
||||
if (arguments[i] !== undefined) {
|
||||
return arguments[i];
|
||||
}
|
||||
}
|
||||
return undefined;
|
||||
},
|
||||
|
||||
defaultMessage: function( element, method) {
|
||||
return this.findDefined(
|
||||
this.customMessage( element.name, method ),
|
||||
this.customMetaMessage( element, method ),
|
||||
// title is never undefined, so handle empty string as undefined
|
||||
!this.settings.ignoreTitle && element.title || undefined,
|
||||
$.validator.messages[method],
|
||||
"<strong>Warning: No message defined for " + element.name + "</strong>"
|
||||
);
|
||||
},
|
||||
|
||||
formatAndAdd: function( element, rule ) {
|
||||
var message = this.defaultMessage( element, rule.method ),
|
||||
theregex = /\$?\{(\d+)\}/g;
|
||||
if ( typeof message === "function" ) {
|
||||
message = message.call(this, rule.parameters, element);
|
||||
} else if (theregex.test(message)) {
|
||||
message = $.validator.format(message.replace(theregex, '{$1}'), rule.parameters);
|
||||
}
|
||||
this.errorList.push({
|
||||
message: message,
|
||||
element: element
|
||||
});
|
||||
|
||||
this.errorMap[element.name] = message;
|
||||
this.submitted[element.name] = message;
|
||||
},
|
||||
|
||||
addWrapper: function(toToggle) {
|
||||
if ( this.settings.wrapper ) {
|
||||
toToggle = toToggle.add( toToggle.parent( this.settings.wrapper ) );
|
||||
}
|
||||
return toToggle;
|
||||
},
|
||||
|
||||
defaultShowErrors: function() {
|
||||
var i, elements;
|
||||
for ( i = 0; this.errorList[i]; i++ ) {
|
||||
var error = this.errorList[i];
|
||||
if ( this.settings.highlight ) {
|
||||
this.settings.highlight.call( this, error.element, this.settings.errorClass, this.settings.validClass );
|
||||
}
|
||||
this.showLabel( error.element, error.message );
|
||||
}
|
||||
if( this.errorList.length ) {
|
||||
this.toShow = this.toShow.add( this.containers );
|
||||
}
|
||||
if (this.settings.success) {
|
||||
for ( i = 0; this.successList[i]; i++ ) {
|
||||
this.showLabel( this.successList[i] );
|
||||
}
|
||||
}
|
||||
if (this.settings.unhighlight) {
|
||||
for ( i = 0, elements = this.validElements(); elements[i]; i++ ) {
|
||||
this.settings.unhighlight.call( this, elements[i], this.settings.errorClass, this.settings.validClass );
|
||||
}
|
||||
}
|
||||
this.toHide = this.toHide.not( this.toShow );
|
||||
this.hideErrors();
|
||||
this.addWrapper( this.toShow ).show();
|
||||
},
|
||||
|
||||
validElements: function() {
|
||||
return this.currentElements.not(this.invalidElements());
|
||||
},
|
||||
|
||||
invalidElements: function() {
|
||||
return $(this.errorList).map(function() {
|
||||
return this.element;
|
||||
});
|
||||
},
|
||||
|
||||
showLabel: function(element, message) {
|
||||
var label = this.errorsFor( element );
|
||||
if ( label.length ) {
|
||||
// refresh error/success class
|
||||
label.removeClass( this.settings.validClass ).addClass( this.settings.errorClass );
|
||||
|
||||
// check if we have a generated label, replace the message then
|
||||
if ( label.attr("generated") ) {
|
||||
label.html(message);
|
||||
}
|
||||
} else {
|
||||
// create label
|
||||
label = $("<" + this.settings.errorElement + "/>")
|
||||
.attr({"for": this.idOrName(element), generated: true})
|
||||
.addClass(this.settings.errorClass)
|
||||
.html(message || "");
|
||||
if ( this.settings.wrapper ) {
|
||||
// make sure the element is visible, even in IE
|
||||
// actually showing the wrapped element is handled elsewhere
|
||||
label = label.hide().show().wrap("<" + this.settings.wrapper + "/>").parent();
|
||||
}
|
||||
if ( !this.labelContainer.append(label).length ) {
|
||||
if ( this.settings.errorPlacement ) {
|
||||
this.settings.errorPlacement(label, $(element) );
|
||||
} else {
|
||||
label.insertAfter(element);
|
||||
}
|
||||
}
|
||||
}
|
||||
if ( !message && this.settings.success ) {
|
||||
label.text("");
|
||||
if ( typeof this.settings.success === "string" ) {
|
||||
label.addClass( this.settings.success );
|
||||
} else {
|
||||
this.settings.success( label );
|
||||
}
|
||||
}
|
||||
this.toShow = this.toShow.add(label);
|
||||
},
|
||||
|
||||
errorsFor: function(element) {
|
||||
var name = this.idOrName(element);
|
||||
return this.errors().filter(function() {
|
||||
return $(this).attr('for') === name;
|
||||
});
|
||||
},
|
||||
|
||||
idOrName: function(element) {
|
||||
return this.groups[element.name] || (this.checkable(element) ? element.name : element.id || element.name);
|
||||
},
|
||||
|
||||
validationTargetFor: function(element) {
|
||||
// if radio/checkbox, validate first element in group instead
|
||||
if (this.checkable(element)) {
|
||||
element = this.findByName( element.name ).not(this.settings.ignore)[0];
|
||||
}
|
||||
return element;
|
||||
},
|
||||
|
||||
checkable: function( element ) {
|
||||
return (/radio|checkbox/i).test(element.type);
|
||||
},
|
||||
|
||||
findByName: function( name ) {
|
||||
// select by name and filter by form for performance over form.find("[name=...]")
|
||||
var form = this.currentForm;
|
||||
return $(document.getElementsByName(name)).map(function(index, element) {
|
||||
return element.form === form && element.name === name && element || null;
|
||||
});
|
||||
},
|
||||
|
||||
getLength: function(value, element) {
|
||||
switch( element.nodeName.toLowerCase() ) {
|
||||
case 'select':
|
||||
return $("option:selected", element).length;
|
||||
case 'input':
|
||||
if( this.checkable( element) ) {
|
||||
return this.findByName(element.name).filter(':checked').length;
|
||||
}
|
||||
}
|
||||
return value.length;
|
||||
},
|
||||
|
||||
depend: function(param, element) {
|
||||
return this.dependTypes[typeof param] ? this.dependTypes[typeof param](param, element) : true;
|
||||
},
|
||||
|
||||
dependTypes: {
|
||||
"boolean": function(param, element) {
|
||||
return param;
|
||||
},
|
||||
"string": function(param, element) {
|
||||
return !!$(param, element.form).length;
|
||||
},
|
||||
"function": function(param, element) {
|
||||
return param(element);
|
||||
}
|
||||
},
|
||||
|
||||
optional: function(element) {
|
||||
var val = this.elementValue(element);
|
||||
return !$.validator.methods.required.call(this, val, element) && "dependency-mismatch";
|
||||
},
|
||||
|
||||
startRequest: function(element) {
|
||||
if (!this.pending[element.name]) {
|
||||
this.pendingRequest++;
|
||||
this.pending[element.name] = true;
|
||||
}
|
||||
},
|
||||
|
||||
stopRequest: function(element, valid) {
|
||||
this.pendingRequest--;
|
||||
// sometimes synchronization fails, make sure pendingRequest is never < 0
|
||||
if (this.pendingRequest < 0) {
|
||||
this.pendingRequest = 0;
|
||||
}
|
||||
delete this.pending[element.name];
|
||||
if ( valid && this.pendingRequest === 0 && this.formSubmitted && this.form() ) {
|
||||
$(this.currentForm).submit();
|
||||
this.formSubmitted = false;
|
||||
} else if (!valid && this.pendingRequest === 0 && this.formSubmitted) {
|
||||
$(this.currentForm).triggerHandler("invalid-form", [this]);
|
||||
this.formSubmitted = false;
|
||||
}
|
||||
},
|
||||
|
||||
previousValue: function(element) {
|
||||
return $.data(element, "previousValue") || $.data(element, "previousValue", {
|
||||
old: null,
|
||||
valid: true,
|
||||
message: this.defaultMessage( element, "remote" )
|
||||
});
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
classRuleSettings: {
|
||||
required: {required: true},
|
||||
email: {email: true},
|
||||
url: {url: true},
|
||||
date: {date: true},
|
||||
dateISO: {dateISO: true},
|
||||
number: {number: true},
|
||||
digits: {digits: true},
|
||||
creditcard: {creditcard: true}
|
||||
},
|
||||
|
||||
addClassRules: function(className, rules) {
|
||||
if ( className.constructor === String ) {
|
||||
this.classRuleSettings[className] = rules;
|
||||
} else {
|
||||
$.extend(this.classRuleSettings, className);
|
||||
}
|
||||
},
|
||||
|
||||
classRules: function(element) {
|
||||
var rules = {};
|
||||
var classes = $(element).attr('class');
|
||||
if ( classes ) {
|
||||
$.each(classes.split(' '), function() {
|
||||
if (this in $.validator.classRuleSettings) {
|
||||
$.extend(rules, $.validator.classRuleSettings[this]);
|
||||
}
|
||||
});
|
||||
}
|
||||
return rules;
|
||||
},
|
||||
|
||||
attributeRules: function(element) {
|
||||
var rules = {};
|
||||
var $element = $(element);
|
||||
|
||||
for (var method in $.validator.methods) {
|
||||
var value;
|
||||
|
||||
// support for <input required> in both html5 and older browsers
|
||||
if (method === 'required') {
|
||||
value = $element.get(0).getAttribute(method);
|
||||
// Some browsers return an empty string for the required attribute
|
||||
// and non-HTML5 browsers might have required="" markup
|
||||
if (value === "") {
|
||||
value = true;
|
||||
} else if (value === "false") {
|
||||
value = false;
|
||||
}
|
||||
// force non-HTML5 browsers to return bool
|
||||
value = !!value;
|
||||
} else {
|
||||
value = $element.attr(method);
|
||||
}
|
||||
|
||||
if (value) {
|
||||
rules[method] = value;
|
||||
} else if ($element[0].getAttribute("type") === method) {
|
||||
rules[method] = true;
|
||||
}
|
||||
}
|
||||
|
||||
// maxlength may be returned as -1, 2147483647 (IE) and 524288 (safari) for text inputs
|
||||
if (rules.maxlength && /-1|2147483647|524288/.test(rules.maxlength)) {
|
||||
delete rules.maxlength;
|
||||
}
|
||||
|
||||
return rules;
|
||||
},
|
||||
|
||||
metadataRules: function(element) {
|
||||
if (!$.metadata) {
|
||||
return {};
|
||||
}
|
||||
|
||||
var meta = $.data(element.form, 'validator').settings.meta;
|
||||
return meta ?
|
||||
$(element).metadata()[meta] :
|
||||
$(element).metadata();
|
||||
},
|
||||
|
||||
staticRules: function(element) {
|
||||
var rules = {};
|
||||
var validator = $.data(element.form, 'validator');
|
||||
if (validator.settings.rules) {
|
||||
rules = $.validator.normalizeRule(validator.settings.rules[element.name]) || {};
|
||||
}
|
||||
return rules;
|
||||
},
|
||||
|
||||
normalizeRules: function(rules, element) {
|
||||
// handle dependency check
|
||||
$.each(rules, function(prop, val) {
|
||||
// ignore rule when param is explicitly false, eg. required:false
|
||||
if (val === false) {
|
||||
delete rules[prop];
|
||||
return;
|
||||
}
|
||||
if (val.param || val.depends) {
|
||||
var keepRule = true;
|
||||
switch (typeof val.depends) {
|
||||
case "string":
|
||||
keepRule = !!$(val.depends, element.form).length;
|
||||
break;
|
||||
case "function":
|
||||
keepRule = val.depends.call(element, element);
|
||||
break;
|
||||
}
|
||||
if (keepRule) {
|
||||
rules[prop] = val.param !== undefined ? val.param : true;
|
||||
} else {
|
||||
delete rules[prop];
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// evaluate parameters
|
||||
$.each(rules, function(rule, parameter) {
|
||||
rules[rule] = $.isFunction(parameter) ? parameter(element) : parameter;
|
||||
});
|
||||
|
||||
// clean number parameters
|
||||
$.each(['minlength', 'maxlength', 'min', 'max'], function() {
|
||||
if (rules[this]) {
|
||||
rules[this] = Number(rules[this]);
|
||||
}
|
||||
});
|
||||
$.each(['rangelength', 'range'], function() {
|
||||
if (rules[this]) {
|
||||
rules[this] = [Number(rules[this][0]), Number(rules[this][1])];
|
||||
}
|
||||
});
|
||||
|
||||
if ($.validator.autoCreateRanges) {
|
||||
// auto-create ranges
|
||||
if (rules.min && rules.max) {
|
||||
rules.range = [rules.min, rules.max];
|
||||
delete rules.min;
|
||||
delete rules.max;
|
||||
}
|
||||
if (rules.minlength && rules.maxlength) {
|
||||
rules.rangelength = [rules.minlength, rules.maxlength];
|
||||
delete rules.minlength;
|
||||
delete rules.maxlength;
|
||||
}
|
||||
}
|
||||
|
||||
// To support custom messages in metadata ignore rule methods titled "messages"
|
||||
if (rules.messages) {
|
||||
delete rules.messages;
|
||||
}
|
||||
|
||||
return rules;
|
||||
},
|
||||
|
||||
// Converts a simple string to a {string: true} rule, e.g., "required" to {required:true}
|
||||
normalizeRule: function(data) {
|
||||
if( typeof data === "string" ) {
|
||||
var transformed = {};
|
||||
$.each(data.split(/\s/), function() {
|
||||
transformed[this] = true;
|
||||
});
|
||||
data = transformed;
|
||||
}
|
||||
return data;
|
||||
},
|
||||
|
||||
// http://docs.jquery.com/Plugins/Validation/Validator/addMethod
|
||||
addMethod: function(name, method, message) {
|
||||
$.validator.methods[name] = method;
|
||||
$.validator.messages[name] = message !== undefined ? message : $.validator.messages[name];
|
||||
if (method.length < 3) {
|
||||
$.validator.addClassRules(name, $.validator.normalizeRule(name));
|
||||
}
|
||||
},
|
||||
|
||||
methods: {
|
||||
|
||||
// http://docs.jquery.com/Plugins/Validation/Methods/required
|
||||
required: function(value, element, param) {
|
||||
// check if dependency is met
|
||||
if ( !this.depend(param, element) ) {
|
||||
return "dependency-mismatch";
|
||||
}
|
||||
if ( element.nodeName.toLowerCase() === "select" ) {
|
||||
// could be an array for select-multiple or a string, both are fine this way
|
||||
var val = $(element).val();
|
||||
return val && val.length > 0;
|
||||
}
|
||||
if ( this.checkable(element) ) {
|
||||
return this.getLength(value, element) > 0;
|
||||
}
|
||||
return $.trim(value).length > 0;
|
||||
},
|
||||
|
||||
// http://docs.jquery.com/Plugins/Validation/Methods/remote
|
||||
remote: function(value, element, param) {
|
||||
if ( this.optional(element) ) {
|
||||
return "dependency-mismatch";
|
||||
}
|
||||
|
||||
var previous = this.previousValue(element);
|
||||
if (!this.settings.messages[element.name] ) {
|
||||
this.settings.messages[element.name] = {};
|
||||
}
|
||||
previous.originalMessage = this.settings.messages[element.name].remote;
|
||||
this.settings.messages[element.name].remote = previous.message;
|
||||
|
||||
param = typeof param === "string" && {url:param} || param;
|
||||
|
||||
if ( this.pending[element.name] ) {
|
||||
return "pending";
|
||||
}
|
||||
if ( previous.old === value ) {
|
||||
return previous.valid;
|
||||
}
|
||||
|
||||
previous.old = value;
|
||||
var validator = this;
|
||||
this.startRequest(element);
|
||||
var data = {};
|
||||
data[element.name] = value;
|
||||
$.ajax($.extend(true, {
|
||||
url: param,
|
||||
mode: "abort",
|
||||
port: "validate" + element.name,
|
||||
dataType: "json",
|
||||
data: data,
|
||||
success: function(response) {
|
||||
validator.settings.messages[element.name].remote = previous.originalMessage;
|
||||
var valid = response === true;
|
||||
if ( valid ) {
|
||||
var submitted = validator.formSubmitted;
|
||||
validator.prepareElement(element);
|
||||
validator.formSubmitted = submitted;
|
||||
validator.successList.push(element);
|
||||
validator.showErrors();
|
||||
} else {
|
||||
var errors = {};
|
||||
var message = response || validator.defaultMessage( element, "remote" );
|
||||
errors[element.name] = previous.message = $.isFunction(message) ? message(value) : message;
|
||||
validator.showErrors(errors);
|
||||
}
|
||||
previous.valid = valid;
|
||||
validator.stopRequest(element, valid);
|
||||
}
|
||||
}, param));
|
||||
return "pending";
|
||||
},
|
||||
|
||||
// http://docs.jquery.com/Plugins/Validation/Methods/minlength
|
||||
minlength: function(value, element, param) {
|
||||
var length = $.isArray( value ) ? value.length : this.getLength($.trim(value), element);
|
||||
return this.optional(element) || length >= param;
|
||||
},
|
||||
|
||||
// http://docs.jquery.com/Plugins/Validation/Methods/maxlength
|
||||
maxlength: function(value, element, param) {
|
||||
var length = $.isArray( value ) ? value.length : this.getLength($.trim(value), element);
|
||||
return this.optional(element) || length <= param;
|
||||
},
|
||||
|
||||
// http://docs.jquery.com/Plugins/Validation/Methods/rangelength
|
||||
rangelength: function(value, element, param) {
|
||||
var length = $.isArray( value ) ? value.length : this.getLength($.trim(value), element);
|
||||
return this.optional(element) || ( length >= param[0] && length <= param[1] );
|
||||
},
|
||||
|
||||
// http://docs.jquery.com/Plugins/Validation/Methods/min
|
||||
min: function( value, element, param ) {
|
||||
return this.optional(element) || value >= param;
|
||||
},
|
||||
|
||||
// http://docs.jquery.com/Plugins/Validation/Methods/max
|
||||
max: function( value, element, param ) {
|
||||
return this.optional(element) || value <= param;
|
||||
},
|
||||
|
||||
// http://docs.jquery.com/Plugins/Validation/Methods/range
|
||||
range: function( value, element, param ) {
|
||||
return this.optional(element) || ( value >= param[0] && value <= param[1] );
|
||||
},
|
||||
|
||||
// http://docs.jquery.com/Plugins/Validation/Methods/email
|
||||
email: function(value, element) {
|
||||
// contributed by Scott Gonzalez: http://projects.scottsplayground.com/email_address_validation/
|
||||
return this.optional(element) || /^((([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+(\.([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+)*)|((\x22)((((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(([\x01-\x08\x0b\x0c\x0e-\x1f\x7f]|\x21|[\x23-\x5b]|[\x5d-\x7e]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(\\([\x01-\x09\x0b\x0c\x0d-\x7f]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]))))*(((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(\x22)))@((([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.)+(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))$/i.test(value);
|
||||
},
|
||||
|
||||
// http://docs.jquery.com/Plugins/Validation/Methods/url
|
||||
url: function(value, element) {
|
||||
// contributed by Scott Gonzalez: http://projects.scottsplayground.com/iri/
|
||||
return this.optional(element) || /^(https?|ftp):\/\/(((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:)*@)?(((\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5]))|((([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.)+(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.?)(:\d*)?)(\/((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)+(\/(([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)*)*)?)?(\?((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)|[\uE000-\uF8FF]|\/|\?)*)?(\#((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)|\/|\?)*)?$/i.test(value);
|
||||
},
|
||||
|
||||
// http://docs.jquery.com/Plugins/Validation/Methods/date
|
||||
date: function(value, element) {
|
||||
return this.optional(element) || !/Invalid|NaN/.test(new Date(value));
|
||||
},
|
||||
|
||||
// http://docs.jquery.com/Plugins/Validation/Methods/dateISO
|
||||
dateISO: function(value, element) {
|
||||
return this.optional(element) || /^\d{4}[\/\-]\d{1,2}[\/\-]\d{1,2}$/.test(value);
|
||||
},
|
||||
|
||||
// http://docs.jquery.com/Plugins/Validation/Methods/number
|
||||
number: function(value, element) {
|
||||
return this.optional(element) || /^-?(?:\d+|\d{1,3}(?:,\d{3})+)?(?:\.\d+)?$/.test(value);
|
||||
},
|
||||
|
||||
// http://docs.jquery.com/Plugins/Validation/Methods/digits
|
||||
digits: function(value, element) {
|
||||
return this.optional(element) || /^\d+$/.test(value);
|
||||
},
|
||||
|
||||
// http://docs.jquery.com/Plugins/Validation/Methods/creditcard
|
||||
// based on http://en.wikipedia.org/wiki/Luhn
|
||||
creditcard: function(value, element) {
|
||||
if ( this.optional(element) ) {
|
||||
return "dependency-mismatch";
|
||||
}
|
||||
// accept only spaces, digits and dashes
|
||||
if (/[^0-9 \-]+/.test(value)) {
|
||||
return false;
|
||||
}
|
||||
var nCheck = 0,
|
||||
nDigit = 0,
|
||||
bEven = false;
|
||||
|
||||
value = value.replace(/\D/g, "");
|
||||
|
||||
for (var n = value.length - 1; n >= 0; n--) {
|
||||
var cDigit = value.charAt(n);
|
||||
nDigit = parseInt(cDigit, 10);
|
||||
if (bEven) {
|
||||
if ((nDigit *= 2) > 9) {
|
||||
nDigit -= 9;
|
||||
}
|
||||
}
|
||||
nCheck += nDigit;
|
||||
bEven = !bEven;
|
||||
}
|
||||
|
||||
return (nCheck % 10) === 0;
|
||||
},
|
||||
|
||||
// http://docs.jquery.com/Plugins/Validation/Methods/accept
|
||||
accept: function(value, element, param) {
|
||||
param = typeof param === "string" ? param.replace(/,/g, '|') : "png|jpe?g|gif";
|
||||
return this.optional(element) || value.match(new RegExp(".(" + param + ")$", "i"));
|
||||
},
|
||||
|
||||
// http://docs.jquery.com/Plugins/Validation/Methods/equalTo
|
||||
equalTo: function(value, element, param) {
|
||||
// bind to the blur event of the target in order to revalidate whenever the target field is updated
|
||||
// TODO find a way to bind the event just once, avoiding the unbind-rebind overhead
|
||||
var target = $(param).unbind(".validate-equalTo").bind("blur.validate-equalTo", function() {
|
||||
$(element).valid();
|
||||
});
|
||||
return value === target.val();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
// deprecated, use $.validator.format instead
|
||||
$.format = $.validator.format;
|
||||
|
||||
}(jQuery));
|
||||
|
||||
// ajax mode: abort
|
||||
// usage: $.ajax({ mode: "abort"[, port: "uniqueport"]});
|
||||
// if mode:"abort" is used, the previous request on that port (port can be undefined) is aborted via XMLHttpRequest.abort()
|
||||
(function($) {
|
||||
var pendingRequests = {};
|
||||
// Use a prefilter if available (1.5+)
|
||||
if ( $.ajaxPrefilter ) {
|
||||
$.ajaxPrefilter(function(settings, _, xhr) {
|
||||
var port = settings.port;
|
||||
if (settings.mode === "abort") {
|
||||
if ( pendingRequests[port] ) {
|
||||
pendingRequests[port].abort();
|
||||
}
|
||||
pendingRequests[port] = xhr;
|
||||
}
|
||||
});
|
||||
} else {
|
||||
// Proxy ajax
|
||||
var ajax = $.ajax;
|
||||
$.ajax = function(settings) {
|
||||
var mode = ( "mode" in settings ? settings : $.ajaxSettings ).mode,
|
||||
port = ( "port" in settings ? settings : $.ajaxSettings ).port;
|
||||
if (mode === "abort") {
|
||||
if ( pendingRequests[port] ) {
|
||||
pendingRequests[port].abort();
|
||||
}
|
||||
return (pendingRequests[port] = ajax.apply(this, arguments));
|
||||
}
|
||||
return ajax.apply(this, arguments);
|
||||
};
|
||||
}
|
||||
}(jQuery));
|
||||
|
||||
// provides cross-browser focusin and focusout events
|
||||
// IE has native support, in other browsers, use event caputuring (neither bubbles)
|
||||
|
||||
// provides delegate(type: String, delegate: Selector, handler: Callback) plugin for easier event delegation
|
||||
// handler is only called when $(event.target).is(delegate), in the scope of the jquery-object for event.target
|
||||
(function($) {
|
||||
// only implement if not provided by jQuery core (since 1.4)
|
||||
// TODO verify if jQuery 1.4's implementation is compatible with older jQuery special-event APIs
|
||||
if (!jQuery.event.special.focusin && !jQuery.event.special.focusout && document.addEventListener) {
|
||||
$.each({
|
||||
focus: 'focusin',
|
||||
blur: 'focusout'
|
||||
}, function( original, fix ){
|
||||
$.event.special[fix] = {
|
||||
setup:function() {
|
||||
this.addEventListener( original, handler, true );
|
||||
},
|
||||
teardown:function() {
|
||||
this.removeEventListener( original, handler, true );
|
||||
},
|
||||
handler: function(e) {
|
||||
var args = arguments;
|
||||
args[0] = $.event.fix(e);
|
||||
args[0].type = fix;
|
||||
return $.event.handle.apply(this, args);
|
||||
}
|
||||
};
|
||||
function handler(e) {
|
||||
e = $.event.fix(e);
|
||||
e.type = fix;
|
||||
return $.event.handle.call(this, e);
|
||||
}
|
||||
});
|
||||
}
|
||||
$.extend($.fn, {
|
||||
validateDelegate: function(delegate, type, handler) {
|
||||
return this.bind(type, function(event) {
|
||||
var target = $(event.target);
|
||||
if (target.is(delegate)) {
|
||||
return handler.apply(target, arguments);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}(jQuery));
|
@ -0,0 +1,32 @@
|
||||
<!DOCTYPE html>
|
||||
<html xmlns:th="http://www.thymeleaf.org" xmlns:layout="http://www.ultraq.net.nz/web/thymeleaf/layout">
|
||||
<head>
|
||||
<title>Layout</title>
|
||||
<link rel="stylesheet"
|
||||
th:href="@{/resources/css/bootstrap.min.css}"
|
||||
href="../../resources/css/bootstrap.min.css"/>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<div class="navbar">
|
||||
<div class="navbar-inner">
|
||||
<a class="brand"
|
||||
href="https://github.com/ultraq/thymeleaf-layout-dialect">
|
||||
Thymeleaf - Layout
|
||||
</a>
|
||||
<ul class="nav">
|
||||
<li>
|
||||
<a th:href="@{/}" href="messages.html">
|
||||
Messages
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<h1 layout:fragment="header">Layout</h1>
|
||||
<div layout:fragment="content">
|
||||
Fake content
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
@ -0,0 +1,43 @@
|
||||
<!DOCTYPE html>
|
||||
<html xmlns:th="http://www.thymeleaf.org"
|
||||
xmlns:layout="http://www.ultraq.net.nz/web/thymeleaf/layout"
|
||||
layout:decorator="layout">
|
||||
<head>
|
||||
<title>Messages : Create</title>
|
||||
</head>
|
||||
<body>
|
||||
<h1 layout:fragment="header">Messages : Create</h1>
|
||||
<div layout:fragment="content"
|
||||
class="container">
|
||||
<form id="messageForm"
|
||||
th:action="@{/(form)}"
|
||||
th:object="${message}"
|
||||
action="#"
|
||||
method="post">
|
||||
<div th:if="${#fields.hasErrors('*')}"
|
||||
class="alert alert-error">
|
||||
<p th:each="error : ${#fields.errors('*')}"
|
||||
th:text="${error}">
|
||||
Validation error
|
||||
</p>
|
||||
</div>
|
||||
<div class="pull-right">
|
||||
<a th:href="@{/}" href="messages.html">
|
||||
Messages
|
||||
</a>
|
||||
</div>
|
||||
<label for="summary">Summary</label>
|
||||
<input type="text"
|
||||
th:field="*{summary}"
|
||||
th:class="${#fields.hasErrors('summary')} ? 'field-error'"/>
|
||||
<label for="text">Message</label>
|
||||
<textarea
|
||||
th:field="*{text}"
|
||||
th:class="${#fields.hasErrors('text')} ? 'field-error'"></textarea>
|
||||
<div class="form-actions">
|
||||
<input type="submit" value="Create"/>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
@ -0,0 +1,45 @@
|
||||
<!DOCTYPE html>
|
||||
<html xmlns:th="http://www.thymeleaf.org"
|
||||
xmlns:layout="http://www.ultraq.net.nz/web/thymeleaf/layout"
|
||||
layout:decorator="layout">
|
||||
<head>
|
||||
<title>Messages : View all</title>
|
||||
</head>
|
||||
<body>
|
||||
<h1 layout:fragment="header">Messages : View all</h1>
|
||||
<div layout:fragment="content" class="container">
|
||||
<div class="pull-right">
|
||||
<a href="form.html" th:href="@{/(form)}">Create Message</a>
|
||||
</div>
|
||||
<table class="table table-bordered table-striped">
|
||||
<thead>
|
||||
<tr>
|
||||
<td>ID</td>
|
||||
<td>Created</td>
|
||||
<td>Summary</td>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr th:if="${messages.empty}">
|
||||
<td colspan="3">
|
||||
No messages
|
||||
</td>
|
||||
</tr>
|
||||
<tr th:each="message : ${messages}">
|
||||
<td th:text="${message.id}">1</td>
|
||||
<td th:text="${#calendars.format(message.created)}">
|
||||
July 11, 2012 2:17:16 PM CDT
|
||||
</td>
|
||||
<td>
|
||||
<a href="view.html"
|
||||
th:href="@{'/' + ${message.id}}"
|
||||
th:text="${message.summary}">
|
||||
The summary
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
@ -0,0 +1,42 @@
|
||||
<html xmlns:th="http://www.thymeleaf.org"
|
||||
xmlns:layout="http://www.ultraq.net.nz/web/thymeleaf/layout"
|
||||
layout:decorator="layout">
|
||||
<head>
|
||||
<title>Messages : View</title>
|
||||
</head>
|
||||
<body>
|
||||
<h1 layout:fragment="header">Messages : Create</h1>
|
||||
<div layout:fragment="content"
|
||||
class="container">
|
||||
<div class="alert alert-success"
|
||||
th:if="${globalMessage}"
|
||||
th:text="${globalMessage}">
|
||||
Some Success message
|
||||
</div>
|
||||
<div class="pull-right">
|
||||
<a th:href="@{/}" href="list.html">
|
||||
Messages
|
||||
</a>
|
||||
</div>
|
||||
<dl>
|
||||
<dt>ID</dt>
|
||||
<dd id="id" th:text="${message.id}">123</dd>
|
||||
<dt>Date</dt>
|
||||
<dd id="created"
|
||||
th:text="${#calendars.format(message.created)}">
|
||||
July 11, 2012 2:17:16 PM CDT
|
||||
</dd>
|
||||
<dt>Summary</dt>
|
||||
<dd id="summary"
|
||||
th:text="${message.summary}">
|
||||
A short summary...
|
||||
</dd>
|
||||
<dt>Message</dt>
|
||||
<dd id="text"
|
||||
th:text="${message.text}">
|
||||
A detailed message that is longer than the summary.
|
||||
</dd>
|
||||
</dl>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
@ -0,0 +1,98 @@
|
||||
package org.springframework.bootstrap.sample.ui;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.URI;
|
||||
import java.util.concurrent.Callable;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.Future;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.junit.AfterClass;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Test;
|
||||
import org.springframework.bootstrap.SpringApplication;
|
||||
import org.springframework.context.ConfigurableApplicationContext;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.http.client.ClientHttpResponse;
|
||||
import org.springframework.util.LinkedMultiValueMap;
|
||||
import org.springframework.util.MultiValueMap;
|
||||
import org.springframework.web.client.DefaultResponseErrorHandler;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
/**
|
||||
* Basic integration tests for demo application.
|
||||
*
|
||||
* @author Dave Syer
|
||||
*
|
||||
*/
|
||||
public class UiBootstrapApplicationTests {
|
||||
|
||||
private static ConfigurableApplicationContext context;
|
||||
|
||||
@BeforeClass
|
||||
public static void start() throws Exception {
|
||||
Future<ConfigurableApplicationContext> future = Executors
|
||||
.newSingleThreadExecutor().submit(
|
||||
new Callable<ConfigurableApplicationContext>() {
|
||||
@Override
|
||||
public ConfigurableApplicationContext call() throws Exception {
|
||||
return (ConfigurableApplicationContext) SpringApplication
|
||||
.run(UiBootstrapApplication.class);
|
||||
}
|
||||
});
|
||||
context = future.get(30, TimeUnit.SECONDS);
|
||||
}
|
||||
|
||||
@AfterClass
|
||||
public static void stop() {
|
||||
if (context != null) {
|
||||
context.close();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHome() throws Exception {
|
||||
ResponseEntity<String> entity = getRestTemplate().getForEntity(
|
||||
"http://localhost:8080", String.class);
|
||||
assertEquals(HttpStatus.OK, entity.getStatusCode());
|
||||
assertTrue("Wrong body (title doesn't match):\n" + entity.getBody(), entity
|
||||
.getBody().contains("<title>Messages"));
|
||||
assertFalse("Wrong body (found layout:fragment):\n" + entity.getBody(), entity
|
||||
.getBody().contains("layout:fragment"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCreate() throws Exception {
|
||||
MultiValueMap<String, String> map = new LinkedMultiValueMap<String, String>();
|
||||
map.set("text", "FOO text");
|
||||
map.set("summary", "FOO");
|
||||
URI location = getRestTemplate().postForLocation("http://localhost:8080", map);
|
||||
assertTrue("Wrong location:\n" + location,
|
||||
location.toString().contains("localhost:8080"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCss() throws Exception {
|
||||
ResponseEntity<String> entity = getRestTemplate().getForEntity(
|
||||
"http://localhost:8080/css/bootstrap.min.css", String.class);
|
||||
assertEquals(HttpStatus.OK, entity.getStatusCode());
|
||||
assertTrue("Wrong body:\n" + entity.getBody(), entity.getBody().contains("body"));
|
||||
}
|
||||
|
||||
private RestTemplate getRestTemplate() {
|
||||
RestTemplate restTemplate = new RestTemplate();
|
||||
restTemplate.setErrorHandler(new DefaultResponseErrorHandler() {
|
||||
@Override
|
||||
public void handleError(ClientHttpResponse response) throws IOException {
|
||||
}
|
||||
});
|
||||
return restTemplate;
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,7 @@
|
||||
handlers = java.util.logging.ConsoleHandler
|
||||
.level = INFO
|
||||
|
||||
java.util.logging.ConsoleHandler.level = FINE
|
||||
sun.net.www.protocol.http.HttpURLConnection.level = ALL
|
||||
org.springframework.bootstrap.context.annotation.level = ALL
|
||||
org.thymeleaf.level = ALL
|
@ -1,5 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
<groupId>org.springframework.bootstrap</groupId>
|
||||
@ -105,6 +106,16 @@
|
||||
<version>1.6</version>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.thymeleaf</groupId>
|
||||
<artifactId>thymeleaf-spring3</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>nz.net.ultraq.web.thymeleaf</groupId>
|
||||
<artifactId>thymeleaf-layout-dialect</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<!-- Test -->
|
||||
<dependency>
|
||||
<groupId>org.hibernate</groupId>
|
||||
|
@ -0,0 +1,46 @@
|
||||
/*
|
||||
* Copyright 2012-2013 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.springframework.bootstrap.autoconfigure;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.bootstrap.context.annotation.ConditionalOnMissingBean;
|
||||
import org.springframework.bootstrap.context.annotation.EnableAutoConfiguration;
|
||||
import org.springframework.context.MessageSource;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.support.ResourceBundleMessageSource;
|
||||
|
||||
/**
|
||||
* {@link EnableAutoConfiguration Auto-configuration} for {@link MessageSource}.
|
||||
*
|
||||
* @author Dave Syer
|
||||
*/
|
||||
@Configuration
|
||||
@ConditionalOnMissingBean(MessageSource.class)
|
||||
public class MessageSourceAutoConfiguration {
|
||||
|
||||
@Value("${spring.messages.basename:messages}")
|
||||
private String basename;
|
||||
|
||||
@Bean
|
||||
public MessageSource messageSource() {
|
||||
ResourceBundleMessageSource messageSource = new ResourceBundleMessageSource();
|
||||
messageSource.setBasename(this.basename);
|
||||
return messageSource;
|
||||
}
|
||||
|
||||
}
|
@ -36,4 +36,5 @@ import org.springframework.data.jpa.repository.support.JpaRepositoryFactoryBean;
|
||||
@ConditionalOnMissingBean(JpaRepositoryFactoryBean.class)
|
||||
@Import(JpaRepositoriesAutoConfigureRegistrar.class)
|
||||
public class JpaRepositoriesAutoConfiguration {
|
||||
|
||||
}
|
||||
|
@ -19,6 +19,7 @@ package org.springframework.bootstrap.autoconfigure.orm.jpa;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.hibernate.ejb.HibernateEntityManager;
|
||||
import org.springframework.bootstrap.autoconfigure.jdbc.EmbeddedDatabaseAutoConfiguration;
|
||||
import org.springframework.bootstrap.context.annotation.ConditionalOnClass;
|
||||
import org.springframework.bootstrap.context.annotation.EnableAutoConfiguration;
|
||||
@ -36,7 +37,7 @@ import org.springframework.transaction.annotation.EnableTransactionManagement;
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
@Configuration
|
||||
@ConditionalOnClass(name = "org.hibernate.ejb.HibernateEntityManager")
|
||||
@ConditionalOnClass(HibernateEntityManager.class)
|
||||
@EnableTransactionManagement
|
||||
public class HibernateJpaAutoConfiguration extends JpaAutoConfiguration {
|
||||
|
||||
|
@ -53,7 +53,7 @@ public abstract class JpaAutoConfiguration implements BeanFactoryAware {
|
||||
private ConfigurableListableBeanFactory beanFactory;
|
||||
|
||||
@Bean
|
||||
public PlatformTransactionManager txManager() {
|
||||
public PlatformTransactionManager transactionManager() {
|
||||
return new JpaTransactionManager(entityManagerFactory().getObject());
|
||||
}
|
||||
|
||||
|
@ -0,0 +1,155 @@
|
||||
/*
|
||||
* Copyright 2012-2013 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.springframework.bootstrap.autoconfigure.thymeleaf;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
|
||||
import javax.servlet.Servlet;
|
||||
|
||||
import nz.net.ultraq.web.thymeleaf.LayoutDialect;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.bootstrap.context.annotation.ConditionalOnClass;
|
||||
import org.springframework.bootstrap.context.annotation.ConditionalOnMissingBean;
|
||||
import org.springframework.bootstrap.context.annotation.ConditionalOnMissingClass;
|
||||
import org.springframework.bootstrap.context.annotation.EnableAutoConfiguration;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.core.io.DefaultResourceLoader;
|
||||
import org.springframework.core.io.ResourceLoader;
|
||||
import org.thymeleaf.TemplateProcessingParameters;
|
||||
import org.thymeleaf.resourceresolver.IResourceResolver;
|
||||
import org.thymeleaf.spring3.SpringTemplateEngine;
|
||||
import org.thymeleaf.spring3.view.ThymeleafViewResolver;
|
||||
import org.thymeleaf.templateresolver.ITemplateResolver;
|
||||
import org.thymeleaf.templateresolver.TemplateResolver;
|
||||
|
||||
/**
|
||||
* {@link EnableAutoConfiguration Auto-configuration} for Thymeleaf templating.
|
||||
*
|
||||
* @author Dave Syer
|
||||
*/
|
||||
@Configuration
|
||||
@ConditionalOnClass(SpringTemplateEngine.class)
|
||||
public class ThymeleafAutoConfiguration {
|
||||
|
||||
@Configuration
|
||||
@ConditionalOnMissingBean(name = "defaultTemplateResolver")
|
||||
protected static class DefaultTemplateResolverConfiguration {
|
||||
|
||||
@Autowired
|
||||
private ResourceLoader resourceLoader = new DefaultResourceLoader();
|
||||
|
||||
@Value("${spring.template.prefix:classpath:/templates/}")
|
||||
private String prefix = "classpath:/templates/";
|
||||
|
||||
@Value("${spring.template.suffix:.html}")
|
||||
private String suffix = ".html";
|
||||
|
||||
@Value("${spring.template.mode:HTML5}")
|
||||
private String templateMode = "HTML5";
|
||||
|
||||
@Bean
|
||||
public ITemplateResolver defaultTemplateResolver() {
|
||||
TemplateResolver resolver = new TemplateResolver();
|
||||
resolver.setResourceResolver(new IResourceResolver() {
|
||||
@Override
|
||||
public InputStream getResourceAsStream(
|
||||
TemplateProcessingParameters templateProcessingParameters,
|
||||
String resourceName) {
|
||||
try {
|
||||
return DefaultTemplateResolverConfiguration.this.resourceLoader
|
||||
.getResource(resourceName).getInputStream();
|
||||
} catch (IOException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "SPRING";
|
||||
}
|
||||
});
|
||||
resolver.setPrefix(this.prefix);
|
||||
resolver.setSuffix(this.suffix);
|
||||
resolver.setTemplateMode(this.templateMode);
|
||||
return resolver;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@ConditionalOnMissingClass("nz.net.ultraq.web.thymeleaf.LayoutDialect")
|
||||
@ConditionalOnMissingBean(SpringTemplateEngine.class)
|
||||
protected static class ThymeleafDefaultConfiguration {
|
||||
|
||||
@Autowired
|
||||
private Collection<ITemplateResolver> templateResolvers = Collections.emptySet();
|
||||
|
||||
@Bean
|
||||
public SpringTemplateEngine templateEngine() {
|
||||
SpringTemplateEngine engine = new SpringTemplateEngine();
|
||||
for (ITemplateResolver templateResolver : this.templateResolvers) {
|
||||
engine.addTemplateResolver(templateResolver);
|
||||
}
|
||||
return engine;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@ConditionalOnClass({ LayoutDialect.class })
|
||||
@ConditionalOnMissingBean(SpringTemplateEngine.class)
|
||||
protected static class ThymeleafWebLayoutConfiguration {
|
||||
|
||||
@Autowired
|
||||
private Collection<ITemplateResolver> templateResolvers = Collections.emptySet();
|
||||
|
||||
@Bean
|
||||
public SpringTemplateEngine templateEngine() {
|
||||
SpringTemplateEngine engine = new SpringTemplateEngine();
|
||||
for (ITemplateResolver templateResolver : this.templateResolvers) {
|
||||
engine.addTemplateResolver(templateResolver);
|
||||
}
|
||||
engine.addDialect(new LayoutDialect());
|
||||
return engine;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@ConditionalOnClass({ Servlet.class })
|
||||
protected static class ThymeleafViewResolverConfiguration {
|
||||
|
||||
@Autowired
|
||||
private SpringTemplateEngine templateEngine;
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean(name = "thymeleafViewResolver")
|
||||
public ThymeleafViewResolver thymeleafViewResolver() {
|
||||
ThymeleafViewResolver resolver = new ThymeleafViewResolver();
|
||||
resolver.setTemplateEngine(this.templateEngine);
|
||||
return resolver;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -18,6 +18,8 @@ package org.springframework.bootstrap.autoconfigure.web;
|
||||
|
||||
import javax.servlet.Servlet;
|
||||
|
||||
import org.springframework.beans.factory.ListableBeanFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.bootstrap.autoconfigure.web.WebMvcAutoConfiguration.WebMvcConfiguration;
|
||||
import org.springframework.bootstrap.context.annotation.ConditionalOnClass;
|
||||
import org.springframework.bootstrap.context.annotation.ConditionalOnMissingBean;
|
||||
@ -25,11 +27,16 @@ import org.springframework.bootstrap.context.annotation.EnableAutoConfiguration;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.core.convert.converter.Converter;
|
||||
import org.springframework.core.convert.converter.GenericConverter;
|
||||
import org.springframework.format.Formatter;
|
||||
import org.springframework.format.FormatterRegistry;
|
||||
import org.springframework.web.servlet.DispatcherServlet;
|
||||
import org.springframework.web.servlet.HandlerAdapter;
|
||||
import org.springframework.web.servlet.HandlerMapping;
|
||||
import org.springframework.web.servlet.config.annotation.DefaultServletHandlerConfigurer;
|
||||
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
|
||||
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
|
||||
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
|
||||
|
||||
/**
|
||||
@ -51,6 +58,9 @@ public class WebMvcAutoConfiguration {
|
||||
@EnableWebMvc
|
||||
public static class WebMvcConfiguration extends WebMvcConfigurerAdapter {
|
||||
|
||||
@Autowired
|
||||
private ListableBeanFactory beanFactory;
|
||||
|
||||
@Bean
|
||||
public DispatcherServlet dispatcherServlet() {
|
||||
return new DispatcherServlet();
|
||||
@ -62,6 +72,29 @@ public class WebMvcAutoConfiguration {
|
||||
configurer.enable();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addFormatters(FormatterRegistry registry) {
|
||||
for (Converter<?, ?> converter : this.beanFactory.getBeansOfType(
|
||||
Converter.class).values()) {
|
||||
registry.addConverter(converter);
|
||||
}
|
||||
for (GenericConverter converter : this.beanFactory.getBeansOfType(
|
||||
GenericConverter.class).values()) {
|
||||
registry.addConverter(converter);
|
||||
}
|
||||
for (Formatter<?> formatter : this.beanFactory
|
||||
.getBeansOfType(Formatter.class).values()) {
|
||||
registry.addFormatter(formatter);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addResourceHandlers(ResourceHandlerRegistry registry) {
|
||||
registry.addResourceHandler("/**").addResourceLocations("/")
|
||||
.addResourceLocations("classpath:/static")
|
||||
.addResourceLocations("classpath:/");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -36,7 +36,7 @@ import org.springframework.util.MultiValueMap;
|
||||
*/
|
||||
abstract class AbstractOnBeanCondition implements Condition {
|
||||
|
||||
private static Log logger = LogFactory.getLog(OnBeanCondition.class);
|
||||
protected Log logger = LogFactory.getLog(getClass());
|
||||
|
||||
protected abstract Class<?> annotationClass();
|
||||
|
||||
@ -53,8 +53,8 @@ abstract class AbstractOnBeanCondition implements Condition {
|
||||
List<String> beanClassesFound = new ArrayList<String>();
|
||||
List<String> beanNamesFound = new ArrayList<String>();
|
||||
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Looking for beans with class: " + beanClasses);
|
||||
if (this.logger.isDebugEnabled()) {
|
||||
this.logger.debug("Looking for beans with class: " + beanClasses);
|
||||
}
|
||||
for (String beanClass : beanClasses) {
|
||||
try {
|
||||
@ -72,8 +72,8 @@ abstract class AbstractOnBeanCondition implements Condition {
|
||||
}
|
||||
}
|
||||
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Looking for beans with names: " + beanNames);
|
||||
if (this.logger.isDebugEnabled()) {
|
||||
this.logger.debug("Looking for beans with names: " + beanNames);
|
||||
}
|
||||
for (String beanName : beanNames) {
|
||||
if (context.getBeanFactory().containsBeanDefinition(beanName)) {
|
||||
@ -82,8 +82,8 @@ abstract class AbstractOnBeanCondition implements Condition {
|
||||
}
|
||||
|
||||
boolean result = evaluate(beanClassesFound, beanNamesFound);
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Finished matching and result is matches" + result);
|
||||
if (this.logger.isDebugEnabled()) {
|
||||
this.logger.debug("Finished matching and result is matches: " + result);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
@ -0,0 +1,43 @@
|
||||
/*
|
||||
* Copyright 2012-2013 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.springframework.bootstrap.context.annotation;
|
||||
|
||||
import java.lang.annotation.Documented;
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
import org.springframework.context.annotation.Conditional;
|
||||
|
||||
/**
|
||||
* {@link Conditional} that only matches when the specified classes are on the classpath.
|
||||
*
|
||||
* @author Dave Syer
|
||||
*/
|
||||
@Target({ ElementType.TYPE, ElementType.METHOD })
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Documented
|
||||
@Conditional(OnMissingClassCondition.class)
|
||||
public @interface ConditionalOnMissingClass {
|
||||
|
||||
/**
|
||||
* The classes names that must be absent.
|
||||
* @return the class names that must be absent.
|
||||
*/
|
||||
public String[] value() default {};
|
||||
}
|
@ -0,0 +1,38 @@
|
||||
/*
|
||||
* Copyright 2012-2013 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.springframework.bootstrap.context.annotation;
|
||||
|
||||
import java.lang.annotation.Documented;
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
import org.springframework.context.annotation.Conditional;
|
||||
|
||||
/**
|
||||
* {@link Conditional} that only matches when the application context is a not a web
|
||||
* application context.
|
||||
*
|
||||
* @author Dave Syer
|
||||
*/
|
||||
@Target({ ElementType.TYPE, ElementType.METHOD })
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Documented
|
||||
@Conditional(OnNotWebApplicationCondition.class)
|
||||
public @interface ConditionalOnNotWebApplication {
|
||||
}
|
@ -0,0 +1,38 @@
|
||||
/*
|
||||
* Copyright 2012-2013 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.springframework.bootstrap.context.annotation;
|
||||
|
||||
import java.lang.annotation.Documented;
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
import org.springframework.context.annotation.Conditional;
|
||||
|
||||
/**
|
||||
* {@link Conditional} that only matches when the application context is a web application
|
||||
* context.
|
||||
*
|
||||
* @author Dave Syer
|
||||
*/
|
||||
@Target({ ElementType.TYPE, ElementType.METHOD })
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Documented
|
||||
@Conditional(OnWebApplicationCondition.class)
|
||||
public @interface ConditionalOnWebApplication {
|
||||
}
|
@ -61,6 +61,9 @@ class OnClassCondition implements Condition {
|
||||
}
|
||||
}
|
||||
}
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Classes not found (search terminated with matches=true)");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -0,0 +1,75 @@
|
||||
/*
|
||||
* Copyright 2012-2013 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.springframework.bootstrap.context.annotation;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.context.annotation.Condition;
|
||||
import org.springframework.context.annotation.ConditionContext;
|
||||
import org.springframework.core.type.AnnotatedTypeMetadata;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ClassUtils;
|
||||
import org.springframework.util.MultiValueMap;
|
||||
|
||||
/**
|
||||
* {@link Condition} that checks for the specific classes.
|
||||
*
|
||||
* @author Dave Syer
|
||||
* @see ConditionalOnMissingClass
|
||||
*/
|
||||
class OnMissingClassCondition implements Condition {
|
||||
|
||||
private static Log logger = LogFactory.getLog(OnMissingClassCondition.class);
|
||||
|
||||
@Override
|
||||
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
|
||||
MultiValueMap<String, Object> attributes = metadata.getAllAnnotationAttributes(
|
||||
ConditionalOnMissingClass.class.getName(), true);
|
||||
if (attributes != null) {
|
||||
List<String> classNames = new ArrayList<String>();
|
||||
collectClassNames(classNames, attributes.get("value"));
|
||||
Assert.isTrue(classNames.size() > 0,
|
||||
"@ConditionalOnMissingClass annotations must specify at least one class value");
|
||||
for (String className : classNames) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Checking for class: " + className);
|
||||
}
|
||||
if (ClassUtils.isPresent(className, context.getClassLoader())) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Found class: " + className
|
||||
+ " (search terminated with matches=false)");
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private void collectClassNames(List<String> classNames, List<Object> values) {
|
||||
for (Object value : values) {
|
||||
for (Object valueItem : (Object[]) value) {
|
||||
classNames.add(valueItem instanceof Class<?> ? ((Class<?>) valueItem)
|
||||
.getName() : valueItem.toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,56 @@
|
||||
/*
|
||||
* Copyright 2012-2013 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.springframework.bootstrap.context.annotation;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.context.annotation.Condition;
|
||||
import org.springframework.context.annotation.ConditionContext;
|
||||
import org.springframework.core.type.AnnotatedTypeMetadata;
|
||||
import org.springframework.util.ClassUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* {@link Condition} that checks for a web application context and returns false if one is
|
||||
* found.
|
||||
*
|
||||
* @author Dave Syer
|
||||
* @see ConditionalOnNotWebApplication
|
||||
*/
|
||||
class OnNotWebApplicationCondition implements Condition {
|
||||
|
||||
private static Log logger = LogFactory.getLog(OnNotWebApplicationCondition.class);
|
||||
|
||||
@Override
|
||||
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
|
||||
if (!ClassUtils.isPresent(
|
||||
"org.springframework.web.context.support.GenericWebApplicationContext",
|
||||
null)) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Web application classes not found");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
boolean result = !StringUtils.arrayToCommaDelimitedString(
|
||||
context.getBeanFactory().getRegisteredScopeNames()).contains("session");
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Web application context found: " + !result);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,57 @@
|
||||
/*
|
||||
* Copyright 2012-2013 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.springframework.bootstrap.context.annotation;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.context.annotation.Condition;
|
||||
import org.springframework.context.annotation.ConditionContext;
|
||||
import org.springframework.core.type.AnnotatedTypeMetadata;
|
||||
import org.springframework.util.ClassUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.web.context.support.StandardServletEnvironment;
|
||||
|
||||
/**
|
||||
* {@link Condition} that checks for a web application context.
|
||||
*
|
||||
* @author Dave Syer
|
||||
* @see ConditionalOnWebApplication
|
||||
*/
|
||||
class OnWebApplicationCondition implements Condition {
|
||||
|
||||
private static Log logger = LogFactory.getLog(OnWebApplicationCondition.class);
|
||||
|
||||
@Override
|
||||
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
|
||||
if (!ClassUtils.isPresent(
|
||||
"org.springframework.web.context.support.GenericWebApplicationContext",
|
||||
null)) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Web application classes not found");
|
||||
}
|
||||
return false;
|
||||
}
|
||||
boolean result = StringUtils.arrayToCommaDelimitedString(
|
||||
context.getBeanFactory().getRegisteredScopeNames()).contains("session")
|
||||
|| context.getEnvironment() instanceof StandardServletEnvironment;
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Web application context found: " + result);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
@ -320,7 +320,7 @@ public abstract class AbstractEmbeddedServletContainerFactory implements
|
||||
*/
|
||||
protected final File getValidDocumentRoot() {
|
||||
File[] roots = new File[] { getDocumentRoot(), new File("src/main/webapp"),
|
||||
new File("public") };
|
||||
new File("public"), new File("static") };
|
||||
for (File root : roots) {
|
||||
if (root != null && root.exists() && root.isDirectory()) {
|
||||
return root.getAbsoluteFile();
|
||||
|
@ -14,12 +14,14 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.bootstrap.logging;
|
||||
package org.springframework.bootstrap.context.initializer;
|
||||
|
||||
import java.lang.management.ManagementFactory;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.bootstrap.logging.JavaLoggerConfigurer;
|
||||
import org.springframework.bootstrap.logging.LogbackConfigurer;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.ApplicationContextInitializer;
|
||||
import org.springframework.context.ConfigurableApplicationContext;
|
||||
@ -198,7 +200,7 @@ public class LoggingInitializer implements
|
||||
}
|
||||
|
||||
// Fallback to the default
|
||||
String defaultPath = ClassUtils.getPackageName(LoggingInitializer.class);
|
||||
String defaultPath = ClassUtils.getPackageName(JavaLoggerConfigurer.class);
|
||||
defaultPath = defaultPath.replace(".", "/");
|
||||
defaultPath = defaultPath + "/" + this.paths[this.paths.length - 1];
|
||||
return "classpath:" + defaultPath;
|
@ -1,13 +1,16 @@
|
||||
org.springframework.bootstrap.context.annotation.EnableAutoConfiguration=\
|
||||
org.springframework.bootstrap.autoconfigure.PropertyPlaceholderAutoConfiguration,\
|
||||
org.springframework.bootstrap.autoconfigure.MessageSourceAutoConfiguration,\
|
||||
org.springframework.bootstrap.autoconfigure.data.JpaRepositoriesAutoConfiguration,\
|
||||
org.springframework.bootstrap.autoconfigure.jdbc.EmbeddedDatabaseAutoConfiguration,\
|
||||
org.springframework.bootstrap.autoconfigure.batch.BatchAutoConfiguration,\
|
||||
org.springframework.bootstrap.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration,\
|
||||
org.springframework.bootstrap.autoconfigure.thymeleaf.ThymeleafAutoConfiguration,\
|
||||
org.springframework.bootstrap.autoconfigure.web.EmbeddedContainerCustomizerConfiguration,\
|
||||
org.springframework.bootstrap.autoconfigure.web.EmbeddedJettyAutoConfiguration,\
|
||||
org.springframework.bootstrap.autoconfigure.web.EmbeddedTomcatAutoConfiguration,\
|
||||
org.springframework.bootstrap.autoconfigure.web.WebMvcAutoConfiguration
|
||||
org.springframework.context.ApplicationContextInitializer=\
|
||||
org.springframework.bootstrap.context.initializer.ConfigFileApplicationContextInitializer,\
|
||||
org.springframework.bootstrap.context.initializer.LoggingInitializer,\
|
||||
org.springframework.bootstrap.context.initializer.EnvironmentDelegateApplicationContextInitializer
|
@ -290,7 +290,7 @@ public class SpringApplicationTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void exitWithExplicitCOde() throws Exception {
|
||||
public void exitWithExplicitCode() throws Exception {
|
||||
SpringApplication application = new SpringApplication(ExampleConfig.class);
|
||||
application.setWebEnvironment(false);
|
||||
ApplicationContext context = application.run();
|
||||
|
@ -0,0 +1,60 @@
|
||||
/*
|
||||
* Copyright 2012-2013 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.springframework.bootstrap.autoconfigure;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
|
||||
import org.springframework.core.env.MapPropertySource;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
/**
|
||||
* @author Dave Syer
|
||||
*
|
||||
*/
|
||||
public class MessageSourceAutoConfigurationTests {
|
||||
|
||||
private AnnotationConfigApplicationContext context;
|
||||
|
||||
@Test
|
||||
public void testDefaultMessageSource() throws Exception {
|
||||
this.context = new AnnotationConfigApplicationContext();
|
||||
this.context.register(MessageSourceAutoConfiguration.class,
|
||||
PropertyPlaceholderAutoConfiguration.class);
|
||||
this.context.refresh();
|
||||
assertEquals("Foo message",
|
||||
this.context.getMessage("foo", null, "Foo message", Locale.UK));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMessageSourceCreated() throws Exception {
|
||||
this.context = new AnnotationConfigApplicationContext();
|
||||
this.context.register(MessageSourceAutoConfiguration.class,
|
||||
PropertyPlaceholderAutoConfiguration.class);
|
||||
Map<String, Object> map = new HashMap<String, Object>();
|
||||
map.put("spring.messages.basename", "test/messages");
|
||||
this.context.getEnvironment().getPropertySources()
|
||||
.addFirst(new MapPropertySource("test", map));
|
||||
this.context.refresh();
|
||||
assertEquals("bar",
|
||||
this.context.getMessage("foo", null, "Foo message", Locale.UK));
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,84 @@
|
||||
/*
|
||||
* Copyright 2012-2013 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.springframework.bootstrap.autoconfigure.thymeleaf;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.springframework.bootstrap.autoconfigure.PropertyPlaceholderAutoConfiguration;
|
||||
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
|
||||
import org.springframework.core.env.MapPropertySource;
|
||||
import org.springframework.mock.web.MockHttpServletRequest;
|
||||
import org.springframework.mock.web.MockHttpServletResponse;
|
||||
import org.springframework.mock.web.MockServletContext;
|
||||
import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;
|
||||
import org.springframework.web.servlet.support.RequestContext;
|
||||
import org.thymeleaf.TemplateEngine;
|
||||
import org.thymeleaf.context.Context;
|
||||
import org.thymeleaf.spring3.view.ThymeleafView;
|
||||
import org.thymeleaf.spring3.view.ThymeleafViewResolver;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
/**
|
||||
* @author Dave Syer
|
||||
*/
|
||||
public class ThymeleafAutoConfigurationTests {
|
||||
|
||||
@Test
|
||||
public void createFromConfigClass() throws Exception {
|
||||
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
|
||||
context.register(ThymeleafAutoConfiguration.class,
|
||||
PropertyPlaceholderAutoConfiguration.class);
|
||||
Map<String, Object> map = new HashMap<String, Object>();
|
||||
map.put("spring.template.mode", "XHTML");
|
||||
map.put("spring.template.suffix", "");
|
||||
context.getEnvironment().getPropertySources()
|
||||
.addFirst(new MapPropertySource("test", map));
|
||||
context.refresh();
|
||||
TemplateEngine engine = context.getBean(TemplateEngine.class);
|
||||
Context attrs = new Context(Locale.UK, Collections.singletonMap("foo", "bar"));
|
||||
String result = engine.process("template.txt", attrs);
|
||||
assertEquals("<html>bar</html>", result);
|
||||
context.close();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createLayoutFromConfigClass() throws Exception {
|
||||
AnnotationConfigWebApplicationContext context = new AnnotationConfigWebApplicationContext();
|
||||
context.register(ThymeleafAutoConfiguration.class,
|
||||
PropertyPlaceholderAutoConfiguration.class);
|
||||
MockServletContext servletContext = new MockServletContext();
|
||||
context.setServletContext(servletContext);
|
||||
context.refresh();
|
||||
ThymeleafView view = (ThymeleafView) context.getBean(ThymeleafViewResolver.class)
|
||||
.resolveViewName("view", Locale.UK);
|
||||
MockHttpServletResponse response = new MockHttpServletResponse();
|
||||
MockHttpServletRequest request = new MockHttpServletRequest();
|
||||
request.setAttribute(RequestContext.WEB_APPLICATION_CONTEXT_ATTRIBUTE, context);
|
||||
view.render(Collections.singletonMap("foo", "bar"), request, response);
|
||||
String result = response.getContentAsString();
|
||||
assertTrue("Wrong result: " + result, result.contains("<title>Content</title>"));
|
||||
assertTrue("Wrong result: " + result, result.contains("<span>bar</span>"));
|
||||
context.close();
|
||||
}
|
||||
|
||||
}
|
@ -23,6 +23,7 @@ import org.apache.commons.logging.LogFactory;
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.springframework.bootstrap.context.initializer.LoggingInitializer;
|
||||
import org.springframework.context.support.GenericApplicationContext;
|
||||
import org.springframework.core.env.PropertySource;
|
||||
|
||||
|
@ -7,7 +7,8 @@
|
||||
<pattern>${LOG_PATTERN}</pattern>
|
||||
</encoder>
|
||||
</appender>
|
||||
<logger name="org.springframework.beans.factory.annotation" level="TRACE" />
|
||||
<logger name="org.springframework.bootstrap.context.annotation" level="TRACE" />
|
||||
<logger name="org.thymeleaf" level="TRACE" />
|
||||
<root level="INFO">
|
||||
<appender-ref ref="CONSOLE" />
|
||||
</root>
|
||||
|
1
spring-bootstrap/src/test/resources/static/css/style.css
Normal file
1
spring-bootstrap/src/test/resources/static/css/style.css
Normal file
@ -0,0 +1 @@
|
||||
body: {background: red}
|
14
spring-bootstrap/src/test/resources/templates/layout.html
Normal file
14
spring-bootstrap/src/test/resources/templates/layout.html
Normal file
@ -0,0 +1,14 @@
|
||||
<!DOCTYPE html>
|
||||
<html xmlns:th="http://www.thymeleaf.org" xmlns:layout="http://www.ultraq.net.nz/web/thymeleaf/layout">
|
||||
<head>
|
||||
<title layout:fragment="title">Layout</title>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<h1 layout:fragment="title">Layout</h1>
|
||||
<div layout:fragment="content">
|
||||
Fake content
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
@ -0,0 +1 @@
|
||||
<html th:text="${foo}">foo</html>
|
13
spring-bootstrap/src/test/resources/templates/tiles.xml
Normal file
13
spring-bootstrap/src/test/resources/templates/tiles.xml
Normal file
@ -0,0 +1,13 @@
|
||||
<?xml version="1.0" encoding="ISO-8859-1" ?>
|
||||
<!DOCTYPE tiles-definitions PUBLIC
|
||||
"-//Apache Software Foundation//DTD Tiles Configuration 2.1//EN"
|
||||
"http://tiles.apache.org/dtds/tiles-config_2_1.dtd">
|
||||
<tiles-definitions>
|
||||
<definition name="*" template="layout">
|
||||
<put-attribute name="content" value="content/{1}" />
|
||||
<put-attribute name="title" value="title/{1}" />
|
||||
</definition>
|
||||
|
||||
<definition name="content/*" template="{1} :: content" />
|
||||
<definition name="title/*" template="{1} :: title" />
|
||||
</tiles-definitions>
|
10
spring-bootstrap/src/test/resources/templates/view.html
Normal file
10
spring-bootstrap/src/test/resources/templates/view.html
Normal file
@ -0,0 +1,10 @@
|
||||
<html xmlns:th="http://www.thymeleaf.org" xmlns:layout="http://www.ultraq.net.nz/web/thymeleaf/layout" layout:decorator="layout">
|
||||
<head>
|
||||
<title layout:fragment="title">Content</title>
|
||||
</head>
|
||||
<body>
|
||||
<div layout:fragment="content">
|
||||
<span th:text="${foo}">foo</span>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
@ -0,0 +1 @@
|
||||
foo=bar
|
Loading…
Reference in New Issue
Block a user