-
Notifications
You must be signed in to change notification settings - Fork 67
/
core.js
180 lines (154 loc) · 8.07 KB
/
core.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
/*
* core.js is used to reference every <a href="?"/> and link them to the different
* hovering functions we support.
*
* It is responsible for checking if links are from known or unknown website and to add events
* regarding wether we have a template for them or not.
*/
document.addEventListener('DOMContentLoaded', () => {
/* Keeping track of the current tab is important as we'll need to disable some of the
* embed shown while hovering on certain site as some already have hovering functions for profiles
* (ex: reddit profiles on reddit)
*/
var CURRENT_TAB = document.location.href;
var previewMetadata = true;
var darkTheme = false;
var container = document.createElement('div');
var capturedNodes = [];
var intentTimeout = null;
var intent = 0.75; // Wait duration in seconds before loading the preview
/* Just in case some sites use the pushState js function to navigate across pages. */
window.onpopstate = () => {
CURRENT_TAB = document.location.href;
};
window.lastHovered = null;
/* <https://www.chromium.org/Home/chromium-security/extension-content-script-fetches>
* "Later in 2019, Extension Manifest V3 will become available, requiring cross-origin requests to occur in background pages rather than content scripts. This new manifest version will have its own migration period, before support for Extension Manifest V2 is eventually removed from Chrome."
* Using the background script to pull data from APIs safely, i.e forwarding request from the content script to the background script
* Direct asynchronous messaging, making it as a function to avoid having some request code across every file
*/
window.survolBackgroundRequest = (url, noJSON) => {
return new Promise((resolve, reject) => {
chrome.runtime.sendMessage({ action: 'request', data: { url, noJSON } }, (res) => {
(res.status == 'OK') ? resolve(res): reject(res);
});
});
};
/* Used to do everything using CSS, however it was messy.
* We will now use a single div that we will move across the screen based on the mouse position
*/
function insertSurvolDiv() {
return new Promise((resolve) => {
container.className = `survol-container ${darkTheme ? 'dark-theme' : ''} hidden`;
container.id = 'survol-container';
//set the buffer (popup distance from mouse)
const buffer = 20;
document.addEventListener('mousemove', function (e) {
// Link detection, doesn't need to refresh nodes on asynchronous requests, doesn't need to add thousands of event listener
// Pretty reliable
if (document.querySelectorAll('a:hover')[0] != window.lastHovered) {
window.lastHovered = document.querySelectorAll('a:hover')[0];
container.innerHTML = '';
if (document.querySelectorAll('a:hover')[0]) {
intentTimeout = setTimeout(() => {
dispatcher(window.lastHovered, window.lastHovered.href);
}, 1000 * intent);
} else {
clearTimeout(intentTimeout);
container.className = `survol-container ${darkTheme ? 'dark-theme' : ''} hidden`;
container.innerHTML = '';
}
}
//get the popup dims
let popupWidth = container.clientWidth;
let popupHeight = container.clientHeight;
//get the current scroll distance
let scrolled = window.pageYOffset || (document.documentElement || document.body.parentNode || document.body).scrollTop;
//calculate the popup positions
//if the current mouse X position plus the popup width is greater than the width of the viewport set the popup to the left, else right
let leftPosition = (e.pageX + popupWidth + buffer >= window.innerWidth) ? `${e.pageX - (popupWidth + buffer)}px` : `${e.pageX + buffer}px`;
//if the current mouse Y position (mius scroll distance) plus the popup height is greater than viewport height, set the popup above mouse, else below
let topPosition = ((e.pageY - scrolled) + popupHeight + buffer >= window.innerHeight) ? `${e.pageY - (popupHeight + buffer)}px` : `${e.pageY + buffer}px`;
//update the popup with the calculated values
container.style.left = leftPosition;
container.style.top = topPosition;
});
document.body.appendChild(container);
// MutationObserver will observe the page if there is any nodes are added to the page
/*const observer = new MutationObserver(domMutationCallback);
const bodyNode = document.getElementsByTagName('BODY')[0];
const config = { childList: true, subtree: true };
observer.observe(bodyNode, config);*/
resolve();
});
}
/* Takes {String} link
* Returns {String} link
* Description: Returns the domain name associated to a full link
*/
function getDomain(link) {
let subdomains = link.replace('http://', '').replace('https://', '').split('/')[0].split('.').length;
return link.replace('http://', '').replace('https://', '').split('/')[0].split('.').slice(subdomains - 2, subdomains).join('.');
}
/* Node is a DOM Node
* domain is a string
* Description: Returns the correct hover CLass for a given domain.
*/
function getPotentialHover(node, domain) {
switch (domain) {
case 'reddit.com':
return new RedditHover(node, getDomain(CURRENT_TAB));
case 'wikipedia.org':
return new WikipediaHover(node, getDomain(CURRENT_TAB));
case 'youtube.com':
case 'youtu.be':
return new YoutubeHover(node, getDomain(CURRENT_TAB));
case 'twitter.com':
return new TwitterHover(node, getDomain(CURRENT_TAB));
case 'stackoverflow.com':
return new StackExchangeHover(node, getDomain(CURRENT_TAB));
case 'soundcloud.com':
return new SoundCloudHover(node, getDomain(CURRENT_TAB));
default:
return previewMetadata ? new BaseHover(node, getDomain(CURRENT_TAB)) : null;
//return new BaseHover(node);
}
}
/* Checks if the hover class is working with the given link and deletes it if it's not.
* takes
* {Node} node
* {String} link
*/
function dispatcher(node, link) {
let domain = getDomain(link);
let potentialHover = getPotentialHover(node, domain);
/* If we do not support the domain we might not get anything in return of getPotentialHover */
if (potentialHover && potentialHover.bindToContainer != null && node.href && node.href.startsWith('http')) {
potentialHover.bindToContainer(node, domain, container);
container.className = `survol-container ${darkTheme ? 'dark-theme' : ''}`;
window.lastHovered = node;
} else {
// In case the node has no href feed it to the garbage collector
potentialHover = null;
}
}
// If the script is part of the extension
if ((window.chrome && chrome.runtime && chrome.runtime.id) || chrome) {
chrome.storage.local.get(['disabledDomains', 'previewMetadata', 'darkThemeToggle'], function (res) {
let disabledDomains = res.disabledDomains ? res.disabledDomains : ['survol.me'];
if (res.previewMetadata === false) {
previewMetadata = false;
}
if (res.darkThemeToggle === true) {
darkTheme = true;
}
if (!disabledDomains.includes(getDomain(CURRENT_TAB).toLowerCase())) {
insertSurvolDiv();
}
});
}
// Else the script is running in demo-mode on survol.me
else {
insertSurvolDiv();
}
});