node-red-contrib-power-saver 3.3.2 → 3.4.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.firebase/hosting.ZG9jcy8udnVlcHJlc3MvZGlzdA.cache +94 -0
- package/.firebaserc +5 -0
- package/.github/workflows/firebase-hosting-merge.yml +20 -0
- package/.github/workflows/firebase-hosting-pull-request.yml +17 -0
- package/README.md +1 -1
- package/docs/.vuepress/components/BestSaveVerificator.vue +3 -3
- package/docs/.vuepress/config.js +15 -1
- package/docs/.vuepress/dist/404.html +23 -5
- package/docs/.vuepress/dist/assets/css/896.styles.21a80cb6.css +1 -0
- package/docs/.vuepress/dist/assets/css/styles.1c48cbd0.css +10 -0
- package/docs/.vuepress/dist/assets/img/heat-capacitor-temperatureVsPrice.6e74905b.png +0 -0
- package/docs/.vuepress/dist/assets/img/node-ps-strategy-heat-capacitor-cascade-control.2e75ed9e.png +0 -0
- package/docs/.vuepress/dist/assets/img/node-ps-strategy-heat-capacitor-simple-flow-example.29d9bf59.png +0 -0
- package/docs/.vuepress/dist/assets/img/oven-setpoint-calculation.5bda0eec.png +0 -0
- package/docs/.vuepress/dist/assets/img/overshoot-time.b3b5d70e.png +0 -0
- package/docs/.vuepress/dist/assets/js/229.5c5378fa.js +1 -0
- package/docs/.vuepress/dist/assets/js/331.872104cd.js +1 -0
- package/docs/.vuepress/dist/assets/js/405.f4edd94d.js +2 -0
- package/docs/.vuepress/dist/assets/js/{619.8ba1b1f6.js.LICENSE.txt → 405.f4edd94d.js.LICENSE.txt} +0 -0
- package/docs/.vuepress/dist/assets/js/490.1e639e05.js +1 -0
- package/docs/.vuepress/dist/assets/js/{491.17a98f38.js → 491.bd938119.js} +1 -1
- package/docs/.vuepress/dist/assets/js/555.d8963d84.js +1 -0
- package/docs/.vuepress/dist/assets/js/{811.6a3392d5.js → 811.5f659592.js} +0 -0
- package/docs/.vuepress/dist/assets/js/app.dfdee6f9.js +1 -0
- package/docs/.vuepress/dist/assets/js/runtime~app.f6ac32d7.js +1 -0
- package/docs/.vuepress/dist/assets/js/v-0607240a.0193a377.js +1 -0
- package/docs/.vuepress/dist/assets/js/v-08683c60.52e94cb6.js +1 -0
- package/docs/.vuepress/dist/assets/js/v-0aca7ba6.cac5d4b9.js +1 -0
- package/docs/.vuepress/dist/assets/js/v-0b5e3c8c.18561f6e.js +1 -0
- package/docs/.vuepress/dist/assets/js/v-1ad821fa.6697a349.js +1 -0
- package/docs/.vuepress/dist/assets/js/v-1b3a0ab8.c6c4e19b.js +1 -0
- package/docs/.vuepress/dist/assets/js/v-1e2b191e.07b8ab21.js +1 -0
- package/docs/.vuepress/dist/assets/js/v-29504124.00be7399.js +1 -0
- package/docs/.vuepress/dist/assets/js/v-30acb564.28af12af.js +1 -0
- package/docs/.vuepress/dist/assets/js/{v-3706649a.d7f73384.js → v-3706649a.c76d575b.js} +1 -1
- package/docs/.vuepress/dist/assets/js/v-4637f9e4.d334c29a.js +1 -0
- package/docs/.vuepress/dist/assets/js/v-4c28314d.8cbb0f9d.js +1 -0
- package/docs/.vuepress/dist/assets/js/v-510ed0d4.c04bc2e4.js +1 -0
- package/docs/.vuepress/dist/assets/js/{v-5954bcb2.937005d0.js → v-5954bcb2.dff3fc67.js} +1 -1
- package/docs/.vuepress/dist/assets/js/v-5db8da3a.e5e6d7a6.js +1 -0
- package/docs/.vuepress/dist/assets/js/v-61f728ca.81968036.js +1 -0
- package/docs/.vuepress/dist/assets/js/v-677dfaed.c159b0f4.js +1 -0
- package/docs/.vuepress/dist/assets/js/v-7446a652.8fc2c591.js +1 -0
- package/docs/.vuepress/dist/assets/js/v-7c87f26e.8ed52391.js +1 -0
- package/docs/.vuepress/dist/assets/js/v-84304104.f3f07ed3.js +1 -0
- package/docs/.vuepress/dist/assets/js/{v-8daa1a0e.c63afc2b.js → v-8daa1a0e.ed84ca09.js} +1 -1
- package/docs/.vuepress/dist/assets/js/{v-b4a42144.733e4e7c.js → v-b4a42144.9a2a0c9f.js} +1 -1
- package/docs/.vuepress/dist/assets/js/v-e8c55052.b7d52fc6.js +1 -0
- package/docs/.vuepress/dist/assets/js/v-fffb8e28.d09ab959.js +1 -0
- package/docs/.vuepress/dist/changelog/index.html +23 -5
- package/docs/.vuepress/dist/contribute/index.html +23 -5
- package/docs/.vuepress/dist/examples/example-cascade-temperature-control.html +304 -0
- package/docs/.vuepress/dist/examples/example-heat-capacitor.html +247 -0
- package/docs/.vuepress/dist/examples/example-next-schedule-entity.html +27 -9
- package/docs/.vuepress/dist/examples/example-nordpool-current-state.html +24 -6
- package/docs/.vuepress/dist/examples/example-nordpool-events-state.html +24 -6
- package/docs/.vuepress/dist/examples/example-tibber-mqtt.html +24 -6
- package/docs/.vuepress/dist/examples/index.html +23 -5
- package/docs/.vuepress/dist/faq/best-save-viewer.html +23 -5
- package/docs/.vuepress/dist/faq/index.html +23 -5
- package/docs/.vuepress/dist/guide/index.html +24 -6
- package/docs/.vuepress/dist/index.html +23 -5
- package/docs/.vuepress/dist/nodes/index.html +23 -5
- package/docs/.vuepress/dist/nodes/old-power-saver-doc.html +25 -7
- package/docs/.vuepress/dist/nodes/power-saver.html +23 -5
- package/docs/.vuepress/dist/nodes/ps-elvia-add-tariff.html +23 -5
- package/docs/.vuepress/dist/nodes/ps-general-add-tariff.html +23 -5
- package/docs/.vuepress/dist/nodes/ps-receive-price.html +26 -8
- package/docs/.vuepress/dist/nodes/ps-strategy-best-save.html +32 -9
- package/docs/.vuepress/dist/nodes/ps-strategy-heat-capacitor.html +260 -0
- package/docs/.vuepress/dist/nodes/ps-strategy-lowest-price.html +30 -7
- package/docs/.vuepress/dist/nodes/strategy-input.html +24 -6
- package/docs/README.md +1 -1
- package/docs/changelog/README.md +14 -0
- package/docs/contribute/README.md +8 -0
- package/docs/examples/README.md +8 -2
- package/docs/examples/example-cascade-temperature-control.md +346 -0
- package/docs/examples/example-heat-capacitor.md +271 -0
- package/docs/images/heat-capacitor-temperatureVsPrice.png +0 -0
- package/docs/images/node-ps-strategy-heat-capacitor-cascade-control.png +0 -0
- package/docs/images/node-ps-strategy-heat-capacitor-simple-flow-example.png +0 -0
- package/docs/images/node-ps-strategy-heat-capacitor.png +0 -0
- package/docs/images/oven-setpoint-calculation.png +0 -0
- package/docs/images/overshoot-time.png +0 -0
- package/docs/images/temperature-manipulation-config.png +0 -0
- package/docs/nodes/README.md +7 -1
- package/docs/nodes/ps-strategy-heat-capacitor.md +346 -0
- package/examples/add-general-tariff.json +103 -0
- package/examples/best-save-for-water-heater.json +140 -0
- package/examples/elvia-add-tariff.json +99 -0
- package/examples/elvia-get-tariff-types.json +58 -0
- package/examples/elvia-get-tariff.json +60 -0
- package/examples/heat-capacitor-for-room-heating.json +186 -0
- package/examples/lowest-price-for-heating-cables.json +159 -0
- package/firebase.json +6 -0
- package/package.json +17 -9
- package/public/404.html +33 -0
- package/public/index.html +89 -0
- package/src/elvia/elvia-tariff.js +2 -1
- package/src/handle-input.js +6 -3
- package/src/strategy-heat-capacitor-functions.js +246 -0
- package/src/strategy-heat-capacitor.html +85 -0
- package/src/strategy-heat-capacitor.js +125 -0
- package/test/data/converted-prices.json +1 -1
- package/test/data/multiple-trades.json +53 -0
- package/test/data/tibber-decreasing-24h.json +101 -0
- package/test/data/tibber-decreasing2-24h.json +101 -0
- package/test/strategy-heat-capacitor-node.test.js +183 -0
- package/test/strategy-heat-capacitor.test.js +103 -0
- package/test/strategy-lowest-price-functions.test.js +1 -1
- package/test/utils.test.js +0 -2
- package/docs/.vuepress/dist/.nojekyll +0 -0
- package/docs/.vuepress/dist/assets/css/563.styles.99f4a8aa.css +0 -1
- package/docs/.vuepress/dist/assets/css/styles.031dcf27.css +0 -9
- package/docs/.vuepress/dist/assets/js/262.cf2c57d2.js +0 -1
- package/docs/.vuepress/dist/assets/js/293.08ea5200.js +0 -1
- package/docs/.vuepress/dist/assets/js/331.15ee3c51.js +0 -1
- package/docs/.vuepress/dist/assets/js/619.8ba1b1f6.js +0 -2
- package/docs/.vuepress/dist/assets/js/app.b705176c.js +0 -1
- package/docs/.vuepress/dist/assets/js/runtime~app.47f4f812.js +0 -1
- package/docs/.vuepress/dist/assets/js/v-0607240a.a57c2199.js +0 -1
- package/docs/.vuepress/dist/assets/js/v-08683c60.ccafdcab.js +0 -1
- package/docs/.vuepress/dist/assets/js/v-0aca7ba6.25903946.js +0 -1
- package/docs/.vuepress/dist/assets/js/v-0b5e3c8c.a6a015b4.js +0 -1
- package/docs/.vuepress/dist/assets/js/v-1ad821fa.5978386f.js +0 -1
- package/docs/.vuepress/dist/assets/js/v-1e2b191e.88dc5555.js +0 -1
- package/docs/.vuepress/dist/assets/js/v-29504124.4aca27d5.js +0 -1
- package/docs/.vuepress/dist/assets/js/v-30acb564.529a3c16.js +0 -1
- package/docs/.vuepress/dist/assets/js/v-4637f9e4.703b1d96.js +0 -1
- package/docs/.vuepress/dist/assets/js/v-510ed0d4.7b142a81.js +0 -1
- package/docs/.vuepress/dist/assets/js/v-5db8da3a.3de3588d.js +0 -1
- package/docs/.vuepress/dist/assets/js/v-61f728ca.21d432fe.js +0 -1
- package/docs/.vuepress/dist/assets/js/v-677dfaed.44a653b9.js +0 -1
- package/docs/.vuepress/dist/assets/js/v-7446a652.74b21d0b.js +0 -1
- package/docs/.vuepress/dist/assets/js/v-7c87f26e.ee5be992.js +0 -1
- package/docs/.vuepress/dist/assets/js/v-e8c55052.ab0a79ec.js +0 -1
- package/docs/.vuepress/dist/assets/js/v-fffb8e28.525be02a.js +0 -1
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
{
|
|
2
|
+
"source": "Tibber",
|
|
3
|
+
"priceData": [
|
|
4
|
+
{
|
|
5
|
+
"value": 1.8019,
|
|
6
|
+
"start": "2022-01-12T00:00:00.000+01:00"
|
|
7
|
+
},
|
|
8
|
+
{
|
|
9
|
+
"value": 1.7711,
|
|
10
|
+
"start": "2022-01-12T01:00:00.000+01:00"
|
|
11
|
+
},
|
|
12
|
+
{
|
|
13
|
+
"value": 1.7539,
|
|
14
|
+
"start": "2022-01-12T02:00:00.000+01:00"
|
|
15
|
+
},
|
|
16
|
+
{
|
|
17
|
+
"value": 1.7508,
|
|
18
|
+
"start": "2022-01-12T03:00:00.000+01:00"
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
"value": 1.7616,
|
|
22
|
+
"start": "2022-01-12T04:00:00.000+01:00"
|
|
23
|
+
},
|
|
24
|
+
{
|
|
25
|
+
"value": 1.7825,
|
|
26
|
+
"start": "2022-01-12T05:00:00.000+01:00"
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
"value": 1.7829,
|
|
30
|
+
"start": "2022-01-12T06:00:00.000+01:00"
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
"value": 1.8422,
|
|
34
|
+
"start": "2022-01-12T07:00:00.000+01:00"
|
|
35
|
+
},
|
|
36
|
+
{
|
|
37
|
+
"value": 1.8725,
|
|
38
|
+
"start": "2022-01-12T08:00:00.000+01:00"
|
|
39
|
+
},
|
|
40
|
+
{
|
|
41
|
+
"value": 1.8356,
|
|
42
|
+
"start": "2022-01-12T09:00:00.000+01:00"
|
|
43
|
+
},
|
|
44
|
+
{
|
|
45
|
+
"value": 1.8053,
|
|
46
|
+
"start": "2022-01-12T10:00:00.000+01:00"
|
|
47
|
+
},
|
|
48
|
+
{
|
|
49
|
+
"value": 1.7625,
|
|
50
|
+
"start": "2022-01-12T11:00:00.000+01:00"
|
|
51
|
+
},
|
|
52
|
+
{
|
|
53
|
+
"value": 1.7601,
|
|
54
|
+
"start": "2022-01-12T12:00:00.000+01:00"
|
|
55
|
+
},
|
|
56
|
+
{
|
|
57
|
+
"value": 1.7481,
|
|
58
|
+
"start": "2022-01-12T13:00:00.000+01:00"
|
|
59
|
+
},
|
|
60
|
+
{
|
|
61
|
+
"value": 1.7269,
|
|
62
|
+
"start": "2022-01-12T14:00:00.000+01:00"
|
|
63
|
+
},
|
|
64
|
+
{
|
|
65
|
+
"value": 1.717,
|
|
66
|
+
"start": "2022-01-12T15:00:00.000+01:00"
|
|
67
|
+
},
|
|
68
|
+
{
|
|
69
|
+
"value": 1.7181,
|
|
70
|
+
"start": "2022-01-12T16:00:00.000+01:00"
|
|
71
|
+
},
|
|
72
|
+
{
|
|
73
|
+
"value": 1.7169,
|
|
74
|
+
"start": "2022-01-12T17:00:00.000+01:00"
|
|
75
|
+
},
|
|
76
|
+
{
|
|
77
|
+
"value": 1.7015,
|
|
78
|
+
"start": "2022-01-12T18:00:00.000+01:00"
|
|
79
|
+
},
|
|
80
|
+
{
|
|
81
|
+
"value": 1.6708,
|
|
82
|
+
"start": "2022-01-12T19:00:00.000+01:00"
|
|
83
|
+
},
|
|
84
|
+
{
|
|
85
|
+
"value": 1.6499,
|
|
86
|
+
"start": "2022-01-12T20:00:00.000+01:00"
|
|
87
|
+
},
|
|
88
|
+
{
|
|
89
|
+
"value": 1.644,
|
|
90
|
+
"start": "2022-01-12T21:00:00.000+01:00"
|
|
91
|
+
},
|
|
92
|
+
{
|
|
93
|
+
"value": 1.624,
|
|
94
|
+
"start": "2022-01-12T22:00:00.000+01:00"
|
|
95
|
+
},
|
|
96
|
+
{
|
|
97
|
+
"value": 1.6078,
|
|
98
|
+
"start": "2022-01-12T23:00:00.000+01:00"
|
|
99
|
+
}
|
|
100
|
+
]
|
|
101
|
+
}
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
{
|
|
2
|
+
"priceData": [
|
|
3
|
+
{
|
|
4
|
+
"value": 1.7261,
|
|
5
|
+
"start": "2022-01-16T00:00:00.000+01:00"
|
|
6
|
+
},
|
|
7
|
+
{
|
|
8
|
+
"value": 1.7153,
|
|
9
|
+
"start": "2022-01-16T01:00:00.000+01:00"
|
|
10
|
+
},
|
|
11
|
+
{
|
|
12
|
+
"value": 1.6872,
|
|
13
|
+
"start": "2022-01-16T02:00:00.000+01:00"
|
|
14
|
+
},
|
|
15
|
+
{
|
|
16
|
+
"value": 1.6842,
|
|
17
|
+
"start": "2022-01-16T03:00:00.000+01:00"
|
|
18
|
+
},
|
|
19
|
+
{
|
|
20
|
+
"value": 1.6831,
|
|
21
|
+
"start": "2022-01-16T04:00:00.000+01:00"
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
"value": 1.6811,
|
|
25
|
+
"start": "2022-01-16T05:00:00.000+01:00"
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
"value": 1.6117,
|
|
29
|
+
"start": "2022-01-16T06:00:00.000+01:00"
|
|
30
|
+
},
|
|
31
|
+
{
|
|
32
|
+
"value": 1.5729,
|
|
33
|
+
"start": "2022-01-16T07:00:00.000+01:00"
|
|
34
|
+
},
|
|
35
|
+
{
|
|
36
|
+
"value": 1.5969,
|
|
37
|
+
"start": "2022-01-16T08:00:00.000+01:00"
|
|
38
|
+
},
|
|
39
|
+
{
|
|
40
|
+
"value": 1.6049,
|
|
41
|
+
"start": "2022-01-16T09:00:00.000+01:00"
|
|
42
|
+
},
|
|
43
|
+
{
|
|
44
|
+
"value": 1.6117,
|
|
45
|
+
"start": "2022-01-16T10:00:00.000+01:00"
|
|
46
|
+
},
|
|
47
|
+
{
|
|
48
|
+
"value": 1.6103,
|
|
49
|
+
"start": "2022-01-16T11:00:00.000+01:00"
|
|
50
|
+
},
|
|
51
|
+
{
|
|
52
|
+
"value": 1.6004,
|
|
53
|
+
"start": "2022-01-16T12:00:00.000+01:00"
|
|
54
|
+
},
|
|
55
|
+
{
|
|
56
|
+
"value": 1.5937,
|
|
57
|
+
"start": "2022-01-16T13:00:00.000+01:00"
|
|
58
|
+
},
|
|
59
|
+
{
|
|
60
|
+
"value": 1.5476,
|
|
61
|
+
"start": "2022-01-16T14:00:00.000+01:00"
|
|
62
|
+
},
|
|
63
|
+
{
|
|
64
|
+
"value": 1.5866,
|
|
65
|
+
"start": "2022-01-16T15:00:00.000+01:00"
|
|
66
|
+
},
|
|
67
|
+
{
|
|
68
|
+
"value": 1.5959,
|
|
69
|
+
"start": "2022-01-16T16:00:00.000+01:00"
|
|
70
|
+
},
|
|
71
|
+
{
|
|
72
|
+
"value": 1.6316,
|
|
73
|
+
"start": "2022-01-16T17:00:00.000+01:00"
|
|
74
|
+
},
|
|
75
|
+
{
|
|
76
|
+
"value": 1.5908,
|
|
77
|
+
"start": "2022-01-16T18:00:00.000+01:00"
|
|
78
|
+
},
|
|
79
|
+
{
|
|
80
|
+
"value": 1.5805,
|
|
81
|
+
"start": "2022-01-16T19:00:00.000+01:00"
|
|
82
|
+
},
|
|
83
|
+
{
|
|
84
|
+
"value": 1.5808,
|
|
85
|
+
"start": "2022-01-16T20:00:00.000+01:00"
|
|
86
|
+
},
|
|
87
|
+
{
|
|
88
|
+
"value": 1.5681,
|
|
89
|
+
"start": "2022-01-16T21:00:00.000+01:00"
|
|
90
|
+
},
|
|
91
|
+
{
|
|
92
|
+
"value": 1.5218,
|
|
93
|
+
"start": "2022-01-16T22:00:00.000+01:00"
|
|
94
|
+
},
|
|
95
|
+
{
|
|
96
|
+
"value": 1.46,
|
|
97
|
+
"start": "2022-01-16T23:00:00.000+01:00"
|
|
98
|
+
}
|
|
99
|
+
],
|
|
100
|
+
"source": "Tibber"
|
|
101
|
+
}
|
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
const cloneDeep = require("lodash.clonedeep");
|
|
3
|
+
const { DateTime } = require("luxon");
|
|
4
|
+
const expect = require("expect");
|
|
5
|
+
const helper = require("node-red-node-test-helper");
|
|
6
|
+
const node = require("../src/strategy-heat-capacitor.js");
|
|
7
|
+
const prices = require("./data/converted-prices.json");
|
|
8
|
+
const multiTrade = require("./data/multiple-trades.json");
|
|
9
|
+
|
|
10
|
+
helper.init(require.resolve("node-red"));
|
|
11
|
+
|
|
12
|
+
describe("ps-strategy-heat-capacitor node", function () {
|
|
13
|
+
beforeEach(function (done) {
|
|
14
|
+
helper.startServer(done);
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
afterEach(function (done) {
|
|
18
|
+
helper.unload().then(function () {
|
|
19
|
+
helper.stopServer(done);
|
|
20
|
+
});
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
it("should be loaded", function (done) {
|
|
24
|
+
const flow = [{ id: "n1", type: "ps-strategy-heat-capacitor", name: "Temp. Adj." }];
|
|
25
|
+
helper.load(node, flow, function () {
|
|
26
|
+
const n1 = helper.getNode("n1");
|
|
27
|
+
expect(n1).toHaveProperty("name", "Temp. Adj.");
|
|
28
|
+
done();
|
|
29
|
+
});
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
it("should log error when illegal data is received", function (done) {
|
|
33
|
+
const flow = [{ id: "n1", type: "ps-strategy-heat-capacitor", name: "Temp. Adj." }];
|
|
34
|
+
helper.load(node, flow, function () {
|
|
35
|
+
const n1 = helper.getNode("n1");
|
|
36
|
+
n1.receive({});
|
|
37
|
+
n1.warn.should.be.calledWithExactly("No payload");
|
|
38
|
+
n1.receive({ payload: "Error" });
|
|
39
|
+
n1.warn.should.be.calledWithExactly("Payload is not an object");
|
|
40
|
+
n1.receive({ payload: [] });
|
|
41
|
+
n1.warn.should.be.calledWithExactly("Payload is missing priceData");
|
|
42
|
+
n1.receive({ payload: { priceData: [] } });
|
|
43
|
+
n1.warn.should.be.calledWithExactly("priceData is empty");
|
|
44
|
+
n1.receive({ payload: { priceData: { today: [], tomorrow: [] } } });
|
|
45
|
+
n1.warn.should.be.calledWithExactly("Illegal priceData in payload. Did you use the receive-price node?");
|
|
46
|
+
|
|
47
|
+
["start", "value"].forEach((attr) => {
|
|
48
|
+
const testData1 = cloneDeep(prices);
|
|
49
|
+
delete testData1.priceData[3][attr];
|
|
50
|
+
n1.receive({ payload: testData1 });
|
|
51
|
+
n1.warn.should.be.calledWithExactly(
|
|
52
|
+
"Malformed entries in priceData. All entries must contain start and value."
|
|
53
|
+
);
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
n1.receive({ payload: cloneDeep(prices) });
|
|
57
|
+
n1.warn.should.not.be.called;
|
|
58
|
+
done();
|
|
59
|
+
});
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
it("should be configurable", function (done) {
|
|
63
|
+
const flow = [{ id: "n1", type: "ps-strategy-heat-capacitor", name: "Heat Capacitor" }];
|
|
64
|
+
helper.load(node, flow, function () {
|
|
65
|
+
const n1 = helper.getNode("n1");
|
|
66
|
+
n1.receive({
|
|
67
|
+
payload: {
|
|
68
|
+
config: {
|
|
69
|
+
timeHeat1C: 1,
|
|
70
|
+
timeCool1C: 2,
|
|
71
|
+
setpoint: 3,
|
|
72
|
+
maxTempAdjustment: 4,
|
|
73
|
+
minSavings: 5,
|
|
74
|
+
boostTempHeat: 6,
|
|
75
|
+
boostTempCool: 7,
|
|
76
|
+
},
|
|
77
|
+
},
|
|
78
|
+
});
|
|
79
|
+
expect(n1).toHaveProperty("timeHeat1C", 1);
|
|
80
|
+
expect(n1).toHaveProperty("timeCool1C", 2);
|
|
81
|
+
expect(n1).toHaveProperty("boostTempHeat", 6);
|
|
82
|
+
expect(n1).toHaveProperty("boostTempCool", 7);
|
|
83
|
+
expect(n1).toHaveProperty("setpoint", 3);
|
|
84
|
+
expect(n1).toHaveProperty("maxTempAdjustment", 4);
|
|
85
|
+
expect(n1).toHaveProperty("minSavings", 5);
|
|
86
|
+
n1.receive({ payload: { config: { setpoint: 24 } } });
|
|
87
|
+
expect(n1).toHaveProperty("setpoint", 24);
|
|
88
|
+
done();
|
|
89
|
+
});
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
it("should plan correctly", function (done) {
|
|
93
|
+
const result = 0.5;
|
|
94
|
+
const flow = makeFlow();
|
|
95
|
+
helper.load(node, flow, function () {
|
|
96
|
+
const n1 = helper.getNode("n1");
|
|
97
|
+
const n2 = helper.getNode("n2");
|
|
98
|
+
const n3 = helper.getNode("n3");
|
|
99
|
+
let bothReceived = false;
|
|
100
|
+
n2.on("input", function (msg) {
|
|
101
|
+
expect(msg).toHaveProperty("payload", 22.5);
|
|
102
|
+
n1.warn.should.not.be.called;
|
|
103
|
+
bothReceived ? done() : (bothReceived = true);
|
|
104
|
+
});
|
|
105
|
+
n3.on("input", function (msg) {
|
|
106
|
+
expect(msg).toHaveProperty("payload", -0.5);
|
|
107
|
+
n1.warn.should.not.be.called;
|
|
108
|
+
bothReceived ? done() : (bothReceived = true);
|
|
109
|
+
});
|
|
110
|
+
const time = DateTime.fromISO(prices.priceData[10].start);
|
|
111
|
+
const p = cloneDeep(prices);
|
|
112
|
+
p.time = time;
|
|
113
|
+
n1.receive({ payload: p });
|
|
114
|
+
});
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
it("should plan correctly, multiTrade", function (done) {
|
|
118
|
+
const result = 0.5;
|
|
119
|
+
const flow = makeFlow();
|
|
120
|
+
helper.load(node, flow, function () {
|
|
121
|
+
const n1 = helper.getNode("n1");
|
|
122
|
+
const n2 = helper.getNode("n2");
|
|
123
|
+
const n3 = helper.getNode("n3");
|
|
124
|
+
let bothReceived = false;
|
|
125
|
+
n2.on("input", function (msg) {
|
|
126
|
+
expect(msg).toHaveProperty("payload", 24.5);
|
|
127
|
+
n1.warn.should.not.be.called;
|
|
128
|
+
bothReceived ? done() : (bothReceived = true);
|
|
129
|
+
});
|
|
130
|
+
n3.on("input", function (msg) {
|
|
131
|
+
expect(msg).toHaveProperty("payload", 1.5);
|
|
132
|
+
n1.warn.should.not.be.called;
|
|
133
|
+
bothReceived ? done() : (bothReceived = true);
|
|
134
|
+
});
|
|
135
|
+
const time = DateTime.fromISO(multiTrade.priceData[4].start).plus({ minutes: 10 });
|
|
136
|
+
multiTrade.time = time;
|
|
137
|
+
n1.receive({ payload: multiTrade });
|
|
138
|
+
});
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
it("should merge and trim priceData", function (done) {
|
|
142
|
+
const flow = makeFlow();
|
|
143
|
+
helper.load(node, flow, function () {
|
|
144
|
+
const n1 = helper.getNode("n1");
|
|
145
|
+
n1.receive({ payload: multiTrade });
|
|
146
|
+
n1.receive({ payload: prices });
|
|
147
|
+
expect(n1.priceData.length).toEqual(72);
|
|
148
|
+
done();
|
|
149
|
+
});
|
|
150
|
+
});
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
function makeFlow() {
|
|
154
|
+
return [
|
|
155
|
+
{
|
|
156
|
+
id: "n1",
|
|
157
|
+
type: "ps-strategy-heat-capacitor",
|
|
158
|
+
name: "Temp. Adj.",
|
|
159
|
+
timeHeat1C: 60,
|
|
160
|
+
timeCool1C: 50,
|
|
161
|
+
boostTempHeat: 1,
|
|
162
|
+
boostTempCool: 1,
|
|
163
|
+
setpoint: 23,
|
|
164
|
+
maxTempAdjustment: 0.5,
|
|
165
|
+
minSavings: 0.08,
|
|
166
|
+
wires: [["n2"], ["n3"], ["n4"]],
|
|
167
|
+
},
|
|
168
|
+
{ id: "n2", type: "helper" },
|
|
169
|
+
{ id: "n3", type: "helper" },
|
|
170
|
+
{ id: "n4", type: "helper" },
|
|
171
|
+
];
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
function makePayload(prices, time) {
|
|
175
|
+
const payload = cloneDeep(prices);
|
|
176
|
+
payload.time = time;
|
|
177
|
+
let entryTime = DateTime.fromISO(payload.time);
|
|
178
|
+
payload.priceData.forEach((e) => {
|
|
179
|
+
e.start = entryTime.toISO();
|
|
180
|
+
entryTime = entryTime.plus({ milliseconds: 10 });
|
|
181
|
+
});
|
|
182
|
+
return payload;
|
|
183
|
+
}
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
const { DateTime } = require("luxon");
|
|
3
|
+
const expect = require("expect");
|
|
4
|
+
const {
|
|
5
|
+
calculateSchedule,
|
|
6
|
+
calculateOpportunities,
|
|
7
|
+
findBestBuySellPattern,
|
|
8
|
+
calculateValueDictList,
|
|
9
|
+
removeLowBuySellPairs,
|
|
10
|
+
} = require("../src/strategy-heat-capacitor-functions");
|
|
11
|
+
const converted_prices = require("./data/converted-prices.json");
|
|
12
|
+
const decreasing_end_prices = require("./data/tibber-decreasing2-24h.json");
|
|
13
|
+
|
|
14
|
+
describe("Test heat capacitor strategy functions", () => {
|
|
15
|
+
let prices, decreasing_24h_prices, start_date, buy_pattern, sell_pattern;
|
|
16
|
+
|
|
17
|
+
//User input
|
|
18
|
+
const timeHeat1C = 60;
|
|
19
|
+
const timeCool1C = 40;
|
|
20
|
+
const maxTempAdjustment = 1;
|
|
21
|
+
const boostTempHeat = 1;
|
|
22
|
+
const boostTempCool = 1;
|
|
23
|
+
const minSavings = 0.1;
|
|
24
|
+
|
|
25
|
+
before(function () {
|
|
26
|
+
prices = converted_prices.priceData.slice(0, 1).map((p) => p.value);
|
|
27
|
+
decreasing_24h_prices = decreasing_end_prices.priceData.slice(0, 1).map((p) => p.value);
|
|
28
|
+
start_date = DateTime.fromISO(converted_prices.priceData[0].start);
|
|
29
|
+
buy_pattern = Array(Math.round(timeHeat1C * maxTempAdjustment * 2)).fill(1);
|
|
30
|
+
sell_pattern = Array(Math.round(timeCool1C * maxTempAdjustment * 2)).fill(1);
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
it("Can calculate procurement opportunities", () => {
|
|
34
|
+
const my_prices = prices.slice(0, 1);
|
|
35
|
+
const my_buy_pattern = Array(5).fill(1);
|
|
36
|
+
//Calculate what it will cost to procure/sell 1 kWh as a function of time
|
|
37
|
+
let result = calculateOpportunities(my_prices, my_buy_pattern, 1);
|
|
38
|
+
//Remove float precisions errors by rounding
|
|
39
|
+
result = result.map((x) => Math.round(x * 1000000) / 1000000);
|
|
40
|
+
expect(result).toEqual(Array(56).fill(my_prices[0]));
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
it("Can find procurement pattern", () => {
|
|
44
|
+
//Use a simple price list
|
|
45
|
+
const my_prices = [1, 2, 2, 1, 8, 1];
|
|
46
|
+
|
|
47
|
+
const buy_prices = calculateOpportunities(my_prices, buy_pattern, 1);
|
|
48
|
+
const sell_prices = calculateOpportunities(my_prices, sell_pattern, 1);
|
|
49
|
+
|
|
50
|
+
const my_buy_sell = findBestBuySellPattern(buy_prices, buy_pattern.length, sell_prices, sell_pattern.length);
|
|
51
|
+
|
|
52
|
+
expect(my_buy_sell).toEqual([
|
|
53
|
+
[0, 141],
|
|
54
|
+
[100, 240],
|
|
55
|
+
]);
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
it("DictList test", () => {
|
|
59
|
+
const my_prices = [1, 2, 2, 1, 8, 1];
|
|
60
|
+
const my_buy_sell_indexes = [
|
|
61
|
+
[0, 173],
|
|
62
|
+
[131, 251],
|
|
63
|
+
];
|
|
64
|
+
const buy_prices = calculateOpportunities(my_prices, buy_pattern, 1);
|
|
65
|
+
const sell_prices = calculateOpportunities(my_prices, sell_pattern, 1);
|
|
66
|
+
const result = calculateValueDictList(my_buy_sell_indexes, buy_prices, sell_prices, start_date);
|
|
67
|
+
|
|
68
|
+
expect(result[0].sellDate).toEqual(start_date.plus({ minutes: 131 }));
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
it("DictList test at decreasing end", () => {
|
|
72
|
+
const my_prices = decreasing_end_prices.priceData.map((p) => p.value);
|
|
73
|
+
const buy_prices = calculateOpportunities(my_prices, buy_pattern, 1);
|
|
74
|
+
const sell_prices = calculateOpportunities(my_prices, sell_pattern, 1);
|
|
75
|
+
|
|
76
|
+
const my_buy_sell = findBestBuySellPattern(buy_prices, buy_pattern.length, sell_prices, sell_pattern.length);
|
|
77
|
+
const my_schedule = calculateSchedule(
|
|
78
|
+
start_date,
|
|
79
|
+
my_buy_sell,
|
|
80
|
+
buy_prices,
|
|
81
|
+
sell_prices,
|
|
82
|
+
maxTempAdjustment,
|
|
83
|
+
boostTempHeat,
|
|
84
|
+
boostTempCool,
|
|
85
|
+
buy_pattern.length,
|
|
86
|
+
sell_pattern.length
|
|
87
|
+
);
|
|
88
|
+
expect(my_schedule.temperatures[my_schedule.temperatures.length - 1]).toEqual(-maxTempAdjustment);
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
it("Check removal of low benefit buy-sell pairs", () => {
|
|
92
|
+
const my_prices = [1, 2, 1, 1.05, 1, 2];
|
|
93
|
+
const buy_prices = calculateOpportunities(my_prices, buy_pattern, 1);
|
|
94
|
+
const sell_prices = calculateOpportunities(my_prices, sell_pattern, 1);
|
|
95
|
+
const my_buy_sell = findBestBuySellPattern(buy_prices, buy_pattern.length, sell_prices, sell_pattern.length);
|
|
96
|
+
|
|
97
|
+
const result = removeLowBuySellPairs(my_buy_sell, buy_prices, sell_prices, minSavings, start_date);
|
|
98
|
+
//Should remove the sell at 1.05 and the re-buy at 1 (only 0.05 difference)
|
|
99
|
+
const compare = [my_buy_sell[0].slice(0, 2), [my_buy_sell[1][0], my_buy_sell[1][2]]];
|
|
100
|
+
|
|
101
|
+
expect(result).toEqual(compare);
|
|
102
|
+
});
|
|
103
|
+
});
|
|
@@ -2,7 +2,7 @@ const { DateTime } = require("luxon");
|
|
|
2
2
|
const expect = require("expect");
|
|
3
3
|
const { getBestContinuous, getBestX } = require("../src/strategy-lowest-price-functions");
|
|
4
4
|
const convertedPrices = require("./data/converted-prices.json");
|
|
5
|
-
const
|
|
5
|
+
const cloneDeep = require("lodash.clonedeep");
|
|
6
6
|
|
|
7
7
|
describe("strategy-utils", () => {
|
|
8
8
|
it("can find best x", () => {
|
package/test/utils.test.js
CHANGED
|
@@ -5,14 +5,12 @@ const {
|
|
|
5
5
|
sortedIndex,
|
|
6
6
|
firstOn,
|
|
7
7
|
getDiffToNextOn,
|
|
8
|
-
isOnOffSequencesOk,
|
|
9
8
|
getSavings,
|
|
10
9
|
countAtEnd,
|
|
11
10
|
makeSchedule,
|
|
12
11
|
fillArray,
|
|
13
12
|
extractPlanForDate,
|
|
14
13
|
isSameDate,
|
|
15
|
-
getStartAtIndex,
|
|
16
14
|
} = require("../src/utils");
|
|
17
15
|
|
|
18
16
|
describe("utils", () => {
|
|
File without changes
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
.config>p[data-v-78951500]{margin:0;padding:0}td[data-v-78951500],th[data-v-78951500]{margin:2px 4px;padding:2px 4px;font-size:small}.highlightSequence[data-v-78951500]{background-color:#87cefa}.highlightSaving[data-v-78951500]{background-color:#ff0}.highlightBoth[data-v-78951500]{background-color:#90ee90}.belowMin[data-v-78951500]{color:red}.selectable[data-v-78951500]{cursor:pointer}.sepcol[data-v-78951500]{background-color:#a9a9a9}
|
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
.icon.outbound{position:relative;display:inline-block;color:#aaa;vertical-align:middle;top:-1px}
|
|
2
|
-
:root{--c-brand:#3eaf7c;--c-brand-light:#4abf8a;--c-bg:#ffffff;--c-bg-light:#f3f4f5;--c-bg-lighter:#eeeeee;--c-bg-navbar:var(--c-bg);--c-bg-sidebar:var(--c-bg);--c-bg-arrow:#cccccc;--c-text:#2c3e50;--c-text-accent:var(--c-brand);--c-text-light:#3a5169;--c-text-lighter:#4e6e8e;--c-text-lightest:#6a8bad;--c-text-quote:#999999;--c-border:#eaecef;--c-border-dark:#dfe2e5;--c-tip:#42b983;--c-tip-bg:var(--c-bg-light);--c-tip-title:var(--c-text);--c-tip-text:var(--c-text);--c-tip-text-accent:var(--c-text-accent);--c-warning:#e7c000;--c-warning-bg:#fffae3;--c-warning-title:#ad9000;--c-warning-text:#746000;--c-warning-text-accent:var(--c-text);--c-danger:#cc0000;--c-danger-bg:#ffe0e0;--c-danger-title:#990000;--c-danger-text:#660000;--c-danger-text-accent:var(--c-text);--c-details-bg:#eeeeee;--c-badge-tip:var(--c-tip);--c-badge-warning:var(--c-warning);--c-badge-danger:var(--c-danger);--t-color:0.3s ease;--t-transform:0.3s ease;--code-bg-color:#282c34;--code-hl-bg-color:rgba(0, 0, 0, 0.66);--code-ln-color:#9e9e9e;--code-ln-wrapper-width:3.5rem;--font-family:-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen, Ubuntu, Cantarell, "Fira Sans", "Droid Sans", "Helvetica Neue", sans-serif;--font-family-code:Consolas, Monaco, "Andale Mono", "Ubuntu Mono", monospace;--navbar-height:3.6rem;--navbar-padding-v:0.7rem;--navbar-padding-h:1.5rem;--sidebar-width:20rem;--sidebar-width-mobile:calc(var(--sidebar-width) * 0.82);--content-width:740px;--homepage-width:960px}.back-to-top{--back-to-top-color:var(--c-brand);--back-to-top-color-hover:var(--c-brand-light)}.DocSearch{--docsearch-primary-color:var(--c-brand);--docsearch-text-color:var(--c-text);--docsearch-highlight-color:var(--c-brand);--docsearch-muted-color:var(--c-text-quote);--docsearch-container-background:rgba(9, 10, 17, 0.8);--docsearch-modal-background:var(--c-bg-light);--docsearch-searchbox-background:var(--c-bg-lighter);--docsearch-searchbox-focus-background:var(--c-bg);--docsearch-searchbox-shadow:inset 0 0 0 2px var(--c-brand);--docsearch-hit-color:var(--c-text-light);--docsearch-hit-active-color:var(--c-bg);--docsearch-hit-background:var(--c-bg);--docsearch-hit-shadow:0 1px 3px 0 var(--c-border-dark);--docsearch-footer-background:var(--c-bg)}.medium-zoom-overlay{--medium-zoom-bg-color:var(--c-bg)}#nprogress{--nprogress-color:var(--c-brand)}.pwa-popup{--pwa-popup-text-color:var(--c-text);--pwa-popup-bg-color:var(--c-bg);--pwa-popup-border-color:var(--c-brand);--pwa-popup-shadow:0 4px 16px var(--c-brand);--pwa-popup-btn-text-color:var(--c-bg);--pwa-popup-btn-bg-color:var(--c-brand);--pwa-popup-btn-hover-bg-color:var(--c-brand-light)}.search-box{--search-bg-color:var(--c-bg);--search-accent-color:var(--c-brand);--search-text-color:var(--c-text);--search-border-color:var(--c-border);--search-item-text-color:var(--c-text-lighter);--search-item-focus-bg-color:var(--c-bg-light)}html.dark{--c-brand:#3aa675;--c-brand-light:#349469;--c-bg:#22272e;--c-bg-light:#2b313a;--c-bg-lighter:#262c34;--c-text:#adbac7;--c-text-light:#96a7b7;--c-text-lighter:#8b9eb0;--c-text-lightest:#8094a8;--c-border:#3e4c5a;--c-border-dark:#34404c;--c-tip:#318a62;--c-warning:#ceab00;--c-warning-bg:#7e755b;--c-warning-title:#ceac03;--c-warning-text:#362e00;--c-danger:#940000;--c-danger-bg:#806161;--c-danger-title:#610000;--c-danger-text:#3a0000;--c-details-bg:#323843;--code-hl-bg-color:#363b46;color-scheme:dark}html.dark .DocSearch{--docsearch-logo-color:var(--c-text);--docsearch-modal-shadow:inset 1px 1px 0 0 #2c2e40, 0 3px 8px 0 #000309;--docsearch-key-shadow:inset 0 -2px 0 0 #282d55, inset 0 0 1px 1px #51577d, 0 2px 2px 0 rgba(3, 4, 9, 0.3);--docsearch-key-gradient:linear-gradient(-225deg, #444950, #1c1e21);--docsearch-footer-shadow:inset 0 1px 0 0 rgba(73, 76, 106, 0.5), 0 -4px 8px 0 rgba(0, 0, 0, 0.2)}body,html{padding:0;margin:0;background-color:var(--c-bg);transition:background-color var(--t-color)}body{font-family:var(--font-family);-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;font-size:16px;color:var(--c-text)}a,p a code{color:var(--c-text-accent)}a{font-weight:500;overflow-wrap:break-word}p a code{font-weight:400}code,kbd{font-family:var(--font-family-code)}kbd{background:var(--c-bg-lighter);border:solid .15rem var(--c-border-dark);border-bottom:solid .25rem var(--c-border-dark);border-radius:.15rem;padding:0 .15em}code{color:var(--c-text-lighter);padding:.25rem .5rem;font-size:.85em;background-color:var(--c-bg-light);border-radius:3px;overflow-wrap:break-word;transition:background-color var(--t-color),color var(--t-color)}blockquote{color:var(--c-text-quote);border-left:.2rem solid var(--c-border-dark);margin:1rem 0;padding:.25rem 0 .25rem 1rem}blockquote>p,code{margin:0}ol,ul{padding-left:1.2em}strong{font-weight:600}h1,h2,h3,h4,h5,h6{font-weight:600;line-height:1.25}h1:hover .header-anchor,h2:hover .header-anchor,h3:hover .header-anchor,h4:hover .header-anchor,h5:hover .header-anchor,h6:hover .header-anchor{opacity:1}h1{font-size:2.2rem}h2{font-size:1.65rem;padding-bottom:.3rem;border-bottom:1px solid var(--c-border);transition:border-color var(--t-color)}h3{font-size:1.35rem}h4{font-size:1.15rem}h5{font-size:1.05rem}blockquote,h6{font-size:1rem}a.header-anchor{font-size:.85em;float:left;margin-left:-.87em;padding-right:.23em;margin-top:.125em;opacity:0}a,a.header-anchor:hover{text-decoration:none}a.header-anchor:focus-visible{opacity:1}ol,p,ul{line-height:1.7}hr{border:0;border-top:1px solid var(--c-border)}table{border-collapse:collapse;margin:1rem 0;display:block;overflow-x:auto}tr{border-top:1px solid var(--c-border-dark)}tr:nth-child(2n){background-color:var(--c-bg-light)}td,th{border:1px solid var(--c-border-dark);padding:.6em 1em}.arrow,.badge{display:inline-block}.arrow{width:0;height:0}.arrow.down,.arrow.up{border-left:4px solid transparent;border-right:4px solid transparent}.arrow.up{border-bottom:6px solid var(--c-bg-arrow)}.arrow.down{border-top:6px solid var(--c-bg-arrow)}.arrow.left,.arrow.right{border-top:4px solid transparent;border-bottom:4px solid transparent}.arrow.right{border-left:6px solid var(--c-bg-arrow)}.arrow.left{border-right:6px solid var(--c-bg-arrow)}.badge{font-size:14px;height:18px;line-height:18px;border-radius:3px;padding:0 6px;color:var(--c-bg);vertical-align:top;transition:color var(--t-color),background-color var(--t-color)}.badge.tip{background-color:var(--c-badge-tip)}.badge.warning{background-color:var(--c-badge-warning)}.badge.danger{background-color:var(--c-badge-danger)}.badge+.badge{margin-left:5px}code[class*=language-],pre[class*=language-]{color:#ccc;background:0 0;font-family:var(--font-family-code);font-size:1em;text-align:left;white-space:pre;word-spacing:normal;word-break:normal;word-wrap:normal;line-height:1.5;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-hyphens:none;-ms-hyphens:none;hyphens:none}pre[class*=language-]{padding:1em;margin:.5em 0;overflow:auto}:not(pre)>code[class*=language-],pre[class*=language-]{background:#2d2d2d}:not(pre)>code[class*=language-]{padding:.1em;border-radius:.3em;white-space:normal}.token.block-comment,.token.cdata,.token.comment,.token.doctype,.token.prolog{color:#999}.token.punctuation{color:#ccc}.token.attr-name,.token.deleted,.token.namespace,.token.tag{color:#ec5975}.token.function-name{color:#6196cc}.token.boolean,.token.function,.token.number{color:#f08d49}.token.class-name,.token.constant,.token.property,.token.symbol{color:#f8c555}.token.atrule,.token.builtin,.token.important,.token.keyword,.token.selector{color:#cc99cd}.token.attr-value,.token.char,.token.regex,.token.string,.token.variable{color:#7ec699}.token.entity,.token.operator,.token.url{color:#67cdcc}.token.bold,.token.important{font-weight:700}.token.italic{font-style:italic}.token.entity{cursor:help}.token.inserted{color:#3eaf7c}.theme-default-content pre,.theme-default-content pre[class*=language-]{line-height:1.4;padding:1.25rem 1.5rem;margin:.85rem 0;border-radius:6px;overflow:auto}.theme-default-content pre code,.theme-default-content pre[class*=language-] code{color:#fff;padding:0;background-color:transparent;border-radius:0;overflow-wrap:unset;-webkit-font-smoothing:auto;-moz-osx-font-smoothing:auto}.theme-default-content .line-number{font-family:var(--font-family-code)}div[class*=language-]{position:relative;background-color:var(--code-bg-color);border-radius:6px}div[class*=language-]::before{position:absolute;z-index:3;top:.8em;right:1em;font-size:.75rem;color:var(--code-ln-color)}div[class*=language-] pre,div[class*=language-] pre[class*=language-]{background:0 0!important;position:relative;z-index:1}div[class*=language-] .highlight-lines{-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;padding-top:1.3rem;position:absolute;top:0;left:0;width:100%;line-height:1.4}div[class*=language-] .highlight-lines .highlight-line{background-color:var(--code-hl-bg-color)}div[class*=language-]:not(.line-numbers-mode) .line-numbers{display:none}div[class*=language-].line-numbers-mode .highlight-lines .highlight-line{position:relative}div[class*=language-].line-numbers-mode .highlight-lines .highlight-line::before{content:" ";position:absolute;z-index:2;left:0;top:0;display:block;width:var(--code-ln-wrapper-width);height:100%}div[class*=language-].line-numbers-mode pre{margin-left:var(--code-ln-wrapper-width);padding-left:1rem;vertical-align:middle}div[class*=language-].line-numbers-mode .line-numbers{position:absolute;top:0;width:var(--code-ln-wrapper-width);text-align:center;color:var(--code-ln-color);padding-top:1.25rem;line-height:1.4}div[class*=language-].line-numbers-mode .line-numbers .line-number,div[class*=language-].line-numbers-mode .line-numbers br{-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}div[class*=language-].line-numbers-mode .line-numbers .line-number{position:relative;z-index:3;font-size:.85em}div[class*=language-].line-numbers-mode::after{content:"";position:absolute;top:0;left:0;width:var(--code-ln-wrapper-width);height:100%;border-radius:6px 0 0 6px;border-right:1px solid var(--code-hl-bg-color)}div[class*=language-].ext-c:before{content:"c"}div[class*=language-].ext-cpp:before{content:"cpp"}div[class*=language-].ext-cs:before{content:"cs"}div[class*=language-].ext-css:before{content:"css"}div[class*=language-].ext-dart:before{content:"dart"}div[class*=language-].ext-docker:before{content:"docker"}div[class*=language-].ext-fs:before{content:"fs"}div[class*=language-].ext-go:before{content:"go"}div[class*=language-].ext-html:before{content:"html"}div[class*=language-].ext-java:before{content:"java"}div[class*=language-].ext-js:before{content:"js"}div[class*=language-].ext-json:before{content:"json"}div[class*=language-].ext-kt:before{content:"kt"}div[class*=language-].ext-less:before{content:"less"}div[class*=language-].ext-makefile:before{content:"makefile"}div[class*=language-].ext-md:before{content:"md"}div[class*=language-].ext-php:before{content:"php"}div[class*=language-].ext-py:before{content:"py"}div[class*=language-].ext-rb:before{content:"rb"}div[class*=language-].ext-rs:before{content:"rs"}div[class*=language-].ext-sass:before{content:"sass"}div[class*=language-].ext-scss:before{content:"scss"}div[class*=language-].ext-sh:before{content:"sh"}div[class*=language-].ext-styl:before{content:"styl"}div[class*=language-].ext-ts:before{content:"ts"}div[class*=language-].ext-toml:before{content:"toml"}div[class*=language-].ext-vue:before{content:"vue"}div[class*=language-].ext-yml:before{content:"yml"}@media (max-width:419px){.theme-default-content div[class*=language-]{margin:.85rem -1.5rem;border-radius:0}}.code-group__nav{margin-top:.85rem;margin-bottom:calc(-1.7rem - 6px);padding-bottom:calc(1.7rem - 6px);padding-left:10px;padding-top:10px;border-top-left-radius:6px;border-top-right-radius:6px;background-color:var(--code-bg-color)}.code-group__ul{margin:auto 0;padding-left:0;display:inline-flex;list-style:none}.code-group__nav-tab{border:0;padding:5px;cursor:pointer;background-color:transparent;font-size:.85em;line-height:1.4;color:rgba(255,255,255,.9);font-weight:600}.code-group__nav-tab:focus{outline:0}.code-group__nav-tab:focus-visible{outline:1px solid rgba(255,255,255,.9)}.code-group__nav-tab-active{border-bottom:var(--c-brand) 1px solid}@media (max-width:419px){.code-group__nav{margin-left:-1.5rem;margin-right:-1.5rem;border-radius:0}}.code-group-item,.dropdown-wrapper .nav-dropdown .dropdown-item .dropdown-subtitle>a.router-link-active::after{display:none}.code-group-item__active{display:block}.code-group-item>pre{background-color:orange}.custom-container{transition:color var(--t-color),border-color var(--t-color),background-color var(--t-color)}.custom-container .custom-container-title{font-weight:600;margin-bottom:-.4rem}.custom-container.danger,.custom-container.tip,.custom-container.warning{padding:.1rem 1.5rem;border-left-width:.5rem;border-left-style:solid;margin:1rem 0}.custom-container.tip{border-color:var(--c-tip);background-color:var(--c-tip-bg);color:var(--c-tip-text)}.custom-container.tip .custom-container-title{color:var(--c-tip-title)}.custom-container.tip a{color:var(--c-tip-text-accent)}.custom-container.warning{border-color:var(--c-warning);background-color:var(--c-warning-bg);color:var(--c-warning-text)}.custom-container.warning .custom-container-title{color:var(--c-warning-title)}.custom-container.warning a{color:var(--c-warning-text-accent)}.custom-container.danger{border-color:var(--c-danger);background-color:var(--c-danger-bg);color:var(--c-danger-text)}.custom-container.danger .custom-container-title{color:var(--c-danger-title)}.custom-container.danger a{color:var(--c-danger-text-accent)}.custom-container.details{display:block;position:relative;border-radius:2px;margin:1.6em 0;padding:1.6em;background-color:var(--c-details-bg)}.custom-container.details h4{margin-top:0}.custom-container.details figure:last-child,.custom-container.details p:last-child{margin-bottom:0;padding-bottom:0}.custom-container.details summary{outline:0;cursor:pointer}.dropdown-wrapper{cursor:pointer}.dropdown-wrapper .dropdown-title,.dropdown-wrapper .mobile-dropdown-title{display:block;font-size:.9rem;font-family:inherit;cursor:inherit;padding:inherit;line-height:1.4rem;background:0 0;border:0;font-weight:500;color:var(--c-text)}.dropdown-wrapper .mobile-dropdown-title{display:none;font-weight:600;font-size:inherit}.dropdown-wrapper .dropdown-title:hover,.dropdown-wrapper .mobile-dropdown-title:hover{border-color:transparent}.dropdown-wrapper .dropdown-title .arrow,.dropdown-wrapper .mobile-dropdown-title .arrow{vertical-align:middle;margin-top:-1px;margin-left:.4rem}.dropdown-wrapper .mobile-dropdown-title:hover{color:var(--c-text-accent)}.dropdown-wrapper .nav-dropdown .dropdown-item{color:inherit;line-height:1.7rem}.dropdown-wrapper .nav-dropdown .dropdown-item .dropdown-subtitle{margin:.45rem 0 0;border-top:1px solid var(--c-border);padding:1rem 0 .45rem;font-size:.9rem}.dropdown-wrapper .nav-dropdown .dropdown-item .dropdown-subtitle>span{padding:0 1.5rem 0 1.25rem}.dropdown-wrapper .nav-dropdown .dropdown-item .dropdown-subtitle>a{font-weight:inherit}.dropdown-wrapper .nav-dropdown .dropdown-item .dropdown-subitem-wrapper{padding:0;list-style:none}.dropdown-wrapper .nav-dropdown .dropdown-item .dropdown-subitem-wrapper .dropdown-subitem{font-size:.9em}.dropdown-wrapper .nav-dropdown .dropdown-item a{display:block;line-height:1.7rem;position:relative;border-bottom:none;font-weight:400;margin-bottom:0;padding:0 1.5rem 0 1.25rem}.dropdown-wrapper .nav-dropdown .dropdown-item a.router-link-active,.dropdown-wrapper .nav-dropdown .dropdown-item a:hover,.navbar-links a.router-link-active,.navbar-links a:hover{color:var(--c-text-accent)}.dropdown-wrapper .nav-dropdown .dropdown-item a.router-link-active::after{content:"";width:0;height:0;border-left:5px solid var(--c-text-accent);border-top:3px solid transparent;border-bottom:3px solid transparent;position:absolute;top:calc(50% - 2px);left:9px}.dropdown-wrapper .nav-dropdown .dropdown-item:first-child .dropdown-subtitle{margin-top:0;padding-top:0;border-top:0}@media (max-width:719px){.dropdown-wrapper.open .dropdown-title,.dropdown-wrapper.open .mobile-dropdown-title{margin-bottom:.5rem}.dropdown-wrapper .dropdown-title{display:none}.dropdown-wrapper .mobile-dropdown-title{display:block}.dropdown-wrapper .nav-dropdown{transition:height .1s ease-out;overflow:hidden}.dropdown-wrapper .nav-dropdown .dropdown-item .dropdown-subtitle{border-top:0;margin-top:0;padding-top:0;padding-bottom:0;font-size:15px;line-height:2rem}.dropdown-wrapper .nav-dropdown .dropdown-item>a{font-size:15px;line-height:2rem}.dropdown-wrapper .nav-dropdown .dropdown-item .dropdown-subitem{font-size:14px;padding-left:1rem}}@media (min-width:720px){.dropdown-wrapper{height:1.8rem}.dropdown-wrapper.open .nav-dropdown,.dropdown-wrapper:hover .nav-dropdown{display:block!important}.dropdown-wrapper.open:blur{display:none}.dropdown-wrapper .nav-dropdown{display:none;height:auto!important;box-sizing:border-box;max-height:calc(100vh - 2.7rem);overflow-y:auto;position:absolute;top:100%;right:0;background-color:var(--c-bg-navbar);padding:.6rem 0;border:1px solid var(--c-border);border-bottom-color:var(--c-border-dark);text-align:left;border-radius:.25rem;white-space:nowrap;margin:0}}.dropdown-enter-from,.dropdown-leave-to{height:0!important}.home{padding:var(--navbar-height) 2rem 0;max-width:var(--homepage-width);margin:0 auto;display:block}.home .hero{text-align:center}.home .hero img{max-width:100%;max-height:280px;display:block;margin:3rem auto 1.5rem}.home .hero h1{font-size:3rem}.home .hero .actions,.home .hero .description,.home .hero h1{margin:1.8rem auto}.home .hero .actions{display:flex;flex-wrap:wrap;gap:1rem;justify-content:center}.home .hero .description{max-width:35rem;font-size:1.6rem;line-height:1.3;color:var(--c-text-lightest)}.home .hero .action-button{display:inline-block;font-size:1.2rem;padding:.8rem 1.6rem;border-width:2px;border-style:solid;border-radius:4px;transition:background-color var(--t-color);box-sizing:border-box}.home .hero .action-button.primary{color:var(--c-bg);background-color:var(--c-brand);border-color:var(--c-brand)}.home .hero .action-button.primary:hover{background-color:var(--c-brand-light)}.home .hero .action-button.secondary{color:var(--c-brand);background-color:var(--c-bg);border-color:var(--c-brand)}.home .hero .action-button.secondary:hover{color:var(--c-bg);background-color:var(--c-brand-light)}.home .features{border-top:1px solid var(--c-border);transition:border-color var(--t-color);padding:1.2rem 0;margin-top:2.5rem;display:flex;flex-wrap:wrap;align-items:flex-start;align-content:stretch;justify-content:space-between}.home .feature{flex-grow:1;flex-basis:30%;max-width:30%}.home .feature h2{font-size:1.4rem;font-weight:500;border-bottom:none;padding-bottom:0;color:var(--c-text-light)}.home .feature p,.home .footer{color:var(--c-text-lighter)}.home .footer{padding:2.5rem;border-top:1px solid var(--c-border);text-align:center;transition:border-color var(--t-color)}@media (max-width:719px){.home .features{flex-direction:column}.home .feature{max-width:100%;padding:0 2.5rem}}@media (max-width:419px){.home{padding-left:1.5rem;padding-right:1.5rem}.home .hero img{max-height:210px;margin:2rem auto 1.2rem}.home .hero h1{font-size:2rem}.home .hero .actions,.home .hero .description,.home .hero h1{margin:1.2rem auto}.home .hero .description{font-size:1.2rem}.home .hero .action-button{font-size:1rem;padding:.6rem 1.2rem}.home .feature h2{font-size:1.25rem}}.theme-default-content:not(.custom){max-width:var(--content-width);margin:0 auto;padding:2rem 2.5rem}@media (max-width:959px){.theme-default-content:not(.custom){padding:2rem}}@media (max-width:419px){.theme-default-content:not(.custom){padding:1.5rem}}.page{padding-top:var(--navbar-height);padding-left:var(--sidebar-width)}.navbar,.sidebar{position:fixed;left:0;box-sizing:border-box}.navbar{z-index:20;top:0;right:0;height:var(--navbar-height);border-bottom:1px solid var(--c-border);background-color:var(--c-bg-navbar);transition:background-color var(--t-color),border-color var(--t-color)}.sidebar{font-size:16px;width:var(--sidebar-width);z-index:10;margin:0;top:var(--navbar-height);bottom:0;border-right:1px solid var(--c-border);overflow-y:auto;scrollbar-width:thin;scrollbar-color:var(--c-brand) var(--c-border);background-color:var(--c-bg-sidebar);transition:transform var(--t-transform),background-color var(--t-color),border-color var(--t-color)}.sidebar::-webkit-scrollbar{width:7px}.sidebar::-webkit-scrollbar-track{background-color:var(--c-border)}.sidebar::-webkit-scrollbar-thumb{background-color:var(--c-brand)}.sidebar-mask{position:fixed;z-index:9;top:0;left:0;width:100vw;height:100vh;display:none}.theme-container.sidebar-open .sidebar-mask{display:block}.theme-container.sidebar-open .navbar>.toggle-sidebar-button .icon span:nth-child(1){transform:rotate(45deg) translate3d(5.5px,5.5px,0)}.theme-container.sidebar-open .navbar>.toggle-sidebar-button .icon span:nth-child(2){transform:scale3d(0,1,1)}.theme-container.sidebar-open .navbar>.toggle-sidebar-button .icon span:nth-child(3){transform:rotate(-45deg) translate3d(6px,-6px,0)}.theme-container.sidebar-open .navbar>.toggle-sidebar-button .icon span:nth-child(1),.theme-container.sidebar-open .navbar>.toggle-sidebar-button .icon span:nth-child(3){transform-origin:center}.theme-container.no-navbar .theme-default-content:not(.custom)>h1,.theme-container.no-navbar h2,.theme-container.no-navbar h3,.theme-container.no-navbar h4,.theme-container.no-navbar h5,.theme-container.no-navbar h6{margin-top:1.5rem;padding-top:0}.theme-container.no-navbar .page{padding-top:0}.theme-container.no-navbar .sidebar{top:0}@media (min-width:720px){.theme-container.no-sidebar .sidebar{display:none}.theme-container.no-sidebar .page{padding-left:0}}.theme-default-content:not(.custom)>h1,.theme-default-content:not(.custom)>h2,.theme-default-content:not(.custom)>h3,.theme-default-content:not(.custom)>h4,.theme-default-content:not(.custom)>h5,.theme-default-content:not(.custom)>h6{margin-top:calc(.5rem - var(--navbar-height));padding-top:calc(1rem + var(--navbar-height));margin-bottom:0}.theme-default-content:not(.custom)>h1:first-child,.theme-default-content:not(.custom)>h2:first-child,.theme-default-content:not(.custom)>h3:first-child,.theme-default-content:not(.custom)>h4:first-child,.theme-default-content:not(.custom)>h5:first-child,.theme-default-content:not(.custom)>h6:first-child{margin-bottom:1rem}.theme-default-content:not(.custom)>h1:first-child+.custom-container,.theme-default-content:not(.custom)>h1:first-child+p,.theme-default-content:not(.custom)>h1:first-child+pre,.theme-default-content:not(.custom)>h2:first-child+.custom-container,.theme-default-content:not(.custom)>h2:first-child+p,.theme-default-content:not(.custom)>h2:first-child+pre,.theme-default-content:not(.custom)>h3:first-child+.custom-container,.theme-default-content:not(.custom)>h3:first-child+p,.theme-default-content:not(.custom)>h3:first-child+pre,.theme-default-content:not(.custom)>h4:first-child+.custom-container,.theme-default-content:not(.custom)>h4:first-child+p,.theme-default-content:not(.custom)>h4:first-child+pre,.theme-default-content:not(.custom)>h5:first-child+.custom-container,.theme-default-content:not(.custom)>h5:first-child+p,.theme-default-content:not(.custom)>h5:first-child+pre,.theme-default-content:not(.custom)>h6:first-child+.custom-container,.theme-default-content:not(.custom)>h6:first-child+p,.theme-default-content:not(.custom)>h6:first-child+pre{margin-top:2rem}.theme-default-content:not(.custom){padding-top:0}.theme-default-content:not(.custom) a:hover{text-decoration:underline}.theme-default-content:not(.custom) img{max-width:100%}.theme-default-content.custom{padding:0;margin:0}.theme-default-content.custom img{max-width:100%}@media (max-width:959px){.sidebar{font-size:15px;width:var(--sidebar-width-mobile)}.page{padding-left:var(--sidebar-width-mobile)}}@media (max-width:719px){.sidebar{top:0;padding-top:var(--navbar-height);transform:translateX(-100%)}.page{padding-left:0}.theme-container.sidebar-open .sidebar{transform:translateX(0)}.theme-container.no-navbar .sidebar{padding-top:0}}@media (max-width:419px){h1{font-size:1.9rem}}.navbar{--navbar-line-height:calc( var(--navbar-height) - 2 * var(--navbar-padding-v) );padding:var(--navbar-padding-v) var(--navbar-padding-h);line-height:var(--navbar-line-height)}.navbar .logo{height:var(--navbar-line-height);margin-right:var(--navbar-padding-v);vertical-align:top}.navbar .site-name{font-size:1.3rem;font-weight:600;color:var(--c-text);position:relative}.navbar .navbar-links-wrapper{display:flex;position:absolute;box-sizing:border-box;top:var(--navbar-padding-v);right:var(--navbar-padding-h);height:var(--navbar-line-height);padding-left:var(--navbar-padding-h);white-space:nowrap;font-size:.9rem}.navbar .navbar-links-wrapper .search-box{flex:0 0 auto;vertical-align:top}@media (max-width:719px){.navbar{padding-left:4rem}.navbar .can-hide{display:none}.navbar .site-name{width:calc(100vw - 9.4rem);overflow:hidden;white-space:nowrap;text-overflow:ellipsis}}.navbar-links,.navbar-links a{display:inline-block}.navbar-links a{line-height:1.4rem;color:inherit}.navbar-links .navbar-links-item{position:relative;display:inline-block;margin-left:1.5rem;line-height:var(--navbar-line-height)}.navbar-links .navbar-links-item:first-child{margin-left:0}@media (max-width:719px){.navbar-links .navbar-links-item{margin-left:0}}@media (min-width:719px){.navbar-links a.router-link-active,.navbar-links a:hover{color:var(--c-text)}.navbar-links-item>a:not(.external).router-link-active,.navbar-links-item>a:not(.external):hover{margin-bottom:-2px;border-bottom:2px solid var(--c-text-accent)}}.toggle-sidebar-button{position:absolute;top:.6rem;left:1rem;display:none;padding:.6rem;cursor:pointer}.toggle-sidebar-button .icon{display:flex;flex-direction:column;justify-content:center;align-items:center;width:1.25rem;height:1.25rem;cursor:inherit}.toggle-sidebar-button .icon span{display:inline-block;width:100%;height:2px;border-radius:2px;background-color:var(--c-text);transition:transform var(--t-transform)}.toggle-sidebar-button .icon span:nth-child(2){margin:6px 0}@media screen and (max-width:719px){.toggle-sidebar-button{display:block}}.toggle-dark-button{display:flex;margin:auto;margin-left:1rem;border:0;background:0 0;color:var(--c-text);opacity:.8;cursor:pointer}.toggle-dark-button:hover{opacity:1}.toggle-dark-button .icon{width:1.25rem;height:1.25rem}.page-meta,.page-nav{max-width:var(--content-width);margin:0 auto;padding:2rem 2.5rem}@media (max-width:959px){.page-meta,.page-nav{padding:2rem}}@media (max-width:419px){.page-meta,.page-nav{padding:1.5rem}}.page{padding-bottom:2rem;display:block}.page-meta{padding-top:1rem;padding-bottom:1rem;overflow:auto}.page-meta .meta-item{cursor:default;margin-top:.8rem}.page-meta .meta-item .meta-item-label{font-weight:500;color:var(--c-text-lighter)}.page-meta .meta-item .meta-item-info{font-weight:400;color:var(--c-text-quote)}.page-meta .edit-link{display:inline-block;margin-right:.25rem}.page-meta .last-updated{float:right}@media (max-width:719px){.page-meta .last-updated{font-size:.8em;float:none}.page-meta .contributors{font-size:.8em}}.page-nav{padding-top:1rem;padding-bottom:0}.page-nav .inner{min-height:2rem;margin-top:0;border-top:1px solid var(--c-border);transition:border-color var(--t-color);padding-top:1rem;overflow:auto}.page-nav .next{float:right}.sidebar ul{padding:0;margin:0;list-style-type:none}.sidebar a{display:inline-block}.sidebar .navbar-links{display:none;border-bottom:1px solid var(--c-border);transition:border-color var(--t-color);padding:.5rem 0 .75rem}.sidebar .navbar-links a{font-weight:600}.sidebar .navbar-links .navbar-links-item{display:block;line-height:1.25rem;font-size:1.1em;padding:.5rem 0 .5rem 1.5rem}.sidebar .sidebar-links{padding:1.5rem 0}.sidebar .sidebar-links>li:not(:first-child),.sidebar-links>.sidebar-item:not(.sidebar-heading):not(:first-child){margin-top:.75rem}.sidebar .sidebar-links .sidebar-sub-items{padding-left:1rem;font-size:.95em}@media (max-width:719px){.sidebar .navbar-links{display:block}.sidebar .navbar-links .dropdown-wrapper .nav-dropdown .dropdown-item a.router-link-active::after{top:calc(1rem - 2px)}.sidebar .sidebar-links{padding:1rem 0}}.sidebar-heading{color:var(--c-text);transition:color .15s ease;font-size:1.1em;font-weight:700;padding:.35rem 1.5rem .35rem 1.25rem;width:100%;box-sizing:border-box;margin:0;border-left:.25rem solid transparent}.sidebar-heading .arrow{position:relative;top:-.12em;left:.5em}.sidebar-item:not(.sidebar-heading){font-size:1em;font-weight:400;display:inline-block;color:var(--c-text);border-left:.25rem solid transparent;margin:0;padding:.35rem 1rem .35rem 2rem;line-height:1.4;width:100%;box-sizing:border-box}.sidebar-sub-items .sidebar-item:not(.sidebar-heading){padding:.25rem 1rem .25rem 1.75rem}.sidebar-item{cursor:default}a.sidebar-item{cursor:pointer}a.sidebar-item.active,a.sidebar-item:hover{color:var(--c-text-accent)}a.sidebar-item.active{font-weight:600;border-left-color:var(--c-text-accent)}a.sidebar-item.sidebar-heading.active{font-weight:700;border-left-color:transparent}.sidebar-sub-items a.sidebar-item.active{font-weight:500;border-left-color:transparent}.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border-width:0;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.table-of-contents .badge{vertical-align:middle}.fade-slide-y-enter-active{transition:all .3s ease}.fade-slide-y-leave-active{transition:all .3s cubic-bezier(1,.5,.8,1)}.fade-slide-y-enter-from,.fade-slide-y-leave-to{transform:translateY(10px);opacity:0}
|
|
3
|
-
:root{--medium-zoom-z-index:100;--medium-zoom-bg-color:#ffffff;--medium-zoom-opacity:1}
|
|
4
|
-
.medium-zoom-overlay{background-color:var(--medium-zoom-bg-color)!important;z-index:var(--medium-zoom-z-index)}.medium-zoom-overlay~img{z-index:calc(var(--medium-zoom-z-index) + 1)}.medium-zoom--opened .medium-zoom-overlay{opacity:var(--medium-zoom-opacity)}
|
|
5
|
-
.resize-observer[data-v-b329ee4c]{border:0;background-color:transparent;opacity:0}.resize-observer[data-v-b329ee4c],.resize-observer[data-v-b329ee4c] object{display:block;position:absolute;top:0;left:0;height:100%;width:100%;overflow:hidden;pointer-events:none;z-index:-1}.v-popper__popper{z-index:10000;top:0;left:0}.v-popper__popper.v-popper__popper--hidden{visibility:hidden;opacity:0;transition:opacity .15s,visibility .15s}.v-popper__popper.v-popper__popper--shown{visibility:visible;opacity:1;transition:opacity .15s}.v-popper__popper.v-popper__popper--skip-transition,.v-popper__popper.v-popper__popper--skip-transition>.v-popper__wrapper{transition:none!important}.v-popper__inner{position:relative;box-sizing:border-box;overflow-y:auto}.v-popper__inner>div{position:relative;z-index:1}.v-popper__arrow-container{position:absolute;width:10px;height:10px}.v-popper__popper--arrow-overflow .v-popper__arrow-container{display:none}.v-popper__arrow-inner,.v-popper__arrow-outer{border-style:solid;position:absolute;top:0;left:0;width:0;height:0}.v-popper__arrow-inner{visibility:hidden;border-width:7px}.v-popper__arrow-outer{border-width:6px}.v-popper__popper[data-popper-placement^=bottom] .v-popper__arrow-inner,.v-popper__popper[data-popper-placement^=top] .v-popper__arrow-inner{left:-2px}.v-popper__popper[data-popper-placement^=bottom] .v-popper__arrow-outer,.v-popper__popper[data-popper-placement^=top] .v-popper__arrow-outer{left:-1px}.v-popper__popper[data-popper-placement^=top] .v-popper__arrow-inner,.v-popper__popper[data-popper-placement^=top] .v-popper__arrow-outer{border-bottom-width:0;border-left-color:transparent!important;border-right-color:transparent!important;border-bottom-color:transparent!important}.v-popper__popper[data-popper-placement^=top] .v-popper__arrow-inner{top:-2px}.v-popper__popper[data-popper-placement^=bottom] .v-popper__arrow-container{top:0}.v-popper__popper[data-popper-placement^=bottom] .v-popper__arrow-inner,.v-popper__popper[data-popper-placement^=bottom] .v-popper__arrow-outer{border-top-width:0;border-left-color:transparent!important;border-right-color:transparent!important;border-top-color:transparent!important}.v-popper__popper[data-popper-placement^=bottom] .v-popper__arrow-inner{top:-4px}.v-popper__popper[data-popper-placement^=bottom] .v-popper__arrow-outer{top:-6px}.v-popper__popper[data-popper-placement^=left] .v-popper__arrow-inner,.v-popper__popper[data-popper-placement^=right] .v-popper__arrow-inner{top:-2px}.v-popper__popper[data-popper-placement^=left] .v-popper__arrow-outer,.v-popper__popper[data-popper-placement^=right] .v-popper__arrow-outer{top:-1px}.v-popper__popper[data-popper-placement^=right] .v-popper__arrow-inner,.v-popper__popper[data-popper-placement^=right] .v-popper__arrow-outer{border-left-width:0;border-left-color:transparent!important;border-top-color:transparent!important;border-bottom-color:transparent!important}.v-popper__popper[data-popper-placement^=right] .v-popper__arrow-inner{left:-4px}.v-popper__popper[data-popper-placement^=right] .v-popper__arrow-outer{left:-6px}.v-popper__popper[data-popper-placement^=left] .v-popper__arrow-container{right:-10px}.v-popper__popper[data-popper-placement^=left] .v-popper__arrow-inner,.v-popper__popper[data-popper-placement^=left] .v-popper__arrow-outer{border-right-width:0;border-top-color:transparent!important;border-right-color:transparent!important;border-bottom-color:transparent!important}.v-popper__popper[data-popper-placement^=left] .v-popper__arrow-inner{left:-2px}.v-popper{width:-webkit-max-content;width:-moz-max-content;width:max-content}.v-popper--theme-dropdown .v-popper__inner{background:#fff;color:#000;border-radius:6px;border:1px solid #ddd;box-shadow:0 6px 30px #0000001a}.v-popper--theme-dropdown .v-popper__arrow-inner{visibility:visible;border-color:#fff}.v-popper--theme-dropdown .v-popper__arrow-outer{border-color:#ddd}.v-popper--theme-tooltip .v-popper__inner{background:rgba(0,0,0,.8);color:#fff;border-radius:6px;padding:7px 12px 6px}.v-popper--theme-tooltip .v-popper__arrow-outer{border-color:#000c}
|
|
6
|
-
:root{--back-to-top-z-index:5;--back-to-top-color:#3eaf7c;--back-to-top-color-hover:#71cda3}
|
|
7
|
-
.back-to-top{cursor:pointer;position:fixed;bottom:2rem;right:2.5rem;width:2rem;height:1.2rem;background-color:var(--back-to-top-color);-webkit-mask:url(/assets/img/back-to-top.8b37f773.svg) no-repeat;mask:url(/assets/img/back-to-top.8b37f773.svg) no-repeat;z-index:var(--back-to-top-z-index)}.back-to-top:hover{background-color:var(--back-to-top-color-hover)}@media (max-width:959px){.back-to-top{display:none}}.back-to-top-enter-active,.back-to-top-leave-active{transition:opacity .3s}.back-to-top-enter-from,.back-to-top-leave-to{opacity:0}
|
|
8
|
-
:root{--nprogress-color:#29d;--nprogress-z-index:1031}
|
|
9
|
-
#nprogress{pointer-events:none}#nprogress .bar{background:var(--nprogress-color);position:fixed;z-index:var(--nprogress-z-index);top:0;left:0;width:100%;height:2px}#nprogress .peg{display:block;position:absolute;right:0;width:100px;height:100%;box-shadow:0 0 10px var(--nprogress-color),0 0 5px var(--nprogress-color);opacity:1;transform:rotate(3deg) translate(0,-4px)}
|