├── .gitignore ├── SpringSecurityApp.iml ├── pom.xml └── src └── main ├── java └── net │ └── proselyte │ └── springsecurityapp │ ├── controller │ └── UserController.java │ ├── dao │ ├── RoleDao.java │ └── UserDao.java │ ├── model │ ├── Role.java │ └── User.java │ ├── service │ ├── SecurityService.java │ ├── SecurityServiceImpl.java │ ├── UserDetailsServiceImpl.java │ ├── UserService.java │ └── UserServiceImpl.java │ └── validator │ └── UserValidator.java ├── resources ├── database.properties ├── database.sql ├── logback.xml └── validation.properties └── webapp ├── WEB-INF ├── appconfig-data.xml ├── appconfig-mvc.xml ├── appconfig-root.xml ├── appconfig-security.xml ├── views │ ├── admin.jsp │ ├── login.jsp │ ├── registration.jsp │ └── welcome.jsp └── web.xml └── resources ├── css ├── bootstrap.min.css └── common.css └── js └── bootstrap.min.js /.gitignore: -------------------------------------------------------------------------------- 1 | .idea/* 2 | *.class 3 | target/* 4 | 5 | /.war 6 | /.ear 7 | /.jar -------------------------------------------------------------------------------- /SpringSecurityApp.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 3 | 4.0.0 4 | net.proselyte 5 | SpringSecurityApp 6 | war 7 | 1.0-SNAPSHOT 8 | SpringSecurityApp Maven Webapp 9 | http://maven.apache.org 10 | 11 | 12 | 4.2.0.RELEASE 13 | 4.0.2.RELEASE 14 | 1.8.2.RELEASE 15 | 4.3.11.Final 16 | 5.2.1.Final 17 | 5.1.36 18 | 1.4 19 | 1.2 20 | 3.8.1 21 | 1.1.3 22 | 1.8 23 | 1.8 24 | 25 | 26 | 27 | 28 | org.springframework 29 | spring-web 30 | ${spring.version} 31 | 32 | 33 | 34 | org.springframework 35 | spring-webmvc 36 | ${spring.version} 37 | 38 | 39 | 40 | org.springframework.security 41 | spring-security-web 42 | ${spring-security.version} 43 | 44 | 45 | 46 | org.springframework.security 47 | spring-security-config 48 | ${spring-security.version} 49 | 50 | 51 | 52 | org.hibernate 53 | hibernate-validator 54 | ${hibernate-validator.version} 55 | 56 | 57 | 58 | org.springframework.data 59 | spring-data-jpa 60 | ${spring-data-jpa.version} 61 | 62 | 63 | 64 | org.hibernate 65 | hibernate-entitymanager 66 | ${hibernate.version} 67 | 68 | 69 | 70 | mysql 71 | mysql-connector-java 72 | ${mysql-connector.version} 73 | 74 | 75 | 76 | commons-dbcp 77 | commons-dbcp 78 | ${commons-dbcp.version} 79 | 80 | 81 | 82 | javax.servlet 83 | jstl 84 | ${jstl.version} 85 | 86 | 87 | 88 | junit 89 | junit 90 | ${junit.version} 91 | test 92 | 93 | 94 | 95 | ch.qos.logback 96 | logback-classic 97 | ${logback.version} 98 | 99 | 100 | 101 | SpringSecurityApp 102 | 103 | 104 | -------------------------------------------------------------------------------- /src/main/java/net/proselyte/springsecurityapp/controller/UserController.java: -------------------------------------------------------------------------------- 1 | package net.proselyte.springsecurityapp.controller; 2 | 3 | import net.proselyte.springsecurityapp.model.User; 4 | import net.proselyte.springsecurityapp.service.SecurityService; 5 | import net.proselyte.springsecurityapp.service.UserService; 6 | import net.proselyte.springsecurityapp.validator.UserValidator; 7 | import org.springframework.beans.factory.annotation.Autowired; 8 | import org.springframework.stereotype.Controller; 9 | import org.springframework.ui.Model; 10 | import org.springframework.validation.BindingResult; 11 | import org.springframework.web.bind.annotation.ModelAttribute; 12 | import org.springframework.web.bind.annotation.RequestMapping; 13 | import org.springframework.web.bind.annotation.RequestMethod; 14 | 15 | /** 16 | * Controller for {@link net.proselyte.springsecurityapp.model.User}'s pages. 17 | * 18 | * @author Eugene Suleimanov 19 | * @version 1.0 20 | */ 21 | 22 | @Controller 23 | public class UserController { 24 | 25 | @Autowired 26 | private UserService userService; 27 | 28 | @Autowired 29 | private SecurityService securityService; 30 | 31 | @Autowired 32 | private UserValidator userValidator; 33 | 34 | @RequestMapping(value = "/registration", method = RequestMethod.GET) 35 | public String registration(Model model) { 36 | model.addAttribute("userForm", new User()); 37 | 38 | return "registration"; 39 | } 40 | 41 | @RequestMapping(value = "/registration", method = RequestMethod.POST) 42 | public String registration(@ModelAttribute("userForm") User userForm, BindingResult bindingResult, Model model) { 43 | userValidator.validate(userForm, bindingResult); 44 | 45 | if (bindingResult.hasErrors()) { 46 | return "registration"; 47 | } 48 | 49 | userService.save(userForm); 50 | 51 | securityService.autoLogin(userForm.getUsername(), userForm.getConfirmPassword()); 52 | 53 | return "redirect:/welcome"; 54 | } 55 | 56 | @RequestMapping(value = "/login", method = RequestMethod.GET) 57 | public String login(Model model, String error, String logout) { 58 | if (error != null) { 59 | model.addAttribute("error", "Username or password is incorrect."); 60 | } 61 | 62 | if (logout != null) { 63 | model.addAttribute("message", "Logged out successfully."); 64 | } 65 | 66 | return "login"; 67 | } 68 | 69 | @RequestMapping(value = {"/", "/welcome"}, method = RequestMethod.GET) 70 | public String welcome(Model model) { 71 | return "welcome"; 72 | } 73 | 74 | @RequestMapping(value = "/admin", method = RequestMethod.GET) 75 | public String admin(Model model) { 76 | return "admin"; 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /src/main/java/net/proselyte/springsecurityapp/dao/RoleDao.java: -------------------------------------------------------------------------------- 1 | package net.proselyte.springsecurityapp.dao; 2 | 3 | import net.proselyte.springsecurityapp.model.Role; 4 | import org.springframework.data.jpa.repository.JpaRepository; 5 | 6 | public interface RoleDao extends JpaRepository { 7 | } 8 | -------------------------------------------------------------------------------- /src/main/java/net/proselyte/springsecurityapp/dao/UserDao.java: -------------------------------------------------------------------------------- 1 | package net.proselyte.springsecurityapp.dao; 2 | 3 | import net.proselyte.springsecurityapp.model.User; 4 | import org.springframework.data.jpa.repository.JpaRepository; 5 | 6 | public interface UserDao extends JpaRepository { 7 | User findByUsername(String username); 8 | } 9 | -------------------------------------------------------------------------------- /src/main/java/net/proselyte/springsecurityapp/model/Role.java: -------------------------------------------------------------------------------- 1 | package net.proselyte.springsecurityapp.model; 2 | 3 | 4 | import javax.persistence.*; 5 | import java.util.Set; 6 | 7 | /** 8 | * Simple JavaBean object that represents role of {@link User}. 9 | * 10 | * @author Eugene Suleimanov 11 | * @version 1.0 12 | */ 13 | 14 | @Entity 15 | @Table(name = "roles") 16 | public class Role { 17 | 18 | @Id 19 | @GeneratedValue(strategy = GenerationType.AUTO) 20 | private Long id; 21 | 22 | @Column(name = "name") 23 | private String name; 24 | 25 | @ManyToMany(mappedBy = "roles") 26 | private Set users; 27 | 28 | public Role() { 29 | } 30 | 31 | public Long getId() { 32 | return id; 33 | } 34 | 35 | public void setId(Long id) { 36 | this.id = id; 37 | } 38 | 39 | public String getName() { 40 | return name; 41 | } 42 | 43 | public void setName(String name) { 44 | this.name = name; 45 | } 46 | 47 | public Set getUsers() { 48 | return users; 49 | } 50 | 51 | public void setUsers(Set users) { 52 | this.users = users; 53 | } 54 | 55 | @Override 56 | public String toString() { 57 | return "Role{" + 58 | "id=" + id + 59 | ", name='" + name + '\'' + 60 | ", users=" + users + 61 | '}'; 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /src/main/java/net/proselyte/springsecurityapp/model/User.java: -------------------------------------------------------------------------------- 1 | package net.proselyte.springsecurityapp.model; 2 | 3 | import javax.persistence.*; 4 | import java.util.Set; 5 | 6 | /** 7 | * Simple JavaBean domain object that represents a User. 8 | * 9 | * @author Eugene Suleimanov 10 | * @version 1.0 11 | */ 12 | 13 | @Entity 14 | @Table(name = "users") 15 | public class User { 16 | 17 | @Id 18 | @GeneratedValue(strategy = GenerationType.AUTO) 19 | private Long id; 20 | 21 | @Column(name = "username") 22 | private String username; 23 | 24 | @Column(name = "password") 25 | private String password; 26 | 27 | @Transient 28 | private String confirmPassword; 29 | 30 | @ManyToMany 31 | @JoinTable(name = "user_roles", joinColumns = @JoinColumn(name = "user_id"), 32 | inverseJoinColumns = @JoinColumn(name = "role_id")) 33 | private Set roles; 34 | 35 | public Long getId() { 36 | return id; 37 | } 38 | 39 | public void setId(Long id) { 40 | this.id = id; 41 | } 42 | 43 | public String getUsername() { 44 | return username; 45 | } 46 | 47 | public void setUsername(String username) { 48 | this.username = username; 49 | } 50 | 51 | public String getPassword() { 52 | return password; 53 | } 54 | 55 | public void setPassword(String password) { 56 | this.password = password; 57 | } 58 | 59 | public String getConfirmPassword() { 60 | return confirmPassword; 61 | } 62 | 63 | public void setConfirmPassword(String confirmPassword) { 64 | this.confirmPassword = confirmPassword; 65 | } 66 | 67 | public Set getRoles() { 68 | return roles; 69 | } 70 | 71 | public void setRoles(Set roles) { 72 | this.roles = roles; 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /src/main/java/net/proselyte/springsecurityapp/service/SecurityService.java: -------------------------------------------------------------------------------- 1 | package net.proselyte.springsecurityapp.service; 2 | 3 | /** 4 | * Service for Security. 5 | * 6 | * @author Eugene Suleimanov 7 | * @version 1.0 8 | */ 9 | 10 | public interface SecurityService { 11 | 12 | String findLoggedInUsername(); 13 | 14 | void autoLogin(String username, String password); 15 | } 16 | -------------------------------------------------------------------------------- /src/main/java/net/proselyte/springsecurityapp/service/SecurityServiceImpl.java: -------------------------------------------------------------------------------- 1 | package net.proselyte.springsecurityapp.service; 2 | 3 | import org.slf4j.Logger; 4 | import org.slf4j.LoggerFactory; 5 | import org.springframework.beans.factory.annotation.Autowired; 6 | import org.springframework.security.authentication.AuthenticationManager; 7 | import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; 8 | import org.springframework.security.core.context.SecurityContextHolder; 9 | import org.springframework.security.core.userdetails.UserDetails; 10 | import org.springframework.security.core.userdetails.UserDetailsService; 11 | import org.springframework.stereotype.Service; 12 | 13 | /** 14 | * Implementation of {@link SecurityService} interface. 15 | * 16 | * @author Eugene Suleimanov 17 | * @version 1.0 18 | */ 19 | 20 | @Service 21 | public class SecurityServiceImpl implements SecurityService { 22 | 23 | private static final Logger logger = LoggerFactory.getLogger(SecurityServiceImpl.class); 24 | 25 | @Autowired 26 | private AuthenticationManager authenticationManager; 27 | 28 | @Autowired 29 | private UserDetailsService userDetailsService; 30 | 31 | @Override 32 | public String findLoggedInUsername() { 33 | Object userDetails = SecurityContextHolder.getContext().getAuthentication().getDetails(); 34 | if (userDetails instanceof UserDetails) { 35 | return ((UserDetails) userDetails).getUsername(); 36 | } 37 | 38 | return null; 39 | } 40 | 41 | @Override 42 | public void autoLogin(String username, String password) { 43 | UserDetails userDetails = userDetailsService.loadUserByUsername(username); 44 | UsernamePasswordAuthenticationToken authenticationToken = 45 | new UsernamePasswordAuthenticationToken(userDetails, password, userDetails.getAuthorities()); 46 | 47 | authenticationManager.authenticate(authenticationToken); 48 | 49 | if (authenticationToken.isAuthenticated()) { 50 | SecurityContextHolder.getContext().setAuthentication(authenticationToken); 51 | 52 | logger.debug(String.format("Successfully %s auto logged in", username)); 53 | } 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/main/java/net/proselyte/springsecurityapp/service/UserDetailsServiceImpl.java: -------------------------------------------------------------------------------- 1 | package net.proselyte.springsecurityapp.service; 2 | 3 | import net.proselyte.springsecurityapp.dao.UserDao; 4 | import net.proselyte.springsecurityapp.model.Role; 5 | import net.proselyte.springsecurityapp.model.User; 6 | import org.springframework.beans.factory.annotation.Autowired; 7 | import org.springframework.security.core.GrantedAuthority; 8 | import org.springframework.security.core.authority.SimpleGrantedAuthority; 9 | import org.springframework.security.core.userdetails.UserDetails; 10 | import org.springframework.security.core.userdetails.UserDetailsService; 11 | import org.springframework.security.core.userdetails.UsernameNotFoundException; 12 | import org.springframework.transaction.annotation.Transactional; 13 | 14 | import java.util.HashSet; 15 | import java.util.Set; 16 | 17 | /** 18 | * Implementation of {@link org.springframework.security.core.userdetails.UserDetailsService} interface. 19 | * 20 | * @author Eugene Suleimanov 21 | * @version 1.0 22 | */ 23 | 24 | 25 | public class UserDetailsServiceImpl implements UserDetailsService { 26 | 27 | @Autowired 28 | private UserDao userDao; 29 | 30 | @Override 31 | @Transactional(readOnly = true) 32 | public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { 33 | User user = userDao.findByUsername(username); 34 | 35 | Set grantedAuthorities = new HashSet<>(); 36 | 37 | for (Role role : user.getRoles()) { 38 | grantedAuthorities.add(new SimpleGrantedAuthority(role.getName())); 39 | } 40 | return new org.springframework.security.core.userdetails.User(user.getUsername(), user.getPassword(), grantedAuthorities); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/main/java/net/proselyte/springsecurityapp/service/UserService.java: -------------------------------------------------------------------------------- 1 | package net.proselyte.springsecurityapp.service; 2 | 3 | import net.proselyte.springsecurityapp.model.User; 4 | 5 | /** 6 | * Service class for {@link net.proselyte.springsecurityapp.model.User} 7 | * 8 | * @author Eugene Suleimanov 9 | * @version 1.0 10 | */ 11 | 12 | public interface UserService { 13 | 14 | void save(User user); 15 | 16 | User findByUsername(String username); 17 | } 18 | -------------------------------------------------------------------------------- /src/main/java/net/proselyte/springsecurityapp/service/UserServiceImpl.java: -------------------------------------------------------------------------------- 1 | package net.proselyte.springsecurityapp.service; 2 | 3 | import net.proselyte.springsecurityapp.dao.RoleDao; 4 | import net.proselyte.springsecurityapp.dao.UserDao; 5 | import net.proselyte.springsecurityapp.model.Role; 6 | import net.proselyte.springsecurityapp.model.User; 7 | import org.springframework.beans.factory.annotation.Autowired; 8 | import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; 9 | import org.springframework.stereotype.Service; 10 | 11 | import java.util.HashSet; 12 | import java.util.Set; 13 | 14 | /** 15 | * Implementation of {@link UserService} interface. 16 | * 17 | * @author Eugene Suleimanov 18 | * @version 1.0 19 | */ 20 | 21 | @Service 22 | public class UserServiceImpl implements UserService { 23 | 24 | @Autowired 25 | private UserDao userDao; 26 | 27 | @Autowired 28 | private RoleDao roleDao; 29 | 30 | @Autowired 31 | private BCryptPasswordEncoder bCryptPasswordEncoder; 32 | 33 | @Override 34 | public void save(User user) { 35 | user.setPassword(bCryptPasswordEncoder.encode(user.getPassword())); 36 | Set roles = new HashSet<>(); 37 | roles.add(roleDao.getOne(1L)); 38 | user.setRoles(roles); 39 | userDao.save(user); 40 | } 41 | 42 | @Override 43 | public User findByUsername(String username) { 44 | return userDao.findByUsername(username); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/main/java/net/proselyte/springsecurityapp/validator/UserValidator.java: -------------------------------------------------------------------------------- 1 | package net.proselyte.springsecurityapp.validator; 2 | 3 | import net.proselyte.springsecurityapp.model.User; 4 | import net.proselyte.springsecurityapp.service.UserService; 5 | import org.springframework.beans.factory.annotation.Autowired; 6 | import org.springframework.stereotype.Component; 7 | import org.springframework.validation.Errors; 8 | import org.springframework.validation.ValidationUtils; 9 | import org.springframework.validation.Validator; 10 | 11 | /** 12 | * Validator for {@link net.proselyte.springsecurityapp.model.User} class, 13 | * implements {@link Validator} interface. 14 | * 15 | * @author Eugene Suleimanov 16 | * @version 1.0 17 | */ 18 | 19 | @Component 20 | public class UserValidator implements Validator { 21 | 22 | @Autowired 23 | private UserService userService; 24 | 25 | @Override 26 | public boolean supports(Class aClass) { 27 | return User.class.equals(aClass); 28 | } 29 | 30 | @Override 31 | public void validate(Object o, Errors errors) { 32 | User user = (User) o; 33 | 34 | ValidationUtils.rejectIfEmptyOrWhitespace(errors, "username", "Required"); 35 | if (user.getUsername().length() < 8 || user.getUsername().length() > 32) { 36 | errors.rejectValue("username", "Size.userForm.username"); 37 | } 38 | 39 | if (userService.findByUsername(user.getUsername()) != null) { 40 | errors.rejectValue("username", "Duplicate.userForm.username"); 41 | } 42 | 43 | ValidationUtils.rejectIfEmptyOrWhitespace(errors, "password", "Required"); 44 | if (user.getPassword().length() < 8 || user.getPassword().length() > 32) { 45 | errors.rejectValue("password", "Size.userForm.password"); 46 | } 47 | 48 | if (!user.getConfirmPassword().equals(user.getPassword())) { 49 | errors.rejectValue("confirmPassword", "Different.userForm.password"); 50 | } 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/main/resources/database.properties: -------------------------------------------------------------------------------- 1 | jdbc.driverClassName=com.mysql.jdbc.Driver 2 | jdbc.url=jdbc:mysql://localhost:3306/spring_security_app 3 | jdbc.username=root 4 | jdbc.password=root -------------------------------------------------------------------------------- /src/main/resources/database.sql: -------------------------------------------------------------------------------- 1 | -- Table: users 2 | CREATE TABLE users ( 3 | id INT NOT NULL AUTO_INCREMENT PRIMARY KEY, 4 | username VARCHAR(255) NOT NULL, 5 | password VARCHAR(255) NOT NULL 6 | ) 7 | ENGINE = InnoDB; 8 | 9 | -- Table: roles 10 | CREATE TABLE roles ( 11 | id INT NOT NULL AUTO_INCREMENT PRIMARY KEY, 12 | name VARCHAR(100) NOT NULL 13 | ) 14 | ENGINE = InnoDB; 15 | 16 | -- Table for mapping user and roles: user_roles 17 | CREATE TABLE user_roles ( 18 | user_id INT NOT NULL, 19 | role_id INT NOT NULL, 20 | 21 | FOREIGN KEY (user_id) REFERENCES users (id), 22 | FOREIGN KEY (role_id) REFERENCES roles (id), 23 | 24 | UNIQUE (user_id, role_id) 25 | ) 26 | ENGINE = InnoDB; 27 | 28 | -- Insert data 29 | 30 | INSERT INTO users VALUES (1, 'proselyte', '$2a$11$uSXS6rLJ91WjgOHhEGDx..VGs7MkKZV68Lv5r1uwFu7HgtRn3dcXG'); 31 | 32 | INSERT INTO roles VALUES (1, 'ROLE_USER'); 33 | INSERT INTO roles VALUES (2, 'ROLE_ADMIN'); 34 | 35 | INSERT INTO user_roles VALUES (1, 2); -------------------------------------------------------------------------------- /src/main/resources/logback.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | %date{HH:mm:ss.SSS} [%thread] %-5level %logger{15}#%line %msg\n 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /src/main/resources/validation.properties: -------------------------------------------------------------------------------- 1 | Required=This field is required. 2 | Size.userForm.username=Username must be between 8 and 32 characters. 3 | Duplicate.userForm.username=Such username already exists. 4 | Size.userForm.password=Password must be over 8 characters. 5 | Different.userForm.password=Password don't match. 6 | -------------------------------------------------------------------------------- /src/main/webapp/WEB-INF/appconfig-data.xml: -------------------------------------------------------------------------------- 1 | 2 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | org.hibernate.dialect.MySQL5Dialect 29 | true 30 | 31 | 32 | 33 | 34 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /src/main/webapp/WEB-INF/appconfig-mvc.xml: -------------------------------------------------------------------------------- 1 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | classpath:validation 15 | 16 | 17 | 18 | 19 | 20 | 21 | /WEB-INF/views/ 22 | 23 | 24 | .jsp 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /src/main/webapp/WEB-INF/appconfig-root.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /src/main/webapp/WEB-INF/appconfig-security.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 29 | 30 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /src/main/webapp/WEB-INF/views/admin.jsp: -------------------------------------------------------------------------------- 1 | <%@ taglib prefix="spring" uri="http://www.springframework.org/tags" %> 2 | <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | Admin 16 | 17 | 18 | 19 | 20 | 21 |
22 | 23 |
24 | 25 |
26 |

Admin Page ${pageContext.request.userPrincipal.name} | Logout 27 |

28 |
29 |
30 | 31 | 32 | 33 | 34 | 35 | -------------------------------------------------------------------------------- /src/main/webapp/WEB-INF/views/login.jsp: -------------------------------------------------------------------------------- 1 | <%@ taglib prefix="spring" uri="http://www.springframework.org/tags" %> 2 | <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> 3 | <%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %> 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | Log in with your account 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 |
29 | 30 | 46 | 47 |
48 | 49 | 50 | 51 | 52 | -------------------------------------------------------------------------------- /src/main/webapp/WEB-INF/views/registration.jsp: -------------------------------------------------------------------------------- 1 | <%@ taglib prefix="spring" uri="http://www.springframework.org/tags" %> 2 | <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> 3 | <%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %> 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | Create an account 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 |
30 | 31 | 58 | 59 |
60 | 61 | 62 | 63 | 64 | -------------------------------------------------------------------------------- /src/main/webapp/WEB-INF/views/welcome.jsp: -------------------------------------------------------------------------------- 1 | <%@ taglib prefix="spring" uri="http://www.springframework.org/tags" %> 2 | <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | Welcome 16 | 17 | 18 | 19 | 20 | 21 |
22 | 23 | 24 |
25 | 26 |
27 | 28 |

Welcome ${pageContext.request.userPrincipal.name} | Logout 29 |

30 | 31 |
32 | 33 |
34 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /src/main/webapp/WEB-INF/web.xml: -------------------------------------------------------------------------------- 1 | 6 | 7 | Spring Security Application 8 | 9 | 10 | contextConfigLocation 11 | /WEB-INF/appconfig-root.xml 12 | 13 | 14 | 15 | springSecurityFilterChain 16 | org.springframework.web.filter.DelegatingFilterProxy 17 | 18 | 19 | springSecurityFilterChain 20 | /* 21 | 22 | 23 | 24 | dispatcher 25 | org.springframework.web.servlet.DispatcherServlet 26 | 27 | contextConfigLocation 28 | 29 | 30 | 1 31 | 32 | 33 | 34 | dispatcher 35 | / 36 | 37 | 38 | 39 | org.springframework.web.context.ContextLoaderListener 40 | 41 | -------------------------------------------------------------------------------- /src/main/webapp/resources/css/common.css: -------------------------------------------------------------------------------- 1 | body { 2 | padding-top: 40px; 3 | padding-bottom: 40px; 4 | background-color: #eee; 5 | } 6 | 7 | .form-signin { 8 | max-width: 330px; 9 | padding: 15px; 10 | margin: 0 auto; 11 | } 12 | 13 | .form-signin .form-signin-heading, 14 | .form-signin .checkbox { 15 | margin-bottom: 10px; 16 | } 17 | 18 | .form-signin .checkbox { 19 | font-weight: normal; 20 | } 21 | 22 | .form-signin .form-control { 23 | position: relative; 24 | height: auto; 25 | -webkit-box-sizing: border-box; 26 | -moz-box-sizing: border-box; 27 | box-sizing: border-box; 28 | padding: 10px; 29 | font-size: 16px; 30 | } 31 | 32 | .form-signin .form-control:focus { 33 | z-index: 2; 34 | } 35 | 36 | .form-signin input { 37 | margin-top: 10px; 38 | border-bottom-right-radius: 0; 39 | border-bottom-left-radius: 0; 40 | } 41 | 42 | .form-signin button { 43 | margin-top: 10px; 44 | } 45 | 46 | .has-error { 47 | color: red 48 | } -------------------------------------------------------------------------------- /src/main/webapp/resources/js/bootstrap.min.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * Bootstrap v3.3.5 (http://getbootstrap.com) 3 | * Copyright 2011-2015 Twitter, Inc. 4 | * Licensed under the MIT license 5 | */ 6 | if ("undefined" == typeof jQuery)throw new Error("Bootstrap's JavaScript requires jQuery"); 7 | +function (a) { 8 | "use strict"; 9 | var b = a.fn.jquery.split(" ")[0].split("."); 10 | if (b[0] < 2 && b[1] < 9 || 1 == b[0] && 9 == b[1] && b[2] < 1)throw new Error("Bootstrap's JavaScript requires jQuery version 1.9.1 or higher") 11 | }(jQuery), +function (a) { 12 | "use strict"; 13 | function b() { 14 | var a = document.createElement("bootstrap"), b = { 15 | WebkitTransition: "webkitTransitionEnd", 16 | MozTransition: "transitionend", 17 | OTransition: "oTransitionEnd otransitionend", 18 | transition: "transitionend" 19 | }; 20 | for (var c in b)if (void 0 !== a.style[c])return {end: b[c]}; 21 | return !1 22 | } 23 | 24 | a.fn.emulateTransitionEnd = function (b) { 25 | var c = !1, d = this; 26 | a(this).one("bsTransitionEnd", function () { 27 | c = !0 28 | }); 29 | var e = function () { 30 | c || a(d).trigger(a.support.transition.end) 31 | }; 32 | return setTimeout(e, b), this 33 | }, a(function () { 34 | a.support.transition = b(), a.support.transition && (a.event.special.bsTransitionEnd = { 35 | bindType: a.support.transition.end, 36 | delegateType: a.support.transition.end, 37 | handle: function (b) { 38 | return a(b.target).is(this) ? b.handleObj.handler.apply(this, arguments) : void 0 39 | } 40 | }) 41 | }) 42 | }(jQuery), +function (a) { 43 | "use strict"; 44 | function b(b) { 45 | return this.each(function () { 46 | var c = a(this), e = c.data("bs.alert"); 47 | e || c.data("bs.alert", e = new d(this)), "string" == typeof b && e[b].call(c) 48 | }) 49 | } 50 | 51 | var c = '[data-dismiss="alert"]', d = function (b) { 52 | a(b).on("click", c, this.close) 53 | }; 54 | d.VERSION = "3.3.5", d.TRANSITION_DURATION = 150, d.prototype.close = function (b) { 55 | function c() { 56 | g.detach().trigger("closed.bs.alert").remove() 57 | } 58 | 59 | var e = a(this), f = e.attr("data-target"); 60 | f || (f = e.attr("href"), f = f && f.replace(/.*(?=#[^\s]*$)/, "")); 61 | var g = a(f); 62 | b && b.preventDefault(), g.length || (g = e.closest(".alert")), g.trigger(b = a.Event("close.bs.alert")), b.isDefaultPrevented() || (g.removeClass("in"), a.support.transition && g.hasClass("fade") ? g.one("bsTransitionEnd", c).emulateTransitionEnd(d.TRANSITION_DURATION) : c()) 63 | }; 64 | var e = a.fn.alert; 65 | a.fn.alert = b, a.fn.alert.Constructor = d, a.fn.alert.noConflict = function () { 66 | return a.fn.alert = e, this 67 | }, a(document).on("click.bs.alert.data-api", c, d.prototype.close) 68 | }(jQuery), +function (a) { 69 | "use strict"; 70 | function b(b) { 71 | return this.each(function () { 72 | var d = a(this), e = d.data("bs.button"), f = "object" == typeof b && b; 73 | e || d.data("bs.button", e = new c(this, f)), "toggle" == b ? e.toggle() : b && e.setState(b) 74 | }) 75 | } 76 | 77 | var c = function (b, d) { 78 | this.$element = a(b), this.options = a.extend({}, c.DEFAULTS, d), this.isLoading = !1 79 | }; 80 | c.VERSION = "3.3.5", c.DEFAULTS = {loadingText: "loading..."}, c.prototype.setState = function (b) { 81 | var c = "disabled", d = this.$element, e = d.is("input") ? "val" : "html", f = d.data(); 82 | b += "Text", null == f.resetText && d.data("resetText", d[e]()), setTimeout(a.proxy(function () { 83 | d[e](null == f[b] ? this.options[b] : f[b]), "loadingText" == b ? (this.isLoading = !0, d.addClass(c).attr(c, c)) : this.isLoading && (this.isLoading = !1, d.removeClass(c).removeAttr(c)) 84 | }, this), 0) 85 | }, c.prototype.toggle = function () { 86 | var a = !0, b = this.$element.closest('[data-toggle="buttons"]'); 87 | if (b.length) { 88 | var c = this.$element.find("input"); 89 | "radio" == c.prop("type") ? (c.prop("checked") && (a = !1), b.find(".active").removeClass("active"), this.$element.addClass("active")) : "checkbox" == c.prop("type") && (c.prop("checked") !== this.$element.hasClass("active") && (a = !1), this.$element.toggleClass("active")), c.prop("checked", this.$element.hasClass("active")), a && c.trigger("change") 90 | } else this.$element.attr("aria-pressed", !this.$element.hasClass("active")), this.$element.toggleClass("active") 91 | }; 92 | var d = a.fn.button; 93 | a.fn.button = b, a.fn.button.Constructor = c, a.fn.button.noConflict = function () { 94 | return a.fn.button = d, this 95 | }, a(document).on("click.bs.button.data-api", '[data-toggle^="button"]', function (c) { 96 | var d = a(c.target); 97 | d.hasClass("btn") || (d = d.closest(".btn")), b.call(d, "toggle"), a(c.target).is('input[type="radio"]') || a(c.target).is('input[type="checkbox"]') || c.preventDefault() 98 | }).on("focus.bs.button.data-api blur.bs.button.data-api", '[data-toggle^="button"]', function (b) { 99 | a(b.target).closest(".btn").toggleClass("focus", /^focus(in)?$/.test(b.type)) 100 | }) 101 | }(jQuery), +function (a) { 102 | "use strict"; 103 | function b(b) { 104 | return this.each(function () { 105 | var d = a(this), e = d.data("bs.carousel"), f = a.extend({}, c.DEFAULTS, d.data(), "object" == typeof b && b), g = "string" == typeof b ? b : f.slide; 106 | e || d.data("bs.carousel", e = new c(this, f)), "number" == typeof b ? e.to(b) : g ? e[g]() : f.interval && e.pause().cycle() 107 | }) 108 | } 109 | 110 | var c = function (b, c) { 111 | this.$element = a(b), this.$indicators = this.$element.find(".carousel-indicators"), this.options = c, this.paused = null, this.sliding = null, this.interval = null, this.$active = null, this.$items = null, this.options.keyboard && this.$element.on("keydown.bs.carousel", a.proxy(this.keydown, this)), "hover" == this.options.pause && !("ontouchstart"in document.documentElement) && this.$element.on("mouseenter.bs.carousel", a.proxy(this.pause, this)).on("mouseleave.bs.carousel", a.proxy(this.cycle, this)) 112 | }; 113 | c.VERSION = "3.3.5", c.TRANSITION_DURATION = 600, c.DEFAULTS = { 114 | interval: 5e3, 115 | pause: "hover", 116 | wrap: !0, 117 | keyboard: !0 118 | }, c.prototype.keydown = function (a) { 119 | if (!/input|textarea/i.test(a.target.tagName)) { 120 | switch (a.which) { 121 | case 37: 122 | this.prev(); 123 | break; 124 | case 39: 125 | this.next(); 126 | break; 127 | default: 128 | return 129 | } 130 | a.preventDefault() 131 | } 132 | }, c.prototype.cycle = function (b) { 133 | return b || (this.paused = !1), this.interval && clearInterval(this.interval), this.options.interval && !this.paused && (this.interval = setInterval(a.proxy(this.next, this), this.options.interval)), this 134 | }, c.prototype.getItemIndex = function (a) { 135 | return this.$items = a.parent().children(".item"), this.$items.index(a || this.$active) 136 | }, c.prototype.getItemForDirection = function (a, b) { 137 | var c = this.getItemIndex(b), d = "prev" == a && 0 === c || "next" == a && c == this.$items.length - 1; 138 | if (d && !this.options.wrap)return b; 139 | var e = "prev" == a ? -1 : 1, f = (c + e) % this.$items.length; 140 | return this.$items.eq(f) 141 | }, c.prototype.to = function (a) { 142 | var b = this, c = this.getItemIndex(this.$active = this.$element.find(".item.active")); 143 | return a > this.$items.length - 1 || 0 > a ? void 0 : this.sliding ? this.$element.one("slid.bs.carousel", function () { 144 | b.to(a) 145 | }) : c == a ? this.pause().cycle() : this.slide(a > c ? "next" : "prev", this.$items.eq(a)) 146 | }, c.prototype.pause = function (b) { 147 | return b || (this.paused = !0), this.$element.find(".next, .prev").length && a.support.transition && (this.$element.trigger(a.support.transition.end), this.cycle(!0)), this.interval = clearInterval(this.interval), this 148 | }, c.prototype.next = function () { 149 | return this.sliding ? void 0 : this.slide("next") 150 | }, c.prototype.prev = function () { 151 | return this.sliding ? void 0 : this.slide("prev") 152 | }, c.prototype.slide = function (b, d) { 153 | var e = this.$element.find(".item.active"), f = d || this.getItemForDirection(b, e), g = this.interval, h = "next" == b ? "left" : "right", i = this; 154 | if (f.hasClass("active"))return this.sliding = !1; 155 | var j = f[0], k = a.Event("slide.bs.carousel", {relatedTarget: j, direction: h}); 156 | if (this.$element.trigger(k), !k.isDefaultPrevented()) { 157 | if (this.sliding = !0, g && this.pause(), this.$indicators.length) { 158 | this.$indicators.find(".active").removeClass("active"); 159 | var l = a(this.$indicators.children()[this.getItemIndex(f)]); 160 | l && l.addClass("active") 161 | } 162 | var m = a.Event("slid.bs.carousel", {relatedTarget: j, direction: h}); 163 | return a.support.transition && this.$element.hasClass("slide") ? (f.addClass(b), f[0].offsetWidth, e.addClass(h), f.addClass(h), e.one("bsTransitionEnd", function () { 164 | f.removeClass([b, h].join(" ")).addClass("active"), e.removeClass(["active", h].join(" ")), i.sliding = !1, setTimeout(function () { 165 | i.$element.trigger(m) 166 | }, 0) 167 | }).emulateTransitionEnd(c.TRANSITION_DURATION)) : (e.removeClass("active"), f.addClass("active"), this.sliding = !1, this.$element.trigger(m)), g && this.cycle(), this 168 | } 169 | }; 170 | var d = a.fn.carousel; 171 | a.fn.carousel = b, a.fn.carousel.Constructor = c, a.fn.carousel.noConflict = function () { 172 | return a.fn.carousel = d, this 173 | }; 174 | var e = function (c) { 175 | var d, e = a(this), f = a(e.attr("data-target") || (d = e.attr("href")) && d.replace(/.*(?=#[^\s]+$)/, "")); 176 | if (f.hasClass("carousel")) { 177 | var g = a.extend({}, f.data(), e.data()), h = e.attr("data-slide-to"); 178 | h && (g.interval = !1), b.call(f, g), h && f.data("bs.carousel").to(h), c.preventDefault() 179 | } 180 | }; 181 | a(document).on("click.bs.carousel.data-api", "[data-slide]", e).on("click.bs.carousel.data-api", "[data-slide-to]", e), a(window).on("load", function () { 182 | a('[data-ride="carousel"]').each(function () { 183 | var c = a(this); 184 | b.call(c, c.data()) 185 | }) 186 | }) 187 | }(jQuery), +function (a) { 188 | "use strict"; 189 | function b(b) { 190 | var c, d = b.attr("data-target") || (c = b.attr("href")) && c.replace(/.*(?=#[^\s]+$)/, ""); 191 | return a(d) 192 | } 193 | 194 | function c(b) { 195 | return this.each(function () { 196 | var c = a(this), e = c.data("bs.collapse"), f = a.extend({}, d.DEFAULTS, c.data(), "object" == typeof b && b); 197 | !e && f.toggle && /show|hide/.test(b) && (f.toggle = !1), e || c.data("bs.collapse", e = new d(this, f)), "string" == typeof b && e[b]() 198 | }) 199 | } 200 | 201 | var d = function (b, c) { 202 | this.$element = a(b), this.options = a.extend({}, d.DEFAULTS, c), this.$trigger = a('[data-toggle="collapse"][href="#' + b.id + '"],[data-toggle="collapse"][data-target="#' + b.id + '"]'), this.transitioning = null, this.options.parent ? this.$parent = this.getParent() : this.addAriaAndCollapsedClass(this.$element, this.$trigger), this.options.toggle && this.toggle() 203 | }; 204 | d.VERSION = "3.3.5", d.TRANSITION_DURATION = 350, d.DEFAULTS = {toggle: !0}, d.prototype.dimension = function () { 205 | var a = this.$element.hasClass("width"); 206 | return a ? "width" : "height" 207 | }, d.prototype.show = function () { 208 | if (!this.transitioning && !this.$element.hasClass("in")) { 209 | var b, e = this.$parent && this.$parent.children(".panel").children(".in, .collapsing"); 210 | if (!(e && e.length && (b = e.data("bs.collapse"), b && b.transitioning))) { 211 | var f = a.Event("show.bs.collapse"); 212 | if (this.$element.trigger(f), !f.isDefaultPrevented()) { 213 | e && e.length && (c.call(e, "hide"), b || e.data("bs.collapse", null)); 214 | var g = this.dimension(); 215 | this.$element.removeClass("collapse").addClass("collapsing")[g](0).attr("aria-expanded", !0), this.$trigger.removeClass("collapsed").attr("aria-expanded", !0), this.transitioning = 1; 216 | var h = function () { 217 | this.$element.removeClass("collapsing").addClass("collapse in")[g](""), this.transitioning = 0, this.$element.trigger("shown.bs.collapse") 218 | }; 219 | if (!a.support.transition)return h.call(this); 220 | var i = a.camelCase(["scroll", g].join("-")); 221 | this.$element.one("bsTransitionEnd", a.proxy(h, this)).emulateTransitionEnd(d.TRANSITION_DURATION)[g](this.$element[0][i]) 222 | } 223 | } 224 | } 225 | }, d.prototype.hide = function () { 226 | if (!this.transitioning && this.$element.hasClass("in")) { 227 | var b = a.Event("hide.bs.collapse"); 228 | if (this.$element.trigger(b), !b.isDefaultPrevented()) { 229 | var c = this.dimension(); 230 | this.$element[c](this.$element[c]())[0].offsetHeight, this.$element.addClass("collapsing").removeClass("collapse in").attr("aria-expanded", !1), this.$trigger.addClass("collapsed").attr("aria-expanded", !1), this.transitioning = 1; 231 | var e = function () { 232 | this.transitioning = 0, this.$element.removeClass("collapsing").addClass("collapse").trigger("hidden.bs.collapse") 233 | }; 234 | return a.support.transition ? void this.$element[c](0).one("bsTransitionEnd", a.proxy(e, this)).emulateTransitionEnd(d.TRANSITION_DURATION) : e.call(this) 235 | } 236 | } 237 | }, d.prototype.toggle = function () { 238 | this[this.$element.hasClass("in") ? "hide" : "show"]() 239 | }, d.prototype.getParent = function () { 240 | return a(this.options.parent).find('[data-toggle="collapse"][data-parent="' + this.options.parent + '"]').each(a.proxy(function (c, d) { 241 | var e = a(d); 242 | this.addAriaAndCollapsedClass(b(e), e) 243 | }, this)).end() 244 | }, d.prototype.addAriaAndCollapsedClass = function (a, b) { 245 | var c = a.hasClass("in"); 246 | a.attr("aria-expanded", c), b.toggleClass("collapsed", !c).attr("aria-expanded", c) 247 | }; 248 | var e = a.fn.collapse; 249 | a.fn.collapse = c, a.fn.collapse.Constructor = d, a.fn.collapse.noConflict = function () { 250 | return a.fn.collapse = e, this 251 | }, a(document).on("click.bs.collapse.data-api", '[data-toggle="collapse"]', function (d) { 252 | var e = a(this); 253 | e.attr("data-target") || d.preventDefault(); 254 | var f = b(e), g = f.data("bs.collapse"), h = g ? "toggle" : e.data(); 255 | c.call(f, h) 256 | }) 257 | }(jQuery), +function (a) { 258 | "use strict"; 259 | function b(b) { 260 | var c = b.attr("data-target"); 261 | c || (c = b.attr("href"), c = c && /#[A-Za-z]/.test(c) && c.replace(/.*(?=#[^\s]*$)/, "")); 262 | var d = c && a(c); 263 | return d && d.length ? d : b.parent() 264 | } 265 | 266 | function c(c) { 267 | c && 3 === c.which || (a(e).remove(), a(f).each(function () { 268 | var d = a(this), e = b(d), f = {relatedTarget: this}; 269 | e.hasClass("open") && (c && "click" == c.type && /input|textarea/i.test(c.target.tagName) && a.contains(e[0], c.target) || (e.trigger(c = a.Event("hide.bs.dropdown", f)), c.isDefaultPrevented() || (d.attr("aria-expanded", "false"), e.removeClass("open").trigger("hidden.bs.dropdown", f)))) 270 | })) 271 | } 272 | 273 | function d(b) { 274 | return this.each(function () { 275 | var c = a(this), d = c.data("bs.dropdown"); 276 | d || c.data("bs.dropdown", d = new g(this)), "string" == typeof b && d[b].call(c) 277 | }) 278 | } 279 | 280 | var e = ".dropdown-backdrop", f = '[data-toggle="dropdown"]', g = function (b) { 281 | a(b).on("click.bs.dropdown", this.toggle) 282 | }; 283 | g.VERSION = "3.3.5", g.prototype.toggle = function (d) { 284 | var e = a(this); 285 | if (!e.is(".disabled, :disabled")) { 286 | var f = b(e), g = f.hasClass("open"); 287 | if (c(), !g) { 288 | "ontouchstart"in document.documentElement && !f.closest(".navbar-nav").length && a(document.createElement("div")).addClass("dropdown-backdrop").insertAfter(a(this)).on("click", c); 289 | var h = {relatedTarget: this}; 290 | if (f.trigger(d = a.Event("show.bs.dropdown", h)), d.isDefaultPrevented())return; 291 | e.trigger("focus").attr("aria-expanded", "true"), f.toggleClass("open").trigger("shown.bs.dropdown", h) 292 | } 293 | return !1 294 | } 295 | }, g.prototype.keydown = function (c) { 296 | if (/(38|40|27|32)/.test(c.which) && !/input|textarea/i.test(c.target.tagName)) { 297 | var d = a(this); 298 | if (c.preventDefault(), c.stopPropagation(), !d.is(".disabled, :disabled")) { 299 | var e = b(d), g = e.hasClass("open"); 300 | if (!g && 27 != c.which || g && 27 == c.which)return 27 == c.which && e.find(f).trigger("focus"), d.trigger("click"); 301 | var h = " li:not(.disabled):visible a", i = e.find(".dropdown-menu" + h); 302 | if (i.length) { 303 | var j = i.index(c.target); 304 | 38 == c.which && j > 0 && j--, 40 == c.which && j < i.length - 1 && j++, ~j || (j = 0), i.eq(j).trigger("focus") 305 | } 306 | } 307 | } 308 | }; 309 | var h = a.fn.dropdown; 310 | a.fn.dropdown = d, a.fn.dropdown.Constructor = g, a.fn.dropdown.noConflict = function () { 311 | return a.fn.dropdown = h, this 312 | }, a(document).on("click.bs.dropdown.data-api", c).on("click.bs.dropdown.data-api", ".dropdown form", function (a) { 313 | a.stopPropagation() 314 | }).on("click.bs.dropdown.data-api", f, g.prototype.toggle).on("keydown.bs.dropdown.data-api", f, g.prototype.keydown).on("keydown.bs.dropdown.data-api", ".dropdown-menu", g.prototype.keydown) 315 | }(jQuery), +function (a) { 316 | "use strict"; 317 | function b(b, d) { 318 | return this.each(function () { 319 | var e = a(this), f = e.data("bs.modal"), g = a.extend({}, c.DEFAULTS, e.data(), "object" == typeof b && b); 320 | f || e.data("bs.modal", f = new c(this, g)), "string" == typeof b ? f[b](d) : g.show && f.show(d) 321 | }) 322 | } 323 | 324 | var c = function (b, c) { 325 | this.options = c, this.$body = a(document.body), this.$element = a(b), this.$dialog = this.$element.find(".modal-dialog"), this.$backdrop = null, this.isShown = null, this.originalBodyPad = null, this.scrollbarWidth = 0, this.ignoreBackdropClick = !1, this.options.remote && this.$element.find(".modal-content").load(this.options.remote, a.proxy(function () { 326 | this.$element.trigger("loaded.bs.modal") 327 | }, this)) 328 | }; 329 | c.VERSION = "3.3.5", c.TRANSITION_DURATION = 300, c.BACKDROP_TRANSITION_DURATION = 150, c.DEFAULTS = { 330 | backdrop: !0, 331 | keyboard: !0, 332 | show: !0 333 | }, c.prototype.toggle = function (a) { 334 | return this.isShown ? this.hide() : this.show(a) 335 | }, c.prototype.show = function (b) { 336 | var d = this, e = a.Event("show.bs.modal", {relatedTarget: b}); 337 | this.$element.trigger(e), this.isShown || e.isDefaultPrevented() || (this.isShown = !0, this.checkScrollbar(), this.setScrollbar(), this.$body.addClass("modal-open"), this.escape(), this.resize(), this.$element.on("click.dismiss.bs.modal", '[data-dismiss="modal"]', a.proxy(this.hide, this)), this.$dialog.on("mousedown.dismiss.bs.modal", function () { 338 | d.$element.one("mouseup.dismiss.bs.modal", function (b) { 339 | a(b.target).is(d.$element) && (d.ignoreBackdropClick = !0) 340 | }) 341 | }), this.backdrop(function () { 342 | var e = a.support.transition && d.$element.hasClass("fade"); 343 | d.$element.parent().length || d.$element.appendTo(d.$body), d.$element.show().scrollTop(0), d.adjustDialog(), e && d.$element[0].offsetWidth, d.$element.addClass("in"), d.enforceFocus(); 344 | var f = a.Event("shown.bs.modal", {relatedTarget: b}); 345 | e ? d.$dialog.one("bsTransitionEnd", function () { 346 | d.$element.trigger("focus").trigger(f) 347 | }).emulateTransitionEnd(c.TRANSITION_DURATION) : d.$element.trigger("focus").trigger(f) 348 | })) 349 | }, c.prototype.hide = function (b) { 350 | b && b.preventDefault(), b = a.Event("hide.bs.modal"), this.$element.trigger(b), this.isShown && !b.isDefaultPrevented() && (this.isShown = !1, this.escape(), this.resize(), a(document).off("focusin.bs.modal"), this.$element.removeClass("in").off("click.dismiss.bs.modal").off("mouseup.dismiss.bs.modal"), this.$dialog.off("mousedown.dismiss.bs.modal"), a.support.transition && this.$element.hasClass("fade") ? this.$element.one("bsTransitionEnd", a.proxy(this.hideModal, this)).emulateTransitionEnd(c.TRANSITION_DURATION) : this.hideModal()) 351 | }, c.prototype.enforceFocus = function () { 352 | a(document).off("focusin.bs.modal").on("focusin.bs.modal", a.proxy(function (a) { 353 | this.$element[0] === a.target || this.$element.has(a.target).length || this.$element.trigger("focus") 354 | }, this)) 355 | }, c.prototype.escape = function () { 356 | this.isShown && this.options.keyboard ? this.$element.on("keydown.dismiss.bs.modal", a.proxy(function (a) { 357 | 27 == a.which && this.hide() 358 | }, this)) : this.isShown || this.$element.off("keydown.dismiss.bs.modal") 359 | }, c.prototype.resize = function () { 360 | this.isShown ? a(window).on("resize.bs.modal", a.proxy(this.handleUpdate, this)) : a(window).off("resize.bs.modal") 361 | }, c.prototype.hideModal = function () { 362 | var a = this; 363 | this.$element.hide(), this.backdrop(function () { 364 | a.$body.removeClass("modal-open"), a.resetAdjustments(), a.resetScrollbar(), a.$element.trigger("hidden.bs.modal") 365 | }) 366 | }, c.prototype.removeBackdrop = function () { 367 | this.$backdrop && this.$backdrop.remove(), this.$backdrop = null 368 | }, c.prototype.backdrop = function (b) { 369 | var d = this, e = this.$element.hasClass("fade") ? "fade" : ""; 370 | if (this.isShown && this.options.backdrop) { 371 | var f = a.support.transition && e; 372 | if (this.$backdrop = a(document.createElement("div")).addClass("modal-backdrop " + e).appendTo(this.$body), this.$element.on("click.dismiss.bs.modal", a.proxy(function (a) { 373 | return this.ignoreBackdropClick ? void(this.ignoreBackdropClick = !1) : void(a.target === a.currentTarget && ("static" == this.options.backdrop ? this.$element[0].focus() : this.hide())) 374 | }, this)), f && this.$backdrop[0].offsetWidth, this.$backdrop.addClass("in"), !b)return; 375 | f ? this.$backdrop.one("bsTransitionEnd", b).emulateTransitionEnd(c.BACKDROP_TRANSITION_DURATION) : b() 376 | } else if (!this.isShown && this.$backdrop) { 377 | this.$backdrop.removeClass("in"); 378 | var g = function () { 379 | d.removeBackdrop(), b && b() 380 | }; 381 | a.support.transition && this.$element.hasClass("fade") ? this.$backdrop.one("bsTransitionEnd", g).emulateTransitionEnd(c.BACKDROP_TRANSITION_DURATION) : g() 382 | } else b && b() 383 | }, c.prototype.handleUpdate = function () { 384 | this.adjustDialog() 385 | }, c.prototype.adjustDialog = function () { 386 | var a = this.$element[0].scrollHeight > document.documentElement.clientHeight; 387 | this.$element.css({ 388 | paddingLeft: !this.bodyIsOverflowing && a ? this.scrollbarWidth : "", 389 | paddingRight: this.bodyIsOverflowing && !a ? this.scrollbarWidth : "" 390 | }) 391 | }, c.prototype.resetAdjustments = function () { 392 | this.$element.css({paddingLeft: "", paddingRight: ""}) 393 | }, c.prototype.checkScrollbar = function () { 394 | var a = window.innerWidth; 395 | if (!a) { 396 | var b = document.documentElement.getBoundingClientRect(); 397 | a = b.right - Math.abs(b.left) 398 | } 399 | this.bodyIsOverflowing = document.body.clientWidth < a, this.scrollbarWidth = this.measureScrollbar() 400 | }, c.prototype.setScrollbar = function () { 401 | var a = parseInt(this.$body.css("padding-right") || 0, 10); 402 | this.originalBodyPad = document.body.style.paddingRight || "", this.bodyIsOverflowing && this.$body.css("padding-right", a + this.scrollbarWidth) 403 | }, c.prototype.resetScrollbar = function () { 404 | this.$body.css("padding-right", this.originalBodyPad) 405 | }, c.prototype.measureScrollbar = function () { 406 | var a = document.createElement("div"); 407 | a.className = "modal-scrollbar-measure", this.$body.append(a); 408 | var b = a.offsetWidth - a.clientWidth; 409 | return this.$body[0].removeChild(a), b 410 | }; 411 | var d = a.fn.modal; 412 | a.fn.modal = b, a.fn.modal.Constructor = c, a.fn.modal.noConflict = function () { 413 | return a.fn.modal = d, this 414 | }, a(document).on("click.bs.modal.data-api", '[data-toggle="modal"]', function (c) { 415 | var d = a(this), e = d.attr("href"), f = a(d.attr("data-target") || e && e.replace(/.*(?=#[^\s]+$)/, "")), g = f.data("bs.modal") ? "toggle" : a.extend({remote: !/#/.test(e) && e}, f.data(), d.data()); 416 | d.is("a") && c.preventDefault(), f.one("show.bs.modal", function (a) { 417 | a.isDefaultPrevented() || f.one("hidden.bs.modal", function () { 418 | d.is(":visible") && d.trigger("focus") 419 | }) 420 | }), b.call(f, g, this) 421 | }) 422 | }(jQuery), +function (a) { 423 | "use strict"; 424 | function b(b) { 425 | return this.each(function () { 426 | var d = a(this), e = d.data("bs.tooltip"), f = "object" == typeof b && b; 427 | (e || !/destroy|hide/.test(b)) && (e || d.data("bs.tooltip", e = new c(this, f)), "string" == typeof b && e[b]()) 428 | }) 429 | } 430 | 431 | var c = function (a, b) { 432 | this.type = null, this.options = null, this.enabled = null, this.timeout = null, this.hoverState = null, this.$element = null, this.inState = null, this.init("tooltip", a, b) 433 | }; 434 | c.VERSION = "3.3.5", c.TRANSITION_DURATION = 150, c.DEFAULTS = { 435 | animation: !0, 436 | placement: "top", 437 | selector: !1, 438 | template: '', 439 | trigger: "hover focus", 440 | title: "", 441 | delay: 0, 442 | html: !1, 443 | container: !1, 444 | viewport: {selector: "body", padding: 0} 445 | }, c.prototype.init = function (b, c, d) { 446 | if (this.enabled = !0, this.type = b, this.$element = a(c), this.options = this.getOptions(d), this.$viewport = this.options.viewport && a(a.isFunction(this.options.viewport) ? this.options.viewport.call(this, this.$element) : this.options.viewport.selector || this.options.viewport), this.inState = { 447 | click: !1, 448 | hover: !1, 449 | focus: !1 450 | }, this.$element[0]instanceof document.constructor && !this.options.selector)throw new Error("`selector` option must be specified when initializing " + this.type + " on the window.document object!"); 451 | for (var e = this.options.trigger.split(" "), f = e.length; f--;) { 452 | var g = e[f]; 453 | if ("click" == g)this.$element.on("click." + this.type, this.options.selector, a.proxy(this.toggle, this)); else if ("manual" != g) { 454 | var h = "hover" == g ? "mouseenter" : "focusin", i = "hover" == g ? "mouseleave" : "focusout"; 455 | this.$element.on(h + "." + this.type, this.options.selector, a.proxy(this.enter, this)), this.$element.on(i + "." + this.type, this.options.selector, a.proxy(this.leave, this)) 456 | } 457 | } 458 | this.options.selector ? this._options = a.extend({}, this.options, { 459 | trigger: "manual", 460 | selector: "" 461 | }) : this.fixTitle() 462 | }, c.prototype.getDefaults = function () { 463 | return c.DEFAULTS 464 | }, c.prototype.getOptions = function (b) { 465 | return b = a.extend({}, this.getDefaults(), this.$element.data(), b), b.delay && "number" == typeof b.delay && (b.delay = { 466 | show: b.delay, 467 | hide: b.delay 468 | }), b 469 | }, c.prototype.getDelegateOptions = function () { 470 | var b = {}, c = this.getDefaults(); 471 | return this._options && a.each(this._options, function (a, d) { 472 | c[a] != d && (b[a] = d) 473 | }), b 474 | }, c.prototype.enter = function (b) { 475 | var c = b instanceof this.constructor ? b : a(b.currentTarget).data("bs." + this.type); 476 | return c || (c = new this.constructor(b.currentTarget, this.getDelegateOptions()), a(b.currentTarget).data("bs." + this.type, c)), b instanceof a.Event && (c.inState["focusin" == b.type ? "focus" : "hover"] = !0), c.tip().hasClass("in") || "in" == c.hoverState ? void(c.hoverState = "in") : (clearTimeout(c.timeout), c.hoverState = "in", c.options.delay && c.options.delay.show ? void(c.timeout = setTimeout(function () { 477 | "in" == c.hoverState && c.show() 478 | }, c.options.delay.show)) : c.show()) 479 | }, c.prototype.isInStateTrue = function () { 480 | for (var a in this.inState)if (this.inState[a])return !0; 481 | return !1 482 | }, c.prototype.leave = function (b) { 483 | var c = b instanceof this.constructor ? b : a(b.currentTarget).data("bs." + this.type); 484 | return c || (c = new this.constructor(b.currentTarget, this.getDelegateOptions()), a(b.currentTarget).data("bs." + this.type, c)), b instanceof a.Event && (c.inState["focusout" == b.type ? "focus" : "hover"] = !1), c.isInStateTrue() ? void 0 : (clearTimeout(c.timeout), c.hoverState = "out", c.options.delay && c.options.delay.hide ? void(c.timeout = setTimeout(function () { 485 | "out" == c.hoverState && c.hide() 486 | }, c.options.delay.hide)) : c.hide()) 487 | }, c.prototype.show = function () { 488 | var b = a.Event("show.bs." + this.type); 489 | if (this.hasContent() && this.enabled) { 490 | this.$element.trigger(b); 491 | var d = a.contains(this.$element[0].ownerDocument.documentElement, this.$element[0]); 492 | if (b.isDefaultPrevented() || !d)return; 493 | var e = this, f = this.tip(), g = this.getUID(this.type); 494 | this.setContent(), f.attr("id", g), this.$element.attr("aria-describedby", g), this.options.animation && f.addClass("fade"); 495 | var h = "function" == typeof this.options.placement ? this.options.placement.call(this, f[0], this.$element[0]) : this.options.placement, i = /\s?auto?\s?/i, j = i.test(h); 496 | j && (h = h.replace(i, "") || "top"), f.detach().css({ 497 | top: 0, 498 | left: 0, 499 | display: "block" 500 | }).addClass(h).data("bs." + this.type, this), this.options.container ? f.appendTo(this.options.container) : f.insertAfter(this.$element), this.$element.trigger("inserted.bs." + this.type); 501 | var k = this.getPosition(), l = f[0].offsetWidth, m = f[0].offsetHeight; 502 | if (j) { 503 | var n = h, o = this.getPosition(this.$viewport); 504 | h = "bottom" == h && k.bottom + m > o.bottom ? "top" : "top" == h && k.top - m < o.top ? "bottom" : "right" == h && k.right + l > o.width ? "left" : "left" == h && k.left - l < o.left ? "right" : h, f.removeClass(n).addClass(h) 505 | } 506 | var p = this.getCalculatedOffset(h, k, l, m); 507 | this.applyPlacement(p, h); 508 | var q = function () { 509 | var a = e.hoverState; 510 | e.$element.trigger("shown.bs." + e.type), e.hoverState = null, "out" == a && e.leave(e) 511 | }; 512 | a.support.transition && this.$tip.hasClass("fade") ? f.one("bsTransitionEnd", q).emulateTransitionEnd(c.TRANSITION_DURATION) : q() 513 | } 514 | }, c.prototype.applyPlacement = function (b, c) { 515 | var d = this.tip(), e = d[0].offsetWidth, f = d[0].offsetHeight, g = parseInt(d.css("margin-top"), 10), h = parseInt(d.css("margin-left"), 10); 516 | isNaN(g) && (g = 0), isNaN(h) && (h = 0), b.top += g, b.left += h, a.offset.setOffset(d[0], a.extend({ 517 | using: function (a) { 518 | d.css({top: Math.round(a.top), left: Math.round(a.left)}) 519 | } 520 | }, b), 0), d.addClass("in"); 521 | var i = d[0].offsetWidth, j = d[0].offsetHeight; 522 | "top" == c && j != f && (b.top = b.top + f - j); 523 | var k = this.getViewportAdjustedDelta(c, b, i, j); 524 | k.left ? b.left += k.left : b.top += k.top; 525 | var l = /top|bottom/.test(c), m = l ? 2 * k.left - e + i : 2 * k.top - f + j, n = l ? "offsetWidth" : "offsetHeight"; 526 | d.offset(b), this.replaceArrow(m, d[0][n], l) 527 | }, c.prototype.replaceArrow = function (a, b, c) { 528 | this.arrow().css(c ? "left" : "top", 50 * (1 - a / b) + "%").css(c ? "top" : "left", "") 529 | }, c.prototype.setContent = function () { 530 | var a = this.tip(), b = this.getTitle(); 531 | a.find(".tooltip-inner")[this.options.html ? "html" : "text"](b), a.removeClass("fade in top bottom left right") 532 | }, c.prototype.hide = function (b) { 533 | function d() { 534 | "in" != e.hoverState && f.detach(), e.$element.removeAttr("aria-describedby").trigger("hidden.bs." + e.type), b && b() 535 | } 536 | 537 | var e = this, f = a(this.$tip), g = a.Event("hide.bs." + this.type); 538 | return this.$element.trigger(g), g.isDefaultPrevented() ? void 0 : (f.removeClass("in"), a.support.transition && f.hasClass("fade") ? f.one("bsTransitionEnd", d).emulateTransitionEnd(c.TRANSITION_DURATION) : d(), this.hoverState = null, this) 539 | }, c.prototype.fixTitle = function () { 540 | var a = this.$element; 541 | (a.attr("title") || "string" != typeof a.attr("data-original-title")) && a.attr("data-original-title", a.attr("title") || "").attr("title", "") 542 | }, c.prototype.hasContent = function () { 543 | return this.getTitle() 544 | }, c.prototype.getPosition = function (b) { 545 | b = b || this.$element; 546 | var c = b[0], d = "BODY" == c.tagName, e = c.getBoundingClientRect(); 547 | null == e.width && (e = a.extend({}, e, {width: e.right - e.left, height: e.bottom - e.top})); 548 | var f = d ? { 549 | top: 0, 550 | left: 0 551 | } : b.offset(), g = {scroll: d ? document.documentElement.scrollTop || document.body.scrollTop : b.scrollTop()}, h = d ? { 552 | width: a(window).width(), 553 | height: a(window).height() 554 | } : null; 555 | return a.extend({}, e, g, h, f) 556 | }, c.prototype.getCalculatedOffset = function (a, b, c, d) { 557 | return "bottom" == a ? { 558 | top: b.top + b.height, 559 | left: b.left + b.width / 2 - c / 2 560 | } : "top" == a ? { 561 | top: b.top - d, 562 | left: b.left + b.width / 2 - c / 2 563 | } : "left" == a ? {top: b.top + b.height / 2 - d / 2, left: b.left - c} : { 564 | top: b.top + b.height / 2 - d / 2, 565 | left: b.left + b.width 566 | } 567 | }, c.prototype.getViewportAdjustedDelta = function (a, b, c, d) { 568 | var e = {top: 0, left: 0}; 569 | if (!this.$viewport)return e; 570 | var f = this.options.viewport && this.options.viewport.padding || 0, g = this.getPosition(this.$viewport); 571 | if (/right|left/.test(a)) { 572 | var h = b.top - f - g.scroll, i = b.top + f - g.scroll + d; 573 | h < g.top ? e.top = g.top - h : i > g.top + g.height && (e.top = g.top + g.height - i) 574 | } else { 575 | var j = b.left - f, k = b.left + f + c; 576 | j < g.left ? e.left = g.left - j : k > g.right && (e.left = g.left + g.width - k) 577 | } 578 | return e 579 | }, c.prototype.getTitle = function () { 580 | var a, b = this.$element, c = this.options; 581 | return a = b.attr("data-original-title") || ("function" == typeof c.title ? c.title.call(b[0]) : c.title) 582 | }, c.prototype.getUID = function (a) { 583 | do a += ~~(1e6 * Math.random()); while (document.getElementById(a)); 584 | return a 585 | }, c.prototype.tip = function () { 586 | if (!this.$tip && (this.$tip = a(this.options.template), 1 != this.$tip.length))throw new Error(this.type + " `template` option must consist of exactly 1 top-level element!"); 587 | return this.$tip 588 | }, c.prototype.arrow = function () { 589 | return this.$arrow = this.$arrow || this.tip().find(".tooltip-arrow") 590 | }, c.prototype.enable = function () { 591 | this.enabled = !0 592 | }, c.prototype.disable = function () { 593 | this.enabled = !1 594 | }, c.prototype.toggleEnabled = function () { 595 | this.enabled = !this.enabled 596 | }, c.prototype.toggle = function (b) { 597 | var c = this; 598 | b && (c = a(b.currentTarget).data("bs." + this.type), c || (c = new this.constructor(b.currentTarget, this.getDelegateOptions()), a(b.currentTarget).data("bs." + this.type, c))), b ? (c.inState.click = !c.inState.click, c.isInStateTrue() ? c.enter(c) : c.leave(c)) : c.tip().hasClass("in") ? c.leave(c) : c.enter(c) 599 | }, c.prototype.destroy = function () { 600 | var a = this; 601 | clearTimeout(this.timeout), this.hide(function () { 602 | a.$element.off("." + a.type).removeData("bs." + a.type), a.$tip && a.$tip.detach(), a.$tip = null, a.$arrow = null, a.$viewport = null 603 | }) 604 | }; 605 | var d = a.fn.tooltip; 606 | a.fn.tooltip = b, a.fn.tooltip.Constructor = c, a.fn.tooltip.noConflict = function () { 607 | return a.fn.tooltip = d, this 608 | } 609 | }(jQuery), +function (a) { 610 | "use strict"; 611 | function b(b) { 612 | return this.each(function () { 613 | var d = a(this), e = d.data("bs.popover"), f = "object" == typeof b && b; 614 | (e || !/destroy|hide/.test(b)) && (e || d.data("bs.popover", e = new c(this, f)), "string" == typeof b && e[b]()) 615 | }) 616 | } 617 | 618 | var c = function (a, b) { 619 | this.init("popover", a, b) 620 | }; 621 | if (!a.fn.tooltip)throw new Error("Popover requires tooltip.js"); 622 | c.VERSION = "3.3.5", c.DEFAULTS = a.extend({}, a.fn.tooltip.Constructor.DEFAULTS, { 623 | placement: "right", 624 | trigger: "click", 625 | content: "", 626 | template: '' 627 | }), c.prototype = a.extend({}, a.fn.tooltip.Constructor.prototype), c.prototype.constructor = c, c.prototype.getDefaults = function () { 628 | return c.DEFAULTS 629 | }, c.prototype.setContent = function () { 630 | var a = this.tip(), b = this.getTitle(), c = this.getContent(); 631 | a.find(".popover-title")[this.options.html ? "html" : "text"](b), a.find(".popover-content").children().detach().end()[this.options.html ? "string" == typeof c ? "html" : "append" : "text"](c), a.removeClass("fade top bottom left right in"), a.find(".popover-title").html() || a.find(".popover-title").hide() 632 | }, c.prototype.hasContent = function () { 633 | return this.getTitle() || this.getContent() 634 | }, c.prototype.getContent = function () { 635 | var a = this.$element, b = this.options; 636 | return a.attr("data-content") || ("function" == typeof b.content ? b.content.call(a[0]) : b.content) 637 | }, c.prototype.arrow = function () { 638 | return this.$arrow = this.$arrow || this.tip().find(".arrow") 639 | }; 640 | var d = a.fn.popover; 641 | a.fn.popover = b, a.fn.popover.Constructor = c, a.fn.popover.noConflict = function () { 642 | return a.fn.popover = d, this 643 | } 644 | }(jQuery), +function (a) { 645 | "use strict"; 646 | function b(c, d) { 647 | this.$body = a(document.body), this.$scrollElement = a(a(c).is(document.body) ? window : c), this.options = a.extend({}, b.DEFAULTS, d), this.selector = (this.options.target || "") + " .nav li > a", this.offsets = [], this.targets = [], this.activeTarget = null, this.scrollHeight = 0, this.$scrollElement.on("scroll.bs.scrollspy", a.proxy(this.process, this)), this.refresh(), this.process() 648 | } 649 | 650 | function c(c) { 651 | return this.each(function () { 652 | var d = a(this), e = d.data("bs.scrollspy"), f = "object" == typeof c && c; 653 | e || d.data("bs.scrollspy", e = new b(this, f)), "string" == typeof c && e[c]() 654 | }) 655 | } 656 | 657 | b.VERSION = "3.3.5", b.DEFAULTS = {offset: 10}, b.prototype.getScrollHeight = function () { 658 | return this.$scrollElement[0].scrollHeight || Math.max(this.$body[0].scrollHeight, document.documentElement.scrollHeight) 659 | }, b.prototype.refresh = function () { 660 | var b = this, c = "offset", d = 0; 661 | this.offsets = [], this.targets = [], this.scrollHeight = this.getScrollHeight(), a.isWindow(this.$scrollElement[0]) || (c = "position", d = this.$scrollElement.scrollTop()), this.$body.find(this.selector).map(function () { 662 | var b = a(this), e = b.data("target") || b.attr("href"), f = /^#./.test(e) && a(e); 663 | return f && f.length && f.is(":visible") && [[f[c]().top + d, e]] || null 664 | }).sort(function (a, b) { 665 | return a[0] - b[0] 666 | }).each(function () { 667 | b.offsets.push(this[0]), b.targets.push(this[1]) 668 | }) 669 | }, b.prototype.process = function () { 670 | var a, b = this.$scrollElement.scrollTop() + this.options.offset, c = this.getScrollHeight(), d = this.options.offset + c - this.$scrollElement.height(), e = this.offsets, f = this.targets, g = this.activeTarget; 671 | if (this.scrollHeight != c && this.refresh(), b >= d)return g != (a = f[f.length - 1]) && this.activate(a); 672 | if (g && b < e[0])return this.activeTarget = null, this.clear(); 673 | for (a = e.length; a--;)g != f[a] && b >= e[a] && (void 0 === e[a + 1] || b < e[a + 1]) && this.activate(f[a]) 674 | }, b.prototype.activate = function (b) { 675 | this.activeTarget = b, this.clear(); 676 | var c = this.selector + '[data-target="' + b + '"],' + this.selector + '[href="' + b + '"]', d = a(c).parents("li").addClass("active"); 677 | d.parent(".dropdown-menu").length && (d = d.closest("li.dropdown").addClass("active")), 678 | d.trigger("activate.bs.scrollspy") 679 | }, b.prototype.clear = function () { 680 | a(this.selector).parentsUntil(this.options.target, ".active").removeClass("active") 681 | }; 682 | var d = a.fn.scrollspy; 683 | a.fn.scrollspy = c, a.fn.scrollspy.Constructor = b, a.fn.scrollspy.noConflict = function () { 684 | return a.fn.scrollspy = d, this 685 | }, a(window).on("load.bs.scrollspy.data-api", function () { 686 | a('[data-spy="scroll"]').each(function () { 687 | var b = a(this); 688 | c.call(b, b.data()) 689 | }) 690 | }) 691 | }(jQuery), +function (a) { 692 | "use strict"; 693 | function b(b) { 694 | return this.each(function () { 695 | var d = a(this), e = d.data("bs.tab"); 696 | e || d.data("bs.tab", e = new c(this)), "string" == typeof b && e[b]() 697 | }) 698 | } 699 | 700 | var c = function (b) { 701 | this.element = a(b) 702 | }; 703 | c.VERSION = "3.3.5", c.TRANSITION_DURATION = 150, c.prototype.show = function () { 704 | var b = this.element, c = b.closest("ul:not(.dropdown-menu)"), d = b.data("target"); 705 | if (d || (d = b.attr("href"), d = d && d.replace(/.*(?=#[^\s]*$)/, "")), !b.parent("li").hasClass("active")) { 706 | var e = c.find(".active:last a"), f = a.Event("hide.bs.tab", {relatedTarget: b[0]}), g = a.Event("show.bs.tab", {relatedTarget: e[0]}); 707 | if (e.trigger(f), b.trigger(g), !g.isDefaultPrevented() && !f.isDefaultPrevented()) { 708 | var h = a(d); 709 | this.activate(b.closest("li"), c), this.activate(h, h.parent(), function () { 710 | e.trigger({type: "hidden.bs.tab", relatedTarget: b[0]}), b.trigger({ 711 | type: "shown.bs.tab", 712 | relatedTarget: e[0] 713 | }) 714 | }) 715 | } 716 | } 717 | }, c.prototype.activate = function (b, d, e) { 718 | function f() { 719 | g.removeClass("active").find("> .dropdown-menu > .active").removeClass("active").end().find('[data-toggle="tab"]').attr("aria-expanded", !1), b.addClass("active").find('[data-toggle="tab"]').attr("aria-expanded", !0), h ? (b[0].offsetWidth, b.addClass("in")) : b.removeClass("fade"), b.parent(".dropdown-menu").length && b.closest("li.dropdown").addClass("active").end().find('[data-toggle="tab"]').attr("aria-expanded", !0), e && e() 720 | } 721 | 722 | var g = d.find("> .active"), h = e && a.support.transition && (g.length && g.hasClass("fade") || !!d.find("> .fade").length); 723 | g.length && h ? g.one("bsTransitionEnd", f).emulateTransitionEnd(c.TRANSITION_DURATION) : f(), g.removeClass("in") 724 | }; 725 | var d = a.fn.tab; 726 | a.fn.tab = b, a.fn.tab.Constructor = c, a.fn.tab.noConflict = function () { 727 | return a.fn.tab = d, this 728 | }; 729 | var e = function (c) { 730 | c.preventDefault(), b.call(a(this), "show") 731 | }; 732 | a(document).on("click.bs.tab.data-api", '[data-toggle="tab"]', e).on("click.bs.tab.data-api", '[data-toggle="pill"]', e) 733 | }(jQuery), +function (a) { 734 | "use strict"; 735 | function b(b) { 736 | return this.each(function () { 737 | var d = a(this), e = d.data("bs.affix"), f = "object" == typeof b && b; 738 | e || d.data("bs.affix", e = new c(this, f)), "string" == typeof b && e[b]() 739 | }) 740 | } 741 | 742 | var c = function (b, d) { 743 | this.options = a.extend({}, c.DEFAULTS, d), this.$target = a(this.options.target).on("scroll.bs.affix.data-api", a.proxy(this.checkPosition, this)).on("click.bs.affix.data-api", a.proxy(this.checkPositionWithEventLoop, this)), this.$element = a(b), this.affixed = null, this.unpin = null, this.pinnedOffset = null, this.checkPosition() 744 | }; 745 | c.VERSION = "3.3.5", c.RESET = "affix affix-top affix-bottom", c.DEFAULTS = { 746 | offset: 0, 747 | target: window 748 | }, c.prototype.getState = function (a, b, c, d) { 749 | var e = this.$target.scrollTop(), f = this.$element.offset(), g = this.$target.height(); 750 | if (null != c && "top" == this.affixed)return c > e ? "top" : !1; 751 | if ("bottom" == this.affixed)return null != c ? e + this.unpin <= f.top ? !1 : "bottom" : a - d >= e + g ? !1 : "bottom"; 752 | var h = null == this.affixed, i = h ? e : f.top, j = h ? g : b; 753 | return null != c && c >= e ? "top" : null != d && i + j >= a - d ? "bottom" : !1 754 | }, c.prototype.getPinnedOffset = function () { 755 | if (this.pinnedOffset)return this.pinnedOffset; 756 | this.$element.removeClass(c.RESET).addClass("affix"); 757 | var a = this.$target.scrollTop(), b = this.$element.offset(); 758 | return this.pinnedOffset = b.top - a 759 | }, c.prototype.checkPositionWithEventLoop = function () { 760 | setTimeout(a.proxy(this.checkPosition, this), 1) 761 | }, c.prototype.checkPosition = function () { 762 | if (this.$element.is(":visible")) { 763 | var b = this.$element.height(), d = this.options.offset, e = d.top, f = d.bottom, g = Math.max(a(document).height(), a(document.body).height()); 764 | "object" != typeof d && (f = e = d), "function" == typeof e && (e = d.top(this.$element)), "function" == typeof f && (f = d.bottom(this.$element)); 765 | var h = this.getState(g, b, e, f); 766 | if (this.affixed != h) { 767 | null != this.unpin && this.$element.css("top", ""); 768 | var i = "affix" + (h ? "-" + h : ""), j = a.Event(i + ".bs.affix"); 769 | if (this.$element.trigger(j), j.isDefaultPrevented())return; 770 | this.affixed = h, this.unpin = "bottom" == h ? this.getPinnedOffset() : null, this.$element.removeClass(c.RESET).addClass(i).trigger(i.replace("affix", "affixed") + ".bs.affix") 771 | } 772 | "bottom" == h && this.$element.offset({top: g - b - f}) 773 | } 774 | }; 775 | var d = a.fn.affix; 776 | a.fn.affix = b, a.fn.affix.Constructor = c, a.fn.affix.noConflict = function () { 777 | return a.fn.affix = d, this 778 | }, a(window).on("load", function () { 779 | a('[data-spy="affix"]').each(function () { 780 | var c = a(this), d = c.data(); 781 | d.offset = d.offset || {}, null != d.offsetBottom && (d.offset.bottom = d.offsetBottom), null != d.offsetTop && (d.offset.top = d.offsetTop), b.call(c, d) 782 | }) 783 | }) 784 | }(jQuery); --------------------------------------------------------------------------------