-
Notifications
You must be signed in to change notification settings - Fork 9
/
searchInjection.js
203 lines (183 loc) · 6.43 KB
/
searchInjection.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
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
function isChrome() {
return typeof chrome !== "undefined";
}
function getBrowser() {
return isChrome() ? chrome : browser;
}
/* Sanitise input to prevent unwanted injection of html or even javascript
through linkding search results, e.g. in the bookmark title or description
*/
function escapeHTML(str) {
let p = document.createElement("p");
p.appendChild(document.createTextNode(str));
return p.innerHTML;
}
const browser = getBrowser();
let port = browser.runtime.connect({ name: "port-from-cs" });
let searchEngine;
if (document.location.hostname.match(/duckduckgo/)) {
searchEngine = "duckduckgo";
} else if (document.location.hostname.match(/google/)) {
searchEngine = "google";
} else if (document.location.hostname.match(/search.brave.com/)) {
searchEngine = "brave";
} else if (document.location.href.match(/http.?:\/\/.+\/search/)) {
searchEngine = "searx";
}
// When background script answers with results, construct html for the result box
port.onMessage.addListener(function (m) {
const parser = new DOMParser();
let theme, themeClass;
let htmlString = "";
let html;
// In case we don't get results, but a message from the background script,
// display it. This is the case before proper configuration
if ("message" in m) {
htmlString = `
<div id="bookmark-list-container" class="${searchEngine}">
<div id="navbar">
<a id="ld-logo">
<img src=${browser.runtime.getURL("icons/logo.svg")} class="setup" />
<h1>linkding injector</h1>
</a>
<a id="ld-options" class="openOptions">
<img class="ld-settings" src=${browser.runtime.getURL(
"icons/cog.svg"
)} />
</a>
</div>
<div id="error-message">
${m.message}
</div>
</div>
`;
// Convert the above string into a DOM document
html = parser.parseFromString(htmlString, "text/html");
}
// If there is no message and there are actual results display them
else if (m.results.length > 0) {
// If the theme for a search engine is not set to auto, we need to add
// specific CSS classes
// Get the theme configuration
switch (searchEngine) {
case "duckduckgo":
theme = m.config.themeDuckduckgo;
break;
case "google":
theme = m.config.themeGoogle;
break;
case "brave":
theme = m.config.themeBrave;
break;
case "searx":
theme = m.config.themeSearx;
break;
}
if (theme == "auto") {
themeClass = ""; // automatic theme detection
} else {
themeClass = theme; // "dark" for dark theme, "light" for light theme
}
// URL of the configured linkding instance (including search term)
let linkdingUrl =
m.config.baseUrl +
(searchTerm.length > 0 ? `/bookmarks?q=${searchTerm}` : "/");
htmlString += `
<div id="bookmark-list-container" class="${searchEngine} ${themeClass}">
<div id="navbar">
<a id="ld-logo" href="${linkdingUrl}">
<img src=${browser.runtime.getURL("icons/logo.svg")} />
<h1>linkding injector</h1>
</a>
<div id="results_amount">
Found <span>${m.results.length}</span> ${
m.results.length == 1 ? "result" : "results"
}.
</div>
<a id="ld-options" class="openOptions">
<img class="ld-settings" src=${browser.runtime.getURL(
"icons/cog.svg"
)} />
</a>
</div>
`;
htmlString += `<ul id="bookmark-list">`;
m.results.forEach((bookmark) => {
htmlString += `
<li>
<div class="title">
<a
href="${bookmark.url}"
target=${m.config.openLinkType == "sameTab" ? "_self" : "_blank"}
rel="noopener"
>${escapeHTML(bookmark.title)}</a
>
</div>
<div class="description ${themeClass}">
<span class="tags">
${bookmark.tags
.map((tag) => {
return "<a>#" + escapeHTML(tag) + "</a>";
})
.join(" ")}
</a>
</span>
${bookmark.tags.length > 0 ? "|" : ""}
<span>
${escapeHTML(bookmark.description)}
</span>
</div>
</li>`;
});
htmlString += `</ul></div>`;
} else {
console.error("linkding injector: no message and no search results");
return;
}
let sidebar; // DOM document for the sidebar
// querySelectors for finding the sidebar in the search engine websites
if (searchEngine == "duckduckgo") {
sidebar = document.querySelector(".sidebar-modules");
} else if (searchEngine == "google") {
sidebar = document.querySelector("#rhs");
if (sidebar == null) {
// google completely omits the sidebar container if there is no content.
// we need to add it manually before injection
let sidebarContainerString = `
<div id="rhs" class="TQc1id hSOk2e rhstc4"></div>`;
// construct DOM document from string
let sidebarContainer = parser.parseFromString(
sidebarContainerString,
"text/html"
);
let container = document.querySelector("#rcnt"); // get main search result container
container.appendChild(sidebarContainer.body.querySelector("div"));
sidebar = document.querySelector("#rhs"); // get the added sidebar container
}
} else if (searchEngine == "brave") {
sidebar = document.querySelector("#side-right");
} else if (searchEngine == "searx") {
sidebar = document.querySelector("#sidebar");
}
// Convert the html string into a DOM document
html = parser.parseFromString(htmlString, "text/html");
// The actual injection
if (document.querySelector("#bookmark-list-container") == null) {
sidebar.prepend(html.body.querySelector("div"));
}
// Event listeners for opening the extension options. These can only be opened
// by the background script, so we need to send a message to it
document.querySelectorAll(".openOptions").forEach((el) => {
el.addEventListener("click", () => {
port.postMessage({ action: "openOptions" });
});
});
});
// Start the search by sending a message to background.js with the search term
let queryString = location.search;
let urlParams = new URLSearchParams(queryString);
let searchTerm = urlParams.get("q");
if (searchEngine == "searx") {
searchTerm = document.querySelector("input#q").value;
}
port.postMessage({ searchTerm: searchTerm });