Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
82004ee9cc | ||
|
|
074f5d5f5f | ||
|
|
5378eaef7a | ||
|
|
31ab13bb07 | ||
|
|
6aa072101e | ||
|
|
8df6d0f779 | ||
|
|
a7b92c2072 | ||
|
|
1f9e0e2b04 | ||
|
|
0fb8cc4974 | ||
|
|
83ee87699b | ||
|
|
6704fdc438 | ||
|
|
5537a15e52 | ||
|
|
775ccb7390 | ||
|
|
a31214da09 | ||
|
|
818a670a6c | ||
|
|
3f50c4e2da | ||
|
|
3551ca241d | ||
|
|
9f5ab34ee2 | ||
|
|
5b218beca9 | ||
|
|
df9313a056 | ||
|
|
9ff02a83a5 | ||
|
|
a34600f0e6 | ||
|
|
3b71a72636 | ||
|
|
6adefe7210 | ||
|
|
9d768185b6 | ||
|
|
88e955889f | ||
|
|
ed47db6efe | ||
|
|
f5b0fac23a | ||
|
|
7d95a8825d | ||
|
|
cf737f9087 | ||
|
|
8ed062168d | ||
|
|
cfc6d719a9 | ||
|
|
2dd3847a81 | ||
|
|
dcac48b1e8 | ||
|
|
2ff29e74d6 | ||
|
|
c99641d755 | ||
|
|
e654bf5ae8 | ||
|
|
e2755fd107 | ||
|
|
2d6f5ebf74 | ||
|
|
ef3088c3f1 | ||
|
|
d4ad6a62d1 | ||
|
|
ab66c9e194 | ||
|
|
da4ffe176c | ||
|
|
95c10f3747 | ||
|
|
1712f50ede | ||
|
|
a3b2d5606f | ||
|
|
845f925559 | ||
|
|
5c674593fb | ||
|
|
8a0b7067c1 | ||
|
|
0d68cec043 | ||
|
|
e0a3ad4532 | ||
|
|
d37d2cc7dc | ||
|
|
9d1e63243c | ||
|
|
e66839296a | ||
|
|
4908bbf28a | ||
|
|
edfc47c1cc | ||
|
|
4744c4ba1c | ||
|
|
f24689032f | ||
|
|
621548e81e | ||
|
|
3c7041c631 | ||
|
|
b592131c78 | ||
|
|
729c773052 | ||
|
|
12c1bff421 | ||
|
|
37ebd63e3b | ||
|
|
5bb115e281 | ||
|
|
cb3dc59c40 | ||
|
|
b528e15276 | ||
|
|
1fffee4958 | ||
|
|
92f326ef36 | ||
|
|
ba0ac231da | ||
|
|
84c87ad53c | ||
|
|
2d5a5416ba | ||
|
|
fbcd8254ba | ||
|
|
13233794eb | ||
|
|
43c1349e12 | ||
|
|
bfc9b96b2a | ||
|
|
1768586ca8 | ||
|
|
9ca7f6137d | ||
|
|
179d975b96 | ||
|
|
ea59a0f820 | ||
|
|
8c3a4c9dc5 | ||
|
|
752f048d7e | ||
|
|
982d47ce83 | ||
|
|
ad49d8d8c6 | ||
|
|
6def14d791 | ||
|
|
d3d54770aa | ||
|
|
11db41a128 | ||
|
|
9f2ed4e448 | ||
|
|
07a432a499 | ||
|
|
c08e026d4c | ||
|
|
74789b7b0d | ||
|
|
9f5f88e1af | ||
|
|
4c8f23ceba | ||
|
|
006e0edfa0 | ||
|
|
46575bf30d | ||
|
|
86f7f1c66d | ||
|
|
701e9f42d5 | ||
|
|
8a2b55f484 | ||
|
|
66e6040044 | ||
|
|
37a7ca9b55 | ||
|
|
dbff79a0a2 | ||
|
|
f4ee79c103 | ||
|
|
1665747cb2 | ||
|
|
54d4938656 | ||
|
|
7d1301c342 | ||
|
|
fe0818ded9 | ||
|
|
f277efc8b9 | ||
|
|
ea8abc9635 | ||
|
|
0002b2e50b | ||
|
|
47dfcd852f | ||
|
|
96b6beaa93 | ||
|
|
841d79c1ac | ||
|
|
70634f878a | ||
|
|
bc184f7884 | ||
|
|
f70d0fdaac | ||
|
|
b33db97fbb | ||
|
|
e20bb50746 | ||
|
|
387dc24a04 | ||
|
|
af9307a383 | ||
|
|
17f09c9205 | ||
|
|
c62c3e7a58 | ||
|
|
ca5637c70f | ||
|
|
e9d8a0e0d1 | ||
|
|
da5977c6a9 | ||
|
|
7b0a14171f | ||
|
|
888d912f4e | ||
|
|
09668563ad | ||
|
|
c4ed0e2997 | ||
|
|
c75c120383 | ||
|
|
72a56789e0 | ||
|
|
4271c1f334 | ||
|
|
b208a90fa0 | ||
|
|
8341f176b2 | ||
|
|
a4f1cda387 | ||
|
|
667dc0da8a | ||
|
|
c97d17b7a6 | ||
|
|
d8472b2410 | ||
|
|
f115af4715 | ||
|
|
fe90281de2 | ||
|
|
cde06f7a85 | ||
|
|
d913d95793 |
@@ -0,0 +1,5 @@
|
|||||||
|
---
|
||||||
|
deployment:
|
||||||
|
tasks:
|
||||||
|
- export DEPLOYPATH=/home/qoxpt/public_html/
|
||||||
|
- /bin/cp -R * $DEPLOYPATH/
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
---
|
||||||
|
name: Bug report
|
||||||
|
about: An issue about things that aren't working as intended.
|
||||||
|
title: "[BUG]"
|
||||||
|
labels: bug
|
||||||
|
assignees: ''
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**Description**
|
||||||
|
What the bug looks like, where it's present, and other relevant information.
|
||||||
|
|
||||||
|
**System**
|
||||||
|
On desktop:
|
||||||
|
* OS (Windows, Linux, macOS, etc.)
|
||||||
|
* Browser (Firefox, Chrome, Safari, etc.)
|
||||||
|
* Version (e.g. 22)
|
||||||
|
|
||||||
|
On mobile:
|
||||||
|
* Device (Samsung galaxy 20, iPhone 13, etc.)
|
||||||
|
* OS (Android 15, iOS 18.2.1, etc.)
|
||||||
|
* Browser (Firefox, Chrome, Safari, etc.)
|
||||||
|
* Version (e.g. 22)
|
||||||
|
|
||||||
|
**Expected behavior**
|
||||||
|
A clear and concise description of what you expected to happen.
|
||||||
|
|
||||||
|
**Screenshots**
|
||||||
|
Recommended but optional screenshots.
|
||||||
|
|
||||||
|
**Additional context**
|
||||||
|
Other context about the problem.
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
---
|
||||||
|
name: Copyright
|
||||||
|
about: For requesting takedown of content used without permission.
|
||||||
|
title: "[COPYRIGHT] Takedown request!"
|
||||||
|
labels: Copyright
|
||||||
|
assignees: ''
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**Description**
|
||||||
|
A clear description of what content is copyrighted, as well as why it violates copyright terms.
|
||||||
|
|
||||||
|
**Proof**
|
||||||
|
Proof that you are the rightful owner/creator of the content. It is recommended to attach screenshots.
|
||||||
|
|
||||||
|
**Additional context**
|
||||||
|
Add any other context or screenshots about the feature request here.
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
---
|
||||||
|
name: Feature request
|
||||||
|
about: An idea you'd like to see implemented in the website.
|
||||||
|
title: ''
|
||||||
|
labels: enhancement
|
||||||
|
assignees: ''
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**Description**
|
||||||
|
A clear description of what you'd like to be implemented.
|
||||||
|
|
||||||
|
**Additional context**
|
||||||
|
Add any other context or screenshots about the feature request here.
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
{
|
||||||
|
"editor.tabSize": 2,
|
||||||
|
"editor.insertSpaces": false,
|
||||||
|
"editor.detectIndentation": false
|
||||||
|
}
|
||||||
@@ -0,0 +1,437 @@
|
|||||||
|
Attribution-NonCommercial-ShareAlike 4.0 International
|
||||||
|
|
||||||
|
=======================================================================
|
||||||
|
|
||||||
|
Creative Commons Corporation ("Creative Commons") is not a law firm and
|
||||||
|
does not provide legal services or legal advice. Distribution of
|
||||||
|
Creative Commons public licenses does not create a lawyer-client or
|
||||||
|
other relationship. Creative Commons makes its licenses and related
|
||||||
|
information available on an "as-is" basis. Creative Commons gives no
|
||||||
|
warranties regarding its licenses, any material licensed under their
|
||||||
|
terms and conditions, or any related information. Creative Commons
|
||||||
|
disclaims all liability for damages resulting from their use to the
|
||||||
|
fullest extent possible.
|
||||||
|
|
||||||
|
Using Creative Commons Public Licenses
|
||||||
|
|
||||||
|
Creative Commons public licenses provide a standard set of terms and
|
||||||
|
conditions that creators and other rights holders may use to share
|
||||||
|
original works of authorship and other material subject to copyright
|
||||||
|
and certain other rights specified in the public license below. The
|
||||||
|
following considerations are for informational purposes only, are not
|
||||||
|
exhaustive, and do not form part of our licenses.
|
||||||
|
|
||||||
|
Considerations for licensors: Our public licenses are
|
||||||
|
intended for use by those authorized to give the public
|
||||||
|
permission to use material in ways otherwise restricted by
|
||||||
|
copyright and certain other rights. Our licenses are
|
||||||
|
irrevocable. Licensors should read and understand the terms
|
||||||
|
and conditions of the license they choose before applying it.
|
||||||
|
Licensors should also secure all rights necessary before
|
||||||
|
applying our licenses so that the public can reuse the
|
||||||
|
material as expected. Licensors should clearly mark any
|
||||||
|
material not subject to the license. This includes other CC-
|
||||||
|
licensed material, or material used under an exception or
|
||||||
|
limitation to copyright. More considerations for licensors:
|
||||||
|
wiki.creativecommons.org/Considerations_for_licensors
|
||||||
|
|
||||||
|
Considerations for the public: By using one of our public
|
||||||
|
licenses, a licensor grants the public permission to use the
|
||||||
|
licensed material under specified terms and conditions. If
|
||||||
|
the licensor's permission is not necessary for any reason--for
|
||||||
|
example, because of any applicable exception or limitation to
|
||||||
|
copyright--then that use is not regulated by the license. Our
|
||||||
|
licenses grant only permissions under copyright and certain
|
||||||
|
other rights that a licensor has authority to grant. Use of
|
||||||
|
the licensed material may still be restricted for other
|
||||||
|
reasons, including because others have copyright or other
|
||||||
|
rights in the material. A licensor may make special requests,
|
||||||
|
such as asking that all changes be marked or described.
|
||||||
|
Although not required by our licenses, you are encouraged to
|
||||||
|
respect those requests where reasonable. More considerations
|
||||||
|
for the public:
|
||||||
|
wiki.creativecommons.org/Considerations_for_licensees
|
||||||
|
|
||||||
|
=======================================================================
|
||||||
|
|
||||||
|
Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International
|
||||||
|
Public License
|
||||||
|
|
||||||
|
By exercising the Licensed Rights (defined below), You accept and agree
|
||||||
|
to be bound by the terms and conditions of this Creative Commons
|
||||||
|
Attribution-NonCommercial-ShareAlike 4.0 International Public License
|
||||||
|
("Public License"). To the extent this Public License may be
|
||||||
|
interpreted as a contract, You are granted the Licensed Rights in
|
||||||
|
consideration of Your acceptance of these terms and conditions, and the
|
||||||
|
Licensor grants You such rights in consideration of benefits the
|
||||||
|
Licensor receives from making the Licensed Material available under
|
||||||
|
these terms and conditions.
|
||||||
|
|
||||||
|
|
||||||
|
Section 1 -- Definitions.
|
||||||
|
|
||||||
|
a. Adapted Material means material subject to Copyright and Similar
|
||||||
|
Rights that is derived from or based upon the Licensed Material
|
||||||
|
and in which the Licensed Material is translated, altered,
|
||||||
|
arranged, transformed, or otherwise modified in a manner requiring
|
||||||
|
permission under the Copyright and Similar Rights held by the
|
||||||
|
Licensor. For purposes of this Public License, where the Licensed
|
||||||
|
Material is a musical work, performance, or sound recording,
|
||||||
|
Adapted Material is always produced where the Licensed Material is
|
||||||
|
synched in timed relation with a moving image.
|
||||||
|
|
||||||
|
b. Adapter's License means the license You apply to Your Copyright
|
||||||
|
and Similar Rights in Your contributions to Adapted Material in
|
||||||
|
accordance with the terms and conditions of this Public License.
|
||||||
|
|
||||||
|
c. BY-NC-SA Compatible License means a license listed at
|
||||||
|
creativecommons.org/compatiblelicenses, approved by Creative
|
||||||
|
Commons as essentially the equivalent of this Public License.
|
||||||
|
|
||||||
|
d. Copyright and Similar Rights means copyright and/or similar rights
|
||||||
|
closely related to copyright including, without limitation,
|
||||||
|
performance, broadcast, sound recording, and Sui Generis Database
|
||||||
|
Rights, without regard to how the rights are labeled or
|
||||||
|
categorized. For purposes of this Public License, the rights
|
||||||
|
specified in Section 2(b)(1)-(2) are not Copyright and Similar
|
||||||
|
Rights.
|
||||||
|
|
||||||
|
e. Effective Technological Measures means those measures that, in the
|
||||||
|
absence of proper authority, may not be circumvented under laws
|
||||||
|
fulfilling obligations under Article 11 of the WIPO Copyright
|
||||||
|
Treaty adopted on December 20, 1996, and/or similar international
|
||||||
|
agreements.
|
||||||
|
|
||||||
|
f. Exceptions and Limitations means fair use, fair dealing, and/or
|
||||||
|
any other exception or limitation to Copyright and Similar Rights
|
||||||
|
that applies to Your use of the Licensed Material.
|
||||||
|
|
||||||
|
g. License Elements means the license attributes listed in the name
|
||||||
|
of a Creative Commons Public License. The License Elements of this
|
||||||
|
Public License are Attribution, NonCommercial, and ShareAlike.
|
||||||
|
|
||||||
|
h. Licensed Material means the artistic or literary work, database,
|
||||||
|
or other material to which the Licensor applied this Public
|
||||||
|
License.
|
||||||
|
|
||||||
|
i. Licensed Rights means the rights granted to You subject to the
|
||||||
|
terms and conditions of this Public License, which are limited to
|
||||||
|
all Copyright and Similar Rights that apply to Your use of the
|
||||||
|
Licensed Material and that the Licensor has authority to license.
|
||||||
|
|
||||||
|
j. Licensor means the individual(s) or entity(ies) granting rights
|
||||||
|
under this Public License.
|
||||||
|
|
||||||
|
k. NonCommercial means not primarily intended for or directed towards
|
||||||
|
commercial advantage or monetary compensation. For purposes of
|
||||||
|
this Public License, the exchange of the Licensed Material for
|
||||||
|
other material subject to Copyright and Similar Rights by digital
|
||||||
|
file-sharing or similar means is NonCommercial provided there is
|
||||||
|
no payment of monetary compensation in connection with the
|
||||||
|
exchange.
|
||||||
|
|
||||||
|
l. Share means to provide material to the public by any means or
|
||||||
|
process that requires permission under the Licensed Rights, such
|
||||||
|
as reproduction, public display, public performance, distribution,
|
||||||
|
dissemination, communication, or importation, and to make material
|
||||||
|
available to the public including in ways that members of the
|
||||||
|
public may access the material from a place and at a time
|
||||||
|
individually chosen by them.
|
||||||
|
|
||||||
|
m. Sui Generis Database Rights means rights other than copyright
|
||||||
|
resulting from Directive 96/9/EC of the European Parliament and of
|
||||||
|
the Council of 11 March 1996 on the legal protection of databases,
|
||||||
|
as amended and/or succeeded, as well as other essentially
|
||||||
|
equivalent rights anywhere in the world.
|
||||||
|
|
||||||
|
n. You means the individual or entity exercising the Licensed Rights
|
||||||
|
under this Public License. Your has a corresponding meaning.
|
||||||
|
|
||||||
|
|
||||||
|
Section 2 -- Scope.
|
||||||
|
|
||||||
|
a. License grant.
|
||||||
|
|
||||||
|
1. Subject to the terms and conditions of this Public License,
|
||||||
|
the Licensor hereby grants You a worldwide, royalty-free,
|
||||||
|
non-sublicensable, non-exclusive, irrevocable license to
|
||||||
|
exercise the Licensed Rights in the Licensed Material to:
|
||||||
|
|
||||||
|
a. reproduce and Share the Licensed Material, in whole or
|
||||||
|
in part, for NonCommercial purposes only; and
|
||||||
|
|
||||||
|
b. produce, reproduce, and Share Adapted Material for
|
||||||
|
NonCommercial purposes only.
|
||||||
|
|
||||||
|
2. Exceptions and Limitations. For the avoidance of doubt, where
|
||||||
|
Exceptions and Limitations apply to Your use, this Public
|
||||||
|
License does not apply, and You do not need to comply with
|
||||||
|
its terms and conditions.
|
||||||
|
|
||||||
|
3. Term. The term of this Public License is specified in Section
|
||||||
|
6(a).
|
||||||
|
|
||||||
|
4. Media and formats; technical modifications allowed. The
|
||||||
|
Licensor authorizes You to exercise the Licensed Rights in
|
||||||
|
all media and formats whether now known or hereafter created,
|
||||||
|
and to make technical modifications necessary to do so. The
|
||||||
|
Licensor waives and/or agrees not to assert any right or
|
||||||
|
authority to forbid You from making technical modifications
|
||||||
|
necessary to exercise the Licensed Rights, including
|
||||||
|
technical modifications necessary to circumvent Effective
|
||||||
|
Technological Measures. For purposes of this Public License,
|
||||||
|
simply making modifications authorized by this Section 2(a)
|
||||||
|
(4) never produces Adapted Material.
|
||||||
|
|
||||||
|
5. Downstream recipients.
|
||||||
|
|
||||||
|
a. Offer from the Licensor -- Licensed Material. Every
|
||||||
|
recipient of the Licensed Material automatically
|
||||||
|
receives an offer from the Licensor to exercise the
|
||||||
|
Licensed Rights under the terms and conditions of this
|
||||||
|
Public License.
|
||||||
|
|
||||||
|
b. Additional offer from the Licensor -- Adapted Material.
|
||||||
|
Every recipient of Adapted Material from You
|
||||||
|
automatically receives an offer from the Licensor to
|
||||||
|
exercise the Licensed Rights in the Adapted Material
|
||||||
|
under the conditions of the Adapter's License You apply.
|
||||||
|
|
||||||
|
c. No downstream restrictions. You may not offer or impose
|
||||||
|
any additional or different terms or conditions on, or
|
||||||
|
apply any Effective Technological Measures to, the
|
||||||
|
Licensed Material if doing so restricts exercise of the
|
||||||
|
Licensed Rights by any recipient of the Licensed
|
||||||
|
Material.
|
||||||
|
|
||||||
|
6. No endorsement. Nothing in this Public License constitutes or
|
||||||
|
may be construed as permission to assert or imply that You
|
||||||
|
are, or that Your use of the Licensed Material is, connected
|
||||||
|
with, or sponsored, endorsed, or granted official status by,
|
||||||
|
the Licensor or others designated to receive attribution as
|
||||||
|
provided in Section 3(a)(1)(A)(i).
|
||||||
|
|
||||||
|
b. Other rights.
|
||||||
|
|
||||||
|
1. Moral rights, such as the right of integrity, are not
|
||||||
|
licensed under this Public License, nor are publicity,
|
||||||
|
privacy, and/or other similar personality rights; however, to
|
||||||
|
the extent possible, the Licensor waives and/or agrees not to
|
||||||
|
assert any such rights held by the Licensor to the limited
|
||||||
|
extent necessary to allow You to exercise the Licensed
|
||||||
|
Rights, but not otherwise.
|
||||||
|
|
||||||
|
2. Patent and trademark rights are not licensed under this
|
||||||
|
Public License.
|
||||||
|
|
||||||
|
3. To the extent possible, the Licensor waives any right to
|
||||||
|
collect royalties from You for the exercise of the Licensed
|
||||||
|
Rights, whether directly or through a collecting society
|
||||||
|
under any voluntary or waivable statutory or compulsory
|
||||||
|
licensing scheme. In all other cases the Licensor expressly
|
||||||
|
reserves any right to collect such royalties, including when
|
||||||
|
the Licensed Material is used other than for NonCommercial
|
||||||
|
purposes.
|
||||||
|
|
||||||
|
|
||||||
|
Section 3 -- License Conditions.
|
||||||
|
|
||||||
|
Your exercise of the Licensed Rights is expressly made subject to the
|
||||||
|
following conditions.
|
||||||
|
|
||||||
|
a. Attribution.
|
||||||
|
|
||||||
|
1. If You Share the Licensed Material (including in modified
|
||||||
|
form), You must:
|
||||||
|
|
||||||
|
a. retain the following if it is supplied by the Licensor
|
||||||
|
with the Licensed Material:
|
||||||
|
|
||||||
|
i. identification of the creator(s) of the Licensed
|
||||||
|
Material and any others designated to receive
|
||||||
|
attribution, in any reasonable manner requested by
|
||||||
|
the Licensor (including by pseudonym if
|
||||||
|
designated);
|
||||||
|
|
||||||
|
ii. a copyright notice;
|
||||||
|
|
||||||
|
iii. a notice that refers to this Public License;
|
||||||
|
|
||||||
|
iv. a notice that refers to the disclaimer of
|
||||||
|
warranties;
|
||||||
|
|
||||||
|
v. a URI or hyperlink to the Licensed Material to the
|
||||||
|
extent reasonably practicable;
|
||||||
|
|
||||||
|
b. indicate if You modified the Licensed Material and
|
||||||
|
retain an indication of any previous modifications; and
|
||||||
|
|
||||||
|
c. indicate the Licensed Material is licensed under this
|
||||||
|
Public License, and include the text of, or the URI or
|
||||||
|
hyperlink to, this Public License.
|
||||||
|
|
||||||
|
2. You may satisfy the conditions in Section 3(a)(1) in any
|
||||||
|
reasonable manner based on the medium, means, and context in
|
||||||
|
which You Share the Licensed Material. For example, it may be
|
||||||
|
reasonable to satisfy the conditions by providing a URI or
|
||||||
|
hyperlink to a resource that includes the required
|
||||||
|
information.
|
||||||
|
3. If requested by the Licensor, You must remove any of the
|
||||||
|
information required by Section 3(a)(1)(A) to the extent
|
||||||
|
reasonably practicable.
|
||||||
|
|
||||||
|
b. ShareAlike.
|
||||||
|
|
||||||
|
In addition to the conditions in Section 3(a), if You Share
|
||||||
|
Adapted Material You produce, the following conditions also apply.
|
||||||
|
|
||||||
|
1. The Adapter's License You apply must be a Creative Commons
|
||||||
|
license with the same License Elements, this version or
|
||||||
|
later, or a BY-NC-SA Compatible License.
|
||||||
|
|
||||||
|
2. You must include the text of, or the URI or hyperlink to, the
|
||||||
|
Adapter's License You apply. You may satisfy this condition
|
||||||
|
in any reasonable manner based on the medium, means, and
|
||||||
|
context in which You Share Adapted Material.
|
||||||
|
|
||||||
|
3. You may not offer or impose any additional or different terms
|
||||||
|
or conditions on, or apply any Effective Technological
|
||||||
|
Measures to, Adapted Material that restrict exercise of the
|
||||||
|
rights granted under the Adapter's License You apply.
|
||||||
|
|
||||||
|
|
||||||
|
Section 4 -- Sui Generis Database Rights.
|
||||||
|
|
||||||
|
Where the Licensed Rights include Sui Generis Database Rights that
|
||||||
|
apply to Your use of the Licensed Material:
|
||||||
|
|
||||||
|
a. for the avoidance of doubt, Section 2(a)(1) grants You the right
|
||||||
|
to extract, reuse, reproduce, and Share all or a substantial
|
||||||
|
portion of the contents of the database for NonCommercial purposes
|
||||||
|
only;
|
||||||
|
|
||||||
|
b. if You include all or a substantial portion of the database
|
||||||
|
contents in a database in which You have Sui Generis Database
|
||||||
|
Rights, then the database in which You have Sui Generis Database
|
||||||
|
Rights (but not its individual contents) is Adapted Material,
|
||||||
|
including for purposes of Section 3(b); and
|
||||||
|
|
||||||
|
c. You must comply with the conditions in Section 3(a) if You Share
|
||||||
|
all or a substantial portion of the contents of the database.
|
||||||
|
|
||||||
|
For the avoidance of doubt, this Section 4 supplements and does not
|
||||||
|
replace Your obligations under this Public License where the Licensed
|
||||||
|
Rights include other Copyright and Similar Rights.
|
||||||
|
|
||||||
|
|
||||||
|
Section 5 -- Disclaimer of Warranties and Limitation of Liability.
|
||||||
|
|
||||||
|
a. UNLESS OTHERWISE SEPARATELY UNDERTAKEN BY THE LICENSOR, TO THE
|
||||||
|
EXTENT POSSIBLE, THE LICENSOR OFFERS THE LICENSED MATERIAL AS-IS
|
||||||
|
AND AS-AVAILABLE, AND MAKES NO REPRESENTATIONS OR WARRANTIES OF
|
||||||
|
ANY KIND CONCERNING THE LICENSED MATERIAL, WHETHER EXPRESS,
|
||||||
|
IMPLIED, STATUTORY, OR OTHER. THIS INCLUDES, WITHOUT LIMITATION,
|
||||||
|
WARRANTIES OF TITLE, MERCHANTABILITY, FITNESS FOR A PARTICULAR
|
||||||
|
PURPOSE, NON-INFRINGEMENT, ABSENCE OF LATENT OR OTHER DEFECTS,
|
||||||
|
ACCURACY, OR THE PRESENCE OR ABSENCE OF ERRORS, WHETHER OR NOT
|
||||||
|
KNOWN OR DISCOVERABLE. WHERE DISCLAIMERS OF WARRANTIES ARE NOT
|
||||||
|
ALLOWED IN FULL OR IN PART, THIS DISCLAIMER MAY NOT APPLY TO YOU.
|
||||||
|
|
||||||
|
b. TO THE EXTENT POSSIBLE, IN NO EVENT WILL THE LICENSOR BE LIABLE
|
||||||
|
TO YOU ON ANY LEGAL THEORY (INCLUDING, WITHOUT LIMITATION,
|
||||||
|
NEGLIGENCE) OR OTHERWISE FOR ANY DIRECT, SPECIAL, INDIRECT,
|
||||||
|
INCIDENTAL, CONSEQUENTIAL, PUNITIVE, EXEMPLARY, OR OTHER LOSSES,
|
||||||
|
COSTS, EXPENSES, OR DAMAGES ARISING OUT OF THIS PUBLIC LICENSE OR
|
||||||
|
USE OF THE LICENSED MATERIAL, EVEN IF THE LICENSOR HAS BEEN
|
||||||
|
ADVISED OF THE POSSIBILITY OF SUCH LOSSES, COSTS, EXPENSES, OR
|
||||||
|
DAMAGES. WHERE A LIMITATION OF LIABILITY IS NOT ALLOWED IN FULL OR
|
||||||
|
IN PART, THIS LIMITATION MAY NOT APPLY TO YOU.
|
||||||
|
|
||||||
|
c. The disclaimer of warranties and limitation of liability provided
|
||||||
|
above shall be interpreted in a manner that, to the extent
|
||||||
|
possible, most closely approximates an absolute disclaimer and
|
||||||
|
waiver of all liability.
|
||||||
|
|
||||||
|
|
||||||
|
Section 6 -- Term and Termination.
|
||||||
|
|
||||||
|
a. This Public License applies for the term of the Copyright and
|
||||||
|
Similar Rights licensed here. However, if You fail to comply with
|
||||||
|
this Public License, then Your rights under this Public License
|
||||||
|
terminate automatically.
|
||||||
|
|
||||||
|
b. Where Your right to use the Licensed Material has terminated under
|
||||||
|
Section 6(a), it reinstates:
|
||||||
|
|
||||||
|
1. automatically as of the date the violation is cured, provided
|
||||||
|
it is cured within 30 days of Your discovery of the
|
||||||
|
violation; or
|
||||||
|
|
||||||
|
2. upon express reinstatement by the Licensor.
|
||||||
|
|
||||||
|
For the avoidance of doubt, this Section 6(b) does not affect any
|
||||||
|
right the Licensor may have to seek remedies for Your violations
|
||||||
|
of this Public License.
|
||||||
|
|
||||||
|
c. For the avoidance of doubt, the Licensor may also offer the
|
||||||
|
Licensed Material under separate terms or conditions or stop
|
||||||
|
distributing the Licensed Material at any time; however, doing so
|
||||||
|
will not terminate this Public License.
|
||||||
|
|
||||||
|
d. Sections 1, 5, 6, 7, and 8 survive termination of this Public
|
||||||
|
License.
|
||||||
|
|
||||||
|
|
||||||
|
Section 7 -- Other Terms and Conditions.
|
||||||
|
|
||||||
|
a. The Licensor shall not be bound by any additional or different
|
||||||
|
terms or conditions communicated by You unless expressly agreed.
|
||||||
|
|
||||||
|
b. Any arrangements, understandings, or agreements regarding the
|
||||||
|
Licensed Material not stated herein are separate from and
|
||||||
|
independent of the terms and conditions of this Public License.
|
||||||
|
|
||||||
|
|
||||||
|
Section 8 -- Interpretation.
|
||||||
|
|
||||||
|
a. For the avoidance of doubt, this Public License does not, and
|
||||||
|
shall not be interpreted to, reduce, limit, restrict, or impose
|
||||||
|
conditions on any use of the Licensed Material that could lawfully
|
||||||
|
be made without permission under this Public License.
|
||||||
|
|
||||||
|
b. To the extent possible, if any provision of this Public License is
|
||||||
|
deemed unenforceable, it shall be automatically reformed to the
|
||||||
|
minimum extent necessary to make it enforceable. If the provision
|
||||||
|
cannot be reformed, it shall be severed from this Public License
|
||||||
|
without affecting the enforceability of the remaining terms and
|
||||||
|
conditions.
|
||||||
|
|
||||||
|
c. No term or condition of this Public License will be waived and no
|
||||||
|
failure to comply consented to unless expressly agreed to by the
|
||||||
|
Licensor.
|
||||||
|
|
||||||
|
d. Nothing in this Public License constitutes or may be interpreted
|
||||||
|
as a limitation upon, or waiver of, any privileges and immunities
|
||||||
|
that apply to the Licensor or You, including from the legal
|
||||||
|
processes of any jurisdiction or authority.
|
||||||
|
|
||||||
|
=======================================================================
|
||||||
|
|
||||||
|
Creative Commons is not a party to its public
|
||||||
|
licenses. Notwithstanding, Creative Commons may elect to apply one of
|
||||||
|
its public licenses to material it publishes and in those instances
|
||||||
|
will be considered the “Licensor.” The text of the Creative Commons
|
||||||
|
public licenses is dedicated to the public domain under the CC0 Public
|
||||||
|
Domain Dedication. Except for the limited purpose of indicating that
|
||||||
|
material is shared under a Creative Commons public license or as
|
||||||
|
otherwise permitted by the Creative Commons policies published at
|
||||||
|
creativecommons.org/policies, Creative Commons does not authorize the
|
||||||
|
use of the trademark "Creative Commons" or any other trademark or logo
|
||||||
|
of Creative Commons without its prior written consent including,
|
||||||
|
without limitation, in connection with any unauthorized modifications
|
||||||
|
to any of its public licenses or any other arrangements,
|
||||||
|
understandings, or agreements concerning use of licensed material. For
|
||||||
|
the avoidance of doubt, this paragraph does not form part of the
|
||||||
|
public licenses.
|
||||||
|
|
||||||
|
Creative Commons may be contacted at creativecommons.org.
|
||||||
@@ -1 +1,27 @@
|
|||||||
# ByteDiceWeb
|
# ByteDiceWeb - [ByteDice.net](https://bytedice.net)
|
||||||
|
> A small website served as a place to find content related to Byte Dice.
|
||||||
|
|
||||||
|
# Compatibility
|
||||||
|
|
||||||
|
| Browser | Render Engine | PC | Mobile |
|
||||||
|
| --------- | ------------- | -- | ------ |
|
||||||
|
| Firefox | Gecko | ✅️ | ✅️ |
|
||||||
|
| Tor | Gecko | ☑️ | ☑️ |
|
||||||
|
| Chrome | Chromium | ✅️ | ✅️ |
|
||||||
|
| MS Edge | Chromium | ✅️ | ✅️ |
|
||||||
|
| Opera | Chromium | ☑️ | ☑️ |
|
||||||
|
| Brave | Chromium | ☑️ | ☑️ |
|
||||||
|
| Helium | Chromium | ✅️ | 🟨 |
|
||||||
|
| GNOME Web | Webkit | ✅️ | 🟨 |
|
||||||
|
| Safari | Webkit | ❌️ | ❌️ |
|
||||||
|
|
||||||
|
> ✅️: Compatible & active testing \
|
||||||
|
> ☑️: Untested but likely works (most browsers fall here) \
|
||||||
|
> 🟨: Untested / Doesn't exist \
|
||||||
|
> ❌️: Known to have issues
|
||||||
|
|
||||||
|
|
||||||
|
# Open-source - Copyright
|
||||||
|
|
||||||
|
**[ByteDiceWeb / ByteDice.net] © 2025 by Byte Dice is licensed under CC BY-NC-SA 4.0.**\
|
||||||
|
**You can learn more about copyright by reading the full [license](/LICENSE.txt).**
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 3.5 KiB After Width: | Height: | Size: 3.5 KiB |
|
After Width: | Height: | Size: 57 KiB |
|
Before Width: | Height: | Size: 1.9 KiB After Width: | Height: | Size: 1.9 KiB |
|
After Width: | Height: | Size: 24 KiB |
|
After Width: | Height: | Size: 112 KiB |
|
After Width: | Height: | Size: 121 KiB |
|
After Width: | Height: | Size: 123 KiB |
|
After Width: | Height: | Size: 4.1 KiB |
|
After Width: | Height: | Size: 1.5 KiB |
|
After Width: | Height: | Size: 121 KiB |
|
After Width: | Height: | Size: 119 KiB |
|
After Width: | Height: | Size: 38 KiB |
|
After Width: | Height: | Size: 3.8 KiB |
|
After Width: | Height: | Size: 3.9 KiB |
|
After Width: | Height: | Size: 495 B |
|
Before Width: | Height: | Size: 166 B After Width: | Height: | Size: 166 B |
@@ -0,0 +1 @@
|
|||||||
|
<svg fill="#232323" height="800" width="800" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 374.116 374.116"><path d="M344.058 207.506c-16.568 0-30 13.432-30 30v76.609h-254v-76.609c0-16.568-13.432-30-30-30s-30 13.432-30 30v106.609c0 16.568 13.432 30 30 30h314c16.568 0 30-13.432 30-30V237.506c0-16.568-13.432-30-30-30"/><path d="m123.57 108.287 33.488 33.488V30c0-16.568 13.432-30 30-30s30 13.432 30 30v111.776l33.488-33.488c5.857-5.858 13.535-8.787 21.213-8.787s15.355 2.929 21.213 8.787c11.716 11.716 11.716 30.71 0 42.426l-84.701 84.7c-11.715 11.717-30.711 11.717-42.426 0l-84.701-84.701c-11.716-11.716-11.716-30.71 0-42.426s30.711-11.716 42.426 0"/></svg>
|
||||||
|
After Width: | Height: | Size: 660 B |
|
Before Width: | Height: | Size: 171 B After Width: | Height: | Size: 171 B |
|
Before Width: | Height: | Size: 488 B After Width: | Height: | Size: 488 B |
|
Before Width: | Height: | Size: 524 B After Width: | Height: | Size: 524 B |
|
Before Width: | Height: | Size: 518 B After Width: | Height: | Size: 518 B |
|
After Width: | Height: | Size: 506 B |
|
After Width: | Height: | Size: 505 B |
@@ -0,0 +1 @@
|
|||||||
|
<svg fill="#232323" height="800" width="800" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 374.116 374.116" xml:space="preserve"><path d="M344.058 207.506c-16.568 0-30 13.432-30 30v76.609h-254v-76.609c0-16.568-13.432-30-30-30s-30 13.432-30 30v106.609c0 16.568 13.432 30 30 30h314c16.568 0 30-13.432 30-30V237.506c0-16.568-13.432-30-30-30"/><path d="m123.57 135.915 33.488-33.488v111.775c0 16.568 13.432 30 30 30s30-13.432 30-30V102.426l33.488 33.488c5.857 5.858 13.535 8.787 21.213 8.787s15.355-2.929 21.213-8.787c11.716-11.716 11.716-30.71 0-42.426l-84.701-84.7c-11.715-11.717-30.711-11.717-42.426 0L81.144 93.489c-11.716 11.716-11.716 30.71 0 42.426s30.711 11.716 42.426 0"/></svg>
|
||||||
|
After Width: | Height: | Size: 685 B |
|
Before Width: | Height: | Size: 510 B After Width: | Height: | Size: 510 B |
|
After Width: | Height: | Size: 11 KiB |
|
After Width: | Height: | Size: 2.1 KiB |
@@ -0,0 +1,70 @@
|
|||||||
|
[
|
||||||
|
"Just don't die.",
|
||||||
|
"15% | 20% | 25% | no tip :(",
|
||||||
|
"Is thisn't not un-veryn't un-understandable?",
|
||||||
|
"mmmmmmmmmmmmmmmmmmmmmicrowave sounds.",
|
||||||
|
"Bad at a game? Skill issue, git gud.",
|
||||||
|
"I AM NOT A FEMBOY NOR GAY. (I support though :thumbsup:)",
|
||||||
|
"There's a really good song that goes like \"do do do do do do do do do.\"",
|
||||||
|
"The real tip was the friends you made along the way.",
|
||||||
|
"Nothing suspicious here... keep scrolling.",
|
||||||
|
"Did you know that... uh... hold on, let me think... I don't know, I forgot.",
|
||||||
|
"Remember that one task you're avoiding? Yeah, keep avoiding it and stay on this epic website.",
|
||||||
|
"I use Gentoo, btw. (Take that, Arch users!)",
|
||||||
|
"This tip took 3 hours to write.",
|
||||||
|
"Water is wet.",
|
||||||
|
"Why is it called a 'loading screen' if the screen has already loaded?",
|
||||||
|
"Look at you go, reading these tips like a pro so I didn't waste my time!",
|
||||||
|
"If this ever reappears, pretend to be extremely surprised.",
|
||||||
|
"Private tip. Anyone who reads will be executed on the spot.",
|
||||||
|
"Why do we park on driveways and drive on parkways?",
|
||||||
|
"If a progress bar ever stops, yell \"MOVE!\" as loud as you can.",
|
||||||
|
"You are now breathing manually.",
|
||||||
|
"If you're reading this, congratulations, you can read!",
|
||||||
|
"bytedice.net/toys",
|
||||||
|
"Loading...",
|
||||||
|
"Bees communicate through twerking.",
|
||||||
|
"Stop breathing! you're addicted!",
|
||||||
|
"How do wireless signals work?",
|
||||||
|
"A wise person once said something important. This is not that.",
|
||||||
|
"Why do they call it oven when you of in the cold food and of out hot eat the food?",
|
||||||
|
"Sometimes when I think really hard, I get an idea. Most of the time, I don't.",
|
||||||
|
"Why is it called a 'building' if it's already built?",
|
||||||
|
"Run.",
|
||||||
|
"Rubber duckies are geniuses. Don't believe me? Talk to one yourself.",
|
||||||
|
"It works on my machine.",
|
||||||
|
"If you think about it, sandwiches are just edible plates.",
|
||||||
|
"The word \"Loading\" backward is \"gnidaoL\". I don't know what that means.",
|
||||||
|
"This tip is non-edible.",
|
||||||
|
"Shoes are technically portable floors.",
|
||||||
|
"Why did Isaac Newton ruin the world by inventing gravity and thus disabling our flight?",
|
||||||
|
"It's better to pee in the shower than to shower in the pee.",
|
||||||
|
"Why does your nose run but your feet smell?",
|
||||||
|
"Fish.", "VGhpcyBpcyBub3QgYW4gQVJHLi4uIG9yIGlzIGl0PyAqVlNhdWNlIG11c2ljIHN0YXJ0cyBwbGF5aW5nLio=",
|
||||||
|
"Don't trust stairs. They're always up to something.",
|
||||||
|
"You can't fail at life if you define success as simply existing.",
|
||||||
|
"You've never seen your neighbors bring in their groceries, have you?",
|
||||||
|
"When you clean out a vacuum cleaner, you become the vacuum cleaner.",
|
||||||
|
"Bread is just a loaf of edible sponge.",
|
||||||
|
"Sponge is just a piece of inedible bread.",
|
||||||
|
"When life gives you lemons, hand them back and ask for chocolate instead.",
|
||||||
|
"[Under review for potential treason.]",
|
||||||
|
"Tomatoes are fruit, which makes ketchup a smoothie.",
|
||||||
|
"Shork gumey.",
|
||||||
|
"You just lost the game.",
|
||||||
|
"H",
|
||||||
|
"A sandwich walks into a bar. The bartender says, 'Sorry, we don't serve food here.'",
|
||||||
|
"This website is sponsored by bytedice.net!",
|
||||||
|
"Waffles are pancakes with abs.",
|
||||||
|
"Opening the fridge again will not spawn more food.",
|
||||||
|
"A spoon is just a tiny bowl on a stick.",
|
||||||
|
"What's red and smells like blue paint? Red paint.",
|
||||||
|
"Ɛ:",
|
||||||
|
":3",
|
||||||
|
"Is it gay to be homosexual?",
|
||||||
|
"Rust. No, not C++ or Java. Rust. Only Rust.",
|
||||||
|
"My eyes are up here, buddy.",
|
||||||
|
"The word 'bed' looks like a bed.",
|
||||||
|
"The word 'queue' is just the letter Q followed silent letters standing in a queue.",
|
||||||
|
"Press escape then write `:q!` to exit VIM."
|
||||||
|
]
|
||||||
@@ -0,0 +1,53 @@
|
|||||||
|
[
|
||||||
|
{
|
||||||
|
"icon": "youtube_8x8.png",
|
||||||
|
"action": "navigate",
|
||||||
|
"actionValue": "https://youtube.com/@ByteDice"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"icon": "github_8x8.png",
|
||||||
|
"action": "navigate",
|
||||||
|
"actionValue": "https://git.bytedice.net/ByteDice/ByteDiceWeb"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"hidden": true,
|
||||||
|
"alt": {
|
||||||
|
"name": "byte",
|
||||||
|
"icon": "dice3_8x8.png",
|
||||||
|
"action": "navigate",
|
||||||
|
"actionValue": "/",
|
||||||
|
"disabled": false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"hidden": true,
|
||||||
|
"alt": {
|
||||||
|
"name": "testtube",
|
||||||
|
"icon": "testtube_8x8.png",
|
||||||
|
"action": "navigate",
|
||||||
|
"actionValue": "/libs/test",
|
||||||
|
"disabled": false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"hidden": true,
|
||||||
|
"alt": {
|
||||||
|
"name": "return",
|
||||||
|
"icon": "return_8x8.png",
|
||||||
|
"action": "navigate",
|
||||||
|
"actionValue": "../",
|
||||||
|
"disabled": false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"icon": "pauseAnimation_8x8.png",
|
||||||
|
"action": "function",
|
||||||
|
"actionValue": "pauseAnimBtn('auto')",
|
||||||
|
"iconId": "animationToggle",
|
||||||
|
"hidden": false,
|
||||||
|
"alt": {
|
||||||
|
"name": "noAnim",
|
||||||
|
"hidden": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
@@ -0,0 +1,291 @@
|
|||||||
|
const PAGES = {
|
||||||
|
_404: -1,
|
||||||
|
null: 0,
|
||||||
|
SYNTHWAVE: 1,
|
||||||
|
TOYS: 2,
|
||||||
|
LICENSE: 3,
|
||||||
|
LOADING: 4,
|
||||||
|
LIBS: 5,
|
||||||
|
CRAFTING: 6
|
||||||
|
}
|
||||||
|
const CONSOLE_COLORS = {
|
||||||
|
red: "color: #FF0000;",
|
||||||
|
green: "color: #00FF00",
|
||||||
|
white: "color: #FFFFFF;",
|
||||||
|
debugTitle: "color: #00FF00;",
|
||||||
|
}
|
||||||
|
|
||||||
|
let CURRENT_PAGE = PAGES.null
|
||||||
|
|
||||||
|
let isAnimating = true
|
||||||
|
let gridVisible = false
|
||||||
|
let navBarPresent = false
|
||||||
|
|
||||||
|
function randomFloat(min, max) {
|
||||||
|
return Math.random() * (max - min) + min
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function randomIntFrom0(max) {
|
||||||
|
return Math.floor(Math.random() * max)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function randomInt(min, max) {
|
||||||
|
return Math.floor(Math.random() * (max - min) + min)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function randomBool(probability) {
|
||||||
|
return Math.random() <= probability
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function clampNearest(n, mul) {
|
||||||
|
if (n > 0) { return Math.ceil(n / mul) * mul }
|
||||||
|
else if (n < 0) { return Math.floor(n / mul) * mul }
|
||||||
|
else { return 0 }
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function lerp(x, y, t) {
|
||||||
|
return x + t * (y - x)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function debugPrint(title, value) {
|
||||||
|
if (!title) {
|
||||||
|
console.error("debugPrint() takes 2 arguments")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (value) {
|
||||||
|
console.log(`%c${title}:\n%c${value}`, CONSOLE_COLORS.debugTitle, CONSOLE_COLORS.white)
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
console.log(`%c${title}`, CONSOLE_COLORS.debugTitle)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function toggleGrid(offX = 0, offY = 0) {
|
||||||
|
if (gridVisible) {
|
||||||
|
document.getElementById("gridCanvas").remove()
|
||||||
|
gridVisible = false
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
let canvasEl = document.createElement("canvas")
|
||||||
|
canvasEl.id = "gridCanvas"
|
||||||
|
canvasEl.width = window.innerWidth
|
||||||
|
canvasEl.height = window.innerHeight
|
||||||
|
canvasEl.style.position = "absolute"
|
||||||
|
canvasEl.style.top = `${offY * pxDensity}px`
|
||||||
|
canvasEl.style.left = `${offX * pxDensity}px`
|
||||||
|
canvasEl.style.pointerEvents = "none"
|
||||||
|
|
||||||
|
let ctx = canvasEl.getContext("2d")
|
||||||
|
|
||||||
|
document.body.appendChild(canvasEl)
|
||||||
|
|
||||||
|
let repeatCountX = window.innerWidth / pxDensity
|
||||||
|
let repeatCountY = window.innerHeight / pxDensity
|
||||||
|
|
||||||
|
ctx.strokeStyle = "#ff0000"
|
||||||
|
ctx.strokeSize = 1
|
||||||
|
ctx.beginPath()
|
||||||
|
|
||||||
|
for (x = 0; x < repeatCountX; x++) {
|
||||||
|
ctx.moveTo(x * pxDensity, 0)
|
||||||
|
ctx.lineTo(x * pxDensity, window.innerHeight)
|
||||||
|
}
|
||||||
|
|
||||||
|
for (y = 0; y < repeatCountY; y++) {
|
||||||
|
ctx.moveTo(0, y * pxDensity)
|
||||||
|
ctx.lineTo(window.innerWidth, y * pxDensity)
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx.stroke()
|
||||||
|
|
||||||
|
gridVisible = true
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function pauseAnimBtn(state) {
|
||||||
|
if (state == "auto") {
|
||||||
|
if (localStorage["isAnimating"] == "true") { localStorage["isAnimating"] = "false" }
|
||||||
|
else { localStorage["isAnimating"] = "true" }
|
||||||
|
|
||||||
|
state = localStorage["isAnimating"]
|
||||||
|
}
|
||||||
|
|
||||||
|
let pauseBtn = document.getElementById("animationToggle")
|
||||||
|
if (pauseBtn == undefined) { return; }
|
||||||
|
|
||||||
|
if (state == "true") {
|
||||||
|
isAnimating = true
|
||||||
|
pauseBtn.src = "/assets/icons/pauseAnimation_8x8.png"
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
isAnimating = false
|
||||||
|
pauseBtn.src = "/assets/icons/playAnimation_8x8.png"
|
||||||
|
}
|
||||||
|
|
||||||
|
localStorage["isAnimating"] = isAnimating
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function setVh() {
|
||||||
|
document.documentElement.style.setProperty("--vh", window.innerHeight * 0.01 + "px")
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*function getEngine() {
|
||||||
|
if ("MozAppearance" in document.documentElement.style)
|
||||||
|
{ return "gecko" }
|
||||||
|
|
||||||
|
if (
|
||||||
|
window.chrome &&
|
||||||
|
CSS.supports("-webkit-appearance", "none") &&
|
||||||
|
!CSS.supports("-apple-pay-button-style", "black")
|
||||||
|
) { return "blink" }
|
||||||
|
|
||||||
|
if (CSS.supports("-apple-pay-button-style", "black"))
|
||||||
|
{ return "webkit" }
|
||||||
|
|
||||||
|
return "unknown";
|
||||||
|
}*/
|
||||||
|
|
||||||
|
|
||||||
|
document.addEventListener("DOMContentLoaded", async function () {
|
||||||
|
setVh()
|
||||||
|
debugPrint("currentPage", `${CURRENT_PAGE} ${Object.keys(PAGES)[CURRENT_PAGE + 1]}`)
|
||||||
|
|
||||||
|
startLoadingScreen()
|
||||||
|
|
||||||
|
/*let engine = getEngine()
|
||||||
|
if (!["gecko", "blink"].includes(engine))
|
||||||
|
{ alert("Your web browser is not supported, expect major issues.\nRendering engine: " + engine) }*/
|
||||||
|
|
||||||
|
setLoadingProgress("Fetching tips...")
|
||||||
|
|
||||||
|
await loadTips()
|
||||||
|
|
||||||
|
newTip()
|
||||||
|
|
||||||
|
setLoadingProgress("Calculating pixel density...")
|
||||||
|
calcPixelDensity()
|
||||||
|
|
||||||
|
if (densityWidth > densityHeight) { debugPrint("pxDensity", `Width, ${pxDensity}`) }
|
||||||
|
else { debugPrint("pxDensity", `Height, ${pxDensity}`) }
|
||||||
|
|
||||||
|
if (navBarPresent) {
|
||||||
|
setLoadingProgress("adding navBar...")
|
||||||
|
await loadNavBarJson()
|
||||||
|
onLoadNavBar()
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (CURRENT_PAGE) {
|
||||||
|
case (PAGES.SYNTHWAVE): loadingScreenSynthwave(); break
|
||||||
|
case (PAGES.LICENSE): loadingScreenStars(); await loadingScreenLicense(); break
|
||||||
|
case (PAGES.TOYS): loadingScreenStars(); break
|
||||||
|
case (PAGES.LIBS): loadingScreenStars(); break
|
||||||
|
case (PAGES.CRAFTING): loadingScreenCrafting(); break
|
||||||
|
}
|
||||||
|
|
||||||
|
if (navBarPresent) {
|
||||||
|
pauseAnimBtn(localStorage["isAnimating"] || "true")
|
||||||
|
debugPrint("isAnimating", isAnimating)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (CURRENT_PAGE == PAGES.LOADING) {
|
||||||
|
clearLoadingScreenClutter()
|
||||||
|
setLoadingProgress("")
|
||||||
|
setInterval(periodicallyChangeTips, 60 * 1000)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!fakeLoading) {
|
||||||
|
removeLoadingScreen()
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
setLoadingProgress("Cleaning up...")
|
||||||
|
loadingComplete = true
|
||||||
|
if (fakeLoadingComplete) {
|
||||||
|
removeLoadingScreen()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, false)
|
||||||
|
|
||||||
|
|
||||||
|
window.addEventListener("resize", function() {
|
||||||
|
setVh()
|
||||||
|
calcPixelDensity()
|
||||||
|
|
||||||
|
switch (CURRENT_PAGE) {
|
||||||
|
case (PAGES.SYNTHWAVE): onResizeSynthwave()
|
||||||
|
case (PAGES.LICENSE): scaleDisplayLicense()
|
||||||
|
}
|
||||||
|
|
||||||
|
// unoptimal but works well enough
|
||||||
|
if (gridVisible) {
|
||||||
|
toggleGrid()
|
||||||
|
toggleGrid()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
if ("scrollRestoration" in history) {
|
||||||
|
history.scrollRestoration = "manual";
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
window.addEventListener("pageshow", (event) => {
|
||||||
|
if (event.persisted) {
|
||||||
|
// reload page to clear unwanted cache.
|
||||||
|
// for some reason the loading screen stays
|
||||||
|
// permanently open without this.
|
||||||
|
window.location.reload()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
let prevAnimationState = isAnimating
|
||||||
|
|
||||||
|
|
||||||
|
async function sha256(text) {
|
||||||
|
const data = new TextEncoder().encode(text);
|
||||||
|
|
||||||
|
const hash = await crypto.subtle.digest("SHA-256", data);
|
||||||
|
|
||||||
|
return [...new Uint8Array(hash)]
|
||||||
|
.map(x => x.toString(16).padStart(2, "0"))
|
||||||
|
.join("");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
document.addEventListener("visibilitychange", function() {
|
||||||
|
if (document.hidden) {
|
||||||
|
prevAnimationState = isAnimating
|
||||||
|
isAnimating = false
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
isAnimating = prevAnimationState
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
window.addEventListener("blur", function() {
|
||||||
|
if (document.visibilityState === "visible") {
|
||||||
|
prevAnimationState = isAnimating
|
||||||
|
isAnimating = false
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
window.addEventListener("focus", function() {
|
||||||
|
if (!document.hidden) {
|
||||||
|
isAnimating = prevAnimationState
|
||||||
|
}
|
||||||
|
})
|
||||||
|
*/
|
||||||
@@ -0,0 +1,152 @@
|
|||||||
|
:root {
|
||||||
|
--navBar_Inner: #43608D;
|
||||||
|
--navBar_Border: #2F3E56;
|
||||||
|
--navBar_SecondInner: #344b6c;
|
||||||
|
--navBarBtn_Inner: #474747;
|
||||||
|
--navBarBtn_Border: #232323;
|
||||||
|
--brightBlue: #569cd6;
|
||||||
|
--pxDensity: 1;
|
||||||
|
--pxDensityPx: 1px;
|
||||||
|
--bottomPxMargin: 1px;
|
||||||
|
}
|
||||||
|
html {
|
||||||
|
font-family: "Roboto", sans-serif;
|
||||||
|
font-size: 1.3rem;
|
||||||
|
color: #ffffff;
|
||||||
|
background-color: #323232;
|
||||||
|
overflow: hidden;
|
||||||
|
width: 100vw;
|
||||||
|
height: calc(var(--vh, 1vh) * 100);
|
||||||
|
}
|
||||||
|
body {
|
||||||
|
height: calc(var(--vh, 1vh) * 100);
|
||||||
|
background-color: #323232;
|
||||||
|
color: #ffffff;
|
||||||
|
overflow: hidden;
|
||||||
|
margin: 0%;
|
||||||
|
width: 100vw;
|
||||||
|
}
|
||||||
|
canvas {
|
||||||
|
position: absolute;
|
||||||
|
}
|
||||||
|
.pxFont {
|
||||||
|
font-family: "Roboto", sans-serif;
|
||||||
|
font-size: min(5vmin, 2vmax, 1rem)
|
||||||
|
}
|
||||||
|
@keyframes slide-left {
|
||||||
|
0% { transform: translateX(0%); }
|
||||||
|
100% { transform: translateX(-100%); }
|
||||||
|
}
|
||||||
|
.borderedRect {
|
||||||
|
background-color: var(--navBar_Inner);
|
||||||
|
box-shadow: calc(1px * var(--pxDensity)) 0px var(--navBar_Border),
|
||||||
|
calc(-1px * var(--pxDensity)) 0px var(--navBar_Border),
|
||||||
|
0px calc(1px * var(--pxDensity)) var(--navBar_Border),
|
||||||
|
0px calc(-1px * var(--pxDensity)) var(--navBar_Border);
|
||||||
|
|
||||||
|
filter: drop-shadow(0px calc(2px * var(--pxDensity)) rgba(0, 0, 0, 0.5));
|
||||||
|
padding: 0%;
|
||||||
|
}
|
||||||
|
.invisible {
|
||||||
|
background-color: transparent;
|
||||||
|
border: none;
|
||||||
|
color: inherit;
|
||||||
|
}
|
||||||
|
.encased {
|
||||||
|
display: inline-block;
|
||||||
|
background-color: var(--navBarBtn_Inner);
|
||||||
|
box-shadow: calc(1px * var(--pxDensity)) 0px var(--navBarBtn_Border),
|
||||||
|
calc(-1px * var(--pxDensity)) 0px var(--navBarBtn_Border),
|
||||||
|
0px calc(1px * var(--pxDensity)) var(--navBarBtn_Border),
|
||||||
|
0px calc(-1px * var(--pxDensity)) var(--navBarBtn_Border);
|
||||||
|
|
||||||
|
padding: calc(1px * var(--pxDensity));
|
||||||
|
margin: calc(3px * var(--pxDensity)) calc(3px * var(--pxDensity)) 0% 0%;
|
||||||
|
width: round(auto, var(--pxDensity));
|
||||||
|
/*height: calc(6px * var(--pxDensity));*/
|
||||||
|
border: none;
|
||||||
|
color: #ffffff;
|
||||||
|
font-size: inherit;
|
||||||
|
}
|
||||||
|
.switch {
|
||||||
|
position: relative;
|
||||||
|
display: inline-block;
|
||||||
|
width: calc(8px * var(--pxDensity));
|
||||||
|
height: calc(4px * var(--pxDensity));
|
||||||
|
}
|
||||||
|
.switch input {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
.switchDecor {
|
||||||
|
position: absolute;
|
||||||
|
cursor: pointer;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
background-color: var(--navBarBtn_Border);
|
||||||
|
transition: 0.2s steps(2, end);
|
||||||
|
}
|
||||||
|
.switchDecor:before {
|
||||||
|
position: absolute;
|
||||||
|
content: "";
|
||||||
|
height: calc(2px * var(--pxDensity));
|
||||||
|
width: calc(2px * var(--pxDensity));
|
||||||
|
left: calc(1px * var(--pxDensity));
|
||||||
|
bottom: calc(1px * var(--pxDensity));
|
||||||
|
background-color: var(--navBarBtn_Inner);
|
||||||
|
transition: 0.2s steps(2, end);
|
||||||
|
}
|
||||||
|
.switch input:checked + .switchDecor {
|
||||||
|
background-color: var(--brightBlue);
|
||||||
|
}
|
||||||
|
.switch input:checked + .switchDecor:before {
|
||||||
|
transform: translateX(calc(4px * var(--pxDensity)));
|
||||||
|
}
|
||||||
|
#starNoise {
|
||||||
|
position: absolute;
|
||||||
|
top: 0%;
|
||||||
|
left: 0%;
|
||||||
|
width: calc(512px * var(--pxDensity));
|
||||||
|
height: auto;
|
||||||
|
aspect-ratio: 1 / 1;
|
||||||
|
image-rendering: pixelated;
|
||||||
|
transform: translate(-50%, -50%);
|
||||||
|
}
|
||||||
|
button {
|
||||||
|
display: inline-block;
|
||||||
|
background-color: var(--navBarBtn_Inner);
|
||||||
|
box-shadow: calc(1px * var(--pxDensity)) 0px var(--navBarBtn_Border),
|
||||||
|
calc(-1px * var(--pxDensity)) 0px var(--navBarBtn_Border),
|
||||||
|
0px calc(1px * var(--pxDensity)) var(--navBarBtn_Border),
|
||||||
|
0px calc(-1px * var(--pxDensity)) var(--navBarBtn_Border);
|
||||||
|
|
||||||
|
padding: calc(1px * var(--pxDensity));
|
||||||
|
margin: calc(3px * var(--pxDensity)) calc(3px * var(--pxDensity)) 0% 0%;
|
||||||
|
width: round(auto, var(--pxDensity));
|
||||||
|
/*height: calc(6px * var(--pxDensity));*/
|
||||||
|
border: none;
|
||||||
|
color: #ffffff;
|
||||||
|
font-size: inherit;
|
||||||
|
}
|
||||||
|
button:hover {
|
||||||
|
filter: brightness(1.35) drop-shadow(0px calc(1px * var(--pxDensity)) rgba(0, 0, 0, 0.5));
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
button:active {
|
||||||
|
filter: brightness(0.75);
|
||||||
|
transform: translateY(calc(1px * var(--pxDensity)));
|
||||||
|
}
|
||||||
|
button:disabled, button[disabled] {
|
||||||
|
filter: brightness(0.75) drop-shadow(0px calc(1px * var(--pxDensity)) rgba(0, 0, 0, 0.5));
|
||||||
|
cursor: not-allowed;
|
||||||
|
}
|
||||||
|
button:disabled:hover, button[disabled]:hover {
|
||||||
|
filter: brightness(0.75) drop-shadow(0px calc(1px * var(--pxDensity)) rgba(0, 0, 0, 0.5));
|
||||||
|
cursor: not-allowed;
|
||||||
|
}
|
||||||
|
button:disabled:active, button[disabled]:active {
|
||||||
|
filter: brightness(0.75) drop-shadow(0px calc(1px * var(--pxDensity)) rgba(0, 0, 0, 0.5));
|
||||||
|
cursor: not-allowed;
|
||||||
|
transform: none;
|
||||||
|
}
|
||||||
@@ -0,0 +1,43 @@
|
|||||||
|
.bgDiv {
|
||||||
|
background-color: #323232;
|
||||||
|
position: absolute;
|
||||||
|
top: 0%;
|
||||||
|
left: 0%;
|
||||||
|
width: 100vw;
|
||||||
|
height: calc(var(--vh, 1vh) * 100);
|
||||||
|
}
|
||||||
|
.loadingImg {
|
||||||
|
position: absolute;
|
||||||
|
top: round(50%, var(--pxDensityPx));
|
||||||
|
left: round(50%, var(--pxDensityPx));
|
||||||
|
transform: translate(-50%, -50%);
|
||||||
|
width: calc(32px * var(--pxDensity));
|
||||||
|
height: calc(32px * var(--pxDensity));
|
||||||
|
image-rendering: pixelated;
|
||||||
|
}
|
||||||
|
@keyframes fade-out {
|
||||||
|
0% {opacity: 100%;}
|
||||||
|
100% {opacity: 0%;}
|
||||||
|
}
|
||||||
|
#loadingCornerText {
|
||||||
|
background-color: transparent;
|
||||||
|
color: #ffffff;
|
||||||
|
border: none;
|
||||||
|
text-align: left;
|
||||||
|
position: absolute;
|
||||||
|
top: 0%;
|
||||||
|
left: 0%;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
#loadingText {
|
||||||
|
position: fixed;
|
||||||
|
bottom: 0;
|
||||||
|
left: calc(50vw - min(45vw, 250px));
|
||||||
|
width: min(90vw, 500px);
|
||||||
|
text-align: center;
|
||||||
|
font-size: min(5vmin, 2vmax, 25px);
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
color: #ffffff;
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
@@ -0,0 +1,106 @@
|
|||||||
|
let isLoading = true
|
||||||
|
let stopLoading = false
|
||||||
|
let loadingScreenEl = document.getElementById("loadingScreen")
|
||||||
|
let loadingComplete = false
|
||||||
|
let fakeLoadingComplete = false
|
||||||
|
|
||||||
|
|
||||||
|
loadingScreenEl.className = "bgDiv"
|
||||||
|
loadingScreenEl.innerHTML = `
|
||||||
|
<img class="loadingImg" draggable="false" src="/assets/byteDiceBlinkAnim_32x32.png" id="loadingImg">
|
||||||
|
|
||||||
|
<div style="margin-left: calc(3px * var(--pxDensity)); display: flex;" id="fakeLoadingToggle">
|
||||||
|
<p class="encased" style="height: 100%; font-size: min(5vmin, 2vmax, 25px)">
|
||||||
|
Use extended loading screen?<br>
|
||||||
|
<small style="margin: 0px; font-size: min(5vmin, 2vmax, 10px)">(For immersion)</small>
|
||||||
|
</p>
|
||||||
|
<div class="encased" style="height: inherit; display: flex;">
|
||||||
|
<label class="switch" style="margin: auto;">
|
||||||
|
<input type="checkbox" name="fakeLoadingToggle" onclick="setShowFakeLoading(this.checked)" ${localStorage["fakeLoading"] != "false" ? "checked" : ""}>
|
||||||
|
<span class="switchDecor"></span>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<p id="loadingText">Loading...<br>Loading files...<br><br>Tip: Tip loading...</p>
|
||||||
|
`
|
||||||
|
|
||||||
|
|
||||||
|
let loadingScreenProgress = document.getElementById("loadingText")
|
||||||
|
|
||||||
|
let tipsJson
|
||||||
|
|
||||||
|
|
||||||
|
async function loadTips() {
|
||||||
|
let tipsText = await fetch("/data/loadingScreenTips.json").then(response => response.text())
|
||||||
|
tipsJson = JSON.parse(tipsText)
|
||||||
|
console.log(tipsJson)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
const fakeLoading = localStorage["fakeLoading"] != "false"
|
||||||
|
let selectedTip = "undefined"
|
||||||
|
|
||||||
|
|
||||||
|
function newTip() {
|
||||||
|
if (randomIntFrom0(1000) == 0) {
|
||||||
|
// If you snitch the code for this easter egg
|
||||||
|
// then I'll permanently remove the easter egg.
|
||||||
|
selectedTip = `You like kissing boys, don't you?<br>
|
||||||
|
<img src="/assets/boykissers/boykisser_64x64.png"
|
||||||
|
style="aspect-ratio: 1 / 1; width: min-content; height: min-content;">`
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
selectedTip = tipsJson[randomIntFrom0(tipsJson.length)]
|
||||||
|
}
|
||||||
|
|
||||||
|
debugPrint("new tip", selectedTip)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function setLoadingProgress(newText) {
|
||||||
|
loadingScreenProgress.innerHTML = `Loading...<br>${newText}<br><br>Tip: ${selectedTip}`
|
||||||
|
}
|
||||||
|
|
||||||
|
function startLoadingScreen() {
|
||||||
|
loadingScreenProgress.innerHTML = `Loading...<br><br>Tip: ${selectedTip}`
|
||||||
|
|
||||||
|
const loadDur = randomFloat(2000, 4000)
|
||||||
|
debugPrint("loadDur initial", loadDur)
|
||||||
|
|
||||||
|
setTimeout(function() {
|
||||||
|
if (loadingComplete) { removeLoadingScreen() }
|
||||||
|
fakeLoadingComplete = true
|
||||||
|
}, loadDur)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function removeLoadingScreen() {
|
||||||
|
if (!isLoading) { return }
|
||||||
|
|
||||||
|
setLoadingProgress("Done!")
|
||||||
|
|
||||||
|
isLoading = false
|
||||||
|
const fadeOutDur = 0.5
|
||||||
|
|
||||||
|
setTimeout(function() {
|
||||||
|
loadingScreenEl.style.animation = `fade-out ${fadeOutDur}s forwards`
|
||||||
|
|
||||||
|
setTimeout(function() {
|
||||||
|
loadingScreenEl.style.left = "100vw"
|
||||||
|
newTip()
|
||||||
|
}, fadeOutDur * 1000)
|
||||||
|
}, 500)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function setShowFakeLoading(state) {
|
||||||
|
localStorage["fakeLoading"] = state.toString()
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
document.addEventListener("keydown", function(event) {
|
||||||
|
if (event.key === "Escape") {
|
||||||
|
removeLoadingScreen()
|
||||||
|
}
|
||||||
|
})
|
||||||
@@ -0,0 +1,39 @@
|
|||||||
|
li {
|
||||||
|
list-style-type: none;
|
||||||
|
display: inline-flex;
|
||||||
|
margin: calc(1px * var(--pxDensity));
|
||||||
|
margin-bottom: calc(2px * var(--pxDensity));
|
||||||
|
}
|
||||||
|
|
||||||
|
.navBar {
|
||||||
|
display: inline;
|
||||||
|
position: absolute;
|
||||||
|
top: round(calc(100% - calc(5px * var(--pxDensity))), var(--pxDensityPx));
|
||||||
|
left: round(50%, var(--pxDensityPx));
|
||||||
|
transform: translate(-50%, -100%);
|
||||||
|
font-size: 0px;
|
||||||
|
width: max-content;
|
||||||
|
}
|
||||||
|
.navBtn {
|
||||||
|
background-color: transparent;
|
||||||
|
display: inline-flex;
|
||||||
|
padding: 0%;
|
||||||
|
border: none;
|
||||||
|
filter: drop-shadow(0px calc(1px * var(--pxDensity)) rgba(0, 0, 0, 0.5));
|
||||||
|
margin: 0%;
|
||||||
|
box-shadow: none;
|
||||||
|
width: auto;
|
||||||
|
}
|
||||||
|
.navBtnImg {
|
||||||
|
width: calc(12px * var(--pxDensity));
|
||||||
|
height: calc(12px * var(--pxDensity));
|
||||||
|
image-rendering: pixelated;
|
||||||
|
margin: 0%;
|
||||||
|
}
|
||||||
|
.navBtnIcon {
|
||||||
|
width: calc(8px * var(--pxDensity));
|
||||||
|
height: calc(8px * var(--pxDensity));
|
||||||
|
image-rendering: pixelated;
|
||||||
|
position: absolute;
|
||||||
|
margin: calc(2px * var(--pxDensity));
|
||||||
|
}
|
||||||
@@ -0,0 +1,79 @@
|
|||||||
|
let navBarJson
|
||||||
|
let alts
|
||||||
|
|
||||||
|
navBarPresent = true
|
||||||
|
|
||||||
|
function onLoadNavBar() {
|
||||||
|
const container = document.getElementById("navBarContainer")
|
||||||
|
const altString = container.dataset.alt || ""
|
||||||
|
alts = altString.replaceAll(" ", "").split(",")
|
||||||
|
|
||||||
|
debugPrint("Navbar alts", alts.join(", "));
|
||||||
|
|
||||||
|
if (!container) { return }
|
||||||
|
|
||||||
|
const navBarAligner = document.createElement("div")
|
||||||
|
navBarAligner.className = "navBarAligner"
|
||||||
|
|
||||||
|
const navBar = document.createElement("ul")
|
||||||
|
navBar.className = "navBar borderedRect"
|
||||||
|
|
||||||
|
|
||||||
|
for (let btn of navBarJson) {
|
||||||
|
createdBtn = createNavBarBtn(
|
||||||
|
btn["icon"] || "",
|
||||||
|
btn["action"] || "",
|
||||||
|
btn["actionValue"] || "",
|
||||||
|
btn["iconId"] || "",
|
||||||
|
btn["alt"] || "",
|
||||||
|
btn["disabled"] || false,
|
||||||
|
btn["hidden"] || false
|
||||||
|
)
|
||||||
|
|
||||||
|
if (createdBtn != null) {
|
||||||
|
navBar.appendChild(createdBtn)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
navBarAligner.appendChild(navBar)
|
||||||
|
container.appendChild(navBarAligner)
|
||||||
|
}
|
||||||
|
|
||||||
|
function createNavBarBtn(icon = "", action = "", actionValue = "", iconId = "", alt = "", disabled = false, hidden = false) {
|
||||||
|
if (alts.includes(alt["name"])) {
|
||||||
|
icon = alt["icon"] || icon || ""
|
||||||
|
action = alt["action"] || action || ""
|
||||||
|
actionValue = alt["actionValue"] || actionValue || ""
|
||||||
|
iconId = alt["iconId"] || iconId || ""
|
||||||
|
|
||||||
|
disabled = alt["disabled"] === undefined ? disabled : alt["disabled"] || false;
|
||||||
|
hidden = alt["hidden"] || false;
|
||||||
|
}
|
||||||
|
|
||||||
|
let onClick
|
||||||
|
switch (action) {
|
||||||
|
case "navigate": onClick = `slideTransition('${actionValue}', event)`; break
|
||||||
|
case "function": onClick = `${actionValue}`; break
|
||||||
|
default: ""
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hidden) { return null; }
|
||||||
|
|
||||||
|
let btn = document.createElement("li")
|
||||||
|
|
||||||
|
btn.innerHTML = `
|
||||||
|
<button onclick="${onClick}" class="navBtn" ${disabled == true ? "disabled" : ""}>
|
||||||
|
<img class="navBtnImg" draggable="false" src="/assets/navBarBtn.png">
|
||||||
|
<img class="navBtnIcon" draggable="false" src="/assets/icons/${icon}" id="${iconId}">
|
||||||
|
</button>
|
||||||
|
`
|
||||||
|
|
||||||
|
return btn;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function loadNavBarJson() {
|
||||||
|
let navBarText = await fetch("/data/navBar.json").then(response => response.text())
|
||||||
|
navBarJson = JSON.parse(navBarText)
|
||||||
|
console.log(navBarJson)
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
let pxDensity = 1
|
||||||
|
let pxDensityDefined = false
|
||||||
|
|
||||||
|
let pxWidth = 256
|
||||||
|
let pxHeight = 164
|
||||||
|
|
||||||
|
let densityWidth = document.documentElement.clientWidth / pxWidth
|
||||||
|
let densityHeight = document.documentElement.clientHeight / pxHeight
|
||||||
|
|
||||||
|
function calcPixelDensity() {
|
||||||
|
densityWidth = document.documentElement.clientWidth / pxWidth
|
||||||
|
densityHeight = document.documentElement.clientHeight / pxHeight
|
||||||
|
|
||||||
|
let widest = Math.max(densityWidth, densityHeight)
|
||||||
|
|
||||||
|
document.documentElement.style.setProperty("--pxDensity", widest)
|
||||||
|
document.documentElement.style.setProperty("--pxDensityPx", `${widest}px`)
|
||||||
|
|
||||||
|
pxDensity = widest
|
||||||
|
|
||||||
|
pxDensityDefined = true
|
||||||
|
}
|
||||||
@@ -0,0 +1,49 @@
|
|||||||
|
let loadingScreen = document.getElementById("loadingScreen")
|
||||||
|
let mainDiv = document.getElementById("mainDiv")
|
||||||
|
|
||||||
|
function slideTransition(url, event) {
|
||||||
|
const ctrlPressed = event.ctrlKey
|
||||||
|
let dur = 500
|
||||||
|
let loadingTime = randomFloat(500, 1500)
|
||||||
|
|
||||||
|
if (localStorage["fakeLoading"] != "true") { dur = 500; loadingTime = 0; }
|
||||||
|
|
||||||
|
debugPrint("loadDur transition", loadingTime)
|
||||||
|
|
||||||
|
const slideLeft = `slide-left ${dur / 1000}s cubic-bezier(0, 0, 0, 1.46) forwards`
|
||||||
|
|
||||||
|
loadingScreen.style.animation = slideLeft
|
||||||
|
loadingScreen.style.pointerEvents = "auto"
|
||||||
|
mainDiv.style.animation = slideLeft
|
||||||
|
|
||||||
|
setLoadingProgress(`Loading URL "${url}"`)
|
||||||
|
|
||||||
|
setTimeout(function() {
|
||||||
|
setLoadingProgress("Done!")
|
||||||
|
setTimeout(function() {
|
||||||
|
if (ctrlPressed) {
|
||||||
|
window.open(url, "_blank")
|
||||||
|
}
|
||||||
|
else if (url.startsWith("http")) {
|
||||||
|
window.open(url, "_blank").focus()
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
document.location = url
|
||||||
|
}
|
||||||
|
|
||||||
|
resetLoadingScreen()
|
||||||
|
}, loadingTime)
|
||||||
|
}, dur + loadingTime)
|
||||||
|
}
|
||||||
|
|
||||||
|
function resetLoadingScreen() {
|
||||||
|
const fadeOutDur = 0.5
|
||||||
|
|
||||||
|
loadingScreen.style.left = "0vw"
|
||||||
|
loadingScreen.style.animation = `fade-out ${fadeOutDur}s forwards`
|
||||||
|
mainDiv.style.animation = "none"
|
||||||
|
|
||||||
|
setTimeout(function() {
|
||||||
|
loadingScreen.style.left = "100vw"
|
||||||
|
}, fadeOutDur * 1000)
|
||||||
|
}
|
||||||
@@ -1,81 +0,0 @@
|
|||||||
let isAnimating = true
|
|
||||||
|
|
||||||
function randomFloat(min, max) {
|
|
||||||
return Math.random() * (max - min) + min;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
function randomIntFrom0(max) {
|
|
||||||
return Math.floor(Math.random() * max)
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
function randomInt(min, max) {
|
|
||||||
return Math.floor(Math.random() * (max - min) + min)
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
function clampNearest(n, mul) {
|
|
||||||
if(n > 0)
|
|
||||||
return Math.ceil(n / mul) * mul;
|
|
||||||
else if( n < 0)
|
|
||||||
return Math.floor(n / mul) * mul;
|
|
||||||
else
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
function showGrid() {
|
|
||||||
let canvasEl = document.createElement("canvas")
|
|
||||||
canvasEl.width = window.innerWidth
|
|
||||||
canvasEl.height = window.innerHeight
|
|
||||||
canvasEl.style.position = "absolute"
|
|
||||||
canvasEl.style.top = "0%"
|
|
||||||
canvasEl.style.left = "0%"
|
|
||||||
canvasEl.style.pointerEvents = "none"
|
|
||||||
|
|
||||||
let ctx = canvasEl.getContext("2d")
|
|
||||||
|
|
||||||
document.body.appendChild(canvasEl)
|
|
||||||
|
|
||||||
let repeatCountX = window.innerWidth / pxDensity
|
|
||||||
let repeatCountY = window.innerHeight / pxDensity
|
|
||||||
|
|
||||||
ctx.strokeStyle = "#ff0000"
|
|
||||||
ctx.beginPath()
|
|
||||||
|
|
||||||
for (x = 0; x < repeatCountX; x++) {
|
|
||||||
ctx.moveTo(x * pxDensity, 0)
|
|
||||||
ctx.lineTo(x * pxDensity, window.innerHeight)
|
|
||||||
}
|
|
||||||
|
|
||||||
for (y = 0; y < repeatCountY; y++) {
|
|
||||||
ctx.moveTo(0, y * pxDensity)
|
|
||||||
ctx.lineTo(window.innerWidth, y * pxDensity)
|
|
||||||
}
|
|
||||||
|
|
||||||
ctx.stroke()
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
function pauseAnimBtn(state) {
|
|
||||||
if (state == "auto") {
|
|
||||||
if (localStorage["isAnimating"] == "true") { localStorage["isAnimating"] = "false" }
|
|
||||||
else { localStorage["isAnimating"] = "true" }
|
|
||||||
|
|
||||||
state = localStorage["isAnimating"]
|
|
||||||
}
|
|
||||||
|
|
||||||
let pauseBtn = document.getElementById("animationToggle")
|
|
||||||
|
|
||||||
if (state == "true") {
|
|
||||||
isAnimating = true
|
|
||||||
pauseBtn.src = "/assets/pauseAnimation_8x8.png"
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
isAnimating = false
|
|
||||||
pauseBtn.src = "/assets/playAnimation_8x8.png"
|
|
||||||
}
|
|
||||||
|
|
||||||
localStorage["isAnimating"] = state
|
|
||||||
}
|
|
||||||
@@ -1,24 +0,0 @@
|
|||||||
:root {
|
|
||||||
--navBar_Inner: #43608D;
|
|
||||||
--navBar_Border: #2F3E56;
|
|
||||||
--navBarBtn_Inner: #474747;
|
|
||||||
--navBarBtn_Border: #232323;
|
|
||||||
--pxDensity: 1;
|
|
||||||
--pxDensityPx: 1px;
|
|
||||||
--bottomPxMargin: 1px;
|
|
||||||
}
|
|
||||||
|
|
||||||
html {
|
|
||||||
font-family: "Roboto", sans-serif;
|
|
||||||
font-size: 1rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.pxFont {
|
|
||||||
font-family: "Roboto", sans-serif;
|
|
||||||
font-size: 1rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
@keyframes slide-left {
|
|
||||||
0% { transform: translateX(0%); }
|
|
||||||
100% { transform: translateX(-100%); }
|
|
||||||
}
|
|
||||||
@@ -3,75 +3,70 @@
|
|||||||
|
|
||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
<meta name="viewport" content="width=device-width, height=device-height, initial-scale=1.0, user-scalable=no">
|
||||||
<title>Byte Dice</title>
|
|
||||||
|
|
||||||
<link rel="stylesheet" href="/navBar.css">
|
<link rel="stylesheet" href="/global/navBar.css">
|
||||||
<link rel="stylesheet" href="/globalStyle.css">
|
<link rel="stylesheet" href="/global/globalStyle.css">
|
||||||
<link rel="stylesheet" href="/loadingScreen.css">
|
<link rel="stylesheet" href="/global/loadingScreen.css">
|
||||||
<link rel="stylesheet" href="style.css">
|
<link rel="stylesheet" href="style.css">
|
||||||
|
|
||||||
<link rel="icon" type="image/png" href="/assets/favicon_32x32.png">
|
<link rel="icon" type="image/png" href="/assets/favicon_32x32.png">
|
||||||
|
<title>Byte Dice</title>
|
||||||
|
|
||||||
|
<meta name="robots" content="index, follow">
|
||||||
|
<link rel="canonical" href="https://bytedice.net">
|
||||||
|
|
||||||
|
<meta name="description" content="A small website served as a place to find content related to Byte Dice.">
|
||||||
|
<meta name="keywords" content="Byte Dice, pixel art, procedural generation, game art, digital art, ByteDice showcase">
|
||||||
|
<meta name="author" content="Byte Dice">
|
||||||
|
|
||||||
|
<meta property="og:title" content="Byte Dice">
|
||||||
|
<meta property="og:description" content="A small website served as a place to find content related to Byte Dice.">
|
||||||
|
<meta property="og:url" content="https://bytedice.net/">
|
||||||
|
<meta property="og:type" content="website">
|
||||||
|
<meta property="og:image" content="/assets/backgrounds/embedImage.png">
|
||||||
|
<meta property="og:image:width" content="1280">
|
||||||
|
<meta property="og:image:height" content="720">
|
||||||
|
|
||||||
|
<!-- No twitter embeds except for card. Elon can go and fucking return it to its previous owners. -->
|
||||||
|
<meta name="twitter:card" content="summary_large_image">
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body style="background-color: #323232; color: #ffffff;">
|
<body>
|
||||||
<div id="mainDiv">
|
<div id="mainDiv">
|
||||||
<img src="/assets/bgGradient_256x164.png" class="bgGradient">
|
<img src="/assets/backgrounds/bgGradient_256x164.png" class="bgGradient">
|
||||||
|
|
||||||
|
<img src="/assets/backgrounds/starNoise_512x512.png" draggable="false" id="starNoise">
|
||||||
|
|
||||||
|
<canvas id="starfallCanvas"></canvas>
|
||||||
|
|
||||||
<img src="/assets/starNoise_512x512.png" draggable="false" id="starNoise">
|
|
||||||
<img src="/assets/synthwaveSun_40x40.png" draggable="false" class="synthSun">
|
<img src="/assets/synthwaveSun_40x40.png" draggable="false" class="synthSun">
|
||||||
<canvas id="synthwave" style="position: absolute; bottom: round(0%, var(--pxDensityPx)); left: round(0%, var(--pxDensityPx));"></canvas>
|
|
||||||
|
|
||||||
<!-- change discord URL to server URL -->
|
<div id="synthwaveCanvas"></div>
|
||||||
<ul class="navBar">
|
<div id="navBarContainer"></div>
|
||||||
<li>
|
|
||||||
<!-- discord URL -->
|
|
||||||
<button onclick="slideTransition('https://discord.com')" class="navBtn">
|
|
||||||
<img class="navBtnImg" draggable="false" src="/assets/navBarBtn.png">
|
|
||||||
<img class="navBtnIcon" draggable="false" src="/assets/discord_8x8.png">
|
|
||||||
</button>
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<button onclick="slideTransition('https://youtube.com/@ByteDice')" class="navBtn">
|
|
||||||
<img class="navBtnImg" draggable="false" src="/assets/navBarBtn.png">
|
|
||||||
<img class="navBtnIcon" draggable="false" src="/assets/youtube_8x8.png">
|
|
||||||
</button>
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<button onclick="slideTransition('https://github.com/ByteDice')" class="navBtn">
|
|
||||||
<img class="navBtnImg" draggable="false" src="/assets/navBarBtn.png">
|
|
||||||
<img class="navBtnIcon" draggable="false" src="/assets/github_8x8.png">
|
|
||||||
</button>
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<button class="navBtn">
|
|
||||||
<img class="navBtnImg" draggable="false" src="/assets/navBarBtn.png">
|
|
||||||
<img class="navBtnIcon" draggable="false" src="/assets/powerplates_8x8.png">
|
|
||||||
</button>
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<button onClick="pauseAnimBtn('auto')" class="navBtn">
|
|
||||||
<img class="navBtnImg" draggable="false" src="/assets/navBarBtn.png">
|
|
||||||
<img class="navBtnIcon" draggable="false" src="" id="animationToggle">
|
|
||||||
</button>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="bgDiv" id="loadingScreen">
|
<div id="loadingScreen"></div>
|
||||||
<img class="loadingImg" draggable="false" src="/assets/byteDiceBlinkAnim_32x32.png">
|
|
||||||
<button class="pxFont" onclick="stopShowingLoading()" id="loadingCornerText">
|
|
||||||
Press ESC to instantly close the loading screen<br>Or click me to never see it again.
|
|
||||||
</button>
|
|
||||||
<p id="loadingText">Loading...<br>Loading files...<br><br>Tip: Tip loading...</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<script src="/globalScript.js"></script>
|
|
||||||
<script src="/pxDensity.js"></script>
|
<!-- libs -->
|
||||||
<script src="/synthwaveEffect.js"></script>
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script>
|
||||||
<script src="/script.js"></script>
|
<script src="https://cdn.jsdelivr.net/npm/three@0.128.0/examples/js/postprocessing/EffectComposer.js"></script>
|
||||||
<script src="/loadingScreen.js"></script>
|
<script src="https://cdn.jsdelivr.net/npm/three@0.128.0/examples/js/postprocessing/RenderPass.js"></script>
|
||||||
<script src="/screenTransition.js"></script>
|
<script src="https://cdn.jsdelivr.net/npm/three@0.128.0/examples/js/postprocessing/ShaderPass.js"></script>
|
||||||
|
<script src="https://cdn.jsdelivr.net/npm/three@0.128.0/examples/js/shaders/CopyShader.js"></script>
|
||||||
|
|
||||||
|
<!-- global -->
|
||||||
|
<script src="/global/globalScript.js"></script>
|
||||||
|
<script src="/global/pxDensity.js"></script>
|
||||||
|
<script src="/global/loadingScreen.js"></script>
|
||||||
|
<script src="/global/navBar.js"></script>
|
||||||
|
<script src="/global/screenTransition.js"></script>
|
||||||
|
|
||||||
|
<!-- local -->
|
||||||
|
<script src="script.js"></script>
|
||||||
|
<script src="synthwaveEffect.js"></script>
|
||||||
|
<script src="stars.js"></script>
|
||||||
</body>
|
</body>
|
||||||
|
|
||||||
</html>
|
</html>
|
||||||
@@ -0,0 +1,66 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, height=device-height, initial-scale=1.0, user-scalable=no">
|
||||||
|
|
||||||
|
<link rel="stylesheet" href="/global/navBar.css">
|
||||||
|
<link rel="stylesheet" href="/global/globalStyle.css">
|
||||||
|
<link rel="stylesheet" href="/global/loadingScreen.css">
|
||||||
|
<link rel="stylesheet" href="style.css">
|
||||||
|
|
||||||
|
<link rel="icon" type="image/png" href="/assets/favicon_32x32.png">
|
||||||
|
<title>Byte Dice - Libs</title>
|
||||||
|
|
||||||
|
<meta name="robots" content="index, follow">
|
||||||
|
<link rel="canonical" href="https://bytedice.net/libs">
|
||||||
|
|
||||||
|
<meta name="description" content="A list of JS libraries made by Byte Dice.">
|
||||||
|
<meta name="keywords" content="Byte Dice, pixel art, procedural generation, game art, digital art, ByteDice showcase, library">
|
||||||
|
<meta name="author" content="Byte Dice">
|
||||||
|
|
||||||
|
<meta property="og:title" content="Byte Dice">
|
||||||
|
<meta property="og:description" content="A list of JS libraries made by Byte Dice.">
|
||||||
|
<meta property="og:url" content="https://bytedice.net/libs">
|
||||||
|
<meta property="og:type" content="website">
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<div id="mainDiv">
|
||||||
|
<img src="/assets/backgrounds/starNoise_512x512.png" draggable="false" id="starNoise">
|
||||||
|
<canvas id="starfallCanvas"></canvas>
|
||||||
|
|
||||||
|
<div id="navBarContainer" data-alt="byte, testtube"></div>
|
||||||
|
|
||||||
|
<ul class="libUl">
|
||||||
|
<li class="libLi">
|
||||||
|
<div class="libContainer borderedRect">
|
||||||
|
<img class="libImg" src="/assets/js.png" style="border-radius: 16px;">
|
||||||
|
<div class="libTextContainer">
|
||||||
|
<h1>Test</h1>
|
||||||
|
<p class="desc">Nothing here yet</p>
|
||||||
|
</div>
|
||||||
|
<div class="libSrcContainer">
|
||||||
|
<script src="nothing here yet"></script>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="loadingScreen"></div>
|
||||||
|
|
||||||
|
<!-- global -->
|
||||||
|
<script src="/global/globalScript.js"></script>
|
||||||
|
<script src="/global/pxDensity.js"></script>
|
||||||
|
<script src="/global/loadingScreen.js"></script>
|
||||||
|
<script src="/global/navBar.js"></script>
|
||||||
|
<script src="/global/screenTransition.js"></script>
|
||||||
|
|
||||||
|
<!-- local -->
|
||||||
|
<script src="script.js"></script>
|
||||||
|
<script src="/stars.js"></script>
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
CURRENT_PAGE = PAGES.LIBS
|
||||||
@@ -0,0 +1,68 @@
|
|||||||
|
.libContainer {
|
||||||
|
border: none;
|
||||||
|
display: grid;
|
||||||
|
width: 100%;
|
||||||
|
max-height: calc(39px * var(--pxDensity));
|
||||||
|
grid-template-columns: calc(24px * var(--pxDensity)) 1fr;
|
||||||
|
grid-template-rows: 1fr auto;
|
||||||
|
color: inherit;
|
||||||
|
}
|
||||||
|
.libImg {
|
||||||
|
image-rendering: pixelated;
|
||||||
|
overflow: hidden;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
object-fit: contain;
|
||||||
|
margin: auto auto;
|
||||||
|
}
|
||||||
|
.libSrcContainer {
|
||||||
|
background-color: var(--navBar_SecondInner);
|
||||||
|
padding: calc(1px * var(--pxDensity));
|
||||||
|
border-top: calc(1px * var(--pxDensity)) solid var(--navBar_Border);
|
||||||
|
text-align: left;
|
||||||
|
overflow-x: hidden;
|
||||||
|
grid-column: 1 / -1;
|
||||||
|
grid-row: 2;
|
||||||
|
}
|
||||||
|
.libTextContainer {
|
||||||
|
background-color: var(--navBar_SecondInner);
|
||||||
|
padding: calc(1px * var(--pxDensity));
|
||||||
|
border-left: calc(1px * var(--pxDensity)) solid var(--navBar_Border);
|
||||||
|
text-align: left;
|
||||||
|
overflow-y: scroll;
|
||||||
|
overflow-x: hidden;
|
||||||
|
height: calc(100% - (2px * var(--pxDensity)));
|
||||||
|
}
|
||||||
|
h1 {
|
||||||
|
font-size: max(1.2em, 2.5vmin);
|
||||||
|
margin: 0%;
|
||||||
|
margin-bottom: calc(1px * var(--pxDensity));
|
||||||
|
}
|
||||||
|
.desc {
|
||||||
|
font-size: max(1em, 2vmin);
|
||||||
|
margin: 0%;
|
||||||
|
}
|
||||||
|
button:hover {
|
||||||
|
filter: brightness(1.15) drop-shadow(0px calc(1px * var(--pxDensity)) rgba(0, 0, 0, 0.5));
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
button:active {
|
||||||
|
filter: brightness(0.85);
|
||||||
|
transform: translateY(calc(1px * var(--pxDensity)));
|
||||||
|
}
|
||||||
|
.libUl {
|
||||||
|
padding: calc(3px * var(--pxDensity));
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 1fr 1fr;
|
||||||
|
gap: calc(6px * var(--pxDensity))
|
||||||
|
}
|
||||||
|
@media (max-width: 1024px) {
|
||||||
|
.libUl { grid-template-columns: 1fr !important; }
|
||||||
|
}
|
||||||
|
.libLi {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
ul {
|
||||||
|
margin: 0%;
|
||||||
|
padding: 0%;
|
||||||
|
}
|
||||||
@@ -0,0 +1,168 @@
|
|||||||
|
//const libCCss = `//#file { ./lib_style.css }`
|
||||||
|
const libCss = `
|
||||||
|
:root {
|
||||||
|
--c-a-cell-width: 0px; /* automatically set */
|
||||||
|
--c-a-cell-height: 0px; /* automatically set */
|
||||||
|
|
||||||
|
--c-width: 700px;
|
||||||
|
--c-height: 400px;
|
||||||
|
--c-cell-x-count: 7;
|
||||||
|
--c-cell-y-count: 7;
|
||||||
|
--c-time-width: 50px;
|
||||||
|
--c-date-height: 1.5em;
|
||||||
|
|
||||||
|
--c-s-grid-line-width: 2px;
|
||||||
|
--c-s-grid-line-color: #000000;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.c-base {
|
||||||
|
background-color: darkgray;
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: var(--c-time-width) 1fr;
|
||||||
|
grid-template-rows: var(--c-date-height) 1fr;
|
||||||
|
width: var(--c-width);
|
||||||
|
height: var(--c-height);
|
||||||
|
padding-bottom: 0.5em;
|
||||||
|
}
|
||||||
|
|
||||||
|
ul.c-ul {
|
||||||
|
list-style-type: none;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
ul.c-times {
|
||||||
|
display: grid;
|
||||||
|
grid-template-rows: repeat(var(--c-cell-y-count), 1fr);
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
margin: -0.5em 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
ul.c-dates {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(var(--c-cell-x-count), 1fr);
|
||||||
|
grid-template-rows: 1fr;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.c-grid {
|
||||||
|
background: /* Grid lines */
|
||||||
|
repeating-linear-gradient(
|
||||||
|
0deg,
|
||||||
|
var(--c-s-grid-line-color),
|
||||||
|
var(--c-s-grid-line-color) var(--c-s-grid-line-width),
|
||||||
|
transparent var(--c-s-grid-line-width),
|
||||||
|
transparent calc((var(--c-height) / var(--c-cell-y-count)))
|
||||||
|
),
|
||||||
|
repeating-linear-gradient(
|
||||||
|
90deg,
|
||||||
|
var(--c-s-grid-line-color),
|
||||||
|
var(--c-s-grid-line-color) var(--c-s-grid-line-width),
|
||||||
|
transparent var(--c-s-grid-line-width),
|
||||||
|
transparent calc((var(--c-width) / var(--c-cell-x-count)))
|
||||||
|
);
|
||||||
|
|
||||||
|
background-size: calc(100% / var(--c-cell-x-count))
|
||||||
|
calc(100% / var(--c-cell-y-count));
|
||||||
|
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(var(--c-cell-x-count), 1fr);
|
||||||
|
grid-template-rows: 1fr;
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
|
||||||
|
class CalendarSettings {
|
||||||
|
// Not implemented
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A calendar-style element.
|
||||||
|
*/
|
||||||
|
class Calendar {
|
||||||
|
debug = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a calendar object.
|
||||||
|
*
|
||||||
|
* @param {number} cellCountX How many cells on the X axis
|
||||||
|
* @param {number} cellCountY How many cells on the Y axis
|
||||||
|
* @param {[string]} timeItems A list of strings that is rendered to the left of the grid (usually timestamps)
|
||||||
|
* @param {[string]} dateItems A list of strings that is rendered above the grid (usually dates)
|
||||||
|
*/
|
||||||
|
constructor(cellCountX, cellCountY, timeItems, dateItems) {
|
||||||
|
this.cellCount = { x: cellCountX, y: cellCountY };
|
||||||
|
this.timeItems = timeItems;
|
||||||
|
this.dateItems = dateItems;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds the calendar element to the document. This method also generates the calendar element.
|
||||||
|
*
|
||||||
|
* @param {HTMLElement} child An optional HTML element where the calendar resides in. No value makes it use the <body>.
|
||||||
|
*/
|
||||||
|
attachElement(child = null) {
|
||||||
|
console.log("Attached calendar to HTML page.");
|
||||||
|
child.appendChild(this.#generateElement());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds pre-made CSS to the document. This CSS can be partially overridden or can be skipped completely.
|
||||||
|
*/
|
||||||
|
attachCss() {
|
||||||
|
let s = document.createElement("style");
|
||||||
|
s.textContent = libCss;
|
||||||
|
document.head.insertBefore(s, document.head.firstChild);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#generateElement() {
|
||||||
|
let c = document.createElement("div");
|
||||||
|
c.className = "c-base";
|
||||||
|
|
||||||
|
let cEmpty = document.createElement("div");
|
||||||
|
cEmpty.style.display = "inline-block";
|
||||||
|
|
||||||
|
let cDates = document.createElement("ul");
|
||||||
|
cDates.className = "c-dates c-ul";
|
||||||
|
|
||||||
|
let cTimes = document.createElement("ul");
|
||||||
|
cTimes.className = "c-times c-ul";
|
||||||
|
|
||||||
|
let cGrid = document.createElement("div");
|
||||||
|
cGrid.className = "c-grid";
|
||||||
|
|
||||||
|
if (this.debug) {
|
||||||
|
let s = "red solid 1px";
|
||||||
|
cDates.style.outline = s;
|
||||||
|
cTimes.style.outline = s;
|
||||||
|
cGrid.style.outline = "blue solid 1px";
|
||||||
|
}
|
||||||
|
|
||||||
|
let all = [cEmpty, cDates, cTimes, cGrid];
|
||||||
|
for (let el of all) { c.appendChild(el); }
|
||||||
|
|
||||||
|
this.#generateDates(cDates);
|
||||||
|
this.#generateTimes(cTimes);
|
||||||
|
|
||||||
|
return c;
|
||||||
|
}
|
||||||
|
|
||||||
|
#generateDates(el) {
|
||||||
|
for (let i of this.dateItems) {
|
||||||
|
let li = document.createElement("li");
|
||||||
|
li.innerText = i;
|
||||||
|
el.appendChild(li);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#generateTimes(el) {
|
||||||
|
for (let i of this.timeItems) {
|
||||||
|
let li = document.createElement("li");
|
||||||
|
li.className = "c-time";
|
||||||
|
li.innerText = i;
|
||||||
|
el.appendChild(li);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,73 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, height=device-height, initial-scale=1.0, user-scalable=no">
|
||||||
|
|
||||||
|
<link rel="stylesheet" href="/global/navBar.css">
|
||||||
|
<link rel="stylesheet" href="/global/globalStyle.css">
|
||||||
|
<link rel="stylesheet" href="/global/loadingScreen.css">
|
||||||
|
<link rel="stylesheet" href="../style.css">
|
||||||
|
|
||||||
|
<link rel="icon" type="image/png" href="/assets/favicon_32x32.png">
|
||||||
|
<title>Byte Dice - Libs [EXPERIMENTAL]</title>
|
||||||
|
|
||||||
|
<meta name="robots" content="index, follow">
|
||||||
|
<link rel="canonical" href="https://bytedice.net/libs/test">
|
||||||
|
|
||||||
|
<meta name="description" content="A list of JS libraries made by Byte Dice. [EXPERIMENTAL]">
|
||||||
|
<meta name="keywords" content="Byte Dice, pixel art, procedural generation, game art, digital art, ByteDice showcase, library">
|
||||||
|
<meta name="author" content="Byte Dice">
|
||||||
|
|
||||||
|
<meta property="og:title" content="Byte Dice">
|
||||||
|
<meta property="og:description" content="A list of JS libraries made by Byte Dice. [EXPERIMENTAL]">
|
||||||
|
<meta property="og:url" content="https://bytedice.net/libs/test">
|
||||||
|
<meta property="og:type" content="website">
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<div id="mainDiv">
|
||||||
|
<img src="/assets/backgrounds/starNoise_512x512.png" draggable="false" id="starNoise">
|
||||||
|
<canvas id="starfallCanvas"></canvas>
|
||||||
|
|
||||||
|
<div id="navBarContainer" data-alt="byte, return"></div>
|
||||||
|
|
||||||
|
<div class="borderedRect" style="margin: calc(3px * var(--pxDensity)); max-width: 30em">
|
||||||
|
<h1 style="margin-bottom: calc(1px * var(--pxDensity)); font-size: 2em; color: red; text-decoration: underline;">
|
||||||
|
WARNING
|
||||||
|
</h1>
|
||||||
|
These are experimental/testing libraries. Do not expect anything from them, they may even be removed in the future.
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<ul class="libUl">
|
||||||
|
<li class="libLi">
|
||||||
|
<div class="libContainer borderedRect">
|
||||||
|
<img class="libImg" src="/assets/js.png" style="border-radius: 16px;">
|
||||||
|
<div class="libTextContainer">
|
||||||
|
<h1>Calendar Library</h1>
|
||||||
|
<p class="desc">Description.</p>
|
||||||
|
</div>
|
||||||
|
<div class="libSrcContainer">
|
||||||
|
<script src="https://www.bytedice.net/libs/test/calendar_test.js"></script>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="loadingScreen"></div>
|
||||||
|
|
||||||
|
<!-- global -->
|
||||||
|
<script src="/global/globalScript.js"></script>
|
||||||
|
<script src="/global/pxDensity.js"></script>
|
||||||
|
<script src="/global/loadingScreen.js"></script>
|
||||||
|
<script src="/global/navBar.js"></script>
|
||||||
|
<script src="/global/screenTransition.js"></script>
|
||||||
|
|
||||||
|
<!-- local -->
|
||||||
|
<script src="../script.js"></script>
|
||||||
|
<script src="/stars.js"></script>
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
||||||
@@ -1,40 +0,0 @@
|
|||||||
.bgDiv {
|
|
||||||
background-color: #323232;
|
|
||||||
position: absolute;
|
|
||||||
top: 0%;
|
|
||||||
left: 0%;
|
|
||||||
width: 100vw;
|
|
||||||
height: 100vh;
|
|
||||||
}
|
|
||||||
.loadingImg {
|
|
||||||
position: absolute;
|
|
||||||
top: round(50%, var(--pxDensityPx));
|
|
||||||
left: round(50%, var(--pxDensityPx));
|
|
||||||
transform: translate(-50%, -50%);
|
|
||||||
width: calc(32px * var(--pxDensity));
|
|
||||||
height: calc(32px * var(--pxDensity));
|
|
||||||
image-rendering: pixelated;
|
|
||||||
}
|
|
||||||
@keyframes fade-out {
|
|
||||||
0% {opacity: 100%;}
|
|
||||||
100% {opacity: 0%;}
|
|
||||||
}
|
|
||||||
/* custom pixel 3x5 font */
|
|
||||||
#loadingCornerText {
|
|
||||||
background-color: transparent;
|
|
||||||
color: #ffffff;
|
|
||||||
border: none;
|
|
||||||
text-align: left;
|
|
||||||
position: absolute;
|
|
||||||
top: 0%;
|
|
||||||
left: 0%;
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
#loadingText {
|
|
||||||
position: absolute;
|
|
||||||
bottom: 0%;
|
|
||||||
left: 50%;
|
|
||||||
transform: translateX(-50%);
|
|
||||||
text-align: center;
|
|
||||||
font-size: 1.5rem;
|
|
||||||
}
|
|
||||||
@@ -1,73 +0,0 @@
|
|||||||
let isLoading = true
|
|
||||||
let stopLoading = false
|
|
||||||
let loadingScreenProgress = document.getElementById("loadingText")
|
|
||||||
let loadingScreenEl = document.getElementById("loadingScreen")
|
|
||||||
|
|
||||||
let tips = [
|
|
||||||
"Just don't die.",
|
|
||||||
"Did you know this is a tip?",
|
|
||||||
"Is thisn't not very un-understandable?",
|
|
||||||
"m".repeat(200),
|
|
||||||
"Bad at a game? Just press the \"get good at games\" button!",
|
|
||||||
"I AM NOT A FEMBOY NOR GAY. (I support though :thumbsup:)",
|
|
||||||
"You should listen to that song that goes \"do do do do do do do do do.\"",
|
|
||||||
"(Almost) Every pixel in this website's pixel art is positioned pixel perfect.",
|
|
||||||
"Shouldn't I be talking about tips?",
|
|
||||||
"Did you know that... uh... hold on let me think... I don't know, I forgot.",
|
|
||||||
"This tip is very helpful.",
|
|
||||||
"The synthwave terrain background is fully procedural. No images or gifs were used."
|
|
||||||
]
|
|
||||||
|
|
||||||
let selectedTip = tips[randomIntFrom0(tips.length)]
|
|
||||||
const showLoading = localStorage["showLoading"] || "true"
|
|
||||||
|
|
||||||
|
|
||||||
function newTip() {
|
|
||||||
selectedTip = tips[randomIntFrom0(tips.length)]
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
function setLoadingProgress(newText) {
|
|
||||||
loadingScreenProgress.innerHTML = `Loading...<br>${newText}<br><br>Tip: ${selectedTip}`
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
function startLoadingScreen() {
|
|
||||||
loadingScreenProgress.innerHTML = `Loading...<br><br>Tip: ${selectedTip}`
|
|
||||||
|
|
||||||
const loadDur = randomFloat(3000, 6000)
|
|
||||||
|
|
||||||
setTimeout(removeLoadingScreen, loadDur)
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
function removeLoadingScreen() {
|
|
||||||
if (!isLoading) { return }
|
|
||||||
|
|
||||||
setLoadingProgress("Done!")
|
|
||||||
|
|
||||||
isLoading = false
|
|
||||||
const fadeOutDur = 0.5
|
|
||||||
|
|
||||||
setTimeout(function() {
|
|
||||||
loadingScreenEl.style.animation = `fade-out ${fadeOutDur}s forwards`
|
|
||||||
|
|
||||||
setTimeout(function() {
|
|
||||||
loadingScreenEl.style.left = "100vw"
|
|
||||||
document.getElementById("loadingCornerText").remove()
|
|
||||||
newTip()
|
|
||||||
}, fadeOutDur * 1000)
|
|
||||||
}, 500)
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
function stopShowingLoading() {
|
|
||||||
localStorage["showLoading"] = false
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
document.addEventListener("keydown", function(event) {
|
|
||||||
if (event.key === "Escape") {
|
|
||||||
removeLoadingScreen()
|
|
||||||
}
|
|
||||||
})
|
|
||||||
@@ -1,52 +0,0 @@
|
|||||||
li {
|
|
||||||
list-style-type: none;
|
|
||||||
display: inline-flex;
|
|
||||||
margin: calc(1px * var(--pxDensity));
|
|
||||||
margin-bottom: calc(2px * var(--pxDensity));
|
|
||||||
}
|
|
||||||
|
|
||||||
.navBar {
|
|
||||||
background-color: var(--navBar_Inner);
|
|
||||||
box-shadow: calc(1px * var(--pxDensity)) 0px var(--navBar_Border),
|
|
||||||
calc(-1px * var(--pxDensity)) 0px var(--navBar_Border),
|
|
||||||
0px calc(1px * var(--pxDensity)) var(--navBar_Border),
|
|
||||||
0px calc(-1px * var(--pxDensity)) var(--navBar_Border);
|
|
||||||
|
|
||||||
filter: drop-shadow(0px calc(2px * var(--pxDensity)) rgba(0, 0, 0, 0.5));
|
|
||||||
padding: 0%;
|
|
||||||
display: inline-block;
|
|
||||||
position: absolute;
|
|
||||||
top: round(calc(100% - calc(5px * var(--pxDensity))), var(--pxDensityPx));
|
|
||||||
left: round(50%, var(--pxDensityPx));
|
|
||||||
transform: translate(-50%, -100%);
|
|
||||||
font-size: 0px;
|
|
||||||
width: max-content;
|
|
||||||
}
|
|
||||||
.navBtn {
|
|
||||||
background-color: transparent;
|
|
||||||
display: inline-flex;
|
|
||||||
padding: 0%;
|
|
||||||
border: none;
|
|
||||||
filter: drop-shadow(0px calc(1px * var(--pxDensity)) rgba(0, 0, 0, 0.5));
|
|
||||||
}
|
|
||||||
.navBtn:hover {
|
|
||||||
filter: brightness(1.35) drop-shadow(0px calc(1px * var(--pxDensity)) rgba(0, 0, 0, 0.5));
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
.navBtn:active {
|
|
||||||
filter: brightness(0.75);
|
|
||||||
transform: translateY(calc(1px * var(--pxDensity)));
|
|
||||||
}
|
|
||||||
.navBtnImg {
|
|
||||||
width: calc(12px * var(--pxDensity));
|
|
||||||
height: calc(12px * var(--pxDensity));
|
|
||||||
image-rendering: pixelated;
|
|
||||||
margin: 0%;
|
|
||||||
}
|
|
||||||
.navBtnIcon {
|
|
||||||
width: calc(8px * var(--pxDensity));
|
|
||||||
height: calc(8px * var(--pxDensity));
|
|
||||||
image-rendering: pixelated;
|
|
||||||
position: absolute;
|
|
||||||
margin: calc(2px * var(--pxDensity));
|
|
||||||
}
|
|
||||||
@@ -1,16 +0,0 @@
|
|||||||
let pxDensity = parseFloat(getComputedStyle(document.body).getPropertyValue("--pxDensity"))
|
|
||||||
let pxDensityDefined = false
|
|
||||||
|
|
||||||
function calcPixelDensity() {
|
|
||||||
let densityWidth = document.documentElement.clientWidth / 256
|
|
||||||
let densityHeight = document.documentElement.clientHeight / 164
|
|
||||||
|
|
||||||
|
|
||||||
document.documentElement.style.setProperty("--pxDensity", Math.max(densityWidth, densityHeight))
|
|
||||||
document.documentElement.style.setProperty("--pxDensityPx", `${Math.max(densityWidth, densityHeight)}px`)
|
|
||||||
|
|
||||||
pxDensity = parseFloat(getComputedStyle(document.body).getPropertyValue("--pxDensity"))
|
|
||||||
|
|
||||||
console.log("pxDensity: " + getComputedStyle(document.body).getPropertyValue("--pxDensity"))
|
|
||||||
pxDensityDefined = true
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
Sitemap: https://bytedice.net/sitemap.xml
|
||||||
|
|
||||||
|
User-agent: Googlebot
|
||||||
|
Allow: /
|
||||||
|
User-agent: googlebot-image
|
||||||
|
Allow: /
|
||||||
|
User-agent: googlebot-mobile
|
||||||
|
Allow: /
|
||||||
|
User-agent: MSNBot
|
||||||
|
Allow: /
|
||||||
|
User-agent: DiscordBot
|
||||||
|
Allow: /
|
||||||
|
|
||||||
|
User-agent: Nutch
|
||||||
|
Disallow: /
|
||||||
|
User-agent: baiduspider
|
||||||
|
Disallow: /
|
||||||
|
User-agent: naverbot
|
||||||
|
Disallow: /
|
||||||
|
User-agent: yeti
|
||||||
|
Disallow: /
|
||||||
|
User-agent: ia_archiver
|
||||||
|
Disallow: /
|
||||||
|
|
||||||
|
User-agent: *
|
||||||
|
Disallow: /
|
||||||
|
|
||||||
|
Disallow: /cgi-bin/
|
||||||
|
Disallow: /ssi/
|
||||||
|
Disallow: /data/
|
||||||
@@ -1,19 +0,0 @@
|
|||||||
function slideTransition(url) {
|
|
||||||
const dur = 0.5
|
|
||||||
const loadingTime = 1.0
|
|
||||||
|
|
||||||
const slideLeft = `slide-left ${dur}s cubic-bezier(0, 0, 0, 1.46) forwards`
|
|
||||||
|
|
||||||
let loadingScreen = document.getElementById("loadingScreen")
|
|
||||||
let mainDiv = document.getElementById("mainDiv")
|
|
||||||
|
|
||||||
loadingScreen.style.animation = slideLeft
|
|
||||||
mainDiv.style.animation = slideLeft
|
|
||||||
|
|
||||||
setLoadingProgress(`Loading URL "${url}"`)
|
|
||||||
|
|
||||||
setTimeout(function() {
|
|
||||||
setLoadingProgress("Done!")
|
|
||||||
setTimeout(function() { document.location = url }, loadingTime * 1000)
|
|
||||||
}, (dur + loadingTime) * 1000)
|
|
||||||
}
|
|
||||||
@@ -1,30 +1,23 @@
|
|||||||
document.addEventListener("DOMContentLoaded", function() {
|
CURRENT_PAGE = PAGES.SYNTHWAVE
|
||||||
startLoadingScreen()
|
|
||||||
|
|
||||||
setLoadingProgress("Calculating pixel density...")
|
function loadingScreenSynthwave() {
|
||||||
calcPixelDensity()
|
setLoadingProgress("Adding stars...")
|
||||||
|
addStars()
|
||||||
|
|
||||||
setLoadingProgress("Adding stars...")
|
setLoadingProgress("Starting starfall animation...")
|
||||||
addStars()
|
startStarfall()
|
||||||
|
|
||||||
setLoadingProgress("Animating synthwave background...")
|
setLoadingProgress("Starting synthwave animation...")
|
||||||
startSynthAnimation()
|
defShaderMaterial()
|
||||||
pauseAnimBtn(localStorage["isAnimating"] || "true")
|
preAnimateSynthwave(25)
|
||||||
|
animateSynthwave(0)
|
||||||
if (showLoading != "true") {
|
}
|
||||||
removeLoadingScreen()
|
|
||||||
}
|
|
||||||
else {
|
function onResizeSynthwave() {
|
||||||
setLoadingProgress("Cleaning up...")
|
renderer.setSize(window.innerWidth, window.innerHeight)
|
||||||
}
|
composer.setSize(window.innerWidth, window.innerHeight)
|
||||||
}, false)
|
camera.aspect = window.innerWidth / window.innerHeight
|
||||||
|
camera.updateProjectionMatrix()
|
||||||
|
if (shaderMaterial) { updateShaderMatPxDensity() }
|
||||||
function addStars() {
|
|
||||||
let starNoise = document.getElementById("starNoise")
|
|
||||||
let randX = randomInt(0, 256)
|
|
||||||
let randY = randomInt(0, 256)
|
|
||||||
|
|
||||||
starNoise.style.top = `round(calc(${randX}px * var(--pxDensity)), var(--pxDensityPx))`
|
|
||||||
starNoise.style.left = `round(calc(${randY}px * var(--pxDensity)), var(--pxDensityPx))`
|
|
||||||
}
|
}
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
CURRENT_PAGE = PAGES._404
|
||||||
@@ -0,0 +1,43 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
|
||||||
|
<url>
|
||||||
|
<loc>https://www.bytedice.net</loc>
|
||||||
|
<priority>1.00</priority>
|
||||||
|
<lastmod>2025-09-08</lastmod>
|
||||||
|
</url>
|
||||||
|
<url>
|
||||||
|
<loc>https://www.bytedice.net/toys</loc>
|
||||||
|
<priority>0.67</priority>
|
||||||
|
<lastmod>2025-11-14</lastmod>
|
||||||
|
</url>
|
||||||
|
<url>
|
||||||
|
<loc>https://www.bytedice.net/libs</loc>
|
||||||
|
<priority>0.67</priority>
|
||||||
|
<lastmod>2025-11-14</lastmod>
|
||||||
|
</url>
|
||||||
|
<url>
|
||||||
|
<loc>https://www.bytedice.net/404.html</loc>
|
||||||
|
<priority>0.67</priority>
|
||||||
|
<lastmod>2025-01-25</lastmod>
|
||||||
|
</url>
|
||||||
|
<url>
|
||||||
|
<loc>https://www.bytedice.net/toys/loading</loc>
|
||||||
|
<priority>0.33</priority>
|
||||||
|
<lastmod>2025-9-14</lastmod>
|
||||||
|
</url>
|
||||||
|
<url>
|
||||||
|
<loc>https://www.bytedice.net/toys/license</loc>
|
||||||
|
<priority>0.33</priority>
|
||||||
|
<lastmod>2026-6-30</lastmod>
|
||||||
|
</url>
|
||||||
|
<url>
|
||||||
|
<loc>https://www.bytedice.net/toys/crafting</loc>
|
||||||
|
<priority>0.33</priority>
|
||||||
|
<lastmod>2027-7-20</lastmod>
|
||||||
|
</url>
|
||||||
|
<url>
|
||||||
|
<loc>https://www.bytedice.net/libs/test</loc>
|
||||||
|
<priority>0.33</priority>
|
||||||
|
<lastmod>2025-11-14</lastmod>
|
||||||
|
</url>
|
||||||
|
</urlset>
|
||||||
@@ -0,0 +1,144 @@
|
|||||||
|
const starfallCanvas = document.getElementById("starfallCanvas")
|
||||||
|
starfallCanvas.width = window.innerWidth
|
||||||
|
starfallCanvas.height = window.innerHeight
|
||||||
|
|
||||||
|
let lastUpd = Date.now()
|
||||||
|
|
||||||
|
const starfallCtx = starfallCanvas.getContext("2d")
|
||||||
|
|
||||||
|
|
||||||
|
class Starfall {
|
||||||
|
constructor() {
|
||||||
|
this.pos = {x: 0, y: 0}
|
||||||
|
this.vel = {x: 0, y: 0}
|
||||||
|
}
|
||||||
|
|
||||||
|
generate() {
|
||||||
|
const dir = randomBool(0.5)
|
||||||
|
this.pos.x = dir ? -50 : pxWidth + 50
|
||||||
|
this.pos.y = randomInt(-starfallCanvas.height / 2, starfallCanvas.height / 2)
|
||||||
|
this.vel.x = dir ? randomFloat(240, 580) : randomFloat(-580, -240)
|
||||||
|
this.vel.y = randomFloat(20, 80)
|
||||||
|
}
|
||||||
|
|
||||||
|
draw() {
|
||||||
|
const LEN_X = this.vel.x / 15
|
||||||
|
const LEN_Y = this.vel.y / 15
|
||||||
|
|
||||||
|
// very expensive but idk how to make it less expensive (thanks canvas)
|
||||||
|
drawPixelLine(
|
||||||
|
this.pos.x,
|
||||||
|
this.pos.y,
|
||||||
|
this.pos.x - LEN_X,
|
||||||
|
this.pos.y - LEN_Y
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
stepAnimation(dt) {
|
||||||
|
this.pos.x += this.vel.x * dt
|
||||||
|
this.pos.y += this.vel.y * dt
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function drawPixelLine(fromX, fromY, toX, toY) {
|
||||||
|
const pxInflate = 0.1
|
||||||
|
|
||||||
|
const dx = Math.abs(toX - fromX)
|
||||||
|
const dy = Math.abs(toY - fromY)
|
||||||
|
const sx = fromX < toX ? 1 : -1
|
||||||
|
const sy = fromY < toY ? 1 : -1
|
||||||
|
let err = dx - dy
|
||||||
|
|
||||||
|
let x = fromX
|
||||||
|
let y = fromY
|
||||||
|
|
||||||
|
starfallCtx.beginPath()
|
||||||
|
|
||||||
|
const steps = Math.max(dx, dy)
|
||||||
|
|
||||||
|
for (let i = 0; i <= steps; i++) {
|
||||||
|
const alpha = i == 0 ? 1 : 0.1 * (1 - i / steps)
|
||||||
|
starfallCtx.fillStyle = `rgba(255, 255, 255, ${alpha})`
|
||||||
|
|
||||||
|
starfallCtx.rect(
|
||||||
|
clampNearest(x * pxDensity, pxDensity) - 0.5,
|
||||||
|
clampNearest(y * pxDensity, pxDensity) - 0.5,
|
||||||
|
pxInflate + pxDensity,
|
||||||
|
pxInflate + pxDensity
|
||||||
|
)
|
||||||
|
|
||||||
|
const e2 = 2 * err
|
||||||
|
if (e2 > -dy) {
|
||||||
|
err -= dy
|
||||||
|
x += sx
|
||||||
|
}
|
||||||
|
if (e2 < dx) {
|
||||||
|
err += dx
|
||||||
|
y += sy
|
||||||
|
}
|
||||||
|
|
||||||
|
starfallCtx.fill()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
let allStarfalls = []
|
||||||
|
|
||||||
|
function animateStarfalls() {
|
||||||
|
let now = Date.now()
|
||||||
|
|
||||||
|
const MILLISEC = 1000
|
||||||
|
let dt = (now - lastUpd) / MILLISEC
|
||||||
|
lastUpd = now
|
||||||
|
|
||||||
|
if (!isAnimating) { return }
|
||||||
|
|
||||||
|
starfallCtx.clearRect(0, 0, starfallCanvas.width, starfallCanvas.height)
|
||||||
|
|
||||||
|
let shouldGenerate = randomBool(0.01)
|
||||||
|
|
||||||
|
if (shouldGenerate) {
|
||||||
|
let newStarfall = new Starfall()
|
||||||
|
newStarfall.generate()
|
||||||
|
allStarfalls.push(newStarfall)
|
||||||
|
}
|
||||||
|
|
||||||
|
for (let starfall of allStarfalls) {
|
||||||
|
if (starfall.pos.x < -50 || starfall.pos.x > pxWidth + 50) {
|
||||||
|
allStarfalls.splice(allStarfalls.indexOf(starfall), 1)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
starfall.stepAnimation(dt)
|
||||||
|
starfall.draw()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function startStarfall() {
|
||||||
|
animateStarfalls()
|
||||||
|
|
||||||
|
requestAnimationFrame(startStarfall)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function addStars() {
|
||||||
|
let starNoise = document.getElementById("starNoise")
|
||||||
|
let randX = randomInt(0, 256)
|
||||||
|
let randY = randomInt(0, 256)
|
||||||
|
|
||||||
|
debugPrint("Star pos", `[x: ${randX}, y: ${randY}]`)
|
||||||
|
|
||||||
|
starNoise.style.top = `round(calc(${randX}px * var(--pxDensity)), var(--pxDensityPx))`
|
||||||
|
starNoise.style.left = `round(calc(${randY}px * var(--pxDensity)), var(--pxDensityPx))`
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function loadingScreenStars() {
|
||||||
|
setLoadingProgress("Adding stars...")
|
||||||
|
addStars()
|
||||||
|
|
||||||
|
setLoadingProgress("Starting starfall animation...")
|
||||||
|
startStarfall()
|
||||||
|
}
|
||||||
@@ -1,39 +1,25 @@
|
|||||||
body {
|
|
||||||
height: 100vh;
|
|
||||||
background-color: #323232;
|
|
||||||
overflow: hidden;
|
|
||||||
margin: 0%;
|
|
||||||
}
|
|
||||||
.synthSun {
|
.synthSun {
|
||||||
width: calc(40px * var(--pxDensity));
|
width: calc(40px * var(--pxDensity));
|
||||||
height: auto;
|
height: auto;
|
||||||
aspect-ratio: 1 / 1;
|
aspect-ratio: 1 / 1;
|
||||||
image-rendering: pixelated;
|
image-rendering: pixelated;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: round(60%, var(--pxDensityPx));
|
top: round(60%, var(--pxDensityPx));
|
||||||
left: round(50%, var(--pxDensityPx));
|
left: round(50%, var(--pxDensityPx));
|
||||||
transform: translate(-50%, -50%);
|
transform: translate(-50%, -50%);
|
||||||
}
|
|
||||||
#starNoise {
|
|
||||||
position: absolute;
|
|
||||||
top: 0%;
|
|
||||||
left: 0%;
|
|
||||||
width: calc(512px * var(--pxDensity));
|
|
||||||
height: auto;
|
|
||||||
aspect-ratio: 1 / 1;
|
|
||||||
image-rendering: pixelated;
|
|
||||||
transform: translate(-50%, -50%);
|
|
||||||
}
|
|
||||||
#mainDiv {
|
|
||||||
height: 100vh;
|
|
||||||
}
|
}
|
||||||
.bgGradient {
|
.bgGradient {
|
||||||
image-rendering: pixelated;
|
image-rendering: pixelated;
|
||||||
width: calc(256px * var(--pxDensity));
|
width: calc(256px * var(--pxDensity));
|
||||||
height: calc(164px * var(--pxDensity));
|
height: calc(164px * var(--pxDensity));
|
||||||
aspect-ratio: auto;
|
aspect-ratio: auto;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: round(50%, var(--pxDensityPx));
|
top: round(50%, var(--pxDensityPx));
|
||||||
left: round(50%, var(--pxDensityPx));
|
left: round(50%, var(--pxDensityPx));
|
||||||
transform: translate(-50%, -50%);
|
transform: translate(-50%, -50%);
|
||||||
|
}
|
||||||
|
#synthwaveCanvas {
|
||||||
|
position: absolute;
|
||||||
|
top: round(0%, var(--pxDensityPx));
|
||||||
|
left: round(0%, var(--pxDensityPx));
|
||||||
}
|
}
|
||||||
@@ -1,323 +1,412 @@
|
|||||||
// TODO: dont render lines behind the fill (fillHillPolygons())
|
let usePostProcessing = true
|
||||||
|
|
||||||
let canvas = document.getElementById("synthwave")
|
const scene = new THREE.Scene()
|
||||||
let ctx = canvas.getContext("2d")
|
const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000)
|
||||||
let hills = []
|
const renderer = new THREE.WebGLRenderer({ alpha: true })
|
||||||
|
|
||||||
let pxInflate = 0.5
|
const composer = new THREE.EffectComposer(renderer)
|
||||||
|
|
||||||
canvas.width = window.innerWidth
|
composer.setSize(window.innerWidth, window.innerHeight)
|
||||||
canvas.height = window.innerHeight
|
|
||||||
ctx.imageSmoothingEnabled = false;
|
const renderPass = new THREE.RenderPass(scene, camera)
|
||||||
|
composer.addPass(renderPass)
|
||||||
|
|
||||||
|
const renderTarget = new THREE.WebGLRenderTarget(window.innerWidth, window.innerHeight, {
|
||||||
|
minFilter: THREE.NearestFilter,
|
||||||
|
magFilter: THREE.NearestFilter,
|
||||||
|
format: THREE.RGBAFormat,
|
||||||
|
type: THREE.UNSIGNED_BYTE
|
||||||
|
})
|
||||||
|
|
||||||
|
let shaderMaterial
|
||||||
|
let shaderPass
|
||||||
|
|
||||||
|
|
||||||
function lerp(x, y, t) {
|
function defShaderMaterial() {
|
||||||
return x + t * (y - x)
|
shaderMaterial = makeShaderMaterial()
|
||||||
|
|
||||||
|
shaderPass = new THREE.ShaderPass(shaderMaterial)
|
||||||
|
composer.addPass(shaderPass)
|
||||||
|
shaderPass.material.uniforms.tDiffuse.value = renderTarget.texture
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function makeShaderMaterial() {
|
||||||
|
return new THREE.ShaderMaterial({
|
||||||
|
vertexShader: `
|
||||||
|
varying vec2 vUv;
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
vUv = uv;
|
||||||
|
gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
fragmentShader: `
|
||||||
|
varying vec2 vUv;
|
||||||
|
|
||||||
|
uniform vec3 edgeColor;
|
||||||
|
uniform vec3 centerColor;
|
||||||
|
uniform sampler2D tDiffuse;
|
||||||
|
uniform float pxDensity;
|
||||||
|
uniform vec2 resolution;
|
||||||
|
|
||||||
|
float brightness(vec3 c) {
|
||||||
|
return (c.r + c.g + c.b) / 3.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
vec2 uv = vUv;
|
||||||
|
|
||||||
|
vec2 screenPxCount = vec2(resolution.x / pxDensity, resolution.y / pxDensity);
|
||||||
|
//screenPxCount = vec2(256.0, 164.0);
|
||||||
|
vec2 p = floor(uv * screenPxCount) / screenPxCount;
|
||||||
|
|
||||||
|
vec4 color = texture2D(tDiffuse, p);
|
||||||
|
|
||||||
|
float b = brightness(color.rgb);
|
||||||
|
float centerB = brightness(centerColor);
|
||||||
|
float alpha = floor(color.a);
|
||||||
|
|
||||||
|
if (b > centerB) {
|
||||||
|
gl_FragColor = vec4(edgeColor, color.a);
|
||||||
|
}
|
||||||
|
else if (color == vec4(0.0)) {
|
||||||
|
gl_FragColor = vec4(0.0);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
gl_FragColor = vec4(centerColor, color.a);
|
||||||
|
}
|
||||||
|
|
||||||
|
//gl_FragColor = color;
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
uniforms: {
|
||||||
|
edgeColor: { value: new THREE.Color(0x00b4f0) },
|
||||||
|
centerColor: { value: new THREE.Color(0x323232) },
|
||||||
|
tDiffuse: { value: null },
|
||||||
|
pxDensity: { value: pxDensity },
|
||||||
|
resolution: { value: new THREE.Vector2(window.innerWidth, window.innerHeight) }
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function updateShaderMatPxDensity() {
|
||||||
|
shaderMaterial.uniforms.resolution.value = new THREE.Vector2(window.innerWidth, window.innerHeight)
|
||||||
|
shaderMaterial.uniforms.pxDensity.value = pxDensity
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// TODO: add pixelation shader
|
||||||
|
|
||||||
|
if (!renderer) {
|
||||||
|
alert("Renderer failed to load, using static images instead.")
|
||||||
|
// TODO: add image logic
|
||||||
|
throw new Error("Renderer initialization failed.")
|
||||||
|
}
|
||||||
|
|
||||||
|
renderer.setSize(window.innerWidth, window.innerHeight)
|
||||||
|
|
||||||
|
const mainCanvas = document.getElementById("synthwaveCanvas")
|
||||||
|
mainCanvas.appendChild(renderer.domElement)
|
||||||
|
|
||||||
|
renderer.setClearColor(0x000000, 0)
|
||||||
|
|
||||||
|
const geometry = new THREE.BufferGeometry()
|
||||||
|
|
||||||
|
let meshVerts = [
|
||||||
|
-1, 0, 1,
|
||||||
|
-1, 1, -1,
|
||||||
|
1, 1, 1,
|
||||||
|
1, 0, -1
|
||||||
|
]
|
||||||
|
|
||||||
|
let meshIndices = [
|
||||||
|
0, 1, 2,
|
||||||
|
1, 2, 3
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
geometry.setAttribute("position", new THREE.BufferAttribute(new Float32Array(meshVerts), 3))
|
||||||
|
geometry.setIndex(new THREE.BufferAttribute(new Uint16Array(meshIndices), 1))
|
||||||
|
geometry.computeVertexNormals()
|
||||||
|
geometry.computeBoundingBox()
|
||||||
|
|
||||||
|
const material = new THREE.MeshBasicMaterial({ color: 0xff0000, wireframe: false, side: THREE.DoubleSide })
|
||||||
|
const mesh = new THREE.Mesh(geometry, material)
|
||||||
|
|
||||||
|
//scene.add(mesh)
|
||||||
|
|
||||||
|
// "toward sun"
|
||||||
|
/* camera.position.z = 5
|
||||||
|
camera.position.y = 2 */
|
||||||
|
|
||||||
|
// far away "toward sun"
|
||||||
|
camera.position.z = 0
|
||||||
|
camera.position.y = 1
|
||||||
|
camera.rotation.x = 0.2
|
||||||
|
|
||||||
|
// top down
|
||||||
|
/* camera.position.y = 5
|
||||||
|
camera.lookAt(0, 0, 0) */
|
||||||
|
|
||||||
|
debugPrint(
|
||||||
|
"Camera transform",
|
||||||
|
`Pos: [x: ${camera.position.x}, y: ${camera.position.y}, z: ${camera.position.z}]
|
||||||
|
Rot: [x: ${camera.rotation.x}, y: ${camera.rotation.y}, z: ${camera.rotation.z}]`
|
||||||
|
)
|
||||||
|
|
||||||
function hillCurve(x, width) {
|
function hillCurve(x, width) {
|
||||||
return (Math.sin(Math.PI * (x + 1)) + 1) ** width
|
return (Math.sin(Math.PI * (x + 1)) + 1) ** width
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
function drawPixelLineLow(fromX, fromY, toX, toY) {
|
function generateHill(width, length, segments, maxHeight, posOffsetZ = 0, prevHillVerts = []) {
|
||||||
let dx = toX - fromX
|
let hillIndices = []
|
||||||
let dy = toY - fromY
|
let startPos = -width / 2
|
||||||
let yi = 1
|
let triCount = segments + 1
|
||||||
|
|
||||||
if (dy < 0) {
|
let prevHillHeights = prevHillVerts.filter((_, index) => (index - 1) % 3 === 0)
|
||||||
yi = -1
|
|
||||||
dy = -dy
|
|
||||||
}
|
|
||||||
|
|
||||||
let d = 2 * dy - dx
|
let hillData = generateHillVerts(
|
||||||
y = fromY
|
triCount,
|
||||||
|
maxHeight,
|
||||||
|
width,
|
||||||
|
startPos,
|
||||||
|
prevHillHeights,
|
||||||
|
segments,
|
||||||
|
length,
|
||||||
|
posOffsetZ
|
||||||
|
)
|
||||||
|
let hillVerts = hillData.verts
|
||||||
|
let hillUVs = hillData.uvs
|
||||||
|
|
||||||
for (let x = fromX; x <= toX; x++) {
|
for (let i = 0; i < segments; i++) {
|
||||||
ctx.rect(
|
hillIndices.push(i * 2, i * 2 + 2, i * 2 + 1)
|
||||||
clampNearest(x * pxDensity, pxDensity) - 0.5,
|
hillIndices.push(i * 2 + 1, i * 2 + 2, i * 2 + 3)
|
||||||
clampNearest(y * pxDensity, pxDensity) - 0.5,
|
}
|
||||||
pxInflate + pxDensity,
|
|
||||||
pxInflate + pxDensity
|
|
||||||
);
|
|
||||||
|
|
||||||
if (d > 0) {
|
const geometry = new THREE.BufferGeometry()
|
||||||
y = y + yi;
|
|
||||||
d = d + (2 * (dy - dx));
|
geometry.setAttribute("position", new THREE.BufferAttribute(new Float32Array(hillVerts), 3))
|
||||||
}
|
geometry.setAttribute("uv", new THREE.BufferAttribute(new Float32Array(hillUVs), 2))
|
||||||
else { d = d + 2 * dy; }
|
geometry.setIndex(new THREE.BufferAttribute(new Uint16Array(hillIndices), 1))
|
||||||
}
|
geometry.computeVertexNormals()
|
||||||
|
geometry.computeBoundingBox()
|
||||||
|
|
||||||
|
const material = generateHillMat(segments, width, length)
|
||||||
|
const newMesh = new THREE.Mesh(geometry, material)
|
||||||
|
|
||||||
|
newMesh.position.z = posOffsetZ
|
||||||
|
|
||||||
|
return {mesh: newMesh, verts: hillVerts, indices: hillIndices}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
function drawPixelLineHigh(fromX, fromY, toX, toY) {
|
function generateHillVerts(
|
||||||
let dx = toX - fromX
|
triCount,
|
||||||
let dy = toY - fromY
|
maxHeight,
|
||||||
let xi = 1
|
width,
|
||||||
|
startPos,
|
||||||
|
prevHillHeights,
|
||||||
|
segments,
|
||||||
|
length
|
||||||
|
) {
|
||||||
|
let hillVerts = []
|
||||||
|
let hillUVs = []
|
||||||
|
|
||||||
if (dx < 0) {
|
for (let i = 0; i < triCount; i++) {
|
||||||
xi = -1
|
let height = randomFloat(0, maxHeight) * hillCurve(i / triCount, 1.2)
|
||||||
dx = -dx
|
|
||||||
}
|
|
||||||
|
|
||||||
let d = 2 * dx - dy
|
hillVerts.push(i / (triCount - 1) * width + startPos)
|
||||||
x = fromX
|
|
||||||
|
|
||||||
for (let y = fromY; y <= toY; y++) {
|
if (prevHillHeights.length / 2 != segments + 1) { hillVerts.push(0) }
|
||||||
ctx.rect(
|
else { hillVerts.push(prevHillHeights[i * 2 + 1]) }
|
||||||
clampNearest(x * pxDensity, pxDensity) - 0.5,
|
|
||||||
clampNearest(y * pxDensity, pxDensity) - 0.5,
|
|
||||||
pxInflate + pxDensity,
|
|
||||||
pxInflate + pxDensity
|
|
||||||
);
|
|
||||||
|
|
||||||
if (d > 0) {
|
hillVerts.push(length / 2)
|
||||||
x = x + xi;
|
|
||||||
d = d + (2 * (dx - dy));
|
hillUVs.push(i / (triCount - 1), 0)
|
||||||
}
|
|
||||||
else { d = d + 2 * dx; }
|
hillVerts.push(
|
||||||
}
|
i / (triCount - 1) * width + startPos,
|
||||||
|
height,
|
||||||
|
-length / 2
|
||||||
|
)
|
||||||
|
hillUVs.push(i / (triCount - 1), 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
return {verts: hillVerts, uvs: hillUVs}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
function idxWidthOffset(hill, idx) {
|
function generateHillMat(segments, width, length) {
|
||||||
let x = ((hill.points[idx][0] * hill.widthMul) + ((-hill.width * hill.widthMul) / 2) + hill.x) / pxDensity
|
//return new THREE.MeshBasicMaterial({ color: 0x00FF00, wireframe: false })
|
||||||
let y = ((hill.points[idx][1] * hill.widthMul) + hill.y) / pxDensity
|
|
||||||
|
|
||||||
return { x: x, y: y }
|
const vertexShader = `
|
||||||
|
varying vec2 vUv;
|
||||||
|
void main() {
|
||||||
|
vUv = uv;
|
||||||
|
gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
|
||||||
|
}
|
||||||
|
`
|
||||||
|
|
||||||
|
const fragmentShader = `
|
||||||
|
varying vec2 vUv;
|
||||||
|
uniform vec3 edgeColor;
|
||||||
|
uniform vec3 centerColor;
|
||||||
|
uniform int segmentCount;
|
||||||
|
uniform vec2 resolution;
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
vec2 uv = vUv;
|
||||||
|
uv.x = fract(uv.x * float(segmentCount));
|
||||||
|
|
||||||
|
float edgeThresholdX = 0.075;
|
||||||
|
float edgeThresholdY = 0.075;
|
||||||
|
|
||||||
|
if (resolution.x > resolution.y) {
|
||||||
|
edgeThresholdX *= resolution.y / resolution.x;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
edgeThresholdY *= resolution.x / resolution.y;
|
||||||
|
}
|
||||||
|
|
||||||
|
float isEdge = 0.0;
|
||||||
|
|
||||||
|
if (uv.y < edgeThresholdY || uv.y > (1.0 - edgeThresholdY)) {
|
||||||
|
isEdge = 1.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (uv.x < edgeThresholdX || uv.x > (1.0 - edgeThresholdX)) {
|
||||||
|
isEdge = 1.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
vec3 color = mix(centerColor, edgeColor, isEdge);
|
||||||
|
|
||||||
|
gl_FragColor = vec4(color, 1.0);
|
||||||
|
}
|
||||||
|
`
|
||||||
|
|
||||||
|
const material = new THREE.ShaderMaterial({
|
||||||
|
vertexShader,
|
||||||
|
fragmentShader,
|
||||||
|
uniforms: {
|
||||||
|
edgeColor: { value: new THREE.Color(0x00b4f0) },
|
||||||
|
centerColor: { value: new THREE.Color(0x323232) },
|
||||||
|
segmentCount: { value: segments },
|
||||||
|
resolution: { value: new THREE.Vector2(width / segments, length) }
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
return material
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
function idxWidthOffsetNoPx(hill, idx) {
|
let allHillMeshes = []
|
||||||
let x = ((hill.points[idx][0] * hill.widthMul) + ((-hill.width * hill.widthMul) / 2) + hill.x)
|
|
||||||
let y = ((hill.points[idx][1] * hill.widthMul) + hill.y)
|
|
||||||
|
|
||||||
return { x: x, y: y }
|
function addHillMesh(mesh) {
|
||||||
|
allHillMeshes.push(mesh)
|
||||||
|
scene.add(mesh)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
function xyWidthOffset(hill, x, y) {
|
const hillLength = 1.2
|
||||||
let x1 = ((x * hill.widthMul) + ((-hill.width * hill.widthMul) / 2) + hill.x) / pxDensity
|
let hillSpawnOffset = -25
|
||||||
let y1 = ((y * hill.widthMul) + hill.y) / pxDensity
|
let lastHillVerts = []
|
||||||
|
let hasPreAnimated = false
|
||||||
|
|
||||||
return { x: x1, y: y1 }
|
function preAnimateSynthwave(times) {
|
||||||
|
for (let i = 0; i < times; i++) {
|
||||||
|
let hillOffset = -(i * hillLength) + 0.02
|
||||||
|
|
||||||
|
let newHill = generateHill(
|
||||||
|
15, //camera.aspect * 10,
|
||||||
|
hillLength,
|
||||||
|
25,
|
||||||
|
4,
|
||||||
|
hillOffset,
|
||||||
|
lastHillVerts
|
||||||
|
)
|
||||||
|
|
||||||
|
addHillMesh(newHill.mesh)
|
||||||
|
lastHillVerts = newHill.verts
|
||||||
|
hillSpawnOffset = hillOffset
|
||||||
|
}
|
||||||
|
|
||||||
|
hasPreAnimated = true
|
||||||
|
debugPrint("Pre-animated synthwave complete")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
function drawPixelLine(fromX, fromY, toX, toY) {
|
let lastTime = 0
|
||||||
if (Math.abs(toY - fromY) < Math.abs(toX - fromX)) {
|
const tickRate = 12
|
||||||
if (fromX > toX) {
|
let tickMultiplier = 1
|
||||||
drawPixelLineLow(toX, toY, fromX, fromY);
|
let distSinceLastHill = 0
|
||||||
}
|
|
||||||
else {
|
function animateSynthwave(timestamp) {
|
||||||
drawPixelLineLow(fromX, fromY, toX, toY);
|
let deltaTime = (timestamp - lastTime) / 1000
|
||||||
}
|
lastTime = timestamp
|
||||||
}
|
|
||||||
else {
|
let currentTickRate = 1 / deltaTime
|
||||||
if (fromY > toY) {
|
tickMultiplier = tickRate / currentTickRate
|
||||||
drawPixelLineHigh(toX, toY, fromX, fromY);
|
|
||||||
}
|
if (!hasPreAnimated) { return }
|
||||||
else {
|
|
||||||
drawPixelLineHigh(fromX, fromY, toX, toY);
|
if (!isAnimating) {
|
||||||
}
|
render()
|
||||||
}
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const moveDist = 0.0035 //0.05 * tickMultiplier
|
||||||
|
distSinceLastHill += moveDist
|
||||||
|
|
||||||
|
for (let hill of allHillMeshes) {
|
||||||
|
hill.position.z += moveDist
|
||||||
|
|
||||||
|
const outOfBoundsCoord = 5
|
||||||
|
if (hill.position.z - hill.geometry.boundingBox.min.y >= outOfBoundsCoord) {
|
||||||
|
scene.remove(hill)
|
||||||
|
allHillMeshes.splice(allHillMeshes.indexOf(hill), 1)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (distSinceLastHill >= hillLength) {
|
||||||
|
|
||||||
|
let hillZOffset = hillLength - distSinceLastHill
|
||||||
|
|
||||||
|
let newHill = generateHill(
|
||||||
|
15, //camera.aspect * 10,
|
||||||
|
hillLength,
|
||||||
|
25,
|
||||||
|
4,
|
||||||
|
hillZOffset + 0.02 + hillSpawnOffset,
|
||||||
|
lastHillVerts
|
||||||
|
)
|
||||||
|
|
||||||
|
addHillMesh(newHill.mesh)
|
||||||
|
distSinceLastHill = -hillZOffset
|
||||||
|
lastHillVerts = newHill.verts
|
||||||
|
}
|
||||||
|
|
||||||
|
render()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
class Hill {
|
function render() {
|
||||||
constructor(x, y, color, width, frameCount) {
|
if (usePostProcessing) {
|
||||||
this.color = color
|
renderer.setRenderTarget(renderTarget)
|
||||||
this.i = 0
|
renderer.render(scene, camera)
|
||||||
this.points = []
|
|
||||||
this.x = x
|
|
||||||
this.y = y
|
|
||||||
this.frameCount = frameCount
|
|
||||||
this.stepSize = 1
|
|
||||||
this.width = width
|
|
||||||
this.widthMul = 0.2
|
|
||||||
}
|
|
||||||
|
|
||||||
generate(iterCount, maxHeight, heightOffset, xRandom) {
|
renderer.setRenderTarget(null)
|
||||||
let offset = 0
|
composer.render()
|
||||||
let stepSize = this.width / iterCount
|
|
||||||
|
|
||||||
for (let i = 0; i < iterCount + 1; i++) {
|
requestAnimationFrame(animateSynthwave)
|
||||||
let offsetNorm = offset / this.width
|
}
|
||||||
let randomHeight = randomInt(0, maxHeight * -hillCurve(offsetNorm, 1.2) * 10) + heightOffset
|
else {
|
||||||
let randomXOffset = undefined
|
renderer.setRenderTarget(null)
|
||||||
|
renderer.render(scene, camera)
|
||||||
|
|
||||||
if (i == 0 || i == iterCount) { randomXOffset = 0 }
|
requestAnimationFrame(animateSynthwave)
|
||||||
else { randomXOffset = randomInt(-xRandom, xRandom) }
|
}
|
||||||
|
|
||||||
this.points.push([offset + randomXOffset, randomHeight])
|
|
||||||
|
|
||||||
offset += stepSize
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
draw(debug) {
|
|
||||||
ctx.lineWidth = pxDensity
|
|
||||||
|
|
||||||
let start = xyWidthOffset(this, this.points[0][0], this.points[0][1])
|
|
||||||
|
|
||||||
let previousX = start.x
|
|
||||||
let previousY = start.y
|
|
||||||
|
|
||||||
ctx.beginPath()
|
|
||||||
if (debug) { ctx.moveTo(start.x * pxDensity, start.y * pxDensity) }
|
|
||||||
|
|
||||||
for (let point of this.points) {
|
|
||||||
let pos = xyWidthOffset(this, point[0], point[1])
|
|
||||||
|
|
||||||
if (debug) { ctx.lineTo(pos.x * pxDensity, pos.y * pxDensity) }
|
|
||||||
else { drawPixelLine(previousX, previousY, pos.x, pos.y) }
|
|
||||||
|
|
||||||
previousX = pos.x
|
|
||||||
previousY = pos.y
|
|
||||||
}
|
|
||||||
if (debug) {
|
|
||||||
ctx.strokeStyle = "#ff0000"
|
|
||||||
ctx.stroke()
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
ctx.fillStyle = `rgb(${this.color[0]}, ${this.color[1]}, ${this.color[2]})`
|
|
||||||
ctx.fill()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
animateStep(stepSize, colorIncrement, debug) {
|
|
||||||
if (this.i >= this.frameCount) {
|
|
||||||
hills.pop(this)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
this.y += stepSize
|
|
||||||
this.draw(debug)
|
|
||||||
|
|
||||||
this.i += 1
|
|
||||||
|
|
||||||
this.color[0] += colorIncrement
|
|
||||||
this.color[1] += colorIncrement
|
|
||||||
this.color[2] += colorIncrement
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
function connectHills(hill, previousHill, hillIdx, debug) {
|
|
||||||
let pHill = previousHill
|
|
||||||
|
|
||||||
if (pHill && hillIdx != 0) {
|
|
||||||
for (let idx in pHill.points) {
|
|
||||||
let start = idxWidthOffset(pHill, idx)
|
|
||||||
let end = idxWidthOffset(hill, idx)
|
|
||||||
|
|
||||||
let grad = ctx.createLinearGradient(start.x, start.y, end.x, end.y)
|
|
||||||
let col = hill.color
|
|
||||||
let pCol = pHill.color
|
|
||||||
|
|
||||||
grad.addColorStop(0, `rgb(${pCol[0]}, ${pCol[1]}, ${pCol[2]})`)
|
|
||||||
grad.addColorStop(1, `rgb(${col[0]}, ${col[1]}, ${col[2]})`)
|
|
||||||
|
|
||||||
ctx.beginPath()
|
|
||||||
|
|
||||||
if (debug) {
|
|
||||||
ctx.strokeStyle = grad
|
|
||||||
|
|
||||||
ctx.moveTo(start.x * pxDensity, start.y * pxDensity)
|
|
||||||
ctx.lineTo(end.x * pxDensity, end.y * pxDensity)
|
|
||||||
ctx.stroke()
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
ctx.fillStyle = grad
|
|
||||||
|
|
||||||
drawPixelLine(start.x, start.y, end.x, end.y)
|
|
||||||
ctx.fill()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
function fillHillPolygons(hill, previousHill, hillIdx) {
|
|
||||||
let pHill = previousHill
|
|
||||||
|
|
||||||
if (pHill && hillIdx != 0) {
|
|
||||||
for (let idx in pHill.points) {
|
|
||||||
if (idx == 0) { continue }
|
|
||||||
|
|
||||||
let br = idxWidthOffsetNoPx(pHill, idx)
|
|
||||||
let bl = idxWidthOffsetNoPx(pHill, idx - 1)
|
|
||||||
let tr = idxWidthOffsetNoPx(hill, idx)
|
|
||||||
let tl = idxWidthOffsetNoPx(hill, idx - 1)
|
|
||||||
|
|
||||||
ctx.beginPath()
|
|
||||||
ctx.fillStyle = "#323232"
|
|
||||||
|
|
||||||
ctx.moveTo(clampNearest(tl.x, pxDensity), clampNearest(tl.y, pxDensity))
|
|
||||||
ctx.lineTo(clampNearest(tr.x, pxDensity), clampNearest(tr.y, pxDensity))
|
|
||||||
ctx.lineTo(clampNearest(br.x, pxDensity), clampNearest(br.y, pxDensity))
|
|
||||||
ctx.lineTo(clampNearest(bl.x, pxDensity), clampNearest(bl.y, pxDensity))
|
|
||||||
ctx.lineTo(clampNearest(tl.x, pxDensity), clampNearest(tl.y, pxDensity))
|
|
||||||
|
|
||||||
ctx.fill()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
const iterToNewHill = 25
|
|
||||||
let iters = 0
|
|
||||||
let previousHill = undefined
|
|
||||||
|
|
||||||
function animateSynthWave() {
|
|
||||||
if (!isAnimating) { return }
|
|
||||||
|
|
||||||
ctx.clearRect(0, 0, canvas.width, canvas.height)
|
|
||||||
|
|
||||||
if (iters >= iterToNewHill) {
|
|
||||||
let hillInstance = new Hill(canvas.width / 2 - (1 * pxDensity), canvas.height / 3 * 2, [0, 180, 240], canvas.width, 350)
|
|
||||||
hillInstance.generate(25, canvas.height / 24, 100, 2 * pxDensity)
|
|
||||||
hills.unshift(hillInstance)
|
|
||||||
iters = 0
|
|
||||||
}
|
|
||||||
|
|
||||||
let prevHillPolygons = undefined
|
|
||||||
|
|
||||||
for (let idx in hills) {
|
|
||||||
let hill = hills[idx]
|
|
||||||
hill.y += hill.stepSize
|
|
||||||
hill.widthMul *= 1.01
|
|
||||||
|
|
||||||
fillHillPolygons(hill, prevHillPolygons, idx)
|
|
||||||
|
|
||||||
prevHillPolygons = hill
|
|
||||||
}
|
|
||||||
|
|
||||||
for (let idx in hills) {
|
|
||||||
let hill = hills[idx]
|
|
||||||
|
|
||||||
//hill.animateStep(hill.stepSize, -0.2, true)
|
|
||||||
hill.animateStep(hill.stepSize, -0.2, false)
|
|
||||||
|
|
||||||
hill.stepSize = -0.005
|
|
||||||
hill.widthMul *= 1.01
|
|
||||||
|
|
||||||
connectHills(hill, previousHill, idx, false)
|
|
||||||
|
|
||||||
previousHill = hill
|
|
||||||
}
|
|
||||||
|
|
||||||
iters++
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
function startSynthAnimation() {
|
|
||||||
for (i = 0; i < 350; i++) {
|
|
||||||
animateSynthWave()
|
|
||||||
}
|
|
||||||
|
|
||||||
setInterval( animateSynthWave, 1000 / 12)
|
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,101 @@
|
|||||||
|
<!-- If you've forked the repository then "ByteDice.net" might want to be changed to your product -->
|
||||||
|
# ByteDice.net/toys/crafting Documentation
|
||||||
|
|
||||||
|
|
||||||
|
## General Information
|
||||||
|
> In this documentation, I will refer to the page (ByteDice.net/toys/crafting) as "CTree" (Crafting-Tree) for simplicity.
|
||||||
|
|
||||||
|
CTree is a tool designed for generating **multiple** "crafting trees", which are tree structures that
|
||||||
|
represent how items combine to other items, eventually leading to a product. It currently only
|
||||||
|
supports additive and transformative crafting, I.E., `A + B + ... = C` and `A -> B`.
|
||||||
|
CTree also uses the TOML format for its input data.
|
||||||
|
|
||||||
|
|
||||||
|
## Making a Tree
|
||||||
|
As said earlier, CTree uses the TOML format as data. The data is split into 2 important parts, `[io]` and `recipes`.
|
||||||
|
|
||||||
|
* `[io]` contain the items that the crating tree is allowed to use as starting items,
|
||||||
|
as well as the products it should produce. This should not contain every item,
|
||||||
|
only the "starting" items (more on that later).
|
||||||
|
* `[recipes]` contains the recipe data, AKA, how items turn into other items
|
||||||
|
|
||||||
|
To start off, lets add the 2 parts and some other required data.
|
||||||
|
|
||||||
|
```toml
|
||||||
|
# This hashtag line is a comment and is ignored by the format.
|
||||||
|
# Comments are only for clarity and are completely optional.
|
||||||
|
|
||||||
|
[io]
|
||||||
|
# Here we add our starting items (without it the program wouldn't do anything)
|
||||||
|
inputs = []
|
||||||
|
# Here we add our products (without it the program wouldn't do anything)
|
||||||
|
products = []
|
||||||
|
|
||||||
|
|
||||||
|
[recipes]
|
||||||
|
# We'll add more here soon
|
||||||
|
```
|
||||||
|
|
||||||
|
in both `inputs` and `products`, you specify items as a string arrays (the square brackets).
|
||||||
|
It's important to note that capitalization matters, "Item" and "item" are not the same thing.
|
||||||
|
|
||||||
|
Make sure you separate each entry with a comma, and encase each string in quotes.
|
||||||
|
|
||||||
|
```toml
|
||||||
|
[io]
|
||||||
|
# Here we add our starting items (without it the program wouldn't do anything)
|
||||||
|
inputs = ["item1", "item2", "item3"]
|
||||||
|
# Here we add our products (without it the program wouldn't do anything)
|
||||||
|
products = ["product1", "product2", "product3"]
|
||||||
|
```
|
||||||
|
|
||||||
|
Now we can add recipes, which is a fairly similar and easy process, but instead of adding items
|
||||||
|
to an array, we are making that array. Only 1 recipe can exist per item since there is no way for
|
||||||
|
the program to choose between 2 recipes.
|
||||||
|
|
||||||
|
The way we declare a recipe is to list the product, followed by an array of its ingredients.
|
||||||
|
Spaces are not allowed in item names by the way, I suggest using underscores or writingLikeThis.
|
||||||
|
the order of the recipes or ingredients also does not matter
|
||||||
|
|
||||||
|
```toml
|
||||||
|
[recipes]
|
||||||
|
# essentially means `product1 = item1 + item2`
|
||||||
|
product1 = ["item1", "item2"]
|
||||||
|
|
||||||
|
# Creating a new item thats neither product nor starting item
|
||||||
|
new_item = ["item2", "item3"]
|
||||||
|
|
||||||
|
# product2 = new_item + 2 item3's
|
||||||
|
product2 = ["new_item", "item3", "item3"]
|
||||||
|
```
|
||||||
|
|
||||||
|
If an item (except starting items) doesn't have a recipe associated, like our "product3",
|
||||||
|
then you will get an error (unless the item is never part of a tree).
|
||||||
|
|
||||||
|
This is what we've made so far, plus a recipe for "product3" so it doesn't error.
|
||||||
|
|
||||||
|
```toml
|
||||||
|
[io]
|
||||||
|
# Here we add our starting items (without it the program wouldn't do anything)
|
||||||
|
inputs = ["item1", "item2", "item3"]
|
||||||
|
# Here we add our products (without it the program wouldn't do anything)
|
||||||
|
products = ["product1", "product2", "product3"]
|
||||||
|
|
||||||
|
|
||||||
|
[recipes]
|
||||||
|
# essentially means `product1 = item1 + item2`
|
||||||
|
product1 = ["item1", "item2"]
|
||||||
|
|
||||||
|
# Creating a new item thats neither product nor starting item
|
||||||
|
new_item = ["item2", "item3"]
|
||||||
|
|
||||||
|
# product2 = new_item + 2 item3's
|
||||||
|
product2 = ["new_item", "item3", "item3"]
|
||||||
|
|
||||||
|
# item1 -> smelted_item
|
||||||
|
smelted_item = ["item1"]
|
||||||
|
# smelted_item -> product3
|
||||||
|
product3 = ["smelted_item"]
|
||||||
|
```
|
||||||
|
|
||||||
|
Bonus: Any items that are blue in the tree structure are starting items or the final item for that tree
|
||||||
@@ -0,0 +1,75 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, height=device-height, initial-scale=1.0, user-scalable=no">
|
||||||
|
|
||||||
|
<link rel="stylesheet" href="/global/navBar.css">
|
||||||
|
<link rel="stylesheet" href="/global/globalStyle.css">
|
||||||
|
<link rel="stylesheet" href="/global/loadingScreen.css">
|
||||||
|
<link rel="stylesheet" href="style.css">
|
||||||
|
<link rel="stylesheet" href="tree.css">
|
||||||
|
|
||||||
|
<link rel="icon" type="image/png" href="/assets/favicon_32x32.png">
|
||||||
|
<title>Byte Dice - Crafting</title>
|
||||||
|
|
||||||
|
<meta name="robots" content="index, follow">
|
||||||
|
<link rel="canonical" href="https://bytedice.net/toys">
|
||||||
|
|
||||||
|
<meta name="description" content="A crafting-tree-like generation tool">
|
||||||
|
<meta name="keywords" content="Byte Dice, pixel art, procedural generation, game art, digital art, ByteDice showcase">
|
||||||
|
<meta name="author" content="Byte Dice">
|
||||||
|
|
||||||
|
<meta property="og:title" content="Byte Dice">
|
||||||
|
<meta property="og:description" content="A crafting-tree-like generation tool">
|
||||||
|
<meta property="og:url" content="https://bytedice.net/toys/crafting">
|
||||||
|
<meta property="og:type" content="website">
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<div id="mainDiv">
|
||||||
|
<img src="/assets/backgrounds/starNoise_512x512.png" draggable="false" id="starNoise">
|
||||||
|
<canvas id="starfallCanvas"></canvas>
|
||||||
|
|
||||||
|
<div id="treeStaticContainer">
|
||||||
|
<div id="treeScaleContainer">
|
||||||
|
<div id="trees"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="tomlInputContainer" id="tomlInputContainer">
|
||||||
|
<div class="tomlInputGrid">
|
||||||
|
<label><a onclick="slideTransition('https://git.bytedice.net/ByteDice/ByteDiceWeb/src/branch/main/toys/crafting/DOCS.md', event)">Tutorial / Docs</a></label>
|
||||||
|
<textarea id="tomlInput" name="tomlInput" placeholder=
|
||||||
|
"[io]
|
||||||
|
inputs = []
|
||||||
|
products = []
|
||||||
|
|
||||||
|
[recipes]
|
||||||
|
# ..."
|
||||||
|
></textarea>
|
||||||
|
<button id="generateButton">Generate Tree</button>
|
||||||
|
</div>
|
||||||
|
<button id="collapseButton"><</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="navBarContainer" data-alt="byte"></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="loadingScreen"></div>
|
||||||
|
|
||||||
|
<!-- global -->
|
||||||
|
<script src="/global/globalScript.js"></script>
|
||||||
|
<script src="/global/pxDensity.js"></script>
|
||||||
|
<script src="/global/loadingScreen.js"></script>
|
||||||
|
<script src="/global/navBar.js"></script>
|
||||||
|
<script src="/global/screenTransition.js"></script>
|
||||||
|
|
||||||
|
<!-- local -->
|
||||||
|
<script src="script.js"></script>
|
||||||
|
<script type="module" src="tree_generator.js"></script>
|
||||||
|
<script src="/stars.js"></script>
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,89 @@
|
|||||||
|
CURRENT_PAGE = PAGES.CRAFTING
|
||||||
|
|
||||||
|
let tomlInputContainer = document.getElementById("tomlInputContainer")
|
||||||
|
let collapseButton = document.getElementById("collapseButton")
|
||||||
|
let treeStatic = document.getElementById("treeStaticContainer")
|
||||||
|
let treeScale = document.getElementById("treeScaleContainer")
|
||||||
|
|
||||||
|
|
||||||
|
let mouseX = 0
|
||||||
|
let mouseY = 0
|
||||||
|
let dragX = 0
|
||||||
|
let dragY = 0
|
||||||
|
let dragOffsetX = 0
|
||||||
|
let dragOffsetY = 0
|
||||||
|
let isDragging = false
|
||||||
|
let scale = 1
|
||||||
|
|
||||||
|
|
||||||
|
let isCollapsed = false
|
||||||
|
|
||||||
|
|
||||||
|
function collapseInput() {
|
||||||
|
const COLLAPSE = "collapse 0.5s cubic-bezier(0, 0, 0, 1.46) forwards"
|
||||||
|
tomlInputContainer.style.animation = COLLAPSE
|
||||||
|
isCollapsed = true
|
||||||
|
collapseButton.textContent = ">"
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function unCollapseInput() {
|
||||||
|
const UN_COLLAPSE = "un-collapse 0.5s cubic-bezier(0, 0, 0, 1.46) forwards"
|
||||||
|
tomlInputContainer.style.animation = UN_COLLAPSE
|
||||||
|
isCollapsed = false
|
||||||
|
collapseButton.textContent = "<"
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
collapseButton.onclick = function() {
|
||||||
|
if (!isCollapsed) { collapseInput(); }
|
||||||
|
else { unCollapseInput(); }
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function loadingScreenCrafting() {
|
||||||
|
unCollapseInput();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function drag_trees(mouse_offset) {
|
||||||
|
dragX = (mouseX - dragOffsetX) / scale
|
||||||
|
dragY = (mouseY - dragOffsetY) / scale
|
||||||
|
|
||||||
|
trees.style.transform = `translate(${dragX}px, ${dragY}px)`
|
||||||
|
}
|
||||||
|
function dragLoop() {
|
||||||
|
if (isDragging) { drag_trees() }
|
||||||
|
|
||||||
|
requestAnimationFrame(dragLoop)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
treeStatic.addEventListener("pointerdown", function(event) {
|
||||||
|
mouseX = event.clientX
|
||||||
|
mouseY = event.clientY
|
||||||
|
dragOffsetX = mouseX - dragX * scale
|
||||||
|
dragOffsetY = mouseY - dragY * scale
|
||||||
|
isDragging = true
|
||||||
|
|
||||||
|
treeStatic.setPointerCapture(event.pointerId);
|
||||||
|
event.preventDefault()
|
||||||
|
})
|
||||||
|
|
||||||
|
document.addEventListener("pointerup", function()
|
||||||
|
{ isDragging = false })
|
||||||
|
document.addEventListener("pointercancel", function()
|
||||||
|
{ isDragging = false })
|
||||||
|
|
||||||
|
treeStatic.addEventListener("wheel", function(event) {
|
||||||
|
scale += (-event.deltaY / 500)
|
||||||
|
scale = Math.max(0.2, scale)
|
||||||
|
treeScale.style.transform = `scale(${scale})`
|
||||||
|
})
|
||||||
|
|
||||||
|
document.addEventListener("pointermove", function(event) {
|
||||||
|
mouseX = event.clientX;
|
||||||
|
mouseY = event.clientY;
|
||||||
|
})
|
||||||
|
|
||||||
|
document.addEventListener("DOMContentLoaded", dragLoop)
|
||||||
@@ -0,0 +1,94 @@
|
|||||||
|
.tomlInputContainer textarea {
|
||||||
|
font-family: "Roboto", sans-serif;
|
||||||
|
background-color: var(--navBarBtn_Inner);
|
||||||
|
box-shadow: calc(1px * var(--pxDensity)) 0px var(--navBarBtn_Border),
|
||||||
|
calc(-1px * var(--pxDensity)) 0px var(--navBarBtn_Border),
|
||||||
|
0px calc(1px * var(--pxDensity)) var(--navBarBtn_Border),
|
||||||
|
0px calc(-1px * var(--pxDensity)) var(--navBarBtn_Border);
|
||||||
|
|
||||||
|
padding: calc(1px * var(--pxDensity));
|
||||||
|
border: none;
|
||||||
|
font-size: min(5vmin, 1.2vmax, 0.9rem);
|
||||||
|
margin: calc(3px * var(--pxDensity)) 0% 0% 0%;
|
||||||
|
box-sizing: border-box;
|
||||||
|
width: calc(60px * var(--pxDensity));
|
||||||
|
/*height: calc(100px * var(--pxDensity));*/
|
||||||
|
height: 45vh;
|
||||||
|
color: inherit;
|
||||||
|
display: inline-block;
|
||||||
|
text-align: left;
|
||||||
|
resize: none
|
||||||
|
}
|
||||||
|
.tomlInputContainer textarea:hover {
|
||||||
|
filter: brightness(1.35);
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.tomlInputContainer label {
|
||||||
|
background-color: var(--navBarBtn_Inner);
|
||||||
|
box-shadow: calc(1px * var(--pxDensity)) 0px var(--navBarBtn_Border),
|
||||||
|
calc(-1px * var(--pxDensity)) 0px var(--navBarBtn_Border),
|
||||||
|
0px calc(1px * var(--pxDensity)) var(--navBarBtn_Border),
|
||||||
|
0px calc(-1px * var(--pxDensity)) var(--navBarBtn_Border);
|
||||||
|
|
||||||
|
padding: calc(1px * var(--pxDensity));
|
||||||
|
box-sizing: border-box;
|
||||||
|
width: round(auto, var(--pxDensity));
|
||||||
|
height: calc(6px * var(--pxDensity));
|
||||||
|
font-weight: 800;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.tomlInputGrid {
|
||||||
|
display: grid;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.tomlInputContainer {
|
||||||
|
position: relative;
|
||||||
|
display: inline-flex;
|
||||||
|
margin: 0% auto 0% auto;
|
||||||
|
width: max-content;
|
||||||
|
max-height: 70vh;
|
||||||
|
padding: calc(2px * var(--pxDensity));
|
||||||
|
max-width: min(600px, 100vw, 100vw);
|
||||||
|
font-size: min(1.5rem, calc(4px * var(--pxDensity)));
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#generateButton {
|
||||||
|
width: 100%;
|
||||||
|
height: calc(6px * var(--pxDensity));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#collapseButton {
|
||||||
|
margin: 0% calc(3px * var(--pxDensity));
|
||||||
|
height: calc(6px * var(--pxDensity));
|
||||||
|
width: calc(6px * var(--pxDensity));
|
||||||
|
aspect-ratio: 1 / 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@keyframes collapse {
|
||||||
|
0% { transform: translateX(0%); }
|
||||||
|
100% { transform: translateX(calc(-63px * var(--pxDensity))); }
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes un-collapse {
|
||||||
|
0% { transform: translateX(calc(-63px * var(--pxDensity))); }
|
||||||
|
100% { transform: translateX(0%); }
|
||||||
|
}
|
||||||
|
|
||||||
|
a {
|
||||||
|
cursor: pointer;
|
||||||
|
color: #2222FF;
|
||||||
|
text-decoration: underline;
|
||||||
|
font-weight: normal;
|
||||||
|
}
|
||||||
|
a:visited {
|
||||||
|
color: #551A8B;
|
||||||
|
}
|
||||||
@@ -0,0 +1,85 @@
|
|||||||
|
#trees {
|
||||||
|
position: absolute;
|
||||||
|
left: 0%;
|
||||||
|
top: 0%;
|
||||||
|
width: max-content;
|
||||||
|
height: max-content;
|
||||||
|
padding: calc(16px * var(--pxDensity)) calc(2px * var(--pxDensity));
|
||||||
|
overflow: visible;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: calc(5px * var(--pxDensity));
|
||||||
|
user-select: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tree {
|
||||||
|
display: inline-flex;
|
||||||
|
justify-content: center;
|
||||||
|
width: min-content;
|
||||||
|
height: min-content;
|
||||||
|
padding: calc(3px * var(--pxDensity));
|
||||||
|
background-color: rgba(0.1, 0.1, 0.1, 0.1);
|
||||||
|
user-select: none;
|
||||||
|
}
|
||||||
|
.item {
|
||||||
|
position: relative;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
overflow: visible;
|
||||||
|
margin-bottom: calc(3px * var(--pxDensity));
|
||||||
|
user-select: none;
|
||||||
|
}
|
||||||
|
.siblings {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
gap: calc(3px * var(--pxDensity));
|
||||||
|
align-items: flex-end;
|
||||||
|
background-color: rgba(0.1, 0.1, 0.1, 0.1);
|
||||||
|
padding: calc(2px * var(--pxDensity));
|
||||||
|
margin-bottom: calc(3px * var(--pxDensity));
|
||||||
|
user-select: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.item p { /* from .encased */
|
||||||
|
display: inline-block;
|
||||||
|
background-color: var(--navBarBtn_Inner);
|
||||||
|
box-shadow: calc(1px * var(--pxDensity)) 0px var(--navBarBtn_Border),
|
||||||
|
calc(-1px * var(--pxDensity)) 0px var(--navBarBtn_Border),
|
||||||
|
0px calc(1px * var(--pxDensity)) var(--navBarBtn_Border),
|
||||||
|
0px calc(-1px * var(--pxDensity)) var(--navBarBtn_Border);
|
||||||
|
|
||||||
|
padding: calc(1px * var(--pxDensity));
|
||||||
|
width: round(auto, var(--pxDensity));
|
||||||
|
height: calc(3px * var(--pxDensity));
|
||||||
|
border: none;
|
||||||
|
color: #ffffff;
|
||||||
|
font-size: min(5vmin, 1.2vmax, 0.9rem);
|
||||||
|
min-width: calc(12px * var(--pxDensity));
|
||||||
|
text-align: center;
|
||||||
|
margin: 0;
|
||||||
|
user-select: none;
|
||||||
|
}
|
||||||
|
.item .blue {
|
||||||
|
background-color: var(--navBar_Inner);
|
||||||
|
box-shadow: calc(1px * var(--pxDensity)) 0px var(--navBar_Border),
|
||||||
|
calc(-1px * var(--pxDensity)) 0px var(--navBar_Border),
|
||||||
|
0px calc(1px * var(--pxDensity)) var(--navBar_Border),
|
||||||
|
0px calc(-1px * var(--pxDensity)) var(--navBar_Border);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#treeStaticContainer {
|
||||||
|
position: fixed;
|
||||||
|
top: 0%;
|
||||||
|
left: 0%;
|
||||||
|
width: 100vw;
|
||||||
|
height: 100vh;
|
||||||
|
display: inline-block;
|
||||||
|
cursor: grab;
|
||||||
|
}
|
||||||
|
|
||||||
|
#treeScaleContainer {
|
||||||
|
width: 100vw;
|
||||||
|
height: 100vh;
|
||||||
|
}
|
||||||
@@ -0,0 +1,120 @@
|
|||||||
|
import { parse } from "https://esm.sh/smol-toml"
|
||||||
|
|
||||||
|
let tomlInput = document.getElementById("tomlInput")
|
||||||
|
let genButton = document.getElementById("generateButton")
|
||||||
|
let trees = document.getElementById("trees")
|
||||||
|
|
||||||
|
let recipes = {}
|
||||||
|
let startItems = []
|
||||||
|
let products = []
|
||||||
|
|
||||||
|
|
||||||
|
genButton.onclick = getDataFromInput
|
||||||
|
|
||||||
|
|
||||||
|
function getDataFromInput() {
|
||||||
|
let input = tomlInput.value
|
||||||
|
try {
|
||||||
|
let json = parse(input)
|
||||||
|
|
||||||
|
startItems = json.io.inputs
|
||||||
|
products = json.io.products
|
||||||
|
recipes = json.recipes
|
||||||
|
} catch (e) { alert(e) }
|
||||||
|
|
||||||
|
clearTrees()
|
||||||
|
|
||||||
|
for (let product of products) {
|
||||||
|
console.log("generating: \"" + product + "\"")
|
||||||
|
genTreeForProduct(product)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function genTreeForProduct(product) {
|
||||||
|
let treeData = RecursivelyGetRecipes(product)
|
||||||
|
|
||||||
|
let treeNode = document.createElement("div")
|
||||||
|
treeNode.className = "tree"
|
||||||
|
|
||||||
|
makeTreeHTML(treeData, treeNode, true)
|
||||||
|
|
||||||
|
trees.appendChild(treeNode)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function RecursivelyGetRecipes(product) {
|
||||||
|
let recipe = recipes[product]
|
||||||
|
|
||||||
|
if (recipe == undefined) {
|
||||||
|
alert("ERROR: Recipe not found for \"" + product + "\"")
|
||||||
|
return [product]
|
||||||
|
}
|
||||||
|
|
||||||
|
let children = []
|
||||||
|
|
||||||
|
for (let item of recipe) {
|
||||||
|
if (startItems.includes(item)) { children.push(item) }
|
||||||
|
else { children.push(...RecursivelyGetRecipes(item)) }
|
||||||
|
}
|
||||||
|
|
||||||
|
return [product, children]
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function prettyTreeString(tree, indent=0) {
|
||||||
|
let string = ""
|
||||||
|
|
||||||
|
for (let item of tree) {
|
||||||
|
if (typeof item == "string")
|
||||||
|
{ string += "| ".repeat(indent) + item + "\n" }
|
||||||
|
else { string += prettyTreeString(item, indent + 1) }
|
||||||
|
}
|
||||||
|
|
||||||
|
return string
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function clearTrees() {
|
||||||
|
while (trees.firstChild)
|
||||||
|
{ trees.firstChild.remove() }
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function makeTreeHTML(tree, parentNode, blue = false) {
|
||||||
|
let previouslyAdded = undefined
|
||||||
|
|
||||||
|
let stringCount = tree.filter((x) => typeof(x) == "string").length
|
||||||
|
let arrCount = tree.length - stringCount
|
||||||
|
|
||||||
|
// add .siblings container if theres more than 1 .item
|
||||||
|
if (stringCount > 1) {
|
||||||
|
let s = document.createElement("div")
|
||||||
|
s.className = "siblings"
|
||||||
|
parentNode.prepend(s)
|
||||||
|
parentNode = s
|
||||||
|
}
|
||||||
|
if (arrCount == 0) { blue = true }
|
||||||
|
|
||||||
|
for (let i = 0; i < tree.length; i++) {
|
||||||
|
if (typeof(tree[i]) == "string") {
|
||||||
|
if (startItems.includes(tree[i])) { blue = true }
|
||||||
|
let n = makeItemNode(tree[i], blue)
|
||||||
|
parentNode.prepend(n)
|
||||||
|
previouslyAdded = n
|
||||||
|
}
|
||||||
|
else { makeTreeHTML(tree[i], previouslyAdded, false) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function makeItemNode(name, blue = false) {
|
||||||
|
let node = document.createElement("div")
|
||||||
|
node.className = "item"
|
||||||
|
let p = document.createElement("p")
|
||||||
|
if (blue) { p.className = "blue" }
|
||||||
|
p.textContent = name
|
||||||
|
node.appendChild(p)
|
||||||
|
|
||||||
|
return node
|
||||||
|
}
|
||||||
@@ -0,0 +1,84 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, height=device-height, initial-scale=1.0, user-scalable=no, interactive-widget=resize-content">
|
||||||
|
|
||||||
|
<link rel="stylesheet" href="/global/navBar.css">
|
||||||
|
<link rel="stylesheet" href="/global/globalStyle.css">
|
||||||
|
<link rel="stylesheet" href="/global/loadingScreen.css">
|
||||||
|
<link rel="stylesheet" href="style.css">
|
||||||
|
|
||||||
|
<link rel="icon" type="image/png" href="/assets/favicon_32x32.png">
|
||||||
|
<title>Byte Dice - Toys</title>
|
||||||
|
|
||||||
|
<meta name="robots" content="index, follow">
|
||||||
|
<link rel="canonical" href="https://bytedice.net/toys">
|
||||||
|
|
||||||
|
<meta name="description" content="A list of fun web-based tools made by Byte Dice.">
|
||||||
|
<meta name="keywords" content="Byte Dice, pixel art, procedural generation, game art, digital art, ByteDice showcase">
|
||||||
|
<meta name="author" content="Byte Dice">
|
||||||
|
|
||||||
|
<meta property="og:title" content="Byte Dice">
|
||||||
|
<meta property="og:description" content="A list of fun web-based tools made by Byte Dice.">
|
||||||
|
<meta property="og:url" content="https://bytedice.net/toys">
|
||||||
|
<meta property="og:type" content="website">
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<div id="mainDiv">
|
||||||
|
<img src="/assets/backgrounds/starNoise_512x512.png" draggable="false" id="starNoise">
|
||||||
|
<canvas id="starfallCanvas"></canvas>
|
||||||
|
|
||||||
|
<div class="toyUl">
|
||||||
|
<div class="toyLi">
|
||||||
|
<button onclick="slideTransition('/toys/license', event)" class="toyContainer borderedRect">
|
||||||
|
<img class="toyImg" src="/assets/boykisserLicense.png">
|
||||||
|
<div class="toyTextContainer">
|
||||||
|
<h1>Boykisser License Generator</h1>
|
||||||
|
<p class="desc">Automatically generates a "Boykissser license" for you. Free of charge and very silly.</p>
|
||||||
|
</div>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div class="toyLi">
|
||||||
|
<button onclick="slideTransition('/toys/loading', event)" class="toyContainer borderedRect">
|
||||||
|
<img class="toyImg" src="/assets/byte_dice_fancy_64x64.png">
|
||||||
|
<div class="toyTextContainer">
|
||||||
|
<h1>Loading Screen</h1>
|
||||||
|
<p class="desc">The same loading screen as the website uses but less clutter and infinite. (Loading the rest of the description...)</p>
|
||||||
|
</div>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div class="toyLi">
|
||||||
|
<button onclick="slideTransition('/toys/crafting', event)" class="toyContainer borderedRect">
|
||||||
|
<img class="toyImg" src="/assets/foobar_tree.png">
|
||||||
|
<div class="toyTextContainer">
|
||||||
|
<h1>Crafting Tree Generator</h1>
|
||||||
|
<p class="desc">A crafting-tree-like generation tool that generates descending trees from a TOML file</p>
|
||||||
|
</div>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- To scroll further, width is needed for some reason -->
|
||||||
|
<div style="margin-top: 50vh; width: 1px"></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="navBarContainer" data-alt="byte"></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="loadingScreen"></div>
|
||||||
|
|
||||||
|
<!-- global -->
|
||||||
|
<script src="/global/globalScript.js"></script>
|
||||||
|
<script src="/global/pxDensity.js"></script>
|
||||||
|
<script src="/global/loadingScreen.js"></script>
|
||||||
|
<script src="/global/navBar.js"></script>
|
||||||
|
<script src="/global/screenTransition.js"></script>
|
||||||
|
|
||||||
|
<!-- local -->
|
||||||
|
<script src="script.js"></script>
|
||||||
|
<script src="/stars.js"></script>
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,196 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, height=device-height, initial-scale=1.0, user-scalable=no">
|
||||||
|
|
||||||
|
<link rel="stylesheet" href="/global/navBar.css">
|
||||||
|
<link rel="stylesheet" href="/global/globalStyle.css">
|
||||||
|
<link rel="stylesheet" href="/global/loadingScreen.css">
|
||||||
|
<link rel="stylesheet" href="style.css">
|
||||||
|
<link rel="stylesheet" href="license.css">
|
||||||
|
<link rel="stylesheet" href="options.css">
|
||||||
|
|
||||||
|
<!-- fonts -->
|
||||||
|
<style>
|
||||||
|
@import url("https://fonts.googleapis.com/css2?family=Yellowtail&display=swap");
|
||||||
|
@import url("https://fonts.googleapis.com/css2?family=Comic+Neue:wght@400&display=swap");
|
||||||
|
@import url('https://fonts.googleapis.com/css2?family=Comic+Neue:wght@700&display=swap');
|
||||||
|
</style>
|
||||||
|
|
||||||
|
|
||||||
|
<link rel="icon" type="image/png" href="/assets/favicon_32x32.png">
|
||||||
|
<title>Byte Dice - Boykisser license</title>
|
||||||
|
|
||||||
|
<meta name="robots" content="index, follow">
|
||||||
|
<link rel="canonical" href="https://bytedice.net/toys/license">
|
||||||
|
|
||||||
|
<meta name="description" content="A 'boykisser license' generator tool. Based off the original by Chilli (link in site). It's completely free of charge and very silly.">
|
||||||
|
<meta name="keywords" content="Byte Dice, pixel art, procedural generation, game art, digital art, ByteDice showcase, Boykisser, license, generator, silly">
|
||||||
|
<meta name="author" content="Byte Dice">
|
||||||
|
|
||||||
|
<meta property="og:title" content="Byte Dice - Boykisser license">
|
||||||
|
<meta property="og:description" content="A 'boykisser license' generator tool. Based off the original by Chilli (link in site). It's completely free of charge and very silly.">
|
||||||
|
<meta property="og:url" content="https://bytedice.net/toys/license">
|
||||||
|
<meta property="og:type" content="website">
|
||||||
|
<meta property="og:image" content="/assets/boykisserLicense.png">
|
||||||
|
<meta property="og:image:width" content="600">
|
||||||
|
<meta property="og:image:height" content="399">
|
||||||
|
|
||||||
|
<!-- No twitter embeds except for card. Elon can go and fucking return it to its previous owners. -->
|
||||||
|
<meta name="twitter:card" content="summary_large_image">
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<div id="mainDiv">
|
||||||
|
<img src="/assets/backgrounds/starNoise_512x512.png" draggable="false" id="starNoise">
|
||||||
|
<canvas id="starfallCanvas"></canvas>
|
||||||
|
|
||||||
|
<div class="allContainer">
|
||||||
|
<div id="optionsContainer" class="borderedRect">
|
||||||
|
<h1 class="optionsCredits h1Top">
|
||||||
|
Based on the <a onclick="slideTransition('https://picrew.me/en/image_maker/2310778', event)">original</a> by Chilli!<br>
|
||||||
|
Boykisser picture presets by <a onclick="slideTransition('https://www.deviantart.com/greenbandit2004', event)">greenbandit2004</a>.<br>
|
||||||
|
<br>
|
||||||
|
For takedown requests, please
|
||||||
|
<a onclick="slideTransition('https://github.com/ByteDice/ByteDiceWeb/issues', event)">submit an issue</a> with the "Copyright" label in the Git repository for this website.
|
||||||
|
</h1><br>
|
||||||
|
|
||||||
|
<h1>Basic</h1><br>
|
||||||
|
<form class="licenseForm licenseFormContainer">
|
||||||
|
<label for="titleInput" >Title</label ><input type="text" id="titleInput" name="titleInput" value="Boykisser License">
|
||||||
|
<label for="authorInput" >User</label ><input type="text" id="authorInput" name="authorInput" value="Byte Dice">
|
||||||
|
<label for="createdInput" >Issued</label ><input type="text" id="createdInput" name="createdInput" value="2025-01-01">
|
||||||
|
<label for="expireInput" >Expires</label ><input type="text" id="expireInput" name="expireInput" value="NEVER!!!">
|
||||||
|
<label for="identityInput">Identity</label ><input type="text" id="identityInput" name="identityInput" value="any pronouns">
|
||||||
|
|
||||||
|
<label for="sillyInput">Silliness</label><div style="display: inline-flex">
|
||||||
|
<div id="sillyNumberValue">03</div><div class="sliderDiv">
|
||||||
|
<input type="range" min="0" max="10" value="3" class="slider" id="sillyInput" name="sillyInput"></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<label for="signatureInput">Signature</label><input type="text" id="signatureInput" name="signatureInput" value="Byte Dice">
|
||||||
|
|
||||||
|
<label for="picInput">Picture</label><div style="display: inline-flex;">
|
||||||
|
<select id="picInput" name="picInput">
|
||||||
|
<option value="boykisser" >Boykisser</option>
|
||||||
|
<option value="bi-kisser" >Bi-kisser</option>
|
||||||
|
<option value="ace-kisser" >Ace-kisser</option>
|
||||||
|
<option value="pan-kisser" >Pan-kisser</option>
|
||||||
|
<option value="trans-kisser" >Trans-kisser</option>
|
||||||
|
<option value="bytedice" >Byte Dice</option>
|
||||||
|
<option value="custom" id="customPic">Custom (upload)</option>
|
||||||
|
</select>
|
||||||
|
<label for="picUpload" id="picUploadLabel"><img src="/assets/icons/upload.svg" id="bgUploadIcon" draggable="false"></label>
|
||||||
|
<input type="file" id="picUpload" name="picUpload" accept=".png,.jpg,.jpeg,.webp">
|
||||||
|
</div>
|
||||||
|
</form><br>
|
||||||
|
|
||||||
|
<h1>Colors</h1><br>
|
||||||
|
<form class="licenseForm licenseFormContainer">
|
||||||
|
<label for="bgTypeInput" name="bgTypeInput">BG Type</label><div style="display: inline-flex;">
|
||||||
|
<select id="bgTypeInput", name="bgTypeInput">
|
||||||
|
<option value="color">Solid Color</option>
|
||||||
|
<option value="image" id="customBg">Image (upload)</option>
|
||||||
|
</select>
|
||||||
|
<label for="bgUpload" id="bgUploadLabel"><img src="/assets/icons/upload.svg" id="bgUploadIcon" draggable="false"></label>
|
||||||
|
<input type="file" id="bgUpload" name="bgUpload" accept=".png,.jpg,.jpeg,.webp">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<label for="bgColInput" >Background</label><input type="color" id="bgColInput" name="bgColInput" value="#FFFFFF">
|
||||||
|
<label for="borderColInput">Borders</label ><input type="color" id="borderColInput" name="borderColInput" value="#000000">
|
||||||
|
<label for="textColInput" >Text</label ><input type="color" id="textColInput" name="textColInput" value="#000000">
|
||||||
|
</form><br>
|
||||||
|
|
||||||
|
<!--<h1>Advanced</h1><br>
|
||||||
|
<form class="licenseForm">
|
||||||
|
<label for="soon2">Soon...</label><input type="text" name="soon2" value="Does nothing"><br>
|
||||||
|
<\!--
|
||||||
|
license border radius
|
||||||
|
pic border radius
|
||||||
|
title gradient (2 colors + direction) or flag
|
||||||
|
license bg-image/bg-gradient
|
||||||
|
-\->
|
||||||
|
</form>-->
|
||||||
|
|
||||||
|
<div style="display: flex; gap: calc(1px * var(--pxDensity))">
|
||||||
|
<button id="generateButton" class="generateButton navBtn" onclick="generateLicense()">
|
||||||
|
Download
|
||||||
|
</button>
|
||||||
|
<button class="generateButton navBtn" onclick="exportData()">
|
||||||
|
Copy Data
|
||||||
|
</button>
|
||||||
|
<button class="generateButton navBtn" onclick="importData()">
|
||||||
|
Import
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div style="display: inline-flex; justify-content: center;">
|
||||||
|
<div id="licenseAnchor" style="display: inline-block; transform-origin: center;">
|
||||||
|
<div id="licenseContainer" class="blackBorderRect sans">
|
||||||
|
<h3 id="titleValue">Boykisser license</h3>
|
||||||
|
<div style="width: max-content;">
|
||||||
|
<div id="picContainer" class="blackBorderRect">
|
||||||
|
<img id="picValue" src="" draggable="false">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="signatureContainer" class="yellowTail">
|
||||||
|
<p id="signatureValue">Byte Dice</p>
|
||||||
|
<p id="signHere">Sign here</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<div id="detailsContainer">
|
||||||
|
<h2>User </h2><h2 id="authorValue" >foo</h2>
|
||||||
|
<h2>Issued </h2><h2 id="createdValue">bar</h2>
|
||||||
|
<h2>Expires </h2><h2 id="expireValue" >baz</h2>
|
||||||
|
<h2>Identity </h2><h2 id="genderValue" >cux</h2>
|
||||||
|
<h2>Silliness</h2>
|
||||||
|
<div class="sillyBar">
|
||||||
|
<div id="sillyValue0" class="sillyValue on"></div>
|
||||||
|
<div id="sillyValue1" class="sillyValue on"></div>
|
||||||
|
<div id="sillyValue2" class="sillyValue on"></div>
|
||||||
|
<div id="sillyValue3" class="sillyValue off"></div>
|
||||||
|
<div id="sillyValue4" class="sillyValue off"></div>
|
||||||
|
<div id="sillyValue5" class="sillyValue off"></div>
|
||||||
|
<div id="sillyValue6" class="sillyValue off"></div>
|
||||||
|
<div id="sillyValue7" class="sillyValue off"></div>
|
||||||
|
<div id="sillyValue8" class="sillyValue off"></div>
|
||||||
|
<div id="sillyValue9" class="sillyValue off"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<p id="credits">Original by Chilli<br>Remade by Byte Dice at bytedice.net/toys</p>
|
||||||
|
<!-- NR-ID = Non-Reversible IDentification -->
|
||||||
|
<p id="hashID">NR-ID: 8f434346648f6b96</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="navBarContainer" data-alt="byte"></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="loadingScreen"></div>
|
||||||
|
|
||||||
|
<!-- libs -->
|
||||||
|
<script src="https://cdn.jsdelivr.net/npm/html2canvas@1.4.1/dist/html2canvas.min.js"></script>
|
||||||
|
|
||||||
|
<!-- global -->
|
||||||
|
<script src="/global/globalScript.js"></script>
|
||||||
|
<script src="/global/pxDensity.js"></script>
|
||||||
|
<script src="/global/loadingScreen.js"></script>
|
||||||
|
<script src="/global/navBar.js"></script>
|
||||||
|
<script src="/global/screenTransition.js"></script>
|
||||||
|
|
||||||
|
<!-- local -->
|
||||||
|
<script src="script.js"></script>
|
||||||
|
<script src="license.js"></script>
|
||||||
|
<script src="input.js"></script>
|
||||||
|
<script src="/stars.js"></script>
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,220 @@
|
|||||||
|
let jsonData = {
|
||||||
|
version: 1,
|
||||||
|
title: "Boykisser License",
|
||||||
|
user: "Byte Dice",
|
||||||
|
issued: "2025-01-01",
|
||||||
|
expires: "NEVER!!!",
|
||||||
|
identity: "any pronouns",
|
||||||
|
signature: "Byte Dice",
|
||||||
|
silly: 3,
|
||||||
|
img: {
|
||||||
|
preset: "boykisser",
|
||||||
|
file: ""
|
||||||
|
},
|
||||||
|
bg: {
|
||||||
|
type: "color",
|
||||||
|
color: "#FFFFFF",
|
||||||
|
file: ""
|
||||||
|
},
|
||||||
|
border: "#000000",
|
||||||
|
text: "#000000"
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
const customPicOption = document.getElementById("customPic")
|
||||||
|
const customBgOption = document.getElementById("customBg")
|
||||||
|
|
||||||
|
|
||||||
|
// funny size sorting
|
||||||
|
const VMAP = {
|
||||||
|
bg: "bgTypeInput",
|
||||||
|
img: "picInput",
|
||||||
|
user: "authorInput",
|
||||||
|
text: "textColInput",
|
||||||
|
title: "titleInput",
|
||||||
|
silly: "sillyInput",
|
||||||
|
bgCol: "bgColInput",
|
||||||
|
issued: "createdInput",
|
||||||
|
border: "borderColInput",
|
||||||
|
expires: "expireInput",
|
||||||
|
identity: "identityInput",
|
||||||
|
bgUpload: "bgUpload",
|
||||||
|
signature: "signatureInput",
|
||||||
|
imgUpload: "picUpload",
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
async function prepInputs() {
|
||||||
|
function getE(e) { return document.getElementById(e) }
|
||||||
|
|
||||||
|
getE(VMAP.bg) .oninput = async function() { jsonData.bg.type = this.value; await updLicense() }
|
||||||
|
getE(VMAP.img) .oninput = async function() { jsonData.img.preset = this.value; await updLicense() }
|
||||||
|
getE(VMAP.user) .oninput = async function() { jsonData.user = this.value; await updLicense() }
|
||||||
|
getE(VMAP.text) .oninput = async function() { jsonData.text = this.value; await updLicense() }
|
||||||
|
getE(VMAP.title) .oninput = async function() { jsonData.title = this.value; await updLicense() }
|
||||||
|
getE(VMAP.silly) .oninput = async function() { jsonData.silly = parseInt(this.value); await updLicense() }
|
||||||
|
getE(VMAP.bgCol) .oninput = async function() { jsonData.bg.color = this.value; await updLicense() }
|
||||||
|
getE(VMAP.issued) .oninput = async function() { jsonData.issued = this.value; await updLicense() }
|
||||||
|
getE(VMAP.border) .oninput = async function() { jsonData.border = this.value; await updLicense() }
|
||||||
|
getE(VMAP.expires) .oninput = async function() { jsonData.expires = this.value; await updLicense() }
|
||||||
|
getE(VMAP.identity) .oninput = async function() { jsonData.identity = this.value; await updLicense() }
|
||||||
|
getE(VMAP.signature).oninput = async function() { jsonData.signature = this.value; await updLicense() }
|
||||||
|
|
||||||
|
getE(VMAP.imgUpload).onchange = async function() {
|
||||||
|
if (this.files.length == 0) { return; }
|
||||||
|
let f = this.files[0]
|
||||||
|
jsonData.img.file = URL.createObjectURL(f)
|
||||||
|
customPicOption.innerHTML = `Custom (${f.name})`
|
||||||
|
|
||||||
|
await updLicense()
|
||||||
|
}
|
||||||
|
|
||||||
|
getE(VMAP.bgUpload).onchange = async function() {
|
||||||
|
if (this.files.length == 0) { return; }
|
||||||
|
let f = this.files[0]
|
||||||
|
jsonData.bg.file = URL.createObjectURL(f)
|
||||||
|
customBgOption.innerHTML = `Image (${f.name})`
|
||||||
|
|
||||||
|
await updLicense()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
async function updLicense() { await license.updateFromJSON(jsonData) }
|
||||||
|
|
||||||
|
|
||||||
|
function importData() {
|
||||||
|
let data = prompt("Input your license data here.")
|
||||||
|
importFromB64(data)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
async function importFromB64(b64) {
|
||||||
|
if (b64.startsWith("B8D6:")) { b64 = b64.slice(5) }
|
||||||
|
|
||||||
|
try {
|
||||||
|
let str = atob(b64)
|
||||||
|
data = JSON.parse(str)
|
||||||
|
|
||||||
|
let isValid = validateJSON(data)
|
||||||
|
|
||||||
|
if (isValid != true)
|
||||||
|
{ alert("Invalid data string imported (Invalid fields). Full results:\n" + isValid.join("\n")); return }
|
||||||
|
|
||||||
|
jsonData = deepOverride(jsonData, data)
|
||||||
|
|
||||||
|
await updLicense()
|
||||||
|
makeOptionsTheSameAsJSON()
|
||||||
|
}
|
||||||
|
catch (err) { alert("Invalid data string imported. Full results:\n" + err); return }
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function deepOverride(target, source) {
|
||||||
|
for (const key in source) {
|
||||||
|
if (
|
||||||
|
typeof source[key] === "object" &&
|
||||||
|
source[key] !== null &&
|
||||||
|
!Array.isArray(source[key])
|
||||||
|
) { target[key] = deepOverride(target[key] ?? {}, source[key]) }
|
||||||
|
else { target[key] = source[key] }
|
||||||
|
}
|
||||||
|
|
||||||
|
return target;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function makeOptionsTheSameAsJSON() {
|
||||||
|
function getE(e) { return document.getElementById(e) }
|
||||||
|
|
||||||
|
getE(VMAP.img).value = jsonData.img.preset
|
||||||
|
getE(VMAP.user).value = jsonData.user
|
||||||
|
getE(VMAP.text).value = jsonData.text
|
||||||
|
getE(VMAP.title).value = jsonData.title
|
||||||
|
getE(VMAP.silly).value = jsonData.silly
|
||||||
|
getE(VMAP.bgCol).value = jsonData.bg.color
|
||||||
|
getE(VMAP.issued).value = jsonData.issued
|
||||||
|
getE(VMAP.border).value = jsonData.border
|
||||||
|
getE(VMAP.expires).value = jsonData.expires
|
||||||
|
getE(VMAP.identity).value = jsonData.identity
|
||||||
|
getE(VMAP.signature).value = jsonData.signature
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// I wanna keep compatibility with older versions
|
||||||
|
function validateJSON(obj) {
|
||||||
|
let is_valid = (
|
||||||
|
typeof obj === "object" &&
|
||||||
|
obj !== null &&
|
||||||
|
typeof obj.bg === "object" &&
|
||||||
|
typeof obj.user === "string" &&
|
||||||
|
typeof obj.title === "string" &&
|
||||||
|
typeof obj.silly === "number" &&
|
||||||
|
typeof obj.issued === "string" &&
|
||||||
|
typeof obj.expires === "string" &&
|
||||||
|
typeof obj.version === "number" &&
|
||||||
|
typeof obj.identity === "string" &&
|
||||||
|
typeof obj.signature === "string" &&
|
||||||
|
typeof obj.bg.color === "string" &&
|
||||||
|
typeof obj.border === "string" &&
|
||||||
|
typeof obj.text === "string"
|
||||||
|
)
|
||||||
|
|
||||||
|
if (is_valid) { return true }
|
||||||
|
|
||||||
|
let errs = []
|
||||||
|
if (typeof obj !== "object") { errs.push("json != object") }
|
||||||
|
if (obj === null) { errs.push("json == null") }
|
||||||
|
|
||||||
|
if (typeof obj.bg !== "object") { errs.push("json.bg != object") }
|
||||||
|
if (typeof obj.user !== "string") { errs.push("json.user != string") }
|
||||||
|
if (typeof obj.text !== "string") { errs.push("json.text != string") }
|
||||||
|
if (typeof obj.title !== "string") { errs.push("json.title != string") }
|
||||||
|
if (typeof obj.issued !== "string") { errs.push("json.issued != string") }
|
||||||
|
if (typeof obj.border !== "string") { errs.push("json.border != string") }
|
||||||
|
if (typeof obj.expires !== "string") { errs.push("json.expires != string") }
|
||||||
|
if (typeof obj.identity !== "string") { errs.push("json.identity != string") }
|
||||||
|
if (typeof obj.bg.color !== "string") { errs.push("json.bg.color != string") }
|
||||||
|
if (typeof obj.signature !== "string") { errs.push("json.signature != string") }
|
||||||
|
|
||||||
|
if (typeof obj.version !== "number") { errs.push("json.version != number") }
|
||||||
|
if (typeof obj.silly !== "number") { errs.push("json.silly != number") }
|
||||||
|
|
||||||
|
return errs
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function exportData() {
|
||||||
|
let strippedData = jsonData
|
||||||
|
delete strippedData.img.file
|
||||||
|
delete strippedData.bg.file
|
||||||
|
let str = JSON.stringify(strippedData)
|
||||||
|
let b64 = btoa(str)
|
||||||
|
let watermarked = "B8D6:" + b64
|
||||||
|
navigator.clipboard.writeText(watermarked)
|
||||||
|
|
||||||
|
alert("Data copied to clipboard! Keep in mind that images do not get exported.\n\nIf it didn't work, you can manually copy it below:\n\n" + watermarked)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function generateLicense() {
|
||||||
|
const element = document.getElementById("licenseContainer")
|
||||||
|
let dupe = element.cloneNode(true)
|
||||||
|
dupe.style.transform = "initial"
|
||||||
|
dupe.style.left = "300vw"
|
||||||
|
dupe.style.position = "absolute"
|
||||||
|
dupe.style.margin = "initial"
|
||||||
|
|
||||||
|
document.body.appendChild(dupe)
|
||||||
|
|
||||||
|
html2canvas(dupe).then(canvas => {
|
||||||
|
const link = document.createElement("a");
|
||||||
|
link.download = "boykisserLicense.png";
|
||||||
|
link.href = canvas.toDataURL("image/png");
|
||||||
|
link.click();
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log(dupe.getBoundingClientRect())
|
||||||
|
|
||||||
|
dupe.remove()
|
||||||
|
}
|
||||||
@@ -0,0 +1,127 @@
|
|||||||
|
:root {
|
||||||
|
--licenseBgCol: #FFFFFF;
|
||||||
|
--licenseBorderCol: #000000;
|
||||||
|
--licenseTextCol: #000000;
|
||||||
|
--licenseScribbleCol: #FF0000; /* cannot get this to work */
|
||||||
|
}
|
||||||
|
|
||||||
|
.sans {
|
||||||
|
font-family: "Comic Neue", serif;
|
||||||
|
font-weight: 400;
|
||||||
|
font-style: normal;
|
||||||
|
}
|
||||||
|
.sans-bold { /* TODO: bold not working */
|
||||||
|
font-family: "Comic Neue", serif;
|
||||||
|
font-weight: 700;
|
||||||
|
font-style: normal;
|
||||||
|
}
|
||||||
|
.yellowTail {
|
||||||
|
font-family: "Yellowtail", serif;
|
||||||
|
font-weight: 400;
|
||||||
|
font-style: normal;
|
||||||
|
}
|
||||||
|
h2 {
|
||||||
|
margin: 5px;
|
||||||
|
}
|
||||||
|
.sillyBar {
|
||||||
|
width: auto;
|
||||||
|
height: auto;
|
||||||
|
background-color: var(--licenseBorderCol);
|
||||||
|
border: 3px solid var(--licenseBorderCol);
|
||||||
|
border-radius: 8px;
|
||||||
|
align-self: center;
|
||||||
|
display: flex;
|
||||||
|
gap: 3px;
|
||||||
|
}
|
||||||
|
.sillyValue {
|
||||||
|
position: relative;
|
||||||
|
width: calc(100% / 10);
|
||||||
|
height: 100%;
|
||||||
|
aspect-ratio: 1 / 1;
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
.on {
|
||||||
|
background-image: url(/assets/scribble_32x32.png);
|
||||||
|
}
|
||||||
|
.off {
|
||||||
|
background-image: none;
|
||||||
|
background-color: var(--licenseBgCol);
|
||||||
|
}
|
||||||
|
.sillyValue:first-child {
|
||||||
|
border-radius: 5px 0px 0px 5px;
|
||||||
|
}
|
||||||
|
.sillyValue:last-child {
|
||||||
|
border-radius: 0px 5px 5px 0px;
|
||||||
|
}
|
||||||
|
#licenseContainer {
|
||||||
|
width: 600px;
|
||||||
|
height: 400px;
|
||||||
|
padding: 0px 20px 40px 20px;
|
||||||
|
font-size: 0.75rem;
|
||||||
|
box-sizing: border-box;
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(2, auto);
|
||||||
|
grid-template-rows: repeat(2, auto);
|
||||||
|
background-color: var(--licenseBgCol);
|
||||||
|
border: 10px var(--licenseBorderCol) solid;
|
||||||
|
border-radius: 32px;
|
||||||
|
color: var(--licenseTextCol);
|
||||||
|
}
|
||||||
|
#picContainer {
|
||||||
|
width: 150px;
|
||||||
|
height: 150px;
|
||||||
|
display: block;
|
||||||
|
overflow: hidden;
|
||||||
|
background-color: transparent;
|
||||||
|
border-radius: 32px;
|
||||||
|
border: 10px var(--licenseBorderCol) solid;
|
||||||
|
}
|
||||||
|
#picValue {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
object-fit: cover;
|
||||||
|
object-position: center;
|
||||||
|
}
|
||||||
|
#credits {
|
||||||
|
position: absolute;
|
||||||
|
bottom: 12px;
|
||||||
|
left: 0%;
|
||||||
|
margin: 0% 0% 0% 20px;
|
||||||
|
color: var(--licenseTextCol);
|
||||||
|
font-size: 0.6rem;
|
||||||
|
}
|
||||||
|
#signatureContainer, #DsignatureContainer {
|
||||||
|
display: inline-flex;
|
||||||
|
flex-direction: column;
|
||||||
|
margin: 5% 0% 0% 0%;
|
||||||
|
}
|
||||||
|
#signatureValue {
|
||||||
|
margin: 0% 0% -0.2rem 0%;/* margin: 0% 0% 0.2rem 0%; */
|
||||||
|
text-align: center;
|
||||||
|
font-size: 1.5rem;
|
||||||
|
}
|
||||||
|
#signHere {
|
||||||
|
border-top: var(--licenseBorderCol) solid 6px;
|
||||||
|
margin: 0%;
|
||||||
|
padding: 0% 25px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
#detailsContainer {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(2, auto);
|
||||||
|
}
|
||||||
|
#titleValue {
|
||||||
|
grid-area: 1 / 1 / 2 / 3;
|
||||||
|
font-size: 2rem;
|
||||||
|
margin: 0% auto 0% auto;
|
||||||
|
height: max-content;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
#hashID {
|
||||||
|
position: absolute;
|
||||||
|
bottom: 12px;
|
||||||
|
right: 0%;
|
||||||
|
margin: 0% 30px 0% 0%;
|
||||||
|
color: var(--licenseTextCol);
|
||||||
|
font-size: 0.6rem;
|
||||||
|
}
|
||||||
@@ -0,0 +1,91 @@
|
|||||||
|
const sillyOptNumber = document.getElementById("sillyNumberValue")
|
||||||
|
|
||||||
|
|
||||||
|
class License {
|
||||||
|
constructor() {
|
||||||
|
this.idE = undefined
|
||||||
|
this.imgE = undefined
|
||||||
|
this.userE = undefined
|
||||||
|
this.baseE = undefined // for bg
|
||||||
|
this.titleE = undefined
|
||||||
|
this.issuedE = undefined
|
||||||
|
this.expiresE = undefined
|
||||||
|
this.identityE = undefined
|
||||||
|
this.signatureE = undefined
|
||||||
|
|
||||||
|
this.sillyE = undefined
|
||||||
|
|
||||||
|
this.sillyValues = []
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
async updateFromJSON(json) {
|
||||||
|
this.userE .textContent = json.user
|
||||||
|
this.titleE .textContent = json.title
|
||||||
|
this.issuedE .textContent = json.issued
|
||||||
|
this.expiresE .textContent = json.expires
|
||||||
|
this.identityE .textContent = json.identity
|
||||||
|
this.signatureE.textContent = json.signature
|
||||||
|
|
||||||
|
this.setSillyValue(json.silly)
|
||||||
|
this.setImgValue(json.img)
|
||||||
|
|
||||||
|
if (json.bg.type == "image")
|
||||||
|
{ this.baseE.style.backgroundImage = `url("${json.bg.file}")` }
|
||||||
|
else { this.baseE.style.backgroundImage = "" }
|
||||||
|
|
||||||
|
document.documentElement.style.setProperty("--licenseBgCol", json.bg.color)
|
||||||
|
document.documentElement.style.setProperty("--licenseBorderCol", json.border)
|
||||||
|
document.documentElement.style.setProperty("--licenseTextCol", json.text)
|
||||||
|
|
||||||
|
let hash = await sha256(JSON.stringify(json))
|
||||||
|
this.idE.textContent = "NR-ID: " + hash.slice(0, 16)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
setSillyValue(value) {
|
||||||
|
sillyOptNumber.innerHTML = prependZero(value)
|
||||||
|
|
||||||
|
for (let i in this.sillyValues) {
|
||||||
|
let sillyValue = this.sillyValues[i]
|
||||||
|
|
||||||
|
let isTurnedOn = i <= value - 1
|
||||||
|
|
||||||
|
if (isTurnedOn) {
|
||||||
|
sillyValue.className = "sillyValue on"
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
sillyValue.className = "sillyValue off"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
setImgValue(json) {
|
||||||
|
if (json.preset != "custom")
|
||||||
|
{ this.imgE.src = `/assets/boykissers/${json.preset}.png` }
|
||||||
|
else { this.imgE.src = json.file }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
let licenseE = document.getElementById("licenseContainer")
|
||||||
|
let anchor = document.getElementById("licenseAnchor")
|
||||||
|
let optContainer = document.getElementById("optionsContainer")
|
||||||
|
|
||||||
|
let license = new License()
|
||||||
|
|
||||||
|
|
||||||
|
function prependZero(number) {
|
||||||
|
return number < 10 ? "0" + number : number
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function scaleDisplayLicense() {
|
||||||
|
let w = optContainer.getBoundingClientRect().width
|
||||||
|
let scaleFactor = (w - 50) / 600;
|
||||||
|
|
||||||
|
anchor.style.transform = `scale(${scaleFactor})`
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -0,0 +1,259 @@
|
|||||||
|
#optionsContainer {
|
||||||
|
display: block;
|
||||||
|
margin: calc(2px * var(--pxDensity)) auto 0% auto;
|
||||||
|
width: max-content;
|
||||||
|
padding: calc(2px * var(--pxDensity));
|
||||||
|
overflow-y: scroll;
|
||||||
|
max-width: min(600px, 100vw);
|
||||||
|
font-size: min(1.5rem, calc(4px * var(--pxDensity)));
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
.optionsCredits {
|
||||||
|
margin: 0%;
|
||||||
|
max-width: calc(100% - (4px * var(--pxDensity)));
|
||||||
|
}
|
||||||
|
.licenseFormContainer {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 40% 60%;
|
||||||
|
}
|
||||||
|
.licenseForm label {
|
||||||
|
background-color: var(--navBarBtn_Inner);
|
||||||
|
box-shadow: calc(1px * var(--pxDensity)) 0px var(--navBarBtn_Border),
|
||||||
|
calc(-1px * var(--pxDensity)) 0px var(--navBarBtn_Border),
|
||||||
|
0px calc(1px * var(--pxDensity)) var(--navBarBtn_Border),
|
||||||
|
0px calc(-1px * var(--pxDensity)) var(--navBarBtn_Border);
|
||||||
|
|
||||||
|
padding: calc(1px * var(--pxDensity));
|
||||||
|
margin: calc(3px * var(--pxDensity)) calc(3px * var(--pxDensity)) 0% 0%;
|
||||||
|
box-sizing: border-box;
|
||||||
|
width: round(auto, var(--pxDensity));
|
||||||
|
height: calc(6px * var(--pxDensity));
|
||||||
|
font-weight: 800;
|
||||||
|
}
|
||||||
|
.licenseForm input[type="text"] {
|
||||||
|
font-family: "Roboto", sans-serif;
|
||||||
|
background-color: var(--navBarBtn_Inner);
|
||||||
|
box-shadow: calc(1px * var(--pxDensity)) 0px var(--navBarBtn_Border),
|
||||||
|
calc(-1px * var(--pxDensity)) 0px var(--navBarBtn_Border),
|
||||||
|
0px calc(1px * var(--pxDensity)) var(--navBarBtn_Border),
|
||||||
|
0px calc(-1px * var(--pxDensity)) var(--navBarBtn_Border);
|
||||||
|
|
||||||
|
padding: calc(1px * var(--pxDensity));
|
||||||
|
border: none;
|
||||||
|
font-size: inherit;
|
||||||
|
margin: calc(3px * var(--pxDensity)) calc(2px * var(--pxDensity)) 0% 0%;
|
||||||
|
box-sizing: border-box;
|
||||||
|
width: round(auto, var(--pxDensity));
|
||||||
|
height: calc(6px * var(--pxDensity));
|
||||||
|
color: inherit;
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
.licenseForm input[type="text"]:hover {
|
||||||
|
filter: brightness(1.35);
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
.slider {
|
||||||
|
-webkit-appearance: none;
|
||||||
|
appearance: none;
|
||||||
|
background-color: var(--navBarBtn_Border);
|
||||||
|
box-shadow: none;
|
||||||
|
filter: none;
|
||||||
|
border: none;
|
||||||
|
font-size: inherit;
|
||||||
|
box-sizing: border-box;
|
||||||
|
width: 100%;
|
||||||
|
height: calc(1px * var(--pxDensity));
|
||||||
|
margin: 0%;
|
||||||
|
outline: none;
|
||||||
|
padding: 0%;
|
||||||
|
}
|
||||||
|
.slider:hover {
|
||||||
|
filter: brightness(1.35);
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
.slider::-moz-range-thumb {
|
||||||
|
-webkit-appearance: none;
|
||||||
|
appearance: none;
|
||||||
|
background-color: var(--navBar_Inner);
|
||||||
|
border: none;
|
||||||
|
cursor: pointer;
|
||||||
|
border-radius: 0%;
|
||||||
|
width: calc(3px * var(--pxDensity));
|
||||||
|
height: calc(3px * var(--pxDensity));
|
||||||
|
}
|
||||||
|
.slider::-webkit-slider-thumb {
|
||||||
|
-webkit-appearance: none;
|
||||||
|
appearance: none;
|
||||||
|
background-color: var(--navBar_Inner);
|
||||||
|
border: none;
|
||||||
|
cursor: pointer;
|
||||||
|
border-radius: 0%;
|
||||||
|
width: calc(3px * var(--pxDensity));
|
||||||
|
height: calc(3px * var(--pxDensity));
|
||||||
|
}
|
||||||
|
.sliderDiv {
|
||||||
|
background-color: var(--navBarBtn_Inner);
|
||||||
|
box-shadow: calc(1px * var(--pxDensity)) 0px var(--navBarBtn_Border),
|
||||||
|
calc(-1px * var(--pxDensity)) 0px var(--navBarBtn_Border),
|
||||||
|
0px calc(1px * var(--pxDensity)) var(--navBarBtn_Border),
|
||||||
|
0px calc(-1px * var(--pxDensity)) var(--navBarBtn_Border);
|
||||||
|
|
||||||
|
padding: calc(1px * var(--pxDensity));
|
||||||
|
border: none;
|
||||||
|
font-size: inherit;
|
||||||
|
margin: calc(3px * var(--pxDensity)) calc(2px * var(--pxDensity)) 0% 0%;
|
||||||
|
box-sizing: border-box;
|
||||||
|
width: 100%;
|
||||||
|
height: calc(6px * var(--pxDensity));
|
||||||
|
color: inherit;
|
||||||
|
align-items: center;
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
#sillyNumberValue {
|
||||||
|
display: inline;
|
||||||
|
background-color: var(--navBarBtn_Inner);
|
||||||
|
box-shadow: calc(1px * var(--pxDensity)) 0px var(--navBarBtn_Border),
|
||||||
|
calc(-1px * var(--pxDensity)) 0px var(--navBarBtn_Border),
|
||||||
|
0px calc(1px * var(--pxDensity)) var(--navBarBtn_Border),
|
||||||
|
0px calc(-1px * var(--pxDensity)) var(--navBarBtn_Border);
|
||||||
|
|
||||||
|
padding: calc(1px * var(--pxDensity));
|
||||||
|
border: none;
|
||||||
|
font-size: inherit;
|
||||||
|
margin: calc(3px * var(--pxDensity)) calc(3px * var(--pxDensity)) 0% 0%;
|
||||||
|
box-sizing: border-box;
|
||||||
|
width: round(auto, var(--pxDensity));
|
||||||
|
height: calc(6px * var(--pxDensity));;
|
||||||
|
aspect-ratio: 1 / 1;
|
||||||
|
}
|
||||||
|
h1 {
|
||||||
|
background-color: var(--navBarBtn_Inner);
|
||||||
|
box-shadow: calc(1px * var(--pxDensity)) 0px var(--navBarBtn_Border),
|
||||||
|
calc(-1px * var(--pxDensity)) 0px var(--navBarBtn_Border),
|
||||||
|
0px calc(1px * var(--pxDensity)) var(--navBarBtn_Border),
|
||||||
|
0px calc(-1px * var(--pxDensity)) var(--navBarBtn_Border);
|
||||||
|
|
||||||
|
padding: calc(1px * var(--pxDensity));
|
||||||
|
border: none;
|
||||||
|
margin: calc(6px * var(--pxDensity)) calc(2px * var(--pxDensity)) 0% 0%;
|
||||||
|
box-sizing: border-box;
|
||||||
|
width: round(auto, var(--pxDensity));
|
||||||
|
height: round(auto, var(--pxDensity));
|
||||||
|
color: inherit;
|
||||||
|
font-size: inherit;
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
.h1Top {
|
||||||
|
margin-top: 0%;
|
||||||
|
}
|
||||||
|
select {
|
||||||
|
background-color: var(--navBarBtn_Inner);
|
||||||
|
box-shadow: calc(1px * var(--pxDensity)) 0px var(--navBarBtn_Border),
|
||||||
|
calc(-1px * var(--pxDensity)) 0px var(--navBarBtn_Border),
|
||||||
|
0px calc(1px * var(--pxDensity)) var(--navBarBtn_Border),
|
||||||
|
0px calc(-1px * var(--pxDensity)) var(--navBarBtn_Border);
|
||||||
|
|
||||||
|
padding: calc(1px * var(--pxDensity));
|
||||||
|
border: none;
|
||||||
|
font-size: inherit;
|
||||||
|
margin: calc(3px * var(--pxDensity)) calc(2px * var(--pxDensity)) 0% 0%;
|
||||||
|
box-sizing: border-box;
|
||||||
|
width: 100%;
|
||||||
|
height: round(auto, var(--pxDensity));
|
||||||
|
color: inherit;
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
select:hover {
|
||||||
|
filter: brightness(1.35);
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
input[type=file] {
|
||||||
|
/* height: max-content;
|
||||||
|
aspect-ratio: 1 / 1;
|
||||||
|
width: auto;
|
||||||
|
overflow: hidden; */
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
#picInput, #bgTypeInput {
|
||||||
|
height: calc(6px * var(--pxDensity));
|
||||||
|
margin-right: calc(3px * var(--pxDensity));
|
||||||
|
}
|
||||||
|
#picUploadLabel, #bgUploadLabel {
|
||||||
|
height: calc(6px * var(--pxDensity));
|
||||||
|
aspect-ratio: 1 / 1;
|
||||||
|
width: auto;
|
||||||
|
background-image: url(/assets/icons/upload.svg);
|
||||||
|
display: inline-flex;
|
||||||
|
margin-right: calc(2px * var(--pxDensity));
|
||||||
|
}
|
||||||
|
#picUploadLabel:hover, #bgUploadLabel:hover {
|
||||||
|
filter: brightness(1.35);
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
#picUploadLabel:active, #bgUploadLabel:active {
|
||||||
|
filter: brightness(0.75);
|
||||||
|
transform: translateY(calc(1px * var(--pxDensity)));
|
||||||
|
}
|
||||||
|
#picUploadIcon, #bgUploadIcon {
|
||||||
|
height: calc(4px * var(--pxDensity));
|
||||||
|
aspect-ratio: 1 / 1;
|
||||||
|
width: auto;
|
||||||
|
display: inline-block;
|
||||||
|
vertical-align: middle;
|
||||||
|
}
|
||||||
|
form {
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
.generateButton {
|
||||||
|
background-color: var(--navBarBtn_Inner);
|
||||||
|
box-shadow: calc(1px * var(--pxDensity)) 0px var(--navBarBtn_Border),
|
||||||
|
calc(-1px * var(--pxDensity)) 0px var(--navBarBtn_Border),
|
||||||
|
0px calc(1px * var(--pxDensity)) var(--navBarBtn_Border),
|
||||||
|
0px calc(-1px * var(--pxDensity)) var(--navBarBtn_Border);
|
||||||
|
|
||||||
|
/* padding: calc(1px * var(--pxDensity)); */
|
||||||
|
border: none;
|
||||||
|
margin: calc(6px * var(--pxDensity)) calc(2px * var(--pxDensity)) 0% 0%;
|
||||||
|
box-sizing: border-box;
|
||||||
|
width: calc(100% - (4px * var(--pxDensity)));
|
||||||
|
height: calc(6px * var(--pxDensity));
|
||||||
|
display: inline-flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
font-size: min(5vmin, 2vmax, 30px);
|
||||||
|
color: #ffffff
|
||||||
|
}
|
||||||
|
.generateButtonIcon {
|
||||||
|
height: calc(4px * var(--pxDensity));
|
||||||
|
aspect-ratio: 1 / 1;
|
||||||
|
width: auto;
|
||||||
|
display: inline-block;
|
||||||
|
margin: auto 0%;
|
||||||
|
}
|
||||||
|
input[type="color"] {
|
||||||
|
background-color: var(--navBarBtn_Inner);
|
||||||
|
box-shadow: calc(1px * var(--pxDensity)) 0px var(--navBarBtn_Border),
|
||||||
|
calc(-1px * var(--pxDensity)) 0px var(--navBarBtn_Border),
|
||||||
|
0px calc(1px * var(--pxDensity)) var(--navBarBtn_Border),
|
||||||
|
0px calc(-1px * var(--pxDensity)) var(--navBarBtn_Border);
|
||||||
|
|
||||||
|
padding: 0;
|
||||||
|
border: none;
|
||||||
|
font-size: inherit;
|
||||||
|
margin: calc(3px * var(--pxDensity)) calc(2px * var(--pxDensity)) 0% 0%;
|
||||||
|
box-sizing: border-box;
|
||||||
|
width: round(auto, var(--pxDensity));
|
||||||
|
height: calc(6px * var(--pxDensity));
|
||||||
|
color: inherit;
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
input[type="color"]::-moz-color-swatch {
|
||||||
|
border: none;
|
||||||
|
}
|
||||||
|
input[type="color"]::-webkit-color-swatch-wrapper {
|
||||||
|
padding: 0;
|
||||||
|
border-radius: 0;
|
||||||
|
}
|
||||||
|
input[type="color"]::-webkit-color-swatch {
|
||||||
|
border: none;
|
||||||
|
}
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
CURRENT_PAGE = PAGES.LICENSE
|
||||||
|
|
||||||
|
|
||||||
|
async function loadingScreenLicense() {
|
||||||
|
setLoadingProgress("Preparing license...")
|
||||||
|
|
||||||
|
const VMAP = {
|
||||||
|
id: "hashID",
|
||||||
|
img: "picValue",
|
||||||
|
user: "authorValue",
|
||||||
|
base: "licenseContainer",
|
||||||
|
title: "titleValue",
|
||||||
|
issued: "createdValue",
|
||||||
|
expires: "expireValue",
|
||||||
|
identity: "genderValue",
|
||||||
|
signature: "signatureValue"
|
||||||
|
}
|
||||||
|
|
||||||
|
function getE(e) { return document.getElementById(e) }
|
||||||
|
|
||||||
|
license.idE = getE(VMAP.id)
|
||||||
|
license.imgE = getE(VMAP.img)
|
||||||
|
license.userE = getE(VMAP.user)
|
||||||
|
license.baseE = getE(VMAP.base)
|
||||||
|
license.titleE = getE(VMAP.title)
|
||||||
|
license.issuedE = getE(VMAP.issued)
|
||||||
|
license.expiresE = getE(VMAP.expires)
|
||||||
|
license.identityE = getE(VMAP.identity)
|
||||||
|
license.signatureE = getE(VMAP.signature)
|
||||||
|
|
||||||
|
for (let i = 0; i < 10; i++) {
|
||||||
|
license.sillyValues.push(document.getElementById(`sillyValue${i}`))
|
||||||
|
}
|
||||||
|
|
||||||
|
setLoadingProgress("Preparing options...")
|
||||||
|
prepInputs()
|
||||||
|
makeOptionsTheSameAsJSON()
|
||||||
|
scaleDisplayLicense()
|
||||||
|
await updLicense()
|
||||||
|
}
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
.allContainer {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: calc(4px * var(--pxDensity));
|
||||||
|
height: calc(100vh - calc(25px * var(--pxDensity)));
|
||||||
|
}
|
||||||
|
.blackBorderRect {
|
||||||
|
background-color: #FFFFFF;
|
||||||
|
border: 10px #000000 solid;
|
||||||
|
border-radius: 32px;
|
||||||
|
color: #000000;
|
||||||
|
}
|
||||||
|
a {
|
||||||
|
cursor: pointer;
|
||||||
|
color: #2222FF;
|
||||||
|
text-decoration: underline;
|
||||||
|
font-weight: normal;
|
||||||
|
}
|
||||||
|
a:visited {
|
||||||
|
color: #551A8B;
|
||||||
|
}
|
||||||
|
.mainDiv {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
@@ -0,0 +1,50 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, height=device-height, initial-scale=1.0, user-scalable=no">
|
||||||
|
|
||||||
|
<link rel="stylesheet" href="/global/navBar.css">
|
||||||
|
<link rel="stylesheet" href="/global/globalStyle.css">
|
||||||
|
<link rel="stylesheet" href="/global/loadingScreen.css">
|
||||||
|
<link rel="stylesheet" href="style.css">
|
||||||
|
|
||||||
|
<link rel="icon" type="image/png" href="/assets/favicon_32x32.png">
|
||||||
|
<title>Byte Dice - Loading...</title>
|
||||||
|
|
||||||
|
<meta name="robots" content="index, follow">
|
||||||
|
<link rel="canonical" href="https://bytedice.net/toys/loading">
|
||||||
|
|
||||||
|
<meta name="description" content="An infinite decorative loading screen.">
|
||||||
|
<meta name="keywords" content="Byte Dice, pixel art, procedural generation, game art, digital art, ByteDice showcase">
|
||||||
|
<meta name="author" content="Byte Dice">
|
||||||
|
|
||||||
|
<meta property="og:title" content="Byte Dice">
|
||||||
|
<meta property="og:description" content="An infinite decorative loading screen.">
|
||||||
|
<meta property="og:url" content="https://bytedice.net/toys/loading">
|
||||||
|
<meta property="og:type" content="website">
|
||||||
|
<meta property="og:image" content="/assets/backgrounds/embedImage.png">
|
||||||
|
<meta property="og:image:width" content="1280">
|
||||||
|
<meta property="og:image:height" content="720">
|
||||||
|
|
||||||
|
<!-- No twitter embeds except for card. Elon can go and fucking return it to its previous owners. -->
|
||||||
|
<meta name="twitter:card" content="summary_large_image">
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<div id="mainDiv">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="loadingScreen"></div>
|
||||||
|
|
||||||
|
<!-- global -->
|
||||||
|
<script src="/global/globalScript.js"></script>
|
||||||
|
<script src="/global/pxDensity.js"></script>
|
||||||
|
<script src="/global/loadingScreen.js"></script>
|
||||||
|
|
||||||
|
<!-- local -->
|
||||||
|
<script src="script.js"></script>
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
CURRENT_PAGE = PAGES.LOADING
|
||||||
|
|
||||||
|
|
||||||
|
let byteDiceSize = 1
|
||||||
|
let loadingImg = undefined
|
||||||
|
|
||||||
|
|
||||||
|
function clearLoadingScreenClutter() {
|
||||||
|
document.getElementById("fakeLoadingToggle").remove()
|
||||||
|
loadingImg = document.getElementById("loadingImg")
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function periodicallyChangeTips() {
|
||||||
|
newTip()
|
||||||
|
setLoadingProgress("")
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function changeLoadingImgSize() {
|
||||||
|
loadingImg.style.width = `calc(${32 * byteDiceSize}px * var(--pxDensity))`
|
||||||
|
loadingImg.style.height = `calc(${32 * byteDiceSize}px * var(--pxDensity))`
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
window.onkeydown = function(e) { // keycodes 48..57 = 0-9
|
||||||
|
let code = e.keyCode
|
||||||
|
|
||||||
|
if (!(code >= 48 && code <= 57)) { return }
|
||||||
|
|
||||||
|
byteDiceSize = (code - 48) / 9 + 1
|
||||||
|
changeLoadingImgSize()
|
||||||
|
}
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
CURRENT_PAGE = PAGES.TOYS
|
||||||
@@ -0,0 +1,63 @@
|
|||||||
|
.toyContainer {
|
||||||
|
border: none;
|
||||||
|
display: inline-grid;
|
||||||
|
width: 25%;
|
||||||
|
min-width: 100%;
|
||||||
|
max-width: 512px;
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
grid-template-rows: calc(32px * var(--pxDensity)) 1fr;
|
||||||
|
color: inherit;
|
||||||
|
}
|
||||||
|
.toyImg {
|
||||||
|
image-rendering: pixelated;
|
||||||
|
overflow: hidden;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
object-fit: contain;
|
||||||
|
margin: auto auto;
|
||||||
|
}
|
||||||
|
.toyTextContainer {
|
||||||
|
background-color: var(--navBar_SecondInner);
|
||||||
|
padding: calc(1px * var(--pxDensity));
|
||||||
|
border-top: calc(1px * var(--pxDensity)) solid var(--navBar_Border);
|
||||||
|
text-align: left;
|
||||||
|
overflow-y: scroll;
|
||||||
|
overflow-x: hidden;
|
||||||
|
height: calc(16px * var(--pxDensity));
|
||||||
|
font-size: min(5vmin, 2vmax, 25px);
|
||||||
|
}
|
||||||
|
h1 {
|
||||||
|
font-size: max(1.2em, 2.5vmin);
|
||||||
|
margin: 0%;
|
||||||
|
margin-bottom: calc(1px * var(--pxDensity));
|
||||||
|
}
|
||||||
|
.desc {
|
||||||
|
font-size: max(1em, 2vmin);
|
||||||
|
margin: 0%;
|
||||||
|
}
|
||||||
|
button:hover {
|
||||||
|
filter: brightness(1.15) drop-shadow(0px calc(2px * var(--pxDensity)) rgba(0, 0, 0, 0.5));
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
button:active {
|
||||||
|
filter: brightness(0.85);
|
||||||
|
transform: translateY(calc(1px * var(--pxDensity)));
|
||||||
|
}
|
||||||
|
.toyUl {
|
||||||
|
padding: 0% calc(3px * var(--pxDensity));
|
||||||
|
display: inline-flex;
|
||||||
|
width: fit-content;
|
||||||
|
justify-content: center;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: calc(6px * var(--pxDensity));
|
||||||
|
margin: 0 auto;
|
||||||
|
overflow-y: scroll;
|
||||||
|
max-height: 100vh;
|
||||||
|
}
|
||||||
|
.toyLi {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
ul {
|
||||||
|
margin: 0%;
|
||||||
|
padding: 0%;
|
||||||
|
}
|
||||||