diff --git a/dita-demo/oxygen-webhelp/app/options/properties.js b/dita-demo/oxygen-webhelp/app/options/properties.js
index 57ac161e..323b0e1d 100644
--- a/dita-demo/oxygen-webhelp/app/options/properties.js
+++ b/dita-demo/oxygen-webhelp/app/options/properties.js
@@ -2,7 +2,7 @@ var properties =
{ "default.language" : "en",
"use.stemming" : "false",
"webhelp.enable.scroll.to.search.term" : "no",
- "webhelp.enable.search.autocomplete" : "true",
+ "webhelp.enable.search.autocomplete" : "no",
"webhelp.enable.search.kuromoji.js" : "true",
"webhelp.enable.sticky.header" : "yes",
"webhelp.enable.sticky.publication.toc" : "yes",
diff --git a/dita-demo/oxygen-webhelp/app/search/index/htmlFileInfoList.js b/dita-demo/oxygen-webhelp/app/search/index/htmlFileInfoList.js
deleted file mode 100644
index cd486a28..00000000
--- a/dita-demo/oxygen-webhelp/app/search/index/htmlFileInfoList.js
+++ /dev/null
@@ -1 +0,0 @@
-var htmlFileInfoList = ["Introduction/background.html@@@Background@@@Welcome and background details...","Introduction/feedback.html@@@Feedback@@@Welcome and background 2 etails...","Introduction/security.html@@@Security@@@Security Page for document set...","Introduction/seven-questions.html@@@7 Questions@@@Seven basic analysis questions...","Pages/SubjectDetail.html@@@Detailed description of some event@@@This long document has some combination data tables, images, animations, audio files, grams. It does not include an analysis worksheet or quiz content Gram time-start 0 time-end 40 freq-start 100...","Pages/analysis-contact.html@@@Contact Classification Analysis@@@Students analyze sonar contacts and fill in classification details. Note: Gray cells (with background-color) are read-only. White cells are editable. Your entries are auto-saved. Contact ID Bearing...","Pages/analysis-tactical.html@@@Tactical Decision Matrix@@@Students record their tactical analysis decisions based on a given scenario. Note: This analysis table is designed for recording tactical decisions. Fill in your recommendations and justifications...","Pages/gram-1.html@@@Gram Analysis 1@@@Analyze the spectrogram below and complete your analysis in the table. Gram time-start 0 time-end 40 freq-start 100 freq-end 300 Analysis Complete your analysis based on the spectrogram above: What is...","Pages/gram-2.html@@@Gram Analysis 2 - Spirit of Whale Island@@@Note: this is Gram 2 - LOFAR 1. Analyze the LOFAR gram and complete your assessment. Gram time-start 0 time-end 40 freq-start 100 freq-end 300 Analysis Complete your LOFAR analysis based on the...","Pages/gram-3.html@@@Gram Analysis 3@@@Note: this page contains two grams Advanced spectrogram analysis exercise. Analyze multiple contacts and environmental conditions. Gram time-start 0 time-end 40 freq-start 100 freq-end 300 time-start...","Pages/gramless-1.html@@@Gramless Analysis 1@@@Some background to this set of questions. Consider environmental factors and operational constraints when answering. Question Answer Detail Have you considered depth effects on sound propagation? 1...","Pages/gramless-2.html@@@Gramless Analysis 2@@@Some background to this set of questions. Analyze operational parameters and system performance. Question Answer Detail What is the optimal operating frequency (in kHz) for this scenario? 5 1 Which...","Pages/quiz-mcq.html@@@MCQ Questions Only@@@This quiz contains only multiple-choice questions with 1-indexed answers. Question Answer Options What does SONAR stand for? 1 Sound Navigation And Ranging Sonic Navigation And Recording Sound...","Pages/quiz-mixed.html@@@Mixed Quiz Questions@@@This quiz demonstrates a mix of multiple-choice questions (with ordered lists) and numeric questions (with tolerance values). Question Answer Detail What is the primary function of a sonar system? 3...","Pages/quiz-numeric.html@@@Numeric Questions Only@@@This quiz contains only numeric questions with tolerance values (\u00B1). Question Correct Value Tolerance (\u00B1) What is the typical beam width (in degrees) for a narrow-beam sonar? 15 2 What is the maximum...","cshelp.html@@@Context Sensitive Help@@@...","page-index.html@@@Assessment@@@CSA Scheme contents...","welcome.html@@@Cover Page 1@@@Cover Page for document set..."];
diff --git a/dita-demo/oxygen-webhelp/app/search/index/index-1.js b/dita-demo/oxygen-webhelp/app/search/index/index-1.js
deleted file mode 100644
index 94a363b7..00000000
--- a/dita-demo/oxygen-webhelp/app/search/index/index-1.js
+++ /dev/null
@@ -1,2 +0,0 @@
-// Auto generated index for searching.
-var index1 = {"0.5":"5*0*26,13*0*1n,14*0*2f","001":"5*1*17$2e","002":"5*1*1a$2g","003":"5*1*1d$2i","004":"5*1*1g$2k","045":"5*0*18","1-indexed":"12*10*-1$f","10":"6*0*26,14*1*1i$1t","100":"4*10*-1$17,7*10*-1$o,8*10*-1$10,9*11*-1$v$1e","120":"5*0*1b","15":"10*0*39,14*10*-1$s","1500":"13*0*2e,14*0*23","20":"13*0*34","2019":"2*0*g,17*0*i","22":"7*0*1v,8*0*29,9*0*2q","270":"5*1*1e$2q","3.2":"2*0*d,17*0*f","300":"4*0*1b,7*10*-1$s,8*10*-1$14,9*11*-1$13$1i","315":"5*0*1h","40":"4*10*-1$13,7*10*-1$k,8*10*-1$s,9*11*-1$r$1a","45":"6*0*25","50":"10*0*38,13*0*2f,14*1*1s$24","500":"11*0*1o,14*0*18","5000":"11*0*1n,14*0*17","above":"5*0*1n,6*0*1f,7*10*-1$13,8*0*1c,9*0*1q","action":"6*0*u,8*0*1q","active":"11*0*u,12*0*2f,13*0*1v","adjust":"11*0*1r","adjustment":"11*0*2g","advanced":"9*10*-1$b","affect":"10*0*2o","affecting":"10*0*10","affects":"10*1*1m$2g,12*0*1s","air":"10*0*25","ambient":"11*0*1u","amplifier":"13*0*2q","amplify":"12*0*2u","analysis":"0*0*n,3*10*-1$6,4*10*-1$o,5*31*2$7$1m,6*21*-1$-1$b$h$1e,7*70*-1$-1$-1$1$5$b$t$10$1k$29,8*61*-1$-1$1$8$15$19$1l$1u$2j,9*52*-1$1$5$d$1j$1n$2f$34,10*30*1$5,11*30*1$5,16*5*c$e$g$i$n$13","analyze":"5*10*-1$9,7*10*-1$6,8*10*-1$f,9*10*-1$f,11*10*-1$a","animations":"4*10*-1$i","answer":"5*1*1o$1s,6*1*1g$1j,7*0*1p,8*0*23,9*0*2k,10*10*-1$i,11*10*-1$g,12*10*-1$j,13*10*-1$m","answering":"7*0*1l,8*0*1v,9*0*2g,10*10*-1$g","answers":"12*10*-1$h","any":"6*1*11$14","appropriate":"11*0*s","array":"10*0*1i,13*0*32","assessment":"6*0*s,7*10*1h$2a,8*20*-1$k$1r$2k,9*10*26$2c$35,16*30*0$3","assign":"6*0*1q","atmospheric":"12*0*24","audio":"4*10*-1$j","auto":"5*10*-1$u","auto-saved":"5*10*-1$t","availble":"0*0*i","background":"0*40*-1$0$3$5,1*11*-1$5$i,5*10*-1$j,10*10*-1$7,11*11*-1$7$24,12*0*3a","background-color":"5*10*-1$i","based":"5*0*1k,6*11*-1$d$1b,7*10*-1$11,8*10*-1$1a,9*0*1o","basic":"3*10*-1$5","beam":"10*0*1n,14*20*-1$-1$l$q","bearing":"5*11*-1$12$2m,8*0*1g","below":"7*10*-1$8","blah":"0*1*c$d","british":"12*0*1i","c-001":"5*1*16$2d","c-002":"5*1*19$2f","c-003":"5*1*1c$2h","c-004":"5*1*1f$2j","calculator":"0*0*l","causes":"13*0*36","cells":"5*20*-1$-1$h$p","change":"6*2*10$13$23","changes":"13*0*3b","characteristics":"7*0*1b","choice":"12*10*-1$d,13*10*-1$d","classification":"5*41*-1$1$6$d$13,8*0*1f,9*1*20$23,16*0*12","classified":"5*0*2o","classify":"5*0*23","color":"5*10*-1$k","combination":"4*10*-1$e","commercial":"0*0*1,1*0*1,2*0*1,3*0*1,4*0*4,5*0*3,6*0*3,7*0*2,8*0*5,9*0*2,10*0*2,11*0*2,12*0*3,13*0*3,14*0*3,16*0*1,17*0*2","communicate":"13*0*10","complete":"7*20*-1$-1$9$u,8*20*-1$-1$i$16,9*0*1k","component":"13*0*2h","composition":"13*0*3k","comprehensive":"9*0*1m","conditions":"9*10*-1$j,10*0*2d,11*0*2a","confidence":"0*0*2,1*0*2,2*0*2,3*0*2,4*0*5,5*2*4$14$2b,6*0*4,7*1*3$1f,8*0*6,9*0*3,10*0*3,11*0*3,12*0*4,13*0*4,14*0*4,16*0*2,17*0*3","consider":"10*10*-1$a","considered":"7*0*1e,10*12*-1$m$1f$29","constant":"11*0*28","constraints":"10*10*-1$e","contact":"5*42*-1$0$5$10$28$2n,8*0*1e,9*1*1v$22,16*0*11","contacts":"5*11*-1$b$20,9*11*-1$h$1t","contains":"9*10*-1$8,12*10*-1$9,14*10*-1$9","content":"4*10*-1$r","contents":"16*10*-1$6","context":"15*20*0","convert":"12*0*31","converts":"13*0*2i","core":"0*0*m","correct":"14*10*-1$g","course":"6*1*v$22","cover":"1*0*f,17*42*-1$0$4$6$a$c","coverage":"10*0*1p","creates":"10*0*t","critical":"6*0*1u","csa":"0*0*7,16*10*-1$4","currents":"13*0*3i","data":"4*10*-1$f","date":"2*0*e,17*0*g","day":"12*0*27","decision":"6*30*1$6,16*0*15","decisions":"6*20*-1$-1$c$m","degrees":"6*0*24,8*0*1i,14*10*-1$n","demonstrates":"13*10*-1$9","depth":"10*14*-1$n$s$13$1a$35,13*0*1b,14*0*1k","depths":"10*0*19","description":"4*30*1$7,16*0*8","designed":"6*10*-1$j","detail":"5*0*1t,6*0*1k,10*10*-1$j,11*10*-1$h,13*10*-1$n","detailed":"4*30*0$6,16*0*7","details":"0*10*-1$6,2*1*9$b,5*10*-1$e","detect":"12*0*2d,13*1*13$1t","detection":"11*0*1k,13*0*2t,14*0*v","developed":"12*0*1a","did":"5*0*21,6*0*1o","display":"12*0*36,13*0*2p","distinct":"9*0*1s","document":"2*10*-1$6,4*10*-1$b,17*10*-1$8","does":"4*10*-1$m,12*10*-1$m,13*0*1g,14*1*1m$28","doesn":"10*1*1s$2n","doesn't":"10*1*1r$2m","dominant":"7*0*15","doppler":"9*0*27,11*0*1e,12*0*2n,13*0*27","during":"10*0*2s,11*0*2c"};
diff --git a/dita-demo/oxygen-webhelp/app/search/index/index-2.js b/dita-demo/oxygen-webhelp/app/search/index/index-2.js
deleted file mode 100644
index ce2158f2..00000000
--- a/dita-demo/oxygen-webhelp/app/search/index/index-2.js
+++ /dev/null
@@ -1,2 +0,0 @@
-// Auto generated index for searching.
-var index2 = {"editable":"5*10*-1$q","effect":"13*0*38","effects":"10*10*-1$o","electrical":"12*1*2v$32,13*0*2j","email":"1*0*8","end":"4*11*-1$12$1a,7*20*-1$-1$j$r,8*20*-1$-1$r$13,9*22*-1$-1$q$12$19$1h","energy":"12*0*33","entries":"5*10*-1$s","environment":"11*0*t","environmental":"7*0*1c,9*11*-1$i$24,10*10*-1$b,12*0*1p","estimate":"8*0*1h","etails":"1*10*-1$6","etc":"1*0*a","event":"4*30*3$9,16*0*a","exercise":"9*10*-1$e","expected":"11*0*1i","extreme":"10*0*18","factor":"12*0*1q","factors":"7*0*1d,10*10*-1$c","features":"8*0*1n","feedback":"1*30*0$3","files":"4*10*-1$k","fill":"5*10*-1$c,6*10*-1$n","filter":"12*0*39","first":"12*0*19","following":"0*0*h,5*0*1p,6*0*1h,7*0*1m,8*0*20,9*0*2h","force":"12*0*18","french":"12*0*1l","freq":"4*11*-1$15$19,7*20*-1$-1$m$q,8*20*-1$-1$u$12,9*22*-1$-1$t$11$1c$1g","freq-end":"4*0*18,7*10*-1$p,8*10*-1$11,9*11*-1$10$1f","freq-start":"4*10*-1$14,7*10*-1$l,8*10*-1$t,9*11*-1$s$1b","frequency":"7*0*17,8*0*1k,11*11*-1$l$1b,13*0*1e,14*0*26","function":"12*0*2r,13*10*-1$q","geometry":"10*0*1o","german":"12*0*1g","given":"6*10*-1$e","going":"0*0*9","gradients":"10*0*v,13*0*3e","gram":"4*18*-1$s,7*49*-1$0$4$d$28,8*69*-1$-1$-1$0$7$d$h$l$2i,9*49*-1$0$4$k$33,16*2*f$h$m","gramless":"10*30*0$4,11*30*0$4,16*1*b$d","grams":"4*10*-1$l,9*10*-1$a","gray":"5*10*-1$g","has":"4*10*-1$c,5*0*29,10*0*14","have":"0*0*g,10*12*-1$k$1d$27","height":"10*3*1g$1l$1q$22","help":"15*20*2","high":"6*0*1t,11*0*1a","high-frequency":"11*0*19","highest":"5*0*2a","how":"5*0*1u,7*0*20,8*0*2a,9*0*2r","hull":"13*0*1j,14*1*14$2b","hull-mounted":"13*0*1i,14*1*13$2a","hz":"7*0*19","id":"5*10*-1$11","identified":"8*0*1o","images":"4*10*-1$h","imaging":"11*0*1c","impact":"10*0*16","include":"4*10*-1$n","increase":"11*0*21","indexed":"12*10*-1$g","information":"1*5*e,7*5*27,8*5*2h,9*5*32","introduce":"0*0*a","irrelevant":"10*0*1b","island":"8*30*4$b,16*0*l","justification":"6*0*18","justifications":"6*10*-1$q","keep":"11*0*26","khz":"11*10*-1$m,13*0*1f,14*0*27","kriegsmarine":"12*0*1h","layers":"13*0*3g","length":"11*0*13,14*0*1b","level":"5*0*2c,6*0*1n,7*1*1g$24,8*0*2e,9*0*2v","levels":"11*0*1t","line":"8*0*1j","listen":"11*0*16","listen-only":"11*0*15","lists":"13*10*-1$g","lofar":"8*30*-1$-1$-1$e$g$18","long":"4*10*-1$a","low":"6*0*1r","m/s":"13*0*2d,14*0*22","many":"5*0*1v,7*0*21,8*0*2b,9*0*2s","marine":"12*0*1m","maritime":"12*0*17","matrix":"6*30*2$7,16*0*16","matter":"10*0*1t","matters":"10*0*24","maximum":"7*0*1s,8*0*26,9*0*2n,11*0*1j,14*10*-1$u","mcq":"12*30*0$5,16*0*o","measure":"13*0*19","medium":"6*0*1s,11*0*11,14*0*1f","medium-range":"14*0*1e","merchant":"5*0*24","meters":"10*0*37,11*0*1m,14*1*11$1l","miles":"13*0*30","milliseconds":"14*0*1c","minimal":"10*0*15","mix":"13*10*-1$a","mixed":"13*30*0$5,16*0*u","mode":"6*0*16,11*3*q$10$18$1d","moon":"12*0*22","most":"11*0*r,12*0*1r","mounted":"13*0*1k,14*1*15$2c","multiple":"9*10*-1$g,12*10*-1$c,13*10*-1$c","multiple-choice":"12*10*-1$b,13*10*-1$b","name":"1*0*7","narrow":"14*10*-1$p","narrow-beam":"14*10*-1$o","narrowband":"8*0*1m","nationale":"12*0*1n","nautical":"13*0*2v","navigate":"13*0*t","navigation":"10*0*31,12*21*-1$-1$q$t$14","navy":"12*1*1f$1k","next":"0*0*p,1*0*b,2*0*h,17*0*j","night":"11*0*2d","noise":"9*0*25,10*0*2i,11*1*1v$25,12*0*3b","note":"5*10*-1$f,6*10*-1$g,8*10*-1$c,9*10*-1$6","notes":"5*0*15","nov":"2*0*f,17*0*h","number":"9*0*1r","numeric":"13*10*-1$h,14*40*-1$0$5$b,16*0*r","object":"12*0*13","objects":"12*0*2e,13*1*15$1u","observation":"12*0*10","observations":"9*0*29","observed":"7*0*25,8*0*2f,9*0*30","occur":"14*0*1p","only":"5*10*-1$n,10*4*17$1v$23$2r$2v,11*1*17$2b,12*40*-1$2$7$a,13*0*1c,14*40*-1$2$7$a,16*1*q$t","operate":"13*0*1m,14*0*2e","operating":"11*10*-1$k","operational":"10*10*-1$d,11*10*-1$b","operations":"10*1*26$2q,11*0*2e"};
diff --git a/dita-demo/oxygen-webhelp/app/search/index/index-3.js b/dita-demo/oxygen-webhelp/app/search/index/index-3.js
deleted file mode 100644
index 6da85a31..00000000
--- a/dita-demo/oxygen-webhelp/app/search/index/index-3.js
+++ /dev/null
@@ -1,2 +0,0 @@
-// Auto generated index for searching.
-var index3 = {"optimal":"11*10*-1$j","options":"7*0*1q,8*0*24,9*0*2l,12*10*-1$k","ordered":"13*10*-1$f","other":"0*0*j,13*0*11","overcome":"11*0*23","page":"0*0*q,1*1*c$g,2*11*-1$5$i,9*10*-1$7,17*43*-1$1$5$7$b$d$k","parameters":"11*10*-1$c","passive":"11*0*14,12*0*2h,13*0*21","paths":"10*0*2k","peak":"7*0*23,8*0*2d,9*0*2u","performance":"11*10*-1$e,12*0*1u","permitted":"11*0*2h","phase":"12*0*23","power":"11*3*1s$22$27$2f","practical":"12*0*1b","pressure":"10*0*u,12*0*25","primary":"8*0*1d,9*0*1u,12*0*2q,13*10*-1$p","propagation":"10*11*-1$q$2j,13*0*3a","pub":"0*0*k","pulse":"11*0*12,14*0*1a","question":"5*0*1r,6*0*1i,7*0*1o,8*0*22,9*0*2j,10*10*-1$h,11*10*-1$f,12*10*-1$i,13*10*-1$l,14*10*-1$f","questions":"3*40*-1$0$3$7,5*10*1j$1q,6*9*1a,10*10*-1$9,11*10*-1$9,12*40*-1$1$6$e,13*50*-1$-1$2$7$e$i,14*40*-1$1$6$c,16*2*p$s$10","quetsions":"7*0*1n,8*0*21,9*0*2i","quiz":"4*10*-1$q,12*10*-1$8,13*40*-1$1$6$8,14*10*-1$8,16*0*v","range":"7*0*18,11*0*1l,13*0*2u,14*1*10$1g","ranging":"12*10*-1$r","reached":"7*0*1u,8*0*28,9*0*2p","read":"5*10*-1$m","read-only":"5*10*-1$l","reason":"3*0*a","receiver":"13*0*2n","recognition":"12*0*11","recommendation":"9*0*2b","recommendations":"6*10*-1$p","recommended":"6*1*t$21,8*0*1p","record":"6*10*-1$9","recording":"6*10*-1$k,12*10*-1$u","reflections":"12*0*2c,13*0*1s","regardless":"11*0*29","related":"1*5*d,7*5*26,8*5*2g,9*5*31","relevant":"10*0*30","reporting":"12*0*15","returns":"12*0*38","royal":"12*0*1j","salinity":"12*0*21,13*0*3c","saved":"5*10*-1$v","scan":"12*0*2l,13*0*25","scenario":"6*10*-1$f,11*10*-1$n","scheme":"0*0*8,16*10*-1$5","sea":"10*0*2b","seafloor":"13*0*3j","search":"11*0*v","seawater":"13*0*2c,14*0*21","secondary":"9*0*21","seconds":"7*0*22,8*0*2c,9*0*2t","security":"1*0*h,2*42*-1$0$3$4$8$a","selection":"6*0*17","sensitive":"15*20*1","set":"2*10*-1$7,10*10*-1$8,11*10*-1$8,17*10*-1$9","seven":"3*10*-1$4","shift":"9*0*28","should":"0*0*f,11*0*1p","side":"12*0*2k,13*0*24","side-scan":"12*0*2j,13*0*23","signal":"7*0*1a","signals":"12*0*30,13*0*2k","skills":"0*0*o","some":"3*0*9,4*40*-1$2$8$d,10*10*-1$6,11*10*-1$6,16*0*9","sonar":"5*10*-1$a,6*0*15,10*1*1c$1u,11*0*p,12*19*-1$n$1c$1t$2a$2g$2i$2m$2o$2s$37,13*18*-1$r$1l$1q$20$22$26$28$33$39,14*13*-1$r$16$1h$2d","sonic":"12*10*-1$s","sound":"10*11*-1$p$12,12*21*-1$-1$p$v$34,13*2*17$2b$2l,14*0*20","source":"7*0*16","spectrogram":"7*20*-1$-1$7$12,8*0*1b,9*11*-1$c$1p","speed":"6*0*12,10*0*11,13*0*2a,14*0*1v","spirit":"8*30*2$9,16*0*j","stand":"12*10*-1$o","standard":"14*0*12","stars":"13*0*v","start":"4*20*-1$-1$v$16,7*20*-1$-1$g$n,8*20*-1$-1$o$v,9*31*-1$-1$-1$n$u$16$1d","state":"10*0*2c","states":"12*0*1e","storms":"10*0*2t","students":"5*10*-1$8,6*10*-1$8","submarine":"5*0*2p","subsea":"12*0*12","summarise":"7*0*1i,8*0*1s,9*0*2d","surface":"10*1*20$2h","system":"11*10*-1$d,13*10*-1$s","table":"6*10*-1$i,7*10*-1$c","tables":"4*10*-1$g","tactical":"6*51*-1$-1$0$5$a$l$1d,9*0*2a,16*0*14","tel":"1*0*9","temperate":"14*0*1q","temperature":"7*0*1t,8*0*27,9*0*2o,12*0*20,13*0*3d","thermocline":"10*0*34,13*0*37,14*0*1n","threat":"6*1*r$1m","time":"4*20*-1$-1$u$11,7*20*-1$-1$f$i,8*20*-1$-1$n$q,9*31*-1$-1$-1$m$p$15$18,12*0*26","time-end":"4*10*-1$10,7*10*-1$h,8*10*-1$p,9*11*-1$o$17","time-start":"4*10*-1$t,7*10*-1$e,8*10*-1$m,9*20*-1$-1$l$14","tolerance":"13*10*-1$j,14*20*-1$-1$d$i","towed":"13*0*31","tracking":"11*0*1g","transducer":"10*1*1h$1k,12*0*2t,13*0*2o","two":"9*10*-1$9","type":"12*0*29,13*0*1p","typical":"10*0*33,13*1*1h$2s,14*12*-1$k$1d$29","typically":"14*0*1o","underwater":"10*0*2p,13*1*14$3h","united":"12*0*1d","uses":"12*0*2b,13*0*1r","using":"13*1*u$16","value":"14*10*-1$h","values":"13*10*-1$k,14*10*-1$e","velocity":"11*0*1f","verification":"5*9*1i,6*9*19","version":"2*0*c,17*0*e","vessels":"5*0*25,10*0*21,13*0*12","water":"12*0*1v,13*1*1a$3f","waters":"10*0*36,14*0*1r","waves":"12*0*35,13*1*18$2m","weather":"10*3*2a$2f$2l$2u","welcome":"0*10*-1$4,1*10*-1$4","whale":"8*30*3$a,16*0*k","what":"5*0*2l,6*1*1l$1v,7*11*-1$14$1r,8*0*25,9*0*2m,10*0*32,11*11*-1$i$1h,12*12*-1$l$1o$2p,13*14*-1$o$1d$29$2r$35,14*24*-1$-1$j$t$19$1j$1u$25","when":"10*10*-1$f","which":"5*0*27,11*10*-1$o,12*1*16$28,13*1*1o$2g","white":"5*10*-1$o","why":"3*0*8","width":"14*10*-1$m","worksheet":"4*10*-1$p","yes":"10*2*r$1j$2e,11*0*20","you":"0*1*b$e,5*0*22,6*0*1p,10*12*-1$l$1e$28,11*0*1q","your":"5*11*-1$r$1l,6*12*-1$o$1c$20,7*21*-1$-1$a$v$1j,8*21*-1$-1$j$17$1t,9*1*1l$2e"};
diff --git a/dita-demo/oxygen-webhelp/app/search/index/index.js b/dita-demo/oxygen-webhelp/app/search/index/index.js
deleted file mode 100644
index 60c367d5..00000000
--- a/dita-demo/oxygen-webhelp/app/search/index/index.js
+++ /dev/null
@@ -1,22 +0,0 @@
-var words = {};
-Object.assign(words, index1, index2, index3);
-
-var index = {
- /**
- * The object with indexed words.
- *
- * {"word" : "topicID*score, topicID*score"}
- */
- w : words,
- /**
- * Auto generated list of analyzer stop words that must be ignored by search.
- */
- stopWords : stopwords,
-
- /**
- * File info list.
- */
- fil : htmlFileInfoList,
-
- link2parent : linkToParent
-}
\ No newline at end of file
diff --git a/dita-demo/oxygen-webhelp/app/search/index/keywords.js b/dita-demo/oxygen-webhelp/app/search/index/keywords.js
deleted file mode 100644
index 91d8ade0..00000000
--- a/dita-demo/oxygen-webhelp/app/search/index/keywords.js
+++ /dev/null
@@ -1,24 +0,0 @@
-var keywords=[{w:"Background",p:["p0"]},{w:"Feedback",p:["p1"]},{w:"Security",p:["p2"]},{w:"7",p:["p3"]},{w:"Questions",p:["p3","p12","p13","p14"]},{w:"Detailed",p:["p4"]},{w:"description",p:["p4"]},{w:"of",p:["p4","p8"]},{w:"some",p:["p4"]},{w:"event",p:["p4"]},{w:"Contact",p:["p5"]},{w:"Classification",p:["p5"]},{w:"Analysis",p:["p5","p7","p8","p9","p10","p11"]},{w:"Tactical",p:["p6"]},{w:"Decision",p:["p6"]},{w:"Matrix",p:["p6"]},{w:"Gram",p:["p7","p8","p9"]},{w:"1",p:["p7","p10","p17"]},{w:"2",p:["p8","p11"]},{w:"-",p:["p8"]},{w:"Spirit",p:["p8"]},{w:"Whale",p:["p8"]},{w:"Island",p:["p8"]},{w:"3",p:["p9"]},{w:"Gramless",p:["p10","p11"]},{w:"MCQ",p:["p12"]},{w:"Only",p:["p12","p14"]},{w:"Mixed",p:["p13"]},{w:"Quiz",p:["p13"]},{w:"Numeric",p:["p14"]},{w:"Context",p:["p15"]},{w:"Sensitive",p:["p15"]},{w:"Help",p:["p15"]},{w:"Assessment",p:["p16"]},{w:"Cover",p:["p17"]},{w:"Page",p:["p17"]}];
-var ph={};
-ph["p0"]=[0];
-ph["p1"]=[1];
-ph["p2"]=[2];
-ph["p3"]=[3, 4];
-ph["p4"]=[5, 6, 7, 8, 9];
-ph["p5"]=[10, 11, 12];
-ph["p6"]=[13, 14, 15];
-ph["p7"]=[16, 12, 17];
-ph["p8"]=[16, 12, 18, 19, 20, 7, 21, 22];
-ph["p9"]=[16, 12, 23];
-ph["p10"]=[24, 12, 17];
-ph["p12"]=[25, 4, 26];
-ph["p11"]=[24, 12, 18];
-ph["p14"]=[29, 4, 26];
-ph["p13"]=[27, 28, 4];
-ph["p16"]=[33];
-ph["p15"]=[30, 31, 32];
-ph["p17"]=[34, 35, 17];
-var keywordsInfo = {
- keywords: keywords,
- ph: ph
-}
diff --git a/dita-demo/oxygen-webhelp/app/search/index/link-to-parent.js b/dita-demo/oxygen-webhelp/app/search/index/link-to-parent.js
deleted file mode 100644
index 35d47459..00000000
--- a/dita-demo/oxygen-webhelp/app/search/index/link-to-parent.js
+++ /dev/null
@@ -1,2 +0,0 @@
-/*Maps current topic to its parent: "topicIndex:parentIndex". -1 represents the map.*/
-var linkToParent = {16:-1,3:-1,1:-1};
diff --git a/dita-demo/oxygen-webhelp/app/search/index/stopwords.js b/dita-demo/oxygen-webhelp/app/search/index/stopwords.js
deleted file mode 100644
index d5d0c19e..00000000
--- a/dita-demo/oxygen-webhelp/app/search/index/stopwords.js
+++ /dev/null
@@ -1,2 +0,0 @@
-// Auto generated list of analyzer stop words that must be ignored by search.
-var stopwords = ["but","be","with","such","then","for","no","will","not","are","and","their","if","this","on","into","a","or","there","in","that","they","was","is","it","an","the","as","at","these","by","to","of"];
diff --git a/dita-demo/oxygen-webhelp/template/resources/sonar-quiz.iife.js b/dita-demo/oxygen-webhelp/template/resources/sonar-quiz.iife.js
index 44bbbf07..448dcc27 100644
--- a/dita-demo/oxygen-webhelp/template/resources/sonar-quiz.iife.js
+++ b/dita-demo/oxygen-webhelp/template/resources/sonar-quiz.iife.js
@@ -1,41 +1,42 @@
-var SonarQuiz=function(t){"use strict";function n(t){if(t.length<2)return"**";if(2===t.length)return t;return t.slice(0,2)+"*".repeat(t.length-2)}function s(t){if(null===t||"object"!=typeof t)return t;const o={};for(const[r,a]of Object.entries(t))"name"!==r&&"passwordHash"!==r&&(o[r]="serviceId"!==r||"string"!=typeof a?"object"!=typeof a||null===a?a:s(a):n(a));return o}function o(t,n){}function r(t,n){if(n instanceof Error){const s={name:n.name,message:n.message};console.error(`[ERROR] ${t}`,s)}else void 0!==n?console.error(`[ERROR] ${t}`,s(n)):console.error(`[ERROR] ${t}`)}function a(t,n){void 0!==n?console.warn(`[WARN] ${t}`,s(n)):console.warn(`[WARN] ${t}`)}function c(t){const n=[],s=[];if(!t.classList.contains("qd-quiz"))return n.push('Table must have class "qd-quiz"'),{element:t,questions:s,errors:n};const o=Array.from(t.querySelectorAll("tbody tr"));return 0===o.length?(n.push("Quiz table has no data rows"),{element:t,questions:s,errors:n}):(o.forEach((t,o)=>{const r=Array.from(t.querySelectorAll("td"));if(3!==r.length)return void n.push(`Row ${o+1} has ${r.length} columns, expected 3 (Question | Answer | Detail)`);const a=r[0],c=r[1],d=r[2];if(!a||!c||!d)return;const l=a.textContent?.trim()||"";if(!l)return void n.push(`Row ${o+1} has empty question text`);const u=c.textContent?.trim()||"";if(!u)return void n.push(`Row ${o+1} has empty answer`);const h=d.querySelector("ol");if(h){const t=(p=h,Array.from(p.querySelectorAll("li")).map(t=>t.textContent?.trim()||"").filter(t=>t.length>0));if(0===t.length)return void n.push(`Row ${o+1} MCQ has no options in
`);s.push({text:l,kind:"mcq",correctAnswer:u,options:t})}else{const t=d.textContent?.trim()||"",r=parseFloat(t);if(isNaN(r))return void n.push(`Row ${o+1} appears to be numeric but has invalid tolerance: "${t}"`);s.push({text:l,kind:"numeric",correctAnswer:u,tolerance:r})}var p}),{element:t,questions:s,errors:n.length>0?n:void 0})}function d(t,n){if(!n||""===n.trim())return!1;const s=n.trim();if("mcq"===t.kind)return s===t.correctAnswer;{const n=parseFloat(s),o=parseFloat(t.correctAnswer);if(isNaN(n)||isNaN(o))return!1;const r=t.tolerance??0;return Math.abs(n-o)<=r}}const l=18e5,u={SESSION:"qd/session",CACHE:"qd/state",INSTRUCTOR:"qd/instructor",PIN_ATTEMPTS:"qd:pin-attempts"},h=3,p=3e4;class SessionService{createSession(t,n,s){const o=new Date,r=o.toISOString(),a={serviceId:t,name:n,release:s,loginTime:r,lastActivity:r,expiresAt:new Date(o.getTime()+l).toISOString(),instructorUnlocked:!1};return this.saveSession(a),this.emitEvent("qd:login",{serviceId:t,name:n,release:s,loginTime:r}),a}getSession(){try{const t=sessionStorage.getItem(u.SESSION);if(!t)return null;const n=JSON.parse(t);return n.serviceId&&n.release&&n.expiresAt?n:(a("Invalid session data, missing required fields"),null)}catch(t){return r("Failed to parse session data",t),null}}updateActivity(){const t=this.getSession();if(!t)return;const n=new Date;t.lastActivity=n.toISOString(),t.expiresAt=new Date(n.getTime()+l).toISOString(),this.saveSession(t)}isExpired(){const t=this.getSession();return!t||function(t,n=new Date){const s=new Date(t);return!!isNaN(s.getTime())||n>=s}(t.expiresAt)}clearSession(){const t=this.getSession();sessionStorage.removeItem(u.SESSION),sessionStorage.removeItem(u.CACHE),sessionStorage.removeItem(u.INSTRUCTOR),sessionStorage.removeItem("qd/instructor/showAnswers"),t&&(t.serviceId,this.emitEvent("qd:logout",{serviceId:t.serviceId,timestamp:(new Date).toISOString()}))}unlockInstructor(){const t=this.getSession();t&&(t.instructorUnlocked=!0,t.unlockTime=(new Date).toISOString(),this.saveSession(t),this.emitEvent("qd:instructor-unlock",{timestamp:t.unlockTime}))}lockInstructor(){const t=this.getSession();t&&(t.instructorUnlocked=!1,delete t.unlockTime,this.saveSession(t),this.emitEvent("qd:instructor-lock",{timestamp:(new Date).toISOString()}))}isInstructorUnlocked(){const t=this.getSession();return!0===t?.instructorUnlocked}getCache(){try{const t=sessionStorage.getItem(u.CACHE);return t?JSON.parse(t):null}catch(t){return r("Failed to parse cache data",t),null}}saveCache(t){try{sessionStorage.setItem(u.CACHE,JSON.stringify(t))}catch(n){r("Failed to save cache",n)}}clearCache(){sessionStorage.removeItem(u.CACHE)}saveSession(t){try{sessionStorage.setItem(u.SESSION,JSON.stringify(t))}catch(n){r("Failed to save session",n)}}emitEvent(t,n){try{const s=new CustomEvent(t,{detail:n,bubbles:!0});document.dispatchEvent(s)}catch(s){r(`Failed to emit event ${t}`,s)}}}function g(t,n){const s=n.answers.length,o=n.answers.filter(t=>""!==t.answer.trim()).length,r=n.answers.filter(t=>t.success).length;return{state:n.state,total:s,answered:o,correct:r,last:n.lastAttempted,answers:n.answers,analysis:n.analysis}}function m(t){return function(t,n="display"){if(null==t)return console.warn("Invalid date provided to formatTimestamp:",t),"Invalid Date";const s="string"==typeof t?new Date(t):t;return isNaN(s.getTime())?(console.warn("Invalid date provided to formatTimestamp:",t),"Invalid Date"):"csv"===n?function(t){return t.toISOString()}(s):function(t){return`${t.toLocaleDateString("en-US",{month:"short"})} ${t.getDate()} ${t.getHours().toString().padStart(2,"0")}:${t.getMinutes().toString().padStart(2,"0")}`}(s)}(t,"display")}class Debouncer{constructor(){this.timers=new Map}debounce(t,n,s=200){const o=this.timers.get(t);void 0!==o&&clearTimeout(o);const r=setTimeout(()=>{this.timers.delete(t),n()},s);this.timers.set(t,r)}cancel(t){const n=this.timers.get(t);return void 0!==n&&(clearTimeout(n),this.timers.delete(t),!0)}cancelAll(){let t=0;for(const n of this.timers.values())clearTimeout(n),t++;return this.timers.clear(),t}isPending(t){return this.timers.has(t)}getPendingCount(){return this.timers.size}}function f(t){const n=t.querySelector("tbody");return n?Array.from(n.querySelectorAll("tr")):[]}function b(t){return Array.from(t.cells)}function y(t){return t&&t.textContent?.trim()||""}function v(t,n,s){return document.createElement(t)}function w(t,...n){t.classList.add(...n)}function S(t,...n){t.classList.remove(...n)}function x(t,n,s){const o=new CustomEvent(t,{detail:n,bubbles:!0,composed:!0,cancelable:!1});return document.dispatchEvent(o)}function E(t,n,s,o){const r=new CustomEvent(n,{detail:s,bubbles:!0,composed:!0,cancelable:!1});return t.dispatchEvent(r)}function C(t){try{const n=sessionStorage.getItem(t);return n?JSON.parse(n):null}catch(n){return a(`Failed to parse JSON from sessionStorage key: ${t}`,n),null}}function $(t,n){try{const s=JSON.stringify(n);return sessionStorage.setItem(t,s),!0}catch(s){return a(`Failed to store JSON in sessionStorage key: ${t}`,s),!1}}function q(){const t=[];for(let n=0;nt.charCodeAt(0).toString()).join(""):""}function P(t){return(new TextEncoder).encode(t)}function _(t,n){if(0===n.length)return t;const s=new Uint8Array(t.length);for(let o=0;o{let s,o=!1;const c=()=>{s&&(clearTimeout(s),s=void 0)};s=window.setTimeout(()=>{if(o)return;o=!0,this.initPromise=null,a("IndexedDB open timed out after 5000ms - attempting recovery");const s=indexedDB.deleteDatabase(this.dbName);s.onsuccess=()=>{this.init().then(t).catch(n)},s.onerror=()=>{n(new StorageError(`Database "${this.dbName}" appears corrupted. Please clear site data in browser settings.`,"init"))},s.onblocked=()=>{n(new StorageError("Cannot recover database - close all other tabs with this site and reload.","init"))}},5e3);const d=indexedDB.open(this.dbName,3);d.onerror=()=>{o||(o=!0,c(),r(`IndexedDB open error: ${d.error?.message||"unknown"}`),this.initPromise=null,n(new StorageError("Failed to open database","init",d.error)))},d.onblocked=()=>{a("IndexedDB open blocked - close other tabs with this database")},d.onsuccess=()=>{if(!o){if(o=!0,c(),this.db=d.result,!this.db.objectStoreNames.contains(F)||!this.db.objectStoreNames.contains(B)||!this.db.objectStoreNames.contains(V)){a(`Database corrupted (missing stores). Found: [${Array.from(this.db.objectStoreNames).join(", ")}]`),this.db.close(),this.db=null;const s=indexedDB.deleteDatabase(this.dbName);return s.onsuccess=()=>{this.initPromise=null,this.init().then(t).catch(n)},void(s.onerror=()=>{this.initPromise=null,n(new StorageError("Failed to delete corrupted database","init",s.error))})}this.initPromise=null,t()}},d.onupgradeneeded=t=>{const n=t.target.result,s=t.target.transaction;s&&(s.onerror=()=>{r(`Upgrade transaction error: ${s.error?.message||"unknown"}`)},s.onabort=()=>{r(`Upgrade transaction aborted: ${s.error?.message||"unknown"}`)});try{if(!n.objectStoreNames.contains(F)){const t=n.createObjectStore(F,{keyPath:null});t.createIndex("by-release","release",{unique:!1}),t.createIndex("by-service-id","serviceId",{unique:!1})}if(!n.objectStoreNames.contains(B)){const t=n.createObjectStore(B,{keyPath:null});t.createIndex("by-original-key","originalKey",{unique:!1}),t.createIndex("by-timestamp","timestamp",{unique:!1})}if(!n.objectStoreNames.contains(V)){const t=n.createObjectStore(V,{keyPath:"eventId"});t.createIndex("by-service-id","serviceId",{unique:!1}),t.createIndex("by-reset-at","resetAt",{unique:!1})}}catch(o){throw r("Error during database upgrade",o),o}}}),this.initPromise)}ensureInitialized(){if(!this.db)throw new StorageNotInitializedError("ensureInitialized");return this.db}async getStudent(t,n){const s=this.ensureInitialized(),o=A(t,n);return new Promise((n,r)=>{try{const a=s.transaction(F,"readonly"),c=a.objectStore(F).get(o);c.onsuccess=()=>{const s=c.result;if(null!=s)try{const r=this.decodeStoredValue(s,t,o);n(r)}catch(a){r(a instanceof Error?a:new Error(String(a)))}else n(null)},c.onerror=()=>{r(new StorageError("Failed to get student record","getStudent",c.error))}}catch(a){r(new StorageError("Failed to get student record","getStudent",a))}})}decodeStoredValue(t,n,s){if(j(t))throw new StorageFormatError("Obfuscated data found with ENCRYPT_STORAGE disabled. Run migration to decrypt or re-enable encryption.","plain","obfuscated",s);return t}async saveStudent(t){const n=this.ensureInitialized(),s=A(t.release,t.serviceId),o=t;return new Promise((t,r)=>{try{const a=n.transaction(F,"readwrite"),c=a.objectStore(F).put(o,s);c.onsuccess=()=>{t()},c.onerror=()=>{"QuotaExceededError"===c.error?.name?r(new StorageQuotaError("saveStudent")):r(new StorageError("Failed to save student record","saveStudent",c.error))},a.onerror=()=>{r(new StorageError("Transaction failed while saving student","saveStudent",a.error))}}catch(a){r(new StorageError("Failed to save student record","saveStudent",a))}})}async getStudentsByRelease(t){const n=this.ensureInitialized();return new Promise((s,o)=>{try{const r=n.transaction(F,"readonly").objectStore(F),a=r.index("by-release").getAll(t);a.onsuccess=()=>{s(a.result||[])},a.onerror=()=>{o(new StorageError("Failed to get students by release","getStudentsByRelease",a.error))}}catch(r){o(new StorageError("Failed to get students by release","getStudentsByRelease",r))}})}async getStudentsByReleaseEncrypted(t){const n=this.ensureInitialized(),s=T(t);return new Promise((o,r)=>{try{const c=n.transaction(F,"readonly"),d=c.objectStore(F).openCursor(),l=[];d.onsuccess=()=>{const n=d.result;if(n){const o=n.value;if(j(o))try{const n=U(o,s);n.release===t&&l.push(n)}catch{a(`Skipping corrupted record at key: ${"string"==typeof n.key?n.key:JSON.stringify(n.key)}`)}n.continue()}else o(l)},d.onerror=()=>{r(new StorageError("Failed to get students by release","getStudentsByRelease",d.error))}}catch(c){r(new StorageError("Failed to get students by release","getStudentsByRelease",c))}})}async clearAll(){const t=this.ensureInitialized();return new Promise((n,s)=>{try{const o=t.transaction([F,B,V],"readwrite"),r=o.objectStore(F),a=o.objectStore(B),c=o.objectStore(V),d=r.clear(),l=a.clear(),u=c.clear();let h=!1,p=!1,g=!1;d.onsuccess=()=>{h=!0,p&&g&&n()},l.onsuccess=()=>{p=!0,h&&g&&n()},u.onsuccess=()=>{g=!0,h&&p&&n()},d.onerror=()=>{s(new StorageError("Failed to clear students","clearAll",d.error))},l.onerror=()=>{s(new StorageError("Failed to clear backups","clearAll",l.error))},u.onerror=()=>{s(new StorageError("Failed to clear audit log","clearAll",u.error))},o.onerror=()=>{s(new StorageError("Transaction failed during clearAll","clearAll",o.error))}}catch(o){s(new StorageError("Failed to clear all data","clearAll",o))}})}async backup(t){const n=this.ensureInitialized(),s=(new Date).toISOString(),o=`backup_${s}_${t.serviceId}`,r=A(t.release,t.serviceId),a={...t,originalKey:r,timestamp:s};return new Promise((t,s)=>{try{const r=n.transaction(B,"readwrite"),c=r.objectStore(B).put(a,o);c.onsuccess=()=>{t()},c.onerror=()=>{"QuotaExceededError"===c.error?.name?s(new StorageQuotaError("backup")):s(new StorageError("Failed to create backup","backup",c.error))},r.onerror=()=>{s(new StorageError("Transaction failed during backup","backup",r.error))}}catch(r){s(new StorageError("Failed to create backup","backup",r))}})}async saveAuditEvent(t){const n=this.ensureInitialized();return new Promise((s,o)=>{try{const r=n.transaction(V,"readwrite"),a=r.objectStore(V).add(t);a.onsuccess=()=>{s()},a.onerror=()=>{o(new StorageError("Failed to save audit event","saveAuditEvent",a.error))}}catch(r){o(new StorageError("Failed to save audit event","saveAuditEvent",r))}})}close(){this.db&&(this.db.close(),this.db=null,this.initPromise=null)}}let Q=null,K=null;function J(t){if(!t)throw new Error("FATAL: dbName is required for getStorageAdapter()");return Q&&K!==t&&(Q.close(),Q=null),Q||(Q=new IndexedDBStorageAdapter(t),K=t),Q}function W(t,n){return 0===n||function(t){return 0===t.length}(t)?"unstarted":function(t,n){if(t.length!==n)return!1;return t.every(t=>!0===t.success)}(t,n)?"complete":"incomplete"}class StorageService{constructor(t){if(!t)throw new Error("FATAL: dbName is required for StorageService");this.dbName=t,this.adapter=J(t)}async init(){try{await this.adapter.init(),this.dbName}catch(t){throw r("Failed to initialize storage service",t),t}}async loadStudentRecord(t){try{const n=await this.adapter.getStudent(t.release,t.serviceId);if(n)return t.serviceId,n;const s={schema:1,docId:t.release,release:t.release,serviceId:t.serviceId,name:t.name,attempted:0,correct:0,updated:(new Date).toISOString(),pages:{}};return t.serviceId,s}catch(n){a(`IndexedDB error, creating new record: ${n.message}`);return{schema:1,docId:t.release,release:t.release,serviceId:t.serviceId,name:t.name,attempted:0,correct:0,updated:(new Date).toISOString(),pages:{}}}}async saveStudentRecord(t){try{t.updated=(new Date).toISOString();const n=function(t){let n=0,s=0;for(const o in t){const r=t[o];if(r&&r.answers&&Array.isArray(r.answers)){const t=r.answers.filter(t=>""!==t.answer.trim());n+=t.length,s+=t.filter(t=>t.success).length}}return{attempted:n,correct:s}}(t.pages);t.attempted=n.attempted,t.correct=n.correct,await this.adapter.saveStudent(t),t.serviceId}catch(n){throw r("Failed to save student record",n),n}}updateRecordWithAnswer(t,n,s,o,r){const a=t.pages[n]||{answers:[],state:"unstarted"};for(;a.answers.length<=s;)a.answers.push({answer:"",success:!1,timestamp:(new Date).toISOString()});a.answers[s]=o;const c=(new Date).toISOString();return a.firstAttempted||(a.firstAttempted=c),a.lastAttempted=c,a.state=W(a.answers,r),{...t,pages:{...t.pages,[n]:a}}}buildCache(t){return function(t){const n={totals:{total:0,answered:0,correct:0},pages:{}};for(const[s,o]of Object.entries(t.pages)){const t=g(0,o);n.pages[s]=t,n.totals.total+=t.total,n.totals.answered+=t.answered,n.totals.correct+=t.correct}return n}(t)}async getStudentsByRelease(t){try{return await this.adapter.getStudentsByRelease(t)}catch(n){throw r("Failed to get students by release",n),n}}async clearAll(){try{await this.adapter.clearAll()}catch(t){throw r("Failed to clear all data",t),t}}async backup(t){try{await this.adapter.backup(t),t.serviceId}catch(n){a(`Failed to create backup for ${t.serviceId}`,n)}}}let Y=null,G=null;function Z(t){if(Y&&!t)return Y;if(Y&&t&&G!==t)return a(`Storage service already initialized with dbName="${G}", ignoring new dbName="${t}"`),Y;if(!Y){if(!t)throw new Error("FATAL: dbName is required for first getStorageService() call");Y=new StorageService(t),G=t}return Y}const X=new WeakMap;function tt(t,n){const s=X.get(t);let o;if(s){if(s.interactive||!n.interactive)return!0;o=s.parsed}else o=c(t),o.errors&&o.errors.length>0&&r("Quiz table has validation errors:",o.errors);const l={parsed:o,interactive:n.interactive,pageId:n.pageId};if(n.interactive){if(!n.pageId)return r("Interactive mode requires pageId option"),!1;n.pageId,l.debouncer=new Debouncer,l.inputs=[]}if(X.set(t,l),n.interactive){const n=function(t,n){const{parsed:s,pageId:o,debouncer:c}=n;if(!o||!c)return r("Interactive mode requires pageId and debouncer"),!1;(function(t){const n=t.querySelectorAll("thead th, thead td");n[1]&&S(n[1],"qd-hidden");const s=t.querySelectorAll("tbody tr");s.forEach(t=>{const n=t.querySelectorAll("td");n[1]&&S(n[1],"qd-hidden")})})(t),st(t);if(!C(u.SESSION))return r("No active session found"),!1;let l=C(u.CACHE);l?(l.totals.total,Object.keys(l.pages).length):l={totals:{total:0,answered:0,correct:0},pages:{}};const h=s.questions.length;l=function(t,n,s){const o=t.pages[n];if(o&&o.total>=s)return t;const r=s-(o?.total||0),a={state:o?.state||"unstarted",total:s,answered:o?.answered||0,correct:o?.correct||0,last:o?.last,answers:o?.answers,analysis:o?.analysis};return{totals:{total:t.totals.total+r,answered:t.totals.answered,correct:t.totals.correct},pages:{...t.pages,[n]:a}}}(l,o,h),$(u.CACHE,l);const p=l?.pages[o],g=p?.answers||[];g.length;const m=t.querySelector("tbody");if(!m)return r("Quiz table has no tbody element"),!1;const f=Array.from(m.querySelectorAll("tr")),b=[];s.questions.forEach((s,o)=>{const c=f[o];if(!c)return;const l=Array.from(c.querySelectorAll("td"));if(3!==l.length)return;const h=l[0],p=l[1];if(!h||!p)return;const m=g[o];m&&m.answer&&(m.answer,m.success);const y=function(t,n){const s=function(t,n){if("mcq"===t.kind){const s=(t.options||[]).map((t,n)=>({value:String(n+1),text:`${n+1}. ${t}`}));return{type:"select",className:"qd-quiz-input",placeholder:"Select an answer...",value:n?.answer||"",options:s}}return{type:"text",className:"qd-quiz-input",placeholder:"Enter value",value:n?.answer||""}}(t,n);if("select"===s.type){const t=v("select");t.className=s.className;const n=v("option");return n.value="",n.textContent=s.placeholder,n.disabled=!0,t.appendChild(n),s.options&&s.options.forEach(n=>{const s=v("option");s.value=n.value,s.textContent=n.text,t.appendChild(s)}),t.value=s.value,t}{const t=v("input");return t.type=s.type,t.className=s.className,t.placeholder=s.placeholder,t.value=s.value,t}}(s,m);b.push(y),S(p,"qd-answer-correct","qd-answer-incorrect"),p.textContent="",p.appendChild(y),m&&et(p,m.success);const w="SELECT"===y.tagName?"change":"input";y.addEventListener(w,()=>{!function(t,n,s,o){const{debouncer:c,pageId:l,parsed:h}=n;if(!c||!l)return;const p=h.questions[s];if(!p)return;c.debounce(`save-answer-${s}`,()=>{!async function(t,n,s,o){const{pageId:c,parsed:l,inputs:h}=n;if(!c||!h)return;const p=l.questions[s];if(!p)return;const g=C(u.SESSION);if(!g)return void r("No active session found");const m=d(p,o),f={answer:o.trim(),success:m,timestamp:(new Date).toISOString()},b=Z();let y;try{y=await b.loadStudentRecord(g)}catch(A){return void a("Failed to load student record, answer not saved",A)}const v=l.questions.length,w=b.updateRecordWithAnswer(y,c,s,f,v);try{await b.saveStudentRecord(w)}catch(A){a("Failed to save student record to IndexedDB",A)}const S=b.buildCache(w);$(u.CACHE,S);const E=t.querySelector(`tbody tr:nth-child(${s+1})`);if(E){const t=E.querySelector("td:nth-child(2)");t&&et(t,m)}x("qd:answer-saved",{pageId:c,answer:f});const q=w.pages[c];q&&x("qd:state-changed",{pageId:c,state:q.state})}(t,n,s,o)},200)}(t,n,o,y.value)})}),n.inputs=b;const y=()=>{rt(t,n)},E=()=>{it(t)};document.addEventListener("qd:instructor-show-answers",y),document.addEventListener("qd:instructor-hide-answers",E);const q="true"===sessionStorage.getItem(u.INSTRUCTOR),A="true"===sessionStorage.getItem("qd/instructor/showAnswers");q&&A&&rt(t,n);const O=()=>{if(t.querySelectorAll("td.qd-answer-correct, td.qd-answer-incorrect").forEach(t=>{S(t,"qd-answer-correct","qd-answer-incorrect")}),n.inputs)for(const t of n.inputs)t instanceof HTMLSelectElement?t.selectedIndex=0:t instanceof HTMLInputElement&&(t.value="");it(t)},T=()=>{O()},P=()=>{O()};return document.addEventListener("qd:logout",T),document.addEventListener("qd:login",P),n.cleanupInstructorListeners=()=>{document.removeEventListener("qd:instructor-show-answers",y),document.removeEventListener("qd:instructor-hide-answers",E),document.removeEventListener("qd:logout",T),document.removeEventListener("qd:login",P)},w(t,"qd-quiz-interactive"),!0}(t,l);return n?o.questions.length:r("Interactive enhancement failed"),n}return function(t){return function(t){const n=t.querySelector("colgroup");n&&n.remove()}(t),nt(t),st(t),w(t,"qd-quiz-non-interactive"),!0}(t)}function et(t,n){S(t,"qd-answer-correct","qd-answer-incorrect"),w(t,n?"qd-answer-correct":"qd-answer-incorrect")}function nt(t){const n=t.querySelectorAll("thead th, thead td");n[1]&&w(n[1],"qd-hidden");t.querySelectorAll("tbody tr").forEach(t=>{const n=t.querySelectorAll("td");n[1]&&(w(n[1],"qd-hidden"),n[1].textContent="")})}function st(t){const n=t.querySelectorAll("thead th, thead td");n[2]&&w(n[2],"qd-hidden");t.querySelectorAll("tbody tr").forEach(t=>{const n=t.querySelectorAll("td");n[2]&&w(n[2],"qd-hidden")})}function ot(t){return X.get(t)}async function rt(t,n){const{pageId:s,parsed:o}=n;if(!s)return;const a=C(u.SESSION);if(!a)return;const c=Z();try{const n=await c.getStudentsByRelease(a.release);if(0===n.length)return void alert("No student data available for this release. Students need to log in and answer questions first.");const r=t.querySelector("tbody");if(!r)return;const d=Array.from(r.querySelectorAll("tr"));o.questions.forEach((t,o)=>{const r=d[o];if(!r)return;const a=Array.from(r.querySelectorAll("td"))[1];if(!a)return;const c=a.querySelector(".qd-student-answers");c&&c.remove();const l=function(t,n,s){const o=[];for(const r of t){const t=r.pages[n];if(!t||!t.answers)continue;const a=t.answers[s];a&&o.push({name:r.name,maskedServiceId:r.serviceId.slice(-4),answer:a.answer,success:a.success,formattedTimestamp:m(a.timestamp),cssClass:a.success?"qd-correct":"qd-incorrect"})}return o}(n,s,o);if(l.length>0){const t=document.createElement("div");t.className="qd-student-answers",l.forEach(n=>{const s=document.createElement("div");s.className=`qd-student-answer ${n.cssClass}`,s.innerHTML=`\n ${n.name} (${n.maskedServiceId}) :\n ${n.answer} \n ${n.formattedTimestamp} \n `,t.appendChild(s)}),a.appendChild(t)}}),n.length}catch(d){r("Failed to load student answers",d)}}function it(t){t.querySelectorAll(".qd-student-answers").forEach(t=>t.remove())}function at(t,n=16){let s=5381;for(let r=0;r{b(t).forEach((t,s)=>{if(lt(t)){const o=y(t),a=dt(n,s,o);r.push({row:n,col:s,key:a})}})}),{element:t,tableId:o,editableCells:r,errors:n.length>0?n:void 0}}const ht=new WeakMap;function pt(t,n){const s=ut(t);s.errors&&s.errors.length>0&&r("Analysis table has validation errors:",s.errors);const o={parsed:s,interactive:n.interactive,pageId:n.pageId};if(n.interactive){if(!n.pageId)return r("Interactive mode requires pageId option"),!1;o.debouncer=new Debouncer,o.cellKeyMap=new Map}return ht.set(t,o),n.interactive?function(t,n){const{parsed:s,pageId:o,debouncer:c,cellKeyMap:d}=n;if(!o||!c||!d)return r("Interactive mode requires pageId, debouncer, and cellKeyMap"),!1;if(!C(u.SESSION))return r("No active session found"),!1;const l=C(u.CACHE),h=l?.pages[o],p=h?.analysis,g=p?.cells||{},m=f(t);return s.editableCells.forEach(({row:t,col:s,key:o})=>{const c=m[t];if(!c)return;const l=b(c)[s];l&&(lt(l)?(d.set(l,o),g[o]&&(l.textContent=g[o]),l.contentEditable="true",w(l,"qd-editable"),l.addEventListener("input",()=>{!function(t,n,s){const{debouncer:o,pageId:c}=t;if(!o||!c)return;const d=y(n);o.debounce(`save-cell-${s}`,()=>{!async function(t,n,s){const{pageId:o,parsed:c}=t;if(!o)return;const d=C(u.SESSION);if(!d)return void r("No active session found");const l=Z();let h;try{h=await l.loadStudentRecord(d)}catch(b){return void a("Failed to load student record, analysis not saved",b)}const p=h.pages[o]||{answers:[],state:"unstarted"},g=p.analysis||{tableId:c.tableId,cells:{}};g.cells[n]=s;const m=(new Date).toISOString();g.firstEdited||(g.firstEdited=m);g.lastEdited=m,p.analysis=g,h.pages[o]=p,h.updated=m;try{await l.saveStudentRecord(h)}catch(b){a("Failed to save student record to IndexedDB",b)}const f=l.buildCache(h);$(u.CACHE,f),x("qd:analysis-saved",{pageId:o,tableId:c.tableId,cellKey:n,content:s})}(t,s,d)},500)}(n,l,o)})):r(`Cell at R${t}C${s} is no longer editable`))}),w(t,"qd-analysis-interactive"),!0}(t,o):function(t){w(t,"qd-analysis-non-interactive");const n=()=>{!async function(t){const n=ht.get(t);if(!n)return void a("Cannot show student entries: table not enhanced");const s=n.pageId||function(){const t=document.body.dataset.pageId;if(t)return t;const n=window.location.pathname,s=(n.split("/").pop()||"").replace(".html","");return s||void 0}();if(!s)return void a("Cannot show student entries: page ID not found");const o=C(u.SESSION);if(!o)return void a("Cannot show student entries: no active session");const c=Z();let d;try{d=await c.getStudentsByRelease(o.release)}catch(g){return void r("Failed to load students for instructor view:",g)}const l=function(t,n){const s={};return t.forEach(t=>{const o=t.pages[n];if(!o||!o.analysis)return;const{cells:r}=o.analysis,a=o.analysis.lastEdited||t.updated;Object.entries(r).forEach(([n,o])=>{s[n]||(s[n]=[]),s[n].push({serviceId:t.serviceId,name:t.name,content:o,timestamp:a})})}),s}(d,s),{editableCells:h}=n.parsed,p=f(t);h.forEach(({row:t,col:n,key:s})=>{const o=p[t];if(!o)return;const r=b(o)[n];if(!r)return;const a=function(t){const n=document.createElement("div");if(n.className="qd-student-entries",0===t.length)return n.className+=" qd-no-entries",n.textContent="(No entries yet)",n.style.cssText="color: #9ca3af; font-style: italic; font-size: 13px; padding: 8px 0;",n;const s=function(t){return[...t].sort((t,n)=>{const s=new Date(t.timestamp).getTime();return new Date(n.timestamp).getTime()-s})}(t);return s.forEach(t=>{const s=document.createElement("div");s.className="qd-entry",s.style.cssText="padding: 8px 0; border-bottom: 1px solid #e5e7eb; font-size: 13px; color: #1f2937;";const o=t.serviceId.slice(-4),r=m(t.timestamp),a=document.createElement("span");a.style.cssText="font-weight: 600; color: #374151;",a.textContent=`${t.name} (${o}) • ${r}: `;const c=document.createElement("span");c.style.cssText="white-space: pre-wrap;",c.textContent=t.content,s.appendChild(a),s.appendChild(c),n.appendChild(s)}),n.style.cssText="margin-top: 12px; padding-top: 8px; border-top: 2px solid #3b82f6;",n}(l[s]||[]);a.setAttribute("data-qd-student-entries","true");const c=r.querySelector("[data-qd-student-entries]");c&&c.remove(),r.appendChild(a)}),h.length}(t)},s=()=>{gt(t)};return document.addEventListener("qd:instructor-show-answers",n),document.addEventListener("qd:instructor-hide-answers",s),!0}(t)}function gt(t){t.querySelectorAll("[data-qd-student-entries]").forEach(t=>t.remove())}class EventCoordinator{constructor(){this.listeners=new Map}initialize(){this.registerLoginHandlers(),this.registerLogoutHandlers(),this.registerAnswerHandlers(),this.registerStateHandlers(),this.registerInstructorHandlers(),this.registerDataHandlers()}registerLoginHandlers(){this.addEventListener("qd:login",t=>{(async()=>{const n=t.detail;if(n.serviceId,n.name,"INSTRUCTOR"===n.serviceId)return;const s=C(u.SESSION);if(!s)return;const o=Z();let r,a;try{r=await o.loadStudentRecord(s),await o.saveStudentRecord(r),a=o.buildCache(r),$(u.CACHE,a),a.totals.total}catch{$(u.CACHE,{totals:{total:0,answered:0,correct:0},pages:{}})}this.dispatchEvent("qd:cache-rebuild",{}),this.upgradeTablesAfterLogin()})()})}upgradeTablesAfterLogin(){const t=window.location.pathname,n=t.substring(t.lastIndexOf("/")+1).replace(/\.html?$/i,"");if(!n)return;if("true"===sessionStorage.getItem(u.INSTRUCTOR)){return void document.querySelectorAll("table.qd-quiz").forEach(t=>{const s=ot(t);if(!s)return;s.pageId=n;t.querySelectorAll("td:nth-child(2), th:nth-child(2)").forEach(t=>{t.classList.remove("qd-hidden")});t.querySelectorAll("tbody td:nth-child(2)").forEach((t,n)=>{const o=s.parsed.questions[n];o&&t instanceof HTMLTableCellElement&&(t.textContent=o.correctAnswer)});t.querySelectorAll("td:nth-child(3), th:nth-child(3)").forEach(t=>t.classList.remove("qd-hidden"));const o=()=>{rt(t,s)};document.addEventListener("qd:instructor-show-answers",o),document.addEventListener("qd:instructor-hide-answers",()=>{it(t)});"true"===sessionStorage.getItem("qd/instructor/showAnswers")&&o()})}const s=document.querySelectorAll("table.qd-quiz");s.length>0&&(s.length,s.forEach(t=>{tt(t,{interactive:!0,pageId:n})}));const o=document.querySelectorAll("table.qd-analysis");o.length>0&&(o.length,o.forEach(t=>{pt(t,{interactive:!0,pageId:n})}))}registerLogoutHandlers(){this.addEventListener("qd:logout",t=>{t.detail.serviceId;document.querySelectorAll("table.qd-quiz").forEach(t=>{!function(t){const n=X.get(t);n&&(n.interactive=!1,n.pageId=void 0,n.inputs=void 0,n.cleanupInstructorListeners?.(),n.cleanupInstructorListeners=void 0,nt(t),st(t),S(t,"qd-quiz-interactive"))}(t)});document.querySelectorAll("table.qd-analysis").forEach(t=>{!function(t){const n=ht.get(t);n&&(gt(t),n.interactive&&(t.querySelectorAll(".qd-editable").forEach(t=>{t instanceof HTMLTableCellElement&&(t.contentEditable="false",t.classList.remove("qd-editable"),t.textContent="")}),t.classList.remove("qd-analysis-interactive"),n.debouncer?.cancelAll()),n.interactive=!1,n.pageId=void 0,n.debouncer=void 0,n.cellKeyMap=void 0)}(t)}),this.dispatchEvent("qd:cache-clear",{})})}registerAnswerHandlers(){this.addEventListener("qd:answer-saved",t=>{const n=t.detail;n.pageId,n.questionIndex,n.answer,n.success,this.dispatchEvent("qd:cache-update",{pageId:n.pageId})})}registerStateHandlers(){this.addEventListener("qd:state-changed",t=>{const n=t.detail;n.pageId,n.state,this.dispatchEvent("qd:badge-update",{pageId:n.pageId,state:n.state})})}registerInstructorHandlers(){this.addEventListener("qd:instructor-unlock",t=>{t.detail.unlockTime}),this.addEventListener("qd:instructor-lock",()=>{})}registerDataHandlers(){this.addEventListener("qd:data-cleared",t=>{t.detail.timestamp,this.dispatchEvent("qd:cache-clear",{})})}addEventListener(t,n){document.addEventListener(t,n);const s=this.listeners.get(t)||[];s.push(n),this.listeners.set(t,s)}dispatchEvent(t,n){const s=new CustomEvent(t,{detail:n,bubbles:!0,composed:!0});document.dispatchEvent(s)}cleanup(){for(const[t,n]of this.listeners)for(const s of n)document.removeEventListener(t,s);this.listeners.clear()}}class SessionCoordinator{constructor(){this.sessionService=new SessionService}initialize(){const t=this.sessionService.getSession();if(t){if(t.serviceId,this.sessionService.isExpired())return a("Session expired, clearing"),void this.sessionService.clearSession();this.scheduleExpiryCheck(t),this.setupActivityTracking()}}scheduleExpiryCheck(t){void 0!==this.expiryTimeoutId&&window.clearTimeout(this.expiryTimeoutId);const n=(new Date).getTime(),s=new Date(t.expiresAt).getTime()-n;s<=0?this.sessionService.clearSession():this.expiryTimeoutId=window.setTimeout(()=>{this.sessionService.clearSession()},s)}setupActivityTracking(){const t=()=>{if(!this.sessionService.getSession())return;this.sessionService.updateActivity();const t=this.sessionService.getSession();t&&this.scheduleExpiryCheck(t)};let n;const s=()=>{void 0!==n&&window.clearTimeout(n),n=window.setTimeout(()=>{t()},5e3)};["click","keydown","scroll","mousemove"].forEach(t=>{document.addEventListener(t,s,{passive:!0})})}cleanup(){void 0!==this.expiryTimeoutId&&window.clearTimeout(this.expiryTimeoutId)}getSessionService(){return this.sessionService}}
+var SonarQuiz=function(t){"use strict";let n=!1;function s(t){if(t.length<2)return"**";if(2===t.length)return t;return t.slice(0,2)+"*".repeat(t.length-2)}function r(t){if(null===t||"object"!=typeof t)return t;const n={};for(const[o,a]of Object.entries(t))"name"!==o&&"passwordHash"!==o&&(n[o]="serviceId"!==o||"string"!=typeof a?"object"!=typeof a||null===a?a:r(a):s(a));return n}function o(t,s){n&&(void 0!==s?console.log(`[INFO] ${t}`,r(s)):console.log(`[INFO] ${t}`))}function a(t,s){if(s instanceof Error){const r={name:s.name,message:s.message};n&&s.stack&&(r.stack=s.stack),console.error(`[ERROR] ${t}`,r)}else void 0!==s?console.error(`[ERROR] ${t}`,r(s)):console.error(`[ERROR] ${t}`)}function d(t,n){void 0!==n?console.warn(`[WARN] ${t}`,r(n)):console.warn(`[WARN] ${t}`)}const c=".wh_top_menu_and_indexterms_link",l=".wh_publication_title .title",u="",p="qd-status-container",h="qd-title-selector",g="qd-instructor-hash",m="qd-db-name",f="qd-answer-key",b="qd-question-total";function y(){const t=document.getElementById(b)?.textContent?.trim()??"";if(!/^\d+$/.test(t))return;const n=Number(t);return n>0?n:void 0}function v(t,n){const s=document.querySelector(`#${t}`);if(!s)return n;const r=s.textContent?.trim()||"";return""===r?(d(`Config element #${t} found but empty, using default: "${n}"`),n):(o(`Config read from #${t}: "${r}"`),r)}function w(){o("Reading configuration from DOM...");const t=function(t){const n=document.querySelector(`#${t}`);if(!n){const n=`FATAL: Required config element #${t} not found in DOM. Processing stopped.`;throw console.error(n),new Error(n)}const s=n.textContent?.trim()||"";if(""===s){const n=`FATAL: Required config element #${t} is empty. Processing stopped.`;throw console.error(n),new Error(n)}return o(`Required config read from #${t}: "${s}"`),s}(m),n={statusPanelContainer:v(p,c),titleSelector:v(h,l),instructorHash:v(g,u),dbName:t};return o("Configuration loaded:",n),n}function x(){const t=document.querySelector(`#${m}`);return t?.textContent?.trim()||""}function S(){const t=document.querySelector(v(h,l));return t?.textContent?.trim()||""}const $="0123456789-+.",q="qd1:";function E(t){return t.trim().startsWith(q)}function C(t){return/^[A-Z]{13}$/.test(t)&&13===new Set(t).size}function A(){return document.getElementById(f)?.textContent?.trim()??""}class AnswerDecodeError extends Error{}function P(t,n){const s=t.trim();if(!s.startsWith(q))return s;if(!C(n))throw new AnswerDecodeError("Encoded quiz answer found but #qd-answer-key is missing or invalid");let r="";for(const o of s.slice(4)){const t=n.indexOf(o);if(t<0)throw new AnswerDecodeError(`Encoded quiz answer contains "${o}", which the key does not map`);r+=$[t]}return r}function O(t,n=""){const s=[],r=[];if(!t.classList.contains("qd-quiz"))return s.push('Table must have class "qd-quiz"'),{element:t,questions:r,errors:s};const o=Array.from(t.querySelectorAll("tbody tr"));return 0===o.length?(s.push("Quiz table has no data rows"),{element:t,questions:r,errors:s}):(o.forEach((t,o)=>{const a=Array.from(t.querySelectorAll("td"));if(3!==a.length)return void s.push(`Row ${o+1} has ${a.length} columns, expected 3 (Question | Answer | Detail)`);const d=a[0],c=a[1],l=a[2];if(!d||!c||!l)return;const u=d.textContent?.trim()||"";if(!u)return void s.push(`Row ${o+1} has empty question text`);let p;try{p=P(c.textContent??"",n)}catch(m){return void s.push(`Row ${o+1}: ${m.message}`)}if(!p)return void s.push(`Row ${o+1} has empty answer`);const h=l.querySelector("ol");if(h){const t=(g=h,Array.from(g.querySelectorAll("li")).map(t=>t.textContent?.trim()||"").filter(t=>t.length>0));if(0===t.length)return void s.push(`Row ${o+1} MCQ has no options in `);r.push({text:u,kind:"mcq",correctAnswer:p,options:t})}else{let t;try{t=P(l.textContent??"",n)}catch(m){return void s.push(`Row ${o+1}: ${m.message}`)}const a=parseFloat(t);if(isNaN(a))return void s.push(`Row ${o+1} appears to be numeric but has invalid tolerance: "${t}"`);r.push({text:u,kind:"numeric",correctAnswer:p,tolerance:a})}var g}),{element:t,questions:r,errors:s.length>0?s:void 0})}function T(t,n){if(!n||""===n.trim())return!1;const s=n.trim();if("mcq"===t.kind)return s===t.correctAnswer;{const n=parseFloat(s),r=parseFloat(t.correctAnswer);if(isNaN(n)||isNaN(r))return!1;const o=t.tolerance??0;return Math.abs(n-r)<=o}}function D(t,n){const s=n.answers.length,r=n.answers.filter(t=>""!==t.answer.trim()).length,o=n.answers.filter(t=>t.success).length;return{state:n.state,total:s,answered:r,correct:o,last:n.lastAttempted,answers:n.answers,analysis:n.analysis}}class Debouncer{constructor(){this.timers=new Map}debounce(t,n,s=200){const r=this.timers.get(t);void 0!==r&&clearTimeout(r);const o=setTimeout(()=>{this.timers.delete(t),n()},s);this.timers.set(t,o)}cancel(t){const n=this.timers.get(t);return void 0!==n&&(clearTimeout(n),this.timers.delete(t),!0)}cancelAll(){let t=0;for(const n of this.timers.values())clearTimeout(n),t++;return this.timers.clear(),t}isPending(t){return this.timers.has(t)}getPendingCount(){return this.timers.size}}function _(t){const n=t.querySelector("tbody");return n?Array.from(n.querySelectorAll("tr")):[]}function U(t){return Array.from(t.cells)}function j(t){return t&&t.textContent?.trim()||""}function B(t,n,s){return document.createElement(t)}function F(t,...n){t.classList.add(...n)}function V(t,...n){t.classList.remove(...n)}function Q(t){return t.replace(/&/g,"&").replace(//g,">").replace(/"/g,""").replace(/'/g,"'")}const K="qd/instructor/showAnswers";function W(t){try{const n=sessionStorage.getItem(t);return n?JSON.parse(n):null}catch(n){return d(`Failed to parse JSON from sessionStorage key: ${t}`,n),null}}function J(t,n){try{const s=JSON.stringify(n);return sessionStorage.setItem(t,s),!0}catch(s){return d(`Failed to store JSON in sessionStorage key: ${t}`,s),!1}}function Y(){const t=[];for(let n=0;n=this.lockoutUntil&&(this.lockoutUntil=null),!0)}recordFailure(){if(this.failureCount++,this.failureCount<=this.freeFailures)return;const t=[2e3,4e3,8e3,16e3,3e4],n=t[Math.min(this.failureCount-this.freeFailures-1,t.length-1)]??3e4;this.lockoutUntil=Date.now()+n}reset(){this.failureCount=0,this.lockoutUntil=null}getRemainingSeconds(){if(!this.lockoutUntil)return 0;const t=Math.max(0,this.lockoutUntil-Date.now());return Math.ceil(t/1e3)}isLockedOut(){return null!==this.lockoutUntil&&Date.now()t.toString(16).padStart(2,"0")).join("")}(t);return async function(t,n){if(t.length!==n.length)return!1;if(0===t.length)return!0;const s=new TextEncoder,r=s.encode(t),o=s.encode(n);try{const t=await crypto.subtle.importKey("raw",r,{name:"HMAC",hash:"SHA-256"},!1,["sign"]),n=await crypto.subtle.sign("HMAC",t,o),s=await crypto.subtle.importKey("raw",o,{name:"HMAC",hash:"SHA-256"},!1,["sign"]),a=await crypto.subtle.sign("HMAC",s,r);if(n.byteLength!==a.byteLength)return!1;const d=new Uint8Array(n),c=new Uint8Array(a);let l=0;for(let r=0;r{const n=t.querySelectorAll("td");n[1]&&(F(n[1],"qd-hidden"),n[1].textContent="")})}function ge(t){const n=t.querySelectorAll("thead th, thead td");n[2]&&F(n[2],"qd-hidden");t.querySelectorAll("tbody tr").forEach(t=>{const n=t.querySelectorAll("td")[2];n&&(F(n,"qd-hidden"),pe.has(n)||pe.set(n,n.innerHTML),n.textContent="")})}const me=new WeakMap;const fe=new WeakMap;function be(t,n){let s=fe.get(t);if(!n)return void s?.remove();s||(s=document.createElement("p"),s.className="qd-login-notice",s.setAttribute("role","note"),s.textContent="Log in at the top of the page to answer these questions.",fe.set(t,s));const r=!0===t.parentElement?.classList.contains("simpletable-container")?t.parentElement:t;s.isConnected||r.parentNode?.insertBefore(s,r)}function ye(t,n){const s=function(t,n){if("mcq"===t.kind){const s=(t.options||[]).map((t,n)=>({value:String(n+1),text:`${n+1}. ${t}`}));return{type:"select",className:"qd-quiz-input",placeholder:"Select an answer...",value:n?.answer||"",options:s}}return{type:"text",className:"qd-quiz-input",placeholder:"Enter value",value:n?.answer||""}}(t,n);if("select"===s.type){const t=B("select");t.className=s.className;const n=B("option");return n.value="",n.textContent=s.placeholder,n.disabled=!0,t.appendChild(n),s.options&&s.options.forEach(n=>{const s=B("option");s.value=n.value,s.textContent=n.text,t.appendChild(s)}),t.value=s.value,t}const r=B("input");return r.type=s.type,r.className=s.className,r.placeholder=s.placeholder,r.value=s.value,r}function ve(t,n){return`qd/${t}/u${n}`}class StorageError extends Error{constructor(t,n,s){super(t),this.operation=n,this.cause=s,this.name="StorageError",s?a(`Storage error in ${n}: ${t}`,s):a(`Storage error in ${n}: ${t}`)}}class StorageNotInitializedError extends StorageError{constructor(t){super("Storage adapter not initialized. Call init() first.",t),this.name="StorageNotInitializedError"}}class StorageQuotaError extends StorageError{constructor(t){super("Storage quota exceeded. Please clear old data or free up space.",t),this.name="StorageQuotaError"}}class StorageFormatError extends StorageError{constructor(t,n,s,r){super(t,"formatCheck"),this.name="StorageFormatError",this.expected=n,this.found=s,this.storageKey=r}}const we="students",xe="backups",Se="auditLog";function $e(t){return new Promise((n,s)=>{let r,o=!1;const c=()=>{r&&(clearTimeout(r),r=void 0)};r=window.setTimeout(()=>{if(o)return;o=!0,d("IndexedDB open timed out after 5000ms - attempting recovery");const r=indexedDB.deleteDatabase(t);r.onsuccess=()=>{$e(t).then(n).catch(s)},r.onerror=()=>{s(new StorageError(`Database "${t}" appears corrupted. Please clear site data in browser settings.`,"init"))},r.onblocked=()=>{s(new StorageError("Cannot recover database - close all other tabs with this site and reload.","init"))}},5e3);const l=indexedDB.open(t,3);l.onerror=()=>{o||(o=!0,c(),a(`IndexedDB open error: ${l.error?.message||"unknown"}`),s(new StorageError("Failed to open database","init",l.error)))},l.onblocked=()=>{d("IndexedDB open blocked - close other tabs with this database")},l.onsuccess=()=>{if(o)return;o=!0,c();const r=l.result;if(function(t){return!t.objectStoreNames.contains(we)||!t.objectStoreNames.contains(xe)||!t.objectStoreNames.contains(Se)}(r)){d(`Database corrupted (missing stores). Found: [${Array.from(r.objectStoreNames).join(", ")}]`),r.close();const o=indexedDB.deleteDatabase(t);return o.onsuccess=()=>{$e(t).then(n).catch(s)},void(o.onerror=()=>{s(new StorageError("Failed to delete corrupted database","init",o.error))})}n(r)},l.onupgradeneeded=t=>{!function(t){const n=t.target.result,s=t.target.transaction;s&&(s.onerror=()=>{a(`Upgrade transaction error: ${s.error?.message||"unknown"}`)},s.onabort=()=>{a(`Upgrade transaction aborted: ${s.error?.message||"unknown"}`)});try{if(!n.objectStoreNames.contains(we)){const t=n.createObjectStore(we,{keyPath:null});t.createIndex("by-release","release",{unique:!1}),t.createIndex("by-service-id","serviceId",{unique:!1})}if(!n.objectStoreNames.contains(xe)){const t=n.createObjectStore(xe,{keyPath:null});t.createIndex("by-original-key","originalKey",{unique:!1}),t.createIndex("by-timestamp","timestamp",{unique:!1})}if(!n.objectStoreNames.contains(Se)){const t=n.createObjectStore(Se,{keyPath:"eventId"});t.createIndex("by-service-id","serviceId",{unique:!1}),t.createIndex("by-reset-at","resetAt",{unique:!1})}}catch(r){throw a("Error during database upgrade",r),r}}(t)}})}function qe(t,n){return new Promise((s,r)=>{t.onsuccess=()=>s(t.result),t.onerror=()=>r(ke(t.error,n))})}function Ee(t,n,s,r,o){return new Promise((a,d)=>{let c;try{const a=t.transaction(n,s),l=a.objectStore(n);c=r(l),a.onerror=()=>d(ke(a.error,o))}catch(l){return void d(new StorageError(`Failed during ${o}`,o,l))}c.onsuccess=()=>a(c.result),c.onerror=()=>d(ke(c.error,o))})}function ke(t,n){return"QuotaExceededError"===t?.name?new StorageQuotaError(n):new StorageError(`Failed during ${n}`,n,t)}const Ce="OBF:";function Ie(t){return t?t.split("").map(t=>t.charCodeAt(0).toString()).join(""):""}function Ae(t){return(new TextEncoder).encode(t)}function Pe(t,n){if(0===n.length)return t;const s=new Uint8Array(t.length);for(let r=0;r{this.db=t}).finally(()=>{this.initPromise=null}),this.initPromise)}ensureInitialized(){if(!this.db)throw new StorageNotInitializedError("ensureInitialized");return this.db}async getStudent(t,n){const s=this.ensureInitialized(),r=ve(t,n),o=await Ee(s,we,"readonly",t=>t.get(r),"getStudent");return null==o?null:function(t,n,s){const r=Le(t);if(!r)throw new StorageFormatError("Unobfuscated data found with ENCRYPT_STORAGE enabled. Run migration to encrypt existing data.","obfuscated","plain",s);if(r){const s=Ie(n);try{return Te(t,s)}catch(o){throw new StorageError("Failed to decode obfuscated data - data may be corrupted or tampered","getStudent",o instanceof Error?o:new Error(String(o)))}}return t}(o,t,r)}async saveStudent(t){const n=this.ensureInitialized(),s=ve(t.release,t.serviceId),r=function(t){return Oe(t,Ie(t.release))}(t);await Ee(n,we,"readwrite",t=>t.put(r,s),"saveStudent")}async getStudentsByRelease(t){return this.ensureInitialized(),this.getStudentsByReleaseEncrypted(t)}getStudentsByReleaseEncrypted(t){const n=this.ensureInitialized();return new Promise((s,r)=>{const o=n.transaction(we,"readonly").objectStore(we).openCursor(),a=[];o.onsuccess=()=>{const n=o.result;if(n){const s=function(t,n){if(!Le(t))return null;try{return Te(t,Ie(n))}catch{return null}}(n.value,t);s&&s.release===t&&a.push(s),n.continue()}else s(a)},o.onerror=()=>{r(new StorageError("Failed during getStudentsByRelease","getStudentsByRelease",o.error))}})}async clearAll(){const t=this.ensureInitialized().transaction([we,xe,Se],"readwrite");await Promise.all([qe(t.objectStore(we).clear(),"clearAll"),qe(t.objectStore(xe).clear(),"clearAll"),qe(t.objectStore(Se).clear(),"clearAll")])}async backup(t){const n=this.ensureInitialized();await async function(t,n){const s=(new Date).toISOString(),r=`backup_${s}_${n.serviceId}`,o=ve(n.release,n.serviceId),a={...n,originalKey:o,timestamp:s};await Ee(t,xe,"readwrite",t=>t.put(a,r),"backup")}(n,t)}async saveAuditEvent(t){const n=this.ensureInitialized();await async function(t,n){await Ee(t,Se,"readwrite",t=>t.add(n),"saveAuditEvent")}(n,t)}close(){this.db&&(this.db.close(),this.db=null,this.initPromise=null)}}let Ne=null,De=null;function _e(t){if(!t)throw new Error("FATAL: dbName is required for getStorageAdapter()");return Ne&&De!==t&&(Ne.close(),Ne=null),Ne||(Ne=new IndexedDBStorageAdapter(t),De=t),Ne}function Re(t,n){return 0===n||function(t){return 0===t.length}(t)?"unstarted":function(t,n){if(t.length!==n)return!1;return t.every(t=>!0===t.success)}(t,n)?"complete":"incomplete"}function ze(t){return t?Object.values(t.cells).filter(t=>""!==t.trim()).length:0}function Me(t){const n=t.totalQuestions??0,s=t.answers.filter(t=>""!==t.answer.trim()),r=ze(t.analysis);if(0===s.length&&0===r)return"unstarted";const o=0===n||"complete"===Re(t.answers,n),a=!t.analysis||void 0!==t.analysis.totalCells&&r>=t.analysis.totalCells;return o&&a?"complete":"incomplete"}function Ue(t){return{schema:1,docId:t.release,release:t.release,serviceId:t.serviceId,name:t.name,attempted:0,correct:0,updated:(new Date).toISOString(),pages:{}}}class StorageService{constructor(t){if(!t)throw new Error("FATAL: dbName is required for StorageService");this.dbName=t,this.adapter=_e(t)}async init(){try{await this.adapter.init(),o(`Storage service initialized (IndexedDB "${this.dbName}" ready)`)}catch(t){throw a("Failed to initialize storage service",t),t}}async loadStudentRecord(t){try{const n=await this.adapter.getStudent(t.release,t.serviceId);if(n)return o(`Loaded student record for ${t.serviceId} from IndexedDB`),n;const s=Ue(t);return o(`Created new student record for ${t.serviceId}`),s}catch(n){return d(`IndexedDB error, creating new record: ${n.message}`),Ue(t)}}async saveStudentRecord(t){try{t.updated=(new Date).toISOString();const n=function(t){let n=0,s=0;for(const r in t){const o=t[r];if(o&&o.answers&&Array.isArray(o.answers)){const t=o.answers.filter(t=>""!==t.answer.trim());n+=t.length,s+=t.filter(t=>t.success).length}}return{attempted:n,correct:s}}(t.pages);t.attempted=n.attempted,t.correct=n.correct,await this.adapter.saveStudent(t),o(`Saved student record for ${t.serviceId} to IndexedDB`)}catch(n){throw a("Failed to save student record",n),n}}updateRecordWithAnswer(t,n,s,r,o,a={}){const d=t.pages[n]||{answers:[],state:"unstarted"};for(;d.answers.length<=s;)d.answers.push({answer:"",success:!1,timestamp:(new Date).toISOString()});d.answers[s]=function(t,n,s){const{answer:r,success:o,timestamp:a}=n;if(void 0!==t&&""!==t.answer.trim()&&void 0===t.attempts)return{answer:r,success:o,timestamp:a};const d=t?.attempts??0;if(!s||""===r.trim())return void 0===t?.firstSuccess?{answer:r,success:o,timestamp:a,attempts:d}:{answer:r,success:o,timestamp:a,attempts:d,firstSuccess:t.firstSuccess};const c=d+1;return{answer:r,success:o,timestamp:a,attempts:c,firstSuccess:1===c?o:t?.firstSuccess??o}}(d.answers[s],r,!0===a.commit);const c=(new Date).toISOString();return d.firstAttempted||(d.firstAttempted=c),d.lastAttempted=c,d.totalQuestions=o,d.state=Me(d),{...t,pages:{...t.pages,[n]:d}}}updateRecordWithAnalysis(t,n,s,r,o,a={}){const d=t.pages[n]||{answers:[],state:"unstarted"},c=d.analysis||{tableId:s,cells:{}};c.cells[r]=o,void 0!==a.totalCells&&(c.totalCells=a.totalCells),a.label&&(c.labels={...c.labels,[r]:a.label});const l=(new Date).toISOString();return c.firstEdited||(c.firstEdited=l),c.lastEdited=l,d.analysis=c,d.state=Me(d),t.pages[n]=d,t.updated=l,t}buildCache(t){return function(t){const n={totals:{total:0,answered:0,correct:0},pages:{}};for(const[s,r]of Object.entries(t.pages)){const t=D(0,r);n.pages[s]=t,n.totals.total+=t.total,n.totals.answered+=t.answered,n.totals.correct+=t.correct}return n}(t)}async getStudentsByRelease(t){try{return await this.adapter.getStudentsByRelease(t)}catch(n){throw a("Failed to get students by release",n),n}}async refreshCacheOnLogin(t){try{const n=await this.loadStudentRecord(t);await this.saveStudentRecord(n),J(G.CACHE,this.buildCache(n)),o(`Cache built from IndexedDB for ${t.serviceId}`)}catch{o("Failed to load from IndexedDB, initializing empty cache"),J(G.CACHE,{totals:{total:0,answered:0,correct:0},pages:{}})}}async clearAll(){try{await this.adapter.clearAll(),o("Cleared all data from IndexedDB")}catch(t){throw a("Failed to clear all data",t),t}}async backup(t){try{await this.adapter.backup(t),o(`Created backup for ${t.serviceId}`)}catch(n){d(`Failed to create backup for ${t.serviceId}`,n)}}}let je=null,He=null;function Be(t){if(je&&!t)return je;if(je&&t&&He!==t)return d(`Storage service already initialized with dbName="${He}", ignoring new dbName="${t}"`),je;if(!je){if(!t)throw new Error("FATAL: dbName is required for first getStorageService() call");je=new StorageService(t),He=t}return je}function Fe(t,n,s){const r=new CustomEvent(t,{detail:n,bubbles:s?.bubbles??!0,composed:s?.composed??!0,cancelable:s?.cancelable??!1});return document.dispatchEvent(r)}function Ve(t,n,s,r){const o=new CustomEvent(n,{detail:s,bubbles:!0,composed:!0,cancelable:!1});return t.dispatchEvent(o)}async function Qe(t,n){const s=Be();try{await s.saveStudentRecord(t)}catch(a){d("Failed to save student record to IndexedDB",a)}const r=s.buildCache(t);J(G.CACHE,r),n.onSavedDom?.();const o=Fe;for(const d of n.events)o(d.name,d.detail)}const Ke="qd-sr-only",We="qd-live-region";let Je=0;function Ye(t,n){return t.id||(Je++,t.id=`${n}-${Je}`),t.id}function Xe(t,n,s,r,c=!1){const{debouncer:l,pageId:u,parsed:p}=n;if(!l||!u)return;p.questions[s]&&l.debounce(`save-answer-${s}`,()=>{!async function(t,n,s,r,c=!1){const{pageId:l,parsed:u,inputs:p}=n;if(!l||!p)return;const h=u.questions[s];if(!h)return;const g=W(G.SESSION);if(!g)return void a("No active session found");const m=T(h,r),f={answer:r.trim(),success:m,timestamp:(new Date).toISOString()},b=Be();let y;try{y=await b.loadStudentRecord(g)}catch(S){return void d("Failed to load student record, answer not saved",S)}const v=u.questions.length,w=b.updateRecordWithAnswer(y,l,s,f,v,{commit:c}),x=w.pages[l];await Qe(w,{onSavedDom:()=>{const n=t.querySelector(`tbody tr:nth-child(${s+1})`),r=n?.querySelector("td:nth-child(2)");r&&Ge(r,m),function(t){let n=document.getElementById(We);n||(n=document.createElement("div"),n.id=We,n.className=Ke,n.setAttribute("role","status"),n.setAttribute("aria-live","polite"),n.setAttribute("aria-atomic","true"),document.body.appendChild(n)),n.textContent="",n.textContent=t}(`Question ${s+1}: ${m?"correct":"incorrect"}`)},events:[{name:"qd:answer-saved",detail:{pageId:l,answer:f}},...x?[{name:"qd:state-changed",detail:{pageId:l,state:x.state}}]:[]]}),o(`Answer saved for question ${s+1} on page ${l}: ${m?"correct":"incorrect"}`)}(t,n,s,r,c)},200)}function Ge(t,n){V(t,"qd-answer-correct","qd-answer-incorrect"),F(t,n?"qd-answer-correct":"qd-answer-incorrect");let s=t.querySelector(".qd-answer-feedback");s||(s=document.createElement("span"),s.className="qd-answer-feedback",t.appendChild(s)),s.replaceChildren();const r=document.createElement("span");r.setAttribute("aria-hidden","true"),r.textContent=n?"✓ Correct":"✗ Incorrect";const o=document.createElement("span");o.className=Ke,o.textContent=n?"Correct":"Incorrect",s.append(r,o);const a=t.querySelector("select, input");a&&a.setAttribute("aria-describedby",Ye(s,"qd-feedback"))}function Ze(t,n,s){let r=t.querySelector(".qd-input-error");r||(r=document.createElement("span"),r.className="qd-input-error",r.setAttribute("role","alert"),n.insertAdjacentElement("afterend",r)),r.textContent=s,n.setAttribute("aria-invalid","true"),n.setAttribute("aria-errormessage",Ye(r,"qd-input-error"))}function et(t,n){t.querySelector(".qd-input-error")?.remove(),n.removeAttribute("aria-invalid"),n.removeAttribute("aria-errormessage")}function tt(t,n,s){const r={};return t&&""!==t.trim()||(r.name="Name required"),n?/^[a-zA-Z0-9]{2,10}$/.test(n)||(r.serviceId="Service ID must be 2-10 alphanumeric characters"):r.serviceId="Service ID required",s?/^\d{4}$/.test(s)||(r.pin="PIN must be exactly 4 digits"):r.pin="PIN required",r}function nt(t){return/^[+-]?(\d+(\.\d*)?|\.\d+)$/.test(t)}const st="Enter a number, for example 1500";function rt(t){return function(t,n="display"){if(null==t)return console.warn("Invalid date provided to formatTimestamp:",t),"Invalid Date";const s="string"==typeof t?new Date(t):t;return isNaN(s.getTime())?(console.warn("Invalid date provided to formatTimestamp:",t),"Invalid Date"):"csv"===n?function(t){return t.toISOString()}(s):function(t){return`${t.toLocaleDateString("en-US",{month:"short"})} ${t.getDate()} ${t.getHours().toString().padStart(2,"0")}:${t.getMinutes().toString().padStart(2,"0")}`}(s)}(t,"display")}
/**
* @license
* Copyright 2019 Google LLC
* SPDX-License-Identifier: BSD-3-Clause
- */const mt=globalThis,ft=mt.ShadowRoot&&(void 0===mt.ShadyCSS||mt.ShadyCSS.nativeShadow)&&"adoptedStyleSheets"in Document.prototype&&"replace"in CSSStyleSheet.prototype,bt=Symbol(),yt=new WeakMap;let vt=class{constructor(t,n,s){if(this._$cssResult$=!0,s!==bt)throw Error("CSSResult is not constructable. Use `unsafeCSS` or `css` instead.");this.cssText=t,this.t=n}get styleSheet(){let t=this.o;const n=this.t;if(ft&&void 0===t){const s=void 0!==n&&1===n.length;s&&(t=yt.get(n)),void 0===t&&((this.o=t=new CSSStyleSheet).replaceSync(this.cssText),s&&yt.set(n,t))}return t}toString(){return this.cssText}};const wt=(t,...n)=>{const s=1===t.length?t[0]:n.reduce((n,s,o)=>n+(t=>{if(!0===t._$cssResult$)return t.cssText;if("number"==typeof t)return t;throw Error("Value passed to 'css' function must be a 'css' function result: "+t+". Use 'unsafeCSS' to pass non-literal values, but take care to ensure page security.")})(s)+t[o+1],t[0]);return new vt(s,t,bt)},St=ft?t=>t:t=>t instanceof CSSStyleSheet?(t=>{let n="";for(const s of t.cssRules)n+=s.cssText;return(t=>new vt("string"==typeof t?t:t+"",void 0,bt))(n)})(t):t,{is:xt,defineProperty:Et,getOwnPropertyDescriptor:Ct,getOwnPropertyNames:$t,getOwnPropertySymbols:It,getPrototypeOf:qt}=Object,At=globalThis,kt=At.trustedTypes,Ot=kt?kt.emptyScript:"",Tt=At.reactiveElementPolyfillSupport,Pt=(t,n)=>t,_t={toAttribute(t,n){switch(n){case Boolean:t=t?Ot:null;break;case Object:case Array:t=null==t?t:JSON.stringify(t)}return t},fromAttribute(t,n){let s=t;switch(n){case Boolean:s=null!==t;break;case Number:s=null===t?null:Number(t);break;case Object:case Array:try{s=JSON.parse(t)}catch(o){s=null}}return s}},Nt=(t,n)=>!xt(t,n),Lt={attribute:!0,type:String,converter:_t,reflect:!1,useDefault:!1,hasChanged:Nt};
+ */
+const ot=globalThis,it=ot.ShadowRoot&&(void 0===ot.ShadyCSS||ot.ShadyCSS.nativeShadow)&&"adoptedStyleSheets"in Document.prototype&&"replace"in CSSStyleSheet.prototype,at=Symbol(),dt=new WeakMap;let ct=class{constructor(t,n,s){if(this._$cssResult$=!0,s!==at)throw Error("CSSResult is not constructable. Use `unsafeCSS` or `css` instead.");this.cssText=t,this.t=n}get styleSheet(){let t=this.o;const n=this.t;if(it&&void 0===t){const s=void 0!==n&&1===n.length;s&&(t=dt.get(n)),void 0===t&&((this.o=t=new CSSStyleSheet).replaceSync(this.cssText),s&&dt.set(n,t))}return t}toString(){return this.cssText}};const lt=(t,...n)=>{const s=1===t.length?t[0]:n.reduce((n,s,r)=>n+(t=>{if(!0===t._$cssResult$)return t.cssText;if("number"==typeof t)return t;throw Error("Value passed to 'css' function must be a 'css' function result: "+t+". Use 'unsafeCSS' to pass non-literal values, but take care to ensure page security.")})(s)+t[r+1],t[0]);return new ct(s,t,at)},ut=it?t=>t:t=>t instanceof CSSStyleSheet?(t=>{let n="";for(const s of t.cssRules)n+=s.cssText;return(t=>new ct("string"==typeof t?t:t+"",void 0,at))(n)})(t):t,{is:pt,defineProperty:ht,getOwnPropertyDescriptor:gt,getOwnPropertyNames:mt,getOwnPropertySymbols:ft,getPrototypeOf:bt}=Object,yt=globalThis,vt=yt.trustedTypes,wt=vt?vt.emptyScript:"",xt=yt.reactiveElementPolyfillSupport,St=(t,n)=>t,$t={toAttribute(t,n){switch(n){case Boolean:t=t?wt:null;break;case Object:case Array:t=null==t?t:JSON.stringify(t)}return t},fromAttribute(t,n){let s=t;switch(n){case Boolean:s=null!==t;break;case Number:s=null===t?null:Number(t);break;case Object:case Array:try{s=JSON.parse(t)}catch(r){s=null}}return s}},qt=(t,n)=>!pt(t,n),Et={attribute:!0,type:String,converter:$t,reflect:!1,useDefault:!1,hasChanged:qt};
/**
* @license
* Copyright 2017 Google LLC
* SPDX-License-Identifier: BSD-3-Clause
- */Symbol.metadata??=Symbol("metadata"),At.litPropertyMetadata??=new WeakMap;let Dt=class extends HTMLElement{static addInitializer(t){this._$Ei(),(this.l??=[]).push(t)}static get observedAttributes(){return this.finalize(),this._$Eh&&[...this._$Eh.keys()]}static createProperty(t,n=Lt){if(n.state&&(n.attribute=!1),this._$Ei(),this.prototype.hasOwnProperty(t)&&((n=Object.create(n)).wrapped=!0),this.elementProperties.set(t,n),!n.noAccessor){const s=Symbol(),o=this.getPropertyDescriptor(t,s,n);void 0!==o&&Et(this.prototype,t,o)}}static getPropertyDescriptor(t,n,s){const{get:o,set:r}=Ct(this.prototype,t)??{get(){return this[n]},set(t){this[n]=t}};return{get:o,set(n){const a=o?.call(this);r?.call(this,n),this.requestUpdate(t,a,s)},configurable:!0,enumerable:!0}}static getPropertyOptions(t){return this.elementProperties.get(t)??Lt}static _$Ei(){if(this.hasOwnProperty(Pt("elementProperties")))return;const t=qt(this);t.finalize(),void 0!==t.l&&(this.l=[...t.l]),this.elementProperties=new Map(t.elementProperties)}static finalize(){if(this.hasOwnProperty(Pt("finalized")))return;if(this.finalized=!0,this._$Ei(),this.hasOwnProperty(Pt("properties"))){const t=this.properties,n=[...$t(t),...It(t)];for(const s of n)this.createProperty(s,t[s])}const t=this[Symbol.metadata];if(null!==t){const n=litPropertyMetadata.get(t);if(void 0!==n)for(const[t,s]of n)this.elementProperties.set(t,s)}this._$Eh=new Map;for(const[n,s]of this.elementProperties){const t=this._$Eu(n,s);void 0!==t&&this._$Eh.set(t,n)}this.elementStyles=this.finalizeStyles(this.styles)}static finalizeStyles(t){const n=[];if(Array.isArray(t)){const s=new Set(t.flat(1/0).reverse());for(const t of s)n.unshift(St(t))}else void 0!==t&&n.push(St(t));return n}static _$Eu(t,n){const s=n.attribute;return!1===s?void 0:"string"==typeof s?s:"string"==typeof t?t.toLowerCase():void 0}constructor(){super(),this._$Ep=void 0,this.isUpdatePending=!1,this.hasUpdated=!1,this._$Em=null,this._$Ev()}_$Ev(){this._$ES=new Promise(t=>this.enableUpdating=t),this._$AL=new Map,this._$E_(),this.requestUpdate(),this.constructor.l?.forEach(t=>t(this))}addController(t){(this._$EO??=new Set).add(t),void 0!==this.renderRoot&&this.isConnected&&t.hostConnected?.()}removeController(t){this._$EO?.delete(t)}_$E_(){const t=new Map,n=this.constructor.elementProperties;for(const s of n.keys())this.hasOwnProperty(s)&&(t.set(s,this[s]),delete this[s]);t.size>0&&(this._$Ep=t)}createRenderRoot(){const t=this.shadowRoot??this.attachShadow(this.constructor.shadowRootOptions);return((t,n)=>{if(ft)t.adoptedStyleSheets=n.map(t=>t instanceof CSSStyleSheet?t:t.styleSheet);else for(const s of n){const n=document.createElement("style"),o=mt.litNonce;void 0!==o&&n.setAttribute("nonce",o),n.textContent=s.cssText,t.appendChild(n)}})(t,this.constructor.elementStyles),t}connectedCallback(){this.renderRoot??=this.createRenderRoot(),this.enableUpdating(!0),this._$EO?.forEach(t=>t.hostConnected?.())}enableUpdating(t){}disconnectedCallback(){this._$EO?.forEach(t=>t.hostDisconnected?.())}attributeChangedCallback(t,n,s){this._$AK(t,s)}_$ET(t,n){const s=this.constructor.elementProperties.get(t),o=this.constructor._$Eu(t,s);if(void 0!==o&&!0===s.reflect){const r=(void 0!==s.converter?.toAttribute?s.converter:_t).toAttribute(n,s.type);this._$Em=t,null==r?this.removeAttribute(o):this.setAttribute(o,r),this._$Em=null}}_$AK(t,n){const s=this.constructor,o=s._$Eh.get(t);if(void 0!==o&&this._$Em!==o){const t=s.getPropertyOptions(o),r="function"==typeof t.converter?{fromAttribute:t.converter}:void 0!==t.converter?.fromAttribute?t.converter:_t;this._$Em=o;const a=r.fromAttribute(n,t.type);this[o]=a??this._$Ej?.get(o)??a,this._$Em=null}}requestUpdate(t,n,s){if(void 0!==t){const o=this.constructor,r=this[t];if(s??=o.getPropertyOptions(t),!((s.hasChanged??Nt)(r,n)||s.useDefault&&s.reflect&&r===this._$Ej?.get(t)&&!this.hasAttribute(o._$Eu(t,s))))return;this.C(t,n,s)}!1===this.isUpdatePending&&(this._$ES=this._$EP())}C(t,n,{useDefault:s,reflect:o,wrapped:r},a){s&&!(this._$Ej??=new Map).has(t)&&(this._$Ej.set(t,a??n??this[t]),!0!==r||void 0!==a)||(this._$AL.has(t)||(this.hasUpdated||s||(n=void 0),this._$AL.set(t,n)),!0===o&&this._$Em!==t&&(this._$Eq??=new Set).add(t))}async _$EP(){this.isUpdatePending=!0;try{await this._$ES}catch(n){Promise.reject(n)}const t=this.scheduleUpdate();return null!=t&&await t,!this.isUpdatePending}scheduleUpdate(){return this.performUpdate()}performUpdate(){if(!this.isUpdatePending)return;if(!this.hasUpdated){if(this.renderRoot??=this.createRenderRoot(),this._$Ep){for(const[t,n]of this._$Ep)this[t]=n;this._$Ep=void 0}const t=this.constructor.elementProperties;if(t.size>0)for(const[n,s]of t){const{wrapped:t}=s,o=this[n];!0!==t||this._$AL.has(n)||void 0===o||this.C(n,void 0,s,o)}}let t=!1;const n=this._$AL;try{t=this.shouldUpdate(n),t?(this.willUpdate(n),this._$EO?.forEach(t=>t.hostUpdate?.()),this.update(n)):this._$EM()}catch(s){throw t=!1,this._$EM(),s}t&&this._$AE(n)}willUpdate(t){}_$AE(t){this._$EO?.forEach(t=>t.hostUpdated?.()),this.hasUpdated||(this.hasUpdated=!0,this.firstUpdated(t)),this.updated(t)}_$EM(){this._$AL=new Map,this.isUpdatePending=!1}get updateComplete(){return this.getUpdateComplete()}getUpdateComplete(){return this._$ES}shouldUpdate(t){return!0}update(t){this._$Eq&&=this._$Eq.forEach(t=>this._$ET(t,this[t])),this._$EM()}updated(t){}firstUpdated(t){}};Dt.elementStyles=[],Dt.shadowRootOptions={mode:"open"},Dt[Pt("elementProperties")]=new Map,Dt[Pt("finalized")]=new Map,Tt?.({ReactiveElement:Dt}),(At.reactiveElementVersions??=[]).push("2.1.1");
+ */Symbol.metadata??=Symbol("metadata"),yt.litPropertyMetadata??=new WeakMap;let kt=class extends HTMLElement{static addInitializer(t){this._$Ei(),(this.l??=[]).push(t)}static get observedAttributes(){return this.finalize(),this._$Eh&&[...this._$Eh.keys()]}static createProperty(t,n=Et){if(n.state&&(n.attribute=!1),this._$Ei(),this.prototype.hasOwnProperty(t)&&((n=Object.create(n)).wrapped=!0),this.elementProperties.set(t,n),!n.noAccessor){const s=Symbol(),r=this.getPropertyDescriptor(t,s,n);void 0!==r&&ht(this.prototype,t,r)}}static getPropertyDescriptor(t,n,s){const{get:r,set:o}=gt(this.prototype,t)??{get(){return this[n]},set(t){this[n]=t}};return{get:r,set(n){const a=r?.call(this);o?.call(this,n),this.requestUpdate(t,a,s)},configurable:!0,enumerable:!0}}static getPropertyOptions(t){return this.elementProperties.get(t)??Et}static _$Ei(){if(this.hasOwnProperty(St("elementProperties")))return;const t=bt(this);t.finalize(),void 0!==t.l&&(this.l=[...t.l]),this.elementProperties=new Map(t.elementProperties)}static finalize(){if(this.hasOwnProperty(St("finalized")))return;if(this.finalized=!0,this._$Ei(),this.hasOwnProperty(St("properties"))){const t=this.properties,n=[...mt(t),...ft(t)];for(const s of n)this.createProperty(s,t[s])}const t=this[Symbol.metadata];if(null!==t){const n=litPropertyMetadata.get(t);if(void 0!==n)for(const[t,s]of n)this.elementProperties.set(t,s)}this._$Eh=new Map;for(const[n,s]of this.elementProperties){const t=this._$Eu(n,s);void 0!==t&&this._$Eh.set(t,n)}this.elementStyles=this.finalizeStyles(this.styles)}static finalizeStyles(t){const n=[];if(Array.isArray(t)){const s=new Set(t.flat(1/0).reverse());for(const t of s)n.unshift(ut(t))}else void 0!==t&&n.push(ut(t));return n}static _$Eu(t,n){const s=n.attribute;return!1===s?void 0:"string"==typeof s?s:"string"==typeof t?t.toLowerCase():void 0}constructor(){super(),this._$Ep=void 0,this.isUpdatePending=!1,this.hasUpdated=!1,this._$Em=null,this._$Ev()}_$Ev(){this._$ES=new Promise(t=>this.enableUpdating=t),this._$AL=new Map,this._$E_(),this.requestUpdate(),this.constructor.l?.forEach(t=>t(this))}addController(t){(this._$EO??=new Set).add(t),void 0!==this.renderRoot&&this.isConnected&&t.hostConnected?.()}removeController(t){this._$EO?.delete(t)}_$E_(){const t=new Map,n=this.constructor.elementProperties;for(const s of n.keys())this.hasOwnProperty(s)&&(t.set(s,this[s]),delete this[s]);t.size>0&&(this._$Ep=t)}createRenderRoot(){const t=this.shadowRoot??this.attachShadow(this.constructor.shadowRootOptions);return((t,n)=>{if(it)t.adoptedStyleSheets=n.map(t=>t instanceof CSSStyleSheet?t:t.styleSheet);else for(const s of n){const n=document.createElement("style"),r=ot.litNonce;void 0!==r&&n.setAttribute("nonce",r),n.textContent=s.cssText,t.appendChild(n)}})(t,this.constructor.elementStyles),t}connectedCallback(){this.renderRoot??=this.createRenderRoot(),this.enableUpdating(!0),this._$EO?.forEach(t=>t.hostConnected?.())}enableUpdating(t){}disconnectedCallback(){this._$EO?.forEach(t=>t.hostDisconnected?.())}attributeChangedCallback(t,n,s){this._$AK(t,s)}_$ET(t,n){const s=this.constructor.elementProperties.get(t),r=this.constructor._$Eu(t,s);if(void 0!==r&&!0===s.reflect){const o=(void 0!==s.converter?.toAttribute?s.converter:$t).toAttribute(n,s.type);this._$Em=t,null==o?this.removeAttribute(r):this.setAttribute(r,o),this._$Em=null}}_$AK(t,n){const s=this.constructor,r=s._$Eh.get(t);if(void 0!==r&&this._$Em!==r){const t=s.getPropertyOptions(r),o="function"==typeof t.converter?{fromAttribute:t.converter}:void 0!==t.converter?.fromAttribute?t.converter:$t;this._$Em=r;const a=o.fromAttribute(n,t.type);this[r]=a??this._$Ej?.get(r)??a,this._$Em=null}}requestUpdate(t,n,s){if(void 0!==t){const r=this.constructor,o=this[t];if(s??=r.getPropertyOptions(t),!((s.hasChanged??qt)(o,n)||s.useDefault&&s.reflect&&o===this._$Ej?.get(t)&&!this.hasAttribute(r._$Eu(t,s))))return;this.C(t,n,s)}!1===this.isUpdatePending&&(this._$ES=this._$EP())}C(t,n,{useDefault:s,reflect:r,wrapped:o},a){s&&!(this._$Ej??=new Map).has(t)&&(this._$Ej.set(t,a??n??this[t]),!0!==o||void 0!==a)||(this._$AL.has(t)||(this.hasUpdated||s||(n=void 0),this._$AL.set(t,n)),!0===r&&this._$Em!==t&&(this._$Eq??=new Set).add(t))}async _$EP(){this.isUpdatePending=!0;try{await this._$ES}catch(n){Promise.reject(n)}const t=this.scheduleUpdate();return null!=t&&await t,!this.isUpdatePending}scheduleUpdate(){return this.performUpdate()}performUpdate(){if(!this.isUpdatePending)return;if(!this.hasUpdated){if(this.renderRoot??=this.createRenderRoot(),this._$Ep){for(const[t,n]of this._$Ep)this[t]=n;this._$Ep=void 0}const t=this.constructor.elementProperties;if(t.size>0)for(const[n,s]of t){const{wrapped:t}=s,r=this[n];!0!==t||this._$AL.has(n)||void 0===r||this.C(n,void 0,s,r)}}let t=!1;const n=this._$AL;try{t=this.shouldUpdate(n),t?(this.willUpdate(n),this._$EO?.forEach(t=>t.hostUpdate?.()),this.update(n)):this._$EM()}catch(s){throw t=!1,this._$EM(),s}t&&this._$AE(n)}willUpdate(t){}_$AE(t){this._$EO?.forEach(t=>t.hostUpdated?.()),this.hasUpdated||(this.hasUpdated=!0,this.firstUpdated(t)),this.updated(t)}_$EM(){this._$AL=new Map,this.isUpdatePending=!1}get updateComplete(){return this.getUpdateComplete()}getUpdateComplete(){return this._$ES}shouldUpdate(t){return!0}update(t){this._$Eq&&=this._$Eq.forEach(t=>this._$ET(t,this[t])),this._$EM()}updated(t){}firstUpdated(t){}};kt.elementStyles=[],kt.shadowRootOptions={mode:"open"},kt[St("elementProperties")]=new Map,kt[St("finalized")]=new Map,xt?.({ReactiveElement:kt}),(yt.reactiveElementVersions??=[]).push("2.1.1");
/**
* @license
* Copyright 2017 Google LLC
* SPDX-License-Identifier: BSD-3-Clause
*/
-const Rt=globalThis,Mt=Rt.trustedTypes,zt=Mt?Mt.createPolicy("lit-html",{createHTML:t=>t}):void 0,Ht="$lit$",Ut=`lit$${Math.random().toFixed(9).slice(2)}$`,jt="?"+Ut,Ft=`<${jt}>`,Bt=document,Vt=()=>Bt.createComment(""),Qt=t=>null===t||"object"!=typeof t&&"function"!=typeof t,Kt=Array.isArray,Jt="[ \t\n\f\r]",Wt=/<(?:(!--|\/[^a-zA-Z])|(\/?[a-zA-Z][^>\s]*)|(\/?$))/g,Yt=/-->/g,Gt=/>/g,Zt=RegExp(`>|${Jt}(?:([^\\s"'>=/]+)(${Jt}*=${Jt}*(?:[^ \t\n\f\r"'\`<>=]|("|')|))|$)`,"g"),Xt=/'/g,te=/"/g,ee=/^(?:script|style|textarea|title)$/i,ne=(ae=1,(t,...n)=>({_$litType$:ae,strings:t,values:n})),se=Symbol.for("lit-noChange"),oe=Symbol.for("lit-nothing"),re=new WeakMap,ie=Bt.createTreeWalker(Bt,129);var ae;function ce(t,n){if(!Kt(t)||!t.hasOwnProperty("raw"))throw Error("invalid template strings array");return void 0!==zt?zt.createHTML(n):n}class N{constructor({strings:t,_$litType$:n},s){let o;this.parts=[];let r=0,a=0;const c=t.length-1,d=this.parts,[l,u]=((t,n)=>{const s=t.length-1,o=[];let r,a=2===n?"":3===n?"":"",c=Wt;for(let d=0;d"===l[0]?(c=r??Wt,u=-1):void 0===l[1]?u=-2:(u=c.lastIndex-l[2].length,s=l[1],c=void 0===l[3]?Zt:'"'===l[3]?te:Xt):c===te||c===Xt?c=Zt:c===Yt||c===Gt?c=Wt:(c=Zt,r=void 0);const p=c===Zt&&t[d+1].startsWith("/>")?" ":"";a+=c===Wt?n+Ft:u>=0?(o.push(s),n.slice(0,u)+Ht+n.slice(u)+Ut+p):n+Ut+(-2===u?d:p)}return[ce(t,a+(t[s]||">")+(2===n?" ":3===n?"":"")),o]})(t,n);if(this.el=N.createElement(l,s),ie.currentNode=this.el.content,2===n||3===n){const t=this.el.content.firstChild;t.replaceWith(...t.childNodes)}for(;null!==(o=ie.nextNode())&&d.length0){o.textContent=Mt?Mt.emptyScript:"";for(let s=0;sKt(t)||"function"==typeof t?.[Symbol.iterator])(t)?this.k(t):this._(t)}O(t){return this._$AA.parentNode.insertBefore(t,this._$AB)}T(t){this._$AH!==t&&(this._$AR(),this._$AH=this.O(t))}_(t){this._$AH!==oe&&Qt(this._$AH)?this._$AA.nextSibling.data=t:this.T(Bt.createTextNode(t)),this._$AH=t}$(t){const{values:n,_$litType$:s}=t,o="number"==typeof s?this._$AC(t):(void 0===s.el&&(s.el=N.createElement(ce(s.h,s.h[0]),this.options)),s);if(this._$AH?._$AD===o)this._$AH.p(n);else{const t=new M(o,this),s=t.u(this.options);t.p(n),this.T(s),this._$AH=t}}_$AC(t){let n=re.get(t.strings);return void 0===n&&re.set(t.strings,n=new N(t)),n}k(t){Kt(this._$AH)||(this._$AH=[],this._$AR());const n=this._$AH;let s,o=0;for(const r of t)o===n.length?n.push(s=new R(this.O(Vt()),this.O(Vt()),this,this.options)):s=n[o],s._$AI(r),o++;o2||""!==s[0]||""!==s[1]?(this._$AH=Array(s.length-1).fill(new String),this.strings=s):this._$AH=oe}_$AI(t,n=this,s,o){const r=this.strings;let a=!1;if(void 0===r)t=de(this,t,n,0),a=!Qt(t)||t!==this._$AH&&t!==se,a&&(this._$AH=t);else{const o=t;let c,d;for(t=r[0],c=0;c{const o=s?.renderBefore??n;let r=o._$litPart$;if(void 0===r){const t=s?.renderBefore??null;o._$litPart$=r=new R(n.insertBefore(Vt(),t),t,void 0,s??{})}return r._$AI(t),r},he=globalThis;
+const Ct=globalThis,It=Ct.trustedTypes,At=It?It.createPolicy("lit-html",{createHTML:t=>t}):void 0,Pt="$lit$",Ot=`lit$${Math.random().toFixed(9).slice(2)}$`,Tt="?"+Ot,Lt=`<${Tt}>`,Nt=document,Dt=()=>Nt.createComment(""),_t=t=>null===t||"object"!=typeof t&&"function"!=typeof t,Rt=Array.isArray,zt="[ \t\n\f\r]",Mt=/<(?:(!--|\/[^a-zA-Z])|(\/?[a-zA-Z][^>\s]*)|(\/?$))/g,Ut=/-->/g,jt=/>/g,Ht=RegExp(`>|${zt}(?:([^\\s"'>=/]+)(${zt}*=${zt}*(?:[^ \t\n\f\r"'\`<>=]|("|')|))|$)`,"g"),Bt=/'/g,Ft=/"/g,Vt=/^(?:script|style|textarea|title)$/i,Qt=(Xt=1,(t,...n)=>({_$litType$:Xt,strings:t,values:n})),Kt=Symbol.for("lit-noChange"),Wt=Symbol.for("lit-nothing"),Jt=new WeakMap,Yt=Nt.createTreeWalker(Nt,129);var Xt;function Gt(t,n){if(!Rt(t)||!t.hasOwnProperty("raw"))throw Error("invalid template strings array");return void 0!==At?At.createHTML(n):n}class N{constructor({strings:t,_$litType$:n},s){let r;this.parts=[];let o=0,a=0;const d=t.length-1,c=this.parts,[l,u]=((t,n)=>{const s=t.length-1,r=[];let o,a=2===n?"":3===n?"":"",d=Mt;for(let c=0;c"===l[0]?(d=o??Mt,u=-1):void 0===l[1]?u=-2:(u=d.lastIndex-l[2].length,s=l[1],d=void 0===l[3]?Ht:'"'===l[3]?Ft:Bt):d===Ft||d===Bt?d=Ht:d===Ut||d===jt?d=Mt:(d=Ht,o=void 0);const h=d===Ht&&t[c+1].startsWith("/>")?" ":"";a+=d===Mt?n+Lt:u>=0?(r.push(s),n.slice(0,u)+Pt+n.slice(u)+Ot+h):n+Ot+(-2===u?c:h)}return[Gt(t,a+(t[s]||">")+(2===n?" ":3===n?"":"")),r]})(t,n);if(this.el=N.createElement(l,s),Yt.currentNode=this.el.content,2===n||3===n){const t=this.el.content.firstChild;t.replaceWith(...t.childNodes)}for(;null!==(r=Yt.nextNode())&&c.length0){r.textContent=It?It.emptyScript:"";for(let s=0;sRt(t)||"function"==typeof t?.[Symbol.iterator])(t)?this.k(t):this._(t)}O(t){return this._$AA.parentNode.insertBefore(t,this._$AB)}T(t){this._$AH!==t&&(this._$AR(),this._$AH=this.O(t))}_(t){this._$AH!==Wt&&_t(this._$AH)?this._$AA.nextSibling.data=t:this.T(Nt.createTextNode(t)),this._$AH=t}$(t){const{values:n,_$litType$:s}=t,r="number"==typeof s?this._$AC(t):(void 0===s.el&&(s.el=N.createElement(Gt(s.h,s.h[0]),this.options)),s);if(this._$AH?._$AD===r)this._$AH.p(n);else{const t=new M(r,this),s=t.u(this.options);t.p(n),this.T(s),this._$AH=t}}_$AC(t){let n=Jt.get(t.strings);return void 0===n&&Jt.set(t.strings,n=new N(t)),n}k(t){Rt(this._$AH)||(this._$AH=[],this._$AR());const n=this._$AH;let s,r=0;for(const o of t)r===n.length?n.push(s=new R(this.O(Dt()),this.O(Dt()),this,this.options)):s=n[r],s._$AI(o),r++;r2||""!==s[0]||""!==s[1]?(this._$AH=Array(s.length-1).fill(new String),this.strings=s):this._$AH=Wt}_$AI(t,n=this,s,r){const o=this.strings;let a=!1;if(void 0===o)t=Zt(this,t,n,0),a=!_t(t)||t!==this._$AH&&t!==Kt,a&&(this._$AH=t);else{const r=t;let d,c;for(t=o[0],d=0;d{const r=s?.renderBefore??n;let o=r._$litPart$;if(void 0===o){const t=s?.renderBefore??null;r._$litPart$=o=new R(n.insertBefore(Dt(),t),t,void 0,s??{})}return o._$AI(t),o},nn=globalThis;
/**
* @license
* Copyright 2017 Google LLC
* SPDX-License-Identifier: BSD-3-Clause
- */let pe=class extends Dt{constructor(){super(...arguments),this.renderOptions={host:this},this._$Do=void 0}createRenderRoot(){const t=super.createRenderRoot();return this.renderOptions.renderBefore??=t.firstChild,t}update(t){const n=this.render();this.hasUpdated||(this.renderOptions.isConnected=this.isConnected),super.update(t),this._$Do=ue(n,this.renderRoot,this.renderOptions)}connectedCallback(){super.connectedCallback(),this._$Do?.setConnected(!0)}disconnectedCallback(){super.disconnectedCallback(),this._$Do?.setConnected(!1)}render(){return se}};pe._$litElement$=!0,pe.finalized=!0,he.litElementHydrateSupport?.({LitElement:pe});const ge=he.litElementPolyfillSupport;ge?.({LitElement:pe}),(he.litElementVersions??=[]).push("4.2.1");
+ */let sn=class extends kt{constructor(){super(...arguments),this.renderOptions={host:this},this._$Do=void 0}createRenderRoot(){const t=super.createRenderRoot();return this.renderOptions.renderBefore??=t.firstChild,t}update(t){const n=this.render();this.hasUpdated||(this.renderOptions.isConnected=this.isConnected),super.update(t),this._$Do=tn(n,this.renderRoot,this.renderOptions)}connectedCallback(){super.connectedCallback(),this._$Do?.setConnected(!0)}disconnectedCallback(){super.disconnectedCallback(),this._$Do?.setConnected(!1)}render(){return Kt}};sn._$litElement$=!0,sn.finalized=!0,nn.litElementHydrateSupport?.({LitElement:sn});const rn=nn.litElementPolyfillSupport;rn?.({LitElement:sn}),(nn.litElementVersions??=[]).push("4.2.1");
/**
* @license
* Copyright 2017 Google LLC
* SPDX-License-Identifier: BSD-3-Clause
*/
-const me=t=>(n,s)=>{void 0!==s?s.addInitializer(()=>{customElements.define(t,n)}):customElements.define(t,n)},fe={attribute:!0,type:String,converter:_t,reflect:!1,hasChanged:Nt},be=(t=fe,n,s)=>{const{kind:o,metadata:r}=s;let a=globalThis.litPropertyMetadata.get(r);if(void 0===a&&globalThis.litPropertyMetadata.set(r,a=new Map),"setter"===o&&((t=Object.create(t)).wrapped=!0),a.set(s.name,t),"accessor"===o){const{name:o}=s;return{set(s){const r=n.get.call(this);n.set.call(this,s),this.requestUpdate(o,r,t)},init(n){return void 0!==n&&this.C(o,void 0,t,n),n}}}if("setter"===o){const{name:o}=s;return function(s){const r=this[o];n.call(this,s),this.requestUpdate(o,r,t)}}throw Error("Unsupported decorator location: "+o)};
+const on=t=>(n,s)=>{void 0!==s?s.addInitializer(()=>{customElements.define(t,n)}):customElements.define(t,n)},an={attribute:!0,type:String,converter:$t,reflect:!1,hasChanged:qt},dn=(t=an,n,s)=>{const{kind:r,metadata:o}=s;let a=globalThis.litPropertyMetadata.get(o);if(void 0===a&&globalThis.litPropertyMetadata.set(o,a=new Map),"setter"===r&&((t=Object.create(t)).wrapped=!0),a.set(s.name,t),"accessor"===r){const{name:r}=s;return{set(s){const o=n.get.call(this);n.set.call(this,s),this.requestUpdate(r,o,t)},init(n){return void 0!==n&&this.C(r,void 0,t,n),n}}}if("setter"===r){const{name:r}=s;return function(s){const o=this[r];n.call(this,s),this.requestUpdate(r,o,t)}}throw Error("Unsupported decorator location: "+r)};
/**
* @license
* Copyright 2017 Google LLC
* SPDX-License-Identifier: BSD-3-Clause
- */function ye(t){return(n,s)=>"object"==typeof s?be(t,n,s):((t,n,s)=>{const o=n.hasOwnProperty(s);return n.constructor.createProperty(s,t),o?Object.getOwnPropertyDescriptor(n,s):void 0})(t,n,s)}
+ */function cn(t){return(n,s)=>"object"==typeof s?dn(t,n,s):((t,n,s)=>{const r=n.hasOwnProperty(s);return n.constructor.createProperty(s,t),r?Object.getOwnPropertyDescriptor(n,s):void 0})(t,n,s)}
/**
* @license
* Copyright 2017 Google LLC
* SPDX-License-Identifier: BSD-3-Clause
- */function ve(t){return ye({...t,state:!0,attribute:!1})}
+ */function ln(t){return cn({...t,state:!0,attribute:!1})}
/**
* @license
* Copyright 2017 Google LLC
@@ -46,13 +47,268 @@ const me=t=>(n,s)=>{void 0!==s?s.addInitializer(()=>{customElements.define(t,n)}
* Copyright 2017 Google LLC
* SPDX-License-Identifier: BSD-3-Clause
*/
-function we(t,n){return(n,s,o)=>((t,n,s)=>(s.configurable=!0,s.enumerable=!0,Reflect.decorate&&"object"!=typeof n&&Object.defineProperty(t,n,s),s))(n,s,{get(){return(n=>n.renderRoot?.querySelector(t)??null)(this)}})}const Se=".wh_top_menu_and_indexterms_link",xe=".wh_publication_title .title",Ee="",Ce="qd-status-container",$e="qd-title-selector",Ie="qd-instructor-hash",qe="qd-db-name";function Ae(t,n){const s=document.querySelector(`#${t}`);if(!s)return n;const o=s.textContent?.trim()||"";return""===o?(a(`Config element #${t} found but empty, using default: "${n}"`),n):o}function ke(){const t=function(t){const n=document.querySelector(`#${t}`);if(!n){const n=`FATAL: Required config element #${t} not found in DOM. Processing stopped.`;throw console.error(n),new Error(n)}const s=n.textContent?.trim()||"";if(""===s){const n=`FATAL: Required config element #${t} is empty. Processing stopped.`;throw console.error(n),new Error(n)}return s}(qe);return{statusPanelContainer:Ae(Ce,Se),titleSelector:Ae($e,xe),instructorHash:Ae(Ie,Ee),dbName:t}}function Oe(t){return t.schema<2}function Te(t){return Boolean(t.pinHash&&t.pinHash.length>0)}function Pe(t,n){return{...t,schema:2,pinHash:n,pinCreatedAt:(new Date).toISOString()}}async function _e(t){const n=(new TextEncoder).encode(t),s=await crypto.subtle.digest("SHA-256",n);return Array.from(new Uint8Array(s)).map(t=>t.toString(16).padStart(2,"0")).join("")}async function Ne(t,n){return function(t,n){if(t.length!==n.length)return!1;let s=0;for(let o=0;oo?{isLocked:!0,remainingMs:s-o}:(ze(t),{isLocked:!1,remainingMs:0})}function Me(t){const s=(new Date).toISOString();let o=De(t);if(o||(o={serviceId:t,attempts:0,lockoutUntil:null,lastAttempt:s}),o.attempts+=1,o.lastAttempt=s,o.attempts>=h){const s=new Date(Date.now()+p);o.lockoutUntil=s.toISOString(),a(`PIN lockout triggered for ${n(t)} after ${o.attempts} failed attempts`)}else o.attempts,n(t);const r=Le(t);return sessionStorage.setItem(r,JSON.stringify(o)),o}function ze(t){const s=De(t);s&&s.attempts>0&&(s.attempts,n(t));const o=Le(t);sessionStorage.removeItem(o)}function He(t){const n=De(t);if(!n)return h;return Re(t).isLocked?0:Math.max(0,h-n.attempts)}var Ue=Object.getOwnPropertyDescriptor;let je=class extends pe{render(){return ne`
+function un(t,n){return(n,s,r)=>((t,n,s)=>(s.configurable=!0,s.enumerable=!0,Reflect.decorate&&"object"!=typeof n&&Object.defineProperty(t,n,s),s))(n,s,{get(){return(n=>n.renderRoot?.querySelector(t)??null)(this)}})}var pn=Object.defineProperty,hn=Object.getOwnPropertyDescriptor,gn=(t,n,s,r)=>{for(var o,a=r>1?void 0:r?hn(n,s):n,d=t.length-1;d>=0;d--)(o=t[d])&&(a=(r?o(n,s,a):o(a))||a);return r&&a&&pn(n,s,a),a};let mn=class extends sn{constructor(){super(...arguments),this.answers=[]}render(){return this.answers.map(t=>Qt`
+ ${t.name} (${t.maskedServiceId}) :
+ ${t.answer}
+ ${t.formattedTimestamp}
+
`)}};async function fn(t,n){const{pageId:s,parsed:r}=n;if(!s)return;const d=W(G.SESSION);if(!d)return;const c=Be();try{const n=await c.getStudentsByRelease(d.release);if(0===n.length)return o("No student data available for this release"),void alert("No student data available for this release. Students need to log in and answer questions first.");const a=t.querySelector("tbody");if(!a)return;const l=Array.from(a.querySelectorAll("tr"));r.questions.forEach((t,r)=>{const o=l[r];if(!o)return;const a=Array.from(o.querySelectorAll("td"))[1];if(!a)return;const d=a.querySelector(".qd-student-answers");d&&d.remove();const c=function(t,n,s){const r=[];for(const o of t){const t=o.pages[n];if(!t||!t.answers)continue;const a=t.answers[s];a&&r.push({name:o.name,maskedServiceId:o.serviceId.slice(-4),answer:a.answer,success:a.success,formattedTimestamp:rt(a.timestamp),cssClass:a.success?"qd-correct":"qd-incorrect"})}return r}(n,s,r);if(c.length>0){const t=document.createElement("qd-student-answers");t.classList.add("qd-student-answers"),t.answers=c,a.appendChild(t)}}),o(`Displayed student answers for ${n.length} students on page ${s}`)}catch(l){a("Failed to load student answers",l)}}function bn(t){t.querySelectorAll(".qd-student-answers").forEach(t=>t.remove()),o("Hid student answers from quiz table")}mn.styles=lt`
+ :host {
+ display: block;
+ margin-top: 12px;
+ padding: 8px;
+ background: #f8f9fa;
+ border-radius: 4px;
+ border: 1px solid #dee2e6;
+ }
+
+ .qd-student-answer {
+ font-size: 12px;
+ padding: 4px 0;
+ line-height: 1.4;
+ }
+
+ .qd-student-answer.qd-correct {
+ color: #28a745;
+ }
+
+ .qd-student-answer.qd-incorrect {
+ color: #dc3545;
+ }
+
+ .qd-student-name {
+ font-weight: 600;
+ }
+
+ .qd-student-answer-text {
+ margin: 0 4px;
+ }
+
+ .qd-timestamp {
+ color: #6c757d;
+ font-size: 11px;
+ margin-left: 8px;
+ }
+ `,gn([cn({attribute:!1})],mn.prototype,"answers",2),mn=gn([on("qd-student-answers")],mn);const yn=new WeakMap;function vn(t,n){const s=yn.get(t);let r;if(s){if(s.interactive||!n.interactive)return o("Quiz table already enhanced, skipping"),!0;o("Upgrading quiz table from non-interactive to interactive mode"),r=s.parsed}else{const n=A();if(function(t,n){return!C(n)&&Array.from(t.querySelectorAll("tbody td")).some(t=>E(t.textContent??""))}(t,n))return a("Quiz table has encoded answers but #qd-answer-key is missing or invalid; table not enhanced"),!1;r=O(t,n),r.errors&&r.errors.length>0&&a("Quiz table has validation errors:",r.errors)}const d={parsed:r,interactive:n.interactive,pageId:n.pageId};if(n.interactive){if(!n.pageId)return a("Interactive mode requires pageId option"),!1;o(`Preparing interactive enhancement for pageId: ${n.pageId}`),d.debouncer=new Debouncer,d.inputs=[]}if(yn.set(t,d),n.interactive){const n=function(t,n){const{parsed:s,pageId:r,debouncer:d}=n;if(!r||!d)return a("Interactive mode requires pageId and debouncer"),!1;(function(t){const n=t.querySelectorAll("thead th, thead td");n[1]&&V(n[1],"qd-hidden"),t.querySelectorAll("tbody tr").forEach(t=>{const n=t.querySelectorAll("td");n[1]&&V(n[1],"qd-hidden")})})(t),function(t){const n=t.querySelectorAll("thead th, thead td")[1];n&&(me.has(t)||me.set(t,n.textContent??""),n.textContent="Your answer")}(t),be(t,!1),ge(t);if(!W(G.SESSION))return a("No active session found"),!1;let c=W(G.CACHE);c?o(`Cache loaded: ${c.totals.total} total questions, ${Object.keys(c.pages).length} pages`):(o("No cache found, creating empty cache"),c={totals:{total:0,answered:0,correct:0},pages:{}});const l=s.questions.length;c=function(t,n,s){const r=t.pages[n];if(r&&r.total>=s)return t;const o=s-(r?.total||0),a={state:r?.state||"unstarted",total:s,answered:r?.answered||0,correct:r?.correct||0,last:r?.last,answers:r?.answers,analysis:r?.analysis};return{totals:{total:t.totals.total+o,answered:t.totals.answered,correct:t.totals.correct},pages:{...t.pages,[n]:a}}}(c,r,l),J(G.CACHE,c);const u=c?.pages[r],p=u?.answers||[];o(`Page ${r}: ${p.length} existing answers, state: ${u?.state||"none"}`);const h=t.querySelector("tbody");if(!h)return a("Quiz table has no tbody element"),!1;const g=Array.from(h.querySelectorAll("tr")),m=[];s.questions.forEach((s,r)=>{const a=g[r];if(!a)return;const d=Array.from(a.querySelectorAll("td"));if(3!==d.length)return;const c=d[0],l=d[1];if(!c||!l)return;const u=p[r];u&&u.answer&&o(`Q${r+1}: Pre-filling with "${u.answer}" (${u.success?"correct":"incorrect"})`);const h=ye(s,u);m.push(h),h.setAttribute("aria-labelledby",Ye(c,"qd-question")),V(l,"qd-answer-correct","qd-answer-incorrect"),l.textContent="",l.appendChild(h),u&&Ge(l,u.success),h instanceof HTMLSelectElement?h.addEventListener("change",()=>{Xe(t,n,r,h.value,!0)}):(h.inputMode="decimal",h.addEventListener("input",()=>{const s=h.value.trim();!function(t){return/[^0-9+\-.\s]/.test(t)}(s)?(et(l,h),(""===s||nt(s))&&Xe(t,n,r,h.value)):Ze(l,h,st)}),h.addEventListener("blur",()=>{const t=h.value.trim();""===t||nt(t)||Ze(l,h,st)}),h.addEventListener("change",()=>{nt(h.value.trim())&&Xe(t,n,r,h.value,!0)}))}),n.inputs=m;const f=()=>{fn(t,n)},b=()=>{bn(t)};document.addEventListener("qd:instructor-show-answers",f),document.addEventListener("qd:instructor-hide-answers",b);const y=ue(),v="true"===sessionStorage.getItem(K);y&&v&&fn(t,n);const w=()=>{if(t.querySelectorAll("tbody tr").forEach(t=>{const n=t.querySelectorAll("td")[1];if(!n)return;!function(t){V(t,"qd-answer-correct","qd-answer-incorrect"),t.querySelector(".qd-answer-feedback")?.remove(),t.querySelector("select, input")?.removeAttribute("aria-describedby")}(n);const s=n.querySelector("select, input");s&&et(n,s)}),n.inputs)for(const t of n.inputs)t instanceof HTMLSelectElement?t.selectedIndex=0:t instanceof HTMLInputElement&&(t.value="");bn(t)},x=()=>{w(),be(t,!0),o("Cleared student UI state from quiz table on logout")},S=()=>{w(),be(t,!1),o("Reset quiz table UI on login")};return document.addEventListener("qd:logout",x),document.addEventListener("qd:login",S),n.cleanupInstructorListeners=()=>{document.removeEventListener("qd:instructor-show-answers",f),document.removeEventListener("qd:instructor-hide-answers",b),document.removeEventListener("qd:logout",x),document.removeEventListener("qd:login",S)},F(t,"qd-quiz-interactive"),o(`Quiz table enhanced in interactive mode for page ${r}`),!0}(t,d);return n?o(`Interactive enhancement succeeded for table with ${r.questions.length} questions`):a("Interactive enhancement failed"),n}return function(t){return function(t){const n=t.querySelector("colgroup");n&&n.remove()}(t),he(t),ge(t),be(t,!0),F(t,"qd-quiz-non-interactive"),o("Quiz table enhanced in non-interactive mode"),!0}(t)}function wn(t){return yn.get(t)}function xn(t,n=16){let s=5381;for(let o=0;o{U(t).forEach((t,s)=>{if(qn(t)){const r=j(t),a=$n(n,s,r);o.push({row:n,col:s,key:a})}})}),{element:t,tableId:r,editableCells:o,errors:n.length>0?n:void 0}}function kn(t,n,s){const{debouncer:r,pageId:c}=t;if(!r||!c)return;const l=j(n),u=function(t){const n=t.getAttribute("aria-labelledby"),s=n?document.getElementById(n)?.textContent:void 0,r=s?.replace(/\s+/g," ").trim().replace(/:$/,"").trim();return r||void 0}(n);r.debounce(`save-cell-${s}`,()=>{!async function(t,n,s,r){const{pageId:c,parsed:l}=t;if(!c)return;const u=W(G.SESSION);if(!u)return void a("No active session found");const p=Be();let h;try{h=await p.loadStudentRecord(u)}catch(m){return void d("Failed to load student record, analysis not saved",m)}const g=p.updateRecordWithAnalysis(h,c,l.tableId,n,s,{totalCells:l.editableCells.length,label:r});await Qe(g,{events:[{name:"qd:analysis-saved",detail:{pageId:c,tableId:l.tableId,cellKey:n,content:s}}]}),o(`Analysis cell saved for ${n} on page ${c}`)}(t,s,l,u)},500)}var Cn=Object.defineProperty,In=Object.getOwnPropertyDescriptor,An=(t,n,s,r)=>{for(var o,a=r>1?void 0:r?In(n,s):n,d=t.length-1;d>=0;d--)(o=t[d])&&(a=(r?o(n,s,a):o(a))||a);return r&&a&&Cn(n,s,a),a};let Pn=class extends sn{constructor(){super(...arguments),this.entries=[]}render(){return 0===this.entries.length?(this.setAttribute("data-empty",""),Qt`(No entries yet)
`):(this.removeAttribute("data-empty"),(t=this.entries,[...t].sort((t,n)=>{const s=new Date(t.timestamp).getTime();return new Date(n.timestamp).getTime()-s})).map(t=>{const n=t.serviceId.slice(-4),s=rt(t.timestamp);return Qt`
+ ${t.name} (${n}) • ${s}:
+ ${t.content}
+
`}));var t}};async function On(t,n){const s=n.pageId||function(){const t=document.body.dataset.pageId;if(t)return t;const n=window.location.pathname,s=(n.split("/").pop()||"").replace(".html","");return s||void 0}();if(!s)return void d("Cannot show student entries: page ID not found");const r=W(G.SESSION);if(!r)return void d("Cannot show student entries: no active session");const c=Be();let l;try{l=await c.getStudentsByRelease(r.release)}catch(g){return void a("Failed to load students for instructor view:",g)}const u=function(t,n){const s={};return t.forEach(t=>{const r=t.pages[n];if(!r||!r.analysis)return;const{cells:o}=r.analysis,a=r.analysis.lastEdited||t.updated;Object.entries(o).forEach(([n,r])=>{s[n]||(s[n]=[]),s[n].push({serviceId:t.serviceId,name:t.name,content:r,timestamp:a})})}),s}(l,s),{editableCells:p}=n.parsed,h=_(t);p.forEach(({row:t,col:n,key:s})=>{const r=h[t];if(!r)return;const o=U(r)[n];if(!o)return;const a=function(t){const n=document.createElement("qd-student-entries");return n.entries=t,n}(u[s]||[]);a.setAttribute("data-qd-student-entries","true");const d=o.querySelector("[data-qd-student-entries]");d&&d.remove(),o.appendChild(a)}),o(`Displayed student entries for ${p.length} cells`)}function Tn(t){t.querySelectorAll("[data-qd-student-entries]").forEach(t=>t.remove()),o("Hidden student entries from analysis table")}Pn.styles=lt`
+ :host {
+ display: block;
+ margin-top: 12px;
+ padding-top: 8px;
+ border-top: 2px solid #3b82f6;
+ }
+
+ :host([data-empty]) {
+ margin-top: 0;
+ padding-top: 0;
+ border-top: none;
+ }
+
+ .qd-no-entries {
+ color: #9ca3af;
+ font-style: italic;
+ font-size: 13px;
+ padding: 8px 0;
+ }
+
+ .qd-entry {
+ padding: 8px 0;
+ border-bottom: 1px solid #e5e7eb;
+ font-size: 13px;
+ color: #1f2937;
+ }
+
+ .qd-entry-name {
+ font-weight: 600;
+ color: #374151;
+ }
+
+ .qd-entry-content {
+ white-space: pre-wrap;
+ }
+ `,An([cn({attribute:!1})],Pn.prototype,"entries",2),Pn=An([on("qd-student-entries")],Pn);const Ln=new WeakMap;function Nn(t,n){const s=En(t);s.errors&&s.errors.length>0&&a("Analysis table has validation errors:",s.errors);const r={parsed:s,interactive:n.interactive,pageId:n.pageId};if(n.interactive){if(!n.pageId)return a("Interactive mode requires pageId option"),!1;r.debouncer=new Debouncer,r.cellKeyMap=new Map}return Ln.set(t,r),n.interactive?function(t,n){const{parsed:s,pageId:r,debouncer:d,cellKeyMap:c}=n;if(!r||!d||!c)return a("Interactive mode requires pageId, debouncer, and cellKeyMap"),!1;if(!W(G.SESSION))return a("No active session found"),!1;const l=W(G.CACHE),u=l?.pages[r],p=u?.analysis,h=p?.cells||{},g=_(t);return s.editableCells.forEach(({row:t,col:s,key:r})=>{const o=g[t];if(!o)return;const d=U(o),l=d[s];if(!l)return;if(!qn(l))return void a(`Cell at R${t}C${s} is no longer editable`);c.set(l,r),h[r]&&(l.textContent=h[r]),l.contentEditable="true",F(l,"qd-editable"),l.setAttribute("role","textbox"),l.setAttribute("aria-multiline","true");const u=function(t,n){for(let s=n-1;s>=0;s--){const n=t[s];if(n&&!qn(n)&&n.textContent?.trim())return n}return}(d,s);u&&l.setAttribute("aria-labelledby",Ye(u,"qd-analysis-label")),l.addEventListener("input",()=>{kn(n,l,r)})}),F(t,"qd-analysis-interactive"),o(`Analysis table enhanced in interactive mode for page ${r}`),!0}(t,r):function(t){F(t,"qd-analysis-non-interactive");const n=()=>{const n=Ln.get(t);n&&On(t,n)},s=()=>{Tn(t)};return document.addEventListener("qd:instructor-show-answers",n),document.addEventListener("qd:instructor-hide-answers",s),o("Analysis table enhanced in non-interactive mode with instructor view support"),!0}(t)}function Dn(t,n,s={}){s.addInstructorClass&&t.classList.add("qd-quiz-instructor");t.querySelectorAll("td:nth-child(2), th:nth-child(2)").forEach(t=>{t.classList.remove("qd-hidden")});t.querySelectorAll("tbody td:nth-child(2)").forEach((t,s)=>{const r=n.parsed.questions[s];r&&t instanceof HTMLTableCellElement&&(t.textContent=r.correctAnswer)}),function(t){const n=t.querySelectorAll("thead th, thead td");n[2]&&V(n[2],"qd-hidden"),t.querySelectorAll("tbody tr").forEach(t=>{const n=t.querySelectorAll("td")[2];if(n){V(n,"qd-hidden");const t=pe.get(n);if(void 0!==t&&(n.innerHTML=t),E(n.textContent??""))try{n.textContent=P(n.textContent??"",A())}catch{}}})}(t),function(t){const n=me.get(t),s=t.querySelectorAll("thead th, thead td")[1];s&&void 0!==n&&(s.textContent=n)}(t),be(t,!1);const r=()=>{fn(t,n)};document.addEventListener("qd:instructor-show-answers",r),document.addEventListener("qd:instructor-hide-answers",()=>{bn(t)});"true"===sessionStorage.getItem(K)&&r()}function _n(t){const n=window.location.pathname.split(/[?#]/)[0]??"";return n.substring(n.lastIndexOf("/")+1).replace(/\.html?$/i,"")}function Rn(){const t=_n();if(!t)return void o("No pageId found, skipping table upgrade to interactive mode");if(ue())return void function(t){o("Instructor session detected, tables remain in non-interactive mode with answers visible");const n=document.querySelectorAll("table.qd-quiz");n.forEach(n=>{const s=wn(n);s&&(s.pageId=t,Dn(n,s))})}(t);const n=document.querySelectorAll("table.qd-quiz");n.length>0&&(o(`Upgrading ${n.length} quiz table(s) to interactive mode...`),n.forEach(n=>vn(n,{interactive:!0,pageId:t})));const s=document.querySelectorAll("table.qd-analysis");s.length>0&&(o(`Upgrading ${s.length} analysis table(s) to interactive mode...`),s.forEach(n=>Nn(n,{interactive:!0,pageId:t})))}class EventCoordinator{constructor(){this.listeners=new Map}initialize(){this.registerLoginHandlers(),this.registerLogoutHandlers(),this.registerAnswerHandlers(),this.registerStateHandlers(),this.registerInstructorHandlers(),this.registerDataHandlers(),o("Event coordinator initialized")}registerLoginHandlers(){this.addEventListener("qd:login",t=>{(async()=>{const n=t.detail;if(o(`Login event: ${n.serviceId} (${n.name})`),"INSTRUCTOR"===n.serviceId)return void o("Instructor login - skipping student record handling");const s=W(G.SESSION);s?(await Be().refreshCacheOnLogin(s),this.dispatchEvent("qd:cache-rebuild",{}),Rn()):o("No session found in storage, skipping cache rebuild")})()})}registerLogoutHandlers(){this.addEventListener("qd:logout",t=>{o(`Logout event: ${t.detail.serviceId}`);document.querySelectorAll("table.qd-quiz").forEach(t=>{!function(t){const n=yn.get(t);n&&(n.interactive=!1,n.pageId=void 0,n.inputs=void 0,n.cleanupInstructorListeners?.(),n.cleanupInstructorListeners=void 0,he(t),ge(t),V(t,"qd-quiz-interactive"),be(t,!0),o("Quiz table reset to non-interactive mode"))}(t)});document.querySelectorAll("table.qd-analysis").forEach(t=>{!function(t){const n=Ln.get(t);n&&(Tn(t),n.interactive&&(t.querySelectorAll(".qd-editable").forEach(t=>{t instanceof HTMLTableCellElement&&(t.contentEditable="false",t.classList.remove("qd-editable"),t.textContent="")}),t.classList.remove("qd-analysis-interactive"),n.debouncer?.cancelAll()),n.interactive=!1,n.pageId=void 0,n.debouncer=void 0,n.cellKeyMap=void 0,o("Reset analysis table to non-interactive mode"))}(t)}),this.dispatchEvent("qd:cache-clear",{})})}registerAnswerHandlers(){this.addEventListener("qd:answer-saved",t=>{const n=t.detail;o(`Answer saved: ${n.pageId} Q${n.questionIndex} = ${n.answer} (${n.success?"correct":"incorrect"})`),this.dispatchEvent("qd:cache-update",{pageId:n.pageId})})}registerStateHandlers(){this.addEventListener("qd:state-changed",t=>{const n=t.detail;o(`State changed: ${n.pageId} → ${n.state}`),this.dispatchEvent("qd:badge-update",{pageId:n.pageId,state:n.state})})}registerInstructorHandlers(){this.addEventListener("qd:instructor-unlock",t=>{o(`Instructor mode unlocked at ${t.detail.unlockTime}`)}),this.addEventListener("qd:instructor-lock",()=>{o("Instructor mode locked")})}registerDataHandlers(){this.addEventListener("qd:data-cleared",t=>{o(`All data cleared at ${t.detail.timestamp}`),this.dispatchEvent("qd:cache-clear",{})})}addEventListener(t,n){document.addEventListener(t,n);const s=this.listeners.get(t)||[];s.push(n),this.listeners.set(t,s)}dispatchEvent(t,n){const s=new CustomEvent(t,{detail:n,bubbles:!0,composed:!0});document.dispatchEvent(s)}cleanup(){for(const[t,n]of this.listeners)for(const s of n)document.removeEventListener(t,s);this.listeners.clear(),o("Event coordinator cleaned up")}}class SessionService{createSession(t,n,s){const r=new Date,a=r.toISOString(),d={serviceId:t,name:n,release:s,loginTime:a,lastActivity:a,expiresAt:new Date(r.getTime()+X).toISOString(),instructorUnlocked:!1};return this.saveSession(d),o(`Session created for ${t} (${n})`),this.emitEvent("qd:login",{serviceId:t,name:n,release:s,loginTime:a}),d}getSession(){try{const t=sessionStorage.getItem(G.SESSION);if(!t)return null;const n=JSON.parse(t);return n.serviceId&&n.release&&n.expiresAt?n:(d("Invalid session data, missing required fields"),null)}catch(t){return a("Failed to parse session data",t),null}}updateActivity(){const t=this.getSession();if(!t)return;const n=new Date;t.lastActivity=n.toISOString(),t.expiresAt=new Date(n.getTime()+X).toISOString(),this.saveSession(t)}isExpired(){const t=this.getSession();return!t||function(t,n=new Date){const s=new Date(t);return!!isNaN(s.getTime())||n>=s}(t.expiresAt)}clearSession(){const t=this.getSession();sessionStorage.removeItem(G.SESSION),sessionStorage.removeItem(G.CACHE),oe=!1,sessionStorage.removeItem(G.INSTRUCTOR),sessionStorage.removeItem(K),t&&(o(`Session cleared for ${t.serviceId}`),this.emitEvent("qd:logout",{serviceId:t.serviceId,timestamp:(new Date).toISOString()}))}unlockInstructor(){const t=this.getSession();t&&(t.instructorUnlocked=!0,t.unlockTime=(new Date).toISOString(),this.saveSession(t),o("Instructor mode unlocked"),this.emitEvent("qd:instructor-unlock",{timestamp:t.unlockTime}))}lockInstructor(){const t=this.getSession();t&&(t.instructorUnlocked=!1,delete t.unlockTime,this.saveSession(t),o("Instructor mode locked"),this.emitEvent("qd:instructor-lock",{timestamp:(new Date).toISOString()}))}isInstructorUnlocked(){const t=this.getSession();return!0===t?.instructorUnlocked}getCache(){try{const t=sessionStorage.getItem(G.CACHE);return t?JSON.parse(t):null}catch(t){return a("Failed to parse cache data",t),null}}saveCache(t){try{sessionStorage.setItem(G.CACHE,JSON.stringify(t))}catch(n){a("Failed to save cache",n)}}clearCache(){sessionStorage.removeItem(G.CACHE)}saveSession(t){try{sessionStorage.setItem(G.SESSION,JSON.stringify(t))}catch(n){a("Failed to save session",n)}}emitEvent(t,n){try{const s=new CustomEvent(t,{detail:n,bubbles:!0});document.dispatchEvent(s)}catch(s){a(`Failed to emit event ${t}`,s)}}}class SessionCoordinator{constructor(){this.activityTrackingActive=!1,this.handleLogin=()=>{const t=this.sessionService.getSession();t&&(this.scheduleExpiryCheck(t),this.setupActivityTracking())},this.sessionService=new SessionService}initialize(){const t=this.sessionService.getSession();if(t){if(o(`Existing session loaded for ${t.serviceId}`),this.sessionService.isExpired())return d("Session expired, clearing"),void this.expireSession();this.scheduleExpiryCheck(t),this.setupActivityTracking()}else o("No existing session found");document.addEventListener("qd:login",this.handleLogin)}expireSession(){sessionStorage.setItem(G.SESSION_EXPIRED,(new Date).toISOString()),document.dispatchEvent(new CustomEvent("qd:session-expired",{bubbles:!0,composed:!0})),this.sessionService.clearSession()}scheduleExpiryCheck(t){void 0!==this.expiryTimeoutId&&window.clearTimeout(this.expiryTimeoutId);const n=(new Date).getTime(),s=new Date(t.expiresAt).getTime()-n;s<=0?this.expireSession():this.expiryTimeoutId=window.setTimeout(()=>{o("Session expired (timeout)"),this.expireSession()},s)}setupActivityTracking(){if(this.activityTrackingActive)return;this.activityTrackingActive=!0;const t=()=>{if(!this.sessionService.getSession())return;this.sessionService.updateActivity();const t=this.sessionService.getSession();t&&this.scheduleExpiryCheck(t)};let n;const s=()=>{void 0!==n&&window.clearTimeout(n),n=window.setTimeout(()=>{t()},5e3)};["click","keydown","scroll","mousemove"].forEach(t=>{document.addEventListener(t,s,{passive:!0})})}cleanup(){void 0!==this.expiryTimeoutId&&window.clearTimeout(this.expiryTimeoutId),document.removeEventListener("qd:login",this.handleLogin)}getSessionService(){return this.sessionService}}const zn=lt`
+ :host {
+ display: none; /* Hidden if already logged in */
+ font-family:
+ system-ui,
+ -apple-system,
+ sans-serif;
+ }
+
+ :host([data-show]) {
+ display: block;
+ }
+
+ .login-container {
+ padding: 8px 12px;
+ background: #fff;
+ border: 1px solid #ddd;
+ border-radius: 4px;
+ /* Wide enough to keep every control on one row, including the PIN field
+ with its "PIN (4 digits)" placeholder. The surrounding header has
+ ample horizontal space; this panel was the constraint, not the page. */
+ max-width: 560px;
+ }
+
+ .title {
+ margin: 0 0 8px 0;
+ font-size: 15px;
+ font-weight: 600;
+ color: #333;
+ }
+
+ .login-form {
+ display: flex;
+ gap: 6px;
+ align-items: flex-start;
+ flex-wrap: wrap;
+ }
+
+ input {
+ padding: 6px 10px;
+ border: 1px solid #ccc;
+ border-radius: 4px;
+ font-size: 11px;
+ width: 110px;
+ min-width: 75px;
+ max-width: 110px;
+ }
+
+ input.pin-input {
+ /* Sized to show the "PIN (4 digits)" placeholder in full, so the field
+ states its own rule rather than reading as an unexplained box. */
+ width: 88px;
+ min-width: 88px;
+ max-width: 88px;
+ text-align: center;
+ letter-spacing: 1px;
+ }
+
+ input:focus {
+ outline: none;
+ border-color: #0066cc;
+ box-shadow: 0 0 0 2px rgba(0, 102, 204, 0.1);
+ }
+
+ input:disabled {
+ background-color: #f5f5f5;
+ cursor: not-allowed;
+ }
+
+ button {
+ padding: 6px 12px;
+ border: none;
+ border-radius: 4px;
+ font-size: 11px;
+ font-weight: 500;
+ cursor: pointer;
+ transition: all 0.2s;
+ white-space: nowrap;
+ }
+
+ .login-btn {
+ background: #0066cc;
+ color: white;
+ }
+
+ .login-btn:hover:not(:disabled) {
+ background: #0052a3;
+ }
+
+ .login-btn:disabled {
+ background: #ccc;
+ cursor: not-allowed;
+ }
+
+ .instructor-btn {
+ background: #6c757d;
+ color: white;
+ }
+
+ .instructor-btn:hover {
+ background: #5a6268;
+ }
+
+ /*
+ * Reserve the message row so typing does not make the header grow and
+ * shrink (SRD 013 UX-11)
+ */
+ .message-slot {
+ width: 100%;
+ min-height: 22px;
+ }
+
+ input[aria-invalid='true'] {
+ border-color: #d32f2f;
+ }
+
+ .hint-message {
+ width: 100%;
+ /* Padding inside the 100% width, so long messages stay in the panel */
+ box-sizing: border-box;
+ color: #555;
+ font-size: 11px;
+ margin-top: 3px;
+ padding: 4px 8px;
+ background: #f1f3f4;
+ border-radius: 3px;
+ border-left: 3px solid #9aa0a6;
+ }
+
+ .notice-message {
+ color: #0b4f8a;
+ font-size: 11px;
+ margin: 0 0 6px;
+ padding: 4px 8px;
+ background: #e8f1fb;
+ border-radius: 3px;
+ border-left: 3px solid #1d6fb8;
+ }
+
+ .error-message {
+ width: 100%;
+ /* Padding inside the 100% width, so long messages stay in the panel */
+ box-sizing: border-box;
+ color: #d32f2f;
+ font-size: 11px;
+ margin-top: 3px;
+ padding: 4px 8px;
+ background: #ffebee;
+ border-radius: 3px;
+ border-left: 3px solid #d32f2f;
+ }
+
+ .lockout-message {
+ width: 100%;
+ /* Padding inside the 100% width, so long messages stay in the panel */
+ box-sizing: border-box;
+ color: #f57c00;
+ font-size: 11px;
+ margin-top: 3px;
+ padding: 4px 8px;
+ background: #fff3e0;
+ border-radius: 3px;
+ border-left: 3px solid #f57c00;
+ }
+
+ /* Responsive */
+ @media (max-width: 600px) {
+ .login-form {
+ flex-direction: column;
+ }
+
+ input,
+ button {
+ width: 100%;
+ }
+`;function Mn(t){return t.schema<2}function Un(t){return Boolean(t.pinHash&&t.pinHash.length>0)}function jn(t){return Array.from(t).map(t=>t.toString(16).padStart(2,"0")).join("")}function Hn(t){const n=new Uint8Array(new ArrayBuffer(t.length/2));for(let s=0;st.toString(16).padStart(2,"0")).join("")}(t);return Qn(s,n)}(t,s)?{valid:!0,upgrade:await Fn(t)}:{valid:!1}}function Qn(t,n){if(t.length!==n.length)return!1;let s=0;for(let r=0;rr?{isLocked:!0,remainingMs:s-r}:(Yn(t),{isLocked:!1,remainingMs:0})}function Yn(t){const n=Wn(t);n&&n.attempts>0&&o(`Cleared ${n.attempts} failed PIN attempts for ${s(t)} on successful login`);const r=Kn(t);sessionStorage.removeItem(r)}function Xn(t){const n=Wn(t);if(!n)return Z;return Jn(t).isLocked?0:Math.max(0,Z-n.attempts)}class AuthService{loginStudent(t){return this.runLogin(t,{checkLock:!0,surfaceMigration:!0,errorMessage:"Login failed. Please try again.",errorLabel:"Student login error:"})}retryAfterMigration(t){return this.runLogin(t,{checkLock:!1,surfaceMigration:!1,errorMessage:"Login failed after migration. Please try again.",errorLabel:"Post-migration login error:"})}async isRegistered(t,n,s){try{const r=_e(s);await r.init();const o=await r.getStudent(n,t);return!!o&&(Un(o)&&!Mn(o))}catch{return null}}async runLogin(t,n){const{serviceId:r,name:a,pin:c,release:l,dbName:u}=t;if(n.checkLock){const t=Jn(r);if(t.isLocked)return{kind:"lockout",lockoutMs:t.remainingMs}}try{const t=_e(u);await t.init();const n=await t.getStudent(l,r);if(n){if(Mn(n)||!Un(n)){const s=function(t,n){return{...t,schema:3,...n,pinCreatedAt:(new Date).toISOString()}}(n,await Fn(c));return await t.saveStudent(s),{kind:"pin-created",serviceId:r,name:a,release:l}}const u=await Vn(c,n);if(!u.valid){const t=function(t){const n=(new Date).toISOString();let r=Wn(t);if(r||(r={serviceId:t,attempts:0,lockoutUntil:null,lastAttempt:n}),r.attempts+=1,r.lastAttempt=n,r.attempts>=Z){const n=new Date(Date.now()+ee);r.lockoutUntil=n.toISOString(),d(`PIN lockout triggered for ${s(t)} after ${r.attempts} failed attempts`)}else o(`Failed PIN attempt ${r.attempts}/${Z} for ${s(t)}`);const a=Kn(t);return sessionStorage.setItem(a,JSON.stringify(r)),r}(r);if(t.lockoutUntil){return{kind:"lockout",lockoutMs:new Date(t.lockoutUntil).getTime()-Date.now()}}return{kind:"bad-pin",remaining:Xn(r)}}return Yn(r),u.upgrade&&await t.saveStudent(function(t,n){return{...t,schema:3,...n}}(n,u.upgrade)),{kind:"pin-verified",serviceId:r,name:a,release:l}}const p=await Fn(c),h={schema:3,docId:"",release:l,serviceId:r,name:a,attempted:0,correct:0,updated:(new Date).toISOString(),pages:{},...p,pinCreatedAt:(new Date).toISOString()};return await t.saveStudent(h),{kind:"pin-created",serviceId:r,name:a,release:l}}catch(p){return n.surfaceMigration&&p instanceof StorageFormatError?{kind:"needs-migration",error:p}:(console.error(n.errorLabel,p),{kind:"error",message:n.errorMessage})}}}var Gn=Object.getOwnPropertyDescriptor;let Zn=class extends sn{render(){return Qt`
i
BrowserTest, from Deep Blue C Ltd
- Built ${"28/Nov/2025"}
+ Built ${"1/Nov/2026"}
- `}};je.styles=wt`
+ `}};Zn.styles=lt`
:host {
display: inline-block;
position: relative;
@@ -125,12 +381,12 @@ function we(t,n){return(n,s,o)=>((t,n,s)=>(s.configurable=!0,s.enumerable=!0,Ref
display: block;
line-height: 1.4;
}
- `,je=((t,n,s,o)=>{for(var r,a=o>1?void 0:o?Ue(n,s):n,c=t.length-1;c>=0;c--)(r=t[c])&&(a=r(a)||a);return a})([me("qd-build-info")],je);var Fe=Object.defineProperty,Be=Object.getOwnPropertyDescriptor,Ve=(t,n,s,o)=>{for(var r,a=o>1?void 0:o?Be(n,s):n,c=t.length-1;c>=0;c--)(r=t[c])&&(a=(o?r(n,s,a):r(a))||a);return o&&a&&Fe(n,s,a),a};const Qe="__qdModalCurrentRef__";function Ke(){return globalThis[Qe]??null}function Je(t){globalThis[Qe]=t}let We=class extends pe{constructor(){super(...arguments),this.open=!1,this.closable=!0,this.previouslyFocused=null,this.originalParent=null,this.originalNextSibling=null,this.isInBody=!1,this.handleKeyDown=t=>{"Escape"===t.key&&this.open&&this.closable&&(this.emitCloseEvent(),this.close())},this.handleBackdropClick=()=>{this.closable&&(this.emitCloseEvent(),this.close())},this.handleCloseClick=()=>{this.emitCloseEvent(),this.close()},this.stopPropagation=t=>{t.stopPropagation()}}connectedCallback(){super.connectedCallback(),document.addEventListener("keydown",this.handleKeyDown)}disconnectedCallback(){super.disconnectedCallback(),document.removeEventListener("keydown",this.handleKeyDown),Ke()!==this||this.isInBody||Je(null)}updated(t){t.has("open")&&(this.open?this.handleOpen():this.handleClose())}moveToBody(){this.isInBody||(this.originalParent=this.parentNode,this.originalNextSibling=this.nextSibling,this.isInBody=!0,document.body.appendChild(this))}restorePosition(){this.isInBody&&this.originalParent&&(this.originalNextSibling?this.originalParent.insertBefore(this,this.originalNextSibling):this.originalParent.appendChild(this),this.originalParent=null,this.originalNextSibling=null,this.isInBody=!1)}render(){return ne`
+ `,Zn=((t,n,s,r)=>{for(var o,a=r>1?void 0:r?Gn(n,s):n,d=t.length-1;d>=0;d--)(o=t[d])&&(a=o(a)||a);return a})([on("qd-build-info")],Zn);var es=Object.defineProperty,ts=Object.getOwnPropertyDescriptor,ns=(t,n,s,r)=>{for(var o,a=r>1?void 0:r?ts(n,s):n,d=t.length-1;d>=0;d--)(o=t[d])&&(a=(r?o(n,s,a):o(a))||a);return r&&a&&es(n,s,a),a};const ss="__qdModalCurrentRef__";function rs(){return globalThis[ss]??null}function os(t){globalThis[ss]=t}let is=class extends sn{constructor(){super(...arguments),this.open=!1,this.closable=!0,this.previouslyFocused=null,this.originalParent=null,this.originalNextSibling=null,this.isInBody=!1,this.handleKeyDown=t=>{"Escape"===t.key&&this.open&&this.closable&&(this.emitCloseEvent(),this.close())},this.handleBackdropClick=()=>{this.closable&&(this.emitCloseEvent(),this.close())},this.handleCloseClick=()=>{this.emitCloseEvent(),this.close()},this.stopPropagation=t=>{t.stopPropagation()}}connectedCallback(){super.connectedCallback(),document.addEventListener("keydown",this.handleKeyDown)}disconnectedCallback(){super.disconnectedCallback(),document.removeEventListener("keydown",this.handleKeyDown),rs()!==this||this.isInBody||os(null)}updated(t){t.has("open")&&(this.open?this.handleOpen():this.handleClose())}moveToBody(){this.isInBody||(this.originalParent=this.parentNode,this.originalNextSibling=this.nextSibling,this.isInBody=!0,document.body.appendChild(this))}restorePosition(){this.isInBody&&this.originalParent&&(this.originalNextSibling?this.originalParent.insertBefore(this,this.originalNextSibling):this.originalParent.appendChild(this),this.originalParent=null,this.originalNextSibling=null,this.isInBody=!1)}render(){return Qt`
- `}show(){this.open=!0}close(){this.open=!1}handleOpen(){const t=Ke();t&&t!==this&&t.close(),Je(this),this.previouslyFocused=document.activeElement,this.moveToBody(),requestAnimationFrame(()=>{this.focusFirstElement()})}handleClose(){Ke()===this&&Je(null),this.restorePosition(),this.previouslyFocused instanceof HTMLElement&&this.previouslyFocused.focus()}focusFirstElement(){const t=this.shadowRoot?.querySelector(".content");if(!t)return;const n=this.shadowRoot?.querySelector("slot:not([name])");if(n){const t=n.assignedElements({flatten:!0});for(const n of t){const t=n.querySelector('button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])');if(t)return void t.focus();if(n instanceof HTMLElement&&n.matches('button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])'))return void n.focus()}}const s=this.shadowRoot?.querySelector(".close-button");s&&s.focus()}emitCloseEvent(){const t=new CustomEvent("qd:modal-close",{bubbles:!0,composed:!0});this.dispatchEvent(t)}};We.styles=wt`
+ `}show(){this.open=!0}close(){this.open=!1}handleOpen(){const t=rs();t&&t!==this&&t.close(),os(this),this.previouslyFocused=document.activeElement,this.moveToBody(),requestAnimationFrame(()=>{this.focusFirstElement()})}handleClose(){rs()===this&&os(null),this.restorePosition(),this.previouslyFocused instanceof HTMLElement&&this.previouslyFocused.focus()}focusFirstElement(){const t=this.shadowRoot?.querySelector(".content");if(!t)return;const n=this.shadowRoot?.querySelector("slot:not([name])");if(n){const t=n.assignedElements({flatten:!0});for(const n of t){const t=n.querySelector('button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])');if(t)return void t.focus();if(n instanceof HTMLElement&&n.matches('button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])'))return void n.focus()}}const s=this.shadowRoot?.querySelector(".close-button");s&&s.focus()}emitCloseEvent(){const t=new CustomEvent("qd:modal-close",{bubbles:!0,composed:!0});this.dispatchEvent(t)}};is.styles=lt`
:host {
display: contents;
}
@@ -244,11 +500,11 @@ function we(t,n){return(n,s,o)=>((t,n,s)=>(s.configurable=!0,s.enumerable=!0,Ref
.body {
padding: 20px;
}
- `,Ve([ye({type:Boolean,reflect:!0})],We.prototype,"open",2),Ve([ye({type:Boolean})],We.prototype,"closable",2),We=Ve([me("qd-modal")],We);var Ye=Object.defineProperty,Ge=Object.getOwnPropertyDescriptor,Ze=(t,n,s,o)=>{for(var r,a=o>1?void 0:o?Ge(n,s):n,c=t.length-1;c>=0;c--)(r=t[c])&&(a=(o?r(n,s,a):r(a))||a);return o&&a&&Ye(n,s,a),a};let Xe=class extends pe{constructor(){super(...arguments),this.open=!1,this.title="Enter Password",this.error="",this.password="",this.handleModalClose=()=>{this.close()},this.handleInput=t=>{const n=t.target;this.password=n.value,this.error&&(this.error="")},this.handleSubmit=t=>{t.preventDefault(),this.password.trim()&&this.dispatchEvent(new CustomEvent("qd:password-submit",{detail:{password:this.password},bubbles:!0,composed:!0}))},this.handleCancel=()=>{this.close()}}show(){this.open=!0,this.password="",this.error=""}close(){this.open=!1,this.password="",this.error="",this.dispatchEvent(new CustomEvent("close",{bubbles:!0,composed:!0}))}updated(t){t.has("open")&&this.open&&(this.password="",this.updateComplete.then(()=>{this.passwordInput?.focus()}))}render(){return ne`
+ `,ns([cn({type:Boolean,reflect:!0})],is.prototype,"open",2),ns([cn({type:Boolean})],is.prototype,"closable",2),is=ns([on("qd-modal")],is);var as=Object.defineProperty,ds=Object.getOwnPropertyDescriptor,cs=(t,n,s,r)=>{for(var o,a=r>1?void 0:r?ds(n,s):n,d=t.length-1;d>=0;d--)(o=t[d])&&(a=(r?o(n,s,a):o(a))||a);return r&&a&&as(n,s,a),a};let ls=class extends sn{constructor(){super(...arguments),this.open=!1,this.title="Enter Password",this.error="",this.password="",this.handleModalClose=()=>{this.close()},this.handleInput=t=>{const n=t.target;this.password=n.value,this.error&&(this.error="")},this.handleSubmit=t=>{t.preventDefault(),this.password.trim()&&this.dispatchEvent(new CustomEvent("qd:password-submit",{detail:{password:this.password},bubbles:!0,composed:!0}))},this.handleCancel=()=>{this.close()}}show(){this.open=!0,this.password="",this.error=""}close(){this.open=!1,this.password="",this.error="",this.dispatchEvent(new CustomEvent("close",{bubbles:!0,composed:!0}))}updated(t){t.has("open")&&this.open&&(this.password="",this.updateComplete.then(()=>{this.passwordInput?.focus()}))}render(){return Qt`
${this.title}
- ${this.open?ne`
+ ${this.open?Qt`
- `:oe}
+ `:Wt}
- `}};Xe.styles=wt`
+ `}};ls.styles=lt`
:host {
display: contents;
}
@@ -354,98 +610,56 @@ function we(t,n){return(n,s,o)=>((t,n,s)=>(s.configurable=!0,s.enumerable=!0,Ref
button[type='button']:hover {
background: #d0d0d0;
}
- `,Ze([ye({type:Boolean,reflect:!0})],Xe.prototype,"open",2),Ze([ye({type:String})],Xe.prototype,"title",2),Ze([ye({type:String})],Xe.prototype,"error",2),Ze([ve()],Xe.prototype,"password",2),Ze([we('input[type="password"]')],Xe.prototype,"passwordInput",2),Xe=Ze([me("qd-password-modal")],Xe);
-/**
- * @license
- * Copyright 2017 Google LLC
- * SPDX-License-Identifier: BSD-3-Clause
- */
-const tn=2;class i{constructor(t){}get _$AU(){return this._$AM._$AU}_$AT(t,n,s){this._$Ct=t,this._$AM=n,this._$Ci=s}_$AS(t,n){return this.update(t,n)}update(t,n){return this.render(...n)}}
-/**
- * @license
- * Copyright 2017 Google LLC
- * SPDX-License-Identifier: BSD-3-Clause
- */class e extends i{constructor(t){if(super(t),this.it=oe,t.type!==tn)throw Error(this.constructor.directiveName+"() can only be used in child bindings")}render(t){if(t===oe||null==t)return this._t=void 0,this.it=t;if(t===se)return t;if("string"!=typeof t)throw Error(this.constructor.directiveName+"() called with a non-string value");if(t===this.it)return this._t;this.it=t;const n=[t];return n.raw=n,this._t={_$litType$:this.constructor.resultType,strings:n,values:[]}}}e.directiveName="unsafeHTML",e.resultType=1;const en=(t=>(...n)=>({_$litDirective$:t,values:n}))(e);var nn=Object.defineProperty,sn=Object.getOwnPropertyDescriptor,on=(t,n,s,o)=>{for(var r,a=o>1?void 0:o?sn(n,s):n,c=t.length-1;c>=0;c--)(r=t[c])&&(a=(o?r(n,s,a):r(a))||a);return o&&a&&nn(n,s,a),a};let rn=class extends pe{constructor(){super(...arguments),this.open=!1,this.title="Confirm",this.message="",this.confirmText="Confirm",this.cancelText="Cancel",this.destructive=!1,this.handleModalClose=()=>{this.close(),this.dispatchEvent(new CustomEvent("qd:cancel",{bubbles:!0,composed:!0}))},this.handleConfirm=()=>{this.close(),this.dispatchEvent(new CustomEvent("qd:confirm",{bubbles:!0,composed:!0}))},this.handleCancel=()=>{this.close(),this.dispatchEvent(new CustomEvent("qd:cancel",{bubbles:!0,composed:!0}))}}show(){this.open=!0}close(){this.open=!1}render(){return ne`
-
- ${this.title}
-
-
-
${en(this.message)}
-
-
-
- ${this.cancelText}
-
-
- ${this.confirmText}
-
-
-
-
- `}};rn.styles=wt`
- :host {
- display: contents;
- }
-
- .confirm-content {
- padding: 8px 0;
- }
-
- .message {
- font-size: 14px;
- color: #333;
- line-height: 1.5;
- margin-bottom: 24px;
- }
-
- .button-row {
- display: flex;
- gap: 8px;
- justify-content: flex-end;
- }
+ `,cs([cn({type:Boolean,reflect:!0})],ls.prototype,"open",2),cs([cn({type:String})],ls.prototype,"title",2),cs([cn({type:String})],ls.prototype,"error",2),cs([ln()],ls.prototype,"password",2),cs([un('input[type="password"]')],ls.prototype,"passwordInput",2),ls=cs([on("qd-password-modal")],ls);var us=Object.defineProperty,ps=Object.getOwnPropertyDescriptor,hs=(t,n,s,r)=>{for(var o,a=r>1?void 0:r?ps(n,s):n,d=t.length-1;d>=0;d--)(o=t[d])&&(a=(r?o(n,s,a):o(a))||a);return r&&a&&us(n,s,a),a};let gs=class extends sn{constructor(){super(...arguments),this.disabled=!1,this.showModal=!1,this.error="",this.rateLimiter=new RateLimiter(Z-1),this.open=()=>{this.showModal=!0,this.error=""},this.handleClose=()=>{this.showModal=!1,this.error=""},this.handleSubmit=t=>{this.login(t.detail.password)}}render(){return Qt`
+
+ Instructor
+
+
+ `}async login(t){if(se())if(this.rateLimiter.attempt())try{if(!(await ce(t)))return this.rateLimiter.recordFailure(),void(this.error="Incorrect password");this.rateLimiter.reset();const n=S();(new SessionService).createSession("INSTRUCTOR","Instructor",n||""),this.dispatchEvent(new CustomEvent("qd:login",{detail:{serviceId:"INSTRUCTOR",name:"Instructor",release:n||"",role:"instructor"},bubbles:!0,composed:!0})),this.showModal=!1,this.error=""}catch(n){this.error="Login failed. Please try again.",console.error("Instructor login error:",n)}else this.error=`Too many attempts. Try again in ${this.rateLimiter.getRemainingSeconds()}s`;else this.error="Instructor password not configured"}};gs.styles=lt`
button {
- padding: 8px 16px;
+ padding: 6px 12px;
border: none;
border-radius: 4px;
- font-size: 13px;
+ font-size: 11px;
font-weight: 500;
cursor: pointer;
- transition: background-color 0.2s;
- }
-
- .cancel-btn {
- background: #e0e0e0;
- color: #333;
- }
-
- .cancel-btn:hover {
- background: #d0d0d0;
- }
-
- .confirm-btn {
- background: #0066cc;
+ transition: all 0.2s;
+ white-space: nowrap;
+ background: #6c757d;
color: white;
}
- .confirm-btn:hover {
- background: #0052a3;
+ button:hover:not(:disabled) {
+ background: #5a6268;
}
- .confirm-btn.destructive {
- background: #d32f2f;
+ button:disabled {
+ cursor: not-allowed;
+ opacity: 0.7;
}
-
- .confirm-btn.destructive:hover {
- background: #b71c1c;
+ `,hs([cn({type:Boolean})],gs.prototype,"disabled",2),hs([ln()],gs.prototype,"showModal",2),hs([ln()],gs.prototype,"error",2),gs=hs([on("qd-instructor-login")],gs);var ms=Object.defineProperty,fs=Object.getOwnPropertyDescriptor,bs=(t,n,s,r)=>{for(var o,a=r>1?void 0:r?fs(n,s):n,d=t.length-1;d>=0;d--)(o=t[d])&&(a=(r?o(n,s,a):o(a))||a);return r&&a&&ms(n,s,a),a};let ys=class extends sn{constructor(){super(...arguments),this.untilMs=0,this.seconds=0,this.interval=null}disconnectedCallback(){super.disconnectedCallback(),this.clearTimer()}willUpdate(t){if(t.has("untilMs")){const t=this.untilMs-Date.now();this.seconds=t>0?Math.ceil(t/1e3):0}}updated(t){t.has("untilMs")&&this.startTimer()}render(){return this.seconds<=0?Qt``:Qt`
+ Too many attempts. Try again in ${this.seconds}s
+
`}startTimer(){this.clearTimer(),this.seconds<=0||(this.interval=window.setInterval(()=>{this.seconds--,this.seconds<=0&&(this.clearTimer(),this.dispatchEvent(new CustomEvent("qd:lockout-expired",{bubbles:!0,composed:!0})))},1e3))}clearTimer(){this.interval&&(clearInterval(this.interval),this.interval=null)}};ys.styles=lt`
+ .lockout-message {
+ width: 100%;
+ color: #f57c00;
+ font-size: 11px;
+ margin-top: 3px;
+ padding: 4px 8px;
+ background: #fff3e0;
+ border-radius: 3px;
+ border-left: 3px solid #f57c00;
}
- `,on([ye({type:Boolean,reflect:!0})],rn.prototype,"open",2),on([ye({type:String})],rn.prototype,"title",2),on([ye({type:String})],rn.prototype,"message",2),on([ye({type:String})],rn.prototype,"confirmText",2),on([ye({type:String})],rn.prototype,"cancelText",2),on([ye({type:Boolean})],rn.prototype,"destructive",2),rn=on([me("qd-confirm-dialog")],rn);var an=Object.defineProperty,cn=Object.getOwnPropertyDescriptor,dn=(t,n,s,o)=>{for(var r,a=o>1?void 0:o?cn(n,s):n,c=t.length-1;c>=0;c--)(r=t[c])&&(a=(o?r(n,s,a):r(a))||a);return o&&a&&an(n,s,a),a};let ln=class extends pe{constructor(){super(...arguments),this.panelType="login",this.handleClick=()=>{this.dispatchEvent(new CustomEvent("qd:help-open",{detail:{panelType:this.panelType},bubbles:!0,composed:!0}))}}render(){return ne`
+ `,bs([cn({type:Number})],ys.prototype,"untilMs",2),bs([ln()],ys.prototype,"seconds",2),ys=bs([on("qd-lockout-banner")],ys);var vs=Object.defineProperty,ws=Object.getOwnPropertyDescriptor,xs=(t,n,s,r)=>{for(var o,a=r>1?void 0:r?ws(n,s):n,d=t.length-1;d>=0;d--)(o=t[d])&&(a=(r?o(n,s,a):o(a))||a);return r&&a&&vs(n,s,a),a};let Ss=class extends sn{constructor(){super(...arguments),this.panelType="login",this.handleClick=()=>{this.dispatchEvent(new CustomEvent("qd:help-open",{detail:{panelType:this.panelType},bubbles:!0,composed:!0}))}}render(){return Qt`
?
- `}};ln.styles=wt`
+ `}};Ss.styles=lt`
:host {
display: inline-block;
}
@@ -483,13 +697,179 @@ const tn=2;class i{constructor(t){}get _$AU(){return this._$AM._$AU}_$AT(t,n,s){
.help-icon:active {
background: #004080;
}
- `,dn([ye({type:String})],ln.prototype,"panelType",2),ln=dn([me("qd-help-trigger")],ln);var un=Object.defineProperty,hn=Object.getOwnPropertyDescriptor,pn=(t,n,s,o)=>{for(var r,a=o>1?void 0:o?hn(n,s):n,c=t.length-1;c>=0;c--)(r=t[c])&&(a=(o?r(n,s,a):r(a))||a);return o&&a&&un(n,s,a),a};let gn=class extends pe{constructor(){super(...arguments),this.portalElement=null,this.previouslyFocused=null,this.open=!1,this.title="Help",this.content="",this._isOpen=!1,this.handleKeyDown=t=>{"Escape"===t.key&&this._isOpen&&this.close()},this.handleBackdropClick=()=>{this.close()},this.handleCloseClick=()=>{this.close()},this.stopPropagation=t=>{t.stopPropagation()}}connectedCallback(){super.connectedCallback(),document.addEventListener("keydown",this.handleKeyDown),this.ensureStyles()}disconnectedCallback(){super.disconnectedCallback(),document.removeEventListener("keydown",this.handleKeyDown),this.removePortal()}updated(t){t.has("open")&&(this.open&&!this._isOpen?this.handleOpen():!this.open&&this._isOpen&&this.handleClose())}ensureStyles(){gn.styleElement||(gn.styleElement=document.createElement("style"),gn.styleElement.textContent="\n.qd-help-backdrop{position:fixed;top:0;left:0;right:0;bottom:0;background:rgba(0,0,0,.5);display:flex;align-items:center;justify-content:center;z-index:99999;font-family:system-ui,-apple-system,sans-serif}\n.qd-help-content{background:#fff;border-radius:8px;box-shadow:0 4px 20px rgba(0,0,0,.3);max-width:450px;max-height:80vh;overflow:auto}\n.qd-help-header{display:flex;align-items:center;justify-content:space-between;padding:16px 20px;border-bottom:1px solid #eee}\n.qd-help-title{font-weight:600;font-size:18px;color:#333;margin:0}\n.qd-help-close{background:none;border:none;font-size:24px;color:#666;cursor:pointer;padding:0;line-height:1;width:28px;height:28px;display:flex;align-items:center;justify-content:center;border-radius:4px}\n.qd-help-close:hover{background:#f0f0f0;color:#333}\n.qd-help-close:focus{outline:2px solid #0066cc;outline-offset:2px}\n.qd-help-body{padding:20px;line-height:1.6;color:#444}\n.qd-help-body h3{margin-top:0;margin-bottom:12px;color:#333;font-size:16px}\n.qd-help-body p{margin:0 0 12px 0}\n.qd-help-body p:last-child{margin-bottom:0}\n.qd-help-body strong{color:#333}",document.head.appendChild(gn.styleElement))}createPortal(){this.removePortal(),this.portalElement=document.createElement("div"),this.portalElement.className="qd-help-backdrop",this.portalElement.addEventListener("click",this.handleBackdropClick);const t=document.createElement("div");t.className="qd-help-content",t.setAttribute("role","dialog"),t.setAttribute("aria-modal","true"),t.setAttribute("aria-labelledby","qd-help-title"),t.addEventListener("click",this.stopPropagation);const n=document.createElement("div");n.className="qd-help-header";const s=document.createElement("h2");s.className="qd-help-title",s.id="qd-help-title",s.textContent=this.title;const o=document.createElement("button");o.className="qd-help-close",o.setAttribute("aria-label","Close"),o.innerHTML="×",o.addEventListener("click",this.handleCloseClick),n.appendChild(s),n.appendChild(o);const r=document.createElement("div");r.className="qd-help-body",r.innerHTML=this.content,t.appendChild(n),t.appendChild(r),this.portalElement.appendChild(t),document.body.appendChild(this.portalElement),requestAnimationFrame(()=>{o.focus()})}removePortal(){this.portalElement&&(this.portalElement.remove(),this.portalElement=null)}handleOpen(){this._isOpen=!0,this.previouslyFocused=document.activeElement,this.createPortal()}handleClose(){this._isOpen=!1,this.removePortal(),this.previouslyFocused instanceof HTMLElement&&this.previouslyFocused.focus()}close(){this.open=!1,this.dispatchEvent(new CustomEvent("qd:modal-close",{bubbles:!0,composed:!0}))}render(){return oe}};gn.styleElement=null,pn([ye({type:Boolean,reflect:!0})],gn.prototype,"open",2),pn([ye({type:String})],gn.prototype,"title",2),pn([ye({type:String})],gn.prototype,"content",2),pn([ve()],gn.prototype,"_isOpen",2),gn=pn([me("qd-help-popup")],gn);const mn="students";async function fn(t,n,s){const o=performance.now(),c={migrated:0,skipped:0,errors:[],durationMs:0},{releaseId:d,dryRun:l=!1}=s,u=T(d),h=await async function(t){return new Promise((n,s)=>{const o=indexedDB.open(t);o.onsuccess=()=>n(o.result),o.onerror=()=>{r(`Failed to open database: ${o.error?.message}`),s(new Error(`Failed to open database: ${o.error?.message}`))}})}(t);try{const t=await async function(t){return new Promise((n,s)=>{const o=t.transaction(mn,"readonly").objectStore(mn).openCursor(),r=[];o.onsuccess=()=>{const t=o.result;if(t){const n="string"==typeof t.key?t.key:JSON.stringify(t.key);r.push({key:n,value:t.value}),t.continue()}else n(r)},o.onerror=()=>{s(new Error(`Failed to read records: ${o.error?.message}`))}})}(h);for(const{key:s,value:o}of t)try{const t=j(o);if("encrypt"===n){if(t){c.skipped++;continue}const n=D(o,u);l||await bn(h,s,n),c.migrated++}else{if(!t){c.skipped++;continue}const n=U(o,u);l||await bn(h,s,n),c.migrated++}}catch(p){const t=p instanceof Error?p.message:String(p);c.errors.push({key:s,error:t}),a(`Migration error for key ${s}: ${t}`)}}finally{h.close()}return c.durationMs=performance.now()-o,c.migrated,c.skipped,c.errors.length,c.durationMs.toFixed(2),c}async function bn(t,n,s){return new Promise((o,r)=>{const a=t.transaction(mn,"readwrite").objectStore(mn).put(s,n);a.onsuccess=()=>o(),a.onerror=()=>{r(new Error(`Failed to save record: ${a.error?.message}`))}})}var yn=Object.defineProperty,vn=Object.getOwnPropertyDescriptor,wn=(t,n,s,o)=>{for(var r,a=o>1?void 0:o?vn(n,s):n,c=t.length-1;c>=0;c--)(r=t[c])&&(a=(o?r(n,s,a):r(a))||a);return o&&a&&yn(n,s,a),a};let Sn=class extends pe{constructor(){super(...arguments),this.open=!1,this.expected="plain",this.found="plain",this.dbName="",this.releaseId="",this.dialogState="password",this.password="",this.error="",this.migrationResult=null,this.handleModalClose=()=>{this.dispatchEvent(new CustomEvent("qd:migration-cancel",{bubbles:!0,composed:!0}))},this.handleInput=t=>{const n=t.target;this.password=n.value,this.error&&(this.error="")},this.handleSubmit=async t=>{if(t.preventDefault(),!this.password.trim())return;await this.validatePassword(this.password)?await this.runMigration():this.error||(this.error="Incorrect instructor password")},this.handleContinue=()=>{this.dispatchEvent(new CustomEvent("qd:migration-complete",{detail:this.migrationResult,bubbles:!0,composed:!0}))},this.handleCancel=()=>{this.dispatchEvent(new CustomEvent("qd:migration-cancel",{bubbles:!0,composed:!0}))}}updated(t){t.has("open")&&this.open&&(this.dialogState="password",this.password="",this.error="",this.migrationResult=null,this.updateComplete.then(()=>{this.passwordInput?.focus()}))}async validatePassword(t){const n=document.getElementById(Ie),s=n?.textContent?.trim();if(!s)return this.error="Instructor password not configured",!1;const o=(new TextEncoder).encode(t),r=await crypto.subtle.digest("SHA-256",o);return Array.from(new Uint8Array(r)).map(t=>t.toString(16).padStart(2,"0")).join("").substring(0,12)===s}async runMigration(){this.dialogState="migrating",this.error="";try{const t="decrypt",n=await fn(this.dbName,t,{releaseId:this.releaseId,dryRun:!1});if(n.errors.length>0)return this.dialogState="error",void(this.error=`Migration completed with ${n.errors.length} error(s). Some records may not have been migrated.`);this.migrationResult={migrated:n.migrated,skipped:n.skipped},this.dialogState="success"}catch(t){this.dialogState="error",this.error=`Migration failed: ${t instanceof Error?t.message:"Unknown error"}`}}render(){return ne`
+ `,xs([cn({type:String})],Ss.prototype,"panelType",2),Ss=xs([on("qd-help-trigger")],Ss);var $s=Object.defineProperty,qs=Object.getOwnPropertyDescriptor,Es=(t,n,s,r)=>{for(var o,a=r>1?void 0:r?qs(n,s):n,d=t.length-1;d>=0;d--)(o=t[d])&&(a=(r?o(n,s,a):o(a))||a);return r&&a&&$s(n,s,a),a};let ks=class extends sn{constructor(){super(...arguments),this.portalElement=null,this.previouslyFocused=null,this.open=!1,this.title="Help",this.content="",this._isOpen=!1,this.handleKeyDown=t=>{"Escape"===t.key&&this._isOpen&&this.close()},this.handleBackdropClick=()=>{this.close()},this.handleCloseClick=()=>{this.close()},this.stopPropagation=t=>{t.stopPropagation()}}connectedCallback(){super.connectedCallback(),document.addEventListener("keydown",this.handleKeyDown),this.ensureStyles()}disconnectedCallback(){super.disconnectedCallback(),document.removeEventListener("keydown",this.handleKeyDown),this.removePortal()}updated(t){t.has("open")&&(this.open&&!this._isOpen?this.handleOpen():!this.open&&this._isOpen&&this.handleClose())}ensureStyles(){ks.styleElement||(ks.styleElement=document.createElement("style"),ks.styleElement.textContent="\n.qd-help-backdrop{position:fixed;top:0;left:0;right:0;bottom:0;background:rgba(0,0,0,.5);display:flex;align-items:center;justify-content:center;z-index:99999;font-family:system-ui,-apple-system,sans-serif}\n.qd-help-content{background:#fff;border-radius:8px;box-shadow:0 4px 20px rgba(0,0,0,.3);max-width:450px;max-height:80vh;overflow:auto}\n.qd-help-header{display:flex;align-items:center;justify-content:space-between;padding:16px 20px;border-bottom:1px solid #eee}\n.qd-help-title{font-weight:600;font-size:18px;color:#333;margin:0}\n.qd-help-close{background:none;border:none;font-size:24px;color:#666;cursor:pointer;padding:0;line-height:1;width:28px;height:28px;display:flex;align-items:center;justify-content:center;border-radius:4px}\n.qd-help-close:hover{background:#f0f0f0;color:#333}\n.qd-help-close:focus{outline:2px solid #0066cc;outline-offset:2px}\n.qd-help-body{padding:20px;line-height:1.6;color:#444}\n.qd-help-body h3{margin-top:0;margin-bottom:12px;color:#333;font-size:16px}\n.qd-help-body p{margin:0 0 12px 0}\n.qd-help-body p:last-child{margin-bottom:0}\n.qd-help-body strong{color:#333}",document.head.appendChild(ks.styleElement))}createPortal(){this.removePortal(),this.portalElement=document.createElement("div"),this.portalElement.className="qd-help-backdrop",this.portalElement.addEventListener("click",this.handleBackdropClick);const t=document.createElement("div");t.className="qd-help-content",t.setAttribute("role","dialog"),t.setAttribute("aria-modal","true"),t.setAttribute("aria-labelledby","qd-help-title"),t.addEventListener("click",this.stopPropagation);const n=document.createElement("div");n.className="qd-help-header";const s=document.createElement("h2");s.className="qd-help-title",s.id="qd-help-title",s.textContent=this.title;const r=document.createElement("button");r.className="qd-help-close",r.setAttribute("aria-label","Close"),r.innerHTML="×",r.addEventListener("click",this.handleCloseClick),n.appendChild(s),n.appendChild(r);const o=document.createElement("div");o.className="qd-help-body",o.innerHTML=this.content,t.appendChild(n),t.appendChild(o),this.portalElement.appendChild(t),document.body.appendChild(this.portalElement),requestAnimationFrame(()=>{r.focus()})}removePortal(){this.portalElement&&(this.portalElement.remove(),this.portalElement=null)}handleOpen(){this._isOpen=!0,this.previouslyFocused=document.activeElement,this.createPortal()}handleClose(){this._isOpen=!1,this.removePortal(),this.previouslyFocused instanceof HTMLElement&&this.previouslyFocused.focus()}close(){this.open=!1,this.dispatchEvent(new CustomEvent("qd:modal-close",{bubbles:!0,composed:!0}))}render(){return Wt}};ks.styleElement=null,Es([cn({type:Boolean,reflect:!0})],ks.prototype,"open",2),Es([cn({type:String})],ks.prototype,"title",2),Es([cn({type:String})],ks.prototype,"content",2),Es([ln()],ks.prototype,"_isOpen",2),ks=Es([on("qd-help-popup")],ks);const Cs="students";async function Is(t,n,s){const r=performance.now(),c={migrated:0,skipped:0,errors:[],durationMs:0},{releaseId:l,dryRun:u=!1}=s,p=Ie(l);o(`Starting obfuscation migration: direction=${n}, dryRun=${u}`);const h=await async function(t){return new Promise((n,s)=>{const r=indexedDB.open(t);r.onsuccess=()=>n(r.result),r.onerror=()=>{a(`Failed to open database: ${r.error?.message}`),s(new Error(`Failed to open database: ${r.error?.message}`))}})}(t);try{const t=await async function(t){return new Promise((n,s)=>{const r=t.transaction(Cs,"readonly").objectStore(Cs).openCursor(),o=[];r.onsuccess=()=>{const t=r.result;if(t){const n="string"==typeof t.key?t.key:JSON.stringify(t.key);o.push({key:n,value:t.value}),t.continue()}else n(o)},r.onerror=()=>{s(new Error(`Failed to read records: ${r.error?.message}`))}})}(h);for(const{key:s,value:r}of t)try{const t=Le(r);if("encrypt"===n){if(t){c.skipped++;continue}const n=Oe(r,p);u||await As(h,s,n),c.migrated++}else{if(!t){c.skipped++;continue}const n=Te(r,p);u||await As(h,s,n),c.migrated++}}catch(g){const t=g instanceof Error?g.message:String(g);c.errors.push({key:s,error:t}),d(`Migration error for key ${s}: ${t}`)}}finally{h.close()}return c.durationMs=performance.now()-r,o(`Migration complete: migrated=${c.migrated}, skipped=${c.skipped}, errors=${c.errors.length}, duration=${c.durationMs.toFixed(2)}ms`),c}async function As(t,n,s){return new Promise((r,o)=>{const a=t.transaction(Cs,"readwrite").objectStore(Cs).put(s,n);a.onsuccess=()=>r(),a.onerror=()=>{o(new Error(`Failed to save record: ${a.error?.message}`))}})}const Ps=lt`
+ :host {
+ display: contents;
+ }
+
+ .migration-content {
+ display: flex;
+ flex-direction: column;
+ gap: 16px;
+ padding: 8px 0;
+ }
+
+ .warning-banner {
+ display: flex;
+ align-items: flex-start;
+ gap: 12px;
+ padding: 12px;
+ background: #fff3cd;
+ border-radius: 4px;
+ border-left: 4px solid #ffc107;
+ }
+
+ .warning-icon {
+ font-size: 20px;
+ line-height: 1;
+ }
+
+ .warning-text {
+ flex: 1;
+ }
+
+ .warning-text strong {
+ display: block;
+ margin-bottom: 4px;
+ color: #856404;
+ }
+
+ .format-info {
+ font-size: 13px;
+ color: #666;
+ }
+
+ .format-row {
+ display: flex;
+ gap: 8px;
+ margin: 4px 0;
+ }
+
+ .format-label {
+ font-weight: 500;
+ min-width: 100px;
+ }
+
+ .format-value {
+ font-family: monospace;
+ background: #f5f5f5;
+ padding: 2px 6px;
+ border-radius: 3px;
+ }
+
+ .form-field {
+ display: flex;
+ flex-direction: column;
+ gap: 4px;
+ }
+
+ label {
+ font-size: 13px;
+ font-weight: 500;
+ color: #333;
+ }
+
+ input[type='password'] {
+ padding: 8px 12px;
+ border: 1px solid #ccc;
+ border-radius: 4px;
+ font-size: 14px;
+ width: 100%;
+ box-sizing: border-box;
+ }
+
+ input[type='password']:focus {
+ outline: none;
+ border-color: #0066cc;
+ box-shadow: 0 0 0 2px rgba(0, 102, 204, 0.1);
+ }
+
+ .error-message {
+ color: #d32f2f;
+ font-size: 12px;
+ padding: 8px;
+ background: #ffebee;
+ border-radius: 4px;
+ border-left: 3px solid #d32f2f;
+ }
+
+ .success-message {
+ color: #2e7d32;
+ font-size: 13px;
+ padding: 12px;
+ background: #e8f5e9;
+ border-radius: 4px;
+ border-left: 3px solid #4caf50;
+ }
+
+ .migrating-state {
+ text-align: center;
+ padding: 20px;
+ }
+
+ .button-row {
+ display: flex;
+ gap: 8px;
+ justify-content: flex-end;
+ margin-top: 8px;
+ }
+
+ button {
+ padding: 8px 16px;
+ border: none;
+ border-radius: 4px;
+ font-size: 13px;
+ font-weight: 500;
+ cursor: pointer;
+ transition: background-color 0.2s;
+ }
+
+ button:disabled {
+ opacity: 0.6;
+ cursor: not-allowed;
+ }
+
+ button.primary {
+ background: #0066cc;
+ color: white;
+ }
+
+ button.primary:hover:not(:disabled) {
+ background: #0052a3;
+ }
+
+ button.secondary {
+ background: #e0e0e0;
+ color: #333;
+ }
+
+ button.secondary:hover:not(:disabled) {
+ background: #d0d0d0;
+ }
+`,Os=lt`
+ .spinner {
+ display: inline-block;
+ width: 24px;
+ height: 24px;
+ border: 3px solid #e0e0e0;
+ border-top-color: #0066cc;
+ border-radius: 50%;
+ animation: spin 1s linear infinite;
+ margin-bottom: 12px;
+ }
+
+ @keyframes spin {
+ to {
+ transform: rotate(360deg);
+ }
+ }
+`;var Ts=Object.getOwnPropertyDescriptor;let Ls=class extends sn{render(){return Qt`
`}};Ls.styles=[Os],Ls=((t,n,s,r)=>{for(var o,a=r>1?void 0:r?Ts(n,s):n,d=t.length-1;d>=0;d--)(o=t[d])&&(a=o(a)||a);return a})([on("qd-spinner")],Ls);var Ns=Object.defineProperty,Ds=Object.getOwnPropertyDescriptor,_s=(t,n,s,r)=>{for(var o,a=r>1?void 0:r?Ds(n,s):n,d=t.length-1;d>=0;d--)(o=t[d])&&(a=(r?o(n,s,a):o(a))||a);return r&&a&&Ns(n,s,a),a};let Rs=class extends sn{constructor(){super(...arguments),this.open=!1,this.expected="plain",this.found="plain",this.dbName="",this.releaseId="",this.dialogState="password",this.password="",this.error="",this.migrationResult=null,this.handleModalClose=()=>{this.dispatchEvent(new CustomEvent("qd:migration-cancel",{bubbles:!0,composed:!0}))},this.handleInput=t=>{const n=t.target;this.password=n.value,this.error&&(this.error="")},this.handleSubmit=async t=>{if(t.preventDefault(),!this.password.trim())return;await this.validatePassword(this.password)?await this.runMigration():this.error||(this.error="Incorrect instructor password")},this.handleContinue=()=>{this.dispatchEvent(new CustomEvent("qd:migration-complete",{detail:this.migrationResult,bubbles:!0,composed:!0}))},this.handleCancel=()=>{this.dispatchEvent(new CustomEvent("qd:migration-cancel",{bubbles:!0,composed:!0}))}}updated(t){t.has("open")&&this.open&&(this.dialogState="password",this.password="",this.error="",this.migrationResult=null,this.updateComplete.then(()=>{this.passwordInput?.focus()}))}async validatePassword(t){return se()?re(t):(this.error="Instructor password not configured",!1)}async runMigration(){this.dialogState="migrating",this.error="";try{const t="encrypt",n=await Is(this.dbName,t,{releaseId:this.releaseId,dryRun:!1});if(n.errors.length>0)return this.dialogState="error",void(this.error=`Migration completed with ${n.errors.length} error(s). Some records may not have been migrated.`);this.migrationResult={migrated:n.migrated,skipped:n.skipped},this.dialogState="success"}catch(t){this.dialogState="error",this.error=`Migration failed: ${t instanceof Error?t.message:"Unknown error"}`}}render(){return Qt`
Database Migration Required
- ${this.open?this.renderContent():oe}
+ ${this.open?this.renderContent():Wt}
- `}renderContent(){switch(this.dialogState){case"password":return this.renderPasswordForm();case"migrating":return this.renderMigrating();case"error":return this.renderError();case"success":return this.renderSuccess()}}renderPasswordForm(){return ne`
+ `}renderContent(){switch(this.dialogState){case"password":return this.renderPasswordForm();case"migrating":return this.renderMigrating();case"error":return this.renderError();case"success":return this.renderSuccess()}}renderPasswordForm(){return Qt`
⚠
@@ -524,7 +904,7 @@ const tn=2;class i{constructor(t){}get _$AU(){return this._$AM._$AU}_$AT(t,n,s){
/>
- ${this.error?ne`
${this.error}
`:oe}
+ ${this.error?Qt`
${this.error}
`:Wt}
Cancel
@@ -532,15 +912,15 @@ const tn=2;class i{constructor(t){}get _$AU(){return this._$AM._$AU}_$AT(t,n,s){
- `}renderMigrating(){return ne`
+ `}renderMigrating(){return Qt`
-
+
Migrating database records...
Please wait, do not close this window.
- `}renderError(){return ne`
+ `}renderError(){return Qt`
${this.error}
@@ -551,7 +931,7 @@ const tn=2;class i{constructor(t){}get _$AU(){return this._$AM._$AU}_$AT(t,n,s){
- `}renderSuccess(){return ne`
+ `}renderSuccess(){return Qt`