blob: 69b3bb76a1e094abd4ae109012a9ce30ba2151fb [file] [log] [blame]
Adrià Vilanova Martínez3465e772021-07-11 19:18:41 +02001import {injectScript, injectStyles, injectStylesheet} from '../../common/contentScriptsUtils.js';
2
3import {autoRefresh} from './autoRefresh.js';
Adrià Vilanova Martínez27c69962021-07-17 23:32:51 +02004import AvatarsHandler from './avatars.js';
Adrià Vilanova Martínez3465e772021-07-11 19:18:41 +02005import {addBatchLockBtn, nodeIsReadToggleBtn} from './batchLock.js';
6import {injectDarkModeButton, isDarkThemeOn} from './darkMode.js';
7import {applyDragAndDropFix} from './dragAndDropFix.js';
8import {markCurrentThreadAsRead} from './forceMarkAsRead.js';
9import {injectPreviousPostsLinks} from './profileHistoryLink.js';
10import {unifiedProfilesFix} from './unifiedProfiles.js';
11
Adrià Vilanova Martínez27c69962021-07-17 23:32:51 +020012var mutationObserver, intersectionObserver, intersectionOptions, options, avatars;
Adrià Vilanova Martínez3465e772021-07-11 19:18:41 +020013
14const watchedNodesSelectors = [
15 // App container (used to set up the intersection observer and inject the dark
16 // mode button)
17 'ec-app',
18
19 // Load more bar (for the "load more"/"load all" buttons)
20 '.load-more-bar',
21
22 // Username span/editor inside ec-user (user profile view)
23 'ec-user .main-card .header > .name > span',
24 'ec-user .main-card .header > .name > ec-display-name-editor',
25
26 // Rich text editor
27 'ec-movable-dialog',
28 'ec-rich-text-editor',
29
30 // Read/unread bulk action in the list of thread, for the batch lock feature
31 'ec-bulk-actions material-button[debugid="mark-read-button"]',
32 'ec-bulk-actions material-button[debugid="mark-unread-button"]',
33
34 // Thread list items (used to inject the avatars)
35 'li',
36
37 // Thread list (used for the autorefresh feature)
38 'ec-thread-list',
39
40 // Unified profile iframe
41 'iframe',
42
43 // Thread component
44 'ec-thread',
45];
46
47function handleCandidateNode(node) {
48 if (typeof node.classList !== 'undefined') {
49 if (('tagName' in node) && node.tagName == 'EC-APP') {
50 // Set up the intersectionObserver
51 if (typeof intersectionObserver === 'undefined') {
52 var scrollableContent = node.querySelector('.scrollable-content');
53 if (scrollableContent !== null) {
54 intersectionOptions = {
55 root: scrollableContent,
56 rootMargin: '0px',
57 threshold: 1.0,
58 };
59
60 intersectionObserver = new IntersectionObserver(
61 intersectionCallback, intersectionOptions);
62 }
63 }
64
65 // Inject the dark mode button
66 if (options.ccdarktheme && options.ccdarktheme_mode == 'switch') {
67 var rightControl = node.querySelector('header .right-control');
68 if (rightControl !== null)
69 injectDarkModeButton(rightControl, options.ccdarktheme_switch_status);
70 }
71 }
72
73 // Start the intersectionObserver for the "load more"/"load all" buttons
74 // inside a thread
75 if ((options.thread || options.threadall) &&
76 node.classList.contains('load-more-bar')) {
77 if (typeof intersectionObserver !== 'undefined') {
78 if (options.thread)
79 intersectionObserver.observe(node.querySelector('.load-more-button'));
80 if (options.threadall)
81 intersectionObserver.observe(node.querySelector('.load-all-button'));
82 } else {
83 console.warn(
84 '[infinitescroll] ' +
85 'The intersectionObserver is not ready yet.');
86 }
87 }
88
89 // Show the "previous posts" links
90 // Here we're selecting the 'ec-user > div' element (unique child)
91 if (options.history &&
92 (node.matches('ec-user .main-card .header > .name > span') ||
93 node.matches(
94 'ec-user .main-card .header > .name > ec-display-name-editor'))) {
95 injectPreviousPostsLinks(node);
96 }
97
98 // Fix the drag&drop issue with the rich text editor
99 //
100 // We target both tags because in different contexts different
101 // elements containing the text editor get added to the DOM structure.
102 // Sometimes it's a EC-MOVABLE-DIALOG which already contains the
103 // EC-RICH-TEXT-EDITOR, and sometimes it's the EC-RICH-TEXT-EDITOR
104 // directly.
105 if (options.ccdragndropfix && ('tagName' in node) &&
106 (node.tagName == 'EC-MOVABLE-DIALOG' ||
107 node.tagName == 'EC-RICH-TEXT-EDITOR')) {
108 applyDragAndDropFix(node);
109 }
110
111 // Inject the batch lock button in the thread list
112 if (options.batchlock && nodeIsReadToggleBtn(node)) {
113 addBatchLockBtn(node);
114 }
115
116 // Inject avatar links to threads in the thread list
117 if (options.threadlistavatars && ('tagName' in node) &&
118 (node.tagName == 'LI') &&
119 node.querySelector('ec-thread-summary') !== null) {
120 avatars.inject(node);
121 }
122
123 // Set up the autorefresh list feature
124 if (options.autorefreshlist && ('tagName' in node) &&
125 node.tagName == 'EC-THREAD-LIST') {
126 autoRefresh.setUp();
127 }
128
129 // Redirect unified profile iframe to dark version if applicable
130 if (node.tagName == 'IFRAME' && isDarkThemeOn(options) &&
131 unifiedProfilesFix.checkIframe(node)) {
132 unifiedProfilesFix.fixIframe(node);
133 }
134
135 // Force mark thread as read
136 if (options.forcemarkasread && node.tagName == 'EC-THREAD') {
137 markCurrentThreadAsRead();
138 }
139 }
140}
141
142function handleRemovedNode(node) {
143 // Remove snackbar when exiting thread list view
144 if (options.autorefreshlist && 'tagName' in node &&
145 node.tagName == 'EC-THREAD-LIST') {
146 autoRefresh.hideUpdatePrompt();
147 }
148}
149
150function mutationCallback(mutationList, observer) {
151 mutationList.forEach((mutation) => {
152 if (mutation.type == 'childList') {
153 mutation.addedNodes.forEach(function(node) {
154 handleCandidateNode(node);
155 });
156
157 mutation.removedNodes.forEach(function(node) {
158 handleRemovedNode(node);
159 });
160 }
161 });
162}
163
164function intersectionCallback(entries, observer) {
165 entries.forEach(entry => {
166 if (entry.isIntersecting) {
167 entry.target.click();
168 }
169 });
170};
171
172var observerOptions = {
173 childList: true,
174 subtree: true,
175};
176
177chrome.storage.sync.get(null, function(items) {
178 options = items;
179
Adrià Vilanova Martínez27c69962021-07-17 23:32:51 +0200180 // Initialize classes needed by the mutation observer
181 if (options.threadlistavatars)
182 avatars = new AvatarsHandler();
183
Adrià Vilanova Martínez3465e772021-07-11 19:18:41 +0200184 // Before starting the mutation Observer, check whether we missed any
185 // mutations by manually checking whether some watched nodes already
186 // exist.
187 var cssSelectors = watchedNodesSelectors.join(',');
188 document.querySelectorAll(cssSelectors)
189 .forEach(node => handleCandidateNode(node));
190
191 mutationObserver = new MutationObserver(mutationCallback);
192 mutationObserver.observe(document.body, observerOptions);
193
194 if (options.fixedtoolbar) {
195 injectStyles(
196 'ec-bulk-actions{position: sticky; top: 0; background: var(--TWPT-primary-background, #fff); z-index: 96;}');
197 }
198
199 if (options.increasecontrast) {
200 injectStyles(
201 '.thread-summary.read:not(.checked){background: var(--TWPT-thread-read-background, #ecedee)!important;}');
202 }
203
204 if (options.stickysidebarheaders) {
205 injectStyles(
206 'material-drawer .main-header{background: var(--TWPT-drawer-background, #fff)!important; position: sticky; top: 0; z-index: 1;}');
207 }
208
209 if (options.enhancedannouncementsdot) {
210 injectStylesheet(
211 chrome.runtime.getURL('css/enhanced_announcements_dot.css'));
212 }
213
214 if (options.repositionexpandthread) {
Adrià Vilanova Martínez27c69962021-07-17 23:32:51 +0200215 injectStylesheet(chrome.runtime.getURL('css/reposition_expand_thread.css'));
Adrià Vilanova Martínez3465e772021-07-11 19:18:41 +0200216 }
217
218 if (options.ccforcehidedrawer) {
219 var drawer = document.querySelector('material-drawer');
220 if (drawer !== null && drawer.classList.contains('mat-drawer-expanded')) {
221 document.querySelector('.material-drawer-button').click();
222 }
223 }
224
225 if (options.batchlock) {
226 injectScript(chrome.runtime.getURL('batchLockInject.bundle.js'));
227 injectStylesheet(chrome.runtime.getURL('css/batchlock_inject.css'));
228 }
229
230 if (options.threadlistavatars) {
Adrià Vilanova Martínez27c69962021-07-17 23:32:51 +0200231 injectStylesheet(chrome.runtime.getURL('css/thread_list_avatars.css'));
Adrià Vilanova Martínez3465e772021-07-11 19:18:41 +0200232 }
233
234 if (options.autorefreshlist) {
235 injectStylesheet(chrome.runtime.getURL('css/autorefresh_list.css'));
236 }
237});