SorrelC commited on
Commit
ee5ccd8
·
verified ·
1 Parent(s): cb0c3bf

Update index.html

Browse files
Files changed (1) hide show
  1. index.html +664 -496
index.html CHANGED
@@ -23,22 +23,62 @@
23
  }
24
  header {
25
  text-align: center;
26
- margin-bottom: 30px;
27
  }
28
  h1 {
29
  font-size: 2rem;
30
- margin-bottom: 10px;
31
  color: #002147;
32
  }
33
  .subtitle {
34
  color: #666;
35
  font-size: 1rem;
36
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
37
  .stats {
38
  display: flex;
39
  justify-content: center;
40
  gap: 30px;
41
- margin: 20px 0;
42
  flex-wrap: wrap;
43
  }
44
  .stat {
@@ -62,11 +102,13 @@
62
  text-transform: uppercase;
63
  letter-spacing: 1px;
64
  }
 
 
65
  .controls {
66
  display: flex;
67
  justify-content: center;
68
  gap: 15px;
69
- margin-bottom: 30px;
70
  flex-wrap: wrap;
71
  }
72
  button {
@@ -98,11 +140,13 @@
98
  .btn-icon {
99
  font-size: 1.2rem;
100
  }
 
 
101
  .instructions {
102
  background: #f8f9fa;
103
  padding: 20px;
104
  border-radius: 10px;
105
- margin-bottom: 30px;
106
  text-align: center;
107
  border: 1px solid #e0e0e0;
108
  }
@@ -124,12 +168,14 @@
124
  display: inline-block;
125
  margin-top: 10px;
126
  }
 
 
127
  .message {
128
  text-align: center;
129
- padding: 20px;
130
- margin: 20px 0;
131
  border-radius: 10px;
132
- font-size: 1.2rem;
133
  display: none;
134
  }
135
  .message.show {
@@ -145,6 +191,25 @@
145
  border: 2px solid #dc3545;
146
  color: #dc3545;
147
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
148
  .game-area {
149
  display: flex;
150
  flex-direction: column;
@@ -183,6 +248,8 @@
183
  grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
184
  padding: 20px 0 150px 0;
185
  }
 
 
186
  .card {
187
  height: 80px;
188
  perspective: 1000px;
@@ -253,7 +320,7 @@
253
  border-width: 3px;
254
  box-shadow: 0 0 15px rgba(255, 193, 7, 0.5);
255
  }
256
- /* Lift and enlarge selected definition cards for better legibility */
257
  .definitions-board .card.selected {
258
  z-index: 100;
259
  position: relative;
@@ -276,7 +343,7 @@
276
  padding: 20px;
277
  line-height: 1.5;
278
  }
279
- /* Also enlarge term cards slightly when selected */
280
  .terms-board .card.selected {
281
  z-index: 100;
282
  position: relative;
@@ -303,6 +370,8 @@
303
  visibility: hidden;
304
  pointer-events: none;
305
  }
 
 
306
  .attribution {
307
  background: #f8f9fa;
308
  padding: 20px;
@@ -321,530 +390,629 @@
321
  .attribution a:hover {
322
  text-decoration: underline;
323
  }
 
 
324
  @media (max-width: 600px) {
325
- h1 {
326
- font-size: 1.5rem;
327
- }
328
  .game-board {
329
  grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
330
  }
331
  .definitions-board {
332
  grid-template-columns: 1fr;
333
  }
334
- .card-back.definition {
335
- font-size: 0.75rem;
336
- }
337
  .definitions-board .card.selected .card-back {
338
  width: 100%;
339
  min-width: unset;
340
  font-size: 0.95rem;
341
  }
 
 
 
 
 
342
  }
343
  </style>
344
  </head>
345
  <body>
346
- <div class="container">
347
- <header>
348
- <h1>🗃️ Digital Preservation Glossary</h1>
349
- <p class="subtitle">Match the terms with their definitions</p>
350
- </header>
351
-
352
- <div class="stats">
353
- <div class="stat">
354
- <div class="stat-value" id="matches">0 / 20</div>
355
- <div class="stat-label">✓ Matches</div>
356
- </div>
357
- <div class="stat">
358
- <div class="stat-value" id="attempts">0</div>
359
- <div class="stat-label">Attempts</div>
360
- </div>
361
- <div class="stat">
362
- <div class="stat-value" id="timer">5:00</div>
363
- <div class="stat-label">⏱ Time Left</div>
364
- </div>
365
- </div>
366
 
367
- <div class="controls">
368
- <button onclick="startNewGame()">
369
- <span class="btn-icon">🔄</span> New Game
370
- </button>
371
- <button class="secondary" onclick="shuffleCards()">
372
- <span class="btn-icon">🔀</span> Shuffle
373
- </button>
374
- <button class="secondary" onclick="showHint()">
375
- <span class="btn-icon">💡</span> Hint
376
- </button>
377
- </div>
378
 
379
- <div class="instructions">
380
- <h2>How to Play</h2>
381
- <p>Click a <strong>term card</strong> above, then click its matching <strong>definition</strong> below.
382
- Matched pairs turn <span style="color: #28a745; font-weight: bold;">green</span> so you can learn them, then disappear when you make your next match. Beat the clock!</p>
383
- <div class="term-count">📚 20 random terms selected from 108 in the DPC Glossary</div>
384
- </div>
 
 
385
 
386
- <div class="message success" id="winMessage">
387
- 🎉 Congratulations! You matched all pairs!
 
 
 
388
  </div>
389
-
390
- <div class="message failure" id="loseMessage">
391
- Time's up! Click New Game to try again.
392
  </div>
393
-
394
- <div class="game-area">
395
- <div class="section terms-section">
396
- <h2 class="section-header">
397
- <span class="icon">📝</span> Terms & Acronyms
398
- </h2>
399
- <div class="game-board terms-board" id="termsBoard"></div>
400
- </div>
401
- <div class="section definitions-section">
402
- <h2 class="section-header">
403
- <span class="icon">📖</span> Definitions
404
- </h2>
405
- <div class="game-board definitions-board" id="definitionsBoard"></div>
406
- </div>
407
  </div>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
408
 
409
- <div class="attribution">
410
- <strong>Source:</strong> Digital Preservation Handbook, 2nd Edition,
411
- <a href="https://www.dpconline.org/handbook" target="_blank">https://www.dpconline.org/handbook</a><br>
412
- Digital Preservation Coalition © 2015 licensed under the
413
- <a href="http://www.nationalarchives.gov.uk/doc/open-government-licence/version/3/" target="_blank">Open Government Licence v3.0</a>.<br><br>
414
- This interactive learning tool was created for educational purposes.
415
- For the most up-to-date glossary, please visit the
416
- <a href="https://www.dpconline.org/handbook/glossary" target="_blank">DPC Handbook Glossary</a>.
 
 
 
 
 
 
 
 
 
 
 
417
  </div>
418
  </div>
419
 
420
- <script>
421
- // Complete glossary data from DPC Handbook (108 terms)
422
- const allGlossaryData = [
423
- { term: "Access", definition: "As defined in the Handbook, access is assumed to mean continued, ongoing usability of a digital resource, retaining all qualities of authenticity, accuracy and functionality deemed to be essential for the purposes the digital material was created and/or acquired for." },
424
- { term: "ADS", definition: "Archaeology Data Service. A UK based service active in digital preservation." },
425
- { term: "AIP", definition: "Archival Information Package. An Information Package, consisting of the Content Information and the associated Preservation Description Information (PDI), which is preserved within an OAIS (OAIS term)." },
426
- { term: "AMIA", definition: "Association of Moving Image Archives, an organisation active in the field of moving image archiving." },
427
- { term: "ARC", definition: "Container format for websites devised by the Internet Archive, superseded by WARC." },
428
- { term: "ASCII", definition: "American Standard Code for Information Interchange, standard for electronic text." },
429
- { term: "Authentication", definition: "A mechanism which attempts to establish the authenticity of digital materials at a particular point in time. For example, digital signatures." },
430
- { term: "Authenticity", definition: "The digital material is what it purports to be. In the case of electronic records, it refers to the trustworthiness of the electronic record as a record. In the case of \"born digital\" and digitised materials, it refers to the fact that whatever is being cited is the same as it was when it was first created unless the accompanying metadata indicates any changes." },
431
- { term: "Bit", definition: "A bit is the basic unit of information in computing. It can have only one of two values commonly represented as either a 0 or 1." },
432
- { term: "Bit Preservation", definition: "A term used to denote a very basic level of preservation of digital resource as it was submitted (literally preservation of the bits forming a digital resource). It may include maintaining onsite and offsite backup copies, virus checking, fixity-checking, and periodic refreshment to new storage media." },
433
- { term: "Born-Digital", definition: "Digital materials which are not intended to have an analogue equivalent, either as the originating source or as a result of conversion to analogue form." },
434
- { term: "BWF", definition: "Broadcast WAV format, the European Broadcasting Union standard for a WAV file, with extra metadata." },
435
- { term: "Byte (B)", definition: "A unit of digital information that most commonly consists of eight bits. Historically, the byte was the number of bits used to encode a single character of text in a computer." },
436
- { term: "CCSDS", definition: "Consultative Committee for Space Data Systems, the body responsible for the OAIS Reference Model." },
437
- { term: "Chain of Custody", definition: "A key concept in forensics whereby the custody and provenance of digital hardware, media and files are safeguarded through, for example, the appointment of evidence custodians." },
438
- { term: "Checksum", definition: "A unique numerical signature derived from a file. Used to compare copies." },
439
- { term: "CLIR", definition: "Council on Library and Information Resources. US based organisation active in digital preservation." },
440
- { term: "CNI", definition: "Coalition for Networked Information. US based organisation active in digital preservation." },
441
- { term: "Continuing Access", definition: "Refers to the right of a subscriber to an electronic publication and their users to have on-going permanent access to electronic materials which have already been leased and paid for by the subscriber from a publisher." },
442
- { term: "COPTR", definition: "Community Owned digital Preservation Tool Registry hosted by The Open Preservation Foundation." },
443
- { term: "Crawl", definition: "The act of browsing the web automatically and methodically to index or download content and other data from the web. The software to do this is often called a web crawler." },
444
- { term: "Dark Archive", definition: "An archive that cannot be accessed by any current users but may be accessible at future dates subject to the occurrence of specific pre-defined events ('trigger event')." },
445
- { term: "DCC", definition: "Digital Curation Centre. A UK based organisation active in digital preservation." },
446
- { term: "DDI", definition: "Data Documentation Initiative. A de facto international metadata standard for describing data from the social, behavioral, and economic sciences." },
447
- { term: "Designated Community", definition: "An identified group of potential consumers who should be able to understand a particular set of information from an archive. These consumers may consist of multiple communities, are designated by the archive, and may change over time (OAIS term)." },
448
- { term: "Digital Archiving", definition: "This term is used very differently within sectors. The library and archiving communities often use it interchangeably with digital preservation. Computing professionals tend to use it to mean the process of backup and ongoing maintenance." },
449
- { term: "Digital Forensics", definition: "The application of scientific technical methods and tools toward the preservation, collection, validation, identification, analysis, interpretation, documentation and presentation of digital information derived after-the-fact from digital sources." },
450
- { term: "Dim Archive", definition: "Provides bit preservation for the content plus digital preservation planning and actions for long-term perpetual access, and also limited current access." },
451
- { term: "DigCurV", definition: "Digital Curator Vocational Education Europe. A project funded by the European Commission to establish a curriculum framework for vocational training in digital curation." },
452
- { term: "Digital Materials", definition: "A broad term encompassing digital surrogates created as a result of converting analogue materials to digital form (digitisation), and \"born digital\" for which there has never been and is never intended to be an analogue equivalent, and digital records." },
453
- { term: "Digital Preservation", definition: "Refers to the series of managed activities necessary to ensure continued access to digital materials for as long as necessary. It includes all actions required to maintain access beyond the limits of media failure or technological and organisational change." },
454
- { term: "Short-term preservation", definition: "Access to digital materials either for a defined period of time while use is predicted but which does not extend beyond the foreseeable future and/or until it becomes inaccessible because of changes in technology." },
455
- { term: "Medium-term preservation", definition: "Continued access to digital materials beyond changes in technology for a defined period of time but not indefinitely." },
456
- { term: "Long-term preservation", definition: "Continued access to digital materials, or at least to the information contained in them, indefinitely." },
457
- { term: "Digital Preservation Management Workshop", definition: "An intensive training workshop and online tutorial developed and maintained by Cornell University Library, extended by ICPSR, and now maintained by MIT Libraries." },
458
- { term: "Digital Publications", definition: "\"Born digital\" objects which have been released for public access and either made available or distributed free of charge or for a fee. They may be networked publications or physical format publications distributed on formats such as optical disks." },
459
- { term: "Digitisation", definition: "The process of creating digital files by scanning or otherwise converting analogue materials. The resulting digital copy, or digital surrogate, would then be classed as digital material." },
460
- { term: "DIP", definition: "Dissemination Information Package. An Information Package, derived from one or more Archival Information Packages (AIPs), and sent by Archives to the Consumer in response to a request to the OAIS (OAIS term)." },
461
- { term: "DLF", definition: "Digital Library Federation. A US based organisation active in digital preservation." },
462
- { term: "Documentation", definition: "The information provided by a creator and the repository which provides enough information to establish provenance, history and context and to enable its use by others." },
463
- { term: "DOI", definition: "Digital Object Identifier. A technical and organisational infrastructure for the registration and use of persistent identifiers widely used in digital publications and for research data." },
464
- { term: "DPC", definition: "Digital Preservation Coalition. A UK and Ireland based organisation active in digital preservation and responsible for the Digital Preservation Handbook." },
465
- { term: "DPTP", definition: "Digital Preservation Training Programme, an intensive training course run by the University of London Computer Centre." },
466
- { term: "DRAMBORA", definition: "Digital Repository Audit Methodology Based on Risk Assessment. A set of risk assessment tools developed by the Digital Curation Centre." },
467
- { term: "DROID", definition: "A file profiling tool developed and distributed by TNA to identify file formats. Based on PRONOM." },
468
- { term: "Electronic Records", definition: "Records created digitally in the day-to-day business of the organisation and assigned formal status by the organisation. They may include word processing documents, emails, databases, or intranet web pages." },
469
- { term: "Emulation", definition: "A means of overcoming technological obsolescence of hardware and software by developing techniques for imitating obsolete systems on future generations of computers." },
470
- { term: "Escrow", definition: "A widespread legal practice of the deposit of content or software source code with a third party. Escrow takes place in a contractual relationship, formalized in an escrow agreement." },
471
- { term: "FIAF", definition: "International Federation of Film Archives, an association of the world's leading film archives." },
472
- { term: "FIAT", definition: "International Federation of Television Archives, a professional association for those engaged in the preservation and exploitation of broadcast archives." },
473
- { term: "File Format", definition: "A standard way that information is encoded for storage in a computer file. It tells the computer how to display, print, process, and save the information. A particular file format is often indicated by a file name extension." },
474
- { term: "Fixity Check", definition: "A method for ensuring the integrity of a file and verifying it has not been altered or corrupted. It is most often accomplished by computing checksums such as MD5, SHA1 or SHA256 for a file and comparing them to a stored value." },
475
- { term: "GIF", definition: "Graphic Interchange Format, an image which typically uses lossy compression." },
476
- { term: "Gigabyte (GB)", definition: "A unit of digital information often used to describe data or data storage size, equates to approximately 1,000 Megabytes (MB)." },
477
- { term: "GIS", definition: "Geographical Information System, a system that processes mapping and data together." },
478
- { term: "HTML", definition: "Hypertext Markup Language, a format used to present text and other information on the World Wide Web. Since 1996, versions have been maintained by the World Wide Web Consortium (W3C)." },
479
- { term: "IASA", definition: "International Association of Sound and Audiovisual Archives, an association for archives that preserve recorded sound and audiovisual documents." },
480
- { term: "IIPC", definition: "The International Internet Preservation Consortium." },
481
- { term: "Information Assurance", definition: "An aspect of digital security, specifically directed at ensuring that the quality of the information is demonstrably safeguarded, that it has not been tampered with or accessed inappropriately." },
482
- { term: "Ingest", definition: "The process of turning a Submission Information Package (SIP) into an Archival Information Package (AIP), i.e. putting data into a digital archive (OAIS term)." },
483
- { term: "InterPARES project", definition: "International Research on Permanent Authentic Records in Electronic Systems." },
484
- { term: "ISO", definition: "International Organization for Standardization." },
485
- { term: "JHove2", definition: "A characterization tool for digital objects. Characterisation is comprised of four elements: identifying the object's format; validating that the object conforms to its format's technical norms; extracting technical metadata; and assessing whether the object should be accepted into a repository." },
486
- { term: "JPEG", definition: "Joint Photographic Experts Group, a committee that oversees international standards for compression and processing of digital photographs. The majority of JPEG formats are lossy." },
487
- { term: "JPEG 2000", definition: "A revision of the JPEG format which can use lossless compression." },
488
- { term: "Kilobyte (KB)", definition: "A unit of digital information often used to describe data or data storage size, equates to approximately 1,000 Bytes." },
489
- { term: "Life-cycle Management", definition: "The need actively to manage digital resources at each stage of their life-cycle and to recognise the inter-dependencies between each stage, commencing preservation activities as early as practicable." },
490
- { term: "Lossless Compression", definition: "A mechanism for reducing file sizes that retains all original data." },
491
- { term: "Lossy Compression", definition: "A mechanism for reducing file sizes that typically discards data." },
492
- { term: "LOTAR", definition: "LOng Term Archiving and Retrieval - a digital preservation standard for 3D CAD models and product data management information developed by LOTAR International." },
493
- { term: "Megabyte (MB)", definition: "A unit of digital information often used to describe data or data storage size, equates to approximately 1,000 Kilobytes (KB)." },
494
- { term: "Metadata", definition: "Information which describes significant aspects of a resource. The emphasis in this Handbook is on what metadata are required to manage and preserve digital materials over time and ensure essential contextual, historical, and technical information are preserved." },
495
- { term: "METS", definition: "Metadata Encoding and Transmission Standard, a standard for presenting metadata using XML." },
496
- { term: "Migration", definition: "A means of overcoming technological obsolescence by transferring digital resources from one hardware/software generation to the next. The purpose is to preserve the intellectual content and retain the ability to retrieve, display, and use digital objects." },
497
- { term: "MIME", definition: "Multipurpose Internet Mail Extensions. A protocol for including non-ASCII information in email messages." },
498
- { term: "MPEG", definition: "Moving Picture Experts Group. A committee responsible for the development of international standards for compression, decompression, processing, and coded representation of moving pictures and audio." },
499
- { term: "NCDD", definition: "The Netherlands Coalition for Digital Preservation." },
500
- { term: "NDSA", definition: "National Digital Stewardship Alliance, a US based organisation active in digital preservation." },
501
- { term: "NESTOR", definition: "The German competence network for digital preservation." },
502
- { term: "OAIS", definition: "Open Archival Information System. An Archive consisting of an organization of people and systems that has accepted the responsibility to preserve information and make it available for a Designated Community. Also refers to the reference model standard (ISO 14721)." },
503
- { term: "OPF", definition: "Open Preservation Foundation, formerly the Open Planets Foundation." },
504
- { term: "PAIMAS", definition: "Space Data and Information Transfer Systems - Producer-Archive Interface - Methodology Abstract Standard. This ISO 20652:2006 standard covers the first stages of the ingest process defined by OAIS reference model." },
505
- { term: "PDF", definition: "Portable Document Format, a set of formats and open standards maintained by the International Organization for Standardization for producing and sharing electronic documents, originally developed by Adobe Systems." },
506
- { term: "PDF/A", definition: "Versions of the PDF standard intended for archival use." },
507
- { term: "PDI", definition: "Preservation Description Information. The information which is necessary for adequate preservation of the Content Information and which can be categorized as Provenance, Reference, Fixity, Context, and Access Rights Information (OAIS term)." },
508
- { term: "Petabyte (PB)", definition: "A unit of digital information often used to describe data or data storage size, equates to approximately 1,000 Terabytes (TB)." },
509
- { term: "PIN", definition: "Pérennisation des Informations Numériques, the French national interest group for digital preservation." },
510
- { term: "PREMIS", definition: "Preservation Metadata: Implementation Strategies. A de facto standard for digital preservation metadata." },
511
- { term: "PRONOM", definition: "A database of file formats, software products and other technical components required to support long-term access to electronic records and other digital objects. Used with DROID." },
512
- { term: "PST", definition: "Personal Storage Table is a file extension for local 'personal stores' written by Microsoft Outlook. PST files contain email messages and calendar entries." },
513
- { term: "Reformatting", definition: "Copying information content from one storage medium to a different storage medium (media reformatting) or converting from one file format to a different file format (file reformatting)." },
514
- { term: "Refreshing", definition: "Copying information content from one storage media to the same storage media." },
515
- { term: "Sandbox Containment", definition: "A secure computing environment for running novel, unattested or experimental code or changes in code, including potentially malicious code. The environment is self-contained with tightly controlled resources." },
516
- { term: "SGML", definition: "Standard Generalized Markup Language, an ISO standard for how to specify a document markup language or tag set." },
517
- { term: "Significant properties", definition: "Characteristics of digital and intellectual objects that must be preserved over time in order to ensure the continued accessibility, usability and meaning of the objects." },
518
- { term: "SIP", definition: "Submission Information Package. An Information Package that is delivered by the Producer to the OAIS for use in the construction or update of one or more Archival Information Packages (AIPs) (OAIS term)." },
519
- { term: "SMPTE", definition: "Society of Motion Picture and Television Engineers, a professional organisation and technical standards body for television and motion picture." },
520
- { term: "TDR", definition: "Trusted Digital Repository. A repository with a mission to provide reliable, long-term access to managed digital resources to its designated community, now and into the future." },
521
- { term: "Terabyte (TB)", definition: "A unit of digital information often used to describe data or data storage size, equates to approximately 1,000 Gigabytes (GB)." },
522
- { term: "Three-Legged Stool", definition: "A conceptual approach to digital preservation that suggests a fully implemented programme addresses organisational issues, technological concerns, and funding questions, balancing them like a three-legged stool." },
523
- { term: "TIFF", definition: "Tagged Image File Format, a common format for images typically lossless." },
524
- { term: "TRAC", definition: "Trusted Repository Audit and Certification, toolkit for auditing a digital repository." },
525
- { term: "Trigger Event", definition: "Terminology used when specific conditions relating to an electronic publication and its continued delivery are met. If the publication is no longer available from the publisher, a trigger event has occurred, which can set in motion access via an archive." },
526
- { term: "UKWA", definition: "UK Web Archive." },
527
- { term: "WARC", definition: "The WARC (Web ARChive) format is a container format for archived websites (ISO 28500:2009). It is a revision of the Internet Archive's ARC File Format." },
528
- { term: "WAV", definition: "The standard file wrapper for audio; see BWF (Broadcast WAV Format) for the professional variant." },
529
- { term: "Writeblockers", definition: "Tools that prevent an examination computer system from writing or altering a collection or subject hard drive or other digital media object." },
530
- { term: "XML", definition: "Extensible Markup Language, a widely used standard (derived from SGML), for representing structured information. It is maintained by the World Wide Web Consortium (W3C)." }
531
- ];
532
-
533
- // Game configuration
534
- const TERMS_PER_GAME = 20;
535
- const GAME_TIME = 5 * 60; // 5 minutes in seconds
536
-
537
- let glossaryData = []; // Current game's selection
538
- let termCards = [];
539
- let definitionCards = [];
540
- let selectedTerm = null;
541
- let selectedDefinition = null;
542
- let lastMatchedTerm = null;
543
- let lastMatchedDefinition = null;
544
- let matchedPairs = 0;
545
- let attempts = 0;
546
- let canSelect = true;
547
- let timerInterval = null;
548
- let secondsRemaining = GAME_TIME;
549
- let gameOver = false;
550
-
551
- function shuffle(array) {
552
- const newArray = [...array];
553
- for (let i = newArray.length - 1; i > 0; i--) {
554
- const j = Math.floor(Math.random() * (i + 1));
555
- [newArray[i], newArray[j]] = [newArray[j], newArray[i]];
556
- }
557
- return newArray;
558
- }
559
-
560
- function selectRandomTerms() {
561
- // Shuffle all terms and pick the first TERMS_PER_GAME
562
- const shuffled = shuffle(allGlossaryData);
563
- return shuffled.slice(0, TERMS_PER_GAME);
564
- }
565
-
566
- function createCards() {
567
- termCards = glossaryData.map((item, index) => ({
568
- id: `term-${index}`,
569
- type: 'term',
570
- content: item.term,
571
- pairId: index
572
- }));
573
- definitionCards = glossaryData.map((item, index) => ({
574
- id: `def-${index}`,
575
- type: 'definition',
576
- content: item.definition,
577
- pairId: index
578
- }));
579
- termCards = shuffle(termCards);
580
- definitionCards = shuffle(definitionCards);
581
- }
582
-
583
- function renderBoard() {
584
- const termsBoard = document.getElementById('termsBoard');
585
- const definitionsBoard = document.getElementById('definitionsBoard');
586
-
587
- termsBoard.innerHTML = '';
588
- definitionsBoard.innerHTML = '';
589
- termCards.forEach(card => {
590
- const cardEl = createCardElement(card);
591
- termsBoard.appendChild(cardEl);
592
- });
593
- definitionCards.forEach(card => {
594
- const cardEl = createCardElement(card);
595
- definitionsBoard.appendChild(cardEl);
596
- });
597
- }
598
-
599
- function createCardElement(card) {
600
- const cardEl = document.createElement('div');
601
- cardEl.className = 'card';
602
- cardEl.dataset.id = card.id;
603
- cardEl.dataset.pairId = card.pairId;
604
- cardEl.dataset.type = card.type;
605
- cardEl.innerHTML = `
606
- <div class="card-inner">
607
- <div class="card-front"></div>
608
- <div class="card-back ${card.type}">
609
- <div class="content">${card.content}</div>
610
- </div>
611
- </div>
612
- `;
613
- cardEl.addEventListener('click', () => handleCardClick(cardEl, card));
614
- return cardEl;
615
- }
616
-
617
- function handleCardClick(cardEl, card) {
618
- if (!canSelect) return;
619
- if (gameOver) return;
620
- if (cardEl.classList.contains('matched')) return;
621
- if (cardEl.classList.contains('removed')) return;
622
-
623
- // Flip the card
624
- cardEl.classList.add('flipped');
625
- if (card.type === 'term') {
626
- // Deselect previous term if any
627
- if (selectedTerm && selectedTerm !== cardEl) {
628
- selectedTerm.classList.remove('selected');
629
- if (!selectedDefinition) {
630
- selectedTerm.classList.remove('flipped');
631
- }
632
- }
633
- cardEl.classList.add('selected');
634
- selectedTerm = cardEl;
635
- } else {
636
- // Deselect previous definition if any
637
- if (selectedDefinition && selectedDefinition !== cardEl) {
638
- selectedDefinition.classList.remove('selected');
639
- if (!selectedTerm) {
640
- selectedDefinition.classList.remove('flipped');
641
- }
642
- }
643
- cardEl.classList.add('selected');
644
- selectedDefinition = cardEl;
645
- }
646
- // Check for match if both selected
647
- if (selectedTerm && selectedDefinition) {
648
- attempts++;
649
- document.getElementById('attempts').textContent = attempts;
650
- checkMatch();
651
- }
652
- }
653
 
654
- function checkMatch() {
655
- canSelect = false;
656
- const termPairId = selectedTerm.dataset.pairId;
657
- const defPairId = selectedDefinition.dataset.pairId;
658
- if (termPairId === defPairId) {
659
- // Match! First remove any previously matched pair
660
- if (lastMatchedTerm && lastMatchedDefinition) {
661
- lastMatchedTerm.classList.add('removing');
662
- lastMatchedDefinition.classList.add('removing');
663
-
664
- setTimeout(() => {
665
- lastMatchedTerm.classList.add('removed');
666
- lastMatchedTerm.classList.remove('removing');
667
- lastMatchedDefinition.classList.add('removed');
668
- lastMatchedDefinition.classList.remove('removing');
669
- }, 500);
670
- }
671
- setTimeout(() => {
672
- selectedTerm.classList.add('matched');
673
- selectedTerm.classList.remove('selected');
674
- selectedDefinition.classList.add('matched');
675
- selectedDefinition.classList.remove('selected');
676
-
677
- // Store this matched pair
678
- lastMatchedTerm = selectedTerm;
679
- lastMatchedDefinition = selectedDefinition;
680
-
681
- matchedPairs++;
682
- document.getElementById('matches').textContent = `${matchedPairs} / ${TERMS_PER_GAME}`;
683
-
684
- selectedTerm = null;
685
- selectedDefinition = null;
686
- canSelect = true;
687
- if (matchedPairs === TERMS_PER_GAME) {
688
- gameWon();
689
- }
690
- }, 500);
691
- } else {
692
- // No match - show red
693
- selectedTerm.classList.add('wrong');
694
- selectedDefinition.classList.add('wrong');
695
-
696
- setTimeout(() => {
697
- selectedTerm.classList.remove('wrong');
698
- selectedDefinition.classList.remove('wrong');
699
-
700
- setTimeout(() => {
701
- selectedTerm.classList.remove('flipped', 'selected');
702
- selectedDefinition.classList.remove('flipped', 'selected');
703
- selectedTerm = null;
704
- selectedDefinition = null;
705
- canSelect = true;
706
- }, 300);
707
- }, 800);
708
- }
709
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
710
 
711
- function gameWon() {
712
- clearInterval(timerInterval);
713
- gameOver = true;
714
- document.getElementById('winMessage').classList.add('show');
715
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
716
 
717
- function gameLost() {
718
- clearInterval(timerInterval);
719
- gameOver = true;
720
- canSelect = false;
721
- document.getElementById('loseMessage').classList.add('show');
722
  }
 
 
723
 
724
- function updateTimer() {
725
- secondsRemaining--;
726
-
727
- if (secondsRemaining <= 0) {
728
- document.getElementById('timer').textContent = '0:00';
729
- gameLost();
730
- return;
731
- }
732
- const mins = Math.floor(secondsRemaining / 60);
733
- const secs = secondsRemaining % 60;
734
- const timerEl = document.getElementById('timer');
735
- timerEl.textContent = `${mins}:${secs.toString().padStart(2, '0')}`;
736
-
737
- // Add warning color when under 1 minute
738
- if (secondsRemaining <= 60) {
739
- timerEl.classList.add('warning');
740
- } else {
741
- timerEl.classList.remove('warning');
742
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
743
  }
744
 
745
- function startNewGame() {
746
- // Reset state
747
- selectedTerm = null;
 
 
 
 
 
 
 
 
 
 
748
  selectedDefinition = null;
749
- lastMatchedTerm = null;
750
- lastMatchedDefinition = null;
751
- matchedPairs = 0;
752
- attempts = 0;
753
- canSelect = true;
754
- secondsRemaining = GAME_TIME;
755
- gameOver = false;
756
-
757
- // Select new random terms for this game
758
- glossaryData = selectRandomTerms();
759
-
760
- // Reset display
761
- document.getElementById('matches').textContent = `0 / ${TERMS_PER_GAME}`;
762
- document.getElementById('attempts').textContent = '0';
763
- document.getElementById('timer').textContent = '5:00';
764
- document.getElementById('timer').classList.remove('warning');
765
- document.getElementById('winMessage').classList.remove('show');
766
- document.getElementById('loseMessage').classList.remove('show');
767
-
768
- // Reset timer
769
- clearInterval(timerInterval);
770
- timerInterval = setInterval(updateTimer, 1000);
771
-
772
- // Create and render cards
773
- createCards();
774
- renderBoard();
775
- }
776
-
777
- function shuffleCards() {
778
- if (matchedPairs === TERMS_PER_GAME) return;
779
- if (gameOver) return;
780
-
781
- // Clear selections
782
- if (selectedTerm) {
783
- selectedTerm.classList.remove('flipped', 'selected');
784
- selectedTerm = null;
785
  }
786
- if (selectedDefinition) {
 
 
 
 
 
 
 
 
 
 
787
  selectedDefinition.classList.remove('flipped', 'selected');
 
788
  selectedDefinition = null;
789
- }
790
- // Get unmatched, non-removed cards
791
- const unmatchedTerms = termCards.filter(card => {
792
- const el = document.querySelector(`[data-id="${card.id}"]`);
793
- return el && !el.classList.contains('matched') && !el.classList.contains('removed');
794
- });
795
- const unmatchedDefs = definitionCards.filter(card => {
796
- const el = document.querySelector(`[data-id="${card.id}"]`);
797
- return el && !el.classList.contains('matched') && !el.classList.contains('removed');
798
- });
799
- // Shuffle
800
- const shuffledTerms = shuffle(unmatchedTerms);
801
- const shuffledDefs = shuffle(unmatchedDefs);
802
- // Update arrays
803
- let termIndex = 0;
804
- let defIndex = 0;
805
- termCards = termCards.map(card => {
806
- const el = document.querySelector(`[data-id="${card.id}"]`);
807
- if (el && (el.classList.contains('matched') || el.classList.contains('removed'))) return card;
808
- return shuffledTerms[termIndex++] || card;
809
- });
810
- definitionCards = definitionCards.map(card => {
811
- const el = document.querySelector(`[data-id="${card.id}"]`);
812
- if (el && (el.classList.contains('matched') || el.classList.contains('removed'))) return card;
813
- return shuffledDefs[defIndex++] || card;
814
- });
815
- renderBoard();
816
- }
817
-
818
- function showHint() {
819
- if (matchedPairs === TERMS_PER_GAME) return;
820
- if (!canSelect) return;
821
- if (gameOver) return;
822
-
823
- // Find first unmatched, non-removed pair
824
- for (let i = 0; i < TERMS_PER_GAME; i++) {
825
- const termEl = document.querySelector(`[data-id="term-${i}"]`);
826
- const defEl = document.querySelector(`[data-id="def-${i}"]`);
827
-
828
- if (termEl && !termEl.classList.contains('matched') && !termEl.classList.contains('removed')) {
829
- // Flash both cards briefly
830
- termEl.classList.add('flipped');
831
- defEl.classList.add('flipped');
832
-
833
- setTimeout(() => {
834
- if (!termEl.classList.contains('matched')) {
835
- termEl.classList.remove('flipped');
836
- }
837
- if (!defEl.classList.contains('matched')) {
838
- defEl.classList.remove('flipped');
839
- }
840
- }, 1500);
841
- break;
842
- }
843
- }
844
  }
845
 
846
- // Start the game on load
847
- startNewGame();
848
- </script>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
849
  </body>
850
  </html>
 
23
  }
24
  header {
25
  text-align: center;
26
+ margin-bottom: 15px;
27
  }
28
  h1 {
29
  font-size: 2rem;
30
+ margin-bottom: 8px;
31
  color: #002147;
32
  }
33
  .subtitle {
34
  color: #666;
35
  font-size: 1rem;
36
  }
37
+
38
+ /* ── Level Progress ── */
39
+ .level-progress {
40
+ display: flex;
41
+ justify-content: center;
42
+ align-items: center;
43
+ margin: 20px 0 14px;
44
+ }
45
+ .level-label {
46
+ padding: 7px 22px;
47
+ border-radius: 20px;
48
+ font-size: 0.85rem;
49
+ font-weight: 600;
50
+ background: #e8e8e8;
51
+ color: #888;
52
+ transition: background 0.4s ease, color 0.4s ease;
53
+ white-space: nowrap;
54
+ position: relative;
55
+ z-index: 1;
56
+ }
57
+ .level-label.completed {
58
+ background: #28a745;
59
+ color: #fff;
60
+ }
61
+ .level-label.active {
62
+ background: #002147;
63
+ color: #fff;
64
+ }
65
+ .level-connector {
66
+ width: 36px;
67
+ height: 4px;
68
+ background: #e0e0e0;
69
+ margin: 0 -2px;
70
+ transition: background 0.4s ease;
71
+ }
72
+ .level-connector.completed {
73
+ background: #28a745;
74
+ }
75
+
76
+ /* ── Stats ── */
77
  .stats {
78
  display: flex;
79
  justify-content: center;
80
  gap: 30px;
81
+ margin: 12px 0 18px;
82
  flex-wrap: wrap;
83
  }
84
  .stat {
 
102
  text-transform: uppercase;
103
  letter-spacing: 1px;
104
  }
105
+
106
+ /* ── Controls ── */
107
  .controls {
108
  display: flex;
109
  justify-content: center;
110
  gap: 15px;
111
+ margin-bottom: 25px;
112
  flex-wrap: wrap;
113
  }
114
  button {
 
140
  .btn-icon {
141
  font-size: 1.2rem;
142
  }
143
+
144
+ /* ── Instructions ── */
145
  .instructions {
146
  background: #f8f9fa;
147
  padding: 20px;
148
  border-radius: 10px;
149
+ margin-bottom: 25px;
150
  text-align: center;
151
  border: 1px solid #e0e0e0;
152
  }
 
168
  display: inline-block;
169
  margin-top: 10px;
170
  }
171
+
172
+ /* ── Messages ── */
173
  .message {
174
  text-align: center;
175
+ padding: 25px 20px;
176
+ margin: 15px 0;
177
  border-radius: 10px;
178
+ font-size: 1.3rem;
179
  display: none;
180
  }
181
  .message.show {
 
191
  border: 2px solid #dc3545;
192
  color: #dc3545;
193
  }
194
+ .message .msg-subtitle {
195
+ font-size: 0.95rem;
196
+ margin-top: 8px;
197
+ color: #555;
198
+ }
199
+ /* Keep buttons inside messages looking correct despite inherited text colour */
200
+ .message.success button,
201
+ .message.failure button {
202
+ color: white;
203
+ background: #002147;
204
+ margin-top: 15px;
205
+ font-size: 1rem;
206
+ }
207
+ .message.success button:hover,
208
+ .message.failure button:hover {
209
+ background: #003366;
210
+ }
211
+
212
+ /* ── Game Board ── */
213
  .game-area {
214
  display: flex;
215
  flex-direction: column;
 
248
  grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
249
  padding: 20px 0 150px 0;
250
  }
251
+
252
+ /* ── Cards ── */
253
  .card {
254
  height: 80px;
255
  perspective: 1000px;
 
320
  border-width: 3px;
321
  box-shadow: 0 0 15px rgba(255, 193, 7, 0.5);
322
  }
323
+ /* Lift selected definition cards */
324
  .definitions-board .card.selected {
325
  z-index: 100;
326
  position: relative;
 
343
  padding: 20px;
344
  line-height: 1.5;
345
  }
346
+ /* Lift selected term cards */
347
  .terms-board .card.selected {
348
  z-index: 100;
349
  position: relative;
 
370
  visibility: hidden;
371
  pointer-events: none;
372
  }
373
+
374
+ /* ── Attribution ── */
375
  .attribution {
376
  background: #f8f9fa;
377
  padding: 20px;
 
390
  .attribution a:hover {
391
  text-decoration: underline;
392
  }
393
+
394
+ /* ── Responsive ── */
395
  @media (max-width: 600px) {
396
+ h1 { font-size: 1.5rem; }
 
 
397
  .game-board {
398
  grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
399
  }
400
  .definitions-board {
401
  grid-template-columns: 1fr;
402
  }
403
+ .card-back.definition { font-size: 0.75rem; }
 
 
404
  .definitions-board .card.selected .card-back {
405
  width: 100%;
406
  min-width: unset;
407
  font-size: 0.95rem;
408
  }
409
+ .level-label {
410
+ padding: 6px 14px;
411
+ font-size: 0.78rem;
412
+ }
413
+ .level-connector { width: 20px; }
414
  }
415
  </style>
416
  </head>
417
  <body>
418
+ <div class="container">
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
419
 
420
+ <header>
421
+ <h1>🗃️ Digital Preservation Glossary</h1>
422
+ <p class="subtitle">Match the terms with their definitions</p>
423
+ </header>
 
 
 
 
 
 
 
424
 
425
+ <!-- Level progress bar -->
426
+ <div class="level-progress">
427
+ <div class="level-label active" id="level-label-0">Level 1</div>
428
+ <div class="level-connector" id="level-conn-0"></div>
429
+ <div class="level-label" id="level-label-1">Level 2</div>
430
+ <div class="level-connector" id="level-conn-1"></div>
431
+ <div class="level-label" id="level-label-2">Level 3</div>
432
+ </div>
433
 
434
+ <!-- Stats -->
435
+ <div class="stats">
436
+ <div class="stat">
437
+ <div class="stat-value" id="matches">0 / 10</div>
438
+ <div class="stat-label">✓ Matches</div>
439
  </div>
440
+ <div class="stat">
441
+ <div class="stat-value" id="attempts">0</div>
442
+ <div class="stat-label">Attempts</div>
443
  </div>
444
+ <div class="stat">
445
+ <div class="stat-value" id="timer">2:30</div>
446
+ <div class="stat-label">⏱ Time Left</div>
 
 
 
 
 
 
 
 
 
 
 
447
  </div>
448
+ </div>
449
+
450
+ <!-- Controls -->
451
+ <div class="controls">
452
+ <button onclick="startNewGame()">
453
+ <span class="btn-icon">🔄</span> New Game
454
+ </button>
455
+ <button class="secondary" onclick="shuffleCards()">
456
+ <span class="btn-icon">🔀</span> Shuffle
457
+ </button>
458
+ <button class="secondary" onclick="showHint()">
459
+ <span class="btn-icon">💡</span> Hint
460
+ </button>
461
+ </div>
462
+
463
+ <!-- Instructions -->
464
+ <div class="instructions">
465
+ <h2>How to Play</h2>
466
+ <p>Complete all <strong>3 levels</strong> to win! Click a <strong>term card</strong>, then its matching <strong>definition</strong> below.
467
+ Matched pairs turn <span style="color:#28a745;font-weight:bold">green</span>, then disappear on your next match.
468
+ Each level has a tighter time limit — stay sharp!</p>
469
+ <div class="term-count">📚 10 terms per level · 3 levels · From 108 terms in the DPC Glossary</div>
470
+ </div>
471
+
472
+ <!-- Messages -->
473
+ <div class="message success" id="levelCompleteMessage">
474
+ <div id="levelCompleteTitle">🎉 Level 1 Complete!</div>
475
+ <div class="msg-subtitle" id="levelCompleteSubtitle"></div>
476
+ <button onclick="nextLevel()" id="nextLevelBtn">Next Level →</button>
477
+ </div>
478
+
479
+ <div class="message success" id="winMessage">
480
+ 🎉 Congratulations! You completed all 3 levels!
481
+ <div class="msg-subtitle">You've mastered the Digital Preservation Glossary!</div>
482
+ <button onclick="startNewGame()">🔄 Play Again</button>
483
+ </div>
484
 
485
+ <div class="message failure" id="loseMessage">
486
+ Time's up!
487
+ <div class="msg-subtitle">Don't give up — try this level again!</div>
488
+ <button onclick="retryLevel()">🔄 Retry Level</button>
489
+ </div>
490
+
491
+ <!-- Game board -->
492
+ <div class="game-area">
493
+ <div class="section terms-section">
494
+ <h2 class="section-header">
495
+ <span class="icon">📝</span> Terms &amp; Acronyms
496
+ </h2>
497
+ <div class="game-board terms-board" id="termsBoard"></div>
498
+ </div>
499
+ <div class="section definitions-section">
500
+ <h2 class="section-header">
501
+ <span class="icon">📖</span> Definitions
502
+ </h2>
503
+ <div class="game-board definitions-board" id="definitionsBoard"></div>
504
  </div>
505
  </div>
506
 
507
+ <!-- Attribution -->
508
+ <div class="attribution">
509
+ <strong>Source:</strong> Digital Preservation Handbook, 2nd Edition,
510
+ <a href="https://www.dpconline.org/handbook" target="_blank">https://www.dpconline.org/handbook</a><br>
511
+ Digital Preservation Coalition © 2015 licensed under the
512
+ <a href="http://www.nationalarchives.gov.uk/doc/open-government-licence/version/3/" target="_blank">Open Government Licence v3.0</a>.<br><br>
513
+ This interactive learning tool was created for educational purposes.
514
+ For the most up-to-date glossary, please visit the
515
+ <a href="https://www.dpconline.org/handbook/glossary" target="_blank">DPC Handbook Glossary</a>.
516
+ </div>
517
+ </div>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
518
 
519
+ <script>
520
+ // =====================================================================
521
+ // GLOSSARY DATA (108 terms – unchanged)
522
+ // =====================================================================
523
+ const allGlossaryData = [
524
+ { term:"Access", definition:"As defined in the Handbook, access is assumed to mean continued, ongoing usability of a digital resource, retaining all qualities of authenticity, accuracy and functionality deemed to be essential for the purposes the digital material was created and/or acquired for." },
525
+ { term:"ADS", definition:"Archaeology Data Service. A UK based service active in digital preservation." },
526
+ { term:"AIP", definition:"Archival Information Package. An Information Package, consisting of the Content Information and the associated Preservation Description Information (PDI), which is preserved within an OAIS (OAIS term)." },
527
+ { term:"AMIA", definition:"Association of Moving Image Archives, an organisation active in the field of moving image archiving." },
528
+ { term:"ARC", definition:"Container format for websites devised by the Internet Archive, superseded by WARC." },
529
+ { term:"ASCII", definition:"American Standard Code for Information Interchange, standard for electronic text." },
530
+ { term:"Authentication", definition:"A mechanism which attempts to establish the authenticity of digital materials at a particular point in time. For example, digital signatures." },
531
+ { term:"Authenticity", definition:"The digital material is what it purports to be. In the case of electronic records, it refers to the trustworthiness of the electronic record as a record. In the case of \"born digital\" and digitised materials, it refers to the fact that whatever is being cited is the same as it was when it was first created unless the accompanying metadata indicates any changes." },
532
+ { term:"Bit", definition:"A bit is the basic unit of information in computing. It can have only one of two values commonly represented as either a 0 or 1." },
533
+ { term:"Bit Preservation", definition:"A term used to denote a very basic level of preservation of digital resource as it was submitted (literally preservation of the bits forming a digital resource). It may include maintaining onsite and offsite backup copies, virus checking, fixity-checking, and periodic refreshment to new storage media." },
534
+ { term:"Born-Digital", definition:"Digital materials which are not intended to have an analogue equivalent, either as the originating source or as a result of conversion to analogue form." },
535
+ { term:"BWF", definition:"Broadcast WAV format, the European Broadcasting Union standard for a WAV file, with extra metadata." },
536
+ { term:"Byte (B)", definition:"A unit of digital information that most commonly consists of eight bits. Historically, the byte was the number of bits used to encode a single character of text in a computer." },
537
+ { term:"CCSDS", definition:"Consultative Committee for Space Data Systems, the body responsible for the OAIS Reference Model." },
538
+ { term:"Chain of Custody", definition:"A key concept in forensics whereby the custody and provenance of digital hardware, media and files are safeguarded through, for example, the appointment of evidence custodians." },
539
+ { term:"Checksum", definition:"A unique numerical signature derived from a file. Used to compare copies." },
540
+ { term:"CLIR", definition:"Council on Library and Information Resources. US based organisation active in digital preservation." },
541
+ { term:"CNI", definition:"Coalition for Networked Information. US based organisation active in digital preservation." },
542
+ { term:"Continuing Access", definition:"Refers to the right of a subscriber to an electronic publication and their users to have on-going permanent access to electronic materials which have already been leased and paid for by the subscriber from a publisher." },
543
+ { term:"COPTR", definition:"Community Owned digital Preservation Tool Registry hosted by The Open Preservation Foundation." },
544
+ { term:"Crawl", definition:"The act of browsing the web automatically and methodically to index or download content and other data from the web. The software to do this is often called a web crawler." },
545
+ { term:"Dark Archive", definition:"An archive that cannot be accessed by any current users but may be accessible at future dates subject to the occurrence of specific pre-defined events ('trigger event')." },
546
+ { term:"DCC", definition:"Digital Curation Centre. A UK based organisation active in digital preservation." },
547
+ { term:"DDI", definition:"Data Documentation Initiative. A de facto international metadata standard for describing data from the social, behavioral, and economic sciences." },
548
+ { term:"Designated Community", definition:"An identified group of potential consumers who should be able to understand a particular set of information from an archive. These consumers may consist of multiple communities, are designated by the archive, and may change over time (OAIS term)." },
549
+ { term:"Digital Archiving", definition:"This term is used very differently within sectors. The library and archiving communities often use it interchangeably with digital preservation. Computing professionals tend to use it to mean the process of backup and ongoing maintenance." },
550
+ { term:"Digital Forensics", definition:"The application of scientific technical methods and tools toward the preservation, collection, validation, identification, analysis, interpretation, documentation and presentation of digital information derived after-the-fact from digital sources." },
551
+ { term:"Dim Archive", definition:"Provides bit preservation for the content plus digital preservation planning and actions for long-term perpetual access, and also limited current access." },
552
+ { term:"DigCurV", definition:"Digital Curator Vocational Education Europe. A project funded by the European Commission to establish a curriculum framework for vocational training in digital curation." },
553
+ { term:"Digital Materials", definition:"A broad term encompassing digital surrogates created as a result of converting analogue materials to digital form (digitisation), and \"born digital\" for which there has never been and is never intended to be an analogue equivalent, and digital records." },
554
+ { term:"Digital Preservation", definition:"Refers to the series of managed activities necessary to ensure continued access to digital materials for as long as necessary. It includes all actions required to maintain access beyond the limits of media failure or technological and organisational change." },
555
+ { term:"Short-term preservation", definition:"Access to digital materials either for a defined period of time while use is predicted but which does not extend beyond the foreseeable future and/or until it becomes inaccessible because of changes in technology." },
556
+ { term:"Medium-term preservation", definition:"Continued access to digital materials beyond changes in technology for a defined period of time but not indefinitely." },
557
+ { term:"Long-term preservation", definition:"Continued access to digital materials, or at least to the information contained in them, indefinitely." },
558
+ { term:"Digital Preservation Management Workshop", definition:"An intensive training workshop and online tutorial developed and maintained by Cornell University Library, extended by ICPSR, and now maintained by MIT Libraries." },
559
+ { term:"Digital Publications", definition:"\"Born digital\" objects which have been released for public access and either made available or distributed free of charge or for a fee. They may be networked publications or physical format publications distributed on formats such as optical disks." },
560
+ { term:"Digitisation", definition:"The process of creating digital files by scanning or otherwise converting analogue materials. The resulting digital copy, or digital surrogate, would then be classed as digital material." },
561
+ { term:"DIP", definition:"Dissemination Information Package. An Information Package, derived from one or more Archival Information Packages (AIPs), and sent by Archives to the Consumer in response to a request to the OAIS (OAIS term)." },
562
+ { term:"DLF", definition:"Digital Library Federation. A US based organisation active in digital preservation." },
563
+ { term:"Documentation", definition:"The information provided by a creator and the repository which provides enough information to establish provenance, history and context and to enable its use by others." },
564
+ { term:"DOI", definition:"Digital Object Identifier. A technical and organisational infrastructure for the registration and use of persistent identifiers widely used in digital publications and for research data." },
565
+ { term:"DPC", definition:"Digital Preservation Coalition. A UK and Ireland based organisation active in digital preservation and responsible for the Digital Preservation Handbook." },
566
+ { term:"DPTP", definition:"Digital Preservation Training Programme, an intensive training course run by the University of London Computer Centre." },
567
+ { term:"DRAMBORA", definition:"Digital Repository Audit Methodology Based on Risk Assessment. A set of risk assessment tools developed by the Digital Curation Centre." },
568
+ { term:"DROID", definition:"A file profiling tool developed and distributed by TNA to identify file formats. Based on PRONOM." },
569
+ { term:"Electronic Records", definition:"Records created digitally in the day-to-day business of the organisation and assigned formal status by the organisation. They may include word processing documents, emails, databases, or intranet web pages." },
570
+ { term:"Emulation", definition:"A means of overcoming technological obsolescence of hardware and software by developing techniques for imitating obsolete systems on future generations of computers." },
571
+ { term:"Escrow", definition:"A widespread legal practice of the deposit of content or software source code with a third party. Escrow takes place in a contractual relationship, formalized in an escrow agreement." },
572
+ { term:"FIAF", definition:"International Federation of Film Archives, an association of the world's leading film archives." },
573
+ { term:"FIAT", definition:"International Federation of Television Archives, a professional association for those engaged in the preservation and exploitation of broadcast archives." },
574
+ { term:"File Format", definition:"A standard way that information is encoded for storage in a computer file. It tells the computer how to display, print, process, and save the information. A particular file format is often indicated by a file name extension." },
575
+ { term:"Fixity Check", definition:"A method for ensuring the integrity of a file and verifying it has not been altered or corrupted. It is most often accomplished by computing checksums such as MD5, SHA1 or SHA256 for a file and comparing them to a stored value." },
576
+ { term:"GIF", definition:"Graphic Interchange Format, an image which typically uses lossy compression." },
577
+ { term:"Gigabyte (GB)", definition:"A unit of digital information often used to describe data or data storage size, equates to approximately 1,000 Megabytes (MB)." },
578
+ { term:"GIS", definition:"Geographical Information System, a system that processes mapping and data together." },
579
+ { term:"HTML", definition:"Hypertext Markup Language, a format used to present text and other information on the World Wide Web. Since 1996, versions have been maintained by the World Wide Web Consortium (W3C)." },
580
+ { term:"IASA", definition:"International Association of Sound and Audiovisual Archives, an association for archives that preserve recorded sound and audiovisual documents." },
581
+ { term:"IIPC", definition:"The International Internet Preservation Consortium." },
582
+ { term:"Information Assurance", definition:"An aspect of digital security, specifically directed at ensuring that the quality of the information is demonstrably safeguarded, that it has not been tampered with or accessed inappropriately." },
583
+ { term:"Ingest", definition:"The process of turning a Submission Information Package (SIP) into an Archival Information Package (AIP), i.e. putting data into a digital archive (OAIS term)." },
584
+ { term:"InterPARES project", definition:"International Research on Permanent Authentic Records in Electronic Systems." },
585
+ { term:"ISO", definition:"International Organization for Standardization." },
586
+ { term:"JHove2", definition:"A characterization tool for digital objects. Characterisation is comprised of four elements: identifying the object's format; validating that the object conforms to its format's technical norms; extracting technical metadata; and assessing whether the object should be accepted into a repository." },
587
+ { term:"JPEG", definition:"Joint Photographic Experts Group, a committee that oversees international standards for compression and processing of digital photographs. The majority of JPEG formats are lossy." },
588
+ { term:"JPEG 2000", definition:"A revision of the JPEG format which can use lossless compression." },
589
+ { term:"Kilobyte (KB)", definition:"A unit of digital information often used to describe data or data storage size, equates to approximately 1,000 Bytes." },
590
+ { term:"Life-cycle Management", definition:"The need actively to manage digital resources at each stage of their life-cycle and to recognise the inter-dependencies between each stage, commencing preservation activities as early as practicable." },
591
+ { term:"Lossless Compression", definition:"A mechanism for reducing file sizes that retains all original data." },
592
+ { term:"Lossy Compression", definition:"A mechanism for reducing file sizes that typically discards data." },
593
+ { term:"LOTAR", definition:"LOng Term Archiving and Retrieval - a digital preservation standard for 3D CAD models and product data management information developed by LOTAR International." },
594
+ { term:"Megabyte (MB)", definition:"A unit of digital information often used to describe data or data storage size, equates to approximately 1,000 Kilobytes (KB)." },
595
+ { term:"Metadata", definition:"Information which describes significant aspects of a resource. The emphasis in this Handbook is on what metadata are required to manage and preserve digital materials over time and ensure essential contextual, historical, and technical information are preserved." },
596
+ { term:"METS", definition:"Metadata Encoding and Transmission Standard, a standard for presenting metadata using XML." },
597
+ { term:"Migration", definition:"A means of overcoming technological obsolescence by transferring digital resources from one hardware/software generation to the next. The purpose is to preserve the intellectual content and retain the ability to retrieve, display, and use digital objects." },
598
+ { term:"MIME", definition:"Multipurpose Internet Mail Extensions. A protocol for including non-ASCII information in email messages." },
599
+ { term:"MPEG", definition:"Moving Picture Experts Group. A committee responsible for the development of international standards for compression, decompression, processing, and coded representation of moving pictures and audio." },
600
+ { term:"NCDD", definition:"The Netherlands Coalition for Digital Preservation." },
601
+ { term:"NDSA", definition:"National Digital Stewardship Alliance, a US based organisation active in digital preservation." },
602
+ { term:"NESTOR", definition:"The German competence network for digital preservation." },
603
+ { term:"OAIS", definition:"Open Archival Information System. An Archive consisting of an organization of people and systems that has accepted the responsibility to preserve information and make it available for a Designated Community. Also refers to the reference model standard (ISO 14721)." },
604
+ { term:"OPF", definition:"Open Preservation Foundation, formerly the Open Planets Foundation." },
605
+ { term:"PAIMAS", definition:"Space Data and Information Transfer Systems - Producer-Archive Interface - Methodology Abstract Standard. This ISO 20652:2006 standard covers the first stages of the ingest process defined by OAIS reference model." },
606
+ { term:"PDF", definition:"Portable Document Format, a set of formats and open standards maintained by the International Organization for Standardization for producing and sharing electronic documents, originally developed by Adobe Systems." },
607
+ { term:"PDF/A", definition:"Versions of the PDF standard intended for archival use." },
608
+ { term:"PDI", definition:"Preservation Description Information. The information which is necessary for adequate preservation of the Content Information and which can be categorized as Provenance, Reference, Fixity, Context, and Access Rights Information (OAIS term)." },
609
+ { term:"Petabyte (PB)", definition:"A unit of digital information often used to describe data or data storage size, equates to approximately 1,000 Terabytes (TB)." },
610
+ { term:"PIN", definition:"Pérennisation des Informations Numériques, the French national interest group for digital preservation." },
611
+ { term:"PREMIS", definition:"Preservation Metadata: Implementation Strategies. A de facto standard for digital preservation metadata." },
612
+ { term:"PRONOM", definition:"A database of file formats, software products and other technical components required to support long-term access to electronic records and other digital objects. Used with DROID." },
613
+ { term:"PST", definition:"Personal Storage Table is a file extension for local 'personal stores' written by Microsoft Outlook. PST files contain email messages and calendar entries." },
614
+ { term:"Reformatting", definition:"Copying information content from one storage medium to a different storage medium (media reformatting) or converting from one file format to a different file format (file reformatting)." },
615
+ { term:"Refreshing", definition:"Copying information content from one storage media to the same storage media." },
616
+ { term:"Sandbox Containment", definition:"A secure computing environment for running novel, unattested or experimental code or changes in code, including potentially malicious code. The environment is self-contained with tightly controlled resources." },
617
+ { term:"SGML", definition:"Standard Generalized Markup Language, an ISO standard for how to specify a document markup language or tag set." },
618
+ { term:"Significant properties", definition:"Characteristics of digital and intellectual objects that must be preserved over time in order to ensure the continued accessibility, usability and meaning of the objects." },
619
+ { term:"SIP", definition:"Submission Information Package. An Information Package that is delivered by the Producer to the OAIS for use in the construction or update of one or more Archival Information Packages (AIPs) (OAIS term)." },
620
+ { term:"SMPTE", definition:"Society of Motion Picture and Television Engineers, a professional organisation and technical standards body for television and motion picture." },
621
+ { term:"TDR", definition:"Trusted Digital Repository. A repository with a mission to provide reliable, long-term access to managed digital resources to its designated community, now and into the future." },
622
+ { term:"Terabyte (TB)", definition:"A unit of digital information often used to describe data or data storage size, equates to approximately 1,000 Gigabytes (GB)." },
623
+ { term:"Three-Legged Stool", definition:"A conceptual approach to digital preservation that suggests a fully implemented programme addresses organisational issues, technological concerns, and funding questions, balancing them like a three-legged stool." },
624
+ { term:"TIFF", definition:"Tagged Image File Format, a common format for images typically lossless." },
625
+ { term:"TRAC", definition:"Trusted Repository Audit and Certification, toolkit for auditing a digital repository." },
626
+ { term:"Trigger Event", definition:"Terminology used when specific conditions relating to an electronic publication and its continued delivery are met. If the publication is no longer available from the publisher, a trigger event has occurred, which can set in motion access via an archive." },
627
+ { term:"UKWA", definition:"UK Web Archive." },
628
+ { term:"WARC", definition:"The WARC (Web ARChive) format is a container format for archived websites (ISO 28500:2009). It is a revision of the Internet Archive's ARC File Format." },
629
+ { term:"WAV", definition:"The standard file wrapper for audio; see BWF (Broadcast WAV Format) for the professional variant." },
630
+ { term:"Writeblockers", definition:"Tools that prevent an examination computer system from writing or altering a collection or subject hard drive or other digital media object." },
631
+ { term:"XML", definition:"Extensible Markup Language, a widely used standard (derived from SGML), for representing structured information. It is maintained by the World Wide Web Consortium (W3C)." }
632
+ ];
633
 
634
+ // =====================================================================
635
+ // CONFIGURATION
636
+ // =====================================================================
637
+ const TERMS_PER_LEVEL = 10;
638
+ const LEVELS = [
639
+ { time: 150 }, // Level 1 – 2:30
640
+ { time: 120 }, // Level 2 – 2:00
641
+ { time: 90 } // Level 3 – 1:30
642
+ ];
643
+
644
+ // =====================================================================
645
+ // STATE
646
+ // =====================================================================
647
+ let glossaryData = [];
648
+ let termCards = [];
649
+ let definitionCards = [];
650
+ let selectedTerm = null;
651
+ let selectedDefinition = null;
652
+ let lastMatchedTerm = null;
653
+ let lastMatchedDefinition = null;
654
+ let matchedPairs = 0;
655
+ let attempts = 0;
656
+ let canSelect = true;
657
+ let timerInterval = null;
658
+ let secondsRemaining = LEVELS[0].time;
659
+ let gameOver = false;
660
+ let currentLevel = 0;
661
+
662
+ // =====================================================================
663
+ // UTILITIES
664
+ // =====================================================================
665
+ function shuffle(array) {
666
+ const a = [...array];
667
+ for (let i = a.length - 1; i > 0; i--) {
668
+ const j = Math.floor(Math.random() * (i + 1));
669
+ [a[i], a[j]] = [a[j], a[i]];
670
+ }
671
+ return a;
672
+ }
673
+
674
+ function selectRandomTerms() {
675
+ return shuffle(allGlossaryData).slice(0, TERMS_PER_LEVEL);
676
+ }
677
+
678
+ function formatTime(totalSeconds) {
679
+ const m = Math.floor(totalSeconds / 60);
680
+ const s = totalSeconds % 60;
681
+ return `${m}:${s.toString().padStart(2, '0')}`;
682
+ }
683
+
684
+ // =====================================================================
685
+ // UI HELPERS
686
+ // =====================================================================
687
+ function updateLevelDisplay() {
688
+ for (let i = 0; i < LEVELS.length; i++) {
689
+ const label = document.getElementById(`level-label-${i}`);
690
+ label.className = 'level-label';
691
+ if (i < currentLevel) label.classList.add('completed');
692
+ else if (i === currentLevel) label.classList.add('active');
693
+ // else: stays plain (future level)
694
 
695
+ if (i < LEVELS.length - 1) {
696
+ const conn = document.getElementById(`level-conn-${i}`);
697
+ conn.className = 'level-connector';
698
+ if (i < currentLevel) conn.classList.add('completed');
 
699
  }
700
+ }
701
+ }
702
 
703
+ function hideAllMessages() {
704
+ ['levelCompleteMessage', 'winMessage', 'loseMessage'].forEach(id =>
705
+ document.getElementById(id).classList.remove('show')
706
+ );
707
+ }
708
+
709
+ // =====================================================================
710
+ // CARDS
711
+ // =====================================================================
712
+ function createCards() {
713
+ termCards = glossaryData.map((item, i) => ({
714
+ id: `term-${i}`, type: 'term', content: item.term, pairId: i
715
+ }));
716
+ definitionCards = glossaryData.map((item, i) => ({
717
+ id: `def-${i}`, type: 'definition', content: item.definition, pairId: i
718
+ }));
719
+ termCards = shuffle(termCards);
720
+ definitionCards = shuffle(definitionCards);
721
+ }
722
+
723
+ function renderBoard() {
724
+ const tb = document.getElementById('termsBoard');
725
+ const db = document.getElementById('definitionsBoard');
726
+ tb.innerHTML = '';
727
+ db.innerHTML = '';
728
+ termCards.forEach(c => tb.appendChild(createCardElement(c)));
729
+ definitionCards.forEach(c => db.appendChild(createCardElement(c)));
730
+ }
731
+
732
+ function createCardElement(card) {
733
+ const el = document.createElement('div');
734
+ el.className = 'card';
735
+ el.dataset.id = card.id;
736
+ el.dataset.pairId = card.pairId;
737
+ el.dataset.type = card.type;
738
+ el.innerHTML = `
739
+ <div class="card-inner">
740
+ <div class="card-front"></div>
741
+ <div class="card-back ${card.type}">
742
+ <div class="content">${card.content}</div>
743
+ </div>
744
+ </div>`;
745
+ el.addEventListener('click', () => handleCardClick(el, card));
746
+ return el;
747
+ }
748
+
749
+ // =====================================================================
750
+ // INTERACTION
751
+ // =====================================================================
752
+ function handleCardClick(cardEl, card) {
753
+ if (!canSelect || gameOver) return;
754
+ if (cardEl.classList.contains('matched') || cardEl.classList.contains('removed')) return;
755
+
756
+ cardEl.classList.add('flipped');
757
+
758
+ if (card.type === 'term') {
759
+ if (selectedTerm && selectedTerm !== cardEl) {
760
+ selectedTerm.classList.remove('selected');
761
+ if (!selectedDefinition) selectedTerm.classList.remove('flipped');
762
+ }
763
+ cardEl.classList.add('selected');
764
+ selectedTerm = cardEl;
765
+ } else {
766
+ if (selectedDefinition && selectedDefinition !== cardEl) {
767
+ selectedDefinition.classList.remove('selected');
768
+ if (!selectedTerm) selectedDefinition.classList.remove('flipped');
769
+ }
770
+ cardEl.classList.add('selected');
771
+ selectedDefinition = cardEl;
772
+ }
773
+
774
+ if (selectedTerm && selectedDefinition) {
775
+ attempts++;
776
+ document.getElementById('attempts').textContent = attempts;
777
+ checkMatch();
778
+ }
779
+ }
780
+
781
+ function checkMatch() {
782
+ canSelect = false;
783
+
784
+ if (selectedTerm.dataset.pairId === selectedDefinition.dataset.pairId) {
785
+ // ── Correct match ──
786
+ // Fade out the previously green pair (if any)
787
+ if (lastMatchedTerm && lastMatchedDefinition) {
788
+ lastMatchedTerm.classList.add('removing');
789
+ lastMatchedDefinition.classList.add('removing');
790
+ setTimeout(() => {
791
+ lastMatchedTerm.classList.add('removed');
792
+ lastMatchedTerm.classList.remove('removing');
793
+ lastMatchedDefinition.classList.add('removed');
794
+ lastMatchedDefinition.classList.remove('removing');
795
+ }, 500);
796
  }
797
 
798
+ setTimeout(() => {
799
+ selectedTerm.classList.add('matched');
800
+ selectedTerm.classList.remove('selected');
801
+ selectedDefinition.classList.add('matched');
802
+ selectedDefinition.classList.remove('selected');
803
+
804
+ lastMatchedTerm = selectedTerm;
805
+ lastMatchedDefinition = selectedDefinition;
806
+
807
+ matchedPairs++;
808
+ document.getElementById('matches').textContent = `${matchedPairs} / ${TERMS_PER_LEVEL}`;
809
+
810
+ selectedTerm = null;
811
  selectedDefinition = null;
812
+ canSelect = true;
813
+
814
+ // Short pause so the player sees the last green pair before the message
815
+ if (matchedPairs === TERMS_PER_LEVEL) {
816
+ setTimeout(levelComplete, 700);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
817
  }
818
+ }, 500);
819
+
820
+ } else {
821
+ // ── Wrong match ──
822
+ selectedTerm.classList.add('wrong');
823
+ selectedDefinition.classList.add('wrong');
824
+ setTimeout(() => {
825
+ selectedTerm.classList.remove('wrong');
826
+ selectedDefinition.classList.remove('wrong');
827
+ setTimeout(() => {
828
+ selectedTerm.classList.remove('flipped', 'selected');
829
  selectedDefinition.classList.remove('flipped', 'selected');
830
+ selectedTerm = null;
831
  selectedDefinition = null;
832
+ canSelect = true;
833
+ }, 300);
834
+ }, 800);
835
+ }
836
+ }
837
+
838
+ // =====================================================================
839
+ // LEVEL / GAME FLOW
840
+ // =====================================================================
841
+ function levelComplete() {
842
+ clearInterval(timerInterval);
843
+ gameOver = true;
844
+
845
+ if (currentLevel < LEVELS.length - 1) {
846
+ // ── Not the final level – show "level complete" prompt ──
847
+ const nextNum = currentLevel + 2; // 1-based display number
848
+ const nextTime = LEVELS[currentLevel + 1].time;
849
+
850
+ document.getElementById('levelCompleteTitle').textContent =
851
+ `🎉 Level ${currentLevel + 1} Complete!`;
852
+
853
+ if (nextNum === LEVELS.length) {
854
+ // The upcoming level is the last one
855
+ document.getElementById('levelCompleteSubtitle').textContent =
856
+ `Ready for the final challenge? You'll have ${formatTime(nextTime)} this time.`;
857
+ document.getElementById('nextLevelBtn').textContent = '⚡ Final Level →';
858
+ } else {
859
+ document.getElementById('levelCompleteSubtitle').textContent =
860
+ `Ready for Level ${nextNum}? You'll have ${formatTime(nextTime)} this time.`;
861
+ document.getElementById('nextLevelBtn').textContent = `Go to Level ${nextNum} →`;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
862
  }
863
 
864
+ document.getElementById('levelCompleteMessage').classList.add('show');
865
+
866
+ } else {
867
+ // ── All three levels finished – overall win ──
868
+ // Mark every level as completed in the progress bar
869
+ currentLevel = LEVELS.length;
870
+ updateLevelDisplay();
871
+ document.getElementById('winMessage').classList.add('show');
872
+ }
873
+ }
874
+
875
+ function gameLost() {
876
+ clearInterval(timerInterval);
877
+ gameOver = true;
878
+ canSelect = false;
879
+ document.getElementById('loseMessage').classList.add('show');
880
+ }
881
+
882
+ // =====================================================================
883
+ // TIMER
884
+ // =====================================================================
885
+ function updateTimer() {
886
+ secondsRemaining--;
887
+ if (secondsRemaining <= 0) {
888
+ document.getElementById('timer').textContent = '0:00';
889
+ gameLost();
890
+ return;
891
+ }
892
+ const timerEl = document.getElementById('timer');
893
+ timerEl.textContent = formatTime(secondsRemaining);
894
+ // Warning colour in the last 30 seconds
895
+ timerEl.classList.toggle('warning', secondsRemaining <= 30);
896
+ }
897
+
898
+ // =====================================================================
899
+ // PUBLIC ACTIONS (called from HTML or from level-flow helpers)
900
+ // =====================================================================
901
+ function startNewGame() {
902
+ currentLevel = 0;
903
+ initLevel();
904
+ }
905
+
906
+ function nextLevel() {
907
+ currentLevel++;
908
+ initLevel();
909
+ }
910
+
911
+ function retryLevel() {
912
+ initLevel(); // currentLevel stays the same
913
+ }
914
+
915
+ function initLevel() {
916
+ // Reset per-level state
917
+ selectedTerm = null;
918
+ selectedDefinition = null;
919
+ lastMatchedTerm = null;
920
+ lastMatchedDefinition = null;
921
+ matchedPairs = 0;
922
+ attempts = 0;
923
+ canSelect = true;
924
+ gameOver = false;
925
+ secondsRemaining = LEVELS[currentLevel].time;
926
+
927
+ // Pick a fresh random set of terms
928
+ glossaryData = selectRandomTerms();
929
+
930
+ // Refresh UI
931
+ hideAllMessages();
932
+ updateLevelDisplay();
933
+ document.getElementById('matches').textContent = `0 / ${TERMS_PER_LEVEL}`;
934
+ document.getElementById('attempts').textContent = '0';
935
+ document.getElementById('timer').textContent = formatTime(secondsRemaining);
936
+ document.getElementById('timer').classList.remove('warning');
937
+
938
+ // (Re)start countdown
939
+ clearInterval(timerInterval);
940
+ timerInterval = setInterval(updateTimer, 1000);
941
+
942
+ // Build & render cards
943
+ createCards();
944
+ renderBoard();
945
+ }
946
+
947
+ // =====================================================================
948
+ // SHUFFLE (rearranges only unmatched cards)
949
+ // =====================================================================
950
+ function shuffleCards() {
951
+ if (matchedPairs === TERMS_PER_LEVEL || gameOver) return;
952
+
953
+ // Drop any in-progress selection
954
+ if (selectedTerm) {
955
+ selectedTerm.classList.remove('flipped', 'selected');
956
+ selectedTerm = null;
957
+ }
958
+ if (selectedDefinition) {
959
+ selectedDefinition.classList.remove('flipped', 'selected');
960
+ selectedDefinition = null;
961
+ }
962
+
963
+ // Collect cards that are still in play
964
+ const unmatchedTerms = termCards.filter(c => {
965
+ const el = document.querySelector(`[data-id="${c.id}"]`);
966
+ return el && !el.classList.contains('matched') && !el.classList.contains('removed');
967
+ });
968
+ const unmatchedDefs = definitionCards.filter(c => {
969
+ const el = document.querySelector(`[data-id="${c.id}"]`);
970
+ return el && !el.classList.contains('matched') && !el.classList.contains('removed');
971
+ });
972
+
973
+ const shuffledTerms = shuffle(unmatchedTerms);
974
+ const shuffledDefs = shuffle(unmatchedDefs);
975
+
976
+ let ti = 0, di = 0;
977
+ termCards = termCards.map(c => {
978
+ const el = document.querySelector(`[data-id="${c.id}"]`);
979
+ if (el && (el.classList.contains('matched') || el.classList.contains('removed'))) return c;
980
+ return shuffledTerms[ti++] || c;
981
+ });
982
+ definitionCards = definitionCards.map(c => {
983
+ const el = document.querySelector(`[data-id="${c.id}"]`);
984
+ if (el && (el.classList.contains('matched') || el.classList.contains('removed'))) return c;
985
+ return shuffledDefs[di++] || c;
986
+ });
987
+
988
+ renderBoard();
989
+ }
990
+
991
+ // =====================================================================
992
+ // HINT (briefly reveals the first unmatched pair)
993
+ // =====================================================================
994
+ function showHint() {
995
+ if (matchedPairs === TERMS_PER_LEVEL || !canSelect || gameOver) return;
996
+
997
+ for (let i = 0; i < TERMS_PER_LEVEL; i++) {
998
+ const termEl = document.querySelector(`[data-id="term-${i}"]`);
999
+ const defEl = document.querySelector(`[data-id="def-${i}"]`);
1000
+ if (termEl && !termEl.classList.contains('matched') && !termEl.classList.contains('removed')) {
1001
+ termEl.classList.add('flipped');
1002
+ defEl.classList.add('flipped');
1003
+ setTimeout(() => {
1004
+ if (!termEl.classList.contains('matched')) termEl.classList.remove('flipped');
1005
+ if (!defEl.classList.contains('matched')) defEl.classList.remove('flipped');
1006
+ }, 1500);
1007
+ break;
1008
+ }
1009
+ }
1010
+ }
1011
+
1012
+ // =====================================================================
1013
+ // BOOT
1014
+ // =====================================================================
1015
+ startNewGame();
1016
+ </script>
1017
  </body>
1018
  </html>