` of commands input by the user called `pastCommands`.
+
+Whenever `AddressBook` is changed, it will store its copy and append it to the `AddressBookList`, increment `index` by 1, and append the command
+that modifies the `AddressBook` to `pastCommands`.
-#### Proposed Implementation
+When `AddressBookList#undo()` is called, the current `AddressBook` changes to `AddressBookList.get(index - 1)` and `index` is decremented by 1.
+If the current `index` is at 0, `AddressBookList` will throw an error stating there is no command to undo.
-The proposed undo/redo mechanism is facilitated by `VersionedAddressBook`. It extends `AddressBook` with an undo/redo history, stored internally as an `addressBookStateList` and `currentStatePointer`. Additionally, it implements the following operations:
+If, after undoing some command, the user decides to modify the `AddressBook` with new commands, `AddressBookList` will overwrite all `AddressBook` after that particular state.
+For example, currently `AddressBookList` has 10 `AddressBook` in it, and the current state is at `AddressBookList.get(5)`. Then, the user adds an employee. All `AddressBook`
+at index 6 to 9 will be deleted, and `AddressBookList.get(6)` will now contain the new `AddressBook` that has the new employee in it.
-* `VersionedAddressBook#commit()` — Saves the current address book state in its history.
-* `VersionedAddressBook#undo()` — Restores the previous address book state from its history.
-* `VersionedAddressBook#redo()` — Restores a previously undone address book state from its history.
+When `AddressBookList#redo()` is called, the current `AddressBook` changes to `AddressBookList.get(index + 1)` and `index` is incremented by 1.
+If the current `index` is at `AddressBookList.size() - 1`, `AddressBookList` will throw an error stating there is no command to redo.
-These operations are exposed in the `Model` interface as `Model#commitAddressBook()`, `Model#undoAddressBook()` and `Model#redoAddressBook()` respectively.
+`AddressBookList` sits in `ModelManager` class where it will be modified whenever `ModelManager#addPerson()`, `ModelManager#deletePerson()`, and `ModelManager#setPerson()`
+are called, i.e., whenever `AddressBook` is changed. `ModelManager#undo()` will call `AddressBookList#undo()` and `ModelManager#redo()` will call `AddressBookList#redo()`.
-Given below is an example usage scenario and how the undo/redo mechanism behaves at each step.
+![UndoSequenceDiagram](images/UndoSequenceDiagram.png)
-Step 1. The user launches the application for the first time. The `VersionedAddressBook` will be initialized with the initial address book state, and the `currentStatePointer` pointing to that single address book state.
+![RedoSequenceDiagram](images/RedoSequenceDiagram.png)
-![UndoRedoState0](images/UndoRedoState0.png)
+#### Design Considerations
-Step 2. The user executes `delete 5` command to delete the 5th person in the address book. The `delete` command calls `Model#commitAddressBook()`, causing the modified state of the address book after the `delete 5` command executes to be saved in the `addressBookStateList`, and the `currentStatePointer` is shifted to the newly inserted address book state.
+**Aspect: How Undo/Redo executes:**
-![UndoRedoState1](images/UndoRedoState1.png)
+* **Alternative 1 (current choice):** Stores the entire address book.
+ * Pros: Easy to implement.
+ * Cons: May have performance issues in terms of memory usage.
-Step 3. The user executes `add n/David …` to add a new person. The `add` command also calls `Model#commitAddressBook()`, causing another modified address book state to be saved into the `addressBookStateList`.
+* **Alternative 2:** Stores only the changes between address book versions. For example, only store the employee being added when the user adds an employee.
+ * Pros: Less memory.
+ * Cons: More complications on the logic side: undoing `add` = `delete`, undoing `clear` = `add` back all employees, etc.
-![UndoRedoState2](images/UndoRedoState2.png)
+### Export Data Feature
-:information_source: **Note:** If a command fails its execution, it will not call `Model#commitAddressBook()`, so the address book state will not be saved into the `addressBookStateList`.
+#### Implementation
-
+The proposed export mechanism is facilitated by `AddressBook`. Additionally, the operation is exposed in the Model interface which allows users to perform `filter` operation to discard selected portions of the Model interface before exporting.
-Step 4. The user now decides that adding the person was a mistake, and decides to undo that action by executing the `undo` command. The `undo` command will call `Model#undoAddressBook()`, which will shift the `currentStatePointer` once to the left, pointing it to the previous address book state, and restores the address book to that state.
+The primary operations that aid in this mechanism are:
+* `getFilteredPersonList()` -- Used in conjunction with `filter` operation so that the model will display the latest Persons of interest.
+* `generateListPeople()` -- Performs extracting of Person objects from the latest model and obtaining the Person's attributes.
+* `generateFile()` -- Creates a directory for exported CSVs and a File object containing the filename imposed by the user.
+* `convertToCsv()`-- Performs writing of Person's attributes into the created File Object in `generateFile()`.
-![UndoRedoState3](images/UndoRedoState3.png)
+Given below is an example usage scenario and how the export mechanism behaves at each step.
-:information_source: **Note:** If the `currentStatePointer` is at index 0, pointing to the initial AddressBook state, then there are no previous AddressBook states to restore. The `undo` command uses `Model#canUndoAddressBook()` to check if this is the case. If so, it will return an error to the user rather
-than attempting to perform the undo.
+Step 1. The user launches the application for the first time. The initial address book state will display all the employees.
-
+Step 2. The user executes `list d/Engineering` command which filters employees belonging to the Engineering department. This command also executes `getFilteredPersonList()` that alters the Model by containing only the filtered Person list.
-The following sequence diagram shows how the undo operation works:
+Step 3. The user executes `export engineering_dept` command which takes the "altered" Model containing filtered Person objects and executes `generateListPeople()` which helps to obtain Person objects from the currentModel and extracting their attributes into a list.
-![UndoSequenceDiagram](images/UndoSequenceDiagram.png)
+Step 4. After completion of step 3 and still in the `export engineering_dept` command, `generateFile()` and `convertToCsv()` are called sequentially to allow writing of Person' attributes into the exported CSV file.
-:information_source: **Note:** The lifeline for `UndoCommand` should end at the destroy marker (X) but due to a limitation of PlantUML, the lifeline reaches the end of diagram.
+The following sequence diagram shows how the export operation works:
-
+![ExportSequenceDiagram](images/ExportSequenceDiagram.png)
-The `redo` command does the opposite — it calls `Model#redoAddressBook()`, which shifts the `currentStatePointer` once to the right, pointing to the previously undone state, and restores the address book to that state.
+The following activity diagram summarizes what happens when a user attempts to export the current employees' data:
-:information_source: **Note:** If the `currentStatePointer` is at index `addressBookStateList.size() - 1`, pointing to the latest address book state, then there are no undone AddressBook states to restore. The `redo` command uses `Model#canRedoAddressBook()` to check if this is the case. If so, it will return an error to the user rather than attempting to perform the redo.
+
-
+#### Design Considerations
-Step 5. The user then decides to execute the command `list`. Commands that do not modify the address book, such as `list`, will usually not call `Model#commitAddressBook()`, `Model#undoAddressBook()` or `Model#redoAddressBook()`. Thus, the `addressBookStateList` remains unchanged.
+**Aspect: How exports executes:**
-![UndoRedoState4](images/UndoRedoState4.png)
+* **Alternative 1 (current choice):** User specifies filename and file is stored in fixed location.
+ * Pros: Easy to implement. Easy for user to find his newly exported file.
+ * Cons: Doesn't provide user a view on whether the filename has already been used. Loss of file is a concern.
-Step 6. The user executes `clear`, which calls `Model#commitAddressBook()`. Since the `currentStatePointer` is not pointing at the end of the `addressBookStateList`, all address book states after the `currentStatePointer` will be purged. Reason: It no longer makes sense to redo the `add n/David …` command. This is the behavior that most modern desktop applications follow.
+* **Alternative 2:** Full flexibility for user to select the exported location of one's files.
+ * Pros: User can perform better categorisation of data according to organisation's needs.
+ * Cons: Harder to implement since program needs to know which locations are out-of-bounds. Example: src files.
-![UndoRedoState5](images/UndoRedoState5.png)
-The following activity diagram summarizes what happens when a user executes a new command:
-
-#### Design considerations:
+### Leave Feature
-**Aspect: How undo & redo executes:**
+#### Implementation
-* **Alternative 1 (current choice):** Saves the entire address book.
- * Pros: Easy to implement.
- * Cons: May have performance issues in terms of memory usage.
+This feature adds a way to record and view registered leaves for the employees.
+This is implemented by adding a `Leave` class as an attribute to `Person` class.
+Manipulating the leave records can be done using `Command#LeaveCommand` and viewing can be done using `Commmand#ViewLeaveCommand`.
+ViewLeaveCommand can be invoked with or without parameter.
+
+Given below is an example usage scenario and how the birthday mechanism behaves at each step.
+
+Step 1: The user launches the application for the first time.
+
+Step 2: The user executes `view_leave` to see the currently registered leaves of the employees.
+This function works like a filter through the `model`. This is done by having a predicate to test if the person has a leave.
+For the default no-parameter `view_leave` function, this will invoke the creation of `HasLeaveAnyMonthPredicate` predicate, which tests if the employees have leaves in any of the months. The command goes through the `ViewLeaveCommandParser` class to check if there is any parameters.
+This command will return a list of employees who has leave in any of the months by invoking a `model.updateFilteredPersonsList(predicate)`.
+
+Step 3: The user adds and removes leaves from employees by doing a `leave 2 m/1,-2`
+This command will go through the `LeaveCommandParser` class. The parser will then extract the relevant information, like the employee's index and the months to add or remove. This command adds a leave in January and removes a leave in February for the employee on index 2.
+
+Step 4: The user wants to see the employees with leaves in January or February in the Engineering department by doing a `view_leave m/1,2 d/engineering`
+This works similarly to the default `view_leave` but with parameters. The `ViewLeaveCommandParser` will parse the command and combine the predicates as it parses the arguments.
+In this case, the `ViewLeaveCommandParser` will create `HasLeaveThisMonthPredicate` and `MatchingDepartmentPredicate`.
+For every month specified, the parser will create a `HasLeaveThisMonthPredicate` and do an `or()` operation of the predicate, resulting in a `combinedPredicate` variable. The parser will also create a `MatchingDepartmentPredicate` and do an `and()` to the combinedPredicate().
+This `combinedPredicate` will then be passed to the `ViewLeaveCommand` constructor, which will create a `ViewLeaveCommand` that filters the `model` using the `combinedPredicate` specified.
+
+The sequence diagram below shows how the `leave` commands execute:
+
+
+The sequence diagram below shows how the `view_leave` commands execute:
+
+
+#### Design Considerations
+
+**Aspect: How Leave executes:**
+
+* **Alternative 1 (current choice):** User can add and remove leaves at the same time for one employee.
+ * Pros: User can add and remove multiple leaves faster than doing it one by one.
+ * Cons: Might be confusing to the user at first, the expected format needs to be properly explained.
+
+* **Alternative 2:** Only allows one addition/removal at a time.
+ * Pros: Straightforward and intuitive for the users to understand.
+ * Cons: Takes longer for the user to add or remove multiple leaves for the same employee.
+
+**Aspect: How View Leave executes:**
+
+* **Alternative 1 (current choice):** User can view leaves for multiple months at once.
+ * Pros: The user can view all the employees with leaves in any of the specified months at once when the use case allows.
+ * Cons: Might be confusing to the user at first, the expected format needs to be properly explained. For example, it might be confusing whether the relationship between the months is an "and" or an "or". In this implementation, it is an "or", meaning it will show employees having leave(s) in any of the months instead of all of the months.
+
+* **Alternative 2:** Only allows users to view one leave month at a time.
+ * Pros: Straightforward and intuitive for the users to understand.
+ * Cons: There might be use cases where the user needs to compare and see different months at once.
+
+
+
+
+### Birthday Feature
+
+#### Implementation
-* **Alternative 2:** Individual command knows how to undo/redo by
- itself.
- * Pros: Will use less memory (e.g. for `delete`, just save the person being deleted).
- * Cons: We must ensure that the implementation of each individual command are correct.
+The birthday feature extends HR Insight by allowing the user to view birthdays in a given month. This is implemented by extracting the `Month` class from the 'Birthday' class.
+This operation is exposed in the Command Interface as `Command#BirthdayCommand`. BirthdayCommand can be invoked with or without a parameter.
-_{more aspects and alternatives to be added}_
+Given below is an example usage scenario and how the birthday mechanism behaves at each step.
-### \[Proposed\] Data archiving
+Step 1: The user launches the application for the first time.
-_{Explain here how the data archiving feature will be implemented}_
+Step 2: The user executes `add n/John Doe ... ` to add a new employee.
+Step 3: After adding a few employees to the application, he/she wants to view the birthdays in the month of January
+to prepare the birthday celebrations in advance. The `birthday m/1` command will display all employees with birthdays
+in the month of January.
+
+Step 4: The user now wants to view all birthdays that fall in the first quarter, which includes Jan, Feb and Mar. The
+`birthday m/1,2,3` command will display all employees with birthdays in the first quarter.
+
+Step 4: The user then realizes that today is the first day of a new month and he/she wants to view which employees
+have birthdays in the current month. The `birthday` command without providing the month will display all employees with
+birthdays in the current month.
+
+The following sequence diagram shows how the `birthday` command works:
+
+![SequenceDiagram](images/BirthdayCommand.png)
+
+#### Design Considerations
+The birthday command is designed to show users birthdays by month instead of week or day as month gives the user a broader
+range to work with. Furthermore, it is also a common practice for companies to have one celebration for all employees'
+birthdays in a month rather than having multiple individual celebrations. Hence, this feature is designed to show
+birthdays by month.
+
+**Aspect: How Birthday executes:**
+
+* **Alternative 1 (current choice)** : Allows the user to view birthday in a month
+ * Pros: Allows the user to view birthdays in a broader range.
+ * Cons: User cannot view birthdays that occur across multiple months
+* **Alternative 2**: Allows the user to view birthdays in multiple months
+ * Pros: User will be able to view birthdays across multiple months with only one use of the command.
+ (e.g. `birthday m/2,3` returns all birthdays in February and March )
+ * Cons: We must ensure that every month given by the user is valid and mention which months have no birthdays.
+
+
+### Change Theme Feature
+
+#### Implementation
+
+This feature adds a way to change the theme of the application.
+The default theme is the dark theme, and it can be changed at any point of usage by using the command `theme`.
+This is implemented by adding a `Command#ThemeCommand` to take the user input for changing themes.
+The actual changing of the theme itself is done by changing the stylesheet of the `MainWindow`.
+`MainWindow` keeps track of the current stylesheet, and when it receives a theme change request, it will remove the current stylesheet and add the new stylesheet using the `setTheme()` function.
+
+Given below is an example usage scenario and how the birthday mechanism behaves at each step.
+
+Step 1: The user launches the application for the first time.
+
+Step 2: The user executes `theme` wishing to see the available themes.
+This command will return feedback that the theme is invalid, as there are no arguments detected. It will then show the names of the valid themes currently implemented. At the point of time of writing this developer guide, the available themes are `light`, `dark`, `red`, `green`, and `blue`.
+
+Step 3: The user changes the theme by calling `theme red`.
+This command will pass through the `ThemeCommandParser` class which will parse the arguments given (`red` in this case). The parse class will then return a `ThemeCommand` containing the name of the stylesheet for the given theme. This will continue to get passed on to `MainWindow` through `CommandResult`. `MainWindow` will then execute the `setTheme()` function, which removes the current stylesheet and adds the new stylesheet to the `MainWindow.fxml` file.
+
+The sequence diagram below shows how the `theme` commands execute:
+
+
+#### Design Considerations
+
+**Aspect: How Theme executes:**
+
+* **Alternative 1 (current choice):** Users can specify the theme they want to use.
+ * Pros: Users can have choices of themes they can use.
+ * Cons: More tedious to implement with a need for parsing user commands and translating them to stylesheets.
+
+* **Alternative 2:** Users can only switch between dark and light themes.
+ * Pros: Easier to implement, no need for arguments for the command.
+ * Cons: Limiting users' choices, the function will be harder to extend in the future.
--------------------------------------------------------------------------------------------------------------------
@@ -251,48 +395,132 @@ _{Explain here how the data archiving feature will be implemented}_
--------------------------------------------------------------------------------------------------------------------
-## **Appendix: Requirements**
+## **Appendix A: Requirements**
### Product scope
**Target user profile**:
-* has a need to manage a significant number of contacts
+* for HR people to manage employees data in the company, including employees' claims and leaves
* prefer desktop apps over other types
* can type fast
* prefers typing to mouse interactions
* is reasonably comfortable using CLI apps
-**Value proposition**: manage contacts faster than a typical mouse/GUI driven app
-
+**Value proposition**: Provide HR employees a centralized employee management system to better manage all employees’ details and improve the efficiency of their workflow.
### User stories
Priorities: High (must have) - `* * *`, Medium (nice to have) - `* *`, Low (unlikely to have) - `*`
-| Priority | As a … | I want to … | So that I can… |
+| Priority | As a(n) … | I want to … | So that I can… |
| -------- | ------------------------------------------ | ------------------------------ | ---------------------------------------------------------------------- |
-| `* * *` | new user | see usage instructions | refer to instructions when I forget how to use the App |
-| `* * *` | user | add a new person | |
-| `* * *` | user | delete a person | remove entries that I no longer need |
-| `* * *` | user | find a person by name | locate details of persons without having to go through the entire list |
-| `* *` | user | hide private contact details | minimize chance of someone else seeing them by accident |
-| `*` | user with many persons in the address book | sort persons by name | locate a person easily |
+| `* * *` | HR employee | add an employee's information | |
+| `* * *` | HR employee | update an employee's information | have the latest information of the employee |
+| `* * *` | HR employee | delete an employee's information | |
+| `* * *` | HR employee | list all employees | see all employees' details in the company |
+| `* * *` | HR employee | add or subtract an employee's claim budget | manage the employee's claims and allowance |
+| `* * *` | HR employee | have an overview on each employee's leaves | track each employee's leaves |
+| `* * *` | HR employee | update an employee's leaves | |
+| `* * *` | HR employee | reset all employees' leaves | efficiently adjust their leaves at the start of a new year |
+| `* *` | HR employee | view all employees who have birthdays in a given month | can plan the celebrations beforehand |
+| `* *` | HR employee | find an employee by name | locate details of an employee without having to go through the entire list |
+| `* *` | HR employee | undo and redo my previous commands | easily recover from input mistakes I made |
+| `*` | HR employee | sort employees based on their details, e.g., name, salary, DOB, etc. | infer some useful information from the employees data |
+| `*` | HR employee | export the employee list into a csv file | send the file to my manager or other stakeholders for review |
+| `*` | HR employee with color preferences | change the application's theme | adjust the user interface according to my preference |
+| `*` | advanced HR employee | navigate through my previous commands using up/down keys | more efficiently input commands that are similar to my previous inputs just like normal computer CLI or terminal |
+| `*` | new HR employee | see a help message with command summary and link to the user guide | learn how to use HR Insight |
-*{More to be added}*
### Use cases
-(For all use cases below, the **System** is the `AddressBook` and the **Actor** is the `user`, unless specified otherwise)
+(For all use cases below, the **System** is the `HR Insight` and the **Actor** is the `user / HR employee`, unless specified otherwise)
-**Use case: Delete a person**
+**Use case: Adding an Employee**
**MSS**
-1. User requests to list persons
-2. AddressBook shows a list of persons
-3. User requests to delete a specific person in the list
-4. AddressBook deletes the person
+1. User requests to add an employee with specified information.
+2. HR Insight adds an employee with the given information.
+3. HR Insight indicates that the new employee has been added.
+4. HR Insight shows a list of employees including the new employee.
+
+ Use case ends.
+
+**Extensions**
+
+* 2a. At least one of the required information is empty or invalid.
+
+ * 2a1. HR Insight shows an error message.
+
+ Use case ends.
+
+* 3a. The employee already exists.
+
+ * 3a1. HR Insight indicates that the employee already exists.
+
+ Use case ends.
+
+**Use case: Listing Employees**
+
+**MSS**
+
+1. User requests to list all employees.
+2. HR Insight shows all employees of an organisation.
+3. User requests to filter employees by a specified department.
+4. HR Insight shows all employees of the specified department.
+
+ Use case ends.
+
+**Extensions**
+
+* 2a. The employee's list is empty.
+
+ Use case ends.
+
+* 3a. The given department is invalid.
+
+ * 3a1. HR Insight shows an error message.
+
+ Use case resumes from step 3.
+
+**Use case: Deleting an Employee**
+
+**MSS**
+
+1. User requests to list employees.
+2. HR Insight shows a list of employees.
+3. User requests to delete an employee in the list specified by its index.
+4. HR Insight deletes the employee.
+5. HR Insight indicates that the employee has been deleted.
+6. HR Insight shows a list of employees excluding the deleted employee.
+
+ Use case ends.
+
+**Extensions**
+
+* 2a. The list is empty.
+
+ Use case ends.
+
+* 3a. The given index is invalid.
+
+ * 3a1. HR Insight shows an error message.
+
+ Use case resumes from step 2.
+
+
+**Use case: Editing an Employee**
+
+**MSS**
+
+1. User requests to list employees.
+2. HR Insight shows a list of employees.
+3. User requests to edit an employee in the list specified by its index, with the employee's new information.
+4. HR Insight edits the employee.
+5. HR Insight indicates that the employee has been edited.
+6. HR Insight shows a list of employees, with the employee's information now edited.
Use case ends.
@@ -304,19 +532,284 @@ Priorities: High (must have) - `* * *`, Medium (nice to have) - `* *`, Low (unli
* 3a. The given index is invalid.
- * 3a1. AddressBook shows an error message.
+ * 3a1. HR Insight shows an error message.
+
+ Use case resumes from step 2.
+
+* 3b. User does not specify any new information for the employee.
+
+ * 3b1. HR Insight shows an error message that at least one field to edit must be provided.
+
+ Use case resumes from step 2.
+
+* 3c. At least one of the field given by the user is empty or invalid.
+
+ * 3c1. HR Insight shows an error message.
+
+ Use case resumes from step 2.
+
+**Use case: Managing Employee's Claim**
+
+**MSS**
+
+1. User requests to list all employees.
+2. HR Insight shows a list of employees.
+3. User requests to allocate or deduct an amount from the employee's claim budget by specifying the employee's index.
+4. HR Insight displays the updated claim of the specified employee.
+
+ Use case ends.
+
+**Extensions**
+
+* 3a. User didn't provide an index and/or claim amount.
+
+ * 3a1. HR Insight shows an error message.
+
+ Use case ends.
+
+* 3b. User provides a wrong index (Either negative or more than the current headcount).
+
+ * 3b1. HR Insight shows an error message.
+
+ Use case ends.
+
+* 3c. User didn't provide +/- when stating the claim amount.
+
+ * 3c1. HR Insight shows an error message.
+
+ Use case ends.
+
+* 3d. User provides a claim amount that is greater than the employee's current entitlement fund balance (Only Applicable for -).
+
+ * 3d1. HR Insight shows an error message.
+
+ Use case ends.
+
+
+**Use case: Updating Employee's Leaves**
+
+**MSS**
+
+1. User requests to list all employees.
+2. HR Insight shows a list of employees.
+3. User requests to update an employee's leaves.
+4. HR Insight records the updated leaves of the employee.
+
+ Use case ends.
+
+**Extensions**
+
+* 3a. User provides a wrong index (Either negative or more than the current headcount).
+
+ * 3a1. HR Insight shows an error message.
+
+ Use case ends.
+
+* 3b. User provides a invalid month (Either negative or more than 12).
+
+ * 3b1. HR Insight shows an error message.
+
+ Use case ends.
+
+
+**Use Case: Viewing Employees' Leaves**
+
+**MSS**
+
+1. User requests to view the leave dates of all employees.
+2. HR Insight shows the leave dates of all employees.
+
+ Use case ends.
+
+**Extensions**
+* 1a. User provides the index of a specific employee.
+ * 1a1. HR Insight shows the leave dates of the specified employee.
+
+ Use case ends.
+
+* 1b. User provides a specific month.
+ * 1b1. HR Insight shows the leave dates of all employees occurring in the specified month.
+
+ Use case ends
+
+* 1c. User provides a specific department.
+ * 1c1. HR Insight shows the leave dates of all employees in the specified department.
+
+ Use case ends.
+
+* 1d. User provides an invalid index/month/department.
+ * 1d1. HR Insight shows an error message.
+
+ Use case ends.
+
+
+**Use Case: Resetting Employees' Leaves**
+
+**MSS**
+
+1. User hopes to reset all employees to have no recorded leaves.
+2. HR Insight will show "-" under the leave attribute for each employee.
+
+ Use case ends.
+
+
+**Use Case: Viewing Employees' Birthdays**
+
+**MSS**
+
+1. User requests to view the birthdays of all employees.
+2. HR Insight shows the birthdays of all employees.
+
+ Use case ends.
+
+**Extensions**
+* 1a. User provides a specific month or a list of months.
+ * 1a1. HR Insight shows all birthdays in the specified month(s).
+
+ Use case ends.
+
+* 1b. User provides an invalid month.
+ * 1b1. HR Insight shows an error message.
+
+ Use case ends.
+
+
+**Use Case: Viewing Employee(s)' Attribute**
+
+**MSS**
+
+1. User requests to view specific attribute of employee(s).
+2. HR Insight shows the specific attribute of the employee(s).
+
+ Use case ends.
+
+**Extensions**
+* 1a. User provides a false attribute parameter (a.k.a., wrong prefix).
+ * 1a1. HR Insight shows an error message.
+ * 1a2. HR Insight shows all the attributes (prefixes) it can display for employee(s).
+
+ Use case ends.
+
+* 1b. User didn't provide any attribute that one wants to view.
+ * 1b1. HR Insight shows an error message.
+ * 1b2. HR Insights shows all the attributes (prefixes) it can display for employee(s).
+
+ Use case ends.
+
+* 1c. User provides more than 1 prefix or attribute that they want to view.
+ * 1c1. HR Insight shows an error message informing the user of the one attribute limit.
+ * 1c2. HR Insights shows all the attributes (prefixes) it can display for employee(s).
+
+ Use case ends.
+
+**Use Case: Sorting the employee list**
+
+**MSS**
+
+1. User requests to sort the employee list based on some parameter.
+2. System sorts the employee list based on the parameter given.
+3. System displays the success message.
+
+ Use case ends.
+
+**Extensions**
+* 1a. User does not provide any parameter.
+ * 1a1. HR Insight shows an error message of invalid command format.
+
+ Use case ends.
+
+* 1b. User provides an invalid parameter.
+ * 1b1. HR Insight shows an error message of the parameter to sort is invalid.
+
+ Use case ends.
+
+**Use Case: Undoing previous commands**
+
+**MSS**
+
+1. User requests to undo the previous command.
+2. System updates the employee list to the previous state.
+3. System displays the success message.
+
+ Use case ends.
+
+**Extensions**
+* 1a. There is no command to undo.
+ * 1a1. HR Insight shows an error message that there is no command to undo.
+
+ Use case ends.
+
+**Use Case: Redoing previous undone commands**
+
+**MSS**
+
+1. User requests to redo the previous undone command.
+2. System updates the employee list to the state before it was undone.
+3. System displays the success message.
+
+ Use case ends.
+
+**Extensions**
+* 1a. There is no previous undo command to redo.
+ * 1a1. HR Insight shows an error message that there is no command to redo.
+
+ Use case ends.
+
+**Use Case: Exporting Employee(s)' data**
+
+**MSS**
+
+1. User requests to download Employee(s)' data into CSV format.
+2. User provides the filename in which the data will be stored as.
+3. HR Insight will download the file into Exported_CSVs folder.
+
+ Use case ends.
+
+**Extensions**
+* 2a. User didn't provide any filenames to store all the data.
+ * 2a1. HR Insight shows an error message requesting the user to indicate a filename.
+
+ Use case ends.
+
+* 2b. User provides excess filenames (> 1) to store the data.
+ * 2b1. HR Insight shows an error message requesting the user to specify only one filename.
+
+ Use case ends.
+
+
+**Use Case: Changing Application's Theme**
+
+**MSS**
+
+1. User requests to change the current theme.
+2. The application theme changes.
+
+ Use case ends.
+
+**Extensions**
+* 1a. User didn't provide any theme names.
+ * 1a1. HR Insight shows an error message alerting the user of invalid theme name and giving a list of valid theme names.
+
+ Use case ends.
+
+* 1b. User provides an invalid theme name.
+ * 1b1. HR Insight shows an error message alerting the user of invalid theme name and giving a list of valid theme names.
+
+ Use case ends.
- Use case resumes at step 2.
-*{More to be added}*
### Non-Functional Requirements
1. Should work on any _mainstream OS_ as long as it has Java `11` or above installed.
2. Should be able to hold up to 1000 persons without a noticeable sluggishness in performance for typical usage.
-3. A user with above average typing speed for regular English text (i.e. not code, not system admin commands) should be able to accomplish most of the tasks faster using commands than using the mouse.
+3. HR people with above average typing speed for regular English text (i.e. not code, not system admin commands) should be able to accomplish most of the tasks faster using commands than using the mouse.
+4. Should respond quickly to user input with minimal latency.
+5. Should not crash under normal usage conditions.
+6. Should have mechanisms to recover gracefully from unexpected errors or crashes.
+7. Should have comprehensive user guide and documentation for developers.
+
-*{More to be added}*
### Glossary
@@ -325,9 +818,10 @@ Priorities: High (must have) - `* * *`, Medium (nice to have) - `* *`, Low (unli
--------------------------------------------------------------------------------------------------------------------
-## **Appendix: Instructions for manual testing**
+## **Appendix B: Instructions for manual testing**
Given below are instructions to test the app manually.
+Prerequisites: List all employees using the list command. The list is non-empty.
:information_source: **Note:** These instructions only provide a starting point for testers to work on;
testers are expected to do more *exploratory* testing.
@@ -339,39 +833,162 @@ testers are expected to do more *exploratory* testing.
1. Initial launch
1. Download the jar file and copy into an empty folder
+ 2. Double-click the jar file Expected: Shows the GUI with a set of sample contacts. The window size may not be optimal.
- 1. Double-click the jar file Expected: Shows the GUI with a set of sample contacts. The window size may not be optimum.
-
-1. Saving window preferences
+2. Saving window preferences
1. Resize the window to an optimum size. Move the window to a different location. Close the window.
+ 2. Re-launch the app by double-clicking the jar file.
+ Expected: The most recent window size and location is retained.
+
+### Adding an employee
+
+1. Positive Test case: `add n/Adam p/12345678 e/adam@gmail.com a/Singapore s/10000 b/5000 d/Engineering dob/2000-01-01`
+ Expected: An employee added with name `adam`, phone `12345678`, email `adam@gmail.com`, address `Singapore`, salary `10000`, claim budget: `5000`, department: `Engineering` and DOB being `1 Jan 2000`.
+
+2. Negative Test case: `add n/Adam`
+ Expected: Error message since other attributes like `phone`, `email`, `address`, `salary`, `claim_budget`, `department` and `DOB` are not provided.
+
+### Listing an employee
+
+1. Positive Test Case: `list`
+ Expected: Listed all employees (7)
+
+2. Positive Test Case: `list d/Engineering`
+ Expected: Listed filtered employees (3)
+
+### Deleting an employee
+
+1. Positive Test Case: `delete 1`
+ Expected: First employee is deleted from the list.
+
+2. Negative Test Case: `delete`
+ Expected: Error message since index is provided.
+
+### Editing an employee
+
+1. Positive Test Case: `edit 1 p/23423423 e/barry@example.com`
+ Expected: Edited the first employee with a new phone number of `23423423` and new email of `barry@example.com`.
+
+2. Negative Test Case: `edit`
+ Expected: Error message since user didn't provide any field that he/she hopes to edit.
+
+### Finding an employee
+
+1. Positive Test Case: `find Alex`
+ Expected: Employees with the name of `Alex` will be displayed.
+
+2. Negative Test Case: `find`
+ Expected: Error message since keyword is not provided.
+
+### Clearing the employee list
+
+1. Positive Test Case: `clear`
+ Expected: There will no longer be any employee's data left in the application.
+
+2. Negative Test Case: `clear asdf`
+ Expected: Error message of `clear` command should not have any arguments.
+
+### Updating an employee's claim budget
+
+Prerequisites: The first employee has a claim budget of over > $500.
+
+1. Positive Test Case: `claim 1 $/-500`
+ Expected: The first employee will have his/her `claim budget` successfully deducted and will display the new remaining amount.
+
+2. Negative Test Case: `claim 1 $/500`
+ Expected: Error message since '+/-' was not provided before the amount, resulting in confusion on whether it is claim/allocation of funds.
+
+### Adding an employee's leaves
+
+1. Positive Test Case: `leave 1 m/3`
+ Expected: First employee in the list will have the month `March` in his/her leave attribute.
+
+2. Negative Test Case: `leave 1`
+ Expected: Error message since the `MONTHS` parameter was not provided.
+
+### Viewing an employee's leaves
+
+1. Positive Test Case: `view_leave m/10 d/IT`
+ Expected: Employees who belong to the IT department and have taken leave in the month of October will be displayed.
+
+2. Positive Test Case: `view_leave`
+ Expected: Employees who have taken leave in the current month (Month in which you are accessing HR Insight) will be displayed.
+
+### Resetting all employees' leaves
+
+1. Positive Test Case: `reset_leaves`
+ Expected: All employees will no longer have any recorded leaves.
+
+2. Negative Test Case: `reset_leaves asdf`
+ Expected: Error message of `reset_leaves` command should not have any arguments.
+
+### Viewing of employees' birthday
+
+1. Positive Test Case: `birthday m/10`
+ Expected: Employees who have birthday in the month of October will be displayed.
+
+2. Negative Test Case: `birthday m/2,3`
+ Expected: Employees who have birthday in the month of Feb and Mar will be displayed.
+
+3. Negative Test Case: `birthday m/69`
+ Expected: Error message since 69 is not a valid month.
+
+### Viewing of employee's details
+
+1. Positive Test Case: `view p/1,3,5`
+ Expected: Phone numbers of the first, third and fifth employee in the list will be displayed.
+
+2. Negative Test Case: `view`
+ Expected: Error message since attribute parameter was not provided.
+
+### Sorting the employee list
+
+1. Positive Test Case: `sort name`
+ Expected: Employees in the list will have their names sorted ascending.
+
+2. Negative Test Case: `sort`
+ Expected: Error message since parameter is not provided.
+
+### Undoing previous commands
+
+1. Undo a command when there is a command that modified the employee list previously. (Example: `delete 1`)
+ - Positive Test Case: `undo`
+ Expected: Restore the employee's details that was previously deleted, which in this case was employee 1.
- 1. Re-launch the app by double-clicking the jar file.
- Expected: The most recent window size and location is retained.
+2. Undo a command when there is no command that modified the internship book previously.
+ - Negative Test Case: `undo`
+ Expected: Error message since no command was executed prior.
-1. _{ more test cases … }_
+### Redoing previous undone commands
-### Deleting a person
+1. Redo a command when there is a command to redo.
+ Prerequisites: Executes any command that modifies the employee list, followed by undo command. (Example: `delete 1` then `undo`)
+ - Positive Test Case: `redo`
+ Expected: Delete the first employee in the list again.
-1. Deleting a person while all persons are being shown
+2. Redo a command when there is no command to redo. You cannot redo your most recent undone command if, after your last `undo`, you execute another command(s) that modifies the employee list.
+ - Negative Test Case: `redo`
+ Expected: Error message since no command available to redo.
- 1. Prerequisites: List all persons using the `list` command. Multiple persons in the list.
+### Exporting employee's details
- 1. Test case: `delete 1`
- Expected: First contact is deleted from the list. Details of the deleted contact shown in the status message. Timestamp in the status bar is updated.
+1. Positive Test Case: `export all_hands`
+ Expected: A CSV file with name `all_hands` will be produced in the `Exported_CSVs` folder with attributes for all employees.
- 1. Test case: `delete 0`
- Expected: No person is deleted. Error details shown in the status message. Status bar remains the same.
+2. Negative Test Case: `export`
+ Expected: Error message since no `file_name` parameter is provided.
- 1. Other incorrect delete commands to try: `delete`, `delete x`, `...` (where x is larger than the list size)
- Expected: Similar to previous.
+### Changing the Application Theme
-1. _{ more test cases … }_
+1. Positive Test Case: `theme light`
+ Expected: GUI to change from black background color to white background color. The text is also expected to change from white to black.
-### Saving data
+2. Negative Test Case: `theme pink`
+ Expected: Error message since pink is not part of the correct `THEME_NAME` parameter.
-1. Dealing with missing/corrupted data files
+## **Appendix C: Planned Enhancements**
- 1. _{explain how to simulate a missing/corrupted file, and the expected behavior}_
+### More detailed leave records [Coming soon]
-1. _{ more test cases … }_
+Currently, HR Insight only records employees’ leave months. In v2.0, we will record the exact dates of employees’ leaves to provide more detailed leave records.
diff --git a/docs/UserGuide.md b/docs/UserGuide.md
index 57437026c7b..6bc6773f381 100644
--- a/docs/UserGuide.md
+++ b/docs/UserGuide.md
@@ -3,41 +3,85 @@ layout: page
title: User Guide
---
-AddressBook Level 3 (AB3) is a **desktop app for managing contacts, optimized for use via a Command Line Interface** (CLI) while still having the benefits of a Graphical User Interface (GUI). If you can type fast, AB3 can get your contact management tasks done faster than traditional GUI apps.
+## Overview
+HR Insight is a **desktop app for HR people, optimized for use via a Command Line Interface** (CLI) while still having the benefits of a Graphical User Interface (GUI).
+The _purpose_ of this app is to provide **HR employees** a _centralized employee management system_ to better manage all employees' details and **improve the
+efficiency** of their workflow.
+
+HR Insight proves to be a particularly valuable tool for professionals specializing in the domains of _Employee Benefits_, _Employee Engagement_ and _Administration_.
+
+- _Employee Benefits_ in terms of keeping count of employees' leaves and claims.
+- _Employee Engagement_ in terms of tracking employees' birthday.
+- _Administration_ in terms of keeping record of employees' details.
+
+---
* Table of Contents
{:toc}
---------------------------------------------------------------------------------------------------------------------
+---
## Quick start
1. Ensure you have Java `11` or above installed in your Computer.
-1. Download the latest `addressbook.jar` from [here](https://github.com/se-edu/addressbook-level3/releases).
+2. Download the latest `HRInsight.jar` from [here](https://github.com/AY2324S1-CS2103-F13-2/tp/releases).
-1. Copy the file to the folder you want to use as the _home folder_ for your AddressBook.
+3. Copy the file to the folder you want to use as the _home folder_ for your HR Insight.
-1. Open a command terminal, `cd` into the folder you put the jar file in, and use the `java -jar addressbook.jar` command to run the application.
- A GUI similar to the below should appear in a few seconds. Note how the app contains some sample data.
+4. Open a command terminal, `cd` into the folder you put the jar file in, and use the `java -jar HRInsight.jar` command to run the application.
+
+ A GUI similar to the one below should appear in a few seconds. Note how the app contains some sample data.
![Ui](images/Ui.png)
-1. Type the command in the command box and press Enter to execute it. e.g. typing **`help`** and pressing Enter will open the help window.
+5. Type the command in the command box and press Enter to execute it. e.g. typing **`help`** and pressing Enter will open the help window.
Some example commands you can try:
- * `list` : Lists all contacts.
+ - `help` : Opens a web browser tab explaining the features HR Insight offers.
+
+ - `add n/John Doe p/87654321 e/john.doe@gmail.com a/Tokyo s/5000 b/2000 d/Sales dob/1992-07-21` :
+ Adds an employee named `John Doe` to the employee list.
+
+ - `list` : Lists all the details of an organization's employees.
+
+ - `delete 3` : Deletes the 3rd employee shown in the current list.
+
+ - `edit 1 p/1234567` : Edits phone attribute for the 1st employee in the list.
+
+ - `find` : Finds employees whose names match any of the given keywords.
+
+ - `clear` : Deletes all employees from the database.
+
+ - `claim 2 $/-60` : Deducts $60 from the claim budget of the 2nd employee in the list.
+
+ - `leave 1 m/1` : Indicates 1st employee in the list will be taking leave in Jan.
+
+ - `view_leave m/2` : Displays employees who have taken leaves in the Feb.
+
+ - `reset_leaves` : Resets all employees to have no recorded leaves.
+
+ - `birthday m/3` : Displays employees who are born in Mar.
- * `add n/John Doe p/98765432 e/johnd@example.com a/John street, block 123, #01-01` : Adds a contact named `John Doe` to the Address Book.
+ - `view n/1,2` : View respective names of the 1st and 2nd employee in the list.
- * `delete 3` : Deletes the 3rd contact shown in the current list.
+ - `sort phone` : Sorts employees based on their phone numbers in ascending order.
- * `clear` : Deletes all contacts.
+ - `undo` : Undo the most recent commands that modified the employee list.
- * `exit` : Exits the app.
+ - `redo` : Redo the most recent commands that was undone.
-1. Refer to the [Features](#features) below for details of each command.
+ - `export all_employee` : Exports employees' data into csv with filename of all_employee.csv.
---------------------------------------------------------------------------------------------------------------------
+ - `theme light` : Change the application theme to light theme.
+
+ - `exit` : Exits the app.
+
+6. You can navigate through your previous commands using ↑ or ↓ on your keyboard, just like your computer's CLI or terminal.
+
+
+7. Refer to the [Features](#features) below for details of each command.
+
+---
## Features
@@ -45,153 +89,416 @@ AddressBook Level 3 (AB3) is a **desktop app for managing contacts, optimized fo
**:information_source: Notes about the command format:**
-* Words in `UPPER_CASE` are the parameters to be supplied by the user.
- e.g. in `add n/NAME`, `NAME` is a parameter which can be used as `add n/John Doe`.
+- Words in `UPPER_CASE` are the parameters to be supplied by the user.
+ e.g. In `add n/NAME`, `NAME` is a parameter that can be used as `add n/John Doe`.
-* Items in square brackets are optional.
- e.g `n/NAME [t/TAG]` can be used as `n/John Doe t/friend` or as `n/John Doe`.
+- Items in square brackets are optional.
+ e.g `list [d/DEPARTMENT]` can be used as `list` or as `list d/Engineering`.
-* Items with `…` after them can be used multiple times including zero times.
- e.g. `[t/TAG]…` can be used as ` ` (i.e. 0 times), `t/friend`, `t/friend t/family` etc.
+- Parameters can be in any order.
+ e.g. If the command specifies `n/NAME p/PHONE_NUMBER`, `p/PHONE_NUMBER n/NAME` is also acceptable.
-* Parameters can be in any order.
- e.g. if the command specifies `n/NAME p/PHONE_NUMBER`, `p/PHONE_NUMBER n/NAME` is also acceptable.
+- Parameters given outside the command format will throw an error or affect other parameters.
+ e.g. `list [d/DEPARTMENT]` only accepts `d/` parameter. `list z/all` will throw an error.
-* Extraneous parameters for commands that do not take in parameters (such as `help`, `list`, `exit` and `clear`) will be ignored.
- e.g. if the command specifies `help 123`, it will be interpreted as `help`.
+- We allow all employee names, not limited to alphanumeric names, to accommodate names such as `X AE A-Xii`, `Dr. Adam Smith, Ph.D.`, and `$helly`.
-* If you are using a PDF version of this document, be careful when copying and pasting commands that span multiple lines as space characters surrounding line-breaks may be omitted when copied over to the application.
+- All words in the parameters given cannot start with the prefixes in that command.
+ e.g. `add` command requires `n/ p/ e/ a/ s/ b/ d/ dob/` prefixes.
+ Therefore, names given in `add` command cannot contain these prefixes because they have been reserved for that command.
+ This constraint applies for all words in all parameters in that command.
+ To accommodate names with `s/o` or `d/o`, we recommend to use `S/O` or `D/O` instead.
+
+- If you are using a PDF version of this document, be careful when copying and pasting commands that span multiple lines as space characters surrounding line-breaks may be omitted when copied over to the application.
### Viewing help : `help`
-Shows a message explaning how to access the help page.
+Automatically opens a new tab in your default browser to this User Guide page.
-![help message](images/helpMessage.png)
+If HRInsight is unable to redirect you to the page, it will show a dialog box containing URL to this page.
Format: `help`
+### Adding an employee: `add`
-### Adding a person: `add`
+Adds an employee to the employee list.
-Adds a person to the address book.
+Format: `add n/NAME p/PHONE_NUMBER e/EMAIL a/ADDRESS s/SALARY b/CLAIM_BUDGET d/DEPARTMENT dob/BIRTH_DATE (YYYY-MM-DD)`
-Format: `add n/NAME p/PHONE_NUMBER e/EMAIL a/ADDRESS [t/TAG]…`
+Examples:
-:bulb: **Tip:**
-A person can have any number of tags (including 0)
-
+- `add n/Adam p/12345678 e/adam@gmail.com a/Singapore s/10000 b/5000 d/Engineering dob/2000-01-01`
+- `add n/John Doe p/87654321 e/john.doe@gmail.com a/Tokyo s/5000 b/2000 d/Sales dob/1992-07-21`
+- `add n/Tharman Shanmugaratnam p/98723459 e/tharman@gmail.com a/Istana, Singapore s/10000 b/10000 d/President dob/1957-02-25`
+
+Executing command: `add n/Adam p/12345678 e/adam@gmail.com a/Singapore s/10000 b/5000 d/Engineering dob/2000-01-01`
+
+![AddEmployeeBeforeAfter](images/AddIndividual.png)
+
+### Listing all employees : `list`
+
+Lists all the details of an organization’s employees, or list all employees of a specified department.
+
+Format: `list [d/DEPARTMENT]`
Examples:
-* `add n/John Doe p/98765432 e/johnd@example.com a/John street, block 123, #01-01`
-* `add n/Betsy Crowe t/friend e/betsycrowe@example.com a/Newgate Prison p/1234567 t/criminal`
-### Listing all persons : `list`
+- `list` Lists all employees in the employee list.
+- `list d/Engineering` Lists all employees in the Engineering department.
+- `list d/Sales` Lists all employees in the Sales department.
-Shows a list of all persons in the address book.
+Executing command: `list d/Engineering`
-Format: `list`
+![ListEmployeesBeforeAfter](images/ListEmployeesBefore.png)
-### Editing a person : `edit`
+![ListEmployeesBeforeAfter](images/ListEmployeesAfter.png)
-Edits an existing person in the address book.
+### Deleting an employee : `delete`
-Format: `edit INDEX [n/NAME] [p/PHONE] [e/EMAIL] [a/ADDRESS] [t/TAG]…`
+Delete an employee in the employee list by the specified index.
+
+Format: `delete INDEX`
-* Edits the person at the specified `INDEX`. The index refers to the index number shown in the displayed person list. The index **must be a positive integer** 1, 2, 3, …
-* At least one of the optional fields must be provided.
-* Existing values will be updated to the input values.
-* When editing tags, the existing tags of the person will be removed i.e adding of tags is not cumulative.
-* You can remove all the person’s tags by typing `t/` without
- specifying any tags after it.
+- Deletes the employee at the specified `INDEX`.
+- The index refers to the index number shown in the displayed employee list.
+- The index **must be a positive integer**: 1, 2, 3, …
Examples:
-* `edit 1 p/91234567 e/johndoe@example.com` Edits the phone number and email address of the 1st person to be `91234567` and `johndoe@example.com` respectively.
-* `edit 2 n/Betsy Crower t/` Edits the name of the 2nd person to be `Betsy Crower` and clears all existing tags.
-### Locating persons by name: `find`
+- `list` followed by `delete 2` deletes the 2nd person in the employee list.
+- `find Betsy` followed by `delete 1` deletes the 1st person in the results of the `find` command.
+- `list d/Engineering` followed by `delete 1` deletes the 1st person in the employee list.
-Finds persons whose names contain any of the given keywords.
+Executing command: `delete 1`
-Format: `find KEYWORD [MORE_KEYWORDS]`
+![DelEmployeeBeforeAfter](images/DeleteEmployeeBefore.png)
+
+![DelEmployeeBeforeAfter](images/DeleteEmployeeAfter.png)
+
+### Editing an employee's information : `edit`
+
+Edits an existing employee in the employee list by the specified index.
-* The search is case-insensitive. e.g `hans` will match `Hans`
-* The order of the keywords does not matter. e.g. `Hans Bo` will match `Bo Hans`
-* Only the name is searched.
-* Only full words will be matched e.g. `Han` will not match `Hans`
-* Persons matching at least one keyword will be returned (i.e. `OR` search).
- e.g. `Hans Bo` will return `Hans Gruber`, `Bo Yang`
+Format: `edit INDEX [n/NAME] [p/PHONE] [e/EMAIL] [a/ADDRESS] [s/SALARY] [d/DEPARTMENT] [dob/BIRTH_DATE (YYYY-MM-DD)]`
+
+- Edits the employee at the specified `INDEX`. The index refers to the index number shown in the displayed employee list.
+ The index **must be a positive integer** 1, 2, 3, …
+- At least **one** of the optional fields must be provided.
+- Existing values will be updated to the input values.
+- Some prefixes allow for additional descriptors to accommodate a variety of input data that fits within the context of the field, provided they conform to the input requirements.
+ - For `[n/NAME]`, specific descriptors (S/O, D/O, etc.) are allowed. Ensure that the full input between the prefix and the next space or prefix is intended as part of the name.
Examples:
-* `find John` returns `john` and `John Doe`
-* `find alex david` returns `Alex Yeoh`, `David Li`
- ![result for 'find alex david'](images/findAlexDavidResult.png)
-### Deleting a person : `delete`
+- `edit 1 p/23423423 e/barry@example.com` Edits the phone number and email address of the 1st person to be `23423423` and `barry@example.com` respectively.
+- `edit 1 n/thomas S/O anthony a/Serangoon` Edits the name and address of the 1st person to be `thomas S/O anthony` and `Serangoon` respectively.
+- `edit 2 s/1000 d/Sales dob/2000-01-01` Edits the salary, department and DOB of the 2nd person to be `$1000`, `Sales` and `1 January 2000` respectively.
-Deletes the specified person from the address book.
+Executing command: `edit 1 p/23423423 e/barry@gmail.com`
-Format: `delete INDEX`
+![EditEmployeeBeforeAfter](images/EditEmployeeBefore.png)
+
+![EditEmployeeBeforeAfter](images/EditEmployeeAfter.png)
+
+### Finding employees by name: `find`
-* Deletes the person at the specified `INDEX`.
-* The index refers to the index number shown in the displayed person list.
-* The index **must be a positive integer** 1, 2, 3, …
+Finds employees whose names match any of the given keywords.
+
+Format: `find KEYWORD [MORE_KEYWORDS]`
+
+- The search is case-insensitive, e.g `hans` will match `Hans`.
+- The order of the keywords does not matter, e.g. `Hans Bo` will match `Bo Hans`.
+- Only the name is searched.
+- Only full words will be matched, e.g. `Han` will not match `Hans`.
+- Persons matching at least one keyword will be returned (i.e., `OR` search).
+ e.g. `Hans Bo` will return `Hans Gruber`, `Bo Yang`.
Examples:
-* `list` followed by `delete 2` deletes the 2nd person in the address book.
-* `find Betsy` followed by `delete 1` deletes the 1st person in the results of the `find` command.
-### Clearing all entries : `clear`
+- `find John` returns `john` and `John Doe`
+- `find alex david` returns `Alex Yeoh`, `David Li`
+- `find Bernice` returns `bernice`.
+
+Executing command: `find Bernice`
+
+![FindBeforeAfter](images/FindEmployeeBefore.png)
-Clears all entries from the address book.
+![FindBeforeAfter](images/FindEmployeeAfter.png)
+
+### Clearing all entries: `clear`
+
+Clear all entries from the employee list.
Format: `clear`
-### Exiting the program : `exit`
+Executing Command: `clear`
+
+![ClearBeforeAfter](images/ClearCommand.png)
+
+### Managing employee's claims: `claim`
+
+Performs adjustments to employee's claims.
+
+Format: `claim INDEX $/CLAIM_AMOUNT`
+
+- The `INDEX` refers to the index number shown in the displayed employee list.
+- The index **must be a positive integer** 1,2,3, ...
+- The `CLAIM_AMOUNT` should consist of either + or - which symbolizes adding/deducting respectively followed by the **amount.**
+ - If the `CLAIM_AMOUNT` is _positive_, it symbolizes **allocation of more** funds to the user's claim budget.
+ - If the `CLAIM_AMOUNT` is _negative_, it symbolizes **deduction of funds** from the user's claim budget.
+
+Examples:
+
+- `list` followed by `claim 1 $/-500` deducts $50 from employee 1's claim budget.
+- `list` followed by `claim 2 $/+60` allocates additional $60 to employee 2's claim budget.
+- `list` followed by `claim 3 $-1000` deducts $1000 from employee 3's claim budget.
+
+Executing command: `claim 1 $/-500`
+
+![ClaimCommandBeforeAfter](images/ClaimBefore.png)
+
+![ClaimCommandBeforeAfter](images/ClaimAfter.png)
+
+### Adding employee’s Leave: `leave`
+
+Adds leave months for an employee.
+
+Format: `leave INDEX m/MONTHS`
+
+- The `INDEX` refers to the index number shown in the displayed employee list.
+- The `INDEX` **must be a positive integer** 1,2,3, ...
+- The `MONTHS` refers to the month of the leave the employee is taking in integer format (between 1-12).
+- Positive `MONTHS` add leaves on the specified months and negative `MONTHS` remove them.
+- Trailing commas in `MONTHS` (`leave 1 m/1,2,3,,,`) will be ignored, but empty months elsewhere (`leave 1 m/1,,,2`) will raise an error.
+
+Examples:
+- `list` followed by `leave 1 m/3,4` adds leaves in March and April for the 1st employee in the list.
+- `list` followed by `leave 3 m/11,-12` adds a leave in Nov and removes a leave in Dec for the 3rd employee in the list.
+- `list` followed by `leave 2 m/3` adds a leave in March for the 2nd employee in the list.
+
+Executing command: `leave 1 m/1,3`
+
+![leaveBeforeAfter](images/LeaveEmployeeBefore.png)
+
+![leaveBeforeAfter](images/LeaveEmployeeAfter.png)
+
+### Viewing all employees' leaves : `view_leave`
+
+Views all employees who are on leave, with optional filters of month and department.
+
+Format: `view_leave [m/MONTHS] [d/DEPARTMENT]`
+
+- Gives a list of **all employees** who have leaves planned for the year.
+- The `MONTHS` and `DEPARTMENT` are optional arguments.
+- Multiple `MONTHS` can be specified, in which employees who have planned leaves in any of the specified months will be shown ("either or" relationship).
+- When specifying multiple `MONTHS`, the months should be separated with commas with no spaces.
+- If no one in the specified department has planned leave dates for the given month(s), an output indicating **no employees is taking leave** is shown.
+- Trailing commas in `MONTHS` (`view_leave m/1,2,3,,,`) will be ignored, but empty months elsewhere (`view_leave m/1,,,2`) will raise an error.
+
+
+Examples:
+- `view_leave` displays all employees who have planned leave dates in the current year.
+- `view_leave m/10` displays all employees who are taking leave in October.
+- `view_leave m/1,2` displays all employees who are taking leave in January or February.
+- `view_leave m/10 d/IT` displays all employees in the IT department who are taking leave in October.
+
+Executing command: `view_leave m/1,3`
+
+![viewLeaveBeforeAfter](images/ViewLeaveBefore.png)
+
+![viewLeaveBeforeAfter](images/ViewLeaveAfter.png)
+
+### Resetting all employees' leaves : `reset_leaves`
+
+Reset all employees to have no recorded leaves.
+
+Format: `reset_leaves`
+
+Executing command: `reset_leaves`
+
+![ResetLeavesBeforeAfter](images/ResetLeaveBefore.png)
+
+![ResetLeavesBeforeAfter](images/ResetLeaveAfter.png)
+
+### Viewing all birthdays in a given month : `birthday`
+
+Views all employees’ birthday in the given months.
+
+Format: `birthday [m/MONTH(s)]`
+
+- Gives a list of **all employees** who have upcoming birthdays in the **inquired month(s)**.
+- The month argument is optional. If **no month** is provided, the birthdays in the **current month** are listed.
+- If there is no birthday in the month provided, return **No employees have birthdays in this month**.
+- Months are separated using ",", e.g. to inquire the employees who have birthdays in Mar and Apr, the input is `birthday m/3,4`.
+- Trailing commas in `MONTH(s)` (`birthday m/1,2,3,,,`) will be ignored, but empty months elsewhere (`birthday m/1,,,2`) will raise an error.
+
+Examples:
+- `birthday` displays all employees who have their birthday in the current month.
+- `birthday m/10` displays all employees who have their birthday in the month of October.
+- `birthday m/1,3,4` displays all employees who have their birthday in the month of Jan, Mar and Apr.
+
+Executing command: `birthday m/1`
+
+![BirthdayBeforeAfter](images/BirthdayBefore.png)
+
+![BirthdayBeforeAfter](images/BirthdayAfter.png)
+
+### Viewing employee's details: `view`
+
+Views employee(s)'s personal attribute.
+
+Format: `view [n/INDEX] [a/INDEX] [e/INDEX] [p/INDEX] [s/INDEX] [b/INDEX] [d/INDEX] [dob/INDEX]`
+
+- ViewCommand provides overview of employee(s)'s attributes.
+- Maximum of one prefix is allowed. This means the user can only view one attribute at a time.
+- INDEX refers to the index number shown in the displayed employee list.
+- INDEX parameters can either be a single digit or digits separated by ",".
+
+Examples:
+- `view s/1,2` displays the 1st and 2nd employee respective salaries.
+- `view a/3,4` displays the 3rd and 4th employee respective addresses.
+- `view dob/1,5` displays the 1st and 5th employee respective DOB.
+
+Executing command: `view p/1,5`
+
+![ViewAttributeBeforeAfter](images/viewAttribute.png)
+
+### Sorting the employee list: `sort`
+
+Sorts the employee list based on the given parameter.
+
+Format: `sort name / phone / email / address / salary / claim / dep / dob [desc]`
+
+- Choose one parameter from `name / phone / email / address / salary / claim / dep / dob` to sort.
+- Put `desc` to sort in descending order.
+
+Examples:
+- `sort name` to sort the employee list based on name in ascending order.
+- `sort salary desc` to sort the employee list based on salary in descending order.
+- `sort claim` to sort the employee list based on claim budget in ascending order.
-Exits the program.
+Executing command: `sort name desc`
+
+![SortNamesBeforeAfter](images/SortNames.png)
+
+### Undoing previous commands: `undo`
+
+Undo the most recent commands that modified the employee list, i.e., `add`, `edit`, `delete`, `leave`, `reset_leaves`, `clear`, `sort`, `redo` commands.
+
+Format: `undo`
+
+Executing command: `undo`
+
+![UndoBeforeAfter](images/UndoDelete.png)
+
+### Redoing previous undone commands: `redo`
+
+Redo the most recent commands that was undone.
+
+Format: `redo`
+
+You cannot redo your most recent undone command if, after your last `undo`, you execute another command(s) that modifies the employee list.
+
+Executing command: `redo`
+
+![RedoBeforeAfter](images/Redo.png)
+
+### Exporting employee's details: `export`
+
+Export employee's details into a csv file format.
+
+Format: `export [file_name]`
+
+- ExportCommand provides HR employees a way to download employees' data into CSV format.
+- The exported file_name.csv will be found in the Exported_CSVs folder.
+- You must provide a file_name. File_name can comprise alphanumeric and special characters.
+- To export only a subset of employees, user should perform filtering before exporting.
+
+Examples:
+- `list` followed by `export all_data` will download all employees' attributes into a csv file.
+- `list d/Engineering` then `export engineering_team` will only download employees in engineering department.
+- `birthday m/1` then `export birthday_Jan` will only download employees with birthdays in January.
+
+![ExportCSVBeforeAfter](images/export.png)
+
+### Changing the Application Theme : `theme`
+
+Changes the theme of the application according to the current available options.
+
+Format: `theme THEME_NAME`
+
+Current available themes:
+`dark`, `light`, `red`, `green`, `blue`.
+
+Examples:
+- `theme red` Changes the application theme to the red theme.
+- `theme light` Changes the application theme to light theme.
+- `theme green` Changes the application theme to green theme.
+
+Executing command: `theme light`
+
+![changeThemeBeforeAfter](images/ThemeChange.png)
+
+### Exiting the app: `exit`
+
+Exits the app.
Format: `exit`
### Saving the data
-AddressBook data are saved in the hard disk automatically after any command that changes the data. There is no need to save manually.
+HR Insight data are saved in the hard disk automatically after any command that changes the data. There is no need to save manually.
### Editing the data file
-AddressBook data are saved automatically as a JSON file `[JAR file location]/data/addressbook.json`. Advanced users are welcome to update data directly by editing that data file.
+HR Insight data are saved automatically as a JSON file `[JAR file location]/data/hrinsight.json`. Advanced users are welcome to update data directly by editing that data file.
-:exclamation: **Caution:**
-If your changes to the data file makes its format invalid, AddressBook will discard all data and start with an empty data file at the next run. Hence, it is recommended to take a backup of the file before editing it.
-
+
-### Archiving data files `[coming in v2.0]`
+**Caution:**
+If your changes to the data file make its format invalid, HR Insight will discard all data and start with an empty data file at the next run. Hence, it is recommended to take a backup of the file before editing it.
+
-_Details coming soon ..._
+### More detailed leave records [Coming soon]
---------------------------------------------------------------------------------------------------------------------
+Currently, HR Insight only records employees' leave months. In v2.0, we will record the exact dates of employees' leaves to provide more detailed leave records.
+
+---
## FAQ
-**Q**: How do I transfer my data to another Computer?
-**A**: Install the app in the other computer and overwrite the empty data file it creates with the file that contains the data of your previous AddressBook home folder.
+**Q**: How do I transfer my data to another computer?
+**A**: Install the app on the other computer and overwrite the empty data file it creates with the file that contains the data of your previous HR Insight home folder.
---------------------------------------------------------------------------------------------------------------------
+---
## Known issues
-1. **When using multiple screens**, if you move the application to a secondary screen, and later switch to using only the primary screen, the GUI will open off-screen. The remedy is to delete the `preferences.json` file created by the application before running the application again.
+1. **When using multiple screens**, if you move the application to a secondary screen and later switch to using only the primary screen, the GUI will open off-screen. The remedy is to delete the `preferences.json` file created by the application before running the application again.
---------------------------------------------------------------------------------------------------------------------
+---
## Command summary
-Action | Format, Examples
---------|------------------
-**Add** | `add n/NAME p/PHONE_NUMBER e/EMAIL a/ADDRESS [t/TAG]…` e.g., `add n/James Ho p/22224444 e/jamesho@example.com a/123, Clementi Rd, 1234665 t/friend t/colleague`
-**Clear** | `clear`
-**Delete** | `delete INDEX` e.g., `delete 3`
-**Edit** | `edit INDEX [n/NAME] [p/PHONE_NUMBER] [e/EMAIL] [a/ADDRESS] [t/TAG]…` e.g.,`edit 2 n/James Lee e/jameslee@example.com`
-**Find** | `find KEYWORD [MORE_KEYWORDS]` e.g., `find James Jake`
-**List** | `list`
-**Help** | `help`
+| Action | Format, Examples |
+|---------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
+| **Help** | `help` |
+| **Add** | `add n/NAME p/PHONE_NUMBER e/EMAIL a/ADDRESS s/SALARY b/CLAIM_BUDGET d/DEPARTMENT dob/BIRTH_DATE (YYYY-MM-DD)` e.g., `add n/John Doe p/87654321 e/john.doe@gmail.com a/Tokyo s/5000 b/2000 d/Sales dob/1992-07-21` |
+| **List** | `list [d/DEPARTMENT]` |
+| **Delete** | `delete INDEX` e.g., `delete 3` |
+| **Edit** | `edit INDEX [n/NAME] [p/PHONE] [e/EMAIL] [a/ADDRESS] [s/SALARY] [d/DEPARTMENT] [dob/BIRTH_DATE (YYYY-MM-DD)]` e.g., `edit 1 p/23423423 e/barry@example.com` |
+| **Find** | `find KEYWORD [MORE_KEYWORDS]` e.g., `find James Jake` |
+| **Clear** | `clear` |
+| **Claim** | `claim INDEX $/CLAIM_AMOUNT` e.g., `claim 1 $/-500` |
+| **Add Leave** | `leave INDEX m/MONTHS` e.g., `leave 1 m/3,-4` |
+| **View Leave** | `view_leave INDEX m/Month d/DEPARTMENT` e.g.,`view_leave m/10 d/IT` |
+| **Reset Leaves** | `reset_leaves` |
+| **View Birthdays** | `birthday [m/MONTH]` e.g., `birthday 10` |
+| **View Attributes** | `view [n/INDEX] [a/INDEX] [e/INDEX] [p/INDEX] [s/INDEX] [b/INDEX] [d/INDEX] [dob/INDEX]` e.g., `view s/1,2` |
+| **Sort** | `sort name / phone / email / address / salary / claim / dep / dob [desc]` |
+| **Undo** | `undo` |
+| **Redo** | `redo` |
+| **Export Data** | `export [file_name]` e.g., `export engineering_dept` |
+| **Change Theme** | `theme THEME_NAME` e.g., `theme light` |
+| **Exit** | `exit` |
diff --git a/docs/_config.yml b/docs/_config.yml
index 6bd245d8f4e..63878d3f0f9 100644
--- a/docs/_config.yml
+++ b/docs/_config.yml
@@ -1,4 +1,4 @@
-title: "AB-3"
+title: "HR Insight"
theme: minima
header_pages:
@@ -8,7 +8,7 @@ header_pages:
markdown: kramdown
-repository: "se-edu/addressbook-level3"
+repository: "AY2324S1-CS2103-F13-2/tp"
github_icon: "images/github-icon.png"
plugins:
diff --git a/docs/_markbind/layouts/default.md b/docs/_markbind/layouts/default.md
new file mode 100644
index 00000000000..f0b33e58446
--- /dev/null
+++ b/docs/_markbind/layouts/default.md
@@ -0,0 +1,66 @@
+
+
+
+
+
+
+
+
+
+
+
+* [Home]({{ baseUrl }}/index.html)
+* [User Guide]({{ baseUrl }}/UserGuide.html) :expanded:
+ * [Quick Start]({{ baseUrl }}/UserGuide.html#quick-start)
+ * [Features]({{ baseUrl }}/UserGuide.html#features)
+ * [FAQ]({{ baseUrl }}/UserGuide.html#faq)
+ * [Command Summary]({{ baseUrl }}/UserGuide.html#faq)
+* [Developer Guide]({{ baseUrl }}/DeveloperGuide.html) :expanded:
+ * [Acknowledgements]({{ baseUrl }}/DeveloperGuide.html#acknowledgements)
+ * [Setting Up]({{ baseUrl }}/DeveloperGuide.html#setting-up-getting-started)
+ * [Design]({{ baseUrl }}/DeveloperGuide.html#design)
+ * [Implementation]({{ baseUrl }}/DeveloperGuide.html#implementation)
+ * [Documentation, logging, testing, configuration, dev-ops]({{ baseUrl }}/DeveloperGuide.html#documentation-logging-testing-configuration-dev-ops)
+ * [Appendix: Requirements]({{ baseUrl }}/DeveloperGuide.html#appendix-requirements)
+ * [Appendix: Instructions for manual testing]({{ baseUrl }}/DeveloperGuide.html#appendix-instructions-for-manual-testing)
+* Tutorials
+ * [Tracing code]({{ baseUrl }}/tutorials/TracingCode.html)
+ * [Adding a command]({{ baseUrl }}/tutorials/AddRemark.html)
+ * [Removing Fields]({{ baseUrl }}/tutorials/RemovingFields.html)
+* [About Us]({{ baseUrl }}/AboutUs.html)
+
+
+
+
+ {{ content }}
+
+
+
+
+
+
+
+
+
+
+
[**Powered by** {{MarkBind}}, generated on {{timestamp}}]
+
+
diff --git a/docs/_markbind/variables.json b/docs/_markbind/variables.json
new file mode 100644
index 00000000000..9d89eb0358b
--- /dev/null
+++ b/docs/_markbind/variables.json
@@ -0,0 +1,3 @@
+{
+ "jsonVariableExample": "Your variables can be defined here as well"
+}
diff --git a/docs/_markbind/variables.md b/docs/_markbind/variables.md
new file mode 100644
index 00000000000..89ae5318fa4
--- /dev/null
+++ b/docs/_markbind/variables.md
@@ -0,0 +1,4 @@
+
+To inject this HTML segment in your markbind files, use {{ example }} where you want to place it.
+More generally, surround the segment's id with double curly braces.
+
diff --git a/docs/diagrams/BirthdayCommand.puml b/docs/diagrams/BirthdayCommand.puml
new file mode 100644
index 00000000000..ed00e912fbb
--- /dev/null
+++ b/docs/diagrams/BirthdayCommand.puml
@@ -0,0 +1,41 @@
+@startuml
+!include style.puml
+skinparam ArrowFontStyle plain
+
+box Logic LOGIC_COLOR_T1
+participant ":LogicManager" as LogicManager LOGIC_COLOR
+participant ":AddressBookParser" as AddressBookParser LOGIC_COLOR
+participant ":BirthdayCommandParser" as BirthdayCommandParser LOGIC_COLOR
+participant ":BirthdayCommand" as BirthdayCommand LOGIC_COLOR
+end box
+
+[-> LogicManager : execute(birthday m/1)
+activate LogicManager
+
+LogicManager -> AddressBookParser : parseCommand(birthday m/1)
+activate AddressBookParser
+
+create BirthdayCommandParser
+AddressBookParser -> BirthdayCommandParser : parse(m/1)
+activate BirthdayCommandParser
+
+create BirthdayCommand
+BirthdayCommandParser -> BirthdayCommand : BirthdayCommand(1)
+activate BirthdayCommand
+
+BirthdayCommand --> BirthdayCommandParser
+deactivate BirthdayCommand
+
+BirthdayCommandParser --> AddressBookParser
+deactivate BirthdayCommandParser
+
+AddressBookParser --> LogicManager : b
+deactivate AddressBookParser
+
+LogicManager -> BirthdayCommand : execute()
+activate BirthdayCommand
+
+BirthdayCommand -> LogicManager
+deactivate BirthdayCommand
+
+@enduml
diff --git a/docs/diagrams/ExportSequenceDiagram.puml b/docs/diagrams/ExportSequenceDiagram.puml
new file mode 100644
index 00000000000..a83200e32fa
--- /dev/null
+++ b/docs/diagrams/ExportSequenceDiagram.puml
@@ -0,0 +1,51 @@
+@startuml
+!include style.puml
+skinparam ArrowFontStyle plain
+
+box Logic LOGIC_COLOR_T1
+participant ":LogicManager" as LogicManager LOGIC_COLOR
+participant ":ExportCommandParser" as ExportCommandParser LOGIC_COLOR
+participant "e:ExportCommand" as ExportCommand LOGIC_COLOR
+end box
+
+box Model MODEL_COLOR_T1
+participant ":Model" as Model MODEL_COLOR
+end box
+
+[-> LogicManager : execute(export)
+activate LogicManager
+
+LogicManager -> ExportCommandParser: parseCommand(export)
+activate ExportCommandParser
+
+ExportCommandParser -> ExportCommandParser : nameChecker(nameArgs)
+
+Create ExportCommand
+ExportCommandParser -> ExportCommand
+activate ExportCommand
+
+ExportCommand --> ExportCommandParser
+deactivate ExportCommand
+
+ExportCommandParser --> LogicManager
+deactivate ExportCommandParser
+
+LogicManager -> ExportCommand : execute(nameArgs)
+Activate ExportCommand
+
+ExportCommand -> Model : getFilteredPersonList()
+activate Model
+Model --> ExportCommand
+deactivate Model
+
+ExportCommand -> ExportCommand : generateListPeople()
+ExportCommand -> ExportCommand : generateFile()
+ExportCommand -> ExportCommand : convertToCsv()
+ExportCommand --> LogicManager : nameArgs.csv
+destroy ExportCommand
+
+
+[<--LogicManager
+deactivate LogicManager
+
+@enduml
diff --git a/docs/diagrams/LeaveCommandSequenceDiagram.puml b/docs/diagrams/LeaveCommandSequenceDiagram.puml
new file mode 100644
index 00000000000..f3023b3bda9
--- /dev/null
+++ b/docs/diagrams/LeaveCommandSequenceDiagram.puml
@@ -0,0 +1,38 @@
+@startuml
+!include style.puml
+skinparam ArrowFontStyle plain
+
+box Logic LOGIC_COLOR_T1
+participant ":LogicManager" as LogicManager LOGIC_COLOR
+participant ":AddressBookParser" as AddressBookParser LOGIC_COLOR
+participant ":LeaveCommandParser" as LeaveCommandParser LOGIC_COLOR
+participant ":LeaveCommand" as LeaveCommand LOGIC_COLOR
+end box
+
+[-> LogicManager : execute(leave 1 m/3)
+activate LogicManager
+
+LogicManager -> AddressBookParser : parseCommand(leave 1 m/3)
+activate AddressBookParser
+
+create LeaveCommandParser
+AddressBookParser -> LeaveCommandParser : parse( 1 m/3)
+activate LeaveCommandParser
+
+create LeaveCommand
+LeaveCommandParser -> LeaveCommand : LeaveCommand(index, month)
+activate LeaveCommand
+
+LeaveCommand --> LeaveCommandParser
+deactivate LeaveCommand
+
+LeaveCommandParser --> AddressBookParser : LeaveCommand
+deactivate LeaveCommandParser
+
+AddressBookParser --> LogicManager : LeaveCommand
+deactivate AddressBookParser
+
+LogicManager -->[ : CommandResult
+deactivate LogicManager
+
+@enduml
diff --git a/docs/diagrams/ListCommandSequenceDiagram.puml b/docs/diagrams/ListCommandSequenceDiagram.puml
new file mode 100644
index 00000000000..6e65a31bd51
--- /dev/null
+++ b/docs/diagrams/ListCommandSequenceDiagram.puml
@@ -0,0 +1,49 @@
+@startuml
+!include style.puml
+skinparam ArrowFontStyle plain
+
+box Logic LOGIC_COLOR_T1
+participant ":LogicManager" as LogicManager LOGIC_COLOR
+participant ":AddressBookParser" as AddressBookParser LOGIC_COLOR
+participant ":ListCommandParser" as ListCommandParser LOGIC_COLOR
+participant ":ListCommand" as ListCommand LOGIC_COLOR
+end box
+
+box Model MODEL_COLOR_T1
+participant ":MatchingDepartmentPredicate" as MatchingDepartmentPredicate MODEL_COLOR
+end box
+
+[-> LogicManager : execute(list d/engineering)
+activate LogicManager
+
+LogicManager -> AddressBookParser : parseCommand(list d/engineering)
+activate AddressBookParser
+
+create ListCommandParser
+AddressBookParser -> ListCommandParser : parse(d/engineering)
+activate ListCommandParser
+
+create MatchingDepartmentPredicate
+ListCommandParser -> MatchingDepartmentPredicate : ListCommand(predicate)
+activate MatchingDepartmentPredicate
+
+MatchingDepartmentPredicate --> ListCommandParser
+deactivate MatchingDepartmentPredicate
+
+create ListCommand
+ListCommandParser -> ListCommand : ListCommand(predicate)
+activate ListCommand
+
+ListCommand --> ListCommandParser
+deactivate ListCommand
+
+ListCommandParser --> AddressBookParser : ListCommand
+deactivate ListCommandParser
+
+AddressBookParser --> LogicManager : ListCommand
+deactivate AddressBookParser
+
+LogicManager -->[ : CommandResult
+deactivate LogicManager
+
+@enduml
diff --git a/docs/diagrams/ModelClassDiagram.puml b/docs/diagrams/ModelClassDiagram.puml
index 0de5673070d..3a68d5116e1 100644
--- a/docs/diagrams/ModelClassDiagram.puml
+++ b/docs/diagrams/ModelClassDiagram.puml
@@ -11,6 +11,7 @@ Class "<>\nModel" as Model
Class AddressBook
Class ModelManager
Class UserPrefs
+Class AddressBookList
Class UniquePersonList
Class Person
@@ -18,7 +19,10 @@ Class Address
Class Email
Class Name
Class Phone
-Class Tag
+Class Money
+Class Birthday
+Class Department
+Class Leave
Class I #FFFFFF
}
@@ -34,6 +38,8 @@ Model .left.> ReadOnlyAddressBook
ModelManager -left-> "1" AddressBook
ModelManager -right-> "1" UserPrefs
UserPrefs .up.|> ReadOnlyUserPrefs
+ModelManager -down-> "1" AddressBookList
+AddressBookList -up-> "*" AddressBook
AddressBook *--> "1" UniquePersonList
UniquePersonList --> "~* all" Person
@@ -41,7 +47,10 @@ Person *--> Name
Person *--> Phone
Person *--> Email
Person *--> Address
-Person *--> "*" Tag
+Person *--> Money
+Person *--> Birthday
+Person *--> Department
+Person *--> Leave
Person -[hidden]up--> I
UniquePersonList -[hidden]right-> I
diff --git a/docs/diagrams/RedoSequenceDiagram.puml b/docs/diagrams/RedoSequenceDiagram.puml
new file mode 100644
index 00000000000..c2700772db2
--- /dev/null
+++ b/docs/diagrams/RedoSequenceDiagram.puml
@@ -0,0 +1,64 @@
+@startuml
+!include style.puml
+skinparam ArrowFontStyle plain
+
+box Logic LOGIC_COLOR_T1
+participant ":LogicManager" as LogicManager LOGIC_COLOR
+participant ":AddressBookParser" as AddressBookParser LOGIC_COLOR
+participant "r:RedoCommand" as RedoCommand LOGIC_COLOR
+end box
+
+box Model MODEL_COLOR_T1
+participant ":Model" as Model MODEL_COLOR
+participant ":AddressBookList" as AddressBookList MODEL_COLOR
+participant ":AddressBook" as AddressBook MODEL_COLOR
+end box
+[-> LogicManager : execute("redo")
+activate LogicManager
+
+LogicManager -> AddressBookParser : parseCommand("redo")
+activate AddressBookParser
+
+AddressBookParser -> AddressBookParser : ensureEmptyArguments(args)
+
+create RedoCommand
+AddressBookParser -> RedoCommand
+activate RedoCommand
+
+RedoCommand --> AddressBookParser
+deactivate RedoCommand
+
+AddressBookParser --> LogicManager : r
+deactivate AddressBookParser
+
+LogicManager -> RedoCommand : execute()
+activate RedoCommand
+
+RedoCommand -> Model : redo()
+activate Model
+
+Model -> AddressBookList : redo()
+activate AddressBookList
+
+AddressBookList --> Model : addressBook
+
+Model -> AddressBook : resetData(addressBook)
+AddressBook --> Model :
+deactivate AddressBook
+
+Model -> AddressBookList : redoPastCommand()
+
+AddressBookList --> Model : pastCommand
+deactivate AddressBookList
+
+Model --> RedoCommand : pastCommand
+deactivate Model
+
+RedoCommand --> LogicManager : result
+deactivate RedoCommand
+RedoCommand -[hidden]-> LogicManager : result
+destroy RedoCommand
+
+[<--LogicManager
+deactivate LogicManager
+@enduml
diff --git a/docs/diagrams/ThemeCommandSequenceDiagram.puml b/docs/diagrams/ThemeCommandSequenceDiagram.puml
new file mode 100644
index 00000000000..9bb1d314f12
--- /dev/null
+++ b/docs/diagrams/ThemeCommandSequenceDiagram.puml
@@ -0,0 +1,45 @@
+@startuml
+!include style.puml
+skinparam ArrowFontStyle plain
+
+box UI UI_COLOR_T1
+participant ":MainWindow" as MainWindow UI_COLOR
+end box
+
+box Logic LOGIC_COLOR_T1
+participant ":LogicManager" as LogicManager LOGIC_COLOR
+participant ":AddressBookParser" as AddressBookParser LOGIC_COLOR
+participant ":ThemeCommandParser" as ThemeCommandParser LOGIC_COLOR
+participant ":ThemeCommand" as ThemeCommand LOGIC_COLOR
+end box
+
+activate MainWindow
+MainWindow-> LogicManager : executeCommand(theme light)
+activate LogicManager
+
+LogicManager -> AddressBookParser : parseCommand(theme light)
+activate AddressBookParser
+
+create ThemeCommandParser
+AddressBookParser -> ThemeCommandParser : parse( light)
+activate ThemeCommandParser
+
+create ThemeCommand
+ThemeCommandParser -> ThemeCommand : ThemeCommand(LightTheme.css)
+activate ThemeCommand
+
+ThemeCommand --> ThemeCommandParser
+deactivate ThemeCommand
+
+ThemeCommandParser --> AddressBookParser : ThemeCommand
+deactivate ThemeCommandParser
+
+AddressBookParser --> LogicManager : ThemeCommand
+deactivate AddressBookParser
+
+LogicManager --> MainWindow : CommandResult
+deactivate LogicManager
+
+MainWindow -> MainWindow : setTheme(LightTheme.css)
+
+@enduml
diff --git a/docs/diagrams/UndoSequenceDiagram.puml b/docs/diagrams/UndoSequenceDiagram.puml
index 87ff3e9237e..050b1146a0a 100644
--- a/docs/diagrams/UndoSequenceDiagram.puml
+++ b/docs/diagrams/UndoSequenceDiagram.puml
@@ -10,14 +10,17 @@ end box
box Model MODEL_COLOR_T1
participant ":Model" as Model MODEL_COLOR
-participant ":VersionedAddressBook" as VersionedAddressBook MODEL_COLOR
+participant ":AddressBookList" as AddressBookList MODEL_COLOR
+participant ":AddressBook" as AddressBook MODEL_COLOR
end box
-[-> LogicManager : execute(undo)
+[-> LogicManager : execute("undo")
activate LogicManager
-LogicManager -> AddressBookParser : parseCommand(undo)
+LogicManager -> AddressBookParser : parseCommand("undo")
activate AddressBookParser
+AddressBookParser -> AddressBookParser : ensureEmptyArguments(args)
+
create UndoCommand
AddressBookParser -> UndoCommand
activate UndoCommand
@@ -31,17 +34,24 @@ deactivate AddressBookParser
LogicManager -> UndoCommand : execute()
activate UndoCommand
-UndoCommand -> Model : undoAddressBook()
+UndoCommand -> Model : undo()
activate Model
-Model -> VersionedAddressBook : undo()
-activate VersionedAddressBook
+Model -> AddressBookList : undo()
+activate AddressBookList
+
+AddressBookList --> Model : addressBook
+
+Model -> AddressBook : resetData(addressBook)
+AddressBook --> Model :
+deactivate AddressBook
+
+Model -> AddressBookList : undoPastCommand()
-VersionedAddressBook -> VersionedAddressBook :resetData(ReadOnlyAddressBook)
-VersionedAddressBook --> Model :
-deactivate VersionedAddressBook
+AddressBookList --> Model : pastCommand
+deactivate AddressBookList
-Model --> UndoCommand
+Model --> UndoCommand : pastCommand
deactivate Model
UndoCommand --> LogicManager : result
diff --git a/docs/diagrams/ViewLeaveCommandSequenceDiagram.puml b/docs/diagrams/ViewLeaveCommandSequenceDiagram.puml
new file mode 100644
index 00000000000..9cd666d69b6
--- /dev/null
+++ b/docs/diagrams/ViewLeaveCommandSequenceDiagram.puml
@@ -0,0 +1,57 @@
+@startuml
+!include style.puml
+skinparam ArrowFontStyle plain
+
+box Logic LOGIC_COLOR_T1
+participant ":LogicManager" as LogicManager LOGIC_COLOR
+participant ":AddressBookParser" as AddressBookParser LOGIC_COLOR
+participant ":ViewLeaveCommandParser" as ViewLeaveCommandParser LOGIC_COLOR
+participant ":ViewLeaveCommand" as ViewLeaveCommand LOGIC_COLOR
+end box
+
+box Model MODEL_COLOR_T1
+participant ":HasLeaveAnyMonthPredicate" as HasLeaveAnyMonthPredicate MODEL_COLOR
+participant ":MatchingDepartmentPredicate" as MatchingDepartmentPredicate MODEL_COLOR
+end box
+
+[-> LogicManager : execute(view_leave d/engineering)
+activate LogicManager
+
+LogicManager -> AddressBookParser : parseCommand(view_leave d/engineering)
+activate AddressBookParser
+
+create ViewLeaveCommandParser
+AddressBookParser -> ViewLeaveCommandParser : parse(d/engineering)
+activate ViewLeaveCommandParser
+
+create HasLeaveAnyMonthPredicate
+ViewLeaveCommandParser -> HasLeaveAnyMonthPredicate : new HasLeaveAnyMonthPredicate()
+activate HasLeaveAnyMonthPredicate
+
+create MatchingDepartmentPredicate
+HasLeaveAnyMonthPredicate -> MatchingDepartmentPredicate : and(new MatchingDepartmentPredicate(Engineering))
+activate MatchingDepartmentPredicate
+
+MatchingDepartmentPredicate --> HasLeaveAnyMonthPredicate
+deactivate MatchingDepartmentPredicate
+
+HasLeaveAnyMonthPredicate --> ViewLeaveCommandParser : combinedPredicate
+deactivate HasLeaveAnyMonthPredicate
+
+create ViewLeaveCommand
+ViewLeaveCommandParser -> ViewLeaveCommand : ViewLeaveCommand(combinedPredicate)
+activate ViewLeaveCommand
+
+ViewLeaveCommand --> ViewLeaveCommandParser
+deactivate ViewLeaveCommand
+
+ViewLeaveCommandParser --> AddressBookParser : ViewLeaveCommand
+deactivate ViewLeaveCommandParser
+
+AddressBookParser --> LogicManager : ViewLeaveCommand
+deactivate AddressBookParser
+
+LogicManager -->[ : CommandResult
+deactivate LogicManager
+
+@enduml
diff --git a/docs/images/AddIndividual.png b/docs/images/AddIndividual.png
new file mode 100644
index 00000000000..847fa3e5bbf
Binary files /dev/null and b/docs/images/AddIndividual.png differ
diff --git a/docs/images/AddLeave.png b/docs/images/AddLeave.png
new file mode 100644
index 00000000000..2d768f1185a
Binary files /dev/null and b/docs/images/AddLeave.png differ
diff --git a/docs/images/Birthday.png b/docs/images/Birthday.png
new file mode 100644
index 00000000000..62db70e05d9
Binary files /dev/null and b/docs/images/Birthday.png differ
diff --git a/docs/images/BirthdayAfter.png b/docs/images/BirthdayAfter.png
new file mode 100644
index 00000000000..f41d9a834eb
Binary files /dev/null and b/docs/images/BirthdayAfter.png differ
diff --git a/docs/images/BirthdayBefore.png b/docs/images/BirthdayBefore.png
new file mode 100644
index 00000000000..cca7b66ff3e
Binary files /dev/null and b/docs/images/BirthdayBefore.png differ
diff --git a/docs/images/BirthdayCommand.png b/docs/images/BirthdayCommand.png
new file mode 100644
index 00000000000..2fe9790f88d
Binary files /dev/null and b/docs/images/BirthdayCommand.png differ
diff --git a/docs/images/ClaimAfter.png b/docs/images/ClaimAfter.png
new file mode 100644
index 00000000000..5399aa3b9b0
Binary files /dev/null and b/docs/images/ClaimAfter.png differ
diff --git a/docs/images/ClaimBefore.png b/docs/images/ClaimBefore.png
new file mode 100644
index 00000000000..b7ef42203db
Binary files /dev/null and b/docs/images/ClaimBefore.png differ
diff --git a/docs/images/ClearCommand.png b/docs/images/ClearCommand.png
new file mode 100644
index 00000000000..4654b7a1120
Binary files /dev/null and b/docs/images/ClearCommand.png differ
diff --git a/docs/images/DeleteEmployee.png b/docs/images/DeleteEmployee.png
new file mode 100644
index 00000000000..89ca5a493ff
Binary files /dev/null and b/docs/images/DeleteEmployee.png differ
diff --git a/docs/images/DeleteEmployeeAfter.png b/docs/images/DeleteEmployeeAfter.png
new file mode 100644
index 00000000000..773100cc858
Binary files /dev/null and b/docs/images/DeleteEmployeeAfter.png differ
diff --git a/docs/images/DeleteEmployeeBefore.png b/docs/images/DeleteEmployeeBefore.png
new file mode 100644
index 00000000000..d14226de87f
Binary files /dev/null and b/docs/images/DeleteEmployeeBefore.png differ
diff --git a/docs/images/EditEmployee.png b/docs/images/EditEmployee.png
new file mode 100644
index 00000000000..4c8398e70aa
Binary files /dev/null and b/docs/images/EditEmployee.png differ
diff --git a/docs/images/EditEmployeeAfter.png b/docs/images/EditEmployeeAfter.png
new file mode 100644
index 00000000000..9ecf549c56d
Binary files /dev/null and b/docs/images/EditEmployeeAfter.png differ
diff --git a/docs/images/EditEmployeeBefore.png b/docs/images/EditEmployeeBefore.png
new file mode 100644
index 00000000000..b6e0ae62160
Binary files /dev/null and b/docs/images/EditEmployeeBefore.png differ
diff --git a/docs/images/ExportActivityDiagram.png b/docs/images/ExportActivityDiagram.png
new file mode 100644
index 00000000000..9af11f00378
Binary files /dev/null and b/docs/images/ExportActivityDiagram.png differ
diff --git a/docs/images/ExportSequenceDiagram.png b/docs/images/ExportSequenceDiagram.png
new file mode 100644
index 00000000000..5245bd11cf8
Binary files /dev/null and b/docs/images/ExportSequenceDiagram.png differ
diff --git a/docs/images/FindEmployee.png b/docs/images/FindEmployee.png
new file mode 100644
index 00000000000..613e9b7aef3
Binary files /dev/null and b/docs/images/FindEmployee.png differ
diff --git a/docs/images/FindEmployeeAfter.png b/docs/images/FindEmployeeAfter.png
new file mode 100644
index 00000000000..d9743dfe56b
Binary files /dev/null and b/docs/images/FindEmployeeAfter.png differ
diff --git a/docs/images/FindEmployeeBefore.png b/docs/images/FindEmployeeBefore.png
new file mode 100644
index 00000000000..1e2c5fd6051
Binary files /dev/null and b/docs/images/FindEmployeeBefore.png differ
diff --git a/docs/images/LeaveCommandSequenceDiagram.png b/docs/images/LeaveCommandSequenceDiagram.png
new file mode 100644
index 00000000000..e1a5360406c
Binary files /dev/null and b/docs/images/LeaveCommandSequenceDiagram.png differ
diff --git a/docs/images/LeaveEmployeeAfter.png b/docs/images/LeaveEmployeeAfter.png
new file mode 100644
index 00000000000..5736311ed87
Binary files /dev/null and b/docs/images/LeaveEmployeeAfter.png differ
diff --git a/docs/images/LeaveEmployeeBefore.png b/docs/images/LeaveEmployeeBefore.png
new file mode 100644
index 00000000000..4821bd2ea09
Binary files /dev/null and b/docs/images/LeaveEmployeeBefore.png differ
diff --git a/docs/images/ListCommandSequenceDiagram.png b/docs/images/ListCommandSequenceDiagram.png
new file mode 100644
index 00000000000..ec73be56e64
Binary files /dev/null and b/docs/images/ListCommandSequenceDiagram.png differ
diff --git a/docs/images/ListEmployees.png b/docs/images/ListEmployees.png
new file mode 100644
index 00000000000..eaa1b5c80d2
Binary files /dev/null and b/docs/images/ListEmployees.png differ
diff --git a/docs/images/ListEmployeesAfter.png b/docs/images/ListEmployeesAfter.png
new file mode 100644
index 00000000000..aa9cc57bac9
Binary files /dev/null and b/docs/images/ListEmployeesAfter.png differ
diff --git a/docs/images/ListEmployeesBefore.png b/docs/images/ListEmployeesBefore.png
new file mode 100644
index 00000000000..283292116e0
Binary files /dev/null and b/docs/images/ListEmployeesBefore.png differ
diff --git a/docs/images/ModelClassDiagram.png b/docs/images/ModelClassDiagram.png
index a19fb1b4ac8..dc234e2b49a 100644
Binary files a/docs/images/ModelClassDiagram.png and b/docs/images/ModelClassDiagram.png differ
diff --git a/docs/images/Redo.png b/docs/images/Redo.png
new file mode 100644
index 00000000000..1a334528389
Binary files /dev/null and b/docs/images/Redo.png differ
diff --git a/docs/images/RedoSequenceDiagram.png b/docs/images/RedoSequenceDiagram.png
new file mode 100644
index 00000000000..3004f739266
Binary files /dev/null and b/docs/images/RedoSequenceDiagram.png differ
diff --git a/docs/images/ResetLeaveAfter.png b/docs/images/ResetLeaveAfter.png
new file mode 100644
index 00000000000..2743370bca0
Binary files /dev/null and b/docs/images/ResetLeaveAfter.png differ
diff --git a/docs/images/ResetLeaveBefore.png b/docs/images/ResetLeaveBefore.png
new file mode 100644
index 00000000000..51894e8e68c
Binary files /dev/null and b/docs/images/ResetLeaveBefore.png differ
diff --git a/docs/images/SortNames.png b/docs/images/SortNames.png
new file mode 100644
index 00000000000..f1f1035fe25
Binary files /dev/null and b/docs/images/SortNames.png differ
diff --git a/docs/images/ThemeChange.png b/docs/images/ThemeChange.png
new file mode 100644
index 00000000000..c6c6cf6b0a2
Binary files /dev/null and b/docs/images/ThemeChange.png differ
diff --git a/docs/images/ThemeCommandSequenceDiagram.png b/docs/images/ThemeCommandSequenceDiagram.png
new file mode 100644
index 00000000000..14f9405edf8
Binary files /dev/null and b/docs/images/ThemeCommandSequenceDiagram.png differ
diff --git a/docs/images/Ui.png b/docs/images/Ui.png
index 5bd77847aa2..b172bb1e63e 100644
Binary files a/docs/images/Ui.png and b/docs/images/Ui.png differ
diff --git a/docs/images/UndoDelete.png b/docs/images/UndoDelete.png
new file mode 100644
index 00000000000..97317f4dbc1
Binary files /dev/null and b/docs/images/UndoDelete.png differ
diff --git a/docs/images/UndoSequenceDiagram.png b/docs/images/UndoSequenceDiagram.png
index c7a7e637266..e3a72d4900e 100644
Binary files a/docs/images/UndoSequenceDiagram.png and b/docs/images/UndoSequenceDiagram.png differ
diff --git a/docs/images/ViewLeave.png b/docs/images/ViewLeave.png
new file mode 100644
index 00000000000..d5a5defb0e5
Binary files /dev/null and b/docs/images/ViewLeave.png differ
diff --git a/docs/images/ViewLeaveAfter.png b/docs/images/ViewLeaveAfter.png
new file mode 100644
index 00000000000..7546a0a10e4
Binary files /dev/null and b/docs/images/ViewLeaveAfter.png differ
diff --git a/docs/images/ViewLeaveBefore.png b/docs/images/ViewLeaveBefore.png
new file mode 100644
index 00000000000..27c2e3c8b02
Binary files /dev/null and b/docs/images/ViewLeaveBefore.png differ
diff --git a/docs/images/ViewLeaveCommandSequenceDiagram.png b/docs/images/ViewLeaveCommandSequenceDiagram.png
new file mode 100644
index 00000000000..091afd398fa
Binary files /dev/null and b/docs/images/ViewLeaveCommandSequenceDiagram.png differ
diff --git a/docs/images/claim.png b/docs/images/claim.png
new file mode 100644
index 00000000000..fbad8e271a9
Binary files /dev/null and b/docs/images/claim.png differ
diff --git a/docs/images/export.png b/docs/images/export.png
new file mode 100644
index 00000000000..64bf8cf9428
Binary files /dev/null and b/docs/images/export.png differ
diff --git a/docs/images/marcellaantania.png b/docs/images/marcellaantania.png
new file mode 100644
index 00000000000..a4ae442fae5
Binary files /dev/null and b/docs/images/marcellaantania.png differ
diff --git a/docs/images/nixonwidjaja.png b/docs/images/nixonwidjaja.png
new file mode 100644
index 00000000000..87d745635e9
Binary files /dev/null and b/docs/images/nixonwidjaja.png differ
diff --git a/docs/images/remuslum.png b/docs/images/remuslum.png
new file mode 100644
index 00000000000..8cd1dd47cf2
Binary files /dev/null and b/docs/images/remuslum.png differ
diff --git a/docs/images/reset_leaves.png b/docs/images/reset_leaves.png
new file mode 100644
index 00000000000..f8a2ca28062
Binary files /dev/null and b/docs/images/reset_leaves.png differ
diff --git a/docs/images/sheryew.png b/docs/images/sheryew.png
new file mode 100644
index 00000000000..e61b693329b
Binary files /dev/null and b/docs/images/sheryew.png differ
diff --git a/docs/images/viewAttribute.png b/docs/images/viewAttribute.png
new file mode 100644
index 00000000000..4207254a74d
Binary files /dev/null and b/docs/images/viewAttribute.png differ
diff --git a/docs/index.md b/docs/index.md
index 7601dbaad0d..b267d17f701 100644
--- a/docs/index.md
+++ b/docs/index.md
@@ -1,6 +1,6 @@
---
layout: page
-title: AddressBook Level-3
+title: HR Insight
---
[![CI Status](https://github.com/se-edu/addressbook-level3/workflows/Java%20CI/badge.svg)](https://github.com/se-edu/addressbook-level3/actions)
@@ -8,12 +8,15 @@ title: AddressBook Level-3
![Ui](images/Ui.png)
-**AddressBook is a desktop application for managing your contact details.** While it has a GUI, most of the user interactions happen using a CLI (Command Line Interface).
-
-* If you are interested in using AddressBook, head over to the [_Quick Start_ section of the **User Guide**](UserGuide.html#quick-start).
-* If you are interested about developing AddressBook, the [**Developer Guide**](DeveloperGuide.html) is a good place to start.
+HR Insight is a **desktop app for HR people, optimized for use via a Command Line Interface** (CLI) while still having the benefits of a Graphical User Interface (GUI).
+The _purpose_ of this app is to provide **HR employees** a _centralized employee management system_ to better manage all employees' details and **improve the
+efficiency** of their workflow.
+- If you are interested in using HR Insight, head over to the [_Quick Start_ section of the **User Guide**](https://ay2324s1-cs2103-f13-2.github.io/tp/UserGuide.html#quick-start).
+- If you are interested in developing HR Insight, the [**Developer Guide**](https://ay2324s1-cs2103-f13-2.github.io/tp/DeveloperGuide.html) is a good place to start.
**Acknowledgements**
-* Libraries used: [JavaFX](https://openjfx.io/), [Jackson](https://github.com/FasterXML/jackson), [JUnit5](https://github.com/junit-team/junit5)
+- This project is adapted from [AddressBook 3 (AB3)](https://github.com/se-edu/addressbook-level3)
+- Libraries used: [JavaFX](https://openjfx.io/), [Jackson](https://github.com/FasterXML/jackson), and [JUnit5](https://github.com/junit-team/junit5)
+- HR Insight logo is obtained from [Vecteezy](https://www.vecteezy.com/vector-art/4702848-abstract-letter-hr-logo-isolated-on-white-background)
diff --git a/docs/package-lock.json b/docs/package-lock.json
new file mode 100644
index 00000000000..63a232e05dc
--- /dev/null
+++ b/docs/package-lock.json
@@ -0,0 +1,8587 @@
+{
+ "name": "docs",
+ "version": "1.0.0",
+ "lockfileVersion": 2,
+ "requires": true,
+ "packages": {
+ "": {
+ "name": "docs",
+ "version": "1.0.0",
+ "devDependencies": {
+ "markbind-cli": "^5.1.0"
+ }
+ },
+ "node_modules/@colors/colors": {
+ "version": "1.5.0",
+ "resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.5.0.tgz",
+ "integrity": "sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.1.90"
+ }
+ },
+ "node_modules/@fortawesome/fontawesome-free": {
+ "version": "6.4.2",
+ "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-free/-/fontawesome-free-6.4.2.tgz",
+ "integrity": "sha512-m5cPn3e2+FDCOgi1mz0RexTUvvQibBebOUlUlW0+YrMjDTPkiJ6VTKukA1GRsvRw+12KyJndNjj0O4AgTxm2Pg==",
+ "dev": true,
+ "hasInstallScript": true,
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/@kwsites/file-exists": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/@kwsites/file-exists/-/file-exists-1.1.1.tgz",
+ "integrity": "sha512-m9/5YGR18lIwxSFDwfE3oA7bWuq9kdau6ugN4H2rJeyhFQZcG9AgSHkQtSD15a8WvTgfz9aikZMrKPHvbpqFiw==",
+ "dev": true,
+ "dependencies": {
+ "debug": "^4.1.1"
+ }
+ },
+ "node_modules/@kwsites/file-exists/node_modules/debug": {
+ "version": "4.3.4",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz",
+ "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==",
+ "dev": true,
+ "dependencies": {
+ "ms": "2.1.2"
+ },
+ "engines": {
+ "node": ">=6.0"
+ },
+ "peerDependenciesMeta": {
+ "supports-color": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@kwsites/file-exists/node_modules/ms": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
+ "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==",
+ "dev": true
+ },
+ "node_modules/@kwsites/promise-deferred": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/@kwsites/promise-deferred/-/promise-deferred-1.1.1.tgz",
+ "integrity": "sha512-GaHYm+c0O9MjZRu0ongGBRbinu8gVAMd2UZjji6jVmqKtZluZnptXGWhz1E8j8D2HJ3f/yMxKAUC0b+57wncIw==",
+ "dev": true
+ },
+ "node_modules/@markbind/core": {
+ "version": "5.1.0",
+ "resolved": "https://registry.npmjs.org/@markbind/core/-/core-5.1.0.tgz",
+ "integrity": "sha512-YAXjH+qCXnrBzpKIAJkayVLmyIUaG/8Dms3Gpd2VIufeZyW8w0diXdgKSsymjzodTMgghZMdxG3Qpng833ARPg==",
+ "dev": true,
+ "dependencies": {
+ "@fortawesome/fontawesome-free": "^6.4.0",
+ "@markbind/core-web": "5.1.0",
+ "@primer/octicons": "^15.0.1",
+ "@sindresorhus/slugify": "^0.9.1",
+ "@tlylt/markdown-it-imsize": "^3.0.0",
+ "bluebird": "^3.7.2",
+ "bootswatch": "5.1.3",
+ "cheerio": "^0.22.0",
+ "crypto-js": "^4.0.0",
+ "csv-parse": "^4.14.2",
+ "ensure-posix-path": "^1.1.1",
+ "fastmatter": "^2.1.1",
+ "fs-extra": "^9.0.1",
+ "gh-pages": "^2.1.1",
+ "highlight.js": "^10.4.1",
+ "htmlparser2": "^3.10.1",
+ "ignore": "^5.1.4",
+ "js-beautify": "1.14.3",
+ "katex": "^0.15.6",
+ "lodash": "^4.17.15",
+ "markdown-it": "^12.3.2",
+ "markdown-it-attrs": "^4.1.3",
+ "markdown-it-emoji": "^1.4.0",
+ "markdown-it-linkify-images": "^3.0.0",
+ "markdown-it-mark": "^3.0.0",
+ "markdown-it-regexp": "^0.4.0",
+ "markdown-it-sub": "^1.0.0",
+ "markdown-it-sup": "^1.0.0",
+ "markdown-it-table-of-contents": "^0.4.4",
+ "markdown-it-task-lists": "^2.1.1",
+ "markdown-it-texmath": "^1.0.0",
+ "markdown-it-video": "^0.6.3",
+ "material-icons": "^1.9.1",
+ "moment": "^2.29.4",
+ "nunjucks": "3.2.2",
+ "path-is-inside": "^1.0.2",
+ "simple-git": "^2.17.0",
+ "url-parse": "^1.5.10",
+ "uuid": "^8.3.1",
+ "vue": "2.6.14",
+ "vue-server-renderer": "2.6.14",
+ "vue-template-compiler": "2.6.14",
+ "walk-sync": "^2.0.2",
+ "winston": "^2.4.4"
+ }
+ },
+ "node_modules/@markbind/core-web": {
+ "version": "5.1.0",
+ "resolved": "https://registry.npmjs.org/@markbind/core-web/-/core-web-5.1.0.tgz",
+ "integrity": "sha512-TRzz8ZCr25pylKvFxF/WwXDi4Gbtsb2OLXV61WyTFqVy03tFoEJ2mqncpbliI9DrfDdKWcm1YZPgDCedVkYjKA==",
+ "dev": true
+ },
+ "node_modules/@primer/octicons": {
+ "version": "15.2.0",
+ "resolved": "https://registry.npmjs.org/@primer/octicons/-/octicons-15.2.0.tgz",
+ "integrity": "sha512-4cHZzcZ3F/HQNL4EKSaFyVsW7XtITiJkTeB1JDDmRuP/XobyWyF9gWxuV9c+byUa8dOB5KNQn37iRvNrIehPUQ==",
+ "dev": true,
+ "dependencies": {
+ "object-assign": "^4.1.1"
+ }
+ },
+ "node_modules/@sindresorhus/slugify": {
+ "version": "0.9.1",
+ "resolved": "https://registry.npmjs.org/@sindresorhus/slugify/-/slugify-0.9.1.tgz",
+ "integrity": "sha512-b6heYM9dzZD13t2GOiEQTDE0qX+I1GyOotMwKh9VQqzuNiVdPVT8dM43fe9HNb/3ul+Qwd5oKSEDrDIfhq3bnQ==",
+ "dev": true,
+ "dependencies": {
+ "escape-string-regexp": "^1.0.5",
+ "lodash.deburr": "^4.1.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/@tlylt/markdown-it-imsize": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/@tlylt/markdown-it-imsize/-/markdown-it-imsize-3.0.0.tgz",
+ "integrity": "sha512-6kTM+vRJTuN2UxNPyJ8yC+NHrzS+MxVHV+z+bDxSr/Fd7eTah2+otLKC2B17YI/1lQnSumA2qokPGuzsA98c6g==",
+ "dev": true
+ },
+ "node_modules/@types/minimatch": {
+ "version": "3.0.5",
+ "resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-3.0.5.tgz",
+ "integrity": "sha512-Klz949h02Gz2uZCMGwDUSDS1YBlTdDDgbWHi+81l29tQALUtvz4rAYi5uoVhE5Lagoq6DeqAUlbrHvW/mXDgdQ==",
+ "dev": true
+ },
+ "node_modules/a-sync-waterfall": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/a-sync-waterfall/-/a-sync-waterfall-1.0.1.tgz",
+ "integrity": "sha512-RYTOHHdWipFUliRFMCS4X2Yn2X8M87V/OpSqWzKKOGhzqyUxzyVmhHDH9sAvG+ZuQf/TAOFsLCpMw09I1ufUnA==",
+ "dev": true
+ },
+ "node_modules/abbrev": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz",
+ "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==",
+ "dev": true
+ },
+ "node_modules/accepts": {
+ "version": "1.3.8",
+ "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz",
+ "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==",
+ "dev": true,
+ "dependencies": {
+ "mime-types": "~2.1.34",
+ "negotiator": "0.6.3"
+ },
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/ansi-regex": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz",
+ "integrity": "sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/ansi-styles": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+ "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+ "dev": true,
+ "dependencies": {
+ "color-convert": "^2.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+ }
+ },
+ "node_modules/anymatch": {
+ "version": "3.1.3",
+ "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz",
+ "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==",
+ "dev": true,
+ "dependencies": {
+ "normalize-path": "^3.0.0",
+ "picomatch": "^2.0.4"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/apache-crypt": {
+ "version": "1.2.5",
+ "resolved": "https://registry.npmjs.org/apache-crypt/-/apache-crypt-1.2.5.tgz",
+ "integrity": "sha512-ICnYQH+DFVmw+S4Q0QY2XRXD8Ne8ewh8HgbuFH4K7022zCxgHM0Hz1xkRnUlEfAXNbwp1Cnhbedu60USIfDxvg==",
+ "dev": true,
+ "dependencies": {
+ "unix-crypt-td-js": "^1.1.4"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/apache-md5": {
+ "version": "1.1.7",
+ "resolved": "https://registry.npmjs.org/apache-md5/-/apache-md5-1.1.7.tgz",
+ "integrity": "sha512-JtHjzZmJxtzfTSjsCyHgPR155HBe5WGyUyHTaEkfy46qhwCFKx1Epm6nAxgUG3WfUZP1dWhGqj9Z2NOBeZ+uBw==",
+ "dev": true,
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/argparse": {
+ "version": "1.0.10",
+ "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz",
+ "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==",
+ "dev": true,
+ "dependencies": {
+ "sprintf-js": "~1.0.2"
+ }
+ },
+ "node_modules/arr-diff": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz",
+ "integrity": "sha512-YVIQ82gZPGBebQV/a8dar4AitzCQs0jjXwMPZllpXMaGjXPYVUawSxQrRsjhjupyVxEvbHgUmIhKVlND+j02kA==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/arr-flatten": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/arr-flatten/-/arr-flatten-1.1.0.tgz",
+ "integrity": "sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/arr-union": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/arr-union/-/arr-union-3.1.0.tgz",
+ "integrity": "sha512-sKpyeERZ02v1FeCZT8lrfJq5u6goHCtpTAzPwJYe7c8SPFOboNjNg1vz2L4VTn9T4PQxEx13TbXLmYUcS6Ug7Q==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/array-union": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/array-union/-/array-union-1.0.2.tgz",
+ "integrity": "sha512-Dxr6QJj/RdU/hCaBjOfxW+q6lyuVE6JFWIrAUpuOOhoJJoQ99cUn3igRaHVB5P9WrgFVN0FfArM3x0cueOU8ng==",
+ "dev": true,
+ "dependencies": {
+ "array-uniq": "^1.0.1"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/array-uniq": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/array-uniq/-/array-uniq-1.0.3.tgz",
+ "integrity": "sha512-MNha4BWQ6JbwhFhj03YK552f7cb3AzoE8SzeljgChvL1dl3IcvggXVz1DilzySZkCja+CXuZbdW7yATchWn8/Q==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/array-unique": {
+ "version": "0.3.2",
+ "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz",
+ "integrity": "sha512-SleRWjh9JUud2wH1hPs9rZBZ33H6T9HOiL0uwGnGx9FpE6wKGyfWugmbkEOIs6qWrZhg0LWeLziLrEwQJhs5mQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/asap": {
+ "version": "2.0.6",
+ "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz",
+ "integrity": "sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==",
+ "dev": true
+ },
+ "node_modules/assign-symbols": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/assign-symbols/-/assign-symbols-1.0.0.tgz",
+ "integrity": "sha512-Q+JC7Whu8HhmTdBph/Tq59IoRtoy6KAm5zzPv00WdujX82lbAL8K7WVjne7vdCsAmbF4AYaDOPyO3k0kl8qIrw==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/async": {
+ "version": "2.6.4",
+ "resolved": "https://registry.npmjs.org/async/-/async-2.6.4.tgz",
+ "integrity": "sha512-mzo5dfJYwAn29PeiJ0zvwTo04zj8HDJj0Mn8TD7sno7q12prdbnasKJHhkm2c1LgrhlJ0teaea8860oxi51mGA==",
+ "dev": true,
+ "dependencies": {
+ "lodash": "^4.17.14"
+ }
+ },
+ "node_modules/async-each": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/async-each/-/async-each-1.0.3.tgz",
+ "integrity": "sha512-z/WhQ5FPySLdvREByI2vZiTWwCnF0moMJ1hK9YQwDTHKh6I7/uSckMetoRGb5UBZPC1z0jlw+n/XCgjeH7y1AQ==",
+ "dev": true
+ },
+ "node_modules/at-least-node": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/at-least-node/-/at-least-node-1.0.0.tgz",
+ "integrity": "sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==",
+ "dev": true,
+ "engines": {
+ "node": ">= 4.0.0"
+ }
+ },
+ "node_modules/atob": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz",
+ "integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==",
+ "dev": true,
+ "bin": {
+ "atob": "bin/atob.js"
+ },
+ "engines": {
+ "node": ">= 4.5.0"
+ }
+ },
+ "node_modules/balanced-match": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
+ "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
+ "dev": true
+ },
+ "node_modules/base": {
+ "version": "0.11.2",
+ "resolved": "https://registry.npmjs.org/base/-/base-0.11.2.tgz",
+ "integrity": "sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg==",
+ "dev": true,
+ "dependencies": {
+ "cache-base": "^1.0.1",
+ "class-utils": "^0.3.5",
+ "component-emitter": "^1.2.1",
+ "define-property": "^1.0.0",
+ "isobject": "^3.0.1",
+ "mixin-deep": "^1.2.0",
+ "pascalcase": "^0.1.1"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/base/node_modules/define-property": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz",
+ "integrity": "sha512-cZTYKFWspt9jZsMscWo8sc/5lbPC9Q0N5nBLgb+Yd915iL3udB1uFgS3B8YCx66UVHq018DAVFoee7x+gxggeA==",
+ "dev": true,
+ "dependencies": {
+ "is-descriptor": "^1.0.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/basic-auth": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/basic-auth/-/basic-auth-2.0.1.tgz",
+ "integrity": "sha512-NF+epuEdnUYVlGuhaxbbq+dvJttwLnGY+YixlXlME5KpQ5W3CnXA5cVTneY3SPbPDRkcjMbifrwmFYcClgOZeg==",
+ "dev": true,
+ "dependencies": {
+ "safe-buffer": "5.1.2"
+ },
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/batch": {
+ "version": "0.6.1",
+ "resolved": "https://registry.npmjs.org/batch/-/batch-0.6.1.tgz",
+ "integrity": "sha512-x+VAiMRL6UPkx+kudNvxTl6hB2XNNCG2r+7wixVfIYwu/2HKRXimwQyaumLjMveWvT2Hkd/cAJw+QBMfJ/EKVw==",
+ "dev": true
+ },
+ "node_modules/bcryptjs": {
+ "version": "2.4.3",
+ "resolved": "https://registry.npmjs.org/bcryptjs/-/bcryptjs-2.4.3.tgz",
+ "integrity": "sha512-V/Hy/X9Vt7f3BbPJEi8BdVFMByHi+jNXrYkW3huaybV/kQ0KJg0Y6PkEMbn+zeT+i+SiKZ/HMqJGIIt4LZDqNQ==",
+ "dev": true
+ },
+ "node_modules/binary-extensions": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz",
+ "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==",
+ "dev": true,
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/bindings": {
+ "version": "1.5.0",
+ "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz",
+ "integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==",
+ "dev": true,
+ "optional": true,
+ "dependencies": {
+ "file-uri-to-path": "1.0.0"
+ }
+ },
+ "node_modules/bluebird": {
+ "version": "3.7.2",
+ "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz",
+ "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==",
+ "dev": true
+ },
+ "node_modules/boolbase": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz",
+ "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==",
+ "dev": true
+ },
+ "node_modules/bootswatch": {
+ "version": "5.1.3",
+ "resolved": "https://registry.npmjs.org/bootswatch/-/bootswatch-5.1.3.tgz",
+ "integrity": "sha512-NmZFN6rOCoXWQ/PkzmD8FFWDe24kocX9OXWHNVaLxVVnpqpAzEbMFsf8bAfKwVtpNXibasZCzv09B5fLieAh2g==",
+ "dev": true
+ },
+ "node_modules/brace-expansion": {
+ "version": "1.1.11",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
+ "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
+ "dev": true,
+ "dependencies": {
+ "balanced-match": "^1.0.0",
+ "concat-map": "0.0.1"
+ }
+ },
+ "node_modules/braces": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz",
+ "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==",
+ "dev": true,
+ "dependencies": {
+ "fill-range": "^7.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/cache-base": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/cache-base/-/cache-base-1.0.1.tgz",
+ "integrity": "sha512-AKcdTnFSWATd5/GCPRxr2ChwIJ85CeyrEyjRHlKxQ56d4XJMGym0uAiKn0xbLOGOl3+yRpOTi484dVCEc5AUzQ==",
+ "dev": true,
+ "dependencies": {
+ "collection-visit": "^1.0.0",
+ "component-emitter": "^1.2.1",
+ "get-value": "^2.0.6",
+ "has-value": "^1.0.0",
+ "isobject": "^3.0.1",
+ "set-value": "^2.0.0",
+ "to-object-path": "^0.3.0",
+ "union-value": "^1.0.0",
+ "unset-value": "^1.0.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/chalk": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz",
+ "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==",
+ "dev": true,
+ "dependencies": {
+ "ansi-styles": "^4.1.0",
+ "supports-color": "^7.1.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/cheerio": {
+ "version": "0.22.0",
+ "resolved": "https://registry.npmjs.org/cheerio/-/cheerio-0.22.0.tgz",
+ "integrity": "sha512-8/MzidM6G/TgRelkzDG13y3Y9LxBjCb+8yOEZ9+wwq5gVF2w2pV0wmHvjfT0RvuxGyR7UEuK36r+yYMbT4uKgA==",
+ "dev": true,
+ "dependencies": {
+ "css-select": "~1.2.0",
+ "dom-serializer": "~0.1.0",
+ "entities": "~1.1.1",
+ "htmlparser2": "^3.9.1",
+ "lodash.assignin": "^4.0.9",
+ "lodash.bind": "^4.1.4",
+ "lodash.defaults": "^4.0.1",
+ "lodash.filter": "^4.4.0",
+ "lodash.flatten": "^4.2.0",
+ "lodash.foreach": "^4.3.0",
+ "lodash.map": "^4.4.0",
+ "lodash.merge": "^4.4.0",
+ "lodash.pick": "^4.2.1",
+ "lodash.reduce": "^4.4.0",
+ "lodash.reject": "^4.4.0",
+ "lodash.some": "^4.4.0"
+ },
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/chokidar": {
+ "version": "3.5.3",
+ "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz",
+ "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "individual",
+ "url": "https://paulmillr.com/funding/"
+ }
+ ],
+ "dependencies": {
+ "anymatch": "~3.1.2",
+ "braces": "~3.0.2",
+ "glob-parent": "~5.1.2",
+ "is-binary-path": "~2.1.0",
+ "is-glob": "~4.0.1",
+ "normalize-path": "~3.0.0",
+ "readdirp": "~3.6.0"
+ },
+ "engines": {
+ "node": ">= 8.10.0"
+ },
+ "optionalDependencies": {
+ "fsevents": "~2.3.2"
+ }
+ },
+ "node_modules/class-utils": {
+ "version": "0.3.6",
+ "resolved": "https://registry.npmjs.org/class-utils/-/class-utils-0.3.6.tgz",
+ "integrity": "sha512-qOhPa/Fj7s6TY8H8esGu5QNpMMQxz79h+urzrNYN6mn+9BnxlDGf5QZ+XeCDsxSjPqsSR56XOZOJmpeurnLMeg==",
+ "dev": true,
+ "dependencies": {
+ "arr-union": "^3.1.0",
+ "define-property": "^0.2.5",
+ "isobject": "^3.0.0",
+ "static-extend": "^0.1.1"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/class-utils/node_modules/define-property": {
+ "version": "0.2.5",
+ "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz",
+ "integrity": "sha512-Rr7ADjQZenceVOAKop6ALkkRAmH1A4Gx9hV/7ZujPUN2rkATqFO0JZLZInbAjpZYoJ1gUx8MRMQVkYemcbMSTA==",
+ "dev": true,
+ "dependencies": {
+ "is-descriptor": "^0.1.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/class-utils/node_modules/is-accessor-descriptor": {
+ "version": "0.1.6",
+ "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz",
+ "integrity": "sha512-e1BM1qnDbMRG3ll2U9dSK0UMHuWOs3pY3AtcFsmvwPtKL3MML/Q86i+GilLfvqEs4GW+ExB91tQ3Ig9noDIZ+A==",
+ "dev": true,
+ "dependencies": {
+ "kind-of": "^3.0.2"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/class-utils/node_modules/is-accessor-descriptor/node_modules/kind-of": {
+ "version": "3.2.2",
+ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
+ "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==",
+ "dev": true,
+ "dependencies": {
+ "is-buffer": "^1.1.5"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/class-utils/node_modules/is-data-descriptor": {
+ "version": "0.1.4",
+ "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz",
+ "integrity": "sha512-+w9D5ulSoBNlmw9OHn3U2v51SyoCd0he+bB3xMl62oijhrspxowjU+AIcDY0N3iEJbUEkB15IlMASQsxYigvXg==",
+ "dev": true,
+ "dependencies": {
+ "kind-of": "^3.0.2"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/class-utils/node_modules/is-data-descriptor/node_modules/kind-of": {
+ "version": "3.2.2",
+ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
+ "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==",
+ "dev": true,
+ "dependencies": {
+ "is-buffer": "^1.1.5"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/class-utils/node_modules/is-descriptor": {
+ "version": "0.1.6",
+ "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz",
+ "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==",
+ "dev": true,
+ "dependencies": {
+ "is-accessor-descriptor": "^0.1.6",
+ "is-data-descriptor": "^0.1.4",
+ "kind-of": "^5.0.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/class-utils/node_modules/kind-of": {
+ "version": "5.1.0",
+ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz",
+ "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/collection-visit": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/collection-visit/-/collection-visit-1.0.0.tgz",
+ "integrity": "sha512-lNkKvzEeMBBjUGHZ+q6z9pSJla0KWAQPvtzhEV9+iGyQYG+pBpl7xKDhxoNSOZH2hhv0v5k0y2yAM4o4SjoSkw==",
+ "dev": true,
+ "dependencies": {
+ "map-visit": "^1.0.0",
+ "object-visit": "^1.0.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/color-convert": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
+ "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+ "dev": true,
+ "dependencies": {
+ "color-name": "~1.1.4"
+ },
+ "engines": {
+ "node": ">=7.0.0"
+ }
+ },
+ "node_modules/color-name": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
+ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
+ "dev": true
+ },
+ "node_modules/colors": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/colors/-/colors-1.4.0.tgz",
+ "integrity": "sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.1.90"
+ }
+ },
+ "node_modules/commander": {
+ "version": "8.3.0",
+ "resolved": "https://registry.npmjs.org/commander/-/commander-8.3.0.tgz",
+ "integrity": "sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==",
+ "dev": true,
+ "engines": {
+ "node": ">= 12"
+ }
+ },
+ "node_modules/component-emitter": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz",
+ "integrity": "sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==",
+ "dev": true
+ },
+ "node_modules/concat-map": {
+ "version": "0.0.1",
+ "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
+ "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==",
+ "dev": true
+ },
+ "node_modules/config-chain": {
+ "version": "1.1.13",
+ "resolved": "https://registry.npmjs.org/config-chain/-/config-chain-1.1.13.tgz",
+ "integrity": "sha512-qj+f8APARXHrM0hraqXYb2/bOVSV4PvJQlNZ/DVj0QrmNM2q2euizkeuVckQ57J+W0mRH6Hvi+k50M4Jul2VRQ==",
+ "dev": true,
+ "dependencies": {
+ "ini": "^1.3.4",
+ "proto-list": "~1.2.1"
+ }
+ },
+ "node_modules/connect": {
+ "version": "3.7.0",
+ "resolved": "https://registry.npmjs.org/connect/-/connect-3.7.0.tgz",
+ "integrity": "sha512-ZqRXc+tZukToSNmh5C2iWMSoV3X1YUcPbqEM4DkEG5tNQXrQUZCNVGGv3IuicnkMtPfGf3Xtp8WCXs295iQ1pQ==",
+ "dev": true,
+ "dependencies": {
+ "debug": "2.6.9",
+ "finalhandler": "1.1.2",
+ "parseurl": "~1.3.3",
+ "utils-merge": "1.0.1"
+ },
+ "engines": {
+ "node": ">= 0.10.0"
+ }
+ },
+ "node_modules/copy-descriptor": {
+ "version": "0.1.1",
+ "resolved": "https://registry.npmjs.org/copy-descriptor/-/copy-descriptor-0.1.1.tgz",
+ "integrity": "sha512-XgZ0pFcakEUlbwQEVNg3+QAis1FyTL3Qel9FYy8pSkQqoG3PNoT0bOCQtOXcOkur21r2Eq2kI+IE+gsmAEVlYw==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/core-util-is": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz",
+ "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==",
+ "dev": true
+ },
+ "node_modules/cors": {
+ "version": "2.8.5",
+ "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz",
+ "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==",
+ "dev": true,
+ "dependencies": {
+ "object-assign": "^4",
+ "vary": "^1"
+ },
+ "engines": {
+ "node": ">= 0.10"
+ }
+ },
+ "node_modules/crypto-js": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/crypto-js/-/crypto-js-4.1.1.tgz",
+ "integrity": "sha512-o2JlM7ydqd3Qk9CA0L4NL6mTzU2sdx96a+oOfPu8Mkl/PK51vSyoi8/rQ8NknZtk44vq15lmhAj9CIAGwgeWKw==",
+ "dev": true
+ },
+ "node_modules/css-select": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/css-select/-/css-select-1.2.0.tgz",
+ "integrity": "sha512-dUQOBoqdR7QwV90WysXPLXG5LO7nhYBgiWVfxF80DKPF8zx1t/pUd2FYy73emg3zrjtM6dzmYgbHKfV2rxiHQA==",
+ "dev": true,
+ "dependencies": {
+ "boolbase": "~1.0.0",
+ "css-what": "2.1",
+ "domutils": "1.5.1",
+ "nth-check": "~1.0.1"
+ }
+ },
+ "node_modules/css-what": {
+ "version": "2.1.3",
+ "resolved": "https://registry.npmjs.org/css-what/-/css-what-2.1.3.tgz",
+ "integrity": "sha512-a+EPoD+uZiNfh+5fxw2nO9QwFa6nJe2Or35fGY6Ipw1R3R4AGz1d1TEZrCegvw2YTmZ0jXirGYlzxxpYSHwpEg==",
+ "dev": true,
+ "engines": {
+ "node": "*"
+ }
+ },
+ "node_modules/csv-parse": {
+ "version": "4.16.3",
+ "resolved": "https://registry.npmjs.org/csv-parse/-/csv-parse-4.16.3.tgz",
+ "integrity": "sha512-cO1I/zmz4w2dcKHVvpCr7JVRu8/FymG5OEpmvsZYlccYolPBLoVGKUHgNoc4ZGkFeFlWGEDmMyBM+TTqRdW/wg==",
+ "dev": true
+ },
+ "node_modules/cycle": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/cycle/-/cycle-1.0.3.tgz",
+ "integrity": "sha512-TVF6svNzeQCOpjCqsy0/CSy8VgObG3wXusJ73xW2GbG5rGx7lC8zxDSURicsXI2UsGdi2L0QNRCi745/wUDvsA==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.4.0"
+ }
+ },
+ "node_modules/de-indent": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/de-indent/-/de-indent-1.0.2.tgz",
+ "integrity": "sha512-e/1zu3xH5MQryN2zdVaF0OrdNLUbvWxzMbi+iNA6Bky7l1RoP8a2fIbRocyHclXt/arDrrR6lL3TqFD9pMQTsg==",
+ "dev": true
+ },
+ "node_modules/debug": {
+ "version": "2.6.9",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
+ "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
+ "dev": true,
+ "dependencies": {
+ "ms": "2.0.0"
+ }
+ },
+ "node_modules/decode-uri-component": {
+ "version": "0.2.0",
+ "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.0.tgz",
+ "integrity": "sha512-hjf+xovcEn31w/EUYdTXQh/8smFL/dzYjohQGEIgjyNavaJfBY2p5F527Bo1VPATxv0VYTUC2bOcXvqFwk78Og==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10"
+ }
+ },
+ "node_modules/define-property": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/define-property/-/define-property-2.0.2.tgz",
+ "integrity": "sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ==",
+ "dev": true,
+ "dependencies": {
+ "is-descriptor": "^1.0.2",
+ "isobject": "^3.0.1"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/depd": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz",
+ "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==",
+ "dev": true,
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/destroy": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz",
+ "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==",
+ "dev": true,
+ "engines": {
+ "node": ">= 0.8",
+ "npm": "1.2.8000 || >= 1.4.16"
+ }
+ },
+ "node_modules/dom-serializer": {
+ "version": "0.1.1",
+ "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-0.1.1.tgz",
+ "integrity": "sha512-l0IU0pPzLWSHBcieZbpOKgkIn3ts3vAh7ZuFyXNwJxJXk/c4Gwj9xaTJwIDVQCXawWD0qb3IzMGH5rglQaO0XA==",
+ "dev": true,
+ "dependencies": {
+ "domelementtype": "^1.3.0",
+ "entities": "^1.1.1"
+ }
+ },
+ "node_modules/domelementtype": {
+ "version": "1.3.1",
+ "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-1.3.1.tgz",
+ "integrity": "sha512-BSKB+TSpMpFI/HOxCNr1O8aMOTZ8hT3pM3GQ0w/mWRmkhEDSFJkkyzz4XQsBV44BChwGkrDfMyjVD0eA2aFV3w==",
+ "dev": true
+ },
+ "node_modules/domhandler": {
+ "version": "2.4.2",
+ "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-2.4.2.tgz",
+ "integrity": "sha512-JiK04h0Ht5u/80fdLMCEmV4zkNh2BcoMFBmZ/91WtYZ8qVXSKjiw7fXMgFPnHcSZgOo3XdinHvmnDUeMf5R4wA==",
+ "dev": true,
+ "dependencies": {
+ "domelementtype": "1"
+ }
+ },
+ "node_modules/domutils": {
+ "version": "1.5.1",
+ "resolved": "https://registry.npmjs.org/domutils/-/domutils-1.5.1.tgz",
+ "integrity": "sha512-gSu5Oi/I+3wDENBsOWBiRK1eoGxcywYSqg3rR960/+EfY0CF4EX1VPkgHOZ3WiS/Jg2DtliF6BhWcHlfpYUcGw==",
+ "dev": true,
+ "dependencies": {
+ "dom-serializer": "0",
+ "domelementtype": "1"
+ }
+ },
+ "node_modules/duplexer": {
+ "version": "0.1.2",
+ "resolved": "https://registry.npmjs.org/duplexer/-/duplexer-0.1.2.tgz",
+ "integrity": "sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg==",
+ "dev": true
+ },
+ "node_modules/editorconfig": {
+ "version": "0.15.3",
+ "resolved": "https://registry.npmjs.org/editorconfig/-/editorconfig-0.15.3.tgz",
+ "integrity": "sha512-M9wIMFx96vq0R4F+gRpY3o2exzb8hEj/n9S8unZtHSvYjibBp/iMufSzvmOcV/laG0ZtuTVGtiJggPOSW2r93g==",
+ "dev": true,
+ "dependencies": {
+ "commander": "^2.19.0",
+ "lru-cache": "^4.1.5",
+ "semver": "^5.6.0",
+ "sigmund": "^1.0.1"
+ },
+ "bin": {
+ "editorconfig": "bin/editorconfig"
+ }
+ },
+ "node_modules/editorconfig/node_modules/commander": {
+ "version": "2.20.3",
+ "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz",
+ "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==",
+ "dev": true
+ },
+ "node_modules/ee-first": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
+ "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==",
+ "dev": true
+ },
+ "node_modules/email-addresses": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/email-addresses/-/email-addresses-3.1.0.tgz",
+ "integrity": "sha512-k0/r7GrWVL32kZlGwfPNgB2Y/mMXVTq/decgLczm/j34whdaspNrZO8CnXPf1laaHxI6ptUlsnAxN+UAPw+fzg==",
+ "dev": true
+ },
+ "node_modules/encodeurl": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz",
+ "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==",
+ "dev": true,
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/ensure-posix-path": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/ensure-posix-path/-/ensure-posix-path-1.1.1.tgz",
+ "integrity": "sha512-VWU0/zXzVbeJNXvME/5EmLuEj2TauvoaTz6aFYK1Z92JCBlDlZ3Gu0tuGR42kpW1754ywTs+QB0g5TP0oj9Zaw==",
+ "dev": true
+ },
+ "node_modules/entities": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/entities/-/entities-1.1.2.tgz",
+ "integrity": "sha512-f2LZMYl1Fzu7YSBKg+RoROelpOaNrcGmE9AZubeDfrCEia483oW4MI4VyFd5VNHIgQ/7qm1I0wUHK1eJnn2y2w==",
+ "dev": true
+ },
+ "node_modules/escape-html": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz",
+ "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==",
+ "dev": true
+ },
+ "node_modules/escape-string-regexp": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
+ "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.8.0"
+ }
+ },
+ "node_modules/esprima": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz",
+ "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==",
+ "dev": true,
+ "bin": {
+ "esparse": "bin/esparse.js",
+ "esvalidate": "bin/esvalidate.js"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/etag": {
+ "version": "1.8.1",
+ "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz",
+ "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==",
+ "dev": true,
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/event-stream": {
+ "version": "3.3.4",
+ "resolved": "https://registry.npmjs.org/event-stream/-/event-stream-3.3.4.tgz",
+ "integrity": "sha512-QHpkERcGsR0T7Qm3HNJSyXKEEj8AHNxkY3PK8TS2KJvQ7NiSHe3DDpwVKKtoYprL/AreyzFBeIkBIWChAqn60g==",
+ "dev": true,
+ "dependencies": {
+ "duplexer": "~0.1.1",
+ "from": "~0",
+ "map-stream": "~0.1.0",
+ "pause-stream": "0.0.11",
+ "split": "0.3",
+ "stream-combiner": "~0.0.4",
+ "through": "~2.3.1"
+ }
+ },
+ "node_modules/event-stream/node_modules/split": {
+ "version": "0.3.3",
+ "resolved": "https://registry.npmjs.org/split/-/split-0.3.3.tgz",
+ "integrity": "sha512-wD2AeVmxXRBoX44wAycgjVpMhvbwdI2aZjCkvfNcH1YqHQvJVa1duWc73OyVGJUc05fhFaTZeQ/PYsrmyH0JVA==",
+ "dev": true,
+ "dependencies": {
+ "through": "2"
+ },
+ "engines": {
+ "node": "*"
+ }
+ },
+ "node_modules/event-stream/node_modules/stream-combiner": {
+ "version": "0.0.4",
+ "resolved": "https://registry.npmjs.org/stream-combiner/-/stream-combiner-0.0.4.tgz",
+ "integrity": "sha512-rT00SPnTVyRsaSz5zgSPma/aHSOic5U1prhYdRy5HS2kTZviFpmDgzilbtsJsxiroqACmayynDN/9VzIbX5DOw==",
+ "dev": true,
+ "dependencies": {
+ "duplexer": "~0.1.1"
+ }
+ },
+ "node_modules/expand-brackets": {
+ "version": "2.1.4",
+ "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz",
+ "integrity": "sha512-w/ozOKR9Obk3qoWeY/WDi6MFta9AoMR+zud60mdnbniMcBxRuFJyDt2LdX/14A1UABeqk+Uk+LDfUpvoGKppZA==",
+ "dev": true,
+ "dependencies": {
+ "debug": "^2.3.3",
+ "define-property": "^0.2.5",
+ "extend-shallow": "^2.0.1",
+ "posix-character-classes": "^0.1.0",
+ "regex-not": "^1.0.0",
+ "snapdragon": "^0.8.1",
+ "to-regex": "^3.0.1"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/expand-brackets/node_modules/define-property": {
+ "version": "0.2.5",
+ "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz",
+ "integrity": "sha512-Rr7ADjQZenceVOAKop6ALkkRAmH1A4Gx9hV/7ZujPUN2rkATqFO0JZLZInbAjpZYoJ1gUx8MRMQVkYemcbMSTA==",
+ "dev": true,
+ "dependencies": {
+ "is-descriptor": "^0.1.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/expand-brackets/node_modules/extend-shallow": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
+ "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==",
+ "dev": true,
+ "dependencies": {
+ "is-extendable": "^0.1.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/expand-brackets/node_modules/is-accessor-descriptor": {
+ "version": "0.1.6",
+ "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz",
+ "integrity": "sha512-e1BM1qnDbMRG3ll2U9dSK0UMHuWOs3pY3AtcFsmvwPtKL3MML/Q86i+GilLfvqEs4GW+ExB91tQ3Ig9noDIZ+A==",
+ "dev": true,
+ "dependencies": {
+ "kind-of": "^3.0.2"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/expand-brackets/node_modules/is-accessor-descriptor/node_modules/kind-of": {
+ "version": "3.2.2",
+ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
+ "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==",
+ "dev": true,
+ "dependencies": {
+ "is-buffer": "^1.1.5"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/expand-brackets/node_modules/is-data-descriptor": {
+ "version": "0.1.4",
+ "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz",
+ "integrity": "sha512-+w9D5ulSoBNlmw9OHn3U2v51SyoCd0he+bB3xMl62oijhrspxowjU+AIcDY0N3iEJbUEkB15IlMASQsxYigvXg==",
+ "dev": true,
+ "dependencies": {
+ "kind-of": "^3.0.2"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/expand-brackets/node_modules/is-data-descriptor/node_modules/kind-of": {
+ "version": "3.2.2",
+ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
+ "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==",
+ "dev": true,
+ "dependencies": {
+ "is-buffer": "^1.1.5"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/expand-brackets/node_modules/is-descriptor": {
+ "version": "0.1.6",
+ "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz",
+ "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==",
+ "dev": true,
+ "dependencies": {
+ "is-accessor-descriptor": "^0.1.6",
+ "is-data-descriptor": "^0.1.4",
+ "kind-of": "^5.0.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/expand-brackets/node_modules/is-extendable": {
+ "version": "0.1.1",
+ "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz",
+ "integrity": "sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/expand-brackets/node_modules/kind-of": {
+ "version": "5.1.0",
+ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz",
+ "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/extend-shallow": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz",
+ "integrity": "sha512-BwY5b5Ql4+qZoefgMj2NUmx+tehVTH/Kf4k1ZEtOHNFcm2wSxMRo992l6X3TIgni2eZVTZ85xMOjF31fwZAj6Q==",
+ "dev": true,
+ "dependencies": {
+ "assign-symbols": "^1.0.0",
+ "is-extendable": "^1.0.1"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/extglob": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz",
+ "integrity": "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==",
+ "dev": true,
+ "dependencies": {
+ "array-unique": "^0.3.2",
+ "define-property": "^1.0.0",
+ "expand-brackets": "^2.1.4",
+ "extend-shallow": "^2.0.1",
+ "fragment-cache": "^0.2.1",
+ "regex-not": "^1.0.0",
+ "snapdragon": "^0.8.1",
+ "to-regex": "^3.0.1"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/extglob/node_modules/define-property": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz",
+ "integrity": "sha512-cZTYKFWspt9jZsMscWo8sc/5lbPC9Q0N5nBLgb+Yd915iL3udB1uFgS3B8YCx66UVHq018DAVFoee7x+gxggeA==",
+ "dev": true,
+ "dependencies": {
+ "is-descriptor": "^1.0.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/extglob/node_modules/extend-shallow": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
+ "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==",
+ "dev": true,
+ "dependencies": {
+ "is-extendable": "^0.1.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/extglob/node_modules/is-extendable": {
+ "version": "0.1.1",
+ "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz",
+ "integrity": "sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/eyes": {
+ "version": "0.1.8",
+ "resolved": "https://registry.npmjs.org/eyes/-/eyes-0.1.8.tgz",
+ "integrity": "sha512-GipyPsXO1anza0AOZdy69Im7hGFCNB7Y/NGjDlZGJ3GJJLtwNSb2vrzYrTYJRrRloVx7pl+bhUaTB8yiccPvFQ==",
+ "dev": true,
+ "engines": {
+ "node": "> 0.1.90"
+ }
+ },
+ "node_modules/fast-safe-stringify": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/fast-safe-stringify/-/fast-safe-stringify-2.1.1.tgz",
+ "integrity": "sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA==",
+ "dev": true
+ },
+ "node_modules/fastmatter": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/fastmatter/-/fastmatter-2.1.1.tgz",
+ "integrity": "sha512-NFrjZEPJZTexoJEuyM5J7n4uFaLf0dOI7Ok4b2IZXOYBqCp1Bh5RskANmQ2TuDsz3M35B1yL2AP/Rn+kp85KeA==",
+ "dev": true,
+ "dependencies": {
+ "js-yaml": "^3.13.0",
+ "split": "^1.0.1",
+ "stream-combiner": "^0.2.2",
+ "through2": "^3.0.1"
+ }
+ },
+ "node_modules/faye-websocket": {
+ "version": "0.11.4",
+ "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.11.4.tgz",
+ "integrity": "sha512-CzbClwlXAuiRQAlUyfqPgvPoNKTckTPGfwZV4ZdAhVcP2lh9KUxJg2b5GkE7XbjKQ3YJnQ9z6D9ntLAlB+tP8g==",
+ "dev": true,
+ "dependencies": {
+ "websocket-driver": ">=0.5.1"
+ },
+ "engines": {
+ "node": ">=0.8.0"
+ }
+ },
+ "node_modules/fecha": {
+ "version": "2.3.3",
+ "resolved": "https://registry.npmjs.org/fecha/-/fecha-2.3.3.tgz",
+ "integrity": "sha512-lUGBnIamTAwk4znq5BcqsDaxSmZ9nDVJaij6NvRt/Tg4R69gERA+otPKbS86ROw9nxVMw2/mp1fnaiWqbs6Sdg==",
+ "dev": true
+ },
+ "node_modules/figlet": {
+ "version": "1.5.2",
+ "resolved": "https://registry.npmjs.org/figlet/-/figlet-1.5.2.tgz",
+ "integrity": "sha512-WOn21V8AhyE1QqVfPIVxe3tupJacq1xGkPTB4iagT6o+P2cAgEOOwIxMftr4+ZCTI6d551ij9j61DFr0nsP2uQ==",
+ "dev": true,
+ "engines": {
+ "node": ">= 0.4.0"
+ }
+ },
+ "node_modules/file-stream-rotator": {
+ "version": "0.4.1",
+ "resolved": "https://registry.npmjs.org/file-stream-rotator/-/file-stream-rotator-0.4.1.tgz",
+ "integrity": "sha512-W3aa3QJEc8BS2MmdVpQiYLKHj3ijpto1gMDlsgCRSKfIUe6MwkcpODGPQ3vZfb0XvCeCqlu9CBQTN7oQri2TZQ==",
+ "dev": true,
+ "dependencies": {
+ "moment": "^2.11.2"
+ }
+ },
+ "node_modules/file-uri-to-path": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz",
+ "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==",
+ "dev": true,
+ "optional": true
+ },
+ "node_modules/filename-reserved-regex": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/filename-reserved-regex/-/filename-reserved-regex-1.0.0.tgz",
+ "integrity": "sha512-UZArj7+U+2reBBVCvVmRlyq9D7EYQdUtuNN+1iz7pF1jGcJ2L0TjiRCxsTZfj2xFbM4c25uGCUDpKTHA7L2TKg==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/filenamify": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/filenamify/-/filenamify-1.2.1.tgz",
+ "integrity": "sha512-DKVP0WQcB7WaIMSwDETqImRej2fepPqvXQjaVib7LRZn9Rxn5UbvK2tYTqGf1A1DkIprQQkG4XSQXSOZp7Q3GQ==",
+ "dev": true,
+ "dependencies": {
+ "filename-reserved-regex": "^1.0.0",
+ "strip-outer": "^1.0.0",
+ "trim-repeated": "^1.0.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/filenamify-url": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/filenamify-url/-/filenamify-url-1.0.0.tgz",
+ "integrity": "sha512-O9K9JcZeF5VdZWM1qR92NSv1WY2EofwudQayPx5dbnnFl9k0IcZha4eV/FGkjnBK+1irOQInij0yiooCHu/0Fg==",
+ "dev": true,
+ "dependencies": {
+ "filenamify": "^1.0.0",
+ "humanize-url": "^1.0.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/fill-range": {
+ "version": "7.0.1",
+ "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz",
+ "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==",
+ "dev": true,
+ "dependencies": {
+ "to-regex-range": "^5.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/finalhandler": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz",
+ "integrity": "sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==",
+ "dev": true,
+ "dependencies": {
+ "debug": "2.6.9",
+ "encodeurl": "~1.0.2",
+ "escape-html": "~1.0.3",
+ "on-finished": "~2.3.0",
+ "parseurl": "~1.3.3",
+ "statuses": "~1.5.0",
+ "unpipe": "~1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/find-up": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz",
+ "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==",
+ "dev": true,
+ "dependencies": {
+ "locate-path": "^5.0.0",
+ "path-exists": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/for-in": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz",
+ "integrity": "sha512-7EwmXrOjyL+ChxMhmG5lnW9MPt1aIeZEwKhQzoBUdTV0N3zuwWDZYVJatDvZ2OyzPUvdIAZDsCetk3coyMfcnQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/fragment-cache": {
+ "version": "0.2.1",
+ "resolved": "https://registry.npmjs.org/fragment-cache/-/fragment-cache-0.2.1.tgz",
+ "integrity": "sha512-GMBAbW9antB8iZRHLoGw0b3HANt57diZYFO/HL1JGIC1MjKrdmhxvrJbupnVvpys0zsz7yBApXdQyfepKly2kA==",
+ "dev": true,
+ "dependencies": {
+ "map-cache": "^0.2.2"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/fresh": {
+ "version": "0.5.2",
+ "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz",
+ "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==",
+ "dev": true,
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/from": {
+ "version": "0.1.7",
+ "resolved": "https://registry.npmjs.org/from/-/from-0.1.7.tgz",
+ "integrity": "sha512-twe20eF1OxVxp/ML/kq2p1uc6KvFK/+vs8WjEbeKmV2He22MKm7YF2ANIt+EOqhJ5L3K/SuuPhk0hWQDjOM23g==",
+ "dev": true
+ },
+ "node_modules/fs-extra": {
+ "version": "9.1.0",
+ "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz",
+ "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==",
+ "dev": true,
+ "dependencies": {
+ "at-least-node": "^1.0.0",
+ "graceful-fs": "^4.2.0",
+ "jsonfile": "^6.0.1",
+ "universalify": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/fs.realpath": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
+ "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==",
+ "dev": true
+ },
+ "node_modules/fsevents": {
+ "version": "2.3.3",
+ "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
+ "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==",
+ "dev": true,
+ "hasInstallScript": true,
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": "^8.16.0 || ^10.6.0 || >=11.0.0"
+ }
+ },
+ "node_modules/function-bind": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz",
+ "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==",
+ "dev": true
+ },
+ "node_modules/get-value": {
+ "version": "2.0.6",
+ "resolved": "https://registry.npmjs.org/get-value/-/get-value-2.0.6.tgz",
+ "integrity": "sha512-Ln0UQDlxH1BapMu3GPtf7CuYNwRZf2gwCuPqbyG6pB8WfmFpzqcy4xtAaAMUhnNqjMKTiCPZG2oMT3YSx8U2NA==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/gh-pages": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/gh-pages/-/gh-pages-2.2.0.tgz",
+ "integrity": "sha512-c+yPkNOPMFGNisYg9r4qvsMIjVYikJv7ImFOhPIVPt0+AcRUamZ7zkGRLHz7FKB0xrlZ+ddSOJsZv9XAFVXLmA==",
+ "dev": true,
+ "dependencies": {
+ "async": "^2.6.1",
+ "commander": "^2.18.0",
+ "email-addresses": "^3.0.1",
+ "filenamify-url": "^1.0.0",
+ "fs-extra": "^8.1.0",
+ "globby": "^6.1.0"
+ },
+ "bin": {
+ "gh-pages": "bin/gh-pages.js",
+ "gh-pages-clean": "bin/gh-pages-clean.js"
+ },
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/gh-pages/node_modules/commander": {
+ "version": "2.20.3",
+ "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz",
+ "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==",
+ "dev": true
+ },
+ "node_modules/gh-pages/node_modules/fs-extra": {
+ "version": "8.1.0",
+ "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz",
+ "integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==",
+ "dev": true,
+ "dependencies": {
+ "graceful-fs": "^4.2.0",
+ "jsonfile": "^4.0.0",
+ "universalify": "^0.1.0"
+ },
+ "engines": {
+ "node": ">=6 <7 || >=8"
+ }
+ },
+ "node_modules/gh-pages/node_modules/jsonfile": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz",
+ "integrity": "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==",
+ "dev": true,
+ "optionalDependencies": {
+ "graceful-fs": "^4.1.6"
+ }
+ },
+ "node_modules/gh-pages/node_modules/universalify": {
+ "version": "0.1.2",
+ "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz",
+ "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==",
+ "dev": true,
+ "engines": {
+ "node": ">= 4.0.0"
+ }
+ },
+ "node_modules/glob": {
+ "version": "7.2.3",
+ "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz",
+ "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==",
+ "dev": true,
+ "dependencies": {
+ "fs.realpath": "^1.0.0",
+ "inflight": "^1.0.4",
+ "inherits": "2",
+ "minimatch": "^3.1.1",
+ "once": "^1.3.0",
+ "path-is-absolute": "^1.0.0"
+ },
+ "engines": {
+ "node": "*"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/glob-parent": {
+ "version": "5.1.2",
+ "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
+ "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
+ "dev": true,
+ "dependencies": {
+ "is-glob": "^4.0.1"
+ },
+ "engines": {
+ "node": ">= 6"
+ }
+ },
+ "node_modules/globby": {
+ "version": "6.1.0",
+ "resolved": "https://registry.npmjs.org/globby/-/globby-6.1.0.tgz",
+ "integrity": "sha512-KVbFv2TQtbzCoxAnfD6JcHZTYCzyliEaaeM/gH8qQdkKr5s0OP9scEgvdcngyk7AVdY6YVW/TJHd+lQ/Df3Daw==",
+ "dev": true,
+ "dependencies": {
+ "array-union": "^1.0.1",
+ "glob": "^7.0.3",
+ "object-assign": "^4.0.1",
+ "pify": "^2.0.0",
+ "pinkie-promise": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/graceful-fs": {
+ "version": "4.2.10",
+ "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.10.tgz",
+ "integrity": "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==",
+ "dev": true
+ },
+ "node_modules/has": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz",
+ "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==",
+ "dev": true,
+ "dependencies": {
+ "function-bind": "^1.1.1"
+ },
+ "engines": {
+ "node": ">= 0.4.0"
+ }
+ },
+ "node_modules/has-ansi": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz",
+ "integrity": "sha512-C8vBJ8DwUCx19vhm7urhTuUsr4/IyP6l4VzNQDv+ryHQObW3TTTp9yB68WpYgRe2bbaGuZ/se74IqFeVnMnLZg==",
+ "dev": true,
+ "dependencies": {
+ "ansi-regex": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/has-flag": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
+ "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/has-value": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/has-value/-/has-value-1.0.0.tgz",
+ "integrity": "sha512-IBXk4GTsLYdQ7Rvt+GRBrFSVEkmuOUy4re0Xjd9kJSUQpnTrWR4/y9RpfexN9vkAPMFuQoeWKwqzPozRTlasGw==",
+ "dev": true,
+ "dependencies": {
+ "get-value": "^2.0.6",
+ "has-values": "^1.0.0",
+ "isobject": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/has-values": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/has-values/-/has-values-1.0.0.tgz",
+ "integrity": "sha512-ODYZC64uqzmtfGMEAX/FvZiRyWLpAC3vYnNunURUnkGVTS+mI0smVsWaPydRBsE3g+ok7h960jChO8mFcWlHaQ==",
+ "dev": true,
+ "dependencies": {
+ "is-number": "^3.0.0",
+ "kind-of": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/has-values/node_modules/is-number": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz",
+ "integrity": "sha512-4cboCqIpliH+mAvFNegjZQ4kgKc3ZUhQVr3HvWbSh5q3WH2v82ct+T2Y1hdU5Gdtorx/cLifQjqCbL7bpznLTg==",
+ "dev": true,
+ "dependencies": {
+ "kind-of": "^3.0.2"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/has-values/node_modules/is-number/node_modules/kind-of": {
+ "version": "3.2.2",
+ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
+ "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==",
+ "dev": true,
+ "dependencies": {
+ "is-buffer": "^1.1.5"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/has-values/node_modules/kind-of": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-4.0.0.tgz",
+ "integrity": "sha512-24XsCxmEbRwEDbz/qz3stgin8TTzZ1ESR56OMCN0ujYg+vRutNSiOj9bHH9u85DKgXguraugV5sFuvbD4FW/hw==",
+ "dev": true,
+ "dependencies": {
+ "is-buffer": "^1.1.5"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/hash-sum": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/hash-sum/-/hash-sum-1.0.2.tgz",
+ "integrity": "sha512-fUs4B4L+mlt8/XAtSOGMUO1TXmAelItBPtJG7CyHJfYTdDjwisntGO2JQz7oUsatOY9o68+57eziUVNw/mRHmA==",
+ "dev": true
+ },
+ "node_modules/he": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz",
+ "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==",
+ "dev": true,
+ "bin": {
+ "he": "bin/he"
+ }
+ },
+ "node_modules/highlight.js": {
+ "version": "10.7.3",
+ "resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-10.7.3.tgz",
+ "integrity": "sha512-tzcUFauisWKNHaRkN4Wjl/ZA07gENAjFl3J/c480dprkGTg5EQstgaNFqBfUqCq54kZRIEcreTsAgF/m2quD7A==",
+ "dev": true,
+ "engines": {
+ "node": "*"
+ }
+ },
+ "node_modules/htmlparser2": {
+ "version": "3.10.1",
+ "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-3.10.1.tgz",
+ "integrity": "sha512-IgieNijUMbkDovyoKObU1DUhm1iwNYE/fuifEoEHfd1oZKZDaONBSkal7Y01shxsM49R4XaMdGez3WnF9UfiCQ==",
+ "dev": true,
+ "dependencies": {
+ "domelementtype": "^1.3.1",
+ "domhandler": "^2.3.0",
+ "domutils": "^1.5.1",
+ "entities": "^1.1.1",
+ "inherits": "^2.0.1",
+ "readable-stream": "^3.1.1"
+ }
+ },
+ "node_modules/http-auth": {
+ "version": "3.1.3",
+ "resolved": "https://registry.npmjs.org/http-auth/-/http-auth-3.1.3.tgz",
+ "integrity": "sha512-Jbx0+ejo2IOx+cRUYAGS1z6RGc6JfYUNkysZM4u4Sfk1uLlGv814F7/PIjQQAuThLdAWxb74JMGd5J8zex1VQg==",
+ "dev": true,
+ "dependencies": {
+ "apache-crypt": "^1.1.2",
+ "apache-md5": "^1.0.6",
+ "bcryptjs": "^2.3.0",
+ "uuid": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=4.6.1"
+ }
+ },
+ "node_modules/http-auth/node_modules/uuid": {
+ "version": "3.4.0",
+ "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz",
+ "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==",
+ "deprecated": "Please upgrade to version 7 or higher. Older versions may use Math.random() in certain circumstances, which is known to be problematic. See https://v8.dev/blog/math-random for details.",
+ "dev": true,
+ "bin": {
+ "uuid": "bin/uuid"
+ }
+ },
+ "node_modules/http-errors": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz",
+ "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==",
+ "dev": true,
+ "dependencies": {
+ "depd": "2.0.0",
+ "inherits": "2.0.4",
+ "setprototypeof": "1.2.0",
+ "statuses": "2.0.1",
+ "toidentifier": "1.0.1"
+ },
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/http-errors/node_modules/statuses": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz",
+ "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==",
+ "dev": true,
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/http-parser-js": {
+ "version": "0.5.8",
+ "resolved": "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.5.8.tgz",
+ "integrity": "sha512-SGeBX54F94Wgu5RH3X5jsDtf4eHyRogWX1XGT3b4HuW3tQPM4AaBzoUji/4AAJNXCEOWZ5O0DgZmJw1947gD5Q==",
+ "dev": true
+ },
+ "node_modules/humanize-url": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/humanize-url/-/humanize-url-1.0.1.tgz",
+ "integrity": "sha512-RtgTzXCPVb/te+e82NDhAc5paj+DuKSratIGAr+v+HZK24eAQ8LMoBGYoL7N/O+9iEc33AKHg45dOMKw3DNldQ==",
+ "dev": true,
+ "dependencies": {
+ "normalize-url": "^1.0.0",
+ "strip-url-auth": "^1.0.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/ignore": {
+ "version": "5.2.4",
+ "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.4.tgz",
+ "integrity": "sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ==",
+ "dev": true,
+ "engines": {
+ "node": ">= 4"
+ }
+ },
+ "node_modules/inflight": {
+ "version": "1.0.6",
+ "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
+ "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==",
+ "dev": true,
+ "dependencies": {
+ "once": "^1.3.0",
+ "wrappy": "1"
+ }
+ },
+ "node_modules/inherits": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
+ "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
+ "dev": true
+ },
+ "node_modules/ini": {
+ "version": "1.3.8",
+ "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz",
+ "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==",
+ "dev": true
+ },
+ "node_modules/is-accessor-descriptor": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz",
+ "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==",
+ "dev": true,
+ "dependencies": {
+ "kind-of": "^6.0.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/is-binary-path": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz",
+ "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==",
+ "dev": true,
+ "dependencies": {
+ "binary-extensions": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/is-buffer": {
+ "version": "1.1.6",
+ "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz",
+ "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==",
+ "dev": true
+ },
+ "node_modules/is-core-module": {
+ "version": "2.13.0",
+ "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.13.0.tgz",
+ "integrity": "sha512-Z7dk6Qo8pOCp3l4tsX2C5ZVas4V+UxwQodwZhLopL91TX8UyyHEXafPcyoeeWuLrwzHcr3igO78wNLwHJHsMCQ==",
+ "dev": true,
+ "dependencies": {
+ "has": "^1.0.3"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-data-descriptor": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz",
+ "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==",
+ "dev": true,
+ "dependencies": {
+ "kind-of": "^6.0.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/is-descriptor": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz",
+ "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==",
+ "dev": true,
+ "dependencies": {
+ "is-accessor-descriptor": "^1.0.0",
+ "is-data-descriptor": "^1.0.0",
+ "kind-of": "^6.0.2"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/is-extendable": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz",
+ "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==",
+ "dev": true,
+ "dependencies": {
+ "is-plain-object": "^2.0.4"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/is-extglob": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
+ "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/is-glob": {
+ "version": "4.0.3",
+ "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz",
+ "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==",
+ "dev": true,
+ "dependencies": {
+ "is-extglob": "^2.1.1"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/is-number": {
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
+ "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.12.0"
+ }
+ },
+ "node_modules/is-plain-obj": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-1.1.0.tgz",
+ "integrity": "sha512-yvkRyxmFKEOQ4pNXCmJG5AEQNlXJS5LaONXo5/cLdTZdWvsZ1ioJEonLGAosKlMWE8lwUy/bJzMjcw8az73+Fg==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/is-plain-object": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz",
+ "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==",
+ "dev": true,
+ "dependencies": {
+ "isobject": "^3.0.1"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/is-windows": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz",
+ "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/is-wsl": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-1.1.0.tgz",
+ "integrity": "sha512-gfygJYZ2gLTDlmbWMI0CE2MwnFzSN/2SZfkMlItC4K/JBlsWVDB0bO6XhqcY13YXE7iMcAJnzTCJjPiTeJJ0Mw==",
+ "dev": true,
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/isarray": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
+ "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==",
+ "dev": true
+ },
+ "node_modules/isobject": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz",
+ "integrity": "sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/isstream": {
+ "version": "0.1.2",
+ "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz",
+ "integrity": "sha512-Yljz7ffyPbrLpLngrMtZ7NduUgVvi6wG9RJ9IUcyCd59YQ911PBJphODUcbOVbqYfxe1wuYf/LJ8PauMRwsM/g==",
+ "dev": true
+ },
+ "node_modules/js-beautify": {
+ "version": "1.14.3",
+ "resolved": "https://registry.npmjs.org/js-beautify/-/js-beautify-1.14.3.tgz",
+ "integrity": "sha512-f1ra8PHtOEu/70EBnmiUlV8nJePS58y9qKjl4JHfYWlFH6bo7ogZBz//FAZp7jDuXtYnGYKymZPlrg2I/9Zo4g==",
+ "dev": true,
+ "dependencies": {
+ "config-chain": "^1.1.13",
+ "editorconfig": "^0.15.3",
+ "glob": "^7.1.3",
+ "nopt": "^5.0.0"
+ },
+ "bin": {
+ "css-beautify": "js/bin/css-beautify.js",
+ "html-beautify": "js/bin/html-beautify.js",
+ "js-beautify": "js/bin/js-beautify.js"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/js-yaml": {
+ "version": "3.14.1",
+ "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz",
+ "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==",
+ "dev": true,
+ "dependencies": {
+ "argparse": "^1.0.7",
+ "esprima": "^4.0.0"
+ },
+ "bin": {
+ "js-yaml": "bin/js-yaml.js"
+ }
+ },
+ "node_modules/jsonfile": {
+ "version": "6.1.0",
+ "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz",
+ "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==",
+ "dev": true,
+ "dependencies": {
+ "universalify": "^2.0.0"
+ },
+ "optionalDependencies": {
+ "graceful-fs": "^4.1.6"
+ }
+ },
+ "node_modules/katex": {
+ "version": "0.15.6",
+ "resolved": "https://registry.npmjs.org/katex/-/katex-0.15.6.tgz",
+ "integrity": "sha512-UpzJy4yrnqnhXvRPhjEuLA4lcPn6eRngixW7Q3TJErjg3Aw2PuLFBzTkdUb89UtumxjhHTqL3a5GDGETMSwgJA==",
+ "dev": true,
+ "funding": [
+ "https://opencollective.com/katex",
+ "https://github.com/sponsors/katex"
+ ],
+ "dependencies": {
+ "commander": "^8.0.0"
+ },
+ "bin": {
+ "katex": "cli.js"
+ }
+ },
+ "node_modules/kind-of": {
+ "version": "6.0.3",
+ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz",
+ "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/linkify-it": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-3.0.3.tgz",
+ "integrity": "sha512-ynTsyrFSdE5oZ/O9GEf00kPngmOfVwazR5GKDq6EYfhlpFug3J2zybX56a2PRRpc9P+FuSoGNAwjlbDs9jJBPQ==",
+ "dev": true,
+ "dependencies": {
+ "uc.micro": "^1.0.1"
+ }
+ },
+ "node_modules/live-server": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/live-server/-/live-server-1.2.1.tgz",
+ "integrity": "sha512-Yn2XCVjErTkqnM3FfTmM7/kWy3zP7+cEtC7x6u+wUzlQ+1UW3zEYbbyJrc0jNDwiMDZI0m4a0i3dxlGHVyXczw==",
+ "dev": true,
+ "dependencies": {
+ "chokidar": "^2.0.4",
+ "colors": "latest",
+ "connect": "^3.6.6",
+ "cors": "latest",
+ "event-stream": "3.3.4",
+ "faye-websocket": "0.11.x",
+ "http-auth": "3.1.x",
+ "morgan": "^1.9.1",
+ "object-assign": "latest",
+ "opn": "latest",
+ "proxy-middleware": "latest",
+ "send": "latest",
+ "serve-index": "^1.9.1"
+ },
+ "bin": {
+ "live-server": "live-server.js"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/live-server/node_modules/anymatch": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-2.0.0.tgz",
+ "integrity": "sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw==",
+ "dev": true,
+ "dependencies": {
+ "micromatch": "^3.1.4",
+ "normalize-path": "^2.1.1"
+ }
+ },
+ "node_modules/live-server/node_modules/anymatch/node_modules/normalize-path": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz",
+ "integrity": "sha512-3pKJwH184Xo/lnH6oyP1q2pMd7HcypqqmRs91/6/i2CGtWwIKGCkOOMTm/zXbgTEWHw1uNpNi/igc3ePOYHb6w==",
+ "dev": true,
+ "dependencies": {
+ "remove-trailing-separator": "^1.0.1"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/live-server/node_modules/binary-extensions": {
+ "version": "1.13.1",
+ "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-1.13.1.tgz",
+ "integrity": "sha512-Un7MIEDdUC5gNpcGDV97op1Ywk748MpHcFTHoYs6qnj1Z3j7I53VG3nwZhKzoBZmbdRNnb6WRdFlwl7tSDuZGw==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/live-server/node_modules/braces": {
+ "version": "2.3.2",
+ "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz",
+ "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==",
+ "dev": true,
+ "dependencies": {
+ "arr-flatten": "^1.1.0",
+ "array-unique": "^0.3.2",
+ "extend-shallow": "^2.0.1",
+ "fill-range": "^4.0.0",
+ "isobject": "^3.0.1",
+ "repeat-element": "^1.1.2",
+ "snapdragon": "^0.8.1",
+ "snapdragon-node": "^2.0.1",
+ "split-string": "^3.0.2",
+ "to-regex": "^3.0.1"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/live-server/node_modules/chokidar": {
+ "version": "2.1.8",
+ "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-2.1.8.tgz",
+ "integrity": "sha512-ZmZUazfOzf0Nve7duiCKD23PFSCs4JPoYyccjUFF3aQkQadqBhfzhjkwBH2mNOG9cTBwhamM37EIsIkZw3nRgg==",
+ "deprecated": "Chokidar 2 does not receive security updates since 2019. Upgrade to chokidar 3 with 15x fewer dependencies",
+ "dev": true,
+ "dependencies": {
+ "anymatch": "^2.0.0",
+ "async-each": "^1.0.1",
+ "braces": "^2.3.2",
+ "glob-parent": "^3.1.0",
+ "inherits": "^2.0.3",
+ "is-binary-path": "^1.0.0",
+ "is-glob": "^4.0.0",
+ "normalize-path": "^3.0.0",
+ "path-is-absolute": "^1.0.0",
+ "readdirp": "^2.2.1",
+ "upath": "^1.1.1"
+ },
+ "optionalDependencies": {
+ "fsevents": "^1.2.7"
+ }
+ },
+ "node_modules/live-server/node_modules/extend-shallow": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
+ "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==",
+ "dev": true,
+ "dependencies": {
+ "is-extendable": "^0.1.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/live-server/node_modules/fill-range": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz",
+ "integrity": "sha512-VcpLTWqWDiTerugjj8e3+esbg+skS3M9e54UuR3iCeIDMXCLTsAH8hTSzDQU/X6/6t3eYkOKoZSef2PlU6U1XQ==",
+ "dev": true,
+ "dependencies": {
+ "extend-shallow": "^2.0.1",
+ "is-number": "^3.0.0",
+ "repeat-string": "^1.6.1",
+ "to-regex-range": "^2.1.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/live-server/node_modules/fsevents": {
+ "version": "1.2.13",
+ "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.13.tgz",
+ "integrity": "sha512-oWb1Z6mkHIskLzEJ/XWX0srkpkTQ7vaopMQkyaEIoq0fmtFVxOthb8cCxeT+p3ynTdkk/RZwbgG4brR5BeWECw==",
+ "deprecated": "fsevents 1 will break on node v14+ and could be using insecure binaries. Upgrade to fsevents 2.",
+ "dev": true,
+ "hasInstallScript": true,
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "dependencies": {
+ "bindings": "^1.5.0",
+ "nan": "^2.12.1"
+ },
+ "engines": {
+ "node": ">= 4.0"
+ }
+ },
+ "node_modules/live-server/node_modules/glob-parent": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz",
+ "integrity": "sha512-E8Ak/2+dZY6fnzlR7+ueWvhsH1SjHr4jjss4YS/h4py44jY9MhK/VFdaZJAWDz6BbL21KeteKxFSFpq8OS5gVA==",
+ "dev": true,
+ "dependencies": {
+ "is-glob": "^3.1.0",
+ "path-dirname": "^1.0.0"
+ }
+ },
+ "node_modules/live-server/node_modules/glob-parent/node_modules/is-glob": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz",
+ "integrity": "sha512-UFpDDrPgM6qpnFNI+rh/p3bUaq9hKLZN8bMUWzxmcnZVS3omf4IPK+BrewlnWjO1WmUsMYuSjKh4UJuV4+Lqmw==",
+ "dev": true,
+ "dependencies": {
+ "is-extglob": "^2.1.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/live-server/node_modules/is-binary-path": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-1.0.1.tgz",
+ "integrity": "sha512-9fRVlXc0uCxEDj1nQzaWONSpbTfx0FmJfzHF7pwlI8DkWGoHBBea4Pg5Ky0ojwwxQmnSifgbKkI06Qv0Ljgj+Q==",
+ "dev": true,
+ "dependencies": {
+ "binary-extensions": "^1.0.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/live-server/node_modules/is-extendable": {
+ "version": "0.1.1",
+ "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz",
+ "integrity": "sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/live-server/node_modules/is-number": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz",
+ "integrity": "sha512-4cboCqIpliH+mAvFNegjZQ4kgKc3ZUhQVr3HvWbSh5q3WH2v82ct+T2Y1hdU5Gdtorx/cLifQjqCbL7bpznLTg==",
+ "dev": true,
+ "dependencies": {
+ "kind-of": "^3.0.2"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/live-server/node_modules/kind-of": {
+ "version": "3.2.2",
+ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
+ "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==",
+ "dev": true,
+ "dependencies": {
+ "is-buffer": "^1.1.5"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/live-server/node_modules/readable-stream": {
+ "version": "2.3.7",
+ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz",
+ "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==",
+ "dev": true,
+ "dependencies": {
+ "core-util-is": "~1.0.0",
+ "inherits": "~2.0.3",
+ "isarray": "~1.0.0",
+ "process-nextick-args": "~2.0.0",
+ "safe-buffer": "~5.1.1",
+ "string_decoder": "~1.1.1",
+ "util-deprecate": "~1.0.1"
+ }
+ },
+ "node_modules/live-server/node_modules/readdirp": {
+ "version": "2.2.1",
+ "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-2.2.1.tgz",
+ "integrity": "sha512-1JU/8q+VgFZyxwrJ+SVIOsh+KywWGpds3NTqikiKpDMZWScmAYyKIgqkO+ARvNWJfXeXR1zxz7aHF4u4CyH6vQ==",
+ "dev": true,
+ "dependencies": {
+ "graceful-fs": "^4.1.11",
+ "micromatch": "^3.1.10",
+ "readable-stream": "^2.0.2"
+ },
+ "engines": {
+ "node": ">=0.10"
+ }
+ },
+ "node_modules/live-server/node_modules/string_decoder": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
+ "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
+ "dev": true,
+ "dependencies": {
+ "safe-buffer": "~5.1.0"
+ }
+ },
+ "node_modules/live-server/node_modules/to-regex-range": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz",
+ "integrity": "sha512-ZZWNfCjUokXXDGXFpZehJIkZqq91BcULFq/Pi7M5i4JnxXdhMKAK682z8bCW3o8Hj1wuuzoKcW3DfVzaP6VuNg==",
+ "dev": true,
+ "dependencies": {
+ "is-number": "^3.0.0",
+ "repeat-string": "^1.6.1"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/locate-path": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz",
+ "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==",
+ "dev": true,
+ "dependencies": {
+ "p-locate": "^4.1.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/lodash": {
+ "version": "4.17.21",
+ "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
+ "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==",
+ "dev": true
+ },
+ "node_modules/lodash._reinterpolate": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/lodash._reinterpolate/-/lodash._reinterpolate-3.0.0.tgz",
+ "integrity": "sha512-xYHt68QRoYGjeeM/XOE1uJtvXQAgvszfBhjV4yvsQH0u2i9I6cI6c6/eG4Hh3UAOVn0y/xAXwmTzEay49Q//HA==",
+ "dev": true
+ },
+ "node_modules/lodash.assignin": {
+ "version": "4.2.0",
+ "resolved": "https://registry.npmjs.org/lodash.assignin/-/lodash.assignin-4.2.0.tgz",
+ "integrity": "sha512-yX/rx6d/UTVh7sSVWVSIMjfnz95evAgDFdb1ZozC35I9mSFCkmzptOzevxjgbQUsc78NR44LVHWjsoMQXy9FDg==",
+ "dev": true
+ },
+ "node_modules/lodash.bind": {
+ "version": "4.2.1",
+ "resolved": "https://registry.npmjs.org/lodash.bind/-/lodash.bind-4.2.1.tgz",
+ "integrity": "sha512-lxdsn7xxlCymgLYo1gGvVrfHmkjDiyqVv62FAeF2i5ta72BipE1SLxw8hPEPLhD4/247Ijw07UQH7Hq/chT5LA==",
+ "dev": true
+ },
+ "node_modules/lodash.deburr": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/lodash.deburr/-/lodash.deburr-4.1.0.tgz",
+ "integrity": "sha512-m/M1U1f3ddMCs6Hq2tAsYThTBDaAKFDX3dwDo97GEYzamXi9SqUpjWi/Rrj/gf3X2n8ktwgZrlP1z6E3v/IExQ==",
+ "dev": true
+ },
+ "node_modules/lodash.defaults": {
+ "version": "4.2.0",
+ "resolved": "https://registry.npmjs.org/lodash.defaults/-/lodash.defaults-4.2.0.tgz",
+ "integrity": "sha512-qjxPLHd3r5DnsdGacqOMU6pb/avJzdh9tFX2ymgoZE27BmjXrNy/y4LoaiTeAb+O3gL8AfpJGtqfX/ae2leYYQ==",
+ "dev": true
+ },
+ "node_modules/lodash.filter": {
+ "version": "4.6.0",
+ "resolved": "https://registry.npmjs.org/lodash.filter/-/lodash.filter-4.6.0.tgz",
+ "integrity": "sha512-pXYUy7PR8BCLwX5mgJ/aNtyOvuJTdZAo9EQFUvMIYugqmJxnrYaANvTbgndOzHSCSR0wnlBBfRXJL5SbWxo3FQ==",
+ "dev": true
+ },
+ "node_modules/lodash.flatten": {
+ "version": "4.4.0",
+ "resolved": "https://registry.npmjs.org/lodash.flatten/-/lodash.flatten-4.4.0.tgz",
+ "integrity": "sha512-C5N2Z3DgnnKr0LOpv/hKCgKdb7ZZwafIrsesve6lmzvZIRZRGaZ/l6Q8+2W7NaT+ZwO3fFlSCzCzrDCFdJfZ4g==",
+ "dev": true
+ },
+ "node_modules/lodash.foreach": {
+ "version": "4.5.0",
+ "resolved": "https://registry.npmjs.org/lodash.foreach/-/lodash.foreach-4.5.0.tgz",
+ "integrity": "sha512-aEXTF4d+m05rVOAUG3z4vZZ4xVexLKZGF0lIxuHZ1Hplpk/3B6Z1+/ICICYRLm7c41Z2xiejbkCkJoTlypoXhQ==",
+ "dev": true
+ },
+ "node_modules/lodash.map": {
+ "version": "4.6.0",
+ "resolved": "https://registry.npmjs.org/lodash.map/-/lodash.map-4.6.0.tgz",
+ "integrity": "sha512-worNHGKLDetmcEYDvh2stPCrrQRkP20E4l0iIS7F8EvzMqBBi7ltvFN5m1HvTf1P7Jk1txKhvFcmYsCr8O2F1Q==",
+ "dev": true
+ },
+ "node_modules/lodash.merge": {
+ "version": "4.6.2",
+ "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz",
+ "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==",
+ "dev": true
+ },
+ "node_modules/lodash.pick": {
+ "version": "4.4.0",
+ "resolved": "https://registry.npmjs.org/lodash.pick/-/lodash.pick-4.4.0.tgz",
+ "integrity": "sha512-hXt6Ul/5yWjfklSGvLQl8vM//l3FtyHZeuelpzK6mm99pNvN9yTDruNZPEJZD1oWrqo+izBmB7oUfWgcCX7s4Q==",
+ "dev": true
+ },
+ "node_modules/lodash.reduce": {
+ "version": "4.6.0",
+ "resolved": "https://registry.npmjs.org/lodash.reduce/-/lodash.reduce-4.6.0.tgz",
+ "integrity": "sha512-6raRe2vxCYBhpBu+B+TtNGUzah+hQjVdu3E17wfusjyrXBka2nBS8OH/gjVZ5PvHOhWmIZTYri09Z6n/QfnNMw==",
+ "dev": true
+ },
+ "node_modules/lodash.reject": {
+ "version": "4.6.0",
+ "resolved": "https://registry.npmjs.org/lodash.reject/-/lodash.reject-4.6.0.tgz",
+ "integrity": "sha512-qkTuvgEzYdyhiJBx42YPzPo71R1aEr0z79kAv7Ixg8wPFEjgRgJdUsGMG3Hf3OYSF/kHI79XhNlt+5Ar6OzwxQ==",
+ "dev": true
+ },
+ "node_modules/lodash.some": {
+ "version": "4.6.0",
+ "resolved": "https://registry.npmjs.org/lodash.some/-/lodash.some-4.6.0.tgz",
+ "integrity": "sha512-j7MJE+TuT51q9ggt4fSgVqro163BEFjAt3u97IqU+JA2DkWl80nFTrowzLpZ/BnpN7rrl0JA/593NAdd8p/scQ==",
+ "dev": true
+ },
+ "node_modules/lodash.template": {
+ "version": "4.5.0",
+ "resolved": "https://registry.npmjs.org/lodash.template/-/lodash.template-4.5.0.tgz",
+ "integrity": "sha512-84vYFxIkmidUiFxidA/KjjH9pAycqW+h980j7Fuz5qxRtO9pgB7MDFTdys1N7A5mcucRiDyEq4fusljItR1T/A==",
+ "dev": true,
+ "dependencies": {
+ "lodash._reinterpolate": "^3.0.0",
+ "lodash.templatesettings": "^4.0.0"
+ }
+ },
+ "node_modules/lodash.templatesettings": {
+ "version": "4.2.0",
+ "resolved": "https://registry.npmjs.org/lodash.templatesettings/-/lodash.templatesettings-4.2.0.tgz",
+ "integrity": "sha512-stgLz+i3Aa9mZgnjr/O+v9ruKZsPsndy7qPZOchbqk2cnTU1ZaldKK+v7m54WoKIyxiuMZTKT2H81F8BeAc3ZQ==",
+ "dev": true,
+ "dependencies": {
+ "lodash._reinterpolate": "^3.0.0"
+ }
+ },
+ "node_modules/lodash.uniq": {
+ "version": "4.5.0",
+ "resolved": "https://registry.npmjs.org/lodash.uniq/-/lodash.uniq-4.5.0.tgz",
+ "integrity": "sha512-xfBaXQd9ryd9dlSDvnvI0lvxfLJlYAZzXomUYzLKtUeOQvOP5piqAWuGtrhWeqaXK9hhoM/iyJc5AV+XfsX3HQ==",
+ "dev": true
+ },
+ "node_modules/logform": {
+ "version": "1.10.0",
+ "resolved": "https://registry.npmjs.org/logform/-/logform-1.10.0.tgz",
+ "integrity": "sha512-em5ojIhU18fIMOw/333mD+ZLE2fis0EzXl1ZwHx4iQzmpQi6odNiY/t+ITNr33JZhT9/KEaH+UPIipr6a9EjWg==",
+ "dev": true,
+ "dependencies": {
+ "colors": "^1.2.1",
+ "fast-safe-stringify": "^2.0.4",
+ "fecha": "^2.3.3",
+ "ms": "^2.1.1",
+ "triple-beam": "^1.2.0"
+ }
+ },
+ "node_modules/logform/node_modules/ms": {
+ "version": "2.1.3",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
+ "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
+ "dev": true
+ },
+ "node_modules/lru-cache": {
+ "version": "4.1.5",
+ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.5.tgz",
+ "integrity": "sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==",
+ "dev": true,
+ "dependencies": {
+ "pseudomap": "^1.0.2",
+ "yallist": "^2.1.2"
+ }
+ },
+ "node_modules/map-cache": {
+ "version": "0.2.2",
+ "resolved": "https://registry.npmjs.org/map-cache/-/map-cache-0.2.2.tgz",
+ "integrity": "sha512-8y/eV9QQZCiyn1SprXSrCmqJN0yNRATe+PO8ztwqrvrbdRLA3eYJF0yaR0YayLWkMbsQSKWS9N2gPcGEc4UsZg==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/map-stream": {
+ "version": "0.1.0",
+ "resolved": "https://registry.npmjs.org/map-stream/-/map-stream-0.1.0.tgz",
+ "integrity": "sha512-CkYQrPYZfWnu/DAmVCpTSX/xHpKZ80eKh2lAkyA6AJTef6bW+6JpbQZN5rofum7da+SyN1bi5ctTm+lTfcCW3g==",
+ "dev": true
+ },
+ "node_modules/map-visit": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/map-visit/-/map-visit-1.0.0.tgz",
+ "integrity": "sha512-4y7uGv8bd2WdM9vpQsiQNo41Ln1NvhvDRuVt0k2JZQ+ezN2uaQes7lZeZ+QQUHOLQAtDaBJ+7wCbi+ab/KFs+w==",
+ "dev": true,
+ "dependencies": {
+ "object-visit": "^1.0.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/markbind-cli": {
+ "version": "5.1.0",
+ "resolved": "https://registry.npmjs.org/markbind-cli/-/markbind-cli-5.1.0.tgz",
+ "integrity": "sha512-6POI1Q++2aZa+Udk/oQ6LX1oNPbKUBDY0mN3Up7VOFeK+XYW51faxuCk2Q91JTBxYRKLNtshxf0y12kB4Cj9Qw==",
+ "dev": true,
+ "dependencies": {
+ "@markbind/core": "5.1.0",
+ "@markbind/core-web": "5.1.0",
+ "bluebird": "^3.7.2",
+ "chalk": "^3.0.0",
+ "cheerio": "^0.22.0",
+ "chokidar": "^3.3.0",
+ "colors": "1.4.0",
+ "commander": "^8.1.0",
+ "figlet": "^1.2.4",
+ "find-up": "^4.1.0",
+ "fs-extra": "^9.0.1",
+ "live-server": "1.2.1",
+ "lodash": "^4.17.15",
+ "url-parse": "^1.5.10",
+ "winston": "^2.4.4",
+ "winston-daily-rotate-file": "^3.10.0"
+ },
+ "bin": {
+ "markbind": "index.js"
+ }
+ },
+ "node_modules/markdown-it": {
+ "version": "12.3.2",
+ "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-12.3.2.tgz",
+ "integrity": "sha512-TchMembfxfNVpHkbtriWltGWc+m3xszaRD0CZup7GFFhzIgQqxIfn3eGj1yZpfuflzPvfkt611B2Q/Bsk1YnGg==",
+ "dev": true,
+ "dependencies": {
+ "argparse": "^2.0.1",
+ "entities": "~2.1.0",
+ "linkify-it": "^3.0.1",
+ "mdurl": "^1.0.1",
+ "uc.micro": "^1.0.5"
+ },
+ "bin": {
+ "markdown-it": "bin/markdown-it.js"
+ }
+ },
+ "node_modules/markdown-it-attrs": {
+ "version": "4.1.6",
+ "resolved": "https://registry.npmjs.org/markdown-it-attrs/-/markdown-it-attrs-4.1.6.tgz",
+ "integrity": "sha512-O7PDKZlN8RFMyDX13JnctQompwrrILuz2y43pW2GagcwpIIElkAdfeek+erHfxUOlXWPsjFeWmZ8ch1xtRLWpA==",
+ "dev": true,
+ "engines": {
+ "node": ">=6"
+ },
+ "peerDependencies": {
+ "markdown-it": ">= 9.0.0"
+ }
+ },
+ "node_modules/markdown-it-emoji": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/markdown-it-emoji/-/markdown-it-emoji-1.4.0.tgz",
+ "integrity": "sha512-QCz3Hkd+r5gDYtS2xsFXmBYrgw6KuWcJZLCEkdfAuwzZbShCmCfta+hwAMq4NX/4xPzkSHduMKgMkkPUJxSXNg==",
+ "dev": true
+ },
+ "node_modules/markdown-it-linkify-images": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/markdown-it-linkify-images/-/markdown-it-linkify-images-3.0.0.tgz",
+ "integrity": "sha512-Vs5yGJa5MWjFgytzgtn8c1U6RcStj3FZKhhx459U8dYbEE5FTWZ6mMRkYMiDlkFO0j4VCsQT1LT557bY0ETgtg==",
+ "dev": true,
+ "dependencies": {
+ "markdown-it": "^13.0.1"
+ }
+ },
+ "node_modules/markdown-it-linkify-images/node_modules/argparse": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
+ "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==",
+ "dev": true
+ },
+ "node_modules/markdown-it-linkify-images/node_modules/entities": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/entities/-/entities-3.0.1.tgz",
+ "integrity": "sha512-WiyBqoomrwMdFG1e0kqvASYfnlb0lp8M5o5Fw2OFq1hNZxxcNk8Ik0Xm7LxzBhuidnZB/UtBqVCgUz3kBOP51Q==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.12"
+ },
+ "funding": {
+ "url": "https://github.com/fb55/entities?sponsor=1"
+ }
+ },
+ "node_modules/markdown-it-linkify-images/node_modules/linkify-it": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-4.0.1.tgz",
+ "integrity": "sha512-C7bfi1UZmoj8+PQx22XyeXCuBlokoyWQL5pWSP+EI6nzRylyThouddufc2c1NDIcP9k5agmN9fLpA7VNJfIiqw==",
+ "dev": true,
+ "dependencies": {
+ "uc.micro": "^1.0.1"
+ }
+ },
+ "node_modules/markdown-it-linkify-images/node_modules/markdown-it": {
+ "version": "13.0.1",
+ "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-13.0.1.tgz",
+ "integrity": "sha512-lTlxriVoy2criHP0JKRhO2VDG9c2ypWCsT237eDiLqi09rmbKoUetyGHq2uOIRoRS//kfoJckS0eUzzkDR+k2Q==",
+ "dev": true,
+ "dependencies": {
+ "argparse": "^2.0.1",
+ "entities": "~3.0.1",
+ "linkify-it": "^4.0.1",
+ "mdurl": "^1.0.1",
+ "uc.micro": "^1.0.5"
+ },
+ "bin": {
+ "markdown-it": "bin/markdown-it.js"
+ }
+ },
+ "node_modules/markdown-it-mark": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/markdown-it-mark/-/markdown-it-mark-3.0.1.tgz",
+ "integrity": "sha512-HyxjAu6BRsdt6Xcv6TKVQnkz/E70TdGXEFHRYBGLncRE9lBFwDNLVtFojKxjJWgJ+5XxUwLaHXy+2sGBbDn+4A==",
+ "dev": true
+ },
+ "node_modules/markdown-it-regexp": {
+ "version": "0.4.0",
+ "resolved": "https://registry.npmjs.org/markdown-it-regexp/-/markdown-it-regexp-0.4.0.tgz",
+ "integrity": "sha512-0XQmr46K/rMKnI93Y3CLXsHj4jIioRETTAiVnJnjrZCEkGaDOmUxTbZj/aZ17G5NlRcVpWBYjqpwSlQ9lj+Kxw==",
+ "dev": true
+ },
+ "node_modules/markdown-it-sub": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/markdown-it-sub/-/markdown-it-sub-1.0.0.tgz",
+ "integrity": "sha512-z2Rm/LzEE1wzwTSDrI+FlPEveAAbgdAdPhdWarq/ZGJrGW/uCQbKAnhoCsE4hAbc3SEym26+W2z/VQB0cQiA9Q==",
+ "dev": true
+ },
+ "node_modules/markdown-it-sup": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/markdown-it-sup/-/markdown-it-sup-1.0.0.tgz",
+ "integrity": "sha512-E32m0nV9iyhRR7CrhnzL5msqic7rL1juWre6TQNxsnApg7Uf+F97JOKxUijg5YwXz86lZ0mqfOnutoryyNdntQ==",
+ "dev": true
+ },
+ "node_modules/markdown-it-table-of-contents": {
+ "version": "0.4.4",
+ "resolved": "https://registry.npmjs.org/markdown-it-table-of-contents/-/markdown-it-table-of-contents-0.4.4.tgz",
+ "integrity": "sha512-TAIHTHPwa9+ltKvKPWulm/beozQU41Ab+FIefRaQV1NRnpzwcV9QOe6wXQS5WLivm5Q/nlo0rl6laGkMDZE7Gw==",
+ "dev": true,
+ "engines": {
+ "node": ">6.4.0"
+ }
+ },
+ "node_modules/markdown-it-task-lists": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/markdown-it-task-lists/-/markdown-it-task-lists-2.1.1.tgz",
+ "integrity": "sha512-TxFAc76Jnhb2OUu+n3yz9RMu4CwGfaT788br6HhEDlvWfdeJcLUsxk1Hgw2yJio0OXsxv7pyIPmvECY7bMbluA==",
+ "dev": true
+ },
+ "node_modules/markdown-it-texmath": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/markdown-it-texmath/-/markdown-it-texmath-1.0.0.tgz",
+ "integrity": "sha512-4hhkiX8/gus+6e53PLCUmUrsa6ZWGgJW2XCW6O0ASvZUiezIK900ZicinTDtG3kAO2kon7oUA/ReWmpW2FByxg==",
+ "dev": true
+ },
+ "node_modules/markdown-it-video": {
+ "version": "0.6.3",
+ "resolved": "https://registry.npmjs.org/markdown-it-video/-/markdown-it-video-0.6.3.tgz",
+ "integrity": "sha512-T4th1kwy0OcvyWSN4u3rqPGxvbDclpucnVSSaH3ZacbGsAts964dxokx9s/I3GYsrDCJs4ogtEeEeVP18DQj0Q==",
+ "dev": true
+ },
+ "node_modules/markdown-it/node_modules/argparse": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
+ "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==",
+ "dev": true
+ },
+ "node_modules/markdown-it/node_modules/entities": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/entities/-/entities-2.1.0.tgz",
+ "integrity": "sha512-hCx1oky9PFrJ611mf0ifBLBRW8lUUVRlFolb5gWRfIELabBlbp9xZvrqZLZAs+NxFnbfQoeGd8wDkygjg7U85w==",
+ "dev": true,
+ "funding": {
+ "url": "https://github.com/fb55/entities?sponsor=1"
+ }
+ },
+ "node_modules/matcher-collection": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/matcher-collection/-/matcher-collection-2.0.1.tgz",
+ "integrity": "sha512-daE62nS2ZQsDg9raM0IlZzLmI2u+7ZapXBwdoeBUKAYERPDDIc0qNqA8E0Rp2D+gspKR7BgIFP52GeujaGXWeQ==",
+ "dev": true,
+ "dependencies": {
+ "@types/minimatch": "^3.0.3",
+ "minimatch": "^3.0.2"
+ },
+ "engines": {
+ "node": "6.* || 8.* || >= 10.*"
+ }
+ },
+ "node_modules/material-icons": {
+ "version": "1.13.11",
+ "resolved": "https://registry.npmjs.org/material-icons/-/material-icons-1.13.11.tgz",
+ "integrity": "sha512-kp2oAdaqo/Zp6hpTZW01rOgDPWmxBUszSdDzkRm1idCjjNvdUMnqu8qu58cll6CObo+o0cydOiPLdoSugLm+mQ==",
+ "dev": true
+ },
+ "node_modules/mdurl": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/mdurl/-/mdurl-1.0.1.tgz",
+ "integrity": "sha512-/sKlQJCBYVY9Ers9hqzKou4H6V5UWc/M59TH2dvkt+84itfnq7uFOMLpOiOS4ujvHP4etln18fmIxA5R5fll0g==",
+ "dev": true
+ },
+ "node_modules/micromatch": {
+ "version": "3.1.10",
+ "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz",
+ "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==",
+ "dev": true,
+ "dependencies": {
+ "arr-diff": "^4.0.0",
+ "array-unique": "^0.3.2",
+ "braces": "^2.3.1",
+ "define-property": "^2.0.2",
+ "extend-shallow": "^3.0.2",
+ "extglob": "^2.0.4",
+ "fragment-cache": "^0.2.1",
+ "kind-of": "^6.0.2",
+ "nanomatch": "^1.2.9",
+ "object.pick": "^1.3.0",
+ "regex-not": "^1.0.0",
+ "snapdragon": "^0.8.1",
+ "to-regex": "^3.0.2"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/micromatch/node_modules/braces": {
+ "version": "2.3.2",
+ "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz",
+ "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==",
+ "dev": true,
+ "dependencies": {
+ "arr-flatten": "^1.1.0",
+ "array-unique": "^0.3.2",
+ "extend-shallow": "^2.0.1",
+ "fill-range": "^4.0.0",
+ "isobject": "^3.0.1",
+ "repeat-element": "^1.1.2",
+ "snapdragon": "^0.8.1",
+ "snapdragon-node": "^2.0.1",
+ "split-string": "^3.0.2",
+ "to-regex": "^3.0.1"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/micromatch/node_modules/braces/node_modules/extend-shallow": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
+ "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==",
+ "dev": true,
+ "dependencies": {
+ "is-extendable": "^0.1.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/micromatch/node_modules/fill-range": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz",
+ "integrity": "sha512-VcpLTWqWDiTerugjj8e3+esbg+skS3M9e54UuR3iCeIDMXCLTsAH8hTSzDQU/X6/6t3eYkOKoZSef2PlU6U1XQ==",
+ "dev": true,
+ "dependencies": {
+ "extend-shallow": "^2.0.1",
+ "is-number": "^3.0.0",
+ "repeat-string": "^1.6.1",
+ "to-regex-range": "^2.1.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/micromatch/node_modules/fill-range/node_modules/extend-shallow": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
+ "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==",
+ "dev": true,
+ "dependencies": {
+ "is-extendable": "^0.1.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/micromatch/node_modules/is-extendable": {
+ "version": "0.1.1",
+ "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz",
+ "integrity": "sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/micromatch/node_modules/is-number": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz",
+ "integrity": "sha512-4cboCqIpliH+mAvFNegjZQ4kgKc3ZUhQVr3HvWbSh5q3WH2v82ct+T2Y1hdU5Gdtorx/cLifQjqCbL7bpznLTg==",
+ "dev": true,
+ "dependencies": {
+ "kind-of": "^3.0.2"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/micromatch/node_modules/is-number/node_modules/kind-of": {
+ "version": "3.2.2",
+ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
+ "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==",
+ "dev": true,
+ "dependencies": {
+ "is-buffer": "^1.1.5"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/micromatch/node_modules/to-regex-range": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz",
+ "integrity": "sha512-ZZWNfCjUokXXDGXFpZehJIkZqq91BcULFq/Pi7M5i4JnxXdhMKAK682z8bCW3o8Hj1wuuzoKcW3DfVzaP6VuNg==",
+ "dev": true,
+ "dependencies": {
+ "is-number": "^3.0.0",
+ "repeat-string": "^1.6.1"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/mime": {
+ "version": "1.6.0",
+ "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz",
+ "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==",
+ "dev": true,
+ "bin": {
+ "mime": "cli.js"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/mime-db": {
+ "version": "1.52.0",
+ "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
+ "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
+ "dev": true,
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/mime-types": {
+ "version": "2.1.35",
+ "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
+ "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
+ "dev": true,
+ "dependencies": {
+ "mime-db": "1.52.0"
+ },
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/minimatch": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
+ "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
+ "dev": true,
+ "dependencies": {
+ "brace-expansion": "^1.1.7"
+ },
+ "engines": {
+ "node": "*"
+ }
+ },
+ "node_modules/mixin-deep": {
+ "version": "1.3.2",
+ "resolved": "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.3.2.tgz",
+ "integrity": "sha512-WRoDn//mXBiJ1H40rqa3vH0toePwSsGb45iInWlTySa+Uu4k3tYUSxa2v1KqAiLtvlrSzaExqS1gtk96A9zvEA==",
+ "dev": true,
+ "dependencies": {
+ "for-in": "^1.0.2",
+ "is-extendable": "^1.0.1"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/moment": {
+ "version": "2.29.4",
+ "resolved": "https://registry.npmjs.org/moment/-/moment-2.29.4.tgz",
+ "integrity": "sha512-5LC9SOxjSc2HF6vO2CyuTDNivEdoz2IvyJJGj6X8DJ0eFyfszE0QiEd+iXmBvUP3WHxSjFH/vIsA0EN00cgr8w==",
+ "dev": true,
+ "engines": {
+ "node": "*"
+ }
+ },
+ "node_modules/morgan": {
+ "version": "1.10.0",
+ "resolved": "https://registry.npmjs.org/morgan/-/morgan-1.10.0.tgz",
+ "integrity": "sha512-AbegBVI4sh6El+1gNwvD5YIck7nSA36weD7xvIxG4in80j/UoK8AEGaWnnz8v1GxonMCltmlNs5ZKbGvl9b1XQ==",
+ "dev": true,
+ "dependencies": {
+ "basic-auth": "~2.0.1",
+ "debug": "2.6.9",
+ "depd": "~2.0.0",
+ "on-finished": "~2.3.0",
+ "on-headers": "~1.0.2"
+ },
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
+ "node_modules/ms": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
+ "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==",
+ "dev": true
+ },
+ "node_modules/nan": {
+ "version": "2.16.0",
+ "resolved": "https://registry.npmjs.org/nan/-/nan-2.16.0.tgz",
+ "integrity": "sha512-UdAqHyFngu7TfQKsCBgAA6pWDkT8MAO7d0jyOecVhN5354xbLqdn8mV9Tat9gepAupm0bt2DbeaSC8vS52MuFA==",
+ "dev": true,
+ "optional": true
+ },
+ "node_modules/nanomatch": {
+ "version": "1.2.13",
+ "resolved": "https://registry.npmjs.org/nanomatch/-/nanomatch-1.2.13.tgz",
+ "integrity": "sha512-fpoe2T0RbHwBTBUOftAfBPaDEi06ufaUai0mE6Yn1kacc3SnTErfb/h+X94VXzI64rKFHYImXSvdwGGCmwOqCA==",
+ "dev": true,
+ "dependencies": {
+ "arr-diff": "^4.0.0",
+ "array-unique": "^0.3.2",
+ "define-property": "^2.0.2",
+ "extend-shallow": "^3.0.2",
+ "fragment-cache": "^0.2.1",
+ "is-windows": "^1.0.2",
+ "kind-of": "^6.0.2",
+ "object.pick": "^1.3.0",
+ "regex-not": "^1.0.0",
+ "snapdragon": "^0.8.1",
+ "to-regex": "^3.0.1"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/negotiator": {
+ "version": "0.6.3",
+ "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz",
+ "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==",
+ "dev": true,
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/nopt": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/nopt/-/nopt-5.0.0.tgz",
+ "integrity": "sha512-Tbj67rffqceeLpcRXrT7vKAN8CwfPeIBgM7E6iBkmKLV7bEMwpGgYLGv0jACUsECaa/vuxP0IjEont6umdMgtQ==",
+ "dev": true,
+ "dependencies": {
+ "abbrev": "1"
+ },
+ "bin": {
+ "nopt": "bin/nopt.js"
+ },
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/normalize-path": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz",
+ "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/normalize-url": {
+ "version": "1.9.1",
+ "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-1.9.1.tgz",
+ "integrity": "sha512-A48My/mtCklowHBlI8Fq2jFWK4tX4lJ5E6ytFsSOq1fzpvT0SQSgKhSg7lN5c2uYFOrUAOQp6zhhJnpp1eMloQ==",
+ "dev": true,
+ "dependencies": {
+ "object-assign": "^4.0.1",
+ "prepend-http": "^1.0.0",
+ "query-string": "^4.1.0",
+ "sort-keys": "^1.0.0"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/nth-check": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-1.0.2.tgz",
+ "integrity": "sha512-WeBOdju8SnzPN5vTUJYxYUxLeXpCaVP5i5e0LF8fg7WORF2Wd7wFX/pk0tYZk7s8T+J7VLy0Da6J1+wCT0AtHg==",
+ "dev": true,
+ "dependencies": {
+ "boolbase": "~1.0.0"
+ }
+ },
+ "node_modules/nunjucks": {
+ "version": "3.2.2",
+ "resolved": "https://registry.npmjs.org/nunjucks/-/nunjucks-3.2.2.tgz",
+ "integrity": "sha512-KUi85OoF2NMygwODAy28Lh9qHmq5hO3rBlbkYoC8v377h4l8Pt5qFjILl0LWpMbOrZ18CzfVVUvIHUIrtED3sA==",
+ "dev": true,
+ "dependencies": {
+ "a-sync-waterfall": "^1.0.0",
+ "asap": "^2.0.3",
+ "commander": "^5.1.0"
+ },
+ "bin": {
+ "nunjucks-precompile": "bin/precompile"
+ },
+ "engines": {
+ "node": ">= 6.9.0"
+ },
+ "optionalDependencies": {
+ "chokidar": "^3.3.0"
+ }
+ },
+ "node_modules/nunjucks/node_modules/commander": {
+ "version": "5.1.0",
+ "resolved": "https://registry.npmjs.org/commander/-/commander-5.1.0.tgz",
+ "integrity": "sha512-P0CysNDQ7rtVw4QIQtm+MRxV66vKFSvlsQvGYXZWR3qFU0jlMKHZZZgw8e+8DSah4UDKMqnknRDQz+xuQXQ/Zg==",
+ "dev": true,
+ "engines": {
+ "node": ">= 6"
+ }
+ },
+ "node_modules/object-assign": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
+ "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/object-copy": {
+ "version": "0.1.0",
+ "resolved": "https://registry.npmjs.org/object-copy/-/object-copy-0.1.0.tgz",
+ "integrity": "sha512-79LYn6VAb63zgtmAteVOWo9Vdj71ZVBy3Pbse+VqxDpEP83XuujMrGqHIwAXJ5I/aM0zU7dIyIAhifVTPrNItQ==",
+ "dev": true,
+ "dependencies": {
+ "copy-descriptor": "^0.1.0",
+ "define-property": "^0.2.5",
+ "kind-of": "^3.0.3"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/object-copy/node_modules/define-property": {
+ "version": "0.2.5",
+ "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz",
+ "integrity": "sha512-Rr7ADjQZenceVOAKop6ALkkRAmH1A4Gx9hV/7ZujPUN2rkATqFO0JZLZInbAjpZYoJ1gUx8MRMQVkYemcbMSTA==",
+ "dev": true,
+ "dependencies": {
+ "is-descriptor": "^0.1.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/object-copy/node_modules/is-accessor-descriptor": {
+ "version": "0.1.6",
+ "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz",
+ "integrity": "sha512-e1BM1qnDbMRG3ll2U9dSK0UMHuWOs3pY3AtcFsmvwPtKL3MML/Q86i+GilLfvqEs4GW+ExB91tQ3Ig9noDIZ+A==",
+ "dev": true,
+ "dependencies": {
+ "kind-of": "^3.0.2"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/object-copy/node_modules/is-data-descriptor": {
+ "version": "0.1.4",
+ "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz",
+ "integrity": "sha512-+w9D5ulSoBNlmw9OHn3U2v51SyoCd0he+bB3xMl62oijhrspxowjU+AIcDY0N3iEJbUEkB15IlMASQsxYigvXg==",
+ "dev": true,
+ "dependencies": {
+ "kind-of": "^3.0.2"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/object-copy/node_modules/is-descriptor": {
+ "version": "0.1.6",
+ "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz",
+ "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==",
+ "dev": true,
+ "dependencies": {
+ "is-accessor-descriptor": "^0.1.6",
+ "is-data-descriptor": "^0.1.4",
+ "kind-of": "^5.0.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/object-copy/node_modules/is-descriptor/node_modules/kind-of": {
+ "version": "5.1.0",
+ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz",
+ "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/object-copy/node_modules/kind-of": {
+ "version": "3.2.2",
+ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
+ "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==",
+ "dev": true,
+ "dependencies": {
+ "is-buffer": "^1.1.5"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/object-hash": {
+ "version": "1.3.1",
+ "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-1.3.1.tgz",
+ "integrity": "sha512-OSuu/pU4ENM9kmREg0BdNrUDIl1heYa4mBZacJc+vVWz4GtAwu7jO8s4AIt2aGRUTqxykpWzI3Oqnsm13tTMDA==",
+ "dev": true,
+ "engines": {
+ "node": ">= 0.10.0"
+ }
+ },
+ "node_modules/object-visit": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/object-visit/-/object-visit-1.0.1.tgz",
+ "integrity": "sha512-GBaMwwAVK9qbQN3Scdo0OyvgPW7l3lnaVMj84uTOZlswkX0KpF6fyDBJhtTthf7pymztoN36/KEr1DyhF96zEA==",
+ "dev": true,
+ "dependencies": {
+ "isobject": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/object.pick": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/object.pick/-/object.pick-1.3.0.tgz",
+ "integrity": "sha512-tqa/UMy/CCoYmj+H5qc07qvSL9dqcs/WZENZ1JbtWBlATP+iVOe778gE6MSijnyCnORzDuX6hU+LA4SZ09YjFQ==",
+ "dev": true,
+ "dependencies": {
+ "isobject": "^3.0.1"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/on-finished": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz",
+ "integrity": "sha512-ikqdkGAAyf/X/gPhXGvfgAytDZtDbr+bkNUJ0N9h5MI/dmdgCs3l6hoHrcUv41sRKew3jIwrp4qQDXiK99Utww==",
+ "dev": true,
+ "dependencies": {
+ "ee-first": "1.1.1"
+ },
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/on-headers": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz",
+ "integrity": "sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==",
+ "dev": true,
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/once": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
+ "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==",
+ "dev": true,
+ "dependencies": {
+ "wrappy": "1"
+ }
+ },
+ "node_modules/opn": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/opn/-/opn-6.0.0.tgz",
+ "integrity": "sha512-I9PKfIZC+e4RXZ/qr1RhgyCnGgYX0UEIlXgWnCOVACIvFgaC9rz6Won7xbdhoHrd8IIhV7YEpHjreNUNkqCGkQ==",
+ "deprecated": "The package has been renamed to `open`",
+ "dev": true,
+ "dependencies": {
+ "is-wsl": "^1.1.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/p-limit": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz",
+ "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==",
+ "dev": true,
+ "dependencies": {
+ "p-try": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/p-locate": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz",
+ "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==",
+ "dev": true,
+ "dependencies": {
+ "p-limit": "^2.2.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/p-try": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz",
+ "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/parseurl": {
+ "version": "1.3.3",
+ "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz",
+ "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==",
+ "dev": true,
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/pascalcase": {
+ "version": "0.1.1",
+ "resolved": "https://registry.npmjs.org/pascalcase/-/pascalcase-0.1.1.tgz",
+ "integrity": "sha512-XHXfu/yOQRy9vYOtUDVMN60OEJjW013GoObG1o+xwQTpB9eYJX/BjXMsdW13ZDPruFhYYn0AG22w0xgQMwl3Nw==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/path-dirname": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/path-dirname/-/path-dirname-1.0.2.tgz",
+ "integrity": "sha512-ALzNPpyNq9AqXMBjeymIjFDAkAFH06mHJH/cSBHAgU0s4vfpBn6b2nf8tiRLvagKD8RbTpq2FKTBg7cl9l3c7Q==",
+ "dev": true
+ },
+ "node_modules/path-exists": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz",
+ "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==",
+ "dev": true,
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/path-is-absolute": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
+ "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/path-is-inside": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/path-is-inside/-/path-is-inside-1.0.2.tgz",
+ "integrity": "sha512-DUWJr3+ULp4zXmol/SZkFf3JGsS9/SIv+Y3Rt93/UjPpDpklB5f1er4O3POIbUuUJ3FXgqte2Q7SrU6zAqwk8w==",
+ "dev": true
+ },
+ "node_modules/path-parse": {
+ "version": "1.0.7",
+ "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz",
+ "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==",
+ "dev": true
+ },
+ "node_modules/pause-stream": {
+ "version": "0.0.11",
+ "resolved": "https://registry.npmjs.org/pause-stream/-/pause-stream-0.0.11.tgz",
+ "integrity": "sha512-e3FBlXLmN/D1S+zHzanP4E/4Z60oFAa3O051qt1pxa7DEJWKAyil6upYVXCWadEnuoqa4Pkc9oUx9zsxYeRv8A==",
+ "dev": true,
+ "dependencies": {
+ "through": "~2.3"
+ }
+ },
+ "node_modules/picomatch": {
+ "version": "2.3.1",
+ "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz",
+ "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==",
+ "dev": true,
+ "engines": {
+ "node": ">=8.6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/jonschlinkert"
+ }
+ },
+ "node_modules/pify": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz",
+ "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/pinkie": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz",
+ "integrity": "sha512-MnUuEycAemtSaeFSjXKW/aroV7akBbY+Sv+RkyqFjgAe73F+MR0TBWKBRDkmfWq/HiFmdavfZ1G7h4SPZXaCSg==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/pinkie-promise": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz",
+ "integrity": "sha512-0Gni6D4UcLTbv9c57DfxDGdr41XfgUjqWZu492f0cIGr16zDU06BWP/RAEvOuo7CQ0CNjHaLlM59YJJFm3NWlw==",
+ "dev": true,
+ "dependencies": {
+ "pinkie": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/posix-character-classes": {
+ "version": "0.1.1",
+ "resolved": "https://registry.npmjs.org/posix-character-classes/-/posix-character-classes-0.1.1.tgz",
+ "integrity": "sha512-xTgYBc3fuo7Yt7JbiuFxSYGToMoz8fLoE6TC9Wx1P/u+LfeThMOAqmuyECnlBaaJb+u1m9hHiXUEtwW4OzfUJg==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/prepend-http": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/prepend-http/-/prepend-http-1.0.4.tgz",
+ "integrity": "sha512-PhmXi5XmoyKw1Un4E+opM2KcsJInDvKyuOumcjjw3waw86ZNjHwVUOOWLc4bCzLdcKNaWBH9e99sbWzDQsVaYg==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/process-nextick-args": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz",
+ "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==",
+ "dev": true
+ },
+ "node_modules/proto-list": {
+ "version": "1.2.4",
+ "resolved": "https://registry.npmjs.org/proto-list/-/proto-list-1.2.4.tgz",
+ "integrity": "sha512-vtK/94akxsTMhe0/cbfpR+syPuszcuwhqVjJq26CuNDgFGj682oRBXOP5MJpv2r7JtE8MsiepGIqvvOTBwn2vA==",
+ "dev": true
+ },
+ "node_modules/proxy-middleware": {
+ "version": "0.15.0",
+ "resolved": "https://registry.npmjs.org/proxy-middleware/-/proxy-middleware-0.15.0.tgz",
+ "integrity": "sha512-EGCG8SeoIRVMhsqHQUdDigB2i7qU7fCsWASwn54+nPutYO8n4q6EiwMzyfWlC+dzRFExP+kvcnDFdBDHoZBU7Q==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.8.0"
+ }
+ },
+ "node_modules/pseudomap": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz",
+ "integrity": "sha512-b/YwNhb8lk1Zz2+bXXpS/LK9OisiZZ1SNsSLxN1x2OXVEhW2Ckr/7mWE5vrC1ZTiJlD9g19jWszTmJsB+oEpFQ==",
+ "dev": true
+ },
+ "node_modules/query-string": {
+ "version": "4.3.4",
+ "resolved": "https://registry.npmjs.org/query-string/-/query-string-4.3.4.tgz",
+ "integrity": "sha512-O2XLNDBIg1DnTOa+2XrIwSiXEV8h2KImXUnjhhn2+UsvZ+Es2uyd5CCRTNQlDGbzUQOW3aYCBx9rVA6dzsiY7Q==",
+ "dev": true,
+ "dependencies": {
+ "object-assign": "^4.1.0",
+ "strict-uri-encode": "^1.0.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/querystringify": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz",
+ "integrity": "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==",
+ "dev": true
+ },
+ "node_modules/randombytes": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz",
+ "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==",
+ "dev": true,
+ "dependencies": {
+ "safe-buffer": "^5.1.0"
+ }
+ },
+ "node_modules/range-parser": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz",
+ "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==",
+ "dev": true,
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/readable-stream": {
+ "version": "3.6.0",
+ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz",
+ "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==",
+ "dev": true,
+ "dependencies": {
+ "inherits": "^2.0.3",
+ "string_decoder": "^1.1.1",
+ "util-deprecate": "^1.0.1"
+ },
+ "engines": {
+ "node": ">= 6"
+ }
+ },
+ "node_modules/readdirp": {
+ "version": "3.6.0",
+ "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz",
+ "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==",
+ "dev": true,
+ "dependencies": {
+ "picomatch": "^2.2.1"
+ },
+ "engines": {
+ "node": ">=8.10.0"
+ }
+ },
+ "node_modules/regex-not": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/regex-not/-/regex-not-1.0.2.tgz",
+ "integrity": "sha512-J6SDjUgDxQj5NusnOtdFxDwN/+HWykR8GELwctJ7mdqhcyy1xEc4SRFHUXvxTp661YaVKAjfRLZ9cCqS6tn32A==",
+ "dev": true,
+ "dependencies": {
+ "extend-shallow": "^3.0.2",
+ "safe-regex": "^1.1.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/remove-trailing-separator": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz",
+ "integrity": "sha512-/hS+Y0u3aOfIETiaiirUFwDBDzmXPvO+jAfKTitUngIPzdKc6Z0LoFjM/CK5PL4C+eKwHohlHAb6H0VFfmmUsw==",
+ "dev": true
+ },
+ "node_modules/repeat-element": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/repeat-element/-/repeat-element-1.1.4.tgz",
+ "integrity": "sha512-LFiNfRcSu7KK3evMyYOuCzv3L10TW7yC1G2/+StMjK8Y6Vqd2MG7r/Qjw4ghtuCOjFvlnms/iMmLqpvW/ES/WQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/repeat-string": {
+ "version": "1.6.1",
+ "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz",
+ "integrity": "sha512-PV0dzCYDNfRi1jCDbJzpW7jNNDRuCOG/jI5ctQcGKt/clZD+YcPS3yIlWuTJMmESC8aevCFmWJy5wjAFgNqN6w==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10"
+ }
+ },
+ "node_modules/requires-port": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz",
+ "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==",
+ "dev": true
+ },
+ "node_modules/resolve": {
+ "version": "1.22.4",
+ "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.4.tgz",
+ "integrity": "sha512-PXNdCiPqDqeUou+w1C2eTQbNfxKSuMxqTCuvlmmMsk1NWHL5fRrhY6Pl0qEYYc6+QqGClco1Qj8XnjPego4wfg==",
+ "dev": true,
+ "dependencies": {
+ "is-core-module": "^2.13.0",
+ "path-parse": "^1.0.7",
+ "supports-preserve-symlinks-flag": "^1.0.0"
+ },
+ "bin": {
+ "resolve": "bin/resolve"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/resolve-url": {
+ "version": "0.2.1",
+ "resolved": "https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz",
+ "integrity": "sha512-ZuF55hVUQaaczgOIwqWzkEcEidmlD/xl44x1UZnhOXcYuFN2S6+rcxpG+C1N3So0wvNI3DmJICUFfu2SxhBmvg==",
+ "deprecated": "https://github.com/lydell/resolve-url#deprecated",
+ "dev": true
+ },
+ "node_modules/ret": {
+ "version": "0.1.15",
+ "resolved": "https://registry.npmjs.org/ret/-/ret-0.1.15.tgz",
+ "integrity": "sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.12"
+ }
+ },
+ "node_modules/safe-buffer": {
+ "version": "5.1.2",
+ "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
+ "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==",
+ "dev": true
+ },
+ "node_modules/safe-regex": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/safe-regex/-/safe-regex-1.1.0.tgz",
+ "integrity": "sha512-aJXcif4xnaNUzvUuC5gcb46oTS7zvg4jpMTnuqtrEPlR3vFr4pxtdTwaF1Qs3Enjn9HK+ZlwQui+a7z0SywIzg==",
+ "dev": true,
+ "dependencies": {
+ "ret": "~0.1.10"
+ }
+ },
+ "node_modules/safe-stable-stringify": {
+ "version": "2.3.1",
+ "resolved": "https://registry.npmjs.org/safe-stable-stringify/-/safe-stable-stringify-2.3.1.tgz",
+ "integrity": "sha512-kYBSfT+troD9cDA85VDnHZ1rpHC50O0g1e6WlGHVCz/g+JS+9WKLj+XwFYyR8UbrZN8ll9HUpDAAddY58MGisg==",
+ "dev": true,
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/semver": {
+ "version": "5.7.2",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz",
+ "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==",
+ "dev": true,
+ "bin": {
+ "semver": "bin/semver"
+ }
+ },
+ "node_modules/send": {
+ "version": "0.18.0",
+ "resolved": "https://registry.npmjs.org/send/-/send-0.18.0.tgz",
+ "integrity": "sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==",
+ "dev": true,
+ "dependencies": {
+ "debug": "2.6.9",
+ "depd": "2.0.0",
+ "destroy": "1.2.0",
+ "encodeurl": "~1.0.2",
+ "escape-html": "~1.0.3",
+ "etag": "~1.8.1",
+ "fresh": "0.5.2",
+ "http-errors": "2.0.0",
+ "mime": "1.6.0",
+ "ms": "2.1.3",
+ "on-finished": "2.4.1",
+ "range-parser": "~1.2.1",
+ "statuses": "2.0.1"
+ },
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
+ "node_modules/send/node_modules/ms": {
+ "version": "2.1.3",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
+ "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
+ "dev": true
+ },
+ "node_modules/send/node_modules/on-finished": {
+ "version": "2.4.1",
+ "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz",
+ "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==",
+ "dev": true,
+ "dependencies": {
+ "ee-first": "1.1.1"
+ },
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/send/node_modules/statuses": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz",
+ "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==",
+ "dev": true,
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/serialize-javascript": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-3.1.0.tgz",
+ "integrity": "sha512-JIJT1DGiWmIKhzRsG91aS6Ze4sFUrYbltlkg2onR5OrnNM02Kl/hnY/T4FN2omvyeBbQmMJv+K4cPOpGzOTFBg==",
+ "dev": true,
+ "dependencies": {
+ "randombytes": "^2.1.0"
+ }
+ },
+ "node_modules/serve-index": {
+ "version": "1.9.1",
+ "resolved": "https://registry.npmjs.org/serve-index/-/serve-index-1.9.1.tgz",
+ "integrity": "sha512-pXHfKNP4qujrtteMrSBb0rc8HJ9Ms/GrXwcUtUtD5s4ewDJI8bT3Cz2zTVRMKtri49pLx2e0Ya8ziP5Ya2pZZw==",
+ "dev": true,
+ "dependencies": {
+ "accepts": "~1.3.4",
+ "batch": "0.6.1",
+ "debug": "2.6.9",
+ "escape-html": "~1.0.3",
+ "http-errors": "~1.6.2",
+ "mime-types": "~2.1.17",
+ "parseurl": "~1.3.2"
+ },
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
+ "node_modules/serve-index/node_modules/depd": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz",
+ "integrity": "sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ==",
+ "dev": true,
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/serve-index/node_modules/http-errors": {
+ "version": "1.6.3",
+ "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz",
+ "integrity": "sha512-lks+lVC8dgGyh97jxvxeYTWQFvh4uw4yC12gVl63Cg30sjPX4wuGcdkICVXDAESr6OJGjqGA8Iz5mkeN6zlD7A==",
+ "dev": true,
+ "dependencies": {
+ "depd": "~1.1.2",
+ "inherits": "2.0.3",
+ "setprototypeof": "1.1.0",
+ "statuses": ">= 1.4.0 < 2"
+ },
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/serve-index/node_modules/inherits": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz",
+ "integrity": "sha512-x00IRNXNy63jwGkJmzPigoySHbaqpNuzKbBOmzK+g2OdZpQ9w+sxCN+VSB3ja7IAge2OP2qpfxTjeNcyjmW1uw==",
+ "dev": true
+ },
+ "node_modules/serve-index/node_modules/setprototypeof": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz",
+ "integrity": "sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ==",
+ "dev": true
+ },
+ "node_modules/set-value": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/set-value/-/set-value-2.0.1.tgz",
+ "integrity": "sha512-JxHc1weCN68wRY0fhCoXpyK55m/XPHafOmK4UWD7m2CI14GMcFypt4w/0+NV5f/ZMby2F6S2wwA7fgynh9gWSw==",
+ "dev": true,
+ "dependencies": {
+ "extend-shallow": "^2.0.1",
+ "is-extendable": "^0.1.1",
+ "is-plain-object": "^2.0.3",
+ "split-string": "^3.0.1"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/set-value/node_modules/extend-shallow": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
+ "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==",
+ "dev": true,
+ "dependencies": {
+ "is-extendable": "^0.1.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/set-value/node_modules/is-extendable": {
+ "version": "0.1.1",
+ "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz",
+ "integrity": "sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/setprototypeof": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz",
+ "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==",
+ "dev": true
+ },
+ "node_modules/sigmund": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/sigmund/-/sigmund-1.0.1.tgz",
+ "integrity": "sha512-fCvEXfh6NWpm+YSuY2bpXb/VIihqWA6hLsgboC+0nl71Q7N7o2eaCW8mJa/NLvQhs6jpd3VZV4UiUQlV6+lc8g==",
+ "dev": true
+ },
+ "node_modules/simple-git": {
+ "version": "2.48.0",
+ "resolved": "https://registry.npmjs.org/simple-git/-/simple-git-2.48.0.tgz",
+ "integrity": "sha512-z4qtrRuaAFJS4PUd0g+xy7aN4y+RvEt/QTJpR184lhJguBA1S/LsVlvE/CM95RsYMOFJG3NGGDjqFCzKU19S/A==",
+ "dev": true,
+ "dependencies": {
+ "@kwsites/file-exists": "^1.1.1",
+ "@kwsites/promise-deferred": "^1.1.1",
+ "debug": "^4.3.2"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/steveukx/"
+ }
+ },
+ "node_modules/simple-git/node_modules/debug": {
+ "version": "4.3.4",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz",
+ "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==",
+ "dev": true,
+ "dependencies": {
+ "ms": "2.1.2"
+ },
+ "engines": {
+ "node": ">=6.0"
+ },
+ "peerDependenciesMeta": {
+ "supports-color": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/simple-git/node_modules/ms": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
+ "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==",
+ "dev": true
+ },
+ "node_modules/snapdragon": {
+ "version": "0.8.2",
+ "resolved": "https://registry.npmjs.org/snapdragon/-/snapdragon-0.8.2.tgz",
+ "integrity": "sha512-FtyOnWN/wCHTVXOMwvSv26d+ko5vWlIDD6zoUJ7LW8vh+ZBC8QdljveRP+crNrtBwioEUWy/4dMtbBjA4ioNlg==",
+ "dev": true,
+ "dependencies": {
+ "base": "^0.11.1",
+ "debug": "^2.2.0",
+ "define-property": "^0.2.5",
+ "extend-shallow": "^2.0.1",
+ "map-cache": "^0.2.2",
+ "source-map": "^0.5.6",
+ "source-map-resolve": "^0.5.0",
+ "use": "^3.1.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/snapdragon-node": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/snapdragon-node/-/snapdragon-node-2.1.1.tgz",
+ "integrity": "sha512-O27l4xaMYt/RSQ5TR3vpWCAB5Kb/czIcqUFOM/C4fYcLnbZUc1PkjTAMjof2pBWaSTwOUd6qUHcFGVGj7aIwnw==",
+ "dev": true,
+ "dependencies": {
+ "define-property": "^1.0.0",
+ "isobject": "^3.0.0",
+ "snapdragon-util": "^3.0.1"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/snapdragon-node/node_modules/define-property": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz",
+ "integrity": "sha512-cZTYKFWspt9jZsMscWo8sc/5lbPC9Q0N5nBLgb+Yd915iL3udB1uFgS3B8YCx66UVHq018DAVFoee7x+gxggeA==",
+ "dev": true,
+ "dependencies": {
+ "is-descriptor": "^1.0.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/snapdragon-util": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/snapdragon-util/-/snapdragon-util-3.0.1.tgz",
+ "integrity": "sha512-mbKkMdQKsjX4BAL4bRYTj21edOf8cN7XHdYUJEe+Zn99hVEYcMvKPct1IqNe7+AZPirn8BCDOQBHQZknqmKlZQ==",
+ "dev": true,
+ "dependencies": {
+ "kind-of": "^3.2.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/snapdragon-util/node_modules/kind-of": {
+ "version": "3.2.2",
+ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
+ "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==",
+ "dev": true,
+ "dependencies": {
+ "is-buffer": "^1.1.5"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/snapdragon/node_modules/define-property": {
+ "version": "0.2.5",
+ "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz",
+ "integrity": "sha512-Rr7ADjQZenceVOAKop6ALkkRAmH1A4Gx9hV/7ZujPUN2rkATqFO0JZLZInbAjpZYoJ1gUx8MRMQVkYemcbMSTA==",
+ "dev": true,
+ "dependencies": {
+ "is-descriptor": "^0.1.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/snapdragon/node_modules/extend-shallow": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
+ "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==",
+ "dev": true,
+ "dependencies": {
+ "is-extendable": "^0.1.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/snapdragon/node_modules/is-accessor-descriptor": {
+ "version": "0.1.6",
+ "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz",
+ "integrity": "sha512-e1BM1qnDbMRG3ll2U9dSK0UMHuWOs3pY3AtcFsmvwPtKL3MML/Q86i+GilLfvqEs4GW+ExB91tQ3Ig9noDIZ+A==",
+ "dev": true,
+ "dependencies": {
+ "kind-of": "^3.0.2"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/snapdragon/node_modules/is-accessor-descriptor/node_modules/kind-of": {
+ "version": "3.2.2",
+ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
+ "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==",
+ "dev": true,
+ "dependencies": {
+ "is-buffer": "^1.1.5"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/snapdragon/node_modules/is-data-descriptor": {
+ "version": "0.1.4",
+ "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz",
+ "integrity": "sha512-+w9D5ulSoBNlmw9OHn3U2v51SyoCd0he+bB3xMl62oijhrspxowjU+AIcDY0N3iEJbUEkB15IlMASQsxYigvXg==",
+ "dev": true,
+ "dependencies": {
+ "kind-of": "^3.0.2"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/snapdragon/node_modules/is-data-descriptor/node_modules/kind-of": {
+ "version": "3.2.2",
+ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
+ "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==",
+ "dev": true,
+ "dependencies": {
+ "is-buffer": "^1.1.5"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/snapdragon/node_modules/is-descriptor": {
+ "version": "0.1.6",
+ "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz",
+ "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==",
+ "dev": true,
+ "dependencies": {
+ "is-accessor-descriptor": "^0.1.6",
+ "is-data-descriptor": "^0.1.4",
+ "kind-of": "^5.0.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/snapdragon/node_modules/is-extendable": {
+ "version": "0.1.1",
+ "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz",
+ "integrity": "sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/snapdragon/node_modules/kind-of": {
+ "version": "5.1.0",
+ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz",
+ "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/sort-keys": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/sort-keys/-/sort-keys-1.1.2.tgz",
+ "integrity": "sha512-vzn8aSqKgytVik0iwdBEi+zevbTYZogewTUM6dtpmGwEcdzbub/TX4bCzRhebDCRC3QzXgJsLRKB2V/Oof7HXg==",
+ "dev": true,
+ "dependencies": {
+ "is-plain-obj": "^1.0.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/source-map": {
+ "version": "0.5.6",
+ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.6.tgz",
+ "integrity": "sha512-MjZkVp0NHr5+TPihLcadqnlVoGIoWo4IBHptutGh9wI3ttUYvCG26HkSuDi+K6lsZ25syXJXcctwgyVCt//xqA==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/source-map-resolve": {
+ "version": "0.5.3",
+ "resolved": "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.5.3.tgz",
+ "integrity": "sha512-Htz+RnsXWk5+P2slx5Jh3Q66vhQj1Cllm0zvnaY98+NFx+Dv2CF/f5O/t8x+KaNdrdIAsruNzoh/KpialbqAnw==",
+ "deprecated": "See https://github.com/lydell/source-map-resolve#deprecated",
+ "dev": true,
+ "dependencies": {
+ "atob": "^2.1.2",
+ "decode-uri-component": "^0.2.0",
+ "resolve-url": "^0.2.1",
+ "source-map-url": "^0.4.0",
+ "urix": "^0.1.0"
+ }
+ },
+ "node_modules/source-map-url": {
+ "version": "0.4.1",
+ "resolved": "https://registry.npmjs.org/source-map-url/-/source-map-url-0.4.1.tgz",
+ "integrity": "sha512-cPiFOTLUKvJFIg4SKVScy4ilPPW6rFgMgfuZJPNoDuMs3nC1HbMUycBoJw77xFIp6z1UJQJOfx6C9GMH80DiTw==",
+ "deprecated": "See https://github.com/lydell/source-map-url#deprecated",
+ "dev": true
+ },
+ "node_modules/split": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/split/-/split-1.0.1.tgz",
+ "integrity": "sha512-mTyOoPbrivtXnwnIxZRFYRrPNtEFKlpB2fvjSnCQUiAA6qAZzqwna5envK4uk6OIeP17CsdF3rSBGYVBsU0Tkg==",
+ "dev": true,
+ "dependencies": {
+ "through": "2"
+ },
+ "engines": {
+ "node": "*"
+ }
+ },
+ "node_modules/split-string": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/split-string/-/split-string-3.1.0.tgz",
+ "integrity": "sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw==",
+ "dev": true,
+ "dependencies": {
+ "extend-shallow": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/sprintf-js": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz",
+ "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==",
+ "dev": true
+ },
+ "node_modules/stack-trace": {
+ "version": "0.0.10",
+ "resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.10.tgz",
+ "integrity": "sha512-KGzahc7puUKkzyMt+IqAep+TVNbKP+k2Lmwhub39m1AsTSkaDutx56aDCo+HLDzf/D26BIHTJWNiTG1KAJiQCg==",
+ "dev": true,
+ "engines": {
+ "node": "*"
+ }
+ },
+ "node_modules/static-extend": {
+ "version": "0.1.2",
+ "resolved": "https://registry.npmjs.org/static-extend/-/static-extend-0.1.2.tgz",
+ "integrity": "sha512-72E9+uLc27Mt718pMHt9VMNiAL4LMsmDbBva8mxWUCkT07fSzEGMYUCk0XWY6lp0j6RBAG4cJ3mWuZv2OE3s0g==",
+ "dev": true,
+ "dependencies": {
+ "define-property": "^0.2.5",
+ "object-copy": "^0.1.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/static-extend/node_modules/define-property": {
+ "version": "0.2.5",
+ "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz",
+ "integrity": "sha512-Rr7ADjQZenceVOAKop6ALkkRAmH1A4Gx9hV/7ZujPUN2rkATqFO0JZLZInbAjpZYoJ1gUx8MRMQVkYemcbMSTA==",
+ "dev": true,
+ "dependencies": {
+ "is-descriptor": "^0.1.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/static-extend/node_modules/is-accessor-descriptor": {
+ "version": "0.1.6",
+ "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz",
+ "integrity": "sha512-e1BM1qnDbMRG3ll2U9dSK0UMHuWOs3pY3AtcFsmvwPtKL3MML/Q86i+GilLfvqEs4GW+ExB91tQ3Ig9noDIZ+A==",
+ "dev": true,
+ "dependencies": {
+ "kind-of": "^3.0.2"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/static-extend/node_modules/is-accessor-descriptor/node_modules/kind-of": {
+ "version": "3.2.2",
+ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
+ "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==",
+ "dev": true,
+ "dependencies": {
+ "is-buffer": "^1.1.5"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/static-extend/node_modules/is-data-descriptor": {
+ "version": "0.1.4",
+ "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz",
+ "integrity": "sha512-+w9D5ulSoBNlmw9OHn3U2v51SyoCd0he+bB3xMl62oijhrspxowjU+AIcDY0N3iEJbUEkB15IlMASQsxYigvXg==",
+ "dev": true,
+ "dependencies": {
+ "kind-of": "^3.0.2"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/static-extend/node_modules/is-data-descriptor/node_modules/kind-of": {
+ "version": "3.2.2",
+ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
+ "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==",
+ "dev": true,
+ "dependencies": {
+ "is-buffer": "^1.1.5"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/static-extend/node_modules/is-descriptor": {
+ "version": "0.1.6",
+ "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz",
+ "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==",
+ "dev": true,
+ "dependencies": {
+ "is-accessor-descriptor": "^0.1.6",
+ "is-data-descriptor": "^0.1.4",
+ "kind-of": "^5.0.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/static-extend/node_modules/kind-of": {
+ "version": "5.1.0",
+ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz",
+ "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/statuses": {
+ "version": "1.5.0",
+ "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz",
+ "integrity": "sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA==",
+ "dev": true,
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/stream-combiner": {
+ "version": "0.2.2",
+ "resolved": "https://registry.npmjs.org/stream-combiner/-/stream-combiner-0.2.2.tgz",
+ "integrity": "sha512-6yHMqgLYDzQDcAkL+tjJDC5nSNuNIx0vZtRZeiPh7Saef7VHX9H5Ijn9l2VIol2zaNYlYEX6KyuT/237A58qEQ==",
+ "dev": true,
+ "dependencies": {
+ "duplexer": "~0.1.1",
+ "through": "~2.3.4"
+ }
+ },
+ "node_modules/strict-uri-encode": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/strict-uri-encode/-/strict-uri-encode-1.1.0.tgz",
+ "integrity": "sha512-R3f198pcvnB+5IpnBlRkphuE9n46WyVl8I39W/ZUTZLz4nqSP/oLYUrcnJrw462Ds8he4YKMov2efsTIw1BDGQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/string_decoder": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz",
+ "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==",
+ "dev": true,
+ "dependencies": {
+ "safe-buffer": "~5.2.0"
+ }
+ },
+ "node_modules/string_decoder/node_modules/safe-buffer": {
+ "version": "5.2.1",
+ "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
+ "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/feross"
+ },
+ {
+ "type": "patreon",
+ "url": "https://www.patreon.com/feross"
+ },
+ {
+ "type": "consulting",
+ "url": "https://feross.org/support"
+ }
+ ]
+ },
+ "node_modules/strip-ansi": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
+ "integrity": "sha512-VhumSSbBqDTP8p2ZLKj40UjBCV4+v8bUSEpUb4KjRgWk9pbqGF4REFj6KEagidb2f/M6AzC0EmFyDNGaw9OCzg==",
+ "dev": true,
+ "dependencies": {
+ "ansi-regex": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/strip-outer": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/strip-outer/-/strip-outer-1.0.1.tgz",
+ "integrity": "sha512-k55yxKHwaXnpYGsOzg4Vl8+tDrWylxDEpknGjhTiZB8dFRU5rTo9CAzeycivxV3s+zlTKwrs6WxMxR95n26kwg==",
+ "dev": true,
+ "dependencies": {
+ "escape-string-regexp": "^1.0.2"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/strip-url-auth": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/strip-url-auth/-/strip-url-auth-1.0.1.tgz",
+ "integrity": "sha512-++41PnXftlL3pvI6lpvhSEO+89g1kIJC4MYB5E6yH+WHa5InIqz51yGd1YOGd7VNSNdoEOfzTMqbAM/2PbgaHQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/supports-color": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
+ "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
+ "dev": true,
+ "dependencies": {
+ "has-flag": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/supports-preserve-symlinks-flag": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz",
+ "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==",
+ "dev": true,
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/through": {
+ "version": "2.3.8",
+ "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz",
+ "integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==",
+ "dev": true
+ },
+ "node_modules/through2": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/through2/-/through2-3.0.2.tgz",
+ "integrity": "sha512-enaDQ4MUyP2W6ZyT6EsMzqBPZaM/avg8iuo+l2d3QCs0J+6RaqkHV/2/lOwDTueBHeJ/2LG9lrLW3d5rWPucuQ==",
+ "dev": true,
+ "dependencies": {
+ "inherits": "^2.0.4",
+ "readable-stream": "2 || 3"
+ }
+ },
+ "node_modules/to-object-path": {
+ "version": "0.3.0",
+ "resolved": "https://registry.npmjs.org/to-object-path/-/to-object-path-0.3.0.tgz",
+ "integrity": "sha512-9mWHdnGRuh3onocaHzukyvCZhzvr6tiflAy/JRFXcJX0TjgfWA9pk9t8CMbzmBE4Jfw58pXbkngtBtqYxzNEyg==",
+ "dev": true,
+ "dependencies": {
+ "kind-of": "^3.0.2"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/to-object-path/node_modules/kind-of": {
+ "version": "3.2.2",
+ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
+ "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==",
+ "dev": true,
+ "dependencies": {
+ "is-buffer": "^1.1.5"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/to-regex": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/to-regex/-/to-regex-3.0.2.tgz",
+ "integrity": "sha512-FWtleNAtZ/Ki2qtqej2CXTOayOH9bHDQF+Q48VpWyDXjbYxA4Yz8iDB31zXOBUlOHHKidDbqGVrTUvQMPmBGBw==",
+ "dev": true,
+ "dependencies": {
+ "define-property": "^2.0.2",
+ "extend-shallow": "^3.0.2",
+ "regex-not": "^1.0.2",
+ "safe-regex": "^1.1.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/to-regex-range": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
+ "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
+ "dev": true,
+ "dependencies": {
+ "is-number": "^7.0.0"
+ },
+ "engines": {
+ "node": ">=8.0"
+ }
+ },
+ "node_modules/toidentifier": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz",
+ "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.6"
+ }
+ },
+ "node_modules/trim-repeated": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/trim-repeated/-/trim-repeated-1.0.0.tgz",
+ "integrity": "sha512-pkonvlKk8/ZuR0D5tLW8ljt5I8kmxp2XKymhepUeOdCEfKpZaktSArkLHZt76OB1ZvO9bssUsDty4SWhLvZpLg==",
+ "dev": true,
+ "dependencies": {
+ "escape-string-regexp": "^1.0.2"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/triple-beam": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/triple-beam/-/triple-beam-1.3.0.tgz",
+ "integrity": "sha512-XrHUvV5HpdLmIj4uVMxHggLbFSZYIn7HEWsqePZcI50pco+MPqJ50wMGY794X7AOOhxOBAjbkqfAbEe/QMp2Lw==",
+ "dev": true
+ },
+ "node_modules/uc.micro": {
+ "version": "1.0.6",
+ "resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-1.0.6.tgz",
+ "integrity": "sha512-8Y75pvTYkLJW2hWQHXxoqRgV7qb9B+9vFEtidML+7koHUFapnVJAZ6cKs+Qjz5Aw3aZWHMC6u0wJE3At+nSGwA==",
+ "dev": true
+ },
+ "node_modules/union-value": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/union-value/-/union-value-1.0.1.tgz",
+ "integrity": "sha512-tJfXmxMeWYnczCVs7XAEvIV7ieppALdyepWMkHkwciRpZraG/xwT+s2JN8+pr1+8jCRf80FFzvr+MpQeeoF4Xg==",
+ "dev": true,
+ "dependencies": {
+ "arr-union": "^3.1.0",
+ "get-value": "^2.0.6",
+ "is-extendable": "^0.1.1",
+ "set-value": "^2.0.1"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/union-value/node_modules/is-extendable": {
+ "version": "0.1.1",
+ "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz",
+ "integrity": "sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/universalify": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz",
+ "integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==",
+ "dev": true,
+ "engines": {
+ "node": ">= 10.0.0"
+ }
+ },
+ "node_modules/unix-crypt-td-js": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/unix-crypt-td-js/-/unix-crypt-td-js-1.1.4.tgz",
+ "integrity": "sha512-8rMeVYWSIyccIJscb9NdCfZKSRBKYTeVnwmiRYT2ulE3qd1RaDQ0xQDP+rI3ccIWbhu/zuo5cgN8z73belNZgw==",
+ "dev": true
+ },
+ "node_modules/unpipe": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz",
+ "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==",
+ "dev": true,
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/unset-value": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/unset-value/-/unset-value-1.0.0.tgz",
+ "integrity": "sha512-PcA2tsuGSF9cnySLHTLSh2qrQiJ70mn+r+Glzxv2TWZblxsxCC52BDlZoPCsz7STd9pN7EZetkWZBAvk4cgZdQ==",
+ "dev": true,
+ "dependencies": {
+ "has-value": "^0.3.1",
+ "isobject": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/unset-value/node_modules/has-value": {
+ "version": "0.3.1",
+ "resolved": "https://registry.npmjs.org/has-value/-/has-value-0.3.1.tgz",
+ "integrity": "sha512-gpG936j8/MzaeID5Yif+577c17TxaDmhuyVgSwtnL/q8UUTySg8Mecb+8Cf1otgLoD7DDH75axp86ER7LFsf3Q==",
+ "dev": true,
+ "dependencies": {
+ "get-value": "^2.0.3",
+ "has-values": "^0.1.4",
+ "isobject": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/unset-value/node_modules/has-value/node_modules/isobject": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz",
+ "integrity": "sha512-+OUdGJlgjOBZDfxnDjYYG6zp487z0JGNQq3cYQYg5f5hKR+syHMsaztzGeml/4kGG55CSpKSpWTY+jYGgsHLgA==",
+ "dev": true,
+ "dependencies": {
+ "isarray": "1.0.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/unset-value/node_modules/has-values": {
+ "version": "0.1.4",
+ "resolved": "https://registry.npmjs.org/has-values/-/has-values-0.1.4.tgz",
+ "integrity": "sha512-J8S0cEdWuQbqD9//tlZxiMuMNmxB8PlEwvYwuxsTmR1G5RXUePEX/SJn7aD0GMLieuZYSwNH0cQuJGwnYunXRQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/upath": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/upath/-/upath-1.2.0.tgz",
+ "integrity": "sha512-aZwGpamFO61g3OlfT7OQCHqhGnW43ieH9WZeP7QxN/G/jS4jfqUkZxoryvJgVPEcrl5NL/ggHsSmLMHuH64Lhg==",
+ "dev": true,
+ "engines": {
+ "node": ">=4",
+ "yarn": "*"
+ }
+ },
+ "node_modules/urix": {
+ "version": "0.1.0",
+ "resolved": "https://registry.npmjs.org/urix/-/urix-0.1.0.tgz",
+ "integrity": "sha512-Am1ousAhSLBeB9cG/7k7r2R0zj50uDRlZHPGbazid5s9rlF1F/QKYObEKSIunSjIOkJZqwRRLpvewjEkM7pSqg==",
+ "deprecated": "Please see https://github.com/lydell/urix#deprecated",
+ "dev": true
+ },
+ "node_modules/url-parse": {
+ "version": "1.5.10",
+ "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.10.tgz",
+ "integrity": "sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==",
+ "dev": true,
+ "dependencies": {
+ "querystringify": "^2.1.1",
+ "requires-port": "^1.0.0"
+ }
+ },
+ "node_modules/use": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/use/-/use-3.1.1.tgz",
+ "integrity": "sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/util-deprecate": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
+ "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==",
+ "dev": true
+ },
+ "node_modules/utils-merge": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz",
+ "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==",
+ "dev": true,
+ "engines": {
+ "node": ">= 0.4.0"
+ }
+ },
+ "node_modules/uuid": {
+ "version": "8.3.2",
+ "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz",
+ "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==",
+ "dev": true,
+ "bin": {
+ "uuid": "dist/bin/uuid"
+ }
+ },
+ "node_modules/vary": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
+ "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==",
+ "dev": true,
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/vue": {
+ "version": "2.6.14",
+ "resolved": "https://registry.npmjs.org/vue/-/vue-2.6.14.tgz",
+ "integrity": "sha512-x2284lgYvjOMj3Za7kqzRcUSxBboHqtgRE2zlos1qWaOye5yUmHn42LB1250NJBLRwEcdrB0JRwyPTEPhfQjiQ==",
+ "dev": true
+ },
+ "node_modules/vue-server-renderer": {
+ "version": "2.6.14",
+ "resolved": "https://registry.npmjs.org/vue-server-renderer/-/vue-server-renderer-2.6.14.tgz",
+ "integrity": "sha512-HifYRa/LW7cKywg9gd4ZtvtRuBlstQBao5ZCWlg40fyB4OPoGfEXAzxb0emSLv4pBDOHYx0UjpqvxpiQFEuoLA==",
+ "dev": true,
+ "dependencies": {
+ "chalk": "^1.1.3",
+ "hash-sum": "^1.0.2",
+ "he": "^1.1.0",
+ "lodash.template": "^4.5.0",
+ "lodash.uniq": "^4.5.0",
+ "resolve": "^1.2.0",
+ "serialize-javascript": "^3.1.0",
+ "source-map": "0.5.6"
+ }
+ },
+ "node_modules/vue-server-renderer/node_modules/ansi-styles": {
+ "version": "2.2.1",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz",
+ "integrity": "sha512-kmCevFghRiWM7HB5zTPULl4r9bVFSWjz62MhqizDGUrq2NWuNMQyuv4tHHoKJHs69M/MF64lEcHdYIocrdWQYA==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/vue-server-renderer/node_modules/chalk": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz",
+ "integrity": "sha512-U3lRVLMSlsCfjqYPbLyVv11M9CPW4I728d6TCKMAOJueEeB9/8o+eSsMnxPJD+Q+K909sdESg7C+tIkoH6on1A==",
+ "dev": true,
+ "dependencies": {
+ "ansi-styles": "^2.2.1",
+ "escape-string-regexp": "^1.0.2",
+ "has-ansi": "^2.0.0",
+ "strip-ansi": "^3.0.0",
+ "supports-color": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/vue-server-renderer/node_modules/supports-color": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz",
+ "integrity": "sha512-KKNVtd6pCYgPIKU4cp2733HWYCpplQhddZLBUryaAHou723x+FRzQ5Df824Fj+IyyuiQTRoub4SnIFfIcrp70g==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.8.0"
+ }
+ },
+ "node_modules/vue-template-compiler": {
+ "version": "2.6.14",
+ "resolved": "https://registry.npmjs.org/vue-template-compiler/-/vue-template-compiler-2.6.14.tgz",
+ "integrity": "sha512-ODQS1SyMbjKoO1JBJZojSw6FE4qnh9rIpUZn2EUT86FKizx9uH5z6uXiIrm4/Nb/gwxTi/o17ZDEGWAXHvtC7g==",
+ "dev": true,
+ "dependencies": {
+ "de-indent": "^1.0.2",
+ "he": "^1.1.0"
+ }
+ },
+ "node_modules/walk-sync": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/walk-sync/-/walk-sync-2.2.0.tgz",
+ "integrity": "sha512-IC8sL7aB4/ZgFcGI2T1LczZeFWZ06b3zoHH7jBPyHxOtIIz1jppWHjjEXkOFvFojBVAK9pV7g47xOZ4LW3QLfg==",
+ "dev": true,
+ "dependencies": {
+ "@types/minimatch": "^3.0.3",
+ "ensure-posix-path": "^1.1.0",
+ "matcher-collection": "^2.0.0",
+ "minimatch": "^3.0.4"
+ },
+ "engines": {
+ "node": "8.* || >= 10.*"
+ }
+ },
+ "node_modules/websocket-driver": {
+ "version": "0.7.4",
+ "resolved": "https://registry.npmjs.org/websocket-driver/-/websocket-driver-0.7.4.tgz",
+ "integrity": "sha512-b17KeDIQVjvb0ssuSDF2cYXSg2iztliJ4B9WdsuB6J952qCPKmnVq4DyW5motImXHDC1cBT/1UezrJVsKw5zjg==",
+ "dev": true,
+ "dependencies": {
+ "http-parser-js": ">=0.5.1",
+ "safe-buffer": ">=5.1.0",
+ "websocket-extensions": ">=0.1.1"
+ },
+ "engines": {
+ "node": ">=0.8.0"
+ }
+ },
+ "node_modules/websocket-extensions": {
+ "version": "0.1.4",
+ "resolved": "https://registry.npmjs.org/websocket-extensions/-/websocket-extensions-0.1.4.tgz",
+ "integrity": "sha512-OqedPIGOfsDlo31UNwYbCFMSaO9m9G/0faIHj5/dZFDMFqPTcx6UwqyOy3COEaEOg/9VsGIpdqn62W5KhoKSpg==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.8.0"
+ }
+ },
+ "node_modules/winston": {
+ "version": "2.4.6",
+ "resolved": "https://registry.npmjs.org/winston/-/winston-2.4.6.tgz",
+ "integrity": "sha512-J5Zu4p0tojLde8mIOyDSsmLmcP8I3Z6wtwpTDHx1+hGcdhxcJaAmG4CFtagkb+NiN1M9Ek4b42pzMWqfc9jm8w==",
+ "dev": true,
+ "dependencies": {
+ "async": "^3.2.3",
+ "colors": "1.0.x",
+ "cycle": "1.0.x",
+ "eyes": "0.1.x",
+ "isstream": "0.1.x",
+ "stack-trace": "0.0.x"
+ },
+ "engines": {
+ "node": ">= 0.10.0"
+ }
+ },
+ "node_modules/winston-compat": {
+ "version": "0.1.5",
+ "resolved": "https://registry.npmjs.org/winston-compat/-/winston-compat-0.1.5.tgz",
+ "integrity": "sha512-EPvPcHT604AV3Ji6d3+vX8ENKIml9VSxMRnPQ+cuK/FX6f3hvPP2hxyoeeCOCFvDrJEujalfcKWlWPvAnFyS9g==",
+ "dev": true,
+ "dependencies": {
+ "cycle": "~1.0.3",
+ "logform": "^1.6.0",
+ "triple-beam": "^1.2.0"
+ },
+ "engines": {
+ "node": ">= 6.4.0"
+ }
+ },
+ "node_modules/winston-daily-rotate-file": {
+ "version": "3.10.0",
+ "resolved": "https://registry.npmjs.org/winston-daily-rotate-file/-/winston-daily-rotate-file-3.10.0.tgz",
+ "integrity": "sha512-KO8CfbI2CvdR3PaFApEH02GPXiwJ+vbkF1mCkTlvRIoXFI8EFlf1ACcuaahXTEiDEKCii6cNe95gsL4ZkbnphA==",
+ "dev": true,
+ "dependencies": {
+ "file-stream-rotator": "^0.4.1",
+ "object-hash": "^1.3.0",
+ "semver": "^6.2.0",
+ "triple-beam": "^1.3.0",
+ "winston-compat": "^0.1.4",
+ "winston-transport": "^4.2.0"
+ },
+ "engines": {
+ "node": ">=6"
+ },
+ "peerDependencies": {
+ "winston": "^2 || ^3"
+ }
+ },
+ "node_modules/winston-daily-rotate-file/node_modules/semver": {
+ "version": "6.3.0",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
+ "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==",
+ "dev": true,
+ "bin": {
+ "semver": "bin/semver.js"
+ }
+ },
+ "node_modules/winston-transport": {
+ "version": "4.5.0",
+ "resolved": "https://registry.npmjs.org/winston-transport/-/winston-transport-4.5.0.tgz",
+ "integrity": "sha512-YpZzcUzBedhlTAfJg6vJDlyEai/IFMIVcaEZZyl3UXIl4gmqRpU7AE89AHLkbzLUsv0NVmw7ts+iztqKxxPW1Q==",
+ "dev": true,
+ "dependencies": {
+ "logform": "^2.3.2",
+ "readable-stream": "^3.6.0",
+ "triple-beam": "^1.3.0"
+ },
+ "engines": {
+ "node": ">= 6.4.0"
+ }
+ },
+ "node_modules/winston-transport/node_modules/fecha": {
+ "version": "4.2.3",
+ "resolved": "https://registry.npmjs.org/fecha/-/fecha-4.2.3.tgz",
+ "integrity": "sha512-OP2IUU6HeYKJi3i0z4A19kHMQoLVs4Hc+DPqqxI2h/DPZHTm/vjsfC6P0b4jCMy14XizLBqvndQ+UilD7707Jw==",
+ "dev": true
+ },
+ "node_modules/winston-transport/node_modules/logform": {
+ "version": "2.4.2",
+ "resolved": "https://registry.npmjs.org/logform/-/logform-2.4.2.tgz",
+ "integrity": "sha512-W4c9himeAwXEdZ05dQNerhFz2XG80P9Oj0loPUMV23VC2it0orMHQhJm4hdnnor3rd1HsGf6a2lPwBM1zeXHGw==",
+ "dev": true,
+ "dependencies": {
+ "@colors/colors": "1.5.0",
+ "fecha": "^4.2.0",
+ "ms": "^2.1.1",
+ "safe-stable-stringify": "^2.3.1",
+ "triple-beam": "^1.3.0"
+ }
+ },
+ "node_modules/winston-transport/node_modules/ms": {
+ "version": "2.1.3",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
+ "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
+ "dev": true
+ },
+ "node_modules/winston/node_modules/async": {
+ "version": "3.2.4",
+ "resolved": "https://registry.npmjs.org/async/-/async-3.2.4.tgz",
+ "integrity": "sha512-iAB+JbDEGXhyIUavoDl9WP/Jj106Kz9DEn1DPgYw5ruDn0e3Wgi3sKFm55sASdGBNOQB8F59d9qQ7deqrHA8wQ==",
+ "dev": true
+ },
+ "node_modules/winston/node_modules/colors": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/colors/-/colors-1.0.3.tgz",
+ "integrity": "sha512-pFGrxThWcWQ2MsAz6RtgeWe4NK2kUE1WfsrvvlctdII745EW9I0yflqhe7++M5LEc7bV2c/9/5zc8sFcpL0Drw==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.1.90"
+ }
+ },
+ "node_modules/wrappy": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
+ "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==",
+ "dev": true
+ },
+ "node_modules/yallist": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz",
+ "integrity": "sha512-ncTzHV7NvsQZkYe1DW7cbDLm0YpzHmZF5r/iyP3ZnQtMiJ+pjzisCiMNI+Sj+xQF5pXhSHxSB3uDbsBTzY/c2A==",
+ "dev": true
+ }
+ },
+ "dependencies": {
+ "@colors/colors": {
+ "version": "1.5.0",
+ "resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.5.0.tgz",
+ "integrity": "sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ==",
+ "dev": true
+ },
+ "@fortawesome/fontawesome-free": {
+ "version": "6.4.2",
+ "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-free/-/fontawesome-free-6.4.2.tgz",
+ "integrity": "sha512-m5cPn3e2+FDCOgi1mz0RexTUvvQibBebOUlUlW0+YrMjDTPkiJ6VTKukA1GRsvRw+12KyJndNjj0O4AgTxm2Pg==",
+ "dev": true
+ },
+ "@kwsites/file-exists": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/@kwsites/file-exists/-/file-exists-1.1.1.tgz",
+ "integrity": "sha512-m9/5YGR18lIwxSFDwfE3oA7bWuq9kdau6ugN4H2rJeyhFQZcG9AgSHkQtSD15a8WvTgfz9aikZMrKPHvbpqFiw==",
+ "dev": true,
+ "requires": {
+ "debug": "^4.1.1"
+ },
+ "dependencies": {
+ "debug": {
+ "version": "4.3.4",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz",
+ "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==",
+ "dev": true,
+ "requires": {
+ "ms": "2.1.2"
+ }
+ },
+ "ms": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
+ "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==",
+ "dev": true
+ }
+ }
+ },
+ "@kwsites/promise-deferred": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/@kwsites/promise-deferred/-/promise-deferred-1.1.1.tgz",
+ "integrity": "sha512-GaHYm+c0O9MjZRu0ongGBRbinu8gVAMd2UZjji6jVmqKtZluZnptXGWhz1E8j8D2HJ3f/yMxKAUC0b+57wncIw==",
+ "dev": true
+ },
+ "@markbind/core": {
+ "version": "5.1.0",
+ "resolved": "https://registry.npmjs.org/@markbind/core/-/core-5.1.0.tgz",
+ "integrity": "sha512-YAXjH+qCXnrBzpKIAJkayVLmyIUaG/8Dms3Gpd2VIufeZyW8w0diXdgKSsymjzodTMgghZMdxG3Qpng833ARPg==",
+ "dev": true,
+ "requires": {
+ "@fortawesome/fontawesome-free": "^6.4.0",
+ "@markbind/core-web": "5.1.0",
+ "@primer/octicons": "^15.0.1",
+ "@sindresorhus/slugify": "^0.9.1",
+ "@tlylt/markdown-it-imsize": "^3.0.0",
+ "bluebird": "^3.7.2",
+ "bootswatch": "5.1.3",
+ "cheerio": "^0.22.0",
+ "crypto-js": "^4.0.0",
+ "csv-parse": "^4.14.2",
+ "ensure-posix-path": "^1.1.1",
+ "fastmatter": "^2.1.1",
+ "fs-extra": "^9.0.1",
+ "gh-pages": "^2.1.1",
+ "highlight.js": "^10.4.1",
+ "htmlparser2": "^3.10.1",
+ "ignore": "^5.1.4",
+ "js-beautify": "1.14.3",
+ "katex": "^0.15.6",
+ "lodash": "^4.17.15",
+ "markdown-it": "^12.3.2",
+ "markdown-it-attrs": "^4.1.3",
+ "markdown-it-emoji": "^1.4.0",
+ "markdown-it-linkify-images": "^3.0.0",
+ "markdown-it-mark": "^3.0.0",
+ "markdown-it-regexp": "^0.4.0",
+ "markdown-it-sub": "^1.0.0",
+ "markdown-it-sup": "^1.0.0",
+ "markdown-it-table-of-contents": "^0.4.4",
+ "markdown-it-task-lists": "^2.1.1",
+ "markdown-it-texmath": "^1.0.0",
+ "markdown-it-video": "^0.6.3",
+ "material-icons": "^1.9.1",
+ "moment": "^2.29.4",
+ "nunjucks": "3.2.2",
+ "path-is-inside": "^1.0.2",
+ "simple-git": "^2.17.0",
+ "url-parse": "^1.5.10",
+ "uuid": "^8.3.1",
+ "vue": "2.6.14",
+ "vue-server-renderer": "2.6.14",
+ "vue-template-compiler": "2.6.14",
+ "walk-sync": "^2.0.2",
+ "winston": "^2.4.4"
+ }
+ },
+ "@markbind/core-web": {
+ "version": "5.1.0",
+ "resolved": "https://registry.npmjs.org/@markbind/core-web/-/core-web-5.1.0.tgz",
+ "integrity": "sha512-TRzz8ZCr25pylKvFxF/WwXDi4Gbtsb2OLXV61WyTFqVy03tFoEJ2mqncpbliI9DrfDdKWcm1YZPgDCedVkYjKA==",
+ "dev": true
+ },
+ "@primer/octicons": {
+ "version": "15.2.0",
+ "resolved": "https://registry.npmjs.org/@primer/octicons/-/octicons-15.2.0.tgz",
+ "integrity": "sha512-4cHZzcZ3F/HQNL4EKSaFyVsW7XtITiJkTeB1JDDmRuP/XobyWyF9gWxuV9c+byUa8dOB5KNQn37iRvNrIehPUQ==",
+ "dev": true,
+ "requires": {
+ "object-assign": "^4.1.1"
+ }
+ },
+ "@sindresorhus/slugify": {
+ "version": "0.9.1",
+ "resolved": "https://registry.npmjs.org/@sindresorhus/slugify/-/slugify-0.9.1.tgz",
+ "integrity": "sha512-b6heYM9dzZD13t2GOiEQTDE0qX+I1GyOotMwKh9VQqzuNiVdPVT8dM43fe9HNb/3ul+Qwd5oKSEDrDIfhq3bnQ==",
+ "dev": true,
+ "requires": {
+ "escape-string-regexp": "^1.0.5",
+ "lodash.deburr": "^4.1.0"
+ }
+ },
+ "@tlylt/markdown-it-imsize": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/@tlylt/markdown-it-imsize/-/markdown-it-imsize-3.0.0.tgz",
+ "integrity": "sha512-6kTM+vRJTuN2UxNPyJ8yC+NHrzS+MxVHV+z+bDxSr/Fd7eTah2+otLKC2B17YI/1lQnSumA2qokPGuzsA98c6g==",
+ "dev": true
+ },
+ "@types/minimatch": {
+ "version": "3.0.5",
+ "resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-3.0.5.tgz",
+ "integrity": "sha512-Klz949h02Gz2uZCMGwDUSDS1YBlTdDDgbWHi+81l29tQALUtvz4rAYi5uoVhE5Lagoq6DeqAUlbrHvW/mXDgdQ==",
+ "dev": true
+ },
+ "a-sync-waterfall": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/a-sync-waterfall/-/a-sync-waterfall-1.0.1.tgz",
+ "integrity": "sha512-RYTOHHdWipFUliRFMCS4X2Yn2X8M87V/OpSqWzKKOGhzqyUxzyVmhHDH9sAvG+ZuQf/TAOFsLCpMw09I1ufUnA==",
+ "dev": true
+ },
+ "abbrev": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz",
+ "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==",
+ "dev": true
+ },
+ "accepts": {
+ "version": "1.3.8",
+ "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz",
+ "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==",
+ "dev": true,
+ "requires": {
+ "mime-types": "~2.1.34",
+ "negotiator": "0.6.3"
+ }
+ },
+ "ansi-regex": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz",
+ "integrity": "sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA==",
+ "dev": true
+ },
+ "ansi-styles": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+ "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+ "dev": true,
+ "requires": {
+ "color-convert": "^2.0.1"
+ }
+ },
+ "anymatch": {
+ "version": "3.1.3",
+ "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz",
+ "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==",
+ "dev": true,
+ "requires": {
+ "normalize-path": "^3.0.0",
+ "picomatch": "^2.0.4"
+ }
+ },
+ "apache-crypt": {
+ "version": "1.2.5",
+ "resolved": "https://registry.npmjs.org/apache-crypt/-/apache-crypt-1.2.5.tgz",
+ "integrity": "sha512-ICnYQH+DFVmw+S4Q0QY2XRXD8Ne8ewh8HgbuFH4K7022zCxgHM0Hz1xkRnUlEfAXNbwp1Cnhbedu60USIfDxvg==",
+ "dev": true,
+ "requires": {
+ "unix-crypt-td-js": "^1.1.4"
+ }
+ },
+ "apache-md5": {
+ "version": "1.1.7",
+ "resolved": "https://registry.npmjs.org/apache-md5/-/apache-md5-1.1.7.tgz",
+ "integrity": "sha512-JtHjzZmJxtzfTSjsCyHgPR155HBe5WGyUyHTaEkfy46qhwCFKx1Epm6nAxgUG3WfUZP1dWhGqj9Z2NOBeZ+uBw==",
+ "dev": true
+ },
+ "argparse": {
+ "version": "1.0.10",
+ "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz",
+ "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==",
+ "dev": true,
+ "requires": {
+ "sprintf-js": "~1.0.2"
+ }
+ },
+ "arr-diff": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz",
+ "integrity": "sha512-YVIQ82gZPGBebQV/a8dar4AitzCQs0jjXwMPZllpXMaGjXPYVUawSxQrRsjhjupyVxEvbHgUmIhKVlND+j02kA==",
+ "dev": true
+ },
+ "arr-flatten": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/arr-flatten/-/arr-flatten-1.1.0.tgz",
+ "integrity": "sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg==",
+ "dev": true
+ },
+ "arr-union": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/arr-union/-/arr-union-3.1.0.tgz",
+ "integrity": "sha512-sKpyeERZ02v1FeCZT8lrfJq5u6goHCtpTAzPwJYe7c8SPFOboNjNg1vz2L4VTn9T4PQxEx13TbXLmYUcS6Ug7Q==",
+ "dev": true
+ },
+ "array-union": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/array-union/-/array-union-1.0.2.tgz",
+ "integrity": "sha512-Dxr6QJj/RdU/hCaBjOfxW+q6lyuVE6JFWIrAUpuOOhoJJoQ99cUn3igRaHVB5P9WrgFVN0FfArM3x0cueOU8ng==",
+ "dev": true,
+ "requires": {
+ "array-uniq": "^1.0.1"
+ }
+ },
+ "array-uniq": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/array-uniq/-/array-uniq-1.0.3.tgz",
+ "integrity": "sha512-MNha4BWQ6JbwhFhj03YK552f7cb3AzoE8SzeljgChvL1dl3IcvggXVz1DilzySZkCja+CXuZbdW7yATchWn8/Q==",
+ "dev": true
+ },
+ "array-unique": {
+ "version": "0.3.2",
+ "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz",
+ "integrity": "sha512-SleRWjh9JUud2wH1hPs9rZBZ33H6T9HOiL0uwGnGx9FpE6wKGyfWugmbkEOIs6qWrZhg0LWeLziLrEwQJhs5mQ==",
+ "dev": true
+ },
+ "asap": {
+ "version": "2.0.6",
+ "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz",
+ "integrity": "sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==",
+ "dev": true
+ },
+ "assign-symbols": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/assign-symbols/-/assign-symbols-1.0.0.tgz",
+ "integrity": "sha512-Q+JC7Whu8HhmTdBph/Tq59IoRtoy6KAm5zzPv00WdujX82lbAL8K7WVjne7vdCsAmbF4AYaDOPyO3k0kl8qIrw==",
+ "dev": true
+ },
+ "async": {
+ "version": "2.6.4",
+ "resolved": "https://registry.npmjs.org/async/-/async-2.6.4.tgz",
+ "integrity": "sha512-mzo5dfJYwAn29PeiJ0zvwTo04zj8HDJj0Mn8TD7sno7q12prdbnasKJHhkm2c1LgrhlJ0teaea8860oxi51mGA==",
+ "dev": true,
+ "requires": {
+ "lodash": "^4.17.14"
+ }
+ },
+ "async-each": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/async-each/-/async-each-1.0.3.tgz",
+ "integrity": "sha512-z/WhQ5FPySLdvREByI2vZiTWwCnF0moMJ1hK9YQwDTHKh6I7/uSckMetoRGb5UBZPC1z0jlw+n/XCgjeH7y1AQ==",
+ "dev": true
+ },
+ "at-least-node": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/at-least-node/-/at-least-node-1.0.0.tgz",
+ "integrity": "sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==",
+ "dev": true
+ },
+ "atob": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz",
+ "integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==",
+ "dev": true
+ },
+ "balanced-match": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
+ "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
+ "dev": true
+ },
+ "base": {
+ "version": "0.11.2",
+ "resolved": "https://registry.npmjs.org/base/-/base-0.11.2.tgz",
+ "integrity": "sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg==",
+ "dev": true,
+ "requires": {
+ "cache-base": "^1.0.1",
+ "class-utils": "^0.3.5",
+ "component-emitter": "^1.2.1",
+ "define-property": "^1.0.0",
+ "isobject": "^3.0.1",
+ "mixin-deep": "^1.2.0",
+ "pascalcase": "^0.1.1"
+ },
+ "dependencies": {
+ "define-property": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz",
+ "integrity": "sha512-cZTYKFWspt9jZsMscWo8sc/5lbPC9Q0N5nBLgb+Yd915iL3udB1uFgS3B8YCx66UVHq018DAVFoee7x+gxggeA==",
+ "dev": true,
+ "requires": {
+ "is-descriptor": "^1.0.0"
+ }
+ }
+ }
+ },
+ "basic-auth": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/basic-auth/-/basic-auth-2.0.1.tgz",
+ "integrity": "sha512-NF+epuEdnUYVlGuhaxbbq+dvJttwLnGY+YixlXlME5KpQ5W3CnXA5cVTneY3SPbPDRkcjMbifrwmFYcClgOZeg==",
+ "dev": true,
+ "requires": {
+ "safe-buffer": "5.1.2"
+ }
+ },
+ "batch": {
+ "version": "0.6.1",
+ "resolved": "https://registry.npmjs.org/batch/-/batch-0.6.1.tgz",
+ "integrity": "sha512-x+VAiMRL6UPkx+kudNvxTl6hB2XNNCG2r+7wixVfIYwu/2HKRXimwQyaumLjMveWvT2Hkd/cAJw+QBMfJ/EKVw==",
+ "dev": true
+ },
+ "bcryptjs": {
+ "version": "2.4.3",
+ "resolved": "https://registry.npmjs.org/bcryptjs/-/bcryptjs-2.4.3.tgz",
+ "integrity": "sha512-V/Hy/X9Vt7f3BbPJEi8BdVFMByHi+jNXrYkW3huaybV/kQ0KJg0Y6PkEMbn+zeT+i+SiKZ/HMqJGIIt4LZDqNQ==",
+ "dev": true
+ },
+ "binary-extensions": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz",
+ "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==",
+ "dev": true
+ },
+ "bindings": {
+ "version": "1.5.0",
+ "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz",
+ "integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==",
+ "dev": true,
+ "optional": true,
+ "requires": {
+ "file-uri-to-path": "1.0.0"
+ }
+ },
+ "bluebird": {
+ "version": "3.7.2",
+ "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz",
+ "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==",
+ "dev": true
+ },
+ "boolbase": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz",
+ "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==",
+ "dev": true
+ },
+ "bootswatch": {
+ "version": "5.1.3",
+ "resolved": "https://registry.npmjs.org/bootswatch/-/bootswatch-5.1.3.tgz",
+ "integrity": "sha512-NmZFN6rOCoXWQ/PkzmD8FFWDe24kocX9OXWHNVaLxVVnpqpAzEbMFsf8bAfKwVtpNXibasZCzv09B5fLieAh2g==",
+ "dev": true
+ },
+ "brace-expansion": {
+ "version": "1.1.11",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
+ "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
+ "dev": true,
+ "requires": {
+ "balanced-match": "^1.0.0",
+ "concat-map": "0.0.1"
+ }
+ },
+ "braces": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz",
+ "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==",
+ "dev": true,
+ "requires": {
+ "fill-range": "^7.0.1"
+ }
+ },
+ "cache-base": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/cache-base/-/cache-base-1.0.1.tgz",
+ "integrity": "sha512-AKcdTnFSWATd5/GCPRxr2ChwIJ85CeyrEyjRHlKxQ56d4XJMGym0uAiKn0xbLOGOl3+yRpOTi484dVCEc5AUzQ==",
+ "dev": true,
+ "requires": {
+ "collection-visit": "^1.0.0",
+ "component-emitter": "^1.2.1",
+ "get-value": "^2.0.6",
+ "has-value": "^1.0.0",
+ "isobject": "^3.0.1",
+ "set-value": "^2.0.0",
+ "to-object-path": "^0.3.0",
+ "union-value": "^1.0.0",
+ "unset-value": "^1.0.0"
+ }
+ },
+ "chalk": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz",
+ "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==",
+ "dev": true,
+ "requires": {
+ "ansi-styles": "^4.1.0",
+ "supports-color": "^7.1.0"
+ }
+ },
+ "cheerio": {
+ "version": "0.22.0",
+ "resolved": "https://registry.npmjs.org/cheerio/-/cheerio-0.22.0.tgz",
+ "integrity": "sha512-8/MzidM6G/TgRelkzDG13y3Y9LxBjCb+8yOEZ9+wwq5gVF2w2pV0wmHvjfT0RvuxGyR7UEuK36r+yYMbT4uKgA==",
+ "dev": true,
+ "requires": {
+ "css-select": "~1.2.0",
+ "dom-serializer": "~0.1.0",
+ "entities": "~1.1.1",
+ "htmlparser2": "^3.9.1",
+ "lodash.assignin": "^4.0.9",
+ "lodash.bind": "^4.1.4",
+ "lodash.defaults": "^4.0.1",
+ "lodash.filter": "^4.4.0",
+ "lodash.flatten": "^4.2.0",
+ "lodash.foreach": "^4.3.0",
+ "lodash.map": "^4.4.0",
+ "lodash.merge": "^4.4.0",
+ "lodash.pick": "^4.2.1",
+ "lodash.reduce": "^4.4.0",
+ "lodash.reject": "^4.4.0",
+ "lodash.some": "^4.4.0"
+ }
+ },
+ "chokidar": {
+ "version": "3.5.3",
+ "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz",
+ "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==",
+ "dev": true,
+ "requires": {
+ "anymatch": "~3.1.2",
+ "braces": "~3.0.2",
+ "fsevents": "~2.3.2",
+ "glob-parent": "~5.1.2",
+ "is-binary-path": "~2.1.0",
+ "is-glob": "~4.0.1",
+ "normalize-path": "~3.0.0",
+ "readdirp": "~3.6.0"
+ }
+ },
+ "class-utils": {
+ "version": "0.3.6",
+ "resolved": "https://registry.npmjs.org/class-utils/-/class-utils-0.3.6.tgz",
+ "integrity": "sha512-qOhPa/Fj7s6TY8H8esGu5QNpMMQxz79h+urzrNYN6mn+9BnxlDGf5QZ+XeCDsxSjPqsSR56XOZOJmpeurnLMeg==",
+ "dev": true,
+ "requires": {
+ "arr-union": "^3.1.0",
+ "define-property": "^0.2.5",
+ "isobject": "^3.0.0",
+ "static-extend": "^0.1.1"
+ },
+ "dependencies": {
+ "define-property": {
+ "version": "0.2.5",
+ "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz",
+ "integrity": "sha512-Rr7ADjQZenceVOAKop6ALkkRAmH1A4Gx9hV/7ZujPUN2rkATqFO0JZLZInbAjpZYoJ1gUx8MRMQVkYemcbMSTA==",
+ "dev": true,
+ "requires": {
+ "is-descriptor": "^0.1.0"
+ }
+ },
+ "is-accessor-descriptor": {
+ "version": "0.1.6",
+ "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz",
+ "integrity": "sha512-e1BM1qnDbMRG3ll2U9dSK0UMHuWOs3pY3AtcFsmvwPtKL3MML/Q86i+GilLfvqEs4GW+ExB91tQ3Ig9noDIZ+A==",
+ "dev": true,
+ "requires": {
+ "kind-of": "^3.0.2"
+ },
+ "dependencies": {
+ "kind-of": {
+ "version": "3.2.2",
+ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
+ "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==",
+ "dev": true,
+ "requires": {
+ "is-buffer": "^1.1.5"
+ }
+ }
+ }
+ },
+ "is-data-descriptor": {
+ "version": "0.1.4",
+ "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz",
+ "integrity": "sha512-+w9D5ulSoBNlmw9OHn3U2v51SyoCd0he+bB3xMl62oijhrspxowjU+AIcDY0N3iEJbUEkB15IlMASQsxYigvXg==",
+ "dev": true,
+ "requires": {
+ "kind-of": "^3.0.2"
+ },
+ "dependencies": {
+ "kind-of": {
+ "version": "3.2.2",
+ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
+ "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==",
+ "dev": true,
+ "requires": {
+ "is-buffer": "^1.1.5"
+ }
+ }
+ }
+ },
+ "is-descriptor": {
+ "version": "0.1.6",
+ "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz",
+ "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==",
+ "dev": true,
+ "requires": {
+ "is-accessor-descriptor": "^0.1.6",
+ "is-data-descriptor": "^0.1.4",
+ "kind-of": "^5.0.0"
+ }
+ },
+ "kind-of": {
+ "version": "5.1.0",
+ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz",
+ "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==",
+ "dev": true
+ }
+ }
+ },
+ "collection-visit": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/collection-visit/-/collection-visit-1.0.0.tgz",
+ "integrity": "sha512-lNkKvzEeMBBjUGHZ+q6z9pSJla0KWAQPvtzhEV9+iGyQYG+pBpl7xKDhxoNSOZH2hhv0v5k0y2yAM4o4SjoSkw==",
+ "dev": true,
+ "requires": {
+ "map-visit": "^1.0.0",
+ "object-visit": "^1.0.0"
+ }
+ },
+ "color-convert": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
+ "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+ "dev": true,
+ "requires": {
+ "color-name": "~1.1.4"
+ }
+ },
+ "color-name": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
+ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
+ "dev": true
+ },
+ "colors": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/colors/-/colors-1.4.0.tgz",
+ "integrity": "sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA==",
+ "dev": true
+ },
+ "commander": {
+ "version": "8.3.0",
+ "resolved": "https://registry.npmjs.org/commander/-/commander-8.3.0.tgz",
+ "integrity": "sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==",
+ "dev": true
+ },
+ "component-emitter": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz",
+ "integrity": "sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==",
+ "dev": true
+ },
+ "concat-map": {
+ "version": "0.0.1",
+ "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
+ "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==",
+ "dev": true
+ },
+ "config-chain": {
+ "version": "1.1.13",
+ "resolved": "https://registry.npmjs.org/config-chain/-/config-chain-1.1.13.tgz",
+ "integrity": "sha512-qj+f8APARXHrM0hraqXYb2/bOVSV4PvJQlNZ/DVj0QrmNM2q2euizkeuVckQ57J+W0mRH6Hvi+k50M4Jul2VRQ==",
+ "dev": true,
+ "requires": {
+ "ini": "^1.3.4",
+ "proto-list": "~1.2.1"
+ }
+ },
+ "connect": {
+ "version": "3.7.0",
+ "resolved": "https://registry.npmjs.org/connect/-/connect-3.7.0.tgz",
+ "integrity": "sha512-ZqRXc+tZukToSNmh5C2iWMSoV3X1YUcPbqEM4DkEG5tNQXrQUZCNVGGv3IuicnkMtPfGf3Xtp8WCXs295iQ1pQ==",
+ "dev": true,
+ "requires": {
+ "debug": "2.6.9",
+ "finalhandler": "1.1.2",
+ "parseurl": "~1.3.3",
+ "utils-merge": "1.0.1"
+ }
+ },
+ "copy-descriptor": {
+ "version": "0.1.1",
+ "resolved": "https://registry.npmjs.org/copy-descriptor/-/copy-descriptor-0.1.1.tgz",
+ "integrity": "sha512-XgZ0pFcakEUlbwQEVNg3+QAis1FyTL3Qel9FYy8pSkQqoG3PNoT0bOCQtOXcOkur21r2Eq2kI+IE+gsmAEVlYw==",
+ "dev": true
+ },
+ "core-util-is": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz",
+ "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==",
+ "dev": true
+ },
+ "cors": {
+ "version": "2.8.5",
+ "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz",
+ "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==",
+ "dev": true,
+ "requires": {
+ "object-assign": "^4",
+ "vary": "^1"
+ }
+ },
+ "crypto-js": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/crypto-js/-/crypto-js-4.1.1.tgz",
+ "integrity": "sha512-o2JlM7ydqd3Qk9CA0L4NL6mTzU2sdx96a+oOfPu8Mkl/PK51vSyoi8/rQ8NknZtk44vq15lmhAj9CIAGwgeWKw==",
+ "dev": true
+ },
+ "css-select": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/css-select/-/css-select-1.2.0.tgz",
+ "integrity": "sha512-dUQOBoqdR7QwV90WysXPLXG5LO7nhYBgiWVfxF80DKPF8zx1t/pUd2FYy73emg3zrjtM6dzmYgbHKfV2rxiHQA==",
+ "dev": true,
+ "requires": {
+ "boolbase": "~1.0.0",
+ "css-what": "2.1",
+ "domutils": "1.5.1",
+ "nth-check": "~1.0.1"
+ }
+ },
+ "css-what": {
+ "version": "2.1.3",
+ "resolved": "https://registry.npmjs.org/css-what/-/css-what-2.1.3.tgz",
+ "integrity": "sha512-a+EPoD+uZiNfh+5fxw2nO9QwFa6nJe2Or35fGY6Ipw1R3R4AGz1d1TEZrCegvw2YTmZ0jXirGYlzxxpYSHwpEg==",
+ "dev": true
+ },
+ "csv-parse": {
+ "version": "4.16.3",
+ "resolved": "https://registry.npmjs.org/csv-parse/-/csv-parse-4.16.3.tgz",
+ "integrity": "sha512-cO1I/zmz4w2dcKHVvpCr7JVRu8/FymG5OEpmvsZYlccYolPBLoVGKUHgNoc4ZGkFeFlWGEDmMyBM+TTqRdW/wg==",
+ "dev": true
+ },
+ "cycle": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/cycle/-/cycle-1.0.3.tgz",
+ "integrity": "sha512-TVF6svNzeQCOpjCqsy0/CSy8VgObG3wXusJ73xW2GbG5rGx7lC8zxDSURicsXI2UsGdi2L0QNRCi745/wUDvsA==",
+ "dev": true
+ },
+ "de-indent": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/de-indent/-/de-indent-1.0.2.tgz",
+ "integrity": "sha512-e/1zu3xH5MQryN2zdVaF0OrdNLUbvWxzMbi+iNA6Bky7l1RoP8a2fIbRocyHclXt/arDrrR6lL3TqFD9pMQTsg==",
+ "dev": true
+ },
+ "debug": {
+ "version": "2.6.9",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
+ "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
+ "dev": true,
+ "requires": {
+ "ms": "2.0.0"
+ }
+ },
+ "decode-uri-component": {
+ "version": "0.2.0",
+ "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.0.tgz",
+ "integrity": "sha512-hjf+xovcEn31w/EUYdTXQh/8smFL/dzYjohQGEIgjyNavaJfBY2p5F527Bo1VPATxv0VYTUC2bOcXvqFwk78Og==",
+ "dev": true
+ },
+ "define-property": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/define-property/-/define-property-2.0.2.tgz",
+ "integrity": "sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ==",
+ "dev": true,
+ "requires": {
+ "is-descriptor": "^1.0.2",
+ "isobject": "^3.0.1"
+ }
+ },
+ "depd": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz",
+ "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==",
+ "dev": true
+ },
+ "destroy": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz",
+ "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==",
+ "dev": true
+ },
+ "dom-serializer": {
+ "version": "0.1.1",
+ "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-0.1.1.tgz",
+ "integrity": "sha512-l0IU0pPzLWSHBcieZbpOKgkIn3ts3vAh7ZuFyXNwJxJXk/c4Gwj9xaTJwIDVQCXawWD0qb3IzMGH5rglQaO0XA==",
+ "dev": true,
+ "requires": {
+ "domelementtype": "^1.3.0",
+ "entities": "^1.1.1"
+ }
+ },
+ "domelementtype": {
+ "version": "1.3.1",
+ "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-1.3.1.tgz",
+ "integrity": "sha512-BSKB+TSpMpFI/HOxCNr1O8aMOTZ8hT3pM3GQ0w/mWRmkhEDSFJkkyzz4XQsBV44BChwGkrDfMyjVD0eA2aFV3w==",
+ "dev": true
+ },
+ "domhandler": {
+ "version": "2.4.2",
+ "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-2.4.2.tgz",
+ "integrity": "sha512-JiK04h0Ht5u/80fdLMCEmV4zkNh2BcoMFBmZ/91WtYZ8qVXSKjiw7fXMgFPnHcSZgOo3XdinHvmnDUeMf5R4wA==",
+ "dev": true,
+ "requires": {
+ "domelementtype": "1"
+ }
+ },
+ "domutils": {
+ "version": "1.5.1",
+ "resolved": "https://registry.npmjs.org/domutils/-/domutils-1.5.1.tgz",
+ "integrity": "sha512-gSu5Oi/I+3wDENBsOWBiRK1eoGxcywYSqg3rR960/+EfY0CF4EX1VPkgHOZ3WiS/Jg2DtliF6BhWcHlfpYUcGw==",
+ "dev": true,
+ "requires": {
+ "dom-serializer": "0",
+ "domelementtype": "1"
+ }
+ },
+ "duplexer": {
+ "version": "0.1.2",
+ "resolved": "https://registry.npmjs.org/duplexer/-/duplexer-0.1.2.tgz",
+ "integrity": "sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg==",
+ "dev": true
+ },
+ "editorconfig": {
+ "version": "0.15.3",
+ "resolved": "https://registry.npmjs.org/editorconfig/-/editorconfig-0.15.3.tgz",
+ "integrity": "sha512-M9wIMFx96vq0R4F+gRpY3o2exzb8hEj/n9S8unZtHSvYjibBp/iMufSzvmOcV/laG0ZtuTVGtiJggPOSW2r93g==",
+ "dev": true,
+ "requires": {
+ "commander": "^2.19.0",
+ "lru-cache": "^4.1.5",
+ "semver": "^5.6.0",
+ "sigmund": "^1.0.1"
+ },
+ "dependencies": {
+ "commander": {
+ "version": "2.20.3",
+ "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz",
+ "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==",
+ "dev": true
+ }
+ }
+ },
+ "ee-first": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
+ "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==",
+ "dev": true
+ },
+ "email-addresses": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/email-addresses/-/email-addresses-3.1.0.tgz",
+ "integrity": "sha512-k0/r7GrWVL32kZlGwfPNgB2Y/mMXVTq/decgLczm/j34whdaspNrZO8CnXPf1laaHxI6ptUlsnAxN+UAPw+fzg==",
+ "dev": true
+ },
+ "encodeurl": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz",
+ "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==",
+ "dev": true
+ },
+ "ensure-posix-path": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/ensure-posix-path/-/ensure-posix-path-1.1.1.tgz",
+ "integrity": "sha512-VWU0/zXzVbeJNXvME/5EmLuEj2TauvoaTz6aFYK1Z92JCBlDlZ3Gu0tuGR42kpW1754ywTs+QB0g5TP0oj9Zaw==",
+ "dev": true
+ },
+ "entities": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/entities/-/entities-1.1.2.tgz",
+ "integrity": "sha512-f2LZMYl1Fzu7YSBKg+RoROelpOaNrcGmE9AZubeDfrCEia483oW4MI4VyFd5VNHIgQ/7qm1I0wUHK1eJnn2y2w==",
+ "dev": true
+ },
+ "escape-html": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz",
+ "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==",
+ "dev": true
+ },
+ "escape-string-regexp": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
+ "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==",
+ "dev": true
+ },
+ "esprima": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz",
+ "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==",
+ "dev": true
+ },
+ "etag": {
+ "version": "1.8.1",
+ "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz",
+ "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==",
+ "dev": true
+ },
+ "event-stream": {
+ "version": "3.3.4",
+ "resolved": "https://registry.npmjs.org/event-stream/-/event-stream-3.3.4.tgz",
+ "integrity": "sha512-QHpkERcGsR0T7Qm3HNJSyXKEEj8AHNxkY3PK8TS2KJvQ7NiSHe3DDpwVKKtoYprL/AreyzFBeIkBIWChAqn60g==",
+ "dev": true,
+ "requires": {
+ "duplexer": "~0.1.1",
+ "from": "~0",
+ "map-stream": "~0.1.0",
+ "pause-stream": "0.0.11",
+ "split": "0.3",
+ "stream-combiner": "~0.0.4",
+ "through": "~2.3.1"
+ },
+ "dependencies": {
+ "split": {
+ "version": "0.3.3",
+ "resolved": "https://registry.npmjs.org/split/-/split-0.3.3.tgz",
+ "integrity": "sha512-wD2AeVmxXRBoX44wAycgjVpMhvbwdI2aZjCkvfNcH1YqHQvJVa1duWc73OyVGJUc05fhFaTZeQ/PYsrmyH0JVA==",
+ "dev": true,
+ "requires": {
+ "through": "2"
+ }
+ },
+ "stream-combiner": {
+ "version": "0.0.4",
+ "resolved": "https://registry.npmjs.org/stream-combiner/-/stream-combiner-0.0.4.tgz",
+ "integrity": "sha512-rT00SPnTVyRsaSz5zgSPma/aHSOic5U1prhYdRy5HS2kTZviFpmDgzilbtsJsxiroqACmayynDN/9VzIbX5DOw==",
+ "dev": true,
+ "requires": {
+ "duplexer": "~0.1.1"
+ }
+ }
+ }
+ },
+ "expand-brackets": {
+ "version": "2.1.4",
+ "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz",
+ "integrity": "sha512-w/ozOKR9Obk3qoWeY/WDi6MFta9AoMR+zud60mdnbniMcBxRuFJyDt2LdX/14A1UABeqk+Uk+LDfUpvoGKppZA==",
+ "dev": true,
+ "requires": {
+ "debug": "^2.3.3",
+ "define-property": "^0.2.5",
+ "extend-shallow": "^2.0.1",
+ "posix-character-classes": "^0.1.0",
+ "regex-not": "^1.0.0",
+ "snapdragon": "^0.8.1",
+ "to-regex": "^3.0.1"
+ },
+ "dependencies": {
+ "define-property": {
+ "version": "0.2.5",
+ "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz",
+ "integrity": "sha512-Rr7ADjQZenceVOAKop6ALkkRAmH1A4Gx9hV/7ZujPUN2rkATqFO0JZLZInbAjpZYoJ1gUx8MRMQVkYemcbMSTA==",
+ "dev": true,
+ "requires": {
+ "is-descriptor": "^0.1.0"
+ }
+ },
+ "extend-shallow": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
+ "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==",
+ "dev": true,
+ "requires": {
+ "is-extendable": "^0.1.0"
+ }
+ },
+ "is-accessor-descriptor": {
+ "version": "0.1.6",
+ "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz",
+ "integrity": "sha512-e1BM1qnDbMRG3ll2U9dSK0UMHuWOs3pY3AtcFsmvwPtKL3MML/Q86i+GilLfvqEs4GW+ExB91tQ3Ig9noDIZ+A==",
+ "dev": true,
+ "requires": {
+ "kind-of": "^3.0.2"
+ },
+ "dependencies": {
+ "kind-of": {
+ "version": "3.2.2",
+ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
+ "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==",
+ "dev": true,
+ "requires": {
+ "is-buffer": "^1.1.5"
+ }
+ }
+ }
+ },
+ "is-data-descriptor": {
+ "version": "0.1.4",
+ "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz",
+ "integrity": "sha512-+w9D5ulSoBNlmw9OHn3U2v51SyoCd0he+bB3xMl62oijhrspxowjU+AIcDY0N3iEJbUEkB15IlMASQsxYigvXg==",
+ "dev": true,
+ "requires": {
+ "kind-of": "^3.0.2"
+ },
+ "dependencies": {
+ "kind-of": {
+ "version": "3.2.2",
+ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
+ "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==",
+ "dev": true,
+ "requires": {
+ "is-buffer": "^1.1.5"
+ }
+ }
+ }
+ },
+ "is-descriptor": {
+ "version": "0.1.6",
+ "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz",
+ "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==",
+ "dev": true,
+ "requires": {
+ "is-accessor-descriptor": "^0.1.6",
+ "is-data-descriptor": "^0.1.4",
+ "kind-of": "^5.0.0"
+ }
+ },
+ "is-extendable": {
+ "version": "0.1.1",
+ "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz",
+ "integrity": "sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw==",
+ "dev": true
+ },
+ "kind-of": {
+ "version": "5.1.0",
+ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz",
+ "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==",
+ "dev": true
+ }
+ }
+ },
+ "extend-shallow": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz",
+ "integrity": "sha512-BwY5b5Ql4+qZoefgMj2NUmx+tehVTH/Kf4k1ZEtOHNFcm2wSxMRo992l6X3TIgni2eZVTZ85xMOjF31fwZAj6Q==",
+ "dev": true,
+ "requires": {
+ "assign-symbols": "^1.0.0",
+ "is-extendable": "^1.0.1"
+ }
+ },
+ "extglob": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz",
+ "integrity": "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==",
+ "dev": true,
+ "requires": {
+ "array-unique": "^0.3.2",
+ "define-property": "^1.0.0",
+ "expand-brackets": "^2.1.4",
+ "extend-shallow": "^2.0.1",
+ "fragment-cache": "^0.2.1",
+ "regex-not": "^1.0.0",
+ "snapdragon": "^0.8.1",
+ "to-regex": "^3.0.1"
+ },
+ "dependencies": {
+ "define-property": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz",
+ "integrity": "sha512-cZTYKFWspt9jZsMscWo8sc/5lbPC9Q0N5nBLgb+Yd915iL3udB1uFgS3B8YCx66UVHq018DAVFoee7x+gxggeA==",
+ "dev": true,
+ "requires": {
+ "is-descriptor": "^1.0.0"
+ }
+ },
+ "extend-shallow": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
+ "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==",
+ "dev": true,
+ "requires": {
+ "is-extendable": "^0.1.0"
+ }
+ },
+ "is-extendable": {
+ "version": "0.1.1",
+ "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz",
+ "integrity": "sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw==",
+ "dev": true
+ }
+ }
+ },
+ "eyes": {
+ "version": "0.1.8",
+ "resolved": "https://registry.npmjs.org/eyes/-/eyes-0.1.8.tgz",
+ "integrity": "sha512-GipyPsXO1anza0AOZdy69Im7hGFCNB7Y/NGjDlZGJ3GJJLtwNSb2vrzYrTYJRrRloVx7pl+bhUaTB8yiccPvFQ==",
+ "dev": true
+ },
+ "fast-safe-stringify": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/fast-safe-stringify/-/fast-safe-stringify-2.1.1.tgz",
+ "integrity": "sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA==",
+ "dev": true
+ },
+ "fastmatter": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/fastmatter/-/fastmatter-2.1.1.tgz",
+ "integrity": "sha512-NFrjZEPJZTexoJEuyM5J7n4uFaLf0dOI7Ok4b2IZXOYBqCp1Bh5RskANmQ2TuDsz3M35B1yL2AP/Rn+kp85KeA==",
+ "dev": true,
+ "requires": {
+ "js-yaml": "^3.13.0",
+ "split": "^1.0.1",
+ "stream-combiner": "^0.2.2",
+ "through2": "^3.0.1"
+ }
+ },
+ "faye-websocket": {
+ "version": "0.11.4",
+ "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.11.4.tgz",
+ "integrity": "sha512-CzbClwlXAuiRQAlUyfqPgvPoNKTckTPGfwZV4ZdAhVcP2lh9KUxJg2b5GkE7XbjKQ3YJnQ9z6D9ntLAlB+tP8g==",
+ "dev": true,
+ "requires": {
+ "websocket-driver": ">=0.5.1"
+ }
+ },
+ "fecha": {
+ "version": "2.3.3",
+ "resolved": "https://registry.npmjs.org/fecha/-/fecha-2.3.3.tgz",
+ "integrity": "sha512-lUGBnIamTAwk4znq5BcqsDaxSmZ9nDVJaij6NvRt/Tg4R69gERA+otPKbS86ROw9nxVMw2/mp1fnaiWqbs6Sdg==",
+ "dev": true
+ },
+ "figlet": {
+ "version": "1.5.2",
+ "resolved": "https://registry.npmjs.org/figlet/-/figlet-1.5.2.tgz",
+ "integrity": "sha512-WOn21V8AhyE1QqVfPIVxe3tupJacq1xGkPTB4iagT6o+P2cAgEOOwIxMftr4+ZCTI6d551ij9j61DFr0nsP2uQ==",
+ "dev": true
+ },
+ "file-stream-rotator": {
+ "version": "0.4.1",
+ "resolved": "https://registry.npmjs.org/file-stream-rotator/-/file-stream-rotator-0.4.1.tgz",
+ "integrity": "sha512-W3aa3QJEc8BS2MmdVpQiYLKHj3ijpto1gMDlsgCRSKfIUe6MwkcpODGPQ3vZfb0XvCeCqlu9CBQTN7oQri2TZQ==",
+ "dev": true,
+ "requires": {
+ "moment": "^2.11.2"
+ }
+ },
+ "file-uri-to-path": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz",
+ "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==",
+ "dev": true,
+ "optional": true
+ },
+ "filename-reserved-regex": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/filename-reserved-regex/-/filename-reserved-regex-1.0.0.tgz",
+ "integrity": "sha512-UZArj7+U+2reBBVCvVmRlyq9D7EYQdUtuNN+1iz7pF1jGcJ2L0TjiRCxsTZfj2xFbM4c25uGCUDpKTHA7L2TKg==",
+ "dev": true
+ },
+ "filenamify": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/filenamify/-/filenamify-1.2.1.tgz",
+ "integrity": "sha512-DKVP0WQcB7WaIMSwDETqImRej2fepPqvXQjaVib7LRZn9Rxn5UbvK2tYTqGf1A1DkIprQQkG4XSQXSOZp7Q3GQ==",
+ "dev": true,
+ "requires": {
+ "filename-reserved-regex": "^1.0.0",
+ "strip-outer": "^1.0.0",
+ "trim-repeated": "^1.0.0"
+ }
+ },
+ "filenamify-url": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/filenamify-url/-/filenamify-url-1.0.0.tgz",
+ "integrity": "sha512-O9K9JcZeF5VdZWM1qR92NSv1WY2EofwudQayPx5dbnnFl9k0IcZha4eV/FGkjnBK+1irOQInij0yiooCHu/0Fg==",
+ "dev": true,
+ "requires": {
+ "filenamify": "^1.0.0",
+ "humanize-url": "^1.0.0"
+ }
+ },
+ "fill-range": {
+ "version": "7.0.1",
+ "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz",
+ "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==",
+ "dev": true,
+ "requires": {
+ "to-regex-range": "^5.0.1"
+ }
+ },
+ "finalhandler": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz",
+ "integrity": "sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==",
+ "dev": true,
+ "requires": {
+ "debug": "2.6.9",
+ "encodeurl": "~1.0.2",
+ "escape-html": "~1.0.3",
+ "on-finished": "~2.3.0",
+ "parseurl": "~1.3.3",
+ "statuses": "~1.5.0",
+ "unpipe": "~1.0.0"
+ }
+ },
+ "find-up": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz",
+ "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==",
+ "dev": true,
+ "requires": {
+ "locate-path": "^5.0.0",
+ "path-exists": "^4.0.0"
+ }
+ },
+ "for-in": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz",
+ "integrity": "sha512-7EwmXrOjyL+ChxMhmG5lnW9MPt1aIeZEwKhQzoBUdTV0N3zuwWDZYVJatDvZ2OyzPUvdIAZDsCetk3coyMfcnQ==",
+ "dev": true
+ },
+ "fragment-cache": {
+ "version": "0.2.1",
+ "resolved": "https://registry.npmjs.org/fragment-cache/-/fragment-cache-0.2.1.tgz",
+ "integrity": "sha512-GMBAbW9antB8iZRHLoGw0b3HANt57diZYFO/HL1JGIC1MjKrdmhxvrJbupnVvpys0zsz7yBApXdQyfepKly2kA==",
+ "dev": true,
+ "requires": {
+ "map-cache": "^0.2.2"
+ }
+ },
+ "fresh": {
+ "version": "0.5.2",
+ "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz",
+ "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==",
+ "dev": true
+ },
+ "from": {
+ "version": "0.1.7",
+ "resolved": "https://registry.npmjs.org/from/-/from-0.1.7.tgz",
+ "integrity": "sha512-twe20eF1OxVxp/ML/kq2p1uc6KvFK/+vs8WjEbeKmV2He22MKm7YF2ANIt+EOqhJ5L3K/SuuPhk0hWQDjOM23g==",
+ "dev": true
+ },
+ "fs-extra": {
+ "version": "9.1.0",
+ "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz",
+ "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==",
+ "dev": true,
+ "requires": {
+ "at-least-node": "^1.0.0",
+ "graceful-fs": "^4.2.0",
+ "jsonfile": "^6.0.1",
+ "universalify": "^2.0.0"
+ }
+ },
+ "fs.realpath": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
+ "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==",
+ "dev": true
+ },
+ "fsevents": {
+ "version": "2.3.3",
+ "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
+ "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==",
+ "dev": true,
+ "optional": true
+ },
+ "function-bind": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz",
+ "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==",
+ "dev": true
+ },
+ "get-value": {
+ "version": "2.0.6",
+ "resolved": "https://registry.npmjs.org/get-value/-/get-value-2.0.6.tgz",
+ "integrity": "sha512-Ln0UQDlxH1BapMu3GPtf7CuYNwRZf2gwCuPqbyG6pB8WfmFpzqcy4xtAaAMUhnNqjMKTiCPZG2oMT3YSx8U2NA==",
+ "dev": true
+ },
+ "gh-pages": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/gh-pages/-/gh-pages-2.2.0.tgz",
+ "integrity": "sha512-c+yPkNOPMFGNisYg9r4qvsMIjVYikJv7ImFOhPIVPt0+AcRUamZ7zkGRLHz7FKB0xrlZ+ddSOJsZv9XAFVXLmA==",
+ "dev": true,
+ "requires": {
+ "async": "^2.6.1",
+ "commander": "^2.18.0",
+ "email-addresses": "^3.0.1",
+ "filenamify-url": "^1.0.0",
+ "fs-extra": "^8.1.0",
+ "globby": "^6.1.0"
+ },
+ "dependencies": {
+ "commander": {
+ "version": "2.20.3",
+ "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz",
+ "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==",
+ "dev": true
+ },
+ "fs-extra": {
+ "version": "8.1.0",
+ "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz",
+ "integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==",
+ "dev": true,
+ "requires": {
+ "graceful-fs": "^4.2.0",
+ "jsonfile": "^4.0.0",
+ "universalify": "^0.1.0"
+ }
+ },
+ "jsonfile": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz",
+ "integrity": "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==",
+ "dev": true,
+ "requires": {
+ "graceful-fs": "^4.1.6"
+ }
+ },
+ "universalify": {
+ "version": "0.1.2",
+ "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz",
+ "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==",
+ "dev": true
+ }
+ }
+ },
+ "glob": {
+ "version": "7.2.3",
+ "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz",
+ "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==",
+ "dev": true,
+ "requires": {
+ "fs.realpath": "^1.0.0",
+ "inflight": "^1.0.4",
+ "inherits": "2",
+ "minimatch": "^3.1.1",
+ "once": "^1.3.0",
+ "path-is-absolute": "^1.0.0"
+ }
+ },
+ "glob-parent": {
+ "version": "5.1.2",
+ "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
+ "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
+ "dev": true,
+ "requires": {
+ "is-glob": "^4.0.1"
+ }
+ },
+ "globby": {
+ "version": "6.1.0",
+ "resolved": "https://registry.npmjs.org/globby/-/globby-6.1.0.tgz",
+ "integrity": "sha512-KVbFv2TQtbzCoxAnfD6JcHZTYCzyliEaaeM/gH8qQdkKr5s0OP9scEgvdcngyk7AVdY6YVW/TJHd+lQ/Df3Daw==",
+ "dev": true,
+ "requires": {
+ "array-union": "^1.0.1",
+ "glob": "^7.0.3",
+ "object-assign": "^4.0.1",
+ "pify": "^2.0.0",
+ "pinkie-promise": "^2.0.0"
+ }
+ },
+ "graceful-fs": {
+ "version": "4.2.10",
+ "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.10.tgz",
+ "integrity": "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==",
+ "dev": true
+ },
+ "has": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz",
+ "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==",
+ "dev": true,
+ "requires": {
+ "function-bind": "^1.1.1"
+ }
+ },
+ "has-ansi": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz",
+ "integrity": "sha512-C8vBJ8DwUCx19vhm7urhTuUsr4/IyP6l4VzNQDv+ryHQObW3TTTp9yB68WpYgRe2bbaGuZ/se74IqFeVnMnLZg==",
+ "dev": true,
+ "requires": {
+ "ansi-regex": "^2.0.0"
+ }
+ },
+ "has-flag": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
+ "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
+ "dev": true
+ },
+ "has-value": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/has-value/-/has-value-1.0.0.tgz",
+ "integrity": "sha512-IBXk4GTsLYdQ7Rvt+GRBrFSVEkmuOUy4re0Xjd9kJSUQpnTrWR4/y9RpfexN9vkAPMFuQoeWKwqzPozRTlasGw==",
+ "dev": true,
+ "requires": {
+ "get-value": "^2.0.6",
+ "has-values": "^1.0.0",
+ "isobject": "^3.0.0"
+ }
+ },
+ "has-values": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/has-values/-/has-values-1.0.0.tgz",
+ "integrity": "sha512-ODYZC64uqzmtfGMEAX/FvZiRyWLpAC3vYnNunURUnkGVTS+mI0smVsWaPydRBsE3g+ok7h960jChO8mFcWlHaQ==",
+ "dev": true,
+ "requires": {
+ "is-number": "^3.0.0",
+ "kind-of": "^4.0.0"
+ },
+ "dependencies": {
+ "is-number": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz",
+ "integrity": "sha512-4cboCqIpliH+mAvFNegjZQ4kgKc3ZUhQVr3HvWbSh5q3WH2v82ct+T2Y1hdU5Gdtorx/cLifQjqCbL7bpznLTg==",
+ "dev": true,
+ "requires": {
+ "kind-of": "^3.0.2"
+ },
+ "dependencies": {
+ "kind-of": {
+ "version": "3.2.2",
+ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
+ "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==",
+ "dev": true,
+ "requires": {
+ "is-buffer": "^1.1.5"
+ }
+ }
+ }
+ },
+ "kind-of": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-4.0.0.tgz",
+ "integrity": "sha512-24XsCxmEbRwEDbz/qz3stgin8TTzZ1ESR56OMCN0ujYg+vRutNSiOj9bHH9u85DKgXguraugV5sFuvbD4FW/hw==",
+ "dev": true,
+ "requires": {
+ "is-buffer": "^1.1.5"
+ }
+ }
+ }
+ },
+ "hash-sum": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/hash-sum/-/hash-sum-1.0.2.tgz",
+ "integrity": "sha512-fUs4B4L+mlt8/XAtSOGMUO1TXmAelItBPtJG7CyHJfYTdDjwisntGO2JQz7oUsatOY9o68+57eziUVNw/mRHmA==",
+ "dev": true
+ },
+ "he": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz",
+ "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==",
+ "dev": true
+ },
+ "highlight.js": {
+ "version": "10.7.3",
+ "resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-10.7.3.tgz",
+ "integrity": "sha512-tzcUFauisWKNHaRkN4Wjl/ZA07gENAjFl3J/c480dprkGTg5EQstgaNFqBfUqCq54kZRIEcreTsAgF/m2quD7A==",
+ "dev": true
+ },
+ "htmlparser2": {
+ "version": "3.10.1",
+ "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-3.10.1.tgz",
+ "integrity": "sha512-IgieNijUMbkDovyoKObU1DUhm1iwNYE/fuifEoEHfd1oZKZDaONBSkal7Y01shxsM49R4XaMdGez3WnF9UfiCQ==",
+ "dev": true,
+ "requires": {
+ "domelementtype": "^1.3.1",
+ "domhandler": "^2.3.0",
+ "domutils": "^1.5.1",
+ "entities": "^1.1.1",
+ "inherits": "^2.0.1",
+ "readable-stream": "^3.1.1"
+ }
+ },
+ "http-auth": {
+ "version": "3.1.3",
+ "resolved": "https://registry.npmjs.org/http-auth/-/http-auth-3.1.3.tgz",
+ "integrity": "sha512-Jbx0+ejo2IOx+cRUYAGS1z6RGc6JfYUNkysZM4u4Sfk1uLlGv814F7/PIjQQAuThLdAWxb74JMGd5J8zex1VQg==",
+ "dev": true,
+ "requires": {
+ "apache-crypt": "^1.1.2",
+ "apache-md5": "^1.0.6",
+ "bcryptjs": "^2.3.0",
+ "uuid": "^3.0.0"
+ },
+ "dependencies": {
+ "uuid": {
+ "version": "3.4.0",
+ "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz",
+ "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==",
+ "dev": true
+ }
+ }
+ },
+ "http-errors": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz",
+ "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==",
+ "dev": true,
+ "requires": {
+ "depd": "2.0.0",
+ "inherits": "2.0.4",
+ "setprototypeof": "1.2.0",
+ "statuses": "2.0.1",
+ "toidentifier": "1.0.1"
+ },
+ "dependencies": {
+ "statuses": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz",
+ "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==",
+ "dev": true
+ }
+ }
+ },
+ "http-parser-js": {
+ "version": "0.5.8",
+ "resolved": "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.5.8.tgz",
+ "integrity": "sha512-SGeBX54F94Wgu5RH3X5jsDtf4eHyRogWX1XGT3b4HuW3tQPM4AaBzoUji/4AAJNXCEOWZ5O0DgZmJw1947gD5Q==",
+ "dev": true
+ },
+ "humanize-url": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/humanize-url/-/humanize-url-1.0.1.tgz",
+ "integrity": "sha512-RtgTzXCPVb/te+e82NDhAc5paj+DuKSratIGAr+v+HZK24eAQ8LMoBGYoL7N/O+9iEc33AKHg45dOMKw3DNldQ==",
+ "dev": true,
+ "requires": {
+ "normalize-url": "^1.0.0",
+ "strip-url-auth": "^1.0.0"
+ }
+ },
+ "ignore": {
+ "version": "5.2.4",
+ "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.4.tgz",
+ "integrity": "sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ==",
+ "dev": true
+ },
+ "inflight": {
+ "version": "1.0.6",
+ "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
+ "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==",
+ "dev": true,
+ "requires": {
+ "once": "^1.3.0",
+ "wrappy": "1"
+ }
+ },
+ "inherits": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
+ "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
+ "dev": true
+ },
+ "ini": {
+ "version": "1.3.8",
+ "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz",
+ "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==",
+ "dev": true
+ },
+ "is-accessor-descriptor": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz",
+ "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==",
+ "dev": true,
+ "requires": {
+ "kind-of": "^6.0.0"
+ }
+ },
+ "is-binary-path": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz",
+ "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==",
+ "dev": true,
+ "requires": {
+ "binary-extensions": "^2.0.0"
+ }
+ },
+ "is-buffer": {
+ "version": "1.1.6",
+ "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz",
+ "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==",
+ "dev": true
+ },
+ "is-core-module": {
+ "version": "2.13.0",
+ "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.13.0.tgz",
+ "integrity": "sha512-Z7dk6Qo8pOCp3l4tsX2C5ZVas4V+UxwQodwZhLopL91TX8UyyHEXafPcyoeeWuLrwzHcr3igO78wNLwHJHsMCQ==",
+ "dev": true,
+ "requires": {
+ "has": "^1.0.3"
+ }
+ },
+ "is-data-descriptor": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz",
+ "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==",
+ "dev": true,
+ "requires": {
+ "kind-of": "^6.0.0"
+ }
+ },
+ "is-descriptor": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz",
+ "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==",
+ "dev": true,
+ "requires": {
+ "is-accessor-descriptor": "^1.0.0",
+ "is-data-descriptor": "^1.0.0",
+ "kind-of": "^6.0.2"
+ }
+ },
+ "is-extendable": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz",
+ "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==",
+ "dev": true,
+ "requires": {
+ "is-plain-object": "^2.0.4"
+ }
+ },
+ "is-extglob": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
+ "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==",
+ "dev": true
+ },
+ "is-glob": {
+ "version": "4.0.3",
+ "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz",
+ "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==",
+ "dev": true,
+ "requires": {
+ "is-extglob": "^2.1.1"
+ }
+ },
+ "is-number": {
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
+ "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
+ "dev": true
+ },
+ "is-plain-obj": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-1.1.0.tgz",
+ "integrity": "sha512-yvkRyxmFKEOQ4pNXCmJG5AEQNlXJS5LaONXo5/cLdTZdWvsZ1ioJEonLGAosKlMWE8lwUy/bJzMjcw8az73+Fg==",
+ "dev": true
+ },
+ "is-plain-object": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz",
+ "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==",
+ "dev": true,
+ "requires": {
+ "isobject": "^3.0.1"
+ }
+ },
+ "is-windows": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz",
+ "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==",
+ "dev": true
+ },
+ "is-wsl": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-1.1.0.tgz",
+ "integrity": "sha512-gfygJYZ2gLTDlmbWMI0CE2MwnFzSN/2SZfkMlItC4K/JBlsWVDB0bO6XhqcY13YXE7iMcAJnzTCJjPiTeJJ0Mw==",
+ "dev": true
+ },
+ "isarray": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
+ "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==",
+ "dev": true
+ },
+ "isobject": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz",
+ "integrity": "sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg==",
+ "dev": true
+ },
+ "isstream": {
+ "version": "0.1.2",
+ "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz",
+ "integrity": "sha512-Yljz7ffyPbrLpLngrMtZ7NduUgVvi6wG9RJ9IUcyCd59YQ911PBJphODUcbOVbqYfxe1wuYf/LJ8PauMRwsM/g==",
+ "dev": true
+ },
+ "js-beautify": {
+ "version": "1.14.3",
+ "resolved": "https://registry.npmjs.org/js-beautify/-/js-beautify-1.14.3.tgz",
+ "integrity": "sha512-f1ra8PHtOEu/70EBnmiUlV8nJePS58y9qKjl4JHfYWlFH6bo7ogZBz//FAZp7jDuXtYnGYKymZPlrg2I/9Zo4g==",
+ "dev": true,
+ "requires": {
+ "config-chain": "^1.1.13",
+ "editorconfig": "^0.15.3",
+ "glob": "^7.1.3",
+ "nopt": "^5.0.0"
+ }
+ },
+ "js-yaml": {
+ "version": "3.14.1",
+ "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz",
+ "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==",
+ "dev": true,
+ "requires": {
+ "argparse": "^1.0.7",
+ "esprima": "^4.0.0"
+ }
+ },
+ "jsonfile": {
+ "version": "6.1.0",
+ "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz",
+ "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==",
+ "dev": true,
+ "requires": {
+ "graceful-fs": "^4.1.6",
+ "universalify": "^2.0.0"
+ }
+ },
+ "katex": {
+ "version": "0.15.6",
+ "resolved": "https://registry.npmjs.org/katex/-/katex-0.15.6.tgz",
+ "integrity": "sha512-UpzJy4yrnqnhXvRPhjEuLA4lcPn6eRngixW7Q3TJErjg3Aw2PuLFBzTkdUb89UtumxjhHTqL3a5GDGETMSwgJA==",
+ "dev": true,
+ "requires": {
+ "commander": "^8.0.0"
+ }
+ },
+ "kind-of": {
+ "version": "6.0.3",
+ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz",
+ "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==",
+ "dev": true
+ },
+ "linkify-it": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-3.0.3.tgz",
+ "integrity": "sha512-ynTsyrFSdE5oZ/O9GEf00kPngmOfVwazR5GKDq6EYfhlpFug3J2zybX56a2PRRpc9P+FuSoGNAwjlbDs9jJBPQ==",
+ "dev": true,
+ "requires": {
+ "uc.micro": "^1.0.1"
+ }
+ },
+ "live-server": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/live-server/-/live-server-1.2.1.tgz",
+ "integrity": "sha512-Yn2XCVjErTkqnM3FfTmM7/kWy3zP7+cEtC7x6u+wUzlQ+1UW3zEYbbyJrc0jNDwiMDZI0m4a0i3dxlGHVyXczw==",
+ "dev": true,
+ "requires": {
+ "chokidar": "^2.0.4",
+ "colors": "latest",
+ "connect": "^3.6.6",
+ "cors": "latest",
+ "event-stream": "3.3.4",
+ "faye-websocket": "0.11.x",
+ "http-auth": "3.1.x",
+ "morgan": "^1.9.1",
+ "object-assign": "latest",
+ "opn": "latest",
+ "proxy-middleware": "latest",
+ "send": "latest",
+ "serve-index": "^1.9.1"
+ },
+ "dependencies": {
+ "anymatch": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-2.0.0.tgz",
+ "integrity": "sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw==",
+ "dev": true,
+ "requires": {
+ "micromatch": "^3.1.4",
+ "normalize-path": "^2.1.1"
+ },
+ "dependencies": {
+ "normalize-path": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz",
+ "integrity": "sha512-3pKJwH184Xo/lnH6oyP1q2pMd7HcypqqmRs91/6/i2CGtWwIKGCkOOMTm/zXbgTEWHw1uNpNi/igc3ePOYHb6w==",
+ "dev": true,
+ "requires": {
+ "remove-trailing-separator": "^1.0.1"
+ }
+ }
+ }
+ },
+ "binary-extensions": {
+ "version": "1.13.1",
+ "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-1.13.1.tgz",
+ "integrity": "sha512-Un7MIEDdUC5gNpcGDV97op1Ywk748MpHcFTHoYs6qnj1Z3j7I53VG3nwZhKzoBZmbdRNnb6WRdFlwl7tSDuZGw==",
+ "dev": true
+ },
+ "braces": {
+ "version": "2.3.2",
+ "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz",
+ "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==",
+ "dev": true,
+ "requires": {
+ "arr-flatten": "^1.1.0",
+ "array-unique": "^0.3.2",
+ "extend-shallow": "^2.0.1",
+ "fill-range": "^4.0.0",
+ "isobject": "^3.0.1",
+ "repeat-element": "^1.1.2",
+ "snapdragon": "^0.8.1",
+ "snapdragon-node": "^2.0.1",
+ "split-string": "^3.0.2",
+ "to-regex": "^3.0.1"
+ }
+ },
+ "chokidar": {
+ "version": "2.1.8",
+ "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-2.1.8.tgz",
+ "integrity": "sha512-ZmZUazfOzf0Nve7duiCKD23PFSCs4JPoYyccjUFF3aQkQadqBhfzhjkwBH2mNOG9cTBwhamM37EIsIkZw3nRgg==",
+ "dev": true,
+ "requires": {
+ "anymatch": "^2.0.0",
+ "async-each": "^1.0.1",
+ "braces": "^2.3.2",
+ "fsevents": "^1.2.7",
+ "glob-parent": "^3.1.0",
+ "inherits": "^2.0.3",
+ "is-binary-path": "^1.0.0",
+ "is-glob": "^4.0.0",
+ "normalize-path": "^3.0.0",
+ "path-is-absolute": "^1.0.0",
+ "readdirp": "^2.2.1",
+ "upath": "^1.1.1"
+ }
+ },
+ "extend-shallow": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
+ "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==",
+ "dev": true,
+ "requires": {
+ "is-extendable": "^0.1.0"
+ }
+ },
+ "fill-range": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz",
+ "integrity": "sha512-VcpLTWqWDiTerugjj8e3+esbg+skS3M9e54UuR3iCeIDMXCLTsAH8hTSzDQU/X6/6t3eYkOKoZSef2PlU6U1XQ==",
+ "dev": true,
+ "requires": {
+ "extend-shallow": "^2.0.1",
+ "is-number": "^3.0.0",
+ "repeat-string": "^1.6.1",
+ "to-regex-range": "^2.1.0"
+ }
+ },
+ "fsevents": {
+ "version": "1.2.13",
+ "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.13.tgz",
+ "integrity": "sha512-oWb1Z6mkHIskLzEJ/XWX0srkpkTQ7vaopMQkyaEIoq0fmtFVxOthb8cCxeT+p3ynTdkk/RZwbgG4brR5BeWECw==",
+ "dev": true,
+ "optional": true,
+ "requires": {
+ "bindings": "^1.5.0",
+ "nan": "^2.12.1"
+ }
+ },
+ "glob-parent": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz",
+ "integrity": "sha512-E8Ak/2+dZY6fnzlR7+ueWvhsH1SjHr4jjss4YS/h4py44jY9MhK/VFdaZJAWDz6BbL21KeteKxFSFpq8OS5gVA==",
+ "dev": true,
+ "requires": {
+ "is-glob": "^3.1.0",
+ "path-dirname": "^1.0.0"
+ },
+ "dependencies": {
+ "is-glob": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz",
+ "integrity": "sha512-UFpDDrPgM6qpnFNI+rh/p3bUaq9hKLZN8bMUWzxmcnZVS3omf4IPK+BrewlnWjO1WmUsMYuSjKh4UJuV4+Lqmw==",
+ "dev": true,
+ "requires": {
+ "is-extglob": "^2.1.0"
+ }
+ }
+ }
+ },
+ "is-binary-path": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-1.0.1.tgz",
+ "integrity": "sha512-9fRVlXc0uCxEDj1nQzaWONSpbTfx0FmJfzHF7pwlI8DkWGoHBBea4Pg5Ky0ojwwxQmnSifgbKkI06Qv0Ljgj+Q==",
+ "dev": true,
+ "requires": {
+ "binary-extensions": "^1.0.0"
+ }
+ },
+ "is-extendable": {
+ "version": "0.1.1",
+ "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz",
+ "integrity": "sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw==",
+ "dev": true
+ },
+ "is-number": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz",
+ "integrity": "sha512-4cboCqIpliH+mAvFNegjZQ4kgKc3ZUhQVr3HvWbSh5q3WH2v82ct+T2Y1hdU5Gdtorx/cLifQjqCbL7bpznLTg==",
+ "dev": true,
+ "requires": {
+ "kind-of": "^3.0.2"
+ }
+ },
+ "kind-of": {
+ "version": "3.2.2",
+ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
+ "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==",
+ "dev": true,
+ "requires": {
+ "is-buffer": "^1.1.5"
+ }
+ },
+ "readable-stream": {
+ "version": "2.3.7",
+ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz",
+ "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==",
+ "dev": true,
+ "requires": {
+ "core-util-is": "~1.0.0",
+ "inherits": "~2.0.3",
+ "isarray": "~1.0.0",
+ "process-nextick-args": "~2.0.0",
+ "safe-buffer": "~5.1.1",
+ "string_decoder": "~1.1.1",
+ "util-deprecate": "~1.0.1"
+ }
+ },
+ "readdirp": {
+ "version": "2.2.1",
+ "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-2.2.1.tgz",
+ "integrity": "sha512-1JU/8q+VgFZyxwrJ+SVIOsh+KywWGpds3NTqikiKpDMZWScmAYyKIgqkO+ARvNWJfXeXR1zxz7aHF4u4CyH6vQ==",
+ "dev": true,
+ "requires": {
+ "graceful-fs": "^4.1.11",
+ "micromatch": "^3.1.10",
+ "readable-stream": "^2.0.2"
+ }
+ },
+ "string_decoder": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
+ "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
+ "dev": true,
+ "requires": {
+ "safe-buffer": "~5.1.0"
+ }
+ },
+ "to-regex-range": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz",
+ "integrity": "sha512-ZZWNfCjUokXXDGXFpZehJIkZqq91BcULFq/Pi7M5i4JnxXdhMKAK682z8bCW3o8Hj1wuuzoKcW3DfVzaP6VuNg==",
+ "dev": true,
+ "requires": {
+ "is-number": "^3.0.0",
+ "repeat-string": "^1.6.1"
+ }
+ }
+ }
+ },
+ "locate-path": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz",
+ "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==",
+ "dev": true,
+ "requires": {
+ "p-locate": "^4.1.0"
+ }
+ },
+ "lodash": {
+ "version": "4.17.21",
+ "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
+ "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==",
+ "dev": true
+ },
+ "lodash._reinterpolate": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/lodash._reinterpolate/-/lodash._reinterpolate-3.0.0.tgz",
+ "integrity": "sha512-xYHt68QRoYGjeeM/XOE1uJtvXQAgvszfBhjV4yvsQH0u2i9I6cI6c6/eG4Hh3UAOVn0y/xAXwmTzEay49Q//HA==",
+ "dev": true
+ },
+ "lodash.assignin": {
+ "version": "4.2.0",
+ "resolved": "https://registry.npmjs.org/lodash.assignin/-/lodash.assignin-4.2.0.tgz",
+ "integrity": "sha512-yX/rx6d/UTVh7sSVWVSIMjfnz95evAgDFdb1ZozC35I9mSFCkmzptOzevxjgbQUsc78NR44LVHWjsoMQXy9FDg==",
+ "dev": true
+ },
+ "lodash.bind": {
+ "version": "4.2.1",
+ "resolved": "https://registry.npmjs.org/lodash.bind/-/lodash.bind-4.2.1.tgz",
+ "integrity": "sha512-lxdsn7xxlCymgLYo1gGvVrfHmkjDiyqVv62FAeF2i5ta72BipE1SLxw8hPEPLhD4/247Ijw07UQH7Hq/chT5LA==",
+ "dev": true
+ },
+ "lodash.deburr": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/lodash.deburr/-/lodash.deburr-4.1.0.tgz",
+ "integrity": "sha512-m/M1U1f3ddMCs6Hq2tAsYThTBDaAKFDX3dwDo97GEYzamXi9SqUpjWi/Rrj/gf3X2n8ktwgZrlP1z6E3v/IExQ==",
+ "dev": true
+ },
+ "lodash.defaults": {
+ "version": "4.2.0",
+ "resolved": "https://registry.npmjs.org/lodash.defaults/-/lodash.defaults-4.2.0.tgz",
+ "integrity": "sha512-qjxPLHd3r5DnsdGacqOMU6pb/avJzdh9tFX2ymgoZE27BmjXrNy/y4LoaiTeAb+O3gL8AfpJGtqfX/ae2leYYQ==",
+ "dev": true
+ },
+ "lodash.filter": {
+ "version": "4.6.0",
+ "resolved": "https://registry.npmjs.org/lodash.filter/-/lodash.filter-4.6.0.tgz",
+ "integrity": "sha512-pXYUy7PR8BCLwX5mgJ/aNtyOvuJTdZAo9EQFUvMIYugqmJxnrYaANvTbgndOzHSCSR0wnlBBfRXJL5SbWxo3FQ==",
+ "dev": true
+ },
+ "lodash.flatten": {
+ "version": "4.4.0",
+ "resolved": "https://registry.npmjs.org/lodash.flatten/-/lodash.flatten-4.4.0.tgz",
+ "integrity": "sha512-C5N2Z3DgnnKr0LOpv/hKCgKdb7ZZwafIrsesve6lmzvZIRZRGaZ/l6Q8+2W7NaT+ZwO3fFlSCzCzrDCFdJfZ4g==",
+ "dev": true
+ },
+ "lodash.foreach": {
+ "version": "4.5.0",
+ "resolved": "https://registry.npmjs.org/lodash.foreach/-/lodash.foreach-4.5.0.tgz",
+ "integrity": "sha512-aEXTF4d+m05rVOAUG3z4vZZ4xVexLKZGF0lIxuHZ1Hplpk/3B6Z1+/ICICYRLm7c41Z2xiejbkCkJoTlypoXhQ==",
+ "dev": true
+ },
+ "lodash.map": {
+ "version": "4.6.0",
+ "resolved": "https://registry.npmjs.org/lodash.map/-/lodash.map-4.6.0.tgz",
+ "integrity": "sha512-worNHGKLDetmcEYDvh2stPCrrQRkP20E4l0iIS7F8EvzMqBBi7ltvFN5m1HvTf1P7Jk1txKhvFcmYsCr8O2F1Q==",
+ "dev": true
+ },
+ "lodash.merge": {
+ "version": "4.6.2",
+ "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz",
+ "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==",
+ "dev": true
+ },
+ "lodash.pick": {
+ "version": "4.4.0",
+ "resolved": "https://registry.npmjs.org/lodash.pick/-/lodash.pick-4.4.0.tgz",
+ "integrity": "sha512-hXt6Ul/5yWjfklSGvLQl8vM//l3FtyHZeuelpzK6mm99pNvN9yTDruNZPEJZD1oWrqo+izBmB7oUfWgcCX7s4Q==",
+ "dev": true
+ },
+ "lodash.reduce": {
+ "version": "4.6.0",
+ "resolved": "https://registry.npmjs.org/lodash.reduce/-/lodash.reduce-4.6.0.tgz",
+ "integrity": "sha512-6raRe2vxCYBhpBu+B+TtNGUzah+hQjVdu3E17wfusjyrXBka2nBS8OH/gjVZ5PvHOhWmIZTYri09Z6n/QfnNMw==",
+ "dev": true
+ },
+ "lodash.reject": {
+ "version": "4.6.0",
+ "resolved": "https://registry.npmjs.org/lodash.reject/-/lodash.reject-4.6.0.tgz",
+ "integrity": "sha512-qkTuvgEzYdyhiJBx42YPzPo71R1aEr0z79kAv7Ixg8wPFEjgRgJdUsGMG3Hf3OYSF/kHI79XhNlt+5Ar6OzwxQ==",
+ "dev": true
+ },
+ "lodash.some": {
+ "version": "4.6.0",
+ "resolved": "https://registry.npmjs.org/lodash.some/-/lodash.some-4.6.0.tgz",
+ "integrity": "sha512-j7MJE+TuT51q9ggt4fSgVqro163BEFjAt3u97IqU+JA2DkWl80nFTrowzLpZ/BnpN7rrl0JA/593NAdd8p/scQ==",
+ "dev": true
+ },
+ "lodash.template": {
+ "version": "4.5.0",
+ "resolved": "https://registry.npmjs.org/lodash.template/-/lodash.template-4.5.0.tgz",
+ "integrity": "sha512-84vYFxIkmidUiFxidA/KjjH9pAycqW+h980j7Fuz5qxRtO9pgB7MDFTdys1N7A5mcucRiDyEq4fusljItR1T/A==",
+ "dev": true,
+ "requires": {
+ "lodash._reinterpolate": "^3.0.0",
+ "lodash.templatesettings": "^4.0.0"
+ }
+ },
+ "lodash.templatesettings": {
+ "version": "4.2.0",
+ "resolved": "https://registry.npmjs.org/lodash.templatesettings/-/lodash.templatesettings-4.2.0.tgz",
+ "integrity": "sha512-stgLz+i3Aa9mZgnjr/O+v9ruKZsPsndy7qPZOchbqk2cnTU1ZaldKK+v7m54WoKIyxiuMZTKT2H81F8BeAc3ZQ==",
+ "dev": true,
+ "requires": {
+ "lodash._reinterpolate": "^3.0.0"
+ }
+ },
+ "lodash.uniq": {
+ "version": "4.5.0",
+ "resolved": "https://registry.npmjs.org/lodash.uniq/-/lodash.uniq-4.5.0.tgz",
+ "integrity": "sha512-xfBaXQd9ryd9dlSDvnvI0lvxfLJlYAZzXomUYzLKtUeOQvOP5piqAWuGtrhWeqaXK9hhoM/iyJc5AV+XfsX3HQ==",
+ "dev": true
+ },
+ "logform": {
+ "version": "1.10.0",
+ "resolved": "https://registry.npmjs.org/logform/-/logform-1.10.0.tgz",
+ "integrity": "sha512-em5ojIhU18fIMOw/333mD+ZLE2fis0EzXl1ZwHx4iQzmpQi6odNiY/t+ITNr33JZhT9/KEaH+UPIipr6a9EjWg==",
+ "dev": true,
+ "requires": {
+ "colors": "^1.2.1",
+ "fast-safe-stringify": "^2.0.4",
+ "fecha": "^2.3.3",
+ "ms": "^2.1.1",
+ "triple-beam": "^1.2.0"
+ },
+ "dependencies": {
+ "ms": {
+ "version": "2.1.3",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
+ "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
+ "dev": true
+ }
+ }
+ },
+ "lru-cache": {
+ "version": "4.1.5",
+ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.5.tgz",
+ "integrity": "sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==",
+ "dev": true,
+ "requires": {
+ "pseudomap": "^1.0.2",
+ "yallist": "^2.1.2"
+ }
+ },
+ "map-cache": {
+ "version": "0.2.2",
+ "resolved": "https://registry.npmjs.org/map-cache/-/map-cache-0.2.2.tgz",
+ "integrity": "sha512-8y/eV9QQZCiyn1SprXSrCmqJN0yNRATe+PO8ztwqrvrbdRLA3eYJF0yaR0YayLWkMbsQSKWS9N2gPcGEc4UsZg==",
+ "dev": true
+ },
+ "map-stream": {
+ "version": "0.1.0",
+ "resolved": "https://registry.npmjs.org/map-stream/-/map-stream-0.1.0.tgz",
+ "integrity": "sha512-CkYQrPYZfWnu/DAmVCpTSX/xHpKZ80eKh2lAkyA6AJTef6bW+6JpbQZN5rofum7da+SyN1bi5ctTm+lTfcCW3g==",
+ "dev": true
+ },
+ "map-visit": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/map-visit/-/map-visit-1.0.0.tgz",
+ "integrity": "sha512-4y7uGv8bd2WdM9vpQsiQNo41Ln1NvhvDRuVt0k2JZQ+ezN2uaQes7lZeZ+QQUHOLQAtDaBJ+7wCbi+ab/KFs+w==",
+ "dev": true,
+ "requires": {
+ "object-visit": "^1.0.0"
+ }
+ },
+ "markbind-cli": {
+ "version": "5.1.0",
+ "resolved": "https://registry.npmjs.org/markbind-cli/-/markbind-cli-5.1.0.tgz",
+ "integrity": "sha512-6POI1Q++2aZa+Udk/oQ6LX1oNPbKUBDY0mN3Up7VOFeK+XYW51faxuCk2Q91JTBxYRKLNtshxf0y12kB4Cj9Qw==",
+ "dev": true,
+ "requires": {
+ "@markbind/core": "5.1.0",
+ "@markbind/core-web": "5.1.0",
+ "bluebird": "^3.7.2",
+ "chalk": "^3.0.0",
+ "cheerio": "^0.22.0",
+ "chokidar": "^3.3.0",
+ "colors": "1.4.0",
+ "commander": "^8.1.0",
+ "figlet": "^1.2.4",
+ "find-up": "^4.1.0",
+ "fs-extra": "^9.0.1",
+ "live-server": "1.2.1",
+ "lodash": "^4.17.15",
+ "url-parse": "^1.5.10",
+ "winston": "^2.4.4",
+ "winston-daily-rotate-file": "^3.10.0"
+ }
+ },
+ "markdown-it": {
+ "version": "12.3.2",
+ "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-12.3.2.tgz",
+ "integrity": "sha512-TchMembfxfNVpHkbtriWltGWc+m3xszaRD0CZup7GFFhzIgQqxIfn3eGj1yZpfuflzPvfkt611B2Q/Bsk1YnGg==",
+ "dev": true,
+ "requires": {
+ "argparse": "^2.0.1",
+ "entities": "~2.1.0",
+ "linkify-it": "^3.0.1",
+ "mdurl": "^1.0.1",
+ "uc.micro": "^1.0.5"
+ },
+ "dependencies": {
+ "argparse": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
+ "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==",
+ "dev": true
+ },
+ "entities": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/entities/-/entities-2.1.0.tgz",
+ "integrity": "sha512-hCx1oky9PFrJ611mf0ifBLBRW8lUUVRlFolb5gWRfIELabBlbp9xZvrqZLZAs+NxFnbfQoeGd8wDkygjg7U85w==",
+ "dev": true
+ }
+ }
+ },
+ "markdown-it-attrs": {
+ "version": "4.1.6",
+ "resolved": "https://registry.npmjs.org/markdown-it-attrs/-/markdown-it-attrs-4.1.6.tgz",
+ "integrity": "sha512-O7PDKZlN8RFMyDX13JnctQompwrrILuz2y43pW2GagcwpIIElkAdfeek+erHfxUOlXWPsjFeWmZ8ch1xtRLWpA==",
+ "dev": true,
+ "requires": {}
+ },
+ "markdown-it-emoji": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/markdown-it-emoji/-/markdown-it-emoji-1.4.0.tgz",
+ "integrity": "sha512-QCz3Hkd+r5gDYtS2xsFXmBYrgw6KuWcJZLCEkdfAuwzZbShCmCfta+hwAMq4NX/4xPzkSHduMKgMkkPUJxSXNg==",
+ "dev": true
+ },
+ "markdown-it-linkify-images": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/markdown-it-linkify-images/-/markdown-it-linkify-images-3.0.0.tgz",
+ "integrity": "sha512-Vs5yGJa5MWjFgytzgtn8c1U6RcStj3FZKhhx459U8dYbEE5FTWZ6mMRkYMiDlkFO0j4VCsQT1LT557bY0ETgtg==",
+ "dev": true,
+ "requires": {
+ "markdown-it": "^13.0.1"
+ },
+ "dependencies": {
+ "argparse": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
+ "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==",
+ "dev": true
+ },
+ "entities": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/entities/-/entities-3.0.1.tgz",
+ "integrity": "sha512-WiyBqoomrwMdFG1e0kqvASYfnlb0lp8M5o5Fw2OFq1hNZxxcNk8Ik0Xm7LxzBhuidnZB/UtBqVCgUz3kBOP51Q==",
+ "dev": true
+ },
+ "linkify-it": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-4.0.1.tgz",
+ "integrity": "sha512-C7bfi1UZmoj8+PQx22XyeXCuBlokoyWQL5pWSP+EI6nzRylyThouddufc2c1NDIcP9k5agmN9fLpA7VNJfIiqw==",
+ "dev": true,
+ "requires": {
+ "uc.micro": "^1.0.1"
+ }
+ },
+ "markdown-it": {
+ "version": "13.0.1",
+ "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-13.0.1.tgz",
+ "integrity": "sha512-lTlxriVoy2criHP0JKRhO2VDG9c2ypWCsT237eDiLqi09rmbKoUetyGHq2uOIRoRS//kfoJckS0eUzzkDR+k2Q==",
+ "dev": true,
+ "requires": {
+ "argparse": "^2.0.1",
+ "entities": "~3.0.1",
+ "linkify-it": "^4.0.1",
+ "mdurl": "^1.0.1",
+ "uc.micro": "^1.0.5"
+ }
+ }
+ }
+ },
+ "markdown-it-mark": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/markdown-it-mark/-/markdown-it-mark-3.0.1.tgz",
+ "integrity": "sha512-HyxjAu6BRsdt6Xcv6TKVQnkz/E70TdGXEFHRYBGLncRE9lBFwDNLVtFojKxjJWgJ+5XxUwLaHXy+2sGBbDn+4A==",
+ "dev": true
+ },
+ "markdown-it-regexp": {
+ "version": "0.4.0",
+ "resolved": "https://registry.npmjs.org/markdown-it-regexp/-/markdown-it-regexp-0.4.0.tgz",
+ "integrity": "sha512-0XQmr46K/rMKnI93Y3CLXsHj4jIioRETTAiVnJnjrZCEkGaDOmUxTbZj/aZ17G5NlRcVpWBYjqpwSlQ9lj+Kxw==",
+ "dev": true
+ },
+ "markdown-it-sub": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/markdown-it-sub/-/markdown-it-sub-1.0.0.tgz",
+ "integrity": "sha512-z2Rm/LzEE1wzwTSDrI+FlPEveAAbgdAdPhdWarq/ZGJrGW/uCQbKAnhoCsE4hAbc3SEym26+W2z/VQB0cQiA9Q==",
+ "dev": true
+ },
+ "markdown-it-sup": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/markdown-it-sup/-/markdown-it-sup-1.0.0.tgz",
+ "integrity": "sha512-E32m0nV9iyhRR7CrhnzL5msqic7rL1juWre6TQNxsnApg7Uf+F97JOKxUijg5YwXz86lZ0mqfOnutoryyNdntQ==",
+ "dev": true
+ },
+ "markdown-it-table-of-contents": {
+ "version": "0.4.4",
+ "resolved": "https://registry.npmjs.org/markdown-it-table-of-contents/-/markdown-it-table-of-contents-0.4.4.tgz",
+ "integrity": "sha512-TAIHTHPwa9+ltKvKPWulm/beozQU41Ab+FIefRaQV1NRnpzwcV9QOe6wXQS5WLivm5Q/nlo0rl6laGkMDZE7Gw==",
+ "dev": true
+ },
+ "markdown-it-task-lists": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/markdown-it-task-lists/-/markdown-it-task-lists-2.1.1.tgz",
+ "integrity": "sha512-TxFAc76Jnhb2OUu+n3yz9RMu4CwGfaT788br6HhEDlvWfdeJcLUsxk1Hgw2yJio0OXsxv7pyIPmvECY7bMbluA==",
+ "dev": true
+ },
+ "markdown-it-texmath": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/markdown-it-texmath/-/markdown-it-texmath-1.0.0.tgz",
+ "integrity": "sha512-4hhkiX8/gus+6e53PLCUmUrsa6ZWGgJW2XCW6O0ASvZUiezIK900ZicinTDtG3kAO2kon7oUA/ReWmpW2FByxg==",
+ "dev": true
+ },
+ "markdown-it-video": {
+ "version": "0.6.3",
+ "resolved": "https://registry.npmjs.org/markdown-it-video/-/markdown-it-video-0.6.3.tgz",
+ "integrity": "sha512-T4th1kwy0OcvyWSN4u3rqPGxvbDclpucnVSSaH3ZacbGsAts964dxokx9s/I3GYsrDCJs4ogtEeEeVP18DQj0Q==",
+ "dev": true
+ },
+ "matcher-collection": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/matcher-collection/-/matcher-collection-2.0.1.tgz",
+ "integrity": "sha512-daE62nS2ZQsDg9raM0IlZzLmI2u+7ZapXBwdoeBUKAYERPDDIc0qNqA8E0Rp2D+gspKR7BgIFP52GeujaGXWeQ==",
+ "dev": true,
+ "requires": {
+ "@types/minimatch": "^3.0.3",
+ "minimatch": "^3.0.2"
+ }
+ },
+ "material-icons": {
+ "version": "1.13.11",
+ "resolved": "https://registry.npmjs.org/material-icons/-/material-icons-1.13.11.tgz",
+ "integrity": "sha512-kp2oAdaqo/Zp6hpTZW01rOgDPWmxBUszSdDzkRm1idCjjNvdUMnqu8qu58cll6CObo+o0cydOiPLdoSugLm+mQ==",
+ "dev": true
+ },
+ "mdurl": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/mdurl/-/mdurl-1.0.1.tgz",
+ "integrity": "sha512-/sKlQJCBYVY9Ers9hqzKou4H6V5UWc/M59TH2dvkt+84itfnq7uFOMLpOiOS4ujvHP4etln18fmIxA5R5fll0g==",
+ "dev": true
+ },
+ "micromatch": {
+ "version": "3.1.10",
+ "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz",
+ "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==",
+ "dev": true,
+ "requires": {
+ "arr-diff": "^4.0.0",
+ "array-unique": "^0.3.2",
+ "braces": "^2.3.1",
+ "define-property": "^2.0.2",
+ "extend-shallow": "^3.0.2",
+ "extglob": "^2.0.4",
+ "fragment-cache": "^0.2.1",
+ "kind-of": "^6.0.2",
+ "nanomatch": "^1.2.9",
+ "object.pick": "^1.3.0",
+ "regex-not": "^1.0.0",
+ "snapdragon": "^0.8.1",
+ "to-regex": "^3.0.2"
+ },
+ "dependencies": {
+ "braces": {
+ "version": "2.3.2",
+ "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz",
+ "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==",
+ "dev": true,
+ "requires": {
+ "arr-flatten": "^1.1.0",
+ "array-unique": "^0.3.2",
+ "extend-shallow": "^2.0.1",
+ "fill-range": "^4.0.0",
+ "isobject": "^3.0.1",
+ "repeat-element": "^1.1.2",
+ "snapdragon": "^0.8.1",
+ "snapdragon-node": "^2.0.1",
+ "split-string": "^3.0.2",
+ "to-regex": "^3.0.1"
+ },
+ "dependencies": {
+ "extend-shallow": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
+ "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==",
+ "dev": true,
+ "requires": {
+ "is-extendable": "^0.1.0"
+ }
+ }
+ }
+ },
+ "fill-range": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz",
+ "integrity": "sha512-VcpLTWqWDiTerugjj8e3+esbg+skS3M9e54UuR3iCeIDMXCLTsAH8hTSzDQU/X6/6t3eYkOKoZSef2PlU6U1XQ==",
+ "dev": true,
+ "requires": {
+ "extend-shallow": "^2.0.1",
+ "is-number": "^3.0.0",
+ "repeat-string": "^1.6.1",
+ "to-regex-range": "^2.1.0"
+ },
+ "dependencies": {
+ "extend-shallow": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
+ "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==",
+ "dev": true,
+ "requires": {
+ "is-extendable": "^0.1.0"
+ }
+ }
+ }
+ },
+ "is-extendable": {
+ "version": "0.1.1",
+ "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz",
+ "integrity": "sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw==",
+ "dev": true
+ },
+ "is-number": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz",
+ "integrity": "sha512-4cboCqIpliH+mAvFNegjZQ4kgKc3ZUhQVr3HvWbSh5q3WH2v82ct+T2Y1hdU5Gdtorx/cLifQjqCbL7bpznLTg==",
+ "dev": true,
+ "requires": {
+ "kind-of": "^3.0.2"
+ },
+ "dependencies": {
+ "kind-of": {
+ "version": "3.2.2",
+ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
+ "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==",
+ "dev": true,
+ "requires": {
+ "is-buffer": "^1.1.5"
+ }
+ }
+ }
+ },
+ "to-regex-range": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz",
+ "integrity": "sha512-ZZWNfCjUokXXDGXFpZehJIkZqq91BcULFq/Pi7M5i4JnxXdhMKAK682z8bCW3o8Hj1wuuzoKcW3DfVzaP6VuNg==",
+ "dev": true,
+ "requires": {
+ "is-number": "^3.0.0",
+ "repeat-string": "^1.6.1"
+ }
+ }
+ }
+ },
+ "mime": {
+ "version": "1.6.0",
+ "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz",
+ "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==",
+ "dev": true
+ },
+ "mime-db": {
+ "version": "1.52.0",
+ "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
+ "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
+ "dev": true
+ },
+ "mime-types": {
+ "version": "2.1.35",
+ "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
+ "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
+ "dev": true,
+ "requires": {
+ "mime-db": "1.52.0"
+ }
+ },
+ "minimatch": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
+ "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
+ "dev": true,
+ "requires": {
+ "brace-expansion": "^1.1.7"
+ }
+ },
+ "mixin-deep": {
+ "version": "1.3.2",
+ "resolved": "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.3.2.tgz",
+ "integrity": "sha512-WRoDn//mXBiJ1H40rqa3vH0toePwSsGb45iInWlTySa+Uu4k3tYUSxa2v1KqAiLtvlrSzaExqS1gtk96A9zvEA==",
+ "dev": true,
+ "requires": {
+ "for-in": "^1.0.2",
+ "is-extendable": "^1.0.1"
+ }
+ },
+ "moment": {
+ "version": "2.29.4",
+ "resolved": "https://registry.npmjs.org/moment/-/moment-2.29.4.tgz",
+ "integrity": "sha512-5LC9SOxjSc2HF6vO2CyuTDNivEdoz2IvyJJGj6X8DJ0eFyfszE0QiEd+iXmBvUP3WHxSjFH/vIsA0EN00cgr8w==",
+ "dev": true
+ },
+ "morgan": {
+ "version": "1.10.0",
+ "resolved": "https://registry.npmjs.org/morgan/-/morgan-1.10.0.tgz",
+ "integrity": "sha512-AbegBVI4sh6El+1gNwvD5YIck7nSA36weD7xvIxG4in80j/UoK8AEGaWnnz8v1GxonMCltmlNs5ZKbGvl9b1XQ==",
+ "dev": true,
+ "requires": {
+ "basic-auth": "~2.0.1",
+ "debug": "2.6.9",
+ "depd": "~2.0.0",
+ "on-finished": "~2.3.0",
+ "on-headers": "~1.0.2"
+ }
+ },
+ "ms": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
+ "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==",
+ "dev": true
+ },
+ "nan": {
+ "version": "2.16.0",
+ "resolved": "https://registry.npmjs.org/nan/-/nan-2.16.0.tgz",
+ "integrity": "sha512-UdAqHyFngu7TfQKsCBgAA6pWDkT8MAO7d0jyOecVhN5354xbLqdn8mV9Tat9gepAupm0bt2DbeaSC8vS52MuFA==",
+ "dev": true,
+ "optional": true
+ },
+ "nanomatch": {
+ "version": "1.2.13",
+ "resolved": "https://registry.npmjs.org/nanomatch/-/nanomatch-1.2.13.tgz",
+ "integrity": "sha512-fpoe2T0RbHwBTBUOftAfBPaDEi06ufaUai0mE6Yn1kacc3SnTErfb/h+X94VXzI64rKFHYImXSvdwGGCmwOqCA==",
+ "dev": true,
+ "requires": {
+ "arr-diff": "^4.0.0",
+ "array-unique": "^0.3.2",
+ "define-property": "^2.0.2",
+ "extend-shallow": "^3.0.2",
+ "fragment-cache": "^0.2.1",
+ "is-windows": "^1.0.2",
+ "kind-of": "^6.0.2",
+ "object.pick": "^1.3.0",
+ "regex-not": "^1.0.0",
+ "snapdragon": "^0.8.1",
+ "to-regex": "^3.0.1"
+ }
+ },
+ "negotiator": {
+ "version": "0.6.3",
+ "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz",
+ "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==",
+ "dev": true
+ },
+ "nopt": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/nopt/-/nopt-5.0.0.tgz",
+ "integrity": "sha512-Tbj67rffqceeLpcRXrT7vKAN8CwfPeIBgM7E6iBkmKLV7bEMwpGgYLGv0jACUsECaa/vuxP0IjEont6umdMgtQ==",
+ "dev": true,
+ "requires": {
+ "abbrev": "1"
+ }
+ },
+ "normalize-path": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz",
+ "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==",
+ "dev": true
+ },
+ "normalize-url": {
+ "version": "1.9.1",
+ "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-1.9.1.tgz",
+ "integrity": "sha512-A48My/mtCklowHBlI8Fq2jFWK4tX4lJ5E6ytFsSOq1fzpvT0SQSgKhSg7lN5c2uYFOrUAOQp6zhhJnpp1eMloQ==",
+ "dev": true,
+ "requires": {
+ "object-assign": "^4.0.1",
+ "prepend-http": "^1.0.0",
+ "query-string": "^4.1.0",
+ "sort-keys": "^1.0.0"
+ }
+ },
+ "nth-check": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-1.0.2.tgz",
+ "integrity": "sha512-WeBOdju8SnzPN5vTUJYxYUxLeXpCaVP5i5e0LF8fg7WORF2Wd7wFX/pk0tYZk7s8T+J7VLy0Da6J1+wCT0AtHg==",
+ "dev": true,
+ "requires": {
+ "boolbase": "~1.0.0"
+ }
+ },
+ "nunjucks": {
+ "version": "3.2.2",
+ "resolved": "https://registry.npmjs.org/nunjucks/-/nunjucks-3.2.2.tgz",
+ "integrity": "sha512-KUi85OoF2NMygwODAy28Lh9qHmq5hO3rBlbkYoC8v377h4l8Pt5qFjILl0LWpMbOrZ18CzfVVUvIHUIrtED3sA==",
+ "dev": true,
+ "requires": {
+ "a-sync-waterfall": "^1.0.0",
+ "asap": "^2.0.3",
+ "chokidar": "^3.3.0",
+ "commander": "^5.1.0"
+ },
+ "dependencies": {
+ "commander": {
+ "version": "5.1.0",
+ "resolved": "https://registry.npmjs.org/commander/-/commander-5.1.0.tgz",
+ "integrity": "sha512-P0CysNDQ7rtVw4QIQtm+MRxV66vKFSvlsQvGYXZWR3qFU0jlMKHZZZgw8e+8DSah4UDKMqnknRDQz+xuQXQ/Zg==",
+ "dev": true
+ }
+ }
+ },
+ "object-assign": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
+ "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==",
+ "dev": true
+ },
+ "object-copy": {
+ "version": "0.1.0",
+ "resolved": "https://registry.npmjs.org/object-copy/-/object-copy-0.1.0.tgz",
+ "integrity": "sha512-79LYn6VAb63zgtmAteVOWo9Vdj71ZVBy3Pbse+VqxDpEP83XuujMrGqHIwAXJ5I/aM0zU7dIyIAhifVTPrNItQ==",
+ "dev": true,
+ "requires": {
+ "copy-descriptor": "^0.1.0",
+ "define-property": "^0.2.5",
+ "kind-of": "^3.0.3"
+ },
+ "dependencies": {
+ "define-property": {
+ "version": "0.2.5",
+ "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz",
+ "integrity": "sha512-Rr7ADjQZenceVOAKop6ALkkRAmH1A4Gx9hV/7ZujPUN2rkATqFO0JZLZInbAjpZYoJ1gUx8MRMQVkYemcbMSTA==",
+ "dev": true,
+ "requires": {
+ "is-descriptor": "^0.1.0"
+ }
+ },
+ "is-accessor-descriptor": {
+ "version": "0.1.6",
+ "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz",
+ "integrity": "sha512-e1BM1qnDbMRG3ll2U9dSK0UMHuWOs3pY3AtcFsmvwPtKL3MML/Q86i+GilLfvqEs4GW+ExB91tQ3Ig9noDIZ+A==",
+ "dev": true,
+ "requires": {
+ "kind-of": "^3.0.2"
+ }
+ },
+ "is-data-descriptor": {
+ "version": "0.1.4",
+ "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz",
+ "integrity": "sha512-+w9D5ulSoBNlmw9OHn3U2v51SyoCd0he+bB3xMl62oijhrspxowjU+AIcDY0N3iEJbUEkB15IlMASQsxYigvXg==",
+ "dev": true,
+ "requires": {
+ "kind-of": "^3.0.2"
+ }
+ },
+ "is-descriptor": {
+ "version": "0.1.6",
+ "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz",
+ "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==",
+ "dev": true,
+ "requires": {
+ "is-accessor-descriptor": "^0.1.6",
+ "is-data-descriptor": "^0.1.4",
+ "kind-of": "^5.0.0"
+ },
+ "dependencies": {
+ "kind-of": {
+ "version": "5.1.0",
+ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz",
+ "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==",
+ "dev": true
+ }
+ }
+ },
+ "kind-of": {
+ "version": "3.2.2",
+ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
+ "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==",
+ "dev": true,
+ "requires": {
+ "is-buffer": "^1.1.5"
+ }
+ }
+ }
+ },
+ "object-hash": {
+ "version": "1.3.1",
+ "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-1.3.1.tgz",
+ "integrity": "sha512-OSuu/pU4ENM9kmREg0BdNrUDIl1heYa4mBZacJc+vVWz4GtAwu7jO8s4AIt2aGRUTqxykpWzI3Oqnsm13tTMDA==",
+ "dev": true
+ },
+ "object-visit": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/object-visit/-/object-visit-1.0.1.tgz",
+ "integrity": "sha512-GBaMwwAVK9qbQN3Scdo0OyvgPW7l3lnaVMj84uTOZlswkX0KpF6fyDBJhtTthf7pymztoN36/KEr1DyhF96zEA==",
+ "dev": true,
+ "requires": {
+ "isobject": "^3.0.0"
+ }
+ },
+ "object.pick": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/object.pick/-/object.pick-1.3.0.tgz",
+ "integrity": "sha512-tqa/UMy/CCoYmj+H5qc07qvSL9dqcs/WZENZ1JbtWBlATP+iVOe778gE6MSijnyCnORzDuX6hU+LA4SZ09YjFQ==",
+ "dev": true,
+ "requires": {
+ "isobject": "^3.0.1"
+ }
+ },
+ "on-finished": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz",
+ "integrity": "sha512-ikqdkGAAyf/X/gPhXGvfgAytDZtDbr+bkNUJ0N9h5MI/dmdgCs3l6hoHrcUv41sRKew3jIwrp4qQDXiK99Utww==",
+ "dev": true,
+ "requires": {
+ "ee-first": "1.1.1"
+ }
+ },
+ "on-headers": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz",
+ "integrity": "sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==",
+ "dev": true
+ },
+ "once": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
+ "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==",
+ "dev": true,
+ "requires": {
+ "wrappy": "1"
+ }
+ },
+ "opn": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/opn/-/opn-6.0.0.tgz",
+ "integrity": "sha512-I9PKfIZC+e4RXZ/qr1RhgyCnGgYX0UEIlXgWnCOVACIvFgaC9rz6Won7xbdhoHrd8IIhV7YEpHjreNUNkqCGkQ==",
+ "dev": true,
+ "requires": {
+ "is-wsl": "^1.1.0"
+ }
+ },
+ "p-limit": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz",
+ "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==",
+ "dev": true,
+ "requires": {
+ "p-try": "^2.0.0"
+ }
+ },
+ "p-locate": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz",
+ "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==",
+ "dev": true,
+ "requires": {
+ "p-limit": "^2.2.0"
+ }
+ },
+ "p-try": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz",
+ "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==",
+ "dev": true
+ },
+ "parseurl": {
+ "version": "1.3.3",
+ "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz",
+ "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==",
+ "dev": true
+ },
+ "pascalcase": {
+ "version": "0.1.1",
+ "resolved": "https://registry.npmjs.org/pascalcase/-/pascalcase-0.1.1.tgz",
+ "integrity": "sha512-XHXfu/yOQRy9vYOtUDVMN60OEJjW013GoObG1o+xwQTpB9eYJX/BjXMsdW13ZDPruFhYYn0AG22w0xgQMwl3Nw==",
+ "dev": true
+ },
+ "path-dirname": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/path-dirname/-/path-dirname-1.0.2.tgz",
+ "integrity": "sha512-ALzNPpyNq9AqXMBjeymIjFDAkAFH06mHJH/cSBHAgU0s4vfpBn6b2nf8tiRLvagKD8RbTpq2FKTBg7cl9l3c7Q==",
+ "dev": true
+ },
+ "path-exists": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz",
+ "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==",
+ "dev": true
+ },
+ "path-is-absolute": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
+ "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==",
+ "dev": true
+ },
+ "path-is-inside": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/path-is-inside/-/path-is-inside-1.0.2.tgz",
+ "integrity": "sha512-DUWJr3+ULp4zXmol/SZkFf3JGsS9/SIv+Y3Rt93/UjPpDpklB5f1er4O3POIbUuUJ3FXgqte2Q7SrU6zAqwk8w==",
+ "dev": true
+ },
+ "path-parse": {
+ "version": "1.0.7",
+ "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz",
+ "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==",
+ "dev": true
+ },
+ "pause-stream": {
+ "version": "0.0.11",
+ "resolved": "https://registry.npmjs.org/pause-stream/-/pause-stream-0.0.11.tgz",
+ "integrity": "sha512-e3FBlXLmN/D1S+zHzanP4E/4Z60oFAa3O051qt1pxa7DEJWKAyil6upYVXCWadEnuoqa4Pkc9oUx9zsxYeRv8A==",
+ "dev": true,
+ "requires": {
+ "through": "~2.3"
+ }
+ },
+ "picomatch": {
+ "version": "2.3.1",
+ "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz",
+ "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==",
+ "dev": true
+ },
+ "pify": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz",
+ "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==",
+ "dev": true
+ },
+ "pinkie": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz",
+ "integrity": "sha512-MnUuEycAemtSaeFSjXKW/aroV7akBbY+Sv+RkyqFjgAe73F+MR0TBWKBRDkmfWq/HiFmdavfZ1G7h4SPZXaCSg==",
+ "dev": true
+ },
+ "pinkie-promise": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz",
+ "integrity": "sha512-0Gni6D4UcLTbv9c57DfxDGdr41XfgUjqWZu492f0cIGr16zDU06BWP/RAEvOuo7CQ0CNjHaLlM59YJJFm3NWlw==",
+ "dev": true,
+ "requires": {
+ "pinkie": "^2.0.0"
+ }
+ },
+ "posix-character-classes": {
+ "version": "0.1.1",
+ "resolved": "https://registry.npmjs.org/posix-character-classes/-/posix-character-classes-0.1.1.tgz",
+ "integrity": "sha512-xTgYBc3fuo7Yt7JbiuFxSYGToMoz8fLoE6TC9Wx1P/u+LfeThMOAqmuyECnlBaaJb+u1m9hHiXUEtwW4OzfUJg==",
+ "dev": true
+ },
+ "prepend-http": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/prepend-http/-/prepend-http-1.0.4.tgz",
+ "integrity": "sha512-PhmXi5XmoyKw1Un4E+opM2KcsJInDvKyuOumcjjw3waw86ZNjHwVUOOWLc4bCzLdcKNaWBH9e99sbWzDQsVaYg==",
+ "dev": true
+ },
+ "process-nextick-args": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz",
+ "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==",
+ "dev": true
+ },
+ "proto-list": {
+ "version": "1.2.4",
+ "resolved": "https://registry.npmjs.org/proto-list/-/proto-list-1.2.4.tgz",
+ "integrity": "sha512-vtK/94akxsTMhe0/cbfpR+syPuszcuwhqVjJq26CuNDgFGj682oRBXOP5MJpv2r7JtE8MsiepGIqvvOTBwn2vA==",
+ "dev": true
+ },
+ "proxy-middleware": {
+ "version": "0.15.0",
+ "resolved": "https://registry.npmjs.org/proxy-middleware/-/proxy-middleware-0.15.0.tgz",
+ "integrity": "sha512-EGCG8SeoIRVMhsqHQUdDigB2i7qU7fCsWASwn54+nPutYO8n4q6EiwMzyfWlC+dzRFExP+kvcnDFdBDHoZBU7Q==",
+ "dev": true
+ },
+ "pseudomap": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz",
+ "integrity": "sha512-b/YwNhb8lk1Zz2+bXXpS/LK9OisiZZ1SNsSLxN1x2OXVEhW2Ckr/7mWE5vrC1ZTiJlD9g19jWszTmJsB+oEpFQ==",
+ "dev": true
+ },
+ "query-string": {
+ "version": "4.3.4",
+ "resolved": "https://registry.npmjs.org/query-string/-/query-string-4.3.4.tgz",
+ "integrity": "sha512-O2XLNDBIg1DnTOa+2XrIwSiXEV8h2KImXUnjhhn2+UsvZ+Es2uyd5CCRTNQlDGbzUQOW3aYCBx9rVA6dzsiY7Q==",
+ "dev": true,
+ "requires": {
+ "object-assign": "^4.1.0",
+ "strict-uri-encode": "^1.0.0"
+ }
+ },
+ "querystringify": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz",
+ "integrity": "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==",
+ "dev": true
+ },
+ "randombytes": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz",
+ "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==",
+ "dev": true,
+ "requires": {
+ "safe-buffer": "^5.1.0"
+ }
+ },
+ "range-parser": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz",
+ "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==",
+ "dev": true
+ },
+ "readable-stream": {
+ "version": "3.6.0",
+ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz",
+ "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==",
+ "dev": true,
+ "requires": {
+ "inherits": "^2.0.3",
+ "string_decoder": "^1.1.1",
+ "util-deprecate": "^1.0.1"
+ }
+ },
+ "readdirp": {
+ "version": "3.6.0",
+ "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz",
+ "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==",
+ "dev": true,
+ "requires": {
+ "picomatch": "^2.2.1"
+ }
+ },
+ "regex-not": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/regex-not/-/regex-not-1.0.2.tgz",
+ "integrity": "sha512-J6SDjUgDxQj5NusnOtdFxDwN/+HWykR8GELwctJ7mdqhcyy1xEc4SRFHUXvxTp661YaVKAjfRLZ9cCqS6tn32A==",
+ "dev": true,
+ "requires": {
+ "extend-shallow": "^3.0.2",
+ "safe-regex": "^1.1.0"
+ }
+ },
+ "remove-trailing-separator": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz",
+ "integrity": "sha512-/hS+Y0u3aOfIETiaiirUFwDBDzmXPvO+jAfKTitUngIPzdKc6Z0LoFjM/CK5PL4C+eKwHohlHAb6H0VFfmmUsw==",
+ "dev": true
+ },
+ "repeat-element": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/repeat-element/-/repeat-element-1.1.4.tgz",
+ "integrity": "sha512-LFiNfRcSu7KK3evMyYOuCzv3L10TW7yC1G2/+StMjK8Y6Vqd2MG7r/Qjw4ghtuCOjFvlnms/iMmLqpvW/ES/WQ==",
+ "dev": true
+ },
+ "repeat-string": {
+ "version": "1.6.1",
+ "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz",
+ "integrity": "sha512-PV0dzCYDNfRi1jCDbJzpW7jNNDRuCOG/jI5ctQcGKt/clZD+YcPS3yIlWuTJMmESC8aevCFmWJy5wjAFgNqN6w==",
+ "dev": true
+ },
+ "requires-port": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz",
+ "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==",
+ "dev": true
+ },
+ "resolve": {
+ "version": "1.22.4",
+ "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.4.tgz",
+ "integrity": "sha512-PXNdCiPqDqeUou+w1C2eTQbNfxKSuMxqTCuvlmmMsk1NWHL5fRrhY6Pl0qEYYc6+QqGClco1Qj8XnjPego4wfg==",
+ "dev": true,
+ "requires": {
+ "is-core-module": "^2.13.0",
+ "path-parse": "^1.0.7",
+ "supports-preserve-symlinks-flag": "^1.0.0"
+ }
+ },
+ "resolve-url": {
+ "version": "0.2.1",
+ "resolved": "https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz",
+ "integrity": "sha512-ZuF55hVUQaaczgOIwqWzkEcEidmlD/xl44x1UZnhOXcYuFN2S6+rcxpG+C1N3So0wvNI3DmJICUFfu2SxhBmvg==",
+ "dev": true
+ },
+ "ret": {
+ "version": "0.1.15",
+ "resolved": "https://registry.npmjs.org/ret/-/ret-0.1.15.tgz",
+ "integrity": "sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==",
+ "dev": true
+ },
+ "safe-buffer": {
+ "version": "5.1.2",
+ "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
+ "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==",
+ "dev": true
+ },
+ "safe-regex": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/safe-regex/-/safe-regex-1.1.0.tgz",
+ "integrity": "sha512-aJXcif4xnaNUzvUuC5gcb46oTS7zvg4jpMTnuqtrEPlR3vFr4pxtdTwaF1Qs3Enjn9HK+ZlwQui+a7z0SywIzg==",
+ "dev": true,
+ "requires": {
+ "ret": "~0.1.10"
+ }
+ },
+ "safe-stable-stringify": {
+ "version": "2.3.1",
+ "resolved": "https://registry.npmjs.org/safe-stable-stringify/-/safe-stable-stringify-2.3.1.tgz",
+ "integrity": "sha512-kYBSfT+troD9cDA85VDnHZ1rpHC50O0g1e6WlGHVCz/g+JS+9WKLj+XwFYyR8UbrZN8ll9HUpDAAddY58MGisg==",
+ "dev": true
+ },
+ "semver": {
+ "version": "5.7.2",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz",
+ "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==",
+ "dev": true
+ },
+ "send": {
+ "version": "0.18.0",
+ "resolved": "https://registry.npmjs.org/send/-/send-0.18.0.tgz",
+ "integrity": "sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==",
+ "dev": true,
+ "requires": {
+ "debug": "2.6.9",
+ "depd": "2.0.0",
+ "destroy": "1.2.0",
+ "encodeurl": "~1.0.2",
+ "escape-html": "~1.0.3",
+ "etag": "~1.8.1",
+ "fresh": "0.5.2",
+ "http-errors": "2.0.0",
+ "mime": "1.6.0",
+ "ms": "2.1.3",
+ "on-finished": "2.4.1",
+ "range-parser": "~1.2.1",
+ "statuses": "2.0.1"
+ },
+ "dependencies": {
+ "ms": {
+ "version": "2.1.3",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
+ "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
+ "dev": true
+ },
+ "on-finished": {
+ "version": "2.4.1",
+ "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz",
+ "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==",
+ "dev": true,
+ "requires": {
+ "ee-first": "1.1.1"
+ }
+ },
+ "statuses": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz",
+ "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==",
+ "dev": true
+ }
+ }
+ },
+ "serialize-javascript": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-3.1.0.tgz",
+ "integrity": "sha512-JIJT1DGiWmIKhzRsG91aS6Ze4sFUrYbltlkg2onR5OrnNM02Kl/hnY/T4FN2omvyeBbQmMJv+K4cPOpGzOTFBg==",
+ "dev": true,
+ "requires": {
+ "randombytes": "^2.1.0"
+ }
+ },
+ "serve-index": {
+ "version": "1.9.1",
+ "resolved": "https://registry.npmjs.org/serve-index/-/serve-index-1.9.1.tgz",
+ "integrity": "sha512-pXHfKNP4qujrtteMrSBb0rc8HJ9Ms/GrXwcUtUtD5s4ewDJI8bT3Cz2zTVRMKtri49pLx2e0Ya8ziP5Ya2pZZw==",
+ "dev": true,
+ "requires": {
+ "accepts": "~1.3.4",
+ "batch": "0.6.1",
+ "debug": "2.6.9",
+ "escape-html": "~1.0.3",
+ "http-errors": "~1.6.2",
+ "mime-types": "~2.1.17",
+ "parseurl": "~1.3.2"
+ },
+ "dependencies": {
+ "depd": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz",
+ "integrity": "sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ==",
+ "dev": true
+ },
+ "http-errors": {
+ "version": "1.6.3",
+ "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz",
+ "integrity": "sha512-lks+lVC8dgGyh97jxvxeYTWQFvh4uw4yC12gVl63Cg30sjPX4wuGcdkICVXDAESr6OJGjqGA8Iz5mkeN6zlD7A==",
+ "dev": true,
+ "requires": {
+ "depd": "~1.1.2",
+ "inherits": "2.0.3",
+ "setprototypeof": "1.1.0",
+ "statuses": ">= 1.4.0 < 2"
+ }
+ },
+ "inherits": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz",
+ "integrity": "sha512-x00IRNXNy63jwGkJmzPigoySHbaqpNuzKbBOmzK+g2OdZpQ9w+sxCN+VSB3ja7IAge2OP2qpfxTjeNcyjmW1uw==",
+ "dev": true
+ },
+ "setprototypeof": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz",
+ "integrity": "sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ==",
+ "dev": true
+ }
+ }
+ },
+ "set-value": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/set-value/-/set-value-2.0.1.tgz",
+ "integrity": "sha512-JxHc1weCN68wRY0fhCoXpyK55m/XPHafOmK4UWD7m2CI14GMcFypt4w/0+NV5f/ZMby2F6S2wwA7fgynh9gWSw==",
+ "dev": true,
+ "requires": {
+ "extend-shallow": "^2.0.1",
+ "is-extendable": "^0.1.1",
+ "is-plain-object": "^2.0.3",
+ "split-string": "^3.0.1"
+ },
+ "dependencies": {
+ "extend-shallow": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
+ "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==",
+ "dev": true,
+ "requires": {
+ "is-extendable": "^0.1.0"
+ }
+ },
+ "is-extendable": {
+ "version": "0.1.1",
+ "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz",
+ "integrity": "sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw==",
+ "dev": true
+ }
+ }
+ },
+ "setprototypeof": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz",
+ "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==",
+ "dev": true
+ },
+ "sigmund": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/sigmund/-/sigmund-1.0.1.tgz",
+ "integrity": "sha512-fCvEXfh6NWpm+YSuY2bpXb/VIihqWA6hLsgboC+0nl71Q7N7o2eaCW8mJa/NLvQhs6jpd3VZV4UiUQlV6+lc8g==",
+ "dev": true
+ },
+ "simple-git": {
+ "version": "2.48.0",
+ "resolved": "https://registry.npmjs.org/simple-git/-/simple-git-2.48.0.tgz",
+ "integrity": "sha512-z4qtrRuaAFJS4PUd0g+xy7aN4y+RvEt/QTJpR184lhJguBA1S/LsVlvE/CM95RsYMOFJG3NGGDjqFCzKU19S/A==",
+ "dev": true,
+ "requires": {
+ "@kwsites/file-exists": "^1.1.1",
+ "@kwsites/promise-deferred": "^1.1.1",
+ "debug": "^4.3.2"
+ },
+ "dependencies": {
+ "debug": {
+ "version": "4.3.4",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz",
+ "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==",
+ "dev": true,
+ "requires": {
+ "ms": "2.1.2"
+ }
+ },
+ "ms": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
+ "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==",
+ "dev": true
+ }
+ }
+ },
+ "snapdragon": {
+ "version": "0.8.2",
+ "resolved": "https://registry.npmjs.org/snapdragon/-/snapdragon-0.8.2.tgz",
+ "integrity": "sha512-FtyOnWN/wCHTVXOMwvSv26d+ko5vWlIDD6zoUJ7LW8vh+ZBC8QdljveRP+crNrtBwioEUWy/4dMtbBjA4ioNlg==",
+ "dev": true,
+ "requires": {
+ "base": "^0.11.1",
+ "debug": "^2.2.0",
+ "define-property": "^0.2.5",
+ "extend-shallow": "^2.0.1",
+ "map-cache": "^0.2.2",
+ "source-map": "^0.5.6",
+ "source-map-resolve": "^0.5.0",
+ "use": "^3.1.0"
+ },
+ "dependencies": {
+ "define-property": {
+ "version": "0.2.5",
+ "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz",
+ "integrity": "sha512-Rr7ADjQZenceVOAKop6ALkkRAmH1A4Gx9hV/7ZujPUN2rkATqFO0JZLZInbAjpZYoJ1gUx8MRMQVkYemcbMSTA==",
+ "dev": true,
+ "requires": {
+ "is-descriptor": "^0.1.0"
+ }
+ },
+ "extend-shallow": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
+ "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==",
+ "dev": true,
+ "requires": {
+ "is-extendable": "^0.1.0"
+ }
+ },
+ "is-accessor-descriptor": {
+ "version": "0.1.6",
+ "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz",
+ "integrity": "sha512-e1BM1qnDbMRG3ll2U9dSK0UMHuWOs3pY3AtcFsmvwPtKL3MML/Q86i+GilLfvqEs4GW+ExB91tQ3Ig9noDIZ+A==",
+ "dev": true,
+ "requires": {
+ "kind-of": "^3.0.2"
+ },
+ "dependencies": {
+ "kind-of": {
+ "version": "3.2.2",
+ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
+ "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==",
+ "dev": true,
+ "requires": {
+ "is-buffer": "^1.1.5"
+ }
+ }
+ }
+ },
+ "is-data-descriptor": {
+ "version": "0.1.4",
+ "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz",
+ "integrity": "sha512-+w9D5ulSoBNlmw9OHn3U2v51SyoCd0he+bB3xMl62oijhrspxowjU+AIcDY0N3iEJbUEkB15IlMASQsxYigvXg==",
+ "dev": true,
+ "requires": {
+ "kind-of": "^3.0.2"
+ },
+ "dependencies": {
+ "kind-of": {
+ "version": "3.2.2",
+ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
+ "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==",
+ "dev": true,
+ "requires": {
+ "is-buffer": "^1.1.5"
+ }
+ }
+ }
+ },
+ "is-descriptor": {
+ "version": "0.1.6",
+ "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz",
+ "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==",
+ "dev": true,
+ "requires": {
+ "is-accessor-descriptor": "^0.1.6",
+ "is-data-descriptor": "^0.1.4",
+ "kind-of": "^5.0.0"
+ }
+ },
+ "is-extendable": {
+ "version": "0.1.1",
+ "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz",
+ "integrity": "sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw==",
+ "dev": true
+ },
+ "kind-of": {
+ "version": "5.1.0",
+ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz",
+ "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==",
+ "dev": true
+ }
+ }
+ },
+ "snapdragon-node": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/snapdragon-node/-/snapdragon-node-2.1.1.tgz",
+ "integrity": "sha512-O27l4xaMYt/RSQ5TR3vpWCAB5Kb/czIcqUFOM/C4fYcLnbZUc1PkjTAMjof2pBWaSTwOUd6qUHcFGVGj7aIwnw==",
+ "dev": true,
+ "requires": {
+ "define-property": "^1.0.0",
+ "isobject": "^3.0.0",
+ "snapdragon-util": "^3.0.1"
+ },
+ "dependencies": {
+ "define-property": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz",
+ "integrity": "sha512-cZTYKFWspt9jZsMscWo8sc/5lbPC9Q0N5nBLgb+Yd915iL3udB1uFgS3B8YCx66UVHq018DAVFoee7x+gxggeA==",
+ "dev": true,
+ "requires": {
+ "is-descriptor": "^1.0.0"
+ }
+ }
+ }
+ },
+ "snapdragon-util": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/snapdragon-util/-/snapdragon-util-3.0.1.tgz",
+ "integrity": "sha512-mbKkMdQKsjX4BAL4bRYTj21edOf8cN7XHdYUJEe+Zn99hVEYcMvKPct1IqNe7+AZPirn8BCDOQBHQZknqmKlZQ==",
+ "dev": true,
+ "requires": {
+ "kind-of": "^3.2.0"
+ },
+ "dependencies": {
+ "kind-of": {
+ "version": "3.2.2",
+ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
+ "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==",
+ "dev": true,
+ "requires": {
+ "is-buffer": "^1.1.5"
+ }
+ }
+ }
+ },
+ "sort-keys": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/sort-keys/-/sort-keys-1.1.2.tgz",
+ "integrity": "sha512-vzn8aSqKgytVik0iwdBEi+zevbTYZogewTUM6dtpmGwEcdzbub/TX4bCzRhebDCRC3QzXgJsLRKB2V/Oof7HXg==",
+ "dev": true,
+ "requires": {
+ "is-plain-obj": "^1.0.0"
+ }
+ },
+ "source-map": {
+ "version": "0.5.6",
+ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.6.tgz",
+ "integrity": "sha512-MjZkVp0NHr5+TPihLcadqnlVoGIoWo4IBHptutGh9wI3ttUYvCG26HkSuDi+K6lsZ25syXJXcctwgyVCt//xqA==",
+ "dev": true
+ },
+ "source-map-resolve": {
+ "version": "0.5.3",
+ "resolved": "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.5.3.tgz",
+ "integrity": "sha512-Htz+RnsXWk5+P2slx5Jh3Q66vhQj1Cllm0zvnaY98+NFx+Dv2CF/f5O/t8x+KaNdrdIAsruNzoh/KpialbqAnw==",
+ "dev": true,
+ "requires": {
+ "atob": "^2.1.2",
+ "decode-uri-component": "^0.2.0",
+ "resolve-url": "^0.2.1",
+ "source-map-url": "^0.4.0",
+ "urix": "^0.1.0"
+ }
+ },
+ "source-map-url": {
+ "version": "0.4.1",
+ "resolved": "https://registry.npmjs.org/source-map-url/-/source-map-url-0.4.1.tgz",
+ "integrity": "sha512-cPiFOTLUKvJFIg4SKVScy4ilPPW6rFgMgfuZJPNoDuMs3nC1HbMUycBoJw77xFIp6z1UJQJOfx6C9GMH80DiTw==",
+ "dev": true
+ },
+ "split": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/split/-/split-1.0.1.tgz",
+ "integrity": "sha512-mTyOoPbrivtXnwnIxZRFYRrPNtEFKlpB2fvjSnCQUiAA6qAZzqwna5envK4uk6OIeP17CsdF3rSBGYVBsU0Tkg==",
+ "dev": true,
+ "requires": {
+ "through": "2"
+ }
+ },
+ "split-string": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/split-string/-/split-string-3.1.0.tgz",
+ "integrity": "sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw==",
+ "dev": true,
+ "requires": {
+ "extend-shallow": "^3.0.0"
+ }
+ },
+ "sprintf-js": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz",
+ "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==",
+ "dev": true
+ },
+ "stack-trace": {
+ "version": "0.0.10",
+ "resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.10.tgz",
+ "integrity": "sha512-KGzahc7puUKkzyMt+IqAep+TVNbKP+k2Lmwhub39m1AsTSkaDutx56aDCo+HLDzf/D26BIHTJWNiTG1KAJiQCg==",
+ "dev": true
+ },
+ "static-extend": {
+ "version": "0.1.2",
+ "resolved": "https://registry.npmjs.org/static-extend/-/static-extend-0.1.2.tgz",
+ "integrity": "sha512-72E9+uLc27Mt718pMHt9VMNiAL4LMsmDbBva8mxWUCkT07fSzEGMYUCk0XWY6lp0j6RBAG4cJ3mWuZv2OE3s0g==",
+ "dev": true,
+ "requires": {
+ "define-property": "^0.2.5",
+ "object-copy": "^0.1.0"
+ },
+ "dependencies": {
+ "define-property": {
+ "version": "0.2.5",
+ "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz",
+ "integrity": "sha512-Rr7ADjQZenceVOAKop6ALkkRAmH1A4Gx9hV/7ZujPUN2rkATqFO0JZLZInbAjpZYoJ1gUx8MRMQVkYemcbMSTA==",
+ "dev": true,
+ "requires": {
+ "is-descriptor": "^0.1.0"
+ }
+ },
+ "is-accessor-descriptor": {
+ "version": "0.1.6",
+ "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz",
+ "integrity": "sha512-e1BM1qnDbMRG3ll2U9dSK0UMHuWOs3pY3AtcFsmvwPtKL3MML/Q86i+GilLfvqEs4GW+ExB91tQ3Ig9noDIZ+A==",
+ "dev": true,
+ "requires": {
+ "kind-of": "^3.0.2"
+ },
+ "dependencies": {
+ "kind-of": {
+ "version": "3.2.2",
+ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
+ "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==",
+ "dev": true,
+ "requires": {
+ "is-buffer": "^1.1.5"
+ }
+ }
+ }
+ },
+ "is-data-descriptor": {
+ "version": "0.1.4",
+ "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz",
+ "integrity": "sha512-+w9D5ulSoBNlmw9OHn3U2v51SyoCd0he+bB3xMl62oijhrspxowjU+AIcDY0N3iEJbUEkB15IlMASQsxYigvXg==",
+ "dev": true,
+ "requires": {
+ "kind-of": "^3.0.2"
+ },
+ "dependencies": {
+ "kind-of": {
+ "version": "3.2.2",
+ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
+ "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==",
+ "dev": true,
+ "requires": {
+ "is-buffer": "^1.1.5"
+ }
+ }
+ }
+ },
+ "is-descriptor": {
+ "version": "0.1.6",
+ "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz",
+ "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==",
+ "dev": true,
+ "requires": {
+ "is-accessor-descriptor": "^0.1.6",
+ "is-data-descriptor": "^0.1.4",
+ "kind-of": "^5.0.0"
+ }
+ },
+ "kind-of": {
+ "version": "5.1.0",
+ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz",
+ "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==",
+ "dev": true
+ }
+ }
+ },
+ "statuses": {
+ "version": "1.5.0",
+ "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz",
+ "integrity": "sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA==",
+ "dev": true
+ },
+ "stream-combiner": {
+ "version": "0.2.2",
+ "resolved": "https://registry.npmjs.org/stream-combiner/-/stream-combiner-0.2.2.tgz",
+ "integrity": "sha512-6yHMqgLYDzQDcAkL+tjJDC5nSNuNIx0vZtRZeiPh7Saef7VHX9H5Ijn9l2VIol2zaNYlYEX6KyuT/237A58qEQ==",
+ "dev": true,
+ "requires": {
+ "duplexer": "~0.1.1",
+ "through": "~2.3.4"
+ }
+ },
+ "strict-uri-encode": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/strict-uri-encode/-/strict-uri-encode-1.1.0.tgz",
+ "integrity": "sha512-R3f198pcvnB+5IpnBlRkphuE9n46WyVl8I39W/ZUTZLz4nqSP/oLYUrcnJrw462Ds8he4YKMov2efsTIw1BDGQ==",
+ "dev": true
+ },
+ "string_decoder": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz",
+ "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==",
+ "dev": true,
+ "requires": {
+ "safe-buffer": "~5.2.0"
+ },
+ "dependencies": {
+ "safe-buffer": {
+ "version": "5.2.1",
+ "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
+ "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==",
+ "dev": true
+ }
+ }
+ },
+ "strip-ansi": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
+ "integrity": "sha512-VhumSSbBqDTP8p2ZLKj40UjBCV4+v8bUSEpUb4KjRgWk9pbqGF4REFj6KEagidb2f/M6AzC0EmFyDNGaw9OCzg==",
+ "dev": true,
+ "requires": {
+ "ansi-regex": "^2.0.0"
+ }
+ },
+ "strip-outer": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/strip-outer/-/strip-outer-1.0.1.tgz",
+ "integrity": "sha512-k55yxKHwaXnpYGsOzg4Vl8+tDrWylxDEpknGjhTiZB8dFRU5rTo9CAzeycivxV3s+zlTKwrs6WxMxR95n26kwg==",
+ "dev": true,
+ "requires": {
+ "escape-string-regexp": "^1.0.2"
+ }
+ },
+ "strip-url-auth": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/strip-url-auth/-/strip-url-auth-1.0.1.tgz",
+ "integrity": "sha512-++41PnXftlL3pvI6lpvhSEO+89g1kIJC4MYB5E6yH+WHa5InIqz51yGd1YOGd7VNSNdoEOfzTMqbAM/2PbgaHQ==",
+ "dev": true
+ },
+ "supports-color": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
+ "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
+ "dev": true,
+ "requires": {
+ "has-flag": "^4.0.0"
+ }
+ },
+ "supports-preserve-symlinks-flag": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz",
+ "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==",
+ "dev": true
+ },
+ "through": {
+ "version": "2.3.8",
+ "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz",
+ "integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==",
+ "dev": true
+ },
+ "through2": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/through2/-/through2-3.0.2.tgz",
+ "integrity": "sha512-enaDQ4MUyP2W6ZyT6EsMzqBPZaM/avg8iuo+l2d3QCs0J+6RaqkHV/2/lOwDTueBHeJ/2LG9lrLW3d5rWPucuQ==",
+ "dev": true,
+ "requires": {
+ "inherits": "^2.0.4",
+ "readable-stream": "2 || 3"
+ }
+ },
+ "to-object-path": {
+ "version": "0.3.0",
+ "resolved": "https://registry.npmjs.org/to-object-path/-/to-object-path-0.3.0.tgz",
+ "integrity": "sha512-9mWHdnGRuh3onocaHzukyvCZhzvr6tiflAy/JRFXcJX0TjgfWA9pk9t8CMbzmBE4Jfw58pXbkngtBtqYxzNEyg==",
+ "dev": true,
+ "requires": {
+ "kind-of": "^3.0.2"
+ },
+ "dependencies": {
+ "kind-of": {
+ "version": "3.2.2",
+ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
+ "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==",
+ "dev": true,
+ "requires": {
+ "is-buffer": "^1.1.5"
+ }
+ }
+ }
+ },
+ "to-regex": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/to-regex/-/to-regex-3.0.2.tgz",
+ "integrity": "sha512-FWtleNAtZ/Ki2qtqej2CXTOayOH9bHDQF+Q48VpWyDXjbYxA4Yz8iDB31zXOBUlOHHKidDbqGVrTUvQMPmBGBw==",
+ "dev": true,
+ "requires": {
+ "define-property": "^2.0.2",
+ "extend-shallow": "^3.0.2",
+ "regex-not": "^1.0.2",
+ "safe-regex": "^1.1.0"
+ }
+ },
+ "to-regex-range": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
+ "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
+ "dev": true,
+ "requires": {
+ "is-number": "^7.0.0"
+ }
+ },
+ "toidentifier": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz",
+ "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==",
+ "dev": true
+ },
+ "trim-repeated": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/trim-repeated/-/trim-repeated-1.0.0.tgz",
+ "integrity": "sha512-pkonvlKk8/ZuR0D5tLW8ljt5I8kmxp2XKymhepUeOdCEfKpZaktSArkLHZt76OB1ZvO9bssUsDty4SWhLvZpLg==",
+ "dev": true,
+ "requires": {
+ "escape-string-regexp": "^1.0.2"
+ }
+ },
+ "triple-beam": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/triple-beam/-/triple-beam-1.3.0.tgz",
+ "integrity": "sha512-XrHUvV5HpdLmIj4uVMxHggLbFSZYIn7HEWsqePZcI50pco+MPqJ50wMGY794X7AOOhxOBAjbkqfAbEe/QMp2Lw==",
+ "dev": true
+ },
+ "uc.micro": {
+ "version": "1.0.6",
+ "resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-1.0.6.tgz",
+ "integrity": "sha512-8Y75pvTYkLJW2hWQHXxoqRgV7qb9B+9vFEtidML+7koHUFapnVJAZ6cKs+Qjz5Aw3aZWHMC6u0wJE3At+nSGwA==",
+ "dev": true
+ },
+ "union-value": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/union-value/-/union-value-1.0.1.tgz",
+ "integrity": "sha512-tJfXmxMeWYnczCVs7XAEvIV7ieppALdyepWMkHkwciRpZraG/xwT+s2JN8+pr1+8jCRf80FFzvr+MpQeeoF4Xg==",
+ "dev": true,
+ "requires": {
+ "arr-union": "^3.1.0",
+ "get-value": "^2.0.6",
+ "is-extendable": "^0.1.1",
+ "set-value": "^2.0.1"
+ },
+ "dependencies": {
+ "is-extendable": {
+ "version": "0.1.1",
+ "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz",
+ "integrity": "sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw==",
+ "dev": true
+ }
+ }
+ },
+ "universalify": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz",
+ "integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==",
+ "dev": true
+ },
+ "unix-crypt-td-js": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/unix-crypt-td-js/-/unix-crypt-td-js-1.1.4.tgz",
+ "integrity": "sha512-8rMeVYWSIyccIJscb9NdCfZKSRBKYTeVnwmiRYT2ulE3qd1RaDQ0xQDP+rI3ccIWbhu/zuo5cgN8z73belNZgw==",
+ "dev": true
+ },
+ "unpipe": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz",
+ "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==",
+ "dev": true
+ },
+ "unset-value": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/unset-value/-/unset-value-1.0.0.tgz",
+ "integrity": "sha512-PcA2tsuGSF9cnySLHTLSh2qrQiJ70mn+r+Glzxv2TWZblxsxCC52BDlZoPCsz7STd9pN7EZetkWZBAvk4cgZdQ==",
+ "dev": true,
+ "requires": {
+ "has-value": "^0.3.1",
+ "isobject": "^3.0.0"
+ },
+ "dependencies": {
+ "has-value": {
+ "version": "0.3.1",
+ "resolved": "https://registry.npmjs.org/has-value/-/has-value-0.3.1.tgz",
+ "integrity": "sha512-gpG936j8/MzaeID5Yif+577c17TxaDmhuyVgSwtnL/q8UUTySg8Mecb+8Cf1otgLoD7DDH75axp86ER7LFsf3Q==",
+ "dev": true,
+ "requires": {
+ "get-value": "^2.0.3",
+ "has-values": "^0.1.4",
+ "isobject": "^2.0.0"
+ },
+ "dependencies": {
+ "isobject": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz",
+ "integrity": "sha512-+OUdGJlgjOBZDfxnDjYYG6zp487z0JGNQq3cYQYg5f5hKR+syHMsaztzGeml/4kGG55CSpKSpWTY+jYGgsHLgA==",
+ "dev": true,
+ "requires": {
+ "isarray": "1.0.0"
+ }
+ }
+ }
+ },
+ "has-values": {
+ "version": "0.1.4",
+ "resolved": "https://registry.npmjs.org/has-values/-/has-values-0.1.4.tgz",
+ "integrity": "sha512-J8S0cEdWuQbqD9//tlZxiMuMNmxB8PlEwvYwuxsTmR1G5RXUePEX/SJn7aD0GMLieuZYSwNH0cQuJGwnYunXRQ==",
+ "dev": true
+ }
+ }
+ },
+ "upath": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/upath/-/upath-1.2.0.tgz",
+ "integrity": "sha512-aZwGpamFO61g3OlfT7OQCHqhGnW43ieH9WZeP7QxN/G/jS4jfqUkZxoryvJgVPEcrl5NL/ggHsSmLMHuH64Lhg==",
+ "dev": true
+ },
+ "urix": {
+ "version": "0.1.0",
+ "resolved": "https://registry.npmjs.org/urix/-/urix-0.1.0.tgz",
+ "integrity": "sha512-Am1ousAhSLBeB9cG/7k7r2R0zj50uDRlZHPGbazid5s9rlF1F/QKYObEKSIunSjIOkJZqwRRLpvewjEkM7pSqg==",
+ "dev": true
+ },
+ "url-parse": {
+ "version": "1.5.10",
+ "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.10.tgz",
+ "integrity": "sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==",
+ "dev": true,
+ "requires": {
+ "querystringify": "^2.1.1",
+ "requires-port": "^1.0.0"
+ }
+ },
+ "use": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/use/-/use-3.1.1.tgz",
+ "integrity": "sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ==",
+ "dev": true
+ },
+ "util-deprecate": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
+ "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==",
+ "dev": true
+ },
+ "utils-merge": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz",
+ "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==",
+ "dev": true
+ },
+ "uuid": {
+ "version": "8.3.2",
+ "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz",
+ "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==",
+ "dev": true
+ },
+ "vary": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
+ "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==",
+ "dev": true
+ },
+ "vue": {
+ "version": "2.6.14",
+ "resolved": "https://registry.npmjs.org/vue/-/vue-2.6.14.tgz",
+ "integrity": "sha512-x2284lgYvjOMj3Za7kqzRcUSxBboHqtgRE2zlos1qWaOye5yUmHn42LB1250NJBLRwEcdrB0JRwyPTEPhfQjiQ==",
+ "dev": true
+ },
+ "vue-server-renderer": {
+ "version": "2.6.14",
+ "resolved": "https://registry.npmjs.org/vue-server-renderer/-/vue-server-renderer-2.6.14.tgz",
+ "integrity": "sha512-HifYRa/LW7cKywg9gd4ZtvtRuBlstQBao5ZCWlg40fyB4OPoGfEXAzxb0emSLv4pBDOHYx0UjpqvxpiQFEuoLA==",
+ "dev": true,
+ "requires": {
+ "chalk": "^1.1.3",
+ "hash-sum": "^1.0.2",
+ "he": "^1.1.0",
+ "lodash.template": "^4.5.0",
+ "lodash.uniq": "^4.5.0",
+ "resolve": "^1.2.0",
+ "serialize-javascript": "^3.1.0",
+ "source-map": "0.5.6"
+ },
+ "dependencies": {
+ "ansi-styles": {
+ "version": "2.2.1",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz",
+ "integrity": "sha512-kmCevFghRiWM7HB5zTPULl4r9bVFSWjz62MhqizDGUrq2NWuNMQyuv4tHHoKJHs69M/MF64lEcHdYIocrdWQYA==",
+ "dev": true
+ },
+ "chalk": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz",
+ "integrity": "sha512-U3lRVLMSlsCfjqYPbLyVv11M9CPW4I728d6TCKMAOJueEeB9/8o+eSsMnxPJD+Q+K909sdESg7C+tIkoH6on1A==",
+ "dev": true,
+ "requires": {
+ "ansi-styles": "^2.2.1",
+ "escape-string-regexp": "^1.0.2",
+ "has-ansi": "^2.0.0",
+ "strip-ansi": "^3.0.0",
+ "supports-color": "^2.0.0"
+ }
+ },
+ "supports-color": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz",
+ "integrity": "sha512-KKNVtd6pCYgPIKU4cp2733HWYCpplQhddZLBUryaAHou723x+FRzQ5Df824Fj+IyyuiQTRoub4SnIFfIcrp70g==",
+ "dev": true
+ }
+ }
+ },
+ "vue-template-compiler": {
+ "version": "2.6.14",
+ "resolved": "https://registry.npmjs.org/vue-template-compiler/-/vue-template-compiler-2.6.14.tgz",
+ "integrity": "sha512-ODQS1SyMbjKoO1JBJZojSw6FE4qnh9rIpUZn2EUT86FKizx9uH5z6uXiIrm4/Nb/gwxTi/o17ZDEGWAXHvtC7g==",
+ "dev": true,
+ "requires": {
+ "de-indent": "^1.0.2",
+ "he": "^1.1.0"
+ }
+ },
+ "walk-sync": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/walk-sync/-/walk-sync-2.2.0.tgz",
+ "integrity": "sha512-IC8sL7aB4/ZgFcGI2T1LczZeFWZ06b3zoHH7jBPyHxOtIIz1jppWHjjEXkOFvFojBVAK9pV7g47xOZ4LW3QLfg==",
+ "dev": true,
+ "requires": {
+ "@types/minimatch": "^3.0.3",
+ "ensure-posix-path": "^1.1.0",
+ "matcher-collection": "^2.0.0",
+ "minimatch": "^3.0.4"
+ }
+ },
+ "websocket-driver": {
+ "version": "0.7.4",
+ "resolved": "https://registry.npmjs.org/websocket-driver/-/websocket-driver-0.7.4.tgz",
+ "integrity": "sha512-b17KeDIQVjvb0ssuSDF2cYXSg2iztliJ4B9WdsuB6J952qCPKmnVq4DyW5motImXHDC1cBT/1UezrJVsKw5zjg==",
+ "dev": true,
+ "requires": {
+ "http-parser-js": ">=0.5.1",
+ "safe-buffer": ">=5.1.0",
+ "websocket-extensions": ">=0.1.1"
+ }
+ },
+ "websocket-extensions": {
+ "version": "0.1.4",
+ "resolved": "https://registry.npmjs.org/websocket-extensions/-/websocket-extensions-0.1.4.tgz",
+ "integrity": "sha512-OqedPIGOfsDlo31UNwYbCFMSaO9m9G/0faIHj5/dZFDMFqPTcx6UwqyOy3COEaEOg/9VsGIpdqn62W5KhoKSpg==",
+ "dev": true
+ },
+ "winston": {
+ "version": "2.4.6",
+ "resolved": "https://registry.npmjs.org/winston/-/winston-2.4.6.tgz",
+ "integrity": "sha512-J5Zu4p0tojLde8mIOyDSsmLmcP8I3Z6wtwpTDHx1+hGcdhxcJaAmG4CFtagkb+NiN1M9Ek4b42pzMWqfc9jm8w==",
+ "dev": true,
+ "requires": {
+ "async": "^3.2.3",
+ "colors": "1.0.x",
+ "cycle": "1.0.x",
+ "eyes": "0.1.x",
+ "isstream": "0.1.x",
+ "stack-trace": "0.0.x"
+ },
+ "dependencies": {
+ "async": {
+ "version": "3.2.4",
+ "resolved": "https://registry.npmjs.org/async/-/async-3.2.4.tgz",
+ "integrity": "sha512-iAB+JbDEGXhyIUavoDl9WP/Jj106Kz9DEn1DPgYw5ruDn0e3Wgi3sKFm55sASdGBNOQB8F59d9qQ7deqrHA8wQ==",
+ "dev": true
+ },
+ "colors": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/colors/-/colors-1.0.3.tgz",
+ "integrity": "sha512-pFGrxThWcWQ2MsAz6RtgeWe4NK2kUE1WfsrvvlctdII745EW9I0yflqhe7++M5LEc7bV2c/9/5zc8sFcpL0Drw==",
+ "dev": true
+ }
+ }
+ },
+ "winston-compat": {
+ "version": "0.1.5",
+ "resolved": "https://registry.npmjs.org/winston-compat/-/winston-compat-0.1.5.tgz",
+ "integrity": "sha512-EPvPcHT604AV3Ji6d3+vX8ENKIml9VSxMRnPQ+cuK/FX6f3hvPP2hxyoeeCOCFvDrJEujalfcKWlWPvAnFyS9g==",
+ "dev": true,
+ "requires": {
+ "cycle": "~1.0.3",
+ "logform": "^1.6.0",
+ "triple-beam": "^1.2.0"
+ }
+ },
+ "winston-daily-rotate-file": {
+ "version": "3.10.0",
+ "resolved": "https://registry.npmjs.org/winston-daily-rotate-file/-/winston-daily-rotate-file-3.10.0.tgz",
+ "integrity": "sha512-KO8CfbI2CvdR3PaFApEH02GPXiwJ+vbkF1mCkTlvRIoXFI8EFlf1ACcuaahXTEiDEKCii6cNe95gsL4ZkbnphA==",
+ "dev": true,
+ "requires": {
+ "file-stream-rotator": "^0.4.1",
+ "object-hash": "^1.3.0",
+ "semver": "^6.2.0",
+ "triple-beam": "^1.3.0",
+ "winston-compat": "^0.1.4",
+ "winston-transport": "^4.2.0"
+ },
+ "dependencies": {
+ "semver": {
+ "version": "6.3.0",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
+ "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==",
+ "dev": true
+ }
+ }
+ },
+ "winston-transport": {
+ "version": "4.5.0",
+ "resolved": "https://registry.npmjs.org/winston-transport/-/winston-transport-4.5.0.tgz",
+ "integrity": "sha512-YpZzcUzBedhlTAfJg6vJDlyEai/IFMIVcaEZZyl3UXIl4gmqRpU7AE89AHLkbzLUsv0NVmw7ts+iztqKxxPW1Q==",
+ "dev": true,
+ "requires": {
+ "logform": "^2.3.2",
+ "readable-stream": "^3.6.0",
+ "triple-beam": "^1.3.0"
+ },
+ "dependencies": {
+ "fecha": {
+ "version": "4.2.3",
+ "resolved": "https://registry.npmjs.org/fecha/-/fecha-4.2.3.tgz",
+ "integrity": "sha512-OP2IUU6HeYKJi3i0z4A19kHMQoLVs4Hc+DPqqxI2h/DPZHTm/vjsfC6P0b4jCMy14XizLBqvndQ+UilD7707Jw==",
+ "dev": true
+ },
+ "logform": {
+ "version": "2.4.2",
+ "resolved": "https://registry.npmjs.org/logform/-/logform-2.4.2.tgz",
+ "integrity": "sha512-W4c9himeAwXEdZ05dQNerhFz2XG80P9Oj0loPUMV23VC2it0orMHQhJm4hdnnor3rd1HsGf6a2lPwBM1zeXHGw==",
+ "dev": true,
+ "requires": {
+ "@colors/colors": "1.5.0",
+ "fecha": "^4.2.0",
+ "ms": "^2.1.1",
+ "safe-stable-stringify": "^2.3.1",
+ "triple-beam": "^1.3.0"
+ }
+ },
+ "ms": {
+ "version": "2.1.3",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
+ "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
+ "dev": true
+ }
+ }
+ },
+ "wrappy": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
+ "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==",
+ "dev": true
+ },
+ "yallist": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz",
+ "integrity": "sha512-ncTzHV7NvsQZkYe1DW7cbDLm0YpzHmZF5r/iyP3ZnQtMiJ+pjzisCiMNI+Sj+xQF5pXhSHxSB3uDbsBTzY/c2A==",
+ "dev": true
+ }
+ }
+}
diff --git a/docs/package.json b/docs/package.json
new file mode 100644
index 00000000000..aa7083fd8a7
--- /dev/null
+++ b/docs/package.json
@@ -0,0 +1,14 @@
+{
+ "name": "docs",
+ "version": "1.0.0",
+ "description": "AB-3 docs",
+ "scripts": {
+ "init": "markbind init",
+ "build": "markbind build",
+ "serve": "markbind serve",
+ "deploy": "markbind deploy"
+ },
+ "devDependencies": {
+ "markbind-cli": "^5.1.0"
+ }
+}
diff --git a/docs/site.json b/docs/site.json
new file mode 100644
index 00000000000..80f76177836
--- /dev/null
+++ b/docs/site.json
@@ -0,0 +1,29 @@
+{
+ "baseUrl": "",
+ "titlePrefix": "",
+ "titleSuffix": "HR Insight",
+ "faviconPath": "images/SeEduLogo.png",
+ "style": {
+ "codeTheme": "light"
+ },
+ "ignore": [
+ "_markbind/layouts/*",
+ "_markbind/logs/*",
+ "_site/*",
+ "site.json",
+ "*.md",
+ "*.njk",
+ ".git/*",
+ "node_modules/*"
+ ],
+ "pagesExclude": ["node_modules/*"],
+ "pages": [
+ {
+ "glob": ["**/index.md", "**/*.md"]
+ }
+ ],
+ "deploy": {
+ "message": "Site Update."
+ },
+ "timeZone": "Asia/Singapore"
+}
diff --git a/docs/stylesheets/main.css b/docs/stylesheets/main.css
new file mode 100644
index 00000000000..1074ade42dd
--- /dev/null
+++ b/docs/stylesheets/main.css
@@ -0,0 +1,144 @@
+mark {
+ background-color: #ff0;
+ border-radius: 5px;
+ padding-top: 0;
+ padding-bottom: 0;
+}
+
+.indented {
+ padding-left: 20px;
+}
+
+.theme-card img {
+ width: 100%;
+}
+
+/* Scrollbar */
+
+.slim-scroll::-webkit-scrollbar {
+ width: 5px;
+}
+
+.slim-scroll::-webkit-scrollbar-thumb {
+ background: #808080;
+ border-radius: 20px;
+}
+
+.slim-scroll::-webkit-scrollbar-track {
+ background: transparent;
+ border-radius: 20px;
+}
+
+.slim-scroll-blue::-webkit-scrollbar {
+ width: 5px;
+}
+
+.slim-scroll-blue::-webkit-scrollbar-thumb {
+ background: #00b0ef;
+ border-radius: 20px;
+}
+
+.slim-scroll-blue::-webkit-scrollbar-track {
+ background: transparent;
+ border-radius: 20px;
+}
+
+/* Layout containers */
+
+#flex-body {
+ display: flex;
+ flex: 1;
+ align-items: start;
+}
+
+#content-wrapper {
+ flex: 1;
+ margin: 0 auto;
+ min-width: 0;
+ max-width: 1000px;
+ overflow-x: auto;
+ padding: 0.8rem 20px 0 20px;
+ transition: 0.4s;
+ -webkit-transition: 0.4s;
+}
+
+#site-nav,
+#page-nav {
+ display: flex;
+ flex-direction: column;
+ position: sticky;
+ top: var(--sticky-header-height);
+ flex: 0 0 auto;
+ max-width: 300px;
+ max-height: calc(100vh - var(--sticky-header-height));
+ width: 300px;
+}
+
+#site-nav {
+ border-right: 1px solid lightgrey;
+ padding-bottom: 20px;
+ z-index: 999;
+}
+
+.site-nav-top {
+ margin: 0.8rem 0;
+ padding: 0 12px 12px 12px;
+}
+
+.nav-component {
+ overflow-y: auto;
+}
+
+#page-nav {
+ border-left: 1px solid lightgrey;
+}
+
+@media screen and (max-width: 1299.98px) {
+ #page-nav {
+ display: none;
+ }
+}
+
+/* Bootstrap medium(md) responsive breakpoint */
+@media screen and (max-width: 991.98px) {
+ #site-nav {
+ display: none;
+ }
+}
+
+/* Bootstrap small(sm) responsive breakpoint */
+@media (max-width: 767.98px) {
+ .indented {
+ padding-left: 10px;
+ }
+
+ #content-wrapper {
+ padding: 0 10px;
+ }
+}
+
+/* Bootstrap extra small(xs) responsive breakpoint */
+@media screen and (max-width: 575.98px) {
+ #site-nav {
+ display: none;
+ }
+}
+
+/* Hide site navigation when printing */
+@media print {
+ #site-nav {
+ display: none;
+ }
+
+ #page-nav {
+ display: none;
+ }
+}
+
+h2,
+h3,
+h4,
+h5,
+h6 {
+ color: #e46c0a;
+}
diff --git a/docs/team/johndoe.md b/docs/team/johndoe.md
deleted file mode 100644
index 773a07794e2..00000000000
--- a/docs/team/johndoe.md
+++ /dev/null
@@ -1,46 +0,0 @@
----
-layout: page
-title: John Doe's Project Portfolio Page
----
-
-### Project: AddressBook Level 3
-
-AddressBook - Level 3 is a desktop address book application used for teaching Software Engineering principles. The user interacts with it using a CLI, and it has a GUI created with JavaFX. It is written in Java, and has about 10 kLoC.
-
-Given below are my contributions to the project.
-
-* **New Feature**: Added the ability to undo/redo previous commands.
- * What it does: allows the user to undo all previous commands one at a time. Preceding undo commands can be reversed by using the redo command.
- * Justification: This feature improves the product significantly because a user can make mistakes in commands and the app should provide a convenient way to rectify them.
- * Highlights: This enhancement affects existing commands and commands to be added in future. It required an in-depth analysis of design alternatives. The implementation too was challenging as it required changes to existing commands.
- * Credits: *{mention here if you reused any code/ideas from elsewhere or if a third-party library is heavily used in the feature so that a reader can make a more accurate judgement of how much effort went into the feature}*
-
-* **New Feature**: Added a history command that allows the user to navigate to previous commands using up/down keys.
-
-* **Code contributed**: [RepoSense link]()
-
-* **Project management**:
- * Managed releases `v1.3` - `v1.5rc` (3 releases) on GitHub
-
-* **Enhancements to existing features**:
- * Updated the GUI color scheme (Pull requests [\#33](), [\#34]())
- * Wrote additional tests for existing features to increase coverage from 88% to 92% (Pull requests [\#36](), [\#38]())
-
-* **Documentation**:
- * User Guide:
- * Added documentation for the features `delete` and `find` [\#72]()
- * Did cosmetic tweaks to existing documentation of features `clear`, `exit`: [\#74]()
- * Developer Guide:
- * Added implementation details of the `delete` feature.
-
-* **Community**:
- * PRs reviewed (with non-trivial review comments): [\#12](), [\#32](), [\#19](), [\#42]()
- * Contributed to forum discussions (examples: [1](), [2](), [3](), [4]())
- * Reported bugs and suggestions for other teams in the class (examples: [1](), [2](), [3]())
- * Some parts of the history feature I added was adopted by several other class mates ([1](), [2]())
-
-* **Tools**:
- * Integrated a third party library (Natty) to the project ([\#42]())
- * Integrated a new Github plugin (CircleCI) to the team repo
-
-* _{you can add/remove categories in the list above}_
diff --git a/docs/team/marcellaantania.md b/docs/team/marcellaantania.md
new file mode 100644
index 00000000000..dbb2a5fd4ef
--- /dev/null
+++ b/docs/team/marcellaantania.md
@@ -0,0 +1,44 @@
+---
+layout: page
+title: Marcella Antania Tan's Project Portfolio Page
+---
+
+### Project: HR Insight
+
+HR Insight is a desktop app for HR people, optimized for use via a Line Interface (CLI) while still having the benefits of a Graphical User Interface (GUI). The user interacts with it using a CLI, and it has a GUI created with JavaFX. It is written in Java, and has about 10 kLoC.
+
+Given below are my contributions to the project.
+
+- **New Feature**: Filter by Departments
+ * What it does: allows the user to view the employees under a certain specified department. (PR [#51](https://github.com/AY2324S1-CS2103-F13-2/tp/pull/51), [#68](https://github.com/AY2324S1-CS2103-F13-2/tp/pull/68))
+ * Justification: This feature is a necessary feature for HR admins as there is very likely to be a time when they need to view the employees under a certain department.
+ * Highlights: This feature uses an existing command `list`. When an optional department argument is given, the command will show a filtered list.
+
+- **New Feature**: Filter by Leave (and Department) using the `view_leave` command.
+ * What it does: allows the user to view the employees with registered leaves in any of the optionally specified months and departments. (PR [#73](https://github.com/AY2324S1-CS2103-F13-2/tp/pull/73))
+ * Justification: This feature allows the user to easily view and manipulate the leaves of the employees. When an employee requests for a new leave, the HR admin can view the employees who have leave under the same month in the same department to ensure that there are not too many leave overlaps between the employees in the same department.
+ * Highlights: This feature can be used with or without arguments. Without arguments, the feature will list all the employees who have planned leaves this year. With the month argument present, this feature will show the employees who have leaves in any of the specified months. With the department argument present, this feature will show all the employees from the specified department who have leaves. With both month and department arguments present, this feature will show all employees from the specified department who have leaves in any of the months.
+
+- **New Feature**: Change GUI Theme
+ * What it does: allows the user to change the theme of the GUI to their preference. (PR [#120](https://github.com/AY2324S1-CS2103-F13-2/tp/pull/120))
+ * Justification: This feature is a nice improvement for the product in terms of GUI and catering to user preferences.
+ * Highlights: This feature allows user to change their GUI theme by using the `theme` command. Valid themes are dark, light, red, green and blue. The default GUI theme when the application is launched is a dark theme.
+
+- **Code contributed**: [RepoSense Link](https://nus-cs2103-ay2324s1.github.io/tp-dashboard/?search=marcellaantania&breakdown=true)
+
+- **Project management**:
+ * Attend weekly meetings and provide timely updates on code progress.
+
+- **Enhancements to existing features**:
+ * Increased test coverage for ListCommand-related classes and methods. (PR [#77](https://github.com/AY2324S1-CS2103-F13-2/tp/pull/77))
+
+- **Documentation**:
+ * User Guide:
+ * Added documentation for the features `add_leave` and `theme` (PR [#30](https://github.com/AY2324S1-CS2103-F13-2/tp/pull/30), [#122](https://github.com/AY2324S1-CS2103-F13-2/tp/pull/122))
+ * Developer Guide:
+ * Added implementation details of the `add_leave` and `theme` features with use cases (PR [#37](https://github.com/AY2324S1-CS2103-F13-2/tp/pull/37), [#102](https://github.com/AY2324S1-CS2103-F13-2/tp/pull/102), [#122](https://github.com/AY2324S1-CS2103-F13-2/tp/pull/122)).
+ * Created and added sequence diagrams for `list d/department`, `add_leave` and `theme` commands. (PR [#125](https://github.com/AY2324S1-CS2103-F13-2/tp/pull/125), [#132](https://github.com/AY2324S1-CS2103-F13-2/tp/pull/132))
+
+- **Community**:
+ * PRs reviewed (with non-trivial comments): [#33](https://github.com/AY2324S1-CS2103-F13-2/tp/pull/33) ,[#70](https://github.com/AY2324S1-CS2103-F13-2/tp/pull/70)
+ * Reported critical bugs, fixed some and assigned the rest to the relevant teammates (Issue [#72](https://github.com/AY2324S1-CS2103-F13-2/tp/issues/72), [#86](https://github.com/AY2324S1-CS2103-F13-2/tp/issues/86), [#121](https://github.com/AY2324S1-CS2103-F13-2/tp/issues/121))
diff --git a/docs/team/nixonwidjaja.md b/docs/team/nixonwidjaja.md
new file mode 100644
index 00000000000..36b7da0280e
--- /dev/null
+++ b/docs/team/nixonwidjaja.md
@@ -0,0 +1,41 @@
+---
+layout: page
+title: Nixon's Project Portfolio Page
+---
+
+### Project: HR Insight
+
+HR Insight is a desktop app for HR people, optimized for use via a Command Line Interface (CLI) while still having the benefits of a Graphical User Interface (GUI). The user interacts with it using a CLI, and it has a GUI created with JavaFX. It is written in Java, and has about 10 kLoC.
+
+Given below are my contributions to the project.
+
+- **New Feature**: Undo and redo commands
+ * What it does: Allows users to undo previous commands and reverse the undo commands with the redo command. (PR [#85](https://github.com/AY2324S1-CS2103-F13-2/tp/pull/85))
+ * Justification: This feature improves the app significantly because users can conveniently undo any mistakes in command, so any errors can be fixed quickly.
+ * Highlights: The design to implement undo and redo commands is challenging and fun to think about.
+
+- **New Feature**: Leave command and employee's leave attribute
+ * What it does: Allows employees to have a leave attribute that records which months they are on leave, and allows users to edit those leaves using the leave command. (PR [#62](https://github.com/AY2324S1-CS2103-F13-2/tp/pull/62))
+ * Justification: HR people should be able to view and edit the leave records of all employees in the company.
+
+- **New Feature**: Sort command
+ * What it does: Allows users to sort the employee list based on a given parameter, e.g., sort based on name, sort salary in descending order, etc. (PR [#108](https://github.com/AY2324S1-CS2103-F13-2/tp/pull/108))
+ * Justification: This feature mimics real employee lists or tables that enable HR people to sort based on certain criteria, so they can get more insights from the employee data.
+ * Highlights: I learned a lot from implementing a custom comparator in Java to enable list sorting.
+
+- **New Feature**: Navigate through previous commands using up/down keys
+ * What it does: Allows users to navigate through their previous inputs conveniently using up/down keys on the keyboard. (PR [#126](https://github.com/AY2324S1-CS2103-F13-2/tp/pull/126))
+ * Justification: This feature improves the user experience significantly because it mimics real computer CLI/terminal behavior.
+
+- **Code contributed**: [RepoSense link](https://nus-cs2103-ay2324s1.github.io/tp-dashboard/?search=nixonwidjaja&breakdown=true)
+
+- **Enhancements to existing features**:
+ * Adapted `add`, `edit`, and `list` commands to work based on HR Insight needs. (PR [#26](https://github.com/AY2324S1-CS2103-F13-2/tp/pull/26))
+
+- **Documentation**:
+ * User Guide and Developer Guide: Added documentation for `undo`, `redo`, `leave`, `sort` commands.
+
+- **Community**:
+ * Reviewed PRs and gave non-trivial suggestions to improve code quality. ([#61](https://github.com/AY2324S1-CS2103-F13-2/tp/issues/61), [#74](https://github.com/AY2324S1-CS2103-F13-2/tp/issues/74), [#117](https://github.com/AY2324S1-CS2103-F13-2/tp/pull/117))
+ * Reported and fixed critical bugs. (PR [#75](https://github.com/AY2324S1-CS2103-F13-2/tp/pull/75), [#110](https://github.com/AY2324S1-CS2103-F13-2/tp/pull/110), [#136](https://github.com/AY2324S1-CS2103-F13-2/tp/pull/136))
+ * Set up the organization, repo, and Codecov for the team.
diff --git a/docs/team/remuslum.md b/docs/team/remuslum.md
new file mode 100644
index 00000000000..5d223ea0bcd
--- /dev/null
+++ b/docs/team/remuslum.md
@@ -0,0 +1,37 @@
+---
+layout: page
+title: Remus Lum's Project Portfolio Page
+---
+
+### Project: HR Insight
+
+HR Insight is a desktop address book application used for teaching Software Engineering principles. The user interacts with it using a CLI, and it has a GUI created with JavaFX. It is written in Java, and has about 10 kLoC.
+
+Given below are my contributions to the project.
+
+* **New Feature**: Birthday Command.
+ * What it does: Allows the user to view all employees' birthdays in a given month/months.
+ * Justification: This feature improves the product significantly because a user can plan birthday celebrations ahead of time and prevent the issue of neglecting an employee's birthday, which could affect morale of the employees.
+ * Highlights: This feature is challenging since parsing checks are required to see if the months provided are valid or not. Furthermore, if no month is provided, all birthdays in the present month are shown.
+ * PR: [#78] https://github.com/AY2324S1-CS2103-F13-2/tp/pull/78
+
+* **Code contributed**: [RepoSense link](https://nus-cs2103-ay2324s1.github.io/tp-dashboard/?search=Remus&sort=groupTitle&sortWithin=title&timeframe=commit&mergegroup=&groupSelect=groupByRepos&breakdown=true&checkedFileTypes=docs~functional-code~test-code&since=2023-09-22&tabOpen=true&tabType=authorship&tabAuthor=remuslum&tabRepo=AY2324S1-CS2103-F13-2%2Ftp%5Bmaster%5D&authorshipIsMergeGroup=false&authorshipFileTypes=docs~functional-code~test-code&authorshipIsBinaryFileTypeChecked=false&authorshipIsIgnoredFilesChecked=false)
+
+* **Project management**:
+ * Managed releases `v1.2` - `v1.3` (2 releases) on GitHub
+
+* **Enhancements to existing features**:
+ * Updated email regex to accept domain names that requires at least one dot (Pull Request [\#178](https://github.com/AY2324S1-CS2103-F13-2/tp/pull/178))
+
+* **Documentation**:
+ * User Guide:
+ * Added documentation for the features `birthday` and `view_leave` [\#36](https://github.com/AY2324S1-CS2103-F13-2/tp/pull/36)
+ * Added modified UI of HR Insight: [\#43](https://github.com/AY2324S1-CS2103-F13-2/tp/pull/43)
+ * Updated User Guide with necessary screenshots of features [\#176](https://github.com/AY2324S1-CS2103-F13-2/tp/pull/176)
+ * Developer Guide:
+ * Added implementation details of the `birthday` feature and UML diagram. [\#94](https://github.com/AY2324S1-CS2103-F13-2/tp/pull/94)
+
+* **Community**:
+ * PRs reviewed (with non-trivial review comments): [\#51](https://github.com/AY2324S1-CS2103-F13-2/tp/pull/51), [\#63](https://github.com/AY2324S1-CS2103-F13-2/tp/pull/63), [\#70](https://github.com/AY2324S1-CS2103-F13-2/tp/pull/70), [\#172](https://github.com/AY2324S1-CS2103-F13-2/tp/pull/172)
+
+
diff --git a/docs/team/sheryew.md b/docs/team/sheryew.md
new file mode 100644
index 00000000000..d93757045b2
--- /dev/null
+++ b/docs/team/sheryew.md
@@ -0,0 +1,57 @@
+---
+layout: page
+title: Sher Yew's Project Portfolio Page
+---
+
+### Project: HR Insight
+
+HR Insight is a desktop app for HR people, optimized for use via a Command Line Interface (CLI) while still having the benefits of a Graphical User Interface (GUI). The user interacts with it using a CLI, and it has a GUI created with JavaFX. It is written in Java, and has about 10 kLoC.
+
+Given below are my contributions to the project.
+
+- **New Feature**: Added the ability to manage claims of employees.
+
+ - What it does: Allows users to keep track of remaining claim budget for each employee. Dynamic allocation/subtraction of claims for each employee, allowing latest claim budget to be displayed.
+ - Justification: This feature reduces the effort required for administrative claim processing, since excessive claims will be rejected automatically by the system and mathematical errors are avoided completely.
+ - Highlights: This feature is challenging since parsing checks are required to determine if user wants to allocate/subtract those funds and thereafter, having to produce different success commands.
+ - PR [#63](https://github.com/AY2324S1-CS2103-F13-2/tp/pull/63)
+
+- **New Feature**: Added the ability to view specific attribute for employee(s).
+
+ - What it does: Allows users to view specific attribute for employee(s). Provides faster access to attributes for employee(s) instead of manual scraping the entire list for just one attribute. (PR)
+ - Justification: This feature is beneficial if user just wants to view one attribute for a large number of employees. Reduces time required for manual scraping of just that attribute for the whole list.
+ - Highlights: This feature is challenging as I incorporated multiple index parsing and the effort to ensure every index is captured and checked for validity is time-consuming.
+ - PR [#70](https://github.com/AY2324S1-CS2103-F13-2/tp/pull/70)
+
+- **New Feature**: Added the ability to export Employees' attributes into a CSV file.
+
+ - What it does: Allows users to export all employees' attributes into a CSV file. Additionally, users are able to export a subset of employees by filtering on criteria like department.
+ - Justification: This feature is beneficial as having a CSV file comprising of employees' attributes allow users to incorporate this data into third-party vendors. Allows our application to be used in harmony with others.
+ - Highlights: Learnt Java and its specific utility classes like PrintWriter and streams to make this feature functional.
+ - PR [#88](https://github.com/AY2324S1-CS2103-F13-2/tp/pull/88)
+
+- **New Feature**: Added the ability to reset all employees' leaves.
+
+ - What it does: Allows users to reset existing leaves for all employees.
+ - Justification: This feature is beneficial whenever a new calendar year strikes, since employees leaves are resetted to zero. We do not want previous year leaves to affect existing year leaves.
+ - PR [#117](https://github.com/AY2324S1-CS2103-F13-2/tp/pull/117)
+
+- **Code contributed**: [RepoSense link](https://nus-cs2103-ay2324s1.github.io/tp-dashboard/?search=sheryew&breakdown=true)
+
+- **Project management**:
+
+ - Managed releases `v1.2` - `v1.3rc` (3 releases) on GitHub
+ - Conducted weekly meetings and jotted down meeting minutes on the project page.
+
+- **Documentation**:
+
+ - User Guide:
+ - Added documentation for the features `claim`, `view_attribute`, `export` and `reset_leaves`.
+ - Added UI images (Before & After) for each feature. (PR [#119](https://github.com/AY2324S1-CS2103-F13-2/tp/pull/119))
+ - Developer Guide:
+ - Added implementation details for `claim`, `view_attribute`, `export` and `reset_leaves`.
+ - Added UML diagrams for `export`.
+
+- **Community**:
+
+ - PRs reviewed. (PR [#130](https://github.com/AY2324S1-CS2103-F13-2/tp/pull/130)), (PR [#85](https://github.com/AY2324S1-CS2103-F13-2/tp/pull/85)), (PR [#30](https://github.com/AY2324S1-CS2103-F13-2/tp/pull/30))
diff --git a/src/main/java/seedu/address/logic/LogicManager.java b/src/main/java/seedu/address/logic/LogicManager.java
index 5aa3b91c7d0..7de3c1625a9 100644
--- a/src/main/java/seedu/address/logic/LogicManager.java
+++ b/src/main/java/seedu/address/logic/LogicManager.java
@@ -48,7 +48,7 @@ public CommandResult execute(String commandText) throws CommandException, ParseE
CommandResult commandResult;
Command command = addressBookParser.parseCommand(commandText);
- commandResult = command.execute(model);
+ commandResult = command.execute(model, commandText);
try {
storage.saveAddressBook(model.getAddressBook());
diff --git a/src/main/java/seedu/address/logic/Messages.java b/src/main/java/seedu/address/logic/Messages.java
index ecd32c31b53..a57d19116f8 100644
--- a/src/main/java/seedu/address/logic/Messages.java
+++ b/src/main/java/seedu/address/logic/Messages.java
@@ -14,10 +14,48 @@ public class Messages {
public static final String MESSAGE_UNKNOWN_COMMAND = "Unknown command";
public static final String MESSAGE_INVALID_COMMAND_FORMAT = "Invalid command format! \n%1$s";
- public static final String MESSAGE_INVALID_PERSON_DISPLAYED_INDEX = "The person index provided is invalid";
- public static final String MESSAGE_PERSONS_LISTED_OVERVIEW = "%1$d persons listed!";
- public static final String MESSAGE_DUPLICATE_FIELDS =
- "Multiple values specified for the following single-valued field(s): ";
+ public static final String MESSAGE_INVALID_PERSON_DISPLAYED_INDEX = "The employee index provided is invalid";
+ public static final String MESSAGE_PERSONS_LISTED_OVERVIEW = "%1$d employees listed!";
+ public static final String MESSAGE_DUPLICATE_FIELDS = "Multiple values specified for the "
+ + "following single-valued field(s): ";
+ public static final String MESSAGE_OVER_CLAIM = "Claim is REJECTED as the amount is greater than "
+ + "the funds the Employee currently has!";
+ public static final String MESSAGE_EMPTY_DEPARTMENT_FILTER = "Department name cannot be empty!";
+ public static final String MESSAGE_EMPTY_MONTH_LEAVE_FILTER = "MONTHS cannot be empty!";
+
+ public static final String MESSAGE_LIST_COMMAND_FORMAT = "list: "
+ + "Lists all the details of an organization’s employees, "
+ + "or list all employees of a specified department (case-insensitive).\n"
+ + "Parameters: [d/DEPARTMENT] (optional)\n"
+ + "Example: list (to list all employees) or "
+ + "list d/Engineering (to list all employees in Engineering department)";
+
+ public static final String MESSAGE_VIEW_LIST_COMMAND_FORMAT = "view_list: "
+ + "Lists all employees with planned leave, optionally within "
+ + "specified department or months.\n"
+ + "Parameters: [d/DEPARTMENT] [m/MONTH(S)] (optional)\n"
+ + "Format: MONTHS must be POSITIVE integers separated by commas without spaces. "
+ + "1: Jan, 2: Feb, ..., 12: Dec.\n"
+ + "Example: view_list d/Engineering m/1,5";
+
+ public static final String MESSAGE_MONTHS_SPACES_DETECTED = "Spaces detected in your MONTHS.\n";
+ public static final String MESSAGE_INVALID_MONTH = "Please check your MONTHS. Invalid month provided.\n";
+ public static final String MESSAGE_LIST_SUCCESS = "Listed all employees (%1$d)";
+ public static final String MESSAGE_FILTER_SUCCESS = "Listed filtered employees (%1$d)";
+ public static final String MESSAGE_BIRTHDAY_FAILURE = "No employees have birthdays in this month.";
+ public static final String MESSAGE_VIEW_LEAVE_SUCCESS = "Employees with specified leave(s) listed (%1$d)";
+
+ public static final String MESSAGE_VIEW_LEAVE_NO_EMPLOYEES = "Currently, no employees in the specified department"
+ + " is taking leave in the specified month(s).";
+ public static final String WRONG_EXPORT_FILE_NAME_FAILURE = "Kindly provide one filename for this set of data.\n"
+ + "Example: export engineering_dept";
+ public static final String MESSAGE_NO_ARGUMENTS_EXPECTED = "\"%s\" command should not have any arguments";
+
+ public static final String MESSAGE_INVALID_MONTH_PREFIX = "Invalid prefix provided. Did you mean to type 'm/'?";
+
+ public static final String MESSAGE_INVALID_THEME = "Invalid theme specified.\nValid themes: "
+ + "dark, light, red, green, blue";
+ public static final String TOO_LARGE_A_NUMBER = "Maximum claim budget is $1,000,000,000,000.";
/**
* Returns an error message indicating the duplicate prefixes.
@@ -43,8 +81,14 @@ public static String format(Person person) {
.append(person.getEmail())
.append("; Address: ")
.append(person.getAddress())
- .append("; Tags: ");
- person.getTags().forEach(builder::append);
+ .append("; Salary: ")
+ .append(person.getSalary())
+ .append("; Claim budget: ")
+ .append(person.getClaimBudget())
+ .append("; Department: ")
+ .append(person.getDepartment())
+ .append("; DOB: ")
+ .append(person.getDob());
return builder.toString();
}
diff --git a/src/main/java/seedu/address/logic/commands/AddCommand.java b/src/main/java/seedu/address/logic/commands/AddCommand.java
index 5d7185a9680..05a2ca0440f 100644
--- a/src/main/java/seedu/address/logic/commands/AddCommand.java
+++ b/src/main/java/seedu/address/logic/commands/AddCommand.java
@@ -2,10 +2,13 @@
import static java.util.Objects.requireNonNull;
import static seedu.address.logic.parser.CliSyntax.PREFIX_ADDRESS;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_CLAIM_BUDGET;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_DEPARTMENT;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_DOB;
import static seedu.address.logic.parser.CliSyntax.PREFIX_EMAIL;
import static seedu.address.logic.parser.CliSyntax.PREFIX_NAME;
import static seedu.address.logic.parser.CliSyntax.PREFIX_PHONE;
-import static seedu.address.logic.parser.CliSyntax.PREFIX_TAG;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_SALARY;
import seedu.address.commons.util.ToStringBuilder;
import seedu.address.logic.Messages;
@@ -20,23 +23,28 @@ public class AddCommand extends Command {
public static final String COMMAND_WORD = "add";
- public static final String MESSAGE_USAGE = COMMAND_WORD + ": Adds a person to the address book. "
+ public static final String MESSAGE_USAGE = COMMAND_WORD + ": Adds an employee to the employee list.\n"
+ "Parameters: "
+ PREFIX_NAME + "NAME "
+ PREFIX_PHONE + "PHONE "
+ PREFIX_EMAIL + "EMAIL "
+ PREFIX_ADDRESS + "ADDRESS "
- + "[" + PREFIX_TAG + "TAG]...\n"
+ + PREFIX_SALARY + "SALARY "
+ + PREFIX_CLAIM_BUDGET + "CLAIM BUDGET "
+ + PREFIX_DEPARTMENT + "DEPARTMENT "
+ + PREFIX_DOB + "BIRTHDATE (YYYY-MM-DD)\n"
+ "Example: " + COMMAND_WORD + " "
+ PREFIX_NAME + "John Doe "
+ PREFIX_PHONE + "98765432 "
+ PREFIX_EMAIL + "johnd@example.com "
+ PREFIX_ADDRESS + "311, Clementi Ave 2, #02-25 "
- + PREFIX_TAG + "friends "
- + PREFIX_TAG + "owesMoney";
+ + PREFIX_SALARY + "7000 "
+ + PREFIX_CLAIM_BUDGET + "1500 "
+ + PREFIX_DEPARTMENT + "Engineering "
+ + PREFIX_DOB + "1999-07-23";
- public static final String MESSAGE_SUCCESS = "New person added: %1$s";
- public static final String MESSAGE_DUPLICATE_PERSON = "This person already exists in the address book";
+ public static final String MESSAGE_SUCCESS = "New employee added: %1$s";
+ public static final String MESSAGE_DUPLICATE_PERSON = "The employee already exists in the address book";
private final Person toAdd;
@@ -49,7 +57,7 @@ public AddCommand(Person person) {
}
@Override
- public CommandResult execute(Model model) throws CommandException {
+ public CommandResult execute(Model model, String commandText) throws CommandException {
requireNonNull(model);
if (model.hasPerson(toAdd)) {
@@ -57,6 +65,7 @@ public CommandResult execute(Model model) throws CommandException {
}
model.addPerson(toAdd);
+ model.addCommandText(commandText);
return new CommandResult(String.format(MESSAGE_SUCCESS, Messages.format(toAdd)));
}
diff --git a/src/main/java/seedu/address/logic/commands/BirthdayCommand.java b/src/main/java/seedu/address/logic/commands/BirthdayCommand.java
new file mode 100644
index 00000000000..9fd11e4b671
--- /dev/null
+++ b/src/main/java/seedu/address/logic/commands/BirthdayCommand.java
@@ -0,0 +1,58 @@
+package seedu.address.logic.commands;
+
+import static java.util.Objects.requireNonNull;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_MONTH;
+
+import seedu.address.logic.Messages;
+import seedu.address.model.Model;
+import seedu.address.model.person.MatchingBirthdayPredicate;
+/**
+ * Shows all employees' birthday in the given month/current month/
+ */
+public class BirthdayCommand extends Command {
+ public static final String COMMAND_WORD = "birthday";
+ public static final String MESSAGE_USAGE = COMMAND_WORD
+ + ": Gives a list of all employees who have birthdays in the given month.\n"
+ + "Parameters (Optional): "
+ + PREFIX_MONTH + "MONTH";
+ public static final String MESSAGE_SUCCESS = Messages.MESSAGE_LIST_SUCCESS + " in the month of ";
+ public static final String MESSAGE_FAILURE = Messages.MESSAGE_BIRTHDAY_FAILURE;
+ private final MatchingBirthdayPredicate predicate;
+
+ /**
+ * Creates a BirthdayCommand to show all employees whose birthday fall in the given month
+ * @param predicate a predicate that is used for comparison
+ */
+ public BirthdayCommand(MatchingBirthdayPredicate predicate) {
+ this.predicate = predicate;
+ }
+
+ public MatchingBirthdayPredicate getPredicate() {
+ return this.predicate;
+ }
+
+ @Override
+ public CommandResult execute(Model model, String commandText) {
+ requireNonNull(model);
+ model.updateFilteredPersonList(predicate);
+ if (model.getFilteredPersonList().isEmpty()) {
+ return new CommandResult(MESSAGE_FAILURE);
+ }
+
+ return new CommandResult(String.format(MESSAGE_SUCCESS, model.getFilteredPersonList().size())
+ + predicate.toString());
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (other == this) {
+ return true;
+ }
+ // instanceof handles nulls
+ if (!(other instanceof BirthdayCommand)) {
+ return false;
+ }
+ BirthdayCommand otherCommand = (BirthdayCommand) other;
+ return predicate.equals(otherCommand.getPredicate());
+ }
+}
diff --git a/src/main/java/seedu/address/logic/commands/ClaimCommand.java b/src/main/java/seedu/address/logic/commands/ClaimCommand.java
new file mode 100644
index 00000000000..61b0dec8075
--- /dev/null
+++ b/src/main/java/seedu/address/logic/commands/ClaimCommand.java
@@ -0,0 +1,128 @@
+package seedu.address.logic.commands;
+
+import static java.util.Objects.requireNonNull;
+import static seedu.address.model.Model.PREDICATE_SHOW_ALL_PERSONS;
+
+import java.util.List;
+
+import seedu.address.commons.core.index.Index;
+import seedu.address.logic.Messages;
+import seedu.address.logic.commands.exceptions.CommandException;
+import seedu.address.model.Model;
+import seedu.address.model.person.Money;
+import seedu.address.model.person.Person;
+
+/**
+ * Performs claims for each employee and alters respective claim budgets.
+ */
+public class ClaimCommand extends Command {
+
+ public static final String COMMAND_WORD = "claim";
+ public static final String MESSAGE_EMPTY = "Kindly state the index and amount that the employee hopes "
+ + "to process!\n" + "Parameters: INDEX (must be positive integer) $/CLAIM_AMOUNT.\n"
+ + "Format: Positive CLAIM_AMOUNT indicates addition and negative indicates subtraction.\n"
+ + "Example: claim 1 $/+500";
+ public static final String AMOUNT_EMPTY = "Kindly state the amount that the employee hopes to process!";
+ public static final String CLAIM_SUCCESS = "Claim has been successfully processed!\n";
+ public static final String ALLOCATE_SUCCESS = "Allocation has been successfully processed!\n";
+
+ private final Index index;
+ private final Boolean isSubtract;
+ private final long amount;
+
+ /**
+ * Initialises a ClaimCommand Objects with three variables being index, isSubtract and amount.
+ *
+ * @param index Index Object representing the position of individual within the list of employees.
+ * @param isSubtract Boolean Object where True represents deduction and False represents addition.
+ * @param amount Long Object representing the claim amount user is submitting.
+ */
+ public ClaimCommand(Index index, Boolean isSubtract, long amount) {
+ this.index = index;
+ this.isSubtract = isSubtract;
+ this.amount = amount;
+ }
+
+ /**
+ * Returns CommandResult Object after successfully updating user's claim budget.
+ * Updating the Person Object in the list.
+ *
+ * @param model {@code Model} which the command should operate on.
+ * @return CommandResult which highlights the new claim budget the individual has.
+ * @throws CommandException Exception thrown if index input from HR is beyond the pre-existing max list index.
+ */
+ @Override
+ public CommandResult execute(Model model, String commandText) throws CommandException {
+ requireNonNull(model);
+ List lastShownList = model.getFilteredPersonList();
+
+ if (index.getZeroBased() >= lastShownList.size()) {
+ throw new CommandException(Messages.MESSAGE_INVALID_PERSON_DISPLAYED_INDEX);
+ }
+
+ Person personToEdit = lastShownList.get(index.getZeroBased());
+ long prevClaimBudget = Long.parseLong(personToEdit.getClaimBudget().amount);
+ Money claimBudget = calculateNewClaimBudget(prevClaimBudget);
+ Person editedPerson = postClaimPerson(personToEdit, claimBudget);
+
+ model.setPerson(personToEdit, editedPerson);
+ model.addCommandText(commandText);
+ model.updateFilteredPersonList(PREDICATE_SHOW_ALL_PERSONS);
+
+ String claimCommandPairing = isSubtract ? CLAIM_SUCCESS : ALLOCATE_SUCCESS;
+ return new CommandResult(String.format("%sRemaining claim budget %s has: %s",
+ claimCommandPairing, editedPerson.getName(), claimBudget));
+ }
+
+ /**
+ * Returns a Money Object which represents the new amount the user has after the claiming process is completed.
+ * The maximum final claim budget remaining for each employee is capped at $1000000000000.
+ *
+ * @param prevClaimBudget long object on user's claim budget before the claim process.
+ * @return Money Object that highlights the new claim budget the user has.
+ * @throws CommandException Exception if the subtracted claim amount is more the user's claim budget.
+ */
+ public Money calculateNewClaimBudget(long prevClaimBudget) throws CommandException {
+ if (this.isSubtract && (this.amount > prevClaimBudget)) {
+ throw new CommandException(Messages.MESSAGE_OVER_CLAIM);
+ }
+ long newClaimBudget;
+ if (this.isSubtract) {
+ newClaimBudget = prevClaimBudget - this.amount;
+ } else {
+ newClaimBudget = prevClaimBudget + this.amount;
+ }
+ if (newClaimBudget > Math.pow(10, 12)) {
+ throw new CommandException(Messages.TOO_LARGE_A_NUMBER);
+ }
+ return new Money(String.valueOf(newClaimBudget));
+ }
+
+ /**
+ * Returns a Person object which contains the new claim budget.
+ * Other variables of the person remains unchanged.
+ *
+ * @param personToEdit Person object (Old).
+ * @param claimBudget Money object which reflects the new claim budget user has.
+ * @return Person Object that contains the new claim budget whilst other variables remain unchanged.
+ */
+ public Person postClaimPerson(Person personToEdit, Money claimBudget) {
+ return new Person(personToEdit.getName(), personToEdit.getPhone(), personToEdit.getEmail(),
+ personToEdit.getAddress(), personToEdit.getSalary(), claimBudget,
+ personToEdit.getDepartment(), personToEdit.getDob(), personToEdit.getLeave());
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (this == other) {
+ return true; // Both are the same instance
+ }
+ if (other == null || getClass() != other.getClass()) {
+ return false; // Other is not an instance of ClaimCommand
+ }
+ ClaimCommand that = (ClaimCommand) other;
+ return index.equals(that.index)
+ && isSubtract.equals(that.isSubtract)
+ && amount == that.amount;
+ }
+}
diff --git a/src/main/java/seedu/address/logic/commands/ClearCommand.java b/src/main/java/seedu/address/logic/commands/ClearCommand.java
index 9c86b1fa6e4..d5b55a8bf57 100644
--- a/src/main/java/seedu/address/logic/commands/ClearCommand.java
+++ b/src/main/java/seedu/address/logic/commands/ClearCommand.java
@@ -11,13 +11,14 @@
public class ClearCommand extends Command {
public static final String COMMAND_WORD = "clear";
- public static final String MESSAGE_SUCCESS = "Address book has been cleared!";
+ public static final String MESSAGE_SUCCESS = "Employee list has been cleared!";
@Override
- public CommandResult execute(Model model) {
+ public CommandResult execute(Model model, String commandText) {
requireNonNull(model);
model.setAddressBook(new AddressBook());
+ model.addCommandText(commandText);
return new CommandResult(MESSAGE_SUCCESS);
}
}
diff --git a/src/main/java/seedu/address/logic/commands/Command.java b/src/main/java/seedu/address/logic/commands/Command.java
index 64f18992160..abbda43748b 100644
--- a/src/main/java/seedu/address/logic/commands/Command.java
+++ b/src/main/java/seedu/address/logic/commands/Command.java
@@ -15,6 +15,6 @@ public abstract class Command {
* @return feedback message of the operation result for display
* @throws CommandException If an error occurs during command execution.
*/
- public abstract CommandResult execute(Model model) throws CommandException;
+ public abstract CommandResult execute(Model model, String commandText) throws CommandException;
}
diff --git a/src/main/java/seedu/address/logic/commands/CommandResult.java b/src/main/java/seedu/address/logic/commands/CommandResult.java
index 249b6072d0d..9951be89a06 100644
--- a/src/main/java/seedu/address/logic/commands/CommandResult.java
+++ b/src/main/java/seedu/address/logic/commands/CommandResult.java
@@ -19,13 +19,21 @@ public class CommandResult {
/** The application should exit. */
private final boolean exit;
+ /** The application should change theme. */
+ private final boolean changeTheme;
+
+ private final String themeStylesheet;
+
/**
* Constructs a {@code CommandResult} with the specified fields.
*/
- public CommandResult(String feedbackToUser, boolean showHelp, boolean exit) {
+ public CommandResult(String feedbackToUser, boolean showHelp, boolean exit,
+ boolean changeTheme, String themeStylesheet) {
this.feedbackToUser = requireNonNull(feedbackToUser);
this.showHelp = showHelp;
this.exit = exit;
+ this.changeTheme = changeTheme;
+ this.themeStylesheet = themeStylesheet;
}
/**
@@ -33,7 +41,7 @@ public CommandResult(String feedbackToUser, boolean showHelp, boolean exit) {
* and other fields set to their default value.
*/
public CommandResult(String feedbackToUser) {
- this(feedbackToUser, false, false);
+ this(feedbackToUser, false, false, false, "");
}
public String getFeedbackToUser() {
@@ -48,6 +56,14 @@ public boolean isExit() {
return exit;
}
+ public boolean isChangeTheme() {
+ return changeTheme;
+ }
+
+ public String getThemeStylesheet() {
+ return themeStylesheet;
+ }
+
@Override
public boolean equals(Object other) {
if (other == this) {
@@ -78,5 +94,4 @@ public String toString() {
.add("exit", exit)
.toString();
}
-
}
diff --git a/src/main/java/seedu/address/logic/commands/DeleteCommand.java b/src/main/java/seedu/address/logic/commands/DeleteCommand.java
index 1135ac19b74..340bf7bbcc4 100644
--- a/src/main/java/seedu/address/logic/commands/DeleteCommand.java
+++ b/src/main/java/seedu/address/logic/commands/DeleteCommand.java
@@ -19,11 +19,11 @@ public class DeleteCommand extends Command {
public static final String COMMAND_WORD = "delete";
public static final String MESSAGE_USAGE = COMMAND_WORD
- + ": Deletes the person identified by the index number used in the displayed person list.\n"
+ + ": Deletes the employee identified by the index number used in the displayed employee list.\n"
+ "Parameters: INDEX (must be a positive integer)\n"
+ "Example: " + COMMAND_WORD + " 1";
- public static final String MESSAGE_DELETE_PERSON_SUCCESS = "Deleted Person: %1$s";
+ public static final String MESSAGE_DELETE_PERSON_SUCCESS = "Employee successfully deleted: %1$s";
private final Index targetIndex;
@@ -32,7 +32,7 @@ public DeleteCommand(Index targetIndex) {
}
@Override
- public CommandResult execute(Model model) throws CommandException {
+ public CommandResult execute(Model model, String commandText) throws CommandException {
requireNonNull(model);
List lastShownList = model.getFilteredPersonList();
@@ -42,7 +42,8 @@ public CommandResult execute(Model model) throws CommandException {
Person personToDelete = lastShownList.get(targetIndex.getZeroBased());
model.deletePerson(personToDelete);
- return new CommandResult(String.format(MESSAGE_DELETE_PERSON_SUCCESS, Messages.format(personToDelete)));
+ model.addCommandText(commandText);
+ return new CommandResult(String.format(MESSAGE_DELETE_PERSON_SUCCESS, personToDelete.getName()));
}
@Override
diff --git a/src/main/java/seedu/address/logic/commands/EditCommand.java b/src/main/java/seedu/address/logic/commands/EditCommand.java
index 4b581c7331e..dbce673379b 100644
--- a/src/main/java/seedu/address/logic/commands/EditCommand.java
+++ b/src/main/java/seedu/address/logic/commands/EditCommand.java
@@ -2,18 +2,17 @@
import static java.util.Objects.requireNonNull;
import static seedu.address.logic.parser.CliSyntax.PREFIX_ADDRESS;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_DEPARTMENT;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_DOB;
import static seedu.address.logic.parser.CliSyntax.PREFIX_EMAIL;
import static seedu.address.logic.parser.CliSyntax.PREFIX_NAME;
import static seedu.address.logic.parser.CliSyntax.PREFIX_PHONE;
-import static seedu.address.logic.parser.CliSyntax.PREFIX_TAG;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_SALARY;
import static seedu.address.model.Model.PREDICATE_SHOW_ALL_PERSONS;
-import java.util.Collections;
-import java.util.HashSet;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
-import java.util.Set;
import seedu.address.commons.core.index.Index;
import seedu.address.commons.util.CollectionUtil;
@@ -22,11 +21,14 @@
import seedu.address.logic.commands.exceptions.CommandException;
import seedu.address.model.Model;
import seedu.address.model.person.Address;
+import seedu.address.model.person.Birthday;
+import seedu.address.model.person.Department;
import seedu.address.model.person.Email;
+import seedu.address.model.person.Leave;
+import seedu.address.model.person.Money;
import seedu.address.model.person.Name;
import seedu.address.model.person.Person;
import seedu.address.model.person.Phone;
-import seedu.address.model.tag.Tag;
/**
* Edits the details of an existing person in the address book.
@@ -35,22 +37,24 @@ public class EditCommand extends Command {
public static final String COMMAND_WORD = "edit";
- public static final String MESSAGE_USAGE = COMMAND_WORD + ": Edits the details of the person identified "
- + "by the index number used in the displayed person list. "
+ public static final String MESSAGE_USAGE = COMMAND_WORD + ": Edits the details of the employee identified "
+ + "by the index number used in the displayed employee list. "
+ "Existing values will be overwritten by the input values.\n"
+ "Parameters: INDEX (must be a positive integer) "
+ "[" + PREFIX_NAME + "NAME] "
+ "[" + PREFIX_PHONE + "PHONE] "
+ "[" + PREFIX_EMAIL + "EMAIL] "
+ "[" + PREFIX_ADDRESS + "ADDRESS] "
- + "[" + PREFIX_TAG + "TAG]...\n"
+ + "[" + PREFIX_SALARY + "SALARY] "
+ + "[" + PREFIX_DEPARTMENT + "DEPARTMENT] "
+ + "[" + PREFIX_DOB + "BIRTHDATE (YYYY-MM-DD)]\n"
+ "Example: " + COMMAND_WORD + " 1 "
+ PREFIX_PHONE + "91234567 "
+ PREFIX_EMAIL + "johndoe@example.com";
- public static final String MESSAGE_EDIT_PERSON_SUCCESS = "Edited Person: %1$s";
- public static final String MESSAGE_NOT_EDITED = "At least one field to edit must be provided.";
- public static final String MESSAGE_DUPLICATE_PERSON = "This person already exists in the address book.";
+ public static final String MESSAGE_EDIT_PERSON_SUCCESS = "Edited employee: %1$s";
+ public static final String MESSAGE_NOT_EDITED = "At least one field to edit must be provided.\n" + MESSAGE_USAGE;
+ public static final String MESSAGE_DUPLICATE_PERSON = "The employee already exists in the employee list.";
private final Index index;
private final EditPersonDescriptor editPersonDescriptor;
@@ -68,7 +72,7 @@ public EditCommand(Index index, EditPersonDescriptor editPersonDescriptor) {
}
@Override
- public CommandResult execute(Model model) throws CommandException {
+ public CommandResult execute(Model model, String commandText) throws CommandException {
requireNonNull(model);
List lastShownList = model.getFilteredPersonList();
@@ -84,6 +88,7 @@ public CommandResult execute(Model model) throws CommandException {
}
model.setPerson(personToEdit, editedPerson);
+ model.addCommandText(commandText);
model.updateFilteredPersonList(PREDICATE_SHOW_ALL_PERSONS);
return new CommandResult(String.format(MESSAGE_EDIT_PERSON_SUCCESS, Messages.format(editedPerson)));
}
@@ -99,9 +104,14 @@ private static Person createEditedPerson(Person personToEdit, EditPersonDescript
Phone updatedPhone = editPersonDescriptor.getPhone().orElse(personToEdit.getPhone());
Email updatedEmail = editPersonDescriptor.getEmail().orElse(personToEdit.getEmail());
Address updatedAddress = editPersonDescriptor.getAddress().orElse(personToEdit.getAddress());
- Set updatedTags = editPersonDescriptor.getTags().orElse(personToEdit.getTags());
-
- return new Person(updatedName, updatedPhone, updatedEmail, updatedAddress, updatedTags);
+ Money updatedSalary = editPersonDescriptor.getSalary().orElse(personToEdit.getSalary());
+ Money updatedClaimBudget = editPersonDescriptor.getClaimBudget().orElse(personToEdit.getClaimBudget());
+ Department updatedDepartment = editPersonDescriptor.getDepartment().orElse(personToEdit.getDepartment());
+ Birthday updateDob = editPersonDescriptor.getDob().orElse(personToEdit.getDob());
+ Leave leave = personToEdit.getLeave();
+
+ return new Person(updatedName, updatedPhone, updatedEmail, updatedAddress,
+ updatedSalary, updatedClaimBudget, updatedDepartment, updateDob, leave);
}
@Override
@@ -137,7 +147,10 @@ public static class EditPersonDescriptor {
private Phone phone;
private Email email;
private Address address;
- private Set tags;
+ private Money salary;
+ private Money claimBudget;
+ private Department department;
+ private Birthday dob;
public EditPersonDescriptor() {}
@@ -150,14 +163,17 @@ public EditPersonDescriptor(EditPersonDescriptor toCopy) {
setPhone(toCopy.phone);
setEmail(toCopy.email);
setAddress(toCopy.address);
- setTags(toCopy.tags);
+ setSalary(toCopy.salary);
+ setClaimBudget(toCopy.claimBudget);
+ setDepartment(toCopy.department);
+ setDob(toCopy.dob);
}
/**
* Returns true if at least one field is edited.
*/
public boolean isAnyFieldEdited() {
- return CollectionUtil.isAnyNonNull(name, phone, email, address, tags);
+ return CollectionUtil.isAnyNonNull(name, phone, email, address, salary, claimBudget, department, dob);
}
public void setName(Name name) {
@@ -192,21 +208,36 @@ public Optional getAddress() {
return Optional.ofNullable(address);
}
- /**
- * Sets {@code tags} to this object's {@code tags}.
- * A defensive copy of {@code tags} is used internally.
- */
- public void setTags(Set tags) {
- this.tags = (tags != null) ? new HashSet<>(tags) : null;
+ public void setSalary(Money salary) {
+ this.salary = salary;
}
- /**
- * Returns an unmodifiable tag set, which throws {@code UnsupportedOperationException}
- * if modification is attempted.
- * Returns {@code Optional#empty()} if {@code tags} is null.
- */
- public Optional> getTags() {
- return (tags != null) ? Optional.of(Collections.unmodifiableSet(tags)) : Optional.empty();
+ public Optional getSalary() {
+ return Optional.ofNullable(salary);
+ }
+
+ public void setClaimBudget(Money claimBudget) {
+ this.claimBudget = claimBudget;
+ }
+
+ public Optional getClaimBudget() {
+ return Optional.ofNullable(claimBudget);
+ }
+
+ public void setDepartment(Department department) {
+ this.department = department;
+ }
+
+ public Optional getDepartment() {
+ return Optional.ofNullable(department);
+ }
+
+ public void setDob(Birthday dob) {
+ this.dob = dob;
+ }
+
+ public Optional getDob() {
+ return Optional.ofNullable(dob);
}
@Override
@@ -225,7 +256,10 @@ public boolean equals(Object other) {
&& Objects.equals(phone, otherEditPersonDescriptor.phone)
&& Objects.equals(email, otherEditPersonDescriptor.email)
&& Objects.equals(address, otherEditPersonDescriptor.address)
- && Objects.equals(tags, otherEditPersonDescriptor.tags);
+ && Objects.equals(salary, otherEditPersonDescriptor.salary)
+ && Objects.equals(claimBudget, otherEditPersonDescriptor.claimBudget)
+ && Objects.equals(department, otherEditPersonDescriptor.department)
+ && Objects.equals(dob, otherEditPersonDescriptor.dob);
}
@Override
@@ -235,7 +269,10 @@ public String toString() {
.add("phone", phone)
.add("email", email)
.add("address", address)
- .add("tags", tags)
+ .add("salary", salary)
+ .add("claimBudget", claimBudget)
+ .add("department", department)
+ .add("dob", dob)
.toString();
}
}
diff --git a/src/main/java/seedu/address/logic/commands/ExitCommand.java b/src/main/java/seedu/address/logic/commands/ExitCommand.java
index 3dd85a8ba90..455856f9f3b 100644
--- a/src/main/java/seedu/address/logic/commands/ExitCommand.java
+++ b/src/main/java/seedu/address/logic/commands/ExitCommand.java
@@ -12,8 +12,8 @@ public class ExitCommand extends Command {
public static final String MESSAGE_EXIT_ACKNOWLEDGEMENT = "Exiting Address Book as requested ...";
@Override
- public CommandResult execute(Model model) {
- return new CommandResult(MESSAGE_EXIT_ACKNOWLEDGEMENT, false, true);
+ public CommandResult execute(Model model, String commandText) {
+ return new CommandResult(MESSAGE_EXIT_ACKNOWLEDGEMENT, false, true, false, "");
}
}
diff --git a/src/main/java/seedu/address/logic/commands/ExportCommand.java b/src/main/java/seedu/address/logic/commands/ExportCommand.java
new file mode 100644
index 00000000000..0271789d7ea
--- /dev/null
+++ b/src/main/java/seedu/address/logic/commands/ExportCommand.java
@@ -0,0 +1,146 @@
+package seedu.address.logic.commands;
+
+import static java.util.Objects.requireNonNull;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+import java.util.stream.Collectors;
+
+import seedu.address.logic.commands.exceptions.CommandException;
+import seedu.address.model.Model;
+import seedu.address.model.person.Person;
+
+/**
+ * Creates a new CSV file containing employee's data.
+ */
+public class ExportCommand extends Command {
+
+ public static final String COMMAND_WORD = "export";
+ public static final String FOLDER_NAME = "Exported_CSVs";
+ public final String fileName;
+
+ /**
+ * Initialises a ExportCommand object containing the filename user wants.
+ *
+ * @param fileName String which represents the name of export file.
+ */
+ public ExportCommand(String fileName) {
+ this.fileName = fileName;
+ }
+
+ /**
+ * Returns a CommandResult object with different response message.
+ * Performs creation of File object first before writing of contents.
+ *
+ * @param model {@code Model} which the command should operate on.
+ * @param commandText Identifier for redo/undo of statements.
+ * @return CommandResult containing the different success/failure response.
+ * @throws CommandException In the event where file creation/writing has failed.
+ */
+ @Override
+ public CommandResult execute(Model model, String commandText) throws CommandException {
+ requireNonNull(model);
+ List existingPeople = model.getFilteredPersonList();
+ List> dataLines = generateListPeople(existingPeople);
+ File csvOutputFile = generateFile(this.fileName);
+ try (PrintWriter pw = new PrintWriter(csvOutputFile)) {
+ dataLines.stream()
+ .map(this::convertToCsv)
+ .forEach(pw::println);
+ return new CommandResult(String.format("%s.csv has been successfully created!\n", this.fileName)
+ + "You can view the file in the Exported_CSVs folder.");
+ } catch (FileNotFoundException e) {
+ throw new CommandException("Error: Unable to write to file!");
+ }
+ }
+
+ /**
+ * Returns a List of List of Strings where each internal List is a particular person's attributes.
+ *
+ * @param existingData List of Person Object. Data is found is retrieved from model.
+ * @return List of List of Strings.
+ */
+ public List> generateListPeople(List existingData) {
+ List> dataLines = new ArrayList<>();
+ dataLines.add(List.of("Name", "Phone", "Email", "Address", "Salary", "Claim Budget", "DOB",
+ "Department", "Leave"));
+ for (Person people: existingData) {
+ List peopleDetails = new ArrayList<>();
+ peopleDetails.add(people.getName().toString());
+ peopleDetails.add(people.getPhone().value);
+ peopleDetails.add(people.getEmail().value);
+ peopleDetails.add(people.getAddress().value);
+ peopleDetails.add(people.getSalary().amount);
+ peopleDetails.add(people.getClaimBudget().amount);
+ peopleDetails.add(people.getDob().dob);
+ peopleDetails.add(people.getDepartment().toString());
+ peopleDetails.add(people.getLeave().toString());
+ dataLines.add(peopleDetails);
+ }
+ return dataLines;
+ }
+
+ /**
+ * Returns a File Object with identifier of fileName and stored in FOLDER_NAME.
+ *
+ * @param fileName String containing the name user wants to name the file.
+ * @return File Object where it is stored in a specific directory coined FOLDER_NAME.
+ * @throws CommandException if directory cannot be created.
+ */
+ public File generateFile(String fileName) throws CommandException {
+ File folder = new File(FOLDER_NAME);
+ if (!folder.exists()) {
+ if (!folder.mkdir()) {
+ throw new CommandException("Something went wrong.\n"
+ + "Directory cannot be created.");
+ }
+ }
+ return new File(folder, String.format("%s.csv", this.fileName));
+ }
+
+ /**
+ * String Object where each attribute of a Person is joined by a delimiter.
+ *
+ * @param data List of Strings where each string is an attribute of a person.
+ * @return String containing the various attributes joined together with ",".
+ */
+ public String convertToCsv(List data) {
+ return data.stream()
+ .map(this::escapeSpecialCharacters)
+ .collect(Collectors.joining(","));
+ }
+
+ /**
+ * Performs formatting of csv for fields containing special characters.
+ * Especially useful when columns like address has multiple commas.
+ *
+ * @param data String containing person's attributes.
+ * @return String of nicely formatted attributes.
+ */
+ public String escapeSpecialCharacters(String data) {
+ String escapedData = data;
+ if (data.contains("\"")) {
+ escapedData = data.replace("\"", "\"\"");
+ }
+ if (data.contains(",") || data.contains("\n")) {
+ escapedData = "\"" + escapedData + "\"";
+ }
+ return escapedData;
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (this == other) {
+ return true; // Both are the same instance
+ }
+ if (other == null || getClass() != other.getClass()) {
+ return false; // Other is not an instance of ExportCommand or is null
+ }
+ ExportCommand that = (ExportCommand) other;
+ return Objects.equals(fileName, that.fileName);
+ }
+}
diff --git a/src/main/java/seedu/address/logic/commands/FindCommand.java b/src/main/java/seedu/address/logic/commands/FindCommand.java
index 72b9eddd3a7..c677f609ddc 100644
--- a/src/main/java/seedu/address/logic/commands/FindCommand.java
+++ b/src/main/java/seedu/address/logic/commands/FindCommand.java
@@ -27,7 +27,7 @@ public FindCommand(NameContainsKeywordsPredicate predicate) {
}
@Override
- public CommandResult execute(Model model) {
+ public CommandResult execute(Model model, String commandText) {
requireNonNull(model);
model.updateFilteredPersonList(predicate);
return new CommandResult(
diff --git a/src/main/java/seedu/address/logic/commands/HelpCommand.java b/src/main/java/seedu/address/logic/commands/HelpCommand.java
index bf824f91bd0..f1bd3ad0c42 100644
--- a/src/main/java/seedu/address/logic/commands/HelpCommand.java
+++ b/src/main/java/seedu/address/logic/commands/HelpCommand.java
@@ -15,7 +15,7 @@ public class HelpCommand extends Command {
public static final String SHOWING_HELP_MESSAGE = "Opened help window.";
@Override
- public CommandResult execute(Model model) {
- return new CommandResult(SHOWING_HELP_MESSAGE, true, false);
+ public CommandResult execute(Model model, String commandText) {
+ return new CommandResult(SHOWING_HELP_MESSAGE, true, false, false, "");
}
}
diff --git a/src/main/java/seedu/address/logic/commands/LeaveCommand.java b/src/main/java/seedu/address/logic/commands/LeaveCommand.java
new file mode 100644
index 00000000000..314da03fe72
--- /dev/null
+++ b/src/main/java/seedu/address/logic/commands/LeaveCommand.java
@@ -0,0 +1,112 @@
+package seedu.address.logic.commands;
+
+import static java.util.Objects.requireNonNull;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_MONTH;
+import static seedu.address.model.Model.PREDICATE_SHOW_ALL_PERSONS;
+
+import java.util.List;
+
+import seedu.address.commons.core.index.Index;
+import seedu.address.commons.util.ToStringBuilder;
+import seedu.address.logic.Messages;
+import seedu.address.logic.commands.exceptions.CommandException;
+import seedu.address.model.Model;
+import seedu.address.model.person.Leave;
+import seedu.address.model.person.Person;
+
+/**
+ * Adds leave months for an employee.
+ */
+public class LeaveCommand extends Command {
+
+ public static final String COMMAND_WORD = "leave";
+
+ public static final String MESSAGE_USAGE = COMMAND_WORD + ": Adds leave months for an employee.\n"
+ + "Parameters: INDEX (must be a positive integer) " + PREFIX_MONTH + "MONTHS\n"
+ + "Format: MONTHS must be integers separated by commas without spaces. "
+ + "1: Jan, 2: Feb, ..., 12: Dec.\n"
+ + "Positive MONTHS add leaves on the specified months and negative MONTHS remove them.\n"
+ + "Example: " + COMMAND_WORD + " 1 "
+ + PREFIX_MONTH + "3,-4 to add leave in March and remove leave in April for the 1st employee in the list.";
+
+ public static final String MESSAGE_AMBIGUOUS = "Please check your MONTHS. Ambiguous leave(s) assignment.\n";
+ public static final String MESSAGE_EMPTY = "MONTHS cannot be empty.\n";
+ public static final String MESSAGE_INVALID_MONTH = "Please check your MONTHS. Invalid month(s) provided.\n";
+ public static final String MESSAGE_LEAVE_SUCCESS = "Leave(s) successfully updated for employee: %1$s.\n"
+ + "Current leave(s): %2$s";
+ public static final String MESSAGE_NOT_EDITED = "The employee's leave(s) does not change from previous state: %1$s";
+ public static final String MESSAGE_SPACES_DETECTED = "Spaces detected in your MONTHS.\n";
+
+ private final Index index;
+ private final String change;
+
+ /**
+ * Constructs a LeaveCommand to update the {@code Leave} of a {@code Person}
+ * with the given index.
+ * @param index
+ * @param change
+ */
+ public LeaveCommand(Index index, String change) {
+ requireNonNull(index);
+ requireNonNull(change);
+
+ this.index = index;
+ this.change = change;
+ }
+
+ @Override
+ public CommandResult execute(Model model, String commandText) throws CommandException {
+ requireNonNull(model);
+ List lastShownList = model.getFilteredPersonList();
+
+ if (index.getZeroBased() >= lastShownList.size()) {
+ throw new CommandException(Messages.MESSAGE_INVALID_PERSON_DISPLAYED_INDEX);
+ }
+
+ Person oldPerson = lastShownList.get(index.getZeroBased());
+ Leave oldLeave = oldPerson.getLeave();
+ Leave newLeave;
+ try {
+ newLeave = oldLeave.update(change);
+ } catch (IllegalArgumentException e) {
+ throw new CommandException(e.getMessage());
+ }
+
+ if (oldLeave.equals(newLeave)) {
+ throw new CommandException(String.format(MESSAGE_NOT_EDITED, oldLeave.toString()));
+ }
+
+ Person newPerson = new Person(oldPerson.getName(), oldPerson.getPhone(), oldPerson.getEmail(),
+ oldPerson.getAddress(), oldPerson.getSalary(), oldPerson.getClaimBudget(),
+ oldPerson.getDepartment(), oldPerson.getDob(), newLeave);
+
+ model.setPerson(oldPerson, newPerson);
+ model.addCommandText(commandText);
+ model.updateFilteredPersonList(PREDICATE_SHOW_ALL_PERSONS);
+ return new CommandResult(String.format(MESSAGE_LEAVE_SUCCESS, newPerson.getName(), newLeave.toString()));
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (other == this) {
+ return true;
+ }
+
+ // instanceof handles nulls
+ if (!(other instanceof LeaveCommand)) {
+ return false;
+ }
+
+ LeaveCommand otherCommand = (LeaveCommand) other;
+ return index.equals(otherCommand.index)
+ && change.equals(otherCommand.change);
+ }
+
+ @Override
+ public String toString() {
+ return new ToStringBuilder(this)
+ .add("index", index)
+ .add("leave", change)
+ .toString();
+ }
+}
diff --git a/src/main/java/seedu/address/logic/commands/ListCommand.java b/src/main/java/seedu/address/logic/commands/ListCommand.java
index 84be6ad2596..ce4c3552f7c 100644
--- a/src/main/java/seedu/address/logic/commands/ListCommand.java
+++ b/src/main/java/seedu/address/logic/commands/ListCommand.java
@@ -3,22 +3,45 @@
import static java.util.Objects.requireNonNull;
import static seedu.address.model.Model.PREDICATE_SHOW_ALL_PERSONS;
+import java.util.function.Predicate;
+
+import seedu.address.logic.Messages;
import seedu.address.model.Model;
+import seedu.address.model.person.Person;
/**
- * Lists all persons in the address book to the user.
+ * Lists all persons or filtered persons in the address book to the user.
*/
public class ListCommand extends Command {
public static final String COMMAND_WORD = "list";
- public static final String MESSAGE_SUCCESS = "Listed all persons";
+ public final String message;
+
+ public final Predicate predicate;
+ /**
+ * Initializes ListCommand non-filtered list
+ */
+ public ListCommand() {
+ this.predicate = PREDICATE_SHOW_ALL_PERSONS;
+ this.message = Messages.MESSAGE_LIST_SUCCESS;
+ }
+
+ /**
+ * Initializes ListCommand for filtered List
+ *
+ * @param predicate predicate for the filtering
+ */
+ public ListCommand(Predicate predicate) {
+ this.predicate = predicate;
+ this.message = Messages.MESSAGE_FILTER_SUCCESS;
+ }
@Override
- public CommandResult execute(Model model) {
+ public CommandResult execute(Model model, String commandText) {
requireNonNull(model);
- model.updateFilteredPersonList(PREDICATE_SHOW_ALL_PERSONS);
- return new CommandResult(MESSAGE_SUCCESS);
+ model.updateFilteredPersonList(predicate);
+ return new CommandResult(String.format(message, model.getFilteredPersonList().size()));
}
}
diff --git a/src/main/java/seedu/address/logic/commands/RedoCommand.java b/src/main/java/seedu/address/logic/commands/RedoCommand.java
new file mode 100644
index 00000000000..7ba49bdfc41
--- /dev/null
+++ b/src/main/java/seedu/address/logic/commands/RedoCommand.java
@@ -0,0 +1,34 @@
+package seedu.address.logic.commands;
+
+import static java.util.Objects.requireNonNull;
+import static seedu.address.model.Model.PREDICATE_SHOW_ALL_PERSONS;
+
+import seedu.address.logic.commands.exceptions.CommandException;
+import seedu.address.model.Model;
+
+/**
+ * Redo the most recent command that was undone.
+ */
+public class RedoCommand extends Command {
+
+ public static final String COMMAND_WORD = "redo";
+
+ public static final String MESSAGE_USAGE = COMMAND_WORD
+ + ": Redo the most recent command that was undone.";
+
+ public static final String MESSAGE_SUCCESS =
+ "The last command that modified the employee list has been redone!\n"
+ + "Successfully redone the following command: %1$s";
+
+ @Override
+ public CommandResult execute(Model model, String cmd) throws CommandException {
+ requireNonNull(model);
+ try {
+ String commandText = model.redo();
+ model.updateFilteredPersonList(PREDICATE_SHOW_ALL_PERSONS);
+ return new CommandResult(String.format(MESSAGE_SUCCESS, commandText));
+ } catch (IllegalArgumentException e) {
+ throw new CommandException(e.getMessage());
+ }
+ }
+}
diff --git a/src/main/java/seedu/address/logic/commands/ResetLeavesCommand.java b/src/main/java/seedu/address/logic/commands/ResetLeavesCommand.java
new file mode 100644
index 00000000000..b178cfa997e
--- /dev/null
+++ b/src/main/java/seedu/address/logic/commands/ResetLeavesCommand.java
@@ -0,0 +1,63 @@
+package seedu.address.logic.commands;
+
+import static java.util.Objects.requireNonNull;
+import static seedu.address.model.Model.PREDICATE_SHOW_ALL_PERSONS;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import seedu.address.model.AddressBook;
+import seedu.address.model.Model;
+import seedu.address.model.person.Person;
+
+/**
+ * Resets all employees' leaves to - (AKA No recorded leaves).
+ * This command is suitable for resetting of leaves per year.
+ */
+public class ResetLeavesCommand extends Command {
+
+ public static final String COMMAND_WORD = "reset_leaves";
+ public static final String MESSAGE_SUCCESS = "All employees' leaves have been reset.";
+
+ /**
+ * @param model {@code Model} which the command should operate on.
+ * @param commandText Identifier for undo statements.
+ * @return CommandResult with modified Model with each employee having no recorded leaves.
+ */
+ @Override
+ public CommandResult execute(Model model, String commandText) {
+ requireNonNull(model);
+ List existingParticipants = model.getAddressBook().getPersonList();
+ List newParticipants = new ArrayList<>();
+ for (int i = 0; i < existingParticipants.size(); i++) {
+ Person individual = existingParticipants.get(i);
+ newParticipants.add(generateNewPerson(individual));
+ }
+ model.setAddressBook(generateNewAb(newParticipants));
+ model.addCommandText(commandText);
+ model.updateFilteredPersonList(PREDICATE_SHOW_ALL_PERSONS);
+ return new CommandResult(MESSAGE_SUCCESS);
+ }
+
+ /**
+ * Generates new Address Book containing modified Persons object.
+ * @param newIndividuals List of Person objects, each with no recorded leaves.
+ * @return Address Book Object.
+ */
+ private AddressBook generateNewAb(List newIndividuals) {
+ AddressBook ab = new AddressBook();
+ ab.setPersons(newIndividuals);
+ return ab;
+ }
+
+ /**
+ * Private function that helps create a new Person with same attributes but 0 leaves.
+ * @param individual Employee (Person Object).
+ * @return Person Object containing same attributes except total of zero leaves.
+ */
+ private Person generateNewPerson(Person individual) {
+ return new Person(individual.getName(), individual.getPhone(), individual.getEmail(),
+ individual.getAddress(), individual.getSalary(), individual.getClaimBudget(),
+ individual.getDepartment(), individual.getDob());
+ }
+}
diff --git a/src/main/java/seedu/address/logic/commands/SortCommand.java b/src/main/java/seedu/address/logic/commands/SortCommand.java
new file mode 100644
index 00000000000..a145def4a1c
--- /dev/null
+++ b/src/main/java/seedu/address/logic/commands/SortCommand.java
@@ -0,0 +1,87 @@
+package seedu.address.logic.commands;
+
+import static java.util.Objects.requireNonNull;
+import static seedu.address.model.Model.PREDICATE_SHOW_ALL_PERSONS;
+
+import java.util.Comparator;
+
+import seedu.address.logic.commands.exceptions.CommandException;
+import seedu.address.model.Model;
+import seedu.address.model.person.Person;
+import seedu.address.model.person.PersonComparator;
+
+/**
+ * Sorts the employee list based on the given parameter.
+ */
+public class SortCommand extends Command {
+
+ public static final String COMMAND_WORD = "sort";
+ public static final String SORT_DESC = "desc";
+
+ public static final String MESSAGE_USAGE = COMMAND_WORD
+ + ": Sorts the employee list based on the given parameter.\n"
+ + "Format: sort name / phone / email / address / salary / claim / dep / dob [desc]\n"
+ + "Choose one parameter to sort. [desc] to sort in descending order is optional.\n"
+ + "Example: " + COMMAND_WORD + " name desc to sort based on name in descending order.";
+
+ public static final String MESSAGE_SUCCESS = "The employee list is now sorted!";
+
+ public static final String ERROR_MESSAGE = "Invalid parameter to sort!\n" + MESSAGE_USAGE;
+
+ private final boolean desc;
+ private final Comparator comparator;
+ private final String param;
+
+ /**
+ * Constructs a SortCommand.
+ * @param param parameter to sort
+ */
+ public SortCommand(String param) {
+ this.comparator = new PersonComparator(param);
+ this.desc = false;
+ this.param = param;
+ }
+
+ /**
+ * Constructs a SortCommand.
+ * @param param parameter to sort
+ * @param desc descending
+ */
+ public SortCommand(String param, boolean desc) {
+ this.comparator = new PersonComparator(param);
+ this.desc = desc;
+ this.param = param;
+ }
+
+ @Override
+ public CommandResult execute(Model model, String commandText) throws CommandException {
+ requireNonNull(model);
+ try {
+ if (desc) {
+ model.sort(comparator.reversed());
+ } else {
+ model.sort(comparator);
+ }
+ model.updateFilteredPersonList(PREDICATE_SHOW_ALL_PERSONS);
+ model.addCommandText(commandText);
+ return new CommandResult(String.format(MESSAGE_SUCCESS, commandText));
+ } catch (IllegalArgumentException e) {
+ throw new CommandException(ERROR_MESSAGE);
+ }
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (other == this) {
+ return true;
+ }
+
+ // instanceof handles nulls
+ if (!(other instanceof SortCommand)) {
+ return false;
+ }
+
+ SortCommand otherCommand = (SortCommand) other;
+ return param.equals(otherCommand.param) && desc == otherCommand.desc;
+ }
+}
diff --git a/src/main/java/seedu/address/logic/commands/ThemeCommand.java b/src/main/java/seedu/address/logic/commands/ThemeCommand.java
new file mode 100644
index 00000000000..507b6382d63
--- /dev/null
+++ b/src/main/java/seedu/address/logic/commands/ThemeCommand.java
@@ -0,0 +1,25 @@
+package seedu.address.logic.commands;
+
+import seedu.address.model.Model;
+
+/**
+ * Changes the application theme to the specified theme.
+ */
+public class ThemeCommand extends Command {
+
+ public static final String COMMAND_WORD = "theme";
+
+ public static final String MESSAGE_THEME_CHANGE_ACKNOWLEDGEMENT = "Theme changed.";
+
+ public final String styleSheet;
+
+ public ThemeCommand(String styleSheet) {
+ this.styleSheet = styleSheet;
+ }
+
+ @Override
+ public CommandResult execute(Model model, String commandText) {
+ return new CommandResult(MESSAGE_THEME_CHANGE_ACKNOWLEDGEMENT, false,
+ false, true, styleSheet);
+ }
+}
diff --git a/src/main/java/seedu/address/logic/commands/UndoCommand.java b/src/main/java/seedu/address/logic/commands/UndoCommand.java
new file mode 100644
index 00000000000..105b83bb9e8
--- /dev/null
+++ b/src/main/java/seedu/address/logic/commands/UndoCommand.java
@@ -0,0 +1,34 @@
+package seedu.address.logic.commands;
+
+import static java.util.Objects.requireNonNull;
+import static seedu.address.model.Model.PREDICATE_SHOW_ALL_PERSONS;
+
+import seedu.address.logic.commands.exceptions.CommandException;
+import seedu.address.model.Model;
+
+/**
+ * Undo the most recent command that modified the employee list.
+ */
+public class UndoCommand extends Command {
+
+ public static final String COMMAND_WORD = "undo";
+
+ public static final String MESSAGE_USAGE = COMMAND_WORD
+ + ": Undo the most recent command that modified the employee list.";
+
+ public static final String MESSAGE_SUCCESS =
+ "The last command that modified the employee list has been undone!\n"
+ + "Successfully undone the following command: %1$s";
+
+ @Override
+ public CommandResult execute(Model model, String cmd) throws CommandException {
+ requireNonNull(model);
+ try {
+ String commandText = model.undo();
+ model.updateFilteredPersonList(PREDICATE_SHOW_ALL_PERSONS);
+ return new CommandResult(String.format(MESSAGE_SUCCESS, commandText));
+ } catch (IllegalArgumentException e) {
+ throw new CommandException(e.getMessage());
+ }
+ }
+}
diff --git a/src/main/java/seedu/address/logic/commands/ViewCommand.java b/src/main/java/seedu/address/logic/commands/ViewCommand.java
new file mode 100644
index 00000000000..4ada8f8d850
--- /dev/null
+++ b/src/main/java/seedu/address/logic/commands/ViewCommand.java
@@ -0,0 +1,159 @@
+package seedu.address.logic.commands;
+
+import static java.util.Objects.requireNonNull;
+import static seedu.address.logic.parser.ViewCommandParser.ADDRESS_IDENTIFIER;
+import static seedu.address.logic.parser.ViewCommandParser.BIRTHDAY;
+import static seedu.address.logic.parser.ViewCommandParser.CLAIM_BUDGET;
+import static seedu.address.logic.parser.ViewCommandParser.DEPARTMENT;
+import static seedu.address.logic.parser.ViewCommandParser.EMAIL_IDENTIFIER;
+import static seedu.address.logic.parser.ViewCommandParser.NAME_IDENTIFIER;
+import static seedu.address.logic.parser.ViewCommandParser.PHONE_IDENTIFIER;
+import static seedu.address.logic.parser.ViewCommandParser.SALARY_IDENTIFIER;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Objects;
+
+import seedu.address.commons.core.index.Index;
+import seedu.address.logic.Messages;
+import seedu.address.logic.commands.exceptions.CommandException;
+import seedu.address.model.Model;
+import seedu.address.model.person.Person;
+
+/**
+ * Returns ViewCommand Object based on what user's wants to view.
+ */
+public class ViewCommand extends Command {
+
+ public static final String COMMAND_WORD = "view";
+ public static final String WRONG_PREFIX = "ViewCommand provides "
+ + "overview of employee(s)'s attributes.\n"
+ + "Allowed Formats: n/, a/, e/, p/, s/, b/, d/, dob/.\n"
+ + "Example: view n/1,2 displays employees with list’s index of 1 and 2 respective salaries.";
+ public static final String EXCESS_PREFIX = "Kindly input only one prefix.\n"
+ + "Allowed Format: n/, a/, e/, p/, s/, b/, d/, dob/.\n"
+ + "Example: view n/1,2 displays employees with list’s index of 1 and 2 respective salaries.";
+
+ public final HashMap> references;
+
+ /**
+ * Initialises ViewCommand Object containing view operations on specific individuals.
+ *
+ * @param references HashMap object referencing view operations on Index individuals in list.
+ */
+ public ViewCommand(HashMap> references) {
+ this.references = references;
+ }
+
+ /**
+ * Returns CommandResult after successfully parsing user's view commands.
+ *
+ * @param model {@code Model} which the command should operate on.
+ * @return CommandResult which contains string representation of person's attribute.
+ * @throws CommandException Exception thrown if index input from HR is beyond the pre-existing max list index.
+ */
+ @Override
+ public CommandResult execute(Model model, String commandText) throws CommandException {
+ requireNonNull(model);
+ List lastShownList = model.getFilteredPersonList();
+
+ String finalString = "";
+
+ for (String key: references.keySet()) {
+ List indexList = references.get(key);
+ String tempStr = "You are viewing " + key + ":\n";
+ for (int i = 0; i < indexList.size(); i++) {
+ int listIndex = indexList.get(i).getZeroBased();
+ if (listIndex >= lastShownList.size()) {
+ throw new CommandException(Messages.MESSAGE_INVALID_PERSON_DISPLAYED_INDEX);
+ }
+ Person personToEdit = lastShownList.get(indexList.get(i).getZeroBased());
+ HashMap personRelevantInfo = generateArrInfo(personToEdit);
+ int index = indexList.get(i).getOneBased();
+ massAssertionFn(key);
+ if (Objects.equals(key, NAME_IDENTIFIER)) {
+ tempStr += String.format("%s. %s.\n", index, personRelevantInfo.get(NAME_IDENTIFIER));
+ } else if (Objects.equals(key, PHONE_IDENTIFIER)) {
+ tempStr += generateNiceStr(personRelevantInfo, index, PHONE_IDENTIFIER);
+ } else if (Objects.equals(key, EMAIL_IDENTIFIER)) {
+ tempStr += generateNiceStr(personRelevantInfo, index, EMAIL_IDENTIFIER);
+ } else if (Objects.equals(key, ADDRESS_IDENTIFIER)) {
+ tempStr += generateNiceStr(personRelevantInfo, index, ADDRESS_IDENTIFIER);
+ } else if (Objects.equals(key, SALARY_IDENTIFIER)) {
+ tempStr += generateNiceStr(personRelevantInfo, index, SALARY_IDENTIFIER);
+ } else if (Objects.equals(key, CLAIM_BUDGET)) {
+ tempStr += generateNiceStr(personRelevantInfo, index, CLAIM_BUDGET);
+ } else if (Objects.equals(key, DEPARTMENT)) {
+ tempStr += generateNiceStr(personRelevantInfo, index, DEPARTMENT);
+ } else {
+ tempStr += generateNiceStr(personRelevantInfo, index, BIRTHDAY);
+ }
+ }
+ finalString += tempStr + "\n";
+ }
+ return new CommandResult(finalString);
+ }
+
+ /**
+ * Assertion test to ensure key corresponds to all the existing identifiers.
+ *
+ * @param key User Input of the prefixes one wants to view.
+ */
+ public void massAssertionFn(String key) {
+ assert Objects.equals(key, NAME_IDENTIFIER) || Objects.equals(key, PHONE_IDENTIFIER)
+ || Objects.equals(key, EMAIL_IDENTIFIER) || Objects.equals(key, ADDRESS_IDENTIFIER)
+ || Objects.equals(key, SALARY_IDENTIFIER) || Objects.equals(key, CLAIM_BUDGET)
+ || Objects.equals(key, DEPARTMENT) || Objects.equals(key, BIRTHDAY);
+ }
+
+ /**
+ * Returns HashMap which contains employee's information.
+ *
+ * @param person Person object that we want to retrieve information from.
+ * @return HashMap, which informs us of the person's primary attributes.
+ */
+ private HashMap generateArrInfo(Person person) {
+ HashMap map = new HashMap<>();
+ map.put(NAME_IDENTIFIER, person.getName().toString());
+ map.put(PHONE_IDENTIFIER, person.getPhone().toString());
+ map.put(EMAIL_IDENTIFIER, person.getEmail().toString());
+ map.put(ADDRESS_IDENTIFIER, person.getAddress().toString());
+ map.put(SALARY_IDENTIFIER, person.getSalary().toString());
+ map.put(CLAIM_BUDGET, person.getClaimBudget().toString());
+ map.put(DEPARTMENT, person.getDepartment().toString());
+ map.put(BIRTHDAY, person.getDob().toString());
+ return map;
+ }
+
+ /**
+ * Returns String containing relevant person's information.
+ *
+ * @param personInfo HashMap containing Person's primary attributes.
+ * @param index Integer which represents index from list.
+ * @param identifier String which represents what attribute to view from.
+ * @return String representation of relevant information.
+ */
+ public String generateNiceStr(HashMap personInfo, int index, String identifier) {
+ String result = personInfo.get(identifier);
+ String name = personInfo.get(NAME_IDENTIFIER);
+ return String.format("%s. %s's %s is %s.\n", index, name, identifier, result);
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ ViewCommand that = (ViewCommand) o;
+ return references.equals(that.references);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(references);
+ }
+
+}
diff --git a/src/main/java/seedu/address/logic/commands/ViewLeaveCommand.java b/src/main/java/seedu/address/logic/commands/ViewLeaveCommand.java
new file mode 100644
index 00000000000..5ef6058cd59
--- /dev/null
+++ b/src/main/java/seedu/address/logic/commands/ViewLeaveCommand.java
@@ -0,0 +1,54 @@
+package seedu.address.logic.commands;
+
+import static java.util.Objects.requireNonNull;
+
+import java.util.function.Predicate;
+
+import seedu.address.logic.Messages;
+import seedu.address.logic.commands.exceptions.CommandException;
+import seedu.address.model.Model;
+import seedu.address.model.person.Person;
+
+/**
+ * Lists persons with planned leave dates in a certain month in a certain department.
+ */
+public class ViewLeaveCommand extends Command {
+
+ public static final String COMMAND_WORD = "view_leave";
+
+ public static final String MESSAGE = Messages.MESSAGE_VIEW_LEAVE_SUCCESS;
+
+ public final Predicate combinedPredicate;
+
+ /**
+ * Initializes ViewLeaveCommand with HasLeaveAnyMonthPredicate predicate.
+ */
+ public ViewLeaveCommand(Predicate combinedPredicate) {
+ this.combinedPredicate = combinedPredicate;
+ }
+
+ @Override
+ public CommandResult execute(Model model, String commandText) throws CommandException {
+ requireNonNull(model);
+ model.updateFilteredPersonList(combinedPredicate);
+ if (model.getFilteredPersonList().size() == 0) {
+ return new CommandResult(Messages.MESSAGE_VIEW_LEAVE_NO_EMPLOYEES);
+ }
+ return new CommandResult(String.format(MESSAGE, model.getFilteredPersonList().size()));
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (other == this) {
+ return true;
+ }
+
+ // instanceof handles nulls
+ if (!(other instanceof ViewLeaveCommand)) {
+ return false;
+ }
+
+ ViewLeaveCommand otherCommand = (ViewLeaveCommand) other;
+ return combinedPredicate.equals(otherCommand.combinedPredicate);
+ }
+}
diff --git a/src/main/java/seedu/address/logic/parser/AddCommandParser.java b/src/main/java/seedu/address/logic/parser/AddCommandParser.java
index 4ff1a97ed77..7eecacac56d 100644
--- a/src/main/java/seedu/address/logic/parser/AddCommandParser.java
+++ b/src/main/java/seedu/address/logic/parser/AddCommandParser.java
@@ -1,23 +1,28 @@
package seedu.address.logic.parser;
+import static java.util.Objects.requireNonNull;
import static seedu.address.logic.Messages.MESSAGE_INVALID_COMMAND_FORMAT;
import static seedu.address.logic.parser.CliSyntax.PREFIX_ADDRESS;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_CLAIM_BUDGET;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_DEPARTMENT;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_DOB;
import static seedu.address.logic.parser.CliSyntax.PREFIX_EMAIL;
import static seedu.address.logic.parser.CliSyntax.PREFIX_NAME;
import static seedu.address.logic.parser.CliSyntax.PREFIX_PHONE;
-import static seedu.address.logic.parser.CliSyntax.PREFIX_TAG;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_SALARY;
-import java.util.Set;
import java.util.stream.Stream;
import seedu.address.logic.commands.AddCommand;
import seedu.address.logic.parser.exceptions.ParseException;
import seedu.address.model.person.Address;
+import seedu.address.model.person.Birthday;
+import seedu.address.model.person.Department;
import seedu.address.model.person.Email;
+import seedu.address.model.person.Money;
import seedu.address.model.person.Name;
import seedu.address.model.person.Person;
import seedu.address.model.person.Phone;
-import seedu.address.model.tag.Tag;
/**
* Parses input arguments and creates a new AddCommand object
@@ -30,22 +35,29 @@ public class AddCommandParser implements Parser {
* @throws ParseException if the user input does not conform the expected format
*/
public AddCommand parse(String args) throws ParseException {
+ requireNonNull(args);
ArgumentMultimap argMultimap =
- ArgumentTokenizer.tokenize(args, PREFIX_NAME, PREFIX_PHONE, PREFIX_EMAIL, PREFIX_ADDRESS, PREFIX_TAG);
+ ArgumentTokenizer.tokenize(args, PREFIX_NAME, PREFIX_PHONE, PREFIX_EMAIL, PREFIX_ADDRESS,
+ PREFIX_SALARY, PREFIX_CLAIM_BUDGET, PREFIX_DEPARTMENT, PREFIX_DOB);
- if (!arePrefixesPresent(argMultimap, PREFIX_NAME, PREFIX_ADDRESS, PREFIX_PHONE, PREFIX_EMAIL)
+ if (!arePrefixesPresent(argMultimap, PREFIX_NAME, PREFIX_ADDRESS, PREFIX_PHONE, PREFIX_EMAIL,
+ PREFIX_SALARY, PREFIX_CLAIM_BUDGET, PREFIX_DEPARTMENT, PREFIX_DOB)
|| !argMultimap.getPreamble().isEmpty()) {
throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, AddCommand.MESSAGE_USAGE));
}
- argMultimap.verifyNoDuplicatePrefixesFor(PREFIX_NAME, PREFIX_PHONE, PREFIX_EMAIL, PREFIX_ADDRESS);
+ argMultimap.verifyNoDuplicatePrefixesFor(PREFIX_NAME, PREFIX_PHONE, PREFIX_EMAIL, PREFIX_ADDRESS,
+ PREFIX_SALARY, PREFIX_CLAIM_BUDGET, PREFIX_DEPARTMENT, PREFIX_DOB);
Name name = ParserUtil.parseName(argMultimap.getValue(PREFIX_NAME).get());
Phone phone = ParserUtil.parsePhone(argMultimap.getValue(PREFIX_PHONE).get());
Email email = ParserUtil.parseEmail(argMultimap.getValue(PREFIX_EMAIL).get());
Address address = ParserUtil.parseAddress(argMultimap.getValue(PREFIX_ADDRESS).get());
- Set tagList = ParserUtil.parseTags(argMultimap.getAllValues(PREFIX_TAG));
+ Money salary = ParserUtil.parseMoney(argMultimap.getValue(PREFIX_SALARY).get());
+ Money claimBudget = ParserUtil.parseMoney(argMultimap.getValue(PREFIX_CLAIM_BUDGET).get());
+ Department department = ParserUtil.parseDepartment(argMultimap.getValue(PREFIX_DEPARTMENT).get());
+ Birthday dob = ParserUtil.parseDob(argMultimap.getValue(PREFIX_DOB).get());
- Person person = new Person(name, phone, email, address, tagList);
+ Person person = new Person(name, phone, email, address, salary, claimBudget, department, dob);
return new AddCommand(person);
}
diff --git a/src/main/java/seedu/address/logic/parser/AddressBookParser.java b/src/main/java/seedu/address/logic/parser/AddressBookParser.java
index 3149ee07e0b..69bd5c148a3 100644
--- a/src/main/java/seedu/address/logic/parser/AddressBookParser.java
+++ b/src/main/java/seedu/address/logic/parser/AddressBookParser.java
@@ -1,6 +1,7 @@
package seedu.address.logic.parser;
import static seedu.address.logic.Messages.MESSAGE_INVALID_COMMAND_FORMAT;
+import static seedu.address.logic.Messages.MESSAGE_NO_ARGUMENTS_EXPECTED;
import static seedu.address.logic.Messages.MESSAGE_UNKNOWN_COMMAND;
import java.util.logging.Logger;
@@ -9,14 +10,25 @@
import seedu.address.commons.core.LogsCenter;
import seedu.address.logic.commands.AddCommand;
+import seedu.address.logic.commands.BirthdayCommand;
+import seedu.address.logic.commands.ClaimCommand;
import seedu.address.logic.commands.ClearCommand;
import seedu.address.logic.commands.Command;
import seedu.address.logic.commands.DeleteCommand;
import seedu.address.logic.commands.EditCommand;
import seedu.address.logic.commands.ExitCommand;
+import seedu.address.logic.commands.ExportCommand;
import seedu.address.logic.commands.FindCommand;
import seedu.address.logic.commands.HelpCommand;
+import seedu.address.logic.commands.LeaveCommand;
import seedu.address.logic.commands.ListCommand;
+import seedu.address.logic.commands.RedoCommand;
+import seedu.address.logic.commands.ResetLeavesCommand;
+import seedu.address.logic.commands.SortCommand;
+import seedu.address.logic.commands.ThemeCommand;
+import seedu.address.logic.commands.UndoCommand;
+import seedu.address.logic.commands.ViewCommand;
+import seedu.address.logic.commands.ViewLeaveCommand;
import seedu.address.logic.parser.exceptions.ParseException;
/**
@@ -52,7 +64,6 @@ public Command parseCommand(String userInput) throws ParseException {
logger.fine("Command word: " + commandWord + "; Arguments: " + arguments);
switch (commandWord) {
-
case AddCommand.COMMAND_WORD:
return new AddCommandParser().parse(arguments);
@@ -63,24 +74,75 @@ public Command parseCommand(String userInput) throws ParseException {
return new DeleteCommandParser().parse(arguments);
case ClearCommand.COMMAND_WORD:
+ ensureEmptyArguments(arguments, ClearCommand.COMMAND_WORD);
return new ClearCommand();
case FindCommand.COMMAND_WORD:
return new FindCommandParser().parse(arguments);
case ListCommand.COMMAND_WORD:
- return new ListCommand();
+ return new ListCommandParser().parse(arguments);
case ExitCommand.COMMAND_WORD:
+ ensureEmptyArguments(arguments, ExitCommand.COMMAND_WORD);
return new ExitCommand();
case HelpCommand.COMMAND_WORD:
+ ensureEmptyArguments(arguments, HelpCommand.COMMAND_WORD);
return new HelpCommand();
+ case ClaimCommand.COMMAND_WORD:
+ return new ClaimCommandParser().parse(arguments);
+
+ case LeaveCommand.COMMAND_WORD:
+ return new LeaveCommandParser().parse(arguments);
+
+ case ViewLeaveCommand.COMMAND_WORD:
+ return new ViewLeaveCommandParser().parse(arguments);
+
+ case ViewCommand.COMMAND_WORD:
+ return new ViewCommandParser().parse(arguments);
+
+ case BirthdayCommand.COMMAND_WORD:
+ return new BirthdayCommandParser().parse(arguments);
+
+ case UndoCommand.COMMAND_WORD:
+ ensureEmptyArguments(arguments, UndoCommand.COMMAND_WORD);
+ return new UndoCommand();
+
+ case RedoCommand.COMMAND_WORD:
+ ensureEmptyArguments(arguments, RedoCommand.COMMAND_WORD);
+ return new RedoCommand();
+
+ case ThemeCommand.COMMAND_WORD:
+ return new ThemeCommandParser().parse(arguments);
+
+ case ExportCommand.COMMAND_WORD:
+ return new ExportCommandParser().parse(arguments);
+
+ case SortCommand.COMMAND_WORD:
+ return new SortCommandParser().parse(arguments);
+
+ case ResetLeavesCommand.COMMAND_WORD:
+ ensureEmptyArguments(arguments, ResetLeavesCommand.COMMAND_WORD);
+ return new ResetLeavesCommand();
+
default:
logger.finer("This user input caused a ParseException: " + userInput);
throw new ParseException(MESSAGE_UNKNOWN_COMMAND);
}
}
+ /**
+ * Ensures that there are no arguments given.
+ *
+ * @param args argument(s) given
+ * @param commandWord commandWord of the checked command
+ * @throws ParseException if the argument is not empty
+ */
+ public void ensureEmptyArguments(String args, String commandWord) throws ParseException {
+ if (!args.isBlank()) {
+ throw new ParseException(String.format(MESSAGE_NO_ARGUMENTS_EXPECTED, commandWord));
+ }
+ }
}
diff --git a/src/main/java/seedu/address/logic/parser/ArgumentMultimap.java b/src/main/java/seedu/address/logic/parser/ArgumentMultimap.java
index 21e26887a83..40fa2a9e053 100644
--- a/src/main/java/seedu/address/logic/parser/ArgumentMultimap.java
+++ b/src/main/java/seedu/address/logic/parser/ArgumentMultimap.java
@@ -75,4 +75,19 @@ public void verifyNoDuplicatePrefixesFor(Prefix... prefixes) throws ParseExcepti
throw new ParseException(Messages.getErrorMessageForDuplicatePrefixes(duplicatedPrefixes));
}
}
+
+ /**
+ * Returns the list of Prefixes from the argMultimap object.
+ *
+ * @return List parsed from argMultimap object.
+ */
+ public List userPrefixes() {
+ List userInputPrefixes = new ArrayList<>();
+ for (Map.Entry> entry : argMultimap.entrySet()) {
+ if (!entry.getKey().toString().isEmpty()) {
+ userInputPrefixes.add(entry.getKey());
+ }
+ }
+ return userInputPrefixes;
+ }
}
diff --git a/src/main/java/seedu/address/logic/parser/ArgumentTokenizer.java b/src/main/java/seedu/address/logic/parser/ArgumentTokenizer.java
index 5c9aebfa488..3b55f19bbd1 100644
--- a/src/main/java/seedu/address/logic/parser/ArgumentTokenizer.java
+++ b/src/main/java/seedu/address/logic/parser/ArgumentTokenizer.java
@@ -2,9 +2,15 @@
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.HashSet;
import java.util.List;
+import java.util.Set;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
import java.util.stream.Collectors;
+import seedu.address.logic.parser.exceptions.ParseException;
+
/**
* Tokenizes arguments string of the form: {@code preamble value value ...}
* e.g. {@code some preamble text t/ 11.00 t/12.00 k/ m/ July} where prefixes are {@code t/ k/ m/}.
@@ -145,4 +151,42 @@ Prefix getPrefix() {
}
}
+ /**
+ * Returns List containing user's prefixes.
+ * This function is required since user's can chain prefixes for view.
+ *
+ * @param argsString Input arguments.
+ * @return List containing user's prefixes.
+ */
+ private static List extractPrefixes(String argsString) {
+ Pattern pattern = Pattern.compile("\\b[^\\s]+/");
+ Matcher matcher = pattern.matcher(argsString);
+ List prefixes = new ArrayList<>();
+ while (matcher.find()) {
+ prefixes.add(matcher.group().trim());
+ }
+ System.out.println(prefixes);
+ return prefixes;
+ }
+
+ /**
+ * Returns ArgumentMultimap object
+ * Converts user's chained prefixes into a list.
+ *
+ * @param argsString User's input.
+ * @return ArgumentMultimap object.
+ * @throws ParseException if duplicate prefixes are found.
+ */
+ public static ArgumentMultimap viewTokenize(String argsString) throws ParseException {
+ List extractedPrefixes = extractPrefixes(argsString);
+
+ Set uniquePrefixes = new HashSet<>(extractedPrefixes);
+ assert(uniquePrefixes.size() == extractedPrefixes.size());
+ if (uniquePrefixes.size() != extractedPrefixes.size()) {
+ throw new ParseException("Duplicate prefix detected in input.");
+ }
+ Prefix[] prefixArray = extractedPrefixes.stream().map(Prefix::new).toArray(Prefix[]::new);
+ List positions = findAllPrefixPositions(argsString, prefixArray);
+ return extractArguments(argsString, positions);
+ }
}
diff --git a/src/main/java/seedu/address/logic/parser/BirthdayCommandParser.java b/src/main/java/seedu/address/logic/parser/BirthdayCommandParser.java
new file mode 100644
index 00000000000..f9c3c7a2eb1
--- /dev/null
+++ b/src/main/java/seedu/address/logic/parser/BirthdayCommandParser.java
@@ -0,0 +1,71 @@
+package seedu.address.logic.parser;
+
+import static java.util.Objects.requireNonNull;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_MONTH;
+
+import java.time.LocalDate;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.stream.Stream;
+
+import seedu.address.logic.Messages;
+import seedu.address.logic.commands.BirthdayCommand;
+import seedu.address.logic.parser.exceptions.ParseException;
+import seedu.address.model.person.MatchingBirthdayPredicate;
+import seedu.address.model.person.Month;
+
+/**
+ * Parses input argument and returns a new BirthdayCommand object.
+ */
+public class BirthdayCommandParser implements Parser {
+ /**
+ * Parses the given {@code String} of arguments in the context of the BirthdayCommand
+ * and returns a BirthdayCommand object for execution.
+ * @param args the given month
+ * @return a BirthdayCommand object
+ * @throws ParseException if the user input does not conform the expected format
+ */
+ public BirthdayCommand parse(String args) throws ParseException {
+ requireNonNull(args);
+ if (args.trim().isEmpty()) {
+ LocalDate date = LocalDate.now();
+ List monthList = new ArrayList<>();
+ monthList.add(new Month(date.getMonthValue()));
+ MatchingBirthdayPredicate predicate = new MatchingBirthdayPredicate(monthList);
+ return new BirthdayCommand(predicate);
+ }
+ ArgumentMultimap argMultimap = ArgumentTokenizer.tokenize(args, PREFIX_MONTH);
+ if (!arePrefixesPresent(argMultimap, PREFIX_MONTH)) {
+ throw new ParseException(Messages.MESSAGE_INVALID_MONTH_PREFIX);
+ } else {
+ argMultimap.verifyNoDuplicatePrefixesFor(PREFIX_MONTH);
+ List monthList = parseBirthday(argMultimap.getValue(PREFIX_MONTH).get());
+ MatchingBirthdayPredicate predicate = new MatchingBirthdayPredicate(monthList);
+ return new BirthdayCommand(predicate);
+ }
+ }
+
+ /**
+ * Parses a list of months given
+ * @param args Months as strings
+ * @return a list of Months
+ */
+ private List parseBirthday(String args) throws ParseException {
+ args = args.trim();
+ List monthList = new ArrayList<>();
+ String[] months = args.split(",");
+ for (String month : months) {
+ int monthValue = ParserUtil.parseMonth(month);
+ monthList.add(new Month(monthValue));
+ }
+ return monthList;
+ }
+
+ /**
+ * Returns true if none of the prefixes contains empty {@code Optional} values in the given
+ * {@code ArgumentMultimap}.
+ */
+ private static boolean arePrefixesPresent(ArgumentMultimap argumentMultimap, Prefix... prefixes) {
+ return Stream.of(prefixes).allMatch(prefix -> argumentMultimap.getValue(prefix).isPresent());
+ }
+}
diff --git a/src/main/java/seedu/address/logic/parser/ClaimCommandParser.java b/src/main/java/seedu/address/logic/parser/ClaimCommandParser.java
new file mode 100644
index 00000000000..7104523a016
--- /dev/null
+++ b/src/main/java/seedu/address/logic/parser/ClaimCommandParser.java
@@ -0,0 +1,51 @@
+package seedu.address.logic.parser;
+
+import static java.util.Objects.requireNonNull;
+import static seedu.address.logic.Messages.MESSAGE_INVALID_COMMAND_FORMAT;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_CLAIM_AMOUNT;
+
+import seedu.address.commons.core.index.Index;
+import seedu.address.logic.commands.ClaimCommand;
+import seedu.address.logic.parser.exceptions.ParseException;
+import seedu.address.model.person.Claim;
+
+
+/**
+ * Parses input arguments and creates a new ClaimCommand object.
+ */
+public class ClaimCommandParser implements Parser {
+
+ /**
+ * Returns ClaimCommand which contains the employee-of-interest's index,
+ * boolean to represent subtraction/addition as well as the claim amount.
+ * Parser to parse out the index as well as claim amount
+ * (containing both symbol [+/-] and amount) based on delimiter of "$/".
+ * Checks are in place to ensure the index is inputted and claim amount consists of only digits.
+ *
+ * @param args String object which represents the user's input.
+ * @return ClaimCommand which consists of employee's index, subtraction/addition boolean indicator and claim amount.
+ * @throws ParseException Exception thrown either when index is not inputted or claim amount contains non-digits.
+ */
+ public ClaimCommand parse(String args) throws ParseException {
+ requireNonNull(args);
+ ArgumentMultimap argMultimap = ArgumentTokenizer.tokenize(args, PREFIX_CLAIM_AMOUNT);
+ argMultimap.verifyNoDuplicatePrefixesFor(PREFIX_CLAIM_AMOUNT);
+
+ Index index;
+ Claim claim;
+
+ try {
+ index = ParserUtil.parseIndex(argMultimap.getPreamble());
+ } catch (ParseException pe) {
+ throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, ClaimCommand.MESSAGE_EMPTY), pe);
+ }
+
+ if (argMultimap.getValue(PREFIX_CLAIM_AMOUNT).isPresent()) {
+ claim = ParserUtil.parseClaim(argMultimap.getValue(PREFIX_CLAIM_AMOUNT).get());
+ } else {
+ throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, ClaimCommand.AMOUNT_EMPTY));
+ }
+
+ return new ClaimCommand(index, claim.isSubtract, claim.amount);
+ }
+}
diff --git a/src/main/java/seedu/address/logic/parser/CliSyntax.java b/src/main/java/seedu/address/logic/parser/CliSyntax.java
index 75b1a9bf119..0aa2c9843b4 100644
--- a/src/main/java/seedu/address/logic/parser/CliSyntax.java
+++ b/src/main/java/seedu/address/logic/parser/CliSyntax.java
@@ -10,6 +10,11 @@ public class CliSyntax {
public static final Prefix PREFIX_PHONE = new Prefix("p/");
public static final Prefix PREFIX_EMAIL = new Prefix("e/");
public static final Prefix PREFIX_ADDRESS = new Prefix("a/");
+ public static final Prefix PREFIX_SALARY = new Prefix("s/");
+ public static final Prefix PREFIX_CLAIM_BUDGET = new Prefix("b/");
+ public static final Prefix PREFIX_DEPARTMENT = new Prefix("d/");
+ public static final Prefix PREFIX_DOB = new Prefix("dob/");
+ public static final Prefix PREFIX_MONTH = new Prefix("m/");
public static final Prefix PREFIX_TAG = new Prefix("t/");
-
+ public static final Prefix PREFIX_CLAIM_AMOUNT = new Prefix("$/");
}
diff --git a/src/main/java/seedu/address/logic/parser/EditCommandParser.java b/src/main/java/seedu/address/logic/parser/EditCommandParser.java
index 46b3309a78b..c165d11afd3 100644
--- a/src/main/java/seedu/address/logic/parser/EditCommandParser.java
+++ b/src/main/java/seedu/address/logic/parser/EditCommandParser.java
@@ -3,21 +3,17 @@
import static java.util.Objects.requireNonNull;
import static seedu.address.logic.Messages.MESSAGE_INVALID_COMMAND_FORMAT;
import static seedu.address.logic.parser.CliSyntax.PREFIX_ADDRESS;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_DEPARTMENT;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_DOB;
import static seedu.address.logic.parser.CliSyntax.PREFIX_EMAIL;
import static seedu.address.logic.parser.CliSyntax.PREFIX_NAME;
import static seedu.address.logic.parser.CliSyntax.PREFIX_PHONE;
-import static seedu.address.logic.parser.CliSyntax.PREFIX_TAG;
-
-import java.util.Collection;
-import java.util.Collections;
-import java.util.Optional;
-import java.util.Set;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_SALARY;
import seedu.address.commons.core.index.Index;
import seedu.address.logic.commands.EditCommand;
import seedu.address.logic.commands.EditCommand.EditPersonDescriptor;
import seedu.address.logic.parser.exceptions.ParseException;
-import seedu.address.model.tag.Tag;
/**
* Parses input arguments and creates a new EditCommand object
@@ -32,7 +28,8 @@ public class EditCommandParser implements Parser {
public EditCommand parse(String args) throws ParseException {
requireNonNull(args);
ArgumentMultimap argMultimap =
- ArgumentTokenizer.tokenize(args, PREFIX_NAME, PREFIX_PHONE, PREFIX_EMAIL, PREFIX_ADDRESS, PREFIX_TAG);
+ ArgumentTokenizer.tokenize(args, PREFIX_NAME, PREFIX_PHONE, PREFIX_EMAIL, PREFIX_ADDRESS,
+ PREFIX_SALARY, PREFIX_DEPARTMENT, PREFIX_DOB);
Index index;
@@ -42,7 +39,8 @@ public EditCommand parse(String args) throws ParseException {
throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, EditCommand.MESSAGE_USAGE), pe);
}
- argMultimap.verifyNoDuplicatePrefixesFor(PREFIX_NAME, PREFIX_PHONE, PREFIX_EMAIL, PREFIX_ADDRESS);
+ argMultimap.verifyNoDuplicatePrefixesFor(PREFIX_NAME, PREFIX_PHONE, PREFIX_EMAIL, PREFIX_ADDRESS,
+ PREFIX_SALARY, PREFIX_DEPARTMENT, PREFIX_DOB);
EditPersonDescriptor editPersonDescriptor = new EditPersonDescriptor();
@@ -58,28 +56,20 @@ public EditCommand parse(String args) throws ParseException {
if (argMultimap.getValue(PREFIX_ADDRESS).isPresent()) {
editPersonDescriptor.setAddress(ParserUtil.parseAddress(argMultimap.getValue(PREFIX_ADDRESS).get()));
}
- parseTagsForEdit(argMultimap.getAllValues(PREFIX_TAG)).ifPresent(editPersonDescriptor::setTags);
-
+ if (argMultimap.getValue(PREFIX_SALARY).isPresent()) {
+ editPersonDescriptor.setSalary(ParserUtil.parseMoney(argMultimap.getValue(PREFIX_SALARY).get()));
+ }
+ if (argMultimap.getValue(PREFIX_DEPARTMENT).isPresent()) {
+ editPersonDescriptor.setDepartment(
+ ParserUtil.parseDepartment(argMultimap.getValue(PREFIX_DEPARTMENT).get()));
+ }
+ if (argMultimap.getValue(PREFIX_DOB).isPresent()) {
+ editPersonDescriptor.setDob(ParserUtil.parseDob(argMultimap.getValue(PREFIX_DOB).get()));
+ }
if (!editPersonDescriptor.isAnyFieldEdited()) {
throw new ParseException(EditCommand.MESSAGE_NOT_EDITED);
}
return new EditCommand(index, editPersonDescriptor);
}
-
- /**
- * Parses {@code Collection tags} into a {@code Set} if {@code tags} is non-empty.
- * If {@code tags} contain only one element which is an empty string, it will be parsed into a
- * {@code Set} containing zero tags.
- */
- private Optional> parseTagsForEdit(Collection tags) throws ParseException {
- assert tags != null;
-
- if (tags.isEmpty()) {
- return Optional.empty();
- }
- Collection tagSet = tags.size() == 1 && tags.contains("") ? Collections.emptySet() : tags;
- return Optional.of(ParserUtil.parseTags(tagSet));
- }
-
}
diff --git a/src/main/java/seedu/address/logic/parser/ExportCommandParser.java b/src/main/java/seedu/address/logic/parser/ExportCommandParser.java
new file mode 100644
index 00000000000..e13ae792ea0
--- /dev/null
+++ b/src/main/java/seedu/address/logic/parser/ExportCommandParser.java
@@ -0,0 +1,50 @@
+package seedu.address.logic.parser;
+
+import static java.util.Objects.requireNonNull;
+
+import seedu.address.logic.Messages;
+import seedu.address.logic.commands.ExportCommand;
+import seedu.address.logic.parser.exceptions.ParseException;
+
+/**
+ * .Performs exporting of employee's data into csv format.
+ */
+public class ExportCommandParser implements Parser {
+
+ /**
+ * Returns ExportCommand containing the name in which user wants to name the file.
+ *
+ * @param args CLI arguments that user typed.
+ * @return ExportCommand Object containing the file name user provided.
+ * @throws ParseException If user provides 2 or more filenames.
+ */
+ @Override
+ public ExportCommand parse(String args) throws ParseException {
+ requireNonNull(args);
+ ArgumentMultimap argMultimap = ArgumentTokenizer.viewTokenize(args);
+ String nameArgs = argMultimap.getPreamble();
+ boolean validFileName = nameChecker(nameArgs);
+ if (!validFileName) {
+ throw new ParseException(Messages.WRONG_EXPORT_FILE_NAME_FAILURE);
+ }
+ return new ExportCommand(nameArgs);
+ }
+
+ /**
+ * Returns boolean to check if user Args only contains exactly one filename.
+ *
+ * @param nameArgs String after the export word.
+ * @return boolean true if nameArgs is valid else false.
+ */
+ public boolean nameChecker(String nameArgs) {
+ String[] variousNames = nameArgs.split(" ");
+ int strlen = variousNames.length;
+ if (strlen == 1 && nameArgs.equals("")) {
+ return false;
+ } else if (strlen > 1) {
+ return false;
+ } else {
+ return true;
+ }
+ }
+}
diff --git a/src/main/java/seedu/address/logic/parser/LeaveCommandParser.java b/src/main/java/seedu/address/logic/parser/LeaveCommandParser.java
new file mode 100644
index 00000000000..bfd4c79baee
--- /dev/null
+++ b/src/main/java/seedu/address/logic/parser/LeaveCommandParser.java
@@ -0,0 +1,82 @@
+package seedu.address.logic.parser;
+
+import static java.util.Objects.requireNonNull;
+import static seedu.address.logic.Messages.MESSAGE_INVALID_COMMAND_FORMAT;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_MONTH;
+
+import seedu.address.commons.core.index.Index;
+import seedu.address.logic.commands.LeaveCommand;
+import seedu.address.logic.parser.exceptions.ParseException;
+import seedu.address.model.person.Leave;
+
+/**
+ * Parses input arguments and creates a new LeaveCommand object
+ */
+public class LeaveCommandParser implements Parser {
+
+ /**
+ * Parses the given {@code String} of arguments in the context of the LeaveCommand
+ * and returns an LeaveCommand object for execution.
+ * @throws ParseException if the user input does not conform the expected format
+ */
+ public LeaveCommand parse(String args) throws ParseException {
+ requireNonNull(args);
+ ArgumentMultimap argMultimap = ArgumentTokenizer.tokenize(args, PREFIX_MONTH);
+
+ Index index;
+ try {
+ index = ParserUtil.parseIndex(argMultimap.getPreamble());
+ } catch (ParseException pe) {
+ throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, LeaveCommand.MESSAGE_USAGE), pe);
+ }
+ argMultimap.verifyNoDuplicatePrefixesFor(PREFIX_MONTH);
+ if (argMultimap.getValue(PREFIX_MONTH).isPresent()) {
+ return new LeaveCommand(index, parseLeave(argMultimap.getValue(PREFIX_MONTH).get()));
+ } else {
+ throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, LeaveCommand.MESSAGE_USAGE));
+ }
+ }
+
+ private String parseLeave(String arg) throws ParseException {
+ arg = arg.trim();
+ if (arg.length() == 0) {
+ throw new ParseException(LeaveCommand.MESSAGE_EMPTY + LeaveCommand.MESSAGE_USAGE);
+ }
+ int len = arg.split("\\s+").length;
+ if (len > 1) {
+ throw new ParseException(LeaveCommand.MESSAGE_SPACES_DETECTED + LeaveCommand.MESSAGE_USAGE);
+ }
+ StringBuilder months = new StringBuilder(Leave.NO_LEAVE);
+ String[] args = arg.split(",");
+ for (int i = 0; i < args.length; i++) {
+ if (args[i].length() <= 0 || args[i].length() > 3) {
+ throw new ParseException(LeaveCommand.MESSAGE_INVALID_MONTH + LeaveCommand.MESSAGE_USAGE);
+ }
+ try {
+ if (args[i].charAt(0) == '-') {
+ Integer month = Integer.valueOf(args[i].substring(1));
+ if (month < 1 || month > 12) {
+ throw new ParseException(LeaveCommand.MESSAGE_INVALID_MONTH + LeaveCommand.MESSAGE_USAGE);
+ }
+ if (months.charAt(month - 1) == '+') {
+ throw new ParseException(LeaveCommand.MESSAGE_AMBIGUOUS + LeaveCommand.MESSAGE_USAGE);
+ }
+ months.setCharAt(month - 1, '-');
+ } else {
+ Integer month = Integer.valueOf(args[i]);
+ if (month < 1 || month > 12) {
+ throw new ParseException(LeaveCommand.MESSAGE_INVALID_MONTH + LeaveCommand.MESSAGE_USAGE);
+ }
+ if (months.charAt(month - 1) == '-') {
+ throw new ParseException(LeaveCommand.MESSAGE_AMBIGUOUS + LeaveCommand.MESSAGE_USAGE);
+ }
+ months.setCharAt(month - 1, '+');
+ }
+ } catch (NumberFormatException e) {
+ throw new ParseException(LeaveCommand.MESSAGE_INVALID_MONTH + LeaveCommand.MESSAGE_USAGE);
+ }
+ }
+ return months.toString();
+ }
+
+}
diff --git a/src/main/java/seedu/address/logic/parser/ListCommandParser.java b/src/main/java/seedu/address/logic/parser/ListCommandParser.java
new file mode 100644
index 00000000000..9e579183d57
--- /dev/null
+++ b/src/main/java/seedu/address/logic/parser/ListCommandParser.java
@@ -0,0 +1,47 @@
+package seedu.address.logic.parser;
+
+import static java.util.Objects.requireNonNull;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_DEPARTMENT;
+
+import seedu.address.logic.Messages;
+import seedu.address.logic.commands.ListCommand;
+import seedu.address.logic.parser.exceptions.ParseException;
+import seedu.address.model.person.Department;
+import seedu.address.model.person.MatchingDepartmentPredicate;
+
+
+/**
+ * Parses input arguments and creates a new ListCommand object
+ */
+public class ListCommandParser implements Parser {
+
+ /**
+ * Parses {@code userInput} into a command and returns it.
+ *
+ * @param args arguments
+ * @throws ParseException if {@code userInput} does not conform the expected format
+ */
+ @Override
+ public ListCommand parse(String args) throws ParseException {
+ requireNonNull(args);
+ ArgumentMultimap argMultimap =
+ ArgumentTokenizer.tokenize(args, PREFIX_DEPARTMENT);
+
+ if (argMultimap.getValue(PREFIX_DEPARTMENT).isEmpty()) {
+ try {
+ String secondArg = args.split(" ")[1];
+ throw new ParseException(String.format(Messages.MESSAGE_INVALID_COMMAND_FORMAT,
+ Messages.MESSAGE_LIST_COMMAND_FORMAT));
+ } catch (IndexOutOfBoundsException e) {
+ return new ListCommand();
+ }
+ }
+ argMultimap.verifyNoDuplicatePrefixesFor(PREFIX_DEPARTMENT);
+ try {
+ Department filteringDepartment = new Department(argMultimap.getValue(PREFIX_DEPARTMENT).get());
+ return new ListCommand(new MatchingDepartmentPredicate(filteringDepartment));
+ } catch (IllegalArgumentException e) {
+ throw new ParseException(Messages.MESSAGE_EMPTY_DEPARTMENT_FILTER);
+ }
+ }
+}
diff --git a/src/main/java/seedu/address/logic/parser/ParserUtil.java b/src/main/java/seedu/address/logic/parser/ParserUtil.java
index b117acb9c55..c100ea3a9e8 100644
--- a/src/main/java/seedu/address/logic/parser/ParserUtil.java
+++ b/src/main/java/seedu/address/logic/parser/ParserUtil.java
@@ -8,9 +8,15 @@
import seedu.address.commons.core.index.Index;
import seedu.address.commons.util.StringUtil;
+import seedu.address.logic.Messages;
import seedu.address.logic.parser.exceptions.ParseException;
import seedu.address.model.person.Address;
+import seedu.address.model.person.Birthday;
+import seedu.address.model.person.Claim;
+import seedu.address.model.person.Department;
import seedu.address.model.person.Email;
+import seedu.address.model.person.Money;
+import seedu.address.model.person.Month;
import seedu.address.model.person.Name;
import seedu.address.model.person.Phone;
import seedu.address.model.tag.Tag;
@@ -95,6 +101,74 @@ public static Email parseEmail(String email) throws ParseException {
return new Email(trimmedEmail);
}
+ /**
+ * Parses a {@code String dollar amount} into a {@code Money}.
+ * Leading and trailing whitespaces will be trimmed.
+ *
+ * @throws ParseException if the given {@code dollar amount} is invalid.
+ */
+ public static Money parseMoney(String money) throws ParseException {
+ requireNonNull(money);
+ String trimmed = money.trim();
+ if (!Money.isValidMoney(trimmed)) {
+ throw new ParseException(Money.MESSAGE_CONSTRAINTS);
+ }
+ return new Money(trimmed);
+ }
+
+ /**
+ * Parses a {@code String department} into a {@code Department}.
+ * Leading and trailing whitespaces will be trimmed.
+ *
+ * @throws ParseException if the given {@code department} is invalid.
+ */
+ public static Department parseDepartment(String department) throws ParseException {
+ requireNonNull(department);
+ String trimmed = department.trim();
+ if (!Department.isValidDepartment(trimmed)) {
+ throw new ParseException(Department.MESSAGE_CONSTRAINTS);
+ }
+ return new Department(trimmed);
+ }
+
+ /**
+ * Parses a {@code String dob} into a {@code Birthday}.
+ * Leading and trailing whitespaces will be trimmed.
+ *
+ * @throws ParseException if the given {@code dob} is invalid.
+ */
+ public static Birthday parseDob(String dob) throws ParseException {
+ requireNonNull(dob);
+ String trimmed = dob.trim();
+ if (!Birthday.isValidDob(trimmed)) {
+ throw new ParseException(Birthday.MESSAGE_CONSTRAINTS);
+ }
+ return new Birthday(trimmed);
+ }
+
+ /**
+ * Returns a Claim Object upon successful checks.
+ * Checks consist of ensuring user inputted either + or - before the claim amount.
+ * Checks also consist of ensuring the claim amount contains only digits.
+ *
+ * @param claimAmount String Object which is parsed out from the user's command line input. Example: +500
+ * @return Claim Object which stores claim amount as well as boolean to indicate addition/subtraction.
+ * @throws ParseException Exception when no symbols were inputted or amount consists of non-digits.
+ */
+ public static Claim parseClaim(String claimAmount) throws ParseException {
+ requireNonNull(claimAmount);
+ String trimmed = claimAmount.trim();
+ if (trimmed.length() > 14) {
+ throw new ParseException(Messages.TOO_LARGE_A_NUMBER);
+ }
+ if (!Claim.comtainsSymbol(trimmed)) {
+ throw new ParseException(Claim.NO_SYMBOLS_ERROR);
+ } else if (!Claim.isCorrectAmountType(trimmed)) {
+ throw new ParseException(Claim.ALPHABETS_ERROR);
+ }
+ return new Claim(trimmed);
+ }
+
/**
* Parses a {@code String tag} into a {@code Tag}.
* Leading and trailing whitespaces will be trimmed.
@@ -121,4 +195,28 @@ public static Set parseTags(Collection tags) throws ParseException
}
return tagSet;
}
+
+ /**
+ * Parses a {@code String month} into a {@code int monthNumber}.
+ * @param month String representation of the month number.
+ * @return the integer representation of the given month.
+ * @throws ParseException if the given {@code String month} is invalid.
+ */
+ public static int parseMonth(String month) throws ParseException {
+ requireNonNull(month);
+ String trimmed = month.trim();
+ if (!trimmed.matches(Month.VALIDATION_REGEX)) {
+ throw new ParseException(Month.MESSAGE_CONSTRAINTS_BLANK_MONTH);
+ } else if (Month.containsAlphabetsOrDecimals(trimmed)) {
+ throw new ParseException(Month.MESSAGE_CONSTRAINTS_MONTH_INVALID_CHARACTERS);
+ } else if (Month.isZeroMonth(trimmed)) {
+ throw new ParseException(Month.MESSAGE_CONSTRAINTS_ZERO_MONTH);
+ } else if (Month.isNegativeMonth(trimmed)) {
+ throw new ParseException(Month.MESSAGE_CONSTRAINTS_NEGATIVE_MONTH);
+ } else if (!Month.isValidMonth(trimmed)) {
+ throw new ParseException(Month.MESSAGE_CONSTRAINTS_MONTH_OVER);
+ } else {
+ return Integer.parseInt(trimmed);
+ }
+ }
}
diff --git a/src/main/java/seedu/address/logic/parser/SortCommandParser.java b/src/main/java/seedu/address/logic/parser/SortCommandParser.java
new file mode 100644
index 00000000000..6e61372c726
--- /dev/null
+++ b/src/main/java/seedu/address/logic/parser/SortCommandParser.java
@@ -0,0 +1,41 @@
+package seedu.address.logic.parser;
+
+import static seedu.address.logic.Messages.MESSAGE_INVALID_COMMAND_FORMAT;
+
+import seedu.address.logic.commands.SortCommand;
+import seedu.address.logic.parser.exceptions.ParseException;
+
+/**
+ * Parses input arguments and creates a new SortCommand object
+ */
+public class SortCommandParser implements Parser {
+
+ /**
+ * Parses the given {@code String} of arguments in the context of the SortCommand
+ * and returns a SortCommand object for execution.
+ * @throws ParseException if the user input does not conform the expected format
+ */
+ public SortCommand parse(String args) throws ParseException {
+ String trimmedArgs = args.trim();
+ if (trimmedArgs.isEmpty()) {
+ throw new ParseException(
+ String.format(MESSAGE_INVALID_COMMAND_FORMAT, SortCommand.MESSAGE_USAGE));
+ }
+
+ String[] params = trimmedArgs.split("\\s+");
+ if (params.length > 2 || params.length == 0) {
+ throw new ParseException(
+ String.format(MESSAGE_INVALID_COMMAND_FORMAT, SortCommand.MESSAGE_USAGE));
+ }
+ if (params.length == 2) {
+ if (params[1].trim().equals(SortCommand.SORT_DESC)) {
+ return new SortCommand(params[0], true);
+ }
+ throw new ParseException(
+ String.format(MESSAGE_INVALID_COMMAND_FORMAT, SortCommand.MESSAGE_USAGE));
+ }
+
+ return new SortCommand(params[0]);
+ }
+
+}
diff --git a/src/main/java/seedu/address/logic/parser/ThemeCommandParser.java b/src/main/java/seedu/address/logic/parser/ThemeCommandParser.java
new file mode 100644
index 00000000000..a4cf1af4760
--- /dev/null
+++ b/src/main/java/seedu/address/logic/parser/ThemeCommandParser.java
@@ -0,0 +1,38 @@
+package seedu.address.logic.parser;
+
+import static java.util.Objects.requireNonNull;
+
+import seedu.address.logic.Messages;
+import seedu.address.logic.commands.ThemeCommand;
+import seedu.address.logic.parser.exceptions.ParseException;
+
+/**
+ * Parses input arguments and creates a new ThemeCommand object
+ */
+public class ThemeCommandParser implements Parser {
+
+ /**
+ * Parses {@code userInput} into a command and returns it.
+ *
+ * @param args arguments given
+ * @throws ParseException if {@code userInput} does not conform the expected format
+ */
+ @Override
+ public ThemeCommand parse(String args) throws ParseException {
+ requireNonNull(args);
+ switch (args.trim()) {
+ case "dark":
+ return new ThemeCommand("DarkTheme.css");
+ case "light":
+ return new ThemeCommand("LightTheme.css");
+ case "red":
+ return new ThemeCommand("RedTheme.css");
+ case "green":
+ return new ThemeCommand("GreenTheme.css");
+ case "blue":
+ return new ThemeCommand("BlueTheme.css");
+ default:
+ throw new ParseException(Messages.MESSAGE_INVALID_THEME);
+ }
+ }
+}
diff --git a/src/main/java/seedu/address/logic/parser/ViewCommandParser.java b/src/main/java/seedu/address/logic/parser/ViewCommandParser.java
new file mode 100644
index 00000000000..f989bbb81e1
--- /dev/null
+++ b/src/main/java/seedu/address/logic/parser/ViewCommandParser.java
@@ -0,0 +1,142 @@
+package seedu.address.logic.parser;
+
+import static java.util.Objects.requireNonNull;
+import static seedu.address.logic.Messages.MESSAGE_INVALID_COMMAND_FORMAT;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_ADDRESS;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_CLAIM_BUDGET;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_DEPARTMENT;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_DOB;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_EMAIL;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_NAME;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_PHONE;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_SALARY;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+
+import seedu.address.commons.core.index.Index;
+import seedu.address.logic.commands.ViewCommand;
+import seedu.address.logic.parser.exceptions.ParseException;
+
+/**
+ * Parses input arguments and creates a new ViewCommand object.
+ */
+public class ViewCommandParser implements Parser {
+
+ public static final String NAME_IDENTIFIER = "Name";
+ public static final String PHONE_IDENTIFIER = "Phone";
+ public static final String EMAIL_IDENTIFIER = "Email";
+ public static final String ADDRESS_IDENTIFIER = "Address";
+ public static final String SALARY_IDENTIFIER = "Salary";
+ public static final String CLAIM_BUDGET = "Claim Budget";
+ public static final String DEPARTMENT = "Department";
+ public static final String BIRTHDAY = "Birthday";
+
+ public static final List VALIDPREFIXES = new ArrayList<>(List.of(PREFIX_NAME,
+ PREFIX_PHONE, PREFIX_EMAIL, PREFIX_ADDRESS, PREFIX_SALARY, PREFIX_CLAIM_BUDGET,
+ PREFIX_DEPARTMENT, PREFIX_DOB));
+
+ /**
+ * Returns ViewCommand Object based on user's input.
+ * HashMap is generated which contains actions and indexes.
+ *
+ * @param args User's input to the system.
+ * @return ViewCommand Object.
+ * @throws ParseException if wrong prefix or no-prefix provided.
+ */
+ public ViewCommand parse(String args) throws ParseException {
+ requireNonNull(args);
+ ArgumentMultimap argMultimap = ArgumentTokenizer.viewTokenize(args);
+ HashMap> map = new HashMap<>();
+ validatePrefixes(argMultimap);
+ if (argMultimap.getAllValues(PREFIX_NAME).size() != 0) {
+ List resultIndex = relevantIndexes(argMultimap, PREFIX_NAME);
+ map.put(NAME_IDENTIFIER, new ArrayList<>(resultIndex));
+ }
+ if (argMultimap.getAllValues(PREFIX_PHONE).size() != 0) {
+ List resultIndex = relevantIndexes(argMultimap, PREFIX_PHONE);
+ map.put(PHONE_IDENTIFIER, new ArrayList<>(resultIndex));
+ }
+ if (argMultimap.getAllValues(PREFIX_EMAIL).size() != 0) {
+ List resultIndex = relevantIndexes(argMultimap, PREFIX_EMAIL);
+ map.put(EMAIL_IDENTIFIER, new ArrayList<>(resultIndex));
+ }
+ if (argMultimap.getAllValues(PREFIX_ADDRESS).size() != 0) {
+ List resultIndex = relevantIndexes(argMultimap, PREFIX_ADDRESS);
+ map.put(ADDRESS_IDENTIFIER, new ArrayList<>(resultIndex));
+ }
+ if (argMultimap.getAllValues(PREFIX_SALARY).size() != 0) {
+ List resultIndex = relevantIndexes(argMultimap, PREFIX_SALARY);
+ map.put(SALARY_IDENTIFIER, new ArrayList<>(resultIndex));
+ }
+ if (argMultimap.getAllValues(PREFIX_CLAIM_BUDGET).size() != 0) {
+ List resultIndex = relevantIndexes(argMultimap, PREFIX_CLAIM_BUDGET);
+ map.put(CLAIM_BUDGET, new ArrayList<>(resultIndex));
+ }
+ if (argMultimap.getAllValues(PREFIX_DEPARTMENT).size() != 0) {
+ List resultIndex = relevantIndexes(argMultimap, PREFIX_DEPARTMENT);
+ map.put(DEPARTMENT, new ArrayList<>(resultIndex));
+ }
+ if (argMultimap.getAllValues(PREFIX_DOB).size() != 0) {
+ List resultIndex = relevantIndexes(argMultimap, PREFIX_DOB);
+ map.put(BIRTHDAY, new ArrayList<>(resultIndex));
+ }
+ return new ViewCommand(map);
+ }
+
+ /**
+ * Returns List of all prefixes split by ",".
+ *
+ * @param arg List returned by getAllValues function.
+ * @return List containing all valid prefixes.
+ */
+ public List parseView(List arg) {
+ List splitList = new ArrayList<>();
+ for (String item : arg) {
+ String[] parts = item.split(",");
+ splitList.addAll(Arrays.asList(parts));
+ }
+ return splitList;
+ }
+
+ /**
+ * Returns a List informing us of the various indexes view operations have to be performed on.
+ *
+ * @param argumentMultimap Object containing the prefixes user typed.
+ * @param prefix Prefix Object that user wants to view.
+ * @return List containing all indexes the particular prefix operation has to be performed on.
+ * @throws ParseException If index is not of the correct format [Non-integer].
+ */
+ public List relevantIndexes(ArgumentMultimap argumentMultimap, Prefix prefix) throws ParseException {
+ List finalIndex = new ArrayList<>();
+ List listStringIndex = argumentMultimap.getAllValues(prefix);
+ List indexList = parseView(listStringIndex);
+ for (String stringIndex : indexList) {
+ finalIndex.add(ParserUtil.parseIndex(stringIndex));
+ }
+ return finalIndex;
+ }
+
+ /**
+ * Validates user's input in ensuring prefixes are given and are valid.
+ *
+ * @param argMultimap Object containing the prefixes user typed.
+ * @throws ParseException In the event user input wrong prefix or didn't input any prefix.
+ */
+ public void validatePrefixes(ArgumentMultimap argMultimap) throws ParseException {
+ List userGivenPrefixes = argMultimap.userPrefixes();
+ if (userGivenPrefixes.size() == 0) {
+ throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, ViewCommand.WRONG_PREFIX));
+ }
+ if (userGivenPrefixes.size() > 1) {
+ throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, ViewCommand.EXCESS_PREFIX));
+ }
+ for (Prefix prefix: userGivenPrefixes) {
+ if (!VALIDPREFIXES.contains(prefix)) {
+ throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, ViewCommand.WRONG_PREFIX));
+ }
+ }
+ }
+}
diff --git a/src/main/java/seedu/address/logic/parser/ViewLeaveCommandParser.java b/src/main/java/seedu/address/logic/parser/ViewLeaveCommandParser.java
new file mode 100644
index 00000000000..e960be3f021
--- /dev/null
+++ b/src/main/java/seedu/address/logic/parser/ViewLeaveCommandParser.java
@@ -0,0 +1,80 @@
+package seedu.address.logic.parser;
+
+import static java.util.Objects.requireNonNull;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_DEPARTMENT;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_MONTH;
+
+import java.util.function.Predicate;
+
+import seedu.address.logic.Messages;
+import seedu.address.logic.commands.ViewLeaveCommand;
+import seedu.address.logic.parser.exceptions.ParseException;
+import seedu.address.model.person.Department;
+import seedu.address.model.person.HasLeaveAnyMonthPredicate;
+import seedu.address.model.person.HasLeaveThisMonthPredicate;
+import seedu.address.model.person.MatchingDepartmentPredicate;
+import seedu.address.model.person.Person;
+
+/**
+ * Parses input arguments and creates a new ViewLeaveCommand object
+ */
+public class ViewLeaveCommandParser implements Parser {
+
+ /**
+ * Parses {@code userInput} into a command and returns it.
+ *
+ * @param args
+ * @throws ParseException if {@code userInput} does not conform the expected format
+ */
+ @Override
+ public ViewLeaveCommand parse(String args) throws ParseException {
+ requireNonNull(args);
+ ArgumentMultimap argMultimap =
+ ArgumentTokenizer.tokenize(args, PREFIX_MONTH, PREFIX_DEPARTMENT);
+ Predicate combinedPredicate = new HasLeaveAnyMonthPredicate();
+ if (argMultimap.getValue(PREFIX_MONTH).isEmpty()
+ && argMultimap.getValue(PREFIX_DEPARTMENT).isEmpty()) {
+ String[] secondArg = args.split(" ");
+ if (secondArg.length != 1) {
+ throw new ParseException(String.format(Messages.MESSAGE_INVALID_COMMAND_FORMAT,
+ Messages.MESSAGE_VIEW_LIST_COMMAND_FORMAT));
+ }
+ }
+ if (argMultimap.getValue(PREFIX_MONTH).isPresent()) {
+ argMultimap.verifyNoDuplicatePrefixesFor(PREFIX_MONTH);
+ String monthArgs = argMultimap.getValue(PREFIX_MONTH).get();
+ if (monthArgs.isEmpty()) {
+ throw new ParseException(Messages.MESSAGE_EMPTY_MONTH_LEAVE_FILTER);
+ }
+ String[] months = monthArgs.split(",");
+ int len = monthArgs.split(" ").length;
+ if (len > 1) {
+ throw new ParseException(Messages.MESSAGE_MONTHS_SPACES_DETECTED
+ + Messages.MESSAGE_VIEW_LIST_COMMAND_FORMAT);
+ }
+ Predicate combinedMonthsPredicate = new HasLeaveAnyMonthPredicate().negate();
+ for (String month: months) {
+ try {
+ if (Integer.parseInt(month) < 1 || Integer.parseInt(month) > 12) {
+ throw new ParseException(Messages.MESSAGE_INVALID_MONTH);
+ }
+ } catch (NumberFormatException e) {
+ throw new ParseException(Messages.MESSAGE_INVALID_MONTH);
+ }
+
+ combinedMonthsPredicate = combinedMonthsPredicate.or(new HasLeaveThisMonthPredicate(month));
+ }
+ combinedPredicate = combinedPredicate.and(combinedMonthsPredicate);
+ }
+ if (argMultimap.getValue(PREFIX_DEPARTMENT).isPresent()) {
+ try {
+ argMultimap.verifyNoDuplicatePrefixesFor(PREFIX_DEPARTMENT);
+ Department filteringDepartment = new Department(argMultimap.getValue(PREFIX_DEPARTMENT).get());
+ combinedPredicate = combinedPredicate.and(new MatchingDepartmentPredicate(filteringDepartment));
+ } catch (IllegalArgumentException e) {
+ throw new ParseException(Messages.MESSAGE_EMPTY_DEPARTMENT_FILTER);
+ }
+ }
+ return new ViewLeaveCommand(combinedPredicate);
+ }
+}
diff --git a/src/main/java/seedu/address/model/AddressBook.java b/src/main/java/seedu/address/model/AddressBook.java
index 73397161e84..63a47db1d02 100644
--- a/src/main/java/seedu/address/model/AddressBook.java
+++ b/src/main/java/seedu/address/model/AddressBook.java
@@ -2,6 +2,7 @@
import static java.util.Objects.requireNonNull;
+import java.util.Comparator;
import java.util.List;
import javafx.collections.ObservableList;
@@ -127,4 +128,8 @@ public boolean equals(Object other) {
public int hashCode() {
return persons.hashCode();
}
+
+ public void sort(Comparator comparator) {
+ persons.sort(comparator);
+ }
}
diff --git a/src/main/java/seedu/address/model/AddressBookList.java b/src/main/java/seedu/address/model/AddressBookList.java
new file mode 100644
index 00000000000..4008ee2acb3
--- /dev/null
+++ b/src/main/java/seedu/address/model/AddressBookList.java
@@ -0,0 +1,90 @@
+package seedu.address.model;
+
+import static seedu.address.commons.util.AppUtil.checkArgument;
+
+import java.util.ArrayList;
+
+/**
+ * Represents the history of AddressBook to allow undo and redo.
+ */
+public class AddressBookList extends ArrayList {
+ public static final String REDO_ERROR_MESSAGE = "There is no command to redo! "
+ + "The command to be redone need to previously modified the employee list.";
+ public static final String UNDO_ERROR_MESSAGE = "There is no command to undo! "
+ + "The command to be undone need to previously modified the employee list.";
+
+ private ArrayList pastCommands = new ArrayList();
+ private int index;
+
+ /**
+ * Initializes the AddressBookList.
+ */
+ public AddressBookList() {
+ super();
+ index = -1;
+ }
+
+ @Override
+ public boolean add(AddressBook addressBook) {
+ index++;
+ if (index < this.size()) {
+ this.removeRange(index, this.size());
+ while (this.pastCommands.size() >= index) {
+ this.pastCommands.remove(pastCommands.size() - 1);
+ }
+ }
+ return super.add(addressBook);
+ }
+
+ /**
+ * Returns the AddressBook at given index.
+ * @return
+ */
+ public AddressBook getAddressBook() {
+ return super.get(index);
+ }
+
+ /**
+ * Updates the current AddressBook to the previous version.
+ * @return AddressBook
+ */
+ public AddressBook undo() {
+ checkArgument(index > 0, UNDO_ERROR_MESSAGE);
+ index--;
+ return getAddressBook();
+ }
+
+ /**
+ * Updates the current AddressBook to the latest undone version.
+ * @return AddressBook
+ */
+ public AddressBook redo() {
+ checkArgument(index < super.size() - 1, REDO_ERROR_MESSAGE);
+ index++;
+ return getAddressBook();
+ }
+
+ /**
+ * Stores the history of command texts.
+ * @param commandText
+ */
+ public void addCommandText(String commandText) {
+ this.pastCommands.add(commandText);
+ }
+
+ /**
+ * Gets the previous command text.
+ * @return previous command text
+ */
+ public String undoPastCommand() {
+ return this.pastCommands.get(index);
+ }
+
+ /**
+ * Gets the latest undone command text.
+ * @return latest undone command text
+ */
+ public String redoPastCommand() {
+ return this.pastCommands.get(index - 1);
+ }
+}
diff --git a/src/main/java/seedu/address/model/Model.java b/src/main/java/seedu/address/model/Model.java
index d54df471c1f..42937d5b1e2 100644
--- a/src/main/java/seedu/address/model/Model.java
+++ b/src/main/java/seedu/address/model/Model.java
@@ -1,6 +1,7 @@
package seedu.address.model;
import java.nio.file.Path;
+import java.util.Comparator;
import java.util.function.Predicate;
import javafx.collections.ObservableList;
@@ -84,4 +85,12 @@ public interface Model {
* @throws NullPointerException if {@code predicate} is null.
*/
void updateFilteredPersonList(Predicate predicate);
+
+ String undo();
+
+ String redo();
+
+ void addCommandText(String commandText);
+
+ void sort(Comparator comparator);
}
diff --git a/src/main/java/seedu/address/model/ModelManager.java b/src/main/java/seedu/address/model/ModelManager.java
index 57bc563fde6..38c9de12091 100644
--- a/src/main/java/seedu/address/model/ModelManager.java
+++ b/src/main/java/seedu/address/model/ModelManager.java
@@ -4,6 +4,7 @@
import static seedu.address.commons.util.CollectionUtil.requireAllNonNull;
import java.nio.file.Path;
+import java.util.Comparator;
import java.util.function.Predicate;
import java.util.logging.Logger;
@@ -20,6 +21,7 @@ public class ModelManager implements Model {
private static final Logger logger = LogsCenter.getLogger(ModelManager.class);
private final AddressBook addressBook;
+ private final AddressBookList addressBookList = new AddressBookList();
private final UserPrefs userPrefs;
private final FilteredList filteredPersons;
@@ -32,8 +34,9 @@ public ModelManager(ReadOnlyAddressBook addressBook, ReadOnlyUserPrefs userPrefs
logger.fine("Initializing with address book: " + addressBook + " and user prefs " + userPrefs);
this.addressBook = new AddressBook(addressBook);
+ this.addressBookList.add(new AddressBook(addressBook));
this.userPrefs = new UserPrefs(userPrefs);
- filteredPersons = new FilteredList<>(this.addressBook.getPersonList());
+ this.filteredPersons = new FilteredList<>(this.addressBook.getPersonList());
}
public ModelManager() {
@@ -80,6 +83,7 @@ public void setAddressBookFilePath(Path addressBookFilePath) {
@Override
public void setAddressBook(ReadOnlyAddressBook addressBook) {
this.addressBook.resetData(addressBook);
+ this.addressBookList.add(new AddressBook(addressBook));
}
@Override
@@ -95,20 +99,45 @@ public boolean hasPerson(Person person) {
@Override
public void deletePerson(Person target) {
- addressBook.removePerson(target);
+ this.addressBook.removePerson(target);
+ this.addressBookList.add(new AddressBook(addressBook));
}
@Override
public void addPerson(Person person) {
- addressBook.addPerson(person);
+ this.addressBook.addPerson(person);
+ this.addressBookList.add(new AddressBook(addressBook));
updateFilteredPersonList(PREDICATE_SHOW_ALL_PERSONS);
}
@Override
public void setPerson(Person target, Person editedPerson) {
requireAllNonNull(target, editedPerson);
+ this.addressBook.setPerson(target, editedPerson);
+ this.addressBookList.add(new AddressBook(addressBook));
+ }
+
+ @Override
+ public String undo() {
+ this.addressBook.resetData(this.addressBookList.undo());
+ return this.addressBookList.undoPastCommand();
+ }
- addressBook.setPerson(target, editedPerson);
+ @Override
+ public String redo() {
+ this.addressBook.resetData(this.addressBookList.redo());
+ return this.addressBookList.redoPastCommand();
+ }
+
+ @Override
+ public void addCommandText(String commandText) {
+ this.addressBookList.addCommandText(commandText);
+ }
+
+ @Override
+ public void sort(Comparator comparator) {
+ this.addressBook.sort(comparator);
+ this.addressBookList.add(new AddressBook(addressBook));
}
//=========== Filtered Person List Accessors =============================================================
diff --git a/src/main/java/seedu/address/model/person/Address.java b/src/main/java/seedu/address/model/person/Address.java
index 469a2cc9a1e..d72d4a01bf4 100644
--- a/src/main/java/seedu/address/model/person/Address.java
+++ b/src/main/java/seedu/address/model/person/Address.java
@@ -54,7 +54,7 @@ public boolean equals(Object other) {
}
Address otherAddress = (Address) other;
- return value.equals(otherAddress.value);
+ return value.equalsIgnoreCase(otherAddress.value);
}
@Override
@@ -62,4 +62,8 @@ public int hashCode() {
return value.hashCode();
}
+ public int compareTo(Address other) {
+ return this.value.compareTo(other.value);
+ }
+
}
diff --git a/src/main/java/seedu/address/model/person/Birthday.java b/src/main/java/seedu/address/model/person/Birthday.java
new file mode 100644
index 00000000000..b486720cdd6
--- /dev/null
+++ b/src/main/java/seedu/address/model/person/Birthday.java
@@ -0,0 +1,84 @@
+package seedu.address.model.person;
+
+import static java.util.Objects.requireNonNull;
+import static seedu.address.commons.util.AppUtil.checkArgument;
+
+import java.time.DateTimeException;
+import java.time.LocalDate;
+import java.time.format.DateTimeFormatter;
+import java.time.temporal.ChronoUnit;
+
+/**
+ * Represents a Person's dob.
+ */
+public class Birthday {
+ public static final String MESSAGE_CONSTRAINTS =
+ "Invalid date of birth. Please provide date of birth with format: YYYY-MM-DD.\n"
+ + "An employee must be at least 13 years old.";
+ public final String dob;
+
+ /**
+ * Constructs a {@code Birthday}.
+ *
+ * @param dateStr A valid dob.
+ */
+ public Birthday(String dateStr) {
+ requireNonNull(dateStr);
+ dateStr = dateStr.trim();
+ checkArgument(isValidDob(dateStr), MESSAGE_CONSTRAINTS);
+ dob = dateStr;
+ }
+
+ /**
+ * Returns true if a given string is a valid dob.
+ */
+ public static boolean isValidDob(String test) {
+ String dateStr = test.trim();
+ try {
+ LocalDate birthday = LocalDate.parse(dateStr);
+ LocalDate legalBirthday = LocalDate.now().minusYears(13);
+ if (legalBirthday.until(birthday, ChronoUnit.DAYS) > 0) {
+ return false;
+ }
+ } catch (DateTimeException e) {
+ return false;
+ }
+ return true;
+ }
+
+ @Override
+ public String toString() {
+ return LocalDate.parse(dob).format(DateTimeFormatter.ofPattern("d MMM yyyy"));
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (other == this) {
+ return true;
+ }
+
+ // instanceof handles nulls
+ if (!(other instanceof Birthday)) {
+ return false;
+ }
+
+ Birthday otherDate = (Birthday) other;
+ return dob.equals(otherDate.dob);
+ }
+
+ @Override
+ public int hashCode() {
+ return dob.hashCode();
+ }
+
+ public Month getMonth() {
+ DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");
+ LocalDate localDate = LocalDate.parse(dob, formatter);
+ return new Month(localDate.getMonthValue());
+ }
+
+ public int compareTo(Birthday other) {
+ return LocalDate.parse(this.dob).compareTo(LocalDate.parse(other.dob));
+ }
+
+}
diff --git a/src/main/java/seedu/address/model/person/Claim.java b/src/main/java/seedu/address/model/person/Claim.java
new file mode 100644
index 00000000000..3c2485c1ca0
--- /dev/null
+++ b/src/main/java/seedu/address/model/person/Claim.java
@@ -0,0 +1,54 @@
+package seedu.address.model.person;
+
+/**
+ * Represents a Person's Claim in the address book.
+ * Ensures boolean variable and amount is provided.
+ */
+public class Claim {
+ public static final String NO_SYMBOLS_ERROR = "Kindly state whether the employee wants to deduct/add "
+ + "the claim amount! Provide either +/- in front of the amount!";
+ public static final String ALPHABETS_ERROR = "Kindly only input integers for the claim amount!";
+
+ public final boolean isSubtract;
+ public final long amount;
+
+ /**
+ * Returns a Claim Object which contains of two variables, isSubtract and amount.
+ * isSubtract variable represents whether this claim is either addition/subtraction of claim budget.
+ * amount variable represents whether the claim amount employee is hoping to claim.
+ *
+ * @param numStr String Object of user's input.
+ */
+ public Claim(String numStr) {
+ char firstChar = numStr.charAt(0);
+ if (firstChar == '+') {
+ this.isSubtract = false;
+ } else {
+ this.isSubtract = true;
+ }
+ this.amount = Long.parseLong(numStr.substring(1));
+ }
+
+ /**
+ * Returns a boolean object where True if symbol (+/-) is provided before claim amount else False.
+ *
+ * @param args String Object that contains parsed out portions from user's CLI input.
+ * @return Boolean where True represents correct claim input format else False.
+ */
+ public static boolean comtainsSymbol(String args) {
+ char firstChar = args.charAt(0);
+ return firstChar == '+' || firstChar == '-';
+ }
+
+ /**
+ * Returns boolean object where True if claim amount only contains of digits else False.
+ * This portion represents post symbol portion. For example, if args == "+500", we are obly care about "500".
+ *
+ * @param args String object that contains parsed out partions from user's CLI input.
+ * @return Boolean where True represents claim amount was given in correct format else False.
+ */
+ public static boolean isCorrectAmountType(String args) {
+ String amount = args.substring(1);
+ return amount.matches("\\d+");
+ }
+}
diff --git a/src/main/java/seedu/address/model/person/Department.java b/src/main/java/seedu/address/model/person/Department.java
new file mode 100644
index 00000000000..5d297956f30
--- /dev/null
+++ b/src/main/java/seedu/address/model/person/Department.java
@@ -0,0 +1,63 @@
+package seedu.address.model.person;
+
+import static java.util.Objects.requireNonNull;
+import static seedu.address.commons.util.AppUtil.checkArgument;
+
+/**
+ * Represents a Person's department.
+ */
+public class Department {
+
+ public static final String MESSAGE_CONSTRAINTS = "Departments should not be blank";
+
+ public final String department;
+
+ /**
+ * Constructs a {@code Department}.
+ *
+ * @param dep A valid department.
+ */
+ public Department(String dep) {
+ requireNonNull(dep);
+ dep = dep.trim();
+ checkArgument(isValidDepartment(dep), MESSAGE_CONSTRAINTS);
+ department = dep;
+ }
+
+ /**
+ * Returns true if a given string is a valid department.
+ */
+ public static boolean isValidDepartment(String test) {
+ return !test.trim().equals("");
+ }
+
+ @Override
+ public String toString() {
+ return department;
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (other == this) {
+ return true;
+ }
+
+ // instanceof handles nulls
+ if (!(other instanceof Department)) {
+ return false;
+ }
+
+ Department otherDep = (Department) other;
+ return department.equalsIgnoreCase(otherDep.department);
+ }
+
+ @Override
+ public int hashCode() {
+ return department.hashCode();
+ }
+
+ public int compareTo(Department other) {
+ return this.department.compareTo(other.department);
+ }
+
+}
diff --git a/src/main/java/seedu/address/model/person/Email.java b/src/main/java/seedu/address/model/person/Email.java
index c62e512bc29..e36cf65215d 100644
--- a/src/main/java/seedu/address/model/person/Email.java
+++ b/src/main/java/seedu/address/model/person/Email.java
@@ -10,29 +10,29 @@
public class Email {
private static final String SPECIAL_CHARACTERS = "+_.-";
- public static final String MESSAGE_CONSTRAINTS = "Emails should be of the format local-part@domain "
- + "and adhere to the following constraints:\n"
- + "1. The local-part should only contain alphanumeric characters and these special characters, excluding "
- + "the parentheses, (" + SPECIAL_CHARACTERS + "). The local-part may not start or end with any special "
- + "characters.\n"
- + "2. This is followed by a '@' and then a domain name. The domain name is made up of domain labels "
- + "separated by periods.\n"
- + "The domain name must:\n"
- + " - end with a domain label at least 2 characters long\n"
- + " - have each domain label start and end with alphanumeric characters\n"
- + " - have each domain label consist of alphanumeric characters, separated only by hyphens, if any.";
- // alphanumeric and special characters
+ // alphanumeric and special characters
private static final String ALPHANUMERIC_NO_UNDERSCORE = "[^\\W_]+"; // alphanumeric characters except underscore
private static final String LOCAL_PART_REGEX = "^" + ALPHANUMERIC_NO_UNDERSCORE + "([" + SPECIAL_CHARACTERS + "]"
- + ALPHANUMERIC_NO_UNDERSCORE + ")*";
- private static final String DOMAIN_PART_REGEX = ALPHANUMERIC_NO_UNDERSCORE
- + "(-" + ALPHANUMERIC_NO_UNDERSCORE + ")*";
- private static final String DOMAIN_LAST_PART_REGEX = "(" + DOMAIN_PART_REGEX + "){2,}$"; // At least two chars
- private static final String DOMAIN_REGEX = "(" + DOMAIN_PART_REGEX + "\\.)*" + DOMAIN_LAST_PART_REGEX;
- public static final String VALIDATION_REGEX = LOCAL_PART_REGEX + "@" + DOMAIN_REGEX;
+ + ALPHANUMERIC_NO_UNDERSCORE + ")*";
+ private static final String DOMAIN_REGEX = ALPHANUMERIC_NO_UNDERSCORE + "([" + SPECIAL_CHARACTERS + "]"
+ + ALPHANUMERIC_NO_UNDERSCORE + ")*";
+ public static final String VALIDATION_REGEX = LOCAL_PART_REGEX + "@(" + DOMAIN_REGEX + "\\.)+[a-zA-Z]{2,6}$";
+
+ public static final String MESSAGE_CONSTRAINTS = "Emails should be of the format "
+ + "local-part@domain.top-level-domain and adhere to the following constraints:\n"
+ + "1. The local-part and domain should only contain alphanumeric characters and these special characters, "
+ + "excluding the parentheses, (" + SPECIAL_CHARACTERS + ").\n"
+ + "2. The local-part may not start or end with any special characters. Each special character cannot "
+ + "be next to each other.\n"
+ + "3. The local-part is followed by a '@' and a domain name. The domain name is made up of domain labels "
+ + "separated by periods.\n"
+ + "4. Each domain label start and end with alphanumeric characters only.\n"
+ + "5. The top-level-domain should only contain 2 to 6 alphabet characters (e.g., .com, .edu).\n";
+
public final String value;
+
/**
* Constructs an {@code Email}.
*
@@ -68,7 +68,7 @@ public boolean equals(Object other) {
}
Email otherEmail = (Email) other;
- return value.equals(otherEmail.value);
+ return value.equalsIgnoreCase(otherEmail.value);
}
@Override
@@ -76,4 +76,8 @@ public int hashCode() {
return value.hashCode();
}
+ public int compareTo(Email other) {
+ return this.value.compareTo(other.value);
+ }
+
}
diff --git a/src/main/java/seedu/address/model/person/HasLeaveAnyMonthPredicate.java b/src/main/java/seedu/address/model/person/HasLeaveAnyMonthPredicate.java
new file mode 100644
index 00000000000..f7e8d350b80
--- /dev/null
+++ b/src/main/java/seedu/address/model/person/HasLeaveAnyMonthPredicate.java
@@ -0,0 +1,30 @@
+package seedu.address.model.person;
+
+import java.util.function.Predicate;
+
+/**
+ * Tests that a {@code Person} has a leave in any month.
+ */
+public class HasLeaveAnyMonthPredicate implements Predicate {
+
+ /**
+ * Evaluates this predicate on the given argument.
+ *
+ * @param person the input argument
+ * @return {@code true} if the input argument matches the predicate, otherwise {@code false}
+ */
+ @Override
+ public boolean test(Person person) {
+ return person.getLeave().leaveExistInAnyMonth();
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (other == this) {
+ return true;
+ }
+
+ // instanceof handles nulls
+ return other instanceof HasLeaveAnyMonthPredicate;
+ }
+}
diff --git a/src/main/java/seedu/address/model/person/HasLeaveThisMonthPredicate.java b/src/main/java/seedu/address/model/person/HasLeaveThisMonthPredicate.java
new file mode 100644
index 00000000000..9edde1bb901
--- /dev/null
+++ b/src/main/java/seedu/address/model/person/HasLeaveThisMonthPredicate.java
@@ -0,0 +1,41 @@
+package seedu.address.model.person;
+
+import java.util.function.Predicate;
+
+/**
+ * Tests that a {@code Person} has a leave in {@code Month}.
+ */
+public class HasLeaveThisMonthPredicate implements Predicate {
+
+ private final String month;
+
+ public HasLeaveThisMonthPredicate(String month) {
+ this.month = month;
+ }
+
+ /**
+ * Evaluates this predicate on the given argument.
+ *
+ * @param person the input argument
+ * @return {@code true} if the input argument matches the predicate, otherwise {@code false}
+ */
+ @Override
+ public boolean test(Person person) {
+ return person.getLeave().leaveExistInMonth(month);
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (other == this) {
+ return true;
+ }
+
+ // instanceof handles nulls
+ if (!(other instanceof HasLeaveThisMonthPredicate)) {
+ return false;
+ }
+
+ HasLeaveThisMonthPredicate otherHasLeaveThisMonthPredicate = (HasLeaveThisMonthPredicate) other;
+ return month.equals(otherHasLeaveThisMonthPredicate.month);
+ }
+}
diff --git a/src/main/java/seedu/address/model/person/Leave.java b/src/main/java/seedu/address/model/person/Leave.java
new file mode 100644
index 00000000000..253ed5d5680
--- /dev/null
+++ b/src/main/java/seedu/address/model/person/Leave.java
@@ -0,0 +1,145 @@
+package seedu.address.model.person;
+
+import static java.util.Objects.requireNonNull;
+import static seedu.address.commons.util.AppUtil.checkArgument;
+
+/**
+ * Represents a Person's leave.
+ */
+public class Leave {
+ public static final String ILLEGAL_MONTH = "Cannot remove leave(s). "
+ + "The employee does not have leave(s) on %1$s.";
+ public static final int LEAVE_LENGTH = 12;
+ public static final String MESSAGE_CONSTRAINTS = "Invalid leave format";
+ public static final String NO_LEAVE = "000000000000";
+ private static final String[] MONTHS = {"Jan", "Feb", "Mar", "Apr", "May", "Jun",
+ "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"};
+
+ public final String leave;
+
+ /**
+ * Constructs an empty {@code Leave}.
+ */
+ public Leave() {
+ this.leave = NO_LEAVE;
+ }
+
+ /**
+ * Constructs a {@code Leave}.
+ *
+ * @param leave A valid leave.
+ */
+ public Leave(String leave) {
+ requireNonNull(leave);
+ leave = leave.trim();
+ checkArgument(isValidLeave(leave), MESSAGE_CONSTRAINTS);
+ this.leave = leave;
+ }
+
+ /**
+ * Returns true if a given string is a valid leave.
+ */
+ public static boolean isValidLeave(String test) {
+ test = test.trim();
+ if (test.length() != LEAVE_LENGTH) {
+ return false;
+ }
+ for (int i = 0; i < LEAVE_LENGTH; i++) {
+ if (test.charAt(i) != '0' && test.charAt(i) != '1') {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Updates current leave.
+ * @param change
+ * @return
+ */
+ public Leave update(String change) {
+ StringBuilder newLeave = new StringBuilder(leave);
+ String illegalMonths = "";
+ for (int i = 0; i < LEAVE_LENGTH; i++) {
+ if (change.charAt(i) == '+') {
+ newLeave.setCharAt(i, '1');
+ }
+ if (change.charAt(i) == '-') {
+ if (newLeave.charAt(i) == '0') {
+ if (illegalMonths.length() > 0) {
+ illegalMonths += ", ";
+ }
+ illegalMonths += MONTHS[i];
+ }
+ newLeave.setCharAt(i, '0');
+ }
+ }
+ if (!illegalMonths.equals("")) {
+ throw new IllegalArgumentException(String.format(ILLEGAL_MONTH, illegalMonths));
+ }
+ return new Leave(newLeave.toString());
+ }
+
+ /**
+ * Checks if leave exist in the specified month
+ * @param month the month to check
+ * @return true if leave exists in specified month
+ */
+ public boolean leaveExistInMonth(String month) {
+ char leaveExist = '1';
+ int index = Integer.parseInt(month) - 1;
+ return leave.charAt(index) == leaveExist;
+ }
+
+ /**
+ * Checks if leave exist in any month
+ * @return true if leave exists in specified month
+ */
+ public boolean leaveExistInAnyMonth() {
+ char leaveExist = '1';
+ for (int i = 0; i < LEAVE_LENGTH; i++) {
+ if (leave.charAt(i) == leaveExist) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ @Override
+ public String toString() {
+ if (leave.equals(NO_LEAVE)) {
+ return "-";
+ }
+ String leaves = "";
+ for (int i = 0; i < LEAVE_LENGTH; i++) {
+ if (leave.charAt(i) == '1') {
+ if (leaves.length() > 0) {
+ leaves += ", ";
+ }
+ leaves += MONTHS[i];
+ }
+ }
+ return leaves;
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (other == this) {
+ return true;
+ }
+
+ // instanceof handles nulls
+ if (!(other instanceof Leave)) {
+ return false;
+ }
+
+ Leave otherLeave = (Leave) other;
+ return leave.equals(otherLeave.leave);
+ }
+
+ @Override
+ public int hashCode() {
+ return leave.hashCode();
+ }
+
+}
diff --git a/src/main/java/seedu/address/model/person/MatchingBirthdayPredicate.java b/src/main/java/seedu/address/model/person/MatchingBirthdayPredicate.java
new file mode 100644
index 00000000000..637a482efb0
--- /dev/null
+++ b/src/main/java/seedu/address/model/person/MatchingBirthdayPredicate.java
@@ -0,0 +1,58 @@
+package seedu.address.model.person;
+
+import java.util.List;
+import java.util.function.Predicate;
+
+/**
+ * Tests that a {@code Person}'s {@code dob} matches the given the month.
+ */
+public class MatchingBirthdayPredicate implements Predicate {
+ private final List month;
+
+ public MatchingBirthdayPredicate(List month) {
+ this.month = month;
+ }
+
+ public List getMonth() {
+ return month;
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (other == this) {
+ return true;
+ }
+
+ // instanceof handles nulls
+ if (!(other instanceof MatchingBirthdayPredicate)) {
+ return false;
+ }
+
+ MatchingBirthdayPredicate otherPredicate = (MatchingBirthdayPredicate) other;
+ List otherList = otherPredicate.getMonth();
+ for (int i = 0; i < this.month.size(); i++) {
+ if (!this.month.get(i).equals(otherList.get(i))) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ @Override
+ public boolean test(Person person) {
+ return this.month.contains(person.getDob().getMonth());
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder message = new StringBuilder();
+ for (int i = 0; i < month.size(); i++) {
+ if (i == month.size() - 1) {
+ message.append(month.get(i).getMonthName());
+ } else {
+ message.append(month.get(i).getMonthName()).append(",");
+ }
+ }
+ return message.toString();
+ }
+}
diff --git a/src/main/java/seedu/address/model/person/MatchingDepartmentPredicate.java b/src/main/java/seedu/address/model/person/MatchingDepartmentPredicate.java
new file mode 100644
index 00000000000..e35bc014d69
--- /dev/null
+++ b/src/main/java/seedu/address/model/person/MatchingDepartmentPredicate.java
@@ -0,0 +1,26 @@
+package seedu.address.model.person;
+
+import java.util.function.Predicate;
+
+/**
+ * Tests that a {@code Person}'s {@code Department} matches the department name given.
+ */
+public class MatchingDepartmentPredicate implements Predicate {
+
+ private final Department department;
+
+ public MatchingDepartmentPredicate(Department department) {
+ this.department = department;
+ }
+
+ /**
+ * Evaluates this predicate on the given argument.
+ *
+ * @param person the input argument
+ * @return {@code true} if the input argument matches the predicate, otherwise {@code false}
+ */
+ @Override
+ public boolean test(Person person) {
+ return this.department.equals(person.getDepartment());
+ }
+}
diff --git a/src/main/java/seedu/address/model/person/Money.java b/src/main/java/seedu/address/model/person/Money.java
new file mode 100644
index 00000000000..bf770de46b7
--- /dev/null
+++ b/src/main/java/seedu/address/model/person/Money.java
@@ -0,0 +1,81 @@
+package seedu.address.model.person;
+
+import static java.util.Objects.requireNonNull;
+import static seedu.address.commons.util.AppUtil.checkArgument;
+
+/**
+ * Represents a Person's salary and claim budget.
+ */
+public class Money {
+
+ public static final String MESSAGE_CONSTRAINTS = "Dollar amount should be a positive integer "
+ + "with max value of $1,000,000,000,000";
+ public static final Long MAX_VALUE = (long) 1e12;
+
+ public final String amount;
+
+ /**
+ * Constructs a {@code Money}.
+ *
+ * @param numStr A valid dollar amount.
+ */
+ public Money(String numStr) {
+ requireNonNull(numStr);
+ checkArgument(isValidMoney(numStr), MESSAGE_CONSTRAINTS);
+ amount = numStr;
+ }
+
+ /**
+ * Returns true if a given string is a valid dollar amount.
+ */
+ public static boolean isValidMoney(String test) {
+ if (test.length() >= 14) {
+ return false;
+ }
+ try {
+ Long num = Long.valueOf(test);
+ return num >= 0 && num <= MAX_VALUE;
+ } catch (NumberFormatException e) {
+ return false;
+ }
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder rev = new StringBuilder(amount).reverse();
+ String money = "";
+ for (int i = 0; i < rev.length(); i++) {
+ if (i > 0 && i % 3 == 0) {
+ money += ",";
+ }
+ money += rev.charAt(i);
+ }
+ money += "$";
+ return new StringBuilder(money).reverse().toString();
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (other == this) {
+ return true;
+ }
+
+ // instanceof handles nulls
+ if (!(other instanceof Money)) {
+ return false;
+ }
+
+ Money otherMoney = (Money) other;
+ return amount.equals(otherMoney.amount);
+ }
+
+ @Override
+ public int hashCode() {
+ return amount.hashCode();
+ }
+
+ public int compareTo(Money other) {
+ return Long.valueOf(this.amount).compareTo(Long.valueOf(other.amount));
+ }
+
+}
diff --git a/src/main/java/seedu/address/model/person/Month.java b/src/main/java/seedu/address/model/person/Month.java
new file mode 100644
index 00000000000..7cd671d35b8
--- /dev/null
+++ b/src/main/java/seedu/address/model/person/Month.java
@@ -0,0 +1,109 @@
+package seedu.address.model.person;
+
+import static seedu.address.commons.util.AppUtil.checkArgument;
+
+/**
+ * Represents the month of the dob of the person.
+ */
+public class Month {
+ public static final String VALIDATION_REGEX = "[^\\s].*";
+ public static final String MESSAGE_CONSTRAINTS_BLANK_MONTH =
+ "Month provided cannot be blank.\n" + "Did you mean to type 'birthday' ?";
+ public static final String MESSAGE_CONSTRAINTS_NEGATIVE_MONTH =
+ "Month provided cannot be negative.";
+ public static final String MESSAGE_CONSTRAINTS_MONTH_OVER =
+ "Month provided cannot be greater than 12.";
+ public static final String MESSAGE_CONSTRAINTS_ZERO_MONTH =
+ "Month provided cannot be 0.";
+ public static final String MESSAGE_CONSTRAINTS_MONTH_INVALID_CHARACTERS =
+ "Month provided cannot be a decimal nor contain alphabets.";
+ public static final String INVALID_MONTH = "Invalid month(s) provided.";
+
+ public final int month;
+ public final String monthName;
+ public final String[] monthNames = {
+ "Jan", "Feb", "Mar", "Apr", "May", "Jun",
+ "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"};
+
+ /**
+ * Constructs an {@code Month}
+ * @param monthValue the integer representation of the intended month.
+ */
+ public Month(int monthValue) {
+ checkArgument(monthValue > 0 && monthValue < 13, INVALID_MONTH);
+ month = monthValue;
+ monthName = monthNames[monthValue - 1];
+ }
+
+ /**
+ * Checks if the given input contains characters or decimals.
+ * @param monthString the string representation of the month number.
+ * @return true if it contains char/decimals, false if clear.
+ */
+ public static boolean containsAlphabetsOrDecimals(String monthString) {
+ String monthTrimmed = monthString.trim();
+ try {
+ Integer.parseInt(monthTrimmed);
+ } catch (NumberFormatException nfe) {
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Check if the given input is 0.
+ * @param monthString the string representation of the month number.
+ * @return true if the value is 0.
+ */
+ public static boolean isZeroMonth(String monthString) {
+ String monthTrimmed = monthString.trim();
+ int value = Integer.parseInt(monthTrimmed);
+ return value == 0;
+ }
+
+ /**
+ * Check if the given input is negative.
+ * @param monthString the string representation of the month number.
+ * @return true if the value is negative.
+ */
+ public static boolean isNegativeMonth(String monthString) {
+ String monthTrimmed = monthString.trim();
+ int value = Integer.parseInt(monthTrimmed);
+ return value < 0;
+ }
+
+ /**
+ * Check if the given input lies between 1 and 12 (inclusive).
+ * @param monthString the string representation of the month number.
+ * @return true if it is between 1 and 12.
+ */
+ public static boolean isValidMonth(String monthString) {
+ String monthTrimmed = monthString.trim();
+ int value = Integer.parseInt(monthTrimmed);
+ return value > 0 && value < 13;
+ }
+
+ public int getMonthValue() {
+ return month;
+ }
+
+ public String getMonthName() {
+ return monthName;
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (other == this) {
+ return true;
+ }
+
+ // instanceof handles nulls
+ if (!(other instanceof Month)) {
+ return false;
+ }
+
+ Month otherDate = (Month) other;
+ return month == otherDate.getMonthValue();
+ }
+
+}
diff --git a/src/main/java/seedu/address/model/person/Name.java b/src/main/java/seedu/address/model/person/Name.java
index 173f15b9b00..6126c255ea8 100644
--- a/src/main/java/seedu/address/model/person/Name.java
+++ b/src/main/java/seedu/address/model/person/Name.java
@@ -4,19 +4,11 @@
import static seedu.address.commons.util.AppUtil.checkArgument;
/**
- * Represents a Person's name in the address book.
+ * Represents a Person's name.
* Guarantees: immutable; is valid as declared in {@link #isValidName(String)}
*/
public class Name {
-
- public static final String MESSAGE_CONSTRAINTS =
- "Names should only contain alphanumeric characters and spaces, and it should not be blank";
-
- /*
- * The first character of the address must not be a whitespace,
- * otherwise " " (a blank string) becomes a valid input.
- */
- public static final String VALIDATION_REGEX = "[\\p{Alnum}][\\p{Alnum} ]*";
+ public static final String MESSAGE_CONSTRAINTS = "Names should not be blank";
public final String fullName;
@@ -27,6 +19,7 @@ public class Name {
*/
public Name(String name) {
requireNonNull(name);
+ name = name.trim();
checkArgument(isValidName(name), MESSAGE_CONSTRAINTS);
fullName = name;
}
@@ -35,10 +28,9 @@ public Name(String name) {
* Returns true if a given string is a valid name.
*/
public static boolean isValidName(String test) {
- return test.matches(VALIDATION_REGEX);
+ return !test.trim().equals("");
}
-
@Override
public String toString() {
return fullName;
@@ -55,8 +47,7 @@ public boolean equals(Object other) {
return false;
}
- Name otherName = (Name) other;
- return fullName.equals(otherName.fullName);
+ return fullName.equalsIgnoreCase(((Name) other).fullName);
}
@Override
@@ -64,4 +55,7 @@ public int hashCode() {
return fullName.hashCode();
}
+ public int compareTo(Name other) {
+ return this.fullName.compareTo(other.fullName);
+ }
}
diff --git a/src/main/java/seedu/address/model/person/Person.java b/src/main/java/seedu/address/model/person/Person.java
index abe8c46b535..ee8a646ef7d 100644
--- a/src/main/java/seedu/address/model/person/Person.java
+++ b/src/main/java/seedu/address/model/person/Person.java
@@ -23,18 +23,45 @@ public class Person {
// Data fields
private final Address address;
+ private final Money salary;
+ private final Money claimBudget;
+ private final Department department;
+ private final Birthday dob;
+ private final Leave leave;
private final Set tags = new HashSet<>();
/**
* Every field must be present and not null.
*/
- public Person(Name name, Phone phone, Email email, Address address, Set tags) {
- requireAllNonNull(name, phone, email, address, tags);
+ public Person(Name name, Phone phone, Email email, Address address,
+ Money salary, Money claimBudget, Department dep, Birthday dob) {
+ requireAllNonNull(name, phone, email, address, salary, claimBudget, dep, dob);
this.name = name;
this.phone = phone;
this.email = email;
this.address = address;
- this.tags.addAll(tags);
+ this.salary = salary;
+ this.claimBudget = claimBudget;
+ this.department = dep;
+ this.dob = dob;
+ this.leave = new Leave();
+ }
+
+ /**
+ * Constructs a {@code Person}.
+ */
+ public Person(Name name, Phone phone, Email email, Address address,
+ Money salary, Money claimBudget, Department dep, Birthday dob, Leave leave) {
+ requireAllNonNull(name, phone, email, address, salary, claimBudget, dep, dob);
+ this.name = name;
+ this.phone = phone;
+ this.email = email;
+ this.address = address;
+ this.salary = salary;
+ this.claimBudget = claimBudget;
+ this.department = dep;
+ this.dob = dob;
+ this.leave = leave;
}
public Name getName() {
@@ -53,6 +80,26 @@ public Address getAddress() {
return address;
}
+ public Money getSalary() {
+ return salary;
+ }
+
+ public Money getClaimBudget() {
+ return claimBudget;
+ }
+
+ public Department getDepartment() {
+ return department;
+ }
+
+ public Birthday getDob() {
+ return dob;
+ }
+
+ public Leave getLeave() {
+ return leave;
+ }
+
/**
* Returns an immutable tag set, which throws {@code UnsupportedOperationException}
* if modification is attempted.
@@ -62,16 +109,14 @@ public Set getTags() {
}
/**
- * Returns true if both persons have the same name.
- * This defines a weaker notion of equality between two persons.
+ * Returns true if both persons are the same.
*/
public boolean isSamePerson(Person otherPerson) {
if (otherPerson == this) {
return true;
}
- return otherPerson != null
- && otherPerson.getName().equals(getName());
+ return otherPerson != null && this.equals(otherPerson);
}
/**
@@ -91,16 +136,15 @@ public boolean equals(Object other) {
Person otherPerson = (Person) other;
return name.equals(otherPerson.name)
- && phone.equals(otherPerson.phone)
- && email.equals(otherPerson.email)
+ && dob.equals(otherPerson.dob)
&& address.equals(otherPerson.address)
- && tags.equals(otherPerson.tags);
+ && (phone.equals(otherPerson.phone) || email.equals(otherPerson.email));
}
@Override
public int hashCode() {
// use this method for custom fields hashing instead of implementing your own
- return Objects.hash(name, phone, email, address, tags);
+ return Objects.hash(name, phone, email, address, salary, claimBudget, department, dob, leave);
}
@Override
@@ -110,7 +154,11 @@ public String toString() {
.add("phone", phone)
.add("email", email)
.add("address", address)
- .add("tags", tags)
+ .add("salary", salary)
+ .add("claimBudget", claimBudget)
+ .add("department", department)
+ .add("dob", dob)
+ .add("leave", leave)
.toString();
}
diff --git a/src/main/java/seedu/address/model/person/PersonComparator.java b/src/main/java/seedu/address/model/person/PersonComparator.java
new file mode 100644
index 00000000000..54b35e6b302
--- /dev/null
+++ b/src/main/java/seedu/address/model/person/PersonComparator.java
@@ -0,0 +1,54 @@
+package seedu.address.model.person;
+
+import static java.util.Objects.requireNonNull;
+
+import java.util.Comparator;
+
+/**
+ * Comparator to sort AddressBook based on given parameter.
+ */
+public class PersonComparator implements Comparator {
+ public static final String SORT_ADDRESS = "address";
+ public static final String SORT_CLAIM = "claim";
+ public static final String SORT_DEPARTMENT = "dep";
+ public static final String SORT_DOB = "dob";
+ public static final String SORT_EMAIL = "email";
+ public static final String SORT_NAME = "name";
+ public static final String SORT_PHONE = "phone";
+ public static final String SORT_SALARY = "salary";
+
+ private final String param;
+
+ /**
+ * Initializes PersonComparator.
+ * @param param parameter to sort
+ */
+ public PersonComparator(String param) {
+ requireNonNull(param);
+ this.param = param.trim();
+ }
+
+ @Override
+ public int compare(Person a, Person b) {
+ switch (param) {
+ case PersonComparator.SORT_NAME:
+ return a.getName().compareTo(b.getName());
+ case PersonComparator.SORT_PHONE:
+ return a.getPhone().compareTo(b.getPhone());
+ case PersonComparator.SORT_EMAIL:
+ return a.getEmail().compareTo(b.getEmail());
+ case PersonComparator.SORT_ADDRESS:
+ return a.getAddress().compareTo(b.getAddress());
+ case PersonComparator.SORT_SALARY:
+ return a.getSalary().compareTo(b.getSalary());
+ case PersonComparator.SORT_CLAIM:
+ return a.getClaimBudget().compareTo(b.getClaimBudget());
+ case PersonComparator.SORT_DEPARTMENT:
+ return a.getDepartment().compareTo(b.getDepartment());
+ case PersonComparator.SORT_DOB:
+ return a.getDob().compareTo(b.getDob());
+ default:
+ throw new IllegalArgumentException();
+ }
+ }
+}
diff --git a/src/main/java/seedu/address/model/person/Phone.java b/src/main/java/seedu/address/model/person/Phone.java
index d733f63d739..cb780ed4721 100644
--- a/src/main/java/seedu/address/model/person/Phone.java
+++ b/src/main/java/seedu/address/model/person/Phone.java
@@ -9,9 +9,9 @@
*/
public class Phone {
-
public static final String MESSAGE_CONSTRAINTS =
- "Phone numbers should only contain numbers, and it should be at least 3 digits long";
+ "Phone numbers should only contain numbers (or start with + sign for international numbers), "
+ + "and it should be between 3 - 20 digits long";
public static final String VALIDATION_REGEX = "\\d{3,}";
public final String value;
@@ -22,6 +22,7 @@ public class Phone {
*/
public Phone(String phone) {
requireNonNull(phone);
+ phone = phone.trim();
checkArgument(isValidPhone(phone), MESSAGE_CONSTRAINTS);
value = phone;
}
@@ -30,6 +31,15 @@ public Phone(String phone) {
* Returns true if a given string is a valid phone number.
*/
public static boolean isValidPhone(String test) {
+ if (test.length() < 3) {
+ return false;
+ }
+ if (test.charAt(0) == '+') {
+ test = test.substring(1);
+ }
+ if (test.length() < 3 || test.length() > 20) {
+ return false;
+ }
return test.matches(VALIDATION_REGEX);
}
@@ -58,4 +68,8 @@ public int hashCode() {
return value.hashCode();
}
+ public int compareTo(Phone other) {
+ return this.value.compareTo(other.value);
+ }
+
}
diff --git a/src/main/java/seedu/address/model/person/UniquePersonList.java b/src/main/java/seedu/address/model/person/UniquePersonList.java
index cc0a68d79f9..90176eac0f7 100644
--- a/src/main/java/seedu/address/model/person/UniquePersonList.java
+++ b/src/main/java/seedu/address/model/person/UniquePersonList.java
@@ -3,6 +3,7 @@
import static java.util.Objects.requireNonNull;
import static seedu.address.commons.util.CollectionUtil.requireAllNonNull;
+import java.util.Comparator;
import java.util.Iterator;
import java.util.List;
@@ -147,4 +148,8 @@ private boolean personsAreUnique(List persons) {
}
return true;
}
+
+ public void sort(Comparator comparator) {
+ this.internalList.sort(comparator);
+ }
}
diff --git a/src/main/java/seedu/address/model/util/SampleDataUtil.java b/src/main/java/seedu/address/model/util/SampleDataUtil.java
index 1806da4facf..7825afeedeb 100644
--- a/src/main/java/seedu/address/model/util/SampleDataUtil.java
+++ b/src/main/java/seedu/address/model/util/SampleDataUtil.java
@@ -1,17 +1,16 @@
package seedu.address.model.util;
-import java.util.Arrays;
-import java.util.Set;
-import java.util.stream.Collectors;
-
import seedu.address.model.AddressBook;
import seedu.address.model.ReadOnlyAddressBook;
import seedu.address.model.person.Address;
+import seedu.address.model.person.Birthday;
+import seedu.address.model.person.Department;
import seedu.address.model.person.Email;
+import seedu.address.model.person.Leave;
+import seedu.address.model.person.Money;
import seedu.address.model.person.Name;
import seedu.address.model.person.Person;
import seedu.address.model.person.Phone;
-import seedu.address.model.tag.Tag;
/**
* Contains utility methods for populating {@code AddressBook} with sample data.
@@ -20,23 +19,23 @@ public class SampleDataUtil {
public static Person[] getSamplePersons() {
return new Person[] {
new Person(new Name("Alex Yeoh"), new Phone("87438807"), new Email("alexyeoh@example.com"),
- new Address("Blk 30 Geylang Street 29, #06-40"),
- getTagSet("friends")),
+ new Address("Blk 30 Geylang Street 29, #06-40"), new Money("10000"), new Money("4000"),
+ new Department("Engineering"), new Birthday("2000-01-01"), new Leave("000100010001")),
new Person(new Name("Bernice Yu"), new Phone("99272758"), new Email("berniceyu@example.com"),
- new Address("Blk 30 Lorong 3 Serangoon Gardens, #07-18"),
- getTagSet("colleagues", "friends")),
+ new Address("Blk 30 Lorong 3 Serangoon Gardens, #07-18"), new Money("5000"), new Money("1000"),
+ new Department("HR"), new Birthday("1990-03-20"), new Leave("110000000000")),
new Person(new Name("Charlotte Oliveiro"), new Phone("93210283"), new Email("charlotte@example.com"),
- new Address("Blk 11 Ang Mo Kio Street 74, #11-04"),
- getTagSet("neighbours")),
+ new Address("Blk 11 Ang Mo Kio Street 74, #11-04"), new Money("7000"), new Money("1500"),
+ new Department("Sales"), new Birthday("1996-04-17"), new Leave("101010010101")),
new Person(new Name("David Li"), new Phone("91031282"), new Email("lidavid@example.com"),
- new Address("Blk 436 Serangoon Gardens Street 26, #16-43"),
- getTagSet("family")),
+ new Address("Blk 436 Serangoon Gardens Street 26, #16-43"), new Money("9000"), new Money("3600"),
+ new Department("Engineering"), new Birthday("1992-09-07"), new Leave("000000000000")),
new Person(new Name("Irfan Ibrahim"), new Phone("92492021"), new Email("irfan@example.com"),
- new Address("Blk 47 Tampines Street 20, #17-35"),
- getTagSet("classmates")),
+ new Address("Blk 47 Tampines Street 20, #17-35"), new Money("8200"), new Money("500"),
+ new Department("IT"), new Birthday("1994-12-05"), new Leave("011011011100")),
new Person(new Name("Roy Balakrishnan"), new Phone("92624417"), new Email("royb@example.com"),
- new Address("Blk 45 Aljunied Street 85, #11-31"),
- getTagSet("colleagues"))
+ new Address("Blk 45 Aljunied Street 85, #11-31"), new Money("6500"), new Money("1200"),
+ new Department("Sales"), new Birthday("2001-07-31"), new Leave("100000000001"))
};
}
@@ -47,14 +46,4 @@ public static ReadOnlyAddressBook getSampleAddressBook() {
}
return sampleAb;
}
-
- /**
- * Returns a tag set containing the list of strings given.
- */
- public static Set getTagSet(String... strings) {
- return Arrays.stream(strings)
- .map(Tag::new)
- .collect(Collectors.toSet());
- }
-
}
diff --git a/src/main/java/seedu/address/storage/JsonAdaptedPerson.java b/src/main/java/seedu/address/storage/JsonAdaptedPerson.java
index bd1ca0f56c8..cf396795937 100644
--- a/src/main/java/seedu/address/storage/JsonAdaptedPerson.java
+++ b/src/main/java/seedu/address/storage/JsonAdaptedPerson.java
@@ -1,34 +1,35 @@
package seedu.address.storage;
-import java.util.ArrayList;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Set;
-import java.util.stream.Collectors;
-
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;
import seedu.address.commons.exceptions.IllegalValueException;
import seedu.address.model.person.Address;
+import seedu.address.model.person.Birthday;
+import seedu.address.model.person.Department;
import seedu.address.model.person.Email;
+import seedu.address.model.person.Leave;
+import seedu.address.model.person.Money;
import seedu.address.model.person.Name;
import seedu.address.model.person.Person;
import seedu.address.model.person.Phone;
-import seedu.address.model.tag.Tag;
/**
* Jackson-friendly version of {@link Person}.
*/
class JsonAdaptedPerson {
- public static final String MISSING_FIELD_MESSAGE_FORMAT = "Person's %s field is missing!";
+ public static final String MISSING_FIELD_MESSAGE_FORMAT = "Employee's %s field is missing!";
private final String name;
private final String phone;
private final String email;
private final String address;
- private final List tags = new ArrayList<>();
+ private final String salary;
+ private final String claimBudget;
+ private final String department;
+ private final String dob;
+ private final String leave;
/**
* Constructs a {@code JsonAdaptedPerson} with the given person details.
@@ -36,14 +37,18 @@ class JsonAdaptedPerson {
@JsonCreator
public JsonAdaptedPerson(@JsonProperty("name") String name, @JsonProperty("phone") String phone,
@JsonProperty("email") String email, @JsonProperty("address") String address,
- @JsonProperty("tags") List tags) {
+ @JsonProperty("salary") String salary, @JsonProperty("claimBudget") String claimBudget,
+ @JsonProperty("department") String department, @JsonProperty("dob") String dob,
+ @JsonProperty("leave") String leave) {
this.name = name;
this.phone = phone;
this.email = email;
this.address = address;
- if (tags != null) {
- this.tags.addAll(tags);
- }
+ this.salary = salary;
+ this.claimBudget = claimBudget;
+ this.department = department;
+ this.dob = dob;
+ this.leave = leave;
}
/**
@@ -54,9 +59,11 @@ public JsonAdaptedPerson(Person source) {
phone = source.getPhone().value;
email = source.getEmail().value;
address = source.getAddress().value;
- tags.addAll(source.getTags().stream()
- .map(JsonAdaptedTag::new)
- .collect(Collectors.toList()));
+ salary = source.getSalary().amount;
+ claimBudget = source.getClaimBudget().amount;
+ department = source.getDepartment().department;
+ dob = source.getDob().dob;
+ leave = source.getLeave().leave;
}
/**
@@ -65,11 +72,6 @@ public JsonAdaptedPerson(Person source) {
* @throws IllegalValueException if there were any data constraints violated in the adapted person.
*/
public Person toModelType() throws IllegalValueException {
- final List personTags = new ArrayList<>();
- for (JsonAdaptedTag tag : tags) {
- personTags.add(tag.toModelType());
- }
-
if (name == null) {
throw new IllegalValueException(String.format(MISSING_FIELD_MESSAGE_FORMAT, Name.class.getSimpleName()));
}
@@ -102,8 +104,51 @@ public Person toModelType() throws IllegalValueException {
}
final Address modelAddress = new Address(address);
- final Set modelTags = new HashSet<>(personTags);
- return new Person(modelName, modelPhone, modelEmail, modelAddress, modelTags);
+ if (salary == null) {
+ throw new IllegalValueException(String.format(MISSING_FIELD_MESSAGE_FORMAT, Money.class.getSimpleName()));
+ }
+ if (!Money.isValidMoney(salary)) {
+ throw new IllegalValueException(Money.MESSAGE_CONSTRAINTS);
+ }
+ final Money modelSalary = new Money(salary);
+
+ if (claimBudget == null) {
+ throw new IllegalValueException(String.format(MISSING_FIELD_MESSAGE_FORMAT, Money.class.getSimpleName()));
+ }
+ if (!Money.isValidMoney(claimBudget)) {
+ throw new IllegalValueException(Money.MESSAGE_CONSTRAINTS);
+ }
+ final Money modelClaimBudget = new Money(claimBudget);
+
+ if (department == null) {
+ throw new IllegalValueException(
+ String.format(MISSING_FIELD_MESSAGE_FORMAT, Department.class.getSimpleName()));
+ }
+ if (!Department.isValidDepartment(department)) {
+ throw new IllegalValueException(Department.MESSAGE_CONSTRAINTS);
+ }
+ final Department modelDepartment = new Department(department);
+
+ if (dob == null) {
+ throw new IllegalValueException(
+ String.format(MISSING_FIELD_MESSAGE_FORMAT, Birthday.class.getSimpleName()));
+ }
+ if (!Birthday.isValidDob(dob)) {
+ throw new IllegalValueException(Birthday.MESSAGE_CONSTRAINTS);
+ }
+ final Birthday modelDob = new Birthday(dob);
+
+ if (leave == null) {
+ throw new IllegalValueException(
+ String.format(MISSING_FIELD_MESSAGE_FORMAT, Leave.class.getSimpleName()));
+ }
+ if (!Leave.isValidLeave(leave)) {
+ throw new IllegalValueException(Leave.MESSAGE_CONSTRAINTS);
+ }
+ final Leave modelLeave = new Leave(leave);
+
+ return new Person(modelName, modelPhone, modelEmail, modelAddress, modelSalary,
+ modelClaimBudget, modelDepartment, modelDob, modelLeave);
}
}
diff --git a/src/main/java/seedu/address/ui/CommandBox.java b/src/main/java/seedu/address/ui/CommandBox.java
index 9e75478664b..302495eca5d 100644
--- a/src/main/java/seedu/address/ui/CommandBox.java
+++ b/src/main/java/seedu/address/ui/CommandBox.java
@@ -1,8 +1,12 @@
package seedu.address.ui;
+import java.util.ArrayList;
+
import javafx.collections.ObservableList;
import javafx.fxml.FXML;
import javafx.scene.control.TextField;
+import javafx.scene.input.KeyCode;
+import javafx.scene.input.KeyEvent;
import javafx.scene.layout.Region;
import seedu.address.logic.commands.CommandResult;
import seedu.address.logic.commands.exceptions.CommandException;
@@ -17,6 +21,7 @@ public class CommandBox extends UiPart {
private static final String FXML = "CommandBox.fxml";
private final CommandExecutor commandExecutor;
+ private final InputHistory inputs;
@FXML
private TextField commandTextField;
@@ -27,6 +32,7 @@ public class CommandBox extends UiPart {
public CommandBox(CommandExecutor commandExecutor) {
super(FXML);
this.commandExecutor = commandExecutor;
+ this.inputs = new InputHistory();
// calls #setStyleToDefault() whenever there is a change to the text of the command box.
commandTextField.textProperty().addListener((unused1, unused2, unused3) -> setStyleToDefault());
}
@@ -42,6 +48,7 @@ private void handleCommandEntered() {
}
try {
+ inputs.add(commandText);
commandExecutor.execute(commandText);
commandTextField.setText("");
} catch (CommandException | ParseException e) {
@@ -49,6 +56,45 @@ private void handleCommandEntered() {
}
}
+ /**
+ * Handles UP and DOWN key pressed event.
+ */
+ @FXML
+ private void handleClick(KeyEvent keyEvent) {
+ KeyCode clickedKey = keyEvent.getCode();
+
+ if (clickedKey.equals(KeyCode.UP)) {
+ keyEvent.consume();
+ getPreviousInput();
+ } else if (clickedKey.equals(KeyCode.DOWN)) {
+ keyEvent.consume();
+ getNextInput();
+ }
+ }
+
+ private void getPreviousInput() {
+ if (inputs.hasPreviousInput()) {
+ updateText(inputs.getPreviousInput());
+ }
+ }
+
+ private void getNextInput() {
+ if (inputs.hasNextInput()) {
+ updateText(inputs.getNextInput());
+ } else {
+ updateText("");
+ inputs.setLastIndex();
+ }
+ }
+
+ /**
+ * Change current text with the given input.
+ */
+ private void updateText(String input) {
+ commandTextField.setText(input);
+ commandTextField.positionCaret(input.length());
+ }
+
/**
* Sets the command box style to use the default style.
*/
@@ -82,4 +128,71 @@ public interface CommandExecutor {
CommandResult execute(String commandText) throws CommandException, ParseException;
}
+ /**
+ * Represents the history of inputs given by the user.
+ */
+ public class InputHistory extends ArrayList {
+ private int index;
+
+ /**
+ * Constructs the InputHistory.
+ */
+ public InputHistory() {
+ super();
+ super.add("");
+ index = 1;
+ }
+
+ @Override
+ public boolean add(String input) {
+ if (!input.equals(super.get(super.size() - 1))) {
+ super.add(input);
+ }
+ index = super.size();
+ return true;
+ }
+
+ /**
+ * Sets the index to be equal to the list size.
+ */
+ public void setLastIndex() {
+ index = super.size();
+ }
+
+ /**
+ * Checks whether the list has previous input.
+ * @return boolean
+ */
+ public boolean hasPreviousInput() {
+ return index > 0;
+ }
+
+ /**
+ * Checks whether the list has next input.
+ * @return boolean
+ */
+ public boolean hasNextInput() {
+ return index < super.size() - 1;
+ }
+
+ /**
+ * Gets the previous input.
+ * @return the previous input
+ */
+ public String getPreviousInput() {
+ assert index > 0;
+ index--;
+ return super.get(index);
+ }
+
+ /**
+ * Gets the next input.
+ * @return the next input
+ */
+ public String getNextInput() {
+ assert index < super.size() - 1;
+ index++;
+ return super.get(index);
+ }
+ }
}
diff --git a/src/main/java/seedu/address/ui/HelpWindow.java b/src/main/java/seedu/address/ui/HelpWindow.java
index 3f16b2fcf26..1abdb97cc19 100644
--- a/src/main/java/seedu/address/ui/HelpWindow.java
+++ b/src/main/java/seedu/address/ui/HelpWindow.java
@@ -15,7 +15,7 @@
*/
public class HelpWindow extends UiPart {
- public static final String USERGUIDE_URL = "https://se-education.org/addressbook-level3/UserGuide.html";
+ public static final String USERGUIDE_URL = "https://ay2324s1-cs2103-f13-2.github.io/tp/UserGuide.html";
public static final String HELP_MESSAGE = "Refer to the user guide: " + USERGUIDE_URL;
private static final Logger logger = LogsCenter.getLogger(HelpWindow.class);
diff --git a/src/main/java/seedu/address/ui/MainWindow.java b/src/main/java/seedu/address/ui/MainWindow.java
index 79e74ef37c0..6192bd447a8 100644
--- a/src/main/java/seedu/address/ui/MainWindow.java
+++ b/src/main/java/seedu/address/ui/MainWindow.java
@@ -1,5 +1,7 @@
package seedu.address.ui;
+import java.awt.Desktop;
+import java.net.URL;
import java.util.logging.Logger;
import javafx.event.ActionEvent;
@@ -34,6 +36,7 @@ public class MainWindow extends UiPart {
private PersonListPanel personListPanel;
private ResultDisplay resultDisplay;
private HelpWindow helpWindow;
+ private String currentTheme;
@FXML
private StackPane commandBoxPlaceholder;
@@ -59,6 +62,7 @@ public MainWindow(Stage primaryStage, Logic logic) {
// Set dependencies
this.primaryStage = primaryStage;
this.logic = logic;
+ this.currentTheme = "DarkTheme.css";
// Configure the UI
setWindowDefaultSize(logic.getGuiSettings());
@@ -136,15 +140,31 @@ private void setWindowDefaultSize(GuiSettings guiSettings) {
}
/**
- * Opens the help window or focuses on it if it's already opened.
+ * Opens the help window.
*/
@FXML
public void handleHelp() {
- if (!helpWindow.isShowing()) {
+ if (!Desktop.isDesktopSupported() || !Desktop.getDesktop().isSupported(Desktop.Action.BROWSE)) {
helpWindow.show();
- } else {
- helpWindow.focus();
+ return;
}
+ try {
+ Desktop.getDesktop().browse(new URL(HelpWindow.USERGUIDE_URL).toURI());
+ } catch (Exception e) {
+ helpWindow.show();
+ }
+ }
+
+ /**
+ * Sets the Window to the specified theme.
+ */
+ @FXML
+ public void setTheme(String newTheme) {
+ String themeToRemove = getClass().getResource("/view/" + currentTheme).toExternalForm();
+ String themeToAdd = getClass().getResource("/view/" + newTheme).toExternalForm();
+ this.getRoot().getScene().getStylesheets().remove(themeToRemove);
+ this.getRoot().getScene().getStylesheets().add(themeToAdd);
+ currentTheme = newTheme;
}
void show() {
@@ -186,6 +206,10 @@ private CommandResult executeCommand(String commandText) throws CommandException
handleExit();
}
+ if (commandResult.isChangeTheme()) {
+ setTheme(commandResult.getThemeStylesheet());
+ }
+
return commandResult;
} catch (CommandException | ParseException e) {
logger.info("An error occurred while executing command: " + commandText);
diff --git a/src/main/java/seedu/address/ui/PersonCard.java b/src/main/java/seedu/address/ui/PersonCard.java
index 094c42cda82..236dbbf652f 100644
--- a/src/main/java/seedu/address/ui/PersonCard.java
+++ b/src/main/java/seedu/address/ui/PersonCard.java
@@ -1,10 +1,7 @@
package seedu.address.ui;
-import java.util.Comparator;
-
import javafx.fxml.FXML;
import javafx.scene.control.Label;
-import javafx.scene.layout.FlowPane;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Region;
import seedu.address.model.person.Person;
@@ -39,7 +36,15 @@ public class PersonCard extends UiPart {
@FXML
private Label email;
@FXML
- private FlowPane tags;
+ private Label salary;
+ @FXML
+ private Label claimBudget;
+ @FXML
+ private Label department;
+ @FXML
+ private Label dob;
+ @FXML
+ private Label leave;
/**
* Creates a {@code PersonCode} with the given {@code Person} and index to display.
@@ -49,11 +54,13 @@ public PersonCard(Person person, int displayedIndex) {
this.person = person;
id.setText(displayedIndex + ". ");
name.setText(person.getName().fullName);
- phone.setText(person.getPhone().value);
- address.setText(person.getAddress().value);
- email.setText(person.getEmail().value);
- person.getTags().stream()
- .sorted(Comparator.comparing(tag -> tag.tagName))
- .forEach(tag -> tags.getChildren().add(new Label(tag.tagName)));
+ phone.setText("Phone: " + person.getPhone().value);
+ address.setText("Address: " + person.getAddress().value);
+ email.setText("Email: " + person.getEmail().value);
+ salary.setText("Salary: " + person.getSalary().toString());
+ claimBudget.setText("Claim budget: " + person.getClaimBudget().toString());
+ department.setText("Department: " + person.getDepartment().toString());
+ dob.setText("DOB: " + person.getDob().toString());
+ leave.setText("Leave: " + person.getLeave().toString());
}
}
diff --git a/src/main/resources/images/address_book_32.png b/src/main/resources/images/address_book_32.png
index 29810cf1fd9..22bbdfde0b7 100644
Binary files a/src/main/resources/images/address_book_32.png and b/src/main/resources/images/address_book_32.png differ
diff --git a/src/main/resources/view/BlueTheme.css b/src/main/resources/view/BlueTheme.css
new file mode 100644
index 00000000000..b2992903f71
--- /dev/null
+++ b/src/main/resources/view/BlueTheme.css
@@ -0,0 +1,359 @@
+.background {
+ -fx-background-color: derive(white, 20%);
+ background-color: antiquewhite; /* Used in the default.html file */
+}
+
+.label {
+ -fx-font-size: 11pt;
+ -fx-font-family: "Segoe UI Semibold";
+ -fx-text-fill: black;
+ -fx-opacity: 0.9;
+}
+
+.label-bright {
+ -fx-font-size: 11pt;
+ -fx-font-family: "Segoe UI Semibold";
+ -fx-text-fill: white;
+ -fx-opacity: 1;
+}
+
+.label-header {
+ -fx-font-size: 32pt;
+ -fx-font-family: "Segoe UI Light";
+ -fx-text-fill: white;
+ -fx-opacity: 1;
+}
+
+.text-field {
+ -fx-font-size: 12pt;
+ -fx-font-family: "Segoe UI Semibold";
+}
+
+.tab-pane {
+ -fx-padding: 0 0 0 1;
+}
+
+.tab-pane .tab-header-area {
+ -fx-padding: 0 0 0 0;
+ -fx-min-height: 0;
+ -fx-max-height: 0;
+}
+
+.table-view {
+ -fx-base: #1d1d1d;
+ -fx-control-inner-background: #1d1d1d;
+ -fx-background-color: #1d1d1d;
+ -fx-table-cell-border-color: transparent;
+ -fx-table-header-border-color: transparent;
+ -fx-padding: 5;
+}
+
+.table-view .column-header-background {
+ -fx-background-color: transparent;
+}
+
+.table-view .column-header, .table-view .filler {
+ -fx-size: 35;
+ -fx-border-width: 0 0 1 0;
+ -fx-background-color: transparent;
+ -fx-border-color:
+ transparent
+ transparent
+ derive(-fx-base, 80%)
+ transparent;
+ -fx-border-insets: 0 10 1 0;
+}
+
+.table-view .column-header .label {
+ -fx-font-size: 20pt;
+ -fx-font-family: "Segoe UI Light";
+ -fx-text-fill: white;
+ -fx-alignment: center-left;
+ -fx-opacity: 1;
+}
+
+.table-view:focused .table-row-cell:filled:focused:selected {
+ -fx-background-color: -fx-focus-color;
+}
+
+.split-pane:horizontal .split-pane-divider {
+ -fx-background-color: derive(#1d1d1d, 20%);
+ -fx-border-color: transparent transparent transparent #4d4d4d;
+}
+
+.split-pane {
+ -fx-border-radius: 1;
+ -fx-border-width: 1;
+ -fx-background-color: derive(#1d1d1d, 20%);
+}
+
+.list-view {
+ -fx-background-insets: 0;
+ -fx-padding: 0;
+ -fx-background-color: derive(rgba(182, 190, 231, 0.46), 65%);
+}
+
+.list-cell {
+ -fx-label-padding: 0 0 0 0;
+ -fx-graphic-text-gap : 0;
+ -fx-padding: 0 0 0 0;
+ -fx-border-color: rgba(183, 183, 183, 0.76);
+ -fx-border-width: 1 0 1 1;
+}
+
+.list-cell:filled:even {
+ -fx-background-color: rgb(231, 232, 238);
+}
+
+.list-cell:filled:odd {
+ -fx-background-color: rgb(255, 255, 255);
+}
+
+.list-cell:filled:selected {
+ -fx-background-color: #a5cdff;
+}
+
+.list-cell:filled:selected #cardPane {
+ -fx-border-color: #3e7b91;
+ -fx-border-width: 1;
+}
+
+.list-cell .label {
+ -fx-text-fill: black;
+}
+
+.cell_big_label {
+ -fx-font-family: "Segoe UI Semibold";
+ -fx-font-size: 16px;
+ -fx-text-fill: #010504;
+}
+
+.cell_small_label {
+ -fx-font-family: "Segoe UI";
+ -fx-font-size: 13px;
+ -fx-text-fill: #010504;
+}
+
+.stack-pane {
+ -fx-background-color: derive(#1d1d1d, 20%);
+}
+
+.pane-with-border {
+ -fx-background-color: derive(rgba(182, 190, 231, 0.46), 20%);
+ -fx-border-color: derive(rgba(182, 190, 231, 0.46), 20%);
+ -fx-border-top-width: 1px;
+}
+
+.status-bar {
+ -fx-background-color: derive(rgb(195, 197, 211), 30%);
+}
+
+.result-display {
+ -fx-background-color: transparent;
+ -fx-font-family: "Segoe UI Light";
+ -fx-font-size: 13pt;
+ -fx-text-fill: #000000;
+}
+
+.result-display .label {
+ -fx-text-fill: black !important;
+}
+
+.status-bar .label {
+ -fx-font-family: "Segoe UI Light";
+ -fx-text-fill: #000000;
+ -fx-padding: 4px;
+ -fx-pref-height: 30px;
+}
+
+.status-bar-with-border {
+ -fx-background-color: derive(#ffffff, 30%);
+ -fx-border-color: derive(rgba(183, 183, 183, 0.76), 25%);
+ -fx-border-width: 1px;
+}
+
+.status-bar-with-border .label {
+ -fx-text-fill: #000000;
+}
+
+
+.grid-pane {
+ -fx-background-color: derive(#eaeaea, 30%);
+ -fx-border-color: derive(rgb(234, 234, 234), 30%);
+ -fx-border-width: 1px;
+}
+
+.grid-pane .stack-pane {
+ -fx-background-color: derive(#eaeaea, 30%);
+}
+
+.context-menu {
+ -fx-background-color: derive(#eaeaea, 50%);
+}
+
+.context-menu .label {
+ -fx-text-fill: #000000;
+}
+
+.menu-bar {
+ -fx-background-color: derive(rgb(101, 110, 147), 30%);
+}
+
+.menu-bar .label {
+ -fx-font-size: 14pt;
+ -fx-font-family: "Segoe UI Light";
+ -fx-text-fill: #000000;
+ -fx-opacity: 0.9;
+}
+
+.menu .left-container {
+ -fx-background-color: #ffffff;
+}
+
+
+/*
+ * Metro style Push Button
+ * Author: Pedro Duque Vieira
+ * http://pixelduke.wordpress.com/2012/10/23/jmetro-windows-8-controls-on-java/
+ */
+.button {
+ -fx-padding: 5 22 5 22;
+ -fx-border-color: #e2e2e2;
+ -fx-border-width: 2;
+ -fx-background-radius: 0;
+ -fx-background-color: #1d1d1d;
+ -fx-font-family: "Segoe UI", Helvetica, Arial, sans-serif;
+ -fx-font-size: 11pt;
+ -fx-text-fill: #d8d8d8;
+ -fx-background-insets: 0 0 0 0, 0, 1, 2;
+}
+
+.button:hover {
+ -fx-background-color: #3a3a3a;
+}
+
+.button:pressed, .button:default:hover:pressed {
+ -fx-background-color: white;
+ -fx-text-fill: #1d1d1d;
+}
+
+.button:focused {
+ -fx-border-color: white, white;
+ -fx-border-width: 1, 1;
+ -fx-border-style: solid, segments(1, 1);
+ -fx-border-radius: 0, 0;
+ -fx-border-insets: 1 1 1 1, 0;
+}
+
+.button:disabled, .button:default:disabled {
+ -fx-opacity: 0.4;
+ -fx-background-color: #1d1d1d;
+ -fx-text-fill: white;
+}
+
+.button:default {
+ -fx-background-color: -fx-focus-color;
+ -fx-text-fill: #ffffff;
+}
+
+.button:default:hover {
+ -fx-background-color: derive(-fx-focus-color, 30%);
+}
+
+.dialog-pane {
+ -fx-background-color: #ffffff;
+}
+
+.dialog-pane > *.button-bar > *.container {
+ -fx-background-color: #ffffff;
+}
+
+.dialog-pane > *.label.content {
+ -fx-font-size: 14px;
+ -fx-font-weight: bold;
+ -fx-text-fill: white;
+}
+
+.dialog-pane:header *.header-panel {
+ -fx-background-color: derive(#ffffff, 25%);
+}
+
+.dialog-pane:header *.header-panel *.label {
+ -fx-font-size: 18px;
+ -fx-font-style: italic;
+ -fx-fill: #000000;
+ -fx-text-fill: #000000;
+}
+
+.scroll-bar {
+ -fx-background-color: derive(#ffffff, 20%);
+ -fx-border-color: rgba(183, 183, 183, 0.76);
+ -fx-border-width: 1;
+}
+
+.scroll-bar .thumb {
+ -fx-background-color: derive(rgba(183, 183, 183, 0.76), 50%);
+ -fx-background-insets: 3;
+}
+
+.scroll-bar .increment-button, .scroll-bar .decrement-button {
+ -fx-background-color: rgb(255, 255, 255);
+ -fx-padding: 0 0 0 0;
+}
+
+.scroll-bar .increment-arrow, .scroll-bar .decrement-arrow {
+ -fx-shape: " ";
+}
+
+.scroll-bar:vertical .increment-arrow, .scroll-bar:vertical .decrement-arrow {
+ -fx-padding: 1 8 1 8;
+}
+
+.scroll-bar:horizontal .increment-arrow, .scroll-bar:horizontal .decrement-arrow {
+ -fx-padding: 8 1 8 1;
+}
+
+#cardPane {
+ -fx-background-color: transparent;
+ -fx-border-width: 0;
+}
+
+#commandTypeLabel {
+ -fx-font-size: 11px;
+ -fx-text-fill: #F70D1A;
+}
+
+#commandTextField {
+ -fx-background-color: rgb(255, 255, 255);
+ -fx-background-insets: 0;
+ -fx-border-color: rgba(183, 183, 183, 0.76) rgba(183, 183, 183, 0.76) rgb(234, 234, 234) rgba(183, 183, 183, 0.76);
+ -fx-border-insets: 0;
+ -fx-border-width: 1;
+ -fx-font-family: "Segoe UI Light";
+ -fx-font-size: 13pt;
+ -fx-text-fill: #000000;
+}
+
+#filterField, #personListPanel, #personWebpage {
+ -fx-effect: innershadow(gaussian, black, 10, 0, 0, 0);
+}
+
+#resultDisplay .content {
+ -fx-background-color: rgb(255, 255, 255);
+ -fx-background-radius: 0;
+ -fx-border-color: rgba(183, 183, 183, 0.76) rgba(183, 183, 183, 0.76) rgb(234, 234, 234) rgba(183, 183, 183, 0.76);
+}
+
+#tags {
+ -fx-hgap: 7;
+ -fx-vgap: 3;
+}
+
+#tags .label {
+ -fx-text-fill: #000000;
+ -fx-background-color: #3e7b91;
+ -fx-padding: 1 3 1 3;
+ -fx-border-radius: 2;
+ -fx-background-radius: 2;
+ -fx-font-size: 11;
+}
diff --git a/src/main/resources/view/CommandBox.fxml b/src/main/resources/view/CommandBox.fxml
index 124283a392e..4191a8bdde1 100644
--- a/src/main/resources/view/CommandBox.fxml
+++ b/src/main/resources/view/CommandBox.fxml
@@ -4,6 +4,6 @@
-
+
diff --git a/src/main/resources/view/Extensions.css b/src/main/resources/view/Extensions.css
index bfe82a85964..47f13afda22 100644
--- a/src/main/resources/view/Extensions.css
+++ b/src/main/resources/view/Extensions.css
@@ -5,7 +5,8 @@
.list-cell:empty {
/* Empty cells will not have alternating colours */
- -fx-background: #383838;
+ -fx-background: transparent;
+ -fx-border-width: 0;
}
.tag-selector {
diff --git a/src/main/resources/view/GreenTheme.css b/src/main/resources/view/GreenTheme.css
new file mode 100644
index 00000000000..06737401f71
--- /dev/null
+++ b/src/main/resources/view/GreenTheme.css
@@ -0,0 +1,359 @@
+.background {
+ -fx-background-color: derive(white, 20%);
+ background-color: antiquewhite; /* Used in the default.html file */
+}
+
+.label {
+ -fx-font-size: 11pt;
+ -fx-font-family: "Segoe UI Semibold";
+ -fx-text-fill: black;
+ -fx-opacity: 0.9;
+}
+
+.label-bright {
+ -fx-font-size: 11pt;
+ -fx-font-family: "Segoe UI Semibold";
+ -fx-text-fill: white;
+ -fx-opacity: 1;
+}
+
+.label-header {
+ -fx-font-size: 32pt;
+ -fx-font-family: "Segoe UI Light";
+ -fx-text-fill: white;
+ -fx-opacity: 1;
+}
+
+.text-field {
+ -fx-font-size: 12pt;
+ -fx-font-family: "Segoe UI Semibold";
+}
+
+.tab-pane {
+ -fx-padding: 0 0 0 1;
+}
+
+.tab-pane .tab-header-area {
+ -fx-padding: 0 0 0 0;
+ -fx-min-height: 0;
+ -fx-max-height: 0;
+}
+
+.table-view {
+ -fx-base: #1d1d1d;
+ -fx-control-inner-background: #1d1d1d;
+ -fx-background-color: #1d1d1d;
+ -fx-table-cell-border-color: transparent;
+ -fx-table-header-border-color: transparent;
+ -fx-padding: 5;
+}
+
+.table-view .column-header-background {
+ -fx-background-color: transparent;
+}
+
+.table-view .column-header, .table-view .filler {
+ -fx-size: 35;
+ -fx-border-width: 0 0 1 0;
+ -fx-background-color: transparent;
+ -fx-border-color:
+ transparent
+ transparent
+ derive(-fx-base, 80%)
+ transparent;
+ -fx-border-insets: 0 10 1 0;
+}
+
+.table-view .column-header .label {
+ -fx-font-size: 20pt;
+ -fx-font-family: "Segoe UI Light";
+ -fx-text-fill: white;
+ -fx-alignment: center-left;
+ -fx-opacity: 1;
+}
+
+.table-view:focused .table-row-cell:filled:focused:selected {
+ -fx-background-color: -fx-focus-color;
+}
+
+.split-pane:horizontal .split-pane-divider {
+ -fx-background-color: derive(#1d1d1d, 20%);
+ -fx-border-color: transparent transparent transparent #4d4d4d;
+}
+
+.split-pane {
+ -fx-border-radius: 1;
+ -fx-border-width: 1;
+ -fx-background-color: derive(#1d1d1d, 20%);
+}
+
+.list-view {
+ -fx-background-insets: 0;
+ -fx-padding: 0;
+ -fx-background-color: derive(rgba(188, 231, 182, 0.46), 65%);
+}
+
+.list-cell {
+ -fx-label-padding: 0 0 0 0;
+ -fx-graphic-text-gap : 0;
+ -fx-padding: 0 0 0 0;
+ -fx-border-color: rgba(183, 183, 183, 0.76);
+ -fx-border-width: 1 0 1 1;
+}
+
+.list-cell:filled:even {
+ -fx-background-color: rgb(231, 238, 231);
+}
+
+.list-cell:filled:odd {
+ -fx-background-color: rgb(255, 255, 255);
+}
+
+.list-cell:filled:selected {
+ -fx-background-color: #a5cdff;
+}
+
+.list-cell:filled:selected #cardPane {
+ -fx-border-color: #3e7b91;
+ -fx-border-width: 1;
+}
+
+.list-cell .label {
+ -fx-text-fill: black;
+}
+
+.cell_big_label {
+ -fx-font-family: "Segoe UI Semibold";
+ -fx-font-size: 16px;
+ -fx-text-fill: #010504;
+}
+
+.cell_small_label {
+ -fx-font-family: "Segoe UI";
+ -fx-font-size: 13px;
+ -fx-text-fill: #010504;
+}
+
+.stack-pane {
+ -fx-background-color: derive(#1d1d1d, 20%);
+}
+
+.pane-with-border {
+ -fx-background-color: derive(rgba(188, 231, 182, 0.46), 20%);
+ -fx-border-color: derive(rgba(188, 231, 182, 0.46), 20%);
+ -fx-border-top-width: 1px;
+}
+
+.status-bar {
+ -fx-background-color: derive(rgb(198, 211, 195), 30%);
+}
+
+.result-display {
+ -fx-background-color: transparent;
+ -fx-font-family: "Segoe UI Light";
+ -fx-font-size: 13pt;
+ -fx-text-fill: #000000;
+}
+
+.result-display .label {
+ -fx-text-fill: black !important;
+}
+
+.status-bar .label {
+ -fx-font-family: "Segoe UI Light";
+ -fx-text-fill: #000000;
+ -fx-padding: 4px;
+ -fx-pref-height: 30px;
+}
+
+.status-bar-with-border {
+ -fx-background-color: derive(#ffffff, 30%);
+ -fx-border-color: derive(rgba(183, 183, 183, 0.76), 25%);
+ -fx-border-width: 1px;
+}
+
+.status-bar-with-border .label {
+ -fx-text-fill: #000000;
+}
+
+
+.grid-pane {
+ -fx-background-color: derive(#eaeaea, 30%);
+ -fx-border-color: derive(rgb(234, 234, 234), 30%);
+ -fx-border-width: 1px;
+}
+
+.grid-pane .stack-pane {
+ -fx-background-color: derive(#eaeaea, 30%);
+}
+
+.context-menu {
+ -fx-background-color: derive(#eaeaea, 50%);
+}
+
+.context-menu .label {
+ -fx-text-fill: #000000;
+}
+
+.menu-bar {
+ -fx-background-color: derive(rgb(106, 147, 101), 30%);
+}
+
+.menu-bar .label {
+ -fx-font-size: 14pt;
+ -fx-font-family: "Segoe UI Light";
+ -fx-text-fill: #000000;
+ -fx-opacity: 0.9;
+}
+
+.menu .left-container {
+ -fx-background-color: #ffffff;
+}
+
+
+/*
+ * Metro style Push Button
+ * Author: Pedro Duque Vieira
+ * http://pixelduke.wordpress.com/2012/10/23/jmetro-windows-8-controls-on-java/
+ */
+.button {
+ -fx-padding: 5 22 5 22;
+ -fx-border-color: #e2e2e2;
+ -fx-border-width: 2;
+ -fx-background-radius: 0;
+ -fx-background-color: #1d1d1d;
+ -fx-font-family: "Segoe UI", Helvetica, Arial, sans-serif;
+ -fx-font-size: 11pt;
+ -fx-text-fill: #d8d8d8;
+ -fx-background-insets: 0 0 0 0, 0, 1, 2;
+}
+
+.button:hover {
+ -fx-background-color: #3a3a3a;
+}
+
+.button:pressed, .button:default:hover:pressed {
+ -fx-background-color: white;
+ -fx-text-fill: #1d1d1d;
+}
+
+.button:focused {
+ -fx-border-color: white, white;
+ -fx-border-width: 1, 1;
+ -fx-border-style: solid, segments(1, 1);
+ -fx-border-radius: 0, 0;
+ -fx-border-insets: 1 1 1 1, 0;
+}
+
+.button:disabled, .button:default:disabled {
+ -fx-opacity: 0.4;
+ -fx-background-color: #1d1d1d;
+ -fx-text-fill: white;
+}
+
+.button:default {
+ -fx-background-color: -fx-focus-color;
+ -fx-text-fill: #ffffff;
+}
+
+.button:default:hover {
+ -fx-background-color: derive(-fx-focus-color, 30%);
+}
+
+.dialog-pane {
+ -fx-background-color: #ffffff;
+}
+
+.dialog-pane > *.button-bar > *.container {
+ -fx-background-color: #ffffff;
+}
+
+.dialog-pane > *.label.content {
+ -fx-font-size: 14px;
+ -fx-font-weight: bold;
+ -fx-text-fill: white;
+}
+
+.dialog-pane:header *.header-panel {
+ -fx-background-color: derive(#ffffff, 25%);
+}
+
+.dialog-pane:header *.header-panel *.label {
+ -fx-font-size: 18px;
+ -fx-font-style: italic;
+ -fx-fill: #000000;
+ -fx-text-fill: #000000;
+}
+
+.scroll-bar {
+ -fx-background-color: derive(#ffffff, 20%);
+ -fx-border-color: rgba(183, 183, 183, 0.76);
+ -fx-border-width: 1;
+}
+
+.scroll-bar .thumb {
+ -fx-background-color: derive(rgba(183, 183, 183, 0.76), 50%);
+ -fx-background-insets: 3;
+}
+
+.scroll-bar .increment-button, .scroll-bar .decrement-button {
+ -fx-background-color: rgb(255, 255, 255);
+ -fx-padding: 0 0 0 0;
+}
+
+.scroll-bar .increment-arrow, .scroll-bar .decrement-arrow {
+ -fx-shape: " ";
+}
+
+.scroll-bar:vertical .increment-arrow, .scroll-bar:vertical .decrement-arrow {
+ -fx-padding: 1 8 1 8;
+}
+
+.scroll-bar:horizontal .increment-arrow, .scroll-bar:horizontal .decrement-arrow {
+ -fx-padding: 8 1 8 1;
+}
+
+#cardPane {
+ -fx-background-color: transparent;
+ -fx-border-width: 0;
+}
+
+#commandTypeLabel {
+ -fx-font-size: 11px;
+ -fx-text-fill: #F70D1A;
+}
+
+#commandTextField {
+ -fx-background-color: rgb(255, 255, 255);
+ -fx-background-insets: 0;
+ -fx-border-color: rgba(183, 183, 183, 0.76) rgba(183, 183, 183, 0.76) rgb(234, 234, 234) rgba(183, 183, 183, 0.76);
+ -fx-border-insets: 0;
+ -fx-border-width: 1;
+ -fx-font-family: "Segoe UI Light";
+ -fx-font-size: 13pt;
+ -fx-text-fill: #000000;
+}
+
+#filterField, #personListPanel, #personWebpage {
+ -fx-effect: innershadow(gaussian, black, 10, 0, 0, 0);
+}
+
+#resultDisplay .content {
+ -fx-background-color: rgb(255, 255, 255);
+ -fx-background-radius: 0;
+ -fx-border-color: rgba(183, 183, 183, 0.76) rgba(183, 183, 183, 0.76) rgb(234, 234, 234) rgba(183, 183, 183, 0.76);
+}
+
+#tags {
+ -fx-hgap: 7;
+ -fx-vgap: 3;
+}
+
+#tags .label {
+ -fx-text-fill: #000000;
+ -fx-background-color: #3e7b91;
+ -fx-padding: 1 3 1 3;
+ -fx-border-radius: 2;
+ -fx-background-radius: 2;
+ -fx-font-size: 11;
+}
diff --git a/src/main/resources/view/LightTheme.css b/src/main/resources/view/LightTheme.css
new file mode 100644
index 00000000000..980348f95e0
--- /dev/null
+++ b/src/main/resources/view/LightTheme.css
@@ -0,0 +1,359 @@
+.background {
+ -fx-background-color: derive(white, 20%);
+ background-color: antiquewhite; /* Used in the default.html file */
+}
+
+.label {
+ -fx-font-size: 11pt;
+ -fx-font-family: "Segoe UI Semibold";
+ -fx-text-fill: black;
+ -fx-opacity: 0.9;
+}
+
+.label-bright {
+ -fx-font-size: 11pt;
+ -fx-font-family: "Segoe UI Semibold";
+ -fx-text-fill: white;
+ -fx-opacity: 1;
+}
+
+.label-header {
+ -fx-font-size: 32pt;
+ -fx-font-family: "Segoe UI Light";
+ -fx-text-fill: white;
+ -fx-opacity: 1;
+}
+
+.text-field {
+ -fx-font-size: 12pt;
+ -fx-font-family: "Segoe UI Semibold";
+}
+
+.tab-pane {
+ -fx-padding: 0 0 0 1;
+}
+
+.tab-pane .tab-header-area {
+ -fx-padding: 0 0 0 0;
+ -fx-min-height: 0;
+ -fx-max-height: 0;
+}
+
+.table-view {
+ -fx-base: #ffffff;
+ -fx-control-inner-background: #ffffff;
+ -fx-background-color: #ffffff;
+ -fx-table-cell-border-color: transparent;
+ -fx-table-header-border-color: transparent;
+ -fx-padding: 5;
+}
+
+.table-view .column-header-background {
+ -fx-background-color: transparent;
+}
+
+.table-view .column-header, .table-view .filler {
+ -fx-size: 35;
+ -fx-border-width: 0 0 1 0;
+ -fx-background-color: transparent;
+ -fx-border-color:
+ transparent
+ transparent
+ derive(-fx-base, 80%)
+ transparent;
+ -fx-border-insets: 0 10 1 0;
+}
+
+.table-view .column-header .label {
+ -fx-font-size: 20pt;
+ -fx-font-family: "Segoe UI Light";
+ -fx-text-fill: white;
+ -fx-alignment: center-left;
+ -fx-opacity: 1;
+}
+
+.table-view:focused .table-row-cell:filled:focused:selected {
+ -fx-background-color: -fx-focus-color;
+}
+
+.split-pane:horizontal .split-pane-divider {
+ -fx-background-color: derive(#ffffff, 20%);
+ -fx-border-color: transparent transparent transparent #4d4d4d;
+}
+
+.split-pane {
+ -fx-border-radius: 1;
+ -fx-border-width: 1;
+ -fx-background-color: derive(#ffffff, 20%);
+}
+
+.list-view {
+ -fx-background-insets: 0;
+ -fx-padding: 0;
+ -fx-background-color: derive(#ffffff, 20%);
+}
+
+.list-cell {
+ -fx-label-padding: 0 0 0 0;
+ -fx-graphic-text-gap : 0;
+ -fx-padding: 0 0 0 0;
+ -fx-border-color: rgba(183, 183, 183, 0.76);
+ -fx-border-width: 1 0 1 1;
+}
+
+.list-cell:filled:even {
+ -fx-background-color: rgb(245, 245, 245);
+}
+
+.list-cell:filled:odd {
+ -fx-background-color: rgb(255, 255, 255);
+}
+
+.list-cell:filled:selected {
+ -fx-background-color: #a5cdff;
+}
+
+.list-cell:filled:selected #cardPane {
+ -fx-border-color: #3e7b91;
+ -fx-border-width: 1;
+}
+
+.list-cell .label {
+ -fx-text-fill: black;
+}
+
+.cell_big_label {
+ -fx-font-family: "Segoe UI Semibold";
+ -fx-font-size: 16px;
+ -fx-text-fill: #010504;
+}
+
+.cell_small_label {
+ -fx-font-family: "Segoe UI";
+ -fx-font-size: 13px;
+ -fx-text-fill: #010504;
+}
+
+.stack-pane {
+ -fx-background-color: derive(#ffffff, 20%);
+}
+
+.pane-with-border {
+ -fx-background-color: derive(rgb(255, 255, 255), 20%);
+ -fx-border-color: derive(rgb(255, 255, 255), 10%);
+ -fx-border-top-width: 1px;
+}
+
+.status-bar {
+ -fx-background-color: derive(rgb(234, 234, 234), 30%);
+}
+
+.result-display {
+ -fx-background-color: transparent;
+ -fx-font-family: "Segoe UI Light";
+ -fx-font-size: 13pt;
+ -fx-text-fill: #000000;
+}
+
+.result-display .label {
+ -fx-text-fill: black !important;
+}
+
+.status-bar .label {
+ -fx-font-family: "Segoe UI Light";
+ -fx-text-fill: #000000;
+ -fx-padding: 4px;
+ -fx-pref-height: 30px;
+}
+
+.status-bar-with-border {
+ -fx-background-color: derive(#ffffff, 30%);
+ -fx-border-color: derive(rgba(183, 183, 183, 0.76), 25%);
+ -fx-border-width: 1px;
+}
+
+.status-bar-with-border .label {
+ -fx-text-fill: #000000;
+}
+
+
+.grid-pane {
+ -fx-background-color: derive(#eaeaea, 30%);
+ -fx-border-color: derive(rgb(234, 234, 234), 30%);
+ -fx-border-width: 1px;
+}
+
+.grid-pane .stack-pane {
+ -fx-background-color: derive(#eaeaea, 30%);
+}
+
+.context-menu {
+ -fx-background-color: derive(#eaeaea, 50%);
+}
+
+.context-menu .label {
+ -fx-text-fill: #000000;
+}
+
+.menu-bar {
+ -fx-background-color: derive(#eaeaea, 20%);
+}
+
+.menu-bar .label {
+ -fx-font-size: 14pt;
+ -fx-font-family: "Segoe UI Light";
+ -fx-text-fill: #000000;
+ -fx-opacity: 0.9;
+}
+
+.menu .left-container {
+ -fx-background-color: #ffffff;
+}
+
+
+/*
+ * Metro style Push Button
+ * Author: Pedro Duque Vieira
+ * http://pixelduke.wordpress.com/2012/10/23/jmetro-windows-8-controls-on-java/
+ */
+.button {
+ -fx-padding: 5 22 5 22;
+ -fx-border-color: #e2e2e2;
+ -fx-border-width: 2;
+ -fx-background-radius: 0;
+ -fx-background-color: #ffffff;
+ -fx-font-family: "Segoe UI", Helvetica, Arial, sans-serif;
+ -fx-font-size: 11pt;
+ -fx-text-fill: #d8d8d8;
+ -fx-background-insets: 0 0 0 0, 0, 1, 2;
+}
+
+.button:hover {
+ -fx-background-color: #ffffff;
+}
+
+.button:pressed, .button:default:hover:pressed {
+ -fx-background-color: white;
+ -fx-text-fill: #1d1d1d;
+}
+
+.button:focused {
+ -fx-border-color: white, white;
+ -fx-border-width: 1, 1;
+ -fx-border-style: solid, segments(1, 1);
+ -fx-border-radius: 0, 0;
+ -fx-border-insets: 1 1 1 1, 0;
+}
+
+.button:disabled, .button:default:disabled {
+ -fx-opacity: 0.4;
+ -fx-background-color: #ffffff;
+ -fx-text-fill: white;
+}
+
+.button:default {
+ -fx-background-color: -fx-focus-color;
+ -fx-text-fill: #ffffff;
+}
+
+.button:default:hover {
+ -fx-background-color: derive(-fx-focus-color, 30%);
+}
+
+.dialog-pane {
+ -fx-background-color: #ffffff;
+}
+
+.dialog-pane > *.button-bar > *.container {
+ -fx-background-color: #ffffff;
+}
+
+.dialog-pane > *.label.content {
+ -fx-font-size: 14px;
+ -fx-font-weight: bold;
+ -fx-text-fill: white;
+}
+
+.dialog-pane:header *.header-panel {
+ -fx-background-color: derive(#ffffff, 25%);
+}
+
+.dialog-pane:header *.header-panel *.label {
+ -fx-font-size: 18px;
+ -fx-font-style: italic;
+ -fx-fill: #000000;
+ -fx-text-fill: #000000;
+}
+
+.scroll-bar {
+ -fx-background-color: derive(#ffffff, 20%);
+ -fx-border-color: rgba(183, 183, 183, 0.76);
+ -fx-border-width: 1;
+}
+
+.scroll-bar .thumb {
+ -fx-background-color: derive(rgba(183, 183, 183, 0.76), 50%);
+ -fx-background-insets: 3;
+}
+
+.scroll-bar .increment-button, .scroll-bar .decrement-button {
+ -fx-background-color: rgb(255, 255, 255);
+ -fx-padding: 0 0 0 0;
+}
+
+.scroll-bar .increment-arrow, .scroll-bar .decrement-arrow {
+ -fx-shape: " ";
+}
+
+.scroll-bar:vertical .increment-arrow, .scroll-bar:vertical .decrement-arrow {
+ -fx-padding: 1 8 1 8;
+}
+
+.scroll-bar:horizontal .increment-arrow, .scroll-bar:horizontal .decrement-arrow {
+ -fx-padding: 8 1 8 1;
+}
+
+#cardPane {
+ -fx-background-color: transparent;
+ -fx-border-width: 0;
+}
+
+#commandTypeLabel {
+ -fx-font-size: 11px;
+ -fx-text-fill: #F70D1A;
+}
+
+#commandTextField {
+ -fx-background-color: rgb(255, 255, 255);
+ -fx-background-insets: 0;
+ -fx-border-color: rgba(183, 183, 183, 0.76) rgba(183, 183, 183, 0.76) rgb(234, 234, 234) rgba(183, 183, 183, 0.76);
+ -fx-border-insets: 0;
+ -fx-border-width: 1;
+ -fx-font-family: "Segoe UI Light";
+ -fx-font-size: 13pt;
+ -fx-text-fill: #000000;
+}
+
+#filterField, #personListPanel, #personWebpage {
+ -fx-effect: innershadow(gaussian, black, 10, 0, 0, 0);
+}
+
+#resultDisplay .content {
+ -fx-background-color: rgb(255, 255, 255);
+ -fx-background-radius: 0;
+ -fx-border-color: rgba(183, 183, 183, 0.76) rgba(183, 183, 183, 0.76) rgb(234, 234, 234) rgba(183, 183, 183, 0.76);
+}
+
+#tags {
+ -fx-hgap: 7;
+ -fx-vgap: 3;
+}
+
+#tags .label {
+ -fx-text-fill: #000000;
+ -fx-background-color: #3e7b91;
+ -fx-padding: 1 3 1 3;
+ -fx-border-radius: 2;
+ -fx-background-radius: 2;
+ -fx-font-size: 11;
+}
diff --git a/src/main/resources/view/MainWindow.fxml b/src/main/resources/view/MainWindow.fxml
index 7778f666a0a..232de1a82bb 100644
--- a/src/main/resources/view/MainWindow.fxml
+++ b/src/main/resources/view/MainWindow.fxml
@@ -12,7 +12,7 @@
+ title="HR Insight" minWidth="450" minHeight="600" onCloseRequest="#handleExit">
diff --git a/src/main/resources/view/PersonListCard.fxml b/src/main/resources/view/PersonListCard.fxml
index f5e812e25e6..f1aa6215703 100644
--- a/src/main/resources/view/PersonListCard.fxml
+++ b/src/main/resources/view/PersonListCard.fxml
@@ -25,12 +25,17 @@
-
+
-
-
-
+
+
+
+
+
+
+
+
diff --git a/src/main/resources/view/RedTheme.css b/src/main/resources/view/RedTheme.css
new file mode 100644
index 00000000000..d4d4fd722af
--- /dev/null
+++ b/src/main/resources/view/RedTheme.css
@@ -0,0 +1,359 @@
+.background {
+ -fx-background-color: derive(white, 20%);
+ background-color: antiquewhite; /* Used in the default.html file */
+}
+
+.label {
+ -fx-font-size: 11pt;
+ -fx-font-family: "Segoe UI Semibold";
+ -fx-text-fill: black;
+ -fx-opacity: 0.9;
+}
+
+.label-bright {
+ -fx-font-size: 11pt;
+ -fx-font-family: "Segoe UI Semibold";
+ -fx-text-fill: white;
+ -fx-opacity: 1;
+}
+
+.label-header {
+ -fx-font-size: 32pt;
+ -fx-font-family: "Segoe UI Light";
+ -fx-text-fill: white;
+ -fx-opacity: 1;
+}
+
+.text-field {
+ -fx-font-size: 12pt;
+ -fx-font-family: "Segoe UI Semibold";
+}
+
+.tab-pane {
+ -fx-padding: 0 0 0 1;
+}
+
+.tab-pane .tab-header-area {
+ -fx-padding: 0 0 0 0;
+ -fx-min-height: 0;
+ -fx-max-height: 0;
+}
+
+.table-view {
+ -fx-base: #1d1d1d;
+ -fx-control-inner-background: #1d1d1d;
+ -fx-background-color: #1d1d1d;
+ -fx-table-cell-border-color: transparent;
+ -fx-table-header-border-color: transparent;
+ -fx-padding: 5;
+}
+
+.table-view .column-header-background {
+ -fx-background-color: transparent;
+}
+
+.table-view .column-header, .table-view .filler {
+ -fx-size: 35;
+ -fx-border-width: 0 0 1 0;
+ -fx-background-color: transparent;
+ -fx-border-color:
+ transparent
+ transparent
+ derive(-fx-base, 80%)
+ transparent;
+ -fx-border-insets: 0 10 1 0;
+}
+
+.table-view .column-header .label {
+ -fx-font-size: 20pt;
+ -fx-font-family: "Segoe UI Light";
+ -fx-text-fill: white;
+ -fx-alignment: center-left;
+ -fx-opacity: 1;
+}
+
+.table-view:focused .table-row-cell:filled:focused:selected {
+ -fx-background-color: -fx-focus-color;
+}
+
+.split-pane:horizontal .split-pane-divider {
+ -fx-background-color: derive(#1d1d1d, 20%);
+ -fx-border-color: transparent transparent transparent #4d4d4d;
+}
+
+.split-pane {
+ -fx-border-radius: 1;
+ -fx-border-width: 1;
+ -fx-background-color: derive(#1d1d1d, 20%);
+}
+
+.list-view {
+ -fx-background-insets: 0;
+ -fx-padding: 0;
+ -fx-background-color: derive(rgba(231, 182, 182, 0.46), 60%);
+}
+
+.list-cell {
+ -fx-label-padding: 0 0 0 0;
+ -fx-graphic-text-gap : 0;
+ -fx-padding: 0 0 0 0;
+ -fx-border-color: rgba(183, 183, 183, 0.76);
+ -fx-border-width: 1 0 1 1;
+}
+
+.list-cell:filled:even {
+ -fx-background-color: rgb(238, 231, 231);
+}
+
+.list-cell:filled:odd {
+ -fx-background-color: rgb(255, 255, 255);
+}
+
+.list-cell:filled:selected {
+ -fx-background-color: #a5cdff;
+}
+
+.list-cell:filled:selected #cardPane {
+ -fx-border-color: #3e7b91;
+ -fx-border-width: 1;
+}
+
+.list-cell .label {
+ -fx-text-fill: black;
+}
+
+.cell_big_label {
+ -fx-font-family: "Segoe UI Semibold";
+ -fx-font-size: 16px;
+ -fx-text-fill: #010504;
+}
+
+.cell_small_label {
+ -fx-font-family: "Segoe UI";
+ -fx-font-size: 13px;
+ -fx-text-fill: #010504;
+}
+
+.stack-pane {
+ -fx-background-color: derive(#1d1d1d, 20%);
+}
+
+.pane-with-border {
+ -fx-background-color: derive(rgba(231, 182, 182, 0.46), 20%);
+ -fx-border-color: derive(rgba(231, 182, 182, 0.46), 20%);
+ -fx-border-top-width: 1px;
+}
+
+.status-bar {
+ -fx-background-color: derive(rgb(211, 195, 195), 30%);
+}
+
+.result-display {
+ -fx-background-color: transparent;
+ -fx-font-family: "Segoe UI Light";
+ -fx-font-size: 13pt;
+ -fx-text-fill: #000000;
+}
+
+.result-display .label {
+ -fx-text-fill: black !important;
+}
+
+.status-bar .label {
+ -fx-font-family: "Segoe UI Light";
+ -fx-text-fill: #000000;
+ -fx-padding: 4px;
+ -fx-pref-height: 30px;
+}
+
+.status-bar-with-border {
+ -fx-background-color: derive(#ffffff, 30%);
+ -fx-border-color: derive(rgba(183, 183, 183, 0.76), 25%);
+ -fx-border-width: 1px;
+}
+
+.status-bar-with-border .label {
+ -fx-text-fill: #000000;
+}
+
+
+.grid-pane {
+ -fx-background-color: derive(#eaeaea, 30%);
+ -fx-border-color: derive(rgb(234, 234, 234), 30%);
+ -fx-border-width: 1px;
+}
+
+.grid-pane .stack-pane {
+ -fx-background-color: derive(#eaeaea, 30%);
+}
+
+.context-menu {
+ -fx-background-color: derive(#eaeaea, 50%);
+}
+
+.context-menu .label {
+ -fx-text-fill: #000000;
+}
+
+.menu-bar {
+ -fx-background-color: derive(rgb(147, 101, 101), 30%);
+}
+
+.menu-bar .label {
+ -fx-font-size: 14pt;
+ -fx-font-family: "Segoe UI Light";
+ -fx-text-fill: #000000;
+ -fx-opacity: 0.9;
+}
+
+.menu .left-container {
+ -fx-background-color: #ffffff;
+}
+
+
+/*
+ * Metro style Push Button
+ * Author: Pedro Duque Vieira
+ * http://pixelduke.wordpress.com/2012/10/23/jmetro-windows-8-controls-on-java/
+ */
+.button {
+ -fx-padding: 5 22 5 22;
+ -fx-border-color: #e2e2e2;
+ -fx-border-width: 2;
+ -fx-background-radius: 0;
+ -fx-background-color: #1d1d1d;
+ -fx-font-family: "Segoe UI", Helvetica, Arial, sans-serif;
+ -fx-font-size: 11pt;
+ -fx-text-fill: #d8d8d8;
+ -fx-background-insets: 0 0 0 0, 0, 1, 2;
+}
+
+.button:hover {
+ -fx-background-color: #3a3a3a;
+}
+
+.button:pressed, .button:default:hover:pressed {
+ -fx-background-color: white;
+ -fx-text-fill: #1d1d1d;
+}
+
+.button:focused {
+ -fx-border-color: white, white;
+ -fx-border-width: 1, 1;
+ -fx-border-style: solid, segments(1, 1);
+ -fx-border-radius: 0, 0;
+ -fx-border-insets: 1 1 1 1, 0;
+}
+
+.button:disabled, .button:default:disabled {
+ -fx-opacity: 0.4;
+ -fx-background-color: #1d1d1d;
+ -fx-text-fill: white;
+}
+
+.button:default {
+ -fx-background-color: -fx-focus-color;
+ -fx-text-fill: #ffffff;
+}
+
+.button:default:hover {
+ -fx-background-color: derive(-fx-focus-color, 30%);
+}
+
+.dialog-pane {
+ -fx-background-color: #ffffff;
+}
+
+.dialog-pane > *.button-bar > *.container {
+ -fx-background-color: #ffffff;
+}
+
+.dialog-pane > *.label.content {
+ -fx-font-size: 14px;
+ -fx-font-weight: bold;
+ -fx-text-fill: white;
+}
+
+.dialog-pane:header *.header-panel {
+ -fx-background-color: derive(#ffffff, 25%);
+}
+
+.dialog-pane:header *.header-panel *.label {
+ -fx-font-size: 18px;
+ -fx-font-style: italic;
+ -fx-fill: #000000;
+ -fx-text-fill: #000000;
+}
+
+.scroll-bar {
+ -fx-background-color: derive(#ffffff, 20%);
+ -fx-border-color: rgba(183, 183, 183, 0.76);
+ -fx-border-width: 1;
+}
+
+.scroll-bar .thumb {
+ -fx-background-color: derive(rgba(183, 183, 183, 0.76), 50%);
+ -fx-background-insets: 3;
+}
+
+.scroll-bar .increment-button, .scroll-bar .decrement-button {
+ -fx-background-color: rgb(255, 255, 255);
+ -fx-padding: 0 0 0 0;
+}
+
+.scroll-bar .increment-arrow, .scroll-bar .decrement-arrow {
+ -fx-shape: " ";
+}
+
+.scroll-bar:vertical .increment-arrow, .scroll-bar:vertical .decrement-arrow {
+ -fx-padding: 1 8 1 8;
+}
+
+.scroll-bar:horizontal .increment-arrow, .scroll-bar:horizontal .decrement-arrow {
+ -fx-padding: 8 1 8 1;
+}
+
+#cardPane {
+ -fx-background-color: transparent;
+ -fx-border-width: 0;
+}
+
+#commandTypeLabel {
+ -fx-font-size: 11px;
+ -fx-text-fill: #F70D1A;
+}
+
+#commandTextField {
+ -fx-background-color: rgb(255, 255, 255);
+ -fx-background-insets: 0;
+ -fx-border-color: rgba(183, 183, 183, 0.76) rgba(183, 183, 183, 0.76) rgb(234, 234, 234) rgba(183, 183, 183, 0.76);
+ -fx-border-insets: 0;
+ -fx-border-width: 1;
+ -fx-font-family: "Segoe UI Light";
+ -fx-font-size: 13pt;
+ -fx-text-fill: #000000;
+}
+
+#filterField, #personListPanel, #personWebpage {
+ -fx-effect: innershadow(gaussian, black, 10, 0, 0, 0);
+}
+
+#resultDisplay .content {
+ -fx-background-color: rgb(255, 255, 255);
+ -fx-background-radius: 0;
+ -fx-border-color: rgba(183, 183, 183, 0.76) rgba(183, 183, 183, 0.76) rgb(234, 234, 234) rgba(183, 183, 183, 0.76);
+}
+
+#tags {
+ -fx-hgap: 7;
+ -fx-vgap: 3;
+}
+
+#tags .label {
+ -fx-text-fill: #000000;
+ -fx-background-color: #3e7b91;
+ -fx-padding: 1 3 1 3;
+ -fx-border-radius: 2;
+ -fx-background-radius: 2;
+ -fx-font-size: 11;
+}
diff --git a/src/test/data/JsonSerializableAddressBookTest/duplicatePersonAddressBook.json b/src/test/data/JsonSerializableAddressBookTest/duplicatePersonAddressBook.json
index a7427fe7aa2..3137e316c63 100644
--- a/src/test/data/JsonSerializableAddressBookTest/duplicatePersonAddressBook.json
+++ b/src/test/data/JsonSerializableAddressBookTest/duplicatePersonAddressBook.json
@@ -1,14 +1,26 @@
{
- "persons": [ {
- "name": "Alice Pauline",
- "phone": "94351253",
- "email": "alice@example.com",
- "address": "123, Jurong West Ave 6, #08-111",
- "tags": [ "friends" ]
- }, {
- "name": "Alice Pauline",
- "phone": "94351253",
- "email": "pauline@example.com",
- "address": "4th street"
- } ]
+ "persons": [
+ {
+ "name": "Alice Pauline",
+ "phone": "94351253",
+ "email": "alice@example.com",
+ "address": "123, Jurong West Ave 6, #08-111",
+ "salary": "100000",
+ "claimBudget": "1",
+ "department": "Finance",
+ "dob": "2002-02-27",
+ "leave": "111101010101"
+ },
+ {
+ "name": "Alice Pauline",
+ "phone": "94351253",
+ "email": "pauline@example.com",
+ "address": "123, Jurong West Ave 6, #08-111",
+ "salary": "100000",
+ "claimBudget": "1",
+ "department": "Finance",
+ "dob": "2002-02-27",
+ "leave": "111101010101"
+ }
+ ]
}
diff --git a/src/test/data/JsonSerializableAddressBookTest/typicalPersonsAddressBook.json b/src/test/data/JsonSerializableAddressBookTest/typicalPersonsAddressBook.json
index 72262099d35..3633f1429bc 100644
--- a/src/test/data/JsonSerializableAddressBookTest/typicalPersonsAddressBook.json
+++ b/src/test/data/JsonSerializableAddressBookTest/typicalPersonsAddressBook.json
@@ -1,46 +1,82 @@
{
"_comment": "AddressBook save file which contains the same Person values as in TypicalPersons#getTypicalAddressBook()",
- "persons" : [ {
- "name" : "Alice Pauline",
- "phone" : "94351253",
- "email" : "alice@example.com",
- "address" : "123, Jurong West Ave 6, #08-111",
- "tags" : [ "friends" ]
- }, {
- "name" : "Benson Meier",
- "phone" : "98765432",
- "email" : "johnd@example.com",
- "address" : "311, Clementi Ave 2, #02-25",
- "tags" : [ "owesMoney", "friends" ]
- }, {
- "name" : "Carl Kurz",
- "phone" : "95352563",
- "email" : "heinz@example.com",
- "address" : "wall street",
- "tags" : [ ]
- }, {
- "name" : "Daniel Meier",
- "phone" : "87652533",
- "email" : "cornelia@example.com",
- "address" : "10th street",
- "tags" : [ "friends" ]
- }, {
- "name" : "Elle Meyer",
- "phone" : "9482224",
- "email" : "werner@example.com",
- "address" : "michegan ave",
- "tags" : [ ]
- }, {
- "name" : "Fiona Kunz",
- "phone" : "9482427",
- "email" : "lydia@example.com",
- "address" : "little tokyo",
- "tags" : [ ]
- }, {
- "name" : "George Best",
- "phone" : "9482442",
- "email" : "anna@example.com",
- "address" : "4th street",
- "tags" : [ ]
- } ]
+ "persons": [
+ {
+ "name": "Alice Pauline",
+ "phone": "94351253",
+ "email": "alice@example.com",
+ "address": "123, Jurong West Ave 6, #08-111",
+ "salary": "10000",
+ "claimBudget": "5000",
+ "department": "Engineering",
+ "dob": "2000-01-01",
+ "leave": "111101010101"
+ },
+ {
+ "name": "Benson Meier",
+ "phone": "98765432",
+ "email": "johnd@example.com",
+ "address": "311, Clementi Ave 2, #02-25",
+ "salary": "100",
+ "claimBudget": "5",
+ "department": "Sales",
+ "dob": "1997-05-05",
+ "leave": "010101010101"
+ },
+ {
+ "name": "Carl Kurz",
+ "phone": "95352563",
+ "email": "heinz@example.com",
+ "address": "wall street",
+ "salary": "1000000000000",
+ "claimBudget": "500000",
+ "department": "Executive",
+ "dob": "1991-12-31",
+ "leave": "000101010001"
+ },
+ {
+ "name": "Daniel Meier",
+ "phone": "87652533",
+ "email": "cornelia@example.com",
+ "address": "10th street",
+ "salary": "23232323232",
+ "claimBudget": "19191919",
+ "department": "Marketing",
+ "dob": "1969-07-27",
+ "leave": "111111111111"
+ },
+ {
+ "name": "Elle Meyer",
+ "phone": "9482224",
+ "email": "werner@example.com",
+ "address": "michegan ave",
+ "salary": "3000",
+ "claimBudget": "200",
+ "department": "HR",
+ "dob": "1999-06-17",
+ "leave": "000000000001"
+ },
+ {
+ "name": "Fiona Kunz",
+ "phone": "9482427",
+ "email": "lydia@example.com",
+ "address": "little tokyo",
+ "salary": "100000",
+ "claimBudget": "1",
+ "department": "Finance",
+ "dob": "2002-02-27",
+ "leave": "000000100000"
+ },
+ {
+ "name": "George Best",
+ "phone": "9482442",
+ "email": "anna@example.com",
+ "address": "4th street",
+ "salary": "10000",
+ "claimBudget": "5000",
+ "department": "Engineering",
+ "dob": "2005-09-30",
+ "leave": "111111000000"
+ }
+ ]
}
diff --git a/src/test/java/seedu/address/logic/LogicManagerTest.java b/src/test/java/seedu/address/logic/LogicManagerTest.java
index baf8ce336a2..f2342255081 100644
--- a/src/test/java/seedu/address/logic/LogicManagerTest.java
+++ b/src/test/java/seedu/address/logic/LogicManagerTest.java
@@ -4,9 +4,13 @@
import static seedu.address.logic.Messages.MESSAGE_INVALID_PERSON_DISPLAYED_INDEX;
import static seedu.address.logic.Messages.MESSAGE_UNKNOWN_COMMAND;
import static seedu.address.logic.commands.CommandTestUtil.ADDRESS_DESC_AMY;
+import static seedu.address.logic.commands.CommandTestUtil.BUDGET_DESC;
+import static seedu.address.logic.commands.CommandTestUtil.DEPARTMENT_DESC;
+import static seedu.address.logic.commands.CommandTestUtil.DOB_DESC;
import static seedu.address.logic.commands.CommandTestUtil.EMAIL_DESC_AMY;
import static seedu.address.logic.commands.CommandTestUtil.NAME_DESC_AMY;
import static seedu.address.logic.commands.CommandTestUtil.PHONE_DESC_AMY;
+import static seedu.address.logic.commands.CommandTestUtil.SALARY_DESC;
import static seedu.address.testutil.Assert.assertThrows;
import static seedu.address.testutil.TypicalPersons.AMY;
@@ -67,7 +71,8 @@ public void execute_commandExecutionError_throwsCommandException() {
@Test
public void execute_validCommand_success() throws Exception {
String listCommand = ListCommand.COMMAND_WORD;
- assertCommandSuccess(listCommand, ListCommand.MESSAGE_SUCCESS, model);
+ assertCommandSuccess(listCommand, String.format(
+ Messages.MESSAGE_LIST_SUCCESS, model.getFilteredPersonList().size()), model);
}
@Test
@@ -165,9 +170,9 @@ public void saveAddressBook(ReadOnlyAddressBook addressBook, Path filePath)
logic = new LogicManager(model, storage);
// Triggers the saveAddressBook method by executing an add command
- String addCommand = AddCommand.COMMAND_WORD + NAME_DESC_AMY + PHONE_DESC_AMY
- + EMAIL_DESC_AMY + ADDRESS_DESC_AMY;
- Person expectedPerson = new PersonBuilder(AMY).withTags().build();
+ String addCommand = AddCommand.COMMAND_WORD + NAME_DESC_AMY + PHONE_DESC_AMY + EMAIL_DESC_AMY
+ + ADDRESS_DESC_AMY + SALARY_DESC + BUDGET_DESC + DEPARTMENT_DESC + DOB_DESC;
+ Person expectedPerson = new PersonBuilder(AMY).build();
ModelManager expectedModel = new ModelManager();
expectedModel.addPerson(expectedPerson);
assertCommandFailure(addCommand, CommandException.class, expectedMessage, expectedModel);
diff --git a/src/test/java/seedu/address/logic/commands/AddCommandTest.java b/src/test/java/seedu/address/logic/commands/AddCommandTest.java
index 90e8253f48e..ba5b6709f76 100644
--- a/src/test/java/seedu/address/logic/commands/AddCommandTest.java
+++ b/src/test/java/seedu/address/logic/commands/AddCommandTest.java
@@ -10,6 +10,7 @@
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.Comparator;
import java.util.function.Predicate;
import org.junit.jupiter.api.Test;
@@ -37,7 +38,7 @@ public void execute_personAcceptedByModel_addSuccessful() throws Exception {
ModelStubAcceptingPersonAdded modelStub = new ModelStubAcceptingPersonAdded();
Person validPerson = new PersonBuilder().build();
- CommandResult commandResult = new AddCommand(validPerson).execute(modelStub);
+ CommandResult commandResult = new AddCommand(validPerson).execute(modelStub, "");
assertEquals(String.format(AddCommand.MESSAGE_SUCCESS, Messages.format(validPerson)),
commandResult.getFeedbackToUser());
@@ -50,7 +51,8 @@ public void execute_duplicatePerson_throwsCommandException() {
AddCommand addCommand = new AddCommand(validPerson);
ModelStub modelStub = new ModelStubWithPerson(validPerson);
- assertThrows(CommandException.class, AddCommand.MESSAGE_DUPLICATE_PERSON, () -> addCommand.execute(modelStub));
+ assertThrows(CommandException.class, AddCommand.MESSAGE_DUPLICATE_PERSON, () -> addCommand.execute(
+ modelStub, ""));
}
@Test
@@ -157,6 +159,26 @@ public ObservableList getFilteredPersonList() {
public void updateFilteredPersonList(Predicate predicate) {
throw new AssertionError("This method should not be called.");
}
+
+ @Override
+ public String undo() {
+ throw new AssertionError("This method should not be called.");
+ }
+
+ @Override
+ public String redo() {
+ throw new AssertionError("This method should not be called.");
+ }
+
+ @Override
+ public void addCommandText(String commandText) {
+ throw new AssertionError("This method should not be called.");
+ }
+
+ @Override
+ public void sort(Comparator comparator) {
+ throw new AssertionError("This method should not be called.");
+ }
}
/**
@@ -199,6 +221,11 @@ public void addPerson(Person person) {
public ReadOnlyAddressBook getAddressBook() {
return new AddressBook();
}
+
+ @Override
+ public void addCommandText(String commandText) {
+ return;
+ }
}
}
diff --git a/src/test/java/seedu/address/logic/commands/BirthdayCommandTest.java b/src/test/java/seedu/address/logic/commands/BirthdayCommandTest.java
new file mode 100644
index 00000000000..542154992e8
--- /dev/null
+++ b/src/test/java/seedu/address/logic/commands/BirthdayCommandTest.java
@@ -0,0 +1,82 @@
+package seedu.address.logic.commands;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotEquals;
+import static seedu.address.logic.commands.BirthdayCommand.MESSAGE_FAILURE;
+import static seedu.address.logic.commands.BirthdayCommand.MESSAGE_SUCCESS;
+import static seedu.address.logic.commands.CommandTestUtil.assertCommandSuccess;
+import static seedu.address.testutil.TypicalPersons.getTypicalAddressBook;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import seedu.address.model.Model;
+import seedu.address.model.ModelManager;
+import seedu.address.model.UserPrefs;
+import seedu.address.model.person.MatchingBirthdayPredicate;
+import seedu.address.model.person.Month;
+
+public class BirthdayCommandTest {
+ private Model model;
+ private Model expectedFilteredModel;
+
+
+ @BeforeEach
+ public void setup() {
+ model = new ModelManager(getTypicalAddressBook(), new UserPrefs());
+ expectedFilteredModel = new ModelManager(model.getAddressBook(), new UserPrefs());
+ }
+
+ @Test
+ public void execute_birthday_success() {
+ List monthList = new ArrayList<>();
+ monthList.add(new Month(2));
+ MatchingBirthdayPredicate matchingBirthdayPredicate = new MatchingBirthdayPredicate(monthList);
+ expectedFilteredModel.updateFilteredPersonList(matchingBirthdayPredicate);
+ assertCommandSuccess(new BirthdayCommand(matchingBirthdayPredicate), model,
+ String.format(MESSAGE_SUCCESS, expectedFilteredModel.getFilteredPersonList().size())
+ + "Feb", expectedFilteredModel);
+ }
+
+ @Test
+ public void execute_birthdayNoResults() {
+ List monthList = new ArrayList<>();
+ monthList.add(new Month(10));
+ CommandResult result = new BirthdayCommand(new MatchingBirthdayPredicate(monthList)).execute(model, "");
+ assertEquals(result.toString(), new CommandResult(MESSAGE_FAILURE).toString());
+ }
+
+ @Test
+ public void checkSameEquals_success() {
+ List monthList = new ArrayList<>();
+ monthList.add(new Month(5));
+ BirthdayCommand command1 = new BirthdayCommand(new MatchingBirthdayPredicate(monthList));
+ assertEquals(command1, command1);
+ }
+
+ @Test
+ public void checkSameMonthEquals_success() {
+ List monthList1 = new ArrayList<>();
+ monthList1.add(new Month(5));
+ monthList1.add(new Month(6));
+ BirthdayCommand command1 = new BirthdayCommand(new MatchingBirthdayPredicate(monthList1));
+
+ List monthList2 = new ArrayList<>();
+ monthList2.add(new Month(5));
+ monthList2.add(new Month(6));
+ BirthdayCommand command2 = new BirthdayCommand(new MatchingBirthdayPredicate(monthList2));
+
+ assertEquals(command1, command2);
+ }
+
+ @Test
+ public void checkEquals_failure() {
+ List monthList1 = new ArrayList<>();
+ monthList1.add(new Month(5));
+ BirthdayCommand command1 = new BirthdayCommand(new MatchingBirthdayPredicate(monthList1));
+ assertNotEquals(command1, new ExitCommand());
+ }
+}
diff --git a/src/test/java/seedu/address/logic/commands/ClaimCommandTest.java b/src/test/java/seedu/address/logic/commands/ClaimCommandTest.java
new file mode 100644
index 00000000000..627c46324db
--- /dev/null
+++ b/src/test/java/seedu/address/logic/commands/ClaimCommandTest.java
@@ -0,0 +1,136 @@
+package seedu.address.logic.commands;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertNotEquals;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static seedu.address.logic.commands.ClaimCommand.ALLOCATE_SUCCESS;
+import static seedu.address.logic.commands.ClaimCommand.CLAIM_SUCCESS;
+import static seedu.address.logic.commands.CommandTestUtil.assertCommandSuccess;
+import static seedu.address.testutil.TypicalIndexes.INDEX_FIRST_PERSON;
+import static seedu.address.testutil.TypicalPersons.getTypicalAddressBook;
+
+import org.junit.jupiter.api.Test;
+
+import seedu.address.logic.Messages;
+import seedu.address.logic.commands.exceptions.CommandException;
+import seedu.address.model.AddressBook;
+import seedu.address.model.Model;
+import seedu.address.model.ModelManager;
+import seedu.address.model.UserPrefs;
+import seedu.address.model.person.Money;
+import seedu.address.model.person.Person;
+
+public class ClaimCommandTest {
+
+ private Model model = new ModelManager(getTypicalAddressBook(), new UserPrefs());
+ private final boolean isSubtract = true;
+ private Person targetPerson = model.getFilteredPersonList().get(INDEX_FIRST_PERSON.getOneBased());
+ private long originalBudget = Long.parseLong(targetPerson.getClaimBudget().amount);
+
+ @Test
+ public void execute_validPersonIndexUnfilteredList_success() {
+ Person personToClaim = model.getFilteredPersonList().get(INDEX_FIRST_PERSON.getZeroBased());
+ long prevClaimBudget = Integer.parseInt(personToClaim.getClaimBudget().amount);
+ Money claimBudget = new Money(Long.toString(prevClaimBudget - 1));
+ Person expectedPersonAfterClaim = new Person(personToClaim.getName(), personToClaim.getPhone(),
+ personToClaim.getEmail(), personToClaim.getAddress(), personToClaim.getSalary(),
+ claimBudget, personToClaim.getDepartment(), personToClaim.getDob(), personToClaim.getLeave());
+ ClaimCommand claimCommand = new ClaimCommand(INDEX_FIRST_PERSON, true, 1);
+ Model expectedModel = new ModelManager(new AddressBook(model.getAddressBook()), new UserPrefs());
+ expectedModel.setPerson(personToClaim, expectedPersonAfterClaim);
+ String expectedMessage = String.format("%sRemaining claim budget %s has: %s",
+ CLAIM_SUCCESS, expectedPersonAfterClaim.getName(), claimBudget);
+ assertCommandSuccess(claimCommand, model, expectedMessage, expectedModel);
+ }
+
+ @Test
+ public void execute_validPersonIndexUnfilteredListAllocation_success() {
+ Person personToClaim = model.getFilteredPersonList().get(INDEX_FIRST_PERSON.getZeroBased());
+ long prevClaimBudget = Integer.parseInt(personToClaim.getClaimBudget().amount);
+ Money claimBudget = new Money(Long.toString(prevClaimBudget + 1));
+ Person expectedPersonAfterClaim = new Person(personToClaim.getName(), personToClaim.getPhone(),
+ personToClaim.getEmail(), personToClaim.getAddress(), personToClaim.getSalary(),
+ claimBudget, personToClaim.getDepartment(), personToClaim.getDob(), personToClaim.getLeave());
+ ClaimCommand claimCommand = new ClaimCommand(INDEX_FIRST_PERSON, false, 1);
+ Model expectedModel = new ModelManager(new AddressBook(model.getAddressBook()), new UserPrefs());
+ expectedModel.setPerson(personToClaim, expectedPersonAfterClaim);
+ String expectedMessage = String.format("%sRemaining claim budget %s has: %s",
+ ALLOCATE_SUCCESS, expectedPersonAfterClaim.getName(), claimBudget);
+ assertCommandSuccess(claimCommand, model, expectedMessage, expectedModel);
+ }
+
+ @Test
+ public void execute_calculationNewBudget_success() throws CommandException {
+ long claimAmount = originalBudget - 1;
+ ClaimCommand claimCommand = new ClaimCommand(INDEX_FIRST_PERSON, isSubtract, claimAmount);
+ Money newMoney = claimCommand.calculateNewClaimBudget(originalBudget);
+ long expectedBudgetAmount = originalBudget - claimAmount;
+ Money expectedMoney = new Money(String.valueOf(expectedBudgetAmount));
+ assertEquals(newMoney, expectedMoney);
+ }
+
+ @Test
+ public void execute_calculationNewBudget_addsuccess() throws CommandException {
+ long claimAmount = originalBudget + 1;
+ ClaimCommand claimCommand = new ClaimCommand(INDEX_FIRST_PERSON, false, claimAmount);
+ Money newMoney = claimCommand.calculateNewClaimBudget(originalBudget);
+ long expectedBudgetAmount = originalBudget + claimAmount;
+ Money expectedMoney = new Money(String.valueOf(expectedBudgetAmount));
+ assertEquals(newMoney, expectedMoney);
+ }
+
+ @Test
+ public void execute_calculationExcessBudget_failure() {
+ long claimAmount = originalBudget + 1;
+ ClaimCommand claimCommand = new ClaimCommand(INDEX_FIRST_PERSON, isSubtract, claimAmount);
+ assertThrows(CommandException.class, () -> {
+ claimCommand.calculateNewClaimBudget(originalBudget);
+ }, Messages.MESSAGE_OVER_CLAIM);
+ }
+
+ @Test
+ public void execute_calculationExcessLongBudget_failure() {
+ long claimAmount = (long) (originalBudget + Math.pow(10, 12));
+ ClaimCommand claimCommand = new ClaimCommand(INDEX_FIRST_PERSON, !isSubtract, claimAmount);
+ assertThrows(CommandException.class, () -> {
+ claimCommand.calculateNewClaimBudget(originalBudget);
+ }, Messages.TOO_LARGE_A_NUMBER);
+ }
+
+ @Test
+ public void execute_generateNewPerson_success() {
+ Money newClaimBudget = new Money(String.valueOf(originalBudget + 1));
+ Person expectedPerson = new Person(targetPerson.getName(), targetPerson.getPhone(), targetPerson.getEmail(),
+ targetPerson.getAddress(), targetPerson.getSalary(), newClaimBudget, targetPerson.getDepartment(),
+ targetPerson.getDob(), targetPerson.getLeave());
+ ClaimCommand claimCommand = new ClaimCommand(INDEX_FIRST_PERSON, true, 0);
+ Person newPerson = claimCommand.postClaimPerson(targetPerson, newClaimBudget);
+ assertEquals(newPerson.toString(), expectedPerson.toString());
+ }
+
+ @Test
+ public void execute_generateNewPerson_failure() {
+ Money newClaimBudget = new Money(String.valueOf(originalBudget + 1));
+ Person expectedPerson = new Person(targetPerson.getName(), targetPerson.getPhone(), targetPerson.getEmail(),
+ targetPerson.getAddress(), targetPerson.getSalary(), targetPerson.getClaimBudget(),
+ targetPerson.getDepartment(), targetPerson.getDob());
+ ClaimCommand claimCommand = new ClaimCommand(INDEX_FIRST_PERSON, true, 0);
+ Person newPerson = claimCommand.postClaimPerson(targetPerson, newClaimBudget);
+ assertNotEquals(newPerson.toString(), expectedPerson.toString());
+ }
+
+ @Test
+ public void equals() {
+ ClaimCommand claimCommand = new ClaimCommand(INDEX_FIRST_PERSON, true, 1);
+ ClaimCommand commandWithSameValues = new ClaimCommand(INDEX_FIRST_PERSON, true, 1);
+ ClaimCommand commandWithDifferentValues = new ClaimCommand(INDEX_FIRST_PERSON, false, 1);
+ String notACommand = "This is a string, not a command";
+ assertTrue(claimCommand.equals(claimCommand));
+ assertTrue(claimCommand.equals(commandWithSameValues));
+ assertFalse(claimCommand.equals(commandWithDifferentValues));
+ assertFalse(claimCommand == null);
+ assertFalse(claimCommand.equals(notACommand));
+ }
+}
diff --git a/src/test/java/seedu/address/logic/commands/CommandResultTest.java b/src/test/java/seedu/address/logic/commands/CommandResultTest.java
index 7b8c7cd4546..c633c65b76c 100644
--- a/src/test/java/seedu/address/logic/commands/CommandResultTest.java
+++ b/src/test/java/seedu/address/logic/commands/CommandResultTest.java
@@ -14,7 +14,7 @@ public void equals() {
// same values -> returns true
assertTrue(commandResult.equals(new CommandResult("feedback")));
- assertTrue(commandResult.equals(new CommandResult("feedback", false, false)));
+ assertTrue(commandResult.equals(new CommandResult("feedback", false, false, false, "")));
// same object -> returns true
assertTrue(commandResult.equals(commandResult));
@@ -29,10 +29,10 @@ public void equals() {
assertFalse(commandResult.equals(new CommandResult("different")));
// different showHelp value -> returns false
- assertFalse(commandResult.equals(new CommandResult("feedback", true, false)));
+ assertFalse(commandResult.equals(new CommandResult("feedback", true, false, false, "")));
// different exit value -> returns false
- assertFalse(commandResult.equals(new CommandResult("feedback", false, true)));
+ assertFalse(commandResult.equals(new CommandResult("feedback", false, true, false, "")));
}
@Test
@@ -46,10 +46,17 @@ public void hashcode() {
assertNotEquals(commandResult.hashCode(), new CommandResult("different").hashCode());
// different showHelp value -> returns different hashcode
- assertNotEquals(commandResult.hashCode(), new CommandResult("feedback", true, false).hashCode());
+ assertNotEquals(commandResult.hashCode(), new CommandResult("feedback", true, false, false, "").hashCode());
// different exit value -> returns different hashcode
- assertNotEquals(commandResult.hashCode(), new CommandResult("feedback", false, true).hashCode());
+ assertNotEquals(commandResult.hashCode(), new CommandResult("feedback", false, true, false, "").hashCode());
+ }
+
+ @Test
+ public void themeChangeCommandsTest() {
+ CommandResult commandResult = new CommandResult("feedback", false, false, true, "RedTheme.css");
+ assertTrue(commandResult.isChangeTheme());
+ assertEquals(commandResult.getThemeStylesheet(), "RedTheme.css");
}
@Test
diff --git a/src/test/java/seedu/address/logic/commands/CommandTestUtil.java b/src/test/java/seedu/address/logic/commands/CommandTestUtil.java
index 643a1d08069..0752fbc9e70 100644
--- a/src/test/java/seedu/address/logic/commands/CommandTestUtil.java
+++ b/src/test/java/seedu/address/logic/commands/CommandTestUtil.java
@@ -2,10 +2,15 @@
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;
+import static seedu.address.logic.Messages.MESSAGE_INVALID_COMMAND_FORMAT;
import static seedu.address.logic.parser.CliSyntax.PREFIX_ADDRESS;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_CLAIM_BUDGET;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_DEPARTMENT;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_DOB;
import static seedu.address.logic.parser.CliSyntax.PREFIX_EMAIL;
import static seedu.address.logic.parser.CliSyntax.PREFIX_NAME;
import static seedu.address.logic.parser.CliSyntax.PREFIX_PHONE;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_SALARY;
import static seedu.address.logic.parser.CliSyntax.PREFIX_TAG;
import static seedu.address.testutil.Assert.assertThrows;
@@ -34,6 +39,11 @@ public class CommandTestUtil {
public static final String VALID_EMAIL_BOB = "bob@example.com";
public static final String VALID_ADDRESS_AMY = "Block 312, Amy Street 1";
public static final String VALID_ADDRESS_BOB = "Block 123, Bobby Street 3";
+ public static final String VALID_SALARY = "10000";
+ public static final String VALID_BUDGET = "2500";
+ public static final String VALID_DEPARTMENT = "Engineering";
+ public static final String VALID_DOB = "2000-01-01";
+ public static final String VALID_LEAVE = "101010101010";
public static final String VALID_TAG_HUSBAND = "husband";
public static final String VALID_TAG_FRIEND = "friend";
@@ -45,14 +55,19 @@ public class CommandTestUtil {
public static final String EMAIL_DESC_BOB = " " + PREFIX_EMAIL + VALID_EMAIL_BOB;
public static final String ADDRESS_DESC_AMY = " " + PREFIX_ADDRESS + VALID_ADDRESS_AMY;
public static final String ADDRESS_DESC_BOB = " " + PREFIX_ADDRESS + VALID_ADDRESS_BOB;
+ public static final String SALARY_DESC = " " + PREFIX_SALARY + VALID_SALARY;
+ public static final String BUDGET_DESC = " " + PREFIX_CLAIM_BUDGET + VALID_BUDGET;
+ public static final String DEPARTMENT_DESC = " " + PREFIX_DEPARTMENT + VALID_DEPARTMENT;
+ public static final String DOB_DESC = " " + PREFIX_DOB + VALID_DOB;
public static final String TAG_DESC_FRIEND = " " + PREFIX_TAG + VALID_TAG_FRIEND;
public static final String TAG_DESC_HUSBAND = " " + PREFIX_TAG + VALID_TAG_HUSBAND;
- public static final String INVALID_NAME_DESC = " " + PREFIX_NAME + "James&"; // '&' not allowed in names
+ public static final String INVALID_NAME_DESC = " " + PREFIX_NAME + " "; // blank not allowed in names
public static final String INVALID_PHONE_DESC = " " + PREFIX_PHONE + "911a"; // 'a' not allowed in phones
public static final String INVALID_EMAIL_DESC = " " + PREFIX_EMAIL + "bob!yahoo"; // missing '@' symbol
public static final String INVALID_ADDRESS_DESC = " " + PREFIX_ADDRESS; // empty string not allowed for addresses
public static final String INVALID_TAG_DESC = " " + PREFIX_TAG + "hubby*"; // '*' not allowed in tags
+ public static final String INVALID_LEAVE = "1+";
public static final String PREAMBLE_WHITESPACE = "\t \r \n";
public static final String PREAMBLE_NON_EMPTY = "NonEmptyPreamble";
@@ -60,6 +75,16 @@ public class CommandTestUtil {
public static final EditCommand.EditPersonDescriptor DESC_AMY;
public static final EditCommand.EditPersonDescriptor DESC_BOB;
+ public static final long CLAIM_AMOUNT = 500;
+ public static final String CLAIM_AMOUNT_STR = "500";
+ public static final String CLAIM_ADDITION = "+";
+ public static final String CLAIM_DELIMITER = "$/";
+ public static final String CLAIM_EMPTY_INDEX = String.format(MESSAGE_INVALID_COMMAND_FORMAT,
+ ClaimCommand.MESSAGE_EMPTY);
+ public static final String CLAIM_EMPTY_AMOUNT = String.format(MESSAGE_INVALID_COMMAND_FORMAT,
+ ClaimCommand.AMOUNT_EMPTY);
+
+
static {
DESC_AMY = new EditPersonDescriptorBuilder().withName(VALID_NAME_AMY)
.withPhone(VALID_PHONE_AMY).withEmail(VALID_EMAIL_AMY).withAddress(VALID_ADDRESS_AMY)
@@ -69,6 +94,8 @@ public class CommandTestUtil {
.withTags(VALID_TAG_HUSBAND, VALID_TAG_FRIEND).build();
}
+
+
/**
* Executes the given {@code command}, confirms that
* - the returned {@link CommandResult} matches {@code expectedCommandResult}
@@ -77,7 +104,7 @@ public class CommandTestUtil {
public static void assertCommandSuccess(Command command, Model actualModel, CommandResult expectedCommandResult,
Model expectedModel) {
try {
- CommandResult result = command.execute(actualModel);
+ CommandResult result = command.execute(actualModel, "");
assertEquals(expectedCommandResult, result);
assertEquals(expectedModel, actualModel);
} catch (CommandException ce) {
@@ -107,10 +134,11 @@ public static void assertCommandFailure(Command command, Model actualModel, Stri
AddressBook expectedAddressBook = new AddressBook(actualModel.getAddressBook());
List expectedFilteredList = new ArrayList<>(actualModel.getFilteredPersonList());
- assertThrows(CommandException.class, expectedMessage, () -> command.execute(actualModel));
+ assertThrows(CommandException.class, expectedMessage, () -> command.execute(actualModel, ""));
assertEquals(expectedAddressBook, actualModel.getAddressBook());
assertEquals(expectedFilteredList, actualModel.getFilteredPersonList());
}
+
/**
* Updates {@code model}'s filtered list to show only the person at the given {@code targetIndex} in the
* {@code model}'s address book.
@@ -124,5 +152,4 @@ public static void showPersonAtIndex(Model model, Index targetIndex) {
assertEquals(1, model.getFilteredPersonList().size());
}
-
}
diff --git a/src/test/java/seedu/address/logic/commands/DeleteCommandTest.java b/src/test/java/seedu/address/logic/commands/DeleteCommandTest.java
index b6f332eabca..ee369c3e510 100644
--- a/src/test/java/seedu/address/logic/commands/DeleteCommandTest.java
+++ b/src/test/java/seedu/address/logic/commands/DeleteCommandTest.java
@@ -33,7 +33,7 @@ public void execute_validIndexUnfilteredList_success() {
DeleteCommand deleteCommand = new DeleteCommand(INDEX_FIRST_PERSON);
String expectedMessage = String.format(DeleteCommand.MESSAGE_DELETE_PERSON_SUCCESS,
- Messages.format(personToDelete));
+ personToDelete.getName());
ModelManager expectedModel = new ModelManager(model.getAddressBook(), new UserPrefs());
expectedModel.deletePerson(personToDelete);
@@ -57,7 +57,7 @@ public void execute_validIndexFilteredList_success() {
DeleteCommand deleteCommand = new DeleteCommand(INDEX_FIRST_PERSON);
String expectedMessage = String.format(DeleteCommand.MESSAGE_DELETE_PERSON_SUCCESS,
- Messages.format(personToDelete));
+ personToDelete.getName());
Model expectedModel = new ModelManager(model.getAddressBook(), new UserPrefs());
expectedModel.deletePerson(personToDelete);
diff --git a/src/test/java/seedu/address/logic/commands/EditPersonDescriptorTest.java b/src/test/java/seedu/address/logic/commands/EditPersonDescriptorTest.java
index b17c1f3d5c2..ffb43d0b739 100644
--- a/src/test/java/seedu/address/logic/commands/EditPersonDescriptorTest.java
+++ b/src/test/java/seedu/address/logic/commands/EditPersonDescriptorTest.java
@@ -9,7 +9,6 @@
import static seedu.address.logic.commands.CommandTestUtil.VALID_EMAIL_BOB;
import static seedu.address.logic.commands.CommandTestUtil.VALID_NAME_BOB;
import static seedu.address.logic.commands.CommandTestUtil.VALID_PHONE_BOB;
-import static seedu.address.logic.commands.CommandTestUtil.VALID_TAG_HUSBAND;
import org.junit.jupiter.api.Test;
@@ -51,10 +50,6 @@ public void equals() {
// different address -> returns false
editedAmy = new EditPersonDescriptorBuilder(DESC_AMY).withAddress(VALID_ADDRESS_BOB).build();
assertFalse(DESC_AMY.equals(editedAmy));
-
- // different tags -> returns false
- editedAmy = new EditPersonDescriptorBuilder(DESC_AMY).withTags(VALID_TAG_HUSBAND).build();
- assertFalse(DESC_AMY.equals(editedAmy));
}
@Test
@@ -64,8 +59,11 @@ public void toStringMethod() {
+ editPersonDescriptor.getName().orElse(null) + ", phone="
+ editPersonDescriptor.getPhone().orElse(null) + ", email="
+ editPersonDescriptor.getEmail().orElse(null) + ", address="
- + editPersonDescriptor.getAddress().orElse(null) + ", tags="
- + editPersonDescriptor.getTags().orElse(null) + "}";
+ + editPersonDescriptor.getAddress().orElse(null) + ", salary="
+ + editPersonDescriptor.getSalary().orElse(null) + ", claimBudget="
+ + editPersonDescriptor.getClaimBudget().orElse(null) + ", department="
+ + editPersonDescriptor.getDepartment().orElse(null) + ", dob="
+ + editPersonDescriptor.getDob().orElse(null) + "}";
assertEquals(expected, editPersonDescriptor.toString());
}
}
diff --git a/src/test/java/seedu/address/logic/commands/ExitCommandTest.java b/src/test/java/seedu/address/logic/commands/ExitCommandTest.java
index 9533c473875..deed2bb6520 100644
--- a/src/test/java/seedu/address/logic/commands/ExitCommandTest.java
+++ b/src/test/java/seedu/address/logic/commands/ExitCommandTest.java
@@ -14,7 +14,7 @@ public class ExitCommandTest {
@Test
public void execute_exit_success() {
- CommandResult expectedCommandResult = new CommandResult(MESSAGE_EXIT_ACKNOWLEDGEMENT, false, true);
+ CommandResult expectedCommandResult = new CommandResult(MESSAGE_EXIT_ACKNOWLEDGEMENT, false, true, false, "");
assertCommandSuccess(new ExitCommand(), model, expectedCommandResult, expectedModel);
}
}
diff --git a/src/test/java/seedu/address/logic/commands/ExportCommandTest.java b/src/test/java/seedu/address/logic/commands/ExportCommandTest.java
new file mode 100644
index 00000000000..e015d181ce8
--- /dev/null
+++ b/src/test/java/seedu/address/logic/commands/ExportCommandTest.java
@@ -0,0 +1,166 @@
+package seedu.address.logic.commands;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertNotEquals;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.junit.jupiter.api.Assertions.fail;
+import static seedu.address.testutil.TypicalIndexes.INDEX_FIRST_PERSON;
+import static seedu.address.testutil.TypicalPersons.getTypicalAddressBook;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import seedu.address.logic.commands.exceptions.CommandException;
+import seedu.address.model.Model;
+import seedu.address.model.ModelManager;
+import seedu.address.model.UserPrefs;
+import seedu.address.model.person.Person;
+
+public class ExportCommandTest {
+
+ private Model model = new ModelManager(getTypicalAddressBook(), new UserPrefs());
+ private final String templateStr = "engineering_dept";
+ private final ExportCommand exportCommand = new ExportCommand(templateStr);
+ private Person targetPerson = model.getFilteredPersonList().get(INDEX_FIRST_PERSON.getOneBased());
+ private List indivAttributeList;
+ private List csvHeaders;
+
+ @BeforeEach
+ public void setUp() {
+ csvHeaders = new ArrayList();
+ csvHeaders.add("Name");
+ csvHeaders.add("Phone");
+ csvHeaders.add("Email");
+ csvHeaders.add("Address");
+ csvHeaders.add("Salary");
+ csvHeaders.add("Claim Budget");
+ csvHeaders.add("DOB");
+ csvHeaders.add("Department");
+ csvHeaders.add("Leave");
+ indivAttributeList = new ArrayList();
+ indivAttributeList.add(targetPerson.getName().toString());
+ indivAttributeList.add(targetPerson.getPhone().toString());
+ indivAttributeList.add(targetPerson.getEmail().toString());
+ indivAttributeList.add(targetPerson.getAddress().toString());
+ indivAttributeList.add(targetPerson.getSalary().amount);
+ indivAttributeList.add(targetPerson.getClaimBudget().amount);
+ indivAttributeList.add(targetPerson.getDob().dob);
+ indivAttributeList.add(targetPerson.getDepartment().toString());
+ indivAttributeList.add(targetPerson.getLeave().toString());
+ }
+
+ @Test
+ public void execute_validFileName_success() throws CommandException {
+ ExportCommand exportCommand = new ExportCommand("testExport");
+ CommandResult commandResult = exportCommand.execute(model, "");
+ String expectedMessage = "testExport.csv has been successfully created!\n"
+ + "You can view the file in the Exported_CSVs folder.";
+ CommandResult expectedCommandResult = new CommandResult(expectedMessage);
+ assertEquals(expectedCommandResult, commandResult);
+ File expectedDirectory = new File(System.getProperty("user.dir"), "Exported_CSVs");
+ File expectedFile = new File(expectedDirectory, "testExport.csv");
+ assertTrue(expectedFile.exists(), "File should have been created");
+ }
+
+ @Test
+ void generateFile_directoryDoesNotExist_createsDirectoryAndFile() {
+ File expectedDirectory = new File(System.getProperty("user.dir"), "Exported_CSVs");
+ File expectedFile = new File(expectedDirectory, "engineering_dept.csv");
+
+ try {
+ File result = exportCommand.generateFile("engineering_dept");
+ assertTrue(expectedDirectory.exists(), "Directory should have been created");
+ assertEquals(expectedFile.getAbsolutePath(), result.getAbsolutePath(),
+ "Generated file does not match expected");
+ } catch (CommandException e) {
+ fail("CommandException should not be thrown");
+ }
+ }
+
+ @Test
+ public void generateList_singlePerson_success() {
+ List singlePerson = List.of(targetPerson);
+ List> firstIndexAttributes = exportCommand.generateListPeople(singlePerson);
+ assertEquals(firstIndexAttributes, List.of(csvHeaders, indivAttributeList));
+ }
+
+ @Test
+ public void generateCsv_attributeFields_success() {
+ List userAttributes = List.of("Nixon", "1234567", "Kent Ridge");
+ String delimitedStr = exportCommand.convertToCsv(userAttributes);
+ String expectedStr = "Nixon,1234567,Kent Ridge";
+ assertEquals(delimitedStr, expectedStr);
+ }
+
+ @Test
+ public void testEscapeSpecialCharacters_noSpecialCharacters_success() {
+ String input = "This is a test";
+ String output = exportCommand.escapeSpecialCharacters(input);
+ assertEquals("This is a test", output);
+ }
+ @Test
+ public void testEscapeSpecialCharacters_containsDoubleQuotes_success() {
+ String input = "This is a \"test\"";
+ String output = exportCommand.escapeSpecialCharacters(input);
+ assertEquals("This is a \"\"test\"\"", output);
+ }
+
+ @Test
+ public void testEscapeSpecialCharacters_containsComma_success() {
+ String input = "This, is a test";
+ String output = exportCommand.escapeSpecialCharacters(input);
+ assertEquals("\"This, is a test\"", output);
+ }
+
+ @Test
+ public void testEscapeSpecialCharacters_containsNewline_success() {
+ String input = "This is a test\nNew line";
+ String output = exportCommand.escapeSpecialCharacters(input);
+ assertEquals("\"This is a test\nNew line\"", output);
+ }
+
+ @Test
+ public void testEscapeSpecialCharacters_containsSpecialChars_failure() {
+ String input = "This is a \"test, with comma\"";
+ String output = exportCommand.escapeSpecialCharacters(input);
+ assertNotEquals(input, output);
+ }
+
+ @Test
+ public void equals_sameObject_true() {
+ ExportCommand command = new ExportCommand("testFile");
+ assertEquals(command, command);
+ }
+
+ @Test
+ public void equals_null_false() {
+ ExportCommand command = new ExportCommand("testFile");
+ assertFalse(command.equals(null));
+ }
+
+ @Test
+ public void equals_differentClass_false() {
+ ExportCommand command = new ExportCommand("testFile");
+ Object other = new Object();
+ assertFalse(command.equals(other));
+ }
+
+ @Test
+ public void equals_differentFileName_false() {
+ ExportCommand command1 = new ExportCommand("testFile1");
+ ExportCommand command2 = new ExportCommand("testFile2");
+ assertFalse(command1.equals(command2));
+ }
+
+ @Test
+ public void equals_sameFileName_true() {
+ ExportCommand command1 = new ExportCommand("testFile");
+ ExportCommand command2 = new ExportCommand("testFile");
+ assertTrue(command1.equals(command2));
+ }
+}
diff --git a/src/test/java/seedu/address/logic/commands/HelpCommandTest.java b/src/test/java/seedu/address/logic/commands/HelpCommandTest.java
index 4904fc4352e..ed03c704e7c 100644
--- a/src/test/java/seedu/address/logic/commands/HelpCommandTest.java
+++ b/src/test/java/seedu/address/logic/commands/HelpCommandTest.java
@@ -14,7 +14,7 @@ public class HelpCommandTest {
@Test
public void execute_help_success() {
- CommandResult expectedCommandResult = new CommandResult(SHOWING_HELP_MESSAGE, true, false);
+ CommandResult expectedCommandResult = new CommandResult(SHOWING_HELP_MESSAGE, true, false, false, "");
assertCommandSuccess(new HelpCommand(), model, expectedCommandResult, expectedModel);
}
}
diff --git a/src/test/java/seedu/address/logic/commands/LeaveCommandTest.java b/src/test/java/seedu/address/logic/commands/LeaveCommandTest.java
new file mode 100644
index 00000000000..387e7adde07
--- /dev/null
+++ b/src/test/java/seedu/address/logic/commands/LeaveCommandTest.java
@@ -0,0 +1,55 @@
+package seedu.address.logic.commands;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static seedu.address.logic.commands.CommandTestUtil.assertCommandFailure;
+import static seedu.address.logic.commands.CommandTestUtil.assertCommandSuccess;
+import static seedu.address.testutil.TypicalIndexes.INDEX_FIRST_PERSON;
+import static seedu.address.testutil.TypicalIndexes.INDEX_SECOND_PERSON;
+import static seedu.address.testutil.TypicalPersons.getTypicalAddressBook;
+
+import org.junit.jupiter.api.Test;
+
+import seedu.address.commons.core.index.Index;
+import seedu.address.logic.Messages;
+import seedu.address.model.Model;
+import seedu.address.model.ModelManager;
+import seedu.address.model.UserPrefs;
+import seedu.address.model.person.Leave;
+import seedu.address.model.person.Person;
+
+public class LeaveCommandTest {
+ private Model model = new ModelManager(getTypicalAddressBook(), new UserPrefs());
+ private LeaveCommand leaveFirstCommand = new LeaveCommand(INDEX_FIRST_PERSON, "0000+0000000");
+ private LeaveCommand leaveSecondCommand = new LeaveCommand(INDEX_SECOND_PERSON, "0+0000000000");
+
+ @Test
+ public void equals() {
+ assertFalse(leaveFirstCommand.equals(null));
+ assertEquals(leaveFirstCommand, leaveFirstCommand);
+ assertFalse(leaveFirstCommand.equals(leaveSecondCommand));
+ assertFalse(leaveFirstCommand.toString().equals(""));
+ }
+
+ @Test
+ public void execute_success() {
+ Person person = model.getFilteredPersonList().get(INDEX_FIRST_PERSON.getZeroBased());
+ Leave leave = person.getLeave().update("0000+0000000");
+ String expected = String.format(LeaveCommand.MESSAGE_LEAVE_SUCCESS, person.getName(), leave.toString());
+ ModelManager expectedModel = new ModelManager(model.getAddressBook(), new UserPrefs());
+ assertCommandSuccess(leaveFirstCommand, model, expected, expectedModel);
+ }
+
+ @Test
+ public void execute_failure() {
+ Index outOfBoundIndex = Index.fromOneBased(model.getFilteredPersonList().size() + 1);
+ LeaveCommand command = new LeaveCommand(outOfBoundIndex, "0000+0000000");
+ assertCommandFailure(command, model, Messages.MESSAGE_INVALID_PERSON_DISPLAYED_INDEX);
+ assertCommandFailure(new LeaveCommand(INDEX_FIRST_PERSON, "0000---00000"), model,
+ String.format(Leave.ILLEGAL_MONTH, "May, Jul"));
+
+ Person person = model.getFilteredPersonList().get(INDEX_SECOND_PERSON.getZeroBased());
+ assertCommandFailure(leaveSecondCommand, model,
+ String.format(LeaveCommand.MESSAGE_NOT_EDITED, person.getLeave().toString()));
+ }
+}
diff --git a/src/test/java/seedu/address/logic/commands/ListCommandTest.java b/src/test/java/seedu/address/logic/commands/ListCommandTest.java
index 435ff1f7275..65e3ecd8d7b 100644
--- a/src/test/java/seedu/address/logic/commands/ListCommandTest.java
+++ b/src/test/java/seedu/address/logic/commands/ListCommandTest.java
@@ -1,39 +1,53 @@
package seedu.address.logic.commands;
+import static seedu.address.logic.commands.CommandTestUtil.VALID_DEPARTMENT;
import static seedu.address.logic.commands.CommandTestUtil.assertCommandSuccess;
-import static seedu.address.logic.commands.CommandTestUtil.showPersonAtIndex;
-import static seedu.address.testutil.TypicalIndexes.INDEX_FIRST_PERSON;
import static seedu.address.testutil.TypicalPersons.getTypicalAddressBook;
+import java.util.function.Predicate;
+
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
+import seedu.address.logic.Messages;
import seedu.address.model.Model;
import seedu.address.model.ModelManager;
import seedu.address.model.UserPrefs;
+import seedu.address.model.person.Department;
+import seedu.address.model.person.MatchingDepartmentPredicate;
+import seedu.address.model.person.Person;
+
+
/**
* Contains integration tests (interaction with the Model) and unit tests for ListCommand.
*/
public class ListCommandTest {
+ private static final Predicate FILTER_TEST_PREDICATE =
+ new MatchingDepartmentPredicate(new Department(VALID_DEPARTMENT));
private Model model;
private Model expectedModel;
+ private Model expectedFilteredModel;
@BeforeEach
public void setUp() {
model = new ModelManager(getTypicalAddressBook(), new UserPrefs());
expectedModel = new ModelManager(model.getAddressBook(), new UserPrefs());
+ expectedFilteredModel = new ModelManager(model.getAddressBook(), new UserPrefs());
+ expectedFilteredModel.updateFilteredPersonList(FILTER_TEST_PREDICATE);
}
@Test
public void execute_listIsNotFiltered_showsSameList() {
- assertCommandSuccess(new ListCommand(), model, ListCommand.MESSAGE_SUCCESS, expectedModel);
+ assertCommandSuccess(new ListCommand(), model, String.format(
+ Messages.MESSAGE_LIST_SUCCESS, expectedModel.getFilteredPersonList().size()), expectedModel);
}
@Test
public void execute_listIsFiltered_showsEverything() {
- showPersonAtIndex(model, INDEX_FIRST_PERSON);
- assertCommandSuccess(new ListCommand(), model, ListCommand.MESSAGE_SUCCESS, expectedModel);
+ assertCommandSuccess(new ListCommand(FILTER_TEST_PREDICATE), model, String.format(
+ Messages.MESSAGE_FILTER_SUCCESS, expectedFilteredModel.getFilteredPersonList().size()),
+ expectedFilteredModel);
}
}
diff --git a/src/test/java/seedu/address/logic/commands/RedoCommandTest.java b/src/test/java/seedu/address/logic/commands/RedoCommandTest.java
new file mode 100644
index 00000000000..da0485b5861
--- /dev/null
+++ b/src/test/java/seedu/address/logic/commands/RedoCommandTest.java
@@ -0,0 +1,32 @@
+package seedu.address.logic.commands;
+
+import static seedu.address.logic.commands.CommandTestUtil.assertCommandFailure;
+import static seedu.address.logic.commands.CommandTestUtil.assertCommandSuccess;
+import static seedu.address.testutil.TypicalPersons.ALICE;
+import static seedu.address.testutil.TypicalPersons.BENSON;
+
+import org.junit.jupiter.api.Test;
+
+import seedu.address.model.AddressBookList;
+import seedu.address.model.Model;
+import seedu.address.model.ModelManager;
+
+public class RedoCommandTest {
+ private Model model = new ModelManager();
+ private RedoCommand command = new RedoCommand();
+
+ @Test
+ public void execute_redo_success() {
+ model.addPerson(ALICE);
+ model.addCommandText("add Alice");
+ model.addPerson(BENSON);
+ model.addCommandText("add Benson");
+ model.undo();
+ assertCommandSuccess(command, model, String.format(RedoCommand.MESSAGE_SUCCESS, "add Benson"), model);
+ }
+
+ @Test
+ public void execute_redo_failure() {
+ assertCommandFailure(command, model, AddressBookList.REDO_ERROR_MESSAGE);
+ }
+}
diff --git a/src/test/java/seedu/address/logic/commands/ResetLeavesCommandTest.java b/src/test/java/seedu/address/logic/commands/ResetLeavesCommandTest.java
new file mode 100644
index 00000000000..7b531fb851d
--- /dev/null
+++ b/src/test/java/seedu/address/logic/commands/ResetLeavesCommandTest.java
@@ -0,0 +1,34 @@
+package seedu.address.logic.commands;
+
+import static seedu.address.logic.commands.CommandTestUtil.assertCommandSuccess;
+import static seedu.address.testutil.TypicalIndexes.INDEX_FIRST_PERSON;
+import static seedu.address.testutil.TypicalPersons.getTypicalAddressBook;
+
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import seedu.address.model.Model;
+import seedu.address.model.ModelManager;
+import seedu.address.model.UserPrefs;
+import seedu.address.model.person.Person;
+
+public class ResetLeavesCommandTest {
+
+ private Model model = new ModelManager(getTypicalAddressBook(), new UserPrefs());
+ private Person targetPerson = model.getFilteredPersonList().get(INDEX_FIRST_PERSON.getOneBased());
+ private Person modifiedPerson;
+
+ @BeforeEach
+ public void setUp() {
+ modifiedPerson = new Person(targetPerson.getName(), targetPerson.getPhone(),
+ targetPerson.getEmail(), targetPerson.getAddress(), targetPerson.getSalary(),
+ targetPerson.getClaimBudget(), targetPerson.getDepartment(), targetPerson.getDob());
+ }
+
+ @Test
+ public void execute_emptyLeavesAddressBook_success() {
+ model.setPerson(targetPerson, modifiedPerson);
+ Model expectedModel = new ModelManager(getTypicalAddressBook(), new UserPrefs());
+ assertCommandSuccess(new ResetLeavesCommand(), model, ResetLeavesCommand.MESSAGE_SUCCESS, expectedModel);
+ }
+}
diff --git a/src/test/java/seedu/address/logic/commands/SortCommandTest.java b/src/test/java/seedu/address/logic/commands/SortCommandTest.java
new file mode 100644
index 00000000000..02eb338b6f8
--- /dev/null
+++ b/src/test/java/seedu/address/logic/commands/SortCommandTest.java
@@ -0,0 +1,42 @@
+package seedu.address.logic.commands;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotEquals;
+import static seedu.address.logic.commands.CommandTestUtil.assertCommandSuccess;
+import static seedu.address.testutil.TypicalPersons.ALICE;
+import static seedu.address.testutil.TypicalPersons.BENSON;
+
+import org.junit.jupiter.api.Test;
+
+import seedu.address.model.Model;
+import seedu.address.model.ModelManager;
+
+public class SortCommandTest {
+ private Model model = new ModelManager();
+ private Model reversed = new ModelManager();
+
+ @Test
+ public void execute_success() {
+ model.addPerson(ALICE);
+ model.addPerson(BENSON);
+ reversed.addPerson(BENSON);
+ reversed.addPerson(ALICE);
+ assertCommandSuccess(new SortCommand("name", true), model, SortCommand.MESSAGE_SUCCESS, reversed);
+ assertCommandSuccess(new SortCommand("name"), model, SortCommand.MESSAGE_SUCCESS, model);
+ assertCommandSuccess(new SortCommand("phone"), model, SortCommand.MESSAGE_SUCCESS, model);
+ assertCommandSuccess(new SortCommand("email"), model, SortCommand.MESSAGE_SUCCESS, model);
+ assertCommandSuccess(new SortCommand("address"), model, SortCommand.MESSAGE_SUCCESS, model);
+ assertCommandSuccess(new SortCommand("salary"), model, SortCommand.MESSAGE_SUCCESS, model);
+ assertCommandSuccess(new SortCommand("claim"), model, SortCommand.MESSAGE_SUCCESS, model);
+ assertCommandSuccess(new SortCommand("dep"), model, SortCommand.MESSAGE_SUCCESS, model);
+ assertCommandSuccess(new SortCommand("dob"), model, SortCommand.MESSAGE_SUCCESS, model);
+ }
+
+ @Test
+ public void equals() {
+ assertNotEquals(new SortCommand("name"), null);
+ assertEquals(new SortCommand("name"), new SortCommand("name"));
+ SortCommand command = new SortCommand("name");
+ assertEquals(command, command);
+ }
+}
diff --git a/src/test/java/seedu/address/logic/commands/ThemeCommandTest.java b/src/test/java/seedu/address/logic/commands/ThemeCommandTest.java
new file mode 100644
index 00000000000..d5c80cfc8c5
--- /dev/null
+++ b/src/test/java/seedu/address/logic/commands/ThemeCommandTest.java
@@ -0,0 +1,19 @@
+package seedu.address.logic.commands;
+
+import static seedu.address.logic.commands.CommandTestUtil.assertCommandSuccess;
+
+import org.junit.jupiter.api.Test;
+
+import seedu.address.model.Model;
+import seedu.address.model.ModelManager;
+
+class ThemeCommandTest {
+
+ private Model model = new ModelManager();
+
+ @Test
+ public void execute_success() {
+ assertCommandSuccess(new ThemeCommand("LightTheme.css"), model,
+ ThemeCommand.MESSAGE_THEME_CHANGE_ACKNOWLEDGEMENT, model);
+ }
+}
diff --git a/src/test/java/seedu/address/logic/commands/UndoCommandTest.java b/src/test/java/seedu/address/logic/commands/UndoCommandTest.java
new file mode 100644
index 00000000000..267fc5798c8
--- /dev/null
+++ b/src/test/java/seedu/address/logic/commands/UndoCommandTest.java
@@ -0,0 +1,31 @@
+package seedu.address.logic.commands;
+
+import static seedu.address.logic.commands.CommandTestUtil.assertCommandFailure;
+import static seedu.address.logic.commands.CommandTestUtil.assertCommandSuccess;
+import static seedu.address.testutil.TypicalPersons.ALICE;
+import static seedu.address.testutil.TypicalPersons.BENSON;
+
+import org.junit.jupiter.api.Test;
+
+import seedu.address.model.AddressBookList;
+import seedu.address.model.Model;
+import seedu.address.model.ModelManager;
+
+public class UndoCommandTest {
+ private Model model = new ModelManager();
+ private UndoCommand command = new UndoCommand();
+
+ @Test
+ public void execute_undo_success() {
+ model.addPerson(ALICE);
+ model.addCommandText("add Alice");
+ model.addPerson(BENSON);
+ model.addCommandText("add Benson");
+ assertCommandSuccess(command, model, String.format(UndoCommand.MESSAGE_SUCCESS, "add Benson"), model);
+ }
+
+ @Test
+ public void execute_undo_failure() {
+ assertCommandFailure(command, model, AddressBookList.UNDO_ERROR_MESSAGE);
+ }
+}
diff --git a/src/test/java/seedu/address/logic/commands/ViewCommandTest.java b/src/test/java/seedu/address/logic/commands/ViewCommandTest.java
new file mode 100644
index 00000000000..2a53e551573
--- /dev/null
+++ b/src/test/java/seedu/address/logic/commands/ViewCommandTest.java
@@ -0,0 +1,169 @@
+package seedu.address.logic.commands;
+
+import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertNotEquals;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static seedu.address.logic.commands.CommandTestUtil.assertCommandFailure;
+import static seedu.address.logic.commands.CommandTestUtil.assertCommandSuccess;
+import static seedu.address.logic.parser.ViewCommandParser.ADDRESS_IDENTIFIER;
+import static seedu.address.logic.parser.ViewCommandParser.BIRTHDAY;
+import static seedu.address.logic.parser.ViewCommandParser.CLAIM_BUDGET;
+import static seedu.address.logic.parser.ViewCommandParser.DEPARTMENT;
+import static seedu.address.logic.parser.ViewCommandParser.EMAIL_IDENTIFIER;
+import static seedu.address.logic.parser.ViewCommandParser.NAME_IDENTIFIER;
+import static seedu.address.logic.parser.ViewCommandParser.PHONE_IDENTIFIER;
+import static seedu.address.logic.parser.ViewCommandParser.SALARY_IDENTIFIER;
+import static seedu.address.testutil.TypicalIndexes.INDEX_FIRST_PERSON;
+import static seedu.address.testutil.TypicalIndexes.INDEX_SECOND_PERSON;
+import static seedu.address.testutil.TypicalPersons.getTypicalAddressBook;
+
+import java.util.HashMap;
+import java.util.List;
+
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import seedu.address.commons.core.index.Index;
+import seedu.address.logic.Messages;
+import seedu.address.logic.commands.exceptions.CommandException;
+import seedu.address.logic.parser.ParserUtil;
+import seedu.address.logic.parser.exceptions.ParseException;
+import seedu.address.model.Model;
+import seedu.address.model.ModelManager;
+import seedu.address.model.UserPrefs;
+import seedu.address.model.person.Person;
+
+public class ViewCommandTest {
+
+ private Model model = new ModelManager(getTypicalAddressBook(), new UserPrefs());
+ private Person targetPerson = model.getFilteredPersonList().get(INDEX_FIRST_PERSON.getOneBased());
+ private final HashMap> templateReference = new HashMap>() {{
+ put("Salary", List.of(INDEX_FIRST_PERSON));
+ }};
+ private HashMap personInfo;
+
+ private final ViewCommand viewCommand = new ViewCommand(templateReference);
+
+ @BeforeEach
+ public void setUp() {
+ personInfo = new HashMap<>();
+ personInfo.put(NAME_IDENTIFIER, targetPerson.getName().toString());
+ personInfo.put(PHONE_IDENTIFIER, targetPerson.getPhone().toString());
+ personInfo.put(EMAIL_IDENTIFIER, targetPerson.getEmail().toString());
+ personInfo.put(ADDRESS_IDENTIFIER, targetPerson.getAddress().toString());
+ personInfo.put(SALARY_IDENTIFIER, targetPerson.getSalary().toString());
+ personInfo.put(CLAIM_BUDGET, targetPerson.getClaimBudget().toString());
+ personInfo.put(DEPARTMENT, targetPerson.getDepartment().toString());
+ personInfo.put(BIRTHDAY, targetPerson.getDob().toString());
+ }
+
+ @Test
+ public void execute_validViewSalary_success() {
+ Person personToView = model.getFilteredPersonList().get(INDEX_FIRST_PERSON.getZeroBased());
+ String expectedResponse = String.format("You are viewing Salary:\n"
+ + "1. %s's Salary is %s.\n\n", personToView.getName(), personToView.getSalary());
+ assertCommandSuccess(viewCommand, model, expectedResponse, model);
+ }
+
+ @Test
+ public void execute_wrongIndex_failure() throws ParseException {
+ HashMap> nameWithWrongIndex = new HashMap>() {{
+ put("Name", List.of(ParserUtil.parseIndex("10000")));
+ }};
+ ViewCommand wrongViewCommand = new ViewCommand(nameWithWrongIndex);
+ String expectedResponse = Messages.MESSAGE_INVALID_PERSON_DISPLAYED_INDEX;
+ assertCommandFailure(wrongViewCommand, model, expectedResponse);
+ }
+
+ @Test
+ public void execute_validViewClaimBudgetDepartment_success() {
+ HashMap> claimBudgetAndDepartment = new HashMap>() {{
+ put("Claim Budget", List.of(INDEX_FIRST_PERSON));
+ put("Department", List.of(INDEX_FIRST_PERSON));
+ }};
+ ViewCommand newViewCommand = new ViewCommand(claimBudgetAndDepartment);
+ Person personToView = model.getFilteredPersonList().get(INDEX_FIRST_PERSON.getZeroBased());
+ String expectedResponse = String.format("You are viewing Department:\n"
+ + "1. %s's Department is %s.\n\n", personToView.getName(), personToView.getDepartment())
+ + String.format("You are viewing Claim Budget:\n"
+ + "1. %s's Claim Budget is %s.\n\n", personToView.getName(), personToView.getClaimBudget());
+ assertCommandSuccess(newViewCommand, model, expectedResponse, model);
+ }
+
+ @Test
+ public void execute_validMultiplePhone_failure() throws CommandException {
+ HashMap> mutiplePhonesMap = new HashMap>() {{
+ put("Phone", List.of(INDEX_FIRST_PERSON, INDEX_SECOND_PERSON));
+ }};
+ ViewCommand newViewCommand = new ViewCommand(mutiplePhonesMap);
+ Person firstPersonToView = model.getFilteredPersonList().get(INDEX_FIRST_PERSON.getZeroBased());
+ Person secondPersonToView = model.getFilteredPersonList().get(INDEX_SECOND_PERSON.getZeroBased());
+ String wrongReponse = String.format("You are viewing Department:\n"
+ + "1. %s's Department is %s.\n\n", firstPersonToView.getName(), firstPersonToView.getDepartment())
+ + String.format("You are viewing Claim Budget:\n"
+ + "1. %s's Claim Budget is %s.\n\n", secondPersonToView.getName(), secondPersonToView.getClaimBudget());
+ assertNotEquals(newViewCommand.execute(model, "").toString(), wrongReponse);
+ }
+
+ @Test
+ public void execute_validMultipleEmail_success() throws CommandException {
+ HashMap> mutiplePhonesMap = new HashMap>() {{
+ put("Email", List.of(INDEX_FIRST_PERSON, INDEX_SECOND_PERSON));
+ }};
+ ViewCommand newViewCommand = new ViewCommand(mutiplePhonesMap);
+ Person firstPersonToView = model.getFilteredPersonList().get(INDEX_FIRST_PERSON.getZeroBased());
+ Person secondPersonToView = model.getFilteredPersonList().get(INDEX_SECOND_PERSON.getZeroBased());
+ String expectedResponse = String.format("You are viewing Email:\n"
+ + "1. %s's Email is %s.\n", firstPersonToView.getName(), firstPersonToView.getEmail())
+ + String.format("2. %s's Email is %s.\n\n", secondPersonToView.getName(),
+ secondPersonToView.getEmail());
+ assertCommandSuccess(newViewCommand, model, expectedResponse, model);
+ }
+
+ @Test
+ public void generate_assertionPrefix_success() {
+ assertDoesNotThrow(() -> viewCommand.massAssertionFn("Name"));
+ }
+
+ @Test
+ public void generate_assertionPrefix_failure() {
+ assertThrows(AssertionError.class, () -> viewCommand.massAssertionFn("INVALID_KEY"));
+ }
+
+ @Test
+ public void generate_responseStr_success() {
+ String returnStr = viewCommand.generateNiceStr(personInfo, 1, "Salary");
+ String expectedStr = "1. Benson Meier's Salary is $100.\n";
+ assertEquals(returnStr, expectedStr);
+ }
+
+ @Test
+ public void generate_responseStr_failure() {
+ String returnStr = viewCommand.generateNiceStr(personInfo, 1, "Salary");
+ String expectedStr = "1. Benson Meier's Name is Benson Meier.\n";
+ assertNotEquals(returnStr, expectedStr);
+ }
+
+ @Test
+ public void viewCommand_equals() throws ParseException {
+ HashMap> references1 = new HashMap<>();
+ references1.put("Key1", List.of(ParserUtil.parseIndex("1")));
+ ViewCommand viewCommand1 = new ViewCommand(references1);
+ HashMap> referencesSame = new HashMap<>();
+ referencesSame.put("Key1", List.of(ParserUtil.parseIndex("1")));
+ ViewCommand viewCommandSameValues = new ViewCommand(referencesSame);
+ HashMap> referencesDifferent = new HashMap<>();
+ referencesDifferent.put("Key2", List.of(ParserUtil.parseIndex("2")));
+ ViewCommand viewCommandDifferentValues = new ViewCommand(referencesDifferent);
+ String notACommand = "This is a string, not a command";
+ assertTrue(viewCommand1.equals(viewCommand1));
+ assertTrue(viewCommand1.equals(viewCommandSameValues));
+ assertFalse(viewCommand1.equals(viewCommandDifferentValues));
+ assertFalse(viewCommand1.equals(null));
+ assertFalse(viewCommand1.equals(notACommand));
+ }
+
+}
diff --git a/src/test/java/seedu/address/logic/commands/ViewLeaveCommandTest.java b/src/test/java/seedu/address/logic/commands/ViewLeaveCommandTest.java
new file mode 100644
index 00000000000..cfceaff3eab
--- /dev/null
+++ b/src/test/java/seedu/address/logic/commands/ViewLeaveCommandTest.java
@@ -0,0 +1,67 @@
+package seedu.address.logic.commands;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static seedu.address.logic.commands.CommandTestUtil.assertCommandSuccess;
+import static seedu.address.testutil.TypicalPersons.getTypicalAddressBook;
+
+import java.util.function.Predicate;
+
+import org.junit.jupiter.api.Test;
+
+import seedu.address.logic.Messages;
+import seedu.address.model.Model;
+import seedu.address.model.ModelManager;
+import seedu.address.model.UserPrefs;
+import seedu.address.model.person.HasLeaveAnyMonthPredicate;
+import seedu.address.model.person.HasLeaveThisMonthPredicate;
+import seedu.address.model.person.Person;
+
+
+
+class ViewLeaveCommandTest {
+
+ private Model model = new ModelManager(getTypicalAddressBook(), new UserPrefs());
+
+ @Test
+ public void equalsTest() {
+ Predicate predicate1 = new HasLeaveThisMonthPredicate("2");
+ Predicate predicate2 = new HasLeaveThisMonthPredicate("2");
+ ViewLeaveCommand command1 = new ViewLeaveCommand(predicate1);
+ ViewLeaveCommand command2 = new ViewLeaveCommand(predicate2);
+ ViewLeaveCommand command3 = new ViewLeaveCommand(new HasLeaveThisMonthPredicate("12"));
+ assertEquals(command1, command1);
+ assertEquals(command1, command2);
+ assertFalse(command1.equals(command3));
+ assertFalse(command1.equals(null));
+ }
+
+ @Test
+ void execute_successAnyMonth() {
+ Model expectedModel = new ModelManager(getTypicalAddressBook(), new UserPrefs());
+ Predicate testPredicate = new HasLeaveAnyMonthPredicate();
+ expectedModel.updateFilteredPersonList(testPredicate);
+ assertCommandSuccess(new ViewLeaveCommand(testPredicate), model,
+ String.format(Messages.MESSAGE_VIEW_LEAVE_SUCCESS,
+ expectedModel.getFilteredPersonList().size()), expectedModel);
+ }
+
+ @Test
+ void execute_successParticularMonth() {
+ Model expectedModel = new ModelManager(getTypicalAddressBook(), new UserPrefs());
+ Predicate testPredicate = new HasLeaveThisMonthPredicate("1");
+ expectedModel.updateFilteredPersonList(testPredicate);
+ assertCommandSuccess(new ViewLeaveCommand(testPredicate), model,
+ String.format(Messages.MESSAGE_VIEW_LEAVE_SUCCESS,
+ expectedModel.getFilteredPersonList().size()), expectedModel);
+ }
+
+ @Test
+ void execute_successNoLeaveInMonths() {
+ Model expectedModel = new ModelManager(getTypicalAddressBook(), new UserPrefs());
+ Predicate testPredicate = new HasLeaveAnyMonthPredicate().negate();
+ expectedModel.updateFilteredPersonList(testPredicate);
+ assertCommandSuccess(new ViewLeaveCommand(testPredicate), model,
+ Messages.MESSAGE_VIEW_LEAVE_NO_EMPLOYEES, expectedModel);
+ }
+}
diff --git a/src/test/java/seedu/address/logic/parser/AddCommandParserTest.java b/src/test/java/seedu/address/logic/parser/AddCommandParserTest.java
index 5bc11d3cdaa..b67d38e32c8 100644
--- a/src/test/java/seedu/address/logic/parser/AddCommandParserTest.java
+++ b/src/test/java/seedu/address/logic/parser/AddCommandParserTest.java
@@ -3,34 +3,36 @@
import static seedu.address.logic.Messages.MESSAGE_INVALID_COMMAND_FORMAT;
import static seedu.address.logic.commands.CommandTestUtil.ADDRESS_DESC_AMY;
import static seedu.address.logic.commands.CommandTestUtil.ADDRESS_DESC_BOB;
+import static seedu.address.logic.commands.CommandTestUtil.BUDGET_DESC;
+import static seedu.address.logic.commands.CommandTestUtil.DEPARTMENT_DESC;
+import static seedu.address.logic.commands.CommandTestUtil.DOB_DESC;
import static seedu.address.logic.commands.CommandTestUtil.EMAIL_DESC_AMY;
import static seedu.address.logic.commands.CommandTestUtil.EMAIL_DESC_BOB;
import static seedu.address.logic.commands.CommandTestUtil.INVALID_ADDRESS_DESC;
import static seedu.address.logic.commands.CommandTestUtil.INVALID_EMAIL_DESC;
import static seedu.address.logic.commands.CommandTestUtil.INVALID_NAME_DESC;
import static seedu.address.logic.commands.CommandTestUtil.INVALID_PHONE_DESC;
-import static seedu.address.logic.commands.CommandTestUtil.INVALID_TAG_DESC;
import static seedu.address.logic.commands.CommandTestUtil.NAME_DESC_AMY;
import static seedu.address.logic.commands.CommandTestUtil.NAME_DESC_BOB;
import static seedu.address.logic.commands.CommandTestUtil.PHONE_DESC_AMY;
import static seedu.address.logic.commands.CommandTestUtil.PHONE_DESC_BOB;
import static seedu.address.logic.commands.CommandTestUtil.PREAMBLE_NON_EMPTY;
import static seedu.address.logic.commands.CommandTestUtil.PREAMBLE_WHITESPACE;
-import static seedu.address.logic.commands.CommandTestUtil.TAG_DESC_FRIEND;
-import static seedu.address.logic.commands.CommandTestUtil.TAG_DESC_HUSBAND;
+import static seedu.address.logic.commands.CommandTestUtil.SALARY_DESC;
import static seedu.address.logic.commands.CommandTestUtil.VALID_ADDRESS_BOB;
import static seedu.address.logic.commands.CommandTestUtil.VALID_EMAIL_BOB;
import static seedu.address.logic.commands.CommandTestUtil.VALID_NAME_BOB;
import static seedu.address.logic.commands.CommandTestUtil.VALID_PHONE_BOB;
-import static seedu.address.logic.commands.CommandTestUtil.VALID_TAG_FRIEND;
-import static seedu.address.logic.commands.CommandTestUtil.VALID_TAG_HUSBAND;
import static seedu.address.logic.parser.CliSyntax.PREFIX_ADDRESS;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_CLAIM_BUDGET;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_DEPARTMENT;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_DOB;
import static seedu.address.logic.parser.CliSyntax.PREFIX_EMAIL;
import static seedu.address.logic.parser.CliSyntax.PREFIX_NAME;
import static seedu.address.logic.parser.CliSyntax.PREFIX_PHONE;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_SALARY;
import static seedu.address.logic.parser.CommandParserTestUtil.assertParseFailure;
import static seedu.address.logic.parser.CommandParserTestUtil.assertParseSuccess;
-import static seedu.address.testutil.TypicalPersons.AMY;
import static seedu.address.testutil.TypicalPersons.BOB;
import org.junit.jupiter.api.Test;
@@ -42,7 +44,6 @@
import seedu.address.model.person.Name;
import seedu.address.model.person.Person;
import seedu.address.model.person.Phone;
-import seedu.address.model.tag.Tag;
import seedu.address.testutil.PersonBuilder;
public class AddCommandParserTest {
@@ -50,25 +51,19 @@ public class AddCommandParserTest {
@Test
public void parse_allFieldsPresent_success() {
- Person expectedPerson = new PersonBuilder(BOB).withTags(VALID_TAG_FRIEND).build();
+ Person expectedPerson = new PersonBuilder(BOB).build();
// whitespace only preamble
assertParseSuccess(parser, PREAMBLE_WHITESPACE + NAME_DESC_BOB + PHONE_DESC_BOB + EMAIL_DESC_BOB
- + ADDRESS_DESC_BOB + TAG_DESC_FRIEND, new AddCommand(expectedPerson));
-
+ + ADDRESS_DESC_BOB + SALARY_DESC + BUDGET_DESC + DEPARTMENT_DESC + DOB_DESC,
+ new AddCommand(expectedPerson));
- // multiple tags - all accepted
- Person expectedPersonMultipleTags = new PersonBuilder(BOB).withTags(VALID_TAG_FRIEND, VALID_TAG_HUSBAND)
- .build();
- assertParseSuccess(parser,
- NAME_DESC_BOB + PHONE_DESC_BOB + EMAIL_DESC_BOB + ADDRESS_DESC_BOB + TAG_DESC_HUSBAND + TAG_DESC_FRIEND,
- new AddCommand(expectedPersonMultipleTags));
}
@Test
public void parse_repeatedNonTagValue_failure() {
String validExpectedPersonString = NAME_DESC_BOB + PHONE_DESC_BOB + EMAIL_DESC_BOB
- + ADDRESS_DESC_BOB + TAG_DESC_FRIEND;
+ + ADDRESS_DESC_BOB + SALARY_DESC + BUDGET_DESC + DEPARTMENT_DESC + DOB_DESC;
// multiple names
assertParseFailure(parser, NAME_DESC_AMY + validExpectedPersonString,
@@ -90,7 +85,8 @@ public void parse_repeatedNonTagValue_failure() {
assertParseFailure(parser,
validExpectedPersonString + PHONE_DESC_AMY + EMAIL_DESC_AMY + NAME_DESC_AMY + ADDRESS_DESC_AMY
+ validExpectedPersonString,
- Messages.getErrorMessageForDuplicatePrefixes(PREFIX_NAME, PREFIX_ADDRESS, PREFIX_EMAIL, PREFIX_PHONE));
+ Messages.getErrorMessageForDuplicatePrefixes(PREFIX_NAME, PREFIX_ADDRESS, PREFIX_EMAIL, PREFIX_PHONE,
+ PREFIX_SALARY, PREFIX_CLAIM_BUDGET, PREFIX_DEPARTMENT, PREFIX_DOB));
// invalid value followed by valid value
@@ -129,32 +125,28 @@ public void parse_repeatedNonTagValue_failure() {
Messages.getErrorMessageForDuplicatePrefixes(PREFIX_ADDRESS));
}
- @Test
- public void parse_optionalFieldsMissing_success() {
- // zero tags
- Person expectedPerson = new PersonBuilder(AMY).withTags().build();
- assertParseSuccess(parser, NAME_DESC_AMY + PHONE_DESC_AMY + EMAIL_DESC_AMY + ADDRESS_DESC_AMY,
- new AddCommand(expectedPerson));
- }
-
@Test
public void parse_compulsoryFieldMissing_failure() {
String expectedMessage = String.format(MESSAGE_INVALID_COMMAND_FORMAT, AddCommand.MESSAGE_USAGE);
// missing name prefix
- assertParseFailure(parser, VALID_NAME_BOB + PHONE_DESC_BOB + EMAIL_DESC_BOB + ADDRESS_DESC_BOB,
+ assertParseFailure(parser, VALID_NAME_BOB + PHONE_DESC_BOB + EMAIL_DESC_BOB + ADDRESS_DESC_BOB
+ + SALARY_DESC + BUDGET_DESC + DEPARTMENT_DESC + DOB_DESC,
expectedMessage);
// missing phone prefix
- assertParseFailure(parser, NAME_DESC_BOB + VALID_PHONE_BOB + EMAIL_DESC_BOB + ADDRESS_DESC_BOB,
+ assertParseFailure(parser, NAME_DESC_BOB + VALID_PHONE_BOB + EMAIL_DESC_BOB + ADDRESS_DESC_BOB
+ + SALARY_DESC + BUDGET_DESC + DEPARTMENT_DESC + DOB_DESC,
expectedMessage);
// missing email prefix
- assertParseFailure(parser, NAME_DESC_BOB + PHONE_DESC_BOB + VALID_EMAIL_BOB + ADDRESS_DESC_BOB,
+ assertParseFailure(parser, NAME_DESC_BOB + PHONE_DESC_BOB + VALID_EMAIL_BOB + ADDRESS_DESC_BOB
+ + SALARY_DESC + BUDGET_DESC + DEPARTMENT_DESC + DOB_DESC,
expectedMessage);
// missing address prefix
- assertParseFailure(parser, NAME_DESC_BOB + PHONE_DESC_BOB + EMAIL_DESC_BOB + VALID_ADDRESS_BOB,
+ assertParseFailure(parser, NAME_DESC_BOB + PHONE_DESC_BOB + EMAIL_DESC_BOB + VALID_ADDRESS_BOB
+ + SALARY_DESC + BUDGET_DESC + DEPARTMENT_DESC + DOB_DESC,
expectedMessage);
// all prefixes missing
@@ -166,31 +158,28 @@ public void parse_compulsoryFieldMissing_failure() {
public void parse_invalidValue_failure() {
// invalid name
assertParseFailure(parser, INVALID_NAME_DESC + PHONE_DESC_BOB + EMAIL_DESC_BOB + ADDRESS_DESC_BOB
- + TAG_DESC_HUSBAND + TAG_DESC_FRIEND, Name.MESSAGE_CONSTRAINTS);
+ + SALARY_DESC + BUDGET_DESC + DEPARTMENT_DESC + DOB_DESC, Name.MESSAGE_CONSTRAINTS);
// invalid phone
assertParseFailure(parser, NAME_DESC_BOB + INVALID_PHONE_DESC + EMAIL_DESC_BOB + ADDRESS_DESC_BOB
- + TAG_DESC_HUSBAND + TAG_DESC_FRIEND, Phone.MESSAGE_CONSTRAINTS);
+ + SALARY_DESC + BUDGET_DESC + DEPARTMENT_DESC + DOB_DESC, Phone.MESSAGE_CONSTRAINTS);
// invalid email
assertParseFailure(parser, NAME_DESC_BOB + PHONE_DESC_BOB + INVALID_EMAIL_DESC + ADDRESS_DESC_BOB
- + TAG_DESC_HUSBAND + TAG_DESC_FRIEND, Email.MESSAGE_CONSTRAINTS);
+ + SALARY_DESC + BUDGET_DESC + DEPARTMENT_DESC + DOB_DESC, Email.MESSAGE_CONSTRAINTS);
// invalid address
assertParseFailure(parser, NAME_DESC_BOB + PHONE_DESC_BOB + EMAIL_DESC_BOB + INVALID_ADDRESS_DESC
- + TAG_DESC_HUSBAND + TAG_DESC_FRIEND, Address.MESSAGE_CONSTRAINTS);
-
- // invalid tag
- assertParseFailure(parser, NAME_DESC_BOB + PHONE_DESC_BOB + EMAIL_DESC_BOB + ADDRESS_DESC_BOB
- + INVALID_TAG_DESC + VALID_TAG_FRIEND, Tag.MESSAGE_CONSTRAINTS);
+ + SALARY_DESC + BUDGET_DESC + DEPARTMENT_DESC + DOB_DESC, Address.MESSAGE_CONSTRAINTS);
// two invalid values, only first invalid value reported
- assertParseFailure(parser, INVALID_NAME_DESC + PHONE_DESC_BOB + EMAIL_DESC_BOB + INVALID_ADDRESS_DESC,
+ assertParseFailure(parser, INVALID_NAME_DESC + PHONE_DESC_BOB + EMAIL_DESC_BOB + INVALID_ADDRESS_DESC
+ + SALARY_DESC + BUDGET_DESC + DEPARTMENT_DESC + DOB_DESC,
Name.MESSAGE_CONSTRAINTS);
// non-empty preamble
assertParseFailure(parser, PREAMBLE_NON_EMPTY + NAME_DESC_BOB + PHONE_DESC_BOB + EMAIL_DESC_BOB
- + ADDRESS_DESC_BOB + TAG_DESC_HUSBAND + TAG_DESC_FRIEND,
+ + ADDRESS_DESC_BOB + SALARY_DESC + BUDGET_DESC + DEPARTMENT_DESC + DOB_DESC,
String.format(MESSAGE_INVALID_COMMAND_FORMAT, AddCommand.MESSAGE_USAGE));
}
}
diff --git a/src/test/java/seedu/address/logic/parser/AddressBookParserTest.java b/src/test/java/seedu/address/logic/parser/AddressBookParserTest.java
index 5a1ab3dbc0c..c4972311bdb 100644
--- a/src/test/java/seedu/address/logic/parser/AddressBookParserTest.java
+++ b/src/test/java/seedu/address/logic/parser/AddressBookParserTest.java
@@ -1,8 +1,11 @@
package seedu.address.logic.parser;
import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static seedu.address.logic.Messages.MESSAGE_INVALID_COMMAND_FORMAT;
+import static seedu.address.logic.Messages.MESSAGE_LIST_COMMAND_FORMAT;
+import static seedu.address.logic.Messages.MESSAGE_NO_ARGUMENTS_EXPECTED;
import static seedu.address.logic.Messages.MESSAGE_UNKNOWN_COMMAND;
import static seedu.address.testutil.Assert.assertThrows;
import static seedu.address.testutil.TypicalIndexes.INDEX_FIRST_PERSON;
@@ -14,6 +17,7 @@
import org.junit.jupiter.api.Test;
import seedu.address.logic.commands.AddCommand;
+import seedu.address.logic.commands.BirthdayCommand;
import seedu.address.logic.commands.ClearCommand;
import seedu.address.logic.commands.DeleteCommand;
import seedu.address.logic.commands.EditCommand;
@@ -21,7 +25,13 @@
import seedu.address.logic.commands.ExitCommand;
import seedu.address.logic.commands.FindCommand;
import seedu.address.logic.commands.HelpCommand;
+import seedu.address.logic.commands.LeaveCommand;
import seedu.address.logic.commands.ListCommand;
+import seedu.address.logic.commands.RedoCommand;
+import seedu.address.logic.commands.SortCommand;
+import seedu.address.logic.commands.ThemeCommand;
+import seedu.address.logic.commands.UndoCommand;
+import seedu.address.logic.commands.ViewLeaveCommand;
import seedu.address.logic.parser.exceptions.ParseException;
import seedu.address.model.person.NameContainsKeywordsPredicate;
import seedu.address.model.person.Person;
@@ -43,7 +53,8 @@ public void parseCommand_add() throws Exception {
@Test
public void parseCommand_clear() throws Exception {
assertTrue(parser.parseCommand(ClearCommand.COMMAND_WORD) instanceof ClearCommand);
- assertTrue(parser.parseCommand(ClearCommand.COMMAND_WORD + " 3") instanceof ClearCommand);
+ assertThrows(ParseException.class, String.format(MESSAGE_NO_ARGUMENTS_EXPECTED, ClearCommand.COMMAND_WORD), ()
+ -> parser.parseCommand(ClearCommand.COMMAND_WORD + " 3"));
}
@Test
@@ -59,13 +70,14 @@ public void parseCommand_edit() throws Exception {
EditPersonDescriptor descriptor = new EditPersonDescriptorBuilder(person).build();
EditCommand command = (EditCommand) parser.parseCommand(EditCommand.COMMAND_WORD + " "
+ INDEX_FIRST_PERSON.getOneBased() + " " + PersonUtil.getEditPersonDescriptorDetails(descriptor));
- assertEquals(new EditCommand(INDEX_FIRST_PERSON, descriptor), command);
+ assertNotEquals(new EditCommand(INDEX_FIRST_PERSON, descriptor), command);
}
@Test
public void parseCommand_exit() throws Exception {
assertTrue(parser.parseCommand(ExitCommand.COMMAND_WORD) instanceof ExitCommand);
- assertTrue(parser.parseCommand(ExitCommand.COMMAND_WORD + " 3") instanceof ExitCommand);
+ assertThrows(ParseException.class, String.format(MESSAGE_NO_ARGUMENTS_EXPECTED, ExitCommand.COMMAND_WORD), ()
+ -> parser.parseCommand(ExitCommand.COMMAND_WORD + " 3"));
}
@Test
@@ -79,23 +91,83 @@ public void parseCommand_find() throws Exception {
@Test
public void parseCommand_help() throws Exception {
assertTrue(parser.parseCommand(HelpCommand.COMMAND_WORD) instanceof HelpCommand);
- assertTrue(parser.parseCommand(HelpCommand.COMMAND_WORD + " 3") instanceof HelpCommand);
+ assertThrows(ParseException.class, String.format(MESSAGE_NO_ARGUMENTS_EXPECTED, HelpCommand.COMMAND_WORD), ()
+ -> parser.parseCommand(HelpCommand.COMMAND_WORD + " 3"));
}
@Test
public void parseCommand_list() throws Exception {
assertTrue(parser.parseCommand(ListCommand.COMMAND_WORD) instanceof ListCommand);
- assertTrue(parser.parseCommand(ListCommand.COMMAND_WORD + " 3") instanceof ListCommand);
+ assertThrows(ParseException.class, String.format(MESSAGE_INVALID_COMMAND_FORMAT,
+ MESSAGE_LIST_COMMAND_FORMAT), ()
+ -> parser.parseCommand(ListCommand.COMMAND_WORD + " 3"));
+ }
+
+ @Test
+ public void parseCommand_leave() throws Exception {
+ assertTrue(parser.parseCommand(LeaveCommand.COMMAND_WORD + " 1 m/2") instanceof LeaveCommand);
+ }
+
+ @Test
+ public void parseCommand_viewLeave() throws Exception {
+ assertTrue(parser.parseCommand(ViewLeaveCommand.COMMAND_WORD + " m/2") instanceof ViewLeaveCommand);
}
@Test
public void parseCommand_unrecognisedInput_throwsParseException() {
assertThrows(ParseException.class, String.format(MESSAGE_INVALID_COMMAND_FORMAT, HelpCommand.MESSAGE_USAGE), ()
- -> parser.parseCommand(""));
+ -> parser.parseCommand(""));
}
@Test
public void parseCommand_unknownCommand_throwsParseException() {
assertThrows(ParseException.class, MESSAGE_UNKNOWN_COMMAND, () -> parser.parseCommand("unknownCommand"));
}
+
+ @Test
+ public void parseCommand_birthday_withMonth() throws Exception {
+ assertTrue(parser.parseCommand(BirthdayCommand.COMMAND_WORD + " m/ 3") instanceof BirthdayCommand);
+ }
+
+ @Test
+ public void parseCommand_birthday_withoutMonth() throws Exception {
+ assertTrue(parser.parseCommand(BirthdayCommand.COMMAND_WORD) instanceof BirthdayCommand);
+ }
+
+ @Test
+ public void parseCommand_undo() throws Exception {
+ assertTrue(parser.parseCommand(UndoCommand.COMMAND_WORD) instanceof UndoCommand);
+ assertThrows(ParseException.class, String.format(MESSAGE_NO_ARGUMENTS_EXPECTED, UndoCommand.COMMAND_WORD), ()
+ -> parser.parseCommand(UndoCommand.COMMAND_WORD + " 3"));
+ }
+
+ @Test
+ public void parseCommand_redo() throws Exception {
+ assertTrue(parser.parseCommand(RedoCommand.COMMAND_WORD) instanceof RedoCommand);
+ assertThrows(ParseException.class, String.format(MESSAGE_NO_ARGUMENTS_EXPECTED, RedoCommand.COMMAND_WORD), ()
+ -> parser.parseCommand(RedoCommand.COMMAND_WORD + " 3"));
+ }
+
+ @Test
+ public void parseCommand_sort() throws Exception {
+ assertTrue(parser.parseCommand(SortCommand.COMMAND_WORD + " name") instanceof SortCommand);
+ assertThrows(ParseException.class, String.format(MESSAGE_INVALID_COMMAND_FORMAT, SortCommand.MESSAGE_USAGE), ()
+ -> parser.parseCommand(SortCommand.COMMAND_WORD));
+ }
+
+ @Test
+ public void ensureEmptyArgument_isEmpty() throws Exception {
+ assertTrue(parser.parseCommand(HelpCommand.COMMAND_WORD) instanceof HelpCommand);
+ }
+
+ @Test
+ public void ensureEmptyArgument_isNotEmpty() throws Exception {
+ assertThrows(ParseException.class, String.format(MESSAGE_NO_ARGUMENTS_EXPECTED, HelpCommand.COMMAND_WORD), ()
+ -> parser.parseCommand(HelpCommand.COMMAND_WORD + " abc ad"));
+ }
+
+ @Test
+ public void parseCommand_theme() throws Exception {
+ assertTrue(parser.parseCommand(ThemeCommand.COMMAND_WORD + " light") instanceof ThemeCommand);
+ }
}
diff --git a/src/test/java/seedu/address/logic/parser/ArgumentTokenizerTest.java b/src/test/java/seedu/address/logic/parser/ArgumentTokenizerTest.java
index c97308935f5..24a870d1c3d 100644
--- a/src/test/java/seedu/address/logic/parser/ArgumentTokenizerTest.java
+++ b/src/test/java/seedu/address/logic/parser/ArgumentTokenizerTest.java
@@ -146,5 +146,4 @@ public void equalsMethod() {
assertNotEquals(aaa, "aaa");
assertNotEquals(aaa, new Prefix("aab"));
}
-
}
diff --git a/src/test/java/seedu/address/logic/parser/BirthdayCommandParserTest.java b/src/test/java/seedu/address/logic/parser/BirthdayCommandParserTest.java
new file mode 100644
index 00000000000..076b7078b9d
--- /dev/null
+++ b/src/test/java/seedu/address/logic/parser/BirthdayCommandParserTest.java
@@ -0,0 +1,61 @@
+package seedu.address.logic.parser;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+
+import java.time.LocalDate;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.junit.jupiter.api.Test;
+
+import seedu.address.logic.commands.BirthdayCommand;
+import seedu.address.logic.parser.exceptions.ParseException;
+import seedu.address.model.person.MatchingBirthdayPredicate;
+import seedu.address.model.person.Month;
+
+public class BirthdayCommandParserTest {
+ private final BirthdayCommandParser parser = new BirthdayCommandParser();
+
+ @Test
+ public void parse_validArgs_returnsBirthdayCommand() throws ParseException {
+ List monthList = new ArrayList<>();
+ monthList.add(new Month(3));
+ BirthdayCommand successfulMatch = new BirthdayCommand(new MatchingBirthdayPredicate(monthList));
+ assertEquals(parser.parse(" m/3"), successfulMatch);
+ }
+
+ @Test
+ public void parse_validMultipleArgs_returnsBirthdayCommand() throws ParseException {
+ List monthList = new ArrayList<>();
+ monthList.add(new Month(3));
+ monthList.add(new Month(4));
+ BirthdayCommand successfulMatch = new BirthdayCommand(new MatchingBirthdayPredicate(monthList));
+ assertEquals(parser.parse(" m/3,4"), successfulMatch);
+ }
+
+ @Test
+ public void parse_invalidMultipleArgs_exceptionThrown() {
+ assertThrows(ParseException.class, () -> parser.parse(" m/1,-1"));
+ }
+
+ @Test
+ public void duplicatePrefix_exceptionThrown() {
+ assertThrows(ParseException.class, () -> parser.parse(" m/1 m/2"));
+ }
+
+ @Test
+ public void parse_noArgs_returnsBirthdayCommand() throws ParseException {
+ List monthList = new ArrayList<>();
+ monthList.add(new Month(LocalDate.now().getMonthValue()));
+ BirthdayCommand currentMonthMatch = new BirthdayCommand(
+ new MatchingBirthdayPredicate(monthList));
+ assertEquals(parser.parse(""), currentMonthMatch);
+ }
+
+ @Test
+ public void parse_invalidArgs_exceptionThrown() throws ParseException {
+ assertThrows(ParseException.class, () -> parser.parse(" r/2"));
+ assertThrows(ParseException.class, () -> parser.parse(" m/a"));
+ }
+}
diff --git a/src/test/java/seedu/address/logic/parser/ClaimCommandParserTest.java b/src/test/java/seedu/address/logic/parser/ClaimCommandParserTest.java
new file mode 100644
index 00000000000..9751326e279
--- /dev/null
+++ b/src/test/java/seedu/address/logic/parser/ClaimCommandParserTest.java
@@ -0,0 +1,64 @@
+package seedu.address.logic.parser;
+
+import static seedu.address.logic.commands.CommandTestUtil.CLAIM_ADDITION;
+import static seedu.address.logic.commands.CommandTestUtil.CLAIM_AMOUNT;
+import static seedu.address.logic.commands.CommandTestUtil.CLAIM_AMOUNT_STR;
+import static seedu.address.logic.commands.CommandTestUtil.CLAIM_DELIMITER;
+import static seedu.address.logic.commands.CommandTestUtil.CLAIM_EMPTY_AMOUNT;
+import static seedu.address.logic.commands.CommandTestUtil.CLAIM_EMPTY_INDEX;
+import static seedu.address.logic.parser.CommandParserTestUtil.assertParseFailure;
+import static seedu.address.logic.parser.CommandParserTestUtil.assertParseSuccess;
+import static seedu.address.model.person.Claim.ALPHABETS_ERROR;
+import static seedu.address.model.person.Claim.NO_SYMBOLS_ERROR;
+import static seedu.address.testutil.TypicalIndexes.INDEX_FIRST_PERSON;
+
+import org.junit.jupiter.api.Test;
+
+import seedu.address.logic.Messages;
+import seedu.address.logic.commands.ClaimCommand;
+
+public class ClaimCommandParserTest {
+
+ private ClaimCommandParser parser = new ClaimCommandParser();
+
+ @Test
+ public void parse_withAllFields_presentReturnsSuccess() {
+ String userInput = String.format("%s %s%s%s", INDEX_FIRST_PERSON.getOneBased(),
+ CLAIM_DELIMITER, CLAIM_ADDITION, CLAIM_AMOUNT_STR);
+ ClaimCommand successClaimCommand = new ClaimCommand(INDEX_FIRST_PERSON,
+ false, CLAIM_AMOUNT);
+ assertParseSuccess(parser, userInput, successClaimCommand);
+ }
+
+ @Test
+ public void parse_withEmptyIndex_returnsFailure() {
+ String userInput = CLAIM_DELIMITER + CLAIM_ADDITION + CLAIM_AMOUNT_STR;
+ assertParseFailure(parser, userInput, CLAIM_EMPTY_INDEX);
+ }
+
+ @Test
+ public void parse_claimTooLarge_returnsFailure() {
+ String userInput = "1 $/+100000000000000000000000000000000";
+ assertParseFailure(parser, userInput, Messages.TOO_LARGE_A_NUMBER);
+ }
+
+ @Test
+ public void parse_withEmptyAmount_returnsFailure() {
+ String userInput = String.valueOf(INDEX_FIRST_PERSON.getOneBased());
+ assertParseFailure(parser, userInput, CLAIM_EMPTY_AMOUNT);
+ }
+
+ @Test
+ public void parse_withEmptySymbols_returnsFailure() {
+ String userInput = String.format("%s %s%s", INDEX_FIRST_PERSON.getOneBased(),
+ CLAIM_DELIMITER, CLAIM_AMOUNT_STR);
+ assertParseFailure(parser, userInput, NO_SYMBOLS_ERROR);
+ }
+
+ @Test
+ public void parse_withNonDigitClaimAmount_returnsFailure() {
+ String userInput = String.format("%s %s%s%s%s", INDEX_FIRST_PERSON.getOneBased(),
+ CLAIM_DELIMITER, CLAIM_ADDITION, CLAIM_AMOUNT_STR, "b");
+ assertParseFailure(parser, userInput, ALPHABETS_ERROR);
+ }
+}
diff --git a/src/test/java/seedu/address/logic/parser/EditCommandParserTest.java b/src/test/java/seedu/address/logic/parser/EditCommandParserTest.java
index cc7175172d4..c10ea258cfa 100644
--- a/src/test/java/seedu/address/logic/parser/EditCommandParserTest.java
+++ b/src/test/java/seedu/address/logic/parser/EditCommandParserTest.java
@@ -9,7 +9,6 @@
import static seedu.address.logic.commands.CommandTestUtil.INVALID_EMAIL_DESC;
import static seedu.address.logic.commands.CommandTestUtil.INVALID_NAME_DESC;
import static seedu.address.logic.commands.CommandTestUtil.INVALID_PHONE_DESC;
-import static seedu.address.logic.commands.CommandTestUtil.INVALID_TAG_DESC;
import static seedu.address.logic.commands.CommandTestUtil.NAME_DESC_AMY;
import static seedu.address.logic.commands.CommandTestUtil.PHONE_DESC_AMY;
import static seedu.address.logic.commands.CommandTestUtil.PHONE_DESC_BOB;
@@ -20,12 +19,9 @@
import static seedu.address.logic.commands.CommandTestUtil.VALID_NAME_AMY;
import static seedu.address.logic.commands.CommandTestUtil.VALID_PHONE_AMY;
import static seedu.address.logic.commands.CommandTestUtil.VALID_PHONE_BOB;
-import static seedu.address.logic.commands.CommandTestUtil.VALID_TAG_FRIEND;
-import static seedu.address.logic.commands.CommandTestUtil.VALID_TAG_HUSBAND;
import static seedu.address.logic.parser.CliSyntax.PREFIX_ADDRESS;
import static seedu.address.logic.parser.CliSyntax.PREFIX_EMAIL;
import static seedu.address.logic.parser.CliSyntax.PREFIX_PHONE;
-import static seedu.address.logic.parser.CliSyntax.PREFIX_TAG;
import static seedu.address.logic.parser.CommandParserTestUtil.assertParseFailure;
import static seedu.address.logic.parser.CommandParserTestUtil.assertParseSuccess;
import static seedu.address.testutil.TypicalIndexes.INDEX_FIRST_PERSON;
@@ -42,13 +38,10 @@
import seedu.address.model.person.Email;
import seedu.address.model.person.Name;
import seedu.address.model.person.Phone;
-import seedu.address.model.tag.Tag;
import seedu.address.testutil.EditPersonDescriptorBuilder;
public class EditCommandParserTest {
- private static final String TAG_EMPTY = " " + PREFIX_TAG;
-
private static final String MESSAGE_INVALID_FORMAT =
String.format(MESSAGE_INVALID_COMMAND_FORMAT, EditCommand.MESSAGE_USAGE);
@@ -87,17 +80,10 @@ public void parse_invalidValue_failure() {
assertParseFailure(parser, "1" + INVALID_PHONE_DESC, Phone.MESSAGE_CONSTRAINTS); // invalid phone
assertParseFailure(parser, "1" + INVALID_EMAIL_DESC, Email.MESSAGE_CONSTRAINTS); // invalid email
assertParseFailure(parser, "1" + INVALID_ADDRESS_DESC, Address.MESSAGE_CONSTRAINTS); // invalid address
- assertParseFailure(parser, "1" + INVALID_TAG_DESC, Tag.MESSAGE_CONSTRAINTS); // invalid tag
// invalid phone followed by valid email
assertParseFailure(parser, "1" + INVALID_PHONE_DESC + EMAIL_DESC_AMY, Phone.MESSAGE_CONSTRAINTS);
- // while parsing {@code PREFIX_TAG} alone will reset the tags of the {@code Person} being edited,
- // parsing it together with a valid tag results in error
- assertParseFailure(parser, "1" + TAG_DESC_FRIEND + TAG_DESC_HUSBAND + TAG_EMPTY, Tag.MESSAGE_CONSTRAINTS);
- assertParseFailure(parser, "1" + TAG_DESC_FRIEND + TAG_EMPTY + TAG_DESC_HUSBAND, Tag.MESSAGE_CONSTRAINTS);
- assertParseFailure(parser, "1" + TAG_EMPTY + TAG_DESC_FRIEND + TAG_DESC_HUSBAND, Tag.MESSAGE_CONSTRAINTS);
-
// multiple invalid values, but only the first invalid value is captured
assertParseFailure(parser, "1" + INVALID_NAME_DESC + INVALID_EMAIL_DESC + VALID_ADDRESS_AMY + VALID_PHONE_AMY,
Name.MESSAGE_CONSTRAINTS);
@@ -106,12 +92,12 @@ public void parse_invalidValue_failure() {
@Test
public void parse_allFieldsSpecified_success() {
Index targetIndex = INDEX_SECOND_PERSON;
- String userInput = targetIndex.getOneBased() + PHONE_DESC_BOB + TAG_DESC_HUSBAND
- + EMAIL_DESC_AMY + ADDRESS_DESC_AMY + NAME_DESC_AMY + TAG_DESC_FRIEND;
+ String userInput = targetIndex.getOneBased() + PHONE_DESC_BOB
+ + EMAIL_DESC_AMY + ADDRESS_DESC_AMY + NAME_DESC_AMY;
EditPersonDescriptor descriptor = new EditPersonDescriptorBuilder().withName(VALID_NAME_AMY)
.withPhone(VALID_PHONE_BOB).withEmail(VALID_EMAIL_AMY).withAddress(VALID_ADDRESS_AMY)
- .withTags(VALID_TAG_HUSBAND, VALID_TAG_FRIEND).build();
+ .build();
EditCommand expectedCommand = new EditCommand(targetIndex, descriptor);
assertParseSuccess(parser, userInput, expectedCommand);
@@ -155,12 +141,6 @@ public void parse_oneFieldSpecified_success() {
descriptor = new EditPersonDescriptorBuilder().withAddress(VALID_ADDRESS_AMY).build();
expectedCommand = new EditCommand(targetIndex, descriptor);
assertParseSuccess(parser, userInput, expectedCommand);
-
- // tags
- userInput = targetIndex.getOneBased() + TAG_DESC_FRIEND;
- descriptor = new EditPersonDescriptorBuilder().withTags(VALID_TAG_FRIEND).build();
- expectedCommand = new EditCommand(targetIndex, descriptor);
- assertParseSuccess(parser, userInput, expectedCommand);
}
@Test
@@ -194,15 +174,4 @@ public void parse_multipleRepeatedFields_failure() {
assertParseFailure(parser, userInput,
Messages.getErrorMessageForDuplicatePrefixes(PREFIX_PHONE, PREFIX_EMAIL, PREFIX_ADDRESS));
}
-
- @Test
- public void parse_resetTags_success() {
- Index targetIndex = INDEX_THIRD_PERSON;
- String userInput = targetIndex.getOneBased() + TAG_EMPTY;
-
- EditPersonDescriptor descriptor = new EditPersonDescriptorBuilder().withTags().build();
- EditCommand expectedCommand = new EditCommand(targetIndex, descriptor);
-
- assertParseSuccess(parser, userInput, expectedCommand);
- }
}
diff --git a/src/test/java/seedu/address/logic/parser/ExportCommandParserTest.java b/src/test/java/seedu/address/logic/parser/ExportCommandParserTest.java
new file mode 100644
index 00000000000..10370c1ddc7
--- /dev/null
+++ b/src/test/java/seedu/address/logic/parser/ExportCommandParserTest.java
@@ -0,0 +1,58 @@
+package seedu.address.logic.parser;
+
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static seedu.address.logic.parser.CommandParserTestUtil.assertParseFailure;
+import static seedu.address.logic.parser.CommandParserTestUtil.assertParseSuccess;
+
+import org.junit.jupiter.api.Test;
+
+import seedu.address.logic.Messages;
+import seedu.address.logic.commands.ExportCommand;
+
+public class ExportCommandParserTest {
+
+ private ExportCommandParser parser = new ExportCommandParser();
+
+ @Test
+ public void parse_validFilename_success() {
+ String userInput = "engineering_dept";
+ ExportCommand successExportCommand = new ExportCommand(userInput);
+ assertParseSuccess(parser, userInput, successExportCommand);
+ }
+
+ @Test
+ public void parse_excessFilenames_failure() {
+ String invalidUserInput = "engineering_dept birthday_jan";
+ assertParseFailure(parser, invalidUserInput, Messages.WRONG_EXPORT_FILE_NAME_FAILURE);
+ }
+
+ @Test
+ public void parse_nullField_failure() {
+ assertThrows(NullPointerException.class, () -> {
+ parser.parse(null);
+ });
+ }
+
+ @Test
+ public void nameChecker_noArgs_failure() {
+ String noFileNames = "";
+ boolean isValidFilename = parser.nameChecker(noFileNames);
+ assertFalse(isValidFilename);
+ }
+
+ @Test
+ public void nameChecker_multipleArgs_failure() {
+ String noFileNames = "engineering_dept superman";
+ boolean isValidFilename = parser.nameChecker(noFileNames);
+ assertFalse(isValidFilename);
+ }
+
+ @Test
+ public void nameChecker_singleArgs_success() {
+ String noFileNames = "hr_department";
+ boolean isValidFilename = parser.nameChecker(noFileNames);
+ assertTrue(isValidFilename);
+ }
+}
diff --git a/src/test/java/seedu/address/logic/parser/LeaveCommandParserTest.java b/src/test/java/seedu/address/logic/parser/LeaveCommandParserTest.java
new file mode 100644
index 00000000000..18fed312555
--- /dev/null
+++ b/src/test/java/seedu/address/logic/parser/LeaveCommandParserTest.java
@@ -0,0 +1,54 @@
+package seedu.address.logic.parser;
+
+import static seedu.address.logic.Messages.MESSAGE_INVALID_COMMAND_FORMAT;
+import static seedu.address.logic.parser.CommandParserTestUtil.assertParseFailure;
+import static seedu.address.logic.parser.CommandParserTestUtil.assertParseSuccess;
+import static seedu.address.testutil.TypicalIndexes.INDEX_FIRST_PERSON;
+
+import org.junit.jupiter.api.Test;
+
+import seedu.address.logic.commands.LeaveCommand;
+
+
+public class LeaveCommandParserTest {
+
+ private LeaveCommandParser parser = new LeaveCommandParser();
+
+ @Test
+ public void parse_validArgs_returnsLeaveCommand() {
+ assertParseSuccess(parser, " 1 m/2", new LeaveCommand(INDEX_FIRST_PERSON, "0+0000000000"));
+ assertParseSuccess(parser, " 1 m/2,,,", new LeaveCommand(INDEX_FIRST_PERSON, "0+0000000000"));
+ assertParseSuccess(parser, " 1 m/-2", new LeaveCommand(INDEX_FIRST_PERSON, "0-0000000000"));
+ }
+
+ @Test
+ public void parse_invalidArgs_throwsParseException() {
+ assertParseFailure(parser, "a", String.format(MESSAGE_INVALID_COMMAND_FORMAT, LeaveCommand.MESSAGE_USAGE));
+
+ assertParseFailure(parser, " 1 m/2, 3", LeaveCommand.MESSAGE_SPACES_DETECTED + LeaveCommand.MESSAGE_USAGE);
+
+ assertParseFailure(parser, " 1 ", String.format(MESSAGE_INVALID_COMMAND_FORMAT, LeaveCommand.MESSAGE_USAGE));
+
+ assertParseFailure(parser, " 1 m/", LeaveCommand.MESSAGE_EMPTY + LeaveCommand.MESSAGE_USAGE);
+
+ assertParseFailure(parser, " 1 m/asdf", LeaveCommand.MESSAGE_INVALID_MONTH + LeaveCommand.MESSAGE_USAGE);
+
+ assertParseFailure(parser, " 1 m/3,-3", LeaveCommand.MESSAGE_AMBIGUOUS + LeaveCommand.MESSAGE_USAGE);
+
+ assertParseFailure(parser, " 1 m/-3,3", LeaveCommand.MESSAGE_AMBIGUOUS + LeaveCommand.MESSAGE_USAGE);
+
+ assertParseFailure(parser, " 1 m/z", LeaveCommand.MESSAGE_INVALID_MONTH + LeaveCommand.MESSAGE_USAGE);
+
+ assertParseFailure(parser, " 1 m/0", LeaveCommand.MESSAGE_INVALID_MONTH + LeaveCommand.MESSAGE_USAGE);
+
+ assertParseFailure(parser, " 1 m/-0", LeaveCommand.MESSAGE_INVALID_MONTH + LeaveCommand.MESSAGE_USAGE);
+
+ assertParseFailure(parser, " 1 m/13", LeaveCommand.MESSAGE_INVALID_MONTH + LeaveCommand.MESSAGE_USAGE);
+
+ assertParseFailure(parser, " 1 m/-13", LeaveCommand.MESSAGE_INVALID_MONTH + LeaveCommand.MESSAGE_USAGE);
+
+ assertParseFailure(parser, " 1 m/1,,,3", LeaveCommand.MESSAGE_INVALID_MONTH + LeaveCommand.MESSAGE_USAGE);
+
+ assertParseFailure(parser, " 1 m/1,123", LeaveCommand.MESSAGE_INVALID_MONTH + LeaveCommand.MESSAGE_USAGE);
+ }
+}
diff --git a/src/test/java/seedu/address/logic/parser/ListCommandParserTest.java b/src/test/java/seedu/address/logic/parser/ListCommandParserTest.java
new file mode 100644
index 00000000000..d1b00ae6701
--- /dev/null
+++ b/src/test/java/seedu/address/logic/parser/ListCommandParserTest.java
@@ -0,0 +1,42 @@
+package seedu.address.logic.parser;
+
+import static seedu.address.logic.commands.CommandTestUtil.assertCommandSuccess;
+import static seedu.address.logic.parser.CommandParserTestUtil.assertParseFailure;
+import static seedu.address.testutil.TypicalPersons.getTypicalAddressBook;
+
+import org.junit.jupiter.api.Test;
+
+import seedu.address.logic.Messages;
+import seedu.address.logic.parser.exceptions.ParseException;
+import seedu.address.model.Model;
+import seedu.address.model.ModelManager;
+import seedu.address.model.UserPrefs;
+import seedu.address.model.person.Department;
+import seedu.address.model.person.MatchingDepartmentPredicate;
+
+class ListCommandParserTest {
+
+ private Model model = new ModelManager(getTypicalAddressBook(), new UserPrefs());
+
+ private ListCommandParser parser = new ListCommandParser();
+
+ @Test
+ void parse_success() throws ParseException {
+ Department department1 = new Department("Engineering");
+ MatchingDepartmentPredicate predicate1 = new MatchingDepartmentPredicate(department1);
+ Model expectedModel1 = new ModelManager(getTypicalAddressBook(), new UserPrefs());
+ expectedModel1.updateFilteredPersonList(predicate1);
+ assertCommandSuccess(parser.parse(" d/engineering"), model,
+ String.format(Messages.MESSAGE_FILTER_SUCCESS,
+ expectedModel1.getFilteredPersonList().size()), expectedModel1);
+ }
+
+ @Test
+ void parse_failure() {
+ assertParseFailure(parser, " d/Engineering d/engineering",
+ Messages.getErrorMessageForDuplicatePrefixes(new Prefix("d/")));
+ assertParseFailure(parser, " abc", String.format(Messages.MESSAGE_INVALID_COMMAND_FORMAT,
+ Messages.MESSAGE_LIST_COMMAND_FORMAT));
+ assertParseFailure(parser, " d/", Messages.MESSAGE_EMPTY_DEPARTMENT_FILTER);
+ }
+}
diff --git a/src/test/java/seedu/address/logic/parser/ParserUtilTest.java b/src/test/java/seedu/address/logic/parser/ParserUtilTest.java
index 4256788b1a7..02a25db846e 100644
--- a/src/test/java/seedu/address/logic/parser/ParserUtilTest.java
+++ b/src/test/java/seedu/address/logic/parser/ParserUtilTest.java
@@ -21,8 +21,8 @@
import seedu.address.model.tag.Tag;
public class ParserUtilTest {
- private static final String INVALID_NAME = "R@chel";
- private static final String INVALID_PHONE = "+651234";
+ private static final String INVALID_NAME = " ";
+ private static final String INVALID_PHONE = "-651234";
private static final String INVALID_ADDRESS = " ";
private static final String INVALID_EMAIL = "example.com";
private static final String INVALID_TAG = "#friend";
@@ -35,6 +35,11 @@ public class ParserUtilTest {
private static final String VALID_TAG_2 = "neighbour";
private static final String WHITESPACE = " \t\r\n";
+ private static final String INVALID_BLANK_MONTH = "m/";
+ private static final String INVALID_CHARACTERS_MONTH = "a";
+ private static final String INVALID_ZERO_MONTH = "0";
+ private static final String INVALID_NEGATIVE_MONTH = "-1";
+ private static final String INVALID_MONTH_OVER_12 = "13";
@Test
public void parseIndex_invalidInput_throwsParseException() {
@@ -193,4 +198,34 @@ public void parseTags_collectionWithValidTags_returnsTagSet() throws Exception {
assertEquals(expectedTagSet, actualTagSet);
}
+
+ @Test
+ public void parseBirthday_success() throws ParseException {
+ assertEquals(2, ParserUtil.parseMonth("2"));
+ }
+
+ @Test
+ public void parseBirthday_noMonthProvided() throws ParseException {
+ assertThrows(ParseException.class, () -> ParserUtil.parseMonth(INVALID_BLANK_MONTH));
+ }
+
+ @Test
+ public void parseBirthday_charProvided() throws ParseException {
+ assertThrows(ParseException.class, () -> ParserUtil.parseMonth(INVALID_CHARACTERS_MONTH));
+ }
+
+ @Test
+ public void parseBirthday_zeroMonthProvided() throws ParseException {
+ assertThrows(ParseException.class, () -> ParserUtil.parseMonth(INVALID_ZERO_MONTH));
+ }
+
+ @Test
+ public void parseBirthday_negativeMonthProvided() throws ParseException {
+ assertThrows(ParseException.class, () -> ParserUtil.parseMonth(INVALID_NEGATIVE_MONTH));
+ }
+
+ @Test
+ public void parseBirthday_monthOverProvided() throws ParseException {
+ assertThrows(ParseException.class, () -> ParserUtil.parseMonth(INVALID_MONTH_OVER_12));
+ }
}
diff --git a/src/test/java/seedu/address/logic/parser/SortCommandParserTest.java b/src/test/java/seedu/address/logic/parser/SortCommandParserTest.java
new file mode 100644
index 00000000000..b066a3c3ddb
--- /dev/null
+++ b/src/test/java/seedu/address/logic/parser/SortCommandParserTest.java
@@ -0,0 +1,28 @@
+package seedu.address.logic.parser;
+
+import static seedu.address.logic.Messages.MESSAGE_INVALID_COMMAND_FORMAT;
+import static seedu.address.logic.parser.CommandParserTestUtil.assertParseFailure;
+import static seedu.address.logic.parser.CommandParserTestUtil.assertParseSuccess;
+
+import org.junit.jupiter.api.Test;
+
+import seedu.address.logic.commands.SortCommand;
+import seedu.address.logic.parser.exceptions.ParseException;
+
+class SortCommandParserTest {
+
+ private SortCommandParser parser = new SortCommandParser();
+
+ @Test
+ void parse_success() throws ParseException {
+ assertParseSuccess(parser, " name", new SortCommand("name"));
+ assertParseSuccess(parser, " name desc", new SortCommand("name", true));
+ }
+
+ @Test
+ void parse_failure() {
+ assertParseFailure(parser, " ", String.format(MESSAGE_INVALID_COMMAND_FORMAT, SortCommand.MESSAGE_USAGE));
+ assertParseFailure(parser, " a b c ", String.format(MESSAGE_INVALID_COMMAND_FORMAT, SortCommand.MESSAGE_USAGE));
+ assertParseFailure(parser, " a b ", String.format(MESSAGE_INVALID_COMMAND_FORMAT, SortCommand.MESSAGE_USAGE));
+ }
+}
diff --git a/src/test/java/seedu/address/logic/parser/ThemeCommandParserTest.java b/src/test/java/seedu/address/logic/parser/ThemeCommandParserTest.java
new file mode 100644
index 00000000000..f99d06f13eb
--- /dev/null
+++ b/src/test/java/seedu/address/logic/parser/ThemeCommandParserTest.java
@@ -0,0 +1,41 @@
+package seedu.address.logic.parser;
+
+import static seedu.address.logic.commands.CommandTestUtil.assertCommandSuccess;
+import static seedu.address.logic.parser.CommandParserTestUtil.assertParseFailure;
+
+import org.junit.jupiter.api.Test;
+
+import seedu.address.logic.Messages;
+import seedu.address.logic.commands.ThemeCommand;
+import seedu.address.logic.parser.exceptions.ParseException;
+import seedu.address.model.Model;
+import seedu.address.model.ModelManager;
+
+
+class ThemeCommandParserTest {
+
+ private Model model = new ModelManager();
+
+ private ThemeCommandParser parser = new ThemeCommandParser();
+
+ @Test
+ public void execute_success() throws ParseException {
+ assertCommandSuccess(parser.parse("red"), model,
+ ThemeCommand.MESSAGE_THEME_CHANGE_ACKNOWLEDGEMENT, model);
+ assertCommandSuccess(parser.parse("green"), model,
+ ThemeCommand.MESSAGE_THEME_CHANGE_ACKNOWLEDGEMENT, model);
+ assertCommandSuccess(parser.parse("blue"), model,
+ ThemeCommand.MESSAGE_THEME_CHANGE_ACKNOWLEDGEMENT, model);
+ assertCommandSuccess(parser.parse("light"), model,
+ ThemeCommand.MESSAGE_THEME_CHANGE_ACKNOWLEDGEMENT, model);
+ assertCommandSuccess(parser.parse("dark"), model,
+ ThemeCommand.MESSAGE_THEME_CHANGE_ACKNOWLEDGEMENT, model);
+ }
+
+ @Test
+ public void execute_failure() throws ParseException {
+ assertParseFailure(parser, " abc", Messages.MESSAGE_INVALID_THEME);
+ assertParseFailure(parser, "", Messages.MESSAGE_INVALID_THEME);
+ assertParseFailure(parser, " ", Messages.MESSAGE_INVALID_THEME);
+ }
+}
diff --git a/src/test/java/seedu/address/logic/parser/ViewCommandParserTest.java b/src/test/java/seedu/address/logic/parser/ViewCommandParserTest.java
new file mode 100644
index 00000000000..e288255dcb1
--- /dev/null
+++ b/src/test/java/seedu/address/logic/parser/ViewCommandParserTest.java
@@ -0,0 +1,121 @@
+package seedu.address.logic.parser;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotEquals;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_NAME;
+import static seedu.address.logic.parser.CommandParserTestUtil.assertParseSuccess;
+import static seedu.address.logic.parser.ViewCommandParser.CLAIM_BUDGET;
+import static seedu.address.logic.parser.ViewCommandParser.DEPARTMENT;
+import static seedu.address.logic.parser.ViewCommandParser.SALARY_IDENTIFIER;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+
+import org.junit.jupiter.api.Test;
+
+import seedu.address.commons.core.index.Index;
+import seedu.address.logic.commands.ViewCommand;
+import seedu.address.logic.parser.exceptions.ParseException;
+
+public class ViewCommandParserTest {
+ private ViewCommandParser parser = new ViewCommandParser();
+
+ @Test
+ public void parse_withClaim_presentReturnsSuccess() throws ParseException {
+ String userInput = " b/1";
+ Index index = ParserUtil.parseIndex("1");
+ List indexOne = new ArrayList<>(List.of(index));
+ HashMap> successHashMap = new HashMap<>();
+ successHashMap.put(CLAIM_BUDGET, indexOne);
+ ViewCommand successCommand = new ViewCommand(successHashMap);
+ assertParseSuccess(parser, userInput, successCommand);
+ }
+
+ @Test
+ public void parse_withDepartment_presentReturnsSuccess() throws ParseException {
+ String userInput = " d/2";
+ Index index = ParserUtil.parseIndex("2");
+ List indexTwo = new ArrayList<>(List.of(index));
+ HashMap> successHashMap = new HashMap<>();
+ successHashMap.put(DEPARTMENT, indexTwo);
+ ViewCommand successCommand = new ViewCommand(successHashMap);
+ assertParseSuccess(parser, userInput, successCommand);
+ }
+
+ @Test
+ public void parse_withSalary_presentReturnsSuccess() throws ParseException {
+ String userInput = " s/4";
+ Index index = ParserUtil.parseIndex("4");
+ List indexTwo = new ArrayList<>(List.of(index));
+ HashMap> successHashMap = new HashMap<>();
+ successHashMap.put(SALARY_IDENTIFIER, indexTwo);
+ ViewCommand successCommand = new ViewCommand(successHashMap);
+ assertParseSuccess(parser, userInput, successCommand);
+ }
+
+ @Test
+ public void parse_delimiter_success() {
+ List testInputArr = new ArrayList<>(List.of("A,B,C"));
+ List expectedParseArr = new ArrayList<>(List.of("A", "B", "C"));
+ List producedArr = parser.parseView(testInputArr);
+ assertEquals(expectedParseArr, producedArr);
+ }
+
+ @Test
+ public void parse_delimiter_failure() {
+ List testInputArr = new ArrayList(List.of("A,B,C"));
+ List expectedParseArr = new ArrayList(List.of("A", "BC"));
+ List producedArr = parser.parseView(testInputArr);
+ assertNotEquals(expectedParseArr, producedArr);
+ }
+
+ @Test
+ public void parse_relevantIndex_success() throws ParseException {
+ String userInput = " n/1,2";
+ ArgumentMultimap argMultimap = ArgumentTokenizer.viewTokenize(userInput);
+ Index indexOne = ParserUtil.parseIndex("1");
+ Index indexTwo = ParserUtil.parseIndex("2");
+ List expectedIndexArr = new ArrayList<>(List.of(indexOne, indexTwo));
+ List producedIndexArr = parser.relevantIndexes(argMultimap, PREFIX_NAME);
+ assertEquals(expectedIndexArr, producedIndexArr);
+ }
+
+ @Test
+ public void parse_relevantIndex_failure() throws ParseException {
+ String userInput = " n/1,2";
+ ArgumentMultimap argMultimap = ArgumentTokenizer.viewTokenize(userInput);
+ Index indexOne = ParserUtil.parseIndex("1");
+ List expectedIndexArr = new ArrayList<>(List.of(indexOne));
+ List producedIndexArr = parser.relevantIndexes(argMultimap, PREFIX_NAME);
+ assertNotEquals(expectedIndexArr, producedIndexArr);
+ }
+
+ @Test
+ public void parse_emptyparams_success() throws ParseException {
+ String userInput = " ";
+ ArgumentMultimap argMultimap = ArgumentTokenizer.viewTokenize(userInput);
+ assertThrows(ParseException.class, () -> {
+ parser.validatePrefixes(argMultimap);
+ });
+ }
+
+ @Test
+ public void parse_excessparams_success() throws ParseException {
+ String userInput = " n/1,2 q/1,2";
+ ArgumentMultimap argMultimap = ArgumentTokenizer.viewTokenize(userInput);
+ assertThrows(ParseException.class, () -> {
+ parser.validatePrefixes(argMultimap);
+ });
+ }
+
+ @Test
+ public void parse_wrongparams_success() throws ParseException {
+ String userInput = " q/1";
+ ArgumentMultimap argMultimap = ArgumentTokenizer.viewTokenize(userInput);
+ assertThrows(ParseException.class, () -> {
+ parser.validatePrefixes(argMultimap);
+ });
+ }
+}
diff --git a/src/test/java/seedu/address/logic/parser/ViewLeaveCommandParserTest.java b/src/test/java/seedu/address/logic/parser/ViewLeaveCommandParserTest.java
new file mode 100644
index 00000000000..b87ae0d4484
--- /dev/null
+++ b/src/test/java/seedu/address/logic/parser/ViewLeaveCommandParserTest.java
@@ -0,0 +1,74 @@
+package seedu.address.logic.parser;
+
+import static seedu.address.logic.commands.CommandTestUtil.VALID_DEPARTMENT;
+import static seedu.address.logic.commands.CommandTestUtil.assertCommandSuccess;
+import static seedu.address.logic.parser.CommandParserTestUtil.assertParseFailure;
+import static seedu.address.testutil.TypicalPersons.getTypicalAddressBook;
+
+import java.util.function.Predicate;
+
+import org.junit.jupiter.api.Test;
+
+import seedu.address.logic.Messages;
+import seedu.address.logic.parser.exceptions.ParseException;
+import seedu.address.model.Model;
+import seedu.address.model.ModelManager;
+import seedu.address.model.UserPrefs;
+import seedu.address.model.person.Department;
+import seedu.address.model.person.HasLeaveAnyMonthPredicate;
+import seedu.address.model.person.HasLeaveThisMonthPredicate;
+import seedu.address.model.person.MatchingDepartmentPredicate;
+import seedu.address.model.person.Person;
+
+class ViewLeaveCommandParserTest {
+
+ private Model model = new ModelManager(getTypicalAddressBook(), new UserPrefs());
+
+ private ViewLeaveCommandParser parser = new ViewLeaveCommandParser();
+
+ @Test
+ void parse_success() throws ParseException {
+ Predicate testPredicateAnyMonth = new HasLeaveAnyMonthPredicate();
+ Predicate testPredicateMonth = new HasLeaveThisMonthPredicate("3");
+ Predicate testPredicateMonths = testPredicateMonth.and(new HasLeaveThisMonthPredicate("1"));
+ Predicate testPredicateMonthAndDept = testPredicateMonth.and(
+ new MatchingDepartmentPredicate(new Department(VALID_DEPARTMENT)));
+
+ Model expectedModel1 = new ModelManager(getTypicalAddressBook(), new UserPrefs());
+ expectedModel1.updateFilteredPersonList(testPredicateAnyMonth);
+ assertCommandSuccess(parser.parse(""), model, String.format(Messages.MESSAGE_VIEW_LEAVE_SUCCESS,
+ expectedModel1.getFilteredPersonList().size()), expectedModel1);
+
+ Model expectedModel2 = new ModelManager(getTypicalAddressBook(), new UserPrefs());
+ expectedModel2.updateFilteredPersonList(testPredicateMonth);
+ assertCommandSuccess(parser.parse(" m/3"), model, String.format(Messages.MESSAGE_VIEW_LEAVE_SUCCESS,
+ expectedModel2.getFilteredPersonList().size()), expectedModel2);
+
+ Model expectedModel3 = new ModelManager(getTypicalAddressBook(), new UserPrefs());
+ expectedModel3.updateFilteredPersonList(testPredicateMonths);
+ assertCommandSuccess(parser.parse(" m/1,3"), model, String.format(Messages.MESSAGE_VIEW_LEAVE_SUCCESS,
+ expectedModel3.getFilteredPersonList().size()), expectedModel3);
+
+ Model expectedModel4 = new ModelManager(getTypicalAddressBook(), new UserPrefs());
+ expectedModel4.updateFilteredPersonList(testPredicateMonthAndDept);
+ assertCommandSuccess(parser.parse(" m/1 d/Engineering"), model,
+ String.format(Messages.MESSAGE_VIEW_LEAVE_SUCCESS,
+ expectedModel4.getFilteredPersonList().size()), expectedModel4);
+ }
+
+ @Test
+ void parse_failure() {
+ assertParseFailure(parser, " d/Engineering d/engineering",
+ Messages.getErrorMessageForDuplicatePrefixes(new Prefix("d/")));
+ assertParseFailure(parser, " m/1 m/5",
+ Messages.getErrorMessageForDuplicatePrefixes(new Prefix("m/")));
+ assertParseFailure(parser, " abc", String.format(Messages.MESSAGE_INVALID_COMMAND_FORMAT,
+ Messages.MESSAGE_VIEW_LIST_COMMAND_FORMAT));
+ assertParseFailure(parser, " m/1, 4", Messages.MESSAGE_MONTHS_SPACES_DETECTED
+ + Messages.MESSAGE_VIEW_LIST_COMMAND_FORMAT);
+ assertParseFailure(parser, " m/15", Messages.MESSAGE_INVALID_MONTH);
+ assertParseFailure(parser, " d/", Messages.MESSAGE_EMPTY_DEPARTMENT_FILTER);
+ assertParseFailure(parser, " m/", Messages.MESSAGE_EMPTY_MONTH_LEAVE_FILTER);
+ assertParseFailure(parser, " m/1.5", Messages.MESSAGE_INVALID_MONTH);
+ }
+}
diff --git a/src/test/java/seedu/address/model/AddressBookListTest.java b/src/test/java/seedu/address/model/AddressBookListTest.java
new file mode 100644
index 00000000000..deba5311501
--- /dev/null
+++ b/src/test/java/seedu/address/model/AddressBookListTest.java
@@ -0,0 +1,40 @@
+package seedu.address.model;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+
+import org.junit.jupiter.api.Test;
+
+public class AddressBookListTest {
+ private final AddressBookList addressBookList = new AddressBookList();
+
+ @Test
+ public void undo_success() {
+ addressBookList.add(new AddressBook());
+ addressBookList.addCommandText("");
+ addressBookList.add(new AddressBook());
+ addressBookList.addCommandText("");
+ assertEquals(addressBookList.undo(), new AddressBook());
+ addressBookList.add(new AddressBook());
+ addressBookList.addCommandText("");
+ assertEquals(addressBookList.undo(), new AddressBook());
+ }
+
+ @Test
+ public void undo_failure() {
+ assertThrows(IllegalArgumentException.class, () -> addressBookList.undo());
+ }
+
+ @Test
+ public void redo_success() {
+ addressBookList.add(new AddressBook());
+ addressBookList.add(new AddressBook());
+ addressBookList.undo();
+ assertEquals(addressBookList.redo(), new AddressBook());
+ }
+
+ @Test
+ public void redo_failure() {
+ assertThrows(IllegalArgumentException.class, () -> addressBookList.redo());
+ }
+}
diff --git a/src/test/java/seedu/address/model/AddressBookTest.java b/src/test/java/seedu/address/model/AddressBookTest.java
index 68c8c5ba4d5..fceb14f3608 100644
--- a/src/test/java/seedu/address/model/AddressBookTest.java
+++ b/src/test/java/seedu/address/model/AddressBookTest.java
@@ -9,17 +9,14 @@
import static seedu.address.testutil.TypicalPersons.ALICE;
import static seedu.address.testutil.TypicalPersons.getTypicalAddressBook;
-import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
-import java.util.List;
import org.junit.jupiter.api.Test;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import seedu.address.model.person.Person;
-import seedu.address.model.person.exceptions.DuplicatePersonException;
import seedu.address.testutil.PersonBuilder;
public class AddressBookTest {
@@ -43,17 +40,6 @@ public void resetData_withValidReadOnlyAddressBook_replacesData() {
assertEquals(newData, addressBook);
}
- @Test
- public void resetData_withDuplicatePersons_throwsDuplicatePersonException() {
- // Two persons with the same identity fields
- Person editedAlice = new PersonBuilder(ALICE).withAddress(VALID_ADDRESS_BOB).withTags(VALID_TAG_HUSBAND)
- .build();
- List newPersons = Arrays.asList(ALICE, editedAlice);
- AddressBookStub newData = new AddressBookStub(newPersons);
-
- assertThrows(DuplicatePersonException.class, () -> addressBook.resetData(newData));
- }
-
@Test
public void hasPerson_nullPerson_throwsNullPointerException() {
assertThrows(NullPointerException.class, () -> addressBook.hasPerson(null));
@@ -75,7 +61,7 @@ public void hasPerson_personWithSameIdentityFieldsInAddressBook_returnsTrue() {
addressBook.addPerson(ALICE);
Person editedAlice = new PersonBuilder(ALICE).withAddress(VALID_ADDRESS_BOB).withTags(VALID_TAG_HUSBAND)
.build();
- assertTrue(addressBook.hasPerson(editedAlice));
+ assertFalse(addressBook.hasPerson(editedAlice));
}
@Test
diff --git a/src/test/java/seedu/address/model/ModelManagerTest.java b/src/test/java/seedu/address/model/ModelManagerTest.java
index 2cf1418d116..53527b0fbde 100644
--- a/src/test/java/seedu/address/model/ModelManagerTest.java
+++ b/src/test/java/seedu/address/model/ModelManagerTest.java
@@ -85,7 +85,10 @@ public void hasPerson_personNotInAddressBook_returnsFalse() {
@Test
public void hasPerson_personInAddressBook_returnsTrue() {
modelManager.addPerson(ALICE);
+ modelManager.addCommandText("add Alice");
assertTrue(modelManager.hasPerson(ALICE));
+ assertTrue(modelManager.undo().equals("add Alice"));
+ assertTrue(modelManager.redo().equals("add Alice"));
}
@Test
diff --git a/src/test/java/seedu/address/model/person/AddressTest.java b/src/test/java/seedu/address/model/person/AddressTest.java
index 314885eca26..2caee8745b6 100644
--- a/src/test/java/seedu/address/model/person/AddressTest.java
+++ b/src/test/java/seedu/address/model/person/AddressTest.java
@@ -1,5 +1,6 @@
package seedu.address.model.person;
+import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static seedu.address.testutil.Assert.assertThrows;
@@ -53,4 +54,11 @@ public void equals() {
// different values -> returns false
assertFalse(address.equals(new Address("Other Valid Address")));
}
+
+ @Test
+ public void compareTo() {
+ assertEquals(new Address("asdf").compareTo(new Address("bcda")), -1);
+ assertEquals(new Address("asdf").compareTo(new Address("asdf")), 0);
+ assertEquals(new Address("c").compareTo(new Address("bcda")), 1);
+ }
}
diff --git a/src/test/java/seedu/address/model/person/EmailTest.java b/src/test/java/seedu/address/model/person/EmailTest.java
index f08cdff0a64..b2594128674 100644
--- a/src/test/java/seedu/address/model/person/EmailTest.java
+++ b/src/test/java/seedu/address/model/person/EmailTest.java
@@ -1,5 +1,6 @@
package seedu.address.model.person;
+import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static seedu.address.testutil.Assert.assertThrows;
@@ -35,7 +36,6 @@ public void isValidEmail() {
// invalid parts
assertFalse(Email.isValidEmail("peterjack@-")); // invalid domain name
- assertFalse(Email.isValidEmail("peterjack@exam_ple.com")); // underscore in domain name
assertFalse(Email.isValidEmail("peter jack@example.com")); // spaces in local part
assertFalse(Email.isValidEmail("peterjack@exam ple.com")); // spaces in domain name
assertFalse(Email.isValidEmail(" peterjack@example.com")); // leading space
@@ -51,27 +51,37 @@ public void isValidEmail() {
assertFalse(Email.isValidEmail("peterjack@-example.com")); // domain name starts with a hyphen
assertFalse(Email.isValidEmail("peterjack@example.com-")); // domain name ends with a hyphen
assertFalse(Email.isValidEmail("peterjack@example.c")); // top level domain has less than two chars
+ assertFalse(Email.isValidEmail("test@localhost")); // alphabets only
+ assertFalse(Email.isValidEmail("123@145")); // numeric domain name
+ assertFalse(Email.isValidEmail("asdf@gmail..com"));
+ assertFalse(Email.isValidEmail("asdf@gmail.co2"));
+ assertFalse(Email.isValidEmail("asdf@gmail_-s.com"));
+ assertFalse(Email.isValidEmail("as._df@x.com"));
+ assertFalse(Email.isValidEmail("asdf@asdf.asdfasdfsdf"));
// valid email
assertTrue(Email.isValidEmail("PeterJack_1190@example.com")); // underscore in local part
assertTrue(Email.isValidEmail("PeterJack.1190@example.com")); // period in local part
assertTrue(Email.isValidEmail("PeterJack+1190@example.com")); // '+' symbol in local part
assertTrue(Email.isValidEmail("PeterJack-1190@example.com")); // hyphen in local part
- assertTrue(Email.isValidEmail("a@bc")); // minimal
- assertTrue(Email.isValidEmail("test@localhost")); // alphabets only
- assertTrue(Email.isValidEmail("123@145")); // numeric local part and domain name
- assertTrue(Email.isValidEmail("a1+be.d@example1.com")); // mixture of alphanumeric and special characters
- assertTrue(Email.isValidEmail("peter_jack@very-very-very-long-example.com")); // long domain name
assertTrue(Email.isValidEmail("if.you.dream.it_you.can.do.it@example.com")); // long local part
assertTrue(Email.isValidEmail("e1234567@u.nus.edu")); // more than one period in domain
+ assertTrue(Email.isValidEmail("peterjack@exam_ple.com")); // underscore in domain name
+ assertTrue(Email.isValidEmail("a1+be.d@example1.com")); // mixture of alphanumeric and special characters
+ assertTrue(Email.isValidEmail("peter_jack@very-very-very-long-example.com")); // domain name has symbols
+ assertTrue(Email.isValidEmail("asdf@asdf2.ai"));
+ assertTrue(Email.isValidEmail("asdf@mercedes-benz.com"));
+ assertTrue(Email.isValidEmail("asdf@mercedes_benz.com"));
+ assertTrue(Email.isValidEmail("asdf_asdfasdf-asdfsFF@mercedes-benz_ccenz.com"));
+ assertTrue(Email.isValidEmail("gg@gg+asedf_google.com"));
}
@Test
public void equals() {
- Email email = new Email("valid@email");
+ Email email = new Email("valid@email.com");
// same values -> returns true
- assertTrue(email.equals(new Email("valid@email")));
+ assertTrue(email.equals(new Email("valid@email.com")));
// same object -> returns true
assertTrue(email.equals(email));
@@ -83,6 +93,13 @@ public void equals() {
assertFalse(email.equals(5.0f));
// different values -> returns false
- assertFalse(email.equals(new Email("other.valid@email")));
+ assertFalse(email.equals(new Email("other.valid@email.com")));
+ }
+
+ @Test
+ public void compareTo() {
+ assertEquals(new Email("asdf@gmail.com").compareTo(new Email("bcda@gmail.com")), -1);
+ assertEquals(new Email("asdf@gmail.com").compareTo(new Email("asdf@gmail.com")), 0);
+ assertEquals(new Email("c@gmail.com").compareTo(new Email("bcda@gmail.com")), 1);
}
}
diff --git a/src/test/java/seedu/address/model/person/HasLeaveAnyMonthPredicateTest.java b/src/test/java/seedu/address/model/person/HasLeaveAnyMonthPredicateTest.java
new file mode 100644
index 00000000000..100bee2cf68
--- /dev/null
+++ b/src/test/java/seedu/address/model/person/HasLeaveAnyMonthPredicateTest.java
@@ -0,0 +1,36 @@
+package seedu.address.model.person;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertNotEquals;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import org.junit.jupiter.api.Test;
+
+import seedu.address.testutil.PersonBuilder;
+
+class HasLeaveAnyMonthPredicateTest {
+
+ @Test
+ void test_hasLeaveAnyMonth_returnsTrue() {
+ HasLeaveAnyMonthPredicate predicate1 = new HasLeaveAnyMonthPredicate();
+ assertTrue(predicate1.test(new PersonBuilder().withLeave("111001000011").build()));
+ }
+
+ @Test
+ void test_hasLeaveAnyMonth_returnsFalse() {
+ HasLeaveAnyMonthPredicate predicate1 = new HasLeaveAnyMonthPredicate();
+ assertFalse(predicate1.test(new PersonBuilder().withLeave("000000000000").build()));
+ }
+
+ @Test
+ void testEquals() {
+ HasLeaveAnyMonthPredicate predicate1 = new HasLeaveAnyMonthPredicate();
+ HasLeaveAnyMonthPredicate predicate2 = new HasLeaveAnyMonthPredicate();
+
+ assertEquals(predicate1, predicate1);
+ assertEquals(predicate1, predicate2);
+ assertNotEquals(predicate1, null);
+ assertNotEquals(predicate1, 1);
+ }
+}
diff --git a/src/test/java/seedu/address/model/person/HasLeaveThisMonthPredicateTest.java b/src/test/java/seedu/address/model/person/HasLeaveThisMonthPredicateTest.java
new file mode 100644
index 00000000000..e12ee0e79a3
--- /dev/null
+++ b/src/test/java/seedu/address/model/person/HasLeaveThisMonthPredicateTest.java
@@ -0,0 +1,37 @@
+package seedu.address.model.person;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertNotEquals;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import org.junit.jupiter.api.Test;
+
+import seedu.address.testutil.PersonBuilder;
+
+class HasLeaveThisMonthPredicateTest {
+
+ @Test
+ void test_hasLeaveThisMonth_returnsTrue() {
+ HasLeaveThisMonthPredicate predicate1 = new HasLeaveThisMonthPredicate("1");
+ assertTrue(predicate1.test(new PersonBuilder().withLeave("111001000011").build()));
+ }
+
+ @Test
+ void test_hasLeaveThisMonth_returnsFalse() {
+ HasLeaveThisMonthPredicate predicate1 = new HasLeaveThisMonthPredicate("4");
+ assertFalse(predicate1.test(new PersonBuilder().withLeave("111001000011").build()));
+ }
+
+ @Test
+ void testEquals() {
+ HasLeaveThisMonthPredicate predicate1 = new HasLeaveThisMonthPredicate("1");
+ HasLeaveThisMonthPredicate predicate2 = new HasLeaveThisMonthPredicate("1");
+ HasLeaveThisMonthPredicate predicate3 = new HasLeaveThisMonthPredicate("12");
+ assertEquals(predicate1, predicate1);
+ assertEquals(predicate1, predicate2);
+ assertNotEquals(predicate1, predicate3);
+ assertNotEquals(predicate1, null);
+ assertNotEquals(predicate1, 1);
+ }
+}
diff --git a/src/test/java/seedu/address/model/person/LeaveTest.java b/src/test/java/seedu/address/model/person/LeaveTest.java
new file mode 100644
index 00000000000..bdbae3279c1
--- /dev/null
+++ b/src/test/java/seedu/address/model/person/LeaveTest.java
@@ -0,0 +1,33 @@
+package seedu.address.model.person;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static seedu.address.logic.commands.CommandTestUtil.INVALID_LEAVE;
+import static seedu.address.logic.commands.CommandTestUtil.VALID_LEAVE;
+import static seedu.address.testutil.TypicalPersons.ALICE;
+import static seedu.address.testutil.TypicalPersons.BOB;
+
+import org.junit.jupiter.api.Test;
+
+public class LeaveTest {
+
+ @Test
+ public void validLeave() {
+ assertEquals(new Leave().leave, Leave.NO_LEAVE);
+ assertTrue(Leave.isValidLeave(ALICE.getLeave().leave));
+ assertTrue(Leave.isValidLeave(BOB.getLeave().leave));
+ assertTrue(Leave.isValidLeave(VALID_LEAVE));
+ assertFalse(Leave.isValidLeave(INVALID_LEAVE));
+ assertFalse(Leave.isValidLeave("111101010102"));
+ assertEquals(new Leave().toString(), "-");
+ assertEquals(ALICE.getLeave().leave, "111101010101");
+ assertEquals(ALICE.getLeave().toString(), "Jan, Feb, Mar, Apr, Jun, Aug, Oct, Dec");
+ assertEquals(new Leave().update("++++0+0+0+0+"), ALICE.getLeave());
+ assertEquals(new Leave(), ALICE.getLeave().update("----0-0-0-0-"));
+ assertEquals(ALICE.getLeave(), ALICE.getLeave());
+ assertFalse(ALICE.getLeave().equals(null));
+ assertFalse(ALICE.getLeave().equals(new Leave()));
+ assertEquals(ALICE.getLeave().hashCode(), ALICE.getLeave().leave.hashCode());
+ }
+}
diff --git a/src/test/java/seedu/address/model/person/MatchingBirthdayPredicateTest.java b/src/test/java/seedu/address/model/person/MatchingBirthdayPredicateTest.java
new file mode 100644
index 00000000000..51495edcb33
--- /dev/null
+++ b/src/test/java/seedu/address/model/person/MatchingBirthdayPredicateTest.java
@@ -0,0 +1,86 @@
+package seedu.address.model.person;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertNotEquals;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.Test;
+
+import seedu.address.testutil.PersonBuilder;
+
+
+public class MatchingBirthdayPredicateTest {
+ private static Person subject;
+
+ @BeforeAll
+ static void setup() {
+ subject = new PersonBuilder().build();
+ }
+
+ @Test
+ public void predicate_success() {
+ List monthList = new ArrayList<>();
+ monthList.add(new Month(1));
+ MatchingBirthdayPredicate predicate1 = new MatchingBirthdayPredicate(monthList);
+ assertTrue(predicate1.test(subject));
+ }
+
+ @Test
+ public void predicate_failure() {
+ List monthList = new ArrayList<>();
+ monthList.add(new Month(2));
+ MatchingBirthdayPredicate predicate2 = new MatchingBirthdayPredicate(monthList);
+ assertFalse(predicate2.test(subject));
+ }
+
+ @Test
+ public void equalsItself_success() {
+ List monthList = new ArrayList<>();
+ monthList.add(new Month(2));
+ MatchingBirthdayPredicate predicate1 = new MatchingBirthdayPredicate(monthList);
+ assertEquals(predicate1, predicate1);
+ }
+
+ @Test
+ public void equalsList_success() {
+ List monthList1 = new ArrayList<>();
+ monthList1.add(new Month(2));
+ monthList1.add(new Month(3));
+ MatchingBirthdayPredicate predicate1 = new MatchingBirthdayPredicate(monthList1);
+
+ List monthList2 = new ArrayList<>();
+ monthList2.add(new Month(2));
+ monthList2.add(new Month(3));
+ MatchingBirthdayPredicate predicate2 = new MatchingBirthdayPredicate(monthList2);
+ assertEquals(predicate1, predicate2);
+ }
+
+ @Test
+ public void equalList_failure() {
+ List monthList1 = new ArrayList<>();
+ monthList1.add(new Month(2));
+ monthList1.add(new Month(3));
+ MatchingBirthdayPredicate predicate1 = new MatchingBirthdayPredicate(monthList1);
+
+ List monthList2 = new ArrayList<>();
+ monthList2.add(new Month(2));
+ monthList2.add(new Month(4));
+ MatchingBirthdayPredicate predicate2 = new MatchingBirthdayPredicate(monthList2);
+ assertNotEquals(predicate1, predicate2);
+ }
+
+ @Test
+ public void stringRep_success() {
+ List monthList1 = new ArrayList<>();
+ monthList1.add(new Month(2));
+ monthList1.add(new Month(3));
+ MatchingBirthdayPredicate predicate1 = new MatchingBirthdayPredicate(monthList1);
+
+ assertEquals(predicate1.toString(), "Feb,Mar");
+ }
+}
diff --git a/src/test/java/seedu/address/model/person/MatchingDepartmentPredicateTest.java b/src/test/java/seedu/address/model/person/MatchingDepartmentPredicateTest.java
new file mode 100644
index 00000000000..17528757c49
--- /dev/null
+++ b/src/test/java/seedu/address/model/person/MatchingDepartmentPredicateTest.java
@@ -0,0 +1,28 @@
+package seedu.address.model.person;
+
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import org.junit.jupiter.api.Test;
+
+import seedu.address.testutil.PersonBuilder;
+
+class MatchingDepartmentPredicateTest {
+
+ @Test
+ void test_matchingDepartment_returnsTrue() {
+ Department department1 = new Department("HR");
+ Department department2 = new Department("hr");
+ MatchingDepartmentPredicate predicate1 = new MatchingDepartmentPredicate(department1);
+ MatchingDepartmentPredicate predicate2 = new MatchingDepartmentPredicate(department2);
+ assertTrue(predicate1.test(new PersonBuilder().withDepartment("HR").build()));
+ assertTrue(predicate2.test(new PersonBuilder().withDepartment("HR").build()));
+ }
+
+ @Test
+ void test_matchingDepartment_returnsFalse() {
+ Department department1 = new Department("HR");
+ MatchingDepartmentPredicate predicate1 = new MatchingDepartmentPredicate(department1);
+ assertFalse(predicate1.test(new PersonBuilder().withDepartment("IT").build()));
+ }
+}
diff --git a/src/test/java/seedu/address/model/person/MonthTest.java b/src/test/java/seedu/address/model/person/MonthTest.java
new file mode 100644
index 00000000000..65e91465f76
--- /dev/null
+++ b/src/test/java/seedu/address/model/person/MonthTest.java
@@ -0,0 +1,84 @@
+package seedu.address.model.person;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertNotEquals;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import org.junit.jupiter.api.Test;
+public class MonthTest {
+ public static final String MONTH_WITH_CHARACTERS = "1a";
+ public static final String MONTH_WITH_DECIMALS = "1.00";
+ public static final String ZERO_MONTH = "0";
+ public static final String NEGATIVE_MONTH = "-1";
+ public static final String INVALID_MONTH = "13";
+ public static final String VALID_MONTH = "1";
+
+ @Test
+ public void execute_monthEqualsSuccess() {
+ Month testMonth = new Month(1);
+ assertEquals(new Month(Integer.parseInt(VALID_MONTH)), testMonth);
+ assertEquals(testMonth, testMonth);
+ assertNotEquals(testMonth, null);
+ }
+
+ @Test
+ public void execute_monthFailure() {
+ assertThrows(Exception.class, () -> new Month(0));
+ assertThrows(Exception.class, () -> new Month(13));
+ assertThrows(Exception.class, () -> new Month(100000));
+ }
+
+ @Test
+ public void execute_containsAlphabetsSuccess() {
+ assertTrue(Month.containsAlphabetsOrDecimals(MONTH_WITH_CHARACTERS));
+ }
+
+ @Test
+ public void execute_containsDecimalsSuccess() {
+ assertTrue(Month.containsAlphabetsOrDecimals(MONTH_WITH_DECIMALS));
+ }
+
+ @Test
+ public void execute_doesNotContainsAlphabetsOrDecimalsSuccess() {
+ assertFalse(Month.containsAlphabetsOrDecimals(VALID_MONTH));
+ }
+
+ @Test
+ public void execute_containsZeroSuccess() {
+ assertTrue(Month.isZeroMonth(ZERO_MONTH));
+ }
+
+ @Test
+ public void execute_doesNotContainsZeroSuccess() {
+ assertFalse(Month.isZeroMonth(VALID_MONTH));
+ }
+
+ @Test
+ public void execute_containsNegativeSuccess() {
+ assertTrue(Month.isNegativeMonth(NEGATIVE_MONTH));
+ }
+
+ @Test
+ public void execute_doesNotContainsNegativeSuccess() {
+ assertFalse(Month.isNegativeMonth(VALID_MONTH));
+ }
+
+ @Test
+ public void execute_invalidMonth() {
+ assertFalse(Month.isValidMonth(INVALID_MONTH));
+ assertFalse(Month.isValidMonth(NEGATIVE_MONTH));
+ }
+
+ @Test
+ public void execute_validMonth() {
+ assertTrue(Month.isValidMonth(VALID_MONTH));
+ }
+
+ @Test
+ public void execute_getMonthName() {
+ Month testMonth = new Month(1);
+ assertEquals(testMonth.getMonthName(), "Jan");
+ }
+}
diff --git a/src/test/java/seedu/address/model/person/NameTest.java b/src/test/java/seedu/address/model/person/NameTest.java
index 94e3dd726bd..c97cfc8b676 100644
--- a/src/test/java/seedu/address/model/person/NameTest.java
+++ b/src/test/java/seedu/address/model/person/NameTest.java
@@ -1,5 +1,6 @@
package seedu.address.model.person;
+import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static seedu.address.testutil.Assert.assertThrows;
@@ -27,10 +28,15 @@ public void isValidName() {
// invalid name
assertFalse(Name.isValidName("")); // empty string
assertFalse(Name.isValidName(" ")); // spaces only
- assertFalse(Name.isValidName("^")); // only non-alphanumeric characters
- assertFalse(Name.isValidName("peter*")); // contains non-alphanumeric characters
// valid name
+ assertTrue(Name.isValidName("^"));
+ assertTrue(Name.isValidName("peter*"));
+ assertTrue(Name.isValidName("Muhammad S/O Ali"));
+ assertTrue(Name.isValidName("X AE A-12"));
+ assertTrue(Name.isValidName("Exa Dark Sideræl"));
+ assertTrue(Name.isValidName("$helly"));
+ assertTrue(Name.isValidName("Llanfairpwllgwyngyllgogerychwyrndrobwllllantysiliogogogoch"));
assertTrue(Name.isValidName("peter jack")); // alphabets only
assertTrue(Name.isValidName("12345")); // numbers only
assertTrue(Name.isValidName("peter the 2nd")); // alphanumeric characters
@@ -57,4 +63,11 @@ public void equals() {
// different values -> returns false
assertFalse(name.equals(new Name("Other Valid Name")));
}
+
+ @Test
+ public void compareTo() {
+ assertEquals(new Name("asdf").compareTo(new Name("bcda")), -1);
+ assertEquals(new Name("asdf").compareTo(new Name("asdf")), 0);
+ assertEquals(new Name("c").compareTo(new Name("bcda")), 1);
+ }
}
diff --git a/src/test/java/seedu/address/model/person/PersonTest.java b/src/test/java/seedu/address/model/person/PersonTest.java
index 31a10d156c9..f67aca25484 100644
--- a/src/test/java/seedu/address/model/person/PersonTest.java
+++ b/src/test/java/seedu/address/model/person/PersonTest.java
@@ -8,22 +8,17 @@
import static seedu.address.logic.commands.CommandTestUtil.VALID_NAME_BOB;
import static seedu.address.logic.commands.CommandTestUtil.VALID_PHONE_BOB;
import static seedu.address.logic.commands.CommandTestUtil.VALID_TAG_HUSBAND;
-import static seedu.address.testutil.Assert.assertThrows;
import static seedu.address.testutil.TypicalPersons.ALICE;
import static seedu.address.testutil.TypicalPersons.BOB;
+import java.util.Objects;
+
import org.junit.jupiter.api.Test;
import seedu.address.testutil.PersonBuilder;
public class PersonTest {
- @Test
- public void asObservableList_modifyList_throwsUnsupportedOperationException() {
- Person person = new PersonBuilder().build();
- assertThrows(UnsupportedOperationException.class, () -> person.getTags().remove(0));
- }
-
@Test
public void isSamePerson() {
// same object -> returns true
@@ -32,23 +27,32 @@ public void isSamePerson() {
// null -> returns false
assertFalse(ALICE.isSamePerson(null));
- // same name, all other attributes different -> returns true
Person editedAlice = new PersonBuilder(ALICE).withPhone(VALID_PHONE_BOB).withEmail(VALID_EMAIL_BOB)
.withAddress(VALID_ADDRESS_BOB).withTags(VALID_TAG_HUSBAND).build();
- assertTrue(ALICE.isSamePerson(editedAlice));
+ assertFalse(ALICE.isSamePerson(editedAlice));
// different name, all other attributes same -> returns false
editedAlice = new PersonBuilder(ALICE).withName(VALID_NAME_BOB).build();
assertFalse(ALICE.isSamePerson(editedAlice));
- // name differs in case, all other attributes same -> returns false
+ editedAlice = new PersonBuilder(ALICE).withDob("1000-01-01").build();
+ assertFalse(ALICE.isSamePerson(editedAlice));
+
+ editedAlice = new PersonBuilder(ALICE).withPhone("911").withEmail("ecila@gmail.com").build();
+ assertFalse(ALICE.isSamePerson(editedAlice));
+
+ // name differs in case, all other attributes same -> returns true
Person editedBob = new PersonBuilder(BOB).withName(VALID_NAME_BOB.toLowerCase()).build();
- assertFalse(BOB.isSamePerson(editedBob));
+ assertTrue(BOB.isSamePerson(editedBob));
- // name has trailing spaces, all other attributes same -> returns false
+ // name has trailing spaces, all other attributes same -> returns true
String nameWithTrailingSpaces = VALID_NAME_BOB + " ";
editedBob = new PersonBuilder(BOB).withName(nameWithTrailingSpaces).build();
- assertFalse(BOB.isSamePerson(editedBob));
+ assertTrue(BOB.isSamePerson(editedBob));
+
+ assertEquals(ALICE.hashCode(), Objects.hash(ALICE.getName(), ALICE.getPhone(), ALICE.getEmail(),
+ ALICE.getAddress(), ALICE.getSalary(), ALICE.getClaimBudget(), ALICE.getDepartment(),
+ ALICE.getDob(), ALICE.getLeave()));
}
@Test
@@ -73,27 +77,25 @@ public void equals() {
Person editedAlice = new PersonBuilder(ALICE).withName(VALID_NAME_BOB).build();
assertFalse(ALICE.equals(editedAlice));
- // different phone -> returns false
+ // different phone -> returns true
editedAlice = new PersonBuilder(ALICE).withPhone(VALID_PHONE_BOB).build();
- assertFalse(ALICE.equals(editedAlice));
+ assertTrue(ALICE.equals(editedAlice));
- // different email -> returns false
+ // different email -> returns true
editedAlice = new PersonBuilder(ALICE).withEmail(VALID_EMAIL_BOB).build();
- assertFalse(ALICE.equals(editedAlice));
+ assertTrue(ALICE.equals(editedAlice));
// different address -> returns false
editedAlice = new PersonBuilder(ALICE).withAddress(VALID_ADDRESS_BOB).build();
assertFalse(ALICE.equals(editedAlice));
-
- // different tags -> returns false
- editedAlice = new PersonBuilder(ALICE).withTags(VALID_TAG_HUSBAND).build();
- assertFalse(ALICE.equals(editedAlice));
}
@Test
public void toStringMethod() {
String expected = Person.class.getCanonicalName() + "{name=" + ALICE.getName() + ", phone=" + ALICE.getPhone()
- + ", email=" + ALICE.getEmail() + ", address=" + ALICE.getAddress() + ", tags=" + ALICE.getTags() + "}";
+ + ", email=" + ALICE.getEmail() + ", address=" + ALICE.getAddress() + ", salary=" + ALICE.getSalary()
+ + ", claimBudget=" + ALICE.getClaimBudget() + ", department=" + ALICE.getDepartment()
+ + ", dob=" + ALICE.getDob() + ", leave=" + ALICE.getLeave() + "}";
assertEquals(expected, ALICE.toString());
}
}
diff --git a/src/test/java/seedu/address/model/person/PhoneTest.java b/src/test/java/seedu/address/model/person/PhoneTest.java
index deaaa5ba190..44b3fa1596c 100644
--- a/src/test/java/seedu/address/model/person/PhoneTest.java
+++ b/src/test/java/seedu/address/model/person/PhoneTest.java
@@ -1,5 +1,6 @@
package seedu.address.model.person;
+import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static seedu.address.testutil.Assert.assertThrows;
@@ -31,11 +32,15 @@ public void isValidPhone() {
assertFalse(Phone.isValidPhone("phone")); // non-numeric
assertFalse(Phone.isValidPhone("9011p041")); // alphabets within digits
assertFalse(Phone.isValidPhone("9312 1534")); // spaces within digits
+ assertFalse(Phone.isValidPhone("+"));
+ assertFalse(Phone.isValidPhone("+99"));
+ assertFalse(Phone.isValidPhone("+1231231231231231321312331321312213213"));
// valid phone numbers
assertTrue(Phone.isValidPhone("911")); // exactly 3 numbers
assertTrue(Phone.isValidPhone("93121534"));
assertTrue(Phone.isValidPhone("124293842033123")); // long phone numbers
+ assertTrue(Phone.isValidPhone("+911"));
}
@Test
@@ -57,4 +62,11 @@ public void equals() {
// different values -> returns false
assertFalse(phone.equals(new Phone("995")));
}
+
+ @Test
+ public void compareTo() {
+ assertEquals(new Phone("111").compareTo(new Phone("222")), -1);
+ assertEquals(new Phone("111").compareTo(new Phone("111")), 0);
+ assertEquals(new Phone("222").compareTo(new Phone("111")), 1);
+ }
}
diff --git a/src/test/java/seedu/address/model/person/UniquePersonListTest.java b/src/test/java/seedu/address/model/person/UniquePersonListTest.java
index 17ae501df08..b4a8ef50089 100644
--- a/src/test/java/seedu/address/model/person/UniquePersonListTest.java
+++ b/src/test/java/seedu/address/model/person/UniquePersonListTest.java
@@ -4,6 +4,7 @@
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static seedu.address.logic.commands.CommandTestUtil.VALID_ADDRESS_BOB;
+import static seedu.address.logic.commands.CommandTestUtil.VALID_PHONE_BOB;
import static seedu.address.logic.commands.CommandTestUtil.VALID_TAG_HUSBAND;
import static seedu.address.testutil.Assert.assertThrows;
import static seedu.address.testutil.TypicalPersons.ALICE;
@@ -42,7 +43,7 @@ public void contains_personInList_returnsTrue() {
@Test
public void contains_personWithSameIdentityFieldsInList_returnsTrue() {
uniquePersonList.add(ALICE);
- Person editedAlice = new PersonBuilder(ALICE).withAddress(VALID_ADDRESS_BOB).withTags(VALID_TAG_HUSBAND)
+ Person editedAlice = new PersonBuilder(ALICE).withPhone(VALID_PHONE_BOB).withTags(VALID_TAG_HUSBAND)
.build();
assertTrue(uniquePersonList.contains(editedAlice));
}
diff --git a/src/test/java/seedu/address/storage/JsonAdaptedPersonTest.java b/src/test/java/seedu/address/storage/JsonAdaptedPersonTest.java
index 83b11331cdb..1dcbb1d48b5 100644
--- a/src/test/java/seedu/address/storage/JsonAdaptedPersonTest.java
+++ b/src/test/java/seedu/address/storage/JsonAdaptedPersonTest.java
@@ -5,32 +5,38 @@
import static seedu.address.testutil.Assert.assertThrows;
import static seedu.address.testutil.TypicalPersons.BENSON;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.stream.Collectors;
-
import org.junit.jupiter.api.Test;
import seedu.address.commons.exceptions.IllegalValueException;
import seedu.address.model.person.Address;
+import seedu.address.model.person.Birthday;
+import seedu.address.model.person.Department;
import seedu.address.model.person.Email;
+import seedu.address.model.person.Leave;
+import seedu.address.model.person.Money;
import seedu.address.model.person.Name;
import seedu.address.model.person.Phone;
public class JsonAdaptedPersonTest {
- private static final String INVALID_NAME = "R@chel";
- private static final String INVALID_PHONE = "+651234";
+ private static final String INVALID_NAME = " ";
+ private static final String INVALID_PHONE = "-651234";
private static final String INVALID_ADDRESS = " ";
private static final String INVALID_EMAIL = "example.com";
- private static final String INVALID_TAG = "#friend";
+ private static final String INVALID_SALARY = "-1";
+ private static final String INVALID_BUDGET = "1000000000000000";
+ private static final String INVALID_DEPARTMENT = " ";
+ private static final String INVALID_DOB = "1";
+ private static final String INVALID_LEAVE = "1+";
private static final String VALID_NAME = BENSON.getName().toString();
private static final String VALID_PHONE = BENSON.getPhone().toString();
private static final String VALID_EMAIL = BENSON.getEmail().toString();
private static final String VALID_ADDRESS = BENSON.getAddress().toString();
- private static final List VALID_TAGS = BENSON.getTags().stream()
- .map(JsonAdaptedTag::new)
- .collect(Collectors.toList());
+ private static final String VALID_SALARY = BENSON.getSalary().amount;
+ private static final String VALID_BUDGET = BENSON.getClaimBudget().amount;
+ private static final String VALID_DEPARTMENT = BENSON.getDepartment().department;
+ private static final String VALID_DOB = BENSON.getDob().dob;
+ private static final String VALID_LEAVE = BENSON.getLeave().leave;
@Test
public void toModelType_validPersonDetails_returnsPerson() throws Exception {
@@ -41,14 +47,16 @@ public void toModelType_validPersonDetails_returnsPerson() throws Exception {
@Test
public void toModelType_invalidName_throwsIllegalValueException() {
JsonAdaptedPerson person =
- new JsonAdaptedPerson(INVALID_NAME, VALID_PHONE, VALID_EMAIL, VALID_ADDRESS, VALID_TAGS);
+ new JsonAdaptedPerson(INVALID_NAME, VALID_PHONE, VALID_EMAIL, VALID_ADDRESS,
+ VALID_SALARY, VALID_BUDGET, VALID_DEPARTMENT, VALID_DOB, VALID_LEAVE);
String expectedMessage = Name.MESSAGE_CONSTRAINTS;
assertThrows(IllegalValueException.class, expectedMessage, person::toModelType);
}
@Test
public void toModelType_nullName_throwsIllegalValueException() {
- JsonAdaptedPerson person = new JsonAdaptedPerson(null, VALID_PHONE, VALID_EMAIL, VALID_ADDRESS, VALID_TAGS);
+ JsonAdaptedPerson person = new JsonAdaptedPerson(null, VALID_PHONE, VALID_EMAIL, VALID_ADDRESS,
+ VALID_SALARY, VALID_BUDGET, VALID_DEPARTMENT, VALID_DOB, VALID_LEAVE);
String expectedMessage = String.format(MISSING_FIELD_MESSAGE_FORMAT, Name.class.getSimpleName());
assertThrows(IllegalValueException.class, expectedMessage, person::toModelType);
}
@@ -56,14 +64,16 @@ public void toModelType_nullName_throwsIllegalValueException() {
@Test
public void toModelType_invalidPhone_throwsIllegalValueException() {
JsonAdaptedPerson person =
- new JsonAdaptedPerson(VALID_NAME, INVALID_PHONE, VALID_EMAIL, VALID_ADDRESS, VALID_TAGS);
+ new JsonAdaptedPerson(VALID_NAME, INVALID_PHONE, VALID_EMAIL, VALID_ADDRESS,
+ VALID_SALARY, VALID_BUDGET, VALID_DEPARTMENT, VALID_DOB, VALID_LEAVE);
String expectedMessage = Phone.MESSAGE_CONSTRAINTS;
assertThrows(IllegalValueException.class, expectedMessage, person::toModelType);
}
@Test
public void toModelType_nullPhone_throwsIllegalValueException() {
- JsonAdaptedPerson person = new JsonAdaptedPerson(VALID_NAME, null, VALID_EMAIL, VALID_ADDRESS, VALID_TAGS);
+ JsonAdaptedPerson person = new JsonAdaptedPerson(VALID_NAME, null, VALID_EMAIL, VALID_ADDRESS,
+ VALID_SALARY, VALID_BUDGET, VALID_DEPARTMENT, VALID_DOB, VALID_LEAVE);
String expectedMessage = String.format(MISSING_FIELD_MESSAGE_FORMAT, Phone.class.getSimpleName());
assertThrows(IllegalValueException.class, expectedMessage, person::toModelType);
}
@@ -71,14 +81,16 @@ public void toModelType_nullPhone_throwsIllegalValueException() {
@Test
public void toModelType_invalidEmail_throwsIllegalValueException() {
JsonAdaptedPerson person =
- new JsonAdaptedPerson(VALID_NAME, VALID_PHONE, INVALID_EMAIL, VALID_ADDRESS, VALID_TAGS);
+ new JsonAdaptedPerson(VALID_NAME, VALID_PHONE, INVALID_EMAIL, VALID_ADDRESS,
+ VALID_SALARY, VALID_BUDGET, VALID_DEPARTMENT, VALID_DOB, VALID_LEAVE);
String expectedMessage = Email.MESSAGE_CONSTRAINTS;
assertThrows(IllegalValueException.class, expectedMessage, person::toModelType);
}
@Test
public void toModelType_nullEmail_throwsIllegalValueException() {
- JsonAdaptedPerson person = new JsonAdaptedPerson(VALID_NAME, VALID_PHONE, null, VALID_ADDRESS, VALID_TAGS);
+ JsonAdaptedPerson person = new JsonAdaptedPerson(VALID_NAME, VALID_PHONE, null, VALID_ADDRESS,
+ VALID_SALARY, VALID_BUDGET, VALID_DEPARTMENT, VALID_DOB, VALID_LEAVE);
String expectedMessage = String.format(MISSING_FIELD_MESSAGE_FORMAT, Email.class.getSimpleName());
assertThrows(IllegalValueException.class, expectedMessage, person::toModelType);
}
@@ -86,25 +98,103 @@ public void toModelType_nullEmail_throwsIllegalValueException() {
@Test
public void toModelType_invalidAddress_throwsIllegalValueException() {
JsonAdaptedPerson person =
- new JsonAdaptedPerson(VALID_NAME, VALID_PHONE, VALID_EMAIL, INVALID_ADDRESS, VALID_TAGS);
+ new JsonAdaptedPerson(VALID_NAME, VALID_PHONE, VALID_EMAIL, INVALID_ADDRESS,
+ VALID_SALARY, VALID_BUDGET, VALID_DEPARTMENT, VALID_DOB, VALID_LEAVE);
String expectedMessage = Address.MESSAGE_CONSTRAINTS;
assertThrows(IllegalValueException.class, expectedMessage, person::toModelType);
}
@Test
public void toModelType_nullAddress_throwsIllegalValueException() {
- JsonAdaptedPerson person = new JsonAdaptedPerson(VALID_NAME, VALID_PHONE, VALID_EMAIL, null, VALID_TAGS);
+ JsonAdaptedPerson person = new JsonAdaptedPerson(VALID_NAME, VALID_PHONE, VALID_EMAIL, null,
+ VALID_SALARY, VALID_BUDGET, VALID_DEPARTMENT, VALID_DOB, VALID_LEAVE);
String expectedMessage = String.format(MISSING_FIELD_MESSAGE_FORMAT, Address.class.getSimpleName());
assertThrows(IllegalValueException.class, expectedMessage, person::toModelType);
}
@Test
- public void toModelType_invalidTags_throwsIllegalValueException() {
- List invalidTags = new ArrayList<>(VALID_TAGS);
- invalidTags.add(new JsonAdaptedTag(INVALID_TAG));
+ public void toModelType_invalidSalary_throwsIllegalValueException() {
+ JsonAdaptedPerson person =
+ new JsonAdaptedPerson(VALID_NAME, VALID_PHONE, VALID_EMAIL, VALID_ADDRESS,
+ INVALID_SALARY, VALID_BUDGET, VALID_DEPARTMENT, VALID_DOB, VALID_LEAVE);
+ String expectedMessage = Money.MESSAGE_CONSTRAINTS;
+ assertThrows(IllegalValueException.class, expectedMessage, person::toModelType);
+ }
+
+ @Test
+ public void toModelType_nullSalary_throwsIllegalValueException() {
+ JsonAdaptedPerson person = new JsonAdaptedPerson(VALID_NAME, VALID_PHONE, VALID_EMAIL, VALID_ADDRESS,
+ null, VALID_BUDGET, VALID_DEPARTMENT, VALID_DOB, VALID_LEAVE);
+ String expectedMessage = String.format(MISSING_FIELD_MESSAGE_FORMAT, Money.class.getSimpleName());
+ assertThrows(IllegalValueException.class, expectedMessage, person::toModelType);
+ }
+
+ @Test
+ public void toModelType_invalidBudget_throwsIllegalValueException() {
+ JsonAdaptedPerson person =
+ new JsonAdaptedPerson(VALID_NAME, VALID_PHONE, VALID_EMAIL, VALID_ADDRESS,
+ VALID_SALARY, INVALID_BUDGET, VALID_DEPARTMENT, VALID_DOB, VALID_LEAVE);
+ String expectedMessage = Money.MESSAGE_CONSTRAINTS;
+ assertThrows(IllegalValueException.class, expectedMessage, person::toModelType);
+ }
+
+ @Test
+ public void toModelType_nullBudget_throwsIllegalValueException() {
+ JsonAdaptedPerson person = new JsonAdaptedPerson(VALID_NAME, VALID_PHONE, VALID_EMAIL, VALID_ADDRESS,
+ VALID_SALARY, null, VALID_DEPARTMENT, VALID_DOB, VALID_LEAVE);
+ String expectedMessage = String.format(MISSING_FIELD_MESSAGE_FORMAT, Money.class.getSimpleName());
+ assertThrows(IllegalValueException.class, expectedMessage, person::toModelType);
+ }
+
+ @Test
+ public void toModelType_invalidDepartment_throwsIllegalValueException() throws Exception {
+ JsonAdaptedPerson person =
+ new JsonAdaptedPerson(VALID_NAME, VALID_PHONE, VALID_EMAIL, VALID_ADDRESS,
+ VALID_SALARY, VALID_BUDGET, INVALID_DEPARTMENT, VALID_DOB, VALID_LEAVE);
+ String expectedMessage = Department.MESSAGE_CONSTRAINTS;
+ assertThrows(IllegalValueException.class, expectedMessage, person::toModelType);
+ }
+
+ @Test
+ public void toModelType_nullDepartment_throwsIllegalValueException() {
+ JsonAdaptedPerson person = new JsonAdaptedPerson(VALID_NAME, VALID_PHONE, VALID_EMAIL, VALID_ADDRESS,
+ VALID_SALARY, VALID_BUDGET, null, VALID_DOB, VALID_LEAVE);
+ String expectedMessage = String.format(MISSING_FIELD_MESSAGE_FORMAT, Department.class.getSimpleName());
+ assertThrows(IllegalValueException.class, expectedMessage, person::toModelType);
+ }
+
+ @Test
+ public void toModelType_invalidDob_throwsIllegalValueException() {
JsonAdaptedPerson person =
- new JsonAdaptedPerson(VALID_NAME, VALID_PHONE, VALID_EMAIL, VALID_ADDRESS, invalidTags);
- assertThrows(IllegalValueException.class, person::toModelType);
+ new JsonAdaptedPerson(VALID_NAME, VALID_PHONE, VALID_EMAIL, VALID_ADDRESS,
+ VALID_SALARY, VALID_BUDGET, VALID_DEPARTMENT, INVALID_DOB, VALID_LEAVE);
+ String expectedMessage = Birthday.MESSAGE_CONSTRAINTS;
+ assertThrows(IllegalValueException.class, expectedMessage, person::toModelType);
+ }
+
+ @Test
+ public void toModelType_nullDob_throwsIllegalValueException() {
+ JsonAdaptedPerson person = new JsonAdaptedPerson(VALID_NAME, VALID_PHONE, VALID_EMAIL, VALID_ADDRESS,
+ VALID_SALARY, VALID_BUDGET, VALID_DEPARTMENT, null, VALID_LEAVE);
+ String expectedMessage = String.format(MISSING_FIELD_MESSAGE_FORMAT, Birthday.class.getSimpleName());
+ assertThrows(IllegalValueException.class, expectedMessage, person::toModelType);
+ }
+
+ @Test
+ public void toModelType_invalidLeave_throwsIllegalValueException() {
+ JsonAdaptedPerson person =
+ new JsonAdaptedPerson(VALID_NAME, VALID_PHONE, VALID_EMAIL, VALID_ADDRESS,
+ VALID_SALARY, VALID_BUDGET, VALID_DEPARTMENT, VALID_DOB, INVALID_LEAVE);
+ String expectedMessage = Leave.MESSAGE_CONSTRAINTS;
+ assertThrows(IllegalValueException.class, expectedMessage, person::toModelType);
+ }
+
+ @Test
+ public void toModelType_nullLeave_throwsIllegalValueException() {
+ JsonAdaptedPerson person = new JsonAdaptedPerson(VALID_NAME, VALID_PHONE, VALID_EMAIL, VALID_ADDRESS,
+ VALID_SALARY, VALID_BUDGET, VALID_DEPARTMENT, VALID_DOB, null);
+ String expectedMessage = String.format(MISSING_FIELD_MESSAGE_FORMAT, Leave.class.getSimpleName());
+ assertThrows(IllegalValueException.class, expectedMessage, person::toModelType);
}
}
diff --git a/src/test/java/seedu/address/testutil/EditPersonDescriptorBuilder.java b/src/test/java/seedu/address/testutil/EditPersonDescriptorBuilder.java
index 4584bd5044e..ff7063178ff 100644
--- a/src/test/java/seedu/address/testutil/EditPersonDescriptorBuilder.java
+++ b/src/test/java/seedu/address/testutil/EditPersonDescriptorBuilder.java
@@ -1,16 +1,11 @@
package seedu.address.testutil;
-import java.util.Set;
-import java.util.stream.Collectors;
-import java.util.stream.Stream;
-
import seedu.address.logic.commands.EditCommand.EditPersonDescriptor;
import seedu.address.model.person.Address;
import seedu.address.model.person.Email;
import seedu.address.model.person.Name;
import seedu.address.model.person.Person;
import seedu.address.model.person.Phone;
-import seedu.address.model.tag.Tag;
/**
* A utility class to help with building EditPersonDescriptor objects.
@@ -36,7 +31,10 @@ public EditPersonDescriptorBuilder(Person person) {
descriptor.setPhone(person.getPhone());
descriptor.setEmail(person.getEmail());
descriptor.setAddress(person.getAddress());
- descriptor.setTags(person.getTags());
+ descriptor.setSalary(person.getSalary());
+ descriptor.setClaimBudget(person.getClaimBudget());
+ descriptor.setDepartment(person.getDepartment());
+ descriptor.setDob(person.getDob());
}
/**
@@ -76,8 +74,8 @@ public EditPersonDescriptorBuilder withAddress(String address) {
* that we are building.
*/
public EditPersonDescriptorBuilder withTags(String... tags) {
- Set tagSet = Stream.of(tags).map(Tag::new).collect(Collectors.toSet());
- descriptor.setTags(tagSet);
+ // Set tagSet = Stream.of(tags).map(Tag::new).collect(Collectors.toSet());
+ // descriptor.setTags(tagSet);
return this;
}
diff --git a/src/test/java/seedu/address/testutil/PersonBuilder.java b/src/test/java/seedu/address/testutil/PersonBuilder.java
index 6be381d39ba..6de67a19782 100644
--- a/src/test/java/seedu/address/testutil/PersonBuilder.java
+++ b/src/test/java/seedu/address/testutil/PersonBuilder.java
@@ -1,15 +1,14 @@
package seedu.address.testutil;
-import java.util.HashSet;
-import java.util.Set;
-
import seedu.address.model.person.Address;
+import seedu.address.model.person.Birthday;
+import seedu.address.model.person.Department;
import seedu.address.model.person.Email;
+import seedu.address.model.person.Leave;
+import seedu.address.model.person.Money;
import seedu.address.model.person.Name;
import seedu.address.model.person.Person;
import seedu.address.model.person.Phone;
-import seedu.address.model.tag.Tag;
-import seedu.address.model.util.SampleDataUtil;
/**
* A utility class to help with building Person objects.
@@ -20,12 +19,21 @@ public class PersonBuilder {
public static final String DEFAULT_PHONE = "85355255";
public static final String DEFAULT_EMAIL = "amy@gmail.com";
public static final String DEFAULT_ADDRESS = "123, Jurong West Ave 6, #08-111";
+ public static final String DEFAULT_SALARY = "10000";
+ public static final String DEFAULT_BUDGET = "2500";
+ public static final String DEFAULT_DEPARTMENT = "Engineering";
+ public static final String DEFAULT_DOB = "2000-01-01";
+ public static final String DEFAULT_LEAVE = "000000000000";
private Name name;
private Phone phone;
private Email email;
private Address address;
- private Set tags;
+ private Money salary;
+ private Money claimBudget;
+ private Department department;
+ private Birthday dob;
+ private Leave leave;
/**
* Creates a {@code PersonBuilder} with the default details.
@@ -35,7 +43,11 @@ public PersonBuilder() {
phone = new Phone(DEFAULT_PHONE);
email = new Email(DEFAULT_EMAIL);
address = new Address(DEFAULT_ADDRESS);
- tags = new HashSet<>();
+ salary = new Money(DEFAULT_SALARY);
+ claimBudget = new Money(DEFAULT_BUDGET);
+ department = new Department(DEFAULT_DEPARTMENT);
+ dob = new Birthday(DEFAULT_DOB);
+ leave = new Leave(DEFAULT_LEAVE);
}
/**
@@ -46,7 +58,11 @@ public PersonBuilder(Person personToCopy) {
phone = personToCopy.getPhone();
email = personToCopy.getEmail();
address = personToCopy.getAddress();
- tags = new HashSet<>(personToCopy.getTags());
+ salary = personToCopy.getSalary();
+ claimBudget = personToCopy.getClaimBudget();
+ department = personToCopy.getDepartment();
+ dob = personToCopy.getDob();
+ leave = personToCopy.getLeave();
}
/**
@@ -61,7 +77,6 @@ public PersonBuilder withName(String name) {
* Parses the {@code tags} into a {@code Set} and set it to the {@code Person} that we are building.
*/
public PersonBuilder withTags(String ... tags) {
- this.tags = SampleDataUtil.getTagSet(tags);
return this;
}
@@ -89,8 +104,48 @@ public PersonBuilder withEmail(String email) {
return this;
}
+ /**
+ * Sets the {@code Salary} of the {@code Person} that we are building.
+ */
+ public PersonBuilder withSalary(String salary) {
+ this.salary = new Money(salary);
+ return this;
+ }
+
+ /**
+ * Sets the {@code ClaimBudget} of the {@code Person} that we are building.
+ */
+ public PersonBuilder withClaimBudget(String budget) {
+ this.claimBudget = new Money(budget);
+ return this;
+ }
+
+ /**
+ * Sets the {@code Department} of the {@code Person} that we are building.
+ */
+ public PersonBuilder withDepartment(String department) {
+ this.department = new Department(department);
+ return this;
+ }
+
+ /**
+ * Sets the {@code Birthday} of the {@code Person} that we are building.
+ */
+ public PersonBuilder withDob(String dob) {
+ this.dob = new Birthday(dob);
+ return this;
+ }
+
+ /**
+ * Sets the {@code Leave} of the {@code Person} that we are building.
+ */
+ public PersonBuilder withLeave(String leave) {
+ this.leave = new Leave(leave);
+ return this;
+ }
+
public Person build() {
- return new Person(name, phone, email, address, tags);
+ return new Person(name, phone, email, address, salary, claimBudget, department, dob, leave);
}
}
diff --git a/src/test/java/seedu/address/testutil/PersonUtil.java b/src/test/java/seedu/address/testutil/PersonUtil.java
index 90849945183..e4e9734385e 100644
--- a/src/test/java/seedu/address/testutil/PersonUtil.java
+++ b/src/test/java/seedu/address/testutil/PersonUtil.java
@@ -1,17 +1,17 @@
package seedu.address.testutil;
import static seedu.address.logic.parser.CliSyntax.PREFIX_ADDRESS;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_CLAIM_BUDGET;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_DEPARTMENT;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_DOB;
import static seedu.address.logic.parser.CliSyntax.PREFIX_EMAIL;
import static seedu.address.logic.parser.CliSyntax.PREFIX_NAME;
import static seedu.address.logic.parser.CliSyntax.PREFIX_PHONE;
-import static seedu.address.logic.parser.CliSyntax.PREFIX_TAG;
-
-import java.util.Set;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_SALARY;
import seedu.address.logic.commands.AddCommand;
import seedu.address.logic.commands.EditCommand.EditPersonDescriptor;
import seedu.address.model.person.Person;
-import seedu.address.model.tag.Tag;
/**
* A utility class for Person.
@@ -34,9 +34,10 @@ public static String getPersonDetails(Person person) {
sb.append(PREFIX_PHONE + person.getPhone().value + " ");
sb.append(PREFIX_EMAIL + person.getEmail().value + " ");
sb.append(PREFIX_ADDRESS + person.getAddress().value + " ");
- person.getTags().stream().forEach(
- s -> sb.append(PREFIX_TAG + s.tagName + " ")
- );
+ sb.append(PREFIX_SALARY + person.getSalary().amount + " ");
+ sb.append(PREFIX_CLAIM_BUDGET + person.getClaimBudget().amount + " ");
+ sb.append(PREFIX_DEPARTMENT + person.getDepartment().department + " ");
+ sb.append(PREFIX_DOB + person.getDob().dob + " ");
return sb.toString();
}
@@ -49,14 +50,9 @@ public static String getEditPersonDescriptorDetails(EditPersonDescriptor descrip
descriptor.getPhone().ifPresent(phone -> sb.append(PREFIX_PHONE).append(phone.value).append(" "));
descriptor.getEmail().ifPresent(email -> sb.append(PREFIX_EMAIL).append(email.value).append(" "));
descriptor.getAddress().ifPresent(address -> sb.append(PREFIX_ADDRESS).append(address.value).append(" "));
- if (descriptor.getTags().isPresent()) {
- Set tags = descriptor.getTags().get();
- if (tags.isEmpty()) {
- sb.append(PREFIX_TAG);
- } else {
- tags.forEach(s -> sb.append(PREFIX_TAG).append(s.tagName).append(" "));
- }
- }
+ descriptor.getSalary().ifPresent(salary -> sb.append(PREFIX_SALARY).append(salary.amount).append(" "));
+ descriptor.getDepartment().ifPresent(dep -> sb.append(PREFIX_DEPARTMENT).append(dep.department).append(" "));
+ descriptor.getDob().ifPresent(dob -> sb.append(PREFIX_DOB).append(dob.dob).append(" "));
return sb.toString();
}
}
diff --git a/src/test/java/seedu/address/testutil/TypicalMonths.java b/src/test/java/seedu/address/testutil/TypicalMonths.java
new file mode 100644
index 00000000000..f3c72c7274d
--- /dev/null
+++ b/src/test/java/seedu/address/testutil/TypicalMonths.java
@@ -0,0 +1,12 @@
+package seedu.address.testutil;
+
+import seedu.address.model.person.Month;
+
+/**
+ * A utility class containing a list of {@code Month} objects to be used in tests.
+ */
+
+public class TypicalMonths {
+ public static final Month VALID_MONTH_SUCCESS = new Month(2);
+ public static final Month VALID_MONTH_FAILURE = new Month(3);
+}
diff --git a/src/test/java/seedu/address/testutil/TypicalPersons.java b/src/test/java/seedu/address/testutil/TypicalPersons.java
index fec76fb7129..b505828fdf8 100644
--- a/src/test/java/seedu/address/testutil/TypicalPersons.java
+++ b/src/test/java/seedu/address/testutil/TypicalPersons.java
@@ -2,14 +2,17 @@
import static seedu.address.logic.commands.CommandTestUtil.VALID_ADDRESS_AMY;
import static seedu.address.logic.commands.CommandTestUtil.VALID_ADDRESS_BOB;
+import static seedu.address.logic.commands.CommandTestUtil.VALID_BUDGET;
+import static seedu.address.logic.commands.CommandTestUtil.VALID_DEPARTMENT;
+import static seedu.address.logic.commands.CommandTestUtil.VALID_DOB;
import static seedu.address.logic.commands.CommandTestUtil.VALID_EMAIL_AMY;
import static seedu.address.logic.commands.CommandTestUtil.VALID_EMAIL_BOB;
+import static seedu.address.logic.commands.CommandTestUtil.VALID_LEAVE;
import static seedu.address.logic.commands.CommandTestUtil.VALID_NAME_AMY;
import static seedu.address.logic.commands.CommandTestUtil.VALID_NAME_BOB;
import static seedu.address.logic.commands.CommandTestUtil.VALID_PHONE_AMY;
import static seedu.address.logic.commands.CommandTestUtil.VALID_PHONE_BOB;
-import static seedu.address.logic.commands.CommandTestUtil.VALID_TAG_FRIEND;
-import static seedu.address.logic.commands.CommandTestUtil.VALID_TAG_HUSBAND;
+import static seedu.address.logic.commands.CommandTestUtil.VALID_SALARY;
import java.util.ArrayList;
import java.util.Arrays;
@@ -25,35 +28,53 @@ public class TypicalPersons {
public static final Person ALICE = new PersonBuilder().withName("Alice Pauline")
.withAddress("123, Jurong West Ave 6, #08-111").withEmail("alice@example.com")
- .withPhone("94351253")
- .withTags("friends").build();
+ .withPhone("94351253").withSalary("10000").withClaimBudget("5000").withDepartment("Engineering")
+ .withDob("2000-01-01").withLeave("111101010101").build();
public static final Person BENSON = new PersonBuilder().withName("Benson Meier")
.withAddress("311, Clementi Ave 2, #02-25")
.withEmail("johnd@example.com").withPhone("98765432")
- .withTags("owesMoney", "friends").build();
+ .withSalary("100").withClaimBudget("5").withDepartment("Sales")
+ .withDob("1997-05-05").withLeave("010101010101").build();
public static final Person CARL = new PersonBuilder().withName("Carl Kurz").withPhone("95352563")
- .withEmail("heinz@example.com").withAddress("wall street").build();
+ .withEmail("heinz@example.com").withAddress("wall street")
+ .withSalary("1000000000000").withClaimBudget("500000").withDepartment("Executive")
+ .withDob("1991-12-31").withLeave("000101010001").build();
public static final Person DANIEL = new PersonBuilder().withName("Daniel Meier").withPhone("87652533")
- .withEmail("cornelia@example.com").withAddress("10th street").withTags("friends").build();
+ .withEmail("cornelia@example.com").withAddress("10th street")
+ .withSalary("23232323232").withClaimBudget("19191919").withDepartment("Marketing")
+ .withDob("1969-07-27").withLeave("111111111111").build();
public static final Person ELLE = new PersonBuilder().withName("Elle Meyer").withPhone("9482224")
- .withEmail("werner@example.com").withAddress("michegan ave").build();
+ .withEmail("werner@example.com").withAddress("michegan ave")
+ .withSalary("3000").withClaimBudget("200").withDepartment("HR")
+ .withDob("1999-06-17").withLeave("000000000001").build();
public static final Person FIONA = new PersonBuilder().withName("Fiona Kunz").withPhone("9482427")
- .withEmail("lydia@example.com").withAddress("little tokyo").build();
+ .withEmail("lydia@example.com").withAddress("little tokyo")
+ .withSalary("100000").withClaimBudget("1").withDepartment("Finance")
+ .withDob("2002-02-27").withLeave("000000100000").build();
public static final Person GEORGE = new PersonBuilder().withName("George Best").withPhone("9482442")
- .withEmail("anna@example.com").withAddress("4th street").build();
+ .withEmail("anna@example.com").withAddress("4th street")
+ .withSalary("10000").withClaimBudget("5000").withDepartment("Engineering")
+ .withDob("2005-09-30").withLeave("111111000000").build();
// Manually added
public static final Person HOON = new PersonBuilder().withName("Hoon Meier").withPhone("8482424")
- .withEmail("stefan@example.com").withAddress("little india").build();
+ .withEmail("stefan@example.com").withAddress("little india")
+ .withSalary("10000").withClaimBudget("5000").withDepartment("Engineering")
+ .withDob("2000-01-01").withLeave("000000000000").build();
public static final Person IDA = new PersonBuilder().withName("Ida Mueller").withPhone("8482131")
- .withEmail("hans@example.com").withAddress("chicago ave").build();
+ .withEmail("hans@example.com").withAddress("chicago ave")
+ .withSalary("10000").withClaimBudget("5000").withDepartment("Engineering")
+ .withDob("2000-01-01").withLeave("111000111000").build();
// Manually added - Person's details found in {@code CommandTestUtil}
public static final Person AMY = new PersonBuilder().withName(VALID_NAME_AMY).withPhone(VALID_PHONE_AMY)
- .withEmail(VALID_EMAIL_AMY).withAddress(VALID_ADDRESS_AMY).withTags(VALID_TAG_FRIEND).build();
+ .withEmail(VALID_EMAIL_AMY).withAddress(VALID_ADDRESS_AMY).withSalary(VALID_SALARY)
+ .withClaimBudget(VALID_BUDGET).withDepartment(VALID_DEPARTMENT).withDob(VALID_DOB)
+ .withLeave(VALID_LEAVE).build();
public static final Person BOB = new PersonBuilder().withName(VALID_NAME_BOB).withPhone(VALID_PHONE_BOB)
- .withEmail(VALID_EMAIL_BOB).withAddress(VALID_ADDRESS_BOB).withTags(VALID_TAG_HUSBAND, VALID_TAG_FRIEND)
- .build();
+ .withEmail(VALID_EMAIL_BOB).withAddress(VALID_ADDRESS_BOB).withSalary(VALID_SALARY)
+ .withClaimBudget(VALID_BUDGET).withDepartment(VALID_DEPARTMENT).withDob(VALID_DOB)
+ .withLeave(VALID_LEAVE).build();
public static final String KEYWORD_MATCHING_MEIER = "Meier"; // A keyword that matches MEIER