First Alpha

This commit is contained in:
Fabian 2019-03-24 21:25:08 -04:00
parent 3ef36ab6ca
commit 8b6c71247f
30 changed files with 636 additions and 478 deletions

1
.gitignore vendored
View File

@ -2,3 +2,4 @@
.vscode/ .vscode/
dist/ dist/
node_modules/ node_modules/
.*

427
package-lock.json generated
View File

@ -5,33 +5,50 @@
"requires": true, "requires": true,
"dependencies": { "dependencies": {
"@babel/runtime": { "@babel/runtime": {
"version": "7.3.4", "version": "7.4.2",
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.3.4.tgz", "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.4.2.tgz",
"integrity": "sha512-IvfvnMdSaLBateu0jfsYIpZTxAc2cKEXEMiezGGN75QcBcecDUKd3PgLAncT0oOgxKy8dd8hrJKj9MfzgfZd6g==", "integrity": "sha512-7Bl2rALb7HpvXFL7TETNzKSAeBVCPHELzc0C//9FCxN8nsiueWSJBqaF+2oIJScyILStASR/Cx5WMkXGYTiJFA==",
"dev": true, "dev": true,
"requires": { "requires": {
"regenerator-runtime": "^0.12.0" "regenerator-runtime": "^0.13.2"
} }
}, },
"@hibas123/secure-file-wrapper": { "@hibas123/secure-file-wrapper": {
"version": "2.3.1", "version": "2.4.0",
"resolved": "https://registry.npmjs.org/@hibas123/secure-file-wrapper/-/secure-file-wrapper-2.3.1.tgz", "resolved": "https://registry.npmjs.org/@hibas123/secure-file-wrapper/-/secure-file-wrapper-2.4.0.tgz",
"integrity": "sha512-AwseTodeEvKnYWXudJbw3SiJX6aLI7l78tWgd+5gRF7zlhXBqquqhiViM6bJZdQqki0223Zv+Zbi3kALbnVHaQ==", "integrity": "sha512-YNFsivgp/6XAooepaTicW21VKmAqso/5vR28V3RzRRcCPVfRWu9gsNPNTLpFRJ7wypyF5+nAb2U00VJmDypSTg==",
"requires": { "requires": {
"cross-fetch": "^3.0.0", "@hibas123/utils": "^2.0.0",
"cross-fetch": "^3.0.1",
"uuid": "^3.3.2" "uuid": "^3.3.2"
},
"dependencies": {
"cross-fetch": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-3.0.1.tgz",
"integrity": "sha512-qWtpgBAF8ioqBOddRD+pHhrdzm/UWOArkrlIU7c08DlNbOxo5GfUbSY2vr90ZypWf0raW+HNN1F38pi5CEOjiQ==",
"requires": {
"node-fetch": "2.3.0",
"whatwg-fetch": "3.0.0"
}
}
} }
}, },
"@hibas123/utils": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/@hibas123/utils/-/utils-2.0.0.tgz",
"integrity": "sha512-kczMJvwLGqUgnFamxtPJjb7V+C5tKHc3dtgKYaCQexbzwQ44VNlwTngYNK7izRA5HfD0N+abAGA0IoBMDmKDpg=="
},
"@types/lodash": { "@types/lodash": {
"version": "4.14.121", "version": "4.14.122",
"resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.121.tgz", "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.122.tgz",
"integrity": "sha512-ORj7IBWj13iYufXt/VXrCNMbUuCTJfhzme5kx9U/UtcIPdJYuvPDUAlHlbNhz/8lKCLy9XGIZnGrqXOtQbPGoQ==", "integrity": "sha512-9IdED8wU93ty8gP06ninox+42SBSJHp2IAamsSYMUY76mshRTeUsid/gtbl8ovnOwy8im41ib4cxTiIYMXGKew==",
"dev": true "dev": true
}, },
"@types/lodash.clonedeep": { "@types/lodash.clonedeep": {
"version": "4.5.5", "version": "4.5.6",
"resolved": "https://registry.npmjs.org/@types/lodash.clonedeep/-/lodash.clonedeep-4.5.5.tgz", "resolved": "https://registry.npmjs.org/@types/lodash.clonedeep/-/lodash.clonedeep-4.5.6.tgz",
"integrity": "sha512-TBEia6gB1KDpKcxldeLlacbTrNDZcRh5wIPRG98yWrzF8ItyZGr5qQSsWbIBPnzhngBqYnwNvvImzSDuBRig2w==", "integrity": "sha512-cE1jYr2dEg1wBImvXlNtp0xDoS79rfEdGozQVgliDZj1uERH4k+rmEMTudP9b4VQ8O6nRb5gPqft0QzEQGMQgA==",
"dev": true, "dev": true,
"requires": { "requires": {
"@types/lodash": "*" "@types/lodash": "*"
@ -1413,13 +1430,14 @@
"dev": true "dev": true
}, },
"copy-webpack-plugin": { "copy-webpack-plugin": {
"version": "5.0.0", "version": "5.0.2",
"resolved": "https://registry.npmjs.org/copy-webpack-plugin/-/copy-webpack-plugin-5.0.0.tgz", "resolved": "https://registry.npmjs.org/copy-webpack-plugin/-/copy-webpack-plugin-5.0.2.tgz",
"integrity": "sha512-iiDj+8nnZeW/i8vYJ3+ABSZkOefJnDYIGLojiZKKFDvf1wcEInABXH1+hN7axQMn04qvJxKjgVOee0e14XPtCg==", "integrity": "sha512-7nC7EynPrnBTtBwwbG1aTqrfNS1aTb9eEjSmQDqFtKAsJrR3uDb+pCDIFT2LzhW+SgGJxQcYzThrmXzzZ720uw==",
"dev": true, "dev": true,
"requires": { "requires": {
"cacache": "^11.3.1", "cacache": "^11.3.1",
"find-cache-dir": "^2.0.0", "find-cache-dir": "^2.0.0",
"glob-parent": "^3.1.0",
"globby": "^7.1.1", "globby": "^7.1.1",
"is-glob": "^4.0.0", "is-glob": "^4.0.0",
"loader-utils": "^1.1.0", "loader-utils": "^1.1.0",
@ -1532,32 +1550,30 @@
"integrity": "sha1-/aGedh/Ad+Af+/3G6f38WeiAbNg=" "integrity": "sha1-/aGedh/Ad+Af+/3G6f38WeiAbNg="
}, },
"css-loader": { "css-loader": {
"version": "2.1.0", "version": "2.1.1",
"resolved": "https://registry.npmjs.org/css-loader/-/css-loader-2.1.0.tgz", "resolved": "https://registry.npmjs.org/css-loader/-/css-loader-2.1.1.tgz",
"integrity": "sha512-MoOu+CStsGrSt5K2OeZ89q3Snf+IkxRfAIt9aAKg4piioTrhtP1iEFPu+OVn3Ohz24FO6L+rw9UJxBILiSBw5Q==", "integrity": "sha512-OcKJU/lt232vl1P9EEDamhoO9iKY3tIjY5GU+XDLblAykTdgs6Ux9P1hTHve8nFKy5KPpOXOsVI/hIwi3841+w==",
"dev": true, "dev": true,
"requires": { "requires": {
"icss-utils": "^4.0.0", "camelcase": "^5.2.0",
"loader-utils": "^1.2.1", "icss-utils": "^4.1.0",
"lodash": "^4.17.11", "loader-utils": "^1.2.3",
"postcss": "^7.0.6", "normalize-path": "^3.0.0",
"postcss": "^7.0.14",
"postcss-modules-extract-imports": "^2.0.0", "postcss-modules-extract-imports": "^2.0.0",
"postcss-modules-local-by-default": "^2.0.3", "postcss-modules-local-by-default": "^2.0.6",
"postcss-modules-scope": "^2.0.0", "postcss-modules-scope": "^2.1.0",
"postcss-modules-values": "^2.0.0", "postcss-modules-values": "^2.0.0",
"postcss-value-parser": "^3.3.0", "postcss-value-parser": "^3.3.0",
"schema-utils": "^1.0.0" "schema-utils": "^1.0.0"
}
}, },
"css-selector-tokenizer": { "dependencies": {
"version": "0.7.1", "camelcase": {
"resolved": "https://registry.npmjs.org/css-selector-tokenizer/-/css-selector-tokenizer-0.7.1.tgz", "version": "5.2.0",
"integrity": "sha512-xYL0AMZJ4gFzJQsHUKa5jiWWi2vH77WVNg7JYRyewwj6oPh4yb/y6Y9ZCw9dsj/9UauMhtuxR+ogQd//EdEVNA==", "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.2.0.tgz",
"dev": true, "integrity": "sha512-IXFsBS2pC+X0j0N/GE7Dm7j3bsEBp+oTpb7F50dwEVX7rf3IgwO9XatnegTsDtniKCUtEJH4fSU6Asw7uoVLfQ==",
"requires": { "dev": true
"cssesc": "^0.1.0", }
"fastparse": "^1.1.1",
"regexpu-core": "^1.0.0"
} }
}, },
"css-what": { "css-what": {
@ -1567,9 +1583,9 @@
"dev": true "dev": true
}, },
"cssesc": { "cssesc": {
"version": "0.1.0", "version": "3.0.0",
"resolved": "https://registry.npmjs.org/cssesc/-/cssesc-0.1.0.tgz", "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz",
"integrity": "sha1-yBSQPkViM3GgR3tAEJqq++6t27Q=", "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==",
"dev": true "dev": true
}, },
"currently-unhandled": { "currently-unhandled": {
@ -2329,12 +2345,6 @@
"integrity": "sha1-1RQsDK7msRifh9OnYREGT4bIu/I=", "integrity": "sha1-1RQsDK7msRifh9OnYREGT4bIu/I=",
"dev": true "dev": true
}, },
"fastparse": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/fastparse/-/fastparse-1.1.2.tgz",
"integrity": "sha512-483XLLxTVIwWK3QTrMGRqUfUpoOs/0hbQrl2oz4J0pAcm3A3bu84wxTFqGqkJzewCLdME38xJLJAxBABfQT8sQ==",
"dev": true
},
"faye-websocket": { "faye-websocket": {
"version": "0.10.0", "version": "0.10.0",
"resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.10.0.tgz", "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.10.0.tgz",
@ -3500,9 +3510,9 @@
} }
}, },
"hoek": { "hoek": {
"version": "4.2.1", "version": "6.1.2",
"resolved": "https://registry.npmjs.org/hoek/-/hoek-4.2.1.tgz", "resolved": "https://registry.npmjs.org/hoek/-/hoek-6.1.2.tgz",
"integrity": "sha512-QLg82fGkfnJ/4iy1xZ81/9SIJiq1NGFUMGs6ParyjBZr6jW2Ufj/snDqTHixNlHdPNwN2RLVD0Pi3igeK9+JfA==", "integrity": "sha512-6qhh/wahGYZHFSFw12tBbJw5fsAhhwrrG/y3Cs0YMTv2WzMnL0oLPnQJjv1QJvEfylRSOFuP+xCu+tdx0tD16Q==",
"dev": true "dev": true
}, },
"homedir-polyfill": { "homedir-polyfill": {
@ -3686,12 +3696,12 @@
"dev": true "dev": true
}, },
"icss-utils": { "icss-utils": {
"version": "4.0.0", "version": "4.1.0",
"resolved": "https://registry.npmjs.org/icss-utils/-/icss-utils-4.0.0.tgz", "resolved": "https://registry.npmjs.org/icss-utils/-/icss-utils-4.1.0.tgz",
"integrity": "sha512-bA/xGiwWM17qjllIs9X/y0EjsB7e0AV08F3OL8UPsoNkNRibIuu8f1eKTnQ8QO1DteKKTxTUAn+IEWUToIwGOA==", "integrity": "sha512-3DEun4VOeMvSczifM3F2cKQrDQ5Pj6WKhkOq6HD4QTnDUAq8MQRxy5TX6Sy1iY6WPBe4gQ3p5vTECjbIkglkkQ==",
"dev": true, "dev": true,
"requires": { "requires": {
"postcss": "^7.0.5" "postcss": "^7.0.14"
} }
}, },
"idb": { "idb": {
@ -3748,6 +3758,12 @@
"repeating": "^2.0.0" "repeating": "^2.0.0"
} }
}, },
"indexes-of": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/indexes-of/-/indexes-of-1.0.1.tgz",
"integrity": "sha1-8w9xbI4r00bHtn0985FVZqfAVgc=",
"dev": true
},
"indexof": { "indexof": {
"version": "0.0.1", "version": "0.0.1",
"resolved": "https://registry.npmjs.org/indexof/-/indexof-0.0.1.tgz", "resolved": "https://registry.npmjs.org/indexof/-/indexof-0.0.1.tgz",
@ -4080,14 +4096,14 @@
"dev": true "dev": true
}, },
"joi": { "joi": {
"version": "11.4.0", "version": "14.3.1",
"resolved": "https://registry.npmjs.org/joi/-/joi-11.4.0.tgz", "resolved": "https://registry.npmjs.org/joi/-/joi-14.3.1.tgz",
"integrity": "sha512-O7Uw+w/zEWgbL6OcHbyACKSj0PkQeUgmehdoXVSxt92QFCq4+1390Rwh5moI2K/OgC7D8RHRZqHZxT2husMJHA==", "integrity": "sha512-LQDdM+pkOrpAn4Lp+neNIFV3axv1Vna3j38bisbQhETPMANYRbFJFUyOZcOClYvM/hppMhGWuKSFEK9vjrB+bQ==",
"dev": true, "dev": true,
"requires": { "requires": {
"hoek": "4.x.x", "hoek": "6.x.x",
"isemail": "3.x.x", "isemail": "3.x.x",
"topo": "2.x.x" "topo": "3.x.x"
} }
}, },
"js-base64": { "js-base64": {
@ -4118,12 +4134,6 @@
"integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=", "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=",
"dev": true "dev": true
}, },
"jsesc": {
"version": "0.5.0",
"resolved": "https://registry.npmjs.org/jsesc/-/jsesc-0.5.0.tgz",
"integrity": "sha1-597mbjXW/Bb3EP6R1c9p9w8IkR0=",
"dev": true
},
"json-parse-better-errors": { "json-parse-better-errors": {
"version": "1.0.2", "version": "1.0.2",
"resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz", "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz",
@ -4816,6 +4826,11 @@
"lower-case": "^1.1.1" "lower-case": "^1.1.1"
} }
}, },
"node-fetch": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.3.0.tgz",
"integrity": "sha512-MOd8pV3fxENbryESLgVIeaGKrdl+uaYhCSSVkjeOb/31/njTpcis5aWfdqgNlHIrKOLRbMnfPINPOML2CIFeXA=="
},
"node-forge": { "node-forge": {
"version": "0.7.5", "version": "0.7.5",
"resolved": "https://registry.npmjs.org/node-forge/-/node-forge-0.7.5.tgz", "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-0.7.5.tgz",
@ -5410,9 +5425,9 @@
"dev": true "dev": true
}, },
"postcss": { "postcss": {
"version": "7.0.13", "version": "7.0.14",
"resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.13.tgz", "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.14.tgz",
"integrity": "sha512-h8SY6kQTd1wISHWjz+E6cswdhMuyBZRb16pSTv3W4zYZ3/YbyWeJdNUeOXB5IdZqE1U76OUEjjjqsC3z2f3hVg==", "integrity": "sha512-NsbD6XUUMZvBxtQAJuWDJeeC4QFsmWsfozWxCJPWf3M55K9iu2iMDaKqyoOdTJ1R4usBXuxlVFAIo8rZPQD4Bg==",
"dev": true, "dev": true,
"requires": { "requires": {
"chalk": "^2.4.2", "chalk": "^2.4.2",
@ -5478,24 +5493,24 @@
} }
}, },
"postcss-modules-local-by-default": { "postcss-modules-local-by-default": {
"version": "2.0.4", "version": "2.0.6",
"resolved": "https://registry.npmjs.org/postcss-modules-local-by-default/-/postcss-modules-local-by-default-2.0.4.tgz", "resolved": "https://registry.npmjs.org/postcss-modules-local-by-default/-/postcss-modules-local-by-default-2.0.6.tgz",
"integrity": "sha512-WvuSaTKXUqYJbnT7R3YrsNrHv/C5vRfr5VglS4bFOk0MYT4CLBfc/xgExA+x2RftlYgiBDvWmVs191Xv8S8gZQ==", "integrity": "sha512-oLUV5YNkeIBa0yQl7EYnxMgy4N6noxmiwZStaEJUSe2xPMcdNc8WmBQuQCx18H5psYbVxz8zoHk0RAAYZXP9gA==",
"dev": true, "dev": true,
"requires": { "requires": {
"css-selector-tokenizer": "^0.7.0",
"postcss": "^7.0.6", "postcss": "^7.0.6",
"postcss-selector-parser": "^6.0.0",
"postcss-value-parser": "^3.3.1" "postcss-value-parser": "^3.3.1"
} }
}, },
"postcss-modules-scope": { "postcss-modules-scope": {
"version": "2.0.1", "version": "2.1.0",
"resolved": "https://registry.npmjs.org/postcss-modules-scope/-/postcss-modules-scope-2.0.1.tgz", "resolved": "https://registry.npmjs.org/postcss-modules-scope/-/postcss-modules-scope-2.1.0.tgz",
"integrity": "sha512-7+6k9c3/AuZ5c596LJx9n923A/j3nF3ormewYBF1RrIQvjvjXe1xE8V8A1KFyFwXbvnshT6FBZFX0k/F1igneg==", "integrity": "sha512-91Rjps0JnmtUB0cujlc8KIKCsJXWjzuxGeT/+Q2i2HXKZ7nBUeF9YQTZZTNvHVoNYj1AthsjnGLtqDUE0Op79A==",
"dev": true, "dev": true,
"requires": { "requires": {
"css-selector-tokenizer": "^0.7.0", "postcss": "^7.0.6",
"postcss": "^7.0.6" "postcss-selector-parser": "^6.0.0"
} }
}, },
"postcss-modules-values": { "postcss-modules-values": {
@ -5508,6 +5523,17 @@
"postcss": "^7.0.6" "postcss": "^7.0.6"
} }
}, },
"postcss-selector-parser": {
"version": "6.0.2",
"resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.2.tgz",
"integrity": "sha512-36P2QR59jDTOAiIkqEprfJDsoNrvwFei3eCqKd1Y0tUsBimsq39BLp7RD+JWny3WgB1zGhJX8XVePwm9k4wdBg==",
"dev": true,
"requires": {
"cssesc": "^3.0.0",
"indexes-of": "^1.0.1",
"uniq": "^1.0.1"
}
},
"postcss-value-parser": { "postcss-value-parser": {
"version": "3.3.1", "version": "3.3.1",
"resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz",
@ -5531,9 +5557,9 @@
} }
}, },
"pretty-bytes": { "pretty-bytes": {
"version": "4.0.2", "version": "5.1.0",
"resolved": "https://registry.npmjs.org/pretty-bytes/-/pretty-bytes-4.0.2.tgz", "resolved": "https://registry.npmjs.org/pretty-bytes/-/pretty-bytes-5.1.0.tgz",
"integrity": "sha1-sr+C5zUNZcbDOqlaqlpPYyf2HNk=", "integrity": "sha512-wa5+qGVg9Yt7PB6rYm3kXlKzgzgivYTLRandezh43jjRqgyDyP+9YxfJpJiLs9yKD1WeU8/OvtToWpW7255FtA==",
"dev": true "dev": true
}, },
"pretty-error": { "pretty-error": {
@ -5871,16 +5897,10 @@
"strip-indent": "^1.0.1" "strip-indent": "^1.0.1"
} }
}, },
"regenerate": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.0.tgz",
"integrity": "sha512-1G6jJVDWrt0rK99kBjvEtziZNCICAuvIPkSiUFIQxVP06RCVpq3dmDo2oi6ABpYaDYaTRr67BEhL8r1wgEZZKg==",
"dev": true
},
"regenerator-runtime": { "regenerator-runtime": {
"version": "0.12.1", "version": "0.13.2",
"resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.12.1.tgz", "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.2.tgz",
"integrity": "sha512-odxIc1/vDlo4iZcfXqRYFj0vpXFNoGdKMAUieAlFYO6m/nl5e9KR/beGf41z4a1FI+aQgtjhuaSlDxQ0hmkrHg==", "integrity": "sha512-S/TQAZJO+D3m9xeN1WTI8dLKBBiRgXBlTJvbWjCThHWZj9EvHK70Ff50/tYj2J/fvBY6JtFVwRuazHN2E7M9BA==",
"dev": true "dev": true
}, },
"regex-not": { "regex-not": {
@ -5893,32 +5913,6 @@
"safe-regex": "^1.1.0" "safe-regex": "^1.1.0"
} }
}, },
"regexpu-core": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-1.0.0.tgz",
"integrity": "sha1-hqdj9Y7k18L2sQLkdkBQ3n7ZDGs=",
"dev": true,
"requires": {
"regenerate": "^1.2.1",
"regjsgen": "^0.2.0",
"regjsparser": "^0.1.4"
}
},
"regjsgen": {
"version": "0.2.0",
"resolved": "https://registry.npmjs.org/regjsgen/-/regjsgen-0.2.0.tgz",
"integrity": "sha1-bAFq3qxVT3WCP+N6wFuS1aTtsfc=",
"dev": true
},
"regjsparser": {
"version": "0.1.5",
"resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.1.5.tgz",
"integrity": "sha1-fuj4Tcb6eS0/0K4ijSS9lJ6tIFw=",
"dev": true,
"requires": {
"jsesc": "~0.5.0"
}
},
"relateurl": { "relateurl": {
"version": "0.2.7", "version": "0.2.7",
"resolved": "https://registry.npmjs.org/relateurl/-/relateurl-0.2.7.tgz", "resolved": "https://registry.npmjs.org/relateurl/-/relateurl-0.2.7.tgz",
@ -7055,12 +7049,12 @@
} }
}, },
"topo": { "topo": {
"version": "2.0.2", "version": "3.0.3",
"resolved": "https://registry.npmjs.org/topo/-/topo-2.0.2.tgz", "resolved": "https://registry.npmjs.org/topo/-/topo-3.0.3.tgz",
"integrity": "sha1-zVYVdSU5BXwNwEkaYhw7xvvh0YI=", "integrity": "sha512-IgpPtvD4kjrJ7CRA3ov2FhWQADwv+Tdqbsf1ZnPUSAtCJ9e1Z44MmoSGDXGk4IppoZA7jd/QRkNddlLJWlUZsQ==",
"dev": true, "dev": true,
"requires": { "requires": {
"hoek": "4.x.x" "hoek": "6.x.x"
} }
}, },
"toposort": { "toposort": {
@ -7196,9 +7190,9 @@
"dev": true "dev": true
}, },
"typescript": { "typescript": {
"version": "3.3.3333", "version": "3.3.4000",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-3.3.3333.tgz", "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.3.4000.tgz",
"integrity": "sha512-JjSKsAfuHBE/fB2oZ8NxtRTk5iGcg6hkYXMnZ3Wc+b2RSqejEqTaem11mHASMnFilHrax3sLK0GDzcJrekZYLw==", "integrity": "sha512-jjOcCZvpkl2+z7JFn0yBOoLQyLoIkNZAs/fYJkUG6VKy6zLPHJGfQJYFHzibB6GJaF/8QrcECtlQ5cpvRHSMEA==",
"dev": true "dev": true
}, },
"ua-parser-js": { "ua-parser-js": {
@ -7225,6 +7219,11 @@
} }
} }
}, },
"uikit": {
"version": "3.0.3",
"resolved": "https://registry.npmjs.org/uikit/-/uikit-3.0.3.tgz",
"integrity": "sha512-Z73TXlnfJCAXczg5mFCZSTwR0Ii1wW2fZumfIR8G1x1x4SyKHpkU0JCZDoIryYFMu1gSpvy5hZiXk3t4i5tTlw=="
},
"union-value": { "union-value": {
"version": "1.0.0", "version": "1.0.0",
"resolved": "https://registry.npmjs.org/union-value/-/union-value-1.0.0.tgz", "resolved": "https://registry.npmjs.org/union-value/-/union-value-1.0.0.tgz",
@ -7260,6 +7259,12 @@
} }
} }
}, },
"uniq": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/uniq/-/uniq-1.0.1.tgz",
"integrity": "sha1-sxxa6CVIRKOoKBVBzisEuGWnNP8=",
"dev": true
},
"unique-filename": { "unique-filename": {
"version": "1.1.1", "version": "1.1.1",
"resolved": "https://registry.npmjs.org/unique-filename/-/unique-filename-1.1.1.tgz", "resolved": "https://registry.npmjs.org/unique-filename/-/unique-filename-1.1.1.tgz",
@ -7606,9 +7611,9 @@
} }
}, },
"webpack-cli": { "webpack-cli": {
"version": "3.2.3", "version": "3.3.0",
"resolved": "https://registry.npmjs.org/webpack-cli/-/webpack-cli-3.2.3.tgz", "resolved": "https://registry.npmjs.org/webpack-cli/-/webpack-cli-3.3.0.tgz",
"integrity": "sha512-Ik3SjV6uJtWIAN5jp5ZuBMWEAaP5E4V78XJ2nI+paFPh8v4HPSwo/myN0r29Xc/6ZKnd2IdrAlpSgNOu2CDQ6Q==", "integrity": "sha512-t1M7G4z5FhHKJ92WRKwZ1rtvi7rHc0NZoZRbSkol0YKl4HvcC8+DsmGDmK7MmZxHSAetHagiOsjOB6MmzC2TUw==",
"dev": true, "dev": true,
"requires": { "requires": {
"chalk": "^2.4.1", "chalk": "^2.4.1",
@ -7621,7 +7626,7 @@
"loader-utils": "^1.1.0", "loader-utils": "^1.1.0",
"supports-color": "^5.5.0", "supports-color": "^5.5.0",
"v8-compile-cache": "^2.0.2", "v8-compile-cache": "^2.0.2",
"yargs": "^12.0.4" "yargs": "^12.0.5"
}, },
"dependencies": { "dependencies": {
"ansi-regex": { "ansi-regex": {
@ -7640,9 +7645,9 @@
} }
}, },
"camelcase": { "camelcase": {
"version": "5.0.0", "version": "5.2.0",
"resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.0.0.tgz", "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.2.0.tgz",
"integrity": "sha512-faqwZqnWxbxn+F1d399ygeamQNy3lPp/H9H6rNrqYh4FSVCtcY+3cub1MxA8o9mDd55mM8Aghuu/kuyYA6VTsA==", "integrity": "sha512-IXFsBS2pC+X0j0N/GE7Dm7j3bsEBp+oTpb7F50dwEVX7rf3IgwO9XatnegTsDtniKCUtEJH4fSU6Asw7uoVLfQ==",
"dev": true "dev": true
}, },
"chalk": { "chalk": {
@ -8066,168 +8071,168 @@
} }
}, },
"workbox-background-sync": { "workbox-background-sync": {
"version": "4.0.0", "version": "4.1.1",
"resolved": "https://registry.npmjs.org/workbox-background-sync/-/workbox-background-sync-4.0.0.tgz", "resolved": "https://registry.npmjs.org/workbox-background-sync/-/workbox-background-sync-4.1.1.tgz",
"integrity": "sha512-U8hCVqF1m/xaQnRVI4N3eRK9sQwlEH6C5BGORwVs4jBTf1bfo3EDUR2K1qhkWdGgRKBWQfSb2YHVBrxlpt6z8g==", "integrity": "sha512-z8iKAx7f3cfQpGaRrrl2CpP4dGe+vHk05vJbzscwA7e1K8vyNl6zALBtIyyAvEZzMsofsiGEZqt2g/8CfyfQ5g==",
"dev": true, "dev": true,
"requires": { "requires": {
"workbox-core": "^4.0.0" "workbox-core": "^4.1.1"
} }
}, },
"workbox-broadcast-update": { "workbox-broadcast-update": {
"version": "4.0.0", "version": "4.1.1",
"resolved": "https://registry.npmjs.org/workbox-broadcast-update/-/workbox-broadcast-update-4.0.0.tgz", "resolved": "https://registry.npmjs.org/workbox-broadcast-update/-/workbox-broadcast-update-4.1.1.tgz",
"integrity": "sha512-xnnhXdgKU5OLFzc4v6n12O0iX+I4IxEZ2zV9xEQiUXCRWh/8gEcyXFdPK7Tayj5C3NwN1wrQgpSCJ5FBwVr63Q==", "integrity": "sha512-gq83a8F6ESQobfltaxzoUTz0mEpTOsXHmy9Po9kKMT1UjXTWh/4NDF3HwQYaxJckOER9NITB3BuoXlXr3tI8aA==",
"dev": true, "dev": true,
"requires": { "requires": {
"workbox-core": "^4.0.0" "workbox-core": "^4.1.1"
} }
}, },
"workbox-build": { "workbox-build": {
"version": "4.0.0", "version": "4.1.1",
"resolved": "https://registry.npmjs.org/workbox-build/-/workbox-build-4.0.0.tgz", "resolved": "https://registry.npmjs.org/workbox-build/-/workbox-build-4.1.1.tgz",
"integrity": "sha512-xWz4I1ZGVWQpgBq+Q8xsFF0fdHkVM+8N+uoPm+XCujmYlhxry+f87c49Yp/DcV1mHr6YIqcA5lnZu0ix77iacg==", "integrity": "sha512-+QRtNFKDq7RlIpigsh26joUNoEN+c3pQ+yT8Rs29RtpM50S1nKggFUQY0HoRvN7tzvuzIgxCrx3osxOQ8hmj7Q==",
"dev": true, "dev": true,
"requires": { "requires": {
"@babel/runtime": "^7.0.0", "@babel/runtime": "^7.3.4",
"common-tags": "^1.4.0", "common-tags": "^1.8.0",
"fs-extra": "^4.0.2", "fs-extra": "^4.0.2",
"glob": "^7.1.2", "glob": "^7.1.3",
"joi": "^11.1.1", "joi": "^14.3.1",
"lodash.template": "^4.4.0", "lodash.template": "^4.4.0",
"pretty-bytes": "^4.0.2", "pretty-bytes": "^5.1.0",
"stringify-object": "^3.2.2", "stringify-object": "^3.3.0",
"strip-comments": "^1.0.2", "strip-comments": "^1.0.2",
"workbox-background-sync": "^4.0.0", "workbox-background-sync": "^4.1.1",
"workbox-broadcast-update": "^4.0.0", "workbox-broadcast-update": "^4.1.1",
"workbox-cacheable-response": "^4.0.0", "workbox-cacheable-response": "^4.1.1",
"workbox-core": "^4.0.0", "workbox-core": "^4.1.1",
"workbox-expiration": "^4.0.0", "workbox-expiration": "^4.1.1",
"workbox-google-analytics": "^4.0.0", "workbox-google-analytics": "^4.1.1",
"workbox-navigation-preload": "^4.0.0", "workbox-navigation-preload": "^4.1.1",
"workbox-precaching": "^4.0.0", "workbox-precaching": "^4.1.1",
"workbox-range-requests": "^4.0.0", "workbox-range-requests": "^4.1.1",
"workbox-routing": "^4.0.0", "workbox-routing": "^4.1.1",
"workbox-strategies": "^4.0.0", "workbox-strategies": "^4.1.1",
"workbox-streams": "^4.0.0", "workbox-streams": "^4.1.1",
"workbox-sw": "^4.0.0", "workbox-sw": "^4.1.1",
"workbox-window": "^4.0.0" "workbox-window": "^4.1.1"
} }
}, },
"workbox-cacheable-response": { "workbox-cacheable-response": {
"version": "4.0.0", "version": "4.1.1",
"resolved": "https://registry.npmjs.org/workbox-cacheable-response/-/workbox-cacheable-response-4.0.0.tgz", "resolved": "https://registry.npmjs.org/workbox-cacheable-response/-/workbox-cacheable-response-4.1.1.tgz",
"integrity": "sha512-cT3b1iotdV5+rYZKnAWvo3D8UAgfuN2HWuf+WuNC1YR0tnGmFAOX8shfEV9DZmnzxDgY8cOcOCK8PSf5uCaGBg==", "integrity": "sha512-uc1zkeidJgAMXHvUbspKJt3NzXHAcb5D+7sX6HrCZIMneS4ZxMvdB86giIR3bveV4PaOssqIYVrWUJvIehK/NA==",
"dev": true, "dev": true,
"requires": { "requires": {
"workbox-core": "^4.0.0" "workbox-core": "^4.1.1"
} }
}, },
"workbox-core": { "workbox-core": {
"version": "4.0.0", "version": "4.1.1",
"resolved": "https://registry.npmjs.org/workbox-core/-/workbox-core-4.0.0.tgz", "resolved": "https://registry.npmjs.org/workbox-core/-/workbox-core-4.1.1.tgz",
"integrity": "sha512-FRoOUuJBl7COTwvGO5FC9k0VyYGv/LkjVqgVwKk9MXQn/Xi+bvGDcqSVF7qfT+sJ6Ffcr/V+dVMpoZAE/X5e+g==", "integrity": "sha512-RbzMWnDW7UvfstwOs8ERDFTH6zr7akm4wIbIednFs1TnAvZbN3gpIBoEv53kaMr0uMYDSXI2KxaLmmz9WX1PXA==",
"dev": true "dev": true
}, },
"workbox-expiration": { "workbox-expiration": {
"version": "4.0.0", "version": "4.1.1",
"resolved": "https://registry.npmjs.org/workbox-expiration/-/workbox-expiration-4.0.0.tgz", "resolved": "https://registry.npmjs.org/workbox-expiration/-/workbox-expiration-4.1.1.tgz",
"integrity": "sha512-j0h8H8hmSd+Sh9qSlmC1JwViRdxtzbdjuH32qmkYgXDGXwMprV6QU2kQ51J+7Dhm10OdQ64RGfGjbe7bjgzDuw==", "integrity": "sha512-N/fbypqCbFrrKDhVnTyGXhkFTgjA8aRUydkxCpgJM1ajf7udQYD4XWTQxXosPJC2UVsa2/kPCBYFQOQ1Fu/2TA==",
"dev": true, "dev": true,
"requires": { "requires": {
"workbox-core": "^4.0.0" "workbox-core": "^4.1.1"
} }
}, },
"workbox-google-analytics": { "workbox-google-analytics": {
"version": "4.0.0", "version": "4.1.1",
"resolved": "https://registry.npmjs.org/workbox-google-analytics/-/workbox-google-analytics-4.0.0.tgz", "resolved": "https://registry.npmjs.org/workbox-google-analytics/-/workbox-google-analytics-4.1.1.tgz",
"integrity": "sha512-WjZM3frkuCYQUmq8uICzpt+3NnjKhO/GEV+EOqRJgZMfZfmFxMUHbUKi3I/8TXWLigWJbtv/mh4zh0OtFX+vKw==", "integrity": "sha512-ByZYHv61u4dFQXQAXZZ1bNgcJ45yA85C8OAlSDGwqOuv72dZoybG3EMtJo/0ChO6irxWI1pictF2pTW7JxcCkQ==",
"dev": true, "dev": true,
"requires": { "requires": {
"workbox-background-sync": "^4.0.0", "workbox-background-sync": "^4.1.1",
"workbox-core": "^4.0.0", "workbox-core": "^4.1.1",
"workbox-routing": "^4.0.0", "workbox-routing": "^4.1.1",
"workbox-strategies": "^4.0.0" "workbox-strategies": "^4.1.1"
} }
}, },
"workbox-navigation-preload": { "workbox-navigation-preload": {
"version": "4.0.0", "version": "4.1.1",
"resolved": "https://registry.npmjs.org/workbox-navigation-preload/-/workbox-navigation-preload-4.0.0.tgz", "resolved": "https://registry.npmjs.org/workbox-navigation-preload/-/workbox-navigation-preload-4.1.1.tgz",
"integrity": "sha512-G2sDCrekZUNFxkBSAcYQHkUbVMJwhkdVFqnCYxmeTiBzOILokVsghmr90CQ4m98gcqc+P4GoMMc+196jN9r+SA==", "integrity": "sha512-U+QEpcOgakBFZ6Aiv438DTvkZQX518qxfu280kEPZnFU88wIFBAK9V4MmJcoX60fk1INTD//YnfSxI0cLy1N+g==",
"dev": true, "dev": true,
"requires": { "requires": {
"workbox-core": "^4.0.0" "workbox-core": "^4.1.1"
} }
}, },
"workbox-precaching": { "workbox-precaching": {
"version": "4.0.0", "version": "4.1.1",
"resolved": "https://registry.npmjs.org/workbox-precaching/-/workbox-precaching-4.0.0.tgz", "resolved": "https://registry.npmjs.org/workbox-precaching/-/workbox-precaching-4.1.1.tgz",
"integrity": "sha512-juI7N+Rj2/CWU3FXedcDLdjzqvRMpkaQsdsfAxBii5017bivw3Dve/kf4qXePccD2hJ973vKY2F7EJRUos8JvA==", "integrity": "sha512-GuoBH85MzVpzmF8c5Sql1i9HYdOqcpRDdNPLrIkWEfuvURO5M/jT+cGcyfFq35Xo7xRb4kE79H4hnF3EnCkFRw==",
"dev": true, "dev": true,
"requires": { "requires": {
"workbox-core": "^4.0.0" "workbox-core": "^4.1.1"
} }
}, },
"workbox-range-requests": { "workbox-range-requests": {
"version": "4.0.0", "version": "4.1.1",
"resolved": "https://registry.npmjs.org/workbox-range-requests/-/workbox-range-requests-4.0.0.tgz", "resolved": "https://registry.npmjs.org/workbox-range-requests/-/workbox-range-requests-4.1.1.tgz",
"integrity": "sha512-zymg51V1kZAXrzRNNS+da9nnDKdrOfkV/hoYYr0H174c5gIuT3mcwJFr27AhdTrP7wdEMtWFwlOFMyv99lQxTQ==", "integrity": "sha512-i9i7tRTcXveCJdi4lK7XstgHweTwkqEGR7GPauYIDGAZplWrxDOAOUDSvkH8ibOxEgO6f0VFhyYY6fPB6u+oSA==",
"dev": true, "dev": true,
"requires": { "requires": {
"workbox-core": "^4.0.0" "workbox-core": "^4.1.1"
} }
}, },
"workbox-routing": { "workbox-routing": {
"version": "4.0.0", "version": "4.1.1",
"resolved": "https://registry.npmjs.org/workbox-routing/-/workbox-routing-4.0.0.tgz", "resolved": "https://registry.npmjs.org/workbox-routing/-/workbox-routing-4.1.1.tgz",
"integrity": "sha512-NCo/S4E/MGi0LPC54pAQEKncWbV7Px5NtB4pWpq1RES2nLisS9SwA/7RnaESk9XpCFt4K788soQOKGzbTcQFdQ==", "integrity": "sha512-slOb+2Nfn8V3fG/TtN0c0k4OOyuwLSnZUv+zyZeJafSU3MrQPC58bPeG7HOZZDwoQAsBG9VSukjRDFR0F1lXKg==",
"dev": true, "dev": true,
"requires": { "requires": {
"workbox-core": "^4.0.0" "workbox-core": "^4.1.1"
} }
}, },
"workbox-strategies": { "workbox-strategies": {
"version": "4.0.0", "version": "4.1.1",
"resolved": "https://registry.npmjs.org/workbox-strategies/-/workbox-strategies-4.0.0.tgz", "resolved": "https://registry.npmjs.org/workbox-strategies/-/workbox-strategies-4.1.1.tgz",
"integrity": "sha512-837Mk734rhNaqsD1uwWuDjoDddnkfaHWQMyCmBwUzsFb4UnRdInLH9SNIXDm6LgkDjKcoEdKAbp6336De60wLg==", "integrity": "sha512-ejmRqmjwn9DYsl1QVZkRb1V/iaBzhsh3YwJelfXQk68JpB36WjwY9csFQ2gSvlLCCg3d4MVFFxKfmHVyVnhwAA==",
"dev": true, "dev": true,
"requires": { "requires": {
"workbox-core": "^4.0.0" "workbox-core": "^4.1.1"
} }
}, },
"workbox-streams": { "workbox-streams": {
"version": "4.0.0", "version": "4.1.1",
"resolved": "https://registry.npmjs.org/workbox-streams/-/workbox-streams-4.0.0.tgz", "resolved": "https://registry.npmjs.org/workbox-streams/-/workbox-streams-4.1.1.tgz",
"integrity": "sha512-q9HE8r3BoLBZFAj5FWOhVnjcfmQ+lMaUxZS4FK259xeYDTF9wfo8cxN+inJyCeX6nq6jbWo7XWqGlXV88vaFfg==", "integrity": "sha512-6TKC4rrvnjbLpWtgHIYWjWS28h0SqSWogkJIKC1f/6MjJCmi2qM7PYJwXR0/t8lJVZj61ujVSulZ92XQmy3GhQ==",
"dev": true, "dev": true,
"requires": { "requires": {
"workbox-core": "^4.0.0" "workbox-core": "^4.1.1"
} }
}, },
"workbox-sw": { "workbox-sw": {
"version": "4.0.0", "version": "4.1.1",
"resolved": "https://registry.npmjs.org/workbox-sw/-/workbox-sw-4.0.0.tgz", "resolved": "https://registry.npmjs.org/workbox-sw/-/workbox-sw-4.1.1.tgz",
"integrity": "sha512-lVSNtzOPbycn8pSuNtfroT+lXNBry8sUL1494cURIZE1MMFcxCZRv/718LoKIBeJnRYBKa/sXO2GXLzssHX15w==", "integrity": "sha512-3nQFWFyG1W21x7TUVBsobrLoFDEy7ck/3nx2W1I3c+DhLCIu7B+IAnQVdefK+oRju5fIDWwOQ63fok8Uz7E/Gw==",
"dev": true "dev": true
}, },
"workbox-webpack-plugin": { "workbox-webpack-plugin": {
"version": "4.0.0", "version": "4.1.1",
"resolved": "https://registry.npmjs.org/workbox-webpack-plugin/-/workbox-webpack-plugin-4.0.0.tgz", "resolved": "https://registry.npmjs.org/workbox-webpack-plugin/-/workbox-webpack-plugin-4.1.1.tgz",
"integrity": "sha512-J1FGWnOcLySCcV9V0t8bIQKTxrQy4OoIT9aBMjnapwPPFJqFcCmxhnU3ozhJDhZh5nrFzE9gbO16yyMs3DQC6g==", "integrity": "sha512-Fygc8qrh/IOeJeZ4NETs9arYtJEwcO0Yy7JRkX5DSOHCSkWHxOX1ryazAcK0ACyMJOQuU9zJVmx+mnn0zqYKtA==",
"dev": true, "dev": true,
"requires": { "requires": {
"@babel/runtime": "^7.0.0", "@babel/runtime": "^7.0.0",
"json-stable-stringify": "^1.0.1", "json-stable-stringify": "^1.0.1",
"workbox-build": "^4.0.0" "workbox-build": "^4.1.1"
} }
}, },
"workbox-window": { "workbox-window": {
"version": "4.0.0", "version": "4.1.1",
"resolved": "https://registry.npmjs.org/workbox-window/-/workbox-window-4.0.0.tgz", "resolved": "https://registry.npmjs.org/workbox-window/-/workbox-window-4.1.1.tgz",
"integrity": "sha512-gdLzP/L0vu5Gi3pdUYtVNmg0yE7OriEuYZBoLt8AYoILXh6cta9WsWHKQ0/pXfmEmZywr4Dfu0qkLLvc9VlZGQ==", "integrity": "sha512-KadE/DdNY1f6Va3MMOSigheLSgNxWHV/K/iDHnLMpo2EBGVpfwRCOuEwJNHlWA3G5WdpZlyTmtShf/5Mbb6dNg==",
"dev": true, "dev": true,
"requires": { "requires": {
"workbox-core": "^4.0.0" "workbox-core": "^4.1.1"
} }
}, },
"worker-farm": { "worker-farm": {

View File

@ -1,6 +1,6 @@
{ {
"name": "@hibas123/securenotes2", "name": "@hibas123/securenotes2",
"version": "1.0.0", "version": "2.0.0-alpha.1",
"description": "", "description": "",
"main": "index.js", "main": "index.js",
"scripts": { "scripts": {
@ -13,7 +13,8 @@
"author": "Fabian Stamm <dev@fabianstamm.de>", "author": "Fabian Stamm <dev@fabianstamm.de>",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@hibas123/secure-file-wrapper": "^2.3.1", "@hibas123/secure-file-wrapper": "^2.4.0",
"@hibas123/utils": "^2.0.0",
"aes-js": "^3.1.2", "aes-js": "^3.1.2",
"crypto-js": "^3.1.9-1", "crypto-js": "^3.1.9-1",
"feather-icons": "^4.19.0", "feather-icons": "^4.19.0",
@ -23,13 +24,14 @@
"lodash.clonedeep": "^4.5.0", "lodash.clonedeep": "^4.5.0",
"mini.css": "^3.0.1", "mini.css": "^3.0.1",
"secure-file-wrapper": "git+https://git.stamm.me/OpenServer/OSSecureFileWrapper.git", "secure-file-wrapper": "git+https://git.stamm.me/OpenServer/OSSecureFileWrapper.git",
"uikit": "^3.0.3",
"uuid": "^3.3.2" "uuid": "^3.3.2"
}, },
"devDependencies": { "devDependencies": {
"@types/lodash.clonedeep": "^4.5.5", "@types/lodash.clonedeep": "^4.5.6",
"@types/uuid": "^3.4.4", "@types/uuid": "^3.4.4",
"copy-webpack-plugin": "^5.0.0", "copy-webpack-plugin": "^5.0.2",
"css-loader": "^2.1.0", "css-loader": "^2.1.1",
"file-loader": "^3.0.1", "file-loader": "^3.0.1",
"html-webpack-plugin": "^3.2.0", "html-webpack-plugin": "^3.2.0",
"mini-css-extract-plugin": "^0.5.0", "mini-css-extract-plugin": "^0.5.0",
@ -39,13 +41,13 @@
"sass-loader": "^7.1.0", "sass-loader": "^7.1.0",
"style-loader": "^0.23.1", "style-loader": "^0.23.1",
"ts-loader": "^5.3.0", "ts-loader": "^5.3.0",
"typescript": "^3.3.3333", "typescript": "^3.3.4000",
"webpack": "^4.29.6", "webpack": "^4.29.6",
"webpack-bundle-analyzer": "^3.1.0", "webpack-bundle-analyzer": "^3.1.0",
"webpack-cli": "^3.2.3", "webpack-cli": "^3.3.0",
"webpack-dev-server": "^3.2.1", "webpack-dev-server": "^3.2.1",
"webpack-visualizer-plugin": "^0.1.11", "webpack-visualizer-plugin": "^0.1.11",
"workbox-webpack-plugin": "^4.0.0", "workbox-webpack-plugin": "^4.1.1",
"worker-loader": "^2.0.0" "worker-loader": "^2.0.0"
} }
} }

View File

@ -3,7 +3,10 @@
"name": "Secure Notes", "name": "Secure Notes",
"decription": "A place to store your notes securly", "decription": "A place to store your notes securly",
"share_target": { "share_target": {
"action": "/#/share", "url_template": "/#/share?title={title}&text={text}&url={url}",
"method": "GET",
"enctype": "application/x-www-form-urlencoded",
"action": "/",
"params": { "params": {
"title": "title", "title": "title",
"text": "text", "text": "text",

121
public/serviceworker.js Normal file
View File

@ -0,0 +1,121 @@
function log(...params) {
console.log.apply(this, [...["%c[SW]: %c", "color: #f4b942;", "color:unset;"], ...params])
}
const CACHE = "offline";
let precacheFiles = [
"/",
"/main.js",
"/main.css",
"/serviceworker.js"
]
//Install stage sets up the cache-array to configure pre-cache content
self.addEventListener('install', (evt) => {
log('The service worker is being installed.');
evt.waitUntil(precache().then(() => {
log('Skip waiting on install');
}).catch(log).then(() => self.skipWaiting()));
});
//allow sw to control of current page
self.addEventListener('activate', (event) => {
log('Claiming clients for current page');
return self.clients.claim();
});
self.addEventListener('message', (event) => {
if (event.data === "clear_cache") {
log("Clearing cache");
caches.delete(CACHE);
}
if (event.data === "update_index") {
log("Updating index");
event.waitUntil(caches.open(CACHE).then((cache) => {
return cache.addAll(["/", "/index.html"]).then(() => {
event.ports[0].postMessage("reload");
});
}))
}
});
var Types;
(function (Types) {
Types[Types["CACHE"] = 0] = "CACHE";
Types[Types["NOCACHE"] = 1] = "NOCACHE";
Types[Types["REFRESH"] = 2] = "REFRESH";
})(Types || (Types = {}));
let rules = [
{
match: (url) => {
return url.indexOf("/api/") >= 0;
},
type: Types.NOCACHE
},
{
match: (url) => {
return url.indexOf("/version_hash") >= 0;
},
type: Types.NOCACHE
},
{
match: () => {
return true;
},
type: Types.REFRESH
}
]
self.addEventListener('fetch', (evt) => {
if (evt.request.method != 'GET') return; // Dont care about POST requests
let rule = rules.find(rule => rule.match(evt.request.url));
evt.respondWith((async () => {
log("Cache:", Types[rule.type]);
switch (rule.type) {
case Types.CACHE:
return fromCache(evt.request);
case Types.REFRESH:
return refresh(evt.request).then(r => {
evt.waitUntil(r.refresh.catch(_ => { }));
return r.result;
});
case Types.NOCACHE:
return fetch(evt.request);
}
})());
});
async function fromCache(request) {
let cache = await caches.open(CACHE);
let matching = await cache.match(request);
if (matching)
return matching
let res = await fetch(request.clone());
await cache.put(request, res);
return await cache.match(request);
}
async function refresh(request) {
let cache = await caches.open(CACHE);
let web = fetch(request.clone()).then(res => {
return cache.put(request, res).then(() => {
return cache.match(request);
})
})
let matching = await cache.match(request);
return {
result: matching ? matching : web,
refresh: web
}
}
function precache() {
return caches.open(CACHE).then(function (cache) {
return cache.addAll(precacheFiles);
});
}

View File

@ -1,7 +1,6 @@
import { h } from "preact"; import { h } from "preact";
import "./add_button.scss"; import "./add_button.scss";
import Plus from "feather-icons/dist/icons/plus.svg"; import Plus from "feather-icons/dist/icons/plus.svg";
export default function AddButton({ onClick }: { onClick: () => void }) { export default function AddButton({ onClick }: { onClick: () => void }) {
return <button title={"add button"} class="add_button_button circular primary shadowed" onClick={() => onClick()}><Plus width={undefined} height={undefined} /></button> return <button title={"add button"} class="add_button_button circular primary shadowed" onClick={() => onClick()}><Plus width={undefined} height={undefined} /></button>
} }

View File

@ -2,11 +2,13 @@ import { Router } from "./Routing";
import { h } from "preact"; import { h } from "preact";
import NotificationsComponent from "./notifications"; import NotificationsComponent from "./notifications";
import { ModalComponent } from "./modals/Modal"; import { ModalComponent } from "./modals/Modal";
import { Footer } from "./Footer";
export default function App() { export default function App() {
return <div> return <div>
<ModalComponent /> <ModalComponent />
<NotificationsComponent /> <NotificationsComponent />
<Router /> <Router />
<Footer />
</div> </div>
} }

70
src/components/Footer.tsx Normal file
View File

@ -0,0 +1,70 @@
import { h, Component } from "preact";
import Notes from "../notes";
import Refresh from "feather-icons/dist/icons/refresh-cw.svg";
import "./footer.scss";
import Notifications from "../notifications";
export class Footer extends Component<{}, { synced: boolean, syncing: boolean }> {
constructor(props) {
super(props);
this.state = { synced: false, syncing: false };
this.onSyncedChange = this.onSyncedChange.bind(this);
this.onSyncChange = this.onSyncChange.bind(this);
}
async componentWillMount() {
Notes.syncedObservable.subscribe(this.onSyncedChange);
Notes.syncObservable.subscribe(this.onSyncChange);
this.setState({ synced: await Notes.isSynced() })
}
componentWillUnmount() {
Notes.syncedObservable.unsubscribe(this.onSyncedChange);
Notes.syncObservable.unsubscribe(this.onSyncChange);
}
onSyncChange(state: boolean) {
console.log("sync", state);
this.setState({ syncing: state })
}
onSyncedChange(state: boolean) {
console.log("synced", state);
this.setState({ synced: state })
}
onSyncClick() {
Notes.sync().then(() => {
Notifications.sendInfo("Finished Synchronisation");
})
}
render() {
let extrac = "";
let color;
let text;
if (this.state.syncing) {
color = "orange";
text = "syncing";
extrac = "reloading"
} else {
if (this.state.synced) {
color = "green";
text = "synced";
} else {
color = "red";
text = "not synced";
}
}
return <footer>
<span>
<span class={extrac} ><a onClick={() => this.onSyncClick()} ><Refresh style="height: 1em; width: 1em;"></Refresh></a></span>
<span style={"margin-left: 8px; color:" + color}>{text}</span>
</span>
<span>
Welcome <b>{Notes.name}</b>
</span>
</footer>
}
}

View File

@ -12,14 +12,14 @@ export class Router extends Component<{}, { next?: JSX.Element, current: JSX.Ele
} }
componentWillMount() { componentWillMount() {
Navigation.pageObservable.subscribe(this.onChange, true) Navigation.pageObservable.subscribe(this.onChange)
} }
componentWillUnmount() { componentWillUnmount() {
Navigation.pageObservable.unsubscribe(this.onChange) Navigation.pageObservable.unsubscribe(this.onChange)
} }
onChange([page]: JSX.Element[]) { onChange(page: JSX.Element) {
this.setState({ next: page, current: this.state.next || this.state.current }); this.setState({ next: page, current: this.state.next || this.state.current });
} }
@ -42,7 +42,7 @@ export class Router extends Component<{}, { next?: JSX.Element, current: JSX.Ele
{this.state.next} {this.state.next}
</div> </div>
} }
return <div style="overflow:hidden; width: 1vw;"> return <div style="position: relative; overflow-x: hidden; width: 100vw; height: 100vh;">
<div class="transition_container" key={this.state.current.key} ref={elm => this.mounted = elm}> <div class="transition_container" key={this.state.current.key} ref={elm => this.mounted = elm}>
{this.state.current} {this.state.current}
</div> </div>

View File

@ -0,0 +1,19 @@
.reloading { animation: turner 1s infinite linear }
@keyframes turner{
from{ transform: rotate(0deg) }
to { transform: rotate(360deg) }
}
footer {
z-index: 128;
position: fixed;
bottom: 0;
left: 0;
right: 0;
display: flex;
justify-content: space-between;
padding: calc(0.9 * var(--universal-padding)) !important;
}

View File

@ -23,7 +23,7 @@ export class InputModal extends Modal<string> {
render() { render() {
return <Modal.BaseModal modal={this.props.modal}> return <Modal.BaseModal modal={this.props.modal}>
<fieldset style="border:none;"> <fieldset style="border:none; min-inline-size:0;">
<label for={this.rand}>{this.props.modal.fieldname}</label> <label for={this.rand}>{this.props.modal.fieldname}</label>
<input style="min-width: 85%" autofocus ref={elm => { <input style="min-width: 85%" autofocus ref={elm => {
this.input = elm this.input = elm
@ -48,40 +48,3 @@ export class InputModal extends Modal<string> {
return <InputModal.IMD modal={this} /> return <InputModal.IMD modal={this} />
} }
} }
// export class InputModal extends Component<{ title: string, fieldname: string, type: "text" | "password", onResult: (result) => void }, {}> {
// input: HTMLInputElement;
// rand: string;
// constructor(props) {
// super(props);
// this.rand = Math.random().toString();
// }
// componentWillUnmount() {
// if (this.input)
// this.input.value = "";
// }
// render() {
// return <Modal title={this.props.title} onClose={() => this.props.onResult(null)}>
// <fieldset style="border:none;">
// <label for={this.rand}>{this.props.fieldname}</label>
// <input style="min-width: 85%" autofocus ref={elm => {
// this.input = elm
// if (this.input)
// setTimeout(() => this.input.focus(), 0)
// }} type={this.props.type} id={this.rand} placeholder={this.props.fieldname} onKeyDown={evt => {
// if (evt.keyCode === 13) {
// this.props.onResult(this.input.value)
// }
// }} />
// <div style="text-align: right;">
// <button class="primary" style="display: inline-block;" onClick={() => {
// this.props.onResult(this.input.value);
// }}>Enter</button>
// </div>
// </fieldset>
// </Modal>
// }
// }

View File

@ -1,9 +1,10 @@
import Observable from "../../helper/observable"; import { Observable } from "@hibas123/utils";
import { h, Component } from "preact"; import { h, Component } from "preact";
import CloseIcon from "feather-icons/dist/icons/x.svg";
export default abstract class Modal<T> { export default abstract class Modal<T> {
// Static // Static
private static modalObservableServer = new Observable<{ modal: Modal<any>, close: boolean }>(false); private static modalObservableServer = new Observable<{ modal: Modal<any>, close: boolean }>();
public static modalObservable = Modal.modalObservableServer.getPublicApi(); public static modalObservable = Modal.modalObservableServer.getPublicApi();
@ -11,9 +12,12 @@ export default abstract class Modal<T> {
// Private // Private
private onResult: (result: T | null) => void; private onResult: (result: T | null) => void;
private closeOnResult: boolean;
// Protected // Protected
protected result(value: T | null) { protected result(value: T | null) {
if (this.closeOnResult)
this.close()
if (this.onResult) if (this.onResult)
this.onResult(value); this.onResult(value);
} }
@ -32,7 +36,8 @@ export default abstract class Modal<T> {
* *
* Call close when successful * Call close when successful
*/ */
public async getResult() { public async getResult(close = true) {
this.closeOnResult = close;
this.show(); this.show();
return new Promise<T | null>((yes) => this.onResult = yes); return new Promise<T | null>((yes) => this.onResult = yes);
} }
@ -65,7 +70,12 @@ export default abstract class Modal<T> {
} }
}}> }}>
<div class="card" > <div class="card" >
<h3 class="section">{this.props.modal.title}</h3> <div class="section" style="display:flex;justify-content:space-between;">
<h3>{this.props.modal.title}</h3>
<h3>
<CloseIcon onClick={() => this.props.modal.result(null)} width={undefined} height={undefined} style="height:calc(1rem * var(--heading-ratio) * var(--heading-ratio))" />
</h3>
</div>
{this.props.children} {this.props.children}
</div> </div>
</div> </div>
@ -74,62 +84,24 @@ export default abstract class Modal<T> {
} }
// export default abstract class Modal<T, S> extends Component<{}, S> {
// // Abstract
// protected abstract renderChilds(): JSX.Element;
// protected abstract title: string;
// render() {
// return <div class="modal_container" onClick={(evt) => {
// let path = evt.composedPath();
// if (!path.find(e => {
// let res = false;
// let s = (e as Element);
// if (s) {
// if (s.classList) {
// res = s.classList.contains("card")
// }
// }
// return res;
// })) {
// this.result(null)
// }
// }} onKeyDown={evt => {
// if (evt.keyCode === 27) {
// this.result(null)
// }
// }}>
// <div class="card" >
// <h3 class="section">{this.title}</h3>
// {this.renderChilds()}
// </div>
// </div>
// }
// }
export class ModalComponent extends Component<{}, { modal: Modal<any> | undefined, component: JSX.Element | undefined }>{ export class ModalComponent extends Component<{}, { modal: Modal<any> | undefined, component: JSX.Element | undefined }>{
constructor(props) { constructor(props) {
super(props); super(props);
this.onModal = this.onModal.bind(this); this.onModal = this.onModal.bind(this);
} }
onModal([{ modal, close }]: { modal: Modal<any>, close: boolean }[]) { onModal({ modal, close }: { modal: Modal<any>, close: boolean }) {
if (!close && this.state.modal !== modal) { if (!close && this.state.modal !== modal) {
this.setState({ modal: modal, component: modal.getComponent() }) this.setState({ modal: modal, component: modal.getComponent() })
} }
else { else {
if (this.state.modal === modal) // Only close if the same if (this.state.modal === modal && close) // Only close if the same
this.setState({ modal: undefined, component: undefined }) this.setState({ modal: undefined, component: undefined })
} }
} }
componentWillMount() { componentWillMount() {
Modal.modalObservable.subscribe(this.onModal, true); Modal.modalObservable.subscribe(this.onModal);
} }
componentWillUnmount() { componentWillUnmount() {

View File

@ -28,7 +28,7 @@ export class YesNoModal extends Modal<boolean> {
render() { render() {
return <Modal.BaseModal modal={this.props.modal}> return <Modal.BaseModal modal={this.props.modal}>
<fieldset style="border:none;"> <fieldset style="border:none;min-inline-size:0;">
<div style="text-align: right;"> <div style="text-align: right;">
<button class="primary" style="display: inline-block;" onClick={() => { <button class="primary" style="display: inline-block;" onClick={() => {
this.props.modal.result(false); this.props.modal.result(false);

View File

@ -1,9 +1,20 @@
:root {
--notification-margin: 15px;
}
@media screen and (min-width: 1000px) {
:root {
--notification-margin: 35px;
}
}
.notifications_container { .notifications_container {
position: fixed; position: fixed;
top: 0; top: 0;
right: 0; right: 0;
z-index: 128; z-index: 128;
margin-top: 35px; margin-top: calc(var(--notification-margin) - 10px);
} }
.notifications_success { .notifications_success {
@ -30,6 +41,7 @@
.notifications_notification { .notifications_notification {
h4 { h4 {
color: inherit;
font-weight: bold; font-weight: bold;
margin: 0; margin: 0;
} }
@ -41,7 +53,7 @@
position: static; position: static;
margin-top:10px; margin-top:10px;
margin-right:35px; margin-right:var(--notification-margin);
padding: 0.5em; padding: 0.5em;
width: 300px; width: 300px;
} }

View File

@ -30,7 +30,7 @@ export default class NotificationsComponent extends Component<{}, {
Notifications.messageObservable.unsubscribe(this.onNotification); Notifications.messageObservable.unsubscribe(this.onNotification);
} }
onNotification([not]: { message: string, type: MessageType }[]) { onNotification(not: { message: string, type: MessageType }) {
console.log("Got notification", not) console.log("Got notification", not)
let n = this.state.notifications.slice(0); let n = this.state.notifications.slice(0);
n.push(not); n.push(not);

View File

@ -31,7 +31,8 @@ export default class EntryComponent extends Component<{ vault: Promise<IVault>,
private toVault() { private toVault() {
// history.back() // history.back()
Navigation.setPage("/vault", { id: this.vault.id }, { entry: "false" }, true); history.back();
// Navigation.setPage("/vault", { id: this.vault.id }, { entry: "false" }, true);
} }
async componentWillMount() { async componentWillMount() {
@ -128,7 +129,6 @@ export default class EntryComponent extends Component<{ vault: Promise<IVault>,
if (this.state.changed) { if (this.state.changed) {
let modal = new YesNoModal("Really want to quit?"); let modal = new YesNoModal("Really want to quit?");
let res = await modal.getResult(); let res = await modal.getResult();
modal.close();
if (res === true) { if (res === true) {
this.skip_save = true; this.skip_save = true;
this.toVault(); this.toVault();
@ -138,7 +138,6 @@ export default class EntryComponent extends Component<{ vault: Promise<IVault>,
} }
textAreaKeyPress(evt: KeyboardEvent) { textAreaKeyPress(evt: KeyboardEvent) {
console.log(evt);
if ((evt.keyCode === 83 || evt.keyCode === 13) && evt.ctrlKey) { if ((evt.keyCode === 83 || evt.keyCode === 13) && evt.ctrlKey) {
evt.preventDefault() evt.preventDefault()
this.save(); this.save();

View File

@ -3,7 +3,6 @@ import Notes, { IVault, BaseNote } from "../../../notes";
import AddButton from "../../AddButton"; import AddButton from "../../AddButton";
import Navigation from "../../../navigation"; import Navigation from "../../../navigation";
import ArrowLeft from "feather-icons/dist/icons/arrow-left.svg" import ArrowLeft from "feather-icons/dist/icons/arrow-left.svg"
import MoreVertival from "feather-icons/dist/icons/more-vertical.svg"
import ContextMenu from "../../modals/context"; import ContextMenu from "../../modals/context";
import Notifications from "../../../notifications"; import Notifications from "../../../notifications";
@ -17,7 +16,9 @@ export default class EntryList extends Component<{ vault: Promise<IVault> }, { n
} }
vault: IVault; vault: IVault;
reloadNotes() { reloadNotes(s?: boolean) {
if (s)
return;
return new Promise(yes => this.vault.getAllNotes().then(entries => this.setState({ notes: entries }, yes))); return new Promise(yes => this.vault.getAllNotes().then(entries => this.setState({ notes: entries }, yes)));
} }
@ -169,7 +170,6 @@ export default class EntryList extends Component<{ vault: Promise<IVault> }, { n
<div class="row"> <div class="row">
<div class="col-sm-12 col-md-8 col-lg-6 col-md-offset-2 col-lg-offset-3"> <div class="col-sm-12 col-md-8 col-lg-6 col-md-offset-2 col-lg-offset-3">
<div class="card fluid"> <div class="card fluid">
<h1 class="section double-padded">Notes: </h1>
<div class="section"> <div class="section">
{elms} {elms}
</div> </div>

View File

@ -7,6 +7,7 @@ import EntryList from "./EntryList";
import "./vault.scss" import "./vault.scss"
export interface VaultProps { export interface VaultProps {
state: { id: string }; state: { id: string };
hidden: { entry: string, id: string, note: string }; hidden: { entry: string, id: string, note: string };
@ -21,10 +22,19 @@ export default class VaultPage extends Page<VaultProps, { entries: BaseNote[] }>
async componentWillMount() { async componentWillMount() {
this.vault = Notes.getVault(this.props.state.id, Notes.getVaultKey(this.props.state.id)) this.vault = Notes.getVault(this.props.state.id, Notes.getVaultKey(this.props.state.id))
this.vault.then(vlt => {
window.debug.activeVault = vlt;
window.debug.createNotes = (cnt = 10) => {
for (let i = 0; i < cnt; i++) {
let nt = vlt.newNote();
nt.__value = `Random Note ${i}\ With some Content ${i}`;
vlt.saveNote(nt);
}
}
})
this.vault.catch(err => { this.vault.catch(err => {
Navigation.setPage("/") Navigation.setPage("/")
}) });
} }
render() { render() {

View File

@ -24,7 +24,9 @@ export default class VaultsPage extends Page<VaultsProps, { vaults: VaultList, m
this.updateVaults = this.updateVaults.bind(this); this.updateVaults = this.updateVaults.bind(this);
} }
updateVaults() { updateVaults(s?: boolean) {
if (s)
return;
return new Promise(yes => { return new Promise(yes => {
Notes.getVaults().then(vaults => this.setState({ vaults }, yes)) Notes.getVaults().then(vaults => this.setState({ vaults }, yes))
}) })
@ -43,9 +45,11 @@ export default class VaultsPage extends Page<VaultsProps, { vaults: VaultList, m
let inp_mod = new InputModal("Enter password for " + vault.name, "Password", "password"); let inp_mod = new InputModal("Enter password for " + vault.name, "Password", "password");
let key = undefined; let key = undefined;
while (true) { while (true) {
let value = await inp_mod.getResult(); // inp_mod.show();
let value = await inp_mod.getResult(false);
if (value === null) { if (value === null) {
inp_mod.close(); console.log("Value is null")
inp_mod.close()
return false; return false;
} else { } else {
key = Notes.passwordToKey(value); key = Notes.passwordToKey(value);
@ -57,13 +61,12 @@ export default class VaultsPage extends Page<VaultsProps, { vaults: VaultList, m
} }
} }
} }
inp_mod.close(); inp_mod.close()
let perm = false; let perm = false;
if (permanent) { if (permanent) {
let save_modal = new YesNoModal("Save permanent?"); let save_modal = new YesNoModal("Save permanent?");
let res = await save_modal.getResult(); let res = await save_modal.getResult();
save_modal.close();
if (res === undefined) { if (res === undefined) {
res = false; res = false;
} }
@ -101,19 +104,16 @@ export default class VaultsPage extends Page<VaultsProps, { vaults: VaultList, m
async addButtonClick() { async addButtonClick() {
let name_modal = new InputModal("Enter new name", "Name", "text"); let name_modal = new InputModal("Enter new name", "Name", "text");
let name = await name_modal.getResult(); let name = await name_modal.getResult();
name_modal.close();
if (name === null) return; if (name === null) return;
let encrypted_modal = new YesNoModal("Encrypt?"); let encrypted_modal = new YesNoModal("Encrypt?");
let encrypted = encrypted_modal.getResult(); let encrypted = encrypted_modal.getResult();
encrypted_modal.close();
if (encrypted === null) return; if (encrypted === null) return;
let password; let password;
if (encrypted) { if (encrypted) {
let password_modal = new InputModal("Enter new password", "Password", "password"); let password_modal = new InputModal("Enter new password", "Password", "password");
password = await password_modal.getResult(); password = await password_modal.getResult();
password_modal.close();
if (password === null) return; if (password === null) return;
} }
@ -127,11 +127,6 @@ export default class VaultsPage extends Page<VaultsProps, { vaults: VaultList, m
onContext(evt: MouseEvent, vault: { name: string, encrypted: boolean, id: string }) { onContext(evt: MouseEvent, vault: { name: string, encrypted: boolean, id: string }) {
evt.preventDefault(); evt.preventDefault();
console.log("Context", evt);
// let context = <div style={{ position: "fixed", left: evt.pageX, top: evt.pageY, zIndex: 10 }}>
// <button>Action 1</button>
// </div>
const close = () => { const close = () => {
window.removeEventListener("click", close); window.removeEventListener("click", close);
@ -142,7 +137,6 @@ export default class VaultsPage extends Page<VaultsProps, { vaults: VaultList, m
let deleteb = <button onClick={async () => { let deleteb = <button onClick={async () => {
let delete_modal = new YesNoModal("Delete Vault? Cannot be undone!"); let delete_modal = new YesNoModal("Delete Vault? Cannot be undone!");
let result = await delete_modal.getResult(); let result = await delete_modal.getResult();
delete_modal.close();
if (result) { if (result) {
Notes.deleteVault(vault.id).then(() => { Notes.deleteVault(vault.id).then(() => {
this.updateVaults(); this.updateVaults();
@ -157,7 +151,10 @@ export default class VaultsPage extends Page<VaultsProps, { vaults: VaultList, m
let delete_key; let delete_key;
if (Notes.getVaultKey(vault.id)) { if (Notes.getVaultKey(vault.id)) {
delete_key = <button onClick={() => { Notes.forgetVaultKey(vault.id); }}> delete_key = <button onClick={() => {
Notes.forgetVaultKey(vault.id);
Notifications.sendSuccess("Forgot key!")
}}>
forget password forget password
</button>; </button>;
} }
@ -219,14 +216,16 @@ export default class VaultsPage extends Page<VaultsProps, { vaults: VaultList, m
return <div style={{ marginTop: "-12px", paddingTop: "12px" }} > return <div style={{ marginTop: "-12px", paddingTop: "12px" }} >
{this.state.modal} {this.state.modal}
{this.state.context} {this.state.context}
<header>
<span></span>
<h1 style="display:inline" class="button header_title" onClick={() => Navigation.setPage("/")}>Your vaults:</h1>
<span></span>
</header>
<AddButton onClick={() => this.addButtonClick()} /> <AddButton onClick={() => this.addButtonClick()} />
<div class="container"> <div class="container">
<div class="row"> <div class="row">
<div class="col-sm-12 col-md-8 col-lg-6 col-md-offset-2 col-lg-offset-3"> <div class="col-sm-12 col-md-8 col-lg-6 col-md-offset-2 col-lg-offset-3">
<div class="card fluid"> <div class="card fluid">
<h1 class="section double-padded">Your vaults:</h1>
<div class="section"> <div class="section">
{elms} {elms}
</div> </div>

View File

@ -6,9 +6,13 @@
width: 100%; width: 100%;
overflow: auto; overflow: auto;
background: var(--back-color); background: var(--back-color);
padding-bottom: 40px;
} }
.transition_slidein { .transition_slidein {
position: absolute;
top: 0;
left: 0;
animation-name: slidein; animation-name: slidein;
animation-duration: 0.3s; animation-duration: 0.3s;
z-index: 64; z-index: 64;

View File

@ -1,4 +1,4 @@
import Lock from "./lock"; import { Lock } from "@hibas123/utils";
import { DB, openDb, Transaction } from "idb"; import { DB, openDb, Transaction } from "idb";

View File

@ -1,36 +0,0 @@
export type Release = { release: () => void };
export default class Lock {
private _locked: boolean = false;
get locked() {
return this._locked;
}
private toCome: (() => void)[] = [];
constructor() {
this.release = this.release.bind(this);
}
async getLock(): Promise<Release> {
if (!this._locked) return { release: this.lock() };
else {
return new Promise<Release>((resolve) => {
this.toCome.push(() => {
resolve({ release: this.lock() });
})
})
}
}
private lock() {
this._locked = true;
return this.release;
}
private async release() {
if (this.toCome.length > 0) {
this.toCome.shift()();
} else {
this._locked = false;
}
}
}

View File

@ -1,46 +0,0 @@
export type ObserverCallback<T> = (data: T[]) => void;
export default class Observable<T = any> {
private subscriber: { callback: ObserverCallback<T>, one: boolean }[] = [];
private events: T[] = [];
private timeout = undefined;
constructor(private collect: boolean = true, private collect_intervall: number = 100) { }
getPublicApi() {
return {
subscribe: (callback: ObserverCallback<T>, one: boolean = false) => {
let oldcb = this.subscriber.find(e => e.callback === callback);
if (oldcb)
oldcb.one = one
else
this.subscriber.push({ callback, one })
},
unsubscribe: (callback: ObserverCallback<T>) => {
let idx = this.subscriber.findIndex(e => e.callback === callback);
if (idx >= 0) {
this.subscriber.splice(idx, 1);
}
}
}
}
send(data: T) {
if (!this.collect)
this.subscriber.forEach(e => e.callback([data]));
else {
this.events.push(data);
if (!this.timeout) {
this.timeout = setTimeout(() => {
this.subscriber.forEach(cb => {
if (cb.one)
this.events.forEach(e => cb.callback([e]));
else
cb.callback(this.events)
});
this.timeout = 0;
}, this.collect_intervall);
}
}
}
}

View File

@ -6,20 +6,17 @@
<meta charset="utf8" /> <meta charset="utf8" />
<meta name="Description" content="Notes app"> <meta name="Description" content="Notes app">
<meta name="viewport" content="width=device-width,initial-scale=1" /> <meta name="viewport" content="width=device-width,initial-scale=1" />
<link rel="manifest" href="/public/manifest.json"> <link rel="manifest" href="/manifest.json">
<!-- <link rel="shortcut icon" href="/public/icon-72x72.png"> --> <!-- <link rel="shortcut icon" href="/public/icon-72x72.png"> -->
<!-- Add to home screen for Safari on iOS --> <!-- Add to home screen for Safari on iOS -->
<meta name="apple-mobile-web-app-capable" content="yes"> <meta name="apple-mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-status-bar-style" content="black"> <meta name="apple-mobile-web-app-status-bar-style" content="black">
<meta name="apple-mobile-web-app-title" content="eBook"> <meta name="apple-mobile-web-app-title" content="Secure Notes">
<link rel="apple-touch-icon" sizes="180x180" href="/public/apple-touch-icon.png"> <link rel="apple-touch-icon" sizes="180x180" href="/apple-touch-icon.png">
<link rel="icon" type="image/png" sizes="32x32" href="/public/favicon-32x32.png"> <link rel="mask-icon" href="/safari-pinned-tab.svg" color="#3E9AE9">
<link rel="icon" type="image/png" sizes="16x16" href="/public/favicon-16x16.png">
<link rel="manifest" href="/public/site.webmanifest">
<link rel="mask-icon" href="/public/safari-pinned-tab.svg" color="#3E9AE9">
<meta name="msapplication-TileColor" content="#3E9AE9"> <meta name="msapplication-TileColor" content="#3E9AE9">
<meta name="theme-color" content="#3E9AE9"> <meta name="theme-color" content="#3E9AE9">
</head> </head>
@ -30,13 +27,33 @@
</noscript> </noscript>
<div id="app"></div> <div id="app"></div>
<script> <script>
// Check that service workers are registered // // Check that service workers are registered
// if ('serviceWorker' in navigator) { // if ('serviceWorker' in navigator) {
// // Use the window load event to keep the page load performant // // Use the window load event to keep the page load performant
// window.addEventListener('load', () => { // window.addEventListener('load', () => {
// navigator.serviceWorker.register('/sw.js'); // navigator.serviceWorker.register('/public/serviceworker.js');
// }); // });
// } // }
if (navigator.serviceWorker.controller) {
if (localStorage.getItem("debug")) {
console.warn("Debuggung and service worker found, make shure to clear cache!");
}
console.log('active service worker found, no need to register')
} else {
if (localStorage.getItem("debug")) {
console.warn("Disabling Service Worker in debug mode!")
} else {
// Register the ServiceWorker
navigator.serviceWorker.register('/serviceworker.js', {
scope: '/'
}).then(function (reg) {
console.log('Service worker has been registered for scope:' + reg.scope);
navigator.serviceWorker.controller.addEventListener("cleared_cache", evt => {
console.log(evt);
})
});
}
}
</script> </script>
</body> </body>

View File

@ -1,8 +1,12 @@
window.debug = {};
import { h, render } from 'preact'; import { h, render } from 'preact';
import App from './components/App'; import App from './components/App';
import "mini.css/src/flavors/mini-dark.scss" // import "mini.css/src/flavors/mini-dark.scss"
// import "mini.css/src/flavors/mini-default.scss" import "mini.css/src/flavors/mini-default.scss"
import "uikit";
import "uikit/dist/css/uikit.css"
import "./index.scss" import "./index.scss"
import Navigation from './navigation'; import Navigation from './navigation';
import VaultsPage from './components/routes/vaults/Vaults'; import VaultsPage from './components/routes/vaults/Vaults';

View File

@ -1,4 +1,4 @@
import Observable from "./helper/observable"; import { Observable } from "@hibas123/utils";
import { Page, PageProps } from "./page"; import { Page, PageProps } from "./page";
import { h, VNode } from "preact"; import { h, VNode } from "preact";
@ -27,7 +27,7 @@ function parseQuery(query: string) {
export default class Navigation { export default class Navigation {
private static _pages: Map<string, typeof Page> = new Map(); private static _pages: Map<string, typeof Page> = new Map();
private static _page: { route: string, page: JSX.Element }; private static _page: { route: string, page: JSX.Element };
private static pageObservableServer = new Observable<JSX.Element>(false); private static pageObservableServer = new Observable<JSX.Element>();
public static pageObservable = Navigation.pageObservableServer.getPublicApi(); public static pageObservable = Navigation.pageObservableServer.getPublicApi();
private static _state: { [key: string]: any }; private static _state: { [key: string]: any };
private static _hidden_state: { [key: string]: any }; private static _hidden_state: { [key: string]: any };
@ -52,13 +52,14 @@ export default class Navigation {
} }
public static setPage(route: string, state?: { [key: string]: string }, hidden?: { [key: string]: string }, replace?: boolean) { public static setPage(route: string, state?: { [key: string]: string }, hidden?: { [key: string]: string }, replace?: boolean) {
if (!state) state = {};
let component = Navigation._pages.get(route); let component = Navigation._pages.get(route);
if (!component && route !== "/404") { if (!component && route !== "/404") {
Navigation.setPage("/404", { route, key: "404" }) Navigation.setPage("/404", { route, key: "404" })
} else { } else {
if (!Navigation.page || Navigation.page.route !== route || JSON.stringify(state) !== JSON.stringify(Navigation._state) || JSON.stringify(Navigation._hidden_state) !== JSON.stringify(hidden)) { if (!Navigation.page || Navigation.page.route !== route || JSON.stringify(state) !== JSON.stringify(Navigation._state) || JSON.stringify(Navigation._hidden_state) !== JSON.stringify(hidden)) {
let s = ""; let s = "";
if (state) { if (Object.keys(state).length > 0) {
s = "?" + serializQuery(state) s = "?" + serializQuery(state)
} }
let newhash = "#" + route + s; let newhash = "#" + route + s;
@ -92,6 +93,7 @@ export default class Navigation {
Navigation._state = state; Navigation._state = state;
Navigation._hidden_state = hidden; Navigation._hidden_state = hidden;
Navigation._page = { page, route }; Navigation._page = { page, route };
console.log(route, state, hidden);
Navigation.pageObservableServer.send(page); Navigation.pageObservableServer.send(page);
} }
} }
@ -143,3 +145,5 @@ export default class Navigation {
Navigation.onHashChange(undefined); Navigation.onHashChange(undefined);
} }
} }
window.debug.navigation = Navigation;

View File

@ -1,6 +1,5 @@
import Observable from "./helper/observable";
import * as config from "../config.json" import * as config from "../config.json"
import Lock from "./helper/lock" import { Lock, Observable } from "@hibas123/utils"
import SecureFile, { IFile } from "@hibas123/secure-file-wrapper"; import SecureFile, { IFile } from "@hibas123/secure-file-wrapper";
import * as b64 from "./helper/base64" import * as b64 from "./helper/base64"
@ -33,9 +32,7 @@ export interface ViewNote extends BaseNote {
__value: string; __value: string;
} }
// import * as AES from "crypto-js/aes"
import * as aesjs from "aes-js"; import * as aesjs from "aes-js";
// import { } from "js-sha512"
import { sha256 } from "js-sha256" import { sha256 } from "js-sha256"
import clonedeep = require("lodash.clonedeep"); import clonedeep = require("lodash.clonedeep");
import * as uuidv4 from "uuid/v4" import * as uuidv4 from "uuid/v4"
@ -87,11 +84,16 @@ export interface IVault {
deleteNote(id: string): Promise<void>; deleteNote(id: string): Promise<void>;
} }
const awaitTimeout = (ms: number) => new Promise<void>(resolve => setTimeout(resolve, ms));
class NotesProvider { class NotesProvider {
private notesObservableServer = new Observable<Note>(true) private notesObservableServer = new Observable<Note>()
public notesObservable = this.notesObservableServer.getPublicApi() public notesObservable = this.notesObservableServer.getPublicApi()
private syncObservableServer = new Observable<null>(true) private syncObservableServer = new Observable<boolean>()
/**
* Will send false once finished and true on start
*/
public syncObservable = this.syncObservableServer.getPublicApi() public syncObservable = this.syncObservableServer.getPublicApi()
private database = new IDB("notes", ["notes", "oplog"]); private database = new IDB("notes", ["notes", "oplog"]);
@ -110,6 +112,18 @@ class NotesProvider {
private generalEncryption: Uint8Array = undefined; private generalEncryption: Uint8Array = undefined;
private syncedObservableServer = new Observable<boolean>();
public syncedObservable = this.syncedObservableServer.getPublicApi();
public async isSynced() {
return (await this.oplogDB.getAll()).length <= 0
}
private _name;
public get name() {
return this._name;
}
loginRequired() { loginRequired() {
return !localStorage.getItem("refreshtoken") || !this.generalEncryption; return !localStorage.getItem("refreshtoken") || !this.generalEncryption;
} }
@ -123,6 +137,8 @@ class NotesProvider {
let res = await req.json(); let res = await req.json();
if (!res.error) { if (!res.error) {
localStorage.setItem("refreshtoken", res.token); localStorage.setItem("refreshtoken", res.token);
localStorage.setItem("name", res.profile.name);
this._name = res.profile.name;
let kb = this.passwordToKey(res.profile.enc_key); let kb = this.passwordToKey(res.profile.enc_key);
localStorage.setItem("enc_key", b64.encode(kb)); localStorage.setItem("enc_key", b64.encode(kb));
this.generalEncryption = kb this.generalEncryption = kb
@ -133,7 +149,7 @@ class NotesProvider {
constructor(public readonly baseurl = "") { constructor(public readonly baseurl = "") {
this._secureFile = new SecureFile(config.secure_file_server); this._secureFile = new SecureFile(config.secure_file_server);
this._secureFile.jwtObservable.subscribe(async ([callback]) => { this._secureFile.jwtObservable.subscribe(async (callback) => {
let jwt = await this.getJWT(); let jwt = await this.getJWT();
callback(jwt); callback(jwt);
}) })
@ -142,6 +158,7 @@ class NotesProvider {
if (key) { if (key) {
this.generalEncryption = b64.decode(key) this.generalEncryption = b64.decode(key)
} }
this._name = localStorage.getItem("name");
} }
public async start() { public async start() {
@ -150,8 +167,10 @@ class NotesProvider {
this.sync().then(() => next); this.sync().then(() => next);
}, 30000) }, 30000)
} }
this.syncedObservableServer.send((await this.oplogDB.getAll()).length <= 0);
return this.apiLockRls.then(lock => lock.release()).then(() => { this.sync() }).then(() => next()) let prs = this.apiLockRls.then(lock => lock.release());
prs.then(() => awaitTimeout(5000)).then(() => this.sync()).then(() => next());
return prs;
} }
private async getJWT() { private async getJWT() {
@ -182,11 +201,13 @@ class NotesProvider {
window.location.reload(); window.location.reload();
} }
private async sync() { async sync() {
const lock = await this.syncLock.getLock() const lock = await this.syncLock.getLock()
const log = (...message: any[]) => { const log = (...message: any[]) => {
console.log("[SYNC]: ", ...message) console.log("[SYNC]: ", ...message)
} }
this.syncObservableServer.send(true);
log("Start") log("Start")
try { try {
log("Fetching"); log("Fetching");
@ -351,10 +372,11 @@ class NotesProvider {
await this.oplogDB.delete(id); await this.oplogDB.delete(id);
} }
log("Stats", stats); log("Stats", stats);
this.syncedObservableServer.send((await this.oplogDB.getAll()).length <= 0)
} finally { } finally {
log("Finished") log("Finished")
lock.release() lock.release()
this.syncObservableServer.send(null); this.syncObservableServer.send(false);
} }
} }
@ -464,6 +486,7 @@ class NotesProvider {
type, type,
values values
}) })
this.syncedObservableServer.send(false);
await this.oplogDB.set(note_id, oplog, tx); await this.oplogDB.set(note_id, oplog, tx);
} }

View File

@ -1,4 +1,4 @@
import Observable from "./helper/observable"; import { Observable } from "@hibas123/utils";
export enum MessageType { export enum MessageType {
INFO, INFO,
@ -8,7 +8,7 @@ export enum MessageType {
} }
export default class Notifications { export default class Notifications {
private static messageObservableServer = new Observable<{ message: string, type: MessageType }>(false) private static messageObservableServer = new Observable<{ message: string, type: MessageType }>()
public static messageObservable = Notifications.messageObservableServer.getPublicApi() public static messageObservable = Notifications.messageObservableServer.getPublicApi()

15
src/types.d.ts vendored
View File

@ -1,2 +1,13 @@
// import { Component } from "preact"; declare module "*.svg" {
declare module "*.svg"; const SVG: (props: {
width?: number | undefined
height?: number | undefined
onClick?: (evt: MouseEvent) => void;
style?: string;
}) => JSX.Element;
export default SVG;
}
interface Window {
debug: any;
}

View File

@ -25,7 +25,7 @@ module.exports = {
public: url.parse(config.callback_url).hostname public: url.parse(config.callback_url).hostname
}, },
plugins: [ plugins: [
new CopyWebpackPlugin([{ from: "public", to: "public" }]), new CopyWebpackPlugin([{ from: "public" }]),
new HtmlWebpackPlugin({ new HtmlWebpackPlugin({
title: 'SecureNotes', title: 'SecureNotes',
template: "./src/index.html" template: "./src/index.html"
@ -45,10 +45,10 @@ module.exports = {
// clientsClaim: true, // clientsClaim: true,
// // importsDirectory: "/dist", // // importsDirectory: "/dist",
// directoryIndex: 'index.html', // directoryIndex: 'index.html',
// ignoreUrlParametersMatching: [/./], // ignoreURLParametersMatching: [/./],
// runtimeCaching: [{ // runtimeCaching: [{
// urlPattern: /images/, // urlPattern: /images/,
// handler: 'cacheFirst', // handler: 'CacheFirst',
// options: { // options: {
// cacheName: 'images', // cacheName: 'images',
// expiration: { // expiration: {
@ -59,7 +59,7 @@ module.exports = {
// }, // },
// }, { // }, {
// urlPattern: /api/, // urlPattern: /api/,
// handler: 'networkOnly' // handler: 'NetworkOnly'
// }] // }]
// }) // })
], ],
@ -80,7 +80,7 @@ module.exports = {
test: /\.(png|jpg)$/, test: /\.(png|jpg)$/,
loader: "file-loader", loader: "file-loader",
}, { }, {
test: /\.scss$/, test: /\.(scss|css)$/,
use: [ use: [
"style-loader", // creates style nodes from JS strings "style-loader", // creates style nodes from JS strings
MiniCssExtractPlugin.loader, MiniCssExtractPlugin.loader,