// ==UserScript==
// @name Universal Iframe Spawner
// @namespace http://tampermonkey.net/
// @version 1.0
// @description Spawns multiple iframes of the current page on Shift + Left Click. Works in private/incognito mode and allows multi-account usage with separate iframe storage.
// @author Adapted from maanimis's territorial.io script
// @match *://*/*
// @grant none
// @run-at document-end
// @license MIT
// ==/UserScript==
(function () {
'use strict';
// Listen for Shift + Left Click on the document
document.addEventListener("click", function (event) {
if (event.shiftKey && event.button === 0) { // Shift + Left Click
event.preventDefault();
event.stopPropagation(); // Prevent the click from propagating further
runScript();
}
}, true); // Use capturing to catch the event early
function runScript() {
const count = +prompt("How many iframes do you want to spawn?", "2");
if (isNaN(count) || count <= 0) {
return; // Exit if the user cancels or enters an invalid number
}
const URL = window.location.href; // Use the current page's URL
createIframes(count, URL);
}
function createIframes(count, url) {
let container = document.getElementById("universalIframeContainer");
// If container doesn't exist, create and style it as an overlay
if (!container) {
container = document.createElement("div");
container.id = "universalIframeContainer";
// Style the container as a full-screen overlay
Object.assign(container.style, {
position: 'fixed',
top: '0',
left: '0',
width: '100vw',
height: '100vh',
zIndex: '99999',
backgroundColor: 'rgba(0, 0, 0, 0.8)',
display: 'grid',
gap: '10px',
padding: '10px',
boxSizing: 'border-box'
});
document.body.appendChild(container);
// Add a "Close All" button to the container
const closeAllButton = createButton("Close All ✖", () => container.remove(), "#d9534f");
Object.assign(closeAllButton.style, {
position: 'fixed',
top: '15px',
right: '15px',
zIndex: '100000'
});
document.body.appendChild(closeAllButton);
// Make sure the button is removed when the container is closed
const observer = new MutationObserver(() => {
if (!document.body.contains(container)) {
closeAllButton.remove();
observer.disconnect();
}
});
observer.observe(document.body, { childList: true });
}
// Calculate grid dimensions
const totalIframes = container.children.length + count;
const gridSize = Math.ceil(Math.sqrt(totalIframes));
container.style.gridTemplateColumns = `repeat(${gridSize}, 1fr)`;
container.style.gridTemplateRows = `repeat(${Math.ceil(totalIframes / gridSize)}, 1fr)`;
// Create and append the new iframes
for (let i = 0; i < count; i++) {
const iframeIndex = container.children.length + 1;
const iframeWrapper = createIframeWrapper(iframeIndex, url);
container.appendChild(iframeWrapper);
}
}
function createIframeWrapper(index, url) {
const wrapper = document.createElement("div");
wrapper.className = "iframe-wrapper";
Object.assign(wrapper.style, {
position: 'relative',
width: '100%',
height: '100%',
display: 'flex'
});
const controls = createControls(wrapper, index);
const iframe = createIframe(index, url);
wrapper.appendChild(controls);
wrapper.appendChild(iframe);
return wrapper;
}
function createIframe(index, url) {
const iframe = document.createElement("iframe");
iframe.id = `frame${index}`;
iframe.src = url;
Object.assign(iframe.style, {
width: '100%',
height: '100%',
border: '2px solid white',
borderRadius: '10px',
backgroundColor: '#000',
transition: 'transform 0.2s ease-in-out'
});
iframe.onmouseover = () => (iframe.style.transform = "scale(1.02)");
iframe.onmouseout = () => (iframe.style.transform = "scale(1)");
// Override storage once the iframe is loaded and has a contentWindow
iframe.onload = () => overrideStorage(iframe, index);
return iframe;
}
function createControls(wrapper, index) {
const controls = document.createElement("div");
controls.className = "controls";
Object.assign(controls.style, {
position: 'absolute',
top: '5px',
right: '5px',
zIndex: '10',
display: 'flex',
gap: '5px'
});
const closeButton = createButton("✖", () => wrapper.remove(), "#d9534f"); // Red
const maxButton = createButton("⛶", () => toggleMaximize(wrapper, maxButton), "#5cb85c"); // Green
controls.appendChild(closeButton);
controls.appendChild(maxButton);
return controls;
}
function createButton(text, onClick, color) {
const button = document.createElement("button");
button.innerText = text;
button.onclick = onClick;
Object.assign(button.style, {
background: color,
border: 'none',
padding: '5px 8px',
cursor: 'pointer',
color: 'white',
borderRadius: '5px',
fontSize: '14px',
lineHeight: '1'
});
return button;
}
function toggleMaximize(wrapper, button) {
const isMaximized = wrapper.classList.toggle('maximized');
const container = document.getElementById("universalIframeContainer");
if (isMaximized) {
// Store original styles to restore them later
wrapper.dataset.originalPosition = wrapper.style.position;
wrapper.dataset.originalZIndex = wrapper.style.zIndex;
Object.assign(wrapper.style, {
position: 'absolute',
top: '0',
left: '0',
zIndex: '100'
});
button.innerText = "🗗"; // Minimize symbol
container.style.zIndex = '99998'; // Ensure container is just below maximized iframe
} else {
// Restore original styles
wrapper.style.position = wrapper.dataset.originalPosition || 'relative';
wrapper.style.zIndex = wrapper.dataset.originalZIndex || '1';
button.innerText = "⛶"; // Maximize symbol
container.style.zIndex = '99999';
}
}
function overrideStorage(iframe, index) {
// This works because the iframe src is the same origin as the parent window
try {
if (iframe.contentWindow) {
iframe.contentWindow.localStorage = (() => {
let storage = {};
console.log(`[Iframe Spawner] Overriding localStorage for iframe ${index}`);
return {
setItem: (key, value) => (storage[key] = value),
getItem: (key) => storage[key] || null,
removeItem: (key) => delete storage[key],
clear: () => (storage = {}),
key: (i) => Object.keys(storage)[i] || null,
get length() {
return Object.keys(storage).length;
}
};
})();
}
} catch (e) {
console.error(`[Iframe Spawner] Could not override storage for iframe ${index} due to a security policy.`, e);
}
}
})();