diff --git a/.dockerignore b/.dockerignore index cd967fc..38bece4 100644 --- a/.dockerignore +++ b/.dockerignore @@ -1,25 +1,25 @@ -**/.dockerignore -**/.env -**/.git -**/.gitignore -**/.project -**/.settings -**/.toolstarget -**/.vs -**/.vscode -**/.idea -**/*.*proj.user -**/*.dbmdl -**/*.jfm -**/azds.yaml -**/bin -**/charts -**/docker-compose* -**/Dockerfile* -**/node_modules -**/npm-debug.log -**/obj -**/secrets.dev.yaml -**/values.dev.yaml -LICENSE +**/.dockerignore +**/.env +**/.git +**/.gitignore +**/.project +**/.settings +**/.toolstarget +**/.vs +**/.vscode +**/.idea +**/*.*proj.user +**/*.dbmdl +**/*.jfm +**/azds.yaml +**/bin +**/charts +**/docker-compose* +**/Dockerfile* +**/node_modules +**/npm-debug.log +**/obj +**/secrets.dev.yaml +**/values.dev.yaml +LICENSE README.md \ No newline at end of file diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 2c7d170..571e881 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -1,7 +1,7 @@ -version: 2 -updates: - # Maintain dependencies for GitHub Actions - - package-ecosystem: "github-actions" - directory: "/" - schedule: - interval: "daily" +version: 2 +updates: + # Maintain dependencies for GitHub Actions + - package-ecosystem: "github-actions" + directory: "/" + schedule: + interval: "daily" diff --git a/.github/workflows/docker-image-cuttingedge.yml b/.github/workflows/docker-image-cuttingedge.yml index d9bd4a0..f17821c 100644 --- a/.github/workflows/docker-image-cuttingedge.yml +++ b/.github/workflows/docker-image-cuttingedge.yml @@ -1,45 +1,45 @@ -name: Docker Image CI - -on: - push: - branches: [ "cuttingedge" ] - workflow_dispatch: - -jobs: - - build: - - runs-on: ubuntu-latest - - steps: - - name: Checkout - uses: actions/checkout@v3 - - # https://github.com/docker/setup-qemu-action#usage - - name: Set up QEMU - uses: docker/setup-qemu-action@v2.2.0 - - # https://github.com/marketplace/actions/docker-setup-buildx - - name: Set up Docker Buildx - id: buildx - uses: docker/setup-buildx-action@v3.3.0 - - # https://github.com/docker/login-action#docker-hub - - name: Login to Docker Hub - uses: docker/login-action@v2 - with: - username: ${{ secrets.DOCKERHUB_USERNAME }} - password: ${{ secrets.DOCKERHUB_TOKEN }} - - # https://github.com/docker/build-push-action#multi-platform-image - - name: Build and push Website - uses: docker/build-push-action@v4.1.1 - with: - context: ./Website - file: ./Dockerfile - #platforms: linux/amd64,linux/arm64,linux/riscv64,linux/ppc64le,linux/s390x,linux/386,linux/mips64le,linux/mips64,linux/arm/v7,linux/arm/v6 - platforms: linux/amd64 - pull: true - push: true - tags: | - glax/tranga-website:cuttingedge +name: Docker Image CI + +on: + push: + branches: [ "cuttingedge" ] + workflow_dispatch: + +jobs: + + build: + + runs-on: ubuntu-latest + + steps: + - name: Checkout + uses: actions/checkout@v3 + + # https://github.com/docker/setup-qemu-action#usage + - name: Set up QEMU + uses: docker/setup-qemu-action@v2.2.0 + + # https://github.com/marketplace/actions/docker-setup-buildx + - name: Set up Docker Buildx + id: buildx + uses: docker/setup-buildx-action@v3.3.0 + + # https://github.com/docker/login-action#docker-hub + - name: Login to Docker Hub + uses: docker/login-action@v2 + with: + username: ${{ secrets.DOCKERHUB_USERNAME }} + password: ${{ secrets.DOCKERHUB_TOKEN }} + + # https://github.com/docker/build-push-action#multi-platform-image + - name: Build and push Website + uses: docker/build-push-action@v4.1.1 + with: + context: ./Website + file: ./Dockerfile + #platforms: linux/amd64,linux/arm64,linux/riscv64,linux/ppc64le,linux/s390x,linux/386,linux/mips64le,linux/mips64,linux/arm/v7,linux/arm/v6 + platforms: linux/amd64 + pull: true + push: true + tags: | + glax/tranga-website:cuttingedge diff --git a/.github/workflows/docker-image-dev.yml b/.github/workflows/docker-image-dev.yml index a8d6df0..cf514ee 100644 --- a/.github/workflows/docker-image-dev.yml +++ b/.github/workflows/docker-image-dev.yml @@ -1,45 +1,45 @@ -name: Docker Image CI - -on: - push: - branches: [ "cuttingedge" ] - workflow_dispatch: - -jobs: - - build: - - runs-on: ubuntu-latest - - steps: - - name: Checkout - uses: actions/checkout@v3 - - # https://github.com/docker/setup-qemu-action#usage - - name: Set up QEMU - uses: docker/setup-qemu-action@v2.2.0 - - # https://github.com/marketplace/actions/docker-setup-buildx - - name: Set up Docker Buildx - id: buildx - uses: docker/setup-buildx-action@v3.3.0 - - # https://github.com/docker/login-action#docker-hub - - name: Login to Docker Hub - uses: docker/login-action@v2 - with: - username: ${{ secrets.DOCKERHUB_USERNAME }} - password: ${{ secrets.DOCKERHUB_TOKEN }} - - # https://github.com/docker/build-push-action#multi-platform-image - - name: Build and push Website - uses: docker/build-push-action@v4.1.1 - with: - context: ./Website - file: ./Dockerfile - #platforms: linux/amd64,linux/arm64,linux/riscv64,linux/ppc64le,linux/s390x,linux/386,linux/mips64le,linux/mips64,linux/arm/v7,linux/arm/v6 - platforms: linux/amd64 - pull: true - push: true - tags: | - glax/tranga-website:dev +name: Docker Image CI + +on: + push: + branches: [ "cuttingedge" ] + workflow_dispatch: + +jobs: + + build: + + runs-on: ubuntu-latest + + steps: + - name: Checkout + uses: actions/checkout@v3 + + # https://github.com/docker/setup-qemu-action#usage + - name: Set up QEMU + uses: docker/setup-qemu-action@v2.2.0 + + # https://github.com/marketplace/actions/docker-setup-buildx + - name: Set up Docker Buildx + id: buildx + uses: docker/setup-buildx-action@v3.3.0 + + # https://github.com/docker/login-action#docker-hub + - name: Login to Docker Hub + uses: docker/login-action@v2 + with: + username: ${{ secrets.DOCKERHUB_USERNAME }} + password: ${{ secrets.DOCKERHUB_TOKEN }} + + # https://github.com/docker/build-push-action#multi-platform-image + - name: Build and push Website + uses: docker/build-push-action@v4.1.1 + with: + context: ./Website + file: ./Dockerfile + #platforms: linux/amd64,linux/arm64,linux/riscv64,linux/ppc64le,linux/s390x,linux/386,linux/mips64le,linux/mips64,linux/arm/v7,linux/arm/v6 + platforms: linux/amd64 + pull: true + push: true + tags: | + glax/tranga-website:dev diff --git a/.github/workflows/docker-image-master.yml b/.github/workflows/docker-image-master.yml index 49e2f7d..000e5da 100644 --- a/.github/workflows/docker-image-master.yml +++ b/.github/workflows/docker-image-master.yml @@ -1,47 +1,47 @@ -name: Docker Image CI - -on: - push: - branches: [ "master" ] - pull_request: - branches: [ "master" ] - workflow_dispatch: - -jobs: - - build: - - runs-on: ubuntu-latest - - steps: - - name: Checkout - uses: actions/checkout@v3 - - # https://github.com/docker/setup-qemu-action#usage - - name: Set up QEMU - uses: docker/setup-qemu-action@v2.2.0 - - # https://github.com/marketplace/actions/docker-setup-buildx - - name: Set up Docker Buildx - id: buildx - uses: docker/setup-buildx-action@v3.3.0 - - # https://github.com/docker/login-action#docker-hub - - name: Login to Docker Hub - uses: docker/login-action@v2 - with: - username: ${{ secrets.DOCKERHUB_USERNAME }} - password: ${{ secrets.DOCKERHUB_TOKEN }} - - # https://github.com/docker/build-push-action#multi-platform-image - - name: Build and push Website - uses: docker/build-push-action@v4.1.1 - with: - context: ./Website - file: ./Dockerfile - #platforms: linux/amd64,linux/arm64,linux/riscv64,linux/ppc64le,linux/s390x,linux/386,linux/mips64le,linux/mips64,linux/arm/v7,linux/arm/v6 - platforms: linux/amd64 - pull: true - push: true - tags: | - glax/tranga-website:latest +name: Docker Image CI + +on: + push: + branches: [ "master" ] + pull_request: + branches: [ "master" ] + workflow_dispatch: + +jobs: + + build: + + runs-on: ubuntu-latest + + steps: + - name: Checkout + uses: actions/checkout@v3 + + # https://github.com/docker/setup-qemu-action#usage + - name: Set up QEMU + uses: docker/setup-qemu-action@v2.2.0 + + # https://github.com/marketplace/actions/docker-setup-buildx + - name: Set up Docker Buildx + id: buildx + uses: docker/setup-buildx-action@v3.3.0 + + # https://github.com/docker/login-action#docker-hub + - name: Login to Docker Hub + uses: docker/login-action@v2 + with: + username: ${{ secrets.DOCKERHUB_USERNAME }} + password: ${{ secrets.DOCKERHUB_TOKEN }} + + # https://github.com/docker/build-push-action#multi-platform-image + - name: Build and push Website + uses: docker/build-push-action@v4.1.1 + with: + context: ./Website + file: ./Dockerfile + #platforms: linux/amd64,linux/arm64,linux/riscv64,linux/ppc64le,linux/s390x,linux/386,linux/mips64le,linux/mips64,linux/arm/v7,linux/arm/v6 + platforms: linux/amd64 + pull: true + push: true + tags: | + glax/tranga-website:latest diff --git a/.gitignore b/.gitignore index a77fa96..700f608 100644 --- a/.gitignore +++ b/.gitignore @@ -1,26 +1,26 @@ -bin/ -obj/ -/packages/ -riderModule.iml -/_ReSharper.Caches/ -# Default ignored files -/shelf/ -/workspace.xml -# Rider ignored files -/projectSettingsUpdater.xml -/.idea.Tranga.iml -/contentModel.xml -/modules.xml -# Editor-based HTTP Client requests -/httpRequests/ -# Datasource local storage ignored files -/dataSources/ -/dataSources.local.xml -/.idea -cover.jpg -cover.png -.vs/slnx.sqlite -.vs/tranga-website/config/applicationhost.config -.vs/tranga-website/FileContentIndex/91a465d3-1190-42e0-95eb-fa3694744e58.vsidx -.vs/tranga-website/v17/.wsuo -.vs/VSWorkspaceState.json +bin/ +obj/ +/packages/ +riderModule.iml +/_ReSharper.Caches/ +# Default ignored files +/shelf/ +/workspace.xml +# Rider ignored files +/projectSettingsUpdater.xml +/.idea.Tranga.iml +/contentModel.xml +/modules.xml +# Editor-based HTTP Client requests +/httpRequests/ +# Datasource local storage ignored files +/dataSources/ +/dataSources.local.xml +/.idea +cover.jpg +cover.png +.vs/slnx.sqlite +.vs/tranga-website/config/applicationhost.config +.vs/tranga-website/FileContentIndex/91a465d3-1190-42e0-95eb-fa3694744e58.vsidx +.vs/tranga-website/v17/.wsuo +.vs/VSWorkspaceState.json diff --git a/Dockerfile b/Dockerfile index d178671..b7f8cb5 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -FROM nginx:alpine3.17-slim -COPY . /usr/share/nginx/html -EXPOSE 80 +FROM nginx:alpine3.17-slim +COPY . /usr/share/nginx/html +EXPOSE 80 CMD ["nginx", "-g", "daemon off;"] \ No newline at end of file diff --git a/LICENSE.txt b/LICENSE.txt index a28fa47..7e2603b 100644 --- a/LICENSE.txt +++ b/LICENSE.txt @@ -1,674 +1,674 @@ - GNU GENERAL PUBLIC LICENSE - Version 3, 29 June 2007 - - Copyright (C) 2007 Free Software Foundation, Inc. - Everyone is permitted to copy and distribute verbatim copies - of this license document, but changing it is not allowed. - - Preamble - - The GNU General Public License is a free, copyleft license for -software and other kinds of works. - - The licenses for most software and other practical works are designed -to take away your freedom to share and change the works. By contrast, -the GNU General Public License is intended to guarantee your freedom to -share and change all versions of a program--to make sure it remains free -software for all its users. We, the Free Software Foundation, use the -GNU General Public License for most of our software; it applies also to -any other work released this way by its authors. You can apply it to -your programs, too. - - When we speak of free software, we are referring to freedom, not -price. Our General Public Licenses are designed to make sure that you -have the freedom to distribute copies of free software (and charge for -them if you wish), that you receive source code or can get it if you -want it, that you can change the software or use pieces of it in new -free programs, and that you know you can do these things. - - To protect your rights, we need to prevent others from denying you -these rights or asking you to surrender the rights. Therefore, you have -certain responsibilities if you distribute copies of the software, or if -you modify it: responsibilities to respect the freedom of others. - - For example, if you distribute copies of such a program, whether -gratis or for a fee, you must pass on to the recipients the same -freedoms that you received. You must make sure that they, too, receive -or can get the source code. And you must show them these terms so they -know their rights. - - Developers that use the GNU GPL protect your rights with two steps: -(1) assert copyright on the software, and (2) offer you this License -giving you legal permission to copy, distribute and/or modify it. - - For the developers' and authors' protection, the GPL clearly explains -that there is no warranty for this free software. For both users' and -authors' sake, the GPL requires that modified versions be marked as -changed, so that their problems will not be attributed erroneously to -authors of previous versions. - - Some devices are designed to deny users access to install or run -modified versions of the software inside them, although the manufacturer -can do so. This is fundamentally incompatible with the aim of -protecting users' freedom to change the software. The systematic -pattern of such abuse occurs in the area of products for individuals to -use, which is precisely where it is most unacceptable. Therefore, we -have designed this version of the GPL to prohibit the practice for those -products. If such problems arise substantially in other domains, we -stand ready to extend this provision to those domains in future versions -of the GPL, as needed to protect the freedom of users. - - Finally, every program is threatened constantly by software patents. -States should not allow patents to restrict development and use of -software on general-purpose computers, but in those that do, we wish to -avoid the special danger that patents applied to a free program could -make it effectively proprietary. To prevent this, the GPL assures that -patents cannot be used to render the program non-free. - - The precise terms and conditions for copying, distribution and -modification follow. - - TERMS AND CONDITIONS - - 0. Definitions. - - "This License" refers to version 3 of the GNU General Public License. - - "Copyright" also means copyright-like laws that apply to other kinds of -works, such as semiconductor masks. - - "The Program" refers to any copyrightable work licensed under this -License. Each licensee is addressed as "you". "Licensees" and -"recipients" may be individuals or organizations. - - To "modify" a work means to copy from or adapt all or part of the work -in a fashion requiring copyright permission, other than the making of an -exact copy. The resulting work is called a "modified version" of the -earlier work or a work "based on" the earlier work. - - A "covered work" means either the unmodified Program or a work based -on the Program. - - To "propagate" a work means to do anything with it that, without -permission, would make you directly or secondarily liable for -infringement under applicable copyright law, except executing it on a -computer or modifying a private copy. Propagation includes copying, -distribution (with or without modification), making available to the -public, and in some countries other activities as well. - - To "convey" a work means any kind of propagation that enables other -parties to make or receive copies. Mere interaction with a user through -a computer network, with no transfer of a copy, is not conveying. - - An interactive user interface displays "Appropriate Legal Notices" -to the extent that it includes a convenient and prominently visible -feature that (1) displays an appropriate copyright notice, and (2) -tells the user that there is no warranty for the work (except to the -extent that warranties are provided), that licensees may convey the -work under this License, and how to view a copy of this License. If -the interface presents a list of user commands or options, such as a -menu, a prominent item in the list meets this criterion. - - 1. Source Code. - - The "source code" for a work means the preferred form of the work -for making modifications to it. "Object code" means any non-source -form of a work. - - A "Standard Interface" means an interface that either is an official -standard defined by a recognized standards body, or, in the case of -interfaces specified for a particular programming language, one that -is widely used among developers working in that language. - - The "System Libraries" of an executable work include anything, other -than the work as a whole, that (a) is included in the normal form of -packaging a Major Component, but which is not part of that Major -Component, and (b) serves only to enable use of the work with that -Major Component, or to implement a Standard Interface for which an -implementation is available to the public in source code form. A -"Major Component", in this context, means a major essential component -(kernel, window system, and so on) of the specific operating system -(if any) on which the executable work runs, or a compiler used to -produce the work, or an object code interpreter used to run it. - - The "Corresponding Source" for a work in object code form means all -the source code needed to generate, install, and (for an executable -work) run the object code and to modify the work, including scripts to -control those activities. However, it does not include the work's -System Libraries, or general-purpose tools or generally available free -programs which are used unmodified in performing those activities but -which are not part of the work. For example, Corresponding Source -includes interface definition files associated with source files for -the work, and the source code for shared libraries and dynamically -linked subprograms that the work is specifically designed to require, -such as by intimate data communication or control flow between those -subprograms and other parts of the work. - - The Corresponding Source need not include anything that users -can regenerate automatically from other parts of the Corresponding -Source. - - The Corresponding Source for a work in source code form is that -same work. - - 2. Basic Permissions. - - All rights granted under this License are granted for the term of -copyright on the Program, and are irrevocable provided the stated -conditions are met. This License explicitly affirms your unlimited -permission to run the unmodified Program. The output from running a -covered work is covered by this License only if the output, given its -content, constitutes a covered work. This License acknowledges your -rights of fair use or other equivalent, as provided by copyright law. - - You may make, run and propagate covered works that you do not -convey, without conditions so long as your license otherwise remains -in force. You may convey covered works to others for the sole purpose -of having them make modifications exclusively for you, or provide you -with facilities for running those works, provided that you comply with -the terms of this License in conveying all material for which you do -not control copyright. Those thus making or running the covered works -for you must do so exclusively on your behalf, under your direction -and control, on terms that prohibit them from making any copies of -your copyrighted material outside their relationship with you. - - Conveying under any other circumstances is permitted solely under -the conditions stated below. Sublicensing is not allowed; section 10 -makes it unnecessary. - - 3. Protecting Users' Legal Rights From Anti-Circumvention Law. - - No covered work shall be deemed part of an effective technological -measure under any applicable law fulfilling obligations under article -11 of the WIPO copyright treaty adopted on 20 December 1996, or -similar laws prohibiting or restricting circumvention of such -measures. - - When you convey a covered work, you waive any legal power to forbid -circumvention of technological measures to the extent such circumvention -is effected by exercising rights under this License with respect to -the covered work, and you disclaim any intention to limit operation or -modification of the work as a means of enforcing, against the work's -users, your or third parties' legal rights to forbid circumvention of -technological measures. - - 4. Conveying Verbatim Copies. - - You may convey verbatim copies of the Program's source code as you -receive it, in any medium, provided that you conspicuously and -appropriately publish on each copy an appropriate copyright notice; -keep intact all notices stating that this License and any -non-permissive terms added in accord with section 7 apply to the code; -keep intact all notices of the absence of any warranty; and give all -recipients a copy of this License along with the Program. - - You may charge any price or no price for each copy that you convey, -and you may offer support or warranty protection for a fee. - - 5. Conveying Modified Source Versions. - - You may convey a work based on the Program, or the modifications to -produce it from the Program, in the form of source code under the -terms of section 4, provided that you also meet all of these conditions: - - a) The work must carry prominent notices stating that you modified - it, and giving a relevant date. - - b) The work must carry prominent notices stating that it is - released under this License and any conditions added under section - 7. This requirement modifies the requirement in section 4 to - "keep intact all notices". - - c) You must license the entire work, as a whole, under this - License to anyone who comes into possession of a copy. This - License will therefore apply, along with any applicable section 7 - additional terms, to the whole of the work, and all its parts, - regardless of how they are packaged. This License gives no - permission to license the work in any other way, but it does not - invalidate such permission if you have separately received it. - - d) If the work has interactive user interfaces, each must display - Appropriate Legal Notices; however, if the Program has interactive - interfaces that do not display Appropriate Legal Notices, your - work need not make them do so. - - A compilation of a covered work with other separate and independent -works, which are not by their nature extensions of the covered work, -and which are not combined with it such as to form a larger program, -in or on a volume of a storage or distribution medium, is called an -"aggregate" if the compilation and its resulting copyright are not -used to limit the access or legal rights of the compilation's users -beyond what the individual works permit. Inclusion of a covered work -in an aggregate does not cause this License to apply to the other -parts of the aggregate. - - 6. Conveying Non-Source Forms. - - You may convey a covered work in object code form under the terms -of sections 4 and 5, provided that you also convey the -machine-readable Corresponding Source under the terms of this License, -in one of these ways: - - a) Convey the object code in, or embodied in, a physical product - (including a physical distribution medium), accompanied by the - Corresponding Source fixed on a durable physical medium - customarily used for software interchange. - - b) Convey the object code in, or embodied in, a physical product - (including a physical distribution medium), accompanied by a - written offer, valid for at least three years and valid for as - long as you offer spare parts or customer support for that product - model, to give anyone who possesses the object code either (1) a - copy of the Corresponding Source for all the software in the - product that is covered by this License, on a durable physical - medium customarily used for software interchange, for a price no - more than your reasonable cost of physically performing this - conveying of source, or (2) access to copy the - Corresponding Source from a network server at no charge. - - c) Convey individual copies of the object code with a copy of the - written offer to provide the Corresponding Source. This - alternative is allowed only occasionally and noncommercially, and - only if you received the object code with such an offer, in accord - with subsection 6b. - - d) Convey the object code by offering access from a designated - place (gratis or for a charge), and offer equivalent access to the - Corresponding Source in the same way through the same place at no - further charge. You need not require recipients to copy the - Corresponding Source along with the object code. If the place to - copy the object code is a network server, the Corresponding Source - may be on a different server (operated by you or a third party) - that supports equivalent copying facilities, provided you maintain - clear directions next to the object code saying where to find the - Corresponding Source. Regardless of what server hosts the - Corresponding Source, you remain obligated to ensure that it is - available for as long as needed to satisfy these requirements. - - e) Convey the object code using peer-to-peer transmission, provided - you inform other peers where the object code and Corresponding - Source of the work are being offered to the general public at no - charge under subsection 6d. - - A separable portion of the object code, whose source code is excluded -from the Corresponding Source as a System Library, need not be -included in conveying the object code work. - - A "User Product" is either (1) a "consumer product", which means any -tangible personal property which is normally used for personal, family, -or household purposes, or (2) anything designed or sold for incorporation -into a dwelling. In determining whether a product is a consumer product, -doubtful cases shall be resolved in favor of coverage. For a particular -product received by a particular user, "normally used" refers to a -typical or common use of that class of product, regardless of the status -of the particular user or of the way in which the particular user -actually uses, or expects or is expected to use, the product. A product -is a consumer product regardless of whether the product has substantial -commercial, industrial or non-consumer uses, unless such uses represent -the only significant mode of use of the product. - - "Installation Information" for a User Product means any methods, -procedures, authorization keys, or other information required to install -and execute modified versions of a covered work in that User Product from -a modified version of its Corresponding Source. The information must -suffice to ensure that the continued functioning of the modified object -code is in no case prevented or interfered with solely because -modification has been made. - - If you convey an object code work under this section in, or with, or -specifically for use in, a User Product, and the conveying occurs as -part of a transaction in which the right of possession and use of the -User Product is transferred to the recipient in perpetuity or for a -fixed term (regardless of how the transaction is characterized), the -Corresponding Source conveyed under this section must be accompanied -by the Installation Information. But this requirement does not apply -if neither you nor any third party retains the ability to install -modified object code on the User Product (for example, the work has -been installed in ROM). - - The requirement to provide Installation Information does not include a -requirement to continue to provide support service, warranty, or updates -for a work that has been modified or installed by the recipient, or for -the User Product in which it has been modified or installed. Access to a -network may be denied when the modification itself materially and -adversely affects the operation of the network or violates the rules and -protocols for communication across the network. - - Corresponding Source conveyed, and Installation Information provided, -in accord with this section must be in a format that is publicly -documented (and with an implementation available to the public in -source code form), and must require no special password or key for -unpacking, reading or copying. - - 7. Additional Terms. - - "Additional permissions" are terms that supplement the terms of this -License by making exceptions from one or more of its conditions. -Additional permissions that are applicable to the entire Program shall -be treated as though they were included in this License, to the extent -that they are valid under applicable law. If additional permissions -apply only to part of the Program, that part may be used separately -under those permissions, but the entire Program remains governed by -this License without regard to the additional permissions. - - When you convey a copy of a covered work, you may at your option -remove any additional permissions from that copy, or from any part of -it. (Additional permissions may be written to require their own -removal in certain cases when you modify the work.) You may place -additional permissions on material, added by you to a covered work, -for which you have or can give appropriate copyright permission. - - Notwithstanding any other provision of this License, for material you -add to a covered work, you may (if authorized by the copyright holders of -that material) supplement the terms of this License with terms: - - a) Disclaiming warranty or limiting liability differently from the - terms of sections 15 and 16 of this License; or - - b) Requiring preservation of specified reasonable legal notices or - author attributions in that material or in the Appropriate Legal - Notices displayed by works containing it; or - - c) Prohibiting misrepresentation of the origin of that material, or - requiring that modified versions of such material be marked in - reasonable ways as different from the original version; or - - d) Limiting the use for publicity purposes of names of licensors or - authors of the material; or - - e) Declining to grant rights under trademark law for use of some - trade names, trademarks, or service marks; or - - f) Requiring indemnification of licensors and authors of that - material by anyone who conveys the material (or modified versions of - it) with contractual assumptions of liability to the recipient, for - any liability that these contractual assumptions directly impose on - those licensors and authors. - - All other non-permissive additional terms are considered "further -restrictions" within the meaning of section 10. If the Program as you -received it, or any part of it, contains a notice stating that it is -governed by this License along with a term that is a further -restriction, you may remove that term. If a license document contains -a further restriction but permits relicensing or conveying under this -License, you may add to a covered work material governed by the terms -of that license document, provided that the further restriction does -not survive such relicensing or conveying. - - If you add terms to a covered work in accord with this section, you -must place, in the relevant source files, a statement of the -additional terms that apply to those files, or a notice indicating -where to find the applicable terms. - - Additional terms, permissive or non-permissive, may be stated in the -form of a separately written license, or stated as exceptions; -the above requirements apply either way. - - 8. Termination. - - You may not propagate or modify a covered work except as expressly -provided under this License. Any attempt otherwise to propagate or -modify it is void, and will automatically terminate your rights under -this License (including any patent licenses granted under the third -paragraph of section 11). - - However, if you cease all violation of this License, then your -license from a particular copyright holder is reinstated (a) -provisionally, unless and until the copyright holder explicitly and -finally terminates your license, and (b) permanently, if the copyright -holder fails to notify you of the violation by some reasonable means -prior to 60 days after the cessation. - - Moreover, your license from a particular copyright holder is -reinstated permanently if the copyright holder notifies you of the -violation by some reasonable means, this is the first time you have -received notice of violation of this License (for any work) from that -copyright holder, and you cure the violation prior to 30 days after -your receipt of the notice. - - Termination of your rights under this section does not terminate the -licenses of parties who have received copies or rights from you under -this License. If your rights have been terminated and not permanently -reinstated, you do not qualify to receive new licenses for the same -material under section 10. - - 9. Acceptance Not Required for Having Copies. - - You are not required to accept this License in order to receive or -run a copy of the Program. Ancillary propagation of a covered work -occurring solely as a consequence of using peer-to-peer transmission -to receive a copy likewise does not require acceptance. However, -nothing other than this License grants you permission to propagate or -modify any covered work. These actions infringe copyright if you do -not accept this License. Therefore, by modifying or propagating a -covered work, you indicate your acceptance of this License to do so. - - 10. Automatic Licensing of Downstream Recipients. - - Each time you convey a covered work, the recipient automatically -receives a license from the original licensors, to run, modify and -propagate that work, subject to this License. You are not responsible -for enforcing compliance by third parties with this License. - - An "entity transaction" is a transaction transferring control of an -organization, or substantially all assets of one, or subdividing an -organization, or merging organizations. If propagation of a covered -work results from an entity transaction, each party to that -transaction who receives a copy of the work also receives whatever -licenses to the work the party's predecessor in interest had or could -give under the previous paragraph, plus a right to possession of the -Corresponding Source of the work from the predecessor in interest, if -the predecessor has it or can get it with reasonable efforts. - - You may not impose any further restrictions on the exercise of the -rights granted or affirmed under this License. For example, you may -not impose a license fee, royalty, or other charge for exercise of -rights granted under this License, and you may not initiate litigation -(including a cross-claim or counterclaim in a lawsuit) alleging that -any patent claim is infringed by making, using, selling, offering for -sale, or importing the Program or any portion of it. - - 11. Patents. - - A "contributor" is a copyright holder who authorizes use under this -License of the Program or a work on which the Program is based. The -work thus licensed is called the contributor's "contributor version". - - A contributor's "essential patent claims" are all patent claims -owned or controlled by the contributor, whether already acquired or -hereafter acquired, that would be infringed by some manner, permitted -by this License, of making, using, or selling its contributor version, -but do not include claims that would be infringed only as a -consequence of further modification of the contributor version. For -purposes of this definition, "control" includes the right to grant -patent sublicenses in a manner consistent with the requirements of -this License. - - Each contributor grants you a non-exclusive, worldwide, royalty-free -patent license under the contributor's essential patent claims, to -make, use, sell, offer for sale, import and otherwise run, modify and -propagate the contents of its contributor version. - - In the following three paragraphs, a "patent license" is any express -agreement or commitment, however denominated, not to enforce a patent -(such as an express permission to practice a patent or covenant not to -sue for patent infringement). To "grant" such a patent license to a -party means to make such an agreement or commitment not to enforce a -patent against the party. - - If you convey a covered work, knowingly relying on a patent license, -and the Corresponding Source of the work is not available for anyone -to copy, free of charge and under the terms of this License, through a -publicly available network server or other readily accessible means, -then you must either (1) cause the Corresponding Source to be so -available, or (2) arrange to deprive yourself of the benefit of the -patent license for this particular work, or (3) arrange, in a manner -consistent with the requirements of this License, to extend the patent -license to downstream recipients. "Knowingly relying" means you have -actual knowledge that, but for the patent license, your conveying the -covered work in a country, or your recipient's use of the covered work -in a country, would infringe one or more identifiable patents in that -country that you have reason to believe are valid. - - If, pursuant to or in connection with a single transaction or -arrangement, you convey, or propagate by procuring conveyance of, a -covered work, and grant a patent license to some of the parties -receiving the covered work authorizing them to use, propagate, modify -or convey a specific copy of the covered work, then the patent license -you grant is automatically extended to all recipients of the covered -work and works based on it. - - A patent license is "discriminatory" if it does not include within -the scope of its coverage, prohibits the exercise of, or is -conditioned on the non-exercise of one or more of the rights that are -specifically granted under this License. You may not convey a covered -work if you are a party to an arrangement with a third party that is -in the business of distributing software, under which you make payment -to the third party based on the extent of your activity of conveying -the work, and under which the third party grants, to any of the -parties who would receive the covered work from you, a discriminatory -patent license (a) in connection with copies of the covered work -conveyed by you (or copies made from those copies), or (b) primarily -for and in connection with specific products or compilations that -contain the covered work, unless you entered into that arrangement, -or that patent license was granted, prior to 28 March 2007. - - Nothing in this License shall be construed as excluding or limiting -any implied license or other defenses to infringement that may -otherwise be available to you under applicable patent law. - - 12. No Surrender of Others' Freedom. - - If conditions are imposed on you (whether by court order, agreement or -otherwise) that contradict the conditions of this License, they do not -excuse you from the conditions of this License. If you cannot convey a -covered work so as to satisfy simultaneously your obligations under this -License and any other pertinent obligations, then as a consequence you may -not convey it at all. For example, if you agree to terms that obligate you -to collect a royalty for further conveying from those to whom you convey -the Program, the only way you could satisfy both those terms and this -License would be to refrain entirely from conveying the Program. - - 13. Use with the GNU Affero General Public License. - - Notwithstanding any other provision of this License, you have -permission to link or combine any covered work with a work licensed -under version 3 of the GNU Affero General Public License into a single -combined work, and to convey the resulting work. The terms of this -License will continue to apply to the part which is the covered work, -but the special requirements of the GNU Affero General Public License, -section 13, concerning interaction through a network will apply to the -combination as such. - - 14. Revised Versions of this License. - - The Free Software Foundation may publish revised and/or new versions of -the GNU General Public License from time to time. Such new versions will -be similar in spirit to the present version, but may differ in detail to -address new problems or concerns. - - Each version is given a distinguishing version number. If the -Program specifies that a certain numbered version of the GNU General -Public License "or any later version" applies to it, you have the -option of following the terms and conditions either of that numbered -version or of any later version published by the Free Software -Foundation. If the Program does not specify a version number of the -GNU General Public License, you may choose any version ever published -by the Free Software Foundation. - - If the Program specifies that a proxy can decide which future -versions of the GNU General Public License can be used, that proxy's -public statement of acceptance of a version permanently authorizes you -to choose that version for the Program. - - Later license versions may give you additional or different -permissions. However, no additional obligations are imposed on any -author or copyright holder as a result of your choosing to follow a -later version. - - 15. Disclaimer of Warranty. - - THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY -APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT -HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY -OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, -THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM -IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF -ALL NECESSARY SERVICING, REPAIR OR CORRECTION. - - 16. Limitation of Liability. - - IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING -WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS -THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY -GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE -USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF -DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD -PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), -EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF -SUCH DAMAGES. - - 17. Interpretation of Sections 15 and 16. - - If the disclaimer of warranty and limitation of liability provided -above cannot be given local legal effect according to their terms, -reviewing courts shall apply local law that most closely approximates -an absolute waiver of all civil liability in connection with the -Program, unless a warranty or assumption of liability accompanies a -copy of the Program in return for a fee. - - END OF TERMS AND CONDITIONS - - How to Apply These Terms to Your New Programs - - If you develop a new program, and you want it to be of the greatest -possible use to the public, the best way to achieve this is to make it -free software which everyone can redistribute and change under these terms. - - To do so, attach the following notices to the program. It is safest -to attach them to the start of each source file to most effectively -state the exclusion of warranty; and each file should have at least -the "copyright" line and a pointer to where the full notice is found. - - - Copyright (C) - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . - -Also add information on how to contact you by electronic and paper mail. - - If the program does terminal interaction, make it output a short -notice like this when it starts in an interactive mode: - - Copyright (C) - This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. - This is free software, and you are welcome to redistribute it - under certain conditions; type `show c' for details. - -The hypothetical commands `show w' and `show c' should show the appropriate -parts of the General Public License. Of course, your program's commands -might be different; for a GUI interface, you would use an "about box". - - You should also get your employer (if you work as a programmer) or school, -if any, to sign a "copyright disclaimer" for the program, if necessary. -For more information on this, and how to apply and follow the GNU GPL, see -. - - The GNU General Public License does not permit incorporating your program -into proprietary programs. If your program is a subroutine library, you -may consider it more useful to permit linking proprietary applications with -the library. If this is what you want to do, use the GNU Lesser General -Public License instead of this License. But first, please read -. + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. diff --git a/README.md b/README.md index fed1b32..7a132b8 100644 --- a/README.md +++ b/README.md @@ -1,123 +1,123 @@ - - - - -
-
- -

Tranga-Website

- -

- Automatic Manga and Metadata downloader -

-

- This is the Website for Tranga (API) -

-
- - - - -
- Table of Contents -
    -
  1. - About The Project - -
  2. -
  3. - Getting Started -
  4. -
  5. Roadmap
  6. -
  7. Contributing
  8. -
  9. License
  10. -
  11. Acknowledgments
  12. -
-
- - - - -## About The Project - -Tranga-Website is the Web-frontend to [Tranga](https://github.com/C9Glax/tranga) (the API). It displays information aquired from Tranga and can create Jobs (Manga-Downloads). - -### What this does do (and nothing else) - -This repo makes HTTP-requests to the [Tranga-API](https://github.com/C9Glax/tranga) to display it's present configuration. - -### Built With - -- nginx -- HTML, CSS, and barebones Javascript -- 💙 Blåhaj 🦈 - -

(back to top)

- - -## Getting Started - -There is a single release: - - -### Docker - -Download [docker-compose.yaml](https://github.com/C9Glax/tranga-website/blob/cuttingedge/docker-compose.yaml) and configure to your needs. -The `docker-compose` also includes [Tranga](https://github.com/C9Glax/tranga) as backend. For its configuration refer to the repo README. - - -## Roadmap - -- [ ] ❓ - -See the [open issues](https://github.com/C9Glax/tranga-website/issues) for a full list of proposed features (and known issues). - -

(back to top)

- - - - -## Contributing - -The following is copy & pasted: - -Contributions are what make the open source community such an amazing place to learn, inspire, and create. Any contributions you make are **greatly appreciated**. - -If you have a suggestion that would make this better, please fork the repo and create a pull request. You can also simply open an issue with the tag "enhancement". -Don't forget to give the project a star! Thanks again! - -1. Fork the Project -2. Create your Feature Branch (`git checkout -b feature/AmazingFeature`) -3. Commit your Changes (`git commit -m 'Add some AmazingFeature'`) -4. Push to the Branch (`git push origin feature/AmazingFeature`) -5. Open a Pull Request - -

(back to top)

- - - - -## License - -Distributed under the GNU GPLv3 License. See `LICENSE.txt` for more information. - -

(back to top)

- - - - -## Acknowledgments - -* [Choose an Open Source License](https://choosealicense.com) -* [Font Awesome](https://fontawesome.com) -* [Best-README-Template](https://github.com/othneildrew/Best-README-Template/tree/master) - -

(back to top)

+ + + + +
+
+ +

Tranga-Website

+ +

+ Automatic Manga and Metadata downloader +

+

+ This is the Website for Tranga (API) +

+
+ + + + +
+ Table of Contents +
    +
  1. + About The Project + +
  2. +
  3. + Getting Started +
  4. +
  5. Roadmap
  6. +
  7. Contributing
  8. +
  9. License
  10. +
  11. Acknowledgments
  12. +
+
+ + + + +## About The Project + +Tranga-Website is the Web-frontend to [Tranga](https://github.com/C9Glax/tranga) (the API). It displays information aquired from Tranga and can create Jobs (Manga-Downloads). + +### What this does do (and nothing else) + +This repo makes HTTP-requests to the [Tranga-API](https://github.com/C9Glax/tranga) to display it's present configuration. + +### Built With + +- nginx +- HTML, CSS, and barebones Javascript +- 💙 Blåhaj 🦈 + +

(back to top)

+ + +## Getting Started + +There is a single release: + + +### Docker + +Download [docker-compose.yaml](https://github.com/C9Glax/tranga-website/blob/cuttingedge/docker-compose.yaml) and configure to your needs. +The `docker-compose` also includes [Tranga](https://github.com/C9Glax/tranga) as backend. For its configuration refer to the repo README. + + +## Roadmap + +- [ ] ❓ + +See the [open issues](https://github.com/C9Glax/tranga-website/issues) for a full list of proposed features (and known issues). + +

(back to top)

+ + + + +## Contributing + +The following is copy & pasted: + +Contributions are what make the open source community such an amazing place to learn, inspire, and create. Any contributions you make are **greatly appreciated**. + +If you have a suggestion that would make this better, please fork the repo and create a pull request. You can also simply open an issue with the tag "enhancement". +Don't forget to give the project a star! Thanks again! + +1. Fork the Project +2. Create your Feature Branch (`git checkout -b feature/AmazingFeature`) +3. Commit your Changes (`git commit -m 'Add some AmazingFeature'`) +4. Push to the Branch (`git push origin feature/AmazingFeature`) +5. Open a Pull Request + +

(back to top)

+ + + + +## License + +Distributed under the GNU GPLv3 License. See `LICENSE.txt` for more information. + +

(back to top)

+ + + + +## Acknowledgments + +* [Choose an Open Source License](https://choosealicense.com) +* [Font Awesome](https://fontawesome.com) +* [Best-README-Template](https://github.com/othneildrew/Best-README-Template/tree/master) + +

(back to top)

diff --git a/Website/apiConnector.js b/Website/apiConnector.js index 64dca93..0d5b80a 100644 --- a/Website/apiConnector.js +++ b/Website/apiConnector.js @@ -1,346 +1,346 @@ -let apiUri = `${window.location.protocol}//${window.location.host.split(':')[0]}:6531` - -if(getCookie("apiUri") != ""){ - apiUri = getCookie("apiUri"); -} -setCookie("apiUri", apiUri); - -function setCookie(cname, cvalue) { - const d = new Date(); - d.setTime(d.getTime() + (365*24*60*60*1000)); - let expires = "expires="+ d.toUTCString(); - document.cookie = cname + "=" + cvalue + ";" + expires + ";path=/;samesite=strict"; -} - -function getCookie(cname) { - let name = cname + "="; - let decodedCookie = decodeURIComponent(document.cookie); - let ca = decodedCookie.split(';'); - for(let i = 0; i < ca.length; i++) { - let c = ca[i]; - while (c.charAt(0) == ' ') { - c = c.substring(1); - } - if (c.indexOf(name) == 0) { - return c.substring(name.length, c.length); - } - } - return ""; -} - -async function GetData(uri){ - let request = await fetch(uri, { - method: 'GET', - headers: { - 'Accept': 'application/json' - } - }); - let json = await request.json(); - return json; -} - -async function PostData(uri){ - let request = await fetch(uri, { - method: 'POST' - }); - //console.log(request); -} - -function DeleteData(uri){ - fetch(uri, { - method: 'DELETE' - }); -} - -async function Ping(){ - let ret = await GetData(`${apiUri}/Ping`); - return ret; -} - -async function GetAvailableControllers(){ - var uri = apiUri + "/Connectors"; - let json = await GetData(uri); - return json; -} - -async function GetPublicationFromConnector(connector, title){ - var uri; - if(title.includes("http")){ - uri = `${apiUri}/Manga/FromConnector?connector=${connector}&url=${title}`; - }else{ - uri = `${apiUri}/Manga/FromConnector?connector=${connector}&title=${title}`; - } - let json = await GetData(uri); - return json; -} - -async function GetChapters(connector, internalId, language){ - var uri = `${apiUri}/Manga/Chapters?connector=${connector}&internalId=${internalId}&translatedLanguage=${language}`; - let json = await GetData(uri); - return json; -} - -function GetCoverUrl(internalId){ - return `${apiUri}/Manga/Cover?internalId=${internalId}`; -} - -async function GetAllJobs(){ - var uri = `${apiUri}/Jobs`; - let json = await GetData(uri); - return json; -} - -async function GetRunningJobs(){ - var uri = `${apiUri}/Jobs/Running`; - let json = await GetData(uri); - return json; -} - -async function GetWaitingJobs(){ - var uri = `${apiUri}/Jobs/Waiting`; - let json = await GetData(uri); - return json; -} - -async function GetMonitorJobs(){ - var uri = `${apiUri}/Jobs/MonitorJobs`; - let json = await GetData(uri); - return json; -} - -async function GetProgress(jobId){ - var uri = `${apiUri}/Jobs/Progress?jobId=${jobId}`; - let json = await GetData(uri); - return json; -} - -async function GetSettings(){ - var uri = `${apiUri}/Settings`; - let json = await GetData(uri); - return json; -} - -async function GetAvailableNotificationConnectors(){ - var uri = `${apiUri}/NotificationConnectors/Types`; - let json = await GetData(uri); - return json; -} - -async function GetNotificationConnectors(){ - var uri = `${apiUri}/NotificationConnectors`; - let json = await GetData(uri); - return json; -} - -async function GetAvailableLibraryConnectors(){ - var uri = `${apiUri}/LibraryConnectors/Types`; - let json = await GetData(uri); - return json; -} - -async function GetLibraryConnectors(){ - var uri = `${apiUri}/LibraryConnectors`; - let json = await GetData(uri); - return json; -} - -async function GetRateLimits() { - var uri = `${apiUri}/Settings/customRequestLimit` - let json = await GetData(uri); - return json; -} - -function CreateMonitorJob(connector, internalId, language){ - var uri = `${apiUri}/Jobs/MonitorManga?connector=${connector}&internalId=${internalId}&interval=03:00:00&translatedLanguage=${language}`; - PostData(uri); -} - -function CreateDownloadNewChaptersJob(connector, internalId, language){ - var uri = `${apiUri}/Jobs/DownloadNewChapters?connector=${connector}&internalId=${internalId}&translatedLanguage=${language}`; - PostData(uri); -} - -function StartJob(jobId){ - var uri = `${apiUri}/Jobs/StartNow?jobId=${jobId}`; - PostData(uri); -} - -function UpdateDownloadLocation(downloadLocation){ - var uri = `${apiUri}/Settings/UpdateDownloadLocation?downloadLocation=${downloadLocation}`; - PostData(uri); -} - -function RefreshLibraryMetadata() { - var uri = `${apiUri}/Jobs/UpdateMetadata`; - PostData(uri); -} - -async function DownloadLogs() { - var uri = `${apiUri}/LogFile`; - - //Below taken from https://developer.mozilla.org/en-US/docs/Web/API/ReadableStream - fetch(uri) - .then((response) => response.body) - .then((rb) => { - const reader = rb.getReader(); - - return new ReadableStream({ - start(controller) { - // The following function handles each data chunk - function push() { - // "done" is a Boolean and value a "Uint8Array" - reader.read().then(({ done, value }) => { - // If there is no more data to read - if (done) { - console.log("done", done); - controller.close(); - return; - } - // Get the data and send it to the browser via the controller - controller.enqueue(value); - // Check chunks by logging to the console - console.log(done, value); - push(); - }); - } - - push(); - }, - }); - }) - .then((stream) => - // Respond with our stream - new Response(stream, { headers: { "Content-Type": "text/html" } }).text(), - ) - .then((result) => { - // Do things with result - //console.log(result); - - //Below download taken from https://stackoverflow.com/questions/3665115/how-to-create-a-file-in-memory-for-user-to-download-but-not-through-server - var element = document.createElement('a'); - element.setAttribute('href', 'data:text/plain;charset-utf-8,' + encodeURIComponent(result)); - var newDate = new Date(); - var filename = "Tranga_Logs_" + newDate.today() + "_" + newDate.timeNow() + ".log"; - element.setAttribute('download', filename); - element.click(); - }); -} - -//Following date-time code taken from: https://stackoverflow.com/questions/10211145/getting-current-date-and-time-in-javascript -// For todays date; -Date.prototype.today = function () { - return ((this.getDate() < 10)?"0":"") + this.getDate() +"/"+(((this.getMonth()+1) < 10)?"0":"") + (this.getMonth()+1) +"/"+ this.getFullYear(); -} - -// For the time now -Date.prototype.timeNow = function () { - return ((this.getHours() < 10)?"0":"") + this.getHours() +"_"+ ((this.getMinutes() < 10)?"0":"") + this.getMinutes() +"_"+ ((this.getSeconds() < 10)?"0":"") + this.getSeconds(); -} - -//Komga -function UpdateKomga(komgaUrl, komgaAuth){ - var uri = `${apiUri}/LibraryConnectors/Update?libraryConnector=Komga&komgaUrl=${komgaUrl}&komgaAuth=${komgaAuth}`; - PostData(uri); -} - -function ResetKomga(){ - var uri = `${apiUri}/LibraryConnectors/Reset?libraryConnector=Komga`; - PostData(uri); -} - -function TestKomga(komgaUrl, komgaAuth){ - var uri = `${apiUri}/LibraryConnectors/Test?libraryConnector=Komga&komgaUrl=${komgaUrl}&komgaAuth=${komgaAuth}`; - PostData(uri); -} - - -//Kavita -function UpdateKavita(kavitaUrl, kavitaUsername, kavitaPassword){ - var uri = `${apiUri}/LibraryConnectors/Update?libraryConnector=Kavita&kavitaUrl=${kavitaUrl}&kavitaUsername=${kavitaUsername}&kavitaPassword=${kavitaPassword}`; - PostData(uri); -} - -function ResetKavita(){ - var uri = `${apiUri}/LibraryConnectors/Reset?libraryConnector=Kavita`; - PostData(uri); -} - -function TestKavita(kavitaUrl, kavitaUsername, kavitaPassword){ - var uri = `${apiUri}/LibraryConnectors/Test?libraryConnector=Kavita&kavitaUrl=${kavitaUrl}&kavitaUsername=${kavitaUsername}&kavitaPassword=${kavitaPassword}`; - PostData(uri); -} - -//Gotify -function UpdateGotify(gotifyUrl, gotifyAppToken){ - var uri = `${apiUri}/NotificationConnectors/Update?notificationConnector=Gotify&gotifyUrl=${gotifyUrl}&gotifyAppToken=${gotifyAppToken}`; - PostData(uri); -} - -function ResetGotify(){ - var uri = `${apiUri}/NotificationConnectors/Reset?notificationConnector=Gotify`; - PostData(uri); -} - -function TestGotify(gotifyUrl, gotifyAppToken){ - var uri = `${apiUri}/NotificationConnectors/Test?notificationConnector=Gotify&gotifyUrl=${gotifyUrl}&gotifyAppToken=${gotifyAppToken}`; - PostData(uri); -} - -//LunaSea -function UpdateLunaSea(lunaseaWebhook){ - var uri = `${apiUri}/NotificationConnectors/Update?notificationConnector=LunaSea&lunaseaWebhook=${lunaseaWebhook}`; - PostData(uri); -} - -function ResetLunaSea(){ - var uri = `${apiUri}/NotificationConnectors/Reset?notificationConnector=LunaSea`; - PostData(uri); -} - -function TestLunaSea(lunaseaWebhook){ - var uri = `${apiUri}/NotificationConnectors/Test?notificationConnector=LunaSea&lunaseaWebhook=${lunaseaWebhook}`; - PostData(uri); -} - -//Ntfy -function UpdateNtfy(ntfyEndpoint, ntfyAuth){ - var uri = `${apiUri}/NotificationConnectors/Update?notificationConnector=Ntfy&ntfyUrl=${ntfyEndpoint}&ntfyAuth=${ntfyAuth}`; - PostData(uri); -} - -function ResetNtfy(){ - var uri = `${apiUri}/NotificationConnectors/Reset?notificationConnector=Ntfy`; - PostData(uri); -} - -function TestNtfy(ntfyEndpoint, ntfyAuth){ - var uri = `${apiUri}/NotificationConnectors/Test?notificationConnector=Ntfy&ntfyUrl=${ntfyEndpoint}&ntfyAuth=${ntfyAuth}`; - PostData(uri); -} - -function UpdateUserAgent(userAgent){ - var uri = `${apiUri}/Settings/userAgent?userAgent=${userAgent}`; - PostData(uri); -} - -function UpdateRateLimit(byteValue, rateLimit) { - var uri = `${apiUri}/Settings/customRequestLimit?requestType=${byteValue}&requestsPerMinute=${rateLimit}`; - PostData(uri); -} - -function RemoveJob(jobId){ - var uri = `${apiUri}/Jobs?jobId=${jobId}`; - DeleteData(uri); -} - -function CancelJob(jobId){ - var uri = `${apiUri}/Jobs/Cancel?jobId=${jobId}`; - PostData(uri); -} - -async function GetLogmessages(count){ - var uri = `${apiUri}/LogMessages?count=${count}`; - let json = await GetData(uri); - console.log(json); - return json; +let apiUri = `${window.location.protocol}//${window.location.host.split(':')[0]}:6531` + +if(getCookie("apiUri") != ""){ + apiUri = getCookie("apiUri"); +} +setCookie("apiUri", apiUri); + +function setCookie(cname, cvalue) { + const d = new Date(); + d.setTime(d.getTime() + (365*24*60*60*1000)); + let expires = "expires="+ d.toUTCString(); + document.cookie = cname + "=" + cvalue + ";" + expires + ";path=/;samesite=strict"; +} + +function getCookie(cname) { + let name = cname + "="; + let decodedCookie = decodeURIComponent(document.cookie); + let ca = decodedCookie.split(';'); + for(let i = 0; i < ca.length; i++) { + let c = ca[i]; + while (c.charAt(0) == ' ') { + c = c.substring(1); + } + if (c.indexOf(name) == 0) { + return c.substring(name.length, c.length); + } + } + return ""; +} + +async function GetData(uri){ + let request = await fetch(uri, { + method: 'GET', + headers: { + 'Accept': 'application/json' + } + }); + let json = await request.json(); + return json; +} + +async function PostData(uri){ + let request = await fetch(uri, { + method: 'POST' + }); + //console.log(request); +} + +function DeleteData(uri){ + fetch(uri, { + method: 'DELETE' + }); +} + +async function Ping(){ + let ret = await GetData(`${apiUri}/Ping`); + return ret; +} + +async function GetAvailableControllers(){ + var uri = apiUri + "/Connectors"; + let json = await GetData(uri); + return json; +} + +async function GetPublicationFromConnector(connector, title){ + var uri; + if(title.includes("http")){ + uri = `${apiUri}/Manga/FromConnector?connector=${connector}&url=${title}`; + }else{ + uri = `${apiUri}/Manga/FromConnector?connector=${connector}&title=${title}`; + } + let json = await GetData(uri); + return json; +} + +async function GetChapters(connector, internalId, language){ + var uri = `${apiUri}/Manga/Chapters?connector=${connector}&internalId=${internalId}&translatedLanguage=${language}`; + let json = await GetData(uri); + return json; +} + +function GetCoverUrl(internalId){ + return `${apiUri}/Manga/Cover?internalId=${internalId}`; +} + +async function GetAllJobs(){ + var uri = `${apiUri}/Jobs`; + let json = await GetData(uri); + return json; +} + +async function GetRunningJobs(){ + var uri = `${apiUri}/Jobs/Running`; + let json = await GetData(uri); + return json; +} + +async function GetWaitingJobs(){ + var uri = `${apiUri}/Jobs/Waiting`; + let json = await GetData(uri); + return json; +} + +async function GetMonitorJobs(){ + var uri = `${apiUri}/Jobs/MonitorJobs`; + let json = await GetData(uri); + return json; +} + +async function GetProgress(jobId){ + var uri = `${apiUri}/Jobs/Progress?jobId=${jobId}`; + let json = await GetData(uri); + return json; +} + +async function GetSettings(){ + var uri = `${apiUri}/Settings`; + let json = await GetData(uri); + return json; +} + +async function GetAvailableNotificationConnectors(){ + var uri = `${apiUri}/NotificationConnectors/Types`; + let json = await GetData(uri); + return json; +} + +async function GetNotificationConnectors(){ + var uri = `${apiUri}/NotificationConnectors`; + let json = await GetData(uri); + return json; +} + +async function GetAvailableLibraryConnectors(){ + var uri = `${apiUri}/LibraryConnectors/Types`; + let json = await GetData(uri); + return json; +} + +async function GetLibraryConnectors(){ + var uri = `${apiUri}/LibraryConnectors`; + let json = await GetData(uri); + return json; +} + +async function GetRateLimits() { + var uri = `${apiUri}/Settings/customRequestLimit` + let json = await GetData(uri); + return json; +} + +function CreateMonitorJob(connector, internalId, language){ + var uri = `${apiUri}/Jobs/MonitorManga?connector=${connector}&internalId=${internalId}&interval=03:00:00&translatedLanguage=${language}`; + PostData(uri); +} + +function CreateDownloadNewChaptersJob(connector, internalId, language){ + var uri = `${apiUri}/Jobs/DownloadNewChapters?connector=${connector}&internalId=${internalId}&translatedLanguage=${language}`; + PostData(uri); +} + +function StartJob(jobId){ + var uri = `${apiUri}/Jobs/StartNow?jobId=${jobId}`; + PostData(uri); +} + +function UpdateDownloadLocation(downloadLocation){ + var uri = `${apiUri}/Settings/UpdateDownloadLocation?downloadLocation=${downloadLocation}`; + PostData(uri); +} + +function RefreshLibraryMetadata() { + var uri = `${apiUri}/Jobs/UpdateMetadata`; + PostData(uri); +} + +async function DownloadLogs() { + var uri = `${apiUri}/LogFile`; + + //Below taken from https://developer.mozilla.org/en-US/docs/Web/API/ReadableStream + fetch(uri) + .then((response) => response.body) + .then((rb) => { + const reader = rb.getReader(); + + return new ReadableStream({ + start(controller) { + // The following function handles each data chunk + function push() { + // "done" is a Boolean and value a "Uint8Array" + reader.read().then(({ done, value }) => { + // If there is no more data to read + if (done) { + console.log("done", done); + controller.close(); + return; + } + // Get the data and send it to the browser via the controller + controller.enqueue(value); + // Check chunks by logging to the console + console.log(done, value); + push(); + }); + } + + push(); + }, + }); + }) + .then((stream) => + // Respond with our stream + new Response(stream, { headers: { "Content-Type": "text/html" } }).text(), + ) + .then((result) => { + // Do things with result + //console.log(result); + + //Below download taken from https://stackoverflow.com/questions/3665115/how-to-create-a-file-in-memory-for-user-to-download-but-not-through-server + var element = document.createElement('a'); + element.setAttribute('href', 'data:text/plain;charset-utf-8,' + encodeURIComponent(result)); + var newDate = new Date(); + var filename = "Tranga_Logs_" + newDate.today() + "_" + newDate.timeNow() + ".log"; + element.setAttribute('download', filename); + element.click(); + }); +} + +//Following date-time code taken from: https://stackoverflow.com/questions/10211145/getting-current-date-and-time-in-javascript +// For todays date; +Date.prototype.today = function () { + return ((this.getDate() < 10)?"0":"") + this.getDate() +"/"+(((this.getMonth()+1) < 10)?"0":"") + (this.getMonth()+1) +"/"+ this.getFullYear(); +} + +// For the time now +Date.prototype.timeNow = function () { + return ((this.getHours() < 10)?"0":"") + this.getHours() +"_"+ ((this.getMinutes() < 10)?"0":"") + this.getMinutes() +"_"+ ((this.getSeconds() < 10)?"0":"") + this.getSeconds(); +} + +//Komga +function UpdateKomga(komgaUrl, komgaAuth){ + var uri = `${apiUri}/LibraryConnectors/Update?libraryConnector=Komga&komgaUrl=${komgaUrl}&komgaAuth=${komgaAuth}`; + PostData(uri); +} + +function ResetKomga(){ + var uri = `${apiUri}/LibraryConnectors/Reset?libraryConnector=Komga`; + PostData(uri); +} + +function TestKomga(komgaUrl, komgaAuth){ + var uri = `${apiUri}/LibraryConnectors/Test?libraryConnector=Komga&komgaUrl=${komgaUrl}&komgaAuth=${komgaAuth}`; + PostData(uri); +} + + +//Kavita +function UpdateKavita(kavitaUrl, kavitaUsername, kavitaPassword){ + var uri = `${apiUri}/LibraryConnectors/Update?libraryConnector=Kavita&kavitaUrl=${kavitaUrl}&kavitaUsername=${kavitaUsername}&kavitaPassword=${kavitaPassword}`; + PostData(uri); +} + +function ResetKavita(){ + var uri = `${apiUri}/LibraryConnectors/Reset?libraryConnector=Kavita`; + PostData(uri); +} + +function TestKavita(kavitaUrl, kavitaUsername, kavitaPassword){ + var uri = `${apiUri}/LibraryConnectors/Test?libraryConnector=Kavita&kavitaUrl=${kavitaUrl}&kavitaUsername=${kavitaUsername}&kavitaPassword=${kavitaPassword}`; + PostData(uri); +} + +//Gotify +function UpdateGotify(gotifyUrl, gotifyAppToken){ + var uri = `${apiUri}/NotificationConnectors/Update?notificationConnector=Gotify&gotifyUrl=${gotifyUrl}&gotifyAppToken=${gotifyAppToken}`; + PostData(uri); +} + +function ResetGotify(){ + var uri = `${apiUri}/NotificationConnectors/Reset?notificationConnector=Gotify`; + PostData(uri); +} + +function TestGotify(gotifyUrl, gotifyAppToken){ + var uri = `${apiUri}/NotificationConnectors/Test?notificationConnector=Gotify&gotifyUrl=${gotifyUrl}&gotifyAppToken=${gotifyAppToken}`; + PostData(uri); +} + +//LunaSea +function UpdateLunaSea(lunaseaWebhook){ + var uri = `${apiUri}/NotificationConnectors/Update?notificationConnector=LunaSea&lunaseaWebhook=${lunaseaWebhook}`; + PostData(uri); +} + +function ResetLunaSea(){ + var uri = `${apiUri}/NotificationConnectors/Reset?notificationConnector=LunaSea`; + PostData(uri); +} + +function TestLunaSea(lunaseaWebhook){ + var uri = `${apiUri}/NotificationConnectors/Test?notificationConnector=LunaSea&lunaseaWebhook=${lunaseaWebhook}`; + PostData(uri); +} + +//Ntfy +function UpdateNtfy(ntfyEndpoint, ntfyAuth){ + var uri = `${apiUri}/NotificationConnectors/Update?notificationConnector=Ntfy&ntfyUrl=${ntfyEndpoint}&ntfyAuth=${ntfyAuth}`; + PostData(uri); +} + +function ResetNtfy(){ + var uri = `${apiUri}/NotificationConnectors/Reset?notificationConnector=Ntfy`; + PostData(uri); +} + +function TestNtfy(ntfyEndpoint, ntfyAuth){ + var uri = `${apiUri}/NotificationConnectors/Test?notificationConnector=Ntfy&ntfyUrl=${ntfyEndpoint}&ntfyAuth=${ntfyAuth}`; + PostData(uri); +} + +function UpdateUserAgent(userAgent){ + var uri = `${apiUri}/Settings/userAgent?userAgent=${userAgent}`; + PostData(uri); +} + +function UpdateRateLimit(byteValue, rateLimit) { + var uri = `${apiUri}/Settings/customRequestLimit?requestType=${byteValue}&requestsPerMinute=${rateLimit}`; + PostData(uri); +} + +function RemoveJob(jobId){ + var uri = `${apiUri}/Jobs?jobId=${jobId}`; + DeleteData(uri); +} + +function CancelJob(jobId){ + var uri = `${apiUri}/Jobs/Cancel?jobId=${jobId}`; + PostData(uri); +} + +async function GetLogmessages(count){ + var uri = `${apiUri}/LogMessages?count=${count}`; + let json = await GetData(uri); + console.log(json); + return json; } \ No newline at end of file diff --git a/Website/connector-icons/komga.svg b/Website/connector-icons/komga.svg index af9f28d..3e21118 100644 --- a/Website/connector-icons/komga.svg +++ b/Website/connector-icons/komga.svg @@ -1,113 +1,113 @@ - - - - - - image/svg+xml - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Website/connector-icons/ntfy.svg b/Website/connector-icons/ntfy.svg index 8530901..1f52d4b 100644 --- a/Website/connector-icons/ntfy.svg +++ b/Website/connector-icons/ntfy.svg @@ -1,40 +1,40 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Website/index.html b/Website/index.html index 1bb8c4c..e16f460 100644 --- a/Website/index.html +++ b/Website/index.html @@ -1,301 +1,301 @@ - - - - - Tranga - - - - - - - - - - website image is Blahaj - Tranga - - - filterFunnel - settingscog - - - - - Filter by: - × - - - - - - - Clear Filter - - - - -
- -
-

Check your Settings > API-URI

-
- -
-

+

-
- - cover - - Sample - Best Manga there is - - -
- - - -
- - -
-
-
- - - - - - Settings - × - - - - - - - - - - - - - -
- Apply Settings -
-
- -
-
- - - - - cover - - Best Manga there is - A Manga - Glax - - An interesting description. The description is very intriguing, yet wholesome. - - - Start Job ▶️ - Cancel Job ❌ - Delete Job 🗑️ - Monitor ➕ - Download Chapter 📥 - - - - - - - - - - Jobs - × - - - - - - - - - - - - - - -
- -
-
- running
0
-
-
- queue
0
-
-

Made with Blåhaj 🦈

-
-
- - - - + + + + + Tranga + + + + + + + + + + website image is Blahaj + Tranga + + + filterFunnel + settingscog + + + + + Filter by: + × + + + + + + + Clear Filter + + + + +
+ +
+

Check your Settings > API-URI

+
+ +
+

+

+
+ + cover + + Sample + Best Manga there is + + +
+ + + +
+ + +
+
+
+ + + + + + Settings + × + + + + + + + + + + + + + +
+ Apply Settings +
+
+ +
+
+ + + + + cover + + Best Manga there is + A Manga + Glax + + An interesting description. The description is very intriguing, yet wholesome. + + + Start Job ▶️ + Cancel Job ❌ + Delete Job 🗑️ + Monitor ➕ + Download Chapter 📥 + + + + + + + + + + Jobs + × + + + + + + + + + + + + + + +
+ +
+
+ running
0
+
+
+ queue
0
+
+

Made with Blåhaj 🦈

+
+
+ + + + \ No newline at end of file diff --git a/Website/interaction.js b/Website/interaction.js index 889ade9..27442ed 100644 --- a/Website/interaction.js +++ b/Website/interaction.js @@ -1,885 +1,885 @@ -let monitoringJobsCount = 0; -let runningJobs = []; -let waitingJobs = []; -let notificationConnectorTypes = []; -let libraryConnectorTypes = []; -let selectedManga; -let selectedJob; -let searchMatch; - -let connectorMatch = []; -let connectorNameMatch; -let statusMatch = []; -let statusNameMatch = []; - -const searchBox = document.querySelector("#searchbox"); -const settingsPopup = document.querySelector("#settingsPopup"); -const filterBox = document.querySelector("#filterBox"); -const settingsCog = document.querySelector("#settingscog"); -const filterFunnel = document.querySelector("#filterFunnel"); -const tasksContent = document.querySelector("content"); -const createMonitorTaskButton = document.querySelector("#createMonitoJobButton"); -const createDownloadChapterTaskButton = document.querySelector("#createDownloadChapterJobButton"); -const startJobButton = document.querySelector("#startJobButton"); -const cancelJobButton = document.querySelector("#cancelJobButton"); -const deleteJobButton = document.querySelector("#deleteJobButton"); - -//Manga viewer popup -const mangaViewerPopup = document.querySelector("#publicationViewerPopup"); -const mangaViewerWindow = document.querySelector("publication-viewer"); -const mangaViewerDescription = document.querySelector("#publicationViewerDescription"); -const mangaViewerName = document.querySelector("#publicationViewerName"); -const mangaViewerTags = document.querySelector("#publicationViewerTags"); -const mangaViewerAuthor = document.querySelector("#publicationViewerAuthor"); -const mangaViewCover = document.querySelector("#pubviewcover"); - -//General Rate Limits -const defaultRL = document.querySelector("#defaultRL"); -const coverRL = document.querySelector("#coverRL"); -const imageRL = document.querySelector("#imageRL"); -const infoRL = document.querySelector("#infoRL"); - -//MangaDex Rate Limits -const mDexAuthorRL = document.querySelector("#mDexAuthorRL"); -const mDexFeedRL = document.querySelector("#mDexFeedRL"); -const mDexImageRL = document.querySelector("#mDexImageRL"); - -//Komga -const settingKomgaUrl = document.querySelector("#komgaUrl"); -const settingKomgaUser = document.querySelector("#komgaUsername"); -const settingKomgaPass = document.querySelector("#komgaPassword"); - -//Kavita -const settingKavitaUrl = document.querySelector("#kavitaUrl"); -const settingKavitaUser = document.querySelector("#kavitaUsername"); -const settingKavitaPass = document.querySelector("#kavitaPassword"); - -//Gotify -const settingGotifyUrl = document.querySelector("#gotifyUrl"); -const settingGotifyAppToken = document.querySelector("#gotifyAppToken"); - -//Lunasea -const settingLunaseaWebhook = document.querySelector("#lunaseaWebhook"); - -//Ntfy -const settingNtfyEndpoint = document.querySelector("#ntfyEndpoint"); -const settingNtfyAuth = document.querySelector("#ntfyAuth"); - -//Connector Configured -const settingKomgaConfigured = document.querySelector("#komgaConfigured"); -const settingKavitaConfigured = document.querySelector("#kavitaConfigured"); -const settingGotifyConfigured = document.querySelector("#gotifyConfigured"); -const settingLunaseaConfigured = document.querySelector("#lunaseaConfigured"); -const settingNtfyConfigured = document.querySelector("#ntfyConfigured"); - -const settingUserAgent = document.querySelector("#userAgent"); -const settingApiUri = document.querySelector("#settingApiUri"); -const settingCSSStyle = document.querySelector('#cssStyle'); -const newMangaPopup = document.querySelector("#newMangaPopup"); -const newMangaConnector = document.querySelector("#newMangaConnector"); -const newMangaTitle = document.querySelector("#newMangaTitle"); -const newMangaResult = document.querySelector("#newMangaResult"); -const newMangaTranslatedLanguage = document.querySelector("#newMangaTranslatedLanguage"); - -//Jobs -const jobsRunningTag = document.querySelector("#jobsRunningTag"); -const jobsQueuedTag = document.querySelector("#jobsQueuedTag"); -const loaderdiv = document.querySelector('#loaderdiv'); -const jobStatusView = document.querySelector("#jobStatusView"); -const jobStatusRunning = document.querySelector("#jobStatusRunning"); -const jobStatusWaiting = document.querySelector("#jobStatusWaiting"); - -function Setup(){ - Ping().then((ret) => { - loaderdiv.style.display = 'none'; - - GetAvailableNotificationConnectors().then((json) => { - //console.log(json); - json.forEach(connector => { - notificationConnectorTypes[connector.Key] = connector.Value; - }); - }); - - GetAvailableLibraryConnectors().then((json) => { - //console.log(json); - json.forEach(connector => { - libraryConnectorTypes[connector.Key] = connector.Value; - }); - }); - - GetAvailableControllers().then((json) => { - //console.log(json); - newMangaConnector.replaceChildren(); - connectorFilterBox = document.querySelector("#connectorFilterBox"); - connectorFilterBox.replaceChildren(); - json.forEach(connector => { - //Add the connector to the New Manga dropdown - var option = document.createElement('option'); - option.value = connector; - option.innerText = connector; - newMangaConnector.appendChild(option); - - //Add the connector to the filter box - connectorFilter = document.createElement('connector-name'); - connectorFilter.innerText = connector; - connectorFilter.className = "pill"; - connectorFilter.style.backgroundColor = stringToColour(connector); - - connectorFilter.addEventListener("click", (event) => { - ToggleFilterConnector(connector, event); - }); - connectorFilterBox.appendChild(connectorFilter); - }); - }); - - //Add the publication status options to the filter bar - publicationStatusOptions = ["Ongoing", "Completed", "On Hiatus", "Cancelled", "Upcoming", "Status Unavailable"]; - statusFilterBox = document.querySelector("#statusFilterBox"); - statusFilterBox.replaceChildren(); - publicationStatusOptions.forEach(publicationStatus => { - var releaseStatus = document.createElement('status-filter'); - releaseStatus.innerText = publicationStatus; - releaseStatus.setAttribute("release-status", publicationStatus); - releaseStatus.addEventListener("click", (event) => { - ToggleFilterStatus(publicationStatus, event); - }); - - statusFilterBox.appendChild(releaseStatus); - }); - - ResetContent(); - UpdateJobs(); - GetSettings().then((json) => { - //console.log(json); - settingApiUri.placeholder = apiUri; - }); - GetRateLimits().then((json) => { - defaultRL.placeholder = json.Default + ' Requests/Minute'; - coverRL.placeholder = json.MangaCover + ' Requests/Minute'; - imageRL.placeholder = json.MangaImage + ' Requests/Minute'; - infoRL.placeholder = json.MangaInfo + ' Requests/Minute'; - mDexAuthorRL.placeholder = json.MangaDexAuthor + ' Requests/Minute'; - mDexFeedRL.placeholder = json.MangaDexFeed + ' Requests/Minute'; - mDexImageRL.placeholder = json.MangaDexImage + ' Requests/Minute'; - }); - - //If the cssStyle key isn't in the local storage of the browser, then set the css style to the default and load the page - //Otherwise get the style key from storage and set it. - if (!localStorage.getItem('cssStyle')) { - localStorage.setItem('cssStyle', 'card_compact'); - document.getElementById('librarystyle').setAttribute('href', 'styles/' + localStorage.getItem('cssStyle') + '.css'); - document.getElementById('card_compact').selected = true; - } else { - css_style = localStorage.getItem('cssStyle'); - document.getElementById('librarystyle').setAttribute('href', 'styles/' + css_style + '.css'); - document.getElementById(css_style).selected = true; - } - setInterval(() => { - UpdateJobs(); - }, 1000); - }); - //Clear the previous values if any exist. - searchBox.value = ""; - connectorMatch.length = 0; - statusMatch.length = 0; -} -Setup(); - -function ToggleFilterConnector(connector, event) { - //console.log("Initial Array:"); - //console.log(connectorMatch); - if (connectorMatch.includes(connector)) { - idx = connectorMatch.indexOf(connector); - connectorMatch.splice(idx, 1); - event.target.style.outline = 'none'; - event.target.style.outlineOffset = "0px"; - } else { - connectorMatch.push(connector); - event.target.style.outline = '4px solid var(--secondary-color)'; - event.target.style.outlineOffset = '3px'; - } - //console.log("Final Array"); - //console.log(connectorMatch); - FilterResults(); -} - -function ToggleFilterStatus(status, event) { - //console.log("Initial Array:"); - //console.log(statusMatch); - if (statusMatch.includes(status)) { - idx = statusMatch.indexOf(status); - statusMatch.splice(idx, 1); - event.target.style.outline = 'none'; - event.target.style.outlineOffset = "0px"; - } else { - statusMatch.push(status); - event.target.style.outline = '4px solid var(--secondary-color)'; - event.target.style.outlineOffset = '3px'; - } - //console.log("Final Array"); - //console.log(statusMatch); - FilterResults(); -} - -function ClearFilter() { - searchBox.value = ""; - statusMatch.length = 0; - connectorMatch.length = 0; - FilterResults(); - - //Get rid of the outlines - connectorFilterBox = document.querySelector("#connectorFilterBox"); - connectorFilterBox.childNodes.forEach(connector => { - if (connector.nodeName.toLowerCase() == 'connector-name') { - connector.style.outline = 'none'; - connector.style.outlineOffset = "0px"; - } - }); - - statusFilterBox = document.querySelector("#statusFilterBox"); - statusFilterBox.childNodes.forEach(publicationStatus => { - if (publicationStatus.nodeName.toLowerCase() == 'status-filter') { - publicationStatus.style.outline = 'none'; - publicationStatus.style.outlineOffset = "0px"; - } - }); -} - -settingCSSStyle.addEventListener("change", (event) => { - localStorage.setItem('cssStyle', settingCSSStyle.value); - document.getElementById('librarystyle').setAttribute('href', 'styles/' + localStorage.getItem('cssStyle') + '.css'); -}); - -function ResetContent(){ - //Delete everything - tasksContent.replaceChildren(); - - //Add "Add new Task" Button - var add = document.createElement("div"); - add.setAttribute("id", "addPublication") - var plus = document.createElement("p"); - plus.innerText = "+"; - add.appendChild(plus); - add.addEventListener("click", () => ShowNewMangaSearch()); - tasksContent.appendChild(add); - - //Populate with the monitored mangas - GetMonitorJobs().then((json) => { - //console.log(json); - json.forEach(job => { - var mangaView = CreateManga(job.manga, job.mangaConnector.name); - mangaView.addEventListener("click", (event) => { - ShowMangaWindow(job, job.manga, event, false); - }); - tasksContent.appendChild(mangaView); - }); - monitoringJobsCount = json.length; - }); -} - -function ShowNewMangaSearch(){ - newMangaTitle.value = ""; - newMangaPopup.style.display = "block"; - newMangaResult.replaceChildren(); -} - -newMangaTitle.addEventListener("keypress", (event) => { if(event.key === "Enter") GetNewMangaItems();}); - - - - -function GetNewMangaItems(){ - if(newMangaTitle.value.length < 4) - return; - - newMangaResult.replaceChildren(); - newMangaConnector.disabled = true; - newMangaTitle.disabled = true; - newMangaTranslatedLanguage.disabled = true; - GetPublicationFromConnector(newMangaConnector.value, newMangaTitle.value).then((json) => { - //console.log(json); - if(json.length > 0) - newMangaResult.style.display = "flex"; - json.forEach(result => { - var mangaElement = CreateManga(result, newMangaConnector.value) - newMangaResult.appendChild(mangaElement); - mangaElement.addEventListener("click", (event) => { - ShowMangaWindow(null, result, event, true); - }); - }); - - newMangaConnector.disabled = false; - newMangaTitle.disabled = false; - newMangaTranslatedLanguage.disabled = false; - }); -} - -//Returns a new "Publication" Item to display in the jobs section -function CreateManga(manga, connector){ - //Create a new publication and set an internal ID - var mangaElement = document.createElement('publication'); - mangaElement.id = GetValidSelector(manga.internalId); - - //Append the cover image to the publication - var mangaImage = document.createElement('img'); - mangaImage.src = GetCoverUrl(manga.internalId); - mangaElement.appendChild(mangaImage); - -//Append the publication information to the publication - //console.log(manga); - var info = document.createElement('publication-information'); - var connectorName = document.createElement('connector-name'); - connectorName.innerText = connector; - connectorName.className = "pill"; - connectorName.style.backgroundColor = stringToColour(connector); - info.appendChild(connectorName); - - var mangaName = document.createElement('publication-name'); - mangaName.innerText = manga.sortName; - - //Create the publication status indicator - var releaseStatus = document.createElement('publication-status'); - releaseStatus.setAttribute("release-status", manga.releaseStatus); - switch(manga.releaseStatus){ - case 0: - releaseStatus.setAttribute("release-status", "Ongoing"); - break; - case 1: - releaseStatus.setAttribute("release-status", "Completed"); - break; - case 2: - releaseStatus.setAttribute("release-status", "On Hiatus"); - break; - case 3: - releaseStatus.setAttribute("release-status", "Cancelled"); - break; - case 4: - releaseStatus.setAttribute("release-status", "Upcoming"); - break; - default: - releaseStatus.setAttribute("release-status", "Status Unavailable"); - break; - } - - info.appendChild(mangaName); - mangaElement.appendChild(info); - mangaElement.appendChild(releaseStatus); //Append the release status indicator to the publication element - return mangaElement; -} - -createMonitorJobButton.addEventListener("click", () => { - CreateMonitorJob(newMangaConnector.value, selectedManga.internalId, newMangaTranslatedLanguage.value); - UpdateJobs(); - mangaViewerPopup.style.display = "none"; -}); -startJobButton.addEventListener("click", () => { - StartJob(selectedJob.id); - mangaViewerPopup.style.display = "none"; -}); -cancelJobButton.addEventListener("click", () => { - CancelJob(selectedJob.id); - mangaViewerPopup.style.display = "none"; -}); -deleteJobButton.addEventListener("click", () => { - RemoveJob(selectedJob.id); - UpdateJobs(); - mangaViewerPopup.style.display = "none"; -}); - -function ShowMangaWindow(job, manga, event, add){ - selectedManga = manga; - selectedJob = job; - //Show popup - mangaViewerPopup.style.display = "block"; - - //Set position to mouse-position - if(event.clientY < window.innerHeight - mangaViewerWindow.offsetHeight) - mangaViewerWindow.style.top = `${event.clientY}px`; - else - mangaViewerWindow.style.top = `${event.clientY - mangaViewerWindow.offsetHeight}px`; - - if(event.clientX < window.innerWidth - mangaViewerWindow.offsetWidth) - mangaViewerWindow.style.left = `${event.clientX}px`; - else - mangaViewerWindow.style.left = `${event.clientX - mangaViewerWindow.offsetWidth}px`; - - //Edit information inside the window - mangaViewerName.innerText = manga.sortName; - mangaViewerTags.innerText = manga.tags.join(", "); - mangaViewerDescription.innerText = manga.description; - mangaViewerAuthor.innerText = manga.authors.join(','); - mangaViewCover.src = GetCoverUrl(manga.internalId); - toEditId = manga.internalId; - - //Check what action should be listed - if(add){ - createMonitorJobButton.style.display = "initial"; - createDownloadChapterJobButton.style.display = "none"; - cancelJobButton.style.display = "none"; - startJobButton.style.display = "none"; - deleteJobButton.style.display = "none"; - } - else{ - createMonitorJobButton.style.display = "none"; - createDownloadChapterJobButton.style.display = "none"; - cancelJobButton.style.display = "initial"; - startJobButton.style.display = "initial"; - deleteJobButton.style.display = "initial"; - } -} - -function HidePublicationPopup(){ - publicationViewerPopup.style.display = "none"; -} - -searchBox.addEventListener("keyup", () => FilterResults()); -//Filter shown jobs -function FilterResults(){ - //For each publication - tasksContent.childNodes.forEach(publication => { - //If the search box isn't empty check that the title contains the searchbox content. If it does then - //'searchMatch' is true and the manga is shown. If the search box is empty, then consider this field - //to be true anyways. - if (searchBox.value.length > 0) { - publication.childNodes.forEach(item => { - if (item.nodeName.toLowerCase() == "publication-information"){ - item.childNodes.forEach(information => { - if (information.nodeName.toLowerCase() == "publication-name") { - if (information.textContent.toLowerCase().includes(searchBox.value.toLowerCase())){ - searchMatch = 1; - } else { - searchMatch = 0; - } - } - }); - } - }); - } else { - searchMatch = 1; - } - - //If the array connectorMatch isn't empty then check that the connector matches one of the ones - //in the array - if (connectorMatch.length > 0) { - publication.childNodes.forEach(item => { - if (item.nodeName.toLowerCase() == "publication-information"){ - item.childNodes.forEach(information => { - if (information.nodeName.toLowerCase() == "connector-name") { - if (connectorMatch.includes(information.textContent)){ - connectorNameMatch = 1; - } else { - connectorNameMatch = 0; - } - } - }); - } - }); - } else { - connectorNameMatch = 1; - } - - //If the array statusMatch isn't empty then check that the status matches one of the ones - //in the array - if (statusMatch.length > 0) { - publication.childNodes.forEach(item => { - if (item.nodeName.toLowerCase() == "publication-status"){ - if (statusMatch.includes(item.getAttribute('release-status'))) { - statusNameMatch = 1; - } else { - statusNameMatch = 0; - } - } - }); - } else { - statusNameMatch = 1; - } - - //If all of the filtering conditions are met then show the manga, otherwise hide it. - if (searchMatch && connectorNameMatch && statusNameMatch) { - publication.style.display = 'initial'; - } else { - publication.style.display = 'none'; - } - }); -} - -settingsCog.addEventListener("click", () => { - OpenSettings(); - settingsPopup.style.display = "flex"; -}); - -filterFunnel.addEventListener("click", () => { - filterBox.classList.toggle("animate"); -}); - -settingKomgaUrl.addEventListener("keypress", (event) => { if(event.key === "Enter") UpdateSettings(); }); -settingKomgaUser.addEventListener("keypress", (event) => { if(event.key === "Enter") UpdateSettings(); }); -settingKomgaPass.addEventListener("keypress", (event) => { if(event.key === "Enter") UpdateSettings(); }); -settingKavitaUrl.addEventListener("keypress", (event) => { if(event.key === "Enter") UpdateSettings(); }); -settingKavitaUser.addEventListener("keypress", (event) => { if(event.key === "Enter") UpdateSettings(); }); -settingKavitaPass.addEventListener("keypress", (event) => { if(event.key === "Enter") UpdateSettings(); }); -settingGotifyUrl.addEventListener("keypress", (event) => { if(event.key === "Enter") UpdateSettings(); }); -settingGotifyAppToken.addEventListener("keypress", (event) => { if(event.key === "Enter") UpdateSettings(); }); -settingLunaseaWebhook.addEventListener("keypress", (event) => { if(event.key === "Enter") UpdateSettings(); }); -settingNtfyEndpoint.addEventListener("keypress", (event) => { if(event.key === "Enter") UpdateSettings(); }); -settingNtfyAuth.addEventListener("keypress", (event) => { if(event.key === "Enter") UpdateSettings(); }); -settingUserAgent.addEventListener("keypress", (event) => { if(event.key === "Enter") UpdateSettings(); }); -settingApiUri.addEventListener("keypress", (event) => { if(event.key === "Enter") UpdateSettings(); }); - -defaultRL.addEventListener("keypress", (event) => { if(event.key === "Enter") UpdateSettings();}); -coverRL.addEventListener("keypress", (event) => { if(event.key === "Enter") UpdateSettings();}); -imageRL.addEventListener("keypress", (event) => { if(event.key === "Enter") UpdateSettings();}); -infoRL.addEventListener("keypress", (event) => { if(event.key === "Enter") UpdateSettings();}); -mDexAuthorRL.addEventListener("keypress", (event) => { if(event.key === "Enter") UpdateSettings();}); -mDexFeedRL.addEventListener("keypress", (event) => { if(event.key === "Enter") UpdateSettings();}); -mDexImageRL.addEventListener("keypress", (event) => { if(event.key === "Enter") UpdateSettings();}); - - -function OpenSettings(){ - settingGotifyConfigured.setAttribute("configuration", "Not Configured"); - settingLunaseaConfigured.setAttribute("configuration", "Not Configured"); - settingNtfyConfigured.setAttribute("configuration", "Not Configured"); - settingKavitaConfigured.setAttribute("configuration", "Not Configured"); - settingKomgaConfigured.setAttribute("configuration", "Not Configured"); - settingKomgaUrl.value = ""; - settingKomgaUser.value = ""; - settingKomgaPass.value = ""; - settingKavitaUrl.value = ""; - settingKavitaUser.value = ""; - settingKavitaPass.value = ""; - settingGotifyUrl.value = ""; - settingGotifyAppToken.value = ""; - settingLunaseaWebhook.value = ""; - settingNtfyAuth.value = ""; - settingNtfyEndpoint.value = ""; - settingUserAgent.value = ""; - settingApiUri.value = ""; - defaultRL.value = ""; - coverRL.value = ""; - imageRL.value = ""; - infoRL.value = ""; - mDexAuthorRL.value = ""; - mDexFeedRL.value = ""; - mDexImageRL.value = ""; - - GetSettings().then((json) => { - //console.log(json); - settingApiUri.value = apiUri; - settingUserAgent.value = json.userAgent; - //console.log(json.styleSheet); - }); - GetRateLimits().then((json) => { - defaultRL.placeholder = json.Default + ' Requests/Minute'; - coverRL.placeholder = json.MangaCover + ' Requests/Minute'; - imageRL.placeholder = json.MangaImage + ' Requests/Minute'; - infoRL.placeholder = json.MangaInfo + ' Requests/Minute'; - mDexAuthorRL.placeholder = json.MangaDexAuthor + ' Requests/Minute'; - mDexFeedRL.placeholder = json.MangaDexFeed + ' Requests/Minute'; - mDexImageRL.placeholder = json.MangaDexImage + ' Requests/Minute'; - }); - GetLibraryConnectors().then((json) => { - //console.log(json); - json.forEach(connector => { - switch(libraryConnectorTypes[connector.libraryType]){ - case "Kavita": - settingKavitaConfigured.setAttribute("configuration", "Active"); - settingKavitaUrl.value = connector.baseUrl; - settingKavitaUser.value = "***"; - settingKavitaPass.value = "***"; - break; - case "Komga": - settingKomgaConfigured.setAttribute("configuration", "Active"); - settingKomgaUrl.value = connector.baseUrl; - settingKomgaUser.value = "***"; - settingKomgaPass.value = "***"; - break; - default: - console.log("Unknown type"); - console.log(connector); - break; - } - }); - }); - GetNotificationConnectors().then((json) => { - json.forEach(connector => { - switch(notificationConnectorTypes[connector.notificationConnectorType]){ - case "Gotify": - settingGotifyUrl.value = connector.endpoint; - settingGotifyAppToken.value = "***"; - settingGotifyConfigured.setAttribute("configuration", "Active"); - break; - case "LunaSea": - settingLunaseaConfigured.setAttribute("configuration", "Active"); - settingLunaseaWebhook.value = connector.id; - break; - case "Ntfy": - settingNtfyConfigured.setAttribute("configuration", "Active"); - settingNtfyEndpoint.value = connector.endpoint; - settingNtfyAuth.value = "***"; - break; - default: - console.log("Unknown type"); - console.log(connector); - break; - } - }); - }); -} - -//Functions for clearing/resetting connectors in the settings pop-up -function ClearKomga(){ - settingKomgaUrl.value = ""; - settingKomgaUser.value = ""; - settingKomgaPass.value = ""; - settingKomgaConfigured.setAttribute("configuration", "Not Configured"); - ResetKomga(); -} - -function ClearKavita(){ - settingKavitaUrl.value = ""; - settingKavitaUser.value = ""; - settingKavitaPass.value = ""; - settingKavitaConfigured.setAttribute("configuration", "Not Configured"); - ResetKavita(); -} - -function ClearGotify(){ - settingGotifyUrl.value = ""; - settingGotifyAppToken.value = "" - settingGotifyConfigured.setAttribute("configuration", "Not Configured"); - ResetGotify(); -} - -function ClearLunasea(){ - settingLunaseaWebhook.value = ""; - settingLunaseaConfigured.setAttribute("configuration", "Not Configured"); - ResetLunaSea(); -} - -function ClearNtfy(){ - settingNtfyEndpoint.value = ""; - settingNtfyAuth.value = ""; - settingNtfyConfigured.setAttribute("configuration", "Not Configured"); - ResetNtfy(); -} - -function UpdateSettings(){ - if(settingApiUri.value != ""){ - apiUri = settingApiUri.value; - setCookie("apiUri", apiUri); - Setup(); - } - - if(settingKomgaUrl.value != "" && - settingKomgaUser.value != "" && - settingKomgaPass.value != ""){ - UpdateKomga(settingKomgaUrl.value, utf8_to_b64(`${settingKomgaUser.value}:${settingKomgaPass.value}`)); - } - - if(settingKavitaUrl.value != "" && - settingKavitaUser.value != "" && - settingKavitaPass.value != ""){ - UpdateKavita(settingKavitaUrl.value, settingKavitaUser.value, settingKavitaPass.value); - } - - if(settingGotifyUrl.value != "" && - settingGotifyAppToken.value != ""){ - UpdateGotify(settingGotifyUrl.value, settingGotifyAppToken.value); - } - - if(settingLunaseaWebhook.value != ""){ - UpdateLunaSea(settingLunaseaWebhook.value); - } - - if(settingNtfyEndpoint.value != "" && - settingNtfyAuth.value != ""){ - UpdateNtfy(settingNtfyEndpoint.value, settingNtfyAuth.value); - } - - if(settingUserAgent.value != ""){ - UpdateUserAgent(settingUserAgent.value); - } - - if (defaultRL.value != "") { - UpdateRateLimit(0, defaultRL.value); - } - - if (coverRL.value != "") { - UpdateRateLimit(3, coverRL.value); - } - - if (imageRL.value != "") { - UpdateRateLimit(2, imageRL.value); - } - - if (infoRL.value != "") { - UpdateRateLimit(6, infoRL.value); - } - - if (mDexAuthorRL.value != "") { - UpdateRateLimit(5, mDexAuthorRL.value); - } - - if (mDexFeedRL.value != "") { - UpdateRateLimit(1, mDexFeedRL.value); - } - - if (mDexImageRL.value != "") { - UpdateRateLimit(5, mDexImageRL.value); - } - - setTimeout(() => { - OpenSettings(); - Setup(); - }, 100) -} - -function utf8_to_b64(str) { - return window.btoa(unescape(encodeURIComponent( str ))); -} - -function UpdateJobs(){ - - GetMonitorJobs().then((json) => { - if(monitoringJobsCount != json.length){ - ResetContent(); - monitoringJobsCount = json.length; - } - }); - - //Get the jobs that are waiting in the queue - GetWaitingJobs().then((json) => { - jobsQueuedTag.innerText = json.length; - - var nowWaitingJobs = []; - - json.forEach(job => { - if(!waitingJobs.includes(GetValidSelector(job.id))){ - var jobDom = createJob(job); - jobStatusWaiting.appendChild(jobDom); - } - nowWaitingJobs.push(GetValidSelector(job.id)); - }); - waitingJobs = nowWaitingJobs; - }); - - jobStatusWaiting.childNodes.forEach(child => { - if(!waitingJobs.includes(child.id)) - jobStatusWaiting.removeChild(child); - }); - - //Get currently running jobs - GetRunningJobs().then((json) => { - jobsRunningTag.innerText = json.length; - - var nowRunningJobs = []; - - json.forEach(job => { - if(!runningJobs.includes(GetValidSelector(job.id))){ - var jobDom = createJob(job); - jobStatusRunning.appendChild(jobDom); - } - nowRunningJobs.push(GetValidSelector(job.id)); - UpdateJobProgress(job.id); - }); - - runningJobs = nowRunningJobs; - }); - - jobStatusRunning.childNodes.forEach(child => { - if(!runningJobs.includes(child.id)) - jobStatusRunning.removeChild(child); - }); -} - -function createJob(jobjson){ - var manga; - if(jobjson.chapter != null) - manga = jobjson.chapter.parentManga; - else if(jobjson.manga != null) - manga = jobjson.manga; - else return null; - - - var wrapper = document.createElement("div"); - wrapper.className = "section-item"; - wrapper.id = GetValidSelector(jobjson.id); - - var image = document.createElement("img"); - image.className = "jobImage"; - image.src = GetCoverUrl(manga.internalId); - wrapper.appendChild(image); - - var details = document.createElement("div"); - details.className = 'jobDetails'; - - var title = document.createElement("span"); - title.className = "jobTitle"; - if(jobjson.chapter != null) - title.innerText = `${manga.sortName} - ${jobjson.chapter.fileName}`; - else if(jobjson.manga != null) - title.innerText = manga.sortName; - details.appendChild(title); - - var progressBar = document.createElement("progress"); - progressBar.className = "jobProgressBar"; - progressBar.id = `jobProgressBar${GetValidSelector(jobjson.id)}`; - details.appendChild(progressBar); - - var progressSpan = document.createElement("span"); - progressSpan.className = "jobProgressSpan"; - progressSpan.id = `jobProgressSpan${GetValidSelector(jobjson.id)}`; - progressSpan.innerText = "Pending..."; - details.appendChild(progressSpan); - - var cancelSpan = document.createElement("span"); - cancelSpan.className = "jobCancel"; - cancelSpan.innerText = "Cancel"; - cancelSpan.addEventListener("click", () => CancelJob(jobjson.id)); - details.appendChild(cancelSpan); - - wrapper.appendChild(details); - - return wrapper; -} - -function ShowJobQueue(){ - jobStatusView.style.display = "initial"; -} - -function UpdateJobProgress(jobId){ - GetProgress(jobId).then((json) => { - var progressBar = document.querySelector(`#jobProgressBar${GetValidSelector(jobId)}`); - var progressSpan = document.querySelector(`#jobProgressSpan${GetValidSelector(jobId)}`); - if(progressBar != null && json.progress != 0){ - progressBar.value = json.progress; - } - if(progressSpan != null){ - var percentageStr = "0%"; - var timeleftStr = "00:00:00"; - if(json.progress != 0){ - percentageStr = Intl.NumberFormat("en-US", { style: "percent"}).format(json.progress); - timeleftStr = json.timeRemaining.split('.')[0]; - } - progressSpan.innerText = `${percentageStr} ${timeleftStr}`; - } - }); -} - -function GetValidSelector(str){ - var clean = [...str.matchAll(/[a-zA-Z0-9]*-*_*/g)]; - return clean.join(''); -} - -const stringToColour = (str) => { - let hash = 0; - str.split('').forEach(char => { - hash = char.charCodeAt(0) + ((hash << 5) - hash) - }) - let colour = '#' - for (let i = 0; i < 3; i++) { - const value = (hash >> (i * 8)) & 0xff - colour += value.toString(16).padStart(2, '0') - } - return colour +let monitoringJobsCount = 0; +let runningJobs = []; +let waitingJobs = []; +let notificationConnectorTypes = []; +let libraryConnectorTypes = []; +let selectedManga; +let selectedJob; +let searchMatch; + +let connectorMatch = []; +let connectorNameMatch; +let statusMatch = []; +let statusNameMatch = []; + +const searchBox = document.querySelector("#searchbox"); +const settingsPopup = document.querySelector("#settingsPopup"); +const filterBox = document.querySelector("#filterBox"); +const settingsCog = document.querySelector("#settingscog"); +const filterFunnel = document.querySelector("#filterFunnel"); +const tasksContent = document.querySelector("content"); +const createMonitorTaskButton = document.querySelector("#createMonitoJobButton"); +const createDownloadChapterTaskButton = document.querySelector("#createDownloadChapterJobButton"); +const startJobButton = document.querySelector("#startJobButton"); +const cancelJobButton = document.querySelector("#cancelJobButton"); +const deleteJobButton = document.querySelector("#deleteJobButton"); + +//Manga viewer popup +const mangaViewerPopup = document.querySelector("#publicationViewerPopup"); +const mangaViewerWindow = document.querySelector("publication-viewer"); +const mangaViewerDescription = document.querySelector("#publicationViewerDescription"); +const mangaViewerName = document.querySelector("#publicationViewerName"); +const mangaViewerTags = document.querySelector("#publicationViewerTags"); +const mangaViewerAuthor = document.querySelector("#publicationViewerAuthor"); +const mangaViewCover = document.querySelector("#pubviewcover"); + +//General Rate Limits +const defaultRL = document.querySelector("#defaultRL"); +const coverRL = document.querySelector("#coverRL"); +const imageRL = document.querySelector("#imageRL"); +const infoRL = document.querySelector("#infoRL"); + +//MangaDex Rate Limits +const mDexAuthorRL = document.querySelector("#mDexAuthorRL"); +const mDexFeedRL = document.querySelector("#mDexFeedRL"); +const mDexImageRL = document.querySelector("#mDexImageRL"); + +//Komga +const settingKomgaUrl = document.querySelector("#komgaUrl"); +const settingKomgaUser = document.querySelector("#komgaUsername"); +const settingKomgaPass = document.querySelector("#komgaPassword"); + +//Kavita +const settingKavitaUrl = document.querySelector("#kavitaUrl"); +const settingKavitaUser = document.querySelector("#kavitaUsername"); +const settingKavitaPass = document.querySelector("#kavitaPassword"); + +//Gotify +const settingGotifyUrl = document.querySelector("#gotifyUrl"); +const settingGotifyAppToken = document.querySelector("#gotifyAppToken"); + +//Lunasea +const settingLunaseaWebhook = document.querySelector("#lunaseaWebhook"); + +//Ntfy +const settingNtfyEndpoint = document.querySelector("#ntfyEndpoint"); +const settingNtfyAuth = document.querySelector("#ntfyAuth"); + +//Connector Configured +const settingKomgaConfigured = document.querySelector("#komgaConfigured"); +const settingKavitaConfigured = document.querySelector("#kavitaConfigured"); +const settingGotifyConfigured = document.querySelector("#gotifyConfigured"); +const settingLunaseaConfigured = document.querySelector("#lunaseaConfigured"); +const settingNtfyConfigured = document.querySelector("#ntfyConfigured"); + +const settingUserAgent = document.querySelector("#userAgent"); +const settingApiUri = document.querySelector("#settingApiUri"); +const settingCSSStyle = document.querySelector('#cssStyle'); +const newMangaPopup = document.querySelector("#newMangaPopup"); +const newMangaConnector = document.querySelector("#newMangaConnector"); +const newMangaTitle = document.querySelector("#newMangaTitle"); +const newMangaResult = document.querySelector("#newMangaResult"); +const newMangaTranslatedLanguage = document.querySelector("#newMangaTranslatedLanguage"); + +//Jobs +const jobsRunningTag = document.querySelector("#jobsRunningTag"); +const jobsQueuedTag = document.querySelector("#jobsQueuedTag"); +const loaderdiv = document.querySelector('#loaderdiv'); +const jobStatusView = document.querySelector("#jobStatusView"); +const jobStatusRunning = document.querySelector("#jobStatusRunning"); +const jobStatusWaiting = document.querySelector("#jobStatusWaiting"); + +function Setup(){ + Ping().then((ret) => { + loaderdiv.style.display = 'none'; + + GetAvailableNotificationConnectors().then((json) => { + //console.log(json); + json.forEach(connector => { + notificationConnectorTypes[connector.Key] = connector.Value; + }); + }); + + GetAvailableLibraryConnectors().then((json) => { + //console.log(json); + json.forEach(connector => { + libraryConnectorTypes[connector.Key] = connector.Value; + }); + }); + + GetAvailableControllers().then((json) => { + //console.log(json); + newMangaConnector.replaceChildren(); + connectorFilterBox = document.querySelector("#connectorFilterBox"); + connectorFilterBox.replaceChildren(); + json.forEach(connector => { + //Add the connector to the New Manga dropdown + var option = document.createElement('option'); + option.value = connector; + option.innerText = connector; + newMangaConnector.appendChild(option); + + //Add the connector to the filter box + connectorFilter = document.createElement('connector-name'); + connectorFilter.innerText = connector; + connectorFilter.className = "pill"; + connectorFilter.style.backgroundColor = stringToColour(connector); + + connectorFilter.addEventListener("click", (event) => { + ToggleFilterConnector(connector, event); + }); + connectorFilterBox.appendChild(connectorFilter); + }); + }); + + //Add the publication status options to the filter bar + publicationStatusOptions = ["Ongoing", "Completed", "On Hiatus", "Cancelled", "Upcoming", "Status Unavailable"]; + statusFilterBox = document.querySelector("#statusFilterBox"); + statusFilterBox.replaceChildren(); + publicationStatusOptions.forEach(publicationStatus => { + var releaseStatus = document.createElement('status-filter'); + releaseStatus.innerText = publicationStatus; + releaseStatus.setAttribute("release-status", publicationStatus); + releaseStatus.addEventListener("click", (event) => { + ToggleFilterStatus(publicationStatus, event); + }); + + statusFilterBox.appendChild(releaseStatus); + }); + + ResetContent(); + UpdateJobs(); + GetSettings().then((json) => { + //console.log(json); + settingApiUri.placeholder = apiUri; + }); + GetRateLimits().then((json) => { + defaultRL.placeholder = json.Default + ' Requests/Minute'; + coverRL.placeholder = json.MangaCover + ' Requests/Minute'; + imageRL.placeholder = json.MangaImage + ' Requests/Minute'; + infoRL.placeholder = json.MangaInfo + ' Requests/Minute'; + mDexAuthorRL.placeholder = json.MangaDexAuthor + ' Requests/Minute'; + mDexFeedRL.placeholder = json.MangaDexFeed + ' Requests/Minute'; + mDexImageRL.placeholder = json.MangaDexImage + ' Requests/Minute'; + }); + + //If the cssStyle key isn't in the local storage of the browser, then set the css style to the default and load the page + //Otherwise get the style key from storage and set it. + if (!localStorage.getItem('cssStyle')) { + localStorage.setItem('cssStyle', 'card_compact'); + document.getElementById('librarystyle').setAttribute('href', 'styles/' + localStorage.getItem('cssStyle') + '.css'); + document.getElementById('card_compact').selected = true; + } else { + css_style = localStorage.getItem('cssStyle'); + document.getElementById('librarystyle').setAttribute('href', 'styles/' + css_style + '.css'); + document.getElementById(css_style).selected = true; + } + setInterval(() => { + UpdateJobs(); + }, 1000); + }); + //Clear the previous values if any exist. + searchBox.value = ""; + connectorMatch.length = 0; + statusMatch.length = 0; +} +Setup(); + +function ToggleFilterConnector(connector, event) { + //console.log("Initial Array:"); + //console.log(connectorMatch); + if (connectorMatch.includes(connector)) { + idx = connectorMatch.indexOf(connector); + connectorMatch.splice(idx, 1); + event.target.style.outline = 'none'; + event.target.style.outlineOffset = "0px"; + } else { + connectorMatch.push(connector); + event.target.style.outline = '4px solid var(--secondary-color)'; + event.target.style.outlineOffset = '3px'; + } + //console.log("Final Array"); + //console.log(connectorMatch); + FilterResults(); +} + +function ToggleFilterStatus(status, event) { + //console.log("Initial Array:"); + //console.log(statusMatch); + if (statusMatch.includes(status)) { + idx = statusMatch.indexOf(status); + statusMatch.splice(idx, 1); + event.target.style.outline = 'none'; + event.target.style.outlineOffset = "0px"; + } else { + statusMatch.push(status); + event.target.style.outline = '4px solid var(--secondary-color)'; + event.target.style.outlineOffset = '3px'; + } + //console.log("Final Array"); + //console.log(statusMatch); + FilterResults(); +} + +function ClearFilter() { + searchBox.value = ""; + statusMatch.length = 0; + connectorMatch.length = 0; + FilterResults(); + + //Get rid of the outlines + connectorFilterBox = document.querySelector("#connectorFilterBox"); + connectorFilterBox.childNodes.forEach(connector => { + if (connector.nodeName.toLowerCase() == 'connector-name') { + connector.style.outline = 'none'; + connector.style.outlineOffset = "0px"; + } + }); + + statusFilterBox = document.querySelector("#statusFilterBox"); + statusFilterBox.childNodes.forEach(publicationStatus => { + if (publicationStatus.nodeName.toLowerCase() == 'status-filter') { + publicationStatus.style.outline = 'none'; + publicationStatus.style.outlineOffset = "0px"; + } + }); +} + +settingCSSStyle.addEventListener("change", (event) => { + localStorage.setItem('cssStyle', settingCSSStyle.value); + document.getElementById('librarystyle').setAttribute('href', 'styles/' + localStorage.getItem('cssStyle') + '.css'); +}); + +function ResetContent(){ + //Delete everything + tasksContent.replaceChildren(); + + //Add "Add new Task" Button + var add = document.createElement("div"); + add.setAttribute("id", "addPublication") + var plus = document.createElement("p"); + plus.innerText = "+"; + add.appendChild(plus); + add.addEventListener("click", () => ShowNewMangaSearch()); + tasksContent.appendChild(add); + + //Populate with the monitored mangas + GetMonitorJobs().then((json) => { + //console.log(json); + json.forEach(job => { + var mangaView = CreateManga(job.manga, job.mangaConnector.name); + mangaView.addEventListener("click", (event) => { + ShowMangaWindow(job, job.manga, event, false); + }); + tasksContent.appendChild(mangaView); + }); + monitoringJobsCount = json.length; + }); +} + +function ShowNewMangaSearch(){ + newMangaTitle.value = ""; + newMangaPopup.style.display = "block"; + newMangaResult.replaceChildren(); +} + +newMangaTitle.addEventListener("keypress", (event) => { if(event.key === "Enter") GetNewMangaItems();}); + + + + +function GetNewMangaItems(){ + if(newMangaTitle.value.length < 4) + return; + + newMangaResult.replaceChildren(); + newMangaConnector.disabled = true; + newMangaTitle.disabled = true; + newMangaTranslatedLanguage.disabled = true; + GetPublicationFromConnector(newMangaConnector.value, newMangaTitle.value).then((json) => { + //console.log(json); + if(json.length > 0) + newMangaResult.style.display = "flex"; + json.forEach(result => { + var mangaElement = CreateManga(result, newMangaConnector.value) + newMangaResult.appendChild(mangaElement); + mangaElement.addEventListener("click", (event) => { + ShowMangaWindow(null, result, event, true); + }); + }); + + newMangaConnector.disabled = false; + newMangaTitle.disabled = false; + newMangaTranslatedLanguage.disabled = false; + }); +} + +//Returns a new "Publication" Item to display in the jobs section +function CreateManga(manga, connector){ + //Create a new publication and set an internal ID + var mangaElement = document.createElement('publication'); + mangaElement.id = GetValidSelector(manga.internalId); + + //Append the cover image to the publication + var mangaImage = document.createElement('img'); + mangaImage.src = GetCoverUrl(manga.internalId); + mangaElement.appendChild(mangaImage); + +//Append the publication information to the publication + //console.log(manga); + var info = document.createElement('publication-information'); + var connectorName = document.createElement('connector-name'); + connectorName.innerText = connector; + connectorName.className = "pill"; + connectorName.style.backgroundColor = stringToColour(connector); + info.appendChild(connectorName); + + var mangaName = document.createElement('publication-name'); + mangaName.innerText = manga.sortName; + + //Create the publication status indicator + var releaseStatus = document.createElement('publication-status'); + releaseStatus.setAttribute("release-status", manga.releaseStatus); + switch(manga.releaseStatus){ + case 0: + releaseStatus.setAttribute("release-status", "Ongoing"); + break; + case 1: + releaseStatus.setAttribute("release-status", "Completed"); + break; + case 2: + releaseStatus.setAttribute("release-status", "On Hiatus"); + break; + case 3: + releaseStatus.setAttribute("release-status", "Cancelled"); + break; + case 4: + releaseStatus.setAttribute("release-status", "Upcoming"); + break; + default: + releaseStatus.setAttribute("release-status", "Status Unavailable"); + break; + } + + info.appendChild(mangaName); + mangaElement.appendChild(info); + mangaElement.appendChild(releaseStatus); //Append the release status indicator to the publication element + return mangaElement; +} + +createMonitorJobButton.addEventListener("click", () => { + CreateMonitorJob(newMangaConnector.value, selectedManga.internalId, newMangaTranslatedLanguage.value); + UpdateJobs(); + mangaViewerPopup.style.display = "none"; +}); +startJobButton.addEventListener("click", () => { + StartJob(selectedJob.id); + mangaViewerPopup.style.display = "none"; +}); +cancelJobButton.addEventListener("click", () => { + CancelJob(selectedJob.id); + mangaViewerPopup.style.display = "none"; +}); +deleteJobButton.addEventListener("click", () => { + RemoveJob(selectedJob.id); + UpdateJobs(); + mangaViewerPopup.style.display = "none"; +}); + +function ShowMangaWindow(job, manga, event, add){ + selectedManga = manga; + selectedJob = job; + //Show popup + mangaViewerPopup.style.display = "block"; + + //Set position to mouse-position + if(event.clientY < window.innerHeight - mangaViewerWindow.offsetHeight) + mangaViewerWindow.style.top = `${event.clientY}px`; + else + mangaViewerWindow.style.top = `${event.clientY - mangaViewerWindow.offsetHeight}px`; + + if(event.clientX < window.innerWidth - mangaViewerWindow.offsetWidth) + mangaViewerWindow.style.left = `${event.clientX}px`; + else + mangaViewerWindow.style.left = `${event.clientX - mangaViewerWindow.offsetWidth}px`; + + //Edit information inside the window + mangaViewerName.innerText = manga.sortName; + mangaViewerTags.innerText = manga.tags.join(", "); + mangaViewerDescription.innerText = manga.description; + mangaViewerAuthor.innerText = manga.authors.join(','); + mangaViewCover.src = GetCoverUrl(manga.internalId); + toEditId = manga.internalId; + + //Check what action should be listed + if(add){ + createMonitorJobButton.style.display = "initial"; + createDownloadChapterJobButton.style.display = "none"; + cancelJobButton.style.display = "none"; + startJobButton.style.display = "none"; + deleteJobButton.style.display = "none"; + } + else{ + createMonitorJobButton.style.display = "none"; + createDownloadChapterJobButton.style.display = "none"; + cancelJobButton.style.display = "initial"; + startJobButton.style.display = "initial"; + deleteJobButton.style.display = "initial"; + } +} + +function HidePublicationPopup(){ + publicationViewerPopup.style.display = "none"; +} + +searchBox.addEventListener("keyup", () => FilterResults()); +//Filter shown jobs +function FilterResults(){ + //For each publication + tasksContent.childNodes.forEach(publication => { + //If the search box isn't empty check that the title contains the searchbox content. If it does then + //'searchMatch' is true and the manga is shown. If the search box is empty, then consider this field + //to be true anyways. + if (searchBox.value.length > 0) { + publication.childNodes.forEach(item => { + if (item.nodeName.toLowerCase() == "publication-information"){ + item.childNodes.forEach(information => { + if (information.nodeName.toLowerCase() == "publication-name") { + if (information.textContent.toLowerCase().includes(searchBox.value.toLowerCase())){ + searchMatch = 1; + } else { + searchMatch = 0; + } + } + }); + } + }); + } else { + searchMatch = 1; + } + + //If the array connectorMatch isn't empty then check that the connector matches one of the ones + //in the array + if (connectorMatch.length > 0) { + publication.childNodes.forEach(item => { + if (item.nodeName.toLowerCase() == "publication-information"){ + item.childNodes.forEach(information => { + if (information.nodeName.toLowerCase() == "connector-name") { + if (connectorMatch.includes(information.textContent)){ + connectorNameMatch = 1; + } else { + connectorNameMatch = 0; + } + } + }); + } + }); + } else { + connectorNameMatch = 1; + } + + //If the array statusMatch isn't empty then check that the status matches one of the ones + //in the array + if (statusMatch.length > 0) { + publication.childNodes.forEach(item => { + if (item.nodeName.toLowerCase() == "publication-status"){ + if (statusMatch.includes(item.getAttribute('release-status'))) { + statusNameMatch = 1; + } else { + statusNameMatch = 0; + } + } + }); + } else { + statusNameMatch = 1; + } + + //If all of the filtering conditions are met then show the manga, otherwise hide it. + if (searchMatch && connectorNameMatch && statusNameMatch) { + publication.style.display = 'initial'; + } else { + publication.style.display = 'none'; + } + }); +} + +settingsCog.addEventListener("click", () => { + OpenSettings(); + settingsPopup.style.display = "flex"; +}); + +filterFunnel.addEventListener("click", () => { + filterBox.classList.toggle("animate"); +}); + +settingKomgaUrl.addEventListener("keypress", (event) => { if(event.key === "Enter") UpdateSettings(); }); +settingKomgaUser.addEventListener("keypress", (event) => { if(event.key === "Enter") UpdateSettings(); }); +settingKomgaPass.addEventListener("keypress", (event) => { if(event.key === "Enter") UpdateSettings(); }); +settingKavitaUrl.addEventListener("keypress", (event) => { if(event.key === "Enter") UpdateSettings(); }); +settingKavitaUser.addEventListener("keypress", (event) => { if(event.key === "Enter") UpdateSettings(); }); +settingKavitaPass.addEventListener("keypress", (event) => { if(event.key === "Enter") UpdateSettings(); }); +settingGotifyUrl.addEventListener("keypress", (event) => { if(event.key === "Enter") UpdateSettings(); }); +settingGotifyAppToken.addEventListener("keypress", (event) => { if(event.key === "Enter") UpdateSettings(); }); +settingLunaseaWebhook.addEventListener("keypress", (event) => { if(event.key === "Enter") UpdateSettings(); }); +settingNtfyEndpoint.addEventListener("keypress", (event) => { if(event.key === "Enter") UpdateSettings(); }); +settingNtfyAuth.addEventListener("keypress", (event) => { if(event.key === "Enter") UpdateSettings(); }); +settingUserAgent.addEventListener("keypress", (event) => { if(event.key === "Enter") UpdateSettings(); }); +settingApiUri.addEventListener("keypress", (event) => { if(event.key === "Enter") UpdateSettings(); }); + +defaultRL.addEventListener("keypress", (event) => { if(event.key === "Enter") UpdateSettings();}); +coverRL.addEventListener("keypress", (event) => { if(event.key === "Enter") UpdateSettings();}); +imageRL.addEventListener("keypress", (event) => { if(event.key === "Enter") UpdateSettings();}); +infoRL.addEventListener("keypress", (event) => { if(event.key === "Enter") UpdateSettings();}); +mDexAuthorRL.addEventListener("keypress", (event) => { if(event.key === "Enter") UpdateSettings();}); +mDexFeedRL.addEventListener("keypress", (event) => { if(event.key === "Enter") UpdateSettings();}); +mDexImageRL.addEventListener("keypress", (event) => { if(event.key === "Enter") UpdateSettings();}); + + +function OpenSettings(){ + settingGotifyConfigured.setAttribute("configuration", "Not Configured"); + settingLunaseaConfigured.setAttribute("configuration", "Not Configured"); + settingNtfyConfigured.setAttribute("configuration", "Not Configured"); + settingKavitaConfigured.setAttribute("configuration", "Not Configured"); + settingKomgaConfigured.setAttribute("configuration", "Not Configured"); + settingKomgaUrl.value = ""; + settingKomgaUser.value = ""; + settingKomgaPass.value = ""; + settingKavitaUrl.value = ""; + settingKavitaUser.value = ""; + settingKavitaPass.value = ""; + settingGotifyUrl.value = ""; + settingGotifyAppToken.value = ""; + settingLunaseaWebhook.value = ""; + settingNtfyAuth.value = ""; + settingNtfyEndpoint.value = ""; + settingUserAgent.value = ""; + settingApiUri.value = ""; + defaultRL.value = ""; + coverRL.value = ""; + imageRL.value = ""; + infoRL.value = ""; + mDexAuthorRL.value = ""; + mDexFeedRL.value = ""; + mDexImageRL.value = ""; + + GetSettings().then((json) => { + //console.log(json); + settingApiUri.value = apiUri; + settingUserAgent.value = json.userAgent; + //console.log(json.styleSheet); + }); + GetRateLimits().then((json) => { + defaultRL.placeholder = json.Default + ' Requests/Minute'; + coverRL.placeholder = json.MangaCover + ' Requests/Minute'; + imageRL.placeholder = json.MangaImage + ' Requests/Minute'; + infoRL.placeholder = json.MangaInfo + ' Requests/Minute'; + mDexAuthorRL.placeholder = json.MangaDexAuthor + ' Requests/Minute'; + mDexFeedRL.placeholder = json.MangaDexFeed + ' Requests/Minute'; + mDexImageRL.placeholder = json.MangaDexImage + ' Requests/Minute'; + }); + GetLibraryConnectors().then((json) => { + //console.log(json); + json.forEach(connector => { + switch(libraryConnectorTypes[connector.libraryType]){ + case "Kavita": + settingKavitaConfigured.setAttribute("configuration", "Active"); + settingKavitaUrl.value = connector.baseUrl; + settingKavitaUser.value = "***"; + settingKavitaPass.value = "***"; + break; + case "Komga": + settingKomgaConfigured.setAttribute("configuration", "Active"); + settingKomgaUrl.value = connector.baseUrl; + settingKomgaUser.value = "***"; + settingKomgaPass.value = "***"; + break; + default: + console.log("Unknown type"); + console.log(connector); + break; + } + }); + }); + GetNotificationConnectors().then((json) => { + json.forEach(connector => { + switch(notificationConnectorTypes[connector.notificationConnectorType]){ + case "Gotify": + settingGotifyUrl.value = connector.endpoint; + settingGotifyAppToken.value = "***"; + settingGotifyConfigured.setAttribute("configuration", "Active"); + break; + case "LunaSea": + settingLunaseaConfigured.setAttribute("configuration", "Active"); + settingLunaseaWebhook.value = connector.id; + break; + case "Ntfy": + settingNtfyConfigured.setAttribute("configuration", "Active"); + settingNtfyEndpoint.value = connector.endpoint; + settingNtfyAuth.value = "***"; + break; + default: + console.log("Unknown type"); + console.log(connector); + break; + } + }); + }); +} + +//Functions for clearing/resetting connectors in the settings pop-up +function ClearKomga(){ + settingKomgaUrl.value = ""; + settingKomgaUser.value = ""; + settingKomgaPass.value = ""; + settingKomgaConfigured.setAttribute("configuration", "Not Configured"); + ResetKomga(); +} + +function ClearKavita(){ + settingKavitaUrl.value = ""; + settingKavitaUser.value = ""; + settingKavitaPass.value = ""; + settingKavitaConfigured.setAttribute("configuration", "Not Configured"); + ResetKavita(); +} + +function ClearGotify(){ + settingGotifyUrl.value = ""; + settingGotifyAppToken.value = "" + settingGotifyConfigured.setAttribute("configuration", "Not Configured"); + ResetGotify(); +} + +function ClearLunasea(){ + settingLunaseaWebhook.value = ""; + settingLunaseaConfigured.setAttribute("configuration", "Not Configured"); + ResetLunaSea(); +} + +function ClearNtfy(){ + settingNtfyEndpoint.value = ""; + settingNtfyAuth.value = ""; + settingNtfyConfigured.setAttribute("configuration", "Not Configured"); + ResetNtfy(); +} + +function UpdateSettings(){ + if(settingApiUri.value != ""){ + apiUri = settingApiUri.value; + setCookie("apiUri", apiUri); + Setup(); + } + + if(settingKomgaUrl.value != "" && + settingKomgaUser.value != "" && + settingKomgaPass.value != ""){ + UpdateKomga(settingKomgaUrl.value, utf8_to_b64(`${settingKomgaUser.value}:${settingKomgaPass.value}`)); + } + + if(settingKavitaUrl.value != "" && + settingKavitaUser.value != "" && + settingKavitaPass.value != ""){ + UpdateKavita(settingKavitaUrl.value, settingKavitaUser.value, settingKavitaPass.value); + } + + if(settingGotifyUrl.value != "" && + settingGotifyAppToken.value != ""){ + UpdateGotify(settingGotifyUrl.value, settingGotifyAppToken.value); + } + + if(settingLunaseaWebhook.value != ""){ + UpdateLunaSea(settingLunaseaWebhook.value); + } + + if(settingNtfyEndpoint.value != "" && + settingNtfyAuth.value != ""){ + UpdateNtfy(settingNtfyEndpoint.value, settingNtfyAuth.value); + } + + if(settingUserAgent.value != ""){ + UpdateUserAgent(settingUserAgent.value); + } + + if (defaultRL.value != "") { + UpdateRateLimit(0, defaultRL.value); + } + + if (coverRL.value != "") { + UpdateRateLimit(3, coverRL.value); + } + + if (imageRL.value != "") { + UpdateRateLimit(2, imageRL.value); + } + + if (infoRL.value != "") { + UpdateRateLimit(6, infoRL.value); + } + + if (mDexAuthorRL.value != "") { + UpdateRateLimit(5, mDexAuthorRL.value); + } + + if (mDexFeedRL.value != "") { + UpdateRateLimit(1, mDexFeedRL.value); + } + + if (mDexImageRL.value != "") { + UpdateRateLimit(5, mDexImageRL.value); + } + + setTimeout(() => { + OpenSettings(); + Setup(); + }, 100) +} + +function utf8_to_b64(str) { + return window.btoa(unescape(encodeURIComponent( str ))); +} + +function UpdateJobs(){ + + GetMonitorJobs().then((json) => { + if(monitoringJobsCount != json.length){ + ResetContent(); + monitoringJobsCount = json.length; + } + }); + + //Get the jobs that are waiting in the queue + GetWaitingJobs().then((json) => { + jobsQueuedTag.innerText = json.length; + + var nowWaitingJobs = []; + + json.forEach(job => { + if(!waitingJobs.includes(GetValidSelector(job.id))){ + var jobDom = createJob(job); + jobStatusWaiting.appendChild(jobDom); + } + nowWaitingJobs.push(GetValidSelector(job.id)); + }); + waitingJobs = nowWaitingJobs; + }); + + jobStatusWaiting.childNodes.forEach(child => { + if(!waitingJobs.includes(child.id)) + jobStatusWaiting.removeChild(child); + }); + + //Get currently running jobs + GetRunningJobs().then((json) => { + jobsRunningTag.innerText = json.length; + + var nowRunningJobs = []; + + json.forEach(job => { + if(!runningJobs.includes(GetValidSelector(job.id))){ + var jobDom = createJob(job); + jobStatusRunning.appendChild(jobDom); + } + nowRunningJobs.push(GetValidSelector(job.id)); + UpdateJobProgress(job.id); + }); + + runningJobs = nowRunningJobs; + }); + + jobStatusRunning.childNodes.forEach(child => { + if(!runningJobs.includes(child.id)) + jobStatusRunning.removeChild(child); + }); +} + +function createJob(jobjson){ + var manga; + if(jobjson.chapter != null) + manga = jobjson.chapter.parentManga; + else if(jobjson.manga != null) + manga = jobjson.manga; + else return null; + + + var wrapper = document.createElement("div"); + wrapper.className = "section-item"; + wrapper.id = GetValidSelector(jobjson.id); + + var image = document.createElement("img"); + image.className = "jobImage"; + image.src = GetCoverUrl(manga.internalId); + wrapper.appendChild(image); + + var details = document.createElement("div"); + details.className = 'jobDetails'; + + var title = document.createElement("span"); + title.className = "jobTitle"; + if(jobjson.chapter != null) + title.innerText = `${manga.sortName} - ${jobjson.chapter.fileName}`; + else if(jobjson.manga != null) + title.innerText = manga.sortName; + details.appendChild(title); + + var progressBar = document.createElement("progress"); + progressBar.className = "jobProgressBar"; + progressBar.id = `jobProgressBar${GetValidSelector(jobjson.id)}`; + details.appendChild(progressBar); + + var progressSpan = document.createElement("span"); + progressSpan.className = "jobProgressSpan"; + progressSpan.id = `jobProgressSpan${GetValidSelector(jobjson.id)}`; + progressSpan.innerText = "Pending..."; + details.appendChild(progressSpan); + + var cancelSpan = document.createElement("span"); + cancelSpan.className = "jobCancel"; + cancelSpan.innerText = "Cancel"; + cancelSpan.addEventListener("click", () => CancelJob(jobjson.id)); + details.appendChild(cancelSpan); + + wrapper.appendChild(details); + + return wrapper; +} + +function ShowJobQueue(){ + jobStatusView.style.display = "initial"; +} + +function UpdateJobProgress(jobId){ + GetProgress(jobId).then((json) => { + var progressBar = document.querySelector(`#jobProgressBar${GetValidSelector(jobId)}`); + var progressSpan = document.querySelector(`#jobProgressSpan${GetValidSelector(jobId)}`); + if(progressBar != null && json.progress != 0){ + progressBar.value = json.progress; + } + if(progressSpan != null){ + var percentageStr = "0%"; + var timeleftStr = "00:00:00"; + if(json.progress != 0){ + percentageStr = Intl.NumberFormat("en-US", { style: "percent"}).format(json.progress); + timeleftStr = json.timeRemaining.split('.')[0]; + } + progressSpan.innerText = `${percentageStr} ${timeleftStr}`; + } + }); +} + +function GetValidSelector(str){ + var clean = [...str.matchAll(/[a-zA-Z0-9]*-*_*/g)]; + return clean.join(''); +} + +const stringToColour = (str) => { + let hash = 0; + str.split('').forEach(char => { + hash = char.charCodeAt(0) + ((hash << 5) - hash) + }) + let colour = '#' + for (let i = 0; i < 3; i++) { + const value = (hash >> (i * 8)) & 0xff + colour += value.toString(16).padStart(2, '0') + } + return colour } \ No newline at end of file diff --git a/Website/media/close-x.svg b/Website/media/close-x.svg index fc8cc4d..1d0a4cd 100644 --- a/Website/media/close-x.svg +++ b/Website/media/close-x.svg @@ -1,4 +1,4 @@ - - - + + + \ No newline at end of file diff --git a/Website/media/filter-funnel.svg b/Website/media/filter-funnel.svg index 5092c6d..42ece2c 100644 --- a/Website/media/filter-funnel.svg +++ b/Website/media/filter-funnel.svg @@ -1,12 +1,12 @@ - - - - - - - + + + + + + + \ No newline at end of file diff --git a/Website/media/running.svg b/Website/media/running.svg index ddde0a6..e4dc3a1 100644 --- a/Website/media/running.svg +++ b/Website/media/running.svg @@ -1,53 +1,53 @@ - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Website/media/settings-cogwheel.svg b/Website/media/settings-cogwheel.svg index 7e61388..74ab07f 100644 --- a/Website/media/settings-cogwheel.svg +++ b/Website/media/settings-cogwheel.svg @@ -1,21 +1,21 @@ - - - - - - - - - + + + + + + + + + \ No newline at end of file diff --git a/Website/media/tasks.svg b/Website/media/tasks.svg index 6e64e66..19aedb4 100644 --- a/Website/media/tasks.svg +++ b/Website/media/tasks.svg @@ -1,10 +1,10 @@ - - - - - - - + + + + + + + \ No newline at end of file diff --git a/Website/styles/base.css b/Website/styles/base.css index ee80fc5..ac41f66 100644 --- a/Website/styles/base.css +++ b/Website/styles/base.css @@ -1,960 +1,960 @@ -:root{ - --background-color: #030304; - --second-background-color: white; - --primary-color: #f5a9b8; - --secondary-color: #5bcefa; - --blur-background: rgba(245, 169, 184, 0.58); - --accent-color: #fff; - /* --primary-color: green; - --secondary-color: gold; - --blur-background: rgba(86, 131, 36, 0.8); - --accent-color: olive; */ - --topbar-height: 60px; - box-sizing: border-box; -} - -body{ - padding: 0; - margin: 0; - height: 100vh; - background-color: var(--background-color); - font-family: "Inter", sans-serif; - overflow-x: hidden; -} - -wrapper { - display: flex; - flex-flow: column; - flex-wrap: nowrap; - height: 100vh; -} - -background-placeholder{ - background-color: var(--second-background-color); - opacity: 1; - position: absolute; - width: 100%; - height: 100%; - border-radius: 0 0 5px 0; - z-index: -1; -} - -topbar { - display: flex; - align-items: center; - height: var(--topbar-height); - background-color: var(--secondary-color); - z-index: 100; - box-shadow: 0 0 20px black; -} - -titlebox { - position: relative; - display: flex; - margin: 0 0 0 40px; - height: 100%; - align-items:center; - justify-content:center; -} - -titlebox span{ - cursor: default; - font-size: 24pt; - font-weight: bold; - background: linear-gradient(150deg, var(--primary-color), var(--accent-color)); - -webkit-background-clip: text; - -webkit-text-fill-color: transparent; - margin-left: 20px; -} - -titlebox img { - height: 100%; - cursor: grab; -} - -spacer{ - flex-grow: 1; -} - -filter-box { - display: none; - align-self: center; - flex-direction: column; - position: relative; - - margin: 10px; - background-color: var(--second-background-color); - border-style: solid; - border-color: var(--primary-color); - border-width: 2px; - border-radius: 15px; - min-width: 300px; - width: 50%; - overflow: hidden; - max-height: 50%; - height: 600px; -} - -filter-box.animate { - display: flex; -} - -filter-box border-bar popup-title{ - font-size: 12pt; -} - -filter-box border-bar popup-close { - height: 20px; - width: 20px; - font-size: 12pt; - -webkit-user-select: none; /* Safari */ - -ms-user-select: none; /* IE 10 and IE 11 */ - user-select: none; /* Standard syntax */ -} - -border-bar-button.clearFilter{ - font-weight: bold; - margin: 0px 10px 10px 10px; - border-color: lightgray; - color: gray; - align-content: center; - justify-content: center; -} - -border-bar-button.clearFilter:hover { - background-color: red; - border-color: var(--second-background-color); - color: var(--second-background-color); -} - -status-filter { - display: block; - margin: 10px; - - /*Text Properties*/ - font-size:10pt; - font-weight:bold; - color:white; - text-align: center; - - /*Size*/ - padding: 3px 8px; - border-radius: 6px; - border: 0px; - background-color: inherit; - - cursor: pointer; - -webkit-user-select: none; /* Safari */ - -ms-user-select: none; /* IE 10 and IE 11 */ - user-select: none; /* Standard syntax */ -} - -status-filter[release-status="Ongoing"]{ - background-color: limegreen; -} - -status-filter[release-status="Completed"]{ - background-color: blueviolet; -} - -status-filter[release-status="On Hiatus"]{ - background-color: darkorange; -} - -status-filter[release-status="Cancelled"]{ - background-color: firebrick; -} - -status-filter[release-status="Upcoming"]{ - background-color: aqua; -} - -status-filter[release-status="Status Unavailable"]{ - background-color: gray; -} - - -searchdiv{ - display: flex; - width: 100%; -} - -#searchbox { - display: flex; - padding: 3px 5px; - margin: 5px; - border-style: solid; - border-width: 2px; - border-radius: 10px; - font-size: 12pt; - outline: none; - border-color: lightgray; - flex-grow: 1; - flex-shrink: 1; -} - -#searchbox:focus { - border-color: var(--secondary-color); -} - -.pill { - flex-grow: 0; - height: 14pt; - font-size: 12pt; - border-radius: 9pt; - background-color: var(--primary-color); - padding: 2pt 17px; - color: black; -} - -#connectorFilterBox .pill { - margin: 10px; - cursor: pointer; - -webkit-user-select: none; /* Safari */ - -ms-user-select: none; /* IE 10 and IE 11 */ - user-select: none; /* Standard syntax */ -} - -#settingscog { - cursor: pointer; - margin: 0px 30px; - margin-left: 15px; - height: 50%; - filter: invert(100%) sepia(0%) saturate(7465%) hue-rotate(115deg) brightness(116%) contrast(101%); -} - -#filterFunnel { - cursor: pointer; - margin: 0px 15px; - height: 50%; - filter: invert(100%) sepia(0%) saturate(7465%) hue-rotate(115deg) brightness(116%) contrast(101%); -} - -viewport { - position: relative; - display: flex; - flex-flow: row; - flex-wrap: nowrap; - flex-grow: 1; - height: 100%; - overflow-y: scroll; - scrollbar-color: var(--accent-color) var(--primary-color); - scrollbar-width: thin; -} - -footer { - display: flex; - flex-direction: row; - flex-wrap: nowrap; - width: 100%; - height: 40px; - align-items: center; - justify-content: center; - background-color: var(--primary-color); - align-content: center; -} - -footer > div { - height: 100%; - margin: 0 30px; - display: flex; - flex-direction: row; - flex-wrap: nowrap; - align-items: center; - cursor: pointer; -} - -footer > div > *{ - height: 40%; - margin: 0 5px; -} - -#madeWith { - flex-grow: 1; - text-align: right; - margin-right: 20px; - cursor: url("media/blahaj.png"), grab; -} - -content { - position: relative; - flex-grow: 1; - border-radius: 5px; - display: flex; - flex-direction: row; - flex-wrap: wrap; - justify-content: start; - align-content: start; -} - -#settingsPopup{ - z-index: 300; -} - -popup{ - display: none; - width: 100%; - min-height: 100%; - top: 0; - left: 0; - position: fixed; - z-index: 2; - flex-direction: column; -} - -border-bar { - display: flex; - flex-direction: row; - background-color: var(--primary-color); - color: var(--accent-color); - font-weight: bolder; - padding: 7px 5px; - margin:0; - align-items: center; - position: relative; - width: 100%; -} - -popup-title { - font-size: 14pt; - display: flex; - margin-top: 3px; - margin-left: 5px; - color: var(--second-background-color); -} - -popup-close { - border: none; - background-color: inherit; - color: var(--second-background-color);; - font-weight: inherit; - font-size: 27px; - font-family: 'Trebuchet MS', 'Lucida Sans Unicode', 'Lucida Grande', 'Lucida Sans', Arial, sans-serif; - display: flex; - cursor: pointer; - margin-left: auto; - margin-right: 15px; - height: 32px; - width: 32px; - border-radius: 16px; - align-content: center; - justify-content: center; -} - -popup-close:hover { - background-color: var(--secondary-color); -} - -border-bar > .button-container { - display: flex; - flex-direction: row; - align-items: center; - flex-wrap: wrap; - margin-right: 0; - margin-left: auto; -} - -border-bar-button { - border-style: solid; - border-width: 2px; - background-color: inherit; - color: var(--second-background-color); - font-weight: inherit; - font-size: inherit; - font-family: inherit; - display: flex; - cursor: pointer; - margin: 0px 5px; - padding: 5px 20px; - border-radius: 20px; - height: 20px; - align-items: center; - border-color: var(--accent-color); - -webkit-user-select: none; /* Safari */ - -ms-user-select: none; /* IE 10 and IE 11 */ - user-select: none; /* Standard syntax */ -} - -border-bar-button:hover { - border-color: var(--secondary-color); -} - -border-bar-button.primary { - background-color: var(--secondary-color); - color: var(--accent-color); - border-color: var(--primary-color); - margin-right: 10px; -} - -border-bar-button.primary:hover { - border-color: var(--accent-color); -} - -border-bar-button.section { - font-weight: bold; - color: darkgray; - border-color: darkgray; - text-align: center; - padding: 5px; - flex-grow: 1; - justify-content: center; -} - -border-bar-button.section:hover { - color: var(--secondary-color); - border-color: var(--secondary-color); -} - - -popup popup-window { - position: absolute; - z-index: 3; - left: 10%; - top: 10%; - height: 80%; - width: 80%; - display: flex; - flex-direction: column; - background-color: var(--second-background-color); - border-radius: 15px; - overflow: hidden; -} - -popup#jobStatusView popup-window { - left: 20%; - top: 20%; - height: 60%; - width: 60%; -} - -popup-content{ - display: flex; - flex-direction: column; - align-items: left; - height: calc(100% - 60px); - overflow-y: auto; - overflow-x: hidden; - scrollbar-width: thin; - scrollbar-color: var(--secondary-color) var(--second-background-color); -} - -popup-content > .popup-section { - margin: 5px; - margin-bottom: 10px; - font-size: 10pt; - font-weight: 100; - display: block; - border-top-style: solid; - border-top-width: 1px; - border-top-color: lightgray; - width: calc(100%-10px); - padding: 10px; -} - -.section-content { - display: flex; - flex-direction: row; - width: 100%; - flex-wrap: wrap; -} - -.section-item { - display: flex; - flex-direction: column; - width: 22%; - min-width: 300px; - height: auto; - border-radius: 10px; - border-style: solid; - border-width: 1px; - border-color: lightgray; - margin: 7px; - padding: 5px; -} - -.section-item.dyn-height { - height: fit-content; -} - -.section-item > .title { - font-weight: bold; - vertical-align: bottom; - line-height: 32px; - font-size: 12pt; - width: 100%; -} - -a:link { - color: inherit; - text-decoration: none; -} - -a:visited { - color: inherit; - text-decoration: none; -} - -a:hover { - color: inherit; - text-decoration: underline solid var(--secondary-color) 3px; -} - -a:active { - color: inherit; - text-decoration: none; -} - -.section-item > .title > img { - width: auto; - height: 32px; - margin: 5px; - vertical-align: middle; - border-radius: 5px; -} - -.section-item > .title > connector-configured { - display:block; - height: 10px; - width: 10px; - border-radius: 50%; - margin: 5px; - float: right; - top: 5px; - right: 5px; -} - -.section-item > .title > connector-configured::after { - display: block; - content: attr(configuration); - float: right; - width: max-content; - width: -webkit-max-content; - width: -mox-max-content; - width: intrinsic; - - visibility: hidden; - - /*Text Properties*/ - font-size:8pt; - font-weight:bold; - color:white; - text-align: right; - - /*Size*/ - padding: 0px 8px; - border-radius: 6px; - border: 0px; - background-color: inherit; -} - -.section-item > .title > connector-configured:hover::after{ - visibility:visible; -} - -.section-item > .title > connector-configured[configuration="Active"] { - background-color: limegreen; -} - -.section-item > .title > connector-configured[configuration="Not Configured"] { - background-color: gray; -} - -.section-item > input { - margin: 2px; - padding: 5px; - height: 20px; - border-radius: 10px; - border-style: solid; - outline: none; -} -.section-item > input:focus { - border-color: var(--secondary-color); -} - -.section-item > row { - width: calc(100%-20px); - display: flex; - flex-direction: row; - align-items: center; - margin-left: 5px; - margin-bottom: 5px; -} - -.section-item > row > input { - margin-left: auto; - margin-right: 2px; - padding: 5px; - height: 20px; - border-radius: 10px; - border-style: solid; - outline: none; - flex-grow: 0; - text-align: end; - float: right; - width: 200px; -} -.section-item > row > input:focus { - border-color: var(--secondary-color); -} - -.section-item > row > select { - margin-left: auto; - margin-right: 2px; - padding: 2px; - height: 30px; - border-radius: 10px; - border-style: solid; - outline: none; - flex-grow: 0; - text-align: end; - float: right; - width: 200px; -} - -.section-item > row > select:focus { - border-color: var(--secondary-color); -} - -.section-buttons-container { - display: flex; - flex-direction: row; - align-items: center; - position: relative; - margin-left: auto; - margin-top: auto; - margin-bottom: 0; - margin-right: 0; -} - -.section-buttons-container > .section-button { - font-size: 12px; - padding: 3px 10px; - margin: 3px; - border-radius: 5px; - border-style: solid; - border-width: 1px; - border-color: lightgray; - font-weight: bold; - color: gray; - cursor: pointer; - -webkit-user-select: none; /* Safari */ - -ms-user-select: none; /* IE 10 and IE 11 */ - user-select: none; /* Standard syntax */ -} - -.section-button#reset:hover { - color: red; - border-color: red; -} -.section-buttons-container > .section-button:hover { - border-color: var(--secondary-color); - color: var(--secondary-color); -} - -#newMangaPopup > div { - z-index: 3; - position: relative; -} - -#newMangaPopup > #newMangaPopupSelector { - width: 600px; - height: 40px; - margin: 80px auto 0; -} - -#newMangaPopup > div > #newMangaConnector, #newMangaTitle, #newMangaTranslatedLanguage { - margin: 0; - display: inline-block; - height: 40px; -} - -#newMangaPopup #newMangaConnector { - width: 100px; - padding: 0 0 0 5px; - border-radius: 5px 0 0 5px; - border: 0; - border-right: 1px solid darkgray; -} - -#newMangaPopup #newMangaTitle{ - width: 445px; - padding: 0 5px 0 5px; - border: 0; -} - -#newMangaPopup #newMangaTranslatedLanguage { - width: 45px; - border-radius: 0 5px 5px 0; - border: 0; - border-left: 1px solid darkgray; - margin-left: -5px; -} - -#newMangaResult { - display: none; - flex-direction: row; - justify-content: flex-start; - margin: 5px auto 0; - border-radius: 5px; - padding: 5px; - width: min-content; - max-width: 98%; - max-height: 400px; - overflow-x: scroll; - overflow-y: hidden; -} - -blur-background { - width: 100%; - height: 100%; - position: absolute; - left: 0; - background: var(--blur-background); - box-shadow: 0 4px 30px rgba(0, 0, 0, 0.1); - backdrop-filter: blur(4.5px); - -webkit-backdrop-filter: blur(4.5px); -} - -#publicationViewerPopup{ - z-index: 5; -} - -publication-viewer{ - display: block; - width: 460px; - position: absolute; - top: 200px; - left: 400px; - background-color: var(--accent-color); - border-radius: 5px; - overflow: hidden; - padding: 15px; -} - -publication-viewer::after{ - content: ''; - position: absolute; - left: 0; top: 0; - border-radius: 5px; - width: 100%; - height: 100%; - background: rgba(0,0,0,0.8); - backdrop-filter: blur(3px); -} - -publication-viewer img { - position: absolute; - left: 0; - top: 0; - height: 100%; - width: 100%; - object-fit: cover; - border-radius: 5px; - z-index: 0; -} - -publication-viewer publication-details > * { - margin: 5px 0; -} - -publication-viewer publication-details publication-name { - width: initial; - overflow-x: scroll; - white-space: nowrap; - scrollbar-width: none; -} - -publication-viewer publication-details publication-tags::before { - content: "Tags"; - display: block; - font-weight: bolder; -} - -publication-viewer publication-details publication-tags { - overflow-x: scroll; - white-space: nowrap; - scrollbar-width: none; -} - -publication-viewer publication-details publication-author::before { - content: "Author: "; - font-weight: bolder; -} - -publication-viewer publication-details publication-description::before { - content: "Description"; - display: block; - font-weight: bolder; -} - -publication-viewer publication-details publication-description { - font-size: 12pt; - margin: 5px 0; - height: 145px; - overflow-x: scroll; -} - -publication-viewer publication-details publication-interactions { - display: flex; - flex-direction: row; - justify-content: end; - align-items: start; - width: 100%; -} - -publication-viewer publication-details publication-interactions > * { - margin: 0 10px; - font-size: 16pt; - cursor: pointer; -} - -publication-viewer publication-details publication-interactions publication-starttask { - color: var(--secondary-color); -} - -publication-viewer publication-details publication-interactions publication-delete { - color: red; -} - -publication-view publication-details publication-interactions publication-canceltask { - color: yellow; -} - -publication-viewer publication-details publication-interactions publication-add { - color: limegreen; -} - -footer-tag-popup { - display: none; - padding: 2px 4px; - position: fixed; - bottom: 58px; - left: 20px; - background-color: var(--second-background-color); - z-index: 8; - border-radius: 5px; - max-height: 400px; -} - -footer-tag-content{ - position: relative; - max-height: 400px; - display: flex; - flex-direction: column; - flex-wrap: nowrap; - overflow-y: scroll; -} - -footer-tag-content > * { - margin: 2px 5px; -} - -footer-tag-popup::before{ - content: ""; - width: 0; - height: 0; - position: absolute; - border-right: 10px solid var(--second-background-color); - border-left: 10px solid transparent; - border-top: 10px solid var(--second-background-color); - border-bottom: 10px solid transparent; - left: 0; - bottom: -17px; - border-radius: 0 0 0 5px; -} - -#loaderdiv { - position: absolute; - top: 0px; - left: 0px; - width: 100%; - height: 100%; - z-index: 200; -} - -#loader { - border: 16px solid transparent; - border-top: 16px solid var(--secondary-color); - border-bottom: 16px solid var(--primary-color); - border-radius: 50%; - width: 120px; - height: 120px; - animation: spin 2s linear infinite; - position: absolute; - left: calc(50% - 60px); - top: calc(50% - 120px); - z-index: 201; -} - -#loaderText { - position: relative; - margin: 0 auto; - top: calc(50% + 80px); - z-index: 201; - text-align: center; - color: var(--second-background-color); - font-size: 20pt; -} - -@keyframes spin { - 0% { transform: rotate(0deg); } - 100% { transform: rotate(360deg); } -} - -#jobStatusRunning > .section-item { - flex-direction: row; - height: 150px; - padding: 0; - overflow: hidden; -} - -#jobStatusWaiting > .section-item { - flex-direction: row; - height: 150px; - padding: 0; - overflow: hidden; -} - -.section-item > .jobImage { - height: 100%; - width: auto; - left: 0; - top: 0; - border-radius: 10px; -} - -.jobDetails { - display: flex; - flex-direction: column; - height: 100%; - width: 100%; -} - -.section-item > .jobDetails > .jobTitle { - margin: 5px; - font-size: 11pt; - font-weight: bold; - text-wrap: wrap; -} - -.section-item > .jobDetails > .jobProgressBar { - margin: 5px; - height: 10px; - border-radius: 7px; -} - -.section-item > .jobDetails > .jobProgressSpan { - margin: 5px; - margin-left: auto; - margin-right: 5px; -} - -.section-item > .jobDetails > .jobCancel { - margin-top: auto; - margin-bottom: 5px; - margin-left: auto; - margin-right: 5px; - font-size: 12pt; - color: var(--secondary-color); - cursor: pointer; +:root{ + --background-color: #030304; + --second-background-color: white; + --primary-color: #f5a9b8; + --secondary-color: #5bcefa; + --blur-background: rgba(245, 169, 184, 0.58); + --accent-color: #fff; + /* --primary-color: green; + --secondary-color: gold; + --blur-background: rgba(86, 131, 36, 0.8); + --accent-color: olive; */ + --topbar-height: 60px; + box-sizing: border-box; +} + +body{ + padding: 0; + margin: 0; + height: 100vh; + background-color: var(--background-color); + font-family: "Inter", sans-serif; + overflow-x: hidden; +} + +wrapper { + display: flex; + flex-flow: column; + flex-wrap: nowrap; + height: 100vh; +} + +background-placeholder{ + background-color: var(--second-background-color); + opacity: 1; + position: absolute; + width: 100%; + height: 100%; + border-radius: 0 0 5px 0; + z-index: -1; +} + +topbar { + display: flex; + align-items: center; + height: var(--topbar-height); + background-color: var(--secondary-color); + z-index: 100; + box-shadow: 0 0 20px black; +} + +titlebox { + position: relative; + display: flex; + margin: 0 0 0 40px; + height: 100%; + align-items:center; + justify-content:center; +} + +titlebox span{ + cursor: default; + font-size: 24pt; + font-weight: bold; + background: linear-gradient(150deg, var(--primary-color), var(--accent-color)); + -webkit-background-clip: text; + -webkit-text-fill-color: transparent; + margin-left: 20px; +} + +titlebox img { + height: 100%; + cursor: grab; +} + +spacer{ + flex-grow: 1; +} + +filter-box { + display: none; + align-self: center; + flex-direction: column; + position: relative; + + margin: 10px; + background-color: var(--second-background-color); + border-style: solid; + border-color: var(--primary-color); + border-width: 2px; + border-radius: 15px; + min-width: 300px; + width: 50%; + overflow: hidden; + max-height: 50%; + height: 600px; +} + +filter-box.animate { + display: flex; +} + +filter-box border-bar popup-title{ + font-size: 12pt; +} + +filter-box border-bar popup-close { + height: 20px; + width: 20px; + font-size: 12pt; + -webkit-user-select: none; /* Safari */ + -ms-user-select: none; /* IE 10 and IE 11 */ + user-select: none; /* Standard syntax */ +} + +border-bar-button.clearFilter{ + font-weight: bold; + margin: 0px 10px 10px 10px; + border-color: lightgray; + color: gray; + align-content: center; + justify-content: center; +} + +border-bar-button.clearFilter:hover { + background-color: red; + border-color: var(--second-background-color); + color: var(--second-background-color); +} + +status-filter { + display: block; + margin: 10px; + + /*Text Properties*/ + font-size:10pt; + font-weight:bold; + color:white; + text-align: center; + + /*Size*/ + padding: 3px 8px; + border-radius: 6px; + border: 0px; + background-color: inherit; + + cursor: pointer; + -webkit-user-select: none; /* Safari */ + -ms-user-select: none; /* IE 10 and IE 11 */ + user-select: none; /* Standard syntax */ +} + +status-filter[release-status="Ongoing"]{ + background-color: limegreen; +} + +status-filter[release-status="Completed"]{ + background-color: blueviolet; +} + +status-filter[release-status="On Hiatus"]{ + background-color: darkorange; +} + +status-filter[release-status="Cancelled"]{ + background-color: firebrick; +} + +status-filter[release-status="Upcoming"]{ + background-color: aqua; +} + +status-filter[release-status="Status Unavailable"]{ + background-color: gray; +} + + +searchdiv{ + display: flex; + width: 100%; +} + +#searchbox { + display: flex; + padding: 3px 5px; + margin: 5px; + border-style: solid; + border-width: 2px; + border-radius: 10px; + font-size: 12pt; + outline: none; + border-color: lightgray; + flex-grow: 1; + flex-shrink: 1; +} + +#searchbox:focus { + border-color: var(--secondary-color); +} + +.pill { + flex-grow: 0; + height: 14pt; + font-size: 12pt; + border-radius: 9pt; + background-color: var(--primary-color); + padding: 2pt 17px; + color: black; +} + +#connectorFilterBox .pill { + margin: 10px; + cursor: pointer; + -webkit-user-select: none; /* Safari */ + -ms-user-select: none; /* IE 10 and IE 11 */ + user-select: none; /* Standard syntax */ +} + +#settingscog { + cursor: pointer; + margin: 0px 30px; + margin-left: 15px; + height: 50%; + filter: invert(100%) sepia(0%) saturate(7465%) hue-rotate(115deg) brightness(116%) contrast(101%); +} + +#filterFunnel { + cursor: pointer; + margin: 0px 15px; + height: 50%; + filter: invert(100%) sepia(0%) saturate(7465%) hue-rotate(115deg) brightness(116%) contrast(101%); +} + +viewport { + position: relative; + display: flex; + flex-flow: row; + flex-wrap: nowrap; + flex-grow: 1; + height: 100%; + overflow-y: scroll; + scrollbar-color: var(--accent-color) var(--primary-color); + scrollbar-width: thin; +} + +footer { + display: flex; + flex-direction: row; + flex-wrap: nowrap; + width: 100%; + height: 40px; + align-items: center; + justify-content: center; + background-color: var(--primary-color); + align-content: center; +} + +footer > div { + height: 100%; + margin: 0 30px; + display: flex; + flex-direction: row; + flex-wrap: nowrap; + align-items: center; + cursor: pointer; +} + +footer > div > *{ + height: 40%; + margin: 0 5px; +} + +#madeWith { + flex-grow: 1; + text-align: right; + margin-right: 20px; + cursor: url("media/blahaj.png"), grab; +} + +content { + position: relative; + flex-grow: 1; + border-radius: 5px; + display: flex; + flex-direction: row; + flex-wrap: wrap; + justify-content: start; + align-content: start; +} + +#settingsPopup{ + z-index: 300; +} + +popup{ + display: none; + width: 100%; + min-height: 100%; + top: 0; + left: 0; + position: fixed; + z-index: 2; + flex-direction: column; +} + +border-bar { + display: flex; + flex-direction: row; + background-color: var(--primary-color); + color: var(--accent-color); + font-weight: bolder; + padding: 7px 5px; + margin:0; + align-items: center; + position: relative; + width: 100%; +} + +popup-title { + font-size: 14pt; + display: flex; + margin-top: 3px; + margin-left: 5px; + color: var(--second-background-color); +} + +popup-close { + border: none; + background-color: inherit; + color: var(--second-background-color);; + font-weight: inherit; + font-size: 27px; + font-family: 'Trebuchet MS', 'Lucida Sans Unicode', 'Lucida Grande', 'Lucida Sans', Arial, sans-serif; + display: flex; + cursor: pointer; + margin-left: auto; + margin-right: 15px; + height: 32px; + width: 32px; + border-radius: 16px; + align-content: center; + justify-content: center; +} + +popup-close:hover { + background-color: var(--secondary-color); +} + +border-bar > .button-container { + display: flex; + flex-direction: row; + align-items: center; + flex-wrap: wrap; + margin-right: 0; + margin-left: auto; +} + +border-bar-button { + border-style: solid; + border-width: 2px; + background-color: inherit; + color: var(--second-background-color); + font-weight: inherit; + font-size: inherit; + font-family: inherit; + display: flex; + cursor: pointer; + margin: 0px 5px; + padding: 5px 20px; + border-radius: 20px; + height: 20px; + align-items: center; + border-color: var(--accent-color); + -webkit-user-select: none; /* Safari */ + -ms-user-select: none; /* IE 10 and IE 11 */ + user-select: none; /* Standard syntax */ +} + +border-bar-button:hover { + border-color: var(--secondary-color); +} + +border-bar-button.primary { + background-color: var(--secondary-color); + color: var(--accent-color); + border-color: var(--primary-color); + margin-right: 10px; +} + +border-bar-button.primary:hover { + border-color: var(--accent-color); +} + +border-bar-button.section { + font-weight: bold; + color: darkgray; + border-color: darkgray; + text-align: center; + padding: 5px; + flex-grow: 1; + justify-content: center; +} + +border-bar-button.section:hover { + color: var(--secondary-color); + border-color: var(--secondary-color); +} + + +popup popup-window { + position: absolute; + z-index: 3; + left: 10%; + top: 10%; + height: 80%; + width: 80%; + display: flex; + flex-direction: column; + background-color: var(--second-background-color); + border-radius: 15px; + overflow: hidden; +} + +popup#jobStatusView popup-window { + left: 20%; + top: 20%; + height: 60%; + width: 60%; +} + +popup-content{ + display: flex; + flex-direction: column; + align-items: left; + height: calc(100% - 60px); + overflow-y: auto; + overflow-x: hidden; + scrollbar-width: thin; + scrollbar-color: var(--secondary-color) var(--second-background-color); +} + +popup-content > .popup-section { + margin: 5px; + margin-bottom: 10px; + font-size: 10pt; + font-weight: 100; + display: block; + border-top-style: solid; + border-top-width: 1px; + border-top-color: lightgray; + width: calc(100%-10px); + padding: 10px; +} + +.section-content { + display: flex; + flex-direction: row; + width: 100%; + flex-wrap: wrap; +} + +.section-item { + display: flex; + flex-direction: column; + width: 22%; + min-width: 300px; + height: auto; + border-radius: 10px; + border-style: solid; + border-width: 1px; + border-color: lightgray; + margin: 7px; + padding: 5px; +} + +.section-item.dyn-height { + height: fit-content; +} + +.section-item > .title { + font-weight: bold; + vertical-align: bottom; + line-height: 32px; + font-size: 12pt; + width: 100%; +} + +a:link { + color: inherit; + text-decoration: none; +} + +a:visited { + color: inherit; + text-decoration: none; +} + +a:hover { + color: inherit; + text-decoration: underline solid var(--secondary-color) 3px; +} + +a:active { + color: inherit; + text-decoration: none; +} + +.section-item > .title > img { + width: auto; + height: 32px; + margin: 5px; + vertical-align: middle; + border-radius: 5px; +} + +.section-item > .title > connector-configured { + display:block; + height: 10px; + width: 10px; + border-radius: 50%; + margin: 5px; + float: right; + top: 5px; + right: 5px; +} + +.section-item > .title > connector-configured::after { + display: block; + content: attr(configuration); + float: right; + width: max-content; + width: -webkit-max-content; + width: -mox-max-content; + width: intrinsic; + + visibility: hidden; + + /*Text Properties*/ + font-size:8pt; + font-weight:bold; + color:white; + text-align: right; + + /*Size*/ + padding: 0px 8px; + border-radius: 6px; + border: 0px; + background-color: inherit; +} + +.section-item > .title > connector-configured:hover::after{ + visibility:visible; +} + +.section-item > .title > connector-configured[configuration="Active"] { + background-color: limegreen; +} + +.section-item > .title > connector-configured[configuration="Not Configured"] { + background-color: gray; +} + +.section-item > input { + margin: 2px; + padding: 5px; + height: 20px; + border-radius: 10px; + border-style: solid; + outline: none; +} +.section-item > input:focus { + border-color: var(--secondary-color); +} + +.section-item > row { + width: calc(100%-20px); + display: flex; + flex-direction: row; + align-items: center; + margin-left: 5px; + margin-bottom: 5px; +} + +.section-item > row > input { + margin-left: auto; + margin-right: 2px; + padding: 5px; + height: 20px; + border-radius: 10px; + border-style: solid; + outline: none; + flex-grow: 0; + text-align: end; + float: right; + width: 200px; +} +.section-item > row > input:focus { + border-color: var(--secondary-color); +} + +.section-item > row > select { + margin-left: auto; + margin-right: 2px; + padding: 2px; + height: 30px; + border-radius: 10px; + border-style: solid; + outline: none; + flex-grow: 0; + text-align: end; + float: right; + width: 200px; +} + +.section-item > row > select:focus { + border-color: var(--secondary-color); +} + +.section-buttons-container { + display: flex; + flex-direction: row; + align-items: center; + position: relative; + margin-left: auto; + margin-top: auto; + margin-bottom: 0; + margin-right: 0; +} + +.section-buttons-container > .section-button { + font-size: 12px; + padding: 3px 10px; + margin: 3px; + border-radius: 5px; + border-style: solid; + border-width: 1px; + border-color: lightgray; + font-weight: bold; + color: gray; + cursor: pointer; + -webkit-user-select: none; /* Safari */ + -ms-user-select: none; /* IE 10 and IE 11 */ + user-select: none; /* Standard syntax */ +} + +.section-button#reset:hover { + color: red; + border-color: red; +} +.section-buttons-container > .section-button:hover { + border-color: var(--secondary-color); + color: var(--secondary-color); +} + +#newMangaPopup > div { + z-index: 3; + position: relative; +} + +#newMangaPopup > #newMangaPopupSelector { + width: 600px; + height: 40px; + margin: 80px auto 0; +} + +#newMangaPopup > div > #newMangaConnector, #newMangaTitle, #newMangaTranslatedLanguage { + margin: 0; + display: inline-block; + height: 40px; +} + +#newMangaPopup #newMangaConnector { + width: 100px; + padding: 0 0 0 5px; + border-radius: 5px 0 0 5px; + border: 0; + border-right: 1px solid darkgray; +} + +#newMangaPopup #newMangaTitle{ + width: 445px; + padding: 0 5px 0 5px; + border: 0; +} + +#newMangaPopup #newMangaTranslatedLanguage { + width: 45px; + border-radius: 0 5px 5px 0; + border: 0; + border-left: 1px solid darkgray; + margin-left: -5px; +} + +#newMangaResult { + display: none; + flex-direction: row; + justify-content: flex-start; + margin: 5px auto 0; + border-radius: 5px; + padding: 5px; + width: min-content; + max-width: 98%; + max-height: 400px; + overflow-x: scroll; + overflow-y: hidden; +} + +blur-background { + width: 100%; + height: 100%; + position: absolute; + left: 0; + background: var(--blur-background); + box-shadow: 0 4px 30px rgba(0, 0, 0, 0.1); + backdrop-filter: blur(4.5px); + -webkit-backdrop-filter: blur(4.5px); +} + +#publicationViewerPopup{ + z-index: 5; +} + +publication-viewer{ + display: block; + width: 460px; + position: absolute; + top: 200px; + left: 400px; + background-color: var(--accent-color); + border-radius: 5px; + overflow: hidden; + padding: 15px; +} + +publication-viewer::after{ + content: ''; + position: absolute; + left: 0; top: 0; + border-radius: 5px; + width: 100%; + height: 100%; + background: rgba(0,0,0,0.8); + backdrop-filter: blur(3px); +} + +publication-viewer img { + position: absolute; + left: 0; + top: 0; + height: 100%; + width: 100%; + object-fit: cover; + border-radius: 5px; + z-index: 0; +} + +publication-viewer publication-details > * { + margin: 5px 0; +} + +publication-viewer publication-details publication-name { + width: initial; + overflow-x: scroll; + white-space: nowrap; + scrollbar-width: none; +} + +publication-viewer publication-details publication-tags::before { + content: "Tags"; + display: block; + font-weight: bolder; +} + +publication-viewer publication-details publication-tags { + overflow-x: scroll; + white-space: nowrap; + scrollbar-width: none; +} + +publication-viewer publication-details publication-author::before { + content: "Author: "; + font-weight: bolder; +} + +publication-viewer publication-details publication-description::before { + content: "Description"; + display: block; + font-weight: bolder; +} + +publication-viewer publication-details publication-description { + font-size: 12pt; + margin: 5px 0; + height: 145px; + overflow-x: scroll; +} + +publication-viewer publication-details publication-interactions { + display: flex; + flex-direction: row; + justify-content: end; + align-items: start; + width: 100%; +} + +publication-viewer publication-details publication-interactions > * { + margin: 0 10px; + font-size: 16pt; + cursor: pointer; +} + +publication-viewer publication-details publication-interactions publication-starttask { + color: var(--secondary-color); +} + +publication-viewer publication-details publication-interactions publication-delete { + color: red; +} + +publication-view publication-details publication-interactions publication-canceltask { + color: yellow; +} + +publication-viewer publication-details publication-interactions publication-add { + color: limegreen; +} + +footer-tag-popup { + display: none; + padding: 2px 4px; + position: fixed; + bottom: 58px; + left: 20px; + background-color: var(--second-background-color); + z-index: 8; + border-radius: 5px; + max-height: 400px; +} + +footer-tag-content{ + position: relative; + max-height: 400px; + display: flex; + flex-direction: column; + flex-wrap: nowrap; + overflow-y: scroll; +} + +footer-tag-content > * { + margin: 2px 5px; +} + +footer-tag-popup::before{ + content: ""; + width: 0; + height: 0; + position: absolute; + border-right: 10px solid var(--second-background-color); + border-left: 10px solid transparent; + border-top: 10px solid var(--second-background-color); + border-bottom: 10px solid transparent; + left: 0; + bottom: -17px; + border-radius: 0 0 0 5px; +} + +#loaderdiv { + position: absolute; + top: 0px; + left: 0px; + width: 100%; + height: 100%; + z-index: 200; +} + +#loader { + border: 16px solid transparent; + border-top: 16px solid var(--secondary-color); + border-bottom: 16px solid var(--primary-color); + border-radius: 50%; + width: 120px; + height: 120px; + animation: spin 2s linear infinite; + position: absolute; + left: calc(50% - 60px); + top: calc(50% - 120px); + z-index: 201; +} + +#loaderText { + position: relative; + margin: 0 auto; + top: calc(50% + 80px); + z-index: 201; + text-align: center; + color: var(--second-background-color); + font-size: 20pt; +} + +@keyframes spin { + 0% { transform: rotate(0deg); } + 100% { transform: rotate(360deg); } +} + +#jobStatusRunning > .section-item { + flex-direction: row; + height: 150px; + padding: 0; + overflow: hidden; +} + +#jobStatusWaiting > .section-item { + flex-direction: row; + height: 150px; + padding: 0; + overflow: hidden; +} + +.section-item > .jobImage { + height: 100%; + width: auto; + left: 0; + top: 0; + border-radius: 10px; +} + +.jobDetails { + display: flex; + flex-direction: column; + height: 100%; + width: 100%; +} + +.section-item > .jobDetails > .jobTitle { + margin: 5px; + font-size: 11pt; + font-weight: bold; + text-wrap: wrap; +} + +.section-item > .jobDetails > .jobProgressBar { + margin: 5px; + height: 10px; + border-radius: 7px; +} + +.section-item > .jobDetails > .jobProgressSpan { + margin: 5px; + margin-left: auto; + margin-right: 5px; +} + +.section-item > .jobDetails > .jobCancel { + margin-top: auto; + margin-bottom: 5px; + margin-left: auto; + margin-right: 5px; + font-size: 12pt; + color: var(--secondary-color); + cursor: pointer; } \ No newline at end of file diff --git a/Website/styles/card_compact.css b/Website/styles/card_compact.css index 74a6648..1b8676e 100644 --- a/Website/styles/card_compact.css +++ b/Website/styles/card_compact.css @@ -1,159 +1,159 @@ -#addPublication { - cursor: pointer; - background-color: var(--secondary-color); - width: 180px; - height: 300px; - border-radius: 5px; - margin: 10px 10px; - padding: 15px 20px; - position: relative; -} - -#addPublication p{ - width: 100%; - text-align: center; - font-size: 150pt; - vertical-align: middle; - line-height: 300px; - margin: 0; - color: var(--accent-color); -} - -.pill { - flex-grow: 0; - height: 14pt; - font-size: 12pt; - border-radius: 9pt; - background-color: var(--primary-color); - padding: 2pt 17px; - color: black; -} - -publication{ - cursor: pointer; - background-color: var(--secondary-color); - width: 180px; - height: 300px; - border-radius: 5px; - margin: 10px 10px; - padding: 15px 19px; - position: relative; - flex-shrink: 0; -} - -publication::after{ - content: ''; - position: absolute; - left: 0; top: 0; - border-radius: 5px; - width: 100%; height: 100%; - background: linear-gradient(rgba(0,0,0,0.8), rgba(0, 0, 0, 0.7),rgba(0, 0, 0, 0.2)); -} - -publication-information { - display: flex; - flex-direction: column; - justify-content: start; -} - -publication-details { - display: flex; - flex-direction: column; - justify-content: start; -} - -publication-information * { - z-index: 1; - color: var(--accent-color); -} - -publication-details * { - z-index: 1; - color: var(--accent-color); -} - -connector-name{ - width: fit-content; - margin: 10px 0; -} - -publication-name{ - width: fit-content; - font-size: 16pt; - font-weight: bold; - color: white; -} - -publication-status { - display:block; - height: 10px; - width: 10px; - border-radius: 50%; - margin: 5px; - position: absolute; - top: 5px; - right: 5px; - z-index: 2; - box-shadow: rgba(0, 0, 0, 0.16) 0px 1px 10px, rgb(51, 51, 51) 0px 0px 10px 3px; -} - -publication-status::after { - content: attr(release-status); - position: absolute; - top: 0; - right: 0; - - visibility: hidden; - - /*Text Properties*/ - font-size:10pt; - font-weight:bold; - color:white; - text-align: center; - - /*Size*/ - padding: 3px 8px; - border-radius: 6px; - border: 0px; - background-color: inherit; -} - -publication-status:hover::after{ - visibility:visible; -} - - -publication-status[release-status="Ongoing"]{ - background-color: limegreen; -} - -publication-status[release-status="Completed"]{ - background-color: blueviolet; -} - -publication-status[release-status="On Hiatus"]{ - background-color: darkorange; -} - -publication-status[release-status="Cancelled"]{ - background-color: firebrick; -} - -publication-status[release-status="Upcoming"]{ - background-color: aqua; -} - -publication-status[release-status="Status Unavailable"]{ - background-color: gray; -} - -publication img { - position: absolute; - top: 0; - left: 0; - width: 100%; - height: 100%; - object-fit: cover; - z-index: 0; - border-radius: 5px; +#addPublication { + cursor: pointer; + background-color: var(--secondary-color); + width: 180px; + height: 300px; + border-radius: 5px; + margin: 10px 10px; + padding: 15px 20px; + position: relative; +} + +#addPublication p{ + width: 100%; + text-align: center; + font-size: 150pt; + vertical-align: middle; + line-height: 300px; + margin: 0; + color: var(--accent-color); +} + +.pill { + flex-grow: 0; + height: 14pt; + font-size: 12pt; + border-radius: 9pt; + background-color: var(--primary-color); + padding: 2pt 17px; + color: black; +} + +publication{ + cursor: pointer; + background-color: var(--secondary-color); + width: 180px; + height: 300px; + border-radius: 5px; + margin: 10px 10px; + padding: 15px 19px; + position: relative; + flex-shrink: 0; +} + +publication::after{ + content: ''; + position: absolute; + left: 0; top: 0; + border-radius: 5px; + width: 100%; height: 100%; + background: linear-gradient(rgba(0,0,0,0.8), rgba(0, 0, 0, 0.7),rgba(0, 0, 0, 0.2)); +} + +publication-information { + display: flex; + flex-direction: column; + justify-content: start; +} + +publication-details { + display: flex; + flex-direction: column; + justify-content: start; +} + +publication-information * { + z-index: 1; + color: var(--accent-color); +} + +publication-details * { + z-index: 1; + color: var(--accent-color); +} + +connector-name{ + width: fit-content; + margin: 10px 0; +} + +publication-name{ + width: fit-content; + font-size: 16pt; + font-weight: bold; + color: white; +} + +publication-status { + display:block; + height: 10px; + width: 10px; + border-radius: 50%; + margin: 5px; + position: absolute; + top: 5px; + right: 5px; + z-index: 2; + box-shadow: rgba(0, 0, 0, 0.16) 0px 1px 10px, rgb(51, 51, 51) 0px 0px 10px 3px; +} + +publication-status::after { + content: attr(release-status); + position: absolute; + top: 0; + right: 0; + + visibility: hidden; + + /*Text Properties*/ + font-size:10pt; + font-weight:bold; + color:white; + text-align: center; + + /*Size*/ + padding: 3px 8px; + border-radius: 6px; + border: 0px; + background-color: inherit; +} + +publication-status:hover::after{ + visibility:visible; +} + + +publication-status[release-status="Ongoing"]{ + background-color: limegreen; +} + +publication-status[release-status="Completed"]{ + background-color: blueviolet; +} + +publication-status[release-status="On Hiatus"]{ + background-color: darkorange; +} + +publication-status[release-status="Cancelled"]{ + background-color: firebrick; +} + +publication-status[release-status="Upcoming"]{ + background-color: aqua; +} + +publication-status[release-status="Status Unavailable"]{ + background-color: gray; +} + +publication img { + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + object-fit: cover; + z-index: 0; + border-radius: 5px; } \ No newline at end of file diff --git a/Website/styles/card_hover.css b/Website/styles/card_hover.css index 6d830ed..192c403 100644 --- a/Website/styles/card_hover.css +++ b/Website/styles/card_hover.css @@ -1,172 +1,172 @@ -#addPublication { - cursor: pointer; - background-color: var(--secondary-color); - width: 180px; - height: 300px; - border-radius: 5px; - margin: 10px 10px; - padding: 15px 20px; - position: relative; -} - -#addPublication p{ - width: 100%; - text-align: center; - font-size: 150pt; - vertical-align: middle; - line-height: 300px; - margin: 0; - color: var(--accent-color); -} - -.pill { - flex-grow: 0; - height: 14pt; - font-size: 12pt; - border-radius: 9pt; - background-color: var(--primary-color); - padding: 2pt 17px; - color: black; -} - -publication{ - cursor: pointer; - background-color: var(--secondary-color); - width: 180px; - height: 300px; - border-radius: 5px; - margin: 10px 10px; - padding: 15px 19px; - position: relative; - flex-shrink: 0; -} - -publication:hover { - background-color: black; -} - -publication:hover::after{ - background: linear-gradient(rgba(0,0,0,0.8), rgba(0, 0, 0, 0.7),rgba(0, 0, 0, 0.2)); -} - -publication:hover > publication-information { - display: flex; - opacity:1; -} - -publication::after{ - content: ''; - position: absolute; - left: 0; top: 0; - border-radius: 5px; - width: 100%; height: 100%; - background: none; -} - -publication-information { - display: none; - flex-direction: column; - justify-content: start; -} - -publication-information * { - z-index: 1; - color: white; -} - -connector-name{ - width: fit-content; - margin: 10px 0; -} - -publication-name{ - width: fit-content; - font-size: 16pt; - font-weight: bold; -} - -publication-status { - display:block; - height: 10px; - width: 10px; - border-radius: 50%; - margin: 5px; - position: absolute; - top: 5px; - right: 5px; - z-index: 2; - box-shadow: rgba(0, 0, 0, 0.16) 0px 1px 10px, rgb(51, 51, 51) 0px 0px 10px 3px; -} - -publication-status::after { - content: attr(release-status); - position: absolute; - top: 0; - right: 0; - - visibility: hidden; - - /*Text Properties*/ - font-size:10pt; - font-weight:bold; - color:white; - text-align: center; - - /*Size*/ - padding: 3px 8px; - border-radius: 6px; - border: 0px; - background-color: inherit; -} - -publication-status:hover::after{ - visibility:visible; -} - - -publication-status[release-status="Ongoing"]{ - background-color: limegreen; -} - -publication-status[release-status="Completed"]{ - background-color: blueviolet; -} - -publication-status[release-status="On Hiatus"]{ - background-color: darkorange; -} - -publication-status[release-status="Cancelled"]{ - background-color: firebrick; -} - -publication-status[release-status="Upcoming"]{ - background-color: aqua; -} - -publication-status[release-status="Status Unavailable"]{ - background-color: gray; -} - - -publication-details { - display: flex; - flex-direction: column; - justify-content: start; -} - -publication-details * { - z-index: 1; - color: var(--accent-color); -} - -publication img { - position: absolute; - top: 0; - left: 0; - width: 100%; - height: 100%; - object-fit: cover; - z-index: 0; - border-radius: 5px; +#addPublication { + cursor: pointer; + background-color: var(--secondary-color); + width: 180px; + height: 300px; + border-radius: 5px; + margin: 10px 10px; + padding: 15px 20px; + position: relative; +} + +#addPublication p{ + width: 100%; + text-align: center; + font-size: 150pt; + vertical-align: middle; + line-height: 300px; + margin: 0; + color: var(--accent-color); +} + +.pill { + flex-grow: 0; + height: 14pt; + font-size: 12pt; + border-radius: 9pt; + background-color: var(--primary-color); + padding: 2pt 17px; + color: black; +} + +publication{ + cursor: pointer; + background-color: var(--secondary-color); + width: 180px; + height: 300px; + border-radius: 5px; + margin: 10px 10px; + padding: 15px 19px; + position: relative; + flex-shrink: 0; +} + +publication:hover { + background-color: black; +} + +publication:hover::after{ + background: linear-gradient(rgba(0,0,0,0.8), rgba(0, 0, 0, 0.7),rgba(0, 0, 0, 0.2)); +} + +publication:hover > publication-information { + display: flex; + opacity:1; +} + +publication::after{ + content: ''; + position: absolute; + left: 0; top: 0; + border-radius: 5px; + width: 100%; height: 100%; + background: none; +} + +publication-information { + display: none; + flex-direction: column; + justify-content: start; +} + +publication-information * { + z-index: 1; + color: white; +} + +connector-name{ + width: fit-content; + margin: 10px 0; +} + +publication-name{ + width: fit-content; + font-size: 16pt; + font-weight: bold; +} + +publication-status { + display:block; + height: 10px; + width: 10px; + border-radius: 50%; + margin: 5px; + position: absolute; + top: 5px; + right: 5px; + z-index: 2; + box-shadow: rgba(0, 0, 0, 0.16) 0px 1px 10px, rgb(51, 51, 51) 0px 0px 10px 3px; +} + +publication-status::after { + content: attr(release-status); + position: absolute; + top: 0; + right: 0; + + visibility: hidden; + + /*Text Properties*/ + font-size:10pt; + font-weight:bold; + color:white; + text-align: center; + + /*Size*/ + padding: 3px 8px; + border-radius: 6px; + border: 0px; + background-color: inherit; +} + +publication-status:hover::after{ + visibility:visible; +} + + +publication-status[release-status="Ongoing"]{ + background-color: limegreen; +} + +publication-status[release-status="Completed"]{ + background-color: blueviolet; +} + +publication-status[release-status="On Hiatus"]{ + background-color: darkorange; +} + +publication-status[release-status="Cancelled"]{ + background-color: firebrick; +} + +publication-status[release-status="Upcoming"]{ + background-color: aqua; +} + +publication-status[release-status="Status Unavailable"]{ + background-color: gray; +} + + +publication-details { + display: flex; + flex-direction: column; + justify-content: start; +} + +publication-details * { + z-index: 1; + color: var(--accent-color); +} + +publication img { + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + object-fit: cover; + z-index: 0; + border-radius: 5px; } \ No newline at end of file