Wiki source code of Linkding + SingleFile Archief

Version 14.1 by XWikiGuest on 2026/03/11 21:03

Show last authors
1 = Linkding + SingleFile Archief =
2
3 Bookmarks uit Linkding met inline archief-viewer. Klik op 📄 om de opgeslagen versie te bekijken.
4
5 {{html clean="false"}}
6 <style>
7 #ldsf-container {
8 display: flex;
9 height: 75vh;
10 border: 1px solid #4e5d6c;
11 border-radius: 4px;
12 overflow: hidden;
13 }
14 #ldsf-left {
15 width: 35%;
16 min-width: 200px;
17 max-width: 60%;
18 overflow-y: auto;
19 background: #2b3e50;
20 display: flex;
21 flex-direction: column;
22 }
23 #ldsf-divider {
24 width: 6px;
25 background: #4e5d6c;
26 cursor: col-resize;
27 flex-shrink: 0;
28 transition: background 0.2s;
29 }
30 #ldsf-divider:hover, #ldsf-divider.dragging { background: #df691a; }
31 #ldsf-right {
32 flex: 1;
33 min-width: 200px;
34 display: flex;
35 flex-direction: column;
36 }
37 #ldsf-status {
38 color: #6b7c8d;
39 font-size: 13px;
40 padding: 8px 12px;
41 flex-shrink: 0;
42 }
43 #ldsf-bookmarks {
44 overflow-y: auto;
45 flex: 1;
46 }
47 #ldsf-bookmarks ul {
48 list-style: none;
49 padding: 0;
50 margin: 0;
51 }
52 #ldsf-bookmarks li {
53 padding: 6px 12px;
54 border-bottom: 1px solid rgba(78,93,108,0.3);
55 display: flex;
56 align-items: center;
57 gap: 8px;
58 }
59 #ldsf-bookmarks li:hover { background: #3b4e60; }
60 .sf-num {
61 display: inline-flex;
62 align-items: center;
63 justify-content: center;
64 width: 22px;
65 height: 22px;
66 border-radius: 50%;
67 background: #4e5d6c;
68 color: #df691a;
69 font-size: 11px;
70 font-weight: bold;
71 flex-shrink: 0;
72 }
73 #ldsf-bookmarks a {
74 color: #5bc0de;
75 text-decoration: none;
76 overflow: hidden;
77 text-overflow: ellipsis;
78 white-space: nowrap;
79 font-size: 13px;
80 }
81 #ldsf-bookmarks a:hover { color: #df691a; }
82 .sf-btn {
83 cursor: pointer;
84 background: #4e5d6c;
85 border: none;
86 color: #5bc0de;
87 padding: 2px 6px;
88 border-radius: 3px;
89 font-size: 13px;
90 flex-shrink: 0;
91 }
92 .sf-btn:hover { background: #df691a; color: #fff; }
93 #ldsf-viewer-bar {
94 display: none;
95 justify-content: space-between;
96 align-items: center;
97 padding: 4px 12px;
98 background: #2b3e50;
99 color: #ebebeb;
100 font-size: 12px;
101 flex-shrink: 0;
102 border-bottom: 1px solid #4e5d6c;
103 }
104 #ldsf-viewer-bar .close-btn {
105 cursor: pointer;
106 color: #d9534f;
107 font-size: 18px;
108 padding: 0 4px;
109 }
110 #ldsf-viewer-bar .close-btn:hover { color: #ff6b6b; }
111 #ldsf-viewer-frame {
112 width: 100%;
113 flex: 1;
114 border: none;
115 background: #fff;
116 display: none;
117 }
118 #ldsf-placeholder {
119 display: flex;
120 align-items: center;
121 justify-content: center;
122 flex: 1;
123 color: #6b7c8d;
124 font-size: 15px;
125 background: #2b3e50;
126 }
127 </style>
128
129 <div id="ldsf-container">
130 <div id="ldsf-left">
131 <div id="ldsf-status">Laden...</div>
132 <div id="ldsf-bookmarks"></div>
133 </div>
134 <div id="ldsf-divider"></div>
135 <div id="ldsf-right">
136 <div id="ldsf-viewer-bar">
137 <span id="ldsf-viewer-title"></span>
138 <span class="close-btn" data-sfclose="1">&times;</span>
139 </div>
140 <div id="ldsf-placeholder">Klik op &#128196; om een archief te bekijken</div>
141 <iframe id="ldsf-viewer-frame" sandbox="allow-same-origin"></iframe>
142 </div>
143 </div>
144
145 <script>
146 /* --- Configuration --- */
147 var LDSF = {
148 linkding: 'https://bookmarks.rhebergen.org/api/bookmarks/',
149 token: '3b7443e0f84e2b2b269adebb96d7475e4a5e653e',
150 tag: 'Nuclear-&-Energy',
151 count: 20,
152 share: 'eT2X9ttBHK5GoEY',
153 webdav: 'https://cloud.rhebergen.org/public.php/webdav/'
154 };
155 LDSF.auth = 'Basic ' + btoa(LDSF.share + ':');
156
157 /* --- State --- */
158 var ldsf_urlToFile = {};
159 var ldsf_fileMap = {};
160
161 /* --- URL matching (exact, with/without slash, without query string) --- */
162 function ldsfMatch(url) {
163 var tries = [url, url.replace(/\/$/, ''), url + '/', url.replace(/\?.*$/, '')];
164 for (var i = 0; i < tries.length; i++) {
165 if (ldsf_urlToFile[tries[i]]) return ldsf_urlToFile[tries[i]];
166 }
167 return null;
168 }
169
170 /* --- Show archived page in viewer --- */
171 function ldsfShow(filename) {
172 document.getElementById('ldsf-viewer-title').textContent = filename.replace(/\.html?$/i, '');
173 document.getElementById('ldsf-viewer-bar').style.display = 'flex';
174 document.getElementById('ldsf-placeholder').style.display = 'none';
175 var frame = document.getElementById('ldsf-viewer-frame');
176 frame.style.display = 'block';
177 frame.srcdoc = '<p style="padding:20px;color:#888">Laden...</p>';
178 fetch(LDSF.webdav + encodeURIComponent(filename), { headers: { 'Authorization': LDSF.auth } })
179 .then(function(r) { return r.text(); })
180 .then(function(html) { frame.srcdoc = html; })
181 .catch(function(e) { frame.srcdoc = '<p style="padding:20px;color:#d9534f">Fout: ' + e.message + '</p>'; });
182 }
183
184 /* --- Close viewer --- */
185 function ldsfClose() {
186 document.getElementById('ldsf-viewer-bar').style.display = 'none';
187 document.getElementById('ldsf-viewer-frame').style.display = 'none';
188 document.getElementById('ldsf-placeholder').style.display = 'flex';
189 }
190
191 /* --- Render bookmark list --- */
192 function ldsfRender(data) {
193 var total = data.count || 0, archived = 0;
194 var container = document.getElementById('ldsf-bookmarks');
195 var html = '<ul>';
196 (data.results || []).forEach(function(bm, i) {
197 var title = bm.title || bm.website_title || bm.url;
198 var file = ldsfMatch(bm.url);
199 if (file) { archived++; ldsf_fileMap[i] = file; }
200 html += '<li><span class="sf-num">' + (i + 1) + '</span>';
201 if (file) html += '<span class="sf-btn" data-sf="' + i + '" title="Archief bekijken">&#128196;</span>';
202 html += '<a href="' + bm.url + '" target="_blank">' + title + '</a></li>';
203 });
204 container.innerHTML = html + '</ul>';
205 document.getElementById('ldsf-status').textContent = total + ' bookmarks, ' + archived + ' met archief';
206 }
207
208 /* --- Click handler (event delegation, works in XWiki html macro) --- */
209 document.addEventListener('click', function(e) {
210 var t = e.target;
211 if (!t || !t.getAttribute) return;
212 var sf = t.getAttribute('data-sf');
213 if (sf !== null && ldsf_fileMap[sf]) ldsfShow(ldsf_fileMap[sf]);
214 if (t.getAttribute('data-sfclose') !== null) ldsfClose();
215 }, true);
216
217 /* --- Draggable divider --- */
218 var ldsf_drag = false;
219 document.getElementById('ldsf-divider').addEventListener('mousedown', function(e) {
220 ldsf_drag = true;
221 this.classList.add('dragging');
222 e.preventDefault();
223 });
224 document.addEventListener('mousemove', function(e) {
225 if (!ldsf_drag) return;
226 var box = document.getElementById('ldsf-container').getBoundingClientRect();
227 var w = e.clientX - box.left;
228 if (w >= 200 && w <= box.width * 0.6)
229 document.getElementById('ldsf-left').style.width = w + 'px';
230 });
231 document.addEventListener('mouseup', function() {
232 if (ldsf_drag) {
233 ldsf_drag = false;
234 document.getElementById('ldsf-divider').classList.remove('dragging');
235 }
236 });
237
238 /* --- Load index, then bookmarks --- */
239 fetch(LDSF.webdav + 'index.json', { headers: { 'Authorization': LDSF.auth } })
240 .then(function(r) { return r.ok ? r.json() : {}; })
241 .then(function(idx) {
242 for (var f in idx) { if (idx.hasOwnProperty(f)) ldsf_urlToFile[idx[f]] = f; }
243 })
244 .catch(function() {})
245 .then(function() {
246 return fetch(LDSF.linkding + '?limit=' + LDSF.count + '&q=%23' + encodeURIComponent(LDSF.tag),
247 { headers: { 'Authorization': 'Token ' + LDSF.token } });
248 })
249 .then(function(r) { return r.json(); })
250 .then(ldsfRender)
251 .catch(function(e) {
252 document.getElementById('ldsf-status').textContent = 'Fout: ' + e.message;
253 });
254 </script>
255 {{/html}}