Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- <!DOCTYPE html>
- <html lang="en">
- <head>
- <meta charset="UTF-8" />
- <meta name="viewport" content="width=device-width, initial-scale=1" />
- <title>Pledge Reminder & Self-Control Tracker</title>
- <style>
- /* Base styles */
- body {
- font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
- background: linear-gradient(135deg, #FFDEE9, #B5FFFC);
- background-size: 200% 200%;
- animation: backgroundShift 10s ease infinite;
- color: #333;
- margin: 0;
- padding: 0 20px;
- transition: background-color 0.3s, color 0.3s;
- }
- @keyframes backgroundShift {
- 0% { background-position: 0% 50%; }
- 50% { background-position: 100% 50%; }
- 100% { background-position: 0% 50%; }
- }
- header {
- text-align: center;
- padding: 20px 0;
- background: rgba(255, 255, 255, 0.8);
- box-shadow: 0 4px 8px rgba(0,0,0,0.2);
- margin-bottom: 20px;
- border-bottom: 4px solid #FF6F61;
- }
- h1 {
- margin: 0;
- font-size: 2.8rem;
- color: #FF6F61;
- text-shadow: 0 2px 5px rgba(255,111,97,0.7);
- animation: pulse 1.5s ease-in-out infinite alternate;
- }
- @keyframes pulse {
- from {
- text-shadow: 0 2px 5px rgba(255,111,97,0.5);
- }
- to {
- text-shadow: 0 2px 10px rgba(255,111,97,1);
- }
- }
- .container {
- max-width: 900px;
- margin: 0 auto;
- background: #fff;
- padding: 20px;
- border-radius: 8px;
- box-shadow: 0 4px 12px rgba(0,0,0,0.1);
- }
- /* Two-column layout for main sections */
- .row {
- display: flex;
- flex-wrap: wrap;
- gap: 20px;
- }
- .column {
- flex: 1;
- min-width: 280px;
- }
- /* Form & list styling */
- form {
- margin-bottom: 20px;
- }
- form input, form button {
- padding: 10px;
- margin-right: 10px;
- font-size: 1rem;
- border: 1px solid #ccc;
- border-radius: 4px;
- transition: box-shadow 0.3s, border 0.3s;
- }
- form input {
- background: #f9f9f9;
- color: #333;
- }
- form input::placeholder {
- color: #999;
- }
- form button {
- background-color: #FF6F61;
- color: #fff;
- cursor: pointer;
- border: none;
- }
- form button:hover {
- background-color: #FF5A4D;
- }
- .site-list {
- list-style-type: none;
- padding: 0;
- }
- .site-item {
- display: flex;
- align-items: center;
- background: #f1f1f1;
- margin-bottom: 10px;
- padding: 10px;
- border-radius: 4px;
- box-shadow: 0 2px 5px rgba(0,0,0,0.1);
- transition: transform 0.2s, background 0.2s;
- }
- .site-item:hover {
- transform: scale(1.02);
- background: #f9f9f9;
- }
- .site-item img {
- width: 32px;
- height: 32px;
- margin-right: 15px;
- }
- .site-details {
- flex-grow: 1;
- }
- .site-link {
- color: #FF6F61;
- text-decoration: none;
- font-weight: bold;
- }
- .site-link:hover {
- text-decoration: underline;
- }
- .site-actions button {
- padding: 5px 10px;
- font-size: 0.9rem;
- margin-left: 5px;
- border: none;
- border-radius: 4px;
- cursor: pointer;
- background-color: #FF6F61;
- color: #fff;
- transition: background-color 0.3s;
- }
- .site-actions button:hover {
- background-color: #FF5A4D;
- }
- /* Dark mode styling */
- .dark-mode {
- background-color: #333;
- color: #ddd;
- }
- .dark-mode header {
- background: rgba(50, 50, 50, 0.9);
- border-bottom: 4px solid #FF6F61;
- }
- .dark-mode h1 {
- color: #FF6F61;
- }
- .dark-mode .container {
- background: #444;
- box-shadow: 0 4px 12px rgba(0,0,0,0.4);
- }
- .dark-mode form input {
- background: #555;
- color: #ddd;
- border: 1px solid #666;
- }
- .dark-mode form input::placeholder {
- color: #bbb;
- }
- .dark-mode .site-item {
- background: #555;
- box-shadow: 0 2px 5px rgba(0,0,0,0.3);
- }
- .dark-mode .site-link {
- color: #FF8A80;
- }
- .dark-mode .site-actions button {
- background-color: #FF8A80;
- }
- .dark-mode .site-actions button:hover {
- background-color: #FF6F61;
- }
- .customization {
- margin-top: 20px;
- border-top: 1px solid #ccc;
- padding-top: 20px;
- }
- .customization label {
- margin-right: 10px;
- }
- .customization input, .customization select, .customization button {
- padding: 8px;
- margin-right: 10px;
- font-size: 1rem;
- border: 1px solid #ccc;
- border-radius: 4px;
- background: #f9f9f9;
- color: #333;
- transition: box-shadow 0.3s;
- }
- .customization button {
- background-color: #FF6F61;
- color: #fff;
- cursor: pointer;
- border: none;
- }
- .customization button:hover {
- background-color: #FF5A4D;
- }
- .ranking {
- margin-top: 20px;
- padding: 15px;
- background: #f9f9f9;
- border-radius: 4px;
- box-shadow: 0 2px 5px rgba(0,0,0,0.1);
- }
- .ranking h2 {
- margin-top: 0;
- color: #FF6F61;
- }
- </style>
- </head>
- <body>
- <header>
- <h1>Pledge Reminder & Self-Control Tracker</h1>
- <button id="toggle-dark-mode">Toggle Dark Mode</button>
- </header>
- <div class="container">
- <div class="row">
- <!-- Left Column: Pledge & Form -->
- <div class="column">
- <!-- Pledge Reminder Message -->
- <section id="pledge-message">
- <p><strong>Remember your commitment!</strong> You pledged to avoid distracting sites for a set period.</p>
- </section>
- <!-- Form to add a restricted site with customizable time span -->
- <section id="site-input">
- <form id="add-site-form">
- <input type="url" id="site-url" placeholder="Enter site URL (https://example.com)" required />
- <input type="number" id="duration" placeholder="Duration in hours" required min="0" step="0.1"/>
- <button type="submit">Add Site</button>
- </form>
- </section>
- <!-- List of restricted sites -->
- <section id="site-list-section">
- <ul class="site-list" id="site-list"></ul>
- </section>
- </div>
- <!-- Right Column: Ranking & Customization -->
- <div class="column">
- <!-- Ranking & Encouragement -->
- <section class="ranking">
- <h2>Your Self-Control Rank:</h2>
- <p id="rank-status">Beginner</p>
- <p>Total Restricted Time Completed: <span id="total-time">0</span> seconds</p>
- </section>
- <!-- Customization Options -->
- <section class="customization">
- <h2>Customize Your Page</h2>
- <div>
- <label for="bg-color">Background Color:</label>
- <input type="color" id="bg-color" value="#ffffff" />
- </div>
- <div>
- <label for="text-color">Text Color:</label>
- <input type="color" id="text-color" value="#333333" />
- </div>
- <div>
- <label for="font-select">Font Family:</label>
- <select id="font-select">
- <option value="'Segoe UI', Tahoma, Geneva, Verdana, sans-serif">Segoe UI</option>
- <option value="Arial, sans-serif">Arial</option>
- <option value="'Courier New', monospace">Courier New</option>
- </select>
- </div>
- <button id="apply-customization">Apply Customization</button>
- </section>
- </div>
- </div>
- </div>
- <script>
- // Global variables to track sites and total restricted time completed
- let sites = [];
- let totalRestrictedTime = 0;
- let rankLevels = [
- { threshold: 0, rank: "Beginner" },
- { threshold: 3600, rank: "Novice" },
- { threshold: 7200, rank: "Intermediate" },
- { threshold: 14400, rank: "Advanced" },
- { threshold: 28800, rank: "Pro" },
- { threshold: 57600, rank: "Expert" },
- { threshold: 115200, rank: "Master" },
- { threshold: 230400, rank: "Guru" }
- ];
- // DOM elements
- const addSiteForm = document.getElementById("add-site-form");
- const siteUrlInput = document.getElementById("site-url");
- const durationInput = document.getElementById("duration");
- const siteList = document.getElementById("site-list");
- const rankStatus = document.getElementById("rank-status");
- const totalTimeEl = document.getElementById("total-time");
- const darkModeButton = document.getElementById("toggle-dark-mode");
- // Customization elements
- const bgColorInput = document.getElementById("bg-color");
- const textColorInput = document.getElementById("text-color");
- const fontSelect = document.getElementById("font-select");
- const applyCustomizationButton = document.getElementById("apply-customization");
- // Save sites and progress to Local Storage
- function saveData() {
- // Save only serializable properties (exclude interval)
- const sitesToSave = sites.map(site => ({
- id: site.id,
- url: site.url,
- duration: site.duration,
- startTime: site.startTime
- }));
- localStorage.setItem("restrictedSites", JSON.stringify(sitesToSave));
- localStorage.setItem("totalRestrictedTime", totalRestrictedTime);
- }
- // Load sites and progress from Local Storage
- function loadSites() {
- const savedSites = JSON.parse(localStorage.getItem("restrictedSites")) || [];
- totalRestrictedTime = parseInt(localStorage.getItem("totalRestrictedTime")) || 0;
- savedSites.forEach(saved => {
- // Create site object and resume its timer
- const siteObj = {
- id: saved.id,
- url: saved.url,
- duration: saved.duration,
- startTime: saved.startTime,
- interval: null
- };
- sites.push(siteObj);
- renderSite(siteObj);
- resumeTimer(siteObj);
- });
- updateRank();
- }
- // When a new site is added, set the startTime and save data
- addSiteForm.addEventListener("submit", function(e) {
- e.preventDefault();
- const url = siteUrlInput.value.trim();
- const durationHours = parseFloat(durationInput.value);
- const duration = Math.floor(durationHours * 3600);
- if (url && duration > 0) {
- addSite(url, duration);
- siteUrlInput.value = "";
- durationInput.value = "";
- }
- });
- function addSite(url, duration) {
- const id = Date.now();
- const siteObj = {
- id,
- url,
- duration,
- startTime: Date.now(), // Mark the time when the timer starts
- interval: null
- };
- sites.push(siteObj);
- renderSite(siteObj);
- startTimer(siteObj);
- saveData();
- }
- // Resume a site's timer based on elapsed time
- function resumeTimer(site) {
- const elapsed = Math.floor((Date.now() - site.startTime) / 1000);
- site.remaining = Math.max(0, site.duration - elapsed);
- updateTimerDisplay(site.id, site.remaining);
- if (site.remaining > 0) {
- startTimer(site);
- } else {
- // If timer is already up, add to total restricted time
- totalRestrictedTime += site.duration;
- updateRank();
- saveData();
- }
- }
- // Render a site item in the list
- function renderSite(site) {
- const li = document.createElement("li");
- li.classList.add("site-item");
- li.setAttribute("data-id", site.id);
- const faviconUrl = `https://www.google.com/s2/favicons?domain=${(new URL(site.url)).hostname}`;
- li.innerHTML = `
- <img src="${faviconUrl}" alt="Logo">
- <div class="site-details">
- <a href="${site.url}" target="_blank" class="site-link">${site.url}</a>
- <div class="timer" id="timer-${site.id}">${formatTime(site.duration)}</div>
- </div>
- <div class="site-actions">
- <button onclick="removeSite(${site.id})">Remove</button>
- <button onclick="visitSite('${site.url}', ${site.id})">Visit</button>
- </div>
- `;
- siteList.appendChild(li);
- }
- // Start or resume the countdown timer
- function startTimer(site) {
- // Initialize remaining if not already set
- if (typeof site.remaining === 'undefined') {
- site.remaining = site.duration;
- }
- site.interval = setInterval(() => {
- if (site.remaining > 0) {
- site.remaining--;
- updateTimerDisplay(site.id, site.remaining);
- saveData();
- } else {
- clearInterval(site.interval);
- totalRestrictedTime += site.duration;
- updateRank();
- saveData();
- }
- }, 1000);
- }
- function updateTimerDisplay(id, time) {
- const timerEl = document.getElementById("timer-" + id);
- if (timerEl) {
- timerEl.textContent = formatTime(time);
- }
- }
- // Format seconds to hh:mm:ss
- function formatTime(seconds) {
- const hrs = Math.floor(seconds / 3600);
- const mins = Math.floor((seconds % 3600) / 60);
- const secs = seconds % 60;
- return `${pad(hrs)}:${pad(mins)}:${pad(secs)}`;
- }
- function pad(num) {
- return num.toString().padStart(2, "0");
- }
- // Remove a site and update data
- function removeSite(id) {
- sites = sites.filter(site => {
- if (site.id === id) {
- clearInterval(site.interval);
- return false;
- }
- return true;
- });
- const li = document.querySelector(`.site-item[data-id="${id}"]`);
- if (li) li.remove();
- saveData();
- }
- // Warning and redirect if timer is still active
- function visitSite(url, id) {
- const site = sites.find(s => s.id === id);
- if (site && site.remaining > 0) {
- alert(`Warning: You pledged to avoid ${url} for ${formatTime(site.duration)}. Timer remaining: ${formatTime(site.remaining)}.`);
- } else {
- window.open(url, "_blank");
- }
- }
- // Update the rank based on total restricted time completed
- function updateRank() {
- let currentRank = "Beginner";
- for (let i = rankLevels.length - 1; i >= 0; i--) {
- if (totalRestrictedTime >= rankLevels[i].threshold) {
- currentRank = rankLevels[i].rank;
- break;
- }
- }
- rankStatus.textContent = currentRank;
- totalTimeEl.textContent = totalRestrictedTime;
- }
- // Dark mode toggle
- darkModeButton.addEventListener("click", function() {
- document.body.classList.toggle("dark-mode");
- });
- // Customization options
- applyCustomizationButton.addEventListener("click", function() {
- document.body.style.backgroundColor = bgColorInput.value;
- document.body.style.color = textColorInput.value;
- document.body.style.fontFamily = fontSelect.value;
- });
- // Expose functions to global scope for inline onclick usage
- window.removeSite = removeSite;
- window.visitSite = visitSite;
- // Load saved sites on page load
- window.onload = loadSites;
- </script>
- </body>
- </html>
Advertisement
Add Comment
Please, Sign In to add comment