From 60cc3d320f3998ad94d525b309b4f001f009177f Mon Sep 17 00:00:00 2001
From: yetris <197530004+yetris@users.noreply.github.com>
Date: Mon, 9 Jun 2025 05:24:07 -0700
Subject: [PATCH 1/7] Create manifest.json
---
submissions/Open all this Tabs/manifest.json | 27 ++++++++++++++++++++
1 file changed, 27 insertions(+)
create mode 100644 submissions/Open all this Tabs/manifest.json
diff --git a/submissions/Open all this Tabs/manifest.json b/submissions/Open all this Tabs/manifest.json
new file mode 100644
index 00000000..1c5d8aca
--- /dev/null
+++ b/submissions/Open all this Tabs/manifest.json
@@ -0,0 +1,27 @@
+{
+ "manifest_version": 3,
+ "name": "Multi Tab Opener",
+ "version": "1.0",
+ "description": "Open multiple tabs with one click",
+ "permissions": [
+ "tabs",
+ "activeTab"
+ ],
+ "action": {
+ "default_popup": "popup.html",
+ "default_title": "Open Multiple Tabs"
+ },
+ "icons": {
+ "16": "icon16.png",
+ "48": "icon48.png",
+ "128": "icon128.png"
+ },
+ "background": {
+ "service_worker": "background.js"
+ },
+ "browser_specific_settings": {
+ "gecko": {
+ "id": "multi-tab-opener@example.com"
+ }
+ }
+}
From fd543b0035103f68d2d9cf96830de74679ba2eb5 Mon Sep 17 00:00:00 2001
From: yetris <197530004+yetris@users.noreply.github.com>
Date: Mon, 9 Jun 2025 05:25:38 -0700
Subject: [PATCH 2/7] Create popup.html
---
submissions/Open all this Tabs/popup.html | 142 ++++++++++++++++++++++
1 file changed, 142 insertions(+)
create mode 100644 submissions/Open all this Tabs/popup.html
diff --git a/submissions/Open all this Tabs/popup.html b/submissions/Open all this Tabs/popup.html
new file mode 100644
index 00000000..5bcf4e4a
--- /dev/null
+++ b/submissions/Open all this Tabs/popup.html
@@ -0,0 +1,142 @@
+
+
+
+
+
+
+
+
+
Multi Tab Opener
+
+
+
+
+
+
+
+
+
+
+
+
+
+
From db9af1a99112de14514feebbdff573e2a7f80a5b Mon Sep 17 00:00:00 2001
From: yetris <197530004+yetris@users.noreply.github.com>
Date: Mon, 9 Jun 2025 05:26:42 -0700
Subject: [PATCH 3/7] Create popup.js
---
submissions/Open all this Tabs/popup.js | 197 ++++++++++++++++++++++++
1 file changed, 197 insertions(+)
create mode 100644 submissions/Open all this Tabs/popup.js
diff --git a/submissions/Open all this Tabs/popup.js b/submissions/Open all this Tabs/popup.js
new file mode 100644
index 00000000..84626361
--- /dev/null
+++ b/submissions/Open all this Tabs/popup.js
@@ -0,0 +1,197 @@
+// Cross-browser compatibility
+const isFirefox = typeof browser !== 'undefined';
+const browserAPI = isFirefox ? browser : chrome;
+
+// Default URLs to open
+const defaultUrls = [
+ 'https://www.google.com',
+ 'https://www.github.com',
+ 'https://www.stackoverflow.com',
+ 'https://www.youtube.com',
+ 'https://www.wikipedia.org'
+];
+
+// Custom URLs storage
+let customUrls = [];
+
+// Load custom URLs from storage
+function loadCustomUrls() {
+ if (isFirefox) {
+ browser.storage.local.get('customUrls').then(result => {
+ customUrls = result.customUrls || [];
+ updateUrlList();
+ });
+ } else {
+ chrome.storage.local.get('customUrls', (result) => {
+ customUrls = result.customUrls || [];
+ updateUrlList();
+ });
+ }
+}
+
+// Save custom URLs to storage
+function saveCustomUrls() {
+ const data = { customUrls: customUrls };
+ if (isFirefox) {
+ browser.storage.local.set(data);
+ } else {
+ chrome.storage.local.set(data);
+ }
+}
+
+// Update URL list display
+function updateUrlList() {
+ const urlList = document.getElementById('urlList');
+ urlList.innerHTML = '';
+
+ customUrls.forEach((url, index) => {
+ const urlItem = document.createElement('div');
+ urlItem.className = 'url-item';
+ urlItem.innerHTML = `
+ ${url}
+
+ `;
+ urlList.appendChild(urlItem);
+ });
+
+ // Add remove listeners
+ document.querySelectorAll('.remove-url').forEach(btn => {
+ btn.addEventListener('click', (e) => {
+ const index = parseInt(e.target.dataset.index);
+ customUrls.splice(index, 1);
+ saveCustomUrls();
+ updateUrlList();
+ });
+ });
+}
+
+// Validate URL
+function isValidUrl(string) {
+ try {
+ const url = new URL(string);
+ return url.protocol === 'http:' || url.protocol === 'https:';
+ } catch (_) {
+ return false;
+ }
+}
+
+// Add URL to custom list
+function addCustomUrl() {
+ const urlInput = document.getElementById('urlInput');
+ let url = urlInput.value.trim();
+
+ if (!url) {
+ showStatus('Please enter a URL', 'error');
+ return;
+ }
+
+ // Add protocol if missing
+ if (!url.startsWith('http://') && !url.startsWith('https://')) {
+ url = 'https://' + url;
+ }
+
+ if (!isValidUrl(url)) {
+ showStatus('Please enter a valid URL', 'error');
+ return;
+ }
+
+ if (customUrls.includes(url)) {
+ showStatus('URL already exists', 'error');
+ return;
+ }
+
+ customUrls.push(url);
+ saveCustomUrls();
+ updateUrlList();
+ urlInput.value = '';
+ showStatus('URL added successfully', 'success');
+}
+
+// Show status message
+function showStatus(message, type = 'info') {
+ const status = document.getElementById('status');
+ status.textContent = message;
+ status.style.color = type === 'error' ? '#ff6b6b' : type === 'success' ? '#51cf66' : 'white';
+
+ setTimeout(() => {
+ status.textContent = '';
+ }, 3000);
+}
+
+// Open tabs function
+async function openTabs(urls) {
+ if (!urls || urls.length === 0) {
+ showStatus('No URLs to open', 'error');
+ return;
+ }
+
+ try {
+ let successCount = 0;
+
+ for (const url of urls) {
+ try {
+ if (isFirefox) {
+ await browser.tabs.create({ url: url, active: false });
+ } else {
+ await new Promise((resolve) => {
+ chrome.tabs.create({ url: url, active: false }, resolve);
+ });
+ }
+ successCount++;
+ } catch (error) {
+ console.error('Error opening tab:', url, error);
+ }
+ }
+
+ showStatus(`Opened ${successCount} of ${urls.length} tabs`, 'success');
+
+ // Close popup after a short delay
+ setTimeout(() => {
+ window.close();
+ }, 1500);
+
+ } catch (error) {
+ console.error('Error opening tabs:', error);
+ showStatus('Error opening tabs. Check permissions.', 'error');
+ }
+}
+
+// Toggle custom section
+function toggleCustomSection() {
+ const customSection = document.getElementById('customSection');
+ const isVisible = customSection.style.display !== 'none';
+ customSection.style.display = isVisible ? 'none' : 'block';
+
+ if (!isVisible) {
+ loadCustomUrls();
+ }
+}
+
+// Event listeners
+document.addEventListener('DOMContentLoaded', () => {
+ // Open predefined tabs
+ document.getElementById('openPredefined').addEventListener('click', () => {
+ openTabs(defaultUrls);
+ });
+
+ // Toggle custom tabs section
+ document.getElementById('openCustom').addEventListener('click', toggleCustomSection);
+
+ // Add custom URL
+ document.getElementById('addUrl').addEventListener('click', addCustomUrl);
+
+ // Enter key to add URL
+ document.getElementById('urlInput').addEventListener('keypress', (e) => {
+ if (e.key === 'Enter') {
+ addCustomUrl();
+ }
+ });
+
+ // Load initial data
+ loadCustomUrls();
+});
+
+// Handle popup unload
+window.addEventListener('beforeunload', () => {
+ saveCustomUrls();
+});
From b1671f2003c7df4dacb812a3d9542a4f2430b17d Mon Sep 17 00:00:00 2001
From: yetris <197530004+yetris@users.noreply.github.com>
Date: Mon, 9 Jun 2025 05:28:21 -0700
Subject: [PATCH 4/7] Create background.js
---
submissions/Open all this Tabs/background.js | 113 +++++++++++++++++++
1 file changed, 113 insertions(+)
create mode 100644 submissions/Open all this Tabs/background.js
diff --git a/submissions/Open all this Tabs/background.js b/submissions/Open all this Tabs/background.js
new file mode 100644
index 00000000..6d208d47
--- /dev/null
+++ b/submissions/Open all this Tabs/background.js
@@ -0,0 +1,113 @@
+// Background script for Chrome Extension
+// Cross-browser compatibility
+const isFirefox = typeof browser !== 'undefined';
+const browserAPI = isFirefox ? browser : chrome;
+
+// Handle extension installation
+browserAPI.runtime.onInstalled.addListener((details) => {
+ console.log('Multi Tab Opener extension installed/updated');
+
+ if (details.reason === 'install') {
+ // Set default custom URLs on first install
+ const defaultCustomUrls = [];
+ browserAPI.storage.local.set({ customUrls: defaultCustomUrls });
+ }
+});
+
+// Handle messages from popup or content scripts
+browserAPI.runtime.onMessage.addListener((request, sender, sendResponse) => {
+ if (request.action === 'openTabs') {
+ openMultipleTabs(request.urls).then(result => {
+ sendResponse({ success: true, count: result });
+ }).catch(error => {
+ console.error('Error opening tabs:', error);
+ sendResponse({ success: false, error: error.message });
+ });
+ return true; // Will respond asynchronously
+ }
+});
+
+// Function to open multiple tabs
+async function openMultipleTabs(urls) {
+ if (!urls || !Array.isArray(urls) || urls.length === 0) {
+ throw new Error('No valid URLs provided');
+ }
+
+ let successCount = 0;
+ const errors = [];
+
+ for (const url of urls) {
+ try {
+ // Validate URL
+ new URL(url); // This will throw if URL is invalid
+
+ // Create new tab
+ await browserAPI.tabs.create({
+ url: url,
+ active: false // Don't make the new tab active
+ });
+
+ successCount++;
+ } catch (error) {
+ console.error(`Failed to open tab for URL: ${url}`, error);
+ errors.push({ url, error: error.message });
+ }
+ }
+
+ if (errors.length > 0) {
+ console.warn('Some tabs failed to open:', errors);
+ }
+
+ return successCount;
+}
+
+// Handle browser action click (fallback for browsers that don't support popups)
+if (browserAPI.action && browserAPI.action.onClicked) {
+ browserAPI.action.onClicked.addListener(async (tab) => {
+ // This is a fallback in case popup fails to load
+ console.log('Browser action clicked, opening default tabs');
+
+ const defaultUrls = [
+ 'https://www.google.com',
+ 'https://www.github.com',
+ 'https://www.stackoverflow.com',
+ 'https://www.youtube.com',
+ 'https://www.wikipedia.org'
+ ];
+
+ try {
+ await openMultipleTabs(defaultUrls);
+ } catch (error) {
+ console.error('Error in fallback tab opening:', error);
+ }
+ });
+}
+
+// Handle context menu (optional feature)
+if (browserAPI.contextMenus) {
+ browserAPI.runtime.onInstalled.addListener(() => {
+ browserAPI.contextMenus.create({
+ id: 'openMultipleTabs',
+ title: 'Open Multiple Tabs',
+ contexts: ['page', 'selection']
+ });
+ });
+
+ browserAPI.contextMenus.onClicked.addListener(async (info, tab) => {
+ if (info.menuItemId === 'openMultipleTabs') {
+ const defaultUrls = [
+ 'https://www.google.com',
+ 'https://www.github.com',
+ 'https://www.stackoverflow.com',
+ 'https://www.youtube.com',
+ 'https://www.wikipedia.org'
+ ];
+
+ try {
+ await openMultipleTabs(defaultUrls);
+ } catch (error) {
+ console.error('Error opening tabs from context menu:', error);
+ }
+ }
+ });
+}
From 931230df3faacff7633292eb496c490034ec7e74 Mon Sep 17 00:00:00 2001
From: yetris <197530004+yetris@users.noreply.github.com>
Date: Mon, 9 Jun 2025 05:29:35 -0700
Subject: [PATCH 5/7] Create manifest-firefox.json
---
.../Open all this Tabs/manifest-firefox.json | 30 +++++++++++++++++++
1 file changed, 30 insertions(+)
create mode 100644 submissions/Open all this Tabs/manifest-firefox.json
diff --git a/submissions/Open all this Tabs/manifest-firefox.json b/submissions/Open all this Tabs/manifest-firefox.json
new file mode 100644
index 00000000..468fd9c0
--- /dev/null
+++ b/submissions/Open all this Tabs/manifest-firefox.json
@@ -0,0 +1,30 @@
+{
+ "manifest_version": 2,
+ "name": "Multi Tab Opener",
+ "version": "1.0",
+ "description": "Open multiple tabs with one click",
+ "permissions": [
+ "tabs",
+ "activeTab",
+ "storage"
+ ],
+ "browser_action": {
+ "default_popup": "popup.html",
+ "default_title": "Open Multiple Tabs"
+ },
+ "icons": {
+ "16": "icon16.png",
+ "48": "icon48.png",
+ "128": "icon128.png"
+ },
+ "background": {
+ "scripts": ["background-firefox.js"],
+ "persistent": false
+ },
+ "browser_specific_settings": {
+ "gecko": {
+ "id": "multi-tab-opener@example.com",
+ "strict_min_version": "57.0"
+ }
+ }
+}
From bd221e222e24ab0135692872a822c8e1621ebe42 Mon Sep 17 00:00:00 2001
From: yetris <197530004+yetris@users.noreply.github.com>
Date: Mon, 9 Jun 2025 05:30:53 -0700
Subject: [PATCH 6/7] Create background-firefox.js
---
.../Open all this Tabs/background-firefox.js | 104 ++++++++++++++++++
1 file changed, 104 insertions(+)
create mode 100644 submissions/Open all this Tabs/background-firefox.js
diff --git a/submissions/Open all this Tabs/background-firefox.js b/submissions/Open all this Tabs/background-firefox.js
new file mode 100644
index 00000000..8e4187ec
--- /dev/null
+++ b/submissions/Open all this Tabs/background-firefox.js
@@ -0,0 +1,104 @@
+// Background script for Firefox
+// Handle extension installation
+browser.runtime.onInstalled.addListener((details) => {
+ console.log('Multi Tab Opener extension installed/updated');
+
+ if (details.reason === 'install') {
+ // Set default custom URLs on first install
+ const defaultCustomUrls = [];
+ browser.storage.local.set({ customUrls: defaultCustomUrls });
+ }
+});
+
+// Handle messages from popup or content scripts
+browser.runtime.onMessage.addListener((request, sender, sendResponse) => {
+ if (request.action === 'openTabs') {
+ openMultipleTabs(request.urls).then(result => {
+ sendResponse({ success: true, count: result });
+ }).catch(error => {
+ console.error('Error opening tabs:', error);
+ sendResponse({ success: false, error: error.message });
+ });
+ return true; // Will respond asynchronously
+ }
+});
+
+// Function to open multiple tabs
+async function openMultipleTabs(urls) {
+ if (!urls || !Array.isArray(urls) || urls.length === 0) {
+ throw new Error('No valid URLs provided');
+ }
+
+ let successCount = 0;
+ const errors = [];
+
+ for (const url of urls) {
+ try {
+ // Validate URL
+ new URL(url); // This will throw if URL is invalid
+
+ // Create new tab
+ await browser.tabs.create({
+ url: url,
+ active: false // Don't make the new tab active
+ });
+
+ successCount++;
+ } catch (error) {
+ console.error(`Failed to open tab for URL: ${url}`, error);
+ errors.push({ url, error: error.message });
+ }
+ }
+
+ if (errors.length > 0) {
+ console.warn('Some tabs failed to open:', errors);
+ }
+
+ return successCount;
+}
+
+// Handle browser action click (fallback)
+browser.browserAction.onClicked.addListener(async (tab) => {
+ console.log('Browser action clicked, opening default tabs');
+
+ const defaultUrls = [
+ 'https://www.google.com',
+ 'https://www.github.com',
+ 'https://www.stackoverflow.com',
+ 'https://www.youtube.com',
+ 'https://www.wikipedia.org'
+ ];
+
+ try {
+ await openMultipleTabs(defaultUrls);
+ } catch (error) {
+ console.error('Error in fallback tab opening:', error);
+ }
+});
+
+// Handle context menu
+browser.runtime.onInstalled.addListener(() => {
+ browser.contextMenus.create({
+ id: 'openMultipleTabs',
+ title: 'Open Multiple Tabs',
+ contexts: ['page', 'selection']
+ });
+});
+
+browser.contextMenus.onClicked.addListener(async (info, tab) => {
+ if (info.menuItemId === 'openMultipleTabs') {
+ const defaultUrls = [
+ 'https://www.google.com',
+ 'https://www.github.com',
+ 'https://www.stackoverflow.com',
+ 'https://www.youtube.com',
+ 'https://www.wikipedia.org'
+ ];
+
+ try {
+ await openMultipleTabs(defaultUrls);
+ } catch (error) {
+ console.error('Error opening tabs from context menu:', error);
+ }
+ }
+});
From ff818797395447976559fbd82883c8e31f2cd7a7 Mon Sep 17 00:00:00 2001
From: yetris <197530004+yetris@users.noreply.github.com>
Date: Mon, 9 Jun 2025 05:36:25 -0700
Subject: [PATCH 7/7] Create readme.md
---
submissions/Open all this Tabs/icons/readme.md | 1 +
1 file changed, 1 insertion(+)
create mode 100644 submissions/Open all this Tabs/icons/readme.md
diff --git a/submissions/Open all this Tabs/icons/readme.md b/submissions/Open all this Tabs/icons/readme.md
new file mode 100644
index 00000000..ef625f89
--- /dev/null
+++ b/submissions/Open all this Tabs/icons/readme.md
@@ -0,0 +1 @@
+Open all this Tabs icons