Skip to content

Instantly share code, notes, and snippets.

@myfreeer
Last active November 19, 2017 13:14
Show Gist options
  • Save myfreeer/cb936f5baefb12a83d375541038364a0 to your computer and use it in GitHub Desktop.
Save myfreeer/cb936f5baefb12a83d375541038364a0 to your computer and use it in GitHub Desktop.
// ==UserScript==
// @name fix to width
// @namespace https://addons.mozilla.org
// @version 0.1
// @description try to take over the world!
// @author You
// @match http://*/*
// @match https://*/*
// @grant none
// @run-at document-start
// ==/UserScript==
'use strict';
var resizeClassName = 'akdorFitToWidth';
var hasLongTextChildClassName = 'akdorHasLongTextChild';
var styleSheetName = 'fitToWidthStyle';
var styleSheet;
var classRule;
function AsyncForeacher(func, batchSize = 5) {
this.func = func;
this.queue = [];
this.batchSize = batchSize;
this.i = 0;
this.processCB = (queueDone) => this._processAsync(queueDone);
this.running = false;
}
AsyncForeacher.prototype = Object.create(Object.prototype);
AsyncForeacher.prototype.
add = function(element, run=true) {
this.queue.push(element);
if (run) {
window.setTimeout( () => this.process(), 5);
}
};
AsyncForeacher.prototype.
_processSync = function(callback) {
var batchLimit = Math.min(this.i+this.batchSize, this.queue.length);
for (; this.i < batchLimit; this.i++) {
this.func(this.queue[this.i]);
this.queue[this.i] = undefined;
}
var queueDone = (this.i == this.queue.length);
callback(queueDone);
};
AsyncForeacher.prototype.
process = function() {
if (this.running) {
return;
}
//console.log(`starting loop! i=${this.i}, l=${this.queue.length}, n=${this.queue[this.i].outerHTML}`);
this.running = true;
this._processAsync();
};
AsyncForeacher.prototype.
_processAsync = function(queueDone = false) {
if (queueDone) {
//console.log('ending loop!');
this.running = false;
return;
}
window.setTimeout( () => this._processSync(this.processCB) , 5);
};
function setupStyle() {
var styleElement = document.createElement('style');
styleElement.title = styleSheetName;
document.head.appendChild(styleElement);
return styleElement;
}
var getStyleSheet = Array.prototype.find.bind(
document.styleSheets,
(maybeStyle) => maybeStyle.title == styleSheetName
);
function addStyleSheetRule(rule, styleSheet) {
var classRuleIndex = styleSheet.insertRule(rule, 0);
var classRule = styleSheet.cssRules[classRuleIndex];
return classRule;
}
function setupClass(styleSheet) {
maxWidthClassRule = addStyleSheetRule(`.${resizeClassName}:not(img) { max-width: auto }`, styleSheet);
addStyleSheetRule(`.${hasLongTextChildClassName} { max-width: auto }`, styleSheet);
addStyleSheetRule(`* .${hasLongTextChildClassName} { max-width: auto }`, styleSheet);
}
function setMaxWidth(widthInPx) {
var widthValue = (widthInPx) ? `${widthInPx}px` : 'auto';
maxWidthClassRule.style.setProperty('max-width', widthValue, 'important');
//classRule.style.setProperty('background-color', 'red', 'important');
}
function setMinWidth(widthInPx) {
var widthValue = (widthInPx) ? `${widthInPx}px` : 'auto';
minWidthClassRule.style.setProperty('min-width', widthValue, 'important');
}
function isLongTextNode(node) {
return (node.nodeType == Node.TEXT_NODE && node.nodeValue.length > 10 && node.nodeValue.trim().length > 10);
}
function isInline(node) {
if (!node.nodeStyle) {
var computedStyle = window.getComputedStyle(node);
if (!computedStyle) {
return true;
}
node.nodeStyle = computedStyle.display;
}
if (!node.nodeStyle) {
return true;
}
switch (node.nodeStyle) {
case 'inline':
//case 'inline-block':
return true;
default:
return false;
}
}
function hasWidth(node) {
if (!node.nodeStyle) {
var computedStyle = window.getComputedStyle(node);
if (!computedStyle) {
return;
}
node.nodeStyle = computedStyle.display;
}
if (!node.nodeStyle) {
return false;
}
switch (node.nodeStyle) {
case 'inline-block':
case 'block':
case 'table-cell':
return true;
default:
return false;
}
}
function shouldResize(node) {
return hasWidth(node) && Array.prototype.every.call(
node.children,
(node) => isInline(node)
);
}
function resize(node) {
node.classList.add(resizeClassName);
}
function onNewNode(node) {
if (node.tested) {
return;
}
node.tested = true;
if (isLongTextNode(node)) {
if (node.parentNode && !node.parentNode.akdorHasLongTextChild) {
node.parentNode.akdorHasLongTextChild = true;
node.parentNode.classList.add(hasLongTextChildClassName);
node.parentNode.classList.add(resizeClassName);
}
}
if (!(node instanceof Element)) {
return;
}
if (isInline(node)) {
//do nothing
} else {
//console.log('stopping a '+node.nodeName+' from resizing');
if (node.parentNode && ! node.parentNode.akdorHasLongTextChild) {
node.parentNode.classList.remove(resizeClassName);
}
}
if (hasWidth(node)) {
//console.log('setting a '+node.nodeName+' to resize');
node.classList.add(resizeClassName);
}
}
function walkChildren(node) {
elementQueue.add(node, false);
if (!node.children) {
return;
}
Array.prototype.forEach.call(node.childNodes, walkChildren);
}
function onMutation(mutations, observer) {
for (let mutation of mutations) {
for (let newNode of mutation.addedNodes) {
try {
if (!loaded) {
//console.error('mutation called before loaded!');
} else {
walkChildren(newNode);
elementQueue.process();
}
} catch (e) {
//console.error('oh no!');
//console.error(e.message);
//console.error(e.stack);
}
}
}
}
var loaded = false;
var elements = Array(1000);
var elementQueue = new AsyncForeacher(onNewNode, 40);
function setupObserver() {
var observeOptions = {
'childList': true,
'subtree': true
};
var observer = new MutationObserver(onMutation);
observer.observe(document.documentElement, observeOptions);
}
var minWidthClassRule;
var maxWidthClassRule;
function main() {
setupStyle();
var styleSheet = getStyleSheet();
setupClass(styleSheet);
document.addEventListener('DOMContentLoaded', onLoad, false);
//walkChildren(document.body);
//for each node
//windowSizeChanged(classRule);
//window.addEventListener('resize', onResize.bind(undefined, classRule), false);
window.setInterval(checkIfResized, 250);
}
function onLoad() {
document.removeEventListener('DOMContentLoaded', onLoad, false);
loaded = true;
if (!forSmallScreens()) {
//console.log('page looks like it\'s for desktop, resizing');
actuallyWalk();
}
}
function metaStringToObject(metaString) {
//"a=asdf; b= adf" => { a: 'asdf', b: 'asdf' }
var contentData = {};
metaString.split(',').forEach((contentDatum) => {
var contentDatumSplit = contentDatum.split('=');
if (contentDatumSplit.length != 2) {
return;
}
contentData[contentDatumSplit[0].trim()] = contentDatumSplit[1].trim();
});
return contentData;
}
function forSmallScreens() {
var viewportElement = document.querySelector('meta[name=viewport]');
if (!viewportElement || !viewportElement.content) {
return false;
}
var contentData = metaStringToObject(viewportElement.content);
if (contentData.width == 'device-width') {
return true;
}
if (parseInt(contentData.width) <= 480) {
return true;
}
if (contentData['initial-scale']) {
return true;
}
return false;
}
function actuallyWalk() {
//console.log('walking');
walkChildren(document.body);
elementQueue.process();
//console.log('walked');
//console.log(`observing ${window.location.href}`);
setupObserver();
//console.log('...for reals');
}
var oldWidth;
var currentlyResizing;
function checkIfResized() {
if (oldWidth != window.innerWidth) {
oldWidth = window.innerWidth;
currentlyResizing = true;
} else {
if (currentlyResizing) {
windowSizeChanged();
}
currentlyResizing = false;
}
}
var resizeTimeout = 0;
function onResize(e) {
//console.error('event got');
//console.log(`clearing timer ${window.resizeTimeout}`)
clearTimeout(resizeTimeout);
resizeTimeout = setTimeout(windowSizeChanged.bind(undefined, classRule), 500);
//console.log(`new timeout is ${window.resizeTimeout}`)
}
function windowSizeChanged() {
var viewportWidth = window.innerWidth - 10;
if (viewportWidth <= 5) {
return;
}
//setMinWidth(viewportWidth, classRule);
setMaxWidth(viewportWidth);
}
main();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment