Wiki source code of Violence against White People
Show last authors
author | version | line-number | content |
---|---|---|---|
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 & 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}} |