1+ <!DOCTYPE html>
2+ < html lang ="en ">
3+ < head >
4+ < meta charset ="UTF-8 ">
5+ < meta name ="viewport " content ="width=device-width, initial-scale=1.0 ">
6+ < title > File Type Test</ title >
7+ < style >
8+ @import url ('https://fonts.googleapis.com/css2?family=IBM+Plex+Mono:wght@400;600&family=IBM+Plex+Sans:wght@300;400&display=swap' );
9+
10+ * , * ::before , * ::after { box-sizing : border-box; margin : 0 ; padding : 0 ; }
11+
12+ : root {
13+ --bg : # 0a0f1e ;
14+ --surface : # 101828 ;
15+ --border : # 1e3050 ;
16+ --accent : # e8f0ff ;
17+ --text : # c8d8f0 ;
18+ --text-dim : # 4a6080 ;
19+ --radius : 4px ;
20+ }
21+
22+ body {
23+ background : var (--bg );
24+ color : var (--text );
25+ font-family : 'IBM Plex Sans' , sans-serif;
26+ font-weight : 300 ;
27+ min-height : 100vh ;
28+ display : flex;
29+ flex-direction : column;
30+ align-items : center;
31+ justify-content : center;
32+ padding : 2rem ;
33+ }
34+
35+ h1 {
36+ font-family : 'IBM Plex Mono' , monospace;
37+ font-size : 0.75rem ;
38+ font-weight : 600 ;
39+ letter-spacing : 0.2em ;
40+ text-transform : uppercase;
41+ color : var (--accent );
42+ margin-bottom : 2rem ;
43+ }
44+
45+ .card {
46+ width : 100% ;
47+ max-width : 760px ;
48+ background : var (--surface );
49+ border : 1px solid var (--border );
50+ border-radius : var (--radius );
51+ overflow : hidden;
52+ }
53+
54+ .card-header {
55+ display : flex;
56+ align-items : center;
57+ gap : 1rem ;
58+ padding : 1rem 1.25rem ;
59+ border-bottom : 1px solid var (--border );
60+ background : # 0a0f1e ;
61+ }
62+
63+ .card-header label {
64+ font-family : 'IBM Plex Mono' , monospace;
65+ font-size : 0.7rem ;
66+ letter-spacing : 0.12em ;
67+ text-transform : uppercase;
68+ color : var (--text-dim );
69+ white-space : nowrap;
70+ }
71+
72+ select {
73+ flex : 1 ;
74+ background : var (--bg );
75+ color : var (--text );
76+ border : 1px solid var (--border );
77+ border-radius : var (--radius );
78+ padding : 0.45rem 0.75rem ;
79+ font-family : 'IBM Plex Mono' , monospace;
80+ font-size : 0.8rem ;
81+ appearance : none;
82+ background-image : url ("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='6'%3E%3Cpath d='M0 0l5 6 5-6z' fill='%23555'/%3E%3C/svg%3E" );
83+ background-repeat : no-repeat;
84+ background-position : right 0.75rem center;
85+ cursor : pointer;
86+ transition : border-color 0.15s ;
87+ }
88+
89+ select : hover , select : focus {
90+ border-color : var (--accent );
91+ outline : none;
92+ }
93+
94+ select option {
95+ background : # 0a0f1e ;
96+ }
97+
98+ .viewer {
99+ position : relative;
100+ min-height : 420px ;
101+ display : flex;
102+ align-items : center;
103+ justify-content : center;
104+ padding : 1.5rem ;
105+ }
106+
107+ .placeholder {
108+ display : flex;
109+ flex-direction : column;
110+ align-items : center;
111+ gap : 0.75rem ;
112+ color : var (--text-dim );
113+ font-family : 'IBM Plex Mono' , monospace;
114+ font-size : 0.75rem ;
115+ letter-spacing : 0.08em ;
116+ }
117+
118+ .placeholder svg {
119+ opacity : 0.3 ;
120+ }
121+
122+ # content-area {
123+ width : 100% ;
124+ height : 100% ;
125+ display : flex;
126+ align-items : center;
127+ justify-content : center;
128+ }
129+
130+ # content-area img {
131+ max-width : 100% ;
132+ max-height : 380px ;
133+ object-fit : contain;
134+ border-radius : var (--radius );
135+ display : block;
136+ }
137+
138+ # content-area video {
139+ max-width : 100% ;
140+ max-height : 380px ;
141+ border-radius : var (--radius );
142+ background : # 050a14 ;
143+ }
144+
145+ # content-area audio {
146+ width : 100% ;
147+ max-width : 480px ;
148+ accent-color : var (--accent );
149+ }
150+
151+ # content-area pre {
152+ width : 100% ;
153+ background : var (--bg );
154+ border : 1px solid var (--border );
155+ border-radius : var (--radius );
156+ padding : 1.25rem ;
157+ font-family : 'IBM Plex Mono' , monospace;
158+ font-size : 0.78rem ;
159+ line-height : 1.7 ;
160+ color : # aaa ;
161+ overflow : auto;
162+ max-height : 380px ;
163+ white-space : pre-wrap;
164+ word-break : break-word;
165+ }
166+
167+ # content-area embed ,
168+ # content-area iframe {
169+ width : 100% ;
170+ height : 380px ;
171+ border : none;
172+ border-radius : var (--radius );
173+ background : # fff ;
174+ }
175+
176+ .tag {
177+ font-family : 'IBM Plex Mono' , monospace;
178+ font-size : 0.65rem ;
179+ letter-spacing : 0.1em ;
180+ text-transform : uppercase;
181+ color : var (--text-dim );
182+ padding : 0.25rem 0.6rem ;
183+ border : 1px solid var (--border );
184+ border-radius : 2px ;
185+ background : # 0a0f1e ;
186+ }
187+
188+ .card-footer {
189+ padding : 0.65rem 1.25rem ;
190+ border-top : 1px solid var (--border );
191+ background : # 0a0f1e ;
192+ display : flex;
193+ align-items : center;
194+ justify-content : space-between;
195+ }
196+
197+ .filepath {
198+ font-family : 'IBM Plex Mono' , monospace;
199+ font-size : 0.7rem ;
200+ color : var (--text-dim );
201+ }
202+
203+ .filepath span {
204+ color : var (--accent );
205+ }
206+ </ style >
207+ </ head >
208+ < body >
209+
210+ < h1 > File Type Viewer</ h1 >
211+
212+ < div class ="card ">
213+ < div class ="card-header ">
214+ < label for ="file-select "> Type</ label >
215+ < select id ="file-select " onchange ="loadFile(this.value) ">
216+ < option value =""> — select a file type —</ option >
217+ < optgroup label ="Images ">
218+ < option value ="jpeg "> JPEG (.jpeg)</ option >
219+ < option value ="jpg "> JPG (.jpg)</ option >
220+ < option value ="png "> PNG (.png)</ option >
221+ < option value ="gif "> GIF (.gif)</ option >
222+ < option value ="webp "> WebP (.webp)</ option >
223+ < option value ="svg "> SVG (.svg)</ option >
224+ </ optgroup >
225+ < optgroup label ="Text ">
226+ < option value ="txt "> TXT (.txt)</ option >
227+ </ optgroup >
228+ < optgroup label ="Document ">
229+ < option value ="pdf "> PDF (.pdf)</ option >
230+ </ optgroup >
231+ < optgroup label ="Video ">
232+ < option value ="mp4 "> MP4 (.mp4)</ option >
233+ < option value ="webm "> WebM (.webm)</ option >
234+ </ optgroup >
235+ < optgroup label ="Audio ">
236+ < option value ="mp3 "> MP3 (.mp3)</ option >
237+ < option value ="wav "> WAV (.wav)</ option >
238+ </ optgroup >
239+ </ select >
240+ < span class ="tag " id ="type-tag "> —</ span >
241+ </ div >
242+
243+ < div class ="viewer ">
244+ < div id ="content-area ">
245+ < div class ="placeholder ">
246+ < svg width ="32 " height ="32 " viewBox ="0 0 24 24 " fill ="none " stroke ="currentColor " stroke-width ="1.5 ">
247+ < rect x ="3 " y ="3 " width ="18 " height ="18 " rx ="2 "/>
248+ < path d ="M3 9h18M9 21V9 "/>
249+ </ svg >
250+ select a file type above
251+ </ div >
252+ </ div >
253+ </ div >
254+
255+ < div class ="card-footer ">
256+ < span class ="filepath " id ="filepath-display "> —</ span >
257+ </ div >
258+ </ div >
259+
260+ <!-- JavaScript which handles file selection and display -->
261+ < script >
262+ // A const mapping the path, category of file and label of each supported file format
263+ const types = {
264+ jpg : { path : '/test-files/test.jpg' , category : 'image' , label : 'jpg' } ,
265+ jpeg : { path : '/test-files/test.jpeg' , category : 'image' , label : 'jpeg' } ,
266+ png : { path : '/test-files/test.png' , category : 'image' , label : 'png' } ,
267+ gif : { path : '/test-files/test.gif' , category : 'image' , label : 'gif' } ,
268+ webp : { path : '/test-files/test.webp' , category : 'image' , label : 'webp' } ,
269+ svg : { path : '/test-files/test.svg' , category : 'image' , label : 'svg' } ,
270+ txt : { path : '/test-files/test.txt' , category : 'text' , label : 'plain' } ,
271+ pdf : { path : '/test-files/test.pdf' , category : 'pdf' , label : 'pdf' } ,
272+ mp4 : { path : '/test-files/test.mp4' , category : 'video' , label : 'video/mp4' } ,
273+ webm : { path : '/test-files/test.webm' , category : 'video' , label : 'video/webm' } ,
274+ mp3 : { path : '/test-files/test.mp3' , category : 'audio' , label : 'audio/mpeg' } ,
275+ wav : { path : '/test-files/test.wav' , category : 'audio' , label : 'audio/wav' } ,
276+ } ;
277+
278+ /**
279+ * Loads the type of file selected in the dropdown menu.
280+ * @param fileType
281+ */
282+ function loadFile ( fileType ) {
283+ const contentArea = document . getElementById ( 'content-area' ) ;
284+ const typeTag = document . getElementById ( 'type-tag' ) ;
285+ const filePathDisplay = document . getElementById ( 'filepath-display' ) ;
286+
287+ // If no filetype is selected then show a message prompting the user to select a file type
288+ if ( ! fileType || ! types [ fileType ] ) {
289+ contentArea . innerHTML = `<div class="placeholder"><svg width="32" height="32" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5"><rect x="3" y="3" width="18" height="18" rx="2"/><path d="M3 9h18M9 21V9"/></svg>select a file type above</div>` ;
290+ typeTag . textContent = '—' ;
291+ filePathDisplay . innerHTML = '—' ;
292+ return ;
293+ }
294+
295+ // Assigns a variable to each value in the types const and displays the information in the card header
296+ const { path, category, label } = types [ fileType ] ;
297+ typeTag . textContent = fileType ;
298+ filePathDisplay . innerHTML = `<span>${ path } </span>` ;
299+
300+ // Displays the file type based on category
301+ if ( category === 'image' ) {
302+ const image = document . createElement ( 'img' ) ;
303+ image . src = path ;
304+ image . alt = `test ${ fileType } ` ;
305+ image . onerror = ( ) => {
306+ image . outerHTML = `<span style = "color:#4a6080;font-family:monospace;font-size:.8rem">file not found: ${ path } </span>` ;
307+ } ;
308+ contentArea . innerHTML = '' ;
309+ contentArea . appendChild ( image ) ;
310+ } else if ( category === 'text' ) {
311+ fetch ( path )
312+ . then ( response => { if ( ! response . ok ) { throw new Error ( 'not found' ) ; } return response . text ( ) ; } )
313+ . then ( responseText => {
314+ const pre = document . createElement ( 'pre' ) ;
315+ pre . textContent = responseText ;
316+ contentArea . innerHTML = '' ;
317+ contentArea . appendChild ( pre ) ;
318+ } )
319+ . catch ( ( ) => { contentArea . innerHTML = `<span style="color:#555;font-family:monospace;font-size:.8rem">file not found: ${ path } </span>` ; } ) ;
320+ } else if ( category === 'pdf' ) {
321+ contentArea . innerHTML = `<embed src="${ path } " type="application/pdf">` ;
322+ } else if ( category === 'video' ) {
323+ const video = document . createElement ( 'video' ) ;
324+ video . controls = true ;
325+ video . innerHTML = `<source src="${ path } " type="${ label } ">Your browser does not support this video format.` ;
326+ video . onerror = ( ) => {
327+ contentArea . innerHTML = `<span style="color:#4a6080;font-family:monospace;font-size:.8rem">file not found: ${ path } </span>` ;
328+ } ;
329+ contentArea . innerHTML = '' ;
330+ contentArea . appendChild ( video ) ;
331+ } else if ( category === 'audio' ) {
332+ const audio = document . createElement ( 'audio' ) ;
333+ audio . controls = true ;
334+ audio . innerHTML = `<source src="${ path } " type="${ label } ">Your browser does not support this audio format.` ;
335+ audio . onerror = ( ) => {
336+ contentArea . innerHTML = `<span style="color:#4a6080;font-family:monospace;font-size:.8rem">file not found: ${ path } </span>` ;
337+ } ;
338+ contentArea . innerHTML = '' ;
339+ contentArea . appendChild ( audio ) ;
340+ }
341+ }
342+ </ script >
343+ </ body >
344+ </ html >
0 commit comments