This commit is contained in:
DBChoco
2022-04-06 17:58:18 +02:00
commit d7247313cb
63 changed files with 45387 additions and 0 deletions

6
.gitignore vendored Normal file
View File

@@ -0,0 +1,6 @@
node_modules
.vscode
dist
flatpak/.flatpak-builder
flatpak/build
ressources/quran/downloads

21
LICENSE Normal file
View File

@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2022 DarkBlackChocolate
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

264
README.md Normal file
View File

@@ -0,0 +1,264 @@
# Muezzin
<!-- PROJECT LOGO -->
<br />
<div align="center">
<a href="https://github.com/DBChoco/MuezzinPrayerTimes">
<img src="ressources/images/icon.png" alt="Logo" width="150" height="150">
</a>
<h3 align="center">Muezzin - مؤذن</h3>
<p align="center">
A prayer times and Adhan application for Windows, macOS and GNU/Linux
<br />
<a href="https://github.com/DBChoco/MuezzinPrayerTimes/releases/latest"><strong>Download »</strong></a>
<br />
<br />
<a href="https://github.com/DBChoco/MuezzinPrayerTimes">View Demo</a>
·
<a href="https://github.com/DBChoco/MuezzinPrayerTimes/issues">Report Bug</a>
·
<a href="https://github.com/DBChoco/MuezzinPrayerTimes/issues">Request Feature</a>
</p>
</div>
<p align="center">
<a href="https://github.com/DBChoco/Muezzin/graphs/contributors" alt="Contributors">
<img src="https://img.shields.io/github/contributors/DBChoco/Muezzin" />
</a>
<a href="https://github.com/DBChoco/Muezzin/pulse" alt="Activity">
<img src="https://img.shields.io/github/commit-activity/m/DBChoco/Muezzin" />
</a>
<a href="https://github.com/DBChoco/Muezzin" alt="Activity">
<img src="https://img.shields.io/github/stars/DBChoco/Muezzin.svg" />
</a>
<a href="https://github.com/login?return_to=https%3A%2F%2Fgithub.com%2FDBChoco" alt="Activity">
<img src="https://img.shields.io/github/followers/DBChoco.svg?style=social&label=Follow&maxAge=2592000" />
</a>
<a href="https://www.reddit.com/r/Muezzin/" alt="Reddit">
<img src="https://aleen42.github.io/badges/src/reddit.svg" />
</a>
<br>
<a href="https://github.com/DBChoco/Muezzin" alt="Activity">
<img src="https://img.shields.io/github/release/DBChoco/Muezzin.svg" />
</a>
<a href="https://aur.archlinux.org/packages/muezzin-bin" alt="AUR">
<img src="https://img.shields.io/aur/version/muezzin-bin" />
</a>
<a href="https://snapcraft.io/muezzin">
<img alt="muezzin" src="https://snapcraft.io/muezzin/badge.svg" />
</a>
</p>
<a href='https://flathub.org/apps/details/io.github.dbchoco.muezzin'><img width='240' alt='Download on Flathub' src='https://flathub.org/assets/badges/flathub-badge-en.png'/></a>
<a href="https://snapcraft.io/muezzin">
<img alt="Get it from the Snap Store" src="https://snapcraft.io/static/images/badges/en/snap-store-black.svg" />
</a>
<!-- TABLE OF CONTENTS -->
## Table of Contents
<ul>
<li><a href="#muezzin">Muezzin</a>
<ul>
<li>
<a href="#about-the-project">About The Project</a>
<ul>
<li><a href="#supported-languages">Supported Languages</a></li>
<li><a href="#built-with">Built With</a></li>
<li><a href="#roadmap">Roadmap</a></li>
</ul>
</li>
<li>
<a href="#how-to-install">How To Install</a>
<ul>
<li><a href="#windows">Windows</a></li>
<li><a href="#macos">macOS</a></li>
<li><a href="#gnulinux">GNU/Linux</a>
<ul>
<li><a href="#global">Global</a></li>
<li><a href="#arch-based-distributions">Arch based distributions</a></li>
<li><a href="#debian-based-distributions">Debian based distributions</a></li>
<li><a href="#others">Others</a></li>
</ul>
</li>
</ul>
</li>
<li><a href="#contributing">Contributing</a></li>
<li><a href="#license">License</a></li>
<li><a href="#contact">Contact</a></li>
<li><a href="#acknowledgments">Acknowledgments</a></li>
</ul>
</li>
</ul>
<!-- ABOUT THE PROJECT -->
## About The Project
<div align="center">
<img src="screenshots/darkMode.png" alt="screenshot1" width="70%">
</div>
We may all be familiar with Muslim Pro on our smartphones, but as someone who doesn't use my phone much, I wanted an app to help me keep track of prayer times without having to go on my smartphone, I wanted an app that was highly customizable and would let me choose <strong> my own </strong> Adhan and theme. And Alhamdoulillah after a few weeks of hard work, I did just that.
On Muezzin you can choose to play an Adhan or not, you can import your own audio file; you can also chose to have a background or not, and import your own.
Currently we support 10 languages (and growing), and if you want us to add one more, you can submit a report <a href="https://github.com/DBChoco/MuezzinPrayerTimes/issues">here</a>
<div align="center">
<img src="screenshots/settingsDark.png" alt="screenshot1" width="70%">
</div>
The app also contains a Qur'an reader that is able to translations and transliterations. It supports many languages and you can expect many improvements in the future inshaAllah.
<div align="center">
<img src="screenshots/quranDark.png" alt="screenshot1" width="70%">
</div>
<!-- SUPPORTED LANGUAGES -->
### Supported Languages
* English
* Français
* Español
* Italiano
* Arabic
* Deutsch
* Nederlands
* Norks
* Svenska
* Dansk
* Urdu
* Turkish (thanks @emrergin)
### Built With
* [Electron](https://www.electronjs.org/)
* [Bootstrap](https://getbootstrap.com)
* [Bootstrap-dark-5](https://vinorodrigues.github.io/bootstrap-dark-5/)
* [adhan-js](https://github.com/batoulapps/adhan-js)
* [FontAwesome](https://fontawesome.com/)
* [Moment & Moment-timezone](https://momentjs.com/)
* [Quran.com API](https://quran.api-docs.io)
* Many electron modules
<!-- ROADMAP -->
### Roadmap
- [x] Add Qur'an reading page
- [ ] Improve the Qur'an page
- [ ] Add mosque mode (improved visibility from afar & delays to prayers) (delayed)
- [ ] Mosque interface with delays
- [ ] Manual times
- [ ] Custom text
- [ ] Add Tasbih
- [ ] Add weather widget
- [x] Add icons to settings
- [ ] Multi-language Support
- [ ] Turkish
See the [open issues](https://github.com/DBChoco/MuezzinPrayerTimes/issues) for a full list of proposed features (and known issues).
<p align="right">(<a href="#muezzin">back to top</a>)</p>
<!-- How to install -->
## How to install
### Windows
From the <a href="https://github.com/DBChoco/MuezzinPrayerTimes/releases/latest"><strong>download page</strong></a>, select the .EXE installer.
Download it and install it.
You might receive a warning, that is because the app is not signed and I do not have the money to do so. The app is completely safe, you can read through the source code or have a trusted friend do it for you, all the code is <b>Open-Source</b>.
### macOS
From the <a href="https://github.com/DBChoco/MuezzinPrayerTimes/releases/latest"><strong>download page</strong></a>, select the .DMG installer.
Download it and install it.
Same problem as the Windows installer
> You might receive a warning, that is because the app is not signed and I do not have the money to do so. The app is completely safe, you can read through the source code or have a trusted friend do it for you, all the code is <b>Open-Source</b>.
### GNU/Linux
### Global
<a href='https://flathub.org/apps/details/io.github.dbchoco.muezzin'><img width='240' alt='Download on Flathub' src='https://flathub.org/assets/badges/flathub-badge-en.png'/></a>
<a href="https://snapcraft.io/muezzin">
<img alt="Get it from the Snap Store" src="https://snapcraft.io/static/images/badges/en/snap-store-black.svg" />
</a>
#### Arch based distributions
You can either:
<ul>
<li><a href="https://github.com/DBChoco/MuezzinPrayerTimes/releases/latest"><strong>Download the .PACMAN file</strong></a> and install it through your favorite package manager</li>
<li>Install it thorugh the AUR package <a href="https://aur.archlinux.org/packages/muezzin-bin"><strong>muezzin-bin</strong></a> with
</li>
`yay -S muezzin-bin`
</ul>
#### Debian based distributions
You can download the <a href="https://github.com/DBChoco/MuezzinPrayerTimes/releases/latest"><strong>Download the .DEB file</strong></a> and install it through your favorite package manager
`sudo apt install path/to/file.deb`
#### Others
For other distributions, on the <a href="https://github.com/DBChoco/MuezzinPrayerTimes/releases/latest"><strong>download page</strong></a> you can choose from:
<ul>
<li>The .APPIMAGE file, which you can run on any Linux distribution and add to your autorun script</li>
<li>The .TAR.GZ file, which you can unarchive wherever you want and launch via the terminal</li>
</ul>
<p align="right">(<a href="#muezzin">back to top</a>)</p>
<!-- CONTRIBUTING -->
## Contributing
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".
If you noticed a translation error or want to add a language yourself, feel free to contact me!
**Don't forget to give the project a star! Thanks again!**
<p align="right">(<a href="#muezzin">back to top</a>)</p>
<!-- LICENSE -->
## License
Distributed under the MIT License. See `LICENSE.txt` for more information.
<!-- CONTACT -->
## Contact
Project Link: [https://github.com/DBChoco/Muezzin](https://github.com/your_username/repo_name)
Discord: [Official Discord server](https://discord.gg/cpF9TTstN5)
<!-- ACKNOWLEDGMENTS -->
## Acknowledgments
* [Source of images](https://unsplash.com/)
* [Source of Mecca Adhan](https://www.youtube.com/watch?v=MaEzj5eRmjc&t)
* [Source of al-Aqsa Adhan](https://www.youtube.com/watch?v=z2xEwSi2vaI)
* [Source of Bismillah startup sound](https://www.youtube.com/c/FatihSeferagic/featured)
* [Inspiration for logo](https://www.youtube.com/watch?v=oM5hNuAmWs0)
* [Template for the README page](https://github.com/othneildrew/Best-README-Template)
* [Source of Arabic fonts](https://github.com/fawazahmed0/quran-api)
* Thanks to a few of my friends for helping me translate Muezzin and build it for macOS
### Source of images
* [Light mode background image](https://unsplash.com/photos/njEXjDmYn8w)
* [Dark mode background image](https://unsplash.com/photos/TAfqq1B3-2s)
<p align="right">(<a href="#muezzin">back to top</a>)</p>

1
_config.yml Normal file
View File

@@ -0,0 +1 @@
theme: jekyll-theme-cayman

File diff suppressed because it is too large Load Diff

15118
flatpak/generated-sources.json Normal file

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,10 @@
[Desktop Entry]
Name=Muezzin
Exec=muezzin-run
Terminal=false
Type=Application
Icon=io.github.dbchoco.muezzin
StartupWMClass=Muezzin
Comment=Islamic prayer times app
MimeType=x-scheme-handler/muezzin;
Categories=Utility;

View File

@@ -0,0 +1,55 @@
<?xml version="1.0" encoding="UTF-8"?>
<component type="desktop-application">
<id>io.github.dbchoco.muezzin</id>
<name>Muezzin</name>
<summary>Islamic prayer times application</summary>
<url type="homepage">https://github.com/DBChoco/Muezzin</url>
<url type="bugtracker">https://github.com/DBChoco/Muezzin/issues</url>
<url type="contact">https://www.reddit.com/r/Muezzin/</url>
<metadata_license>CC0-1.0</metadata_license>
<project_license>MIT</project_license>
<description>
<p>
Islamic prayer times (Adhan) and Qur'an application that is highly customizable, free and open source.
Muezzin supports many languages, advanced prayer options and many more.
Keywords: Athan, Adhan, Azan, Islam, Muslim, Prayers, times, Muezzin, Salah, Salat, Namaz, Islamic, Hijri, Quran, Qur'an, Koran.
</p>
</description>
<content_rating type="oars-1.0" />
<launchable type="desktop-id">io.github.dbchoco.muezzin.desktop</launchable>
<screenshots>
<screenshot type="default">
<caption>Main Screen - Light Mode</caption>
<image>https://github.com/DBChoco/Muezzin/blob/master/screenshots/lightMode.png?raw=true</image>
</screenshot>
<screenshot>
<caption>Main Screen - Dark Mode</caption>
<image>https://github.com/DBChoco/Muezzin/blob/master/screenshots/darkMode.png?raw=true</image>
</screenshot>
<screenshot>
<caption>Settings Screen</caption>
<image>https://github.com/DBChoco/Muezzin/blob/master/screenshots/settingsDark.png?raw=true</image>
</screenshot>
</screenshots>
<releases>
<release version="v0.2.0" date="2022-04-04">
<description>
<p>General</p>
<ul>
<li>Added Qur'an reader with translations and transliterations.</li>
<li>You can now double-click on the system tray (Windows and macOS).</li>
<li>There is now a default background image for dark mode.</li>
<li>Added Turkish (thanks to @emrergin).</li>
<li>Menu bar is now hidden by default (press ALT to reveal it).</li>
</ul>
<p>BugFixes</p>
<ul>
<li>Fixed a few visual elements.</li>
<li>Fixed a few things in the main.js and renderer.js.</li>
</ul>
</description>
</release>
</releases>
</component>

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

View File

@@ -0,0 +1,71 @@
app-id: io.github.dbchoco.muezzin
runtime: org.freedesktop.Platform
runtime-version: '21.08'
sdk: org.freedesktop.Sdk
base: org.electronjs.Electron2.BaseApp
base-version: '21.08'
sdk-extensions:
- org.freedesktop.Sdk.Extension.node14
command: muezzin-run
separate-locales: false
finish-args:
- --share=ipc
- --socket=x11
- --socket=pulseaudio
- --share=network
build-options:
append-path: /usr/lib/sdk/node14/bin
env:
NPM_CONFIG_LOGLEVEL: info
modules:
- name: muezzin
buildsystem: simple
build-options:
env:
XDG_CACHE_HOME: /run/build/muezzin/flatpak-node/cache
npm_config_cache: /run/build/muezzin/flatpak-node/npm-cache
npm_config_nodedir: /usr/lib/sdk/node14
npm_config_offline: 'true'
subdir: main
sources:
- type: archive
url: https://github.com/DBChoco/Muezzin/archive/refs/tags/v0.2.0.tar.gz
sha256: 2439555bec3f3553a9757a59598cf0422ba26d4e009f931a253240e0bef57c9e
dest: main
- generated-sources.json
# Wrapper to launch the app
- type: script
dest-filename: muezzin-run
commands:
- zypak-wrapper.sh /app/main/muezzin "$@"
build-commands:
# Install npm dependencies
- npm install --offline
# Build the app; in this example the `dist` script
# in package.json runs electron-builder
- |
. ../flatpak-node/electron-builder-arch-args.sh
jq '.build.linux.target="dir"' <<<$(<package.json) > package.json
npm run dist -- $ELECTRON_BUILDER_ARCH_ARGS --linux --dir
# Bundle app and dependencies
- cp -a dist/linux*unpacked /app/main
# Install app wrapper
- install -Dm755 -t /app/bin/ ../muezzin-run
- name: platform-bootstrap
buildsystem: simple
build-commands:
- |
install -Dm644 io.github.dbchoco.muezzin.png /app/share/icons/hicolor/128x128/apps/$FLATPAK_ID.png
install -Dm644 io.github.dbchoco.muezzin.desktop /app/share/applications/${FLATPAK_ID}.desktop
install -Dm644 io.github.dbchoco.muezzin.metainfo.xml /app/share/metainfo/$FLATPAK_ID.appdata.xml
sources:
- type: file
path: io.github.dbchoco.muezzin.metainfo.xml
- type: file
path: io.github.dbchoco.muezzin.png
- type: file
path: io.github.dbchoco.muezzin.desktop

14231
package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

110
package.json Normal file
View File

@@ -0,0 +1,110 @@
{
"name": "muezzin",
"version": "0.2.0",
"description": "Prayer Time Caller",
"main": "src/main/main.js",
"scripts": {
"start": "electron-forge start",
"package": "electron-forge package",
"make": "electron-forge make",
"dist": "electron-builder",
"build": "electron-builder build --linux --win --publish never",
"deploy": "electron-builder build --linux --win --publish always"
},
"icon": "ressources/images/icon.png",
"repository": {
"type": "git",
"url": "git+https://github.com/DBChoco/Muezzin.git"
},
"keywords": [],
"author": "DBChoco <smuky2k@gmail.com>",
"license": "CC0-1.0",
"devDependencies": {
"@electron-forge/cli": "^6.0.0-beta.63",
"@electron-forge/maker-deb": "^6.0.0-beta.63",
"@electron-forge/maker-rpm": "^6.0.0-beta.63",
"@electron-forge/maker-squirrel": "^6.0.0-beta.63",
"@electron-forge/maker-zip": "^6.0.0-beta.63",
"electron": "^17.0.0",
"electron-builder": "^22.14.13"
},
"dependencies": {
"adhan": "^4.3.1",
"auto-launch": "^5.0.5",
"bootstrap": "^5.1.3",
"bootstrap-dark-5": "^1.1.3",
"electron-store": "^8.0.1",
"ip-geolocation-api-javascript-sdk": "^1.0.7",
"weather.js": "^0.1.0"
},
"build": {
"appId": "com.muezzin.app",
"publish": [
{
"provider": "github",
"owner": "DBChoco",
"repo": "Muezzin"
}
],
"win": {
"icon": "ressources/images/icon.ico",
"target": "nsis"
},
"linux": {
"icon": "ressources/images/icon.png",
"category": "Utility",
"target": [
"AppImage",
"snap",
"tar.gz",
"pacman",
"deb"
]
},
"mac": {
"icon": "ressources/images/icon.png",
"category": "public.app-category.utilities",
"target": [
"dmg"
]
},
"appx": {
"applicationId": "Muezzin",
"backgroundColor": "#382bcc",
"publisher": "DBChoco",
"languages": [
"EN",
"FR",
"ES",
"AR",
"DE",
"IT",
"NB",
"SV",
"DA",
"NL",
"UR"
]
},
"snap": {
"confinement": "strict",
"summary": "Islamic prayer times application",
"grade": "stable"
},
"linux": {
"icon": "ressources/images/logo.png",
"category": "Utility",
"target": ["AppImage", "snap", "tar.gz", "pacman"]
},
"mac": {
"icon": "ressources/images/logo.png",
"category": "public.app-category.utilities",
"target": ["dmg"]
}
},
"bugs": {
"url": "https://github.com/DBChoco/Muezzin/issues"
},
"homepage": "https://github.com/DBChoco/Muezzin#readme"
}

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
ressources/audio/dua.mp3 Normal file

Binary file not shown.

File diff suppressed because one or more lines are too long

Binary file not shown.

Binary file not shown.

View File

@@ -0,0 +1,14 @@
/*
Source: https://fonts.qurancomplex.gov.sa/ & https://cdn.jsdelivr.net/gh/fawazahmed0/quran-api@1/fonts.json
*/
@font-face {
font-family: arabic-symbols;
src: url(Basmalah\ Ver01.otf);
}
@font-face {
font-family: arabic;
src: url(hafs-uthmanic-v14-full-org.ttf);
}

Binary file not shown.

File diff suppressed because it is too large Load Diff

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 808 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 296 KiB

BIN
ressources/images/icon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 133 KiB

BIN
ressources/images/icon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 138 KiB

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

1436
ressources/timezones.json Normal file

File diff suppressed because it is too large Load Diff

BIN
screenshots/darkMode.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 MiB

BIN
screenshots/lightMode.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 MiB

BIN
screenshots/quranDark.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 108 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 79 KiB

14
snapcraft.yaml Normal file
View File

@@ -0,0 +1,14 @@
name: muezzin # you probably want to 'snapcraft register <name>'
base: core20 # the base snap is the execution environment for this snap
version: '0.1.1' # just for humans, typically '1.2+git' or '1.3.2'
summary: Highly customizable prayer times application # 79 char long summary
description: |
Highly customizable **prayer times** application with custom background and call to prayers (Adhan / Azan). **Multi language** support with many more coming in the future insha'Allah.
grade: stable # must be 'stable' to release into candidate/stable channels
confinement: strict # use 'strict' once you have the right plugs and slots
parts:
my-part:
# See 'snapcraft plugins'
plugin: nil

113
src/common/allStrings Normal file
View File

@@ -0,0 +1,113 @@
Language
Time format
24 Hour
12 Hour
Show seconds
Date format
DD/MM/YYYY
MM/DD/YYYY
YYYY/MM/DD
Notifications
Enable notifications
Coordinates
Latitude
Longitude
Time zone
Enable Adhan
Adhan Mecca
Adhan al-Aqsa
Custom Adhan
Du'a after Adhan
Theme
Dark Mode
Background Image
Enable Background Image
Calculation Methods
Muslim World League
Egyptian
Karachi
Umm al-Qura
Dubai
Qatar
Kuwait
Moonsighting Committee
Singapore
Turkey
Tehran
ISNA (America)
Madhab
Shafi
Hanafi
High Latitude Rule
Middle of the Night
Seventh of the Night
Twilight Angle
Polar Circle Resolution
Closest City
Closest Date
Do not calculate
Shafaq
General
Red Twilight (ahmer)
White Twilight (abyad)
Return
General
Location
Audio
Appearance
Advanced
Muezzin
Auto Start
Start at launch
Copyright 2022, Muezzin, All rights reserved.
Indeed, prayer has been decreed upon the believers a decree of specified times
Qur'an: 4/103
Fajr
Sunrise
Dhuhr
Asr
Maghrib
Isha
Now
Start Up Sound
Play sound on startup
System tray
Minimize to tray
Custom settings
Enable Custom Calculation Settings
Fajr Angle
Maghrib Angle
Isha Angle
Delay after Maghrib
Delay (minutes)
France
Russia
Gulf Region
Preferences
Reset settings
Adjustments
Enable adjustments
Fajr Adjustments
Dhurh Adjustments
Asr Adjustments
Maghrib Adjustments
Isha Adjustments
Show Sunnah times
Middle of the night
Third of the night
Start minimized
Update available
Version
is available for download on GitHub
Download
Later
Qur'an
Font
Font size
Translation
Show translation
Different language from application
Transliteration
Show transliteration

90
src/common/common.js Normal file
View File

@@ -0,0 +1,90 @@
module.exports = {
foo: function () {
// whatever
},
toggleDarkMode: function toggleDarkMode(darkMode, mainCSS){
var head = document.getElementsByTagName('HEAD')[0];
var link = document.createElement('link');
link.rel = 'stylesheet';
link.type = 'text/css';
var css = document.createElement('link');
css.type = 'text/css';
css.href = mainCSS
var shaders = document.getElementsByClassName("shader")
if (darkMode){
link.href = '../../node_modules/bootstrap-dark-5/dist/css/bootstrap-dark.min.css';
changeButtons(darkMode)
changeNavLink(darkMode)
document.body.style.borderColor = "#FFFFFF";
//document.body.style.backgroundColor = "#212121";
for (let element of shaders){
element.style.backgroundColor = 'rgba(0, 0, 0, 0.5)'
}
}
else{
link.href = '../../node_modules/bootstrap-dark-5/dist/css/bootstrap.min.css';
changeButtons(darkMode)
changeNavLink(darkMode)
document.body.style.borderColor = "#000000";
//document.body.style.backgroundColor = "#FFFFFF";
for (let element of shaders){
element.style.backgroundColor = 'rgba(255, 255, 255, 0.5)'
}
}
css.rel = 'stylesheet';
head.appendChild(link);
head.appendChild(css);
function changeButtons(darkMode){
if (darkMode){
var buttons = document.getElementsByClassName("btn")
for (let element of buttons){
element.classList.add("btn-light")
if (element.classList.contains("btn-dark")){
element.classList.remove("btn-dark")
}
}
}
else{
var buttons = document.getElementsByClassName("btn")
for (let element of buttons){
element.classList.add("btn-dark")
if (element.classList.contains("btn-light")){
element.classList.remove("btn-light")
}
}
}
}
function changeNavLink(darkMode){
var buttons = document.getElementsByClassName("nav-pills")
var buttons2 = document.getElementsByClassName("nav-link")
if (darkMode){
for (let element of buttons){
element.classList.add("dark-pills")
element.classList.remove("light-pills")
}
for (let element of buttons2){
element.classList.add("dark-link")
element.classList.remove("light-link")
}
}
else{
for (element of buttons){
element.classList.add("light-pills")
element.classList.remove("dark-pills")
}
for (element of buttons2){
element.classList.add("light-link")
element.classList.remove("dark-link")
}
}
}
}
};

1402
src/common/language.js Normal file

File diff suppressed because it is too large Load Diff

124
src/main/index.html Normal file
View File

@@ -0,0 +1,124 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<!-- https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP -->
<meta http-equiv="Content-Security-Policy" content="script-src 'self'; style-src 'self' 'unsafe-inline'">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link href="../../node_modules/bootstrap-dark-5/dist/css/bootstrap-night.min.css" rel="stylesheet" >
<link href="../../ressources/fonts/FontAwesome/all.min.css" rel="stylesheet" >
<link href="../../ressources/fonts/weather-icons/css/weather-icons.css" rel="stylesheet" >
<script src="../../node_modules/bootstrap/dist/js/bootstrap.min.js"></script>
<link href="styles.css" rel="stylesheet">
<title>Muezzin</title>
</head>
<div class="center" id="loader">
<div>
<div class="spinner-border" style="width: 3rem; height: 3rem;" role="status"></div>
<strong>
<h1>
Muezzin
</h1>
</strong>
</div>
</div>
</div>
<div class="modal fade" id="updateModal" tabindex="-1" aria-labelledby="updateModalLabel" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="updateModalLabel">Update</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close" id="modalClose"></button>
</div>
<div class="modal-body" id="modalBody">
X
</div>
<div class="modal-footer">
<button type="button" class="btn btn-primary" id="modalButton1">Download</button>
<button type="button" class="btn btn-secondary" id="modalButton2">Later</button>
</div>
</div>
</div>
</div>
<header>
<h1 id="title">Muezzin</h1>
<div id="weatherContainer">
<div id="weatherTemp"> </div>
</div>
</header>
<body class="d-flex flex-column min-vh-100">
<div id="mainContainer">
<div id="main" class="d-flex flex-column">
<h1 class="shader" id="clock">00:00</h1>
<h3 class="shader" id="dateLoc">Date today - Location</h3>
<h1 class="shader" id="timeLeft">Time until XYZ prayer: 00:00</h1>
</div>
</div>
<div id="boxContainer">
<div class="box" id="box">
<div class="grid-container shader" id="prayerGrid">
<div class="grid-item left grid-top" id="fajr"> Fajr </div>
<div class="grid-item right grid-top" id="fajrTime"> 00:00 </div>
<div class="grid-item left" id="sunrise"> Sunrise </div>
<div class="grid-item right" id="sunriseTime"> 00:00 </div>
<div class="grid-item left" id="dhuhr"> Duhr </div>
<div class="grid-item right" id="dhuhrTime"> 00:00 </div>
<div class="grid-item left" id="asr"> Asr </div>
<div class="grid-item right" id="asrTime"> 00:00 </div>
<div class="grid-item left" id="maghrib"> Maghrib </div>
<div class="grid-item right" id="maghribTime"> 00:00 </div>
<div class="grid-item left grid-bottom" id="isha"> Isha </div>
<div class="grid-item right grid-bottom" id="ishaTime"> 00:00 </div>
<div class="grid-item left motn" id="motn"> Middle of the night </div>
<div class="grid-item right motn" id="motnTime"> 00:00 </div>
<div class="grid-item left totn" id="totn"> Third of the night </div>
<div class="grid-item right totn" id="totnTime"> 00:00 </div>
</div>
<div class="box-bottom">
<input data-provide="datepicker" type="date" id="calendar" name="calendar">
</div>
</div>
</div>
<!-- You can also require other files to run in this process -->
<script src="renderer.js"></script>
</body>
<footer class="mt-auto" >
<div id="footer" >
<div id="upperPart">
<div id="audioControls">
<button type="button" id="stopB" class="btn btn-dark"><i class="fa fa-stop"></i></button>
<button type="button" id="playB" class="btn btn-dark"><i class="fa fa-play"></i></button>
<div id="volume">
<i class="fa fa-volume-down"></i>
<input type="range" class="form-range" id="volSlider">
<i class="fa fa-volume-up"></i>
</div>
</div>
<div id="buttons">
<button href="../quran/quran.html" id="quranButton" class="btn btn-dark">
<i class="fa-solid fa-book-quran"></i>
Qur'an
</button>
<button href="../settings/settings.html" id="settingsWheel" class="btn btn-dark">
Settings
</button>
</div>
</div>
</div>
</footer>
</html>

811
src/main/main.js Normal file
View File

@@ -0,0 +1,811 @@
// Modules to control application life and create native browser window
const {app, BrowserWindow, ipcMain, Tray, Menu, Notification, nativeImage, net, shell} = require('electron')
const path = require('path')
const Store = require('electron-store');
const store = new Store();
const adhan = require('adhan')
const language = require('../common/language.js')
const AutoLaunch = require('auto-launch');
var autoLauncher = new AutoLaunch({
name: "Muezzin",
mac:{
useLaunchAgent: true
}
});
let tray = null
let mainWindow, mediaWindow;
function createWindow () {
// Create the browser window.
mainWindow = new BrowserWindow({
width: 800,
height: 600,
minWidth: 600,
minHeight: 600,
backgroundColor:"#212121",
icon: '../../ressources/images/icon.png',
webPreferences: {
nodeIntegration: false, // is default value after Electron v5
contextIsolation: true, // protect against prototype pollution
enableRemoteModule: false, // turn off remote
preload: path.join(__dirname, 'preload.js'),
webviewTag: true
}
})
//DEBUG
//mainWindow.webContents.openDevTools()
const isMac = process.platform === 'darwin'
const template = [
// { role: 'appMenu' }
...(isMac ? [{
label: app.name,
submenu: [
{ role: 'about' },
{ type: 'separator' },
{ role: 'services' },
{ type: 'separator' },
{ role: 'hide' },
{ role: 'hideOthers' },
{ role: 'unhide' },
{ type: 'separator' },
{ role: 'quit' }
]
}] : []),
// { role: 'fileMenu' }
{
label: 'File',
submenu: [
{
label: 'Reset settings',
click: function(){
store.clear();
app.relaunch()
app.exit()
}
},
isMac ? { role: 'close' } : { role: 'quit' }
]
},
// { role: 'viewMenu' }
{
label: 'View',
submenu: [
{ role: 'resetZoom' },
{ role: 'zoomIn' },
{ role: 'zoomOut' },
{ type: 'separator' },
{ role: 'togglefullscreen' }
]
},
// { role: 'windowMenu' }
{
label: 'Window',
submenu: [
{ role: 'minimize' },
{ role: 'zoom' },
...(isMac ? [
{ type: 'separator' },
{ role: 'front' },
{ type: 'separator' },
{ role: 'window' }
] : [
{ role: 'close' }
])
]
},
{
label: 'Debug',
submenu: [
{ role: 'reload' },
{ role: 'forceReload' },
{ type: 'separator' },
{ role: 'toggleDevTools' }
]
},
{
role: 'help',
submenu: [
{
label: 'Website',
click: async () => {
await shell.openExternal('https://github.com/DBChoco/Muezzin')
}
},
{
label: 'Reddit',
click: async () => {
await shell.openExternal('https://www.reddit.com/r/Muezzin/')
}
},
{
label: 'Discord',
click: async () => {
await shell.openExternal('https://discord.gg/cpF9TTstN5')
}
},
{ type: 'separator' },
{
label: 'Report an issue',
click: async () => {
await shell.openExternal('https://github.com/DBChoco/Muezzin/issues/new')
}
}
]
}
]
const menu = Menu.buildFromTemplate(template)
Menu.setApplicationMenu(menu)
mainWindow.setMenuBarVisibility(false)
mainWindow.setAutoHideMenuBar(true)
// and load the index.html of the app.
mainWindow.loadFile('src/main/index.html')
mainWindow.maximize();
mainWindow.on('minimize',function(event){
event.preventDefault();
mainWindow.hide();
});
mainWindow.on('close', function (event) {
if(!app.isQuiting && settings.systray){
event.preventDefault();
mainWindow.hide();
return false;
}
else{
app.isQuiting = true;
app.quit();
return true;
}
});
//Create hidden mediaPlayer
mediaWindow = new BrowserWindow({
show: false,
webPreferences: {
nodeIntegration: false, // is default value after Electron v5
contextIsolation: true, // protect against prototype pollution
enableRemoteModule: false, // turn off remote
preload: path.join(__dirname, 'preload.js'),
}
});
mediaWindow.loadFile('src/mediaPlayer/mediaPlayer.html');
}
app.on('before-quit', function () {
app.isQuiting = true;
});
// This method will be called when Electron has finished
// initialization and is ready to create browser windows.
// Some APIs can only be used after this event occurs.
app.whenReady().then(() => {
createWindow()
app.on('activate', function () {
// On macOS it's common to re-create a window in the app when the
// dock icon is clicked and there are no other windows open.
if (BrowserWindow.getAllWindows().length === 0) createWindow()
})
let iconPath;
if (process.platform == "win32"){
iconPath = path.join(__dirname, '../../ressources/images/icon.ico');
} else{
iconPath = path.join(__dirname, '../../ressources/images/icon.png');
}
if (process.platform == "darwin"){
tray = new Tray(nativeImage.createFromPath(iconPath).resize({ width: 16, height: 16 }))
}
else{
tray = new Tray(nativeImage.createFromPath(iconPath))
}
const contextMenu = Menu.buildFromTemplate([
{ label: 'Open', click: function(){
mainWindow.show(); }},
{ label: 'Quit', click: function(){
app.isQuiting = true;
app.quit();}}
])
tray.setToolTip('Muezzin')
tray.setContextMenu(contextMenu)
tray.addListener("double-click", function(){
mainWindow.show();
})
checkFirstTime()
loadSettings();
setUpHandlers();
checkTime();
setUpdates()
})
var lat, lon, calculationMethod, prayerTimes, adhanPath;
var customValues, delay;
var prayerTimes, datePrayerTimes, tomorrowPrayers;
var settings
var langFajr, langDhuhr, langAsr, langMaghrib, langIsha, langNow;
var adjustements;
var updateInterval;
var startup = true; // To check if the app just started or not when going back to the main page.
var today = new Date
// Quit when all windows are closed, except on macOS. There, it's common
// for applications and their menu bar to stay active until the user quits
// explicitly with Cmd + Q.
app.on('window-all-closed', function () {
if (process.platform !== 'darwin' && settings.systray) app.quit()
})
function setUpdates(){
setTimeout(function(){
getVersion()
}, 3000);
updateInterval = setInterval(function(){
getVersion()
}, 600000) //10 minutes
}
function getVersion(){
console.log("Looking for updates")
const request = net.request({
method: 'GET',
protocol: 'http:',
hostname: 'api.github.com',
path: '/repositories/459335904/releases/latest',
redirect: 'follow'
});
request.on('response', (response) => {
//console.log(`STATUS: ${response.statusCode}`);
//console.log(`HEADERS: ${JSON.stringify(response.headers)}`);
response.on('data', (chunk) => {
if (app.getVersion() < JSON.parse(chunk).name){
mainWindow.webContents.send('update-available', [app.getVersion(), JSON.parse(chunk).name]);
clearInterval(updateInterval);
}
});
});
request.on('finish', () => {
//console.log('Request is Finished')
});
request.on('abort', () => {
console.log('Request is Aborted')
});
request.on('error', (error) => {
console.log(`ERROR: ${JSON.stringify(error)}`)
});
request.on('close', (error) => {
//console.log('Last Transaction has occured')
});
request.setHeader('Content-Type', 'application/json');
request.end();
}
function showNotification (message) {
if (Notification.isSupported()){
const NOTIFICATION_TITLE = 'Muezzin'
const NOTIFICATION_BODY = message
new Notification({ title: NOTIFICATION_TITLE, body: NOTIFICATION_BODY, icon:"../ressources/images/icon.png" }).show()
}
}
/**
* Checks for Athan time and current time, then if they are the same, it plays the Athan
*/
function checkTime(){
var interval = setInterval(function(){
var prayers = nextPrayer();
if (prayers != undefined && prayers[0] != undefined && (settings.adhanCheck || settings.notifCheck)){
var timeUntilCurrentPrayer = timeUntilPrayer(prayers[0])
//console.log(timeUntilCurrentPrayer)
if(timeUntilCurrentPrayer[0] == -1 && timeUntilCurrentPrayer[1] == -1 && timeUntilCurrentPrayer[2] == 0){ //-1 because math.floor
//if(timeUntilCurrentPrayer[0] == -1 && timeUntilCurrentPrayer[1] == -26 && timeUntilCurrentPrayer[2] == 0){ //TESTING
if (settings.adhanCheck){
mediaWindow.webContents.send('play', adhanPath);
}
if (settings.notifCheck){
showNotification(langNow + ": " + prayers[2])
}
}
}
}, 1000)
setInterval(function(){
if (today.getDate != (new Date).getDate){
mainWindow.webContents.send('update');
}
}, 900000)
}
/**
* Loads all required variables from the store
*/
async function loadSettings(){
lat = await store.get('latitude', 0.00);
lon = await store.get('longitude', 0.00);
timezone = await store.get('timezone', 'Europe/Brussels');
calculationMethod = await store.get("calculationMethod", {
calcMethod: 'MWL',
madhab: 'Shafi',
hlr: 'TA',
pcr: 'CC',
shafaq: 'shafaqG'
})
settings = await store.get("settings", {
startupSound: false,
notifCheck: true,
systray: true,
adhanCheck: true,
autoStart: true,
minStart: false
})
adhanPath = await store.get('adhanFile', [false, "../../ressources/audio/Adhan - Mecca.mp3", true]);
lang = await store.get('language', 'en');
loadLang();
customValues = await store.get('customSettings', [false, 0,0,0]);
delay = await store.get('delay', [false, 0]);
adjustements = await store.get('adj', [false, 0,0,0,0,0]);
if (settings.minStart && startup){
mainWindow.hide()
}
setAutoStart(settings.autoStart)
}
/**
* Takes all the loaded settings and calculated the prayers times for 'date'
* @param {Date} date
* @return {adhan.PrayerTimes} prayerTimes
*/
function calcPrayerTimes(date = new Date()){
console.log("Calculating prayer times")
var coordinates = new adhan.Coordinates(lat, lon)
// https://github.com/batoulapps/adhan-js/blob/master/METHODS.md
//TO add one, change CalculationMethod.js, Adhan.d.ts and Adhan.js
if (customValues != undefined && !customValues[0]){
switch(calculationMethod.calcMethod) {
case "MWL":
params = adhan.CalculationMethod.MuslimWorldLeague();
break;
case "Egyptian":
params = adhan.CalculationMethod.Egyptian();
break;
case "Karachi":
params = adhan.CalculationMethod.Karachi();
break;
case "UAQ":
params = adhan.CalculationMethod.UmmAlQura();
break;
case "Dubai":
params = adhan.CalculationMethod.Dubai();
break;
case "Qatar":
params = adhan.CalculationMethod.Qatar();
break;
case "Kuwait":
params = adhan.CalculationMethod.Kuwait();
break;
case "MC":
params = adhan.CalculationMethod.MoonsightingCommittee();
break;
case "Singapore":
params = adhan.CalculationMethod.Singapore();
break;
case "Turkey":
params = adhan.CalculationMethod.Turkey();
break;
case "Tehran":
params = adhan.CalculationMethod.Tehran();
break;
case "ISNA":
params = adhan.CalculationMethod.NorthAmerica();
break;
case "France12":
params = adhan.CalculationMethod.France12();
break;
case "France15":
params = adhan.CalculationMethod.France15();
break;
case "France18":
params = adhan.CalculationMethod.France18();
break;
case "Russia":
params = adhan.CalculationMethod.Russia();
break;
case "Gulf":
params = adhan.CalculationMethod.Gulf();
break;
default:
params = adhan.CalculationMethod.MuslimWorldLeague();
}
}
else{
params = adhan.CalculationMethod.Other();
params.fajrAngle = customValues[1]
params.maghribAngle = customValues[2]
params.ishaAngle = customValues[3]
}
if (delay[0]){
params.ishaInterval = delay[1]
}
switch(calculationMethod.madhab){
case "Shafi":
params.madhab = adhan.Madhab.Shafi;
break;
case "Hanafi":
params.madhab = adhan.Madhab.Hanafi;
break;
default:
params.madhab = adhan.Madhab.Shafi;
}
switch(calculationMethod.hlr){
case "MOTN":
params.HighLatitudeRule = adhan.HighLatitudeRule.MiddleOfTheNight;
break;
case "SOTN":
params.HighLatitudeRule = adhan.HighLatitudeRule.SeventhOfTheNight;
break;
case "TA":
params.HighLatitudeRule = adhan.HighLatitudeRule.TwilightAngle;
break;
default:
params.HighLatitudeRule = adhan.HighLatitudeRule.recommended(coordinates)
}
switch(calculationMethod.pcr){
case "CC":
params.PolarCircleResolution = adhan.PolarCircleResolution.AqrabBalad;
break;
case "CD":
params.PolarCircleResolution = adhan.PolarCircleResolution.AqrabYaum;
break;
case "UND":
params.PolarCircleResolution = adhan.PolarCircleResolution.Unresolved;
break;
default:
params.PolarCircleResolution = adhan.PolarCircleResolution.AqrabBalad;
}
switch(calculationMethod.shafaq){
case "shafaqG":
params.shafaq = adhan.Shafaq.General;
break;
case "shafaqR":
params.shafaq = adhan.Shafaq.Ahmer;
break;
case "shafaqW":
params.shafaq = adhan.Shafaq.Abyad;
break;
default:
params.shafaq = adhan.Shafaq.General;
}
if(adjustements[0]){
params.adjustments.fajr = adjustements[1]
params.adjustments.dhuhr = adjustements[2]
params.adjustments.asr = adjustements[3]
params.adjustments.maghrib = adjustements[4]
params.adjustments.isha = adjustements[5]
}
let calculatedTimes = new adhan.PrayerTimes(coordinates, date, params);
return calculatedTimes;
}
//Sets up all the IPC handlers for communication with renderers
async function setUpHandlers(){
ipcMain.handle('getStoreValue', (event, key, defaultValue) => {
if (defaultValue != undefined){
return store.get(key, defaultValue);
}
else{
return store.get(key);
}
});
ipcMain.handle('setStoreValue', (event, key, value) => {
return store.set(key, value);
});
ipcMain.handle('prayers', (event, message) => {
console.log("Requesting prayer times for today")
waitFor(lat, prayerTimes = calcPrayerTimes())
event.sender.send('prayersReply', prayerTimes)
});
ipcMain.handle('date-request', (event, message) => {
console.log("Requesting prayer times for " + message)
waitFor(lat, datePrayerTimes = calcPrayerTimes(new Date(message)));
event.sender.send('date-reply', datePrayerTimes)
});
ipcMain.handle('tomorrow-request', (event, message) => {
console.log("Requesting prayer times for " + message.toDateString())
waitFor(lat, tomorrowPrayers = calcPrayerTimes(message))
event.sender.send('tomorrow-reply', tomorrowPrayers)
});
//Media Handlers
ipcMain.handle('play', (event, message) => {
mediaWindow.webContents.send('play', adhanPath);
});
ipcMain.handle('stop', (event, message) => {
mediaWindow.webContents.send('stop', {});
});
ipcMain.handle('volume-request', (event, message) => {
mediaWindow.webContents.send('volume-reply', message);
});
ipcMain.handle('progress-request', (event, message) => {
mainWindow.webContents.send('progress-reply', message);
});
ipcMain.handle("settingsO", function(){
})
ipcMain.handle("settingsC", function(){
startup = false;
loadSettings()
//calcPrayerTimes()
})
ipcMain.handle("startup-request", function(){
mediaWindow.webContents.send('startup-reply', settings.startupSound);
})
}
function calcTomorrowPrayers(){
const today = new Date();
const tomorrow = new Date();
tomorrow.setDate(today.getDate() + 1);
tomorrowPrayers = calcPrayerTimes(tomorrow)
}
//Waits for someting to be defined before doing some other thing
async function waitFor(something, doSomething){
var valueChecker = setInterval(function(){
if (something != undefined){
clearInterval(valueChecker)
doSomething;
return true;
}
}, 100)
}
/**
* Returns the current and next prayers with their names
* @return {[adhan.PrayerTimes.prayer & string]} [currentPrayer, nextPrayer, currentName, nextName]
*/
function nextPrayer(){
var now = new Date();
var currentPrayer, nextPrayer, currentName, nextName;
if (prayerTimes != undefined){
if (now >= prayerTimes.isha){
if (tomorrowPrayers == undefined){
calcTomorrowPrayers();
}
currentPrayer = prayerTimes.isha;
nextPrayer = tomorrowPrayers.fajr
currentName = langIsha
nextName = langFajr
}
else if (now >= prayerTimes.maghrib){
currentPrayer = prayerTimes.maghrib;
nextPrayer = prayerTimes.isha;
currentName = langMaghrib
nextName = langIsha
}
else if (now >= prayerTimes.asr){
currentPrayer = prayerTimes.asr;
nextPrayer = prayerTimes.maghrib;
currentName = langAsr
nextName = langMaghrib
}
else if (now >= prayerTimes.dhuhr){
currentPrayer = prayerTimes.dhuhr;
nextPrayer = prayerTimes.asr;
currentName = langDhuhr
nextName = langAsr
}
else if (now >= prayerTimes.fajr){
currentPrayer = prayerTimes.fajr;
nextPrayer = prayerTimes.dhuhr;
currentName = langFajr
nextName = langDhuhr
}
else{
currentPrayer = prayerTimes.fajr;
nextPrayer = prayerTimes.fajr;
currentName = langFajr
nextName = langFajr
}
}
return [currentPrayer, nextPrayer, currentName, nextName]
}
/**
* If the prayer is defined, returns the [hours, minutes, seconds] before/after the prayer
* @param {adhan.PrayerTimes} prayer
* @return {Numer[]} time
*/
function timeUntilPrayer(prayer) {
var now = new Date()
if (prayer != undefined){
return msToTime(prayer - now);
}
else{
return null;
}
}
/**
* Converts miliseconds to [hours, minutes, seconds]
* @param {Number} duration
* @return {Numer[]} res
*/
function msToTime(duration){ //https://stackoverflow.com/questions/19700283/how-to-convert-time-in-milliseconds-to-hours-min-sec-format-in-javascript
var seconds = Math.floor((duration / 1000) % 60) + 1,
minutes = Math.floor((duration / (1000 * 60)) % 60),
hours = Math.floor((duration / (1000 * 60 * 60)) % 24);
var res = [hours, minutes, seconds]
return res;
}
//checks if the app is being launched for the first time and if so, get location from location api
function checkFirstTime(){
var first = !store.has("first")
if (first){
var IPGeolocationAPI = require('ip-geolocation-api-javascript-sdk');
// Create IPGeolocationAPI object. Constructor takes two parameters.
var ipgeolocationApi = new IPGeolocationAPI("b9aed80a71d043149013540fb449a384", false);
//var GeolocationParams = require('ip-geolocation-api-javascript-sdk/GeolocationParams.js');
// Get complete geolocation for the calling machine's IP address
ipgeolocationApi.getGeolocation(handleResponse);
// Function to handle response from IP Geolocation API
function handleResponse(json) {
loadDefaultCalcMethod(json["continent_code"], json["country_code2"])
store.set('latitude', json["latitude"])
store.set('longitude', json["longitude"])
store.set('timezone', json["time_zone"]["name"])
//loadSettings()
store.set('first', false)
loadSettings()
mainWindow.webContents.send('update');
}
}
//Looks at the continent and country of the user and chooses a calculation method
function loadDefaultCalcMethod(continentCode, countryCode){
if (countryCode == "RU"){
store.set('calculationMethod.calcMethod', "Russia");
}
else if (countryCode == "GB"){
store.set('calculationMethod.calcMethod', "ISNA");
}
if (countryCode == "SG"){
store.set('calculationMethod.calcMethod', "Singapore");
}
else if (countryCode == "QA"){
store.set('calculationMethod.calcMethod', "Qatar");
}
else if (countryCode == "TR"){
store.set('calculationMethod.calcMethod', "Turkey");
}
else if (countryCode == "IR"){
store.set('calculationMethod.calcMethod', "Tehran");
}
else if (countryCode == "KW"){
store.set('calculationMethod.calcMethod', "Kuwait");
}
else if (countryCode == "AE"){
store.set('calculationMethod.calcMethod', "Dubai");
}
else if (countryCode == "PK"){
store.set('calculationMethod.calcMethod', "Karachi");
}
else if (countryCode == "EG"){
store.set('calculationMethod.calcMethod', "Egyptian");
}
else if (countryCode == "SA"){
store.set('calculationMethod.calcMethod', "UAQ");
}
else{
switch (continentCode){
case "NA": //N America
store.set('calculationMethod.calcMethod', "ISNA");
break;
case "AF": //Africa
break;
case "EU": //EuropeD
store.set('calculationMethod.calcMethod', "MWL");
break;
case "AS": //Asia
store.set('calculationMethod.calcMethod', "ISNA");
break;
case "SA": //S America
store.set('calculationMethod.calcMethod', "MWL");
break;
case "OC": //Oceania
store.set('calculationMethod.calcMethod', "MWL");
break;
case "AN": //Antartica
store.set('calculationMethod.calcMethod', "MC");
break;
default:
store.set('calculationMethod.calcMethod', "MWL");
}
}
}
}
function loadLang(){
langFajr = language.loadTrans(lang, 'fajr')
langSunrise = language.loadTrans(lang, 'sunrise')
langDhuhr = language.loadTrans(lang, 'dhuhr')
langAsr= language.loadTrans(lang, 'asr')
langMaghrib = language.loadTrans(lang, 'maghrib')
langIsha = language.loadTrans(lang, 'isha')
langAdhan = language.loadTrans(lang, 'adhan')
langNow = language.loadTrans(lang, 'now')
}
function setAutoStart(autoStart){
if(autoStart){
// Checking if autoLaunch is enabled, if not then enabling it.
autoLauncher.isEnabled().then(function(isEnabled) {
if (isEnabled) return;
autoLauncher.enable();
}).catch(function (err) {
throw err;
});
}
else{
if (autoLauncher.isEnabled()){
try {
autoLauncher.disable();
} catch (error) {
throw error
}
}
}
}

22
src/main/preload.js Normal file
View File

@@ -0,0 +1,22 @@
// All of the Node.js APIs are available in the preload process.
// It has the same sandbox as a Chrome extension.
const { contextBridge, ipcRenderer} = require('electron')
const adhan = require('adhan')
const settings = require('../common/common.js')
const language = require('../common/language.js')
const shell = require('electron').shell
contextBridge.exposeInMainWorld( 'api', {
send: ( channel, data ) => ipcRenderer.invoke( channel, data ),
handle: ( channel, func) => ipcRenderer.on( channel, (event, ...args) => func(...args) ),
coordinates: (lat, lon) => adhan.Coordinates(lat, lon),
getFromStore: (key, def) => ipcRenderer.invoke('getStoreValue', key, def),
setToStore: (key, value) => ipcRenderer.invoke('setStoreValue', key , value),
setTheme: (darkmode, css) => settings.toggleDarkMode(darkmode, css),
getLanguage: (lang, value) => language.loadTrans(lang, value),
getSunnah: (prayerTimes) => new adhan.SunnahTimes(prayerTimes),
getPrayerTimes: (coordinates, date, parameters) => new adhan.PrayerTimes(coordinates, date, parameters),
openExternal: (link) => shell.openExternal(link)
})

699
src/main/renderer.js Normal file
View File

@@ -0,0 +1,699 @@
// This file is required by the index.html file and will
// be executed in the renderer process for that window.
// No Node.js APIs are available in this process because
// `nodeIntegration` is turned off. Use `preload.js` to
// selectively enable features needed in the rendering
// process.
var lat = 0;
var lon = 0;
var timezone, timeFormat, shortTimeFormat, clockDisplay, lang;
var prayerTimes, calPrayers, tommorowPrayers, sunnahTimes;
var datePick, volume;
var loadedUI = false;
var langFajr, langSunrise, langDhuhr, langMaghrib, langIsha, langAdhan, langNow, langTimeUntil;
var sunnahTimes, motnCheckOG, totnCheckOG, totn, motn;
var athanPlaying = false;
var weatherSettings;
var loaded = true;
var event1 = new Event('loadedSettings')
var event2 = new Event('loadedUI')
loadHandles()
loadSettings()
window.api.send('prayers');
window.addEventListener('loadedSettings', () => {
datePick = document.getElementById('calendar');
getTomorrowPrayers()
loadClock();
loadHijriDate();
loadCalendar()
loadPrayers()
volumeSlider()
loadBackgroundImage()
setKeyPress()
setupButtonListeners()
setupUpdateModal()
setupWeather()
const interval = setInterval(function() {
loadClock()
loadNextPrayer()
}, 1000)
})
window.addEventListener('loadedUI', () => {
loadedUI = true;
hideLoader()
})
function loadClock(){
document.getElementById("clock").innerText = changeclockDisplay(new Date, timeFormat)
}
/**
* Changes the format of the Date to a string (hours)
* @param {Date} date
* @param {String} timeformat
*/
function changeclockDisplay(date, timeformat){
if (timeformat[0] == 'H'){
if (timeformat[6] == 's'){
return show0(date.getHours()) + ":" + show0(date.getMinutes()) + ":" + show0(date.getSeconds())
}
return show0(date.getHours()) + ":" + show0(date.getMinutes())
}
else{
let amPm = date.getHours() < 12 ? 'am' : 'pm'
if (amPm == 'am'){
if (timeformat[6] == 's'){
return show0(date.getHours()) + ":" + show0(date.getMinutes()) + ":" + show0(date.getSeconds()) + ' AM'
}
return show0(date.getHours()) + ":" + show0(date.getMinutes()) + ' AM'
}
else{
if (timeformat[6] == 's'){
return show0(date.getHours() - 12) + ":" + show0(date.getMinutes()) + ":" + show0(date.getSeconds()) + ' PM'
}
return show0(date.getHours() - 12) + ":" + show0(date.getMinutes()) + ' PM'
}
}
/**
* @param {Int} number
* @returns A string of the 0 + number if it is < than 12
*/
function show0(number){
let res
number < 10 ? res = "0" + number.toString() : res = number.toString()
return res
}
}
function loadHijriDate(){
var hijri = true;
document.getElementById("dateLoc").innerText = (new Date).toLocaleDateString(lang,
{ weekday: 'long', year: 'numeric', month: 'long', day: 'numeric' }).capitalize()
hijri = true;
setInterval(function() {
if (hijri){
document.getElementById("dateLoc").innerText = new Intl.DateTimeFormat(lang + '-TN-u-ca-islamic',
{day: 'numeric', month: 'long',weekday: 'long',year : 'numeric'}).format(Date.now()).capitalize();
hijri = false;
}
else{
if (lang == "ur"){
document.getElementById("dateLoc").innerText = ((new Date).toLocaleDateString("ar",
{ weekday: 'long', year: 'numeric', month: 'long', day: 'numeric' })).capitalize()
}
else{
document.getElementById("dateLoc").innerText = ((new Date).toLocaleDateString(lang,
{ weekday: 'long', year: 'numeric', month: 'long', day: 'numeric' })).capitalize()
}
hijri = true;
};
}, 5000)
}
Object.defineProperty(String.prototype, 'capitalize', {
value: function() {
var upperCaseString = ''
for (let i = 0; i < this.length; i ++){
if (this.charAt(i) == ' ' && i != this.length-1){
upperCaseString += ' ' + this.charAt(i+1).toUpperCase();
i++
}
else{
upperCaseString += this.charAt(i)
}
}
res = upperCaseString.charAt(0).toUpperCase() + upperCaseString.slice(1);
return res
},
enumerable: false
});
//Picks the date from the calendar and adds a listener to the calendar, when the dates changes it sends a reuquest for time prayers.
function loadCalendar(){
Date.prototype.toDateInputValue = (function() {
var local = new Date(this);
local.setMinutes(this.getMinutes() - this.getTimezoneOffset());
return local.toJSON().slice(0,10);
});
datePick.value = new Date().toDateInputValue();
//window.api.send('date-request', datePick.value);
datePick.addEventListener('change', function(){
window.api.send('date-request', datePick.value);
})
}
//Load all the prayers of the day and shows them on the screen
function loadPrayers(){
if (datePick.value == new Date().toDateInputValue()){
calPrayers = prayerTimes
}
if (calPrayers != undefined){
document.getElementById("fajrTime").innerText = changeclockDisplay(convertTZ(calPrayers.fajr, timezone), shortTimeFormat);
document.getElementById("sunriseTime").innerText = changeclockDisplay(convertTZ(calPrayers.sunrise, timezone), shortTimeFormat);
document.getElementById("dhuhrTime").innerText = changeclockDisplay(convertTZ(calPrayers.dhuhr, timezone), shortTimeFormat);
document.getElementById("asrTime").innerText = changeclockDisplay(convertTZ(calPrayers.asr, timezone), shortTimeFormat);
document.getElementById("maghribTime").innerText = changeclockDisplay(convertTZ(calPrayers.maghrib, timezone), shortTimeFormat);
document.getElementById("ishaTime").innerText = changeclockDisplay(convertTZ(calPrayers.isha, timezone), shortTimeFormat);
if (sunnahTimes.totn && totn != undefined){
document.getElementById("totnTime").innerText = changeclockDisplay(convertTZ(totn, timezone), shortTimeFormat);
}
if (sunnahTimes.motn && motn != undefined){
document.getElementById("motnTime").innerText = changeclockDisplay(convertTZ(motn, timezone), shortTimeFormat);
}
}
}
function convertTZ(date, tzString) {
return new Date((typeof date === "string" ? new Date(date) : date).toLocaleString("en-US", {timeZone: tzString}));
}
//Checks the store for saved settings, or gets default values
async function loadSettings(){
lat = await window.api.getFromStore('latitude', 0.00);
lon = await window.api.getFromStore('longitude', 0.00);
timezone = await window.api.getFromStore('timezone', 'US/Central');
lang = await window.api.getFromStore('language', "en");
volume = await window.api.getFromStore('volume', 50)
darkmode = await window.api.getFromStore('darkMode', false)
window.api.setTheme(darkmode, "styles.css");
sunnahTimes = await window.api.getFromStore("sunnahTimes", {
motn: false,
totn: false
})
motnCheckOG = sunnahTimes.motn; totnCheckOG = sunnahTimes.totn;
weatherSettings = await window.api.getFromStore('weather', {
enabled: true,
unit: "C"
})
loadLang()
await hidePlayer()
await loadClockDisplay()
window.dispatchEvent(event1)
}
//Calculates time left until #prayer
function timeUntilPrayer(prayer){
var now = new Date();
if (prayer === src_Prayer.Fajr) {
if (now.getHours() < 12){
return this.msToTime(this.fajr - now);
}
else{
now.getHours()
const today = new Date();
const tomorrow = new Date();
tomorrow.setDate(today.getDate() + 1);
let tommorowPrayer = new PrayerTimes_PrayerTimes(this.coordinates, tomorrow, this.calculationParameters)
return this.msToTime(tommorowPrayer.fajr - now)
}
} else if (prayer === src_Prayer.Sunrise) {
return this.msToTime(this.sunrise - now);
} else if (prayer === src_Prayer.Dhuhr) {
return this.msToTime(this.dhuhr - now);
} else if (prayer === src_Prayer.asr) {
return this.msToTime(this.asr - now);
} else if (prayer === src_Prayer.maghrib) {
return this.msToTime(this.maghrib - now);
} else if (prayer === src_Prayer.isha) {
return this.msToTime(this.isha - now);
}
else {
return null;
}
}
//Loads the next prayers text: Prayer X in Y time;
function loadNextPrayer(){
var prayers = nextPrayer();
if (prayers[0] != undefined){
var timeUntilCurrentPrayer = timeUntilPrayer(prayers[0])
if (athanPlaying && timeUntilCurrentPrayer[0] == -1 && timeUntilCurrentPrayer[1] >= -5){
document.getElementById("timeLeft").innerText = langAdhan
}
else if(timeUntilCurrentPrayer[0] == -1 && timeUntilCurrentPrayer[1] >= -10){ //-1 because math.floor
document.getElementById("timeLeft").innerText = langNow + ": " + prayers[2];
}
else{
var time = timeUntilPrayer(prayers[1])
document.getElementById("timeLeft").innerText = langTimeUntil + " " + prayers[3] + ": " + intToHour(time);
}
if (!loadedUI){
window.dispatchEvent(event2)
}
}
}
function nextPrayer(){
var now = new Date();
var currentPrayer, nextPrayer, currentName, nextName;
if (prayerTimes != undefined && langFajr != undefined){
if (now >= prayerTimes.isha){
currentPrayer = prayerTimes.isha;
nextPrayer = tommorowPrayers.fajr
currentName = langIsha
nextName = langFajr
}
else if (now >= prayerTimes.maghrib){
currentPrayer = prayerTimes.maghrib;
nextPrayer = prayerTimes.isha;
currentName = langMaghrib
nextName = langIsha
}
else if (now >= prayerTimes.asr){
currentPrayer = prayerTimes.asr;
nextPrayer = prayerTimes.maghrib;
currentName = langAsr
nextName = langMaghrib
}
else if (now >= prayerTimes.dhuhr){
currentPrayer = prayerTimes.dhuhr;
nextPrayer = prayerTimes.asr;
currentName = langDhuhr
nextName = langAsr
}
else if (now >= prayerTimes.fajr){
currentPrayer = prayerTimes.fajr;
nextPrayer = prayerTimes.dhuhr;
currentName = langFajr
nextName = langDhuhr
}
else{
currentPrayer = prayerTimes.fajr;
nextPrayer = prayerTimes.fajr;
currentName = langFajr
nextName = langFajr
}
return [currentPrayer, nextPrayer, currentName, nextName]
}
}
function timeUntilPrayer(prayer) {
var now = new Date()
if (prayer != undefined){
return msToTime(prayer - now);
}
else{
return null;
}
}
function msToTime(duration){ //https://stackoverflow.com/questions/19700283/how-to-convert-time-in-milliseconds-to-hours-min-sec-format-in-javascript
var seconds = Math.floor((duration / 1000) % 60) + 1,
minutes = Math.floor((duration / (1000 * 60)) % 60),
hours = Math.floor((duration / (1000 * 60 * 60)) % 24);
if (seconds == 60){
if (minutes == 0){
minutes = 1;
}
seconds = 00;
}
var res = [hours, minutes, seconds]
return res;
}
function getTomorrowPrayers(){
const today = new Date();
const tomorrow = new Date();
tomorrow.setDate(today.getDate() + 1);
window.api.send('tomorrow-request', tomorrow);
}
function intToHour(time){
var hours = time[0];
var minutes = time[1];
var seconds = time[2];
hours = (hours < 10) ? "0" + hours : hours;
minutes = (minutes < 10) ? "0" + minutes : minutes;
seconds = (seconds < 10) ? "0" + seconds : seconds;
if (timeFormat[6] != 's' && (hours > 0 || minutes > 0)){
return hours + ":" + minutes
}
else{
return hours + ":" + minutes + ":" + seconds
}
}
function loadHandles(){
window.api.handle('date-reply', msg => {
calPrayers = msg;
if (timezone != undefined){
if (calPrayers.date.getDate() == new Date().getDate() && calPrayers.date.getMonth() == new Date().getMonth() && calPrayers.date.getFullYear() == new Date().getFullYear()){
sunnahTimes.motn = motnCheckOG;
sunnahTimes.totn = totnCheckOG;
}
else{
sunnahTimes.motn = false;
sunnahTimes.totn = false;
}
setupSunnah();
loadPrayers();
}
})
window.api.handle('prayersReply', msg => {
prayerTimes = msg
nextPrayer()
})
window.api.handle('tomorrow-reply', msg => {
tommorowPrayers = msg
setupSunnah()
})
window.api.handle('progress-reply', msg => {
athanPlaying = msg;
})
window.api.handle('update', msg => {
loadSettings()
window.api.send('prayers', "Send me the times please");
})
}
//Loads time format (for now date format does nothing)
async function loadClockDisplay(){
var clockDisplay = await window.api.getFromStore("timeDisplay", {
clockFormat: 12,
dateFormat: 'DD/MM/YYYY',
showSeconds: true
})
timeFormat = "hh:mm"
if (clockDisplay.showSeconds){
timeFormat += ":ss"
}
if (clockDisplay.clockFormat == 12){
timeFormat += " A"
}
else{
timeFormat = timeFormat.replace("hh", 'HH')
}
shortTimeFormat = timeFormat.replace(":ss", "")
}
//Sets event listeners and IPCRenderers to make volume sliders work
function volumeSlider(){
var volSlider = document.getElementById('volSlider');
volSlider.value = volume
volSlider.addEventListener('pointermove', function(){
window.api.send('volume-request', volSlider.value);
})
volSlider.addEventListener('change', function(){
window.api.send('volume-request', volSlider.value);
window.api.setToStore('volume', volSlider.value)
})
}
//Hides the media player in case the Adhan is disabled
async function hidePlayer(){
var enableAdhan = await window.api.getFromStore('adhanCheck', true)
if (!enableAdhan){
document.getElementById('audioControls').style.display = "none";
}
}
/**
* If a bgImage is set in the settings, it gets applied, otherwise it disables the shaders
*/
async function loadBackgroundImage(){
var bgImage = await window.api.getFromStore('bgImage', [true, '../../ressources/images/bgImage.jpg'])
if (bgImage[0]){
document.body.style.backgroundImage = "url('" + bgImage[1] + "')";
}
else{
var shaders = document.getElementsByClassName("shader")
for (let element of shaders){
element.style.backgroundColor = "transparent"
}
document.body.style.backgroundImage = "none";
}
}
/**
*Adds listener that brings you to the settings page when you press "Crtl + O"
*/
function setKeyPress(){
var crtl = false;
document.addEventListener('keydown', function(key){
if (key.ctrlKey){
crtl = true;
}
if ((key.key == "o" || key.key == "O") && crtl){
window.location.assign("../settings/settings.html");
}
})
document.addEventListener('keyup', function(key){
if (key.ctrlKey){
crtl = false;
}
})
}
function hideLoader(){
document.getElementById('loader').style.display = "none"
}
function loadLang(){
langFajr = window.api.getLanguage(lang, 'fajr')
langSunrise = window.api.getLanguage(lang, 'sunrise')
langDhuhr = window.api.getLanguage(lang, 'dhuhr')
langAsr= window.api.getLanguage(lang, 'asr')
langMaghrib = window.api.getLanguage(lang, 'maghrib')
langIsha = window.api.getLanguage(lang, 'isha')
langAdhan = window.api.getLanguage(lang, 'adhan')
langNow = window.api.getLanguage(lang, 'now')
langTimeUntil = window.api.getLanguage(lang, 'timeUntil')
document.getElementById('fajr').innerText = langFajr
document.getElementById('sunrise').innerText = langSunrise
document.getElementById('dhuhr').innerText = langDhuhr
document.getElementById('asr').innerText = langAsr
document.getElementById('maghrib').innerText = langMaghrib
document.getElementById('isha').innerText = langIsha
document.getElementById('settingsWheel').innerHTML = '<i class="fa fa-cog" aria-hidden="true"></i> ' + window.api.getLanguage(lang, 'settings')
document.getElementById('motn').innerText = window.api.getLanguage(lang, 'motn')
document.getElementById('totn').innerText = window.api.getLanguage(lang, 'totn')
document.getElementById('quranButton').innerHTML = '<i class="fa-solid fa-book-quran"></i> ' + window.api.getLanguage(lang, 'quran')
}
function setupButtonListeners(){
document.getElementById('playB').addEventListener("click", function(){
window.api.send("play");
})
document.getElementById('stopB').addEventListener("click", function(){
window.api.send("stop");
})
document.getElementById('settingsWheel').addEventListener("click", function(){
window.api.send("settingsO");
window.location.href = "../settings/settings.html";
})
document.getElementById('quranButton').addEventListener("click", function(){
window.location.href = "../quran/quran.html";
})
}
function setupSunnah(){
if (prayerTimes != undefined && tommorowPrayers != undefined){
if (sunnahTimes.motn || sunnahTimes.totn){
calculateSunnah()
document.getElementById("box").style.marginTop = "0"
if (sunnahTimes.motn && sunnahTimes.totn) document.getElementById("clock").style.marginTop = "1vh";
else document.getElementById("clock").style.marginTop = "3vh";
}
else {
document.getElementById("clock").style.marginTop = "4vh"
document.getElementById("box").style.marginTop = "2vh"
}
var motnDiv = document.getElementsByClassName("motn");
if (sunnahTimes.motn){
for (element of motnDiv){
element.style.display = "block"
}
}
else{
for (element of motnDiv){
element.style.display = "none"
}
}
var totnDiv = document.getElementsByClassName("totn");
if (sunnahTimes.totn){
for (element of totnDiv){
element.style.display = "block"
}
}
else{
for (element of totnDiv){
element.style.display = "none"
}
}
}
}
function calculateSunnah(){
const nightDuration = (tommorowPrayers.fajr.getTime() - prayerTimes.maghrib.getTime());
//motn = intToHour(msToTime(prayerTimes.maghrib.getTime() + nightDuration / 2));
//totn = intToHour(msToTime(prayerTimes.maghrib.getTime() + nightDuration * (2 / 3);
motn = new Date(prayerTimes.maghrib.getTime() + nightDuration / 2);
totn = new Date(prayerTimes.maghrib.getTime() + nightDuration * (2 / 3));
if (sunnahTimes.totn || sunnahTimes.motn){
loadPrayers()
}
}
/**
* Sets up the update availible modal
*/
function setupUpdateModal(){
var myModal = new bootstrap.Modal(document.getElementById('updateModal'), {
})
var modalButton1 = document.getElementById('modalButton1')
var modalButton2 = document.getElementById('modalButton2')
var modalClose = document.getElementById('modalClose')
window.api.handle('update-available', msg => {
/*modalTitle.innerText = window.api.getLanguage(lang, 'updateAvalible')
modalBody.innerText = window.api.getLanguage(lang, 'downloadSoon')
modalButton1.innerText = window.api.getLanguage(lang, 'ok')*/
document.getElementById("updateModalLabel").innerText = window.api.getLanguage(lang, 'updateAvailable')
document.getElementById('modalBody').innerText = window.api.getLanguage(lang, 'version') + " " + msg[1] + " " + window.api.getLanguage(lang, 'available')
modalButton1.innerText = window.api.getLanguage(lang, 'download')
modalButton2.innerText = window.api.getLanguage(lang, 'later')
modalButton1.addEventListener("click", function(){
window.api.openExternal("https://github.com/DBChoco/Muezzin/releases/latest")
})
modalButton2.addEventListener("click", function(){
myModal.hide()
})
modalClose.addEventListener("click", function(){
myModal.hide()
})
myModal.show()
})
}
/**
* Checks if the weather is enabled, if so, sends api request and shows weather.
* Then starts a timer that reloads the weather every 15m
*/
async function setupWeather(){
if (weatherSettings.enabled){
let units, system;
switch (weatherSettings.units){
case "K":
units = "°K"
system = "standard"
break;
case "F":
units = "°F"
system = "imperial"
break;
default:
units = "°C"
system = "metric"
}
await getWeather(units, system);
setInterval(await getWeather(units, system), 900000) // 15min
}
}
/**
* Sends api request for weather
* @param {*} units
* @param {*} system
*/
async function getWeather(units, system){
try{
response = await fetch('https://api.openweathermap.org/data/2.5/weather?lat=' + lat + '&lon=' + lon + '&units=' + system + '&appid=d8c0aa0e61cf524575a92e44d457ded7', {method: "GET"})
.then(res => res.json())
.then((json) => {
console.debug("Loading weather")
let time
json["weather"][0]["icon"].charAt(json["weather"][0]["icon"].length - 1) == 'd' ? time = "day" : time = "night"
document.getElementById("weatherTemp").innerHTML = loadWeatherIcon(json["weather"][0]["id"], time) + " " + Math.round(json["main"]["temp"]) + units
});
}catch(e){
console.error("Error while loading weather => " + e)
}
}
/**
* @param {Int} weather Id of the weather situation
* @param {String} time Day or night
* @returns An HTML string which contains a weather icon
*/
async function setupWeather(){
try{
response = await fetch('https://api.openweathermap.org/data/2.5/weather?lat=' + lat + '&lon=' + lon + '&lang=' + lang + '&units=metric' + '&appid=d8c0aa0e61cf524575a92e44d457ded7', {method: "GET"})
.then(res => res.json())
.then((json) => {
let time
json["weather"][0]["icon"].charAt(json["weather"][0]["icon"].length - 1) == 'd' ? time = "day" : time = "night"
document.getElementById("weatherTemp").innerHTML = loadWeatherIcon(json["weather"][0]["id"], time) + " " + Math.round(json["main"]["temp"]) + "°C"
});
}catch(e){
console.error("Error while loading weather => " + e)
}
}
function loadWeatherIcon(weather, time = "day"){
if (weather == 210 || weather == 211 || weather == 212 || weather == 221) return '<i class="wi wi-lightning"></i>'
else if (weather >= 200 && weather <= 232) return '<i class="wi wi-storm-showers"></i>'
else if (weather >= 300 && weather < 321) return '<i class="wi wi-rain"></i>'
else if (weather >= 500 && weather <= 504) return '<i class="wi wi-rain"></i>'
else if (weather == 511) return '<i class="wi wi-snowflake-cold'
else if (weather >= 520 && weather <= 531) return '<i class="wi wi-showers"></i>'
else if (weather >= 600 && weather <= 622) return '<i class="wi wi-snow"></i>'
else if (weather == 701 || weather == 741) return '<i class="wi wi-fog"></i>'
else if (weather == 711 || weather == 731 || weather == 751 || weather == 761) return '<i class="wi wi-dust"></i>'
else if (weather == 721) return '<i class="wi wi-smog"></i>'
else if ( weather == 771 || weather == 781 || weather == 900) return '<i class="wi wi-tornado"></i>'
else if (weather == 762) return '<i class="wi wi-volcano"></i>'
else if (weather == 800 || weather == 951){
if (time == "day") return '<i class="wi wi-day-sunny"></i>'
else return '<i class="wi wi-night-clear"></i>'
}
else if (weather == 801){
if (time == "day") return '<i class="wi wi-day-cloudy"></i>'
else return '<i class="wi wi-night-alt-cloudy"></i>'
}
else if (weather == 802) return '<i class="wi wi-cloud"></i>'
else if (weather == 803) return '<i class="wi wi-cloudy"></i>'
else if (weather == 804) return '<i class="wi wi-cloudy"></i>'
else return '<i class="wi wi-na"></i>'
}

161
src/main/styles.css Normal file
View File

@@ -0,0 +1,161 @@
/* styles.css */
header{
display: flex;
flex-direction: row;
}
body{
background-repeat: no-repeat;
background-attachment: fixed;
background-size: cover;
background-position: center;
}
.shader{
background-color: rgba(255, 255, 255, 0.5);
}
#main{
align-items: center;
text-align: center;
margin-left: auto;
margin-right: auto;
}
#clock{
font-size: 10vh;
margin-top: 4vh;
}
#title{
font-size: 3vh;
margin: 5px 0 0 5px;
}
#weatherContainer{
font-size: 3vh;
margin: 0px 10px 0 auto;
}
#dateLoc{
font-size: 3vh;
margin-bottom: 5vh;
}
#timeLeft{
font-size: 6vh;
}
.grid-container {
margin: auto;
margin-bottom: 1vh;
display: grid;
grid-template-columns: auto auto;
width: 48vh;
}
.grid-item {
border-top: 1px solid ;
border-bottom: 1px solid;
padding: 5px;
font-size: 3vh;
text-align: center;
}
.grid-top{
border-top: 2px solid;
}
.grid-bottom{
border-bottom: 2px solid;
}
.left{
text-align: left;
}
.right{
text-align: right;
}
.box-bottom{
text-align: center;
}
.box{
margin: auto 0 auto 0;
align-items: center;
}
#calendar{
height: max(25px, 3vh);
font-size: max(12px, 1.5vh);
}
#buttons{
margin-left: auto;
}
#settingsWheel , #quranButton{
margin: 10px;
font-size: max(1.8vh, 15px);
}
#stopB, #playB{
margin: 10px;
font-size: max(1.5vh, 15px);
}
#volume{
margin-left: 10px;
display: flex;
align-items: center;
font-size: x-large;
}
#mainContainer{
width: fit-content;
display: flex;
align-items: center;
margin: 3vh auto 5vh auto;
}
.form-range{
width: 100px;
align-content: center;
}
#audioControls, #upperPart{
flex-direction: row;
display: flex;
align-items: center;
}
#volSlider{
margin: 0px 10px 0px 10px;
}
.center{
position: absolute;
width: 100%;
height: 100%;
text-align: center;
background-color: #212121;
display: -webkit-flexbox;
display: -ms-flexbox;
display: -webkit-flex;
display: flex;
-webkit-flex-align: center;
-ms-flex-align: center;
-webkit-align-items: center;
align-items: center;
justify-content: center;
color: white;
}
.btn-light, .btn-light:hover{
color: #212121;
}

View File

@@ -0,0 +1,12 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta http-equiv="Content-Security-Policy" content="default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'">
<title>MediaPlayer</title>
</head>
<body>
MEDIA
<script src="renderer.js"></script>
</body>
</html>

View File

@@ -0,0 +1,11 @@
// All of the Node.js APIs are available in the preload process.
// It has the same sandbox as a Chrome extension.
const { contextBridge, ipcRenderer } = require('electron')
contextBridge.exposeInMainWorld( 'api', {
send: ( channel, data ) => ipcRenderer.invoke( channel, data ),
handle: ( channel, func) => ipcRenderer.on( channel, (event, ...args) => func(...args) ),
getFromStore: (key) => ipcRenderer.invoke('getStoreValue', key),
})

View File

@@ -0,0 +1,75 @@
var athan = new Audio('../../ressources/audio/azan3.mp3');
var dua = new Audio('../../ressources/audio/dua.mp3');
var playDua = true;
var interval;
var volume;
loadSettings()
setUpHandlers()
async function loadSettings(){
volume = await window.api.getFromStore('volume', 50)
athan.volume = volume/100
dua.volume = volume/100
}
function setUpHandlers(){
window.api.handle('volume-reply', msg => {
athan.volume = msg/100
dua.volume = msg/100
})
window.api.handle('play', msg => {
if (!athan.paused || !dua.paused){
stop()
}
playDua = msg[2]
athan = new Audio(msg[1]);
setUpAdhanListeners()
athan.play();
})
window.api.handle('stop', msg => {
stop()
})
window.api.send('startup-request');
window.api.handle('startup-reply', msg => {
if (msg == true){
var bismillah = new Audio('../../ressources/audio/Bismillah - Fatih Sefaragic.mp3');
bismillah.volume = volume/100;
bismillah.play()
}
})
}
function stop(){
athan.pause();
athan.load();
dua.pause();
dua.load();
}
function setUpAdhanListeners(){
athan.addEventListener('ended', function(){
window.api.send('progress-request', false)
if (playDua){
dua.play();
}
clearInterval(interval)
})
athan.addEventListener('play', function(){
loadSettings()
window.api.send('progress-request', true)
interval = setInterval(function(){
window.api.send('progress-request', true)
},5000)
})
athan.addEventListener('abort', function(){
window.api.send('progress-request', false)
clearInterval(interval)
})
}

124
src/quran/quran.css Normal file
View File

@@ -0,0 +1,124 @@
@import url("../../ressources/fonts/arabic/fonts.css");
header{
text-align: center;
align-items: center;
}
#title{
font-size: 8vh;
margin: 2vh 0 4vh 0;
}
#surahSelector{
width: max-content;
margin: 0 auto 0 auto;
padding-bottom: 3vh;
display: flex;
flex-direction: row;
}
.quranButton{
display: flex;
white-space: nowrap;
margin: 0 20px 0 20px;
}
#reader{
flex-direction: column;
display: flex;
align-items: center;
width: min(800px, 85vw);
margin: 0 auto 0 auto;
}
.verseContainer{
display: flex;
flex-direction: row;
width: 100%;
border-top: 2px solid;
}
.textContainer, .latinText{
width: 100%
}
.word{
padding: 5px 0px 5px 5px;
}
.wordLatin{
padding: 2px 2px 2px 0px;
}
.arabText{
text-align: right;
font-size: 30px;
margin: 30px;
display: flex;
flex-direction: row-reverse;
flex-wrap: wrap;
font-family: arabic;
font-size: 42px;
}
.arabicWord{
font-family: arabic;
font-size: 42px;
}
.latinText{
display: flex;
flex-direction: row;
flex-wrap: wrap;
margin-bottom: 15px;
font-style: italic;
}
.transText{
margin-bottom: 20px;
}
.sidebar{
width: fit-content;
margin-right: 20px;
align-items: center;
}
.verseNumber{
font-weight: bold;
margin: 10px;
}
.verse{
width: 50%;
height: max-content
}
footer{
flex-direction: row;
display: flex;
align-items: center;
}
#buttonContainer{
margin: 0px 10px 10px auto;
display: flex;
flex-direction: row;
}
#settings, #return{
font-size: max(1.8vh, 15px);
margin-left: 20px;
}
.bismillah{
font-family: arabic-symbols;
font-size: 22vmin;
pointer-events: none;
user-select: none;
}
.btn-light, .btn-light:hover{
color: #212121;
}

61
src/quran/quran.html Normal file
View File

@@ -0,0 +1,61 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<!-- https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP -->
<meta http-equiv="Content-Security-Policy" content="script-src 'self'; style-src 'self' 'unsafe-inline'">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link href="../../node_modules/bootstrap-dark-5/dist/css/bootstrap-night.min.css" rel="stylesheet" >
<script src="../../node_modules/bootstrap/dist/js/bootstrap.min.js"></script>
<link href="../../ressources/fonts/FontAwesome/all.min.css" rel="stylesheet" >
<link href="quran.css" rel="stylesheet">
<script src="quran.js"></script>
<title>Muezzin</title>
</head>
<header>
<h1 id="title">
Qur'an
</h1>
</header>
<body class="d-flex flex-column min-vh-100">
<div id="surahSelector">
<button class="btn btn-dark quranButton" id="previousSurah">
Previous Surah
</button>
<select class="form-control" id="chaptersList">
</select>
<button class="btn btn-dark quranButton" id="nextSurah">
Next Surah
</button>
</div>
<div id="reader">
<div class="bismillah">
U
</div>
</div>
</body>
<footer class="mt-auto fixed-bottom">
<div id="buttonContainer">
<button class="btn btn-dark" id="toTheTop">
<i class="fa-solid fa-angles-up"></i>
</button>
<button class="btn btn-dark" id="settings">
<i class="fa-solid fa-gear"></i>
Settings
</button>
<button class="btn btn-dark" id="return">
<i class="fa fa-arrow-circle-left"></i>
Return
</button>
</div>
</footer>
</html>

256
src/quran/quran.js Normal file
View File

@@ -0,0 +1,256 @@
var lang;
var quran;
window.addEventListener('DOMContentLoaded', () => {
loadSettings()
buttonListeners();
loadQuranList();
//Make initial greeter when no Surahs are loaded
//Add settings
setupPreviousNextButtons()
})
//Generates the div for 1 single verse.
//Calls the functio to generate the arabText as well
function generateVerse(number, arabText){
let verseContainer = createDiv("verseContainer")
var sidebarDiv = createDiv("sidebar")
var verseNumberDiv = createDiv("verseNumber")
var textContainerDiv = createDiv("textContainer")
var arabTextDiv = createDiv("arabText")
verseNumberDiv.innerHTML = number;
generateArabText(arabTextDiv, arabText, number.split(":")[1])
sidebarDiv.appendChild(verseNumberDiv)
textContainerDiv.appendChild(arabTextDiv)
textContainerDiv.id = "textContainer" + number
verseContainer.appendChild(sidebarDiv)
verseContainer.appendChild(textContainerDiv)
document.getElementById("reader").appendChild(verseContainer)
}
//Takes the div and the text, divides the text into divs and puts them into the mother div
function generateArabText(arabTextDiv, arabText, verseNumber){
var arabWords = arabText.split(" ");
for (let word of arabWords){
wordDiv = document.createElement("div");
wordDiv.classList.add("word")
wordDiv.classList.add("arabicWord")
wordDiv.innerText = word;
wordDiv.style.fontSize = quran.fontsize +"px";
arabTextDiv.appendChild(wordDiv)
}
wordDiv = document.createElement("div");
wordDiv.classList.add("word")
wordDiv.innerText = new Intl.NumberFormat('ar-SA').format(verseNumber)
wordDiv.style.fontSize = quran.fontsize +"px";
arabTextDiv.appendChild(wordDiv)
}
//Loads the list of Surahs
//TODO: Make it local.
async function loadQuranList(){
let chaptersList = document.getElementById("chaptersList")
try{
response = await fetch('../../ressources/quran/chapters.json')
.then(res => res.json())
.then((json) => {
for (let chapter of json["chapters"]){
var option = document.createElement("option")
option.value = chapter["id"];
option.innerText = "[" + chapter["id"] + "] " + chapter["name_simple"] + " - " + chapter["name_arabic"];
chaptersList.appendChild(option)
}
chaptersList.addEventListener("change", function(){
loadSurah(chaptersList.options[chaptersList.selectedIndex].value)
})
chaptersList.selectedIndex = -1
});
}catch(e){
console.error("Error while loading list of Surahs" + e)
}
}
//When selecting a Surah, this is launched.
//Calls the apis for the arabText, latins and translations and applies them.
async function loadSurah(number){
console.debug("Loading Surah n°" + number)
document.getElementById("reader").innerHTML = ""
var numberVerses = 0;
try{
response = await fetch('https://api.quran.com/api/v4/quran/verses/uthmani?chapter_number=' + number + '', {method: "GET"})
.then(res => res.json())
.then((json) => {
for (let verse of json["verses"]){
generateVerse(verse["verse_key"], verse["text_uthmani"])
numberVerses ++;
}
});
}catch(e){
console.error("Couldn't load the Surah: " + e);
}
var numberTranslated = 0;
var page = 1;
if (quran.transliteration.show || quran.translation.show){
while (numberTranslated < numberVerses){
try{
response = await fetch('https://api.quran.com/api/v4/verses/by_chapter/' + number + '?language='+ quran.translation.lang +
'&words=true&translations=' + quran.translation.trans + '&page=' + page, {method: "GET"})
.then(res => res.json())
.then((json) => {
for (let verse of json["verses"]){
if (quran.transliteration.show){
addLatinText(verse)
}
if (quran.translation.show){
addTranslation(verse)
}
numberTranslated ++;
}
page++
});
}catch(e){
console.error("Couldn't load the translation: " + e)
break; //If the user switches Surahs too fast, this saves lives.
}
}
}
}
//Takes a verse made up of words and adds them separatly to the verseContainer
//Possibility to add mouseOver events later on.
function addLatinText(verse){
var textContainerDiv = document.getElementById("textContainer" + verse["verse_key"])
var latinTextDiv = createDiv("latinText")
for (let word of verse["words"]){
var wordDiv = document.createElement("div")
wordDiv.classList.add("wordLatin")
wordDiv.innerText = word["transliteration"]["text"]
latinTextDiv.appendChild(wordDiv)
}
latinTextDiv.style.fontSize = quran.transliteration.fontsize + "px";
textContainerDiv.appendChild(latinTextDiv)
}
//Takes a verse and loads the translation to the textContainerDiv
function addTranslation(verse){
var textContainerDiv = document.getElementById("textContainer" + verse["verse_key"])
var transTextDiv = createDiv("transText")
transTextDiv.innerHTML = verse["translations"][0]["text"]
transTextDiv.style.fontSize = quran.translation.fontsize + "px";
textContainerDiv.appendChild(transTextDiv)
}
//Creates a div with the class name = divClass
function createDiv(divClass){
var divv = document.createElement("div");
divv.classList.add(divClass);
return divv;
}
//Loads all the necessary settings
async function loadSettings(){
lang = await window.api.getFromStore('language', 'en')
translate()
quran = await window.api.getFromStore('quran', {
fontsize: 42,
translation:{
show: true,
lang: {
enabled: false,
lang: "en"
},
trans: 131,
fontsize: 14
},
transliteration:{
show: true,
fontsize: 14
},
})
var darkmode = await window.api.getFromStore('darkMode', false)
window.api.setTheme(darkmode, "quran.css");
}
function buttonListeners(){
document.getElementById("settings").addEventListener("click", function(){
window.location.assign("../settings/settings.html?page=quran");
})
document.getElementById("return").addEventListener("click", function(){
window.location.assign("../main/index.html");
})
document.getElementById("toTheTop").addEventListener("click", function(){
window.scrollTo({ top: 0, behavior: 'smooth' });
})
}
//Does not work yet, I'll have to work on this one.
function downloadOrFetch(link, path, filename){
var absolutePath = appDataPath + path
var file = new File(absolutePath + filename)
if (file.exists()){
console.log("yes")
}
else{
window.api.send("download", {
url: link,
properties: {
directory: absolutePath,
filename: filename
}
});
}
}
function translate(){
document.getElementById("title").innerHTML = window.api.getLanguage(lang, "quran");
document.getElementById("settings").innerHTML = '<i class="fa-solid fa-gear"></i> ' + window.api.getLanguage(lang, "settings");
document.getElementById("return").innerHTML = '<i class="fa fa-arrow-circle-left"></i> ' + window.api.getLanguage(lang, "return");
}
function setupPreviousNextButtons(){
let chapterList = document.getElementById("chaptersList")
let previous = document.getElementById("previousSurah")
let next = document.getElementById("nextSurah")
chapterList.addEventListener("change", function(){
previous.disabled = false;
next.disabled = false;
if (chaptersList.options[chaptersList.selectedIndex].value == 1){
previous.disabled = true;
} else if (chaptersList.options[chaptersList.selectedIndex].value == 114){
next.disabled = true;
}
})
previous.addEventListener("click", function(){
if (!previous.disabled){
chapterList.selectedIndex -= 1;
chapterList.dispatchEvent(new Event('change'))
}
})
next.addEventListener("click", function(){
if (!next.disabled){
chapterList.selectedIndex += 1;
chapterList.dispatchEvent(new Event('change'))
}
})
}

151
src/settings/settings.css Normal file
View File

@@ -0,0 +1,151 @@
#settingsTitle{
padding: 30px;
font-size: 72px;
text-align: center;
}
.grid-container {
display: grid;
grid-template-columns: auto auto;
padding: 10px;
}
.grid-itemL {
text-align: left;
margin-right: 30px;
}
.grid-itemR {
width: fit-content;
margin-left: auto;
text-align: right;
align-items: right;
padding-bottom: 15px;
font-size: 16px;
}
.grid-R-spacer{
padding-bottom: 10px;
}
.grid-itemR-S {
width: 50%;
}
.grid-sub{
margin-left: 25px;
}
.colorLabel{
text-align: right;
padding-left: auto;
}
#v-pills-tab{
border-style: none solid none none ;
border-width: 2px;
padding-right: 20px;
font-size: 26px;
}
/* Style the button that is used to open and close the collapsible content */
.collapsible {
cursor: pointer;
padding: 18px;
width: 100%;
border: none;
text-align: left;
outline: none;
font-size: 30px;
}
/*Pill color*/
.nav-pills .nav-link.active {
background-color: #212121
}
/*Font Color*/
.nav-link{
color: black;
}
/* Add a background color to the button if it is clicked on (add the .active class with JS), and when you move the mouse over it (hover)
.active, .collapsible:hover {
}*/
/* Style the collapsible content. Note: hidden by default */
.content {
padding: 0 18px;
display: none;
overflow: hidden;
position: absolute;
}
#v-pills-tabContent{
width: 65%;
}
#return{
font-size: max(1.8vh, 15px);
margin: 0px 10px 10px auto;
}
.container{
max-width: 900px;
margin-left: auto;
margin-right: auto;
padding-bottom: auto;
}
.image{
height: 50px;
}
/*Pill color*/
.light-pills .light-link.active {
background-color: #212121
}
/*Font Color*/
.light-link{
color: black;
}
/*Pill color*/
.dark-pills .dark-link.active {
background-color: #adb5bd;
color: #212121
}
/*Font Color*/
.dark-link{
color: white;
}
/*Hover color
.nav-link:hover {
color: pink;
}*/
.btn-light, .btn-light:hover{
color: #212121;
}
#citation{
text-align: center;
margin:auto 10% auto 10%
}
footer{
flex-direction: row;
display: flex;
align-items: center;
}
.shortInput{
width: 100px;
margin-left: auto;
}

603
src/settings/settings.html Normal file
View File

@@ -0,0 +1,603 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<!-- https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP -->
<meta http-equiv="Content-Security-Policy" content="script-src 'self'; style-src 'self' 'unsafe-inline'">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link href="../../node_modules/bootstrap-dark-5/dist/css/bootstrap-night.min.css" rel="stylesheet" >
<script src="../../node_modules/bootstrap/dist/js/bootstrap.min.js"></script>
<link href="../../ressources/fonts/FontAwesome/all.min.css" rel="stylesheet" >
<link href="settings.css" rel="stylesheet">
<script src="settings.js"></script>
<title>Muezzin</title>
</head>
<body class="d-flex flex-column min-vh-100">
<h1 id=settingsTitle>Settings</h1>
<div id="settingsContainer" class="container">
<div class="d-flex align-items-start">
<div class="nav flex-column nav-pills me-3" id="v-pills-tab" role="tablist" aria-orientation="vertical">
<button class="nav-link active" id="v-pills-general-tab" data-bs-toggle="pill" data-bs-target="#v-pills-general" type="button" role="tab" aria-controls="v-pills-general" aria-selected="true">
General
</button>
<button class="nav-link" id="v-pills-location-tab" data-bs-toggle="pill" data-bs-target="#v-pills-location" type="button" role="tab" aria-controls="v-pills-location" aria-selected="false">
Location
</button>
<button class="nav-link" id="v-pills-audio-tab" data-bs-toggle="pill" data-bs-target="#v-pills-audio" type="button" role="tab" aria-controls="v-pills-audio" aria-selected="false">Audio</button>
<button class="nav-link" id="v-pills-appearance-tab" data-bs-toggle="pill" data-bs-target="#v-pills-appearance" type="button" role="tab" aria-controls="v-pills-appearance" aria-selected="false">Appearance</button>
<button class="nav-link" id="v-pills-advanced-tab" data-bs-toggle="pill" data-bs-target="#v-pills-advanced" type="button" role="tab" aria-controls="v-pills-advanced" aria-selected="false">Advanced</button>
<button class="nav-link" id="v-pills-adjustments-tab" data-bs-toggle="pill" data-bs-target="#v-pills-adjustments" type="button" role="tab" aria-controls="v-pills-adjustments" aria-selected="false">Adjustments</button>
<button class="nav-link" id="v-pills-quran-tab" data-bs-toggle="pill" data-bs-target="#v-pills-quran" type="button" role="tab" aria-controls="v-pills-quran" aria-selected="false">
<i class="fa-solid fa-book-quran"></i>
Qur'an
</button>
</div>
<div class="tab-content" id="v-pills-tabContent">
<div class="tab-pane fade show active" id="v-pills-general" role="tabpanel" aria-labelledby="v-pills-general-tab"> <!-- General -->
<div class="grid-container">
<div class="grid-itemL">
<p class="h4" id="langText">Language </p>
</div>
<div class="grid-itemR">
<form >
<select class="form-select" id="langlist" name="Language">
<option value="en">English</option>
<option value="fr">Français</option>
<option value="es">Español</option>
<option value="it">Italiano</option>
<option value="ar">عربي</option>
<option value="de">Deutsch</option>
<option value="nl">Nederlands</option>
<option value="no">Norsk (bokmål)</option>
<option value="sv">Svenska</option>
<option value="da">Dansk</option>
<option value="ur">Urdu</option>
<option value="tr">Türkçe</option>
</select>
</form>
</div>
<div class="grid-itemL">
<p class="h4" id="tfText">Time format</p>
</div>
<div class="grid-itemR">
<form >
<div class="form-check">
<input class="form-check-input" type="radio" name="timeFormat" id="24hTimeFormat">
<label class="form-check-label" for="24hTimeFormat" id="24hTimeFormatText">
24 Hour
</label>
</div>
<div class="form-check">
<input class="form-check-input" type="radio" name="timeFormat" id="12hTimeFormat" checked>
<label class="form-check-label" for="12hTimeFormat" id="12hTimeFormatText">
12 Hour
</label>
</div>
<div class="form-check">
<input class="form-check-input" type="checkbox" value="" id="showSeconds" checked>
<label class="form-check-label" for="showSeconds" id="showSecondsText">
Show seconds
</label>
</div>
</form>
</div>
<div class="grid-itemL">
<p class="h4" id="dfText">Date format</p>
</div>
<div class="grid-itemR">
<form >
<div class="form-check">
<input class="form-check-input" type="radio" name="dateFormat" id="dateFormat1" checked>
<label class="form-check-label" for="dateFormat1" id="df1Text">
DD/MM/YYYY
</label>
</div>
<div class="form-check">
<input class="form-check-input" type="radio" name="dateFormat" id="dateFormat2">
<label class="form-check-label" for="dateFormat2" id="df2Text">
MM/DD/YYYY
</label>
</div>
<div class="form-check">
<input class="form-check-input" type="radio" name="dateFormat" id="dateFormat3">
<label class="form-check-label" for="dateFormat3" id="df3Text">
YYYY/MM/DD
</label>
</div>
</form>
</div>
<div class="grid-itemL">
<p class="h4" id="notifText">Notifications</p>
</div>
<div class="grid-itemR">
<form>
<div class="form-check grid-R-spacer">
<label class="form-check-label" for="notifCheck" id="notifCheckText">
Enable notifications
</label>
<input class="form-check-input" type="checkbox" id="notifCheck" checked>
</div>
</form>
</div>
<div class="grid-itemL">
<p class="h4" id="systrayText">System tray</p>
</div>
<div class="grid-itemR">
<form>
<div class="form-check grid-R-spacer">
<label class="form-check-label" for="systrayCheck" id="systrayCheckText">
Minimize to tray
</label>
<input class="form-check-input" type="checkbox" id="systrayCheck" checked>
</div>
</form>
</div>
<div class="grid-itemL">
<p class="h4" id="autoStartText">Auto Start</p>
</div>
<div class="grid-itemR">
<form>
<div class="form-check grid-R-spacer">
<label class="form-check-label" for="autoStartCheck" id="autoStartCheckText">
Start at launch
</label>
<input class="form-check-input" type="checkbox" id="autoStartCheck" checked>
</div>
<div class="form-check grid-R-spacer">
<label class="form-check-label" for="minStartCheck" id="minStartCheckText">
Start minimized
</label>
<input class="form-check-input" type="checkbox" id="minStartCheck" unchecked>
</div>
</form>
</div>
</div>
</div>
<div class="tab-pane fade" id="v-pills-location" role="tabpanel" aria-labelledby="v-pills-location-tab"> <!-- Location -->
<div class="grid-container">
<div class="grid-itemL">
<p class="h4" id="coordinatesText">Coordinates</p>
</div>
<div class="grid-itemR grid-itemR-S">
<label class="form-check-label" for="latInput" id="latText">
Latitude
</label>
<input id="latInput" class="form-control" type="number" value="0.00" min="-90" max="90" step="0.1" maxlength="5"
placeholder="00.00">
</div>
<div class="grid-itemL grid-sub">
</div>
<div class="grid-itemR grid-itemR-S">
<label class="form-check-label" for="lonInput" id="lonText">
Longitude
</label>
<input id="lonInput" class="form-control" type="number" value="0.00" min="-180" max="180" step="0.1"
placeholder="00.00">
</div>
<div class="grid-itemL">
<p class="h4" id="tzText">Time zone</p>
</div>
<div class="grid-itemR">
<form >
<select class="form-select" id="tzlist" name="TimeZone"></select>
</form>
</div>
</div>
</div>
<div class="tab-pane fade" id="v-pills-audio" role="tabpanel" aria-labelledby="v-pills-audio-tab"> <!-- Audio -->
<div class="grid-container">
<div class="grid-itemL">
<p class="h4" id="adhanText">Adhan </p>
</div>
<div class="grid-itemR">
<form >
<div class="form-check grid-R-spacer">
<label class="form-check-label" for="adhanCheck" id="adhanCheckText">
Enable Adhan
</label>
<input class="form-check-input" type="checkbox" id="adhanCheck" unchecked>
</div>
<div class="grid-R-spacer">
<select class="form-select" id="adhanList" name="adhanList">
<option id="adhanMecca" value="../../ressources/audio/Adhan - Mecca.mp3" id="adhanMeccaText">Adhan Mecca</option>
<option id="adhanAqsa" value="../../ressources/audio/Adhan - al-Aqsa.mp3" id="adhanAqsaText">Adhan al-Aqsa</option>
</select>
</div>
<div class="form-check grid-R-spacer">
<label class="form-check-label" for="customAdhan" id="customAdhanText">
Custom Adhan
</label>
<input class="form-check-input" type="checkbox" id="customAdhan" checked>
</div>
<div class="mb-3">
<input class="form-control" type="file" id="customAdhanFile" accept="audio/*,.mp3, .ogg, .wav">
</div>
</form>
</div>
<div class="grid-itemL">
<p class="h4" id="duaAfterText">Du'a after Adhan</p>
</div>
<div class="grid-itemR">
<div class="gform-check grid-R-spacer">
<input class="form-check-input" type="checkbox" value="" id="duaCheck" checked>
<label class="form-check-label" for="duaCheck" id="duaCheckText">
Play Du'a after Adhan
</label>
</div>
</div>
<div class="grid-itemL">
<p class="h4" id="startUpSoundText">Start Up Sound</p>
</div>
<div class="grid-itemR">
<form>
<div class="form-check grid-R-spacer">
<label class="form-check-label" for="startUpSound" id="startUpSoundText2">
Play sound on startup
</label>
<input class="form-check-input" type="checkbox" id="startUpSound" checked>
</div>
</form>
</div>
</div>
</div>
<div class="tab-pane fade" id="v-pills-appearance" role="tabpanel" aria-labelledby="v-pills-appearance-tab"> <!-- Appearance -->
<div class="grid-container">
<div class="grid-itemL">
<p class="h4" id="themeText">Theme</p>
</div>
<div class="grid-itemR">
<div class="form-check grid-R-spacer">
<label class="form-check-label" for="darkModeCheck" id="darkModeText">Enable Dark Mode</label>
<input class="form-check-input" type="checkbox" value="" id="darkModeCheck" checked>
</div>
</div>
<div class="grid-itemL">
<p class="h4" id="bgImageText">Background Image</p>
</div>
<div class="grid-itemR">
<form >
<div class="form-check grid-R-spacer">
<label class="form-check-label" for="bgImageCheck" id="bgImageCheckText">Enable Background Image</label>
<input class="form-check-input" type="checkbox" value="" id="bgImageCheck" checked>
</div>
<div class="mb-3">
<input class="form-control" type="file" id="customBgImage" accept="image/*">
</div>
</form>
</div>
<div class="grid-itemL">
<p class="h4" id="sunnahTimesText">Show Sunnah times</p>
</div>
<div class="grid-itemR">
<form >
<div class="form-check grid-R-spacer">
<label class="form-check-label" for="MOTNCheck" id="MOTNCheckText">Middle of the night</label>
<input class="form-check-input" type="checkbox" value="" id="MOTNCheck" unchecked>
</div>
<div class="form-check grid-R-spacer">
<label class="form-check-label" for="TOTNCheck" id="TOTNCheckText">Third of the night</label>
<input class="form-check-input" type="checkbox" value="" id="TOTNCheck" unchecked>
</div>
</form>
</div>
<div class="grid-itemL">
<p class="h4" id="sunnahTimesText">Weather</p>
</div>
<div class="grid-itemR">
<form >
<div class="form-check grid-R-spacer">
<label class="form-check-label" for="weatherCheck" id="weatherCheckText">Show weather</label>
<input class="form-check-input" type="checkbox" value="" id="weatherCheck" checked>
</div>
<div class="form-check grid-R-spacer">
<form >
<label class="form-check-label" for="unitList" id="unitListText">Units</label>
<select class="form-select" id="unitList" name="units">
<option value="C">Celcius</option>
<option value="K">Kelvin</option>
<option value="F">Fahrenheit</option>
</select>
</form>
</div>
</form>
</div>
</div>
</div>
<div class="tab-pane fade" id="v-pills-advanced" role="tabpanel" aria-labelledby="v-pills-advanced-tab"> <!-- Advanced -->
<div class="grid-container">
<div class="grid-itemL">
<p class="h4" id="calcMethodsText">Calculation Methods</p>
</div>
<div class="grid-itemR">
<form >
<select class="form-select" id="calcMethodList" name="calcMethodList">
<option id="MWL" value="MWL" selected>Muslim World League</option>
<option id="Egyptian" value="Egyptian">Egyptian</option>
<option id="Karachi" value="Karachi">Karachi</option>
<option id="UAQ" value="UAQ">Umm al-Qura</option>
<option id="Dubai" value="Dubai">Dubai</option>
<option id="Qatar" value="Qatar">Qatar</option>
<option id="Kuwait" value="Kuwait">Kuwait</option>
<option id="MC" value="MC">Moonsighting Committee</option>
<option id="Singapore" value="Singapore">Singapore</option>
<option id="Turkey" value="Turkey">Turkey</option>
<option id="Tehran" value="Tehran">Tehran</option>
<option id="ISNA" value="ISNA">ISNA</option>
<option id="France12" value="France12">France 12</option>
<option id="France15" value="France15">France 15</option>
<option id="France18" value="France18">France 18</option>
<option id="Russia" value="Russia">Russia</option>
<option id="Gulf" value="Gulf">Gulf Region</option>
</select> <br>
</form>
</div>
<div class="grid-itemL">
<p class="h4" id="MadhabText">Madhab</p>
</div>
<div class="grid-itemR">
<select class="form-select" id="madhabList" name="madhabList">
<option id="Shafi" value="Shafi" selected>Shafi</option>
<option id="Hanafi" value="Hanafi">Hanafi</option>
</select>
</div>
<div class="grid-itemL">
<p class="h4" id="hlrText">High Latitude Rule</p>
</div>
<div class="grid-itemR">
<select class="form-select" id="highLatitudeRuleList" name="highLatitudeRuleList">
<option id="MOTN" value="MOTN">Middle of the Night</option>
<option id="SOTN" value="SOTN">Seventh of the Night</option>
<option id="TA" value="TA" selected>Twilight Angle</option>
</select>
</div>
<div class="grid-itemL" data-bs-toggle="tooltip" data-bs-placement="top" title="Tooltip on top">
<p class="h4" id="pcrText">Polar Circle Resolution</p>
</div>
<div class="grid-itemR">
<select class="form-select" id="polarCircleResolutionList" name="polarCircleResolutionList">
<option id="CC" value="CC" selected>Closest City</option>
<option id="CD" value="CD">Closest Date</option>
<option id="UND" value="UND">Do not calculate</option>
</select>
</div>
<div class="grid-itemL">
<p class="h4" id="ShafaqText">Shafaq</p>
</div>
<div class="grid-itemR">
<select class="form-select" id="shafaqList" name="shafaqList">
<option id="shafaqG" value="shafaqG" selected>General</option>
<option id="shafaqR" value="shafaqR">Red Twilight (ahmer)</option>
<option id="shafaqW" value="shafaqW">White Twilight (abyad)</option>
</select>
</div>
<div class="grid-itemL">
<p class="h4" id="customSettText">Custom settings</p>
</div>
<div class="grid-itemR">
<form>
<div class="form-check">
<label class="form-check-label" for="customCalcCheck" id="enableCalcText">
Enable Custom Calculation Settings
</label>
<input class="form-check-input" type="checkbox" id="customCalcCheck" unchecked>
</div>
<label class="form-check-label" for="fajrAngle" id="fajrAngleText">
Fajr Angle
</label>
<input id="fajrAngle" class="form-control shortInput" type="number" value="0.00" min="-90" max="90" step="0.1" maxlength="5"
placeholder="00.00">
<label class="form-check-label" for="maghribAngle" id="maghribAngleText">
Maghrib Angle
</label>
<input id="maghribAngle" class="form-control shortInput" type="number" value="0.00" min="-90" max="90" step="0.1" maxlength="5"
placeholder="00.00">
<label class="form-check-label" for="ishaAngle" id="ishaAngleText">
Isha Angle
</label>
<input id="ishaAngle" class="form-control shortInput" type="number" value="0.00" min="-90" max="90" step="0.1" maxlength="5"
placeholder="00.00">
</form>
</div>
<div class="grid-itemL">
</div>
<div class="grid-itemR">
<form>
<div class="form-check">
<label class="form-check-label" for="delayCheck" id="delayText">
Delay after Maghrib
</label>
<input class="form-check-input" type="checkbox" id="delayCheck" unchecked>
</div>
<label class="form-check-label" for="ishaAngle" id="delayFormText">
Delay (minutes)
</label>
<input id="delayForm" class="form-control shortInput" type="number" value="0" min="0" max="300" step="1" maxlength="3"
placeholder="00">
</form>
</div>
</div>
</div>
<div class="tab-pane fade" id="v-pills-adjustments" role="tabpanel" aria-labelledby="v-pills-adjustments-tab"> <!-- Adjustements -->
<div class="grid-container">
<div class="grid-itemL">
<p class="h4" id="adjustmentsText">Adjustments</p>
</div>
<div class="grid-itemR">
<form >
<div class="form-check grid-R-spacer">
<label class="form-check-label" for="adjCheck" id="adjCheckText">
Enable adjustments
</label>
<input class="form-check-input" type="checkbox" id="adjCheck" unchecked>
</div>
<div class="form-check grid-R-spacer">
<label class="form-check-label" for="fajrAdjInput" id="fajrAdjText">
Fajr Adjustments
</label>
<input id="fajrAdjInput" class="form-control shortInput" type="number" value="0" min="-90" max="90" step="1" maxlength="5"
placeholder="00.00">
</div>
<div class="form-check grid-R-spacer">
<label class="form-check-label" for="dhuhrAdjInput" id="dhuhrAdjText">
Dhuhr Adjustments
</label>
<input id="dhuhrAdjInput" class="form-control shortInput" type="number" value="0" min="-90" max="90" step="1" maxlength="5"
placeholder="00.00">
</div>
<div class="form-check grid-R-spacer">
<label class="form-check-label" for="asrAdjInput" id="asrAdjText">
Asr Adjustments
</label>
<input id="asrAdjInput" class="form-control shortInput" type="number" value="0" min="-90" max="90" step="1" maxlength="5"
placeholder="00.00">
</div>
<div class="form-check grid-R-spacer">
<label class="form-check-label" for="maghribAdjInput" id="maghribAdjText">
Maghrib Adjustments
</label>
<input id="maghribAdjInput" class="form-control shortInput" type="number" value="0" min="-90" max="90" step="1" maxlength="5"
placeholder="00.00">
</div>
<div class="form-check grid-R-spacer">
<label class="form-check-label" for="ishaAdjInput" id="ishaAdjText">
Isha Adjustments
</label>
<input id="ishaAdjInput" class="form-control shortInput" type="number" value="0" min="-90" max="90" step="1" maxlength="5"
placeholder="00.00">
</div>
</form>
</div>
</div>
</div>
<div class="tab-pane fade" id="v-pills-quran" role="tabpanel" aria-labelledby="v-pills-quran-tab"> <!-- Qur'an -->
<div class="grid-container">
<div class="grid-itemL">
<p class="h4" id="quranFontText">Font</p>
</div>
<div class="grid-itemR">
<div class="form-check grid-R-spacer">
<label class="form-check-label" for="quranFontSize" id="quranFontSizeText">
Font Size
</label>
<input id="quranFontSize" class="form-control shortInput" type="number" value="20" min="12" max="100" step="1" placeholder="20">
</div>
</div>
<div class="grid-itemL">
<p class="h4" id="translationText">Translation</p>
</div>
<div class="grid-itemR">
<div class=" grid-R-spacer">
<input class="form-check-input" type="checkbox" id="showTranslationCheck" checked>
<label class="form-check-label" for="showTranslationCheck" id="showTranslationCheckText">
Show translation
</label>
</div>
<div class="grid-R-spacer">
<input class="form-check-input" type="checkbox" id="quranLangCheck" unchecked>
<label class="form-check-label" for="quranLangCheck" id="quranLangCheckText">
Different language from app
</label>
</div>
<div class="form-check grid-R-spacer">
<form >
<select class="form-select" id="quranLangList" name="quranLanguage">
</select>
</form>
</div>
<div class="form-check grid-R-spacer">
<form >
<select class="form-select" id="translationList" name="translationList">
</select>
</form>
</div>
<div class="form-check">
<label class="form-check-label" for="translationFontSize" id="translationFontSizeText">
Font Size
</label>
<input id="translationFontSize" class="form-control shortInput" type="number" value="20" min="12" max="100" step="1" placeholder="20">
</div>
</div>
<div class="grid-itemL">
<p class="h4" id="transliterationText">Transliteration</p>
</div>
<div class="grid-itemR">
<div class="form-check grid-R-spacer">
<label class="form-check-label" for="showTransliterationCheck" id="showTransliterationCheckText">
Show transliteration
</label>
<input class="form-check-input" type="checkbox" id="showTransliterationCheck" checked>
</div>
<div class="form-check grid-R-spacer">
<label class="form-check-label" for="TransliterationFontSize" id="transliterationFontSizeText">
Font Size
</label>
<input id="transliterationFontSize" class="form-control shortInput" type="number" value="20" min="12" max="100" step="1" placeholder="20">
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<figure id="citation">
<blockquote id="quote" class="blockquote" >
<p>Indeed, prayer has been decreed upon the believers a decree of specified times.</p>
</blockquote>
<figcaption class="blockquote-footer">
<cite id=source title="Source of quote">Qur'an: 4/103</cite>
</figcaption>
</figure>
</body>
<footer class="mt-auto">
<button class="btn btn-dark" id="return">Return</button>
</footer>
</html>

944
src/settings/settings.js Normal file
View File

@@ -0,0 +1,944 @@
var timeDisplay, language, adhanFile, bgImage;
var lat,lon;
var fromQuran = false;
window.addEventListener('DOMContentLoaded', () => {
loadSettings()
setNumberLimit(document.getElementById("latInput"));
setNumberLimit(document.getElementById("lonInput"));
setTZlist();
returnButton();
addThemeListener()
setKeyPress();
addLangListener()
loadQueryString()
})
/**
* Saves all settings to the store
*/
async function saveSettings(){
await saveTimeDateFormat()
//Values that might change too much should not be saved automaticaly (no saver)
if (lat != document.getElementById("latInput").value) await window.api.setToStore('latitude', lat);
if (lon != document.getElementById("lonInput").value) await window.api.setToStore('longitude', lon);
await saveAdhan()
await saveBgImage()
await saveAdjustments()
await saveCustomSettings()
await saveQuran()
}
/**
* Loads all the settings from the store
*/
async function loadSettings(){
lat = await window.api.getFromStore('latitude', 0.00);
lon = await window.api.getFromStore('longitude', 0.00);
var tzVal = await window.api.getFromStore('timezone', 'US/Central');
var darkMode = await window.api.getFromStore('darkMode', false);
language = await window.api.getFromStore('language', 'en');
var sunnahTimes = await window.api.getFromStore("sunnahTimes", {
motn: false,
totn: false
})
timeDisplay = await window.api.getFromStore("timeDisplay", {
clockFormat: 12,
dateFormat: 'DD/MM/YYYY',
showSeconds: true
})
var settings = await window.api.getFromStore("settings", {
startupSound: false,
notifCheck: true,
systray: true,
adhanCheck: true,
autoStart: true,
minStart: false
})
var weather = await window.api.getFromStore("weather", {
enabled: true,
units: "C"
})
var calculationMethod = await window.api.getFromStore("calculationMethod", {
calcMethod: 'MWL',
madhab: 'Shafi',
hlr: 'TA',
pcr: 'CC',
shafaq: 'shafaqG'
})
await loadAdhan()
await loadCustomSettings()
await loadAdjustments()
document.getElementById("latInput").value = lat
document.getElementById("lonInput").value = lon
//Selects the loaded value from the lists
selectFromList(document.getElementById('langlist'), language)
selectFromList(document.getElementById("tzlist"), tzVal)
selectFromList(document.getElementById("calcMethodList"), calculationMethod.calcMethod)
selectFromList(document.getElementById("madhabList"), calculationMethod.madhab)
selectFromList(document.getElementById("highLatitudeRuleList"), calculationMethod.hlr)
selectFromList(document.getElementById("polarCircleResolutionList"), calculationMethod.pcr)
selectFromList(document.getElementById("shafaqList"), calculationMethod.shafaq)
await addSaverValue(document.getElementById("tzlist"), tzVal, "timezone")
await addSaverValue(document.getElementById("calcMethodList"), calculationMethod.calcMethod, "calculationMethod.calcMethod")
await addSaverValue(document.getElementById("madhabList"), calculationMethod.madhab, "calculationMethod.madhab")
await addSaverValue(document.getElementById("highLatitudeRuleList"), calculationMethod.hlr, "calculationMethod.hlr")
await addSaverValue(document.getElementById("polarCircleResolutionList"), calculationMethod.pcr, "calculationMethod.pcr")
await addSaverValue(document.getElementById("shafaqList"), calculationMethod.shafaq, "calculationMethod.shafaq")
await addSaverValue(document.getElementById("langlist"), language, "language")
await addSaverValue(document.getElementById("unitList"), weather.units, "weather.units")
await addSaverChecked(document.getElementById("darkModeCheck"), darkMode, 'darkMode')
await addSaverChecked(document.getElementById("notifCheck"), settings.notifCheck, 'settings.notifCheck');
await addSaverChecked(document.getElementById("adhanCheck"), settings.adhanCheck, 'settings.adhanCheck');
await addSaverChecked(document.getElementById("systrayCheck"), settings.systray, 'settings.systray');
await addSaverChecked(document.getElementById("startUpSound"), settings.startupSound, 'settings.startupSound');
await addSaverChecked(document.getElementById("autoStartCheck"), settings.autoStart, 'settings.autoStart');
await addSaverChecked(document.getElementById("minStartCheck"), settings.minStart, 'settings.minStart');
await addSaverChecked(document.getElementById("MOTNCheck"), sunnahTimes.motn, 'sunnahTimes.motn');
await addSaverChecked(document.getElementById("TOTNCheck"), sunnahTimes.totn, 'sunnahTimes.totn');
await addSaverChecked(document.getElementById("showSeconds"), timeDisplay.showSeconds, "timeDisplay.showSeconds")
await addSaverChecked(document.getElementById("weatherCheck"), weather.enabled, "weather.enabled")
window.api.setTheme(darkMode, "settings.css");
setTimeDateFormat()
disableAdhanListener()
loadLanguage(language)
loadBgImage()
await loadQuranSettings()
}
/**
* Sets the element to the value and adds a listener
* If the element changes, the value is directly sent to the store value (thanks to its name)
*
* @param element the UI element that gets the value and event listener.
* @param value if this one is defined
* @param name if this one is defined
*/
async function addSaverChecked(element, value, name){
element.checked = value;
element.addEventListener("change", async function(){
await window.api.setToStore(name, element.checked)
console.debug("Saved " + element.checked + " to store (from " + value + ")")
})
}
/**
* Sets the element to the value and adds a listener
* If the element changes, the value is directly sent to the store value (thanks to its name)
*
* @param element the UI element that gets the value and event listener.
* @param value if this one is defined
* @param name if this one is defined
*/
async function addSaverValue(element, value, name){
element.value = value;
element.addEventListener("change", async function(){
await window.api.setToStore(name, element.value)
console.debug("Saved " + element.value + " to store (from " + value + ")")
})
}
/**
* Saves and brings you back to index.html when you click on the retrn button (adds listener)
*/
async function returnButton(){
var set = document.getElementById("return");
set.onclick= async function(){
await saveSettings()
window.api.send("settingsC");
if (!fromQuran){
window.location.assign("../main/index.html");
}
else{
window.location.assign("../quran/quran.html");
}
}
}
/**
* Saves and brings you back to index.html when you press the ESC key
*/
async function setKeyPress(){
document.addEventListener('keydown', async function(key){
console.debug("Pressed the: " + key.key + " key")
if (key.key == "Escape"){
await saveSettings()
window.api.send("settingsC");
window.location.assign("../main/index.html");
}
})
}
/**
* Makes you unable to type out of bounds for the lat and lon fields
*/
function setNumberLimit(){
var latInput = document.getElementById("latInput");
var latOldValue = latInput.value;
latInput.addEventListener("focus", function(){
latOldValue = latInput.value;
});
latInput.addEventListener("input", function(){
if (latInput.value > 90 || latInput.value < -90 || latInput.value.length > 7){
latInput.value = latOldValue;
}
else{
latOldValue = latInput.value;
}
});
var lonInput = document.getElementById("lonInput");
var lonOldValue = lonInput.value;
lonInput.addEventListener("focus", function(){
lonOldValue = lonInput.value;
});
lonInput.addEventListener("input", function(){
if (lonInput.value > 180 || lonInput.value < -180 || lonInput.value.length > 7){
lonInput.value = lonOldValue;
}
else{
lonOldValue = lonInput.value;
}
});
}
/**
* Loads all timezones, and adds them to the TZ list
*/
async function setTZlist(){
const container = document.getElementById("tzlist");
response = await fetch('../../ressources/timezones.json')
.then(res => res.json())
.then((json) => {
for (zone of json){
for (timezone of zone["utc"]){
var option = document.createElement("option")
option.id = timezone;
option.value = timezone;
option.innerText = timezone;
container.appendChild(option)
}
}
sortSelect(container)
})
}
function sortSelect(selElem) { //Source https://stackoverflow.com/questions/278089/javascript-to-sort-contents-of-select-element
var tmpAry = new Array();
for (var i=0;i<selElem.options.length;i++) {
tmpAry[i] = new Array();
tmpAry[i][0] = selElem.options[i].text;
tmpAry[i][1] = selElem.options[i].value;
}
tmpAry.sort();
while (selElem.options.length > 0) {
selElem.options[0] = null;
}
for (var i=0;i<tmpAry.length;i++) {
var op = new Option(tmpAry[i][0], tmpAry[i][1]);
selElem.options[i] = op;
}
return;
}
/**
* Goes through the form/list and selects the val
*/
function selectFromList(list, val){
list.childNodes.forEach(function(node){
if (node.value == val){
node.selected = true;
}
});
}
//Looks at the checkboxes/radios and sets the variables according to the formats
async function saveTimeDateFormat(){
if (document.getElementById("24hTimeFormat").checked){
timeDisplay.clockFormat = 24;
}
else {
timeDisplay.clockFormat = 12;
}
if (document.getElementById("dateFormat1").checked){
timeDisplay.dateFormat = "DD/MM/YYYY"
}
else if (document.getElementById("dateFormat2").checked){
timeDisplay.dateFormat = "MM/DD/YYYY"
}
else{
timeDisplay.dateFormat = "YYYY/MM/DD"
}
await window.api.setToStore('timeDisplay.clockFormat', timeDisplay.clockFormat);
await window.api.setToStore('timeDisplay.dateFormat', timeDisplay.dateFormat);
}
/**
* From the loaded values, checks and unchecks the time/formats checkboxes
*/
function setTimeDateFormat(){
if (timeDisplay != undefined){
document.getElementById("showSeconds").checked = timeDisplay.showSeconds;
if (timeDisplay.clockFormat == 24){
document.getElementById("24hTimeFormat").checked = true;
} else{
document.getElementById("12hTimeFormat").checked = true;
}
if(timeDisplay.dateFormat == "DD/MM/YYYY"){
document.getElementById("dateFormat1").checked = true;
}else if ( dateFormat == "MM/DD/YYYY"){
document.getElementById("dateFormat2").checked = true;
}else{
document.getElementById("dateFormat3").checked = true;
}
}
}
/**
* If val is defined, change the value of the element to @val
*
* @param element the element gets the value
* @param val if this one is defined
*/
function setSavedVal(element, val){
if (val != undefined){
element.value = val;
}
}
/**
* Loads the custom/preset Adhan (bool, source path)
*/
async function loadAdhan(){
adhanFile = await window.api.getFromStore('adhanFile', [false, "ressources/audio/Adhan - Mecca.mp3", true]);
var customCheck = document.getElementById("customAdhan");
customCheck.checked = adhanFile[0]
var duaCheck = document.getElementById("duaCheck");
duaCheck.checked = adhanFile[2]
if (!customCheck.checked){
selectFromList(document.getElementById("adhanList"), adhanFile[1])
}
setUpAdhan()
customCheck.addEventListener('change', function(){
setUpAdhan()
})
document.getElementById("customAdhanFile").addEventListener("change", function(){
var file = document.getElementById("customAdhanFile").files[0].path;
console.debug("Loaded: " + file)
})
function setUpAdhan(){ //changes the states of the forms if custom adhans are enabled/disabled
if (!customCheck.checked){
document.getElementById("customAdhanFile").disabled = true;
document.getElementById("adhanList").disabled = false;
}
else{
document.getElementById("customAdhanFile").disabled = false;
document.getElementById("adhanList").disabled = true;
}
}
}
/**
* Adds listeners to the many Adhan checkboxes and disables them when they are not used
*/
function disableAdhanListener(){
var adhanCheck = document.getElementById("adhanCheck")
disableAdhan(adhanCheck)
adhanCheck.addEventListener('change', function(){
disableAdhan(adhanCheck)
})
function disableAdhan(adhanCheck){
if (!adhanCheck.checked){
document.getElementById("customAdhanFile").disabled = true;
document.getElementById("adhanList").disabled = true;
document.getElementById("duaCheck").disabled = true;
document.getElementById("customAdhan").disabled = true;
}
else{
document.getElementById("duaCheck").disabled = false;
document.getElementById("customAdhan").disabled = false;
if (!document.getElementById("customAdhan").checked){
document.getElementById("customAdhanFile").disabled = true;
document.getElementById("adhanList").disabled = false;
}
else{
document.getElementById("customAdhanFile").disabled = false;
document.getElementById("adhanList").disabled = true;
}
}
}
}
/**
* Saves Adhan values (bool, source)
*/
async function saveAdhan(){
var customCheck = document.getElementById("customAdhan");
var duaCheck = document.getElementById("duaCheck");
var path;
if (!customCheck.checked){
path = document.getElementById("adhanList").value;
}
else {
var file = document.getElementById("customAdhanFile").files
if (file != undefined && file.length != 0){
path = file[0].path;
}
else{
path = adhanFile[1]
}
}
adhanFile = [customCheck.checked, path, duaCheck.checked]
await window.api.setToStore('adhanFile', adhanFile)
}
/**
* Event listener in case the darkmode check changes
*/
function addThemeListener(){
var darkTheme = document.getElementById("darkModeCheck")
darkTheme.addEventListener('change', function(){
window.api.setTheme(darkTheme.checked, "settings.css");
})
}
/**
* Reloads the stylesheet if @param darkmode changes
*
* @param darkMode bool: if darkmode is enabled => true
*/
function toggleDarkMode(darkMode){
var head = document.getElementsByTagName('HEAD')[0];
var link = document.createElement('link');
link.rel = 'stylesheet';
link.type = 'text/css';
if (darkMode){
link.href = '../../node_modules/bootstrap-dark-5/dist/css/bootstrap-dark.css';
//document.body.style.backgroundColor = "#0b5345 "
}
else{
link.href = '../../node_modules/bootstrap/dist/css/bootstrap.css';
//document.body.style.backgroundColor = "#85929e"
}
head.appendChild(link);
}
/**
* Loads the background values (bool, source), and if true, sets the background + an event listener
*/
async function loadBgImage(){
bgImage = await window.api.getFromStore('bgImage', [true, '../../ressources/images/bgImage.jpg']);
var bgImageCheck = document.getElementById("bgImageCheck")
bgImageCheck.checked = bgImage[0];
var file = document.getElementById("customBgImage")
if (!bgImageCheck.checked ){
file.disabled = true;
}
bgImageCheck.addEventListener('change', function(){
if (!bgImageCheck.checked ){
file.disabled = true;
}
else{
file.disabled = false;
}
})
}
/**
* Saves the background image values (bool, source)
*/
async function saveBgImage(){
var bgImageCheck = document.getElementById("bgImageCheck").checked
if (!bgImageCheck){
await window.api.setToStore('bgImage', [false, '../../ressources/images/bgImage.jpg'])
}
else{
var file = document.getElementById("customBgImage").files
if (file != undefined && file.length != 0){
await window.api.setToStore('bgImage', [true, file[0].path])
}
else{
let darkmode = document.getElementById("darkModeCheck").checked
if (darkmode && bgImage[1] == '../../ressources/images/bgImage.jpg'){
await window.api.setToStore('bgImage', [true, '../../ressources/images/bgImage_dark.avif'])
}
else if (!darkmode && bgImage[1] == '../../ressources/images/bgImage_dark.avif'){
await window.api.setToStore('bgImage', [true, '../../ressources/images/bgImage.jpg'])
}
else{
await window.api.setToStore('bgImage', [true, bgImage[1]])
}
}
}
}
function addLangListener(){
var langList = document.getElementById('langlist')
langList.addEventListener('change', function(){
loadLanguage(langList.value)
})
}
/**
* Changes the innerTexts of all the elements in the page
*
* @param lang the language selected by the user
*/
function loadLanguage(lang){
document.getElementById("v-pills-general-tab").innerHTML = '<i class="fa-solid fa-kaaba"></i> ' + window.api.getLanguage(lang, "general");
document.getElementById("v-pills-location-tab").innerHTML = '<i class="fa-solid fa-location-dot"></i> ' + window.api.getLanguage(lang, "location");
document.getElementById("v-pills-audio-tab").innerHTML = '<i class="fa-solid fa-volume-high"></i> ' + window.api.getLanguage(lang, "audio");
document.getElementById("v-pills-appearance-tab").innerHTML = '<i class="fa-solid fa-palette"></i> ' + window.api.getLanguage(lang, "appearance");
document.getElementById("v-pills-advanced-tab").innerHTML = '<i class="fa-solid fa-sliders"></i> ' + window.api.getLanguage(lang, "advanced");
document.getElementById("v-pills-adjustments-tab").innerHTML = '<i class="fa-solid fa-clock"></i> ' + window.api.getLanguage(lang, "adjustements");
document.getElementById("v-pills-quran-tab").innerHTML = '<i class="fa-solid fa-book-quran"></i> ' + window.api.getLanguage(lang, "quran");
document.getElementById("return").innerHTML = '<i class="fa fa-arrow-circle-left"></i> ' + window.api.getLanguage(lang, "return");
document.getElementById("langText").innerText = window.api.getLanguage(lang, "language");
document.getElementById("settingsTitle").innerText = window.api.getLanguage(lang, "settings");
document.getElementById("tfText").innerText = window.api.getLanguage(lang, "timeDisplay");
document.getElementById("24hTimeFormatText").innerText = window.api.getLanguage(lang, "24hour");
document.getElementById("12hTimeFormatText").innerText = window.api.getLanguage(lang, "12hour");
document.getElementById("showSecondsText").innerText = window.api.getLanguage(lang, "showSseconds");
document.getElementById("dfText").innerText = window.api.getLanguage(lang, "dateFormat");
document.getElementById("df1Text").innerText = window.api.getLanguage(lang, "dateFormat1");
document.getElementById("df2Text").innerText = window.api.getLanguage(lang, "dateFormat2");
document.getElementById("df3Text").innerText = window.api.getLanguage(lang, "dateFormat3");
document.getElementById("notifText").innerText = window.api.getLanguage(lang, "notifications");
document.getElementById("notifCheckText").innerText = window.api.getLanguage(lang, "notifCheck");
document.getElementById("coordinatesText").innerText = window.api.getLanguage(lang, "coordinates");
document.getElementById("latText").innerText = window.api.getLanguage(lang, "latitude");
document.getElementById("lonText").innerText = window.api.getLanguage(lang, "longitude");
document.getElementById("tzText").innerText = window.api.getLanguage(lang, "timezone");
document.getElementById("adhanText").innerText = window.api.getLanguage(lang, "adhan");
document.getElementById("adhanCheckText").innerText = window.api.getLanguage(lang, "adhanCheck");
//document.getElementById("adhanMeccaText").innerText = window.api.getLanguage(lang, "adhanMecca");
//document.getElementById("adhanAqsaText").innerText = window.api.getLanguage(lang, "adhanAqsa");
document.getElementById("customAdhanText").innerText = window.api.getLanguage(lang, "customAdhan");
document.getElementById("duaAfterText").innerText = window.api.getLanguage(lang, "duaAfterAdhan");
document.getElementById("themeText").innerText = window.api.getLanguage(lang, "theme");
document.getElementById("darkModeText").innerText = window.api.getLanguage(lang, "darkMode");
document.getElementById("bgImageText").innerText = window.api.getLanguage(lang, "bgImage");
document.getElementById("bgImageCheckText").innerText = window.api.getLanguage(lang, "bgImageCheck");
document.getElementById("calcMethodsText").innerText = window.api.getLanguage(lang, "calcMethods");
document.getElementById("MWL").innerText = window.api.getLanguage(lang, "mwl");
document.getElementById("Egyptian").innerText = window.api.getLanguage(lang, "egyptian");
document.getElementById("Karachi").innerText = window.api.getLanguage(lang, "karachi");
document.getElementById("UAQ").innerText = window.api.getLanguage(lang, "uaq");
document.getElementById("Dubai").innerText = window.api.getLanguage(lang, "dubai");
document.getElementById("Qatar").innerText = window.api.getLanguage(lang, "qatar");
document.getElementById("Kuwait").innerText = window.api.getLanguage(lang, "kuwait");
document.getElementById("MC").innerText = window.api.getLanguage(lang, "mc");
document.getElementById("Singapore").innerText = window.api.getLanguage(lang, "singapore");
document.getElementById("Turkey").innerText = window.api.getLanguage(lang, "turkey");
document.getElementById("Tehran").innerText = window.api.getLanguage(lang, "tehran");
document.getElementById("ISNA").innerText = window.api.getLanguage(lang, "isna");
document.getElementById("MadhabText").innerText = window.api.getLanguage(lang, "madhab");
document.getElementById("Shafi").innerText = window.api.getLanguage(lang, "shafi");
document.getElementById("Hanafi").innerText = window.api.getLanguage(lang, "hanafi");
document.getElementById("hlrText").innerText = window.api.getLanguage(lang, "hlr");
document.getElementById("MOTN").innerText = window.api.getLanguage(lang, "motn");
document.getElementById("SOTN").innerText = window.api.getLanguage(lang, "sotn");
document.getElementById("TA").innerText = window.api.getLanguage(lang, "ta");
document.getElementById("pcrText").innerText = window.api.getLanguage(lang, "pcr");
document.getElementById("CC").innerText = window.api.getLanguage(lang, "cc");
document.getElementById("CD").innerText = window.api.getLanguage(lang, "cd");
document.getElementById("UND").innerText = window.api.getLanguage(lang, "und");
document.getElementById("ShafaqText").innerText = window.api.getLanguage(lang, "shafaq");
document.getElementById("shafaqG").innerText = window.api.getLanguage(lang, "general");
document.getElementById("shafaqR").innerText = window.api.getLanguage(lang, "ahmer");
document.getElementById("shafaqW").innerText = window.api.getLanguage(lang, "abyad");
document.getElementById("autoStartText").innerText = window.api.getLanguage(lang, "autoStart");
document.getElementById("autoStartCheckText").innerText = window.api.getLanguage(lang, "startAtLaunch");
document.getElementById("quote").innerText = window.api.getLanguage(lang, "quote");
document.getElementById("source").innerText = window.api.getLanguage(lang, "source");
document.getElementById("startUpSoundText").innerText = window.api.getLanguage(lang, "startUpSound");
document.getElementById("startUpSoundText2").innerText = window.api.getLanguage(lang, "playSound");
document.getElementById("systrayText").innerText = window.api.getLanguage(lang, "sysTray");
document.getElementById("systrayCheckText").innerText = window.api.getLanguage(lang, "minToTray");
document.getElementById("customSettText").innerText = window.api.getLanguage(lang, "customSettings");
document.getElementById("enableCalcText").innerText = window.api.getLanguage(lang, "enableCS");
document.getElementById("fajrAngleText").innerText = window.api.getLanguage(lang, "fAngle");
document.getElementById("maghribAngleText").innerText = window.api.getLanguage(lang, "mAngle");
document.getElementById("ishaAngleText").innerText = window.api.getLanguage(lang, "iAngle");
document.getElementById("delayText").innerText = window.api.getLanguage(lang, "delayAfterM");
document.getElementById("delayFormText").innerText = window.api.getLanguage(lang, "delayMin");
document.getElementById("France12").innerText = window.api.getLanguage(lang, "france") + " 12";
document.getElementById("France15").innerText = window.api.getLanguage(lang, "france") + " 15";
document.getElementById("France18").innerText = window.api.getLanguage(lang, "france") + " 18";
document.getElementById("Russia").innerText = window.api.getLanguage(lang, "russia");
document.getElementById("Gulf").innerText = window.api.getLanguage(lang, "gulf");
document.getElementById("adjustmentsText").innerHTML = window.api.getLanguage(lang, "adjustements");
document.getElementById("adjCheckText").innerText = window.api.getLanguage(lang, "enableAdj");
document.getElementById("fajrAdjText").innerText = window.api.getLanguage(lang, "fajrAdj");
document.getElementById("dhuhrAdjText").innerText = window.api.getLanguage(lang, "dhuhrAdj");
document.getElementById("asrAdjText").innerText = window.api.getLanguage(lang, "asrAdj");
document.getElementById("maghribAdjText").innerText = window.api.getLanguage(lang, "maghribAdj");
document.getElementById("ishaAdjText").innerText = window.api.getLanguage(lang, "ishaAdj");
document.getElementById("sunnahTimesText").innerText = window.api.getLanguage(lang, "showSunnah");
document.getElementById("MOTNCheckText").innerText = window.api.getLanguage(lang, "motn");
document.getElementById("TOTNCheckText").innerText = window.api.getLanguage(lang, "totn");
document.getElementById("minStartCheckText").innerText = window.api.getLanguage(lang, "minStart");
document.getElementById("adhanMecca").innerHTML = window.api.getLanguage(lang, "AdhanMecca");
document.getElementById("adhanAqsa").innerHTML = window.api.getLanguage(lang, "adhanAqsa");
document.getElementById("quranFontText").innerHTML = window.api.getLanguage(lang, "font");
document.getElementById("quranFontSizeText").innerHTML = window.api.getLanguage(lang, "fontsize");
document.getElementById("translationText").innerHTML = window.api.getLanguage(lang, "translation");
document.getElementById("showTranslationCheckText").innerHTML = window.api.getLanguage(lang, "showTrans");
document.getElementById("quranLangCheckText").innerHTML = window.api.getLanguage(lang, "diffLang");
document.getElementById("translationFontSizeText").innerHTML = window.api.getLanguage(lang, "fontsize");
document.getElementById("transliterationText").innerHTML = window.api.getLanguage(lang, "transliteration");
document.getElementById("showTransliterationCheckText").innerHTML = window.api.getLanguage(lang, "showTransliteration");
document.getElementById("transliterationFontSizeText").innerHTML = window.api.getLanguage(lang, "fontsize");
}
async function loadCustomSettings(){
var customValues = await window.api.getFromStore('customSettings', [false, 0,0,0]);
var customCheck = document.getElementById('customCalcCheck');
var fajrAngle = document.getElementById("fajrAngle");
var maghribAngle = document.getElementById("maghribAngle");
var ishaAngle = document.getElementById("ishaAngle");
var delay = await window.api.getFromStore('delay', [false, 0]);
var delayCheck = document.getElementById('delayCheck');
var delayForm = document.getElementById('delayForm');
if(customValues[0]){
customCheck.checked = true;
fajrAngle.value = customValues[1]
maghribAngle.value = customValues[2]
ishaAngle.value = customValues[3]
}
else{
fajrAngle.disabled = true
maghribAngle.disabled = true
ishaAngle.disabled = true
document.getElementById("calcMethodList").disabled = false;
}
customCheck.addEventListener('change', function(){
if (customCheck.checked){
fajrAngle.disabled = false
maghribAngle.disabled = false
ishaAngle.disabled = false
document.getElementById("calcMethodList").disabled = true;
}
else{
fajrAngle.disabled = true
maghribAngle.disabled = true
ishaAngle.disabled = true
document.getElementById("calcMethodList").disabled = false;
}
})
if (delay[0]){
delayCheck.checked = true
delayForm.value = delay[1]
}else{
delayForm.disabled = true;
}
delayCheck.addEventListener("change", function(){
if(delayCheck.checked){
delayForm.disabled = false;
}
else{
delayForm.disabled = true;
}
})
}
async function saveCustomSettings(){
var customCheck = document.getElementById('customCalcCheck');
var delayCheck = document.getElementById('delayCheck');
if(customCheck.checked){
var fajrAngle = document.getElementById("fajrAngle");
var maghribAngle = document.getElementById("maghribAngle");
var ishaAngle = document.getElementById("ishaAngle");
await window.api.setToStore('customSettings', [true, fajrAngle.value, maghribAngle.value, ishaAngle.value])
}
else{
await window.api.setToStore('customSettings', [false, 0,0,0])
}
if(delayCheck.checked){
var delayForm = document.getElementById('delayForm');
await window.api.setToStore('delay', [true, delayForm.value])
}
else{
await window.api.setToStore('delay', [false, 0])
}
}
//Loads the prayer times adjustements from the store and adds an event listener for the adjustements check box
async function loadAdjustments(){
var adjustements = await window.api.getFromStore('adj', [false, 0,0,0,0,0]);
for (let i = 1; i <= 5; i++){
if (adjustements[i] == undefined){
adjustements[i] = 0;
}
}
document.getElementById("adjCheck").checked = adjustements[0];
document.getElementById("fajrAdjInput").value = adjustements[1];
document.getElementById("dhuhrAdjInput").value = adjustements[2];
document.getElementById("asrAdjInput").value = adjustements[3];
document.getElementById("maghribAdjInput").value = adjustements[4];
document.getElementById("ishaAdjInput").value = adjustements[5];
enableAdjustements(document.getElementById("adjCheck").checked)
document.getElementById("adjCheck").addEventListener("change", function(){
enableAdjustements(document.getElementById("adjCheck").checked)
})
function enableAdjustements(boolean){
document.getElementById("fajrAdjInput").disabled = !boolean;
document.getElementById("dhuhrAdjInput").disabled = !boolean;
document.getElementById("asrAdjInput").disabled = !boolean;
document.getElementById("maghribAdjInput").disabled = !boolean;
document.getElementById("ishaAdjInput").disabled = !boolean;
}
}
//Rounds the adjustements (Math.round) and saves them to the store
async function saveAdjustments(){
var adjCheck = document.getElementById("adjCheck").checked;
var fajrAdj = document.getElementById("fajrAdjInput").value;
var dhuhrAdj = document.getElementById("dhuhrAdjInput").value;
var asrAdj = document.getElementById("asrAdjInput").value;
var maghribrAdj = document.getElementById("maghribAdjInput").value;
var ishaAdj = document.getElementById("ishaAdjInput").value;
var adjustements = [adjCheck, Math.round(fajrAdj),Math.round(dhuhrAdj),Math.round(asrAdj),Math.round(maghribrAdj),Math.round(ishaAdj)]
await window.api.setToStore('adj', adjustements);
}
/**
* Loads all elements related to the Quran reader
*/
async function loadQuranSettings(){
let quranFontsize = document.getElementById("quranFontSize")
let showTranslationDiv = document.getElementById("showTranslationCheck")
let diffLangDiv = document.getElementById("quranLangCheck")
let quranLangListDiv = document.getElementById("quranLangList")
let transListDiv = document.getElementById("translationList")
let transFontSizeDiv = document.getElementById("translationFontSize")
let showTransliterationDiv = document.getElementById("showTransliterationCheck")
let transliFontSizeDiv = document.getElementById("transliterationFontSize")
let quran = await window.api.getFromStore('quran', {
fontsize: 42,
translation:{
show: true,
lang: {
enabled: false,
lang: "en"
},
trans: 131,
fontsize: 14
},
transliteration:{
show: true,
fontsize: 14
},
})
loadTranslationList()
loadLanguageList()
quranFontsize.value = quran.fontsize;
showTranslationDiv.checked = quran.translation.show;
diffLangDiv.checked = quran.translation.lang.enabled;
transFontSizeDiv.value = quran.translation.fontsize;
showTransliterationDiv.checked = quran.transliteration.show;
transliFontSizeDiv.value = quran.transliteration.fontsize;
quranDisableListeners()
/**
* Loads the list of languages from a downloaded JSON and fill an inputList
*/
function loadLanguageList(){
fetch('../../ressources/quran/languages.json')
.then(reponse => reponse.json())
.then(json => {
for (transLang of json["languages"]){
var option = document.createElement("option")
option.dataset.language_name = (transLang["translated_name"]["name"]).toLowerCase()
option.value = transLang["iso_code"]
option.innerText = transLang["native_name"] != "" ? transLang["native_name"] : transLang["name"]
quranLangListDiv.appendChild(option)
}
loadDefaultLang()
})
quranLangListDiv.addEventListener("change", function(){
hideTranslations()
})
diffLangDiv.addEventListener("change", function(){
if (!diffLangDiv.checked){
loadDefaultLang()
}
})
document.getElementById("langlist").addEventListener("change",function(){
if (!diffLangDiv.checked){
console.log("YES")
loadDefaultLang()
}
})
}
/**
* This function is called when "Different languages from app" is disabled, it loads the default value (selected general) and changes it and the translation list
*/
function loadDefaultLang(language = undefined){
if (language != undefined){
if ((language.value == quran.translation.lang.lang && diffLangDiv.checked) ||
(!diffLangDiv.checked && language.value == document.getElementById("langlist").options[document.getElementById("langlist").selectedIndex].value))
language.selected = true;
hideTranslations()
}
else{
for (let language of quranLangListDiv){
if ((language.value == quran.translation.lang.lang && diffLangDiv.checked) ||
(!diffLangDiv.checked && language.value == document.getElementById("langlist").options[document.getElementById("langlist").selectedIndex].value))
language.selected = true;
hideTranslations()
}
}
}
/**
* Loads the list of translation from a downloaded JSON and fill an inputList
*/
function loadTranslationList(){
fetch('../../ressources/quran/translations.json')
.then(reponse => reponse.json())
.then(json => {
for (translation of json["translations"]){
var option = document.createElement("option")
option.dataset.lang = translation["language_name"]
option.value = translation["id"]
option.innerText = translation["name"]
if (option.value == quran.translation.trans) option.selected = true;
transListDiv.appendChild(option)
}
})
transListDiv.addEventListener("change", function(){
quran.translation.trans = transListDiv.options[transListDiv.selectedIndex].value;
console.log(quran.translation.trans)
})
}
/**
* Adds listeners to disable some inputs
*/
function quranDisableListeners(){
showTranslationDiv.addEventListener("change", function(){
diffLangDiv.disabled = !showTranslationDiv.checked;
quranLangListDiv.disabled = !showTranslationDiv.checked || !diffLangDiv.checked;
transListDiv.disabled = !showTranslationDiv.checked;
transFontSizeDiv.disabled = !showTranslationDiv.checked;
})
diffLangDiv.addEventListener("change", function(){
quranLangListDiv.disabled = !diffLangDiv.checked
})
diffLangDiv.disabled = !showTranslationDiv.checked;
quranLangListDiv.disabled = !showTranslationDiv.checked || !diffLangDiv.checked;
transListDiv.disabled = !showTranslationDiv.checked;
transFontSizeDiv.disabled = !showTranslationDiv.checked;
showTransliterationDiv.addEventListener("change", function(){
transliFontSizeDiv.disabled = !showTransliterationDiv.checked
})
transliFontSizeDiv.disabled = !showTransliterationDiv.checked
}
/**
* Checks the selected language and hides translations that are not in that language
*/
function hideTranslations(){
var selectedOne = false;
let selectedLang = quranLangListDiv.options[quranLangListDiv.selectedIndex].dataset.language_name
for (translation of transListDiv){
translation.dataset.lang == selectedLang ? translation.style.display = "block" : translation.style.display = "none"
if ((translation.value == quran.translation.trans || !selectedOne) && translation.style.display == "block"){
translation.selected = true;
selectedOne = true;
}
}
}
}
async function saveQuran(){
let quran = {
fontsize: document.getElementById("quranFontSize").value,
translation:{
show: document.getElementById("showTranslationCheck").checked,
lang: {
enabled: document.getElementById("quranLangCheck").checked,
lang: document.getElementById("quranLangList").options[document.getElementById("quranLangList").selectedIndex].value
},
trans: document.getElementById("translationList").options[document.getElementById("translationList").selectedIndex].value,
fontsize: document.getElementById("translationFontSize").value
},
transliteration:{
show: document.getElementById("showTransliterationCheck").checked,
fontsize: document.getElementById("transliterationFontSize").value
},
}
await window.api.setToStore("quran", quran)
}
function loadQueryString(){
const queryString = window.location.search;
if (queryString == "?page=quran"){
var quranTab = new bootstrap.Tab(document.getElementById("v-pills-quran-tab"))
quranTab.show();
fromQuran = true;
}
}

4516
yarn.lock Normal file

File diff suppressed because it is too large Load Diff