0 Votes

Wiki source code of Violence against White People

Version 85.1 by Ryan C on 2025/09/11 19:12

Show last authors
1 = Violence against White people =
2
3
4 This page will serve as video evidence of the nonstop racial hatred White people face daily. These are the crimes you don't hear about from the mainstream media unless they are forced to confront them ([[and subsequently make excuses for them>>doc:Main Categories.Race.Nonwhite Privilege.WebHome]]) in order to uphold the narrative and convince Whites that it is them who are the oppressive violent ones rather than the victims.
5
6 {{velocity}}
7 #pagePicker_import
8
9 ## 1) Collect video attachments on this page
10 #set($videoExts = ['mp4','webm','ogg','avi','mov','wmv','flv','m4v'])
11 #set($videos = [])
12 #foreach($att in $doc.getAttachmentList())
13 #set($n = $att.getFilename())
14 #set($ln = $n.toLowerCase())
15 #foreach($e in $videoExts)
16 #if($ln.endsWith("." + $e))
17 #set($discard = $videos.add($att))
18 #break
19 #end
20 #end
21 #end
22
23 ## 2) Cache HTML (auto-bust on version)
24 {{cache id="vid-list-$doc.fullName-$doc.version" timeToLive="21600"}}
25 {{html wiki="false" clean="false"}}
26 <div id="xwiki-video-manager" style="margin:20px 0;">
27 <h2>📹 Videos on: ${escapetool.xml($doc.fullName)}</h2>
28
29 #if($videos.size() == 0)
30 <div style="text-align:center;padding:40px;background:#f8f9fa;border-radius:8px;">
31 <h3>No Videos Found</h3>
32 <p>Attach video files to this page to see them here.</p>
33 </div>
34 #else
35
36 <!-- Bulk toolbar -->
37 <div id="bulk-bar" class="bulk-bar">
38 <label style="margin-right:8px;">
39 <input type="checkbox" id="pick-all"> Select visible
40 </label>
41 <span class="sep"></span>
42 <input type="text" class="bulk-dest suggest-pages" placeholder="Find a page…"
43 data-search-scope="wiki:${escapetool.xml($xcontext.database)}">
44 <button type="button" class="btn btn-sm btn-warning" id="bulk-move">Move selected</button>
45 <button type="button" class="btn btn-sm btn-danger" id="bulk-delete">Delete selected</button>
46 <span id="bulk-status" class="bulk-status"></span>
47 </div>
48
49 <script>
50 window.VID_CHUNK_SIZE = 48;
51 window.VID_LAZY_MARGIN = '600px';
52 window.XWIKI_WIKI = ${jsontool.serialize($xcontext.database)};
53 window.SOURCE_SPACE = ${jsontool.serialize($doc.space)};
54 window.SOURCE_PAGE = ${jsontool.serialize($doc.name)};
55 </script>
56
57 <div id="video-chunks">
58 #set($i = 0)
59 #set($chunkIndex = 0)
60 #foreach($att in $videos)
61 #set($i = $i + 1)
62 #set($filename = $att.getFilename())
63 #set($lname = $filename.toLowerCase())
64 #set($url = $doc.getAttachmentURL($filename))
65
66 ## MIME guess
67 #set($videoType = "video/mp4")
68 #if($lname.endsWith(".webm"))
69 #set($videoType = "video/webm")
70 #elseif($lname.endsWith(".ogg"))
71 #set($videoType = "video/ogg")
72 #elseif($lname.endsWith(".avi"))
73 #set($videoType = "video/x-msvideo")
74 #elseif($lname.endsWith(".mov"))
75 #set($videoType = "video/quicktime")
76 #elseif($lname.endsWith(".wmv"))
77 #set($videoType = "video/x-ms-wmv")
78 #elseif($lname.endsWith(".flv"))
79 #set($videoType = "video/x-flv")
80 #elseif($lname.endsWith(".m4v"))
81 #set($videoType = "video/mp4")
82 #end
83
84 #if($i == 1 || ($i - 1) % 48 == 0)
85 #set($chunkIndex = $chunkIndex + 1)
86 <div class="vid-chunk" data-chunk="$chunkIndex" style="display: #if($chunkIndex == 1) block #else none #end;">
87 <div class="video-display-grid">
88 #end
89
90 <div class="video-container">
91 <div class="video-header">
92 <label class="pick"><input type="checkbox" class="vid-pick" data-filename="${escapetool.xml($filename)}"></label>
93 <h4 class="video-title">${escapetool.xml($filename)}</h4>
94 </div>
95
96 <div class="video-frame"
97 data-src="${url}"
98 data-type="${videoType}"
99 data-name="${escapetool.xml($filename)}">
100 <canvas class="vid-canvas" width="320" height="180"></canvas>
101 <button class="btn btn-sm btn-primary" type="button">Load &amp; Play</button>
102 </div>
103
104 <div class="video-controls">
105 <a href="${url}" download="${escapetool.xml($filename)}" class="btn btn-sm btn-success">📥 Download</a>
106 <button class="btn btn-sm btn-danger del-one" data-filename="${escapetool.xml($filename)}">Delete</button>
107 <span class="vid-duration">Duration: —</span>
108 </div>
109
110 <!-- Move-to-page -->
111 <div class="move-box">
112 <label>Move to page:</label>
113 <div class="move-row">
114 <input type="text"
115 class="move-input suggest-pages"
116 placeholder="Find a page…"
117 data-search-scope="wiki:${escapetool.xml($xcontext.database)}"
118 data-filename="${escapetool.xml($filename)}">
119 <button type="button"
120 class="btn btn-sm btn-warning move-go"
121 data-filename="${escapetool.xml($filename)}">Move</button>
122 </div>
123 <small class="hint">Pick a page (e.g., <code>Main.SomePage</code>). Click <b>Move</b> to relocate this file.</small>
124 </div>
125 </div> <!-- .video-container -->
126
127 #if(($i % 48 == 0) || $foreach.last)
128 </div> <!-- .video-display-grid -->
129 #if(!$foreach.last)
130 <div class="loadmore-wrap">
131 <button class="btn btn-secondary load-more" data-next="$mathtool.add($chunkIndex,1)">Load more</button>
132 </div>
133 #end
134 </div> <!-- .vid-chunk -->
135 #end
136 #end
137 </div>
138 #end
139 </div>
140
141 <script>
142 (function(){
143 /* ===== constants & helpers ===== */
144 var WIKI = window.XWIKI_WIKI;
145 var CURRENT_SPACE = document.documentElement.getAttribute('data-xwiki-space') || (window.SOURCE_SPACE || 'Main');
146 var ROOT_SPACE = CURRENT_SPACE.split('.')[0];
147
148 function spacesPath(dotPath){
149 if(!dotPath) return '';
150 return dotPath.split('.').map(function(s){ return 'spaces/' + encodeURIComponent(s); }).join('/');
151 }
152 function parseFullName(full){
153 full = String(full||'').replace(/^[^:]+:/,'');
154 var parts = full.split('.');
155 var page = parts.pop();
156 return {spacePath: parts.join('.'), page: page};
157 }
158 function getFormToken(){
159 return document.documentElement.getAttribute('data-xwiki-form-token') || '';
160 }
161
162 /* ===== existence check (prevents DocumentDoesNotExist) ===== */
163 async function docExists(fullRef){
164 var p = parseFullName(fullRef);
165 var url = '/rest/wikis/' + encodeURIComponent(WIKI) + '/' +
166 spacesPath(p.spacePath) + '/pages/' + encodeURIComponent(p.page) + '?media=json';
167 var r = await fetch(url, {credentials:'same-origin'});
168 return r.status === 200;
169 }
170
171 /* ===== robust resolver for Page Picker / typed titles ===== */
172 async function resolveReference(inp){
173 var ref = inp.getAttribute('data-reference') || (inp.dataset && inp.dataset.reference) || '';
174 var raw = (inp.value || '').trim();
175
176 if (ref){
177 if (/\.WebHome$/i.test(ref)) { if (await docExists(ref)) return ref; }
178 else {
179 if (await docExists(ref)) return ref;
180 var rh = ref + '.WebHome'; if (await docExists(rh)) return rh;
181 }
182 }
183 var base = raw.indexOf('.') === -1 ? (ROOT_SPACE + '.' + raw) : raw;
184 if (await docExists(base)) return base;
185 var home2 = /\.WebHome$/i.test(base) ? base : (base + '.WebHome');
186 if (await docExists(home2)) return home2;
187
188 throw new Error('Target page not found: "' + raw + '". Choose a suggestion or type a full reference like "Main Categories.SomePage".');
189 }
190
191 /* ===== posters & video mount ===== */
192 async function makePoster(frame){
193 if(frame.getAttribute('data-poster-ready')==='1') return;
194 var src = frame.getAttribute('data-src');
195 try{
196 var v = document.createElement('video');
197 v.preload = 'metadata'; v.muted = true; v.playsInline = true; v.src = src;
198 function once(t,e){return new Promise(function(res){t.addEventListener(e,res,{once:true});});}
199 await once(v,'loadedmetadata');
200 if (typeof v.requestVideoFrameCallback === 'function'){
201 await new Promise(function(res){ v.requestVideoFrameCallback(function(){ res(); }); });
202 } else {
203 try { v.currentTime = 0.25; } catch(e){}
204 await once(v,'seeked').catch(function(){});
205 await once(v,'loadeddata').catch(function(){});
206 }
207 var canvas = frame.querySelector('.vid-canvas');
208 if(canvas){
209 var w = 320, h = Math.round(320 * (v.videoHeight||9) / (v.videoWidth||16));
210 canvas.width = 320; canvas.height = h>0?h:180;
211 var ctx = canvas.getContext('2d', {willReadFrequently:true});
212 ctx.drawImage(v, 0, 0, canvas.width, canvas.height);
213 try { frame.setAttribute('data-poster', canvas.toDataURL('image/webp', 0.85)); } catch(e){}
214 }
215 frame.setAttribute('data-poster-ready','1');
216 }catch(e){}
217 }
218
219 function mountVideo(frame){
220 if(frame.getAttribute('data-mounted')==='1') return;
221 var src = frame.getAttribute('data-src');
222 var type = frame.getAttribute('data-type') || 'video/mp4';
223 var poster = frame.getAttribute('data-poster');
224
225 var v = document.createElement('video');
226 v.setAttribute('controls',''); v.setAttribute('preload','none');
227 if(poster) v.setAttribute('poster', poster);
228 v.style.width='100%'; v.style.maxWidth='100%'; v.style.borderRadius='4px';
229
230 var s = document.createElement('source'); s.src = src; s.type = type; v.appendChild(s);
231 v.addEventListener('loadedmetadata', function(){
232 var d = Math.round(v.duration||0), mm = Math.floor(d/60), ss = String(d%60).padStart(2,'0');
233 var dur = frame.parentElement.querySelector('.vid-duration'); if(dur) dur.textContent = 'Duration: '+mm+':'+ss;
234 });
235
236 frame.replaceChildren(v);
237 frame.setAttribute('data-mounted','1');
238 }
239
240 /* ===== observers & UI wiring ===== */
241 if('IntersectionObserver' in window){
242 var io = new IntersectionObserver(function(entries){
243 entries.forEach(function(e){
244 if(e.isIntersecting){ makePoster(e.target); io.unobserve(e.target); }
245 });
246 }, { rootMargin: (window.VID_LAZY_MARGIN||'600px') });
247 document.querySelectorAll('.video-frame').forEach(function(el){ io.observe(el); });
248 }
249
250 document.addEventListener('click', function(ev){
251 var frame = ev.target.closest('.video-frame');
252 if(frame){
253 mountVideo(frame);
254 var v = frame.querySelector('video'); if(v) v.play().catch(function(){});
255 }
256 });
257
258 document.addEventListener('click', function(ev){
259 var b = ev.target.closest('.load-more'); if(!b) return;
260 var next = b.getAttribute('data-next');
261 var nxt = document.querySelector('.vid-chunk[data-chunk="'+ next +'"]');
262 if(nxt){ nxt.style.display='block'; b.parentElement.style.display='none'; }
263 });
264
265 /* ===== MOVE & DELETE core ===== */
266 async function moveAttachment(opts){
267 var srcSpace = opts.srcSpace, srcPage = opts.srcPage, filename = opts.filename, dstFull = opts.dstFull;
268 var pf = parseFullName(dstFull);
269 var srcSpacesPath = spacesPath(srcSpace);
270 var dstSpacesPath = spacesPath(pf.spacePath);
271
272 var sel = '.video-container input.move-input[data-filename="' + CSS.escape(filename) + '"]';
273 var inp = document.querySelector(sel);
274 var card = inp ? inp.closest('.video-container') : null;
275 var frame = card ? card.querySelector('.video-frame') : null;
276 var srcURL = frame ? frame.getAttribute('data-src') : null;
277 if(!srcURL) throw new Error('Missing source URL');
278
279 var downloading = await fetch(srcURL, {credentials:'same-origin'});
280 if(!downloading.ok) throw new Error('Download failed: ' + downloading.status);
281 var blob = await downloading.blob();
282
283 var token = getFormToken();
284
285 var putURL = '/rest/wikis/' + encodeURIComponent(WIKI) + '/' + dstSpacesPath +
286 '/pages/' + encodeURIComponent(pf.page) +
287 '/attachments/' + encodeURIComponent(filename) + '?media=json';
288 var uploading = await fetch(putURL, {
289 method: 'PUT',
290 body: blob,
291 headers: {'Content-Type':'application/octet-stream','XWiki-Form-Token': token},
292 credentials:'same-origin'
293 });
294 if(!(uploading.status===201 || uploading.status===202)){
295 var txt = await uploading.text().catch(function(){ return String(uploading.status); });
296 throw new Error('Upload failed: ' + txt);
297 }
298
299 var delURL = '/rest/wikis/' + encodeURIComponent(WIKI) + '/' + srcSpacesPath +
300 '/pages/' + encodeURIComponent(srcPage) +
301 '/attachments/' + encodeURIComponent(filename);
302 var deleting = await fetch(delURL, {method:'DELETE', headers:{'XWiki-Form-Token': token}, credentials:'same-origin'});
303 if(!(deleting.status===204)){ console.warn('Delete original failed', deleting.status); }
304 return true;
305 }
306
307 async function deleteAttachment(filename){
308 var token = getFormToken();
309 var srcSpacesPath = spacesPath(window.SOURCE_SPACE);
310 var url = '/rest/wikis/' + encodeURIComponent(WIKI) + '/' + srcSpacesPath +
311 '/pages/' + encodeURIComponent(window.SOURCE_PAGE) +
312 '/attachments/' + encodeURIComponent(filename);
313 var r = await fetch(url, {
314 method: 'DELETE',
315 headers: {'XWiki-Form-Token': token},
316 credentials: 'same-origin'
317 });
318 if (r.status !== 204) {
319 var t = await r.text().catch(()=>String(r.status));
320 throw new Error('Delete failed: ' + t);
321 }
322 return true;
323 }
324
325 function removeCardByFilename(filename){
326 var card = document.querySelector('.video-container input.vid-pick[data-filename="'+CSS.escape(filename)+'"]');
327 card = card ? card.closest('.video-container') : null;
328 if (card) card.remove();
329 }
330 function selectedFilenames(){
331 return Array.from(document.querySelectorAll('.vid-pick:checked'))
332 .map(ch => ch.getAttribute('data-filename'));
333 }
334
335 /* ===== Move (per-card) ===== */
336 document.addEventListener('click', function(e){
337 var btn = e.target.closest('.move-go'); if(!btn) return;
338 var box = btn.closest('.move-box');
339 var inp = box.querySelector('.move-input');
340 (async function(){
341 var notice = box.querySelector('.move-notice');
342 if(!notice){ notice = document.createElement('div'); notice.className = 'move-notice'; box.appendChild(notice); }
343 try{
344 var ref = await resolveReference(inp);
345 var filename = btn.getAttribute('data-filename');
346 notice.style.color = '#666';
347 notice.textContent = 'Moving “' + filename + '” to ' + ref + ' …';
348
349 await moveAttachment({ srcSpace: window.SOURCE_SPACE, srcPage: window.SOURCE_PAGE, filename: filename, dstFull: ref });
350
351 notice.textContent = 'Moved ✔ — reloading…';
352 setTimeout(function(){ location.reload(); }, 600);
353 }catch(err){
354 notice.style.color = '#b00020';
355 notice.textContent = 'Move failed: ' + (err && err.message ? err.message : err);
356 }
357 })();
358 });
359
360 /* ===== Delete (per-card) ===== */
361 document.addEventListener('click', function(e){
362 var btn = e.target.closest('.del-one'); if(!btn) return;
363 var filename = btn.getAttribute('data-filename');
364 if(!confirm('Delete this file permanently?\n\n' + filename)) return;
365 var notice = document.createElement('div');
366 notice.className = 'move-notice'; notice.textContent = 'Deleting…';
367 btn.parentElement.appendChild(notice);
368 deleteAttachment(filename)
369 .then(()=>{ notice.textContent = 'Deleted ✔'; removeCardByFilename(filename); })
370 .catch(err=>{ notice.style.color='#b00020'; notice.textContent = (err && err.message)||String(err); });
371 });
372
373 /* ===== Bulk: select visible ===== */
374 document.addEventListener('change', function(e){
375 if (e.target && e.target.id === 'pick-all'){
376 var on = e.target.checked;
377 document.querySelectorAll('.vid-pick').forEach(ch => { ch.checked = on; });
378 }
379 });
380
381 /* ===== Bulk: Move ===== */
382 document.getElementById('bulk-move')?.addEventListener('click', async function(){
383 var files = selectedFilenames();
384 if (!files.length) return alert('No videos selected.');
385 var destInput = document.querySelector('.bulk-dest');
386 var status = document.getElementById('bulk-status');
387 try{
388 var ref = await resolveReference(destInput);
389 if(!confirm('Move '+files.length+' file(s) to:\n\n'+ref+' ?')) return;
390 status.style.color=''; status.textContent = 'Moving 0/' + files.length + '…';
391 let done = 0;
392 for (const f of files){
393 await moveAttachment({ srcSpace: window.SOURCE_SPACE, srcPage: window.SOURCE_PAGE, filename: f, dstFull: ref });
394 done++; status.textContent = 'Moving ' + done + '/' + files.length + '…';
395 removeCardByFilename(f);
396 }
397 status.textContent = 'Move complete ✔';
398 }catch(err){
399 status.style.color = '#b00020';
400 status.textContent = 'Move failed: ' + ((err && err.message) || String(err));
401 }
402 });
403
404 /* ===== Bulk: Delete ===== */
405 document.getElementById('bulk-delete')?.addEventListener('click', async function(){
406 var files = selectedFilenames();
407 if (!files.length) return alert('No videos selected.');
408 if(!confirm('DELETE '+files.length+' file(s)? This cannot be undone.')) return;
409 var status = document.getElementById('bulk-status');
410 try{
411 status.style.color=''; status.textContent = 'Deleting 0/' + files.length + '…';
412 let done = 0;
413 for (const f of files){
414 await deleteAttachment(f);
415 done++; status.textContent = 'Deleting ' + done + '/' + files.length + '…';
416 removeCardByFilename(f);
417 }
418 status.textContent = 'Delete complete ✔';
419 }catch(err){
420 status.style.color = '#b00020';
421 status.textContent = 'Delete failed: ' + ((err && err.message) || String(err));
422 }
423 });
424
425 })();
426 </script>
427
428 <style>
429 .video-display-grid{
430 display:grid;
431 grid-template-columns:repeat(auto-fit,minmax(320px,1fr));
432 gap:20px; align-items:start;
433 }
434 .video-container{border:1px solid #ddd; border-radius:8px; padding:12px; background:#fff;}
435 .video-header{display:flex;gap:8px;align-items:center;margin-bottom:8px;}
436 .video-title{margin:0;flex:1;min-width:0;word-break:break-word;}
437 .pick{margin-right:2px}
438 .video-frame{
439 position:relative;width:100%;aspect-ratio:16/9;background:#111;border-radius:4px;
440 overflow:hidden;display:flex;align-items:center;justify-content:center;cursor:pointer;
441 }
442 .vid-canvas{position:absolute;inset:0;width:100%;height:100%;object-fit:cover;}
443 .video-controls{margin-top:8px;display:flex;flex-wrap:wrap;gap:6px;align-items:center;}
444 .move-box{margin-top:10px;}
445 .move-row{display:flex;gap:6px;align-items:center;flex-wrap:wrap;}
446 .move-input{flex:1;min-width:220px;padding:4px;border:1px solid #ccc;border-radius:4px;}
447 .hint{color:#888;}
448 .loadmore-wrap{text-align:center;margin:12px 0 28px;}
449 .btn{padding:4px 8px;border:1px solid #ddd;background:#f8f9fa;border-radius:4px;cursor:pointer;text-decoration:none;display:inline-block;}
450 .btn:hover{background:#e9ecef;}
451 .btn-primary{background:#007bff;color:#fff;border-color:#007bff;}
452 .btn-secondary{background:#6c757d;color:#fff;border-color:#6c757d;}
453 .btn-success{background:#28a745;color:#fff;border-color:#28a745;}
454 .btn-danger{background:#dc3545;color:#fff;border-color:#dc3545;}
455 .btn-danger:hover{background:#c82333}
456 .btn-sm{font-size:12px;padding:2px 6px;}
457 .move-notice{font-size:12px;color:#666;margin-top:6px;max-height:6.5em;overflow:auto;white-space:normal;overflow-wrap:anywhere;}
458 .bulk-bar{display:flex;gap:8px;align-items:center;flex-wrap:wrap;margin:8px 0 16px;}
459 .bulk-bar .sep{width:1px;height:18px;background:#ddd;margin:0 4px;}
460 .bulk-status{font-size:12px;color:#666;margin-left:6px;white-space:normal;overflow-wrap:anywhere;}
461 @media (max-width:768px){.video-display-grid{grid-template-columns:1fr}}
462 </style>
463 {{/html}}
464 {{/cache}}
465 {{/velocity}}