forked from anoved/Columnate
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathcolumnate-wip
227 lines (196 loc) · 9.44 KB
/
columnate-wip
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
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
function Columnate() {
// Apply columnate stylesheets to document
var LoadStylesheet = function(url) {
var c = document.createElement('link');
c.rel = 'stylesheet';
c.type = 'text/css';
c.media = 'all';
c.href = url;
document.getElementsByTagName('head')[0].appendChild(c);
console.log('Stylesheet loaded: ' + url);
};
// Function to clean problematic attributes
var CleanHTML = function(doc) {
var elements = doc.querySelectorAll('*');
for (var i = 0; i < elements.length; i++) {
var el = elements[i];
var attributes = el.attributes;
for (var j = attributes.length - 1; j >= 0; j--) {
var attr = attributes[j];
if (attr.name.indexOf('@') === 0 || attr.name.indexOf('v-') === 0 || attr.name.indexOf(':') !== -1) {
el.removeAttribute(attr.name);
}
}
}
console.log('HTML cleaned of problematic attributes.');
};
var UnfoldSections = function(doc) {
try {
// Remove 'hidden="until-found"' attributes
var hiddenElements = doc.querySelectorAll('[hidden="until-found"]');
for (var i = 0; i < hiddenElements.length; i++) {
hiddenElements[i].removeAttribute('hidden');
console.log('Removed hidden="until-found" attribute:', hiddenElements[i]);
}
// Handle Wikipedia-specific collapsible toggles
var collapsibleToggles = doc.getElementsByClassName("mw-collapsible-toggle-collapsed");
for (var j = 0; j < collapsibleToggles.length; j++) {
var toggle = collapsibleToggles[j];
if (toggle.childNodes[1] && typeof toggle.childNodes[1].click === "function") {
toggle.childNodes[1].click();
console.log("Clicked to expand a collapsible section:", toggle);
}
}
// Handle other generic collapsible elements
var unfoldSelectors = [
'.collapse', // Bootstrap-style collapsible content
'.expandable', // Generic expandable content
'[aria-expanded="false"]' // ARIA attributes for collapsed sections
];
for (var k = 0; k < unfoldSelectors.length; k++) {
var elements = doc.querySelectorAll(unfoldSelectors[k]);
for (var l = 0; l < elements.length; l++) {
var el = elements[l];
if (el.getAttribute('aria-expanded') === 'false') {
el.setAttribute('aria-expanded', 'true');
}
if (typeof el.click === 'function') {
el.click();
}
console.log("Expanded section:", el);
}
}
console.log("Collapsible sections unfolded.");
} catch (error) {
console.error("Error unfolding sections:", error);
}
};
// Function to force load all images
var LoadAllImages = function(doc) {
var images = doc.querySelectorAll('img');
images.forEach(function(img) {
img.setAttribute('loading', 'eager');
img.src = img.src; // Trigger image load
});
console.log('All images set to load eagerly.');
};
// Function to set the color scheme based on the time
var SetColorScheme = function() {
var hour = new Date().getHours();
var prefersDark = (hour >= 18 || hour < 6);
if (prefersDark) {
document.documentElement.setAttribute('data-theme', 'dark');
} else {
document.documentElement.setAttribute('data-theme', 'light');
}
console.log('Color scheme set based on time: ' + (prefersDark ? 'dark' : 'light'));
};
// Function to retrieve the hero image
var getHeroImage = function(document, article) {
const images = document.querySelectorAll('img');
let heroImage = null;
// Criteria arrays for ease of customization
const largeDimensions = { width: 600, height: 300 };
const prominentClasses = ['hero', 'featured', 'main-image'];
const descriptiveAltKeywords = ['article', 'hero'];
const exclusionClasses = ['logo', 'icon', 'thumbnail', 'header', 'footer', 'sidebar'];
const exclusionPatterns = ['logo', 'icon', 'thumbnail'];
images.forEach(img => {
const width = img.naturalWidth;
const height = img.naturalHeight;
const altText = img.alt.toLowerCase();
const src = img.src.toLowerCase();
const className = img.className.toLowerCase();
const id = img.id.toLowerCase();
// Criteria for hero image
const isLarge = width > largeDimensions.width && height > largeDimensions.height;
const isProminent = prominentClasses.some(cls => className.includes(cls) || id.includes(cls));
const hasDescriptiveAlt = descriptiveAltKeywords.some(keyword => altText.includes(keyword)) || altText.length > 10;
// Exclusion criteria
const isSmall = width < 300 || height < 200;
const isRepetitive = exclusionPatterns.some(pattern => src.includes(pattern));
const isInNonContentArea = exclusionClasses.some(cls => className.includes(cls));
if ((isLarge || isProminent || hasDescriptiveAlt) && !(isSmall || isRepetitive || isInNonContentArea)) {
heroImage = img;
}
});
if (heroImage && !article.content.includes(heroImage.outerHTML)) {
const articleElement = document.querySelector('#article-title');
if (articleElement) {
articleElement.insertAdjacentHTML('beforebegin', `<img src="${heroImage.src}" alt="${heroImage.alt}" class="${heroImage.className}" id="${heroImage.id}" />`);
}
}
return heroImage ? heroImage.src : null;
};
// Callback that will replace document content with readable version
var MakeReadable = function() {
// Unfold collapsible sections
UnfoldSections(document);
// Force load all images
LoadAllImages(document);
var doclone = document.cloneNode(true);
// Clean problematic attributes from the document clone
CleanHTML(doclone);
try {
var article = new Readability(doclone).parse();
// Retrieve and inject hero image if applicable
getHeroImage(document, article);
// Strip stray styling from the html tag itself
var htmltag = document.getElementsByTagName("html")[0];
htmltag.removeAttribute("class");
htmltag.removeAttribute("style");
// Reset head to nothing but our stylesheets
document.head.innerHTML = "";
// Add the meta tag for viewport settings
var metaTag = document.createElement('meta');
metaTag.name = 'viewport';
metaTag.content = 'width=device-width, initial-scale=1.0, user-scalable=no';
document.head.appendChild(metaTag);
// Load stylesheets
LoadStylesheet('//eink-reader.netlify.app/columnate.css');
LoadStylesheet('//eink-reader.netlify.app/appearance.css');
// Set color scheme based on time
SetColorScheme();
document.title = article.title;
// Reset body html to nothing but reformatted content
document.body.removeAttribute("class");
document.body.removeAttribute("style");
document.body.innerHTML = "<h1 id='article-title'>" + article.title + "</h1><h2 id='article-byline'>" + article.byline + "</h2><h3 id='article-excerpt'>" + article.excerpt + "</h3>" + article.content;
console.log('Document made readable and styles applied.');
// After content is ready, load the navigation.js script
loadNavigationScript();
} catch (error) {
console.error('Error parsing document with Readability:', error);
}
};
// Function to load the navigation.js script
var loadNavigationScript = function() {
var navScript = document.createElement('script');
navScript.type = 'text/javascript';
navScript.src = '//eink-reader.netlify.app/navigation.js'; // Replace with actual path to navigation.js
navScript.onload = function() {
console.log('navigation.js loaded and ready!');
if (typeof initNavigation === 'function') {
console.log('Executing initNavigation...');
initNavigation();
} else {
console.log('initNavigation function not found in navigation.js');
}
};
navScript.onerror = function() {
console.error('Failed to load navigation.js');
};
document.getElementsByTagName('head')[0].appendChild(navScript);
};
// Load readability script and set it to be applied when loaded
var cmjs = document.createElement('script');
cmjs.type = 'text/javascript';
cmjs.src = '//eink-reader.netlify.app/Readability.js';
cmjs.onreadystatechange = MakeReadable;
cmjs.onload = MakeReadable;
cmjs.onerror = function() {
console.error('Failed to load Readability.js');
};
document.getElementsByTagName('head')[0].appendChild(cmjs);
console.log('Readability script loading initiated.');
}