diff --git a/src/main/java/uk/ac/cf/spring/demo/exception/ResourceNotFoundException.java b/src/main/java/uk/ac/cf/spring/demo/exception/ResourceNotFoundException.java new file mode 100644 index 0000000000000000000000000000000000000000..9593c9c187ddf5411ac674c727ab0b10c84ebaef --- /dev/null +++ b/src/main/java/uk/ac/cf/spring/demo/exception/ResourceNotFoundException.java @@ -0,0 +1,8 @@ +package uk.ac.cf.spring.demo.exception; + +public class ResourceNotFoundException extends RuntimeException { + public ResourceNotFoundException(String message) { + super(message); + } +} + diff --git a/src/main/java/uk/ac/cf/spring/demo/sports/Userdetail/User.java b/src/main/java/uk/ac/cf/spring/demo/sports/Userdetail/User.java deleted file mode 100644 index 340184f88eafafecbec47650f2a45598f2f119fb..0000000000000000000000000000000000000000 --- a/src/main/java/uk/ac/cf/spring/demo/sports/Userdetail/User.java +++ /dev/null @@ -1,13 +0,0 @@ -package uk.ac.cf.spring.demo.sports.Userdetail; - - -import lombok.AllArgsConstructor; -import lombok.Data; - -@Data -@AllArgsConstructor -public class User { - private Long id; - private String email; - private String username; -} \ No newline at end of file diff --git a/src/main/java/uk/ac/cf/spring/demo/sports/Userdetail/UserController.java b/src/main/java/uk/ac/cf/spring/demo/sports/Userdetail/UserController.java deleted file mode 100644 index b908c5565cc3ad3d4cec565f5c03f97949672920..0000000000000000000000000000000000000000 --- a/src/main/java/uk/ac/cf/spring/demo/sports/Userdetail/UserController.java +++ /dev/null @@ -1,63 +0,0 @@ -package uk.ac.cf.spring.demo.sports.Userdetail; - -import org.springframework.stereotype.Controller; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.PathVariable; -import org.springframework.web.servlet.ModelAndView; - -import java.util.List; - -@Controller -public class UserController { - // ä¾èµ–注入 UserService - private final UserService userService; - - public UserController(UserService userService) { - this.userService = userService; - } - - // 显示所有用户列表 - @GetMapping("/users") - public ModelAndView getUsers() { - ModelAndView modelAndView = new ModelAndView("user/allUserList"); - List<User> users = userService.getAllUsers(); - modelAndView.addObject("users", users); - return modelAndView; - } - - // 通过用户 ID 查看用户详情 - @GetMapping("/users/{id}") - public ModelAndView getUserById(@PathVariable Long id) { - ModelAndView modelAndView = new ModelAndView("user/userDetails"); - User user = userService.getUserById(id); - modelAndView.addObject("user", user); - return modelAndView; - } - - // æ˜¾ç¤ºæ·»åŠ ç”¨æˆ·çš„è¡¨å• - @GetMapping("/users/add") - public ModelAndView addUser() { - ModelAndView modelAndView = new ModelAndView("user/userForm"); - Userform emptyUserform = new Userform(); - modelAndView.addObject("user", emptyUserform); - return modelAndView; - } - - // 显示编辑用户信æ¯çš„è¡¨å• - @GetMapping("/users/edit/{id}") - public ModelAndView editUser(@PathVariable("id") Long id) { - ModelAndView modelAndView = new ModelAndView("user/userForm"); - // 获å–需è¦ç¼–辑的用户 - User userToEdit = userService.getUserById(id); - - // 创建表å•对象 - Userform userform = new Userform( - userToEdit.getId(), - userToEdit.getEmail(), - userToEdit.getUsername() - ); - - modelAndView.addObject("user", userform); - return modelAndView; - } -} \ No newline at end of file diff --git a/src/main/java/uk/ac/cf/spring/demo/sports/Userdetail/UserService.java b/src/main/java/uk/ac/cf/spring/demo/sports/Userdetail/UserService.java deleted file mode 100644 index 640518b30d217eb4fbc329e25af55424e4655cdd..0000000000000000000000000000000000000000 --- a/src/main/java/uk/ac/cf/spring/demo/sports/Userdetail/UserService.java +++ /dev/null @@ -1,21 +0,0 @@ -package uk.ac.cf.spring.demo.sports.Userdetail; - - -import java.util.List; - -public interface UserService { - // èŽ·å–æ‰€æœ‰ç”¨æˆ· - List<User> getAllUsers(); - - // 通过 ID 获å–用户 - User getUserById(Long id); - - // 通过用户å获å–用户 - List<User> getUsersByUsername(String username); - - // 通过邮箱获å–用户 - User getUserByEmail(String email); - - // æ·»åŠ ç”¨æˆ· - void addUser(User user); -} \ No newline at end of file diff --git a/src/main/java/uk/ac/cf/spring/demo/sports/Userdetail/UserServiceImpl.java b/src/main/java/uk/ac/cf/spring/demo/sports/Userdetail/UserServiceImpl.java deleted file mode 100644 index bfdae7761de7d41de3bc97ff6aec73ef5cc30a6e..0000000000000000000000000000000000000000 --- a/src/main/java/uk/ac/cf/spring/demo/sports/Userdetail/UserServiceImpl.java +++ /dev/null @@ -1,33 +0,0 @@ -package uk.ac.cf.spring.demo.sports.Userdetail; - -import org.springframework.stereotype.Service; - -import java.util.List; - -@Service -public class UserServiceImpl implements UserService{ - @Override - public List<User> getAllUsers() { - return null; - } - - @Override - public User getUserById(Long id) { - return null; - } - - @Override - public List<User> getUsersByUsername(String username) { - return null; - } - - @Override - public User getUserByEmail(String email) { - return null; - } - - @Override - public void addUser(User user) { - - } -} diff --git a/src/main/java/uk/ac/cf/spring/demo/sports/Userdetail/Userform.java b/src/main/java/uk/ac/cf/spring/demo/sports/Userdetail/Userform.java deleted file mode 100644 index 4fe5fd6ed70ca68e9800f1d9f6ca1e4f159c2085..0000000000000000000000000000000000000000 --- a/src/main/java/uk/ac/cf/spring/demo/sports/Userdetail/Userform.java +++ /dev/null @@ -1,17 +0,0 @@ -package uk.ac.cf.spring.demo.sports.Userdetail; - -import lombok.AllArgsConstructor; -import lombok.Data; - -@Data -@AllArgsConstructor -public class Userform { - private Long id; - private String email; - private String username; - - public Userform() { - - } -} - diff --git a/src/main/java/uk/ac/cf/spring/demo/sports/match/MatchController.java b/src/main/java/uk/ac/cf/spring/demo/sports/match/MatchController.java index 167b19df6fce765706dc4f6945e12407ee7f87ca..7187714d98bf0645345a1cc3befbd6055567acba 100644 --- a/src/main/java/uk/ac/cf/spring/demo/sports/match/MatchController.java +++ b/src/main/java/uk/ac/cf/spring/demo/sports/match/MatchController.java @@ -2,11 +2,8 @@ package uk.ac.cf.spring.demo.sports.match; import org.springframework.beans.factory.annotation.Autowired; //import org.springframework.stereotype.Controller; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.PathVariable; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RestController; -import org.springframework.web.servlet.ModelAndView; +import org.springframework.web.bind.annotation.*; +//import org.springframework.web.servlet.ModelAndView; import java.util.List; @RestController @@ -23,11 +20,33 @@ public class MatchController { public List<MatchItem> getAllMenuItems() { return matchService.getMatchItems(); } + // get Match by id @GetMapping("/{id}") public MatchItem getMenuItem(@PathVariable Long id) { return matchService.getMatchItemById(id); } -// @GetMapping("/match") + + // add + @PostMapping + public void addMatchItem(@RequestBody MatchItem matchItem) { + matchService.addMatchItem(matchItem); + } + + // update + @PutMapping("/{id}") + public void updateMatchItem(@PathVariable Long id, @RequestBody MatchItem matchItem) { + matchItem.setId(id); + matchService.updateMatchItem(matchItem); + } + + //delete + @DeleteMapping("/{id}") + public void deleteMatchItem(@PathVariable Long id) { + matchService.deleteMatchItem(id); + } + + + // @GetMapping("/match") // public ModelAndView getMatch() { // ModelAndView modelAndView = new ModelAndView("match/allMatchList"); // List<MatchItem> matchItems = matchService.getMatchItems(); @@ -41,39 +60,38 @@ public class MatchController { // MatchItem matchItem = matchService.getMatchItemById(id); // modelAndView.addObject("matchItem", matchItem); // return modelAndView; +// }//matchItemForm add match +// @GetMapping("/match/add") +// public ModelAndView addMatchItem() { +// ModelAndView modelAndView = new ModelAndView("match/matchItemForm"); +// MatchItemForm emptyMatchItem = new MatchItemForm(); +// modelAndView.addObject("matchItem", emptyMatchItem); +// return modelAndView; // } - //matchItemForm add match - @GetMapping("/match/add") - public ModelAndView addMatchItem() { - ModelAndView modelAndView = new ModelAndView("match/matchItemForm"); - MatchItemForm emptyMatchItem = new MatchItemForm(); - modelAndView.addObject("matchItem", emptyMatchItem); - return modelAndView; - } // - @GetMapping("/match/edit/{id}") - public ModelAndView editMatchItemTh(@PathVariable("id") Long id) { - ModelAndView modelAndView = new ModelAndView("match/matchItemForm"); - // construct matchToUpdate object to get matchItem by id - MatchItem matchToUpdate = matchService.getMatchItemById(id); - // Create the form object for the match - MatchItemForm matchItemForm = new MatchItemForm( - matchToUpdate.getId(), - matchToUpdate.getSport(), - matchToUpdate.getPlayerAId(), - matchToUpdate.getPlayerBId(), - matchToUpdate.getPlanTime(), - matchToUpdate.getStatus(), - matchToUpdate.getScoreA(), - matchToUpdate.getScoreB(), - matchToUpdate.getConfirmByA(), - matchToUpdate.getConfirmByB(), - matchToUpdate.getWinnerId() - ); - - modelAndView.addObject("matchItem", matchItemForm); - return modelAndView; - } +// @GetMapping("/match/edit/{id}") +// public ModelAndView editMatchItemTh(@PathVariable("id") Long id) { +// ModelAndView modelAndView = new ModelAndView("match/matchItemForm"); +// // construct matchToUpdate object to get matchItem by id +// MatchItem matchToUpdate = matchService.getMatchItemById(id); +// // Create the form object for the match +// MatchItemForm matchItemForm = new MatchItemForm( +// matchToUpdate.getId(), +// matchToUpdate.getSport(), +// matchToUpdate.getPlayerAId(), +// matchToUpdate.getPlayerBId(), +// matchToUpdate.getPlanTime(), +// matchToUpdate.getStatus(), +// matchToUpdate.getScoreA(), +// matchToUpdate.getScoreB(), +// matchToUpdate.getConfirmByA(), +// matchToUpdate.getConfirmByB(), +// matchToUpdate.getWinnerId() +// ); +// +// modelAndView.addObject("matchItem", matchItemForm); +// return modelAndView; +// } } diff --git a/src/main/java/uk/ac/cf/spring/demo/sports/match/MatchRepository.java b/src/main/java/uk/ac/cf/spring/demo/sports/match/MatchRepository.java index e25408f8bf46eb57b5b6a25e5a1ad04ed6352e49..cdce9b7b21fef6deebe0059a0648f9a000ec4415 100644 --- a/src/main/java/uk/ac/cf/spring/demo/sports/match/MatchRepository.java +++ b/src/main/java/uk/ac/cf/spring/demo/sports/match/MatchRepository.java @@ -11,5 +11,9 @@ public interface MatchRepository { List<MatchItem> getMatchItemBySport(String sport); // add MatchItem void addMatchItem(MatchItem matchItem); +// update MatchItem + void updateMatchItem(MatchItem matchItem); +// delete MatchItem + void deleteMatchItem(Long id); } diff --git a/src/main/java/uk/ac/cf/spring/demo/sports/match/MatchRepositoryImpl.java b/src/main/java/uk/ac/cf/spring/demo/sports/match/MatchRepositoryImpl.java index 03556e55369d344ef0c2afd62a2269e99a5686cd..ec8c33707ebfb0fbe4c0a0776915d32745ea44fe 100644 --- a/src/main/java/uk/ac/cf/spring/demo/sports/match/MatchRepositoryImpl.java +++ b/src/main/java/uk/ac/cf/spring/demo/sports/match/MatchRepositoryImpl.java @@ -9,7 +9,7 @@ import java.util.List; @Repository public class MatchRepositoryImpl implements MatchRepository{ // jdbcTemplate - dependency injection - private JdbcTemplate jdbc; + private final JdbcTemplate jdbc; private RowMapper<MatchItem> matchItemMapper; public MatchRepositoryImpl(JdbcTemplate aJdbc) { this.jdbc = aJdbc; @@ -70,4 +70,28 @@ public class MatchRepositoryImpl implements MatchRepository{ matchItem.getWinnerId() ); } + + @Override + public void updateMatchItem(MatchItem matchItem) { + String sql = "update match_item set sport = ?, player_AId = ?, player_BId = ?, plan_Time = ?, status = ?, score_a = ?, score_b = ?, confirm_a = ?, confirm_b = ?, winner_id = ? where id = ?"; + jdbc.update(sql, + matchItem.getSport(), + matchItem.getPlayerAId(), + matchItem.getPlayerBId(), + Timestamp.valueOf(matchItem.getPlanTime()), + matchItem.getStatus(), + matchItem.getScoreA(), + matchItem.getScoreB(), + matchItem.getConfirmByA(), + matchItem.getConfirmByB(), + matchItem.getWinnerId(), + matchItem.getId() + ); + } + + @Override + public void deleteMatchItem(Long id) { + String sql = "DELETE FROM match_item WHERE id = ?"; + jdbc.update(sql, id); + } } diff --git a/src/main/java/uk/ac/cf/spring/demo/sports/match/MatchService.java b/src/main/java/uk/ac/cf/spring/demo/sports/match/MatchService.java index 9b784ce149c64ad144f365af77a07c4a16037d81..bd0900e3b2dda196774ed77f2e6cd77276bcfda7 100644 --- a/src/main/java/uk/ac/cf/spring/demo/sports/match/MatchService.java +++ b/src/main/java/uk/ac/cf/spring/demo/sports/match/MatchService.java @@ -11,4 +11,8 @@ public interface MatchService { List<MatchItem> getMatchItemBySport(String sport); // add MatchItem void addMatchItem(MatchItem matchItem); + // update MatchItem + void updateMatchItem(MatchItem matchItem); + // delete MatchItem + void deleteMatchItem(Long id); } diff --git a/src/main/java/uk/ac/cf/spring/demo/sports/match/MatchServiceListImpl.java b/src/main/java/uk/ac/cf/spring/demo/sports/match/MatchServiceListImpl.java index 723d174b5062bd4815e92c776db5d5fc750ccc66..8c17f955a87680877d0aa9a74bc5535d4f501f0f 100644 --- a/src/main/java/uk/ac/cf/spring/demo/sports/match/MatchServiceListImpl.java +++ b/src/main/java/uk/ac/cf/spring/demo/sports/match/MatchServiceListImpl.java @@ -27,9 +27,28 @@ public class MatchServiceListImpl implements MatchService{ public List<MatchItem> getMatchItemBySport(String sport) { return matchRepository.getMatchItemBySport(sport); } - + // add @Override public void addMatchItem(MatchItem matchItem) { matchRepository.addMatchItem(matchItem); } + // update + @Override + public void updateMatchItem(MatchItem matchItem) { + if (matchItem.getId() == null || matchItem.getId() <= 0) { + throw new IllegalArgumentException("Invalid Match Item ID"); + } + MatchItem existingItem = matchRepository.getMatchItemById(matchItem.getId()); + if (existingItem != null) { + matchRepository.updateMatchItem(matchItem); + } else { + throw new IllegalArgumentException("Match item not found"); + } + + } + // delete + @Override + public void deleteMatchItem(Long id) { + matchRepository.deleteMatchItem(id); + } } diff --git a/src/main/java/uk/ac/cf/spring/demo/sports/rankingtable/RankingController.java b/src/main/java/uk/ac/cf/spring/demo/sports/rankingtable/RankingController.java new file mode 100644 index 0000000000000000000000000000000000000000..51b0dbb4d593a1d6d8aa744cc12a77a65eaaa06c --- /dev/null +++ b/src/main/java/uk/ac/cf/spring/demo/sports/rankingtable/RankingController.java @@ -0,0 +1,48 @@ +package uk.ac.cf.spring.demo.sports.rankingtable; + +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +@RestController +@RequestMapping("/rankings") // 公共路径 +public class RankingController { + private final RankingService rankingService; + + public RankingController(RankingService rankingService) { + this.rankingService = rankingService; + } + + /** + * get all rankings + * + * @return all rankings + */ + @GetMapping + public List<RankingTable> getAllRankings() { + return rankingService.getAllRankings(); + } + @GetMapping("/byallandsort") + public List<TotalWinsDTO> getRankingsByAllSort(String order) { + //rankingService.processCompletedMatches(); + return rankingService.getTotalWinsByAll(order); + + } + + /** + * get rankings filtered by sports and wins + * + * @return rankings + */ + @GetMapping("/bysportandsort") + public List<RankingTable> getRankingsBySportAndOrder( + @RequestParam(required = false) String sport, + @RequestParam(defaultValue = "desc") String order + ) { + //rankingService.processCompletedMatches(); + return rankingService.getRankingsBySportAndOrder(sport, order); + } + + + +} diff --git a/src/main/java/uk/ac/cf/spring/demo/sports/rankingtable/RankingRepository.java b/src/main/java/uk/ac/cf/spring/demo/sports/rankingtable/RankingRepository.java new file mode 100644 index 0000000000000000000000000000000000000000..884f5637d825fd96543d89ef23b96a02ba025b2c --- /dev/null +++ b/src/main/java/uk/ac/cf/spring/demo/sports/rankingtable/RankingRepository.java @@ -0,0 +1,133 @@ +package uk.ac.cf.spring.demo.sports.rankingtable; + +import lombok.Getter; +import org.springframework.jdbc.core.JdbcTemplate; +import org.springframework.jdbc.core.RowMapper; +import org.springframework.stereotype.Repository; + +import java.util.List; +@Repository + +public class RankingRepository { + public List<RankingTable> findBySportAndOrder(String sport, String order) { + // ç¡®ä¿ order 傿•°æ˜¯æœ‰æ•ˆå€¼ + if (!"asc".equalsIgnoreCase(order) && !"desc".equalsIgnoreCase(order)) { + order = "desc"; + } + + String sql = "SELECT * FROM ranking WHERE sport = ? ORDER BY wins " + order.toUpperCase(); + return jdbcTemplate.query(sql, mapRowToRanking(), sport); + } + + + @Getter + public static class MatchResult { + private final Long winnerId; + private final String sport; + + public MatchResult(Long winnerId, String sport) { + this.winnerId = winnerId; + this.sport = sport; + } + + } + + private final JdbcTemplate jdbcTemplate; + + + public RankingRepository(JdbcTemplate jdbcTemplate) { + this.jdbcTemplate = jdbcTemplate; + } + + // get all rankings sql code + public List<RankingTable> findAll() { + String sql = "SELECT * FROM ranking"; + return jdbcTemplate.query(sql, mapRowToRanking()); + } + + public List<RankingTable> findBySport(String sport) { + System.out.println(sport); + String sql = "SELECT * FROM ranking WHERE sport = ?"; + return jdbcTemplate.query(sql, mapRowToRanking(), sport); + } + + + // get ranking by userid and sport + public RankingTable findByUserIdAndSport(Long userId, String sport) { + String sql = "SELECT * FROM ranking WHERE user_id = ? AND sport = ?"; + return jdbcTemplate.queryForObject(sql, mapRowToRanking(), userId, sport); + } + + // update rankings + public void saveOrUpdate(Long userId, String sport, int newWins) { + String sql = """ + INSERT INTO ranking (user_id, sport, wins) + VALUES (?, ?, ?) + ON DUPLICATE KEY UPDATE wins = ? + """; + jdbcTemplate.update(sql, userId, sport, newWins, newWins); + } + + + // get result from match where status is completed + public List<MatchResult> findWinnersAndSportsFromCompletedMatches() { + String sql = """ + SELECT winner_id, sport + FROM match_item + WHERE status = 'completed' + """; + return jdbcTemplate.query(sql, (rs, rowNum) -> new MatchResult( + rs.getLong("winnerId"), + rs.getString("sport") + + )); + } + public List<RankingTable> findAllSortedByWins(String order) { + // ç¡®ä¿order傿•°çš„值是"asc"或"desc"ï¼Œå¦‚æžœä¸æ˜¯åˆ™é»˜è®¤ä¸º"desc" + if (order == null || (!order.equalsIgnoreCase("asc") && !order.equalsIgnoreCase("desc"))) { + order = "asc"; // 默认é™åº + } + + String sql = "SELECT * FROM ranking ORDER BY wins " + order.toUpperCase(); + return jdbcTemplate.query(sql, mapRowToRanking()); + } + //总胜利数查询 + public List<TotalWinsDTO> findTotalWinsByAll(String order) { + // éªŒè¯ order 傿•°æ˜¯å¦åˆæ³•,åªå…许 "asc" 或 "desc" + String sortOrder = "desc"; // 默认值 + if ("asc".equalsIgnoreCase(order)) { + sortOrder = "asc"; + } + + String sql = "SELECT user_id,username,'totalwins' AS sport, SUM(wins) AS wins FROM ranking GROUP BY user_id ORDER BY wins " + sortOrder; + + + return jdbcTemplate.query(sql, mapRowToTotalWinsDTO()); + } + + // a function can merge result into a ranking + private RowMapper<RankingTable> mapRowToRanking() { + return (rs, rowNum) -> { + RankingTable ranking = new RankingTable(); + ranking.setId(rs.getLong("id")); // æ˜ å°„ id + ranking.setUserId(rs.getLong("user_id")); // æ˜ å°„ user_id + ranking.setSport(rs.getString("sport")); // æ˜ å°„ sport + ranking.setWins(rs.getInt("wins")); // æ˜ å°„ wins + ranking.setUsername(rs.getString("username")); // æ˜ å°„ username + return ranking; + }; + } + //èšåˆæŸ¥è¯¢ä¸“ç”¨æ˜ å°„æ–¹æ³• + private RowMapper<TotalWinsDTO> mapRowToTotalWinsDTO() { + return (rs, rowNum) -> { + TotalWinsDTO dto = new TotalWinsDTO(); + dto.setUsername(rs.getString("username")); + dto.setUserId(rs.getLong("user_id")); + dto.setSport("totalwins"); // èšåˆæŸ¥è¯¢æ—¶ sport 固定为 "totalwins" + dto.setWins(rs.getInt("wins")); + return dto; + }; + } + +} + diff --git a/src/main/java/uk/ac/cf/spring/demo/sports/rankingtable/RankingService.java b/src/main/java/uk/ac/cf/spring/demo/sports/rankingtable/RankingService.java new file mode 100644 index 0000000000000000000000000000000000000000..9eacf144a6cb4bc211c818881e09fa789fa405b1 --- /dev/null +++ b/src/main/java/uk/ac/cf/spring/demo/sports/rankingtable/RankingService.java @@ -0,0 +1,28 @@ +package uk.ac.cf.spring.demo.sports.rankingtable; + +import org.springframework.stereotype.Service; + +import java.util.List; +@Service + +public interface RankingService { + + List<RankingTable> getAllRankings(); + + + List<RankingTable> getRankingsBySport(String sport); + + + void updateRanking(Long userId, String sport, int wins); + + + void processCompletedMatches(); + + List<RankingTable> getAllRankingsSortedByWins(String order); + + List<RankingTable> getRankingsBySportAndOrder(String sport, String order); + + List<TotalWinsDTO> getTotalWinsByAll(String order); + + +} diff --git a/src/main/java/uk/ac/cf/spring/demo/sports/rankingtable/RankingServiceImpl.java b/src/main/java/uk/ac/cf/spring/demo/sports/rankingtable/RankingServiceImpl.java new file mode 100644 index 0000000000000000000000000000000000000000..5f352391d40b2050a2878b0442538d623ad2156b --- /dev/null +++ b/src/main/java/uk/ac/cf/spring/demo/sports/rankingtable/RankingServiceImpl.java @@ -0,0 +1,72 @@ +package uk.ac.cf.spring.demo.sports.rankingtable; + +import org.springframework.stereotype.Service; + +import java.util.List; + +@Service +public class RankingServiceImpl implements RankingService { + private final RankingRepository rankingRepository; + + public RankingServiceImpl(RankingRepository rankingRepository) { + this.rankingRepository = rankingRepository; + } + + + @Override + public List<RankingTable> getAllRankings() { + return rankingRepository.findAll(); + } + + + @Override + public List<RankingTable> getRankingsBySport(String sport) { + return rankingRepository.findBySport(sport); + } + + + @Override + public void updateRanking(Long userId, String sport, int wins) { + rankingRepository.saveOrUpdate(userId, sport, wins); + } + + + @Override + public void processCompletedMatches() { + + List<RankingRepository.MatchResult> completedMatches = rankingRepository.findWinnersAndSportsFromCompletedMatches(); + + // loop every match result + for (RankingRepository.MatchResult result : completedMatches) { + Long winnerId = result.getWinnerId(); + String sport = result.getSport(); + + // current user wins in particular sport + RankingTable currentRanking = rankingRepository.findByUserIdAndSport(winnerId, sport); + int currentWins = currentRanking != null ? currentRanking.getWins() : 0; + + // update + rankingRepository.saveOrUpdate(winnerId, sport, currentWins + 1); + } + } + @Override + public List<RankingTable> getAllRankingsSortedByWins(String order) { + return rankingRepository.findAllSortedByWins(order); + } + + @Override + public List<RankingTable> getRankingsBySportAndOrder(String sport, String order) { + if (sport == null || sport.equalsIgnoreCase("all")) { + // 如果没有选择特定è¿åŠ¨ï¼Œåˆ™æŒ‰æ€»èƒœåˆ©æ•°æŽ’åº + return rankingRepository.findAllSortedByWins(order); + } else { + // 按特定è¿åŠ¨å’ŒæŽ’åºæ–¹å¼ç›é€‰ + return rankingRepository.findBySportAndOrder(sport, order); + } + } + public List<TotalWinsDTO> getTotalWinsByAll(String order) { + return rankingRepository.findTotalWinsByAll(order); + } + +} + diff --git a/src/main/java/uk/ac/cf/spring/demo/sports/rankingtable/RankingTable.java b/src/main/java/uk/ac/cf/spring/demo/sports/rankingtable/RankingTable.java new file mode 100644 index 0000000000000000000000000000000000000000..f483afac15c65f4f23af743f9b9574b68ecb38cf --- /dev/null +++ b/src/main/java/uk/ac/cf/spring/demo/sports/rankingtable/RankingTable.java @@ -0,0 +1,17 @@ +package uk.ac.cf.spring.demo.sports.rankingtable; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; +@Data +@NoArgsConstructor + +public class RankingTable { + //create entity class + private Long id; // + private Long userId; // 用户 ID + private int wins; // 获胜场次 + private String sport; + private String username; + +} + diff --git a/src/main/java/uk/ac/cf/spring/demo/sports/rankingtable/TotalWinsDTO.java b/src/main/java/uk/ac/cf/spring/demo/sports/rankingtable/TotalWinsDTO.java new file mode 100644 index 0000000000000000000000000000000000000000..2107882393bd1837776695ff41475fdea3a11927 --- /dev/null +++ b/src/main/java/uk/ac/cf/spring/demo/sports/rankingtable/TotalWinsDTO.java @@ -0,0 +1,12 @@ +package uk.ac.cf.spring.demo.sports.rankingtable; +import lombok.Data; +import lombok.NoArgsConstructor; +@Data +@NoArgsConstructor + +public class TotalWinsDTO { + private Long userId; + private String sport; + private Integer wins; + private String username; +} diff --git a/src/main/java/uk/ac/cf/spring/demo/sports/security/SecurityConfig.java b/src/main/java/uk/ac/cf/spring/demo/sports/security/SecurityConfig.java index a9cbb8d53139455a824eb60b36328397fb7ff999..10a8e80dedb69bb13ea24da5ff55cbf1fa1189ff 100644 --- a/src/main/java/uk/ac/cf/spring/demo/sports/security/SecurityConfig.java +++ b/src/main/java/uk/ac/cf/spring/demo/sports/security/SecurityConfig.java @@ -11,34 +11,72 @@ import org.springframework.security.provisioning.InMemoryUserDetailsManager; import org.springframework.security.web.SecurityFilterChain; import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; +import static org.springframework.security.config.Customizer.withDefaults; + @Configuration @EnableWebSecurity public class SecurityConfig { public static final String[] ENDPOINTS_WHITELIST = { - "/images/**", - "/", - "/api/**", - "/html/**", - "/order/**", - "/register", // Make sure the sign-up page isn't redirected - "/login" // Make sure the login page isn't redirected + "/pics/**", + "/css/**", // .CSS Style Files + "/js/**", // .JS Files + "/images/**", // images + "/", // root path + "/match", // match data path + "/match/**", + "/users", // user data path + "/users/**", + "/api/**", // API + "/html/**", // static HTML + "/html/register.html", // registration path + "/html/login.html", // login + "/html/table-tennisrules.html", + "/rankings", //ranking data path + "/rankings/**", + "/html/table-tennisrules.html" + }; + + public static final String[] ADMIN_WHITELIST = { + "/admin/**", }; @Bean public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { http + // Configure access rights for paths .authorizeHttpRequests(request -> request - .requestMatchers(ENDPOINTS_WHITELIST).permitAll() // å…许白åå•页é¢ä¸éœ€è¦è®¤è¯ - .anyRequest().authenticated() // 其他页é¢éœ€è¦è®¤è¯ + .requestMatchers(ENDPOINTS_WHITELIST).permitAll() // Allow whitelisted pages without authentication + .requestMatchers("/users/current").permitAll() + .requestMatchers("/html/backGroundMatch.html").hasRole("ADMIN") // Only users with the ADMIN role are allowed to access + .anyRequest().authenticated() // Other paths require authentication ) + + // Disable CSRF (can be enabled on demand) .csrf().disable() + + // Forms Login Configuration .formLogin(form -> form - .loginPage("/login").permitAll() // å®šä¹‰è‡ªå®šä¹‰ç™»å½•é¡µé¢ + .loginPage("/html/login.html").permitAll() // Using a customized login page + .defaultSuccessUrl("/html/matchSchedule.html", true) // After successful login, you will be redirected to this page + .failureUrl("/html/login.html?error=true") // You will be redirected to this page after a failed login + ) + + // Basic authentication configuration (for API access) + .httpBasic(withDefaults()) // Basic Auth is supported for API calls + + // Logout Configuration + .logout(logout -> logout + .logoutUrl("/logout") // Set the logout path + .logoutSuccessUrl("/html/login.html") // The path to which you are redirected after the logout is successful + .invalidateHttpSession(true) // Invalidate the session + .clearAuthentication(true) // Clear authentication information ); return http.build(); } + + @Bean public UserDetailsService userDetailsService() { UserDetails user = @@ -52,7 +90,7 @@ public class SecurityConfig { @Bean public BCryptPasswordEncoder passwordEncoder() { - return new BCryptPasswordEncoder(); + return new BCryptPasswordEncoder(); // Use BCrypt for password encryption } } diff --git a/src/main/java/uk/ac/cf/spring/demo/sports/security/SecurityUtils.java b/src/main/java/uk/ac/cf/spring/demo/sports/security/SecurityUtils.java new file mode 100644 index 0000000000000000000000000000000000000000..2fff4a26178f8336eb9175b5436c92ffac94935e --- /dev/null +++ b/src/main/java/uk/ac/cf/spring/demo/sports/security/SecurityUtils.java @@ -0,0 +1,48 @@ +package uk.ac.cf.spring.demo.util; + +import org.springframework.security.core.Authentication; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.security.core.authority.SimpleGrantedAuthority; +import uk.ac.cf.spring.demo.user.User; +import uk.ac.cf.spring.demo.user.UserRepository; +import org.springframework.beans.factory.annotation.Autowired; +import java.util.Optional; + +public class SecurityUtils { + + @Autowired + private UserRepository userRepository; + + // 获å–当å‰è®¤è¯å¯¹è±¡ + public static Authentication getAuthentication() { + return SecurityContextHolder.getContext().getAuthentication(); + } + + // 检查当å‰ç”¨æˆ·æ˜¯å¦ä¸ºç®¡ç†å‘˜ + public static boolean isAdmin() { + Authentication authentication = getAuthentication(); + return authentication.getAuthorities().stream() + .anyMatch(authority -> authority.getAuthority().equals("ROLE_ADMIN")); + } + + // 获å–当å‰ç”¨æˆ·çš„角色 + public static String getCurrentUserRole() { + Authentication authentication = getAuthentication(); + return authentication.getAuthorities().stream() + .map(authority -> authority.getAuthority()) + .findFirst() + .orElse("ROLE_USER"); + } + + // 获å–当å‰ç”¨æˆ·å + public static String getCurrentUsername() { + Authentication authentication = getAuthentication(); + return authentication.getName(); + } + + // 从数æ®åº“ä¸åŠ è½½å½“å‰ç”¨æˆ·è¯¦ç»†ä¿¡æ¯ + public Optional<User> getCurrentUserDetails() { + String username = getCurrentUsername(); + return userRepository.findByUsername(username); + } +} diff --git a/src/main/java/uk/ac/cf/spring/demo/user/CustomUserDetailsService.java b/src/main/java/uk/ac/cf/spring/demo/user/CustomUserDetailsService.java new file mode 100644 index 0000000000000000000000000000000000000000..0455ac126d18cfe586b299c37a72ab7026ee1bdd --- /dev/null +++ b/src/main/java/uk/ac/cf/spring/demo/user/CustomUserDetailsService.java @@ -0,0 +1,35 @@ +package uk.ac.cf.spring.demo.user; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.core.GrantedAuthority; +import org.springframework.security.core.authority.SimpleGrantedAuthority; +import org.springframework.security.core.userdetails.UserDetails; +import org.springframework.security.core.userdetails.UserDetailsService; +import org.springframework.security.core.userdetails.UsernameNotFoundException; +import org.springframework.stereotype.Service; + +import java.util.List; + +@Service +public class CustomUserDetailsService implements UserDetailsService { + + @Autowired + private UserRepository userRepository; + + @Override + public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { + User user = userRepository.findByUsername(username) + .orElseThrow(() -> new UsernameNotFoundException("User not found")); + + // 将用户的角色转æ¢ä¸º GrantedAuthority + List<GrantedAuthority> authorities = List.of(new SimpleGrantedAuthority("ROLE_" + user.getRole())); + + // 返回 UserDetails 对象 + return new org.springframework.security.core.userdetails.User( + user.getUsername(), + user.getPassword(), + authorities + ); + } +} + diff --git a/src/main/java/uk/ac/cf/spring/demo/user/UserRepository.java b/src/main/java/uk/ac/cf/spring/demo/user/UserRepository.java index b3fbd2f9bbb35466a35e07382c1e9ace7e2e8613..2d0c05a6782cedb994dfa065c36be8036403ac2c 100644 --- a/src/main/java/uk/ac/cf/spring/demo/user/UserRepository.java +++ b/src/main/java/uk/ac/cf/spring/demo/user/UserRepository.java @@ -3,6 +3,8 @@ package uk.ac.cf.spring.demo.user; import org.springframework.stereotype.Repository; import java.sql.*; +import java.util.ArrayList; +import java.util.List; import java.util.Optional; @Repository @@ -12,6 +14,39 @@ public class UserRepository { private static final String USERNAME = "root"; private static final String PASSWORD = "comsc"; + public List<User> findAll() { + String sql = "SELECT * FROM information"; + List<User> users = new ArrayList<>(); + try (Connection conn = DriverManager.getConnection(URL, USERNAME, PASSWORD); + PreparedStatement ps = conn.prepareStatement(sql); + ResultSet rs = ps.executeQuery()) { + + while (rs.next()) { + User user = new User(); + user.setId(rs.getInt("id")); + user.setUsername(rs.getString("username")); + user.setEmail(rs.getString("email")); + user.setPassword(rs.getString("password")); + user.setRole(rs.getString("role")); + users.add(user); + } + } catch (SQLException e) { + e.printStackTrace(); + } + return users; + } + + public void delete(User user) { + String sql = "DELETE FROM information WHERE id = ?"; + try (Connection conn = DriverManager.getConnection(URL, USERNAME, PASSWORD); + PreparedStatement ps = conn.prepareStatement(sql)) { + ps.setInt(1, user.getId()); + ps.executeUpdate(); + } catch (SQLException e) { + e.printStackTrace(); + } + } + // æ£€æŸ¥ç”¨æˆ·åæ˜¯å¦å˜åœ¨ public boolean existsByUsername(String username) { String sql = "SELECT COUNT(*) FROM information WHERE username = ?"; @@ -47,7 +82,7 @@ public class UserRepository { } // æ’入用户 - public void save(User user) { + public User save(User user) { String sql = "INSERT INTO information (username, email, password, role) VALUES (?, ?, ?, ?)"; try (Connection conn = DriverManager.getConnection(URL, USERNAME, PASSWORD); PreparedStatement ps = conn.prepareStatement(sql)) { @@ -59,8 +94,32 @@ public class UserRepository { } catch (SQLException e) { e.printStackTrace(); } + return user; } + public Optional<User> findById(int id) { + String sql = "SELECT * FROM information WHERE id = ?"; + try (Connection conn = DriverManager.getConnection(URL, USERNAME, PASSWORD); + PreparedStatement ps = conn.prepareStatement(sql)) { + ps.setInt(1, id); + try (ResultSet rs = ps.executeQuery()) { + if (rs.next()) { + User user = new User(); + user.setId(rs.getInt("id")); + user.setUsername(rs.getString("username")); + user.setEmail(rs.getString("email")); + user.setPassword(rs.getString("password")); + user.setRole(rs.getString("role")); + return Optional.of(user); + } + } + } catch (SQLException e) { + e.printStackTrace(); + } + return Optional.empty(); + } + + // æ ¹æ®é‚®ç®±æŸ¥æ‰¾ç”¨æˆ· public Optional<User> findByEmail(String email) { String sql = "SELECT * FROM information WHERE email = ?"; @@ -83,5 +142,28 @@ public class UserRepository { } return Optional.empty(); } + + public Optional<User> findByUsername(String username) { + String sql = "SELECT * FROM information WHERE username = ?"; + try (Connection conn = DriverManager.getConnection(URL, USERNAME, PASSWORD); + PreparedStatement ps = conn.prepareStatement(sql)) { + ps.setString(1, username); + try (ResultSet rs = ps.executeQuery()) { + if (rs.next()) { + User user = new User(); + user.setId(rs.getInt("id")); + user.setUsername(rs.getString("username")); + user.setEmail(rs.getString("email")); + user.setPassword(rs.getString("password")); + user.setRole(rs.getString("role")); + return Optional.of(user); + } + } + } catch (SQLException e) { + e.printStackTrace(); + } + return Optional.empty(); + } + } diff --git a/src/main/java/uk/ac/cf/spring/demo/user/controller/LoginController.java b/src/main/java/uk/ac/cf/spring/demo/user/controller/LoginController.java index e3c00644a581c39c881b9cbb87fdf0894bb77f8a..8cc779cf024dd395a8d8fc896bb3dc205214b9e5 100644 --- a/src/main/java/uk/ac/cf/spring/demo/user/controller/LoginController.java +++ b/src/main/java/uk/ac/cf/spring/demo/user/controller/LoginController.java @@ -1,14 +1,21 @@ package uk.ac.cf.spring.demo.user.controller; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; +import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.web.bind.annotation.*; import uk.ac.cf.spring.demo.user.User; import uk.ac.cf.spring.demo.user.UserRepository; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.GetMapping; + +import java.util.Map; import java.util.Optional; import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; +import static uk.ac.cf.spring.demo.util.SecurityUtils.getCurrentUserRole; @Controller public class LoginController { @@ -22,7 +29,7 @@ public class LoginController { // Display the login page @GetMapping("/login") public String showLoginForm() { - return "page/login"; // return login.html + return "html/login.html"; // return login.html } // Login User @@ -46,5 +53,36 @@ public class LoginController { // If the password is verified return ResponseEntity.ok("{\"message\": \"Login successful\"}"); } + + @GetMapping("/html/backGroundMatch.html") + public ResponseEntity<String> accessRestrictedPage() { + if (uk.ac.cf.spring.demo.util.SecurityUtils.isAdmin()) { + return ResponseEntity.ok("Welcome, Admin!"); + } else { + return ResponseEntity.status(HttpStatus.FORBIDDEN).body("Access Denied"); + } + } + + @GetMapping("/users/current") + public ResponseEntity<?> getCurrentUser() { + String username = uk.ac.cf.spring.demo.util.SecurityUtils.getCurrentUsername(); + if (username == null) { + return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body("{\"message\": \"User not logged in\"}"); + } + + // Return user information + return ResponseEntity.ok(Map.of( + "username", username + )); + } + + + +// @GetMapping("/logout") +// public ResponseEntity<?> logoutUser(HttpServletRequest request, HttpServletResponse response) { +// SecurityContextHolder.clearContext(); // 清除用户认è¯ä¿¡æ¯ +// request.getSession().invalidate(); // 销æ¯ä¼šè¯ +// return ResponseEntity.ok("Logged out successfully"); +// } } diff --git a/src/main/java/uk/ac/cf/spring/demo/user/controller/UserController.java b/src/main/java/uk/ac/cf/spring/demo/user/controller/UserController.java new file mode 100644 index 0000000000000000000000000000000000000000..20fba0d17bfc552762947a2c6fb74e1eca815a3d --- /dev/null +++ b/src/main/java/uk/ac/cf/spring/demo/user/controller/UserController.java @@ -0,0 +1,58 @@ +package uk.ac.cf.spring.demo.user.controller; + +import uk.ac.cf.spring.demo.exception.ResourceNotFoundException; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; +import uk.ac.cf.spring.demo.user.User; +import uk.ac.cf.spring.demo.user.*; + +import java.util.List; + +@RestController +@RequestMapping("/users") +public class UserController { + + @Autowired + private UserRepository userRepository; + + @GetMapping + public List<User> getAllUsers() { + return userRepository.findAll(); + } + + @GetMapping("/{id}") + public User getUserById(@PathVariable Integer id) { + return userRepository.findById(id) + .orElseThrow(() -> new ResourceNotFoundException("User not found with ID: " + id)); + } + + @PostMapping + public User createUser(@RequestBody User user) { + return userRepository.save(user); + } + + @PutMapping("/{id}") + public User updateUser(@PathVariable Integer id, @RequestBody User userDetails) { + User user = userRepository.findById(id) + .orElseThrow(() -> new ResourceNotFoundException("User not found with ID: " + id)); + + user.setUsername(userDetails.getUsername()); + user.setEmail(userDetails.getEmail()); + user.setPassword(userDetails.getPassword()); + user.setRole(userDetails.getRole()); + + return userRepository.save(user); + } + + @DeleteMapping("/{id}") + public ResponseEntity<?> deleteUser(@PathVariable Integer id) { + User user = userRepository.findById(id) + .orElseThrow(() -> new ResourceNotFoundException("User not found with ID: " + id)); + + userRepository.delete(user); + return ResponseEntity.ok().build(); + } + + +} diff --git a/src/main/resources/data.sql b/src/main/resources/data.sql index a972476b4317082773551cdcacb0cec991ddce5c..20f0049e108b141d5cabd5e5a74c4afb4ee87063 100644 --- a/src/main/resources/data.sql +++ b/src/main/resources/data.sql @@ -1,4 +1,5 @@ delete from match_item; +delete from ranking; insert into match_item (sport, player_AId, player_BId, plan_Time, status, score_a, score_b, confirm_a, confirm_b, winner_id) values -- test data @@ -14,3 +15,9 @@ values ('Darts', 1, 2, '2024-11-29 15:30:00', 'completed', 300, 280, TRUE, TRUE, 2), -- 6: Confirmed status, Player 1 wins ('TableTennis', 1, 2, '2024-11-30 17:00:00', 'confirmed', 3, 1, TRUE, TRUE, 1); +INSERT INTO ranking (user_id,username,sport, wins) VALUES + (1, 'xzc','Pools', 5), + (2, 'shy','Darts', 3), + (3, 'xx','TableTennis', 10), + (2, 'shy','TableTennis', 4); + diff --git a/src/main/resources/schema.sql b/src/main/resources/schema.sql index c6fd7c57bba5ba618181214d0c503c2a75ea4fbb..79e19efcafd5f442a7381d58c0f239973ea5b207 100644 --- a/src/main/resources/schema.sql +++ b/src/main/resources/schema.sql @@ -1,4 +1,5 @@ drop table if exists match_item; +drop table if exists rankings; create table if not exists match_item ( -- Match ID id BIGINT AUTO_INCREMENT PRIMARY KEY, @@ -17,11 +18,29 @@ create table if not exists match_item ( confirm_b BOOLEAN DEFAULT FALSE, winner_id BIGINT DEFAULT NULL ); - +drop table if exists information; CREATE TABLE information ( id INT AUTO_INCREMENT PRIMARY KEY, username VARCHAR(50) UNIQUE NOT NULL, email VARCHAR(50) UNIQUE NOT NULL, password VARCHAR(255) NOT NULL, role VARCHAR(20) DEFAULT 'USER' NOT NULL -); \ No newline at end of file +); +drop table if exists ranking; +CREATE TABLE IF NOT EXISTS ranking ( + id INT AUTO_INCREMENT PRIMARY KEY, + user_id INT NOT NULL, + sport ENUM('Pools', 'Darts', 'TableTennis') NOT NULL, + wins INT DEFAULT 0, + username VARCHAR(255)DEFAULT NULL + + + + +); +CREATE TABLE images ( + id INT AUTO_INCREMENT PRIMARY KEY, + filename VARCHAR(255) NOT NULL, -- Image file name or path + uploader VARCHAR(100) NOT NULL, -- Uploader Name + upload_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP -- Upload time +); diff --git a/src/main/resources/static/css/UserCenter.css b/src/main/resources/static/css/UserCenter.css new file mode 100644 index 0000000000000000000000000000000000000000..effebd9fb9fd169470b2b6209c4aa50bfabf8228 --- /dev/null +++ b/src/main/resources/static/css/UserCenter.css @@ -0,0 +1,10 @@ +/* 顶部区域 */ +.header { + background-color: gray; /* 背景颜色为ç°è‰² */ + color: white; /* å—体颜色为白色 */ + text-align: center; /* æ–‡å—å±…ä¸å¯¹é½ */ + padding: 20px 0; /* 上下内边è·ï¼Œå¢žå¤§é«˜åº¦ */ + margin: 0; /* åŽ»æŽ‰é»˜è®¤çš„å¤–è¾¹è· */ + font-size: 24px; /* å—ä½“å¤§å° */ + font-weight: bold; /* å—ä½“åŠ ç²— */ +} \ No newline at end of file diff --git a/src/main/resources/static/css/backGroundMatch.css b/src/main/resources/static/css/backGroundMatch.css new file mode 100644 index 0000000000000000000000000000000000000000..8e40e3b8970e757b6914c9eb8b94ae2915a08468 --- /dev/null +++ b/src/main/resources/static/css/backGroundMatch.css @@ -0,0 +1,46 @@ +* { + margin: 0; + padding: 0; + box-sizing: border-box; +} +h1, h2 { + color: darkblue; +} +#matchForm { + /*display: grid;*/ + align-items: center; +} +form input, form button { + margin: 5px; + padding: 10px; + font-size: 14px; + +} + +table { + width: 100%; + border-collapse: collapse; + margin-top: 20px; + border: 1px solid white; +} + +th, td { + padding: 10px; + text-align: left; + border: 1px solid white; +} + +button { + background-color: dodgerblue; + color: white; + border: none; + border-radius: 10px; + cursor: pointer; + margin: 5px; + padding: 10px; + width: 60px; +} + +button:hover { + background-color: cornflowerblue; +} diff --git a/src/main/resources/static/css/backGroundUser.css b/src/main/resources/static/css/backGroundUser.css new file mode 100644 index 0000000000000000000000000000000000000000..d94c9908a43447074b1bf7df4266e24e60144d32 --- /dev/null +++ b/src/main/resources/static/css/backGroundUser.css @@ -0,0 +1,42 @@ +body { + font-family: Gill Sans MT, sans-serif; + margin: 20px; + padding: 0; + background-color: #f4f4f4; +} + +h1, h2 { + text-align: center; +} + +table { + width: 100%; + border-collapse: collapse; + margin: 20px 0; +} + +th, td { + border: 1px solid #ddd; + padding: 8px; + text-align: center; +} + +th { + background-color: #f4f4f4; +} + +button { + padding: 5px 10px; + margin: 2px; + border: none; + border-radius: 3px; + cursor: pointer; +} + +button:hover { + background-color: #ddd; +} + +button:active { + background-color: #ccc; +} \ No newline at end of file diff --git a/src/main/resources/static/css/dartsrules.css b/src/main/resources/static/css/dartsrules.css new file mode 100644 index 0000000000000000000000000000000000000000..b813eebb6bba912e9cd5c116ff4a2d88f4823c73 --- /dev/null +++ b/src/main/resources/static/css/dartsrules.css @@ -0,0 +1,26 @@ + +main { + max-width: 800px; + margin: 30px auto; + background: white; + padding: 20px; + border-radius: 10px; + box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1); + font-family: Arial, sans-serif; + color: #333; +} +h1 { + text-align: center; + color: #444; + margin-bottom: 20px; +} +ol { + padding-left: 20px; + line-height: 1.8; +} +ol li { + margin-bottom: 15px; +} +strong { + color: #222; +} diff --git a/src/main/resources/static/css/footer.css b/src/main/resources/static/css/footer.css new file mode 100644 index 0000000000000000000000000000000000000000..c4616f08f9410f2f4966569a942f600258905f4e --- /dev/null +++ b/src/main/resources/static/css/footer.css @@ -0,0 +1,122 @@ +* { + margin: 0; + padding: 0; + box-sizing: border-box; +} + +.footer { + /*position: fixed;*/ + /*bottom: 0;*/ + /*left: 0;*/ + /*width: 100%;*/ + background-color: #333; + color: white; + padding: 40px 30px; + font-size: 14px; + text-align: center; +} + +.footer-container { + display: flex; + flex-wrap: wrap; + justify-content: space-between; + gap: 30px; +} + +.footer-logo { + flex: 1; + max-width: 200px; +} + +.footer-logo .logo { + font-size: 24px; + font-weight: bold; + color: white; + text-decoration: none; +} + +.footer-logo p { + margin-top: 10px; + font-size: 12px; +} + +.footer-nav ul { + list-style: none; +} + +.footer-nav ul li { + margin-bottom: 10px; +} + +.footer-nav ul li a { + color: white; + text-decoration: none; + font-size: 14px; +} + +.footer-nav ul li a:hover { + text-decoration: underline; +} + +.footer-social { + flex: 1; + display: flex; + justify-content: space-around; + margin-top: 10px; +} + +.footer-social .social-icon { + color: white; + text-decoration: none; + font-size: 16px; +} + +.footer-social .social-icon:hover { + opacity: 0.7; +} + +.footer-subscribe { + flex: 1; + max-width: 300px; +} + +.footer-subscribe p { + font-size: 14px; + margin-bottom: 10px; +} + +.footer-subscribe input { + padding: 10px; + width: 80%; + margin-right: 10px; + border: none; + border-radius: 5px; +} + +.footer-subscribe button { + padding: 10px 20px; + background-color: #007BFF; + border: none; + border-radius: 5px; + color: white; + cursor: pointer; +} + +.footer-subscribe button:hover { + opacity: 0.8; +} + +@media (max-width: 768px) { + .footer-container { + flex-direction: column; + align-items: center; + } + + .footer-social { + margin-top: 10px; + } + + .footer-nav ul { + margin-top: 20px; + } +} diff --git a/src/main/resources/static/css/login.css b/src/main/resources/static/css/login.css new file mode 100644 index 0000000000000000000000000000000000000000..25efe488019c72828d914bafec2ccba288c215ac --- /dev/null +++ b/src/main/resources/static/css/login.css @@ -0,0 +1,154 @@ +body { + background-image: url('../pics/photo.jpg'); + background-size: cover; + background-position: center; + height: 100vh; + margin: 0; + display: flex; + justify-content: center; + align-items: center; + font-family: Gill Sans MT, sans-serif; +} + +form { + background-color: rgba(255, 255, 255, 0.8); + padding: 20px; + border-radius: 10px; + backdrop-filter: blur(5px); +} + +h1 { + font-weight: bold; + text-align: center; + color: #333; +} + +#message { + text-align: center; + color: red; +} + +body { + font-size: 16px; +} + +/* é»˜è®¤æ ·å¼ */ +#loginForm { + max-width: 800px; + margin: 0 auto; + position: relative; + top: 20px; + padding: 20px; + border: 1px solid #ccc; + border-radius: 5px; +} + +#loginForm label { + display: block; + margin-bottom: 5px; +} + +#loginForm input[type="email"] { + width: 95%; + margin-bottom: 10px; + padding: 10px; + font-size: 16px; + border: 1px solid #ccc; + border-radius: 5px; +} + +#loginForm input[type="password"] { + width: 95%; + margin-bottom: 60px; + padding: 10px; + font-size: 16px; + border: 1px solid #ccc; + border-radius: 5px; +} + +#loginForm button { + width: 100%; + margin-bottom: 20px; + padding: 10px; + font-size: 16px; + border: 1px solid #ccc; + border-radius: 5px; + background-color: #007BFF; + color: white; + cursor: pointer; +} + +#loginForm label { + font-weight: bold; /* è®¾ç½®æ ‡ç¾å—ä½“åŠ ç²— */ + /*color: #333; !* è®¾ç½®æ ‡ç¾å—体颜色 *!*/ + font-family: Gill Sans MT, sans-serif; /* è®¾ç½®æ ‡ç¾å—ä½“æ— */ +} + +#loginForm button { + font-weight: bold; /* è®¾ç½®æ ‡ç¾å—ä½“åŠ ç²— */ + /*color: #333; !* è®¾ç½®æ ‡ç¾å—体颜色 *!*/ + font-family: Gill Sans MT, sans-serif; /* è®¾ç½®æ ‡ç¾å—ä½“æ— */ +} + +@media screen and (max-width: 600px) { + body { + font-size: 14px; + } + #loginForm { + padding: 10px; + } + + #loginForm input[type="email"], + #loginForm input[type="password"], + #loginForm button { + font-size: 14px; + } +} + +@media screen and (min-width: 601px) and (max-width: 900px) { + body { + font-size: 18px; + } + #loginForm { + min-width: 300px; + } + + #loginForm input[type="email"], + #loginForm input[type="password"], + #loginForm button { + font-size: 18px; + } +} + +@media screen and (min-width: 901px) and (max-width: 1200px) { + body { + font-size: 22px; + } + #loginForm { + min-width: 450px; + } + + #loginForm input[type="email"], + #loginForm input[type="password"], + #loginForm button { + font-size: 22px; + } +} + +@media screen and (min-width: 1201px) { + body { + font-size: 30px; + } + #loginForm { + min-width: 600px; + } + + #loginForm input[type="email"], + #loginForm input[type="password"], + #loginForm button { + font-size: 20px; + } +} + + + diff --git a/src/main/resources/static/css/matchDetail.css b/src/main/resources/static/css/matchDetail.css new file mode 100644 index 0000000000000000000000000000000000000000..dbfba9b8921ac79cac810489f7c1b289bdbb7321 --- /dev/null +++ b/src/main/resources/static/css/matchDetail.css @@ -0,0 +1,99 @@ + +* { + margin: 0; + padding: 0; + box-sizing: border-box; +} + +body { + /*font-family: Arial, sans-serif;*/ + font-family: Gill Sans MT, sans-serif; + background-color: #f4f4f4; + color: #333; +} +.title-h1{ + h1 { + background-color: #4a4a4a; + p { + font-size: 36px; + color: white; + font-weight: bold; + text-align: center; + } + + } +} +.container { + width: 80%; + margin: 20px auto; +} + +header { + text-align: center; + margin-bottom: 30px; +} +#matchDetails { + margin-left: 45px; + align-items: center; + background-color: #fff; + border-radius: 12px; + box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1); + width: 100%; + max-width: 900px; + padding: 30px; + box-sizing: border-box; + margin-top: 30px; +} + +h2 { + text-align: center; + font-size: 2.5em; + color: #333; + margin-bottom: 20px; +} + +#matchScore { + font-size: 2.4em; + line-height: 1.6; + color: #444; + margin: 20px 0; +} + +#matchScore p { + text-align: center; +} + +#matchInfo { + font-size: 1.2em; + line-height: 1.6; + color: #444; +} + +#matchInfo p { + margin: 15px 0; + padding: 10px; + background-color: #f9f9f9; + border-radius: 6px; +} +button { + padding: 10px 20px; + margin-left: 10px; + border: none; + border-radius: 5px; + cursor: pointer; +} +strong { + color: #2d2d2d; +} +/*adjust for phone*/ +@media screen and (max-width: 768px) { + .container { + width: 95%; + } + + .match-item { + flex-direction: column; + align-items: flex-start; + } +} + diff --git a/src/main/resources/static/css/matchDetailChangeScore.css b/src/main/resources/static/css/matchDetailChangeScore.css new file mode 100644 index 0000000000000000000000000000000000000000..8fafb117de2001eed4b70313002dc35d3d1be398 --- /dev/null +++ b/src/main/resources/static/css/matchDetailChangeScore.css @@ -0,0 +1,111 @@ + +* { + margin: 0; + padding: 0; + box-sizing: border-box; +} + +body { + font-family: Arial, sans-serif; + background-color: #f4f4f4; + color: #333; +} +.title-h1{ + h1 { + background-color: #4a4a4a; + p { + font-size: 36px; + color: white; + + font-weight: bold; + text-align: center; + } + + } +} +.container { + width: 80%; + margin: 20px auto; +} + +header { + text-align: center; + margin-bottom: 30px; +} +#matchDetails { + margin-left: 45px; + align-items: center; + background-color: #fff; + border-radius: 12px; + box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1); + width: 100%; + max-width: 900px; + padding: 30px; + box-sizing: border-box; + margin-top: 30px; +} + +h2 { + text-align: center; + font-size: 2.5em; + color: #333; + margin-bottom: 20px; +} + +#matchScore { + font-size: 2.4em; + line-height: 1.6; + color: #444; + margin: 20px 0; +} + +#matchScore p { + text-align: center; +} + +#matchInfo { + font-size: 1.2em; + line-height: 1.6; + color: #444; +} + +#matchInfo p { + margin: 15px 0; + padding: 10px; + background-color: #f9f9f9; + border-radius: 6px; +} +#sport, +#playerAId, +#playerBId, +#status, +#confirmByA, +#confirmByB { + display: none; +} +button { + margin: 10px; + padding: 10px 20px; + /*margin-left: 10px;*/ + border: none; + border-radius: 5px; + cursor: pointer; +} +#updateMatchForm { +display: block; +} +strong { + color: #2d2d2d; +} +/*adjust for phone*/ +@media screen and (max-width: 768px) { + .container { + width: 95%; + } + + .match-item { + flex-direction: column; + align-items: flex-start; + } +} + diff --git a/src/main/resources/static/css/matchSchedule.css b/src/main/resources/static/css/matchSchedule.css index 28fe8e6d9e6aba508bce323a14340562c0e77789..1b20c5882ad8e934598252d9ee73b3521d1ce7e7 100644 --- a/src/main/resources/static/css/matchSchedule.css +++ b/src/main/resources/static/css/matchSchedule.css @@ -5,7 +5,8 @@ } body { - font-family: Arial, sans-serif; + /*font-family: Arial, sans-serif;*/ + font-family: Gill Sans MT, sans-serif; background-color: #f4f4f4; color: #333; } @@ -19,12 +20,21 @@ header { text-align: center; margin-bottom: 30px; } +.title-h1{ + h1 { + background-color: #4a4a4a; + p { + font-size: 36px; + color: white; -h1 { - font-size: 36px; - color: #333; + font-weight: bold; + text-align: center; + } + + } } + .filter-panel { display: flex; justify-content: flex-start; @@ -35,18 +45,21 @@ h1 { margin-left: 10px; margin-right: 10px; font-size: 16px; + font-family: Gill Sans MT, sans-serif; } .filter-panel select { margin-top: 4px; padding: 8px; font-size: 14px; + font-family: Gill Sans MT, sans-serif; border: 1px solid #ddd; border-radius: 5px; } /*button*/ button { background-color: #4CAF50; + font-family: Gill Sans MT, sans-serif; color: white; padding: 10px 20px; border: none; @@ -59,6 +72,7 @@ button { width: 100%; padding: 10px 15px; font-size: 16px; + font-family: Gill Sans MT, sans-serif; margin: 20px 0; border-radius: 6px; border: 1px solid #ddd; @@ -90,6 +104,7 @@ button { justify-content: space-between; align-items: center; gap: 20px; + cursor: pointer; } .match-item p { diff --git a/src/main/resources/static/css/navBar.css b/src/main/resources/static/css/navBar.css new file mode 100644 index 0000000000000000000000000000000000000000..d82debcbde6a3b8dc4b8aa938a5e4a0cd1d9aeed --- /dev/null +++ b/src/main/resources/static/css/navBar.css @@ -0,0 +1,121 @@ +* { + margin: 0; + padding: 0; + box-sizing: border-box; +} + +body { + font-family: Arial, sans-serif; + background-color: #f4f4f4; +} +/*navbar*/ +.navbar { + /*position: fixed;*/ + /*top: 0;*/ + /*left: 0;*/ + /*width: 100%;*/ + display: flex; + justify-content: space-between; + align-items: center; + /*padding: 10px 21px;*/ + padding: 10px 3%; + background-color: #333; + color: white; + font-weight: bold; +} +/*a*/ +.navbar .logo a { + text-decoration: none; + font-size: 24px; + color: white; + font-weight: bold; +} + +.navbar .nav-links { + display: flex; + list-style: none; +} + +.navbar .nav-links li { + margin: 0 15px; +} + +.navbar .nav-links a { + color: white; + text-decoration: none; + font-size: 16px; + padding: 10px; +} + +.navbar .nav-links a:hover { + background-color: #444; + border-radius: 5px; +} + +.navbar .auth-buttons { + display: flex; + flex-direction: column; + button { + /*padding: 10px 20px;*/ + /*margin-left: 10px;*/ + padding: 10px 3%; + margin: 5px 0; + border: none; + border-radius: 5px; + cursor: pointer; + } +} + +@media screen and (max-width: 480px) { + .navbar .nav-links li:not(:first-child):not(:last-child) { + margin: 0 10px; + } +} + +@media screen and (min-width: 481px) and (max-width: 960px) { + .navbar .nav-links li:not(:first-child):not(:last-child) { + margin: 0 30px; + } +} + +@media screen and (min-width: 961px) and (max-width: 1280px) { + .navbar .nav-links li:not(:first-child):not(:last-child) { + margin: 0 50px; + } +} + +@media screen and (min-width: 1281px) { + .navbar .nav-links li:not(:first-child):not(:last-child) { + margin: 0 70px; + } +} + +.navbar .auth-buttons .login-btn { + background-color: #007BFF; + width: 100px; + text-align: center; + margin-bottom: 5px; + color: white; + font-weight: bold; +} + +.navbar .auth-buttons .signup-btn { + background-color: #28a745; + width: 100px; + text-align: center; + margin-bottom: 5px; + color: white; + font-weight: bold; +} +.navbar .auth-buttons .subscribe-btn { + background-color: #d68418; + width: 100px; + text-align: center; + margin-bottom: 5px; + color: white; + font-weight: bold; +} + +.navbar .auth-buttons button:hover { + opacity: 0.8; +} diff --git a/src/main/resources/static/css/rankingTable.css b/src/main/resources/static/css/rankingTable.css new file mode 100644 index 0000000000000000000000000000000000000000..f116967ca25363ea131a73e83416651cbaa0d185 --- /dev/null +++ b/src/main/resources/static/css/rankingTable.css @@ -0,0 +1,117 @@ +/* å…¨å±€æ ·å¼é‡ç½® */ +body { + font-family: Arial, sans-serif; + background-color: #f8f9fa; + margin: 0; + padding: 0; + box-sizing: border-box; + color: #333; +} + +/* 滤镜部分 */ +.filter-container { + text-align: center; + margin: 20px 0; + font-size: 14px; +} + +.filter-container label { + margin: 0 10px; + font-weight: bold; + color: #555; +} + +.filter-container select { + padding: 8px 12px; + border-radius: 25px; + border: 2px solid #ff758c; + outline: none; + font-size: 14px; + cursor: pointer; + transition: all 0.3s ease; + background: white; +} + +.filter-container select:hover { + background: #ff758c; + color: white; +} + +/* è¡¨æ ¼å®¹å™¨æ ·å¼ */ +.table-container { + width: 90%; + margin: 20px auto; + text-align: center; /* åå…ƒç´ å±…ä¸ */ +} + +/* è¡¨æ ¼æ ‡é¢˜ */ +.table-title { + font-size: 24px; + font-weight: bold; + color: #333; + margin-bottom: 10px; + text-transform: uppercase; + letter-spacing: 1px; +} + +/* è¡¨æ ¼æ ·å¼ */ +table { + width: 100%; + border-collapse: collapse; + margin: 0 auto; + background: #2d2d2d; + color: #f9f9f9; + border-radius: 10px; + overflow: hidden; + box-shadow: 0 8px 15px rgba(0, 0, 0, 0.2); + transition: all 0.3s ease; +} + +thead { + background: linear-gradient(90deg, #ff7eb3, #ff758c); + color: white; + font-weight: bold; +} + +thead th { + padding: 12px 15px; + text-align: left; +} + +tbody tr:nth-child(odd) { + background: rgba(255, 255, 255, 0.05); +} + +tbody tr:nth-child(even) { + background: rgba(0, 0, 0, 0.05); +} + +tbody tr:hover { + background: #ff7eb3; + color: white; + cursor: pointer; + transform: scale(1.02); + transition: all 0.2s ease-in-out; +} + +tbody td { + padding: 10px 15px; + text-align: left; +} + +/* 动画效果 */ +.fade-in { + animation: fadeIn 0.5s ease-in-out forwards; +} + +@keyframes fadeIn { + from { + opacity: 0; + transform: translateY(10px); + } + to { + opacity: 1; + transform: translateY(0); + } +} + diff --git a/src/main/resources/static/css/register.css b/src/main/resources/static/css/register.css new file mode 100644 index 0000000000000000000000000000000000000000..d75816fc41aef0d25b524269a98e8daea7c7c7d0 --- /dev/null +++ b/src/main/resources/static/css/register.css @@ -0,0 +1,163 @@ +body { + background-image: url('../pics/photo.jpg'); + background-size: cover; + background-position: center; + height: 100vh; + margin: 0; + display: flex; + justify-content: center; + align-items: center; +} + +form { + background-color: rgba(255, 255, 255, 0.8); + padding: 20px; + border-radius: 10px; + backdrop-filter: blur(5px); +} + +h1 { + text-align: center; + color: #333; +} + +#message { + text-align: center; + color: red; +} + +body { + font-size: 16px; + font-family: Gill Sans MT, sans-serif; +} + +/* é»˜è®¤æ ·å¼ */ +#registerForm { + max-width: 800px; + margin: 0 auto; + padding: 20px; + border: 1px solid #ccc; + border-radius: 5px; +} + +#registerForm label { + display: block; + margin-bottom: 5px; +} + +#registerForm input[type="username"] { + width: 100%; + margin-bottom: 10px; + padding: 10px; + font-size: 16px; + border: 1px solid #ccc; + border-radius: 5px; +} + +#registerForm input[type="email"] { + width: 100%; + margin-bottom: 10px; + padding: 10px; + font-size: 16px; + border: 1px solid #ccc; + border-radius: 5px; +} + +#registerForm input[type="password"] { + width: 100%; + margin-bottom: 60px; + padding: 10px; + font-size: 16px; + border: 1px solid #ccc; + border-radius: 5px; +} + +#registerForm button { + width: 100%; + margin-bottom: 20px; + padding: 10px; + font-size: 16px; + border: 1px solid #ccc; + border-radius: 5px; + background-color: #007BFF; + color: white; + cursor: pointer; +} + +#registerForm label { + font-weight: bold; /* è®¾ç½®æ ‡ç¾å—ä½“åŠ ç²— */ + /*color: #333; !* è®¾ç½®æ ‡ç¾å—体颜色 *!*/ + font-family: Gill Sans MT, sans-serif; /* è®¾ç½®æ ‡ç¾å—ä½“æ— */ +} + +#registerForm button { + font-weight: bold; /* è®¾ç½®æ ‡ç¾å—ä½“åŠ ç²— */ + /*color: #333; !* è®¾ç½®æ ‡ç¾å—体颜色 *!*/ + font-family: Gill Sans MT, sans-serif; /* è®¾ç½®æ ‡ç¾å—ä½“æ— */ +} + +@media screen and (max-width: 600px) { + body { + font-size: 14px; + } + #registerForm { + padding: 10px; + } + + #registerForm input[type="email"], + #registerForm input[type="password"], + #registerForm button { + font-size: 14px; + } +} + +@media screen and (min-width: 601px) and (max-width: 900px) { + body { + font-size: 18px; + } + #registerForm { + min-width: 300px; + } + + #registerForm input[type="username"], + #registerForm input[type="email"], + #registerForm input[type="password"], + #registerForm button { + font-size: 18px; + } +} + +@media screen and (min-width: 901px) and (max-width: 1200px) { + body { + font-size: 22px; + } + #registerForm { + min-width: 450px; + } + + #registerForm input[type="username"], + #registerForm input[type="email"], + #registerForm input[type="password"], + #registerForm button { + font-size: 22px; + } +} + +@media screen and (min-width: 1201px) { + body { + font-size: 25px; + } + #registerForm { + min-width: 600px; + } + + #registerForm input[type="username"], + #registerForm input[type="email"], + #registerForm input[type="password"], + #registerForm button { + font-size: 20px; + } +} + + + diff --git a/src/main/resources/static/css/search.css b/src/main/resources/static/css/search.css new file mode 100644 index 0000000000000000000000000000000000000000..8f8c3ae342a902d4bc2ec7dd0f3054f577f2d776 --- /dev/null +++ b/src/main/resources/static/css/search.css @@ -0,0 +1,54 @@ +/* æœç´¢å®¹å™¨æ ·å¼ */ +.search-container { + max-width: 600px; + margin: 30px auto; + text-align: center; + font-family: Arial, sans-serif; +} + +#search-bar { + width: 100%; + padding: 15px; + font-size: 16px; + border: 1px solid #ccc; + border-radius: 5px; + margin-bottom: 20px; + box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); +} + +#search-results { + margin-top: 20px; + text-align: left; +} + +.search-result-item { + padding: 15px; + margin-bottom: 10px; + border: 1px solid #e0e0e0; + border-radius: 8px; + background-color: #fff; + box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); + transition: transform 0.2s, box-shadow 0.2s; +} + +.search-result-item:hover { + transform: translateY(-2px); + box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2); +} + +.search-result-item h3 { + font-size: 18px; + margin: 0; + color: #0073e6; +} + +.search-result-item p { + font-size: 14px; + color: #555; + margin: 5px 0 0; +} + +.search-result-item a { + text-decoration: none; + color: inherit; +} diff --git a/src/main/resources/static/html/UserCenter.html b/src/main/resources/static/html/UserCenter.html new file mode 100644 index 0000000000000000000000000000000000000000..b920bc68c24302e6cc0cffc056afd9cedd3d9bb9 --- /dev/null +++ b/src/main/resources/static/html/UserCenter.html @@ -0,0 +1,45 @@ +<!DOCTYPE html> +<html lang="en"> +<head> + <meta charset="UTF-8"> + <title>UserCenter</title> + <link rel="stylesheet" type="text/css" href="/css/navBar.css"> + <link rel="stylesheet" href="/css/footer.css"> + <link rel="stylesheet" href="/css/matchSchedule.css"> +</head> +<body> +<div id="navbar-container"></div> +<div class="title-h1"> + <h1><p>UserCenter</p></h1> +</div> +<table> + <thead> + <tr> + <th>ID</th> + <th>Username</th> + <th>Email</th> + <th>Actions</th> + </tr> + </thead> + <tbody> + <!-- 动æ€ç”Ÿæˆç”¨æˆ·æ•°æ® --> + <tr th:each="user : ${users}"> + <td th:text="${user.id}"></td> + <td th:text="${user.username}"></td> + <td th:text="${user.email}"></td> + <td> + <a th:href="@{/users/edit/{id}(id=${user.id})}">Edit</a> + <a th:href="@{/users/delete/{id}(id=${user.id})}">Delete</a> + </td> + </tr> + </tbody> +</table> +<div id="footer-container"></div> +<script src="/js/navBar.js"></script> +<script src="/js/footer.js"></script> +<script> + loadNavbar('navbar-container'); + loadFooter('footer-container'); +</script> +</body> +</html> \ No newline at end of file diff --git a/src/main/resources/static/html/backGroundMatch.html b/src/main/resources/static/html/backGroundMatch.html new file mode 100644 index 0000000000000000000000000000000000000000..68434033b4d832d1f3feaa880a7a436630a0def4 --- /dev/null +++ b/src/main/resources/static/html/backGroundMatch.html @@ -0,0 +1,61 @@ +<!DOCTYPE html> +<html lang="en"> +<head> + <meta charset="UTF-8"> + <meta name="viewport" content="width=device-width, initial-scale=1.0"> + <title>Match Management</title> + <link rel="stylesheet" href="/css/backGroundMatch.css"> +</head> +<body> + <h1>Match Management</h1> + <h2>Match List</h2> + <table id="matchTable"> + <thead> + <tr> + <th>ID</th> + <th>Sport</th> + <th>Player A ID</th> + <th>Player B ID</th> + <th>Plan Time</th> + <th>Status</th> + <th>Score A</th> + <th>Score B</th> + <th>Confirm A</th> + <th>Confirm B</th> + <th>Winner ID</th> + </tr> + </thead> + <tbody> + <!-- matches show threr ++ --> + </tbody> + </table> + <hr> + <div id="addMatchForm"> + <h2>Add Match</h2> + <form id="matchForm"> + <label for="sport">Sport: </label> + <input type="text" id="sport" name="sport" required><br> + <label for="playerAId">Player A ID: </label> + <input type="number" id="playerAId" name="playerAId" required><br> + <label for="playerBId">Player B ID: </label> + <input type="number" id="playerBId" name="playerBId" required><br> + <label for="planTime">Plan Time: </label> + <input type="datetime-local" id="planTime" name="planTime" required><br> + <label for="status">Status: </label> + <input type="text" id="status" name="status" required><br> + <label for="scoreA">Score A: </label> + <input type="number" id="scoreA" name="scoreA" required><br> + <label for="scoreB">Score B: </label> + <input type="number" id="scoreB" name="scoreB" required><br> + <label for="confirmByA">Confirm by A: </label> + <input type="checkbox" id="confirmByA" name="confirmByA"><br> + <label for="confirmByB">Confirm by B: </label> + <input type="checkbox" id="confirmByB" name="confirmByB"><br> + <label for="winnerId">Winner ID: </label> + <input type="number" id="winnerId" name="winnerId"><br> + <button type="button" onclick="addMatch()">Add Match</button> + </form> + </div> + <script src="/js/backGroundMatch.js"></script> +</body> +</html> diff --git a/src/main/resources/static/html/backGroundUser.html b/src/main/resources/static/html/backGroundUser.html new file mode 100644 index 0000000000000000000000000000000000000000..13fa7cb018d34f957423352d41a9c54416ba80c9 --- /dev/null +++ b/src/main/resources/static/html/backGroundUser.html @@ -0,0 +1,46 @@ +<!DOCTYPE html> +<html lang="en"> +<head> + <meta charset="UTF-8"> + <meta name="viewport" content="width=device-width, initial-scale=1.0"> + <title>User Management</title> + <link rel="stylesheet" href="/css/backGroundUser.css"> +</head> +<body> +<h1>User Management</h1> +<h2>User List</h2> +<table id="userTable"> + <thead> + <tr> + <th>ID</th> + <th>Username</th> + <th>Email</th> + <th>Role</th> + <th>Actions</th> + </tr> + </thead> + <tbody> + <!-- Users will be displayed here --> + </tbody> +</table> +<hr> +<div id="addUserForm"> + <h2>Add User</h2> + <form id="userForm"> + <label for="username">Username: </label> + <input type="text" id="username" name="username" required><br> + <label for="email">Email: </label> + <input type="email" id="email" name="email" required><br> + <label for="password">Password: </label> + <input type="password" id="password" name="password" required><br> + <label for="role">Role: </label> + <select id="role" name="role"> + <option value="USER">USER</option> + <option value="ADMIN">ADMIN</option> + </select><br> + <button type="button" onclick="addUser()">Add User</button> + </form> +</div> +<script src="/js/backGroundUser.js"></script> +</body> +</html> \ No newline at end of file diff --git a/src/main/resources/static/html/dartsrules.html b/src/main/resources/static/html/dartsrules.html new file mode 100644 index 0000000000000000000000000000000000000000..b0ce34c1eb3f4081ab7ce8bc610bb88c96870daa --- /dev/null +++ b/src/main/resources/static/html/dartsrules.html @@ -0,0 +1,39 @@ +<!DOCTYPE html> +<html lang="en"> +<head> + <meta charset="UTF-8"> + <meta name="viewport" content="width=device-width, initial-scale=1.0"> + <title>Darts Rules</title> + <link rel="stylesheet" href="/css/navBar.css"> + <link rel="stylesheet" href="/css/footer.css"> + <link rel="stylesheet" href="/css/dartsrules.css"> +</head> +<body> +<!-- 导航æ --> +<div id="navbar-container"></div> + +<!-- 主内容 --> +<main> + <h1>Dart Rules</h1> + <ol> + <li><strong>Objective:</strong> The aim is to reduce your score from 301 to exactly zero.</li> + <li><strong>Throwing:</strong> Each player throws three darts per turn. Players determine who goes first by throwing a single dart – closest to bull wins.</li> + <li><strong>Scoring:</strong> Points are scored by hitting various sections of the dartboard. The outer ring doubles the score of that section, and the inner ring triples it. Hitting the bullseye scores 50 points, and the outer bull scores 25.</li> + <li><strong>Finishing:</strong> To win, you must reach exactly zero by ending with a double (a dart that lands in the outer ring of a number) or a bullseye. If you score more than needed or fail to end on a double, your score for that turn is voided, and you try again on your next turn.</li> + <li><strong>Turns:</strong> Players alternate turns, throwing three darts each time until one player reaches exactly zero.</li> + </ol> +</main> + +<!-- 页脚 --> +<div id="footer-container"></div> + +<!-- 脚本 --> +<script src="/js/navBar.js"></script> +<script src="/js/footer.js"></script> +<script> + loadNavbar('navbar-container'); + loadFooter('footer-container'); +</script> +</body> +</html> + diff --git a/src/main/resources/static/html/footer.html b/src/main/resources/static/html/footer.html new file mode 100644 index 0000000000000000000000000000000000000000..1fb36bbf2589c4b87a880646643effee685f0180 --- /dev/null +++ b/src/main/resources/static/html/footer.html @@ -0,0 +1,28 @@ +<footer class="footer"> + <div class="footer-container"> + <!-- logo --> + <div class="footer-logo"> + <a href="/" class="logo">Sports League Application</a> + <p>© 2024 Sports League Application. All rights reserved.</p> + </div> + + <!-- footer navigation links --> + <div class="footer-nav"> + <ul> + <li><a href="#">About</a></li> + <li><a href="#">Contact</a></li> + <li><a href="#">Privacy Policy</a></li> + <li><a href="#">Terms & Conditions</a></li> + <li><a href="#">Jobs</a></li> + </ul> + </div> + + <!-- social Media Links --> + <div class="footer-social"> + <a href="#" class="social-icon">Facebook</a> + <a href="#" class="social-icon">Twitter</a> + <a href="#" class="social-icon">Instagram</a> + <a href="#" class="social-icon">YouTube</a> + </div> + </div> +</footer> \ No newline at end of file diff --git a/src/main/resources/templates/page/login.html b/src/main/resources/static/html/login.html similarity index 85% rename from src/main/resources/templates/page/login.html rename to src/main/resources/static/html/login.html index e143c04266c4d3014345dacedc186aa87e400ab5..fe97d230b8bbb56ee9ca1582e565620456389aa8 100644 --- a/src/main/resources/templates/page/login.html +++ b/src/main/resources/static/html/login.html @@ -4,14 +4,16 @@ <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>User Login</title> - <link rel="stylesheet" href="../../css/index.css"> - <script src="https://code.jquery.com/jquery-3.6.0.min.js"></script> + <link rel="stylesheet" href="/css/index.css"> + <link rel="stylesheet" href="/css/login.css"> +<!-- <script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>--> <script src="/js/bcrypt.min.js"></script> - <script src="https://cdn.bootcdn.net/ajax/libs/bcryptjs/2.4.3/bcrypt.min.js"></script> + <script src="/js/jquery-3.6.0.min.js"></script> </head> <body> -<h1>User Login</h1> + <form id="loginForm"> + <h1>User Login</h1> <label for="email">E-mail:</label> <input type="email" id="email" name="email" required><br> diff --git a/src/main/resources/static/html/matchDetail.html b/src/main/resources/static/html/matchDetail.html index fe9da877b5ac5de2909b2e80a6086dc9f557c2e7..68d2969f9e14108c0d760ac1375ffc4f86fa006b 100644 --- a/src/main/resources/static/html/matchDetail.html +++ b/src/main/resources/static/html/matchDetail.html @@ -3,39 +3,37 @@ <head> <meta charset="UTF-8"> <title>Match Detail</title> + <link rel="stylesheet" href="/css/matchDetail.css"> + <link rel="stylesheet" href="/css/navBar.css"> + <link rel="stylesheet" href="/css/footer.css"> </head> <body> - <div id="matchDetails"> - <h2>Match Details</h2> - <div id="matchInfo"></div> - </div> - -</body> -<script> - const urlParams = new URLSearchParams(window.location.search); - const matchId = urlParams.get(`id`); - // èŽ·å–æ¯”赛详情 - async function fetchMatchDetails(id) { - try { - const response = await fetch(`/match/${id}`); - const matchData = await response.json(); + <!-- navbar--> - const matchInfo = document.getElementById('matchInfo'); - matchInfo.innerHTML = ` - <p><strong>Sport: </strong>${matchData.sport}</p> - <p><strong>Players: </strong>${matchData.playerAId} vs ${matchData.playerBId}</p> - <p><strong>Scheduled Time: </strong>${new Date(matchData.planTime).toLocaleString()}</p> - <p><strong>Status: </strong>${matchData.status}</p> - <p><strong>Score: </strong>${matchData.scoreA} - ${matchData.scoreB}</p> - <p><strong>Winner: </strong>${matchData.winnerId ? matchData.winnerId : 'N/A'}</p> - `; - } catch (error) { - console.error('Error fetching match details:', error); - } - } + <div id="navbar-container"></div> + <div class="title-h1"> + <h1><p>Match Details</p></h1> + </div> + <div class="container"> + <div id="matchDetails"> + <h2>Match Details</h2> + <div id="matchScore"></div> + <div id="matchInfo"></div> + <button id="updateBtn" type="submit">Update Score</button> + <a href="http://localhost:8080/html/matchSchedule.html"> + <button class="cancel-btn">Cancel</button> + </a> + </div> - // åŠ è½½æ¯”èµ›è¯¦æƒ… - fetchMatchDetails(matchId); -</script> + </div> + <div id="footer-container"></div> + <script src="/js/navBar.js"></script> + <script src="/js/footer.js"></script> + <script> + loadNavbar('navbar-container'); + loadFooter('footer-container'); + </script> +</body> +<script src="/js/matchDetail.js"></script> </html> \ No newline at end of file diff --git a/src/main/resources/static/html/matchDetailChangeScore.html b/src/main/resources/static/html/matchDetailChangeScore.html new file mode 100644 index 0000000000000000000000000000000000000000..ada5b7a395967a9dbb5ff985e8684bfcf3fd7c7e --- /dev/null +++ b/src/main/resources/static/html/matchDetailChangeScore.html @@ -0,0 +1,58 @@ +<!DOCTYPE html> +<html lang="en"> +<head> + <meta charset="UTF-8"> + <title>Match Detail</title> + <link rel="stylesheet" href="/css/matchDetailChangeScore.css"> + <link rel="stylesheet" href="/css/navBar.css"> + <link rel="stylesheet" href="/css/footer.css"> +</head> +<body> +<!-- navbar--> +<div id="navbar-container"></div> +<div class="title-h1"> + <h1>Score Change</h1> +</div> +<div class="container"> + <div id="matchDetails"> + <h2>Score Change</h2> + <form id="updateMatchForm"> + <label for="scoreA">Score A:</label> + <input type="number" id="scoreA" name="scoreA" min="0" required> + + <label for="scoreB">Score B:</label> + <input type="number" id="scoreB" name="scoreB" min="0" required> + + <label for="winnerId">Winner ID:</label> + <input type="number" id="winnerId" name="winnerId" min="0" required> + +<!-- <label for="sport">Sport: </label>--> + <input type="text" id="sport" name="sport" required><br> +<!-- <label for="playerAId">Player A ID: </label>--> + <input type="number" id="playerAId" name="playerAId" required><br> +<!-- <label for="playerBId">Player B ID: </label>--> + <input type="number" id="playerBId" name="playerBId" required><br> + <label for="planTime">Plan Time: </label> + <input type="datetime-local" id="planTime" name="planTime" required><br> +<!-- <label for="status">Status: </label>--> + <input type="text" id="status" name="status" required><br> +<!-- <label for="confirmByA">Confirm by A: </label>--> + <input type="checkbox" id="confirmByA" name="confirmByA"><br> +<!-- <label for="confirmByB">Confirm by B: </label>--> + <input type="checkbox" id="confirmByB" name="confirmByB"><br> + <button type="submit">Update Score</button> + </form> + <button onclick="history.back()" class="cancel-btn">Cancel</button> + </div> +</div> + +<div id="footer-container"></div> +<script src="/js/navBar.js"></script> +<script src="/js/footer.js"></script> +<script> + loadNavbar('navbar-container'); + loadFooter('footer-container'); +</script> + <script src="/js/matchDetailChangeScore.js"></script> +</body> +</html> diff --git a/src/main/resources/static/html/matchSchedule.html b/src/main/resources/static/html/matchSchedule.html index e6413bc27c17afc8877dcbbb3403e6951651b35f..884a117f7dbd436c7aa1778c3730cb8e66e65d44 100644 --- a/src/main/resources/static/html/matchSchedule.html +++ b/src/main/resources/static/html/matchSchedule.html @@ -3,11 +3,20 @@ <head> <meta charset="UTF-8"> <title>Sports league Schedule</title> + <link rel="stylesheet" type="text/css" href="/css/navBar.css"> + <link rel="stylesheet" href="/css/footer.css"> <link rel="stylesheet" href="/css/matchSchedule.css"> + </head> <body> + <!-- navbar--> + <div id="navbar-container"></div> + <div class="title-h1"> + <h1><p>Matches</p></h1> + </div> + <div class="container"> - <h1>Sports League Schedule</h1> +<!-- <h1>Sports League Schedule</h1>--> <!-- Filter --> <div class="filter-panel"> <label for="sportSelect">Filter by Sport:</label> @@ -28,13 +37,21 @@ <!-- time sort button --> <button id="sortTimeBtn">Sort by Time</button> </div> - <!-- æœç´¢æ¡† --> + <!-- search --> <input type="text" id="searchInput" placeholder="Search matches id or player id" /> <!-- Matches List --> <div id="matchesList" class="matches-list"> <!-- generate match in there --> </div> </div> + <!-- footer--> + <div id="footer-container"></div> + <script src="/js/navBar.js"></script> + <script src="/js/footer.js"></script> + <script> + loadNavbar('navbar-container'); + loadFooter('footer-container'); + </script> <script src="/js/matchSchedule.js"></script> </body> </html> \ No newline at end of file diff --git a/src/main/resources/static/html/matchTable.html b/src/main/resources/static/html/matchTable.html new file mode 100644 index 0000000000000000000000000000000000000000..645cec2c6b2449d61922b27abe9c7df7535aeca5 --- /dev/null +++ b/src/main/resources/static/html/matchTable.html @@ -0,0 +1,10 @@ +<!DOCTYPE html> +<html lang="en"> +<head> + <meta charset="UTF-8"> + <title>Match Table</title> +</head> +<body> + +</body> +</html> \ No newline at end of file diff --git a/src/main/resources/static/html/navBar.html b/src/main/resources/static/html/navBar.html new file mode 100644 index 0000000000000000000000000000000000000000..4d9895c801b6ca59aa56f193fb168e9806501275 --- /dev/null +++ b/src/main/resources/static/html/navBar.html @@ -0,0 +1,33 @@ +<nav class="navbar"> + <div class="logo"> + <a href="/">Sports League Application</a> + </div> +<!-- put nav links here --> + <ul class="nav-links"> + <li><a href="#">News</a></li> + <li><a href="#">Videos</a></li> + <li><a href="http://localhost:8080/html/rules.html">Rules</a></li> + <li><a href="http://localhost:8080/html/UserCenter.html">Players</a></li> + <li><a href="http://localhost:8080/html/matchSchedule.html">Matches</a></li> + <li><a href="http://localhost:8080/html/ranking.html">Rankings</a></li> + </ul> + <!-- put login〠signup subscribe links here--> +<!-- <div class="auth-buttons">--> +<!-- <button class="login-btn">Login</button>--> +<!-- <li><a href="/html/login.html">Login</a></li>--> +<!-- <button class="signup-btn">Sign Up</button>--> +<!-- <button class="subscribe-btn">Subscribe</button>--> +<!-- </div>--> +<!-- <div class="auth-buttons">--> +<!-- <a href="http://localhost:8080/html/login.html" class="login-btn">Login</a>--> +<!-- <a href="http://localhost:8080/html/register.html" class="signup-btn">Sign Up</a>--> +<!-- <a href="http://localhost:8080/logout" class="logout-btn">Logout</a>--> +<!-- </div>--> + + <div class="auth-buttons"> + <a id="auth-btn" href="http://localhost:8080/html/login.html" class="login-btn">Login</a> + <a href="http://localhost:8080/html/register.html" class="signup-btn">Sign Up</a> + <a id="logout-btn" href="http://localhost:8080/logout" class="logout-btn">Logout</a> + </div> + +</nav> \ No newline at end of file diff --git a/src/main/resources/static/html/ranking.html b/src/main/resources/static/html/ranking.html new file mode 100644 index 0000000000000000000000000000000000000000..2be8d2275c1f3958708e60d74d19678f7d0b0ffb --- /dev/null +++ b/src/main/resources/static/html/ranking.html @@ -0,0 +1,57 @@ +<!DOCTYPE html> +<html lang="en"> +<head> + <meta charset="UTF-8"> + <title>Sports league rankings</title> + <link rel="stylesheet" href="/css/navBar.css"> + <link rel="stylesheet" href="/css/footer.css"> + <link rel="stylesheet" href="/css/rankingTable.css"> +</head> +<body> +<div id="navbar-container"></div> + +<div class="filter-container"> + <label for="filter-sport">Filter by Sport:</label> + <select id="filter-sport" onchange="loadRankings()"> + <option value="all">All</option> + <option value="TableTennis">Table Tennis</option> + <option value="Darts">Dart</option> + <option value="Pools">Pools</option> + </select> + + <label for="sort-option">Sort by:</label> + <select id="sort-option" onchange="loadRankings()"> + <option value="asc">Ascending</option> + <option value="desc">Descending</option> + </select> +</div> +<div class="table-container"> + <h1 class="table-title">Rankings</h1> + +<table> + <thead> + <tr> + <th>Ranking</th> + <th>Username</th> + <th>Wins</th> + <th>sport</th> + + </tr> + </thead> + <tbody id="ranking-table"> + <!-- Data will be dynamically inserted here --> + </tbody> +</table> +</div> + +<div id="footer-container"></div> +<script src="/js/navBar.js"></script> +<script src="/js/footer.js"></script> +<script> + loadNavbar('navbar-container'); + loadFooter('footer-container'); +</script> +<script src="/js/rankingTable.js"></script> +<!--<script src="/js/matchSchedule.js"></script>--> +</body> +</html> \ No newline at end of file diff --git a/src/main/resources/templates/page/register.html b/src/main/resources/static/html/register.html similarity index 82% rename from src/main/resources/templates/page/register.html rename to src/main/resources/static/html/register.html index 9ed64ddb8e762f4ae71ebb6674d757122853db0f..e478e34d24abd4144a7331027f2d859500a927fe 100644 --- a/src/main/resources/templates/page/register.html +++ b/src/main/resources/static/html/register.html @@ -4,15 +4,15 @@ <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>User Registration</title> - <link rel="stylesheet" href="../../css/index.css"> + <link rel="stylesheet" href="/css/register.css"> <!-- Link to external CSS file --> <script src="https://code.jquery.com/jquery-3.6.0.min.js"></script> -<!-- <script src="/js/bcrypt.min.js"></script>--> - <script src="https://cdn.bootcdn.net/ajax/libs/bcryptjs/2.4.3/bcrypt.min.js"></script> -<!-- <script src="https://cdn.jsdelivr.net/npm/bcrypt@5.1.1/bcrypt.min.js"></script>--> + <script src="/js/bcrypt.min.js"></script> </head> + <body> -<h1>User Register</h1> + <form id="registerForm"> + <h1>User Registration</h1> <label for="username">Username:</label> <input type="username" id="username" name="username" required><br> @@ -26,7 +26,6 @@ </form> <p id="message"></p> - <script> $(document).ready(function () { $('#registerForm').on('submit', async function (e) { @@ -61,7 +60,7 @@ $('#message').text(data.message); if (data.message === 'Registration successful') { - window.location.href = '/login'; // After successful registration, you will be redirected to the login page + window.location.href = '/html/login.html'; // After successful registration, you will be redirected to the login page } }, error: function () { diff --git a/src/main/resources/static/html/rules.html b/src/main/resources/static/html/rules.html new file mode 100644 index 0000000000000000000000000000000000000000..80f23205ff3515fa836962eb1dfb9a4d0f13d330 --- /dev/null +++ b/src/main/resources/static/html/rules.html @@ -0,0 +1,37 @@ +<!DOCTYPE html> +<html lang="en"> +<head> + <meta charset="UTF-8"> + <meta name="viewport" content="width=device-width, initial-scale=1.0"> + <title>Sports Rules</title> + <link rel="stylesheet" href="/css/navBar.css"> + <link rel="stylesheet" href="/css/footer.css"> + <link rel="stylesheet" href="/css/search.css"> <!-- è‡ªå®šä¹‰æ ·å¼ --> +</head> +<body> +<!-- 导航æ --> +<div id="navbar-container"></div> + +<!-- 主内容 --> +<main> + <div class="search-container"> + <input id="search-bar" type="text" placeholder="Search for rules..."> + <div id="search-results"></div> + </div> +</main> + +<!-- 页脚 --> +<div id="footer-container"></div> + +<!-- 脚本 --> +<script src="/js/navBar.js"></script> +<script src="/js/footer.js"></script> +<script src="https://cdn.jsdelivr.net/npm/fuse.js@6"></script> <!-- Fuse.js --> +<script src="/js/search.js"></script> +<script> + loadNavbar('navbar-container'); + loadFooter('footer-container'); +</script> +</body> +</html> + diff --git a/src/main/resources/static/html/table-tennisrules.html b/src/main/resources/static/html/table-tennisrules.html new file mode 100644 index 0000000000000000000000000000000000000000..54645f77d668f08acbd150b5447118fe7e6b7cd1 --- /dev/null +++ b/src/main/resources/static/html/table-tennisrules.html @@ -0,0 +1,50 @@ +<!DOCTYPE html> +<html lang="en"> +<head> + <meta charset="UTF-8"> + <meta name="viewport" content="width=device-width, initial-scale=1.0"> + <title>Table Tennis Rules</title> + <link rel="stylesheet" href="/css/navBar.css"> + <link rel="stylesheet" href="/css/footer.css"> + <link rel="stylesheet" href="/css/dartsrules.css"> +</head> +<body> +<!-- 导航æ --> +<div id="navbar-container"></div> + +<!-- 主内容 --> +<main> + <h1>Table Tennis Rules</h1> + <h2>Basic Rules:</h2> + <ol> + <li><strong>Coin toss:</strong> Whoever wins the coin toss decides whether to serve first or choose a side of the table.</li> + <li><strong>Service:</strong> A coin toss will determine who serves first. The game then starts with a serve. The server must toss the ball and strike it so it bounces once on their side and once on the opponent's side. If the ball touches the net but still lands on the opponent's side, the serve is replayed without penalty (this is called a "let").</li> + <li><strong>Scoring:</strong> A point is scored after each ball is put into play. Points can be scored by the receiver if the server fails to make a correct service or return. Conversely, the server scores if the receiver fails to return the ball correctly. The rally continues until one player fails to return the ball according to the rules.</li> + <li><strong>Game Play:</strong> The standard game in competitive table tennis is played to 11 points. However, a player must win by at least a 2-point margin. If the score ties at 10-10, play continues until one player gains a 2-point lead. + <ul> + <li>In a 10-10 situation, the next point will be an advantage point which is then lost if levelled (i.e., the score goes back to 10-10). When 2 consecutive points are scored, the final score becomes 12-10.</li> + </ul> + </li> + </ol> + + <h2>Match Format:</h2> + <ul> + <li><strong>Individual Matches:</strong> A match is best of 3 games. This means the first player to win 2 games wins the match. The players should swap sides after every game played.</li> + </ul> + + <h2>Service Alternation:</h2> + <p><strong>Players</strong> alternate serving every two points. In the event of a 10-10 tie, service alternates after every point.</p> +</main> + +<!-- 页脚 --> +<div id="footer-container"></div> + +<!-- 脚本 --> +<script src="/js/navBar.js"></script> +<script src="/js/footer.js"></script> +<script> + loadNavbar('navbar-container'); + loadFooter('footer-container'); +</script> +</body> +</html> diff --git a/src/main/resources/static/js/UserCenter.js b/src/main/resources/static/js/UserCenter.js new file mode 100644 index 0000000000000000000000000000000000000000..5a1bef3a6200a405aa4b62f210ef7da6afedb037 --- /dev/null +++ b/src/main/resources/static/js/UserCenter.js @@ -0,0 +1,38 @@ +// userTable.js + +// 动æ€åŠ è½½ç”¨æˆ·æ•°æ® +async function loadUsers(id) { + try { + const response = await fetch('/api/users/${id}'); // ç¡®ä¿åŽç«¯æä¾›äº†æ¤ API + if (!response.ok) { + throw new Error(`HTTP error! Status: ${response.status}`); + } + + const users = await response.json(); + + // 获å–è¡¨æ ¼çš„ tbody å…ƒç´ + const tbody = document.querySelector('tbody'); + tbody.innerHTML = ''; // æ¸…ç©ºæ—§çš„æ•°æ® + + // é历用户数æ®å¹¶ç”Ÿæˆè¡¨æ ¼è¡Œ + users.forEach(user => { + const row = document.createElement('tr'); + row.innerHTML = ` + <td>${user.id}</td> + <td>${user.username}</td> + <td>${user.email}</td> + <td> + <a href="/users/edit/${user.id}">Edit</a> + <a href="/users/delete/${user.id}">Delete</a> + </td> + `; + tbody.appendChild(row); + }); + } + catch (error) { + console.error('Error loading users:', error); + } +} + +// ç‰é¡µé¢åŠ è½½å®ŒæˆåŽæ‰§è¡ŒåŠ è½½æ“作 +document.addEventListener('DOMContentLoaded', loadUsers); diff --git a/src/main/resources/static/js/backGroundMatch.js b/src/main/resources/static/js/backGroundMatch.js new file mode 100644 index 0000000000000000000000000000000000000000..7e5948c1a690fe7768d9b7c2957869dcdd0a61fb --- /dev/null +++ b/src/main/resources/static/js/backGroundMatch.js @@ -0,0 +1,183 @@ +const MATCHES_API_URL = '/match'; +// let matches = [] +window.onload = function() { + fetchMatchItems(); +}; +// // get all match items +// async function fetchMatch() { +// function displayMatch(match) { +// +// } +// +// try { +// const res = await fetch(MATCHES_API_URL); +// matches = await res.json(); +// displayMatch(matches); +// }catch(err){ +// console.error('error fetching match data', err) +// } +// } +// function displayMatch(matches) { +// // generate the match in tbody +// const tableBody = document.querySelector('#matchTable tbody'); +// // Clear previous rows +// tableBody.innerHTML = ''; +// // all match +// matches.forEach(match => { +// const row = document.createElement('tr'); +// row.innerHTML = ` +// <td>${match.id}</td> +// <td>${match.sport}</td> +// <td>${match.playerAId}</td> +// <td>${match.playerBId}</td> +// <td>${match.planTime}</td> +// <td>${match.status}</td> +// <td>${match.scoreA}</td> +// <td>${match.scoreB}</td> +// <td>${match.confirmByA}</td> +// <td>${match.confirmByB}</td> +// <td>${match.winnerId}</td> +// <td> +// <!-- edit button --> +// <button onclick="editMatch(${match.id})">Edit</button> +// <!-- delete button --> +// <button onclick="deleteMatch(${match.id})">Delete</button> +// </td> +// `; +// tableBody.appendChild(row); +// }); +// } + +// show all matches +function fetchMatchItems() { + fetch(MATCHES_API_URL) + .then(res => res.json()) + .then(data => { + // console.log(data) + // generate the match in tbody + const tableBody = document.querySelector('#matchTable tbody'); + // Clear previous rows + tableBody.innerHTML = ''; + // all match + data.forEach(match => { + const matchItem = document.createElement('tr'); + matchItem.innerHTML = ` + <td>${match.id}</td> + <td>${match.sport}</td> + <td>${match.playerAId}</td> + <td>${match.playerBId}</td> + <td>${match.planTime}</td> + <td>${match.status}</td> + <td>${match.scoreA}</td> + <td>${match.scoreB}</td> + <td>${match.confirmByA}</td> + <td>${match.confirmByB}</td> + <td>${match.winnerId}</td> + <td> + <!-- edit button --> + <button onclick="editMatch(${match.id})">Edit</button> + <!-- delete button --> + <button onclick="deleteMatch(${match.id})">Delete</button> + </td> + `; + tableBody.appendChild(matchItem); + }); + }) + .catch(err => console.error('error:', err)); +} + +// Add a new match +function addMatch() { + const matchForm = document.getElementById('matchForm'); + // formData object + const formData = new FormData(matchForm); + const matchData = {}; + + formData.forEach((value, key) => { + matchData[key] = value; + }); + fetch(MATCHES_API_URL, { + method: 'POST', + // for post must set headers for json + headers: { + 'Content-Type': 'application/json', + }, + // json => string + body: JSON.stringify(matchData), + }) + .then(res => res.json()) + .then(() => { + alert('Match added successfully!'); + // refresh form + fetchMatchItems(); + matchForm.reset(); + }) + .catch(err => console.error('error when add match:', err)); +} + +// delete a match by id +function deleteMatch(id) { + fetch(`/match/${id}`, { + method: 'DELETE', + }) + .then(res => res.json()) + .then(() => { + alert('match deleted successfully!'); + fetchMatchItems(); // Refresh match list + }) + .catch(err => console.error('error when delete match:', err)); +} + +// edit a match and show it in the same palce as add +function editMatch(id) { + fetch(`/match/${id}`) + .then(res => res.json()) + .then(match => { + document.getElementById('sport').value = match.sport; + document.getElementById('playerAId').value = match.playerAId; + document.getElementById('playerBId').value = match.playerBId; + document.getElementById('planTime').value = match.planTime; + document.getElementById('status').value = match.status; + document.getElementById('scoreA').value = match.scoreA; + document.getElementById('scoreB').value = match.scoreB; + document.getElementById('confirmByA').checked = match.confirmByA; + document.getElementById('confirmByB').checked = match.confirmByB; + document.getElementById('winnerId').value = match.winnerId; + // change the add button to an update button + const addButton = document.querySelector('#matchForm button'); + addButton.textContent = 'Update Match'; + // update an existing match + addButton.setAttribute('onclick', `updateMatch(${id})`); + }) + .catch(error => console.error('error when edit match:', error)); +} + +// update an existing match +function updateMatch(id) { + const matchForm = document.getElementById('matchForm'); + const formData = new FormData(matchForm); + const matchData = {}; + + formData.forEach((value, key) => { + matchData[key] = value; + }); + + fetch(`/match/${id}`, { + method: 'PUT', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify(matchData), + }) + .then(res => res.json()) + .then(() => { + alert('match updated successfully!'); + fetchMatchItems(); + matchForm.reset(); + // change the Update button back to Add button + const addButton = document.querySelector('#matchForm button'); + addButton.textContent = 'Add Match'; + addButton.setAttribute('onclick', 'addMatch()'); + }) + .catch(err => console.error('error update match:', err)); +} diff --git a/src/main/resources/static/js/backGroundUser.js b/src/main/resources/static/js/backGroundUser.js new file mode 100644 index 0000000000000000000000000000000000000000..b32035d1dfb83ff644e3f055cdfc056e8e41144f --- /dev/null +++ b/src/main/resources/static/js/backGroundUser.js @@ -0,0 +1,117 @@ +const USERS_API_URL = '/users'; + +window.onload = function () { + fetchUsers(); +}; + +// Fetch and display all users +function fetchUsers() { + fetch(USERS_API_URL) + .then(res => res.json()) + .then(data => { + const tableBody = document.querySelector('#userTable tbody'); + tableBody.innerHTML = ''; // Clear previous rows + + data.forEach(user => { + const userRow = document.createElement('tr'); + userRow.innerHTML = ` + <td>${user.id}</td> + <td>${user.username}</td> + <td>${user.email}</td> + <td>${user.role}</td> + <td> + <button onclick="editUser(${user.id})">Edit</button> + <button onclick="deleteUser(${user.id})">Delete</button> + </td> + `; + tableBody.appendChild(userRow); + }); + }) + .catch(err => console.error('Error fetching users:', err)); +} + +// Add a new user +function addUser() { + const userForm = document.getElementById('userForm'); + const formData = new FormData(userForm); + const userData = {}; + + formData.forEach((value, key) => { + userData[key] = value; + }); + + fetch(USERS_API_URL, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify(userData), + }) + .then(res => res.json()) + .then(() => { + alert('User added successfully!'); + fetchUsers(); + userForm.reset(); + }) + .catch(err => console.error('Error adding user:', err)); +} + +// Delete a user by ID +function deleteUser(id) { + fetch(`${USERS_API_URL}/${id}`, { + method: 'DELETE', + }) + .then(res => res.json()) + .then(() => { + alert('User deleted successfully!'); + fetchUsers(); + }) + .catch(err => console.error('Error deleting user:', err)); +} + +// Edit a user and pre-fill the form +function editUser(id) { + fetch(`${USERS_API_URL}/${id}`) + .then(res => res.json()) + .then(user => { + document.getElementById('username').value = user.username; + document.getElementById('email').value = user.email; + document.getElementById('password').value = ''; // Leave empty for security + document.getElementById('role').value = user.role; + + const addButton = document.querySelector('#userForm button'); + addButton.textContent = 'Update User'; + addButton.setAttribute('onclick', `updateUser(${id})`); + }) + .catch(err => console.error('Error editing user:', err)); +} + +// Update an existing user +function updateUser(id) { + const userForm = document.getElementById('userForm'); + const formData = new FormData(userForm); + const userData = {}; + + formData.forEach((value, key) => { + userData[key] = value; + }); + + fetch(`${USERS_API_URL}/${id}`, { + method: 'PUT', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify(userData), + }) + .then(res => res.json()) + .then(() => { + alert('User updated successfully!'); + fetchUsers(); + userForm.reset(); + + const addButton = document.querySelector('#userForm button'); + addButton.textContent = 'Add User'; + addButton.setAttribute('onclick', 'addUser()'); + }) + .catch(err => console.error('Error updating user:', err)); +} \ No newline at end of file diff --git a/src/main/resources/static/js/footer.js b/src/main/resources/static/js/footer.js new file mode 100644 index 0000000000000000000000000000000000000000..6232721100c725d1174d677b659f6213a374626c --- /dev/null +++ b/src/main/resources/static/js/footer.js @@ -0,0 +1,11 @@ +function loadFooter(targetElementId) { + fetch('footer.html') + .then(response => response.text()) + .then(data => { + // add the bar + document.getElementById(targetElementId).innerHTML = data; + }) + .catch(error => { + console.error('footer errors:', error); + }); +} \ No newline at end of file diff --git a/src/main/resources/static/js/jquery-3.6.0.min.js b/src/main/resources/static/js/jquery-3.6.0.min.js new file mode 100644 index 0000000000000000000000000000000000000000..c4c6022f2982e8dae64cebd6b9a2b59f2547faad --- /dev/null +++ b/src/main/resources/static/js/jquery-3.6.0.min.js @@ -0,0 +1,2 @@ +/*! jQuery v3.6.0 | (c) OpenJS Foundation and other contributors | jquery.org/license */ +!function(e,t){"use strict";"object"==typeof module&&"object"==typeof module.exports?module.exports=e.document?t(e,!0):function(e){if(!e.document)throw new Error("jQuery requires a window with a document");return t(e)}:t(e)}("undefined"!=typeof window?window:this,function(C,e){"use strict";var t=[],r=Object.getPrototypeOf,s=t.slice,g=t.flat?function(e){return t.flat.call(e)}:function(e){return t.concat.apply([],e)},u=t.push,i=t.indexOf,n={},o=n.toString,v=n.hasOwnProperty,a=v.toString,l=a.call(Object),y={},m=function(e){return"function"==typeof e&&"number"!=typeof e.nodeType&&"function"!=typeof e.item},x=function(e){return null!=e&&e===e.window},E=C.document,c={type:!0,src:!0,nonce:!0,noModule:!0};function b(e,t,n){var r,i,o=(n=n||E).createElement("script");if(o.text=e,t)for(r in c)(i=t[r]||t.getAttribute&&t.getAttribute(r))&&o.setAttribute(r,i);n.head.appendChild(o).parentNode.removeChild(o)}function w(e){return null==e?e+"":"object"==typeof e||"function"==typeof e?n[o.call(e)]||"object":typeof e}var f="3.6.0",S=function(e,t){return new S.fn.init(e,t)};function p(e){var t=!!e&&"length"in e&&e.length,n=w(e);return!m(e)&&!x(e)&&("array"===n||0===t||"number"==typeof t&&0<t&&t-1 in e)}S.fn=S.prototype={jquery:f,constructor:S,length:0,toArray:function(){return s.call(this)},get:function(e){return null==e?s.call(this):e<0?this[e+this.length]:this[e]},pushStack:function(e){var t=S.merge(this.constructor(),e);return t.prevObject=this,t},each:function(e){return S.each(this,e)},map:function(n){return this.pushStack(S.map(this,function(e,t){return n.call(e,t,e)}))},slice:function(){return this.pushStack(s.apply(this,arguments))},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},even:function(){return this.pushStack(S.grep(this,function(e,t){return(t+1)%2}))},odd:function(){return this.pushStack(S.grep(this,function(e,t){return t%2}))},eq:function(e){var t=this.length,n=+e+(e<0?t:0);return this.pushStack(0<=n&&n<t?[this[n]]:[])},end:function(){return this.prevObject||this.constructor()},push:u,sort:t.sort,splice:t.splice},S.extend=S.fn.extend=function(){var e,t,n,r,i,o,a=arguments[0]||{},s=1,u=arguments.length,l=!1;for("boolean"==typeof a&&(l=a,a=arguments[s]||{},s++),"object"==typeof a||m(a)||(a={}),s===u&&(a=this,s--);s<u;s++)if(null!=(e=arguments[s]))for(t in e)r=e[t],"__proto__"!==t&&a!==r&&(l&&r&&(S.isPlainObject(r)||(i=Array.isArray(r)))?(n=a[t],o=i&&!Array.isArray(n)?[]:i||S.isPlainObject(n)?n:{},i=!1,a[t]=S.extend(l,o,r)):void 0!==r&&(a[t]=r));return a},S.extend({expando:"jQuery"+(f+Math.random()).replace(/\D/g,""),isReady:!0,error:function(e){throw new Error(e)},noop:function(){},isPlainObject:function(e){var t,n;return!(!e||"[object Object]"!==o.call(e))&&(!(t=r(e))||"function"==typeof(n=v.call(t,"constructor")&&t.constructor)&&a.call(n)===l)},isEmptyObject:function(e){var t;for(t in e)return!1;return!0},globalEval:function(e,t,n){b(e,{nonce:t&&t.nonce},n)},each:function(e,t){var n,r=0;if(p(e)){for(n=e.length;r<n;r++)if(!1===t.call(e[r],r,e[r]))break}else for(r in e)if(!1===t.call(e[r],r,e[r]))break;return e},makeArray:function(e,t){var n=t||[];return null!=e&&(p(Object(e))?S.merge(n,"string"==typeof e?[e]:e):u.call(n,e)),n},inArray:function(e,t,n){return null==t?-1:i.call(t,e,n)},merge:function(e,t){for(var n=+t.length,r=0,i=e.length;r<n;r++)e[i++]=t[r];return e.length=i,e},grep:function(e,t,n){for(var r=[],i=0,o=e.length,a=!n;i<o;i++)!t(e[i],i)!==a&&r.push(e[i]);return r},map:function(e,t,n){var r,i,o=0,a=[];if(p(e))for(r=e.length;o<r;o++)null!=(i=t(e[o],o,n))&&a.push(i);else for(o in e)null!=(i=t(e[o],o,n))&&a.push(i);return g(a)},guid:1,support:y}),"function"==typeof Symbol&&(S.fn[Symbol.iterator]=t[Symbol.iterator]),S.each("Boolean Number String Function Array Date RegExp Object Error Symbol".split(" "),function(e,t){n["[object "+t+"]"]=t.toLowerCase()});var d=function(n){var e,d,b,o,i,h,f,g,w,u,l,T,C,a,E,v,s,c,y,S="sizzle"+1*new Date,p=n.document,k=0,r=0,m=ue(),x=ue(),A=ue(),N=ue(),j=function(e,t){return e===t&&(l=!0),0},D={}.hasOwnProperty,t=[],q=t.pop,L=t.push,H=t.push,O=t.slice,P=function(e,t){for(var n=0,r=e.length;n<r;n++)if(e[n]===t)return n;return-1},R="checked|selected|async|autofocus|autoplay|controls|defer|disabled|hidden|ismap|loop|multiple|open|readonly|required|scoped",M="[\\x20\\t\\r\\n\\f]",I="(?:\\\\[\\da-fA-F]{1,6}"+M+"?|\\\\[^\\r\\n\\f]|[\\w-]|[^\0-\\x7f])+",W="\\["+M+"*("+I+")(?:"+M+"*([*^$|!~]?=)"+M+"*(?:'((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\"|("+I+"))|)"+M+"*\\]",F=":("+I+")(?:\\((('((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\")|((?:\\\\.|[^\\\\()[\\]]|"+W+")*)|.*)\\)|)",B=new RegExp(M+"+","g"),$=new RegExp("^"+M+"+|((?:^|[^\\\\])(?:\\\\.)*)"+M+"+$","g"),_=new RegExp("^"+M+"*,"+M+"*"),z=new RegExp("^"+M+"*([>+~]|"+M+")"+M+"*"),U=new RegExp(M+"|>"),X=new RegExp(F),V=new RegExp("^"+I+"$"),G={ID:new RegExp("^#("+I+")"),CLASS:new RegExp("^\\.("+I+")"),TAG:new RegExp("^("+I+"|[*])"),ATTR:new RegExp("^"+W),PSEUDO:new RegExp("^"+F),CHILD:new RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+M+"*(even|odd|(([+-]|)(\\d*)n|)"+M+"*(?:([+-]|)"+M+"*(\\d+)|))"+M+"*\\)|)","i"),bool:new RegExp("^(?:"+R+")$","i"),needsContext:new RegExp("^"+M+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+M+"*((?:-\\d)?\\d*)"+M+"*\\)|)(?=[^-]|$)","i")},Y=/HTML$/i,Q=/^(?:input|select|textarea|button)$/i,J=/^h\d$/i,K=/^[^{]+\{\s*\[native \w/,Z=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,ee=/[+~]/,te=new RegExp("\\\\[\\da-fA-F]{1,6}"+M+"?|\\\\([^\\r\\n\\f])","g"),ne=function(e,t){var n="0x"+e.slice(1)-65536;return t||(n<0?String.fromCharCode(n+65536):String.fromCharCode(n>>10|55296,1023&n|56320))},re=/([\0-\x1f\x7f]|^-?\d)|^-$|[^\0-\x1f\x7f-\uFFFF\w-]/g,ie=function(e,t){return t?"\0"===e?"\ufffd":e.slice(0,-1)+"\\"+e.charCodeAt(e.length-1).toString(16)+" ":"\\"+e},oe=function(){T()},ae=be(function(e){return!0===e.disabled&&"fieldset"===e.nodeName.toLowerCase()},{dir:"parentNode",next:"legend"});try{H.apply(t=O.call(p.childNodes),p.childNodes),t[p.childNodes.length].nodeType}catch(e){H={apply:t.length?function(e,t){L.apply(e,O.call(t))}:function(e,t){var n=e.length,r=0;while(e[n++]=t[r++]);e.length=n-1}}}function se(t,e,n,r){var i,o,a,s,u,l,c,f=e&&e.ownerDocument,p=e?e.nodeType:9;if(n=n||[],"string"!=typeof t||!t||1!==p&&9!==p&&11!==p)return n;if(!r&&(T(e),e=e||C,E)){if(11!==p&&(u=Z.exec(t)))if(i=u[1]){if(9===p){if(!(a=e.getElementById(i)))return n;if(a.id===i)return n.push(a),n}else if(f&&(a=f.getElementById(i))&&y(e,a)&&a.id===i)return n.push(a),n}else{if(u[2])return H.apply(n,e.getElementsByTagName(t)),n;if((i=u[3])&&d.getElementsByClassName&&e.getElementsByClassName)return H.apply(n,e.getElementsByClassName(i)),n}if(d.qsa&&!N[t+" "]&&(!v||!v.test(t))&&(1!==p||"object"!==e.nodeName.toLowerCase())){if(c=t,f=e,1===p&&(U.test(t)||z.test(t))){(f=ee.test(t)&&ye(e.parentNode)||e)===e&&d.scope||((s=e.getAttribute("id"))?s=s.replace(re,ie):e.setAttribute("id",s=S)),o=(l=h(t)).length;while(o--)l[o]=(s?"#"+s:":scope")+" "+xe(l[o]);c=l.join(",")}try{return H.apply(n,f.querySelectorAll(c)),n}catch(e){N(t,!0)}finally{s===S&&e.removeAttribute("id")}}}return g(t.replace($,"$1"),e,n,r)}function ue(){var r=[];return function e(t,n){return r.push(t+" ")>b.cacheLength&&delete e[r.shift()],e[t+" "]=n}}function le(e){return e[S]=!0,e}function ce(e){var t=C.createElement("fieldset");try{return!!e(t)}catch(e){return!1}finally{t.parentNode&&t.parentNode.removeChild(t),t=null}}function fe(e,t){var n=e.split("|"),r=n.length;while(r--)b.attrHandle[n[r]]=t}function pe(e,t){var n=t&&e,r=n&&1===e.nodeType&&1===t.nodeType&&e.sourceIndex-t.sourceIndex;if(r)return r;if(n)while(n=n.nextSibling)if(n===t)return-1;return e?1:-1}function de(t){return function(e){return"input"===e.nodeName.toLowerCase()&&e.type===t}}function he(n){return function(e){var t=e.nodeName.toLowerCase();return("input"===t||"button"===t)&&e.type===n}}function ge(t){return function(e){return"form"in e?e.parentNode&&!1===e.disabled?"label"in e?"label"in e.parentNode?e.parentNode.disabled===t:e.disabled===t:e.isDisabled===t||e.isDisabled!==!t&&ae(e)===t:e.disabled===t:"label"in e&&e.disabled===t}}function ve(a){return le(function(o){return o=+o,le(function(e,t){var n,r=a([],e.length,o),i=r.length;while(i--)e[n=r[i]]&&(e[n]=!(t[n]=e[n]))})})}function ye(e){return e&&"undefined"!=typeof e.getElementsByTagName&&e}for(e in d=se.support={},i=se.isXML=function(e){var t=e&&e.namespaceURI,n=e&&(e.ownerDocument||e).documentElement;return!Y.test(t||n&&n.nodeName||"HTML")},T=se.setDocument=function(e){var t,n,r=e?e.ownerDocument||e:p;return r!=C&&9===r.nodeType&&r.documentElement&&(a=(C=r).documentElement,E=!i(C),p!=C&&(n=C.defaultView)&&n.top!==n&&(n.addEventListener?n.addEventListener("unload",oe,!1):n.attachEvent&&n.attachEvent("onunload",oe)),d.scope=ce(function(e){return a.appendChild(e).appendChild(C.createElement("div")),"undefined"!=typeof e.querySelectorAll&&!e.querySelectorAll(":scope fieldset div").length}),d.attributes=ce(function(e){return e.className="i",!e.getAttribute("className")}),d.getElementsByTagName=ce(function(e){return e.appendChild(C.createComment("")),!e.getElementsByTagName("*").length}),d.getElementsByClassName=K.test(C.getElementsByClassName),d.getById=ce(function(e){return a.appendChild(e).id=S,!C.getElementsByName||!C.getElementsByName(S).length}),d.getById?(b.filter.ID=function(e){var t=e.replace(te,ne);return function(e){return e.getAttribute("id")===t}},b.find.ID=function(e,t){if("undefined"!=typeof t.getElementById&&E){var n=t.getElementById(e);return n?[n]:[]}}):(b.filter.ID=function(e){var n=e.replace(te,ne);return function(e){var t="undefined"!=typeof e.getAttributeNode&&e.getAttributeNode("id");return t&&t.value===n}},b.find.ID=function(e,t){if("undefined"!=typeof t.getElementById&&E){var n,r,i,o=t.getElementById(e);if(o){if((n=o.getAttributeNode("id"))&&n.value===e)return[o];i=t.getElementsByName(e),r=0;while(o=i[r++])if((n=o.getAttributeNode("id"))&&n.value===e)return[o]}return[]}}),b.find.TAG=d.getElementsByTagName?function(e,t){return"undefined"!=typeof t.getElementsByTagName?t.getElementsByTagName(e):d.qsa?t.querySelectorAll(e):void 0}:function(e,t){var n,r=[],i=0,o=t.getElementsByTagName(e);if("*"===e){while(n=o[i++])1===n.nodeType&&r.push(n);return r}return o},b.find.CLASS=d.getElementsByClassName&&function(e,t){if("undefined"!=typeof t.getElementsByClassName&&E)return t.getElementsByClassName(e)},s=[],v=[],(d.qsa=K.test(C.querySelectorAll))&&(ce(function(e){var t;a.appendChild(e).innerHTML="<a id='"+S+"'></a><select id='"+S+"-\r\\' msallowcapture=''><option selected=''></option></select>",e.querySelectorAll("[msallowcapture^='']").length&&v.push("[*^$]="+M+"*(?:''|\"\")"),e.querySelectorAll("[selected]").length||v.push("\\["+M+"*(?:value|"+R+")"),e.querySelectorAll("[id~="+S+"-]").length||v.push("~="),(t=C.createElement("input")).setAttribute("name",""),e.appendChild(t),e.querySelectorAll("[name='']").length||v.push("\\["+M+"*name"+M+"*="+M+"*(?:''|\"\")"),e.querySelectorAll(":checked").length||v.push(":checked"),e.querySelectorAll("a#"+S+"+*").length||v.push(".#.+[+~]"),e.querySelectorAll("\\\f"),v.push("[\\r\\n\\f]")}),ce(function(e){e.innerHTML="<a href='' disabled='disabled'></a><select disabled='disabled'><option/></select>";var t=C.createElement("input");t.setAttribute("type","hidden"),e.appendChild(t).setAttribute("name","D"),e.querySelectorAll("[name=d]").length&&v.push("name"+M+"*[*^$|!~]?="),2!==e.querySelectorAll(":enabled").length&&v.push(":enabled",":disabled"),a.appendChild(e).disabled=!0,2!==e.querySelectorAll(":disabled").length&&v.push(":enabled",":disabled"),e.querySelectorAll("*,:x"),v.push(",.*:")})),(d.matchesSelector=K.test(c=a.matches||a.webkitMatchesSelector||a.mozMatchesSelector||a.oMatchesSelector||a.msMatchesSelector))&&ce(function(e){d.disconnectedMatch=c.call(e,"*"),c.call(e,"[s!='']:x"),s.push("!=",F)}),v=v.length&&new RegExp(v.join("|")),s=s.length&&new RegExp(s.join("|")),t=K.test(a.compareDocumentPosition),y=t||K.test(a.contains)?function(e,t){var n=9===e.nodeType?e.documentElement:e,r=t&&t.parentNode;return e===r||!(!r||1!==r.nodeType||!(n.contains?n.contains(r):e.compareDocumentPosition&&16&e.compareDocumentPosition(r)))}:function(e,t){if(t)while(t=t.parentNode)if(t===e)return!0;return!1},j=t?function(e,t){if(e===t)return l=!0,0;var n=!e.compareDocumentPosition-!t.compareDocumentPosition;return n||(1&(n=(e.ownerDocument||e)==(t.ownerDocument||t)?e.compareDocumentPosition(t):1)||!d.sortDetached&&t.compareDocumentPosition(e)===n?e==C||e.ownerDocument==p&&y(p,e)?-1:t==C||t.ownerDocument==p&&y(p,t)?1:u?P(u,e)-P(u,t):0:4&n?-1:1)}:function(e,t){if(e===t)return l=!0,0;var n,r=0,i=e.parentNode,o=t.parentNode,a=[e],s=[t];if(!i||!o)return e==C?-1:t==C?1:i?-1:o?1:u?P(u,e)-P(u,t):0;if(i===o)return pe(e,t);n=e;while(n=n.parentNode)a.unshift(n);n=t;while(n=n.parentNode)s.unshift(n);while(a[r]===s[r])r++;return r?pe(a[r],s[r]):a[r]==p?-1:s[r]==p?1:0}),C},se.matches=function(e,t){return se(e,null,null,t)},se.matchesSelector=function(e,t){if(T(e),d.matchesSelector&&E&&!N[t+" "]&&(!s||!s.test(t))&&(!v||!v.test(t)))try{var n=c.call(e,t);if(n||d.disconnectedMatch||e.document&&11!==e.document.nodeType)return n}catch(e){N(t,!0)}return 0<se(t,C,null,[e]).length},se.contains=function(e,t){return(e.ownerDocument||e)!=C&&T(e),y(e,t)},se.attr=function(e,t){(e.ownerDocument||e)!=C&&T(e);var n=b.attrHandle[t.toLowerCase()],r=n&&D.call(b.attrHandle,t.toLowerCase())?n(e,t,!E):void 0;return void 0!==r?r:d.attributes||!E?e.getAttribute(t):(r=e.getAttributeNode(t))&&r.specified?r.value:null},se.escape=function(e){return(e+"").replace(re,ie)},se.error=function(e){throw new Error("Syntax error, unrecognized expression: "+e)},se.uniqueSort=function(e){var t,n=[],r=0,i=0;if(l=!d.detectDuplicates,u=!d.sortStable&&e.slice(0),e.sort(j),l){while(t=e[i++])t===e[i]&&(r=n.push(i));while(r--)e.splice(n[r],1)}return u=null,e},o=se.getText=function(e){var t,n="",r=0,i=e.nodeType;if(i){if(1===i||9===i||11===i){if("string"==typeof e.textContent)return e.textContent;for(e=e.firstChild;e;e=e.nextSibling)n+=o(e)}else if(3===i||4===i)return e.nodeValue}else while(t=e[r++])n+=o(t);return n},(b=se.selectors={cacheLength:50,createPseudo:le,match:G,attrHandle:{},find:{},relative:{">":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(e){return e[1]=e[1].replace(te,ne),e[3]=(e[3]||e[4]||e[5]||"").replace(te,ne),"~="===e[2]&&(e[3]=" "+e[3]+" "),e.slice(0,4)},CHILD:function(e){return e[1]=e[1].toLowerCase(),"nth"===e[1].slice(0,3)?(e[3]||se.error(e[0]),e[4]=+(e[4]?e[5]+(e[6]||1):2*("even"===e[3]||"odd"===e[3])),e[5]=+(e[7]+e[8]||"odd"===e[3])):e[3]&&se.error(e[0]),e},PSEUDO:function(e){var t,n=!e[6]&&e[2];return G.CHILD.test(e[0])?null:(e[3]?e[2]=e[4]||e[5]||"":n&&X.test(n)&&(t=h(n,!0))&&(t=n.indexOf(")",n.length-t)-n.length)&&(e[0]=e[0].slice(0,t),e[2]=n.slice(0,t)),e.slice(0,3))}},filter:{TAG:function(e){var t=e.replace(te,ne).toLowerCase();return"*"===e?function(){return!0}:function(e){return e.nodeName&&e.nodeName.toLowerCase()===t}},CLASS:function(e){var t=m[e+" "];return t||(t=new RegExp("(^|"+M+")"+e+"("+M+"|$)"))&&m(e,function(e){return t.test("string"==typeof e.className&&e.className||"undefined"!=typeof e.getAttribute&&e.getAttribute("class")||"")})},ATTR:function(n,r,i){return function(e){var t=se.attr(e,n);return null==t?"!="===r:!r||(t+="","="===r?t===i:"!="===r?t!==i:"^="===r?i&&0===t.indexOf(i):"*="===r?i&&-1<t.indexOf(i):"$="===r?i&&t.slice(-i.length)===i:"~="===r?-1<(" "+t.replace(B," ")+" ").indexOf(i):"|="===r&&(t===i||t.slice(0,i.length+1)===i+"-"))}},CHILD:function(h,e,t,g,v){var y="nth"!==h.slice(0,3),m="last"!==h.slice(-4),x="of-type"===e;return 1===g&&0===v?function(e){return!!e.parentNode}:function(e,t,n){var r,i,o,a,s,u,l=y!==m?"nextSibling":"previousSibling",c=e.parentNode,f=x&&e.nodeName.toLowerCase(),p=!n&&!x,d=!1;if(c){if(y){while(l){a=e;while(a=a[l])if(x?a.nodeName.toLowerCase()===f:1===a.nodeType)return!1;u=l="only"===h&&!u&&"nextSibling"}return!0}if(u=[m?c.firstChild:c.lastChild],m&&p){d=(s=(r=(i=(o=(a=c)[S]||(a[S]={}))[a.uniqueID]||(o[a.uniqueID]={}))[h]||[])[0]===k&&r[1])&&r[2],a=s&&c.childNodes[s];while(a=++s&&a&&a[l]||(d=s=0)||u.pop())if(1===a.nodeType&&++d&&a===e){i[h]=[k,s,d];break}}else if(p&&(d=s=(r=(i=(o=(a=e)[S]||(a[S]={}))[a.uniqueID]||(o[a.uniqueID]={}))[h]||[])[0]===k&&r[1]),!1===d)while(a=++s&&a&&a[l]||(d=s=0)||u.pop())if((x?a.nodeName.toLowerCase()===f:1===a.nodeType)&&++d&&(p&&((i=(o=a[S]||(a[S]={}))[a.uniqueID]||(o[a.uniqueID]={}))[h]=[k,d]),a===e))break;return(d-=v)===g||d%g==0&&0<=d/g}}},PSEUDO:function(e,o){var t,a=b.pseudos[e]||b.setFilters[e.toLowerCase()]||se.error("unsupported pseudo: "+e);return a[S]?a(o):1<a.length?(t=[e,e,"",o],b.setFilters.hasOwnProperty(e.toLowerCase())?le(function(e,t){var n,r=a(e,o),i=r.length;while(i--)e[n=P(e,r[i])]=!(t[n]=r[i])}):function(e){return a(e,0,t)}):a}},pseudos:{not:le(function(e){var r=[],i=[],s=f(e.replace($,"$1"));return s[S]?le(function(e,t,n,r){var i,o=s(e,null,r,[]),a=e.length;while(a--)(i=o[a])&&(e[a]=!(t[a]=i))}):function(e,t,n){return r[0]=e,s(r,null,n,i),r[0]=null,!i.pop()}}),has:le(function(t){return function(e){return 0<se(t,e).length}}),contains:le(function(t){return t=t.replace(te,ne),function(e){return-1<(e.textContent||o(e)).indexOf(t)}}),lang:le(function(n){return V.test(n||"")||se.error("unsupported lang: "+n),n=n.replace(te,ne).toLowerCase(),function(e){var t;do{if(t=E?e.lang:e.getAttribute("xml:lang")||e.getAttribute("lang"))return(t=t.toLowerCase())===n||0===t.indexOf(n+"-")}while((e=e.parentNode)&&1===e.nodeType);return!1}}),target:function(e){var t=n.location&&n.location.hash;return t&&t.slice(1)===e.id},root:function(e){return e===a},focus:function(e){return e===C.activeElement&&(!C.hasFocus||C.hasFocus())&&!!(e.type||e.href||~e.tabIndex)},enabled:ge(!1),disabled:ge(!0),checked:function(e){var t=e.nodeName.toLowerCase();return"input"===t&&!!e.checked||"option"===t&&!!e.selected},selected:function(e){return e.parentNode&&e.parentNode.selectedIndex,!0===e.selected},empty:function(e){for(e=e.firstChild;e;e=e.nextSibling)if(e.nodeType<6)return!1;return!0},parent:function(e){return!b.pseudos.empty(e)},header:function(e){return J.test(e.nodeName)},input:function(e){return Q.test(e.nodeName)},button:function(e){var t=e.nodeName.toLowerCase();return"input"===t&&"button"===e.type||"button"===t},text:function(e){var t;return"input"===e.nodeName.toLowerCase()&&"text"===e.type&&(null==(t=e.getAttribute("type"))||"text"===t.toLowerCase())},first:ve(function(){return[0]}),last:ve(function(e,t){return[t-1]}),eq:ve(function(e,t,n){return[n<0?n+t:n]}),even:ve(function(e,t){for(var n=0;n<t;n+=2)e.push(n);return e}),odd:ve(function(e,t){for(var n=1;n<t;n+=2)e.push(n);return e}),lt:ve(function(e,t,n){for(var r=n<0?n+t:t<n?t:n;0<=--r;)e.push(r);return e}),gt:ve(function(e,t,n){for(var r=n<0?n+t:n;++r<t;)e.push(r);return e})}}).pseudos.nth=b.pseudos.eq,{radio:!0,checkbox:!0,file:!0,password:!0,image:!0})b.pseudos[e]=de(e);for(e in{submit:!0,reset:!0})b.pseudos[e]=he(e);function me(){}function xe(e){for(var t=0,n=e.length,r="";t<n;t++)r+=e[t].value;return r}function be(s,e,t){var u=e.dir,l=e.next,c=l||u,f=t&&"parentNode"===c,p=r++;return e.first?function(e,t,n){while(e=e[u])if(1===e.nodeType||f)return s(e,t,n);return!1}:function(e,t,n){var r,i,o,a=[k,p];if(n){while(e=e[u])if((1===e.nodeType||f)&&s(e,t,n))return!0}else while(e=e[u])if(1===e.nodeType||f)if(i=(o=e[S]||(e[S]={}))[e.uniqueID]||(o[e.uniqueID]={}),l&&l===e.nodeName.toLowerCase())e=e[u]||e;else{if((r=i[c])&&r[0]===k&&r[1]===p)return a[2]=r[2];if((i[c]=a)[2]=s(e,t,n))return!0}return!1}}function we(i){return 1<i.length?function(e,t,n){var r=i.length;while(r--)if(!i[r](e,t,n))return!1;return!0}:i[0]}function Te(e,t,n,r,i){for(var o,a=[],s=0,u=e.length,l=null!=t;s<u;s++)(o=e[s])&&(n&&!n(o,r,i)||(a.push(o),l&&t.push(s)));return a}function Ce(d,h,g,v,y,e){return v&&!v[S]&&(v=Ce(v)),y&&!y[S]&&(y=Ce(y,e)),le(function(e,t,n,r){var i,o,a,s=[],u=[],l=t.length,c=e||function(e,t,n){for(var r=0,i=t.length;r<i;r++)se(e,t[r],n);return n}(h||"*",n.nodeType?[n]:n,[]),f=!d||!e&&h?c:Te(c,s,d,n,r),p=g?y||(e?d:l||v)?[]:t:f;if(g&&g(f,p,n,r),v){i=Te(p,u),v(i,[],n,r),o=i.length;while(o--)(a=i[o])&&(p[u[o]]=!(f[u[o]]=a))}if(e){if(y||d){if(y){i=[],o=p.length;while(o--)(a=p[o])&&i.push(f[o]=a);y(null,p=[],i,r)}o=p.length;while(o--)(a=p[o])&&-1<(i=y?P(e,a):s[o])&&(e[i]=!(t[i]=a))}}else p=Te(p===t?p.splice(l,p.length):p),y?y(null,t,p,r):H.apply(t,p)})}function Ee(e){for(var i,t,n,r=e.length,o=b.relative[e[0].type],a=o||b.relative[" "],s=o?1:0,u=be(function(e){return e===i},a,!0),l=be(function(e){return-1<P(i,e)},a,!0),c=[function(e,t,n){var r=!o&&(n||t!==w)||((i=t).nodeType?u(e,t,n):l(e,t,n));return i=null,r}];s<r;s++)if(t=b.relative[e[s].type])c=[be(we(c),t)];else{if((t=b.filter[e[s].type].apply(null,e[s].matches))[S]){for(n=++s;n<r;n++)if(b.relative[e[n].type])break;return Ce(1<s&&we(c),1<s&&xe(e.slice(0,s-1).concat({value:" "===e[s-2].type?"*":""})).replace($,"$1"),t,s<n&&Ee(e.slice(s,n)),n<r&&Ee(e=e.slice(n)),n<r&&xe(e))}c.push(t)}return we(c)}return me.prototype=b.filters=b.pseudos,b.setFilters=new me,h=se.tokenize=function(e,t){var n,r,i,o,a,s,u,l=x[e+" "];if(l)return t?0:l.slice(0);a=e,s=[],u=b.preFilter;while(a){for(o in n&&!(r=_.exec(a))||(r&&(a=a.slice(r[0].length)||a),s.push(i=[])),n=!1,(r=z.exec(a))&&(n=r.shift(),i.push({value:n,type:r[0].replace($," ")}),a=a.slice(n.length)),b.filter)!(r=G[o].exec(a))||u[o]&&!(r=u[o](r))||(n=r.shift(),i.push({value:n,type:o,matches:r}),a=a.slice(n.length));if(!n)break}return t?a.length:a?se.error(e):x(e,s).slice(0)},f=se.compile=function(e,t){var n,v,y,m,x,r,i=[],o=[],a=A[e+" "];if(!a){t||(t=h(e)),n=t.length;while(n--)(a=Ee(t[n]))[S]?i.push(a):o.push(a);(a=A(e,(v=o,m=0<(y=i).length,x=0<v.length,r=function(e,t,n,r,i){var o,a,s,u=0,l="0",c=e&&[],f=[],p=w,d=e||x&&b.find.TAG("*",i),h=k+=null==p?1:Math.random()||.1,g=d.length;for(i&&(w=t==C||t||i);l!==g&&null!=(o=d[l]);l++){if(x&&o){a=0,t||o.ownerDocument==C||(T(o),n=!E);while(s=v[a++])if(s(o,t||C,n)){r.push(o);break}i&&(k=h)}m&&((o=!s&&o)&&u--,e&&c.push(o))}if(u+=l,m&&l!==u){a=0;while(s=y[a++])s(c,f,t,n);if(e){if(0<u)while(l--)c[l]||f[l]||(f[l]=q.call(r));f=Te(f)}H.apply(r,f),i&&!e&&0<f.length&&1<u+y.length&&se.uniqueSort(r)}return i&&(k=h,w=p),c},m?le(r):r))).selector=e}return a},g=se.select=function(e,t,n,r){var i,o,a,s,u,l="function"==typeof e&&e,c=!r&&h(e=l.selector||e);if(n=n||[],1===c.length){if(2<(o=c[0]=c[0].slice(0)).length&&"ID"===(a=o[0]).type&&9===t.nodeType&&E&&b.relative[o[1].type]){if(!(t=(b.find.ID(a.matches[0].replace(te,ne),t)||[])[0]))return n;l&&(t=t.parentNode),e=e.slice(o.shift().value.length)}i=G.needsContext.test(e)?0:o.length;while(i--){if(a=o[i],b.relative[s=a.type])break;if((u=b.find[s])&&(r=u(a.matches[0].replace(te,ne),ee.test(o[0].type)&&ye(t.parentNode)||t))){if(o.splice(i,1),!(e=r.length&&xe(o)))return H.apply(n,r),n;break}}}return(l||f(e,c))(r,t,!E,n,!t||ee.test(e)&&ye(t.parentNode)||t),n},d.sortStable=S.split("").sort(j).join("")===S,d.detectDuplicates=!!l,T(),d.sortDetached=ce(function(e){return 1&e.compareDocumentPosition(C.createElement("fieldset"))}),ce(function(e){return e.innerHTML="<a href='#'></a>","#"===e.firstChild.getAttribute("href")})||fe("type|href|height|width",function(e,t,n){if(!n)return e.getAttribute(t,"type"===t.toLowerCase()?1:2)}),d.attributes&&ce(function(e){return e.innerHTML="<input/>",e.firstChild.setAttribute("value",""),""===e.firstChild.getAttribute("value")})||fe("value",function(e,t,n){if(!n&&"input"===e.nodeName.toLowerCase())return e.defaultValue}),ce(function(e){return null==e.getAttribute("disabled")})||fe(R,function(e,t,n){var r;if(!n)return!0===e[t]?t.toLowerCase():(r=e.getAttributeNode(t))&&r.specified?r.value:null}),se}(C);S.find=d,S.expr=d.selectors,S.expr[":"]=S.expr.pseudos,S.uniqueSort=S.unique=d.uniqueSort,S.text=d.getText,S.isXMLDoc=d.isXML,S.contains=d.contains,S.escapeSelector=d.escape;var h=function(e,t,n){var r=[],i=void 0!==n;while((e=e[t])&&9!==e.nodeType)if(1===e.nodeType){if(i&&S(e).is(n))break;r.push(e)}return r},T=function(e,t){for(var n=[];e;e=e.nextSibling)1===e.nodeType&&e!==t&&n.push(e);return n},k=S.expr.match.needsContext;function A(e,t){return e.nodeName&&e.nodeName.toLowerCase()===t.toLowerCase()}var N=/^<([a-z][^\/\0>:\x20\t\r\n\f]*)[\x20\t\r\n\f]*\/?>(?:<\/\1>|)$/i;function j(e,n,r){return m(n)?S.grep(e,function(e,t){return!!n.call(e,t,e)!==r}):n.nodeType?S.grep(e,function(e){return e===n!==r}):"string"!=typeof n?S.grep(e,function(e){return-1<i.call(n,e)!==r}):S.filter(n,e,r)}S.filter=function(e,t,n){var r=t[0];return n&&(e=":not("+e+")"),1===t.length&&1===r.nodeType?S.find.matchesSelector(r,e)?[r]:[]:S.find.matches(e,S.grep(t,function(e){return 1===e.nodeType}))},S.fn.extend({find:function(e){var t,n,r=this.length,i=this;if("string"!=typeof e)return this.pushStack(S(e).filter(function(){for(t=0;t<r;t++)if(S.contains(i[t],this))return!0}));for(n=this.pushStack([]),t=0;t<r;t++)S.find(e,i[t],n);return 1<r?S.uniqueSort(n):n},filter:function(e){return this.pushStack(j(this,e||[],!1))},not:function(e){return this.pushStack(j(this,e||[],!0))},is:function(e){return!!j(this,"string"==typeof e&&k.test(e)?S(e):e||[],!1).length}});var D,q=/^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]+))$/;(S.fn.init=function(e,t,n){var r,i;if(!e)return this;if(n=n||D,"string"==typeof e){if(!(r="<"===e[0]&&">"===e[e.length-1]&&3<=e.length?[null,e,null]:q.exec(e))||!r[1]&&t)return!t||t.jquery?(t||n).find(e):this.constructor(t).find(e);if(r[1]){if(t=t instanceof S?t[0]:t,S.merge(this,S.parseHTML(r[1],t&&t.nodeType?t.ownerDocument||t:E,!0)),N.test(r[1])&&S.isPlainObject(t))for(r in t)m(this[r])?this[r](t[r]):this.attr(r,t[r]);return this}return(i=E.getElementById(r[2]))&&(this[0]=i,this.length=1),this}return e.nodeType?(this[0]=e,this.length=1,this):m(e)?void 0!==n.ready?n.ready(e):e(S):S.makeArray(e,this)}).prototype=S.fn,D=S(E);var L=/^(?:parents|prev(?:Until|All))/,H={children:!0,contents:!0,next:!0,prev:!0};function O(e,t){while((e=e[t])&&1!==e.nodeType);return e}S.fn.extend({has:function(e){var t=S(e,this),n=t.length;return this.filter(function(){for(var e=0;e<n;e++)if(S.contains(this,t[e]))return!0})},closest:function(e,t){var n,r=0,i=this.length,o=[],a="string"!=typeof e&&S(e);if(!k.test(e))for(;r<i;r++)for(n=this[r];n&&n!==t;n=n.parentNode)if(n.nodeType<11&&(a?-1<a.index(n):1===n.nodeType&&S.find.matchesSelector(n,e))){o.push(n);break}return this.pushStack(1<o.length?S.uniqueSort(o):o)},index:function(e){return e?"string"==typeof e?i.call(S(e),this[0]):i.call(this,e.jquery?e[0]:e):this[0]&&this[0].parentNode?this.first().prevAll().length:-1},add:function(e,t){return this.pushStack(S.uniqueSort(S.merge(this.get(),S(e,t))))},addBack:function(e){return this.add(null==e?this.prevObject:this.prevObject.filter(e))}}),S.each({parent:function(e){var t=e.parentNode;return t&&11!==t.nodeType?t:null},parents:function(e){return h(e,"parentNode")},parentsUntil:function(e,t,n){return h(e,"parentNode",n)},next:function(e){return O(e,"nextSibling")},prev:function(e){return O(e,"previousSibling")},nextAll:function(e){return h(e,"nextSibling")},prevAll:function(e){return h(e,"previousSibling")},nextUntil:function(e,t,n){return h(e,"nextSibling",n)},prevUntil:function(e,t,n){return h(e,"previousSibling",n)},siblings:function(e){return T((e.parentNode||{}).firstChild,e)},children:function(e){return T(e.firstChild)},contents:function(e){return null!=e.contentDocument&&r(e.contentDocument)?e.contentDocument:(A(e,"template")&&(e=e.content||e),S.merge([],e.childNodes))}},function(r,i){S.fn[r]=function(e,t){var n=S.map(this,i,e);return"Until"!==r.slice(-5)&&(t=e),t&&"string"==typeof t&&(n=S.filter(t,n)),1<this.length&&(H[r]||S.uniqueSort(n),L.test(r)&&n.reverse()),this.pushStack(n)}});var P=/[^\x20\t\r\n\f]+/g;function R(e){return e}function M(e){throw e}function I(e,t,n,r){var i;try{e&&m(i=e.promise)?i.call(e).done(t).fail(n):e&&m(i=e.then)?i.call(e,t,n):t.apply(void 0,[e].slice(r))}catch(e){n.apply(void 0,[e])}}S.Callbacks=function(r){var e,n;r="string"==typeof r?(e=r,n={},S.each(e.match(P)||[],function(e,t){n[t]=!0}),n):S.extend({},r);var i,t,o,a,s=[],u=[],l=-1,c=function(){for(a=a||r.once,o=i=!0;u.length;l=-1){t=u.shift();while(++l<s.length)!1===s[l].apply(t[0],t[1])&&r.stopOnFalse&&(l=s.length,t=!1)}r.memory||(t=!1),i=!1,a&&(s=t?[]:"")},f={add:function(){return s&&(t&&!i&&(l=s.length-1,u.push(t)),function n(e){S.each(e,function(e,t){m(t)?r.unique&&f.has(t)||s.push(t):t&&t.length&&"string"!==w(t)&&n(t)})}(arguments),t&&!i&&c()),this},remove:function(){return S.each(arguments,function(e,t){var n;while(-1<(n=S.inArray(t,s,n)))s.splice(n,1),n<=l&&l--}),this},has:function(e){return e?-1<S.inArray(e,s):0<s.length},empty:function(){return s&&(s=[]),this},disable:function(){return a=u=[],s=t="",this},disabled:function(){return!s},lock:function(){return a=u=[],t||i||(s=t=""),this},locked:function(){return!!a},fireWith:function(e,t){return a||(t=[e,(t=t||[]).slice?t.slice():t],u.push(t),i||c()),this},fire:function(){return f.fireWith(this,arguments),this},fired:function(){return!!o}};return f},S.extend({Deferred:function(e){var o=[["notify","progress",S.Callbacks("memory"),S.Callbacks("memory"),2],["resolve","done",S.Callbacks("once memory"),S.Callbacks("once memory"),0,"resolved"],["reject","fail",S.Callbacks("once memory"),S.Callbacks("once memory"),1,"rejected"]],i="pending",a={state:function(){return i},always:function(){return s.done(arguments).fail(arguments),this},"catch":function(e){return a.then(null,e)},pipe:function(){var i=arguments;return S.Deferred(function(r){S.each(o,function(e,t){var n=m(i[t[4]])&&i[t[4]];s[t[1]](function(){var e=n&&n.apply(this,arguments);e&&m(e.promise)?e.promise().progress(r.notify).done(r.resolve).fail(r.reject):r[t[0]+"With"](this,n?[e]:arguments)})}),i=null}).promise()},then:function(t,n,r){var u=0;function l(i,o,a,s){return function(){var n=this,r=arguments,e=function(){var e,t;if(!(i<u)){if((e=a.apply(n,r))===o.promise())throw new TypeError("Thenable self-resolution");t=e&&("object"==typeof e||"function"==typeof e)&&e.then,m(t)?s?t.call(e,l(u,o,R,s),l(u,o,M,s)):(u++,t.call(e,l(u,o,R,s),l(u,o,M,s),l(u,o,R,o.notifyWith))):(a!==R&&(n=void 0,r=[e]),(s||o.resolveWith)(n,r))}},t=s?e:function(){try{e()}catch(e){S.Deferred.exceptionHook&&S.Deferred.exceptionHook(e,t.stackTrace),u<=i+1&&(a!==M&&(n=void 0,r=[e]),o.rejectWith(n,r))}};i?t():(S.Deferred.getStackHook&&(t.stackTrace=S.Deferred.getStackHook()),C.setTimeout(t))}}return S.Deferred(function(e){o[0][3].add(l(0,e,m(r)?r:R,e.notifyWith)),o[1][3].add(l(0,e,m(t)?t:R)),o[2][3].add(l(0,e,m(n)?n:M))}).promise()},promise:function(e){return null!=e?S.extend(e,a):a}},s={};return S.each(o,function(e,t){var n=t[2],r=t[5];a[t[1]]=n.add,r&&n.add(function(){i=r},o[3-e][2].disable,o[3-e][3].disable,o[0][2].lock,o[0][3].lock),n.add(t[3].fire),s[t[0]]=function(){return s[t[0]+"With"](this===s?void 0:this,arguments),this},s[t[0]+"With"]=n.fireWith}),a.promise(s),e&&e.call(s,s),s},when:function(e){var n=arguments.length,t=n,r=Array(t),i=s.call(arguments),o=S.Deferred(),a=function(t){return function(e){r[t]=this,i[t]=1<arguments.length?s.call(arguments):e,--n||o.resolveWith(r,i)}};if(n<=1&&(I(e,o.done(a(t)).resolve,o.reject,!n),"pending"===o.state()||m(i[t]&&i[t].then)))return o.then();while(t--)I(i[t],a(t),o.reject);return o.promise()}});var W=/^(Eval|Internal|Range|Reference|Syntax|Type|URI)Error$/;S.Deferred.exceptionHook=function(e,t){C.console&&C.console.warn&&e&&W.test(e.name)&&C.console.warn("jQuery.Deferred exception: "+e.message,e.stack,t)},S.readyException=function(e){C.setTimeout(function(){throw e})};var F=S.Deferred();function B(){E.removeEventListener("DOMContentLoaded",B),C.removeEventListener("load",B),S.ready()}S.fn.ready=function(e){return F.then(e)["catch"](function(e){S.readyException(e)}),this},S.extend({isReady:!1,readyWait:1,ready:function(e){(!0===e?--S.readyWait:S.isReady)||(S.isReady=!0)!==e&&0<--S.readyWait||F.resolveWith(E,[S])}}),S.ready.then=F.then,"complete"===E.readyState||"loading"!==E.readyState&&!E.documentElement.doScroll?C.setTimeout(S.ready):(E.addEventListener("DOMContentLoaded",B),C.addEventListener("load",B));var $=function(e,t,n,r,i,o,a){var s=0,u=e.length,l=null==n;if("object"===w(n))for(s in i=!0,n)$(e,t,s,n[s],!0,o,a);else if(void 0!==r&&(i=!0,m(r)||(a=!0),l&&(a?(t.call(e,r),t=null):(l=t,t=function(e,t,n){return l.call(S(e),n)})),t))for(;s<u;s++)t(e[s],n,a?r:r.call(e[s],s,t(e[s],n)));return i?e:l?t.call(e):u?t(e[0],n):o},_=/^-ms-/,z=/-([a-z])/g;function U(e,t){return t.toUpperCase()}function X(e){return e.replace(_,"ms-").replace(z,U)}var V=function(e){return 1===e.nodeType||9===e.nodeType||!+e.nodeType};function G(){this.expando=S.expando+G.uid++}G.uid=1,G.prototype={cache:function(e){var t=e[this.expando];return t||(t={},V(e)&&(e.nodeType?e[this.expando]=t:Object.defineProperty(e,this.expando,{value:t,configurable:!0}))),t},set:function(e,t,n){var r,i=this.cache(e);if("string"==typeof t)i[X(t)]=n;else for(r in t)i[X(r)]=t[r];return i},get:function(e,t){return void 0===t?this.cache(e):e[this.expando]&&e[this.expando][X(t)]},access:function(e,t,n){return void 0===t||t&&"string"==typeof t&&void 0===n?this.get(e,t):(this.set(e,t,n),void 0!==n?n:t)},remove:function(e,t){var n,r=e[this.expando];if(void 0!==r){if(void 0!==t){n=(t=Array.isArray(t)?t.map(X):(t=X(t))in r?[t]:t.match(P)||[]).length;while(n--)delete r[t[n]]}(void 0===t||S.isEmptyObject(r))&&(e.nodeType?e[this.expando]=void 0:delete e[this.expando])}},hasData:function(e){var t=e[this.expando];return void 0!==t&&!S.isEmptyObject(t)}};var Y=new G,Q=new G,J=/^(?:\{[\w\W]*\}|\[[\w\W]*\])$/,K=/[A-Z]/g;function Z(e,t,n){var r,i;if(void 0===n&&1===e.nodeType)if(r="data-"+t.replace(K,"-$&").toLowerCase(),"string"==typeof(n=e.getAttribute(r))){try{n="true"===(i=n)||"false"!==i&&("null"===i?null:i===+i+""?+i:J.test(i)?JSON.parse(i):i)}catch(e){}Q.set(e,t,n)}else n=void 0;return n}S.extend({hasData:function(e){return Q.hasData(e)||Y.hasData(e)},data:function(e,t,n){return Q.access(e,t,n)},removeData:function(e,t){Q.remove(e,t)},_data:function(e,t,n){return Y.access(e,t,n)},_removeData:function(e,t){Y.remove(e,t)}}),S.fn.extend({data:function(n,e){var t,r,i,o=this[0],a=o&&o.attributes;if(void 0===n){if(this.length&&(i=Q.get(o),1===o.nodeType&&!Y.get(o,"hasDataAttrs"))){t=a.length;while(t--)a[t]&&0===(r=a[t].name).indexOf("data-")&&(r=X(r.slice(5)),Z(o,r,i[r]));Y.set(o,"hasDataAttrs",!0)}return i}return"object"==typeof n?this.each(function(){Q.set(this,n)}):$(this,function(e){var t;if(o&&void 0===e)return void 0!==(t=Q.get(o,n))?t:void 0!==(t=Z(o,n))?t:void 0;this.each(function(){Q.set(this,n,e)})},null,e,1<arguments.length,null,!0)},removeData:function(e){return this.each(function(){Q.remove(this,e)})}}),S.extend({queue:function(e,t,n){var r;if(e)return t=(t||"fx")+"queue",r=Y.get(e,t),n&&(!r||Array.isArray(n)?r=Y.access(e,t,S.makeArray(n)):r.push(n)),r||[]},dequeue:function(e,t){t=t||"fx";var n=S.queue(e,t),r=n.length,i=n.shift(),o=S._queueHooks(e,t);"inprogress"===i&&(i=n.shift(),r--),i&&("fx"===t&&n.unshift("inprogress"),delete o.stop,i.call(e,function(){S.dequeue(e,t)},o)),!r&&o&&o.empty.fire()},_queueHooks:function(e,t){var n=t+"queueHooks";return Y.get(e,n)||Y.access(e,n,{empty:S.Callbacks("once memory").add(function(){Y.remove(e,[t+"queue",n])})})}}),S.fn.extend({queue:function(t,n){var e=2;return"string"!=typeof t&&(n=t,t="fx",e--),arguments.length<e?S.queue(this[0],t):void 0===n?this:this.each(function(){var e=S.queue(this,t,n);S._queueHooks(this,t),"fx"===t&&"inprogress"!==e[0]&&S.dequeue(this,t)})},dequeue:function(e){return this.each(function(){S.dequeue(this,e)})},clearQueue:function(e){return this.queue(e||"fx",[])},promise:function(e,t){var n,r=1,i=S.Deferred(),o=this,a=this.length,s=function(){--r||i.resolveWith(o,[o])};"string"!=typeof e&&(t=e,e=void 0),e=e||"fx";while(a--)(n=Y.get(o[a],e+"queueHooks"))&&n.empty&&(r++,n.empty.add(s));return s(),i.promise(t)}});var ee=/[+-]?(?:\d*\.|)\d+(?:[eE][+-]?\d+|)/.source,te=new RegExp("^(?:([+-])=|)("+ee+")([a-z%]*)$","i"),ne=["Top","Right","Bottom","Left"],re=E.documentElement,ie=function(e){return S.contains(e.ownerDocument,e)},oe={composed:!0};re.getRootNode&&(ie=function(e){return S.contains(e.ownerDocument,e)||e.getRootNode(oe)===e.ownerDocument});var ae=function(e,t){return"none"===(e=t||e).style.display||""===e.style.display&&ie(e)&&"none"===S.css(e,"display")};function se(e,t,n,r){var i,o,a=20,s=r?function(){return r.cur()}:function(){return S.css(e,t,"")},u=s(),l=n&&n[3]||(S.cssNumber[t]?"":"px"),c=e.nodeType&&(S.cssNumber[t]||"px"!==l&&+u)&&te.exec(S.css(e,t));if(c&&c[3]!==l){u/=2,l=l||c[3],c=+u||1;while(a--)S.style(e,t,c+l),(1-o)*(1-(o=s()/u||.5))<=0&&(a=0),c/=o;c*=2,S.style(e,t,c+l),n=n||[]}return n&&(c=+c||+u||0,i=n[1]?c+(n[1]+1)*n[2]:+n[2],r&&(r.unit=l,r.start=c,r.end=i)),i}var ue={};function le(e,t){for(var n,r,i,o,a,s,u,l=[],c=0,f=e.length;c<f;c++)(r=e[c]).style&&(n=r.style.display,t?("none"===n&&(l[c]=Y.get(r,"display")||null,l[c]||(r.style.display="")),""===r.style.display&&ae(r)&&(l[c]=(u=a=o=void 0,a=(i=r).ownerDocument,s=i.nodeName,(u=ue[s])||(o=a.body.appendChild(a.createElement(s)),u=S.css(o,"display"),o.parentNode.removeChild(o),"none"===u&&(u="block"),ue[s]=u)))):"none"!==n&&(l[c]="none",Y.set(r,"display",n)));for(c=0;c<f;c++)null!=l[c]&&(e[c].style.display=l[c]);return e}S.fn.extend({show:function(){return le(this,!0)},hide:function(){return le(this)},toggle:function(e){return"boolean"==typeof e?e?this.show():this.hide():this.each(function(){ae(this)?S(this).show():S(this).hide()})}});var ce,fe,pe=/^(?:checkbox|radio)$/i,de=/<([a-z][^\/\0>\x20\t\r\n\f]*)/i,he=/^$|^module$|\/(?:java|ecma)script/i;ce=E.createDocumentFragment().appendChild(E.createElement("div")),(fe=E.createElement("input")).setAttribute("type","radio"),fe.setAttribute("checked","checked"),fe.setAttribute("name","t"),ce.appendChild(fe),y.checkClone=ce.cloneNode(!0).cloneNode(!0).lastChild.checked,ce.innerHTML="<textarea>x</textarea>",y.noCloneChecked=!!ce.cloneNode(!0).lastChild.defaultValue,ce.innerHTML="<option></option>",y.option=!!ce.lastChild;var ge={thead:[1,"<table>","</table>"],col:[2,"<table><colgroup>","</colgroup></table>"],tr:[2,"<table><tbody>","</tbody></table>"],td:[3,"<table><tbody><tr>","</tr></tbody></table>"],_default:[0,"",""]};function ve(e,t){var n;return n="undefined"!=typeof e.getElementsByTagName?e.getElementsByTagName(t||"*"):"undefined"!=typeof e.querySelectorAll?e.querySelectorAll(t||"*"):[],void 0===t||t&&A(e,t)?S.merge([e],n):n}function ye(e,t){for(var n=0,r=e.length;n<r;n++)Y.set(e[n],"globalEval",!t||Y.get(t[n],"globalEval"))}ge.tbody=ge.tfoot=ge.colgroup=ge.caption=ge.thead,ge.th=ge.td,y.option||(ge.optgroup=ge.option=[1,"<select multiple='multiple'>","</select>"]);var me=/<|&#?\w+;/;function xe(e,t,n,r,i){for(var o,a,s,u,l,c,f=t.createDocumentFragment(),p=[],d=0,h=e.length;d<h;d++)if((o=e[d])||0===o)if("object"===w(o))S.merge(p,o.nodeType?[o]:o);else if(me.test(o)){a=a||f.appendChild(t.createElement("div")),s=(de.exec(o)||["",""])[1].toLowerCase(),u=ge[s]||ge._default,a.innerHTML=u[1]+S.htmlPrefilter(o)+u[2],c=u[0];while(c--)a=a.lastChild;S.merge(p,a.childNodes),(a=f.firstChild).textContent=""}else p.push(t.createTextNode(o));f.textContent="",d=0;while(o=p[d++])if(r&&-1<S.inArray(o,r))i&&i.push(o);else if(l=ie(o),a=ve(f.appendChild(o),"script"),l&&ye(a),n){c=0;while(o=a[c++])he.test(o.type||"")&&n.push(o)}return f}var be=/^([^.]*)(?:\.(.+)|)/;function we(){return!0}function Te(){return!1}function Ce(e,t){return e===function(){try{return E.activeElement}catch(e){}}()==("focus"===t)}function Ee(e,t,n,r,i,o){var a,s;if("object"==typeof t){for(s in"string"!=typeof n&&(r=r||n,n=void 0),t)Ee(e,s,n,r,t[s],o);return e}if(null==r&&null==i?(i=n,r=n=void 0):null==i&&("string"==typeof n?(i=r,r=void 0):(i=r,r=n,n=void 0)),!1===i)i=Te;else if(!i)return e;return 1===o&&(a=i,(i=function(e){return S().off(e),a.apply(this,arguments)}).guid=a.guid||(a.guid=S.guid++)),e.each(function(){S.event.add(this,t,i,r,n)})}function Se(e,i,o){o?(Y.set(e,i,!1),S.event.add(e,i,{namespace:!1,handler:function(e){var t,n,r=Y.get(this,i);if(1&e.isTrigger&&this[i]){if(r.length)(S.event.special[i]||{}).delegateType&&e.stopPropagation();else if(r=s.call(arguments),Y.set(this,i,r),t=o(this,i),this[i](),r!==(n=Y.get(this,i))||t?Y.set(this,i,!1):n={},r!==n)return e.stopImmediatePropagation(),e.preventDefault(),n&&n.value}else r.length&&(Y.set(this,i,{value:S.event.trigger(S.extend(r[0],S.Event.prototype),r.slice(1),this)}),e.stopImmediatePropagation())}})):void 0===Y.get(e,i)&&S.event.add(e,i,we)}S.event={global:{},add:function(t,e,n,r,i){var o,a,s,u,l,c,f,p,d,h,g,v=Y.get(t);if(V(t)){n.handler&&(n=(o=n).handler,i=o.selector),i&&S.find.matchesSelector(re,i),n.guid||(n.guid=S.guid++),(u=v.events)||(u=v.events=Object.create(null)),(a=v.handle)||(a=v.handle=function(e){return"undefined"!=typeof S&&S.event.triggered!==e.type?S.event.dispatch.apply(t,arguments):void 0}),l=(e=(e||"").match(P)||[""]).length;while(l--)d=g=(s=be.exec(e[l])||[])[1],h=(s[2]||"").split(".").sort(),d&&(f=S.event.special[d]||{},d=(i?f.delegateType:f.bindType)||d,f=S.event.special[d]||{},c=S.extend({type:d,origType:g,data:r,handler:n,guid:n.guid,selector:i,needsContext:i&&S.expr.match.needsContext.test(i),namespace:h.join(".")},o),(p=u[d])||((p=u[d]=[]).delegateCount=0,f.setup&&!1!==f.setup.call(t,r,h,a)||t.addEventListener&&t.addEventListener(d,a)),f.add&&(f.add.call(t,c),c.handler.guid||(c.handler.guid=n.guid)),i?p.splice(p.delegateCount++,0,c):p.push(c),S.event.global[d]=!0)}},remove:function(e,t,n,r,i){var o,a,s,u,l,c,f,p,d,h,g,v=Y.hasData(e)&&Y.get(e);if(v&&(u=v.events)){l=(t=(t||"").match(P)||[""]).length;while(l--)if(d=g=(s=be.exec(t[l])||[])[1],h=(s[2]||"").split(".").sort(),d){f=S.event.special[d]||{},p=u[d=(r?f.delegateType:f.bindType)||d]||[],s=s[2]&&new RegExp("(^|\\.)"+h.join("\\.(?:.*\\.|)")+"(\\.|$)"),a=o=p.length;while(o--)c=p[o],!i&&g!==c.origType||n&&n.guid!==c.guid||s&&!s.test(c.namespace)||r&&r!==c.selector&&("**"!==r||!c.selector)||(p.splice(o,1),c.selector&&p.delegateCount--,f.remove&&f.remove.call(e,c));a&&!p.length&&(f.teardown&&!1!==f.teardown.call(e,h,v.handle)||S.removeEvent(e,d,v.handle),delete u[d])}else for(d in u)S.event.remove(e,d+t[l],n,r,!0);S.isEmptyObject(u)&&Y.remove(e,"handle events")}},dispatch:function(e){var t,n,r,i,o,a,s=new Array(arguments.length),u=S.event.fix(e),l=(Y.get(this,"events")||Object.create(null))[u.type]||[],c=S.event.special[u.type]||{};for(s[0]=u,t=1;t<arguments.length;t++)s[t]=arguments[t];if(u.delegateTarget=this,!c.preDispatch||!1!==c.preDispatch.call(this,u)){a=S.event.handlers.call(this,u,l),t=0;while((i=a[t++])&&!u.isPropagationStopped()){u.currentTarget=i.elem,n=0;while((o=i.handlers[n++])&&!u.isImmediatePropagationStopped())u.rnamespace&&!1!==o.namespace&&!u.rnamespace.test(o.namespace)||(u.handleObj=o,u.data=o.data,void 0!==(r=((S.event.special[o.origType]||{}).handle||o.handler).apply(i.elem,s))&&!1===(u.result=r)&&(u.preventDefault(),u.stopPropagation()))}return c.postDispatch&&c.postDispatch.call(this,u),u.result}},handlers:function(e,t){var n,r,i,o,a,s=[],u=t.delegateCount,l=e.target;if(u&&l.nodeType&&!("click"===e.type&&1<=e.button))for(;l!==this;l=l.parentNode||this)if(1===l.nodeType&&("click"!==e.type||!0!==l.disabled)){for(o=[],a={},n=0;n<u;n++)void 0===a[i=(r=t[n]).selector+" "]&&(a[i]=r.needsContext?-1<S(i,this).index(l):S.find(i,this,null,[l]).length),a[i]&&o.push(r);o.length&&s.push({elem:l,handlers:o})}return l=this,u<t.length&&s.push({elem:l,handlers:t.slice(u)}),s},addProp:function(t,e){Object.defineProperty(S.Event.prototype,t,{enumerable:!0,configurable:!0,get:m(e)?function(){if(this.originalEvent)return e(this.originalEvent)}:function(){if(this.originalEvent)return this.originalEvent[t]},set:function(e){Object.defineProperty(this,t,{enumerable:!0,configurable:!0,writable:!0,value:e})}})},fix:function(e){return e[S.expando]?e:new S.Event(e)},special:{load:{noBubble:!0},click:{setup:function(e){var t=this||e;return pe.test(t.type)&&t.click&&A(t,"input")&&Se(t,"click",we),!1},trigger:function(e){var t=this||e;return pe.test(t.type)&&t.click&&A(t,"input")&&Se(t,"click"),!0},_default:function(e){var t=e.target;return pe.test(t.type)&&t.click&&A(t,"input")&&Y.get(t,"click")||A(t,"a")}},beforeunload:{postDispatch:function(e){void 0!==e.result&&e.originalEvent&&(e.originalEvent.returnValue=e.result)}}}},S.removeEvent=function(e,t,n){e.removeEventListener&&e.removeEventListener(t,n)},S.Event=function(e,t){if(!(this instanceof S.Event))return new S.Event(e,t);e&&e.type?(this.originalEvent=e,this.type=e.type,this.isDefaultPrevented=e.defaultPrevented||void 0===e.defaultPrevented&&!1===e.returnValue?we:Te,this.target=e.target&&3===e.target.nodeType?e.target.parentNode:e.target,this.currentTarget=e.currentTarget,this.relatedTarget=e.relatedTarget):this.type=e,t&&S.extend(this,t),this.timeStamp=e&&e.timeStamp||Date.now(),this[S.expando]=!0},S.Event.prototype={constructor:S.Event,isDefaultPrevented:Te,isPropagationStopped:Te,isImmediatePropagationStopped:Te,isSimulated:!1,preventDefault:function(){var e=this.originalEvent;this.isDefaultPrevented=we,e&&!this.isSimulated&&e.preventDefault()},stopPropagation:function(){var e=this.originalEvent;this.isPropagationStopped=we,e&&!this.isSimulated&&e.stopPropagation()},stopImmediatePropagation:function(){var e=this.originalEvent;this.isImmediatePropagationStopped=we,e&&!this.isSimulated&&e.stopImmediatePropagation(),this.stopPropagation()}},S.each({altKey:!0,bubbles:!0,cancelable:!0,changedTouches:!0,ctrlKey:!0,detail:!0,eventPhase:!0,metaKey:!0,pageX:!0,pageY:!0,shiftKey:!0,view:!0,"char":!0,code:!0,charCode:!0,key:!0,keyCode:!0,button:!0,buttons:!0,clientX:!0,clientY:!0,offsetX:!0,offsetY:!0,pointerId:!0,pointerType:!0,screenX:!0,screenY:!0,targetTouches:!0,toElement:!0,touches:!0,which:!0},S.event.addProp),S.each({focus:"focusin",blur:"focusout"},function(e,t){S.event.special[e]={setup:function(){return Se(this,e,Ce),!1},trigger:function(){return Se(this,e),!0},_default:function(){return!0},delegateType:t}}),S.each({mouseenter:"mouseover",mouseleave:"mouseout",pointerenter:"pointerover",pointerleave:"pointerout"},function(e,i){S.event.special[e]={delegateType:i,bindType:i,handle:function(e){var t,n=e.relatedTarget,r=e.handleObj;return n&&(n===this||S.contains(this,n))||(e.type=r.origType,t=r.handler.apply(this,arguments),e.type=i),t}}}),S.fn.extend({on:function(e,t,n,r){return Ee(this,e,t,n,r)},one:function(e,t,n,r){return Ee(this,e,t,n,r,1)},off:function(e,t,n){var r,i;if(e&&e.preventDefault&&e.handleObj)return r=e.handleObj,S(e.delegateTarget).off(r.namespace?r.origType+"."+r.namespace:r.origType,r.selector,r.handler),this;if("object"==typeof e){for(i in e)this.off(i,t,e[i]);return this}return!1!==t&&"function"!=typeof t||(n=t,t=void 0),!1===n&&(n=Te),this.each(function(){S.event.remove(this,e,n,t)})}});var ke=/<script|<style|<link/i,Ae=/checked\s*(?:[^=]|=\s*.checked.)/i,Ne=/^\s*<!(?:\[CDATA\[|--)|(?:\]\]|--)>\s*$/g;function je(e,t){return A(e,"table")&&A(11!==t.nodeType?t:t.firstChild,"tr")&&S(e).children("tbody")[0]||e}function De(e){return e.type=(null!==e.getAttribute("type"))+"/"+e.type,e}function qe(e){return"true/"===(e.type||"").slice(0,5)?e.type=e.type.slice(5):e.removeAttribute("type"),e}function Le(e,t){var n,r,i,o,a,s;if(1===t.nodeType){if(Y.hasData(e)&&(s=Y.get(e).events))for(i in Y.remove(t,"handle events"),s)for(n=0,r=s[i].length;n<r;n++)S.event.add(t,i,s[i][n]);Q.hasData(e)&&(o=Q.access(e),a=S.extend({},o),Q.set(t,a))}}function He(n,r,i,o){r=g(r);var e,t,a,s,u,l,c=0,f=n.length,p=f-1,d=r[0],h=m(d);if(h||1<f&&"string"==typeof d&&!y.checkClone&&Ae.test(d))return n.each(function(e){var t=n.eq(e);h&&(r[0]=d.call(this,e,t.html())),He(t,r,i,o)});if(f&&(t=(e=xe(r,n[0].ownerDocument,!1,n,o)).firstChild,1===e.childNodes.length&&(e=t),t||o)){for(s=(a=S.map(ve(e,"script"),De)).length;c<f;c++)u=e,c!==p&&(u=S.clone(u,!0,!0),s&&S.merge(a,ve(u,"script"))),i.call(n[c],u,c);if(s)for(l=a[a.length-1].ownerDocument,S.map(a,qe),c=0;c<s;c++)u=a[c],he.test(u.type||"")&&!Y.access(u,"globalEval")&&S.contains(l,u)&&(u.src&&"module"!==(u.type||"").toLowerCase()?S._evalUrl&&!u.noModule&&S._evalUrl(u.src,{nonce:u.nonce||u.getAttribute("nonce")},l):b(u.textContent.replace(Ne,""),u,l))}return n}function Oe(e,t,n){for(var r,i=t?S.filter(t,e):e,o=0;null!=(r=i[o]);o++)n||1!==r.nodeType||S.cleanData(ve(r)),r.parentNode&&(n&&ie(r)&&ye(ve(r,"script")),r.parentNode.removeChild(r));return e}S.extend({htmlPrefilter:function(e){return e},clone:function(e,t,n){var r,i,o,a,s,u,l,c=e.cloneNode(!0),f=ie(e);if(!(y.noCloneChecked||1!==e.nodeType&&11!==e.nodeType||S.isXMLDoc(e)))for(a=ve(c),r=0,i=(o=ve(e)).length;r<i;r++)s=o[r],u=a[r],void 0,"input"===(l=u.nodeName.toLowerCase())&&pe.test(s.type)?u.checked=s.checked:"input"!==l&&"textarea"!==l||(u.defaultValue=s.defaultValue);if(t)if(n)for(o=o||ve(e),a=a||ve(c),r=0,i=o.length;r<i;r++)Le(o[r],a[r]);else Le(e,c);return 0<(a=ve(c,"script")).length&&ye(a,!f&&ve(e,"script")),c},cleanData:function(e){for(var t,n,r,i=S.event.special,o=0;void 0!==(n=e[o]);o++)if(V(n)){if(t=n[Y.expando]){if(t.events)for(r in t.events)i[r]?S.event.remove(n,r):S.removeEvent(n,r,t.handle);n[Y.expando]=void 0}n[Q.expando]&&(n[Q.expando]=void 0)}}}),S.fn.extend({detach:function(e){return Oe(this,e,!0)},remove:function(e){return Oe(this,e)},text:function(e){return $(this,function(e){return void 0===e?S.text(this):this.empty().each(function(){1!==this.nodeType&&11!==this.nodeType&&9!==this.nodeType||(this.textContent=e)})},null,e,arguments.length)},append:function(){return He(this,arguments,function(e){1!==this.nodeType&&11!==this.nodeType&&9!==this.nodeType||je(this,e).appendChild(e)})},prepend:function(){return He(this,arguments,function(e){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var t=je(this,e);t.insertBefore(e,t.firstChild)}})},before:function(){return He(this,arguments,function(e){this.parentNode&&this.parentNode.insertBefore(e,this)})},after:function(){return He(this,arguments,function(e){this.parentNode&&this.parentNode.insertBefore(e,this.nextSibling)})},empty:function(){for(var e,t=0;null!=(e=this[t]);t++)1===e.nodeType&&(S.cleanData(ve(e,!1)),e.textContent="");return this},clone:function(e,t){return e=null!=e&&e,t=null==t?e:t,this.map(function(){return S.clone(this,e,t)})},html:function(e){return $(this,function(e){var t=this[0]||{},n=0,r=this.length;if(void 0===e&&1===t.nodeType)return t.innerHTML;if("string"==typeof e&&!ke.test(e)&&!ge[(de.exec(e)||["",""])[1].toLowerCase()]){e=S.htmlPrefilter(e);try{for(;n<r;n++)1===(t=this[n]||{}).nodeType&&(S.cleanData(ve(t,!1)),t.innerHTML=e);t=0}catch(e){}}t&&this.empty().append(e)},null,e,arguments.length)},replaceWith:function(){var n=[];return He(this,arguments,function(e){var t=this.parentNode;S.inArray(this,n)<0&&(S.cleanData(ve(this)),t&&t.replaceChild(e,this))},n)}}),S.each({appendTo:"append",prependTo:"prepend",insertBefore:"before",insertAfter:"after",replaceAll:"replaceWith"},function(e,a){S.fn[e]=function(e){for(var t,n=[],r=S(e),i=r.length-1,o=0;o<=i;o++)t=o===i?this:this.clone(!0),S(r[o])[a](t),u.apply(n,t.get());return this.pushStack(n)}});var Pe=new RegExp("^("+ee+")(?!px)[a-z%]+$","i"),Re=function(e){var t=e.ownerDocument.defaultView;return t&&t.opener||(t=C),t.getComputedStyle(e)},Me=function(e,t,n){var r,i,o={};for(i in t)o[i]=e.style[i],e.style[i]=t[i];for(i in r=n.call(e),t)e.style[i]=o[i];return r},Ie=new RegExp(ne.join("|"),"i");function We(e,t,n){var r,i,o,a,s=e.style;return(n=n||Re(e))&&(""!==(a=n.getPropertyValue(t)||n[t])||ie(e)||(a=S.style(e,t)),!y.pixelBoxStyles()&&Pe.test(a)&&Ie.test(t)&&(r=s.width,i=s.minWidth,o=s.maxWidth,s.minWidth=s.maxWidth=s.width=a,a=n.width,s.width=r,s.minWidth=i,s.maxWidth=o)),void 0!==a?a+"":a}function Fe(e,t){return{get:function(){if(!e())return(this.get=t).apply(this,arguments);delete this.get}}}!function(){function e(){if(l){u.style.cssText="position:absolute;left:-11111px;width:60px;margin-top:1px;padding:0;border:0",l.style.cssText="position:relative;display:block;box-sizing:border-box;overflow:scroll;margin:auto;border:1px;padding:1px;width:60%;top:1%",re.appendChild(u).appendChild(l);var e=C.getComputedStyle(l);n="1%"!==e.top,s=12===t(e.marginLeft),l.style.right="60%",o=36===t(e.right),r=36===t(e.width),l.style.position="absolute",i=12===t(l.offsetWidth/3),re.removeChild(u),l=null}}function t(e){return Math.round(parseFloat(e))}var n,r,i,o,a,s,u=E.createElement("div"),l=E.createElement("div");l.style&&(l.style.backgroundClip="content-box",l.cloneNode(!0).style.backgroundClip="",y.clearCloneStyle="content-box"===l.style.backgroundClip,S.extend(y,{boxSizingReliable:function(){return e(),r},pixelBoxStyles:function(){return e(),o},pixelPosition:function(){return e(),n},reliableMarginLeft:function(){return e(),s},scrollboxSize:function(){return e(),i},reliableTrDimensions:function(){var e,t,n,r;return null==a&&(e=E.createElement("table"),t=E.createElement("tr"),n=E.createElement("div"),e.style.cssText="position:absolute;left:-11111px;border-collapse:separate",t.style.cssText="border:1px solid",t.style.height="1px",n.style.height="9px",n.style.display="block",re.appendChild(e).appendChild(t).appendChild(n),r=C.getComputedStyle(t),a=parseInt(r.height,10)+parseInt(r.borderTopWidth,10)+parseInt(r.borderBottomWidth,10)===t.offsetHeight,re.removeChild(e)),a}}))}();var Be=["Webkit","Moz","ms"],$e=E.createElement("div").style,_e={};function ze(e){var t=S.cssProps[e]||_e[e];return t||(e in $e?e:_e[e]=function(e){var t=e[0].toUpperCase()+e.slice(1),n=Be.length;while(n--)if((e=Be[n]+t)in $e)return e}(e)||e)}var Ue=/^(none|table(?!-c[ea]).+)/,Xe=/^--/,Ve={position:"absolute",visibility:"hidden",display:"block"},Ge={letterSpacing:"0",fontWeight:"400"};function Ye(e,t,n){var r=te.exec(t);return r?Math.max(0,r[2]-(n||0))+(r[3]||"px"):t}function Qe(e,t,n,r,i,o){var a="width"===t?1:0,s=0,u=0;if(n===(r?"border":"content"))return 0;for(;a<4;a+=2)"margin"===n&&(u+=S.css(e,n+ne[a],!0,i)),r?("content"===n&&(u-=S.css(e,"padding"+ne[a],!0,i)),"margin"!==n&&(u-=S.css(e,"border"+ne[a]+"Width",!0,i))):(u+=S.css(e,"padding"+ne[a],!0,i),"padding"!==n?u+=S.css(e,"border"+ne[a]+"Width",!0,i):s+=S.css(e,"border"+ne[a]+"Width",!0,i));return!r&&0<=o&&(u+=Math.max(0,Math.ceil(e["offset"+t[0].toUpperCase()+t.slice(1)]-o-u-s-.5))||0),u}function Je(e,t,n){var r=Re(e),i=(!y.boxSizingReliable()||n)&&"border-box"===S.css(e,"boxSizing",!1,r),o=i,a=We(e,t,r),s="offset"+t[0].toUpperCase()+t.slice(1);if(Pe.test(a)){if(!n)return a;a="auto"}return(!y.boxSizingReliable()&&i||!y.reliableTrDimensions()&&A(e,"tr")||"auto"===a||!parseFloat(a)&&"inline"===S.css(e,"display",!1,r))&&e.getClientRects().length&&(i="border-box"===S.css(e,"boxSizing",!1,r),(o=s in e)&&(a=e[s])),(a=parseFloat(a)||0)+Qe(e,t,n||(i?"border":"content"),o,r,a)+"px"}function Ke(e,t,n,r,i){return new Ke.prototype.init(e,t,n,r,i)}S.extend({cssHooks:{opacity:{get:function(e,t){if(t){var n=We(e,"opacity");return""===n?"1":n}}}},cssNumber:{animationIterationCount:!0,columnCount:!0,fillOpacity:!0,flexGrow:!0,flexShrink:!0,fontWeight:!0,gridArea:!0,gridColumn:!0,gridColumnEnd:!0,gridColumnStart:!0,gridRow:!0,gridRowEnd:!0,gridRowStart:!0,lineHeight:!0,opacity:!0,order:!0,orphans:!0,widows:!0,zIndex:!0,zoom:!0},cssProps:{},style:function(e,t,n,r){if(e&&3!==e.nodeType&&8!==e.nodeType&&e.style){var i,o,a,s=X(t),u=Xe.test(t),l=e.style;if(u||(t=ze(s)),a=S.cssHooks[t]||S.cssHooks[s],void 0===n)return a&&"get"in a&&void 0!==(i=a.get(e,!1,r))?i:l[t];"string"===(o=typeof n)&&(i=te.exec(n))&&i[1]&&(n=se(e,t,i),o="number"),null!=n&&n==n&&("number"!==o||u||(n+=i&&i[3]||(S.cssNumber[s]?"":"px")),y.clearCloneStyle||""!==n||0!==t.indexOf("background")||(l[t]="inherit"),a&&"set"in a&&void 0===(n=a.set(e,n,r))||(u?l.setProperty(t,n):l[t]=n))}},css:function(e,t,n,r){var i,o,a,s=X(t);return Xe.test(t)||(t=ze(s)),(a=S.cssHooks[t]||S.cssHooks[s])&&"get"in a&&(i=a.get(e,!0,n)),void 0===i&&(i=We(e,t,r)),"normal"===i&&t in Ge&&(i=Ge[t]),""===n||n?(o=parseFloat(i),!0===n||isFinite(o)?o||0:i):i}}),S.each(["height","width"],function(e,u){S.cssHooks[u]={get:function(e,t,n){if(t)return!Ue.test(S.css(e,"display"))||e.getClientRects().length&&e.getBoundingClientRect().width?Je(e,u,n):Me(e,Ve,function(){return Je(e,u,n)})},set:function(e,t,n){var r,i=Re(e),o=!y.scrollboxSize()&&"absolute"===i.position,a=(o||n)&&"border-box"===S.css(e,"boxSizing",!1,i),s=n?Qe(e,u,n,a,i):0;return a&&o&&(s-=Math.ceil(e["offset"+u[0].toUpperCase()+u.slice(1)]-parseFloat(i[u])-Qe(e,u,"border",!1,i)-.5)),s&&(r=te.exec(t))&&"px"!==(r[3]||"px")&&(e.style[u]=t,t=S.css(e,u)),Ye(0,t,s)}}}),S.cssHooks.marginLeft=Fe(y.reliableMarginLeft,function(e,t){if(t)return(parseFloat(We(e,"marginLeft"))||e.getBoundingClientRect().left-Me(e,{marginLeft:0},function(){return e.getBoundingClientRect().left}))+"px"}),S.each({margin:"",padding:"",border:"Width"},function(i,o){S.cssHooks[i+o]={expand:function(e){for(var t=0,n={},r="string"==typeof e?e.split(" "):[e];t<4;t++)n[i+ne[t]+o]=r[t]||r[t-2]||r[0];return n}},"margin"!==i&&(S.cssHooks[i+o].set=Ye)}),S.fn.extend({css:function(e,t){return $(this,function(e,t,n){var r,i,o={},a=0;if(Array.isArray(t)){for(r=Re(e),i=t.length;a<i;a++)o[t[a]]=S.css(e,t[a],!1,r);return o}return void 0!==n?S.style(e,t,n):S.css(e,t)},e,t,1<arguments.length)}}),((S.Tween=Ke).prototype={constructor:Ke,init:function(e,t,n,r,i,o){this.elem=e,this.prop=n,this.easing=i||S.easing._default,this.options=t,this.start=this.now=this.cur(),this.end=r,this.unit=o||(S.cssNumber[n]?"":"px")},cur:function(){var e=Ke.propHooks[this.prop];return e&&e.get?e.get(this):Ke.propHooks._default.get(this)},run:function(e){var t,n=Ke.propHooks[this.prop];return this.options.duration?this.pos=t=S.easing[this.easing](e,this.options.duration*e,0,1,this.options.duration):this.pos=t=e,this.now=(this.end-this.start)*t+this.start,this.options.step&&this.options.step.call(this.elem,this.now,this),n&&n.set?n.set(this):Ke.propHooks._default.set(this),this}}).init.prototype=Ke.prototype,(Ke.propHooks={_default:{get:function(e){var t;return 1!==e.elem.nodeType||null!=e.elem[e.prop]&&null==e.elem.style[e.prop]?e.elem[e.prop]:(t=S.css(e.elem,e.prop,""))&&"auto"!==t?t:0},set:function(e){S.fx.step[e.prop]?S.fx.step[e.prop](e):1!==e.elem.nodeType||!S.cssHooks[e.prop]&&null==e.elem.style[ze(e.prop)]?e.elem[e.prop]=e.now:S.style(e.elem,e.prop,e.now+e.unit)}}}).scrollTop=Ke.propHooks.scrollLeft={set:function(e){e.elem.nodeType&&e.elem.parentNode&&(e.elem[e.prop]=e.now)}},S.easing={linear:function(e){return e},swing:function(e){return.5-Math.cos(e*Math.PI)/2},_default:"swing"},S.fx=Ke.prototype.init,S.fx.step={};var Ze,et,tt,nt,rt=/^(?:toggle|show|hide)$/,it=/queueHooks$/;function ot(){et&&(!1===E.hidden&&C.requestAnimationFrame?C.requestAnimationFrame(ot):C.setTimeout(ot,S.fx.interval),S.fx.tick())}function at(){return C.setTimeout(function(){Ze=void 0}),Ze=Date.now()}function st(e,t){var n,r=0,i={height:e};for(t=t?1:0;r<4;r+=2-t)i["margin"+(n=ne[r])]=i["padding"+n]=e;return t&&(i.opacity=i.width=e),i}function ut(e,t,n){for(var r,i=(lt.tweeners[t]||[]).concat(lt.tweeners["*"]),o=0,a=i.length;o<a;o++)if(r=i[o].call(n,t,e))return r}function lt(o,e,t){var n,a,r=0,i=lt.prefilters.length,s=S.Deferred().always(function(){delete u.elem}),u=function(){if(a)return!1;for(var e=Ze||at(),t=Math.max(0,l.startTime+l.duration-e),n=1-(t/l.duration||0),r=0,i=l.tweens.length;r<i;r++)l.tweens[r].run(n);return s.notifyWith(o,[l,n,t]),n<1&&i?t:(i||s.notifyWith(o,[l,1,0]),s.resolveWith(o,[l]),!1)},l=s.promise({elem:o,props:S.extend({},e),opts:S.extend(!0,{specialEasing:{},easing:S.easing._default},t),originalProperties:e,originalOptions:t,startTime:Ze||at(),duration:t.duration,tweens:[],createTween:function(e,t){var n=S.Tween(o,l.opts,e,t,l.opts.specialEasing[e]||l.opts.easing);return l.tweens.push(n),n},stop:function(e){var t=0,n=e?l.tweens.length:0;if(a)return this;for(a=!0;t<n;t++)l.tweens[t].run(1);return e?(s.notifyWith(o,[l,1,0]),s.resolveWith(o,[l,e])):s.rejectWith(o,[l,e]),this}}),c=l.props;for(!function(e,t){var n,r,i,o,a;for(n in e)if(i=t[r=X(n)],o=e[n],Array.isArray(o)&&(i=o[1],o=e[n]=o[0]),n!==r&&(e[r]=o,delete e[n]),(a=S.cssHooks[r])&&"expand"in a)for(n in o=a.expand(o),delete e[r],o)n in e||(e[n]=o[n],t[n]=i);else t[r]=i}(c,l.opts.specialEasing);r<i;r++)if(n=lt.prefilters[r].call(l,o,c,l.opts))return m(n.stop)&&(S._queueHooks(l.elem,l.opts.queue).stop=n.stop.bind(n)),n;return S.map(c,ut,l),m(l.opts.start)&&l.opts.start.call(o,l),l.progress(l.opts.progress).done(l.opts.done,l.opts.complete).fail(l.opts.fail).always(l.opts.always),S.fx.timer(S.extend(u,{elem:o,anim:l,queue:l.opts.queue})),l}S.Animation=S.extend(lt,{tweeners:{"*":[function(e,t){var n=this.createTween(e,t);return se(n.elem,e,te.exec(t),n),n}]},tweener:function(e,t){m(e)?(t=e,e=["*"]):e=e.match(P);for(var n,r=0,i=e.length;r<i;r++)n=e[r],lt.tweeners[n]=lt.tweeners[n]||[],lt.tweeners[n].unshift(t)},prefilters:[function(e,t,n){var r,i,o,a,s,u,l,c,f="width"in t||"height"in t,p=this,d={},h=e.style,g=e.nodeType&&ae(e),v=Y.get(e,"fxshow");for(r in n.queue||(null==(a=S._queueHooks(e,"fx")).unqueued&&(a.unqueued=0,s=a.empty.fire,a.empty.fire=function(){a.unqueued||s()}),a.unqueued++,p.always(function(){p.always(function(){a.unqueued--,S.queue(e,"fx").length||a.empty.fire()})})),t)if(i=t[r],rt.test(i)){if(delete t[r],o=o||"toggle"===i,i===(g?"hide":"show")){if("show"!==i||!v||void 0===v[r])continue;g=!0}d[r]=v&&v[r]||S.style(e,r)}if((u=!S.isEmptyObject(t))||!S.isEmptyObject(d))for(r in f&&1===e.nodeType&&(n.overflow=[h.overflow,h.overflowX,h.overflowY],null==(l=v&&v.display)&&(l=Y.get(e,"display")),"none"===(c=S.css(e,"display"))&&(l?c=l:(le([e],!0),l=e.style.display||l,c=S.css(e,"display"),le([e]))),("inline"===c||"inline-block"===c&&null!=l)&&"none"===S.css(e,"float")&&(u||(p.done(function(){h.display=l}),null==l&&(c=h.display,l="none"===c?"":c)),h.display="inline-block")),n.overflow&&(h.overflow="hidden",p.always(function(){h.overflow=n.overflow[0],h.overflowX=n.overflow[1],h.overflowY=n.overflow[2]})),u=!1,d)u||(v?"hidden"in v&&(g=v.hidden):v=Y.access(e,"fxshow",{display:l}),o&&(v.hidden=!g),g&&le([e],!0),p.done(function(){for(r in g||le([e]),Y.remove(e,"fxshow"),d)S.style(e,r,d[r])})),u=ut(g?v[r]:0,r,p),r in v||(v[r]=u.start,g&&(u.end=u.start,u.start=0))}],prefilter:function(e,t){t?lt.prefilters.unshift(e):lt.prefilters.push(e)}}),S.speed=function(e,t,n){var r=e&&"object"==typeof e?S.extend({},e):{complete:n||!n&&t||m(e)&&e,duration:e,easing:n&&t||t&&!m(t)&&t};return S.fx.off?r.duration=0:"number"!=typeof r.duration&&(r.duration in S.fx.speeds?r.duration=S.fx.speeds[r.duration]:r.duration=S.fx.speeds._default),null!=r.queue&&!0!==r.queue||(r.queue="fx"),r.old=r.complete,r.complete=function(){m(r.old)&&r.old.call(this),r.queue&&S.dequeue(this,r.queue)},r},S.fn.extend({fadeTo:function(e,t,n,r){return this.filter(ae).css("opacity",0).show().end().animate({opacity:t},e,n,r)},animate:function(t,e,n,r){var i=S.isEmptyObject(t),o=S.speed(e,n,r),a=function(){var e=lt(this,S.extend({},t),o);(i||Y.get(this,"finish"))&&e.stop(!0)};return a.finish=a,i||!1===o.queue?this.each(a):this.queue(o.queue,a)},stop:function(i,e,o){var a=function(e){var t=e.stop;delete e.stop,t(o)};return"string"!=typeof i&&(o=e,e=i,i=void 0),e&&this.queue(i||"fx",[]),this.each(function(){var e=!0,t=null!=i&&i+"queueHooks",n=S.timers,r=Y.get(this);if(t)r[t]&&r[t].stop&&a(r[t]);else for(t in r)r[t]&&r[t].stop&&it.test(t)&&a(r[t]);for(t=n.length;t--;)n[t].elem!==this||null!=i&&n[t].queue!==i||(n[t].anim.stop(o),e=!1,n.splice(t,1));!e&&o||S.dequeue(this,i)})},finish:function(a){return!1!==a&&(a=a||"fx"),this.each(function(){var e,t=Y.get(this),n=t[a+"queue"],r=t[a+"queueHooks"],i=S.timers,o=n?n.length:0;for(t.finish=!0,S.queue(this,a,[]),r&&r.stop&&r.stop.call(this,!0),e=i.length;e--;)i[e].elem===this&&i[e].queue===a&&(i[e].anim.stop(!0),i.splice(e,1));for(e=0;e<o;e++)n[e]&&n[e].finish&&n[e].finish.call(this);delete t.finish})}}),S.each(["toggle","show","hide"],function(e,r){var i=S.fn[r];S.fn[r]=function(e,t,n){return null==e||"boolean"==typeof e?i.apply(this,arguments):this.animate(st(r,!0),e,t,n)}}),S.each({slideDown:st("show"),slideUp:st("hide"),slideToggle:st("toggle"),fadeIn:{opacity:"show"},fadeOut:{opacity:"hide"},fadeToggle:{opacity:"toggle"}},function(e,r){S.fn[e]=function(e,t,n){return this.animate(r,e,t,n)}}),S.timers=[],S.fx.tick=function(){var e,t=0,n=S.timers;for(Ze=Date.now();t<n.length;t++)(e=n[t])()||n[t]!==e||n.splice(t--,1);n.length||S.fx.stop(),Ze=void 0},S.fx.timer=function(e){S.timers.push(e),S.fx.start()},S.fx.interval=13,S.fx.start=function(){et||(et=!0,ot())},S.fx.stop=function(){et=null},S.fx.speeds={slow:600,fast:200,_default:400},S.fn.delay=function(r,e){return r=S.fx&&S.fx.speeds[r]||r,e=e||"fx",this.queue(e,function(e,t){var n=C.setTimeout(e,r);t.stop=function(){C.clearTimeout(n)}})},tt=E.createElement("input"),nt=E.createElement("select").appendChild(E.createElement("option")),tt.type="checkbox",y.checkOn=""!==tt.value,y.optSelected=nt.selected,(tt=E.createElement("input")).value="t",tt.type="radio",y.radioValue="t"===tt.value;var ct,ft=S.expr.attrHandle;S.fn.extend({attr:function(e,t){return $(this,S.attr,e,t,1<arguments.length)},removeAttr:function(e){return this.each(function(){S.removeAttr(this,e)})}}),S.extend({attr:function(e,t,n){var r,i,o=e.nodeType;if(3!==o&&8!==o&&2!==o)return"undefined"==typeof e.getAttribute?S.prop(e,t,n):(1===o&&S.isXMLDoc(e)||(i=S.attrHooks[t.toLowerCase()]||(S.expr.match.bool.test(t)?ct:void 0)),void 0!==n?null===n?void S.removeAttr(e,t):i&&"set"in i&&void 0!==(r=i.set(e,n,t))?r:(e.setAttribute(t,n+""),n):i&&"get"in i&&null!==(r=i.get(e,t))?r:null==(r=S.find.attr(e,t))?void 0:r)},attrHooks:{type:{set:function(e,t){if(!y.radioValue&&"radio"===t&&A(e,"input")){var n=e.value;return e.setAttribute("type",t),n&&(e.value=n),t}}}},removeAttr:function(e,t){var n,r=0,i=t&&t.match(P);if(i&&1===e.nodeType)while(n=i[r++])e.removeAttribute(n)}}),ct={set:function(e,t,n){return!1===t?S.removeAttr(e,n):e.setAttribute(n,n),n}},S.each(S.expr.match.bool.source.match(/\w+/g),function(e,t){var a=ft[t]||S.find.attr;ft[t]=function(e,t,n){var r,i,o=t.toLowerCase();return n||(i=ft[o],ft[o]=r,r=null!=a(e,t,n)?o:null,ft[o]=i),r}});var pt=/^(?:input|select|textarea|button)$/i,dt=/^(?:a|area)$/i;function ht(e){return(e.match(P)||[]).join(" ")}function gt(e){return e.getAttribute&&e.getAttribute("class")||""}function vt(e){return Array.isArray(e)?e:"string"==typeof e&&e.match(P)||[]}S.fn.extend({prop:function(e,t){return $(this,S.prop,e,t,1<arguments.length)},removeProp:function(e){return this.each(function(){delete this[S.propFix[e]||e]})}}),S.extend({prop:function(e,t,n){var r,i,o=e.nodeType;if(3!==o&&8!==o&&2!==o)return 1===o&&S.isXMLDoc(e)||(t=S.propFix[t]||t,i=S.propHooks[t]),void 0!==n?i&&"set"in i&&void 0!==(r=i.set(e,n,t))?r:e[t]=n:i&&"get"in i&&null!==(r=i.get(e,t))?r:e[t]},propHooks:{tabIndex:{get:function(e){var t=S.find.attr(e,"tabindex");return t?parseInt(t,10):pt.test(e.nodeName)||dt.test(e.nodeName)&&e.href?0:-1}}},propFix:{"for":"htmlFor","class":"className"}}),y.optSelected||(S.propHooks.selected={get:function(e){var t=e.parentNode;return t&&t.parentNode&&t.parentNode.selectedIndex,null},set:function(e){var t=e.parentNode;t&&(t.selectedIndex,t.parentNode&&t.parentNode.selectedIndex)}}),S.each(["tabIndex","readOnly","maxLength","cellSpacing","cellPadding","rowSpan","colSpan","useMap","frameBorder","contentEditable"],function(){S.propFix[this.toLowerCase()]=this}),S.fn.extend({addClass:function(t){var e,n,r,i,o,a,s,u=0;if(m(t))return this.each(function(e){S(this).addClass(t.call(this,e,gt(this)))});if((e=vt(t)).length)while(n=this[u++])if(i=gt(n),r=1===n.nodeType&&" "+ht(i)+" "){a=0;while(o=e[a++])r.indexOf(" "+o+" ")<0&&(r+=o+" ");i!==(s=ht(r))&&n.setAttribute("class",s)}return this},removeClass:function(t){var e,n,r,i,o,a,s,u=0;if(m(t))return this.each(function(e){S(this).removeClass(t.call(this,e,gt(this)))});if(!arguments.length)return this.attr("class","");if((e=vt(t)).length)while(n=this[u++])if(i=gt(n),r=1===n.nodeType&&" "+ht(i)+" "){a=0;while(o=e[a++])while(-1<r.indexOf(" "+o+" "))r=r.replace(" "+o+" "," ");i!==(s=ht(r))&&n.setAttribute("class",s)}return this},toggleClass:function(i,t){var o=typeof i,a="string"===o||Array.isArray(i);return"boolean"==typeof t&&a?t?this.addClass(i):this.removeClass(i):m(i)?this.each(function(e){S(this).toggleClass(i.call(this,e,gt(this),t),t)}):this.each(function(){var e,t,n,r;if(a){t=0,n=S(this),r=vt(i);while(e=r[t++])n.hasClass(e)?n.removeClass(e):n.addClass(e)}else void 0!==i&&"boolean"!==o||((e=gt(this))&&Y.set(this,"__className__",e),this.setAttribute&&this.setAttribute("class",e||!1===i?"":Y.get(this,"__className__")||""))})},hasClass:function(e){var t,n,r=0;t=" "+e+" ";while(n=this[r++])if(1===n.nodeType&&-1<(" "+ht(gt(n))+" ").indexOf(t))return!0;return!1}});var yt=/\r/g;S.fn.extend({val:function(n){var r,e,i,t=this[0];return arguments.length?(i=m(n),this.each(function(e){var t;1===this.nodeType&&(null==(t=i?n.call(this,e,S(this).val()):n)?t="":"number"==typeof t?t+="":Array.isArray(t)&&(t=S.map(t,function(e){return null==e?"":e+""})),(r=S.valHooks[this.type]||S.valHooks[this.nodeName.toLowerCase()])&&"set"in r&&void 0!==r.set(this,t,"value")||(this.value=t))})):t?(r=S.valHooks[t.type]||S.valHooks[t.nodeName.toLowerCase()])&&"get"in r&&void 0!==(e=r.get(t,"value"))?e:"string"==typeof(e=t.value)?e.replace(yt,""):null==e?"":e:void 0}}),S.extend({valHooks:{option:{get:function(e){var t=S.find.attr(e,"value");return null!=t?t:ht(S.text(e))}},select:{get:function(e){var t,n,r,i=e.options,o=e.selectedIndex,a="select-one"===e.type,s=a?null:[],u=a?o+1:i.length;for(r=o<0?u:a?o:0;r<u;r++)if(((n=i[r]).selected||r===o)&&!n.disabled&&(!n.parentNode.disabled||!A(n.parentNode,"optgroup"))){if(t=S(n).val(),a)return t;s.push(t)}return s},set:function(e,t){var n,r,i=e.options,o=S.makeArray(t),a=i.length;while(a--)((r=i[a]).selected=-1<S.inArray(S.valHooks.option.get(r),o))&&(n=!0);return n||(e.selectedIndex=-1),o}}}}),S.each(["radio","checkbox"],function(){S.valHooks[this]={set:function(e,t){if(Array.isArray(t))return e.checked=-1<S.inArray(S(e).val(),t)}},y.checkOn||(S.valHooks[this].get=function(e){return null===e.getAttribute("value")?"on":e.value})}),y.focusin="onfocusin"in C;var mt=/^(?:focusinfocus|focusoutblur)$/,xt=function(e){e.stopPropagation()};S.extend(S.event,{trigger:function(e,t,n,r){var i,o,a,s,u,l,c,f,p=[n||E],d=v.call(e,"type")?e.type:e,h=v.call(e,"namespace")?e.namespace.split("."):[];if(o=f=a=n=n||E,3!==n.nodeType&&8!==n.nodeType&&!mt.test(d+S.event.triggered)&&(-1<d.indexOf(".")&&(d=(h=d.split(".")).shift(),h.sort()),u=d.indexOf(":")<0&&"on"+d,(e=e[S.expando]?e:new S.Event(d,"object"==typeof e&&e)).isTrigger=r?2:3,e.namespace=h.join("."),e.rnamespace=e.namespace?new RegExp("(^|\\.)"+h.join("\\.(?:.*\\.|)")+"(\\.|$)"):null,e.result=void 0,e.target||(e.target=n),t=null==t?[e]:S.makeArray(t,[e]),c=S.event.special[d]||{},r||!c.trigger||!1!==c.trigger.apply(n,t))){if(!r&&!c.noBubble&&!x(n)){for(s=c.delegateType||d,mt.test(s+d)||(o=o.parentNode);o;o=o.parentNode)p.push(o),a=o;a===(n.ownerDocument||E)&&p.push(a.defaultView||a.parentWindow||C)}i=0;while((o=p[i++])&&!e.isPropagationStopped())f=o,e.type=1<i?s:c.bindType||d,(l=(Y.get(o,"events")||Object.create(null))[e.type]&&Y.get(o,"handle"))&&l.apply(o,t),(l=u&&o[u])&&l.apply&&V(o)&&(e.result=l.apply(o,t),!1===e.result&&e.preventDefault());return e.type=d,r||e.isDefaultPrevented()||c._default&&!1!==c._default.apply(p.pop(),t)||!V(n)||u&&m(n[d])&&!x(n)&&((a=n[u])&&(n[u]=null),S.event.triggered=d,e.isPropagationStopped()&&f.addEventListener(d,xt),n[d](),e.isPropagationStopped()&&f.removeEventListener(d,xt),S.event.triggered=void 0,a&&(n[u]=a)),e.result}},simulate:function(e,t,n){var r=S.extend(new S.Event,n,{type:e,isSimulated:!0});S.event.trigger(r,null,t)}}),S.fn.extend({trigger:function(e,t){return this.each(function(){S.event.trigger(e,t,this)})},triggerHandler:function(e,t){var n=this[0];if(n)return S.event.trigger(e,t,n,!0)}}),y.focusin||S.each({focus:"focusin",blur:"focusout"},function(n,r){var i=function(e){S.event.simulate(r,e.target,S.event.fix(e))};S.event.special[r]={setup:function(){var e=this.ownerDocument||this.document||this,t=Y.access(e,r);t||e.addEventListener(n,i,!0),Y.access(e,r,(t||0)+1)},teardown:function(){var e=this.ownerDocument||this.document||this,t=Y.access(e,r)-1;t?Y.access(e,r,t):(e.removeEventListener(n,i,!0),Y.remove(e,r))}}});var bt=C.location,wt={guid:Date.now()},Tt=/\?/;S.parseXML=function(e){var t,n;if(!e||"string"!=typeof e)return null;try{t=(new C.DOMParser).parseFromString(e,"text/xml")}catch(e){}return n=t&&t.getElementsByTagName("parsererror")[0],t&&!n||S.error("Invalid XML: "+(n?S.map(n.childNodes,function(e){return e.textContent}).join("\n"):e)),t};var Ct=/\[\]$/,Et=/\r?\n/g,St=/^(?:submit|button|image|reset|file)$/i,kt=/^(?:input|select|textarea|keygen)/i;function At(n,e,r,i){var t;if(Array.isArray(e))S.each(e,function(e,t){r||Ct.test(n)?i(n,t):At(n+"["+("object"==typeof t&&null!=t?e:"")+"]",t,r,i)});else if(r||"object"!==w(e))i(n,e);else for(t in e)At(n+"["+t+"]",e[t],r,i)}S.param=function(e,t){var n,r=[],i=function(e,t){var n=m(t)?t():t;r[r.length]=encodeURIComponent(e)+"="+encodeURIComponent(null==n?"":n)};if(null==e)return"";if(Array.isArray(e)||e.jquery&&!S.isPlainObject(e))S.each(e,function(){i(this.name,this.value)});else for(n in e)At(n,e[n],t,i);return r.join("&")},S.fn.extend({serialize:function(){return S.param(this.serializeArray())},serializeArray:function(){return this.map(function(){var e=S.prop(this,"elements");return e?S.makeArray(e):this}).filter(function(){var e=this.type;return this.name&&!S(this).is(":disabled")&&kt.test(this.nodeName)&&!St.test(e)&&(this.checked||!pe.test(e))}).map(function(e,t){var n=S(this).val();return null==n?null:Array.isArray(n)?S.map(n,function(e){return{name:t.name,value:e.replace(Et,"\r\n")}}):{name:t.name,value:n.replace(Et,"\r\n")}}).get()}});var Nt=/%20/g,jt=/#.*$/,Dt=/([?&])_=[^&]*/,qt=/^(.*?):[ \t]*([^\r\n]*)$/gm,Lt=/^(?:GET|HEAD)$/,Ht=/^\/\//,Ot={},Pt={},Rt="*/".concat("*"),Mt=E.createElement("a");function It(o){return function(e,t){"string"!=typeof e&&(t=e,e="*");var n,r=0,i=e.toLowerCase().match(P)||[];if(m(t))while(n=i[r++])"+"===n[0]?(n=n.slice(1)||"*",(o[n]=o[n]||[]).unshift(t)):(o[n]=o[n]||[]).push(t)}}function Wt(t,i,o,a){var s={},u=t===Pt;function l(e){var r;return s[e]=!0,S.each(t[e]||[],function(e,t){var n=t(i,o,a);return"string"!=typeof n||u||s[n]?u?!(r=n):void 0:(i.dataTypes.unshift(n),l(n),!1)}),r}return l(i.dataTypes[0])||!s["*"]&&l("*")}function Ft(e,t){var n,r,i=S.ajaxSettings.flatOptions||{};for(n in t)void 0!==t[n]&&((i[n]?e:r||(r={}))[n]=t[n]);return r&&S.extend(!0,e,r),e}Mt.href=bt.href,S.extend({active:0,lastModified:{},etag:{},ajaxSettings:{url:bt.href,type:"GET",isLocal:/^(?:about|app|app-storage|.+-extension|file|res|widget):$/.test(bt.protocol),global:!0,processData:!0,async:!0,contentType:"application/x-www-form-urlencoded; charset=UTF-8",accepts:{"*":Rt,text:"text/plain",html:"text/html",xml:"application/xml, text/xml",json:"application/json, text/javascript"},contents:{xml:/\bxml\b/,html:/\bhtml/,json:/\bjson\b/},responseFields:{xml:"responseXML",text:"responseText",json:"responseJSON"},converters:{"* text":String,"text html":!0,"text json":JSON.parse,"text xml":S.parseXML},flatOptions:{url:!0,context:!0}},ajaxSetup:function(e,t){return t?Ft(Ft(e,S.ajaxSettings),t):Ft(S.ajaxSettings,e)},ajaxPrefilter:It(Ot),ajaxTransport:It(Pt),ajax:function(e,t){"object"==typeof e&&(t=e,e=void 0),t=t||{};var c,f,p,n,d,r,h,g,i,o,v=S.ajaxSetup({},t),y=v.context||v,m=v.context&&(y.nodeType||y.jquery)?S(y):S.event,x=S.Deferred(),b=S.Callbacks("once memory"),w=v.statusCode||{},a={},s={},u="canceled",T={readyState:0,getResponseHeader:function(e){var t;if(h){if(!n){n={};while(t=qt.exec(p))n[t[1].toLowerCase()+" "]=(n[t[1].toLowerCase()+" "]||[]).concat(t[2])}t=n[e.toLowerCase()+" "]}return null==t?null:t.join(", ")},getAllResponseHeaders:function(){return h?p:null},setRequestHeader:function(e,t){return null==h&&(e=s[e.toLowerCase()]=s[e.toLowerCase()]||e,a[e]=t),this},overrideMimeType:function(e){return null==h&&(v.mimeType=e),this},statusCode:function(e){var t;if(e)if(h)T.always(e[T.status]);else for(t in e)w[t]=[w[t],e[t]];return this},abort:function(e){var t=e||u;return c&&c.abort(t),l(0,t),this}};if(x.promise(T),v.url=((e||v.url||bt.href)+"").replace(Ht,bt.protocol+"//"),v.type=t.method||t.type||v.method||v.type,v.dataTypes=(v.dataType||"*").toLowerCase().match(P)||[""],null==v.crossDomain){r=E.createElement("a");try{r.href=v.url,r.href=r.href,v.crossDomain=Mt.protocol+"//"+Mt.host!=r.protocol+"//"+r.host}catch(e){v.crossDomain=!0}}if(v.data&&v.processData&&"string"!=typeof v.data&&(v.data=S.param(v.data,v.traditional)),Wt(Ot,v,t,T),h)return T;for(i in(g=S.event&&v.global)&&0==S.active++&&S.event.trigger("ajaxStart"),v.type=v.type.toUpperCase(),v.hasContent=!Lt.test(v.type),f=v.url.replace(jt,""),v.hasContent?v.data&&v.processData&&0===(v.contentType||"").indexOf("application/x-www-form-urlencoded")&&(v.data=v.data.replace(Nt,"+")):(o=v.url.slice(f.length),v.data&&(v.processData||"string"==typeof v.data)&&(f+=(Tt.test(f)?"&":"?")+v.data,delete v.data),!1===v.cache&&(f=f.replace(Dt,"$1"),o=(Tt.test(f)?"&":"?")+"_="+wt.guid+++o),v.url=f+o),v.ifModified&&(S.lastModified[f]&&T.setRequestHeader("If-Modified-Since",S.lastModified[f]),S.etag[f]&&T.setRequestHeader("If-None-Match",S.etag[f])),(v.data&&v.hasContent&&!1!==v.contentType||t.contentType)&&T.setRequestHeader("Content-Type",v.contentType),T.setRequestHeader("Accept",v.dataTypes[0]&&v.accepts[v.dataTypes[0]]?v.accepts[v.dataTypes[0]]+("*"!==v.dataTypes[0]?", "+Rt+"; q=0.01":""):v.accepts["*"]),v.headers)T.setRequestHeader(i,v.headers[i]);if(v.beforeSend&&(!1===v.beforeSend.call(y,T,v)||h))return T.abort();if(u="abort",b.add(v.complete),T.done(v.success),T.fail(v.error),c=Wt(Pt,v,t,T)){if(T.readyState=1,g&&m.trigger("ajaxSend",[T,v]),h)return T;v.async&&0<v.timeout&&(d=C.setTimeout(function(){T.abort("timeout")},v.timeout));try{h=!1,c.send(a,l)}catch(e){if(h)throw e;l(-1,e)}}else l(-1,"No Transport");function l(e,t,n,r){var i,o,a,s,u,l=t;h||(h=!0,d&&C.clearTimeout(d),c=void 0,p=r||"",T.readyState=0<e?4:0,i=200<=e&&e<300||304===e,n&&(s=function(e,t,n){var r,i,o,a,s=e.contents,u=e.dataTypes;while("*"===u[0])u.shift(),void 0===r&&(r=e.mimeType||t.getResponseHeader("Content-Type"));if(r)for(i in s)if(s[i]&&s[i].test(r)){u.unshift(i);break}if(u[0]in n)o=u[0];else{for(i in n){if(!u[0]||e.converters[i+" "+u[0]]){o=i;break}a||(a=i)}o=o||a}if(o)return o!==u[0]&&u.unshift(o),n[o]}(v,T,n)),!i&&-1<S.inArray("script",v.dataTypes)&&S.inArray("json",v.dataTypes)<0&&(v.converters["text script"]=function(){}),s=function(e,t,n,r){var i,o,a,s,u,l={},c=e.dataTypes.slice();if(c[1])for(a in e.converters)l[a.toLowerCase()]=e.converters[a];o=c.shift();while(o)if(e.responseFields[o]&&(n[e.responseFields[o]]=t),!u&&r&&e.dataFilter&&(t=e.dataFilter(t,e.dataType)),u=o,o=c.shift())if("*"===o)o=u;else if("*"!==u&&u!==o){if(!(a=l[u+" "+o]||l["* "+o]))for(i in l)if((s=i.split(" "))[1]===o&&(a=l[u+" "+s[0]]||l["* "+s[0]])){!0===a?a=l[i]:!0!==l[i]&&(o=s[0],c.unshift(s[1]));break}if(!0!==a)if(a&&e["throws"])t=a(t);else try{t=a(t)}catch(e){return{state:"parsererror",error:a?e:"No conversion from "+u+" to "+o}}}return{state:"success",data:t}}(v,s,T,i),i?(v.ifModified&&((u=T.getResponseHeader("Last-Modified"))&&(S.lastModified[f]=u),(u=T.getResponseHeader("etag"))&&(S.etag[f]=u)),204===e||"HEAD"===v.type?l="nocontent":304===e?l="notmodified":(l=s.state,o=s.data,i=!(a=s.error))):(a=l,!e&&l||(l="error",e<0&&(e=0))),T.status=e,T.statusText=(t||l)+"",i?x.resolveWith(y,[o,l,T]):x.rejectWith(y,[T,l,a]),T.statusCode(w),w=void 0,g&&m.trigger(i?"ajaxSuccess":"ajaxError",[T,v,i?o:a]),b.fireWith(y,[T,l]),g&&(m.trigger("ajaxComplete",[T,v]),--S.active||S.event.trigger("ajaxStop")))}return T},getJSON:function(e,t,n){return S.get(e,t,n,"json")},getScript:function(e,t){return S.get(e,void 0,t,"script")}}),S.each(["get","post"],function(e,i){S[i]=function(e,t,n,r){return m(t)&&(r=r||n,n=t,t=void 0),S.ajax(S.extend({url:e,type:i,dataType:r,data:t,success:n},S.isPlainObject(e)&&e))}}),S.ajaxPrefilter(function(e){var t;for(t in e.headers)"content-type"===t.toLowerCase()&&(e.contentType=e.headers[t]||"")}),S._evalUrl=function(e,t,n){return S.ajax({url:e,type:"GET",dataType:"script",cache:!0,async:!1,global:!1,converters:{"text script":function(){}},dataFilter:function(e){S.globalEval(e,t,n)}})},S.fn.extend({wrapAll:function(e){var t;return this[0]&&(m(e)&&(e=e.call(this[0])),t=S(e,this[0].ownerDocument).eq(0).clone(!0),this[0].parentNode&&t.insertBefore(this[0]),t.map(function(){var e=this;while(e.firstElementChild)e=e.firstElementChild;return e}).append(this)),this},wrapInner:function(n){return m(n)?this.each(function(e){S(this).wrapInner(n.call(this,e))}):this.each(function(){var e=S(this),t=e.contents();t.length?t.wrapAll(n):e.append(n)})},wrap:function(t){var n=m(t);return this.each(function(e){S(this).wrapAll(n?t.call(this,e):t)})},unwrap:function(e){return this.parent(e).not("body").each(function(){S(this).replaceWith(this.childNodes)}),this}}),S.expr.pseudos.hidden=function(e){return!S.expr.pseudos.visible(e)},S.expr.pseudos.visible=function(e){return!!(e.offsetWidth||e.offsetHeight||e.getClientRects().length)},S.ajaxSettings.xhr=function(){try{return new C.XMLHttpRequest}catch(e){}};var Bt={0:200,1223:204},$t=S.ajaxSettings.xhr();y.cors=!!$t&&"withCredentials"in $t,y.ajax=$t=!!$t,S.ajaxTransport(function(i){var o,a;if(y.cors||$t&&!i.crossDomain)return{send:function(e,t){var n,r=i.xhr();if(r.open(i.type,i.url,i.async,i.username,i.password),i.xhrFields)for(n in i.xhrFields)r[n]=i.xhrFields[n];for(n in i.mimeType&&r.overrideMimeType&&r.overrideMimeType(i.mimeType),i.crossDomain||e["X-Requested-With"]||(e["X-Requested-With"]="XMLHttpRequest"),e)r.setRequestHeader(n,e[n]);o=function(e){return function(){o&&(o=a=r.onload=r.onerror=r.onabort=r.ontimeout=r.onreadystatechange=null,"abort"===e?r.abort():"error"===e?"number"!=typeof r.status?t(0,"error"):t(r.status,r.statusText):t(Bt[r.status]||r.status,r.statusText,"text"!==(r.responseType||"text")||"string"!=typeof r.responseText?{binary:r.response}:{text:r.responseText},r.getAllResponseHeaders()))}},r.onload=o(),a=r.onerror=r.ontimeout=o("error"),void 0!==r.onabort?r.onabort=a:r.onreadystatechange=function(){4===r.readyState&&C.setTimeout(function(){o&&a()})},o=o("abort");try{r.send(i.hasContent&&i.data||null)}catch(e){if(o)throw e}},abort:function(){o&&o()}}}),S.ajaxPrefilter(function(e){e.crossDomain&&(e.contents.script=!1)}),S.ajaxSetup({accepts:{script:"text/javascript, application/javascript, application/ecmascript, application/x-ecmascript"},contents:{script:/\b(?:java|ecma)script\b/},converters:{"text script":function(e){return S.globalEval(e),e}}}),S.ajaxPrefilter("script",function(e){void 0===e.cache&&(e.cache=!1),e.crossDomain&&(e.type="GET")}),S.ajaxTransport("script",function(n){var r,i;if(n.crossDomain||n.scriptAttrs)return{send:function(e,t){r=S("<script>").attr(n.scriptAttrs||{}).prop({charset:n.scriptCharset,src:n.url}).on("load error",i=function(e){r.remove(),i=null,e&&t("error"===e.type?404:200,e.type)}),E.head.appendChild(r[0])},abort:function(){i&&i()}}});var _t,zt=[],Ut=/(=)\?(?=&|$)|\?\?/;S.ajaxSetup({jsonp:"callback",jsonpCallback:function(){var e=zt.pop()||S.expando+"_"+wt.guid++;return this[e]=!0,e}}),S.ajaxPrefilter("json jsonp",function(e,t,n){var r,i,o,a=!1!==e.jsonp&&(Ut.test(e.url)?"url":"string"==typeof e.data&&0===(e.contentType||"").indexOf("application/x-www-form-urlencoded")&&Ut.test(e.data)&&"data");if(a||"jsonp"===e.dataTypes[0])return r=e.jsonpCallback=m(e.jsonpCallback)?e.jsonpCallback():e.jsonpCallback,a?e[a]=e[a].replace(Ut,"$1"+r):!1!==e.jsonp&&(e.url+=(Tt.test(e.url)?"&":"?")+e.jsonp+"="+r),e.converters["script json"]=function(){return o||S.error(r+" was not called"),o[0]},e.dataTypes[0]="json",i=C[r],C[r]=function(){o=arguments},n.always(function(){void 0===i?S(C).removeProp(r):C[r]=i,e[r]&&(e.jsonpCallback=t.jsonpCallback,zt.push(r)),o&&m(i)&&i(o[0]),o=i=void 0}),"script"}),y.createHTMLDocument=((_t=E.implementation.createHTMLDocument("").body).innerHTML="<form></form><form></form>",2===_t.childNodes.length),S.parseHTML=function(e,t,n){return"string"!=typeof e?[]:("boolean"==typeof t&&(n=t,t=!1),t||(y.createHTMLDocument?((r=(t=E.implementation.createHTMLDocument("")).createElement("base")).href=E.location.href,t.head.appendChild(r)):t=E),o=!n&&[],(i=N.exec(e))?[t.createElement(i[1])]:(i=xe([e],t,o),o&&o.length&&S(o).remove(),S.merge([],i.childNodes)));var r,i,o},S.fn.load=function(e,t,n){var r,i,o,a=this,s=e.indexOf(" ");return-1<s&&(r=ht(e.slice(s)),e=e.slice(0,s)),m(t)?(n=t,t=void 0):t&&"object"==typeof t&&(i="POST"),0<a.length&&S.ajax({url:e,type:i||"GET",dataType:"html",data:t}).done(function(e){o=arguments,a.html(r?S("<div>").append(S.parseHTML(e)).find(r):e)}).always(n&&function(e,t){a.each(function(){n.apply(this,o||[e.responseText,t,e])})}),this},S.expr.pseudos.animated=function(t){return S.grep(S.timers,function(e){return t===e.elem}).length},S.offset={setOffset:function(e,t,n){var r,i,o,a,s,u,l=S.css(e,"position"),c=S(e),f={};"static"===l&&(e.style.position="relative"),s=c.offset(),o=S.css(e,"top"),u=S.css(e,"left"),("absolute"===l||"fixed"===l)&&-1<(o+u).indexOf("auto")?(a=(r=c.position()).top,i=r.left):(a=parseFloat(o)||0,i=parseFloat(u)||0),m(t)&&(t=t.call(e,n,S.extend({},s))),null!=t.top&&(f.top=t.top-s.top+a),null!=t.left&&(f.left=t.left-s.left+i),"using"in t?t.using.call(e,f):c.css(f)}},S.fn.extend({offset:function(t){if(arguments.length)return void 0===t?this:this.each(function(e){S.offset.setOffset(this,t,e)});var e,n,r=this[0];return r?r.getClientRects().length?(e=r.getBoundingClientRect(),n=r.ownerDocument.defaultView,{top:e.top+n.pageYOffset,left:e.left+n.pageXOffset}):{top:0,left:0}:void 0},position:function(){if(this[0]){var e,t,n,r=this[0],i={top:0,left:0};if("fixed"===S.css(r,"position"))t=r.getBoundingClientRect();else{t=this.offset(),n=r.ownerDocument,e=r.offsetParent||n.documentElement;while(e&&(e===n.body||e===n.documentElement)&&"static"===S.css(e,"position"))e=e.parentNode;e&&e!==r&&1===e.nodeType&&((i=S(e).offset()).top+=S.css(e,"borderTopWidth",!0),i.left+=S.css(e,"borderLeftWidth",!0))}return{top:t.top-i.top-S.css(r,"marginTop",!0),left:t.left-i.left-S.css(r,"marginLeft",!0)}}},offsetParent:function(){return this.map(function(){var e=this.offsetParent;while(e&&"static"===S.css(e,"position"))e=e.offsetParent;return e||re})}}),S.each({scrollLeft:"pageXOffset",scrollTop:"pageYOffset"},function(t,i){var o="pageYOffset"===i;S.fn[t]=function(e){return $(this,function(e,t,n){var r;if(x(e)?r=e:9===e.nodeType&&(r=e.defaultView),void 0===n)return r?r[i]:e[t];r?r.scrollTo(o?r.pageXOffset:n,o?n:r.pageYOffset):e[t]=n},t,e,arguments.length)}}),S.each(["top","left"],function(e,n){S.cssHooks[n]=Fe(y.pixelPosition,function(e,t){if(t)return t=We(e,n),Pe.test(t)?S(e).position()[n]+"px":t})}),S.each({Height:"height",Width:"width"},function(a,s){S.each({padding:"inner"+a,content:s,"":"outer"+a},function(r,o){S.fn[o]=function(e,t){var n=arguments.length&&(r||"boolean"!=typeof e),i=r||(!0===e||!0===t?"margin":"border");return $(this,function(e,t,n){var r;return x(e)?0===o.indexOf("outer")?e["inner"+a]:e.document.documentElement["client"+a]:9===e.nodeType?(r=e.documentElement,Math.max(e.body["scroll"+a],r["scroll"+a],e.body["offset"+a],r["offset"+a],r["client"+a])):void 0===n?S.css(e,t,i):S.style(e,t,n,i)},s,n?e:void 0,n)}})}),S.each(["ajaxStart","ajaxStop","ajaxComplete","ajaxError","ajaxSuccess","ajaxSend"],function(e,t){S.fn[t]=function(e){return this.on(t,e)}}),S.fn.extend({bind:function(e,t,n){return this.on(e,null,t,n)},unbind:function(e,t){return this.off(e,null,t)},delegate:function(e,t,n,r){return this.on(t,e,n,r)},undelegate:function(e,t,n){return 1===arguments.length?this.off(e,"**"):this.off(t,e||"**",n)},hover:function(e,t){return this.mouseenter(e).mouseleave(t||e)}}),S.each("blur focus focusin focusout resize scroll click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select submit keydown keypress keyup contextmenu".split(" "),function(e,n){S.fn[n]=function(e,t){return 0<arguments.length?this.on(n,null,e,t):this.trigger(n)}});var Xt=/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g;S.proxy=function(e,t){var n,r,i;if("string"==typeof t&&(n=e[t],t=e,e=n),m(e))return r=s.call(arguments,2),(i=function(){return e.apply(t||this,r.concat(s.call(arguments)))}).guid=e.guid=e.guid||S.guid++,i},S.holdReady=function(e){e?S.readyWait++:S.ready(!0)},S.isArray=Array.isArray,S.parseJSON=JSON.parse,S.nodeName=A,S.isFunction=m,S.isWindow=x,S.camelCase=X,S.type=w,S.now=Date.now,S.isNumeric=function(e){var t=S.type(e);return("number"===t||"string"===t)&&!isNaN(e-parseFloat(e))},S.trim=function(e){return null==e?"":(e+"").replace(Xt,"")},"function"==typeof define&&define.amd&&define("jquery",[],function(){return S});var Vt=C.jQuery,Gt=C.$;return S.noConflict=function(e){return C.$===S&&(C.$=Gt),e&&C.jQuery===S&&(C.jQuery=Vt),S},"undefined"==typeof e&&(C.jQuery=C.$=S),S}); diff --git a/src/main/resources/static/js/matchDetail.js b/src/main/resources/static/js/matchDetail.js new file mode 100644 index 0000000000000000000000000000000000000000..0cb7b2fe9a9018dbffd183c1d5ba1a5e99916b77 --- /dev/null +++ b/src/main/resources/static/js/matchDetail.js @@ -0,0 +1,29 @@ + const urlParams = new URLSearchParams(window.location.search); + const matchId = urlParams.get(`id`); + // èŽ·å–æ¯”赛详情 + async function fetchMatchDetails(id) { + try { + const response = await fetch(`/match/${id}`); + const matchData = await response.json(); + + const matchInfo = document.getElementById('matchInfo'); + const matchScore = document.getElementById('matchScore'); + // btn give the id + const updateBtn = document.getElementById("updateBtn"); + updateBtn.addEventListener('click', function (){ + window.location.href = `/html/matchDetailChangeScore.html?id=${id}`; + }) + matchScore.innerHTML = `<p><strong></strong>${matchData.scoreA} - ${matchData.scoreB}</p> +` + matchInfo.innerHTML = ` + <p><strong>Sport: </strong>${matchData.sport}</p> + <p><strong>Players: </strong>${matchData.playerAId} vs ${matchData.playerBId}</p> + <p><strong>Scheduled Time: </strong>${new Date(matchData.planTime).toLocaleString()}</p> + <p><strong>Status: </strong>${matchData.status}</p> + <p><strong>Winner: </strong>${matchData.winnerId ? matchData.winnerId : 'N/A'}</p> + `; +} catch (error) { + console.error('Error fetching match details:', error); +} +} + fetchMatchDetails(matchId); \ No newline at end of file diff --git a/src/main/resources/static/js/matchDetailChangeScore.js b/src/main/resources/static/js/matchDetailChangeScore.js new file mode 100644 index 0000000000000000000000000000000000000000..7c9f1c0f39ede7683f9cfbe372a6366ed789c69b --- /dev/null +++ b/src/main/resources/static/js/matchDetailChangeScore.js @@ -0,0 +1,67 @@ +loadNavbar('navbar-container'); +loadFooter('footer-container'); +const urlParams = new URLSearchParams(window.location.search); +const matchId = urlParams.get('id'); + +async function fetchMatchDetails(id) { + try { + const response = await fetch(`/match/${id}`); + const matchData = await response.json(); + document.getElementById('sport').value = matchData.sport; + document.getElementById('playerAId').value = matchData.playerAId; + document.getElementById('playerBId').value = matchData.playerBId; + document.getElementById('planTime').value = matchData.planTime; + document.getElementById('status').value = matchData.status; + document.getElementById('scoreA').value = matchData.scoreA; + document.getElementById('scoreB').value = matchData.scoreB; + document.getElementById('confirmByA').checked = matchData.confirmByA; + document.getElementById('confirmByB').checked = matchData.confirmByB; + document.getElementById('winnerId').value = matchData.winnerId; + // document.getElementById('scoreA').value = matchData.scoreA; + // document.getElementById('scoreB').value = matchData.scoreB; + // document.getElementById('winnerId').value = matchData.winnerId ? matchData.winnerId : ''; + } catch (error) { + console.error('Error fetching match details:', error); + } +} + +document.getElementById('updateMatchForm').addEventListener('submit', async function(event) { + // prevent evnts + event.preventDefault(); + + + const updatedMatch = { + sport: document.getElementById('sport').value, + playerAId: document.getElementById('playerAId').value, + playerBId: document.getElementById('playerBId').value, + planTime: document.getElementById('planTime').value, + status: document.getElementById('status').value, + scoreA: document.getElementById('scoreA').value, + scoreB: document.getElementById('scoreB').value, + confirmByA: document.getElementById('confirmByA').checked, + confirmByB: document.getElementById('confirmByB').checked, + winnerId: document.getElementById('winnerId').value + }; + + try { + const res = await fetch(`/match/${matchId}`, { + method: 'PUT', + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify(updatedMatch) + }); + + if (res.ok) { + alert('Match updated successfully'); + window.location.href = `/html/matchDetail.html?id=${matchId}`; + } else { + alert('Failed to update match'); + } + } catch (err) { + console.error('error updating match:', err); + alert('error updating match'); + } +}); + +fetchMatchDetails(matchId); \ No newline at end of file diff --git a/src/main/resources/static/js/navBar.js b/src/main/resources/static/js/navBar.js new file mode 100644 index 0000000000000000000000000000000000000000..9976453a376e89ad01838efb3626197bd0629f6f --- /dev/null +++ b/src/main/resources/static/js/navBar.js @@ -0,0 +1,60 @@ +// function loadNavbar(targetElementId) { +// fetch('navbar.html') +// .then(response => response.text()) +// .then(data => { +// // add the bar +// document.getElementById(targetElementId).innerHTML = data; +// }) +// .catch(error => { +// console.error('loadNav errors:', error); +// }); +// } + +// Load the navigation bar dynamically +function loadNavbar(targetElementId) { + fetch('navbar.html') + .then(response => response.text()) + .then(data => { + // Add the navbar content to the target element + document.getElementById(targetElementId).innerHTML = data; + + // After the navbar is loaded, check and update user authentication state + updateAuthButtons(); + }) + .catch(error => { + console.error('loadNav errors:', error); + }); +} + +// Update authentication buttons based on user login state +function updateAuthButtons() { + fetch('/users/current', { method: 'GET' }) + .then(response => { + if (!response.ok) { + throw new Error('User not logged in'); + } + return response.json(); + }) + .then(data => { + // If logged in, update Login button to display username + const authBtn = document.getElementById('auth-btn'); + authBtn.textContent = data.username; + authBtn.href = '#'; // Disable navigation for username display + authBtn.classList.remove('login-btn'); + authBtn.classList.add('username-btn'); + + // Show the Logout button + document.getElementById('logout-btn').style.display = 'block'; + }) + .catch(() => { + // If not logged in, reset the buttons to default state + const authBtn = document.getElementById('auth-btn'); + authBtn.textContent = 'Login'; + authBtn.href = '/html/login.html'; // Link back to the login page + authBtn.classList.add('login-btn'); + authBtn.classList.remove('username-btn'); + + // Hide the Logout button + document.getElementById('logout-btn').style.display = 'none'; + }); +} diff --git a/src/main/resources/static/js/rankingTable.js b/src/main/resources/static/js/rankingTable.js new file mode 100644 index 0000000000000000000000000000000000000000..4fc73715d56666ebf495744b70e9ecc44d899a4f --- /dev/null +++ b/src/main/resources/static/js/rankingTable.js @@ -0,0 +1,55 @@ +// Function to add fade-in animation +function applyFadeIn() { + const tableBody = document.getElementById('ranking-table'); + tableBody.classList.add('fade-in'); // Add animation class + setTimeout(() => tableBody.classList.remove('fade-in'), 500); // Remove class after animation +} + +async function loadRankings() { + const sport = document.getElementById('filter-sport').value; + const sortOption = document.getElementById('sort-option').value; + + let apiUrl; + + if (sport === "all") { + apiUrl = `/rankings/byallandsort?order=${sortOption}`; + } else { + apiUrl = `/rankings/bysportandsort?sport=${sport}&order=${sortOption}`; + } + try { + const response = await fetch(apiUrl); + if (!response.ok) { + throw new Error(`HTTP error! Status: ${response.status}`); + } + + const rankings = await response.json(); + + const tableBody = document.getElementById('ranking-table'); + tableBody.innerHTML = ''; // Clear current table content + + // Add rows to the table + rankings.forEach((ranking, index) => { + const row = document.createElement('tr'); + row.innerHTML = ` + <td>${index + 1}</td> + <td>${ranking.username || 'N/A'}</td> + <td>${ranking.wins || 0}</td> + <td>${ranking.sport || 'All'}</td> + `; + tableBody.appendChild(row); + }); + + // Apply fade-in effect + applyFadeIn(); + } catch (error) { + console.error('Error fetching rankings:', error); + } +} + +// Add event listeners +document.getElementById('filter-sport').addEventListener('change', loadRankings); +document.getElementById('sort-option').addEventListener('change', loadRankings); + +window.onload = loadRankings; + + diff --git a/src/main/resources/static/js/search.js b/src/main/resources/static/js/search.js new file mode 100644 index 0000000000000000000000000000000000000000..0ebd5fe534496ea2490199bb090e08ccea79fb63 --- /dev/null +++ b/src/main/resources/static/js/search.js @@ -0,0 +1,79 @@ +// æ•°æ®åº“ +const data = [ + { + title: 'Pools Rules', + description: 'Learn the basic rules of pools, including scoring and penalties.', + url: 'https://rileys.co.uk/games/english-pool' + }, + { + title: 'Darts Rules', + description: 'An overview of darts rules, including fouls and court dimensions.', + url: 'http://localhost:8080/html/dartsrules.html' + }, + { + title: 'Table-Tennis Rules', + description: 'Understand the rules of table-tennis, including scoring and serving.', + url: 'http://localhost:8080/html/table-tennisrules.html' + }, + +]; + +// åˆå§‹åŒ– Fuse.js +const fuse = new Fuse(data, { + includeScore: true, + keys: ['title', 'description'] +}); + +// å…ƒç´ èŽ·å– +const searchBar = document.getElementById('search-bar'); +const searchResults = document.getElementById('search-results'); + +// 页é¢åŠ è½½æ—¶æ˜¾ç¤ºæ‰€æœ‰è§„åˆ™ +renderResults(data); + +// æœç´¢ç›‘å¬ +searchBar.addEventListener('input', (event) => { + const query = event.target.value.trim(); + if (query === '') { + renderResults(data); // 显示全部 + return; + } + + // 执行æœç´¢ + const results = fuse.search(query).map(result => result.item); + renderResults(results); +}); + +// 渲染结果 +function renderResults(results) { + searchResults.innerHTML = ''; + + if (results.length === 0) { + searchResults.innerHTML = '<p>No results found.</p>'; + return; + } + + results.forEach(item => { + const { title, description, url } = item; + + // 创建结果项 + const itemDiv = document.createElement('div'); + itemDiv.className = 'search-result-item'; + + const link = document.createElement('a'); + link.href = url; + link.target = '_blank'; + + const titleElement = document.createElement('h3'); + titleElement.textContent = title; + + const descriptionElement = document.createElement('p'); + descriptionElement.textContent = description; + + link.appendChild(titleElement); + itemDiv.appendChild(link); + itemDiv.appendChild(descriptionElement); + searchResults.appendChild(itemDiv); + }); +} + diff --git a/src/main/resources/static/pics/photo.jpg b/src/main/resources/static/pics/photo.jpg new file mode 100644 index 0000000000000000000000000000000000000000..b4f050d36e92f0e73d664d6a67699b83c1cff538 Binary files /dev/null and b/src/main/resources/static/pics/photo.jpg differ diff --git a/src/main/resources/templates/Usercenter/Usercenter.html b/src/main/resources/templates/Usercenter/Usercenter.html index 4f1c57ee84ce5cc4ed12c58ffdb3bb8a24636538..170d68f4ff4ddf962309b5160e6605e7cad0e770 100644 --- a/src/main/resources/templates/Usercenter/Usercenter.html +++ b/src/main/resources/templates/Usercenter/Usercenter.html @@ -3,8 +3,10 @@ <head> <meta charset="UTF-8"> <title>Title</title> + <link rel="stylesheet" href="../css/UserCenter.css"> </head> <body> + <table> <thead> <tr> diff --git a/src/main/resources/templates/ranking/ranking.html b/src/main/resources/templates/ranking/ranking.html new file mode 100644 index 0000000000000000000000000000000000000000..566549bdf8fae810809c1a81066000687cb338f6 --- /dev/null +++ b/src/main/resources/templates/ranking/ranking.html @@ -0,0 +1,10 @@ +<!DOCTYPE html> +<html lang="en"> +<head> + <meta charset="UTF-8"> + <title>Title</title> +</head> +<body> + +</body> +</html> \ No newline at end of file diff --git a/src/test/java/uk/ac/cf/spring/demo/MatchControllerTest.java b/src/test/java/uk/ac/cf/spring/demo/MatchControllerTest.java new file mode 100644 index 0000000000000000000000000000000000000000..a1a39daff8130fc5f7d5be03974d6923de5cd8d2 --- /dev/null +++ b/src/test/java/uk/ac/cf/spring/demo/MatchControllerTest.java @@ -0,0 +1,61 @@ +package uk.ac.cf.spring.demo; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.setup.MockMvcBuilders; +import uk.ac.cf.spring.demo.sports.match.MatchController; +import uk.ac.cf.spring.demo.sports.match.MatchItem; +import uk.ac.cf.spring.demo.sports.match.MatchService; + +import java.util.Arrays; +import java.util.List; + +import static org.mockito.Mockito.*; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +@SpringBootTest +public class MatchControllerTest { + + @Mock + private MatchService matchService; + + @InjectMocks + private MatchController matchController; + + private MockMvc mockMvc; + + @BeforeEach + void setUp() { + mockMvc = MockMvcBuilders.standaloneSetup(matchController).build(); + } + + @Test + void testGetAllMatches() throws Exception { + MatchItem match1 = new MatchItem(1L, "Soccer", 1L, 2L, null, "completed", 2L, 3L, true, true, 2L); + List<MatchItem> matches = Arrays.asList(match1); + + when(matchService.getMatchItems()).thenReturn(matches); + + mockMvc.perform(get("/match")) + .andExpect(status().isOk()); + + verify(matchService, times(1)).getMatchItems(); + } + + @Test + void testGetMatchById() throws Exception { + MatchItem match1 = new MatchItem(1L, "Soccer", 1L, 2L, null, "completed", 2L, 3L, true, true, 2L); + + when(matchService.getMatchItemById(1L)).thenReturn(match1); + + mockMvc.perform(get("/match/1")) + .andExpect(status().isOk()); + + verify(matchService, times(1)).getMatchItemById(1L); + } +} diff --git a/src/test/java/uk/ac/cf/spring/demo/MatchServiceTest.java b/src/test/java/uk/ac/cf/spring/demo/MatchServiceTest.java new file mode 100644 index 0000000000000000000000000000000000000000..a45d45c29b4ba8e311c9a79cc925b52d3ecf92a2 --- /dev/null +++ b/src/test/java/uk/ac/cf/spring/demo/MatchServiceTest.java @@ -0,0 +1,69 @@ +package uk.ac.cf.spring.demo; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import uk.ac.cf.spring.demo.sports.match.MatchItem; +import uk.ac.cf.spring.demo.sports.match.MatchRepository; +import uk.ac.cf.spring.demo.sports.match.MatchServiceListImpl; + +import java.time.LocalDateTime; +import java.util.Arrays; +import java.util.List; + +import static org.mockito.Mockito.*; +import static org.junit.jupiter.api.Assertions.*; + +@ExtendWith(MockitoExtension.class) // 使得 Mockito 能够工作 +public class MatchServiceTest { + + @Mock + private MatchRepository matchRepository; // 模拟 MatchRepository + + @InjectMocks + private MatchServiceListImpl matchService; // 注入 MatchServiceListImpl + + private MatchItem matchItem1; + private MatchItem matchItem2; + + @BeforeEach + public void setUp() { + // 设置一些åˆå§‹çš„ MatchItem æ•°æ® + matchItem1 = new MatchItem(1L, "Soccer", 1L, 2L, LocalDateTime.now(), "pending", 0L, 0L, false, false, 0L); + matchItem2 = new MatchItem(2L, "Basketball", 3L, 4L, LocalDateTime.now(), "pending", 0L, 0L, false, false, 0L); + } + + @Test + public void testGetMatchItems() { + + when(matchRepository.getMatchItems()).thenReturn(Arrays.asList(matchItem1, matchItem2)); + List<MatchItem> matchItems = matchService.getMatchItems(); + assertNotNull(matchItems); + assertEquals(2, matchItems.size()); + assertEquals("Soccer", matchItems.get(0).getSport()); + assertEquals("Basketball", matchItems.get(1).getSport()); + verify(matchRepository, times(1)).getMatchItems(); + } + + @Test + public void testGetMatchItemById() { + + when(matchRepository.getMatchItemById(1L)).thenReturn(matchItem1); + MatchItem matchItem = matchService.getMatchItemById(1L); + assertNotNull(matchItem); + assertEquals(1L, matchItem.getId()); + assertEquals("Soccer", matchItem.getSport()); + + verify(matchRepository, times(1)).getMatchItemById(1L); + } + + @Test + public void testAddMatchItem() { + MatchItem newMatchItem = new MatchItem(0L, "Tennis", 5L, 6L, LocalDateTime.now(), "pending", 0L, 0L, false, false, 0L); + matchService.addMatchItem(newMatchItem); + verify(matchRepository, times(1)).addMatchItem(newMatchItem); + } +}