@Koalaknightmi/

bible quiz

Nodejs

No description

fork
loading
Files
  • index.js
  • public
  • veiws
  • .babelrc
  • CODE_OF_CONDUCT.md
  • functions.js
  • hebrews-peter.txt
  • package-lock.json
  • package.json
  • watch.json
  • webpack.config.js
  • webpackRunner.js

This Plugin Crashed!

Error: Error: must not create an existing file {"type":"CREATE_FILE","wid":"0.48596911921042274","path":"index.js","file":{"path":"index.js","content":{"asEncoding":{"base64":"dmFyIHJlcXMgPSAwOy8vaHR0cHM6Ly9iaXQubHkvMkw3dUhEagpjb25zdCBleHByZXNzID0gcmVxdWlyZSgnZXhwcmVzcycpOwpjb25zdCBzb2NrZXRpbyA9IHJlcXVpcmUoJ3NvY2tldC5pbycpOwpjb25zdCBodHRwID0gcmVxdWlyZSgnaHR0cCcpOwpjb25zdCBhcHAgPSBleHByZXNzKCk7CmNvbnN0IHNlcnZlciA9IGh0dHAuU2VydmVyKGFwcCk7CnZhciBwMnAgPSByZXF1aXJlKCdzb2NrZXQuaW8tcDJwLXNlcnZlcicpLlNlcnZlcjsKY29uc3QgaW8gPSBzb2NrZXRpbyhzZXJ2ZXIpOyAvLyBBdHRhY2ggc29ja2V0LmlvIHRvIG91ciBzZXJ2ZXIKY29uc3QgZW1haWwgPSAiTmF6YXJlbmVCaWJsZVF1aXpPbmxpbmVAYmlibGUtcXVpei1vbmxpbmUuZ2xpdGNoLm1lIjsKY29uc3QgaGJzID0gcmVxdWlyZSgnaGJzJyk7CmNvbnN0IHdlYlB1c2ggPSByZXF1aXJlKCd3ZWItcHVzaCcpOwp2YXIgYm9keVBhcnNlciA9IHJlcXVpcmUoJ2JvZHktcGFyc2VyJyk7CnZhciBzZXJ2ZUluZGV4ID0gcmVxdWlyZSgnc2VydmUtaW5kZXgnKTsKdmFyIHNlcnZlU3RhdGljID0gcmVxdWlyZSgnc2VydmUtc3RhdGljJyk7CnZhciBmcyA9IHJlcXVpcmUoJ2ZzJyk7CmNvbnN0IGZyYW1lZ3VhcmQgPSByZXF1aXJlKCdmcmFtZWd1YXJkJyk7CmNvbnN0IGZpbGVVcGxvYWQgPSByZXF1aXJlKCdleHByZXNzLWZpbGV1cGxvYWQnKTsKdmFyIGdsaWNrbyA9IHJlcXVpcmUoImdsaWNrby10d28iKQpjb25zdCBmID0gcmVxdWlyZSgiLi9mdW5jdGlvbnMiKTsKdmFyIEppbXAgPSByZXF1aXJlKCdqaW1wJyk7CnJlcXVpcmUoJy4vd2VicGFja1J1bm5lcicpOwpjb25zdCB0aXRsZXMgPSBbCiAge3RpdGxlOiJiaWJsZVN1cGVyR3JhbmRNYXN0ZXIiLHJlcXVpcmVkcnQ6MjcwMCxsb3NzcnQ6MjYwMCxhYnI6IkJTR00ifSwKICB7dGl0bGU6ImJpYmxlR3JhbmRNYXN0ZXIiLHJlcXVpcmVkcnQ6MjUwMCxsb3NzcnQ6MjQwMCxhYnI6IkJHTSJ9LAogIHt0aXRsZToiYmlibGVNYXN0ZXIiLHJlcXVpcmVkcnQ6MjIwMCxsb3NzcnQ6MjEwMCxhYnI6IkJNIn0sCiAge3RpdGxlOiJiaWJsZUV4cGVydCIscmVxdWlyZWRydDoyMDAwLGxvc3NydDoxOTAwLGFicjoiQkUifQpdOwoKLy9jb25zdCBkYiA9IHJlcXVpcmUoIi4vZGF0YWJhc2UiKTsKLyphcHAudXNlKGZyYW1lZ3VhcmQoewogIGFjdGlvbjogJ1NBTUVPUklHSU4nCn0pKSovCmFwcC51c2UoZXhwcmVzcy5qc29uKCkpOyAvLyBmb3IgcGFyc2luZyBhcHBsaWNhdGlvbi9qc29uCmFwcC51c2UoZXhwcmVzcy51cmxlbmNvZGVkKHsKICBleHRlbmRlZDogdHJ1ZQp9KSk7IC8vIGZvciBwYXJzaW5nIGFwcGxpY2F0aW9uL3gtd3d3LWZvcm0tdXJsZW5jb2RlZAphcHAudXNlKGZpbGVVcGxvYWQoKSk7CmFwcC51c2UoJy9maWxlcycsIHNlcnZlSW5kZXgoJy8nLCB7CiAgJ2ljb25zJzogdHJ1ZQp9KSk7CmFwcC51c2UoJy9maWxlcycsIHNlcnZlU3RhdGljKCcvJykpOwphcHAucG9zdCgiL3Rlc3QiLCAocmVxLCByZXMpID0+IHsKICBjb25zb2xlLmxvZyhyZXEuYm9keSk7IC8vIHNvIHdoZW4geW91IHJ1biAKICAvKgogIGZldGNoKCcvdGVzdCcsIHsKICAgICAgICBtZXRob2Q6ICdQT1NUJywgLy8gKkdFVCwgUE9TVCwgUFVULCBERUxFVEUsIGV0Yy4KICAgICAgICBoZWFkZXJzOiB7ICdDb250ZW50LVR5cGUnOiAnYXBwbGljYXRpb24vanNvbicgfSwg8J+RiCBkb24ndCBmb3JnZXQgdGhpcyEgdGh4CiAgICAgICAgYm9keTogSlNPTi5zdHJpbmdpZnkoe2E6IDEsIGI6IDJ9KSwgLy8gYm9keSBkYXRhIHR5cGUgbXVzdCBtYXRjaCAiQ29udGVudC1UeXBlIiBoZWFkZXIKICAgIH0pOwogICAgdGhhdCBpbiB0aGUgY29uc29sZSB3aGVuIHZlaXdpbmcgdGhlIHBhZ2UgaXRzIHNvcHBvc2VkIHRvIGxvZyB0aGUgYm9keSBidXQgaXQgaXN0IHdvcmtpbmcgcGx6IGhlbHAKICAqLwogIGNvbnNvbGUubG9nKCdmb3JtIGJvZHk6JywgcmVxLmJvZHksIHJlcS5nZXQoJ2NvbnRlbnQtdHlwZScpKTsKICByZXMuZW5kKCk7IC8vIPCfkYggZG9uJ3QgZm9yZ2V0IHRoaXMhCn0pOwovKmNvbnN0IHRlc3RGb2xkZXIgPSBfX2Rpcm5hbWUrJy9wdWJsaWMvY3NzJzsKCmZzLnJlYWRkaXIodGVzdEZvbGRlciwgKGVyciwgZmlsZXMpID0+IHsKICBmaWxlcy5mb3JFYWNoKGZpbGUgPT4gewogICAgY29uc29sZS5sb2coIlwiL2Nzcy8iK2ZpbGUrIlwiLCIpOwogIH0pOwp9KTsqLwoKaWYgKCFwcm9jZXNzLmVudi5WQVBJRF9QVUJMSUNfS0VZIHx8ICFwcm9jZXNzLmVudi5WQVBJRF9QUklWQVRFX0tFWSkgewogIGNvbnNvbGUubG9nKCJZb3UgbXVzdCBzZXQgdGhlIFZBUElEX1BVQkxJQ19LRVkgYW5kIFZBUElEX1BSSVZBVEVfS0VZICIgKyAiZW52aXJvbm1lbnQgdmFyaWFibGVzLiBZb3UgY2FuIHVzZSB0aGUgZm9sbG93aW5nIG9uZXM6Iik7CiAgY29uc29sZS5sb2cod2ViUHVzaC5nZW5lcmF0ZVZBUElES2V5cygpKTsKICAvL3JldHVybjsKfQp3ZWJQdXNoLnNldFZhcGlkRGV0YWlscygnaHR0cHM6Ly9zZXJ2aWNld29ya2UucnMvJywgcHJvY2Vzcy5lbnYuVkFQSURfUFVCTElDX0tFWSwgcHJvY2Vzcy5lbnYuVkFQSURfUFJJVkFURV9LRVkpOwpjb25zdCBvcHRpb25zID0gewogIFRUTDogMSAqIDYwICogNjAgKiAyNAp9Owpjb25zdCBhZG1pbiA9IHJlcXVpcmUoJ2ZpcmViYXNlLWFkbWluJyk7CnZhciBzZXJ2aWNlQWNjb3VudCA9IHsKICAidHlwZSI6IHByb2Nlc3MuZW52LlNBdHlwZSwKICAicHJvamVjdF9pZCI6IHByb2Nlc3MuZW52LlNBcHJvamVjdF9pZCwKICAicHJpdmF0ZV9rZXlfaWQiOiBwcm9jZXNzLmVudi5TQXByaXZhdGVfa2V5X2lkLAogICJwcml2YXRlX2tleSI6IHByb2Nlc3MuZW52LlNBcHJpdmF0ZV9rZXksCiAgImNsaWVudF9lbWFpbCI6IHByb2Nlc3MuZW52LlNBY2xpZW50X2VtYWlsLAogICJjbGllbnRfaWQiOiBwcm9jZXNzLmVudi5TQWNsaWVudF9pZCwKICAiYXV0aF91cmkiOiBwcm9jZXNzLmVudi5TQWF1dGhfdXJpLAogICJ0b2tlbl91cmkiOiBwcm9jZXNzLmVudi5TQXRva2VuX3VyaSwKICAiYXV0aF9wcm92aWRlcl94NTA5X2NlcnRfdXJsIjogcHJvY2Vzcy5lbnYuU0FhdXRoX3Byb3ZpZGVyX3g1MDlfY2VydF91cmwsCiAgImNsaWVudF94NTA5X2NlcnRfdXJsIjogcHJvY2Vzcy5lbnYuU0FjbGllbnRfeDUwOV9jZXJ0X3VybAp9OwphZG1pbi5pbml0aWFsaXplQXBwKHsKICBjcmVkZW50aWFsOiBhZG1pbi5jcmVkZW50aWFsLmNlcnQoc2VydmljZUFjY291bnQpLAogIGRhdGFiYXNlVVJMOiAiaHR0cHM6Ly9iaWJsZS1xdWl6LWUxZWY0LmZpcmViYXNlaW8uY29tIgp9KTsKLy92YXIgc3RvcmFnZVJlZiA9IGFkbWluLnN0b3JhZ2UoKS5idWNrZXQoKTsKdmFyIGRiID0gYWRtaW4uZmlyZXN0b3JlKCk7CnZhciB1c2Vyc3JlZiA9IGRiLmNvbGxlY3Rpb24oInVzZXJzIik7CnZhciBzdWJzcmVmID0gZGIuY29sbGVjdGlvbigic3VicyIpOwp2YXIgdHNjb3Jlc3JlZiA9IGRiLmNvbGxlY3Rpb24oInR5cGVxdWl6emluZ3Njb3JlcyIpOwp2YXIgY2hhdHJlZiA9IGRiLmNvbGxlY3Rpb24oImNoYXRzIik7CmxldCBGaWVsZFZhbHVlID0gYWRtaW4uZmlyZXN0b3JlLkZpZWxkVmFsdWU7CnZhciB1c2VydGltb3V0cyA9IHt9CnZhciBwYXlsb2FkID0gImhpIHRoZXJlIjsKLy92YXIgU2VxdWVsaXplID0gcmVxdWlyZSgnc2VxdWVsaXplJyk7Ci8vY29uc3QgT3AgPSBTZXF1ZWxpemUuT3A7CnZhciBpcCA9ICIiOwp2YXIgbG9nID0gY29uc29sZS5sb2c7CnZhciBjaGF0ID0gW107CnZhciBvbmxpbmVwbHMgPSB7fTsKdmFyIGdhbWVyb29tcyA9IHt9Owp2YXIgcGxheWVyc0RBVEEgPSB7fTsKdmFyIGVtYWlsbXNnID0gYDxiPndlbGNvbWUgdG8gTmF6YXJlbmUgQmlibGUgUXVpemluZyBPbmxpbmU8L2I+IDxicj4KV2UgaG9wZSB0aGF0IHlvdSBlbmpveSB5b3VyIGV4cGVyaWVuY2Ugd2l0aCBvdXIgc2l0ZTxicj4KaWYgeW91IGhhdmUgYW50IHF1ZXN0aW9uIG9yIGNvbmNlcm5zIHBseiBlbWFpbCA8YSBocmVmID0gIm1haWx0bzpiaWJsZS1xdWl6LWRldmVsZXBlcnNAZ29vZ2xlZ3JvdXBzLmNvbSI+YmlibGUtcXVpei1kZXZlbGVwZXJzQGdvb2dsZWdyb3Vwcy5jb208L2E+YCAKdmFyIHNlbmRtYWlsID0gZi5zZW5kbWFpbAp2YXIgbmV3RCA9IGZ1bmN0aW9uIChjLCBuLCBkYXRhKSB7CiAgbGV0IGRvY1JlZiA9IGRiLmNvbGxlY3Rpb24oYykuZG9jKG4pOwogIGRhdGEuY3JlYXRlZEF0ID0gbmV3IERhdGUoKS50b0lTT1N0cmluZygpOwogIGRhdGEudXBkYXRlZEF0ID0gbmV3IERhdGUoKS50b0lTT1N0cmluZygpOwogIGRvY1JlZi5zZXQoZGF0YSk7Cn0KdmFyIG5ld0QyID0gZnVuY3Rpb24gKGMsIGRhdGEsIHQpIHsKICBkYXRhLmNyZWF0ZWRBdCA9IG5ldyBEYXRlKCkudG9JU09TdHJpbmcoKTsKICBkYXRhLnVwZGF0ZWRBdCA9IG5ldyBEYXRlKCkudG9JU09TdHJpbmcoKTsKICBsZXQgYWRkRG9jID0gZGIuY29sbGVjdGlvbihjKS5hZGQoZGF0YSkudGhlbihyZWYgPT4gewogICAgaWYgKHQpIHsKICAgICAgdChyZWYpCiAgICB9CiAgfSk7Cn0KdmFyIHVwZGF0ZU9uZSA9IGZ1bmN0aW9uIChjLCBuLCBkYXRhKSB7CiAgbGV0IGRSZWYgPSBkYi5jb2xsZWN0aW9uKGMpLmRvYyhuKTsKICBkYXRhLnVwZGF0ZWRBdCA9IEZpZWxkVmFsdWUuc2VydmVyVGltZXN0YW1wKCk7CiAgZFJlZi51cGRhdGUoZGF0YSk7Cn0KdmFyIG9ubGluZXBsYXllcnMgPSB7fTsKdmFyIEFkbWlucyA9IFsia29hbGFzdHJpa2VybWkiXTsKLy9zZW5kbWFpbCgia29hbGFzdHJpa2VybWlAZ21haWwuY29tIikKLyp2YXIgdXNlcnMyID0gWwoiSmFpZGVubWNkQGljbG91ZC5jb20iLAoiS29hbGFrbmlnaHRtaSIsCiJNYWRkaWVKb3kiLAoiUGluayBrb2FsYSIsCiJSaXZlciBnYWwiLAoia29hbGFzdHJpa2VybWkiLAoia29hbGFzdHJpa2VybWkyIiwKImtvYWxhc3RyaWtlcm1pNyIsCiJtYWRkaWUyMDA1IgpdCmZvcih2YXIgaSA9IDA7aTx1c2VyczIubGVuZ3RoO2krKyl7CnVwZGF0ZU9uZSgidXNlcnMiLCB1c2VyczJbaV0sewogIHRpdGxlZDpmYWxzZSwKICB0aXRsZToiIiwKICB0YWJyOiIiCn0pCn0qLwovL3ZhciBjaGF0cm9vbXMgPSB7fTsKdmFyIGdvX3AycCA9IGZ1bmN0aW9uIChzb2NrZXQsIHJvb20pIHsKICBwMnBzZXJ2ZXIoc29ja2V0LCBudWxsLCByb29tKQp9CnZhciBwdXNoID0gcHVzaCA9IChvcHQsdG8sd2ViUHVzaCkgPT4gewogIGlmICh0byA9PT0gIiIpIHsKICAgIGxldCBxdWVyeSA9IHN1YnNyZWYuZ2V0KCkKICAudGhlbihzdWJzID0+IHsgCiAgICBzdWJzLmZvckVhY2goc3ViID0+IHsKICAgICAgLy9jb25zb2xlLmxvZyhzdWIuZGF0YSgpLnN1Yik7CiAgICAgICAgcmV0dXJuIHdlYlB1c2guc2VuZE5vdGlmaWNhdGlvbihzdWIuZGF0YSgpLnN1Yiwgb3B0KS5jYXRjaCgoZXJyKSA9PiB7CiAgICAgICAgICBpZiAoZXJyLnN0YXR1c0NvZGUgPT09IDQxMCkgewogICAgICAgICAgICBjb25zb2xlLmxvZygnU3Vic2NyaXB0aW9uIGlzIG5vIGxvbmdlciB2YWxpZDogJywgZXJyKTsKICAgICAgICAgICAgc3Vic3JlZi5kb2Moc3ViLmlkKS5kZWxldGUoKTsKICAgICAgICAgIH0KICAgICAgICAgIGVsc2UgewogICAgICAgICAgICBjb25zb2xlLmxvZyhlcnIpCiAgICAgICAgICB9CiAgICAgICAgfSk7CiAgICAgIH0pOwogICAgfSk7CiAgfQogIGVsc2UgewogICAgbGV0IHF1ZXJ5ID0gc3Vic3JlZi53aGVyZSgidXNlck5hbWUiLCI9PSIsdG8pLmdldCgpCiAgICAudGhlbihzdWJzID0+IHsgCiAgICAgIGlmIChzdWJzLmVtcHR5KSB7CiAgICAgICAgY29uc29sZS5sb2coJ05vIG1hdGNoaW5nIGRvY3VtZW50cy41Jyk7CiAgICAgICAgcmV0dXJuOwogICAgICB9ICAKICAgICAgc3Vicy5mb3JFYWNoKHN1YiA9PiB7CiAgICAgIC8vY29uc29sZS5sb2coc3ViLmRhdGEoKS5zdWIpOwogICAgICAgIHJldHVybiB3ZWJQdXNoLnNlbmROb3RpZmljYXRpb24oc3ViLmRhdGEoKS5zdWIsIG9wdCkuY2F0Y2goKGVycikgPT4gewogICAgICAgICAgaWYgKGVyci5zdGF0dXNDb2RlID09PSA0MTApIHsKICAgICAgICAgICAgY29uc29sZS5sb2coJ1N1YnNjcmlwdGlvbiBpcyBubyBsb25nZXIgdmFsaWQ6ICcsIGVycik7CiAgICAgICAgICAgIHN1YnNyZWYuZG9jKHN1Yi5pZCkuZGVsZXRlKCk7CiAgICAgICAgICB9CiAgICAgICAgICBlbHNlIHsKICAgICAgICAgICAgY29uc29sZS5sb2coZXJyKQogICAgICAgICAgfQogICAgICAgIH0pOwogICAgICB9KTsKICAgIH0pOwogIH0KfTsKdmFyIHRpbWVTaW5jZSA9IGYudGltZVNpbmNlOwp2YXIgdG90aW1lID0gZi50b3RpbWU7CnZhciBhc29ydCA9IGYuYXNvcnQ7Ci8vbWFkZSBieSBwb3J0ZXIgb24ga2hhbiBhY2FkZW15IGh0dHBzOi8vd3d3LktoYW5hY2FkZW15Lm9yZy9wcm9maWxlL2JhdHRsZWJveTIxClN0cmluZy5wcm90b3R5cGUucGFkID0gZi5wYWQ7Ci8qaGJzLnJlZ2lzdGVySGVscGVyKCdmaWx0ZXInLCBmdW5jdGlvbiAoY29udGV4dCwgb3B0aW9ucykgewogIHZhciBhID0gY29udGV4dC5zb3J0KGZ1bmN0aW9uIChhLCBiKSB7CiAgICByZXR1cm4gYi5zY29yZSAtIGEuc2NvcmU7CiAgfSk7CiAgcmV0dXJuIG9wdGlvbnMuZm4odGhpcykgKyAiPHRkPiIgKyBhWzBdLnNjb3JlICsgIjwvdGQ+Igp9KTsqLwpmdW5jdGlvbiBQbGF5ZXIoaWQsIHVzZXJuYW1lLCBjb2wsIHIpIHsKICB0aGlzLnVzZXIgPSB1c2VybmFtZTsKICB0aGlzLmlkID0gaWQ7CiAgdGhpcy5wY29sID0gY29sOwogIHRoaXMucHJhbmsgPSByOwogIHRoaXMuc2NvcmUgPSAwOwogIHRoaXMueCA9IDMxOwogIHRoaXMueSA9IDE1OwogIHRoaXMueiA9IDA7CiAgdGhpcy5yeCA9IDA7CiAgdGhpcy5yeSA9IDA7CiAgdGhpcy5yeiA9IDA7CiAgdGhpcy5lbnRpdHkgPSBudWxsOwp9CnZhciBnZXRxdWVzdGlvbnNldCA9IGZ1bmN0aW9uKG4pewogIGxldCBhID0gW107CiAgZm9yKHZhciBpID0gMDtpPG47aSsrKXsKICAgIGEucHVzaChpKQogIH0KICByZXR1cm4gYQp9CmZ1bmN0aW9uIG1hdGNoKHR5cGUsIGlkLCBjcmVhdG9yLGNvbCkgewogIHRoaXMudHlwZSA9IHR5cGU7CiAgdGhpcy5pZCA9IGlkOwogIHRoaXMuY3JlYXRvciA9IGNyZWF0b3I7CiAgdGhpcy5xdWl6bWFzdGVyID0gIiI7CiAgdGhpcy50ZWFtMSA9IHt9OwogIHRoaXMudGVhbTIgPSB7fTsKICB0aGlzLnBsYXllck51bSA9IDE7CiAgdGhpcy5jcmVhdG9yY29sID0gY29sOwogIHRoaXMucWNvbCA9ICIiOwogIHRoaXMucXVlc3Rpb25zID0gZ2V0cXVlc3Rpb25zZXQoMjApOwogIHRoaXMucXNkb25lID0gMDsKfQpmdW5jdGlvbiBjaGVja3RpdGxlcmVxKHVzZXIscnQpewogIHRpdGxlcy5zb3J0KGZ1bmN0aW9uKGEsIGIpIHsKICAgIHJldHVybiBhIC0gYjsKICB9KTsKICB0aXRsZXMuZm9yRWFjaChmdW5jdGlvbih0KSB7CiAgICBjb25zb2xlLmxvZyh0KTsKICAgIGlmKHJ0Pj10LnJlcXVpcmVkcnQpewogICAgICB1cGRhdGVPbmUoInVzZXJzIix1c2VyLHt0aXRsZWQ6dHJ1ZSx0aXRsZTp0LnRpdGxlLHRhYnI6dC5hYnJ9KQogICAgfQogIH0pOwp9OwpmdW5jdGlvbiBjaGVja3RpdGxlbG9zcyh1c2VyLHJ0KXsKICB0aXRsZXMuc29ydChmdW5jdGlvbihhLCBiKSB7CiAgICByZXR1cm4gYiAtIGE7CiAgfSk7CiAgdGl0bGVzLmZvckVhY2goZnVuY3Rpb24odCxpLGEpIHsKICAgIGNvbnNvbGUubG9nKHQpOwogICAgaWYocnQ8PXQubG9zc3J0JiZ0LnRpdGxlID09PSAiYmlibGVFeHBlcnQiKXsKICAgICAgdXBkYXRlT25lKCJ1c2VycyIsdXNlcix7dGl0bGVkOmZhbHNlLHRpdGxlOiIiLHRhYnI6IiJ9KQogICAgfSBlbHNlIGlmIChydDw9dC5sb3NzcnQpewogICAgICB1cGRhdGVPbmUoInVzZXJzIix1c2VyLHt0aXRsZWQ6dHJ1ZSx0aXRsZTphW2krMV0udGl0bGUsdGFicjphW2krMV0uYWJyfSkKICAgIH0KICB9KTsKfTsKdmFyIGdhbWVyb29tcyA9IHt9OwpoYnMucmVnaXN0ZXJQYXJ0aWFscyhfX2Rpcm5hbWUgKyAnL3ZlaXdzL3BhcnRpYWxzJyk7CmFwcC5zZXQoJ3ZpZXcgZW5naW5lJywgJ2hicycpOwphcHAuc2V0KCd2aWV3cycsIF9fZGlybmFtZSArICcvdmVpd3MvJyk7CmFwcC51c2UoKHJlcSwgcmVzLCBuZXh0KSA9PiB7CiAgcmVxcysrOwogIC8vY29uc29sZS5sb2cocmVxcykKICBuZXh0KCk7Cn0pOwphcHAudXNlKGV4cHJlc3Muc3RhdGljKCdwdWJsaWMnKSk7CmFwcC5nZXQoJy8nLCBmdW5jdGlvbiAocmVxdWVzdCwgcmVzcG9uc2UpIHsKICBpcCA9IHJlcXVlc3QuaGVhZGVyc1sneC1mb3J3YXJkZWQtZm9yJ10KICAvL2NvbnNvbGUubG9nKHJlcXVlc3QpCiAgcmVzcG9uc2Uuc2VuZEZpbGUoX19kaXJuYW1lICsgJy9wdWJsaWMvaHRtbC9pbmRleC5odG1sJyk7Cn0pOwphcHAuZ2V0KCcvZ2V0dGV4dCcsIGZ1bmN0aW9uIChyZXF1ZXN0LCByZXMpIHsKICBmcy5yZWFkRmlsZSgnaGVicmV3cy1wZXRlci50eHQnLCBmdW5jdGlvbiAoZXJyLCBkYXRhKSB7CiAgICByZXMud3JpdGVIZWFkKDIwMCwgewogICAgICAnQ29udGVudC1UeXBlJzogJ3RleHQnCiAgICB9KTsKICAgIHJlcy53cml0ZShkYXRhKTsKICAgIHJlcy5lbmQoKTsKICB9KTsKfSk7CmFwcC5nZXQoJy9sZWFkZXJib2FyZGZldGNoJywgZnVuY3Rpb24gKHJlcXVlc3QsIHJlcykgewogIGRiLmNvbGxlY3Rpb24oInR5cGVxdWl6emluZ3Njb3JlcyIpLmdldCgpLnRoZW4oKHNjb3JlcykgPT4gewogICAgbGV0IGRhdGEgPSBbXTsKICAgIHNjb3Jlcy5mb3JFYWNoKChkb2MpID0+IHsKICAgICAgbGV0IGR0YSA9IGRvYy5kYXRhKCk7CiAgICAgIC8vaWYoZHRhLnNjb3JlPDEwMCl7CiAgICAgICAvLyB0c2NvcmVzcmVmLmRvYyhkb2MuaWQpLmRlbGV0ZSgpOwogICAgICAvL30gZWxzZXsKICAgICAgICBkdGEucHJvZmlsZUlNRyA9ICIvaW1hZ2VzL3VzZXJzLyIrZW5jb2RlVVJJKGR0YS51c2VyTmFtZSkrIi5wbmciCiAgICAgICAgZGF0YS5wdXNoKGR0YSkKICAgICAgLy99CiAgICAgIAogICAgfSk7CiAgICByZXMud3JpdGVIZWFkKDIwMCwgewogICAgICAnQ29udGVudC1UeXBlJzogJ2pzb24nCiAgICB9KTsKICAgIC8vY29uc29sZS5sb2coZGF0YSkKICAgIHJlcy53cml0ZShKU09OLnN0cmluZ2lmeShkYXRhKSk7CiAgICByZXMuZW5kKCk7CiAgfSkKfSk7CmFwcC5wb3N0KCIvcG9zdHF1b3RlIiwgKHJlcSwgcmVzKSA9PiB7CiAgY29uc29sZS5sb2cocmVxLmJvZHkpCiAgdmFyIGRhdGEgPSByZXEuYm9keTsKICBsZXQgbWF0Y2ggPSBmYWxzZTsKICBsZXQgcXVlcnkgPSB1c2Vyc3JlZi53aGVyZSgidXNlck5hbWUiLCAiPT0iLCBkYXRhLnVzZXIpLmdldCgpLnRoZW4odXNlcnMgPT4gewogICAgaWYgKHVzZXJzLmVtcHR5KSB7CiAgICAgIGNvbnNvbGUubG9nKCdObyBtYXRjaGluZyBkb2N1bWVudHMuJyk7CiAgICAgIC8vc29ja2V0LmVtaXQoImxvZ2luIGZhaWxlZCIpOwogICAgICByZXR1cm47CiAgICB9CiAgICB1c2Vycy5mb3JFYWNoKHVzZXIgPT4gewogICAgICBjb25zb2xlLmxvZyh1c2VyLmRhdGEoKS51c2VyTmFtZSkKICAgICAgaWYgKHVzZXIuZGF0YSgpLnBhc3N3b3JkID09PSBkYXRhLnBhc3MpIHsKICAgICAgICBtYXRjaCA9IHRydWU7CiAgICAgIH0KICAgICAgaWYgKG1hdGNoKSB7CiAgICAgICAgaWYgKGRhdGEucHJvbXB0ID09PSAwKSB7CiAgICAgICAgICBwcm9tcHQgPSBmYWxzZTsKICAgICAgICB9CiAgICAgICAgZWxzZSB7CiAgICAgICAgICBwcm9tcHQgPSB0cnVlOwogICAgICAgIH0KICAgICAgICBuZXdEMigidHlwZXF1aXp6aW5nc2NvcmVzIiwgewogICAgICAgICAgY2g6IGRhdGEuY2gsCiAgICAgICAgICB1c2VyTmFtZTogZGF0YS51c2VyLAogICAgICAgICAgc2NvcmU6IGRhdGEuc2NvcmUsCiAgICAgICAgICB0eXBlOiAicXVvdGVkLSIgKyBwcm9tcHQsCiAgICAgICAgICBwcm9maWxlSU1HOiB1c2VyLmRhdGEoKS5wcm9maWxlSU1HLAogICAgICAgICAgbmFtZUNPbDogdXNlci5kYXRhKCkubmFtZUNPbAogICAgICAgIH0pOwogICAgICB9CiAgICAgIHJlcy53cml0ZUhlYWQoMjAwLCB7CiAgICAgICAgJ0NvbnRlbnQtVHlwZSc6ICdhcHBsaWNhdGlvbi9qc29uJwogICAgICB9KTsKICAgICAgcmVzLmVuZCgpOwogICAgfSk7CiAgfSk7Cn0pCmFwcC5wb3N0KCIvdnBvc3RxdW90ZSIsIChyZXEsIHJlcykgPT4gewogIGNvbnNvbGUubG9nKHJlcS5ib2R5KQogIHZhciBkYXRhID0gcmVxLmJvZHk7CiAgbGV0IG1hdGNoID0gZmFsc2U7CiAgbGV0IHF1ZXJ5ID0gdXNlcnNyZWYud2hlcmUoInVzZXJOYW1lIiwgIj09IiwgZGF0YS51c2VyKS5nZXQoKS50aGVuKHVzZXJzID0+IHsKICAgIGlmICh1c2Vycy5lbXB0eSkgewogICAgICBjb25zb2xlLmxvZygnTm8gbWF0Y2hpbmcgZG9jdW1lbnRzLicpOwogICAgICAvL3NvY2tldC5lbWl0KCJsb2dpbiBmYWlsZWQiKTsKICAgICAgcmV0dXJuOwogICAgfQogICAgdXNlcnMuZm9yRWFjaCh1c2VyID0+IHsKICAgICAgY29uc29sZS5sb2codXNlci5kYXRhKCkudXNlck5hbWUpCiAgICAgIGlmICh1c2VyLmRhdGEoKS5wYXNzd29yZCA9PT0gZGF0YS5wYXNzKSB7CiAgICAgICAgbWF0Y2ggPSB0cnVlOwogICAgICB9CiAgICAgIGlmIChtYXRjaCkgewogICAgICAgIGlmIChkYXRhLnByb21wdCA9PT0gMCkgewogICAgICAgICAgcHJvbXB0ID0gZmFsc2U7CiAgICAgICAgfQogICAgICAgIGVsc2UgewogICAgICAgICAgcHJvbXB0ID0gdHJ1ZTsKICAgICAgICB9CiAgICAgICAgbmV3RDIoInR5cGVxdWl6emluZ3Njb3JlcyIsIHsKICAgICAgICAgIGNoOiBkYXRhLmNoLAogICAgICAgICAgdXNlck5hbWU6IGRhdGEudXNlciwKICAgICAgICAgIHNjb3JlOiBkYXRhLnNjb3JlLAogICAgICAgICAgdHlwZTogInF1b3RlZC0iICsgcHJvbXB0ICsgIi12IiwKICAgICAgICAgIHByb2ZpbGVJTUc6IHVzZXIuZGF0YSgpLnByb2ZpbGVJTUcsCiAgICAgICAgICBuYW1lQ09sOiB1c2VyLmRhdGEoKS5uYW1lQ09sCiAgICAgICAgfSk7CiAgICAgIH0KICAgICAgcmVzLndyaXRlSGVhZCgyMDAsIHsKICAgICAgICAnQ29udGVudC1UeXBlJzogJ2FwcGxpY2F0aW9uL2pzb24nCiAgICAgIH0pOwogICAgICByZXMuZW5kKCk7CiAgICB9KTsKICB9KTsKfSkKYXBwLnBvc3QoIi9wb3N0Y29tcGxldGUiLCAocmVxLCByZXMpID0+IHsKICBjb25zb2xlLmxvZyhyZXEuYm9keSkKICB2YXIgZGF0YSA9IHJlcS5ib2R5OwogIGxldCBxdWVyeSA9IHVzZXJzcmVmLndoZXJlKCJ1c2VyTmFtZSIsICI9PSIsIGRhdGEudXNlcikuZ2V0KCkudGhlbih1c2VycyA9PiB7CiAgICBpZiAodXNlcnMuZW1wdHkpIHsKICAgICAgY29uc29sZS5sb2coJ05vIG1hdGNoaW5nIGRvY3VtZW50cy4nKTsKICAgICAgLy9zb2NrZXQuZW1pdCgibG9naW4gZmFpbGVkIik7CiAgICAgIHJldHVybjsKICAgIH0KICAgIHVzZXJzLmZvckVhY2godXNlciA9PiB7CiAgICAgIGNvbnNvbGUubG9nKHVzZXIuZGF0YSgpLnVzZXJOYW1lLHVzZXIuZGF0YSgpLnBhc3N3b3JkPT09ZGF0YS5wYXNzKQogICAgICBpZiAodXNlci5kYXRhKCkucGFzc3dvcmQgPT09IGRhdGEucGFzcykgewogICAgICAgIGlmIChkYXRhLnByb21wdCA9PT0gMCkgewogICAgICAgICAgcHJvbXB0ID0gZmFsc2U7CiAgICAgICAgfQogICAgICAgIGVsc2UgewogICAgICAgICAgcHJvbXB0ID0gdHJ1ZTsKICAgICAgICB9CiAgICAgICAgbmV3RDIoInR5cGVxdWl6emluZ3Njb3JlcyIsIHsKICAgICAgICAgIGNoOiBkYXRhLmNoLAogICAgICAgICAgdXNlck5hbWU6IGRhdGEudXNlciwKICAgICAgICAgIHNjb3JlOiBkYXRhLnNjb3JlLAogICAgICAgICAgdHlwZTogImNvbXBsZXRlZC0iICsgcHJvbXB0LAogICAgICAgICAgcHJvZmlsZUlNRzogdXNlci5kYXRhKCkucHJvZmlsZUlNRywKICAgICAgICAgIG5hbWVDT2w6IHVzZXIuZGF0YSgpLm5hbWVDT2wKICAgICAgICB9LGZ1bmN0aW9uKHQpewogICAgICAgICAgLy9sb2codC5jb2xsZWN0aW9uKCJ0eXBlcXVpenppbmdzY29yZXMiKS5kb2MoJ2xPQXp6eGVBUFFXdkJqb1JtTVdrJykpCiAgICAgICAgfSk7CiAgICAgICAgY29uc29sZS5sb2coSlNPTi5zdHJpbmdpZnkoewogICAgICAgICAgY2g6IGRhdGEuY2gsCiAgICAgICAgICB1c2VyTmFtZTogZGF0YS51c2VyLAogICAgICAgICAgc2NvcmU6IGRhdGEuc2NvcmUsCiAgICAgICAgICB0eXBlOiAiY29tcGxldGVkLSIgKyBwcm9tcHQsCiAgICAgICAgICBwcm9maWxlSU1HOiB1c2VyLmRhdGEoKS5wcm9maWxlSU1HLAogICAgICAgICAgbmFtZUNPbDogdXNlci5kYXRhKCkubmFtZUNPbAogICAgICAgIH0pKQogICAgICB9CiAgICAgIHJlcy53cml0ZUhlYWQoMjAwLCB7CiAgICAgICAgJ0NvbnRlbnQtVHlwZSc6ICdhcHBsaWNhdGlvbi9qc29uJwogICAgICB9KTsKICAgICAgcmVzLndyaXRlKEpTT04uc3RyaW5naWZ5KGRhdGEpKTsKICAgICAgcmVzLmVuZCgpOwogICAgfSk7CiAgfSk7Cgp9KQphcHAucG9zdCgiL3Zwb3N0Y29tcGxldGUiLCAocmVxLCByZXMpID0+IHsKICBjb25zb2xlLmxvZyhyZXEuYm9keSkKICB2YXIgZGF0YSA9IHJlcS5ib2R5OwogIGxldCBtYXRjaCA9IGZhbHNlOwogIGxldCBxdWVyeSA9IHVzZXJzcmVmLndoZXJlKCJ1c2VyTmFtZSIsICI9PSIsIGRhdGEudXNlcikuZ2V0KCkudGhlbih1c2VycyA9PiB7CiAgICBpZiAodXNlcnMuZW1wdHkpIHsKICAgICAgY29uc29sZS5sb2coJ05vIG1hdGNoaW5nIGRvY3VtZW50cy4nKTsKICAgICAgLy9zb2NrZXQuZW1pdCgibG9naW4gZmFpbGVkIik7CiAgICAgIHJldHVybjsKICAgIH0KICAgIHVzZXJzLmZvckVhY2godXNlciA9PiB7CiAgICAgIGNvbnNvbGUubG9nKHVzZXIuZGF0YSgpLnVzZXJOYW1lKQogICAgICBpZiAodXNlci5kYXRhKCkucGFzc3dvcmQgPT09IGRhdGEucGFzcykgewogICAgICAgIG1hdGNoID0gdHJ1ZTsKICAgICAgfQogICAgICBpZiAobWF0Y2gpIHsKICAgICAgICBpZiAoZGF0YS5wcm9tcHQgPT09IDApIHsKICAgICAgICAgIHByb21wdCA9IGZhbHNlOwogICAgICAgIH0KICAgICAgICBlbHNlIHsKICAgICAgICAgIHByb21wdCA9IHRydWU7CiAgICAgICAgfQogICAgICAgIG5ld0QyKCJ0eXBlcXVpenppbmdzY29yZXMiLCB7CiAgICAgICAgICBjaDogZGF0YS5jaCwKICAgICAgICAgIHVzZXJOYW1lOiBkYXRhLnVzZXIsCiAgICAgICAgICBzY29yZTogZGF0YS5zY29yZSwKICAgICAgICAgIHR5cGU6ICJjb21wbGV0ZWQtIiArIHByb21wdCArICItdiIsCiAgICAgICAgICBwcm9maWxlSU1HOiB1c2VyLmRhdGEoKS5wcm9maWxlSU1HLAogICAgICAgICAgbmFtZUNPbDogdXNlci5kYXRhKCkubmFtZUNPbAogICAgICAgIH0pOwogICAgICB9CiAgICAgIHJlcy53cml0ZUhlYWQoMjAwLCB7CiAgICAgICAgJ0NvbnRlbnQtVHlwZSc6ICdhcHBsaWNhdGlvbi9qc29uJwogICAgICB9KTsKICAgICAgcmVzLmVuZCgpOwogICAgfSk7CiAgfSk7Cn0pCmFwcC5wb3N0KCIvc2V0UHJvZmlsZUltZyIsIChyZXEsIHJlcykgPT4gewogIC8vY29uc29sZS5sb2cocmVxLHJlcS5nZXQoJ2NvbnRlbnQtdHlwZScpKQogIGxldCBtYXRjaCA9IGZhbHNlOwogIGxldCBxdWVyeSA9IHVzZXJzcmVmLndoZXJlKCJ1c2VyTmFtZSIsICI9PSIsIHJlcS5ib2R5LnVzZXIpLmdldCgpLnRoZW4odXNlcnMgPT4gewogICAgaWYgKHVzZXJzLmVtcHR5KSB7CiAgICAgIGNvbnNvbGUubG9nKCdObyBtYXRjaGluZyBkb2N1bWVudHMuJyk7CiAgICAgIC8vc29ja2V0LmVtaXQoImxvZ2luIGZhaWxlZCIpOwogICAgICByZXR1cm47CiAgICB9CiAgICB1c2Vycy5mb3JFYWNoKHVzZXIgPT4gewogICAgICBjb25zb2xlLmxvZyh1c2VyLmRhdGEoKS5wYXNzd29yZCA9PT0gcmVxLmJvZHkucGFzcykKICAgICAgaWYgKHVzZXIuZGF0YSgpLnBhc3N3b3JkID09PSByZXEuYm9keS5wYXNzKSB7CiAgICAgICAgbWF0Y2ggPSB0cnVlOwogICAgICAgIGNvbnNvbGUubG9nKCJtYXRjaGVkIikKICAgICAgfQogICAgfSk7CiAgaWYobWF0Y2gpewogICAgbGV0IG5jb2wgPSByZXEuYm9keS5uYW1lY29sOwogICAgLy9jb25zb2xlLmxvZyhyZXEuYm9keSkKICAgIGlmKHJlcS5ib2R5Lm5jb2xvcmNoYW5nZT09PSJ0cnVlIil7CiAgICAgIHVwZGF0ZU9uZSgidXNlcnMiLHJlcS5ib2R5LnVzZXIsewogICAgICAgIG5hbWVDT2w6bmNvbAogICAgICB9KQogICAgICBsZXQgdHNjb3Jlc3F1ZXJ5ID0gdHNjb3Jlc3JlZi53aGVyZSgidXNlck5hbWUiLCAiPT0iLCByZXEuYm9keS51c2VyKS5nZXQoKS50aGVuKHNjb3JlcyA9PiB7CiAgICAgICAgc2NvcmVzLmZvckVhY2godXNlciA9PiB7CiAgICAgICAgICAvL2NvbnNvbGUubG9nKHVzZXIuaWQsIG5jb2wpCiAgICAgICAgICB1cGRhdGVPbmUoInR5cGVxdWl6emluZ3Njb3JlcyIsdXNlci5pZCx7CiAgICAgICAgICAgIG5hbWVDT2w6bmNvbAogICAgICAgICAgfSkKICAgICAgICB9KQogICAgICB9KQogICAgfQogICAgaWYocmVxLmZpbGVzIT09bnVsbCl7CiAgICAgICBsZXQgc2FtcGxlRmlsZSA9IHJlcS5maWxlcy5maWxlOwogICAgICBzYW1wbGVGaWxlLm12KF9fZGlybmFtZSArICcvcHVibGljL2ltYWdlcy91c2Vycy8nICsgc2FtcGxlRmlsZS5uYW1lICwgZnVuY3Rpb24oZXJyKSB7CiAgICAgICAgaWYgKGVycikKICAgICAgICAgIHJldHVybiByZXMuc3RhdHVzKDUwMCkvKi5zZW5kKGVycik7Ki8KCiAgICAgICAgICBKaW1wLnJlYWQoX19kaXJuYW1lICsgJy9wdWJsaWMvaW1hZ2VzL3VzZXJzLycgKyBzYW1wbGVGaWxlLm5hbWUsIChlcnIsIGltZykgPT4gewogICAgICAgICAgICBpZiAoZXJyKSB0aHJvdyBlcnI7CgogICAgICAgICAgICBmcy51bmxpbmsoX19kaXJuYW1lICsgJy9wdWJsaWMvaW1hZ2VzL3VzZXJzLycgKyBzYW1wbGVGaWxlLm5hbWUsIGZ1bmN0aW9uIChlcnIpIHsKICAgICAgICAgICAgICBpZiAoZXJyKSB0aHJvdyBlcnI7CiAgICAgICAgICAgICAgY29uc29sZS5sb2coJ0ZpbGUgZGVsZXRlZCEnKTsKICAgICAgICAgICAgfSk7CiAgICAgICAgICAgIGltZwogICAgICAgICAgICAgIC53cml0ZShfX2Rpcm5hbWUgKyAnL3B1YmxpYy9pbWFnZXMvdXNlcnMvJyArIHJlcS5ib2R5LnVzZXIgKyAnLnBuZycpOyAvLyBzYXZlCgogICAgICAgICAgfSk7CiAgICAgICAgLy9yZXMuc2VuZEZpbGUoX19kaXJuYW1lICsgJy9wdWJsaWMvaW1hZ2VzL3VzZXJzLycgKyByZXEuYm9keS51c2VyICsgJy5wbmcnKTsKICAgICAgfSk7CiAgICB9CiAgICByZXMud3JpdGVIZWFkKDIwMCwgewogICAgICAnQ29udGVudC1UeXBlJzogJ3RleHQnCiAgICB9KTsKICAgIHJlcy53cml0ZSgic3Vjc2VzcyIpCiAgICByZXMuZW5kKCk7CiAgfQp9KTsKfSkKYXBwLnBvc3QoIi9jcmVhdGVxdWVzdGlvbiIsIChyZXEsIHJlcykgPT4gewogICAgY29uc29sZS5sb2cocmVxLmJvZHkpCiAgICBsZXQgZGF0YSA9IHJlcS5ib2R5OwogICAgbGV0IHR5cGUgPSBkYXRhLnR5cGUscmVmID0gZGF0YS5yZWZyZW5jZSwgcXVlc3Rpb24gPSBkYXRhLnF1ZXN0aW9uLGFuc3dlciA9IGRhdGEuYW5zd2VyLGNyZWF0b3IgPSBkYXRhLmNyZWF0b3I7CiAgICBsZXQgYWRkRG9jID0gZGIuY29sbGVjdGlvbigicXVlc3Rpb25zIikuZG9jKHR5cGUpLmNvbGxlY3Rpb24oInF1ZXN0aW9ucyIpLmFkZCh7CiAgICAgIHR5cGU6dHlwZSwKICAgICAgcXVlc3Rpb246cXVlc3Rpb24sCiAgICAgIGFuc3dlcjphbnN3ZXIsCiAgICAgIGNyZWF0b3I6Y3JlYXRvciwKICAgICAgY3JlYXRlZEF0Om5ldyBEYXRlKCkudG9JU09TdHJpbmcoKSwKICAgICAgcmVmcmVuY2U6cmVmCiAgICB9KS50aGVuKHJlZiA9PiB7CiAgICAgIHJlcy53cml0ZUhlYWQoMjAwLCB7CiAgICAgICAgJ0NvbnRlbnQtVHlwZSc6ICd0ZXh0JwogICAgICB9KTsKICAgICAgcmVzLndyaXRlKCJzdWNzZXNzIikKICAgICAgcmVzLmVuZCgpOwogICAgfSk7Cn0pCmFwcC5nZXQoJy91c2VyLzp1c2VyJywgZnVuY3Rpb24gKHJlcXVlc3QsIHJlcykgewogIHZhciB1c2VyZGF0YSA9IHt9OwogIHZhciB0eXBlcXVpenppbmdzY29yZXMgPSBbXTsKICB2YXIgZnJpZW5kc2RhdGEgPSBbXTsKICB2YXIgc2NvcmVzZGF0YSA9IFtdOwogIHZhciB1c2VybmFtZSA9IHJlcXVlc3QucGFyYW1zLnVzZXI7CgogIGxldCB1c2VycXVlcnkgPSB1c2Vyc3JlZi53aGVyZSgidXNlck5hbWUiLCAiPT0iLCB1c2VybmFtZSkuZ2V0KCkudGhlbih1c2VycyA9PiB7CiAgICBjb25zb2xlLmxvZyh1c2VybmFtZSkKICAgIC8vY29uc29sZS5sb2codXNlcnMpCiAgICBpZiAodXNlcnMuZW1wdHkpIHsKICAgICAgY29uc29sZS5sb2coJ05vIG1hdGNoaW5nIGRvY3VtZW50cy4xJyk7CiAgICAgIC8vc29ja2V0LmVtaXQoImxvZ2luIGZhaWxlZCIpOwogICAgICAvL3JldHVybjsKICAgIH0KICAgIHVzZXJzLmZvckVhY2godXNlciA9PiB7CiAgICAgIGxldCBkdCA9IHVzZXIuZGF0YSgpOwogICAgICBkdC5lbWFpbCA9ICIiOwogICAgICAvL2NvbnNvbGUubG9nKG5ldyBEYXRlKGR0Lmxhc3RMb2dpbikpCiAgICAgIGR0Lmxhc3RMb2dpbiA9IHRpbWVTaW5jZShuZXcgRGF0ZShkdC5sYXN0TG9naW4pKQogICAgICBkdC5zdGF0ZSA9IGR0LnN0YXRlLnRvVXBwZXJDYXNlKCk7CiAgICAgIGR0LnByb2ZpbGVJTUcgPSAiL2ltYWdlcy91c2Vycy8iK2VuY29kZVVSSShkdC5wcm9maWxlSU1HKSsiLnBuZyIKICAgICAgdXNlcmRhdGEgPSBkdDsKICAgIH0pOwogICAgLy9jb25zb2xlLmxvZyh1c2VyZGF0YSkKICAgIGlmICh1c2VyZGF0YS5mcmllbmRzLmxlbmd0aCA+IDApIHsKICAgICAgZm9yICh2YXIgaSA9IDA7IGkgPCB1c2VyZGF0YS5mcmllbmRzLmxlbmd0aDsgaSsrKSB7CiAgICAgICAgbG9nKHVzZXJkYXRhLmZyaWVuZHNbaV0pCiAgICAgICAgbGV0IGZyaWVuZHF1ZXJ5ID0gdXNlcnNyZWYud2hlcmUoInVzZXJOYW1lIiwgIj09IiwgdXNlcmRhdGEuZnJpZW5kc1tpXSkuZ2V0KCkudGhlbih1c2VycyA9PiB7CiAgICAgICAgICAvL2NvbnNvbGUubG9nKHVzZXJzLmVtcHR5KQogICAgICAgICAgLy9jb25zb2xlLmxvZyh1c2VycykKICAgICAgICAgIGlmICh1c2Vycy5lbXB0eSkgewogICAgICAgICAgICBjb25zb2xlLmxvZygnTm8gbWF0Y2hpbmcgZG9jdW1lbnRzLjInKTsKICAgICAgICAgICAgLy9zb2NrZXQuZW1pdCgibG9naW4gZmFpbGVkIik7CiAgICAgICAgICAgIHJldHVybjsKICAgICAgICAgIH0KICAgICAgICAgIHVzZXJzLmZvckVhY2godXNlciA9PiB7CiAgICAgICAgICAgIGxldCBkdGEgPSB1c2VyLmRhdGEoKTsKICAgICAgICAgICAgZHRhLnN0YXRlID0gZHRhLnN0YXRlLnRvVXBwZXJDYXNlKCk7CiAgICAgICAgICAgIGR0YS5wcm9maWxlSU1HID0gIi9pbWFnZXMvdXNlcnMvIitlbmNvZGVVUkkoZHRhLnByb2ZpbGVJTUcpKyIucG5nIgogICAgICAgICAgICBmcmllbmRzZGF0YS5wdXNoKGR0YSkKICAgICAgICAgIH0pOwogICAgICAgICAgLy9sb2coZnJpZW5kc2RhdGEpCgogICAgICAgIH0pOwogICAgICB9ICAgICAgICAgIAogICAgICB2YXIgdHMgPSB7fTsKICAgICAgdmFyIGN0cyA9IFtdOwogICAgICB2YXIgcXRzID0gW107CiAgICAgIHZhciBjcHRzID0gW107CiAgICAgIHZhciBxcHRzID0gW107CiAgICAgIGxldCB0c2NvcmVzcXVlcnkgPSB0c2NvcmVzcmVmLndoZXJlKCJ1c2VyTmFtZSIsICI9PSIsIHVzZXJuYW1lKS5nZXQoKS50aGVuKHNjb3JlcyA9PiB7CiAgICAgICAgaWYgKHNjb3Jlcy5lbXB0eSkgewogICAgICAgICAgY29uc29sZS5sb2coJ05vIG1hdGNoaW5nIGRvY3VtZW50cy4zJyk7CiAgICAgICAgICAvLyBzb2NrZXQuZW1pdCgibG9naW4gZmFpbGVkIik7CiAgICAgICAgIC8vIHJldHVybjsKICAgICAgICAgcmVzLnJlbmRlcigndXNlcicsIHsKICAgICAgICAgIHVzZXJkYXRhOiB1c2VyZGF0YSwKICAgICAgICAgIHNjb3Jlc2RhdGE6IHt9LAogICAgICAgICAgZnJpZW5kc2RhdGE6IGZyaWVuZHNkYXRhLAogICAgICAgICAgdHM6IHt9CiAgICAgICAgfSk7CiAgICAgICAgfQogICAgICAgIGVsc2UKICAgICAgICB7CiAgICAgICAgICBzY29yZXMuZm9yRWFjaChzY29yZSA9PiB7CiAgICAgICAgICBsZXQgc2NkdCA9IHNjb3JlLmRhdGEoKTsKICAgICAgICAgIHNjZHQuY3JlYXRlZEF0ID0gdGltZVNpbmNlKG5ldyBEYXRlKHNjZHQuY3JlYXRlZEF0KSk7CiAgICAgICAgICBpZiAoc2NkdC50eXBlLmluZGV4T2YoInF1b3RlIikgIT09IC0xKSB7CiAgICAgICAgICAgIHNjZHQuc2NvcmUgPSB0b3RpbWUoc2NkdC5zY29yZSk7CiAgICAgICAgICB9CiAgICAgICAgICBpZiAoc2NkdC50eXBlID09PSAicXVvdGVkLXRydWUiKSB7CiAgICAgICAgICAgIHNjZHQudHlwZSA9ICJxdW90ZWQgd2l0aCBwcm9tcHQiOwogICAgICAgICAgICBxcHRzLnB1c2goc2NkdCk7CiAgICAgICAgICB9CiAgICAgICAgICBlbHNlIGlmIChzY2R0LnR5cGUgPT09ICJxdW90ZWQtZmFsc2UiKSB7CiAgICAgICAgICAgIHNjZHQudHlwZSA9ICJxdW90ZWQgd2l0aG91dCBwcm9tcHQiOwogICAgICAgICAgICBxdHMucHVzaChzY2R0KTsKICAgICAgICAgIH0KICAgICAgICAgIGVsc2UgaWYgKHNjZHQudHlwZSA9PT0gImNvbXBsZXRlZC1mYWxzZSIpIHsKICAgICAgICAgICAgc2NkdC50eXBlID0gImNvbXBsZXRlZCB3aXRob3V0IHByb21wdCI7CiAgICAgICAgICAgIGN0cy5wdXNoKHNjZHQpOwogICAgICAgICAgfQogICAgICAgICAgZWxzZSB7CiAgICAgICAgICAgIHNjZHQudHlwZSA9ICJjb21wbGV0ZWQgd2l0aCBwcm9tcHQiOwogICAgICAgICAgICBjcHRzLnB1c2goc2NkdCk7CiAgICAgICAgICB9CiAgICAgICAgICB0eXBlcXVpenppbmdzY29yZXMucHVzaChzY2R0KTsKICAgICAgICB9KTsKICAgICAgICBjdHMgPSBhc29ydChjdHMsICJobCIsICJzY29yZSIpCiAgICAgICAgY3B0cyA9IGFzb3J0KGNwdHMsICJobCIsICJzY29yZSIpCiAgICAgICAgcXB0cyA9IGFzb3J0KHFwdHMsICJsaCIsICJzY29yZSIpCiAgICAgICAgcXRzID0gYXNvcnQocXRzLCAibGgiLCAic2NvcmUiKQogICAgICAgIHRzID0gewogICAgICAgICAgYzogY3RzLAogICAgICAgICAgY3A6IGNwdHMsCiAgICAgICAgICBxOiBxdHMsCiAgICAgICAgICBxcDogcXB0cwogICAgICAgIH07CiAgICAgICAgdHlwZXF1aXp6aW5nc2NvcmVzID0gdHlwZXF1aXp6aW5nc2NvcmVzLnNvcnQoZnVuY3Rpb24oYSxiKXsKICAgICAgICAgIGlmKGEudXBkYXRlZEF0Ll9zZWNvbmRzPT09dW5kZWZpbmVkKXsKICAgICAgICAgICAgYSA9IG5ldyBEYXRlKGEudXBkYXRlZEF0KS5nZXRUaW1lKCkKICAgICAgICAgIH0gZWxzZXsKICAgICAgICAgICAgYT1uZXcgRGF0ZShhLnVwZGF0ZWRBdC5fc2Vjb25kcykuZ2V0VGltZSgpCiAgICAgICAgICB9CiAgICAgICAgICBpZihiLnVwZGF0ZWRBdC5fc2Vjb25kcyA9PT0gdW5kZWZpbmVkKXsKICAgICAgICAgICAgYiA9IG5ldyBEYXRlKGIudXBkYXRlZEF0KS5nZXRUaW1lKCkKICAgICAgICAgIH0gZWxzZXsKICAgICAgICAgICAgYj1uZXcgRGF0ZShiLnVwZGF0ZWRBdC5fc2Vjb25kcykuZ2V0VGltZSgpOwogICAgICAgICAgfQogICAgICAgICAgY29uc29sZS5sb2coYSxiKQogICAgICAgICAgcmV0dXJuIGItYTsKICAgICAgICB9KQogICAgICAgIGNvbnNvbGUubG9nKHR5cGVxdWl6emluZ3Njb3JlcykKICAgICAgICByZXMucmVuZGVyKCd1c2VyJywgewogICAgICAgICAgdXNlcmRhdGE6IHVzZXJkYXRhLAogICAgICAgICAgc2NvcmVzZGF0YTogdHlwZXF1aXp6aW5nc2NvcmVzLAogICAgICAgICAgZnJpZW5kc2RhdGE6IGZyaWVuZHNkYXRhLAogICAgICAgICAgdHM6IHRzCiAgICAgICAgfSk7CiAgICAgICAgfQogICAgICB9KTsKICAgIH0KICAgIGVsc2UgewogICAgICB2YXIgdHMgPSB7fTsKICAgICAgdmFyIGN0cyA9IFtdOwogICAgICB2YXIgcXRzID0gW107CiAgICAgIHZhciBjcHRzID0gW107CiAgICAgIHZhciBxcHRzID0gW107CiAgICAgIGxldCB0c2NvcmVzcXVlcnkgPSB0c2NvcmVzcmVmLndoZXJlKCJ1c2VyTmFtZSIsICI9PSIsIHVzZXJuYW1lKS5nZXQoKS50aGVuKHNjb3JlcyA9PiB7CiAgICAgICAgaWYgKHNjb3Jlcy5lbXB0eSkgewogICAgICAgICAgY29uc29sZS5sb2coJ05vIG1hdGNoaW5nIGRvY3VtZW50cy4nKTsKICAgICAgICAgIC8vc29ja2V0LmVtaXQoImxvZ2luIGZhaWxlZCIpOwogICAgICAgICAgcmVzLnJlbmRlcigndXNlcicsIHsKICAgICAgICAgIHVzZXJkYXRhOiB1c2VyZGF0YSwKICAgICAgICAgIHNjb3Jlc2RhdGE6IHR5cGVxdWl6emluZ3Njb3JlcywKICAgICAgICAgIGZyaWVuZHNkYXRhOiBmcmllbmRzZGF0YSwKICAgICAgICAgIHRzOiB0cwogICAgICAgIH0pOwogICAgICAgIH0KICAgICAgICBlbHNlewogICAgICAgICAgICAgICAgICAvL2NvbnNvbGUubG9nKHNjb3Jlcy5lbXB0eSkKICAgICAgICBzY29yZXMuZm9yRWFjaChzY29yZSA9PiB7CiAgICAgICAgICBsZXQgc2NkdCA9IHNjb3JlLmRhdGEoKTsKICAgICAgICAgIC8vbG9nKHNjZHQudHlwZSkKICAgICAgICAgIHNjZHQuY3JlYXRlZEF0ID0gdGltZVNpbmNlKG5ldyBEYXRlKHNjZHQuY3JlYXRlZEF0KSkKICAgICAgICAgIGlmIChzY2R0LnR5cGUuaW5kZXhPZigicXVvdGUiKSAhPT0gLTEpIHsKICAgICAgICAgICAgc2NkdC5zY29yZSA9IHRvdGltZShzY2R0LnNjb3JlKTsKICAgICAgICAgIH0KICAgICAgICAgIGlmIChzY2R0LnR5cGUgPT09ICJxdW90ZWQtdHJ1ZSIpIHsKICAgICAgICAgICAgc2NkdC50eXBlID0gInF1b3RlZCB3aXRoIHByb21wdCIKICAgICAgICAgICAgcXB0cy5wdXNoKHNjZHQpCiAgICAgICAgICB9CiAgICAgICAgICBlbHNlIGlmIChzY2R0LnR5cGUgPT09ICJxdW90ZWQtZmFsc2UiKSB7CiAgICAgICAgICAgIHNjZHQudHlwZSA9ICJxdW90ZWQgd2l0aG91dCBwcm9tcHQiCiAgICAgICAgICAgIHF0cy5wdXNoKHNjZHQpCiAgICAgICAgICB9CiAgICAgICAgICBlbHNlIGlmIChzY2R0LnR5cGUgPT09ICJjb21wbGV0ZWQtZmFsc2UiKSB7CiAgICAgICAgICAgIHNjZHQudHlwZSA9ICJjb21wbGV0ZWQgd2l0aG91dCBwcm9tcHQiCiAgICAgICAgICAgIGN0cy5wdXNoKHNjZHQpCiAgICAgICAgICB9CiAgICAgICAgICBlbHNlIHsKICAgICAgICAgICAgc2NkdC50eXBlID0gImNvbXBsZXRlZCB3aXRoIHByb21wdCIKICAgICAgICAgICAgY3B0cy5wdXNoKHNjZHQpCiAgICAgICAgICB9CiAgICAgICAgICB0eXBlcXVpenppbmdzY29yZXMucHVzaChzY2R0KQogICAgICAgICAgLy9jb25zb2xlLmxvZygpCiAgICAgICAgICAvL2ZyaWVuZHNkYXRhLnB1c2goc2NvcmUuZGF0YSgpKQogICAgICAgIH0pOwogICAgICAgIGN0cyA9IGFzb3J0KGN0cywgImhsIiwgInNjb3JlIikKICAgICAgICBjcHRzID0gYXNvcnQoY3B0cywgImhsIiwgInNjb3JlIikKICAgICAgICBxcHRzID0gYXNvcnQocXB0cywgImxoIiwgInNjb3JlIikKICAgICAgICBxdHMgPSBhc29ydChxdHMsICJsaCIsICJzY29yZSIpCiAgICAgICAgdHMgPSB7CiAgICAgICAgICBjOiBjdHMsCiAgICAgICAgICBjcDogY3B0cywKICAgICAgICAgIHE6IHF0cywKICAgICAgICAgIHFwOiBxcHRzCiAgICAgICAgfTsKICAgICAgICByZXMucmVuZGVyKCd1c2VyJywgewogICAgICAgICAgdXNlcmRhdGE6IHVzZXJkYXRhLAogICAgICAgICAgc2NvcmVzZGF0YTogdHlwZXF1aXp6aW5nc2NvcmVzLAogICAgICAgICAgZnJpZW5kc2RhdGE6IGZyaWVuZHNkYXRhLAogICAgICAgICAgdHM6IHRzCiAgICAgICAgfSk7CiAgICAgICAgfQoKICAgICAgfSk7CiAgICB9CiAgfSk7CiAgLyoqLwp9KTsKCmlvLnNvY2tldHMub24oJ2Nvbm5lY3Rpb24nLCBmdW5jdGlvbiAoc29ja2V0KSB7CiAgLypzb2NrZXQub24oJ21lc3NhZ2UnLCBmdW5jdGlvbiAoZGF0YSkgewogICAgVXNlci5maW5kT25lKHsKICAgICAgd2hlcmU6IHsKICAgICAgICB1c2VyTmFtZTogZGF0YS51c2VyCiAgICAgIH0KICAgIH0pLnRoZW4odXNlciA9PiB7CiAgICAgIGRhdGEuY29sID0gdXNlci5kYXRhVmFsdWVzLm5hbWVDT2w7CiAgICB9KTsKICAgIGRhdGEudGltZXNpbmNlID0gKG5ldyBEYXRlKCkudG9JU09TdHJpbmcoKSkKICAgIGNoYXQudW5zaGlmdChkYXRhKTsKICAgIGNvbnNvbGUubG9nKGNoYXQpOwogICAgaW8uZW1pdCgibWVzc2FnZSIsY2hhdCk7CiAgfSk7Ki8KICBzb2NrZXQub24oJ3ZhcGlkUHVibGljS2V5JywgKGRhdGEpID0+IHsKICAgIHNvY2tldC5lbWl0KCJ2cGsiLCBwcm9jZXNzLmVudi5WQVBJRF9QVUJMSUNfS0VZKQogIH0pOyAvLyBsaXN0ZW4gdG8gdGhlIGV2ZW50CiAgc29ja2V0Lm9uKCJyZWdpc3RlciIsIGZ1bmN0aW9uIChkYXRhLCBzdWIpIHsKICAgIGNvbnNvbGUubG9nKHN1YikKICAgIGxldCB1c2VkID0gZmFsc2U7CiAgICBsZXQgcXVlcnkgPSB1c2Vyc3JlZi5nZXQoKS50aGVuKHVzZXJzID0+IHsKICAgICAgdXNlcnMuZm9yRWFjaCh1c2VyID0+IHsKICAgICAgICBjb25zb2xlLmxvZyh1c2VyKQogICAgICAgIGlmICh1c2VyLmlkID09PSBkYXRhLm5hbWUpIHsKICAgICAgICAgIHVzZWQgPSB0cnVlOwogICAgICAgIH0KICAgICAgfSk7CiAgICAgIGlmICghdXNlZCkgewogICAgICAgIGlmIChBZG1pbnMuaW5kZXhPZihkYXRhLm5hbWUpID4gLTEpIHsKICAgICAgICAgIEppbXAucmVhZChfX2Rpcm5hbWUgKyAnL3B1YmxpYy9pbWFnZXMvYXZhdGFyIGdlbmVyaWMucG5nJywgKGVyciwgaW1nKSA9PiB7CiAgICAgICAgICAgIGlmIChlcnIpIHRocm93IGVycjsKCiAgICAgICAgICAgIGltZwogICAgICAgICAgICAgIC53cml0ZShfX2Rpcm5hbWUgKyAnL3B1YmxpYy9pbWFnZXMvdXNlcnMvJyArIGRhdGEubmFtZSArICcucG5nJyk7IC8vIHNhdmUKICAgICAgICAgIH0pOwogICAgICAgICAgbmV3RCgidXNlcnMiLCBkYXRhLm5hbWUsIHsKICAgICAgICAgICAgaWQ6IDEsCiAgICAgICAgICAgIHVzZXJOYW1lOiBkYXRhLm5hbWUsCiAgICAgICAgICAgIGVtYWlsOiBkYXRhLmVtYWlsLAogICAgICAgICAgICBwYXNzd29yZDogZGF0YS5wYXNzLAogICAgICAgICAgICBsYXN0TG9naW46IG5ldyBEYXRlKCkudG9JU09TdHJpbmcoKSwKICAgICAgICAgICAgaXNBZG1pbjogdHJ1ZSwKICAgICAgICAgICAgdmlzaXROdW06IDAsCiAgICAgICAgICAgIG5hbWVDT2w6ICdibHVlJywKICAgICAgICAgICAgcmF0aW5nczpbCiAgICAgICAgICAgICAge29wZW5PbmxpbmU6MTAwMCxyZDozNTB9LAogICAgICAgICAgICAgIHt0ZWFtc09ubGluZToxMDAwLHJkOjM1MH0KICAgICAgICAgICAgXSwKICAgICAgICAgICAgZ2FtZXNQbGF5ZWQ6IDAsCiAgICAgICAgICAgIG9ubGluZTogdHJ1ZSwKICAgICAgICAgICAgdG91cm5hbWVudHM6ICcnLAogICAgICAgICAgICBmcmllbmRzOiBbXSwKICAgICAgICAgICAgbW9udGhTY29yZTogMCwKICAgICAgICAgICAgYWxsVGltZVNjb3JlOiAwLAogICAgICAgICAgICBwcm9maWxlSU1HOiBkYXRhLm5hbWUsCiAgICAgICAgICAgIHN0YXRlOiBkYXRhLnN0YXRlLAogICAgICAgICAgICBpcEFEOiBpcCwKICAgICAgICAgICAgYmFubmVkOiBmYWxzZQogICAgICAgICAgfSk7CiAgICAgICAgICBuZXdEMigic3VicyIsIHsKICAgICAgICAgICAgdXNlck5hbWU6IGRhdGEubmFtZSwKICAgICAgICAgICAgc3ViOiBzdWIKICAgICAgICAgIH0pCiAgICAgICAgICAvL2NvbnNvbGUubG9nKCd1c2VyICcgKyBkYXRhLm5hbWUgKyAnIHJlZ2lzdGVyZWQnKTsKICAgICAgICAgIHNvY2tldC5lbWl0KCdyZWdpc3RlcmVkJywgZGF0YS5uYW1lKTsKICAgICAgICAgIC8vdmFyIGlkID0gc29ja2V0LmlkOwogICAgICAgICAgc2VuZG1haWwoZGF0YS5lbWFpbCk7CiAgICAgICAgICAvKmRiLmNvbGxlY3Rpb24oInVzZXJzIikuZ2V0KCkudGhlbih1c2VycyA9PiB7CiAgICAgICAgICAgIGlvLmVtaXQoImxlYWRlcmJvYXJkIiwgdXNlcnMpOwogICAgICAgICAgICAvL2NvbnNvbGUubG9nKHVzZXJzKTsKICAgICAgICAgIH0pKi8KICAgICAgICB9CiAgICAgICAgZWxzZSB7CiAgICAgICAgICBKaW1wLnJlYWQoX19kaXJuYW1lICsgJy9wdWJsaWMvaW1hZ2VzL2F2YXRhciBnZW5lcmljLnBuZycsIChlcnIsIGltZykgPT4gewogICAgICAgICAgICBpZiAoZXJyKSB0aHJvdyBlcnI7CgogICAgICAgICAgICBpbWcKICAgICAgICAgICAgICAud3JpdGUoX19kaXJuYW1lICsgJy9wdWJsaWMvaW1hZ2VzL3VzZXJzLycgKyBkYXRhLm5hbWUgKyAnLnBuZycpOyAvLyBzYXZlCiAgICAgICAgICB9KTsKICAgICAgICAgIG5ld0QoInVzZXJzIiwgZGF0YS5uYW1lLCB7CiAgICAgICAgICAgIGlkOiAxLAogICAgICAgICAgICB1c2VyTmFtZTogZGF0YS5uYW1lLAogICAgICAgICAgICBlbWFpbDogZGF0YS5lbWFpbCwKICAgICAgICAgICAgcGFzc3dvcmQ6IGRhdGEucGFzcywKICAgICAgICAgICAgbGFzdExvZ2luOiBuZXcgRGF0ZSgpLnRvSVNPU3RyaW5nKCksCiAgICAgICAgICAgIGlzQWRtaW46IGZhbHNlLAogICAgICAgICAgICB2aXNpdE51bTogMCwKICAgICAgICAgICAgbmFtZUNPbDogJ2JsdWUnLAogICAgICAgICAgICByYXRpbmdzOlsKICAgICAgICAgICAgICB7b3Blbk9ubGluZToxMDAwLHJkOjM1MH0sCiAgICAgICAgICAgICAge3RlYW1zT25saW5lOjEwMDAscmQ6MzUwfQogICAgICAgICAgICBdLAogICAgICAgICAgICBnYW1lc1BsYXllZDogMCwKICAgICAgICAgICAgb25saW5lOiB0cnVlLAogICAgICAgICAgICB0b3VybmFtZW50czogJycsCiAgICAgICAgICAgIGZyaWVuZHM6IFtdLAogICAgICAgICAgICBtb250aFNjb3JlOiAwLAogICAgICAgICAgICBhbGxUaW1lU2NvcmU6IDAsCiAgICAgICAgICAgIHByb2ZpbGVJTUc6IGRhdGEubmFtZSwKICAgICAgICAgICAgc3RhdGU6IGRhdGEuc3RhdGUsCiAgICAgICAgICAgIGlwQUQ6IGlwLAogICAgICAgICAgICBiYW5uZWQ6IGZhbHNlCiAgICAgICAgICB9KTsKICAgICAgICAgIG5ld0QyKCJzdWJzIiwgewogICAgICAgICAgICB1c2VyTmFtZTogZGF0YS5uYW1lLAogICAgICAgICAgICBzdWI6IHN1YgogICAgICAgICAgfSkKICAgICAgICAgIC8vY29uc29sZS5sb2coJ3VzZXIgJyArIGRhdGEubmFtZSArICcgcmVnaXN0ZXJlZCcpOwogICAgICAgICAgc29ja2V0LmVtaXQoJ3JlZ2lzdGVyZWQnLCBkYXRhLm5hbWUpOwogICAgICAgICAgdmFyIGlkID0gc29ja2V0LmlkOwogICAgICAgICAgc2VuZG1haWwoZGF0YS5lbWFpbCk7CiAgICAgICAgICAvKnZhciBpZCA9IHNvY2tldC5pZDsKICAgICAgICAgIHZhciBuZXdwbGF5ZXIgPSB7CiAgICAgICAgICAgIGlkOiBpZCwKICAgICAgICAgICAgdXNlcjogZGF0YS51c2VyCiAgICAgICAgICB9OwogICAgICAgICAgb25saW5lcGxheWVyc1tpZF0gPSBuZXdwbGF5ZXI7CiAgICAgICAgICBvbmxpbmVwbHNbc29ja2V0LmlkXSA9IHsKICAgICAgICAgICAgdXNlcjogZGF0YS51c2VyCiAgICAgICAgICB9OyovCiAgICAgICAgICAvKmNoYXQudW5zaGlmdCh7CiAgICAgICAgICAgIGNvbDogImJsYWNrIiwKICAgICAgICAgICAgdXNlcjogJ2JvdCcsCiAgICAgICAgICAgIG1lc3NhZ2U6IGRhdGEudXNlciArICcgaXMgb25saW5lIG5vdycsCiAgICAgICAgICAgIHRpbWVzaW5jZTogbmV3IERhdGUoKS50b0lTT1N0cmluZygpCiAgICAgICAgICB9KTsqLwogICAgICAgICAgLy9pby5lbWl0KCJtZXNzYWdlIiwgY2hhdCk7CiAgICAgICAgICAvKmRiLmNvbGxlY3Rpb24oInVzZXJzIikuZ2V0KCkudGhlbih1c2VycyA9PiB7CiAgICAgICAgICAgIGlvLmVtaXQoImxlYWRlcmJvYXJkIiwgdXNlcnMpOwogICAgICAgICAgICAvL2NvbnNvbGUubG9nKHVzZXJzKTsKICAgICAgICAgIH0pKi8KICAgICAgICB9CiAgICAgIH0KICAgICAgZWxzZSB7CiAgICAgICAgc29ja2V0LmVtaXQoJ2FscmVhZHkgdXNlZCcsIGRhdGEubmFtZSk7CiAgICAgICAgY29uc29sZS5sb2coZGF0YS5uYW1lICsgIiB1c2VybmFtZSBhbHJlYWR5IHVzZWQiKTsKICAgICAgfQogICAgfSk7CiAgfSk7CiAgc29ja2V0Lm9uKCJsb2dpbiBhdHRlbXB0IiwgZnVuY3Rpb24gKGRhdGEpIHsKICAgIC8vY29uc29sZS5sb2coImxvZ2luIGF0dGVtcHQiICsgSlNPTi5zdHJpbmdpZnkoZGF0YSkpOwogICAgbGV0IG1hdGNoID0gZmFsc2U7CiAgICBsZXQgcXVlcnkgPSB1c2Vyc3JlZi53aGVyZSgidXNlck5hbWUiLCAiPT0iLCBkYXRhLnVzZXIpLmdldCgpLnRoZW4odXNlcnMgPT4gewogICAgICBpZiAodXNlcnMuZW1wdHkpIHsKICAgICAgICBjb25zb2xlLmxvZygnTm8gbWF0Y2hpbmcgZG9jdW1lbnRzLicpOwogICAgICAgIHNvY2tldC5lbWl0KCJsb2dpbiBmYWlsZWQiKTsKICAgICAgICByZXR1cm47CiAgICAgIH0KICAgICAgdXNlcnMuZm9yRWFjaCh1c2VyID0+IHsKICAgICAgICAvL2NvbnNvbGUubG9nKHVzZXIuZGF0YSgpLnVzZXJOYW1lKQogICAgICAgIGlmICh1c2VyLmRhdGEoKS5wYXNzd29yZCA9PT0gZGF0YS5wYXNzKSB7CiAgICAgICAgICBtYXRjaCA9IHRydWU7CiAgICAgICAgfQogICAgICB9KTsKICAgICAgaWYgKG1hdGNoKSB7CiAgICAgICAgdXBkYXRlT25lKCJ1c2VycyIsIGRhdGEudXNlciwgewogICAgICAgICAgdmlzaXROdW06IEZpZWxkVmFsdWUuaW5jcmVtZW50KDEpLAogICAgICAgICAgbGFzdExvZ2luOiBuZXcgRGF0ZSgpLnRvSVNPU3RyaW5nKCksCiAgICAgICAgICBvbmxpbmU6IHRydWUKICAgICAgICB9KQogICAgICAgIGlmIChkYXRhLnN1YikgewogICAgICAgICAgbmV3RDIoInN1YnMiLCB7CiAgICAgICAgICAgIHVzZXJOYW1lOiBkYXRhLnVzZXIsCiAgICAgICAgICAgIHN1YjogZGF0YS5zdWIKICAgICAgICAgIH0pCiAgICAgICAgfQogICAgICAgIC8vcHVzaChwYXlsb2FkLCAiIix3ZWJQdXNoKQogICAgICAgIHNvY2tldC5lbWl0KCJsb2dnZWQgaW4iLCBkYXRhLnVzZXIpOwogICAgICB9CiAgICAgIGVsc2UgewogICAgICAgIHNvY2tldC5lbWl0KCJsb2dpbiBmYWlsZWQiKTsKICAgICAgfQogICAgfSkKICAgIC8qVXNlci5maW5kT25lKHsKICAgICAgd2hlcmU6IHsKICAgICAgICB1c2VyTmFtZTogZGF0YS51c2VyCiAgICAgIH0KICAgIH0pLnRoZW4odXNlcnMgPT4gewogICAgICBpZiAodXNlcnMgPT09IG51bGwpIHsKICAgICAgICBzb2NrZXQuZW1pdCgibG9naW4gZmFpbGVkIik7CiAgICAgICAgLy9jb25zb2xlLmxvZygibG9naW4gZmFpbGVkICIgKyBkYXRhLnVzZXIgKyAiIGlzIG5vdCByZWdlc3RlcmVkIik7CiAgICAgIH0KICAgICAgZWxzZSBpZiAodXNlcnMuZGF0YVZhbHVlcy5wYXNzd29yZCA9PT0gZGF0YS5wYXNzKSB7CiAgICAgICAgLy9jb25zb2xlLmxvZyh1c2Vycyk7CiAgICAgICAgLy9jb25zb2xlLmxvZyh1c2Vycy5kYXRhVmFsdWVzLnBhc3N3b3JkKTsKICAgICAgICBVc2VyLnVwZGF0ZSh7CiAgICAgICAgICBsYXN0TG9naW46IG5ldyBEYXRlKCksCiAgICAgICAgICB2aXNpdE51bTogdXNlcnMudmlzaXROdW0gKyAxLAogICAgICAgICAgb25saW5lOiB0cnVlCiAgICAgICAgfSwgewogICAgICAgICAgd2hlcmU6IHsKICAgICAgICAgICAgdXNlck5hbWU6IGRhdGEudXNlcgogICAgICAgICAgfQogICAgICAgIH0pOwogICAgICAgIHN1YnMuY3JlYXRlKHsKICAgICAgICAgIHVzZXJOYW1lOiBkYXRhLm5hbWUsCiAgICAgICAgICBzdWI6IGRhdGEuc3ViCiAgICAgICAgfSkKICAgICAgICAKICAgICAgICAvL2NvbnNvbGUubG9nKCJsb2dnZWQgaW46Iit1c2Vycy5wYXNzd29yZCsiID0gIitkYXRhLnBhc3MpOwogICAgICAgIHZhciBpZCA9IHNvY2tldC5pZDsKICAgICAgICB2YXIgbmV3cGxheWVyID0gewogICAgICAgICAgaWQ6IGlkLAogICAgICAgICAgdXNlcjogZGF0YS51c2VyCiAgICAgICAgfTsKICAgICAgICBvbmxpbmVwbGF5ZXJzW2lkXSA9IG5ld3BsYXllcjsKICAgICAgICBjb25zb2xlLmxvZygicGxheWVyIGluaXRhbGl6ZWQgIiArIGRhdGEudXNlcik7CiAgICAgICAgaW8uZW1pdCgiaW4gZ2FtZSBwbGF5ZXJzIiwgewogICAgICAgICAgaWQ6IGlkLAogICAgICAgICAgb25saW5lcGxheWVyczogb25saW5lcGxheWVycwogICAgICAgIH0pOwogICAgICAgIC8vY29uc29sZS5sb2cob25saW5lcGxheWVycyk7CiAgICAgICAgLypVc2VyLmZpbmRBbGwoKS50aGVuKHVzZXJzID0+IHsKICAgICAgICAgIGZvciAodmFyIGkgaW4gdXNlcnMpIHsKICAgICAgICAgICAgLy9jb25zb2xlLmxvZyh1c2Vyc1tpXS5kYXRhVmFsdWVzLmlkLCB1c2Vyc1tpXS5kYXRhVmFsdWVzLnVzZXJOYW1lKTsKICAgICAgICAgIH0KICAgICAgICAgIC8vc29ja2V0LmVtaXQoImxlYWRlcmJvYXJkIiwgdXNlcnMpOwogICAgICAgICAgc29ja2V0LmVtaXQoImxvZ2dlZCBpbiIsIGRhdGEudXNlcik7CiAgICAgICAgICBvbmxpbmVwbHNbc29ja2V0LmlkXSA9IHsKICAgICAgICAgICAgdXNlcjogZGF0YS51c2VyCiAgICAgICAgICB9OwogICAgICAgICAgY2hhdC51bnNoaWZ0KHsKICAgICAgICAgICAgY29sOiAiYmxhY2siLAogICAgICAgICAgICB1c2VyOiAnYm90JywKICAgICAgICAgICAgbWVzc2FnZTogZGF0YS51c2VyICsgJyBpcyBvbmxpbmUgbm93JywKICAgICAgICAgICAgdGltZXNpbmNlOiBuZXcgRGF0ZSgpLnRvSVNPU3RyaW5nKCkKICAgICAgICAgIH0pOwogICAgICAgICAgaW8uZW1pdCgibWVzc2FnZSIsIGNoYXQpOwogICAgICAgIH0pCiAgICAgIH0KICAgICAgZWxzZSB7CiAgICAgICAgc29ja2V0LmVtaXQoImxvZ2luIGZhaWxlZCIpOwogICAgICAgIC8vY29uc29sZS5sb2coImxvZ2luIGZhaWxlZCAiICsgdXNlcnMucGFzc3dvcmQgKyAiIT09IiArIGRhdGEucGFzcyk7CiAgICAgIH0KICAgIH0pCiAgICAvKnNvY2tldC5vbigicGxheWVyIG1vdmVkIiwgZnVuY3Rpb24gKGRhdGEpIHsKICAgICAgaWYoIW9ubGluZXBsYXllcnNbZGF0YS5pZF0pIHJldHVybjsKICAgICAgb25saW5lcGxheWVyc1tkYXRhLmlkXS54ID0gZGF0YS54OwogICAgICBvbmxpbmVwbGF5ZXJzW2RhdGEuaWRdLnogPSBkYXRhLno7CiAgICAgIG9ubGluZXBsYXllcnNbZGF0YS5pZF0ueSA9IGRhdGEueTsKICAgICAgLy9jb25zb2xlLmxvZyhvbmxpbmVwbGF5ZXJzKTsKICAgICAgc29ja2V0LmJyb2FkY2FzdC5lbWl0KCJpbmdhbWUgcGxheWVycyBtb3ZlZCIsIGRhdGEpOwogICAgfSk7CiAgfSk7Ki8KICAgIC8qc29ja2V0Lm9uKCJxdW90ZWQiLCAoZGF0YSkgPT4gewogICAgICAvL2NvbnNvbGUubG9nKGRhdGEpCiAgICAgIFVzZXIuZmluZE9uZSh7CiAgICAgICAgd2hlcmU6IHsKICAgICAgICAgIHVzZXJOYW1lOiBkYXRhLnVzZXIKICAgICAgICB9CiAgICAgIH0pLnRoZW4odXNlciA9PiB7CiAgICAgICAgaWYgKHVzZXIgPT09IG51bGwpIHsKICAgICAgICAgIHNvY2tldC5lbWl0KCJsb2dpbiBmYWlsZWQiKTsKICAgICAgICAgIC8vY29uc29sZS5sb2coImxvZ2luIGZhaWxlZCAiICsgZGF0YS51c2VyICsgIiBpcyBub3QgcmVnZXN0ZXJlZCIpOwogICAgICAgIH0KICAgICAgICBlbHNlIGlmICh1c2VyLmRhdGFWYWx1ZXMucGFzc3dvcmQgPT09IGRhdGEucGFzcykgewogICAgICBpZiAoZGF0YS5wcm9tcHQgPT09IDApIHsKICAgICAgICBwcm9tcHQgPSBmYWxzZTsKICAgICAgfQogICAgICBlbHNlIHsKICAgICAgICBwcm9tcHQgPSB0cnVlOwogICAgICB9CiAgICAgIAogICAgICAgIHR5cGVxdWl6emluZ3Njb3Jlcy5jcmVhdGUoewogICAgICAgICAgY2g6IGRhdGEuY2gsCiAgICAgICAgICB1c2VyTmFtZTogZGF0YS51c2VyLAogICAgICAgICAgc2NvcmU6IGRhdGEuc2NvcmUsCiAgICAgICAgICB0eXBlOiAicXVvdGVkLSIgKyBwcm9tcHQsCiAgICAgICAgICBwcm9maWxlSU1HOiB1c2VyLmRhdGFWYWx1ZXMucHJvZmlsZUlNRywKICAgICAgICAgIG5hbWVDT0w6IHVzZXIuZGF0YVZhbHVlcy5uYW1lQ09sCiAgICAgICAgfSk7CiAgICAgICAgCiAgICAgICAgfQogICAgICAgIGVsc2UgewogICAgICAgICAgc29ja2V0LmVtaXQoImxvZ2luIGZhaWxlZCIpOwogICAgICAgIH0gIAogICAgICB9KQogICAgfSkKICAgIHNvY2tldC5vbigiY29tcGxldGVkIiwgKGRhdGEpID0+IHsKICAgICAgVXNlci5maW5kT25lKHsKICAgICAgICB3aGVyZTogewogICAgICAgICAgdXNlck5hbWU6IGRhdGEudXNlcgogICAgICAgIH0KICAgICAgfSkudGhlbih1c2VyID0+IHsKICAgICAgICBpZiAodXNlciA9PT0gbnVsbCkgewogICAgICAgICAgc29ja2V0LmVtaXQoImxvZ2luIGZhaWxlZCIpOwogICAgICAgICAgLy9jb25zb2xlLmxvZygibG9naW4gZmFpbGVkICIgKyBkYXRhLnVzZXIgKyAiIGlzIG5vdCByZWdlc3RlcmVkIik7CiAgICAgICAgfQogICAgICAgIGVsc2UgaWYgKHVzZXIuZGF0YVZhbHVlcy5wYXNzd29yZCA9PT0gZGF0YS5wYXNzKSB7CiAgICAgIGlmIChkYXRhLnByb21wdCA9PT0gMCkgewogICAgICAgIHByb21wdCA9IGZhbHNlOwogICAgICB9CiAgICAgIGVsc2UgewogICAgICAgIHByb21wdCA9IHRydWU7CiAgICAgIH0KICAgICAgICB0eXBlcXVpenppbmdzY29yZXMuY3JlYXRlKHsKICAgICAgICAgIGNoOiBkYXRhLmNoLAogICAgICAgICAgdXNlck5hbWU6IGRhdGEudXNlciwKICAgICAgICAgIHNjb3JlOiBkYXRhLnNjb3JlLAogICAgICAgICAgdHlwZTogImNvbXBsZXRlZC0iICsgcHJvbXB0LAogICAgICAgICAgcHJvZmlsZUlNRzogdXNlci5kYXRhVmFsdWVzLnByb2ZpbGVJTUcsCiAgICAgICAgICBuYW1lQ09MOiB1c2VyLmRhdGFWYWx1ZXMubmFtZUNPbAogICAgICAgIH0pOwogICAgICAgIH0KICAgICAgICBlbHNlIHsKICAgICAgICAgIHNvY2tldC5lbWl0KCJsb2dpbiBmYWlsZWQiKTsKICAgICAgICB9ICAKICAgICAgfSkqLwogIH0pCiAgc29ja2V0Lm9uKCJpZGxlIiwgKHVzZXIpID0+IHsKICAgIC8vY29uc29sZS5sb2codXNlciArICIgbGVmdCIpCiAgICB1c2VydGltb3V0c1t1c2VyXSA9IHNldFRpbWVvdXQoZnVuY3Rpb24gKCkgewogICAgICB1cGRhdGVPbmUoInVzZXJzIiwgdXNlciwgewogICAgICAgIG9ubGluZTogZmFsc2UKICAgICAgfSkKICAgIH0sIDIwMDAgKiA2MCAqIDQuOSk7CiAgfSkKICBzb2NrZXQub24oImFjdGl2ZSIsICh1c2VyKSA9PiB7CiAgICAvL2NvbnNvbGUubG9nKHVzZXIgKyAiIGNhbWUgYmFjayIpCiAgICBjbGVhclRpbWVvdXQodXNlcnRpbW91dHNbdXNlcl0pOwogIH0pCiAgLypzb2NrZXQub24oJ2dldGxlYWRlcmJvYXJkJywgZnVuY3Rpb24gKGZuKSB7CiAgICB0eXBlcXVpenppbmdzY29yZXMuZmluZEFsbCgpLnRoZW4oc2NvcmVzID0+IHsKICAgICAgY29uc29sZS5sb2coc2NvcmVzKTsgCiAgICAgIGZuKHNjb3Jlcyk7CiAgICB9KQogIH0pOyovCiAgc29ja2V0Lm9uKCJkaXNjb25uZWN0IiwgZnVuY3Rpb24gKCkgewogICAgLyppZiAoIW9ubGluZXBsYXllcnNbc29ja2V0LmlkXSkgcmV0dXJuOwogICAgVXNlci51cGRhdGUoewogICAgICBvbmxpbmU6IGZhbHNlCiAgICB9LCB7CiAgICAgICAgd2hlcmU6IHsKICAgICAgICAgIHVzZXJOYW1lOiBvbmxpbmVwbGF5ZXJzW3NvY2tldC5pZF0udXNlcgogICAgICAgIH0KICAgICAgfSk7ZGVsZXRlIG9ubGluZXBsYXllcnNbc29ja2V0LmlkXTsKICAgIC8vIFVwZGF0ZSBjbGllbnRzIHdpdGggdGhlIG5ldyBwbGF5ZXIga2lsbGVkIAogICAgc29ja2V0LmJyb2FkY2FzdC5lbWl0KCdsZWF2ZScsIHNvY2tldC5pZCk7CiAgICBpZiAoIW9ubGluZXBscykgcmV0dXJuOwogICAgaWYgKG9ubGluZXBsc1tzb2NrZXQuaWRdICE9PSB1bmRlZmluZWQpIHsKICAgICAgdmFyIGwgPSBvbmxpbmVwbHNbc29ja2V0LmlkXS51c2VyOwogICAgICBjaGF0LnVuc2hpZnQoewogICAgICAgIGNvbDogImJsYWNrIiwKICAgICAgICB1c2VyOiAnYm90JywKICAgICAgICBtZXNzYWdlOiBsICsgJyBpcyBvZmZsaW5lIG5vdycsCiAgICAgICAgdGltZXNpbmNlOiBuZXcgRGF0ZSgpLnRvSVNPU3RyaW5nKCkKICAgICAgfSk7CiAgICB9CiAgICBkZWxldGUgb25saW5lcGxzW3NvY2tldC5pZF07CgogICAgaW8uZW1pdCgibWVzc2FnZSIsIGNoYXQpOyovCiAgfSk7Cn0pOwp2YXIgcnRzdW0gPSBmdW5jdGlvbihhKXsKICB2YXIgYTIgPSAwOwogIGZvcih2YXIgaSBpbiBhKXsKICAgIGEyKz1hW2ldLnJhdGluZ2R0LnJhdGluZwogIH0KICByZXR1cm4gYTIKfQp2YXIgT3F1aXp6aW5nID0gaW8KICAub2YoJy9PcXVpenppbmcnKQogIC5vbignY29ubmVjdGlvbicsIGZ1bmN0aW9uIChzb2NrZXQpIHsKICAgIC8vbGV0IHBkYXRhID0gcGxheWVyc0RBVEFbc29ja2V0LmlkXTsKICAgIC8vY29uc29sZS5sb2cocGRhdGEpOwogICAgbGV0IHI7CiAgICBzb2NrZXQuZW1pdCgiZ2V0dXNlcm5hbWUiKQogICAgT3F1aXp6aW5nLmVtaXQoInBsYXl0aW1lIiwgZ2FtZXJvb21zKTsKICAgIC8vY29uc29sZS5sb2coZ2FtZXJvb21zKTsKICAgIHNldEludGVydmFsKCgpID0+IHsKICAgICAgLy9jb25zb2xlLmxvZyhnYW1lcm9vbXMua29hbGFzdHJpa2VybWkyLnBsYXllcmRhdGEpOwogICAgICBzb2NrZXQuZW1pdCgncGxheXRpbWUnLCBnYW1lcm9vbXMpOwogICAgfSwgNTAwMCk7CiAgICBzb2NrZXQub24oInVzZXJkYXRhIixmdW5jdGlvbihkYXRhKXsKICAgICAgbGV0IHBkYXRhID0gcGxheWVyc0RBVEFbc29ja2V0LmlkXTsKICAgICAgY29uc29sZS5sb2cocGRhdGEpOwogICAgICBjb25zb2xlLmxvZygiZ290IHVzZXJuYW1lIikKICAgICAgbGV0IHF1ZXJ5ID0gdXNlcnNyZWYuZG9jKGRhdGEudXNlcikuZ2V0KCkudGhlbih1c2VyID0+IHsKICAgICAgICAgIC8vY29uc29sZS5sb2codXNlci5kYXRhKCkudXNlck5hbWUpCiAgICAgICAgICBpZiAodXNlci5kYXRhKCkucGFzc3dvcmQgPT09IGRhdGEucGFzcykgewogICAgICAgICAgICBwbGF5ZXJzREFUQVtzb2NrZXQuaWRdID0gdXNlci5kYXRhKCk7CiAgICAgICAgICAgIHBsYXllcnNEQVRBW3NvY2tldC5pZF0ucm9vbXMgPSB7Z2FtZXJvb206e319CiAgICAgICAgICAgIC8vY29uc29sZS5sb2cocGxheWVyc0RBVEFbc29ja2V0LmlkXSkKICAgICAgICAgIH0KICAgICAgfSk7CiAgICB9KQogICAgc29ja2V0Lm9uKCJjcmVhdGUgbWF0Y2giLGZ1bmN0aW9uKGRhdGEpewogICAgICBsZXQgcGRhdGEgPSBwbGF5ZXJzREFUQVtzb2NrZXQuaWRdOwogICAgICBjb25zb2xlLmxvZyhwZGF0YSk7CiAgICAgIGxldCBwbGF5ZXIgPSBuZXcgZ2xpY2tvLlBsYXllcih7CiAgICAgICAgZGVmYXVsdFJhdGluZzogMTAwMCwKICAgICAgICByYXRpbmc6IHBkYXRhLnJhdGluZ3Mub3Blbk9ubGluZS5ydCwKICAgICAgICByYXRpbmdEZXZpYXRpb246IHBkYXRhLnJhdGluZ3Mub3Blbk9ubGluZS5yZCwKICAgICAgICB0YXU6IDAuNSwKICAgICAgICB2b2xhdGlsaXR5OiBwZGF0YS5yYXRpbmdzLm9wZW5PbmxpbmUucnYsCiAgICAgIH0pOwogICAgICBsZXQgbmRhdGEgPSB7dXNlcjpwZGF0YS51c2VyTmFtZSxzdGF0ZTpwZGF0YS5zdGF0ZSxyYXRpbmdkdDpwbGF5ZXIscGNvbDpwZGF0YS5uYW1lQ09sLHNjb3JlOjAsaWQ6c29ja2V0LmlkfQogICAgICBsZXQgbSA9IG5ldyBtYXRjaChkYXRhLnR5cGUscGRhdGEudXNlck5hbWUscGRhdGEudXNlck5hbWUscGRhdGEubmFtZUNPbCkKICAgICAgbS50ZWFtMS5wbGF5ZXJzID0ge30KICAgICAgbS50ZWFtMi5wbGF5ZXJzID0ge30KICAgICAgbS50ZWFtMS5uYW1lID0gInRlYW0xIjsKICAgICAgbS50ZWFtMi5uYW1lID0gInRlYW0yIjsKICAgICAgbS50ZWFtMi5lcnJvcnMgPSAwOwogICAgICBtLnRlYW0xLmVycm9ycyA9IDA7CiAgICAgIG0udGVhbTIudGltZW91dHMgPSAyOwogICAgICBtLnRlYW0xLnRpbWVvdXRzID0gMjsKICAgICAgbS50ZWFtMi5wbGF5ZXJzbnVtID0gMDsKICAgICAgbS50ZWFtMS5wbGF5ZXJzW3NvY2tldC5pZF0gPSBuZGF0YQogICAgICBtLnRlYW0xLnBsYXllcnNudW0gPSAxOwogICAgICBtLnJhdGluZ2F2ZyA9IHBkYXRhLnJhdGluZ3Mub3Blbk9ubGluZS5ydDsKICAgICAgbS5zY29yZXNoZWV0ID0gewogICAgICAgIGNxOjEsCiAgICAgICAgcToyMCwKICAgICAgICB0OlsiZyIsImciLCJnIiwiZyIsImciLCJnIiwiZyIsImciLCJnIiwiZyIsImciLCJnIiwiZyIsImciLCJnIiwiZyIsImciLCJnIiwiZyIsImciXSwKICAgICAgICB0ZWFtMTpbCiAgICAgICAgICB7c2NvcmU6MCxlcnJvcnM6MCxjOiJjIixmOltmYWxzZSxmYWxzZSxmYWxzZV0sbmFtZTpwZGF0YS51c2VyTmFtZSxxOlswLDAsMCwwLDAsMCwwLDAsMCwwLDAsMCwwLDAsMCwwLDAsMCwwLDBdLGNvbDpwZGF0YS5uYW1lQ09sfQogICAgICAgIF0sCiAgICAgICAgdGVhbTI6W10sCiAgICAgICAgdGVhbTFib251c2VzOlswLDAsMCwwLDAsMCwwLDAsMCwwLDAsMCwwLDAsMCwwLDAsMCwwLDBdLAogICAgICAgIHRlYW0yYm9udXNlczpbMCwwLDAsMCwwLDAsMCwwLDAsMCwwLDAsMCwwLDAsMCwwLDAsMCwwXSwKICAgICAgICB0ZWFtMXNjb3JlOlswLDAsMCwwLDAsMCwwLDAsMCwwLDAsMCwwLDAsMCwwLDAsMCwwLDBdLAogICAgICAgIHRlYW0yc2NvcmU6WzAsMCwwLDAsMCwwLDAsMCwwLDAsMCwwLDAsMCwwLDAsMCwwLDAsMF0sCiAgICAgICAgdGVhbTFzYzowLAogICAgICAgIHRlYW0yc2M6MCwKICAgICAgICB0ZWFtMWJzYzowLAogICAgICAgIHRlYW0yYnNjOjAKICAgICAgfQogICAgICBnYW1lcm9vbXNbcGRhdGEudXNlck5hbWVdID0gbQogICAgICBjb25zb2xlLmxvZyhtKQogICAgICBwbGF5ZXJzREFUQVtzb2NrZXQuaWRdLnJvb21zLmdhbWVyb29tLm5hbWUgPSBwZGF0YS51c2VyTmFtZTsKICAgICAgcGxheWVyc0RBVEFbc29ja2V0LmlkXS5yb29tcy5nYW1lcm9vbS50ZWFtID0gMTsKICAgICAgc29ja2V0LmpvaW4ocGRhdGEudXNlck5hbWUsIGZ1bmN0aW9uICgpIHsKICAgICAgICByID0gT2JqZWN0LmtleXMoc29ja2V0LnJvb21zKTsKICAgICAgICBjb25zb2xlLmxvZyhyKTsKICAgICAgfSk7CiAgICAgIHAycChzb2NrZXQsIG51bGwsIHBkYXRhLnVzZXJOYW1lKTsKICAgICAgc29ja2V0LmVtaXQoIk9tYXRjaCBqb2luZWQiLHt1OmdhbWVyb29tc1twZGF0YS51c2VyTmFtZV0saWQ6c29ja2V0LmlkfSkKICAgIH0pCiAgICBzb2NrZXQub24oImpvaW4gbWF0Y2giLGZ1bmN0aW9uKGtleSl7CiAgICAgIGxldCBwZGF0YSA9IHBsYXllcnNEQVRBW3NvY2tldC5pZF07CiAgICAgIGxldCBwbGF5ZXIgPSBuZXcgZ2xpY2tvLlBsYXllcih7CiAgICAgICAgZGVmYXVsdFJhdGluZzogMTAwMCwKICAgICAgICByYXRpbmc6IHBkYXRhLnJhdGluZ3Mub3Blbk9ubGluZS5ydCwKICAgICAgICByYXRpbmdEZXZpYXRpb246IHBkYXRhLnJhdGluZ3Mub3Blbk9ubGluZS5yZCwKICAgICAgICB0YXU6IDAuNSwKICAgICAgICB2b2xhdGlsaXR5OiBwZGF0YS5yYXRpbmdzLm9wZW5PbmxpbmUucnYsCiAgICAgIH0pOwogICAgICBsZXQgbmRhdGEgPSB7dXNlcjpwZGF0YS51c2VyTmFtZSxzdGF0ZTpwZGF0YS5zdGF0ZSxyYXRpbmdkdDpwbGF5ZXIscGNvbDpwZGF0YS5uYW1lQ09sLHNjb3JlOjAsaWQ6c29ja2V0LmlkfQogICAgICBwbGF5ZXJzREFUQVtzb2NrZXQuaWRdLnJvb21zLmdhbWVyb29tLm5hbWUgPSBrZXk7CiAgICAgIGlmKGdhbWVyb29tc1trZXldLnRlYW0xLnBsYXllcnNudW08PWdhbWVyb29tc1trZXldLnRlYW0yLnBsYXllcnNudW0pewogICAgICAgIHBsYXllcnNEQVRBW3NvY2tldC5pZF0ucm9vbXMuZ2FtZXJvb20udGVhbSA9IDE7CiAgICAgICAgZ2FtZXJvb21zW2tleV0udGVhbTEucGxheWVyc1tzb2NrZXQuaWRdID0gbmRhdGEKICAgICAgICBnYW1lcm9vbXNba2V5XS50ZWFtMS5wbGF5ZXJzbnVtICs9IDE7CiAgICAgICAgZ2FtZXJvb21zW2tleV0uc2NvcmVzaGVldC50ZWFtMS5wdXNoKHtzY29yZTowLGVycm9yczowLG5hbWU6cGRhdGEudXNlck5hbWUsYzoiIixmOltmYWxzZSxmYWxzZSxmYWxzZV0scTpbMCwwLDAsMCwwLDAsMCwwLDAsMCwwLDAsMCwwLDAsMCwwLDAsMCwwXSxjb2w6cGRhdGEubmFtZUNPbH0KKTsKICAgICAgfSBlbHNlewogICAgICAgIHBsYXllcnNEQVRBW3NvY2tldC5pZF0ucm9vbXMuZ2FtZXJvb20udGVhbSA9IDI7CiAgICAgICAgZ2FtZXJvb21zW2tleV0udGVhbTIucGxheWVyc1tzb2NrZXQuaWRdID0gbmRhdGEKICAgICAgICBnYW1lcm9vbXNba2V5XS50ZWFtMi5wbGF5ZXJzbnVtICs9IDE7CiAgICAgICAgaWYoZ2FtZXJvb21zW2tleV0udGVhbTIucGxheWVyc251bSA9PT0gMSl7CiAgICAgICAgICBnYW1lcm9vbXNba2V5XS5zY29yZXNoZWV0LnRlYW0yLnB1c2goe3Njb3JlOjAsZXJyb3JzOjAsYzoiYyIsZjpbZmFsc2UsZmFsc2UsZmFsc2VdLG5hbWU6cGRhdGEudXNlck5hbWUscTpbMCwwLDAsMCwwLDAsMCwwLDAsMCwwLDAsMCwwLDAsMCwwLDAsMCwwXSxjb2w6cGRhdGEubmFtZUNPbH0KICAgICAgICAgICk7CiAgICAgICAgfSBlbHNlewogICAgICAgICAgZ2FtZXJvb21zW2tleV0uc2NvcmVzaGVldC50ZWFtMi5wdXNoKHtzY29yZTowLGVycm9yczowLGM6IiIsZjpbZmFsc2UsZmFsc2UsZmFsc2VdLG5hbWU6cGRhdGEudXNlck5hbWUscTpbMCwwLDAsMCwwLDAsMCwwLDAsMCwwLDAsMCwwLDAsMCwwLDAsMCwwXSxjb2w6cGRhdGEubmFtZUNPbH0KICAgICAgICAgICk7CiAgICAgICAgfQogICAgICAgIAogICAgICB9CiAgICAgIGdhbWVyb29tc1trZXldLnBsYXllck51bSsrOwogICAgICBnYW1lcm9vbXNba2V5XS5yYXRpbmdhdmcgPSBNYXRoLnJvdW5kKChydHN1bShnYW1lcm9vbXNba2V5XS50ZWFtMS5wbGF5ZXJzKStydHN1bShnYW1lcm9vbXNba2V5XS50ZWFtMi5wbGF5ZXJzKSkvZ2FtZXJvb21zW2tleV0ucGxheWVyTnVtKTsKICAgICAgT3F1aXp6aW5nLmVtaXQoInBsYXl0aW1lIiwgZ2FtZXJvb21zKTsKICAgICAgc29ja2V0LmpvaW4oa2V5LCBmdW5jdGlvbiAoKSB7CiAgICAgICAgciA9IE9iamVjdC5rZXlzKHNvY2tldC5yb29tcyk7CiAgICAgICAgY29uc29sZS5sb2cocik7CiAgICAgICAgc29ja2V0LmVtaXQoIk9tYXRjaCBqb2luZWQiLGdhbWVyb29tc1trZXldKQogICAgICB9KTsKICAgICAgcDJwKHNvY2tldCwgbnVsbCwga2V5KTsKICAgICAgc29ja2V0LnRvKGtleSkuZW1pdCgicGxheWVyIGpvaW5lZCIse3U6Z2FtZXJvb21zW2tleV0saWQ6c29ja2V0LmlkfSkKICAgIH0pCiAgICBzb2NrZXQub24oInN0YXJ0IG1hdGNoIixmdW5jdGlvbihkYXRhKXsKCiAgICB9KQogICAgc29ja2V0Lm9uKCJlbmQgbWF0Y2giLGZ1bmN0aW9uKGRhdGEpewoKICAgIH0pCiAgICBzb2NrZXQub24oImRpc2Nvbm5lY3RpbmciLCBmdW5jdGlvbiAoKSB7CiAgICAgIHIgPSBPYmplY3Qua2V5cyhzb2NrZXQucm9vbXMpOwogICAgfSk7CiAgICBzb2NrZXQub24oImRpc2Nvbm5lY3QiLCBmdW5jdGlvbiAoKSB7CiAgICAgIGxldCBwZGF0YSA9IHBsYXllcnNEQVRBW3NvY2tldC5pZF07CiAgICAgIHNvY2tldC5icm9hZGNhc3QuZW1pdCgnbGVhdmUnLCBzb2NrZXQuaWQpOwogICAgICAvL2NvbnNvbGUubG9nKHBkYXRhLnJvb21zKQogICAgICBpZihwZGF0YS5yb29tcyE9PXVuZGVmaW5lZCl7CiAgICAgICAgaWYoZ2FtZXJvb21zW3BkYXRhLnJvb21zLmdhbWVyb29tLm5hbWVdIT09dW5kZWZpbmVkKXsKICAgICAgICAgIGdhbWVyb29tc1twZGF0YS5yb29tcy5nYW1lcm9vbS5uYW1lXS5wbGF5ZXJOdW0gLT0gMTsKICAgICAgICAgIGlmKHBkYXRhLnJvb21zLmdhbWVyb29tLnRlYW09PT0xKXsKICAgICAgICAgICAgZ2FtZXJvb21zW3BkYXRhLnJvb21zLmdhbWVyb29tLm5hbWVdLnRlYW0xLnBsYXllcnNudW0gLT0gMTsKICAgICAgICAgICAgZGVsZXRlIGdhbWVyb29tc1twZGF0YS5yb29tcy5nYW1lcm9vbS5uYW1lXS50ZWFtMS5wbGF5ZXJzW3NvY2tldC5pZF07CiAgICAgICAgICB9IGVsc2V7CiAgICAgICAgICAgIGdhbWVyb29tc1twZGF0YS5yb29tcy5nYW1lcm9vbS5uYW1lXS50ZWFtMi5wbGF5ZXJzbnVtIC09IDE7CiAgICAgICAgICAgIGRlbGV0ZSBnYW1lcm9vbXNbcGRhdGEucm9vbXMuZ2FtZXJvb20ubmFtZV0udGVhbTIucGxheWVyc1tzb2NrZXQuaWRdOwogICAgICAgICAgfQogICAgICAgICAgZ2FtZXJvb21zW3BkYXRhLnJvb21zLmdhbWVyb29tLm5hbWVdLnJhdGluZ2F2ZyA9IE1hdGgucm91bmQoKHJ0c3VtKGdhbWVyb29tc1twZGF0YS5yb29tcy5nYW1lcm9vbS5uYW1lXS50ZWFtMS5wbGF5ZXJzKStydHN1bShnYW1lcm9vbXNbcGRhdGEucm9vbXMuZ2FtZXJvb20ubmFtZV0udGVhbTIucGxheWVycykpL2dhbWVyb29tc1twZGF0YS5yb29tcy5nYW1lcm9vbS5uYW1lXS5wbGF5ZXJOdW0pOwogICAgICAgICAgaWYoZ2FtZXJvb21zW3BkYXRhLnJvb21zLmdhbWVyb29tLm5hbWVdLnBsYXllck51bSA9PT0gMCkgewogICAgICAgICAgICBkZWxldGUgZ2FtZXJvb21zW3BkYXRhLnJvb21zLmdhbWVyb29tLm5hbWVdOwogICAgICAgICAgfQogICAgICAgIH0KICAgICAgfQogICAgICBkZWxldGUgcGxheWVyc0RBVEFbc29ja2V0LmlkXTsKICAgIH0pCiAgfSkKc2V0SW50ZXJ2YWwoKCkgPT4gewogIC8vdHAuZ2V0KGBodHRwOi8vJHtwcm9jZXNzLmVudi5QUk9KRUNUX0RPTUFJTn0uZ2xpdGNoLm1lL2ApOwp9LCAyODAwMDApOwpzZXJ2ZXIubGlzdGVuKDMwMDAsICgpID0+IGNvbnNvbGUubG9nKCdzZXJ2ZXIgc3RhcnRlZCcpKTs="},"asBuffer":null},"loaded":true}}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
var reqs = 0;//https://bit.ly/2L7uHDj
const express = require('express');
const socketio = require('socket.io');
const http = require('http');
const app = express();
const server = http.Server(app);
var p2p = require('socket.io-p2p-server').Server;
const io = socketio(server); // Attach socket.io to our server
const email = "[email protected]";
const hbs = require('hbs');
const webPush = require('web-push');
var bodyParser = require('body-parser');
var serveIndex = require('serve-index');
var serveStatic = require('serve-static');
var fs = require('fs');
const frameguard = require('frameguard');
const fileUpload = require('express-fileupload');
var glicko = require("glicko-two")
const f = require("./functions");
var Jimp = require('jimp');
require('./webpackRunner');
const titles = [
  {title:"bibleSuperGrandMaster",requiredrt:2700,lossrt:2600,abr:"BSGM"},
  {title:"bibleGrandMaster",requiredrt:2500,lossrt:2400,abr:"BGM"},
  {title:"bibleMaster",requiredrt:2200,lossrt:2100,abr:"BM"},
  {title:"bibleExpert",requiredrt:2000,lossrt:1900,abr:"BE"}
];

//const db = require("./database");
/*app.use(frameguard({
  action: 'SAMEORIGIN'
}))*/
app.use(express.json()); // for parsing application/json
app.use(express.urlencoded({
  extended: true
})); // for parsing application/x-www-form-urlencoded
app.use(fileUpload());
app.use('/files', serveIndex('/', {
  'icons': true
}));
app.use('/files', serveStatic('/'));
app.post("/test", (req, res) => {
  console.log(req.body); // so when you run 
  /*
  fetch('/test', {
        method: 'POST', // *GET, POST, PUT, DELETE, etc.
        headers: { 'Content-Type': 'application/json' }, 👈 don't forget this! thx
        body: JSON.stringify({a: 1, b: 2}), // body data type must match "Content-Type" header
    });
    that in the console when veiwing the page its sopposed to log the body but it ist working plz help
  */
  console.log('form body:', req.body, req.get('content-type'));
  res.end(); // 👈 don't forget this!
});
/*const testFolder = __dirname+'/public/css';

fs.readdir(testFolder, (err, files) => {
  files.forEach(file => {
    console.log("\"/css/"+file+"\",");
  });
});*/

if (!process.env.VAPID_PUBLIC_KEY || !process.env.VAPID_PRIVATE_KEY) {
  console.log("You must set the VAPID_PUBLIC_KEY and VAPID_PRIVATE_KEY " + "environment variables. You can use the following ones:");
  console.log(webPush.generateVAPIDKeys());
  //return;
}
webPush.setVapidDetails('https://serviceworke.rs/', process.env.VAPID_PUBLIC_KEY, process.env.VAPID_PRIVATE_KEY);
const options = {
  TTL: 1 * 60 * 60 * 24
};
const admin = require('firebase-admin');
var serviceAccount = {
  "type": process.env.SAtype,
  "project_id": process.env.SAproject_id,
  "private_key_id": process.env.SAprivate_key_id,
  "private_key": process.env.SAprivate_key,
  "client_email": process.env.SAclient_email,
  "client_id": process.env.SAclient_id,
  "auth_uri": process.env.SAauth_uri,
  "token_uri": process.env.SAtoken_uri,
  "auth_provider_x509_cert_url": process.env.SAauth_provider_x509_cert_url,
  "client_x509_cert_url": process.env.SAclient_x509_cert_url
};
admin.initializeApp({
  credential: admin.credential.cert(serviceAccount),
  databaseURL: "https://bible-quiz-e1ef4.firebaseio.com"
});
//var storageRef = admin.storage().bucket();
var db = admin.firestore();
var usersref = db.collection("users");
var subsref = db.collection("subs");
var tscoresref = db.collection("typequizzingscores");
var chatref = db.collection("chats");
let FieldValue = admin.firestore.FieldValue;
var usertimouts = {}
var payload = "hi there";
//var Sequelize = require('sequelize');
//const Op = Sequelize.Op;
var ip = "";
var log = console.log;
var chat = [];
var onlinepls = {};
var gamerooms = {};
var playersDATA = {};
var emailmsg = `<b>welcome to Nazarene Bible Quizing Online</b> <br>
We hope that you enjoy your experience with our site<br>
if you have ant question or concerns plz email <a href = "mailto:[email protected]">[email protected]</a>` 
var sendmail = f.sendmail
var newD = function (c, n, data) {
  let docRef = db.collection(c).doc(n);
  data.createdAt = new Date().toISOString();
  data.updatedAt = new Date().toISOString();
  docRef.set(data);
}
var newD2 = function (c, data, t) {
  data.createdAt = new Date().toISOString();
  data.updatedAt = new Date().toISOString();
  let addDoc = db.collection(c).add(data).then(ref => {
    if (t) {
      t(ref)
    }
  });
}
var updateOne = function (c, n, data) {
  let dRef = db.collection(c).doc(n);
  data.updatedAt = FieldValue.serverTimestamp();
  dRef.update(data);
}
var onlineplayers = {};
var Admins = ["koalastrikermi"];
//sendmail("[email protected]")
/*var users2 = [
"[email protected]",
"Koalaknightmi",
"MaddieJoy",
"Pink koala",
"River gal",
"koalastrikermi",
"koalastrikermi2",
"koalastrikermi7",
"maddie2005"
]
for(var i = 0;i<users2.length;i++){
updateOne("users", users2[i],{
  titled:false,
  title:"",
  tabr:""
})
}*/
//var chatrooms = {};
var go_p2p = function (socket, room) {
  p2pserver(socket, null, room)
}
var push = push = (opt,to,webPush) => {
  if (to === "") {
    let query = subsref.get()
  .then(subs => { 
    subs.forEach(sub => {
      //console.log(sub.data().sub);
        return webPush.sendNotification(sub.data().sub, opt).catch((err) => {
          if (err.statusCode === 410) {
            console.log('Subscription is no longer valid: ', err);
            subsref.doc(sub.id).delete();
          }
          else {
            console.log(err)
          }
        });
      });
    });
  }
  else {
    let query = subsref.where("userName","==",to).get()
    .then(subs => { 
      if (subs.empty) {
        console.log('No matching documents.5');
        return;
      }  
      subs.forEach(sub => {
      //console.log(sub.data().sub);
        return webPush.sendNotification(sub.data().sub, opt).catch((err) => {
          if (err.statusCode === 410) {
            console.log('Subscription is no longer valid: ', err);
            subsref.doc(sub.id).delete();
          }
          else {
            console.log(err)
          }
        });
      });
    });
  }
};
var timeSince = f.timeSince;
var totime = f.totime;
var asort = f.asort;
//made by porter on khan academy https://www.Khanacademy.org/profile/battleboy21
String.prototype.pad = f.pad;
/*hbs.registerHelper('filter', function (context, options) {
  var a = context.sort(function (a, b) {
    return b.score - a.score;
  });
  return options.fn(this) + "<td>" + a[0].score + "</td>"
});*/
function Player(id, username, col, r) {
  this.user = username;
  this.id = id;
  this.pcol = col;
  this.prank = r;
  this.score = 0;
  this.x = 31;
  this.y = 15;
  this.z = 0;
  this.rx = 0;
  this.ry = 0;
  this.rz = 0;
  this.entity = null;
}
var getquestionset = function(n){
  let a = [];
  for(var i = 0;i<n;i++){
    a.push(i)
  }
  return a
}
function match(type, id, creator,col) {
  this.type = type;
  this.id = id;
  this.creator = creator;
  this.quizmaster = "";
  this.team1 = {};
  this.team2 = {};
  this.playerNum = 1;
  this.creatorcol = col;
  this.qcol = "";
  this.questions = getquestionset(20);
  this.qsdone = 0;
}
function checktitlereq(user,rt){
  titles.sort(function(a, b) {
    return a - b;
  });
  titles.forEach(function(t) {
    console.log(t);
    if(rt>=t.requiredrt){
      updateOne("users",user,{titled:true,title:t.title,tabr:t.abr})
    }
  });
};
function checktitleloss(user,rt){
  titles.sort(function(a, b) {
    return b - a;
  });
  titles.forEach(function(t,i,a) {
    console.log(t);
    if(rt<=t.lossrt&&t.title === "bibleExpert"){
      updateOne("users",user,{titled:false,title:"",tabr:""})
    } else if (rt<=t.lossrt){
      updateOne("users",user,{titled:true,title:a[i+1].title,tabr:a[i+1].abr})
    }
  });
};
var gamerooms = {};
hbs.registerPartials(__dirname + '/veiws/partials');
app.set('view engine', 'hbs');
app.set('views', __dirname + '/veiws/');
app.use((req, res, next) => {
  reqs++;
  //console.log(reqs)
  next();
});
app.use(express.static('public'));
app.get('/', function (request, response) {
  ip = request.headers['x-forwarded-for']
  //console.log(request)
  response.sendFile(__dirname + '/public/html/index.html');
});
app.get('/gettext', function (request, res) {
  fs.readFile('hebrews-peter.txt', function (err, data) {
    res.writeHead(200, {
      'Content-Type': 'text'
    });
    res.write(data);
    res.end();
  });
});
app.get('/leaderboardfetch', function (request, res) {
  db.collection("typequizzingscores").get().then((scores) => {
    let data = [];
    scores.forEach((doc) => {
      let dta = doc.data();
      //if(dta.score<100){
       // tscoresref.doc(doc.id).delete();
      //} else{
        dta.profileIMG = "/images/users/"+encodeURI(dta.userName)+".png"
        data.push(dta)
      //}
      
    });
    res.writeHead(200, {
      'Content-Type': 'json'
    });
    //console.log(data)
    res.write(JSON.stringify(data));
    res.end();
  })
});
app.post("/postquote", (req, res) => {
  console.log(req.body)
  var data = req.body;
  let match = false;
  let query = usersref.where("userName", "==", data.user).get().then(users => {
    if (users.empty) {
      console.log('No matching documents.');
      //socket.emit("login failed");
      return;
    }
    users.forEach(user => {
      console.log(user.data().userName)
      if (user.data().password === data.pass) {
        match = true;
      }
      if (match) {
        if (data.prompt === 0) {
          prompt = false;
        }
        else {
          prompt = true;
        }
        newD2("typequizzingscores", {
          ch: data.ch,
          userName: data.user,
          score: data.score,
          type: "quoted-" + prompt,
          profileIMG: user.data().profileIMG,
          nameCOl: user.data().nameCOl
        });
      }
      res.writeHead(200, {
        'Content-Type': 'application/json'
      });
      res.end();
    });
  });
})
app.post("/vpostquote", (req, res) => {
  console.log(req.body)
  var data = req.body;
  let match = false;
  let query = usersref.where("userName", "==", data.user).get().then(users => {
    if (users.empty) {
      console.log('No matching documents.');
      //socket.emit("login failed");
      return;
    }
    users.forEach(user => {
      console.log(user.data().userName)
      if (user.data().password === data.pass) {
        match = true;
      }
      if (match) {
        if (data.prompt === 0) {
          prompt = false;
        }
        else {
          prompt = true;
        }
        newD2("typequizzingscores", {
          ch: data.ch,
          userName: data.user,
          score: data.score,
          type: "quoted-" + prompt + "-v",
          profileIMG: user.data().profileIMG,
          nameCOl: user.data().nameCOl
        });
      }
      res.writeHead(200, {
        'Content-Type': 'application/json'
      });
      res.end();
    });
  });
})
app.post("/postcomplete", (req, res) => {
  console.log(req.body)
  var data = req.body;
  let query = usersref.where("userName", "==", data.user).get().then(users => {
    if (users.empty) {
      console.log('No matching documents.');
      //socket.emit("login failed");
      return;
    }
    users.forEach(user => {
      console.log(user.data().userName,user.data().password===data.pass)
      if (user.data().password === data.pass) {
        if (data.prompt === 0) {
          prompt = false;
        }
        else {
          prompt = true;
        }
        newD2("typequizzingscores", {
          ch: data.ch,
          userName: data.user,
          score: data.score,
          type: "completed-" + prompt,
          profileIMG: user.data().profileIMG,
          nameCOl: user.data().nameCOl
        },function(t){
          //log(t.collection("typequizzingscores").doc('lOAzzxeAPQWvBjoRmMWk'))
        });
        console.log(JSON.stringify({
          ch: data.ch,
          userName: data.user,
          score: data.score,
          type: "completed-" + prompt,
          profileIMG: user.data().profileIMG,
          nameCOl: user.data().nameCOl
        }))
      }
      res.writeHead(200, {
        'Content-Type': 'application/json'
      });
      res.write(JSON.stringify(data));
      res.end();
    });
  });

})
app.post("/vpostcomplete", (req, res) => {
  console.log(req.body)
  var data = req.body;
  let match = false;
  let query = usersref.where("userName", "==", data.user).get().then(users => {
    if (users.empty) {
      console.log('No matching documents.');
      //socket.emit("login failed");
      return;
    }
    users.forEach(user => {
      console.log(user.data().userName)
      if (user.data().password === data.pass) {
        match = true;
      }
      if (match) {
        if (data.prompt === 0) {
          prompt = false;
        }
        else {
          prompt = true;
        }
        newD2("typequizzingscores", {
          ch: data.ch,
          userName: data.user,
          score: data.score,
          type: "completed-" + prompt + "-v",
          profileIMG: user.data().profileIMG,
          nameCOl: user.data().nameCOl
        });
      }
      res.writeHead(200, {
        'Content-Type': 'application/json'
      });
      res.end();
    });
  });
})
app.post("/setProfileImg", (req, res) => {
  //console.log(req,req.get('content-type'))
  let match = false;
  let query = usersref.where("userName", "==", req.body.user).get().then(users => {
    if (users.empty) {
      console.log('No matching documents.');
      //socket.emit("login failed");
      return;
    }
    users.forEach(user => {
      console.log(user.data().password === req.body.pass)
      if (user.data().password === req.body.pass) {
        match = true;
        console.log("matched")
      }
    });
  if(match){
    let ncol = req.body.namecol;
    //console.log(req.body)
    if(req.body.ncolorchange==="true"){
      updateOne("users",req.body.user,{
        nameCOl:ncol
      })
      let tscoresquery = tscoresref.where("userName", "==", req.body.user).get().then(scores => {
        scores.forEach(user => {
          //console.log(user.id, ncol)
          updateOne("typequizzingscores",user.id,{
            nameCOl:ncol
          })
        })
      })
    }
    if(req.files!==null){
       let sampleFile = req.files.file;
      sampleFile.mv(__dirname + '/public/images/users/' + sampleFile.name , function(err) {
        if (err)
          return res.status(500)/*.send(err);*/

          Jimp.read(__dirname + '/public/images/users/' + sampleFile.name, (err, img) => {
            if (err) throw err;

            fs.unlink(__dirname + '/public/images/users/' + sampleFile.name, function (err) {
              if (err) throw err;
              console.log('File deleted!');
            });
            img
              .write(__dirname + '/public/images/users/' + req.body.user + '.png'); // save

          });
        //res.sendFile(__dirname + '/public/images/users/' + req.body.user + '.png');
      });
    }
    res.writeHead(200, {
      'Content-Type': 'text'
    });
    res.write("sucsess")
    res.end();
  }
});
})
app.post("/createquestion", (req, res) => {
    console.log(req.body)
    let data = req.body;
    let type = data.type,ref = data.refrence, question = data.question,answer = data.answer,creator = data.creator;
    let addDoc = db.collection("questions").doc(type).collection("questions").add({
      type:type,
      question:question,
      answer:answer,
      creator:creator,
      createdAt:new Date().toISOString(),
      refrence:ref
    }).then(ref => {
      res.writeHead(200, {
        'Content-Type': 'text'
      });
      res.write("sucsess")
      res.end();
    });
})
app.get('/user/:user', function (request, res) {
  var userdata = {};
  var typequizzingscores = [];
  var friendsdata = [];
  var scoresdata = [];
  var username = request.params.user;

  let userquery = usersref.where("userName", "==", username).get().then(users => {
    console.log(username)
    //console.log(users)
    if (users.empty) {
      console.log('No matching documents.1');
      //socket.emit("login failed");
      //return;
    }
    users.forEach(user => {
      let dt = user.data();
      dt.email = "";
      //console.log(new Date(dt.lastLogin))
      dt.lastLogin = timeSince(new Date(dt.lastLogin))
      dt.state = dt.state.toUpperCase();
      dt.profileIMG = "/images/users/"+encodeURI(dt.profileIMG)+".png"
      userdata = dt;
    });
    //console.log(userdata)
    if (userdata.friends.length > 0) {
      for (var i = 0; i < userdata.friends.length; i++) {
        log(userdata.friends[i])
        let friendquery = usersref.where("userName", "==", userdata.friends[i]).get().then(users => {
          //console.log(users.empty)
          //console.log(users)
          if (users.empty) {
            console.log('No matching documents.2');
            //socket.emit("login failed");
            return;
          }
          users.forEach(user => {
            let dta = user.data();
            dta.state = dta.state.toUpperCase();
            dta.profileIMG = "/images/users/"+encodeURI(dta.profileIMG)+".png"
            friendsdata.push(dta)
          });
          //log(friendsdata)

        });
      }          
      var ts = {};
      var cts = [];
      var qts = [];
      var cpts = [];
      var qpts = [];
      let tscoresquery = tscoresref.where("userName", "==", username).get().then(scores => {
        if (scores.empty) {
          console.log('No matching documents.3');
          // socket.emit("login failed");
         // return;
         res.render('user', {
          userdata: userdata,
          scoresdata: {},
          friendsdata: friendsdata,
          ts: {}
        });
        }
        else
        {
          scores.forEach(score => {
          let scdt = score.data();
          scdt.createdAt = timeSince(new Date(scdt.createdAt));
          if (scdt.type.indexOf("quote") !== -1) {
            scdt.score = totime(scdt.score);
          }
          if (scdt.type === "quoted-true") {
            scdt.type = "quoted with prompt";
            qpts.push(scdt);
          }
          else if (scdt.type === "quoted-false") {
            scdt.type = "quoted without prompt";
            qts.push(scdt);
          }
          else if (scdt.type === "completed-false") {
            scdt.type = "completed without prompt";
            cts.push(scdt);
          }
          else {
            scdt.type = "completed with prompt";
            cpts.push(scdt);
          }
          typequizzingscores.push(scdt);
        });
        cts = asort(cts, "hl", "score")
        cpts = asort(cpts, "hl", "score")
        qpts = asort(qpts, "lh", "score")
        qts = asort(qts, "lh", "score")
        ts = {
          c: cts,
          cp: cpts,
          q: qts,
          qp: qpts
        };
        typequizzingscores = typequizzingscores.sort(function(a,b){
          if(a.updatedAt._seconds===undefined){
            a = new Date(a.updatedAt).getTime()
          } else{
            a=new Date(a.updatedAt._seconds).getTime()
          }
          if(b.updatedAt._seconds === undefined){
            b = new Date(b.updatedAt).getTime()
          } else{
            b=new Date(b.updatedAt._seconds).getTime();
          }
          console.log(a,b)
          return b-a;
        })
        console.log(typequizzingscores)
        res.render('user', {
          userdata: userdata,
          scoresdata: typequizzingscores,
          friendsdata: friendsdata,
          ts: ts
        });
        }
      });
    }
    else {
      var ts = {};
      var cts = [];
      var qts = [];
      var cpts = [];
      var qpts = [];
      let tscoresquery = tscoresref.where("userName", "==", username).get().then(scores => {
        if (scores.empty) {
          console.log('No matching documents.');
          //socket.emit("login failed");
          res.render('user', {
          userdata: userdata,
          scoresdata: typequizzingscores,
          friendsdata: friendsdata,
          ts: ts
        });
        }
        else{
                  //console.log(scores.empty)
        scores.forEach(score => {
          let scdt = score.data();
          //log(scdt.type)
          scdt.createdAt = timeSince(new Date(scdt.createdAt))
          if (scdt.type.indexOf("quote") !== -1) {
            scdt.score = totime(scdt.score);
          }
          if (scdt.type === "quoted-true") {
            scdt.type = "quoted with prompt"
            qpts.push(scdt)
          }
          else if (scdt.type === "quoted-false") {
            scdt.type = "quoted without prompt"
            qts.push(scdt)
          }
          else if (scdt.type === "completed-false") {
            scdt.type = "completed without prompt"
            cts.push(scdt)
          }
          else {
            scdt.type = "completed with prompt"
            cpts.push(scdt)
          }
          typequizzingscores.push(scdt)
          //console.log()
          //friendsdata.push(score.data())
        });
        cts = asort(cts, "hl", "score")
        cpts = asort(cpts, "hl", "score")
        qpts = asort(qpts, "lh", "score")
        qts = asort(qts, "lh", "score")
        ts = {
          c: cts,
          cp: cpts,
          q: qts,
          qp: qpts
        };
        res.render('user', {
          userdata: userdata,
          scoresdata: typequizzingscores,
          friendsdata: friendsdata,
          ts: ts
        });
        }

      });
    }
  });
  /**/
});

io.sockets.on('connection', function (socket) {
  /*socket.on('message', function (data) {
    User.findOne({
      where: {
        userName: data.user
      }
    }).then(user => {
      data.col = user.dataValues.nameCOl;
    });
    data.timesince = (new Date().toISOString())
    chat.unshift(data);
    console.log(chat);
    io.emit("message",chat);
  });*/
  socket.on('vapidPublicKey', (data) => {
    socket.emit("vpk", process.env.VAPID_PUBLIC_KEY)
  }); // listen to the event
  socket.on("register", function (data, sub) {
    console.log(sub)
    let used = false;
    let query = usersref.get().then(users => {
      users.forEach(user => {
        console.log(user)
        if (user.id === data.name) {
          used = true;
        }
      });
      if (!used) {
        if (Admins.indexOf(data.name) > -1) {
          Jimp.read(__dirname + '/public/images/avatar generic.png', (err, img) => {
            if (err) throw err;

            img
              .write(__dirname + '/public/images/users/' + data.name + '.png'); // save
          });
          newD("users", data.name, {
            id: 1,
            userName: data.name,
            email: data.email,
            password: data.pass,
            lastLogin: new Date().toISOString(),
            isAdmin: true,
            visitNum: 0,
            nameCOl: 'blue',
            ratings:[
              {openOnline:1000,rd:350},
              {teamsOnline:1000,rd:350}
            ],
            gamesPlayed: 0,
            online: true,
            tournaments: '',
            friends: [],
            monthScore: 0,
            allTimeScore: 0,
            profileIMG: data.name,
            state: data.state,
            ipAD: ip,
            banned: false
          });
          newD2("subs", {
            userName: data.name,
            sub: sub
          })
          //console.log('user ' + data.name + ' registered');
          socket.emit('registered', data.name);
          //var id = socket.id;
          sendmail(data.email);
          /*db.collection("users").get().then(users => {
            io.emit("leaderboard", users);
            //console.log(users);
          })*/
        }
        else {
          Jimp.read(__dirname + '/public/images/avatar generic.png', (err, img) => {
            if (err) throw err;

            img
              .write(__dirname + '/public/images/users/' + data.name + '.png'); // save
          });
          newD("users", data.name, {
            id: 1,
            userName: data.name,
            email: data.email,
            password: data.pass,
            lastLogin: new Date().toISOString(),
            isAdmin: false,
            visitNum: 0,
            nameCOl: 'blue',
            ratings:[
              {openOnline:1000,rd:350},
              {teamsOnline:1000,rd:350}
            ],
            gamesPlayed: 0,
            online: true,
            tournaments: '',
            friends: [],
            monthScore: 0,
            allTimeScore: 0,
            profileIMG: data.name,
            state: data.state,
            ipAD: ip,
            banned: false
          });
          newD2("subs", {
            userName: data.name,
            sub: sub
          })
          //console.log('user ' + data.name + ' registered');
          socket.emit('registered', data.name);
          var id = socket.id;
          sendmail(data.email);
          /*var id = socket.id;
          var newplayer = {
            id: id,
            user: data.user
          };
          onlineplayers[id] = newplayer;
          onlinepls[socket.id] = {
            user: data.user
          };*/
          /*chat.unshift({
            col: "black",
            user: 'bot',
            message: data.user + ' is online now',
            timesince: new Date().toISOString()
          });*/
          //io.emit("message", chat);
          /*db.collection("users").get().then(users => {
            io.emit("leaderboard", users);
            //console.log(users);
          })*/
        }
      }
      else {
        socket.emit('already used', data.name);
        console.log(data.name + " username already used");
      }
    });
  });
  socket.on("login attempt", function (data) {
    //console.log("login attempt" + JSON.stringify(data));
    let match = false;
    let query = usersref.where("userName", "==", data.user).get().then(users => {
      if (users.empty) {
        console.log('No matching documents.');
        socket.emit("login failed");
        return;
      }
      users.forEach(user => {
        //console.log(user.data().userName)
        if (user.data().password === data.pass) {
          match = true;
        }
      });
      if (match) {
        updateOne("users", data.user, {
          visitNum: FieldValue.increment(1),
          lastLogin: new Date().toISOString(),
          online: true
        })
        if (data.sub) {
          newD2("subs", {
            userName: data.user,
            sub: data.sub
          })
        }
        //push(payload, "",webPush)
        socket.emit("logged in", data.user);
      }
      else {
        socket.emit("login failed");
      }
    })
    /*User.findOne({
      where: {
        userName: data.user
      }
    }).then(users => {
      if (users === null) {
        socket.emit("login failed");
        //console.log("login failed " + data.user + " is not regestered");
      }
      else if (users.dataValues.password === data.pass) {
        //console.log(users);
        //console.log(users.dataValues.password);
        User.update({
          lastLogin: new Date(),
          visitNum: users.visitNum + 1,
          online: true
        }, {
          where: {
            userName: data.user
          }
        });
        subs.create({
          userName: data.name,
          sub: data.sub
        })
        
        //console.log("logged in:"+users.password+" = "+data.pass);
        var id = socket.id;
        var newplayer = {
          id: id,
          user: data.user
        };
        onlineplayers[id] = newplayer;
        console.log("player initalized " + data.user);
        io.emit("in game players", {
          id: id,
          onlineplayers: onlineplayers
        });
        //console.log(onlineplayers);
        /*User.findAll().then(users => {
          for (var i in users) {
            //console.log(users[i].dataValues.id, users[i].dataValues.userName);
          }
          //socket.emit("leaderboard", users);
          socket.emit("logged in", data.user);
          onlinepls[socket.id] = {
            user: data.user
          };
          chat.unshift({
            col: "black",
            user: 'bot',
            message: data.user + ' is online now',
            timesince: new Date().toISOString()
          });
          io.emit("message", chat);
        })
      }
      else {
        socket.emit("login failed");
        //console.log("login failed " + users.password + "!==" + data.pass);
      }
    })
    /*socket.on("player moved", function (data) {
      if(!onlineplayers[data.id]) return;
      onlineplayers[data.id].x = data.x;
      onlineplayers[data.id].z = data.z;
      onlineplayers[data.id].y = data.y;
      //console.log(onlineplayers);
      socket.broadcast.emit("ingame players moved", data);
    });
  });*/
    /*socket.on("quoted", (data) => {
      //console.log(data)
      User.findOne({
        where: {
          userName: data.user
        }
      }).then(user => {
        if (user === null) {
          socket.emit("login failed");
          //console.log("login failed " + data.user + " is not regestered");
        }
        else if (user.dataValues.password === data.pass) {
      if (data.prompt === 0) {
        prompt = false;
      }
      else {
        prompt = true;
      }
      
        typequizzingscores.create({
          ch: data.ch,
          userName: data.user,
          score: data.score,
          type: "quoted-" + prompt,
          profileIMG: user.dataValues.profileIMG,
          nameCOL: user.dataValues.nameCOl
        });
        
        }
        else {
          socket.emit("login failed");
        }  
      })
    })
    socket.on("completed", (data) => {
      User.findOne({
        where: {
          userName: data.user
        }
      }).then(user => {
        if (user === null) {
          socket.emit("login failed");
          //console.log("login failed " + data.user + " is not regestered");
        }
        else if (user.dataValues.password === data.pass) {
      if (data.prompt === 0) {
        prompt = false;
      }
      else {
        prompt = true;
      }
        typequizzingscores.create({
          ch: data.ch,
          userName: data.user,
          score: data.score,
          type: "completed-" + prompt,
          profileIMG: user.dataValues.profileIMG,
          nameCOL: user.dataValues.nameCOl
        });
        }
        else {
          socket.emit("login failed");
        }  
      })*/
  })
  socket.on("idle", (user) => {
    //console.log(user + " left")
    usertimouts[user] = setTimeout(function () {
      updateOne("users", user, {
        online: false
      })
    }, 2000 * 60 * 4.9);
  })
  socket.on("active", (user) => {
    //console.log(user + " came back")
    clearTimeout(usertimouts[user]);
  })
  /*socket.on('getleaderboard', function (fn) {
    typequizzingscores.findAll().then(scores => {
      console.log(scores); 
      fn(scores);
    })
  });*/
  socket.on("disconnect", function () {
    /*if (!onlineplayers[socket.id]) return;
    User.update({
      online: false
    }, {
        where: {
          userName: onlineplayers[socket.id].user
        }
      });delete onlineplayers[socket.id];
    // Update clients with the new player killed 
    socket.broadcast.emit('leave', socket.id);
    if (!onlinepls) return;
    if (onlinepls[socket.id] !== undefined) {
      var l = onlinepls[socket.id].user;
      chat.unshift({
        col: "black",
        user: 'bot',
        message: l + ' is offline now',
        timesince: new Date().toISOString()
      });
    }
    delete onlinepls[socket.id];

    io.emit("message", chat);*/
  });
});
var rtsum = function(a){
  var a2 = 0;
  for(var i in a){
    a2+=a[i].ratingdt.rating
  }
  return a2
}
var Oquizzing = io
  .of('/Oquizzing')
  .on('connection', function (socket) {
    //let pdata = playersDATA[socket.id];
    //console.log(pdata);
    let r;
    socket.emit("getusername")
    Oquizzing.emit("playtime", gamerooms);
    //console.log(gamerooms);
    setInterval(() => {
      //console.log(gamerooms.koalastrikermi2.playerdata);
      socket.emit('playtime', gamerooms);
    }, 5000);
    socket.on("userdata",function(data){
      let pdata = playersDATA[socket.id];
      console.log(pdata);
      console.log("got username")
      let query = usersref.doc(data.user).get().then(user => {
          //console.log(user.data().userName)
          if (user.data().password === data.pass) {
            playersDATA[socket.id] = user.data();
            playersDATA[socket.id].rooms = {gameroom:{}}
            //console.log(playersDATA[socket.id])
          }
      });
    })
    socket.on("create match",function(data){
      let pdata = playersDATA[socket.id];
      console.log(pdata);
      let player = new glicko.Player({
        defaultRating: 1000,
        rating: pdata.ratings.openOnline.rt,
        ratingDeviation: pdata.ratings.openOnline.rd,
        tau: 0.5,
        volatility: pdata.ratings.openOnline.rv,
      });
      let ndata = {user:pdata.userName,state:pdata.state,ratingdt:player,pcol:pdata.nameCOl,score:0,id:socket.id}
      let m = new match(data.type,pdata.userName,pdata.userName,pdata.nameCOl)
      m.team1.players = {}
      m.team2.players = {}
      m.team1.name = "team1";
      m.team2.name = "team2";
      m.team2.errors = 0;
      m.team1.errors = 0;
      m.team2.timeouts = 2;
      m.team1.timeouts = 2;
      m.team2.playersnum = 0;
      m.team1.players[socket.id] = ndata
      m.team1.playersnum = 1;
      m.ratingavg = pdata.ratings.openOnline.rt;
      m.scoresheet = {
        cq:1,
        q:20,
        t:["g","g","g","g","g","g","g","g","g","g","g","g","g","g","g","g","g","g","g","g"],
        team1:[
          {score:0,errors:0,c:"c",f:[false,false,false],name:pdata.userName,q:[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],col:pdata.nameCOl}
        ],
        team2:[],
        team1bonuses:[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
        team2bonuses:[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
        team1score:[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
        team2score:[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
        team1sc:0,
        team2sc:0,
        team1bsc:0,
        team2bsc:0
      }
      gamerooms[pdata.userName] = m
      console.log(m)
      playersDATA[socket.id].rooms.gameroom.name = pdata.userName;
      playersDATA[socket.id].rooms.gameroom.team = 1;
      socket.join(pdata.userName, function () {
        r = Object.keys(socket.rooms);
        console.log(r);
      });
      p2p(socket, null, pdata.userName);
      socket.emit("Omatch joined",{u:gamerooms[pdata.userName],id:socket.id})
    })
    socket.on("join match",function(key){
      let pdata = playersDATA[socket.id];
      let player = new glicko.Player({
        defaultRating: 1000,
        rating: pdata.ratings.openOnline.rt,
        ratingDeviation: pdata.ratings.openOnline.rd,
        tau: 0.5,
        volatility: pdata.ratings.openOnline.rv,
      });
      let ndata = {user:pdata.userName,state:pdata.state,ratingdt:player,pcol:pdata.nameCOl,score:0,id:socket.id}
      playersDATA[socket.id].rooms.gameroom.name = key;
      if(gamerooms[key].team1.playersnum<=gamerooms[key].team2.playersnum){
        playersDATA[socket.id].rooms.gameroom.team = 1;
        gamerooms[key].team1.players[socket.id] = ndata
        gamerooms[key].team1.playersnum += 1;
        gamerooms[key].scoresheet.team1.push({score:0,errors:0,name:pdata.userName,c:"",f:[false,false,false],q:[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],col:pdata.nameCOl}
);
      } else{
        playersDATA[socket.id].rooms.gameroom.team = 2;
        gamerooms[key].team2.players[socket.id] = ndata
        gamerooms[key].team2.playersnum += 1;
        if(gamerooms[key].team2.playersnum === 1){
          gamerooms[key].scoresheet.team2.push({score:0,errors:0,c:"c",f:[false,false,false],name:pdata.userName,q:[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],col:pdata.nameCOl}
          );
        } else{
          gamerooms[key].scoresheet.team2.push({score:0,errors:0,c:"",f:[false,false,false],name:pdata.userName,q:[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],col:pdata.nameCOl}
          );
        }
        
      }
      gamerooms[key].playerNum++;
      gamerooms[key].ratingavg = Math.round((rtsum(gamerooms[key].team1.players)+rtsum(gamerooms[key].team2.players))/gamerooms[key].playerNum);
      Oquizzing.emit("playtime", gamerooms);
      socket.join(key, function () {
        r = Object.keys(socket.rooms);
        console.log(r);
        socket.emit("Omatch joined",gamerooms[key])
      });
      p2p(socket, null, key);
      socket.to(key).emit("player joined",{u:gamerooms[key],id:socket.id})
    })
    socket.on("start match",function(data){

    })
    socket.on("end match",function(data){

    })
    socket.on("disconnecting", function () {
      r = Object.keys(socket.rooms);
    });
    socket.on("disconnect", function () {
      let pdata = playersDATA[socket.id];
      socket.broadcast.emit('leave', socket.id);
      //console.log(pdata.rooms)
      if(pdata.rooms!==undefined){
        if(gamerooms[pdata.rooms.gameroom.name]!==undefined){
          gamerooms[pdata.rooms.gameroom.name].playerNum -= 1;
          if(pdata.rooms.gameroom.team===1){
            gamerooms[pdata.rooms.gameroom.name].team1.playersnum -= 1;
            delete gamerooms[pdata.rooms.gameroom.name].team1.players[socket.id];
          } else{
            gamerooms[pdata.rooms.gameroom.name].team2.playersnum -= 1;
            delete gamerooms[pdata.rooms.gameroom.name].team2.players[socket.id];
          }
          gamerooms[pdata.rooms.gameroom.name].ratingavg = Math.round((rtsum(gamerooms[pdata.rooms.gameroom.name].team1.players)+rtsum(gamerooms[pdata.rooms.gameroom.name].team2.players))/gamerooms[pdata.rooms.gameroom.name].playerNum);
          if(gamerooms[pdata.rooms.gameroom.name].playerNum === 0) {
            delete gamerooms[pdata.rooms.gameroom.name];
          }
        }
      }
      delete playersDATA[socket.id];
    })
  })
setInterval(() => {
  //tp.get(`http://${process.env.PROJECT_DOMAIN}.glitch.me/`);
}, 280000);
server.listen(3000, () => console.log('server started'));
node v10.16.0