Wiki source code of SingleFile Archief

Version 1.1 by XWikiGuest on 2026/03/11 19:24

Hide last authors
XWikiGuest 1.1 1 = SingleFile Archief =
2
3 Opgeslagen webpagina's via SingleFile + Nextcloud WebDAV.
4
5 {{html clean="false"}}
6 <style>
7 #sf-container {
8 display: flex;
9 height: 75vh;
10 border: 1px solid #4e5d6c;
11 border-radius: 4px;
12 overflow: hidden;
13 background: #1a2a3a;
14 }
15 #sf-filelist {
16 width: 30%;
17 min-width: 150px;
18 max-width: 70%;
19 overflow-y: auto;
20 background: #2b3e50;
21 border-right: none;
22 }
23 #sf-divider {
24 width: 6px;
25 background: #4e5d6c;
26 cursor: col-resize;
27 flex-shrink: 0;
28 transition: background 0.2s;
29 }
30 #sf-divider:hover, #sf-divider.dragging {
31 background: #df691a;
32 }
33 #sf-viewer {
34 flex: 1;
35 background: #fff;
36 min-width: 200px;
37 }
38 #sf-viewer iframe {
39 width: 100%;
40 height: 100%;
41 border: none;
42 }
43 #sf-filelist .sf-search {
44 width: calc(100% - 16px);
45 margin: 8px;
46 padding: 6px 10px;
47 background: #1a2a3a;
48 color: #ebebeb;
49 border: 1px solid #4e5d6c;
50 border-radius: 4px;
51 font-size: 14px;
52 }
53 #sf-filelist .sf-search::placeholder { color: #6b7c8d; }
54 #sf-filelist ul {
55 list-style: none;
56 padding: 0;
57 margin: 0;
58 }
59 #sf-filelist li {
60 padding: 6px 12px;
61 cursor: pointer;
62 color: #5bc0de;
63 font-size: 13px;
64 border-bottom: 1px solid rgba(78,93,108,0.3);
65 white-space: nowrap;
66 overflow: hidden;
67 text-overflow: ellipsis;
68 }
69 #sf-filelist li:hover { background: #3b4e60; }
70 #sf-filelist li.active { background: #4e5d6c; color: #df691a; font-weight: bold; }
71 #sf-filelist .sf-status { color: #6b7c8d; padding: 12px; font-size: 13px; }
72 #sf-viewer .sf-placeholder {
73 display: flex;
74 align-items: center;
75 justify-content: center;
76 height: 100%;
77 color: #6b7c8d;
78 font-size: 16px;
79 background: #2b3e50;
80 }
81 </style>
82
83 <div id="sf-container">
84 <div id="sf-filelist">
85 <input type="text" class="sf-search" id="sf-search" placeholder="Zoeken..." oninput="sfFilter()">
86 <div class="sf-status" id="sf-status">Laden...</div>
87 <ul id="sf-list"></ul>
88 </div>
89 <div id="sf-divider"></div>
90 <div id="sf-viewer">
91 <div class="sf-placeholder" id="sf-placeholder">Klik op een pagina om te bekijken</div>
92 </div>
93 </div>
94
95 <script>
96 (function() {
97 var WEBDAV_BASE = 'https://cloud.rhebergen.org/remote.php/dav/files/jan_rhebergen/SingleFile/';
98 var AUTH = 'Basic amFuX3JoZWJlcmdlbjpAWnMzUUwkJmZ4TVE=';
99 var allFiles = [];
100
101 // --- Load file list via PROPFIND ---
102 function loadFiles() {
103 var xhr = new XMLHttpRequest();
104 xhr.open('PROPFIND', WEBDAV_BASE, true);
105 xhr.setRequestHeader('Depth', '1');
106 xhr.setRequestHeader('Authorization', AUTH);
107 xhr.onload = function() {
108 if (xhr.status >= 200 && xhr.status < 300 || xhr.status === 207) {
109 var parser = new DOMParser();
110 var xml = parser.parseFromString(xhr.responseText, 'text/xml');
111 var responses = xml.querySelectorAll('response');
112 allFiles = [];
113 responses.forEach(function(resp) {
114 var href = resp.querySelector('href').textContent;
115 var filename = decodeURIComponent(href.split('/').pop());
116 if (!filename || filename === '') return;
117 if (!filename.match(/\.html?$/i)) return;
118 var lm = resp.querySelector('getlastmodified');
119 var date = lm ? lm.textContent : '';
120 allFiles.push({ name: filename, href: href, date: date });
121 });
122 allFiles.sort(function(a, b) { return b.date.localeCompare(a.date); });
123 document.getElementById('sf-status').textContent = allFiles.length + ' pagina\'s';
124 renderList(allFiles);
125 } else {
126 document.getElementById('sf-status').textContent = 'Fout: HTTP ' + xhr.status;
127 }
128 };
129 xhr.onerror = function() {
130 document.getElementById('sf-status').textContent = 'Verbindingsfout (CORS?)';
131 };
132 xhr.send('<?xml version="1.0" encoding="UTF-8"?><d:propfind xmlns:d="DAV:"><d:prop><d:getlastmodified/></d:prop></d:propfind>');
133 }
134
135 function renderList(files) {
136 var ul = document.getElementById('sf-list');
137 ul.innerHTML = '';
138 files.forEach(function(f) {
139 var li = document.createElement('li');
140 var title = f.name.replace(/\.html?$/i, '').replace(/_/g, ' ');
141 li.textContent = title;
142 li.title = f.name + (f.date ? ' (' + new Date(f.date).toLocaleDateString('nl-NL') + ')' : '');
143 li.onclick = function() {
144 document.querySelectorAll('#sf-list li').forEach(function(el) { el.className = ''; });
145 li.className = 'active';
146 loadPage(f.name);
147 };
148 ul.appendChild(li);
149 });
150 }
151
152 // --- Load and render a page ---
153 function loadPage(filename) {
154 var placeholder = document.getElementById('sf-placeholder');
155 if (placeholder) placeholder.textContent = 'Laden...';
156
157 fetch(WEBDAV_BASE + encodeURIComponent(filename), {
158 headers: { 'Authorization': AUTH }
159 })
160 .then(function(r) { return r.text(); })
161 .then(function(html) {
162 var viewer = document.getElementById('sf-viewer');
163 viewer.innerHTML = '';
164 var iframe = document.createElement('iframe');
165 iframe.sandbox = 'allow-same-origin';
166 viewer.appendChild(iframe);
167 iframe.srcdoc = html;
168 })
169 .catch(function(err) {
170 var viewer = document.getElementById('sf-viewer');
171 viewer.innerHTML = '<div class="sf-placeholder">Fout: ' + err.message + '</div>';
172 });
173 }
174
175 // --- Search filter ---
176 window.sfFilter = function() {
177 var q = document.getElementById('sf-search').value.toLowerCase();
178 if (!q) { renderList(allFiles); return; }
179 renderList(allFiles.filter(function(f) { return f.name.toLowerCase().indexOf(q) >= 0; }));
180 };
181
182 // --- Resizable divider ---
183 var divider = document.getElementById('sf-divider');
184 var filelist = document.getElementById('sf-filelist');
185 var dragging = false;
186
187 divider.addEventListener('mousedown', function(e) {
188 dragging = true;
189 divider.classList.add('dragging');
190 e.preventDefault();
191 });
192
193 document.addEventListener('mousemove', function(e) {
194 if (!dragging) return;
195 var container = document.getElementById('sf-container');
196 var rect = container.getBoundingClientRect();
197 var newWidth = e.clientX - rect.left;
198 var minW = 150, maxW = rect.width * 0.7;
199 if (newWidth >= minW && newWidth <= maxW) {
200 filelist.style.width = newWidth + 'px';
201 }
202 });
203
204 document.addEventListener('mouseup', function() {
205 if (dragging) {
206 dragging = false;
207 divider.classList.remove('dragging');
208 }
209 });
210
211 loadFiles();
212 })();
213 </script>
214 {{/html}}
215