diff --git a/.drone.yml b/.drone.yml index 5733c35..13ee39a 100644 --- a/.drone.yml +++ b/.drone.yml @@ -125,7 +125,7 @@ steps: commands: - yarn test:unit --coverage depends_on: - - dependencies + - test # Must run after test otherwise SQLite will get mad - name: upload_coverage image: plugins/s3 diff --git a/.gitignore b/.gitignore index 4583f0f..8a64c73 100644 --- a/.gitignore +++ b/.gitignore @@ -2,6 +2,7 @@ node_modules /dist coverage +*.sqlite # local env files .env.local diff --git a/package.json b/package.json index 26304fa..6f206c5 100644 --- a/package.json +++ b/package.json @@ -9,12 +9,20 @@ "test:unit": "vue-cli-service test:unit" }, "dependencies": { + "axios": "^0.18.0", + "babel-core": "7.0.0-bridge.0", + "babel-eslint": "^10.0.1", "buefy": "^0.8.2", + "bulma-prefers-dark": "^0.1.0-beta.0", "core-js": "^2.6.5", "dexie": "^2.0.4", "eventemitter3": "^4.0.0", + "node-sass": "^4.9.0", "peerjs": "^1.0.4", "register-service-worker": "^1.6.2", + "sass": "^1.18.0", + "sass-loader": "^7.1.0", + "typescript": "^3.4.3", "vue": "^2.6.10", "vue-class-component": "^7.0.2", "vue-property-decorator": "^8.1.0", @@ -33,17 +41,13 @@ "@vue/eslint-config-prettier": "^5.0.0", "@vue/eslint-config-typescript": "^4.0.0", "@vue/test-utils": "^1.0.0-beta.29", - "babel-core": "7.0.0-bridge.0", - "babel-eslint": "^10.0.1", "eslint": "^5.16.0", "eslint-plugin-prettier": "^3.1.0", "eslint-plugin-vue": "^5.0.0", - "node-sass": "^4.9.0", + "indexeddbshim": "^4.1.0", "prettier": "^1.18.2", - "sass": "^1.18.0", - "sass-loader": "^7.1.0", "ts-jest": "^23.0.0", - "typescript": "^3.4.3", + "vue-cli-plugin-axios": "^0.0.4", "vue-cli-plugin-buefy": "^0.3.7", "vue-template-compiler": "^2.6.10" }, diff --git a/public/manifest.json b/public/manifest.json index 51189e7..7478986 100644 --- a/public/manifest.json +++ b/public/manifest.json @@ -1,14 +1,13 @@ { "name": "mcgvue", "short_name": "mcgvue", - "icons": [ - { - "src": "./img/icons/android-chrome-192x192.png", + "icons": [{ + "src": "./images/icons/android-chrome-192x192.png", "sizes": "192x192", "type": "image/png" }, { - "src": "./img/icons/android-chrome-512x512.png", + "src": "./images/icons/android-chrome-512x512.png", "sizes": "512x512", "type": "image/png" } @@ -17,4 +16,4 @@ "display": "standalone", "background_color": "#000000", "theme_color": "#4DBA87" -} +} \ No newline at end of file diff --git a/src/App.vue b/src/App.vue index 79d6ce9..e3ebb68 100644 --- a/src/App.vue +++ b/src/App.vue @@ -9,6 +9,10 @@ + + diff --git a/src/components/DeckBuilder/DeckList.vue b/src/components/DeckBuilder/DeckList.vue new file mode 100644 index 0000000..6b56b57 --- /dev/null +++ b/src/components/DeckBuilder/DeckList.vue @@ -0,0 +1,229 @@ + + + + + diff --git a/src/main.ts b/src/main.ts index 9ee82ad..b1637e4 100644 --- a/src/main.ts +++ b/src/main.ts @@ -1,15 +1,19 @@ import Vue from "vue"; +import "./plugins/axios"; import App from "./App.vue"; import router from "./router"; import store from "./store"; import "./registerServiceWorker"; import Buefy from "buefy"; import "./assets/scss/app.scss"; +import { initDB } from "./mlpccg"; Vue.use(Buefy); Vue.config.productionTip = false; +initDB(); + new Vue({ router, store, diff --git a/src/mlpccg/card.ts b/src/mlpccg/card.ts index 00966da..448f11d 100644 --- a/src/mlpccg/card.ts +++ b/src/mlpccg/card.ts @@ -1,5 +1,71 @@ -const imgBaseURL = "https://mcg.zyg.ovh/images/cards/"; +import { Card } from "./types"; -export function cardImageURL(cardid: string): string { - return `${imgBaseURL}${cardid}.webp`; +export function cardFullName(card: Card): string { + if (card.Subname != "") { + return `${card.Name}, ${card.Subname}`; + } + return card.Name; +} + +export function createPonyheadURL(cards: Card[]): string { + const cardlist = cards.map(c => `${c.ID}x1`); + return "https://ponyhead.com/deckbuilder?v1code=" + cardlist.join("-"); +} + +export const colorNames = [ + "Loyalty", + "Honesty", + "Laughter", + "Magic", + "Generosity", + "Kindness", + "None" +]; + +export const typeNames = [ + "Mane Character", + "Friend", + "Event", + "Resource", + "Troublemaker", + "Problem" +]; + +export const rarityNames = ["C", "U", "R", "SR", "UR", "RR", "F"]; + +// Trasform string from list to a number that can be used for comparison/sorting +function arrIndex(arr: string[]) { + return function(comp: string) { + const idx = arr.indexOf(comp); + if (idx < 0) { + return arr.length; + } + return idx; + }; +} + +export const elemIndex = arrIndex(colorNames); +export const typeIndex = arrIndex(typeNames); +export const rarityIndex = arrIndex(rarityNames); + +// Convert Element[] to number by scaling elements for fair comparisons +// Example: ["Loyalty", "Kindness"] -> [0, 5] -> [1, 6] -> 16 +export function multiElemStr(elems: string[]): number { + return elems + .map(elemIndex) + .reduce( + (acc, elem, idx, arr) => acc + (elem + 1) * 10 ** (arr.length - idx - 1), + 0 + ); +} + +export function cardLimit(type: string) { + switch (type) { + case "Mane Character": + return 1; + case "Problem": + return 2; + default: + return 3; + } } diff --git a/src/mlpccg/database.ts b/src/mlpccg/database.ts index 641f702..0fdb78b 100644 --- a/src/mlpccg/database.ts +++ b/src/mlpccg/database.ts @@ -1,21 +1,32 @@ import Dexie from "dexie"; -import { Card, CardFilter } from "./types"; +import { Card, CardFilter, StoredImages } from "./types"; +import { cardFullName } from "./card"; class CardDatabase extends Dexie { public cards: Dexie.Table; + public images: Dexie.Table; public constructor() { super("CardDatabase"); this.version(1).stores({ - cards: "ID,Set,Type,Cost,Power" + cards: "ID,Set,Type,Cost,Power", + images: "id" }); this.cards = this.table("cards"); + this.images = this.table("images"); } } -export let Database = new CardDatabase(); +export let Database: CardDatabase | null = null; + +export function initDB() { + Database = new CardDatabase(); +} export async function getCards(filter: CardFilter) { + if (Database == null) { + throw new Error("Database was not initialized, init with 'initDB()'"); + } let table = Database.cards; // Get best IDB index let query: Dexie.Collection; @@ -43,95 +54,100 @@ export async function getCards(filter: CardFilter) { } } - return await query - .filter(x => { - if (filter.Name) { - if ( - !`${x.Name}, ${x.Subname}` - .toLowerCase() - .includes(filter.Name.toLowerCase()) - ) { - return false; + const results = query.filter(x => { + if (filter.Name) { + if ( + !cardFullName(x) + .toLowerCase() + .includes(filter.Name.toLowerCase()) + ) { + return false; + } + } + if (filter.Rules) { + if ( + !`${x.Keywords.join(" ~ ")} ~ ${x.Text}` + .toLowerCase() + .includes(filter.Rules.toLowerCase()) + ) { + return false; + } + } + if (filter.Traits && filter.Traits.length > 0) { + let found = false; + for (const trait of x.Traits) { + if (filter.Traits.includes(trait)) { + found = true; + break; } } - if (filter.Rules) { - if ( - !`${x.Keywords.join(" ~ ")} ~ ${x.Text}` - .toLowerCase() - .includes(filter.Rules.toLowerCase()) - ) { - return false; + if (!found) { + return false; + } + } + if (filter.Sets && filter.Sets.length > 0) { + if (!filter.Sets.includes(x.Set)) { + return false; + } + } + if (filter.Types && filter.Types.length > 0) { + if (!filter.Types.includes(x.Type)) { + return false; + } + } + if (filter.Elements && filter.Elements.length > 0) { + let found = false; + for (const element of x.Element) { + if (filter.Elements.includes(element)) { + found = true; + break; } } - if (filter.Traits && filter.Traits.length > 0) { - let found = false; - for (const trait of x.Traits) { - if (filter.Traits.includes(trait)) { - found = true; - break; - } - } - if (!found) { - return false; - } - } - if (filter.Sets && filter.Sets.length > 0) { - if (!filter.Sets.includes(x.Set)) { - return false; - } - } - if (filter.Types && filter.Types.length > 0) { - if (!filter.Types.includes(x.Type)) { - return false; - } - } - if (filter.Elements && filter.Elements.length > 0) { - let found = false; - for (const element of x.Element) { + if (x.Requirement) { + for (const element in x.Requirement) { if (filter.Elements.includes(element)) { found = true; break; } } - if (x.Requirement) { - for (const element in x.Requirement) { - if (filter.Elements.includes(element)) { - found = true; - break; - } + } + if (x.ProblemRequirement) { + for (const element in x.ProblemRequirement) { + if (filter.Elements.includes(element)) { + found = true; + break; } } - if (x.ProblemRequirement) { - for (const element in x.ProblemRequirement) { - if (filter.Elements.includes(element)) { - found = true; - break; - } - } - } - if (!found) { - return false; - } } - if (filter.Powers && filter.Powers.length > 0) { - if ( - typeof x.Power === "undefined" || - !filter.Powers.includes(x.Power) - ) { - return false; - } + // For "None" element searches, "nothing" is actually ok + if ( + filter.Elements.includes("None") && + x.Element.length == 0 && + (!x.Requirement || x.Requirement.length == 0) && + (!x.ProblemRequirement || x.ProblemRequirement.length == 0) + ) { + found = true; } - if (filter.Costs && filter.Costs.length > 0) { - if (typeof x.Cost === "undefined" || !filter.Costs.includes(x.Cost)) { - return false; - } + if (!found) { + return false; } - if (filter.Rarities && filter.Rarities.length > 0) { - if (!filter.Rarities.includes(x.Rarity)) { - return false; - } + } + if (filter.Powers && filter.Powers.length > 0) { + if (typeof x.Power === "undefined" || !filter.Powers.includes(x.Power)) { + return false; } - return true; - }) - .toArray(); + } + if (filter.Costs && filter.Costs.length > 0) { + if (typeof x.Cost === "undefined" || !filter.Costs.includes(x.Cost)) { + return false; + } + } + if (filter.Rarities && filter.Rarities.length > 0) { + if (!filter.Rarities.includes(x.Rarity)) { + return false; + } + } + return true; + }); + return await results.toArray(); } diff --git a/src/mlpccg/images.ts b/src/mlpccg/images.ts new file mode 100644 index 0000000..eb35fb1 --- /dev/null +++ b/src/mlpccg/images.ts @@ -0,0 +1,35 @@ +import axios from "axios"; +import { Database } from "./database"; + +const imgBaseURL = "https://mcg.zyg.ovh/images/cards/"; + +export function cardImageURL(cardid: string): string { + return `${imgBaseURL}${cardid}.webp`; +} + +async function getCardImageList(): Promise { + const req = await axios(`${imgBaseURL}list.txt`); + return req.data; +} + +export async function getImages() { + if (Database == null) { + throw new Error("Database was not initialized, init with 'initDB()'"); + } + const itemcount = await Database.images.count(); + if (itemcount > 100) { + // DB already filled, exit early + return; + } + const imglist = await getCardImageList(); + + let table = Database.images; + const promises = imglist.map(async img => { + const req = await axios({ + url: `${imgBaseURL}${img}`, + responseType: "blob" + }); + return table.put({ id: img, image: req.data }); + }); + return await Promise.all(promises); +} diff --git a/src/mlpccg/index.ts b/src/mlpccg/index.ts new file mode 100644 index 0000000..322722a --- /dev/null +++ b/src/mlpccg/index.ts @@ -0,0 +1,5 @@ +export * from "./card"; +export * from "./database"; +export * from "./set"; +export * from "./types"; +export * from "./images"; diff --git a/src/mlpccg/set.ts b/src/mlpccg/set.ts index 694a360..7e00929 100644 --- a/src/mlpccg/set.ts +++ b/src/mlpccg/set.ts @@ -1,8 +1,9 @@ import { SetFile } from "./types"; import { Database } from "./database"; +import axios from "axios"; const baseURL = "https://mcg.zyg.ovh/setdata/"; -const allSets = [ +export const allSets = [ "PR", "CN", "RR", @@ -19,6 +20,9 @@ const allSets = [ ]; export async function loadSets() { + if (Database == null) { + throw new Error("Database was not initialized, init with 'initDB()'"); + } const itemcount = await Database.cards.count(); if (itemcount > 100) { // DB already filled, exit early @@ -27,6 +31,9 @@ export async function loadSets() { const sets = await Promise.all(allSets.map(set => downloadSet(set))); await Promise.all( sets.map(async set => { + if (Database == null) { + throw new Error("Database was not initialized, init with 'initDB()'"); + } console.log(`Processing cards from ${set.Name}`); return await Database.cards.bulkPut(set.Cards); }) @@ -34,11 +41,10 @@ export async function loadSets() { } async function downloadSet(setid: string): Promise { - const setfile = await fetch(`${baseURL}${setid.toLowerCase()}.json`); - const setdata: SetFile = await setfile.json(); - setdata.Cards = setdata.Cards.map(c => { + const setdata = await axios(`${baseURL}${setid.toLowerCase()}.json`); + setdata.data.Cards = (setdata.data as SetFile).Cards.map(c => { c.Set = setid; return c; }); - return setdata; + return setdata.data; } diff --git a/src/mlpccg/types.ts b/src/mlpccg/types.ts index 2630a48..7a8e6d3 100644 --- a/src/mlpccg/types.ts +++ b/src/mlpccg/types.ts @@ -2,6 +2,11 @@ export type Rarity = "C" | "U" | "R" | "SR" | "UR" | "RR"; export type PowerRequirement = { [key: string]: number }; +export interface StoredImages { + id: string; + image: Blob; +} + export interface SetFile { Name: string; Cards: Card[]; @@ -38,3 +43,9 @@ export interface CardFilter { Powers?: number[]; Rarities?: string[]; } + +export interface CardSlot { + data: Card; + limit: number; + howmany: number; +} diff --git a/src/plugins/axios.js b/src/plugins/axios.js new file mode 100644 index 0000000..04f5cd3 --- /dev/null +++ b/src/plugins/axios.js @@ -0,0 +1,61 @@ +"use strict"; + +import Vue from "vue"; +import axios from "axios"; + +// Full config: https://github.com/axios/axios#request-config +// axios.defaults.baseURL = process.env.baseURL || process.env.apiUrl || ''; +// axios.defaults.headers.common['Authorization'] = AUTH_TOKEN; +// axios.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded'; + +let config = { + // baseURL: process.env.baseURL || process.env.apiUrl || "" + // timeout: 60 * 1000, // Timeout + // withCredentials: true, // Check cross-site Access-Control +}; + +const _axios = axios.create(config); + +_axios.interceptors.request.use( + function(config) { + // Do something before request is sent + return config; + }, + function(error) { + // Do something with request error + return Promise.reject(error); + } +); + +// Add a response interceptor +_axios.interceptors.response.use( + function(response) { + // Do something with response data + return response; + }, + function(error) { + // Do something with response error + return Promise.reject(error); + } +); + +Plugin.install = function(Vue, options) { + Vue.axios = _axios; + window.axios = _axios; + Object.defineProperties(Vue.prototype, { + axios: { + get() { + return _axios; + } + }, + $axios: { + get() { + return _axios; + } + } + }); +}; + +Vue.use(Plugin); + +export default Plugin; diff --git a/src/testing/index.ts b/src/testing/index.ts index 647d674..a0824bf 100644 --- a/src/testing/index.ts +++ b/src/testing/index.ts @@ -1,3 +1,4 @@ export * from "./MockDataConnection"; export * from "./MockPeer"; export * from "./MockHelper"; +export * from "./EventHook"; diff --git a/src/tests/unit/cards.spec.ts b/src/tests/unit/cards.spec.ts new file mode 100644 index 0000000..2a5f4c9 --- /dev/null +++ b/src/tests/unit/cards.spec.ts @@ -0,0 +1,16 @@ +import { Card, createPonyheadURL, cardFullName } from "@/mlpccg"; + +describe("mlpccg/cards", () => { + test("Card full names are correctly generated in all cases", () => { + const card1 = { Name: "Name", Subname: "" }; + const card2 = { Name: "Name1", Subname: "the Name2" }; + expect(cardFullName(card1 as Card)).toEqual("Name"); + expect(cardFullName(card2 as Card)).toEqual("Name1, the Name2"); + }); + + test("Ponyhead URL is generated correctly", () => { + const cards: any[] = [{ ID: "pr10" }, { ID: "pr12" }, { ID: "pr13" }]; + const url = "https://ponyhead.com/deckbuilder?v1code=pr10x1-pr12x1-pr13x1"; + expect(createPonyheadURL(cards!)).toEqual(url); + }); +}); diff --git a/src/tests/unit/components/CardPicker.spec.ts b/src/tests/unit/components/CardPicker.spec.ts new file mode 100644 index 0000000..6a93cd6 --- /dev/null +++ b/src/tests/unit/components/CardPicker.spec.ts @@ -0,0 +1,37 @@ +import CardPicker from "@/components/DeckBuilder/CardPicker.vue"; +import { shallowMount } from "@vue/test-utils"; + +// Generate 10 test cards +const testCards = new Array(10) + .fill("test") + .map((t, i) => ({ ID: `${t}${i}` })); +const testSlots = testCards.map(c => ({ data: c, limit: 3, howmany: 1 })); + +describe("components/DeckBuilder/CardPicker", () => { + test("CardPicker correctly creates images for each card", () => { + const wrapper = shallowMount(CardPicker, { + propsData: { + rows: 2, + columns: 5, + cards: testSlots + } + }); + const cards = wrapper.findAll(".ccgcard"); + expect(cards.contains("img")).toBe(true); + }); + + test("CardPicker correctly aligns items in a grid", () => { + const wrapper = shallowMount(CardPicker, { + propsData: { + rows: 3, + columns: 5, + cards: testSlots + } + }); + const section = wrapper.find(".cardpicker"); + const style = section.attributes("style"); + expect(style).toMatch( + /grid-template-rows: \S+ \S+ \S+; grid-template-columns: \S+ \S+ \S+ \S+ \S+;/i + ); + }); +}); diff --git a/src/tests/unit/components/DeckList.spec.ts b/src/tests/unit/components/DeckList.spec.ts new file mode 100644 index 0000000..75b8a2a --- /dev/null +++ b/src/tests/unit/components/DeckList.spec.ts @@ -0,0 +1,36 @@ +import DeckList from "@/components/DeckBuilder/DeckList.vue"; +import { shallowMount } from "@vue/test-utils"; +import { colorNames } from "@/mlpccg"; + +// Generate 10 test cards +const testCards = new Array(3).fill("test").map((t, i) => ({ + ID: `test${i}`, + Name: `Test name ${i}`, + Subname: `Subname ${i}`, + Type: "Friend", + Element: [colorNames[i]], + Power: i, + Cost: i, + Requirement: { Generosity: i } +})); +const testSlots = testCards.map((c, i) => ({ data: c, limit: 3, howmany: i })); + +describe("components/DeckBuilder/DeckList", () => { + test("DeckList correctly detects card info", () => { + const wrapper = shallowMount(DeckList, { + propsData: { + cards: testSlots + } + }); + const cards = wrapper.findAll(".ccgcard"); + expect(cards.contains(".fullname")).toBe(true); + for (let index = 0; index < testSlots.length; index++) { + const item = cards.at(index); + const card = testSlots[index]; + expect(item.find(".amt").text()).toEqual(`${card.howmany}`); + expect(item.find(".fullname .name").text()).toEqual(card.data.Name); + expect(item.find(".fullname .subname").text()).toEqual(card.data.Subname); + //TODO Add more fields check as they are added + } + }); +}); diff --git a/src/tests/unit/database.spec.ts b/src/tests/unit/database.spec.ts new file mode 100644 index 0000000..b87cf75 --- /dev/null +++ b/src/tests/unit/database.spec.ts @@ -0,0 +1,39 @@ +import { loadSets, getCards, Database, initDB, cardFullName } from "@/mlpccg"; +import Dexie from "dexie"; +const setGlobalVars = require("indexeddbshim"); +setGlobalVars(Dexie.dependencies); + +describe("mlpccg/Database", () => { + beforeAll(async () => { + jest.setTimeout(15000); + initDB(); + await loadSets(); + }); + + test("getCards without a filter returns all the cards", async () => { + expect(Database).toBeTruthy(); + const allCards = await Database!.cards.count(); + const filtered = await getCards({}); + expect(filtered).toHaveLength(allCards); + }); + + test("getCards with a primary filter filters card correctly", async () => { + expect(Database).toBeTruthy(); + const filtered = await getCards({ + Types: ["Troublemaker"] + }); + for (const card of filtered) { + expect(card.Type).toBe("Troublemaker"); + } + }); + + test("getCards with a secondary filter filters card correctly", async () => { + expect(Database).toBeTruthy(); + const filtered = await getCards({ + Name: "Rainbow Dash" + }); + for (const card of filtered) { + expect(cardFullName(card).indexOf("Rainbow Dash") >= 0).toBeTruthy(); + } + }); +}); diff --git a/src/tests/unit/network.spec.ts b/src/tests/unit/network.spec.ts index dfe425d..4d88a1c 100644 --- a/src/tests/unit/network.spec.ts +++ b/src/tests/unit/network.spec.ts @@ -1,6 +1,5 @@ -import { MockHelper } from "@/testing"; +import { MockHelper, EventHook } from "@/testing"; import { NetworkMessage, LocalClient, ChatMessage } from "@/network"; -import { EventHook } from "@/testing/EventHook"; const sampleRoom = () => ({ max_players: 3, diff --git a/src/views/DeckBuilder.vue b/src/views/DeckBuilder.vue index 68e11ee..ef2b9d3 100644 --- a/src/views/DeckBuilder.vue +++ b/src/views/DeckBuilder.vue @@ -1,14 +1,470 @@ - + diff --git a/src/views/Draft.vue b/src/views/Draft.vue index 9f94af5..e274df2 100644 --- a/src/views/Draft.vue +++ b/src/views/Draft.vue @@ -3,8 +3,12 @@
Players
-
Card pool
-
Cards
+
+ Card pool +
+
+ Cards +
@@ -12,8 +16,8 @@ .draftview { display: grid; height: 100vh; - grid-gap: 10px; - grid-template-columns: 200px 1fr 250px; + gap: 10px; + grid-template-columns: minmax(200px, 1fr) 3fr minmax(250px, 1fr); section { grid-row: 1; border: 1px solid #555; diff --git a/yarn.lock b/yarn.lock index 6cdb382..c83d140 100644 --- a/yarn.lock +++ b/yarn.lock @@ -573,6 +573,14 @@ "@babel/helper-regex" "^7.4.4" regexpu-core "^4.5.4" +"@babel/polyfill@^7.0.0": + version "7.6.0" + resolved "https://registry.yarnpkg.com/@babel/polyfill/-/polyfill-7.6.0.tgz#6d89203f8b6cd323e8d946e47774ea35dc0619cc" + integrity sha512-q5BZJI0n/B10VaQQvln1IlDK3BTBJFbADx7tv+oXDPIDZuTo37H5Adb9jhlXm/fEN4Y7/64qD9mnrJJG7rmaTw== + dependencies: + core-js "^2.6.5" + regenerator-runtime "^0.13.2" + "@babel/preset-env@^7.0.0 < 7.4.0": version "7.3.4" resolved "https://registry.yarnpkg.com/@babel/preset-env/-/preset-env-7.3.4.tgz#887cf38b6d23c82f19b5135298bdb160062e33e1" @@ -1488,6 +1496,11 @@ argparse@^1.0.7: dependencies: sprintf-js "~1.0.2" +argsarray@^0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/argsarray/-/argsarray-0.0.1.tgz#6e7207b4ecdb39b0af88303fa5ae22bda8df61cb" + integrity sha1-bnIHtOzbObCviDA/pa4ivajfYcs= + arr-diff@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/arr-diff/-/arr-diff-2.0.0.tgz#8f3b827f955a8bd669697e4a4256ac3ceae356cf" @@ -1656,6 +1669,14 @@ aws4@^1.8.0: resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.8.0.tgz#f0e003d9ca9e7f59c7a508945d7b2ef9a04a542f" integrity sha512-ReZxvNHIOv88FlT7rxcXIIC0fPt4KZqZbOlivyWtXLt8ESx84zd3kMC6iK5jVeS2qt+g7ftS7ye4fi06X5rtRQ== +axios@^0.18.0: + version "0.18.1" + resolved "https://registry.yarnpkg.com/axios/-/axios-0.18.1.tgz#ff3f0de2e7b5d180e757ad98000f1081b87bcea3" + integrity sha512-0BfJq4NSfQXd+SkFdrvFbG7addhYSBA2mQwISr46pD6E5iqkWg02RAs8vyTT/j0RTnoYmeXauBuSv1qKwR179g== + dependencies: + follow-redirects "1.5.10" + is-buffer "^2.0.2" + babel-code-frame@^6.22.0, babel-code-frame@^6.26.0: version "6.26.0" resolved "https://registry.yarnpkg.com/babel-code-frame/-/babel-code-frame-6.26.0.tgz#63fd43f7dc1e3bb7ce35947db8fe369a3f58c74b" @@ -1909,6 +1930,11 @@ balanced-match@^1.0.0: resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767" integrity sha1-ibTRmasr7kneFk6gK4nORi1xt2c= +base64-arraybuffer-es6@0.4.2: + version "0.4.2" + resolved "https://registry.yarnpkg.com/base64-arraybuffer-es6/-/base64-arraybuffer-es6-0.4.2.tgz#b567d364065843113589b6c1436bd9492701c7fe" + integrity sha512-HaJx92u12By863ZXVHZs4Bp1nkKaLpbs3Ec9SI1OKzq60Hz+Ks6z7UvdD8pIx61Ck3e8F9MH/IPEu5T0xKSbkQ== + base64-js@^1.0.2: version "1.3.1" resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.3.1.tgz#58ece8cb75dd07e71ed08c736abc5fac4dbf8df1" @@ -2199,6 +2225,11 @@ builtin-status-codes@^3.0.0: resolved "https://registry.yarnpkg.com/builtin-status-codes/-/builtin-status-codes-3.0.0.tgz#85982878e21b98e1c66425e03d0174788f569ee8" integrity sha1-hZgoeOIbmOHGZCXgPQF0eI9Wnug= +bulma-prefers-dark@^0.1.0-beta.0: + version "0.1.0-beta.0" + resolved "https://registry.yarnpkg.com/bulma-prefers-dark/-/bulma-prefers-dark-0.1.0-beta.0.tgz#646350738ed00ac66d0f84ec6821a677aa1a66c5" + integrity sha512-EeDW8pQrkYEOXo2l3WykfghbUzi8jlQWGI+Cu2HwmXwQHMcoGF6yiKYCNShttN+8z3atq8fLWh3B7pqXUV4fBA== + bulma@0.7.5: version "0.7.5" resolved "https://registry.yarnpkg.com/bulma/-/bulma-0.7.5.tgz#35066c37f82c088b68f94450be758fc00a967208" @@ -3169,6 +3200,13 @@ debug@2.6.9, debug@^2.2.0, debug@^2.3.3, debug@^2.6.8, debug@^2.6.9: dependencies: ms "2.0.0" +debug@=3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/debug/-/debug-3.1.0.tgz#5bb5a0672628b64149566ba16819e61518c67261" + integrity sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g== + dependencies: + ms "2.0.0" + debug@^3.0.0, debug@^3.1.0, debug@^3.2.5, debug@^3.2.6: version "3.2.6" resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.6.tgz#e83d17de16d8a7efb7717edbe5fb10135eee629b" @@ -3906,6 +3944,13 @@ eventsource@^1.0.7: dependencies: original "^1.0.0" +eventtargeter@0.5.0: + version "0.5.0" + resolved "https://registry.yarnpkg.com/eventtargeter/-/eventtargeter-0.5.0.tgz#ab5e05cc7d96bef6a05a0a4a7053bf8fb7621ba7" + integrity sha512-FQbP+ToTYLKEF3VpyaciNbaexbvIOrXW1V1Hg7kKCT+AiX6sq8rUn1NIQiYEpA04eWzHpopH/QRHqm3K2KnLtQ== + dependencies: + "@babel/polyfill" "^7.0.0" + evp_bytestokey@^1.0.0, evp_bytestokey@^1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/evp_bytestokey/-/evp_bytestokey-1.0.3.tgz#7fcbdb198dc71959432efe13842684e0525acb02" @@ -4377,6 +4422,13 @@ flush-write-stream@^1.0.0: inherits "^2.0.3" readable-stream "^2.3.6" +follow-redirects@1.5.10: + version "1.5.10" + resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.5.10.tgz#7b7a9f9aea2fdff36786a94ff643ed07f4ff5e2a" + integrity sha512-0V5l4Cizzvqt5D44aTXbFZz+FtyXV1vrDN6qrelxtfYQKW0KO0W2T/hkE8xvGa/540LkZlkaUjO4ailYTFtHVQ== + dependencies: + debug "=3.1.0" + follow-redirects@^1.0.0: version "1.8.1" resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.8.1.tgz#24804f9eaab67160b0e840c085885d606371a35b" @@ -5081,6 +5133,11 @@ ignore@^4.0.3, ignore@^4.0.6: resolved "https://registry.yarnpkg.com/ignore/-/ignore-4.0.6.tgz#750e3db5862087b4737ebac8207ffd1ef27b25fc" integrity sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg== +immediate@^3.2.2: + version "3.2.3" + resolved "https://registry.yarnpkg.com/immediate/-/immediate-3.2.3.tgz#d140fa8f614659bd6541233097ddaac25cdd991c" + integrity sha1-0UD6j2FGWb1lQSMwl92qwlzdmRw= + import-cwd@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/import-cwd/-/import-cwd-2.1.0.tgz#aa6cf36e722761285cb371ec6519f53e2435b0a9" @@ -5144,6 +5201,18 @@ indent-string@^2.1.0: dependencies: repeating "^2.0.0" +indexeddbshim@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/indexeddbshim/-/indexeddbshim-4.1.0.tgz#6fffc99a2e302445c9df7b3366ef072c9925225a" + integrity sha512-gnhy0Fz1fWU9pnIo16uKC9dGimsv/vKlXzZ9zasN2EUkx/KxtHkCIfR8I1XRqujsuTelQmn1/34RpDFycNVxtw== + dependencies: + "@babel/polyfill" "^7.0.0" + eventtargeter "0.5.0" + sync-promise "git+https://github.com/brettz9/sync-promise.git#full-sync-missing-promise-features" + typeson "5.11.0" + typeson-registry "1.0.0-alpha.26" + websql "git+https://github.com/brettz9/node-websql.git#configurable-secure2" + indexes-of@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/indexes-of/-/indexes-of-1.0.1.tgz#f30f716c8e2bd346c7b67d3df3915566a7c05607" @@ -5343,6 +5412,11 @@ is-buffer@^1.1.5: resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.6.tgz#efaa2ea9daa0d7ab2ea13a97b2b8ad51fefbe8be" integrity sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w== +is-buffer@^2.0.2: + version "2.0.3" + resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-2.0.3.tgz#4ecf3fcf749cbd1e472689e109ac66261a25e725" + integrity sha512-U15Q7MXTuZlrbymiz95PJpZxu8IlipAp4dtS3wOdgPXx3mqBnslrWU14kxfHB+Py/+2PVKSr37dMAgM2A4uArw== + is-callable@^1.1.4: version "1.1.4" resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.1.4.tgz#1e1adf219e1eeb684d691f9d6a05ff0d30a24d75" @@ -7092,6 +7166,22 @@ node-notifier@^5.2.1: shellwords "^0.1.1" which "^1.3.0" +node-pre-gyp@^0.11.0: + version "0.11.0" + resolved "https://registry.yarnpkg.com/node-pre-gyp/-/node-pre-gyp-0.11.0.tgz#db1f33215272f692cd38f03238e3e9b47c5dd054" + integrity sha512-TwWAOZb0j7e9eGaf9esRx3ZcLaE5tQ2lvYy1pb5IAaG1a2e2Kv5Lms1Y4hpj+ciXJRofIxxlt5haeQ/2ANeE0Q== + dependencies: + detect-libc "^1.0.2" + mkdirp "^0.5.1" + needle "^2.2.1" + nopt "^4.0.1" + npm-packlist "^1.1.6" + npmlog "^4.0.2" + rc "^1.2.7" + rimraf "^2.6.1" + semver "^5.3.0" + tar "^4" + node-pre-gyp@^0.12.0: version "0.12.0" resolved "https://registry.yarnpkg.com/node-pre-gyp/-/node-pre-gyp-0.12.0.tgz#39ba4bb1439da030295f899e3b520b7785766149" @@ -7138,6 +7228,11 @@ node-sass@^4.9.0: stdout-stream "^1.4.0" "true-case-path" "^1.0.2" +noop-fn@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/noop-fn/-/noop-fn-1.0.0.tgz#5f33d47f13d2150df93e0cb036699e982f78ffbf" + integrity sha1-XzPUfxPSFQ35PgywNmmemC94/78= + "nopt@2 || 3": version "3.0.6" resolved "https://registry.yarnpkg.com/nopt/-/nopt-3.0.6.tgz#c6465dbf08abcd4db359317f79ac68a646b28ff9" @@ -9381,6 +9476,15 @@ sprintf-js@~1.0.2: resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c" integrity sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw= +sqlite3@^4.0.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/sqlite3/-/sqlite3-4.1.0.tgz#e051fb9c133be15726322a69e2e37ec560368380" + integrity sha512-RvqoKxq+8pDHsJo7aXxsFR18i+dU2Wp5o12qAJOV5LNcDt+fgJsc2QKKg3sIRfXrN9ZjzY1T7SNe/DFVqAXjaw== + dependencies: + nan "^2.12.1" + node-pre-gyp "^0.11.0" + request "^2.87.0" + sshpk@^1.7.0: version "1.16.1" resolved "https://registry.yarnpkg.com/sshpk/-/sshpk-1.16.1.tgz#fb661c0bef29b39db40769ee39fa70093d6f6877" @@ -9694,6 +9798,10 @@ symbol-tree@^3.2.2: resolved "https://registry.yarnpkg.com/symbol-tree/-/symbol-tree-3.2.4.tgz#430637d248ba77e078883951fb9aa0eed7c63fa2" integrity sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw== +"sync-promise@git+https://github.com/brettz9/sync-promise.git#full-sync-missing-promise-features": + version "1.0.1" + resolved "git+https://github.com/brettz9/sync-promise.git#25845a49a00aa2d2c985a5149b97c86a1fcdc75a" + table@4.0.2: version "4.0.2" resolved "https://registry.yarnpkg.com/table/-/table-4.0.2.tgz#a33447375391e766ad34d3486e6e2aedc84d2e36" @@ -9841,6 +9949,11 @@ timsort@^0.3.0: resolved "https://registry.yarnpkg.com/timsort/-/timsort-0.3.0.tgz#405411a8e7e6339fe64db9a234de11dc31e02bd4" integrity sha1-QFQRqOfmM5/mTbmiNN4R3DHgK9Q= +tiny-queue@^0.2.1: + version "0.2.1" + resolved "https://registry.yarnpkg.com/tiny-queue/-/tiny-queue-0.2.1.tgz#25a67f2c6e253b2ca941977b5ef7442ef97a6046" + integrity sha1-JaZ/LG4lOyypQZd7XvdELvl6YEY= + tmp@^0.0.33: version "0.0.33" resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.0.33.tgz#6d34335889768d21b2bcda0aa277ced3b1bfadf9" @@ -10083,6 +10196,21 @@ typescript@^3.4.3: resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.6.2.tgz#105b0f1934119dde543ac8eb71af3a91009efe54" integrity sha512-lmQ4L+J6mnu3xweP8+rOrUwzmN+MRAj7TgtJtDaXE5PMyX2kCrklhg3rvOsOIfNeAWMQWO2F1GPc1kMD2vLAfw== +typeson-registry@1.0.0-alpha.26: + version "1.0.0-alpha.26" + resolved "https://registry.yarnpkg.com/typeson-registry/-/typeson-registry-1.0.0-alpha.26.tgz#d1f337584196c5d5d112ad981e0dbbd2ced30c30" + integrity sha512-R0wwXIYSiJMh+1XfvyUsCnEGVERoJcNrMl9e/ka30dJ+gQyh4/0NU9WHaqUm8oHtZzZYCz+A5fDRCiXYIq7H1Q== + dependencies: + base64-arraybuffer-es6 "0.4.2" + typeson "5.11.0" + uuid "3.3.2" + whatwg-url "7.0.0" + +typeson@5.11.0: + version "5.11.0" + resolved "https://registry.yarnpkg.com/typeson/-/typeson-5.11.0.tgz#a8273f00050be9eeef974aaa04a0c95a394f821a" + integrity sha512-S5KtLzcU4dr4BXh8VuJDYugsRGsDQYlumCbrmwuAX1a1GNpbVYK4p9wluCIfTVPFvVyV6wRfExXX6Q1+YDItEQ== + uglify-js@3.4.x: version "3.4.10" resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-3.4.10.tgz#9ad9563d8eb3acdfb8d38597d2af1d815f6a755f" @@ -10268,6 +10396,11 @@ utils-merge@1.0.1: resolved "https://registry.yarnpkg.com/utils-merge/-/utils-merge-1.0.1.tgz#9f95710f50a267947b2ccc124741c1028427e713" integrity sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM= +uuid@3.3.2: + version "3.3.2" + resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.3.2.tgz#1b4af4955eb3077c501c23872fc6513811587131" + integrity sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA== + uuid@^3.0.1, uuid@^3.3.2: version "3.3.3" resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.3.3.tgz#4568f0216e78760ee1dbf3a4d2cf53e224112866" @@ -10310,6 +10443,11 @@ vue-class-component@^7.0.1, vue-class-component@^7.0.2: resolved "https://registry.yarnpkg.com/vue-class-component/-/vue-class-component-7.1.0.tgz#b33efcb10e17236d684f70b1e96f1946ec793e87" integrity sha512-G9152NzUkz0i0xTfhk0Afc8vzdXxDR1pfN4dTwE72cskkgJtdXfrKBkMfGvDuxUh35U500g5Ve4xL8PEGdWeHg== +vue-cli-plugin-axios@^0.0.4: + version "0.0.4" + resolved "https://registry.yarnpkg.com/vue-cli-plugin-axios/-/vue-cli-plugin-axios-0.0.4.tgz#29d4eb48275c7fe15b92e1fd5d95fbe2a966436f" + integrity sha512-p2b/fvPJuPBnvU8027PAAuU5DiOzUn2lku8XLG/f6c8FU0N+/MXWZAlOuHhqd9e7+KIZitwe/c8qlmv7TglbTg== + vue-cli-plugin-buefy@^0.3.7: version "0.3.7" resolved "https://registry.yarnpkg.com/vue-cli-plugin-buefy/-/vue-cli-plugin-buefy-0.3.7.tgz#31e5637529482a5a4564676f539db16278b0895c" @@ -10619,6 +10757,16 @@ websocket-extensions@>=0.1.1: resolved "https://registry.yarnpkg.com/websocket-extensions/-/websocket-extensions-0.1.3.tgz#5d2ff22977003ec687a4b87073dfbbac146ccf29" integrity sha512-nqHUnMXmBzT0w570r2JpJxfiSD1IzoI+HGVdd3aZ0yNi3ngvQ4jv1dtHt5VGxfI2yj5yqImPhOK4vmIh2xMbGg== +"websql@git+https://github.com/brettz9/node-websql.git#configurable-secure2": + version "1.0.0" + resolved "git+https://github.com/brettz9/node-websql.git#5149bc0763376ca757fc32dc74345ada0467bfbb" + dependencies: + argsarray "^0.0.1" + immediate "^3.2.2" + noop-fn "^1.0.0" + sqlite3 "^4.0.0" + tiny-queue "^0.2.1" + whatwg-encoding@^1.0.1, whatwg-encoding@^1.0.3: version "1.0.5" resolved "https://registry.yarnpkg.com/whatwg-encoding/-/whatwg-encoding-1.0.5.tgz#5abacf777c32166a51d085d6b4f3e7d27113ddb0" @@ -10631,19 +10779,19 @@ whatwg-mimetype@^2.1.0, whatwg-mimetype@^2.2.0: resolved "https://registry.yarnpkg.com/whatwg-mimetype/-/whatwg-mimetype-2.3.0.tgz#3d4b1e0312d2079879f826aff18dbeeca5960fbf" integrity sha512-M4yMwr6mAnQz76TbJm914+gPpB/nCwvZbJU28cUD6dR004SAxDLOOSUaB1JDRqLtaOV/vi0IC5lEAGFgrjGv/g== -whatwg-url@^6.4.1: - version "6.5.0" - resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-6.5.0.tgz#f2df02bff176fd65070df74ad5ccbb5a199965a8" - integrity sha512-rhRZRqx/TLJQWUpQ6bmrt2UV4f0HCQ463yQuONJqC6fO2VoEb1pTYddbe59SkYq87aoM5A3bdhMZiUiVws+fzQ== +whatwg-url@7.0.0, whatwg-url@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-7.0.0.tgz#fde926fa54a599f3adf82dff25a9f7be02dc6edd" + integrity sha512-37GeVSIJ3kn1JgKyjiYNmSLP1yzbpb29jdmwBSgkD9h40/hyrR/OifpVUndji3tmwGgD8qpw7iQu3RSbCrBpsQ== dependencies: lodash.sortby "^4.7.0" tr46 "^1.0.1" webidl-conversions "^4.0.2" -whatwg-url@^7.0.0: - version "7.0.0" - resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-7.0.0.tgz#fde926fa54a599f3adf82dff25a9f7be02dc6edd" - integrity sha512-37GeVSIJ3kn1JgKyjiYNmSLP1yzbpb29jdmwBSgkD9h40/hyrR/OifpVUndji3tmwGgD8qpw7iQu3RSbCrBpsQ== +whatwg-url@^6.4.1: + version "6.5.0" + resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-6.5.0.tgz#f2df02bff176fd65070df74ad5ccbb5a199965a8" + integrity sha512-rhRZRqx/TLJQWUpQ6bmrt2UV4f0HCQ463yQuONJqC6fO2VoEb1pTYddbe59SkYq87aoM5A3bdhMZiUiVws+fzQ== dependencies: lodash.sortby "^4.7.0" tr46 "^1.0.1"