- // ==UserScript==
- // @name Lazyloadfier Userscript
- // @namespace Lazyloadfier Userscript - Violentmonkey Scripts
- // @match *://*/*
- // @grant none
- // @version 1.0
- // @run-at document-start
- // @author -
- // @description 9/8/2023, 1:42:06 AM
- // ==/UserScript==
-
- /* global document, addEventListener, MutationObserver, IntersectionObserver, innerHeight, innerWidth, setTimeout, clearTimeout */
-
- const SRC_ATTRIBUTE_NAME = "src";
- const SRCSET_ATTRIBUTE_NAME = "srcset";
- const POSTER_ATTRIBUTE_NAME = "poster";
- const LOADING_ATTRIBUTE_NAME = "loading";
- const LAZY_LOADING_ATTRIBUTE_VALUE = "lazy";
- const IMG_TAG_NAME = "IMG";
- const VIDEO_TAG_NAME = "VIDEO";
- const AUDIO_TAG_NAME = "AUDIO";
- const SOURCE_TAG_NAME = "SOURCE";
- const IFRAME_TAG_NAME = "IFRAME";
- const FRAME_TAG_NAME = "FRAME";
- const EMBED_TAG_NAME = "EMBED";
- const TAG_NAMES_WITH_SRC_ATTRIBUTE = new Set([IMG_TAG_NAME, VIDEO_TAG_NAME, AUDIO_TAG_NAME, SOURCE_TAG_NAME, IFRAME_TAG_NAME, FRAME_TAG_NAME, EMBED_TAG_NAME]);
- const TAG_NAMES_WITH_SRCSET_ATTRIBUTE = new Set([IMG_TAG_NAME, SOURCE_TAG_NAME]);
- const TAG_NAMES_WITH_POSTER_ATTRIBUTE = new Set([VIDEO_TAG_NAME]);
- const OBSERVED_TAGS_SELECTOR = Array.from(TAG_NAMES_WITH_SRC_ATTRIBUTE).join(",");
- const UNSENT_READY_STATE = 0;
- const DOM_CONTENT_LOADED_EVENT = "DOMContentLoaded";
- const EMPTY_DEFAULT_DATA_URI = "data:,";
- const EMPTY_IMAGE_DATA_URI = "data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACH5BAEAAAAALAAAAAABAAEAAAICRAEAOw==";
- const EMPTY_TEXT_DATA_URI = "data:text/plain,";
- const EMPTY_DATA_URI = new Map([
- [VIDEO_TAG_NAME, EMPTY_DEFAULT_DATA_URI],
- [AUDIO_TAG_NAME, EMPTY_DEFAULT_DATA_URI],
- [SOURCE_TAG_NAME, EMPTY_IMAGE_DATA_URI],
- [IMG_TAG_NAME, EMPTY_IMAGE_DATA_URI],
- [IFRAME_TAG_NAME, EMPTY_TEXT_DATA_URI],
- [FRAME_TAG_NAME, EMPTY_TEXT_DATA_URI],
- [EMBED_TAG_NAME, EMPTY_TEXT_DATA_URI]
- ]);
- const MUTATION_OBSERVER_OPTIONS = { childList: true, subtree: true };
- const MINIMUM_INTERSECTION_RATIO = 0;
- const MUTATION_OBSERVER_TIMEOUT = 2500;
-
- observeDocumentMutations();
-
- function observeDocumentMutations() {
- const disconnectObserverTimeout = {};
- const mutationObserver = new MutationObserver(mutationsList => mutationObserverCallback(mutationsList, callDeferDisconnectObserver));
- mutationObserver.observe(document, MUTATION_OBSERVER_OPTIONS);
- addEventListener(DOM_CONTENT_LOADED_EVENT, callDeferDisconnectObserver);
-
- function callDeferDisconnectObserver() {
- deferDisconnectObserver(mutationObserver, disconnectObserverTimeout);
- }
- }
-
- function deferDisconnectObserver(mutationObserver, disconnectObserverTimeout) {
- if (disconnectObserverTimeout.id) {
- clearTimeout(disconnectObserverTimeout.id);
- }
- disconnectObserverTimeout.id = setTimeout(() => mutationObserver.disconnect(), MUTATION_OBSERVER_TIMEOUT);
- }
-
- function mutationObserverCallback(mutationsList, onProgressCallback) {
- const observedNodes = getObservedNodes(mutationsList);
- if (observedNodes.length) {
- observedNodes.forEach(observeNodeIntersection);
- onProgressCallback(observedNodes);
- }
- }
-
- function getObservedNodes(mutationsList) {
- const observedNodes = [];
- mutationsList.forEach(mutationRecord => {
- const newNodes = new Set(mutationRecord.addedNodes);
- newNodes.forEach(node => {
- if (node.querySelectorAll) {
- node.querySelectorAll(OBSERVED_TAGS_SELECTOR).forEach(node => newNodes.add(node));
- }
- });
- observedNodes.splice(0, 0, ...Array.from(newNodes).filter(matchObservedNode));
- });
- return observedNodes;
- }
-
- function matchObservedNode(node) {
- return TAG_NAMES_WITH_SRC_ATTRIBUTE.has(node.tagName) &&
- nodeIsHidden(node) &&
- node[LOADING_ATTRIBUTE_NAME] != LAZY_LOADING_ATTRIBUTE_VALUE;
- }
-
- function nodeIsHidden(node) {
- const boundingClientRect = node.getBoundingClientRect();
- return boundingClientRect.bottom < 0 ||
- boundingClientRect.top > innerHeight ||
- boundingClientRect.left < 0 ||
- boundingClientRect.right > innerWidth;
- }
-
- function observeNodeIntersection(node) {
- const src = resetSource(node, SRC_ATTRIBUTE_NAME);
- const srcset = resetSource(node, SRCSET_ATTRIBUTE_NAME);
- const poster = resetSource(node, POSTER_ATTRIBUTE_NAME);
- const intersectionObserver = new IntersectionObserver((entries, observer) => intersectionObserverCallback(entries, node, observer, { src, srcset, poster }));
- intersectionObserver.observe(node.tagName == SOURCE_TAG_NAME ? node.parentElement : node);
- }
-
- function intersectionObserverCallback(entries, node, observer, values) {
- const entry = entries[0];
- if (entry) {
- if (entry.intersectionRatio > MINIMUM_INTERSECTION_RATIO) {
- replaceSource(node, values);
- observer.disconnect();
- }
- }
- }
-
- function replaceSource(node, values) {
- setSource(node, SRC_ATTRIBUTE_NAME, values.src);
- if (TAG_NAMES_WITH_SRCSET_ATTRIBUTE.has(node.tagName)) {
- setSource(node, SRCSET_ATTRIBUTE_NAME, values.srcset);
- }
- if (TAG_NAMES_WITH_POSTER_ATTRIBUTE.has(node.tagName)) {
- setSource(node, POSTER_ATTRIBUTE_NAME, values.poster);
- }
- }
-
- function resetSource(node, attributeName) {
- const originalValue = node[attributeName];
- if (originalValue) {
- node[attributeName] = EMPTY_DATA_URI.get(node.tagName);
- return originalValue;
- }
- }
-
- function setSource(node, attributeName, value) {
- if (node[attributeName] == EMPTY_DATA_URI.get(node.tagName)) {
- if (value) {
- node[attributeName] = value;
- } else {
- node.removeAttribute(attributeName);
- if (node.readyState === UNSENT_READY_STATE) {
- node.load();
- }
- }
- }
- }