├── src ├── main │ ├── resources │ │ ├── application.properties │ │ ├── templates │ │ │ └── ajax.html │ │ └── static │ │ │ └── js │ │ │ ├── main.js │ │ │ ├── jquery.autocomplete.js │ │ │ └── jquery-3.2.0.min.js │ └── java │ │ └── com │ │ └── example │ │ └── demo │ │ ├── SymbolRepository.java │ │ ├── RealizedStockRepository.java │ │ ├── SuggestionWrapper.java │ │ ├── StockTransactionRepository.java │ │ ├── MainController.java │ │ ├── Symbol.java │ │ ├── AutocompleteController.java │ │ ├── StockTotalObject.java │ │ ├── StockRealized.java │ │ ├── StockTransaction.java │ │ ├── StockController.java │ │ ├── DemoApplication.java │ │ └── StockService.java └── test │ └── java │ └── com │ └── example │ └── demo │ └── DemoApplicationTests.java ├── .mvn └── wrapper │ ├── maven-wrapper.jar │ └── maven-wrapper.properties ├── .gitignore ├── Dockerfile ├── README.md ├── pom.xml ├── mvnw.cmd └── mvnw /src/main/resources/application.properties: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.mvn/wrapper/maven-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jonathanborenstein/StockTracker/HEAD/.mvn/wrapper/maven-wrapper.jar -------------------------------------------------------------------------------- /.mvn/wrapper/maven-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionUrl=https://repo1.maven.org/maven2/org/apache/maven/apache-maven/3.3.9/apache-maven-3.3.9-bin.zip 2 | -------------------------------------------------------------------------------- /src/main/java/com/example/demo/SymbolRepository.java: -------------------------------------------------------------------------------- 1 | package com.example.demo; 2 | 3 | import org.springframework.data.jpa.repository.JpaRepository; 4 | 5 | public interface SymbolRepository extends JpaRepository { 6 | 7 | } 8 | -------------------------------------------------------------------------------- /src/main/java/com/example/demo/RealizedStockRepository.java: -------------------------------------------------------------------------------- 1 | package com.example.demo; 2 | 3 | import org.springframework.data.jpa.repository.JpaRepository; 4 | 5 | public interface RealizedStockRepository extends JpaRepository {} 6 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | target/ 2 | !.mvn/wrapper/maven-wrapper.jar 3 | 4 | ### STS ### 5 | .classpath 6 | .factorypath 7 | .project 8 | .settings 9 | .springBeans 10 | 11 | ### IntelliJ IDEA ### 12 | .idea 13 | *.iws 14 | *.iml 15 | *.ipr 16 | 17 | ### NetBeans ### 18 | nbproject/private/ 19 | build/ 20 | nbbuild/ 21 | dist/ 22 | nbdist/ 23 | .nb-gradle/ -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM maven:3.6.0-jdk-8-slim AS build 2 | COPY src /home/app/src 3 | COPY pom.xml /home/app 4 | RUN mvn -f /home/app/pom.xml clean package 5 | 6 | # 7 | # Package stage 8 | # 9 | FROM openjdk:8-jdk-alpine 10 | COPY --from=build /home/app/target/demo-0.0.1-SNAPSHOT.jar /usr/local/lib/demo.jar 11 | EXPOSE 8080 12 | ENTRYPOINT ["java","-jar","/usr/local/lib/demo.jar"] 13 | -------------------------------------------------------------------------------- /src/main/java/com/example/demo/SuggestionWrapper.java: -------------------------------------------------------------------------------- 1 | package com.example.demo; 2 | 3 | 4 | import java.util.List; 5 | 6 | public class SuggestionWrapper { 7 | 8 | List suggestions; 9 | 10 | public List getSuggestions() { 11 | return suggestions; 12 | } 13 | 14 | public void setSuggestions(List suggestions) { 15 | this.suggestions = suggestions; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/main/java/com/example/demo/StockTransactionRepository.java: -------------------------------------------------------------------------------- 1 | package com.example.demo; 2 | 3 | import java.util.List; 4 | 5 | import org.springframework.data.jpa.repository.JpaRepository; 6 | 7 | public interface StockTransactionRepository extends JpaRepository { 8 | 9 | List findBySymbol(String symbol); 10 | 11 | List findAllByOrderBySymbolAsc(); 12 | 13 | 14 | 15 | } 16 | -------------------------------------------------------------------------------- /src/test/java/com/example/demo/DemoApplicationTests.java: -------------------------------------------------------------------------------- 1 | package com.example.demo; 2 | 3 | import org.junit.Test; 4 | import org.junit.runner.RunWith; 5 | import org.springframework.boot.test.context.SpringBootTest; 6 | import org.springframework.test.context.junit4.SpringRunner; 7 | 8 | @RunWith(SpringRunner.class) 9 | @SpringBootTest 10 | public class DemoApplicationTests { 11 | 12 | @Test 13 | public void contextLoads() { 14 | } 15 | 16 | } 17 | -------------------------------------------------------------------------------- /src/main/java/com/example/demo/MainController.java: -------------------------------------------------------------------------------- 1 | package com.example.demo; 2 | 3 | import org.springframework.stereotype.Controller; 4 | import org.springframework.ui.Model; 5 | import org.springframework.web.bind.annotation.GetMapping; 6 | 7 | @Controller 8 | public class MainController { 9 | 10 | @GetMapping("/") 11 | public String home(StockTransaction stockTransaction, Model model){ 12 | model.addAttribute("stockTransaction", stockTransaction); 13 | return "ajax"; 14 | } 15 | 16 | } 17 | -------------------------------------------------------------------------------- /src/main/java/com/example/demo/Symbol.java: -------------------------------------------------------------------------------- 1 | package com.example.demo; 2 | 3 | import javax.persistence.Entity; 4 | import javax.persistence.GeneratedValue; 5 | import javax.persistence.Id; 6 | 7 | @Entity 8 | public class Symbol { 9 | 10 | @Id 11 | @GeneratedValue 12 | private Long id; 13 | 14 | private String symbol; 15 | 16 | public Symbol(){}; 17 | 18 | public Symbol(String symbol){ 19 | this.symbol = symbol; 20 | } 21 | 22 | public String getSymbol() { 23 | return symbol; 24 | } 25 | 26 | public void setSymbol(String symbol) { 27 | this.symbol = symbol; 28 | } 29 | 30 | public Long getId() { 31 | return id; 32 | } 33 | 34 | public void setId(Long id) { 35 | this.id = id; 36 | } 37 | 38 | } 39 | -------------------------------------------------------------------------------- /src/main/java/com/example/demo/AutocompleteController.java: -------------------------------------------------------------------------------- 1 | package com.example.demo; 2 | 3 | 4 | import java.util.ArrayList; 5 | import java.util.List; 6 | import java.util.stream.Collectors; 7 | 8 | import org.springframework.stereotype.Controller; 9 | import org.springframework.web.bind.annotation.RequestMapping; 10 | import org.springframework.web.bind.annotation.RequestMethod; 11 | import org.springframework.web.bind.annotation.RequestParam; 12 | import org.springframework.web.bind.annotation.ResponseBody; 13 | 14 | @Controller 15 | public class AutocompleteController { 16 | 17 | private final SymbolRepository symbolRepository; 18 | 19 | 20 | 21 | public AutocompleteController(SymbolRepository symbolRepository) { 22 | this.symbolRepository = symbolRepository; 23 | } 24 | 25 | 26 | @RequestMapping(value = "/suggestion", method = RequestMethod.GET, produces = "application/json") 27 | @ResponseBody 28 | public SuggestionWrapper autocompleteSuggestions(@RequestParam("searchstr") String searchstr) { 29 | 30 | List suggestions = new ArrayList<>(); 31 | 32 | List symbols = symbolRepository.findAll().stream() 33 | .map(e -> e.getSymbol()) 34 | .collect(Collectors.toList()); 35 | 36 | for (String symbol : symbols) { 37 | 38 | if (symbol.toLowerCase().contains(searchstr.toLowerCase())) { 39 | suggestions.add(symbol); 40 | } 41 | } 42 | 43 | int n = suggestions.size() > 20 ? 20 : suggestions.size(); 44 | List sulb = new ArrayList<>(suggestions).subList(0, n); 45 | 46 | SuggestionWrapper sw = new SuggestionWrapper(); 47 | sw.setSuggestions(sulb); 48 | return sw; 49 | } 50 | 51 | 52 | } 53 | -------------------------------------------------------------------------------- /src/main/java/com/example/demo/StockTotalObject.java: -------------------------------------------------------------------------------- 1 | package com.example.demo; 2 | 3 | import java.math.BigDecimal; 4 | 5 | public class StockTotalObject { 6 | 7 | private BigDecimal totalPrice; 8 | private BigDecimal averagePrice; 9 | private BigDecimal profit; 10 | private BigDecimal currentPrice; 11 | private int totalShares; 12 | private String symbol; 13 | 14 | 15 | public StockTotalObject(BigDecimal totalPrice, BigDecimal averagePrice, BigDecimal profit, BigDecimal currentPrice, 16 | int totalShares, String symbol) { 17 | this.totalPrice = totalPrice; 18 | this.averagePrice = averagePrice; 19 | this.profit = profit; 20 | this.currentPrice = currentPrice; 21 | this.totalShares = totalShares; 22 | this.symbol = symbol; 23 | } 24 | 25 | public BigDecimal getTotalPrice() { 26 | return totalPrice; 27 | } 28 | public void setTotalPrice(BigDecimal totalPrice) { 29 | this.totalPrice = totalPrice; 30 | } 31 | public BigDecimal getAveragePrice() { 32 | return averagePrice; 33 | } 34 | public void setAveragePrice(BigDecimal averagePrice) { 35 | this.averagePrice = averagePrice; 36 | } 37 | public BigDecimal getProfit() { 38 | return profit; 39 | } 40 | public void setProfit(BigDecimal profit) { 41 | this.profit = profit; 42 | } 43 | public BigDecimal getCurrentPrice() { 44 | return currentPrice; 45 | } 46 | public void setCurrentPrice(BigDecimal currentPrice) { 47 | this.currentPrice = currentPrice; 48 | } 49 | public int getTotalShares() { 50 | return totalShares; 51 | } 52 | public void setTotalShares(int totalShares) { 53 | this.totalShares = totalShares; 54 | } 55 | public String getSymbol() { 56 | return symbol; 57 | } 58 | public void setSymbol(String symbol) { 59 | this.symbol = symbol; 60 | } 61 | 62 | 63 | 64 | } 65 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # StockTracker 2 | A web application for tracking your portfolio 3 | 4 | UPDATE: An example can be found here: https://stocktracker1.herokuapp.com/ 5 | 6 | UPDATE: The program has been updated so that when you sell some of your shares, the realized profit will be taken from the first lot of shares you bought that has not been sold off yet. For example, if you purchase 10 shares of AAPL at $100, purchase another 10 shares of AAPL at $105, and then decide to sell 12 shares at $110, the first 10 shares will be sold and recorded with a profit of ($110 - $100), while the next 2 shares will be sold and recorded at a price of ($110 - 105). Once a lot is completely sold off it is not taken into account anymore. 7 | 8 | This program is built with the Spring Framework, such as the Spring Boot, Spring MVC, and Spring JPA projects. It uses Google's GSON, as well as an emedded H2 database. If you don't want to use the H2 database you can save the information to a database in the application.properties file. The program already has the connector jar for MySQL. 9 | 10 | The program is very simple to use. Just run the program and go to Add Stock Transactions. Here you are just entering information about stocks you have bought. For example, if you purchased 10 shares of NFLX at 100 dollars per share you would add that. You are then shown the current unrealized profit on those shares. 11 | 12 | Perhaps you would add 20 shares of VZ bought at 40 dollars per share. You would add that and see it in your unrealized profits list. You will also see the dates of these transactions in a table. 13 | 14 | If you decide to sell 5 shares of your NFLX stock at a price of 105 dollars per share ($25 profit in this case) you will see that in your realized gains. In order to sell shares all you have to do is simply put a minus sign when you are adding a stock transaction (-5 in this case). 15 | 16 | An easy way to run the project is: 17 | git clone https://github.com/jonathanborenstein/StockTracker.git 18 | 19 | Go to the directory where you cloned it and go to StockTracker/StockProject 20 | 21 | Once there run 22 | mvn clean spring-boot:run 23 | 24 | Once the program is running go to localhost:8080 as your starting point. 25 | 26 | To user Docker, first build the image with the command docker build -t stocktracker .. To run the container use the command docker run -p 8080:8080 stocktracker:latest and it should run at localhost:8080. 27 | 28 | 29 | -------------------------------------------------------------------------------- /src/main/java/com/example/demo/StockRealized.java: -------------------------------------------------------------------------------- 1 | package com.example.demo; 2 | 3 | import java.math.BigDecimal; 4 | 5 | import javax.persistence.Column; 6 | import javax.persistence.Entity; 7 | import javax.persistence.GeneratedValue; 8 | import javax.persistence.GenerationType; 9 | import javax.persistence.Id; 10 | 11 | @Entity 12 | public class StockRealized { 13 | 14 | @Id 15 | @GeneratedValue(strategy = GenerationType.AUTO) 16 | private Long id; 17 | 18 | private int numShares; 19 | 20 | @Column(precision = 11, scale = 3) 21 | private BigDecimal price; 22 | 23 | @Column(precision = 11, scale = 3) 24 | private BigDecimal proceeds; 25 | 26 | @Column(precision = 11, scale = 3) 27 | private BigDecimal cost; 28 | 29 | private String symbol; 30 | 31 | @Column(precision = 11, scale = 3) 32 | private BigDecimal realizedProfit; 33 | 34 | public StockRealized(){} 35 | 36 | public StockRealized(int numShares, BigDecimal price, BigDecimal proceeds, BigDecimal cost, String symbol, 37 | BigDecimal realizedProfit) { 38 | this.numShares = numShares; 39 | this.price = price; 40 | this.proceeds = proceeds; 41 | this.cost = cost; 42 | this.symbol = symbol; 43 | this.realizedProfit = realizedProfit; 44 | } 45 | 46 | 47 | public Long getId() { 48 | return id; 49 | } 50 | 51 | public void setId(Long id) { 52 | this.id = id; 53 | } 54 | 55 | public int getNumShares() { 56 | return numShares; 57 | } 58 | 59 | public void setNumShares(int numShares) { 60 | this.numShares = numShares; 61 | } 62 | 63 | public BigDecimal getPrice() { 64 | return price; 65 | } 66 | 67 | public void setPrice(BigDecimal price) { 68 | this.price = price; 69 | } 70 | 71 | public BigDecimal getProceeds() { 72 | return proceeds; 73 | } 74 | 75 | public void setProceeds(BigDecimal proceeds) { 76 | this.proceeds = proceeds; 77 | } 78 | 79 | public BigDecimal getCost() { 80 | return cost; 81 | } 82 | 83 | public void setCost(BigDecimal cost) { 84 | this.cost = cost; 85 | } 86 | 87 | public String getSymbol() { 88 | return symbol; 89 | } 90 | 91 | public void setSymbol(String symbol) { 92 | this.symbol = symbol; 93 | } 94 | 95 | public BigDecimal getRealizedProfit() { 96 | return realizedProfit; 97 | } 98 | 99 | public void setRealizedProfit(BigDecimal realizedProfit) { 100 | this.realizedProfit = realizedProfit; 101 | } 102 | 103 | 104 | 105 | 106 | 107 | } -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | com.example 7 | demo 8 | 0.0.1-SNAPSHOT 9 | jar 10 | 11 | StockTracker 12 | StockTracker 13 | 14 | 15 | org.springframework.boot 16 | spring-boot-starter-parent 17 | 1.5.9.RELEASE 18 | 19 | 20 | 21 | 22 | UTF-8 23 | UTF-8 24 | 1.8 25 | 26 | 27 | 28 | 29 | 30 | org.springframework.boot 31 | spring-boot-starter-data-jpa 32 | 33 | 34 | com.google.code.gson 35 | gson 36 | 2.6.2 37 | 38 | 39 | commons-io 40 | commons-io 41 | 2.5 42 | 43 | 44 | org.springframework.boot 45 | spring-boot-starter-thymeleaf 46 | 47 | 48 | 49 | org.springframework.boot 50 | spring-boot-devtools 51 | true 52 | 53 | 54 | 55 | org.webjars 56 | jquery 57 | 2.2.4 58 | 59 | 60 | 61 | org.webjars 62 | bootstrap 63 | 3.3.7 64 | 65 | 66 | 67 | org.springframework.boot 68 | spring-boot-starter-web 69 | 70 | 71 | 72 | com.h2database 73 | h2 74 | runtime 75 | 76 | 77 | org.springframework.boot 78 | spring-boot-starter-test 79 | test 80 | 81 | 82 | 83 | 84 | 85 | 86 | org.springframework.boot 87 | spring-boot-maven-plugin 88 | 89 | 90 | 91 | 92 | 93 | 94 | -------------------------------------------------------------------------------- /src/main/java/com/example/demo/StockTransaction.java: -------------------------------------------------------------------------------- 1 | package com.example.demo; 2 | 3 | import java.math.BigDecimal; 4 | import java.util.Date; 5 | 6 | import javax.persistence.Column; 7 | import javax.persistence.Entity; 8 | import javax.persistence.GeneratedValue; 9 | import javax.persistence.Id; 10 | import javax.persistence.PrePersist; 11 | import javax.persistence.Temporal; 12 | import javax.persistence.TemporalType; 13 | import javax.validation.constraints.Digits; 14 | 15 | import org.hibernate.validator.constraints.NotBlank; 16 | import org.springframework.format.annotation.DateTimeFormat; 17 | 18 | @Entity 19 | public class StockTransaction { 20 | 21 | @Id 22 | @GeneratedValue 23 | private Long id; 24 | 25 | @Digits(integer = 8, fraction = 0) 26 | private int numOfShares; 27 | 28 | @Digits(integer = 8, fraction = 10) 29 | private int sharesInLot; 30 | 31 | private State state; 32 | 33 | public static enum State { 34 | PURCHASE, SALE 35 | } 36 | 37 | 38 | @Column(precision = 11, scale = 3) 39 | private BigDecimal priceOfShares; 40 | 41 | @NotBlank 42 | private String symbol; 43 | 44 | @Column(name = "added") 45 | @Temporal(TemporalType.TIMESTAMP) 46 | @DateTimeFormat(pattern="yyyy/MM/dd hh:mm:ss") 47 | private Date added; 48 | 49 | 50 | @PrePersist 51 | protected void onCreate() { 52 | if (added == null) { 53 | added = new Date(); 54 | } 55 | } 56 | 57 | public State getState() { 58 | return state; 59 | } 60 | 61 | public void setState(State state) { 62 | this.state = state; 63 | } 64 | 65 | public Date getAdded() { 66 | return added; 67 | } 68 | 69 | public void setAdded(Date added) { 70 | this.added = added; 71 | } 72 | 73 | 74 | public StockTransaction() {} 75 | 76 | public StockTransaction(int numOfShares, int sharesInLot, BigDecimal priceOfShares, String symbol) { 77 | this.numOfShares = numOfShares; 78 | this.sharesInLot = sharesInLot; 79 | this.priceOfShares = priceOfShares; 80 | this.symbol = symbol; 81 | } 82 | 83 | public Long getId() { 84 | return id; 85 | } 86 | 87 | public void setId(Long id) { 88 | this.id = id; 89 | } 90 | 91 | public int getNumOfShares() { 92 | return numOfShares; 93 | } 94 | 95 | public void setNumOfShares(int numOfShares) { 96 | this.numOfShares = numOfShares; 97 | } 98 | 99 | public int getSharesInLot() { 100 | return sharesInLot; 101 | } 102 | 103 | public void setSharesInLot(int sharesInLot) { 104 | this.sharesInLot = sharesInLot; 105 | } 106 | 107 | public BigDecimal getPriceOfShares() { 108 | return priceOfShares; 109 | } 110 | 111 | public void setPriceOfShares(BigDecimal priceOfShares) { 112 | this.priceOfShares = priceOfShares; 113 | } 114 | 115 | public String getSymbol() { 116 | return symbol; 117 | } 118 | 119 | public void setSymbol(String symbol) { 120 | this.symbol = symbol; 121 | } 122 | 123 | 124 | } 125 | -------------------------------------------------------------------------------- /src/main/java/com/example/demo/StockController.java: -------------------------------------------------------------------------------- 1 | package com.example.demo; 2 | 3 | import java.math.BigDecimal; 4 | import java.util.ArrayList; 5 | import java.util.List; 6 | 7 | import javax.validation.Valid; 8 | 9 | import org.springframework.http.ResponseEntity; 10 | import org.springframework.validation.Errors; 11 | import org.springframework.web.bind.annotation.GetMapping; 12 | import org.springframework.web.bind.annotation.PostMapping; 13 | import org.springframework.web.bind.annotation.RequestBody; 14 | import org.springframework.web.bind.annotation.RestController; 15 | 16 | import com.example.demo.StockTransaction.State; 17 | 18 | @RestController 19 | public class StockController { 20 | 21 | private final StockTransactionRepository stockTransactionRepository; 22 | private final RealizedStockRepository realizedStockRepository; 23 | private final SymbolRepository symbolRepository; 24 | private final StockService stockService; 25 | 26 | 27 | public StockController(StockTransactionRepository stockTransactionRepository, 28 | RealizedStockRepository realizedStockRepository, SymbolRepository symbolRepository, 29 | StockService stockService) { 30 | this.stockTransactionRepository = stockTransactionRepository; 31 | this.realizedStockRepository = realizedStockRepository; 32 | this.symbolRepository = symbolRepository; 33 | this.stockService = stockService; 34 | } 35 | 36 | @GetMapping("/current") 37 | public List currentStockInfo() { 38 | List stockList = stockTransactionRepository.findAllByOrderBySymbolAsc(); 39 | List stockTotalObjectList = stockService.getStockInfo(stockList); 40 | 41 | return stockTotalObjectList; 42 | } 43 | 44 | @GetMapping("/profits") 45 | public String profits() { 46 | 47 | List stockList = stockTransactionRepository.findAllByOrderBySymbolAsc(); 48 | List stockTotalObjectList = stockService.getStockInfo(stockList); 49 | 50 | String profit = stockService.getTotalProfit(stockTotalObjectList); 51 | 52 | return profit; 53 | } 54 | 55 | @GetMapping("/realized") 56 | public List realizedProfits() { 57 | List realizedList = realizedStockRepository.findAll(); 58 | return realizedList; 59 | 60 | } 61 | 62 | @GetMapping("/transactions") 63 | public List transactions() { 64 | List transactions = stockTransactionRepository.findAllByOrderBySymbolAsc(); 65 | return transactions; 66 | 67 | } 68 | 69 | @PostMapping("/post") 70 | public ResponseEntity post(@Valid @RequestBody StockTransaction st, Errors errors){ 71 | 72 | 73 | if (errors.hasErrors() || stockService.getCurrentPrice(st.getSymbol()).compareTo(BigDecimal.ZERO) == 0 74 | || st.getNumOfShares() == 0) { 75 | return ResponseEntity.badRequest().body("Bad Request"); 76 | } 77 | 78 | st.setSymbol(st.getSymbol().toUpperCase()); 79 | 80 | if (st.getState().name().equals("PURCHASE")) 81 | st.setSharesInLot(st.getNumOfShares()); 82 | 83 | if (st.getState().name().equals("SALE")){ 84 | stockService.calcRealizedProfit(st); 85 | } 86 | 87 | 88 | return ResponseEntity.ok(stockTransactionRepository.save(st)); 89 | 90 | 91 | } 92 | 93 | @GetMapping("/sample") 94 | public ResponseEntity sample(){ 95 | 96 | String[] array = {"AAPL", "TWTR", "NFLX", "MSFT", "GOOG", "TSLA", "AMD", "NVDA", "FB", "UBER", "GE", "AMZN"}; 97 | 98 | String random = array[(int) (Math.random() * array.length)]; 99 | 100 | StockTransaction st = new StockTransaction(); 101 | st.setSymbol(random); 102 | st.setNumOfShares(100); 103 | st.setSharesInLot(st.getNumOfShares()); 104 | st.setState(State.PURCHASE); 105 | st.setPriceOfShares(stockService.getCurrentPrice(random)); 106 | 107 | 108 | return ResponseEntity.ok(stockTransactionRepository.save(st)); 109 | 110 | } 111 | 112 | @GetMapping("/samplesale") 113 | public ResponseEntity sampleSale(){ 114 | 115 | String[] array = {"AAPL", "MSFT", "FB", "GOOG"}; 116 | 117 | String random = array[(int) (Math.random() * array.length)]; 118 | 119 | StockTransaction st = new StockTransaction(); 120 | st.setSymbol(random); 121 | st.setNumOfShares(100); 122 | st.setPriceOfShares(stockService.getCurrentPrice(random)); 123 | st.setState(State.SALE); 124 | 125 | 126 | stockService.calcRealizedProfit(st); 127 | 128 | return ResponseEntity.ok(stockTransactionRepository.save(st)); 129 | 130 | 131 | 132 | } 133 | 134 | } 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | -------------------------------------------------------------------------------- /src/main/resources/templates/ajax.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | StockTracker 6 | 7 | 8 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 20 | 21 | 22 | 34 | 35 | 88 | 89 | 90 | 97 | 98 | 99 | 100 | 101 |
102 | 103 |
104 | 105 |

This is a way to track your portfolio in real time. If you 106 | purchased 10 shares of Apple (SYMBOL:AAPL) at 150.00, you would 107 | enter the symbol (AAPL), the amount of shares (10), and the price 108 | you purchased at (150). Make sure the purchase tab is selected. If 109 | you later on sold these shares at 200.00, you would enter the symbol 110 | (AAPL), the amount of shares sold (10), and the price sold at (200). 111 | Make sure the sale tab is selected.

112 | 113 |

Select Sample Purchase or Sample Sale to see an example 114 | transaction.

115 | 116 | 117 |
118 | 119 | 121 |
122 | 123 |

124 | 125 |
126 | 128 |
129 | 130 |

131 | 132 | 133 |
135 | 136 | 139 | 146 | 148 |
149 | 150 |
151 | 152 | 153 |

Portfolio

154 | 155 |
156 | 157 |
158 |

Current Profit:

159 |
160 |
161 | 162 | 163 |

Realized Profits

164 | 165 | 166 |
167 | 168 |

Transactions

169 | 170 |
171 | 172 | 173 | 174 | 175 | 176 |
177 | 178 | 179 | 180 | -------------------------------------------------------------------------------- /mvnw.cmd: -------------------------------------------------------------------------------- 1 | @REM ---------------------------------------------------------------------------- 2 | @REM Licensed to the Apache Software Foundation (ASF) under one 3 | @REM or more contributor license agreements. See the NOTICE file 4 | @REM distributed with this work for additional information 5 | @REM regarding copyright ownership. The ASF licenses this file 6 | @REM to you under the Apache License, Version 2.0 (the 7 | @REM "License"); you may not use this file except in compliance 8 | @REM with the License. You may obtain a copy of the License at 9 | @REM 10 | @REM http://www.apache.org/licenses/LICENSE-2.0 11 | @REM 12 | @REM Unless required by applicable law or agreed to in writing, 13 | @REM software distributed under the License is distributed on an 14 | @REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | @REM KIND, either express or implied. See the License for the 16 | @REM specific language governing permissions and limitations 17 | @REM under the License. 18 | @REM ---------------------------------------------------------------------------- 19 | 20 | @REM ---------------------------------------------------------------------------- 21 | @REM Maven2 Start Up Batch script 22 | @REM 23 | @REM Required ENV vars: 24 | @REM JAVA_HOME - location of a JDK home dir 25 | @REM 26 | @REM Optional ENV vars 27 | @REM M2_HOME - location of maven2's installed home dir 28 | @REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands 29 | @REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a key stroke before ending 30 | @REM MAVEN_OPTS - parameters passed to the Java VM when running Maven 31 | @REM e.g. to debug Maven itself, use 32 | @REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 33 | @REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files 34 | @REM ---------------------------------------------------------------------------- 35 | 36 | @REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on' 37 | @echo off 38 | @REM enable echoing my setting MAVEN_BATCH_ECHO to 'on' 39 | @if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO% 40 | 41 | @REM set %HOME% to equivalent of $HOME 42 | if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%") 43 | 44 | @REM Execute a user defined script before this one 45 | if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre 46 | @REM check for pre script, once with legacy .bat ending and once with .cmd ending 47 | if exist "%HOME%\mavenrc_pre.bat" call "%HOME%\mavenrc_pre.bat" 48 | if exist "%HOME%\mavenrc_pre.cmd" call "%HOME%\mavenrc_pre.cmd" 49 | :skipRcPre 50 | 51 | @setlocal 52 | 53 | set ERROR_CODE=0 54 | 55 | @REM To isolate internal variables from possible post scripts, we use another setlocal 56 | @setlocal 57 | 58 | @REM ==== START VALIDATION ==== 59 | if not "%JAVA_HOME%" == "" goto OkJHome 60 | 61 | echo. 62 | echo Error: JAVA_HOME not found in your environment. >&2 63 | echo Please set the JAVA_HOME variable in your environment to match the >&2 64 | echo location of your Java installation. >&2 65 | echo. 66 | goto error 67 | 68 | :OkJHome 69 | if exist "%JAVA_HOME%\bin\java.exe" goto init 70 | 71 | echo. 72 | echo Error: JAVA_HOME is set to an invalid directory. >&2 73 | echo JAVA_HOME = "%JAVA_HOME%" >&2 74 | echo Please set the JAVA_HOME variable in your environment to match the >&2 75 | echo location of your Java installation. >&2 76 | echo. 77 | goto error 78 | 79 | @REM ==== END VALIDATION ==== 80 | 81 | :init 82 | 83 | set MAVEN_CMD_LINE_ARGS=%* 84 | 85 | @REM Find the project base dir, i.e. the directory that contains the folder ".mvn". 86 | @REM Fallback to current working directory if not found. 87 | 88 | set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR% 89 | IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir 90 | 91 | set EXEC_DIR=%CD% 92 | set WDIR=%EXEC_DIR% 93 | :findBaseDir 94 | IF EXIST "%WDIR%"\.mvn goto baseDirFound 95 | cd .. 96 | IF "%WDIR%"=="%CD%" goto baseDirNotFound 97 | set WDIR=%CD% 98 | goto findBaseDir 99 | 100 | :baseDirFound 101 | set MAVEN_PROJECTBASEDIR=%WDIR% 102 | cd "%EXEC_DIR%" 103 | goto endDetectBaseDir 104 | 105 | :baseDirNotFound 106 | set MAVEN_PROJECTBASEDIR=%EXEC_DIR% 107 | cd "%EXEC_DIR%" 108 | 109 | :endDetectBaseDir 110 | 111 | IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig 112 | 113 | @setlocal EnableExtensions EnableDelayedExpansion 114 | for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a 115 | @endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS% 116 | 117 | :endReadAdditionalConfig 118 | 119 | SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe" 120 | 121 | set WRAPPER_JAR="".\.mvn\wrapper\maven-wrapper.jar"" 122 | set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain 123 | 124 | %MAVEN_JAVA_EXE% %JVM_CONFIG_MAVEN_PROPS% %MAVEN_OPTS% %MAVEN_DEBUG_OPTS% -classpath %WRAPPER_JAR% "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" %WRAPPER_LAUNCHER% %MAVEN_CMD_LINE_ARGS% 125 | if ERRORLEVEL 1 goto error 126 | goto end 127 | 128 | :error 129 | set ERROR_CODE=1 130 | 131 | :end 132 | @endlocal & set ERROR_CODE=%ERROR_CODE% 133 | 134 | if not "%MAVEN_SKIP_RC%" == "" goto skipRcPost 135 | @REM check for post script, once with legacy .bat ending and once with .cmd ending 136 | if exist "%HOME%\mavenrc_post.bat" call "%HOME%\mavenrc_post.bat" 137 | if exist "%HOME%\mavenrc_post.cmd" call "%HOME%\mavenrc_post.cmd" 138 | :skipRcPost 139 | 140 | @REM pause the script if MAVEN_BATCH_PAUSE is set to 'on' 141 | if "%MAVEN_BATCH_PAUSE%" == "on" pause 142 | 143 | if "%MAVEN_TERMINATE_CMD%" == "on" exit %ERROR_CODE% 144 | 145 | exit /B %ERROR_CODE% -------------------------------------------------------------------------------- /src/main/java/com/example/demo/DemoApplication.java: -------------------------------------------------------------------------------- 1 | package com.example.demo; 2 | 3 | import java.math.BigDecimal; 4 | import java.util.Arrays; 5 | 6 | import org.springframework.boot.CommandLineRunner; 7 | import org.springframework.boot.SpringApplication; 8 | import org.springframework.boot.autoconfigure.SpringBootApplication; 9 | import org.springframework.context.annotation.Bean; 10 | import org.springframework.core.annotation.Order; 11 | 12 | @SpringBootApplication 13 | public class DemoApplication { 14 | 15 | 16 | public static void main(String[] args) { 17 | SpringApplication.run(DemoApplication.class, args); 18 | } 19 | 20 | @Bean 21 | @Order(1) 22 | CommandLineRunner command1(SymbolRepository symbolRepository) { 23 | return args -> { 24 | 25 | Arrays.asList("AAPL", "ABT", "ABBV", "ACN", "ACE", "ADBE", "ADT", "AAP", "AES", "AET", "AFL", 26 | "AMG", "A", "GAS", "ARE", "APD", "AKAM", "AA", "AGN", "ALXN", "ALLE", "ADS", "ALL", 27 | "ALTR", "MO", "AMZN", "AEE", "AAL", "AEP", "AXP", "AIG", "AMT", "AMP", "ABC", "AME", 28 | "AMGN", "APH", "APC", "ADI", "AON", "APA", "AIV", "AMAT", "ADM", "AIZ", "T", "ADSK", 29 | "ADP", "AN", "AZO", "AVGO", "AVB", "AVY", "BHI", "BLL", "BAC", "BK", "BCR", "BXLT", 30 | "BAX", "BBT", "BDX", "BBBY", "BRK.B", "BBY", "BLX", "HRB", "BA", "BWA", "BXP", "BSX", 31 | "BMY", "BRCM", "BF.B", "CHRW", "CA", "CVC", "COG", "CAM", 32 | "CPB", "COF", "CAH", "HSIC", "KMX", "CCL", "CAT", "CBG", "CBS", "CELG", "CNP", "CTL", 33 | "CERN", "CF", "SCHW", "CHK", "CVX", "CMG", "CB", "CI", "XEC", "CINF", "CTAS", "CSCO", 34 | "C", "CTXS", "CLX", "CME", "CMS", "COH", "KO", "CCE", "CTSH", "CL", "CMCSA", "CMA", "CSC", 35 | "CAG", "COP", "CNX", "ED", "STZ", "GLW", "COST", "CCI", "CSX", "CMI", "CVS", "DHI", "DHR", 36 | "DRI", "DVA", "DE", "DLPH", "DAL", "XRAY", "DVN", "DO", "DTV", "DFS", "DISCA", "DISCK", "DG", 37 | "DLTR", "D", "DOV", "DOW", "DPS", "DTE", "DD", "DUK", "DNB", "ETFC", "EMN", "ETN", "EBAY", "ECL", 38 | "EIX", "EW", "EA", "EMC", "EMR", "ENDP", "ESV", "ETR", "EOG", "EQT", "EFX", "EQIX", "EQR", "ESS", 39 | "EL", "ES", "EXC", "EXPE", "EXPD", "ESRX", "XOM", "FFIV", "FB", "FAST", "FDX", "FIS", "FITB", "FSLR", 40 | "FE", "FISV", "FLIR", "FLS", "FLR", "FMC", "FTI", "F", "FOSL", "BEN", "FCX", "FTR", "GME", "GPS", 41 | "GRMN", "GD", "GE", "GGP", "GIS", "GM", "GPC", "GNW", "GILD", "GS", "GT", "GOOGL", "GOOG", "GWW", 42 | "HAL", "HBI", "HOG", "HAR", "HRS", "HIG", "HAS", "HCA", "HCP", "HCN", "HP", "HES", "HPQ", "HD", "HON", "HRL", 43 | "HSP", "HST", "HCBK", "HUM", "HBAN", "ITW", "IR", "INTC", "ICE", "IBM", "IP", "IPG", "IFF", "INTU", 44 | "ISRG", "IVZ", "IRM", "JEC", "JBHT", "JNJ", "JCI", "JOY", "JPM", "JNPR", "KSU", "K", "KEY", "GMCR", 45 | "KMB", "KIM", "KMI", "KLAC", "KSS", "KRFT", "KR", "LB", "LLL", "LH", "LRCX", "LM", "LEG", "LEN", "LVLT", 46 | "LUK", "LLY", "LNC", "LLTC", "LMT", "L", "LOW", "LYB", "MTB", "MAC", "M", "MNK", "MRO", "MPC", "MAR", "MMC", 47 | "MLM", "MAS", "MA", "MAT", "MKC", "MCD", "MCK", "MJN", "MMV", "MDT", "MRK", "MET", "KORS", "MCHP", "MU", "MSFT", 48 | "MHK", "TAP", "MDLZ", "MON", "MNST", "MCO", "MS", "MOS", "MSI", "MUR", "MYL", "NDAQ", "NOV", "NAVI", "NTAP", "NFLX", 49 | "NWL", "NFX", "NEM", "NWSA", "NEE", "NLSN", "NKE", "NI", "NE", "NBL", "JWN", "NSC", "NTRS", "NOC", "NRG", "NUE", 50 | "NVDA", "ORLY", "OXY", "OMC", "OKE", "ORCL", "OI", "PCAR", "PLL", "PH", "PDCO", "PAYX", "PNR", "PBCT", "POM", "PEP", 51 | "PKI", "PRGO", "PFE", "PCG", "PM", "PSX", "PNW", "PXD", "PBI", "PCL", "PNC", "RL", "PPG", "PPL", "PX", "PCP", "PCLN", "PFG", 52 | "PG", "PGR", "PLD", "PRU", "PEG", "PSA", "PHM", "PVH", "QRVO", "PWR", "QCOM", "DGX", "RRC", "RTN", "O", "RHT", 53 | "REGN", "RF", "RSG", "RAI", "RHI", "ROK", "COL", "ROP", "ROST", "RLD", "R", 54 | "CRM", "SNDK", "SCG", "SLB", "SNI", "STX", "SEE", "SRE", "SHW", "SPG", 55 | "SWKS", "SLG", "SJM", "SNA", "SO", "LUV", "SWN", "SE", "STJ", "SWK", 56 | "SPLS", "SBUX", "HOT", "STT", "SRCL", "SYK", "STI", "SYMC", "SYY", 57 | "TROW", "TGT", "TEL", "TE", "TGNA", "THC", "TDC", "TSO", "TXN", "TXT", "HSY", 58 | "TRV", "TMO", "TIF", "TWX", "TWC", "TJX", "TMK", "TSS", "TSCO", "RIG", "TRIP", "FOXA", 59 | "TSN", "TYC", "UA", "UNP", "UNH", "UPS", "URI", "UTX", "UHS", "UNM", 60 | "URBN", "VFC", "VLO", "VAR", "VTR", "VRSN", "VZ", "VRTX", "VIAB", "V", 61 | "VNO", "VMC", "WMT", "WBA", "DIS", "WM", "WAT", "ANTM", "WFC", "WDC", "WU", 62 | "WY", "WHR", "WFM", "WMB", "WEC", "WYN", "WYNN", "XEL", "XRX", "XLNX", "XL", 63 | "XYL", "YHOO", "YUM", "ZBH", "ZION", "ZTS", "TWTR", "FB").forEach(symbol -> symbolRepository.save(new Symbol(symbol))); 64 | 65 | 66 | 67 | 68 | 69 | 70 | }; 71 | } 72 | 73 | @Bean 74 | CommandLineRunner command(StockTransactionRepository stockRepo, StockService stockService) { 75 | return args -> { 76 | 77 | 78 | 79 | StockTransaction transaction1 = new StockTransaction(100, 100, new BigDecimal(100.25), "GOOG"); 80 | StockTransaction transaction2 = new StockTransaction(100, 100, new BigDecimal(100.25), "AAPL"); 81 | StockTransaction transaction5 = new StockTransaction(100, 100, new BigDecimal(100.33), "AAPL"); 82 | StockTransaction transaction7 = new StockTransaction(100, 100, new BigDecimal(10.25), "AAPL"); 83 | 84 | 85 | 86 | StockTransaction transaction3 = new StockTransaction(100, 100, new BigDecimal(100.777), "FB"); 87 | 88 | StockTransaction transaction4 = new StockTransaction(100, 100, new BigDecimal(100.22), "MSFT"); 89 | StockTransaction transaction6 = new StockTransaction(50, 50, new BigDecimal(120.22), "MSFT"); 90 | 91 | 92 | stockRepo.save(transaction1); 93 | stockRepo.save(transaction2); 94 | stockRepo.save(transaction3); 95 | stockRepo.save(transaction4); 96 | stockRepo.save(transaction5); 97 | stockRepo.save(transaction6); 98 | stockRepo.save(transaction7); 99 | 100 | 101 | 102 | }; 103 | } 104 | 105 | } 106 | 107 | 108 | 109 | -------------------------------------------------------------------------------- /src/main/java/com/example/demo/StockService.java: -------------------------------------------------------------------------------- 1 | package com.example.demo; 2 | 3 | import java.io.IOException; 4 | import java.math.BigDecimal; 5 | import java.math.RoundingMode; 6 | import java.net.MalformedURLException; 7 | import java.net.URL; 8 | import java.text.NumberFormat; 9 | import java.util.ArrayList; 10 | import java.util.HashSet; 11 | import java.util.List; 12 | import java.util.Set; 13 | import java.util.function.Function; 14 | 15 | import org.apache.commons.io.IOUtils; 16 | import org.springframework.beans.factory.annotation.Autowired; 17 | import org.springframework.stereotype.Service; 18 | 19 | import com.google.gson.JsonElement; 20 | import com.google.gson.JsonObject; 21 | import com.google.gson.JsonParser; 22 | 23 | @Service 24 | public class StockService { 25 | 26 | @Autowired 27 | private StockTransactionRepository stockTransactionRepository; 28 | 29 | @Autowired 30 | private RealizedStockRepository realizedStockRepository; 31 | 32 | 33 | public List getStockInfo(List stockList) { 34 | 35 | BigDecimal totalPrice; 36 | BigDecimal averagePrice; 37 | BigDecimal profit; 38 | BigDecimal currentPrice; 39 | int totalShares; 40 | String symbol; 41 | List stockTotal = new ArrayList(); 42 | Set stockSet = new HashSet(); 43 | 44 | 45 | for(int i = 0; i < stockList.size(); i++){ 46 | 47 | if (!stockSet.contains(stockList.get(i).getSymbol())){ 48 | stockSet.add(stockList.get(i).getSymbol()); 49 | totalShares = getTotalShares(stockList.get(i)); 50 | 51 | if (totalShares > 0){ 52 | totalPrice = getTotalPrice(stockList.get(i)); 53 | averagePrice = getAveragePrice(totalPrice, totalShares); 54 | profit = getUnrealizedProfit(stockList.get(i), totalPrice, totalShares).setScale(3, RoundingMode.HALF_UP); 55 | symbol = stockList.get(i).getSymbol(); 56 | currentPrice = getCurrentPrice(symbol); 57 | 58 | stockTotal.add(new StockTotalObject(totalPrice, averagePrice, profit, 59 | currentPrice, totalShares, symbol)); 60 | 61 | } 62 | } 63 | } 64 | 65 | stockSet.clear(); 66 | return stockTotal; 67 | 68 | } 69 | 70 | public int getTotalShares(StockTransaction stock){ 71 | 72 | int totalShares; 73 | 74 | List stockList = stockTransactionRepository.findBySymbol(stock.getSymbol()); 75 | 76 | totalShares = stockList 77 | .stream() 78 | .filter(e -> e.getSharesInLot() > 0) 79 | .mapToInt(e -> e.getSharesInLot()) 80 | .sum(); 81 | 82 | return totalShares; 83 | 84 | } 85 | 86 | public BigDecimal getTotalPrice(StockTransaction stock){ 87 | 88 | Function stockMapper = s-> s.getPriceOfShares() 89 | .multiply(new BigDecimal(s.getSharesInLot())); 90 | 91 | BigDecimal totalPrice; 92 | 93 | List stockList = stockTransactionRepository.findBySymbol(stock.getSymbol()); 94 | 95 | totalPrice = stockList 96 | .stream() 97 | .filter(e -> e.getSharesInLot() > 0) 98 | .map(stockMapper) 99 | .reduce(BigDecimal.ZERO, BigDecimal::add); 100 | 101 | 102 | 103 | return totalPrice.setScale(3, RoundingMode.HALF_UP); 104 | 105 | } 106 | 107 | public BigDecimal getAveragePrice(BigDecimal totalPrice, int totalShares){ 108 | return totalPrice.divide(new BigDecimal(totalShares), 3, RoundingMode.HALF_UP); 109 | } 110 | 111 | public BigDecimal getMarketValue(StockTransaction stock, int totalShares){ 112 | return getCurrentPrice(stock.getSymbol()).multiply(new BigDecimal(totalShares)); 113 | } 114 | 115 | public BigDecimal getUnrealizedProfit(StockTransaction stock, BigDecimal totalPrice, int totalShares){ 116 | return getMarketValue(stock, totalShares).subtract(totalPrice); 117 | } 118 | 119 | public String getTotalProfit(List stockTotalList){ 120 | 121 | BigDecimal profit = stockTotalList 122 | .stream() 123 | .map(e -> e.getProfit()) 124 | .reduce(BigDecimal.ZERO, BigDecimal::add); 125 | 126 | 127 | NumberFormat formatter = NumberFormat.getCurrencyInstance(); 128 | 129 | return formatter.format(profit); 130 | } 131 | 132 | public BigDecimal getCurrentPrice(String symbol){ 133 | 134 | JsonParser parser; 135 | JsonElement element; 136 | JsonObject dataset; 137 | String url; 138 | String json = ""; 139 | 140 | BigDecimal price; 141 | 142 | url = "https://financialmodelingprep.com/api/v3/stock/real-time-price/"+ symbol; 143 | 144 | try { 145 | json = IOUtils.toString(new URL(url), "UTF-8"); 146 | } catch (MalformedURLException e) { 147 | e.printStackTrace(); 148 | } catch (IOException e) { 149 | //e.printStackTrace(); 150 | return price = BigDecimal.ZERO; 151 | } 152 | parser = new JsonParser(); 153 | element = parser.parse(json); 154 | dataset = element.getAsJsonObject(); 155 | price = BigDecimal.valueOf(dataset.get("price").getAsDouble()); 156 | 157 | return price.setScale(3, RoundingMode.HALF_UP); 158 | } 159 | 160 | public List calcRealizedProfit(StockTransaction stock) { 161 | 162 | 163 | List stockPrice = stockTransactionRepository.findBySymbol(stock.getSymbol()); 164 | int shares = stock.getNumOfShares();//* -1; 165 | BigDecimal proceeds = BigDecimal.ZERO; 166 | BigDecimal cost = BigDecimal.ZERO; 167 | BigDecimal realized = BigDecimal.ZERO; 168 | 169 | for (int i =0; i < stockPrice.size(); i++) { 170 | 171 | StockTransaction currentStock = stockPrice.get(i); 172 | 173 | if(shares <= currentStock.getSharesInLot() && currentStock.getSharesInLot() > 0){ 174 | 175 | currentStock.setSharesInLot(currentStock.getSharesInLot() - shares); 176 | stockTransactionRepository.save(currentStock); 177 | 178 | proceeds = stock.getPriceOfShares().multiply(new BigDecimal(shares)); 179 | cost = currentStock.getPriceOfShares().multiply(new BigDecimal(shares)); 180 | realized = proceeds.subtract(cost); 181 | realizedStockRepository.save(new StockRealized(shares, stock.getPriceOfShares(), 182 | proceeds, cost, stock.getSymbol(), realized)); 183 | 184 | break; 185 | 186 | } else if (shares > currentStock.getSharesInLot() && currentStock.getSharesInLot() > 0){ 187 | 188 | 189 | cost = currentStock.getPriceOfShares().multiply(new BigDecimal(currentStock.getSharesInLot())); 190 | proceeds = stock.getPriceOfShares().multiply(new BigDecimal(currentStock.getSharesInLot())); 191 | realized = proceeds.subtract(cost); 192 | realizedStockRepository.save(new StockRealized(currentStock.getSharesInLot(), stock.getPriceOfShares(), 193 | proceeds, cost, stock.getSymbol(), realized)); 194 | 195 | shares = shares - currentStock.getSharesInLot(); 196 | currentStock.setSharesInLot(0); 197 | stockTransactionRepository.save(currentStock); 198 | } 199 | } 200 | return realizedStockRepository.findAll(); 201 | } 202 | 203 | 204 | 205 | 206 | } 207 | -------------------------------------------------------------------------------- /src/main/resources/static/js/main.js: -------------------------------------------------------------------------------- 1 | $(document).ready(function() { 2 | 3 | fire_ajax_submit(); 4 | realizedProfit(); 5 | transactions(); 6 | profits(); 7 | 8 | 9 | $("#search-form2").submit(function(event) { 10 | 11 | event.preventDefault(); 12 | stock(); 13 | 14 | }); 15 | 16 | $("#sample-purchase").submit(function(event) { 17 | 18 | event.preventDefault(); 19 | samplePurchase(); 20 | 21 | }); 22 | 23 | $("#sample-sale").submit(function(event) { 24 | 25 | event.preventDefault(); 26 | sampleSale(); 27 | 28 | }); 29 | 30 | }); 31 | 32 | 33 | function realizedProfit() { 34 | 35 | 36 | $.ajax({ 37 | type : "GET", 38 | contentType : "application/json", 39 | url : "/realized", 40 | // data: JSON.stringify(search), 41 | dataType : 'json', 42 | cache : false, 43 | timeout : 600000, 44 | success : function(data) { 45 | 46 | var txt = ""; 47 | 48 | txt += " " + 49 | " " + 50 | "" + 51 | "" + 52 | "" + 53 | "" 54 | for (x in data) { 55 | txt += ""; 62 | } 63 | txt += "
SymbolNumber of SharesPriceProceedsCostRealized Profit
" + data[x].symbol + 56 | "" + data[x].numShares + 57 | "" + data[x].price + 58 | "" + data[x].proceeds+ 59 | "" + data[x].cost + 60 | "" + data[x].realizedProfit + 61 | "
" 64 | document.getElementById("messages").innerHTML = txt; 65 | 66 | 67 | $('#messages').html(txt); 68 | 69 | console.log("SUCCESS : ", data); 70 | $("#btn-search").prop("disabled", false) 71 | //setTimeout(realizedProfit, 10000); 72 | }, 73 | error : function(e) { 74 | 75 | 76 | } 77 | }); 78 | 79 | } 80 | 81 | function samplePurchase() { 82 | 83 | 84 | $.ajax({ 85 | type : "GET", 86 | contentType : "application/json", 87 | url : "/sample", 88 | // data: JSON.stringify(search), 89 | dataType : 'text', 90 | cache : false, 91 | timeout : 600000, 92 | success : function(data) { 93 | 94 | 95 | console.log("SUCCESS : ", data); 96 | profits(); 97 | transactions(); 98 | realizedProfit(); 99 | 100 | }, 101 | error : function(e) { 102 | 103 | } 104 | }); 105 | 106 | } 107 | 108 | function sampleSale() { 109 | 110 | 111 | $.ajax({ 112 | type : "GET", 113 | contentType : "application/json", 114 | url : "/samplesale", 115 | // data: JSON.stringify(search), 116 | dataType : 'text', 117 | cache : false, 118 | timeout : 600000, 119 | success : function(data) { 120 | 121 | 122 | console.log("SUCCESS : ", data); 123 | profits(); 124 | transactions(); 125 | realizedProfit(); 126 | 127 | }, 128 | error : function(e) { 129 | 130 | } 131 | }); 132 | 133 | } 134 | 135 | function profits() { 136 | 137 | 138 | $.ajax({ 139 | type : "GET", 140 | contentType : "application/json", 141 | url : "/profits", 142 | // data: JSON.stringify(search), 143 | dataType : 'text', 144 | cache : false, 145 | timeout : 600000, 146 | success : function(data) { 147 | 148 | 149 | $('#feed').html(data); 150 | 151 | console.log("SUCCESS : ", data); 152 | 153 | }, 154 | error : function(e) { 155 | 156 | } 157 | }); 158 | 159 | } 160 | function transactions() { 161 | 162 | 163 | $.ajax({ 164 | type : "GET", 165 | contentType : "application/json", 166 | url : "/transactions", 167 | // data: JSON.stringify(search), 168 | dataType : 'json', 169 | cache : false, 170 | timeout : 600000, 171 | success : function(data) { 172 | 173 | var txt = ""; 174 | 175 | txt += " " + 176 | " " + 177 | "" + 178 | "" 179 | 180 | 181 | for (x in data) { 182 | 183 | var nowDate = new Date(parseInt(data[x].added)); 184 | 185 | txt += ""; 192 | } 193 | txt += "
SymbolNumber of SharesPrice Of SharesDate
" + data[x].symbol + 186 | "" + data[x].numOfShares + 187 | "" + data[x].priceOfShares + 188 | "" + nowDate + 189 | 190 | 191 | "
" 194 | document.getElementById("stocks").innerHTML = txt; 195 | 196 | $('#stocks').html(txt); 197 | 198 | console.log("SUCCESS : ", data); 199 | $("#btn-search").prop("disabled", false) 200 | //setTimeout(transactions, 10000); 201 | }, 202 | error : function(e) { 203 | 204 | console.log("ERROR : ", e); 205 | 206 | } 207 | }); 208 | 209 | } 210 | 211 | function fire_ajax_submit() { 212 | 213 | $.ajax({ 214 | type : "GET", 215 | contentType : "application/json", 216 | url : "/current", 217 | // data: JSON.stringify(search), 218 | dataType : 'json', 219 | cache : false, 220 | timeout : 600000, 221 | success : function(data) { 222 | 223 | 224 | var txt = ""; 225 | 226 | txt += " " + 227 | " " + 228 | "" + 229 | "" + 230 | "" + 231 | "" 232 | for (x in data) { 233 | txt += ""; 240 | } 241 | txt += "
SymbolCurrent PriceTotal SharesTotal PriceAverage PriceProfit
" + data[x].symbol + 234 | "" + data[x].currentPrice + 235 | "" + data[x].totalShares + 236 | "" + data[x].totalPrice+ 237 | "" + data[x].averagePrice + 238 | "" + data[x].profit + 239 | "
" 242 | document.getElementById("feedback").innerHTML = txt; 243 | 244 | $('#feedback').html(txt); 245 | profits(); 246 | 247 | console.log("SUCCESS : ", data); 248 | setTimeout(fire_ajax_submit, 10000); 249 | }, 250 | error : function(e) { 251 | 252 | var json = "

Ajax Response

" + e.responseText
253 | 			+ "
"; 254 | $('#feedback').html(json); 255 | 256 | console.log("ERROR : ", e); 257 | $("#btn-search").prop("disabled", false); 258 | 259 | } 260 | }); 261 | 262 | } 263 | 264 | function stock() { 265 | 266 | var data = new Object(); 267 | data.symbol = $('#symbol').val() 268 | data.numOfShares = $('#shares').val(); 269 | data.priceOfShares = $('#price').val(); 270 | data.state = $('#state').val(); 271 | 272 | 273 | $.ajax({ 274 | type: "POST", 275 | contentType: "application/json", 276 | url: "/post", 277 | data: JSON.stringify(data), 278 | dataType: 'json', 279 | cache: false, 280 | timeout: 600000, 281 | success: function (data) { 282 | 283 | $('input[type="text"],textarea').val(''); 284 | $('input[type="number"],textarea').val(''); 285 | 286 | profits(); 287 | transactions(); 288 | realizedProfit(); 289 | 290 | 291 | 292 | }, 293 | error: function (e) { 294 | 295 | console.log("ERROR : ", e); 296 | 297 | 298 | } 299 | }); 300 | 301 | } -------------------------------------------------------------------------------- /mvnw: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # ---------------------------------------------------------------------------- 3 | # Licensed to the Apache Software Foundation (ASF) under one 4 | # or more contributor license agreements. See the NOTICE file 5 | # distributed with this work for additional information 6 | # regarding copyright ownership. The ASF licenses this file 7 | # to you under the Apache License, Version 2.0 (the 8 | # "License"); you may not use this file except in compliance 9 | # with the License. You may obtain a copy of the License at 10 | # 11 | # http://www.apache.org/licenses/LICENSE-2.0 12 | # 13 | # Unless required by applicable law or agreed to in writing, 14 | # software distributed under the License is distributed on an 15 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16 | # KIND, either express or implied. See the License for the 17 | # specific language governing permissions and limitations 18 | # under the License. 19 | # ---------------------------------------------------------------------------- 20 | 21 | # ---------------------------------------------------------------------------- 22 | # Maven2 Start Up Batch script 23 | # 24 | # Required ENV vars: 25 | # ------------------ 26 | # JAVA_HOME - location of a JDK home dir 27 | # 28 | # Optional ENV vars 29 | # ----------------- 30 | # M2_HOME - location of maven2's installed home dir 31 | # MAVEN_OPTS - parameters passed to the Java VM when running Maven 32 | # e.g. to debug Maven itself, use 33 | # set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 34 | # MAVEN_SKIP_RC - flag to disable loading of mavenrc files 35 | # ---------------------------------------------------------------------------- 36 | 37 | if [ -z "$MAVEN_SKIP_RC" ] ; then 38 | 39 | if [ -f /etc/mavenrc ] ; then 40 | . /etc/mavenrc 41 | fi 42 | 43 | if [ -f "$HOME/.mavenrc" ] ; then 44 | . "$HOME/.mavenrc" 45 | fi 46 | 47 | fi 48 | 49 | # OS specific support. $var _must_ be set to either true or false. 50 | cygwin=false; 51 | darwin=false; 52 | mingw=false 53 | case "`uname`" in 54 | CYGWIN*) cygwin=true ;; 55 | MINGW*) mingw=true;; 56 | Darwin*) darwin=true 57 | # 58 | # Look for the Apple JDKs first to preserve the existing behaviour, and then look 59 | # for the new JDKs provided by Oracle. 60 | # 61 | if [ -z "$JAVA_HOME" ] && [ -L /System/Library/Frameworks/JavaVM.framework/Versions/CurrentJDK ] ; then 62 | # 63 | # Apple JDKs 64 | # 65 | export JAVA_HOME=/System/Library/Frameworks/JavaVM.framework/Versions/CurrentJDK/Home 66 | fi 67 | 68 | if [ -z "$JAVA_HOME" ] && [ -L /System/Library/Java/JavaVirtualMachines/CurrentJDK ] ; then 69 | # 70 | # Apple JDKs 71 | # 72 | export JAVA_HOME=/System/Library/Java/JavaVirtualMachines/CurrentJDK/Contents/Home 73 | fi 74 | 75 | if [ -z "$JAVA_HOME" ] && [ -L "/Library/Java/JavaVirtualMachines/CurrentJDK" ] ; then 76 | # 77 | # Oracle JDKs 78 | # 79 | export JAVA_HOME=/Library/Java/JavaVirtualMachines/CurrentJDK/Contents/Home 80 | fi 81 | 82 | if [ -z "$JAVA_HOME" ] && [ -x "/usr/libexec/java_home" ]; then 83 | # 84 | # Apple JDKs 85 | # 86 | export JAVA_HOME=`/usr/libexec/java_home` 87 | fi 88 | ;; 89 | esac 90 | 91 | if [ -z "$JAVA_HOME" ] ; then 92 | if [ -r /etc/gentoo-release ] ; then 93 | JAVA_HOME=`java-config --jre-home` 94 | fi 95 | fi 96 | 97 | if [ -z "$M2_HOME" ] ; then 98 | ## resolve links - $0 may be a link to maven's home 99 | PRG="$0" 100 | 101 | # need this for relative symlinks 102 | while [ -h "$PRG" ] ; do 103 | ls=`ls -ld "$PRG"` 104 | link=`expr "$ls" : '.*-> \(.*\)$'` 105 | if expr "$link" : '/.*' > /dev/null; then 106 | PRG="$link" 107 | else 108 | PRG="`dirname "$PRG"`/$link" 109 | fi 110 | done 111 | 112 | saveddir=`pwd` 113 | 114 | M2_HOME=`dirname "$PRG"`/.. 115 | 116 | # make it fully qualified 117 | M2_HOME=`cd "$M2_HOME" && pwd` 118 | 119 | cd "$saveddir" 120 | # echo Using m2 at $M2_HOME 121 | fi 122 | 123 | # For Cygwin, ensure paths are in UNIX format before anything is touched 124 | if $cygwin ; then 125 | [ -n "$M2_HOME" ] && 126 | M2_HOME=`cygpath --unix "$M2_HOME"` 127 | [ -n "$JAVA_HOME" ] && 128 | JAVA_HOME=`cygpath --unix "$JAVA_HOME"` 129 | [ -n "$CLASSPATH" ] && 130 | CLASSPATH=`cygpath --path --unix "$CLASSPATH"` 131 | fi 132 | 133 | # For Migwn, ensure paths are in UNIX format before anything is touched 134 | if $mingw ; then 135 | [ -n "$M2_HOME" ] && 136 | M2_HOME="`(cd "$M2_HOME"; pwd)`" 137 | [ -n "$JAVA_HOME" ] && 138 | JAVA_HOME="`(cd "$JAVA_HOME"; pwd)`" 139 | # TODO classpath? 140 | fi 141 | 142 | if [ -z "$JAVA_HOME" ]; then 143 | javaExecutable="`which javac`" 144 | if [ -n "$javaExecutable" ] && ! [ "`expr \"$javaExecutable\" : '\([^ ]*\)'`" = "no" ]; then 145 | # readlink(1) is not available as standard on Solaris 10. 146 | readLink=`which readlink` 147 | if [ ! `expr "$readLink" : '\([^ ]*\)'` = "no" ]; then 148 | if $darwin ; then 149 | javaHome="`dirname \"$javaExecutable\"`" 150 | javaExecutable="`cd \"$javaHome\" && pwd -P`/javac" 151 | else 152 | javaExecutable="`readlink -f \"$javaExecutable\"`" 153 | fi 154 | javaHome="`dirname \"$javaExecutable\"`" 155 | javaHome=`expr "$javaHome" : '\(.*\)/bin'` 156 | JAVA_HOME="$javaHome" 157 | export JAVA_HOME 158 | fi 159 | fi 160 | fi 161 | 162 | if [ -z "$JAVACMD" ] ; then 163 | if [ -n "$JAVA_HOME" ] ; then 164 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 165 | # IBM's JDK on AIX uses strange locations for the executables 166 | JAVACMD="$JAVA_HOME/jre/sh/java" 167 | else 168 | JAVACMD="$JAVA_HOME/bin/java" 169 | fi 170 | else 171 | JAVACMD="`which java`" 172 | fi 173 | fi 174 | 175 | if [ ! -x "$JAVACMD" ] ; then 176 | echo "Error: JAVA_HOME is not defined correctly." >&2 177 | echo " We cannot execute $JAVACMD" >&2 178 | exit 1 179 | fi 180 | 181 | if [ -z "$JAVA_HOME" ] ; then 182 | echo "Warning: JAVA_HOME environment variable is not set." 183 | fi 184 | 185 | CLASSWORLDS_LAUNCHER=org.codehaus.plexus.classworlds.launcher.Launcher 186 | 187 | # For Cygwin, switch paths to Windows format before running java 188 | if $cygwin; then 189 | [ -n "$M2_HOME" ] && 190 | M2_HOME=`cygpath --path --windows "$M2_HOME"` 191 | [ -n "$JAVA_HOME" ] && 192 | JAVA_HOME=`cygpath --path --windows "$JAVA_HOME"` 193 | [ -n "$CLASSPATH" ] && 194 | CLASSPATH=`cygpath --path --windows "$CLASSPATH"` 195 | fi 196 | 197 | # traverses directory structure from process work directory to filesystem root 198 | # first directory with .mvn subdirectory is considered project base directory 199 | find_maven_basedir() { 200 | local basedir=$(pwd) 201 | local wdir=$(pwd) 202 | while [ "$wdir" != '/' ] ; do 203 | if [ -d "$wdir"/.mvn ] ; then 204 | basedir=$wdir 205 | break 206 | fi 207 | wdir=$(cd "$wdir/.."; pwd) 208 | done 209 | echo "${basedir}" 210 | } 211 | 212 | # concatenates all lines of a file 213 | concat_lines() { 214 | if [ -f "$1" ]; then 215 | echo "$(tr -s '\n' ' ' < "$1")" 216 | fi 217 | } 218 | 219 | export MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-$(find_maven_basedir)} 220 | MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS" 221 | 222 | # Provide a "standardized" way to retrieve the CLI args that will 223 | # work with both Windows and non-Windows executions. 224 | MAVEN_CMD_LINE_ARGS="$MAVEN_CONFIG $@" 225 | export MAVEN_CMD_LINE_ARGS 226 | 227 | WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain 228 | 229 | exec "$JAVACMD" \ 230 | $MAVEN_OPTS \ 231 | -classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \ 232 | "-Dmaven.home=${M2_HOME}" "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \ 233 | ${WRAPPER_LAUNCHER} "$@" 234 | -------------------------------------------------------------------------------- /src/main/resources/static/js/jquery.autocomplete.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Ajax Autocomplete for jQuery, version 1.4.1 3 | * (c) 2017 Tomas Kirda 4 | * 5 | * Ajax Autocomplete for jQuery is freely distributable under the terms of an MIT-style license. 6 | * For details, see the web site: https://github.com/devbridge/jQuery-Autocomplete 7 | */ 8 | 9 | /*jslint browser: true, white: true, single: true, this: true, multivar: true */ 10 | /*global define, window, document, jQuery, exports, require */ 11 | 12 | // Expose plugin as an AMD module if AMD loader is present: 13 | (function (factory) { 14 | "use strict"; 15 | if (typeof define === 'function' && define.amd) { 16 | // AMD. Register as an anonymous module. 17 | define(['jquery'], factory); 18 | } else if (typeof exports === 'object' && typeof require === 'function') { 19 | // Browserify 20 | factory(require('jquery')); 21 | } else { 22 | // Browser globals 23 | factory(jQuery); 24 | } 25 | }(function ($) { 26 | 'use strict'; 27 | 28 | var 29 | utils = (function () { 30 | return { 31 | escapeRegExChars: function (value) { 32 | return value.replace(/[|\\{}()[\]^$+*?.]/g, "\\$&"); 33 | }, 34 | createNode: function (containerClass) { 35 | var div = document.createElement('div'); 36 | div.className = containerClass; 37 | div.style.position = 'absolute'; 38 | div.style.display = 'none'; 39 | return div; 40 | } 41 | }; 42 | }()), 43 | 44 | keys = { 45 | ESC: 27, 46 | TAB: 9, 47 | RETURN: 13, 48 | LEFT: 37, 49 | UP: 38, 50 | RIGHT: 39, 51 | DOWN: 40 52 | }, 53 | 54 | noop = $.noop; 55 | 56 | function Autocomplete(el, options) { 57 | var that = this; 58 | 59 | // Shared variables: 60 | that.element = el; 61 | that.el = $(el); 62 | that.suggestions = []; 63 | that.badQueries = []; 64 | that.selectedIndex = -1; 65 | that.currentValue = that.element.value; 66 | that.timeoutId = null; 67 | that.cachedResponse = {}; 68 | that.onChangeTimeout = null; 69 | that.onChange = null; 70 | that.isLocal = false; 71 | that.suggestionsContainer = null; 72 | that.noSuggestionsContainer = null; 73 | that.options = $.extend({}, Autocomplete.defaults, options); 74 | that.classes = { 75 | selected: 'autocomplete-selected', 76 | suggestion: 'autocomplete-suggestion' 77 | }; 78 | that.hint = null; 79 | that.hintValue = ''; 80 | that.selection = null; 81 | 82 | // Initialize and set options: 83 | that.initialize(); 84 | that.setOptions(options); 85 | } 86 | 87 | Autocomplete.utils = utils; 88 | 89 | $.Autocomplete = Autocomplete; 90 | 91 | Autocomplete.defaults = { 92 | ajaxSettings: {}, 93 | autoSelectFirst: false, 94 | appendTo: 'body', 95 | serviceUrl: null, 96 | lookup: null, 97 | onSelect: null, 98 | width: 'auto', 99 | minChars: 1, 100 | maxHeight: 300, 101 | deferRequestBy: 0, 102 | params: {}, 103 | formatResult: _formatResult, 104 | formatGroup: _formatGroup, 105 | delimiter: null, 106 | zIndex: 9999, 107 | type: 'GET', 108 | noCache: false, 109 | onSearchStart: noop, 110 | onSearchComplete: noop, 111 | onSearchError: noop, 112 | preserveInput: false, 113 | containerClass: 'autocomplete-suggestions', 114 | tabDisabled: false, 115 | dataType: 'text', 116 | currentRequest: null, 117 | triggerSelectOnValidInput: true, 118 | preventBadQueries: true, 119 | lookupFilter: _lookupFilter, 120 | paramName: 'query', 121 | transformResult: _transformResult, 122 | showNoSuggestionNotice: false, 123 | noSuggestionNotice: 'No results', 124 | orientation: 'bottom', 125 | forceFixPosition: false 126 | }; 127 | 128 | function _lookupFilter(suggestion, originalQuery, queryLowerCase) { 129 | return suggestion.value.toLowerCase().indexOf(queryLowerCase) !== -1; 130 | }; 131 | 132 | function _transformResult(response) { 133 | return typeof response === 'string' ? $.parseJSON(response) : response; 134 | }; 135 | 136 | function _formatResult(suggestion, currentValue) { 137 | // Do not replace anything if the current value is empty 138 | if (!currentValue) { 139 | return suggestion.value; 140 | } 141 | 142 | var pattern = '(' + utils.escapeRegExChars(currentValue) + ')'; 143 | 144 | return suggestion.value 145 | .replace(new RegExp(pattern, 'gi'), '$1<\/strong>') 146 | .replace(/&/g, '&') 147 | .replace(//g, '>') 149 | .replace(/"/g, '"') 150 | .replace(/<(\/?strong)>/g, '<$1>'); 151 | }; 152 | 153 | function _formatGroup(suggestion, category) { 154 | return '
' + category + '
'; 155 | }; 156 | 157 | Autocomplete.prototype = { 158 | 159 | initialize: function () { 160 | var that = this, 161 | suggestionSelector = '.' + that.classes.suggestion, 162 | selected = that.classes.selected, 163 | options = that.options, 164 | container; 165 | 166 | // Remove autocomplete attribute to prevent native suggestions: 167 | that.element.setAttribute('autocomplete', 'off'); 168 | 169 | // html() deals with many types: htmlString or Element or Array or jQuery 170 | that.noSuggestionsContainer = $('
') 171 | .html(this.options.noSuggestionNotice).get(0); 172 | 173 | that.suggestionsContainer = Autocomplete.utils.createNode(options.containerClass); 174 | 175 | container = $(that.suggestionsContainer); 176 | 177 | container.appendTo(options.appendTo || 'body'); 178 | 179 | // Only set width if it was provided: 180 | if (options.width !== 'auto') { 181 | container.css('width', options.width); 182 | } 183 | 184 | // Listen for mouse over event on suggestions list: 185 | container.on('mouseover.autocomplete', suggestionSelector, function () { 186 | that.activate($(this).data('index')); 187 | }); 188 | 189 | // Deselect active element when mouse leaves suggestions container: 190 | container.on('mouseout.autocomplete', function () { 191 | that.selectedIndex = -1; 192 | container.children('.' + selected).removeClass(selected); 193 | }); 194 | 195 | 196 | // Listen for click event on suggestions list: 197 | container.on('click.autocomplete', suggestionSelector, function () { 198 | that.select($(this).data('index')); 199 | }); 200 | 201 | container.on('click.autocomplete', function () { 202 | clearTimeout(that.blurTimeoutId); 203 | }) 204 | 205 | that.fixPositionCapture = function () { 206 | if (that.visible) { 207 | that.fixPosition(); 208 | } 209 | }; 210 | 211 | $(window).on('resize.autocomplete', that.fixPositionCapture); 212 | 213 | that.el.on('keydown.autocomplete', function (e) { that.onKeyPress(e); }); 214 | that.el.on('keyup.autocomplete', function (e) { that.onKeyUp(e); }); 215 | that.el.on('blur.autocomplete', function () { that.onBlur(); }); 216 | that.el.on('focus.autocomplete', function () { that.onFocus(); }); 217 | that.el.on('change.autocomplete', function (e) { that.onKeyUp(e); }); 218 | that.el.on('input.autocomplete', function (e) { that.onKeyUp(e); }); 219 | }, 220 | 221 | onFocus: function () { 222 | var that = this; 223 | 224 | that.fixPosition(); 225 | 226 | if (that.el.val().length >= that.options.minChars) { 227 | that.onValueChange(); 228 | } 229 | }, 230 | 231 | onBlur: function () { 232 | var that = this; 233 | 234 | // If user clicked on a suggestion, hide() will 235 | // be canceled, otherwise close suggestions 236 | that.blurTimeoutId = setTimeout(function () { 237 | that.hide(); 238 | }, 200); 239 | }, 240 | 241 | abortAjax: function () { 242 | var that = this; 243 | if (that.currentRequest) { 244 | that.currentRequest.abort(); 245 | that.currentRequest = null; 246 | } 247 | }, 248 | 249 | setOptions: function (suppliedOptions) { 250 | var that = this, 251 | options = that.options; 252 | 253 | this.options = $.extend({}, options, suppliedOptions); 254 | 255 | that.isLocal = $.isArray(options.lookup); 256 | 257 | if (that.isLocal) { 258 | options.lookup = that.verifySuggestionsFormat(options.lookup); 259 | } 260 | 261 | options.orientation = that.validateOrientation(options.orientation, 'bottom'); 262 | 263 | // Adjust height, width and z-index: 264 | $(that.suggestionsContainer).css({ 265 | 'max-height': options.maxHeight + 'px', 266 | 'width': options.width + 'px', 267 | 'z-index': options.zIndex 268 | }); 269 | }, 270 | 271 | 272 | clearCache: function () { 273 | this.cachedResponse = {}; 274 | this.badQueries = []; 275 | }, 276 | 277 | clear: function () { 278 | this.clearCache(); 279 | this.currentValue = ''; 280 | this.suggestions = []; 281 | }, 282 | 283 | disable: function () { 284 | var that = this; 285 | that.disabled = true; 286 | clearTimeout(that.onChangeTimeout); 287 | that.abortAjax(); 288 | }, 289 | 290 | enable: function () { 291 | this.disabled = false; 292 | }, 293 | 294 | fixPosition: function () { 295 | // Use only when container has already its content 296 | 297 | var that = this, 298 | $container = $(that.suggestionsContainer), 299 | containerParent = $container.parent().get(0); 300 | // Fix position automatically when appended to body. 301 | // In other cases force parameter must be given. 302 | if (containerParent !== document.body && !that.options.forceFixPosition) { 303 | return; 304 | } 305 | 306 | // Choose orientation 307 | var orientation = that.options.orientation, 308 | containerHeight = $container.outerHeight(), 309 | height = that.el.outerHeight(), 310 | offset = that.el.offset(), 311 | styles = { 'top': offset.top, 'left': offset.left }; 312 | 313 | if (orientation === 'auto') { 314 | var viewPortHeight = $(window).height(), 315 | scrollTop = $(window).scrollTop(), 316 | topOverflow = -scrollTop + offset.top - containerHeight, 317 | bottomOverflow = scrollTop + viewPortHeight - (offset.top + height + containerHeight); 318 | 319 | orientation = (Math.max(topOverflow, bottomOverflow) === topOverflow) ? 'top' : 'bottom'; 320 | } 321 | 322 | if (orientation === 'top') { 323 | styles.top += -containerHeight; 324 | } else { 325 | styles.top += height; 326 | } 327 | 328 | // If container is not positioned to body, 329 | // correct its position using offset parent offset 330 | if(containerParent !== document.body) { 331 | var opacity = $container.css('opacity'), 332 | parentOffsetDiff; 333 | 334 | if (!that.visible){ 335 | $container.css('opacity', 0).show(); 336 | } 337 | 338 | parentOffsetDiff = $container.offsetParent().offset(); 339 | styles.top -= parentOffsetDiff.top; 340 | styles.left -= parentOffsetDiff.left; 341 | 342 | if (!that.visible){ 343 | $container.css('opacity', opacity).hide(); 344 | } 345 | } 346 | 347 | if (that.options.width === 'auto') { 348 | styles.width = that.el.outerWidth() + 'px'; 349 | } 350 | 351 | $container.css(styles); 352 | }, 353 | 354 | isCursorAtEnd: function () { 355 | var that = this, 356 | valLength = that.el.val().length, 357 | selectionStart = that.element.selectionStart, 358 | range; 359 | 360 | if (typeof selectionStart === 'number') { 361 | return selectionStart === valLength; 362 | } 363 | if (document.selection) { 364 | range = document.selection.createRange(); 365 | range.moveStart('character', -valLength); 366 | return valLength === range.text.length; 367 | } 368 | return true; 369 | }, 370 | 371 | onKeyPress: function (e) { 372 | var that = this; 373 | 374 | // If suggestions are hidden and user presses arrow down, display suggestions: 375 | if (!that.disabled && !that.visible && e.which === keys.DOWN && that.currentValue) { 376 | that.suggest(); 377 | return; 378 | } 379 | 380 | if (that.disabled || !that.visible) { 381 | return; 382 | } 383 | 384 | switch (e.which) { 385 | case keys.ESC: 386 | that.el.val(that.currentValue); 387 | that.hide(); 388 | break; 389 | case keys.RIGHT: 390 | if (that.hint && that.options.onHint && that.isCursorAtEnd()) { 391 | that.selectHint(); 392 | break; 393 | } 394 | return; 395 | case keys.TAB: 396 | if (that.hint && that.options.onHint) { 397 | that.selectHint(); 398 | return; 399 | } 400 | if (that.selectedIndex === -1) { 401 | that.hide(); 402 | return; 403 | } 404 | that.select(that.selectedIndex); 405 | if (that.options.tabDisabled === false) { 406 | return; 407 | } 408 | break; 409 | case keys.RETURN: 410 | if (that.selectedIndex === -1) { 411 | that.hide(); 412 | return; 413 | } 414 | that.select(that.selectedIndex); 415 | break; 416 | case keys.UP: 417 | that.moveUp(); 418 | break; 419 | case keys.DOWN: 420 | that.moveDown(); 421 | break; 422 | default: 423 | return; 424 | } 425 | 426 | // Cancel event if function did not return: 427 | e.stopImmediatePropagation(); 428 | e.preventDefault(); 429 | }, 430 | 431 | onKeyUp: function (e) { 432 | var that = this; 433 | 434 | if (that.disabled) { 435 | return; 436 | } 437 | 438 | switch (e.which) { 439 | case keys.UP: 440 | case keys.DOWN: 441 | return; 442 | } 443 | 444 | clearTimeout(that.onChangeTimeout); 445 | 446 | if (that.currentValue !== that.el.val()) { 447 | that.findBestHint(); 448 | if (that.options.deferRequestBy > 0) { 449 | // Defer lookup in case when value changes very quickly: 450 | that.onChangeTimeout = setTimeout(function () { 451 | that.onValueChange(); 452 | }, that.options.deferRequestBy); 453 | } else { 454 | that.onValueChange(); 455 | } 456 | } 457 | }, 458 | 459 | onValueChange: function () { 460 | var that = this, 461 | options = that.options, 462 | value = that.el.val(), 463 | query = that.getQuery(value); 464 | 465 | if (that.selection && that.currentValue !== query) { 466 | that.selection = null; 467 | (options.onInvalidateSelection || $.noop).call(that.element); 468 | } 469 | 470 | clearTimeout(that.onChangeTimeout); 471 | that.currentValue = value; 472 | that.selectedIndex = -1; 473 | 474 | // Check existing suggestion for the match before proceeding: 475 | if (options.triggerSelectOnValidInput && that.isExactMatch(query)) { 476 | that.select(0); 477 | return; 478 | } 479 | 480 | if (query.length < options.minChars) { 481 | that.hide(); 482 | } else { 483 | that.getSuggestions(query); 484 | } 485 | }, 486 | 487 | isExactMatch: function (query) { 488 | var suggestions = this.suggestions; 489 | 490 | return (suggestions.length === 1 && suggestions[0].value.toLowerCase() === query.toLowerCase()); 491 | }, 492 | 493 | getQuery: function (value) { 494 | var delimiter = this.options.delimiter, 495 | parts; 496 | 497 | if (!delimiter) { 498 | return value; 499 | } 500 | parts = value.split(delimiter); 501 | return $.trim(parts[parts.length - 1]); 502 | }, 503 | 504 | getSuggestionsLocal: function (query) { 505 | var that = this, 506 | options = that.options, 507 | queryLowerCase = query.toLowerCase(), 508 | filter = options.lookupFilter, 509 | limit = parseInt(options.lookupLimit, 10), 510 | data; 511 | 512 | data = { 513 | suggestions: $.grep(options.lookup, function (suggestion) { 514 | return filter(suggestion, query, queryLowerCase); 515 | }) 516 | }; 517 | 518 | if (limit && data.suggestions.length > limit) { 519 | data.suggestions = data.suggestions.slice(0, limit); 520 | } 521 | 522 | return data; 523 | }, 524 | 525 | getSuggestions: function (q) { 526 | var response, 527 | that = this, 528 | options = that.options, 529 | serviceUrl = options.serviceUrl, 530 | params, 531 | cacheKey, 532 | ajaxSettings; 533 | 534 | options.params[options.paramName] = q; 535 | 536 | if (options.onSearchStart.call(that.element, options.params) === false) { 537 | return; 538 | } 539 | 540 | params = options.ignoreParams ? null : options.params; 541 | 542 | if ($.isFunction(options.lookup)){ 543 | options.lookup(q, function (data) { 544 | that.suggestions = data.suggestions; 545 | that.suggest(); 546 | options.onSearchComplete.call(that.element, q, data.suggestions); 547 | }); 548 | return; 549 | } 550 | 551 | if (that.isLocal) { 552 | response = that.getSuggestionsLocal(q); 553 | } else { 554 | if ($.isFunction(serviceUrl)) { 555 | serviceUrl = serviceUrl.call(that.element, q); 556 | } 557 | cacheKey = serviceUrl + '?' + $.param(params || {}); 558 | response = that.cachedResponse[cacheKey]; 559 | } 560 | 561 | if (response && $.isArray(response.suggestions)) { 562 | that.suggestions = response.suggestions; 563 | that.suggest(); 564 | options.onSearchComplete.call(that.element, q, response.suggestions); 565 | } else if (!that.isBadQuery(q)) { 566 | that.abortAjax(); 567 | 568 | ajaxSettings = { 569 | url: serviceUrl, 570 | data: params, 571 | type: options.type, 572 | dataType: options.dataType 573 | }; 574 | 575 | $.extend(ajaxSettings, options.ajaxSettings); 576 | 577 | that.currentRequest = $.ajax(ajaxSettings).done(function (data) { 578 | var result; 579 | that.currentRequest = null; 580 | result = options.transformResult(data, q); 581 | that.processResponse(result, q, cacheKey); 582 | options.onSearchComplete.call(that.element, q, result.suggestions); 583 | }).fail(function (jqXHR, textStatus, errorThrown) { 584 | options.onSearchError.call(that.element, q, jqXHR, textStatus, errorThrown); 585 | }); 586 | } else { 587 | options.onSearchComplete.call(that.element, q, []); 588 | } 589 | }, 590 | 591 | isBadQuery: function (q) { 592 | if (!this.options.preventBadQueries){ 593 | return false; 594 | } 595 | 596 | var badQueries = this.badQueries, 597 | i = badQueries.length; 598 | 599 | while (i--) { 600 | if (q.indexOf(badQueries[i]) === 0) { 601 | return true; 602 | } 603 | } 604 | 605 | return false; 606 | }, 607 | 608 | hide: function () { 609 | var that = this, 610 | container = $(that.suggestionsContainer); 611 | 612 | if ($.isFunction(that.options.onHide) && that.visible) { 613 | that.options.onHide.call(that.element, container); 614 | } 615 | 616 | that.visible = false; 617 | that.selectedIndex = -1; 618 | clearTimeout(that.onChangeTimeout); 619 | $(that.suggestionsContainer).hide(); 620 | that.signalHint(null); 621 | }, 622 | 623 | suggest: function () { 624 | if (!this.suggestions.length) { 625 | if (this.options.showNoSuggestionNotice) { 626 | this.noSuggestions(); 627 | } else { 628 | this.hide(); 629 | } 630 | return; 631 | } 632 | 633 | var that = this, 634 | options = that.options, 635 | groupBy = options.groupBy, 636 | formatResult = options.formatResult, 637 | value = that.getQuery(that.currentValue), 638 | className = that.classes.suggestion, 639 | classSelected = that.classes.selected, 640 | container = $(that.suggestionsContainer), 641 | noSuggestionsContainer = $(that.noSuggestionsContainer), 642 | beforeRender = options.beforeRender, 643 | html = '', 644 | category, 645 | formatGroup = function (suggestion, index) { 646 | var currentCategory = suggestion.data[groupBy]; 647 | 648 | if (category === currentCategory){ 649 | return ''; 650 | } 651 | 652 | category = currentCategory; 653 | 654 | return options.formatGroup(suggestion, category); 655 | }; 656 | 657 | if (options.triggerSelectOnValidInput && that.isExactMatch(value)) { 658 | that.select(0); 659 | return; 660 | } 661 | 662 | // Build suggestions inner HTML: 663 | $.each(that.suggestions, function (i, suggestion) { 664 | if (groupBy){ 665 | html += formatGroup(suggestion, value, i); 666 | } 667 | 668 | html += '
' + formatResult(suggestion, value, i) + '
'; 669 | }); 670 | 671 | this.adjustContainerWidth(); 672 | 673 | noSuggestionsContainer.detach(); 674 | container.html(html); 675 | 676 | if ($.isFunction(beforeRender)) { 677 | beforeRender.call(that.element, container, that.suggestions); 678 | } 679 | 680 | that.fixPosition(); 681 | container.show(); 682 | 683 | // Select first value by default: 684 | if (options.autoSelectFirst) { 685 | that.selectedIndex = 0; 686 | container.scrollTop(0); 687 | container.children('.' + className).first().addClass(classSelected); 688 | } 689 | 690 | that.visible = true; 691 | that.findBestHint(); 692 | }, 693 | 694 | noSuggestions: function() { 695 | var that = this, 696 | beforeRender = that.options.beforeRender, 697 | container = $(that.suggestionsContainer), 698 | noSuggestionsContainer = $(that.noSuggestionsContainer); 699 | 700 | this.adjustContainerWidth(); 701 | 702 | // Some explicit steps. Be careful here as it easy to get 703 | // noSuggestionsContainer removed from DOM if not detached properly. 704 | noSuggestionsContainer.detach(); 705 | 706 | // clean suggestions if any 707 | container.empty(); 708 | container.append(noSuggestionsContainer); 709 | 710 | if ($.isFunction(beforeRender)) { 711 | beforeRender.call(that.element, container, that.suggestions); 712 | } 713 | 714 | that.fixPosition(); 715 | 716 | container.show(); 717 | that.visible = true; 718 | }, 719 | 720 | adjustContainerWidth: function() { 721 | var that = this, 722 | options = that.options, 723 | width, 724 | container = $(that.suggestionsContainer); 725 | 726 | // If width is auto, adjust width before displaying suggestions, 727 | // because if instance was created before input had width, it will be zero. 728 | // Also it adjusts if input width has changed. 729 | if (options.width === 'auto') { 730 | width = that.el.outerWidth(); 731 | container.css('width', width > 0 ? width : 300); 732 | } else if(options.width === 'flex') { 733 | // Trust the source! Unset the width property so it will be the max length 734 | // the containing elements. 735 | container.css('width', ''); 736 | } 737 | }, 738 | 739 | findBestHint: function () { 740 | var that = this, 741 | value = that.el.val().toLowerCase(), 742 | bestMatch = null; 743 | 744 | if (!value) { 745 | return; 746 | } 747 | 748 | $.each(that.suggestions, function (i, suggestion) { 749 | var foundMatch = suggestion.value.toLowerCase().indexOf(value) === 0; 750 | if (foundMatch) { 751 | bestMatch = suggestion; 752 | } 753 | return !foundMatch; 754 | }); 755 | 756 | that.signalHint(bestMatch); 757 | }, 758 | 759 | signalHint: function (suggestion) { 760 | var hintValue = '', 761 | that = this; 762 | if (suggestion) { 763 | hintValue = that.currentValue + suggestion.value.substr(that.currentValue.length); 764 | } 765 | if (that.hintValue !== hintValue) { 766 | that.hintValue = hintValue; 767 | that.hint = suggestion; 768 | (this.options.onHint || $.noop)(hintValue); 769 | } 770 | }, 771 | 772 | verifySuggestionsFormat: function (suggestions) { 773 | // If suggestions is string array, convert them to supported format: 774 | if (suggestions.length && typeof suggestions[0] === 'string') { 775 | return $.map(suggestions, function (value) { 776 | return { value: value, data: null }; 777 | }); 778 | } 779 | 780 | return suggestions; 781 | }, 782 | 783 | validateOrientation: function(orientation, fallback) { 784 | orientation = $.trim(orientation || '').toLowerCase(); 785 | 786 | if($.inArray(orientation, ['auto', 'bottom', 'top']) === -1){ 787 | orientation = fallback; 788 | } 789 | 790 | return orientation; 791 | }, 792 | 793 | processResponse: function (result, originalQuery, cacheKey) { 794 | var that = this, 795 | options = that.options; 796 | 797 | result.suggestions = that.verifySuggestionsFormat(result.suggestions); 798 | 799 | // Cache results if cache is not disabled: 800 | if (!options.noCache) { 801 | that.cachedResponse[cacheKey] = result; 802 | if (options.preventBadQueries && !result.suggestions.length) { 803 | that.badQueries.push(originalQuery); 804 | } 805 | } 806 | 807 | // Return if originalQuery is not matching current query: 808 | if (originalQuery !== that.getQuery(that.currentValue)) { 809 | return; 810 | } 811 | 812 | that.suggestions = result.suggestions; 813 | that.suggest(); 814 | }, 815 | 816 | activate: function (index) { 817 | var that = this, 818 | activeItem, 819 | selected = that.classes.selected, 820 | container = $(that.suggestionsContainer), 821 | children = container.find('.' + that.classes.suggestion); 822 | 823 | container.find('.' + selected).removeClass(selected); 824 | 825 | that.selectedIndex = index; 826 | 827 | if (that.selectedIndex !== -1 && children.length > that.selectedIndex) { 828 | activeItem = children.get(that.selectedIndex); 829 | $(activeItem).addClass(selected); 830 | return activeItem; 831 | } 832 | 833 | return null; 834 | }, 835 | 836 | selectHint: function () { 837 | var that = this, 838 | i = $.inArray(that.hint, that.suggestions); 839 | 840 | that.select(i); 841 | }, 842 | 843 | select: function (i) { 844 | var that = this; 845 | that.hide(); 846 | that.onSelect(i); 847 | }, 848 | 849 | moveUp: function () { 850 | var that = this; 851 | 852 | if (that.selectedIndex === -1) { 853 | return; 854 | } 855 | 856 | if (that.selectedIndex === 0) { 857 | $(that.suggestionsContainer).children().first().removeClass(that.classes.selected); 858 | that.selectedIndex = -1; 859 | that.el.val(that.currentValue); 860 | that.findBestHint(); 861 | return; 862 | } 863 | 864 | that.adjustScroll(that.selectedIndex - 1); 865 | }, 866 | 867 | moveDown: function () { 868 | var that = this; 869 | 870 | if (that.selectedIndex === (that.suggestions.length - 1)) { 871 | return; 872 | } 873 | 874 | that.adjustScroll(that.selectedIndex + 1); 875 | }, 876 | 877 | adjustScroll: function (index) { 878 | var that = this, 879 | activeItem = that.activate(index); 880 | 881 | if (!activeItem) { 882 | return; 883 | } 884 | 885 | var offsetTop, 886 | upperBound, 887 | lowerBound, 888 | heightDelta = $(activeItem).outerHeight(); 889 | 890 | offsetTop = activeItem.offsetTop; 891 | upperBound = $(that.suggestionsContainer).scrollTop(); 892 | lowerBound = upperBound + that.options.maxHeight - heightDelta; 893 | 894 | if (offsetTop < upperBound) { 895 | $(that.suggestionsContainer).scrollTop(offsetTop); 896 | } else if (offsetTop > lowerBound) { 897 | $(that.suggestionsContainer).scrollTop(offsetTop - that.options.maxHeight + heightDelta); 898 | } 899 | 900 | if (!that.options.preserveInput) { 901 | that.el.val(that.getValue(that.suggestions[index].value)); 902 | } 903 | that.signalHint(null); 904 | }, 905 | 906 | onSelect: function (index) { 907 | var that = this, 908 | onSelectCallback = that.options.onSelect, 909 | suggestion = that.suggestions[index]; 910 | 911 | that.currentValue = that.getValue(suggestion.value); 912 | 913 | if (that.currentValue !== that.el.val() && !that.options.preserveInput) { 914 | that.el.val(that.currentValue); 915 | } 916 | 917 | that.signalHint(null); 918 | that.suggestions = []; 919 | that.selection = suggestion; 920 | 921 | if ($.isFunction(onSelectCallback)) { 922 | onSelectCallback.call(that.element, suggestion); 923 | } 924 | }, 925 | 926 | getValue: function (value) { 927 | var that = this, 928 | delimiter = that.options.delimiter, 929 | currentValue, 930 | parts; 931 | 932 | if (!delimiter) { 933 | return value; 934 | } 935 | 936 | currentValue = that.currentValue; 937 | parts = currentValue.split(delimiter); 938 | 939 | if (parts.length === 1) { 940 | return value; 941 | } 942 | 943 | return currentValue.substr(0, currentValue.length - parts[parts.length - 1].length) + value; 944 | }, 945 | 946 | dispose: function () { 947 | var that = this; 948 | that.el.off('.autocomplete').removeData('autocomplete'); 949 | $(window).off('resize.autocomplete', that.fixPositionCapture); 950 | $(that.suggestionsContainer).remove(); 951 | } 952 | }; 953 | 954 | // Create chainable jQuery plugin: 955 | $.fn.devbridgeAutocomplete = function (options, args) { 956 | var dataKey = 'autocomplete'; 957 | // If function invoked without argument return 958 | // instance of the first matched element: 959 | if (!arguments.length) { 960 | return this.first().data(dataKey); 961 | } 962 | 963 | return this.each(function () { 964 | var inputElement = $(this), 965 | instance = inputElement.data(dataKey); 966 | 967 | if (typeof options === 'string') { 968 | if (instance && typeof instance[options] === 'function') { 969 | instance[options](args); 970 | } 971 | } else { 972 | // If instance already exists, destroy it: 973 | if (instance && instance.dispose) { 974 | instance.dispose(); 975 | } 976 | instance = new Autocomplete(this, options); 977 | inputElement.data(dataKey, instance); 978 | } 979 | }); 980 | }; 981 | 982 | // Don't overwrite if it already exists 983 | if (!$.fn.autocomplete) { 984 | $.fn.autocomplete = $.fn.devbridgeAutocomplete; 985 | } 986 | })); 987 | -------------------------------------------------------------------------------- /src/main/resources/static/js/jquery-3.2.0.min.js: -------------------------------------------------------------------------------- 1 | /*! jQuery v3.2.0 | (c) JS Foundation and other contributors | jquery.org/license */ 2 | !function(a,b){"use strict";"object"==typeof module&&"object"==typeof module.exports?module.exports=a.document?b(a,!0):function(a){if(!a.document)throw new Error("jQuery requires a window with a document");return b(a)}:b(a)}("undefined"!=typeof window?window:this,function(a,b){"use strict";var c=[],d=a.document,e=Object.getPrototypeOf,f=c.slice,g=c.concat,h=c.push,i=c.indexOf,j={},k=j.toString,l=j.hasOwnProperty,m=l.toString,n=m.call(Object),o={};function p(a,b){b=b||d;var c=b.createElement("script");c.text=a,b.head.appendChild(c).parentNode.removeChild(c)}var q="3.2.0",r=function(a,b){return new r.fn.init(a,b)},s=/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g,t=/^-ms-/,u=/-([a-z])/g,v=function(a,b){return b.toUpperCase()};r.fn=r.prototype={jquery:q,constructor:r,length:0,toArray:function(){return f.call(this)},get:function(a){return null==a?f.call(this):a<0?this[a+this.length]:this[a]},pushStack:function(a){var b=r.merge(this.constructor(),a);return b.prevObject=this,b},each:function(a){return r.each(this,a)},map:function(a){return this.pushStack(r.map(this,function(b,c){return a.call(b,c,b)}))},slice:function(){return this.pushStack(f.apply(this,arguments))},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},eq:function(a){var b=this.length,c=+a+(a<0?b:0);return this.pushStack(c>=0&&c0&&b-1 in a)}var x=function(a){var b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u="sizzle"+1*new Date,v=a.document,w=0,x=0,y=ha(),z=ha(),A=ha(),B=function(a,b){return a===b&&(l=!0),0},C={}.hasOwnProperty,D=[],E=D.pop,F=D.push,G=D.push,H=D.slice,I=function(a,b){for(var c=0,d=a.length;c+~]|"+K+")"+K+"*"),S=new RegExp("="+K+"*([^\\]'\"]*?)"+K+"*\\]","g"),T=new RegExp(N),U=new RegExp("^"+L+"$"),V={ID:new RegExp("^#("+L+")"),CLASS:new RegExp("^\\.("+L+")"),TAG:new RegExp("^("+L+"|[*])"),ATTR:new RegExp("^"+M),PSEUDO:new RegExp("^"+N),CHILD:new RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+K+"*(even|odd|(([+-]|)(\\d*)n|)"+K+"*(?:([+-]|)"+K+"*(\\d+)|))"+K+"*\\)|)","i"),bool:new RegExp("^(?:"+J+")$","i"),needsContext:new RegExp("^"+K+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+K+"*((?:-\\d)?\\d*)"+K+"*\\)|)(?=[^-]|$)","i")},W=/^(?:input|select|textarea|button)$/i,X=/^h\d$/i,Y=/^[^{]+\{\s*\[native \w/,Z=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,$=/[+~]/,_=new RegExp("\\\\([\\da-f]{1,6}"+K+"?|("+K+")|.)","ig"),aa=function(a,b,c){var d="0x"+b-65536;return d!==d||c?b:d<0?String.fromCharCode(d+65536):String.fromCharCode(d>>10|55296,1023&d|56320)},ba=/([\0-\x1f\x7f]|^-?\d)|^-$|[^\0-\x1f\x7f-\uFFFF\w-]/g,ca=function(a,b){return b?"\0"===a?"\ufffd":a.slice(0,-1)+"\\"+a.charCodeAt(a.length-1).toString(16)+" ":"\\"+a},da=function(){m()},ea=ta(function(a){return a.disabled===!0&&("form"in a||"label"in a)},{dir:"parentNode",next:"legend"});try{G.apply(D=H.call(v.childNodes),v.childNodes),D[v.childNodes.length].nodeType}catch(fa){G={apply:D.length?function(a,b){F.apply(a,H.call(b))}:function(a,b){var c=a.length,d=0;while(a[c++]=b[d++]);a.length=c-1}}}function ga(a,b,d,e){var f,h,j,k,l,o,r,s=b&&b.ownerDocument,w=b?b.nodeType:9;if(d=d||[],"string"!=typeof a||!a||1!==w&&9!==w&&11!==w)return d;if(!e&&((b?b.ownerDocument||b:v)!==n&&m(b),b=b||n,p)){if(11!==w&&(l=Z.exec(a)))if(f=l[1]){if(9===w){if(!(j=b.getElementById(f)))return d;if(j.id===f)return d.push(j),d}else if(s&&(j=s.getElementById(f))&&t(b,j)&&j.id===f)return d.push(j),d}else{if(l[2])return G.apply(d,b.getElementsByTagName(a)),d;if((f=l[3])&&c.getElementsByClassName&&b.getElementsByClassName)return G.apply(d,b.getElementsByClassName(f)),d}if(c.qsa&&!A[a+" "]&&(!q||!q.test(a))){if(1!==w)s=b,r=a;else if("object"!==b.nodeName.toLowerCase()){(k=b.getAttribute("id"))?k=k.replace(ba,ca):b.setAttribute("id",k=u),o=g(a),h=o.length;while(h--)o[h]="#"+k+" "+sa(o[h]);r=o.join(","),s=$.test(a)&&qa(b.parentNode)||b}if(r)try{return G.apply(d,s.querySelectorAll(r)),d}catch(x){}finally{k===u&&b.removeAttribute("id")}}}return i(a.replace(P,"$1"),b,d,e)}function ha(){var a=[];function b(c,e){return a.push(c+" ")>d.cacheLength&&delete b[a.shift()],b[c+" "]=e}return b}function ia(a){return a[u]=!0,a}function ja(a){var b=n.createElement("fieldset");try{return!!a(b)}catch(c){return!1}finally{b.parentNode&&b.parentNode.removeChild(b),b=null}}function ka(a,b){var c=a.split("|"),e=c.length;while(e--)d.attrHandle[c[e]]=b}function la(a,b){var c=b&&a,d=c&&1===a.nodeType&&1===b.nodeType&&a.sourceIndex-b.sourceIndex;if(d)return d;if(c)while(c=c.nextSibling)if(c===b)return-1;return a?1:-1}function ma(a){return function(b){var c=b.nodeName.toLowerCase();return"input"===c&&b.type===a}}function na(a){return function(b){var c=b.nodeName.toLowerCase();return("input"===c||"button"===c)&&b.type===a}}function oa(a){return function(b){return"form"in b?b.parentNode&&b.disabled===!1?"label"in b?"label"in b.parentNode?b.parentNode.disabled===a:b.disabled===a:b.isDisabled===a||b.isDisabled!==!a&&ea(b)===a:b.disabled===a:"label"in b&&b.disabled===a}}function pa(a){return ia(function(b){return b=+b,ia(function(c,d){var e,f=a([],c.length,b),g=f.length;while(g--)c[e=f[g]]&&(c[e]=!(d[e]=c[e]))})})}function qa(a){return a&&"undefined"!=typeof a.getElementsByTagName&&a}c=ga.support={},f=ga.isXML=function(a){var b=a&&(a.ownerDocument||a).documentElement;return!!b&&"HTML"!==b.nodeName},m=ga.setDocument=function(a){var b,e,g=a?a.ownerDocument||a:v;return g!==n&&9===g.nodeType&&g.documentElement?(n=g,o=n.documentElement,p=!f(n),v!==n&&(e=n.defaultView)&&e.top!==e&&(e.addEventListener?e.addEventListener("unload",da,!1):e.attachEvent&&e.attachEvent("onunload",da)),c.attributes=ja(function(a){return a.className="i",!a.getAttribute("className")}),c.getElementsByTagName=ja(function(a){return a.appendChild(n.createComment("")),!a.getElementsByTagName("*").length}),c.getElementsByClassName=Y.test(n.getElementsByClassName),c.getById=ja(function(a){return o.appendChild(a).id=u,!n.getElementsByName||!n.getElementsByName(u).length}),c.getById?(d.filter.ID=function(a){var b=a.replace(_,aa);return function(a){return a.getAttribute("id")===b}},d.find.ID=function(a,b){if("undefined"!=typeof b.getElementById&&p){var c=b.getElementById(a);return c?[c]:[]}}):(d.filter.ID=function(a){var b=a.replace(_,aa);return function(a){var c="undefined"!=typeof a.getAttributeNode&&a.getAttributeNode("id");return c&&c.value===b}},d.find.ID=function(a,b){if("undefined"!=typeof b.getElementById&&p){var c,d,e,f=b.getElementById(a);if(f){if(c=f.getAttributeNode("id"),c&&c.value===a)return[f];e=b.getElementsByName(a),d=0;while(f=e[d++])if(c=f.getAttributeNode("id"),c&&c.value===a)return[f]}return[]}}),d.find.TAG=c.getElementsByTagName?function(a,b){return"undefined"!=typeof b.getElementsByTagName?b.getElementsByTagName(a):c.qsa?b.querySelectorAll(a):void 0}:function(a,b){var c,d=[],e=0,f=b.getElementsByTagName(a);if("*"===a){while(c=f[e++])1===c.nodeType&&d.push(c);return d}return f},d.find.CLASS=c.getElementsByClassName&&function(a,b){if("undefined"!=typeof b.getElementsByClassName&&p)return b.getElementsByClassName(a)},r=[],q=[],(c.qsa=Y.test(n.querySelectorAll))&&(ja(function(a){o.appendChild(a).innerHTML="",a.querySelectorAll("[msallowcapture^='']").length&&q.push("[*^$]="+K+"*(?:''|\"\")"),a.querySelectorAll("[selected]").length||q.push("\\["+K+"*(?:value|"+J+")"),a.querySelectorAll("[id~="+u+"-]").length||q.push("~="),a.querySelectorAll(":checked").length||q.push(":checked"),a.querySelectorAll("a#"+u+"+*").length||q.push(".#.+[+~]")}),ja(function(a){a.innerHTML="";var b=n.createElement("input");b.setAttribute("type","hidden"),a.appendChild(b).setAttribute("name","D"),a.querySelectorAll("[name=d]").length&&q.push("name"+K+"*[*^$|!~]?="),2!==a.querySelectorAll(":enabled").length&&q.push(":enabled",":disabled"),o.appendChild(a).disabled=!0,2!==a.querySelectorAll(":disabled").length&&q.push(":enabled",":disabled"),a.querySelectorAll("*,:x"),q.push(",.*:")})),(c.matchesSelector=Y.test(s=o.matches||o.webkitMatchesSelector||o.mozMatchesSelector||o.oMatchesSelector||o.msMatchesSelector))&&ja(function(a){c.disconnectedMatch=s.call(a,"*"),s.call(a,"[s!='']:x"),r.push("!=",N)}),q=q.length&&new RegExp(q.join("|")),r=r.length&&new RegExp(r.join("|")),b=Y.test(o.compareDocumentPosition),t=b||Y.test(o.contains)?function(a,b){var c=9===a.nodeType?a.documentElement:a,d=b&&b.parentNode;return a===d||!(!d||1!==d.nodeType||!(c.contains?c.contains(d):a.compareDocumentPosition&&16&a.compareDocumentPosition(d)))}:function(a,b){if(b)while(b=b.parentNode)if(b===a)return!0;return!1},B=b?function(a,b){if(a===b)return l=!0,0;var d=!a.compareDocumentPosition-!b.compareDocumentPosition;return d?d:(d=(a.ownerDocument||a)===(b.ownerDocument||b)?a.compareDocumentPosition(b):1,1&d||!c.sortDetached&&b.compareDocumentPosition(a)===d?a===n||a.ownerDocument===v&&t(v,a)?-1:b===n||b.ownerDocument===v&&t(v,b)?1:k?I(k,a)-I(k,b):0:4&d?-1:1)}:function(a,b){if(a===b)return l=!0,0;var c,d=0,e=a.parentNode,f=b.parentNode,g=[a],h=[b];if(!e||!f)return a===n?-1:b===n?1:e?-1:f?1:k?I(k,a)-I(k,b):0;if(e===f)return la(a,b);c=a;while(c=c.parentNode)g.unshift(c);c=b;while(c=c.parentNode)h.unshift(c);while(g[d]===h[d])d++;return d?la(g[d],h[d]):g[d]===v?-1:h[d]===v?1:0},n):n},ga.matches=function(a,b){return ga(a,null,null,b)},ga.matchesSelector=function(a,b){if((a.ownerDocument||a)!==n&&m(a),b=b.replace(S,"='$1']"),c.matchesSelector&&p&&!A[b+" "]&&(!r||!r.test(b))&&(!q||!q.test(b)))try{var d=s.call(a,b);if(d||c.disconnectedMatch||a.document&&11!==a.document.nodeType)return d}catch(e){}return ga(b,n,null,[a]).length>0},ga.contains=function(a,b){return(a.ownerDocument||a)!==n&&m(a),t(a,b)},ga.attr=function(a,b){(a.ownerDocument||a)!==n&&m(a);var e=d.attrHandle[b.toLowerCase()],f=e&&C.call(d.attrHandle,b.toLowerCase())?e(a,b,!p):void 0;return void 0!==f?f:c.attributes||!p?a.getAttribute(b):(f=a.getAttributeNode(b))&&f.specified?f.value:null},ga.escape=function(a){return(a+"").replace(ba,ca)},ga.error=function(a){throw new Error("Syntax error, unrecognized expression: "+a)},ga.uniqueSort=function(a){var b,d=[],e=0,f=0;if(l=!c.detectDuplicates,k=!c.sortStable&&a.slice(0),a.sort(B),l){while(b=a[f++])b===a[f]&&(e=d.push(f));while(e--)a.splice(d[e],1)}return k=null,a},e=ga.getText=function(a){var b,c="",d=0,f=a.nodeType;if(f){if(1===f||9===f||11===f){if("string"==typeof a.textContent)return a.textContent;for(a=a.firstChild;a;a=a.nextSibling)c+=e(a)}else if(3===f||4===f)return a.nodeValue}else while(b=a[d++])c+=e(b);return c},d=ga.selectors={cacheLength:50,createPseudo:ia,match:V,attrHandle:{},find:{},relative:{">":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(a){return a[1]=a[1].replace(_,aa),a[3]=(a[3]||a[4]||a[5]||"").replace(_,aa),"~="===a[2]&&(a[3]=" "+a[3]+" "),a.slice(0,4)},CHILD:function(a){return a[1]=a[1].toLowerCase(),"nth"===a[1].slice(0,3)?(a[3]||ga.error(a[0]),a[4]=+(a[4]?a[5]+(a[6]||1):2*("even"===a[3]||"odd"===a[3])),a[5]=+(a[7]+a[8]||"odd"===a[3])):a[3]&&ga.error(a[0]),a},PSEUDO:function(a){var b,c=!a[6]&&a[2];return V.CHILD.test(a[0])?null:(a[3]?a[2]=a[4]||a[5]||"":c&&T.test(c)&&(b=g(c,!0))&&(b=c.indexOf(")",c.length-b)-c.length)&&(a[0]=a[0].slice(0,b),a[2]=c.slice(0,b)),a.slice(0,3))}},filter:{TAG:function(a){var b=a.replace(_,aa).toLowerCase();return"*"===a?function(){return!0}:function(a){return a.nodeName&&a.nodeName.toLowerCase()===b}},CLASS:function(a){var b=y[a+" "];return b||(b=new RegExp("(^|"+K+")"+a+"("+K+"|$)"))&&y(a,function(a){return b.test("string"==typeof a.className&&a.className||"undefined"!=typeof a.getAttribute&&a.getAttribute("class")||"")})},ATTR:function(a,b,c){return function(d){var e=ga.attr(d,a);return null==e?"!="===b:!b||(e+="","="===b?e===c:"!="===b?e!==c:"^="===b?c&&0===e.indexOf(c):"*="===b?c&&e.indexOf(c)>-1:"$="===b?c&&e.slice(-c.length)===c:"~="===b?(" "+e.replace(O," ")+" ").indexOf(c)>-1:"|="===b&&(e===c||e.slice(0,c.length+1)===c+"-"))}},CHILD:function(a,b,c,d,e){var f="nth"!==a.slice(0,3),g="last"!==a.slice(-4),h="of-type"===b;return 1===d&&0===e?function(a){return!!a.parentNode}:function(b,c,i){var j,k,l,m,n,o,p=f!==g?"nextSibling":"previousSibling",q=b.parentNode,r=h&&b.nodeName.toLowerCase(),s=!i&&!h,t=!1;if(q){if(f){while(p){m=b;while(m=m[p])if(h?m.nodeName.toLowerCase()===r:1===m.nodeType)return!1;o=p="only"===a&&!o&&"nextSibling"}return!0}if(o=[g?q.firstChild:q.lastChild],g&&s){m=q,l=m[u]||(m[u]={}),k=l[m.uniqueID]||(l[m.uniqueID]={}),j=k[a]||[],n=j[0]===w&&j[1],t=n&&j[2],m=n&&q.childNodes[n];while(m=++n&&m&&m[p]||(t=n=0)||o.pop())if(1===m.nodeType&&++t&&m===b){k[a]=[w,n,t];break}}else if(s&&(m=b,l=m[u]||(m[u]={}),k=l[m.uniqueID]||(l[m.uniqueID]={}),j=k[a]||[],n=j[0]===w&&j[1],t=n),t===!1)while(m=++n&&m&&m[p]||(t=n=0)||o.pop())if((h?m.nodeName.toLowerCase()===r:1===m.nodeType)&&++t&&(s&&(l=m[u]||(m[u]={}),k=l[m.uniqueID]||(l[m.uniqueID]={}),k[a]=[w,t]),m===b))break;return t-=e,t===d||t%d===0&&t/d>=0}}},PSEUDO:function(a,b){var c,e=d.pseudos[a]||d.setFilters[a.toLowerCase()]||ga.error("unsupported pseudo: "+a);return e[u]?e(b):e.length>1?(c=[a,a,"",b],d.setFilters.hasOwnProperty(a.toLowerCase())?ia(function(a,c){var d,f=e(a,b),g=f.length;while(g--)d=I(a,f[g]),a[d]=!(c[d]=f[g])}):function(a){return e(a,0,c)}):e}},pseudos:{not:ia(function(a){var b=[],c=[],d=h(a.replace(P,"$1"));return d[u]?ia(function(a,b,c,e){var f,g=d(a,null,e,[]),h=a.length;while(h--)(f=g[h])&&(a[h]=!(b[h]=f))}):function(a,e,f){return b[0]=a,d(b,null,f,c),b[0]=null,!c.pop()}}),has:ia(function(a){return function(b){return ga(a,b).length>0}}),contains:ia(function(a){return a=a.replace(_,aa),function(b){return(b.textContent||b.innerText||e(b)).indexOf(a)>-1}}),lang:ia(function(a){return U.test(a||"")||ga.error("unsupported lang: "+a),a=a.replace(_,aa).toLowerCase(),function(b){var c;do if(c=p?b.lang:b.getAttribute("xml:lang")||b.getAttribute("lang"))return c=c.toLowerCase(),c===a||0===c.indexOf(a+"-");while((b=b.parentNode)&&1===b.nodeType);return!1}}),target:function(b){var c=a.location&&a.location.hash;return c&&c.slice(1)===b.id},root:function(a){return a===o},focus:function(a){return a===n.activeElement&&(!n.hasFocus||n.hasFocus())&&!!(a.type||a.href||~a.tabIndex)},enabled:oa(!1),disabled:oa(!0),checked:function(a){var b=a.nodeName.toLowerCase();return"input"===b&&!!a.checked||"option"===b&&!!a.selected},selected:function(a){return a.parentNode&&a.parentNode.selectedIndex,a.selected===!0},empty:function(a){for(a=a.firstChild;a;a=a.nextSibling)if(a.nodeType<6)return!1;return!0},parent:function(a){return!d.pseudos.empty(a)},header:function(a){return X.test(a.nodeName)},input:function(a){return W.test(a.nodeName)},button:function(a){var b=a.nodeName.toLowerCase();return"input"===b&&"button"===a.type||"button"===b},text:function(a){var b;return"input"===a.nodeName.toLowerCase()&&"text"===a.type&&(null==(b=a.getAttribute("type"))||"text"===b.toLowerCase())},first:pa(function(){return[0]}),last:pa(function(a,b){return[b-1]}),eq:pa(function(a,b,c){return[c<0?c+b:c]}),even:pa(function(a,b){for(var c=0;c=0;)a.push(d);return a}),gt:pa(function(a,b,c){for(var d=c<0?c+b:c;++d1?function(b,c,d){var e=a.length;while(e--)if(!a[e](b,c,d))return!1;return!0}:a[0]}function va(a,b,c){for(var d=0,e=b.length;d-1&&(f[j]=!(g[j]=l))}}else r=wa(r===g?r.splice(o,r.length):r),e?e(null,g,r,i):G.apply(g,r)})}function ya(a){for(var b,c,e,f=a.length,g=d.relative[a[0].type],h=g||d.relative[" "],i=g?1:0,k=ta(function(a){return a===b},h,!0),l=ta(function(a){return I(b,a)>-1},h,!0),m=[function(a,c,d){var e=!g&&(d||c!==j)||((b=c).nodeType?k(a,c,d):l(a,c,d));return b=null,e}];i1&&ua(m),i>1&&sa(a.slice(0,i-1).concat({value:" "===a[i-2].type?"*":""})).replace(P,"$1"),c,i0,e=a.length>0,f=function(f,g,h,i,k){var l,o,q,r=0,s="0",t=f&&[],u=[],v=j,x=f||e&&d.find.TAG("*",k),y=w+=null==v?1:Math.random()||.1,z=x.length;for(k&&(j=g===n||g||k);s!==z&&null!=(l=x[s]);s++){if(e&&l){o=0,g||l.ownerDocument===n||(m(l),h=!p);while(q=a[o++])if(q(l,g||n,h)){i.push(l);break}k&&(w=y)}c&&((l=!q&&l)&&r--,f&&t.push(l))}if(r+=s,c&&s!==r){o=0;while(q=b[o++])q(t,u,g,h);if(f){if(r>0)while(s--)t[s]||u[s]||(u[s]=E.call(i));u=wa(u)}G.apply(i,u),k&&!f&&u.length>0&&r+b.length>1&&ga.uniqueSort(i)}return k&&(w=y,j=v),t};return c?ia(f):f}return h=ga.compile=function(a,b){var c,d=[],e=[],f=A[a+" "];if(!f){b||(b=g(a)),c=b.length;while(c--)f=ya(b[c]),f[u]?d.push(f):e.push(f);f=A(a,za(e,d)),f.selector=a}return f},i=ga.select=function(a,b,c,e){var f,i,j,k,l,m="function"==typeof a&&a,n=!e&&g(a=m.selector||a);if(c=c||[],1===n.length){if(i=n[0]=n[0].slice(0),i.length>2&&"ID"===(j=i[0]).type&&9===b.nodeType&&p&&d.relative[i[1].type]){if(b=(d.find.ID(j.matches[0].replace(_,aa),b)||[])[0],!b)return c;m&&(b=b.parentNode),a=a.slice(i.shift().value.length)}f=V.needsContext.test(a)?0:i.length;while(f--){if(j=i[f],d.relative[k=j.type])break;if((l=d.find[k])&&(e=l(j.matches[0].replace(_,aa),$.test(i[0].type)&&qa(b.parentNode)||b))){if(i.splice(f,1),a=e.length&&sa(i),!a)return G.apply(c,e),c;break}}}return(m||h(a,n))(e,b,!p,c,!b||$.test(a)&&qa(b.parentNode)||b),c},c.sortStable=u.split("").sort(B).join("")===u,c.detectDuplicates=!!l,m(),c.sortDetached=ja(function(a){return 1&a.compareDocumentPosition(n.createElement("fieldset"))}),ja(function(a){return a.innerHTML="","#"===a.firstChild.getAttribute("href")})||ka("type|href|height|width",function(a,b,c){if(!c)return a.getAttribute(b,"type"===b.toLowerCase()?1:2)}),c.attributes&&ja(function(a){return a.innerHTML="",a.firstChild.setAttribute("value",""),""===a.firstChild.getAttribute("value")})||ka("value",function(a,b,c){if(!c&&"input"===a.nodeName.toLowerCase())return a.defaultValue}),ja(function(a){return null==a.getAttribute("disabled")})||ka(J,function(a,b,c){var d;if(!c)return a[b]===!0?b.toLowerCase():(d=a.getAttributeNode(b))&&d.specified?d.value:null}),ga}(a);r.find=x,r.expr=x.selectors,r.expr[":"]=r.expr.pseudos,r.uniqueSort=r.unique=x.uniqueSort,r.text=x.getText,r.isXMLDoc=x.isXML,r.contains=x.contains,r.escapeSelector=x.escape;var y=function(a,b,c){var d=[],e=void 0!==c;while((a=a[b])&&9!==a.nodeType)if(1===a.nodeType){if(e&&r(a).is(c))break;d.push(a)}return d},z=function(a,b){for(var c=[];a;a=a.nextSibling)1===a.nodeType&&a!==b&&c.push(a);return c},A=r.expr.match.needsContext;function B(a,b){return a.nodeName&&a.nodeName.toLowerCase()===b.toLowerCase()}var C=/^<([a-z][^\/\0>:\x20\t\r\n\f]*)[\x20\t\r\n\f]*\/?>(?:<\/\1>|)$/i,D=/^.[^:#\[\.,]*$/;function E(a,b,c){return r.isFunction(b)?r.grep(a,function(a,d){return!!b.call(a,d,a)!==c}):b.nodeType?r.grep(a,function(a){return a===b!==c}):"string"!=typeof b?r.grep(a,function(a){return i.call(b,a)>-1!==c}):D.test(b)?r.filter(b,a,c):(b=r.filter(b,a),r.grep(a,function(a){return i.call(b,a)>-1!==c&&1===a.nodeType}))}r.filter=function(a,b,c){var d=b[0];return c&&(a=":not("+a+")"),1===b.length&&1===d.nodeType?r.find.matchesSelector(d,a)?[d]:[]:r.find.matches(a,r.grep(b,function(a){return 1===a.nodeType}))},r.fn.extend({find:function(a){var b,c,d=this.length,e=this;if("string"!=typeof a)return this.pushStack(r(a).filter(function(){for(b=0;b1?r.uniqueSort(c):c},filter:function(a){return this.pushStack(E(this,a||[],!1))},not:function(a){return this.pushStack(E(this,a||[],!0))},is:function(a){return!!E(this,"string"==typeof a&&A.test(a)?r(a):a||[],!1).length}});var F,G=/^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]+))$/,H=r.fn.init=function(a,b,c){var e,f;if(!a)return this;if(c=c||F,"string"==typeof a){if(e="<"===a[0]&&">"===a[a.length-1]&&a.length>=3?[null,a,null]:G.exec(a),!e||!e[1]&&b)return!b||b.jquery?(b||c).find(a):this.constructor(b).find(a);if(e[1]){if(b=b instanceof r?b[0]:b,r.merge(this,r.parseHTML(e[1],b&&b.nodeType?b.ownerDocument||b:d,!0)),C.test(e[1])&&r.isPlainObject(b))for(e in b)r.isFunction(this[e])?this[e](b[e]):this.attr(e,b[e]);return this}return f=d.getElementById(e[2]),f&&(this[0]=f,this.length=1),this}return a.nodeType?(this[0]=a,this.length=1,this):r.isFunction(a)?void 0!==c.ready?c.ready(a):a(r):r.makeArray(a,this)};H.prototype=r.fn,F=r(d);var I=/^(?:parents|prev(?:Until|All))/,J={children:!0,contents:!0,next:!0,prev:!0};r.fn.extend({has:function(a){var b=r(a,this),c=b.length;return this.filter(function(){for(var a=0;a-1:1===c.nodeType&&r.find.matchesSelector(c,a))){f.push(c);break}return this.pushStack(f.length>1?r.uniqueSort(f):f)},index:function(a){return a?"string"==typeof a?i.call(r(a),this[0]):i.call(this,a.jquery?a[0]:a):this[0]&&this[0].parentNode?this.first().prevAll().length:-1},add:function(a,b){return this.pushStack(r.uniqueSort(r.merge(this.get(),r(a,b))))},addBack:function(a){return this.add(null==a?this.prevObject:this.prevObject.filter(a))}});function K(a,b){while((a=a[b])&&1!==a.nodeType);return a}r.each({parent:function(a){var b=a.parentNode;return b&&11!==b.nodeType?b:null},parents:function(a){return y(a,"parentNode")},parentsUntil:function(a,b,c){return y(a,"parentNode",c)},next:function(a){return K(a,"nextSibling")},prev:function(a){return K(a,"previousSibling")},nextAll:function(a){return y(a,"nextSibling")},prevAll:function(a){return y(a,"previousSibling")},nextUntil:function(a,b,c){return y(a,"nextSibling",c)},prevUntil:function(a,b,c){return y(a,"previousSibling",c)},siblings:function(a){return z((a.parentNode||{}).firstChild,a)},children:function(a){return z(a.firstChild)},contents:function(a){return B(a,"iframe")?a.contentDocument:(B(a,"template")&&(a=a.content||a),r.merge([],a.childNodes))}},function(a,b){r.fn[a]=function(c,d){var e=r.map(this,b,c);return"Until"!==a.slice(-5)&&(d=c),d&&"string"==typeof d&&(e=r.filter(d,e)),this.length>1&&(J[a]||r.uniqueSort(e),I.test(a)&&e.reverse()),this.pushStack(e)}});var L=/[^\x20\t\r\n\f]+/g;function M(a){var b={};return r.each(a.match(L)||[],function(a,c){b[c]=!0}),b}r.Callbacks=function(a){a="string"==typeof a?M(a):r.extend({},a);var b,c,d,e,f=[],g=[],h=-1,i=function(){for(e=e||a.once,d=b=!0;g.length;h=-1){c=g.shift();while(++h-1)f.splice(c,1),c<=h&&h--}),this},has:function(a){return a?r.inArray(a,f)>-1:f.length>0},empty:function(){return f&&(f=[]),this},disable:function(){return e=g=[],f=c="",this},disabled:function(){return!f},lock:function(){return e=g=[],c||b||(f=c=""),this},locked:function(){return!!e},fireWith:function(a,c){return e||(c=c||[],c=[a,c.slice?c.slice():c],g.push(c),b||i()),this},fire:function(){return j.fireWith(this,arguments),this},fired:function(){return!!d}};return j};function N(a){return a}function O(a){throw a}function P(a,b,c,d){var e;try{a&&r.isFunction(e=a.promise)?e.call(a).done(b).fail(c):a&&r.isFunction(e=a.then)?e.call(a,b,c):b.apply(void 0,[a].slice(d))}catch(a){c.apply(void 0,[a])}}r.extend({Deferred:function(b){var c=[["notify","progress",r.Callbacks("memory"),r.Callbacks("memory"),2],["resolve","done",r.Callbacks("once memory"),r.Callbacks("once memory"),0,"resolved"],["reject","fail",r.Callbacks("once memory"),r.Callbacks("once memory"),1,"rejected"]],d="pending",e={state:function(){return d},always:function(){return f.done(arguments).fail(arguments),this},"catch":function(a){return e.then(null,a)},pipe:function(){var a=arguments;return r.Deferred(function(b){r.each(c,function(c,d){var e=r.isFunction(a[d[4]])&&a[d[4]];f[d[1]](function(){var a=e&&e.apply(this,arguments);a&&r.isFunction(a.promise)?a.promise().progress(b.notify).done(b.resolve).fail(b.reject):b[d[0]+"With"](this,e?[a]:arguments)})}),a=null}).promise()},then:function(b,d,e){var f=0;function g(b,c,d,e){return function(){var h=this,i=arguments,j=function(){var a,j;if(!(b=f&&(d!==O&&(h=void 0,i=[a]),c.rejectWith(h,i))}};b?k():(r.Deferred.getStackHook&&(k.stackTrace=r.Deferred.getStackHook()),a.setTimeout(k))}}return r.Deferred(function(a){c[0][3].add(g(0,a,r.isFunction(e)?e:N,a.notifyWith)),c[1][3].add(g(0,a,r.isFunction(b)?b:N)),c[2][3].add(g(0,a,r.isFunction(d)?d:O))}).promise()},promise:function(a){return null!=a?r.extend(a,e):e}},f={};return r.each(c,function(a,b){var g=b[2],h=b[5];e[b[1]]=g.add,h&&g.add(function(){d=h},c[3-a][2].disable,c[0][2].lock),g.add(b[3].fire),f[b[0]]=function(){return f[b[0]+"With"](this===f?void 0:this,arguments),this},f[b[0]+"With"]=g.fireWith}),e.promise(f),b&&b.call(f,f),f},when:function(a){var b=arguments.length,c=b,d=Array(c),e=f.call(arguments),g=r.Deferred(),h=function(a){return function(c){d[a]=this,e[a]=arguments.length>1?f.call(arguments):c,--b||g.resolveWith(d,e)}};if(b<=1&&(P(a,g.done(h(c)).resolve,g.reject,!b),"pending"===g.state()||r.isFunction(e[c]&&e[c].then)))return g.then();while(c--)P(e[c],h(c),g.reject);return g.promise()}});var Q=/^(Eval|Internal|Range|Reference|Syntax|Type|URI)Error$/;r.Deferred.exceptionHook=function(b,c){a.console&&a.console.warn&&b&&Q.test(b.name)&&a.console.warn("jQuery.Deferred exception: "+b.message,b.stack,c)},r.readyException=function(b){a.setTimeout(function(){throw b})};var R=r.Deferred();r.fn.ready=function(a){return R.then(a)["catch"](function(a){r.readyException(a)}),this},r.extend({isReady:!1,readyWait:1,ready:function(a){(a===!0?--r.readyWait:r.isReady)||(r.isReady=!0,a!==!0&&--r.readyWait>0||R.resolveWith(d,[r]))}}),r.ready.then=R.then;function S(){d.removeEventListener("DOMContentLoaded",S), 3 | a.removeEventListener("load",S),r.ready()}"complete"===d.readyState||"loading"!==d.readyState&&!d.documentElement.doScroll?a.setTimeout(r.ready):(d.addEventListener("DOMContentLoaded",S),a.addEventListener("load",S));var T=function(a,b,c,d,e,f,g){var h=0,i=a.length,j=null==c;if("object"===r.type(c)){e=!0;for(h in c)T(a,b,h,c[h],!0,f,g)}else if(void 0!==d&&(e=!0,r.isFunction(d)||(g=!0),j&&(g?(b.call(a,d),b=null):(j=b,b=function(a,b,c){return j.call(r(a),c)})),b))for(;h1,null,!0)},removeData:function(a){return this.each(function(){X.remove(this,a)})}}),r.extend({queue:function(a,b,c){var d;if(a)return b=(b||"fx")+"queue",d=W.get(a,b),c&&(!d||Array.isArray(c)?d=W.access(a,b,r.makeArray(c)):d.push(c)),d||[]},dequeue:function(a,b){b=b||"fx";var c=r.queue(a,b),d=c.length,e=c.shift(),f=r._queueHooks(a,b),g=function(){r.dequeue(a,b)};"inprogress"===e&&(e=c.shift(),d--),e&&("fx"===b&&c.unshift("inprogress"),delete f.stop,e.call(a,g,f)),!d&&f&&f.empty.fire()},_queueHooks:function(a,b){var c=b+"queueHooks";return W.get(a,c)||W.access(a,c,{empty:r.Callbacks("once memory").add(function(){W.remove(a,[b+"queue",c])})})}}),r.fn.extend({queue:function(a,b){var c=2;return"string"!=typeof a&&(b=a,a="fx",c--),arguments.length\x20\t\r\n\f]+)/i,la=/^$|\/(?:java|ecma)script/i,ma={option:[1,""],thead:[1,"","
"],col:[2,"","
"],tr:[2,"","
"],td:[3,"","
"],_default:[0,"",""]};ma.optgroup=ma.option,ma.tbody=ma.tfoot=ma.colgroup=ma.caption=ma.thead,ma.th=ma.td;function na(a,b){var c;return c="undefined"!=typeof a.getElementsByTagName?a.getElementsByTagName(b||"*"):"undefined"!=typeof a.querySelectorAll?a.querySelectorAll(b||"*"):[],void 0===b||b&&B(a,b)?r.merge([a],c):c}function oa(a,b){for(var c=0,d=a.length;c-1)e&&e.push(f);else if(j=r.contains(f.ownerDocument,f),g=na(l.appendChild(f),"script"),j&&oa(g),c){k=0;while(f=g[k++])la.test(f.type||"")&&c.push(f)}return l}!function(){var a=d.createDocumentFragment(),b=a.appendChild(d.createElement("div")),c=d.createElement("input");c.setAttribute("type","radio"),c.setAttribute("checked","checked"),c.setAttribute("name","t"),b.appendChild(c),o.checkClone=b.cloneNode(!0).cloneNode(!0).lastChild.checked,b.innerHTML="",o.noCloneChecked=!!b.cloneNode(!0).lastChild.defaultValue}();var ra=d.documentElement,sa=/^key/,ta=/^(?:mouse|pointer|contextmenu|drag|drop)|click/,ua=/^([^.]*)(?:\.(.+)|)/;function va(){return!0}function wa(){return!1}function xa(){try{return d.activeElement}catch(a){}}function ya(a,b,c,d,e,f){var g,h;if("object"==typeof b){"string"!=typeof c&&(d=d||c,c=void 0);for(h in b)ya(a,h,c,d,b[h],f);return a}if(null==d&&null==e?(e=c,d=c=void 0):null==e&&("string"==typeof c?(e=d,d=void 0):(e=d,d=c,c=void 0)),e===!1)e=wa;else if(!e)return a;return 1===f&&(g=e,e=function(a){return r().off(a),g.apply(this,arguments)},e.guid=g.guid||(g.guid=r.guid++)),a.each(function(){r.event.add(this,b,e,d,c)})}r.event={global:{},add:function(a,b,c,d,e){var f,g,h,i,j,k,l,m,n,o,p,q=W.get(a);if(q){c.handler&&(f=c,c=f.handler,e=f.selector),e&&r.find.matchesSelector(ra,e),c.guid||(c.guid=r.guid++),(i=q.events)||(i=q.events={}),(g=q.handle)||(g=q.handle=function(b){return"undefined"!=typeof r&&r.event.triggered!==b.type?r.event.dispatch.apply(a,arguments):void 0}),b=(b||"").match(L)||[""],j=b.length;while(j--)h=ua.exec(b[j])||[],n=p=h[1],o=(h[2]||"").split(".").sort(),n&&(l=r.event.special[n]||{},n=(e?l.delegateType:l.bindType)||n,l=r.event.special[n]||{},k=r.extend({type:n,origType:p,data:d,handler:c,guid:c.guid,selector:e,needsContext:e&&r.expr.match.needsContext.test(e),namespace:o.join(".")},f),(m=i[n])||(m=i[n]=[],m.delegateCount=0,l.setup&&l.setup.call(a,d,o,g)!==!1||a.addEventListener&&a.addEventListener(n,g)),l.add&&(l.add.call(a,k),k.handler.guid||(k.handler.guid=c.guid)),e?m.splice(m.delegateCount++,0,k):m.push(k),r.event.global[n]=!0)}},remove:function(a,b,c,d,e){var f,g,h,i,j,k,l,m,n,o,p,q=W.hasData(a)&&W.get(a);if(q&&(i=q.events)){b=(b||"").match(L)||[""],j=b.length;while(j--)if(h=ua.exec(b[j])||[],n=p=h[1],o=(h[2]||"").split(".").sort(),n){l=r.event.special[n]||{},n=(d?l.delegateType:l.bindType)||n,m=i[n]||[],h=h[2]&&new RegExp("(^|\\.)"+o.join("\\.(?:.*\\.|)")+"(\\.|$)"),g=f=m.length;while(f--)k=m[f],!e&&p!==k.origType||c&&c.guid!==k.guid||h&&!h.test(k.namespace)||d&&d!==k.selector&&("**"!==d||!k.selector)||(m.splice(f,1),k.selector&&m.delegateCount--,l.remove&&l.remove.call(a,k));g&&!m.length&&(l.teardown&&l.teardown.call(a,o,q.handle)!==!1||r.removeEvent(a,n,q.handle),delete i[n])}else for(n in i)r.event.remove(a,n+b[j],c,d,!0);r.isEmptyObject(i)&&W.remove(a,"handle events")}},dispatch:function(a){var b=r.event.fix(a),c,d,e,f,g,h,i=new Array(arguments.length),j=(W.get(this,"events")||{})[b.type]||[],k=r.event.special[b.type]||{};for(i[0]=b,c=1;c=1))for(;j!==this;j=j.parentNode||this)if(1===j.nodeType&&("click"!==a.type||j.disabled!==!0)){for(f=[],g={},c=0;c-1:r.find(e,this,null,[j]).length),g[e]&&f.push(d);f.length&&h.push({elem:j,handlers:f})}return j=this,i\x20\t\r\n\f]*)[^>]*)\/>/gi,Aa=/\s*$/g;function Ea(a,b){return B(a,"table")&&B(11!==b.nodeType?b:b.firstChild,"tr")?r(">tbody",a)[0]||a:a}function Fa(a){return a.type=(null!==a.getAttribute("type"))+"/"+a.type,a}function Ga(a){var b=Ca.exec(a.type);return b?a.type=b[1]:a.removeAttribute("type"),a}function Ha(a,b){var c,d,e,f,g,h,i,j;if(1===b.nodeType){if(W.hasData(a)&&(f=W.access(a),g=W.set(b,f),j=f.events)){delete g.handle,g.events={};for(e in j)for(c=0,d=j[e].length;c1&&"string"==typeof q&&!o.checkClone&&Ba.test(q))return a.each(function(e){var f=a.eq(e);s&&(b[0]=q.call(this,e,f.html())),Ja(f,b,c,d)});if(m&&(e=qa(b,a[0].ownerDocument,!1,a,d),f=e.firstChild,1===e.childNodes.length&&(e=f),f||d)){for(h=r.map(na(e,"script"),Fa),i=h.length;l")},clone:function(a,b,c){var d,e,f,g,h=a.cloneNode(!0),i=r.contains(a.ownerDocument,a);if(!(o.noCloneChecked||1!==a.nodeType&&11!==a.nodeType||r.isXMLDoc(a)))for(g=na(h),f=na(a),d=0,e=f.length;d0&&oa(g,!i&&na(a,"script")),h},cleanData:function(a){for(var b,c,d,e=r.event.special,f=0;void 0!==(c=a[f]);f++)if(U(c)){if(b=c[W.expando]){if(b.events)for(d in b.events)e[d]?r.event.remove(c,d):r.removeEvent(c,d,b.handle);c[W.expando]=void 0}c[X.expando]&&(c[X.expando]=void 0)}}}),r.fn.extend({detach:function(a){return Ka(this,a,!0)},remove:function(a){return Ka(this,a)},text:function(a){return T(this,function(a){return void 0===a?r.text(this):this.empty().each(function(){1!==this.nodeType&&11!==this.nodeType&&9!==this.nodeType||(this.textContent=a)})},null,a,arguments.length)},append:function(){return Ja(this,arguments,function(a){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var b=Ea(this,a);b.appendChild(a)}})},prepend:function(){return Ja(this,arguments,function(a){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var b=Ea(this,a);b.insertBefore(a,b.firstChild)}})},before:function(){return Ja(this,arguments,function(a){this.parentNode&&this.parentNode.insertBefore(a,this)})},after:function(){return Ja(this,arguments,function(a){this.parentNode&&this.parentNode.insertBefore(a,this.nextSibling)})},empty:function(){for(var a,b=0;null!=(a=this[b]);b++)1===a.nodeType&&(r.cleanData(na(a,!1)),a.textContent="");return this},clone:function(a,b){return a=null!=a&&a,b=null==b?a:b,this.map(function(){return r.clone(this,a,b)})},html:function(a){return T(this,function(a){var b=this[0]||{},c=0,d=this.length;if(void 0===a&&1===b.nodeType)return b.innerHTML;if("string"==typeof a&&!Aa.test(a)&&!ma[(ka.exec(a)||["",""])[1].toLowerCase()]){a=r.htmlPrefilter(a);try{for(;c1)}});function _a(a,b,c,d,e){return new _a.prototype.init(a,b,c,d,e)}r.Tween=_a,_a.prototype={constructor:_a,init:function(a,b,c,d,e,f){this.elem=a,this.prop=c,this.easing=e||r.easing._default,this.options=b,this.start=this.now=this.cur(),this.end=d,this.unit=f||(r.cssNumber[c]?"":"px")},cur:function(){var a=_a.propHooks[this.prop];return a&&a.get?a.get(this):_a.propHooks._default.get(this)},run:function(a){var b,c=_a.propHooks[this.prop];return this.options.duration?this.pos=b=r.easing[this.easing](a,this.options.duration*a,0,1,this.options.duration):this.pos=b=a,this.now=(this.end-this.start)*b+this.start,this.options.step&&this.options.step.call(this.elem,this.now,this),c&&c.set?c.set(this):_a.propHooks._default.set(this),this}},_a.prototype.init.prototype=_a.prototype,_a.propHooks={_default:{get:function(a){var b;return 1!==a.elem.nodeType||null!=a.elem[a.prop]&&null==a.elem.style[a.prop]?a.elem[a.prop]:(b=r.css(a.elem,a.prop,""),b&&"auto"!==b?b:0)},set:function(a){r.fx.step[a.prop]?r.fx.step[a.prop](a):1!==a.elem.nodeType||null==a.elem.style[r.cssProps[a.prop]]&&!r.cssHooks[a.prop]?a.elem[a.prop]=a.now:r.style(a.elem,a.prop,a.now+a.unit)}}},_a.propHooks.scrollTop=_a.propHooks.scrollLeft={set:function(a){a.elem.nodeType&&a.elem.parentNode&&(a.elem[a.prop]=a.now)}},r.easing={linear:function(a){return a},swing:function(a){return.5-Math.cos(a*Math.PI)/2},_default:"swing"},r.fx=_a.prototype.init,r.fx.step={};var ab,bb,cb=/^(?:toggle|show|hide)$/,db=/queueHooks$/;function eb(){bb&&(d.hidden===!1&&a.requestAnimationFrame?a.requestAnimationFrame(eb):a.setTimeout(eb,r.fx.interval),r.fx.tick())}function fb(){return a.setTimeout(function(){ab=void 0}),ab=r.now()}function gb(a,b){var c,d=0,e={height:a};for(b=b?1:0;d<4;d+=2-b)c=ca[d],e["margin"+c]=e["padding"+c]=a;return b&&(e.opacity=e.width=a),e}function hb(a,b,c){for(var d,e=(kb.tweeners[b]||[]).concat(kb.tweeners["*"]),f=0,g=e.length;f1)},removeAttr:function(a){return this.each(function(){r.removeAttr(this,a)})}}),r.extend({attr:function(a,b,c){var d,e,f=a.nodeType;if(3!==f&&8!==f&&2!==f)return"undefined"==typeof a.getAttribute?r.prop(a,b,c):(1===f&&r.isXMLDoc(a)||(e=r.attrHooks[b.toLowerCase()]||(r.expr.match.bool.test(b)?lb:void 0)),void 0!==c?null===c?void r.removeAttr(a,b):e&&"set"in e&&void 0!==(d=e.set(a,c,b))?d:(a.setAttribute(b,c+""),c):e&&"get"in e&&null!==(d=e.get(a,b))?d:(d=r.find.attr(a,b),null==d?void 0:d)); 4 | },attrHooks:{type:{set:function(a,b){if(!o.radioValue&&"radio"===b&&B(a,"input")){var c=a.value;return a.setAttribute("type",b),c&&(a.value=c),b}}}},removeAttr:function(a,b){var c,d=0,e=b&&b.match(L);if(e&&1===a.nodeType)while(c=e[d++])a.removeAttribute(c)}}),lb={set:function(a,b,c){return b===!1?r.removeAttr(a,c):a.setAttribute(c,c),c}},r.each(r.expr.match.bool.source.match(/\w+/g),function(a,b){var c=mb[b]||r.find.attr;mb[b]=function(a,b,d){var e,f,g=b.toLowerCase();return d||(f=mb[g],mb[g]=e,e=null!=c(a,b,d)?g:null,mb[g]=f),e}});var nb=/^(?:input|select|textarea|button)$/i,ob=/^(?:a|area)$/i;r.fn.extend({prop:function(a,b){return T(this,r.prop,a,b,arguments.length>1)},removeProp:function(a){return this.each(function(){delete this[r.propFix[a]||a]})}}),r.extend({prop:function(a,b,c){var d,e,f=a.nodeType;if(3!==f&&8!==f&&2!==f)return 1===f&&r.isXMLDoc(a)||(b=r.propFix[b]||b,e=r.propHooks[b]),void 0!==c?e&&"set"in e&&void 0!==(d=e.set(a,c,b))?d:a[b]=c:e&&"get"in e&&null!==(d=e.get(a,b))?d:a[b]},propHooks:{tabIndex:{get:function(a){var b=r.find.attr(a,"tabindex");return b?parseInt(b,10):nb.test(a.nodeName)||ob.test(a.nodeName)&&a.href?0:-1}}},propFix:{"for":"htmlFor","class":"className"}}),o.optSelected||(r.propHooks.selected={get:function(a){var b=a.parentNode;return b&&b.parentNode&&b.parentNode.selectedIndex,null},set:function(a){var b=a.parentNode;b&&(b.selectedIndex,b.parentNode&&b.parentNode.selectedIndex)}}),r.each(["tabIndex","readOnly","maxLength","cellSpacing","cellPadding","rowSpan","colSpan","useMap","frameBorder","contentEditable"],function(){r.propFix[this.toLowerCase()]=this});function pb(a){var b=a.match(L)||[];return b.join(" ")}function qb(a){return a.getAttribute&&a.getAttribute("class")||""}r.fn.extend({addClass:function(a){var b,c,d,e,f,g,h,i=0;if(r.isFunction(a))return this.each(function(b){r(this).addClass(a.call(this,b,qb(this)))});if("string"==typeof a&&a){b=a.match(L)||[];while(c=this[i++])if(e=qb(c),d=1===c.nodeType&&" "+pb(e)+" "){g=0;while(f=b[g++])d.indexOf(" "+f+" ")<0&&(d+=f+" ");h=pb(d),e!==h&&c.setAttribute("class",h)}}return this},removeClass:function(a){var b,c,d,e,f,g,h,i=0;if(r.isFunction(a))return this.each(function(b){r(this).removeClass(a.call(this,b,qb(this)))});if(!arguments.length)return this.attr("class","");if("string"==typeof a&&a){b=a.match(L)||[];while(c=this[i++])if(e=qb(c),d=1===c.nodeType&&" "+pb(e)+" "){g=0;while(f=b[g++])while(d.indexOf(" "+f+" ")>-1)d=d.replace(" "+f+" "," ");h=pb(d),e!==h&&c.setAttribute("class",h)}}return this},toggleClass:function(a,b){var c=typeof a;return"boolean"==typeof b&&"string"===c?b?this.addClass(a):this.removeClass(a):r.isFunction(a)?this.each(function(c){r(this).toggleClass(a.call(this,c,qb(this),b),b)}):this.each(function(){var b,d,e,f;if("string"===c){d=0,e=r(this),f=a.match(L)||[];while(b=f[d++])e.hasClass(b)?e.removeClass(b):e.addClass(b)}else void 0!==a&&"boolean"!==c||(b=qb(this),b&&W.set(this,"__className__",b),this.setAttribute&&this.setAttribute("class",b||a===!1?"":W.get(this,"__className__")||""))})},hasClass:function(a){var b,c,d=0;b=" "+a+" ";while(c=this[d++])if(1===c.nodeType&&(" "+pb(qb(c))+" ").indexOf(b)>-1)return!0;return!1}});var rb=/\r/g;r.fn.extend({val:function(a){var b,c,d,e=this[0];{if(arguments.length)return d=r.isFunction(a),this.each(function(c){var e;1===this.nodeType&&(e=d?a.call(this,c,r(this).val()):a,null==e?e="":"number"==typeof e?e+="":Array.isArray(e)&&(e=r.map(e,function(a){return null==a?"":a+""})),b=r.valHooks[this.type]||r.valHooks[this.nodeName.toLowerCase()],b&&"set"in b&&void 0!==b.set(this,e,"value")||(this.value=e))});if(e)return b=r.valHooks[e.type]||r.valHooks[e.nodeName.toLowerCase()],b&&"get"in b&&void 0!==(c=b.get(e,"value"))?c:(c=e.value,"string"==typeof c?c.replace(rb,""):null==c?"":c)}}}),r.extend({valHooks:{option:{get:function(a){var b=r.find.attr(a,"value");return null!=b?b:pb(r.text(a))}},select:{get:function(a){var b,c,d,e=a.options,f=a.selectedIndex,g="select-one"===a.type,h=g?null:[],i=g?f+1:e.length;for(d=f<0?i:g?f:0;d-1)&&(c=!0);return c||(a.selectedIndex=-1),f}}}}),r.each(["radio","checkbox"],function(){r.valHooks[this]={set:function(a,b){if(Array.isArray(b))return a.checked=r.inArray(r(a).val(),b)>-1}},o.checkOn||(r.valHooks[this].get=function(a){return null===a.getAttribute("value")?"on":a.value})});var sb=/^(?:focusinfocus|focusoutblur)$/;r.extend(r.event,{trigger:function(b,c,e,f){var g,h,i,j,k,m,n,o=[e||d],p=l.call(b,"type")?b.type:b,q=l.call(b,"namespace")?b.namespace.split("."):[];if(h=i=e=e||d,3!==e.nodeType&&8!==e.nodeType&&!sb.test(p+r.event.triggered)&&(p.indexOf(".")>-1&&(q=p.split("."),p=q.shift(),q.sort()),k=p.indexOf(":")<0&&"on"+p,b=b[r.expando]?b:new r.Event(p,"object"==typeof b&&b),b.isTrigger=f?2:3,b.namespace=q.join("."),b.rnamespace=b.namespace?new RegExp("(^|\\.)"+q.join("\\.(?:.*\\.|)")+"(\\.|$)"):null,b.result=void 0,b.target||(b.target=e),c=null==c?[b]:r.makeArray(c,[b]),n=r.event.special[p]||{},f||!n.trigger||n.trigger.apply(e,c)!==!1)){if(!f&&!n.noBubble&&!r.isWindow(e)){for(j=n.delegateType||p,sb.test(j+p)||(h=h.parentNode);h;h=h.parentNode)o.push(h),i=h;i===(e.ownerDocument||d)&&o.push(i.defaultView||i.parentWindow||a)}g=0;while((h=o[g++])&&!b.isPropagationStopped())b.type=g>1?j:n.bindType||p,m=(W.get(h,"events")||{})[b.type]&&W.get(h,"handle"),m&&m.apply(h,c),m=k&&h[k],m&&m.apply&&U(h)&&(b.result=m.apply(h,c),b.result===!1&&b.preventDefault());return b.type=p,f||b.isDefaultPrevented()||n._default&&n._default.apply(o.pop(),c)!==!1||!U(e)||k&&r.isFunction(e[p])&&!r.isWindow(e)&&(i=e[k],i&&(e[k]=null),r.event.triggered=p,e[p](),r.event.triggered=void 0,i&&(e[k]=i)),b.result}},simulate:function(a,b,c){var d=r.extend(new r.Event,c,{type:a,isSimulated:!0});r.event.trigger(d,null,b)}}),r.fn.extend({trigger:function(a,b){return this.each(function(){r.event.trigger(a,b,this)})},triggerHandler:function(a,b){var c=this[0];if(c)return r.event.trigger(a,b,c,!0)}}),r.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(a,b){r.fn[b]=function(a,c){return arguments.length>0?this.on(b,null,a,c):this.trigger(b)}}),r.fn.extend({hover:function(a,b){return this.mouseenter(a).mouseleave(b||a)}}),o.focusin="onfocusin"in a,o.focusin||r.each({focus:"focusin",blur:"focusout"},function(a,b){var c=function(a){r.event.simulate(b,a.target,r.event.fix(a))};r.event.special[b]={setup:function(){var d=this.ownerDocument||this,e=W.access(d,b);e||d.addEventListener(a,c,!0),W.access(d,b,(e||0)+1)},teardown:function(){var d=this.ownerDocument||this,e=W.access(d,b)-1;e?W.access(d,b,e):(d.removeEventListener(a,c,!0),W.remove(d,b))}}});var tb=a.location,ub=r.now(),vb=/\?/;r.parseXML=function(b){var c;if(!b||"string"!=typeof b)return null;try{c=(new a.DOMParser).parseFromString(b,"text/xml")}catch(d){c=void 0}return c&&!c.getElementsByTagName("parsererror").length||r.error("Invalid XML: "+b),c};var wb=/\[\]$/,xb=/\r?\n/g,yb=/^(?:submit|button|image|reset|file)$/i,zb=/^(?:input|select|textarea|keygen)/i;function Ab(a,b,c,d){var e;if(Array.isArray(b))r.each(b,function(b,e){c||wb.test(a)?d(a,e):Ab(a+"["+("object"==typeof e&&null!=e?b:"")+"]",e,c,d)});else if(c||"object"!==r.type(b))d(a,b);else for(e in b)Ab(a+"["+e+"]",b[e],c,d)}r.param=function(a,b){var c,d=[],e=function(a,b){var c=r.isFunction(b)?b():b;d[d.length]=encodeURIComponent(a)+"="+encodeURIComponent(null==c?"":c)};if(Array.isArray(a)||a.jquery&&!r.isPlainObject(a))r.each(a,function(){e(this.name,this.value)});else for(c in a)Ab(c,a[c],b,e);return d.join("&")},r.fn.extend({serialize:function(){return r.param(this.serializeArray())},serializeArray:function(){return this.map(function(){var a=r.prop(this,"elements");return a?r.makeArray(a):this}).filter(function(){var a=this.type;return this.name&&!r(this).is(":disabled")&&zb.test(this.nodeName)&&!yb.test(a)&&(this.checked||!ja.test(a))}).map(function(a,b){var c=r(this).val();return null==c?null:Array.isArray(c)?r.map(c,function(a){return{name:b.name,value:a.replace(xb,"\r\n")}}):{name:b.name,value:c.replace(xb,"\r\n")}}).get()}});var Bb=/%20/g,Cb=/#.*$/,Db=/([?&])_=[^&]*/,Eb=/^(.*?):[ \t]*([^\r\n]*)$/gm,Fb=/^(?:about|app|app-storage|.+-extension|file|res|widget):$/,Gb=/^(?:GET|HEAD)$/,Hb=/^\/\//,Ib={},Jb={},Kb="*/".concat("*"),Lb=d.createElement("a");Lb.href=tb.href;function Mb(a){return function(b,c){"string"!=typeof b&&(c=b,b="*");var d,e=0,f=b.toLowerCase().match(L)||[];if(r.isFunction(c))while(d=f[e++])"+"===d[0]?(d=d.slice(1)||"*",(a[d]=a[d]||[]).unshift(c)):(a[d]=a[d]||[]).push(c)}}function Nb(a,b,c,d){var e={},f=a===Jb;function g(h){var i;return e[h]=!0,r.each(a[h]||[],function(a,h){var j=h(b,c,d);return"string"!=typeof j||f||e[j]?f?!(i=j):void 0:(b.dataTypes.unshift(j),g(j),!1)}),i}return g(b.dataTypes[0])||!e["*"]&&g("*")}function Ob(a,b){var c,d,e=r.ajaxSettings.flatOptions||{};for(c in b)void 0!==b[c]&&((e[c]?a:d||(d={}))[c]=b[c]);return d&&r.extend(!0,a,d),a}function Pb(a,b,c){var d,e,f,g,h=a.contents,i=a.dataTypes;while("*"===i[0])i.shift(),void 0===d&&(d=a.mimeType||b.getResponseHeader("Content-Type"));if(d)for(e in h)if(h[e]&&h[e].test(d)){i.unshift(e);break}if(i[0]in c)f=i[0];else{for(e in c){if(!i[0]||a.converters[e+" "+i[0]]){f=e;break}g||(g=e)}f=f||g}if(f)return f!==i[0]&&i.unshift(f),c[f]}function Qb(a,b,c,d){var e,f,g,h,i,j={},k=a.dataTypes.slice();if(k[1])for(g in a.converters)j[g.toLowerCase()]=a.converters[g];f=k.shift();while(f)if(a.responseFields[f]&&(c[a.responseFields[f]]=b),!i&&d&&a.dataFilter&&(b=a.dataFilter(b,a.dataType)),i=f,f=k.shift())if("*"===f)f=i;else if("*"!==i&&i!==f){if(g=j[i+" "+f]||j["* "+f],!g)for(e in j)if(h=e.split(" "),h[1]===f&&(g=j[i+" "+h[0]]||j["* "+h[0]])){g===!0?g=j[e]:j[e]!==!0&&(f=h[0],k.unshift(h[1]));break}if(g!==!0)if(g&&a["throws"])b=g(b);else try{b=g(b)}catch(l){return{state:"parsererror",error:g?l:"No conversion from "+i+" to "+f}}}return{state:"success",data:b}}r.extend({active:0,lastModified:{},etag:{},ajaxSettings:{url:tb.href,type:"GET",isLocal:Fb.test(tb.protocol),global:!0,processData:!0,async:!0,contentType:"application/x-www-form-urlencoded; charset=UTF-8",accepts:{"*":Kb,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":r.parseXML},flatOptions:{url:!0,context:!0}},ajaxSetup:function(a,b){return b?Ob(Ob(a,r.ajaxSettings),b):Ob(r.ajaxSettings,a)},ajaxPrefilter:Mb(Ib),ajaxTransport:Mb(Jb),ajax:function(b,c){"object"==typeof b&&(c=b,b=void 0),c=c||{};var e,f,g,h,i,j,k,l,m,n,o=r.ajaxSetup({},c),p=o.context||o,q=o.context&&(p.nodeType||p.jquery)?r(p):r.event,s=r.Deferred(),t=r.Callbacks("once memory"),u=o.statusCode||{},v={},w={},x="canceled",y={readyState:0,getResponseHeader:function(a){var b;if(k){if(!h){h={};while(b=Eb.exec(g))h[b[1].toLowerCase()]=b[2]}b=h[a.toLowerCase()]}return null==b?null:b},getAllResponseHeaders:function(){return k?g:null},setRequestHeader:function(a,b){return null==k&&(a=w[a.toLowerCase()]=w[a.toLowerCase()]||a,v[a]=b),this},overrideMimeType:function(a){return null==k&&(o.mimeType=a),this},statusCode:function(a){var b;if(a)if(k)y.always(a[y.status]);else for(b in a)u[b]=[u[b],a[b]];return this},abort:function(a){var b=a||x;return e&&e.abort(b),A(0,b),this}};if(s.promise(y),o.url=((b||o.url||tb.href)+"").replace(Hb,tb.protocol+"//"),o.type=c.method||c.type||o.method||o.type,o.dataTypes=(o.dataType||"*").toLowerCase().match(L)||[""],null==o.crossDomain){j=d.createElement("a");try{j.href=o.url,j.href=j.href,o.crossDomain=Lb.protocol+"//"+Lb.host!=j.protocol+"//"+j.host}catch(z){o.crossDomain=!0}}if(o.data&&o.processData&&"string"!=typeof o.data&&(o.data=r.param(o.data,o.traditional)),Nb(Ib,o,c,y),k)return y;l=r.event&&o.global,l&&0===r.active++&&r.event.trigger("ajaxStart"),o.type=o.type.toUpperCase(),o.hasContent=!Gb.test(o.type),f=o.url.replace(Cb,""),o.hasContent?o.data&&o.processData&&0===(o.contentType||"").indexOf("application/x-www-form-urlencoded")&&(o.data=o.data.replace(Bb,"+")):(n=o.url.slice(f.length),o.data&&(f+=(vb.test(f)?"&":"?")+o.data,delete o.data),o.cache===!1&&(f=f.replace(Db,"$1"),n=(vb.test(f)?"&":"?")+"_="+ub++ +n),o.url=f+n),o.ifModified&&(r.lastModified[f]&&y.setRequestHeader("If-Modified-Since",r.lastModified[f]),r.etag[f]&&y.setRequestHeader("If-None-Match",r.etag[f])),(o.data&&o.hasContent&&o.contentType!==!1||c.contentType)&&y.setRequestHeader("Content-Type",o.contentType),y.setRequestHeader("Accept",o.dataTypes[0]&&o.accepts[o.dataTypes[0]]?o.accepts[o.dataTypes[0]]+("*"!==o.dataTypes[0]?", "+Kb+"; q=0.01":""):o.accepts["*"]);for(m in o.headers)y.setRequestHeader(m,o.headers[m]);if(o.beforeSend&&(o.beforeSend.call(p,y,o)===!1||k))return y.abort();if(x="abort",t.add(o.complete),y.done(o.success),y.fail(o.error),e=Nb(Jb,o,c,y)){if(y.readyState=1,l&&q.trigger("ajaxSend",[y,o]),k)return y;o.async&&o.timeout>0&&(i=a.setTimeout(function(){y.abort("timeout")},o.timeout));try{k=!1,e.send(v,A)}catch(z){if(k)throw z;A(-1,z)}}else A(-1,"No Transport");function A(b,c,d,h){var j,m,n,v,w,x=c;k||(k=!0,i&&a.clearTimeout(i),e=void 0,g=h||"",y.readyState=b>0?4:0,j=b>=200&&b<300||304===b,d&&(v=Pb(o,y,d)),v=Qb(o,v,y,j),j?(o.ifModified&&(w=y.getResponseHeader("Last-Modified"),w&&(r.lastModified[f]=w),w=y.getResponseHeader("etag"),w&&(r.etag[f]=w)),204===b||"HEAD"===o.type?x="nocontent":304===b?x="notmodified":(x=v.state,m=v.data,n=v.error,j=!n)):(n=x,!b&&x||(x="error",b<0&&(b=0))),y.status=b,y.statusText=(c||x)+"",j?s.resolveWith(p,[m,x,y]):s.rejectWith(p,[y,x,n]),y.statusCode(u),u=void 0,l&&q.trigger(j?"ajaxSuccess":"ajaxError",[y,o,j?m:n]),t.fireWith(p,[y,x]),l&&(q.trigger("ajaxComplete",[y,o]),--r.active||r.event.trigger("ajaxStop")))}return y},getJSON:function(a,b,c){return r.get(a,b,c,"json")},getScript:function(a,b){return r.get(a,void 0,b,"script")}}),r.each(["get","post"],function(a,b){r[b]=function(a,c,d,e){return r.isFunction(c)&&(e=e||d,d=c,c=void 0),r.ajax(r.extend({url:a,type:b,dataType:e,data:c,success:d},r.isPlainObject(a)&&a))}}),r._evalUrl=function(a){return r.ajax({url:a,type:"GET",dataType:"script",cache:!0,async:!1,global:!1,"throws":!0})},r.fn.extend({wrapAll:function(a){var b;return this[0]&&(r.isFunction(a)&&(a=a.call(this[0])),b=r(a,this[0].ownerDocument).eq(0).clone(!0),this[0].parentNode&&b.insertBefore(this[0]),b.map(function(){var a=this;while(a.firstElementChild)a=a.firstElementChild;return a}).append(this)),this},wrapInner:function(a){return r.isFunction(a)?this.each(function(b){r(this).wrapInner(a.call(this,b))}):this.each(function(){var b=r(this),c=b.contents();c.length?c.wrapAll(a):b.append(a)})},wrap:function(a){var b=r.isFunction(a);return this.each(function(c){r(this).wrapAll(b?a.call(this,c):a)})},unwrap:function(a){return this.parent(a).not("body").each(function(){r(this).replaceWith(this.childNodes)}),this}}),r.expr.pseudos.hidden=function(a){return!r.expr.pseudos.visible(a)},r.expr.pseudos.visible=function(a){return!!(a.offsetWidth||a.offsetHeight||a.getClientRects().length)},r.ajaxSettings.xhr=function(){try{return new a.XMLHttpRequest}catch(b){}};var Rb={0:200,1223:204},Sb=r.ajaxSettings.xhr();o.cors=!!Sb&&"withCredentials"in Sb,o.ajax=Sb=!!Sb,r.ajaxTransport(function(b){var c,d;if(o.cors||Sb&&!b.crossDomain)return{send:function(e,f){var g,h=b.xhr();if(h.open(b.type,b.url,b.async,b.username,b.password),b.xhrFields)for(g in b.xhrFields)h[g]=b.xhrFields[g];b.mimeType&&h.overrideMimeType&&h.overrideMimeType(b.mimeType),b.crossDomain||e["X-Requested-With"]||(e["X-Requested-With"]="XMLHttpRequest");for(g in e)h.setRequestHeader(g,e[g]);c=function(a){return function(){c&&(c=d=h.onload=h.onerror=h.onabort=h.onreadystatechange=null,"abort"===a?h.abort():"error"===a?"number"!=typeof h.status?f(0,"error"):f(h.status,h.statusText):f(Rb[h.status]||h.status,h.statusText,"text"!==(h.responseType||"text")||"string"!=typeof h.responseText?{binary:h.response}:{text:h.responseText},h.getAllResponseHeaders()))}},h.onload=c(),d=h.onerror=c("error"),void 0!==h.onabort?h.onabort=d:h.onreadystatechange=function(){4===h.readyState&&a.setTimeout(function(){c&&d()})},c=c("abort");try{h.send(b.hasContent&&b.data||null)}catch(i){if(c)throw i}},abort:function(){c&&c()}}}),r.ajaxPrefilter(function(a){a.crossDomain&&(a.contents.script=!1)}),r.ajaxSetup({accepts:{script:"text/javascript, application/javascript, application/ecmascript, application/x-ecmascript"},contents:{script:/\b(?:java|ecma)script\b/},converters:{"text script":function(a){return r.globalEval(a),a}}}),r.ajaxPrefilter("script",function(a){void 0===a.cache&&(a.cache=!1),a.crossDomain&&(a.type="GET")}),r.ajaxTransport("script",function(a){if(a.crossDomain){var b,c;return{send:function(e,f){b=r("