From e05d2269a6c405969a6222e02f7ba06a46606acd Mon Sep 17 00:00:00 2001 From: Brian Huisman Date: Tue, 11 Apr 2023 22:02:16 -0400 Subject: [PATCH] Initial commit --- .gitattributes | 2 + LICENSE | 674 +++ README.md | 2 + example.html | 18 + example.php | 18 + os3/Mustache/Autoloader.php | 88 + os3/Mustache/Cache.php | 43 + os3/Mustache/Cache/AbstractCache.php | 60 + os3/Mustache/Cache/FilesystemCache.php | 161 + os3/Mustache/Cache/NoopCache.php | 47 + os3/Mustache/Compiler.php | 718 +++ os3/Mustache/Context.php | 242 + os3/Mustache/Engine.php | 831 +++ os3/Mustache/Exception.php | 18 + .../Exception/InvalidArgumentException.php | 18 + os3/Mustache/Exception/LogicException.php | 18 + os3/Mustache/Exception/RuntimeException.php | 18 + os3/Mustache/Exception/SyntaxException.php | 41 + .../Exception/UnknownFilterException.php | 38 + .../Exception/UnknownHelperException.php | 38 + .../Exception/UnknownTemplateException.php | 38 + os3/Mustache/HelperCollection.php | 172 + os3/Mustache/LICENSE | 21 + os3/Mustache/LambdaHelper.php | 76 + os3/Mustache/Loader.php | 27 + os3/Mustache/Loader/ArrayLoader.php | 79 + os3/Mustache/Loader/CascadingLoader.php | 69 + os3/Mustache/Loader/FilesystemLoader.php | 135 + os3/Mustache/Loader/InlineLoader.php | 123 + os3/Mustache/Loader/MutableLoader.php | 31 + .../Loader/ProductionFilesystemLoader.php | 86 + os3/Mustache/Loader/StringLoader.php | 39 + os3/Mustache/Logger.php | 126 + os3/Mustache/Logger/AbstractLogger.php | 121 + os3/Mustache/Logger/StreamLogger.php | 194 + os3/Mustache/Parser.php | 383 ++ os3/Mustache/Source.php | 40 + os3/Mustache/Source/FilesystemSource.php | 77 + os3/Mustache/Template.php | 180 + os3/Mustache/Tokenizer.php | 408 ++ os3/PHPMailer/Exception.php | 40 + os3/PHPMailer/LICENSE | 502 ++ os3/PHPMailer/PHPMailer.php | 5126 +++++++++++++++++ os3/PHPMailer/SMTP.php | 1466 +++++ os3/PdfParser/Config.php | 154 + os3/PdfParser/Document.php | 306 + os3/PdfParser/Element.php | 150 + os3/PdfParser/Element/ElementArray.php | 139 + os3/PdfParser/Element/ElementBoolean.php | 75 + os3/PdfParser/Element/ElementDate.php | 139 + os3/PdfParser/Element/ElementHexa.php | 85 + os3/PdfParser/Element/ElementMissing.php | 66 + os3/PdfParser/Element/ElementName.php | 69 + os3/PdfParser/Element/ElementNull.php | 71 + os3/PdfParser/Element/ElementNumeric.php | 62 + os3/PdfParser/Element/ElementString.php | 93 + os3/PdfParser/Element/ElementStruct.php | 75 + os3/PdfParser/Element/ElementXRef.php | 98 + os3/PdfParser/Encoding.php | 157 + os3/PdfParser/Encoding/AbstractEncoding.php | 8 + os3/PdfParser/Encoding/EncodingLocator.php | 17 + os3/PdfParser/Encoding/ISOLatin1Encoding.php | 76 + os3/PdfParser/Encoding/ISOLatin9Encoding.php | 76 + os3/PdfParser/Encoding/MacRomanEncoding.php | 80 + os3/PdfParser/Encoding/PostScriptGlyphs.php | 1099 ++++ os3/PdfParser/Encoding/StandardEncoding.php | 76 + os3/PdfParser/Encoding/WinAnsiEncoding.php | 76 + .../Exception/EncodingNotFoundException.php | 7 + os3/PdfParser/Font.php | 664 +++ os3/PdfParser/Font/FontCIDFontType0.php | 42 + os3/PdfParser/Font/FontCIDFontType2.php | 42 + os3/PdfParser/Font/FontTrueType.php | 42 + os3/PdfParser/Font/FontType0.php | 42 + os3/PdfParser/Font/FontType1.php | 42 + os3/PdfParser/Font/FontType3.php | 42 + os3/PdfParser/Header.php | 194 + os3/PdfParser/LICENSE.txt | 165 + os3/PdfParser/PDFObject.php | 779 +++ os3/PdfParser/Page.php | 953 +++ os3/PdfParser/Pages.php | 73 + os3/PdfParser/Parser.php | 327 ++ os3/PdfParser/RawData/FilterHelper.php | 396 ++ os3/PdfParser/RawData/RawDataParser.php | 902 +++ os3/PdfParser/XObject/Form.php | 51 + os3/PdfParser/XObject/Image.php | 47 + os3/PdfParser/alt_autoload.php-dist | 75 + os3/admin.php | 3061 ++++++++++ os3/config.ini.php | 18 + os3/config.php | 740 +++ os3/crawler.php | 1606 ++++++ os3/css/admin.css | 78 + os3/css/bootstrap.min.css | 5 + os3/css/search.css | 36 + os3/geoip2/README.txt | 14 + os3/img/arrow-down.svg | 5 + os3/img/clock.svg | 6 + os3/img/flags/README.txt | 3 + os3/img/flags/ad.png | Bin 0 -> 384 bytes os3/img/flags/ae.png | Bin 0 -> 122 bytes os3/img/flags/af.png | Bin 0 -> 516 bytes os3/img/flags/ag.png | Bin 0 -> 497 bytes os3/img/flags/ai.png | Bin 0 -> 369 bytes os3/img/flags/al.png | Bin 0 -> 293 bytes os3/img/flags/am.png | Bin 0 -> 114 bytes os3/img/flags/ao.png | Bin 0 -> 339 bytes os3/img/flags/aq.png | Bin 0 -> 310 bytes os3/img/flags/ar.png | Bin 0 -> 220 bytes os3/img/flags/as.png | Bin 0 -> 448 bytes os3/img/flags/at.png | Bin 0 -> 93 bytes os3/img/flags/au.png | Bin 0 -> 312 bytes os3/img/flags/aw.png | Bin 0 -> 186 bytes os3/img/flags/ax.png | Bin 0 -> 165 bytes os3/img/flags/az.png | Bin 0 -> 167 bytes os3/img/flags/ba.png | Bin 0 -> 235 bytes os3/img/flags/bb.png | Bin 0 -> 212 bytes os3/img/flags/bd.png | Bin 0 -> 175 bytes os3/img/flags/be.png | Bin 0 -> 109 bytes os3/img/flags/bf.png | Bin 0 -> 198 bytes os3/img/flags/bg.png | Bin 0 -> 99 bytes os3/img/flags/bh.png | Bin 0 -> 208 bytes os3/img/flags/bi.png | Bin 0 -> 364 bytes os3/img/flags/bj.png | Bin 0 -> 108 bytes os3/img/flags/bl.png | Bin 0 -> 970 bytes os3/img/flags/bm.png | Bin 0 -> 536 bytes os3/img/flags/bn.png | Bin 0 -> 685 bytes os3/img/flags/bo.png | Bin 0 -> 99 bytes os3/img/flags/bq.png | Bin 0 -> 489 bytes os3/img/flags/br.png | Bin 0 -> 449 bytes os3/img/flags/bs.png | Bin 0 -> 211 bytes os3/img/flags/bt.png | Bin 0 -> 628 bytes os3/img/flags/bv.png | Bin 0 -> 199 bytes os3/img/flags/bw.png | Bin 0 -> 122 bytes os3/img/flags/by.png | Bin 0 -> 228 bytes os3/img/flags/bz.png | Bin 0 -> 516 bytes os3/img/flags/ca.png | Bin 0 -> 249 bytes os3/img/flags/cc.png | Bin 0 -> 299 bytes os3/img/flags/cd.png | Bin 0 -> 316 bytes os3/img/flags/cf.png | Bin 0 -> 244 bytes os3/img/flags/cg.png | Bin 0 -> 263 bytes os3/img/flags/ch.png | Bin 0 -> 150 bytes os3/img/flags/ci.png | Bin 0 -> 108 bytes os3/img/flags/ck.png | Bin 0 -> 367 bytes os3/img/flags/cl.png | Bin 0 -> 193 bytes os3/img/flags/cm.png | Bin 0 -> 160 bytes os3/img/flags/cn.png | Bin 0 -> 196 bytes os3/img/flags/co.png | Bin 0 -> 124 bytes os3/img/flags/cr.png | Bin 0 -> 213 bytes os3/img/flags/cu.png | Bin 0 -> 258 bytes os3/img/flags/cv.png | Bin 0 -> 265 bytes os3/img/flags/cw.png | Bin 0 -> 237 bytes os3/img/flags/cx.png | Bin 0 -> 373 bytes os3/img/flags/cy.png | Bin 0 -> 283 bytes os3/img/flags/cz.png | Bin 0 -> 284 bytes os3/img/flags/de.png | Bin 0 -> 99 bytes os3/img/flags/dj.png | Bin 0 -> 352 bytes os3/img/flags/dk.png | Bin 0 -> 138 bytes os3/img/flags/dm.png | Bin 0 -> 290 bytes os3/img/flags/do.png | Bin 0 -> 273 bytes os3/img/flags/dz.png | Bin 0 -> 274 bytes os3/img/flags/ec.png | Bin 0 -> 528 bytes os3/img/flags/ee.png | Bin 0 -> 114 bytes os3/img/flags/eg.png | Bin 0 -> 219 bytes os3/img/flags/eh.png | Bin 0 -> 284 bytes os3/img/flags/er.png | Bin 0 -> 304 bytes os3/img/flags/es.png | Bin 0 -> 392 bytes os3/img/flags/et.png | Bin 0 -> 314 bytes os3/img/flags/fi.png | Bin 0 -> 138 bytes os3/img/flags/fj.png | Bin 0 -> 477 bytes os3/img/flags/fk.png | Bin 0 -> 560 bytes os3/img/flags/fm.png | Bin 0 -> 159 bytes os3/img/flags/fo.png | Bin 0 -> 171 bytes os3/img/flags/fr.png | Bin 0 -> 108 bytes os3/img/flags/ga.png | Bin 0 -> 99 bytes os3/img/flags/gb-eng.png | Bin 0 -> 128 bytes os3/img/flags/gb-nir.png | Bin 0 -> 304 bytes os3/img/flags/gb-sct.png | Bin 0 -> 281 bytes os3/img/flags/gb-wls.png | Bin 0 -> 910 bytes os3/img/flags/gb.png | Bin 0 -> 298 bytes os3/img/flags/gd.png | Bin 0 -> 376 bytes os3/img/flags/ge.png | Bin 0 -> 261 bytes os3/img/flags/gf.png | Bin 0 -> 359 bytes os3/img/flags/gg.png | Bin 0 -> 206 bytes os3/img/flags/gh.png | Bin 0 -> 196 bytes os3/img/flags/gi.png | Bin 0 -> 446 bytes os3/img/flags/gl.png | Bin 0 -> 251 bytes os3/img/flags/gm.png | Bin 0 -> 140 bytes os3/img/flags/gn.png | Bin 0 -> 135 bytes os3/img/flags/gp.png | Bin 0 -> 574 bytes os3/img/flags/gq.png | Bin 0 -> 382 bytes os3/img/flags/gr.png | Bin 0 -> 123 bytes os3/img/flags/gs.png | Bin 0 -> 663 bytes os3/img/flags/gt.png | Bin 0 -> 267 bytes os3/img/flags/gu.png | Bin 0 -> 322 bytes os3/img/flags/gw.png | Bin 0 -> 159 bytes os3/img/flags/gy.png | Bin 0 -> 374 bytes os3/img/flags/hk.png | Bin 0 -> 284 bytes os3/img/flags/hm.png | Bin 0 -> 316 bytes os3/img/flags/hn.png | Bin 0 -> 161 bytes os3/img/flags/hr.png | Bin 0 -> 344 bytes os3/img/flags/ht.png | Bin 0 -> 233 bytes os3/img/flags/hu.png | Bin 0 -> 114 bytes os3/img/flags/id.png | Bin 0 -> 104 bytes os3/img/flags/ie.png | Bin 0 -> 107 bytes os3/img/flags/il.png | Bin 0 -> 229 bytes os3/img/flags/im.png | Bin 0 -> 328 bytes os3/img/flags/in.png | Bin 0 -> 165 bytes os3/img/flags/io.png | Bin 0 -> 838 bytes os3/img/flags/iq.png | Bin 0 -> 232 bytes os3/img/flags/ir.png | Bin 0 -> 309 bytes os3/img/flags/is.png | Bin 0 -> 165 bytes os3/img/flags/it.png | Bin 0 -> 109 bytes os3/img/flags/je.png | Bin 0 -> 460 bytes os3/img/flags/jm.png | Bin 0 -> 243 bytes os3/img/flags/jo.png | Bin 0 -> 225 bytes os3/img/flags/jp.png | Bin 0 -> 202 bytes os3/img/flags/ke.png | Bin 0 -> 303 bytes os3/img/flags/kg.png | Bin 0 -> 247 bytes os3/img/flags/kh.png | Bin 0 -> 374 bytes os3/img/flags/ki.png | Bin 0 -> 493 bytes os3/img/flags/km.png | Bin 0 -> 341 bytes os3/img/flags/kn.png | Bin 0 -> 626 bytes os3/img/flags/kp.png | Bin 0 -> 221 bytes os3/img/flags/kr.png | Bin 0 -> 440 bytes os3/img/flags/kw.png | Bin 0 -> 182 bytes os3/img/flags/ky.png | Bin 0 -> 543 bytes os3/img/flags/kz.png | Bin 0 -> 272 bytes os3/img/flags/la.png | Bin 0 -> 206 bytes os3/img/flags/lb.png | Bin 0 -> 300 bytes os3/img/flags/lc.png | Bin 0 -> 277 bytes os3/img/flags/li.png | Bin 0 -> 249 bytes os3/img/flags/lk.png | Bin 0 -> 433 bytes os3/img/flags/lr.png | Bin 0 -> 212 bytes os3/img/flags/ls.png | Bin 0 -> 233 bytes os3/img/flags/lt.png | Bin 0 -> 99 bytes os3/img/flags/lu.png | Bin 0 -> 99 bytes os3/img/flags/lv.png | Bin 0 -> 93 bytes os3/img/flags/ly.png | Bin 0 -> 142 bytes os3/img/flags/ma.png | Bin 0 -> 147 bytes os3/img/flags/mc.png | Bin 0 -> 91 bytes os3/img/flags/md.png | Bin 0 -> 345 bytes os3/img/flags/me.png | Bin 0 -> 293 bytes os3/img/flags/mf.png | Bin 0 -> 108 bytes os3/img/flags/mg.png | Bin 0 -> 137 bytes os3/img/flags/mh.png | Bin 0 -> 548 bytes os3/img/flags/mk.png | Bin 0 -> 312 bytes os3/img/flags/ml.png | Bin 0 -> 117 bytes os3/img/flags/mm.png | Bin 0 -> 313 bytes os3/img/flags/mn.png | Bin 0 -> 204 bytes os3/img/flags/mo.png | Bin 0 -> 326 bytes os3/img/flags/mp.png | Bin 0 -> 778 bytes os3/img/flags/mq.png | Bin 0 -> 401 bytes os3/img/flags/mr.png | Bin 0 -> 287 bytes os3/img/flags/ms.png | Bin 0 -> 459 bytes os3/img/flags/mt.png | Bin 0 -> 214 bytes os3/img/flags/mu.png | Bin 0 -> 143 bytes os3/img/flags/mv.png | Bin 0 -> 206 bytes os3/img/flags/mw.png | Bin 0 -> 200 bytes os3/img/flags/mx.png | Bin 0 -> 339 bytes os3/img/flags/my.png | Bin 0 -> 260 bytes os3/img/flags/mz.png | Bin 0 -> 494 bytes os3/img/flags/na.png | Bin 0 -> 578 bytes os3/img/flags/nc.png | Bin 0 -> 386 bytes os3/img/flags/ne.png | Bin 0 -> 183 bytes os3/img/flags/nf.png | Bin 0 -> 259 bytes os3/img/flags/ng.png | Bin 0 -> 99 bytes os3/img/flags/ni.png | Bin 0 -> 197 bytes os3/img/flags/nl.png | Bin 0 -> 99 bytes os3/img/flags/no.png | Bin 0 -> 199 bytes os3/img/flags/np.png | Bin 0 -> 609 bytes os3/img/flags/nr.png | Bin 0 -> 155 bytes os3/img/flags/nu.png | Bin 0 -> 273 bytes os3/img/flags/nz.png | Bin 0 -> 298 bytes os3/img/flags/om.png | Bin 0 -> 174 bytes os3/img/flags/pa.png | Bin 0 -> 244 bytes os3/img/flags/pe.png | Bin 0 -> 100 bytes os3/img/flags/pf.png | Bin 0 -> 445 bytes os3/img/flags/pg.png | Bin 0 -> 422 bytes os3/img/flags/ph.png | Bin 0 -> 337 bytes os3/img/flags/pk.png | Bin 0 -> 264 bytes os3/img/flags/pl.png | Bin 0 -> 98 bytes os3/img/flags/pm.png | Bin 0 -> 1474 bytes os3/img/flags/pn.png | Bin 0 -> 634 bytes os3/img/flags/pr.png | Bin 0 -> 370 bytes os3/img/flags/ps.png | Bin 0 -> 233 bytes os3/img/flags/pt.png | Bin 0 -> 525 bytes os3/img/flags/pw.png | Bin 0 -> 182 bytes os3/img/flags/py.png | Bin 0 -> 200 bytes os3/img/flags/qa.png | Bin 0 -> 193 bytes os3/img/flags/re.png | Bin 0 -> 521 bytes os3/img/flags/ro.png | Bin 0 -> 113 bytes os3/img/flags/rs.png | Bin 0 -> 628 bytes os3/img/flags/ru.png | Bin 0 -> 120 bytes os3/img/flags/rw.png | Bin 0 -> 224 bytes os3/img/flags/sa.png | Bin 0 -> 234 bytes os3/img/flags/sb.png | Bin 0 -> 277 bytes os3/img/flags/sc.png | Bin 0 -> 355 bytes os3/img/flags/sd.png | Bin 0 -> 234 bytes os3/img/flags/se.png | Bin 0 -> 104 bytes os3/img/flags/sg.png | Bin 0 -> 199 bytes os3/img/flags/sh.png | Bin 0 -> 410 bytes os3/img/flags/si.png | Bin 0 -> 188 bytes os3/img/flags/sj.png | Bin 0 -> 199 bytes os3/img/flags/sk.png | Bin 0 -> 316 bytes os3/img/flags/sl.png | Bin 0 -> 105 bytes os3/img/flags/sm.png | Bin 0 -> 664 bytes os3/img/flags/sn.png | Bin 0 -> 174 bytes os3/img/flags/so.png | Bin 0 -> 186 bytes os3/img/flags/sr.png | Bin 0 -> 221 bytes os3/img/flags/ss.png | Bin 0 -> 280 bytes os3/img/flags/st.png | Bin 0 -> 219 bytes os3/img/flags/sv.png | Bin 0 -> 274 bytes os3/img/flags/sx.png | Bin 0 -> 616 bytes os3/img/flags/sy.png | Bin 0 -> 195 bytes os3/img/flags/sz.png | Bin 0 -> 570 bytes os3/img/flags/tc.png | Bin 0 -> 385 bytes os3/img/flags/td.png | Bin 0 -> 126 bytes os3/img/flags/tf.png | Bin 0 -> 283 bytes os3/img/flags/tg.png | Bin 0 -> 191 bytes os3/img/flags/th.png | Bin 0 -> 112 bytes os3/img/flags/tj.png | Bin 0 -> 189 bytes os3/img/flags/tk.png | Bin 0 -> 302 bytes os3/img/flags/tl.png | Bin 0 -> 276 bytes os3/img/flags/tm.png | Bin 0 -> 691 bytes os3/img/flags/tn.png | Bin 0 -> 239 bytes os3/img/flags/to.png | Bin 0 -> 154 bytes os3/img/flags/tr.png | Bin 0 -> 227 bytes os3/img/flags/tt.png | Bin 0 -> 401 bytes os3/img/flags/tv.png | Bin 0 -> 359 bytes os3/img/flags/tw.png | Bin 0 -> 197 bytes os3/img/flags/tz.png | Bin 0 -> 383 bytes os3/img/flags/ua.png | Bin 0 -> 120 bytes os3/img/flags/ug.png | Bin 0 -> 333 bytes os3/img/flags/um.png | Bin 0 -> 252 bytes os3/img/flags/us.png | Bin 0 -> 252 bytes os3/img/flags/uy.png | Bin 0 -> 358 bytes os3/img/flags/uz.png | Bin 0 -> 173 bytes os3/img/flags/va.png | Bin 0 -> 634 bytes os3/img/flags/vc.png | Bin 0 -> 217 bytes os3/img/flags/ve.png | Bin 0 -> 215 bytes os3/img/flags/vg.png | Bin 0 -> 519 bytes os3/img/flags/vi.png | Bin 0 -> 1034 bytes os3/img/flags/vn.png | Bin 0 -> 199 bytes os3/img/flags/vu.png | Bin 0 -> 345 bytes os3/img/flags/wf.png | Bin 0 -> 227 bytes os3/img/flags/ws.png | Bin 0 -> 160 bytes os3/img/flags/xk.png | Bin 0 -> 345 bytes os3/img/flags/ye.png | Bin 0 -> 99 bytes os3/img/flags/yt.png | Bin 0 -> 749 bytes os3/img/flags/za.png | Bin 0 -> 384 bytes os3/img/flags/zm.png | Bin 0 -> 228 bytes os3/img/flags/zw.png | Bin 0 -> 321 bytes os3/img/help.svg | 6 + os3/img/hidden.svg | 5 + os3/img/logout.svg | 5 + os3/img/new.svg | 6 + os3/img/warning.svg | 6 + os3/js/LICENSE | 11 + os3/js/admin.js | 600 ++ os3/js/bootstrap.bundle.min.js | 6 + os3/js/mustache.js | 764 +++ os3/search.php | 730 +++ 361 files changed, 28495 insertions(+) create mode 100644 .gitattributes create mode 100644 LICENSE create mode 100644 README.md create mode 100644 example.html create mode 100644 example.php create mode 100644 os3/Mustache/Autoloader.php create mode 100644 os3/Mustache/Cache.php create mode 100644 os3/Mustache/Cache/AbstractCache.php create mode 100644 os3/Mustache/Cache/FilesystemCache.php create mode 100644 os3/Mustache/Cache/NoopCache.php create mode 100644 os3/Mustache/Compiler.php create mode 100644 os3/Mustache/Context.php create mode 100644 os3/Mustache/Engine.php create mode 100644 os3/Mustache/Exception.php create mode 100644 os3/Mustache/Exception/InvalidArgumentException.php create mode 100644 os3/Mustache/Exception/LogicException.php create mode 100644 os3/Mustache/Exception/RuntimeException.php create mode 100644 os3/Mustache/Exception/SyntaxException.php create mode 100644 os3/Mustache/Exception/UnknownFilterException.php create mode 100644 os3/Mustache/Exception/UnknownHelperException.php create mode 100644 os3/Mustache/Exception/UnknownTemplateException.php create mode 100644 os3/Mustache/HelperCollection.php create mode 100644 os3/Mustache/LICENSE create mode 100644 os3/Mustache/LambdaHelper.php create mode 100644 os3/Mustache/Loader.php create mode 100644 os3/Mustache/Loader/ArrayLoader.php create mode 100644 os3/Mustache/Loader/CascadingLoader.php create mode 100644 os3/Mustache/Loader/FilesystemLoader.php create mode 100644 os3/Mustache/Loader/InlineLoader.php create mode 100644 os3/Mustache/Loader/MutableLoader.php create mode 100644 os3/Mustache/Loader/ProductionFilesystemLoader.php create mode 100644 os3/Mustache/Loader/StringLoader.php create mode 100644 os3/Mustache/Logger.php create mode 100644 os3/Mustache/Logger/AbstractLogger.php create mode 100644 os3/Mustache/Logger/StreamLogger.php create mode 100644 os3/Mustache/Parser.php create mode 100644 os3/Mustache/Source.php create mode 100644 os3/Mustache/Source/FilesystemSource.php create mode 100644 os3/Mustache/Template.php create mode 100644 os3/Mustache/Tokenizer.php create mode 100644 os3/PHPMailer/Exception.php create mode 100644 os3/PHPMailer/LICENSE create mode 100644 os3/PHPMailer/PHPMailer.php create mode 100644 os3/PHPMailer/SMTP.php create mode 100644 os3/PdfParser/Config.php create mode 100644 os3/PdfParser/Document.php create mode 100644 os3/PdfParser/Element.php create mode 100644 os3/PdfParser/Element/ElementArray.php create mode 100644 os3/PdfParser/Element/ElementBoolean.php create mode 100644 os3/PdfParser/Element/ElementDate.php create mode 100644 os3/PdfParser/Element/ElementHexa.php create mode 100644 os3/PdfParser/Element/ElementMissing.php create mode 100644 os3/PdfParser/Element/ElementName.php create mode 100644 os3/PdfParser/Element/ElementNull.php create mode 100644 os3/PdfParser/Element/ElementNumeric.php create mode 100644 os3/PdfParser/Element/ElementString.php create mode 100644 os3/PdfParser/Element/ElementStruct.php create mode 100644 os3/PdfParser/Element/ElementXRef.php create mode 100644 os3/PdfParser/Encoding.php create mode 100644 os3/PdfParser/Encoding/AbstractEncoding.php create mode 100644 os3/PdfParser/Encoding/EncodingLocator.php create mode 100644 os3/PdfParser/Encoding/ISOLatin1Encoding.php create mode 100644 os3/PdfParser/Encoding/ISOLatin9Encoding.php create mode 100644 os3/PdfParser/Encoding/MacRomanEncoding.php create mode 100644 os3/PdfParser/Encoding/PostScriptGlyphs.php create mode 100644 os3/PdfParser/Encoding/StandardEncoding.php create mode 100644 os3/PdfParser/Encoding/WinAnsiEncoding.php create mode 100644 os3/PdfParser/Exception/EncodingNotFoundException.php create mode 100644 os3/PdfParser/Font.php create mode 100644 os3/PdfParser/Font/FontCIDFontType0.php create mode 100644 os3/PdfParser/Font/FontCIDFontType2.php create mode 100644 os3/PdfParser/Font/FontTrueType.php create mode 100644 os3/PdfParser/Font/FontType0.php create mode 100644 os3/PdfParser/Font/FontType1.php create mode 100644 os3/PdfParser/Font/FontType3.php create mode 100644 os3/PdfParser/Header.php create mode 100644 os3/PdfParser/LICENSE.txt create mode 100644 os3/PdfParser/PDFObject.php create mode 100644 os3/PdfParser/Page.php create mode 100644 os3/PdfParser/Pages.php create mode 100644 os3/PdfParser/Parser.php create mode 100644 os3/PdfParser/RawData/FilterHelper.php create mode 100644 os3/PdfParser/RawData/RawDataParser.php create mode 100644 os3/PdfParser/XObject/Form.php create mode 100644 os3/PdfParser/XObject/Image.php create mode 100644 os3/PdfParser/alt_autoload.php-dist create mode 100644 os3/admin.php create mode 100644 os3/config.ini.php create mode 100644 os3/config.php create mode 100644 os3/crawler.php create mode 100644 os3/css/admin.css create mode 100644 os3/css/bootstrap.min.css create mode 100644 os3/css/search.css create mode 100644 os3/geoip2/README.txt create mode 100644 os3/img/arrow-down.svg create mode 100644 os3/img/clock.svg create mode 100644 os3/img/flags/README.txt create mode 100644 os3/img/flags/ad.png create mode 100644 os3/img/flags/ae.png create mode 100644 os3/img/flags/af.png create mode 100644 os3/img/flags/ag.png create mode 100644 os3/img/flags/ai.png create mode 100644 os3/img/flags/al.png create mode 100644 os3/img/flags/am.png create mode 100644 os3/img/flags/ao.png create mode 100644 os3/img/flags/aq.png create mode 100644 os3/img/flags/ar.png create mode 100644 os3/img/flags/as.png create mode 100644 os3/img/flags/at.png create mode 100644 os3/img/flags/au.png create mode 100644 os3/img/flags/aw.png create mode 100644 os3/img/flags/ax.png create mode 100644 os3/img/flags/az.png create mode 100644 os3/img/flags/ba.png create mode 100644 os3/img/flags/bb.png create mode 100644 os3/img/flags/bd.png create mode 100644 os3/img/flags/be.png create mode 100644 os3/img/flags/bf.png create mode 100644 os3/img/flags/bg.png create mode 100644 os3/img/flags/bh.png create mode 100644 os3/img/flags/bi.png create mode 100644 os3/img/flags/bj.png create mode 100644 os3/img/flags/bl.png create mode 100644 os3/img/flags/bm.png create mode 100644 os3/img/flags/bn.png create mode 100644 os3/img/flags/bo.png create mode 100644 os3/img/flags/bq.png create mode 100644 os3/img/flags/br.png create mode 100644 os3/img/flags/bs.png create mode 100644 os3/img/flags/bt.png create mode 100644 os3/img/flags/bv.png create mode 100644 os3/img/flags/bw.png create mode 100644 os3/img/flags/by.png create mode 100644 os3/img/flags/bz.png create mode 100644 os3/img/flags/ca.png create mode 100644 os3/img/flags/cc.png create mode 100644 os3/img/flags/cd.png create mode 100644 os3/img/flags/cf.png create mode 100644 os3/img/flags/cg.png create mode 100644 os3/img/flags/ch.png create mode 100644 os3/img/flags/ci.png create mode 100644 os3/img/flags/ck.png create mode 100644 os3/img/flags/cl.png create mode 100644 os3/img/flags/cm.png create mode 100644 os3/img/flags/cn.png create mode 100644 os3/img/flags/co.png create mode 100644 os3/img/flags/cr.png create mode 100644 os3/img/flags/cu.png create mode 100644 os3/img/flags/cv.png create mode 100644 os3/img/flags/cw.png create mode 100644 os3/img/flags/cx.png create mode 100644 os3/img/flags/cy.png create mode 100644 os3/img/flags/cz.png create mode 100644 os3/img/flags/de.png create mode 100644 os3/img/flags/dj.png create mode 100644 os3/img/flags/dk.png create mode 100644 os3/img/flags/dm.png create mode 100644 os3/img/flags/do.png create mode 100644 os3/img/flags/dz.png create mode 100644 os3/img/flags/ec.png create mode 100644 os3/img/flags/ee.png create mode 100644 os3/img/flags/eg.png create mode 100644 os3/img/flags/eh.png create mode 100644 os3/img/flags/er.png create mode 100644 os3/img/flags/es.png create mode 100644 os3/img/flags/et.png create mode 100644 os3/img/flags/fi.png create mode 100644 os3/img/flags/fj.png create mode 100644 os3/img/flags/fk.png create mode 100644 os3/img/flags/fm.png create mode 100644 os3/img/flags/fo.png create mode 100644 os3/img/flags/fr.png create mode 100644 os3/img/flags/ga.png create mode 100644 os3/img/flags/gb-eng.png create mode 100644 os3/img/flags/gb-nir.png create mode 100644 os3/img/flags/gb-sct.png create mode 100644 os3/img/flags/gb-wls.png create mode 100644 os3/img/flags/gb.png create mode 100644 os3/img/flags/gd.png create mode 100644 os3/img/flags/ge.png create mode 100644 os3/img/flags/gf.png create mode 100644 os3/img/flags/gg.png create mode 100644 os3/img/flags/gh.png create mode 100644 os3/img/flags/gi.png create mode 100644 os3/img/flags/gl.png create mode 100644 os3/img/flags/gm.png create mode 100644 os3/img/flags/gn.png create mode 100644 os3/img/flags/gp.png create mode 100644 os3/img/flags/gq.png create mode 100644 os3/img/flags/gr.png create mode 100644 os3/img/flags/gs.png create mode 100644 os3/img/flags/gt.png create mode 100644 os3/img/flags/gu.png create mode 100644 os3/img/flags/gw.png create mode 100644 os3/img/flags/gy.png create mode 100644 os3/img/flags/hk.png create mode 100644 os3/img/flags/hm.png create mode 100644 os3/img/flags/hn.png create mode 100644 os3/img/flags/hr.png create mode 100644 os3/img/flags/ht.png create mode 100644 os3/img/flags/hu.png create mode 100644 os3/img/flags/id.png create mode 100644 os3/img/flags/ie.png create mode 100644 os3/img/flags/il.png create mode 100644 os3/img/flags/im.png create mode 100644 os3/img/flags/in.png create mode 100644 os3/img/flags/io.png create mode 100644 os3/img/flags/iq.png create mode 100644 os3/img/flags/ir.png create mode 100644 os3/img/flags/is.png create mode 100644 os3/img/flags/it.png create mode 100644 os3/img/flags/je.png create mode 100644 os3/img/flags/jm.png create mode 100644 os3/img/flags/jo.png create mode 100644 os3/img/flags/jp.png create mode 100644 os3/img/flags/ke.png create mode 100644 os3/img/flags/kg.png create mode 100644 os3/img/flags/kh.png create mode 100644 os3/img/flags/ki.png create mode 100644 os3/img/flags/km.png create mode 100644 os3/img/flags/kn.png create mode 100644 os3/img/flags/kp.png create mode 100644 os3/img/flags/kr.png create mode 100644 os3/img/flags/kw.png create mode 100644 os3/img/flags/ky.png create mode 100644 os3/img/flags/kz.png create mode 100644 os3/img/flags/la.png create mode 100644 os3/img/flags/lb.png create mode 100644 os3/img/flags/lc.png create mode 100644 os3/img/flags/li.png create mode 100644 os3/img/flags/lk.png create mode 100644 os3/img/flags/lr.png create mode 100644 os3/img/flags/ls.png create mode 100644 os3/img/flags/lt.png create mode 100644 os3/img/flags/lu.png create mode 100644 os3/img/flags/lv.png create mode 100644 os3/img/flags/ly.png create mode 100644 os3/img/flags/ma.png create mode 100644 os3/img/flags/mc.png create mode 100644 os3/img/flags/md.png create mode 100644 os3/img/flags/me.png create mode 100644 os3/img/flags/mf.png create mode 100644 os3/img/flags/mg.png create mode 100644 os3/img/flags/mh.png create mode 100644 os3/img/flags/mk.png create mode 100644 os3/img/flags/ml.png create mode 100644 os3/img/flags/mm.png create mode 100644 os3/img/flags/mn.png create mode 100644 os3/img/flags/mo.png create mode 100644 os3/img/flags/mp.png create mode 100644 os3/img/flags/mq.png create mode 100644 os3/img/flags/mr.png create mode 100644 os3/img/flags/ms.png create mode 100644 os3/img/flags/mt.png create mode 100644 os3/img/flags/mu.png create mode 100644 os3/img/flags/mv.png create mode 100644 os3/img/flags/mw.png create mode 100644 os3/img/flags/mx.png create mode 100644 os3/img/flags/my.png create mode 100644 os3/img/flags/mz.png create mode 100644 os3/img/flags/na.png create mode 100644 os3/img/flags/nc.png create mode 100644 os3/img/flags/ne.png create mode 100644 os3/img/flags/nf.png create mode 100644 os3/img/flags/ng.png create mode 100644 os3/img/flags/ni.png create mode 100644 os3/img/flags/nl.png create mode 100644 os3/img/flags/no.png create mode 100644 os3/img/flags/np.png create mode 100644 os3/img/flags/nr.png create mode 100644 os3/img/flags/nu.png create mode 100644 os3/img/flags/nz.png create mode 100644 os3/img/flags/om.png create mode 100644 os3/img/flags/pa.png create mode 100644 os3/img/flags/pe.png create mode 100644 os3/img/flags/pf.png create mode 100644 os3/img/flags/pg.png create mode 100644 os3/img/flags/ph.png create mode 100644 os3/img/flags/pk.png create mode 100644 os3/img/flags/pl.png create mode 100644 os3/img/flags/pm.png create mode 100644 os3/img/flags/pn.png create mode 100644 os3/img/flags/pr.png create mode 100644 os3/img/flags/ps.png create mode 100644 os3/img/flags/pt.png create mode 100644 os3/img/flags/pw.png create mode 100644 os3/img/flags/py.png create mode 100644 os3/img/flags/qa.png create mode 100644 os3/img/flags/re.png create mode 100644 os3/img/flags/ro.png create mode 100644 os3/img/flags/rs.png create mode 100644 os3/img/flags/ru.png create mode 100644 os3/img/flags/rw.png create mode 100644 os3/img/flags/sa.png create mode 100644 os3/img/flags/sb.png create mode 100644 os3/img/flags/sc.png create mode 100644 os3/img/flags/sd.png create mode 100644 os3/img/flags/se.png create mode 100644 os3/img/flags/sg.png create mode 100644 os3/img/flags/sh.png create mode 100644 os3/img/flags/si.png create mode 100644 os3/img/flags/sj.png create mode 100644 os3/img/flags/sk.png create mode 100644 os3/img/flags/sl.png create mode 100644 os3/img/flags/sm.png create mode 100644 os3/img/flags/sn.png create mode 100644 os3/img/flags/so.png create mode 100644 os3/img/flags/sr.png create mode 100644 os3/img/flags/ss.png create mode 100644 os3/img/flags/st.png create mode 100644 os3/img/flags/sv.png create mode 100644 os3/img/flags/sx.png create mode 100644 os3/img/flags/sy.png create mode 100644 os3/img/flags/sz.png create mode 100644 os3/img/flags/tc.png create mode 100644 os3/img/flags/td.png create mode 100644 os3/img/flags/tf.png create mode 100644 os3/img/flags/tg.png create mode 100644 os3/img/flags/th.png create mode 100644 os3/img/flags/tj.png create mode 100644 os3/img/flags/tk.png create mode 100644 os3/img/flags/tl.png create mode 100644 os3/img/flags/tm.png create mode 100644 os3/img/flags/tn.png create mode 100644 os3/img/flags/to.png create mode 100644 os3/img/flags/tr.png create mode 100644 os3/img/flags/tt.png create mode 100644 os3/img/flags/tv.png create mode 100644 os3/img/flags/tw.png create mode 100644 os3/img/flags/tz.png create mode 100644 os3/img/flags/ua.png create mode 100644 os3/img/flags/ug.png create mode 100644 os3/img/flags/um.png create mode 100644 os3/img/flags/us.png create mode 100644 os3/img/flags/uy.png create mode 100644 os3/img/flags/uz.png create mode 100644 os3/img/flags/va.png create mode 100644 os3/img/flags/vc.png create mode 100644 os3/img/flags/ve.png create mode 100644 os3/img/flags/vg.png create mode 100644 os3/img/flags/vi.png create mode 100644 os3/img/flags/vn.png create mode 100644 os3/img/flags/vu.png create mode 100644 os3/img/flags/wf.png create mode 100644 os3/img/flags/ws.png create mode 100644 os3/img/flags/xk.png create mode 100644 os3/img/flags/ye.png create mode 100644 os3/img/flags/yt.png create mode 100644 os3/img/flags/za.png create mode 100644 os3/img/flags/zm.png create mode 100644 os3/img/flags/zw.png create mode 100644 os3/img/help.svg create mode 100644 os3/img/hidden.svg create mode 100644 os3/img/logout.svg create mode 100644 os3/img/new.svg create mode 100644 os3/img/warning.svg create mode 100644 os3/js/LICENSE create mode 100644 os3/js/admin.js create mode 100644 os3/js/bootstrap.bundle.min.js create mode 100644 os3/js/mustache.js create mode 100644 os3/search.php diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..dfe0770 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,2 @@ +# Auto detect text files and perform LF normalization +* text=auto diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..e62ec04 --- /dev/null +++ b/LICENSE @@ -0,0 +1,674 @@ +GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. diff --git a/README.md b/README.md new file mode 100644 index 0000000..d833ad4 --- /dev/null +++ b/README.md @@ -0,0 +1,2 @@ +# orca-search3 + Orca PHP Search - Self-crawling, indexing and search-engine script diff --git a/example.html b/example.html new file mode 100644 index 0000000..8b251c2 --- /dev/null +++ b/example.html @@ -0,0 +1,18 @@ + + + + + + + + + Orca PHP Search - Offline Javascript Search + + +

Orca PHP Search - Offline Javascript Search

+ + + + + + \ No newline at end of file diff --git a/example.php b/example.php new file mode 100644 index 0000000..2d7958b --- /dev/null +++ b/example.php @@ -0,0 +1,18 @@ + + + + + + + Orca PHP Search <?php echo $_ODATA['version']; ?> + + +

Orca PHP Search

+ + render(); ?> + + diff --git a/os3/Mustache/Autoloader.php b/os3/Mustache/Autoloader.php new file mode 100644 index 0000000..e8ea3f4 --- /dev/null +++ b/os3/Mustache/Autoloader.php @@ -0,0 +1,88 @@ +baseDir = $realDir; + } else { + $this->baseDir = $baseDir; + } + } + + /** + * Register a new instance as an SPL autoloader. + * + * @param string $baseDir Mustache library base directory (default: dirname(__FILE__).'/..') + * + * @return Mustache_Autoloader Registered Autoloader instance + */ + public static function register($baseDir = null) + { + $key = $baseDir ? $baseDir : 0; + + if (!isset(self::$instances[$key])) { + self::$instances[$key] = new self($baseDir); + } + + $loader = self::$instances[$key]; + spl_autoload_register(array($loader, 'autoload')); + + return $loader; + } + + /** + * Autoload Mustache classes. + * + * @param string $class + */ + public function autoload($class) + { + if ($class[0] === '\\') { + $class = substr($class, 1); + } + + if (strpos($class, 'Mustache') !== 0) { + return; + } + + $file = sprintf('%s/%s.php', $this->baseDir, str_replace('_', '/', $class)); + if (is_file($file)) { + require $file; + } + } +} diff --git a/os3/Mustache/Cache.php b/os3/Mustache/Cache.php new file mode 100644 index 0000000..3292efa --- /dev/null +++ b/os3/Mustache/Cache.php @@ -0,0 +1,43 @@ +logger; + } + + /** + * Set a logger instance. + * + * @param Mustache_Logger|Psr\Log\LoggerInterface $logger + */ + public function setLogger($logger = null) + { + if ($logger !== null && !($logger instanceof Mustache_Logger || is_a($logger, 'Psr\\Log\\LoggerInterface'))) { + throw new Mustache_Exception_InvalidArgumentException('Expected an instance of Mustache_Logger or Psr\\Log\\LoggerInterface.'); + } + + $this->logger = $logger; + } + + /** + * Add a log record if logging is enabled. + * + * @param string $level The logging level + * @param string $message The log message + * @param array $context The log context + */ + protected function log($level, $message, array $context = array()) + { + if (isset($this->logger)) { + $this->logger->log($level, $message, $context); + } + } +} diff --git a/os3/Mustache/Cache/FilesystemCache.php b/os3/Mustache/Cache/FilesystemCache.php new file mode 100644 index 0000000..3e742b7 --- /dev/null +++ b/os3/Mustache/Cache/FilesystemCache.php @@ -0,0 +1,161 @@ +cache($className, $compiledSource); + * + * The FilesystemCache benefits from any opcode caching that may be setup in your environment. So do that, k? + */ +class Mustache_Cache_FilesystemCache extends Mustache_Cache_AbstractCache +{ + private $baseDir; + private $fileMode; + + /** + * Filesystem cache constructor. + * + * @param string $baseDir Directory for compiled templates + * @param int $fileMode Override default permissions for cache files. Defaults to using the system-defined umask + */ + public function __construct($baseDir, $fileMode = null) + { + $this->baseDir = $baseDir; + $this->fileMode = $fileMode; + } + + /** + * Load the class from cache using `require_once`. + * + * @param string $key + * + * @return bool + */ + public function load($key) + { + $fileName = $this->getCacheFilename($key); + if (!is_file($fileName)) { + return false; + } + + require_once $fileName; + + return true; + } + + /** + * Cache and load the compiled class. + * + * @param string $key + * @param string $value + */ + public function cache($key, $value) + { + $fileName = $this->getCacheFilename($key); + + $this->log( + Mustache_Logger::DEBUG, + 'Writing to template cache: "{fileName}"', + array('fileName' => $fileName) + ); + + $this->writeFile($fileName, $value); + $this->load($key); + } + + /** + * Build the cache filename. + * Subclasses should override for custom cache directory structures. + * + * @param string $name + * + * @return string + */ + protected function getCacheFilename($name) + { + return sprintf('%s/%s.php', $this->baseDir, $name); + } + + /** + * Create cache directory. + * + * @throws Mustache_Exception_RuntimeException If unable to create directory + * + * @param string $fileName + * + * @return string + */ + private function buildDirectoryForFilename($fileName) + { + $dirName = dirname($fileName); + if (!is_dir($dirName)) { + $this->log( + Mustache_Logger::INFO, + 'Creating Mustache template cache directory: "{dirName}"', + array('dirName' => $dirName) + ); + + @mkdir($dirName, 0777, true); + // @codeCoverageIgnoreStart + if (!is_dir($dirName)) { + throw new Mustache_Exception_RuntimeException(sprintf('Failed to create cache directory "%s".', $dirName)); + } + // @codeCoverageIgnoreEnd + } + + return $dirName; + } + + /** + * Write cache file. + * + * @throws Mustache_Exception_RuntimeException If unable to write file + * + * @param string $fileName + * @param string $value + */ + private function writeFile($fileName, $value) + { + $dirName = $this->buildDirectoryForFilename($fileName); + + $this->log( + Mustache_Logger::DEBUG, + 'Caching compiled template to "{fileName}"', + array('fileName' => $fileName) + ); + + $tempFile = tempnam($dirName, basename($fileName)); + if (false !== @file_put_contents($tempFile, $value)) { + if (@rename($tempFile, $fileName)) { + $mode = isset($this->fileMode) ? $this->fileMode : (0666 & ~umask()); + @chmod($fileName, $mode); + + return; + } + + // @codeCoverageIgnoreStart + $this->log( + Mustache_Logger::ERROR, + 'Unable to rename Mustache temp cache file: "{tempName}" -> "{fileName}"', + array('tempName' => $tempFile, 'fileName' => $fileName) + ); + // @codeCoverageIgnoreEnd + } + + // @codeCoverageIgnoreStart + throw new Mustache_Exception_RuntimeException(sprintf('Failed to write cache file "%s".', $fileName)); + // @codeCoverageIgnoreEnd + } +} diff --git a/os3/Mustache/Cache/NoopCache.php b/os3/Mustache/Cache/NoopCache.php new file mode 100644 index 0000000..ed9eec9 --- /dev/null +++ b/os3/Mustache/Cache/NoopCache.php @@ -0,0 +1,47 @@ +log( + Mustache_Logger::WARNING, + 'Template cache disabled, evaluating "{className}" class at runtime', + array('className' => $key) + ); + eval('?>' . $value); + } +} diff --git a/os3/Mustache/Compiler.php b/os3/Mustache/Compiler.php new file mode 100644 index 0000000..2b0d1f9 --- /dev/null +++ b/os3/Mustache/Compiler.php @@ -0,0 +1,718 @@ +pragmas = $this->defaultPragmas; + $this->sections = array(); + $this->blocks = array(); + $this->source = $source; + $this->indentNextLine = true; + $this->customEscape = $customEscape; + $this->entityFlags = $entityFlags; + $this->charset = $charset; + $this->strictCallables = $strictCallables; + + return $this->writeCode($tree, $name); + } + + /** + * Enable pragmas across all templates, regardless of the presence of pragma + * tags in the individual templates. + * + * @internal Users should set global pragmas in Mustache_Engine, not here :) + * + * @param string[] $pragmas + */ + public function setPragmas(array $pragmas) + { + $this->pragmas = array(); + foreach ($pragmas as $pragma) { + $this->pragmas[$pragma] = true; + } + $this->defaultPragmas = $this->pragmas; + } + + /** + * Helper function for walking the Mustache token parse tree. + * + * @throws Mustache_Exception_SyntaxException upon encountering unknown token types + * + * @param array $tree Parse tree of Mustache tokens + * @param int $level (default: 0) + * + * @return string Generated PHP source code + */ + private function walk(array $tree, $level = 0) + { + $code = ''; + $level++; + foreach ($tree as $node) { + switch ($node[Mustache_Tokenizer::TYPE]) { + case Mustache_Tokenizer::T_PRAGMA: + $this->pragmas[$node[Mustache_Tokenizer::NAME]] = true; + break; + + case Mustache_Tokenizer::T_SECTION: + $code .= $this->section( + $node[Mustache_Tokenizer::NODES], + $node[Mustache_Tokenizer::NAME], + isset($node[Mustache_Tokenizer::FILTERS]) ? $node[Mustache_Tokenizer::FILTERS] : array(), + $node[Mustache_Tokenizer::INDEX], + $node[Mustache_Tokenizer::END], + $node[Mustache_Tokenizer::OTAG], + $node[Mustache_Tokenizer::CTAG], + $level + ); + break; + + case Mustache_Tokenizer::T_INVERTED: + $code .= $this->invertedSection( + $node[Mustache_Tokenizer::NODES], + $node[Mustache_Tokenizer::NAME], + isset($node[Mustache_Tokenizer::FILTERS]) ? $node[Mustache_Tokenizer::FILTERS] : array(), + $level + ); + break; + + case Mustache_Tokenizer::T_PARTIAL: + $code .= $this->partial( + $node[Mustache_Tokenizer::NAME], + isset($node[Mustache_Tokenizer::DYNAMIC]) ? $node[Mustache_Tokenizer::DYNAMIC] : false, + isset($node[Mustache_Tokenizer::INDENT]) ? $node[Mustache_Tokenizer::INDENT] : '', + $level + ); + break; + + case Mustache_Tokenizer::T_PARENT: + $code .= $this->parent( + $node[Mustache_Tokenizer::NAME], + isset($node[Mustache_Tokenizer::DYNAMIC]) ? $node[Mustache_Tokenizer::DYNAMIC] : false, + isset($node[Mustache_Tokenizer::INDENT]) ? $node[Mustache_Tokenizer::INDENT] : '', + $node[Mustache_Tokenizer::NODES], + $level + ); + break; + + case Mustache_Tokenizer::T_BLOCK_ARG: + $code .= $this->blockArg( + $node[Mustache_Tokenizer::NODES], + $node[Mustache_Tokenizer::NAME], + $node[Mustache_Tokenizer::INDEX], + $node[Mustache_Tokenizer::END], + $node[Mustache_Tokenizer::OTAG], + $node[Mustache_Tokenizer::CTAG], + $level + ); + break; + + case Mustache_Tokenizer::T_BLOCK_VAR: + $code .= $this->blockVar( + $node[Mustache_Tokenizer::NODES], + $node[Mustache_Tokenizer::NAME], + $node[Mustache_Tokenizer::INDEX], + $node[Mustache_Tokenizer::END], + $node[Mustache_Tokenizer::OTAG], + $node[Mustache_Tokenizer::CTAG], + $level + ); + break; + + case Mustache_Tokenizer::T_COMMENT: + break; + + case Mustache_Tokenizer::T_ESCAPED: + case Mustache_Tokenizer::T_UNESCAPED: + case Mustache_Tokenizer::T_UNESCAPED_2: + $code .= $this->variable( + $node[Mustache_Tokenizer::NAME], + isset($node[Mustache_Tokenizer::FILTERS]) ? $node[Mustache_Tokenizer::FILTERS] : array(), + $node[Mustache_Tokenizer::TYPE] === Mustache_Tokenizer::T_ESCAPED, + $level + ); + break; + + case Mustache_Tokenizer::T_TEXT: + $code .= $this->text($node[Mustache_Tokenizer::VALUE], $level); + break; + + default: + throw new Mustache_Exception_SyntaxException(sprintf('Unknown token type: %s', $node[Mustache_Tokenizer::TYPE]), $node); + } + } + + return $code; + } + + const KLASS = 'lambdaHelper = new Mustache_LambdaHelper($this->mustache, $context); + $buffer = \'\'; + %s + + return $buffer; + } + %s + %s + }'; + + const KLASS_NO_LAMBDAS = 'walk($tree); + $sections = implode("\n", $this->sections); + $blocks = implode("\n", $this->blocks); + $klass = empty($this->sections) && empty($this->blocks) ? self::KLASS_NO_LAMBDAS : self::KLASS; + + $callable = $this->strictCallables ? $this->prepare(self::STRICT_CALLABLE) : ''; + + return sprintf($this->prepare($klass, 0, false, true), $name, $callable, $code, $sections, $blocks); + } + + const BLOCK_VAR = ' + $blockFunction = $context->findInBlock(%s); + if (is_callable($blockFunction)) { + $buffer .= call_user_func($blockFunction, $context); + %s} + '; + + const BLOCK_VAR_ELSE = '} else {%s'; + + /** + * Generate Mustache Template inheritance block variable PHP source. + * + * @param array $nodes Array of child tokens + * @param string $id Section name + * @param int $start Section start offset + * @param int $end Section end offset + * @param string $otag Current Mustache opening tag + * @param string $ctag Current Mustache closing tag + * @param int $level + * + * @return string Generated PHP source code + */ + private function blockVar($nodes, $id, $start, $end, $otag, $ctag, $level) + { + $id = var_export($id, true); + + $else = $this->walk($nodes, $level); + if ($else !== '') { + $else = sprintf($this->prepare(self::BLOCK_VAR_ELSE, $level + 1, false, true), $else); + } + + return sprintf($this->prepare(self::BLOCK_VAR, $level), $id, $else); + } + + const BLOCK_ARG = '%s => array($this, \'block%s\'),'; + + /** + * Generate Mustache Template inheritance block argument PHP source. + * + * @param array $nodes Array of child tokens + * @param string $id Section name + * @param int $start Section start offset + * @param int $end Section end offset + * @param string $otag Current Mustache opening tag + * @param string $ctag Current Mustache closing tag + * @param int $level + * + * @return string Generated PHP source code + */ + private function blockArg($nodes, $id, $start, $end, $otag, $ctag, $level) + { + $key = $this->block($nodes); + $id = var_export($id, true); + + return sprintf($this->prepare(self::BLOCK_ARG, $level), $id, $key); + } + + const BLOCK_FUNCTION = ' + public function block%s($context) + { + $indent = $buffer = \'\';%s + + return $buffer; + } + '; + + /** + * Generate Mustache Template inheritance block function PHP source. + * + * @param array $nodes Array of child tokens + * + * @return string key of new block function + */ + private function block($nodes) + { + $code = $this->walk($nodes, 0); + $key = ucfirst(md5($code)); + + if (!isset($this->blocks[$key])) { + $this->blocks[$key] = sprintf($this->prepare(self::BLOCK_FUNCTION, 0), $key, $code); + } + + return $key; + } + + const SECTION_CALL = ' + $value = $context->%s(%s);%s + $buffer .= $this->section%s($context, $indent, $value); + '; + + const SECTION = ' + private function section%s(Mustache_Context $context, $indent, $value) + { + $buffer = \'\'; + + if (%s) { + $source = %s; + $result = (string) call_user_func($value, $source, %s); + if (strpos($result, \'{{\') === false) { + $buffer .= $result; + } else { + $buffer .= $this->mustache + ->loadLambda($result%s) + ->renderInternal($context); + } + } elseif (!empty($value)) { + $values = $this->isIterable($value) ? $value : array($value); + foreach ($values as $value) { + $context->push($value); + %s + $context->pop(); + } + } + + return $buffer; + } + '; + + /** + * Generate Mustache Template section PHP source. + * + * @param array $nodes Array of child tokens + * @param string $id Section name + * @param string[] $filters Array of filters + * @param int $start Section start offset + * @param int $end Section end offset + * @param string $otag Current Mustache opening tag + * @param string $ctag Current Mustache closing tag + * @param int $level + * + * @return string Generated section PHP source code + */ + private function section($nodes, $id, $filters, $start, $end, $otag, $ctag, $level) + { + $source = var_export(substr($this->source, $start, $end - $start), true); + $callable = $this->getCallable(); + + if ($otag !== '{{' || $ctag !== '}}') { + $delimTag = var_export(sprintf('{{= %s %s =}}', $otag, $ctag), true); + $helper = sprintf('$this->lambdaHelper->withDelimiters(%s)', $delimTag); + $delims = ', ' . $delimTag; + } else { + $helper = '$this->lambdaHelper'; + $delims = ''; + } + + $key = ucfirst(md5($delims . "\n" . $source)); + + if (!isset($this->sections[$key])) { + $this->sections[$key] = sprintf($this->prepare(self::SECTION), $key, $callable, $source, $helper, $delims, $this->walk($nodes, 2)); + } + + $method = $this->getFindMethod($id); + $id = var_export($id, true); + $filters = $this->getFilters($filters, $level); + + return sprintf($this->prepare(self::SECTION_CALL, $level), $method, $id, $filters, $key); + } + + const INVERTED_SECTION = ' + $value = $context->%s(%s);%s + if (empty($value)) { + %s + } + '; + + /** + * Generate Mustache Template inverted section PHP source. + * + * @param array $nodes Array of child tokens + * @param string $id Section name + * @param string[] $filters Array of filters + * @param int $level + * + * @return string Generated inverted section PHP source code + */ + private function invertedSection($nodes, $id, $filters, $level) + { + $method = $this->getFindMethod($id); + $id = var_export($id, true); + $filters = $this->getFilters($filters, $level); + + return sprintf($this->prepare(self::INVERTED_SECTION, $level), $method, $id, $filters, $this->walk($nodes, $level)); + } + + const DYNAMIC_NAME = '$this->resolveValue($context->%s(%s), $context)'; + + /** + * Generate Mustache Template dynamic name resolution PHP source. + * + * @param string $id Tag name + * @param bool $dynamic True if the name is dynamic + * + * @return string Dynamic name resolution PHP source code + */ + private function resolveDynamicName($id, $dynamic) + { + if (!$dynamic) { + return var_export($id, true); + } + + $method = $this->getFindMethod($id); + $id = ($method !== 'last') ? var_export($id, true) : ''; + + // TODO: filters? + + return sprintf(self::DYNAMIC_NAME, $method, $id); + } + + const PARTIAL_INDENT = ', $indent . %s'; + const PARTIAL = ' + if ($partial = $this->mustache->loadPartial(%s)) { + $buffer .= $partial->renderInternal($context%s); + } + '; + + /** + * Generate Mustache Template partial call PHP source. + * + * @param string $id Partial name + * @param bool $dynamic Partial name is dynamic + * @param string $indent Whitespace indent to apply to partial + * @param int $level + * + * @return string Generated partial call PHP source code + */ + private function partial($id, $dynamic, $indent, $level) + { + if ($indent !== '') { + $indentParam = sprintf(self::PARTIAL_INDENT, var_export($indent, true)); + } else { + $indentParam = ''; + } + + return sprintf( + $this->prepare(self::PARTIAL, $level), + $this->resolveDynamicName($id, $dynamic), + $indentParam + ); + } + + const PARENT = ' + if ($parent = $this->mustache->loadPartial(%s)) { + $context->pushBlockContext(array(%s + )); + $buffer .= $parent->renderInternal($context, $indent); + $context->popBlockContext(); + } + '; + + const PARENT_NO_CONTEXT = ' + if ($parent = $this->mustache->loadPartial(%s)) { + $buffer .= $parent->renderInternal($context, $indent); + } + '; + + /** + * Generate Mustache Template inheritance parent call PHP source. + * + * @param string $id Parent tag name + * @param bool $dynamic Tag name is dynamic + * @param string $indent Whitespace indent to apply to parent + * @param array $children Child nodes + * @param int $level + * + * @return string Generated PHP source code + */ + private function parent($id, $dynamic, $indent, array $children, $level) + { + $realChildren = array_filter($children, array(__CLASS__, 'onlyBlockArgs')); + $partialName = $this->resolveDynamicName($id, $dynamic); + + if (empty($realChildren)) { + return sprintf($this->prepare(self::PARENT_NO_CONTEXT, $level), $partialName); + } + + return sprintf( + $this->prepare(self::PARENT, $level), + $partialName, + $this->walk($realChildren, $level + 1) + ); + } + + /** + * Helper method for filtering out non-block-arg tokens. + * + * @param array $node + * + * @return bool True if $node is a block arg token + */ + private static function onlyBlockArgs(array $node) + { + return $node[Mustache_Tokenizer::TYPE] === Mustache_Tokenizer::T_BLOCK_ARG; + } + + const VARIABLE = ' + $value = $this->resolveValue($context->%s(%s), $context);%s + $buffer .= %s($value === null ? \'\' : %s); + '; + + /** + * Generate Mustache Template variable interpolation PHP source. + * + * @param string $id Variable name + * @param string[] $filters Array of filters + * @param bool $escape Escape the variable value for output? + * @param int $level + * + * @return string Generated variable interpolation PHP source + */ + private function variable($id, $filters, $escape, $level) + { + $method = $this->getFindMethod($id); + $id = ($method !== 'last') ? var_export($id, true) : ''; + $filters = $this->getFilters($filters, $level); + $value = $escape ? $this->getEscape() : '$value'; + + return sprintf($this->prepare(self::VARIABLE, $level), $method, $id, $filters, $this->flushIndent(), $value); + } + + const FILTER = ' + $filter = $context->%s(%s); + if (!(%s)) { + throw new Mustache_Exception_UnknownFilterException(%s); + } + $value = call_user_func($filter, $value);%s + '; + + /** + * Generate Mustache Template variable filtering PHP source. + * + * @param string[] $filters Array of filters + * @param int $level + * + * @return string Generated filter PHP source + */ + private function getFilters(array $filters, $level) + { + if (empty($filters)) { + return ''; + } + + $name = array_shift($filters); + $method = $this->getFindMethod($name); + $filter = ($method !== 'last') ? var_export($name, true) : ''; + $callable = $this->getCallable('$filter'); + $msg = var_export($name, true); + + return sprintf($this->prepare(self::FILTER, $level), $method, $filter, $callable, $msg, $this->getFilters($filters, $level)); + } + + const LINE = '$buffer .= "\n";'; + const TEXT = '$buffer .= %s%s;'; + + /** + * Generate Mustache Template output Buffer call PHP source. + * + * @param string $text + * @param int $level + * + * @return string Generated output Buffer call PHP source + */ + private function text($text, $level) + { + $indentNextLine = (substr($text, -1) === "\n"); + $code = sprintf($this->prepare(self::TEXT, $level), $this->flushIndent(), var_export($text, true)); + $this->indentNextLine = $indentNextLine; + + return $code; + } + + /** + * Prepare PHP source code snippet for output. + * + * @param string $text + * @param int $bonus Additional indent level (default: 0) + * @param bool $prependNewline Prepend a newline to the snippet? (default: true) + * @param bool $appendNewline Append a newline to the snippet? (default: false) + * + * @return string PHP source code snippet + */ + private function prepare($text, $bonus = 0, $prependNewline = true, $appendNewline = false) + { + $text = ($prependNewline ? "\n" : '') . trim($text); + if ($prependNewline) { + $bonus++; + } + if ($appendNewline) { + $text .= "\n"; + } + + return preg_replace("/\n( {8})?/", "\n" . str_repeat(' ', $bonus * 4), $text); + } + + const DEFAULT_ESCAPE = 'htmlspecialchars(%s, %s, %s)'; + const CUSTOM_ESCAPE = 'call_user_func($this->mustache->getEscape(), %s)'; + + /** + * Get the current escaper. + * + * @param string $value (default: '$value') + * + * @return string Either a custom callback, or an inline call to `htmlspecialchars` + */ + private function getEscape($value = '$value') + { + if ($this->customEscape) { + return sprintf(self::CUSTOM_ESCAPE, $value); + } + + return sprintf(self::DEFAULT_ESCAPE, $value, var_export($this->entityFlags, true), var_export($this->charset, true)); + } + + /** + * Select the appropriate Context `find` method for a given $id. + * + * The return value will be one of `find`, `findDot`, `findAnchoredDot` or `last`. + * + * @see Mustache_Context::find + * @see Mustache_Context::findDot + * @see Mustache_Context::last + * + * @param string $id Variable name + * + * @return string `find` method name + */ + private function getFindMethod($id) + { + if ($id === '.') { + return 'last'; + } + + if (isset($this->pragmas[Mustache_Engine::PRAGMA_ANCHORED_DOT]) && $this->pragmas[Mustache_Engine::PRAGMA_ANCHORED_DOT]) { + if (substr($id, 0, 1) === '.') { + return 'findAnchoredDot'; + } + } + + if (strpos($id, '.') === false) { + return 'find'; + } + + return 'findDot'; + } + + const IS_CALLABLE = '!is_string(%s) && is_callable(%s)'; + const STRICT_IS_CALLABLE = 'is_object(%s) && is_callable(%s)'; + + /** + * Helper function to compile strict vs lax "is callable" logic. + * + * @param string $variable (default: '$value') + * + * @return string "is callable" logic + */ + private function getCallable($variable = '$value') + { + $tpl = $this->strictCallables ? self::STRICT_IS_CALLABLE : self::IS_CALLABLE; + + return sprintf($tpl, $variable, $variable); + } + + const LINE_INDENT = '$indent . '; + + /** + * Get the current $indent prefix to write to the buffer. + * + * @return string "$indent . " or "" + */ + private function flushIndent() + { + if (!$this->indentNextLine) { + return ''; + } + + $this->indentNextLine = false; + + return self::LINE_INDENT; + } +} diff --git a/os3/Mustache/Context.php b/os3/Mustache/Context.php new file mode 100644 index 0000000..69c02e0 --- /dev/null +++ b/os3/Mustache/Context.php @@ -0,0 +1,242 @@ +stack = array($context); + } + } + + /** + * Push a new Context frame onto the stack. + * + * @param mixed $value Object or array to use for context + */ + public function push($value) + { + array_push($this->stack, $value); + } + + /** + * Push a new Context frame onto the block context stack. + * + * @param mixed $value Object or array to use for block context + */ + public function pushBlockContext($value) + { + array_push($this->blockStack, $value); + } + + /** + * Pop the last Context frame from the stack. + * + * @return mixed Last Context frame (object or array) + */ + public function pop() + { + return array_pop($this->stack); + } + + /** + * Pop the last block Context frame from the stack. + * + * @return mixed Last block Context frame (object or array) + */ + public function popBlockContext() + { + return array_pop($this->blockStack); + } + + /** + * Get the last Context frame. + * + * @return mixed Last Context frame (object or array) + */ + public function last() + { + return end($this->stack); + } + + /** + * Find a variable in the Context stack. + * + * Starting with the last Context frame (the context of the innermost section), and working back to the top-level + * rendering context, look for a variable with the given name: + * + * * If the Context frame is an associative array which contains the key $id, returns the value of that element. + * * If the Context frame is an object, this will check first for a public method, then a public property named + * $id. Failing both of these, it will try `__isset` and `__get` magic methods. + * * If a value named $id is not found in any Context frame, returns an empty string. + * + * @param string $id Variable name + * + * @return mixed Variable value, or '' if not found + */ + public function find($id) + { + return $this->findVariableInStack($id, $this->stack); + } + + /** + * Find a 'dot notation' variable in the Context stack. + * + * Note that dot notation traversal bubbles through scope differently than the regular find method. After finding + * the initial chunk of the dotted name, each subsequent chunk is searched for only within the value of the previous + * result. For example, given the following context stack: + * + * $data = array( + * 'name' => 'Fred', + * 'child' => array( + * 'name' => 'Bob' + * ), + * ); + * + * ... and the Mustache following template: + * + * {{ child.name }} + * + * ... the `name` value is only searched for within the `child` value of the global Context, not within parent + * Context frames. + * + * @param string $id Dotted variable selector + * + * @return mixed Variable value, or '' if not found + */ + public function findDot($id) + { + $chunks = explode('.', $id); + $first = array_shift($chunks); + $value = $this->findVariableInStack($first, $this->stack); + + foreach ($chunks as $chunk) { + if ($value === '') { + return $value; + } + + $value = $this->findVariableInStack($chunk, array($value)); + } + + return $value; + } + + /** + * Find an 'anchored dot notation' variable in the Context stack. + * + * This is the same as findDot(), except it looks in the top of the context + * stack for the first value, rather than searching the whole context stack + * and starting from there. + * + * @see Mustache_Context::findDot + * + * @throws Mustache_Exception_InvalidArgumentException if given an invalid anchored dot $id + * + * @param string $id Dotted variable selector + * + * @return mixed Variable value, or '' if not found + */ + public function findAnchoredDot($id) + { + $chunks = explode('.', $id); + $first = array_shift($chunks); + if ($first !== '') { + throw new Mustache_Exception_InvalidArgumentException(sprintf('Unexpected id for findAnchoredDot: %s', $id)); + } + + $value = $this->last(); + + foreach ($chunks as $chunk) { + if ($value === '') { + return $value; + } + + $value = $this->findVariableInStack($chunk, array($value)); + } + + return $value; + } + + /** + * Find an argument in the block context stack. + * + * @param string $id + * + * @return mixed Variable value, or '' if not found + */ + public function findInBlock($id) + { + foreach ($this->blockStack as $context) { + if (array_key_exists($id, $context)) { + return $context[$id]; + } + } + + return ''; + } + + /** + * Helper function to find a variable in the Context stack. + * + * @see Mustache_Context::find + * + * @param string $id Variable name + * @param array $stack Context stack + * + * @return mixed Variable value, or '' if not found + */ + private function findVariableInStack($id, array $stack) + { + for ($i = count($stack) - 1; $i >= 0; $i--) { + $frame = &$stack[$i]; + + switch (gettype($frame)) { + case 'object': + if (!($frame instanceof Closure)) { + // Note that is_callable() *will not work here* + // See https://github.com/bobthecow/mustache.php/wiki/Magic-Methods + if (method_exists($frame, $id)) { + return $frame->$id(); + } + + if (isset($frame->$id)) { + return $frame->$id; + } + + if ($frame instanceof ArrayAccess && isset($frame[$id])) { + return $frame[$id]; + } + } + break; + + case 'array': + if (array_key_exists($id, $frame)) { + return $frame[$id]; + } + break; + } + } + + return ''; + } +} diff --git a/os3/Mustache/Engine.php b/os3/Mustache/Engine.php new file mode 100644 index 0000000..7a31ac0 --- /dev/null +++ b/os3/Mustache/Engine.php @@ -0,0 +1,831 @@ + true, + self::PRAGMA_BLOCKS => true, + self::PRAGMA_ANCHORED_DOT => true, + self::PRAGMA_DYNAMIC_NAMES => true, + ); + + // Template cache + private $templates = array(); + + // Environment + private $templateClassPrefix = '__Mustache_'; + private $cache; + private $lambdaCache; + private $cacheLambdaTemplates = false; + private $loader; + private $partialsLoader; + private $helpers; + private $escape; + private $entityFlags = ENT_COMPAT; + private $charset = 'UTF-8'; + private $logger; + private $strictCallables = false; + private $pragmas = array(); + private $delimiters; + + // Services + private $tokenizer; + private $parser; + private $compiler; + + /** + * Mustache class constructor. + * + * Passing an $options array allows overriding certain Mustache options during instantiation: + * + * $options = array( + * // The class prefix for compiled templates. Defaults to '__Mustache_'. + * 'template_class_prefix' => '__MyTemplates_', + * + * // A Mustache cache instance or a cache directory string for compiled templates. + * // Mustache will not cache templates unless this is set. + * 'cache' => dirname(__FILE__).'/tmp/cache/mustache', + * + * // Override default permissions for cache files. Defaults to using the system-defined umask. It is + * // *strongly* recommended that you configure your umask properly rather than overriding permissions here. + * 'cache_file_mode' => 0666, + * + * // Optionally, enable caching for lambda section templates. This is generally not recommended, as lambda + * // sections are often too dynamic to benefit from caching. + * 'cache_lambda_templates' => true, + * + * // Customize the tag delimiters used by this engine instance. Note that overriding here changes the + * // delimiters used to parse all templates and partials loaded by this instance. To override just for a + * // single template, use an inline "change delimiters" tag at the start of the template file: + * // + * // {{=<% %>=}} + * // + * 'delimiters' => '<% %>', + * + * // A Mustache template loader instance. Uses a StringLoader if not specified. + * 'loader' => new Mustache_Loader_FilesystemLoader(dirname(__FILE__).'/views'), + * + * // A Mustache loader instance for partials. + * 'partials_loader' => new Mustache_Loader_FilesystemLoader(dirname(__FILE__).'/views/partials'), + * + * // An array of Mustache partials. Useful for quick-and-dirty string template loading, but not as + * // efficient or lazy as a Filesystem (or database) loader. + * 'partials' => array('foo' => file_get_contents(dirname(__FILE__).'/views/partials/foo.mustache')), + * + * // An array of 'helpers'. Helpers can be global variables or objects, closures (e.g. for higher order + * // sections), or any other valid Mustache context value. They will be prepended to the context stack, + * // so they will be available in any template loaded by this Mustache instance. + * 'helpers' => array('i18n' => function ($text) { + * // do something translatey here... + * }), + * + * // An 'escape' callback, responsible for escaping double-mustache variables. + * 'escape' => function ($value) { + * return htmlspecialchars($buffer, ENT_COMPAT, 'UTF-8'); + * }, + * + * // Type argument for `htmlspecialchars`. Defaults to ENT_COMPAT. You may prefer ENT_QUOTES. + * 'entity_flags' => ENT_QUOTES, + * + * // Character set for `htmlspecialchars`. Defaults to 'UTF-8'. Use 'UTF-8'. + * 'charset' => 'ISO-8859-1', + * + * // A Mustache Logger instance. No logging will occur unless this is set. Using a PSR-3 compatible + * // logging library -- such as Monolog -- is highly recommended. A simple stream logger implementation is + * // available as well: + * 'logger' => new Mustache_Logger_StreamLogger('php://stderr'), + * + * // Only treat Closure instances and invokable classes as callable. If true, values like + * // `array('ClassName', 'methodName')` and `array($classInstance, 'methodName')`, which are traditionally + * // "callable" in PHP, are not called to resolve variables for interpolation or section contexts. This + * // helps protect against arbitrary code execution when user input is passed directly into the template. + * // This currently defaults to false, but will default to true in v3.0. + * 'strict_callables' => true, + * + * // Enable pragmas across all templates, regardless of the presence of pragma tags in the individual + * // templates. + * 'pragmas' => [Mustache_Engine::PRAGMA_FILTERS], + * ); + * + * @throws Mustache_Exception_InvalidArgumentException If `escape` option is not callable + * + * @param array $options (default: array()) + */ + public function __construct(array $options = array()) + { + if (isset($options['template_class_prefix'])) { + if ((string) $options['template_class_prefix'] === '') { + throw new Mustache_Exception_InvalidArgumentException('Mustache Constructor "template_class_prefix" must not be empty'); + } + + $this->templateClassPrefix = $options['template_class_prefix']; + } + + if (isset($options['cache'])) { + $cache = $options['cache']; + + if (is_string($cache)) { + $mode = isset($options['cache_file_mode']) ? $options['cache_file_mode'] : null; + $cache = new Mustache_Cache_FilesystemCache($cache, $mode); + } + + $this->setCache($cache); + } + + if (isset($options['cache_lambda_templates'])) { + $this->cacheLambdaTemplates = (bool) $options['cache_lambda_templates']; + } + + if (isset($options['loader'])) { + $this->setLoader($options['loader']); + } + + if (isset($options['partials_loader'])) { + $this->setPartialsLoader($options['partials_loader']); + } + + if (isset($options['partials'])) { + $this->setPartials($options['partials']); + } + + if (isset($options['helpers'])) { + $this->setHelpers($options['helpers']); + } + + if (isset($options['escape'])) { + if (!is_callable($options['escape'])) { + throw new Mustache_Exception_InvalidArgumentException('Mustache Constructor "escape" option must be callable'); + } + + $this->escape = $options['escape']; + } + + if (isset($options['entity_flags'])) { + $this->entityFlags = $options['entity_flags']; + } + + if (isset($options['charset'])) { + $this->charset = $options['charset']; + } + + if (isset($options['logger'])) { + $this->setLogger($options['logger']); + } + + if (isset($options['strict_callables'])) { + $this->strictCallables = $options['strict_callables']; + } + + if (isset($options['delimiters'])) { + $this->delimiters = $options['delimiters']; + } + + if (isset($options['pragmas'])) { + foreach ($options['pragmas'] as $pragma) { + if (!isset(self::$knownPragmas[$pragma])) { + throw new Mustache_Exception_InvalidArgumentException(sprintf('Unknown pragma: "%s".', $pragma)); + } + $this->pragmas[$pragma] = true; + } + } + } + + /** + * Shortcut 'render' invocation. + * + * Equivalent to calling `$mustache->loadTemplate($template)->render($context);` + * + * @see Mustache_Engine::loadTemplate + * @see Mustache_Template::render + * + * @param string $template + * @param mixed $context (default: array()) + * + * @return string Rendered template + */ + public function render($template, $context = array()) + { + return $this->loadTemplate($template)->render($context); + } + + /** + * Get the current Mustache escape callback. + * + * @return callable|null + */ + public function getEscape() + { + return $this->escape; + } + + /** + * Get the current Mustache entitity type to escape. + * + * @return int + */ + public function getEntityFlags() + { + return $this->entityFlags; + } + + /** + * Get the current Mustache character set. + * + * @return string + */ + public function getCharset() + { + return $this->charset; + } + + /** + * Get the current globally enabled pragmas. + * + * @return array + */ + public function getPragmas() + { + return array_keys($this->pragmas); + } + + /** + * Set the Mustache template Loader instance. + * + * @param Mustache_Loader $loader + */ + public function setLoader(Mustache_Loader $loader) + { + $this->loader = $loader; + } + + /** + * Get the current Mustache template Loader instance. + * + * If no Loader instance has been explicitly specified, this method will instantiate and return + * a StringLoader instance. + * + * @return Mustache_Loader + */ + public function getLoader() + { + if (!isset($this->loader)) { + $this->loader = new Mustache_Loader_StringLoader(); + } + + return $this->loader; + } + + /** + * Set the Mustache partials Loader instance. + * + * @param Mustache_Loader $partialsLoader + */ + public function setPartialsLoader(Mustache_Loader $partialsLoader) + { + $this->partialsLoader = $partialsLoader; + } + + /** + * Get the current Mustache partials Loader instance. + * + * If no Loader instance has been explicitly specified, this method will instantiate and return + * an ArrayLoader instance. + * + * @return Mustache_Loader + */ + public function getPartialsLoader() + { + if (!isset($this->partialsLoader)) { + $this->partialsLoader = new Mustache_Loader_ArrayLoader(); + } + + return $this->partialsLoader; + } + + /** + * Set partials for the current partials Loader instance. + * + * @throws Mustache_Exception_RuntimeException If the current Loader instance is immutable + * + * @param array $partials (default: array()) + */ + public function setPartials(array $partials = array()) + { + if (!isset($this->partialsLoader)) { + $this->partialsLoader = new Mustache_Loader_ArrayLoader(); + } + + if (!$this->partialsLoader instanceof Mustache_Loader_MutableLoader) { + throw new Mustache_Exception_RuntimeException('Unable to set partials on an immutable Mustache Loader instance'); + } + + $this->partialsLoader->setTemplates($partials); + } + + /** + * Set an array of Mustache helpers. + * + * An array of 'helpers'. Helpers can be global variables or objects, closures (e.g. for higher order sections), or + * any other valid Mustache context value. They will be prepended to the context stack, so they will be available in + * any template loaded by this Mustache instance. + * + * @throws Mustache_Exception_InvalidArgumentException if $helpers is not an array or Traversable + * + * @param array|Traversable $helpers + */ + public function setHelpers($helpers) + { + if (!is_array($helpers) && !$helpers instanceof Traversable) { + throw new Mustache_Exception_InvalidArgumentException('setHelpers expects an array of helpers'); + } + + $this->getHelpers()->clear(); + + foreach ($helpers as $name => $helper) { + $this->addHelper($name, $helper); + } + } + + /** + * Get the current set of Mustache helpers. + * + * @see Mustache_Engine::setHelpers + * + * @return Mustache_HelperCollection + */ + public function getHelpers() + { + if (!isset($this->helpers)) { + $this->helpers = new Mustache_HelperCollection(); + } + + return $this->helpers; + } + + /** + * Add a new Mustache helper. + * + * @see Mustache_Engine::setHelpers + * + * @param string $name + * @param mixed $helper + */ + public function addHelper($name, $helper) + { + $this->getHelpers()->add($name, $helper); + } + + /** + * Get a Mustache helper by name. + * + * @see Mustache_Engine::setHelpers + * + * @param string $name + * + * @return mixed Helper + */ + public function getHelper($name) + { + return $this->getHelpers()->get($name); + } + + /** + * Check whether this Mustache instance has a helper. + * + * @see Mustache_Engine::setHelpers + * + * @param string $name + * + * @return bool True if the helper is present + */ + public function hasHelper($name) + { + return $this->getHelpers()->has($name); + } + + /** + * Remove a helper by name. + * + * @see Mustache_Engine::setHelpers + * + * @param string $name + */ + public function removeHelper($name) + { + $this->getHelpers()->remove($name); + } + + /** + * Set the Mustache Logger instance. + * + * @throws Mustache_Exception_InvalidArgumentException If logger is not an instance of Mustache_Logger or Psr\Log\LoggerInterface + * + * @param Mustache_Logger|Psr\Log\LoggerInterface $logger + */ + public function setLogger($logger = null) + { + if ($logger !== null && !($logger instanceof Mustache_Logger || is_a($logger, 'Psr\\Log\\LoggerInterface'))) { + throw new Mustache_Exception_InvalidArgumentException('Expected an instance of Mustache_Logger or Psr\\Log\\LoggerInterface.'); + } + + if ($this->getCache()->getLogger() === null) { + $this->getCache()->setLogger($logger); + } + + $this->logger = $logger; + } + + /** + * Get the current Mustache Logger instance. + * + * @return Mustache_Logger|Psr\Log\LoggerInterface + */ + public function getLogger() + { + return $this->logger; + } + + /** + * Set the Mustache Tokenizer instance. + * + * @param Mustache_Tokenizer $tokenizer + */ + public function setTokenizer(Mustache_Tokenizer $tokenizer) + { + $this->tokenizer = $tokenizer; + } + + /** + * Get the current Mustache Tokenizer instance. + * + * If no Tokenizer instance has been explicitly specified, this method will instantiate and return a new one. + * + * @return Mustache_Tokenizer + */ + public function getTokenizer() + { + if (!isset($this->tokenizer)) { + $this->tokenizer = new Mustache_Tokenizer(); + } + + return $this->tokenizer; + } + + /** + * Set the Mustache Parser instance. + * + * @param Mustache_Parser $parser + */ + public function setParser(Mustache_Parser $parser) + { + $this->parser = $parser; + } + + /** + * Get the current Mustache Parser instance. + * + * If no Parser instance has been explicitly specified, this method will instantiate and return a new one. + * + * @return Mustache_Parser + */ + public function getParser() + { + if (!isset($this->parser)) { + $this->parser = new Mustache_Parser(); + } + + return $this->parser; + } + + /** + * Set the Mustache Compiler instance. + * + * @param Mustache_Compiler $compiler + */ + public function setCompiler(Mustache_Compiler $compiler) + { + $this->compiler = $compiler; + } + + /** + * Get the current Mustache Compiler instance. + * + * If no Compiler instance has been explicitly specified, this method will instantiate and return a new one. + * + * @return Mustache_Compiler + */ + public function getCompiler() + { + if (!isset($this->compiler)) { + $this->compiler = new Mustache_Compiler(); + } + + return $this->compiler; + } + + /** + * Set the Mustache Cache instance. + * + * @param Mustache_Cache $cache + */ + public function setCache(Mustache_Cache $cache) + { + if (isset($this->logger) && $cache->getLogger() === null) { + $cache->setLogger($this->getLogger()); + } + + $this->cache = $cache; + } + + /** + * Get the current Mustache Cache instance. + * + * If no Cache instance has been explicitly specified, this method will instantiate and return a new one. + * + * @return Mustache_Cache + */ + public function getCache() + { + if (!isset($this->cache)) { + $this->setCache(new Mustache_Cache_NoopCache()); + } + + return $this->cache; + } + + /** + * Get the current Lambda Cache instance. + * + * If 'cache_lambda_templates' is enabled, this is the default cache instance. Otherwise, it is a NoopCache. + * + * @see Mustache_Engine::getCache + * + * @return Mustache_Cache + */ + protected function getLambdaCache() + { + if ($this->cacheLambdaTemplates) { + return $this->getCache(); + } + + if (!isset($this->lambdaCache)) { + $this->lambdaCache = new Mustache_Cache_NoopCache(); + } + + return $this->lambdaCache; + } + + /** + * Helper method to generate a Mustache template class. + * + * This method must be updated any time options are added which make it so + * the same template could be parsed and compiled multiple different ways. + * + * @param string|Mustache_Source $source + * + * @return string Mustache Template class name + */ + public function getTemplateClassName($source) + { + // For the most part, adding a new option here should do the trick. + // + // Pick a value here which is unique for each possible way the template + // could be compiled... but not necessarily unique per option value. See + // escape below, which only needs to differentiate between 'custom' and + // 'default' escapes. + // + // Keep this list in alphabetical order :) + $chunks = array( + 'charset' => $this->charset, + 'delimiters' => $this->delimiters ? $this->delimiters : '{{ }}', + 'entityFlags' => $this->entityFlags, + 'escape' => isset($this->escape) ? 'custom' : 'default', + 'key' => ($source instanceof Mustache_Source) ? $source->getKey() : 'source', + 'pragmas' => $this->getPragmas(), + 'strictCallables' => $this->strictCallables, + 'version' => self::VERSION, + ); + + $key = json_encode($chunks); + + // Template Source instances have already provided their own source key. For strings, just include the whole + // source string in the md5 hash. + if (!$source instanceof Mustache_Source) { + $key .= "\n" . $source; + } + + return $this->templateClassPrefix . md5($key); + } + + /** + * Load a Mustache Template by name. + * + * @param string $name + * + * @return Mustache_Template + */ + public function loadTemplate($name) + { + return $this->loadSource($this->getLoader()->load($name)); + } + + /** + * Load a Mustache partial Template by name. + * + * This is a helper method used internally by Template instances for loading partial templates. You can most likely + * ignore it completely. + * + * @param string $name + * + * @return Mustache_Template + */ + public function loadPartial($name) + { + try { + if (isset($this->partialsLoader)) { + $loader = $this->partialsLoader; + } elseif (isset($this->loader) && !$this->loader instanceof Mustache_Loader_StringLoader) { + $loader = $this->loader; + } else { + throw new Mustache_Exception_UnknownTemplateException($name); + } + + return $this->loadSource($loader->load($name)); + } catch (Mustache_Exception_UnknownTemplateException $e) { + // If the named partial cannot be found, log then return null. + $this->log( + Mustache_Logger::WARNING, + 'Partial not found: "{name}"', + array('name' => $e->getTemplateName()) + ); + } + } + + /** + * Load a Mustache lambda Template by source. + * + * This is a helper method used by Template instances to generate subtemplates for Lambda sections. You can most + * likely ignore it completely. + * + * @param string $source + * @param string $delims (default: null) + * + * @return Mustache_Template + */ + public function loadLambda($source, $delims = null) + { + if ($delims !== null) { + $source = $delims . "\n" . $source; + } + + return $this->loadSource($source, $this->getLambdaCache()); + } + + /** + * Instantiate and return a Mustache Template instance by source. + * + * Optionally provide a Mustache_Cache instance. This is used internally by Mustache_Engine::loadLambda to respect + * the 'cache_lambda_templates' configuration option. + * + * @see Mustache_Engine::loadTemplate + * @see Mustache_Engine::loadPartial + * @see Mustache_Engine::loadLambda + * + * @param string|Mustache_Source $source + * @param Mustache_Cache $cache (default: null) + * + * @return Mustache_Template + */ + private function loadSource($source, Mustache_Cache $cache = null) + { + $className = $this->getTemplateClassName($source); + + if (!isset($this->templates[$className])) { + if ($cache === null) { + $cache = $this->getCache(); + } + + if (!class_exists($className, false)) { + if (!$cache->load($className)) { + $compiled = $this->compile($source); + $cache->cache($className, $compiled); + } + } + + $this->log( + Mustache_Logger::DEBUG, + 'Instantiating template: "{className}"', + array('className' => $className) + ); + + $this->templates[$className] = new $className($this); + } + + return $this->templates[$className]; + } + + /** + * Helper method to tokenize a Mustache template. + * + * @see Mustache_Tokenizer::scan + * + * @param string $source + * + * @return array Tokens + */ + private function tokenize($source) + { + return $this->getTokenizer()->scan($source, $this->delimiters); + } + + /** + * Helper method to parse a Mustache template. + * + * @see Mustache_Parser::parse + * + * @param string $source + * + * @return array Token tree + */ + private function parse($source) + { + $parser = $this->getParser(); + $parser->setPragmas($this->getPragmas()); + + return $parser->parse($this->tokenize($source)); + } + + /** + * Helper method to compile a Mustache template. + * + * @see Mustache_Compiler::compile + * + * @param string|Mustache_Source $source + * + * @return string generated Mustache template class code + */ + private function compile($source) + { + $name = $this->getTemplateClassName($source); + + $this->log( + Mustache_Logger::INFO, + 'Compiling template to "{className}" class', + array('className' => $name) + ); + + if ($source instanceof Mustache_Source) { + $source = $source->getSource(); + } + $tree = $this->parse($source); + + $compiler = $this->getCompiler(); + $compiler->setPragmas($this->getPragmas()); + + return $compiler->compile($source, $tree, $name, isset($this->escape), $this->charset, $this->strictCallables, $this->entityFlags); + } + + /** + * Add a log record if logging is enabled. + * + * @param int $level The logging level + * @param string $message The log message + * @param array $context The log context + */ + private function log($level, $message, array $context = array()) + { + if (isset($this->logger)) { + $this->logger->log($level, $message, $context); + } + } +} diff --git a/os3/Mustache/Exception.php b/os3/Mustache/Exception.php new file mode 100644 index 0000000..d4001a9 --- /dev/null +++ b/os3/Mustache/Exception.php @@ -0,0 +1,18 @@ +token = $token; + if (version_compare(PHP_VERSION, '5.3.0', '>=')) { + parent::__construct($msg, 0, $previous); + } else { + parent::__construct($msg); // @codeCoverageIgnore + } + } + + /** + * @return array + */ + public function getToken() + { + return $this->token; + } +} diff --git a/os3/Mustache/Exception/UnknownFilterException.php b/os3/Mustache/Exception/UnknownFilterException.php new file mode 100644 index 0000000..0651c17 --- /dev/null +++ b/os3/Mustache/Exception/UnknownFilterException.php @@ -0,0 +1,38 @@ +filterName = $filterName; + $message = sprintf('Unknown filter: %s', $filterName); + if (version_compare(PHP_VERSION, '5.3.0', '>=')) { + parent::__construct($message, 0, $previous); + } else { + parent::__construct($message); // @codeCoverageIgnore + } + } + + public function getFilterName() + { + return $this->filterName; + } +} diff --git a/os3/Mustache/Exception/UnknownHelperException.php b/os3/Mustache/Exception/UnknownHelperException.php new file mode 100644 index 0000000..193be78 --- /dev/null +++ b/os3/Mustache/Exception/UnknownHelperException.php @@ -0,0 +1,38 @@ +helperName = $helperName; + $message = sprintf('Unknown helper: %s', $helperName); + if (version_compare(PHP_VERSION, '5.3.0', '>=')) { + parent::__construct($message, 0, $previous); + } else { + parent::__construct($message); // @codeCoverageIgnore + } + } + + public function getHelperName() + { + return $this->helperName; + } +} diff --git a/os3/Mustache/Exception/UnknownTemplateException.php b/os3/Mustache/Exception/UnknownTemplateException.php new file mode 100644 index 0000000..32a778a --- /dev/null +++ b/os3/Mustache/Exception/UnknownTemplateException.php @@ -0,0 +1,38 @@ +templateName = $templateName; + $message = sprintf('Unknown template: %s', $templateName); + if (version_compare(PHP_VERSION, '5.3.0', '>=')) { + parent::__construct($message, 0, $previous); + } else { + parent::__construct($message); // @codeCoverageIgnore + } + } + + public function getTemplateName() + { + return $this->templateName; + } +} diff --git a/os3/Mustache/HelperCollection.php b/os3/Mustache/HelperCollection.php new file mode 100644 index 0000000..5d8f73c --- /dev/null +++ b/os3/Mustache/HelperCollection.php @@ -0,0 +1,172 @@ + $helper` pairs. + * + * @throws Mustache_Exception_InvalidArgumentException if the $helpers argument isn't an array or Traversable + * + * @param array|Traversable $helpers (default: null) + */ + public function __construct($helpers = null) + { + if ($helpers === null) { + return; + } + + if (!is_array($helpers) && !$helpers instanceof Traversable) { + throw new Mustache_Exception_InvalidArgumentException('HelperCollection constructor expects an array of helpers'); + } + + foreach ($helpers as $name => $helper) { + $this->add($name, $helper); + } + } + + /** + * Magic mutator. + * + * @see Mustache_HelperCollection::add + * + * @param string $name + * @param mixed $helper + */ + public function __set($name, $helper) + { + $this->add($name, $helper); + } + + /** + * Add a helper to this collection. + * + * @param string $name + * @param mixed $helper + */ + public function add($name, $helper) + { + $this->helpers[$name] = $helper; + } + + /** + * Magic accessor. + * + * @see Mustache_HelperCollection::get + * + * @param string $name + * + * @return mixed Helper + */ + public function __get($name) + { + return $this->get($name); + } + + /** + * Get a helper by name. + * + * @throws Mustache_Exception_UnknownHelperException If helper does not exist + * + * @param string $name + * + * @return mixed Helper + */ + public function get($name) + { + if (!$this->has($name)) { + throw new Mustache_Exception_UnknownHelperException($name); + } + + return $this->helpers[$name]; + } + + /** + * Magic isset(). + * + * @see Mustache_HelperCollection::has + * + * @param string $name + * + * @return bool True if helper is present + */ + public function __isset($name) + { + return $this->has($name); + } + + /** + * Check whether a given helper is present in the collection. + * + * @param string $name + * + * @return bool True if helper is present + */ + public function has($name) + { + return array_key_exists($name, $this->helpers); + } + + /** + * Magic unset(). + * + * @see Mustache_HelperCollection::remove + * + * @param string $name + */ + public function __unset($name) + { + $this->remove($name); + } + + /** + * Check whether a given helper is present in the collection. + * + * @throws Mustache_Exception_UnknownHelperException if the requested helper is not present + * + * @param string $name + */ + public function remove($name) + { + if (!$this->has($name)) { + throw new Mustache_Exception_UnknownHelperException($name); + } + + unset($this->helpers[$name]); + } + + /** + * Clear the helper collection. + * + * Removes all helpers from this collection + */ + public function clear() + { + $this->helpers = array(); + } + + /** + * Check whether the helper collection is empty. + * + * @return bool True if the collection is empty + */ + public function isEmpty() + { + return empty($this->helpers); + } +} diff --git a/os3/Mustache/LICENSE b/os3/Mustache/LICENSE new file mode 100644 index 0000000..e0aecc9 --- /dev/null +++ b/os3/Mustache/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2010-2015 Justin Hileman + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE +OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/os3/Mustache/LambdaHelper.php b/os3/Mustache/LambdaHelper.php new file mode 100644 index 0000000..e93dbfa --- /dev/null +++ b/os3/Mustache/LambdaHelper.php @@ -0,0 +1,76 @@ + =}}`. (default: null) + */ + public function __construct(Mustache_Engine $mustache, Mustache_Context $context, $delims = null) + { + $this->mustache = $mustache; + $this->context = $context; + $this->delims = $delims; + } + + /** + * Render a string as a Mustache template with the current rendering context. + * + * @param string $string + * + * @return string Rendered template + */ + public function render($string) + { + return $this->mustache + ->loadLambda((string) $string, $this->delims) + ->renderInternal($this->context); + } + + /** + * Render a string as a Mustache template with the current rendering context. + * + * @param string $string + * + * @return string Rendered template + */ + public function __invoke($string) + { + return $this->render($string); + } + + /** + * Get a Lambda Helper with custom delimiters. + * + * @param string $delims Custom delimiters, in the format `{{= <% %> =}}` + * + * @return Mustache_LambdaHelper + */ + public function withDelimiters($delims) + { + return new self($this->mustache, $this->context, $delims); + } +} diff --git a/os3/Mustache/Loader.php b/os3/Mustache/Loader.php new file mode 100644 index 0000000..23adba1 --- /dev/null +++ b/os3/Mustache/Loader.php @@ -0,0 +1,27 @@ + '{{ bar }}', + * 'baz' => 'Hey {{ qux }}!' + * ); + * + * $tpl = $loader->load('foo'); // '{{ bar }}' + * + * The ArrayLoader is used internally as a partials loader by Mustache_Engine instance when an array of partials + * is set. It can also be used as a quick-and-dirty Template loader. + */ +class Mustache_Loader_ArrayLoader implements Mustache_Loader, Mustache_Loader_MutableLoader +{ + private $templates; + + /** + * ArrayLoader constructor. + * + * @param array $templates Associative array of Template source (default: array()) + */ + public function __construct(array $templates = array()) + { + $this->templates = $templates; + } + + /** + * Load a Template. + * + * @throws Mustache_Exception_UnknownTemplateException If a template file is not found + * + * @param string $name + * + * @return string Mustache Template source + */ + public function load($name) + { + if (!isset($this->templates[$name])) { + throw new Mustache_Exception_UnknownTemplateException($name); + } + + return $this->templates[$name]; + } + + /** + * Set an associative array of Template sources for this loader. + * + * @param array $templates + */ + public function setTemplates(array $templates) + { + $this->templates = $templates; + } + + /** + * Set a Template source by name. + * + * @param string $name + * @param string $template Mustache Template source + */ + public function setTemplate($name, $template) + { + $this->templates[$name] = $template; + } +} diff --git a/os3/Mustache/Loader/CascadingLoader.php b/os3/Mustache/Loader/CascadingLoader.php new file mode 100644 index 0000000..3fb6353 --- /dev/null +++ b/os3/Mustache/Loader/CascadingLoader.php @@ -0,0 +1,69 @@ +loaders = array(); + foreach ($loaders as $loader) { + $this->addLoader($loader); + } + } + + /** + * Add a Loader instance. + * + * @param Mustache_Loader $loader + */ + public function addLoader(Mustache_Loader $loader) + { + $this->loaders[] = $loader; + } + + /** + * Load a Template by name. + * + * @throws Mustache_Exception_UnknownTemplateException If a template file is not found + * + * @param string $name + * + * @return string Mustache Template source + */ + public function load($name) + { + foreach ($this->loaders as $loader) { + try { + return $loader->load($name); + } catch (Mustache_Exception_UnknownTemplateException $e) { + // do nothing, check the next loader. + } + } + + throw new Mustache_Exception_UnknownTemplateException($name); + } +} diff --git a/os3/Mustache/Loader/FilesystemLoader.php b/os3/Mustache/Loader/FilesystemLoader.php new file mode 100644 index 0000000..e366df7 --- /dev/null +++ b/os3/Mustache/Loader/FilesystemLoader.php @@ -0,0 +1,135 @@ +load('foo'); // equivalent to `file_get_contents(dirname(__FILE__).'/views/foo.mustache'); + * + * This is probably the most useful Mustache Loader implementation. It can be used for partials and normal Templates: + * + * $m = new Mustache(array( + * 'loader' => new Mustache_Loader_FilesystemLoader(dirname(__FILE__).'/views'), + * 'partials_loader' => new Mustache_Loader_FilesystemLoader(dirname(__FILE__).'/views/partials'), + * )); + */ +class Mustache_Loader_FilesystemLoader implements Mustache_Loader +{ + private $baseDir; + private $extension = '.mustache'; + private $templates = array(); + + /** + * Mustache filesystem Loader constructor. + * + * Passing an $options array allows overriding certain Loader options during instantiation: + * + * $options = array( + * // The filename extension used for Mustache templates. Defaults to '.mustache' + * 'extension' => '.ms', + * ); + * + * @throws Mustache_Exception_RuntimeException if $baseDir does not exist + * + * @param string $baseDir Base directory containing Mustache template files + * @param array $options Array of Loader options (default: array()) + */ + public function __construct($baseDir, array $options = array()) + { + $this->baseDir = $baseDir; + + if (strpos($this->baseDir, '://') === false) { + $this->baseDir = realpath($this->baseDir); + } + + if ($this->shouldCheckPath() && !is_dir($this->baseDir)) { + throw new Mustache_Exception_RuntimeException(sprintf('FilesystemLoader baseDir must be a directory: %s', $baseDir)); + } + + if (array_key_exists('extension', $options)) { + if (empty($options['extension'])) { + $this->extension = ''; + } else { + $this->extension = '.' . ltrim($options['extension'], '.'); + } + } + } + + /** + * Load a Template by name. + * + * $loader = new Mustache_Loader_FilesystemLoader(dirname(__FILE__).'/views'); + * $loader->load('admin/dashboard'); // loads "./views/admin/dashboard.mustache"; + * + * @param string $name + * + * @return string Mustache Template source + */ + public function load($name) + { + if (!isset($this->templates[$name])) { + $this->templates[$name] = $this->loadFile($name); + } + + return $this->templates[$name]; + } + + /** + * Helper function for loading a Mustache file by name. + * + * @throws Mustache_Exception_UnknownTemplateException If a template file is not found + * + * @param string $name + * + * @return string Mustache Template source + */ + protected function loadFile($name) + { + $fileName = $this->getFileName($name); + + if ($this->shouldCheckPath() && !file_exists($fileName)) { + throw new Mustache_Exception_UnknownTemplateException($name); + } + + return file_get_contents($fileName); + } + + /** + * Helper function for getting a Mustache template file name. + * + * @param string $name + * + * @return string Template file name + */ + protected function getFileName($name) + { + $fileName = $this->baseDir . '/' . $name; + if (substr($fileName, 0 - strlen($this->extension)) !== $this->extension) { + $fileName .= $this->extension; + } + + return $fileName; + } + + /** + * Only check if baseDir is a directory and requested templates are files if + * baseDir is using the filesystem stream wrapper. + * + * @return bool Whether to check `is_dir` and `file_exists` + */ + protected function shouldCheckPath() + { + return strpos($this->baseDir, '://') === false || strpos($this->baseDir, 'file://') === 0; + } +} diff --git a/os3/Mustache/Loader/InlineLoader.php b/os3/Mustache/Loader/InlineLoader.php new file mode 100644 index 0000000..ae297fe --- /dev/null +++ b/os3/Mustache/Loader/InlineLoader.php @@ -0,0 +1,123 @@ +load('hello'); + * $goodbye = $loader->load('goodbye'); + * + * __halt_compiler(); + * + * @@ hello + * Hello, {{ planet }}! + * + * @@ goodbye + * Goodbye, cruel {{ planet }} + * + * Templates are deliniated by lines containing only `@@ name`. + * + * The InlineLoader is well-suited to micro-frameworks such as Silex: + * + * $app->register(new MustacheServiceProvider, array( + * 'mustache.loader' => new Mustache_Loader_InlineLoader(__FILE__, __COMPILER_HALT_OFFSET__) + * )); + * + * $app->get('/{name}', function ($name) use ($app) { + * return $app['mustache']->render('hello', compact('name')); + * }) + * ->value('name', 'world'); + * + * // ... + * + * __halt_compiler(); + * + * @@ hello + * Hello, {{ name }}! + */ +class Mustache_Loader_InlineLoader implements Mustache_Loader +{ + protected $fileName; + protected $offset; + protected $templates; + + /** + * The InlineLoader requires a filename and offset to process templates. + * + * The magic constants `__FILE__` and `__COMPILER_HALT_OFFSET__` are usually + * perfectly suited to the job: + * + * $loader = new Mustache_Loader_InlineLoader(__FILE__, __COMPILER_HALT_OFFSET__); + * + * Note that this only works if the loader is instantiated inside the same + * file as the inline templates. If the templates are located in another + * file, it would be necessary to manually specify the filename and offset. + * + * @param string $fileName The file to parse for inline templates + * @param int $offset A string offset for the start of the templates. + * This usually coincides with the `__halt_compiler` + * call, and the `__COMPILER_HALT_OFFSET__` + */ + public function __construct($fileName, $offset) + { + if (!is_file($fileName)) { + throw new Mustache_Exception_InvalidArgumentException('InlineLoader expects a valid filename.'); + } + + if (!is_int($offset) || $offset < 0) { + throw new Mustache_Exception_InvalidArgumentException('InlineLoader expects a valid file offset.'); + } + + $this->fileName = $fileName; + $this->offset = $offset; + } + + /** + * Load a Template by name. + * + * @throws Mustache_Exception_UnknownTemplateException If a template file is not found + * + * @param string $name + * + * @return string Mustache Template source + */ + public function load($name) + { + $this->loadTemplates(); + + if (!array_key_exists($name, $this->templates)) { + throw new Mustache_Exception_UnknownTemplateException($name); + } + + return $this->templates[$name]; + } + + /** + * Parse and load templates from the end of a source file. + */ + protected function loadTemplates() + { + if ($this->templates === null) { + $this->templates = array(); + $data = file_get_contents($this->fileName, false, null, $this->offset); + foreach (preg_split("/^@@(?= [\w\d\.]+$)/m", $data, -1) as $chunk) { + if (trim($chunk)) { + list($name, $content) = explode("\n", $chunk, 2); + $this->templates[trim($name)] = trim($content); + } + } + } + } +} diff --git a/os3/Mustache/Loader/MutableLoader.php b/os3/Mustache/Loader/MutableLoader.php new file mode 100644 index 0000000..57fe5be --- /dev/null +++ b/os3/Mustache/Loader/MutableLoader.php @@ -0,0 +1,31 @@ + '.ms', + * 'stat_props' => array('size', 'mtime'), + * ); + * + * Specifying 'stat_props' overrides the stat properties used to invalidate the template cache. By default, this + * uses 'mtime' and 'size', but this can be set to any of the properties supported by stat(): + * + * http://php.net/manual/en/function.stat.php + * + * You can also disable filesystem stat entirely: + * + * $options = array('stat_props' => null); + * + * But with great power comes great responsibility. Namely, if you disable stat-based cache invalidation, + * YOU MUST CLEAR THE TEMPLATE CACHE YOURSELF when your templates change. Make it part of your build or deploy + * process so you don't forget! + * + * @throws Mustache_Exception_RuntimeException if $baseDir does not exist. + * + * @param string $baseDir Base directory containing Mustache template files. + * @param array $options Array of Loader options (default: array()) + */ + public function __construct($baseDir, array $options = array()) + { + parent::__construct($baseDir, $options); + + if (array_key_exists('stat_props', $options)) { + if (empty($options['stat_props'])) { + $this->statProps = array(); + } else { + $this->statProps = $options['stat_props']; + } + } else { + $this->statProps = array('size', 'mtime'); + } + } + + /** + * Helper function for loading a Mustache file by name. + * + * @throws Mustache_Exception_UnknownTemplateException If a template file is not found. + * + * @param string $name + * + * @return Mustache_Source Mustache Template source + */ + protected function loadFile($name) + { + $fileName = $this->getFileName($name); + + if (!file_exists($fileName)) { + throw new Mustache_Exception_UnknownTemplateException($name); + } + + return new Mustache_Source_FilesystemSource($fileName, $this->statProps); + } +} diff --git a/os3/Mustache/Loader/StringLoader.php b/os3/Mustache/Loader/StringLoader.php new file mode 100644 index 0000000..7012c03 --- /dev/null +++ b/os3/Mustache/Loader/StringLoader.php @@ -0,0 +1,39 @@ +load('{{ foo }}'); // '{{ foo }}' + * + * This is the default Template Loader instance used by Mustache: + * + * $m = new Mustache; + * $tpl = $m->loadTemplate('{{ foo }}'); + * echo $tpl->render(array('foo' => 'bar')); // "bar" + */ +class Mustache_Loader_StringLoader implements Mustache_Loader +{ + /** + * Load a Template by source. + * + * @param string $name Mustache Template source + * + * @return string Mustache Template source + */ + public function load($name) + { + return $name; + } +} diff --git a/os3/Mustache/Logger.php b/os3/Mustache/Logger.php new file mode 100644 index 0000000..cb4037a --- /dev/null +++ b/os3/Mustache/Logger.php @@ -0,0 +1,126 @@ +log(Mustache_Logger::EMERGENCY, $message, $context); + } + + /** + * Action must be taken immediately. + * + * Example: Entire website down, database unavailable, etc. This should + * trigger the SMS alerts and wake you up. + * + * @param string $message + * @param array $context + */ + public function alert($message, array $context = array()) + { + $this->log(Mustache_Logger::ALERT, $message, $context); + } + + /** + * Critical conditions. + * + * Example: Application component unavailable, unexpected exception. + * + * @param string $message + * @param array $context + */ + public function critical($message, array $context = array()) + { + $this->log(Mustache_Logger::CRITICAL, $message, $context); + } + + /** + * Runtime errors that do not require immediate action but should typically + * be logged and monitored. + * + * @param string $message + * @param array $context + */ + public function error($message, array $context = array()) + { + $this->log(Mustache_Logger::ERROR, $message, $context); + } + + /** + * Exceptional occurrences that are not errors. + * + * Example: Use of deprecated APIs, poor use of an API, undesirable things + * that are not necessarily wrong. + * + * @param string $message + * @param array $context + */ + public function warning($message, array $context = array()) + { + $this->log(Mustache_Logger::WARNING, $message, $context); + } + + /** + * Normal but significant events. + * + * @param string $message + * @param array $context + */ + public function notice($message, array $context = array()) + { + $this->log(Mustache_Logger::NOTICE, $message, $context); + } + + /** + * Interesting events. + * + * Example: User logs in, SQL logs. + * + * @param string $message + * @param array $context + */ + public function info($message, array $context = array()) + { + $this->log(Mustache_Logger::INFO, $message, $context); + } + + /** + * Detailed debug information. + * + * @param string $message + * @param array $context + */ + public function debug($message, array $context = array()) + { + $this->log(Mustache_Logger::DEBUG, $message, $context); + } +} diff --git a/os3/Mustache/Logger/StreamLogger.php b/os3/Mustache/Logger/StreamLogger.php new file mode 100644 index 0000000..402a148 --- /dev/null +++ b/os3/Mustache/Logger/StreamLogger.php @@ -0,0 +1,194 @@ + 100, + self::INFO => 200, + self::NOTICE => 250, + self::WARNING => 300, + self::ERROR => 400, + self::CRITICAL => 500, + self::ALERT => 550, + self::EMERGENCY => 600, + ); + + protected $level; + protected $stream = null; + protected $url = null; + + /** + * @throws InvalidArgumentException if the logging level is unknown + * + * @param resource|string $stream Resource instance or URL + * @param int $level The minimum logging level at which this handler will be triggered + */ + public function __construct($stream, $level = Mustache_Logger::ERROR) + { + $this->setLevel($level); + + if (is_resource($stream)) { + $this->stream = $stream; + } else { + $this->url = $stream; + } + } + + /** + * Close stream resources. + */ + public function __destruct() + { + if (is_resource($this->stream)) { + fclose($this->stream); + } + } + + /** + * Set the minimum logging level. + * + * @throws Mustache_Exception_InvalidArgumentException if the logging level is unknown + * + * @param int $level The minimum logging level which will be written + */ + public function setLevel($level) + { + if (!array_key_exists($level, self::$levels)) { + throw new Mustache_Exception_InvalidArgumentException(sprintf('Unexpected logging level: %s', $level)); + } + + $this->level = $level; + } + + /** + * Get the current minimum logging level. + * + * @return int + */ + public function getLevel() + { + return $this->level; + } + + /** + * Logs with an arbitrary level. + * + * @throws Mustache_Exception_InvalidArgumentException if the logging level is unknown + * + * @param mixed $level + * @param string $message + * @param array $context + */ + public function log($level, $message, array $context = array()) + { + if (!array_key_exists($level, self::$levels)) { + throw new Mustache_Exception_InvalidArgumentException(sprintf('Unexpected logging level: %s', $level)); + } + + if (self::$levels[$level] >= self::$levels[$this->level]) { + $this->writeLog($level, $message, $context); + } + } + + /** + * Write a record to the log. + * + * @throws Mustache_Exception_LogicException If neither a stream resource nor url is present + * @throws Mustache_Exception_RuntimeException If the stream url cannot be opened + * + * @param int $level The logging level + * @param string $message The log message + * @param array $context The log context + */ + protected function writeLog($level, $message, array $context = array()) + { + if (!is_resource($this->stream)) { + if (!isset($this->url)) { + throw new Mustache_Exception_LogicException('Missing stream url, the stream can not be opened. This may be caused by a premature call to close().'); + } + + $this->stream = fopen($this->url, 'a'); + if (!is_resource($this->stream)) { + // @codeCoverageIgnoreStart + throw new Mustache_Exception_RuntimeException(sprintf('The stream or file "%s" could not be opened.', $this->url)); + // @codeCoverageIgnoreEnd + } + } + + fwrite($this->stream, self::formatLine($level, $message, $context)); + } + + /** + * Gets the name of the logging level. + * + * @throws InvalidArgumentException if the logging level is unknown + * + * @param int $level + * + * @return string + */ + protected static function getLevelName($level) + { + return strtoupper($level); + } + + /** + * Format a log line for output. + * + * @param int $level The logging level + * @param string $message The log message + * @param array $context The log context + * + * @return string + */ + protected static function formatLine($level, $message, array $context = array()) + { + return sprintf( + "%s: %s\n", + self::getLevelName($level), + self::interpolateMessage($message, $context) + ); + } + + /** + * Interpolate context values into the message placeholders. + * + * @param string $message + * @param array $context + * + * @return string + */ + protected static function interpolateMessage($message, array $context = array()) + { + if (strpos($message, '{') === false) { + return $message; + } + + // build a replacement array with braces around the context keys + $replace = array(); + foreach ($context as $key => $val) { + $replace['{' . $key . '}'] = $val; + } + + // interpolate replacement values into the the message and return + return strtr($message, $replace); + } +} diff --git a/os3/Mustache/Parser.php b/os3/Mustache/Parser.php new file mode 100644 index 0000000..02e3d89 --- /dev/null +++ b/os3/Mustache/Parser.php @@ -0,0 +1,383 @@ +lineNum = -1; + $this->lineTokens = 0; + $this->pragmas = $this->defaultPragmas; + + $this->pragmaFilters = isset($this->pragmas[Mustache_Engine::PRAGMA_FILTERS]); + $this->pragmaBlocks = isset($this->pragmas[Mustache_Engine::PRAGMA_BLOCKS]); + $this->pragmaDynamicNames = isset($this->pragmas[Mustache_Engine::PRAGMA_DYNAMIC_NAMES]); + + return $this->buildTree($tokens); + } + + /** + * Enable pragmas across all templates, regardless of the presence of pragma + * tags in the individual templates. + * + * @internal Users should set global pragmas in Mustache_Engine, not here :) + * + * @param string[] $pragmas + */ + public function setPragmas(array $pragmas) + { + $this->pragmas = array(); + foreach ($pragmas as $pragma) { + $this->enablePragma($pragma); + } + $this->defaultPragmas = $this->pragmas; + } + + /** + * Helper method for recursively building a parse tree. + * + * @throws Mustache_Exception_SyntaxException when nesting errors or mismatched section tags are encountered + * + * @param array &$tokens Set of Mustache tokens + * @param array $parent Parent token (default: null) + * + * @return array Mustache Token parse tree + */ + private function buildTree(array &$tokens, array $parent = null) + { + $nodes = array(); + + while (!empty($tokens)) { + $token = array_shift($tokens); + + if ($token[Mustache_Tokenizer::LINE] === $this->lineNum) { + $this->lineTokens++; + } else { + $this->lineNum = $token[Mustache_Tokenizer::LINE]; + $this->lineTokens = 0; + } + + if ($token[Mustache_Tokenizer::TYPE] !== Mustache_Tokenizer::T_COMMENT) { + if ($this->pragmaDynamicNames && isset($token[Mustache_Tokenizer::NAME])) { + list($name, $isDynamic) = $this->getDynamicName($token); + if ($isDynamic) { + $token[Mustache_Tokenizer::NAME] = $name; + $token[Mustache_Tokenizer::DYNAMIC] = true; + } + } + + if ($this->pragmaFilters && isset($token[Mustache_Tokenizer::NAME])) { + list($name, $filters) = $this->getNameAndFilters($token[Mustache_Tokenizer::NAME]); + if (!empty($filters)) { + $token[Mustache_Tokenizer::NAME] = $name; + $token[Mustache_Tokenizer::FILTERS] = $filters; + } + } + } + + switch ($token[Mustache_Tokenizer::TYPE]) { + case Mustache_Tokenizer::T_DELIM_CHANGE: + $this->checkIfTokenIsAllowedInParent($parent, $token); + $this->clearStandaloneLines($nodes, $tokens); + break; + + case Mustache_Tokenizer::T_SECTION: + case Mustache_Tokenizer::T_INVERTED: + $this->checkIfTokenIsAllowedInParent($parent, $token); + $this->clearStandaloneLines($nodes, $tokens); + $nodes[] = $this->buildTree($tokens, $token); + break; + + case Mustache_Tokenizer::T_END_SECTION: + if (!isset($parent)) { + $msg = sprintf( + 'Unexpected closing tag: /%s on line %d', + $token[Mustache_Tokenizer::NAME], + $token[Mustache_Tokenizer::LINE] + ); + throw new Mustache_Exception_SyntaxException($msg, $token); + } + + $sameName = $token[Mustache_Tokenizer::NAME] !== $parent[Mustache_Tokenizer::NAME]; + $tokenDynamic = isset($token[Mustache_Tokenizer::DYNAMIC]) && $token[Mustache_Tokenizer::DYNAMIC]; + $parentDynamic = isset($parent[Mustache_Tokenizer::DYNAMIC]) && $parent[Mustache_Tokenizer::DYNAMIC]; + + if ($sameName || ($tokenDynamic !== $parentDynamic)) { + $msg = sprintf( + 'Nesting error: %s (on line %d) vs. %s (on line %d)', + $parent[Mustache_Tokenizer::NAME], + $parent[Mustache_Tokenizer::LINE], + $token[Mustache_Tokenizer::NAME], + $token[Mustache_Tokenizer::LINE] + ); + throw new Mustache_Exception_SyntaxException($msg, $token); + } + + $this->clearStandaloneLines($nodes, $tokens); + $parent[Mustache_Tokenizer::END] = $token[Mustache_Tokenizer::INDEX]; + $parent[Mustache_Tokenizer::NODES] = $nodes; + + return $parent; + + case Mustache_Tokenizer::T_PARTIAL: + $this->checkIfTokenIsAllowedInParent($parent, $token); + //store the whitespace prefix for laters! + if ($indent = $this->clearStandaloneLines($nodes, $tokens)) { + $token[Mustache_Tokenizer::INDENT] = $indent[Mustache_Tokenizer::VALUE]; + } + $nodes[] = $token; + break; + + case Mustache_Tokenizer::T_PARENT: + $this->checkIfTokenIsAllowedInParent($parent, $token); + $nodes[] = $this->buildTree($tokens, $token); + break; + + case Mustache_Tokenizer::T_BLOCK_VAR: + if ($this->pragmaBlocks) { + // BLOCKS pragma is enabled, let's do this! + if (isset($parent) && $parent[Mustache_Tokenizer::TYPE] === Mustache_Tokenizer::T_PARENT) { + $token[Mustache_Tokenizer::TYPE] = Mustache_Tokenizer::T_BLOCK_ARG; + } + $this->clearStandaloneLines($nodes, $tokens); + $nodes[] = $this->buildTree($tokens, $token); + } else { + // pretend this was just a normal "escaped" token... + $token[Mustache_Tokenizer::TYPE] = Mustache_Tokenizer::T_ESCAPED; + // TODO: figure out how to figure out if there was a space after this dollar: + $token[Mustache_Tokenizer::NAME] = '$' . $token[Mustache_Tokenizer::NAME]; + $nodes[] = $token; + } + break; + + case Mustache_Tokenizer::T_PRAGMA: + $this->enablePragma($token[Mustache_Tokenizer::NAME]); + // no break + + case Mustache_Tokenizer::T_COMMENT: + $this->clearStandaloneLines($nodes, $tokens); + $nodes[] = $token; + break; + + default: + $nodes[] = $token; + break; + } + } + + if (isset($parent)) { + $msg = sprintf( + 'Missing closing tag: %s opened on line %d', + $parent[Mustache_Tokenizer::NAME], + $parent[Mustache_Tokenizer::LINE] + ); + throw new Mustache_Exception_SyntaxException($msg, $parent); + } + + return $nodes; + } + + /** + * Clear standalone line tokens. + * + * Returns a whitespace token for indenting partials, if applicable. + * + * @param array $nodes Parsed nodes + * @param array $tokens Tokens to be parsed + * + * @return array|null Resulting indent token, if any + */ + private function clearStandaloneLines(array &$nodes, array &$tokens) + { + if ($this->lineTokens > 1) { + // this is the third or later node on this line, so it can't be standalone + return; + } + + $prev = null; + if ($this->lineTokens === 1) { + // this is the second node on this line, so it can't be standalone + // unless the previous node is whitespace. + if ($prev = end($nodes)) { + if (!$this->tokenIsWhitespace($prev)) { + return; + } + } + } + + if ($next = reset($tokens)) { + // If we're on a new line, bail. + if ($next[Mustache_Tokenizer::LINE] !== $this->lineNum) { + return; + } + + // If the next token isn't whitespace, bail. + if (!$this->tokenIsWhitespace($next)) { + return; + } + + if (count($tokens) !== 1) { + // Unless it's the last token in the template, the next token + // must end in newline for this to be standalone. + if (substr($next[Mustache_Tokenizer::VALUE], -1) !== "\n") { + return; + } + } + + // Discard the whitespace suffix + array_shift($tokens); + } + + if ($prev) { + // Return the whitespace prefix, if any + return array_pop($nodes); + } + } + + /** + * Check whether token is a whitespace token. + * + * True if token type is T_TEXT and value is all whitespace characters. + * + * @param array $token + * + * @return bool True if token is a whitespace token + */ + private function tokenIsWhitespace(array $token) + { + if ($token[Mustache_Tokenizer::TYPE] === Mustache_Tokenizer::T_TEXT) { + return preg_match('/^\s*$/', $token[Mustache_Tokenizer::VALUE]); + } + + return false; + } + + /** + * Check whether a token is allowed inside a parent tag. + * + * @throws Mustache_Exception_SyntaxException if an invalid token is found inside a parent tag + * + * @param array|null $parent + * @param array $token + */ + private function checkIfTokenIsAllowedInParent($parent, array $token) + { + if (isset($parent) && $parent[Mustache_Tokenizer::TYPE] === Mustache_Tokenizer::T_PARENT) { + throw new Mustache_Exception_SyntaxException('Illegal content in < parent tag', $token); + } + } + + /** + * Parse dynamic names. + * + * @throws Mustache_Exception_SyntaxException when a tag does not allow * + * @throws Mustache_Exception_SyntaxException on multiple *s, or dots or filters with * + */ + private function getDynamicName(array $token) + { + $name = $token[Mustache_Tokenizer::NAME]; + $isDynamic = false; + + if (preg_match('/^\s*\*\s*/', $name)) { + $this->ensureTagAllowsDynamicNames($token); + $name = preg_replace('/^\s*\*\s*/', '', $name); + $isDynamic = true; + } + + return array($name, $isDynamic); + } + + /** + * Check whether the given token supports dynamic tag names. + * + * @throws Mustache_Exception_SyntaxException when a tag does not allow * + * + * @param array $token + */ + private function ensureTagAllowsDynamicNames(array $token) + { + switch ($token[Mustache_Tokenizer::TYPE]) { + case Mustache_Tokenizer::T_PARTIAL: + case Mustache_Tokenizer::T_PARENT: + case Mustache_Tokenizer::T_END_SECTION: + return; + } + + $msg = sprintf( + 'Invalid dynamic name: %s in %s tag', + $token[Mustache_Tokenizer::NAME], + Mustache_Tokenizer::getTagName($token[Mustache_Tokenizer::TYPE]) + ); + + throw new Mustache_Exception_SyntaxException($msg, $token); + } + + + /** + * Split a tag name into name and filters. + * + * @param string $name + * + * @return array [Tag name, Array of filters] + */ + private function getNameAndFilters($name) + { + $filters = array_map('trim', explode('|', $name)); + $name = array_shift($filters); + + return array($name, $filters); + } + + /** + * Enable a pragma. + * + * @param string $name + */ + private function enablePragma($name) + { + $this->pragmas[$name] = true; + + switch ($name) { + case Mustache_Engine::PRAGMA_BLOCKS: + $this->pragmaBlocks = true; + break; + + case Mustache_Engine::PRAGMA_FILTERS: + $this->pragmaFilters = true; + break; + + case Mustache_Engine::PRAGMA_DYNAMIC_NAMES: + $this->pragmaDynamicNames = true; + break; + } + } +} diff --git a/os3/Mustache/Source.php b/os3/Mustache/Source.php new file mode 100644 index 0000000..278c2cb --- /dev/null +++ b/os3/Mustache/Source.php @@ -0,0 +1,40 @@ +fileName = $fileName; + $this->statProps = $statProps; + } + + /** + * Get the Source key (used to generate the compiled class name). + * + * @throws Mustache_Exception_RuntimeException when a source file cannot be read + * + * @return string + */ + public function getKey() + { + $chunks = array( + 'fileName' => $this->fileName, + ); + + if (!empty($this->statProps)) { + if (!isset($this->stat)) { + $this->stat = @stat($this->fileName); + } + + if ($this->stat === false) { + throw new Mustache_Exception_RuntimeException(sprintf('Failed to read source file "%s".', $this->fileName)); + } + + foreach ($this->statProps as $prop) { + $chunks[$prop] = $this->stat[$prop]; + } + } + + return json_encode($chunks); + } + + /** + * Get the template Source. + * + * @return string + */ + public function getSource() + { + return file_get_contents($this->fileName); + } +} diff --git a/os3/Mustache/Template.php b/os3/Mustache/Template.php new file mode 100644 index 0000000..4de8239 --- /dev/null +++ b/os3/Mustache/Template.php @@ -0,0 +1,180 @@ +mustache = $mustache; + } + + /** + * Mustache Template instances can be treated as a function and rendered by simply calling them. + * + * $m = new Mustache_Engine; + * $tpl = $m->loadTemplate('Hello, {{ name }}!'); + * echo $tpl(array('name' => 'World')); // "Hello, World!" + * + * @see Mustache_Template::render + * + * @param mixed $context Array or object rendering context (default: array()) + * + * @return string Rendered template + */ + public function __invoke($context = array()) + { + return $this->render($context); + } + + /** + * Render this template given the rendering context. + * + * @param mixed $context Array or object rendering context (default: array()) + * + * @return string Rendered template + */ + public function render($context = array()) + { + return $this->renderInternal( + $this->prepareContextStack($context) + ); + } + + /** + * Internal rendering method implemented by Mustache Template concrete subclasses. + * + * This is where the magic happens :) + * + * NOTE: This method is not part of the Mustache.php public API. + * + * @param Mustache_Context $context + * @param string $indent (default: '') + * + * @return string Rendered template + */ + abstract public function renderInternal(Mustache_Context $context, $indent = ''); + + /** + * Tests whether a value should be iterated over (e.g. in a section context). + * + * In most languages there are two distinct array types: list and hash (or whatever you want to call them). Lists + * should be iterated, hashes should be treated as objects. Mustache follows this paradigm for Ruby, Javascript, + * Java, Python, etc. + * + * PHP, however, treats lists and hashes as one primitive type: array. So Mustache.php needs a way to distinguish + * between between a list of things (numeric, normalized array) and a set of variables to be used as section context + * (associative array). In other words, this will be iterated over: + * + * $items = array( + * array('name' => 'foo'), + * array('name' => 'bar'), + * array('name' => 'baz'), + * ); + * + * ... but this will be used as a section context block: + * + * $items = array( + * 1 => array('name' => 'foo'), + * 'banana' => array('name' => 'bar'), + * 42 => array('name' => 'baz'), + * ); + * + * @param mixed $value + * + * @return bool True if the value is 'iterable' + */ + protected function isIterable($value) + { + switch (gettype($value)) { + case 'object': + return $value instanceof Traversable; + + case 'array': + $i = 0; + foreach ($value as $k => $v) { + if ($k !== $i++) { + return false; + } + } + + return true; + + default: + return false; + } + } + + /** + * Helper method to prepare the Context stack. + * + * Adds the Mustache HelperCollection to the stack's top context frame if helpers are present. + * + * @param mixed $context Optional first context frame (default: null) + * + * @return Mustache_Context + */ + protected function prepareContextStack($context = null) + { + $stack = new Mustache_Context(); + + $helpers = $this->mustache->getHelpers(); + if (!$helpers->isEmpty()) { + $stack->push($helpers); + } + + if (!empty($context)) { + $stack->push($context); + } + + return $stack; + } + + /** + * Resolve a context value. + * + * Invoke the value if it is callable, otherwise return the value. + * + * @param mixed $value + * @param Mustache_Context $context + * + * @return string + */ + protected function resolveValue($value, Mustache_Context $context) + { + if (($this->strictCallables ? is_object($value) : !is_string($value)) && is_callable($value)) { + return $this->mustache + ->loadLambda((string) call_user_func($value)) + ->renderInternal($context); + } + + return $value; + } +} diff --git a/os3/Mustache/Tokenizer.php b/os3/Mustache/Tokenizer.php new file mode 100644 index 0000000..d96f129 --- /dev/null +++ b/os3/Mustache/Tokenizer.php @@ -0,0 +1,408 @@ +'; + const T_PARENT = '<'; + const T_DELIM_CHANGE = '='; + const T_ESCAPED = '_v'; + const T_UNESCAPED = '{'; + const T_UNESCAPED_2 = '&'; + const T_TEXT = '_t'; + const T_PRAGMA = '%'; + const T_BLOCK_VAR = '$'; + const T_BLOCK_ARG = '$arg'; + + // Valid token types + private static $tagTypes = array( + self::T_SECTION => true, + self::T_INVERTED => true, + self::T_END_SECTION => true, + self::T_COMMENT => true, + self::T_PARTIAL => true, + self::T_PARENT => true, + self::T_DELIM_CHANGE => true, + self::T_ESCAPED => true, + self::T_UNESCAPED => true, + self::T_UNESCAPED_2 => true, + self::T_PRAGMA => true, + self::T_BLOCK_VAR => true, + ); + + private static $tagNames = array( + self::T_SECTION => 'section', + self::T_INVERTED => 'inverted section', + self::T_END_SECTION => 'section end', + self::T_COMMENT => 'comment', + self::T_PARTIAL => 'partial', + self::T_PARENT => 'parent', + self::T_DELIM_CHANGE => 'set delimiter', + self::T_ESCAPED => 'variable', + self::T_UNESCAPED => 'unescaped variable', + self::T_UNESCAPED_2 => 'unescaped variable', + self::T_PRAGMA => 'pragma', + self::T_BLOCK_VAR => 'block variable', + self::T_BLOCK_ARG => 'block variable', + ); + + // Token properties + const TYPE = 'type'; + const NAME = 'name'; + const DYNAMIC = 'dynamic'; + const OTAG = 'otag'; + const CTAG = 'ctag'; + const LINE = 'line'; + const INDEX = 'index'; + const END = 'end'; + const INDENT = 'indent'; + const NODES = 'nodes'; + const VALUE = 'value'; + const FILTERS = 'filters'; + + private $state; + private $tagType; + private $buffer; + private $tokens; + private $seenTag; + private $line; + + private $otag; + private $otagChar; + private $otagLen; + + private $ctag; + private $ctagChar; + private $ctagLen; + + /** + * Scan and tokenize template source. + * + * @throws Mustache_Exception_SyntaxException when mismatched section tags are encountered + * @throws Mustache_Exception_InvalidArgumentException when $delimiters string is invalid + * + * @param string $text Mustache template source to tokenize + * @param string $delimiters Optionally, pass initial opening and closing delimiters (default: empty string) + * + * @return array Set of Mustache tokens + */ + public function scan($text, $delimiters = '') + { + // Setting mbstring.func_overload makes things *really* slow. + // Let's do everyone a favor and scan this string as ASCII instead. + // + // The INI directive was removed in PHP 8.0 so we don't need to check there (and can drop it + // when we remove support for older versions of PHP). + // + // @codeCoverageIgnoreStart + $encoding = null; + if (version_compare(PHP_VERSION, '8.0.0', '<')) { + if (function_exists('mb_internal_encoding') && ini_get('mbstring.func_overload') & 2) { + $encoding = mb_internal_encoding(); + mb_internal_encoding('ASCII'); + } + } + // @codeCoverageIgnoreEnd + + $this->reset(); + + if (is_string($delimiters) && $delimiters = trim($delimiters)) { + $this->setDelimiters($delimiters); + } + + $len = strlen($text); + for ($i = 0; $i < $len; $i++) { + switch ($this->state) { + case self::IN_TEXT: + $char = $text[$i]; + // Test whether it's time to change tags. + if ($char === $this->otagChar && substr($text, $i, $this->otagLen) === $this->otag) { + $i--; + $this->flushBuffer(); + $this->state = self::IN_TAG_TYPE; + } else { + $this->buffer .= $char; + if ($char === "\n") { + $this->flushBuffer(); + $this->line++; + } + } + break; + + case self::IN_TAG_TYPE: + $i += $this->otagLen - 1; + $char = $text[$i + 1]; + if (isset(self::$tagTypes[$char])) { + $tag = $char; + $this->tagType = $tag; + } else { + $tag = null; + $this->tagType = self::T_ESCAPED; + } + + if ($this->tagType === self::T_DELIM_CHANGE) { + $i = $this->changeDelimiters($text, $i); + $this->state = self::IN_TEXT; + } elseif ($this->tagType === self::T_PRAGMA) { + $i = $this->addPragma($text, $i); + $this->state = self::IN_TEXT; + } else { + if ($tag !== null) { + $i++; + } + $this->state = self::IN_TAG; + } + $this->seenTag = $i; + break; + + default: + $char = $text[$i]; + // Test whether it's time to change tags. + if ($char === $this->ctagChar && substr($text, $i, $this->ctagLen) === $this->ctag) { + $token = array( + self::TYPE => $this->tagType, + self::NAME => trim($this->buffer), + self::OTAG => $this->otag, + self::CTAG => $this->ctag, + self::LINE => $this->line, + self::INDEX => ($this->tagType === self::T_END_SECTION) ? $this->seenTag - $this->otagLen : $i + $this->ctagLen, + ); + + if ($this->tagType === self::T_UNESCAPED) { + // Clean up `{{{ tripleStache }}}` style tokens. + if ($this->ctag === '}}') { + if (($i + 2 < $len) && $text[$i + 2] === '}') { + $i++; + } else { + $msg = sprintf( + 'Mismatched tag delimiters: %s on line %d', + $token[self::NAME], + $token[self::LINE] + ); + + throw new Mustache_Exception_SyntaxException($msg, $token); + } + } else { + $lastName = $token[self::NAME]; + if (substr($lastName, -1) === '}') { + $token[self::NAME] = trim(substr($lastName, 0, -1)); + } else { + $msg = sprintf( + 'Mismatched tag delimiters: %s on line %d', + $token[self::NAME], + $token[self::LINE] + ); + + throw new Mustache_Exception_SyntaxException($msg, $token); + } + } + } + + $this->buffer = ''; + $i += $this->ctagLen - 1; + $this->state = self::IN_TEXT; + $this->tokens[] = $token; + } else { + $this->buffer .= $char; + } + break; + } + } + + if ($this->state !== self::IN_TEXT) { + $this->throwUnclosedTagException(); + } + + $this->flushBuffer(); + + // Restore the user's encoding... + // @codeCoverageIgnoreStart + if ($encoding) { + mb_internal_encoding($encoding); + } + // @codeCoverageIgnoreEnd + + return $this->tokens; + } + + /** + * Helper function to reset tokenizer internal state. + */ + private function reset() + { + $this->state = self::IN_TEXT; + $this->tagType = null; + $this->buffer = ''; + $this->tokens = array(); + $this->seenTag = false; + $this->line = 0; + + $this->otag = '{{'; + $this->otagChar = '{'; + $this->otagLen = 2; + + $this->ctag = '}}'; + $this->ctagChar = '}'; + $this->ctagLen = 2; + } + + /** + * Flush the current buffer to a token. + */ + private function flushBuffer() + { + if (strlen($this->buffer) > 0) { + $this->tokens[] = array( + self::TYPE => self::T_TEXT, + self::LINE => $this->line, + self::VALUE => $this->buffer, + ); + $this->buffer = ''; + } + } + + /** + * Change the current Mustache delimiters. Set new `otag` and `ctag` values. + * + * @throws Mustache_Exception_SyntaxException when delimiter string is invalid + * + * @param string $text Mustache template source + * @param int $index Current tokenizer index + * + * @return int New index value + */ + private function changeDelimiters($text, $index) + { + $startIndex = strpos($text, '=', $index) + 1; + $close = '=' . $this->ctag; + $closeIndex = strpos($text, $close, $index); + + if ($closeIndex === false) { + $this->throwUnclosedTagException(); + } + + $token = array( + self::TYPE => self::T_DELIM_CHANGE, + self::LINE => $this->line, + ); + + try { + $this->setDelimiters(trim(substr($text, $startIndex, $closeIndex - $startIndex))); + } catch (Mustache_Exception_InvalidArgumentException $e) { + throw new Mustache_Exception_SyntaxException($e->getMessage(), $token); + } + + $this->tokens[] = $token; + + return $closeIndex + strlen($close) - 1; + } + + /** + * Set the current Mustache `otag` and `ctag` delimiters. + * + * @throws Mustache_Exception_InvalidArgumentException when delimiter string is invalid + * + * @param string $delimiters + */ + private function setDelimiters($delimiters) + { + if (!preg_match('/^\s*(\S+)\s+(\S+)\s*$/', $delimiters, $matches)) { + throw new Mustache_Exception_InvalidArgumentException(sprintf('Invalid delimiters: %s', $delimiters)); + } + + list($_, $otag, $ctag) = $matches; + + $this->otag = $otag; + $this->otagChar = $otag[0]; + $this->otagLen = strlen($otag); + + $this->ctag = $ctag; + $this->ctagChar = $ctag[0]; + $this->ctagLen = strlen($ctag); + } + + /** + * Add pragma token. + * + * Pragmas are hoisted to the front of the template, so all pragma tokens + * will appear at the front of the token list. + * + * @param string $text + * @param int $index + * + * @return int New index value + */ + private function addPragma($text, $index) + { + $end = strpos($text, $this->ctag, $index); + if ($end === false) { + $this->throwUnclosedTagException(); + } + + $pragma = trim(substr($text, $index + 2, $end - $index - 2)); + + // Pragmas are hoisted to the front of the template. + array_unshift($this->tokens, array( + self::TYPE => self::T_PRAGMA, + self::NAME => $pragma, + self::LINE => 0, + )); + + return $end + $this->ctagLen - 1; + } + + + private function throwUnclosedTagException() + { + $name = trim($this->buffer); + if ($name !== '') { + $msg = sprintf('Unclosed tag: %s on line %d', $name, $this->line); + } else { + $msg = sprintf('Unclosed tag on line %d', $this->line); + } + + throw new Mustache_Exception_SyntaxException($msg, array( + self::TYPE => $this->tagType, + self::NAME => $name, + self::OTAG => $this->otag, + self::CTAG => $this->ctag, + self::LINE => $this->line, + self::INDEX => $this->seenTag - $this->otagLen, + )); + } + + /** + * Get the human readable name for a tag type. + * + * @param string $tagType One of the tokenizer T_* constants + * + * @return string + */ + static function getTagName($tagType) + { + return isset(self::$tagNames[$tagType]) ? self::$tagNames[$tagType] : 'unknown'; + } +} diff --git a/os3/PHPMailer/Exception.php b/os3/PHPMailer/Exception.php new file mode 100644 index 0000000..52eaf95 --- /dev/null +++ b/os3/PHPMailer/Exception.php @@ -0,0 +1,40 @@ + + * @author Jim Jagielski (jimjag) + * @author Andy Prevost (codeworxtech) + * @author Brent R. Matzelle (original founder) + * @copyright 2012 - 2020 Marcus Bointon + * @copyright 2010 - 2012 Jim Jagielski + * @copyright 2004 - 2009 Andy Prevost + * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License + * @note This program is distributed in the hope that it will be useful - WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. + */ + +namespace PHPMailer\PHPMailer; + +/** + * PHPMailer exception handler. + * + * @author Marcus Bointon + */ +class Exception extends \Exception +{ + /** + * Prettify error message output. + * + * @return string + */ + public function errorMessage() + { + return '' . htmlspecialchars($this->getMessage(), ENT_COMPAT | ENT_HTML401) . "
\n"; + } +} diff --git a/os3/PHPMailer/LICENSE b/os3/PHPMailer/LICENSE new file mode 100644 index 0000000..f166cc5 --- /dev/null +++ b/os3/PHPMailer/LICENSE @@ -0,0 +1,502 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 2.1, February 1999 + + Copyright (C) 1991, 1999 Free Software Foundation, Inc. + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + +[This is the first released version of the Lesser GPL. It also counts + as the successor of the GNU Library Public License, version 2, hence + the version number 2.1.] + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +Licenses are intended to guarantee your freedom to share and change +free software--to make sure the software is free for all its users. + + This license, the Lesser General Public License, applies to some +specially designated software packages--typically libraries--of the +Free Software Foundation and other authors who decide to use it. You +can use it too, but we suggest you first think carefully about whether +this license or the ordinary General Public License is the better +strategy to use in any particular case, based on the explanations below. + + When we speak of free software, we are referring to freedom of use, +not price. Our General Public Licenses are designed to make sure that +you have the freedom to distribute copies of free software (and charge +for this service if you wish); that you receive source code or can get +it if you want it; that you can change the software and use pieces of +it in new free programs; and that you are informed that you can do +these things. + + To protect your rights, we need to make restrictions that forbid +distributors to deny you these rights or to ask you to surrender these +rights. These restrictions translate to certain responsibilities for +you if you distribute copies of the library or if you modify it. + + For example, if you distribute copies of the library, whether gratis +or for a fee, you must give the recipients all the rights that we gave +you. You must make sure that they, too, receive or can get the source +code. If you link other code with the library, you must provide +complete object files to the recipients, so that they can relink them +with the library after making changes to the library and recompiling +it. And you must show them these terms so they know their rights. + + We protect your rights with a two-step method: (1) we copyright the +library, and (2) we offer you this license, which gives you legal +permission to copy, distribute and/or modify the library. + + To protect each distributor, we want to make it very clear that +there is no warranty for the free library. Also, if the library is +modified by someone else and passed on, the recipients should know +that what they have is not the original version, so that the original +author's reputation will not be affected by problems that might be +introduced by others. + + Finally, software patents pose a constant threat to the existence of +any free program. We wish to make sure that a company cannot +effectively restrict the users of a free program by obtaining a +restrictive license from a patent holder. Therefore, we insist that +any patent license obtained for a version of the library must be +consistent with the full freedom of use specified in this license. + + Most GNU software, including some libraries, is covered by the +ordinary GNU General Public License. This license, the GNU Lesser +General Public License, applies to certain designated libraries, and +is quite different from the ordinary General Public License. We use +this license for certain libraries in order to permit linking those +libraries into non-free programs. + + When a program is linked with a library, whether statically or using +a shared library, the combination of the two is legally speaking a +combined work, a derivative of the original library. The ordinary +General Public License therefore permits such linking only if the +entire combination fits its criteria of freedom. The Lesser General +Public License permits more lax criteria for linking other code with +the library. + + We call this license the "Lesser" General Public License because it +does Less to protect the user's freedom than the ordinary General +Public License. It also provides other free software developers Less +of an advantage over competing non-free programs. These disadvantages +are the reason we use the ordinary General Public License for many +libraries. However, the Lesser license provides advantages in certain +special circumstances. + + For example, on rare occasions, there may be a special need to +encourage the widest possible use of a certain library, so that it becomes +a de-facto standard. To achieve this, non-free programs must be +allowed to use the library. A more frequent case is that a free +library does the same job as widely used non-free libraries. In this +case, there is little to gain by limiting the free library to free +software only, so we use the Lesser General Public License. + + In other cases, permission to use a particular library in non-free +programs enables a greater number of people to use a large body of +free software. For example, permission to use the GNU C Library in +non-free programs enables many more people to use the whole GNU +operating system, as well as its variant, the GNU/Linux operating +system. + + Although the Lesser General Public License is Less protective of the +users' freedom, it does ensure that the user of a program that is +linked with the Library has the freedom and the wherewithal to run +that program using a modified version of the Library. + + The precise terms and conditions for copying, distribution and +modification follow. Pay close attention to the difference between a +"work based on the library" and a "work that uses the library". The +former contains code derived from the library, whereas the latter must +be combined with the library in order to run. + + GNU LESSER GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License Agreement applies to any software library or other +program which contains a notice placed by the copyright holder or +other authorized party saying it may be distributed under the terms of +this Lesser General Public License (also called "this License"). +Each licensee is addressed as "you". + + A "library" means a collection of software functions and/or data +prepared so as to be conveniently linked with application programs +(which use some of those functions and data) to form executables. + + The "Library", below, refers to any such software library or work +which has been distributed under these terms. A "work based on the +Library" means either the Library or any derivative work under +copyright law: that is to say, a work containing the Library or a +portion of it, either verbatim or with modifications and/or translated +straightforwardly into another language. (Hereinafter, translation is +included without limitation in the term "modification".) + + "Source code" for a work means the preferred form of the work for +making modifications to it. For a library, complete source code means +all the source code for all modules it contains, plus any associated +interface definition files, plus the scripts used to control compilation +and installation of the library. + + Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running a program using the Library is not restricted, and output from +such a program is covered only if its contents constitute a work based +on the Library (independent of the use of the Library in a tool for +writing it). Whether that is true depends on what the Library does +and what the program that uses the Library does. + + 1. You may copy and distribute verbatim copies of the Library's +complete source code as you receive it, in any medium, provided that +you conspicuously and appropriately publish on each copy an +appropriate copyright notice and disclaimer of warranty; keep intact +all the notices that refer to this License and to the absence of any +warranty; and distribute a copy of this License along with the +Library. + + You may charge a fee for the physical act of transferring a copy, +and you may at your option offer warranty protection in exchange for a +fee. + + 2. You may modify your copy or copies of the Library or any portion +of it, thus forming a work based on the Library, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) The modified work must itself be a software library. + + b) You must cause the files modified to carry prominent notices + stating that you changed the files and the date of any change. + + c) You must cause the whole of the work to be licensed at no + charge to all third parties under the terms of this License. + + d) If a facility in the modified Library refers to a function or a + table of data to be supplied by an application program that uses + the facility, other than as an argument passed when the facility + is invoked, then you must make a good faith effort to ensure that, + in the event an application does not supply such function or + table, the facility still operates, and performs whatever part of + its purpose remains meaningful. + + (For example, a function in a library to compute square roots has + a purpose that is entirely well-defined independent of the + application. Therefore, Subsection 2d requires that any + application-supplied function or table used by this function must + be optional: if the application does not supply it, the square + root function must still compute square roots.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Library, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Library, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote +it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Library. + +In addition, mere aggregation of another work not based on the Library +with the Library (or with a work based on the Library) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may opt to apply the terms of the ordinary GNU General Public +License instead of this License to a given copy of the Library. To do +this, you must alter all the notices that refer to this License, so +that they refer to the ordinary GNU General Public License, version 2, +instead of to this License. (If a newer version than version 2 of the +ordinary GNU General Public License has appeared, then you can specify +that version instead if you wish.) Do not make any other change in +these notices. + + Once this change is made in a given copy, it is irreversible for +that copy, so the ordinary GNU General Public License applies to all +subsequent copies and derivative works made from that copy. + + This option is useful when you wish to copy part of the code of +the Library into a program that is not a library. + + 4. You may copy and distribute the Library (or a portion or +derivative of it, under Section 2) in object code or executable form +under the terms of Sections 1 and 2 above provided that you accompany +it with the complete corresponding machine-readable source code, which +must be distributed under the terms of Sections 1 and 2 above on a +medium customarily used for software interchange. + + If distribution of object code is made by offering access to copy +from a designated place, then offering equivalent access to copy the +source code from the same place satisfies the requirement to +distribute the source code, even though third parties are not +compelled to copy the source along with the object code. + + 5. A program that contains no derivative of any portion of the +Library, but is designed to work with the Library by being compiled or +linked with it, is called a "work that uses the Library". Such a +work, in isolation, is not a derivative work of the Library, and +therefore falls outside the scope of this License. + + However, linking a "work that uses the Library" with the Library +creates an executable that is a derivative of the Library (because it +contains portions of the Library), rather than a "work that uses the +library". The executable is therefore covered by this License. +Section 6 states terms for distribution of such executables. + + When a "work that uses the Library" uses material from a header file +that is part of the Library, the object code for the work may be a +derivative work of the Library even though the source code is not. +Whether this is true is especially significant if the work can be +linked without the Library, or if the work is itself a library. The +threshold for this to be true is not precisely defined by law. + + If such an object file uses only numerical parameters, data +structure layouts and accessors, and small macros and small inline +functions (ten lines or less in length), then the use of the object +file is unrestricted, regardless of whether it is legally a derivative +work. (Executables containing this object code plus portions of the +Library will still fall under Section 6.) + + Otherwise, if the work is a derivative of the Library, you may +distribute the object code for the work under the terms of Section 6. +Any executables containing that work also fall under Section 6, +whether or not they are linked directly with the Library itself. + + 6. As an exception to the Sections above, you may also combine or +link a "work that uses the Library" with the Library to produce a +work containing portions of the Library, and distribute that work +under terms of your choice, provided that the terms permit +modification of the work for the customer's own use and reverse +engineering for debugging such modifications. + + You must give prominent notice with each copy of the work that the +Library is used in it and that the Library and its use are covered by +this License. You must supply a copy of this License. If the work +during execution displays copyright notices, you must include the +copyright notice for the Library among them, as well as a reference +directing the user to the copy of this License. Also, you must do one +of these things: + + a) Accompany the work with the complete corresponding + machine-readable source code for the Library including whatever + changes were used in the work (which must be distributed under + Sections 1 and 2 above); and, if the work is an executable linked + with the Library, with the complete machine-readable "work that + uses the Library", as object code and/or source code, so that the + user can modify the Library and then relink to produce a modified + executable containing the modified Library. (It is understood + that the user who changes the contents of definitions files in the + Library will not necessarily be able to recompile the application + to use the modified definitions.) + + b) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (1) uses at run time a + copy of the library already present on the user's computer system, + rather than copying library functions into the executable, and (2) + will operate properly with a modified version of the library, if + the user installs one, as long as the modified version is + interface-compatible with the version that the work was made with. + + c) Accompany the work with a written offer, valid for at + least three years, to give the same user the materials + specified in Subsection 6a, above, for a charge no more + than the cost of performing this distribution. + + d) If distribution of the work is made by offering access to copy + from a designated place, offer equivalent access to copy the above + specified materials from the same place. + + e) Verify that the user has already received a copy of these + materials or that you have already sent this user a copy. + + For an executable, the required form of the "work that uses the +Library" must include any data and utility programs needed for +reproducing the executable from it. However, as a special exception, +the materials to be distributed need not include anything that is +normally distributed (in either source or binary form) with the major +components (compiler, kernel, and so on) of the operating system on +which the executable runs, unless that component itself accompanies +the executable. + + It may happen that this requirement contradicts the license +restrictions of other proprietary libraries that do not normally +accompany the operating system. Such a contradiction means you cannot +use both them and the Library together in an executable that you +distribute. + + 7. You may place library facilities that are a work based on the +Library side-by-side in a single library together with other library +facilities not covered by this License, and distribute such a combined +library, provided that the separate distribution of the work based on +the Library and of the other library facilities is otherwise +permitted, and provided that you do these two things: + + a) Accompany the combined library with a copy of the same work + based on the Library, uncombined with any other library + facilities. This must be distributed under the terms of the + Sections above. + + b) Give prominent notice with the combined library of the fact + that part of it is a work based on the Library, and explaining + where to find the accompanying uncombined form of the same work. + + 8. You may not copy, modify, sublicense, link with, or distribute +the Library except as expressly provided under this License. Any +attempt otherwise to copy, modify, sublicense, link with, or +distribute the Library is void, and will automatically terminate your +rights under this License. However, parties who have received copies, +or rights, from you under this License will not have their licenses +terminated so long as such parties remain in full compliance. + + 9. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Library or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Library (or any work based on the +Library), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Library or works based on it. + + 10. Each time you redistribute the Library (or any work based on the +Library), the recipient automatically receives a license from the +original licensor to copy, distribute, link with or modify the Library +subject to these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties with +this License. + + 11. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Library at all. For example, if a patent +license would not permit royalty-free redistribution of the Library by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Library. + +If any portion of this section is held invalid or unenforceable under any +particular circumstance, the balance of the section is intended to apply, +and the section as a whole is intended to apply in other circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 12. If the distribution and/or use of the Library is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Library under this License may add +an explicit geographical distribution limitation excluding those countries, +so that distribution is permitted only in or among countries not thus +excluded. In such case, this License incorporates the limitation as if +written in the body of this License. + + 13. The Free Software Foundation may publish revised and/or new +versions of the Lesser General Public License from time to time. +Such new versions will be similar in spirit to the present version, +but may differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Library +specifies a version number of this License which applies to it and +"any later version", you have the option of following the terms and +conditions either of that version or of any later version published by +the Free Software Foundation. If the Library does not specify a +license version number, you may choose any version ever published by +the Free Software Foundation. + + 14. If you wish to incorporate parts of the Library into other free +programs whose distribution conditions are incompatible with these, +write to the author to ask for permission. For software which is +copyrighted by the Free Software Foundation, write to the Free +Software Foundation; we sometimes make exceptions for this. Our +decision will be guided by the two goals of preserving the free status +of all derivatives of our free software and of promoting the sharing +and reuse of software generally. + + NO WARRANTY + + 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO +WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR +OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY +KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE +LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME +THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN +WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY +AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU +FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR +CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE +LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING +RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A +FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF +SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Libraries + + If you develop a new library, and you want it to be of the greatest +possible use to the public, we recommend making it free software that +everyone can redistribute and change. You can do so by permitting +redistribution under these terms (or, alternatively, under the terms of the +ordinary General Public License). + + To apply these terms, attach the following notices to the library. It is +safest to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least the +"copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +Also add information on how to contact you by electronic and paper mail. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the library, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the + library `Frob' (a library for tweaking knobs) written by James Random Hacker. + + , 1 April 1990 + Ty Coon, President of Vice + +That's all there is to it! \ No newline at end of file diff --git a/os3/PHPMailer/PHPMailer.php b/os3/PHPMailer/PHPMailer.php new file mode 100644 index 0000000..a644d2c --- /dev/null +++ b/os3/PHPMailer/PHPMailer.php @@ -0,0 +1,5126 @@ + + * @author Jim Jagielski (jimjag) + * @author Andy Prevost (codeworxtech) + * @author Brent R. Matzelle (original founder) + * @copyright 2012 - 2020 Marcus Bointon + * @copyright 2010 - 2012 Jim Jagielski + * @copyright 2004 - 2009 Andy Prevost + * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License + * @note This program is distributed in the hope that it will be useful - WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. + */ + +namespace PHPMailer\PHPMailer; + +/** + * PHPMailer - PHP email creation and transport class. + * + * @author Marcus Bointon (Synchro/coolbru) + * @author Jim Jagielski (jimjag) + * @author Andy Prevost (codeworxtech) + * @author Brent R. Matzelle (original founder) + */ +class PHPMailer +{ + const CHARSET_ASCII = 'us-ascii'; + const CHARSET_ISO88591 = 'iso-8859-1'; + const CHARSET_UTF8 = 'utf-8'; + + const CONTENT_TYPE_PLAINTEXT = 'text/plain'; + const CONTENT_TYPE_TEXT_CALENDAR = 'text/calendar'; + const CONTENT_TYPE_TEXT_HTML = 'text/html'; + const CONTENT_TYPE_MULTIPART_ALTERNATIVE = 'multipart/alternative'; + const CONTENT_TYPE_MULTIPART_MIXED = 'multipart/mixed'; + const CONTENT_TYPE_MULTIPART_RELATED = 'multipart/related'; + + const ENCODING_7BIT = '7bit'; + const ENCODING_8BIT = '8bit'; + const ENCODING_BASE64 = 'base64'; + const ENCODING_BINARY = 'binary'; + const ENCODING_QUOTED_PRINTABLE = 'quoted-printable'; + + const ENCRYPTION_STARTTLS = 'tls'; + const ENCRYPTION_SMTPS = 'ssl'; + + const ICAL_METHOD_REQUEST = 'REQUEST'; + const ICAL_METHOD_PUBLISH = 'PUBLISH'; + const ICAL_METHOD_REPLY = 'REPLY'; + const ICAL_METHOD_ADD = 'ADD'; + const ICAL_METHOD_CANCEL = 'CANCEL'; + const ICAL_METHOD_REFRESH = 'REFRESH'; + const ICAL_METHOD_COUNTER = 'COUNTER'; + const ICAL_METHOD_DECLINECOUNTER = 'DECLINECOUNTER'; + + /** + * Email priority. + * Options: null (default), 1 = High, 3 = Normal, 5 = low. + * When null, the header is not set at all. + * + * @var int|null + */ + public $Priority; + + /** + * The character set of the message. + * + * @var string + */ + public $CharSet = self::CHARSET_ISO88591; + + /** + * The MIME Content-type of the message. + * + * @var string + */ + public $ContentType = self::CONTENT_TYPE_PLAINTEXT; + + /** + * The message encoding. + * Options: "8bit", "7bit", "binary", "base64", and "quoted-printable". + * + * @var string + */ + public $Encoding = self::ENCODING_8BIT; + + /** + * Holds the most recent mailer error message. + * + * @var string + */ + public $ErrorInfo = ''; + + /** + * The From email address for the message. + * + * @var string + */ + public $From = ''; + + /** + * The From name of the message. + * + * @var string + */ + public $FromName = ''; + + /** + * The envelope sender of the message. + * This will usually be turned into a Return-Path header by the receiver, + * and is the address that bounces will be sent to. + * If not empty, will be passed via `-f` to sendmail or as the 'MAIL FROM' value over SMTP. + * + * @var string + */ + public $Sender = ''; + + /** + * The Subject of the message. + * + * @var string + */ + public $Subject = ''; + + /** + * An HTML or plain text message body. + * If HTML then call isHTML(true). + * + * @var string + */ + public $Body = ''; + + /** + * The plain-text message body. + * This body can be read by mail clients that do not have HTML email + * capability such as mutt & Eudora. + * Clients that can read HTML will view the normal Body. + * + * @var string + */ + public $AltBody = ''; + + /** + * An iCal message part body. + * Only supported in simple alt or alt_inline message types + * To generate iCal event structures, use classes like EasyPeasyICS or iCalcreator. + * + * @see http://sprain.ch/blog/downloads/php-class-easypeasyics-create-ical-files-with-php/ + * @see http://kigkonsult.se/iCalcreator/ + * + * @var string + */ + public $Ical = ''; + + /** + * Value-array of "method" in Contenttype header "text/calendar" + * + * @var string[] + */ + protected static $IcalMethods = [ + self::ICAL_METHOD_REQUEST, + self::ICAL_METHOD_PUBLISH, + self::ICAL_METHOD_REPLY, + self::ICAL_METHOD_ADD, + self::ICAL_METHOD_CANCEL, + self::ICAL_METHOD_REFRESH, + self::ICAL_METHOD_COUNTER, + self::ICAL_METHOD_DECLINECOUNTER, + ]; + + /** + * The complete compiled MIME message body. + * + * @var string + */ + protected $MIMEBody = ''; + + /** + * The complete compiled MIME message headers. + * + * @var string + */ + protected $MIMEHeader = ''; + + /** + * Extra headers that createHeader() doesn't fold in. + * + * @var string + */ + protected $mailHeader = ''; + + /** + * Word-wrap the message body to this number of chars. + * Set to 0 to not wrap. A useful value here is 78, for RFC2822 section 2.1.1 compliance. + * + * @see static::STD_LINE_LENGTH + * + * @var int + */ + public $WordWrap = 0; + + /** + * Which method to use to send mail. + * Options: "mail", "sendmail", or "smtp". + * + * @var string + */ + public $Mailer = 'mail'; + + /** + * The path to the sendmail program. + * + * @var string + */ + public $Sendmail = '/usr/sbin/sendmail'; + + /** + * Whether mail() uses a fully sendmail-compatible MTA. + * One which supports sendmail's "-oi -f" options. + * + * @var bool + */ + public $UseSendmailOptions = true; + + /** + * The email address that a reading confirmation should be sent to, also known as read receipt. + * + * @var string + */ + public $ConfirmReadingTo = ''; + + /** + * The hostname to use in the Message-ID header and as default HELO string. + * If empty, PHPMailer attempts to find one with, in order, + * $_SERVER['SERVER_NAME'], gethostname(), php_uname('n'), or the value + * 'localhost.localdomain'. + * + * @see PHPMailer::$Helo + * + * @var string + */ + public $Hostname = ''; + + /** + * An ID to be used in the Message-ID header. + * If empty, a unique id will be generated. + * You can set your own, but it must be in the format "", + * as defined in RFC5322 section 3.6.4 or it will be ignored. + * + * @see https://tools.ietf.org/html/rfc5322#section-3.6.4 + * + * @var string + */ + public $MessageID = ''; + + /** + * The message Date to be used in the Date header. + * If empty, the current date will be added. + * + * @var string + */ + public $MessageDate = ''; + + /** + * SMTP hosts. + * Either a single hostname or multiple semicolon-delimited hostnames. + * You can also specify a different port + * for each host by using this format: [hostname:port] + * (e.g. "smtp1.example.com:25;smtp2.example.com"). + * You can also specify encryption type, for example: + * (e.g. "tls://smtp1.example.com:587;ssl://smtp2.example.com:465"). + * Hosts will be tried in order. + * + * @var string + */ + public $Host = 'localhost'; + + /** + * The default SMTP server port. + * + * @var int + */ + public $Port = 25; + + /** + * The SMTP HELO/EHLO name used for the SMTP connection. + * Default is $Hostname. If $Hostname is empty, PHPMailer attempts to find + * one with the same method described above for $Hostname. + * + * @see PHPMailer::$Hostname + * + * @var string + */ + public $Helo = ''; + + /** + * What kind of encryption to use on the SMTP connection. + * Options: '', static::ENCRYPTION_STARTTLS, or static::ENCRYPTION_SMTPS. + * + * @var string + */ + public $SMTPSecure = ''; + + /** + * Whether to enable TLS encryption automatically if a server supports it, + * even if `SMTPSecure` is not set to 'tls'. + * Be aware that in PHP >= 5.6 this requires that the server's certificates are valid. + * + * @var bool + */ + public $SMTPAutoTLS = true; + + /** + * Whether to use SMTP authentication. + * Uses the Username and Password properties. + * + * @see PHPMailer::$Username + * @see PHPMailer::$Password + * + * @var bool + */ + public $SMTPAuth = false; + + /** + * Options array passed to stream_context_create when connecting via SMTP. + * + * @var array + */ + public $SMTPOptions = []; + + /** + * SMTP username. + * + * @var string + */ + public $Username = ''; + + /** + * SMTP password. + * + * @var string + */ + public $Password = ''; + + /** + * SMTP authentication type. Options are CRAM-MD5, LOGIN, PLAIN, XOAUTH2. + * If not specified, the first one from that list that the server supports will be selected. + * + * @var string + */ + public $AuthType = ''; + + /** + * An implementation of the PHPMailer OAuthTokenProvider interface. + * + * @var OAuthTokenProvider + */ + protected $oauth; + + /** + * The SMTP server timeout in seconds. + * Default of 5 minutes (300sec) is from RFC2821 section 4.5.3.2. + * + * @var int + */ + public $Timeout = 300; + + /** + * Comma separated list of DSN notifications + * 'NEVER' under no circumstances a DSN must be returned to the sender. + * If you use NEVER all other notifications will be ignored. + * 'SUCCESS' will notify you when your mail has arrived at its destination. + * 'FAILURE' will arrive if an error occurred during delivery. + * 'DELAY' will notify you if there is an unusual delay in delivery, but the actual + * delivery's outcome (success or failure) is not yet decided. + * + * @see https://tools.ietf.org/html/rfc3461 See section 4.1 for more information about NOTIFY + */ + public $dsn = ''; + + /** + * SMTP class debug output mode. + * Debug output level. + * Options: + * @see SMTP::DEBUG_OFF: No output + * @see SMTP::DEBUG_CLIENT: Client messages + * @see SMTP::DEBUG_SERVER: Client and server messages + * @see SMTP::DEBUG_CONNECTION: As SERVER plus connection status + * @see SMTP::DEBUG_LOWLEVEL: Noisy, low-level data output, rarely needed + * + * @see SMTP::$do_debug + * + * @var int + */ + public $SMTPDebug = 0; + + /** + * How to handle debug output. + * Options: + * * `echo` Output plain-text as-is, appropriate for CLI + * * `html` Output escaped, line breaks converted to `
`, appropriate for browser output + * * `error_log` Output to error log as configured in php.ini + * By default PHPMailer will use `echo` if run from a `cli` or `cli-server` SAPI, `html` otherwise. + * Alternatively, you can provide a callable expecting two params: a message string and the debug level: + * + * ```php + * $mail->Debugoutput = function($str, $level) {echo "debug level $level; message: $str";}; + * ``` + * + * Alternatively, you can pass in an instance of a PSR-3 compatible logger, though only `debug` + * level output is used: + * + * ```php + * $mail->Debugoutput = new myPsr3Logger; + * ``` + * + * @see SMTP::$Debugoutput + * + * @var string|callable|\Psr\Log\LoggerInterface + */ + public $Debugoutput = 'echo'; + + /** + * Whether to keep the SMTP connection open after each message. + * If this is set to true then the connection will remain open after a send, + * and closing the connection will require an explicit call to smtpClose(). + * It's a good idea to use this if you are sending multiple messages as it reduces overhead. + * See the mailing list example for how to use it. + * + * @var bool + */ + public $SMTPKeepAlive = false; + + /** + * Whether to split multiple to addresses into multiple messages + * or send them all in one message. + * Only supported in `mail` and `sendmail` transports, not in SMTP. + * + * @var bool + * + * @deprecated 6.0.0 PHPMailer isn't a mailing list manager! + */ + public $SingleTo = false; + + /** + * Storage for addresses when SingleTo is enabled. + * + * @var array + */ + protected $SingleToArray = []; + + /** + * Whether to generate VERP addresses on send. + * Only applicable when sending via SMTP. + * + * @see https://en.wikipedia.org/wiki/Variable_envelope_return_path + * @see http://www.postfix.org/VERP_README.html Postfix VERP info + * + * @var bool + */ + public $do_verp = false; + + /** + * Whether to allow sending messages with an empty body. + * + * @var bool + */ + public $AllowEmpty = false; + + /** + * DKIM selector. + * + * @var string + */ + public $DKIM_selector = ''; + + /** + * DKIM Identity. + * Usually the email address used as the source of the email. + * + * @var string + */ + public $DKIM_identity = ''; + + /** + * DKIM passphrase. + * Used if your key is encrypted. + * + * @var string + */ + public $DKIM_passphrase = ''; + + /** + * DKIM signing domain name. + * + * @example 'example.com' + * + * @var string + */ + public $DKIM_domain = ''; + + /** + * DKIM Copy header field values for diagnostic use. + * + * @var bool + */ + public $DKIM_copyHeaderFields = true; + + /** + * DKIM Extra signing headers. + * + * @example ['List-Unsubscribe', 'List-Help'] + * + * @var array + */ + public $DKIM_extraHeaders = []; + + /** + * DKIM private key file path. + * + * @var string + */ + public $DKIM_private = ''; + + /** + * DKIM private key string. + * + * If set, takes precedence over `$DKIM_private`. + * + * @var string + */ + public $DKIM_private_string = ''; + + /** + * Callback Action function name. + * + * The function that handles the result of the send email action. + * It is called out by send() for each email sent. + * + * Value can be any php callable: http://www.php.net/is_callable + * + * Parameters: + * bool $result result of the send action + * array $to email addresses of the recipients + * array $cc cc email addresses + * array $bcc bcc email addresses + * string $subject the subject + * string $body the email body + * string $from email address of sender + * string $extra extra information of possible use + * "smtp_transaction_id' => last smtp transaction id + * + * @var string + */ + public $action_function = ''; + + /** + * What to put in the X-Mailer header. + * Options: An empty string for PHPMailer default, whitespace/null for none, or a string to use. + * + * @var string|null + */ + public $XMailer = ''; + + /** + * Which validator to use by default when validating email addresses. + * May be a callable to inject your own validator, but there are several built-in validators. + * The default validator uses PHP's FILTER_VALIDATE_EMAIL filter_var option. + * + * @see PHPMailer::validateAddress() + * + * @var string|callable + */ + public static $validator = 'php'; + + /** + * An instance of the SMTP sender class. + * + * @var SMTP + */ + protected $smtp; + + /** + * The array of 'to' names and addresses. + * + * @var array + */ + protected $to = []; + + /** + * The array of 'cc' names and addresses. + * + * @var array + */ + protected $cc = []; + + /** + * The array of 'bcc' names and addresses. + * + * @var array + */ + protected $bcc = []; + + /** + * The array of reply-to names and addresses. + * + * @var array + */ + protected $ReplyTo = []; + + /** + * An array of all kinds of addresses. + * Includes all of $to, $cc, $bcc. + * + * @see PHPMailer::$to + * @see PHPMailer::$cc + * @see PHPMailer::$bcc + * + * @var array + */ + protected $all_recipients = []; + + /** + * An array of names and addresses queued for validation. + * In send(), valid and non duplicate entries are moved to $all_recipients + * and one of $to, $cc, or $bcc. + * This array is used only for addresses with IDN. + * + * @see PHPMailer::$to + * @see PHPMailer::$cc + * @see PHPMailer::$bcc + * @see PHPMailer::$all_recipients + * + * @var array + */ + protected $RecipientsQueue = []; + + /** + * An array of reply-to names and addresses queued for validation. + * In send(), valid and non duplicate entries are moved to $ReplyTo. + * This array is used only for addresses with IDN. + * + * @see PHPMailer::$ReplyTo + * + * @var array + */ + protected $ReplyToQueue = []; + + /** + * The array of attachments. + * + * @var array + */ + protected $attachment = []; + + /** + * The array of custom headers. + * + * @var array + */ + protected $CustomHeader = []; + + /** + * The most recent Message-ID (including angular brackets). + * + * @var string + */ + protected $lastMessageID = ''; + + /** + * The message's MIME type. + * + * @var string + */ + protected $message_type = ''; + + /** + * The array of MIME boundary strings. + * + * @var array + */ + protected $boundary = []; + + /** + * The array of available text strings for the current language. + * + * @var array + */ + protected $language = []; + + /** + * The number of errors encountered. + * + * @var int + */ + protected $error_count = 0; + + /** + * The S/MIME certificate file path. + * + * @var string + */ + protected $sign_cert_file = ''; + + /** + * The S/MIME key file path. + * + * @var string + */ + protected $sign_key_file = ''; + + /** + * The optional S/MIME extra certificates ("CA Chain") file path. + * + * @var string + */ + protected $sign_extracerts_file = ''; + + /** + * The S/MIME password for the key. + * Used only if the key is encrypted. + * + * @var string + */ + protected $sign_key_pass = ''; + + /** + * Whether to throw exceptions for errors. + * + * @var bool + */ + protected $exceptions = false; + + /** + * Unique ID used for message ID and boundaries. + * + * @var string + */ + protected $uniqueid = ''; + + /** + * The PHPMailer Version number. + * + * @var string + */ + const VERSION = '6.8.0'; + + /** + * Error severity: message only, continue processing. + * + * @var int + */ + const STOP_MESSAGE = 0; + + /** + * Error severity: message, likely ok to continue processing. + * + * @var int + */ + const STOP_CONTINUE = 1; + + /** + * Error severity: message, plus full stop, critical error reached. + * + * @var int + */ + const STOP_CRITICAL = 2; + + /** + * The SMTP standard CRLF line break. + * If you want to change line break format, change static::$LE, not this. + */ + const CRLF = "\r\n"; + + /** + * "Folding White Space" a white space string used for line folding. + */ + const FWS = ' '; + + /** + * SMTP RFC standard line ending; Carriage Return, Line Feed. + * + * @var string + */ + protected static $LE = self::CRLF; + + /** + * The maximum line length supported by mail(). + * + * Background: mail() will sometimes corrupt messages + * with headers headers longer than 65 chars, see #818. + * + * @var int + */ + const MAIL_MAX_LINE_LENGTH = 63; + + /** + * The maximum line length allowed by RFC 2822 section 2.1.1. + * + * @var int + */ + const MAX_LINE_LENGTH = 998; + + /** + * The lower maximum line length allowed by RFC 2822 section 2.1.1. + * This length does NOT include the line break + * 76 means that lines will be 77 or 78 chars depending on whether + * the line break format is LF or CRLF; both are valid. + * + * @var int + */ + const STD_LINE_LENGTH = 76; + + /** + * Constructor. + * + * @param bool $exceptions Should we throw external exceptions? + */ + public function __construct($exceptions = null) + { + if (null !== $exceptions) { + $this->exceptions = (bool) $exceptions; + } + //Pick an appropriate debug output format automatically + $this->Debugoutput = (strpos(PHP_SAPI, 'cli') !== false ? 'echo' : 'html'); + } + + /** + * Destructor. + */ + public function __destruct() + { + //Close any open SMTP connection nicely + $this->smtpClose(); + } + + /** + * Call mail() in a safe_mode-aware fashion. + * Also, unless sendmail_path points to sendmail (or something that + * claims to be sendmail), don't pass params (not a perfect fix, + * but it will do). + * + * @param string $to To + * @param string $subject Subject + * @param string $body Message Body + * @param string $header Additional Header(s) + * @param string|null $params Params + * + * @return bool + */ + private function mailPassthru($to, $subject, $body, $header, $params) + { + //Check overloading of mail function to avoid double-encoding + if ((int)ini_get('mbstring.func_overload') & 1) { + $subject = $this->secureHeader($subject); + } else { + $subject = $this->encodeHeader($this->secureHeader($subject)); + } + //Calling mail() with null params breaks + $this->edebug('Sending with mail()'); + $this->edebug('Sendmail path: ' . ini_get('sendmail_path')); + $this->edebug("Envelope sender: {$this->Sender}"); + $this->edebug("To: {$to}"); + $this->edebug("Subject: {$subject}"); + $this->edebug("Headers: {$header}"); + if (!$this->UseSendmailOptions || null === $params) { + $result = @mail($to, $subject, $body, $header); + } else { + $this->edebug("Additional params: {$params}"); + $result = @mail($to, $subject, $body, $header, $params); + } + $this->edebug('Result: ' . ($result ? 'true' : 'false')); + return $result; + } + + /** + * Output debugging info via a user-defined method. + * Only generates output if debug output is enabled. + * + * @see PHPMailer::$Debugoutput + * @see PHPMailer::$SMTPDebug + * + * @param string $str + */ + protected function edebug($str) + { + if ($this->SMTPDebug <= 0) { + return; + } + //Is this a PSR-3 logger? + if ($this->Debugoutput instanceof \Psr\Log\LoggerInterface) { + $this->Debugoutput->debug($str); + + return; + } + //Avoid clash with built-in function names + if (is_callable($this->Debugoutput) && !in_array($this->Debugoutput, ['error_log', 'html', 'echo'])) { + call_user_func($this->Debugoutput, $str, $this->SMTPDebug); + + return; + } + switch ($this->Debugoutput) { + case 'error_log': + //Don't output, just log + /** @noinspection ForgottenDebugOutputInspection */ + error_log($str); + break; + case 'html': + //Cleans up output a bit for a better looking, HTML-safe output + echo htmlentities( + preg_replace('/[\r\n]+/', '', $str), + ENT_QUOTES, + 'UTF-8' + ), "
\n"; + break; + case 'echo': + default: + //Normalize line breaks + $str = preg_replace('/\r\n|\r/m', "\n", $str); + echo gmdate('Y-m-d H:i:s'), + "\t", + //Trim trailing space + trim( + //Indent for readability, except for trailing break + str_replace( + "\n", + "\n \t ", + trim($str) + ) + ), + "\n"; + } + } + + /** + * Sets message type to HTML or plain. + * + * @param bool $isHtml True for HTML mode + */ + public function isHTML($isHtml = true) + { + if ($isHtml) { + $this->ContentType = static::CONTENT_TYPE_TEXT_HTML; + } else { + $this->ContentType = static::CONTENT_TYPE_PLAINTEXT; + } + } + + /** + * Send messages using SMTP. + */ + public function isSMTP() + { + $this->Mailer = 'smtp'; + } + + /** + * Send messages using PHP's mail() function. + */ + public function isMail() + { + $this->Mailer = 'mail'; + } + + /** + * Send messages using $Sendmail. + */ + public function isSendmail() + { + $ini_sendmail_path = ini_get('sendmail_path'); + + if (false === stripos($ini_sendmail_path, 'sendmail')) { + $this->Sendmail = '/usr/sbin/sendmail'; + } else { + $this->Sendmail = $ini_sendmail_path; + } + $this->Mailer = 'sendmail'; + } + + /** + * Send messages using qmail. + */ + public function isQmail() + { + $ini_sendmail_path = ini_get('sendmail_path'); + + if (false === stripos($ini_sendmail_path, 'qmail')) { + $this->Sendmail = '/var/qmail/bin/qmail-inject'; + } else { + $this->Sendmail = $ini_sendmail_path; + } + $this->Mailer = 'qmail'; + } + + /** + * Add a "To" address. + * + * @param string $address The email address to send to + * @param string $name + * + * @throws Exception + * + * @return bool true on success, false if address already used or invalid in some way + */ + public function addAddress($address, $name = '') + { + return $this->addOrEnqueueAnAddress('to', $address, $name); + } + + /** + * Add a "CC" address. + * + * @param string $address The email address to send to + * @param string $name + * + * @throws Exception + * + * @return bool true on success, false if address already used or invalid in some way + */ + public function addCC($address, $name = '') + { + return $this->addOrEnqueueAnAddress('cc', $address, $name); + } + + /** + * Add a "BCC" address. + * + * @param string $address The email address to send to + * @param string $name + * + * @throws Exception + * + * @return bool true on success, false if address already used or invalid in some way + */ + public function addBCC($address, $name = '') + { + return $this->addOrEnqueueAnAddress('bcc', $address, $name); + } + + /** + * Add a "Reply-To" address. + * + * @param string $address The email address to reply to + * @param string $name + * + * @throws Exception + * + * @return bool true on success, false if address already used or invalid in some way + */ + public function addReplyTo($address, $name = '') + { + return $this->addOrEnqueueAnAddress('Reply-To', $address, $name); + } + + /** + * Add an address to one of the recipient arrays or to the ReplyTo array. Because PHPMailer + * can't validate addresses with an IDN without knowing the PHPMailer::$CharSet (that can still + * be modified after calling this function), addition of such addresses is delayed until send(). + * Addresses that have been added already return false, but do not throw exceptions. + * + * @param string $kind One of 'to', 'cc', 'bcc', or 'ReplyTo' + * @param string $address The email address + * @param string $name An optional username associated with the address + * + * @throws Exception + * + * @return bool true on success, false if address already used or invalid in some way + */ + protected function addOrEnqueueAnAddress($kind, $address, $name) + { + $pos = false; + if ($address !== null) { + $address = trim($address); + $pos = strrpos($address, '@'); + } + if (false === $pos) { + //At-sign is missing. + $error_message = sprintf( + '%s (%s): %s', + $this->lang('invalid_address'), + $kind, + $address + ); + $this->setError($error_message); + $this->edebug($error_message); + if ($this->exceptions) { + throw new Exception($error_message); + } + + return false; + } + if ($name !== null && is_string($name)) { + $name = trim(preg_replace('/[\r\n]+/', '', $name)); //Strip breaks and trim + } else { + $name = ''; + } + $params = [$kind, $address, $name]; + //Enqueue addresses with IDN until we know the PHPMailer::$CharSet. + //Domain is assumed to be whatever is after the last @ symbol in the address + if (static::idnSupported() && $this->has8bitChars(substr($address, ++$pos))) { + if ('Reply-To' !== $kind) { + if (!array_key_exists($address, $this->RecipientsQueue)) { + $this->RecipientsQueue[$address] = $params; + + return true; + } + } elseif (!array_key_exists($address, $this->ReplyToQueue)) { + $this->ReplyToQueue[$address] = $params; + + return true; + } + + return false; + } + + //Immediately add standard addresses without IDN. + return call_user_func_array([$this, 'addAnAddress'], $params); + } + + /** + * Set the boundaries to use for delimiting MIME parts. + * If you override this, ensure you set all 3 boundaries to unique values. + * The default boundaries include a "=_" sequence which cannot occur in quoted-printable bodies, + * as suggested by https://www.rfc-editor.org/rfc/rfc2045#section-6.7 + * + * @return void + */ + public function setBoundaries() + { + $this->uniqueid = $this->generateId(); + $this->boundary[1] = 'b1=_' . $this->uniqueid; + $this->boundary[2] = 'b2=_' . $this->uniqueid; + $this->boundary[3] = 'b3=_' . $this->uniqueid; + } + + /** + * Add an address to one of the recipient arrays or to the ReplyTo array. + * Addresses that have been added already return false, but do not throw exceptions. + * + * @param string $kind One of 'to', 'cc', 'bcc', or 'ReplyTo' + * @param string $address The email address to send, resp. to reply to + * @param string $name + * + * @throws Exception + * + * @return bool true on success, false if address already used or invalid in some way + */ + protected function addAnAddress($kind, $address, $name = '') + { + if (!in_array($kind, ['to', 'cc', 'bcc', 'Reply-To'])) { + $error_message = sprintf( + '%s: %s', + $this->lang('Invalid recipient kind'), + $kind + ); + $this->setError($error_message); + $this->edebug($error_message); + if ($this->exceptions) { + throw new Exception($error_message); + } + + return false; + } + if (!static::validateAddress($address)) { + $error_message = sprintf( + '%s (%s): %s', + $this->lang('invalid_address'), + $kind, + $address + ); + $this->setError($error_message); + $this->edebug($error_message); + if ($this->exceptions) { + throw new Exception($error_message); + } + + return false; + } + if ('Reply-To' !== $kind) { + if (!array_key_exists(strtolower($address), $this->all_recipients)) { + $this->{$kind}[] = [$address, $name]; + $this->all_recipients[strtolower($address)] = true; + + return true; + } + } elseif (!array_key_exists(strtolower($address), $this->ReplyTo)) { + $this->ReplyTo[strtolower($address)] = [$address, $name]; + + return true; + } + + return false; + } + + /** + * Parse and validate a string containing one or more RFC822-style comma-separated email addresses + * of the form "display name
" into an array of name/address pairs. + * Uses the imap_rfc822_parse_adrlist function if the IMAP extension is available. + * Note that quotes in the name part are removed. + * + * @see http://www.andrew.cmu.edu/user/agreen1/testing/mrbs/web/Mail/RFC822.php A more careful implementation + * + * @param string $addrstr The address list string + * @param bool $useimap Whether to use the IMAP extension to parse the list + * @param string $charset The charset to use when decoding the address list string. + * + * @return array + */ + public static function parseAddresses($addrstr, $useimap = true, $charset = self::CHARSET_ISO88591) + { + $addresses = []; + if ($useimap && function_exists('imap_rfc822_parse_adrlist')) { + //Use this built-in parser if it's available + $list = imap_rfc822_parse_adrlist($addrstr, ''); + // Clear any potential IMAP errors to get rid of notices being thrown at end of script. + imap_errors(); + foreach ($list as $address) { + if ( + '.SYNTAX-ERROR.' !== $address->host && + static::validateAddress($address->mailbox . '@' . $address->host) + ) { + //Decode the name part if it's present and encoded + if ( + property_exists($address, 'personal') && + //Check for a Mbstring constant rather than using extension_loaded, which is sometimes disabled + defined('MB_CASE_UPPER') && + preg_match('/^=\?.*\?=$/s', $address->personal) + ) { + $origCharset = mb_internal_encoding(); + mb_internal_encoding($charset); + //Undo any RFC2047-encoded spaces-as-underscores + $address->personal = str_replace('_', '=20', $address->personal); + //Decode the name + $address->personal = mb_decode_mimeheader($address->personal); + mb_internal_encoding($origCharset); + } + + $addresses[] = [ + 'name' => (property_exists($address, 'personal') ? $address->personal : ''), + 'address' => $address->mailbox . '@' . $address->host, + ]; + } + } + } else { + //Use this simpler parser + $list = explode(',', $addrstr); + foreach ($list as $address) { + $address = trim($address); + //Is there a separate name part? + if (strpos($address, '<') === false) { + //No separate name, just use the whole thing + if (static::validateAddress($address)) { + $addresses[] = [ + 'name' => '', + 'address' => $address, + ]; + } + } else { + list($name, $email) = explode('<', $address); + $email = trim(str_replace('>', '', $email)); + $name = trim($name); + if (static::validateAddress($email)) { + //Check for a Mbstring constant rather than using extension_loaded, which is sometimes disabled + //If this name is encoded, decode it + if (defined('MB_CASE_UPPER') && preg_match('/^=\?.*\?=$/s', $name)) { + $origCharset = mb_internal_encoding(); + mb_internal_encoding($charset); + //Undo any RFC2047-encoded spaces-as-underscores + $name = str_replace('_', '=20', $name); + //Decode the name + $name = mb_decode_mimeheader($name); + mb_internal_encoding($origCharset); + } + $addresses[] = [ + //Remove any surrounding quotes and spaces from the name + 'name' => trim($name, '\'" '), + 'address' => $email, + ]; + } + } + } + } + + return $addresses; + } + + /** + * Set the From and FromName properties. + * + * @param string $address + * @param string $name + * @param bool $auto Whether to also set the Sender address, defaults to true + * + * @throws Exception + * + * @return bool + */ + public function setFrom($address, $name = '', $auto = true) + { + $address = trim((string)$address); + $name = trim(preg_replace('/[\r\n]+/', '', $name)); //Strip breaks and trim + //Don't validate now addresses with IDN. Will be done in send(). + $pos = strrpos($address, '@'); + if ( + (false === $pos) + || ((!$this->has8bitChars(substr($address, ++$pos)) || !static::idnSupported()) + && !static::validateAddress($address)) + ) { + $error_message = sprintf( + '%s (From): %s', + $this->lang('invalid_address'), + $address + ); + $this->setError($error_message); + $this->edebug($error_message); + if ($this->exceptions) { + throw new Exception($error_message); + } + + return false; + } + $this->From = $address; + $this->FromName = $name; + if ($auto && empty($this->Sender)) { + $this->Sender = $address; + } + + return true; + } + + /** + * Return the Message-ID header of the last email. + * Technically this is the value from the last time the headers were created, + * but it's also the message ID of the last sent message except in + * pathological cases. + * + * @return string + */ + public function getLastMessageID() + { + return $this->lastMessageID; + } + + /** + * Check that a string looks like an email address. + * Validation patterns supported: + * * `auto` Pick best pattern automatically; + * * `pcre8` Use the squiloople.com pattern, requires PCRE > 8.0; + * * `pcre` Use old PCRE implementation; + * * `php` Use PHP built-in FILTER_VALIDATE_EMAIL; + * * `html5` Use the pattern given by the HTML5 spec for 'email' type form input elements. + * * `noregex` Don't use a regex: super fast, really dumb. + * Alternatively you may pass in a callable to inject your own validator, for example: + * + * ```php + * PHPMailer::validateAddress('user@example.com', function($address) { + * return (strpos($address, '@') !== false); + * }); + * ``` + * + * You can also set the PHPMailer::$validator static to a callable, allowing built-in methods to use your validator. + * + * @param string $address The email address to check + * @param string|callable $patternselect Which pattern to use + * + * @return bool + */ + public static function validateAddress($address, $patternselect = null) + { + if (null === $patternselect) { + $patternselect = static::$validator; + } + //Don't allow strings as callables, see SECURITY.md and CVE-2021-3603 + if (is_callable($patternselect) && !is_string($patternselect)) { + return call_user_func($patternselect, $address); + } + //Reject line breaks in addresses; it's valid RFC5322, but not RFC5321 + if (strpos($address, "\n") !== false || strpos($address, "\r") !== false) { + return false; + } + switch ($patternselect) { + case 'pcre': //Kept for BC + case 'pcre8': + /* + * A more complex and more permissive version of the RFC5322 regex on which FILTER_VALIDATE_EMAIL + * is based. + * In addition to the addresses allowed by filter_var, also permits: + * * dotless domains: `a@b` + * * comments: `1234 @ local(blah) .machine .example` + * * quoted elements: `'"test blah"@example.org'` + * * numeric TLDs: `a@b.123` + * * unbracketed IPv4 literals: `a@192.168.0.1` + * * IPv6 literals: 'first.last@[IPv6:a1::]' + * Not all of these will necessarily work for sending! + * + * @see http://squiloople.com/2009/12/20/email-address-validation/ + * @copyright 2009-2010 Michael Rushton + * Feel free to use and redistribute this code. But please keep this copyright notice. + */ + return (bool) preg_match( + '/^(?!(?>(?1)"?(?>\\\[ -~]|[^"])"?(?1)){255,})(?!(?>(?1)"?(?>\\\[ -~]|[^"])"?(?1)){65,}@)' . + '((?>(?>(?>((?>(?>(?>\x0D\x0A)?[\t ])+|(?>[\t ]*\x0D\x0A)?[\t ]+)?)(\((?>(?2)' . + '(?>[\x01-\x08\x0B\x0C\x0E-\'*-\[\]-\x7F]|\\\[\x00-\x7F]|(?3)))*(?2)\)))+(?2))|(?2))?)' . + '([!#-\'*+\/-9=?^-~-]+|"(?>(?2)(?>[\x01-\x08\x0B\x0C\x0E-!#-\[\]-\x7F]|\\\[\x00-\x7F]))*' . + '(?2)")(?>(?1)\.(?1)(?4))*(?1)@(?!(?1)[a-z0-9-]{64,})(?1)(?>([a-z0-9](?>[a-z0-9-]*[a-z0-9])?)' . + '(?>(?1)\.(?!(?1)[a-z0-9-]{64,})(?1)(?5)){0,126}|\[(?:(?>IPv6:(?>([a-f0-9]{1,4})(?>:(?6)){7}' . + '|(?!(?:.*[a-f0-9][:\]]){8,})((?6)(?>:(?6)){0,6})?::(?7)?))|(?>(?>IPv6:(?>(?6)(?>:(?6)){5}:' . + '|(?!(?:.*[a-f0-9]:){6,})(?8)?::(?>((?6)(?>:(?6)){0,4}):)?))?(25[0-5]|2[0-4][0-9]|1[0-9]{2}' . + '|[1-9]?[0-9])(?>\.(?9)){3}))\])(?1)$/isD', + $address + ); + case 'html5': + /* + * This is the pattern used in the HTML5 spec for validation of 'email' type form input elements. + * + * @see https://html.spec.whatwg.org/#e-mail-state-(type=email) + */ + return (bool) preg_match( + '/^[a-zA-Z0-9.!#$%&\'*+\/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}' . + '[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/sD', + $address + ); + case 'php': + default: + return filter_var($address, FILTER_VALIDATE_EMAIL) !== false; + } + } + + /** + * Tells whether IDNs (Internationalized Domain Names) are supported or not. This requires the + * `intl` and `mbstring` PHP extensions. + * + * @return bool `true` if required functions for IDN support are present + */ + public static function idnSupported() + { + return function_exists('idn_to_ascii') && function_exists('mb_convert_encoding'); + } + + /** + * Converts IDN in given email address to its ASCII form, also known as punycode, if possible. + * Important: Address must be passed in same encoding as currently set in PHPMailer::$CharSet. + * This function silently returns unmodified address if: + * - No conversion is necessary (i.e. domain name is not an IDN, or is already in ASCII form) + * - Conversion to punycode is impossible (e.g. required PHP functions are not available) + * or fails for any reason (e.g. domain contains characters not allowed in an IDN). + * + * @see PHPMailer::$CharSet + * + * @param string $address The email address to convert + * + * @return string The encoded address in ASCII form + */ + public function punyencodeAddress($address) + { + //Verify we have required functions, CharSet, and at-sign. + $pos = strrpos($address, '@'); + if ( + !empty($this->CharSet) && + false !== $pos && + static::idnSupported() + ) { + $domain = substr($address, ++$pos); + //Verify CharSet string is a valid one, and domain properly encoded in this CharSet. + if ($this->has8bitChars($domain) && @mb_check_encoding($domain, $this->CharSet)) { + //Convert the domain from whatever charset it's in to UTF-8 + $domain = mb_convert_encoding($domain, self::CHARSET_UTF8, $this->CharSet); + //Ignore IDE complaints about this line - method signature changed in PHP 5.4 + $errorcode = 0; + if (defined('INTL_IDNA_VARIANT_UTS46')) { + //Use the current punycode standard (appeared in PHP 7.2) + $punycode = idn_to_ascii( + $domain, + \IDNA_DEFAULT | \IDNA_USE_STD3_RULES | \IDNA_CHECK_BIDI | + \IDNA_CHECK_CONTEXTJ | \IDNA_NONTRANSITIONAL_TO_ASCII, + \INTL_IDNA_VARIANT_UTS46 + ); + } elseif (defined('INTL_IDNA_VARIANT_2003')) { + //Fall back to this old, deprecated/removed encoding + $punycode = idn_to_ascii($domain, $errorcode, \INTL_IDNA_VARIANT_2003); + } else { + //Fall back to a default we don't know about + $punycode = idn_to_ascii($domain, $errorcode); + } + if (false !== $punycode) { + return substr($address, 0, $pos) . $punycode; + } + } + } + + return $address; + } + + /** + * Create a message and send it. + * Uses the sending method specified by $Mailer. + * + * @throws Exception + * + * @return bool false on error - See the ErrorInfo property for details of the error + */ + public function send() + { + try { + if (!$this->preSend()) { + return false; + } + + return $this->postSend(); + } catch (Exception $exc) { + $this->mailHeader = ''; + $this->setError($exc->getMessage()); + if ($this->exceptions) { + throw $exc; + } + + return false; + } + } + + /** + * Prepare a message for sending. + * + * @throws Exception + * + * @return bool + */ + public function preSend() + { + if ( + 'smtp' === $this->Mailer + || ('mail' === $this->Mailer && (\PHP_VERSION_ID >= 80000 || stripos(PHP_OS, 'WIN') === 0)) + ) { + //SMTP mandates RFC-compliant line endings + //and it's also used with mail() on Windows + static::setLE(self::CRLF); + } else { + //Maintain backward compatibility with legacy Linux command line mailers + static::setLE(PHP_EOL); + } + //Check for buggy PHP versions that add a header with an incorrect line break + if ( + 'mail' === $this->Mailer + && ((\PHP_VERSION_ID >= 70000 && \PHP_VERSION_ID < 70017) + || (\PHP_VERSION_ID >= 70100 && \PHP_VERSION_ID < 70103)) + && ini_get('mail.add_x_header') === '1' + && stripos(PHP_OS, 'WIN') === 0 + ) { + trigger_error($this->lang('buggy_php'), E_USER_WARNING); + } + + try { + $this->error_count = 0; //Reset errors + $this->mailHeader = ''; + + //Dequeue recipient and Reply-To addresses with IDN + foreach (array_merge($this->RecipientsQueue, $this->ReplyToQueue) as $params) { + $params[1] = $this->punyencodeAddress($params[1]); + call_user_func_array([$this, 'addAnAddress'], $params); + } + if (count($this->to) + count($this->cc) + count($this->bcc) < 1) { + throw new Exception($this->lang('provide_address'), self::STOP_CRITICAL); + } + + //Validate From, Sender, and ConfirmReadingTo addresses + foreach (['From', 'Sender', 'ConfirmReadingTo'] as $address_kind) { + $this->{$address_kind} = trim($this->{$address_kind}); + if (empty($this->{$address_kind})) { + continue; + } + $this->{$address_kind} = $this->punyencodeAddress($this->{$address_kind}); + if (!static::validateAddress($this->{$address_kind})) { + $error_message = sprintf( + '%s (%s): %s', + $this->lang('invalid_address'), + $address_kind, + $this->{$address_kind} + ); + $this->setError($error_message); + $this->edebug($error_message); + if ($this->exceptions) { + throw new Exception($error_message); + } + + return false; + } + } + + //Set whether the message is multipart/alternative + if ($this->alternativeExists()) { + $this->ContentType = static::CONTENT_TYPE_MULTIPART_ALTERNATIVE; + } + + $this->setMessageType(); + //Refuse to send an empty message unless we are specifically allowing it + if (!$this->AllowEmpty && empty($this->Body)) { + throw new Exception($this->lang('empty_message'), self::STOP_CRITICAL); + } + + //Trim subject consistently + $this->Subject = trim($this->Subject); + //Create body before headers in case body makes changes to headers (e.g. altering transfer encoding) + $this->MIMEHeader = ''; + $this->MIMEBody = $this->createBody(); + //createBody may have added some headers, so retain them + $tempheaders = $this->MIMEHeader; + $this->MIMEHeader = $this->createHeader(); + $this->MIMEHeader .= $tempheaders; + + //To capture the complete message when using mail(), create + //an extra header list which createHeader() doesn't fold in + if ('mail' === $this->Mailer) { + if (count($this->to) > 0) { + $this->mailHeader .= $this->addrAppend('To', $this->to); + } else { + $this->mailHeader .= $this->headerLine('To', 'undisclosed-recipients:;'); + } + $this->mailHeader .= $this->headerLine( + 'Subject', + $this->encodeHeader($this->secureHeader($this->Subject)) + ); + } + + //Sign with DKIM if enabled + if ( + !empty($this->DKIM_domain) + && !empty($this->DKIM_selector) + && (!empty($this->DKIM_private_string) + || (!empty($this->DKIM_private) + && static::isPermittedPath($this->DKIM_private) + && file_exists($this->DKIM_private) + ) + ) + ) { + $header_dkim = $this->DKIM_Add( + $this->MIMEHeader . $this->mailHeader, + $this->encodeHeader($this->secureHeader($this->Subject)), + $this->MIMEBody + ); + $this->MIMEHeader = static::stripTrailingWSP($this->MIMEHeader) . static::$LE . + static::normalizeBreaks($header_dkim) . static::$LE; + } + + return true; + } catch (Exception $exc) { + $this->setError($exc->getMessage()); + if ($this->exceptions) { + throw $exc; + } + + return false; + } + } + + /** + * Actually send a message via the selected mechanism. + * + * @throws Exception + * + * @return bool + */ + public function postSend() + { + try { + //Choose the mailer and send through it + switch ($this->Mailer) { + case 'sendmail': + case 'qmail': + return $this->sendmailSend($this->MIMEHeader, $this->MIMEBody); + case 'smtp': + return $this->smtpSend($this->MIMEHeader, $this->MIMEBody); + case 'mail': + return $this->mailSend($this->MIMEHeader, $this->MIMEBody); + default: + $sendMethod = $this->Mailer . 'Send'; + if (method_exists($this, $sendMethod)) { + return $this->{$sendMethod}($this->MIMEHeader, $this->MIMEBody); + } + + return $this->mailSend($this->MIMEHeader, $this->MIMEBody); + } + } catch (Exception $exc) { + $this->setError($exc->getMessage()); + $this->edebug($exc->getMessage()); + if ($this->Mailer === 'smtp' && $this->SMTPKeepAlive == true && $this->smtp->connected()) { + $this->smtp->reset(); + } + if ($this->exceptions) { + throw $exc; + } + } + + return false; + } + + /** + * Send mail using the $Sendmail program. + * + * @see PHPMailer::$Sendmail + * + * @param string $header The message headers + * @param string $body The message body + * + * @throws Exception + * + * @return bool + */ + protected function sendmailSend($header, $body) + { + if ($this->Mailer === 'qmail') { + $this->edebug('Sending with qmail'); + } else { + $this->edebug('Sending with sendmail'); + } + $header = static::stripTrailingWSP($header) . static::$LE . static::$LE; + //This sets the SMTP envelope sender which gets turned into a return-path header by the receiver + //A space after `-f` is optional, but there is a long history of its presence + //causing problems, so we don't use one + //Exim docs: http://www.exim.org/exim-html-current/doc/html/spec_html/ch-the_exim_command_line.html + //Sendmail docs: http://www.sendmail.org/~ca/email/man/sendmail.html + //Qmail docs: http://www.qmail.org/man/man8/qmail-inject.html + //Example problem: https://www.drupal.org/node/1057954 + + //PHP 5.6 workaround + $sendmail_from_value = ini_get('sendmail_from'); + if (empty($this->Sender) && !empty($sendmail_from_value)) { + //PHP config has a sender address we can use + $this->Sender = ini_get('sendmail_from'); + } + //CVE-2016-10033, CVE-2016-10045: Don't pass -f if characters will be escaped. + if (!empty($this->Sender) && static::validateAddress($this->Sender) && self::isShellSafe($this->Sender)) { + if ($this->Mailer === 'qmail') { + $sendmailFmt = '%s -f%s'; + } else { + $sendmailFmt = '%s -oi -f%s -t'; + } + } else { + //allow sendmail to choose a default envelope sender. It may + //seem preferable to force it to use the From header as with + //SMTP, but that introduces new problems (see + //), and + //it has historically worked this way. + $sendmailFmt = '%s -oi -t'; + } + + $sendmail = sprintf($sendmailFmt, escapeshellcmd($this->Sendmail), $this->Sender); + $this->edebug('Sendmail path: ' . $this->Sendmail); + $this->edebug('Sendmail command: ' . $sendmail); + $this->edebug('Envelope sender: ' . $this->Sender); + $this->edebug("Headers: {$header}"); + + if ($this->SingleTo) { + foreach ($this->SingleToArray as $toAddr) { + $mail = @popen($sendmail, 'w'); + if (!$mail) { + throw new Exception($this->lang('execute') . $this->Sendmail, self::STOP_CRITICAL); + } + $this->edebug("To: {$toAddr}"); + fwrite($mail, 'To: ' . $toAddr . "\n"); + fwrite($mail, $header); + fwrite($mail, $body); + $result = pclose($mail); + $addrinfo = static::parseAddresses($toAddr, true, $this->CharSet); + $this->doCallback( + ($result === 0), + [[$addrinfo['address'], $addrinfo['name']]], + $this->cc, + $this->bcc, + $this->Subject, + $body, + $this->From, + [] + ); + $this->edebug("Result: " . ($result === 0 ? 'true' : 'false')); + if (0 !== $result) { + throw new Exception($this->lang('execute') . $this->Sendmail, self::STOP_CRITICAL); + } + } + } else { + $mail = @popen($sendmail, 'w'); + if (!$mail) { + throw new Exception($this->lang('execute') . $this->Sendmail, self::STOP_CRITICAL); + } + fwrite($mail, $header); + fwrite($mail, $body); + $result = pclose($mail); + $this->doCallback( + ($result === 0), + $this->to, + $this->cc, + $this->bcc, + $this->Subject, + $body, + $this->From, + [] + ); + $this->edebug("Result: " . ($result === 0 ? 'true' : 'false')); + if (0 !== $result) { + throw new Exception($this->lang('execute') . $this->Sendmail, self::STOP_CRITICAL); + } + } + + return true; + } + + /** + * Fix CVE-2016-10033 and CVE-2016-10045 by disallowing potentially unsafe shell characters. + * Note that escapeshellarg and escapeshellcmd are inadequate for our purposes, especially on Windows. + * + * @see https://github.com/PHPMailer/PHPMailer/issues/924 CVE-2016-10045 bug report + * + * @param string $string The string to be validated + * + * @return bool + */ + protected static function isShellSafe($string) + { + //It's not possible to use shell commands safely (which includes the mail() function) without escapeshellarg, + //but some hosting providers disable it, creating a security problem that we don't want to have to deal with, + //so we don't. + if (!function_exists('escapeshellarg') || !function_exists('escapeshellcmd')) { + return false; + } + + if ( + escapeshellcmd($string) !== $string + || !in_array(escapeshellarg($string), ["'$string'", "\"$string\""]) + ) { + return false; + } + + $length = strlen($string); + + for ($i = 0; $i < $length; ++$i) { + $c = $string[$i]; + + //All other characters have a special meaning in at least one common shell, including = and +. + //Full stop (.) has a special meaning in cmd.exe, but its impact should be negligible here. + //Note that this does permit non-Latin alphanumeric characters based on the current locale. + if (!ctype_alnum($c) && strpos('@_-.', $c) === false) { + return false; + } + } + + return true; + } + + /** + * Check whether a file path is of a permitted type. + * Used to reject URLs and phar files from functions that access local file paths, + * such as addAttachment. + * + * @param string $path A relative or absolute path to a file + * + * @return bool + */ + protected static function isPermittedPath($path) + { + //Matches scheme definition from https://tools.ietf.org/html/rfc3986#section-3.1 + return !preg_match('#^[a-z][a-z\d+.-]*://#i', $path); + } + + /** + * Check whether a file path is safe, accessible, and readable. + * + * @param string $path A relative or absolute path to a file + * + * @return bool + */ + protected static function fileIsAccessible($path) + { + if (!static::isPermittedPath($path)) { + return false; + } + $readable = is_file($path); + //If not a UNC path (expected to start with \\), check read permission, see #2069 + if (strpos($path, '\\\\') !== 0) { + $readable = $readable && is_readable($path); + } + return $readable; + } + + /** + * Send mail using the PHP mail() function. + * + * @see http://www.php.net/manual/en/book.mail.php + * + * @param string $header The message headers + * @param string $body The message body + * + * @throws Exception + * + * @return bool + */ + protected function mailSend($header, $body) + { + $header = static::stripTrailingWSP($header) . static::$LE . static::$LE; + + $toArr = []; + foreach ($this->to as $toaddr) { + $toArr[] = $this->addrFormat($toaddr); + } + $to = trim(implode(', ', $toArr)); + + //If there are no To-addresses (e.g. when sending only to BCC-addresses) + //the following should be added to get a correct DKIM-signature. + //Compare with $this->preSend() + if ($to === '') { + $to = 'undisclosed-recipients:;'; + } + + $params = null; + //This sets the SMTP envelope sender which gets turned into a return-path header by the receiver + //A space after `-f` is optional, but there is a long history of its presence + //causing problems, so we don't use one + //Exim docs: http://www.exim.org/exim-html-current/doc/html/spec_html/ch-the_exim_command_line.html + //Sendmail docs: http://www.sendmail.org/~ca/email/man/sendmail.html + //Qmail docs: http://www.qmail.org/man/man8/qmail-inject.html + //Example problem: https://www.drupal.org/node/1057954 + //CVE-2016-10033, CVE-2016-10045: Don't pass -f if characters will be escaped. + + //PHP 5.6 workaround + $sendmail_from_value = ini_get('sendmail_from'); + if (empty($this->Sender) && !empty($sendmail_from_value)) { + //PHP config has a sender address we can use + $this->Sender = ini_get('sendmail_from'); + } + if (!empty($this->Sender) && static::validateAddress($this->Sender)) { + if (self::isShellSafe($this->Sender)) { + $params = sprintf('-f%s', $this->Sender); + } + $old_from = ini_get('sendmail_from'); + ini_set('sendmail_from', $this->Sender); + } + $result = false; + if ($this->SingleTo && count($toArr) > 1) { + foreach ($toArr as $toAddr) { + $result = $this->mailPassthru($toAddr, $this->Subject, $body, $header, $params); + $addrinfo = static::parseAddresses($toAddr, true, $this->CharSet); + $this->doCallback( + $result, + [[$addrinfo['address'], $addrinfo['name']]], + $this->cc, + $this->bcc, + $this->Subject, + $body, + $this->From, + [] + ); + } + } else { + $result = $this->mailPassthru($to, $this->Subject, $body, $header, $params); + $this->doCallback($result, $this->to, $this->cc, $this->bcc, $this->Subject, $body, $this->From, []); + } + if (isset($old_from)) { + ini_set('sendmail_from', $old_from); + } + if (!$result) { + throw new Exception($this->lang('instantiate'), self::STOP_CRITICAL); + } + + return true; + } + + /** + * Get an instance to use for SMTP operations. + * Override this function to load your own SMTP implementation, + * or set one with setSMTPInstance. + * + * @return SMTP + */ + public function getSMTPInstance() + { + if (!is_object($this->smtp)) { + $this->smtp = new SMTP(); + } + + return $this->smtp; + } + + /** + * Provide an instance to use for SMTP operations. + * + * @return SMTP + */ + public function setSMTPInstance(SMTP $smtp) + { + $this->smtp = $smtp; + + return $this->smtp; + } + + /** + * Send mail via SMTP. + * Returns false if there is a bad MAIL FROM, RCPT, or DATA input. + * + * @see PHPMailer::setSMTPInstance() to use a different class. + * + * @uses \PHPMailer\PHPMailer\SMTP + * + * @param string $header The message headers + * @param string $body The message body + * + * @throws Exception + * + * @return bool + */ + protected function smtpSend($header, $body) + { + $header = static::stripTrailingWSP($header) . static::$LE . static::$LE; + $bad_rcpt = []; + if (!$this->smtpConnect($this->SMTPOptions)) { + throw new Exception($this->lang('smtp_connect_failed'), self::STOP_CRITICAL); + } + //Sender already validated in preSend() + if ('' === $this->Sender) { + $smtp_from = $this->From; + } else { + $smtp_from = $this->Sender; + } + if (!$this->smtp->mail($smtp_from)) { + $this->setError($this->lang('from_failed') . $smtp_from . ' : ' . implode(',', $this->smtp->getError())); + throw new Exception($this->ErrorInfo, self::STOP_CRITICAL); + } + + $callbacks = []; + //Attempt to send to all recipients + foreach ([$this->to, $this->cc, $this->bcc] as $togroup) { + foreach ($togroup as $to) { + if (!$this->smtp->recipient($to[0], $this->dsn)) { + $error = $this->smtp->getError(); + $bad_rcpt[] = ['to' => $to[0], 'error' => $error['detail']]; + $isSent = false; + } else { + $isSent = true; + } + + $callbacks[] = ['issent' => $isSent, 'to' => $to[0], 'name' => $to[1]]; + } + } + + //Only send the DATA command if we have viable recipients + if ((count($this->all_recipients) > count($bad_rcpt)) && !$this->smtp->data($header . $body)) { + throw new Exception($this->lang('data_not_accepted'), self::STOP_CRITICAL); + } + + $smtp_transaction_id = $this->smtp->getLastTransactionID(); + + if ($this->SMTPKeepAlive) { + $this->smtp->reset(); + } else { + $this->smtp->quit(); + $this->smtp->close(); + } + + foreach ($callbacks as $cb) { + $this->doCallback( + $cb['issent'], + [[$cb['to'], $cb['name']]], + [], + [], + $this->Subject, + $body, + $this->From, + ['smtp_transaction_id' => $smtp_transaction_id] + ); + } + + //Create error message for any bad addresses + if (count($bad_rcpt) > 0) { + $errstr = ''; + foreach ($bad_rcpt as $bad) { + $errstr .= $bad['to'] . ': ' . $bad['error']; + } + throw new Exception($this->lang('recipients_failed') . $errstr, self::STOP_CONTINUE); + } + + return true; + } + + /** + * Initiate a connection to an SMTP server. + * Returns false if the operation failed. + * + * @param array $options An array of options compatible with stream_context_create() + * + * @throws Exception + * + * @uses \PHPMailer\PHPMailer\SMTP + * + * @return bool + */ + public function smtpConnect($options = null) + { + if (null === $this->smtp) { + $this->smtp = $this->getSMTPInstance(); + } + + //If no options are provided, use whatever is set in the instance + if (null === $options) { + $options = $this->SMTPOptions; + } + + //Already connected? + if ($this->smtp->connected()) { + return true; + } + + $this->smtp->setTimeout($this->Timeout); + $this->smtp->setDebugLevel($this->SMTPDebug); + $this->smtp->setDebugOutput($this->Debugoutput); + $this->smtp->setVerp($this->do_verp); + if ($this->Host === null) { + $this->Host = 'localhost'; + } + $hosts = explode(';', $this->Host); + $lastexception = null; + + foreach ($hosts as $hostentry) { + $hostinfo = []; + if ( + !preg_match( + '/^(?:(ssl|tls):\/\/)?(.+?)(?::(\d+))?$/', + trim($hostentry), + $hostinfo + ) + ) { + $this->edebug($this->lang('invalid_hostentry') . ' ' . trim($hostentry)); + //Not a valid host entry + continue; + } + //$hostinfo[1]: optional ssl or tls prefix + //$hostinfo[2]: the hostname + //$hostinfo[3]: optional port number + //The host string prefix can temporarily override the current setting for SMTPSecure + //If it's not specified, the default value is used + + //Check the host name is a valid name or IP address before trying to use it + if (!static::isValidHost($hostinfo[2])) { + $this->edebug($this->lang('invalid_host') . ' ' . $hostinfo[2]); + continue; + } + $prefix = ''; + $secure = $this->SMTPSecure; + $tls = (static::ENCRYPTION_STARTTLS === $this->SMTPSecure); + if ('ssl' === $hostinfo[1] || ('' === $hostinfo[1] && static::ENCRYPTION_SMTPS === $this->SMTPSecure)) { + $prefix = 'ssl://'; + $tls = false; //Can't have SSL and TLS at the same time + $secure = static::ENCRYPTION_SMTPS; + } elseif ('tls' === $hostinfo[1]) { + $tls = true; + //TLS doesn't use a prefix + $secure = static::ENCRYPTION_STARTTLS; + } + //Do we need the OpenSSL extension? + $sslext = defined('OPENSSL_ALGO_SHA256'); + if (static::ENCRYPTION_STARTTLS === $secure || static::ENCRYPTION_SMTPS === $secure) { + //Check for an OpenSSL constant rather than using extension_loaded, which is sometimes disabled + if (!$sslext) { + throw new Exception($this->lang('extension_missing') . 'openssl', self::STOP_CRITICAL); + } + } + $host = $hostinfo[2]; + $port = $this->Port; + if ( + array_key_exists(3, $hostinfo) && + is_numeric($hostinfo[3]) && + $hostinfo[3] > 0 && + $hostinfo[3] < 65536 + ) { + $port = (int) $hostinfo[3]; + } + if ($this->smtp->connect($prefix . $host, $port, $this->Timeout, $options)) { + try { + if ($this->Helo) { + $hello = $this->Helo; + } else { + $hello = $this->serverHostname(); + } + $this->smtp->hello($hello); + //Automatically enable TLS encryption if: + //* it's not disabled + //* we have openssl extension + //* we are not already using SSL + //* the server offers STARTTLS + if ($this->SMTPAutoTLS && $sslext && 'ssl' !== $secure && $this->smtp->getServerExt('STARTTLS')) { + $tls = true; + } + if ($tls) { + if (!$this->smtp->startTLS()) { + $message = $this->getSmtpErrorMessage('connect_host'); + throw new Exception($message); + } + //We must resend EHLO after TLS negotiation + $this->smtp->hello($hello); + } + if ( + $this->SMTPAuth && !$this->smtp->authenticate( + $this->Username, + $this->Password, + $this->AuthType, + $this->oauth + ) + ) { + throw new Exception($this->lang('authenticate')); + } + + return true; + } catch (Exception $exc) { + $lastexception = $exc; + $this->edebug($exc->getMessage()); + //We must have connected, but then failed TLS or Auth, so close connection nicely + $this->smtp->quit(); + } + } + } + //If we get here, all connection attempts have failed, so close connection hard + $this->smtp->close(); + //As we've caught all exceptions, just report whatever the last one was + if ($this->exceptions && null !== $lastexception) { + throw $lastexception; + } + if ($this->exceptions) { + // no exception was thrown, likely $this->smtp->connect() failed + $message = $this->getSmtpErrorMessage('connect_host'); + throw new Exception($message); + } + + return false; + } + + /** + * Close the active SMTP session if one exists. + */ + public function smtpClose() + { + if ((null !== $this->smtp) && $this->smtp->connected()) { + $this->smtp->quit(); + $this->smtp->close(); + } + } + + /** + * Set the language for error messages. + * The default language is English. + * + * @param string $langcode ISO 639-1 2-character language code (e.g. French is "fr") + * Optionally, the language code can be enhanced with a 4-character + * script annotation and/or a 2-character country annotation. + * @param string $lang_path Path to the language file directory, with trailing separator (slash) + * Do not set this from user input! + * + * @return bool Returns true if the requested language was loaded, false otherwise. + */ + public function setLanguage($langcode = 'en', $lang_path = '') + { + //Backwards compatibility for renamed language codes + $renamed_langcodes = [ + 'br' => 'pt_br', + 'cz' => 'cs', + 'dk' => 'da', + 'no' => 'nb', + 'se' => 'sv', + 'rs' => 'sr', + 'tg' => 'tl', + 'am' => 'hy', + ]; + + if (array_key_exists($langcode, $renamed_langcodes)) { + $langcode = $renamed_langcodes[$langcode]; + } + + //Define full set of translatable strings in English + $PHPMAILER_LANG = [ + 'authenticate' => 'SMTP Error: Could not authenticate.', + 'buggy_php' => 'Your version of PHP is affected by a bug that may result in corrupted messages.' . + ' To fix it, switch to sending using SMTP, disable the mail.add_x_header option in' . + ' your php.ini, switch to MacOS or Linux, or upgrade your PHP to version 7.0.17+ or 7.1.3+.', + 'connect_host' => 'SMTP Error: Could not connect to SMTP host.', + 'data_not_accepted' => 'SMTP Error: data not accepted.', + 'empty_message' => 'Message body empty', + 'encoding' => 'Unknown encoding: ', + 'execute' => 'Could not execute: ', + 'extension_missing' => 'Extension missing: ', + 'file_access' => 'Could not access file: ', + 'file_open' => 'File Error: Could not open file: ', + 'from_failed' => 'The following From address failed: ', + 'instantiate' => 'Could not instantiate mail function.', + 'invalid_address' => 'Invalid address: ', + 'invalid_header' => 'Invalid header name or value', + 'invalid_hostentry' => 'Invalid hostentry: ', + 'invalid_host' => 'Invalid host: ', + 'mailer_not_supported' => ' mailer is not supported.', + 'provide_address' => 'You must provide at least one recipient email address.', + 'recipients_failed' => 'SMTP Error: The following recipients failed: ', + 'signing' => 'Signing Error: ', + 'smtp_code' => 'SMTP code: ', + 'smtp_code_ex' => 'Additional SMTP info: ', + 'smtp_connect_failed' => 'SMTP connect() failed.', + 'smtp_detail' => 'Detail: ', + 'smtp_error' => 'SMTP server error: ', + 'variable_set' => 'Cannot set or reset variable: ', + ]; + if (empty($lang_path)) { + //Calculate an absolute path so it can work if CWD is not here + $lang_path = dirname(__DIR__) . DIRECTORY_SEPARATOR . 'language' . DIRECTORY_SEPARATOR; + } + + //Validate $langcode + $foundlang = true; + $langcode = strtolower($langcode); + if ( + !preg_match('/^(?P[a-z]{2})(?P + + + \ No newline at end of file diff --git a/os3/config.ini.php b/os3/config.ini.php new file mode 100644 index 0000000..efb9e8e --- /dev/null +++ b/os3/config.ini.php @@ -0,0 +1,18 @@ + \ No newline at end of file diff --git a/os3/config.php b/os3/config.php new file mode 100644 index 0000000..e779ee7 --- /dev/null +++ b/os3/config.php @@ -0,0 +1,740 @@ +errorInfo(); +if ($err[0]) die('Fatal database connection error: '.$err[0]); +$_DDATA['pdo']->setAttribute(PDO::ATTR_EMULATE_PREPARES, false); +$_DDATA['pdo']->setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE, PDO::FETCH_ASSOC); + +$_DDATA['tables'] = $_DDATA['pdo']->query( + 'SHOW TABLES FROM `'.$_DDATA['database'].'` LIKE \''.$_DDATA['tbprefix'].'%\';' +); +$err = $_DDATA['tables']->errorInfo(); +if ($err[0] == '00000') { + $_DDATA['tables'] = $_DDATA['tables']->fetchAll(PDO::FETCH_NUM); + foreach($_DDATA['tables'] as $key => $value) + $_DDATA['tables'][$key] = $value[0]; +} else die('Fatal database read error: '.$err[2]); + + +// ***** Create the configuration table if it doesn't exist +if (!in_array($_DDATA['tbprefix'].'config', $_DDATA['tables'])) { + $create = $_DDATA['pdo']->query( + 'CREATE TABLE `'.$_DDATA['tbprefix'].'config` ( + `version` VARCHAR(8) NOT NULL, + `admin_email` TEXT NOT NULL, + `admin_install_domain` TINYTEXT NOT NULL, + `admin_index_pagination` SMALLINT UNSIGNED NOT NULL, + `sp_key` TINYTEXT NOT NULL, + `sp_starting` TEXT NOT NULL, + `sp_limit_store` SMALLINT UNSIGNED NOT NULL, + `sp_limit_crawl` SMALLINT UNSIGNED NOT NULL, + `sp_limit_depth` TINYINT UNSIGNED NOT NULL, + `sp_limit_filesize` SMALLINT UNSIGNED NOT NULL, + `sp_sleep` SMALLINT UNSIGNED NOT NULL, + `sp_ignore_ext` TEXT NOT NULL, + `sp_ignore_url` TEXT NOT NULL, + `sp_ignore_css` TEXT NOT NULL, + `sp_require_url` TEXT NOT NULL, + `sp_title_strip` TEXT NOT NULL, + `sp_timeout_url` TINYINT UNSIGNED NOT NULL, + `sp_timeout_crawl` SMALLINT UNSIGNED NOT NULL, + `sp_interval` TINYINT UNSIGNED NOT NULL, + `sp_interval_start` TIME NOT NULL, + `sp_interval_stop` TIME NOT NULL, + `sp_timezone` TINYTEXT NOT NULL, + `sp_time_start` INT UNSIGNED NOT NULL, + `sp_time_end` INT UNSIGNED NOT NULL, + `sp_time_end_success` INT UNSIGNED NOT NULL, + `sp_time_last` SMALLINT UNSIGNED NOT NULL, + `sp_data_transferred` INT UNSIGNED NOT NULL, + `sp_data_stored` INT UNSIGNED NOT NULL, + `sp_links_crawled` SMALLINT UNSIGNED NOT NULL, + `sp_pages_stored` SMALLINT UNSIGNED NOT NULL, + `sp_autodelete` BOOLEAN NOT NULL, + `sp_ifmodifiedsince` BOOLEAN NOT NULL, + `sp_cookies` BOOLEAN NOT NULL, + `sp_sitemap_file` TINYTEXT NOT NULL, + `sp_sitemap_hostname` TINYTEXT NOT NULL, + `sp_useragent` TINYTEXT NOT NULL, + `sp_crawling` BOOLEAN NOT NULL, + `sp_cancel` BOOLEAN NOT NULL, + `sp_progress` TINYTEXT NOT NULL, + `sp_email_success` BOOLEAN NOT NULL, + `sp_email_failure` BOOLEAN NOT NULL, + `sp_log` MEDIUMTEXT NOT NULL, + `s_limit_terms` TINYINT UNSIGNED NOT NULL, + `s_limit_term_length` TINYINT UNSIGNED NOT NULL, + `s_limit_results` TINYINT UNSIGNED NOT NULL, + `s_results_pagination` TINYINT UNSIGNED NOT NULL, + `s_limit_matchtext` SMALLINT UNSIGNED NOT NULL, + `s_limit_cache` SMALLINT UNSIGNED NOT NULL, + `s_weights` TINYTEXT NOT NULL, + `s_weight_css` TEXT NOT NULL, + `s_show_orphans` BOOLEAN NOT NULL, + `s_show_filetype_html` BOOLEAN NOT NULL, + `s_charset` TINYTEXT NOT NULL, + `s_result_template` TEXT NOT NULL, + `s_limit_query_log` TINYINT UNSIGNED NOT NULL, + `jw_hostname` TINYTEXT NOT NULL, + `jw_compression` TINYINT UNSIGNED NOT NULL, + PRIMARY KEY (`version`) + ) ENGINE = MyISAM, COLLATE = utf8_general_ci;' + ); +} + +$testConf = $_DDATA['pdo']->query( + 'SELECT `version` FROM `'.$_DDATA['tbprefix'].'config`;' +); +$err = $testConf->errorInfo(); +if ($err[0] == '00000') { + $testConf = $testConf->fetchAll(); +} else die('Fatal configuration table read error: '.$err[2]); + +// ***** Set default configuration table values +if (!count($testConf)) { + $insert = $_DDATA['pdo']->query( + 'INSERT INTO `'.$_DDATA['tbprefix'].'config` SET + `version`=\'3.0\', + `admin_email`=\'\', + `admin_install_domain`=\'\', + `admin_index_pagination`=100, + `sp_key`=\'\', + `sp_starting`=\'\', + `sp_limit_store`=500, + `sp_limit_crawl`=2500, + `sp_limit_depth`=9, + `sp_limit_filesize`=200, + `sp_sleep`=0, + `sp_ignore_ext`=\'7z au aiff avi bin bz bz2 cab cda cdr class com css csv doc docx dll dtd dwg dxf eps exe gif hqx ico image jar jav java jfif jpeg jpg js kbd mid mkv moov mov movie mp3 mp4 mpeg mpg ocx ogg png pps ppt ps psd qt ra ram rar rm rpm rtf scr sea sit svg swf sys tar.gz tga tgz tif tiff ttf uu uue vob wav woff woff2 xls xlsx z zip\', + `sp_ignore_url`=\'\', + `sp_ignore_css`=\'.noindex footer form head nav noscript select style svg textarea\', + `sp_require_url`=\'\', + `sp_title_strip`=\'\', + `sp_timeout_url`=10, + `sp_timeout_crawl`=300, + `sp_interval`=24, + `sp_interval_start`=\'00:00:00\', + `sp_interval_stop`=\'00:00:00\', + `sp_timezone`=\''.date_default_timezone_get().'\', + `sp_time_start`=0, + `sp_time_end`=0, + `sp_time_end_success`=0, + `sp_time_last`=0, + `sp_data_transferred`=0, + `sp_data_stored`=0, + `sp_links_crawled`=0, + `sp_pages_stored`=0, + `sp_autodelete`=0, + `sp_ifmodifiedsince`=1, + `sp_cookies`=1, + `sp_sitemap_file`=\'\', + `sp_sitemap_hostname`=\''.$_SERVER['HTTP_HOST'].'\', + `sp_useragent`=\'OrcaPHPSearch/3.0 (https://greywyvern.com/orca/#search)\', + `sp_crawling`=0, + `sp_cancel`=0, + `sp_progress`=\'\', + `sp_email_success`=0, + `sp_email_failure`=1, + `sp_log`=\'\', + `s_limit_terms`=7, + `s_limit_term_length`=3, + `s_limit_results`=30, + `s_results_pagination`=10, + `s_limit_matchtext`=256, + `s_limit_cache`=256, + `s_weights`=\'1.3%0.5%2.1%0.4%1.9%0.2%2.5%1.5\', + `s_weight_css`=\'.important dt h1 h2 h3\', + `s_show_orphans`=0, + `s_show_filetype_html`=0, + `s_charset`=\'UTF-8\', + `s_result_template`=\'\', + `s_limit_query_log`=14, + `jw_hostname`=\'\', + `jw_compression`=25 + ;' + ); +} + +// ***** Create the crawldata table if it doesn't exist +if (!in_array($_DDATA['tbprefix'].'crawldata', $_DDATA['tables'])) { + $create = $_DDATA['pdo']->query( + 'CREATE TABLE `'.$_DDATA['tbprefix'].'crawldata` ( + `url` TEXT NOT NULL, + `url_base` TINYTEXT NOT NULL, + `url_sort` SMALLINT UNSIGNED NOT NULL, + `title` TEXT NOT NULL, + `description` TEXT NOT NULL, + `keywords` TEXT NOT NULL, + `category` TINYTEXT NOT NULL, + `weighted` TEXT NOT NULL, + `links` TEXT NOT NULL, + `content` MEDIUMTEXT NOT NULL, + `content_mime` TINYTEXT NOT NULL, + `content_charset` TINYTEXT NOT NULL, + `content_checksum` BINARY(20) NOT NULL, + `status` TINYTEXT NOT NULL, + `status_noindex` TINYTEXT NOT NULL, + `flag_unlisted` BOOLEAN NOT NULL, + `flag_updated` BOOLEAN NOT NULL, + `last_modified` INT NOT NULL, + `priority` DECIMAL(2,1) NOT NULL, + UNIQUE `content_checksum` (`content_checksum`) + ) ENGINE = MyISAM, COLLATE = utf8_general_ci;' + ); +} + +// ***** Create the query log table if it doesn't exist +if (!in_array($_DDATA['tbprefix'].'query', $_DDATA['tables'])) { + $create = $_DDATA['pdo']->query( + 'CREATE TABLE `'.$_DDATA['tbprefix'].'query` ( + `query` TINYTEXT NOT NULL, + `results` TINYINT UNSIGNED NOT NULL, + `stamp` INT UNSIGNED NOT NULL, + `ip` INT UNSIGNED NOT NULL, + `cache` MEDIUMBLOB NOT NULL + ) ENGINE = MyISAM, COLLATE = utf8_general_ci;' + ); +} + + +/** + * Generates a readable filesize string from an integer byte-count + * $abbr => Optional tag with title attribute added + */ +function OS_readSize($bytes, $abbr = false) { + $bytes = (int)$bytes; + if ($bytes >= 1020054733) return round(($bytes / 1073741824), 1).' '.(($abbr) ? 'GiB' : 'GiB'); + if ($bytes >= 996148) return round(($bytes / 1048576), 1).' '.(($abbr) ? 'MiB' : 'MiB'); + if ($bytes >= 973) return round(($bytes / 1024), 1).' '.(($abbr) ? 'kiB' : 'kiB'); + if ($bytes >= 0) return $bytes.' '.(($abbr) ? 'B' : 'B'); + return ''; +} + + +/** + * Set an $_ODATA value by updating it in the config database + * + */ +function OS_setValue($columnName, $value) { + global $_ODATA, $_DDATA; + + if (!isset($_ODATA[$columnName])) return 0; + + $update = $_DDATA['pdo']->prepare( + 'UPDATE `'.$_DDATA['tbprefix'].'config` SET `'.$columnName.'`=:value;' + ); + $update->execute(array('value' => $value)); + + $err = $update->errorInfo(); + if ($err[0] != '00000') { + if (isset($_SESSION['error'])) + $_SESSION['error'][] = 'Could not set value \''.$columnName.'\' in config database.'; + return 0; + + } else if ($update->rowCount()) + $_ODATA[$columnName] = $value; + + return $update->rowCount(); +} + + +/** + * Get a single live $_ODATA value from the database + * + */ +function OS_getValue($columnName) { + global $_ODATA, $_DDATA; + + if (isset($_ODATA[$columnName])) { + $select = $_DDATA['pdo']->query( + 'SELECT `'.$columnName.'` FROM `'.$_DDATA['tbprefix'].'config`;' + ); + + $err = $select->errorInfo(); + if ($err[0] == '00000') { + $select = $select->fetchAll(); + if (count($select)) + $_ODATA[$columnName] = $select[0][$columnName]; + + } else if (isset($_SESSION['error'])) + $_SESSION['error'][] = 'Could not get live value of \''.$columnName.'\' from config database.'; + + } + + return $_ODATA[$columnName]; +} + + +/** + * Initialize a generic cURL connection + * - If creating a cURL connection fails, we should try some fallbacks + * + */ +function OS_getConnection() { + global $_ODATA; + + if (function_exists('curl_init')) { + $_ = curl_init(); + curl_setopt($_, CURLOPT_USERAGENT, $_ODATA['sp_useragent']); + curl_setopt($_, CURLOPT_RETURNTRANSFER, true); + curl_setopt($_, CURLOPT_CONNECTTIMEOUT, $_ODATA['sp_timeout_url']); + curl_setopt($_, CURLOPT_TIMEOUT, $_ODATA['sp_timeout_url']); + curl_setopt($_, CURLOPT_ENCODING, 'gzip'); + curl_setopt($_, CURLOPT_FILETIME, true); + curl_setopt($_, CURLOPT_PROTOCOLS, CURLPROTO_HTTP | CURLPROTO_HTTPS); + + } else { + // Fall back to file_get_contents with stream context? + + // Fall back to socket connection? + + $_ = false; + } + + return $_; +} + + +// ***** Pull the configuration data from the database +$_ODATA = $_DDATA['pdo']->query( + 'SELECT * FROM `'.$_DDATA['tbprefix'].'config`;' +)->fetchAll()[0]; + +error_reporting(E_ALL); +date_default_timezone_set($_ODATA['sp_timezone']); +ini_set('mbstring.substitute_character', 'none'); + + +// ***** Determine the install domain from run location +if (!$_ODATA['admin_install_domain']) { + if (isset($_SERVER['REQUEST_SCHEME']) && $_SERVER['REQUEST_SCHEME'] && + isset($_SERVER['HTTP_HOST']) && $_SERVER['HTTP_HOST']) { + $base = $_SERVER['REQUEST_SCHEME'].'://'.$_SERVER['HTTP_HOST']; + if (isset($_SERVER['SCRIPT_URI']) && $_SERVER['SCRIPT_URI']) { + $psuri = parse_url($_SERVER['SCRIPT_URI']); + if ($psuri && isset($psuri['port']) && !is_null($psuri['port'])) + $base .= ':'.$psuri['port']; + } else if (isset($_SERVER['SERVER_PORT'])) { + if ($_SERVER['SERVER_PORT'] == '80') { + if ($_SERVER['REQUEST_SCHEME'] != 'http') + $base .= ':'.$_SERVER['SERVER_PORT']; + } else if ($_SERVER['SERVER_PORT'] == '443') { + if ($_SERVER['REQUEST_SCHEME'] != 'https') + $base .= ':'.$_SERVER['SERVER_PORT']; + } else $base .= ':'.$_SERVER['SERVER_PORT']; + } + OS_setValue('admin_install_domain', $base); + } +} +if (!$_ODATA['sp_starting']) { + if (!$_ODATA['admin_install_domain']) { + die('Fatal error, could not determine install domain. Please run this script from a web browser.'); + } else OS_setValue('sp_starting', $_ODATA['admin_install_domain'].'/'); +} + + +// ***** Load and Initialize PHPMailer +if (!class_exists('PHPMailer\PHPMailer\PHPMailer')) { + if (file_exists(__DIR__.'/PHPMailer/src/PHPMailer.php')) { + include __DIR__.'/PHPMailer/src/PHPMailer.php'; + include __DIR__.'/PHPMailer/src/Exception.php'; + include __DIR__.'/PHPMailer/src/SMTP.php'; + } +} +if (class_exists('PHPMailer\PHPMailer\PHPMailer')) { + $_MAIL = new PHPMailer\PHPMailer\PHPMailer(); + $_MAIL->From = $_SERVER['SERVER_ADMIN']; + $_MAIL->FromName = "Orca PHP Search Crawler"; + $_MAIL->CharSet = $_ODATA['s_charset']; + if (count($ad = $_MAIL->parseAddresses($_ODATA['admin_email']))) + foreach ($ad as $a) $_MAIL->AddAddress($a['address'], $a['name']); +} else $_MAIL = false; + + +// ***** Load the default Search Result Template +if (!$_ODATA['s_result_template']) { + OS_setValue('s_result_template', << + + + {{#errors}} +
    + {{#error_list}} +
  • {{text}}
  • + {{/error_list}} +
+ {{/errors}} + + {{#searchable}} + {{#searched}} + {{#category}} +
+

+ Searching within category: + {{request_c}} +

+
+ {{/category}} + + {{#results}} +
+ Showing results + {{from}}{{to}} + of {{of}} + {{#in}} + in {{in}} seconds + {{/in}} +
+ +
    + {{#result_list}} +
  1. +
    + {{filetype}} + {{{title_highlight}}} + {{category}} +
    +
    +

    {{{matchtext_highlight}}}

    +
    +
    + {{{url_highlight}}} + ({{relevance}}) +
    +
  2. + {{/result_list}} +
+ + {{#pagination}} + + {{/pagination}} + {{/results}} + {{^results}} +
+

+ Sorry, no results were found. + {{#category}} + Try this search in + all categories? + {{/category}} +

+
+ {{/results}} + {{/searched}} + {{^searched}} +
+

+ Please enter your search terms below. +

+
    +
  • Search terms with fewer than {{limit_term_length}} characters are ignored
  • +
  • Enclose groups of terms in quotes ("") to search for phrases
  • +
  • Prefix terms with a plus-sign (+) to make them important
  • +
  • Prefix terms with a minus-sign (-) or exclamation point (!) to exclude terms
  • +
+
+ {{/searched}} + +
+ + {{#categories}} + + {{/categories}} + +
+ {{/searchable}} + {{^searchable}} +
+

+ There are no searchable pages in the database. + Please try again later. +

+
+ {{/searchable}} + + + +ORCAPHP); +} + +// {{{{{ Initialize the Mustache templating engine +class OS_Mustache { + public $errors; + + public function __construct() {} + + public function addError($text) { + if (!$this->errors) { + $this->errors = new stdClass(); + $this->errors->error_list = array(); + } + $this->errors->error_list[] = $text; + } + + // We'll only autoload the Mustache engine if we need it + public function render() { + global $_ODATA; + + require __DIR__.'/Mustache/Autoloader.php'; + Mustache_Autoloader::register(); + + $output = new Mustache_Engine(array('entity_flags' => ENT_QUOTES)); + echo $output->render($_ODATA['s_result_template'], $this); + } +} + + +// Purge entries from the search query log older than +// 's_limit_query_log' ago +$deleteold = $_DDATA['pdo']->prepare( + 'DELETE FROM `'.$_DDATA['tbprefix'].'query` WHERE `stamp`<:cutoff;' +); +$deleteold->execute(array('cutoff' => time() - $_ODATA['s_limit_query_log'] * 86400)); +$err = $deleteold->errorInfo(); +if ($err[0] != '00000') { + if (isset($_SESSION['error'])) + $_SESSION['error'][] = 'Database error purging old records from the query log.'; +} + +// Reduce search result cache size to within limits +$_RDATA['s_cache_size'] = 0; +$_RDATA['s_cached_searches'] = 0; +$cachesize = $_DDATA['pdo']->query( + 'SELECT COUNT(`cache`) AS `count`, SUM(LENGTH(`cache`)) AS `size` FROM `'.$_DDATA['tbprefix'].'query` WHERE `cache`<>\'\';' +); +$err = $cachesize->errorInfo(); +if ($err[0] == '00000') { + $cachesize = $cachesize->fetchAll(); + + $_RDATA['s_cached_searches'] = $cachesize[0]['count']; + + // If search result cache is over the size limit + if ($cachesize[0]['size'] > $_ODATA['s_limit_cache'] * 1024) { + $select = $_DDATA['pdo']->query( + 'SELECT `query`, `cache` FROM `'.$_DDATA['tbprefix'].'query` WHERE `cache`<>\'\' ORDER BY `stamp` ASC;' + ); + $err = $select->errorInfo(); + if ($err[0] == '00000') { + + // Find out how many cache entries we need to delete, sorted by + // the oldest cached search queries first + $toDel = array(); + $select = $select->fetchAll(); + do { + $first = array_shift($select); + $toDel[$first['query']] = strlen($first['cache']); + } while ($cachesize[0]['size'] - array_sum($toDel) > $_ODATA['s_limit_cache'] * 1024); + + // Delete cache entries with the oldest `cache` values until we + // are below the cache size limit + foreach ($toDel as $del => $size) { + $update = $_DDATA['pdo']->prepare( + 'UPDATE `'.$_DDATA['tbprefix'].'query` SET `cache`=\'\' WHERE `query`=:query;' + ); + $update->execute(array('query' => $del)); + if (!$update->rowCount()) { + if (isset($_SESSION['error'])) + $_SESSION['error'][] = 'Database error while limiting the search result cache size.'; + break; + } else { + $cachesize[0]['size'] -= $size; + $_RDATA['s_cached_searches']--; + } + } + + } else if (isset($_SESSION['error'])) + $_SESSION['error'][] = 'Could not read from search result cache.'; + + } + $_RDATA['s_cache_size'] = $cachesize[0]['size']; + +} else if (isset($_SESSION['error'])) + $_SESSION['error'][] = 'Could not read search result cache size.'; + +// Get a list of all categories in the search database +$_RDATA['s_category_list'] = array('' => 0); +$_RDATA['s_pages_stored'] = 0; +$categories = $_DDATA['pdo']->query( + 'SELECT `category`, COUNT(`category`) AS `count` + FROM `'.$_DDATA['tbprefix'].'crawldata` + GROUP BY `category` ORDER BY `category`;' +); +$err = $categories->errorInfo(); +if ($err[0] == '00000') { + $categories = $categories->fetchAll(); + foreach ($categories as $category) { + $_RDATA['s_category_list'][$category['category']] = $category['count']; + $_RDATA['s_pages_stored'] += $category['count']; + } +} else if (isset($_SESSION['error'])) + $_SESSION['error'][] = 'Could not read categories from the search database.'; + +// Count base URLs / domains from the crawldata: if there is only one +// in the search database then we don't have to show it in a number of +// places +$_RDATA['s_crawldata_domains'] = array(); +$domains = $_DDATA['pdo']->query( + 'SELECT `url_base`, COUNT(`url_base`) as `count` + FROM `'.$_DDATA['tbprefix'].'crawldata` + GROUP BY `url_base` ORDER BY `count` DESC;' +); +$err = $domains->errorInfo(); +if ($err[0] == '00000') { + $domains = $domains->fetchAll(); + foreach ($domains as $domain) + $_RDATA['s_crawldata_domains'][$domain['url_base']] = $domain['count']; +} else $_SESSION['error'][] = 'Could not read domain count data from search database.'; +if (count($_RDATA['s_crawldata_domains']) == 1) + OS_setValue('jw_hostname', key($_RDATA['s_crawldata_domains'])); + +// Count searchable pages +$_RDATA['s_searchable_pages'] = 0; +$query_status = ($_ODATA['s_show_orphans']) ? '(`status`=\'OK\' || `status`=\'Orphan\')' : '`status`=\'OK\''; +$searchable = $_DDATA['pdo']->query( + 'SELECT COUNT(`status`) as `count` + FROM `'.$_DDATA['tbprefix'].'crawldata` + WHERE '.$query_status.' AND `flag_unlisted`=0;' +); +$err = $searchable->errorInfo(); +if ($err[0] == '00000') { + $searchable = $searchable->fetchAll(); + $_RDATA['s_searchable_pages'] = $searchable[0]['count']; +} else $_SESSION['error'][] = 'Could not read status data from search database.'; + + +// Match Weighting Values +$weights = explode('%', $_ODATA['s_weights']); +$_RDATA['s_weights'] = array( + 'title' => $weights[0], + 'body' => $weights[1], + 'keywords' => $weights[2], + 'description' => $weights[3], + 'css_value' => $weights[4], + 'url' => $weights[5], + 'multi' => $weights[6], + 'important' => $weights[7] +); + +$_RDATA['sp_smart'] = array( + '’' => '\'', + '‘' => '\'', + '“' => '"', + '”' => '"', + '‟' => '"', + '„' => '"', + '…' => '...', + '·' => '•', + '›' => '>', + '‖' => '|' +); +$_RDATA['s_latin'] = array( + 'a' => array('á', 'Á', 'à', 'À', 'â', 'Â', 'ä', 'Ä', 'ã', 'Ã', 'å', 'Å', 'ą', 'Ą', 'ă', 'Ă'), + 'ae' => array('æ', 'Æ'), + 'c' => array('ç', 'Ç', 'ć', 'Ć', 'č', 'Č'), + 'd' => array('ð', 'Ð', 'ď', 'Ď', 'đ', 'Đ'), + 'e' => array('é', 'É', 'è', 'È', 'ê', 'Ê', 'ë', 'Ë', 'ę', 'Ę', 'ě', 'Ě'), + 'g' => array('ğ', 'Ğ'), + 'i' => array('í', 'Í', 'ì', 'Ì', 'î', 'Î', 'ï', 'Ï', 'ı', 'İ'), + 'l' => array('ł', 'Ł', 'ľ', 'Ľ', 'ĺ', 'Ĺ'), + 'n' => array('ñ', 'Ñ', 'ń', 'Ń', 'ň', 'Ň'), + 'o' => array('ó', 'Ó', 'ò', 'Ò', 'ô', 'Ô', 'ö', 'Ö', 'õ', 'Õ', 'ø', 'Ø', 'ő', 'Ő'), + 'oe' => array('œ', 'Œ'), + 'r' => array('ŕ', 'Ŕ', 'ř', 'Ř'), + 's' => array('ş', 'Ş', 'ś', 'Ś', 'š', 'Š'), + 'sz' => array('ß'), + 't' => array('ť', 'Ť', 'ţ', 'Ţ'), + 'th' => array('þ', 'Þ'), + 'u' => array('ú', 'Ú', 'ù', 'Ù', 'û', 'Û', 'ü', 'Ü', 'ů', 'Ů', 'ű', 'Ű'), + 'x' => array('×'), + 'y' => array('ý', 'Ý', 'ÿ', 'Ÿ'), + 'z' => array('ź', 'Ź', 'ž', 'Ž', 'ż', 'Ż'), + '?' => array('¿') +); +$_RDATA['s_filetypes'] = array( + 'PDF' => array('application/pdf'), + 'JPG' => array('image/jpeg'), + 'HTML' => array('text/html', 'application/xhtml+xml'), + 'XML' => array('text/xml', 'application/xml'), + 'TXT' => array('text/plain') +); + + +$_SERVER['REQUEST_URI'] = preg_replace('/\?.*$/', '', $_SERVER['REQUEST_URI']); + + +$_RDATA['x_generated_by'] = 'X-Generated-By: Orca PHP Search/'.$_ODATA['version']; +header($_RDATA['x_generated_by']); + + +// ***** Prevent caching of these pages +header('Expires: Mon, 26 Jul 1997 05:00:00 GMT'); +header('Last-Modified: '.gmdate('D, d M Y H:i:s').' GMT'); +header('Cache-Control: no-store, no-cache, must-revalidate'); +header('Cache-Control: post-check=0, pre-check=0', false); +header('Pragma: no-cache'); ?> \ No newline at end of file diff --git a/os3/crawler.php b/os3/crawler.php new file mode 100644 index 0000000..a146558 --- /dev/null +++ b/os3/crawler.php @@ -0,0 +1,1606 @@ + '; + } + + fwrite($_RDATA['sp_log'], $level.$text."\n"); + if ($_DEBUGMODE) echo $level.$text."\n"; +} + + +/** + * Format a full or partial URL into a full URL according to a base URL + * + */ +function OS_formatURL($_, $base) { + $_ = str_replace(' ', '%20', $_); + $_ = preg_replace('/#.*$/', '', filter_var($_, FILTER_SANITIZE_URL)); + $_ = str_replace('%20', ' ', $_); + $dirbase = preg_replace('/(? parse_url($url), + 'body' => curl_exec($_cURL), + 'base' => $url, + 'info' => curl_getinfo($_cURL), + 'error' => curl_error($_cURL), + 'errno' => curl_errno($_cURL), + 'links' => array(), + 'title' => '', + 'content' => '', + 'keywords' => '', + 'weighted' => '', + 'description' => '' + ); + + $_['info']['url'] = $url; + + // Process any cURL errors + switch ($_['errno']) { + case 0: // Success + $_['info']['noindex'] = ''; + $_['info']['nofollow'] = false; + + case 42: // Aborted by callback + if ($_['info']['http_code'] >= 400) { + $_['errno'] = 22; + $_['error'] = $_['info']['http_code'].' error'; + $_['body'] = $url; + $_['info']['noindex'] = '400'; + $_['info']['nofollow'] = true; + + } else if ($_['info']['redirect_url']) { + $_['errno'] = 300; + $_['error'] = 'Redirected by HTTP header to: '.$_['info']['redirect_url']; + $_['links'][] = $_['info']['redirect_url']; + $_['info']['noindex'] = 'redirect-location'; + $_['info']['nofollow'] = true; + + } else if ($_RDATA['sp_robots_header']) { + $_['errno'] = 777; + $_['error'] = 'Blocked by \'X-Robots-Tag\' HTTP header'; + $_['info']['noindex'] = 'robots-http'; + $_['info']['nofollow'] = true; + + } else if ($_RDATA['sp_self_reference']) { + $_['errno'] = 888; + $_['error'] = 'Refused to index myself'; + $_['info']['noindex'] = 'self-reference'; + $_['info']['nofollow'] = true; + + } else if ($_['errno'] == 42) { + $_['errno'] = 999; + $_['error'] = 'Max filesize exceeded'; + $_['body'] = $url; + $_['info']['noindex'] = 'too-large'; + $_['info']['nofollow'] = true; + } + break; + + case 28: // Timeout + $_['error'] = 'Timed out waiting for data'; + $_['body'] = $url; + $_['info']['noindex'] = 'timeout'; + $_['info']['nofollow'] = true; + break; + + case 55: // Network send error + case 56: // Network receive error + $_['error'] = 'Network error retrieving data'; + $_['body'] = $url; + $_['info']['noindex'] = 'network-error'; + $_['info']['nofollow'] = true; + break; + + case 6: // Could not resolve host + case 7: // Could not connect to host + $_['error'] = 'Couldn\'t connect to host: '.$_['url']['host']; + $_['body'] = $url; + $_['info']['noindex'] = 'couldnt-connect'; + $_['info']['nofollow'] = true; + break; + + default: // Uncaught cURL error + OS_crawlLog('Uncaught cURL error:'.$url, 2); + OS_crawlLog($_['errno'], 1); + OS_crawlLog($_['error'], 1); + OS_crawlLog(print_r($_['info'], true), 1); + die('Uncaught cURL error'); + + } + + return $_; +} + + +/** + * Parse the content of a fetched URL for the crawler + * - Modifies the supplied argument array from a OS_fetchURL() call + * - Returns nothing + * + */ +function OS_parseURLContent($_) { + global $_ODATA, $_RDATA, $_PDF; + + if ($_['info']['http_code'] == 304) { + OS_crawlLog('Page hasn\'t been modified since the last successful crawl', 0); + $_['info']['noindex'] = 'not-modified'; + return $_; + } + + + // Detect MIME-type using extension? + if (!isset($_['info']['content_type'])) + $_['info']['content_type'] = 'text/plain'; + + // Parse MIME-type + $_['info']['mime_type'] = ''; + if (preg_match('/\w+\/[\w.+-]+/', $_['info']['content_type'], $m)) + $_['info']['mime_type'] = $m[0]; + + $_['info']['charset'] = ''; + if (preg_match('/charset=([\w\d.:-]+)/i', $_['info']['content_type'], $m)) + $_['info']['charset'] = $m[1]; + if (!$_['info']['charset']) + $_['info']['charset'] = 'ISO-8859-1'; + + + while (strpos($_['body'], "\x1f\x8b") === 0) + $_['body'] = gzinflate(substr($_['body'], 10)); + + $_['info']['sha1'] = sha1($_['body']); + + + switch ($_['info']['mime_type']) { + case 'text/plain': + + $_['content'] = strtr($_['body'], $_RDATA['sp_smart']); + $_['content'] = strtr($_['content'], $_RDATA['sp_utf_replace']); + + if (!trim($_['content'])) { + $_['error'] = 'Server returned no content for'; + $_['info']['noindex'] = 'empty'; + break; + } + + $_['title'] = basename($_['info']['url']); + break; + + + case 'text/xml': + case 'application/xml': + + parseDocXML: + $_['content'] = preg_replace(array('//s', '//is'), '', $_['body']); + $_['content'] = str_replace('><', '> <', $_['content']); + + $_['content'] = html_entity_decode($_['content'], ENT_XML1 | ENT_SUBSTITUTE, $_['info']['charset']); + if (strtoupper($_['info']['charset']) != 'UTF-8') + $_['content'] = mb_convert_encoding($_['content'], 'UTF-8', $_['info']['charset']); + + $_['content'] = strtr($_['content'], $_RDATA['sp_smart']); + $_['content'] = strtr($_['content'], $_RDATA['sp_utf_replace']); + + if (!trim($_['content'])) { + $_['error'] = 'Server returned no content'; + $_['info']['noindex'] = 'empty'; + break; + } + + $document = new DOMDocument(); + if ($document->loadXML($_['content'], LIBXML_PARSEHUGE | LIBXML_BIGLINES | LIBXML_NOBLANKS | LIBXML_NSCLEAN)) { + + // Check XML document charset + if (strtolower($_['info']['charset']) != strtolower($document->xmlEncoding)) { + OS_crawlLog('Charset in Content-type header ('.(($_['info']['charset']) ? $_['info']['charset'] : '').') differs from document charset ('.(($document->xmlEncoding) ? $document->xmlEncoding : '').') at: '.$_['info']['url'], 1); + $_['info']['charset'] = $document->xmlEncoding; + goto parseDocXML; + } + + $_['content'] = $document->textContent; + + } else { + $_['error'] = 'Invalid XML; could not parse content'; + $_['info']['nofollow'] = true; + + $_['content'] = strip_tags($_['content']); + } + + $_['title'] = basename($_['info']['url']); + break; + + + case 'text/html': + case 'application/xhtml+xml': + + parseDocHTML: + $_['content'] = preg_replace(array('//s', '//is'), '', $_['body']); + $_['content'] = str_replace('><', '> <', $_['content']); + $_['content'] = preg_replace('//', "\n", $_['content']); + + $_['content'] = html_entity_decode($_['content'], ENT_HTML5 | ENT_SUBSTITUTE, $_['info']['charset']); + if (strtoupper($_['info']['charset']) != 'UTF-8') + $_['content'] = mb_convert_encoding($_['content'], 'UTF-8', $_['info']['charset']); + + $_['content'] = strtr($_['content'], $_RDATA['sp_smart']); + $_['content'] = strtr($_['content'], $_RDATA['sp_utf_replace']); + + if (!trim($_['content'])) { + $_['error'] = 'Server returned no content'; + $_['info']['noindex'] = 'empty'; + break; + } + + $document = new DOMDocument(); + if ($document->loadHTML($_['content'], LIBXML_PARSEHUGE | LIBXML_BIGLINES | LIBXML_NOBLANKS | LIBXML_NSCLEAN)) { + + // ***** Process elements + $head = $document->getElementsByTagName('head'); + + $base = $head[0]->getElementsByTagName('base'); + if (isset($base[0])) + for ($x = 0; $x < count($base[0]->attributes); $x++) + if (strtolower($base[0]->attributes[$x]->name) == 'href') + $_['base'] = filter_var($base[0]->attributes[$x]->value, FILTER_SANITIZE_URL); + + $metas = $head[0]->getElementsByTagName('meta'); + foreach ($metas as $meta) { + for ($x = 0; $x < count($meta->attributes); $x++) { + if (strtolower($meta->attributes[$x]->name) == 'charset') { + if (strtolower($_['info']['charset']) != strtolower($meta->attributes[$x]->value)) { + OS_crawlLog('Charset in Content-type header ('.(($_['info']['charset']) ? $_['info']['charset'] : '').') differs from document charset ('.(($meta->attributes[$x]->value) ? $meta->attributes[$x]->value : '').') at: '.$_['info']['url'], 1); + $_['info']['charset'] = $meta->attributes[$x]->value; + goto parseDocHTML; + } + + } else if (strtolower($meta->attributes[$x]->name) == 'http-equiv') { + switch (strtolower($meta->attributes[$x]->value)) { + case 'refresh': + for ($y = 0; $y < count($meta->attributes); $y++) { + if (strtolower($meta->attributes[$y]->name) == 'content') { + if (preg_match('/(\d+)\s?;\s?url\s?=\s?([\'"])(.+?)\2?\s?$/i', $meta->attributes[$y]->value, $m)) { + $_['links'][] = $m[3]; + if ((int)$m[1] <= $_ODATA['sp_timeout_url']) { + $_['errno'] = 300; + $_['error'] = $_['info']['url'].' redirected by element to: '.$m[3]; + $_['info']['noindex'] = 'redirect-meta'; + $_['info']['nofollow'] = true; + break 4; + } + } + } + } + break; + + case 'content-type': + for ($y = 0; $y < count($meta->attributes); $y++) { + if (strtolower($meta->attributes[$y]->name) == 'content' && preg_match('/charset=([\w\d.:-]+)/i', $meta->attributes[$y]->value, $m)) { + if (strtolower($_['info']['charset']) != strtolower($m[1])) { + OS_crawlLog('Charset in Content-type header ('.(($_['info']['charset']) ? $_['info']['charset'] : '').') differs from document charset ('.(($m[1]) ? $m[1] : '').') at: '.$_['info']['url'], 1); + $_['info']['charset'] = $m[1]; + goto parseDocHTML; + } + } + } + + } + + } else if (strtolower($meta->attributes[$x]->name) == 'name') { + switch (strtolower($meta->attributes[$x]->value)) { + case 'keywords': + for ($y = 0; $y < count($meta->attributes); $y++) + if (strtolower($meta->attributes[$y]->name) == 'content') + $_['keywords'] = $meta->attributes[$y]->value; + break; + + case 'description': + for ($y = 0; $y < count($meta->attributes); $y++) + if (strtolower($meta->attributes[$y]->name) == 'content') + $_['description'] = $meta->attributes[$y]->value; + break; + + case 'robots': + case 'orcacrawler': + case 'orcaphpcrawler': + case 'orca-crawler': + case 'orcaphp-crawler': + case 'orca-phpcrawler': + case 'orca-php-crawler': + for ($y = 0; $y < count($meta->attributes); $y++) { + if (strtolower($meta->attributes[$y]->name) == 'content') { + $content = explode(',', $meta->attributes[$y]->value); + foreach ($content as $con) { + switch (trim(strtolower($con))) { + case 'nofollow': + $_['info']['nofollow'] = true; + break; + + case 'noindex': + $_['error'] = 'Not indexed due to robots element'; + $_['info']['noindex'] = 'robots-meta'; + + } + } + } + } + + } + } + } + } + + $title = $head[0]->getElementsByTagName('title'); + $_['title'] = $title[0]->textContent; + + $links = $head[0]->getElementsByTagName('link'); + foreach ($links as $link) { + for ($x = 0; $x < count($link->attributes); $x++) { + if (strtolower($link->attributes[$x]->name) == 'rel') { + for ($y = 0; $y < count($link->attributes); $y++) { + if (strtolower($link->attributes[$y]->name) == 'href') { + $linkurl = filter_var($link->attributes[$y]->value, FILTER_SANITIZE_URL); + + switch (strtolower($link->attributes[$x]->value)) { + case 'canonical': + if (OS_formatURL($linkurl, $_['base']) != $_['info']['url']) { + $_['info']['noindex'] = 'non-canonical'; + $_['info']['canonical'] = $linkurl; + } + + case 'alternate': + case 'author': + case 'help': + case 'license': + case 'me': + case 'next': + case 'prev': + case 'search': + case 'alternate': + $_['links'][] = $linkurl; + + } + break; + } + } + } + } + } + + + // ***** Process elements + $body = $document->getElementsByTagName('body'); + + // Replace tags with their alt text + $imgs = $body[0]->getElementsByTagName('img'); + foreach ($imgs as $img) { + for ($x = 0; $x < count($img->attributes); $x++) { + if (strtolower($img->attributes[$x]->name) == 'alt') { + $img->parentNode->replaceChild( + $document->createTextNode(' '.$img->attributes[$x]->value.' '), + $img + ); + break; + } + } + } + + $as = $body[0]->getElementsByTagName('a'); + foreach ($as as $a) { + for ($x = 0; $x < count($a->attributes); $x++) { + if (strtolower($a->attributes[$x]->name) == 'href') { + for ($y = 0; $y < count($a->attributes); $y++) + if (strtolower($a->attributes[$y]->name) == 'rel' && strtolower($a->attributes[$y]->value) == 'nofollow') continue 3; + $_['links'][] = $a->attributes[$x]->value; + } + } + } + + $areas = $body[0]->getElementsByTagName('area'); + foreach ($areas as $area) { + for ($x = 0; $x < count($area->attributes); $x++) { + if (strtolower($area->attributes[$x]->name) == 'href') { + for ($y = 0; $y < count($area->attributes); $y++) + if (strtolower($area->attributes[$y]->name) == 'rel' && strtolower($area->attributes[$y]->value) == 'nofollow') continue 3; + $_['links'][] = $area->attributes[$x]->value; + } + } + } + + $frames = $body[0]->getElementsByTagName('frame'); + foreach ($frames as $frame) + for ($x = 0; $x < count($frame->attributes); $x++) + if (strtolower($frame->attributes[$x]->name) == 'src') + $_['links'][] = $frame->attributes[$x]->value; + + $iframes = $body[0]->getElementsByTagName('iframe'); + foreach ($iframes as $iframe) + for ($x = 0; $x < count($iframe->attributes); $x++) + if (strtolower($iframe->attributes[$x]->name) == 'src') + $_['links'][] = $iframe->attributes[$x]->value; + + $_['links'] = array_map(function($l) { + if (preg_match('/^(tel|telnet|mailto|ftp|sftp|ssh|gopher|news|ldap|urn|onion|magnet):/i', $l)) return ''; + return preg_replace('/#.*$/', '', $l); + }, $_['links']); + $_['links'] = array_filter(array_unique($_['links'])); + + // Remove tags + foreach ($_RDATA['sp_ignore_css'] as $ignoreCSS) { + switch ($ignoreCSS[0]) { + case '#': // Remove by ID + $id = $document->getElementById(substr($ignoreCSS, 1)); + if (!is_null($id)) $id->parentNode->removeChild($id); + break; + + case '.': // Remove by class + $xpath = new DOMXpath($document); + foreach ($xpath->evaluate('//*[contains(concat(" ", normalize-space(@class), " "), " '.substr($ignoreCSS, 1).' ")]') as $cls) + $cls->parentNode->removeChild($cls); + break; + + default: // Remove by tag name + $tags = $document->getElementsByTagName($ignoreCSS); + foreach ($tags as $tag) + $tag->parentNode->removeChild($tag); + + } + } + + // Weighted elements + foreach ($_RDATA['s_weight_css'] as $weightCSS) { + switch ($weightCSS[0]) { + case '#': // Get content by ID + $id = $document->getElementById(substr($weightCSS, 1)); + if (!is_null($id)) $_['weighted'] .= $id->textContent.' '; + break; + + case '.': // Get content by class + $xpath = new DOMXpath($document); + foreach ($xpath->evaluate('//*[contains(concat(" ", normalize-space(@class), " "), " '.substr($weightCSS, 1).' ")]') as $cls) + $_['weighted'] .= $cls->textContent.' '; + break; + + default: // Get content by tag name + $tags = $document->getElementsByTagName($weightCSS); + foreach ($tags as $tag) + $_['weighted'] .= $tag->textContent.' '; + + } + } + + $_['content'] = $document->textContent; + + } else { + $_['error'] = 'Invalid HTML; could not parse content'; + $_['info']['nofollow'] = true; + + $_['content'] = strip_tags($_['content']); + } + break; + + + case 'application/pdf': + $_['info']['charset'] = 'ISO-8859-1'; + + if ($_PDF) { + try { + $pdf = $_PDF->parseContent($_['body']); + + $_['content'] = $pdf->getText(); + + $_['title'] = basename($_['info']['url']); + + $meta = $pdf->getDetails(); + if (isset($meta['Title'])) $_['title'] = strtr($meta['Title'], $_RDATA['sp_utf_replace']); + if (isset($meta['Subject'])) $_['description'] = strtr($meta['Subject'], $_RDATA['sp_utf_replace']); + if (isset($meta['Keywords'])) $_['keywords'] = strtr($meta['Keywords'], $_RDATA['sp_utf_replace']); + + // remove escaped whitespace + $_['title'] = str_replace(array("\\\n\r", "\\\n"), '', $_['title']); + $_['description'] = str_replace(array("\\\n\r", "\\\n"), '', $_['description']); + $_['keywords'] = str_replace(array("\\\n\r", "\\\n"), '', $_['keywords']); + $_['content'] = str_replace(array("\\\n\r", "\\\n"), '', $_['content']); + + $_['info']['charset'] = mb_detect_encoding($_['content']); + + if (!$_['info']['charset']) $_['info']['charset'] = 'ISO-8859-1'; + if (strtoupper($_['info']['charset']) != 'UTF-8') { + $_['title'] = mb_convert_encoding($_['title'], 'UTF-8', $_['info']['charset']); + $_['description'] = mb_convert_encoding($_['description'], 'UTF-8', $_['info']['charset']); + $_['keywords'] = mb_convert_encoding($_['keywords'], 'UTF-8', $_['info']['charset']); + $_['content'] = mb_convert_encoding($_['content'], 'UTF-8', $_['info']['charset']); + } + + // Discard the PDF text if it contains Unicode control + // characters; some of these might be simple PDF ligatures + // but PdfParser doesn't support them + if (strpos($_['content'], "\u{3}") === false && + strpos($_['content'], "\u{2}") === false && + strpos($_['content'], "\u{1}") === false) { + + $_['content'] = strtr($_['content'], $_RDATA['sp_smart']); + $_['content'] = strtr($_['content'], $_RDATA['sp_utf_replace']); + + } else { + $_['errno'] = 702; + $_['error'] = 'Failed to decode PDF text'; + $_['content'] = ''; + $_['info']['noindex'] = 'couldnt-decode-pdf'; + } + + } catch (Exception $e) { + $_['errno'] = 701; + $_['error'] = 'PDF is secured/encrypted; text extraction failed'; + $_['content'] = ''; + $_['info']['noindex'] = 'secured-pdf'; + } + + } else $_['info']['noindex'] = 'missing-pdfparser'; + break; + + + // Unknown MIME-type + default: + $_['info']['charset'] = 'ISO-8859-1'; + + $_['error'] = 'Not indexed due to unknown MIME type ('.$_['info']['mime_type'].')'; + $_['info']['noindex'] = 'unknown-mime'; + $_['info']['nofollow'] = true; + + } + + // White-space normalize + $_['title'] = preg_replace(array('/\s/', '/ {2,}/'), ' ', trim($_['title'])); + $_['description'] = preg_replace(array('/\s/', '/ {2,}/'), ' ', trim($_['description'])); + $_['keywords'] = preg_replace(array('/\s/', '/ {2,}/'), ' ', trim($_['keywords'])); + $_['weighted'] = preg_replace(array('/\s/', '/ {2,}/'), ' ', trim($_['weighted'])); + $_['content'] = preg_replace(array('/\s/', '/ {2,}/'), ' ', trim($_['content'])); + + return $_; +} + + +/** + * Shutdown function to provide cleanup before exit + * + */ +function OS_crawlCleanUp() { + global $_DDATA, $_ODATA, $_RDATA, $_cURL, $_MAIL, $_DEBUGMODE; + + // If the crawl has already been canceled, don't bother + if (!$_ODATA['sp_crawling']) return; + + $error = error_get_last(); + if (!is_null($error) && $error['type'] == E_ERROR) { + OS_crawlLog($error['message'], 2); + OS_crawlLog('File: \''.$error['file'].'\' at line number: '.$error['line'], 0); + $_RDATA['sp_complete'] = false; + } + + // Save or display cookies? + $cookies = curl_getinfo($_cURL, CURLINFO_COOKIELIST); + // var_dump($cookies); + curl_close($_cURL); + + OS_setValue('sp_time_end', time()); + OS_setValue('sp_time_last', $_ODATA['sp_time_end'] - $_ODATA['sp_time_start']); + OS_setValue('sp_data_transferred', $_RDATA['sp_data_transferred']); + + if ($_RDATA['sp_complete']) { + OS_crawlLog('Cleaning up database tables...', 1); + + natcasesort($_RDATA['sp_store']); + $_RDATA['sp_store'] = array_values($_RDATA['sp_store']); + $url_sort = $_DDATA['pdo']->prepare( + 'UPDATE `'.$_DDATA['tbprefix'].'crawltemp` SET `url_sort`=:url_sort WHERE `url`=:url;' + ); + foreach ($_RDATA['sp_store'] as $key => $stored_url) { + $url_sort->execute(array( + 'url_sort' => $key, + 'url' => $stored_url + )); + $err = $url_sort->errorInfo(); + if ($err[0] != '00000') { + OS_crawlLog('Error sorting the search database', 1); + OS_crawlLog($err[2], 0); + break; + } + } + + $truncate = $_DDATA['pdo']->query( + 'TRUNCATE `'.$_DDATA['tbprefix'].'crawldata`;' + ); + $err = $truncate->errorInfo(); + if ($err[0] != '00000') { + OS_crawlLog('Could not truncate the search database', 1); + OS_crawlLog($err[2], 0); + $_RDATA['sp_complete'] = false; + } + } + + // If crawl completed successfully + if ($_RDATA['sp_complete']) { + + // Select all rows from the temp table into the existing search table + $insert = $_DDATA['pdo']->query( + 'INSERT INTO `'.$_DDATA['tbprefix'].'crawldata` + SELECT * FROM `'.$_DDATA['tbprefix'].'crawltemp` + ;' + ); + + $err = $insert->errorInfo(); + if ($err[0] == '00000') { + $tableinfo = $_DDATA['pdo']->query( + 'SHOW TABLE STATUS LIKE \''.$_DDATA['tbprefix'].'crawldata\';' + ); + $err = $tableinfo->errorInfo(); + if ($err[0] == '00000') { + $tableinfo = $tableinfo->fetchAll(); + OS_setValue('sp_data_stored', $tableinfo[0]['Data_length']); + } else OS_crawlLog('Could not read crawl table status', 1); + + // Purge the search result cache + if ($_ODATA['s_limit_cache']) { + $purge = $_DDATA['pdo']->query( + 'UPDATE `'.$_DDATA['tbprefix'].'query` SET `cache`=\'\';' + ); + $err = $purge->errorInfo(); + if ($err[0] != '00000') + OS_crawlLog('Could not purge search result cache', 1); + } + + OS_setValue('sp_links_crawled', count($_RDATA['sp_links'])); + OS_setValue('sp_pages_stored', count($_RDATA['sp_store'])); + OS_setValue('sp_time_end_success', $_ODATA['sp_time_end']); + + OS_crawlLog('***** Crawl completed in '.$_ODATA['sp_time_last'].'s *****', 1); + OS_crawlLog('Total data transferred: '.OS_readSize($_RDATA['sp_data_transferred']), 1); + OS_crawlLog('Average transfer speed: '.OS_readSize(round($_RDATA['sp_data_transferred'] / $_ODATA['sp_time_last'])).'/s', 1); + if ($_RDATA['sp_sleep']) + OS_crawlLog('Time spent sleeping: '.(round($_RDATA['sp_sleep'] / 10) / 100).'s', 1); + OS_crawlLog('Time taken by cURL: '.(round($_RDATA['sp_time_curl'] * 100) / 100).'s', 1); + OS_crawlLog($_ODATA['sp_links_crawled'].' page'.(($_ODATA['sp_links_crawled'] == 1) ? '' : 's').' crawled', 1); + OS_crawlLog($_ODATA['sp_pages_stored'].' page'.(($_ODATA['sp_pages_stored'] == 1) ? '' : 's').' stored', 1); + + if ($_RDATA['sp_status']['New']) + OS_crawlLog($_RDATA['sp_status']['New'].' new '.(($_RDATA['sp_status']['New'] == 1) ? 'page' : 'pages').' found', 0); + if ($_RDATA['sp_status']['Updated']) + OS_crawlLog($_RDATA['sp_status']['Updated'].' '.(($_RDATA['sp_status']['Updated'] == 1) ? 'page' : 'pages').' updated', 0); + if ($_RDATA['sp_status']['Blocked']) + OS_crawlLog($_RDATA['sp_status']['Blocked'].' '.(($_RDATA['sp_status']['Blocked'] == 1) ? 'page' : 'pages').' blocked', 0); + if ($_RDATA['sp_status']['Not Found']) + OS_crawlLog($_RDATA['sp_status']['Not Found'].' '.(($_RDATA['sp_status']['Not Found'] == 1) ? 'page' : 'pages').' not found', 0); + if ($_RDATA['sp_status']['Orphan']) + OS_crawlLog($_RDATA['sp_status']['Orphan'].' orphaned '.(($_RDATA['sp_status']['Orphan'] == 1) ? 'page' : 'pages'), 0); + + if ($_ODATA['sp_autodelete']) + OS_crawlLog('Orphaned pages were auto-deleted', 1); + + // Send success email to the admin(s) + if ($_MAIL && count($_MAIL->getAllRecipientAddresses()) && $_ODATA['sp_email_success']) { + $_MAIL->Subject = 'Orca PHP Search Crawler: Crawl succeeded'; + $_MAIL->Body = implode(" \r\n", preg_grep('/^[\[\*]/', explode("\n", file_get_contents($_ODATA['sp_log'])))); + if (!$_MAIL->Send()) OS_crawlLog('Could not send notification email', 2); + } + + $cronMessage = 'Crawl completed successfully'; + $jsonMessage = json_encode(array( + 'status' => 'Success', + 'message' => $cronMessage + ), JSON_INVALID_UTF8_IGNORE); + + // We truncated the search table but FAILED to populate it! + // This is a serious error that disables searching until the + // crawler is run again! + } else { + OS_crawlLog('Could not populate the search table', 2); + OS_crawlLog($err[2], 0); + + OS_crawlLog('***** Crawl failed; runtime '.$_ODATA['sp_time_last'].'s *****', 1); + OS_crawlLog('Search table was cleared, but could not be repopulated!', 1); + OS_crawlLog('The crawler MUST be run again to fix this issue!', 1); + + // Send failure email to the admin(s) + if ($_MAIL && count($_MAIL->getAllRecipientAddresses()) && $_ODATA['sp_email_failure']) { + $_MAIL->Subject = 'Orca PHP Search Crawler: Catastrophic failure!'; + $_MAIL->Body = implode(" \r\n", preg_grep('/^[\[\*\w\d]/', explode("\n", file_get_contents($_ODATA['sp_log'])))); + if (!$_MAIL->Send()) OS_crawlLog('Could not send notification email', 2); + } + + $cronMessage = 'Could not populate search table; search table is currently empty!'; + $jsonMessage = json_encode(array( + 'status' => 'Error', + 'message' => $cronMessage + ), JSON_INVALID_UTF8_IGNORE); + } + + // ... else if the crawl failed + } else { + OS_crawlLog('***** Crawl failed; runtime '.$_ODATA['sp_time_last'].'s *****', 1); + OS_crawlLog('Search table was NOT updated', 1); + + if ($_ODATA['sp_sitemap_file']) + OS_crawlLog('Sitemap was NOT updated', 1); + + // Send failure email to the admin(s) + if ($_MAIL && count($_MAIL->getAllRecipientAddresses()) && $_ODATA['sp_email_failure'] && !$_ODATA['sp_cancel']) { + $_MAIL->Subject = 'Orca PHP Search Crawler: Crawl failed'; + $_MAIL->Body = implode(" \r\n", preg_grep('/^[\[\*\w\d]/', explode("\n", file_get_contents($_ODATA['sp_log'])))); + if (!$_MAIL->Send()) OS_crawlLog('Could not send notification email', 2); + } + + $cronMessage = 'Crawl failed; see the log for details'; + $jsonMessage = json_encode(array( + 'status' => 'Error', + 'message' => $cronMessage + ), JSON_INVALID_UTF8_IGNORE); + } + + // Delete the temp search table + $drop = $_DDATA['pdo']->query( + 'DROP TABLE IF EXISTS `'.$_DDATA['tbprefix'].'crawltemp`;' + ); + $err = $drop->errorInfo(); + if ($err[0] != '00000') { + OS_crawlLog('Could not delete the temporary search table', 1); + OS_crawlLog($err[2], 0); + } + + // Store the log file to the config database + OS_setValue('sp_log', file_get_contents($_ODATA['sp_log'])); + fclose($_RDATA['sp_log']); + + + OS_setValue('sp_crawling', 0); + + if ($_SERVER['REQUEST_METHOD'] != 'CRON') { + if (!$_DEBUGMODE) + header('Content-type: application/json; charset='.strtolower($_ODATA['s_charset'])); + die($jsonMessage); + } else die($cronMessage); +} + + + + +// This is most likely a crontab request +if (!isset($_SERVER['REQUEST_METHOD'])) { + $_SERVER['REQUEST_METHOD'] = 'CRON'; + chdir(dirname($_SERVER['argv'][0])); + +} else { + + /* ***** Handle POST Requests ************************************** */ + if ($_SERVER['REQUEST_METHOD'] == 'POST') { + + // JSON POST request + // These are usually sent by javascript fetch() + if (strpos(trim($_SERVER['CONTENT_TYPE']), 'application/json') === 0) { + $postBody = file_get_contents('php://input'); + $_POST = json_decode($postBody, false); + + $response = array(); + + if (!isset($_POST->action)) $_POST->action = ''; + switch ($_POST->action) { + case 'crawl': + if (isset($_POST->sp_key) && + $_ODATA['sp_key'] && + $_POST->sp_key == $_ODATA['sp_key']) { + if ($_ODATA['sp_crawling']) { + $response = array( + 'status' => 'Error', + 'message' => 'Crawler is already running; current progress: '.$_ODATA['sp_progress'] + ); + } + + // Go crawl! + OS_setValue('sp_key', ''); + + } else { + $response = array( + 'status' => 'Error', + 'message' => 'Incorrect key to initiate crawler' + ); + } + break; + + case 'progress': + $lines = array(); + if ($_ODATA['sp_crawling']) { + if (strpos($_ODATA['sp_log'], "\n") === false && file_exists($_ODATA['sp_log'])) + $lines = file($_ODATA['sp_log'], FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES); + } else $lines = explode("\n", $_ODATA['sp_log']); + + if (!isset($_POST->grep)) $_POST->grep = 'all'; + switch ($_POST->grep) { + case 'all': break; + case 'errors': $lines = preg_grep('/^[\[\*]/', $lines); break; + default: $lines = preg_grep('/^[\[\*\w\d]/', $lines); + } + + if ($_ODATA['sp_crawling']) $lines = array_slice($lines, -15); + + $response = array( + 'status' => ($_ODATA['sp_crawling']) ? 'Crawling' : 'Complete', + 'progress' => $_ODATA['sp_progress'], + 'time_crawl' => time() - $_ODATA['sp_time_start'], + 'timeout_crawl' => $_ODATA['sp_timeout_crawl'], + 'tail' => trim(implode("\n", $lines)) + ); + break; + + case 'cancel': + if ($_ODATA['sp_crawling']) { + + // IF the crawler 'time_start' is more than 'timeout_crawl' + // seconds ago, or the 'force' token is set, the crawler is + // probably stuck. Unstick it. + if (!isset($_POST->force)) $_POST->force = ''; + if ($_POST->force || time() - $_ODATA['sp_time_start'] > $_ODATA['sp_timeout_crawl']) { + OS_setValue('sp_crawling', 0); + + if (!isset($_POST->reason) || !$_POST->reason) + $_POST->reason = 'The crawler halted unexpectedly'; + + if (strpos($_ODATA['sp_log'], "\n") === false && file_exists($_ODATA['sp_log'])) { + $log = file_get_contents($_ODATA['sp_log']); + OS_setValue('sp_log', $log."\n".'[ERROR] '.$_POST->reason); + } else OS_setValue('sp_log', '[ERROR] '.$_POST->reason); + OS_setValue('sp_time_end', time()); + OS_setValue('sp_time_last', time() - $_ODATA['sp_time_start']); + OS_setValue('sp_data_transferred', 0); + OS_setValue('sp_data_stored', 0); + + // Send failure email to the admin(s) + if ($_MAIL && count($_MAIL->getAllRecipientAddresses()) && $_ODATA['sp_email_failure']) { + $_MAIL->Subject = 'Orca PHP Search Crawler: Crawler halted unexpectedly'; + $_MAIL->Body = implode(" \r\n", preg_grep('/^[\[\*\w\d]/', explode("\n", $_ODATA['sp_log']))); + if (!$_MAIL->Send()) OS_setValue('sp_log', $_ODATA['sp_log']."\n".'[ERROR] Could not send notification email'); + } + } + + OS_setValue('sp_cancel', 1); + $response = array( + 'status' => 'Success', + 'message' => 'Cancel flag was set', + 'crawl_time' => time() - $_ODATA['sp_time_start'] + ); + + } else { + $response = array( + 'status' => 'Error', + 'message' => 'Crawler is not currently running' + ); + } + break; + + default: + $response = array( + 'status' => 'Error', + 'message' => 'Unrecognized command' + ); + + } + + if ($response) { + header('Content-type: application/json; charset='.strtolower($_ODATA['s_charset'])); + die(json_encode($response, JSON_INVALID_UTF8_IGNORE)); + } + + // Don't do anything for normal POST request + } else { + header('Content-type: text/plain; charset='.strtolower($_ODATA['s_charset'])); + die($_ODATA['sp_useragent']); + } + + // Don't do anything for GET requests + } else if ($_SERVER['REQUEST_METHOD'] == 'GET') { + header('Content-type: text/plain; charset='.strtolower($_ODATA['s_charset'])); + if (!$_DEBUGMODE) die($_ODATA['sp_useragent']); + } +} + + +/* ***** Begin Crawl Execution ************************************* */ +register_shutdown_function('OS_crawlCleanUp'); +ignore_user_abort(true); +@set_time_limit($_ODATA['sp_timeout_crawl'] * 1.1); +libxml_use_internal_errors(true); +if (function_exists('apache_setenv')) + apache_setenv('no-gzip', '1'); + +OS_setValue('sp_crawling', 1); +OS_setValue('sp_cancel', 0); +OS_setValue('sp_time_start', time()); +OS_setValue('sp_links_crawled', 0); +OS_setValue('sp_pages_stored', 0); +OS_setValue('sp_data_stored', 0); +OS_setValue('sp_data_transferred', 0); +OS_setValue('sp_time_last', 0); + + +$_RDATA['sp_log'] = tmpfile(); +OS_setValue('sp_log', stream_get_meta_data($_RDATA['sp_log'])['uri']); +OS_crawlLog('***** Crawl started: '.date('r').' *****', 1); + + +// ***** Prepare runtime data +$_RDATA['sp_starting'] = array_filter(array_map('trim', explode("\n", $_ODATA['sp_starting']))); +$_RDATA['sp_hostnames'] = array(); +$_RDATA['sp_ignore_url'] = array_filter(array_map('trim', explode("\n", $_ODATA['sp_ignore_url']))); +$_RDATA['sp_ignore_css'] = array_filter(explode(' ', $_ODATA['sp_ignore_css'])); +$_RDATA['s_weight_css'] = array_filter(explode(' ', $_ODATA['s_weight_css'])); +$_RDATA['sp_require_url'] = array_filter(array_map('trim', explode("\n", $_ODATA['sp_require_url']))); +$_RDATA['sp_ignore_ext_regexp'] = implode('|', array_map('preg_quote', array_filter(explode(' ', $_ODATA['sp_ignore_ext'])))); +$_RDATA['sp_robots_header'] = 0; +$_RDATA['sp_complete'] = false; +$_RDATA['sp_links'] = array(); +$_RDATA['sp_store'] = array(); +$_RDATA['sp_sitemap'] = array(); +$_RDATA['sp_robots'] = array(); +$_RDATA['sp_status'] = array('Orphan' => 0, 'Blocked' => 0, 'Not Found' => 0, 'Updated' => 0, 'New' => 0); +$_RDATA['sp_filter'] = array(); +$_RDATA['sp_prev_dls'] = 0; +$_RDATA['sp_data_transferred'] = 0; +$_RDATA['sp_time_curl'] = 0; +$_RDATA['sp_sleep'] = 0; +$_RDATA['sp_sha1'] = array(); +$_RDATA['sp_entity'] = array( + "\n" => array(10, 11, 12, 13, 133, 8232, 8233), + ' ' => array(9, 160, 5760, 8192, 8193, 8194, 8195, 8196, 8197, 8198, 8199, 8200, 8201, 8202, 8204, 8239, 8287, 12288), + '' => array(173, 8205, 8288, 65279), + '-' => array(1418, 1470, 5120, 6150, 8208, 8209, 8210, 8211, 8212, 8213, 11799, 11802, 11834, 11835, 11840, 12316, 12336, 12448, 65073, 65074, 65112, 65123, 65293, 69293) +); +$_RDATA['sp_utf_replace'] = array(); +foreach ($_RDATA['sp_entity'] as $key => $value) + foreach ($value as $code) + $_RDATA['sp_utf_replace'][mb_chr($code)] = $key; + + + +// ***** Load PDF parser +if (!class_exists('\Smalot\PdfParser\Parser')) + if (file_exists(__DIR__.'/PdfParser/alt_autoload.php-dist')) + include __DIR__.'/PdfParser/alt_autoload.php-dist'; +if (class_exists('\Smalot\PdfParser\Parser')) { + $config = new \Smalot\PdfParser\Config(); + $config->setRetainImageContent(false); + $config->setDecodeMemoryLimit(16777216); + $_PDF = new \Smalot\PdfParser\Parser([], $config); +} else { + OS_crawlLog('Could not include \'PdfParser\'; PDFs will not be indexed', 1); + $_PDF = false; +} + + +// ***** Check for PHPMailer +if (!$_MAIL) { + OS_crawlLog('Could not include \'PHPMailer\'; Crawler cannot send mail', 1); +} else if (!count($_MAIL->getAllRecipientAddresses())) + OS_crawlLog('No admin emails specified; Crawler will not send mail', 1); + + +// ***** Initialize the cURL connection +$_cURL = OS_getConnection(); +if ($_cURL) { + + // Customize this cURL connection + if ($_ODATA['sp_cookies']) + curl_setopt($_cURL, CURLOPT_COOKIEFILE, ''); + if ($_ODATA['sp_time_end_success']) + curl_setopt($_cURL, CURLOPT_TIMEVALUE, $_ODATA['sp_time_end_success']); + curl_setopt($_cURL, CURLOPT_HEADERFUNCTION, function($_cURL, $line) { + global $_RDATA; + + if (preg_match('/^X-Robots-Tag:\s*(noindex|none)/i', $line)) + $_RDATA['sp_robots_header'] = 1; + + if (trim($line) == $_RDATA['x_generated_by']) + $_RDATA['sp_self_reference'] = 1; + + return strlen($line); + }); + curl_setopt($_cURL, CURLOPT_NOPROGRESS, false); + curl_setopt($_cURL, CURLOPT_PROGRESSFUNCTION, + function($_cURL, $dls, $dl, $uls, $ul) { + global $_ODATA, $_RDATA; + + if ($_RDATA['sp_robots_header']) return 1; + if ($_RDATA['sp_self_reference']) return 1; + + // Prevent comparing this value until a Content-length header has + // been received by the cURL connection + if ($dls != $_RDATA['sp_prev_dls']) { + $_RDATA['sp_prev_dls'] = $dls; + if ($dls > $_ODATA['sp_limit_filesize'] * 1024) return 1; + } + if ($dl > $_ODATA['sp_limit_filesize'] * 1024) return 1; + + $i = curl_getinfo($_cURL); + if ($i['redirect_url']) return 1; + if ($i['http_code'] && $i['http_code'] >= 400) return 1; + + return $_RDATA['sp_robots_header']; + } + ); + +} else OS_crawlLog('cURL functions are not enabled; cannot perform crawl', 2); + + +// ***** Pre-fill queue with starting URL(s) at depth 0, blank referer +$_QUEUE = array(); +foreach ($_RDATA['sp_starting'] as $starting) { + $starting = OS_formatURL($starting, $_ODATA['admin_install_domain'].'/'); + $_QUEUE[] = array($starting, 0, ''); + + // Add starting URLs to required URLs so the crawler cannot travel + // into parent directories + $_RDATA['sp_require_url'][] = preg_replace('/\/[^\/]*$/', '/', $starting); + + $host = parse_url($starting)['host']; + if (!in_array($host, $_RDATA['sp_hostnames'])) + $_RDATA['sp_hostnames'][] = $host; +} + +// ***** List of previously crawled links from the database +$_EXIST = $_DDATA['pdo']->query( + 'SELECT `url` FROM `'.$_DDATA['tbprefix'].'crawldata`' +)->fetchAll(); +foreach ($_EXIST as $key => $value) + $_EXIST[$key] = $value['url']; + + + +// Drop previous temp table if it exists +$drop = $_DDATA['pdo']->query( + 'DROP TABLE IF EXISTS `'.$_DDATA['tbprefix'].'crawltemp`;' +); + +// Create a temp MySQL storage table using schema of the existing table +$create = $_DDATA['pdo']->query( + 'CREATE TABLE `'.$_DDATA['tbprefix'].'crawltemp` LIKE `'.$_DDATA['tbprefix'].'crawldata`;' +); + + +// Prepare SQL statements +$selectData = $_DDATA['pdo']->prepare( + 'SELECT + `url`, `links`, `content_checksum`, `flag_updated`, + `last_modified`, `flag_unlisted`, `priority` + FROM `'.$_DDATA['tbprefix'].'crawldata` WHERE `url`=:url;' +); +$updateURL = $_DDATA['pdo']->prepare( + 'UPDATE `'.$_DDATA['tbprefix'].'crawltemp` SET + `url`=:url WHERE `content_checksum`=:content_checksum;' +); +$insertTemp = $_DDATA['pdo']->prepare( + 'INSERT INTO `'.$_DDATA['tbprefix'].'crawltemp` SET + `url`=:url, + `url_base`=:url_base, + `url_sort`=0, + `title`=:title, + `description`=:description, + `keywords`=:keywords, + `category`=:category, + `weighted`=:weighted, + `links`=:links, + `content`=:content, + `content_mime`=:content_mime, + `content_charset`=:content_charset, + `content_checksum`=:content_checksum, + `status`=:status, + `status_noindex`=:status_noindex, + `flag_unlisted`=:flag_unlisted, + `flag_updated`=:flag_updated, + `last_modified`=:last_modified, + `priority`=:priority + ;' +); +$insertNotModified = $_DDATA['pdo']->prepare( + 'INSERT INTO `'.$_DDATA['tbprefix'].'crawltemp` SELECT * FROM `'.$_DDATA['tbprefix'].'crawldata` WHERE `url`=:url;' +); +$updateNotModified = $_DDATA['pdo']->prepare( + 'UPDATE `'.$_DDATA['tbprefix'].'crawltemp` SET `flag_updated`=0, `status`=:status WHERE `url`=:url;' +); + + +// ***** Begin crawling URLs from the queue +while ($_cURL && count($_QUEUE)) { + + // Check if we have run out of execution time + if ($_ODATA['sp_time_start'] + $_ODATA['sp_timeout_crawl'] <= time()) { + OS_crawlLog('Maximum script runtime ('.$_ODATA['sp_timeout_crawl'].'s) reached', 2); + break; + } + + // Check if user has canceled the crawl + if (OS_getValue('sp_cancel')) { + OS_crawlLog('Crawl canceled manually by user', 2); + break; + } + + // Check if we have exceeded the maximum number of crawled links + if (count($_RDATA['sp_links']) > $_ODATA['sp_limit_crawl']) { + OS_crawlLog('Maximum number of crawled pages exceeded', 2); + break; + } + + // Retrieve next link to crawl from the queue + list($url, $depth, $referer) = array_shift($_QUEUE); + $_RDATA['sp_links'][] = $url; + + // Check if URL is beyond the depth limit + if ($depth > $_ODATA['sp_limit_depth']) { + OS_crawlLog('Maximum link depth ('.$_ODATA['sp_limit_depth'].') exceeded; URL at depth '.$depth.' was not stored: '.$url, 2); + continue; + } + + // Check robots.txt for newly encountered hostnames + $purl = parse_url($url); + $port = (isset($purl['port']) && !is_null($purl['port'])) ? ':'.$purl['port'] : ''; + if (!isset($_RDATA['sp_robots'][$purl['host']])) { + $_RDATA['sp_robots'][$purl['host']] = array(); + OS_crawlLog('Fetching robots.txt for domain: '.$purl['host'], 1); + + curl_setopt($_cURL, CURLOPT_TIMECONDITION, CURL_TIMECOND_NONE); + $robotstxt = OS_fetchURL($purl['scheme'].'://'.$purl['host'].$port.'/robots.txt', ''); + + if (!$robotstxt['errno']) { + $robots = array(); + $robot = ''; + $robolines = explode("\n", $robotstxt['content']); + foreach ($robolines as $line) { + if (preg_match('/^user-agent\s*:\s*(.*)\s*$/i', $line, $r)) { + if (!isset($robots[$robot = $r[1]])) + $robots[$robot] = array('disallow' => array(), 'allow' => array()); + } else if (preg_match('/((dis)?allow)\s*:\s*(.*)\s*$/i', $line, $r)) + $robots[$robot][strtolower($r[1])][] = OS_formatURL($r[3], $url); + } + foreach ($robots as $agent => $rules) { + if (preg_match('/^orca(-?php)?-?crawler$/i', $agent) || $agent == '*') { + foreach ($rules['disallow'] as $disrule) + if (!in_array($disrule, $_RDATA['sp_robots'][$purl['host']])) + $_RDATA['sp_robots'][$purl['host']][] = $disrule; + foreach ($rules['allow'] as $rule) { + $key = array_search($rule, $_RDATA['sp_robots'][$purl['host']]); + if ($key !== false) unset($_RDATA['sp_robots'][$purl['host']][$key]); + } + } + } + } + } + + if ($_DEBUGMODE) + OS_crawlLog('Memory used: '.OS_readSize(memory_get_usage(true)), 1); + + OS_crawlLog('Crawling: '.$url.' (Depth: '.$depth.')', 1); + OS_setValue('sp_progress', count($_RDATA['sp_links']).'/'.(count($_RDATA['sp_links']) + count($_QUEUE))); + + if ($_ODATA['sp_ifmodifiedsince'] && (!count($_EXIST) || in_array($url, $_EXIST))) { + curl_setopt($_cURL, CURLOPT_TIMECONDITION, CURL_TIMECOND_IFMODSINCE); + } else curl_setopt($_cURL, CURLOPT_TIMECONDITION, CURL_TIMECOND_NONE); + + $data = OS_fetchURL($url, $referer); + + $_RDATA['sp_data_transferred'] += $data['info']['size_download']; + $_RDATA['sp_time_curl'] += $data['info']['total_time']; + + if (!$data['errno']) + $data = OS_parseURLContent($data); + + // Decide whether or not to 'index' / store this page + switch ($data['info']['noindex']) { + + // ***** There is no 'noindex' reason, so store the page + case '': + case 'not-modified': + + $data['info']['status'] = 'OK'; + if ($referer == '') { + $data['info']['status'] = 'Orphan'; + $_RDATA['sp_status']['Orphan']++; + } + + // ***** Successfully scanned this URL and got content + if (!$data['info']['noindex']) { + + // Prevent duplicate content + if (isset($_RDATA['sp_sha1'][$data['info']['sha1']])) { + OS_crawlLog('Content is a duplicate of already indexed page: '.$_RDATA['sp_sha1'][$data['info']['sha1']], 2); + OS_crawlLog('Consider editing faulty redirects, or setting a \'canonical\' element to avoid this', 0); + + // Update the stored URL to the shortest version + if (strlen($url) < strlen($_RDATA['sp_sha1'][$data['info']['sha1']])) { + $updateURL->execute(array( + 'url' => $url, + 'content_checksum' => $data['info']['sha1'] + )); + } + break; + } + $_RDATA['sp_sha1'][$data['info']['sha1']] = $url; + + // If this URL exists (or existed) in the live table... + if (in_array($url, $_EXIST) || $referer == '') { + $selectData->execute(array('url' => $url)); + $err = $selectData->errorInfo(); + if ($err[0] != '00000') { + OS_crawlLog('Database select error: '.$url, 2); + OS_crawlLog($err[2], 0); + break; + } + $row = $selectData->fetchAll()[0]; + + // else provide default values to compare a new url against + } else { + $row = array( + 'content_checksum' => '', + 'flag_unlisted' => 0, + 'last_modified' => time(), + 'priority' => 0.5 + ); + } + + // If the content checksum is new + if ($row['content_checksum'] != $data['info']['sha1']) { + $row['flag_updated'] = 1; + if ($data['info']['filetime'] == -1) + $data['info']['filetime'] = time(); + + if (isset($row['url'])) { + $_RDATA['sp_status']['Updated']++; + } else $_RDATA['sp_status']['New']++; + + // else the content hasn't changed + } else { + $row['flag_updated'] = 0; + $data['info']['filetime'] = $row['last_modified']; + } + + $port = (isset($data['url']['port']) && !is_null($data['url']['port'])) ? ':'.$data['url']['port'] : ''; + $insertTemp->execute(array( + 'url' => $url, + 'url_base' => $data['url']['scheme'].'://'.$data['url']['host'].$port, + 'title' => $data['title'], + 'description' => $data['description'], + 'keywords' => $data['keywords'], + 'category' => 'Main', + 'weighted' => $data['weighted'], + 'links' => json_encode($data['links'], JSON_INVALID_UTF8_IGNORE), + 'content' => $data['content'], + 'content_mime' => $data['info']['mime_type'], + 'content_charset' => $data['info']['charset'], + 'content_checksum' => $data['info']['sha1'], + 'status' => $data['info']['status'], + 'status_noindex' => $data['info']['noindex'], + 'flag_unlisted' => $row['flag_unlisted'], + 'flag_updated' => $row['flag_updated'], + 'last_modified' => $data['info']['filetime'], + 'priority' => $row['priority'] + )); + if (!$insertTemp->rowCount()) { + OS_crawlLog('Database primary insert error: '.$url, 2); + $err = $insertTemp->errorInfo(); + if ($err[0] != '00000') OS_crawlLog($err[2], 0); + } else $_RDATA['sp_store'][] = $url; + + + // ***** URL hasn't been modified since the last successful crawl + } else { + + // Get previous entry from existing search database + $insertNotModified->execute(array('url' => $url)); + if (!$insertNotModified->rowCount()) { + OS_crawlLog('Database \'not-modified\' insert error: '.$url, 2); + $err = $insertNotModified->errorInfo(); + if ($err[0] != '00000') OS_crawlLog($err[2], 0); + + } else { + $_RDATA['sp_store'][] = $url; + + // Unset the 'flag_updated' column + $updateNotModified->execute(array( + 'url' => $url, + 'status' => $data['info']['status'] + )); + $err = $updateNotModified->errorInfo(); + if ($err[0] != '00000') { + OS_crawlLog('Database unset \'flag_updated\' update error: '.$url, 2); + OS_crawlLog($err[2], 0); + } else { + + // Select the 'priority' value for use in the sitemap + $selectData->execute(array('url' => $url)); + $err = $selectData->errorInfo(); + if ($err[0] == '00000') { + $row = $selectData->fetchAll()[0]; + $data['links'] = json_decode($row['links'], true); + } else { + OS_crawlLog('Database existing table row read error: '.$url, 2); + $row = array('priority' => 0.5); + } + } + } + } + + // Store data for use in the sitemap + if ($_ODATA['sp_sitemap_file'] && + $data['url']['host'] == $_ODATA['sp_sitemap_hostname']) { + $delta = time() - $data['info']['filetime']; + $cf = 'always'; + if ($delta > 2700 && $delta <= 64800) $cf = 'hourly'; + if ($delta > 64800 && $delta <= 432000) $cf = 'daily'; + if ($delta > 432000 && $delta <= 2160000) $cf = 'weekly'; + if ($delta > 2160000 && $delta <= 21600000) $cf = 'monthly'; + if ($delta > 21600000 && $delta <= 62400000) $cf = 'yearly'; + if ($delta > 62400000) $cf = 'never'; + + $_RDATA['sp_sitemap'][] = array( + 'loc' => str_replace(' ', '%20', htmlentities($url)), + 'lastmod' => date('Y-m-d', $data['info']['filetime']), + 'changefreq' => $cf, + 'priority' => $row['priority'] + ); + } + break; + + + // ***** Otherwise, log the reason why this page was not stored + case 'timeout': + case 'network-error': + case 'couldnt-connect': + OS_crawlLog($data['error'].': '.$url, 2); + if ($referer == '') $_RDATA['sp_status']['Blocked']++; + break; + + case 'empty': + case 'too-large': + case 'robots-meta': + case 'robots-http': + case 'unknown-mime': + case 'self-reference': + case 'secured-pdf': + case 'couldnt-decode-pdf': + OS_crawlLog($data['error'], 1); + if ($referer == '') $_RDATA['sp_status']['Blocked']++; + break; + + case '400': + OS_crawlLog($data['error'].': '.$url.' (Referrer was: '.$referer.')', 2); + if ($referer == '') $_RDATA['sp_status']['Not Found']++; + break; + + case 'redirect-meta': + case 'redirect-location': + OS_crawlLog($data['error'].': '.$url.' (Referrer was: '.$referer.')', 2); + OS_crawlLog('Previously indexed page was removed in favour of redirected URL', 0); + break; + + case 'non-canonical': + OS_crawlLog('Not indexed due to canonical element: '.$data['info']['canonical'], 1); + OS_crawlLog('Referrer was: '.$referer, 0); + break; + + default: + OS_crawlLog('Not indexed due to noindex rule \''.$data['info']['noindex'].'\': '.$url.' (Referrer was: '.$referer.')', 2); + if ($referer == '') $_RDATA['sp_status']['Blocked']++; + break; + + } + + // Check if we have stored the maximum allowed number of pages + if (count($_RDATA['sp_store']) >= $_ODATA['sp_limit_store']) { + OS_crawlLog('Maximum number of crawled pages reached ('.$_ODATA['sp_limit_store'].')', 1); + $_RDATA['sp_complete'] = true; + break; + } + + // If we fetched more links from the content above, parse and add + // them to the queue + if (!$data['info']['nofollow']) { + foreach ($data['links'] as $link) { + + $link = OS_formatURL($link, $data['base']); + + // ***** If this link hasn't been crawled yet + if (!in_array($link, $_RDATA['sp_links'])) { + + // ... and if link hasn't been queued yet + foreach ($_QUEUE as $queue) + if ($link == $queue[0]) continue 2; + + // ... and if link passes our user filters + if ($nx = OS_filterURL($link, $data['base'])) { + OS_crawlLog('Link ignored due to noindex rule \''.$nx.'\': '.$link, 0); + continue; + } + + // ... then add the link to the queue + $_QUEUE[] = array($link, $depth + 1, $url); + } + } + } + + // If we've completed the queue, check for orphans + if (!count($_QUEUE)) { + $_EXIST = array_diff($_EXIST, $_RDATA['sp_links']); + if (count($_EXIST) && !$_ODATA['sp_autodelete']) { + OS_crawlLog('Adding '.count($_EXIST).' orphan(s) to queue...', 1); + while (count($_EXIST)) { + + $link = array_shift($_EXIST); + + // If orphan URL passes our user filters + if ($nx = OS_filterURL($link, $data['base'])) { + OS_crawlLog('Orphan URL ignored due to noindex rule \''.$nx.'\': '.$link, 0); + $_RDATA['sp_status']['Blocked']++; + continue; + } + + $_QUEUE[] = array($link, 0, ''); + } + + } else if (count($_RDATA['sp_store'])) { + $_RDATA['sp_complete'] = true; + + } else OS_crawlLog('No pages could be indexed; check your starting URL(s)', 2); + } + + gc_collect_cycles(); + + usleep($_ODATA['sp_sleep'] * 1000); + $_RDATA['sp_sleep'] += $_ODATA['sp_sleep']; +} + +// ***** Write sitemap +if ($_RDATA['sp_complete'] && $_ODATA['sp_sitemap_file']) { + if (file_exists($_ODATA['sp_sitemap_file'])) { + if (is_writable($_ODATA['sp_sitemap_file'])) { + $sm = array(''); + $sm[] = ''; + foreach ($_RDATA['sp_sitemap'] as $sitemap) { + $sm[] = ' '; + foreach ($sitemap as $key => $value) { + if ($key == 'priority' && $value == 0.5) continue; + $sm[] = ' <'.$key.'>'.$value.''; + } + $sm[] = ' '; + } + $sm[] = ''; + + if (preg_match('/\.xml\.gz$/', $_ODATA['sp_sitemap_file'])) { + if (function_exists('gzopen')) { + $smf = gzopen($_ODATA['sp_sitemap_file'], 'w'); + gzwrite($smf, implode("\n", $sm)); + gzclose($smf); + OS_crawlLog('Sitemap written successfully: '.$_ODATA['sp_sitemap_file'], 1); + + } else OS_crawlLog('Could not write sitemap; PHP gzip functions are not enabled', 2); + + } else if (preg_match('/\.xml$/', $_ODATA['sp_sitemap_file'])) { + $smf = fopen($_ODATA['sp_sitemap_file'], 'w'); + fwrite($smf, implode("\n", $sm)); + fclose($smf); + OS_crawlLog('Sitemap written successfully: '.$_ODATA['sp_sitemap_file'], 1); + + } else OS_crawlLog('Sitemap filename ('.$_ODATA['sp_sitemap_file'].') must have extension \'.xml\' or \'.xml.gz\'', 2); + + } else OS_crawlLog('Sitemap file \''.$_ODATA['sp_sitemap_file'].'\' is not writable', 2); + + } else OS_crawlLog('Sitemap file \''.$_ODATA['sp_sitemap_file'].'\' does not exist', 2); +} ?> \ No newline at end of file diff --git a/os3/css/admin.css b/os3/css/admin.css new file mode 100644 index 0000000..329bd76 --- /dev/null +++ b/os3/css/admin.css @@ -0,0 +1,78 @@ +progress { + height:1.6em; +} +progress::-webkit-progress-bar { + background:transparent; +} +progress::-moz-progress-bar { + background-color:lightblue !important; +} +progress::-webkit-progress-value { + background-color:lightblue !important; +} +progress::after { + content:attr(data-progress); + position:absolute; + top:0; + left:0; + width:100%; + height:100%; + color:black; + font-weight:bold; + line-height:1.5em; + text-align:center; + vertical-align:middle; + white-space:nowrap; +} + +#crawlerModal .crawl-log-prev, +#crawlerModal .modal-footer button, +#crawlerModal.crawler-log .crawl-controls, +#crawlerModal.crawler-log .modal-footer p, +#crawlerModal.crawler-log .crawl-progress, +#crawlerModal.crawler-log .crawl-log { + display:none; +} +#crawlerModal.crawler-log .modal-footer button, +#crawlerModal.crawler-log .crawl-log-prev { + display:block; +} + +input[type="number"] { + max-width:5em; +} + +.mw-90 { + max-width:90%; +} +.mw-10em { + max-width:10em; +} + +.table-fixed { + table-layout:fixed; +} + +.text-ellipsis { + text-overflow:ellipsis; +} + +img.svg-icon { + height:1.2em; +} +img.svg-icon-sm { + height:0.75em; +} +img.svg-icon-flag { + width:1.5em; +} + +#os_queries_thead > tr > th > img { + visibility:hidden; +} +#os_queries_thead > tr > th.os_sorting > img { + visibility:visible; +} +#os_queries_thead > tr > th.os_asc > img { + transform:rotate(180deg); +} diff --git a/os3/css/bootstrap.min.css b/os3/css/bootstrap.min.css new file mode 100644 index 0000000..fc0655e --- /dev/null +++ b/os3/css/bootstrap.min.css @@ -0,0 +1,5 @@ +@charset "UTF-8";/*! + * Bootstrap v5.3.0-alpha2 (https://getbootstrap.com/) + * Copyright 2011-2023 The Bootstrap Authors + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) + */:root,[data-bs-theme=light]{--bs-blue:#0d6efd;--bs-indigo:#6610f2;--bs-purple:#6f42c1;--bs-pink:#d63384;--bs-red:#dc3545;--bs-orange:#fd7e14;--bs-yellow:#ffc107;--bs-green:#198754;--bs-teal:#20c997;--bs-cyan:#0dcaf0;--bs-black:#000;--bs-white:#fff;--bs-gray:#6c757d;--bs-gray-dark:#343a40;--bs-gray-100:#f8f9fa;--bs-gray-200:#e9ecef;--bs-gray-300:#dee2e6;--bs-gray-400:#ced4da;--bs-gray-500:#adb5bd;--bs-gray-600:#6c757d;--bs-gray-700:#495057;--bs-gray-800:#343a40;--bs-gray-900:#212529;--bs-primary:#0d6efd;--bs-secondary:#6c757d;--bs-success:#198754;--bs-info:#0dcaf0;--bs-warning:#ffc107;--bs-danger:#dc3545;--bs-light:#f8f9fa;--bs-dark:#212529;--bs-primary-rgb:13,110,253;--bs-secondary-rgb:108,117,125;--bs-success-rgb:25,135,84;--bs-info-rgb:13,202,240;--bs-warning-rgb:255,193,7;--bs-danger-rgb:220,53,69;--bs-light-rgb:248,249,250;--bs-dark-rgb:33,37,41;--bs-primary-text-emphasis:#052c65;--bs-secondary-text-emphasis:#2b2f32;--bs-success-text-emphasis:#0a3622;--bs-info-text-emphasis:#055160;--bs-warning-text-emphasis:#664d03;--bs-danger-text-emphasis:#58151c;--bs-light-text-emphasis:#495057;--bs-dark-text-emphasis:#495057;--bs-primary-bg-subtle:#cfe2ff;--bs-secondary-bg-subtle:#e2e3e5;--bs-success-bg-subtle:#d1e7dd;--bs-info-bg-subtle:#cff4fc;--bs-warning-bg-subtle:#fff3cd;--bs-danger-bg-subtle:#f8d7da;--bs-light-bg-subtle:#fcfcfd;--bs-dark-bg-subtle:#ced4da;--bs-primary-border-subtle:#9ec5fe;--bs-secondary-border-subtle:#c4c8cb;--bs-success-border-subtle:#a3cfbb;--bs-info-border-subtle:#9eeaf9;--bs-warning-border-subtle:#ffe69c;--bs-danger-border-subtle:#f1aeb5;--bs-light-border-subtle:#e9ecef;--bs-dark-border-subtle:#adb5bd;--bs-white-rgb:255,255,255;--bs-black-rgb:0,0,0;--bs-font-sans-serif:system-ui,-apple-system,"Segoe UI",Roboto,"Helvetica Neue","Noto Sans","Liberation Sans",Arial,sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";--bs-font-monospace:SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace;--bs-gradient:linear-gradient(180deg, rgba(255, 255, 255, 0.15), rgba(255, 255, 255, 0));--bs-body-font-family:var(--bs-font-sans-serif);--bs-body-font-size:1rem;--bs-body-font-weight:400;--bs-body-line-height:1.5;--bs-body-color:#212529;--bs-body-color-rgb:33,37,41;--bs-body-bg:#fff;--bs-body-bg-rgb:255,255,255;--bs-emphasis-color:#000;--bs-emphasis-color-rgb:0,0,0;--bs-secondary-color:rgba(33, 37, 41, 0.75);--bs-secondary-color-rgb:33,37,41;--bs-secondary-bg:#e9ecef;--bs-secondary-bg-rgb:233,236,239;--bs-tertiary-color:rgba(33, 37, 41, 0.5);--bs-tertiary-color-rgb:33,37,41;--bs-tertiary-bg:#f8f9fa;--bs-tertiary-bg-rgb:248,249,250;--bs-link-color:#0d6efd;--bs-link-color-rgb:13,110,253;--bs-link-decoration:underline;--bs-link-hover-color:#0a58ca;--bs-link-hover-color-rgb:10,88,202;--bs-code-color:#d63384;--bs-highlight-bg:#fff3cd;--bs-border-width:1px;--bs-border-style:solid;--bs-border-color:#dee2e6;--bs-border-color-translucent:rgba(0, 0, 0, 0.175);--bs-border-radius:0.375rem;--bs-border-radius-sm:0.25rem;--bs-border-radius-lg:0.5rem;--bs-border-radius-xl:1rem;--bs-border-radius-xxl:2rem;--bs-border-radius-2xl:var(--bs-border-radius-xxl);--bs-border-radius-pill:50rem;--bs-box-shadow:0 0.5rem 1rem rgba(0, 0, 0, 0.15);--bs-box-shadow-sm:0 0.125rem 0.25rem rgba(0, 0, 0, 0.075);--bs-box-shadow-lg:0 1rem 3rem rgba(0, 0, 0, 0.175);--bs-box-shadow-inset:inset 0 1px 2px rgba(0, 0, 0, 0.075);--bs-focus-ring-width:0.25rem;--bs-focus-ring-opacity:0.25;--bs-focus-ring-color:rgba(13, 110, 253, 0.25);--bs-form-valid-color:#198754;--bs-form-valid-border-color:#198754;--bs-form-invalid-color:#dc3545;--bs-form-invalid-border-color:#dc3545}[data-bs-theme=dark]{color-scheme:dark;--bs-body-color:#adb5bd;--bs-body-color-rgb:173,181,189;--bs-body-bg:#212529;--bs-body-bg-rgb:33,37,41;--bs-emphasis-color:#fff;--bs-emphasis-color-rgb:255,255,255;--bs-secondary-color:rgba(173, 181, 189, 0.75);--bs-secondary-color-rgb:173,181,189;--bs-secondary-bg:#343a40;--bs-secondary-bg-rgb:52,58,64;--bs-tertiary-color:rgba(173, 181, 189, 0.5);--bs-tertiary-color-rgb:173,181,189;--bs-tertiary-bg:#2b3035;--bs-tertiary-bg-rgb:43,48,53;--bs-primary-text-emphasis:#6ea8fe;--bs-secondary-text-emphasis:#a7acb1;--bs-success-text-emphasis:#75b798;--bs-info-text-emphasis:#6edff6;--bs-warning-text-emphasis:#ffda6a;--bs-danger-text-emphasis:#ea868f;--bs-light-text-emphasis:#f8f9fa;--bs-dark-text-emphasis:#dee2e6;--bs-primary-bg-subtle:#031633;--bs-secondary-bg-subtle:#161719;--bs-success-bg-subtle:#051b11;--bs-info-bg-subtle:#032830;--bs-warning-bg-subtle:#332701;--bs-danger-bg-subtle:#2c0b0e;--bs-light-bg-subtle:#343a40;--bs-dark-bg-subtle:#1a1d20;--bs-primary-border-subtle:#084298;--bs-secondary-border-subtle:#41464b;--bs-success-border-subtle:#0f5132;--bs-info-border-subtle:#087990;--bs-warning-border-subtle:#997404;--bs-danger-border-subtle:#842029;--bs-light-border-subtle:#495057;--bs-dark-border-subtle:#343a40;--bs-link-color:#6ea8fe;--bs-link-hover-color:#8bb9fe;--bs-link-color-rgb:110,168,254;--bs-link-hover-color-rgb:139,185,254;--bs-code-color:#e685b5;--bs-border-color:#495057;--bs-border-color-translucent:rgba(255, 255, 255, 0.15);--bs-form-valid-color:#75b798;--bs-form-valid-border-color:#75b798;--bs-form-invalid-color:#ea868f;--bs-form-invalid-border-color:#ea868f}*,::after,::before{box-sizing:border-box}@media (prefers-reduced-motion:no-preference){:root{scroll-behavior:smooth}}body{margin:0;font-family:var(--bs-body-font-family);font-size:var(--bs-body-font-size);font-weight:var(--bs-body-font-weight);line-height:var(--bs-body-line-height);color:var(--bs-body-color);text-align:var(--bs-body-text-align);background-color:var(--bs-body-bg);-webkit-text-size-adjust:100%;-webkit-tap-highlight-color:transparent}hr{margin:1rem 0;color:inherit;border:0;border-top:var(--bs-border-width) solid;opacity:.25}.h1,.h2,.h3,.h4,.h5,.h6,h1,h2,h3,h4,h5,h6{margin-top:0;margin-bottom:.5rem;font-weight:500;line-height:1.2;color:var(--bs-heading-color,inherit)}.h1,h1{font-size:calc(1.375rem + 1.5vw)}@media (min-width:1200px){.h1,h1{font-size:2.5rem}}.h2,h2{font-size:calc(1.325rem + .9vw)}@media (min-width:1200px){.h2,h2{font-size:2rem}}.h3,h3{font-size:calc(1.3rem + .6vw)}@media (min-width:1200px){.h3,h3{font-size:1.75rem}}.h4,h4{font-size:calc(1.275rem + .3vw)}@media (min-width:1200px){.h4,h4{font-size:1.5rem}}.h5,h5{font-size:1.25rem}.h6,h6{font-size:1rem}p{margin-top:0;margin-bottom:1rem}abbr[title]{-webkit-text-decoration:underline dotted;text-decoration:underline dotted;cursor:help;-webkit-text-decoration-skip-ink:none;text-decoration-skip-ink:none}address{margin-bottom:1rem;font-style:normal;line-height:inherit}ol,ul{padding-left:2rem}dl,ol,ul{margin-top:0;margin-bottom:1rem}ol ol,ol ul,ul ol,ul ul{margin-bottom:0}dt{font-weight:700}dd{margin-bottom:.5rem;margin-left:0}blockquote{margin:0 0 1rem}b,strong{font-weight:bolder}.small,small{font-size:.875em}.mark,mark{padding:.1875em;background-color:var(--bs-highlight-bg)}sub,sup{position:relative;font-size:.75em;line-height:0;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}a{color:rgba(var(--bs-link-color-rgb),var(--bs-link-opacity,1));text-decoration:underline}a:hover{--bs-link-color-rgb:var(--bs-link-hover-color-rgb)}a:not([href]):not([class]),a:not([href]):not([class]):hover{color:inherit;text-decoration:none}code,kbd,pre,samp{font-family:var(--bs-font-monospace);font-size:1em}pre{display:block;margin-top:0;margin-bottom:1rem;overflow:auto;font-size:.875em}pre code{font-size:inherit;color:inherit;word-break:normal}code{font-size:.875em;color:var(--bs-code-color);word-wrap:break-word}a>code{color:inherit}kbd{padding:.1875rem .375rem;font-size:.875em;color:var(--bs-body-bg);background-color:var(--bs-body-color);border-radius:.25rem}kbd kbd{padding:0;font-size:1em}figure{margin:0 0 1rem}img,svg{vertical-align:middle}table{caption-side:bottom;border-collapse:collapse}caption{padding-top:.5rem;padding-bottom:.5rem;color:var(--bs-secondary-color);text-align:left}th{text-align:inherit;text-align:-webkit-match-parent}tbody,td,tfoot,th,thead,tr{border-color:inherit;border-style:solid;border-width:0}label{display:inline-block}button{border-radius:0}button:focus:not(:focus-visible){outline:0}button,input,optgroup,select,textarea{margin:0;font-family:inherit;font-size:inherit;line-height:inherit}button,select{text-transform:none}[role=button]{cursor:pointer}select{word-wrap:normal}select:disabled{opacity:1}[list]:not([type=date]):not([type=datetime-local]):not([type=month]):not([type=week]):not([type=time])::-webkit-calendar-picker-indicator{display:none!important}[type=button],[type=reset],[type=submit],button{-webkit-appearance:button}[type=button]:not(:disabled),[type=reset]:not(:disabled),[type=submit]:not(:disabled),button:not(:disabled){cursor:pointer}::-moz-focus-inner{padding:0;border-style:none}textarea{resize:vertical}fieldset{min-width:0;padding:0;margin:0;border:0}legend{float:left;width:100%;padding:0;margin-bottom:.5rem;font-size:calc(1.275rem + .3vw);line-height:inherit}@media (min-width:1200px){legend{font-size:1.5rem}}legend+*{clear:left}::-webkit-datetime-edit-day-field,::-webkit-datetime-edit-fields-wrapper,::-webkit-datetime-edit-hour-field,::-webkit-datetime-edit-minute,::-webkit-datetime-edit-month-field,::-webkit-datetime-edit-text,::-webkit-datetime-edit-year-field{padding:0}::-webkit-inner-spin-button{height:auto}[type=search]{outline-offset:-2px;-webkit-appearance:textfield}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-color-swatch-wrapper{padding:0}::-webkit-file-upload-button{font:inherit;-webkit-appearance:button}::file-selector-button{font:inherit;-webkit-appearance:button}output{display:inline-block}iframe{border:0}summary{display:list-item;cursor:pointer}progress{vertical-align:baseline}[hidden]{display:none!important}.lead{font-size:1.25rem;font-weight:300}.display-1{font-size:calc(1.625rem + 4.5vw);font-weight:300;line-height:1.2}@media (min-width:1200px){.display-1{font-size:5rem}}.display-2{font-size:calc(1.575rem + 3.9vw);font-weight:300;line-height:1.2}@media (min-width:1200px){.display-2{font-size:4.5rem}}.display-3{font-size:calc(1.525rem + 3.3vw);font-weight:300;line-height:1.2}@media (min-width:1200px){.display-3{font-size:4rem}}.display-4{font-size:calc(1.475rem + 2.7vw);font-weight:300;line-height:1.2}@media (min-width:1200px){.display-4{font-size:3.5rem}}.display-5{font-size:calc(1.425rem + 2.1vw);font-weight:300;line-height:1.2}@media (min-width:1200px){.display-5{font-size:3rem}}.display-6{font-size:calc(1.375rem + 1.5vw);font-weight:300;line-height:1.2}@media (min-width:1200px){.display-6{font-size:2.5rem}}.list-unstyled{padding-left:0;list-style:none}.list-inline{padding-left:0;list-style:none}.list-inline-item{display:inline-block}.list-inline-item:not(:last-child){margin-right:.5rem}.initialism{font-size:.875em;text-transform:uppercase}.blockquote{margin-bottom:1rem;font-size:1.25rem}.blockquote>:last-child{margin-bottom:0}.blockquote-footer{margin-top:-1rem;margin-bottom:1rem;font-size:.875em;color:#6c757d}.blockquote-footer::before{content:"— "}.img-fluid{max-width:100%;height:auto}.img-thumbnail{padding:.25rem;background-color:var(--bs-body-bg);border:var(--bs-border-width) solid var(--bs-border-color);border-radius:var(--bs-border-radius);max-width:100%;height:auto}.figure{display:inline-block}.figure-img{margin-bottom:.5rem;line-height:1}.figure-caption{font-size:.875em;color:var(--bs-secondary-color)}.container,.container-fluid,.container-lg,.container-md,.container-sm,.container-xl,.container-xxl{--bs-gutter-x:1.5rem;--bs-gutter-y:0;width:100%;padding-right:calc(var(--bs-gutter-x) * .5);padding-left:calc(var(--bs-gutter-x) * .5);margin-right:auto;margin-left:auto}@media (min-width:576px){.container,.container-sm{max-width:540px}}@media (min-width:768px){.container,.container-md,.container-sm{max-width:720px}}@media (min-width:992px){.container,.container-lg,.container-md,.container-sm{max-width:960px}}@media (min-width:1200px){.container,.container-lg,.container-md,.container-sm,.container-xl{max-width:1140px}}@media (min-width:1400px){.container,.container-lg,.container-md,.container-sm,.container-xl,.container-xxl{max-width:1320px}}:root{--bs-breakpoint-xs:0;--bs-breakpoint-sm:576px;--bs-breakpoint-md:768px;--bs-breakpoint-lg:992px;--bs-breakpoint-xl:1200px;--bs-breakpoint-xxl:1400px}.row{--bs-gutter-x:1.5rem;--bs-gutter-y:0;display:flex;flex-wrap:wrap;margin-top:calc(-1 * var(--bs-gutter-y));margin-right:calc(-.5 * var(--bs-gutter-x));margin-left:calc(-.5 * var(--bs-gutter-x))}.row>*{flex-shrink:0;width:100%;max-width:100%;padding-right:calc(var(--bs-gutter-x) * .5);padding-left:calc(var(--bs-gutter-x) * .5);margin-top:var(--bs-gutter-y)}.col{flex:1 0 0%}.row-cols-auto>*{flex:0 0 auto;width:auto}.row-cols-1>*{flex:0 0 auto;width:100%}.row-cols-2>*{flex:0 0 auto;width:50%}.row-cols-3>*{flex:0 0 auto;width:33.3333333333%}.row-cols-4>*{flex:0 0 auto;width:25%}.row-cols-5>*{flex:0 0 auto;width:20%}.row-cols-6>*{flex:0 0 auto;width:16.6666666667%}.col-auto{flex:0 0 auto;width:auto}.col-1{flex:0 0 auto;width:8.33333333%}.col-2{flex:0 0 auto;width:16.66666667%}.col-3{flex:0 0 auto;width:25%}.col-4{flex:0 0 auto;width:33.33333333%}.col-5{flex:0 0 auto;width:41.66666667%}.col-6{flex:0 0 auto;width:50%}.col-7{flex:0 0 auto;width:58.33333333%}.col-8{flex:0 0 auto;width:66.66666667%}.col-9{flex:0 0 auto;width:75%}.col-10{flex:0 0 auto;width:83.33333333%}.col-11{flex:0 0 auto;width:91.66666667%}.col-12{flex:0 0 auto;width:100%}.offset-1{margin-left:8.33333333%}.offset-2{margin-left:16.66666667%}.offset-3{margin-left:25%}.offset-4{margin-left:33.33333333%}.offset-5{margin-left:41.66666667%}.offset-6{margin-left:50%}.offset-7{margin-left:58.33333333%}.offset-8{margin-left:66.66666667%}.offset-9{margin-left:75%}.offset-10{margin-left:83.33333333%}.offset-11{margin-left:91.66666667%}.g-0,.gx-0{--bs-gutter-x:0}.g-0,.gy-0{--bs-gutter-y:0}.g-1,.gx-1{--bs-gutter-x:0.25rem}.g-1,.gy-1{--bs-gutter-y:0.25rem}.g-2,.gx-2{--bs-gutter-x:0.5rem}.g-2,.gy-2{--bs-gutter-y:0.5rem}.g-3,.gx-3{--bs-gutter-x:1rem}.g-3,.gy-3{--bs-gutter-y:1rem}.g-4,.gx-4{--bs-gutter-x:1.5rem}.g-4,.gy-4{--bs-gutter-y:1.5rem}.g-5,.gx-5{--bs-gutter-x:3rem}.g-5,.gy-5{--bs-gutter-y:3rem}@media (min-width:576px){.col-sm{flex:1 0 0%}.row-cols-sm-auto>*{flex:0 0 auto;width:auto}.row-cols-sm-1>*{flex:0 0 auto;width:100%}.row-cols-sm-2>*{flex:0 0 auto;width:50%}.row-cols-sm-3>*{flex:0 0 auto;width:33.3333333333%}.row-cols-sm-4>*{flex:0 0 auto;width:25%}.row-cols-sm-5>*{flex:0 0 auto;width:20%}.row-cols-sm-6>*{flex:0 0 auto;width:16.6666666667%}.col-sm-auto{flex:0 0 auto;width:auto}.col-sm-1{flex:0 0 auto;width:8.33333333%}.col-sm-2{flex:0 0 auto;width:16.66666667%}.col-sm-3{flex:0 0 auto;width:25%}.col-sm-4{flex:0 0 auto;width:33.33333333%}.col-sm-5{flex:0 0 auto;width:41.66666667%}.col-sm-6{flex:0 0 auto;width:50%}.col-sm-7{flex:0 0 auto;width:58.33333333%}.col-sm-8{flex:0 0 auto;width:66.66666667%}.col-sm-9{flex:0 0 auto;width:75%}.col-sm-10{flex:0 0 auto;width:83.33333333%}.col-sm-11{flex:0 0 auto;width:91.66666667%}.col-sm-12{flex:0 0 auto;width:100%}.offset-sm-0{margin-left:0}.offset-sm-1{margin-left:8.33333333%}.offset-sm-2{margin-left:16.66666667%}.offset-sm-3{margin-left:25%}.offset-sm-4{margin-left:33.33333333%}.offset-sm-5{margin-left:41.66666667%}.offset-sm-6{margin-left:50%}.offset-sm-7{margin-left:58.33333333%}.offset-sm-8{margin-left:66.66666667%}.offset-sm-9{margin-left:75%}.offset-sm-10{margin-left:83.33333333%}.offset-sm-11{margin-left:91.66666667%}.g-sm-0,.gx-sm-0{--bs-gutter-x:0}.g-sm-0,.gy-sm-0{--bs-gutter-y:0}.g-sm-1,.gx-sm-1{--bs-gutter-x:0.25rem}.g-sm-1,.gy-sm-1{--bs-gutter-y:0.25rem}.g-sm-2,.gx-sm-2{--bs-gutter-x:0.5rem}.g-sm-2,.gy-sm-2{--bs-gutter-y:0.5rem}.g-sm-3,.gx-sm-3{--bs-gutter-x:1rem}.g-sm-3,.gy-sm-3{--bs-gutter-y:1rem}.g-sm-4,.gx-sm-4{--bs-gutter-x:1.5rem}.g-sm-4,.gy-sm-4{--bs-gutter-y:1.5rem}.g-sm-5,.gx-sm-5{--bs-gutter-x:3rem}.g-sm-5,.gy-sm-5{--bs-gutter-y:3rem}}@media (min-width:768px){.col-md{flex:1 0 0%}.row-cols-md-auto>*{flex:0 0 auto;width:auto}.row-cols-md-1>*{flex:0 0 auto;width:100%}.row-cols-md-2>*{flex:0 0 auto;width:50%}.row-cols-md-3>*{flex:0 0 auto;width:33.3333333333%}.row-cols-md-4>*{flex:0 0 auto;width:25%}.row-cols-md-5>*{flex:0 0 auto;width:20%}.row-cols-md-6>*{flex:0 0 auto;width:16.6666666667%}.col-md-auto{flex:0 0 auto;width:auto}.col-md-1{flex:0 0 auto;width:8.33333333%}.col-md-2{flex:0 0 auto;width:16.66666667%}.col-md-3{flex:0 0 auto;width:25%}.col-md-4{flex:0 0 auto;width:33.33333333%}.col-md-5{flex:0 0 auto;width:41.66666667%}.col-md-6{flex:0 0 auto;width:50%}.col-md-7{flex:0 0 auto;width:58.33333333%}.col-md-8{flex:0 0 auto;width:66.66666667%}.col-md-9{flex:0 0 auto;width:75%}.col-md-10{flex:0 0 auto;width:83.33333333%}.col-md-11{flex:0 0 auto;width:91.66666667%}.col-md-12{flex:0 0 auto;width:100%}.offset-md-0{margin-left:0}.offset-md-1{margin-left:8.33333333%}.offset-md-2{margin-left:16.66666667%}.offset-md-3{margin-left:25%}.offset-md-4{margin-left:33.33333333%}.offset-md-5{margin-left:41.66666667%}.offset-md-6{margin-left:50%}.offset-md-7{margin-left:58.33333333%}.offset-md-8{margin-left:66.66666667%}.offset-md-9{margin-left:75%}.offset-md-10{margin-left:83.33333333%}.offset-md-11{margin-left:91.66666667%}.g-md-0,.gx-md-0{--bs-gutter-x:0}.g-md-0,.gy-md-0{--bs-gutter-y:0}.g-md-1,.gx-md-1{--bs-gutter-x:0.25rem}.g-md-1,.gy-md-1{--bs-gutter-y:0.25rem}.g-md-2,.gx-md-2{--bs-gutter-x:0.5rem}.g-md-2,.gy-md-2{--bs-gutter-y:0.5rem}.g-md-3,.gx-md-3{--bs-gutter-x:1rem}.g-md-3,.gy-md-3{--bs-gutter-y:1rem}.g-md-4,.gx-md-4{--bs-gutter-x:1.5rem}.g-md-4,.gy-md-4{--bs-gutter-y:1.5rem}.g-md-5,.gx-md-5{--bs-gutter-x:3rem}.g-md-5,.gy-md-5{--bs-gutter-y:3rem}}@media (min-width:992px){.col-lg{flex:1 0 0%}.row-cols-lg-auto>*{flex:0 0 auto;width:auto}.row-cols-lg-1>*{flex:0 0 auto;width:100%}.row-cols-lg-2>*{flex:0 0 auto;width:50%}.row-cols-lg-3>*{flex:0 0 auto;width:33.3333333333%}.row-cols-lg-4>*{flex:0 0 auto;width:25%}.row-cols-lg-5>*{flex:0 0 auto;width:20%}.row-cols-lg-6>*{flex:0 0 auto;width:16.6666666667%}.col-lg-auto{flex:0 0 auto;width:auto}.col-lg-1{flex:0 0 auto;width:8.33333333%}.col-lg-2{flex:0 0 auto;width:16.66666667%}.col-lg-3{flex:0 0 auto;width:25%}.col-lg-4{flex:0 0 auto;width:33.33333333%}.col-lg-5{flex:0 0 auto;width:41.66666667%}.col-lg-6{flex:0 0 auto;width:50%}.col-lg-7{flex:0 0 auto;width:58.33333333%}.col-lg-8{flex:0 0 auto;width:66.66666667%}.col-lg-9{flex:0 0 auto;width:75%}.col-lg-10{flex:0 0 auto;width:83.33333333%}.col-lg-11{flex:0 0 auto;width:91.66666667%}.col-lg-12{flex:0 0 auto;width:100%}.offset-lg-0{margin-left:0}.offset-lg-1{margin-left:8.33333333%}.offset-lg-2{margin-left:16.66666667%}.offset-lg-3{margin-left:25%}.offset-lg-4{margin-left:33.33333333%}.offset-lg-5{margin-left:41.66666667%}.offset-lg-6{margin-left:50%}.offset-lg-7{margin-left:58.33333333%}.offset-lg-8{margin-left:66.66666667%}.offset-lg-9{margin-left:75%}.offset-lg-10{margin-left:83.33333333%}.offset-lg-11{margin-left:91.66666667%}.g-lg-0,.gx-lg-0{--bs-gutter-x:0}.g-lg-0,.gy-lg-0{--bs-gutter-y:0}.g-lg-1,.gx-lg-1{--bs-gutter-x:0.25rem}.g-lg-1,.gy-lg-1{--bs-gutter-y:0.25rem}.g-lg-2,.gx-lg-2{--bs-gutter-x:0.5rem}.g-lg-2,.gy-lg-2{--bs-gutter-y:0.5rem}.g-lg-3,.gx-lg-3{--bs-gutter-x:1rem}.g-lg-3,.gy-lg-3{--bs-gutter-y:1rem}.g-lg-4,.gx-lg-4{--bs-gutter-x:1.5rem}.g-lg-4,.gy-lg-4{--bs-gutter-y:1.5rem}.g-lg-5,.gx-lg-5{--bs-gutter-x:3rem}.g-lg-5,.gy-lg-5{--bs-gutter-y:3rem}}@media (min-width:1200px){.col-xl{flex:1 0 0%}.row-cols-xl-auto>*{flex:0 0 auto;width:auto}.row-cols-xl-1>*{flex:0 0 auto;width:100%}.row-cols-xl-2>*{flex:0 0 auto;width:50%}.row-cols-xl-3>*{flex:0 0 auto;width:33.3333333333%}.row-cols-xl-4>*{flex:0 0 auto;width:25%}.row-cols-xl-5>*{flex:0 0 auto;width:20%}.row-cols-xl-6>*{flex:0 0 auto;width:16.6666666667%}.col-xl-auto{flex:0 0 auto;width:auto}.col-xl-1{flex:0 0 auto;width:8.33333333%}.col-xl-2{flex:0 0 auto;width:16.66666667%}.col-xl-3{flex:0 0 auto;width:25%}.col-xl-4{flex:0 0 auto;width:33.33333333%}.col-xl-5{flex:0 0 auto;width:41.66666667%}.col-xl-6{flex:0 0 auto;width:50%}.col-xl-7{flex:0 0 auto;width:58.33333333%}.col-xl-8{flex:0 0 auto;width:66.66666667%}.col-xl-9{flex:0 0 auto;width:75%}.col-xl-10{flex:0 0 auto;width:83.33333333%}.col-xl-11{flex:0 0 auto;width:91.66666667%}.col-xl-12{flex:0 0 auto;width:100%}.offset-xl-0{margin-left:0}.offset-xl-1{margin-left:8.33333333%}.offset-xl-2{margin-left:16.66666667%}.offset-xl-3{margin-left:25%}.offset-xl-4{margin-left:33.33333333%}.offset-xl-5{margin-left:41.66666667%}.offset-xl-6{margin-left:50%}.offset-xl-7{margin-left:58.33333333%}.offset-xl-8{margin-left:66.66666667%}.offset-xl-9{margin-left:75%}.offset-xl-10{margin-left:83.33333333%}.offset-xl-11{margin-left:91.66666667%}.g-xl-0,.gx-xl-0{--bs-gutter-x:0}.g-xl-0,.gy-xl-0{--bs-gutter-y:0}.g-xl-1,.gx-xl-1{--bs-gutter-x:0.25rem}.g-xl-1,.gy-xl-1{--bs-gutter-y:0.25rem}.g-xl-2,.gx-xl-2{--bs-gutter-x:0.5rem}.g-xl-2,.gy-xl-2{--bs-gutter-y:0.5rem}.g-xl-3,.gx-xl-3{--bs-gutter-x:1rem}.g-xl-3,.gy-xl-3{--bs-gutter-y:1rem}.g-xl-4,.gx-xl-4{--bs-gutter-x:1.5rem}.g-xl-4,.gy-xl-4{--bs-gutter-y:1.5rem}.g-xl-5,.gx-xl-5{--bs-gutter-x:3rem}.g-xl-5,.gy-xl-5{--bs-gutter-y:3rem}}@media (min-width:1400px){.col-xxl{flex:1 0 0%}.row-cols-xxl-auto>*{flex:0 0 auto;width:auto}.row-cols-xxl-1>*{flex:0 0 auto;width:100%}.row-cols-xxl-2>*{flex:0 0 auto;width:50%}.row-cols-xxl-3>*{flex:0 0 auto;width:33.3333333333%}.row-cols-xxl-4>*{flex:0 0 auto;width:25%}.row-cols-xxl-5>*{flex:0 0 auto;width:20%}.row-cols-xxl-6>*{flex:0 0 auto;width:16.6666666667%}.col-xxl-auto{flex:0 0 auto;width:auto}.col-xxl-1{flex:0 0 auto;width:8.33333333%}.col-xxl-2{flex:0 0 auto;width:16.66666667%}.col-xxl-3{flex:0 0 auto;width:25%}.col-xxl-4{flex:0 0 auto;width:33.33333333%}.col-xxl-5{flex:0 0 auto;width:41.66666667%}.col-xxl-6{flex:0 0 auto;width:50%}.col-xxl-7{flex:0 0 auto;width:58.33333333%}.col-xxl-8{flex:0 0 auto;width:66.66666667%}.col-xxl-9{flex:0 0 auto;width:75%}.col-xxl-10{flex:0 0 auto;width:83.33333333%}.col-xxl-11{flex:0 0 auto;width:91.66666667%}.col-xxl-12{flex:0 0 auto;width:100%}.offset-xxl-0{margin-left:0}.offset-xxl-1{margin-left:8.33333333%}.offset-xxl-2{margin-left:16.66666667%}.offset-xxl-3{margin-left:25%}.offset-xxl-4{margin-left:33.33333333%}.offset-xxl-5{margin-left:41.66666667%}.offset-xxl-6{margin-left:50%}.offset-xxl-7{margin-left:58.33333333%}.offset-xxl-8{margin-left:66.66666667%}.offset-xxl-9{margin-left:75%}.offset-xxl-10{margin-left:83.33333333%}.offset-xxl-11{margin-left:91.66666667%}.g-xxl-0,.gx-xxl-0{--bs-gutter-x:0}.g-xxl-0,.gy-xxl-0{--bs-gutter-y:0}.g-xxl-1,.gx-xxl-1{--bs-gutter-x:0.25rem}.g-xxl-1,.gy-xxl-1{--bs-gutter-y:0.25rem}.g-xxl-2,.gx-xxl-2{--bs-gutter-x:0.5rem}.g-xxl-2,.gy-xxl-2{--bs-gutter-y:0.5rem}.g-xxl-3,.gx-xxl-3{--bs-gutter-x:1rem}.g-xxl-3,.gy-xxl-3{--bs-gutter-y:1rem}.g-xxl-4,.gx-xxl-4{--bs-gutter-x:1.5rem}.g-xxl-4,.gy-xxl-4{--bs-gutter-y:1.5rem}.g-xxl-5,.gx-xxl-5{--bs-gutter-x:3rem}.g-xxl-5,.gy-xxl-5{--bs-gutter-y:3rem}}.table{--bs-table-color:var(--bs-body-color);--bs-table-bg:transparent;--bs-table-border-color:var(--bs-border-color);--bs-table-accent-bg:transparent;--bs-table-striped-color:var(--bs-body-color);--bs-table-striped-bg:rgba(0, 0, 0, 0.05);--bs-table-active-color:var(--bs-body-color);--bs-table-active-bg:rgba(0, 0, 0, 0.1);--bs-table-hover-color:var(--bs-body-color);--bs-table-hover-bg:rgba(0, 0, 0, 0.075);width:100%;margin-bottom:1rem;color:var(--bs-table-color);vertical-align:top;border-color:var(--bs-table-border-color)}.table>:not(caption)>*>*{padding:.5rem .5rem;background-color:var(--bs-table-bg);border-bottom-width:var(--bs-border-width);box-shadow:inset 0 0 0 9999px var(--bs-table-accent-bg)}.table>tbody{vertical-align:inherit}.table>thead{vertical-align:bottom}.table-group-divider{border-top:calc(var(--bs-border-width) * 2) solid currentcolor}.caption-top{caption-side:top}.table-sm>:not(caption)>*>*{padding:.25rem .25rem}.table-bordered>:not(caption)>*{border-width:var(--bs-border-width) 0}.table-bordered>:not(caption)>*>*{border-width:0 var(--bs-border-width)}.table-borderless>:not(caption)>*>*{border-bottom-width:0}.table-borderless>:not(:first-child){border-top-width:0}.table-striped>tbody>tr:nth-of-type(odd)>*{--bs-table-accent-bg:var(--bs-table-striped-bg);color:var(--bs-table-striped-color)}.table-striped-columns>:not(caption)>tr>:nth-child(2n){--bs-table-accent-bg:var(--bs-table-striped-bg);color:var(--bs-table-striped-color)}.table-active{--bs-table-accent-bg:var(--bs-table-active-bg);color:var(--bs-table-active-color)}.table-hover>tbody>tr:hover>*{--bs-table-accent-bg:var(--bs-table-hover-bg);color:var(--bs-table-hover-color)}.table-primary{--bs-table-color:#000;--bs-table-bg:#cfe2ff;--bs-table-border-color:#bacbe6;--bs-table-striped-bg:#c5d7f2;--bs-table-striped-color:#000;--bs-table-active-bg:#bacbe6;--bs-table-active-color:#000;--bs-table-hover-bg:#bfd1ec;--bs-table-hover-color:#000;color:var(--bs-table-color);border-color:var(--bs-table-border-color)}.table-secondary{--bs-table-color:#000;--bs-table-bg:#e2e3e5;--bs-table-border-color:#cbccce;--bs-table-striped-bg:#d7d8da;--bs-table-striped-color:#000;--bs-table-active-bg:#cbccce;--bs-table-active-color:#000;--bs-table-hover-bg:#d1d2d4;--bs-table-hover-color:#000;color:var(--bs-table-color);border-color:var(--bs-table-border-color)}.table-success{--bs-table-color:#000;--bs-table-bg:#d1e7dd;--bs-table-border-color:#bcd0c7;--bs-table-striped-bg:#c7dbd2;--bs-table-striped-color:#000;--bs-table-active-bg:#bcd0c7;--bs-table-active-color:#000;--bs-table-hover-bg:#c1d6cc;--bs-table-hover-color:#000;color:var(--bs-table-color);border-color:var(--bs-table-border-color)}.table-info{--bs-table-color:#000;--bs-table-bg:#cff4fc;--bs-table-border-color:#badce3;--bs-table-striped-bg:#c5e8ef;--bs-table-striped-color:#000;--bs-table-active-bg:#badce3;--bs-table-active-color:#000;--bs-table-hover-bg:#bfe2e9;--bs-table-hover-color:#000;color:var(--bs-table-color);border-color:var(--bs-table-border-color)}.table-warning{--bs-table-color:#000;--bs-table-bg:#fff3cd;--bs-table-border-color:#e6dbb9;--bs-table-striped-bg:#f2e7c3;--bs-table-striped-color:#000;--bs-table-active-bg:#e6dbb9;--bs-table-active-color:#000;--bs-table-hover-bg:#ece1be;--bs-table-hover-color:#000;color:var(--bs-table-color);border-color:var(--bs-table-border-color)}.table-danger{--bs-table-color:#000;--bs-table-bg:#f8d7da;--bs-table-border-color:#dfc2c4;--bs-table-striped-bg:#eccccf;--bs-table-striped-color:#000;--bs-table-active-bg:#dfc2c4;--bs-table-active-color:#000;--bs-table-hover-bg:#e5c7ca;--bs-table-hover-color:#000;color:var(--bs-table-color);border-color:var(--bs-table-border-color)}.table-light{--bs-table-color:#000;--bs-table-bg:#f8f9fa;--bs-table-border-color:#dfe0e1;--bs-table-striped-bg:#ecedee;--bs-table-striped-color:#000;--bs-table-active-bg:#dfe0e1;--bs-table-active-color:#000;--bs-table-hover-bg:#e5e6e7;--bs-table-hover-color:#000;color:var(--bs-table-color);border-color:var(--bs-table-border-color)}.table-dark{--bs-table-color:#fff;--bs-table-bg:#212529;--bs-table-border-color:#373b3e;--bs-table-striped-bg:#2c3034;--bs-table-striped-color:#fff;--bs-table-active-bg:#373b3e;--bs-table-active-color:#fff;--bs-table-hover-bg:#323539;--bs-table-hover-color:#fff;color:var(--bs-table-color);border-color:var(--bs-table-border-color)}.table-responsive{overflow-x:auto;-webkit-overflow-scrolling:touch}@media (max-width:575.98px){.table-responsive-sm{overflow-x:auto;-webkit-overflow-scrolling:touch}}@media (max-width:767.98px){.table-responsive-md{overflow-x:auto;-webkit-overflow-scrolling:touch}}@media (max-width:991.98px){.table-responsive-lg{overflow-x:auto;-webkit-overflow-scrolling:touch}}@media (max-width:1199.98px){.table-responsive-xl{overflow-x:auto;-webkit-overflow-scrolling:touch}}@media (max-width:1399.98px){.table-responsive-xxl{overflow-x:auto;-webkit-overflow-scrolling:touch}}.form-label{margin-bottom:.5rem}.col-form-label{padding-top:calc(.375rem + var(--bs-border-width));padding-bottom:calc(.375rem + var(--bs-border-width));margin-bottom:0;font-size:inherit;line-height:1.5}.col-form-label-lg{padding-top:calc(.5rem + var(--bs-border-width));padding-bottom:calc(.5rem + var(--bs-border-width));font-size:1.25rem}.col-form-label-sm{padding-top:calc(.25rem + var(--bs-border-width));padding-bottom:calc(.25rem + var(--bs-border-width));font-size:.875rem}.form-text{margin-top:.25rem;font-size:.875em;color:var(--bs-secondary-color)}.form-control{display:block;width:100%;padding:.375rem .75rem;font-size:1rem;font-weight:400;line-height:1.5;color:var(--bs-body-color);background-color:var(--bs-body-bg);background-clip:padding-box;border:var(--bs-border-width) solid var(--bs-border-color);-webkit-appearance:none;-moz-appearance:none;appearance:none;border-radius:.375rem;transition:border-color .15s ease-in-out,box-shadow .15s ease-in-out}@media (prefers-reduced-motion:reduce){.form-control{transition:none}}.form-control[type=file]{overflow:hidden}.form-control[type=file]:not(:disabled):not([readonly]){cursor:pointer}.form-control:focus{color:var(--bs-body-color);background-color:var(--bs-body-bg);border-color:#86b7fe;outline:0;box-shadow:0 0 0 .25rem rgba(13,110,253,.25)}.form-control::-webkit-date-and-time-value{min-width:85px;height:1.5em;margin:0}.form-control::-webkit-datetime-edit{display:block;padding:0}.form-control::-moz-placeholder{color:var(--bs-secondary-color);opacity:1}.form-control::placeholder{color:var(--bs-secondary-color);opacity:1}.form-control:disabled{background-color:var(--bs-secondary-bg);opacity:1}.form-control::-webkit-file-upload-button{padding:.375rem .75rem;margin:-.375rem -.75rem;-webkit-margin-end:.75rem;margin-inline-end:.75rem;color:var(--bs-body-color);background-color:var(--bs-tertiary-bg);pointer-events:none;border-color:inherit;border-style:solid;border-width:0;border-inline-end-width:var(--bs-border-width);border-radius:0;-webkit-transition:color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;transition:color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out}.form-control::file-selector-button{padding:.375rem .75rem;margin:-.375rem -.75rem;-webkit-margin-end:.75rem;margin-inline-end:.75rem;color:var(--bs-body-color);background-color:var(--bs-tertiary-bg);pointer-events:none;border-color:inherit;border-style:solid;border-width:0;border-inline-end-width:var(--bs-border-width);border-radius:0;transition:color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out}@media (prefers-reduced-motion:reduce){.form-control::-webkit-file-upload-button{-webkit-transition:none;transition:none}.form-control::file-selector-button{transition:none}}.form-control:hover:not(:disabled):not([readonly])::-webkit-file-upload-button{background-color:var(--bs-secondary-bg)}.form-control:hover:not(:disabled):not([readonly])::file-selector-button{background-color:var(--bs-secondary-bg)}.form-control-plaintext{display:block;width:100%;padding:.375rem 0;margin-bottom:0;line-height:1.5;color:var(--bs-body-color);background-color:transparent;border:solid transparent;border-width:var(--bs-border-width) 0}.form-control-plaintext:focus{outline:0}.form-control-plaintext.form-control-lg,.form-control-plaintext.form-control-sm{padding-right:0;padding-left:0}.form-control-sm{min-height:calc(1.5em + .5rem + calc(var(--bs-border-width) * 2));padding:.25rem .5rem;font-size:.875rem;border-radius:.25rem}.form-control-sm::-webkit-file-upload-button{padding:.25rem .5rem;margin:-.25rem -.5rem;-webkit-margin-end:.5rem;margin-inline-end:.5rem}.form-control-sm::file-selector-button{padding:.25rem .5rem;margin:-.25rem -.5rem;-webkit-margin-end:.5rem;margin-inline-end:.5rem}.form-control-lg{min-height:calc(1.5em + 1rem + calc(var(--bs-border-width) * 2));padding:.5rem 1rem;font-size:1.25rem;border-radius:.5rem}.form-control-lg::-webkit-file-upload-button{padding:.5rem 1rem;margin:-.5rem -1rem;-webkit-margin-end:1rem;margin-inline-end:1rem}.form-control-lg::file-selector-button{padding:.5rem 1rem;margin:-.5rem -1rem;-webkit-margin-end:1rem;margin-inline-end:1rem}textarea.form-control{min-height:calc(1.5em + .75rem + calc(var(--bs-border-width) * 2))}textarea.form-control-sm{min-height:calc(1.5em + .5rem + calc(var(--bs-border-width) * 2))}textarea.form-control-lg{min-height:calc(1.5em + 1rem + calc(var(--bs-border-width) * 2))}.form-control-color{width:3rem;height:calc(1.5em + .75rem + calc(var(--bs-border-width) * 2));padding:.375rem}.form-control-color:not(:disabled):not([readonly]){cursor:pointer}.form-control-color::-moz-color-swatch{border:0!important;border-radius:.375rem}.form-control-color::-webkit-color-swatch{border:0!important;border-radius:.375rem}.form-control-color.form-control-sm{height:calc(1.5em + .5rem + calc(var(--bs-border-width) * 2))}.form-control-color.form-control-lg{height:calc(1.5em + 1rem + calc(var(--bs-border-width) * 2))}.form-select{--bs-form-select-bg-img:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3e%3cpath fill='none' stroke='%23343a40' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='m2 5 6 6 6-6'/%3e%3c/svg%3e");display:block;width:100%;padding:.375rem 2.25rem .375rem .75rem;font-size:1rem;font-weight:400;line-height:1.5;color:var(--bs-body-color);background-color:var(--bs-body-bg);background-image:var(--bs-form-select-bg-img),var(--bs-form-select-bg-icon,none);background-repeat:no-repeat;background-position:right .75rem center;background-size:16px 12px;border:var(--bs-border-width) solid var(--bs-border-color);border-radius:.375rem;transition:border-color .15s ease-in-out,box-shadow .15s ease-in-out;-webkit-appearance:none;-moz-appearance:none;appearance:none}@media (prefers-reduced-motion:reduce){.form-select{transition:none}}.form-select:focus{border-color:#86b7fe;outline:0;box-shadow:0 0 0 .25rem rgba(13,110,253,.25)}.form-select[multiple],.form-select[size]:not([size="1"]){padding-right:.75rem;background-image:none}.form-select:disabled{background-color:var(--bs-secondary-bg)}.form-select:-moz-focusring{color:transparent;text-shadow:0 0 0 var(--bs-body-color)}.form-select-sm{padding-top:.25rem;padding-bottom:.25rem;padding-left:.5rem;font-size:.875rem;border-radius:.25rem}.form-select-lg{padding-top:.5rem;padding-bottom:.5rem;padding-left:1rem;font-size:1.25rem;border-radius:.5rem}[data-bs-theme=dark] .form-select{--bs-form-select-bg-img:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3e%3cpath fill='none' stroke='%23adb5bd' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='m2 5 6 6 6-6'/%3e%3c/svg%3e")}.form-check{display:block;min-height:1.5rem;padding-left:1.5em;margin-bottom:.125rem}.form-check .form-check-input{float:left;margin-left:-1.5em}.form-check-reverse{padding-right:1.5em;padding-left:0;text-align:right}.form-check-reverse .form-check-input{float:right;margin-right:-1.5em;margin-left:0}.form-check-input{--bs-form-check-bg:var(--bs-body-bg);width:1em;height:1em;margin-top:.25em;vertical-align:top;background-color:var(--bs-form-check-bg);background-image:var(--bs-form-check-bg-image);background-repeat:no-repeat;background-position:center;background-size:contain;border:var(--bs-border-width) solid var(--bs-border-color);-webkit-appearance:none;-moz-appearance:none;appearance:none;-webkit-print-color-adjust:exact;color-adjust:exact;print-color-adjust:exact}.form-check-input[type=checkbox]{border-radius:.25em}.form-check-input[type=radio]{border-radius:50%}.form-check-input:active{filter:brightness(90%)}.form-check-input:focus{border-color:#86b7fe;outline:0;box-shadow:0 0 0 .25rem rgba(13,110,253,.25)}.form-check-input:checked{background-color:#0d6efd;border-color:#0d6efd}.form-check-input:checked[type=checkbox]{--bs-form-check-bg-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20'%3e%3cpath fill='none' stroke='%23fff' stroke-linecap='round' stroke-linejoin='round' stroke-width='3' d='m6 10 3 3 6-6'/%3e%3c/svg%3e")}.form-check-input:checked[type=radio]{--bs-form-check-bg-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3e%3ccircle r='2' fill='%23fff'/%3e%3c/svg%3e")}.form-check-input[type=checkbox]:indeterminate{background-color:#0d6efd;border-color:#0d6efd;--bs-form-check-bg-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20'%3e%3cpath fill='none' stroke='%23fff' stroke-linecap='round' stroke-linejoin='round' stroke-width='3' d='M6 10h8'/%3e%3c/svg%3e")}.form-check-input:disabled{pointer-events:none;filter:none;opacity:.5}.form-check-input:disabled~.form-check-label,.form-check-input[disabled]~.form-check-label{cursor:default;opacity:.5}.form-switch{padding-left:2.5em}.form-switch .form-check-input{--bs-form-switch-bg:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3e%3ccircle r='3' fill='rgba%280, 0, 0, 0.25%29'/%3e%3c/svg%3e");width:2em;margin-left:-2.5em;background-image:var(--bs-form-switch-bg);background-position:left center;border-radius:2em;transition:background-position .15s ease-in-out}@media (prefers-reduced-motion:reduce){.form-switch .form-check-input{transition:none}}.form-switch .form-check-input:focus{--bs-form-switch-bg:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3e%3ccircle r='3' fill='%2386b7fe'/%3e%3c/svg%3e")}.form-switch .form-check-input:checked{background-position:right center;--bs-form-switch-bg:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3e%3ccircle r='3' fill='%23fff'/%3e%3c/svg%3e")}.form-switch.form-check-reverse{padding-right:2.5em;padding-left:0}.form-switch.form-check-reverse .form-check-input{margin-right:-2.5em;margin-left:0}.form-check-inline{display:inline-block;margin-right:1rem}.btn-check{position:absolute;clip:rect(0,0,0,0);pointer-events:none}.btn-check:disabled+.btn,.btn-check[disabled]+.btn{pointer-events:none;filter:none;opacity:.65}[data-bs-theme=dark] .form-switch .form-check-input:not(:checked):not(:focus){--bs-form-switch-bg:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3e%3ccircle r='3' fill='rgba%28255, 255, 255, 0.25%29'/%3e%3c/svg%3e")}.form-range{width:100%;height:1.5rem;padding:0;background-color:transparent;-webkit-appearance:none;-moz-appearance:none;appearance:none}.form-range:focus{outline:0}.form-range:focus::-webkit-slider-thumb{box-shadow:0 0 0 1px #fff,0 0 0 .25rem rgba(13,110,253,.25)}.form-range:focus::-moz-range-thumb{box-shadow:0 0 0 1px #fff,0 0 0 .25rem rgba(13,110,253,.25)}.form-range::-moz-focus-outer{border:0}.form-range::-webkit-slider-thumb{width:1rem;height:1rem;margin-top:-.25rem;background-color:#0d6efd;border:0;border-radius:1rem;-webkit-transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;-webkit-appearance:none;appearance:none}@media (prefers-reduced-motion:reduce){.form-range::-webkit-slider-thumb{-webkit-transition:none;transition:none}}.form-range::-webkit-slider-thumb:active{background-color:#b6d4fe}.form-range::-webkit-slider-runnable-track{width:100%;height:.5rem;color:transparent;cursor:pointer;background-color:var(--bs-tertiary-bg);border-color:transparent;border-radius:1rem}.form-range::-moz-range-thumb{width:1rem;height:1rem;background-color:#0d6efd;border:0;border-radius:1rem;-moz-transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;-moz-appearance:none;appearance:none}@media (prefers-reduced-motion:reduce){.form-range::-moz-range-thumb{-moz-transition:none;transition:none}}.form-range::-moz-range-thumb:active{background-color:#b6d4fe}.form-range::-moz-range-track{width:100%;height:.5rem;color:transparent;cursor:pointer;background-color:var(--bs-tertiary-bg);border-color:transparent;border-radius:1rem}.form-range:disabled{pointer-events:none}.form-range:disabled::-webkit-slider-thumb{background-color:var(--bs-secondary-color)}.form-range:disabled::-moz-range-thumb{background-color:var(--bs-secondary-color)}.form-floating{position:relative}.form-floating:not(.form-control:disabled)::before{position:absolute;top:var(--bs-border-width);left:var(--bs-border-width);width:calc(100% - (calc(calc(.375em + .1875rem) + calc(.75em + .375rem))));height:1.875em;content:"";background-color:var(--bs-body-bg);border-radius:.375rem}.form-floating>.form-control,.form-floating>.form-control-plaintext,.form-floating>.form-select{height:calc(3.5rem + calc(var(--bs-border-width) * 2));line-height:1.25}.form-floating>label{position:absolute;top:0;left:0;width:100%;height:100%;padding:1rem .75rem;overflow:hidden;text-align:start;text-overflow:ellipsis;white-space:nowrap;pointer-events:none;border:var(--bs-border-width) solid transparent;transform-origin:0 0;transition:opacity .1s ease-in-out,transform .1s ease-in-out}@media (prefers-reduced-motion:reduce){.form-floating>label{transition:none}}.form-floating>.form-control,.form-floating>.form-control-plaintext{padding:1rem .75rem}.form-floating>.form-control-plaintext::-moz-placeholder,.form-floating>.form-control::-moz-placeholder{color:transparent}.form-floating>.form-control-plaintext::placeholder,.form-floating>.form-control::placeholder{color:transparent}.form-floating>.form-control-plaintext:not(:-moz-placeholder-shown),.form-floating>.form-control:not(:-moz-placeholder-shown){padding-top:1.625rem;padding-bottom:.625rem}.form-floating>.form-control-plaintext:focus,.form-floating>.form-control-plaintext:not(:placeholder-shown),.form-floating>.form-control:focus,.form-floating>.form-control:not(:placeholder-shown){padding-top:1.625rem;padding-bottom:.625rem}.form-floating>.form-control-plaintext:-webkit-autofill,.form-floating>.form-control:-webkit-autofill{padding-top:1.625rem;padding-bottom:.625rem}.form-floating>.form-select{padding-top:1.625rem;padding-bottom:.625rem}.form-floating>.form-control:not(:-moz-placeholder-shown)~label{opacity:.65;transform:scale(.85) translateY(-.5rem) translateX(.15rem)}.form-floating>.form-control-plaintext~label,.form-floating>.form-control:focus~label,.form-floating>.form-control:not(:placeholder-shown)~label,.form-floating>.form-select~label{opacity:.65;transform:scale(.85) translateY(-.5rem) translateX(.15rem)}.form-floating>.form-control:-webkit-autofill~label{opacity:.65;transform:scale(.85) translateY(-.5rem) translateX(.15rem)}.form-floating>.form-control-plaintext~label{border-width:var(--bs-border-width) 0}.form-floating>.form-control:disabled~label{color:#6c757d}.input-group{position:relative;display:flex;flex-wrap:wrap;align-items:stretch;width:100%}.input-group>.form-control,.input-group>.form-floating,.input-group>.form-select{position:relative;flex:1 1 auto;width:1%;min-width:0}.input-group>.form-control:focus,.input-group>.form-floating:focus-within,.input-group>.form-select:focus{z-index:5}.input-group .btn{position:relative;z-index:2}.input-group .btn:focus{z-index:5}.input-group-text{display:flex;align-items:center;padding:.375rem .75rem;font-size:1rem;font-weight:400;line-height:1.5;color:var(--bs-body-color);text-align:center;white-space:nowrap;background-color:var(--bs-tertiary-bg);border:var(--bs-border-width) solid var(--bs-border-color);border-radius:.375rem}.input-group-lg>.btn,.input-group-lg>.form-control,.input-group-lg>.form-select,.input-group-lg>.input-group-text{padding:.5rem 1rem;font-size:1.25rem;border-radius:.5rem}.input-group-sm>.btn,.input-group-sm>.form-control,.input-group-sm>.form-select,.input-group-sm>.input-group-text{padding:.25rem .5rem;font-size:.875rem;border-radius:.25rem}.input-group-lg>.form-select,.input-group-sm>.form-select{padding-right:3rem}.input-group:not(.has-validation)>.dropdown-toggle:nth-last-child(n+3),.input-group:not(.has-validation)>.form-floating:not(:last-child)>.form-control,.input-group:not(.has-validation)>.form-floating:not(:last-child)>.form-select,.input-group:not(.has-validation)>:not(:last-child):not(.dropdown-toggle):not(.dropdown-menu):not(.form-floating){border-top-right-radius:0;border-bottom-right-radius:0}.input-group.has-validation>.dropdown-toggle:nth-last-child(n+4),.input-group.has-validation>.form-floating:nth-last-child(n+3)>.form-control,.input-group.has-validation>.form-floating:nth-last-child(n+3)>.form-select,.input-group.has-validation>:nth-last-child(n+3):not(.dropdown-toggle):not(.dropdown-menu):not(.form-floating){border-top-right-radius:0;border-bottom-right-radius:0}.input-group>:not(:first-child):not(.dropdown-menu):not(.valid-tooltip):not(.valid-feedback):not(.invalid-tooltip):not(.invalid-feedback){margin-left:calc(var(--bs-border-width) * -1);border-top-left-radius:0;border-bottom-left-radius:0}.input-group>.form-floating:not(:first-child)>.form-control,.input-group>.form-floating:not(:first-child)>.form-select{border-top-left-radius:0;border-bottom-left-radius:0}.valid-feedback{display:none;width:100%;margin-top:.25rem;font-size:.875em;color:var(--bs-form-valid-color)}.valid-tooltip{position:absolute;top:100%;z-index:5;display:none;max-width:100%;padding:.25rem .5rem;margin-top:.1rem;font-size:.875rem;color:#fff;background-color:var(--bs-success);border-radius:var(--bs-border-radius)}.is-valid~.valid-feedback,.is-valid~.valid-tooltip,.was-validated :valid~.valid-feedback,.was-validated :valid~.valid-tooltip{display:block}.form-control.is-valid,.was-validated .form-control:valid{border-color:var(--bs-form-valid-border-color);padding-right:calc(1.5em + .75rem);background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3e%3cpath fill='%23198754' d='M2.3 6.73.6 4.53c-.4-1.04.46-1.4 1.1-.8l1.1 1.4 3.4-3.8c.6-.63 1.6-.27 1.2.7l-4 4.6c-.43.5-.8.4-1.1.1z'/%3e%3c/svg%3e");background-repeat:no-repeat;background-position:right calc(.375em + .1875rem) center;background-size:calc(.75em + .375rem) calc(.75em + .375rem)}.form-control.is-valid:focus,.was-validated .form-control:valid:focus{border-color:var(--bs-form-valid-border-color);box-shadow:0 0 0 .25rem rgba(var(--bs-success-rgb),.25)}.was-validated textarea.form-control:valid,textarea.form-control.is-valid{padding-right:calc(1.5em + .75rem);background-position:top calc(.375em + .1875rem) right calc(.375em + .1875rem)}.form-select.is-valid,.was-validated .form-select:valid{border-color:var(--bs-form-valid-border-color)}.form-select.is-valid:not([multiple]):not([size]),.form-select.is-valid:not([multiple])[size="1"],.was-validated .form-select:valid:not([multiple]):not([size]),.was-validated .form-select:valid:not([multiple])[size="1"]{--bs-form-select-bg-icon:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3e%3cpath fill='%23198754' d='M2.3 6.73.6 4.53c-.4-1.04.46-1.4 1.1-.8l1.1 1.4 3.4-3.8c.6-.63 1.6-.27 1.2.7l-4 4.6c-.43.5-.8.4-1.1.1z'/%3e%3c/svg%3e");padding-right:4.125rem;background-position:right .75rem center,center right 2.25rem;background-size:16px 12px,calc(.75em + .375rem) calc(.75em + .375rem)}.form-select.is-valid:focus,.was-validated .form-select:valid:focus{border-color:var(--bs-form-valid-border-color);box-shadow:0 0 0 .25rem rgba(var(--bs-success-rgb),.25)}.form-control-color.is-valid,.was-validated .form-control-color:valid{width:calc(3rem + calc(1.5em + .75rem))}.form-check-input.is-valid,.was-validated .form-check-input:valid{border-color:var(--bs-form-valid-border-color)}.form-check-input.is-valid:checked,.was-validated .form-check-input:valid:checked{background-color:var(--bs-form-valid-color)}.form-check-input.is-valid:focus,.was-validated .form-check-input:valid:focus{box-shadow:0 0 0 .25rem rgba(var(--bs-success-rgb),.25)}.form-check-input.is-valid~.form-check-label,.was-validated .form-check-input:valid~.form-check-label{color:var(--bs-form-valid-color)}.form-check-inline .form-check-input~.valid-feedback{margin-left:.5em}.input-group>.form-control:not(:focus).is-valid,.input-group>.form-floating:not(:focus-within).is-valid,.input-group>.form-select:not(:focus).is-valid,.was-validated .input-group>.form-control:not(:focus):valid,.was-validated .input-group>.form-floating:not(:focus-within):valid,.was-validated .input-group>.form-select:not(:focus):valid{z-index:3}.invalid-feedback{display:none;width:100%;margin-top:.25rem;font-size:.875em;color:var(--bs-form-invalid-color)}.invalid-tooltip{position:absolute;top:100%;z-index:5;display:none;max-width:100%;padding:.25rem .5rem;margin-top:.1rem;font-size:.875rem;color:#fff;background-color:var(--bs-danger);border-radius:var(--bs-border-radius)}.is-invalid~.invalid-feedback,.is-invalid~.invalid-tooltip,.was-validated :invalid~.invalid-feedback,.was-validated :invalid~.invalid-tooltip{display:block}.form-control.is-invalid,.was-validated .form-control:invalid{border-color:var(--bs-form-invalid-border-color);padding-right:calc(1.5em + .75rem);background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 12' width='12' height='12' fill='none' stroke='%23dc3545'%3e%3ccircle cx='6' cy='6' r='4.5'/%3e%3cpath stroke-linejoin='round' d='M5.8 3.6h.4L6 6.5z'/%3e%3ccircle cx='6' cy='8.2' r='.6' fill='%23dc3545' stroke='none'/%3e%3c/svg%3e");background-repeat:no-repeat;background-position:right calc(.375em + .1875rem) center;background-size:calc(.75em + .375rem) calc(.75em + .375rem)}.form-control.is-invalid:focus,.was-validated .form-control:invalid:focus{border-color:var(--bs-form-invalid-border-color);box-shadow:0 0 0 .25rem rgba(var(--bs-danger-rgb),.25)}.was-validated textarea.form-control:invalid,textarea.form-control.is-invalid{padding-right:calc(1.5em + .75rem);background-position:top calc(.375em + .1875rem) right calc(.375em + .1875rem)}.form-select.is-invalid,.was-validated .form-select:invalid{border-color:var(--bs-form-invalid-border-color)}.form-select.is-invalid:not([multiple]):not([size]),.form-select.is-invalid:not([multiple])[size="1"],.was-validated .form-select:invalid:not([multiple]):not([size]),.was-validated .form-select:invalid:not([multiple])[size="1"]{--bs-form-select-bg-icon:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 12' width='12' height='12' fill='none' stroke='%23dc3545'%3e%3ccircle cx='6' cy='6' r='4.5'/%3e%3cpath stroke-linejoin='round' d='M5.8 3.6h.4L6 6.5z'/%3e%3ccircle cx='6' cy='8.2' r='.6' fill='%23dc3545' stroke='none'/%3e%3c/svg%3e");padding-right:4.125rem;background-position:right .75rem center,center right 2.25rem;background-size:16px 12px,calc(.75em + .375rem) calc(.75em + .375rem)}.form-select.is-invalid:focus,.was-validated .form-select:invalid:focus{border-color:var(--bs-form-invalid-border-color);box-shadow:0 0 0 .25rem rgba(var(--bs-danger-rgb),.25)}.form-control-color.is-invalid,.was-validated .form-control-color:invalid{width:calc(3rem + calc(1.5em + .75rem))}.form-check-input.is-invalid,.was-validated .form-check-input:invalid{border-color:var(--bs-form-invalid-border-color)}.form-check-input.is-invalid:checked,.was-validated .form-check-input:invalid:checked{background-color:var(--bs-form-invalid-color)}.form-check-input.is-invalid:focus,.was-validated .form-check-input:invalid:focus{box-shadow:0 0 0 .25rem rgba(var(--bs-danger-rgb),.25)}.form-check-input.is-invalid~.form-check-label,.was-validated .form-check-input:invalid~.form-check-label{color:var(--bs-form-invalid-color)}.form-check-inline .form-check-input~.invalid-feedback{margin-left:.5em}.input-group>.form-control:not(:focus).is-invalid,.input-group>.form-floating:not(:focus-within).is-invalid,.input-group>.form-select:not(:focus).is-invalid,.was-validated .input-group>.form-control:not(:focus):invalid,.was-validated .input-group>.form-floating:not(:focus-within):invalid,.was-validated .input-group>.form-select:not(:focus):invalid{z-index:4}.btn{--bs-btn-padding-x:0.75rem;--bs-btn-padding-y:0.375rem;--bs-btn-font-family: ;--bs-btn-font-size:1rem;--bs-btn-font-weight:400;--bs-btn-line-height:1.5;--bs-btn-color:var(--bs-body-color);--bs-btn-bg:transparent;--bs-btn-border-width:var(--bs-border-width);--bs-btn-border-color:transparent;--bs-btn-border-radius:0.375rem;--bs-btn-hover-border-color:transparent;--bs-btn-box-shadow:inset 0 1px 0 rgba(255, 255, 255, 0.15),0 1px 1px rgba(0, 0, 0, 0.075);--bs-btn-disabled-opacity:0.65;--bs-btn-focus-box-shadow:0 0 0 0.25rem rgba(var(--bs-btn-focus-shadow-rgb), .5);display:inline-block;padding:var(--bs-btn-padding-y) var(--bs-btn-padding-x);font-family:var(--bs-btn-font-family);font-size:var(--bs-btn-font-size);font-weight:var(--bs-btn-font-weight);line-height:var(--bs-btn-line-height);color:var(--bs-btn-color);text-align:center;text-decoration:none;vertical-align:middle;cursor:pointer;-webkit-user-select:none;-moz-user-select:none;user-select:none;border:var(--bs-btn-border-width) solid var(--bs-btn-border-color);border-radius:var(--bs-btn-border-radius);background-color:var(--bs-btn-bg);transition:color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out}@media (prefers-reduced-motion:reduce){.btn{transition:none}}.btn:hover{color:var(--bs-btn-hover-color);background-color:var(--bs-btn-hover-bg);border-color:var(--bs-btn-hover-border-color)}.btn-check+.btn:hover{color:var(--bs-btn-color);background-color:var(--bs-btn-bg);border-color:var(--bs-btn-border-color)}.btn:focus-visible{color:var(--bs-btn-hover-color);background-color:var(--bs-btn-hover-bg);border-color:var(--bs-btn-hover-border-color);outline:0;box-shadow:var(--bs-btn-focus-box-shadow)}.btn-check:focus-visible+.btn{border-color:var(--bs-btn-hover-border-color);outline:0;box-shadow:var(--bs-btn-focus-box-shadow)}.btn-check:checked+.btn,.btn.active,.btn.show,.btn:first-child:active,:not(.btn-check)+.btn:active{color:var(--bs-btn-active-color);background-color:var(--bs-btn-active-bg);border-color:var(--bs-btn-active-border-color)}.btn-check:checked+.btn:focus-visible,.btn.active:focus-visible,.btn.show:focus-visible,.btn:first-child:active:focus-visible,:not(.btn-check)+.btn:active:focus-visible{box-shadow:var(--bs-btn-focus-box-shadow)}.btn.disabled,.btn:disabled,fieldset:disabled .btn{color:var(--bs-btn-disabled-color);pointer-events:none;background-color:var(--bs-btn-disabled-bg);border-color:var(--bs-btn-disabled-border-color);opacity:var(--bs-btn-disabled-opacity)}.btn-primary{--bs-btn-color:#fff;--bs-btn-bg:#0d6efd;--bs-btn-border-color:#0d6efd;--bs-btn-hover-color:#fff;--bs-btn-hover-bg:#0b5ed7;--bs-btn-hover-border-color:#0a58ca;--bs-btn-focus-shadow-rgb:49,132,253;--bs-btn-active-color:#fff;--bs-btn-active-bg:#0a58ca;--bs-btn-active-border-color:#0a53be;--bs-btn-active-shadow:inset 0 3px 5px rgba(0, 0, 0, 0.125);--bs-btn-disabled-color:#fff;--bs-btn-disabled-bg:#0d6efd;--bs-btn-disabled-border-color:#0d6efd}.btn-secondary{--bs-btn-color:#fff;--bs-btn-bg:#6c757d;--bs-btn-border-color:#6c757d;--bs-btn-hover-color:#fff;--bs-btn-hover-bg:#5c636a;--bs-btn-hover-border-color:#565e64;--bs-btn-focus-shadow-rgb:130,138,145;--bs-btn-active-color:#fff;--bs-btn-active-bg:#565e64;--bs-btn-active-border-color:#51585e;--bs-btn-active-shadow:inset 0 3px 5px rgba(0, 0, 0, 0.125);--bs-btn-disabled-color:#fff;--bs-btn-disabled-bg:#6c757d;--bs-btn-disabled-border-color:#6c757d}.btn-success{--bs-btn-color:#fff;--bs-btn-bg:#198754;--bs-btn-border-color:#198754;--bs-btn-hover-color:#fff;--bs-btn-hover-bg:#157347;--bs-btn-hover-border-color:#146c43;--bs-btn-focus-shadow-rgb:60,153,110;--bs-btn-active-color:#fff;--bs-btn-active-bg:#146c43;--bs-btn-active-border-color:#13653f;--bs-btn-active-shadow:inset 0 3px 5px rgba(0, 0, 0, 0.125);--bs-btn-disabled-color:#fff;--bs-btn-disabled-bg:#198754;--bs-btn-disabled-border-color:#198754}.btn-info{--bs-btn-color:#000;--bs-btn-bg:#0dcaf0;--bs-btn-border-color:#0dcaf0;--bs-btn-hover-color:#000;--bs-btn-hover-bg:#31d2f2;--bs-btn-hover-border-color:#25cff2;--bs-btn-focus-shadow-rgb:11,172,204;--bs-btn-active-color:#000;--bs-btn-active-bg:#3dd5f3;--bs-btn-active-border-color:#25cff2;--bs-btn-active-shadow:inset 0 3px 5px rgba(0, 0, 0, 0.125);--bs-btn-disabled-color:#000;--bs-btn-disabled-bg:#0dcaf0;--bs-btn-disabled-border-color:#0dcaf0}.btn-warning{--bs-btn-color:#000;--bs-btn-bg:#ffc107;--bs-btn-border-color:#ffc107;--bs-btn-hover-color:#000;--bs-btn-hover-bg:#ffca2c;--bs-btn-hover-border-color:#ffc720;--bs-btn-focus-shadow-rgb:217,164,6;--bs-btn-active-color:#000;--bs-btn-active-bg:#ffcd39;--bs-btn-active-border-color:#ffc720;--bs-btn-active-shadow:inset 0 3px 5px rgba(0, 0, 0, 0.125);--bs-btn-disabled-color:#000;--bs-btn-disabled-bg:#ffc107;--bs-btn-disabled-border-color:#ffc107}.btn-danger{--bs-btn-color:#fff;--bs-btn-bg:#dc3545;--bs-btn-border-color:#dc3545;--bs-btn-hover-color:#fff;--bs-btn-hover-bg:#bb2d3b;--bs-btn-hover-border-color:#b02a37;--bs-btn-focus-shadow-rgb:225,83,97;--bs-btn-active-color:#fff;--bs-btn-active-bg:#b02a37;--bs-btn-active-border-color:#a52834;--bs-btn-active-shadow:inset 0 3px 5px rgba(0, 0, 0, 0.125);--bs-btn-disabled-color:#fff;--bs-btn-disabled-bg:#dc3545;--bs-btn-disabled-border-color:#dc3545}.btn-light{--bs-btn-color:#000;--bs-btn-bg:#f8f9fa;--bs-btn-border-color:#f8f9fa;--bs-btn-hover-color:#000;--bs-btn-hover-bg:#d3d4d5;--bs-btn-hover-border-color:#c6c7c8;--bs-btn-focus-shadow-rgb:211,212,213;--bs-btn-active-color:#000;--bs-btn-active-bg:#c6c7c8;--bs-btn-active-border-color:#babbbc;--bs-btn-active-shadow:inset 0 3px 5px rgba(0, 0, 0, 0.125);--bs-btn-disabled-color:#000;--bs-btn-disabled-bg:#f8f9fa;--bs-btn-disabled-border-color:#f8f9fa}.btn-dark{--bs-btn-color:#fff;--bs-btn-bg:#212529;--bs-btn-border-color:#212529;--bs-btn-hover-color:#fff;--bs-btn-hover-bg:#424649;--bs-btn-hover-border-color:#373b3e;--bs-btn-focus-shadow-rgb:66,70,73;--bs-btn-active-color:#fff;--bs-btn-active-bg:#4d5154;--bs-btn-active-border-color:#373b3e;--bs-btn-active-shadow:inset 0 3px 5px rgba(0, 0, 0, 0.125);--bs-btn-disabled-color:#fff;--bs-btn-disabled-bg:#212529;--bs-btn-disabled-border-color:#212529}.btn-outline-primary{--bs-btn-color:#0d6efd;--bs-btn-border-color:#0d6efd;--bs-btn-hover-color:#fff;--bs-btn-hover-bg:#0d6efd;--bs-btn-hover-border-color:#0d6efd;--bs-btn-focus-shadow-rgb:13,110,253;--bs-btn-active-color:#fff;--bs-btn-active-bg:#0d6efd;--bs-btn-active-border-color:#0d6efd;--bs-btn-active-shadow:inset 0 3px 5px rgba(0, 0, 0, 0.125);--bs-btn-disabled-color:#0d6efd;--bs-btn-disabled-bg:transparent;--bs-btn-disabled-border-color:#0d6efd;--bs-gradient:none}.btn-outline-secondary{--bs-btn-color:#6c757d;--bs-btn-border-color:#6c757d;--bs-btn-hover-color:#fff;--bs-btn-hover-bg:#6c757d;--bs-btn-hover-border-color:#6c757d;--bs-btn-focus-shadow-rgb:108,117,125;--bs-btn-active-color:#fff;--bs-btn-active-bg:#6c757d;--bs-btn-active-border-color:#6c757d;--bs-btn-active-shadow:inset 0 3px 5px rgba(0, 0, 0, 0.125);--bs-btn-disabled-color:#6c757d;--bs-btn-disabled-bg:transparent;--bs-btn-disabled-border-color:#6c757d;--bs-gradient:none}.btn-outline-success{--bs-btn-color:#198754;--bs-btn-border-color:#198754;--bs-btn-hover-color:#fff;--bs-btn-hover-bg:#198754;--bs-btn-hover-border-color:#198754;--bs-btn-focus-shadow-rgb:25,135,84;--bs-btn-active-color:#fff;--bs-btn-active-bg:#198754;--bs-btn-active-border-color:#198754;--bs-btn-active-shadow:inset 0 3px 5px rgba(0, 0, 0, 0.125);--bs-btn-disabled-color:#198754;--bs-btn-disabled-bg:transparent;--bs-btn-disabled-border-color:#198754;--bs-gradient:none}.btn-outline-info{--bs-btn-color:#0dcaf0;--bs-btn-border-color:#0dcaf0;--bs-btn-hover-color:#000;--bs-btn-hover-bg:#0dcaf0;--bs-btn-hover-border-color:#0dcaf0;--bs-btn-focus-shadow-rgb:13,202,240;--bs-btn-active-color:#000;--bs-btn-active-bg:#0dcaf0;--bs-btn-active-border-color:#0dcaf0;--bs-btn-active-shadow:inset 0 3px 5px rgba(0, 0, 0, 0.125);--bs-btn-disabled-color:#0dcaf0;--bs-btn-disabled-bg:transparent;--bs-btn-disabled-border-color:#0dcaf0;--bs-gradient:none}.btn-outline-warning{--bs-btn-color:#ffc107;--bs-btn-border-color:#ffc107;--bs-btn-hover-color:#000;--bs-btn-hover-bg:#ffc107;--bs-btn-hover-border-color:#ffc107;--bs-btn-focus-shadow-rgb:255,193,7;--bs-btn-active-color:#000;--bs-btn-active-bg:#ffc107;--bs-btn-active-border-color:#ffc107;--bs-btn-active-shadow:inset 0 3px 5px rgba(0, 0, 0, 0.125);--bs-btn-disabled-color:#ffc107;--bs-btn-disabled-bg:transparent;--bs-btn-disabled-border-color:#ffc107;--bs-gradient:none}.btn-outline-danger{--bs-btn-color:#dc3545;--bs-btn-border-color:#dc3545;--bs-btn-hover-color:#fff;--bs-btn-hover-bg:#dc3545;--bs-btn-hover-border-color:#dc3545;--bs-btn-focus-shadow-rgb:220,53,69;--bs-btn-active-color:#fff;--bs-btn-active-bg:#dc3545;--bs-btn-active-border-color:#dc3545;--bs-btn-active-shadow:inset 0 3px 5px rgba(0, 0, 0, 0.125);--bs-btn-disabled-color:#dc3545;--bs-btn-disabled-bg:transparent;--bs-btn-disabled-border-color:#dc3545;--bs-gradient:none}.btn-outline-light{--bs-btn-color:#f8f9fa;--bs-btn-border-color:#f8f9fa;--bs-btn-hover-color:#000;--bs-btn-hover-bg:#f8f9fa;--bs-btn-hover-border-color:#f8f9fa;--bs-btn-focus-shadow-rgb:248,249,250;--bs-btn-active-color:#000;--bs-btn-active-bg:#f8f9fa;--bs-btn-active-border-color:#f8f9fa;--bs-btn-active-shadow:inset 0 3px 5px rgba(0, 0, 0, 0.125);--bs-btn-disabled-color:#f8f9fa;--bs-btn-disabled-bg:transparent;--bs-btn-disabled-border-color:#f8f9fa;--bs-gradient:none}.btn-outline-dark{--bs-btn-color:#212529;--bs-btn-border-color:#212529;--bs-btn-hover-color:#fff;--bs-btn-hover-bg:#212529;--bs-btn-hover-border-color:#212529;--bs-btn-focus-shadow-rgb:33,37,41;--bs-btn-active-color:#fff;--bs-btn-active-bg:#212529;--bs-btn-active-border-color:#212529;--bs-btn-active-shadow:inset 0 3px 5px rgba(0, 0, 0, 0.125);--bs-btn-disabled-color:#212529;--bs-btn-disabled-bg:transparent;--bs-btn-disabled-border-color:#212529;--bs-gradient:none}.btn-link{--bs-btn-font-weight:400;--bs-btn-color:var(--bs-link-color);--bs-btn-bg:transparent;--bs-btn-border-color:transparent;--bs-btn-hover-color:var(--bs-link-hover-color);--bs-btn-hover-border-color:transparent;--bs-btn-active-color:var(--bs-link-hover-color);--bs-btn-active-border-color:transparent;--bs-btn-disabled-color:#6c757d;--bs-btn-disabled-border-color:transparent;--bs-btn-box-shadow:0 0 0 #000;--bs-btn-focus-shadow-rgb:49,132,253;text-decoration:underline}.btn-link:focus-visible{color:var(--bs-btn-color)}.btn-link:hover{color:var(--bs-btn-hover-color)}.btn-group-lg>.btn,.btn-lg{--bs-btn-padding-y:0.5rem;--bs-btn-padding-x:1rem;--bs-btn-font-size:1.25rem;--bs-btn-border-radius:0.5rem}.btn-group-sm>.btn,.btn-sm{--bs-btn-padding-y:0.25rem;--bs-btn-padding-x:0.5rem;--bs-btn-font-size:0.875rem;--bs-btn-border-radius:0.25rem}.fade{transition:opacity .15s linear}@media (prefers-reduced-motion:reduce){.fade{transition:none}}.fade:not(.show){opacity:0}.collapse:not(.show){display:none}.collapsing{height:0;overflow:hidden;transition:height .35s ease}@media (prefers-reduced-motion:reduce){.collapsing{transition:none}}.collapsing.collapse-horizontal{width:0;height:auto;transition:width .35s ease}@media (prefers-reduced-motion:reduce){.collapsing.collapse-horizontal{transition:none}}.dropdown,.dropdown-center,.dropend,.dropstart,.dropup,.dropup-center{position:relative}.dropdown-toggle{white-space:nowrap}.dropdown-toggle::after{display:inline-block;margin-left:.255em;vertical-align:.255em;content:"";border-top:.3em solid;border-right:.3em solid transparent;border-bottom:0;border-left:.3em solid transparent}.dropdown-toggle:empty::after{margin-left:0}.dropdown-menu{--bs-dropdown-zindex:1000;--bs-dropdown-min-width:10rem;--bs-dropdown-padding-x:0;--bs-dropdown-padding-y:0.5rem;--bs-dropdown-spacer:0.125rem;--bs-dropdown-font-size:1rem;--bs-dropdown-color:var(--bs-body-color);--bs-dropdown-bg:var(--bs-body-bg);--bs-dropdown-border-color:var(--bs-border-color-translucent);--bs-dropdown-border-radius:0.375rem;--bs-dropdown-border-width:var(--bs-border-width);--bs-dropdown-inner-border-radius:calc(0.375rem - var(--bs-border-width));--bs-dropdown-divider-bg:var(--bs-border-color-translucent);--bs-dropdown-divider-margin-y:0.5rem;--bs-dropdown-box-shadow:0 0.5rem 1rem rgba(0, 0, 0, 0.15);--bs-dropdown-link-color:var(--bs-body-color);--bs-dropdown-link-hover-color:var(--bs-body-color);--bs-dropdown-link-hover-bg:var(--bs-tertiary-bg);--bs-dropdown-link-active-color:#fff;--bs-dropdown-link-active-bg:#0d6efd;--bs-dropdown-link-disabled-color:var(--bs-tertiary-color);--bs-dropdown-item-padding-x:1rem;--bs-dropdown-item-padding-y:0.25rem;--bs-dropdown-header-color:#6c757d;--bs-dropdown-header-padding-x:1rem;--bs-dropdown-header-padding-y:0.5rem;position:absolute;z-index:var(--bs-dropdown-zindex);display:none;min-width:var(--bs-dropdown-min-width);padding:var(--bs-dropdown-padding-y) var(--bs-dropdown-padding-x);margin:0;font-size:var(--bs-dropdown-font-size);color:var(--bs-dropdown-color);text-align:left;list-style:none;background-color:var(--bs-dropdown-bg);background-clip:padding-box;border:var(--bs-dropdown-border-width) solid var(--bs-dropdown-border-color);border-radius:var(--bs-dropdown-border-radius)}.dropdown-menu[data-bs-popper]{top:100%;left:0;margin-top:var(--bs-dropdown-spacer)}.dropdown-menu-start{--bs-position:start}.dropdown-menu-start[data-bs-popper]{right:auto;left:0}.dropdown-menu-end{--bs-position:end}.dropdown-menu-end[data-bs-popper]{right:0;left:auto}@media (min-width:576px){.dropdown-menu-sm-start{--bs-position:start}.dropdown-menu-sm-start[data-bs-popper]{right:auto;left:0}.dropdown-menu-sm-end{--bs-position:end}.dropdown-menu-sm-end[data-bs-popper]{right:0;left:auto}}@media (min-width:768px){.dropdown-menu-md-start{--bs-position:start}.dropdown-menu-md-start[data-bs-popper]{right:auto;left:0}.dropdown-menu-md-end{--bs-position:end}.dropdown-menu-md-end[data-bs-popper]{right:0;left:auto}}@media (min-width:992px){.dropdown-menu-lg-start{--bs-position:start}.dropdown-menu-lg-start[data-bs-popper]{right:auto;left:0}.dropdown-menu-lg-end{--bs-position:end}.dropdown-menu-lg-end[data-bs-popper]{right:0;left:auto}}@media (min-width:1200px){.dropdown-menu-xl-start{--bs-position:start}.dropdown-menu-xl-start[data-bs-popper]{right:auto;left:0}.dropdown-menu-xl-end{--bs-position:end}.dropdown-menu-xl-end[data-bs-popper]{right:0;left:auto}}@media (min-width:1400px){.dropdown-menu-xxl-start{--bs-position:start}.dropdown-menu-xxl-start[data-bs-popper]{right:auto;left:0}.dropdown-menu-xxl-end{--bs-position:end}.dropdown-menu-xxl-end[data-bs-popper]{right:0;left:auto}}.dropup .dropdown-menu[data-bs-popper]{top:auto;bottom:100%;margin-top:0;margin-bottom:var(--bs-dropdown-spacer)}.dropup .dropdown-toggle::after{display:inline-block;margin-left:.255em;vertical-align:.255em;content:"";border-top:0;border-right:.3em solid transparent;border-bottom:.3em solid;border-left:.3em solid transparent}.dropup .dropdown-toggle:empty::after{margin-left:0}.dropend .dropdown-menu[data-bs-popper]{top:0;right:auto;left:100%;margin-top:0;margin-left:var(--bs-dropdown-spacer)}.dropend .dropdown-toggle::after{display:inline-block;margin-left:.255em;vertical-align:.255em;content:"";border-top:.3em solid transparent;border-right:0;border-bottom:.3em solid transparent;border-left:.3em solid}.dropend .dropdown-toggle:empty::after{margin-left:0}.dropend .dropdown-toggle::after{vertical-align:0}.dropstart .dropdown-menu[data-bs-popper]{top:0;right:100%;left:auto;margin-top:0;margin-right:var(--bs-dropdown-spacer)}.dropstart .dropdown-toggle::after{display:inline-block;margin-left:.255em;vertical-align:.255em;content:""}.dropstart .dropdown-toggle::after{display:none}.dropstart .dropdown-toggle::before{display:inline-block;margin-right:.255em;vertical-align:.255em;content:"";border-top:.3em solid transparent;border-right:.3em solid;border-bottom:.3em solid transparent}.dropstart .dropdown-toggle:empty::after{margin-left:0}.dropstart .dropdown-toggle::before{vertical-align:0}.dropdown-divider{height:0;margin:var(--bs-dropdown-divider-margin-y) 0;overflow:hidden;border-top:1px solid var(--bs-dropdown-divider-bg);opacity:1}.dropdown-item{display:block;width:100%;padding:var(--bs-dropdown-item-padding-y) var(--bs-dropdown-item-padding-x);clear:both;font-weight:400;color:var(--bs-dropdown-link-color);text-align:inherit;text-decoration:none;white-space:nowrap;background-color:transparent;border:0;border-radius:var(--bs-dropdown-item-border-radius,0)}.dropdown-item:focus,.dropdown-item:hover{color:var(--bs-dropdown-link-hover-color);background-color:var(--bs-dropdown-link-hover-bg)}.dropdown-item.active,.dropdown-item:active{color:var(--bs-dropdown-link-active-color);text-decoration:none;background-color:var(--bs-dropdown-link-active-bg)}.dropdown-item.disabled,.dropdown-item:disabled{color:var(--bs-dropdown-link-disabled-color);pointer-events:none;background-color:transparent}.dropdown-menu.show{display:block}.dropdown-header{display:block;padding:var(--bs-dropdown-header-padding-y) var(--bs-dropdown-header-padding-x);margin-bottom:0;font-size:.875rem;color:var(--bs-dropdown-header-color);white-space:nowrap}.dropdown-item-text{display:block;padding:var(--bs-dropdown-item-padding-y) var(--bs-dropdown-item-padding-x);color:var(--bs-dropdown-link-color)}.dropdown-menu-dark{--bs-dropdown-color:#dee2e6;--bs-dropdown-bg:#343a40;--bs-dropdown-border-color:var(--bs-border-color-translucent);--bs-dropdown-box-shadow: ;--bs-dropdown-link-color:#dee2e6;--bs-dropdown-link-hover-color:#fff;--bs-dropdown-divider-bg:var(--bs-border-color-translucent);--bs-dropdown-link-hover-bg:rgba(255, 255, 255, 0.15);--bs-dropdown-link-active-color:#fff;--bs-dropdown-link-active-bg:#0d6efd;--bs-dropdown-link-disabled-color:#adb5bd;--bs-dropdown-header-color:#adb5bd}.btn-group,.btn-group-vertical{position:relative;display:inline-flex;vertical-align:middle}.btn-group-vertical>.btn,.btn-group>.btn{position:relative;flex:1 1 auto}.btn-group-vertical>.btn-check:checked+.btn,.btn-group-vertical>.btn-check:focus+.btn,.btn-group-vertical>.btn.active,.btn-group-vertical>.btn:active,.btn-group-vertical>.btn:focus,.btn-group-vertical>.btn:hover,.btn-group>.btn-check:checked+.btn,.btn-group>.btn-check:focus+.btn,.btn-group>.btn.active,.btn-group>.btn:active,.btn-group>.btn:focus,.btn-group>.btn:hover{z-index:1}.btn-toolbar{display:flex;flex-wrap:wrap;justify-content:flex-start}.btn-toolbar .input-group{width:auto}.btn-group{border-radius:.375rem}.btn-group>.btn-group:not(:first-child),.btn-group>:not(.btn-check:first-child)+.btn{margin-left:calc(var(--bs-border-width) * -1)}.btn-group>.btn-group:not(:last-child)>.btn,.btn-group>.btn.dropdown-toggle-split:first-child,.btn-group>.btn:not(:last-child):not(.dropdown-toggle){border-top-right-radius:0;border-bottom-right-radius:0}.btn-group>.btn-group:not(:first-child)>.btn,.btn-group>.btn:nth-child(n+3),.btn-group>:not(.btn-check)+.btn{border-top-left-radius:0;border-bottom-left-radius:0}.dropdown-toggle-split{padding-right:.5625rem;padding-left:.5625rem}.dropdown-toggle-split::after,.dropend .dropdown-toggle-split::after,.dropup .dropdown-toggle-split::after{margin-left:0}.dropstart .dropdown-toggle-split::before{margin-right:0}.btn-group-sm>.btn+.dropdown-toggle-split,.btn-sm+.dropdown-toggle-split{padding-right:.375rem;padding-left:.375rem}.btn-group-lg>.btn+.dropdown-toggle-split,.btn-lg+.dropdown-toggle-split{padding-right:.75rem;padding-left:.75rem}.btn-group-vertical{flex-direction:column;align-items:flex-start;justify-content:center}.btn-group-vertical>.btn,.btn-group-vertical>.btn-group{width:100%}.btn-group-vertical>.btn-group:not(:first-child),.btn-group-vertical>.btn:not(:first-child){margin-top:calc(var(--bs-border-width) * -1)}.btn-group-vertical>.btn-group:not(:last-child)>.btn,.btn-group-vertical>.btn:not(:last-child):not(.dropdown-toggle){border-bottom-right-radius:0;border-bottom-left-radius:0}.btn-group-vertical>.btn-group:not(:first-child)>.btn,.btn-group-vertical>.btn~.btn{border-top-left-radius:0;border-top-right-radius:0}.nav{--bs-nav-link-padding-x:1rem;--bs-nav-link-padding-y:0.5rem;--bs-nav-link-font-weight: ;--bs-nav-link-color:var(--bs-link-color);--bs-nav-link-hover-color:var(--bs-link-hover-color);--bs-nav-link-disabled-color:var(--bs-secondary-color);display:flex;flex-wrap:wrap;padding-left:0;margin-bottom:0;list-style:none}.nav-link{display:block;padding:var(--bs-nav-link-padding-y) var(--bs-nav-link-padding-x);font-size:var(--bs-nav-link-font-size);font-weight:var(--bs-nav-link-font-weight);color:var(--bs-nav-link-color);text-decoration:none;background:0 0;border:0;transition:color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out}@media (prefers-reduced-motion:reduce){.nav-link{transition:none}}.nav-link:focus,.nav-link:hover{color:var(--bs-nav-link-hover-color)}.nav-link:focus-visible{outline:0;box-shadow:0 0 0 .25rem rgba(13,110,253,.25)}.nav-link.disabled{color:var(--bs-nav-link-disabled-color);pointer-events:none;cursor:default}.nav-tabs{--bs-nav-tabs-border-width:var(--bs-border-width);--bs-nav-tabs-border-color:var(--bs-border-color);--bs-nav-tabs-border-radius:var(--bs-border-radius);--bs-nav-tabs-link-hover-border-color:var(--bs-secondary-bg) var(--bs-secondary-bg) var(--bs-border-color);--bs-nav-tabs-link-active-color:var(--bs-emphasis-color);--bs-nav-tabs-link-active-bg:var(--bs-body-bg);--bs-nav-tabs-link-active-border-color:var(--bs-border-color) var(--bs-border-color) var(--bs-body-bg);border-bottom:var(--bs-nav-tabs-border-width) solid var(--bs-nav-tabs-border-color)}.nav-tabs .nav-link{margin-bottom:calc(-1 * var(--bs-nav-tabs-border-width));border:var(--bs-nav-tabs-border-width) solid transparent;border-top-left-radius:var(--bs-nav-tabs-border-radius);border-top-right-radius:var(--bs-nav-tabs-border-radius)}.nav-tabs .nav-link:focus,.nav-tabs .nav-link:hover{isolation:isolate;border-color:var(--bs-nav-tabs-link-hover-border-color)}.nav-tabs .nav-link.disabled,.nav-tabs .nav-link:disabled{color:var(--bs-nav-link-disabled-color);background-color:transparent;border-color:transparent}.nav-tabs .nav-item.show .nav-link,.nav-tabs .nav-link.active{color:var(--bs-nav-tabs-link-active-color);background-color:var(--bs-nav-tabs-link-active-bg);border-color:var(--bs-nav-tabs-link-active-border-color)}.nav-tabs .dropdown-menu{margin-top:calc(-1 * var(--bs-nav-tabs-border-width));border-top-left-radius:0;border-top-right-radius:0}.nav-pills{--bs-nav-pills-border-radius:0.375rem;--bs-nav-pills-link-active-color:#fff;--bs-nav-pills-link-active-bg:#0d6efd}.nav-pills .nav-link{border-radius:var(--bs-nav-pills-border-radius)}.nav-pills .nav-link:disabled{color:var(--bs-nav-link-disabled-color);background-color:transparent;border-color:transparent}.nav-pills .nav-link.active,.nav-pills .show>.nav-link{color:var(--bs-nav-pills-link-active-color);background-color:var(--bs-nav-pills-link-active-bg)}.nav-underline{--bs-nav-underline-gap:1rem;--bs-nav-underline-border-width:0.125rem;--bs-nav-underline-link-active-color:var(--bs-emphasis-color);gap:var(--bs-nav-underline-gap)}.nav-underline .nav-link{padding-right:0;padding-left:0;border-bottom:var(--bs-nav-underline-border-width) solid transparent}.nav-underline .nav-link:focus,.nav-underline .nav-link:hover{border-bottom-color:currentcolor}.nav-underline .nav-link.active,.nav-underline .show>.nav-link{font-weight:700;color:var(--bs-nav-underline-link-active-color);border-bottom-color:currentcolor}.nav-fill .nav-item,.nav-fill>.nav-link{flex:1 1 auto;text-align:center}.nav-justified .nav-item,.nav-justified>.nav-link{flex-basis:0;flex-grow:1;text-align:center}.nav-fill .nav-item .nav-link,.nav-justified .nav-item .nav-link{width:100%}.tab-content>.tab-pane{display:none}.tab-content>.active{display:block}.navbar{--bs-navbar-padding-x:0;--bs-navbar-padding-y:0.5rem;--bs-navbar-color:rgba(var(--bs-emphasis-color-rgb), 0.65);--bs-navbar-hover-color:rgba(var(--bs-emphasis-color-rgb), 0.8);--bs-navbar-disabled-color:rgba(var(--bs-emphasis-color-rgb), 0.3);--bs-navbar-active-color:rgba(var(--bs-emphasis-color-rgb), 1);--bs-navbar-brand-padding-y:0.3125rem;--bs-navbar-brand-margin-end:1rem;--bs-navbar-brand-font-size:1.25rem;--bs-navbar-brand-color:rgba(var(--bs-emphasis-color-rgb), 1);--bs-navbar-brand-hover-color:rgba(var(--bs-emphasis-color-rgb), 1);--bs-navbar-nav-link-padding-x:0.5rem;--bs-navbar-toggler-padding-y:0.25rem;--bs-navbar-toggler-padding-x:0.75rem;--bs-navbar-toggler-font-size:1.25rem;--bs-navbar-toggler-icon-bg:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 30 30'%3e%3cpath stroke='rgba%2833, 37, 41, 0.75%29' stroke-linecap='round' stroke-miterlimit='10' stroke-width='2' d='M4 7h22M4 15h22M4 23h22'/%3e%3c/svg%3e");--bs-navbar-toggler-border-color:rgba(var(--bs-emphasis-color-rgb), 0.15);--bs-navbar-toggler-border-radius:0.375rem;--bs-navbar-toggler-focus-width:0.25rem;--bs-navbar-toggler-transition:box-shadow 0.15s ease-in-out;position:relative;display:flex;flex-wrap:wrap;align-items:center;justify-content:space-between;padding:var(--bs-navbar-padding-y) var(--bs-navbar-padding-x)}.navbar>.container,.navbar>.container-fluid,.navbar>.container-lg,.navbar>.container-md,.navbar>.container-sm,.navbar>.container-xl,.navbar>.container-xxl{display:flex;flex-wrap:inherit;align-items:center;justify-content:space-between}.navbar-brand{padding-top:var(--bs-navbar-brand-padding-y);padding-bottom:var(--bs-navbar-brand-padding-y);margin-right:var(--bs-navbar-brand-margin-end);font-size:var(--bs-navbar-brand-font-size);color:var(--bs-navbar-brand-color);text-decoration:none;white-space:nowrap}.navbar-brand:focus,.navbar-brand:hover{color:var(--bs-navbar-brand-hover-color)}.navbar-nav{--bs-nav-link-padding-x:0;--bs-nav-link-padding-y:0.5rem;--bs-nav-link-font-weight: ;--bs-nav-link-color:var(--bs-navbar-color);--bs-nav-link-hover-color:var(--bs-navbar-hover-color);--bs-nav-link-disabled-color:var(--bs-navbar-disabled-color);display:flex;flex-direction:column;padding-left:0;margin-bottom:0;list-style:none}.navbar-nav .nav-link.active,.navbar-nav .nav-link.show{color:var(--bs-navbar-active-color)}.navbar-nav .dropdown-menu{position:static}.navbar-text{padding-top:.5rem;padding-bottom:.5rem;color:var(--bs-navbar-color)}.navbar-text a,.navbar-text a:focus,.navbar-text a:hover{color:var(--bs-navbar-active-color)}.navbar-collapse{flex-basis:100%;flex-grow:1;align-items:center}.navbar-toggler{padding:var(--bs-navbar-toggler-padding-y) var(--bs-navbar-toggler-padding-x);font-size:var(--bs-navbar-toggler-font-size);line-height:1;color:var(--bs-navbar-color);background-color:transparent;border:var(--bs-border-width) solid var(--bs-navbar-toggler-border-color);border-radius:var(--bs-navbar-toggler-border-radius);transition:var(--bs-navbar-toggler-transition)}@media (prefers-reduced-motion:reduce){.navbar-toggler{transition:none}}.navbar-toggler:hover{text-decoration:none}.navbar-toggler:focus{text-decoration:none;outline:0;box-shadow:0 0 0 var(--bs-navbar-toggler-focus-width)}.navbar-toggler-icon{display:inline-block;width:1.5em;height:1.5em;vertical-align:middle;background-image:var(--bs-navbar-toggler-icon-bg);background-repeat:no-repeat;background-position:center;background-size:100%}.navbar-nav-scroll{max-height:var(--bs-scroll-height,75vh);overflow-y:auto}@media (min-width:576px){.navbar-expand-sm{flex-wrap:nowrap;justify-content:flex-start}.navbar-expand-sm .navbar-nav{flex-direction:row}.navbar-expand-sm .navbar-nav .dropdown-menu{position:absolute}.navbar-expand-sm .navbar-nav .nav-link{padding-right:var(--bs-navbar-nav-link-padding-x);padding-left:var(--bs-navbar-nav-link-padding-x)}.navbar-expand-sm .navbar-nav-scroll{overflow:visible}.navbar-expand-sm .navbar-collapse{display:flex!important;flex-basis:auto}.navbar-expand-sm .navbar-toggler{display:none}.navbar-expand-sm .offcanvas{position:static;z-index:auto;flex-grow:1;width:auto!important;height:auto!important;visibility:visible!important;background-color:transparent!important;border:0!important;transform:none!important;transition:none}.navbar-expand-sm .offcanvas .offcanvas-header{display:none}.navbar-expand-sm .offcanvas .offcanvas-body{display:flex;flex-grow:0;padding:0;overflow-y:visible}}@media (min-width:768px){.navbar-expand-md{flex-wrap:nowrap;justify-content:flex-start}.navbar-expand-md .navbar-nav{flex-direction:row}.navbar-expand-md .navbar-nav .dropdown-menu{position:absolute}.navbar-expand-md .navbar-nav .nav-link{padding-right:var(--bs-navbar-nav-link-padding-x);padding-left:var(--bs-navbar-nav-link-padding-x)}.navbar-expand-md .navbar-nav-scroll{overflow:visible}.navbar-expand-md .navbar-collapse{display:flex!important;flex-basis:auto}.navbar-expand-md .navbar-toggler{display:none}.navbar-expand-md .offcanvas{position:static;z-index:auto;flex-grow:1;width:auto!important;height:auto!important;visibility:visible!important;background-color:transparent!important;border:0!important;transform:none!important;transition:none}.navbar-expand-md .offcanvas .offcanvas-header{display:none}.navbar-expand-md .offcanvas .offcanvas-body{display:flex;flex-grow:0;padding:0;overflow-y:visible}}@media (min-width:992px){.navbar-expand-lg{flex-wrap:nowrap;justify-content:flex-start}.navbar-expand-lg .navbar-nav{flex-direction:row}.navbar-expand-lg .navbar-nav .dropdown-menu{position:absolute}.navbar-expand-lg .navbar-nav .nav-link{padding-right:var(--bs-navbar-nav-link-padding-x);padding-left:var(--bs-navbar-nav-link-padding-x)}.navbar-expand-lg .navbar-nav-scroll{overflow:visible}.navbar-expand-lg .navbar-collapse{display:flex!important;flex-basis:auto}.navbar-expand-lg .navbar-toggler{display:none}.navbar-expand-lg .offcanvas{position:static;z-index:auto;flex-grow:1;width:auto!important;height:auto!important;visibility:visible!important;background-color:transparent!important;border:0!important;transform:none!important;transition:none}.navbar-expand-lg .offcanvas .offcanvas-header{display:none}.navbar-expand-lg .offcanvas .offcanvas-body{display:flex;flex-grow:0;padding:0;overflow-y:visible}}@media (min-width:1200px){.navbar-expand-xl{flex-wrap:nowrap;justify-content:flex-start}.navbar-expand-xl .navbar-nav{flex-direction:row}.navbar-expand-xl .navbar-nav .dropdown-menu{position:absolute}.navbar-expand-xl .navbar-nav .nav-link{padding-right:var(--bs-navbar-nav-link-padding-x);padding-left:var(--bs-navbar-nav-link-padding-x)}.navbar-expand-xl .navbar-nav-scroll{overflow:visible}.navbar-expand-xl .navbar-collapse{display:flex!important;flex-basis:auto}.navbar-expand-xl .navbar-toggler{display:none}.navbar-expand-xl .offcanvas{position:static;z-index:auto;flex-grow:1;width:auto!important;height:auto!important;visibility:visible!important;background-color:transparent!important;border:0!important;transform:none!important;transition:none}.navbar-expand-xl .offcanvas .offcanvas-header{display:none}.navbar-expand-xl .offcanvas .offcanvas-body{display:flex;flex-grow:0;padding:0;overflow-y:visible}}@media (min-width:1400px){.navbar-expand-xxl{flex-wrap:nowrap;justify-content:flex-start}.navbar-expand-xxl .navbar-nav{flex-direction:row}.navbar-expand-xxl .navbar-nav .dropdown-menu{position:absolute}.navbar-expand-xxl .navbar-nav .nav-link{padding-right:var(--bs-navbar-nav-link-padding-x);padding-left:var(--bs-navbar-nav-link-padding-x)}.navbar-expand-xxl .navbar-nav-scroll{overflow:visible}.navbar-expand-xxl .navbar-collapse{display:flex!important;flex-basis:auto}.navbar-expand-xxl .navbar-toggler{display:none}.navbar-expand-xxl .offcanvas{position:static;z-index:auto;flex-grow:1;width:auto!important;height:auto!important;visibility:visible!important;background-color:transparent!important;border:0!important;transform:none!important;transition:none}.navbar-expand-xxl .offcanvas .offcanvas-header{display:none}.navbar-expand-xxl .offcanvas .offcanvas-body{display:flex;flex-grow:0;padding:0;overflow-y:visible}}.navbar-expand{flex-wrap:nowrap;justify-content:flex-start}.navbar-expand .navbar-nav{flex-direction:row}.navbar-expand .navbar-nav .dropdown-menu{position:absolute}.navbar-expand .navbar-nav .nav-link{padding-right:var(--bs-navbar-nav-link-padding-x);padding-left:var(--bs-navbar-nav-link-padding-x)}.navbar-expand .navbar-nav-scroll{overflow:visible}.navbar-expand .navbar-collapse{display:flex!important;flex-basis:auto}.navbar-expand .navbar-toggler{display:none}.navbar-expand .offcanvas{position:static;z-index:auto;flex-grow:1;width:auto!important;height:auto!important;visibility:visible!important;background-color:transparent!important;border:0!important;transform:none!important;transition:none}.navbar-expand .offcanvas .offcanvas-header{display:none}.navbar-expand .offcanvas .offcanvas-body{display:flex;flex-grow:0;padding:0;overflow-y:visible}.navbar-dark{--bs-navbar-color:rgba(255, 255, 255, 0.55);--bs-navbar-hover-color:rgba(255, 255, 255, 0.75);--bs-navbar-disabled-color:rgba(255, 255, 255, 0.25);--bs-navbar-active-color:#fff;--bs-navbar-brand-color:#fff;--bs-navbar-brand-hover-color:#fff;--bs-navbar-toggler-border-color:rgba(255, 255, 255, 0.1);--bs-navbar-toggler-icon-bg:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 30 30'%3e%3cpath stroke='rgba%28255, 255, 255, 0.55%29' stroke-linecap='round' stroke-miterlimit='10' stroke-width='2' d='M4 7h22M4 15h22M4 23h22'/%3e%3c/svg%3e")}[data-bs-theme=dark] .navbar-toggler-icon{--bs-navbar-toggler-icon-bg:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 30 30'%3e%3cpath stroke='rgba%28255, 255, 255, 0.55%29' stroke-linecap='round' stroke-miterlimit='10' stroke-width='2' d='M4 7h22M4 15h22M4 23h22'/%3e%3c/svg%3e")}.card{--bs-card-spacer-y:1rem;--bs-card-spacer-x:1rem;--bs-card-title-spacer-y:0.5rem;--bs-card-title-color: ;--bs-card-subtitle-color: ;--bs-card-border-width:var(--bs-border-width);--bs-card-border-color:var(--bs-border-color-translucent);--bs-card-border-radius:var(--bs-border-radius);--bs-card-box-shadow: ;--bs-card-inner-border-radius:calc(var(--bs-border-radius) - (var(--bs-border-width)));--bs-card-cap-padding-y:0.5rem;--bs-card-cap-padding-x:1rem;--bs-card-cap-bg:rgba(var(--bs-body-color-rgb), 0.03);--bs-card-cap-color: ;--bs-card-height: ;--bs-card-color: ;--bs-card-bg:var(--bs-body-bg);--bs-card-img-overlay-padding:1rem;--bs-card-group-margin:0.75rem;position:relative;display:flex;flex-direction:column;min-width:0;height:var(--bs-card-height);color:var(--bs-body-color);word-wrap:break-word;background-color:var(--bs-card-bg);background-clip:border-box;border:var(--bs-card-border-width) solid var(--bs-card-border-color);border-radius:var(--bs-card-border-radius)}.card>hr{margin-right:0;margin-left:0}.card>.list-group{border-top:inherit;border-bottom:inherit}.card>.list-group:first-child{border-top-width:0;border-top-left-radius:var(--bs-card-inner-border-radius);border-top-right-radius:var(--bs-card-inner-border-radius)}.card>.list-group:last-child{border-bottom-width:0;border-bottom-right-radius:var(--bs-card-inner-border-radius);border-bottom-left-radius:var(--bs-card-inner-border-radius)}.card>.card-header+.list-group,.card>.list-group+.card-footer{border-top:0}.card-body{flex:1 1 auto;padding:var(--bs-card-spacer-y) var(--bs-card-spacer-x);color:var(--bs-card-color)}.card-title{margin-bottom:var(--bs-card-title-spacer-y);color:var(--bs-card-title-color)}.card-subtitle{margin-top:calc(-.5 * var(--bs-card-title-spacer-y));margin-bottom:0;color:var(--bs-card-subtitle-color)}.card-text:last-child{margin-bottom:0}.card-link+.card-link{margin-left:var(--bs-card-spacer-x)}.card-header{padding:var(--bs-card-cap-padding-y) var(--bs-card-cap-padding-x);margin-bottom:0;color:var(--bs-card-cap-color);background-color:var(--bs-card-cap-bg);border-bottom:var(--bs-card-border-width) solid var(--bs-card-border-color)}.card-header:first-child{border-radius:var(--bs-card-inner-border-radius) var(--bs-card-inner-border-radius) 0 0}.card-footer{padding:var(--bs-card-cap-padding-y) var(--bs-card-cap-padding-x);color:var(--bs-card-cap-color);background-color:var(--bs-card-cap-bg);border-top:var(--bs-card-border-width) solid var(--bs-card-border-color)}.card-footer:last-child{border-radius:0 0 var(--bs-card-inner-border-radius) var(--bs-card-inner-border-radius)}.card-header-tabs{margin-right:calc(-.5 * var(--bs-card-cap-padding-x));margin-bottom:calc(-1 * var(--bs-card-cap-padding-y));margin-left:calc(-.5 * var(--bs-card-cap-padding-x));border-bottom:0}.card-header-tabs .nav-link.active{background-color:var(--bs-card-bg);border-bottom-color:var(--bs-card-bg)}.card-header-pills{margin-right:calc(-.5 * var(--bs-card-cap-padding-x));margin-left:calc(-.5 * var(--bs-card-cap-padding-x))}.card-img-overlay{position:absolute;top:0;right:0;bottom:0;left:0;padding:var(--bs-card-img-overlay-padding);border-radius:var(--bs-card-inner-border-radius)}.card-img,.card-img-bottom,.card-img-top{width:100%}.card-img,.card-img-top{border-top-left-radius:var(--bs-card-inner-border-radius);border-top-right-radius:var(--bs-card-inner-border-radius)}.card-img,.card-img-bottom{border-bottom-right-radius:var(--bs-card-inner-border-radius);border-bottom-left-radius:var(--bs-card-inner-border-radius)}.card-group>.card{margin-bottom:var(--bs-card-group-margin)}@media (min-width:576px){.card-group{display:flex;flex-flow:row wrap}.card-group>.card{flex:1 0 0%;margin-bottom:0}.card-group>.card+.card{margin-left:0;border-left:0}.card-group>.card:not(:last-child){border-top-right-radius:0;border-bottom-right-radius:0}.card-group>.card:not(:last-child) .card-header,.card-group>.card:not(:last-child) .card-img-top{border-top-right-radius:0}.card-group>.card:not(:last-child) .card-footer,.card-group>.card:not(:last-child) .card-img-bottom{border-bottom-right-radius:0}.card-group>.card:not(:first-child){border-top-left-radius:0;border-bottom-left-radius:0}.card-group>.card:not(:first-child) .card-header,.card-group>.card:not(:first-child) .card-img-top{border-top-left-radius:0}.card-group>.card:not(:first-child) .card-footer,.card-group>.card:not(:first-child) .card-img-bottom{border-bottom-left-radius:0}}.accordion{--bs-accordion-color:var(--bs-body-color);--bs-accordion-bg:var(--bs-body-bg);--bs-accordion-transition:color 0.15s ease-in-out,background-color 0.15s ease-in-out,border-color 0.15s ease-in-out,box-shadow 0.15s ease-in-out,border-radius 0.15s ease;--bs-accordion-border-color:var(--bs-border-color);--bs-accordion-border-width:var(--bs-border-width);--bs-accordion-border-radius:var(--bs-border-radius);--bs-accordion-inner-border-radius:calc(var(--bs-border-radius) - (var(--bs-border-width)));--bs-accordion-btn-padding-x:1.25rem;--bs-accordion-btn-padding-y:1rem;--bs-accordion-btn-color:var(--bs-body-color);--bs-accordion-btn-bg:var(--bs-accordion-bg);--bs-accordion-btn-icon:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%23212529'%3e%3cpath fill-rule='evenodd' d='M1.646 4.646a.5.5 0 0 1 .708 0L8 10.293l5.646-5.647a.5.5 0 0 1 .708.708l-6 6a.5.5 0 0 1-.708 0l-6-6a.5.5 0 0 1 0-.708z'/%3e%3c/svg%3e");--bs-accordion-btn-icon-width:1.25rem;--bs-accordion-btn-icon-transform:rotate(-180deg);--bs-accordion-btn-icon-transition:transform 0.2s ease-in-out;--bs-accordion-btn-active-icon:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%23052c65'%3e%3cpath fill-rule='evenodd' d='M1.646 4.646a.5.5 0 0 1 .708 0L8 10.293l5.646-5.647a.5.5 0 0 1 .708.708l-6 6a.5.5 0 0 1-.708 0l-6-6a.5.5 0 0 1 0-.708z'/%3e%3c/svg%3e");--bs-accordion-btn-focus-border-color:#86b7fe;--bs-accordion-btn-focus-box-shadow:0 0 0 0.25rem rgba(13, 110, 253, 0.25);--bs-accordion-body-padding-x:1.25rem;--bs-accordion-body-padding-y:1rem;--bs-accordion-active-color:var(--bs-primary-text-emphasis);--bs-accordion-active-bg:var(--bs-primary-bg-subtle)}.accordion-button{position:relative;display:flex;align-items:center;width:100%;padding:var(--bs-accordion-btn-padding-y) var(--bs-accordion-btn-padding-x);font-size:1rem;color:var(--bs-accordion-btn-color);text-align:left;background-color:var(--bs-accordion-btn-bg);border:0;border-radius:0;overflow-anchor:none;transition:var(--bs-accordion-transition)}@media (prefers-reduced-motion:reduce){.accordion-button{transition:none}}.accordion-button:not(.collapsed){color:var(--bs-accordion-active-color);background-color:var(--bs-accordion-active-bg);box-shadow:inset 0 calc(-1 * var(--bs-accordion-border-width)) 0 var(--bs-accordion-border-color)}.accordion-button:not(.collapsed)::after{background-image:var(--bs-accordion-btn-active-icon);transform:var(--bs-accordion-btn-icon-transform)}.accordion-button::after{flex-shrink:0;width:var(--bs-accordion-btn-icon-width);height:var(--bs-accordion-btn-icon-width);margin-left:auto;content:"";background-image:var(--bs-accordion-btn-icon);background-repeat:no-repeat;background-size:var(--bs-accordion-btn-icon-width);transition:var(--bs-accordion-btn-icon-transition)}@media (prefers-reduced-motion:reduce){.accordion-button::after{transition:none}}.accordion-button:hover{z-index:2}.accordion-button:focus{z-index:3;border-color:var(--bs-accordion-btn-focus-border-color);outline:0;box-shadow:var(--bs-accordion-btn-focus-box-shadow)}.accordion-header{margin-bottom:0}.accordion-item{color:var(--bs-accordion-color);background-color:var(--bs-accordion-bg);border:var(--bs-accordion-border-width) solid var(--bs-accordion-border-color)}.accordion-item:first-of-type{border-top-left-radius:var(--bs-accordion-border-radius);border-top-right-radius:var(--bs-accordion-border-radius)}.accordion-item:first-of-type .accordion-button{border-top-left-radius:var(--bs-accordion-inner-border-radius);border-top-right-radius:var(--bs-accordion-inner-border-radius)}.accordion-item:not(:first-of-type){border-top:0}.accordion-item:last-of-type{border-bottom-right-radius:var(--bs-accordion-border-radius);border-bottom-left-radius:var(--bs-accordion-border-radius)}.accordion-item:last-of-type .accordion-button.collapsed{border-bottom-right-radius:var(--bs-accordion-inner-border-radius);border-bottom-left-radius:var(--bs-accordion-inner-border-radius)}.accordion-item:last-of-type .accordion-collapse{border-bottom-right-radius:var(--bs-accordion-border-radius);border-bottom-left-radius:var(--bs-accordion-border-radius)}.accordion-body{padding:var(--bs-accordion-body-padding-y) var(--bs-accordion-body-padding-x)}.accordion-flush .accordion-collapse{border-width:0}.accordion-flush .accordion-item{border-right:0;border-left:0;border-radius:0}.accordion-flush .accordion-item:first-child{border-top:0}.accordion-flush .accordion-item:last-child{border-bottom:0}.accordion-flush .accordion-item .accordion-button,.accordion-flush .accordion-item .accordion-button.collapsed{border-radius:0}[data-bs-theme=dark] .accordion-button::after{--bs-accordion-btn-icon:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%236ea8fe'%3e%3cpath fill-rule='evenodd' d='M1.646 4.646a.5.5 0 0 1 .708 0L8 10.293l5.646-5.647a.5.5 0 0 1 .708.708l-6 6a.5.5 0 0 1-.708 0l-6-6a.5.5 0 0 1 0-.708z'/%3e%3c/svg%3e");--bs-accordion-btn-active-icon:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%236ea8fe'%3e%3cpath fill-rule='evenodd' d='M1.646 4.646a.5.5 0 0 1 .708 0L8 10.293l5.646-5.647a.5.5 0 0 1 .708.708l-6 6a.5.5 0 0 1-.708 0l-6-6a.5.5 0 0 1 0-.708z'/%3e%3c/svg%3e")}.breadcrumb{--bs-breadcrumb-padding-x:0;--bs-breadcrumb-padding-y:0;--bs-breadcrumb-margin-bottom:1rem;--bs-breadcrumb-bg: ;--bs-breadcrumb-border-radius: ;--bs-breadcrumb-divider-color:var(--bs-secondary-color);--bs-breadcrumb-item-padding-x:0.5rem;--bs-breadcrumb-item-active-color:var(--bs-secondary-color);display:flex;flex-wrap:wrap;padding:var(--bs-breadcrumb-padding-y) var(--bs-breadcrumb-padding-x);margin-bottom:var(--bs-breadcrumb-margin-bottom);font-size:var(--bs-breadcrumb-font-size);list-style:none;background-color:var(--bs-breadcrumb-bg);border-radius:var(--bs-breadcrumb-border-radius)}.breadcrumb-item+.breadcrumb-item{padding-left:var(--bs-breadcrumb-item-padding-x)}.breadcrumb-item+.breadcrumb-item::before{float:left;padding-right:var(--bs-breadcrumb-item-padding-x);color:var(--bs-breadcrumb-divider-color);content:var(--bs-breadcrumb-divider, "/")}.breadcrumb-item.active{color:var(--bs-breadcrumb-item-active-color)}.pagination{--bs-pagination-padding-x:0.75rem;--bs-pagination-padding-y:0.375rem;--bs-pagination-font-size:1rem;--bs-pagination-color:var(--bs-link-color);--bs-pagination-bg:var(--bs-body-bg);--bs-pagination-border-width:var(--bs-border-width);--bs-pagination-border-color:var(--bs-border-color);--bs-pagination-border-radius:var(--bs-border-radius);--bs-pagination-hover-color:var(--bs-link-hover-color);--bs-pagination-hover-bg:var(--bs-tertiary-bg);--bs-pagination-hover-border-color:var(--bs-border-color);--bs-pagination-focus-color:var(--bs-link-hover-color);--bs-pagination-focus-bg:var(--bs-secondary-bg);--bs-pagination-focus-box-shadow:0 0 0 0.25rem rgba(13, 110, 253, 0.25);--bs-pagination-active-color:#fff;--bs-pagination-active-bg:#0d6efd;--bs-pagination-active-border-color:#0d6efd;--bs-pagination-disabled-color:var(--bs-secondary-color);--bs-pagination-disabled-bg:var(--bs-secondary-bg);--bs-pagination-disabled-border-color:var(--bs-border-color);display:flex;padding-left:0;list-style:none}.page-link{position:relative;display:block;padding:var(--bs-pagination-padding-y) var(--bs-pagination-padding-x);font-size:var(--bs-pagination-font-size);color:var(--bs-pagination-color);text-decoration:none;background-color:var(--bs-pagination-bg);border:var(--bs-pagination-border-width) solid var(--bs-pagination-border-color);transition:color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out}@media (prefers-reduced-motion:reduce){.page-link{transition:none}}.page-link:hover{z-index:2;color:var(--bs-pagination-hover-color);background-color:var(--bs-pagination-hover-bg);border-color:var(--bs-pagination-hover-border-color)}.page-link:focus{z-index:3;color:var(--bs-pagination-focus-color);background-color:var(--bs-pagination-focus-bg);outline:0;box-shadow:var(--bs-pagination-focus-box-shadow)}.active>.page-link,.page-link.active{z-index:3;color:var(--bs-pagination-active-color);background-color:var(--bs-pagination-active-bg);border-color:var(--bs-pagination-active-border-color)}.disabled>.page-link,.page-link.disabled{color:var(--bs-pagination-disabled-color);pointer-events:none;background-color:var(--bs-pagination-disabled-bg);border-color:var(--bs-pagination-disabled-border-color)}.page-item:not(:first-child) .page-link{margin-left:calc(var(--bs-border-width) * -1)}.page-item:first-child .page-link{border-top-left-radius:var(--bs-pagination-border-radius);border-bottom-left-radius:var(--bs-pagination-border-radius)}.page-item:last-child .page-link{border-top-right-radius:var(--bs-pagination-border-radius);border-bottom-right-radius:var(--bs-pagination-border-radius)}.pagination-lg{--bs-pagination-padding-x:1.5rem;--bs-pagination-padding-y:0.75rem;--bs-pagination-font-size:1.25rem;--bs-pagination-border-radius:0.5rem}.pagination-sm{--bs-pagination-padding-x:0.5rem;--bs-pagination-padding-y:0.25rem;--bs-pagination-font-size:0.875rem;--bs-pagination-border-radius:0.25rem}.badge{--bs-badge-padding-x:0.65em;--bs-badge-padding-y:0.35em;--bs-badge-font-size:0.75em;--bs-badge-font-weight:700;--bs-badge-color:#fff;--bs-badge-border-radius:0.375rem;display:inline-block;padding:var(--bs-badge-padding-y) var(--bs-badge-padding-x);font-size:var(--bs-badge-font-size);font-weight:var(--bs-badge-font-weight);line-height:1;color:var(--bs-badge-color);text-align:center;white-space:nowrap;vertical-align:baseline;border-radius:var(--bs-badge-border-radius)}.badge:empty{display:none}.btn .badge{position:relative;top:-1px}.alert{--bs-alert-bg:transparent;--bs-alert-padding-x:1rem;--bs-alert-padding-y:1rem;--bs-alert-margin-bottom:1rem;--bs-alert-color:inherit;--bs-alert-border-color:transparent;--bs-alert-border:var(--bs-border-width) solid var(--bs-alert-border-color);--bs-alert-border-radius:0.375rem;--bs-alert-link-color:inherit;position:relative;padding:var(--bs-alert-padding-y) var(--bs-alert-padding-x);margin-bottom:var(--bs-alert-margin-bottom);color:var(--bs-alert-color);background-color:var(--bs-alert-bg);border:var(--bs-alert-border);border-radius:var(--bs-alert-border-radius)}.alert-heading{color:inherit}.alert-link{font-weight:700;color:var(--bs-alert-link-color)}.alert-dismissible{padding-right:3rem}.alert-dismissible .btn-close{position:absolute;top:0;right:0;z-index:2;padding:1.25rem 1rem}.alert-primary{--bs-alert-color:var(--bs-primary-text-emphasis);--bs-alert-bg:var(--bs-primary-bg-subtle);--bs-alert-border-color:var(--bs-primary-border-subtle);--bs-alert-link-color:var(--bs-primary-text-emphasis)}.alert-secondary{--bs-alert-color:var(--bs-secondary-text-emphasis);--bs-alert-bg:var(--bs-secondary-bg-subtle);--bs-alert-border-color:var(--bs-secondary-border-subtle);--bs-alert-link-color:var(--bs-secondary-text-emphasis)}.alert-success{--bs-alert-color:var(--bs-success-text-emphasis);--bs-alert-bg:var(--bs-success-bg-subtle);--bs-alert-border-color:var(--bs-success-border-subtle);--bs-alert-link-color:var(--bs-success-text-emphasis)}.alert-info{--bs-alert-color:var(--bs-info-text-emphasis);--bs-alert-bg:var(--bs-info-bg-subtle);--bs-alert-border-color:var(--bs-info-border-subtle);--bs-alert-link-color:var(--bs-info-text-emphasis)}.alert-warning{--bs-alert-color:var(--bs-warning-text-emphasis);--bs-alert-bg:var(--bs-warning-bg-subtle);--bs-alert-border-color:var(--bs-warning-border-subtle);--bs-alert-link-color:var(--bs-warning-text-emphasis)}.alert-danger{--bs-alert-color:var(--bs-danger-text-emphasis);--bs-alert-bg:var(--bs-danger-bg-subtle);--bs-alert-border-color:var(--bs-danger-border-subtle);--bs-alert-link-color:var(--bs-danger-text-emphasis)}.alert-light{--bs-alert-color:var(--bs-light-text-emphasis);--bs-alert-bg:var(--bs-light-bg-subtle);--bs-alert-border-color:var(--bs-light-border-subtle);--bs-alert-link-color:var(--bs-light-text-emphasis)}.alert-dark{--bs-alert-color:var(--bs-dark-text-emphasis);--bs-alert-bg:var(--bs-dark-bg-subtle);--bs-alert-border-color:var(--bs-dark-border-subtle);--bs-alert-link-color:var(--bs-dark-text-emphasis)}@keyframes progress-bar-stripes{0%{background-position-x:1rem}}.progress,.progress-stacked{--bs-progress-height:1rem;--bs-progress-font-size:0.75rem;--bs-progress-bg:var(--bs-secondary-bg);--bs-progress-border-radius:var(--bs-border-radius);--bs-progress-box-shadow:var(--bs-box-shadow-inset);--bs-progress-bar-color:#fff;--bs-progress-bar-bg:#0d6efd;--bs-progress-bar-transition:width 0.6s ease;display:flex;height:var(--bs-progress-height);overflow:hidden;font-size:var(--bs-progress-font-size);background-color:var(--bs-progress-bg);border-radius:var(--bs-progress-border-radius)}.progress-bar{display:flex;flex-direction:column;justify-content:center;overflow:hidden;color:var(--bs-progress-bar-color);text-align:center;white-space:nowrap;background-color:var(--bs-progress-bar-bg);transition:var(--bs-progress-bar-transition)}@media (prefers-reduced-motion:reduce){.progress-bar{transition:none}}.progress-bar-striped{background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-size:var(--bs-progress-height) var(--bs-progress-height)}.progress-stacked>.progress{overflow:visible}.progress-stacked>.progress>.progress-bar{width:100%}.progress-bar-animated{animation:1s linear infinite progress-bar-stripes}@media (prefers-reduced-motion:reduce){.progress-bar-animated{animation:none}}.list-group{--bs-list-group-color:var(--bs-body-color);--bs-list-group-bg:var(--bs-body-bg);--bs-list-group-border-color:var(--bs-border-color);--bs-list-group-border-width:var(--bs-border-width);--bs-list-group-border-radius:var(--bs-border-radius);--bs-list-group-item-padding-x:1rem;--bs-list-group-item-padding-y:0.5rem;--bs-list-group-action-color:var(--bs-secondary-color);--bs-list-group-action-hover-color:var(--bs-emphasis-color);--bs-list-group-action-hover-bg:var(--bs-tertiary-bg);--bs-list-group-action-active-color:var(--bs-body-color);--bs-list-group-action-active-bg:var(--bs-secondary-bg);--bs-list-group-disabled-color:var(--bs-secondary-color);--bs-list-group-disabled-bg:var(--bs-body-bg);--bs-list-group-active-color:#fff;--bs-list-group-active-bg:#0d6efd;--bs-list-group-active-border-color:#0d6efd;display:flex;flex-direction:column;padding-left:0;margin-bottom:0;border-radius:var(--bs-list-group-border-radius)}.list-group-numbered{list-style-type:none;counter-reset:section}.list-group-numbered>.list-group-item::before{content:counters(section, ".") ". ";counter-increment:section}.list-group-item-action{width:100%;color:var(--bs-list-group-action-color);text-align:inherit}.list-group-item-action:focus,.list-group-item-action:hover{z-index:1;color:var(--bs-list-group-action-hover-color);text-decoration:none;background-color:var(--bs-list-group-action-hover-bg)}.list-group-item-action:active{color:var(--bs-list-group-action-active-color);background-color:var(--bs-list-group-action-active-bg)}.list-group-item{position:relative;display:block;padding:var(--bs-list-group-item-padding-y) var(--bs-list-group-item-padding-x);color:var(--bs-list-group-color);text-decoration:none;background-color:var(--bs-list-group-bg);border:var(--bs-list-group-border-width) solid var(--bs-list-group-border-color)}.list-group-item:first-child{border-top-left-radius:inherit;border-top-right-radius:inherit}.list-group-item:last-child{border-bottom-right-radius:inherit;border-bottom-left-radius:inherit}.list-group-item.disabled,.list-group-item:disabled{color:var(--bs-list-group-disabled-color);pointer-events:none;background-color:var(--bs-list-group-disabled-bg)}.list-group-item.active{z-index:2;color:var(--bs-list-group-active-color);background-color:var(--bs-list-group-active-bg);border-color:var(--bs-list-group-active-border-color)}.list-group-item+.list-group-item{border-top-width:0}.list-group-item+.list-group-item.active{margin-top:calc(-1 * var(--bs-list-group-border-width));border-top-width:var(--bs-list-group-border-width)}.list-group-horizontal{flex-direction:row}.list-group-horizontal>.list-group-item:first-child:not(:last-child){border-bottom-left-radius:var(--bs-list-group-border-radius);border-top-right-radius:0}.list-group-horizontal>.list-group-item:last-child:not(:first-child){border-top-right-radius:var(--bs-list-group-border-radius);border-bottom-left-radius:0}.list-group-horizontal>.list-group-item.active{margin-top:0}.list-group-horizontal>.list-group-item+.list-group-item{border-top-width:var(--bs-list-group-border-width);border-left-width:0}.list-group-horizontal>.list-group-item+.list-group-item.active{margin-left:calc(-1 * var(--bs-list-group-border-width));border-left-width:var(--bs-list-group-border-width)}@media (min-width:576px){.list-group-horizontal-sm{flex-direction:row}.list-group-horizontal-sm>.list-group-item:first-child:not(:last-child){border-bottom-left-radius:var(--bs-list-group-border-radius);border-top-right-radius:0}.list-group-horizontal-sm>.list-group-item:last-child:not(:first-child){border-top-right-radius:var(--bs-list-group-border-radius);border-bottom-left-radius:0}.list-group-horizontal-sm>.list-group-item.active{margin-top:0}.list-group-horizontal-sm>.list-group-item+.list-group-item{border-top-width:var(--bs-list-group-border-width);border-left-width:0}.list-group-horizontal-sm>.list-group-item+.list-group-item.active{margin-left:calc(-1 * var(--bs-list-group-border-width));border-left-width:var(--bs-list-group-border-width)}}@media (min-width:768px){.list-group-horizontal-md{flex-direction:row}.list-group-horizontal-md>.list-group-item:first-child:not(:last-child){border-bottom-left-radius:var(--bs-list-group-border-radius);border-top-right-radius:0}.list-group-horizontal-md>.list-group-item:last-child:not(:first-child){border-top-right-radius:var(--bs-list-group-border-radius);border-bottom-left-radius:0}.list-group-horizontal-md>.list-group-item.active{margin-top:0}.list-group-horizontal-md>.list-group-item+.list-group-item{border-top-width:var(--bs-list-group-border-width);border-left-width:0}.list-group-horizontal-md>.list-group-item+.list-group-item.active{margin-left:calc(-1 * var(--bs-list-group-border-width));border-left-width:var(--bs-list-group-border-width)}}@media (min-width:992px){.list-group-horizontal-lg{flex-direction:row}.list-group-horizontal-lg>.list-group-item:first-child:not(:last-child){border-bottom-left-radius:var(--bs-list-group-border-radius);border-top-right-radius:0}.list-group-horizontal-lg>.list-group-item:last-child:not(:first-child){border-top-right-radius:var(--bs-list-group-border-radius);border-bottom-left-radius:0}.list-group-horizontal-lg>.list-group-item.active{margin-top:0}.list-group-horizontal-lg>.list-group-item+.list-group-item{border-top-width:var(--bs-list-group-border-width);border-left-width:0}.list-group-horizontal-lg>.list-group-item+.list-group-item.active{margin-left:calc(-1 * var(--bs-list-group-border-width));border-left-width:var(--bs-list-group-border-width)}}@media (min-width:1200px){.list-group-horizontal-xl{flex-direction:row}.list-group-horizontal-xl>.list-group-item:first-child:not(:last-child){border-bottom-left-radius:var(--bs-list-group-border-radius);border-top-right-radius:0}.list-group-horizontal-xl>.list-group-item:last-child:not(:first-child){border-top-right-radius:var(--bs-list-group-border-radius);border-bottom-left-radius:0}.list-group-horizontal-xl>.list-group-item.active{margin-top:0}.list-group-horizontal-xl>.list-group-item+.list-group-item{border-top-width:var(--bs-list-group-border-width);border-left-width:0}.list-group-horizontal-xl>.list-group-item+.list-group-item.active{margin-left:calc(-1 * var(--bs-list-group-border-width));border-left-width:var(--bs-list-group-border-width)}}@media (min-width:1400px){.list-group-horizontal-xxl{flex-direction:row}.list-group-horizontal-xxl>.list-group-item:first-child:not(:last-child){border-bottom-left-radius:var(--bs-list-group-border-radius);border-top-right-radius:0}.list-group-horizontal-xxl>.list-group-item:last-child:not(:first-child){border-top-right-radius:var(--bs-list-group-border-radius);border-bottom-left-radius:0}.list-group-horizontal-xxl>.list-group-item.active{margin-top:0}.list-group-horizontal-xxl>.list-group-item+.list-group-item{border-top-width:var(--bs-list-group-border-width);border-left-width:0}.list-group-horizontal-xxl>.list-group-item+.list-group-item.active{margin-left:calc(-1 * var(--bs-list-group-border-width));border-left-width:var(--bs-list-group-border-width)}}.list-group-flush{border-radius:0}.list-group-flush>.list-group-item{border-width:0 0 var(--bs-list-group-border-width)}.list-group-flush>.list-group-item:last-child{border-bottom-width:0}.list-group-item-primary{--bs-list-group-color:var(--bs-primary-text-emphasis);--bs-list-group-bg:var(--bs-primary-bg-subtle);--bs-list-group-border-color:var(--bs-primary-border-subtle);--bs-list-group-action-hover-color:var(--bs-emphasis-color);--bs-list-group-action-hover-bg:var(--bs-primary-border-subtle);--bs-list-group-action-active-color:var(--bs-emphasis-color);--bs-list-group-action-active-bg:var(--bs-primary-border-subtle);--bs-list-group-active-color:var(--bs-primary-bg-subtle);--bs-list-group-active-bg:var(--bs-primary-text-emphasis);--bs-list-group-active-border-color:var(--bs-primary-text-emphasis)}.list-group-item-secondary{--bs-list-group-color:var(--bs-secondary-text-emphasis);--bs-list-group-bg:var(--bs-secondary-bg-subtle);--bs-list-group-border-color:var(--bs-secondary-border-subtle);--bs-list-group-action-hover-color:var(--bs-emphasis-color);--bs-list-group-action-hover-bg:var(--bs-secondary-border-subtle);--bs-list-group-action-active-color:var(--bs-emphasis-color);--bs-list-group-action-active-bg:var(--bs-secondary-border-subtle);--bs-list-group-active-color:var(--bs-secondary-bg-subtle);--bs-list-group-active-bg:var(--bs-secondary-text-emphasis);--bs-list-group-active-border-color:var(--bs-secondary-text-emphasis)}.list-group-item-success{--bs-list-group-color:var(--bs-success-text-emphasis);--bs-list-group-bg:var(--bs-success-bg-subtle);--bs-list-group-border-color:var(--bs-success-border-subtle);--bs-list-group-action-hover-color:var(--bs-emphasis-color);--bs-list-group-action-hover-bg:var(--bs-success-border-subtle);--bs-list-group-action-active-color:var(--bs-emphasis-color);--bs-list-group-action-active-bg:var(--bs-success-border-subtle);--bs-list-group-active-color:var(--bs-success-bg-subtle);--bs-list-group-active-bg:var(--bs-success-text-emphasis);--bs-list-group-active-border-color:var(--bs-success-text-emphasis)}.list-group-item-info{--bs-list-group-color:var(--bs-info-text-emphasis);--bs-list-group-bg:var(--bs-info-bg-subtle);--bs-list-group-border-color:var(--bs-info-border-subtle);--bs-list-group-action-hover-color:var(--bs-emphasis-color);--bs-list-group-action-hover-bg:var(--bs-info-border-subtle);--bs-list-group-action-active-color:var(--bs-emphasis-color);--bs-list-group-action-active-bg:var(--bs-info-border-subtle);--bs-list-group-active-color:var(--bs-info-bg-subtle);--bs-list-group-active-bg:var(--bs-info-text-emphasis);--bs-list-group-active-border-color:var(--bs-info-text-emphasis)}.list-group-item-warning{--bs-list-group-color:var(--bs-warning-text-emphasis);--bs-list-group-bg:var(--bs-warning-bg-subtle);--bs-list-group-border-color:var(--bs-warning-border-subtle);--bs-list-group-action-hover-color:var(--bs-emphasis-color);--bs-list-group-action-hover-bg:var(--bs-warning-border-subtle);--bs-list-group-action-active-color:var(--bs-emphasis-color);--bs-list-group-action-active-bg:var(--bs-warning-border-subtle);--bs-list-group-active-color:var(--bs-warning-bg-subtle);--bs-list-group-active-bg:var(--bs-warning-text-emphasis);--bs-list-group-active-border-color:var(--bs-warning-text-emphasis)}.list-group-item-danger{--bs-list-group-color:var(--bs-danger-text-emphasis);--bs-list-group-bg:var(--bs-danger-bg-subtle);--bs-list-group-border-color:var(--bs-danger-border-subtle);--bs-list-group-action-hover-color:var(--bs-emphasis-color);--bs-list-group-action-hover-bg:var(--bs-danger-border-subtle);--bs-list-group-action-active-color:var(--bs-emphasis-color);--bs-list-group-action-active-bg:var(--bs-danger-border-subtle);--bs-list-group-active-color:var(--bs-danger-bg-subtle);--bs-list-group-active-bg:var(--bs-danger-text-emphasis);--bs-list-group-active-border-color:var(--bs-danger-text-emphasis)}.list-group-item-light{--bs-list-group-color:var(--bs-light-text-emphasis);--bs-list-group-bg:var(--bs-light-bg-subtle);--bs-list-group-border-color:var(--bs-light-border-subtle);--bs-list-group-action-hover-color:var(--bs-emphasis-color);--bs-list-group-action-hover-bg:var(--bs-light-border-subtle);--bs-list-group-action-active-color:var(--bs-emphasis-color);--bs-list-group-action-active-bg:var(--bs-light-border-subtle);--bs-list-group-active-color:var(--bs-light-bg-subtle);--bs-list-group-active-bg:var(--bs-light-text-emphasis);--bs-list-group-active-border-color:var(--bs-light-text-emphasis)}.list-group-item-dark{--bs-list-group-color:var(--bs-dark-text-emphasis);--bs-list-group-bg:var(--bs-dark-bg-subtle);--bs-list-group-border-color:var(--bs-dark-border-subtle);--bs-list-group-action-hover-color:var(--bs-emphasis-color);--bs-list-group-action-hover-bg:var(--bs-dark-border-subtle);--bs-list-group-action-active-color:var(--bs-emphasis-color);--bs-list-group-action-active-bg:var(--bs-dark-border-subtle);--bs-list-group-active-color:var(--bs-dark-bg-subtle);--bs-list-group-active-bg:var(--bs-dark-text-emphasis);--bs-list-group-active-border-color:var(--bs-dark-text-emphasis)}.btn-close{--bs-btn-close-color:#000;--bs-btn-close-bg:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%23000'%3e%3cpath d='M.293.293a1 1 0 0 1 1.414 0L8 6.586 14.293.293a1 1 0 1 1 1.414 1.414L9.414 8l6.293 6.293a1 1 0 0 1-1.414 1.414L8 9.414l-6.293 6.293a1 1 0 0 1-1.414-1.414L6.586 8 .293 1.707a1 1 0 0 1 0-1.414z'/%3e%3c/svg%3e");--bs-btn-close-opacity:0.5;--bs-btn-close-hover-opacity:0.75;--bs-btn-close-focus-shadow:0 0 0 0.25rem rgba(13, 110, 253, 0.25);--bs-btn-close-focus-opacity:1;--bs-btn-close-disabled-opacity:0.25;--bs-btn-close-white-filter:invert(1) grayscale(100%) brightness(200%);box-sizing:content-box;width:1em;height:1em;padding:.25em .25em;color:var(--bs-btn-close-color);background:transparent var(--bs-btn-close-bg) center/1em auto no-repeat;border:0;border-radius:.375rem;opacity:var(--bs-btn-close-opacity)}.btn-close:hover{color:var(--bs-btn-close-color);text-decoration:none;opacity:var(--bs-btn-close-hover-opacity)}.btn-close:focus{outline:0;box-shadow:var(--bs-btn-close-focus-shadow);opacity:var(--bs-btn-close-focus-opacity)}.btn-close.disabled,.btn-close:disabled{pointer-events:none;-webkit-user-select:none;-moz-user-select:none;user-select:none;opacity:var(--bs-btn-close-disabled-opacity)}.btn-close-white{filter:var(--bs-btn-close-white-filter)}[data-bs-theme=dark] .btn-close{filter:var(--bs-btn-close-white-filter)}.toast{--bs-toast-zindex:1090;--bs-toast-padding-x:0.75rem;--bs-toast-padding-y:0.5rem;--bs-toast-spacing:1.5rem;--bs-toast-max-width:350px;--bs-toast-font-size:0.875rem;--bs-toast-color: ;--bs-toast-bg:rgba(var(--bs-body-bg-rgb), 0.85);--bs-toast-border-width:var(--bs-border-width);--bs-toast-border-color:var(--bs-border-color-translucent);--bs-toast-border-radius:var(--bs-border-radius);--bs-toast-box-shadow:var(--bs-box-shadow);--bs-toast-header-color:var(--bs-secondary-color);--bs-toast-header-bg:rgba(var(--bs-body-bg-rgb), 0.85);--bs-toast-header-border-color:var(--bs-border-color-translucent);width:var(--bs-toast-max-width);max-width:100%;font-size:var(--bs-toast-font-size);color:var(--bs-toast-color);pointer-events:auto;background-color:var(--bs-toast-bg);background-clip:padding-box;border:var(--bs-toast-border-width) solid var(--bs-toast-border-color);box-shadow:var(--bs-toast-box-shadow);border-radius:var(--bs-toast-border-radius)}.toast.showing{opacity:0}.toast:not(.show){display:none}.toast-container{--bs-toast-zindex:1090;position:absolute;z-index:var(--bs-toast-zindex);width:-webkit-max-content;width:-moz-max-content;width:max-content;max-width:100%;pointer-events:none}.toast-container>:not(:last-child){margin-bottom:var(--bs-toast-spacing)}.toast-header{display:flex;align-items:center;padding:var(--bs-toast-padding-y) var(--bs-toast-padding-x);color:var(--bs-toast-header-color);background-color:var(--bs-toast-header-bg);background-clip:padding-box;border-bottom:var(--bs-toast-border-width) solid var(--bs-toast-header-border-color);border-top-left-radius:calc(var(--bs-toast-border-radius) - var(--bs-toast-border-width));border-top-right-radius:calc(var(--bs-toast-border-radius) - var(--bs-toast-border-width))}.toast-header .btn-close{margin-right:calc(-.5 * var(--bs-toast-padding-x));margin-left:var(--bs-toast-padding-x)}.toast-body{padding:var(--bs-toast-padding-x);word-wrap:break-word}.modal{--bs-modal-zindex:1055;--bs-modal-width:500px;--bs-modal-padding:1rem;--bs-modal-margin:0.5rem;--bs-modal-color: ;--bs-modal-bg:var(--bs-body-bg);--bs-modal-border-color:var(--bs-border-color-translucent);--bs-modal-border-width:var(--bs-border-width);--bs-modal-border-radius:var(--bs-border-radius-lg);--bs-modal-box-shadow:0 0.125rem 0.25rem rgba(0, 0, 0, 0.075);--bs-modal-inner-border-radius:calc(var(--bs-border-radius-lg) - (var(--bs-border-width)));--bs-modal-header-padding-x:1rem;--bs-modal-header-padding-y:1rem;--bs-modal-header-padding:1rem 1rem;--bs-modal-header-border-color:var(--bs-border-color);--bs-modal-header-border-width:var(--bs-border-width);--bs-modal-title-line-height:1.5;--bs-modal-footer-gap:0.5rem;--bs-modal-footer-bg: ;--bs-modal-footer-border-color:var(--bs-border-color);--bs-modal-footer-border-width:var(--bs-border-width);position:fixed;top:0;left:0;z-index:var(--bs-modal-zindex);display:none;width:100%;height:100%;overflow-x:hidden;overflow-y:auto;outline:0}.modal-dialog{position:relative;width:auto;margin:var(--bs-modal-margin);pointer-events:none}.modal.fade .modal-dialog{transition:transform .3s ease-out;transform:translate(0,-50px)}@media (prefers-reduced-motion:reduce){.modal.fade .modal-dialog{transition:none}}.modal.show .modal-dialog{transform:none}.modal.modal-static .modal-dialog{transform:scale(1.02)}.modal-dialog-scrollable{height:calc(100% - var(--bs-modal-margin) * 2)}.modal-dialog-scrollable .modal-content{max-height:100%;overflow:hidden}.modal-dialog-scrollable .modal-body{overflow-y:auto}.modal-dialog-centered{display:flex;align-items:center;min-height:calc(100% - var(--bs-modal-margin) * 2)}.modal-content{position:relative;display:flex;flex-direction:column;width:100%;color:var(--bs-modal-color);pointer-events:auto;background-color:var(--bs-modal-bg);background-clip:padding-box;border:var(--bs-modal-border-width) solid var(--bs-modal-border-color);border-radius:var(--bs-modal-border-radius);outline:0}.modal-backdrop{--bs-backdrop-zindex:1050;--bs-backdrop-bg:#000;--bs-backdrop-opacity:0.5;position:fixed;top:0;left:0;z-index:var(--bs-backdrop-zindex);width:100vw;height:100vh;background-color:var(--bs-backdrop-bg)}.modal-backdrop.fade{opacity:0}.modal-backdrop.show{opacity:var(--bs-backdrop-opacity)}.modal-header{display:flex;flex-shrink:0;align-items:center;justify-content:space-between;padding:var(--bs-modal-header-padding);border-bottom:var(--bs-modal-header-border-width) solid var(--bs-modal-header-border-color);border-top-left-radius:var(--bs-modal-inner-border-radius);border-top-right-radius:var(--bs-modal-inner-border-radius)}.modal-header .btn-close{padding:calc(var(--bs-modal-header-padding-y) * .5) calc(var(--bs-modal-header-padding-x) * .5);margin:calc(-.5 * var(--bs-modal-header-padding-y)) calc(-.5 * var(--bs-modal-header-padding-x)) calc(-.5 * var(--bs-modal-header-padding-y)) auto}.modal-title{margin-bottom:0;line-height:var(--bs-modal-title-line-height)}.modal-body{position:relative;flex:1 1 auto;padding:var(--bs-modal-padding)}.modal-footer{display:flex;flex-shrink:0;flex-wrap:wrap;align-items:center;justify-content:flex-end;padding:calc(var(--bs-modal-padding) - var(--bs-modal-footer-gap) * .5);background-color:var(--bs-modal-footer-bg);border-top:var(--bs-modal-footer-border-width) solid var(--bs-modal-footer-border-color);border-bottom-right-radius:var(--bs-modal-inner-border-radius);border-bottom-left-radius:var(--bs-modal-inner-border-radius)}.modal-footer>*{margin:calc(var(--bs-modal-footer-gap) * .5)}@media (min-width:576px){.modal{--bs-modal-margin:1.75rem;--bs-modal-box-shadow:0 0.5rem 1rem rgba(0, 0, 0, 0.15)}.modal-dialog{max-width:var(--bs-modal-width);margin-right:auto;margin-left:auto}.modal-sm{--bs-modal-width:300px}}@media (min-width:992px){.modal-lg,.modal-xl{--bs-modal-width:800px}}@media (min-width:1200px){.modal-xl{--bs-modal-width:1140px}}.modal-fullscreen{width:100vw;max-width:none;height:100%;margin:0}.modal-fullscreen .modal-content{height:100%;border:0;border-radius:0}.modal-fullscreen .modal-footer,.modal-fullscreen .modal-header{border-radius:0}.modal-fullscreen .modal-body{overflow-y:auto}@media (max-width:575.98px){.modal-fullscreen-sm-down{width:100vw;max-width:none;height:100%;margin:0}.modal-fullscreen-sm-down .modal-content{height:100%;border:0;border-radius:0}.modal-fullscreen-sm-down .modal-footer,.modal-fullscreen-sm-down .modal-header{border-radius:0}.modal-fullscreen-sm-down .modal-body{overflow-y:auto}}@media (max-width:767.98px){.modal-fullscreen-md-down{width:100vw;max-width:none;height:100%;margin:0}.modal-fullscreen-md-down .modal-content{height:100%;border:0;border-radius:0}.modal-fullscreen-md-down .modal-footer,.modal-fullscreen-md-down .modal-header{border-radius:0}.modal-fullscreen-md-down .modal-body{overflow-y:auto}}@media (max-width:991.98px){.modal-fullscreen-lg-down{width:100vw;max-width:none;height:100%;margin:0}.modal-fullscreen-lg-down .modal-content{height:100%;border:0;border-radius:0}.modal-fullscreen-lg-down .modal-footer,.modal-fullscreen-lg-down .modal-header{border-radius:0}.modal-fullscreen-lg-down .modal-body{overflow-y:auto}}@media (max-width:1199.98px){.modal-fullscreen-xl-down{width:100vw;max-width:none;height:100%;margin:0}.modal-fullscreen-xl-down .modal-content{height:100%;border:0;border-radius:0}.modal-fullscreen-xl-down .modal-footer,.modal-fullscreen-xl-down .modal-header{border-radius:0}.modal-fullscreen-xl-down .modal-body{overflow-y:auto}}@media (max-width:1399.98px){.modal-fullscreen-xxl-down{width:100vw;max-width:none;height:100%;margin:0}.modal-fullscreen-xxl-down .modal-content{height:100%;border:0;border-radius:0}.modal-fullscreen-xxl-down .modal-footer,.modal-fullscreen-xxl-down .modal-header{border-radius:0}.modal-fullscreen-xxl-down .modal-body{overflow-y:auto}}.tooltip{--bs-tooltip-zindex:1080;--bs-tooltip-max-width:200px;--bs-tooltip-padding-x:0.5rem;--bs-tooltip-padding-y:0.25rem;--bs-tooltip-margin: ;--bs-tooltip-font-size:0.875rem;--bs-tooltip-color:var(--bs-body-bg);--bs-tooltip-bg:var(--bs-emphasis-color);--bs-tooltip-border-radius:var(--bs-border-radius);--bs-tooltip-opacity:0.9;--bs-tooltip-arrow-width:0.8rem;--bs-tooltip-arrow-height:0.4rem;z-index:var(--bs-tooltip-zindex);display:block;padding:var(--bs-tooltip-arrow-height);margin:var(--bs-tooltip-margin);font-family:var(--bs-font-sans-serif);font-style:normal;font-weight:400;line-height:1.5;text-align:left;text-align:start;text-decoration:none;text-shadow:none;text-transform:none;letter-spacing:normal;word-break:normal;white-space:normal;word-spacing:normal;line-break:auto;font-size:var(--bs-tooltip-font-size);word-wrap:break-word;opacity:0}.tooltip.show{opacity:var(--bs-tooltip-opacity)}.tooltip .tooltip-arrow{display:block;width:var(--bs-tooltip-arrow-width);height:var(--bs-tooltip-arrow-height)}.tooltip .tooltip-arrow::before{position:absolute;content:"";border-color:transparent;border-style:solid}.bs-tooltip-auto[data-popper-placement^=top] .tooltip-arrow,.bs-tooltip-top .tooltip-arrow{bottom:0}.bs-tooltip-auto[data-popper-placement^=top] .tooltip-arrow::before,.bs-tooltip-top .tooltip-arrow::before{top:-1px;border-width:var(--bs-tooltip-arrow-height) calc(var(--bs-tooltip-arrow-width) * .5) 0;border-top-color:var(--bs-tooltip-bg)}.bs-tooltip-auto[data-popper-placement^=right] .tooltip-arrow,.bs-tooltip-end .tooltip-arrow{left:0;width:var(--bs-tooltip-arrow-height);height:var(--bs-tooltip-arrow-width)}.bs-tooltip-auto[data-popper-placement^=right] .tooltip-arrow::before,.bs-tooltip-end .tooltip-arrow::before{right:-1px;border-width:calc(var(--bs-tooltip-arrow-width) * .5) var(--bs-tooltip-arrow-height) calc(var(--bs-tooltip-arrow-width) * .5) 0;border-right-color:var(--bs-tooltip-bg)}.bs-tooltip-auto[data-popper-placement^=bottom] .tooltip-arrow,.bs-tooltip-bottom .tooltip-arrow{top:0}.bs-tooltip-auto[data-popper-placement^=bottom] .tooltip-arrow::before,.bs-tooltip-bottom .tooltip-arrow::before{bottom:-1px;border-width:0 calc(var(--bs-tooltip-arrow-width) * .5) var(--bs-tooltip-arrow-height);border-bottom-color:var(--bs-tooltip-bg)}.bs-tooltip-auto[data-popper-placement^=left] .tooltip-arrow,.bs-tooltip-start .tooltip-arrow{right:0;width:var(--bs-tooltip-arrow-height);height:var(--bs-tooltip-arrow-width)}.bs-tooltip-auto[data-popper-placement^=left] .tooltip-arrow::before,.bs-tooltip-start .tooltip-arrow::before{left:-1px;border-width:calc(var(--bs-tooltip-arrow-width) * .5) 0 calc(var(--bs-tooltip-arrow-width) * .5) var(--bs-tooltip-arrow-height);border-left-color:var(--bs-tooltip-bg)}.tooltip-inner{max-width:var(--bs-tooltip-max-width);padding:var(--bs-tooltip-padding-y) var(--bs-tooltip-padding-x);color:var(--bs-tooltip-color);text-align:center;background-color:var(--bs-tooltip-bg);border-radius:var(--bs-tooltip-border-radius)}.popover{--bs-popover-zindex:1070;--bs-popover-max-width:276px;--bs-popover-font-size:0.875rem;--bs-popover-bg:var(--bs-body-bg);--bs-popover-border-width:var(--bs-border-width);--bs-popover-border-color:var(--bs-border-color-translucent);--bs-popover-border-radius:var(--bs-border-radius-lg);--bs-popover-inner-border-radius:calc(var(--bs-border-radius-lg) - var(--bs-border-width));--bs-popover-box-shadow:0 0.5rem 1rem rgba(0, 0, 0, 0.15);--bs-popover-header-padding-x:1rem;--bs-popover-header-padding-y:0.5rem;--bs-popover-header-font-size:1rem;--bs-popover-header-color: ;--bs-popover-header-bg:var(--bs-secondary-bg);--bs-popover-body-padding-x:1rem;--bs-popover-body-padding-y:1rem;--bs-popover-body-color:var(--bs-body-color);--bs-popover-arrow-width:1rem;--bs-popover-arrow-height:0.5rem;--bs-popover-arrow-border:var(--bs-popover-border-color);z-index:var(--bs-popover-zindex);display:block;max-width:var(--bs-popover-max-width);font-family:var(--bs-font-sans-serif);font-style:normal;font-weight:400;line-height:1.5;text-align:left;text-align:start;text-decoration:none;text-shadow:none;text-transform:none;letter-spacing:normal;word-break:normal;white-space:normal;word-spacing:normal;line-break:auto;font-size:var(--bs-popover-font-size);word-wrap:break-word;background-color:var(--bs-popover-bg);background-clip:padding-box;border:var(--bs-popover-border-width) solid var(--bs-popover-border-color);border-radius:var(--bs-popover-border-radius)}.popover .popover-arrow{display:block;width:var(--bs-popover-arrow-width);height:var(--bs-popover-arrow-height)}.popover .popover-arrow::after,.popover .popover-arrow::before{position:absolute;display:block;content:"";border-color:transparent;border-style:solid;border-width:0}.bs-popover-auto[data-popper-placement^=top]>.popover-arrow,.bs-popover-top>.popover-arrow{bottom:calc(-1 * (var(--bs-popover-arrow-height)) - var(--bs-popover-border-width))}.bs-popover-auto[data-popper-placement^=top]>.popover-arrow::after,.bs-popover-auto[data-popper-placement^=top]>.popover-arrow::before,.bs-popover-top>.popover-arrow::after,.bs-popover-top>.popover-arrow::before{border-width:var(--bs-popover-arrow-height) calc(var(--bs-popover-arrow-width) * .5) 0}.bs-popover-auto[data-popper-placement^=top]>.popover-arrow::before,.bs-popover-top>.popover-arrow::before{bottom:0;border-top-color:var(--bs-popover-arrow-border)}.bs-popover-auto[data-popper-placement^=top]>.popover-arrow::after,.bs-popover-top>.popover-arrow::after{bottom:var(--bs-popover-border-width);border-top-color:var(--bs-popover-bg)}.bs-popover-auto[data-popper-placement^=right]>.popover-arrow,.bs-popover-end>.popover-arrow{left:calc(-1 * (var(--bs-popover-arrow-height)) - var(--bs-popover-border-width));width:var(--bs-popover-arrow-height);height:var(--bs-popover-arrow-width)}.bs-popover-auto[data-popper-placement^=right]>.popover-arrow::after,.bs-popover-auto[data-popper-placement^=right]>.popover-arrow::before,.bs-popover-end>.popover-arrow::after,.bs-popover-end>.popover-arrow::before{border-width:calc(var(--bs-popover-arrow-width) * .5) var(--bs-popover-arrow-height) calc(var(--bs-popover-arrow-width) * .5) 0}.bs-popover-auto[data-popper-placement^=right]>.popover-arrow::before,.bs-popover-end>.popover-arrow::before{left:0;border-right-color:var(--bs-popover-arrow-border)}.bs-popover-auto[data-popper-placement^=right]>.popover-arrow::after,.bs-popover-end>.popover-arrow::after{left:var(--bs-popover-border-width);border-right-color:var(--bs-popover-bg)}.bs-popover-auto[data-popper-placement^=bottom]>.popover-arrow,.bs-popover-bottom>.popover-arrow{top:calc(-1 * (var(--bs-popover-arrow-height)) - var(--bs-popover-border-width))}.bs-popover-auto[data-popper-placement^=bottom]>.popover-arrow::after,.bs-popover-auto[data-popper-placement^=bottom]>.popover-arrow::before,.bs-popover-bottom>.popover-arrow::after,.bs-popover-bottom>.popover-arrow::before{border-width:0 calc(var(--bs-popover-arrow-width) * .5) var(--bs-popover-arrow-height)}.bs-popover-auto[data-popper-placement^=bottom]>.popover-arrow::before,.bs-popover-bottom>.popover-arrow::before{top:0;border-bottom-color:var(--bs-popover-arrow-border)}.bs-popover-auto[data-popper-placement^=bottom]>.popover-arrow::after,.bs-popover-bottom>.popover-arrow::after{top:var(--bs-popover-border-width);border-bottom-color:var(--bs-popover-bg)}.bs-popover-auto[data-popper-placement^=bottom] .popover-header::before,.bs-popover-bottom .popover-header::before{position:absolute;top:0;left:50%;display:block;width:var(--bs-popover-arrow-width);margin-left:calc(-.5 * var(--bs-popover-arrow-width));content:"";border-bottom:var(--bs-popover-border-width) solid var(--bs-popover-header-bg)}.bs-popover-auto[data-popper-placement^=left]>.popover-arrow,.bs-popover-start>.popover-arrow{right:calc(-1 * (var(--bs-popover-arrow-height)) - var(--bs-popover-border-width));width:var(--bs-popover-arrow-height);height:var(--bs-popover-arrow-width)}.bs-popover-auto[data-popper-placement^=left]>.popover-arrow::after,.bs-popover-auto[data-popper-placement^=left]>.popover-arrow::before,.bs-popover-start>.popover-arrow::after,.bs-popover-start>.popover-arrow::before{border-width:calc(var(--bs-popover-arrow-width) * .5) 0 calc(var(--bs-popover-arrow-width) * .5) var(--bs-popover-arrow-height)}.bs-popover-auto[data-popper-placement^=left]>.popover-arrow::before,.bs-popover-start>.popover-arrow::before{right:0;border-left-color:var(--bs-popover-arrow-border)}.bs-popover-auto[data-popper-placement^=left]>.popover-arrow::after,.bs-popover-start>.popover-arrow::after{right:var(--bs-popover-border-width);border-left-color:var(--bs-popover-bg)}.popover-header{padding:var(--bs-popover-header-padding-y) var(--bs-popover-header-padding-x);margin-bottom:0;font-size:var(--bs-popover-header-font-size);color:var(--bs-popover-header-color);background-color:var(--bs-popover-header-bg);border-bottom:var(--bs-popover-border-width) solid var(--bs-popover-border-color);border-top-left-radius:var(--bs-popover-inner-border-radius);border-top-right-radius:var(--bs-popover-inner-border-radius)}.popover-header:empty{display:none}.popover-body{padding:var(--bs-popover-body-padding-y) var(--bs-popover-body-padding-x);color:var(--bs-popover-body-color)}.carousel{position:relative}.carousel.pointer-event{touch-action:pan-y}.carousel-inner{position:relative;width:100%;overflow:hidden}.carousel-inner::after{display:block;clear:both;content:""}.carousel-item{position:relative;display:none;float:left;width:100%;margin-right:-100%;-webkit-backface-visibility:hidden;backface-visibility:hidden;transition:transform .6s ease-in-out}@media (prefers-reduced-motion:reduce){.carousel-item{transition:none}}.carousel-item-next,.carousel-item-prev,.carousel-item.active{display:block}.active.carousel-item-end,.carousel-item-next:not(.carousel-item-start){transform:translateX(100%)}.active.carousel-item-start,.carousel-item-prev:not(.carousel-item-end){transform:translateX(-100%)}.carousel-fade .carousel-item{opacity:0;transition-property:opacity;transform:none}.carousel-fade .carousel-item-next.carousel-item-start,.carousel-fade .carousel-item-prev.carousel-item-end,.carousel-fade .carousel-item.active{z-index:1;opacity:1}.carousel-fade .active.carousel-item-end,.carousel-fade .active.carousel-item-start{z-index:0;opacity:0;transition:opacity 0s .6s}@media (prefers-reduced-motion:reduce){.carousel-fade .active.carousel-item-end,.carousel-fade .active.carousel-item-start{transition:none}}.carousel-control-next,.carousel-control-prev{position:absolute;top:0;bottom:0;z-index:1;display:flex;align-items:center;justify-content:center;width:15%;padding:0;color:#fff;text-align:center;background:0 0;border:0;opacity:.5;transition:opacity .15s ease}@media (prefers-reduced-motion:reduce){.carousel-control-next,.carousel-control-prev{transition:none}}.carousel-control-next:focus,.carousel-control-next:hover,.carousel-control-prev:focus,.carousel-control-prev:hover{color:#fff;text-decoration:none;outline:0;opacity:.9}.carousel-control-prev{left:0}.carousel-control-next{right:0}.carousel-control-next-icon,.carousel-control-prev-icon{display:inline-block;width:2rem;height:2rem;background-repeat:no-repeat;background-position:50%;background-size:100% 100%}.carousel-control-prev-icon{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%23fff'%3e%3cpath d='M11.354 1.646a.5.5 0 0 1 0 .708L5.707 8l5.647 5.646a.5.5 0 0 1-.708.708l-6-6a.5.5 0 0 1 0-.708l6-6a.5.5 0 0 1 .708 0z'/%3e%3c/svg%3e")}.carousel-control-next-icon{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%23fff'%3e%3cpath d='M4.646 1.646a.5.5 0 0 1 .708 0l6 6a.5.5 0 0 1 0 .708l-6 6a.5.5 0 0 1-.708-.708L10.293 8 4.646 2.354a.5.5 0 0 1 0-.708z'/%3e%3c/svg%3e")}.carousel-indicators{position:absolute;right:0;bottom:0;left:0;z-index:2;display:flex;justify-content:center;padding:0;margin-right:15%;margin-bottom:1rem;margin-left:15%}.carousel-indicators [data-bs-target]{box-sizing:content-box;flex:0 1 auto;width:30px;height:3px;padding:0;margin-right:3px;margin-left:3px;text-indent:-999px;cursor:pointer;background-color:#fff;background-clip:padding-box;border:0;border-top:10px solid transparent;border-bottom:10px solid transparent;opacity:.5;transition:opacity .6s ease}@media (prefers-reduced-motion:reduce){.carousel-indicators [data-bs-target]{transition:none}}.carousel-indicators .active{opacity:1}.carousel-caption{position:absolute;right:15%;bottom:1.25rem;left:15%;padding-top:1.25rem;padding-bottom:1.25rem;color:#fff;text-align:center}.carousel-dark .carousel-control-next-icon,.carousel-dark .carousel-control-prev-icon{filter:invert(1) grayscale(100)}.carousel-dark .carousel-indicators [data-bs-target]{background-color:#000}.carousel-dark .carousel-caption{color:#000}[data-bs-theme=dark] .carousel .carousel-control-next-icon,[data-bs-theme=dark] .carousel .carousel-control-prev-icon,[data-bs-theme=dark].carousel .carousel-control-next-icon,[data-bs-theme=dark].carousel .carousel-control-prev-icon{filter:invert(1) grayscale(100)}[data-bs-theme=dark] .carousel .carousel-indicators [data-bs-target],[data-bs-theme=dark].carousel .carousel-indicators [data-bs-target]{background-color:#000}[data-bs-theme=dark] .carousel .carousel-caption,[data-bs-theme=dark].carousel .carousel-caption{color:#000}.spinner-border,.spinner-grow{display:inline-block;width:var(--bs-spinner-width);height:var(--bs-spinner-height);vertical-align:var(--bs-spinner-vertical-align);border-radius:50%;animation:var(--bs-spinner-animation-speed) linear infinite var(--bs-spinner-animation-name)}@keyframes spinner-border{to{transform:rotate(360deg)}}.spinner-border{--bs-spinner-width:2rem;--bs-spinner-height:2rem;--bs-spinner-vertical-align:-0.125em;--bs-spinner-border-width:0.25em;--bs-spinner-animation-speed:0.75s;--bs-spinner-animation-name:spinner-border;border:var(--bs-spinner-border-width) solid currentcolor;border-right-color:transparent}.spinner-border-sm{--bs-spinner-width:1rem;--bs-spinner-height:1rem;--bs-spinner-border-width:0.2em}@keyframes spinner-grow{0%{transform:scale(0)}50%{opacity:1;transform:none}}.spinner-grow{--bs-spinner-width:2rem;--bs-spinner-height:2rem;--bs-spinner-vertical-align:-0.125em;--bs-spinner-animation-speed:0.75s;--bs-spinner-animation-name:spinner-grow;background-color:currentcolor;opacity:0}.spinner-grow-sm{--bs-spinner-width:1rem;--bs-spinner-height:1rem}@media (prefers-reduced-motion:reduce){.spinner-border,.spinner-grow{--bs-spinner-animation-speed:1.5s}}.offcanvas,.offcanvas-lg,.offcanvas-md,.offcanvas-sm,.offcanvas-xl,.offcanvas-xxl{--bs-offcanvas-zindex:1045;--bs-offcanvas-width:400px;--bs-offcanvas-height:30vh;--bs-offcanvas-padding-x:1rem;--bs-offcanvas-padding-y:1rem;--bs-offcanvas-color:var(--bs-body-color);--bs-offcanvas-bg:var(--bs-body-bg);--bs-offcanvas-border-width:var(--bs-border-width);--bs-offcanvas-border-color:var(--bs-border-color-translucent);--bs-offcanvas-box-shadow:0 0.125rem 0.25rem rgba(0, 0, 0, 0.075);--bs-offcanvas-transition:transform 0.3s ease-in-out;--bs-offcanvas-title-line-height:1.5}@media (max-width:575.98px){.offcanvas-sm{position:fixed;bottom:0;z-index:var(--bs-offcanvas-zindex);display:flex;flex-direction:column;max-width:100%;color:var(--bs-offcanvas-color);visibility:hidden;background-color:var(--bs-offcanvas-bg);background-clip:padding-box;outline:0;transition:var(--bs-offcanvas-transition)}}@media (max-width:575.98px) and (prefers-reduced-motion:reduce){.offcanvas-sm{transition:none}}@media (max-width:575.98px){.offcanvas-sm.offcanvas-start{top:0;left:0;width:var(--bs-offcanvas-width);border-right:var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);transform:translateX(-100%)}}@media (max-width:575.98px){.offcanvas-sm.offcanvas-end{top:0;right:0;width:var(--bs-offcanvas-width);border-left:var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);transform:translateX(100%)}}@media (max-width:575.98px){.offcanvas-sm.offcanvas-top{top:0;right:0;left:0;height:var(--bs-offcanvas-height);max-height:100%;border-bottom:var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);transform:translateY(-100%)}}@media (max-width:575.98px){.offcanvas-sm.offcanvas-bottom{right:0;left:0;height:var(--bs-offcanvas-height);max-height:100%;border-top:var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);transform:translateY(100%)}}@media (max-width:575.98px){.offcanvas-sm.show:not(.hiding),.offcanvas-sm.showing{transform:none}}@media (max-width:575.98px){.offcanvas-sm.hiding,.offcanvas-sm.show,.offcanvas-sm.showing{visibility:visible}}@media (min-width:576px){.offcanvas-sm{--bs-offcanvas-height:auto;--bs-offcanvas-border-width:0;background-color:transparent!important}.offcanvas-sm .offcanvas-header{display:none}.offcanvas-sm .offcanvas-body{display:flex;flex-grow:0;padding:0;overflow-y:visible;background-color:transparent!important}}@media (max-width:767.98px){.offcanvas-md{position:fixed;bottom:0;z-index:var(--bs-offcanvas-zindex);display:flex;flex-direction:column;max-width:100%;color:var(--bs-offcanvas-color);visibility:hidden;background-color:var(--bs-offcanvas-bg);background-clip:padding-box;outline:0;transition:var(--bs-offcanvas-transition)}}@media (max-width:767.98px) and (prefers-reduced-motion:reduce){.offcanvas-md{transition:none}}@media (max-width:767.98px){.offcanvas-md.offcanvas-start{top:0;left:0;width:var(--bs-offcanvas-width);border-right:var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);transform:translateX(-100%)}}@media (max-width:767.98px){.offcanvas-md.offcanvas-end{top:0;right:0;width:var(--bs-offcanvas-width);border-left:var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);transform:translateX(100%)}}@media (max-width:767.98px){.offcanvas-md.offcanvas-top{top:0;right:0;left:0;height:var(--bs-offcanvas-height);max-height:100%;border-bottom:var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);transform:translateY(-100%)}}@media (max-width:767.98px){.offcanvas-md.offcanvas-bottom{right:0;left:0;height:var(--bs-offcanvas-height);max-height:100%;border-top:var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);transform:translateY(100%)}}@media (max-width:767.98px){.offcanvas-md.show:not(.hiding),.offcanvas-md.showing{transform:none}}@media (max-width:767.98px){.offcanvas-md.hiding,.offcanvas-md.show,.offcanvas-md.showing{visibility:visible}}@media (min-width:768px){.offcanvas-md{--bs-offcanvas-height:auto;--bs-offcanvas-border-width:0;background-color:transparent!important}.offcanvas-md .offcanvas-header{display:none}.offcanvas-md .offcanvas-body{display:flex;flex-grow:0;padding:0;overflow-y:visible;background-color:transparent!important}}@media (max-width:991.98px){.offcanvas-lg{position:fixed;bottom:0;z-index:var(--bs-offcanvas-zindex);display:flex;flex-direction:column;max-width:100%;color:var(--bs-offcanvas-color);visibility:hidden;background-color:var(--bs-offcanvas-bg);background-clip:padding-box;outline:0;transition:var(--bs-offcanvas-transition)}}@media (max-width:991.98px) and (prefers-reduced-motion:reduce){.offcanvas-lg{transition:none}}@media (max-width:991.98px){.offcanvas-lg.offcanvas-start{top:0;left:0;width:var(--bs-offcanvas-width);border-right:var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);transform:translateX(-100%)}}@media (max-width:991.98px){.offcanvas-lg.offcanvas-end{top:0;right:0;width:var(--bs-offcanvas-width);border-left:var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);transform:translateX(100%)}}@media (max-width:991.98px){.offcanvas-lg.offcanvas-top{top:0;right:0;left:0;height:var(--bs-offcanvas-height);max-height:100%;border-bottom:var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);transform:translateY(-100%)}}@media (max-width:991.98px){.offcanvas-lg.offcanvas-bottom{right:0;left:0;height:var(--bs-offcanvas-height);max-height:100%;border-top:var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);transform:translateY(100%)}}@media (max-width:991.98px){.offcanvas-lg.show:not(.hiding),.offcanvas-lg.showing{transform:none}}@media (max-width:991.98px){.offcanvas-lg.hiding,.offcanvas-lg.show,.offcanvas-lg.showing{visibility:visible}}@media (min-width:992px){.offcanvas-lg{--bs-offcanvas-height:auto;--bs-offcanvas-border-width:0;background-color:transparent!important}.offcanvas-lg .offcanvas-header{display:none}.offcanvas-lg .offcanvas-body{display:flex;flex-grow:0;padding:0;overflow-y:visible;background-color:transparent!important}}@media (max-width:1199.98px){.offcanvas-xl{position:fixed;bottom:0;z-index:var(--bs-offcanvas-zindex);display:flex;flex-direction:column;max-width:100%;color:var(--bs-offcanvas-color);visibility:hidden;background-color:var(--bs-offcanvas-bg);background-clip:padding-box;outline:0;transition:var(--bs-offcanvas-transition)}}@media (max-width:1199.98px) and (prefers-reduced-motion:reduce){.offcanvas-xl{transition:none}}@media (max-width:1199.98px){.offcanvas-xl.offcanvas-start{top:0;left:0;width:var(--bs-offcanvas-width);border-right:var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);transform:translateX(-100%)}}@media (max-width:1199.98px){.offcanvas-xl.offcanvas-end{top:0;right:0;width:var(--bs-offcanvas-width);border-left:var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);transform:translateX(100%)}}@media (max-width:1199.98px){.offcanvas-xl.offcanvas-top{top:0;right:0;left:0;height:var(--bs-offcanvas-height);max-height:100%;border-bottom:var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);transform:translateY(-100%)}}@media (max-width:1199.98px){.offcanvas-xl.offcanvas-bottom{right:0;left:0;height:var(--bs-offcanvas-height);max-height:100%;border-top:var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);transform:translateY(100%)}}@media (max-width:1199.98px){.offcanvas-xl.show:not(.hiding),.offcanvas-xl.showing{transform:none}}@media (max-width:1199.98px){.offcanvas-xl.hiding,.offcanvas-xl.show,.offcanvas-xl.showing{visibility:visible}}@media (min-width:1200px){.offcanvas-xl{--bs-offcanvas-height:auto;--bs-offcanvas-border-width:0;background-color:transparent!important}.offcanvas-xl .offcanvas-header{display:none}.offcanvas-xl .offcanvas-body{display:flex;flex-grow:0;padding:0;overflow-y:visible;background-color:transparent!important}}@media (max-width:1399.98px){.offcanvas-xxl{position:fixed;bottom:0;z-index:var(--bs-offcanvas-zindex);display:flex;flex-direction:column;max-width:100%;color:var(--bs-offcanvas-color);visibility:hidden;background-color:var(--bs-offcanvas-bg);background-clip:padding-box;outline:0;transition:var(--bs-offcanvas-transition)}}@media (max-width:1399.98px) and (prefers-reduced-motion:reduce){.offcanvas-xxl{transition:none}}@media (max-width:1399.98px){.offcanvas-xxl.offcanvas-start{top:0;left:0;width:var(--bs-offcanvas-width);border-right:var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);transform:translateX(-100%)}}@media (max-width:1399.98px){.offcanvas-xxl.offcanvas-end{top:0;right:0;width:var(--bs-offcanvas-width);border-left:var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);transform:translateX(100%)}}@media (max-width:1399.98px){.offcanvas-xxl.offcanvas-top{top:0;right:0;left:0;height:var(--bs-offcanvas-height);max-height:100%;border-bottom:var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);transform:translateY(-100%)}}@media (max-width:1399.98px){.offcanvas-xxl.offcanvas-bottom{right:0;left:0;height:var(--bs-offcanvas-height);max-height:100%;border-top:var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);transform:translateY(100%)}}@media (max-width:1399.98px){.offcanvas-xxl.show:not(.hiding),.offcanvas-xxl.showing{transform:none}}@media (max-width:1399.98px){.offcanvas-xxl.hiding,.offcanvas-xxl.show,.offcanvas-xxl.showing{visibility:visible}}@media (min-width:1400px){.offcanvas-xxl{--bs-offcanvas-height:auto;--bs-offcanvas-border-width:0;background-color:transparent!important}.offcanvas-xxl .offcanvas-header{display:none}.offcanvas-xxl .offcanvas-body{display:flex;flex-grow:0;padding:0;overflow-y:visible;background-color:transparent!important}}.offcanvas{position:fixed;bottom:0;z-index:var(--bs-offcanvas-zindex);display:flex;flex-direction:column;max-width:100%;color:var(--bs-offcanvas-color);visibility:hidden;background-color:var(--bs-offcanvas-bg);background-clip:padding-box;outline:0;transition:var(--bs-offcanvas-transition)}@media (prefers-reduced-motion:reduce){.offcanvas{transition:none}}.offcanvas.offcanvas-start{top:0;left:0;width:var(--bs-offcanvas-width);border-right:var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);transform:translateX(-100%)}.offcanvas.offcanvas-end{top:0;right:0;width:var(--bs-offcanvas-width);border-left:var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);transform:translateX(100%)}.offcanvas.offcanvas-top{top:0;right:0;left:0;height:var(--bs-offcanvas-height);max-height:100%;border-bottom:var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);transform:translateY(-100%)}.offcanvas.offcanvas-bottom{right:0;left:0;height:var(--bs-offcanvas-height);max-height:100%;border-top:var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);transform:translateY(100%)}.offcanvas.show:not(.hiding),.offcanvas.showing{transform:none}.offcanvas.hiding,.offcanvas.show,.offcanvas.showing{visibility:visible}.offcanvas-backdrop{position:fixed;top:0;left:0;z-index:1040;width:100vw;height:100vh;background-color:#000}.offcanvas-backdrop.fade{opacity:0}.offcanvas-backdrop.show{opacity:.5}.offcanvas-header{display:flex;align-items:center;justify-content:space-between;padding:var(--bs-offcanvas-padding-y) var(--bs-offcanvas-padding-x)}.offcanvas-header .btn-close{padding:calc(var(--bs-offcanvas-padding-y) * .5) calc(var(--bs-offcanvas-padding-x) * .5);margin-top:calc(-.5 * var(--bs-offcanvas-padding-y));margin-right:calc(-.5 * var(--bs-offcanvas-padding-x));margin-bottom:calc(-.5 * var(--bs-offcanvas-padding-y))}.offcanvas-title{margin-bottom:0;line-height:var(--bs-offcanvas-title-line-height)}.offcanvas-body{flex-grow:1;padding:var(--bs-offcanvas-padding-y) var(--bs-offcanvas-padding-x);overflow-y:auto}.placeholder{display:inline-block;min-height:1em;vertical-align:middle;cursor:wait;background-color:currentcolor;opacity:.5}.placeholder.btn::before{display:inline-block;content:""}.placeholder-xs{min-height:.6em}.placeholder-sm{min-height:.8em}.placeholder-lg{min-height:1.2em}.placeholder-glow .placeholder{animation:placeholder-glow 2s ease-in-out infinite}@keyframes placeholder-glow{50%{opacity:.2}}.placeholder-wave{-webkit-mask-image:linear-gradient(130deg,#000 55%,rgba(0,0,0,0.8) 75%,#000 95%);mask-image:linear-gradient(130deg,#000 55%,rgba(0,0,0,0.8) 75%,#000 95%);-webkit-mask-size:200% 100%;mask-size:200% 100%;animation:placeholder-wave 2s linear infinite}@keyframes placeholder-wave{100%{-webkit-mask-position:-200% 0%;mask-position:-200% 0%}}.clearfix::after{display:block;clear:both;content:""}.text-bg-primary{color:#fff!important;background-color:RGBA(13,110,253,var(--bs-bg-opacity,1))!important}.text-bg-secondary{color:#fff!important;background-color:RGBA(108,117,125,var(--bs-bg-opacity,1))!important}.text-bg-success{color:#fff!important;background-color:RGBA(25,135,84,var(--bs-bg-opacity,1))!important}.text-bg-info{color:#000!important;background-color:RGBA(13,202,240,var(--bs-bg-opacity,1))!important}.text-bg-warning{color:#000!important;background-color:RGBA(255,193,7,var(--bs-bg-opacity,1))!important}.text-bg-danger{color:#fff!important;background-color:RGBA(220,53,69,var(--bs-bg-opacity,1))!important}.text-bg-light{color:#000!important;background-color:RGBA(248,249,250,var(--bs-bg-opacity,1))!important}.text-bg-dark{color:#fff!important;background-color:RGBA(33,37,41,var(--bs-bg-opacity,1))!important}.link-primary{color:RGBA(var(--bs-primary-rgb,var(--bs-link-opacity,1)));-webkit-text-decoration-color:RGBA(var(--bs-primary-rgb),var(--bs-link-underline-opacity,1));text-decoration-color:RGBA(var(--bs-primary-rgb),var(--bs-link-underline-opacity,1))}.link-primary:focus,.link-primary:hover{color:RGBA(10,88,202,var(--bs-link-opacity,1));-webkit-text-decoration-color:RGBA(10,88,202,var(--bs-link-underline-opacity,1));text-decoration-color:RGBA(10,88,202,var(--bs-link-underline-opacity,1))}.link-secondary{color:RGBA(var(--bs-secondary-rgb,var(--bs-link-opacity,1)));-webkit-text-decoration-color:RGBA(var(--bs-secondary-rgb),var(--bs-link-underline-opacity,1));text-decoration-color:RGBA(var(--bs-secondary-rgb),var(--bs-link-underline-opacity,1))}.link-secondary:focus,.link-secondary:hover{color:RGBA(86,94,100,var(--bs-link-opacity,1));-webkit-text-decoration-color:RGBA(86,94,100,var(--bs-link-underline-opacity,1));text-decoration-color:RGBA(86,94,100,var(--bs-link-underline-opacity,1))}.link-success{color:RGBA(var(--bs-success-rgb,var(--bs-link-opacity,1)));-webkit-text-decoration-color:RGBA(var(--bs-success-rgb),var(--bs-link-underline-opacity,1));text-decoration-color:RGBA(var(--bs-success-rgb),var(--bs-link-underline-opacity,1))}.link-success:focus,.link-success:hover{color:RGBA(20,108,67,var(--bs-link-opacity,1));-webkit-text-decoration-color:RGBA(20,108,67,var(--bs-link-underline-opacity,1));text-decoration-color:RGBA(20,108,67,var(--bs-link-underline-opacity,1))}.link-info{color:RGBA(var(--bs-info-rgb,var(--bs-link-opacity,1)));-webkit-text-decoration-color:RGBA(var(--bs-info-rgb),var(--bs-link-underline-opacity,1));text-decoration-color:RGBA(var(--bs-info-rgb),var(--bs-link-underline-opacity,1))}.link-info:focus,.link-info:hover{color:RGBA(61,213,243,var(--bs-link-opacity,1));-webkit-text-decoration-color:RGBA(61,213,243,var(--bs-link-underline-opacity,1));text-decoration-color:RGBA(61,213,243,var(--bs-link-underline-opacity,1))}.link-warning{color:RGBA(var(--bs-warning-rgb,var(--bs-link-opacity,1)));-webkit-text-decoration-color:RGBA(var(--bs-warning-rgb),var(--bs-link-underline-opacity,1));text-decoration-color:RGBA(var(--bs-warning-rgb),var(--bs-link-underline-opacity,1))}.link-warning:focus,.link-warning:hover{color:RGBA(255,205,57,var(--bs-link-opacity,1));-webkit-text-decoration-color:RGBA(255,205,57,var(--bs-link-underline-opacity,1));text-decoration-color:RGBA(255,205,57,var(--bs-link-underline-opacity,1))}.link-danger{color:RGBA(var(--bs-danger-rgb,var(--bs-link-opacity,1)));-webkit-text-decoration-color:RGBA(var(--bs-danger-rgb),var(--bs-link-underline-opacity,1));text-decoration-color:RGBA(var(--bs-danger-rgb),var(--bs-link-underline-opacity,1))}.link-danger:focus,.link-danger:hover{color:RGBA(176,42,55,var(--bs-link-opacity,1));-webkit-text-decoration-color:RGBA(176,42,55,var(--bs-link-underline-opacity,1));text-decoration-color:RGBA(176,42,55,var(--bs-link-underline-opacity,1))}.link-light{color:RGBA(var(--bs-light-rgb,var(--bs-link-opacity,1)));-webkit-text-decoration-color:RGBA(var(--bs-light-rgb),var(--bs-link-underline-opacity,1));text-decoration-color:RGBA(var(--bs-light-rgb),var(--bs-link-underline-opacity,1))}.link-light:focus,.link-light:hover{color:RGBA(249,250,251,var(--bs-link-opacity,1));-webkit-text-decoration-color:RGBA(249,250,251,var(--bs-link-underline-opacity,1));text-decoration-color:RGBA(249,250,251,var(--bs-link-underline-opacity,1))}.link-dark{color:RGBA(var(--bs-dark-rgb,var(--bs-link-opacity,1)));-webkit-text-decoration-color:RGBA(var(--bs-dark-rgb),var(--bs-link-underline-opacity,1));text-decoration-color:RGBA(var(--bs-dark-rgb),var(--bs-link-underline-opacity,1))}.link-dark:focus,.link-dark:hover{color:RGBA(26,30,33,var(--bs-link-opacity,1));-webkit-text-decoration-color:RGBA(26,30,33,var(--bs-link-underline-opacity,1));text-decoration-color:RGBA(26,30,33,var(--bs-link-underline-opacity,1))}.link-body-emphasis{color:RGBA(var(--bs-emphasis-color-rgb),var(--bs-link-opacity,1));-webkit-text-decoration-color:RGBA(var(--bs-emphasis-color-rgb),var(--bs-link-underline-opacity,1));text-decoration-color:RGBA(var(--bs-emphasis-color-rgb),var(--bs-link-underline-opacity,1))}.link-body-emphasis:focus,.link-body-emphasis:hover{color:RGBA(var(--bs-emphasis-color-rgb),var(--bs-link-opacity,.75));-webkit-text-decoration-color:RGBA(var(--bs-emphasis-color-rgb),var(--bs-link-underline-opacity,0.75));text-decoration-color:RGBA(var(--bs-emphasis-color-rgb),var(--bs-link-underline-opacity,0.75))}.focus-ring:focus{outline:0;box-shadow:var(--bs-focus-ring-x,0) var(--bs-focus-ring-y,0) var(--bs-focus-ring-blur,0) var(--bs-focus-ring-width) var(--bs-focus-ring-color)}.icon-link{display:inline-flex;gap:.375rem;align-items:center;-webkit-text-decoration-color:rgba(var(--bs-link-color-rgb),var(--bs-link-opacity,0.5));text-decoration-color:rgba(var(--bs-link-color-rgb),var(--bs-link-opacity,0.5));text-underline-offset:0.25em;-webkit-backface-visibility:hidden;backface-visibility:hidden}.icon-link>.bi{flex-shrink:0;width:1em;height:1em;fill:currentcolor;transition:.2s ease-in-out transform}@media (prefers-reduced-motion:reduce){.icon-link>.bi{transition:none}}.icon-link-hover:focus-visible>.bi,.icon-link-hover:hover>.bi{transform:var(--bs-icon-link-transform,translate3d(.25em,0,0))}.ratio{position:relative;width:100%}.ratio::before{display:block;padding-top:var(--bs-aspect-ratio);content:""}.ratio>*{position:absolute;top:0;left:0;width:100%;height:100%}.ratio-1x1{--bs-aspect-ratio:100%}.ratio-4x3{--bs-aspect-ratio:75%}.ratio-16x9{--bs-aspect-ratio:56.25%}.ratio-21x9{--bs-aspect-ratio:42.8571428571%}.fixed-top{position:fixed;top:0;right:0;left:0;z-index:1030}.fixed-bottom{position:fixed;right:0;bottom:0;left:0;z-index:1030}.sticky-top{position:-webkit-sticky;position:sticky;top:0;z-index:1020}.sticky-bottom{position:-webkit-sticky;position:sticky;bottom:0;z-index:1020}@media (min-width:576px){.sticky-sm-top{position:-webkit-sticky;position:sticky;top:0;z-index:1020}.sticky-sm-bottom{position:-webkit-sticky;position:sticky;bottom:0;z-index:1020}}@media (min-width:768px){.sticky-md-top{position:-webkit-sticky;position:sticky;top:0;z-index:1020}.sticky-md-bottom{position:-webkit-sticky;position:sticky;bottom:0;z-index:1020}}@media (min-width:992px){.sticky-lg-top{position:-webkit-sticky;position:sticky;top:0;z-index:1020}.sticky-lg-bottom{position:-webkit-sticky;position:sticky;bottom:0;z-index:1020}}@media (min-width:1200px){.sticky-xl-top{position:-webkit-sticky;position:sticky;top:0;z-index:1020}.sticky-xl-bottom{position:-webkit-sticky;position:sticky;bottom:0;z-index:1020}}@media (min-width:1400px){.sticky-xxl-top{position:-webkit-sticky;position:sticky;top:0;z-index:1020}.sticky-xxl-bottom{position:-webkit-sticky;position:sticky;bottom:0;z-index:1020}}.hstack{display:flex;flex-direction:row;align-items:center;align-self:stretch}.vstack{display:flex;flex:1 1 auto;flex-direction:column;align-self:stretch}.visually-hidden,.visually-hidden-focusable:not(:focus):not(:focus-within){width:1px!important;height:1px!important;padding:0!important;margin:-1px!important;overflow:hidden!important;clip:rect(0,0,0,0)!important;white-space:nowrap!important;border:0!important}.visually-hidden-focusable:not(:focus):not(:focus-within):not(caption),.visually-hidden:not(caption){position:absolute!important}.stretched-link::after{position:absolute;top:0;right:0;bottom:0;left:0;z-index:1;content:""}.text-truncate{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.vr{display:inline-block;align-self:stretch;width:1px;min-height:1em;background-color:currentcolor;opacity:.25}.align-baseline{vertical-align:baseline!important}.align-top{vertical-align:top!important}.align-middle{vertical-align:middle!important}.align-bottom{vertical-align:bottom!important}.align-text-bottom{vertical-align:text-bottom!important}.align-text-top{vertical-align:text-top!important}.float-start{float:left!important}.float-end{float:right!important}.float-none{float:none!important}.object-fit-contain{-o-object-fit:contain!important;object-fit:contain!important}.object-fit-cover{-o-object-fit:cover!important;object-fit:cover!important}.object-fit-fill{-o-object-fit:fill!important;object-fit:fill!important}.object-fit-scale{-o-object-fit:scale-down!important;object-fit:scale-down!important}.object-fit-none{-o-object-fit:none!important;object-fit:none!important}.opacity-0{opacity:0!important}.opacity-25{opacity:.25!important}.opacity-50{opacity:.5!important}.opacity-75{opacity:.75!important}.opacity-100{opacity:1!important}.overflow-auto{overflow:auto!important}.overflow-hidden{overflow:hidden!important}.overflow-visible{overflow:visible!important}.overflow-scroll{overflow:scroll!important}.overflow-x-auto{overflow-x:auto!important}.overflow-x-hidden{overflow-x:hidden!important}.overflow-x-visible{overflow-x:visible!important}.overflow-x-scroll{overflow-x:scroll!important}.overflow-y-auto{overflow-y:auto!important}.overflow-y-hidden{overflow-y:hidden!important}.overflow-y-visible{overflow-y:visible!important}.overflow-y-scroll{overflow-y:scroll!important}.d-inline{display:inline!important}.d-inline-block{display:inline-block!important}.d-block{display:block!important}.d-grid{display:grid!important}.d-table{display:table!important}.d-table-row{display:table-row!important}.d-table-cell{display:table-cell!important}.d-flex{display:flex!important}.d-inline-flex{display:inline-flex!important}.d-none{display:none!important}.shadow{box-shadow:0 .5rem 1rem rgba(0,0,0,.15)!important}.shadow-sm{box-shadow:0 .125rem .25rem rgba(0,0,0,.075)!important}.shadow-lg{box-shadow:0 1rem 3rem rgba(0,0,0,.175)!important}.shadow-none{box-shadow:none!important}.focus-ring-primary{--bs-focus-ring-color:rgba(var(--bs-primary-rgb), var(--bs-focus-ring-opacity))}.focus-ring-secondary{--bs-focus-ring-color:rgba(var(--bs-secondary-rgb), var(--bs-focus-ring-opacity))}.focus-ring-success{--bs-focus-ring-color:rgba(var(--bs-success-rgb), var(--bs-focus-ring-opacity))}.focus-ring-info{--bs-focus-ring-color:rgba(var(--bs-info-rgb), var(--bs-focus-ring-opacity))}.focus-ring-warning{--bs-focus-ring-color:rgba(var(--bs-warning-rgb), var(--bs-focus-ring-opacity))}.focus-ring-danger{--bs-focus-ring-color:rgba(var(--bs-danger-rgb), var(--bs-focus-ring-opacity))}.focus-ring-light{--bs-focus-ring-color:rgba(var(--bs-light-rgb), var(--bs-focus-ring-opacity))}.focus-ring-dark{--bs-focus-ring-color:rgba(var(--bs-dark-rgb), var(--bs-focus-ring-opacity))}.position-static{position:static!important}.position-relative{position:relative!important}.position-absolute{position:absolute!important}.position-fixed{position:fixed!important}.position-sticky{position:-webkit-sticky!important;position:sticky!important}.top-0{top:0!important}.top-50{top:50%!important}.top-100{top:100%!important}.bottom-0{bottom:0!important}.bottom-50{bottom:50%!important}.bottom-100{bottom:100%!important}.start-0{left:0!important}.start-50{left:50%!important}.start-100{left:100%!important}.end-0{right:0!important}.end-50{right:50%!important}.end-100{right:100%!important}.translate-middle{transform:translate(-50%,-50%)!important}.translate-middle-x{transform:translateX(-50%)!important}.translate-middle-y{transform:translateY(-50%)!important}.border{border:var(--bs-border-width) var(--bs-border-style) var(--bs-border-color)!important}.border-0{border:0!important}.border-top{border-top:var(--bs-border-width) var(--bs-border-style) var(--bs-border-color)!important}.border-top-0{border-top:0!important}.border-end{border-right:var(--bs-border-width) var(--bs-border-style) var(--bs-border-color)!important}.border-end-0{border-right:0!important}.border-bottom{border-bottom:var(--bs-border-width) var(--bs-border-style) var(--bs-border-color)!important}.border-bottom-0{border-bottom:0!important}.border-start{border-left:var(--bs-border-width) var(--bs-border-style) var(--bs-border-color)!important}.border-start-0{border-left:0!important}.border-primary{--bs-border-opacity:1;border-color:rgba(var(--bs-primary-rgb),var(--bs-border-opacity))!important}.border-secondary{--bs-border-opacity:1;border-color:rgba(var(--bs-secondary-rgb),var(--bs-border-opacity))!important}.border-success{--bs-border-opacity:1;border-color:rgba(var(--bs-success-rgb),var(--bs-border-opacity))!important}.border-info{--bs-border-opacity:1;border-color:rgba(var(--bs-info-rgb),var(--bs-border-opacity))!important}.border-warning{--bs-border-opacity:1;border-color:rgba(var(--bs-warning-rgb),var(--bs-border-opacity))!important}.border-danger{--bs-border-opacity:1;border-color:rgba(var(--bs-danger-rgb),var(--bs-border-opacity))!important}.border-light{--bs-border-opacity:1;border-color:rgba(var(--bs-light-rgb),var(--bs-border-opacity))!important}.border-dark{--bs-border-opacity:1;border-color:rgba(var(--bs-dark-rgb),var(--bs-border-opacity))!important}.border-black{--bs-border-opacity:1;border-color:rgba(var(--bs-black-rgb),var(--bs-border-opacity))!important}.border-white{--bs-border-opacity:1;border-color:rgba(var(--bs-white-rgb),var(--bs-border-opacity))!important}.border-primary-subtle{border-color:var(--bs-primary-border-subtle)!important}.border-secondary-subtle{border-color:var(--bs-secondary-border-subtle)!important}.border-success-subtle{border-color:var(--bs-success-border-subtle)!important}.border-info-subtle{border-color:var(--bs-info-border-subtle)!important}.border-warning-subtle{border-color:var(--bs-warning-border-subtle)!important}.border-danger-subtle{border-color:var(--bs-danger-border-subtle)!important}.border-light-subtle{border-color:var(--bs-light-border-subtle)!important}.border-dark-subtle{border-color:var(--bs-dark-border-subtle)!important}.border-1{border-width:1px!important}.border-2{border-width:2px!important}.border-3{border-width:3px!important}.border-4{border-width:4px!important}.border-5{border-width:5px!important}.border-opacity-10{--bs-border-opacity:0.1}.border-opacity-25{--bs-border-opacity:0.25}.border-opacity-50{--bs-border-opacity:0.5}.border-opacity-75{--bs-border-opacity:0.75}.border-opacity-100{--bs-border-opacity:1}.w-25{width:25%!important}.w-50{width:50%!important}.w-75{width:75%!important}.w-100{width:100%!important}.w-auto{width:auto!important}.mw-100{max-width:100%!important}.vw-100{width:100vw!important}.min-vw-100{min-width:100vw!important}.h-25{height:25%!important}.h-50{height:50%!important}.h-75{height:75%!important}.h-100{height:100%!important}.h-auto{height:auto!important}.mh-100{max-height:100%!important}.vh-100{height:100vh!important}.min-vh-100{min-height:100vh!important}.flex-fill{flex:1 1 auto!important}.flex-row{flex-direction:row!important}.flex-column{flex-direction:column!important}.flex-row-reverse{flex-direction:row-reverse!important}.flex-column-reverse{flex-direction:column-reverse!important}.flex-grow-0{flex-grow:0!important}.flex-grow-1{flex-grow:1!important}.flex-shrink-0{flex-shrink:0!important}.flex-shrink-1{flex-shrink:1!important}.flex-wrap{flex-wrap:wrap!important}.flex-nowrap{flex-wrap:nowrap!important}.flex-wrap-reverse{flex-wrap:wrap-reverse!important}.justify-content-start{justify-content:flex-start!important}.justify-content-end{justify-content:flex-end!important}.justify-content-center{justify-content:center!important}.justify-content-between{justify-content:space-between!important}.justify-content-around{justify-content:space-around!important}.justify-content-evenly{justify-content:space-evenly!important}.align-items-start{align-items:flex-start!important}.align-items-end{align-items:flex-end!important}.align-items-center{align-items:center!important}.align-items-baseline{align-items:baseline!important}.align-items-stretch{align-items:stretch!important}.align-content-start{align-content:flex-start!important}.align-content-end{align-content:flex-end!important}.align-content-center{align-content:center!important}.align-content-between{align-content:space-between!important}.align-content-around{align-content:space-around!important}.align-content-stretch{align-content:stretch!important}.align-self-auto{align-self:auto!important}.align-self-start{align-self:flex-start!important}.align-self-end{align-self:flex-end!important}.align-self-center{align-self:center!important}.align-self-baseline{align-self:baseline!important}.align-self-stretch{align-self:stretch!important}.order-first{order:-1!important}.order-0{order:0!important}.order-1{order:1!important}.order-2{order:2!important}.order-3{order:3!important}.order-4{order:4!important}.order-5{order:5!important}.order-last{order:6!important}.m-0{margin:0!important}.m-1{margin:.25rem!important}.m-2{margin:.5rem!important}.m-3{margin:1rem!important}.m-4{margin:1.5rem!important}.m-5{margin:3rem!important}.m-auto{margin:auto!important}.mx-0{margin-right:0!important;margin-left:0!important}.mx-1{margin-right:.25rem!important;margin-left:.25rem!important}.mx-2{margin-right:.5rem!important;margin-left:.5rem!important}.mx-3{margin-right:1rem!important;margin-left:1rem!important}.mx-4{margin-right:1.5rem!important;margin-left:1.5rem!important}.mx-5{margin-right:3rem!important;margin-left:3rem!important}.mx-auto{margin-right:auto!important;margin-left:auto!important}.my-0{margin-top:0!important;margin-bottom:0!important}.my-1{margin-top:.25rem!important;margin-bottom:.25rem!important}.my-2{margin-top:.5rem!important;margin-bottom:.5rem!important}.my-3{margin-top:1rem!important;margin-bottom:1rem!important}.my-4{margin-top:1.5rem!important;margin-bottom:1.5rem!important}.my-5{margin-top:3rem!important;margin-bottom:3rem!important}.my-auto{margin-top:auto!important;margin-bottom:auto!important}.mt-0{margin-top:0!important}.mt-1{margin-top:.25rem!important}.mt-2{margin-top:.5rem!important}.mt-3{margin-top:1rem!important}.mt-4{margin-top:1.5rem!important}.mt-5{margin-top:3rem!important}.mt-auto{margin-top:auto!important}.me-0{margin-right:0!important}.me-1{margin-right:.25rem!important}.me-2{margin-right:.5rem!important}.me-3{margin-right:1rem!important}.me-4{margin-right:1.5rem!important}.me-5{margin-right:3rem!important}.me-auto{margin-right:auto!important}.mb-0{margin-bottom:0!important}.mb-1{margin-bottom:.25rem!important}.mb-2{margin-bottom:.5rem!important}.mb-3{margin-bottom:1rem!important}.mb-4{margin-bottom:1.5rem!important}.mb-5{margin-bottom:3rem!important}.mb-auto{margin-bottom:auto!important}.ms-0{margin-left:0!important}.ms-1{margin-left:.25rem!important}.ms-2{margin-left:.5rem!important}.ms-3{margin-left:1rem!important}.ms-4{margin-left:1.5rem!important}.ms-5{margin-left:3rem!important}.ms-auto{margin-left:auto!important}.p-0{padding:0!important}.p-1{padding:.25rem!important}.p-2{padding:.5rem!important}.p-3{padding:1rem!important}.p-4{padding:1.5rem!important}.p-5{padding:3rem!important}.px-0{padding-right:0!important;padding-left:0!important}.px-1{padding-right:.25rem!important;padding-left:.25rem!important}.px-2{padding-right:.5rem!important;padding-left:.5rem!important}.px-3{padding-right:1rem!important;padding-left:1rem!important}.px-4{padding-right:1.5rem!important;padding-left:1.5rem!important}.px-5{padding-right:3rem!important;padding-left:3rem!important}.py-0{padding-top:0!important;padding-bottom:0!important}.py-1{padding-top:.25rem!important;padding-bottom:.25rem!important}.py-2{padding-top:.5rem!important;padding-bottom:.5rem!important}.py-3{padding-top:1rem!important;padding-bottom:1rem!important}.py-4{padding-top:1.5rem!important;padding-bottom:1.5rem!important}.py-5{padding-top:3rem!important;padding-bottom:3rem!important}.pt-0{padding-top:0!important}.pt-1{padding-top:.25rem!important}.pt-2{padding-top:.5rem!important}.pt-3{padding-top:1rem!important}.pt-4{padding-top:1.5rem!important}.pt-5{padding-top:3rem!important}.pe-0{padding-right:0!important}.pe-1{padding-right:.25rem!important}.pe-2{padding-right:.5rem!important}.pe-3{padding-right:1rem!important}.pe-4{padding-right:1.5rem!important}.pe-5{padding-right:3rem!important}.pb-0{padding-bottom:0!important}.pb-1{padding-bottom:.25rem!important}.pb-2{padding-bottom:.5rem!important}.pb-3{padding-bottom:1rem!important}.pb-4{padding-bottom:1.5rem!important}.pb-5{padding-bottom:3rem!important}.ps-0{padding-left:0!important}.ps-1{padding-left:.25rem!important}.ps-2{padding-left:.5rem!important}.ps-3{padding-left:1rem!important}.ps-4{padding-left:1.5rem!important}.ps-5{padding-left:3rem!important}.gap-0{gap:0!important}.gap-1{gap:.25rem!important}.gap-2{gap:.5rem!important}.gap-3{gap:1rem!important}.gap-4{gap:1.5rem!important}.gap-5{gap:3rem!important}.row-gap-0{row-gap:0!important}.row-gap-1{row-gap:.25rem!important}.row-gap-2{row-gap:.5rem!important}.row-gap-3{row-gap:1rem!important}.row-gap-4{row-gap:1.5rem!important}.row-gap-5{row-gap:3rem!important}.column-gap-0{-moz-column-gap:0!important;column-gap:0!important}.column-gap-1{-moz-column-gap:0.25rem!important;column-gap:.25rem!important}.column-gap-2{-moz-column-gap:0.5rem!important;column-gap:.5rem!important}.column-gap-3{-moz-column-gap:1rem!important;column-gap:1rem!important}.column-gap-4{-moz-column-gap:1.5rem!important;column-gap:1.5rem!important}.column-gap-5{-moz-column-gap:3rem!important;column-gap:3rem!important}.font-monospace{font-family:var(--bs-font-monospace)!important}.fs-1{font-size:calc(1.375rem + 1.5vw)!important}.fs-2{font-size:calc(1.325rem + .9vw)!important}.fs-3{font-size:calc(1.3rem + .6vw)!important}.fs-4{font-size:calc(1.275rem + .3vw)!important}.fs-5{font-size:1.25rem!important}.fs-6{font-size:1rem!important}.fst-italic{font-style:italic!important}.fst-normal{font-style:normal!important}.fw-lighter{font-weight:lighter!important}.fw-light{font-weight:300!important}.fw-normal{font-weight:400!important}.fw-medium{font-weight:500!important}.fw-semibold{font-weight:600!important}.fw-bold{font-weight:700!important}.fw-bolder{font-weight:bolder!important}.lh-1{line-height:1!important}.lh-sm{line-height:1.25!important}.lh-base{line-height:1.5!important}.lh-lg{line-height:2!important}.text-start{text-align:left!important}.text-end{text-align:right!important}.text-center{text-align:center!important}.text-decoration-none{text-decoration:none!important}.text-decoration-underline{text-decoration:underline!important}.text-decoration-line-through{text-decoration:line-through!important}.text-lowercase{text-transform:lowercase!important}.text-uppercase{text-transform:uppercase!important}.text-capitalize{text-transform:capitalize!important}.text-wrap{white-space:normal!important}.text-nowrap{white-space:nowrap!important}.text-break{word-wrap:break-word!important;word-break:break-word!important}.text-primary{--bs-text-opacity:1;color:rgba(var(--bs-primary-rgb),var(--bs-text-opacity))!important}.text-secondary{--bs-text-opacity:1;color:rgba(var(--bs-secondary-rgb),var(--bs-text-opacity))!important}.text-success{--bs-text-opacity:1;color:rgba(var(--bs-success-rgb),var(--bs-text-opacity))!important}.text-info{--bs-text-opacity:1;color:rgba(var(--bs-info-rgb),var(--bs-text-opacity))!important}.text-warning{--bs-text-opacity:1;color:rgba(var(--bs-warning-rgb),var(--bs-text-opacity))!important}.text-danger{--bs-text-opacity:1;color:rgba(var(--bs-danger-rgb),var(--bs-text-opacity))!important}.text-light{--bs-text-opacity:1;color:rgba(var(--bs-light-rgb),var(--bs-text-opacity))!important}.text-dark{--bs-text-opacity:1;color:rgba(var(--bs-dark-rgb),var(--bs-text-opacity))!important}.text-black{--bs-text-opacity:1;color:rgba(var(--bs-black-rgb),var(--bs-text-opacity))!important}.text-white{--bs-text-opacity:1;color:rgba(var(--bs-white-rgb),var(--bs-text-opacity))!important}.text-body{--bs-text-opacity:1;color:rgba(var(--bs-body-color-rgb),var(--bs-text-opacity))!important}.text-muted{--bs-text-opacity:1;color:var(--bs-secondary-color)!important}.text-black-50{--bs-text-opacity:1;color:rgba(0,0,0,.5)!important}.text-white-50{--bs-text-opacity:1;color:rgba(255,255,255,.5)!important}.text-body-secondary{--bs-text-opacity:1;color:var(--bs-secondary-color)!important}.text-body-tertiary{--bs-text-opacity:1;color:var(--bs-tertiary-color)!important}.text-body-emphasis{--bs-text-opacity:1;color:var(--bs-emphasis-color)!important}.text-reset{--bs-text-opacity:1;color:inherit!important}.text-opacity-25{--bs-text-opacity:0.25}.text-opacity-50{--bs-text-opacity:0.5}.text-opacity-75{--bs-text-opacity:0.75}.text-opacity-100{--bs-text-opacity:1}.text-primary-emphasis{color:var(--bs-primary-text-emphasis)!important}.text-secondary-emphasis{color:var(--bs-secondary-text-emphasis)!important}.text-success-emphasis{color:var(--bs-success-text-emphasis)!important}.text-info-emphasis{color:var(--bs-info-text-emphasis)!important}.text-warning-emphasis{color:var(--bs-warning-text-emphasis)!important}.text-danger-emphasis{color:var(--bs-danger-text-emphasis)!important}.text-light-emphasis{color:var(--bs-light-text-emphasis)!important}.text-dark-emphasis{color:var(--bs-dark-text-emphasis)!important}.link-opacity-10{--bs-link-opacity:0.1}.link-opacity-10-hover:hover{--bs-link-opacity:0.1}.link-opacity-25{--bs-link-opacity:0.25}.link-opacity-25-hover:hover{--bs-link-opacity:0.25}.link-opacity-50{--bs-link-opacity:0.5}.link-opacity-50-hover:hover{--bs-link-opacity:0.5}.link-opacity-75{--bs-link-opacity:0.75}.link-opacity-75-hover:hover{--bs-link-opacity:0.75}.link-opacity-100{--bs-link-opacity:1}.link-opacity-100-hover:hover{--bs-link-opacity:1}.link-offset-1{text-underline-offset:0.125em!important}.link-offset-1-hover:hover{text-underline-offset:0.125em!important}.link-offset-2{text-underline-offset:0.25em!important}.link-offset-2-hover:hover{text-underline-offset:0.25em!important}.link-offset-3{text-underline-offset:0.375em!important}.link-offset-3-hover:hover{text-underline-offset:0.375em!important}.link-underline-primary{--bs-link-underline-opacity:1;-webkit-text-decoration-color:rgba(var(--bs-primary-rgb),var(--bs-link-underline-opacity))!important;text-decoration-color:rgba(var(--bs-primary-rgb),var(--bs-link-underline-opacity))!important}.link-underline-secondary{--bs-link-underline-opacity:1;-webkit-text-decoration-color:rgba(var(--bs-secondary-rgb),var(--bs-link-underline-opacity))!important;text-decoration-color:rgba(var(--bs-secondary-rgb),var(--bs-link-underline-opacity))!important}.link-underline-success{--bs-link-underline-opacity:1;-webkit-text-decoration-color:rgba(var(--bs-success-rgb),var(--bs-link-underline-opacity))!important;text-decoration-color:rgba(var(--bs-success-rgb),var(--bs-link-underline-opacity))!important}.link-underline-info{--bs-link-underline-opacity:1;-webkit-text-decoration-color:rgba(var(--bs-info-rgb),var(--bs-link-underline-opacity))!important;text-decoration-color:rgba(var(--bs-info-rgb),var(--bs-link-underline-opacity))!important}.link-underline-warning{--bs-link-underline-opacity:1;-webkit-text-decoration-color:rgba(var(--bs-warning-rgb),var(--bs-link-underline-opacity))!important;text-decoration-color:rgba(var(--bs-warning-rgb),var(--bs-link-underline-opacity))!important}.link-underline-danger{--bs-link-underline-opacity:1;-webkit-text-decoration-color:rgba(var(--bs-danger-rgb),var(--bs-link-underline-opacity))!important;text-decoration-color:rgba(var(--bs-danger-rgb),var(--bs-link-underline-opacity))!important}.link-underline-light{--bs-link-underline-opacity:1;-webkit-text-decoration-color:rgba(var(--bs-light-rgb),var(--bs-link-underline-opacity))!important;text-decoration-color:rgba(var(--bs-light-rgb),var(--bs-link-underline-opacity))!important}.link-underline-dark{--bs-link-underline-opacity:1;-webkit-text-decoration-color:rgba(var(--bs-dark-rgb),var(--bs-link-underline-opacity))!important;text-decoration-color:rgba(var(--bs-dark-rgb),var(--bs-link-underline-opacity))!important}.link-underline{--bs-link-underline-opacity:1;-webkit-text-decoration-color:rgba(var(--bs-link-color-rgb),var(--bs-link-underline-opacity,1))!important;text-decoration-color:rgba(var(--bs-link-color-rgb),var(--bs-link-underline-opacity,1))!important}.link-underline-opacity-0{--bs-link-underline-opacity:0}.link-underline-opacity-0-hover:hover{--bs-link-underline-opacity:0}.link-underline-opacity-10{--bs-link-underline-opacity:0.1}.link-underline-opacity-10-hover:hover{--bs-link-underline-opacity:0.1}.link-underline-opacity-25{--bs-link-underline-opacity:0.25}.link-underline-opacity-25-hover:hover{--bs-link-underline-opacity:0.25}.link-underline-opacity-50{--bs-link-underline-opacity:0.5}.link-underline-opacity-50-hover:hover{--bs-link-underline-opacity:0.5}.link-underline-opacity-75{--bs-link-underline-opacity:0.75}.link-underline-opacity-75-hover:hover{--bs-link-underline-opacity:0.75}.link-underline-opacity-100{--bs-link-underline-opacity:1}.link-underline-opacity-100-hover:hover{--bs-link-underline-opacity:1}.bg-primary{--bs-bg-opacity:1;background-color:rgba(var(--bs-primary-rgb),var(--bs-bg-opacity))!important}.bg-secondary{--bs-bg-opacity:1;background-color:rgba(var(--bs-secondary-rgb),var(--bs-bg-opacity))!important}.bg-success{--bs-bg-opacity:1;background-color:rgba(var(--bs-success-rgb),var(--bs-bg-opacity))!important}.bg-info{--bs-bg-opacity:1;background-color:rgba(var(--bs-info-rgb),var(--bs-bg-opacity))!important}.bg-warning{--bs-bg-opacity:1;background-color:rgba(var(--bs-warning-rgb),var(--bs-bg-opacity))!important}.bg-danger{--bs-bg-opacity:1;background-color:rgba(var(--bs-danger-rgb),var(--bs-bg-opacity))!important}.bg-light{--bs-bg-opacity:1;background-color:rgba(var(--bs-light-rgb),var(--bs-bg-opacity))!important}.bg-dark{--bs-bg-opacity:1;background-color:rgba(var(--bs-dark-rgb),var(--bs-bg-opacity))!important}.bg-black{--bs-bg-opacity:1;background-color:rgba(var(--bs-black-rgb),var(--bs-bg-opacity))!important}.bg-white{--bs-bg-opacity:1;background-color:rgba(var(--bs-white-rgb),var(--bs-bg-opacity))!important}.bg-body{--bs-bg-opacity:1;background-color:rgba(var(--bs-body-bg-rgb),var(--bs-bg-opacity))!important}.bg-transparent{--bs-bg-opacity:1;background-color:transparent!important}.bg-body-secondary{--bs-bg-opacity:1;background-color:rgba(var(--bs-secondary-bg-rgb),var(--bs-bg-opacity))!important}.bg-body-tertiary{--bs-bg-opacity:1;background-color:rgba(var(--bs-tertiary-bg-rgb),var(--bs-bg-opacity))!important}.bg-opacity-10{--bs-bg-opacity:0.1}.bg-opacity-25{--bs-bg-opacity:0.25}.bg-opacity-50{--bs-bg-opacity:0.5}.bg-opacity-75{--bs-bg-opacity:0.75}.bg-opacity-100{--bs-bg-opacity:1}.bg-primary-subtle{background-color:var(--bs-primary-bg-subtle)!important}.bg-secondary-subtle{background-color:var(--bs-secondary-bg-subtle)!important}.bg-success-subtle{background-color:var(--bs-success-bg-subtle)!important}.bg-info-subtle{background-color:var(--bs-info-bg-subtle)!important}.bg-warning-subtle{background-color:var(--bs-warning-bg-subtle)!important}.bg-danger-subtle{background-color:var(--bs-danger-bg-subtle)!important}.bg-light-subtle{background-color:var(--bs-light-bg-subtle)!important}.bg-dark-subtle{background-color:var(--bs-dark-bg-subtle)!important}.bg-gradient{background-image:var(--bs-gradient)!important}.user-select-all{-webkit-user-select:all!important;-moz-user-select:all!important;user-select:all!important}.user-select-auto{-webkit-user-select:auto!important;-moz-user-select:auto!important;user-select:auto!important}.user-select-none{-webkit-user-select:none!important;-moz-user-select:none!important;user-select:none!important}.pe-none{pointer-events:none!important}.pe-auto{pointer-events:auto!important}.rounded{border-radius:var(--bs-border-radius)!important}.rounded-0{border-radius:0!important}.rounded-1{border-radius:var(--bs-border-radius-sm)!important}.rounded-2{border-radius:var(--bs-border-radius)!important}.rounded-3{border-radius:var(--bs-border-radius-lg)!important}.rounded-4{border-radius:var(--bs-border-radius-xl)!important}.rounded-5{border-radius:var(--bs-border-radius-xxl)!important}.rounded-circle{border-radius:50%!important}.rounded-pill{border-radius:var(--bs-border-radius-pill)!important}.rounded-top{border-top-left-radius:var(--bs-border-radius)!important;border-top-right-radius:var(--bs-border-radius)!important}.rounded-top-0{border-top-left-radius:0!important;border-top-right-radius:0!important}.rounded-top-1{border-top-left-radius:var(--bs-border-radius-sm)!important;border-top-right-radius:var(--bs-border-radius-sm)!important}.rounded-top-2{border-top-left-radius:var(--bs-border-radius)!important;border-top-right-radius:var(--bs-border-radius)!important}.rounded-top-3{border-top-left-radius:var(--bs-border-radius-lg)!important;border-top-right-radius:var(--bs-border-radius-lg)!important}.rounded-top-4{border-top-left-radius:var(--bs-border-radius-xl)!important;border-top-right-radius:var(--bs-border-radius-xl)!important}.rounded-top-5{border-top-left-radius:var(--bs-border-radius-xxl)!important;border-top-right-radius:var(--bs-border-radius-xxl)!important}.rounded-top-circle{border-top-left-radius:50%!important;border-top-right-radius:50%!important}.rounded-top-pill{border-top-left-radius:var(--bs-border-radius-pill)!important;border-top-right-radius:var(--bs-border-radius-pill)!important}.rounded-end{border-top-right-radius:var(--bs-border-radius)!important;border-bottom-right-radius:var(--bs-border-radius)!important}.rounded-end-0{border-top-right-radius:0!important;border-bottom-right-radius:0!important}.rounded-end-1{border-top-right-radius:var(--bs-border-radius-sm)!important;border-bottom-right-radius:var(--bs-border-radius-sm)!important}.rounded-end-2{border-top-right-radius:var(--bs-border-radius)!important;border-bottom-right-radius:var(--bs-border-radius)!important}.rounded-end-3{border-top-right-radius:var(--bs-border-radius-lg)!important;border-bottom-right-radius:var(--bs-border-radius-lg)!important}.rounded-end-4{border-top-right-radius:var(--bs-border-radius-xl)!important;border-bottom-right-radius:var(--bs-border-radius-xl)!important}.rounded-end-5{border-top-right-radius:var(--bs-border-radius-xxl)!important;border-bottom-right-radius:var(--bs-border-radius-xxl)!important}.rounded-end-circle{border-top-right-radius:50%!important;border-bottom-right-radius:50%!important}.rounded-end-pill{border-top-right-radius:var(--bs-border-radius-pill)!important;border-bottom-right-radius:var(--bs-border-radius-pill)!important}.rounded-bottom{border-bottom-right-radius:var(--bs-border-radius)!important;border-bottom-left-radius:var(--bs-border-radius)!important}.rounded-bottom-0{border-bottom-right-radius:0!important;border-bottom-left-radius:0!important}.rounded-bottom-1{border-bottom-right-radius:var(--bs-border-radius-sm)!important;border-bottom-left-radius:var(--bs-border-radius-sm)!important}.rounded-bottom-2{border-bottom-right-radius:var(--bs-border-radius)!important;border-bottom-left-radius:var(--bs-border-radius)!important}.rounded-bottom-3{border-bottom-right-radius:var(--bs-border-radius-lg)!important;border-bottom-left-radius:var(--bs-border-radius-lg)!important}.rounded-bottom-4{border-bottom-right-radius:var(--bs-border-radius-xl)!important;border-bottom-left-radius:var(--bs-border-radius-xl)!important}.rounded-bottom-5{border-bottom-right-radius:var(--bs-border-radius-xxl)!important;border-bottom-left-radius:var(--bs-border-radius-xxl)!important}.rounded-bottom-circle{border-bottom-right-radius:50%!important;border-bottom-left-radius:50%!important}.rounded-bottom-pill{border-bottom-right-radius:var(--bs-border-radius-pill)!important;border-bottom-left-radius:var(--bs-border-radius-pill)!important}.rounded-start{border-bottom-left-radius:var(--bs-border-radius)!important;border-top-left-radius:var(--bs-border-radius)!important}.rounded-start-0{border-bottom-left-radius:0!important;border-top-left-radius:0!important}.rounded-start-1{border-bottom-left-radius:var(--bs-border-radius-sm)!important;border-top-left-radius:var(--bs-border-radius-sm)!important}.rounded-start-2{border-bottom-left-radius:var(--bs-border-radius)!important;border-top-left-radius:var(--bs-border-radius)!important}.rounded-start-3{border-bottom-left-radius:var(--bs-border-radius-lg)!important;border-top-left-radius:var(--bs-border-radius-lg)!important}.rounded-start-4{border-bottom-left-radius:var(--bs-border-radius-xl)!important;border-top-left-radius:var(--bs-border-radius-xl)!important}.rounded-start-5{border-bottom-left-radius:var(--bs-border-radius-xxl)!important;border-top-left-radius:var(--bs-border-radius-xxl)!important}.rounded-start-circle{border-bottom-left-radius:50%!important;border-top-left-radius:50%!important}.rounded-start-pill{border-bottom-left-radius:var(--bs-border-radius-pill)!important;border-top-left-radius:var(--bs-border-radius-pill)!important}.visible{visibility:visible!important}.invisible{visibility:hidden!important}.z-n1{z-index:-1!important}.z-0{z-index:0!important}.z-1{z-index:1!important}.z-2{z-index:2!important}.z-3{z-index:3!important}@media (min-width:576px){.float-sm-start{float:left!important}.float-sm-end{float:right!important}.float-sm-none{float:none!important}.object-fit-sm-contain{-o-object-fit:contain!important;object-fit:contain!important}.object-fit-sm-cover{-o-object-fit:cover!important;object-fit:cover!important}.object-fit-sm-fill{-o-object-fit:fill!important;object-fit:fill!important}.object-fit-sm-scale{-o-object-fit:scale-down!important;object-fit:scale-down!important}.object-fit-sm-none{-o-object-fit:none!important;object-fit:none!important}.d-sm-inline{display:inline!important}.d-sm-inline-block{display:inline-block!important}.d-sm-block{display:block!important}.d-sm-grid{display:grid!important}.d-sm-table{display:table!important}.d-sm-table-row{display:table-row!important}.d-sm-table-cell{display:table-cell!important}.d-sm-flex{display:flex!important}.d-sm-inline-flex{display:inline-flex!important}.d-sm-none{display:none!important}.flex-sm-fill{flex:1 1 auto!important}.flex-sm-row{flex-direction:row!important}.flex-sm-column{flex-direction:column!important}.flex-sm-row-reverse{flex-direction:row-reverse!important}.flex-sm-column-reverse{flex-direction:column-reverse!important}.flex-sm-grow-0{flex-grow:0!important}.flex-sm-grow-1{flex-grow:1!important}.flex-sm-shrink-0{flex-shrink:0!important}.flex-sm-shrink-1{flex-shrink:1!important}.flex-sm-wrap{flex-wrap:wrap!important}.flex-sm-nowrap{flex-wrap:nowrap!important}.flex-sm-wrap-reverse{flex-wrap:wrap-reverse!important}.justify-content-sm-start{justify-content:flex-start!important}.justify-content-sm-end{justify-content:flex-end!important}.justify-content-sm-center{justify-content:center!important}.justify-content-sm-between{justify-content:space-between!important}.justify-content-sm-around{justify-content:space-around!important}.justify-content-sm-evenly{justify-content:space-evenly!important}.align-items-sm-start{align-items:flex-start!important}.align-items-sm-end{align-items:flex-end!important}.align-items-sm-center{align-items:center!important}.align-items-sm-baseline{align-items:baseline!important}.align-items-sm-stretch{align-items:stretch!important}.align-content-sm-start{align-content:flex-start!important}.align-content-sm-end{align-content:flex-end!important}.align-content-sm-center{align-content:center!important}.align-content-sm-between{align-content:space-between!important}.align-content-sm-around{align-content:space-around!important}.align-content-sm-stretch{align-content:stretch!important}.align-self-sm-auto{align-self:auto!important}.align-self-sm-start{align-self:flex-start!important}.align-self-sm-end{align-self:flex-end!important}.align-self-sm-center{align-self:center!important}.align-self-sm-baseline{align-self:baseline!important}.align-self-sm-stretch{align-self:stretch!important}.order-sm-first{order:-1!important}.order-sm-0{order:0!important}.order-sm-1{order:1!important}.order-sm-2{order:2!important}.order-sm-3{order:3!important}.order-sm-4{order:4!important}.order-sm-5{order:5!important}.order-sm-last{order:6!important}.m-sm-0{margin:0!important}.m-sm-1{margin:.25rem!important}.m-sm-2{margin:.5rem!important}.m-sm-3{margin:1rem!important}.m-sm-4{margin:1.5rem!important}.m-sm-5{margin:3rem!important}.m-sm-auto{margin:auto!important}.mx-sm-0{margin-right:0!important;margin-left:0!important}.mx-sm-1{margin-right:.25rem!important;margin-left:.25rem!important}.mx-sm-2{margin-right:.5rem!important;margin-left:.5rem!important}.mx-sm-3{margin-right:1rem!important;margin-left:1rem!important}.mx-sm-4{margin-right:1.5rem!important;margin-left:1.5rem!important}.mx-sm-5{margin-right:3rem!important;margin-left:3rem!important}.mx-sm-auto{margin-right:auto!important;margin-left:auto!important}.my-sm-0{margin-top:0!important;margin-bottom:0!important}.my-sm-1{margin-top:.25rem!important;margin-bottom:.25rem!important}.my-sm-2{margin-top:.5rem!important;margin-bottom:.5rem!important}.my-sm-3{margin-top:1rem!important;margin-bottom:1rem!important}.my-sm-4{margin-top:1.5rem!important;margin-bottom:1.5rem!important}.my-sm-5{margin-top:3rem!important;margin-bottom:3rem!important}.my-sm-auto{margin-top:auto!important;margin-bottom:auto!important}.mt-sm-0{margin-top:0!important}.mt-sm-1{margin-top:.25rem!important}.mt-sm-2{margin-top:.5rem!important}.mt-sm-3{margin-top:1rem!important}.mt-sm-4{margin-top:1.5rem!important}.mt-sm-5{margin-top:3rem!important}.mt-sm-auto{margin-top:auto!important}.me-sm-0{margin-right:0!important}.me-sm-1{margin-right:.25rem!important}.me-sm-2{margin-right:.5rem!important}.me-sm-3{margin-right:1rem!important}.me-sm-4{margin-right:1.5rem!important}.me-sm-5{margin-right:3rem!important}.me-sm-auto{margin-right:auto!important}.mb-sm-0{margin-bottom:0!important}.mb-sm-1{margin-bottom:.25rem!important}.mb-sm-2{margin-bottom:.5rem!important}.mb-sm-3{margin-bottom:1rem!important}.mb-sm-4{margin-bottom:1.5rem!important}.mb-sm-5{margin-bottom:3rem!important}.mb-sm-auto{margin-bottom:auto!important}.ms-sm-0{margin-left:0!important}.ms-sm-1{margin-left:.25rem!important}.ms-sm-2{margin-left:.5rem!important}.ms-sm-3{margin-left:1rem!important}.ms-sm-4{margin-left:1.5rem!important}.ms-sm-5{margin-left:3rem!important}.ms-sm-auto{margin-left:auto!important}.p-sm-0{padding:0!important}.p-sm-1{padding:.25rem!important}.p-sm-2{padding:.5rem!important}.p-sm-3{padding:1rem!important}.p-sm-4{padding:1.5rem!important}.p-sm-5{padding:3rem!important}.px-sm-0{padding-right:0!important;padding-left:0!important}.px-sm-1{padding-right:.25rem!important;padding-left:.25rem!important}.px-sm-2{padding-right:.5rem!important;padding-left:.5rem!important}.px-sm-3{padding-right:1rem!important;padding-left:1rem!important}.px-sm-4{padding-right:1.5rem!important;padding-left:1.5rem!important}.px-sm-5{padding-right:3rem!important;padding-left:3rem!important}.py-sm-0{padding-top:0!important;padding-bottom:0!important}.py-sm-1{padding-top:.25rem!important;padding-bottom:.25rem!important}.py-sm-2{padding-top:.5rem!important;padding-bottom:.5rem!important}.py-sm-3{padding-top:1rem!important;padding-bottom:1rem!important}.py-sm-4{padding-top:1.5rem!important;padding-bottom:1.5rem!important}.py-sm-5{padding-top:3rem!important;padding-bottom:3rem!important}.pt-sm-0{padding-top:0!important}.pt-sm-1{padding-top:.25rem!important}.pt-sm-2{padding-top:.5rem!important}.pt-sm-3{padding-top:1rem!important}.pt-sm-4{padding-top:1.5rem!important}.pt-sm-5{padding-top:3rem!important}.pe-sm-0{padding-right:0!important}.pe-sm-1{padding-right:.25rem!important}.pe-sm-2{padding-right:.5rem!important}.pe-sm-3{padding-right:1rem!important}.pe-sm-4{padding-right:1.5rem!important}.pe-sm-5{padding-right:3rem!important}.pb-sm-0{padding-bottom:0!important}.pb-sm-1{padding-bottom:.25rem!important}.pb-sm-2{padding-bottom:.5rem!important}.pb-sm-3{padding-bottom:1rem!important}.pb-sm-4{padding-bottom:1.5rem!important}.pb-sm-5{padding-bottom:3rem!important}.ps-sm-0{padding-left:0!important}.ps-sm-1{padding-left:.25rem!important}.ps-sm-2{padding-left:.5rem!important}.ps-sm-3{padding-left:1rem!important}.ps-sm-4{padding-left:1.5rem!important}.ps-sm-5{padding-left:3rem!important}.gap-sm-0{gap:0!important}.gap-sm-1{gap:.25rem!important}.gap-sm-2{gap:.5rem!important}.gap-sm-3{gap:1rem!important}.gap-sm-4{gap:1.5rem!important}.gap-sm-5{gap:3rem!important}.row-gap-sm-0{row-gap:0!important}.row-gap-sm-1{row-gap:.25rem!important}.row-gap-sm-2{row-gap:.5rem!important}.row-gap-sm-3{row-gap:1rem!important}.row-gap-sm-4{row-gap:1.5rem!important}.row-gap-sm-5{row-gap:3rem!important}.column-gap-sm-0{-moz-column-gap:0!important;column-gap:0!important}.column-gap-sm-1{-moz-column-gap:0.25rem!important;column-gap:.25rem!important}.column-gap-sm-2{-moz-column-gap:0.5rem!important;column-gap:.5rem!important}.column-gap-sm-3{-moz-column-gap:1rem!important;column-gap:1rem!important}.column-gap-sm-4{-moz-column-gap:1.5rem!important;column-gap:1.5rem!important}.column-gap-sm-5{-moz-column-gap:3rem!important;column-gap:3rem!important}.text-sm-start{text-align:left!important}.text-sm-end{text-align:right!important}.text-sm-center{text-align:center!important}}@media (min-width:768px){.float-md-start{float:left!important}.float-md-end{float:right!important}.float-md-none{float:none!important}.object-fit-md-contain{-o-object-fit:contain!important;object-fit:contain!important}.object-fit-md-cover{-o-object-fit:cover!important;object-fit:cover!important}.object-fit-md-fill{-o-object-fit:fill!important;object-fit:fill!important}.object-fit-md-scale{-o-object-fit:scale-down!important;object-fit:scale-down!important}.object-fit-md-none{-o-object-fit:none!important;object-fit:none!important}.d-md-inline{display:inline!important}.d-md-inline-block{display:inline-block!important}.d-md-block{display:block!important}.d-md-grid{display:grid!important}.d-md-table{display:table!important}.d-md-table-row{display:table-row!important}.d-md-table-cell{display:table-cell!important}.d-md-flex{display:flex!important}.d-md-inline-flex{display:inline-flex!important}.d-md-none{display:none!important}.flex-md-fill{flex:1 1 auto!important}.flex-md-row{flex-direction:row!important}.flex-md-column{flex-direction:column!important}.flex-md-row-reverse{flex-direction:row-reverse!important}.flex-md-column-reverse{flex-direction:column-reverse!important}.flex-md-grow-0{flex-grow:0!important}.flex-md-grow-1{flex-grow:1!important}.flex-md-shrink-0{flex-shrink:0!important}.flex-md-shrink-1{flex-shrink:1!important}.flex-md-wrap{flex-wrap:wrap!important}.flex-md-nowrap{flex-wrap:nowrap!important}.flex-md-wrap-reverse{flex-wrap:wrap-reverse!important}.justify-content-md-start{justify-content:flex-start!important}.justify-content-md-end{justify-content:flex-end!important}.justify-content-md-center{justify-content:center!important}.justify-content-md-between{justify-content:space-between!important}.justify-content-md-around{justify-content:space-around!important}.justify-content-md-evenly{justify-content:space-evenly!important}.align-items-md-start{align-items:flex-start!important}.align-items-md-end{align-items:flex-end!important}.align-items-md-center{align-items:center!important}.align-items-md-baseline{align-items:baseline!important}.align-items-md-stretch{align-items:stretch!important}.align-content-md-start{align-content:flex-start!important}.align-content-md-end{align-content:flex-end!important}.align-content-md-center{align-content:center!important}.align-content-md-between{align-content:space-between!important}.align-content-md-around{align-content:space-around!important}.align-content-md-stretch{align-content:stretch!important}.align-self-md-auto{align-self:auto!important}.align-self-md-start{align-self:flex-start!important}.align-self-md-end{align-self:flex-end!important}.align-self-md-center{align-self:center!important}.align-self-md-baseline{align-self:baseline!important}.align-self-md-stretch{align-self:stretch!important}.order-md-first{order:-1!important}.order-md-0{order:0!important}.order-md-1{order:1!important}.order-md-2{order:2!important}.order-md-3{order:3!important}.order-md-4{order:4!important}.order-md-5{order:5!important}.order-md-last{order:6!important}.m-md-0{margin:0!important}.m-md-1{margin:.25rem!important}.m-md-2{margin:.5rem!important}.m-md-3{margin:1rem!important}.m-md-4{margin:1.5rem!important}.m-md-5{margin:3rem!important}.m-md-auto{margin:auto!important}.mx-md-0{margin-right:0!important;margin-left:0!important}.mx-md-1{margin-right:.25rem!important;margin-left:.25rem!important}.mx-md-2{margin-right:.5rem!important;margin-left:.5rem!important}.mx-md-3{margin-right:1rem!important;margin-left:1rem!important}.mx-md-4{margin-right:1.5rem!important;margin-left:1.5rem!important}.mx-md-5{margin-right:3rem!important;margin-left:3rem!important}.mx-md-auto{margin-right:auto!important;margin-left:auto!important}.my-md-0{margin-top:0!important;margin-bottom:0!important}.my-md-1{margin-top:.25rem!important;margin-bottom:.25rem!important}.my-md-2{margin-top:.5rem!important;margin-bottom:.5rem!important}.my-md-3{margin-top:1rem!important;margin-bottom:1rem!important}.my-md-4{margin-top:1.5rem!important;margin-bottom:1.5rem!important}.my-md-5{margin-top:3rem!important;margin-bottom:3rem!important}.my-md-auto{margin-top:auto!important;margin-bottom:auto!important}.mt-md-0{margin-top:0!important}.mt-md-1{margin-top:.25rem!important}.mt-md-2{margin-top:.5rem!important}.mt-md-3{margin-top:1rem!important}.mt-md-4{margin-top:1.5rem!important}.mt-md-5{margin-top:3rem!important}.mt-md-auto{margin-top:auto!important}.me-md-0{margin-right:0!important}.me-md-1{margin-right:.25rem!important}.me-md-2{margin-right:.5rem!important}.me-md-3{margin-right:1rem!important}.me-md-4{margin-right:1.5rem!important}.me-md-5{margin-right:3rem!important}.me-md-auto{margin-right:auto!important}.mb-md-0{margin-bottom:0!important}.mb-md-1{margin-bottom:.25rem!important}.mb-md-2{margin-bottom:.5rem!important}.mb-md-3{margin-bottom:1rem!important}.mb-md-4{margin-bottom:1.5rem!important}.mb-md-5{margin-bottom:3rem!important}.mb-md-auto{margin-bottom:auto!important}.ms-md-0{margin-left:0!important}.ms-md-1{margin-left:.25rem!important}.ms-md-2{margin-left:.5rem!important}.ms-md-3{margin-left:1rem!important}.ms-md-4{margin-left:1.5rem!important}.ms-md-5{margin-left:3rem!important}.ms-md-auto{margin-left:auto!important}.p-md-0{padding:0!important}.p-md-1{padding:.25rem!important}.p-md-2{padding:.5rem!important}.p-md-3{padding:1rem!important}.p-md-4{padding:1.5rem!important}.p-md-5{padding:3rem!important}.px-md-0{padding-right:0!important;padding-left:0!important}.px-md-1{padding-right:.25rem!important;padding-left:.25rem!important}.px-md-2{padding-right:.5rem!important;padding-left:.5rem!important}.px-md-3{padding-right:1rem!important;padding-left:1rem!important}.px-md-4{padding-right:1.5rem!important;padding-left:1.5rem!important}.px-md-5{padding-right:3rem!important;padding-left:3rem!important}.py-md-0{padding-top:0!important;padding-bottom:0!important}.py-md-1{padding-top:.25rem!important;padding-bottom:.25rem!important}.py-md-2{padding-top:.5rem!important;padding-bottom:.5rem!important}.py-md-3{padding-top:1rem!important;padding-bottom:1rem!important}.py-md-4{padding-top:1.5rem!important;padding-bottom:1.5rem!important}.py-md-5{padding-top:3rem!important;padding-bottom:3rem!important}.pt-md-0{padding-top:0!important}.pt-md-1{padding-top:.25rem!important}.pt-md-2{padding-top:.5rem!important}.pt-md-3{padding-top:1rem!important}.pt-md-4{padding-top:1.5rem!important}.pt-md-5{padding-top:3rem!important}.pe-md-0{padding-right:0!important}.pe-md-1{padding-right:.25rem!important}.pe-md-2{padding-right:.5rem!important}.pe-md-3{padding-right:1rem!important}.pe-md-4{padding-right:1.5rem!important}.pe-md-5{padding-right:3rem!important}.pb-md-0{padding-bottom:0!important}.pb-md-1{padding-bottom:.25rem!important}.pb-md-2{padding-bottom:.5rem!important}.pb-md-3{padding-bottom:1rem!important}.pb-md-4{padding-bottom:1.5rem!important}.pb-md-5{padding-bottom:3rem!important}.ps-md-0{padding-left:0!important}.ps-md-1{padding-left:.25rem!important}.ps-md-2{padding-left:.5rem!important}.ps-md-3{padding-left:1rem!important}.ps-md-4{padding-left:1.5rem!important}.ps-md-5{padding-left:3rem!important}.gap-md-0{gap:0!important}.gap-md-1{gap:.25rem!important}.gap-md-2{gap:.5rem!important}.gap-md-3{gap:1rem!important}.gap-md-4{gap:1.5rem!important}.gap-md-5{gap:3rem!important}.row-gap-md-0{row-gap:0!important}.row-gap-md-1{row-gap:.25rem!important}.row-gap-md-2{row-gap:.5rem!important}.row-gap-md-3{row-gap:1rem!important}.row-gap-md-4{row-gap:1.5rem!important}.row-gap-md-5{row-gap:3rem!important}.column-gap-md-0{-moz-column-gap:0!important;column-gap:0!important}.column-gap-md-1{-moz-column-gap:0.25rem!important;column-gap:.25rem!important}.column-gap-md-2{-moz-column-gap:0.5rem!important;column-gap:.5rem!important}.column-gap-md-3{-moz-column-gap:1rem!important;column-gap:1rem!important}.column-gap-md-4{-moz-column-gap:1.5rem!important;column-gap:1.5rem!important}.column-gap-md-5{-moz-column-gap:3rem!important;column-gap:3rem!important}.text-md-start{text-align:left!important}.text-md-end{text-align:right!important}.text-md-center{text-align:center!important}}@media (min-width:992px){.float-lg-start{float:left!important}.float-lg-end{float:right!important}.float-lg-none{float:none!important}.object-fit-lg-contain{-o-object-fit:contain!important;object-fit:contain!important}.object-fit-lg-cover{-o-object-fit:cover!important;object-fit:cover!important}.object-fit-lg-fill{-o-object-fit:fill!important;object-fit:fill!important}.object-fit-lg-scale{-o-object-fit:scale-down!important;object-fit:scale-down!important}.object-fit-lg-none{-o-object-fit:none!important;object-fit:none!important}.d-lg-inline{display:inline!important}.d-lg-inline-block{display:inline-block!important}.d-lg-block{display:block!important}.d-lg-grid{display:grid!important}.d-lg-table{display:table!important}.d-lg-table-row{display:table-row!important}.d-lg-table-cell{display:table-cell!important}.d-lg-flex{display:flex!important}.d-lg-inline-flex{display:inline-flex!important}.d-lg-none{display:none!important}.flex-lg-fill{flex:1 1 auto!important}.flex-lg-row{flex-direction:row!important}.flex-lg-column{flex-direction:column!important}.flex-lg-row-reverse{flex-direction:row-reverse!important}.flex-lg-column-reverse{flex-direction:column-reverse!important}.flex-lg-grow-0{flex-grow:0!important}.flex-lg-grow-1{flex-grow:1!important}.flex-lg-shrink-0{flex-shrink:0!important}.flex-lg-shrink-1{flex-shrink:1!important}.flex-lg-wrap{flex-wrap:wrap!important}.flex-lg-nowrap{flex-wrap:nowrap!important}.flex-lg-wrap-reverse{flex-wrap:wrap-reverse!important}.justify-content-lg-start{justify-content:flex-start!important}.justify-content-lg-end{justify-content:flex-end!important}.justify-content-lg-center{justify-content:center!important}.justify-content-lg-between{justify-content:space-between!important}.justify-content-lg-around{justify-content:space-around!important}.justify-content-lg-evenly{justify-content:space-evenly!important}.align-items-lg-start{align-items:flex-start!important}.align-items-lg-end{align-items:flex-end!important}.align-items-lg-center{align-items:center!important}.align-items-lg-baseline{align-items:baseline!important}.align-items-lg-stretch{align-items:stretch!important}.align-content-lg-start{align-content:flex-start!important}.align-content-lg-end{align-content:flex-end!important}.align-content-lg-center{align-content:center!important}.align-content-lg-between{align-content:space-between!important}.align-content-lg-around{align-content:space-around!important}.align-content-lg-stretch{align-content:stretch!important}.align-self-lg-auto{align-self:auto!important}.align-self-lg-start{align-self:flex-start!important}.align-self-lg-end{align-self:flex-end!important}.align-self-lg-center{align-self:center!important}.align-self-lg-baseline{align-self:baseline!important}.align-self-lg-stretch{align-self:stretch!important}.order-lg-first{order:-1!important}.order-lg-0{order:0!important}.order-lg-1{order:1!important}.order-lg-2{order:2!important}.order-lg-3{order:3!important}.order-lg-4{order:4!important}.order-lg-5{order:5!important}.order-lg-last{order:6!important}.m-lg-0{margin:0!important}.m-lg-1{margin:.25rem!important}.m-lg-2{margin:.5rem!important}.m-lg-3{margin:1rem!important}.m-lg-4{margin:1.5rem!important}.m-lg-5{margin:3rem!important}.m-lg-auto{margin:auto!important}.mx-lg-0{margin-right:0!important;margin-left:0!important}.mx-lg-1{margin-right:.25rem!important;margin-left:.25rem!important}.mx-lg-2{margin-right:.5rem!important;margin-left:.5rem!important}.mx-lg-3{margin-right:1rem!important;margin-left:1rem!important}.mx-lg-4{margin-right:1.5rem!important;margin-left:1.5rem!important}.mx-lg-5{margin-right:3rem!important;margin-left:3rem!important}.mx-lg-auto{margin-right:auto!important;margin-left:auto!important}.my-lg-0{margin-top:0!important;margin-bottom:0!important}.my-lg-1{margin-top:.25rem!important;margin-bottom:.25rem!important}.my-lg-2{margin-top:.5rem!important;margin-bottom:.5rem!important}.my-lg-3{margin-top:1rem!important;margin-bottom:1rem!important}.my-lg-4{margin-top:1.5rem!important;margin-bottom:1.5rem!important}.my-lg-5{margin-top:3rem!important;margin-bottom:3rem!important}.my-lg-auto{margin-top:auto!important;margin-bottom:auto!important}.mt-lg-0{margin-top:0!important}.mt-lg-1{margin-top:.25rem!important}.mt-lg-2{margin-top:.5rem!important}.mt-lg-3{margin-top:1rem!important}.mt-lg-4{margin-top:1.5rem!important}.mt-lg-5{margin-top:3rem!important}.mt-lg-auto{margin-top:auto!important}.me-lg-0{margin-right:0!important}.me-lg-1{margin-right:.25rem!important}.me-lg-2{margin-right:.5rem!important}.me-lg-3{margin-right:1rem!important}.me-lg-4{margin-right:1.5rem!important}.me-lg-5{margin-right:3rem!important}.me-lg-auto{margin-right:auto!important}.mb-lg-0{margin-bottom:0!important}.mb-lg-1{margin-bottom:.25rem!important}.mb-lg-2{margin-bottom:.5rem!important}.mb-lg-3{margin-bottom:1rem!important}.mb-lg-4{margin-bottom:1.5rem!important}.mb-lg-5{margin-bottom:3rem!important}.mb-lg-auto{margin-bottom:auto!important}.ms-lg-0{margin-left:0!important}.ms-lg-1{margin-left:.25rem!important}.ms-lg-2{margin-left:.5rem!important}.ms-lg-3{margin-left:1rem!important}.ms-lg-4{margin-left:1.5rem!important}.ms-lg-5{margin-left:3rem!important}.ms-lg-auto{margin-left:auto!important}.p-lg-0{padding:0!important}.p-lg-1{padding:.25rem!important}.p-lg-2{padding:.5rem!important}.p-lg-3{padding:1rem!important}.p-lg-4{padding:1.5rem!important}.p-lg-5{padding:3rem!important}.px-lg-0{padding-right:0!important;padding-left:0!important}.px-lg-1{padding-right:.25rem!important;padding-left:.25rem!important}.px-lg-2{padding-right:.5rem!important;padding-left:.5rem!important}.px-lg-3{padding-right:1rem!important;padding-left:1rem!important}.px-lg-4{padding-right:1.5rem!important;padding-left:1.5rem!important}.px-lg-5{padding-right:3rem!important;padding-left:3rem!important}.py-lg-0{padding-top:0!important;padding-bottom:0!important}.py-lg-1{padding-top:.25rem!important;padding-bottom:.25rem!important}.py-lg-2{padding-top:.5rem!important;padding-bottom:.5rem!important}.py-lg-3{padding-top:1rem!important;padding-bottom:1rem!important}.py-lg-4{padding-top:1.5rem!important;padding-bottom:1.5rem!important}.py-lg-5{padding-top:3rem!important;padding-bottom:3rem!important}.pt-lg-0{padding-top:0!important}.pt-lg-1{padding-top:.25rem!important}.pt-lg-2{padding-top:.5rem!important}.pt-lg-3{padding-top:1rem!important}.pt-lg-4{padding-top:1.5rem!important}.pt-lg-5{padding-top:3rem!important}.pe-lg-0{padding-right:0!important}.pe-lg-1{padding-right:.25rem!important}.pe-lg-2{padding-right:.5rem!important}.pe-lg-3{padding-right:1rem!important}.pe-lg-4{padding-right:1.5rem!important}.pe-lg-5{padding-right:3rem!important}.pb-lg-0{padding-bottom:0!important}.pb-lg-1{padding-bottom:.25rem!important}.pb-lg-2{padding-bottom:.5rem!important}.pb-lg-3{padding-bottom:1rem!important}.pb-lg-4{padding-bottom:1.5rem!important}.pb-lg-5{padding-bottom:3rem!important}.ps-lg-0{padding-left:0!important}.ps-lg-1{padding-left:.25rem!important}.ps-lg-2{padding-left:.5rem!important}.ps-lg-3{padding-left:1rem!important}.ps-lg-4{padding-left:1.5rem!important}.ps-lg-5{padding-left:3rem!important}.gap-lg-0{gap:0!important}.gap-lg-1{gap:.25rem!important}.gap-lg-2{gap:.5rem!important}.gap-lg-3{gap:1rem!important}.gap-lg-4{gap:1.5rem!important}.gap-lg-5{gap:3rem!important}.row-gap-lg-0{row-gap:0!important}.row-gap-lg-1{row-gap:.25rem!important}.row-gap-lg-2{row-gap:.5rem!important}.row-gap-lg-3{row-gap:1rem!important}.row-gap-lg-4{row-gap:1.5rem!important}.row-gap-lg-5{row-gap:3rem!important}.column-gap-lg-0{-moz-column-gap:0!important;column-gap:0!important}.column-gap-lg-1{-moz-column-gap:0.25rem!important;column-gap:.25rem!important}.column-gap-lg-2{-moz-column-gap:0.5rem!important;column-gap:.5rem!important}.column-gap-lg-3{-moz-column-gap:1rem!important;column-gap:1rem!important}.column-gap-lg-4{-moz-column-gap:1.5rem!important;column-gap:1.5rem!important}.column-gap-lg-5{-moz-column-gap:3rem!important;column-gap:3rem!important}.text-lg-start{text-align:left!important}.text-lg-end{text-align:right!important}.text-lg-center{text-align:center!important}}@media (min-width:1200px){.float-xl-start{float:left!important}.float-xl-end{float:right!important}.float-xl-none{float:none!important}.object-fit-xl-contain{-o-object-fit:contain!important;object-fit:contain!important}.object-fit-xl-cover{-o-object-fit:cover!important;object-fit:cover!important}.object-fit-xl-fill{-o-object-fit:fill!important;object-fit:fill!important}.object-fit-xl-scale{-o-object-fit:scale-down!important;object-fit:scale-down!important}.object-fit-xl-none{-o-object-fit:none!important;object-fit:none!important}.d-xl-inline{display:inline!important}.d-xl-inline-block{display:inline-block!important}.d-xl-block{display:block!important}.d-xl-grid{display:grid!important}.d-xl-table{display:table!important}.d-xl-table-row{display:table-row!important}.d-xl-table-cell{display:table-cell!important}.d-xl-flex{display:flex!important}.d-xl-inline-flex{display:inline-flex!important}.d-xl-none{display:none!important}.flex-xl-fill{flex:1 1 auto!important}.flex-xl-row{flex-direction:row!important}.flex-xl-column{flex-direction:column!important}.flex-xl-row-reverse{flex-direction:row-reverse!important}.flex-xl-column-reverse{flex-direction:column-reverse!important}.flex-xl-grow-0{flex-grow:0!important}.flex-xl-grow-1{flex-grow:1!important}.flex-xl-shrink-0{flex-shrink:0!important}.flex-xl-shrink-1{flex-shrink:1!important}.flex-xl-wrap{flex-wrap:wrap!important}.flex-xl-nowrap{flex-wrap:nowrap!important}.flex-xl-wrap-reverse{flex-wrap:wrap-reverse!important}.justify-content-xl-start{justify-content:flex-start!important}.justify-content-xl-end{justify-content:flex-end!important}.justify-content-xl-center{justify-content:center!important}.justify-content-xl-between{justify-content:space-between!important}.justify-content-xl-around{justify-content:space-around!important}.justify-content-xl-evenly{justify-content:space-evenly!important}.align-items-xl-start{align-items:flex-start!important}.align-items-xl-end{align-items:flex-end!important}.align-items-xl-center{align-items:center!important}.align-items-xl-baseline{align-items:baseline!important}.align-items-xl-stretch{align-items:stretch!important}.align-content-xl-start{align-content:flex-start!important}.align-content-xl-end{align-content:flex-end!important}.align-content-xl-center{align-content:center!important}.align-content-xl-between{align-content:space-between!important}.align-content-xl-around{align-content:space-around!important}.align-content-xl-stretch{align-content:stretch!important}.align-self-xl-auto{align-self:auto!important}.align-self-xl-start{align-self:flex-start!important}.align-self-xl-end{align-self:flex-end!important}.align-self-xl-center{align-self:center!important}.align-self-xl-baseline{align-self:baseline!important}.align-self-xl-stretch{align-self:stretch!important}.order-xl-first{order:-1!important}.order-xl-0{order:0!important}.order-xl-1{order:1!important}.order-xl-2{order:2!important}.order-xl-3{order:3!important}.order-xl-4{order:4!important}.order-xl-5{order:5!important}.order-xl-last{order:6!important}.m-xl-0{margin:0!important}.m-xl-1{margin:.25rem!important}.m-xl-2{margin:.5rem!important}.m-xl-3{margin:1rem!important}.m-xl-4{margin:1.5rem!important}.m-xl-5{margin:3rem!important}.m-xl-auto{margin:auto!important}.mx-xl-0{margin-right:0!important;margin-left:0!important}.mx-xl-1{margin-right:.25rem!important;margin-left:.25rem!important}.mx-xl-2{margin-right:.5rem!important;margin-left:.5rem!important}.mx-xl-3{margin-right:1rem!important;margin-left:1rem!important}.mx-xl-4{margin-right:1.5rem!important;margin-left:1.5rem!important}.mx-xl-5{margin-right:3rem!important;margin-left:3rem!important}.mx-xl-auto{margin-right:auto!important;margin-left:auto!important}.my-xl-0{margin-top:0!important;margin-bottom:0!important}.my-xl-1{margin-top:.25rem!important;margin-bottom:.25rem!important}.my-xl-2{margin-top:.5rem!important;margin-bottom:.5rem!important}.my-xl-3{margin-top:1rem!important;margin-bottom:1rem!important}.my-xl-4{margin-top:1.5rem!important;margin-bottom:1.5rem!important}.my-xl-5{margin-top:3rem!important;margin-bottom:3rem!important}.my-xl-auto{margin-top:auto!important;margin-bottom:auto!important}.mt-xl-0{margin-top:0!important}.mt-xl-1{margin-top:.25rem!important}.mt-xl-2{margin-top:.5rem!important}.mt-xl-3{margin-top:1rem!important}.mt-xl-4{margin-top:1.5rem!important}.mt-xl-5{margin-top:3rem!important}.mt-xl-auto{margin-top:auto!important}.me-xl-0{margin-right:0!important}.me-xl-1{margin-right:.25rem!important}.me-xl-2{margin-right:.5rem!important}.me-xl-3{margin-right:1rem!important}.me-xl-4{margin-right:1.5rem!important}.me-xl-5{margin-right:3rem!important}.me-xl-auto{margin-right:auto!important}.mb-xl-0{margin-bottom:0!important}.mb-xl-1{margin-bottom:.25rem!important}.mb-xl-2{margin-bottom:.5rem!important}.mb-xl-3{margin-bottom:1rem!important}.mb-xl-4{margin-bottom:1.5rem!important}.mb-xl-5{margin-bottom:3rem!important}.mb-xl-auto{margin-bottom:auto!important}.ms-xl-0{margin-left:0!important}.ms-xl-1{margin-left:.25rem!important}.ms-xl-2{margin-left:.5rem!important}.ms-xl-3{margin-left:1rem!important}.ms-xl-4{margin-left:1.5rem!important}.ms-xl-5{margin-left:3rem!important}.ms-xl-auto{margin-left:auto!important}.p-xl-0{padding:0!important}.p-xl-1{padding:.25rem!important}.p-xl-2{padding:.5rem!important}.p-xl-3{padding:1rem!important}.p-xl-4{padding:1.5rem!important}.p-xl-5{padding:3rem!important}.px-xl-0{padding-right:0!important;padding-left:0!important}.px-xl-1{padding-right:.25rem!important;padding-left:.25rem!important}.px-xl-2{padding-right:.5rem!important;padding-left:.5rem!important}.px-xl-3{padding-right:1rem!important;padding-left:1rem!important}.px-xl-4{padding-right:1.5rem!important;padding-left:1.5rem!important}.px-xl-5{padding-right:3rem!important;padding-left:3rem!important}.py-xl-0{padding-top:0!important;padding-bottom:0!important}.py-xl-1{padding-top:.25rem!important;padding-bottom:.25rem!important}.py-xl-2{padding-top:.5rem!important;padding-bottom:.5rem!important}.py-xl-3{padding-top:1rem!important;padding-bottom:1rem!important}.py-xl-4{padding-top:1.5rem!important;padding-bottom:1.5rem!important}.py-xl-5{padding-top:3rem!important;padding-bottom:3rem!important}.pt-xl-0{padding-top:0!important}.pt-xl-1{padding-top:.25rem!important}.pt-xl-2{padding-top:.5rem!important}.pt-xl-3{padding-top:1rem!important}.pt-xl-4{padding-top:1.5rem!important}.pt-xl-5{padding-top:3rem!important}.pe-xl-0{padding-right:0!important}.pe-xl-1{padding-right:.25rem!important}.pe-xl-2{padding-right:.5rem!important}.pe-xl-3{padding-right:1rem!important}.pe-xl-4{padding-right:1.5rem!important}.pe-xl-5{padding-right:3rem!important}.pb-xl-0{padding-bottom:0!important}.pb-xl-1{padding-bottom:.25rem!important}.pb-xl-2{padding-bottom:.5rem!important}.pb-xl-3{padding-bottom:1rem!important}.pb-xl-4{padding-bottom:1.5rem!important}.pb-xl-5{padding-bottom:3rem!important}.ps-xl-0{padding-left:0!important}.ps-xl-1{padding-left:.25rem!important}.ps-xl-2{padding-left:.5rem!important}.ps-xl-3{padding-left:1rem!important}.ps-xl-4{padding-left:1.5rem!important}.ps-xl-5{padding-left:3rem!important}.gap-xl-0{gap:0!important}.gap-xl-1{gap:.25rem!important}.gap-xl-2{gap:.5rem!important}.gap-xl-3{gap:1rem!important}.gap-xl-4{gap:1.5rem!important}.gap-xl-5{gap:3rem!important}.row-gap-xl-0{row-gap:0!important}.row-gap-xl-1{row-gap:.25rem!important}.row-gap-xl-2{row-gap:.5rem!important}.row-gap-xl-3{row-gap:1rem!important}.row-gap-xl-4{row-gap:1.5rem!important}.row-gap-xl-5{row-gap:3rem!important}.column-gap-xl-0{-moz-column-gap:0!important;column-gap:0!important}.column-gap-xl-1{-moz-column-gap:0.25rem!important;column-gap:.25rem!important}.column-gap-xl-2{-moz-column-gap:0.5rem!important;column-gap:.5rem!important}.column-gap-xl-3{-moz-column-gap:1rem!important;column-gap:1rem!important}.column-gap-xl-4{-moz-column-gap:1.5rem!important;column-gap:1.5rem!important}.column-gap-xl-5{-moz-column-gap:3rem!important;column-gap:3rem!important}.text-xl-start{text-align:left!important}.text-xl-end{text-align:right!important}.text-xl-center{text-align:center!important}}@media (min-width:1400px){.float-xxl-start{float:left!important}.float-xxl-end{float:right!important}.float-xxl-none{float:none!important}.object-fit-xxl-contain{-o-object-fit:contain!important;object-fit:contain!important}.object-fit-xxl-cover{-o-object-fit:cover!important;object-fit:cover!important}.object-fit-xxl-fill{-o-object-fit:fill!important;object-fit:fill!important}.object-fit-xxl-scale{-o-object-fit:scale-down!important;object-fit:scale-down!important}.object-fit-xxl-none{-o-object-fit:none!important;object-fit:none!important}.d-xxl-inline{display:inline!important}.d-xxl-inline-block{display:inline-block!important}.d-xxl-block{display:block!important}.d-xxl-grid{display:grid!important}.d-xxl-table{display:table!important}.d-xxl-table-row{display:table-row!important}.d-xxl-table-cell{display:table-cell!important}.d-xxl-flex{display:flex!important}.d-xxl-inline-flex{display:inline-flex!important}.d-xxl-none{display:none!important}.flex-xxl-fill{flex:1 1 auto!important}.flex-xxl-row{flex-direction:row!important}.flex-xxl-column{flex-direction:column!important}.flex-xxl-row-reverse{flex-direction:row-reverse!important}.flex-xxl-column-reverse{flex-direction:column-reverse!important}.flex-xxl-grow-0{flex-grow:0!important}.flex-xxl-grow-1{flex-grow:1!important}.flex-xxl-shrink-0{flex-shrink:0!important}.flex-xxl-shrink-1{flex-shrink:1!important}.flex-xxl-wrap{flex-wrap:wrap!important}.flex-xxl-nowrap{flex-wrap:nowrap!important}.flex-xxl-wrap-reverse{flex-wrap:wrap-reverse!important}.justify-content-xxl-start{justify-content:flex-start!important}.justify-content-xxl-end{justify-content:flex-end!important}.justify-content-xxl-center{justify-content:center!important}.justify-content-xxl-between{justify-content:space-between!important}.justify-content-xxl-around{justify-content:space-around!important}.justify-content-xxl-evenly{justify-content:space-evenly!important}.align-items-xxl-start{align-items:flex-start!important}.align-items-xxl-end{align-items:flex-end!important}.align-items-xxl-center{align-items:center!important}.align-items-xxl-baseline{align-items:baseline!important}.align-items-xxl-stretch{align-items:stretch!important}.align-content-xxl-start{align-content:flex-start!important}.align-content-xxl-end{align-content:flex-end!important}.align-content-xxl-center{align-content:center!important}.align-content-xxl-between{align-content:space-between!important}.align-content-xxl-around{align-content:space-around!important}.align-content-xxl-stretch{align-content:stretch!important}.align-self-xxl-auto{align-self:auto!important}.align-self-xxl-start{align-self:flex-start!important}.align-self-xxl-end{align-self:flex-end!important}.align-self-xxl-center{align-self:center!important}.align-self-xxl-baseline{align-self:baseline!important}.align-self-xxl-stretch{align-self:stretch!important}.order-xxl-first{order:-1!important}.order-xxl-0{order:0!important}.order-xxl-1{order:1!important}.order-xxl-2{order:2!important}.order-xxl-3{order:3!important}.order-xxl-4{order:4!important}.order-xxl-5{order:5!important}.order-xxl-last{order:6!important}.m-xxl-0{margin:0!important}.m-xxl-1{margin:.25rem!important}.m-xxl-2{margin:.5rem!important}.m-xxl-3{margin:1rem!important}.m-xxl-4{margin:1.5rem!important}.m-xxl-5{margin:3rem!important}.m-xxl-auto{margin:auto!important}.mx-xxl-0{margin-right:0!important;margin-left:0!important}.mx-xxl-1{margin-right:.25rem!important;margin-left:.25rem!important}.mx-xxl-2{margin-right:.5rem!important;margin-left:.5rem!important}.mx-xxl-3{margin-right:1rem!important;margin-left:1rem!important}.mx-xxl-4{margin-right:1.5rem!important;margin-left:1.5rem!important}.mx-xxl-5{margin-right:3rem!important;margin-left:3rem!important}.mx-xxl-auto{margin-right:auto!important;margin-left:auto!important}.my-xxl-0{margin-top:0!important;margin-bottom:0!important}.my-xxl-1{margin-top:.25rem!important;margin-bottom:.25rem!important}.my-xxl-2{margin-top:.5rem!important;margin-bottom:.5rem!important}.my-xxl-3{margin-top:1rem!important;margin-bottom:1rem!important}.my-xxl-4{margin-top:1.5rem!important;margin-bottom:1.5rem!important}.my-xxl-5{margin-top:3rem!important;margin-bottom:3rem!important}.my-xxl-auto{margin-top:auto!important;margin-bottom:auto!important}.mt-xxl-0{margin-top:0!important}.mt-xxl-1{margin-top:.25rem!important}.mt-xxl-2{margin-top:.5rem!important}.mt-xxl-3{margin-top:1rem!important}.mt-xxl-4{margin-top:1.5rem!important}.mt-xxl-5{margin-top:3rem!important}.mt-xxl-auto{margin-top:auto!important}.me-xxl-0{margin-right:0!important}.me-xxl-1{margin-right:.25rem!important}.me-xxl-2{margin-right:.5rem!important}.me-xxl-3{margin-right:1rem!important}.me-xxl-4{margin-right:1.5rem!important}.me-xxl-5{margin-right:3rem!important}.me-xxl-auto{margin-right:auto!important}.mb-xxl-0{margin-bottom:0!important}.mb-xxl-1{margin-bottom:.25rem!important}.mb-xxl-2{margin-bottom:.5rem!important}.mb-xxl-3{margin-bottom:1rem!important}.mb-xxl-4{margin-bottom:1.5rem!important}.mb-xxl-5{margin-bottom:3rem!important}.mb-xxl-auto{margin-bottom:auto!important}.ms-xxl-0{margin-left:0!important}.ms-xxl-1{margin-left:.25rem!important}.ms-xxl-2{margin-left:.5rem!important}.ms-xxl-3{margin-left:1rem!important}.ms-xxl-4{margin-left:1.5rem!important}.ms-xxl-5{margin-left:3rem!important}.ms-xxl-auto{margin-left:auto!important}.p-xxl-0{padding:0!important}.p-xxl-1{padding:.25rem!important}.p-xxl-2{padding:.5rem!important}.p-xxl-3{padding:1rem!important}.p-xxl-4{padding:1.5rem!important}.p-xxl-5{padding:3rem!important}.px-xxl-0{padding-right:0!important;padding-left:0!important}.px-xxl-1{padding-right:.25rem!important;padding-left:.25rem!important}.px-xxl-2{padding-right:.5rem!important;padding-left:.5rem!important}.px-xxl-3{padding-right:1rem!important;padding-left:1rem!important}.px-xxl-4{padding-right:1.5rem!important;padding-left:1.5rem!important}.px-xxl-5{padding-right:3rem!important;padding-left:3rem!important}.py-xxl-0{padding-top:0!important;padding-bottom:0!important}.py-xxl-1{padding-top:.25rem!important;padding-bottom:.25rem!important}.py-xxl-2{padding-top:.5rem!important;padding-bottom:.5rem!important}.py-xxl-3{padding-top:1rem!important;padding-bottom:1rem!important}.py-xxl-4{padding-top:1.5rem!important;padding-bottom:1.5rem!important}.py-xxl-5{padding-top:3rem!important;padding-bottom:3rem!important}.pt-xxl-0{padding-top:0!important}.pt-xxl-1{padding-top:.25rem!important}.pt-xxl-2{padding-top:.5rem!important}.pt-xxl-3{padding-top:1rem!important}.pt-xxl-4{padding-top:1.5rem!important}.pt-xxl-5{padding-top:3rem!important}.pe-xxl-0{padding-right:0!important}.pe-xxl-1{padding-right:.25rem!important}.pe-xxl-2{padding-right:.5rem!important}.pe-xxl-3{padding-right:1rem!important}.pe-xxl-4{padding-right:1.5rem!important}.pe-xxl-5{padding-right:3rem!important}.pb-xxl-0{padding-bottom:0!important}.pb-xxl-1{padding-bottom:.25rem!important}.pb-xxl-2{padding-bottom:.5rem!important}.pb-xxl-3{padding-bottom:1rem!important}.pb-xxl-4{padding-bottom:1.5rem!important}.pb-xxl-5{padding-bottom:3rem!important}.ps-xxl-0{padding-left:0!important}.ps-xxl-1{padding-left:.25rem!important}.ps-xxl-2{padding-left:.5rem!important}.ps-xxl-3{padding-left:1rem!important}.ps-xxl-4{padding-left:1.5rem!important}.ps-xxl-5{padding-left:3rem!important}.gap-xxl-0{gap:0!important}.gap-xxl-1{gap:.25rem!important}.gap-xxl-2{gap:.5rem!important}.gap-xxl-3{gap:1rem!important}.gap-xxl-4{gap:1.5rem!important}.gap-xxl-5{gap:3rem!important}.row-gap-xxl-0{row-gap:0!important}.row-gap-xxl-1{row-gap:.25rem!important}.row-gap-xxl-2{row-gap:.5rem!important}.row-gap-xxl-3{row-gap:1rem!important}.row-gap-xxl-4{row-gap:1.5rem!important}.row-gap-xxl-5{row-gap:3rem!important}.column-gap-xxl-0{-moz-column-gap:0!important;column-gap:0!important}.column-gap-xxl-1{-moz-column-gap:0.25rem!important;column-gap:.25rem!important}.column-gap-xxl-2{-moz-column-gap:0.5rem!important;column-gap:.5rem!important}.column-gap-xxl-3{-moz-column-gap:1rem!important;column-gap:1rem!important}.column-gap-xxl-4{-moz-column-gap:1.5rem!important;column-gap:1.5rem!important}.column-gap-xxl-5{-moz-column-gap:3rem!important;column-gap:3rem!important}.text-xxl-start{text-align:left!important}.text-xxl-end{text-align:right!important}.text-xxl-center{text-align:center!important}}@media (min-width:1200px){.fs-1{font-size:2.5rem!important}.fs-2{font-size:2rem!important}.fs-3{font-size:1.75rem!important}.fs-4{font-size:1.5rem!important}}@media print{.d-print-inline{display:inline!important}.d-print-inline-block{display:inline-block!important}.d-print-block{display:block!important}.d-print-grid{display:grid!important}.d-print-table{display:table!important}.d-print-table-row{display:table-row!important}.d-print-table-cell{display:table-cell!important}.d-print-flex{display:flex!important}.d-print-inline-flex{display:inline-flex!important}.d-print-none{display:none!important}} \ No newline at end of file diff --git a/os3/css/search.css b/os3/css/search.css new file mode 100644 index 0000000..2b1493d --- /dev/null +++ b/os3/css/search.css @@ -0,0 +1,36 @@ +#os_results ol > li { + margin-bottom:1em; +} +#os_results ol > li > header > span, +#os_results ol > li > header > a { + font-size:120%; +} +#os_results ol > li > blockquote, +#os_results ol > li > blockquote > p{ + margin:0.2em 0; +} +#os_results ol > li > footer > cite { + color:#008800; +} +#os_results ol > li *:empty { + display:none; +} + +#os_results nav > ul { + list-style-type:none; + padding-left:0; + display:flex; + justify-content:center; +} +#os_results nav > ul li { + padding-left:0.3em; + padding-right:0.3em; +} + +#os_results > form { + text-align:center; +} + +#os_results > footer { + text-align:center; +} \ No newline at end of file diff --git a/os3/geoip2/README.txt b/os3/geoip2/README.txt new file mode 100644 index 0000000..c7b0dae --- /dev/null +++ b/os3/geoip2/README.txt @@ -0,0 +1,14 @@ +To enable the Geo-Location service, follow the steps below: + +1) Download the latest Maxmind GeoIP2 .phar file from the Github, and + place it in the same directory as this README.txt file: + https://github.com/maxmind/GeoIP2-php/releases + +2) Login at the Maxmind website; account registration is free: + https://www.maxmind.com/en/account/login + +3) Navigate to the "Downloads" area of your Maxmind account, and + download the GeoLite Country (not CSV) GZIP package. + +4) Unzip the 'GeoLite2-Country.mmdb' file and place it in the same + directory as this README.txt file. \ No newline at end of file diff --git a/os3/img/arrow-down.svg b/os3/img/arrow-down.svg new file mode 100644 index 0000000..8fedfce --- /dev/null +++ b/os3/img/arrow-down.svg @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/os3/img/clock.svg b/os3/img/clock.svg new file mode 100644 index 0000000..1e74e5d --- /dev/null +++ b/os3/img/clock.svg @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/os3/img/flags/README.txt b/os3/img/flags/README.txt new file mode 100644 index 0000000..0ebceed --- /dev/null +++ b/os3/img/flags/README.txt @@ -0,0 +1,3 @@ +Flag images from Flagpedia.net + +https://flagpedia.net/ \ No newline at end of file diff --git a/os3/img/flags/ad.png b/os3/img/flags/ad.png new file mode 100644 index 0000000000000000000000000000000000000000..eccc47028f51a8f6cfa488bed7b5556076a2f5bf GIT binary patch literal 384 zcmV-`0e}99P)04(vMaNiUe#h9!lh5csaf5PE!25j z?WTgrYDo9j1l5Kz?4e}-?Qh*#Ld`@k`PO60yHdgc006W}L_t(I%jMKb5`!QR1yF&| zNk9Pwr$pmy#`~Wyy3|h^?KBPv zrA-PpZEQ;LMm2o;N-!1_IFvB_-rpI5!yw6^d-dH6c{XB=3S`?poj*;a@H5tN7z$m5 zag9&yKW3;z9zRMH1|S&|CeJ1J@kRnk- z#1lAVCkrGjrIe-r|M&IK3(UmnrPFu*W<0i@{I+<`5>I)5dr2Iw{s;<8MCEsAVk%}L zv#>A$U@Cy|3{18L0A>K7O%{V?eFGqXKmc4e9}#%04}htN7#py?Zl+;dwgmtf-_?mN>Z6dHumg(pniv;tdUuF3;JSFXN7`$^1 z>ccRszOu(8tdgV{c?W>O%wxaKJ||&aAA4sWd?#`+x6NN3@@X!0xfOVra_SY*tRt7H zj&0*4Kz<6u%{8AsdXbzC$_iyOC|mQ$A|tG-A`m-fW=9nET^DUnABc7-v)?(Qa~AL8 z4QZ-sqP9zMnJMnUlwBb5j@8a{4QC@&K$VR9|6bSFPw@>+!W~IDsRD=q00007`;0j-Y?%@8I0(G`EV;?5B!|Ns90a>o4g^wcdt836>R5hVaPV^Ty0erXFoGzHyQ zXZF+A&lV~^1Pff4KZIo2HMTp*N8UK~Z1iDSW)KuCyLa|wfxokYD8D_@%H3@wzIVJ@-;{)#-j1}acy zGA=ITtPNBkT+j1%`@@s6!+uvDUFZ<;t8p>9&{~~`{XoSG|Y&iY_?FS!%r<1_R00000NkvXXu0mjfwy)Lf literal 0 HcmV?d00001 diff --git a/os3/img/flags/ai.png b/os3/img/flags/ai.png new file mode 100644 index 0000000000000000000000000000000000000000..54a31cc3595f55e897b24a83b76bb4a267ed2f5f GIT binary patch literal 369 zcmV-%0gnEOP)ci|n^ z`LDW*21S}gz>p)9+cS%c6zpLGt2)tF2dWhhwVYC_86yxO!U)l(q`M;8;Yabx`8aca z@uLlzn5hUs!kD(TOKOOpoq+Y$GfL7T&g^I0&A-=BEf{a258Zmc)oE2&v|{>*KJW{- zs!Qgz!<|}25aRJ_TdZUg6z@_(Dc~(Af~GWt8v;-bWjR#fjM?|6yuafOqI(KIAngn{ P00000NkvXXu0mjf73;a! literal 0 HcmV?d00001 diff --git a/os3/img/flags/al.png b/os3/img/flags/al.png new file mode 100644 index 0000000000000000000000000000000000000000..53fb8512b57f7746ffd69480fd0ea0113acea554 GIT binary patch literal 293 zcmeAS@N?(olHy`uVBq!ia0vp^8bB<|!VDy}(v2N}lx~1ei0gkKgMnc^14BInLp}pT zJW!Z{;S>WyKLf*k1_ply273mE_Y4dR7#R2&7(C8IdjnN(_H=O!(Kw%+ut0ytf#Sst zd%rWNcRl#ze7mSG^>xNVj&rw4BRwvbeU%b9xU}(6p+C2)b0Eu98CRjBGOkksSiE^y z8y#-Qx*DhmsA#>d9)*=qGXc;e&%bD981lOd@*ch;Ca-vn#1HpNuTEVc0*K}K# z#;RWreSuPPo-U3d5|@(`Bmxq8SPeLm7{3UY9AuK1vVmb2ClkZAyNafA8`%SaDj7Uo L{an^LB{Ts5!e$+P literal 0 HcmV?d00001 diff --git a/os3/img/flags/ao.png b/os3/img/flags/ao.png new file mode 100644 index 0000000000000000000000000000000000000000..8d0e951a358fb427fcb1ed8575afa6aac7fa86ab GIT binary patch literal 339 zcmeAS@N?(olHy`uVBq!ia0vp^8bB<~!3-oV&)LQRsi**-5LX~Az{_w|SM8##RvL@= zDGt5=rx}2ZGo1QBDSvy0hmq0;W-=(tFnr$6@UDaZ(;C)zZ-xm)45hIQcia>f)H7UI z%CMn_;nOVc`F?yaOGP$ka38Jba4=@jt=~N#Xaq+|kYDiM|6uU%*y2=spx6RW7sn8b z(^vaX@*P&-aWORUJHzLD>F@vg&2A@K!?z?vm+Y5VrpNne$%094rHw|9?nvg= zgvdUa?7NZoRD+}6L%rF3Y`o%H{>*biS2nH9*eNf5nBO8UYwx|=N8Go{wJwO_6o0TV zcduBzLhMDWMK2x-=f`PBoxbO0nc4IA{5pNk|Ns9+h0#)t)V0p-cctCV-}8{RCs;!V$c(^r@*03eV{_mFVLQC8slc8^Og4NgdWycu02WV||Y53B4*2z$&A z-@_td4nV*d1;!0ZHf?#TCT*6`Y7o1GssjsHg9@F1N$m(y_F5|$Sb===zz*gz|L9@N zxcElRf3}Dw^LSh_j}2#si^cFvVC&a#4mW2#xx93)&Cfr8Cl)Xr9qbxKF8}}l07*qo IM6N<$f~%K^EdT%j literal 0 HcmV?d00001 diff --git a/os3/img/flags/ar.png b/os3/img/flags/ar.png new file mode 100644 index 0000000000000000000000000000000000000000..dbe8d69dc907d14fbc1f4419ba94a9d5b29b8bda GIT binary patch literal 220 zcmV<203-j2P)X0{{R3bOxMP0000#P)t-s|NsB) z*01Qxn9`q8_wC&I^zQE5y>zVK(c|>}{QT(7sNcGNBwzOnYLwRaLsfss_ zYY~oY6n=DO%D|$qnOq430p0)r0L@86K~yNumD1-9f-n>X;H!17suU2}!@+is_kS^g z$OtJ|&##hvY09}5h71{vdwpO*oyl|tOehTlGJT@~W@x@xWjE=*dN3(4MS~mx7_zD! zWxWP7wC*k;4?Y|bk2ra^)1VQh(KtSFoFbg|P@rvhBSI}!;35UNI6XI_B%+0di-OSk z7ld2#y8<5+JyPG7kcf~sk(|)07EZ$&SRQd~dk?gF$_^ox2!83`kG84A(nGpOS7@Ad qIp@%LIyPoq3TTDQYxdXTR`Cb0AR4(!4^c`00000Eal|aXmRff%^|T<0Q{b3=9V5I#+)B9ry>7W$<+Mb6Mw<&;$VQh#AEI literal 0 HcmV?d00001 diff --git a/os3/img/flags/au.png b/os3/img/flags/au.png new file mode 100644 index 0000000000000000000000000000000000000000..f7bf79f2aaadc5c74112416167e8228ac366389f GIT binary patch literal 312 zcmV-80muG{P)<{9u1Q2eR4C82klPl7AP7atFf_b$nfm`vov39w*=qSR z=S6{y2Z64M7B(3oBWK%p4LnH-f*znmg9Ug&1uQU|1!mK{PM(SU7diZvFZ9&K4S;D-CPx`3`^^|Q0LN%2M4K;7B1+0Gc~H_ z$FWFhUDb(US;D7=49ua1Q-;~7V#0@Uv~xukW5g0fEZdcW|LF_i^apt3rPwF{0000< KMNUMnLSTYA2$2p#HE+__pVM{^te_0@E)Kprjj7P;J^QRTY4`8dA6P| zjv*T7lM~F2F|hGiu&iy~$Z<<0S#ZzIR!s@By-HIp9M|017~OK!^T_WE1=gt>7}x~G hc^-HOzn#L##1MEjJE**Ub_~!U22WQ%mvv4FO#p=FMfU&z literal 0 HcmV?d00001 diff --git a/os3/img/flags/ax.png b/os3/img/flags/ax.png new file mode 100644 index 0000000000000000000000000000000000000000..e0ca965b9c7279aa0f77fb07419d0cb073badd64 GIT binary patch literal 165 zcmeAS@N?(olHy`uVBq!ia0vp^8bBzopr00}rU8vpgTe~DWM4ftcg6D literal 0 HcmV?d00001 diff --git a/os3/img/flags/ba.png b/os3/img/flags/ba.png new file mode 100644 index 0000000000000000000000000000000000000000..4afae8c0620e1eb76b67ca91a6b778cfb52ec282 GIT binary patch literal 235 zcmeAS@N?(olHy`uVBq!ia0vp^8bB<5DG~3}$cGbuS=en|_$AM{VuTE9tU&;gQu&X%Q~loCIF%(RuljL literal 0 HcmV?d00001 diff --git a/os3/img/flags/bb.png b/os3/img/flags/bb.png new file mode 100644 index 0000000000000000000000000000000000000000..6457ab763874090c43ee01d8be33bb9e0aaceb68 GIT binary patch literal 212 zcmeAS@N?(olHy`uVBq!ia0vp^8bB<~!VDz;*j?A+8K+_5Y8nt(xF$Z^&_Z ziJXE2i-9UzlpF8-8u9nLl=n}SDUK2Vs$Jh9#m~dENpy7+kdx%;;uxZFJ~@GbSR$T3(pI#$92%E5Qad9PUu`q=! z?G$h{6*=h8u|Q+l#20N^PAsh)+(}pat}asPSYVZSJo&2{bGOno21eFc3uawLhRVs@ Vi&lhwGXt8=;OXk;vd$@?2>@qwF?Ij| literal 0 HcmV?d00001 diff --git a/os3/img/flags/be.png b/os3/img/flags/be.png new file mode 100644 index 0000000000000000000000000000000000000000..cb01ae4dbf97b2305a7f0a658427cf195488c0cc GIT binary patch literal 109 zcmeAS@N?(olHy`uVBq!ia0vp^8bGYf!VDxu#6#x-DgFST5ZAYwmO$pO2TtD>+AW)^ zQ#4~oE|4$j>EaloaXwjqg;`M5z#yTaA@E?}!9+z4hC^$OmAT$~^aB+!c)I$ztaD0e F0st(#96kU5 literal 0 HcmV?d00001 diff --git a/os3/img/flags/bf.png b/os3/img/flags/bf.png new file mode 100644 index 0000000000000000000000000000000000000000..46cd68c851902f68df057352a0acaf8d86e1bbd9 GIT binary patch literal 198 zcmeAS@N?(olHy`uVBq!ia0vp^8bB<~!VDz;*j?A+F^qRt)nz-)rkuBwPKu zAey_y@S~mPkGV3x_KDBftGRxH*8MY5-@E0$^~fnKaoo9}@()lWV@Z%-@IR@??6p9S zyQhm|h{pNkfJ00YZF3ex35hx0b&g;$on^ps)hj0EP9mFufwXhO)b7(s8kZX!1=zM# u9G+R=Xn9_9M$@9kMU4_oClWlknHY>-+idvyS#Sc-NCr<=KbLh*2~7a@s71*D literal 0 HcmV?d00001 diff --git a/os3/img/flags/bg.png b/os3/img/flags/bg.png new file mode 100644 index 0000000000000000000000000000000000000000..ca0cba6d1e456601d867de0804578d95f83ee989 GIT binary patch literal 99 zcmeAS@N?(olHy`uVBq!ia0vp^8bB<;#0(_Az6uWpQk(%kA+8M5@~)`~{r~^}pzr^A wKtWMY7sn8Z%gGU}>^vcmdKI;Vst0I!o9T>t<8 literal 0 HcmV?d00001 diff --git a/os3/img/flags/bh.png b/os3/img/flags/bh.png new file mode 100644 index 0000000000000000000000000000000000000000..05719e105beb3b6fb9876916d10b7de8ce3a62b0 GIT binary patch literal 208 zcmeAS@N?(olHy`uVBq!ia0vp^8bB<;!VDyDU3DlC}>Eak7aXDFlg>fqzn+SuL zh)HCFYrqAO?gg({d=9?li8$k8EVL?CgL$@V){Le@?;^I$KawUmYhr+L>)s8VyOJ-; zb-MoM%n@5C*?Iiqi#8{wB@Er)VsGd;^{d!iY)sSGroqJUCrNbvfkij0f%Y+Yy85}S Ib4q9e0LZ&i7XSbN literal 0 HcmV?d00001 diff --git a/os3/img/flags/bi.png b/os3/img/flags/bi.png new file mode 100644 index 0000000000000000000000000000000000000000..ade818a081e9d8765394df207db784ce3055c77b GIT binary patch literal 364 zcmV-y0h9iTP)I>6f7V@$%tncl!SOoZhIN-l>+| zqukia=ZKU3`TFp+ySO7K&8M8&OkBZqYrtn=y*)kMTWU~B#g_m80L@86K~yNujgv_d zgCGz^zan;Q2b^)r{jZq?5-|pfUQjGv@u2w)09pD9R+_<9CH$DE43ifCP&YoVsR5wK zL6k^%{l+R2A`ns8dHkofE6IRFuKzt|*|@q^qAz&)#cZxVi_3Zgt$zY|Ss0iu>m9Uy zKx!?`?zH}b)^AR9tWS`7IMtIMEL^Y%f@&BP%o+lFus#^zLvu)-NbtTfz}wFL$C&mg zb*+EfQ?|C@a5n9u;gMhHhGO-H;s0>&DV_kI5Z4!ix_>T;F|-GMDpXvv z>L?>fk*AAeh{WaO2v(Mb2?q=!X8b?Ma3-ap!SMhKLtT3M%wvn*#sZZwc)I$ztaD0e F0syi;AVvTH literal 0 HcmV?d00001 diff --git a/os3/img/flags/bl.png b/os3/img/flags/bl.png new file mode 100644 index 0000000000000000000000000000000000000000..655935f38e1c2137c8d6bfb5cc49cf33cf0bfe5f GIT binary patch literal 970 zcmV;*12z1KP)BkWx6H>J2$szaj z?BKy204&Wxx(${i$>Fx31%TFL%vEbm~6;Z9KDN66;q+NG@nU_lD*S7KKxa!-A=hk-C zjEL>%!?asNZkmcUa^6J{>*r)Q}%i+Vb>e5_%nZ?8p zA=Z6-*`%ejn-;mJGQG{<%hu(%vTfh1Doc4F6eJ1}LnIclu! z+1S%mVvdeg+J;TklX06iS50q$!k0S2m^#;uGU0tZP%K~y-)eUxW&n?MkT#R-lph9E=)NJu#$tM@LA zJhsQ(PHe|rSFc>_T^K^AJ zP6t6Afh5DT`o*2GF6w7cmdG^708z^b4v$8fc3Ri8(cm{kWmN*q4s;B`Q$_=-&)bRRSokhQr|p$H(`1s!|FFrDS5)kgAjk zwC{m$^B(vS3yd$q$#WE`RF=j5Ev}6XFZPL0CRCg{aHdFtfp;sXrg>B`-=$A>bweO2 zHVGm@VT!X?D1G~~tGI)7df-;>a^D}eAeizfi<|G6 zN4_eVFJ6~8huF)mcvDm4))KiMl%KDg=6XSfy(^b07*qoM6N<$g5KKr=l}o! literal 0 HcmV?d00001 diff --git a/os3/img/flags/bm.png b/os3/img/flags/bm.png new file mode 100644 index 0000000000000000000000000000000000000000..707e96e2ce00956335cfbd9ed3562af1836d6661 GIT binary patch literal 536 zcmV+z0_XjSP)f$^jv1z{uLh4=vSyjnsaOK=))S<1|b%x=huH~+`u*8SE4l3Z@yv(>$ zd$XbU`1{!1=+@8Nxg<7+c~!y=E1G3dnPgChHY|$3gS;Fzcs(K3>C}#8GPcdF!pxT4 z+pe5JHkhE1h_|J*v30ILF`c}xfwswZN-UkJc%P3_ptp>$n~I3U+QVH&($mt3q*-K0B(k(AgJEMHR1jc aH_0!T`Vy;h=8F{o0000=0g1N@c;k-|Ns991_RbIA>nCu&=e^B`OTP$4g2z(%()W&{rV6O z1}!QH`|f#+jEnKt7UX;#;cFd11Pkhwof-lJ`QhXC($-uD5IQvl|NitG92!zmPxbZn zXI%+tWHkHmkv={?b#-<3;U50{>H6tbw6wJN<0qx1rT+We?W`KzTOIuMv9dfGp+-=y z6CI^580UwQvL+m(85<}M1pW8KrIru)|0?WL{cc!X^T6j>7vhCW7P zA0|>pNAU3QwGJUa{p!;I00ALML_t&-8I8f!a>4)*0KkjhB@}mecb6)4cX#*ye=d~C zOrRaeE@);GKpP^n-pC6u5eqMshB(eHZyR#GE;rm>Hx7g&_LV3Rj3~+S z^;NZ6K0QV?YaB*6N-meH>~tRO?`>O91;*K!SqNHi046dSVnGQJv@sb#n=bqS>P{ue TlFi8V00000NkvXXu0mjfs0>&Db4_&5Z9~PatxIwKOZx29WImw w3W|EVIEF}EPEJ^08^OwMlH7KPaqSF-(r1y&ZwF}J1}b3iboFyt=akR{05u&NKmY&$ literal 0 HcmV?d00001 diff --git a/os3/img/flags/bq.png b/os3/img/flags/bq.png new file mode 100644 index 0000000000000000000000000000000000000000..53b237a1469c169c3ffbd160da22874f1a158b35 GIT binary patch literal 489 zcmVR`cXznBxB)7M`PmQM7ajBT_3iKWuDjV$ZLJbHkNNrh`r$wP?1rhS zsr%vLPft%dIXTD2$MBh(Z->0j)#u~r@-b7M{`k>@gM+`x;hn9~ftADm{qOnREI?nS z{`SIHSXkcP-yR+w1}b}-``?lVq;@t?1F{zr>Wj0D*yW6VSBXv z>32{cObh@30Rl-xK~y-)jnHdP!Y}}Z;j^vnQ7&`rs>noC1_~me{{O#btC(5SlAaH1 zvOIa46c_fgcepkj!*&xV!ziT%vzaGHTF{q^X+dA^gS6lTvURw-m=?@tqO7V?{9}V_ z(tM)|E4G zVKS=@g@FaSm2l(?{NA!gV_<`NX>#`o-Efnm*&mPl2Kh$kZb=VBsUPQX&<|8Pb3KgS zqSNu>%JmS@P6{D+(R0G{5U8uF_T?;y*Up=>;q;LVf%AkR@O-X^^OPZQIwnKlC}jv7 f2ILG3%ISRpEyo^1hRTED00000NkvXXu0mjfAqn|_ literal 0 HcmV?d00001 diff --git a/os3/img/flags/br.png b/os3/img/flags/br.png new file mode 100644 index 0000000000000000000000000000000000000000..41b82bcc0760b8d3d9768ede9f9d14d444d4ebf9 GIT binary patch literal 449 zcmV;y0Y3hTP)xd4uP6wfH~^bE04H`6pfwLMZy!L0kB>Cx*#$pTSS?JBJ6e_a-2tx2 z5}Cvo(bEd3zsrrm8GO7RUVfz@K4;(72M{!V0h>Bwwj;sL4Wq^t*6rXQJ!g`+xFAA@ zt<1mg+5<{$nPYT#OLL5zuWz;C#lqmyI#hX-#PwhR008w#L_t(I%e~ae5`r)c0MHfH`y#k+|NnhvD2pQ?^+FCkWZq0jngVOdfJLCDNkJn32#k5CCxDUYGm!3~i9P)R zv-}K-<(Q^pDPZT>6jTdw^vvRSxsNzA4{>$ rGRZYVD!aJiDR?qZLH= literal 0 HcmV?d00001 diff --git a/os3/img/flags/bs.png b/os3/img/flags/bs.png new file mode 100644 index 0000000000000000000000000000000000000000..e5716035e0366ec38c857974d38ff56da905609a GIT binary patch literal 211 zcmeAS@N?(olHy`uVBq!ia0vp^8bB<V3n|Nq%bixxQKKfIRSCLrc*w|It$MN1c_lV-1g*ed}oJGa_|h248y zL(eIfUv|ihRt$M#?_BZas2=lmXK}vy0@IqV@rX^YYn477BKD`mG}05~5KmV>mvv4F FO#pYaL;?T+ literal 0 HcmV?d00001 diff --git a/os3/img/flags/bt.png b/os3/img/flags/bt.png new file mode 100644 index 0000000000000000000000000000000000000000..fbaf574e42dd1f7777952c9873c62b157b1b8b8c GIT binary patch literal 628 zcmV-)0*n2LP)NB${{H^_{QUn}AOENt|4tJB)gb@ZE&ABa`rp?1@a_BETKMRt|9Tex${zlB zOZ=^p{NsH2^WOY{SN~ZO|JO16ns@u&WB%DY`pv%jqJ#e2NBiX5`jBb+c1Zj4)ceuG z`Ldb+svG$0vHFy9`s0WE;&J@GsQT}}_~*F$_4EAr=KSg4`i5Wn@yYz!NBOs){NvC2 z=g9WKulm`R{K~lex}*JVJo?U>`r?%Owom+QJN#fP{H<2^?&bb;Fv`lx|7H}Mw5=)t z000PdQchF-|NsC0|Ns48sx*ZF00B%%L_t(I%Z=0Na)K}v1z-Z)^s*}g4x1oQcigRO z_rCu#RmILYEuoxWVfZ+cJ0u(@)k(9#)q;<0tOJ`3#DAeIU+UP<^9I9Wajj!P*|UsZ zZ#*7I@;1f_xb%z!fWYT;y^FhSxYT=gP6&ZbzN1*xD4qKz3z)^(zI&*Gx=o0&{czN* z@0zq~kJc34(>ca-Py|+^Z*@G$pNOD=5|Kc4%b`D9*wgXgEhC_|6y8~dv>aLqoTmj% zC1N1(c$Z($Aj#>ds1X2!h-^4qkkc2U&<{bQRj^}&lU{yBss$!GD<+%(q@c`^3MQ{< zqS7ycOI^~BxiTB6urFSQVS@5o|D%?Z6mcwt(12aVxjtD3K O0000J^H SwhXmE)Yk4S50lFRV3Zd$eR`GeNjbsc7OTDUHe4;Ub6=p zlIH2+7@~20?fHYE4GKKW0a=%m7##1`N4P7n7ynzI$(5K_I{#6NV`Jy_*-Yw_gBt}? zysnn7Zf9a=TVispJp+N32U%>6xOPxRid2^>L9lUE)*^17dC?uMlf chn959mpvEATI4;Y3TPvPr>mdKI;Vst0I`={+W-In literal 0 HcmV?d00001 diff --git a/os3/img/flags/bz.png b/os3/img/flags/bz.png new file mode 100644 index 0000000000000000000000000000000000000000..59e54255b7e4e0895d8835118e2a5802e858a2ae GIT binary patch literal 516 zcmV+f0{i`mP)2S)>T!Ika(%0$!XaWNth#u%L&Lt6;>$oMD$RVVXP{b;tSo z`_0VWzrl#u%A3Q|-hQr%v8b9Lddse;eK`$;uK)l5(n&-?R4C7_(^V3KKoA5_WOtKg zkpMxHiV=5rcfbE}a)W_N(ckZ@r-$QWPa8O~R6?Ga0Nr4GXRWS_$L&A$? z#g?S9Di8?Lf`H~#yBaL&Y)Ao^VU1eA?2S|@xn>rC8XK3EHE+LORVJbaz&u0P@tjWE z!AMR4&|nDr-nHHFL!c%ijQwY?fidC-0L%*YL-%y~oVzZPGEhBJruOfx*YVp_ED9E$ zR&MU2|L*Z>4nASdYVopcig^+`3Vml8+wf7uu*b-+5PNZ?I(Aq90000ci@bLejp#NZC|A2u1Z*Tu_aQ{$H|B#Rn6$3y3 z004nWL_t(2&#jW%3IibwM30wfqVDDYzqStr+rsKv+Ia|!95NUNy7Gm%()VB~OQ4xZ zwgpfQIAj5g0Pz;oyaVi6XQ0;R&jL`IZ&in&0mMFR9PQq{8%L+lhcw=p{LA_@`*rBy zF3itk@-$*McQoQw>lU$vQm8Jr=l@^`zcl;-YYYhMI)_lL00000NkvXXu0mjfL(OlC literal 0 HcmV?d00001 diff --git a/os3/img/flags/cc.png b/os3/img/flags/cc.png new file mode 100644 index 0000000000000000000000000000000000000000..40caa26daaf8c0f3fa59bd3c34ea7298396c7753 GIT binary patch literal 299 zcmV+`0o4A9P)$bmmqm>gNlNchW)-v;u+1ha@}o;1Z&F~D)mTjvn-;m6)9I80D@%O46z*weV~M>|cp$5> xQ!5VyEe6a}A*azRN7%foHV$#l=8zcqKVSHX1ZUp-;kN()002ovPDHLkV1n($c*Xz# literal 0 HcmV?d00001 diff --git a/os3/img/flags/cd.png b/os3/img/flags/cd.png new file mode 100644 index 0000000000000000000000000000000000000000..0c1dc24d638b95fe30cdf6f5bb39450a41d8d8f7 GIT binary patch literal 316 zcmV-C0mJ@@P)M#HR0JTX(K~y-)jnLZ?gCGn=(WodvP_)|r|J9b@1#}WP&ug-0 z0=i5ZC|b7)~AFVl-^hoUVCE zz-032$qp$C|NsBbV9=y7UE@Q)psQ-ehYfq;v$U;4Q`%c>(+d2a@8>zOpKEzzopr05W`Dd;kCd literal 0 HcmV?d00001 diff --git a/os3/img/flags/cg.png b/os3/img/flags/cg.png new file mode 100644 index 0000000000000000000000000000000000000000..14fcb1f0a006f23350fb49ec1b3dafd312d050b7 GIT binary patch literal 263 zcmeAS@N?(olHy`uVBq!ia0vp^8bB<~!VDz;*ja9-+&?a`?%wNGx6{o{<<&Q!F2<4|zu>>kufmQ4Io+Nv zjv*T7b59-QbubWV|0s3fprg{GFJUWOj+{NnWMS#hkNJUZ$`Ubr7IP-+vLa6EQ_bhT59;UmH04W>PecP#{z`j;@MDi|14m4%Ew_7uDWn#17f>gTe~DWM4fvgI>^ literal 0 HcmV?d00001 diff --git a/os3/img/flags/ci.png b/os3/img/flags/ci.png new file mode 100644 index 0000000000000000000000000000000000000000..9133a6f986e3022c09d131e8bedee8b8c0a836c0 GIT binary patch literal 108 zcmeAS@N?(olHy`uVBq!ia0vp^8bB<~!VDz;*jcwk{jV}Y$FnGH9xvXP)t-s0U>E% zbfMtn?budpKv<3+JbN%og~$*t5H51S$lCJ8&D7cDXKtF*d5-=2{EC^nzsK3Iy3lZf zt59Z`psdII@$>f8+HZlX_1@#}q9iu}007@fL_t(2&$W^3lEWYfh0!2lY@_DZzW6N*-_xeHXmPmF;d4_OWya?bI&Z16NeQ*HnJzR37J}hlMVm? N002ovPDHLkV1oTjubTh> literal 0 HcmV?d00001 diff --git a/os3/img/flags/cl.png b/os3/img/flags/cl.png new file mode 100644 index 0000000000000000000000000000000000000000..cb2dd9fbe9f365ed8de4facc49ce1d43aef3ec45 GIT binary patch literal 193 zcmeAS@N?(olHy`uVBq!ia0vp^8bB<~!VDz;*jCMST6hj z|NpBgeFx6H%r;CEa9!h+yKl|@M+!k38|GX-Ro$$s_T&&y7gI@)U+~|5y)C_$fjk#a z7sn8d^T`QjY>dX@DF+-TJxI%G(l%h#P5X7`SV-lORMuq+rgDoJpH1^UCi2E0Z`p~D qGnyVfRS1yo;*prLp<$v92gCn2S$~Qh@}>jLWAJqKb6Mw<&;$S7dwQ zV)1FE#6=mEN2zkp%Vj%EEKSbqrvg>ldb&7^-i)gfxF<6*H)k=3 tUEge-(B*sV+1YJOI!!)2iy8PC7`}CJ9(;XK$`@!8gQu&X%Q~loCID+uK)wI~ literal 0 HcmV?d00001 diff --git a/os3/img/flags/co.png b/os3/img/flags/co.png new file mode 100644 index 0000000000000000000000000000000000000000..ad63dd07958b6a5cd468ac22ab44687cc1baf49a GIT binary patch literal 124 zcmeAS@N?(olHy`uVBq!ia0vp^8bB<~!VDz;*j*hviVvM@GV WtE5V#Z)^anX7F_Nb6Mw<&;$U9;Ubm* literal 0 HcmV?d00001 diff --git a/os3/img/flags/cr.png b/os3/img/flags/cr.png new file mode 100644 index 0000000000000000000000000000000000000000..4150d8d8bd10b738cc55023d0de7a33b235b68b3 GIT binary patch literal 213 zcmeAS@N?(olHy`uVBq!ia0vp^8bB<;!3-q#NBMsOQuYBpA+F~H)h=$Tez|jPQ&G|t zJ;%ott-pT%`2YVugLeJh=&a92PIhnJHFtUa-lIz&U)`xu;p3Pe?U=UKD|yk<<2T#) zoHPiC+yFEx(9^{+MC1I`%a%e6iX1M11&3qPzx+!#HQ8c(T7Ex^#);;Ot{MsE56?wp zb7<@^iD5`sV|ks!K{Hc~QQ`YFE9QWg@2_)7wMj}n+cHl)@9%;0I_zB{wnsvI1%QS# Nc)I$ztaD0e0sz}4RjL30 literal 0 HcmV?d00001 diff --git a/os3/img/flags/cu.png b/os3/img/flags/cu.png new file mode 100644 index 0000000000000000000000000000000000000000..4b6f177f3eb649588112e01a69b0574f4fe88eaa GIT binary patch literal 258 zcmV+d0sa1oP)?sJR$TQIUZLv|&WKP-i>LEXJx-926{UA`VExmQe;FKMf~ ze0#bGXcS{fkYDhhFK^QVft(6Y7sn8d^KU0bb2ljPwC>HFCBEz5|NByD8#p$GUI>1E z@Z>}1{@G3u(^`HUluocWaInqd-Y~UW*!G#|-_t$EL{F~xHa9qM*Jhr^&O0H@&c}lk z3KYxNHI|sB`mF8In&kc7s)66FqwZ9yR&BnijKO)SMq|l__x~Pq**}x&3u!8x1#|<0 Mr>mdKI;Vst0MH6=_5c6? literal 0 HcmV?d00001 diff --git a/os3/img/flags/cw.png b/os3/img/flags/cw.png new file mode 100644 index 0000000000000000000000000000000000000000..98d11c5f9633a5afd536511b1b5f7f4a854f5dc3 GIT binary patch literal 237 zcmeAS@N?(olHy`uVBq!ia0vp^8bB<~!3-oV&)LR+r~sc3R|c&*d6&Nb|NjeEwA)9e z*v8I#^!$TeRLaj6A`IH~>-JnL?b)RlFfFcf-Jauj|NZ-a>hhCGi%(2ia(wTKlutlI zI7)*2g8u>a1HnHJouoTJv1m^h#}JFtcP~crH5hQP1c)tU%8h+jAFsYu zA^{zFj)xP>xA!@G%{{)#n4c%Bz(sS%R~?RN-8&S8+c})CR-R*Y?0di-|D$wAQzhdz gRKP)pjfOB$xq&?%l zIn=s3T5rnA(e7YhfvK-N)w(*6p4-r;PXGV^Kv==By5@k8)|{+7ZIDi6Z*avs{BQsO z0Ln>3K~yNujnUTaw&Y)Y=u)3c662ba>8Ff(Xdu)lwJGzQ!y|tR3 zumb>`=UnWGhGze{tVdEQb7oa-znlOI^+JJ_bY6J(mrl=J|2Or0l%Eub!r~e-DtDebsmX{r2zO;Mnc!5ZCL%lApi+j5Q1^=-+f* zMCx@{uZCUW>&~A4|NjfR*6lxgXO*1(1yLCh*V>a}3hH59M)4EoDw>_Y^|V0Ow`KmG z_@)&}am~GZCU^o3#p*m=978Nl_nvVVYETeh4ZL8;(-ir> zKBQRE?ZNk7p{qTo2Xsj@J3R|&7MS%;VCFmnyS)h|RnB?59EUH9m*n;z+#1+Cp*EQD zX0JlZJmbiZ#WFf4uZZv3qr~*!zn{Z>zXL~vnEviI=Xo>FU`O?VC2B0sWCfWP?`CG& eEvvMfS5Ku_=8kgMwf#VsFnGH9xvXd!Q}N%s2wjKuA$;<6a!FNq%o9LlpXZ{ztR_qvRH4P&m=PmlE+n& zARyyV2$?rRNG634lJ`&una4s%>Ou%f&sAIKxoQuon}1NP`-LCQx3%@`ss)ZpnMoXr1%4TLR^n=>tFRqKE`A4|NsB{ zW!(>__<{5=mIV0)|C4&mUJK-Cdb&79c2>0rNRUu$YD!SZ$x-TH;5in- iu(T;cSL&c5Kf|(|(DP=D4cmd57(8A5T-G@yGywn?|0oy$ literal 0 HcmV?d00001 diff --git a/os3/img/flags/dm.png b/os3/img/flags/dm.png new file mode 100644 index 0000000000000000000000000000000000000000..8e19bdeaa9822dc8980684f7e6e1fe1edd3243d2 GIT binary patch literal 290 zcmV+-0p0$IP)ko+>~ryS~h;3E8a;d+(;;iGBmGw5J^Ziyh9YbPHl%~TeUK2MjjSy0001eNklrNXOj2Gq<*5@W= z5!UG=?5zkXk5>^y7ihHRh4HL_&Mr`AaRzE*WOjiL7AsKO4Vhh_!Ac4&t%D>NsJ8;7 o+^-@CF8scpzZbrV>-q8uZwf>Tr%w=IR{#J207*qoM6N<$f;;1D#sB~S literal 0 HcmV?d00001 diff --git a/os3/img/flags/do.png b/os3/img/flags/do.png new file mode 100644 index 0000000000000000000000000000000000000000..ec44af7d8f5519ab53360d6fcf4911078bbd9462 GIT binary patch literal 273 zcmV+s0q*{ZP)FQnKr@hu zjC6Zw!;_e3rEQcxM0Txv(Oy@rw7r=*GMk2+y=;Hc<<|0(i_!o900DGTPE-AZ>Q=`9 z003o4L_t(I%k9v)7JwiOMA0CkBbx&5%m4qTebP`_`O2KzWT@vE0O$rD{qSR`8kWYP zF}6G+uyl;EIDRDwgDty#DxkMHZ@vOf_P&-Xn9eoL5{mam7lPA@M`ExJ0F0RThabZi X?%W{I$%S4n00000NkvXXu0mjf{EK{e literal 0 HcmV?d00001 diff --git a/os3/img/flags/dz.png b/os3/img/flags/dz.png new file mode 100644 index 0000000000000000000000000000000000000000..6f031c937bbc8fe8e25f2250976d9ec002e0f64e GIT binary patch literal 274 zcmeAS@N?(olHy`uVBq!ia0vp^8bB<~!3-oV&)LQRDc1m>5ZC|z|9`)9!_?O})6)3M z;WO7%U6n(O7?O;E;+F(WK5p5+U)=cD!>1muMlE{Aw;kgqY8pMxY?!BF{O7}`XBE9) zj+}iERb;S)|3A<`#*!ev;J?kU!j1zu)t)YnAr_~fPPP_mP~dU-$&w=K>bmEDpsP-) zLhICJo}blDzVmbW9`G*CsYW(L)5u{rlRmQeoO0qqV~Lb94cOGb5NO*gf}6T7-tF-3=TYFnU!TE@LSQ;|ZPs8OY@HCbtyns+ZzU2mb2 zh=I-Hx2cz{Y-2@zbY`t&v4DMfla6MxK*HYU@Avt#)7>RjB%lBQ00DGTPE-CG;|F~J z0078IL_t(I%iYbz4#Gebh2f!tj8vgGRe~0GcX!_ZWmuOc9oGEIlP~vzF#4%5_;1+E zA{1l8bV5uFJ1tSDGay8ouow&N2J0g1LrqL06odXSo}pQ}EH5X$Oxzd3oUyJ{%K4R% zgZ{7ejr@GQk0De4e$9nkQF6LF#5oY^-Z>D!uDHVOTFpPs1zopr0J#Vpi2wiq literal 0 HcmV?d00001 diff --git a/os3/img/flags/eg.png b/os3/img/flags/eg.png new file mode 100644 index 0000000000000000000000000000000000000000..812a6a69061969abb88c55e2e55f0856d23f5762 GIT binary patch literal 219 zcmeAS@N?(olHy`uVBq!ia0vp^8bB<~!3-oV&)LR+r~sc3S0H_TcfzOVr(RrJ`tR?r zAD^GSf3)YEpxXcc|Ns8|`|wQr*Ed)1pQw3#ckS6#X2%!EJiof&oS^FAnVb^mbLIdw z$9lRrhFF|_d%=>g!GOo*VufmT!*BU%%n~P;Ej*>acDd%9zJ$!f>pxVrs?H91vo_*{ z&Y?HXZEBYqYAqTWrg3v0wtY3L@o1cAHUELGRT0brQ!`BD(=JX9cvD^W{~Lp`g2EI&BtE^8f$;3{|#YE?v2ilsQjT?d6gce?EM8yJh=)UeRiEHQr(?)ikpzE&*Of zMpb53uOI_2Hm>tFu0I|Hh>WJI+a`x^Vm`s+8Q~XUZ*V{?<$V;Ae@YN0X0f3R~O#$I65d zHg(k%Oe~lz>>M;zc*uZPpFxS;60Ixzs3>D@FPTF#&l)xGIx+f^Df3k>?kO|(ek=86 zF7|OP|Gy^w{1R>e0068>L_t(2&yCRA0>U5&24E^WPywR8|Fed!m89!+KvIoPQEevf&cs3XU@Cd&dk}*-le&uN=C1nAO3A@**i%bzz$?Hn@&J*-~5<$=m zznO8JL<3OLaw5P=3a$gorM-DYT7}2MTR*$QSLr3;M$jzpOs%oCnTn4b!jy(u@n}rxwLl6ylN$utpuMSryiK54B@I zxn~R0q94C{T+yXT%#$3`ltr;E63mGgv8Y*uJ2GrjU%6Wp;*GSENgUL(o?dBN&7^Q@ zhmyGf0004WQchF;8si6j0001&Nkl!DQ% zLy?-nT61%?5%9)h)7bmPxq;;{%-OL&KS$Ow1w&u&IPZE%wGYD}TXR0jF31uXu9k~> md)oV-iKr0j8ht%~`CM-+NhRK`Crds600000005pzL_t(2&%Mvf4#FT1 zM&UDqfLDzQU*tPB)e(3k`0kKvW>IeHKRsaA1 M07*qoM6N<$g057AC;$Ke literal 0 HcmV?d00001 diff --git a/os3/img/flags/fi.png b/os3/img/flags/fi.png new file mode 100644 index 0000000000000000000000000000000000000000..c652fc0f036df3c35699809d027ae69c7b9ce5e1 GIT binary patch literal 138 zcmeAS@N?(olHy`uVBq!ia0vp^8bB<;!VDyB84z@-iaSr7;YJ{XOD}4X} literal 0 HcmV?d00001 diff --git a/os3/img/flags/fj.png b/os3/img/flags/fj.png new file mode 100644 index 0000000000000000000000000000000000000000..b5455a6354b72c0aa9fc2c33671586d8a9a6e320 GIT binary patch literal 477 zcmV<30V4j1P)F?$lBoK?c|7+XKtFm$l3e)`)04>)=Od8-RHnU z7yR+__SM?RMOtjG-EW}Ho4C$qZJNYc57>l!)OrZ;@bIzS#o~;b$%ux_k#yO-0L|mp z&n`&Qh>-Q(`1j2A|{`X7Lu{TOBewpGj*(l{79VlN@v0=kHXpc2YT3$vzx z*(EJ>D5ZQD1}?^Ky7IK7T8K4yH@uvRXMoW655C{5Poa7`4R?c3JRO8XE@}z4^?v`@ z+%I7^OulH@s3eAYpK59t*ywfbORu|AP_pcMB$e=dYbVL|)K*Zk&xZ>t;ov?X~+MaX7GJp%+|z9wWo83AL;RfMW#@JlKc@K1RG3+)iz TcXmbc00000NkvXXu0mjf7$)r& literal 0 HcmV?d00001 diff --git a/os3/img/flags/fk.png b/os3/img/flags/fk.png new file mode 100644 index 0000000000000000000000000000000000000000..e4637d7a8c2f38efabc756bf2736c7bb05e08f19 GIT binary patch literal 560 zcmV-00?+-4P)E% zbfMIFjw5ur*j8)u#?5DLn#d3?z{uJTEO8}3eIPu0`uO?3$JrBn$Kd4cVsM_{f8+NEt%w~l$Tw!qok=gzyfgR#{gb-RI&oVvZg965QggKjfSgs_&1 z2`Fy9&Dh%7+Ml+_qJea4q0hY4>Q{iUAc4u6oq_e<<92O=F?_v`Xkd|+tT24Np?(Xi+($xTAj~{7pjIPJPnS*nK zldPwivax;1h+=4<$FG}asI7ywfn&+TsA7?*0g_2Y0002{Nklv*KUI_6oBDF zfEZJgC{Q+n?45M)y_(aCv(w@+2j9SldKvs1l<7B69b zzzFgDV42`7pzaRYJG#6RC{)*<%-=s2pYusy0bw@yd3(-_naaV<`sLM)@++0I(`)x@mB*p2fN>h# yO{bTg;{yde?DTr$i!hA#l;5Uyx63c^-~9r+0~T%{*|FIG0000YG z>fUg_o|m6~UU>AGF?Da9OWB!+pSE3o`?ufxH&DHkr;B5V#`)v~hDH&^r4yLgObkL; zdmR%v0}3`9${1|f(ZCRJftAljg5_Ysq)RT2EC~UJm^=iIWT}PS-ngsyhATS*d%KPA Uin7xJK+_mJUHx3vIVCg!0AzJMi~s-t literal 0 HcmV?d00001 diff --git a/os3/img/flags/fr.png b/os3/img/flags/fr.png new file mode 100644 index 0000000000000000000000000000000000000000..fd06f8489e5a6d3c434be5eca546509dbbcd3157 GIT binary patch literal 108 zcmeAS@N?(olHy`uVBq!ia0vp^8bB<~!VDz;*jFVdQ&MBb@0BQjnF#rGn literal 0 HcmV?d00001 diff --git a/os3/img/flags/gb-eng.png b/os3/img/flags/gb-eng.png new file mode 100644 index 0000000000000000000000000000000000000000..40df912a827cbda81f1c6755e9bb0df762a42b10 GIT binary patch literal 128 zcmeAS@N?(olHy`uVBq!ia0vp^8bB<;!VDyzHZTB_0Py{t#@7u1P4)j(2Kmu7jf29pA++Zpd!MaK<~=K=?PSFa1B<0Ih^!WUCtlY5B>!`};t_Aw>+`#x?f?J)r%6OX zR2b7^5EM);U_b)wVS<7Tf`Wp|$bv>d0ft5(u>>LJ2jogH_yS3>aF|$WB9MCpqF%@V zBF3x;HI01+kRjy&5@T5iWOjn>W!VU1wiZAv7L;~C)r4#i0|O_>2}is@7Eebw#9I&w zwxBy1-5U%H(?DXGsA3F(AVF1BF>KngSp;#C4~mnpIUbug!2u$J79iMy&Kp}GqeTZe fRIo=JwkQSwAi62Q4!PLE00000NkvXXu0mjfXe4jN literal 0 HcmV?d00001 diff --git a/os3/img/flags/gb-wls.png b/os3/img/flags/gb-wls.png new file mode 100644 index 0000000000000000000000000000000000000000..da8e6842d9d7c79ad37865b25d51c87fb0232fec GIT binary patch literal 910 zcmV;919AL`P)(CA|7LPO14>M^>F74IWtp_K`8#@Y_G=w)Uycjg710>+4u_T2uFncrR&e6j_S+f)@ zohB~l!^qZrjj0SM&|r0|9x&OVtnK0BB7ZM@Ehkf6Fj-0|bU!UEb}(x=Chp46I&d(} zH&dhpB*ZjNv?4viCPUC{e)IG5UQ;jTxWG9yg zG2^ki&r@m4P-pP)@RTJi$bE~~h?n59x}z5<(t3%(S8Ldop}%5tzaKo!MPACAqq|66 z+`7QPO=9HO+PpGMk~m7KHcXxXAg)AL-_zCek;Z=j00JgSL_t(2&z+EGTjD?v$G2s( z!={js1VV30lcs=xp4jlQ_jdih|EIV_9~E-Gy8oM<@|)S2L25+tPpnXA88ef~H|%*U ze761tP5XNFx*S`1_3VSPshXwpZ%d%9tI273@`GWc`P#0T)ecL|ui3E7PQHWs$;IR4 zP2*hCjNwbFE;URn?5>8%<1ZgyZ;TJh<)6P&g88Mu8-iqjsethx)vSvJ&|W#!OXKFa zrkyn78@@#dVT8bDOV#U=eSjtVE(opUV=33)h_4E45kWQAx7B36IXSp^2QP2#?}8mS zjhuGdicfLaj|uu<<}vdpdMo}0nwuoAs(Cre;giCR#V3=rx3Szl}x`}KDEof&x5ViG&NC7i2qZWo5O-P!cnWOn4hZKa4C?gu+Tq;$A zRLqc-sssS(ErX#vS4hakKuTs1tQGlD<_s*z6r$ifJJ>UXtYnI76hvZst|$sGSlf;g z0hK-ELC@<4@j)eKiHa9k+}Msb45Orn(tLlQAl^1AjGDOk#kLv_M#nOJSAZc!~AW*r2S(;dzSj+u`lc z*h*lO^7Oi90001~NklyK^B7`3$g2>&HyhDkK{WZlN6;{R(-{56k}%oYt#1z&tApm+_&JiZhvLf})7zce@Wt~CfY8;WQGkT;uqp~m7Lm}d2 z9p!Kxg{U`%OE|NhD&58&<;fiE%NoC0D4{bj(HtcAz7y?|8S9G~N0UhMrx|;GI=#9l z+lB$W0002TNkl>XRtQ8TOaHeUuXI&b-);18)E5j`d zOYgn%1!tNVG)%nDTtPKKngJ{FykL+~HPnzkMooJzCGQPpz!4x-(_sRb2DCy7d8Q!& zAO{eWJJnkkQWghW?A+G;{fPvxv{{8>$?f)M* z@c;AY|Mm6%uV4TF?Aias#Q$^W{@<|SzrOzehKBzR4lCycv;#Fx_H=O!(Kw%+(7=@D z=*Y}g(x7+ZU6VkGW&@W|ry`56s6x^jebx1cS+dp7aW`~uad91F5oKjnOp@8X#3Z4~ zmpk12f!XB7siKM!0u7hv>IFPd)~H}P#unAfnW40Th1II5sXZZQ&cn{D?l=CN+vt6Z zS?vZBziQ6IlUbTG`Gj?5TS#VUaBusN67I-hCHkPdoX3%&T0(P2--+Y}K<6=dy85}S Ib4q9e0Q#|HegFUf literal 0 HcmV?d00001 diff --git a/os3/img/flags/gf.png b/os3/img/flags/gf.png new file mode 100644 index 0000000000000000000000000000000000000000..57dee2546e45dd1b08b20cfa9baf489c92b5a4cd GIT binary patch literal 359 zcmV-t0hs=YP)cTfa<3j0jV3-;RyHJK;(+X+UV zB+@Gx{M`u$i7?s{8u6SC9ege6W)WjlCbB0Ui#{Uy&&DdG+vC&pcHGS@0001%Nkl*`5>OD-?B5K|-v5p+zz9}$)%r!2Ya1`kQ65AH`$B|L)B#d-g_M~HAuUA6DH9>2 zwFv3X-g`d`QMg2>Nq}^{pJxHyMl&kupQe3?Do@oV^)U1|Z|Z5-{`$6*K%f=lY? z^}1svLR=z3*i|CjM*qW2GzM|G2!BxlE>Q(S+lY`d{{p}sRFo{+uo3_O002ovPDHLk FV1k((mG1xm literal 0 HcmV?d00001 diff --git a/os3/img/flags/gg.png b/os3/img/flags/gg.png new file mode 100644 index 0000000000000000000000000000000000000000..fd6df212a7b30ab689512e000ce628d899372834 GIT binary patch literal 206 zcmeAS@N?(olHy`uVBq!ia0vp^8bB<~!VDz;*jH<`}3~Y&%>wQ z`6PZQRs6b2`n9>nkBee&eN^7qhJ2YTd*8dR7^ptg)5S4F<9u>L1Cz6(VLQ8eLc>EfA7X;M}%! z=~C5|jGa6x2c2@#xDK%>Hq_M}m;ZEolV(?viX+402KmS855M&QEoAU?^>bP0l+XkK DLE}ju literal 0 HcmV?d00001 diff --git a/os3/img/flags/gh.png b/os3/img/flags/gh.png new file mode 100644 index 0000000000000000000000000000000000000000..bfee0413f3516f67c380c6707209ec0046adcc15 GIT binary patch literal 196 zcmeAS@N?(olHy`uVBq!ia0vp^8bB<~!VDz;*jj>5OM?7@|2Dr0I}YS{d%8G=Xq-u}0TNnG4C$JI2AwbNSp$t_@O1TaS?83{1OS_?JPZH; literal 0 HcmV?d00001 diff --git a/os3/img/flags/gi.png b/os3/img/flags/gi.png new file mode 100644 index 0000000000000000000000000000000000000000..bea8dfa38ba57fe179e3215e2cafa6cf62d69c0b GIT binary patch literal 446 zcmV;v0YUzWP)1J)z01E&B0Gj~?&=?8w^78cb^tb>D)kaRZ8zWT! z0m3*5#e9Gp1OTEF8Lt2akpTyz00hpOoxX~VyC(*{M+U-jciY9r)4IF4CM?Bna?q=- zadQ2>AtLI9Krmz7&|KBH}!O-QFju@Dhzor&rSU*a%~F1VV2wS9`a zE;`w@e8seA-e22xUQLn8mQ<45vv!uXv%EZ!UZZ11Fk_f7tV_F>j1U$Cj1abzL+lEl z^H1PnIn)V12+qZU6KOR>hT(h1qvsn2g8kW|7fPvV?)RoiDeX^yI*7J;YQxtNsJd%) o9(=6kLG3;hu5WXH++0WC4;HW#a@4iAHUIzs07*qoM6N<$f@3eg1^@s6 literal 0 HcmV?d00001 diff --git a/os3/img/flags/gl.png b/os3/img/flags/gl.png new file mode 100644 index 0000000000000000000000000000000000000000..40d30063a83c2db928a93e6781540c2244a337ca GIT binary patch literal 251 zcmeAS@N?(olHy`uVBq!ia0vp^8bB<~!VDz;*j!bKr+Es>!`qE zGZ7IJC*kBw5tgQ~5XbZhGZqvubUkp{u;2k>ieXvDj0Gl}6!=2|BqApWYH7S^P?6ve z_G#$UNMz)3U}ZC4WnXCJ(8Ofm=&a&s7{S1y#OdY4x^n_6E4M;Zr$&?7gaktuK@m1> wUojvxh3VH(PXXV6fE}5e6xtND76b?|n7PY(Ui)}78t60zPgg&ebxsLQ00Z4si~s-t literal 0 HcmV?d00001 diff --git a/os3/img/flags/gm.png b/os3/img/flags/gm.png new file mode 100644 index 0000000000000000000000000000000000000000..4312aac93c7b64d5d1ae419af3f763e36d853a3b GIT binary patch literal 140 zcmeAS@N?(olHy`uVBq!ia0vp^8bB<~!VDz;*jxc)I$ztaD0e0svOLDBJ)5 literal 0 HcmV?d00001 diff --git a/os3/img/flags/gn.png b/os3/img/flags/gn.png new file mode 100644 index 0000000000000000000000000000000000000000..8b42a9e3b12c2067342d9e25ee085414d3014ed7 GIT binary patch literal 135 zcmeAS@N?(olHy`uVBq!ia0vp^8bB<~!VDz;*jj{kbT{FePEd z9=*5yQg0?mGd*7f(!p2~S->P)@?hgmi@y=YxU9i`x2Aq zQ-)!{OGj^x5cvKWBm;bC0X>tLrVIe=o^)MyZ<9m|0uo4=W^sWyj;Iul{6XIl2_X<6 zaZ4BBpwWRK$z0^u>zkty){cgP5RvCMyn+;Gz7CYr3vtU7N27RPIuHu)az1T9+Gt0= zye!5ocxrxuteL%8)cU_b@K4=lnLI~@>1jb=ooabLOJ4~>L<6!RSp8|Ui%F0?rmiC- zKE|^*88A3LQSIm_*^Y6S`Qz;2k$68q`OcD{34%cX%yweHzW#yn4=DR9kROs1X8-^I M07*qoM6N<$g7XsUi2wiq literal 0 HcmV?d00001 diff --git a/os3/img/flags/gq.png b/os3/img/flags/gq.png new file mode 100644 index 0000000000000000000000000000000000000000..87a86bba6e66416e7c76dc96caa42461ea3112b5 GIT binary patch literal 382 zcmV-^0fGLBP)^Yioi_1gCE%o>At+#?+jd(kqE z*yHf}G?X5^+VYz^QGdARJD3LM=H}A9t!BcB=-A0Zu4!CRgkw*B3VEvyd!oE9HZWnQ z#wsr-WUoV)+pn*$$jHck%bUIA%h~MUSM;K*00001bW%=J{u<*4eEFc1V_D7(cihlq+C;(h!6?`H_mn3&4U&&ii|vR*X9;`u8O(xC()%~41Z3Za=D zq+JwNmFB-kTzq(+&tgxhO{2xt8JTqFdw1b1{!#g z;||6=xsesuHLKVKbK5)urGmTb>+d`2s?yFo7BZC{?gfMA41@kfFjxo%cY?udhC%-% c81yhFA1aU|KZrnmod5s;07*qoM6N<$g6Iyi6951J literal 0 HcmV?d00001 diff --git a/os3/img/flags/gr.png b/os3/img/flags/gr.png new file mode 100644 index 0000000000000000000000000000000000000000..0fa7e5e5dae492e63d66b154139fd57b8c05417e GIT binary patch literal 123 zcmeAS@N?(olHy`uVBq!ia0vp^8bB<~#0(^h>s0>&DV_kI5LceK_5c6>_i8_U`1)&^ z|2LNc#kD+L977~7CnqG7CNwa{_?!zcIB<~Vk;ICiH$F{j0wvBGG7l1jSVU$sFzjMg VPk9p{y$z^~!PC{xWt~$(699mGCYk^M literal 0 HcmV?d00001 diff --git a/os3/img/flags/gs.png b/os3/img/flags/gs.png new file mode 100644 index 0000000000000000000000000000000000000000..c1e58c232558f61a36dd2d67a4038058c739a167 GIT binary patch literal 663 zcmV;I0%-k-P)E% zbfMIGjQ}2I*;;P#!^&rEnaB_>z{uJFAZY&o|4>z04Jc;dpTwP&-6cQaZU0zo^Djr5#K|To}UjPXoH8DT1xW@(%F>QaH zR%C7rFGgo*Y$hioS1ut33KRzsrCB~Bevz|jeUNXCuJ`!+{P6PF-RSJT$kxx?4k%zY zLRok=As8i1COvwFHx~&dXemN;4=r&-Dlen0!a^}Ct%eywKRX;RlPxMUr-L3bJ|KHx zR>zhRN>^zI2L}KOykBmM7#p4|HKzs+hB-TUR9J{7H(FOxSvg>MKUIsxsG4S2M|P9| zVT+kgc!XSQkWz4=V~GF&VLp(fGI)$ng`tIff05_!>rZYkQiU9&xH|v<00AROL_t(2 z&y~{Wa)Lk<1>i+MxPXFmTh!QlOEtyx-h1!w{|sSOBAGbs6IXkUv0Jy`c2+IV5_%KumuSTCi zKC0Lk2PfMxFLSTQL%}{j6ip>r%nzYkxEGG;py=6qxzDe9t(ZdoSrWtbr`yKEehPdY zofFOM>q|{3m;GT+6mJTRx5umZY5&P!u2-w~cOTa~BskySOy{33mBIqyFmzPg+BJ-| x#RLJOxE2S{HDiVZ!%^oiqvw){82EYoC*S;#9w4A5IVk`D002ovPDHLkV1i0>50n4^ literal 0 HcmV?d00001 diff --git a/os3/img/flags/gt.png b/os3/img/flags/gt.png new file mode 100644 index 0000000000000000000000000000000000000000..b7450fca34cb3afec07daf08e8ce2fb82cfa96b6 GIT binary patch literal 267 zcmV+m0rdWfP)X0{{R3bOxMP00018P)t-s|NsBs z=h>RBgyYw#y2+&U_w)4h@ky7^!rbra@8<5|ld8L#*Wb;Lh-j6febd;=n4wt0&$s#d z`pn$KqPCHkn{;=RU4fiv%Dk-4-Ms$$>ha~B^y#1Q R0LuUX002ovPDHLkV1ks;hxPyf literal 0 HcmV?d00001 diff --git a/os3/img/flags/gu.png b/os3/img/flags/gu.png new file mode 100644 index 0000000000000000000000000000000000000000..05693540b4c2036006d7c786c92d20440d0b6272 GIT binary patch literal 322 zcmV-I0lof-P)gJy04aOMAvusdVUSL0Y)pQLKx$-bop3vBK%v1js9tEj ztcBF)ugBtnTX~v>r@Wa+(8Os8huD9gn6kNhm$tNtf~bhGvsapDlBAYRpVq&Jm%E|4 zYOL6m#>uqg{Va5z%K}$G00008bW%=J{r~^}|NsB}U#c{P0001SNkl74Q4pTbiYJ6+Mf?jw8~_W(S_T}bT%Kz~Lzs%i=^99= zB+dn*B4LWXFX=l}o!07*qoM6N<$f;$^ zrB(j%wphT3B&=g}%ACRH)BMpE&JiXF2mz$rzU}z&K(}`q0tAi=8Z`t7{QdR)-dFwO zasKR{{M9Sj@Z^lst^fAU{^*DN*+9LV5ZtyDwGJU?2ou2%Bmes49~uH58UlW43w~$| zPeKO${O(Rc2EC|GLrX_`xR-!>Vt7KKx&QzGut`KgR4C7llUEYLFboAlD3T!%dWVD( z+WilRvCTwIk+t7%W}j>+dcBiKM5;ccVYd?w;$(bz#>JrD3kC6fQuo7bk*(GVA4EK+ z>I$&iZnzR?(EdFDEa&mGt%MC)Km2Gbfq$&u&{zUXoZnC{L4w5ZN))67dJuoL1XiGa zuS7kVOM+T`6R7nt4{w;Pc^GPCo`+bu2QRJs7dmTbDXcZPX2vR5T|NsB(hk5ze)8tV-@u8RELoMiQS@W)?;5HxQQ9VDj z{~-VX00DGTPE-CG;|F~J005LpL_t(I%iYt<4FfR+8IV6uULP1dZc0`oJh1YIIL z0X;SuF*SK*5AnR6I#;w0>00ln_1qC@xv?JLFwm8>+5-%vl2c9WVOC(3Y3Lq~o>DHQ iG>gM~jKBGruizV}>l}8H1z$w~0000|Y5>Dtl z-As#``DLj>#xzyJ$buGfL>yZv|dkP?tNW{--x=TL+L#CePgf O0000F;%HSB!U7vQ+YPbz?cp*2dNTW)?pInXc$Pgg&e IbxsLQ079NPp#T5? literal 0 HcmV?d00001 diff --git a/os3/img/flags/hr.png b/os3/img/flags/hr.png new file mode 100644 index 0000000000000000000000000000000000000000..1dc15e24fdd1d0a0b0d5a30aabb2d636196487b0 GIT binary patch literal 344 zcmV-e0jK_nP)wC@-lkCC#lZie7b zS@prkB^8WCiiD@2x&IIl--(liJbX}Oy<-b0cqj3{;1Bm0n7stRhYO0001wNkls#|{G_3`J1`!H^70 z?S>(#Arz08k%HS8+G%H||ow~k%&sh?H>9KoO*K=H@0hnLRBaZFs zo>RCX)$PvJ)^rCN-T)B}2%!WM%$*2A8EGkXB!h6oJ-Qo%aNv@=8@_+NB^fqxJ~2ag qP&8X07IrZ&p;Be(8GX>(AK?dzuoR!l-(}MP0000F570h~}kP$3H& z8?QJw8w(K%`FNR#QYgeM$j2;!sgNBFF!C^qi3;;DqKjiHWRen*6ksB(kc*!c1h@z) js0>&DV_kI5ZC`ez`*dozWzTWgTe~DWM4fF{B(~ literal 0 HcmV?d00001 diff --git a/os3/img/flags/ie.png b/os3/img/flags/ie.png new file mode 100644 index 0000000000000000000000000000000000000000..a90f8f1d9eb03d02fd2d05de30c4dc8a5a67bf21 GIT binary patch literal 107 zcmeAS@N?(olHy`uVBq!ia0vp^8bB<^P)|0000pP)t-s|NsBO z&+bcd&5owvT!7L&XUX^Z{Qx+)x5n$g&F$Xh_I8umAyUB)L%gQB<}+W$LTt;;{Gek1 z003=CL_t(I%hi)h4uBvGL`!Kwe!~4vtGL%rB8l-Wx|&S?M9bMgD1v+`Odzi&CW7gb zs+}l!k;dFae0WxBk+JVXiW-)=#%UaDwvu zecQ*W#W+dFMN_(aambN%%AcglpO?#08pwZ$y>KeVP+-rWioHuM(T0xAEkV`HlHJma z)xW~a93|3`nc}y*)2grY=jhJ4i^QjJ!ku63?d?0_i)sJ>0C`D7K~xwS-N8o!f-n?7 z!3PBX553qM_U`?!rx%c9<-!^Mm-1nEfQT?)oT}Y+ouI4Pbi{WuFT-OSRj^(PL(*O( zc{L(e!yr9D_IR2i%yF@|;2s5s1P8rDWo^tR#-ZM~3aU6&;aTyLH2DHAMUdeop5VvE a{K6ajrUhks3j+!O0000vVD=zopr0OS`yPyhe` literal 0 HcmV?d00001 diff --git a/os3/img/flags/io.png b/os3/img/flags/io.png new file mode 100644 index 0000000000000000000000000000000000000000..940936534df717151abf74789445d4c4222a15b4 GIT binary patch literal 838 zcmV-M1G)T(P)$DXOrC^3>09%fD0}!?|gZ^m+QUcN_y|@|NoOn_Pxo6MB^C& zyBcY=hIWf7jS+-lKzVzs-2#kESVP&00_q@us132Xbwsy=HIcGKg4xGKk-!Ed;-<*q zXIWOd>39F5%8BeC;G9>t{Q-pGMSpNt;guVnGZR$64v%+d-wdQ#&Jh1M3I;{wrq5rD z-%k$aUIk#pe~BbYiv68lZ#4Zf+o!9B0HeAa^e@5?2L0QL?pZ8zI653oM)%`a08Er+ zj$#yRwYRoOOnrKyH3KkezV~cA9M1M0bvQ03g@(3S&jVG;$jSNS<<97!_jIYflo}e^ z--Q!ZIb9k!`q;VZ9mDQpi~A|Tt!if!IjrD^!iQIz#b$wnZ(yJWjPfImI`tvH~j0_yANk44eU@)z<}`-Gg`nI?h63T zF+mFFr>Eyt4RZtZ>!_|_9cmE($HBc#O@S@t;_YJ5z$OMAi~2WnLQo5x2tgC*QpE0Z zwYq$OH87H7orHXOSOP& z_d1e^PNb{P=mMV68Jza_6+XEdhMZbR=dqU&iI&)lDX$fd!pr4S)G!MA13kknJk#H3 Qc>n+a07*qoM6N<$f*_5W?f?J) literal 0 HcmV?d00001 diff --git a/os3/img/flags/iq.png b/os3/img/flags/iq.png new file mode 100644 index 0000000000000000000000000000000000000000..32581bce94e943235fa4f35719a4bff8c0d47680 GIT binary patch literal 232 zcmeAS@N?(olHy`uVBq!ia0vp^8bB<~!3-oV&)LQRDdPa25Z7}8s!K2Idimwu{(I;D z|NUFPXKvxv$w0~f|Noy8R6GCdmT0T5!|Yu9SveDqu8LpX8nC!BNas)*Pz7U2kYDiM z=2v0Ift+Md7sn8b({C?0@--;%xCDC5v|?iVZogJLob5=9YW?3!KCkXrPA!&R*Vq`` zs`q971)J8CkdMw86Io7HB$foOUBS2{fhBS0(>qyHX9PJ(IxsT`eq|7e{jr9>{@&-N d<0(Lm<`p*-nwoSbegj&^;OXk;vd$@?2>@2!UGM+^ literal 0 HcmV?d00001 diff --git a/os3/img/flags/ir.png b/os3/img/flags/ir.png new file mode 100644 index 0000000000000000000000000000000000000000..bf8d9681b5f5e37dc7fdd1862800e47347585e9b GIT binary patch literal 309 zcmV-50m}Y~P)4gJ?00wn|NrDlN?o{fsMxe$x^yI;L0GkJ<3B&{jEtk! zuk^66n9-*C+1d8Lzw@Z5L#|No&EK&!Jz?UKx!uv;Xsd_`K|Rl(hnCnE3M z4d1|B;Tni`IA#@_Aed1X%z|LT#3kA>1ksMhoafWk zlSb62R|ej|3huQV6%&lZU;~%66+AGi_@^7)9|1lh!K;ElgxeWLCs%OH00000NkvXX Hu0mjfE5nc{ literal 0 HcmV?d00001 diff --git a/os3/img/flags/is.png b/os3/img/flags/is.png new file mode 100644 index 0000000000000000000000000000000000000000..dc6d5db0fc755783e586969afc3fb33a4a921f3c GIT binary patch literal 165 zcmeAS@N?(olHy`uVBq!ia0vp^8bB<|!VDy}(v2N}lyrbkh$~~@od5s-AGq@Dj+`k| z(423_&NQw(`{Mn#{gM?sQkYnNL;uxZFKDmIAad|?rp}0js zf<#@Dm%;+M9G!J6>{2{U2?9Af9V`+(8ySRHHqPoiTBh1IQScMP;p?sW+m`x%( z{~O#978we*JwFr};B}5O!1-n;z|(&3&&6)TIjlJ6Sxr_|rpxA$)g3@&Fjrun&<$m^xQe3@~ssf>Ks!tv$# z(Lbf5F8ovtKf%SiZ*DitP9z@HiIs*ghU<4L47Fqskl|P9X&6pv6`(rlaFH+;qdMgy zztoQ${+7NBsqjfj_OFc^-L{|5zsU$=VHHcp+A|%A*kcU`@|y#OyI(i~LS|Abyp$s4 zdu2vddu8@cB`8<8Z><0!KDNSZbH(^T+a+f3*sedF+Yj>oj=~TC0000UxH_nJ=aR6*qdiIEF}E&b@e$w?To2 z^+J8aYR~WjuAPQW(K9R>#QXam@U45m(f7bAXkpey+kI0${A9?>yZT1(1>;io2N%C) znZNzXdc>-eH-}|c>2ImWtCP1$Yo??|dm6k>W{tcoBD`dR((WX#*<3eYJ-+5T-=jOh p;867WEnId z9UL6yYwDa6RQvz`KSP!6E$`@AJTe#Mjegv^JDW%5ik{=M_DLxfQ7I;tlipl71=O7E z>EaloalZG0BVU68kIO}+Qyon!zT2;HlWOLE^XIGg>@YL^m;KZ9gU#ylE8+&e+Uu^rlUw`y-RZ XEg^wZkA?mLtz+bP0l+XkKH{?@r literal 0 HcmV?d00001 diff --git a/os3/img/flags/jp.png b/os3/img/flags/jp.png new file mode 100644 index 0000000000000000000000000000000000000000..d2517a39e9c98d136d0b65bd9c37d5323cf3fc6a GIT binary patch literal 202 zcmeAS@N?(olHy`uVBq!ia0vp^8bB<~!VDz;*jfL~b|omg1TeW82qtUI3=9ke1OyzM7;fCXu_GX0F$2Se3l|z1 z8WP$QCQO(h5y|ju!Gctdo}EBLXL`CghFF}wI`K4Lvx0!@`XtT6&PTWZ``^B8^NXfH z=I%Tj&c-`)?(i_!^sTd=ziwvr0j&k|ji;Vwdp+mqwv;3L0zSu}2&suJS9tVFWtGFZz<@}>X?xV8Rpi(;|E%9waqM=q`2NSDbe=t> zG`%q1bhPtDnm{r-UW|#4>j^ literal 0 HcmV?d00001 diff --git a/os3/img/flags/kg.png b/os3/img/flags/kg.png new file mode 100644 index 0000000000000000000000000000000000000000..44e90c274849d07b64e53f5b60440243483e15bd GIT binary patch literal 247 zcmV|8M~RKmh;10RNBx|L_3+FaZBx0RP|s|9}Ah7y$p!0RR60$TV=V0001j zNkl z&d%BZA>eq4-~c7x043Da)NvPLaTj6(E}`P$;@{uj$HTx8R-fkl}25|UwC0X{gutw<5C#=DR{q@8%x9?#vFIaH`ulpU?~LV0forou(K#K z0u*Sw2uzGsC5`|imdct)W8vuOysors$Q6;s$wN*6K%~J>k;c_SvsF9W8u05m#5&g@ zIIUd>Ixp}P&M8rvzQ=q9%^(SsltfZ0NabKoVAk`+a?a~|A9mq=_6aW^#b5r+3oQ^I U=`E0*$p8QV07*qoM6N<$f+R4n#Q*>R literal 0 HcmV?d00001 diff --git a/os3/img/flags/ki.png b/os3/img/flags/ki.png new file mode 100644 index 0000000000000000000000000000000000000000..d27557a66f2ef8c7c9de1a7ab69e61a3b18642fd GIT binary patch literal 493 zcmV|HjPRzGrddzr)B75Z3H4i5Ox z0Dgc?n3C1r8 z-;w~}jR4fxY`Tzg`3UpFVQ~dw`|K#%j@aExzvChHgTT5)=&%q)EGP&m j0%k_&7wO3W{j>f6ubmw{;+PEg00000NkvXXu0mjf;Z5k# literal 0 HcmV?d00001 diff --git a/os3/img/flags/km.png b/os3/img/flags/km.png new file mode 100644 index 0000000000000000000000000000000000000000..16a8b9fc6253f6919afffb50b76b03c779c5875a GIT binary patch literal 341 zcmV-b0jmCqP)e*nTSZZf3V{9(a{n}6f006B? zL_t(2&yCUB4uc>NMbSY9%786ed%yqxSC)d9G!dBd#KVeP5pL%VYXG|o69#%`LI6fT zK}H|)TK%LtZ53Eal|}Y^j9Q0-uR}}pf(W7CSD=z4mNx}0i%=6OfYHxVi4qhff6;z< z=z2dxr4~3iTv<+w{GP*vxDNk6=IyWq)Ae{?u4Wi7yz9bYLaf617k(TzVc7O+s>|$X n373IAX`_L8t|NjaI0L~F60G>(w(H6Ry57Z_j&=@5X4*^0r1rQJr$fpte`}>P? z4Arp`SxN`*#uVJP6CD@=2%tytp&9PZ7#5>M`^y&DIU@D582G^!-A*8-i4K>Smqo2Q zYgh?+w=XfMKjmy4I5;>UARsIx19o8w?d|QQrKQ};93!Pc#l|6=y(#L48-QvHxx*vp zdmLA=H_^x)etv$Id=0v~x>#3JjEs!9xVYrxKcEgv5rO-L`Pz$Scr ze8|Yi?UEXBaB#D;vs2-BX8-^JOi4sRR5;6p(r0&qFc^m6S4>Q!I8Z=Z?GQ(uU3O@# z+P(h&59%AB7$PGd_`-dzbCUDGLCR^}@Re<4tm$T!-*0tb9)8UsJaCOp#c&?1Ux0H^2uE7sWd!d7ziGQnWO6gj3 zbJ9L%ObxpiyeK$W%x3iQ*M)HnIi`lrGqKF@rdzzuL_{0W^1<6gquPi*h}S-04gWk*mFnM+v#2%!MIx6IiUl{R}!Y$_3%& z6|ao}MuuVdVp-+z8YS;6HUdwgv$D+p|Nr%Nef-te_@$}&vbOuj$@PDQ@J3JfnVw@C zX%zqf0Afi*K~xwSrNN64!ypU=!2<;H<6}GhH=7cqNp|rhJPA+YO5zv8rjbyKs@6-y zq=r-mfphNK_q`{9Qt7?38v*GGHFnhcg60;bV4YZ*jrNjfjFVUO{@D+y1+lklW3 X)HVwrM3eh900000NkvXXu0mjfdiY+9 literal 0 HcmV?d00001 diff --git a/os3/img/flags/kr.png b/os3/img/flags/kr.png new file mode 100644 index 0000000000000000000000000000000000000000..cf95dc373f2cba2b7c14b9345bd514a06fcae6f8 GIT binary patch literal 440 zcmV;p0Z0CcP)Tt*x!RFG4Oykky2cb8eodGEL@|oYO~9@4dn7u(XqitfZH=?B?uWUtg4zl>GAY zk9VEfVrx@cm6w;7P*70cU1)Lu008(&L_t(I%e9k<4uc>Jh6RV|0xb=EQotR;YV&L@&)C1%F^g<)1ZbBC&rTy#@n6|hKiUN<1nwM zJb%V~!S02i#wJ%){1rBdst!#CAS34wK>YQGPSND%-!w@ne84=>%HxsW9@d&U_UZaYczK| ioEm>~XFk7$ztRIkjSV5~?$av(0000Eak7aXEQK&>~276Z2t$0DX*T$^?-VA}8?B{_sa=(O1}ktZg{L{e_|xE5?OT$W(A cY=$AjqP=D=$y&|ZfCe*oy85}Sb4q9e00fyglK=n! literal 0 HcmV?d00001 diff --git a/os3/img/flags/ky.png b/os3/img/flags/ky.png new file mode 100644 index 0000000000000000000000000000000000000000..08210d3bb513a46caa56cf9a11df1e76e5aaebc5 GIT binary patch literal 543 zcmV+)0^t3LP)E% zbfMIFjtMAj^2W_)Zki=OeaH|lz{uLzR%^e<+2G{u-{b70fRy_9`gntc{PFXOk%;!y z+S%OatgDUbt~^dneg!3K(#my2S%HXugTugp=fh$dHg&zeZr06CAllB`p|Pt}dw)`ReD&VrF*R?+ zU{yt3k)Nq`Gd6I@ZCb~@pp1~6TV;UMm}_p3Z?mwcL3w*ZgImUtXw57x4O%RYe0aZ^ zr2tbP)=fOqrcOt4I97Q*YfA1`0002?Nklv*>Zv~6b9g6FaeYu+;>o`fP3q% zt+v|b{okjYsi}6x0q*+Wz{&qjCJ7SxEQi7flnU@or$ipz3luvcd`A^UG^%_q(GI##A&zq14rksR{F?s4VU3E@WJVY2`L002ovPDHLkV1nau4B`L) literal 0 HcmV?d00001 diff --git a/os3/img/flags/kz.png b/os3/img/flags/kz.png new file mode 100644 index 0000000000000000000000000000000000000000..9d5fc9d26ab53a1d3ed4af191e8327b119a4ffa5 GIT binary patch literal 272 zcmV+r0q_2aP)F|(x~vavt4npC%nFSDlp#SDGAY=*mKpS@B_wvq3{7m&MM&%iFIe-$JE z005XtL_t(2&!v*V4#O}ALjwq~o21?Uf9(#tEJ@Wx8b|a5#<-O2a*aLkI_e%MNA7`n z_}l{Eci^PaQc`jyMf=G0O0xBZls0PgJV{b8gE6+uL6S@q8$rFkCqnHrF)?e`I)E<; zJ$cr2UXQC=MkF#_n?D|dK)8Dc^oKn#3r&YC_S7X5718wOGmB#-V1I;SJ7(L#-K;lF W-2`&)x#4aA0000T+NJ literal 0 HcmV?d00001 diff --git a/os3/img/flags/la.png b/os3/img/flags/la.png new file mode 100644 index 0000000000000000000000000000000000000000..063bcfcceb62b3017c07abff9e7ebb6cc78c7e04 GIT binary patch literal 206 zcmeAS@N?(olHy`uVBq!ia0vp^8bB<~!VDz;*j#BDL7a6?`t^))WTR2M?sQkQ3_Z;uxZF zK3U)hlf(vg!F64e)C&F{^gTe~DWM4f DgZ)VC literal 0 HcmV?d00001 diff --git a/os3/img/flags/lb.png b/os3/img/flags/lb.png new file mode 100644 index 0000000000000000000000000000000000000000..cdb368465361350cf9f57d81b92ac3a81af98bc7 GIT binary patch literal 300 zcmV+{0n`48P)N^^{rv!@QSnn*|NsB(93&sHX0PMO@A~wW+P1&x(|^*ZU&oQw@#AmJ zoZDPkPr`?rg_`dG000PdQchF-|NsC0|Ns8Rqh&t;005LpL_t(I%jMI{4g(IDw67k3wnce?+(*M)(|NPMw0001s&ct6|U%$V<&(F`EgAJ?Q;p^tOkB^VcsS%`+ zKIZK25Dfu|(c#_c?+_3WpP!#EFE3wD2!gYqC_zf(0001dNklSx1j=AMw}^uV`4J7n3=@JACYa@5F=Q6Z<}q(eq)I5920Tdr4f bE%?bB?@0w3AXy0F00000NkvXXu0mjf<^^{v literal 0 HcmV?d00001 diff --git a/os3/img/flags/li.png b/os3/img/flags/li.png new file mode 100644 index 0000000000000000000000000000000000000000..ed694a2161eb14f022c1b9daf57a16704abed107 GIT binary patch literal 249 zcmeAS@N?(olHy`uVBq!ia0vp^8bB<;!3-q#NBMsOQsDtUA+8MC_1z^hQGT48Eub@r{v*JF??e0jvg)8C3gHuBCc!?GezFJ~!puY}AL zqqWn`_%%bPHfsOAV!g6YH6>ahGeXoyFi{X_MXaZbV~EE2x98pY8Vq<`E?OKp=*PeJ z?*BP52NRoHj>PXj+7sk&yzJu7btePQXit1&G;fyR-X}>L!m_i|Gu3ko7@1f;p4@a@ v`Kn2Ww0c^aoOIu0?V}T`w(r|0Unh6(X)*sAuBP-5phXOxu6{1-oD!MuubO6R_FtSZDe-JbIryR6oCxsC;ylg1%uqE}h zCDMu~-aE07~F*O4aSvy9sS008kxL_t(2&yA8va>F1HMVk<(GY|#|5Fpu_ZQuWd zX_PEnv2gh;fRdmHv-mERLoXW~0kVt{W51yDMZ{DQLV9zo!sJa%SjlIiU6ovg%~XNb8& zA9Mf!09i>yK~yNu#n3qpfG`Y1QIo)rv$5R&vQ%lUq2kpe`OC(VYREnf8$k1r0gMcs zd&nSP?qe#ayE~9d$bftHtTlqvD86S~9*eoiWL0E`NN+5nKO5!K>hJ~lL>5=llW6Av O0000;_4R-M z{bgce>hJGoP?*m!)%pMb|9kfAIe-5A`}gnf-@h*}FR!n!KY#vwe}Dht4bPl_CNP!+ z`33)Neie2c$Vv2caSX9I{q~$E-vI>Z6~f)T9jS5n&!F^0}$_#x@PQOD_8 literal 0 HcmV?d00001 diff --git a/os3/img/flags/ly.png b/os3/img/flags/ly.png new file mode 100644 index 0000000000000000000000000000000000000000..29a99731cdde15c14111c6cc5e1c2c3ccb41023e GIT binary patch literal 142 zcmeAS@N?(olHy`uVBq!ia0vp^8bB<iL(&fpU7DE{-7*my-hyF-c@7YILv)2$&Qe6A{T1(>o#{QYgab mtKucFx@?V@V43<}1_rHE+y#z literal 0 HcmV?d00001 diff --git a/os3/img/flags/ma.png b/os3/img/flags/ma.png new file mode 100644 index 0000000000000000000000000000000000000000..54db35bc4b5c31f4d008b5797f1429e1e0419a77 GIT binary patch literal 147 zcmeAS@N?(olHy`uVBq!ia0vp^8bB<~!VDz;*jdBtOk0#0KTY!AxTtFFJD?ncr>mdKI;Vst0NS-0aR2}S literal 0 HcmV?d00001 diff --git a/os3/img/flags/md.png b/os3/img/flags/md.png new file mode 100644 index 0000000000000000000000000000000000000000..0f2a2b2692f8d8c99c035336dd76c449c01c11d5 GIT binary patch literal 345 zcmV-f0jBE6R~S7@Pnc>#r`pq{0001lNklmk-#`lVmY-`?0SWWY zV|8!^32_V|TtrSmuE$s3Kcp}OwC+1VgA%s{G{f_AZ;ukm8@uB(KbL<)sbw*`f-|0gMrbk+wg3PCk4Z#9R4C7_(n%5nF$e=du)!m| z%|2QF|Ipl1iS4RH=e`I;(_m<#q5KH_7>xTK`_K_7phT_>JwjM>KuGXSsAh_Fg_>y- zCShc=3>Mdf!5Fu#RM_y`Kkn;3;a`00000NkvXXu0mjfr$Tv2 literal 0 HcmV?d00001 diff --git a/os3/img/flags/mf.png b/os3/img/flags/mf.png new file mode 100644 index 0000000000000000000000000000000000000000..fd06f8489e5a6d3c434be5eca546509dbbcd3157 GIT binary patch literal 108 zcmeAS@N?(olHy`uVBq!ia0vp^8bB<~!VDz;*j5w9<$d1Ir5$^jv*T7lLc6q4OLSP7-(s{YvNhZ hwCI?}fdmz9CWb-@kK*aP`XI{~JYD@<);T3K0RREIC)xl2 literal 0 HcmV?d00001 diff --git a/os3/img/flags/mh.png b/os3/img/flags/mh.png new file mode 100644 index 0000000000000000000000000000000000000000..dd12f8ba0d11b75da9d6a49d0a82177fca30fbfb GIT binary patch literal 548 zcmV+<0^9wGP){r(z1f+a$GkY6*UWhC9);H0?N zNN}>6u+(acz|){=(|Qv1@8jg@!nd%!lWNPx$JU53A3%Qa z@$Qwiloi5wZWE$fO3ei8cd!eQ=-Sy=hEEl!*7gw0003K zNklV0+K=EM(OAlabv;>f;^Q`C(NKj)C?1HwYQ%G6E-x7;m%HQ@2$2BUK|DDT2@jzg28p-r4oTqIv}x zj{SsC!}ePcAX7@55}i@^kvWOUd<1N|%t2-6XOX$ZOvbW96JYlG5LLo$j1bsrE(-M> zZ}qAQWx*B@;4Asuosos|;(GFup*$FU{5o@F+2bGOP6X+#Z_ m>Eeyk=-=my-q*O%ulWIoQz7_vq3@dj0000z_RAK|A1CIA9p;7}=wD|~0002P zNklKj1c3ugU}`%9Lo$P)83U6ch?l?)B83$gk_8zAfuj5yfm{a`AR+w$ zCF25P%x+>ytExECdj~*?lQR%$e7B&5DBCtEM3YLFn|G2#+-qn7|zEkh*djQ zix5t7L~|0<@e;_62YFK#&6_9zq6rZ+W8gys-2+I_VGCqz(E$K>3O;CQ&SoJ10000< KMNUMnLSTZv>}_lS literal 0 HcmV?d00001 diff --git a/os3/img/flags/ml.png b/os3/img/flags/ml.png new file mode 100644 index 0000000000000000000000000000000000000000..4a086ebc2c8da3d456b621f0807cfe4386397d9b GIT binary patch literal 117 zcmeAS@N?(olHy`uVBq!ia0vp^8bB<~!VDz;*jgTe~DWM4f<}xCq literal 0 HcmV?d00001 diff --git a/os3/img/flags/mm.png b/os3/img/flags/mm.png new file mode 100644 index 0000000000000000000000000000000000000000..a77a25d7f2ef24091cb4b7ded4268adc924dc7cf GIT binary patch literal 313 zcmV-90mlA`P)P)t-s{>uQ& z?92Q4@<6sf>L@wsC^$5-GBmO?{>uRW|NrxTiR?pB``6n3+fe5A=32m6_MN2eP+s`7 zx&Q2-St%?wT3cas{!%ztU>Yyj`1=w8jOhnUH zu_-_x0v6&TA6Y48eO?zVcSK1^BpyCJf4Z5QF|-?N1HTN8FE`@Z*6)_>+4qF)>5xOM zl62AZeLPHB*Ew_qkr*JyLrcyot+aJyS!lX_2E)Qz@miYkD8Gbv{eK)0bOvjZ00000 LNkvXXu0mjfAwZ1T literal 0 HcmV?d00001 diff --git a/os3/img/flags/mn.png b/os3/img/flags/mn.png new file mode 100644 index 0000000000000000000000000000000000000000..bbe5f54a0f45ad04d480075c0480d73c78d75cc5 GIT binary patch literal 204 zcmeAS@N?(olHy`uVBq!ia0vp^8bB<FVdQ&MBb@05?BE Af&c&j literal 0 HcmV?d00001 diff --git a/os3/img/flags/mo.png b/os3/img/flags/mo.png new file mode 100644 index 0000000000000000000000000000000000000000..e57b3f58d7d1a0e2a81ee8531035ec0f9e826ebb GIT binary patch literal 326 zcmV-M0lEH(P)P)t-s4s>CL zxv<0C*d>B+Jdb~s!n&l(#1D01|Ns9>mx(QcSzV%)9(!H1)Xq|qPid*0@b~u8<>Iux zHav+{;qL5sv8U3?F0;KirOU)DI=>G90004WQchF;8si6j0001~Nkl~# z`?FbKQ5R>#tgR~sn5M$9id5qAe))%GB8p&aE#rawBC>b_Z-Eg)-A6Ts8Xy{AFPcG( zN6!XG^(2R*B3U!8I0^!wjJR4e5rUYy0@*6NY1wJU(RI*7o8lVb7Nm#L_!x_5=1 zx4xaZ$X9ugWSOvxcT3A;F0;O?w1a8I#F5TgU?@^z=K3!c>Q;eW}KXf|js<#?HH> z%e2|hlzy3RD~XqP*vjVE$F=D1^Xui&*wfROYd*x5z2CR0Eo_>jf^Ujgf*UFZameR6#*UqA8ev#0B zHaml}rhr~*d{DZVozbJEK!|IF0003+Nkl0Hj^It_1*uI+o@kW0~@X8L+!FmzD;&YNMnYGUj;ATmU>;+}}{F0G9G> z6GLjk8=Uk}v&c!2FS)X7ha6YTOe+?@RS3f!tq5*aG4dc=*{p5Q5TaYc34Ny;ww00001bW%=J{e$XO#{d8U14%?dR5;7El);Y0AP7aP0i+04?f?JlEZR<0 zUNUbpEN@QWg5b98YcMlAaocV%0-WT)6d=(EmV$^OE#RddbH?2i#FsV&TEKkD2akNn z;&I0cEjbWFZ?=FbX>ednXRaein!9Uer`%y`hT_RRr8CD7lvpnn-;9L70NQZMNbyo- z@+gQ%&SOEuyL58F%Tzl?tXzxH@{O^6@fnx08KspA#T%EWfJ^P=O->QPlhZ z0004WQchF-gX&ht0001*NklGf z#?5DLnqYLH0HVDBAZOTCYse5Tz{uJFp1Ce(lRj07rauP!`uo4f*&8=@_SoIvyHmfi_)_ z0CkA~QC^yGQix9|QfMDQS68@W004=g5m{%dM-*_7k6u?)aHx|%q=YR=G$3PaaH)F{ zdMf~pV^t#nFpySNep(!xp+{j9lwFk`0002eNkl=;AI{>EA(tyQ6$l|-&Fcm0^+7whk&O8mEf^t~NEXQ#oN&_1q zo2?NWol=NOB5sU4GFXc-9xXAB>v=UxWCxU22b4y977**>`F5q_`nz0fDP~jUkI_8E zY^eD$gZgIIEudiU@A;w}9&h^s3ikPMP7ByMb-${AguzFC>l=&6@jQ^R@?Dc0Aj*d-aSd2m2?V?(9E)Sp7IqJ7YDW8Vq=tFQ}YlcecOtf6IpED?B%wdzQv|`TRD-18xEu*;N9|BeCc)B=-Xq-=;k<-JdBl$w@MH8!mh-M-~=`YT< pBu1XzCJBe8x~9wshQkd^4C&LoUOtYTxfH06!PC{xWt~$(69BxaD^UOd literal 0 HcmV?d00001 diff --git a/os3/img/flags/mv.png b/os3/img/flags/mv.png new file mode 100644 index 0000000000000000000000000000000000000000..68bece5c69b785d2faeb024ce76d33218f2f3ab6 GIT binary patch literal 206 zcmeAS@N?(olHy`uVBq!ia0vp^8bB<~!VDz;*jCoj zHf5-_G@739|Ns9362=pbugu@pzvK3CmIfRCR@Z=KA)5S4F<9u>L&>;pco-ZOfW^PPk0gmpDDhf^!0eRt%0(lPp>|7|J)gQ{p8EE2o zGD;_Ho}NmWYMAl)GyW>q<~B7gWZBzkI)zQ|5ksP!`}0J}SBXG#89ZJ6T-G@yGywo0 CfJJEl literal 0 HcmV?d00001 diff --git a/os3/img/flags/mw.png b/os3/img/flags/mw.png new file mode 100644 index 0000000000000000000000000000000000000000..88cb2b24167626e61cecb1d65fae38380c8688ee GIT binary patch literal 200 zcmeAS@N?(olHy`uVBq!ia0vp^8bB<~!VDz;*jp8`?^C{kbs?7pak>u&(7@~1LS>OnhL;}y{O#PBvx9K|{7r~tDzNCbRtciU(8a9XLFmg0m^G;+@HCp4))P0+4 uVyM)tHYUdn1|h#Eu=#mOIHa!NU}CtVYP`CK)%!WnHU>{uKbLh*2~7anBswYp literal 0 HcmV?d00001 diff --git a/os3/img/flags/mx.png b/os3/img/flags/mx.png new file mode 100644 index 0000000000000000000000000000000000000000..23d95af4416ebc796bc03f52c85e85d57ca8f34a GIT binary patch literal 339 zcmV-Z0j&OsP)({l; z#Jate(bBh_hHq$~p{0z2Tb`cY-_GaN$K0;1)6uQ5)Z57D=BI2zbyPswxp>&RpNhG< zz>#NsyvMhge!8%y>$$Ryprp>Wgrt>ZN|29iSW9$Xj+X!c00DGTPE-8!?PgH`004JM zL_t(2&tu?32CVEzfQ17IFffcNgeVkLl4p`4LZOVJ3Lh^IKS70pjLblFJfeh6{bE!(`h>3AC5^|#?n;bK0001kNklm=460cN~vi!RBAxBnyTe#`YZs^ zy3Nz5bby@o?|F0-8(MjAq@SPJ9O2<`(@#h6Y>UGE?oaQFmeU{F%@LFKOVCpQ0000< KMNUMnLSTaG^=mo+ literal 0 HcmV?d00001 diff --git a/os3/img/flags/mz.png b/os3/img/flags/mz.png new file mode 100644 index 0000000000000000000000000000000000000000..92b535e01fe40f8d4fcd33dd0502d929a237ed9b GIT binary patch literal 494 zcmVn|} z1s8^FT=mccuc?A)5D_{w1*dNbv7G?U!B})_73$S1vH9kF00001bW%=J{u<*4eE)w?))e+8(kwBXo5xgE55sZzI kK${p5^hQPm8>VvX7m%hV2^iSrK>z>%07*qoM6N<$f>S=voB#j- literal 0 HcmV?d00001 diff --git a/os3/img/flags/na.png b/os3/img/flags/na.png new file mode 100644 index 0000000000000000000000000000000000000000..ef082303d0e0007f79dd0ed9f84cb698eea27a4c GIT binary patch literal 578 zcmV-I0=@l-P)f9RN&fZs5I%UJlrPj8JODL-(hxNL{QS}qHulcd=z^8sQEUi1h1MiRAV_f9Iad4Q z=kB4f5<`jezR1@uP1fJ*)a~Df&89W0YEN{i==Sasol*Sj?x>k7*tQdtjWwUwxaRBg zNwj&Cs>yxAS+TcKFpxZ-V@UXn&YJsb~)!|-tP-R%b z=I=nTa+K1r>hAUY@batN!Cq}qcY8#@ryn?0WSEaL!l@p9nZH!HfnaY?E8gcB0003o zNkl2iWF6o%o$5VM|Ln_S;H8900_eRdogzoM|QKuUMJvAA>mp)r}SkIg_oRVJimZw^`8aD z>2syu6Lw%bHJyI4)w#iuMclc^H#suy+zU89j91@1xE~Vk7vO$4bBZ~De`4Gy4{ literal 0 HcmV?d00001 diff --git a/os3/img/flags/nc.png b/os3/img/flags/nc.png new file mode 100644 index 0000000000000000000000000000000000000000..303514cbed51861c3f44f9db0740a3f2076d3c36 GIT binary patch literal 386 zcmV-|0e$|7P)ys;U400H;Yr zK~yNu&CkaYf-nF@(GWD|LZsRiQHs6(|IfKVcWB%=<5}OCH&67wBz_rY>*BHwGHZAa zmGWYh2bQpKKt@2=UW!iR&F7cINPqoE((&V^}T3XNy&8jFI_L g*et6gNhe*gdg07*qoM6N<$g3f%J9RL6T literal 0 HcmV?d00001 diff --git a/os3/img/flags/ne.png b/os3/img/flags/ne.png new file mode 100644 index 0000000000000000000000000000000000000000..ed09794dc1d4a1e2885d468807e778687e0a5a75 GIT binary patch literal 183 zcmeAS@N?(olHy`uVBq!ia0vp^8bGYX!VDzud$(BtDa8Pv5Ley}+W-Ime-Om>_2`=Q zPj|kkasKn}>HEc1k77h0#)y2sd|+;WOe9copr?yth{pNkfJ00YZSxlxiXBkkU`<;3 zu+zXS=7N*wO77TzHJ+ZH%7VTDGAWLVvleK07)+3GTyyKFnxGa*nJ`5zuf3Pgg&ebxsLQ0J}0k%m4rY literal 0 HcmV?d00001 diff --git a/os3/img/flags/nf.png b/os3/img/flags/nf.png new file mode 100644 index 0000000000000000000000000000000000000000..247114f0665f70f2b936706898ef6582b07e39dc GIT binary patch literal 259 zcmV+e0sQ`nP)9 zb4khJBme*ag-Jv~R4C7l(>V^rKnMgtGhBwb=l_r0)GJwrPAPTNT>8j;{pEh%t~a=n z5M~liffOw0B?VcGGqAK~l7T|aj3I=8jHJFoI7ypz;t&=?cl8P(tef0S!p3@>=unja z7HLXd$@m*W>s^m&=eoWdC!+0bG6uw>7&W<`fk{|6yYRPH_yNUi5yo5;XMX?y002ov JPDHLkV1oHpaO(g7 literal 0 HcmV?d00001 diff --git a/os3/img/flags/ng.png b/os3/img/flags/ng.png new file mode 100644 index 0000000000000000000000000000000000000000..f49a5fee2a1d4b93cf935a90bd6c930d698242fa GIT binary patch literal 99 zcmeAS@N?(olHy`uVBq!ia0vp^8bB<<#0(^xG^T?%oB=)|t_s0>&Db4_&5LZRF?*IS)uanVe?tW?q w6cqJzaSV~ToE*W*&a)+{?GWSH8C%#HlvN{_N9g|h2UNh|>FVdQ&MBb@0Fgc$xBvhE literal 0 HcmV?d00001 diff --git a/os3/img/flags/no.png b/os3/img/flags/no.png new file mode 100644 index 0000000000000000000000000000000000000000..ab5146e41cac4d18c47418228fbc85638aba26b3 GIT binary patch literal 199 zcmeAS@N?(olHy`uVBq!ia0vp^8bB<|!VDy}(v2N}lzM&g~G1>7h)7#=eU?5ST@R+LK=JC-LKeQT4 z>wA&(w!&N}Z)=Odl^<8Hz}xqEpUNrv^w!U(_3& zb|#2f%rX5^R0A;pi<^WAS#+Jsub&yLD_}AS38YvR6^x6LCx)1}Sz08cPDPV%s zlnz>- z2{$coGX@*n5D7L=zOjYZdBVyYF^rc3R8?2~5@JYP&(Ubv99IL>&EV@$vOuAxq!m?AhGs{PFYl)!L_= zBkGV5_1@#l9V=`xMSb`=Z~y=Rhe_+7m?D*P zjFXO-s64_lMtOw4(YbXV-THn%Is{w2TP8RTuruFoJ|m3Qvu(5P)t-s0U>GB zd5+-Z?GP?oIeL)fYz$iUx?wm{BM+;!{nR4se zOe!Ti2p8%@bthxs$vP|9=+QcQpRhlzuyyx!W@1`!nNro~Q2%*T^bKqA9B6oKg*5eU wgBCcVuh4=a|0PMsxLxagjMRG2+Wa!hCoszf#8xWBM*si-07*qoM6N<$f@~3np8x;= literal 0 HcmV?d00001 diff --git a/os3/img/flags/om.png b/os3/img/flags/om.png new file mode 100644 index 0000000000000000000000000000000000000000..46beee0ab7498625fbe9b23c4f9c151c365009ff GIT binary patch literal 174 zcmeAS@N?(olHy`uVBq!ia0vp^8bB<S15y~zNm%ihz)F+}2WvH%Msn~8$P zHfzD)fFI|ad=9RgDLbPna`I_IR@ckShOEA<&8)qFjLiyS4(X2KAq;{cPOK%uEDS=N WstZ#X+2;U_V(@hJb6Mw<&;$VP!8!*3 literal 0 HcmV?d00001 diff --git a/os3/img/flags/pa.png b/os3/img/flags/pa.png new file mode 100644 index 0000000000000000000000000000000000000000..270917c661ec7d9cecc805af9783f424d000f2dc GIT binary patch literal 244 zcmeAS@N?(olHy`uVBq!ia0vp^8bB<~!3-oV&)LR+r~sc3*Z=?jFJ61#+s99D>RVbS zt$Ewod+6jfHs$bJLQ+7f=LxB=N-J*(h~GD~IDG25PeQA%Yk{bK!nb3`Pf4G62Q-AS zB*-uLZ}Y3L<3LWPr;B5V#p$CLBDoq2cw8@T>GXY``MP)9t%hcywUu+pRbPn=nDfSQzN$***nZRirW;nM@TE{ pPm7YArKc;#vFZEI_11Qc>Q@UCZBwmo&H`G>;OXk;vd$@?2>_#_V|)Mr literal 0 HcmV?d00001 diff --git a/os3/img/flags/pe.png b/os3/img/flags/pe.png new file mode 100644 index 0000000000000000000000000000000000000000..b39fe6d5f84e443f88f3da19a7d45281becacaf1 GIT binary patch literal 100 zcmeAS@N?(olHy`uVBq!ia0vp^8bB<~#0(^h>s0>&Db4_&5Z9ah%K!iW|FnMViNma_ xKtVB27sn8Z%gG4|2?4yky$KBsfoghc3~Uwha!OIlo&!}dc)I$ztaD0e0sz1E8wdaZ literal 0 HcmV?d00001 diff --git a/os3/img/flags/pf.png b/os3/img/flags/pf.png new file mode 100644 index 0000000000000000000000000000000000000000..4e08dbe78a53eb3621793790eada93414b178375 GIT binary patch literal 445 zcmV;u0Yd(XP)#R&Jia6{{Cfz!rDt(|NsBnOfvO;CcIh@jE;w`c#z zda8z@{`vR+t2W=Dm3WcJx5eb!;_q5~zW2w6%%ija(2U0^JKvp)=&O9PN>ssGW#pNA z-I<8Qhm7f)UDk$;^2e9|{rUgaoIYi;|LV*OspZ=@N034qxo0Qf0UnomLkNd5%bwmdG5 zL_2Apuj3B!#D!xjv~wU_azrO@T}pe!FxaAIW|cyzan8*JQ{2G1?z{5-P@u*X6X-U= zz^Q^O_UOoME>}S)vxqqwsl-Z)n^mM+Ph@sP1jiP^hdA^&>rwSk$Aql0%1cJ{@m|Ni n$c=DNgFyU}_2ldM%jbFnr??>7Vxsr<00000NkvXXu0mjfvuf;` literal 0 HcmV?d00001 diff --git a/os3/img/flags/pg.png b/os3/img/flags/pg.png new file mode 100644 index 0000000000000000000000000000000000000000..4570a2b91cae7777f4b0f96db9e17812eac3c3bf GIT binary patch literal 422 zcmV;X0a^ZuP)P^_%1@u3;@_4SaDkl^6p^Q{M2u90l*|f(ALxElfyDy2x7eHCosc(FnhhS zRnGvF0x!~lK;#kfC_0T%)=~pii%fm8$d&!~J+uQ}M_EN`nl?@gvB;4gw zT37i7;^Ofm@zA%}*6UJ;TZu#eOCrrEJblTj1&XHm??@l#?dcosmx;Z-3ug09dmk(c zFVy*Zg%ELY)suu-^eNz?PXe|61JvjkT?i{9 Q0{{R307*qoM6N<$f}UKsH~;_u literal 0 HcmV?d00001 diff --git a/os3/img/flags/ph.png b/os3/img/flags/ph.png new file mode 100644 index 0000000000000000000000000000000000000000..7aa14148281d6a5db7141f1ff9be939e4d910b45 GIT binary patch literal 337 zcmV-X0j~auP)3|NsC0{P6wTNd4ep{M9S|>zMEI`Vd5|{PFV9A2Y?$ z?Edk!=ZKTqOk97P(_ezf;A(e3YQC4Q-2Ui`?4PIe#K|C0v(zp@_S4t-;^gqPyWi&Y zufXK2gB5fD005myL_t(2&yCQ>62dSHMNvC0dP?sJq*DI>8(D-E!`Qm(GqNSs?n)N; zUWuT~T{(#+8V1^%0A9U!8Dx}WC4h+r@S0*!dzW#RM7|2Z>D;Cr03dKG!28r%yLbjn zQH}vvq}JM{W55)3PyhRQdTl5GwNI{Pwt*-Fzvp?(L@@}U?i-T8=Rp*KhJkaSSjy$D jo++LJuMJ7yc95tKsq+xHS3d|H00000NkvXXu0mjf5GJVn literal 0 HcmV?d00001 diff --git a/os3/img/flags/pk.png b/os3/img/flags/pk.png new file mode 100644 index 0000000000000000000000000000000000000000..30b4b061c5a8440d3fa0dbff8a2c823b5ad926ec GIT binary patch literal 264 zcmV+j0r&oiP)HBTCl8FsN0I<_zyj)q3f16%0n2DMyWHQwI21*)Yv#-7KPlop1qOg# zGhhL5nHBZs2(+$g$=wV9T2(|7sk`esN|hh3XE`d(p6`*jjq{&;u6yjdOBFN)ftFtY O0000Vhm8P|$p8QU|IYx-007^$01%hU@9*#L!T?~p z-2jotP_x;;n*czp)(B@WFs0HUp3mlhfZyNW0FcI?fdBxG!wQD0aKYdhfRik7X^wLM zA$55}cUa5-0MMua6JRW_udkn02qFzOKw74fk<3Z3M-w+xs?DWgot)x0~MOh(}{$FClR9*2-!eD2$jmt3;;EU zej%F3Glir*ZBcWQMb}LL)I9*dp$H>;fTWrnF@AOHod7hMz}Z~@eT_AM$mG*9070L{ zUSuC)Tn~tU7U_!s?6&|He25 zIswEa1>t-E%oG4Eb#7*dPilofD2tHdl>nooDX5hYzojRZmnJz{IZJ<6r-uM9d2pDE z6i`J2$}j>sc4pBe0MM`wUTrIDgH&pgQkI)ImWB@Z#{h}2mQg?*duGDGpyh-B8j+^} zhq_0PsMNPt?e0009aNklvaro6P=pX7;~J z7+;;CqVZZP|Kcaz|BN_cjLipdGS;}_uPOafkjgU}&*96)*hykBXw$`PN=WOK^pcX5 zDn-EP=-lIH@rBd5(QC8-Ao1P0nEl%xwt&yf3@m_n(OzqT(#*;>Z52d}{#?-Uxdz6QBzG`@2EV-&Jo&=EZ)cfLuOlvD{2ODB z2jf**i`z(YylOiTWZ7+SnMxY0sez2F1+(g7!K*b1?+3$aJt{{QRjagZ%Y*KKUX>>f z$T$o`l&YewWml_rOatvfaJ!w7w7EvpaT-9VKg|Lt?Nw4kZLOO%@H5n;p9CC^Y*llD zWwBPAh0->=zH>|0o)Bz^bkpBCPM$cjj}{x$QzyP))?>j#ahP4MyW>r8DnYW+b@=Fn zEXEIX^>(K2;VQ3bI-bWE-Z@CTZEVa357mOavD> zYnSW}({5UHh@2P{Wzk)vQ#7!@Vz9jaYyW|fl_~2|QWRav!SJyUhsk5(JGdV+qr=~( zMz3EfiCITbZw6;O+6QZsn~3J5jJmre%}FJ1GfvLDZ?2o0GH0Be%c;9zDo#ows0|xC znm)E4>OVlB0)&`igmTOX6=xt+kb~d{VlWgC)J0yh_RZH cnU8>f0G$A`FVh7ZTL1t607*qoM6N<$g2OSOnE(I) literal 0 HcmV?d00001 diff --git a/os3/img/flags/pn.png b/os3/img/flags/pn.png new file mode 100644 index 0000000000000000000000000000000000000000..e35e2c6bdc80aea4f6b40f4c44cb755835bac94b GIT binary patch literal 634 zcmV-=0)_pFP)E; zZkiE3Nd+lh^2W_zbfMIFj>r%$z{uLzR%;L|WCCu+JWf#xGF4n=L;U*tzsK3&YA_93d4rKB zeV#*+t6BpkWF9wMoSZ5WIZ{JiJuO04GeK--ZJI%JCCIxZHDXp|cs8f86<=d53C9dVN~c$XM!fIVq!R*Zgi9%y~8v=wDqQ%Pu4q`HKPe|jK$rtR?T zL5YV~mtOAx00AmVL_t(2&$W`*a>6hWMHd@maK{xlOmnEAw^Y)5@BjZLk?au9WElA= z_d(L#J(^wZ2nyK}14wz6&Eq|jD`a7lZ~~(-h+UBZAfOohi_zj%V)BZjEEm2bv#gBP zIkL(+Cz|_vzaYCoI4&3fz18FZfO1J7r>idX&Zis*UYU+cBh6*U$E6*AvBVyQZ8`{o z%lnHnUDN2FPMK1+kjvo5Fhz9Qj@whqlskQ?SGz|8jZ3igcoMXIGOA0D4|P-2r08ml zB?HH3l(^O#!}rN+-)sU=-oW7b@Pn^WYKI6`%nCny3;Cy!)FOgnmEs$p;jGqaNkmXM zHNLE|)u^czo807~a9e)9IUerPkRpk?yR!P6k;Z{1RL53Z-$@0#=s(!dGHf|1>_lx)C $IlKe&GZS^L~Q2&HIRHvU#KT%&WYU z)rEwF928aoO8)=<|D2%Oa$Y(0Gz)cRcE2DKKQ_MWHh$kOU3-vO_UFULi}FTqm#$sl z;TG*5aOjd_15i)Ar;B5V#`(1u-1!;|1XvCpRpYh%|9>Ti&yJaP7oJaY+x?7d+R7}2 zkb90AmRu)m8WR3B1#L>bD7T_cBYAJYXQqWFe*c}UeETCWSA0Jl{adSMw;9V`$;O0l fEh*nl7^hk5cM7i7_FMNKXcdE}tDnm{r-UW|sV!K_ literal 0 HcmV?d00001 diff --git a/os3/img/flags/pt.png b/os3/img/flags/pt.png new file mode 100644 index 0000000000000000000000000000000000000000..6c2af784b4b4cafa06a0a360decf220a1c211041 GIT binary patch literal 525 zcmV+o0`mQdP)IIWdQpY(An&Dpa9j?0O{xec%=a2cL4c2I^>N2^>+Y@q0sCA0OHgD<|P1Vp8&ec0D`0d zjjI6SG63#A0=vZko3;S>Nk{p7eC}E;`fY9Ml2Yt=CHBd=pR(92R;uS`0M4-h*1-Tl zg#hvj0N7$7-QWP+e*o>o0MCH{-_JSctU}X`0Msx5=zSmbb5h@qJ>Ne7%eF%qZ2*Lt zDw?!C`9c8TT1VgI^5=aZ?(z6%gu?Bh_;>&S00DGTPE-CG;|F~J007ZRL_t(I%jMI@ z4uVh+1yJA>pO2y}UrcYQLM%*_Zv_L{)^2*M9TiVNx&5KBsK z770cDU|hizuTp7}gkh|NWUa8%I#PRzvECE9Y`)_F4xK|9iM`DhpG zb! eO=4!g%g9iy&ikl4;B-0A1_n=8KbLh*2~7YfmOB{$ literal 0 HcmV?d00001 diff --git a/os3/img/flags/py.png b/os3/img/flags/py.png new file mode 100644 index 0000000000000000000000000000000000000000..aa5e86fc3ca481b54caefe554b1748553289259d GIT binary patch literal 200 zcmV;(05|`MP)LBKYM(dj9zM`P7spJMvpMBvtOI>|Gh0Guu4$&W^*cuHJrm<(8r7#cZHn#*!ev;NMxD zpFaUP_MR?|ArhC91DF`MN;fufC>&rA{33WnB*f_8vLYq3sjnhfrbcgES<~d3YPzw> rD}KYuIbLffZ?L}Ca)ZH<+lpc3V&$dRyw`F9jbiY0^>bP0l+XkKYW_?f literal 0 HcmV?d00001 diff --git a/os3/img/flags/re.png b/os3/img/flags/re.png new file mode 100644 index 0000000000000000000000000000000000000000..56d93e8c289fcb9f35849cc0843f5d62608cc8e3 GIT binary patch literal 521 zcmV+k0`~ohP)dcA2Yek-7Ew3{Y3wi>Z-5Z}CHN>!rJK`T_?=bm!08J4ajP!X8VHIE>xqDSknr zhCP=AILc7;Z1oi;!kMcE^zOaq40} z>ngq?fL;20DRTd0_p?z12yt1MVo~C7NGBplj-&q7`j?F`m#`-4|BI}RJg@yM6U8Jl zMePW?7j5Ag!CVAsc|ZtwmV}vjNUSh&iG3cn&aRa1Z)FoV3a#XT*obOMk#WuI)sC4c z8}&<{mWc(+L2#&#W3pCt_989qMWjx9h&8$4{r&z8KXeX4cg8k6|Ns9CKz7e{EJH+W%xXT)k0smB&)&Vi#ymgJl3mGg zQ_HKd&s;RYGB^77_x9xFK|*Wp(9z|wv&m#D;M3I7e1FP#GQ>VL)RigAl$Ge&+Tq#R z)VH_Mu&~aAhsaS%_1oOdoovEXE#ji2MMiGTZ#dS7C(&wA(Qk9ESz@kic=7M=#haeW zOi|IAI?#)b*OHaf%+1PLR^GD-x{%cP{lg@?p-dDWq!>$kbgiA$eG zSKNw(;+KeRN<-3~O{6zM!Cp4biekonCfcW}yo{E#Upvr+e$aPLs&_Zqt7ForG{T!R z)TfKsv9jUL&+Xvg$;83BadF6ef1o0|vH$=80d!JMQ~iVLR>uGU0R>4!K~y-)#m>i4 z!eAJM;S`Xcm~M(mNF#+NO+s%1D)!#n{a=O)one9I%ee7fKXc|_SSC>z_BY5&$sm4u z%qM^a;oK^TB@qXpd>~ZI_wq?ufELf_8qLtPY5{`w1>-mjV?ZBy{I1h^bRMpM`Iydr zwQ<-OjLf!6H`+Vd_0lWYRBUU~dordv0JmB_S!=H$E2kw3!}`mSs0>&DV_kI5Z9|(vj6}8XRus$Ra>rd zwKGVXu_VYZ_@C5c_F5oE%G1R$MB;KXM=LX1lwtTm#Gvk-MbV55O>tc_5bbdbNNz}wBz<=Y^BPC$`ou+hLUhgYH7#zUZ08L=4kgsb}r)%+?A|yelb|;^pLqWntg-C)yjkT%SRox|-f=Gg? zos*LMwGN3DDk2Forpn6BcqHoPu8Aa=(~dy03;+NC literal 0 HcmV?d00001 diff --git a/os3/img/flags/sb.png b/os3/img/flags/sb.png new file mode 100644 index 0000000000000000000000000000000000000000..0333583ffd5408e74f377faa051d7adc0ea760f3 GIT binary patch literal 277 zcmV+w0qXvVP)005CmL_t(2&yCQ@4#F@DMbQ@rxFthJ{{LH9gsPQj(s*OpI+6zg z4mi?9yq?1#NV$|b)!E}M7PA##HCyUTf^jRYacnTDY!|}SiYHbeewuun{UGyv!=&<; zlpwU2gwN3f_)qqACgIEiKrnN4CP9o2z{S{L`!TeJ{=)`4!zMf64cp-@E3kt}olNRv bQYX_FyHX6cB`4h}00000NkvXXu0mjfu+Ds5 literal 0 HcmV?d00001 diff --git a/os3/img/flags/sc.png b/os3/img/flags/sc.png new file mode 100644 index 0000000000000000000000000000000000000000..ba67a2c6203015eb679813844c158d07e7f69145 GIT binary patch literal 355 zcmV-p0i6DcP)ev006H^L_t(2&wb6=7J?uYK+%g+p_P$9SvL3o-xxCJ1;O*Y*E+C8HT%t6 zol_iO$dEhiXcF?7osN_yAsPqU0J7xPhdjOY;53eGRRY3sY%7La1^zg))k@`!7h5r8 zT1}9}wn#b1ms{c~3-uUpNtr>I8_(N?s@l7Jff-L*Uun{v3l-?G-6B=By}TG=f^n?3 zq`bz7gM8c?)%K4Fv9PIgr4)I0&tfI`e&lbRnEucr89Od=jbi`+002ovPDHLkV1fWQ Br^EmN literal 0 HcmV?d00001 diff --git a/os3/img/flags/sd.png b/os3/img/flags/sd.png new file mode 100644 index 0000000000000000000000000000000000000000..154e72e974a7274e957820a667de72a3c8541ecb GIT binary patch literal 234 zcmeAS@N?(olHy`uVBq!ia0vp^8bB<SDitQ|b@`8e$>n#3iKiFsmcRFT>*cEv8yMN8 gA{n%~FI4m}zm64L9{;?0C(teiPgg&ebxsLQ0Np=WcmMzZ literal 0 HcmV?d00001 diff --git a/os3/img/flags/se.png b/os3/img/flags/se.png new file mode 100644 index 0000000000000000000000000000000000000000..7b62bc6a02cf6697ade3a2f60091f11477320386 GIT binary patch literal 104 zcmeAS@N?(olHy`uVBq!ia0vp^8bB<`#0(_uwzYf%Qk(%kA+8Kr%m1BWsGl8N_uKb?*DZ6<>R&-Mv&N3Dgqp>EaloaXvZW0As`?!=pFE zwKgYng!!dyC~y#uk$8~MmAGhuw=wf%rd0~FRh%uDqk)Xg3TEQIIt5xB&f+{V2NDIk w^iJqLY;dhF>t|#Zbl~Jp5IDl*wn>5E({_1&nc5>?fz~j1y85}Sb4q9e00<{ZwEzGB literal 0 HcmV?d00001 diff --git a/os3/img/flags/sh.png b/os3/img/flags/sh.png new file mode 100644 index 0000000000000000000000000000000000000000..f7fe03a8f3b437054bb77e4dcbeeef72e11b09fc GIT binary patch literal 410 zcmV;L0cHM)P)EC zJ!7(+XZ-s6^2W_)Zkk|pp~w&}z{uLzR%^e<*#IDC;Nz6d5z!W z?ELZb_SM>3kB`~h=NB(zA4PrwGUhrhcKg&)wP`Ok0?PRI8V6QgwD&aBL8Txw4K?b(4Ubn2r{Ianb+)0KZ8@ zK~yNu-O$$#gCGM?F?SBC|@P!i&XtGP7iAt#|cE!?U z7>eSww1tA11Ym>;ILcIA*TdTPBub@b4VzLQgmfT3LL)*+f+C}D-6WJeU$pl8Iuvz# zgD`#+45QbZ#FaHi5uYKZtXWEbju@*d%+MF>$MZ>T&Mz6~b_ZMb9=M<#EXrV6UKcPG z`-bm9u$+d*5uejTYD_}NPIZcX!|ZI7>u4ohnDa4(pxz;JkxhVbHCrqw_r89ZJ6T-G@yGywqUnMU^j literal 0 HcmV?d00001 diff --git a/os3/img/flags/sj.png b/os3/img/flags/sj.png new file mode 100644 index 0000000000000000000000000000000000000000..ab5146e41cac4d18c47418228fbc85638aba26b3 GIT binary patch literal 199 zcmeAS@N?(olHy`uVBq!ia0vp^8bB<|!VDy}(v2N}lzMuq0N}2ktu>SG#{o~{qN}D)u zv*dMuVOfu0T9J~Ky7-)+oo!^pGbPS&yS%4O$@pK0004WQchF;8si6j0001! zNkl~x?OfU* ziqL+0>ug%?ZDkmz^_=2AsBn$Od>;wVQ#(X>_5BmD3=xL`@e-7}7yJQ1T_QO^5T8~6 O0000s0>&DV_kI5LbqxWB>pEm)mM3ztuWw zmM0@nT*A}EF+}2Wa>Z6It}x>ZU6NevCdrrN7#NbyMSobl`kgya4TGnvpUXO@geCwg CG96X` literal 0 HcmV?d00001 diff --git a/os3/img/flags/sm.png b/os3/img/flags/sm.png new file mode 100644 index 0000000000000000000000000000000000000000..588cc57dd2f85b2a04f438556341714cdf49d4c7 GIT binary patch literal 664 zcmV;J0%!e+P)V|8X=zf0Xd`rjjG%TOkaa3wYH@K?1ZqqTaZ?d=xZFR0p+0SOONX8;QAtv!)9C8x z;p5>=o5D0%MMr5!c!YJEo16Fd_rb=(S#*9lP)0RUJgcs%Yk6oWM?o)6Q($skadTK( zR4<2hLuzeVP)H$ZYcX?mId+`0YldoglccJQMtP>ad|ee(Uuc(!XStFfaE_i_r_QFm z$y%h#-_NV>-@2r`$Ubz0NQI`~-`~W!q}tKJuePlrE*wQtG<$hnGCnC+V^tzSHoUyN zSXVTtfiH1^Z9YRGwzjsojWtC4009h1L_t(I%jM5SbAwP61z;e^Bf$wyv~l;wz3%Sr^8eTE zGFkA*w$rmc-`q17LH=eo))$0(y9+U2=H?>fb9Xyrqu+0kF;|GA`edS`xcf=cv`kZX zWehU5HO(F)#U&Wa5|{5bYFX%ztd%jE(ap6%wKH*s24Fhf;GcT%2>P>#@( zWUmCJD~er(10oD=C3*g}_43xfFB~K&XZSmQcwT9?+RX~j1)(q=j-Oo<*&^c&d7zNq y#EEn+mnQI48Vaek03F>9M(Mx`1&5CLpYR7uXD<|3t&S=H0000 literal 0 HcmV?d00001 diff --git a/os3/img/flags/sn.png b/os3/img/flags/sn.png new file mode 100644 index 0000000000000000000000000000000000000000..0fb08c084e65465c5ac0884321558d090c7641ce GIT binary patch literal 174 zcmeAS@N?(olHy`uVBq!ia0vp^8bB<~!VDz;*jEaloaXvYLfjLaopg@F$F|kci zeT5}w_kt=xF~?pt9>=x3JUn;0FNCf#G*C&*%71XcBzW@P1i`iiQIn!BUEuY1YGPPt Ws2z0f3!gO5Kn71&KbLh*2~7Y(4LRHZ literal 0 HcmV?d00001 diff --git a/os3/img/flags/so.png b/os3/img/flags/so.png new file mode 100644 index 0000000000000000000000000000000000000000..1fcea0ab9e92357438f44895a31293f2cbf65ba5 GIT binary patch literal 186 zcmeAS@N?(olHy`uVBq!ia0vp^8bB<~!VDz;*jlCwvoXa?v8l`t#@^U=*QsUQ47FJOMmPi&>Spyc~^pdVbj%iW` k?a3Tw1_lKNJcbMm3uHO26&p-_0W_S!)78&qol`;+0Mf`rvH$=8 literal 0 HcmV?d00001 diff --git a/os3/img/flags/sr.png b/os3/img/flags/sr.png new file mode 100644 index 0000000000000000000000000000000000000000..a95ff17aa2f656c6ab4a69b131852ce2b3e9892c GIT binary patch literal 221 zcmeAS@N?(olHy`uVBq!ia0vp^8bB<~!VDz;*jdu{!E4QsVYb8)vs17?bp+MxkuJe*{A}ji?JlgFZiF-WA<7gC(+Zz zF+}5hasuyB21cGO0wIT(B)nn_8m!A49d#NGO0caFJ3N!a@%7mVmR$X0j;OLNsU<#c z;g-6(y28%8L!)jb@i@-jvgNQu)3uqG&43DIwq>?)Cw&$C$k=3R^HOv+L(|_*C1Ve< QWT0gXp00i_>zopr0Cu2EZ~y=R literal 0 HcmV?d00001 diff --git a/os3/img/flags/ss.png b/os3/img/flags/ss.png new file mode 100644 index 0000000000000000000000000000000000000000..48dfff090c96d7708ba4b4463de23607e7a4a860 GIT binary patch literal 280 zcmeAS@N?(olHy`uVBq!ia0vp^8bB<nOJiq&T_F7jq#b9oeX0Fr{p2%*t7K(Io89zr;hr`{%iP<`N#0X_dSohx?&bP0l+XkK^9g8V literal 0 HcmV?d00001 diff --git a/os3/img/flags/st.png b/os3/img/flags/st.png new file mode 100644 index 0000000000000000000000000000000000000000..2a0893ef8423bc5fae7e53b7606819dcda20b8c5 GIT binary patch literal 219 zcmeAS@N?(olHy`uVBq!ia0vp^8bB<b zG;l>VED1iGz~Zt&@W`*Nd~OY_x*KlZ>u?oV{djH%uYh_Ivt)D$14Dz8xig>CIgryC NJYD@<);T3K0RVaPLyQ0b literal 0 HcmV?d00001 diff --git a/os3/img/flags/sv.png b/os3/img/flags/sv.png new file mode 100644 index 0000000000000000000000000000000000000000..fdaef20bf1af213a623ce3f3de4297d100e42add GIT binary patch literal 274 zcmeAS@N?(olHy`uVBq!ia0vp^8bB=0!3-q*c>bybsi**-5ZC`e@aO&iZ|ASS+Rfm; zn$dmr|DX4PV)iBbf#jl#Q>PzqJo9wJ%a^wnp4h&0-jvIJhSY&A}{L%N{e&0dq{C}b) UItuq$fL1Yhy85}Sb4q9e092rfl>h($ literal 0 HcmV?d00001 diff --git a/os3/img/flags/sx.png b/os3/img/flags/sx.png new file mode 100644 index 0000000000000000000000000000000000000000..4904a3e06dcf193f42c143c359330696e5f99f9f GIT binary patch literal 616 zcmV-u0+;=XP)`OHTlxioMe9e?d|XK_~lw&_Tggt?4kGH zM)TJn!)SO1FpAwBBlpF}?vRs7XQ}Ooi_qHUkEh3Tlfj0X#>dj(z{=t6+I9BC#{Kuu z%DqUsuR__#L;m-}%!f#-S7eX5(|?Al`|Y%Lk)@)dqPDiSdV74b#?;x{-REOxRB*2G zr>Xw)^Wiu>t%9B8P*gu&r047O`{Cm4=e+FWpzZ0)_wDQc`QPc&KJ3aklxl|KsVVyB zHuSc*jghh0jxw8>PWREohU2t(|e%+000PdQchF-|NsC0|Ns8Rqh&t;008(&L_t(I%Z<&~ zQo>LaM&WakpWr3|QUIm*-chg@K(Y5;-v2qg@J46Iaj)xdW{GiPg_9v*ZZaVz>r4pf z2_}RYVnT$YObEHggurTu0c~$ySMLo0z$~6O8Yf44s=XlqG_R}&%R1IodqV(--9HVw zt+Ns!!`n*uI2?^y?GgYfQ@*g=iM{lDz0d6F0sycTSek%)XLSc(&;9q5R|aI3mL>#> z*EgAubbi>@t5fXmkT2Y?UEaPn-=exRLqQ{dh=Z!<{9 literal 0 HcmV?d00001 diff --git a/os3/img/flags/sz.png b/os3/img/flags/sz.png new file mode 100644 index 0000000000000000000000000000000000000000..1a44d58980ca53d50546eae23b1605198a5687f6 GIT binary patch literal 570 zcmV-A0>%A_P)!t$$00960|FH}UK3=*1*#I0H8?rhHxHdaXFfl-KR>A$7>taJ znTh_~TNvx8j z8k*O4w>M+@Ap%fSdYIU@y;yL*BdHnTWECXziF`zaH)P_@92^J1>hYy=EY-Hvj+t07*qo IM6N<$f-Xq!8UO$Q literal 0 HcmV?d00001 diff --git a/os3/img/flags/tc.png b/os3/img/flags/tc.png new file mode 100644 index 0000000000000000000000000000000000000000..a4ced626a5bc7c5b211b3a655d6328e1bf6da0f5 GIT binary patch literal 385 zcmV-{0e=38P)H4++006Z~L_t(2&)tyO4uc>NMMqK5 zB7*zAv}*VN|Gq*AO`8~PUwa;a;hr#Iz_UyT1TMy|+-<|NUCb6;72jpJxO$H!RYscY~2sjENhjZqG*~lMXxs<)A9^keny@2J>oas zo=Wtg>G8a)h@Uwri57LB2QF2W)P!8FZB)Y+Swy;yMA@czWYs;cShy)dkjHTzz!wCJ fedPg!A^ar|(v%AWIN>S~00000NkvXXu0mjf3Cy<| literal 0 HcmV?d00001 diff --git a/os3/img/flags/td.png b/os3/img/flags/td.png new file mode 100644 index 0000000000000000000000000000000000000000..bdb4c2534d2e9b1a003f4f19907852c16db58330 GIT binary patch literal 126 zcmeAS@N?(olHy`uVBq!ia0vp^8bB<~!VDz;*j#q0;W4g+NwOM?7@|2Dr0I}YSXdAc};Xq-r-|X$ W0&kOYQ=!E`RScf4elF{r5}E-2&m;K& literal 0 HcmV?d00001 diff --git a/os3/img/flags/tf.png b/os3/img/flags/tf.png new file mode 100644 index 0000000000000000000000000000000000000000..71954b00963ab4d4008f347decde99b495009c61 GIT binary patch literal 283 zcmV+$0p$LPP)@XB$1wl^0F+5YK~y-)?UF$f!XOMqe}pC)5K61}zp6x?aa^=P7p{DZ z4~sV;1OOvS=WsM8?C}?WCkEqU;`MB7ihOzpACl<7>Y7RH h13eZU{1_qN3vROb2cN7AJ$e8D002ovPDHLkV1jJ(dmI1& literal 0 HcmV?d00001 diff --git a/os3/img/flags/tg.png b/os3/img/flags/tg.png new file mode 100644 index 0000000000000000000000000000000000000000..26519e4a784c84c4dc47e73d7787997f7ca39997 GIT binary patch literal 191 zcmeAS@N?(olHy`uVBq!ia0vp^8bB<`!VDxgek%+EQn~>?A+8KrewPGH{-0y`|L^~L zIg6Y6+SgQFU-d8e^Zw(5=;B+pQ9o|pe^%N1_4MTr8}~@2&hr3j^!0Rc4AD5BEYQNh z&X(dZX@P~=L7tqZc6BZ;HcdrFHpjx`#4T3UGj9!8Hw#}+ px;4*Zp}8UJ)C~-?I4*HBFi3Avvpv+f^*hjD22WQ%mvv4FO#oMbM>+ri literal 0 HcmV?d00001 diff --git a/os3/img/flags/th.png b/os3/img/flags/th.png new file mode 100644 index 0000000000000000000000000000000000000000..a49ac81e22dc880b3990a3103569dee241e1f3c7 GIT binary patch literal 112 zcmeAS@N?(olHy`uVBq!ia0vp^8bB<~#0(^h>s0>&DV_kI5Z9%WhF`w^IMY5wSIf(Y zKd%}nF7N5$7$R{wIYEJOO%rolfQ7=DKm6<_$&((*sKifTU{F?VthZMR_yAPO;OXk; Jvd$@?2>|O69$o+d literal 0 HcmV?d00001 diff --git a/os3/img/flags/tj.png b/os3/img/flags/tj.png new file mode 100644 index 0000000000000000000000000000000000000000..beda8e9e11392405129d4a8867caa4507d0bf470 GIT binary patch literal 189 zcmeAS@N?(olHy`uVBq!ia0vp^8bB<H$6>t_*1mXBZg%|Nno>!^3lu z=f5BK|GwSu@AJvOFBklIHu?9%hJYz<|9`(f$^UgaP)nevi(`ny_B(hoW=a%IkvF1-~}OQx}yi|R=PZ~ddOYeCS_O>4@0Zl{+h m<&-xv<%>_(H85k|%fRq&v94!K&l*FZ;S8RxelF{r5}E)au|~=O literal 0 HcmV?d00001 diff --git a/os3/img/flags/tk.png b/os3/img/flags/tk.png new file mode 100644 index 0000000000000000000000000000000000000000..564db8b1685ab720ef6a9bf934a6dd6844026a33 GIT binary patch literal 302 zcmV+}0nz@6P)P)t-s|Iq+h zUPM4mPT#r>5G!JDZaSZjCjlX8fO#~)r5ayzq435AA3J(CP>9d36rHNY5H51BnIRxH zTadv-Qc)lBaI005pzL_t(2&yCXC4uc>NMbQI{%1v#xxBvgr z($*-&2QPu z8Av$K;;2`k0_9o5=3vg?zT78jLme`Qt_dxQrUoPjmg4aHiL~JkhDta-z1a3w&u>pm zf3}(`A+2a-b4aQ?di8s&O)0$`(DGa!on1558^etf0kp7KSpWb407*qoM6N<$f>BL? AqyPW_ literal 0 HcmV?d00001 diff --git a/os3/img/flags/tl.png b/os3/img/flags/tl.png new file mode 100644 index 0000000000000000000000000000000000000000..271088bddfeb454188f0c8d2f07c0db3c31761db GIT binary patch literal 276 zcmV+v0qg#WP)+8><9-Et+)<_=6fgG!= zt1vJyTU%R)hlegME^%>j&z(A&0001oNkl`;AA`6u7AA2}Mn%5H;0I^qFaU%-0fRg?ayQ34FTZdwK%;HiL@(f3Ez~bBU`sdJOD@3S z&yqSZwQ?}7NH2jxGQC+ZgJm$UXfT~dFK|LMrBX0^YdE@aFv3PJqCYR9U|>~lJeF2D zg;YCFV>_l;FiKlCk9JXNU^8&Kl3=oisV*^UNDI~F=$dlkzz7=M?brejn`r>!h0~LbZ<0T zHGsmNP^x*b*~72c!p=G`mv=qPJv_C5FMw!Cp>8m~R7z`WGPjSCpgKN%WoTJdHdH?{ zl4fO;VOpAHGIdijJ$yZ;eoC58Ozw&;UW82RfGwDVTYOnht!73|Y{V}B0004WQchF; z8si6j0003pNkl+1Q1T0Z<`9wS$678Du*N z7Xwh?4K{IpL)fl~V4zFdx!ovLD6R*GrpDvL5@rV@Z%-@ZaWF zVaI`-Tu&Fr5RLQ62_i=rn0UU3i|`&1ITI2zt4&Yi@|KF}#~e)@mj?wVbBl&J?(90^ z>*e)|Kaj<>#C6KH)!LSeIJj1GX~&)pGI9}|5;8?c&}NCr#0_3+(oX$lxVi4Xz~Q;m nFW&9E>mKp9&vpu%$|DB5NfuMp^7Uka7BhId`njxgN@xNA5#nC$ literal 0 HcmV?d00001 diff --git a/os3/img/flags/to.png b/os3/img/flags/to.png new file mode 100644 index 0000000000000000000000000000000000000000..c7d58cb434b00a7f6c31945a794c5ade2123f3fd GIT binary patch literal 154 zcmeAS@N?(olHy`uVBq!ia0vp^8bB<+S+-X7F_Nb6Mw<&;$U4 CYc~b} literal 0 HcmV?d00001 diff --git a/os3/img/flags/tr.png b/os3/img/flags/tr.png new file mode 100644 index 0000000000000000000000000000000000000000..6c6b1b3fe1793b23d353c541c59af05126dd8a3d GIT binary patch literal 227 zcmeAS@N?(olHy`uVBq!ia0vp^8bB<~!VDz;*j?A+C=(#U68s|NsC0xu)rB zpRlLW>OT%0eVdy5@59Fz7EbR=s=qE?@#o>AZ=1G#Z0VBka#I6pWGo5t3;rkdn7tOr zN%3@X4AD5BoFH<9fr+O?Ok`e%ro`PPQ=0TNE-i_ExmCdTg44=?00S0Z15f@Ve0(lq zB4Jh>Nwah2^r&boDiJ&)cHPrOXi9JuM^WgcNy>^-3yga?Z}#1t8&TVG_5BU!%mqq( Z4DTkGXuM$&)C1bb;OXk;vd$@?2>{LXRM!9i literal 0 HcmV?d00001 diff --git a/os3/img/flags/tt.png b/os3/img/flags/tt.png new file mode 100644 index 0000000000000000000000000000000000000000..7d206d7030aa560601bba50576b5dd15798b7bd8 GIT binary patch literal 401 zcmV;C0dD?@P)(e0=9_e_mc*ZJD&y0002UNklP%7He~cenDXks-!0}z9#=C-C zkW31Ct4Sw5!0`t@mAq*Ij=zX-H5)F?$lCn(_|$oh;N-_#k;&n17kWd7X%+1%%kzE@v!p=NEGZb3>o0002aNkldLEm=#lN=kBTbD5Noxg=U6NSu{O0 zj%2W3RNuEoh|?X002ovPDHLk FV1kR!q00aO literal 0 HcmV?d00001 diff --git a/os3/img/flags/tw.png b/os3/img/flags/tw.png new file mode 100644 index 0000000000000000000000000000000000000000..e5104ee66944a4ea6112b3fd3178be8475c38f94 GIT binary patch literal 197 zcmeAS@N?(olHy`uVBq!ia0vp^8bB<~!VDz;*j><*^$i{L&RYNB( zqeD^S-lM<{J&iXnx2AL{?m9K;LO^-R)Jd`n7s?7>44u`|W|bUqWs~VG=EF*lRpX9H th^1bWncwkJIng<3&q@|{p2ZB}3=Ez!Qkuu7{yhwGj;E`i%Q~loCIHn9Mi~GA literal 0 HcmV?d00001 diff --git a/os3/img/flags/tz.png b/os3/img/flags/tz.png new file mode 100644 index 0000000000000000000000000000000000000000..8d6e8d2d317d14e35f37730c235d728cfbb0f266 GIT binary patch literal 383 zcmV-_0f7FAP)6;L9swzAh1_&rY(RlEWs|$~38r4pT)3&8iWdzhJY+A*sPv z)5{#q$U1$xdM~WF)&mF20002ONkluc3$u_w ze|J6D*g8hkl`v-6!W(%#z~U%!mQm)+BjXBz1805=cjV}}kC dEITX5`T&xg9N+$yE{OmD002ovPDHLkV1iYsr$qn& literal 0 HcmV?d00001 diff --git a/os3/img/flags/ua.png b/os3/img/flags/ua.png new file mode 100644 index 0000000000000000000000000000000000000000..3e71d69a0369606af2ffe2a9927b903a7a3c45bf GIT binary patch literal 120 zcmeAS@N?(olHy`uVBq!ia0vp^8bB<~#0(^h>s0>&DV_kI5Z8tovH!0!FhuVLGPb4~ zm;uEZOM?7@|4BV&uLW|XJY5_^BrYdMu(IPR5gwO PRWW$F`njxgN@xNA@^&IT literal 0 HcmV?d00001 diff --git a/os3/img/flags/ug.png b/os3/img/flags/ug.png new file mode 100644 index 0000000000000000000000000000000000000000..ccafc713b2dc62c3d6dde984bdd9204abb4a3f4c GIT binary patch literal 333 zcmeAS@N?(olHy`uVBq!ia0vp^8bB<~!3-oV&)LQRsr&$+5LX~wm&dfmobmtv|2Kgg z28LI;j1ZAOcUW#RGW@>H+}qn5A0Pef`Li86w%ryG__lxl%l!Oz6D9}<2y6>bIB?*= z`RFJmRi#J05$_jHE+{CNGiT0`BS&7ZSaHnMbltjjyJqzNe*4zd-Sy#w2}dWEwFB+o zED7=p{s*)Y2>$(J*J|Ph3buH%}nP!|X=teTBRJOUAmdXp;T* zDfE4u_EvwVl|4xtJr@Q1n$dDjGi-PKt;;*V9y=xc`q(UWUs+M>H_|T_DRpdYP@i^N zlzGQZU;VbPaaH0>d2wos;<-N=%-`*DwC;&oPpy-!_|91lwvFjq{%zLa>tuPw8I&rr Y|L7)Zu0sMzKR_Yj>FVdQ&MBb@0F57y?*IS* literal 0 HcmV?d00001 diff --git a/os3/img/flags/um.png b/os3/img/flags/um.png new file mode 100644 index 0000000000000000000000000000000000000000..68ca505eab80f20a9c98b827ecdc861ea0a4ee0a GIT binary patch literal 252 zcmeAS@N?(olHy`uVBq!ia0vp^8bB<{!VDyjnQYVrQn~>?A+EmX78zI1_7#F3)Y@} zcwC`p(OroQHIJo2LJ1rEyl?Xy((p67J#Fr^ZMxQyuDdO7Oucs0cp^iy>;)kKw~PC@ zxU42HG%JW55a?nx*pa|^W2-R#hOF>IOdG5?8KgVX*K!AiPXIZ~)78&qol`;+0K~#w AZvX%Q literal 0 HcmV?d00001 diff --git a/os3/img/flags/us.png b/os3/img/flags/us.png new file mode 100644 index 0000000000000000000000000000000000000000..68ca505eab80f20a9c98b827ecdc861ea0a4ee0a GIT binary patch literal 252 zcmeAS@N?(olHy`uVBq!ia0vp^8bB<{!VDyjnQYVrQn~>?A+EmX78zI1_7#F3)Y@} zcwC`p(OroQHIJo2LJ1rEyl?Xy((p67J#Fr^ZMxQyuDdO7Oucs0cp^iy>;)kKw~PC@ zxU42HG%JW55a?nx*pa|^W2-R#hOF>IOdG5?8KgVX*K!AiPXIZ~)78&qol`;+0K~#w AZvX%Q literal 0 HcmV?d00001 diff --git a/os3/img/flags/uy.png b/os3/img/flags/uy.png new file mode 100644 index 0000000000000000000000000000000000000000..a198d4b85fb8730a733023cae3f6fd781e738121 GIT binary patch literal 358 zcmV-s0h#`ZP)D|u8qF%j;2+y*7!<$UctzE5m z3ATR*@aNX)+P&!1uJGf*-^rTw?ccnK8MJ^I&8JQ8=;XVQJlDUH-NlE@scq1xM$V=~ z!jKZdk}1@q4f)O#!I2-%o(`yH=(zv@00DGTPE-AZ>Q=`9005RrL_t(I%k9!f4#F@H z1yGkUPTWg+fY8hRFNiomm>`iIfBiI?Mu=P#LcW5c7G4`1Ry6b>?5LJDHbB zrbrRkH2#tu3|3X2p|Ai?c1tIX^w_E77-M-<1{tQmI(BC;9+8T*iV0i>&Re8My6@mI z9F)M;V+8lHwfG*TA;aI-!$I_k*MUWo7OvWBtC+~xHGx~~fIk7@anH)6u$%Gi(e2%F=gbl0-nHJ&X3)mB$H&L<=&RVlEbOJF(Zg%hM@QV(xZT7Y-GhVR zb93~@#_G$<>F3(n3=is-m;Czd;oiyOR6NqZr1|H};ijhJtc2>)Ht^LK-qxSix3~4y z*1fx}*VotIjg91;o#*A(+0Tu@ypynOY|m&9-jqS-hlkUeQR2!m?alz{ya2~#F81c; zOTmd(kw*)009z7L_t(I%k9*~a>OtU24Kf= zQadzZ&N4IamYJDhOYi>_eSxjx>7|#|`5S5U&q!cpL4ifJgnx&6h7r1u2@=24sI$QH zVduWV0P%$~j}?rm5J^N1yq73e>q>?2l#cm>HutIyRsS>0HVFliNpHoOMN6I}w5Y8(mayc^gch}o0~3}UJJ6%{;=qB62M##(c$u<&PU1118o6j% z(v5Xb3>$?H&-Gfxy`#y1<*KAamqBRWgJU5pO&W#0HC*M7Dy@ifSv!H9;Xzopr01R1BfdBvi literal 0 HcmV?d00001 diff --git a/os3/img/flags/ve.png b/os3/img/flags/ve.png new file mode 100644 index 0000000000000000000000000000000000000000..39db5fb2061bb825c4cda886c9aceaeaa76e4635 GIT binary patch literal 215 zcmeAS@N?(olHy`uVBq!ia0vp^8bB<~!VDz;*j?A+G0zwf>)BU{I+&FQWbT z6eE*z&F&+&r7gQ&zWJ=@HD$$?%QoS&gR_=}=dEm=df=h-k4Zp{j3q&S!GD`yg&hZS zB0OCjLp07O7l<5TP~vfRHSpkMUU7)^s&%@^Lhe6_Iiy>12lHg-RR@j0r(Z@7pRYfOar= My85}Sb4q9e0M@rjmjD0& literal 0 HcmV?d00001 diff --git a/os3/img/flags/vg.png b/os3/img/flags/vg.png new file mode 100644 index 0000000000000000000000000000000000000000..a6241a50be452e43f584494172a452297e39c19b GIT binary patch literal 519 zcmV+i0{H!jP)D~ zY9Z8ljy_e1^2W_)Zkjz*ipUTyz{uLzR%dICCO*pMp<=MlEbL9B)4xV<%jKCCk{&%g({M%eE<2DfeAxbl(4&COwd$p3Q0=-8g?Oxt$-G$ zY;nPyX~}4j$nz^*Ld3d_piW>54?4U>H96cV>1HkuKjRePD>=6C2oG1YzJxK8&-0I3 z^!C`S?@KvaHspf?jUgM)HeY5J*D{Do6td70h?iu zzklYvW@u-+UF~ol&iN#4)B?rmdi!*3H}-YaXhDNxCs4DG=NA;*6hFRA#{B>Q002ov JPDHLkV1lZ3_ksWb literal 0 HcmV?d00001 diff --git a/os3/img/flags/vi.png b/os3/img/flags/vi.png new file mode 100644 index 0000000000000000000000000000000000000000..700880a7318b9ec76dbcb563dbee3ac102725409 GIT binary patch literal 1034 zcmV+l1oiugP)bv{@t=Q6eMH({QTp#Hvj+s_4W1It~2}l`xYi=)~hl3`T5VPwf6Jsq&8NkH(21u zf#beb1$(=AN`Lv^M6sIM1aolP*kitjx=!F~^=Pd#uJOh`ZObMvAe=j=0dh znmDdxZR_B^;JZ`O;p&;a(XGqdr^D2-&fkHx(M6TOti`X<#-w$jd<1W++O|<;m2AY* z#(=1WPK#H`zm~0LdepXI+|jb;%Y4=XR{QwxbECfI(wmZeMw5O;^20mq;lQ|;pt_i$pIvcWo@FM1 zw4I+>o3 z>qHvbyO$;ifjsRtI297QY3o4uH|yN1idIH z6f{ou0-07r=qG|eX=WU=;`0|G=a|t^fa4mhE!95T^jfPUP{c52M6p6gD4Jz{q1-_0J*GAAS2$n-37915y2Mo9vi?_k} z*afiWE~DC1PZ3zPS%9{7tCccVQ{lP<*29zmvzL)z5ch+*R>-DG^~=GLS>8B1R;rHA z8pO<)eh0TDe`?;*skT%nG`f5BwdOxR4aDN{Sm5;B-;Df>_WXTaSok1n5*#6XEXEVU zB{a4lfA3$s^XVHn;UR1oTP<>eetqA;caU4}SkRPHNf#w=0$=gH?9QPWZAEQ2CzED7 z$Y{0mvU?{8aRvj2nV`0q{nxO`Bqb&i$^74JBJjZW2RtNIN-q`DumAu607*qoM6N<$ Ef{)%P00000 literal 0 HcmV?d00001 diff --git a/os3/img/flags/vn.png b/os3/img/flags/vn.png new file mode 100644 index 0000000000000000000000000000000000000000..6476c626e9ea7dd6e28470f566e3effff3ca59f1 GIT binary patch literal 199 zcmeAS@N?(olHy`uVBq!ia0vp^8bB<~!VDz;*j^(nnjn$?tGz!|zlKt-)@*_6Wp0Ra_^yBIs0nvx{G>4=Dk91_qGF%vm1 vU{c~O=q8>ll$yX1;=s?hfFYRS@B&r_KOIg-?IZ;!pk)l6u6{1-oD!MUp|0Dn2N4J;1}0RWXl(hxMEgA0FW2%muq{mv48X9+(u10NXy05KLu1rfKE z3;;(TWd|9LbP52AKEw|&&ZrJBCj(AD1ORa|oeU>#S_GyHC>nAY9RL6Tw@E}nR4C7l z(94d(FbqJ^YX|I^Bu${unNlAA|CuZ*SQV1HvvefO=6L(*3ZsuAgK^w zu#eJP6cd`pm6&0&E60H&R(#|R%nBW!eQtB r6>5-Z4d&j--05TP7>BEO)XChR7`++I6nWnW00000NkvXXu0mjfm7j)b literal 0 HcmV?d00001 diff --git a/os3/img/flags/wf.png b/os3/img/flags/wf.png new file mode 100644 index 0000000000000000000000000000000000000000..ab9badba7ab0247dc8799f18001c757aeb24ff04 GIT binary patch literal 227 zcmeAS@N?(olHy`uVBq!ia0vp^8bB<~!VDz;*jkbU%2AP)(aheWdcCGj3q&S!GD`y zg&hZS5?EBTB-m5 literal 0 HcmV?d00001 diff --git a/os3/img/flags/ws.png b/os3/img/flags/ws.png new file mode 100644 index 0000000000000000000000000000000000000000..fd5281057feba662c759cfb06b82c507cd2c17fe GIT binary patch literal 160 zcmeAS@N?(olHy`uVBq!ia0vp^8bB<7;$7tgR8-wDH2ngA`VOtV`12lD(<>nE&2k`ECx?kKbLh* G2~7Z{nJi`i literal 0 HcmV?d00001 diff --git a/os3/img/flags/xk.png b/os3/img/flags/xk.png new file mode 100644 index 0000000000000000000000000000000000000000..af231de1940852cddb4989e4eb888cfa3af294c0 GIT binary patch literal 345 zcmV-f0jB|0000;P)t-sBT1!A zY_>U7no?qp*WT~NpjAI$up~;Q(56r^RjP)6bS_YzL|c_@ae$?dWv`T9eU!>&f54KZ z(z=>jdU$&B^!lc@+q=i(<*)2F00001bW%=J{u<*4eEMddfTDdl{{j88aObIp?BuAZZlrxnf{fzNTMv6E~5 r3`Y}VakjlPl0VxS*7MizbpiYU|D_+Sy~F3c00000NkvXXu0mjflWU*k literal 0 HcmV?d00001 diff --git a/os3/img/flags/ye.png b/os3/img/flags/ye.png new file mode 100644 index 0000000000000000000000000000000000000000..2448e5b0b7f6c923c047abe8b62ba838065de04c GIT binary patch literal 99 zcmeAS@N?(olHy`uVBq!ia0vp^8bB<~#0(^h>s0>&Db4_&5LY05PEhUt|NjNg{v88y uL_J*`LnJOIbF?zEMHz-4WIQ`#3p>LU?#Shlo-PhR1q`09elF{r5}E*LQyM7% literal 0 HcmV?d00001 diff --git a/os3/img/flags/yt.png b/os3/img/flags/yt.png new file mode 100644 index 0000000000000000000000000000000000000000..c93a0d742e4a8c07ef2f54ee0eaacc8dce28e346 GIT binary patch literal 749 zcmVZ% z)iD*r!^3=hvZ<-5fq{XFioG&2j;X27x3{;{h+9gIr{BW8;ci=%mAZ0raz;joOiYs$6p7M% zZN7#VzL6NemL}l3vzM2`y^|#L%R&$U0004WQchF;8si6j0005BNklP?Jh)uom8maAgfT_a&PIy`Ty_9gYytcbS7orFgP;{%-*e3@JCgt{5{NEcyKcp zZk9J1x$EU_o|nOV@nyWiDocqb4r!nz-O4dzN!$blyR5DFWRw#B`C{`lslrnoPxwt*FgknGlI;__SVXLZ3zcea)eq2jvm z1Jc8Xu$G2lS_`{EQ{WextHU=c!wcgRzX+FK)Aai?ys#9Qf;*~ggdJ`lYRfNa`gs|4 zEjOhj>>CSA?8WG%^=RSvfHnOS*nt27@wn*TEf#m(Pmf{<2y{+h06ui@RBCkV0t006*AL_t(I%Z-xR62c%10ApJWV!hg`bv&!}`2Q~y zL=?)o&pSII1PZ~5(nmE3!Qdkn%Q>BGQp9MK!BLsvC?C z8M}lTd*jt3oBtlWggp*_=2C-={A)c|Zo!3hJ@%c)I$ztaD0e0szC-O^N^j literal 0 HcmV?d00001 diff --git a/os3/img/flags/zw.png b/os3/img/flags/zw.png new file mode 100644 index 0000000000000000000000000000000000000000..e506cc7bd5baad8bececb3295d86d63bb752fcea GIT binary patch literal 321 zcmV-H0lxl;P)5M|Ns90WB}69(88Pm+KU77udn^@@9mR@_|?@ANDwee zFl1<8IdK4?hY-{v5)cp&W?*1m004AoFz0V?&|5p_xd3P|FmP&Mh!7C;zrU+F0M-u= z-{0T&s1Mz00ARc`#sB~SheiO@(6BAH;D4F7*37p|7J&t0{( zv$JxWwFwcnMiIZ;ugVC(9Tr6dK)Be%l>Dt)<`LlA9^74dfcK{Z!Zj~8y%fLF(D>7N zK8>J$jj?J0+;KLEfFX_*2C?a4d?qN5uHn%Zm*I~k9>5m3{e;F+SZii2uJsjujH?t1 TLd_n}00000NkvXXu0mjfy6}cA literal 0 HcmV?d00001 diff --git a/os3/img/help.svg b/os3/img/help.svg new file mode 100644 index 0000000..4ac2f82 --- /dev/null +++ b/os3/img/help.svg @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/os3/img/hidden.svg b/os3/img/hidden.svg new file mode 100644 index 0000000..17ed6b3 --- /dev/null +++ b/os3/img/hidden.svg @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/os3/img/logout.svg b/os3/img/logout.svg new file mode 100644 index 0000000..3f9bbcb --- /dev/null +++ b/os3/img/logout.svg @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/os3/img/new.svg b/os3/img/new.svg new file mode 100644 index 0000000..ed21e08 --- /dev/null +++ b/os3/img/new.svg @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/os3/img/warning.svg b/os3/img/warning.svg new file mode 100644 index 0000000..d859455 --- /dev/null +++ b/os3/img/warning.svg @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/os3/js/LICENSE b/os3/js/LICENSE new file mode 100644 index 0000000..4df7d1a --- /dev/null +++ b/os3/js/LICENSE @@ -0,0 +1,11 @@ +The MIT License + +Copyright (c) 2009 Chris Wanstrath (Ruby) +Copyright (c) 2010-2014 Jan Lehnardt (JavaScript) +Copyright (c) 2010-2015 The mustache.js community + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/os3/js/admin.js b/os3/js/admin.js new file mode 100644 index 0000000..42bdf5e --- /dev/null +++ b/os3/js/admin.js @@ -0,0 +1,600 @@ +/* ******************************************************************** + * Orca PHP Search - Administration UI Javascript + * + */ + + +/** + * Generates a readable filesize string from an integer byte-count + * + */ +let readSize = function(bytes, abbr = false) { + bytes = parseInt(bytes); + if (bytes >= 1020054733) return Math.round((bytes / 1073741824), 1) + ' ' + ((abbr) ? 'GiB' : 'GiB'); + if (bytes >= 996148) return Math.round((bytes / 1048576), 1) + ' ' + ((abbr) ? 'MiB' : 'MiB'); + if (bytes >= 973) return Math.round((bytes / 1024), 1) + ' ' + ((abbr) ? 'kiB' : 'kiB'); + if (bytes >= 0) return bytes + ' ' + ((abbr) ? 'B' : 'B'); + return ''; +} + + +let toolTipElems = document.querySelectorAll('[data-bs-toggle="tooltip"]'); +let toolTipList = [...toolTipElems].map(elem => new bootstrap.Tooltip(elem)); + + +let countUpTimers = document.querySelectorAll('span.countup_timer'); +let timeTracker = (new Date()).getTime(); +let countUpPeriods = [['day', 'days'], ['hour', 'hours'], ['minute', 'minutes'], ['second', 'seconds']]; +for (let x = 0; x < countUpTimers.length; x++) { + countUpTimers[x].originalStart = parseInt(countUpTimers[x].getAttribute('data-start').trim()); + + countUpTimers[x].spans = []; + for (let y = 0; y < countUpPeriods.length; y++) { + countUpTimers[x].spans[y] = countUpTimers[x].querySelector(':scope span[data-period="' + countUpPeriods[y][1] + '"]'); + countUpTimers[x].spans[y].tVar = countUpTimers[x].spans[y].getElementsByTagName('var')[0]; + } + + countUpTimers[x].incrementTime = function() { + + // If the start date has changed, or if we are more than 5 seconds + // off the tracker, update all the values + let timeNow = (new Date()).getTime(); + let dataStart = parseInt(this.getAttribute('data-start').trim()); + if (timeTracker + 5000 < timeNow || this.originalStart != dataStart) { + this.originalStart = dataStart; + + let since = Math.round((new Date()).getTime() / 1000) - dataStart; + let periods = []; + periods[0] = Math.floor(since / 86400); since %= 86400; + periods[1] = Math.floor(since / 3600); since %= 3600; + periods[2] = Math.floor(since / 60); + periods[3] = (since %= 60) - 1; + + for (let y = 0; y < periods.length; y++) + this.spans[y].tVar.firstChild.nodeValue = parseInt(periods[y]); + + } + + timeTracker = timeNow; + + this.spans[3].tVar.firstChild.nodeValue = parseInt(this.spans[3].tVar.firstChild.nodeValue) + 1; + if (parseInt(this.spans[3].tVar.firstChild.nodeValue) > 59) { + this.spans[3].tVar.firstChild.nodeValue = 0; + this.spans[2].tVar.firstChild.nodeValue = parseInt(this.spans[2].tVar.firstChild.nodeValue) + 1; + } + if (parseInt(this.spans[2].tVar.firstChild.nodeValue) > 59) { + this.spans[2].tVar.firstChild.nodeValue = 0; + this.spans[1].tVar.firstChild.nodeValue = parseInt(this.spans[1].tVar.firstChild.nodeValue) + 1; + } + if (parseInt(this.spans[1].tVar.firstChild.nodeValue) > 23) { + this.spans[1].tVar.firstChild.nodeValue = 0; + this.spans[0].tVar.firstChild.nodeValue = parseInt(this.spans[0].tVar.firstChild.nodeValue) + 1; + } + + // Plurals + display + let dayPlural = (parseInt(this.spans[0].tVar.firstChild.nodeValue) == 1) ? 0 : 1; + this.spans[0].tVar.nextSibling.nodeValue = ' ' + countUpPeriods[0][dayPlural] + ','; + if (!parseInt(this.spans[0].tVar.firstChild.nodeValue)) { + this.spans[0].classList.add('d-none'); + } else this.spans[0].classList.remove('d-none'); + + let houPlural = (parseInt(this.spans[1].tVar.firstChild.nodeValue) == 1) ? 0 : 1; + this.spans[1].tVar.nextSibling.nodeValue = ' ' + countUpPeriods[1][houPlural] + ','; + if (!parseInt(this.spans[0].tVar.firstChild.nodeValue) && + !parseInt(this.spans[1].tVar.firstChild.nodeValue)) { + this.spans[1].classList.add('d-none'); + } else this.spans[1].classList.remove('d-none'); + + let minPlural = (parseInt(this.spans[2].tVar.firstChild.nodeValue) == 1) ? 0 : 1; + this.spans[2].tVar.nextSibling.nodeValue = ' ' + countUpPeriods[2][minPlural] + ','; + if (!parseInt(this.spans[0].tVar.firstChild.nodeValue) && + !parseInt(this.spans[1].tVar.firstChild.nodeValue) && + !parseInt(this.spans[2].tVar.firstChild.nodeValue)) { + this.spans[2].classList.add('d-none'); + } else this.spans[2].classList.remove('d-none'); + + let secPlural = (parseInt(this.spans[3].tVar.firstChild.nodeValue) == 1) ? 0 : 1; + this.spans[3].tVar.nextSibling.nodeValue = ' ' + countUpPeriods[3][secPlural] + ' ago'; + }; + + setInterval(function() { + countUpTimers[x].incrementTime(); + }, 1000); +} + + +/* ***** Page >> Page Index **************************************** */ +let select_pagination = document.querySelectorAll('select[name="os_index_select_pagination"]'); +for (let x = 0; x < select_pagination.length; x++) { + select_pagination[x].addEventListener('change', function() { + let hidden_pagination = document.querySelector('input[name="os_index_hidden_pagination"]'); + if (hidden_pagination) { + hidden_pagination.value = this.value; + this.form.submit(); + } + }, false); +} + +let os_index_pagination_page_select = document.querySelectorAll('select[name="os_index_pagination_page_select"]'); +for (let x = 0; x < os_index_pagination_page_select.length; x++) { + os_index_pagination_page_select[x].addEventListener('change', function() { + window.location.href = '?ipage=' + this.value; + }, false); +} + +let os_index_filter_text = document.querySelector('input[name="os_index_filter_text"]'); +let os_index_filter_text_clear = document.querySelector('button[name="os_index_filter_text_clear"]'); +if (os_index_filter_text_clear && os_index_filter_text) { + os_index_filter_text_clear.addEventListener('click', function() { + os_index_filter_text.value = ''; + let os_submit = document.querySelector('button[name="os_submit"][value="os_index_filter_text"]'); + if (os_submit) os_submit.click(); + }, false); +} + +let os_index_filter_by_category = document.querySelector('select[name="os_index_filter_by_category"]'); +if (os_index_filter_by_category) { + os_index_filter_by_category.addEventListener('change', function() { + let new_filter_category = document.querySelector('input[name="os_index_new_filter_category"]'); + if (new_filter_category) { + new_filter_category.value = this.value; + this.form.submit(); + } + }, false); +} + +let os_index_filter_by_status = document.querySelector('select[name="os_index_filter_by_status"]'); +if (os_index_filter_by_status) { + os_index_filter_by_status.addEventListener('change', function() { + let new_filter_status = document.querySelector('input[name="os_index_new_filter_status"]'); + if (new_filter_status) { + new_filter_status.value = this.value; + this.form.submit(); + } + }, false); +} + +let os_index_check_all = document.querySelectorAll('input[name="os_index_check_all"]'); +for (let x = 0; x < os_index_check_all.length; x++) { + os_index_check_all[x].addEventListener('click', function() { + let os_index_pages = document.querySelectorAll('input[name="os_index_pages[]"]'); + for (let y = 0; y < os_index_pages.length; y++) + os_index_pages[y].checked = this.checked; + for (let y = 0; y < os_index_check_all.length; y++) + os_index_check_all[y].checked = this.checked; + }, false); +} + +let os_index_pages_last_checked = false; +let os_index_pages = document.querySelectorAll('input[name="os_index_pages[]"]'); +for (let x = 0; x < os_index_pages.length; x++) { + os_index_pages[x].addEventListener('click', function(e) { + let thisIndex = parseInt(this.getAttribute('data-index')); + if (e.shiftKey && os_index_pages_last_checked !== false) { + let i = os_index_pages_last_checked; + do { + if (i < thisIndex) { i++; } else i--; + let box = document.querySelector('input[name="os_index_pages[]"][data-index="' + i + '"]'); + if (box) box.checked = 'checked'; + } while (i != thisIndex); + os_index_pages_last_checked = thisIndex; + } else if (this.checked) { + os_index_pages_last_checked = thisIndex; + } else os_index_pages_last_checked = false; + }, false); +} + +let os_index_select_action = document.querySelectorAll('select[name="os_index_select_action"]'); +let os_index_with_selected = document.querySelectorAll('button[name="os_submit"][value="os_index_with_selected"]'); +for (let x = 0; x < os_index_with_selected.length; x++) { + os_index_with_selected[x].addEventListener('click', function(e) { + let any_checked = false; + for (let y = 0; y < os_index_pages.length && !any_checked; y++) + if (os_index_pages[y].checked) any_checked = true; + + if (any_checked) { + let sib_select = this.parentNode.getElementsByTagName('select')[0]; + for (let z = 0; z < os_index_select_action.length; z++) + if (os_index_select_action[z] != sib_select) + os_index_select_action[z].selectedIndex = sib_select.selectedIndex; + + switch (sib_select.value) { + case 'delete': + if (confirm('Are you sure you\'d like to delete the selected pages from the search database?')) { + return true; + } + break; + + case 'category': + let new_category = prompt('Enter a category name to apply to the selected pages. Maximum length is 30 characters, but shorter is better.'); + if (new_category) { + let os_apply_new_category = document.querySelector('input[name="os_apply_new_category"]'); + if (os_apply_new_category) os_apply_new_category.value = new_category; + return true; + } + break; + + case 'priority': + let new_priority = prompt('Enter a new priority value for these pages. A valid value is bewteen 0.0 and 1.0 inclusive.'); + if (new_priority) { + let os_apply_new_priority = document.querySelector('input[name="os_apply_new_priority"]'); + if (os_apply_new_priority) os_apply_new_priority.value = new_priority; + return true; + } + break; + + case 'unlisted': + return true; + + default: + alert('Select an action'); + + } + } else alert('No pages selected'); + + e.preventDefault(); + return false; + }, false); +} + + +/* ***** Page >> Query Log ***************************************** */ +let os_queries_tbody = document.getElementById('os_queries_tbody'); +if (os_queries_tbody) { + let os_queries_sort = function() { + let self = this; + let sorted = this.sorted; + + Object.keys(os_queries_columns).forEach(key => { + os_queries_columns[key].parentNode.classList.remove('os_sorting', 'os_asc', 'os_desc'); + os_queries_columns[key].sorted = ''; + }); + + if (sorted == 'desc') { + this.sorted = 'asc'; + } else this.sorted = 'desc'; + + this.parentNode.classList.add('os_sorting', 'os_' + this.sorted); + + let row_list = Array.prototype.slice.call( + os_queries_tbody.getElementsByTagName('tr'), 0); + + row_list.sort(function(a, b) { + let adval = parseInt(a.cells[self.index].getAttribute('data-value')); + let bdval = parseInt(b.cells[self.index].getAttribute('data-value')); + if (bdval == adval) return 0; + if (self.sorted == 'desc') { + return (bdval > adval) ? 1 : -1; + } else return (bdval > adval) ? -1 : 1; + }); + + // Add back the sorted rows + for (let x = 0; x < row_list.length; x++) + os_queries_tbody.appendChild(row_list[x]); + + }; + + let os_queries_columns = { + query: document.getElementById('os_queries_query'), + hits: document.getElementById('os_queries_hits'), + results: document.getElementById('os_queries_results'), + stamp: document.getElementById('os_queries_stamp') + }; + let index = 0; + Object.keys(os_queries_columns).forEach(key => { + os_queries_columns[key].addEventListener('click', os_queries_sort, false); + os_queries_columns[key].index = index++; + }); + os_queries_columns.hits.sorted = 'desc'; +} + +let queriesModal = document.getElementById('queriesModal'); +if (queriesModal) { + queriesModal.addEventListener('show.bs.modal', function(e) { + let btn = e.relatedTarget; + + let parentRow = btn.parentNode; + while (parentRow.nodeName != 'TR') + parentRow = parentRow.parentNode; + + let values = parentRow.querySelectorAll('[data-value]'); + values = { + query: btn.title, + hits: values[1].getAttribute('data-value'), + results: values[2].getAttribute('data-value'), + stamp: new Date(parseInt(values[3].getAttribute('data-value')) * 1000).toString(), + ipaddr: values[4].innerHTML + }; + + Object.keys(values).forEach(keys => { + let dd = document.getElementById('os_queries_modal_' + keys); + dd.innerHTML = values[keys]; + }); + + + }, false); +} + + +/* ***** Crawler Modal ********************************************* */ +let os_get_crawl_progress = function() { + fetch(new Request('./crawler.php'), { + method: 'POST', + headers: { 'Content-type': 'application/json' }, + body: JSON.stringify({ + action: 'progress', + grep: document.querySelector('input[name="os_crawl_grep"]:checked').value + }) + }) + .then((response) => response.text()) + .then((data) => { + try { + data = JSON.parse(data); + } catch(e) { + console.log('Invalid JSON received: ' + data); + return; + } + + if (!data.tail) return; + + if (data.status == 'Crawling' && data.time_crawl > data.timeout_crawl) { + clearInterval(os_crawl_interval); + data.status = 'Complete'; + + alert('The Crawl appears to have timed out. Canceling...'); + + os_crawl_cancel.force = true; + os_crawl_cancel.click(); + } + + os_crawl_start.allow_grep = true; + os_crawl_log.value = data.tail; + + if (os_crawl_interval) { + data.progress = data.progress.split('/'); + os_crawl_progress.value = data.progress[0]; + os_crawl_progress.max = data.progress[1]; + os_crawl_progress.setAttribute('data-progress', data.progress[0] + ' / ' + data.progress[1]); + os_crawl_progress.innerHTML = Math.ceil(data.progress[0] / data.progress[1]) + '%'; + os_crawl_log.scrollTop = os_crawl_log.scrollHeight; + } + + if (!os_crawl_start.complete && data.status == 'Complete') { + clearInterval(os_crawl_interval); + + os_crawl_cancel.disabled = 'disabled'; + os_crawl_log_download.disabled = ''; + os_crawl_start.complete = true; + + if (os_crawl_interval) { + os_crawl_start.innerHTML = 'Crawl Complete'; + os_crawl_navbar.innerHTML = 'Crawler'; + os_crawl_interval = false; + + // Check if the crawler modal window is open + if (crawlerModal && crawlerModal.classList.contains('show')) { + + // Don't refresh the page until the user closes the modal + crawlerModal.addEventListener('hide.bs.modal', function() { + window.location.reload(); + }, false); + } else window.location.reload(); + } + } + }); +}; + +let os_crawl_interval; +let os_crawl_start = document.getElementById('os_crawl_start'); +let os_crawl_navbar = document.getElementById('os_crawl_navbar'); +let os_crawl_cancel = document.getElementById('os_crawl_cancel'); +let os_crawl_grep = document.querySelectorAll('input[name="os_crawl_grep"]'); +let os_crawl_progress = document.getElementById('os_crawl_progress'); +let os_crawl_log = document.getElementById('os_crawl_log'); +let os_crawl_log_download = document.getElementById('os_crawl_log_download'); + +os_crawl_cancel.force = false; +os_crawl_cancel.reason = ''; +os_crawl_start.allow_grep = false; +os_crawl_start.complete = false; +os_crawl_start.addEventListener('click', function(e) { + e.preventDefault(); + + os_crawl_start.disabled = 'disabled'; + os_crawl_start.innerHTML = 'Starting Crawl...'; + os_crawl_navbar.innerHTML = 'Starting Crawl...'; + os_crawl_log.value = ''; + + os_crawl_progress.value = 0 + os_crawl_progress.max = 1; + os_crawl_progress.setAttribute('data-progress', ''); + os_crawl_progress.innerHTML = '0%'; + + os_crawl_start.allow_grep = false; + os_crawl_start.complete = false; + + // Create a crawl key + fetch(new Request('./admin.php'), { + method: 'POST', + headers: { 'Content-type': 'application/json' }, + body: JSON.stringify({ action: 'setkey' }) + }) + + // If we successfully set a crawl key, start the crawl with it + .then((response) => response.text()) + .then((data) => { + try { + data = JSON.parse(data); + } catch(e) { + data = { + 'status': 'Error', + 'message': 'Invalid key response from server' + }; + } + + if (data.status == 'Success') { + os_crawl_cancel.disabled = ''; + os_crawl_log_download.disabled = 'disabled'; + os_crawl_start.innerHTML = 'Crawling...'; + os_crawl_navbar.innerHTML = 'Crawling...'; + + fetch(new Request('./crawler.php'), { + method: 'POST', + headers: { 'Content-type': 'application/json' }, + body: JSON.stringify({ action: 'crawl', sp_key: data.sp_key }) + }) + .then((response) => { + if (response.status === 200) { + response.text().then((data) => { + try { + data = JSON.parse(data); + console.log(data); + } catch(e) { + console.log('Invalid JSON received: ' + data); + } + }); + + // Cancel immediately if we get a 500 response + } else if (response.status >= 500) { + clearInterval(os_crawl_interval); + + os_crawl_cancel.reason = 'The crawler unexpectedly halted with HTTP response code ' + response.status + ': ' + response.statusText; + + alert( + 'Error: ' + os_crawl_cancel.reason + "\n" + + 'The crawl will be cancelled and reset.' + ); + + throw Error(response.statusText); + } + }) + .catch(error => { + console.error('Error: ', error); + os_crawl_cancel.force = true; + os_crawl_cancel.click(); + }); + + // Start an interval progress check + os_crawl_interval = setInterval(os_get_crawl_progress, 1000); + + } else if (data.status = 'Error') { + os_crawl_log.value = data.message; + os_crawl_start.innerHTML = 'Couldn\'t Start Crawl'; + os_crawl_navbar.innerHTML = 'Crawler'; + + setTimeout(function() { + os_crawl_start.disabled = ''; + os_crawl_start.innerHTML = 'Start Crawl'; + }, 5000); + } + }); + + return false; +}, false); + +// If start button is disabled on pageload, then the crawler is already running +if (os_crawl_start.disabled) { + + // Start an interval progress check + os_crawl_interval = setInterval(os_get_crawl_progress, 1000); +} + +os_crawl_cancel.addEventListener('click', function() { + if (this.force || window.confirm('Are you sure you want to cancel the crawl currently in progress?')) { + if (this.force || !this.disabled) { + fetch(new Request('./crawler.php'), { + method: 'POST', + headers: { 'Content-type': 'application/json' }, + body: JSON.stringify({ + action: 'cancel', + force: os_crawl_cancel.force, + reason: os_crawl_cancel.reason + }) + }) + .then((response) => response.text()) + .then((data) => { + try { + data = JSON.parse(data); + if (data.status == 'Success') + os_get_crawl_progress(); + } catch(e) { + console.log('Invalid JSON received: ' + data); + } + }); + } + this.force = false; + }; +}, false); + +os_crawl_log_download.addEventListener('click', function() { + fetch(new Request('./admin.php'), { + method: 'POST', + headers: { 'Content-type': 'application/json' }, + body: JSON.stringify({ + action: 'download', + content: 'crawl_log', + grep: document.querySelector('input[name="os_crawl_grep"]:checked').value + }) + }) + .then((response) => { + if (response.status === 200) { + let ct = response.headers.get('content-type').trim(); + if (ct == 'application/json') { + response.json().then((data) => { + if (data.status == 'Error') + alert(data.message); + }); + } else { + let cd = response.headers.get('content-disposition'); + let filename = cd.match(/filename="([^"]+)"/); + filename = (filename.length > 1) ? filename[1] : 'log.txt'; + response.blob().then((blob) => { + let file = window.URL.createObjectURL(blob); + let a = document.createElement('a'); + a.href = file; + a.download = filename; + document.body.appendChild(a); + a.click(); + a.remove(); + }); + } + } + }); +}, false); + +for (let x = 0; x < os_crawl_grep.length; x++) { + os_crawl_grep[x].addEventListener('input', function() { + if (os_crawl_start.allow_grep || + crawlerModal.classList.contains('crawler-log')) { + os_get_crawl_progress(); + } + }, false); +} + +let crawlerModal = document.getElementById('crawlerModal'); +crawlerModal.addEventListener('show.bs.modal', function(e) { + let btn = e.relatedTarget; + let label = document.getElementById('crawlerModalLabel'); + + os_crawl_log.value = ''; + + switch (btn.getAttribute('data-bs-crawl')) { + case 'run': + this.classList.remove('crawler-log'); + label.firstChild.nodeValue = 'Run Crawler Manually'; + + os_crawl_progress.value = 0; + os_crawl_progress.max = 1; + os_crawl_progress.setAttribute('data-progress', ''); + os_crawl_progress.innerHTML = ''; + break; + + case 'log': + default: + this.classList.add('crawler-log'); + label.firstChild.nodeValue = 'View Crawler Log'; + + os_get_crawl_progress(); + + } +}, false); diff --git a/os3/js/bootstrap.bundle.min.js b/os3/js/bootstrap.bundle.min.js new file mode 100644 index 0000000..ff3d60e --- /dev/null +++ b/os3/js/bootstrap.bundle.min.js @@ -0,0 +1,6 @@ +/*! + * Bootstrap v5.3.0-alpha2 (https://getbootstrap.com/) + * Copyright 2011-2023 The Bootstrap Authors (https://github.com/twbs/bootstrap/graphs/contributors) + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) + */ +!function(t,e){"object"==typeof exports&&"undefined"!=typeof module?module.exports=e():"function"==typeof define&&define.amd?define(e):(t="undefined"!=typeof globalThis?globalThis:t||self).bootstrap=e()}(this,(function(){"use strict";const t="transitionend",e=t=>(t&&window.CSS&&window.CSS.escape&&(t=t.replace(/#([^\s"#']+)/g,((t,e)=>`#${CSS.escape(e)}`))),t),i=e=>{e.dispatchEvent(new Event(t))},n=t=>!(!t||"object"!=typeof t)&&(void 0!==t.jquery&&(t=t[0]),void 0!==t.nodeType),s=t=>n(t)?t.jquery?t[0]:t:"string"==typeof t&&t.length>0?document.querySelector(e(t)):null,o=t=>{if(!n(t)||0===t.getClientRects().length)return!1;const e="visible"===getComputedStyle(t).getPropertyValue("visibility"),i=t.closest("details:not([open])");if(!i)return e;if(i!==t){const e=t.closest("summary");if(e&&e.parentNode!==i)return!1;if(null===e)return!1}return e},r=t=>!t||t.nodeType!==Node.ELEMENT_NODE||!!t.classList.contains("disabled")||(void 0!==t.disabled?t.disabled:t.hasAttribute("disabled")&&"false"!==t.getAttribute("disabled")),a=t=>{if(!document.documentElement.attachShadow)return null;if("function"==typeof t.getRootNode){const e=t.getRootNode();return e instanceof ShadowRoot?e:null}return t instanceof ShadowRoot?t:t.parentNode?a(t.parentNode):null},l=()=>{},c=t=>{t.offsetHeight},h=()=>window.jQuery&&!document.body.hasAttribute("data-bs-no-jquery")?window.jQuery:null,d=[],u=()=>"rtl"===document.documentElement.dir,f=t=>{var e;e=()=>{const e=h();if(e){const i=t.NAME,n=e.fn[i];e.fn[i]=t.jQueryInterface,e.fn[i].Constructor=t,e.fn[i].noConflict=()=>(e.fn[i]=n,t.jQueryInterface)}},"loading"===document.readyState?(d.length||document.addEventListener("DOMContentLoaded",(()=>{for(const t of d)t()})),d.push(e)):e()},p=(t,e=[],i=t)=>"function"==typeof t?t(...e):i,m=(e,n,s=!0)=>{if(!s)return void p(e);const o=(t=>{if(!t)return 0;let{transitionDuration:e,transitionDelay:i}=window.getComputedStyle(t);const n=Number.parseFloat(e),s=Number.parseFloat(i);return n||s?(e=e.split(",")[0],i=i.split(",")[0],1e3*(Number.parseFloat(e)+Number.parseFloat(i))):0})(n)+5;let r=!1;const a=({target:i})=>{i===n&&(r=!0,n.removeEventListener(t,a),p(e))};n.addEventListener(t,a),setTimeout((()=>{r||i(n)}),o)},g=(t,e,i,n)=>{const s=t.length;let o=t.indexOf(e);return-1===o?!i&&n?t[s-1]:t[0]:(o+=i?1:-1,n&&(o=(o+s)%s),t[Math.max(0,Math.min(o,s-1))])},_=/[^.]*(?=\..*)\.|.*/,b=/\..*/,v=/::\d+$/,y={};let w=1;const A={mouseenter:"mouseover",mouseleave:"mouseout"},E=new Set(["click","dblclick","mouseup","mousedown","contextmenu","mousewheel","DOMMouseScroll","mouseover","mouseout","mousemove","selectstart","selectend","keydown","keypress","keyup","orientationchange","touchstart","touchmove","touchend","touchcancel","pointerdown","pointermove","pointerup","pointerleave","pointercancel","gesturestart","gesturechange","gestureend","focus","blur","change","reset","select","submit","focusin","focusout","load","unload","beforeunload","resize","move","DOMContentLoaded","readystatechange","error","abort","scroll"]);function T(t,e){return e&&`${e}::${w++}`||t.uidEvent||w++}function C(t){const e=T(t);return t.uidEvent=e,y[e]=y[e]||{},y[e]}function O(t,e,i=null){return Object.values(t).find((t=>t.callable===e&&t.delegationSelector===i))}function x(t,e,i){const n="string"==typeof e,s=n?i:e||i;let o=D(t);return E.has(o)||(o=t),[n,s,o]}function k(t,e,i,n,s){if("string"!=typeof e||!t)return;let[o,r,a]=x(e,i,n);if(e in A){const t=t=>function(e){if(!e.relatedTarget||e.relatedTarget!==e.delegateTarget&&!e.delegateTarget.contains(e.relatedTarget))return t.call(this,e)};r=t(r)}const l=C(t),c=l[a]||(l[a]={}),h=O(c,r,o?i:null);if(h)return void(h.oneOff=h.oneOff&&s);const d=T(r,e.replace(_,"")),u=o?function(t,e,i){return function n(s){const o=t.querySelectorAll(e);for(let{target:r}=s;r&&r!==this;r=r.parentNode)for(const a of o)if(a===r)return N(s,{delegateTarget:r}),n.oneOff&&I.off(t,s.type,e,i),i.apply(r,[s])}}(t,i,r):function(t,e){return function i(n){return N(n,{delegateTarget:t}),i.oneOff&&I.off(t,n.type,e),e.apply(t,[n])}}(t,r);u.delegationSelector=o?i:null,u.callable=r,u.oneOff=s,u.uidEvent=d,c[d]=u,t.addEventListener(a,u,o)}function L(t,e,i,n,s){const o=O(e[i],n,s);o&&(t.removeEventListener(i,o,Boolean(s)),delete e[i][o.uidEvent])}function S(t,e,i,n){const s=e[i]||{};for(const[o,r]of Object.entries(s))o.includes(n)&&L(t,e,i,r.callable,r.delegationSelector)}function D(t){return t=t.replace(b,""),A[t]||t}const I={on(t,e,i,n){k(t,e,i,n,!1)},one(t,e,i,n){k(t,e,i,n,!0)},off(t,e,i,n){if("string"!=typeof e||!t)return;const[s,o,r]=x(e,i,n),a=r!==e,l=C(t),c=l[r]||{},h=e.startsWith(".");if(void 0===o){if(h)for(const i of Object.keys(l))S(t,l,i,e.slice(1));for(const[i,n]of Object.entries(c)){const s=i.replace(v,"");a&&!e.includes(s)||L(t,l,r,n.callable,n.delegationSelector)}}else{if(!Object.keys(c).length)return;L(t,l,r,o,s?i:null)}},trigger(t,e,i){if("string"!=typeof e||!t)return null;const n=h();let s=null,o=!0,r=!0,a=!1;e!==D(e)&&n&&(s=n.Event(e,i),n(t).trigger(s),o=!s.isPropagationStopped(),r=!s.isImmediatePropagationStopped(),a=s.isDefaultPrevented());let l=new Event(e,{bubbles:o,cancelable:!0});return l=N(l,i),a&&l.preventDefault(),r&&t.dispatchEvent(l),l.defaultPrevented&&s&&s.preventDefault(),l}};function N(t,e={}){for(const[i,n]of Object.entries(e))try{t[i]=n}catch(e){Object.defineProperty(t,i,{configurable:!0,get:()=>n})}return t}const P=new Map,j={set(t,e,i){P.has(t)||P.set(t,new Map);const n=P.get(t);n.has(e)||0===n.size?n.set(e,i):console.error(`Bootstrap doesn't allow more than one instance per element. Bound instance: ${Array.from(n.keys())[0]}.`)},get:(t,e)=>P.has(t)&&P.get(t).get(e)||null,remove(t,e){if(!P.has(t))return;const i=P.get(t);i.delete(e),0===i.size&&P.delete(t)}};function M(t){if("true"===t)return!0;if("false"===t)return!1;if(t===Number(t).toString())return Number(t);if(""===t||"null"===t)return null;if("string"!=typeof t)return t;try{return JSON.parse(decodeURIComponent(t))}catch(e){return t}}function F(t){return t.replace(/[A-Z]/g,(t=>`-${t.toLowerCase()}`))}const H={setDataAttribute(t,e,i){t.setAttribute(`data-bs-${F(e)}`,i)},removeDataAttribute(t,e){t.removeAttribute(`data-bs-${F(e)}`)},getDataAttributes(t){if(!t)return{};const e={},i=Object.keys(t.dataset).filter((t=>t.startsWith("bs")&&!t.startsWith("bsConfig")));for(const n of i){let i=n.replace(/^bs/,"");i=i.charAt(0).toLowerCase()+i.slice(1,i.length),e[i]=M(t.dataset[n])}return e},getDataAttribute:(t,e)=>M(t.getAttribute(`data-bs-${F(e)}`))};class ${static get Default(){return{}}static get DefaultType(){return{}}static get NAME(){throw new Error('You have to implement the static method "NAME", for each component!')}_getConfig(t){return t=this._mergeConfigObj(t),t=this._configAfterMerge(t),this._typeCheckConfig(t),t}_configAfterMerge(t){return t}_mergeConfigObj(t,e){const i=n(e)?H.getDataAttribute(e,"config"):{};return{...this.constructor.Default,..."object"==typeof i?i:{},...n(e)?H.getDataAttributes(e):{},..."object"==typeof t?t:{}}}_typeCheckConfig(t,e=this.constructor.DefaultType){for(const[s,o]of Object.entries(e)){const e=t[s],r=n(e)?"element":null==(i=e)?`${i}`:Object.prototype.toString.call(i).match(/\s([a-z]+)/i)[1].toLowerCase();if(!new RegExp(o).test(r))throw new TypeError(`${this.constructor.NAME.toUpperCase()}: Option "${s}" provided type "${r}" but expected type "${o}".`)}var i}}class W extends ${constructor(t,e){super(),(t=s(t))&&(this._element=t,this._config=this._getConfig(e),j.set(this._element,this.constructor.DATA_KEY,this))}dispose(){j.remove(this._element,this.constructor.DATA_KEY),I.off(this._element,this.constructor.EVENT_KEY);for(const t of Object.getOwnPropertyNames(this))this[t]=null}_queueCallback(t,e,i=!0){m(t,e,i)}_getConfig(t){return t=this._mergeConfigObj(t,this._element),t=this._configAfterMerge(t),this._typeCheckConfig(t),t}static getInstance(t){return j.get(s(t),this.DATA_KEY)}static getOrCreateInstance(t,e={}){return this.getInstance(t)||new this(t,"object"==typeof e?e:null)}static get VERSION(){return"5.3.0-alpha2"}static get DATA_KEY(){return`bs.${this.NAME}`}static get EVENT_KEY(){return`.${this.DATA_KEY}`}static eventName(t){return`${t}${this.EVENT_KEY}`}}const B=t=>{let i=t.getAttribute("data-bs-target");if(!i||"#"===i){let e=t.getAttribute("href");if(!e||!e.includes("#")&&!e.startsWith("."))return null;e.includes("#")&&!e.startsWith("#")&&(e=`#${e.split("#")[1]}`),i=e&&"#"!==e?e.trim():null}return e(i)},z={find:(t,e=document.documentElement)=>[].concat(...Element.prototype.querySelectorAll.call(e,t)),findOne:(t,e=document.documentElement)=>Element.prototype.querySelector.call(e,t),children:(t,e)=>[].concat(...t.children).filter((t=>t.matches(e))),parents(t,e){const i=[];let n=t.parentNode.closest(e);for(;n;)i.push(n),n=n.parentNode.closest(e);return i},prev(t,e){let i=t.previousElementSibling;for(;i;){if(i.matches(e))return[i];i=i.previousElementSibling}return[]},next(t,e){let i=t.nextElementSibling;for(;i;){if(i.matches(e))return[i];i=i.nextElementSibling}return[]},focusableChildren(t){const e=["a","button","input","textarea","select","details","[tabindex]",'[contenteditable="true"]'].map((t=>`${t}:not([tabindex^="-"])`)).join(",");return this.find(e,t).filter((t=>!r(t)&&o(t)))},getSelectorFromElement(t){const e=B(t);return e&&z.findOne(e)?e:null},getElementFromSelector(t){const e=B(t);return e?z.findOne(e):null},getMultipleElementsFromSelector(t){const e=B(t);return e?z.find(e):[]}},R=(t,e="hide")=>{const i=`click.dismiss${t.EVENT_KEY}`,n=t.NAME;I.on(document,i,`[data-bs-dismiss="${n}"]`,(function(i){if(["A","AREA"].includes(this.tagName)&&i.preventDefault(),r(this))return;const s=z.getElementFromSelector(this)||this.closest(`.${n}`);t.getOrCreateInstance(s)[e]()}))};class q extends W{static get NAME(){return"alert"}close(){if(I.trigger(this._element,"close.bs.alert").defaultPrevented)return;this._element.classList.remove("show");const t=this._element.classList.contains("fade");this._queueCallback((()=>this._destroyElement()),this._element,t)}_destroyElement(){this._element.remove(),I.trigger(this._element,"closed.bs.alert"),this.dispose()}static jQueryInterface(t){return this.each((function(){const e=q.getOrCreateInstance(this);if("string"==typeof t){if(void 0===e[t]||t.startsWith("_")||"constructor"===t)throw new TypeError(`No method named "${t}"`);e[t](this)}}))}}R(q,"close"),f(q);const V='[data-bs-toggle="button"]';class K extends W{static get NAME(){return"button"}toggle(){this._element.setAttribute("aria-pressed",this._element.classList.toggle("active"))}static jQueryInterface(t){return this.each((function(){const e=K.getOrCreateInstance(this);"toggle"===t&&e[t]()}))}}I.on(document,"click.bs.button.data-api",V,(t=>{t.preventDefault();const e=t.target.closest(V);K.getOrCreateInstance(e).toggle()})),f(K);const Q={endCallback:null,leftCallback:null,rightCallback:null},X={endCallback:"(function|null)",leftCallback:"(function|null)",rightCallback:"(function|null)"};class Y extends ${constructor(t,e){super(),this._element=t,t&&Y.isSupported()&&(this._config=this._getConfig(e),this._deltaX=0,this._supportPointerEvents=Boolean(window.PointerEvent),this._initEvents())}static get Default(){return Q}static get DefaultType(){return X}static get NAME(){return"swipe"}dispose(){I.off(this._element,".bs.swipe")}_start(t){this._supportPointerEvents?this._eventIsPointerPenTouch(t)&&(this._deltaX=t.clientX):this._deltaX=t.touches[0].clientX}_end(t){this._eventIsPointerPenTouch(t)&&(this._deltaX=t.clientX-this._deltaX),this._handleSwipe(),p(this._config.endCallback)}_move(t){this._deltaX=t.touches&&t.touches.length>1?0:t.touches[0].clientX-this._deltaX}_handleSwipe(){const t=Math.abs(this._deltaX);if(t<=40)return;const e=t/this._deltaX;this._deltaX=0,e&&p(e>0?this._config.rightCallback:this._config.leftCallback)}_initEvents(){this._supportPointerEvents?(I.on(this._element,"pointerdown.bs.swipe",(t=>this._start(t))),I.on(this._element,"pointerup.bs.swipe",(t=>this._end(t))),this._element.classList.add("pointer-event")):(I.on(this._element,"touchstart.bs.swipe",(t=>this._start(t))),I.on(this._element,"touchmove.bs.swipe",(t=>this._move(t))),I.on(this._element,"touchend.bs.swipe",(t=>this._end(t))))}_eventIsPointerPenTouch(t){return this._supportPointerEvents&&("pen"===t.pointerType||"touch"===t.pointerType)}static isSupported(){return"ontouchstart"in document.documentElement||navigator.maxTouchPoints>0}}const U="next",G="prev",J="left",Z="right",tt="slid.bs.carousel",et="carousel",it="active",nt={ArrowLeft:Z,ArrowRight:J},st={interval:5e3,keyboard:!0,pause:"hover",ride:!1,touch:!0,wrap:!0},ot={interval:"(number|boolean)",keyboard:"boolean",pause:"(string|boolean)",ride:"(boolean|string)",touch:"boolean",wrap:"boolean"};class rt extends W{constructor(t,e){super(t,e),this._interval=null,this._activeElement=null,this._isSliding=!1,this.touchTimeout=null,this._swipeHelper=null,this._indicatorsElement=z.findOne(".carousel-indicators",this._element),this._addEventListeners(),this._config.ride===et&&this.cycle()}static get Default(){return st}static get DefaultType(){return ot}static get NAME(){return"carousel"}next(){this._slide(U)}nextWhenVisible(){!document.hidden&&o(this._element)&&this.next()}prev(){this._slide(G)}pause(){this._isSliding&&i(this._element),this._clearInterval()}cycle(){this._clearInterval(),this._updateInterval(),this._interval=setInterval((()=>this.nextWhenVisible()),this._config.interval)}_maybeEnableCycle(){this._config.ride&&(this._isSliding?I.one(this._element,tt,(()=>this.cycle())):this.cycle())}to(t){const e=this._getItems();if(t>e.length-1||t<0)return;if(this._isSliding)return void I.one(this._element,tt,(()=>this.to(t)));const i=this._getItemIndex(this._getActive());if(i===t)return;const n=t>i?U:G;this._slide(n,e[t])}dispose(){this._swipeHelper&&this._swipeHelper.dispose(),super.dispose()}_configAfterMerge(t){return t.defaultInterval=t.interval,t}_addEventListeners(){this._config.keyboard&&I.on(this._element,"keydown.bs.carousel",(t=>this._keydown(t))),"hover"===this._config.pause&&(I.on(this._element,"mouseenter.bs.carousel",(()=>this.pause())),I.on(this._element,"mouseleave.bs.carousel",(()=>this._maybeEnableCycle()))),this._config.touch&&Y.isSupported()&&this._addTouchEventListeners()}_addTouchEventListeners(){for(const t of z.find(".carousel-item img",this._element))I.on(t,"dragstart.bs.carousel",(t=>t.preventDefault()));const t={leftCallback:()=>this._slide(this._directionToOrder(J)),rightCallback:()=>this._slide(this._directionToOrder(Z)),endCallback:()=>{"hover"===this._config.pause&&(this.pause(),this.touchTimeout&&clearTimeout(this.touchTimeout),this.touchTimeout=setTimeout((()=>this._maybeEnableCycle()),500+this._config.interval))}};this._swipeHelper=new Y(this._element,t)}_keydown(t){if(/input|textarea/i.test(t.target.tagName))return;const e=nt[t.key];e&&(t.preventDefault(),this._slide(this._directionToOrder(e)))}_getItemIndex(t){return this._getItems().indexOf(t)}_setActiveIndicatorElement(t){if(!this._indicatorsElement)return;const e=z.findOne(".active",this._indicatorsElement);e.classList.remove(it),e.removeAttribute("aria-current");const i=z.findOne(`[data-bs-slide-to="${t}"]`,this._indicatorsElement);i&&(i.classList.add(it),i.setAttribute("aria-current","true"))}_updateInterval(){const t=this._activeElement||this._getActive();if(!t)return;const e=Number.parseInt(t.getAttribute("data-bs-interval"),10);this._config.interval=e||this._config.defaultInterval}_slide(t,e=null){if(this._isSliding)return;const i=this._getActive(),n=t===U,s=e||g(this._getItems(),i,n,this._config.wrap);if(s===i)return;const o=this._getItemIndex(s),r=e=>I.trigger(this._element,e,{relatedTarget:s,direction:this._orderToDirection(t),from:this._getItemIndex(i),to:o});if(r("slide.bs.carousel").defaultPrevented)return;if(!i||!s)return;const a=Boolean(this._interval);this.pause(),this._isSliding=!0,this._setActiveIndicatorElement(o),this._activeElement=s;const l=n?"carousel-item-start":"carousel-item-end",h=n?"carousel-item-next":"carousel-item-prev";s.classList.add(h),c(s),i.classList.add(l),s.classList.add(l),this._queueCallback((()=>{s.classList.remove(l,h),s.classList.add(it),i.classList.remove(it,h,l),this._isSliding=!1,r(tt)}),i,this._isAnimated()),a&&this.cycle()}_isAnimated(){return this._element.classList.contains("slide")}_getActive(){return z.findOne(".active.carousel-item",this._element)}_getItems(){return z.find(".carousel-item",this._element)}_clearInterval(){this._interval&&(clearInterval(this._interval),this._interval=null)}_directionToOrder(t){return u()?t===J?G:U:t===J?U:G}_orderToDirection(t){return u()?t===G?J:Z:t===G?Z:J}static jQueryInterface(t){return this.each((function(){const e=rt.getOrCreateInstance(this,t);if("number"!=typeof t){if("string"==typeof t){if(void 0===e[t]||t.startsWith("_")||"constructor"===t)throw new TypeError(`No method named "${t}"`);e[t]()}}else e.to(t)}))}}I.on(document,"click.bs.carousel.data-api","[data-bs-slide], [data-bs-slide-to]",(function(t){const e=z.getElementFromSelector(this);if(!e||!e.classList.contains(et))return;t.preventDefault();const i=rt.getOrCreateInstance(e),n=this.getAttribute("data-bs-slide-to");return n?(i.to(n),void i._maybeEnableCycle()):"next"===H.getDataAttribute(this,"slide")?(i.next(),void i._maybeEnableCycle()):(i.prev(),void i._maybeEnableCycle())})),I.on(window,"load.bs.carousel.data-api",(()=>{const t=z.find('[data-bs-ride="carousel"]');for(const e of t)rt.getOrCreateInstance(e)})),f(rt);const at="show",lt="collapse",ct="collapsing",ht='[data-bs-toggle="collapse"]',dt={parent:null,toggle:!0},ut={parent:"(null|element)",toggle:"boolean"};class ft extends W{constructor(t,e){super(t,e),this._isTransitioning=!1,this._triggerArray=[];const i=z.find(ht);for(const t of i){const e=z.getSelectorFromElement(t),i=z.find(e).filter((t=>t===this._element));null!==e&&i.length&&this._triggerArray.push(t)}this._initializeChildren(),this._config.parent||this._addAriaAndCollapsedClass(this._triggerArray,this._isShown()),this._config.toggle&&this.toggle()}static get Default(){return dt}static get DefaultType(){return ut}static get NAME(){return"collapse"}toggle(){this._isShown()?this.hide():this.show()}show(){if(this._isTransitioning||this._isShown())return;let t=[];if(this._config.parent&&(t=this._getFirstLevelChildren(".collapse.show, .collapse.collapsing").filter((t=>t!==this._element)).map((t=>ft.getOrCreateInstance(t,{toggle:!1})))),t.length&&t[0]._isTransitioning)return;if(I.trigger(this._element,"show.bs.collapse").defaultPrevented)return;for(const e of t)e.hide();const e=this._getDimension();this._element.classList.remove(lt),this._element.classList.add(ct),this._element.style[e]=0,this._addAriaAndCollapsedClass(this._triggerArray,!0),this._isTransitioning=!0;const i=`scroll${e[0].toUpperCase()+e.slice(1)}`;this._queueCallback((()=>{this._isTransitioning=!1,this._element.classList.remove(ct),this._element.classList.add(lt,at),this._element.style[e]="",I.trigger(this._element,"shown.bs.collapse")}),this._element,!0),this._element.style[e]=`${this._element[i]}px`}hide(){if(this._isTransitioning||!this._isShown())return;if(I.trigger(this._element,"hide.bs.collapse").defaultPrevented)return;const t=this._getDimension();this._element.style[t]=`${this._element.getBoundingClientRect()[t]}px`,c(this._element),this._element.classList.add(ct),this._element.classList.remove(lt,at);for(const t of this._triggerArray){const e=z.getElementFromSelector(t);e&&!this._isShown(e)&&this._addAriaAndCollapsedClass([t],!1)}this._isTransitioning=!0,this._element.style[t]="",this._queueCallback((()=>{this._isTransitioning=!1,this._element.classList.remove(ct),this._element.classList.add(lt),I.trigger(this._element,"hidden.bs.collapse")}),this._element,!0)}_isShown(t=this._element){return t.classList.contains(at)}_configAfterMerge(t){return t.toggle=Boolean(t.toggle),t.parent=s(t.parent),t}_getDimension(){return this._element.classList.contains("collapse-horizontal")?"width":"height"}_initializeChildren(){if(!this._config.parent)return;const t=this._getFirstLevelChildren(ht);for(const e of t){const t=z.getElementFromSelector(e);t&&this._addAriaAndCollapsedClass([e],this._isShown(t))}}_getFirstLevelChildren(t){const e=z.find(":scope .collapse .collapse",this._config.parent);return z.find(t,this._config.parent).filter((t=>!e.includes(t)))}_addAriaAndCollapsedClass(t,e){if(t.length)for(const i of t)i.classList.toggle("collapsed",!e),i.setAttribute("aria-expanded",e)}static jQueryInterface(t){const e={};return"string"==typeof t&&/show|hide/.test(t)&&(e.toggle=!1),this.each((function(){const i=ft.getOrCreateInstance(this,e);if("string"==typeof t){if(void 0===i[t])throw new TypeError(`No method named "${t}"`);i[t]()}}))}}I.on(document,"click.bs.collapse.data-api",ht,(function(t){("A"===t.target.tagName||t.delegateTarget&&"A"===t.delegateTarget.tagName)&&t.preventDefault();for(const t of z.getMultipleElementsFromSelector(this))ft.getOrCreateInstance(t,{toggle:!1}).toggle()})),f(ft);var pt="top",mt="bottom",gt="right",_t="left",bt="auto",vt=[pt,mt,gt,_t],yt="start",wt="end",At="clippingParents",Et="viewport",Tt="popper",Ct="reference",Ot=vt.reduce((function(t,e){return t.concat([e+"-"+yt,e+"-"+wt])}),[]),xt=[].concat(vt,[bt]).reduce((function(t,e){return t.concat([e,e+"-"+yt,e+"-"+wt])}),[]),kt="beforeRead",Lt="read",St="afterRead",Dt="beforeMain",It="main",Nt="afterMain",Pt="beforeWrite",jt="write",Mt="afterWrite",Ft=[kt,Lt,St,Dt,It,Nt,Pt,jt,Mt];function Ht(t){return t?(t.nodeName||"").toLowerCase():null}function $t(t){if(null==t)return window;if("[object Window]"!==t.toString()){var e=t.ownerDocument;return e&&e.defaultView||window}return t}function Wt(t){return t instanceof $t(t).Element||t instanceof Element}function Bt(t){return t instanceof $t(t).HTMLElement||t instanceof HTMLElement}function zt(t){return"undefined"!=typeof ShadowRoot&&(t instanceof $t(t).ShadowRoot||t instanceof ShadowRoot)}const Rt={name:"applyStyles",enabled:!0,phase:"write",fn:function(t){var e=t.state;Object.keys(e.elements).forEach((function(t){var i=e.styles[t]||{},n=e.attributes[t]||{},s=e.elements[t];Bt(s)&&Ht(s)&&(Object.assign(s.style,i),Object.keys(n).forEach((function(t){var e=n[t];!1===e?s.removeAttribute(t):s.setAttribute(t,!0===e?"":e)})))}))},effect:function(t){var e=t.state,i={popper:{position:e.options.strategy,left:"0",top:"0",margin:"0"},arrow:{position:"absolute"},reference:{}};return Object.assign(e.elements.popper.style,i.popper),e.styles=i,e.elements.arrow&&Object.assign(e.elements.arrow.style,i.arrow),function(){Object.keys(e.elements).forEach((function(t){var n=e.elements[t],s=e.attributes[t]||{},o=Object.keys(e.styles.hasOwnProperty(t)?e.styles[t]:i[t]).reduce((function(t,e){return t[e]="",t}),{});Bt(n)&&Ht(n)&&(Object.assign(n.style,o),Object.keys(s).forEach((function(t){n.removeAttribute(t)})))}))}},requires:["computeStyles"]};function qt(t){return t.split("-")[0]}var Vt=Math.max,Kt=Math.min,Qt=Math.round;function Xt(){var t=navigator.userAgentData;return null!=t&&t.brands?t.brands.map((function(t){return t.brand+"/"+t.version})).join(" "):navigator.userAgent}function Yt(){return!/^((?!chrome|android).)*safari/i.test(Xt())}function Ut(t,e,i){void 0===e&&(e=!1),void 0===i&&(i=!1);var n=t.getBoundingClientRect(),s=1,o=1;e&&Bt(t)&&(s=t.offsetWidth>0&&Qt(n.width)/t.offsetWidth||1,o=t.offsetHeight>0&&Qt(n.height)/t.offsetHeight||1);var r=(Wt(t)?$t(t):window).visualViewport,a=!Yt()&&i,l=(n.left+(a&&r?r.offsetLeft:0))/s,c=(n.top+(a&&r?r.offsetTop:0))/o,h=n.width/s,d=n.height/o;return{width:h,height:d,top:c,right:l+h,bottom:c+d,left:l,x:l,y:c}}function Gt(t){var e=Ut(t),i=t.offsetWidth,n=t.offsetHeight;return Math.abs(e.width-i)<=1&&(i=e.width),Math.abs(e.height-n)<=1&&(n=e.height),{x:t.offsetLeft,y:t.offsetTop,width:i,height:n}}function Jt(t,e){var i=e.getRootNode&&e.getRootNode();if(t.contains(e))return!0;if(i&&zt(i)){var n=e;do{if(n&&t.isSameNode(n))return!0;n=n.parentNode||n.host}while(n)}return!1}function Zt(t){return $t(t).getComputedStyle(t)}function te(t){return["table","td","th"].indexOf(Ht(t))>=0}function ee(t){return((Wt(t)?t.ownerDocument:t.document)||window.document).documentElement}function ie(t){return"html"===Ht(t)?t:t.assignedSlot||t.parentNode||(zt(t)?t.host:null)||ee(t)}function ne(t){return Bt(t)&&"fixed"!==Zt(t).position?t.offsetParent:null}function se(t){for(var e=$t(t),i=ne(t);i&&te(i)&&"static"===Zt(i).position;)i=ne(i);return i&&("html"===Ht(i)||"body"===Ht(i)&&"static"===Zt(i).position)?e:i||function(t){var e=/firefox/i.test(Xt());if(/Trident/i.test(Xt())&&Bt(t)&&"fixed"===Zt(t).position)return null;var i=ie(t);for(zt(i)&&(i=i.host);Bt(i)&&["html","body"].indexOf(Ht(i))<0;){var n=Zt(i);if("none"!==n.transform||"none"!==n.perspective||"paint"===n.contain||-1!==["transform","perspective"].indexOf(n.willChange)||e&&"filter"===n.willChange||e&&n.filter&&"none"!==n.filter)return i;i=i.parentNode}return null}(t)||e}function oe(t){return["top","bottom"].indexOf(t)>=0?"x":"y"}function re(t,e,i){return Vt(t,Kt(e,i))}function ae(t){return Object.assign({},{top:0,right:0,bottom:0,left:0},t)}function le(t,e){return e.reduce((function(e,i){return e[i]=t,e}),{})}const ce={name:"arrow",enabled:!0,phase:"main",fn:function(t){var e,i=t.state,n=t.name,s=t.options,o=i.elements.arrow,r=i.modifiersData.popperOffsets,a=qt(i.placement),l=oe(a),c=[_t,gt].indexOf(a)>=0?"height":"width";if(o&&r){var h=function(t,e){return ae("number"!=typeof(t="function"==typeof t?t(Object.assign({},e.rects,{placement:e.placement})):t)?t:le(t,vt))}(s.padding,i),d=Gt(o),u="y"===l?pt:_t,f="y"===l?mt:gt,p=i.rects.reference[c]+i.rects.reference[l]-r[l]-i.rects.popper[c],m=r[l]-i.rects.reference[l],g=se(o),_=g?"y"===l?g.clientHeight||0:g.clientWidth||0:0,b=p/2-m/2,v=h[u],y=_-d[c]-h[f],w=_/2-d[c]/2+b,A=re(v,w,y),E=l;i.modifiersData[n]=((e={})[E]=A,e.centerOffset=A-w,e)}},effect:function(t){var e=t.state,i=t.options.element,n=void 0===i?"[data-popper-arrow]":i;null!=n&&("string"!=typeof n||(n=e.elements.popper.querySelector(n)))&&Jt(e.elements.popper,n)&&(e.elements.arrow=n)},requires:["popperOffsets"],requiresIfExists:["preventOverflow"]};function he(t){return t.split("-")[1]}var de={top:"auto",right:"auto",bottom:"auto",left:"auto"};function ue(t){var e,i=t.popper,n=t.popperRect,s=t.placement,o=t.variation,r=t.offsets,a=t.position,l=t.gpuAcceleration,c=t.adaptive,h=t.roundOffsets,d=t.isFixed,u=r.x,f=void 0===u?0:u,p=r.y,m=void 0===p?0:p,g="function"==typeof h?h({x:f,y:m}):{x:f,y:m};f=g.x,m=g.y;var _=r.hasOwnProperty("x"),b=r.hasOwnProperty("y"),v=_t,y=pt,w=window;if(c){var A=se(i),E="clientHeight",T="clientWidth";A===$t(i)&&"static"!==Zt(A=ee(i)).position&&"absolute"===a&&(E="scrollHeight",T="scrollWidth"),(s===pt||(s===_t||s===gt)&&o===wt)&&(y=mt,m-=(d&&A===w&&w.visualViewport?w.visualViewport.height:A[E])-n.height,m*=l?1:-1),s!==_t&&(s!==pt&&s!==mt||o!==wt)||(v=gt,f-=(d&&A===w&&w.visualViewport?w.visualViewport.width:A[T])-n.width,f*=l?1:-1)}var C,O=Object.assign({position:a},c&&de),x=!0===h?function(t){var e=t.x,i=t.y,n=window.devicePixelRatio||1;return{x:Qt(e*n)/n||0,y:Qt(i*n)/n||0}}({x:f,y:m}):{x:f,y:m};return f=x.x,m=x.y,l?Object.assign({},O,((C={})[y]=b?"0":"",C[v]=_?"0":"",C.transform=(w.devicePixelRatio||1)<=1?"translate("+f+"px, "+m+"px)":"translate3d("+f+"px, "+m+"px, 0)",C)):Object.assign({},O,((e={})[y]=b?m+"px":"",e[v]=_?f+"px":"",e.transform="",e))}const fe={name:"computeStyles",enabled:!0,phase:"beforeWrite",fn:function(t){var e=t.state,i=t.options,n=i.gpuAcceleration,s=void 0===n||n,o=i.adaptive,r=void 0===o||o,a=i.roundOffsets,l=void 0===a||a,c={placement:qt(e.placement),variation:he(e.placement),popper:e.elements.popper,popperRect:e.rects.popper,gpuAcceleration:s,isFixed:"fixed"===e.options.strategy};null!=e.modifiersData.popperOffsets&&(e.styles.popper=Object.assign({},e.styles.popper,ue(Object.assign({},c,{offsets:e.modifiersData.popperOffsets,position:e.options.strategy,adaptive:r,roundOffsets:l})))),null!=e.modifiersData.arrow&&(e.styles.arrow=Object.assign({},e.styles.arrow,ue(Object.assign({},c,{offsets:e.modifiersData.arrow,position:"absolute",adaptive:!1,roundOffsets:l})))),e.attributes.popper=Object.assign({},e.attributes.popper,{"data-popper-placement":e.placement})},data:{}};var pe={passive:!0};const me={name:"eventListeners",enabled:!0,phase:"write",fn:function(){},effect:function(t){var e=t.state,i=t.instance,n=t.options,s=n.scroll,o=void 0===s||s,r=n.resize,a=void 0===r||r,l=$t(e.elements.popper),c=[].concat(e.scrollParents.reference,e.scrollParents.popper);return o&&c.forEach((function(t){t.addEventListener("scroll",i.update,pe)})),a&&l.addEventListener("resize",i.update,pe),function(){o&&c.forEach((function(t){t.removeEventListener("scroll",i.update,pe)})),a&&l.removeEventListener("resize",i.update,pe)}},data:{}};var ge={left:"right",right:"left",bottom:"top",top:"bottom"};function _e(t){return t.replace(/left|right|bottom|top/g,(function(t){return ge[t]}))}var be={start:"end",end:"start"};function ve(t){return t.replace(/start|end/g,(function(t){return be[t]}))}function ye(t){var e=$t(t);return{scrollLeft:e.pageXOffset,scrollTop:e.pageYOffset}}function we(t){return Ut(ee(t)).left+ye(t).scrollLeft}function Ae(t){var e=Zt(t),i=e.overflow,n=e.overflowX,s=e.overflowY;return/auto|scroll|overlay|hidden/.test(i+s+n)}function Ee(t){return["html","body","#document"].indexOf(Ht(t))>=0?t.ownerDocument.body:Bt(t)&&Ae(t)?t:Ee(ie(t))}function Te(t,e){var i;void 0===e&&(e=[]);var n=Ee(t),s=n===(null==(i=t.ownerDocument)?void 0:i.body),o=$t(n),r=s?[o].concat(o.visualViewport||[],Ae(n)?n:[]):n,a=e.concat(r);return s?a:a.concat(Te(ie(r)))}function Ce(t){return Object.assign({},t,{left:t.x,top:t.y,right:t.x+t.width,bottom:t.y+t.height})}function Oe(t,e,i){return e===Et?Ce(function(t,e){var i=$t(t),n=ee(t),s=i.visualViewport,o=n.clientWidth,r=n.clientHeight,a=0,l=0;if(s){o=s.width,r=s.height;var c=Yt();(c||!c&&"fixed"===e)&&(a=s.offsetLeft,l=s.offsetTop)}return{width:o,height:r,x:a+we(t),y:l}}(t,i)):Wt(e)?function(t,e){var i=Ut(t,!1,"fixed"===e);return i.top=i.top+t.clientTop,i.left=i.left+t.clientLeft,i.bottom=i.top+t.clientHeight,i.right=i.left+t.clientWidth,i.width=t.clientWidth,i.height=t.clientHeight,i.x=i.left,i.y=i.top,i}(e,i):Ce(function(t){var e,i=ee(t),n=ye(t),s=null==(e=t.ownerDocument)?void 0:e.body,o=Vt(i.scrollWidth,i.clientWidth,s?s.scrollWidth:0,s?s.clientWidth:0),r=Vt(i.scrollHeight,i.clientHeight,s?s.scrollHeight:0,s?s.clientHeight:0),a=-n.scrollLeft+we(t),l=-n.scrollTop;return"rtl"===Zt(s||i).direction&&(a+=Vt(i.clientWidth,s?s.clientWidth:0)-o),{width:o,height:r,x:a,y:l}}(ee(t)))}function xe(t){var e,i=t.reference,n=t.element,s=t.placement,o=s?qt(s):null,r=s?he(s):null,a=i.x+i.width/2-n.width/2,l=i.y+i.height/2-n.height/2;switch(o){case pt:e={x:a,y:i.y-n.height};break;case mt:e={x:a,y:i.y+i.height};break;case gt:e={x:i.x+i.width,y:l};break;case _t:e={x:i.x-n.width,y:l};break;default:e={x:i.x,y:i.y}}var c=o?oe(o):null;if(null!=c){var h="y"===c?"height":"width";switch(r){case yt:e[c]=e[c]-(i[h]/2-n[h]/2);break;case wt:e[c]=e[c]+(i[h]/2-n[h]/2)}}return e}function ke(t,e){void 0===e&&(e={});var i=e,n=i.placement,s=void 0===n?t.placement:n,o=i.strategy,r=void 0===o?t.strategy:o,a=i.boundary,l=void 0===a?At:a,c=i.rootBoundary,h=void 0===c?Et:c,d=i.elementContext,u=void 0===d?Tt:d,f=i.altBoundary,p=void 0!==f&&f,m=i.padding,g=void 0===m?0:m,_=ae("number"!=typeof g?g:le(g,vt)),b=u===Tt?Ct:Tt,v=t.rects.popper,y=t.elements[p?b:u],w=function(t,e,i,n){var s="clippingParents"===e?function(t){var e=Te(ie(t)),i=["absolute","fixed"].indexOf(Zt(t).position)>=0&&Bt(t)?se(t):t;return Wt(i)?e.filter((function(t){return Wt(t)&&Jt(t,i)&&"body"!==Ht(t)})):[]}(t):[].concat(e),o=[].concat(s,[i]),r=o[0],a=o.reduce((function(e,i){var s=Oe(t,i,n);return e.top=Vt(s.top,e.top),e.right=Kt(s.right,e.right),e.bottom=Kt(s.bottom,e.bottom),e.left=Vt(s.left,e.left),e}),Oe(t,r,n));return a.width=a.right-a.left,a.height=a.bottom-a.top,a.x=a.left,a.y=a.top,a}(Wt(y)?y:y.contextElement||ee(t.elements.popper),l,h,r),A=Ut(t.elements.reference),E=xe({reference:A,element:v,strategy:"absolute",placement:s}),T=Ce(Object.assign({},v,E)),C=u===Tt?T:A,O={top:w.top-C.top+_.top,bottom:C.bottom-w.bottom+_.bottom,left:w.left-C.left+_.left,right:C.right-w.right+_.right},x=t.modifiersData.offset;if(u===Tt&&x){var k=x[s];Object.keys(O).forEach((function(t){var e=[gt,mt].indexOf(t)>=0?1:-1,i=[pt,mt].indexOf(t)>=0?"y":"x";O[t]+=k[i]*e}))}return O}function Le(t,e){void 0===e&&(e={});var i=e,n=i.placement,s=i.boundary,o=i.rootBoundary,r=i.padding,a=i.flipVariations,l=i.allowedAutoPlacements,c=void 0===l?xt:l,h=he(n),d=h?a?Ot:Ot.filter((function(t){return he(t)===h})):vt,u=d.filter((function(t){return c.indexOf(t)>=0}));0===u.length&&(u=d);var f=u.reduce((function(e,i){return e[i]=ke(t,{placement:i,boundary:s,rootBoundary:o,padding:r})[qt(i)],e}),{});return Object.keys(f).sort((function(t,e){return f[t]-f[e]}))}const Se={name:"flip",enabled:!0,phase:"main",fn:function(t){var e=t.state,i=t.options,n=t.name;if(!e.modifiersData[n]._skip){for(var s=i.mainAxis,o=void 0===s||s,r=i.altAxis,a=void 0===r||r,l=i.fallbackPlacements,c=i.padding,h=i.boundary,d=i.rootBoundary,u=i.altBoundary,f=i.flipVariations,p=void 0===f||f,m=i.allowedAutoPlacements,g=e.options.placement,_=qt(g),b=l||(_!==g&&p?function(t){if(qt(t)===bt)return[];var e=_e(t);return[ve(t),e,ve(e)]}(g):[_e(g)]),v=[g].concat(b).reduce((function(t,i){return t.concat(qt(i)===bt?Le(e,{placement:i,boundary:h,rootBoundary:d,padding:c,flipVariations:p,allowedAutoPlacements:m}):i)}),[]),y=e.rects.reference,w=e.rects.popper,A=new Map,E=!0,T=v[0],C=0;C=0,S=L?"width":"height",D=ke(e,{placement:O,boundary:h,rootBoundary:d,altBoundary:u,padding:c}),I=L?k?gt:_t:k?mt:pt;y[S]>w[S]&&(I=_e(I));var N=_e(I),P=[];if(o&&P.push(D[x]<=0),a&&P.push(D[I]<=0,D[N]<=0),P.every((function(t){return t}))){T=O,E=!1;break}A.set(O,P)}if(E)for(var j=function(t){var e=v.find((function(e){var i=A.get(e);if(i)return i.slice(0,t).every((function(t){return t}))}));if(e)return T=e,"break"},M=p?3:1;M>0&&"break"!==j(M);M--);e.placement!==T&&(e.modifiersData[n]._skip=!0,e.placement=T,e.reset=!0)}},requiresIfExists:["offset"],data:{_skip:!1}};function De(t,e,i){return void 0===i&&(i={x:0,y:0}),{top:t.top-e.height-i.y,right:t.right-e.width+i.x,bottom:t.bottom-e.height+i.y,left:t.left-e.width-i.x}}function Ie(t){return[pt,gt,mt,_t].some((function(e){return t[e]>=0}))}const Ne={name:"hide",enabled:!0,phase:"main",requiresIfExists:["preventOverflow"],fn:function(t){var e=t.state,i=t.name,n=e.rects.reference,s=e.rects.popper,o=e.modifiersData.preventOverflow,r=ke(e,{elementContext:"reference"}),a=ke(e,{altBoundary:!0}),l=De(r,n),c=De(a,s,o),h=Ie(l),d=Ie(c);e.modifiersData[i]={referenceClippingOffsets:l,popperEscapeOffsets:c,isReferenceHidden:h,hasPopperEscaped:d},e.attributes.popper=Object.assign({},e.attributes.popper,{"data-popper-reference-hidden":h,"data-popper-escaped":d})}},Pe={name:"offset",enabled:!0,phase:"main",requires:["popperOffsets"],fn:function(t){var e=t.state,i=t.options,n=t.name,s=i.offset,o=void 0===s?[0,0]:s,r=xt.reduce((function(t,i){return t[i]=function(t,e,i){var n=qt(t),s=[_t,pt].indexOf(n)>=0?-1:1,o="function"==typeof i?i(Object.assign({},e,{placement:t})):i,r=o[0],a=o[1];return r=r||0,a=(a||0)*s,[_t,gt].indexOf(n)>=0?{x:a,y:r}:{x:r,y:a}}(i,e.rects,o),t}),{}),a=r[e.placement],l=a.x,c=a.y;null!=e.modifiersData.popperOffsets&&(e.modifiersData.popperOffsets.x+=l,e.modifiersData.popperOffsets.y+=c),e.modifiersData[n]=r}},je={name:"popperOffsets",enabled:!0,phase:"read",fn:function(t){var e=t.state,i=t.name;e.modifiersData[i]=xe({reference:e.rects.reference,element:e.rects.popper,strategy:"absolute",placement:e.placement})},data:{}},Me={name:"preventOverflow",enabled:!0,phase:"main",fn:function(t){var e=t.state,i=t.options,n=t.name,s=i.mainAxis,o=void 0===s||s,r=i.altAxis,a=void 0!==r&&r,l=i.boundary,c=i.rootBoundary,h=i.altBoundary,d=i.padding,u=i.tether,f=void 0===u||u,p=i.tetherOffset,m=void 0===p?0:p,g=ke(e,{boundary:l,rootBoundary:c,padding:d,altBoundary:h}),_=qt(e.placement),b=he(e.placement),v=!b,y=oe(_),w="x"===y?"y":"x",A=e.modifiersData.popperOffsets,E=e.rects.reference,T=e.rects.popper,C="function"==typeof m?m(Object.assign({},e.rects,{placement:e.placement})):m,O="number"==typeof C?{mainAxis:C,altAxis:C}:Object.assign({mainAxis:0,altAxis:0},C),x=e.modifiersData.offset?e.modifiersData.offset[e.placement]:null,k={x:0,y:0};if(A){if(o){var L,S="y"===y?pt:_t,D="y"===y?mt:gt,I="y"===y?"height":"width",N=A[y],P=N+g[S],j=N-g[D],M=f?-T[I]/2:0,F=b===yt?E[I]:T[I],H=b===yt?-T[I]:-E[I],$=e.elements.arrow,W=f&&$?Gt($):{width:0,height:0},B=e.modifiersData["arrow#persistent"]?e.modifiersData["arrow#persistent"].padding:{top:0,right:0,bottom:0,left:0},z=B[S],R=B[D],q=re(0,E[I],W[I]),V=v?E[I]/2-M-q-z-O.mainAxis:F-q-z-O.mainAxis,K=v?-E[I]/2+M+q+R+O.mainAxis:H+q+R+O.mainAxis,Q=e.elements.arrow&&se(e.elements.arrow),X=Q?"y"===y?Q.clientTop||0:Q.clientLeft||0:0,Y=null!=(L=null==x?void 0:x[y])?L:0,U=N+K-Y,G=re(f?Kt(P,N+V-Y-X):P,N,f?Vt(j,U):j);A[y]=G,k[y]=G-N}if(a){var J,Z="x"===y?pt:_t,tt="x"===y?mt:gt,et=A[w],it="y"===w?"height":"width",nt=et+g[Z],st=et-g[tt],ot=-1!==[pt,_t].indexOf(_),rt=null!=(J=null==x?void 0:x[w])?J:0,at=ot?nt:et-E[it]-T[it]-rt+O.altAxis,lt=ot?et+E[it]+T[it]-rt-O.altAxis:st,ct=f&&ot?function(t,e,i){var n=re(t,e,i);return n>i?i:n}(at,et,lt):re(f?at:nt,et,f?lt:st);A[w]=ct,k[w]=ct-et}e.modifiersData[n]=k}},requiresIfExists:["offset"]};function Fe(t,e,i){void 0===i&&(i=!1);var n,s,o=Bt(e),r=Bt(e)&&function(t){var e=t.getBoundingClientRect(),i=Qt(e.width)/t.offsetWidth||1,n=Qt(e.height)/t.offsetHeight||1;return 1!==i||1!==n}(e),a=ee(e),l=Ut(t,r,i),c={scrollLeft:0,scrollTop:0},h={x:0,y:0};return(o||!o&&!i)&&(("body"!==Ht(e)||Ae(a))&&(c=(n=e)!==$t(n)&&Bt(n)?{scrollLeft:(s=n).scrollLeft,scrollTop:s.scrollTop}:ye(n)),Bt(e)?((h=Ut(e,!0)).x+=e.clientLeft,h.y+=e.clientTop):a&&(h.x=we(a))),{x:l.left+c.scrollLeft-h.x,y:l.top+c.scrollTop-h.y,width:l.width,height:l.height}}function He(t){var e=new Map,i=new Set,n=[];function s(t){i.add(t.name),[].concat(t.requires||[],t.requiresIfExists||[]).forEach((function(t){if(!i.has(t)){var n=e.get(t);n&&s(n)}})),n.push(t)}return t.forEach((function(t){e.set(t.name,t)})),t.forEach((function(t){i.has(t.name)||s(t)})),n}var $e={placement:"bottom",modifiers:[],strategy:"absolute"};function We(){for(var t=arguments.length,e=new Array(t),i=0;iNumber.parseInt(t,10))):"function"==typeof t?e=>t(e,this._element):t}_getPopperConfig(){const t={placement:this._getPlacement(),modifiers:[{name:"preventOverflow",options:{boundary:this._config.boundary}},{name:"offset",options:{offset:this._getOffset()}}]};return(this._inNavbar||"static"===this._config.display)&&(H.setDataAttribute(this._menu,"popper","static"),t.modifiers=[{name:"applyStyles",enabled:!1}]),{...t,...p(this._config.popperConfig,[t])}}_selectMenuItem({key:t,target:e}){const i=z.find(".dropdown-menu .dropdown-item:not(.disabled):not(:disabled)",this._menu).filter((t=>o(t)));i.length&&g(i,e,t===Xe,!i.includes(e)).focus()}static jQueryInterface(t){return this.each((function(){const e=ci.getOrCreateInstance(this,t);if("string"==typeof t){if(void 0===e[t])throw new TypeError(`No method named "${t}"`);e[t]()}}))}static clearMenus(t){if(2===t.button||"keyup"===t.type&&"Tab"!==t.key)return;const e=z.find(Ze);for(const i of e){const e=ci.getInstance(i);if(!e||!1===e._config.autoClose)continue;const n=t.composedPath(),s=n.includes(e._menu);if(n.includes(e._element)||"inside"===e._config.autoClose&&!s||"outside"===e._config.autoClose&&s)continue;if(e._menu.contains(t.target)&&("keyup"===t.type&&"Tab"===t.key||/input|select|option|textarea|form/i.test(t.target.tagName)))continue;const o={relatedTarget:e._element};"click"===t.type&&(o.clickEvent=t),e._completeHide(o)}}static dataApiKeydownHandler(t){const e=/input|textarea/i.test(t.target.tagName),i="Escape"===t.key,n=[Qe,Xe].includes(t.key);if(!n&&!i)return;if(e&&!i)return;t.preventDefault();const s=this.matches(Je)?this:z.prev(this,Je)[0]||z.next(this,Je)[0]||z.findOne(Je,t.delegateTarget.parentNode),o=ci.getOrCreateInstance(s);if(n)return t.stopPropagation(),o.show(),void o._selectMenuItem(t);o._isShown()&&(t.stopPropagation(),o.hide(),s.focus())}}I.on(document,Ue,Je,ci.dataApiKeydownHandler),I.on(document,Ue,ti,ci.dataApiKeydownHandler),I.on(document,Ye,ci.clearMenus),I.on(document,"keyup.bs.dropdown.data-api",ci.clearMenus),I.on(document,Ye,Je,(function(t){t.preventDefault(),ci.getOrCreateInstance(this).toggle()})),f(ci);const hi=".fixed-top, .fixed-bottom, .is-fixed, .sticky-top",di=".sticky-top",ui="padding-right",fi="margin-right";class pi{constructor(){this._element=document.body}getWidth(){const t=document.documentElement.clientWidth;return Math.abs(window.innerWidth-t)}hide(){const t=this.getWidth();this._disableOverFlow(),this._setElementAttributes(this._element,ui,(e=>e+t)),this._setElementAttributes(hi,ui,(e=>e+t)),this._setElementAttributes(di,fi,(e=>e-t))}reset(){this._resetElementAttributes(this._element,"overflow"),this._resetElementAttributes(this._element,ui),this._resetElementAttributes(hi,ui),this._resetElementAttributes(di,fi)}isOverflowing(){return this.getWidth()>0}_disableOverFlow(){this._saveInitialAttribute(this._element,"overflow"),this._element.style.overflow="hidden"}_setElementAttributes(t,e,i){const n=this.getWidth();this._applyManipulationCallback(t,(t=>{if(t!==this._element&&window.innerWidth>t.clientWidth+n)return;this._saveInitialAttribute(t,e);const s=window.getComputedStyle(t).getPropertyValue(e);t.style.setProperty(e,`${i(Number.parseFloat(s))}px`)}))}_saveInitialAttribute(t,e){const i=t.style.getPropertyValue(e);i&&H.setDataAttribute(t,e,i)}_resetElementAttributes(t,e){this._applyManipulationCallback(t,(t=>{const i=H.getDataAttribute(t,e);null!==i?(H.removeDataAttribute(t,e),t.style.setProperty(e,i)):t.style.removeProperty(e)}))}_applyManipulationCallback(t,e){if(n(t))e(t);else for(const i of z.find(t,this._element))e(i)}}const mi="show",gi="mousedown.bs.backdrop",_i={className:"modal-backdrop",clickCallback:null,isAnimated:!1,isVisible:!0,rootElement:"body"},bi={className:"string",clickCallback:"(function|null)",isAnimated:"boolean",isVisible:"boolean",rootElement:"(element|string)"};class vi extends ${constructor(t){super(),this._config=this._getConfig(t),this._isAppended=!1,this._element=null}static get Default(){return _i}static get DefaultType(){return bi}static get NAME(){return"backdrop"}show(t){if(!this._config.isVisible)return void p(t);this._append();const e=this._getElement();this._config.isAnimated&&c(e),e.classList.add(mi),this._emulateAnimation((()=>{p(t)}))}hide(t){this._config.isVisible?(this._getElement().classList.remove(mi),this._emulateAnimation((()=>{this.dispose(),p(t)}))):p(t)}dispose(){this._isAppended&&(I.off(this._element,gi),this._element.remove(),this._isAppended=!1)}_getElement(){if(!this._element){const t=document.createElement("div");t.className=this._config.className,this._config.isAnimated&&t.classList.add("fade"),this._element=t}return this._element}_configAfterMerge(t){return t.rootElement=s(t.rootElement),t}_append(){if(this._isAppended)return;const t=this._getElement();this._config.rootElement.append(t),I.on(t,gi,(()=>{p(this._config.clickCallback)})),this._isAppended=!0}_emulateAnimation(t){m(t,this._getElement(),this._config.isAnimated)}}const yi=".bs.focustrap",wi="backward",Ai={autofocus:!0,trapElement:null},Ei={autofocus:"boolean",trapElement:"element"};class Ti extends ${constructor(t){super(),this._config=this._getConfig(t),this._isActive=!1,this._lastTabNavDirection=null}static get Default(){return Ai}static get DefaultType(){return Ei}static get NAME(){return"focustrap"}activate(){this._isActive||(this._config.autofocus&&this._config.trapElement.focus(),I.off(document,yi),I.on(document,"focusin.bs.focustrap",(t=>this._handleFocusin(t))),I.on(document,"keydown.tab.bs.focustrap",(t=>this._handleKeydown(t))),this._isActive=!0)}deactivate(){this._isActive&&(this._isActive=!1,I.off(document,yi))}_handleFocusin(t){const{trapElement:e}=this._config;if(t.target===document||t.target===e||e.contains(t.target))return;const i=z.focusableChildren(e);0===i.length?e.focus():this._lastTabNavDirection===wi?i[i.length-1].focus():i[0].focus()}_handleKeydown(t){"Tab"===t.key&&(this._lastTabNavDirection=t.shiftKey?wi:"forward")}}const Ci="hidden.bs.modal",Oi="show.bs.modal",xi="modal-open",ki="show",Li="modal-static",Si={backdrop:!0,focus:!0,keyboard:!0},Di={backdrop:"(boolean|string)",focus:"boolean",keyboard:"boolean"};class Ii extends W{constructor(t,e){super(t,e),this._dialog=z.findOne(".modal-dialog",this._element),this._backdrop=this._initializeBackDrop(),this._focustrap=this._initializeFocusTrap(),this._isShown=!1,this._isTransitioning=!1,this._scrollBar=new pi,this._addEventListeners()}static get Default(){return Si}static get DefaultType(){return Di}static get NAME(){return"modal"}toggle(t){return this._isShown?this.hide():this.show(t)}show(t){this._isShown||this._isTransitioning||I.trigger(this._element,Oi,{relatedTarget:t}).defaultPrevented||(this._isShown=!0,this._isTransitioning=!0,this._scrollBar.hide(),document.body.classList.add(xi),this._adjustDialog(),this._backdrop.show((()=>this._showElement(t))))}hide(){this._isShown&&!this._isTransitioning&&(I.trigger(this._element,"hide.bs.modal").defaultPrevented||(this._isShown=!1,this._isTransitioning=!0,this._focustrap.deactivate(),this._element.classList.remove(ki),this._queueCallback((()=>this._hideModal()),this._element,this._isAnimated())))}dispose(){for(const t of[window,this._dialog])I.off(t,".bs.modal");this._backdrop.dispose(),this._focustrap.deactivate(),super.dispose()}handleUpdate(){this._adjustDialog()}_initializeBackDrop(){return new vi({isVisible:Boolean(this._config.backdrop),isAnimated:this._isAnimated()})}_initializeFocusTrap(){return new Ti({trapElement:this._element})}_showElement(t){document.body.contains(this._element)||document.body.append(this._element),this._element.style.display="block",this._element.removeAttribute("aria-hidden"),this._element.setAttribute("aria-modal",!0),this._element.setAttribute("role","dialog"),this._element.scrollTop=0;const e=z.findOne(".modal-body",this._dialog);e&&(e.scrollTop=0),c(this._element),this._element.classList.add(ki),this._queueCallback((()=>{this._config.focus&&this._focustrap.activate(),this._isTransitioning=!1,I.trigger(this._element,"shown.bs.modal",{relatedTarget:t})}),this._dialog,this._isAnimated())}_addEventListeners(){I.on(this._element,"keydown.dismiss.bs.modal",(t=>{"Escape"===t.key&&(this._config.keyboard?this.hide():this._triggerBackdropTransition())})),I.on(window,"resize.bs.modal",(()=>{this._isShown&&!this._isTransitioning&&this._adjustDialog()})),I.on(this._element,"mousedown.dismiss.bs.modal",(t=>{I.one(this._element,"click.dismiss.bs.modal",(e=>{this._element===t.target&&this._element===e.target&&("static"!==this._config.backdrop?this._config.backdrop&&this.hide():this._triggerBackdropTransition())}))}))}_hideModal(){this._element.style.display="none",this._element.setAttribute("aria-hidden",!0),this._element.removeAttribute("aria-modal"),this._element.removeAttribute("role"),this._isTransitioning=!1,this._backdrop.hide((()=>{document.body.classList.remove(xi),this._resetAdjustments(),this._scrollBar.reset(),I.trigger(this._element,Ci)}))}_isAnimated(){return this._element.classList.contains("fade")}_triggerBackdropTransition(){if(I.trigger(this._element,"hidePrevented.bs.modal").defaultPrevented)return;const t=this._element.scrollHeight>document.documentElement.clientHeight,e=this._element.style.overflowY;"hidden"===e||this._element.classList.contains(Li)||(t||(this._element.style.overflowY="hidden"),this._element.classList.add(Li),this._queueCallback((()=>{this._element.classList.remove(Li),this._queueCallback((()=>{this._element.style.overflowY=e}),this._dialog)}),this._dialog),this._element.focus())}_adjustDialog(){const t=this._element.scrollHeight>document.documentElement.clientHeight,e=this._scrollBar.getWidth(),i=e>0;if(i&&!t){const t=u()?"paddingLeft":"paddingRight";this._element.style[t]=`${e}px`}if(!i&&t){const t=u()?"paddingRight":"paddingLeft";this._element.style[t]=`${e}px`}}_resetAdjustments(){this._element.style.paddingLeft="",this._element.style.paddingRight=""}static jQueryInterface(t,e){return this.each((function(){const i=Ii.getOrCreateInstance(this,t);if("string"==typeof t){if(void 0===i[t])throw new TypeError(`No method named "${t}"`);i[t](e)}}))}}I.on(document,"click.bs.modal.data-api",'[data-bs-toggle="modal"]',(function(t){const e=z.getElementFromSelector(this);["A","AREA"].includes(this.tagName)&&t.preventDefault(),I.one(e,Oi,(t=>{t.defaultPrevented||I.one(e,Ci,(()=>{o(this)&&this.focus()}))}));const i=z.findOne(".modal.show");i&&Ii.getInstance(i).hide(),Ii.getOrCreateInstance(e).toggle(this)})),R(Ii),f(Ii);const Ni="show",Pi="showing",ji="hiding",Mi=".offcanvas.show",Fi="hidePrevented.bs.offcanvas",Hi="hidden.bs.offcanvas",$i={backdrop:!0,keyboard:!0,scroll:!1},Wi={backdrop:"(boolean|string)",keyboard:"boolean",scroll:"boolean"};class Bi extends W{constructor(t,e){super(t,e),this._isShown=!1,this._backdrop=this._initializeBackDrop(),this._focustrap=this._initializeFocusTrap(),this._addEventListeners()}static get Default(){return $i}static get DefaultType(){return Wi}static get NAME(){return"offcanvas"}toggle(t){return this._isShown?this.hide():this.show(t)}show(t){this._isShown||I.trigger(this._element,"show.bs.offcanvas",{relatedTarget:t}).defaultPrevented||(this._isShown=!0,this._backdrop.show(),this._config.scroll||(new pi).hide(),this._element.setAttribute("aria-modal",!0),this._element.setAttribute("role","dialog"),this._element.classList.add(Pi),this._queueCallback((()=>{this._config.scroll&&!this._config.backdrop||this._focustrap.activate(),this._element.classList.add(Ni),this._element.classList.remove(Pi),I.trigger(this._element,"shown.bs.offcanvas",{relatedTarget:t})}),this._element,!0))}hide(){this._isShown&&(I.trigger(this._element,"hide.bs.offcanvas").defaultPrevented||(this._focustrap.deactivate(),this._element.blur(),this._isShown=!1,this._element.classList.add(ji),this._backdrop.hide(),this._queueCallback((()=>{this._element.classList.remove(Ni,ji),this._element.removeAttribute("aria-modal"),this._element.removeAttribute("role"),this._config.scroll||(new pi).reset(),I.trigger(this._element,Hi)}),this._element,!0)))}dispose(){this._backdrop.dispose(),this._focustrap.deactivate(),super.dispose()}_initializeBackDrop(){const t=Boolean(this._config.backdrop);return new vi({className:"offcanvas-backdrop",isVisible:t,isAnimated:!0,rootElement:this._element.parentNode,clickCallback:t?()=>{"static"!==this._config.backdrop?this.hide():I.trigger(this._element,Fi)}:null})}_initializeFocusTrap(){return new Ti({trapElement:this._element})}_addEventListeners(){I.on(this._element,"keydown.dismiss.bs.offcanvas",(t=>{"Escape"===t.key&&(this._config.keyboard?this.hide():I.trigger(this._element,Fi))}))}static jQueryInterface(t){return this.each((function(){const e=Bi.getOrCreateInstance(this,t);if("string"==typeof t){if(void 0===e[t]||t.startsWith("_")||"constructor"===t)throw new TypeError(`No method named "${t}"`);e[t](this)}}))}}I.on(document,"click.bs.offcanvas.data-api",'[data-bs-toggle="offcanvas"]',(function(t){const e=z.getElementFromSelector(this);if(["A","AREA"].includes(this.tagName)&&t.preventDefault(),r(this))return;I.one(e,Hi,(()=>{o(this)&&this.focus()}));const i=z.findOne(Mi);i&&i!==e&&Bi.getInstance(i).hide(),Bi.getOrCreateInstance(e).toggle(this)})),I.on(window,"load.bs.offcanvas.data-api",(()=>{for(const t of z.find(Mi))Bi.getOrCreateInstance(t).show()})),I.on(window,"resize.bs.offcanvas",(()=>{for(const t of z.find("[aria-modal][class*=show][class*=offcanvas-]"))"fixed"!==getComputedStyle(t).position&&Bi.getOrCreateInstance(t).hide()})),R(Bi),f(Bi);const zi=new Set(["background","cite","href","itemtype","longdesc","poster","src","xlink:href"]),Ri=/^(?:(?:https?|mailto|ftp|tel|file|sms):|[^#&/:?]*(?:[#/?]|$))/i,qi=/^data:(?:image\/(?:bmp|gif|jpeg|jpg|png|tiff|webp)|video\/(?:mpeg|mp4|ogg|webm)|audio\/(?:mp3|oga|ogg|opus));base64,[\d+/a-z]+=*$/i,Vi=(t,e)=>{const i=t.nodeName.toLowerCase();return e.includes(i)?!zi.has(i)||Boolean(Ri.test(t.nodeValue)||qi.test(t.nodeValue)):e.filter((t=>t instanceof RegExp)).some((t=>t.test(i)))},Ki={"*":["class","dir","id","lang","role",/^aria-[\w-]*$/i],a:["target","href","title","rel"],area:[],b:[],br:[],col:[],code:[],div:[],em:[],hr:[],h1:[],h2:[],h3:[],h4:[],h5:[],h6:[],i:[],img:["src","srcset","alt","title","width","height"],li:[],ol:[],p:[],pre:[],s:[],small:[],span:[],sub:[],sup:[],strong:[],u:[],ul:[]},Qi={allowList:Ki,content:{},extraClass:"",html:!1,sanitize:!0,sanitizeFn:null,template:"
"},Xi={allowList:"object",content:"object",extraClass:"(string|function)",html:"boolean",sanitize:"boolean",sanitizeFn:"(null|function)",template:"string"},Yi={entry:"(string|element|function|null)",selector:"(string|element)"};class Ui extends ${constructor(t){super(),this._config=this._getConfig(t)}static get Default(){return Qi}static get DefaultType(){return Xi}static get NAME(){return"TemplateFactory"}getContent(){return Object.values(this._config.content).map((t=>this._resolvePossibleFunction(t))).filter(Boolean)}hasContent(){return this.getContent().length>0}changeContent(t){return this._checkContent(t),this._config.content={...this._config.content,...t},this}toHtml(){const t=document.createElement("div");t.innerHTML=this._maybeSanitize(this._config.template);for(const[e,i]of Object.entries(this._config.content))this._setContent(t,i,e);const e=t.children[0],i=this._resolvePossibleFunction(this._config.extraClass);return i&&e.classList.add(...i.split(" ")),e}_typeCheckConfig(t){super._typeCheckConfig(t),this._checkContent(t.content)}_checkContent(t){for(const[e,i]of Object.entries(t))super._typeCheckConfig({selector:e,entry:i},Yi)}_setContent(t,e,i){const o=z.findOne(i,t);o&&((e=this._resolvePossibleFunction(e))?n(e)?this._putElementInTemplate(s(e),o):this._config.html?o.innerHTML=this._maybeSanitize(e):o.textContent=e:o.remove())}_maybeSanitize(t){return this._config.sanitize?function(t,e,i){if(!t.length)return t;if(i&&"function"==typeof i)return i(t);const n=(new window.DOMParser).parseFromString(t,"text/html"),s=[].concat(...n.body.querySelectorAll("*"));for(const t of s){const i=t.nodeName.toLowerCase();if(!Object.keys(e).includes(i)){t.remove();continue}const n=[].concat(...t.attributes),s=[].concat(e["*"]||[],e[i]||[]);for(const e of n)Vi(e,s)||t.removeAttribute(e.nodeName)}return n.body.innerHTML}(t,this._config.allowList,this._config.sanitizeFn):t}_resolvePossibleFunction(t){return p(t,[this])}_putElementInTemplate(t,e){if(this._config.html)return e.innerHTML="",void e.append(t);e.textContent=t.textContent}}const Gi=new Set(["sanitize","allowList","sanitizeFn"]),Ji="fade",Zi="show",tn=".modal",en="hide.bs.modal",nn="hover",sn="focus",on={AUTO:"auto",TOP:"top",RIGHT:u()?"left":"right",BOTTOM:"bottom",LEFT:u()?"right":"left"},rn={allowList:Ki,animation:!0,boundary:"clippingParents",container:!1,customClass:"",delay:0,fallbackPlacements:["top","right","bottom","left"],html:!1,offset:[0,0],placement:"top",popperConfig:null,sanitize:!0,sanitizeFn:null,selector:!1,template:'',title:"",trigger:"hover focus"},an={allowList:"object",animation:"boolean",boundary:"(string|element)",container:"(string|element|boolean)",customClass:"(string|function)",delay:"(number|object)",fallbackPlacements:"array",html:"boolean",offset:"(array|string|function)",placement:"(string|function)",popperConfig:"(null|object|function)",sanitize:"boolean",sanitizeFn:"(null|function)",selector:"(string|boolean)",template:"string",title:"(string|element|function)",trigger:"string"};class ln extends W{constructor(t,e){if(void 0===Ve)throw new TypeError("Bootstrap's tooltips require Popper (https://popper.js.org)");super(t,e),this._isEnabled=!0,this._timeout=0,this._isHovered=null,this._activeTrigger={},this._popper=null,this._templateFactory=null,this._newContent=null,this.tip=null,this._setListeners(),this._config.selector||this._fixTitle()}static get Default(){return rn}static get DefaultType(){return an}static get NAME(){return"tooltip"}enable(){this._isEnabled=!0}disable(){this._isEnabled=!1}toggleEnabled(){this._isEnabled=!this._isEnabled}toggle(){this._isEnabled&&(this._activeTrigger.click=!this._activeTrigger.click,this._isShown()?this._leave():this._enter())}dispose(){clearTimeout(this._timeout),I.off(this._element.closest(tn),en,this._hideModalHandler),this._element.getAttribute("data-bs-original-title")&&this._element.setAttribute("title",this._element.getAttribute("data-bs-original-title")),this._disposePopper(),super.dispose()}show(){if("none"===this._element.style.display)throw new Error("Please use show on visible elements");if(!this._isWithContent()||!this._isEnabled)return;const t=I.trigger(this._element,this.constructor.eventName("show")),e=(a(this._element)||this._element.ownerDocument.documentElement).contains(this._element);if(t.defaultPrevented||!e)return;this._disposePopper();const i=this._getTipElement();this._element.setAttribute("aria-describedby",i.getAttribute("id"));const{container:n}=this._config;if(this._element.ownerDocument.documentElement.contains(this.tip)||(n.append(i),I.trigger(this._element,this.constructor.eventName("inserted"))),this._popper=this._createPopper(i),i.classList.add(Zi),"ontouchstart"in document.documentElement)for(const t of[].concat(...document.body.children))I.on(t,"mouseover",l);this._queueCallback((()=>{I.trigger(this._element,this.constructor.eventName("shown")),!1===this._isHovered&&this._leave(),this._isHovered=!1}),this.tip,this._isAnimated())}hide(){if(this._isShown()&&!I.trigger(this._element,this.constructor.eventName("hide")).defaultPrevented){if(this._getTipElement().classList.remove(Zi),"ontouchstart"in document.documentElement)for(const t of[].concat(...document.body.children))I.off(t,"mouseover",l);this._activeTrigger.click=!1,this._activeTrigger.focus=!1,this._activeTrigger.hover=!1,this._isHovered=null,this._queueCallback((()=>{this._isWithActiveTrigger()||(this._isHovered||this._disposePopper(),this._element.removeAttribute("aria-describedby"),I.trigger(this._element,this.constructor.eventName("hidden")))}),this.tip,this._isAnimated())}}update(){this._popper&&this._popper.update()}_isWithContent(){return Boolean(this._getTitle())}_getTipElement(){return this.tip||(this.tip=this._createTipElement(this._newContent||this._getContentForTemplate())),this.tip}_createTipElement(t){const e=this._getTemplateFactory(t).toHtml();if(!e)return null;e.classList.remove(Ji,Zi),e.classList.add(`bs-${this.constructor.NAME}-auto`);const i=(t=>{do{t+=Math.floor(1e6*Math.random())}while(document.getElementById(t));return t})(this.constructor.NAME).toString();return e.setAttribute("id",i),this._isAnimated()&&e.classList.add(Ji),e}setContent(t){this._newContent=t,this._isShown()&&(this._disposePopper(),this.show())}_getTemplateFactory(t){return this._templateFactory?this._templateFactory.changeContent(t):this._templateFactory=new Ui({...this._config,content:t,extraClass:this._resolvePossibleFunction(this._config.customClass)}),this._templateFactory}_getContentForTemplate(){return{".tooltip-inner":this._getTitle()}}_getTitle(){return this._resolvePossibleFunction(this._config.title)||this._element.getAttribute("data-bs-original-title")}_initializeOnDelegatedTarget(t){return this.constructor.getOrCreateInstance(t.delegateTarget,this._getDelegateConfig())}_isAnimated(){return this._config.animation||this.tip&&this.tip.classList.contains(Ji)}_isShown(){return this.tip&&this.tip.classList.contains(Zi)}_createPopper(t){const e=p(this._config.placement,[this,t,this._element]),i=on[e.toUpperCase()];return qe(this._element,t,this._getPopperConfig(i))}_getOffset(){const{offset:t}=this._config;return"string"==typeof t?t.split(",").map((t=>Number.parseInt(t,10))):"function"==typeof t?e=>t(e,this._element):t}_resolvePossibleFunction(t){return p(t,[this._element])}_getPopperConfig(t){const e={placement:t,modifiers:[{name:"flip",options:{fallbackPlacements:this._config.fallbackPlacements}},{name:"offset",options:{offset:this._getOffset()}},{name:"preventOverflow",options:{boundary:this._config.boundary}},{name:"arrow",options:{element:`.${this.constructor.NAME}-arrow`}},{name:"preSetPlacement",enabled:!0,phase:"beforeMain",fn:t=>{this._getTipElement().setAttribute("data-popper-placement",t.state.placement)}}]};return{...e,...p(this._config.popperConfig,[e])}}_setListeners(){const t=this._config.trigger.split(" ");for(const e of t)if("click"===e)I.on(this._element,this.constructor.eventName("click"),this._config.selector,(t=>{this._initializeOnDelegatedTarget(t).toggle()}));else if("manual"!==e){const t=e===nn?this.constructor.eventName("mouseenter"):this.constructor.eventName("focusin"),i=e===nn?this.constructor.eventName("mouseleave"):this.constructor.eventName("focusout");I.on(this._element,t,this._config.selector,(t=>{const e=this._initializeOnDelegatedTarget(t);e._activeTrigger["focusin"===t.type?sn:nn]=!0,e._enter()})),I.on(this._element,i,this._config.selector,(t=>{const e=this._initializeOnDelegatedTarget(t);e._activeTrigger["focusout"===t.type?sn:nn]=e._element.contains(t.relatedTarget),e._leave()}))}this._hideModalHandler=()=>{this._element&&this.hide()},I.on(this._element.closest(tn),en,this._hideModalHandler)}_fixTitle(){const t=this._element.getAttribute("title");t&&(this._element.getAttribute("aria-label")||this._element.textContent.trim()||this._element.setAttribute("aria-label",t),this._element.setAttribute("data-bs-original-title",t),this._element.removeAttribute("title"))}_enter(){this._isShown()||this._isHovered?this._isHovered=!0:(this._isHovered=!0,this._setTimeout((()=>{this._isHovered&&this.show()}),this._config.delay.show))}_leave(){this._isWithActiveTrigger()||(this._isHovered=!1,this._setTimeout((()=>{this._isHovered||this.hide()}),this._config.delay.hide))}_setTimeout(t,e){clearTimeout(this._timeout),this._timeout=setTimeout(t,e)}_isWithActiveTrigger(){return Object.values(this._activeTrigger).includes(!0)}_getConfig(t){const e=H.getDataAttributes(this._element);for(const t of Object.keys(e))Gi.has(t)&&delete e[t];return t={...e,..."object"==typeof t&&t?t:{}},t=this._mergeConfigObj(t),t=this._configAfterMerge(t),this._typeCheckConfig(t),t}_configAfterMerge(t){return t.container=!1===t.container?document.body:s(t.container),"number"==typeof t.delay&&(t.delay={show:t.delay,hide:t.delay}),"number"==typeof t.title&&(t.title=t.title.toString()),"number"==typeof t.content&&(t.content=t.content.toString()),t}_getDelegateConfig(){const t={};for(const[e,i]of Object.entries(this._config))this.constructor.Default[e]!==i&&(t[e]=i);return t.selector=!1,t.trigger="manual",t}_disposePopper(){this._popper&&(this._popper.destroy(),this._popper=null),this.tip&&(this.tip.remove(),this.tip=null)}static jQueryInterface(t){return this.each((function(){const e=ln.getOrCreateInstance(this,t);if("string"==typeof t){if(void 0===e[t])throw new TypeError(`No method named "${t}"`);e[t]()}}))}}f(ln);const cn={...ln.Default,content:"",offset:[0,8],placement:"right",template:'',trigger:"click"},hn={...ln.DefaultType,content:"(null|string|element|function)"};class dn extends ln{static get Default(){return cn}static get DefaultType(){return hn}static get NAME(){return"popover"}_isWithContent(){return this._getTitle()||this._getContent()}_getContentForTemplate(){return{".popover-header":this._getTitle(),".popover-body":this._getContent()}}_getContent(){return this._resolvePossibleFunction(this._config.content)}static jQueryInterface(t){return this.each((function(){const e=dn.getOrCreateInstance(this,t);if("string"==typeof t){if(void 0===e[t])throw new TypeError(`No method named "${t}"`);e[t]()}}))}}f(dn);const un="click.bs.scrollspy",fn="active",pn="[href]",mn={offset:null,rootMargin:"0px 0px -25%",smoothScroll:!1,target:null,threshold:[.1,.5,1]},gn={offset:"(number|null)",rootMargin:"string",smoothScroll:"boolean",target:"element",threshold:"array"};class _n extends W{constructor(t,e){super(t,e),this._targetLinks=new Map,this._observableSections=new Map,this._rootElement="visible"===getComputedStyle(this._element).overflowY?null:this._element,this._activeTarget=null,this._observer=null,this._previousScrollData={visibleEntryTop:0,parentScrollTop:0},this.refresh()}static get Default(){return mn}static get DefaultType(){return gn}static get NAME(){return"scrollspy"}refresh(){this._initializeTargetsAndObservables(),this._maybeEnableSmoothScroll(),this._observer?this._observer.disconnect():this._observer=this._getNewObserver();for(const t of this._observableSections.values())this._observer.observe(t)}dispose(){this._observer.disconnect(),super.dispose()}_configAfterMerge(t){return t.target=s(t.target)||document.body,t.rootMargin=t.offset?`${t.offset}px 0px -30%`:t.rootMargin,"string"==typeof t.threshold&&(t.threshold=t.threshold.split(",").map((t=>Number.parseFloat(t)))),t}_maybeEnableSmoothScroll(){this._config.smoothScroll&&(I.off(this._config.target,un),I.on(this._config.target,un,pn,(t=>{const e=this._observableSections.get(t.target.hash);if(e){t.preventDefault();const i=this._rootElement||window,n=e.offsetTop-this._element.offsetTop;if(i.scrollTo)return void i.scrollTo({top:n,behavior:"smooth"});i.scrollTop=n}})))}_getNewObserver(){const t={root:this._rootElement,threshold:this._config.threshold,rootMargin:this._config.rootMargin};return new IntersectionObserver((t=>this._observerCallback(t)),t)}_observerCallback(t){const e=t=>this._targetLinks.get(`#${t.target.id}`),i=t=>{this._previousScrollData.visibleEntryTop=t.target.offsetTop,this._process(e(t))},n=(this._rootElement||document.documentElement).scrollTop,s=n>=this._previousScrollData.parentScrollTop;this._previousScrollData.parentScrollTop=n;for(const o of t){if(!o.isIntersecting){this._activeTarget=null,this._clearActiveClass(e(o));continue}const t=o.target.offsetTop>=this._previousScrollData.visibleEntryTop;if(s&&t){if(i(o),!n)return}else s||t||i(o)}}_initializeTargetsAndObservables(){this._targetLinks=new Map,this._observableSections=new Map;const t=z.find(pn,this._config.target);for(const e of t){if(!e.hash||r(e))continue;const t=z.findOne(e.hash,this._element);o(t)&&(this._targetLinks.set(e.hash,e),this._observableSections.set(e.hash,t))}}_process(t){this._activeTarget!==t&&(this._clearActiveClass(this._config.target),this._activeTarget=t,t.classList.add(fn),this._activateParents(t),I.trigger(this._element,"activate.bs.scrollspy",{relatedTarget:t}))}_activateParents(t){if(t.classList.contains("dropdown-item"))z.findOne(".dropdown-toggle",t.closest(".dropdown")).classList.add(fn);else for(const e of z.parents(t,".nav, .list-group"))for(const t of z.prev(e,".nav-link, .nav-item > .nav-link, .list-group-item"))t.classList.add(fn)}_clearActiveClass(t){t.classList.remove(fn);const e=z.find("[href].active",t);for(const t of e)t.classList.remove(fn)}static jQueryInterface(t){return this.each((function(){const e=_n.getOrCreateInstance(this,t);if("string"==typeof t){if(void 0===e[t]||t.startsWith("_")||"constructor"===t)throw new TypeError(`No method named "${t}"`);e[t]()}}))}}I.on(window,"load.bs.scrollspy.data-api",(()=>{for(const t of z.find('[data-bs-spy="scroll"]'))_n.getOrCreateInstance(t)})),f(_n);const bn="ArrowLeft",vn="ArrowRight",yn="ArrowUp",wn="ArrowDown",An="active",En="fade",Tn="show",Cn='[data-bs-toggle="tab"], [data-bs-toggle="pill"], [data-bs-toggle="list"]',On=`.nav-link:not(.dropdown-toggle), .list-group-item:not(.dropdown-toggle), [role="tab"]:not(.dropdown-toggle), ${Cn}`;class xn extends W{constructor(t){super(t),this._parent=this._element.closest('.list-group, .nav, [role="tablist"]'),this._parent&&(this._setInitialAttributes(this._parent,this._getChildren()),I.on(this._element,"keydown.bs.tab",(t=>this._keydown(t))))}static get NAME(){return"tab"}show(){const t=this._element;if(this._elemIsActive(t))return;const e=this._getActiveElem(),i=e?I.trigger(e,"hide.bs.tab",{relatedTarget:t}):null;I.trigger(t,"show.bs.tab",{relatedTarget:e}).defaultPrevented||i&&i.defaultPrevented||(this._deactivate(e,t),this._activate(t,e))}_activate(t,e){t&&(t.classList.add(An),this._activate(z.getElementFromSelector(t)),this._queueCallback((()=>{"tab"===t.getAttribute("role")?(t.removeAttribute("tabindex"),t.setAttribute("aria-selected",!0),this._toggleDropDown(t,!0),I.trigger(t,"shown.bs.tab",{relatedTarget:e})):t.classList.add(Tn)}),t,t.classList.contains(En)))}_deactivate(t,e){t&&(t.classList.remove(An),t.blur(),this._deactivate(z.getElementFromSelector(t)),this._queueCallback((()=>{"tab"===t.getAttribute("role")?(t.setAttribute("aria-selected",!1),t.setAttribute("tabindex","-1"),this._toggleDropDown(t,!1),I.trigger(t,"hidden.bs.tab",{relatedTarget:e})):t.classList.remove(Tn)}),t,t.classList.contains(En)))}_keydown(t){if(![bn,vn,yn,wn].includes(t.key))return;t.stopPropagation(),t.preventDefault();const e=[vn,wn].includes(t.key),i=g(this._getChildren().filter((t=>!r(t))),t.target,e,!0);i&&(i.focus({preventScroll:!0}),xn.getOrCreateInstance(i).show())}_getChildren(){return z.find(On,this._parent)}_getActiveElem(){return this._getChildren().find((t=>this._elemIsActive(t)))||null}_setInitialAttributes(t,e){this._setAttributeIfNotExists(t,"role","tablist");for(const t of e)this._setInitialAttributesOnChild(t)}_setInitialAttributesOnChild(t){t=this._getInnerElement(t);const e=this._elemIsActive(t),i=this._getOuterElement(t);t.setAttribute("aria-selected",e),i!==t&&this._setAttributeIfNotExists(i,"role","presentation"),e||t.setAttribute("tabindex","-1"),this._setAttributeIfNotExists(t,"role","tab"),this._setInitialAttributesOnTargetPanel(t)}_setInitialAttributesOnTargetPanel(t){const e=z.getElementFromSelector(t);e&&(this._setAttributeIfNotExists(e,"role","tabpanel"),t.id&&this._setAttributeIfNotExists(e,"aria-labelledby",`${t.id}`))}_toggleDropDown(t,e){const i=this._getOuterElement(t);if(!i.classList.contains("dropdown"))return;const n=(t,n)=>{const s=z.findOne(t,i);s&&s.classList.toggle(n,e)};n(".dropdown-toggle",An),n(".dropdown-menu",Tn),i.setAttribute("aria-expanded",e)}_setAttributeIfNotExists(t,e,i){t.hasAttribute(e)||t.setAttribute(e,i)}_elemIsActive(t){return t.classList.contains(An)}_getInnerElement(t){return t.matches(On)?t:z.findOne(On,t)}_getOuterElement(t){return t.closest(".nav-item, .list-group-item")||t}static jQueryInterface(t){return this.each((function(){const e=xn.getOrCreateInstance(this);if("string"==typeof t){if(void 0===e[t]||t.startsWith("_")||"constructor"===t)throw new TypeError(`No method named "${t}"`);e[t]()}}))}}I.on(document,"click.bs.tab",Cn,(function(t){["A","AREA"].includes(this.tagName)&&t.preventDefault(),r(this)||xn.getOrCreateInstance(this).show()})),I.on(window,"load.bs.tab",(()=>{for(const t of z.find('.active[data-bs-toggle="tab"], .active[data-bs-toggle="pill"], .active[data-bs-toggle="list"]'))xn.getOrCreateInstance(t)})),f(xn);const kn="hide",Ln="show",Sn="showing",Dn={animation:"boolean",autohide:"boolean",delay:"number"},In={animation:!0,autohide:!0,delay:5e3};class Nn extends W{constructor(t,e){super(t,e),this._timeout=null,this._hasMouseInteraction=!1,this._hasKeyboardInteraction=!1,this._setListeners()}static get Default(){return In}static get DefaultType(){return Dn}static get NAME(){return"toast"}show(){I.trigger(this._element,"show.bs.toast").defaultPrevented||(this._clearTimeout(),this._config.animation&&this._element.classList.add("fade"),this._element.classList.remove(kn),c(this._element),this._element.classList.add(Ln,Sn),this._queueCallback((()=>{this._element.classList.remove(Sn),I.trigger(this._element,"shown.bs.toast"),this._maybeScheduleHide()}),this._element,this._config.animation))}hide(){this.isShown()&&(I.trigger(this._element,"hide.bs.toast").defaultPrevented||(this._element.classList.add(Sn),this._queueCallback((()=>{this._element.classList.add(kn),this._element.classList.remove(Sn,Ln),I.trigger(this._element,"hidden.bs.toast")}),this._element,this._config.animation)))}dispose(){this._clearTimeout(),this.isShown()&&this._element.classList.remove(Ln),super.dispose()}isShown(){return this._element.classList.contains(Ln)}_maybeScheduleHide(){this._config.autohide&&(this._hasMouseInteraction||this._hasKeyboardInteraction||(this._timeout=setTimeout((()=>{this.hide()}),this._config.delay)))}_onInteraction(t,e){switch(t.type){case"mouseover":case"mouseout":this._hasMouseInteraction=e;break;case"focusin":case"focusout":this._hasKeyboardInteraction=e}if(e)return void this._clearTimeout();const i=t.relatedTarget;this._element===i||this._element.contains(i)||this._maybeScheduleHide()}_setListeners(){I.on(this._element,"mouseover.bs.toast",(t=>this._onInteraction(t,!0))),I.on(this._element,"mouseout.bs.toast",(t=>this._onInteraction(t,!1))),I.on(this._element,"focusin.bs.toast",(t=>this._onInteraction(t,!0))),I.on(this._element,"focusout.bs.toast",(t=>this._onInteraction(t,!1)))}_clearTimeout(){clearTimeout(this._timeout),this._timeout=null}static jQueryInterface(t){return this.each((function(){const e=Nn.getOrCreateInstance(this,t);if("string"==typeof t){if(void 0===e[t])throw new TypeError(`No method named "${t}"`);e[t](this)}}))}}return R(Nn),f(Nn),{Alert:q,Button:K,Carousel:rt,Collapse:ft,Dropdown:ci,Modal:Ii,Offcanvas:Bi,Popover:dn,ScrollSpy:_n,Tab:xn,Toast:Nn,Tooltip:ln}})); \ No newline at end of file diff --git a/os3/js/mustache.js b/os3/js/mustache.js new file mode 100644 index 0000000..d75fce2 --- /dev/null +++ b/os3/js/mustache.js @@ -0,0 +1,764 @@ +/*! + * mustache.js - Logic-less {{mustache}} templates with JavaScript + * http://github.com/janl/mustache.js + */ + +var objectToString = Object.prototype.toString; +var isArray = Array.isArray || function isArrayPolyfill (object) { + return objectToString.call(object) === '[object Array]'; +}; + +function isFunction (object) { + return typeof object === 'function'; +} + +/** + * More correct typeof string handling array + * which normally returns typeof 'object' + */ +function typeStr (obj) { + return isArray(obj) ? 'array' : typeof obj; +} + +function escapeRegExp (string) { + return string.replace(/[\-\[\]{}()*+?.,\\\^$|#\s]/g, '\\$&'); +} + +/** + * Null safe way of checking whether or not an object, + * including its prototype, has a given property + */ +function hasProperty (obj, propName) { + return obj != null && typeof obj === 'object' && (propName in obj); +} + +/** + * Safe way of detecting whether or not the given thing is a primitive and + * whether it has the given property + */ +function primitiveHasOwnProperty (primitive, propName) { + return ( + primitive != null + && typeof primitive !== 'object' + && primitive.hasOwnProperty + && primitive.hasOwnProperty(propName) + ); +} + +// Workaround for https://issues.apache.org/jira/browse/COUCHDB-577 +// See https://github.com/janl/mustache.js/issues/189 +var regExpTest = RegExp.prototype.test; +function testRegExp (re, string) { + return regExpTest.call(re, string); +} + +var nonSpaceRe = /\S/; +function isWhitespace (string) { + return !testRegExp(nonSpaceRe, string); +} + +var entityMap = { + '&': '&', + '<': '<', + '>': '>', + '"': '"', + "'": ''', + '/': '/', + '`': '`', + '=': '=' +}; + +function escapeHtml (string) { + return String(string).replace(/[&<>"'`=\/]/g, function fromEntityMap (s) { + return entityMap[s]; + }); +} + +var whiteRe = /\s*/; +var spaceRe = /\s+/; +var equalsRe = /\s*=/; +var curlyRe = /\s*\}/; +var tagRe = /#|\^|\/|>|\{|&|=|!/; + +/** + * Breaks up the given `template` string into a tree of tokens. If the `tags` + * argument is given here it must be an array with two string values: the + * opening and closing tags used in the template (e.g. [ "<%", "%>" ]). Of + * course, the default is to use mustaches (i.e. mustache.tags). + * + * A token is an array with at least 4 elements. The first element is the + * mustache symbol that was used inside the tag, e.g. "#" or "&". If the tag + * did not contain a symbol (i.e. {{myValue}}) this element is "name". For + * all text that appears outside a symbol this element is "text". + * + * The second element of a token is its "value". For mustache tags this is + * whatever else was inside the tag besides the opening symbol. For text tokens + * this is the text itself. + * + * The third and fourth elements of the token are the start and end indices, + * respectively, of the token in the original template. + * + * Tokens that are the root node of a subtree contain two more elements: 1) an + * array of tokens in the subtree and 2) the index in the original template at + * which the closing tag for that section begins. + * + * Tokens for partials also contain two more elements: 1) a string value of + * indendation prior to that tag and 2) the index of that tag on that line - + * eg a value of 2 indicates the partial is the third tag on this line. + */ +function parseTemplate (template, tags) { + if (!template) + return []; + var lineHasNonSpace = false; + var sections = []; // Stack to hold section tokens + var tokens = []; // Buffer to hold the tokens + var spaces = []; // Indices of whitespace tokens on the current line + var hasTag = false; // Is there a {{tag}} on the current line? + var nonSpace = false; // Is there a non-space char on the current line? + var indentation = ''; // Tracks indentation for tags that use it + var tagIndex = 0; // Stores a count of number of tags encountered on a line + + // Strips all whitespace tokens array for the current line + // if there was a {{#tag}} on it and otherwise only space. + function stripSpace () { + if (hasTag && !nonSpace) { + while (spaces.length) + delete tokens[spaces.pop()]; + } else { + spaces = []; + } + + hasTag = false; + nonSpace = false; + } + + var openingTagRe, closingTagRe, closingCurlyRe; + function compileTags (tagsToCompile) { + if (typeof tagsToCompile === 'string') + tagsToCompile = tagsToCompile.split(spaceRe, 2); + + if (!isArray(tagsToCompile) || tagsToCompile.length !== 2) + throw new Error('Invalid tags: ' + tagsToCompile); + + openingTagRe = new RegExp(escapeRegExp(tagsToCompile[0]) + '\\s*'); + closingTagRe = new RegExp('\\s*' + escapeRegExp(tagsToCompile[1])); + closingCurlyRe = new RegExp('\\s*' + escapeRegExp('}' + tagsToCompile[1])); + } + + compileTags(tags || mustache.tags); + + var scanner = new Scanner(template); + + var start, type, value, chr, token, openSection; + while (!scanner.eos()) { + start = scanner.pos; + + // Match any text between tags. + value = scanner.scanUntil(openingTagRe); + + if (value) { + for (var i = 0, valueLength = value.length; i < valueLength; ++i) { + chr = value.charAt(i); + + if (isWhitespace(chr)) { + spaces.push(tokens.length); + indentation += chr; + } else { + nonSpace = true; + lineHasNonSpace = true; + indentation += ' '; + } + + tokens.push([ 'text', chr, start, start + 1 ]); + start += 1; + + // Check for whitespace on the current line. + if (chr === '\n') { + stripSpace(); + indentation = ''; + tagIndex = 0; + lineHasNonSpace = false; + } + } + } + + // Match the opening tag. + if (!scanner.scan(openingTagRe)) + break; + + hasTag = true; + + // Get the tag type. + type = scanner.scan(tagRe) || 'name'; + scanner.scan(whiteRe); + + // Get the tag value. + if (type === '=') { + value = scanner.scanUntil(equalsRe); + scanner.scan(equalsRe); + scanner.scanUntil(closingTagRe); + } else if (type === '{') { + value = scanner.scanUntil(closingCurlyRe); + scanner.scan(curlyRe); + scanner.scanUntil(closingTagRe); + type = '&'; + } else { + value = scanner.scanUntil(closingTagRe); + } + + // Match the closing tag. + if (!scanner.scan(closingTagRe)) + throw new Error('Unclosed tag at ' + scanner.pos); + + if (type == '>') { + token = [ type, value, start, scanner.pos, indentation, tagIndex, lineHasNonSpace ]; + } else { + token = [ type, value, start, scanner.pos ]; + } + tagIndex++; + tokens.push(token); + + if (type === '#' || type === '^') { + sections.push(token); + } else if (type === '/') { + // Check section nesting. + openSection = sections.pop(); + + if (!openSection) + throw new Error('Unopened section "' + value + '" at ' + start); + + if (openSection[1] !== value) + throw new Error('Unclosed section "' + openSection[1] + '" at ' + start); + } else if (type === 'name' || type === '{' || type === '&') { + nonSpace = true; + } else if (type === '=') { + // Set the tags for the next time around. + compileTags(value); + } + } + + stripSpace(); + + // Make sure there are no open sections when we're done. + openSection = sections.pop(); + + if (openSection) + throw new Error('Unclosed section "' + openSection[1] + '" at ' + scanner.pos); + + return nestTokens(squashTokens(tokens)); +} + +/** + * Combines the values of consecutive text tokens in the given `tokens` array + * to a single token. + */ +function squashTokens (tokens) { + var squashedTokens = []; + + var token, lastToken; + for (var i = 0, numTokens = tokens.length; i < numTokens; ++i) { + token = tokens[i]; + + if (token) { + if (token[0] === 'text' && lastToken && lastToken[0] === 'text') { + lastToken[1] += token[1]; + lastToken[3] = token[3]; + } else { + squashedTokens.push(token); + lastToken = token; + } + } + } + + return squashedTokens; +} + +/** + * Forms the given array of `tokens` into a nested tree structure where + * tokens that represent a section have two additional items: 1) an array of + * all tokens that appear in that section and 2) the index in the original + * template that represents the end of that section. + */ +function nestTokens (tokens) { + var nestedTokens = []; + var collector = nestedTokens; + var sections = []; + + var token, section; + for (var i = 0, numTokens = tokens.length; i < numTokens; ++i) { + token = tokens[i]; + + switch (token[0]) { + case '#': + case '^': + collector.push(token); + sections.push(token); + collector = token[4] = []; + break; + case '/': + section = sections.pop(); + section[5] = token[2]; + collector = sections.length > 0 ? sections[sections.length - 1][4] : nestedTokens; + break; + default: + collector.push(token); + } + } + + return nestedTokens; +} + +/** + * A simple string scanner that is used by the template parser to find + * tokens in template strings. + */ +function Scanner (string) { + this.string = string; + this.tail = string; + this.pos = 0; +} + +/** + * Returns `true` if the tail is empty (end of string). + */ +Scanner.prototype.eos = function eos () { + return this.tail === ''; +}; + +/** + * Tries to match the given regular expression at the current position. + * Returns the matched text if it can match, the empty string otherwise. + */ +Scanner.prototype.scan = function scan (re) { + var match = this.tail.match(re); + + if (!match || match.index !== 0) + return ''; + + var string = match[0]; + + this.tail = this.tail.substring(string.length); + this.pos += string.length; + + return string; +}; + +/** + * Skips all text until the given regular expression can be matched. Returns + * the skipped string, which is the entire tail if no match can be made. + */ +Scanner.prototype.scanUntil = function scanUntil (re) { + var index = this.tail.search(re), match; + + switch (index) { + case -1: + match = this.tail; + this.tail = ''; + break; + case 0: + match = ''; + break; + default: + match = this.tail.substring(0, index); + this.tail = this.tail.substring(index); + } + + this.pos += match.length; + + return match; +}; + +/** + * Represents a rendering context by wrapping a view object and + * maintaining a reference to the parent context. + */ +function Context (view, parentContext) { + this.view = view; + this.cache = { '.': this.view }; + this.parent = parentContext; +} + +/** + * Creates a new context using the given view with this context + * as the parent. + */ +Context.prototype.push = function push (view) { + return new Context(view, this); +}; + +/** + * Returns the value of the given name in this context, traversing + * up the context hierarchy if the value is absent in this context's view. + */ +Context.prototype.lookup = function lookup (name) { + var cache = this.cache; + + var value; + if (cache.hasOwnProperty(name)) { + value = cache[name]; + } else { + var context = this, intermediateValue, names, index, lookupHit = false; + + while (context) { + if (name.indexOf('.') > 0) { + intermediateValue = context.view; + names = name.split('.'); + index = 0; + + /** + * Using the dot notion path in `name`, we descend through the + * nested objects. + * + * To be certain that the lookup has been successful, we have to + * check if the last object in the path actually has the property + * we are looking for. We store the result in `lookupHit`. + * + * This is specially necessary for when the value has been set to + * `undefined` and we want to avoid looking up parent contexts. + * + * In the case where dot notation is used, we consider the lookup + * to be successful even if the last "object" in the path is + * not actually an object but a primitive (e.g., a string, or an + * integer), because it is sometimes useful to access a property + * of an autoboxed primitive, such as the length of a string. + **/ + while (intermediateValue != null && index < names.length) { + if (index === names.length - 1) + lookupHit = ( + hasProperty(intermediateValue, names[index]) + || primitiveHasOwnProperty(intermediateValue, names[index]) + ); + + intermediateValue = intermediateValue[names[index++]]; + } + } else { + intermediateValue = context.view[name]; + + /** + * Only checking against `hasProperty`, which always returns `false` if + * `context.view` is not an object. Deliberately omitting the check + * against `primitiveHasOwnProperty` if dot notation is not used. + * + * Consider this example: + * ``` + * Mustache.render("The length of a football field is {{#length}}{{length}}{{/length}}.", {length: "100 yards"}) + * ``` + * + * If we were to check also against `primitiveHasOwnProperty`, as we do + * in the dot notation case, then render call would return: + * + * "The length of a football field is 9." + * + * rather than the expected: + * + * "The length of a football field is 100 yards." + **/ + lookupHit = hasProperty(context.view, name); + } + + if (lookupHit) { + value = intermediateValue; + break; + } + + context = context.parent; + } + + cache[name] = value; + } + + if (isFunction(value)) + value = value.call(this.view); + + return value; +}; + +/** + * A Writer knows how to take a stream of tokens and render them to a + * string, given a context. It also maintains a cache of templates to + * avoid the need to parse the same template twice. + */ +function Writer () { + this.templateCache = { + _cache: {}, + set: function set (key, value) { + this._cache[key] = value; + }, + get: function get (key) { + return this._cache[key]; + }, + clear: function clear () { + this._cache = {}; + } + }; +} + +/** + * Clears all cached templates in this writer. + */ +Writer.prototype.clearCache = function clearCache () { + if (typeof this.templateCache !== 'undefined') { + this.templateCache.clear(); + } +}; + +/** + * Parses and caches the given `template` according to the given `tags` or + * `mustache.tags` if `tags` is omitted, and returns the array of tokens + * that is generated from the parse. + */ +Writer.prototype.parse = function parse (template, tags) { + var cache = this.templateCache; + var cacheKey = template + ':' + (tags || mustache.tags).join(':'); + var isCacheEnabled = typeof cache !== 'undefined'; + var tokens = isCacheEnabled ? cache.get(cacheKey) : undefined; + + if (tokens == undefined) { + tokens = parseTemplate(template, tags); + isCacheEnabled && cache.set(cacheKey, tokens); + } + return tokens; +}; + +/** + * High-level method that is used to render the given `template` with + * the given `view`. + * + * The optional `partials` argument may be an object that contains the + * names and templates of partials that are used in the template. It may + * also be a function that is used to load partial templates on the fly + * that takes a single argument: the name of the partial. + * + * If the optional `config` argument is given here, then it should be an + * object with a `tags` attribute or an `escape` attribute or both. + * If an array is passed, then it will be interpreted the same way as + * a `tags` attribute on a `config` object. + * + * The `tags` attribute of a `config` object must be an array with two + * string values: the opening and closing tags used in the template (e.g. + * [ "<%", "%>" ]). The default is to mustache.tags. + * + * The `escape` attribute of a `config` object must be a function which + * accepts a string as input and outputs a safely escaped string. + * If an `escape` function is not provided, then an HTML-safe string + * escaping function is used as the default. + */ +Writer.prototype.render = function render (template, view, partials, config) { + var tags = this.getConfigTags(config); + var tokens = this.parse(template, tags); + var context = (view instanceof Context) ? view : new Context(view, undefined); + return this.renderTokens(tokens, context, partials, template, config); +}; + +/** + * Low-level method that renders the given array of `tokens` using + * the given `context` and `partials`. + * + * Note: The `originalTemplate` is only ever used to extract the portion + * of the original template that was contained in a higher-order section. + * If the template doesn't use higher-order sections, this argument may + * be omitted. + */ +Writer.prototype.renderTokens = function renderTokens (tokens, context, partials, originalTemplate, config) { + var buffer = ''; + + var token, symbol, value; + for (var i = 0, numTokens = tokens.length; i < numTokens; ++i) { + value = undefined; + token = tokens[i]; + symbol = token[0]; + + if (symbol === '#') value = this.renderSection(token, context, partials, originalTemplate, config); + else if (symbol === '^') value = this.renderInverted(token, context, partials, originalTemplate, config); + else if (symbol === '>') value = this.renderPartial(token, context, partials, config); + else if (symbol === '&') value = this.unescapedValue(token, context); + else if (symbol === 'name') value = this.escapedValue(token, context, config); + else if (symbol === 'text') value = this.rawValue(token); + + if (value !== undefined) + buffer += value; + } + + return buffer; +}; + +Writer.prototype.renderSection = function renderSection (token, context, partials, originalTemplate, config) { + var self = this; + var buffer = ''; + var value = context.lookup(token[1]); + + // This function is used to render an arbitrary template + // in the current context by higher-order sections. + function subRender (template) { + return self.render(template, context, partials, config); + } + + if (!value) return; + + if (isArray(value)) { + for (var j = 0, valueLength = value.length; j < valueLength; ++j) { + buffer += this.renderTokens(token[4], context.push(value[j]), partials, originalTemplate, config); + } + } else if (typeof value === 'object' || typeof value === 'string' || typeof value === 'number') { + buffer += this.renderTokens(token[4], context.push(value), partials, originalTemplate, config); + } else if (isFunction(value)) { + if (typeof originalTemplate !== 'string') + throw new Error('Cannot use higher-order sections without the original template'); + + // Extract the portion of the original template that the section contains. + value = value.call(context.view, originalTemplate.slice(token[3], token[5]), subRender); + + if (value != null) + buffer += value; + } else { + buffer += this.renderTokens(token[4], context, partials, originalTemplate, config); + } + return buffer; +}; + +Writer.prototype.renderInverted = function renderInverted (token, context, partials, originalTemplate, config) { + var value = context.lookup(token[1]); + + // Use JavaScript's definition of falsy. Include empty arrays. + // See https://github.com/janl/mustache.js/issues/186 + if (!value || (isArray(value) && value.length === 0)) + return this.renderTokens(token[4], context, partials, originalTemplate, config); +}; + +Writer.prototype.indentPartial = function indentPartial (partial, indentation, lineHasNonSpace) { + var filteredIndentation = indentation.replace(/[^ \t]/g, ''); + var partialByNl = partial.split('\n'); + for (var i = 0; i < partialByNl.length; i++) { + if (partialByNl[i].length && (i > 0 || !lineHasNonSpace)) { + partialByNl[i] = filteredIndentation + partialByNl[i]; + } + } + return partialByNl.join('\n'); +}; + +Writer.prototype.renderPartial = function renderPartial (token, context, partials, config) { + if (!partials) return; + var tags = this.getConfigTags(config); + + var value = isFunction(partials) ? partials(token[1]) : partials[token[1]]; + if (value != null) { + var lineHasNonSpace = token[6]; + var tagIndex = token[5]; + var indentation = token[4]; + var indentedValue = value; + if (tagIndex == 0 && indentation) { + indentedValue = this.indentPartial(value, indentation, lineHasNonSpace); + } + var tokens = this.parse(indentedValue, tags); + return this.renderTokens(tokens, context, partials, indentedValue, config); + } +}; + +Writer.prototype.unescapedValue = function unescapedValue (token, context) { + var value = context.lookup(token[1]); + if (value != null) + return value; +}; + +Writer.prototype.escapedValue = function escapedValue (token, context, config) { + var escape = this.getConfigEscape(config) || mustache.escape; + var value = context.lookup(token[1]); + if (value != null) + return (typeof value === 'number' && escape === mustache.escape) ? String(value) : escape(value); +}; + +Writer.prototype.rawValue = function rawValue (token) { + return token[1]; +}; + +Writer.prototype.getConfigTags = function getConfigTags (config) { + if (isArray(config)) { + return config; + } + else if (config && typeof config === 'object') { + return config.tags; + } + else { + return undefined; + } +}; + +Writer.prototype.getConfigEscape = function getConfigEscape (config) { + if (config && typeof config === 'object' && !isArray(config)) { + return config.escape; + } + else { + return undefined; + } +}; + +var mustache = { + name: 'mustache.js', + version: '4.2.0', + tags: [ '{{', '}}' ], + clearCache: undefined, + escape: undefined, + parse: undefined, + render: undefined, + Scanner: undefined, + Context: undefined, + Writer: undefined, + /** + * Allows a user to override the default caching strategy, by providing an + * object with set, get and clear methods. This can also be used to disable + * the cache by setting it to the literal `undefined`. + */ + set templateCache (cache) { + defaultWriter.templateCache = cache; + }, + /** + * Gets the default or overridden caching object from the default writer. + */ + get templateCache () { + return defaultWriter.templateCache; + } +}; + +// All high-level mustache.* functions use this writer. +var defaultWriter = new Writer(); + +/** + * Clears all cached templates in the default writer. + */ +mustache.clearCache = function clearCache () { + return defaultWriter.clearCache(); +}; + +/** + * Parses and caches the given template in the default writer and returns the + * array of tokens it contains. Doing this ahead of time avoids the need to + * parse templates on the fly as they are rendered. + */ +mustache.parse = function parse (template, tags) { + return defaultWriter.parse(template, tags); +}; + +/** + * Renders the `template` with the given `view`, `partials`, and `config` + * using the default writer. + */ +mustache.render = function render (template, view, partials, config) { + if (typeof template !== 'string') { + throw new TypeError('Invalid template! Template should be a "string" ' + + 'but "' + typeStr(template) + '" was given as the first ' + + 'argument for mustache#render(template, view, partials)'); + } + + return defaultWriter.render(template, view, partials, config); +}; + +// Export the escaping function so that the user may override it. +// See https://github.com/janl/mustache.js/issues/244 +mustache.escape = escapeHtml; + +// Export these mainly for testing, but also for advanced usage. +mustache.Scanner = Scanner; +mustache.Context = Context; +mustache.Writer = Writer; + +// export default mustache; diff --git a/os3/search.php b/os3/search.php new file mode 100644 index 0000000..58995b3 --- /dev/null +++ b/os3/search.php @@ -0,0 +1,730 @@ + array(), + 'formatted' => array(), + 'cache' => array( + 'data' => '', + 'ip' => '', + 'stamp' => 0, + 'raw' => '' + ), + 'results' => array(), + 'json' => array(), + 'pages' => 1, + 'time' => microtime(true) +); + + +foreach ($_RDATA['s_weights'] as $key => $weight) + $_RDATA['s_weights'][$key] = (float)$weight; + + +// {{{{{ Create the Mustache template +$_TEMPLATE = new OS_Mustache(); +$_TEMPLATE->version = $_ODATA['version']; +$_TEMPLATE->limit_term_length = $_ODATA['s_limit_term_length']; + + +// Check if there are rows in the search database +if ($_RDATA['s_searchable_pages']) { + $_TEMPLATE->searchable = new stdClass(); + $_TEMPLATE->searchable->form_action = $_SERVER['REQUEST_URI']; + + if (!isset($_REQUEST['c']) || !isset($_RDATA['s_category_list'][$_REQUEST['c']])) + $_REQUEST['c'] = ''; + + if (count($_RDATA['s_category_list']) > 2) { + $_TEMPLATE->searchable->categories = new stdClass(); + $_TEMPLATE->searchable->categories->category_list = array(); + foreach ($_RDATA['s_category_list'] as $category => $count) { + $cat = new stdClass(); + $cat->name = ($category = '') ? 'All Categories' : $category; + $cat->value = $category; + $cat->selected = ($_REQUEST['c'] == $category); + $_TEMPLATE->searchable->categories->category_list[] = $cat; + } + } + + if (!isset($_REQUEST['q']) || !is_string($_REQUEST['q'])) + $_REQUEST['q'] = ''; + + $_REQUEST['q'] = preg_replace(array('/\s/', '/ {2,}/'), ' ', trim($_REQUEST['q'])); + + // If there is a text request + if ($_REQUEST['q']) { + + // Convert to UTF-8 from specified encoding + $_REQUEST['q'] = mb_convert_encoding($_REQUEST['q'], 'UTF-8', $_ODATA['s_charset']); + + if (strlen($_REQUEST['q']) > 127) { + $_REQUEST['q'] = substr($_REQUEST['q'], 0, 127); + $_TEMPLATE->addError('Search query truncated to maximum 127 characters'); + } + + $_TEMPLATE->searchable->request_q = $_REQUEST['q']; + + // Split request string on quotation marks (") + $request = explode('"', ' '.$_REQUEST['q'].' '); + for ($x = 0; $x < count($request) && count($_SDATA['terms']) < $_ODATA['s_limit_terms']; $x++) { + + // Every second + 1 group of terms just a list of terms + if (!($x % 2)) { + + // Split this list of terms on spaces + $request[$x] = explode(' ', $request[$x]); + + foreach ($request[$x] as $t) { + if (!$t) continue; + + // Leading + means important, a MUST match + if ($t[0] == '+') { + + // Just count it as a 'phrase' of one word, functionally equivalent + $_SDATA['terms'][] = array('phrase', substr($t, 1), false); + + // Leading - or ! means negative, a MUST exclude + } else if ($t[0] == '-' || $t[0] == '!') { + $_SDATA['terms'][] = array('exclude', substr($t, 1), false); + + // Restrict to a specific filetype (not yet implemented) + // Really, we'd only allow HTML, XML and PDF here, maybe JPG? + } else if (strpos('filetype:', $t) === 0) { + $t = trim(substr($t, 9)); + if ($t && isset($_RDATA['s_filetypes'][strtoupper($t)])) + $_SDATA['terms'][] = array('filetype', $t, false); + + // Else if the term is greater than the term length limit, add it + } else if (strlen($t) >= $_ODATA['s_limit_term_length']) + $_SDATA['terms'][] = array('term', $t, false); + } + + // Every second group of terms is a phrase, a MUST match + } else $_SDATA['terms'][] = array('phrase', $request[$x], false); + } + + + // If we successfully procured some terms + if (count($_SDATA['terms'])) { + $_TEMPLATE->searchable->searched = new stdClass(); + if ($_REQUEST['c'] != '') { + $_TEMPLATE->searchable->searched->category = new stdClass(); + $_TEMPLATE->searchable->searched->category->request_c = $_REQUEST['c']; + } + + // Prepare PCRE match text for each phrase and term + foreach ($_SDATA['terms'] as $key => list($type, $term, $pcre)) { + switch ($type) { + case 'filetype': + $_SDATA['formatted'][] = $type.':'.$term; + break; + + case 'exclude': + $_SDATA['formatted'][] = '-'.$term; + break; + + case 'phrase': + $_SDATA['formatted'][] = '"'.$term.'"'; + + case 'term': + if ($type == 'term') + $_SDATA['formatted'][] = $term; + + $_SDATA['terms'][$key][2] = preg_quote(strtolower($term), '/'); + foreach ($_RDATA['s_latin'] as $char => $latin) { + $_SDATA['terms'][$key][2] = str_replace($latin, $char, $_SDATA['terms'][$key][2]); + if (strlen($char) > 1) { + $_SDATA['terms'][$key][2] = str_replace($char, '('.$char.'|'.implode('|', $latin).')', $_SDATA['terms'][$key][2]); + } else $_SDATA['terms'][$key][2] = str_replace($char, '['.$char.implode('', $latin).']', $_SDATA['terms'][$key][2]); + } + $_SDATA['terms'][$key][2] = '/('.$_SDATA['terms'][$key][2].')/iu'; + + } + } + + + // Without this, category searches are merged, maybe okay? + // if ($_REQUEST['c'] != '') + // $_SDATA['formatted'][] = '('. + + + // Check if this search is already cached + $_SDATA['formatted'] = implode(' ', $_SDATA['formatted']); + $checkCache = $_DDATA['pdo']->prepare( + 'SELECT `stamp`, INET_NTOA(`ip`) AS `ip`, `cache` + FROM `'.$_DDATA['tbprefix'].'query` + WHERE `query`=:query AND `cache`<>\'\' + ORDER BY `stamp` DESC LIMIT 1;' + ); + $checkCache->execute(array('query' => $_SDATA['formatted'])); + $err = $checkCache->errorInfo(); + if ($err[0] == '00000') { + $checkCache = $checkCache->fetchAll(); + + // If we retrieved a matching row from the query log + if (count($checkCache)) { + $_SDATA['cache']['ip'] = $checkCache[0]['ip']; + $_SDATA['cache']['stamp'] = $checkCache[0]['stamp']; + $_SDATA['cache']['raw'] = $checkCache[0]['cache']; + $_SDATA['cache']['data'] = $checkCache[0]['cache']; + + // Try to gzunzip the cache data + if (function_exists('gzuncompress')) { + $checkGZ = gzuncompress($_SDATA['cache']['data']); + if ($checkGZ) $_SDATA['cache']['data'] = $checkGZ; + } + + // Try to json_decode the cache data + // If this step fails, assume there is no cache data + $checkJS = json_decode($_SDATA['cache']['data'], true); + $_SDATA['cache']['data'] = ($checkJS) ? $checkJS : ''; + } + + // Database error accessing the query log + } else $_TEMPLATE->addError('Error reading the search result cache'); + + + // ***** Nothing in the cache, so do an actual search + if (!is_array($_SDATA['cache']['data'])) { + + // Begin building the basic query + $searchSQL = ' + SELECT `url`, `category`, `content`, `content_mime`, `title`, + `description`, `keywords`, `weighted`, `priority` + FROM `'.$_DDATA['tbprefix'].'crawldata` + WHERE `flag_unlisted`=0 AND `priority`>0 AND'; + + // Restrict by category + if ($_REQUEST['c'] != '') + $searchSQL .= ' `category`=\''.addslashes($_REQUEST['c']).'\' AND '; + + // Show or do not show Orphans + if (!$_ODATA['s_show_orphans']) + $searchSQL .= ' `status`!=\'Orphan\' AND '; + + $ands = array(); + $ors = array(); + $negs = array(); + foreach ($_SDATA['terms'] as list($type, $term, $pcre)) { + switch ($type) { + case 'filetype': // Nothing for filetype yet + break; + + case 'exclude': + $negs[] = '`content` NOT LIKE \'%'.addslashes($term).'%\''; + $negs[] = '`url` NOT LIKE \'%'.addslashes($term).'%\''; + $negs[] = '`title` NOT LIKE \'%'.addslashes($term).'%\''; + $negs[] = '`description` NOT LIKE \'%'.addslashes($term).'%\''; + $negs[] = '`keywords` NOT LIKE \'%'.addslashes($term).'%\''; + $negs[] = '`weighted` NOT LIKE \'%'.addslashes($term).'%\''; + break; + + case 'phrase': + $ands[] = '('.implode(' OR ', array( + '`content` LIKE \'%'.addslashes($term).'%\'', + '`url` LIKE \'%'.addslashes($term).'%\'', + '`title` LIKE \'%'.addslashes($term).'%\'', + '`description` LIKE \'%'.addslashes($term).'%\'', + '`keywords` LIKE \'%'.addslashes($term).'%\'', + '`weighted` LIKE \'%'.addslashes($term).'%\'' + )).')'; + break; + + case 'term': + $ors[] = '`content` LIKE \'%'.addslashes($term).'%\''; + $ors[] = '`url` LIKE \'%'.addslashes($term).'%\''; + $ors[] = '`title` LIKE \'%'.addslashes($term).'%\''; + $ors[] = '`description` LIKE \'%'.addslashes($term).'%\''; + $ors[] = '`keywords` LIKE \'%'.addslashes($term).'%\''; + $ors[] = '`weighted` LIKE \'%'.addslashes($term).'%\''; + + } + } + + if (count($ands)) { + $searchSQL .= ' '.implode(' AND ', $ands).' '; + } else $searchSQL .= ' ('.implode(' OR ', $ors).') '; + + if (count($negs)) + $searchSQL .= ' AND '.implode(' AND ', $negs); + + // Execute the query + $searchQuery = $_DDATA['pdo']->query($searchSQL.';'); + $err = $searchQuery->errorInfo(); + if ($err[0] == '00000') { + $searchQuery = $searchQuery->fetchAll(); + + // Apply relevance to each listing and then sort + foreach ($searchQuery as $key => $row) { + $searchQuery[$key]['relevance'] = 0; + $searchQuery[$key]['multi'] = -1; + $searchQuery[$key]['phrase'] = 0; + + // Lowercase values for easy compare + $row['lc_content'] = strtolower($row['content']); + $row['lc_url'] = strtolower($row['url']); + $row['lc_title'] = strtolower($row['title']); + $row['lc_description'] = strtolower($row['description']); + $row['lc_keywords'] = strtolower($row['keywords']); + $row['lc_weighted'] = strtolower($row['weighted']); + + // Remove latin character accents + foreach ($_RDATA['s_latin'] as $char => $latin) { + $row['lc_content'] = str_replace($latin, $char, $row['lc_content']); + $row['lc_url'] = str_replace($latin, $char, $row['lc_url']); + $row['lc_title'] = str_replace($latin, $char, $row['lc_title']); + $row['lc_description'] = str_replace($latin, $char, $row['lc_description']); + $row['lc_keywords'] = str_replace($latin, $char, $row['lc_keywords']); + $row['lc_weighted'] = str_replace($latin, $char, $row['lc_weighted']); + } + + // Run through each term and check content for matches + foreach ($_SDATA['terms'] as list($type, $term, $pcre)) { + switch ($type) { + case 'filetype': break; + case 'exclude': break; + + case 'phrase': + $searchQuery[$key]['phrase']++; + + case 'term': + $term = strtolower($term); + foreach ($_RDATA['s_latin'] as $char => $latin) + $term = str_replace($latin, $char, $term); + + $pcreterm = '/\b'.preg_quote($term, '/').'/i'; + + // Give full points for every instance of a term + // that's at the beginning of a word or phrase + $i = preg_match_all($pcreterm, $row['lc_content']); + $j = preg_match_all($pcreterm, $row['lc_url']); + $k = preg_match_all($pcreterm, $row['lc_title']); + $l = preg_match_all($pcreterm, $row['lc_description']); + $m = preg_match_all($pcreterm, $row['lc_keywords']); + $n = preg_match_all($pcreterm, $row['lc_weighted']); + + if ($i || $j || $k || $l || $m || $n) + $searchQuery[$key]['multi']++; + + // Limit generic matches to a maximum of three + $a = $i + min(substr_count($row['lc_content'], $term), 3); + $b = $j + min(substr_count($row['lc_url'], $term), 3); + $c = $k + min(substr_count($row['lc_title'], $term), 3); + $d = $l + min(substr_count($row['lc_description'], $term), 3); + $e = $m + min(substr_count($row['lc_keywords'], $term), 3); + $f = $n + min(substr_count($row['lc_weighted'], $term), 3); + + $searchQuery[$key]['relevance'] += $a * $_RDATA['s_weights']['body']; + $searchQuery[$key]['relevance'] += $b * $_RDATA['s_weights']['url']; + $searchQuery[$key]['relevance'] += $c * $_RDATA['s_weights']['title']; + $searchQuery[$key]['relevance'] += $d * $_RDATA['s_weights']['description']; + $searchQuery[$key]['relevance'] += $e * $_RDATA['s_weights']['keywords']; + $searchQuery[$key]['relevance'] += $f * $_RDATA['s_weights']['css_value']; + + } + } + + // Calculate multipliers + $searchQuery[$key]['relevance'] *= $_RDATA['s_weights']['multi'] ** $searchQuery[$key]['multi']; + $searchQuery[$key]['relevance'] *= $_RDATA['s_weights']['important'] ** $searchQuery[$key]['phrase']; + + $searchQuery[$key]['relevance'] *= $row['priority']; + } + + // Sort the list by relevance value + usort($searchQuery, function($a, $b) { + if ($b['relevance'] == $a['relevance']) return 0; + return ($b['relevance'] > $a['relevance']) ? 1 : -1; + }); + + // Normalize results from 0 - 100 and delete results with + // relevance values < 5% of the top result + for ($x = count($searchQuery) - 1; $x >= 0; $x--) { + if ($searchQuery[0]['relevance'] * 0.05 <= $searchQuery[$x]['relevance']) { + $searchQuery[$x]['relevance'] /= $searchQuery[0]['relevance'] * 0.01; + } else unset($searchQuery[$x]); + } + + // The final results list is the top slice of this data + // limited by the 's_limit_results' value + $_SDATA['results'] = array_slice($searchQuery, 0, $_ODATA['s_limit_results']); + + + // Now loop through the remaining results to generate the + // proper match text for each + foreach ($_SDATA['results'] as $key => $row) { + $_SDATA['results'][$key]['matchtext'] = array(); + + // Add the page description to use as a default match text + if (trim($row['description'])) { + $_SDATA['results'][$key]['matchtext'][] = array( + 'rank' => 0, + 'text' => substr($row['description'], 0, $_ODATA['s_limit_matchtext']) + ); + } + + // Loop through each term to capture matchtexts + foreach ($_SDATA['terms'] as list($type, $term, $pcre)) { + switch ($type) { + case 'filetype': break; + case 'exclude': break; + + case 'phrase': + case 'term': + + // Split the content on the current term + $splitter = preg_split($pcre, $row['content'], 0, + PREG_SPLIT_NO_EMPTY | PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_OFFSET_CAPTURE); + + // For each match, gather the appropriate amount of match + // text from either side of it + foreach ($splitter as $split) { + if (preg_match($pcre, $split[0]) || count($splitter) == 1) { + if (count($splitter) == 1) { + // Grab some random content if there were no + // matches in the content + $offset = mt_rand(0, strlen($row['content']) - $_ODATA['s_limit_matchtext']); + } else $offset = floor(max(0, $split[1] - (strlen($term) + $_ODATA['s_limit_matchtext']) / 2)); + $match = trim(substr($row['content'], $offset, $_ODATA['s_limit_matchtext'])); + + // Add appropriate ellipses + if ($offset + ((strlen($term) + $_ODATA['s_limit_matchtext']) / 2) < strlen($row['content'])) + $match .= "\u{2026}"; + + if ($offset) $match = "\u{2026}".$match; + + $_SDATA['results'][$key]['matchtext'][] = array( + 'rank' => 0, + 'text' => $match + ); + } + } + + } + } + + // For each found match text, add a point for every time a + // term is found in the match text; triple points for phrase + // or important (+) matches + foreach ($_SDATA['results'][$key]['matchtext'] as $mkey => $matchtext) { + foreach ($_SDATA['terms'] as $tkey => list($type, $term, $pcre)) { + switch ($type) { + case 'filetype': break; + case 'exclude': break; + + case 'phrase': + case 'term': + $points = preg_match_all($pcre, $matchtext['text']); // / ($tkey + 1); + if ($type == 'phrase') $points *= 3; + $_SDATA['results'][$key]['matchtext'][$mkey]['rank'] += $points; + + } + } + } + + // Sort the match texts by score + usort($_SDATA['results'][$key]['matchtext'], function($a , $b) { + if ($b['rank'] == $a['rank']) return 0; + return ($b['rank'] > $a['rank']) ? 1 : -1; + }); + + // Use the top-ranked match text as the official match text + // Run an mb_convert_encoding() in case we chopped a UTF-8 + // character in the middle of its bytes + $_SDATA['results'][$key]['matchtext'] = mb_convert_encoding( + $_SDATA['results'][$key]['matchtext'][0]['text'], + 'UTF-8', + 'UTF-8' + ); + + // Unset result values we no longer need so they don't + // bloat the cache unnecessarily + unset($_SDATA['results'][$key]['content']); + unset($_SDATA['results'][$key]['keywords']); + unset($_SDATA['results'][$key]['weighted']); + unset($_SDATA['results'][$key]['multi']); + unset($_SDATA['results'][$key]['phrase']); + } + + } else $_TEMPLATE->addError('Database error reading results: '.$err[2]); + + + // ***** Else this is a cached set of results + } else $_SDATA['results'] = $_SDATA['cache']['data']; + + + // Limit $_REQUEST['page'] to within boundaries + $_REQUEST['page'] = (isset($_REQUEST['page'])) ? max(1, (int)$_REQUEST['page']) : 1; + $_SDATA['pages'] = ceil(count($_SDATA['results']) / $_ODATA['s_results_pagination']); + $_REQUEST['page'] = min($_SDATA['pages'], $_REQUEST['page']); + + if (!isset($_REQUEST['json']) || $_REQUEST['json'] !== 'true') + $_REQUEST['json'] = ''; + + // Database log (and potentially cache) this page only if: + // - This is not a JSON output request + // - The user is visiting page 1 of results + // - Their IP does not match the IP of the previous request for + // this same query + // - ... but if their IP *does* match, check that their last + // request for this same query was more than ten seconds ago + if ($_REQUEST['json'] !== 'true' && $_REQUEST['page'] == 1 && + ($_SDATA['cache']['ip'] != $_SERVER['REMOTE_ADDR'] || + $_SDATA['cache']['stamp'] + 10 < time())) { + + // Delete the cache from all other searches for this query + $clear = $_DDATA['pdo']->prepare( + 'UPDATE `'.$_DDATA['tbprefix'].'query` SET `cache`=\'\' + WHERE `query`=:query;' + ); + $clear->execute(array('query' => $_SDATA['formatted'])); + $err = $clear->errorInfo(); + if ($err[0] != '00000') + $_TEMPLATE->addError('Could not clear previous search cache: '.$err[2]); + + // If we are caching search results + if ($_ODATA['s_limit_cache']) { + + // If this search query hasn't been cached yet + if (!is_array($_SDATA['cache']['data'])) { + + // JSON encode and potentially gzip the results for storage + $searchCache = json_encode($_SDATA['results'], JSON_INVALID_UTF8_IGNORE); + if (function_exists('gzcompress')) + $searchCache = gzcompress($searchCache); + + // else use the cache we retrieved from the database + } else $searchCache = $_SDATA['cache']['raw']; + + } else $searchCache = ''; + + $insertQuery = $_DDATA['pdo']->prepare( + 'INSERT INTO `'.$_DDATA['tbprefix'].'query` SET + `query`=:query, + `results`=:results, + `stamp`=UNIX_TIMESTAMP(), + `ip`=INET_ATON(:ipaddr), + `cache`=:cache + ;' + ); + $insertQuery->execute(array( + 'query' => $_SDATA['formatted'], + 'results' => count($_SDATA['results']), + 'ipaddr' => $_SERVER['REMOTE_ADDR'], + 'cache' => $searchCache + )); + if (!$insertQuery->rowCount()) { + $_TEMPLATE->addError('Could not cache search results'); + $err = $insertQuery->errorInfo(); + if ($err[0] != '00000') + $_TEMPLATE->addError('MySQL error: '.$err[2]); + } + } + + + // ***** We have completed searching and caching! ***** + // Now it's time to focus on how we will format the data we + // obtained for display to the viewer + + + // Get a slice of the results that corresponds to the current + // search results pagination page we are on + $resultsPage = array_slice( + $_SDATA['results'], + ($_REQUEST['page'] - 1) * $_ODATA['s_results_pagination'], + $_ODATA['s_results_pagination'] + ); + + // If we have more than zero results... + if (count($resultsPage)) { + $_TEMPLATE->searchable->searched->results = new stdClass(); + $_TEMPLATE->searchable->searched->results->result_list = array(); + + // Prepare PCRE for removing base domains + if (count($_RDATA['s_crawldata_domains']) == 1) + $repStr = '/^'.preg_quote(key($_RDATA['s_crawldata_domains']), '/').'/'; + + // Do a last once-over of the results + foreach ($resultsPage as $key => $result) { + $_RESULT = new stdClass(); + + $_RESULT->filetype = ''; + foreach ($_RDATA['s_filetypes'] as $type => $mimes) + foreach ($mimes as $mime) + if ($result['content_mime'] == $mime) + $_RESULT->filetype = $type; + + // Don't display filetype of HTML pages + if (!$_ODATA['s_show_filetype_html']) + if ($_RESULT->filetype == 'HTML') + $_RESULT->filetype = ''; + + if ($_RESULT->filetype) + $_RESULT->filetype = '['.$_RESULT->filetype.']'; + + // Don't display category if there's only one + if (count($_RDATA['s_category_list']) > 2) { + $_RESULT->category = $result['category']; + } else $_RESULT->category = ''; + + // Format relevance + $_RESULT->relevance = number_format($result['relevance'], 2, '.', ''); + + // Remove base domain from URL if they are all the same + if (count($_RDATA['s_crawldata_domains']) == 1) + $result['url'] = preg_replace($repStr, '', $result['url']); + + // Highlight the terms in the title, url and matchtext + $_RESULT->title = htmlspecialchars($result['title']); + $_RESULT->url = htmlspecialchars($result['url']); + $_RESULT->matchtext = htmlspecialchars($result['matchtext']); + $_RESULT->description = htmlspecialchars($result['description']); + $_RESULT->title_highlight = $_RESULT->title; + $_RESULT->url_highlight = $_RESULT->url; + $_RESULT->matchtext_highlight = $_RESULT->matchtext; + $_RESULT->description_highlight = $_RESULT->description; + + foreach ($_SDATA['terms'] as list($type, $term, $pcre)) { + switch ($type) { + case 'filetype': break; + case 'exclude': break; + + case 'phrase': + case 'term': + $_RESULT->title_highlight = preg_replace($pcre, '$1', $_RESULT->title_highlight); + $_RESULT->url_highlight = preg_replace($pcre, '$1', $_RESULT->url_highlight); + $_RESULT->matchtext_highlight = preg_replace($pcre, '$1', $_RESULT->matchtext_highlight); + $_RESULT->description_highlight = preg_replace($pcre, '$1', $_RESULT->description_highlight); + + } + } + + // Convert output back to $_ODATA['s_charset'] before storing + if (strtoupper($_ODATA['s_charset']) != 'UTF-8') { + $_RESULT = json_encode($_RESULT, JSON_INVALID_UTF8_IGNORE); + $_RESULT = mb_convert_encoding($_RESULT, $_ODATA['s_charset'], 'UTF-8'); + $_RESULT = json_decode($_RESULT, true); + } + + $_TEMPLATE->searchable->searched->results->result_list[] = $_RESULT; + } + + // If there are more than just one page of results, prepare all + // the pagination variables for the template + if ($_SDATA['pages'] > 1) { + $pagination = new stdClass(); + $pagination->page_gt1 = ($_REQUEST['page'] > 1); + $pagination->page_minus1 = $_REQUEST['page'] - 1; + $pagination->page_list = array(); + for ($x = 1; $x <= $_SDATA['pages']; $x++) { + $page = new stdClass(); + $page->index = $x; + $page->current = ($x == $_REQUEST['page']); + $pagination->page_list[] = $page; + } + $pagination->page_ltpages = ($_REQUEST['page'] < $_SDATA['pages']); + $pagination->page_plus1 = $_REQUEST['page'] + 1; + $_TEMPLATE->searchable->searched->results->pagination = $pagination; + } + + // Final numerical and stopwatch time values + $_TEMPLATE->searchable->searched->results->from = min(count($_SDATA['results']), ($_REQUEST['page'] - 1) * $_ODATA['s_results_pagination'] + 1); + $_TEMPLATE->searchable->searched->results->to = min(count($_SDATA['results']), $_REQUEST['page'] * $_ODATA['s_results_pagination']); + $_TEMPLATE->searchable->searched->results->of = count($_SDATA['results']); + $_TEMPLATE->searchable->searched->results->in = number_format(microtime(true) - $_SDATA['time'], 2, '.', ''); + + $_SDATA['json'] = array_slice($_TEMPLATE->searchable->searched->results->result_list, 0, 5); + + } // No results + + } // No valid terms + + // Convert request query back to $_ODATA['s_charset'] before exiting + if (strtoupper($_ODATA['s_charset']) != 'UTF-8') + $_REQUEST['q'] = mb_convert_encoding($_REQUEST['q'], $_ODATA['s_charset'], 'UTF-8'); + + } // No request data + +} // No searchable pages in search database + + + +// ***** Trigger another crawl +if ($_ODATA['sp_interval'] && + time() - $_ODATA['sp_time_end_success'] > $_ODATA['sp_interval'] * 3600 && + !OS_getValue('sp_crawling')) { + + // If we can only trigger the crawl during certain time period + if ($_ODATA['sp_interval_start'] != $_ODATA['sp_interval_stop']) { + $timeNow = new DateTime(); + $timeStart = DateTime::createFromFormat('H:i:s', $_ODATA['sp_interval_start']); + $timeStop = DateTime::createFromFormat('H:i:s', $_ODATA['sp_interval_stop']); + + // Move PM start times back one day + if ($timeStart > $timeStop) $timeStart->modify('-1 day'); + + // Make sure at least the stop time is in the future + if ($timeNow > $timeStop) { + $timeStart->modify('+1 day'); + $timeStop->modify('+1 day'); + } + + $allowCrawl = ($timeStart < $timeNow && $timeNow < $timeStop); + + // Otherwise always allow the crawl + } else $allowCrawl = true; + + if ($allowCrawl) { + + // Set the key for initiating the crawler + $md5 = md5(hrtime(true)); + OS_setValue('sp_key', $md5); + + // ***** Initialize the cURL connection + $_cURL = OS_getConnection(); + if ($_cURL) { + + // Customize this cURL connection + curl_setopt($_cURL, CURLOPT_POST, true); + curl_setopt($_cURL, CURLOPT_POSTFIELDS, json_encode(array( + 'action' => 'crawl', + 'sp_key' => $_ODATA['sp_key'] + ))); + curl_setopt($_cURL, CURLOPT_HTTPHEADER, array( + 'Content-type: application/json; charset='.$_ODATA['s_charset'] + )); + curl_setopt($_cURL, CURLOPT_CONNECTTIMEOUT, 1); + curl_setopt($_cURL, CURLOPT_TIMEOUT, 1); + + $crawlerDir = str_replace($_SERVER['DOCUMENT_ROOT'], '', __DIR__); + $crawlerURL = $_ODATA['admin_install_domain'].$crawlerDir.'/crawler.php'; + curl_setopt($_cURL, CURLOPT_URL, str_replace(' ', '%20', $crawlerURL)); + + curl_exec($_cURL); + + // Error code 28 (timeout) is okay + $errno = curl_errno($_cURL); + if ($errno && $errno != 28) { + $error = curl_error($_cURL); + if ($error) $_TEMPLATE->addError($error); // Hide this? + } + + curl_close($_cURL); + + } // Could not create a connection, but don't let the user know + } +} + + +// Output JSON and exit if requested +if (isset($_REQUEST['json']) && $_REQUEST['json'] === 'true') { + header('Content-type: application/json; charset='.$_ODATA['s_charset']); + die(json_encode($_SDATA['json'], JSON_INVALID_UTF8_IGNORE)); +} ?> \ No newline at end of file