From deab2fb9813538c249426748d436b54156cb1b87 Mon Sep 17 00:00:00 2001 From: Mark Tyers Date: Thu, 4 Jun 2020 15:09:56 +0100 Subject: [PATCH] handles custom max score --- .DS_Store | Bin 6148 -> 6148 bytes .gitignore | 3 ++ README.md | 1 - css/style.css | 13 ++++++- imsmanifest.xml | 1 + index.html | 18 ++++++--- js/scormfunctions.js | 85 +------------------------------------------ js/script.js | 25 ++++++++++--- scormData.json | 81 +++++++++++++++++++++++++++++++++++++++++ template 1.9.zip | Bin 16127 -> 0 bytes 10 files changed, 131 insertions(+), 96 deletions(-) create mode 100644 .gitignore create mode 100644 scormData.json delete mode 100644 template 1.9.zip diff --git a/.DS_Store b/.DS_Store index be5e202f0e29a05259b9665a48d42abb9282b8f0..cad951e317a80f23853dac2300f4dcf6d0cd0f1d 100644 GIT binary patch delta 67 zcmZoMXffCj%(A(YWeJlmA44%iGDAK?5koG63qvA92@vZsWC2C<8S*@H@{^Nt@{<@C T7zBWL + diff --git a/index.html b/index.html index c2874dd..0d9c5fc 100644 --- a/index.html +++ b/index.html @@ -14,10 +14,18 @@ -

Simple SCORM Template

-

your name here...

- - - +
+

Simple SCORM Template

+
+
+

your name here...

+

+

+
+ + + +
+
diff --git a/js/scormfunctions.js b/js/scormfunctions.js index 21c03c9..ecd72f7 100755 --- a/js/scormfunctions.js +++ b/js/scormfunctions.js @@ -16,89 +16,6 @@ some basic SCORM error handling. //Include the standard ADL-supplied API discovery algorithm -// list of writable SCORM data values: - -const scormData = { - cmi: { - core: { - student_id: { - readable: true, - description: 'student username', - datatype: 'string' - }, - student_name: { - readable: true, - description: 'student name', - datatype: 'string' - }, - lesson_location: { - readable: true, - writable: true, - description: 'bookmark', - datatype: 'string' - }, - credit: { - readable: true, - description: 'bookmark', - datatype: 'string', - options: [ - 'credit', - 'no-credit' - ] - }, - lesson_status: { - readable: true, - writable: true, - description: 'bookmark', - datatype: 'string', - options: [ - 'passed', - 'completed', - 'failed', - 'incomplete', - 'browsed', - 'not attempted' - ] - }, - entry: { - readable: true, - description: 'Asserts whether the learner has previously accessed the SCO', - datatype: 'string', - options: [ - 'ab-initio', - 'resume', - '' - ] - }, - score: { - raw: { - readable: true, - writable: true, - description: 'the performance of the learner', - datatype: 'number' - }, - max: { - readable: true, - writable: true, - description: 'Maximum value in the range for the raw score', - datatype: 'number' - }, - min: { - readable: true, - writable: true, - description: 'Minimum value in the range for the raw score', - datatype: 'number' - } - }, - total_time: { - readable: true, - description: 'Sum of all of the learner’s session times accumulated in the current learner attempt', - datatype: 'CMITimespan' - } - } - } -} - /////////////////////////////////////////// //Begin ADL-provided API discovery algorithm /////////////////////////////////////////// @@ -129,7 +46,7 @@ function findAPI(win) { } /** - * open the API and return it. + * open the API and return */ function getAPI() { // start by looking for the API in the current window diff --git a/js/script.js b/js/script.js index eca9224..6b855ce 100644 --- a/js/script.js +++ b/js/script.js @@ -1,4 +1,7 @@ +const MIN_SCORE = 0 +let MAX_SCORE = 100 + document.addEventListener('DOMContentLoaded', event => { console.log('DOM CONTENT LOADED') document.querySelector('button#update').addEventListener('click', event => { @@ -8,6 +11,7 @@ document.addEventListener('DOMContentLoaded', event => { }) document.querySelector('button#end').addEventListener('click', event => { console.log('END BUTTON CLICKED') + document.querySelector('section').hidden = true endGame(parseInt(document.querySelector('input').value)) ScormProcessFinish() }) @@ -16,18 +20,26 @@ document.addEventListener('DOMContentLoaded', event => { function updateScore(score) { score = parseInt(score) console.log(`score: ${score}`) - ScormProcessSetValue('cmi.core.score.raw', parseInt(score)) + const percent = (score / MAX_SCORE) * 100 + if(!isNaN(score) && score >= MIN_SCORE && score <= MAX_SCORE) { // is the score a number in a valid range + ScormProcessSetValue('cmi.core.score.raw', percent) + } else { + console.warn(`invalid score (${score}): needs to be a number between ${MIN_SCORE} and ${MAX_SCORE}`) + } + return percent } function endGame(score) { - updateScore(score) + const percent = updateScore(score) + document.querySelector('h2').innerText = `${percent}%` // set the status const mastery = parseInt(ScormProcessGetValue('cmi.student_data.mastery_score')) console.log(`mastery: (${mastery})`) if(!isNaN(mastery)) { // NaN if mastery score not provided by the API - const status = parseInt(score) >= mastery ? 'passed' : 'failed' // if mastery score then pass/fail + const status = percent >= mastery ? 'passed' : 'failed' // if mastery score then pass/fail console.log(`status: (${status})`) ScormProcessSetValue('cmi.core.lesson_status', status) + document.querySelector('h3').innerText = `status: ${status.toUpperCase()}` } else { console.log('status is: (completed)') ScormProcessSetValue('cmi.core.lesson_status', 'completed') // if no mastery then completed @@ -47,8 +59,11 @@ window.addEventListener('load', event => { document.querySelector('p').innerText = name const username = ScormProcessGetValue('cmi.core.student_id') console.log(`student ID: ${username}`) - ScormProcessSetValue('cmi.core.score.min', 0) - ScormProcessSetValue('cmi.core.score.max', 100) + const maxScore = parseInt(ScormProcessGetValue('cmi.core.score.max')) // does the LMS provide a max score? + console.log(`moodle max score: (${maxScore})`) + ScormProcessSetValue('cmi.core.score.min', MIN_SCORE) + MAX_SCORE = !isNaN(maxScore) ? maxScore : MAX_SCORE + ScormProcessSetValue('cmi.core.score.max', MAX_SCORE) }) window.addEventListener('beforeunload', event => { diff --git a/scormData.json b/scormData.json new file mode 100644 index 0000000..e3ebc98 --- /dev/null +++ b/scormData.json @@ -0,0 +1,81 @@ + +{ + "cmi": { + "core": { + "student_id": { + "readable": true, + "description": "student username", + "datatype": "string" + }, + "student_name": { + "readable": true, + "description": "student name", + "datatype": "string" + }, + "lesson_location": { + "readable": true, + "writable": true, + "description": "bookmark", + "datatype": "string" + }, + "credit": { + "readable": true, + "description": "bookmark", + "datatype": "string", + "options": [ + "credit", + "no-credit" + ] + }, + "lesson_status": { + "readable": true, + "writable": true, + "description": "bookmark", + "datatype": "string", + "options": [ + "passed", + "completed", + "failed", + "incomplete", + "browsed", + "not attempted" + ] + }, + "entry": { + "readable": true, + "description": "Asserts whether the learner has previously accessed the SCO", + "datatype": "string", + "options": [ + "ab-initio", + "resume", + "" + ] + }, + "score": { + "raw": { + "readable": true, + "writable": true, + "description": "the performance of the learner", + "datatype": "number" + }, + "max": { + "readable": true, + "writable": true, + "description": "Maximum value in the range for the raw score", + "datatype": "number" + }, + "min": { + "readable": true, + "writable": true, + "description": "Minimum value in the range for the raw score", + "datatype": "number" + } + }, + "total_time": { + "readable": true, + "description": "Sum of all of the learner’s session times accumulated in the current learner attempt", + "datatype": "CMITimespan" + } + } + } +} diff --git a/template 1.9.zip b/template 1.9.zip deleted file mode 100644 index 5c6549b792fbaec7e0d04a2c00ee075cdc94b0a6..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 16127 zcmdtJbyS_(u0FhQ*CNH;N{c%bcXxLwt_vvc-s0}=#kIIoq_{g2m*U0CkG1#dX;1Gy z=kD`;|J^X&@v;VxIg^=$Cz*-76a*v&0QTuCQAu1J@DEBu0|3wg!~g?hDtQqFg!DRyid+r z*ko`f_%T>~>l3Rbd z-?CbVnoNG{<$SVtac=B4!vYyZroN^kzQNu3(G7m%#*tRdjESa(jzbHGCXvS>i2~;) z|AP1QfkmcpZ$0i^xVQV!feg8;53C$G(Pz_|dGaKgZ9Sy0zVQ&M8?K3DX*wPRk>e45 z9P;#?X5$urxlgxB>Q?*@6L}`@;aYI}D>=p=2?7L1S4;DW@QJ+7H9pE&vWAm~rn?b@ zLOsG5_ji%UzvOcLq=JWXWE2(sk#S`D1xWyG(rtF+Hu?+)+;o@JoRObUQ$b~ki5k+$ zgG1=|O+I;sboXtK!IwZ;mavcVA$4x|f`K>5^qif8hq|MmH9w!H;=|hq^>iV;b3-ZK znY;BY^-kW;SWx%z=?{=B;ZFd_+965?G=z!zbVn5~EQ0riM|2KM2;nLOhQ2DoBJ{8A zg0Fyz>?%?;3G6eEf$ymy*Y1!Vr&BdROq2EMqX743v7L2>SA45xmi5kp{c-qcp+wq8 z*$)>u_Buh{QPIB8F}^8J1KpRw(?7#5iJupN9lO1v0zX3~UT<-JW~13Kh>K%K1JR(n zg!H>&|6%nk4!aM-rk=BR%R3XPzh zIqqRBkJxup>+#LXIMaviqKh1f+uU4rv@^5o=yRg4-yCB$lY>kXgLBi^CO9v&fMO|FvZsO2V-UC?nH8&#p% z#=@P`_wuSNT~p0{3TBc#mz;SJ0Q+E#zaO(=Z(ZhMtu$l5(3gzvKoy#0za!tw0r6~c z_^3S{S81urFv6-Qs-Z>v8}okWI*f-9-r2&6#Fxtf-mV7uP~?V_&zct=^nx(LrXKfsWVXx2*}(@Kd5M z?62>5N47}KR#v!9h~%YUVBhj}Ws*Sv03b(`|4SF-c=9NMCl}PylMxV-Q&Oj=)B9h% z@n&%DcW*S($22n4H`G6okybrHQ4Nvz0grkPL4}IP$iPqyW=@&nAAzJrQxefj+9pC(Hy&EQ;-TECkHk=$4ORu zmg*^1MxgU_oujk;3@ea{g&hcFvvYQ0)(7bV238kqN3-fFlz=qwr*1$|=>xD^9`yb} zkWacX0s`s(2uQX0v$`}-!3gyH7La~wm&W-g2azW>NdMpfbab^cp?l)>2P@r@SzSlk zHuX|eZDGa~R%pw4YcUATjf{~%X%t@PnL^&p(9>P0zQ=WA4@`eKG9vpdQr}IN)W5sm zK_~a&o8vWBNJn~1Qt{@NN81VPTY(*W()zjVS~bbHiH%$3bvW*JEtp!qm_w%7ZYZdq z6UQCk)ZyM?VnkM&EB7NU0dvVe*nVBJEQMd`Vs&^mAkZj3L2Vs)AgljsXzq>`A;_L4 zek(zH)YK?XG5?&aZ%wnSZ6pLmn=Hy>x9-I_Y5^zMBC&^u-Jn;&L_bve7u7@l=hShr znCh6ki$Z~z_wPQQzHlwJP~6W4FM$fdVf~DZPIh`{4@Tl|K@z^@Xjs6oKF{3Auk*#9 zyt!`w4ucMt&T`+UV%XgNF7dibOs~3Xq4~HWR^@$XH`e>tRdH$@+)Z&+31;g214(>W z?){d=a=iGg_Jb+JpIszm;k}2Z~vu5)S^k1Bs## zU7AiI5?Vygk(#usAb}U$+wXJO&9Rn^1Xgm^Pd+d-g%}Fj3%*h4;)B`SdCzFiCqcD0 z<-L|o;nF!FgY`-cyfwK^Wb)L-ubg)|C)1FvpRtLOBNt7_@A=ltX9^jJW0*)DLxStx z%e1D5P5K&L(fVed=){(~2hTE1hQ^mai;pm-x;Lg;4hU%`^o zWV$9`CK;zx@ktQJu|^Sel9?_Lx;?;a5c$?npgjUYT*4sE)PaMAO4kR^U%Y`)AdqQ$ z{N9Yl+I=Lyv(Dy(gJJ$fRi?dM(l7?~1uu9(7r@GA0ZV7JagV*M2{{MfY8iI5NY_MO#87LtcGB;CO@Cg2DsTf@=e^ z(8DI5i>9~+xS1xh?+_i25j7;jGKrf|lE(rJE)bBv2PQJHzk?eLC82mUm||!)?O|?) z25*Kgrw=neHkUU!zFxND!pS;68BIgDi6})srGLob$HI|vv7#juSCC z!SFORsGXzED*=y5W11>9iaLwN3cQG-7&vDMToBy^^HDiQao92DuwSM@?~@gJa*qQX^N;WMx#b zK+*M(Y>MFf(@NmR!t|m+b$>R>Q4*q@Vlr~wH)`7BWU-5Jbf>7X1=0y)6>A7eTjUDn zTTo6-(BhbA4>G6EAcrS=$fI;*?R)b3(z)N7MW(I=TM8yJSk_Y$;7pyr?8ONX;vYdS zQCIm|!X;A)%i^q7QZaep+$D%klw#(F05>l(VzMAzTM(mCC|8I~e6otG6w2Xr`YhMP1`lC?UCF0b#$y8Snk|llq zWTrDC0J<6Oem3o9OP5!gOx2sphxHrL8E?cP!z}Re!drIyKy=x|ZPCUo(z8YvJ=6E-b8?o6LOf z+8ah<3Tdz;@(^G~ahM=Uu-QKw$~uv&?bbCBe(jm}jo6wn-XTE<*k|Zx&R4M}gWX@U z(%#D~b;5ZqV(Y}E*1KtPoljOY^1TFz%!}=G74R*v=nmD)y=nw_dgG1MGE#^z6D~92 zRg^!+NPI82GzO($%Df0}N~1*;Xklq>8>IIlITB;fkZQrNDw+#^!Bc}gm3zcmOBP6! zH6n<-#7keM{)*WNQ43`f%hZ`rDIV{zvZYMEkbcgqFrstk43;9~yt2_XvZzJ)`XDha zwB5hI$ae{9b+nj!#s*D)rIqK?|}Ssp<>KaMHAq zt+@Q0lbNoo9^D;KyTHl!Pyy!~qVcpDzK3V>=;Pkd0vVmTRa9~jpBvIfG-SI=SSzYW zakTlgxQgo@pJ&-uo*04c`S!yXU9bCecC>NF;$6mdu$Waces;PPR+XQZXII&hXjNb; z{N~=XC6kZF-jLwl^ZE6A1;*9IsgRB{!BS<~wuYklt9;QquiyoRblb_DSJ2XG298JX zB9Sdx%4gXK>AeK9>n<8(Uuqf=P!#T3|7AfDhbckXc>zA+q z-@f;Xl>0X1MZOkqFwm}+6LXZ&ec||`&+e1g1*Ty&SC_oFg_kh3w4UL0Ib+ z*|tMps;#+-C?hDFWiqRz+MGW-StV;2_ZTrx+UTMTNKbBG4h(5#UQId9JzF@s=Bk@) zg;)xAtWo2Gw0VndPYz{PZm3r;tl2$B66Wc5{YbcJSy}7uc-Mt9BVOS!ZZ%X^-{bIh z`beYB6zn*YY83O#NShr=GE$p8eKNLUc6<+_s{h=_lkzS3yHCFNV~sBlN*Y-?udH+1 zMJcZ|7j32~gm;)rJjIle7JHL#cuQ?UmDx#03|10ucRj3Y&Qq$QEy9~qjv81VxcCdD zj#k0G0(y8qc_?ZZ$RS8UAWzpF`e&QmPTLQZTC{)QC9GUc?1I?VZsZ|?pQbg z00iy+JrXARA4vE=1<&ek8OS+_?k;n;8f#LW zJ8WjiZmzG%n;JPSpxhmGHEWDrxlGp}*eTRz)?#pm*50tDVjagAv*}3HsK6f3udWk! zBeN|@ft51~SU`5hpPA!5xWc%)rH>TU?4g)mRrjE?QC-hp69)#=m38@Y2S7TLOuc)_ zjgMT6J+r5*zMQapoCm|>prh1|UdPJIK{zHfN5VqbsajD7WA5W`qZ}pixk14aeznW$ zMYz0-1f)lYn2;E$WhTp8IhGX_ZZ9()jt^xB0UhoW!c2xtoDgkj;H{5#P&MYXxMT|! zX53(ED_y__RXTd9`GyVCX=(LmX)5pp{)1daxe8Mi@sbNMGbY_v4tORp$r9BTWi%}8 z)q+Gm9{J<6p*9kZTu9d%Z~VDUYW4zb=C3L6=3dnmCPY#TS}e11-!hSjPoaz~ zRE{3Bw0Y5KpD2d9Da(d*w&ycu(-UA2w&KSV@b<+V-0+NgEiae%BD2cnnXjgGsPAlb ziCxcxaoj!*1|{-KYV`2DnSar|ZXupCv;($}uM3_?{#Xm>>#tAgJxImCg`js`b4-&C zvzeLRsc2Z<#+b*!5gvW}F(-e&`}Gw18=*eGi!q2Bg0l0A8Cu!JO5=wwE?FE0pMsNf z`yG?HY5Wl3)huz?s~1o2fSS=xopcI{mmy_Pgbr*S_^MLdV~c%od1FPr2Oq{q0=Idy zxO>aAJX5geY3&j->vWnKYu3}!4ag!%r%F$CWW4c_nB)=Hl_!r+eNvqxX#$C`qFB9hxqlt$le7=6$b(S(p%U-n;eHQSBnRkN(p`6KE%J99z zFdO+JdxS)SVdjSx>x@FRcfB?ZsloR3p}PbT+i})dfLfmnj_Y)VlvVo4A;-P!ozbXM z7!1mJ%=?Cu!!`_9-nK`RDvz79MUk90+<*)y>$OB|Y^jJZqNrZ5EROyoUF1Xd4*Ivz zwEU##vE<%j8Xht;jUi|8SH&F)jmQ%6L{_lpXBM1#Ihm`g+O6$D2{4A}Mu8u?IPzbL zNtDJ#d?=4QrJw5mm;@d2NRX5UA!~bzTTIi~b)UN>XCcK3-a5ZwEHox(kf2mA_8G0; z&Q8=WW=tFPT(c}-7o|w=#3VMdvGEP|W15avxXsQiFuMuZ#zsxanE}Po4?6=3HpEOb z!Rhe5Rg;CvVdCWSa0Abg*_*q4nJJ~<+yM$a&K53ApwR#~&@1T}?KyS?DaCihEsS1{ zs|LeOe%zHnxCqEXz5qCOpkpjL5sSoN4aDS^Sph~X=7wI*7fhr{@AUZ-n2C>_Gtoyl z$`hcm-h?NFFkvW(icYKvq39hjpgp@|@ijjVOQRmV-qiAtlZFqlo7oi{XCM-T$V`fs z6Zy3H9^wt*O9b-xv())5HP{QnWAwZWbWiclmmy=#+;j*6yZxz_%6TpHQgEp42*Bc~ zcF&tP)JXb{;kh{09vIkyKz zrKD1)o#LM(H*!1p=Fv9G&6GKKnFqa`#-`D5O~sWzX1e`IzY!?Yv4 zSA40=TR;=s6+P>%^2X1PQxAWw=$XhB#|v0ZSDf(BYaOc624j5{3V*Xe=~`&}U0Wir zhnr)V^v%$hmlTJF_4&@3_B==hLnVQ(uvESVB5V1|9Mp$2q+aEI&nTc^kj{MT+KEsQ zk$a!7T6%sgzsOfAGB98s(8f!PIpnsmIVuFgEy%!SM0I0stRGUb(O(2&7P@2Q|~$)m+;3MB}O+e%4i#NiotWCQjY2Mk?xD13aVor(;!KPtA%VMbK9gu z7i*%roTQgm31Y3%9>1f%zRihK$TXhE3yF1^gQimzUl6iSy6?Y2fYz}Kd4K9}fjL0% zD#iygs5&l1U*^>vTS8J~ib?nIYQh2Mr%wlz^sjz!tE zl~8VA7TPW$40X>AM-JYuzB`oFHz$Y+fhHegyAQs`lTaAzFDGH(kt{EpPQ^;t~I|2g1*h`$mll+*!eI?UhdhnAA^sdBY6eWArmmo%m3-B|L-*m6E z8ER$Cybz#(NXL?H8S>plsa{pUqCTnK1BZ`=2lwI*QqzEjGTtG;&k8iiP7lCB8%BQ) z9^sEQOtpPm`x-coS{~YdMLTeSj7?nUS!2Q7xdBh=j2HwUe*dl!@fs5!5>B)N0cwD8 zbSSh)jM@CknLisK4Oub@WfNVBrv8Cb{^Sup>d_fd4k2MeS}0Bi29+$DLT=jGZ!b;> zs<=6=Ls5=aeBRmj6iL~(xOxkm(L)QyG(jK$k#HL3F%hRqs%$Zk{JtV1_R$%sAt7f{ zkl!p5+l|C{2(AmBk6xWMdfyJPeVwg{JiZGawc%k2cKWo_(+0R67dMD;0=f={ioBP& zbb#)+&L{B<0ZL$msi(vKVCK$mg>tm(aJcJu9EfSp#n81`w9CQnWhg}F9X_vru|PPg z6`9u7VJD(OE{VS2T;pN56QXqd-RI9_I&}fKlv~eTI+>%Ndb=Ry~gu``oN`9?VhoErrgrrBTjjSZs)DP^1Npp z!rjTcjT2u;ed!O+``$C5(PFRb%Mhlx2_(S~!(>Z2bn&gp;JZts9i@NATe8JY_8OAtE&W$o*dT7UeC)iUJ#cxw2 z1I3M{TW(Y|B-}eZoX@Hv=pJJ=RYwmaR~9tUCq30|<(4B>!50;eJ$EgPn{NRPZNkq- zW4+wj3026ffqq%tE)dhSSvFGNd1k7r)?aNk2i1hoGwr z!1U0;J-5Xr!z|0KM-gQIN~{5t+uyo%(H50#i(E^YCO5T%<%%mw&k-SVgW>%se%yg@ zZGN7;Ix8K4RD);`=A8YGl(ONhZty+sLrz}p2MMc~tEltcMj`tT&voMu;JpYpcEjX! zzqlJ1lmi#@OkszDB?SwpcuE;e97FQ;6u)uN#M3XiL#c~Otk3tYs9ugJ&GklNDyaK&FtVQp zkKuras*qgu);l*SGDZQ55~HhH;wdK>9h&>?@5=9n_r< zR$5=zJRae`?Ne!NmAJTjbS5_vO&^;%UkuK~9o!V8mRZqOXA(Y(KYot0mX=YwpVD!W zaQR5BV+=`9#K=Q)y$u zG_AQ9%$B#Ny~fp+titW2gJl7Vt(eW=1-$n~YZ7yaXGy7^k~E$46_U(~1DZ_^x>;*5 zdq83hear_NqztVrixoT`TULv9-`U2lw`6`@Bkb1~=N8c=ql6lINoE>3xtT!CB%W!$ zd`Dw8&vze(v`G%_gs7_9NY5bO2UY-9-Uq4&hy|F<*?Ff{#QrL-o`QGt9a& zf11LjFx}QOtaYWQ3zf|h1d3GM+B11$MjDhmniQ@NwZWuWx;5AVMC2&9MQUO4>Do1X z#un>DTxlo%k;hSt7dd12l^nuBjqmSDOyLn`+=evjRLfV`mg(j{o-K<$>_d~W*YTBb z%D7fR;=i$4tdBK1y5^9*FcJe3BcSA6(ng5FtV#`0gtWam6>V0(ZxSxjJq^LMgYGp8 zY>LEMaoJ`MsxdLqwYp)?1Q{u!6Fdg}lNo}P{il(Fnif>kejWqpGsM#|Sp1`ZLF8yw zcjoBeG0pxQ40P#--bizt{N{+S;CVv!cKpa5r3xC1sD5>@7BryUK)yh-*vo)Aom<-^RPikkDkOE|9k|V#+v(G|e+FP%;aLU; zYzt!&eqs@coQxK%zP zySpvTEp=E?QQsQYl0~KvZATY`CFvih0T=bp?j#x)+k z$Wfjp)szE4uqVuJL#Np{6ny%*4=)UgYho`;4r{}h0~qN1*xtQz*N(bDWVE$uz|{9m zuu`abwr`2>F8g(`PnS2A?q#3I6DutkFp`K~cF>_1>`5EnKB*m~xxdz_%hOM>pLF`q z`|_vLq0{?o_5L>z1FQ-U)&Clke;A;&PTb)G!4VVyDUgiGptB3w2@EVkNCyY&)7qf* zf9?!g^=F0d9qd0(8))QUZs$m6@n^G&PWPPdkLNeLNmp5vUF1jgq|{@SiMEoW9#hMc z7ci7p-p``kvs6En(3oCvYPBpMY=7(QNiv5FMozAJz|6ceoR)q;in9#||5{&Rb9H<@ zhBh3Wc@{^`J0fpvTecdOYzHt#tnUIo+S)1)Pho`La~9d#WrjQappTq5Ia$T$+_IvZ zeyFni}=zms>*#|gpB=kAxmY?D|?DHx?4!TNMMze`m(OJp$_(s2sTtj z&#Knp#Jqj#SK7j7ZC_0KYG+n7E#5?PA{og+&ZhO=!|7BwXlq2ZaF@PmShgo9`4G6@ zXO)NJXSCE++eiM2yg_pj;E|K7txUkYejpj2#onkTap{A&VE!s}BUi@6Iq=3-t+!q# zVFK%mUl4OY3~ngWhwDxFnn?SNaaGaSs&%D&kH=fd#;gQ_R=g2H*zZTXMRP4$=`Q}X zemjPVWTW@zd-UFDj+{0Aki2%*luH3Kd~mig3o@oK(cfuFA0T$GR*BF4=!koEA(Qdp zDPV$Z{ihea)BNcLYD9k)p`P3lXk_bP{l>}0$kE)^2Kd7pwx1f{cAg&4`!{bm(pa>? z`-sz)0BD7nrB+q6id?c8hr(|xqjg7saIUUQ`a{kDIDY8;=sgP2p6=U=oZ0lK>;Kzc<&Ht4)r~!R1a_i6AyqUKp>OJ#z9MjW?35@oRr(#pLDb z+6mdv&~EsQW*%e$x>C3ew`)-+jN0=7AXm%8bTxK%DG`OLOsi^=$lHb0wf?p zXMtT3rs6=(%N%s25>G0Go{*i7f$y37RhdRLC_lY1UBjgo5s!u>cuet(21RO6iVDXxBx&>Gea(rxT2}|YVioC&iONl2$S2m(4^%WN z*H!^KVR2o_tCSzMs7A|d7A)O$N)a4p2W#OA7-74>(Fc&PZw!S`_npLYnd7%9ssn1Z zVBl?-oIM=cY=^d(5;@jjT96Mv;U6SrRAFM>uvA3Qe~GBU%Sh}ZL^qM#X7tvks&(q& zy=Y3WjSBuQ1cOKFb*K*GAJ?3qQ8yt#XF~oE#iq2si zi8=LgG5>@d`s>xh?Xs>Ewmpy7;uqGfF3qxXVEP&H7o))vGp`=M;+bQ3o0ZqH+$D^$ zUug)vG`*I_GP((?`2vp9z2sGo-rh$24sp)Anz9&DF?AH+pU8lBak-p9*?k%oc+`ty%MP94=*pW| zWpYd6It%z)DFuE@XDrm8O?v|6t|(aPbeaR4m9daA8(d9NDV7;M>b|!u_Z}5CuYWv_ z?#jx?);8$KK4nSM!<#yhjqk&wF3N%=()ZGim#$IR`LrlL0Ut(wHiX)^Fg*KMM(MNz{-9aw{}bp zva=A1Hb8pA@HKRY5P}sAnPEeS@GWVId4*fJKs^js5?})uOS`J zxLTJ+p`v7Za|*q|XGjPk%~eaLghrkh>l1G+Tll^ihSn7lT)`(`RN20X@_cYoe_!Br zdD4W9)#C(sGd3DdZ5D}$K*7Jaz(YZ5?X|ibK3q{N^xad+E`_%5vod%j$!ou)*U-De zhk}WmSsXsp`^cL7=0o#{S@@b?Zjdx3PNlwCvs4NZOYV@K+S!cpT^2=zz-cQ&e!D`G z5%f04o990RgHJQqO``I_I0wQOOR-%{=jl{3@z^I^|dhLG^O-9U9BS2l4^ zaQRN@;%8sl>U0D!6jXHFnK6pa=*Wa-DHYBt1x%(y7yQXX5zE4|+KNrB!$_hp_LyM% zeKb=Tiy}v)$vZXw49g-y-wG&}yPyg_Ndju?;hg%0y zkWH;Cf*Bd{&D;YstXh<+Q>#KYeokRX{b(t1j$Sez@TK~B%Z)=xwRgYc9?RJS_*+eL zdrY)rYF{(+FoK|##LWsUr`5ZT;Ti8^)-;%|xsApFMJ+lftJFb&q> zLf=DX`}x{QYQKfS^j#)Tf~=W^G?!Ut#o@Z?)YLqp#_bMyxX{05x~&zr;)IeWd>Nx% z^ik{Z(W9{$*jS^bj3-ie%G`d6jvMN96*3>z!zK}vzUoG&5WQI#;324%3WY0;D2$TO3Sf~0+sg28 z=fM!Nn80MZXWFPDu~ZzzKI4bhNAscUD}L>9|X%;&=Syb(Y-;yz)0Z%ySJ7UFG@|13h5rDi`KcyWRr>dOYCo7BJoUcq$4CQ#y`9- z2!6j*bhP%3DwgGz!K9I!UqE(V+qjKFOSFFDysQko%xj(yH(@l@6DFR8mAYd_ zm9IQJER=XSmDp3ZGVP!S4ZjwY==a81-{V1SCbiRdwi|2->}yJ zadoiw=#Q|N$=UEBH@joec3m!2*RCWHy&HghC z{I7DfQ&M8+(+UPuD10G7zbxsf2poPZ|AGKC61tSM%+f@4`KZ_uHT4wrxFofhql%10GN1cYTo=&b*oy&C*9r#tlY zfZo4>TopwDn?(lHmIF+LYf^|ZqA@8nmfg>+W1EC!11i!1q)=a({Jb)=jdQ89CheR} zO+}Y|b?HYF%kN}gh$<)pNm=VljFUZN&tel&$#I1?#Ahv_gWh#|h7=lPx-3!!J^RKH zQ7+2X*<#SuMV?nfo-5FaoW*WMhMi2tO%kCfZ4T$0+qsp;IVd4Hkk5x4H^gVR9^+HD z&g`uZd?kp$yM`gEmPVX1B;-2L1vhqHtTD+-X7TD^(mRi!_Ys7ZLzDPW&~2U3 zYzhHv4k2z!xr(vXyjI?%3u^*R?91*rA4M0X%W}k00%Cb1M5I1WPkzh3Xx>gZpsh~% zuC>DMX{;`6%Xv$-Bm_<0#%hSi7pO3GB4yyZ+O&Yl|FQh7LfQiq$gXAfuWiD?007WQ z%1eQPV?g{Iwfl7f2sCQiy@5P*Z8&74K&5@gN}R`p3>$g#{ape z?Y}e|P{92`N4}%zzf19-7yFCw`>QyhzMEe=azGtuKj_H!=-D6Q{M*(WP*2)#(eHQW zzn%xEspkhB`CeiDF3G=L=YJ6bbn)=(mBhcE3F=d+@##$d^WDS$uQO>T{U4J2$M&56 zMI6vg!LP+@&`rS)%JZEkdlKhw-y8fm4$$BKTDb;&29>^l(2;;X1>o-$!hgmH5W`;! zx}Z|{56bh+{*&QfS8o5JF#cn!pb!D#{A)oLR1p3_dA^sFzv%nFD-Zv=2&lIEYrX4V zw;xIMhd%yxrT16PpnBP_XzE`LGXBLsIRBT`v)@