Skip to content

Commit

Permalink
Refactor: pull out triggers
Browse files Browse the repository at this point in the history
  • Loading branch information
eight04 committed Feb 16, 2024
1 parent e9b676e commit df58c78
Show file tree
Hide file tree
Showing 12 changed files with 508 additions and 371 deletions.
7 changes: 6 additions & 1 deletion .cjsescache
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,10 @@
"node_modules/webextension-polyfill/dist/browser-polyfill.js",
"src/lib/extension-pref.js",
"src/lib/pref-body.js",
"src/lib/pref-default.js"
"src/lib/pref-default.js",
"src/lib/triggers/click.mjs",
"src/lib/triggers/hover.mjs",
"src/lib/triggers/index.mjs",
"src/lib/triggers/load.mjs",
"src/lib/triggers/mutation.mjs"
]
4 changes: 2 additions & 2 deletions .eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,10 @@ module.exports = {
},
overrides: [
{
files: ["rollup.config.js"],
files: ["rollup.config.js", "**/*.mjs"],
parserOptions: {
sourceType: "module"
}
}
},
]
};
302 changes: 179 additions & 123 deletions dist/linkify-plus-plus.user.js
Original file line number Diff line number Diff line change
Expand Up @@ -3580,98 +3580,9 @@ function linkify(...args) {
});
}

// NOTE: there is a weird transition flickering with :hover on Firefox
// const sentinel = require('sentinel-js'); // default

const MAX_PROCESSES = 100;
const processedNodes = new WeakSet;
const nodeValidationCache = new WeakMap; // Node -> boolean
let processes = 0;
const triggers = [
{
enabled: pref => pref.get("triggerByPageLoad"),
trigger: async options => {
await prepareDocument();
processedNodes.add(document.body);
await linkify({...options, root: document.body, recursive: true});
}
},
{
enabled: pref => pref.get("triggerByNewNode"),
trigger: async options => {
await prepareDocument();
const observer = new MutationObserver(function(mutations){
// Filter out mutations generated by LPP
var lastRecord = mutations[mutations.length - 1],
nodes = lastRecord.addedNodes,
i;

if (nodes.length >= 2) {
for (i = 0; i < 2; i++) {
if (nodes[i].className == "linkifyplus") {
return;
}
}
}

for (var record of mutations) {
for (const node of record.addedNodes) {
if (node.nodeType === 1 && !processedNodes.has(node)) {
if (processes >= MAX_PROCESSES) {
throw new Error("Too many processes");
}
if (processedNodes.has(node)) {
continue;
}
processedNodes.add(node);
processes++;
linkify({...options, root: node, recursive: true})
.finally(() => {
processes--;
});
}
}
}
});
observer.observe(document.body, {
childList: true,
subtree: true
});
}
},
{
enabled: pref => pref.get("triggerByHover"),
trigger: options => {
// catch the first mousemove event since mouseover doesn't fire at page refresh
document.addEventListener("mousemove", handle, {passive: true, once: true});
document.addEventListener("mouseover", handle, { passive: true });

function handle(e) {
const el = e.target;
if (validRoot(el, options.validator)) {
processedNodes.add(el);
linkify({...options, root: el, recursive: false});
}
}
}
},
{
enabled: pref => pref.get("triggerByClick"),
trigger: options => {
document.addEventListener("click", function(e){
var el = e.target;
if (validRoot(el, options.validator)) {
processedNodes.add(el);
linkify({...options, root: el, recursive: false});
}
}, {
passive: true
});
}
}
];

// Valid root node before linkifing
function validRoot(node, validator) {
if (processedNodes.has(node)) {
return false;
Expand Down Expand Up @@ -3699,6 +3610,174 @@ function validRoot(node, validator) {
}
}

function prepareDocument() {
// wait till everything is ready
return prepareBody().then(prepareApp);

function prepareApp() {
const appRoot = document.querySelector("[data-server-rendered]");
if (!appRoot) {
return;
}
return new Promise(resolve => {
const onChange = () => {
if (!appRoot.hasAttribute("data-server-rendered")) {
resolve();
observer.disconnect();
}
};
const observer = new MutationObserver(onChange);
observer.observe(appRoot, {attributes: true});
});
}

function prepareBody() {
if (document.readyState !== "loading") {
return Promise.resolve();
}
return new Promise(resolve => {
// https://github.com/Tampermonkey/tampermonkey/issues/485
document.addEventListener("DOMContentLoaded", resolve, {once: true});
});
}
}

var load = {
key: "triggerByPageLoad",
enable: async options => {
await prepareDocument();
processedNodes.add(document.body);
await linkify({...options, root: document.body, recursive: true});
},
disable: () => {}
};

let options$1;

const EVENTS$1 = [
["click", handle$1, {passive: true}],
];

function handle$1(e) {
const el = e.target;
if (validRoot(el, options$1.validator)) {
processedNodes.add(el);
linkify({...options$1, root: el, recursive: false});
}
}

function enable$2(_options) {
options$1 = _options;
for (const [event, handler, options] of EVENTS$1) {
document.addEventListener(event, handler, options);
}
}

function disable$2() {
for (const [event, handler, options] of EVENTS$1) {
document.removeEventListener(event, handler, options);
}
}

var click = {
key: "triggerByClick",
enable: enable$2,
disable: disable$2
};

let options;

const EVENTS = [
// catch the first mousemove event since mouseover doesn't fire at page refresh
["mousemove", handle, {passive: true, once: true}],
["mouseover", handle, {passive: true}]
];

function handle(e) {
const el = e.target;
if (validRoot(el, options.validator)) {
processedNodes.add(el);
linkify({...options, root: el, recursive: false});
}
}

function enable$1(_options) {
options = _options;
for (const [event, handler, options] of EVENTS) {
document.addEventListener(event, handler, options);
}
}

function disable$1() {
for (const [event, handler, options] of EVENTS) {
document.removeEventListener(event, handler, options);
}
}

var hover = {
key: "triggerByHover",
enable: enable$1,
disable: disable$1
};

const MAX_PROCESSES = 100;
let processes = 0;
let observer;

async function enable(options) {
await prepareDocument();
observer = new MutationObserver(function(mutations){
// Filter out mutations generated by LPP
var lastRecord = mutations[mutations.length - 1],
nodes = lastRecord.addedNodes,
i;

if (nodes.length >= 2) {
for (i = 0; i < 2; i++) {
if (nodes[i].className == "linkifyplus") {
return;
}
}
}

for (var record of mutations) {
for (const node of record.addedNodes) {
if (node.nodeType === 1 && !processedNodes.has(node)) {
if (processes >= MAX_PROCESSES) {
throw new Error("Too many processes");
}
if (processedNodes.has(node)) {
continue;
}
processedNodes.add(node);
processes++;
linkify({...options, root: node, recursive: true})
.finally(() => {
processes--;
});
}
}
}
});
observer.observe(document.body, {
childList: true,
subtree: true
});
}

async function disable() {
await prepareDocument();
observer && observer.disconnect();
}

var mutation = {
key: "triggerByNewNode",
enable,
disable
};

var triggers = [load, click, hover, mutation];

function createValidator({includeElement, excludeElement}) {
const f = function(node) {
if (processedNodes.has(node)) {
Expand Down Expand Up @@ -3784,43 +3863,20 @@ async function startLinkifyPlusPlus(getPref) {
const pref = await getPref();
const options = createOptions(pref);
for (const trigger of triggers) {
if (trigger.enabled(pref)) {
trigger.trigger(options);
if (pref.get(trigger.key)) {
trigger.enable(options);
}
}
// TODO: disable trigger when options change?
}

function prepareDocument() {
// wait till everything is ready
return prepareBody().then(prepareApp);

function prepareApp() {
const appRoot = document.querySelector("[data-server-rendered]");
if (!appRoot) {
return;
}
return new Promise(resolve => {
const onChange = () => {
if (!appRoot.hasAttribute("data-server-rendered")) {
resolve();
observer.disconnect();
}
};
const observer = new MutationObserver(onChange);
observer.observe(appRoot, {attributes: true});
});
}

function prepareBody() {
if (document.readyState !== "loading") {
return Promise.resolve();
pref.on("change", changes => {
for (const trigger of triggers) {
if (changes[trigger.key] === true) {
trigger.enable(options);
}
if (changes[trigger.key] === false) {
trigger.disable();
}
}
return new Promise(resolve => {
// https://github.com/Tampermonkey/tampermonkey/issues/485
document.addEventListener("DOMContentLoaded", resolve, {once: true});
});
}
});
}

/* global $inline */
Expand Down
Loading

0 comments on commit df58c78

Please sign in to comment.