forked from xXx_M0mMy_xXx/JSTAR-Tab
Delete js directory
This commit is contained in:
parent
8f341d1c7e
commit
d09a09f90e
98
js/main.js
98
js/main.js
|
@ -1,98 +0,0 @@
|
||||||
// Update greeting based on time of day and user settings
|
|
||||||
function updateGreeting() {
|
|
||||||
const greeting = document.getElementById('greeting');
|
|
||||||
if (!greeting) return;
|
|
||||||
|
|
||||||
const hour = new Date().getHours();
|
|
||||||
const isAnonymous = Storage.get('anonymousMode') || false;
|
|
||||||
const userName = isAnonymous ?
|
|
||||||
(Storage.get('anonymousName') || anonymousNames.generate()) :
|
|
||||||
(Storage.get('userName') || 'Friend');
|
|
||||||
|
|
||||||
let timeGreeting = 'Hello';
|
|
||||||
if (hour >= 5 && hour < 12) timeGreeting = 'Good Morning';
|
|
||||||
else if (hour >= 12 && hour < 17) timeGreeting = 'Good Afternoon';
|
|
||||||
else if (hour >= 17 && hour < 20) timeGreeting = 'Good Evening';
|
|
||||||
else timeGreeting = 'Good Night';
|
|
||||||
|
|
||||||
greeting.textContent = `${timeGreeting}, ${userName}!`;
|
|
||||||
greeting.style.opacity = '0';
|
|
||||||
setTimeout(() => {
|
|
||||||
greeting.style.opacity = '1';
|
|
||||||
}, 100);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set up event listeners for modal interactions
|
|
||||||
function initModalHandlers() {
|
|
||||||
const modals = document.querySelectorAll('.modal');
|
|
||||||
|
|
||||||
modals.forEach(modal => {
|
|
||||||
modal.addEventListener('click', (e) => {
|
|
||||||
if (e.target === modal) {
|
|
||||||
closeModal(modal);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
const modalContent = modal.querySelector('.modal-content');
|
|
||||||
if (modalContent) {
|
|
||||||
modalContent.addEventListener('click', (e) => {
|
|
||||||
e.stopPropagation();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Open modal with animation
|
|
||||||
function openModal(modal) {
|
|
||||||
if (!modal) return;
|
|
||||||
modal.classList.remove('hidden');
|
|
||||||
requestAnimationFrame(() => {
|
|
||||||
modal.classList.add('active');
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Close modal with animation
|
|
||||||
function closeModal(modal) {
|
|
||||||
if (!modal) return;
|
|
||||||
modal.classList.remove('active');
|
|
||||||
setTimeout(() => {
|
|
||||||
modal.classList.add('hidden');
|
|
||||||
}, 300);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Initialize application
|
|
||||||
document.addEventListener('DOMContentLoaded', () => {
|
|
||||||
// Apply visibility settings
|
|
||||||
['greeting', 'search', 'shortcuts', 'addShortcut'].forEach(element => {
|
|
||||||
const isVisible = Storage.get(`show_${element}`);
|
|
||||||
if (isVisible === false) {
|
|
||||||
const elementNode = document.getElementById(element === 'search' ? 'search-container' : element);
|
|
||||||
if (elementNode) elementNode.style.display = 'none';
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Start onboarding or show main content
|
|
||||||
if (!Storage.get('onboardingComplete')) {
|
|
||||||
onboarding.start();
|
|
||||||
} else {
|
|
||||||
document.getElementById('main-content').classList.remove('hidden');
|
|
||||||
}
|
|
||||||
|
|
||||||
// Initialize features
|
|
||||||
search.init();
|
|
||||||
shortcuts.init();
|
|
||||||
settings.init();
|
|
||||||
initModalHandlers();
|
|
||||||
|
|
||||||
// Set up greeting
|
|
||||||
updateGreeting();
|
|
||||||
setInterval(updateGreeting, 60000);
|
|
||||||
|
|
||||||
// Settings button handler
|
|
||||||
const settingsButton = document.getElementById('settings-button');
|
|
||||||
const settingsModal = document.getElementById('settings-modal');
|
|
||||||
|
|
||||||
settingsButton.addEventListener('click', () => {
|
|
||||||
openModal(settingsModal);
|
|
||||||
});
|
|
||||||
});
|
|
|
@ -1,131 +0,0 @@
|
||||||
// Notification System Class
|
|
||||||
class NotificationSystem {
|
|
||||||
constructor() {
|
|
||||||
this.container = document.getElementById('notification-container');
|
|
||||||
this.notifications = new Map();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Display a new notification
|
|
||||||
show(message, type = 'info', duration = 3000) {
|
|
||||||
const id = Date.now().toString();
|
|
||||||
const notification = document.createElement('div');
|
|
||||||
notification.className = `notification notification-${type}`;
|
|
||||||
|
|
||||||
// Create notification elements
|
|
||||||
const icon = this.createIcon(type);
|
|
||||||
const content = this.createContent(message);
|
|
||||||
const closeBtn = this.createCloseButton(id);
|
|
||||||
const progress = this.createProgressBar(type);
|
|
||||||
|
|
||||||
// Assemble notification
|
|
||||||
notification.appendChild(icon);
|
|
||||||
notification.appendChild(content);
|
|
||||||
notification.appendChild(closeBtn);
|
|
||||||
notification.appendChild(progress);
|
|
||||||
|
|
||||||
this.container.appendChild(notification);
|
|
||||||
|
|
||||||
// Set removal timer
|
|
||||||
setTimeout(() => this.remove(id), duration);
|
|
||||||
|
|
||||||
// Store notification reference
|
|
||||||
this.notifications.set(id, {
|
|
||||||
element: notification,
|
|
||||||
duration
|
|
||||||
});
|
|
||||||
|
|
||||||
this.updateProgress(id);
|
|
||||||
|
|
||||||
return id;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Remove a notification
|
|
||||||
remove(id) {
|
|
||||||
const notification = this.notifications.get(id);
|
|
||||||
if (notification) {
|
|
||||||
notification.element.style.animation = 'slideOutRight 0.3s cubic-bezier(0.16, 1, 0.3, 1)';
|
|
||||||
setTimeout(() => {
|
|
||||||
notification.element.remove();
|
|
||||||
this.notifications.delete(id);
|
|
||||||
}, 300);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update progress bar
|
|
||||||
updateProgress(id) {
|
|
||||||
const notification = this.notifications.get(id);
|
|
||||||
if (notification) {
|
|
||||||
const progress = notification.element.querySelector('.notification-progress');
|
|
||||||
const startTime = Date.now();
|
|
||||||
|
|
||||||
const update = () => {
|
|
||||||
const elapsed = Date.now() - startTime;
|
|
||||||
const percent = 100 - (elapsed / notification.duration * 100);
|
|
||||||
|
|
||||||
if (percent > 0) {
|
|
||||||
progress.style.width = `${percent}%`;
|
|
||||||
requestAnimationFrame(update);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
requestAnimationFrame(update);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Helper methods for creating notification elements
|
|
||||||
createIcon(type) {
|
|
||||||
const icon = document.createElement('i');
|
|
||||||
switch(type) {
|
|
||||||
case 'success':
|
|
||||||
icon.className = 'fas fa-check-circle';
|
|
||||||
icon.style.color = 'var(--success-color, #4caf50)';
|
|
||||||
break;
|
|
||||||
case 'error':
|
|
||||||
icon.className = 'fas fa-times-circle';
|
|
||||||
icon.style.color = 'var(--error-color, #f44336)';
|
|
||||||
break;
|
|
||||||
case 'info':
|
|
||||||
default:
|
|
||||||
icon.className = 'fas fa-info-circle';
|
|
||||||
icon.style.color = 'var(--info-color, #2196f3)';
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
return icon;
|
|
||||||
}
|
|
||||||
|
|
||||||
createContent(message) {
|
|
||||||
const content = document.createElement('div');
|
|
||||||
content.className = 'notification-content';
|
|
||||||
content.textContent = message;
|
|
||||||
return content;
|
|
||||||
}
|
|
||||||
|
|
||||||
createCloseButton(id) {
|
|
||||||
const closeBtn = document.createElement('button');
|
|
||||||
closeBtn.className = 'notification-close';
|
|
||||||
closeBtn.innerHTML = '<i class="fas fa-times"></i>';
|
|
||||||
closeBtn.onclick = () => this.remove(id);
|
|
||||||
return closeBtn;
|
|
||||||
}
|
|
||||||
|
|
||||||
createProgressBar(type) {
|
|
||||||
const progress = document.createElement('div');
|
|
||||||
progress.className = 'notification-progress';
|
|
||||||
switch(type) {
|
|
||||||
case 'success':
|
|
||||||
progress.style.background = 'var(--success-color, #4caf50)';
|
|
||||||
break;
|
|
||||||
case 'error':
|
|
||||||
progress.style.background = 'var(--error-color, #f44336)';
|
|
||||||
break;
|
|
||||||
case 'info':
|
|
||||||
default:
|
|
||||||
progress.style.background = 'var(--info-color, #2196f3)';
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
return progress;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Initialize the notification system
|
|
||||||
const notifications = new NotificationSystem();
|
|
|
@ -1,73 +0,0 @@
|
||||||
// Onboarding module
|
|
||||||
const onboarding = {
|
|
||||||
// Check if onboarding is complete
|
|
||||||
isComplete: () => {
|
|
||||||
return Storage.get('onboardingComplete') === true;
|
|
||||||
},
|
|
||||||
|
|
||||||
// Start the onboarding process
|
|
||||||
start: () => {
|
|
||||||
const modal = document.getElementById('onboarding-modal');
|
|
||||||
const mainContent = document.getElementById('main-content');
|
|
||||||
|
|
||||||
if (!onboarding.isComplete()) {
|
|
||||||
modal.classList.remove('hidden');
|
|
||||||
modal.classList.add('active');
|
|
||||||
mainContent.classList.add('hidden');
|
|
||||||
|
|
||||||
document.getElementById('next-step-btn').addEventListener('click', () => onboarding.nextStep(1));
|
|
||||||
document.getElementById('complete-setup-btn').addEventListener('click', onboarding.complete);
|
|
||||||
|
|
||||||
// Set up search engine selection
|
|
||||||
const engines = document.querySelectorAll('.search-engine-option');
|
|
||||||
engines.forEach(engine => {
|
|
||||||
engine.addEventListener('click', () => {
|
|
||||||
engines.forEach(e => e.classList.remove('selected'));
|
|
||||||
engine.classList.add('selected');
|
|
||||||
});
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
modal.classList.add('hidden');
|
|
||||||
mainContent.classList.remove('hidden');
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
// Move to the next step in onboarding
|
|
||||||
nextStep: (currentStep) => {
|
|
||||||
const currentStepEl = document.querySelector(`[data-step="${currentStep}"]`);
|
|
||||||
const nextStepEl = document.querySelector(`[data-step="${currentStep + 1}"]`);
|
|
||||||
const name = document.getElementById('user-name').value.trim();
|
|
||||||
|
|
||||||
if (!name) {
|
|
||||||
notifications.show('Please enter your name!', 'error');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
Storage.set('userName', name);
|
|
||||||
|
|
||||||
currentStepEl.classList.add('hidden');
|
|
||||||
nextStepEl.classList.remove('hidden');
|
|
||||||
nextStepEl.classList.add('visible');
|
|
||||||
},
|
|
||||||
|
|
||||||
// Complete the onboarding process
|
|
||||||
complete: () => {
|
|
||||||
const selectedEngine = document.querySelector('.search-engine-option.selected');
|
|
||||||
if (!selectedEngine) {
|
|
||||||
notifications.show('Please select a search engine!', 'error');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const searchEngine = selectedEngine.dataset.engine;
|
|
||||||
Storage.set('searchEngine', searchEngine);
|
|
||||||
Storage.set('onboardingComplete', true);
|
|
||||||
|
|
||||||
const modal = document.getElementById('onboarding-modal');
|
|
||||||
const mainContent = document.getElementById('main-content');
|
|
||||||
|
|
||||||
modal.classList.add('hidden');
|
|
||||||
mainContent.classList.remove('hidden');
|
|
||||||
notifications.show('Welcome to your new tab! 👋', 'success');
|
|
||||||
updateGreeting();
|
|
||||||
}
|
|
||||||
};
|
|
47
js/search.js
47
js/search.js
|
@ -1,47 +0,0 @@
|
||||||
const search = {
|
|
||||||
// Supported search engines and their URLs
|
|
||||||
engines: {
|
|
||||||
google: 'https://www.google.com/search?q=',
|
|
||||||
bing: 'https://www.bing.com/search?q=',
|
|
||||||
duckduckgo: 'https://duckduckgo.com/?q=',
|
|
||||||
brave: 'https://search.brave.com/search?q=',
|
|
||||||
qwant: 'https://www.qwant.com/?q=',
|
|
||||||
searxng: 'https://searx.org/search?q='
|
|
||||||
},
|
|
||||||
|
|
||||||
// Perform search using selected engine
|
|
||||||
perform: () => {
|
|
||||||
const searchBar = document.getElementById('search-bar');
|
|
||||||
const query = searchBar.value.trim();
|
|
||||||
const engine = Storage.get('searchEngine') || 'google';
|
|
||||||
|
|
||||||
if (query) {
|
|
||||||
const searchUrl = search.engines[engine] + encodeURIComponent(query);
|
|
||||||
window.location.href = searchUrl;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
// Initialize search functionality
|
|
||||||
init: () => {
|
|
||||||
const searchBar = document.getElementById('search-bar');
|
|
||||||
const searchButton = document.getElementById('search-button');
|
|
||||||
|
|
||||||
searchBar.addEventListener('keypress', (e) => {
|
|
||||||
if (e.key === 'Enter') {
|
|
||||||
search.perform();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
searchButton.addEventListener('click', search.perform);
|
|
||||||
|
|
||||||
// Global keyboard shortcut to focus search bar
|
|
||||||
document.addEventListener('keydown', (e) => {
|
|
||||||
if (e.key === '/' &&
|
|
||||||
!['INPUT', 'TEXTAREA'].includes(document.activeElement.tagName) &&
|
|
||||||
window.getSelection().toString() === '') {
|
|
||||||
e.preventDefault();
|
|
||||||
searchBar.focus();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
336
js/settings.js
336
js/settings.js
|
@ -1,336 +0,0 @@
|
||||||
// Anonymous name generator
|
|
||||||
const anonymousNames = {
|
|
||||||
adjectives: ['Hidden', 'Secret', 'Mystery', 'Shadow', 'Unknown', 'Silent', 'Stealth', 'Phantom', 'Ghost', 'Anon'],
|
|
||||||
nouns: [' User', ' Visitor', ' Guest', ' Agent', ' Entity', ' Person', ' Browser', ' Explorer', ' Wanderer', ' Navigator'],
|
|
||||||
|
|
||||||
generate: function() {
|
|
||||||
const adjective = this.adjectives[Math.floor(Math.random() * this.adjectives.length)];
|
|
||||||
const noun = this.nouns[Math.floor(Math.random() * this.nouns.length)];
|
|
||||||
return `${adjective}${noun}`;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// Main settings object
|
|
||||||
const settings = {
|
|
||||||
// Toggle between light and dark themes
|
|
||||||
toggleTheme: () => {
|
|
||||||
const currentTheme = document.body.getAttribute('data-theme');
|
|
||||||
const newTheme = currentTheme === 'dark' ? 'light' : 'dark';
|
|
||||||
document.body.setAttribute('data-theme', newTheme);
|
|
||||||
Storage.set('theme', newTheme);
|
|
||||||
|
|
||||||
const themeIcon = document.querySelector('#toggle-theme i');
|
|
||||||
themeIcon.className = `fas fa-${newTheme === 'dark' ? 'sun' : 'moon'}`;
|
|
||||||
},
|
|
||||||
|
|
||||||
// Toggle anonymous mode
|
|
||||||
toggleAnonymousMode: () => {
|
|
||||||
const isAnonymous = Storage.get('anonymousMode') || false;
|
|
||||||
Storage.set('anonymousMode', !isAnonymous);
|
|
||||||
|
|
||||||
if (!isAnonymous) {
|
|
||||||
const randomName = anonymousNames.generate();
|
|
||||||
Storage.set('anonymousName', randomName);
|
|
||||||
notifications.show('Anonymous mode enabled!', 'info');
|
|
||||||
} else {
|
|
||||||
Storage.remove('anonymousName');
|
|
||||||
notifications.show('Anonymous mode disabled!', 'info');
|
|
||||||
}
|
|
||||||
|
|
||||||
shortcuts.render();
|
|
||||||
updateGreeting();
|
|
||||||
},
|
|
||||||
|
|
||||||
// Update the search engine
|
|
||||||
updateSearchEngine: (engine) => {
|
|
||||||
Storage.set('searchEngine', engine);
|
|
||||||
notifications.show('Search engine updated successfully!', 'success');
|
|
||||||
},
|
|
||||||
|
|
||||||
// Update visibility of UI elements
|
|
||||||
updateVisibility: () => {
|
|
||||||
const elements = {
|
|
||||||
greeting: {
|
|
||||||
id: 'greeting',
|
|
||||||
toggle: 'toggle-greeting',
|
|
||||||
functions: ['updateGreeting'],
|
|
||||||
name: 'Greeting'
|
|
||||||
},
|
|
||||||
search: {
|
|
||||||
id: 'search-container',
|
|
||||||
toggle: 'toggle-search',
|
|
||||||
functions: ['search.init', 'search.perform'],
|
|
||||||
name: 'Search bar'
|
|
||||||
},
|
|
||||||
shortcuts: {
|
|
||||||
id: 'shortcuts-grid',
|
|
||||||
toggle: 'toggle-shortcuts',
|
|
||||||
functions: ['shortcuts.init', 'shortcuts.render'],
|
|
||||||
name: 'Shortcuts'
|
|
||||||
},
|
|
||||||
addShortcut: {
|
|
||||||
id: 'add-shortcut',
|
|
||||||
toggle: 'toggle-add-shortcut',
|
|
||||||
functions: [],
|
|
||||||
name: 'Add shortcut button'
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
Object.entries(elements).forEach(([key, element]) => {
|
|
||||||
const isVisible = Storage.get(`show_${key}`);
|
|
||||||
if (isVisible === null) Storage.set(`show_${key}`, true);
|
|
||||||
|
|
||||||
const toggle = document.getElementById(element.toggle);
|
|
||||||
const elementNode = document.getElementById(element.id);
|
|
||||||
|
|
||||||
if (toggle && elementNode) {
|
|
||||||
toggle.checked = isVisible !== false;
|
|
||||||
if (isVisible === false) {
|
|
||||||
elementNode.style.visibility = 'hidden';
|
|
||||||
elementNode.style.opacity = '0';
|
|
||||||
elementNode.style.position = 'absolute';
|
|
||||||
elementNode.style.pointerEvents = 'none';
|
|
||||||
}
|
|
||||||
|
|
||||||
toggle.addEventListener('change', (e) => {
|
|
||||||
const isChecked = e.target.checked;
|
|
||||||
Storage.set(`show_${key}`, isChecked);
|
|
||||||
|
|
||||||
if (isChecked) {
|
|
||||||
elementNode.style.visibility = 'visible';
|
|
||||||
elementNode.style.opacity = '1';
|
|
||||||
elementNode.style.position = 'relative';
|
|
||||||
elementNode.style.pointerEvents = 'auto';
|
|
||||||
} else {
|
|
||||||
elementNode.style.visibility = 'hidden';
|
|
||||||
elementNode.style.opacity = '0';
|
|
||||||
elementNode.style.position = 'absolute';
|
|
||||||
elementNode.style.pointerEvents = 'none';
|
|
||||||
}
|
|
||||||
|
|
||||||
if (key === 'shortcuts') {
|
|
||||||
const addShortcutBtn = document.getElementById('add-shortcut');
|
|
||||||
const addShortcutVisible = Storage.get('show_addShortcut') !== false;
|
|
||||||
|
|
||||||
if (addShortcutBtn && !isChecked && !addShortcutVisible) {
|
|
||||||
addShortcutBtn.style.visibility = 'hidden';
|
|
||||||
addShortcutBtn.style.opacity = '0';
|
|
||||||
addShortcutBtn.style.position = 'absolute';
|
|
||||||
addShortcutBtn.style.pointerEvents = 'none';
|
|
||||||
} else if (addShortcutBtn && addShortcutVisible) {
|
|
||||||
addShortcutBtn.style.visibility = 'visible';
|
|
||||||
addShortcutBtn.style.opacity = '1';
|
|
||||||
addShortcutBtn.style.position = 'relative';
|
|
||||||
addShortcutBtn.style.pointerEvents = 'auto';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
notifications.show(
|
|
||||||
`${element.name} ${isChecked ? 'shown' : 'hidden'}!`,
|
|
||||||
isChecked ? 'success' : 'info'
|
|
||||||
);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
// Initialize settings
|
|
||||||
init: () => {
|
|
||||||
const settingsButton = document.getElementById('settings-button');
|
|
||||||
const settingsModal = document.getElementById('settings-modal');
|
|
||||||
const closeSettings = document.getElementById('close-settings');
|
|
||||||
|
|
||||||
settingsButton.addEventListener('click', (e) => {
|
|
||||||
e.stopPropagation();
|
|
||||||
const userName = Storage.get('userName') || '';
|
|
||||||
const isAnonymous = Storage.get('anonymousMode') || false;
|
|
||||||
const currentEngine = Storage.get('searchEngine') || 'google';
|
|
||||||
|
|
||||||
document.getElementById('settings-name').value = userName;
|
|
||||||
document.getElementById('toggle-anonymous').checked = isAnonymous;
|
|
||||||
document.getElementById('search-engine-select').value = currentEngine;
|
|
||||||
|
|
||||||
settingsModal.classList.remove('hidden');
|
|
||||||
settingsModal.classList.add('active');
|
|
||||||
});
|
|
||||||
|
|
||||||
closeSettings.addEventListener('click', () => {
|
|
||||||
settingsModal.classList.remove('active');
|
|
||||||
setTimeout(() => {
|
|
||||||
settingsModal.classList.add('hidden');
|
|
||||||
}, 300);
|
|
||||||
});
|
|
||||||
|
|
||||||
const themeToggle = document.getElementById('toggle-theme');
|
|
||||||
themeToggle.addEventListener('click', settings.toggleTheme);
|
|
||||||
|
|
||||||
const anonymousToggle = document.getElementById('toggle-anonymous');
|
|
||||||
anonymousToggle.addEventListener('change', settings.toggleAnonymousMode);
|
|
||||||
|
|
||||||
const searchEngineSelect = document.getElementById('search-engine-select');
|
|
||||||
searchEngineSelect.addEventListener('change', (e) => {
|
|
||||||
settings.updateSearchEngine(e.target.value);
|
|
||||||
});
|
|
||||||
|
|
||||||
const nameInput = document.getElementById('settings-name');
|
|
||||||
nameInput.addEventListener('change', (e) => {
|
|
||||||
const newName = e.target.value.trim();
|
|
||||||
if (newName) {
|
|
||||||
Storage.set('userName', newName);
|
|
||||||
updateGreeting();
|
|
||||||
notifications.show('Name updated successfully!', 'success');
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
const savedTheme = Storage.get('theme') || 'light';
|
|
||||||
document.body.setAttribute('data-theme', savedTheme);
|
|
||||||
const themeIcon = document.querySelector('#toggle-theme i');
|
|
||||||
themeIcon.className = `fas fa-${savedTheme === 'dark' ? 'sun' : 'moon'}`;
|
|
||||||
|
|
||||||
settings.initDataManagement();
|
|
||||||
settings.updateVisibility();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// Initialize data management
|
|
||||||
settings.initDataManagement = () => {
|
|
||||||
const exportBtn = document.getElementById('export-data');
|
|
||||||
const importBtn = document.getElementById('import-data');
|
|
||||||
const resetBtn = document.getElementById('reset-data');
|
|
||||||
const fileInput = document.getElementById('import-file');
|
|
||||||
|
|
||||||
// Export Data
|
|
||||||
exportBtn.addEventListener('click', () => {
|
|
||||||
try {
|
|
||||||
const data = {
|
|
||||||
settings: {
|
|
||||||
theme: Storage.get('theme'),
|
|
||||||
userName: Storage.get('userName'),
|
|
||||||
anonymousMode: Storage.get('anonymousMode'),
|
|
||||||
anonymousName: Storage.get('anonymousName'),
|
|
||||||
searchEngine: Storage.get('searchEngine'),
|
|
||||||
show_greeting: Storage.get('show_greeting'),
|
|
||||||
show_search: Storage.get('show_search'),
|
|
||||||
show_shortcuts: Storage.get('show_shortcuts'),
|
|
||||||
show_addShortcut: Storage.get('show_addShortcut')
|
|
||||||
},
|
|
||||||
shortcuts: Storage.get('shortcuts') || []
|
|
||||||
};
|
|
||||||
|
|
||||||
const blob = new Blob([JSON.stringify(data, null, 2)], { type: 'application/json' });
|
|
||||||
const url = URL.createObjectURL(blob);
|
|
||||||
const a = document.createElement('a');
|
|
||||||
a.href = url;
|
|
||||||
a.download = 'jstar-tab-backup.json';
|
|
||||||
document.body.appendChild(a);
|
|
||||||
a.click();
|
|
||||||
document.body.removeChild(a);
|
|
||||||
URL.revokeObjectURL(url);
|
|
||||||
|
|
||||||
notifications.show('Data exported successfully!', 'success');
|
|
||||||
} catch (error) {
|
|
||||||
notifications.show('Failed to export data!', 'error');
|
|
||||||
console.error('Export error:', error);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Import Data
|
|
||||||
importBtn.addEventListener('click', () => fileInput.click());
|
|
||||||
|
|
||||||
fileInput.addEventListener('change', (e) => {
|
|
||||||
const file = e.target.files[0];
|
|
||||||
if (!file) return;
|
|
||||||
|
|
||||||
const reader = new FileReader();
|
|
||||||
reader.onload = (event) => {
|
|
||||||
try {
|
|
||||||
const data = JSON.parse(event.target.result);
|
|
||||||
|
|
||||||
if (!data.shortcuts || !data.settings) {
|
|
||||||
throw new Error('Invalid backup file format');
|
|
||||||
}
|
|
||||||
|
|
||||||
Object.entries(data.settings).forEach(([key, value]) => {
|
|
||||||
Storage.set(key, value);
|
|
||||||
});
|
|
||||||
|
|
||||||
Storage.set('shortcuts', data.shortcuts);
|
|
||||||
|
|
||||||
fileInput.value = '';
|
|
||||||
|
|
||||||
['greeting', 'search', 'shortcuts', 'addShortcut'].forEach(element => {
|
|
||||||
const isVisible = data.settings[`show_${element}`];
|
|
||||||
const elementNode = document.getElementById(element === 'search' ? 'search-container' : element);
|
|
||||||
const toggle = document.getElementById(`toggle-${element}`);
|
|
||||||
|
|
||||||
if (elementNode && toggle) {
|
|
||||||
toggle.checked = isVisible !== false;
|
|
||||||
if (isVisible === false) {
|
|
||||||
elementNode.style.visibility = 'hidden';
|
|
||||||
elementNode.style.opacity = '0';
|
|
||||||
elementNode.style.position = 'absolute';
|
|
||||||
elementNode.style.pointerEvents = 'none';
|
|
||||||
} else {
|
|
||||||
elementNode.style.visibility = 'visible';
|
|
||||||
elementNode.style.opacity = '1';
|
|
||||||
elementNode.style.position = 'relative';
|
|
||||||
elementNode.style.pointerEvents = 'auto';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
const shortcutsVisible = data.settings.show_shortcuts !== false;
|
|
||||||
const addShortcutVisible = data.settings.show_addShortcut !== false;
|
|
||||||
const addShortcutBtn = document.getElementById('add-shortcut');
|
|
||||||
|
|
||||||
if (addShortcutBtn) {
|
|
||||||
if (!shortcutsVisible && !addShortcutVisible) {
|
|
||||||
addShortcutBtn.style.visibility = 'hidden';
|
|
||||||
addShortcutBtn.style.opacity = '0';
|
|
||||||
addShortcutBtn.style.position = 'absolute';
|
|
||||||
addShortcutBtn.style.pointerEvents = 'none';
|
|
||||||
} else if (addShortcutVisible) {
|
|
||||||
addShortcutBtn.style.visibility = 'visible';
|
|
||||||
addShortcutBtn.style.opacity = '1';
|
|
||||||
addShortcutBtn.style.position = 'relative';
|
|
||||||
addShortcutBtn.style.pointerEvents = 'auto';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
shortcuts.render();
|
|
||||||
updateGreeting();
|
|
||||||
document.body.setAttribute('data-theme', data.settings.theme || 'light');
|
|
||||||
|
|
||||||
notifications.show('Data imported successfully!', 'success');
|
|
||||||
} catch (error) {
|
|
||||||
notifications.show('Failed to import data: Invalid file format!', 'error');
|
|
||||||
console.error('Import error:', error);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
reader.onerror = () => {
|
|
||||||
notifications.show('Failed to read file!', 'error');
|
|
||||||
};
|
|
||||||
|
|
||||||
reader.readAsText(file);
|
|
||||||
});
|
|
||||||
|
|
||||||
// Reset Data
|
|
||||||
resetBtn.addEventListener('click', () => {
|
|
||||||
const confirmReset = confirm('Are you sure you want to reset all data? This action cannot be undone.');
|
|
||||||
|
|
||||||
if (confirmReset) {
|
|
||||||
try {
|
|
||||||
Storage.clear();
|
|
||||||
closeModal(document.getElementById('settings-modal'));
|
|
||||||
notifications.show('All data has been reset!', 'success');
|
|
||||||
setTimeout(() => {
|
|
||||||
window.location.reload();
|
|
||||||
}, 1000);
|
|
||||||
} catch (error) {
|
|
||||||
notifications.show('Failed to reset data!', 'error');
|
|
||||||
console.error('Reset error:', error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
};
|
|
255
js/shortcuts.js
255
js/shortcuts.js
|
@ -1,255 +0,0 @@
|
||||||
const shortcuts = {
|
|
||||||
MAX_SHORTCUTS: 12,
|
|
||||||
|
|
||||||
// Validate and format URL
|
|
||||||
validateAndFormatUrl: (url) => {
|
|
||||||
if (!/^https?:\/\//i.test(url)) {
|
|
||||||
url = 'https://' + url;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
new URL(url);
|
|
||||||
return url;
|
|
||||||
} catch (e) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
// Add new shortcut
|
|
||||||
add: (url, name) => {
|
|
||||||
const currentShortcuts = Storage.get('shortcuts') || [];
|
|
||||||
if (currentShortcuts.length >= shortcuts.MAX_SHORTCUTS) {
|
|
||||||
notifications.show('Maximum shortcuts limit reached!', 'error');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const formattedUrl = shortcuts.validateAndFormatUrl(url);
|
|
||||||
if (!formattedUrl) {
|
|
||||||
notifications.show('Invalid URL format!', 'error');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
currentShortcuts.push({ url: formattedUrl, name });
|
|
||||||
Storage.set('shortcuts', currentShortcuts);
|
|
||||||
shortcuts.render();
|
|
||||||
},
|
|
||||||
|
|
||||||
// Remove shortcut
|
|
||||||
remove: (index) => {
|
|
||||||
const currentShortcuts = Storage.get('shortcuts') || [];
|
|
||||||
currentShortcuts.splice(index, 1);
|
|
||||||
Storage.set('shortcuts', currentShortcuts);
|
|
||||||
shortcuts.render();
|
|
||||||
notifications.show('Shortcut removed!', 'success');
|
|
||||||
},
|
|
||||||
|
|
||||||
// Edit existing shortcut
|
|
||||||
edit: (index, newUrl, newName) => {
|
|
||||||
const currentShortcuts = Storage.get('shortcuts') || [];
|
|
||||||
currentShortcuts[index] = { url: newUrl, name: newName };
|
|
||||||
Storage.set('shortcuts', currentShortcuts);
|
|
||||||
shortcuts.render();
|
|
||||||
notifications.show('Shortcut updated!', 'success');
|
|
||||||
},
|
|
||||||
|
|
||||||
// Show context menu for shortcut
|
|
||||||
showContextMenu: (e, index) => {
|
|
||||||
e.preventDefault();
|
|
||||||
const menu = document.getElementById('context-menu');
|
|
||||||
const rect = e.target.getBoundingClientRect();
|
|
||||||
|
|
||||||
menu.style.top = `${e.clientY}px`;
|
|
||||||
menu.style.left = `${e.clientX}px`;
|
|
||||||
menu.classList.remove('hidden');
|
|
||||||
menu.dataset.shortcutIndex = index;
|
|
||||||
|
|
||||||
const handleClickOutside = (event) => {
|
|
||||||
if (!menu.contains(event.target)) {
|
|
||||||
menu.classList.add('hidden');
|
|
||||||
document.removeEventListener('click', handleClickOutside);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
setTimeout(() => {
|
|
||||||
document.addEventListener('click', handleClickOutside);
|
|
||||||
}, 0);
|
|
||||||
},
|
|
||||||
|
|
||||||
// Render shortcuts grid
|
|
||||||
render: () => {
|
|
||||||
const grid = document.getElementById('shortcuts-grid');
|
|
||||||
const currentShortcuts = Storage.get('shortcuts') || [];
|
|
||||||
const isAnonymous = Storage.get('anonymousMode') || false;
|
|
||||||
|
|
||||||
grid.innerHTML = '';
|
|
||||||
|
|
||||||
currentShortcuts.forEach((shortcut, index) => {
|
|
||||||
const element = document.createElement('div');
|
|
||||||
element.className = `shortcut ${isAnonymous ? 'blurred' : ''}`;
|
|
||||||
|
|
||||||
const icon = document.createElement('img');
|
|
||||||
icon.src = `https://www.google.com/s2/favicons?domain=${shortcut.url}&sz=64`;
|
|
||||||
icon.alt = shortcut.name;
|
|
||||||
|
|
||||||
const name = document.createElement('span');
|
|
||||||
name.textContent = shortcut.name;
|
|
||||||
|
|
||||||
element.appendChild(icon);
|
|
||||||
element.appendChild(name);
|
|
||||||
|
|
||||||
element.addEventListener('click', (e) => {
|
|
||||||
if (e.ctrlKey) {
|
|
||||||
window.open(shortcut.url, '_blank');
|
|
||||||
} else {
|
|
||||||
window.location.href = shortcut.url;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
element.addEventListener('contextmenu', (e) => {
|
|
||||||
e.preventDefault();
|
|
||||||
const menu = document.getElementById('context-menu');
|
|
||||||
|
|
||||||
menu.style.top = `${e.pageY}px`;
|
|
||||||
menu.style.left = `${e.pageX}px`;
|
|
||||||
menu.classList.remove('hidden');
|
|
||||||
menu.dataset.shortcutIndex = index;
|
|
||||||
|
|
||||||
const closeMenu = (event) => {
|
|
||||||
if (!menu.contains(event.target)) {
|
|
||||||
menu.classList.add('hidden');
|
|
||||||
document.removeEventListener('click', closeMenu);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
setTimeout(() => {
|
|
||||||
document.addEventListener('click', closeMenu);
|
|
||||||
}, 0);
|
|
||||||
});
|
|
||||||
|
|
||||||
grid.appendChild(element);
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
// Initialize shortcuts functionality
|
|
||||||
init: () => {
|
|
||||||
const addShortcutButton = document.getElementById('add-shortcut');
|
|
||||||
if (addShortcutButton) {
|
|
||||||
addShortcutButton.addEventListener('click', (e) => {
|
|
||||||
e.stopPropagation();
|
|
||||||
const currentShortcuts = Storage.get('shortcuts') || [];
|
|
||||||
|
|
||||||
if (currentShortcuts.length >= shortcuts.MAX_SHORTCUTS) {
|
|
||||||
notifications.show('Maximum shortcuts limit reached!', 'error');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const modal = document.getElementById('add-shortcut-modal');
|
|
||||||
if (modal) {
|
|
||||||
modal.classList.remove('hidden');
|
|
||||||
modal.classList.add('active');
|
|
||||||
|
|
||||||
const urlInput = document.getElementById('shortcut-url');
|
|
||||||
const nameInput = document.getElementById('shortcut-name');
|
|
||||||
|
|
||||||
const saveShortcutButton = document.getElementById('save-shortcut');
|
|
||||||
if (saveShortcutButton) {
|
|
||||||
saveShortcutButton.onclick = () => {
|
|
||||||
const url = urlInput.value.trim();
|
|
||||||
const name = nameInput.value.trim();
|
|
||||||
|
|
||||||
if (url && name) {
|
|
||||||
try {
|
|
||||||
new URL(url);
|
|
||||||
shortcuts.add(url, name);
|
|
||||||
modal.classList.remove('active');
|
|
||||||
setTimeout(() => {
|
|
||||||
modal.classList.add('hidden');
|
|
||||||
urlInput.value = '';
|
|
||||||
nameInput.value = '';
|
|
||||||
}, 300);
|
|
||||||
notifications.show('Shortcut added successfully!', 'success');
|
|
||||||
} catch (e) {
|
|
||||||
notifications.show('Invalid URL format!', 'error');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
const cancelShortcutButton = document.getElementById('cancel-shortcut');
|
|
||||||
if (cancelShortcutButton) {
|
|
||||||
cancelShortcutButton.onclick = () => {
|
|
||||||
modal.classList.remove('active');
|
|
||||||
setTimeout(() => {
|
|
||||||
modal.classList.add('hidden');
|
|
||||||
urlInput.value = '';
|
|
||||||
nameInput.value = '';
|
|
||||||
}, 300);
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Context menu actions
|
|
||||||
const contextMenu = document.getElementById('context-menu');
|
|
||||||
if (contextMenu) {
|
|
||||||
contextMenu.addEventListener('click', (e) => {
|
|
||||||
const action = e.target.closest('.context-menu-item')?.dataset.action;
|
|
||||||
const index = parseInt(contextMenu.dataset.shortcutIndex);
|
|
||||||
|
|
||||||
if (action === 'edit') {
|
|
||||||
const currentShortcuts = Storage.get('shortcuts') || [];
|
|
||||||
const shortcut = currentShortcuts[index];
|
|
||||||
const modal = document.getElementById('edit-shortcut-modal');
|
|
||||||
|
|
||||||
if (modal) {
|
|
||||||
const urlInput = document.getElementById('edit-shortcut-url');
|
|
||||||
const nameInput = document.getElementById('edit-shortcut-name');
|
|
||||||
|
|
||||||
urlInput.value = shortcut.url;
|
|
||||||
nameInput.value = shortcut.name;
|
|
||||||
|
|
||||||
modal.classList.remove('hidden');
|
|
||||||
modal.classList.add('active');
|
|
||||||
|
|
||||||
const saveButton = document.getElementById('save-edit-shortcut');
|
|
||||||
const closeButton = document.getElementById('close-edit-shortcut');
|
|
||||||
const cancelButton = document.getElementById('cancel-edit-shortcut');
|
|
||||||
|
|
||||||
const closeModal = () => {
|
|
||||||
modal.classList.remove('active');
|
|
||||||
setTimeout(() => {
|
|
||||||
modal.classList.add('hidden');
|
|
||||||
}, 300);
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleSave = () => {
|
|
||||||
const newUrl = urlInput.value.trim();
|
|
||||||
const newName = nameInput.value.trim();
|
|
||||||
|
|
||||||
if (newUrl && newName) {
|
|
||||||
const formattedUrl = shortcuts.validateAndFormatUrl(newUrl);
|
|
||||||
if (formattedUrl) {
|
|
||||||
shortcuts.edit(index, formattedUrl, newName);
|
|
||||||
closeModal();
|
|
||||||
} else {
|
|
||||||
notifications.show('Invalid URL format!', 'error');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
saveButton.onclick = handleSave;
|
|
||||||
closeButton.onclick = closeModal;
|
|
||||||
cancelButton.onclick = closeModal;
|
|
||||||
}
|
|
||||||
} else if (action === 'delete') {
|
|
||||||
shortcuts.remove(index);
|
|
||||||
}
|
|
||||||
|
|
||||||
contextMenu.classList.add('hidden');
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
shortcuts.render();
|
|
||||||
}
|
|
||||||
};
|
|
|
@ -1,44 +0,0 @@
|
||||||
/**
|
|
||||||
* Storage utility object for managing localStorage operations
|
|
||||||
* All methods handle JSON parsing/stringifying and error cases
|
|
||||||
*/
|
|
||||||
const Storage = {
|
|
||||||
// Retrieve and parse stored value
|
|
||||||
get: (key) => {
|
|
||||||
try {
|
|
||||||
return JSON.parse(localStorage.getItem(key));
|
|
||||||
} catch (e) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
// Store value as JSON string
|
|
||||||
set: (key, value) => {
|
|
||||||
try {
|
|
||||||
localStorage.setItem(key, JSON.stringify(value));
|
|
||||||
return true;
|
|
||||||
} catch (e) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
// Delete specific key
|
|
||||||
remove: (key) => {
|
|
||||||
try {
|
|
||||||
localStorage.removeItem(key);
|
|
||||||
return true;
|
|
||||||
} catch (e) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
// Remove all stored data
|
|
||||||
clear: () => {
|
|
||||||
try {
|
|
||||||
localStorage.clear();
|
|
||||||
return true;
|
|
||||||
} catch (e) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
Loading…
Reference in New Issue
Block a user