Last active
August 1, 2018 22:49
-
-
Save houtianze/f6b0eb1c5825d1c214d61a3bf286922d to your computer and use it in GitHub Desktop.
Power Mode TamperMonkey Script
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// ==UserScript== | |
// @name Power Mode! | |
// @namespace http://houtianze.github.io/ | |
// @updateURL https://gist.githubusercontent.com/houtianze/f6b0eb1c5825d1c214d61a3bf286922d/raw | |
// @version 0.1.1 | |
// @description Turn on Power Mode! | |
// @author ibic | |
// @match *://*/* | |
// @exclude *://developer.chrome.com/* | |
// @grant none | |
// ==/UserScript== | |
(function () { | |
'use strict'; | |
(function () { | |
// The properties that we copy into a mirrored div. | |
// Note that some browsers, such as Firefox, | |
// do not concatenate properties, i.e. padding-top, bottom etc. -> padding, | |
// so we have to do every single property specifically. | |
var properties = [ | |
'direction', // RTL support | |
'boxSizing', | |
'width', // on Chrome and IE, exclude the scrollbar, so the mirror div wraps exactly as the textarea does | |
'height', | |
'overflowX', | |
'overflowY', // copy the scrollbar for IE | |
'borderTopWidth', | |
'borderRightWidth', | |
'borderBottomWidth', | |
'borderLeftWidth', | |
'borderStyle', | |
'paddingTop', | |
'paddingRight', | |
'paddingBottom', | |
'paddingLeft', | |
// https://developer.mozilla.org/en-US/docs/Web/CSS/font | |
'fontStyle', | |
'fontVariant', | |
'fontWeight', | |
'fontStretch', | |
'fontSize', | |
'fontSizeAdjust', | |
'lineHeight', | |
'fontFamily', | |
'textAlign', | |
'textTransform', | |
'textIndent', | |
'textDecoration', // might not make a difference, but better be safe | |
'letterSpacing', | |
'wordSpacing', | |
'tabSize', | |
'MozTabSize' | |
]; | |
var isFirefox = window.mozInnerScreenX != null; | |
function getCaretCoordinates(element, position, options) { | |
var debug = options && options.debug || false; | |
if (debug) { | |
var el = document.querySelector('#input-textarea-caret-position-mirror-div'); | |
if (el) { el.parentNode.removeChild(el); } | |
} | |
// mirrored div | |
var div = document.createElement('div'); | |
div.id = 'input-textarea-caret-position-mirror-div'; | |
document.body.appendChild(div); | |
var style = div.style; | |
var computed = window.getComputedStyle ? getComputedStyle(element) : element.currentStyle; // currentStyle for IE < 9 | |
// default textarea styles | |
style.whiteSpace = 'pre-wrap'; | |
if (element.nodeName !== 'INPUT') | |
style.wordWrap = 'break-word'; // only for textarea-s | |
// position off-screen | |
style.position = 'absolute'; // required to return coordinates properly | |
if (!debug) | |
style.visibility = 'hidden'; // not 'display: none' because we want rendering | |
// transfer the element's properties to the div | |
properties.forEach(function (prop) { | |
style[prop] = computed[prop]; | |
}); | |
if (isFirefox) { | |
// Firefox lies about the overflow property for textareas: https://bugzilla.mozilla.org/show_bug.cgi?id=984275 | |
if (element.scrollHeight > parseInt(computed.height)) | |
style.overflowY = 'scroll'; | |
} else { | |
style.overflow = 'hidden'; // for Chrome to not render a scrollbar; IE keeps overflowY = 'scroll' | |
} | |
div.textContent = element.value.substring(0, position); | |
// the second special handling for input type="text" vs textarea: spaces need to be replaced with non-breaking spaces - http://stackoverflow.com/a/13402035/1269037 | |
if (element.nodeName === 'INPUT') | |
div.textContent = div.textContent.replace(/\s/g, "\u00a0"); | |
var span = document.createElement('span'); | |
// Wrapping must be replicated *exactly*, including when a long word gets | |
// onto the next line, with whitespace at the end of the line before (#7). | |
// The *only* reliable way to do that is to copy the *entire* rest of the | |
// textarea's content into the <span> created at the caret position. | |
// for inputs, just '.' would be enough, but why bother? | |
span.textContent = element.value.substring(position) || '.'; // || because a completely empty faux span doesn't render at all | |
div.appendChild(span); | |
var coordinates = { | |
top: span.offsetTop + parseInt(computed['borderTopWidth']), | |
left: span.offsetLeft + parseInt(computed['borderLeftWidth']) | |
}; | |
if (debug) { | |
span.style.backgroundColor = '#aaa'; | |
} else { | |
document.body.removeChild(div); | |
} | |
return coordinates; | |
} | |
if (typeof module != "undefined" && typeof module.exports != "undefined") { | |
module.exports = getCaretCoordinates; | |
} else { | |
window.getCaretCoordinates = getCaretCoordinates; | |
} | |
}()); | |
var canvas = document.createElement('canvas'); | |
canvas.width = window.innerWidth; | |
canvas.height = window.innerHeight; | |
canvas.style.cssText = 'position:fixed;top:0;left:0;pointer-events:none;z-index:999999'; | |
window.addEventListener('resize', function () { | |
canvas.width = window.innerWidth; | |
canvas.height = window.innerHeight; | |
}); | |
document.body.appendChild(canvas); | |
var context = canvas.getContext('2d'); | |
var particles = []; | |
var particlePointer = 0; | |
var rendering = false; | |
var baseBodyMarginTop = {}; | |
var baseBodyMarginLeft = {}; | |
function getRandom(min, max) { | |
return Math.random() * (max - min) + min; | |
} | |
function getColor(el) { | |
if (POWERMODE.colorful) { | |
var u = getRandom(0, 360); | |
return 'hsla(' + getRandom(u - 10, u + 10) + ', 100%, ' + getRandom(50, 80) + '%, ' + 1 + ')'; | |
} else { | |
return window.getComputedStyle(el).color; | |
} | |
} | |
function getCaret() { | |
var el = document.activeElement; | |
var bcr; | |
if (el.tagName === 'TEXTAREA' || | |
(el.tagName === 'INPUT' && el.getAttribute('type') === 'text')) { | |
var offset = getCaretCoordinates(el, el.selectionEnd); | |
bcr = el.getBoundingClientRect(); | |
return { | |
x: offset.left + bcr.left, | |
y: offset.top + bcr.top, | |
color: getColor(el) | |
}; | |
} | |
var selection = window.getSelection(); | |
if (selection.rangeCount) { | |
var range = selection.getRangeAt(0); | |
var startNode = range.startContainer; | |
if (startNode.nodeType === document.TEXT_NODE) { | |
startNode = startNode.parentNode; | |
} | |
bcr = range.getBoundingClientRect(); | |
return { | |
x: bcr.left, | |
y: bcr.top, | |
color: getColor(startNode) | |
}; | |
} | |
return { x: 0, y: 0, color: 'transparent' }; | |
} | |
function createParticle(x, y, color) { | |
return { | |
x: x, | |
y: y, | |
alpha: 1, | |
color: color, | |
velocity: { | |
x: -1 + Math.random() * 2, | |
y: -3.5 + Math.random() * 2 | |
} | |
}; | |
} | |
function POWERMODE() { | |
{ // spawn particles | |
var caret = getCaret(); | |
var numParticles = POWERMODE.minParticles | |
+ Math.round(Math.random() * (POWERMODE.maxParticles - POWERMODE.minParticles)); | |
while (numParticles--) { | |
particles[particlePointer] = createParticle(caret.x, caret.y, caret.color); | |
particlePointer = (particlePointer + 1) % 500; | |
} | |
} | |
{ // shake screen | |
if (POWERMODE.shake) { | |
var intensity = POWERMODE.shakeIntensity + POWERMODE.shakeIntensity * 2 * Math.random(); | |
var x = intensity * (Math.random() > 0.5 ? -1 : 1); | |
var y = intensity * (Math.random() > 0.5 ? -1 : 1); | |
var style = document.body.style | |
style.marginLeft = (baseBodyMarginLeft.value + x) + baseBodyMarginLeft.unit; | |
style.marginTop = (baseBodyMarginTop.value + y) + baseBodyMarginTop.unit; | |
setTimeout(function () { | |
style.marginLeft = baseBodyMarginLeft.value + baseBodyMarginLeft.unit; | |
style.marginTop = baseBodyMarginTop.value + baseBodyMarginTop.unit; | |
}, 75); | |
} | |
} | |
if (!rendering) { | |
requestAnimationFrame(loop); | |
} | |
}; | |
function loop() { | |
rendering = true; | |
context.clearRect(0, 0, canvas.width, canvas.height); | |
var rendered = false; | |
var rect = canvas.getBoundingClientRect(); | |
for (var i = 0; i < particles.length; ++i) { | |
var particle = particles[i]; | |
if (particle.alpha <= 0.1) continue; | |
particle.velocity.y += 0.075; | |
particle.x += particle.velocity.x; | |
particle.y += particle.velocity.y; | |
particle.alpha *= 0.96; | |
context.globalAlpha = particle.alpha; | |
context.fillStyle = particle.color; | |
context.fillRect( | |
Math.round(particle.x - 1.5) - rect.left, | |
Math.round(particle.y - 1.5) - rect.top, | |
3, 3 | |
); | |
rendered = true; | |
} | |
if (rendered) { | |
requestAnimationFrame(loop); | |
} else { | |
rendering = false; | |
} | |
} | |
function splitValueAndUnit(measurement) { | |
var r = {}; | |
var i = 0; | |
for (; i < measurement.length; i++) { | |
var c = measurement[i] | |
if (c > '9' || c < '0') { | |
break; | |
} | |
} | |
r.value = parseFloat(measurement.substring(0, i)) | |
r.unit = measurement.substring(i).trim() | |
return r | |
} | |
function caculateBodyMargin(event) { | |
var p = document.body; | |
var style = p.currentStyle || window.getComputedStyle(p); | |
baseBodyMarginLeft = splitValueAndUnit(style.marginLeft) | |
baseBodyMarginTop = splitValueAndUnit(style.marginTop) | |
} | |
if (document.readyState == "complete" || document.readyState == "loaded" || document.readyState == "interactive") { | |
caculateBodyMargin() | |
} else { | |
document.addEventListener("DOMContentLoaded", caculateBodyMargin) | |
} | |
POWERMODE.shake = false; | |
POWERMODE.shakeIntensity = 1; | |
POWERMODE.colorful = true; | |
POWERMODE.minParticles = 2; | |
POWERMODE.maxParticles = 8; | |
document.body.addEventListener('input', POWERMODE); | |
})(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment