poke-browser 0.4.3 → 0.4.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1,1515 @@
1
- LyoqCiAqIFJlbGF5cyBhdXRvbWF0aW9uIGNvbW1hbmRzIGZyb20gdGhlIHNlcnZpY2Ugd29ya2VyIGludG8gdGhlIHBhZ2UuCiAqLwoKY29uc3QgQ09OU09MRV9SSU5HX01BWCA9IDUwMDsKY29uc3QgUEFHRV9FUlJPUl9SSU5HX01BWCA9IDIwMDsKCi8qKiBAdHlwZSB7QXJyYXk8eyBsZXZlbDogc3RyaW5nOyBtZXNzYWdlOiBzdHJpbmc7IHRpbWVzdGFtcDogbnVtYmVyOyBzdGFjaz86IHN0cmluZyB9Pn0gKi8KbGV0IGNvbnNvbGVSaW5nID0gW107CgovKioKICogVW5jYXVnaHQgZXJyb3JzIGFuZCB1bmhhbmRsZWQgcmVqZWN0aW9ucyAoc2VwYXJhdGUgZnJvbSBjb25zb2xlIHJpbmcpLgogKiBAdHlwZSB7QXJyYXk8eyBraW5kOiBzdHJpbmc7IG1lc3NhZ2U6IHN0cmluZzsgc3RhY2s/OiBzdHJpbmc7IGZpbGVuYW1lPzogc3RyaW5nOyBsaW5lbm8/OiBudW1iZXI7IGNvbG5vPzogbnVtYmVyOyB0aW1lc3RhbXA6IG51bWJlciB9Pn0KICovCmxldCBwYWdlRXJyb3JSaW5nID0gW107CgovKioKICogQHBhcmFtIHt7IGtpbmQ6IHN0cmluZzsgbWVzc2FnZTogc3RyaW5nOyBzdGFjaz86IHN0cmluZzsgZmlsZW5hbWU/OiBzdHJpbmc7IGxpbmVubz86IG51bWJlcjsgY29sbm8/OiBudW1iZXI7IHRpbWVzdGFtcDogbnVtYmVyIH19IGVudHJ5CiAqLwpmdW5jdGlvbiBwdXNoUGFnZUVycm9yKGVudHJ5KSB7CiAgcGFnZUVycm9yUmluZy5wdXNoKGVudHJ5KTsKICB3aGlsZSAocGFnZUVycm9yUmluZy5sZW5ndGggPiBQQUdFX0VSUk9SX1JJTkdfTUFYKSBwYWdlRXJyb3JSaW5nLnNoaWZ0KCk7Cn0KCndpbmRvdy5hZGRFdmVudExpc3RlbmVyKCJlcnJvciIsIChldikgPT4gewogIHRyeSB7CiAgICBwdXNoUGFnZUVycm9yKHsKICAgICAga2luZDogImVycm9yIiwKICAgICAgbWVzc2FnZTogZXYubWVzc2FnZSB8fCBTdHJpbmcoZXYuZXJyb3IgfHwgImVycm9yIiksCiAgICAgIHN0YWNrOiBldi5lcnJvciBpbnN0YW5jZW9mIEVycm9yID8gZXYuZXJyb3Iuc3RhY2sgOiB1bmRlZmluZWQsCiAgICAgIGZpbGVuYW1lOiBldi5maWxlbmFtZSwKICAgICAgbGluZW5vOiBldi5saW5lbm8sCiAgICAgIGNvbG5vOiBldi5jb2xubywKICAgICAgdGltZXN0YW1wOiBEYXRlLm5vdygpLAogICAgfSk7CiAgfSBjYXRjaCB7CiAgICAvKiBpZ25vcmUgKi8KICB9Cn0pOwoKd2luZG93LmFkZEV2ZW50TGlzdGVuZXIoInVuaGFuZGxlZHJlamVjdGlvbiIsIChldikgPT4gewogIHRyeSB7CiAgICBjb25zdCByZWFzb24gPSBldi5yZWFzb247CiAgICBjb25zdCBtZXNzYWdlID0KICAgICAgcmVhc29uIGluc3RhbmNlb2YgRXJyb3IgPyByZWFzb24ubWVzc2FnZSA6IHR5cGVvZiByZWFzb24gPT09ICJzdHJpbmciID8gcmVhc29uIDogU3RyaW5nKHJlYXNvbik7CiAgICBjb25zdCBzdGFjayA9IHJlYXNvbiBpbnN0YW5jZW9mIEVycm9yID8gcmVhc29uLnN0YWNrIDogdW5kZWZpbmVkOwogICAgcHVzaFBhZ2VFcnJvcih7CiAgICAgIGtpbmQ6ICJ1bmhhbmRsZWRyZWplY3Rpb24iLAogICAgICBtZXNzYWdlLAogICAgICBzdGFjaywKICAgICAgdGltZXN0YW1wOiBEYXRlLm5vdygpLAogICAgfSk7CiAgfSBjYXRjaCB7CiAgICAvKiBpZ25vcmUgKi8KICB9Cn0pOwoKLyoqCiAqIEBwYXJhbSB7dW5rbm93bn0gYQogKi8KZnVuY3Rpb24gZm9ybWF0Q29uc29sZUFyZyhhKSB7CiAgaWYgKGEgaW5zdGFuY2VvZiBFcnJvcikgcmV0dXJuIGEuc3RhY2sgfHwgYS5tZXNzYWdlOwogIGlmICh0eXBlb2YgYSA9PT0gIm9iamVjdCIgJiYgYSAhPT0gbnVsbCkgewogICAgdHJ5IHsKICAgICAgcmV0dXJuIEpTT04uc3RyaW5naWZ5KGEpOwogICAgfSBjYXRjaCB7CiAgICAgIHJldHVybiBTdHJpbmcoYSk7CiAgICB9CiAgfQogIHJldHVybiBTdHJpbmcoYSk7Cn0KCi8qKgogKiBAcGFyYW0ge3N0cmluZ30gbGV2ZWwKICogQHBhcmFtIHt1bmtub3duW119IGFyZ3MKICovCmZ1bmN0aW9uIHB1c2hDb25zb2xlRW50cnkobGV2ZWwsIGFyZ3MpIHsKICBjb25zdCBtZXNzYWdlID0gYXJncy5tYXAoZm9ybWF0Q29uc29sZUFyZykuam9pbigiICIpLnNsaWNlKDAsIDIwMDAwKTsKICBjb25zdCBlcnJBcmcgPSBhcmdzLmZpbmQoKHgpID0+IHggaW5zdGFuY2VvZiBFcnJvcik7CiAgY29uc29sZVJpbmcucHVzaCh7CiAgICBsZXZlbCwKICAgIG1lc3NhZ2UsCiAgICB0aW1lc3RhbXA6IERhdGUubm93KCksCiAgICBzdGFjazogZXJyQXJnIGluc3RhbmNlb2YgRXJyb3IgPyBlcnJBcmcuc3RhY2sgOiB1bmRlZmluZWQsCiAgfSk7CiAgd2hpbGUgKGNvbnNvbGVSaW5nLmxlbmd0aCA+IENPTlNPTEVfUklOR19NQVgpIGNvbnNvbGVSaW5nLnNoaWZ0KCk7Cn0KClsibG9nIiwgImluZm8iLCAid2FybiIsICJlcnJvciJdLmZvckVhY2goKGxldmVsKSA9PiB7CiAgY29uc3Qgb3JpZyA9IGNvbnNvbGVbbGV2ZWxdLmJpbmQoY29uc29sZSk7CiAgY29uc29sZVtsZXZlbF0gPSBmdW5jdGlvbiBwb2tlQ29uc29sZVBhdGNoZWQoLi4uYXJncykgewogICAgdHJ5IHsKICAgICAgcHVzaENvbnNvbGVFbnRyeShsZXZlbCwgYXJncyk7CiAgICB9IGNhdGNoIHsKICAgICAgLyogaWdub3JlIHJpbmcgZmFpbHVyZXMgKi8KICAgIH0KICAgIG9yaWcoLi4uYXJncyk7CiAgfTsKfSk7CgovKioKICogUXVlcnkgc2VsZWN0b3IgYWNyb3NzIHRoZSBkb2N1bWVudCB0cmVlIGFuZCBpbnNpZGUgb3BlbiBzaGFkb3cgcm9vdHMgKHNhbWUtZG9jdW1lbnQ7IGRvZXMgbm90IGNyb3NzIGlmcmFtZXMpLgogKiBAcGFyYW0ge0RvY3VtZW50IHwgU2hhZG93Um9vdCB8IEVsZW1lbnR9IHJvb3QKICogQHBhcmFtIHtzdHJpbmd9IHNlbGVjdG9yCiAqIEByZXR1cm5zIHtFbGVtZW50W119CiAqLwpmdW5jdGlvbiBkZWVwUXVlcnlBbGwocm9vdCwgc2VsZWN0b3IpIHsKICAvKiogQHR5cGUge0VsZW1lbnRbXX0gKi8KICBjb25zdCByZXN1bHRzID0gW107CiAgdHJ5IHsKICAgIHJlc3VsdHMucHVzaCguLi5yb290LnF1ZXJ5U2VsZWN0b3JBbGwoc2VsZWN0b3IpKTsKICB9IGNhdGNoIHsKICAgIHJldHVybiByZXN1bHRzOwogIH0KICBmb3IgKGNvbnN0IGVsIG9mIHJvb3QucXVlcnlTZWxlY3RvckFsbCgiKiIpKSB7CiAgICBpZiAoZWwuc2hhZG93Um9vdCkgewogICAgICByZXN1bHRzLnB1c2goLi4uZGVlcFF1ZXJ5QWxsKGVsLnNoYWRvd1Jvb3QsIHNlbGVjdG9yKSk7CiAgICB9CiAgfQogIHJldHVybiByZXN1bHRzOwp9CgovKioKICogWFBhdGggYWNyb3NzIHRoZSBsaWdodCB0cmVlIGFuZCBlYWNoIG9wZW4gc2hhZG93IHJvb3QgKHNoYWRvdyBldmFsdWF0ZWQgd2l0aCB0aGF0IHJvb3QgYXMgY29udGV4dCkuCiAqIEBwYXJhbSB7c3RyaW5nfSBleHByCiAqIEByZXR1cm5zIHtFbGVtZW50W119CiAqLwpmdW5jdGlvbiBkZWVwWFBhdGhBbGwoZXhwcikgewogIC8qKiBAdHlwZSB7RWxlbWVudFtdfSAqLwogIGNvbnN0IG91dCA9IFtdOwogIC8qKgogICAqIEBwYXJhbSB7RG9jdW1lbnQgfCBTaGFkb3dSb290fSBjb250ZXh0CiAgICovCiAgZnVuY3Rpb24gY29sbGVjdEZyb20oY29udGV4dCkgewogICAgdHJ5IHsKICAgICAgY29uc3QgciA9IGRvY3VtZW50LmV2YWx1YXRlKGV4cHIsIGNvbnRleHQsIG51bGwsIFhQYXRoUmVzdWx0Lk9SREVSRURfTk9ERV9TTkFQU0hPVF9UWVBFLCBudWxsKTsKICAgICAgZm9yIChsZXQgaSA9IDA7IGkgPCByLnNuYXBzaG90TGVuZ3RoOyBpKyspIHsKICAgICAgICBjb25zdCBuID0gci5zbmFwc2hvdEl0ZW0oaSk7CiAgICAgICAgaWYgKG4gaW5zdGFuY2VvZiBFbGVtZW50KSBvdXQucHVzaChuKTsKICAgICAgfQogICAgfSBjYXRjaCB7CiAgICAgIC8qIGlnbm9yZSAqLwogICAgfQogIH0KICBjb2xsZWN0RnJvbShkb2N1bWVudCk7CiAgLyoqCiAgICogQHBhcmFtIHtFbGVtZW50fSBlbAogICAqLwogIGZ1bmN0aW9uIHdhbGsoZWwpIHsKICAgIGlmIChlbC5zaGFkb3dSb290KSB7CiAgICAgIGNvbGxlY3RGcm9tKGVsLnNoYWRvd1Jvb3QpOwogICAgICBmb3IgKGNvbnN0IGMgb2YgZWwuc2hhZG93Um9vdC5jaGlsZHJlbikgd2FsayhjKTsKICAgIH0KICAgIGZvciAoY29uc3QgYyBvZiBlbC5jaGlsZHJlbikgd2FsayhjKTsKICB9CiAgaWYgKGRvY3VtZW50LmRvY3VtZW50RWxlbWVudCkgd2Fsayhkb2N1bWVudC5kb2N1bWVudEVsZW1lbnQpOwogIHJldHVybiBvdXQ7Cn0KCi8qKgogKiBAcGFyYW0ge3N0cmluZ30gc2VsZWN0b3IKICogQHJldHVybnMge0VsZW1lbnQgfCBudWxsfQogKi8KZnVuY3Rpb24gcXVlcnlTZWxlY3Rvck9yWFBhdGgoc2VsZWN0b3IpIHsKICBjb25zdCBzID0gc2VsZWN0b3IudHJpbSgpOwogIGlmIChzLnN0YXJ0c1dpdGgoIi8vIikgfHwgcy50b0xvd2VyQ2FzZSgpLnN0YXJ0c1dpdGgoInhwYXRoOiIpKSB7CiAgICBjb25zdCBleHByID0gcy50b0xvd2VyQ2FzZSgpLnN0YXJ0c1dpdGgoInhwYXRoOiIpID8gcy5zbGljZSg2KS50cmltKCkgOiBzOwogICAgY29uc3QgYWxsID0gZGVlcFhQYXRoQWxsKGV4cHIpOwogICAgcmV0dXJuIGFsbFswXSA/PyBudWxsOwogIH0KICBjb25zdCBhbGwgPSBkZWVwUXVlcnlBbGwoZG9jdW1lbnQsIHMpOwogIHJldHVybiBhbGxbMF0gPz8gbnVsbDsKfQoKLyoqCiAqIEBwYXJhbSB7RWxlbWVudH0gZWwKICovCmZ1bmN0aW9uIGVsZW1lbnRTdW1tYXJ5KGVsKSB7CiAgY29uc3QgdGFnID0gZWwudGFnTmFtZS50b0xvd2VyQ2FzZSgpOwogIGNvbnN0IGlkID0gZWwuaWQgfHwgdW5kZWZpbmVkOwogIGNvbnN0IGNsYXNzZXMgPSB0eXBlb2YgZWwuY2xhc3NOYW1lID09PSAic3RyaW5nIiA/IGVsLmNsYXNzTmFtZSA6ICIiOwogIGxldCB0ZXh0ID0gIiI7CiAgaWYgKGVsIGluc3RhbmNlb2YgSFRNTElucHV0RWxlbWVudCB8fCBlbCBpbnN0YW5jZW9mIEhUTUxUZXh0QXJlYUVsZW1lbnQpIHsKICAgIHRleHQgPSBlbC52YWx1ZT8uc2xpY2UoMCwgMjAwKSA/PyAiIjsKICB9IGVsc2UgewogICAgdGV4dCA9IChlbC50ZXh0Q29udGVudCB8fCAiIikudHJpbSgpLnNsaWNlKDAsIDIwMCk7CiAgfQogIHJldHVybiB7IHRhZywgaWQsIGNsYXNzZXMsIHRleHQgfTsKfQoKLyoqCiAqIFZpZXdwb3J0IGNsaWVudCBjb29yZGluYXRlcyB1c2VkIGFzIHRoZSBzeW50aGV0aWMgY2xpY2sgYW5jaG9yIChzYW1lIGFzIHN5bnRoZXRpY0NsaWNrKS4KICogQHBhcmFtIHtFbGVtZW50fSBlbAogKi8KZnVuY3Rpb24gZ2V0U3ludGhldGljQ2xpY2tDbGllbnRQb2ludChlbCkgewogIGNvbnN0IHIgPSBlbC5nZXRCb3VuZGluZ0NsaWVudFJlY3QoKTsKICByZXR1cm4gewogICAgeDogci5sZWZ0ICsgTWF0aC5taW4oci53aWR0aCAvIDIsIDUwKSwKICAgIHk6IHIudG9wICsgTWF0aC5taW4oci5oZWlnaHQgLyAyLCA1MCksCiAgfTsKfQoKLyoqCiAqIEBwYXJhbSB7RWxlbWVudH0gZWwKICovCmZ1bmN0aW9uIHN5bnRoZXRpY0NsaWNrKGVsKSB7CiAgY29uc3QgeyB4LCB5IH0gPSBnZXRTeW50aGV0aWNDbGlja0NsaWVudFBvaW50KGVsKTsKICBjb25zdCBpbml0ID0geyBidWJibGVzOiB0cnVlLCBjYW5jZWxhYmxlOiB0cnVlLCB2aWV3OiB3aW5kb3csIGNsaWVudFg6IHgsIGNsaWVudFk6IHkgfTsKICBlbC5kaXNwYXRjaEV2ZW50KG5ldyBNb3VzZUV2ZW50KCJtb3VzZWRvd24iLCBpbml0KSk7CiAgZWwuZGlzcGF0Y2hFdmVudChuZXcgTW91c2VFdmVudCgibW91c2V1cCIsIGluaXQpKTsKICBpZiAodHlwZW9mIGVsLmNsaWNrID09PSAiZnVuY3Rpb24iKSBlbC5jbGljaygpOwogIGVsc2UgZWwuZGlzcGF0Y2hFdmVudChuZXcgTW91c2VFdmVudCgiY2xpY2siLCBpbml0KSk7Cn0KCi8qKgogKiBAcGFyYW0ge3Vua25vd259IG1lc3NhZ2UKICogQHBhcmFtIHsocjogdW5rbm93bikgPT4gdm9pZH0gc2VuZFJlc3BvbnNlCiAqLwpmdW5jdGlvbiBoYW5kbGVDbGlja0VsZW1lbnQobWVzc2FnZSwgc2VuZFJlc3BvbnNlKSB7CiAgY29uc3QgbSA9IC8qKiBAdHlwZSB7eyBzZWxlY3Rvcj86IHN0cmluZyB9fSAqLyAobWVzc2FnZSk7CiAgY29uc3Qgc2VsZWN0b3IgPSB0eXBlb2YgbS5zZWxlY3RvciA9PT0gInN0cmluZyIgPyBtLnNlbGVjdG9yIDogIiI7CiAgaWYgKCFzZWxlY3RvcikgewogICAgc2VuZFJlc3BvbnNlKHsgc3VjY2VzczogZmFsc2UsIGVycm9yOiAiTWlzc2luZyBzZWxlY3RvciIgfSk7CiAgICByZXR1cm47CiAgfQogIGNvbnN0IGVsID0gcXVlcnlTZWxlY3Rvck9yWFBhdGgoc2VsZWN0b3IpOwogIGlmICghZWwpIHsKICAgIHNlbmRSZXNwb25zZSh7IHN1Y2Nlc3M6IGZhbHNlLCBlcnJvcjogIkVsZW1lbnQgbm90IGZvdW5kIiB9KTsKICAgIHJldHVybjsKICB9CiAgdHJ5IHsKICAgIHN5bnRoZXRpY0NsaWNrKGVsKTsKICAgIHNlbmRSZXNwb25zZSh7IHN1Y2Nlc3M6IHRydWUsIGVsZW1lbnQ6IGVsZW1lbnRTdW1tYXJ5KGVsKSB9KTsKICB9IGNhdGNoIChlcnIpIHsKICAgIHNlbmRSZXNwb25zZSh7IHN1Y2Nlc3M6IGZhbHNlLCBlcnJvcjogU3RyaW5nKGVycikgfSk7CiAgfQp9CgovKioKICogQHBhcmFtIHt1bmtub3dufSBtZXNzYWdlCiAqIEBwYXJhbSB7KHI6IHVua25vd24pID0+IHZvaWR9IHNlbmRSZXNwb25zZQogKi8KZnVuY3Rpb24gaGFuZGxlUmVzb2x2ZUNsaWNrUG9pbnQobWVzc2FnZSwgc2VuZFJlc3BvbnNlKSB7CiAgY29uc3QgbSA9IC8qKiBAdHlwZSB7eyBzZWxlY3Rvcj86IHN0cmluZyB9fSAqLyAobWVzc2FnZSk7CiAgY29uc3Qgc2VsZWN0b3IgPSB0eXBlb2YgbS5zZWxlY3RvciA9PT0gInN0cmluZyIgPyBtLnNlbGVjdG9yIDogIiI7CiAgaWYgKCFzZWxlY3RvcikgewogICAgc2VuZFJlc3BvbnNlKHsgc3VjY2VzczogZmFsc2UsIGVycm9yOiAiTWlzc2luZyBzZWxlY3RvciIgfSk7CiAgICByZXR1cm47CiAgfQogIGNvbnN0IGVsID0gcXVlcnlTZWxlY3Rvck9yWFBhdGgoc2VsZWN0b3IpOwogIGlmICghZWwpIHsKICAgIHNlbmRSZXNwb25zZSh7IHN1Y2Nlc3M6IGZhbHNlLCBlcnJvcjogIkVsZW1lbnQgbm90IGZvdW5kIiB9KTsKICAgIHJldHVybjsKICB9CiAgY29uc3QgeyB4LCB5IH0gPSBnZXRTeW50aGV0aWNDbGlja0NsaWVudFBvaW50KGVsKTsKICBzZW5kUmVzcG9uc2UoeyBzdWNjZXNzOiB0cnVlLCB4LCB5IH0pOwp9CgovKioKICogQHBhcmFtIHt1bmtub3dufSBtZXNzYWdlCiAqIEBwYXJhbSB7KHI6IHVua25vd24pID0+IHZvaWR9IHNlbmRSZXNwb25zZQogKi8KZnVuY3Rpb24gaGFuZGxlVHlwZVRleHQobWVzc2FnZSwgc2VuZFJlc3BvbnNlKSB7CiAgY29uc3QgbSA9IC8qKiBAdHlwZSB7eyB0ZXh0Pzogc3RyaW5nOyBzZWxlY3Rvcj86IHN0cmluZzsgY2xlYXI/OiBib29sZWFuIH19ICovIChtZXNzYWdlKTsKICBjb25zdCB0ZXh0ID0gdHlwZW9mIG0udGV4dCA9PT0gInN0cmluZyIgPyBtLnRleHQgOiAiIjsKICBjb25zdCBzaG91bGRDbGVhciA9IG0uY2xlYXIgIT09IGZhbHNlOwogIGxldCBlbCA9IG51bGw7CiAgaWYgKHR5cGVvZiBtLnNlbGVjdG9yID09PSAic3RyaW5nIiAmJiBtLnNlbGVjdG9yLnRyaW0oKSkgewogICAgZWwgPSBxdWVyeVNlbGVjdG9yT3JYUGF0aChtLnNlbGVjdG9yKTsKICB9IGVsc2UgewogICAgY29uc3QgYSA9IGRvY3VtZW50LmFjdGl2ZUVsZW1lbnQ7CiAgICBlbCA9IGEgaW5zdGFuY2VvZiBFbGVtZW50ID8gYSA6IG51bGw7CiAgfQogIGlmICghZWwgfHwgIShlbCBpbnN0YW5jZW9mIEhUTUxFbGVtZW50KSkgewogICAgc2VuZFJlc3BvbnNlKHsgc3VjY2VzczogZmFsc2UsIGNoYXJzVHlwZWQ6IDAgfSk7CiAgICByZXR1cm47CiAgfQoKICB0cnkgewogICAgaWYgKGVsLmlzQ29udGVudEVkaXRhYmxlKSB7CiAgICAgIGVsLmZvY3VzKCk7CiAgICAgIGlmIChzaG91bGRDbGVhcikgewogICAgICAgIGNvbnN0IHNlbCA9IHdpbmRvdy5nZXRTZWxlY3Rpb24oKTsKICAgICAgICBpZiAoc2VsICYmIGVsLmZpcnN0Q2hpbGQpIHsKICAgICAgICAgIGNvbnN0IHJhbmdlID0gZG9jdW1lbnQuY3JlYXRlUmFuZ2UoKTsKICAgICAgICAgIHJhbmdlLnNlbGVjdE5vZGVDb250ZW50cyhlbCk7CiAgICAgICAgICBzZWwucmVtb3ZlQWxsUmFuZ2VzKCk7CiAgICAgICAgICBzZWwuYWRkUmFuZ2UocmFuZ2UpOwogICAgICAgIH0KICAgICAgICBkb2N1bWVudC5leGVjQ29tbWFuZCgiZGVsZXRlIik7CiAgICAgICAgLy8gVXNlIGV4ZWNDb21tYW5kKCdpbnNlcnRUZXh0Jykgc28gUmVhY3QvRHJhZnQuanMgc3ludGhldGljIGV2ZW50cyBmaXJlIGNvcnJlY3RseS4KICAgICAgICAvLyBEaXJlY3QgdGV4dENvbnRlbnQgYXNzaWdubWVudCBieXBhc3NlcyB0aGUgYmVmb3JlaW5wdXQvaW5wdXQgZXZlbnQgY2hhaW4uCiAgICAgICAgZG9jdW1lbnQuZXhlY0NvbW1hbmQoImluc2VydFRleHQiLCBmYWxzZSwgdGV4dCk7CiAgICAgIH0gZWxzZSB7CiAgICAgICAgZG9jdW1lbnQuZXhlY0NvbW1hbmQoImluc2VydFRleHQiLCBmYWxzZSwgdGV4dCk7CiAgICAgIH0KICAgICAgLy8gZXhlY0NvbW1hbmQgZmlyZXMgaXRzIG93biBpbnB1dCBldmVudHM7IG9ubHkgZGlzcGF0Y2ggY2hhbmdlIGZvciBub24tUmVhY3QgbGlzdGVuZXJzLgogICAgICBlbC5kaXNwYXRjaEV2ZW50KG5ldyBFdmVudCgiY2hhbmdlIiwgeyBidWJibGVzOiB0cnVlIH0pKTsKICAgICAgc2VuZFJlc3BvbnNlKHsgc3VjY2VzczogdHJ1ZSwgY2hhcnNUeXBlZDogdGV4dC5sZW5ndGggfSk7CiAgICAgIHJldHVybjsKICAgIH0KCiAgICBjb25zdCB0YWcgPSBlbC50YWdOYW1lLnRvTG93ZXJDYXNlKCk7CiAgICBpZiAodGFnID09PSAiaW5wdXQiIHx8IHRhZyA9PT0gInRleHRhcmVhIikgewogICAgICBjb25zdCBpbnB1dCA9IC8qKiBAdHlwZSB7SFRNTElucHV0RWxlbWVudCB8IEhUTUxUZXh0QXJlYUVsZW1lbnR9ICovIChlbCk7CiAgICAgIGlucHV0LmZvY3VzKCk7CiAgICAgIGlmIChzaG91bGRDbGVhcikgewogICAgICAgIGlucHV0LnNlbGVjdCgpOwogICAgICAgIGRvY3VtZW50LmV4ZWNDb21tYW5kKCJkZWxldGUiKTsKICAgICAgICBpbnB1dC52YWx1ZSA9IHRleHQ7CiAgICAgIH0gZWxzZSB7CiAgICAgICAgaW5wdXQudmFsdWUgPSAoaW5wdXQudmFsdWUgfHwgIiIpICsgdGV4dDsKICAgICAgfQogICAgICBpbnB1dC5kaXNwYXRjaEV2ZW50KG5ldyBJbnB1dEV2ZW50KCJpbnB1dCIsIHsgYnViYmxlczogdHJ1ZSwgZGF0YTogdGV4dCwgaW5wdXRUeXBlOiAiaW5zZXJ0VGV4dCIgfSkpOwogICAgICBpbnB1dC5kaXNwYXRjaEV2ZW50KG5ldyBFdmVudCgiY2hhbmdlIiwgeyBidWJibGVzOiB0cnVlIH0pKTsKICAgICAgc2VuZFJlc3BvbnNlKHsgc3VjY2VzczogdHJ1ZSwgY2hhcnNUeXBlZDogdGV4dC5sZW5ndGggfSk7CiAgICAgIHJldHVybjsKICAgIH0KCiAgICBzZW5kUmVzcG9uc2UoeyBzdWNjZXNzOiBmYWxzZSwgY2hhcnNUeXBlZDogMCB9KTsKICB9IGNhdGNoIChlcnIpIHsKICAgIHNlbmRSZXNwb25zZSh7IHN1Y2Nlc3M6IGZhbHNlLCBjaGFyc1R5cGVkOiAwLCBlcnJvcjogU3RyaW5nKGVycikgfSk7CiAgfQp9CgovKioKICogQHBhcmFtIHt1bmtub3dufSBtZXNzYWdlCiAqIEBwYXJhbSB7KHI6IHVua25vd24pID0+IHZvaWR9IHNlbmRSZXNwb25zZQogKi8KZnVuY3Rpb24gaGFuZGxlU2Nyb2xsV2luZG93KG1lc3NhZ2UsIHNlbmRSZXNwb25zZSkgewogIGNvbnN0IG0gPSAvKiogQHR5cGUge3sgcGF5bG9hZD86IFJlY29yZDxzdHJpbmcsIHVua25vd24+IH19ICovIChtZXNzYWdlKTsKICBjb25zdCBwID0gbS5wYXlsb2FkICYmIHR5cGVvZiBtLnBheWxvYWQgPT09ICJvYmplY3QiID8gbS5wYXlsb2FkIDoge307CiAgY29uc3QgYmVoYXZpb3IgPSBwLmJlaGF2aW9yID09PSAic21vb3RoIiA/ICJzbW9vdGgiIDogImF1dG8iOwogIGNvbnN0IHNlbGVjdG9yID0gdHlwZW9mIHAuc2VsZWN0b3IgPT09ICJzdHJpbmciID8gcC5zZWxlY3Rvci50cmltKCkgOiAiIjsKICBjb25zdCBkaXJSYXcgPSB0eXBlb2YgcC5kaXJlY3Rpb24gPT09ICJzdHJpbmciID8gcC5kaXJlY3Rpb24udG9Mb3dlckNhc2UoKSA6ICIiOwogIGNvbnN0IGRpciA9CiAgICBkaXJSYXcgPT09ICJ1cCIgfHwgZGlyUmF3ID09PSAiZG93biIgfHwgZGlyUmF3ID09PSAibGVmdCIgfHwgZGlyUmF3ID09PSAicmlnaHQiID8gZGlyUmF3IDogIiI7CgogIHRyeSB7CiAgICBpZiAoc2VsZWN0b3IpIHsKICAgICAgY29uc3QgZWwgPSBxdWVyeVNlbGVjdG9yT3JYUGF0aChzZWxlY3Rvcik7CiAgICAgIGlmICghZWwpIHsKICAgICAgICBzZW5kUmVzcG9uc2UoeyBzdWNjZXNzOiBmYWxzZSwgc2Nyb2xsWDogd2luZG93LnNjcm9sbFgsIHNjcm9sbFk6IHdpbmRvdy5zY3JvbGxZLCBlcnJvcjogIkVsZW1lbnQgbm90IGZvdW5kIiB9KTsKICAgICAgICByZXR1cm47CiAgICAgIH0KICAgICAgZWwuc2Nyb2xsSW50b1ZpZXcoeyBiZWhhdmlvciwgYmxvY2s6ICJjZW50ZXIiLCBpbmxpbmU6ICJuZWFyZXN0IiB9KTsKICAgIH0gZWxzZSBpZiAodHlwZW9mIHAueCA9PT0gIm51bWJlciIgfHwgdHlwZW9mIHAueSA9PT0gIm51bWJlciIpIHsKICAgICAgY29uc3QgbGVmdCA9IHR5cGVvZiBwLnggPT09ICJudW1iZXIiID8gcC54IDogd2luZG93LnNjcm9sbFg7CiAgICAgIGNvbnN0IHRvcCA9IHR5cGVvZiBwLnkgPT09ICJudW1iZXIiID8gcC55IDogd2luZG93LnNjcm9sbFk7CiAgICAgIHdpbmRvdy5zY3JvbGxUbyh7IGxlZnQsIHRvcCwgYmVoYXZpb3IgfSk7CiAgICB9IGVsc2UgewogICAgICBsZXQgZHggPSB0eXBlb2YgcC5kZWx0YVggPT09ICJudW1iZXIiICYmIE51bWJlci5pc0Zpbml0ZShwLmRlbHRhWCkgPyBwLmRlbHRhWCA6IDA7CiAgICAgIGxldCBkeSA9IHR5cGVvZiBwLmRlbHRhWSA9PT0gIm51bWJlciIgJiYgTnVtYmVyLmlzRmluaXRlKHAuZGVsdGFZKSA/IHAuZGVsdGFZIDogMDsKICAgICAgaWYgKGRpcikgewogICAgICAgIGxldCBhbXQgPSB0eXBlb2YgcC5hbW91bnQgPT09ICJudW1iZXIiICYmIE51bWJlci5pc0Zpbml0ZShwLmFtb3VudCkgPyBNYXRoLmFicyhwLmFtb3VudCkgOiBOYU47CiAgICAgICAgaWYgKCFOdW1iZXIuaXNGaW5pdGUoYW10KSB8fCBhbXQgPT09IDApIHsKICAgICAgICAgIGlmIChkaXIgPT09ICJ1cCIgfHwgZGlyID09PSAiZG93biIpIHsKICAgICAgICAgICAgY29uc3QgZnJvbURlbHRhID0gdHlwZW9mIHAuZGVsdGFZID09PSAibnVtYmVyIiAmJiBOdW1iZXIuaXNGaW5pdGUocC5kZWx0YVkpICYmIHAuZGVsdGFZICE9PSAwOwogICAgICAgICAgICBhbXQgPSBmcm9tRGVsdGEgPyBNYXRoLmFicyhwLmRlbHRhWSkgOiBNYXRoLm1heCgyMDAsIE1hdGguZmxvb3Iod2luZG93LmlubmVySGVpZ2h0ICogMC44NSkpOwogICAgICAgICAgfSBlbHNlIHsKICAgICAgICAgICAgY29uc3QgZnJvbURlbHRhID0gdHlwZW9mIHAuZGVsdGFYID09PSAibnVtYmVyIiAmJiBOdW1iZXIuaXNGaW5pdGUocC5kZWx0YVgpICYmIHAuZGVsdGFYICE9PSAwOwogICAgICAgICAgICBhbXQgPSBmcm9tRGVsdGEgPyBNYXRoLmFicyhwLmRlbHRhWCkgOiBNYXRoLm1heCgyMDAsIE1hdGguZmxvb3Iod2luZG93LmlubmVyV2lkdGggKiAwLjg1KSk7CiAgICAgICAgICB9CiAgICAgICAgfQogICAgICAgIGR4ID0gZGlyID09PSAibGVmdCIgPyAtYW10IDogZGlyID09PSAicmlnaHQiID8gYW10IDogMDsKICAgICAgICBkeSA9IGRpciA9PT0gInVwIiA/IC1hbXQgOiBkaXIgPT09ICJkb3duIiA/IGFtdCA6IDA7CiAgICAgIH0KICAgICAgd2luZG93LnNjcm9sbEJ5KHsgbGVmdDogZHgsIHRvcDogZHksIGJlaGF2aW9yIH0pOwogICAgfQogICAgc2VuZFJlc3BvbnNlKHsgc3VjY2VzczogdHJ1ZSwgc2Nyb2xsWDogd2luZG93LnNjcm9sbFgsIHNjcm9sbFk6IHdpbmRvdy5zY3JvbGxZIH0pOwogIH0gY2F0Y2ggKGVycikgewogICAgc2VuZFJlc3BvbnNlKHsgc3VjY2VzczogZmFsc2UsIHNjcm9sbFg6IHdpbmRvdy5zY3JvbGxYLCBzY3JvbGxZOiB3aW5kb3cuc2Nyb2xsWSwgZXJyb3I6IFN0cmluZyhlcnIpIH0pOwogIH0KfQoKLyoqCiAqIEBwYXJhbSB7dW5rbm93bn0gbWVzc2FnZQogKiBAcGFyYW0geyhyOiB1bmtub3duKSA9PiB2b2lkfSBzZW5kUmVzcG9uc2UKICovCmZ1bmN0aW9uIGhhbmRsZUV2YWwobWVzc2FnZSwgc2VuZFJlc3BvbnNlKSB7CiAgY29uc3QgbSA9IC8qKiBAdHlwZSB7eyByZXF1ZXN0SWQ/OiBzdHJpbmc7IGNvZGU/OiBzdHJpbmc7IHRpbWVvdXRNcz86IG51bWJlciB9fSAqLyAobWVzc2FnZSk7CiAgY29uc3QgcmVxdWVzdElkID0gbS5yZXF1ZXN0SWQgfHwgYHBva2UtJHtEYXRlLm5vdygpfS0ke01hdGgucmFuZG9tKCkudG9TdHJpbmcoMTYpLnNsaWNlKDIpfWA7CiAgY29uc3QgY29kZSA9IFN0cmluZyhtLmNvZGUgPz8gIiIpOwogIGxldCBmaW5pc2hlZCA9IGZhbHNlOwogIGNvbnN0IHRpbWVvdXRNcyA9IHR5cGVvZiBtLnRpbWVvdXRNcyA9PT0gIm51bWJlciIgPyBtLnRpbWVvdXRNcyA6IDMwMDAwOwoKICBjb25zdCB0aW1lciA9IHNldFRpbWVvdXQoKCkgPT4gewogICAgaWYgKGZpbmlzaGVkKSByZXR1cm47CiAgICBmaW5pc2hlZCA9IHRydWU7CiAgICB3aW5kb3cucmVtb3ZlRXZlbnRMaXN0ZW5lcigibWVzc2FnZSIsIG9uV2luZG93TWVzc2FnZSk7CiAgICBzZW5kUmVzcG9uc2UoeyBvazogZmFsc2UsIGVycm9yOiAiZXZhbHVhdGVfanMgdGltZWQgb3V0IGluIGNvbnRlbnQgc2NyaXB0IiB9KTsKICB9LCB0aW1lb3V0TXMpOwoKICAvKioKICAgKiBAcGFyYW0ge01lc3NhZ2VFdmVudH0gZXZlbnQKICAgKi8KICBmdW5jdGlvbiBvbldpbmRvd01lc3NhZ2UoZXZlbnQpIHsKICAgIGlmIChldmVudC5zb3VyY2UgIT09IHdpbmRvdykgcmV0dXJuOwogICAgY29uc3QgZGF0YSA9IGV2ZW50LmRhdGE7CiAgICBpZiAoIWRhdGEgfHwgZGF0YS50eXBlICE9PSAiUE9LRV9FVkFMX1JFU1VMVCIgfHwgZGF0YS5yZXF1ZXN0SWQgIT09IHJlcXVlc3RJZCkgcmV0dXJuOwogICAgaWYgKGZpbmlzaGVkKSByZXR1cm47CiAgICBmaW5pc2hlZCA9IHRydWU7CiAgICBjbGVhclRpbWVvdXQodGltZXIpOwogICAgd2luZG93LnJlbW92ZUV2ZW50TGlzdGVuZXIoIm1lc3NhZ2UiLCBvbldpbmRvd01lc3NhZ2UpOwogICAgaWYgKGRhdGEub2spIHsKICAgICAgc2VuZFJlc3BvbnNlKHsgb2s6IHRydWUsIHJlc3VsdDogZGF0YS5yZXN1bHQgfSk7CiAgICB9IGVsc2UgewogICAgICBzZW5kUmVzcG9uc2UoeyBvazogZmFsc2UsIGVycm9yOiBkYXRhLmVycm9yIHx8ICJldmFsdWF0ZSBmYWlsZWQiIH0pOwogICAgfQogIH0KCiAgd2luZG93LmFkZEV2ZW50TGlzdGVuZXIoIm1lc3NhZ2UiLCBvbldpbmRvd01lc3NhZ2UpOwoKICBjb25zdCBzID0gZG9jdW1lbnQuY3JlYXRlRWxlbWVudCgic2NyaXB0Iik7CiAgcy50ZXh0Q29udGVudCA9IGAKICAgICAgKGZ1bmN0aW9uICgpIHsKICAgICAgICB2YXIgcmVxdWVzdElkID0gJHtKU09OLnN0cmluZ2lmeShyZXF1ZXN0SWQpfTsKICAgICAgICB0cnkgewogICAgICAgICAgdmFyIHJlc3VsdCA9ICgwLCBldmFsKSgke0pTT04uc3RyaW5naWZ5KGNvZGUpfSk7CiAgICAgICAgICB3aW5kb3cucG9zdE1lc3NhZ2UoeyB0eXBlOiAiUE9LRV9FVkFMX1JFU1VMVCIsIHJlcXVlc3RJZDogcmVxdWVzdElkLCBvazogdHJ1ZSwgcmVzdWx0OiByZXN1bHQgfSwgIioiKTsKICAgICAgICB9IGNhdGNoIChlKSB7CiAgICAgICAgICB3aW5kb3cucG9zdE1lc3NhZ2UoeyB0eXBlOiAiUE9LRV9FVkFMX1JFU1VMVCIsIHJlcXVlc3RJZDogcmVxdWVzdElkLCBvazogZmFsc2UsIGVycm9yOiBTdHJpbmcoZSkgfSwgIioiKTsKICAgICAgICB9CiAgICAgIH0pKCk7CiAgICBgOwogIChkb2N1bWVudC5kb2N1bWVudEVsZW1lbnQgfHwgZG9jdW1lbnQuaGVhZCB8fCBkb2N1bWVudC5ib2R5KS5hcHBlbmRDaGlsZChzKTsKICBzLnJlbW92ZSgpOwp9CgovLyAtLS0gUGVyY2VwdGlvbjogc2hhcmVkIGhlbHBlcnMgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQoKLyoqCiAqIEBwYXJhbSB7UmVjb3JkPHN0cmluZywgdW5rbm93bj59IG9iagogKi8KZnVuY3Rpb24gY29tcGFjdEpzb24ob2JqKSB7CiAgcmV0dXJuIEpTT04ucGFyc2UoSlNPTi5zdHJpbmdpZnkob2JqKSk7Cn0KCi8qKgogKiBAcGFyYW0ge0VsZW1lbnR9IGVsCiAqLwpmdW5jdGlvbiBjc3NFc2NhcGVJZChpZCkgewogIGlmICh0eXBlb2YgQ1NTICE9PSAidW5kZWZpbmVkIiAmJiBDU1MuZXNjYXBlKSByZXR1cm4gQ1NTLmVzY2FwZShpZCk7CiAgcmV0dXJuIGlkLnJlcGxhY2UoLyhbXlx3LV0pL2csICJcXCQxIik7Cn0KCi8qKgogKiBAcGFyYW0ge0VsZW1lbnR9IGVsCiAqLwpmdW5jdGlvbiB1bmlxdWVTZWxlY3RvcihlbCkgewogIGlmICghKGVsIGluc3RhbmNlb2YgRWxlbWVudCkpIHJldHVybiAiIjsKICBpZiAoZWwuaWQgJiYgZGVlcFF1ZXJ5QWxsKGRvY3VtZW50LCBgIyR7Y3NzRXNjYXBlSWQoZWwuaWQpfWApLmxlbmd0aCA9PT0gMSkgewogICAgcmV0dXJuIGAjJHtjc3NFc2NhcGVJZChlbC5pZCl9YDsKICB9CiAgY29uc3QgcGFydHMgPSBbXTsKICBsZXQgY3VyID0gZWw7CiAgd2hpbGUgKGN1ciAmJiBjdXIubm9kZVR5cGUgPT09IE5vZGUuRUxFTUVOVF9OT0RFICYmIGN1ciAhPT0gZG9jdW1lbnQuZG9jdW1lbnRFbGVtZW50KSB7CiAgICBsZXQgcGFydCA9IGN1ci50YWdOYW1lLnRvTG93ZXJDYXNlKCk7CiAgICBpZiAoY3VyLmlkKSB7CiAgICAgIHBhcnRzLnVuc2hpZnQoYCMke2Nzc0VzY2FwZUlkKGN1ci5pZCl9YCk7CiAgICAgIGJyZWFrOwogICAgfQogICAgY29uc3QgcGFyZW50ID0gY3VyLnBhcmVudEVsZW1lbnQ7CiAgICBpZiAocGFyZW50KSB7CiAgICAgIGNvbnN0IHNpYmxpbmdzID0gQXJyYXkuZnJvbShwYXJlbnQuY2hpbGRyZW4pLmZpbHRlcigoYykgPT4gYy50YWdOYW1lID09PSBjdXIudGFnTmFtZSk7CiAgICAgIGNvbnN0IGlkeCA9IHNpYmxpbmdzLmluZGV4T2YoY3VyKSArIDE7CiAgICAgIGlmIChzaWJsaW5ncy5sZW5ndGggPiAxKSBwYXJ0ICs9IGA6bnRoLW9mLXR5cGUoJHtpZHh9KWA7CiAgICB9CiAgICBwYXJ0cy51bnNoaWZ0KHBhcnQpOwogICAgY3VyID0gLyoqIEB0eXBlIHtFbGVtZW50fSAqLyAocGFyZW50KTsKICB9CiAgcmV0dXJuIHBhcnRzLmpvaW4oIiA+ICIpOwp9CgovKioKICogQHBhcmFtIHtFbGVtZW50fSBlbAogKi8KZnVuY3Rpb24gZWxlbWVudEludGVyYWN0aXZlKGVsKSB7CiAgaWYgKCEoZWwgaW5zdGFuY2VvZiBFbGVtZW50KSkgcmV0dXJuIGZhbHNlOwogIGNvbnN0IHRhZyA9IGVsLnRhZ05hbWUudG9Mb3dlckNhc2UoKTsKICBpZiAoWyJhIiwgImJ1dHRvbiIsICJpbnB1dCIsICJzZWxlY3QiLCAidGV4dGFyZWEiLCAic3VtbWFyeSIsICJvcHRpb24iLCAibGFiZWwiXS5pbmNsdWRlcyh0YWcpKSB7CiAgICByZXR1cm4gdHJ1ZTsKICB9CiAgY29uc3Qgcm9sZSA9IGVsLmdldEF0dHJpYnV0ZSgicm9sZSIpOwogIGlmICgKICAgIHJvbGUgJiYKICAgIFsiYnV0dG9uIiwgImxpbmsiLCAibWVudWl0ZW0iLCAidGFiIiwgImNoZWNrYm94IiwgInJhZGlvIiwgInN3aXRjaCIsICJ0ZXh0Ym94IiwgInNlYXJjaGJveCIsICJjb21ib2JveCIsICJzbGlkZXIiLCAic3BpbmJ1dHRvbiJdLmluY2x1ZGVzKAogICAgICByb2xlCiAgICApCiAgKSB7CiAgICByZXR1cm4gdHJ1ZTsKICB9CiAgaWYgKGVsLmhhc0F0dHJpYnV0ZSgib25jbGljayIpKSByZXR1cm4gdHJ1ZTsKICBpZiAoZWwgaW5zdGFuY2VvZiBIVE1MRWxlbWVudCAmJiBlbC5pc0NvbnRlbnRFZGl0YWJsZSkgcmV0dXJuIHRydWU7CiAgY29uc3QgdGFiID0gZWwuZ2V0QXR0cmlidXRlKCJ0YWJpbmRleCIpOwogIGlmICh0YWIgIT09IG51bGwgJiYgdGFiICE9PSAiLTEiICYmICFOdW1iZXIuaXNOYU4oTnVtYmVyLnBhcnNlSW50KHRhYiwgMTApKSkgcmV0dXJuIHRydWU7CiAgcmV0dXJuIGZhbHNlOwp9CgovKioKICogQHBhcmFtIHtFbGVtZW50fSBlbAogKiBAcGFyYW0ge2Jvb2xlYW59IGluY2x1ZGVIaWRkZW4KICovCmZ1bmN0aW9uIGlzU2tpcHBlZEhpZGRlbihlbCwgaW5jbHVkZUhpZGRlbikgewogIGlmIChpbmNsdWRlSGlkZGVuKSByZXR1cm4gZmFsc2U7CiAgaWYgKCEoZWwgaW5zdGFuY2VvZiBIVE1MRWxlbWVudCkpIHJldHVybiB0cnVlOwogIGlmIChlbCA9PT0gZG9jdW1lbnQuYm9keSB8fCBlbCA9PT0gZG9jdW1lbnQuZG9jdW1lbnRFbGVtZW50KSByZXR1cm4gZmFsc2U7CiAgY29uc3Qgc3QgPSB3aW5kb3cuZ2V0Q29tcHV0ZWRTdHlsZShlbCk7CiAgaWYgKHN0LmRpc3BsYXkgPT09ICJub25lIiB8fCBzdC52aXNpYmlsaXR5ID09PSAiaGlkZGVuIikgcmV0dXJuIHRydWU7CiAgaWYgKGVsLm9mZnNldFBhcmVudCA9PT0gbnVsbCkgewogICAgY29uc3QgcG9zID0gc3QucG9zaXRpb247CiAgICBpZiAocG9zICE9PSAiZml4ZWQiICYmIHBvcyAhPT0gInN0aWNreSIpIHJldHVybiB0cnVlOwogIH0KICByZXR1cm4gZmFsc2U7Cn0KCi8qKgogKiBAcGFyYW0ge0VsZW1lbnR9IGVsCiAqIEBwYXJhbSB7bnVtYmVyfSBtYXhMZW4KICovCmZ1bmN0aW9uIHRyaW1UZXh0KGVsLCBtYXhMZW4pIHsKICBsZXQgdCA9IChlbC50ZXh0Q29udGVudCB8fCAiIikudHJpbSgpLnJlcGxhY2UoL1xzKy9nLCAiICIpOwogIGlmICh0Lmxlbmd0aCA+IG1heExlbikgdCA9IHQuc2xpY2UoMCwgbWF4TGVuKTsKICByZXR1cm4gdDsKfQoKLyoqCiAqIEBwYXJhbSB7RWxlbWVudH0gZWwKICogQHBhcmFtIHtudW1iZXJ9IGRlcHRoCiAqIEBwYXJhbSB7bnVtYmVyfSBtYXhEZXB0aAogKiBAcGFyYW0ge2Jvb2xlYW59IGluY2x1ZGVIaWRkZW4KICogQHBhcmFtIHtib29sZWFufSBbaW5TaGFkb3ddCiAqLwpmdW5jdGlvbiBidWlsZERvbVNuYXBzaG90Tm9kZShlbCwgZGVwdGgsIG1heERlcHRoLCBpbmNsdWRlSGlkZGVuLCBpblNoYWRvdykgewogIGlmIChkZXB0aCA+IG1heERlcHRoKSByZXR1cm4gbnVsbDsKICBpZiAoaXNTa2lwcGVkSGlkZGVuKGVsLCBpbmNsdWRlSGlkZGVuKSkgcmV0dXJuIG51bGw7CiAgY29uc3QgciA9IGVsLmdldEJvdW5kaW5nQ2xpZW50UmVjdCgpOwogIC8qKiBAdHlwZSB7UmVjb3JkPHN0cmluZywgdW5rbm93bj59ICovCiAgY29uc3Qgbm9kZSA9IHsKICAgIHRhZzogZWwudGFnTmFtZS50b0xvd2VyQ2FzZSgpLAogICAgcmVjdDogeyB4OiByLngsIHk6IHIueSwgd2lkdGg6IHIud2lkdGgsIGhlaWdodDogci5oZWlnaHQgfSwKICAgIGludGVyYWN0aXZlOiBlbGVtZW50SW50ZXJhY3RpdmUoZWwpLAogIH07CiAgaWYgKGluU2hhZG93KSBub2RlLmlzU2hhZG93ID0gdHJ1ZTsKICBpZiAoZWwuaWQpIG5vZGUuaWQgPSBlbC5pZDsKICBjb25zdCBjbHMgPQogICAgdHlwZW9mIGVsLmNsYXNzTmFtZSA9PT0gInN0cmluZyIgJiYgZWwuY2xhc3NOYW1lLnRyaW0oKQogICAgICA/IGVsLmNsYXNzTmFtZS50cmltKCkuc3BsaXQoL1xzKy8pLmZpbHRlcihCb29sZWFuKQogICAgICA6IFtdOwogIGlmIChjbHMubGVuZ3RoKSBub2RlLmNsYXNzZXMgPSBjbHM7CiAgY29uc3Qgcm9sZSA9IGVsLmdldEF0dHJpYnV0ZSgicm9sZSIpOwogIGlmIChyb2xlKSBub2RlLnJvbGUgPSByb2xlOwogIGNvbnN0IGFsID0gZWwuZ2V0QXR0cmlidXRlKCJhcmlhLWxhYmVsIik7CiAgaWYgKGFsKSBub2RlWyJhcmlhLWxhYmVsIl0gPSBhbDsKICBjb25zdCB0eCA9IHRyaW1UZXh0KGVsLCAxMjApOwogIGlmICh0eCkgbm9kZS50ZXh0ID0gdHg7CiAgY29uc3QgY2hpbGRFbHMgPSBBcnJheS5mcm9tKGVsLmNoaWxkcmVuKTsKICBjb25zdCBjaGlsZHJlbiA9IFtdOwogIGZvciAoY29uc3QgYyBvZiBjaGlsZEVscykgewogICAgY29uc3Qgc24gPSBidWlsZERvbVNuYXBzaG90Tm9kZShjLCBkZXB0aCArIDEsIG1heERlcHRoLCBpbmNsdWRlSGlkZGVuLCBpblNoYWRvdyk7CiAgICBpZiAoc24pIGNoaWxkcmVuLnB1c2goc24pOwogIH0KICBpZiAoZWwuc2hhZG93Um9vdCkgewogICAgZm9yIChjb25zdCBjIG9mIEFycmF5LmZyb20oZWwuc2hhZG93Um9vdC5jaGlsZHJlbikpIHsKICAgICAgY29uc3Qgc24gPSBidWlsZERvbVNuYXBzaG90Tm9kZShjLCBkZXB0aCArIDEsIG1heERlcHRoLCBpbmNsdWRlSGlkZGVuLCB0cnVlKTsKICAgICAgaWYgKHNuKSBjaGlsZHJlbi5wdXNoKHNuKTsKICAgIH0KICB9CiAgaWYgKGNoaWxkcmVuLmxlbmd0aCkgbm9kZS5jaGlsZHJlbiA9IGNoaWxkcmVuOwogIHJldHVybiBub2RlOwp9CgovKioKICogQHBhcmFtIHt1bmtub3dufSBtZXNzYWdlCiAqIEBwYXJhbSB7KHI6IHVua25vd24pID0+IHZvaWR9IHNlbmRSZXNwb25zZQogKi8KZnVuY3Rpb24gaGFuZGxlR2V0RG9tU25hcHNob3QobWVzc2FnZSwgc2VuZFJlc3BvbnNlKSB7CiAgY29uc3QgbSA9IC8qKiBAdHlwZSB7eyBpbmNsdWRlSGlkZGVuPzogYm9vbGVhbjsgbWF4RGVwdGg/OiBudW1iZXIgfX0gKi8gKG1lc3NhZ2UpOwogIGNvbnN0IGluY2x1ZGVIaWRkZW4gPSBtLmluY2x1ZGVIaWRkZW4gPT09IHRydWU7CiAgY29uc3QgbWF4RGVwdGggPSB0eXBlb2YgbS5tYXhEZXB0aCA9PT0gIm51bWJlciIgJiYgTnVtYmVyLmlzRmluaXRlKG0ubWF4RGVwdGgpID8gTWF0aC5tYXgoMCwgTWF0aC5taW4oNTAsIG0ubWF4RGVwdGgpKSA6IDY7CiAgaWYgKCFkb2N1bWVudC5ib2R5KSB7CiAgICBzZW5kUmVzcG9uc2UoeyBlcnJvcjogIk5vIGRvY3VtZW50LmJvZHkiIH0pOwogICAgcmV0dXJuOwogIH0KICBjb25zdCBzbmFwc2hvdCA9IGJ1aWxkRG9tU25hcHNob3ROb2RlKGRvY3VtZW50LmJvZHksIDAsIG1heERlcHRoLCBpbmNsdWRlSGlkZGVuKTsKICBzZW5kUmVzcG9uc2UoCiAgICBjb21wYWN0SnNvbih7CiAgICAgIHNuYXBzaG90LAogICAgICB1cmw6IGxvY2F0aW9uLmhyZWYsCiAgICAgIHRpdGxlOiBkb2N1bWVudC50aXRsZSB8fCAiIiwKICAgICAgdGltZXN0YW1wOiBEYXRlLm5vdygpLAogICAgfSkKICApOwp9CgovKioKICogQHBhcmFtIHtFbGVtZW50fSBlbAogKi8KZnVuY3Rpb24gaW1wbGllZFJvbGUoZWwpIHsKICBjb25zdCByID0gZWwuZ2V0QXR0cmlidXRlKCJyb2xlIik7CiAgaWYgKHIpIHJldHVybiByOwogIGNvbnN0IHQgPSBlbC50YWdOYW1lLnRvTG93ZXJDYXNlKCk7CiAgaWYgKHQgPT09ICJhIikgcmV0dXJuICJsaW5rIjsKICBpZiAodCA9PT0gImJ1dHRvbiIpIHJldHVybiAiYnV0dG9uIjsKICBpZiAodCA9PT0gInNlbGVjdCIpIHJldHVybiAiY29tYm9ib3giOwogIGlmICh0ID09PSAidGV4dGFyZWEiKSByZXR1cm4gInRleHRib3giOwogIGlmICh0ID09PSAiaW1nIikgcmV0dXJuICJpbWciOwogIGlmICh0ID09PSAiZm9ybSIpIHJldHVybiAiZm9ybSI7CiAgaWYgKHQgPT09ICJpbnB1dCIpIHsKICAgIGNvbnN0IHR5cGUgPSAoLyoqIEB0eXBlIHtIVE1MSW5wdXRFbGVtZW50fSAqLyAoZWwpKS50eXBlIHx8ICJ0ZXh0IjsKICAgIGlmICh0eXBlID09PSAiY2hlY2tib3giKSByZXR1cm4gImNoZWNrYm94IjsKICAgIGlmICh0eXBlID09PSAicmFkaW8iKSByZXR1cm4gInJhZGlvIjsKICAgIGlmICh0eXBlID09PSAiYnV0dG9uIiB8fCB0eXBlID09PSAic3VibWl0IiB8fCB0eXBlID09PSAicmVzZXQiKSByZXR1cm4gImJ1dHRvbiI7CiAgICByZXR1cm4gInRleHRib3giOwogIH0KICBpZiAoL15oWzEtNl0kLy50ZXN0KHQpKSByZXR1cm4gImhlYWRpbmciOwogIGlmICh0ID09PSAicCIpIHJldHVybiAicGFyYWdyYXBoIjsKICBpZiAodCA9PT0gImxpIikgcmV0dXJuICJsaXN0aXRlbSI7CiAgcmV0dXJuIHQ7Cn0KCi8qKgogKiBAcGFyYW0ge0VsZW1lbnR9IGVsCiAqLwpmdW5jdGlvbiBhY2Nlc3NpYmlsaXR5TmFtZShlbCkgewogIGNvbnN0IGFyaWEgPSBlbC5nZXRBdHRyaWJ1dGUoImFyaWEtbGFiZWwiKTsKICBpZiAoYXJpYSAmJiBhcmlhLnRyaW0oKSkgcmV0dXJuIGFyaWEudHJpbSgpLnNsaWNlKDAsIDgwKTsKICBpZiAoZWwgaW5zdGFuY2VvZiBIVE1MSW1hZ2VFbGVtZW50ICYmIGVsLmFsdCkgcmV0dXJuIGVsLmFsdC50cmltKCkuc2xpY2UoMCwgODApOwogIGNvbnN0IHRpdGxlID0gZWwuZ2V0QXR0cmlidXRlKCJ0aXRsZSIpOwogIGlmICh0aXRsZSAmJiB0aXRsZS50cmltKCkpIHJldHVybiB0aXRsZS50cmltKCkuc2xpY2UoMCwgODApOwogIGNvbnN0IHBoID0gZWwuZ2V0QXR0cmlidXRlKCJhcmlhLXBsYWNlaG9sZGVyIik7CiAgaWYgKHBoICYmIHBoLnRyaW0oKSkgcmV0dXJuIHBoLnRyaW0oKS5zbGljZSgwLCA4MCk7CiAgY29uc3QgaXQgPSAoZWwuaW5uZXJUZXh0IHx8ICIiKS50cmltKCkucmVwbGFjZSgvXHMrL2csICIgIik7CiAgcmV0dXJuIGl0Lmxlbmd0aCA+IDgwID8gaXQuc2xpY2UoMCwgODApIDogaXQ7Cn0KCi8qKgogKiBAcGFyYW0ge0VsZW1lbnR9IGVsCiAqLwpmdW5jdGlvbiBpc0ZvY3VzYWJsZUludGVyYWN0aXZlKGVsKSB7CiAgaWYgKCEoZWwgaW5zdGFuY2VvZiBIVE1MRWxlbWVudCkpIHJldHVybiBmYWxzZTsKICBpZiAoZWwuaGFzQXR0cmlidXRlKCJkaXNhYmxlZCIpKSByZXR1cm4gZmFsc2U7CiAgaWYgKGVsZW1lbnRJbnRlcmFjdGl2ZShlbCkpIHsKICAgIGNvbnN0IHRhYiA9IGVsLmdldEF0dHJpYnV0ZSgidGFiaW5kZXgiKTsKICAgIGlmICh0YWIgPT09ICItMSIgJiYgIVsiQSIsICJCVVRUT04iLCAiSU5QVVQiLCAiU0VMRUNUIiwgIlRFWFRBUkVBIiwgIlNVTU1BUlkiXS5pbmNsdWRlcyhlbC50YWdOYW1lKSkgewogICAgICByZXR1cm4gZmFsc2U7CiAgICB9CiAgICByZXR1cm4gdHJ1ZTsKICB9CiAgcmV0dXJuIGZhbHNlOwp9CgovKioKICogQHBhcmFtIHt1bmtub3dufSBtZXNzYWdlCiAqIEBwYXJhbSB7KHI6IHVua25vd24pID0+IHZvaWR9IHNlbmRSZXNwb25zZQogKi8KZnVuY3Rpb24gaGFuZGxlR2V0QWNjZXNzaWJpbGl0eVRyZWUobWVzc2FnZSwgc2VuZFJlc3BvbnNlKSB7CiAgY29uc3QgbSA9IC8qKiBAdHlwZSB7eyBpbnRlcmFjdGl2ZU9ubHk/OiBib29sZWFuIH19ICovIChtZXNzYWdlKTsKICBjb25zdCBpbnRlcmFjdGl2ZU9ubHkgPSBtLmludGVyYWN0aXZlT25seSA9PT0gdHJ1ZTsKICBjb25zdCBzZWwgPQogICAgJ1tyb2xlXSwgYSwgYnV0dG9uLCBpbnB1dCwgc2VsZWN0LCB0ZXh0YXJlYSwgaDEsIGgyLCBoMywgaDQsIGg1LCBoNiwgcCwgbGksIGltZywgZm9ybSc7CiAgY29uc3QgbGlzdCA9IEFycmF5LmZyb20oZG9jdW1lbnQucXVlcnlTZWxlY3RvckFsbChzZWwpKTsKICAvKiogQHR5cGUge0FycmF5PFJlY29yZDxzdHJpbmcsIHVua25vd24+Pn0gKi8KICBjb25zdCByYXcgPSBbXTsKICBmb3IgKGNvbnN0IGVsIG9mIGxpc3QpIHsKICAgIGlmICghKGVsIGluc3RhbmNlb2YgRWxlbWVudCkpIGNvbnRpbnVlOwogICAgaWYgKGlzU2tpcHBlZEhpZGRlbihlbCwgZmFsc2UpKSBjb250aW51ZTsKICAgIGlmIChpbnRlcmFjdGl2ZU9ubHkgJiYgIWlzRm9jdXNhYmxlSW50ZXJhY3RpdmUoZWwpKSBjb250aW51ZTsKICAgIGNvbnN0IHIgPSBlbC5nZXRCb3VuZGluZ0NsaWVudFJlY3QoKTsKICAgIGNvbnN0IHRhZyA9IGVsLnRhZ05hbWUudG9Mb3dlckNhc2UoKTsKICAgIC8qKiBAdHlwZSB7UmVjb3JkPHN0cmluZywgdW5rbm93bj59ICovCiAgICBjb25zdCByb3cgPSB7CiAgICAgIHJvbGU6IGltcGxpZWRSb2xlKGVsKSwKICAgICAgbmFtZTogYWNjZXNzaWJpbGl0eU5hbWUoZWwpLAogICAgICBzZWxlY3RvcjogdW5pcXVlU2VsZWN0b3IoZWwpLAogICAgICBkaXNhYmxlZDogZWwgaW5zdGFuY2VvZiBIVE1MRWxlbWVudCAmJiAoZWwuaGFzQXR0cmlidXRlKCJkaXNhYmxlZCIpIHx8IC8qKiBAdHlwZSB7SFRNTElucHV0RWxlbWVudH0gKi8gKGVsKS5kaXNhYmxlZCA9PT0gdHJ1ZSksCiAgICAgIHJlY3Q6IHsgeDogci54LCB5OiByLnksIHc6IHIud2lkdGgsIGg6IHIuaGVpZ2h0IH0sCiAgICB9OwogICAgaWYgKGVsLmlkKSByb3cuaWQgPSBlbC5pZDsKICAgIGlmICgvXmhbMS02XSQvLnRlc3QodGFnKSkgcm93LmxldmVsID0gTnVtYmVyKHRhZ1sxXSk7CiAgICBpZiAoZWwgaW5zdGFuY2VvZiBIVE1MSW5wdXRFbGVtZW50IHx8IGVsIGluc3RhbmNlb2YgSFRNTFRleHRBcmVhRWxlbWVudCB8fCBlbCBpbnN0YW5jZW9mIEhUTUxTZWxlY3RFbGVtZW50KSB7CiAgICAgIHJvdy52YWx1ZSA9IGVsLnZhbHVlOwogICAgICBpZiAoZWwgaW5zdGFuY2VvZiBIVE1MSW5wdXRFbGVtZW50ICYmIChlbC50eXBlID09PSAiY2hlY2tib3giIHx8IGVsLnR5cGUgPT09ICJyYWRpbyIpKSB7CiAgICAgICAgcm93LmNoZWNrZWQgPSBlbC5jaGVja2VkOwogICAgICB9CiAgICB9CiAgICByYXcucHVzaChyb3cpOwogIH0KICByYXcuc29ydCgoYSwgYikgPT4gewogICAgY29uc3QgcmEgPSAvKiogQHR5cGUge3sgeDogbnVtYmVyOyB5OiBudW1iZXIgfX0gKi8gKGEucmVjdCk7CiAgICBjb25zdCByYiA9IC8qKiBAdHlwZSB7eyB4OiBudW1iZXI7IHk6IG51bWJlciB9fSAqLyAoYi5yZWN0KTsKICAgIGlmIChNYXRoLmFicyhyYS55IC0gcmIueSkgPiAxKSByZXR1cm4gcmEueSAtIHJiLnk7CiAgICByZXR1cm4gcmEueCAtIHJiLng7CiAgfSk7CiAgY29uc3Qgbm9kZXMgPSByYXcubWFwKChyb3cpID0+IGNvbXBhY3RKc29uKHsgLi4ucm93IH0pKTsKICBzZW5kUmVzcG9uc2UoewogICAgbm9kZXMsCiAgICBjb3VudDogbm9kZXMubGVuZ3RoLAogICAgdXJsOiBsb2NhdGlvbi5ocmVmLAogIH0pOwp9CgovKioKICogQHBhcmFtIHtzdHJpbmd9IGV4cHIKICogQHJldHVybnMge0VsZW1lbnRbXX0KICovCmZ1bmN0aW9uIHhwYXRoRWxlbWVudHMoZXhwcikgewogIHJldHVybiBkZWVwWFBhdGhBbGwoZXhwcik7Cn0KCi8qKgogKiBAcGFyYW0ge3N0cmluZ30gcQogKiBAcmV0dXJucyB7RWxlbWVudFtdfQogKi8KZnVuY3Rpb24gZmluZEVsZW1lbnRzQnlUZXh0KHEpIHsKICBjb25zdCBxbCA9IHEudG9Mb3dlckNhc2UoKS50cmltKCk7CiAgaWYgKCFxbCkgcmV0dXJuIFtdOwogIGNvbnN0IGFsbCA9IGRlZXBRdWVyeUFsbChkb2N1bWVudCwgIioiKTsKICAvKiogQHR5cGUge0VsZW1lbnRbXX0gKi8KICBjb25zdCBleGFjdCA9IFtdOwogIC8qKiBAdHlwZSB7RWxlbWVudFtdfSAqLwogIGNvbnN0IHBhcnRpYWwgPSBbXTsKICBmb3IgKGNvbnN0IGVsIG9mIGFsbCkgewogICAgaWYgKCEoZWwgaW5zdGFuY2VvZiBIVE1MRWxlbWVudCkpIGNvbnRpbnVlOwogICAgY29uc3QgdG4gPSBlbC50YWdOYW1lOwogICAgaWYgKHRuID09PSAiU0NSSVBUIiB8fCB0biA9PT0gIlNUWUxFIiB8fCB0biA9PT0gIk5PU0NSSVBUIikgY29udGludWU7CiAgICBjb25zdCB0ID0gKGVsLmlubmVyVGV4dCB8fCAiIikudHJpbSgpOwogICAgaWYgKCF0KSBjb250aW51ZTsKICAgIGNvbnN0IHRsID0gdC50b0xvd2VyQ2FzZSgpOwogICAgaWYgKHRsID09PSBxbCkgZXhhY3QucHVzaChlbCk7CiAgICBlbHNlIGlmICh0bC5pbmNsdWRlcyhxbCkpIHBhcnRpYWwucHVzaChlbCk7CiAgfQogIGNvbnN0IHBvb2wgPSBleGFjdC5sZW5ndGggPyBleGFjdCA6IHBhcnRpYWw7CiAgcmV0dXJuIGZpbHRlck91dEFuY2VzdG9ycyhwb29sKTsKfQoKLyoqCiAqIEBwYXJhbSB7RWxlbWVudFtdfSBlbHMKICovCmZ1bmN0aW9uIGZpbHRlck91dEFuY2VzdG9ycyhlbHMpIHsKICAvKiogQHR5cGUge0VsZW1lbnRbXX0gKi8KICBjb25zdCBvdXQgPSBbXTsKICBmb3IgKGNvbnN0IGVsIG9mIGVscykgewogICAgbGV0IHN1YiA9IGZhbHNlOwogICAgZm9yIChjb25zdCBvIG9mIGVscykgewogICAgICBpZiAobyAhPT0gZWwgJiYgby5jb250YWlucyhlbCkpIHsKICAgICAgICBzdWIgPSB0cnVlOwogICAgICAgIGJyZWFrOwogICAgICB9CiAgICB9CiAgICBpZiAoIXN1Yikgb3V0LnB1c2goZWwpOwogIH0KICByZXR1cm4gb3V0Owp9CgovKioKICogQHBhcmFtIHtzdHJpbmd9IHEKICogQHJldHVybnMge0VsZW1lbnRbXX0KICovCmZ1bmN0aW9uIGZpbmRFbGVtZW50c0J5QXJpYShxKSB7CiAgY29uc3QgcWwgPSBxLnRvTG93ZXJDYXNlKCkudHJpbSgpOwogIGlmICghcWwpIHJldHVybiBbXTsKICBjb25zdCBhbGwgPSBkZWVwUXVlcnlBbGwoZG9jdW1lbnQsICIqIik7CiAgLyoqIEB0eXBlIHtFbGVtZW50W119ICovCiAgY29uc3QgaGl0cyA9IFtdOwogIGZvciAoY29uc3QgZWwgb2YgYWxsKSB7CiAgICBpZiAoIShlbCBpbnN0YW5jZW9mIEVsZW1lbnQpKSBjb250aW51ZTsKICAgIGNvbnN0IHRuID0gZWwudGFnTmFtZTsKICAgIGlmICh0biA9PT0gIlNDUklQVCIgfHwgdG4gPT09ICJTVFlMRSIgfHwgdG4gPT09ICJOT1NDUklQVCIpIGNvbnRpbnVlOwogICAgY29uc3QgY2h1bmtzID0gWwogICAgICBlbC5nZXRBdHRyaWJ1dGUoImFyaWEtbGFiZWwiKSwKICAgICAgZWwuZ2V0QXR0cmlidXRlKCJhcmlhLXBsYWNlaG9sZGVyIiksCiAgICAgIGVsLmdldEF0dHJpYnV0ZSgidGl0bGUiKSwKICAgICAgZWwgaW5zdGFuY2VvZiBIVE1MSW1hZ2VFbGVtZW50ID8gZWwuYWx0IDogbnVsbCwKICAgIF0KICAgICAgLmZpbHRlcihCb29sZWFuKQogICAgICAubWFwKChzKSA9PiBTdHJpbmcocykudG9Mb3dlckNhc2UoKSk7CiAgICBpZiAoY2h1bmtzLnNvbWUoKGMpID0+IGMuaW5jbHVkZXMocWwpKSkgaGl0cy5wdXNoKGVsKTsKICB9CiAgcmV0dXJuIGZpbHRlck91dEFuY2VzdG9ycyhoaXRzKTsKfQoKLyoqCiAqIEBwYXJhbSB7RWxlbWVudH0gZWwKICogQHBhcmFtIHtudW1iZXJ9IGluZGV4CiAqLwpmdW5jdGlvbiB0b0ZvdW5kRWxlbWVudChlbCwgaW5kZXgpIHsKICBjb25zdCByID0gZWwuZ2V0Qm91bmRpbmdDbGllbnRSZWN0KCk7CiAgY29uc3QgY2xzID0KICAgIHR5cGVvZiBlbC5jbGFzc05hbWUgPT09ICJzdHJpbmciICYmIGVsLmNsYXNzTmFtZS50cmltKCkKICAgICAgPyBlbC5jbGFzc05hbWUudHJpbSgpLnNwbGl0KC9ccysvKS5maWx0ZXIoQm9vbGVhbikKICAgICAgOiBbXTsKICAvKiogQHR5cGUge1JlY29yZDxzdHJpbmcsIHVua25vd24+fSAqLwogIGNvbnN0IG8gPSB7CiAgICBpbmRleCwKICAgIHRhZzogZWwudGFnTmFtZS50b0xvd2VyQ2FzZSgpLAogICAgdGV4dDogKGVsLmlubmVyVGV4dCB8fCAiIikudHJpbSgpLnNsaWNlKDAsIDIwMCksCiAgICBzZWxlY3RvcjogdW5pcXVlU2VsZWN0b3IoZWwpLAogICAgcmVjdDogeyB4OiByLngsIHk6IHIueSwgd2lkdGg6IHIud2lkdGgsIGhlaWdodDogci5oZWlnaHQgfSwKICAgIGludGVyYWN0aXZlOiBlbGVtZW50SW50ZXJhY3RpdmUoZWwpLAogIH07CiAgaWYgKGVsLmlkKSBvLmlkID0gZWwuaWQ7CiAgaWYgKGNscy5sZW5ndGgpIG8uY2xhc3NlcyA9IGNsczsKICByZXR1cm4gY29tcGFjdEpzb24obyk7Cn0KCi8qKgogKiBAcGFyYW0ge3Vua25vd259IG1lc3NhZ2UKICogQHBhcmFtIHsocjogdW5rbm93bikgPT4gdm9pZH0gc2VuZFJlc3BvbnNlCiAqLwpmdW5jdGlvbiBoYW5kbGVGaW5kRWxlbWVudChtZXNzYWdlLCBzZW5kUmVzcG9uc2UpIHsKICBjb25zdCBtID0gLyoqIEB0eXBlIHt7IHF1ZXJ5Pzogc3RyaW5nOyBzdHJhdGVneT86IHN0cmluZyB9fSAqLyAobWVzc2FnZSk7CiAgY29uc3QgcXVlcnkgPSB0eXBlb2YgbS5xdWVyeSA9PT0gInN0cmluZyIgPyBtLnF1ZXJ5IDogIiI7CiAgY29uc3Qgc3RyYXRlZ3kgPSBtLnN0cmF0ZWd5ID09PSAiY3NzIiB8fCBtLnN0cmF0ZWd5ID09PSAidGV4dCIgfHwgbS5zdHJhdGVneSA9PT0gImFyaWEiIHx8IG0uc3RyYXRlZ3kgPT09ICJ4cGF0aCIgPyBtLnN0cmF0ZWd5IDogImF1dG8iOwogIGlmICghcXVlcnkudHJpbSgpKSB7CiAgICBzZW5kUmVzcG9uc2UoeyBlbGVtZW50czogW10sIHF1ZXJ5OiAiIiwgc3RyYXRlZ3lfdXNlZDogc3RyYXRlZ3kgfSk7CiAgICByZXR1cm47CiAgfQoKICAvKiogQHR5cGUge0VsZW1lbnRbXX0gKi8KICBsZXQgZm91bmQgPSBbXTsKICAvKiogQHR5cGUge3N0cmluZ30gKi8KICBsZXQgdXNlZCA9IHN0cmF0ZWd5OwoKICBmdW5jdGlvbiB0cnlDc3MoKSB7CiAgICB0cnkgewogICAgICByZXR1cm4gZGVlcFF1ZXJ5QWxsKGRvY3VtZW50LCBxdWVyeSk7CiAgICB9IGNhdGNoIHsKICAgICAgcmV0dXJuIFtdOwogICAgfQogIH0KCiAgaWYgKHN0cmF0ZWd5ID09PSAiYXV0byIpIHsKICAgIGZvdW5kID0gdHJ5Q3NzKCk7CiAgICB1c2VkID0gImNzcyI7CiAgICBpZiAoZm91bmQubGVuZ3RoID09PSAwKSB7CiAgICAgIGZvdW5kID0gZmluZEVsZW1lbnRzQnlUZXh0KHF1ZXJ5KTsKICAgICAgdXNlZCA9ICJ0ZXh0IjsKICAgIH0KICAgIGlmIChmb3VuZC5sZW5ndGggPT09IDApIHsKICAgICAgZm91bmQgPSBmaW5kRWxlbWVudHNCeUFyaWEocXVlcnkpOwogICAgICB1c2VkID0gImFyaWEiOwogICAgfQogIH0gZWxzZSBpZiAoc3RyYXRlZ3kgPT09ICJjc3MiKSB7CiAgICBmb3VuZCA9IHRyeUNzcygpOwogIH0gZWxzZSBpZiAoc3RyYXRlZ3kgPT09ICJ0ZXh0IikgewogICAgZm91bmQgPSBmaW5kRWxlbWVudHNCeVRleHQocXVlcnkpOwogIH0gZWxzZSBpZiAoc3RyYXRlZ3kgPT09ICJhcmlhIikgewogICAgZm91bmQgPSBmaW5kRWxlbWVudHNCeUFyaWEocXVlcnkpOwogIH0gZWxzZSBpZiAoc3RyYXRlZ3kgPT09ICJ4cGF0aCIpIHsKICAgIGZvdW5kID0geHBhdGhFbGVtZW50cyhxdWVyeSk7CiAgICB1c2VkID0gInhwYXRoIjsKICB9CgogIGNvbnN0IHRvcCA9IGZvdW5kLnNsaWNlKDAsIDUpOwogIGNvbnN0IGVsZW1lbnRzID0gdG9wLm1hcCgoZWwsIGkpID0+IHRvRm91bmRFbGVtZW50KGVsLCBpKSk7CiAgc2VuZFJlc3BvbnNlKHsgZWxlbWVudHMsIHF1ZXJ5LCBzdHJhdGVneV91c2VkOiB1c2VkIH0pOwp9CgovKioKICogQHJldHVybnMge0hUTUxFbGVtZW50fQogKi8KZnVuY3Rpb24gZ2V0UmVhZFBhZ2VSb290KCkgewogIGNvbnN0IG1haW4gPQogICAgZG9jdW1lbnQucXVlcnlTZWxlY3RvcigibWFpbiIpIHx8CiAgICBkb2N1bWVudC5xdWVyeVNlbGVjdG9yKCJhcnRpY2xlIikgfHwKICAgIGRvY3VtZW50LnF1ZXJ5U2VsZWN0b3IoJ1tyb2xlPSJtYWluIl0nKTsKICBpZiAobWFpbiBpbnN0YW5jZW9mIEhUTUxFbGVtZW50KSByZXR1cm4gbWFpbjsKICByZXR1cm4gZG9jdW1lbnQuYm9keSB8fCBkb2N1bWVudC5kb2N1bWVudEVsZW1lbnQ7Cn0KCi8qKgogKiBAcGFyYW0ge0hUTUxFbGVtZW50fSBlbAogKi8KZnVuY3Rpb24gc3RyaXBOb2lzZShlbCkgewogIGVsLnF1ZXJ5U2VsZWN0b3JBbGwoInNjcmlwdCwgc3R5bGUsIG5vc2NyaXB0LCBuYXYsIGhlYWRlciwgZm9vdGVyIikuZm9yRWFjaCgobikgPT4gbi5yZW1vdmUoKSk7Cn0KCi8qKgogKiBAcGFyYW0ge3N0cmluZ30gdGV4dAogKi8KZnVuY3Rpb24gd29yZENvdW50RnJvbSh0ZXh0KSB7CiAgY29uc3QgdyA9IHRleHQudHJpbSgpLnNwbGl0KC9ccysvKS5maWx0ZXIoQm9vbGVhbik7CiAgcmV0dXJuIHcubGVuZ3RoOwp9CgovKioKICogQHBhcmFtIHtIVE1MRWxlbWVudH0gcm9vdAogKi8KZnVuY3Rpb24gcmVhZFN0cnVjdHVyZWQocm9vdCkgewogIGNvbnN0IGNsb25lID0gLyoqIEB0eXBlIHtIVE1MRWxlbWVudH0gKi8gKHJvb3QuY2xvbmVOb2RlKHRydWUpKTsKICBzdHJpcE5vaXNlKGNsb25lKTsKICBjb25zdCBkZXNjTWV0YSA9IGRvY3VtZW50LnF1ZXJ5U2VsZWN0b3IoJ21ldGFbbmFtZT0iZGVzY3JpcHRpb24iXScpOwogIGNvbnN0IGRlc2NyaXB0aW9uID0gZGVzY01ldGE/LmdldEF0dHJpYnV0ZSgiY29udGVudCIpPy50cmltKCkgfHwgIiI7CiAgLyoqIEB0eXBlIHt7IGxldmVsOiBudW1iZXI7IHRleHQ6IHN0cmluZyB9W119ICovCiAgY29uc3QgaGVhZGluZ3MgPSBbXTsKICBjbG9uZS5xdWVyeVNlbGVjdG9yQWxsKCJoMSwgaDIsIGgzLCBoNCwgaDUsIGg2IikuZm9yRWFjaCgoaCkgPT4gewogICAgY29uc3QgdGFnID0gaC50YWdOYW1lLnRvTG93ZXJDYXNlKCk7CiAgICBoZWFkaW5ncy5wdXNoKHsgbGV2ZWw6IE51bWJlcih0YWdbMV0pLCB0ZXh0OiAoaC50ZXh0Q29udGVudCB8fCAiIikudHJpbSgpIH0pOwogIH0pOwogIC8qKiBAdHlwZSB7eyB0ZXh0OiBzdHJpbmc7IGhyZWY6IHN0cmluZyB9W119ICovCiAgY29uc3QgbGlua3MgPSBbXTsKICBjbG9uZS5xdWVyeVNlbGVjdG9yQWxsKCJhW2hyZWZdIikuZm9yRWFjaCgoYSkgPT4gewogICAgY29uc3QgaHJlZiA9IGEuZ2V0QXR0cmlidXRlKCJocmVmIikgfHwgIiI7CiAgICBsaW5rcy5wdXNoKHsgdGV4dDogKGEudGV4dENvbnRlbnQgfHwgIiIpLnRyaW0oKSwgaHJlZiB9KTsKICB9KTsKICAvKiogQHR5cGUge3sgYWx0OiBzdHJpbmc7IHNyYzogc3RyaW5nIH1bXX0gKi8KICBjb25zdCBpbWFnZXMgPSBbXTsKICBjbG9uZS5xdWVyeVNlbGVjdG9yQWxsKCJpbWdbc3JjXSIpLmZvckVhY2goKGltZykgPT4gewogICAgaW1hZ2VzLnB1c2goeyBhbHQ6IGltZy5nZXRBdHRyaWJ1dGUoImFsdCIpIHx8ICIiLCBzcmM6IGltZy5nZXRBdHRyaWJ1dGUoInNyYyIpIHx8ICIiIH0pOwogIH0pOwogIGNvbnN0IG1haW5UZXh0ID0gKGNsb25lLmlubmVyVGV4dCB8fCAiIikudHJpbSgpLnJlcGxhY2UoL1xzKy9nLCAiICIpOwogIHJldHVybiB7CiAgICB0aXRsZTogZG9jdW1lbnQudGl0bGUgfHwgIiIsCiAgICB1cmw6IGxvY2F0aW9uLmhyZWYsCiAgICBkZXNjcmlwdGlvbiwKICAgIG1haW5UZXh0LAogICAgaGVhZGluZ3MsCiAgICBsaW5rcywKICAgIGltYWdlcywKICB9Owp9CgovKioKICogQHBhcmFtIHtIVE1MRWxlbWVudH0gZWwKICogQHJldHVybnMge3N0cmluZ30KICovCmZ1bmN0aW9uIGVsZW1lbnRUb01hcmtkb3duKGVsKSB7CiAgY29uc3QgdGFnID0gZWwudGFnTmFtZS50b0xvd2VyQ2FzZSgpOwogIGlmIChbInNjcmlwdCIsICJzdHlsZSIsICJub3NjcmlwdCIsICJuYXYiLCAiaGVhZGVyIiwgImZvb3RlciJdLmluY2x1ZGVzKHRhZykpIHJldHVybiAiIjsKICBpZiAodGFnID09PSAiYnIiKSByZXR1cm4gIlxuIjsKICBpZiAoZWwuY2hpbGROb2Rlcy5sZW5ndGggPT09IDApIHJldHVybiAiIjsKCiAgLyoqIEB0eXBlIHtzdHJpbmdbXX0gKi8KICBjb25zdCBiaXRzID0gW107CiAgZm9yIChjb25zdCBub2RlIG9mIGVsLmNoaWxkTm9kZXMpIHsKICAgIGlmIChub2RlLm5vZGVUeXBlID09PSBOb2RlLlRFWFRfTk9ERSkgewogICAgICBjb25zdCB0ID0gbm9kZS50ZXh0Q29udGVudCB8fCAiIjsKICAgICAgaWYgKHQudHJpbSgpKSBiaXRzLnB1c2godCk7CiAgICB9IGVsc2UgaWYgKG5vZGUubm9kZVR5cGUgPT09IE5vZGUuRUxFTUVOVF9OT0RFKSB7CiAgICAgIGNvbnN0IGNoaWxkID0gLyoqIEB0eXBlIHtIVE1MRWxlbWVudH0gKi8gKG5vZGUpOwogICAgICBjb25zdCBjdCA9IGNoaWxkLnRhZ05hbWUudG9Mb3dlckNhc2UoKTsKICAgICAgaWYgKFsic2NyaXB0IiwgInN0eWxlIiwgIm5vc2NyaXB0IiwgIm5hdiIsICJoZWFkZXIiLCAiZm9vdGVyIl0uaW5jbHVkZXMoY3QpKSBjb250aW51ZTsKICAgICAgaWYgKC9eaFsxLTZdJC8udGVzdChjdCkpIHsKICAgICAgICBjb25zdCBsZXZlbCA9IE51bWJlcihjdFsxXSk7CiAgICAgICAgYml0cy5wdXNoKGAkeyIjIi5yZXBlYXQobGV2ZWwpfSAkeyhjaGlsZC5pbm5lclRleHQgfHwgIiIpLnRyaW0oKX1cblxuYCk7CiAgICAgIH0gZWxzZSBpZiAoY3QgPT09ICJwIikgewogICAgICAgIGJpdHMucHVzaChgJHsoY2hpbGQuaW5uZXJUZXh0IHx8ICIiKS50cmltKCl9XG5cbmApOwogICAgICB9IGVsc2UgaWYgKGN0ID09PSAiYSIgJiYgY2hpbGQuZ2V0QXR0cmlidXRlKCJocmVmIikpIHsKICAgICAgICBjb25zdCBocmVmID0gY2hpbGQuZ2V0QXR0cmlidXRlKCJocmVmIikgfHwgIiI7CiAgICAgICAgYml0cy5wdXNoKGBbJHsoY2hpbGQudGV4dENvbnRlbnQgfHwgIiIpLnRyaW0oKX1dKCR7aHJlZn0pYCk7CiAgICAgIH0gZWxzZSBpZiAoY3QgPT09ICJ1bCIpIHsKICAgICAgICBmb3IgKGNvbnN0IGxpIG9mIGNoaWxkLnF1ZXJ5U2VsZWN0b3JBbGwoIjpzY29wZSA+IGxpIikpIHsKICAgICAgICAgIGJpdHMucHVzaChgLSAkeyhsaS50ZXh0Q29udGVudCB8fCAiIikudHJpbSgpfVxuYCk7CiAgICAgICAgfQogICAgICAgIGJpdHMucHVzaCgiXG4iKTsKICAgICAgfSBlbHNlIGlmIChjdCA9PT0gIm9sIikgewogICAgICAgIGxldCBpID0gMTsKICAgICAgICBmb3IgKGNvbnN0IGxpIG9mIGNoaWxkLnF1ZXJ5U2VsZWN0b3JBbGwoIjpzY29wZSA+IGxpIikpIHsKICAgICAgICAgIGJpdHMucHVzaChgJHtpfS4gJHsobGkudGV4dENvbnRlbnQgfHwgIiIpLnRyaW0oKX1cbmApOwogICAgICAgICAgaSArPSAxOwogICAgICAgIH0KICAgICAgICBiaXRzLnB1c2goIlxuIik7CiAgICAgIH0gZWxzZSBpZiAoY3QgPT09ICJwcmUiKSB7CiAgICAgICAgYml0cy5wdXNoKGBcYFxgXGBcbiR7KGNoaWxkLnRleHRDb250ZW50IHx8ICIiKS50cmltKCl9XG5cYFxgXGBcblxuYCk7CiAgICAgIH0gZWxzZSBpZiAoY3QgPT09ICJjb2RlIiAmJiBjaGlsZC5wYXJlbnRFbGVtZW50Py50YWdOYW1lLnRvTG93ZXJDYXNlKCkgIT09ICJwcmUiKSB7CiAgICAgICAgYml0cy5wdXNoKGBcYCR7KGNoaWxkLnRleHRDb250ZW50IHx8ICIiKS50cmltKCl9XGBgKTsKICAgICAgfSBlbHNlIGlmIChjdCA9PT0gInN0cm9uZyIgfHwgY3QgPT09ICJiIikgewogICAgICAgIGJpdHMucHVzaChgKiokeyhjaGlsZC50ZXh0Q29udGVudCB8fCAiIikudHJpbSgpfSoqYCk7CiAgICAgIH0gZWxzZSBpZiAoY3QgPT09ICJpbWciICYmIGNoaWxkLmdldEF0dHJpYnV0ZSgic3JjIikpIHsKICAgICAgICBjb25zdCBzcmMgPSBjaGlsZC5nZXRBdHRyaWJ1dGUoInNyYyIpIHx8ICIiOwogICAgICAgIGNvbnN0IGFsdCA9IGNoaWxkLmdldEF0dHJpYnV0ZSgiYWx0IikgfHwgIiI7CiAgICAgICAgYml0cy5wdXNoKGAhWyR7YWx0fV0oJHtzcmN9KWApOwogICAgICB9IGVsc2UgewogICAgICAgIGJpdHMucHVzaChlbGVtZW50VG9NYXJrZG93bihjaGlsZCkpOwogICAgICB9CiAgICB9CiAgfQogIHJldHVybiBiaXRzLmpvaW4oIiIpOwp9CgovKioKICogQHBhcmFtIHt1bmtub3dufSBtZXNzYWdlCiAqIEBwYXJhbSB7KHI6IHVua25vd24pID0+IHZvaWR9IHNlbmRSZXNwb25zZQogKi8KLyoqCiAqIEBwYXJhbSB7RWxlbWVudH0gZWwKICogQHBhcmFtIHtib29sZWFufSByZXF1aXJlVmlzaWJsZQogKi8KZnVuY3Rpb24gZWxlbWVudE1hdGNoZXNWaXNpYmxlKGVsLCByZXF1aXJlVmlzaWJsZSkgewogIGlmICghcmVxdWlyZVZpc2libGUpIHJldHVybiB0cnVlOwogIGlmICghKGVsIGluc3RhbmNlb2YgSFRNTEVsZW1lbnQpKSByZXR1cm4gZmFsc2U7CiAgaWYgKGVsLm9mZnNldFBhcmVudCA9PT0gbnVsbCkgewogICAgY29uc3Qgc3QgPSBnZXRDb21wdXRlZFN0eWxlKGVsKTsKICAgIGNvbnN0IHBvcyA9IHN0LnBvc2l0aW9uOwogICAgaWYgKHBvcyAhPT0gImZpeGVkIiAmJiBwb3MgIT09ICJzdGlja3kiKSByZXR1cm4gZmFsc2U7CiAgfQogIGNvbnN0IHN0ID0gZ2V0Q29tcHV0ZWRTdHlsZShlbCk7CiAgaWYgKHN0LmRpc3BsYXkgPT09ICJub25lIiB8fCBzdC52aXNpYmlsaXR5ID09PSAiaGlkZGVuIiB8fCBOdW1iZXIucGFyc2VGbG9hdChzdC5vcGFjaXR5KSA9PT0gMCkgewogICAgcmV0dXJuIGZhbHNlOwogIH0KICByZXR1cm4gdHJ1ZTsKfQoKLyoqCiAqIEBwYXJhbSB7dW5rbm93bn0gbWVzc2FnZQogKiBAcGFyYW0geyhyOiB1bmtub3duKSA9PiB2b2lkfSBzZW5kUmVzcG9uc2UKICovCmZ1bmN0aW9uIGhhbmRsZVdhaXRGb3JTZWxlY3RvcihtZXNzYWdlLCBzZW5kUmVzcG9uc2UpIHsKICBjb25zdCBtID0gLyoqIEB0eXBlIHt7IHNlbGVjdG9yPzogc3RyaW5nOyB0aW1lb3V0PzogbnVtYmVyOyB2aXNpYmxlPzogYm9vbGVhbiB9fSAqLyAobWVzc2FnZSk7CiAgY29uc3Qgc2VsZWN0b3IgPSB0eXBlb2YgbS5zZWxlY3RvciA9PT0gInN0cmluZyIgPyBtLnNlbGVjdG9yIDogIiI7CiAgY29uc3QgdGltZW91dCA9IHR5cGVvZiBtLnRpbWVvdXQgPT09ICJudW1iZXIiICYmIG0udGltZW91dCA+IDAgPyBtLnRpbWVvdXQgOiAxMDAwMDsKICBjb25zdCB2aXNpYmxlID0gbS52aXNpYmxlID09PSB0cnVlOwogIGNvbnN0IHN0YXJ0ID0gRGF0ZS5ub3coKTsKCiAgLyoqIEB0eXBlIHtSZXR1cm5UeXBlPHR5cGVvZiBzZXRJbnRlcnZhbD4gfCB1bmRlZmluZWR9ICovCiAgbGV0IGl2OwoKICBmdW5jdGlvbiB0aWNrKCkgewogICAgY29uc3QgZWwgPSBxdWVyeVNlbGVjdG9yT3JYUGF0aChzZWxlY3Rvcik7CiAgICBpZiAoZWwgJiYgZWxlbWVudE1hdGNoZXNWaXNpYmxlKGVsLCB2aXNpYmxlKSkgewogICAgICBpZiAoaXYgIT09IHVuZGVmaW5lZCkgY2xlYXJJbnRlcnZhbChpdik7CiAgICAgIGNvbnN0IHIgPSBlbC5nZXRCb3VuZGluZ0NsaWVudFJlY3QoKTsKICAgICAgc2VuZFJlc3BvbnNlKHsKICAgICAgICBmb3VuZDogdHJ1ZSwKICAgICAgICBzZWxlY3RvciwKICAgICAgICBlbGFwc2VkOiBEYXRlLm5vdygpIC0gc3RhcnQsCiAgICAgICAgZWxlbWVudDogewogICAgICAgICAgdGFnOiBlbC50YWdOYW1lLnRvTG93ZXJDYXNlKCksCiAgICAgICAgICBpZDogZWwuaWQgfHwgdW5kZWZpbmVkLAogICAgICAgICAgdGV4dDogKGVsLnRleHRDb250ZW50IHx8ICIiKS50cmltKCkuc2xpY2UoMCwgMjAwKSwKICAgICAgICAgIHJlY3Q6IHsgeDogci54LCB5OiByLnksIHdpZHRoOiByLndpZHRoLCBoZWlnaHQ6IHIuaGVpZ2h0IH0sCiAgICAgICAgfSwKICAgICAgfSk7CiAgICAgIHJldHVybjsKICAgIH0KICAgIGlmIChEYXRlLm5vdygpIC0gc3RhcnQgPj0gdGltZW91dCkgewogICAgICBpZiAoaXYgIT09IHVuZGVmaW5lZCkgY2xlYXJJbnRlcnZhbChpdik7CiAgICAgIHNlbmRSZXNwb25zZSh7CiAgICAgICAgZm91bmQ6IGZhbHNlLAogICAgICAgIHNlbGVjdG9yLAogICAgICAgIGVsYXBzZWQ6IERhdGUubm93KCkgLSBzdGFydCwKICAgICAgICBlcnJvcjogInRpbWVvdXQiLAogICAgICB9KTsKICAgIH0KICB9CgogIGl2ID0gc2V0SW50ZXJ2YWwodGljaywgMTAwKTsKICB0aWNrKCk7Cn0KCi8qKgogKiBAcGFyYW0ge3Vua25vd259IG1lc3NhZ2UKICogQHBhcmFtIHsocjogdW5rbm93bikgPT4gdm9pZH0gc2VuZFJlc3BvbnNlCiAqLwpmdW5jdGlvbiBoYW5kbGVHZXRDb25zb2xlTG9ncyhtZXNzYWdlLCBzZW5kUmVzcG9uc2UpIHsKICBjb25zdCBtID0gLyoqIEB0eXBlIHt7IGxldmVsPzogc3RyaW5nOyBsaW1pdD86IG51bWJlciB9fSAqLyAobWVzc2FnZSk7CiAgY29uc3QgbGV2ZWwgPSBtLmxldmVsID09PSAiZXJyb3IiIHx8IG0ubGV2ZWwgPT09ICJ3YXJuIiB8fCBtLmxldmVsID09PSAiaW5mbyIgfHwgbS5sZXZlbCA9PT0gImxvZyIgPyBtLmxldmVsIDogImFsbCI7CiAgY29uc3QgbGltaXQgPSB0eXBlb2YgbS5saW1pdCA9PT0gIm51bWJlciIgPyBNYXRoLm1pbig1MDAsIE1hdGgubWF4KDEsIG0ubGltaXQpKSA6IDEwMDsKICBsZXQgbG9ncyA9IGNvbnNvbGVSaW5nOwogIGlmIChsZXZlbCAhPT0gImFsbCIpIHsKICAgIGxvZ3MgPSBsb2dzLmZpbHRlcigoZSkgPT4gZS5sZXZlbCA9PT0gbGV2ZWwpOwogIH0KICBjb25zdCBzbGljZWQgPSBsb2dzLnNsaWNlKC1saW1pdCk7CiAgc2VuZFJlc3BvbnNlKHsgbG9nczogc2xpY2VkLCBjb3VudDogc2xpY2VkLmxlbmd0aCB9KTsKfQoKLyoqCiAqIEBwYXJhbSB7dW5rbm93bn0gX21lc3NhZ2UKICogQHBhcmFtIHsocjogdW5rbm93bikgPT4gdm9pZH0gc2VuZFJlc3BvbnNlCiAqLwpmdW5jdGlvbiBoYW5kbGVDbGVhckNvbnNvbGVMb2dzKF9tZXNzYWdlLCBzZW5kUmVzcG9uc2UpIHsKICBjb25zb2xlUmluZyA9IFtdOwogIHNlbmRSZXNwb25zZSh7IGNsZWFyZWQ6IHRydWUgfSk7Cn0KCi8qKgogKiBAcGFyYW0ge3Vua25vd259IG1lc3NhZ2UKICogQHBhcmFtIHsocjogdW5rbm93bikgPT4gdm9pZH0gc2VuZFJlc3BvbnNlCiAqLwpmdW5jdGlvbiBoYW5kbGVHZXRQYWdlRXJyb3JzKG1lc3NhZ2UsIHNlbmRSZXNwb25zZSkgewogIGNvbnN0IG0gPSAvKiogQHR5cGUge3sgbGltaXQ/OiBudW1iZXIgfX0gKi8gKG1lc3NhZ2UpOwogIGNvbnN0IGxpbWl0ID0gdHlwZW9mIG0ubGltaXQgPT09ICJudW1iZXIiID8gTWF0aC5taW4oMjAwLCBNYXRoLm1heCgxLCBtLmxpbWl0KSkgOiA1MDsKICBjb25zdCBzbGljZWQgPSBwYWdlRXJyb3JSaW5nLnNsaWNlKC1saW1pdCk7CiAgc2VuZFJlc3BvbnNlKHsgZXJyb3JzOiBzbGljZWQsIGNvdW50OiBzbGljZWQubGVuZ3RoIH0pOwp9CgovKioKICogQHBhcmFtIHt1bmtub3dufSBfbWVzc2FnZQogKiBAcGFyYW0geyhyOiB1bmtub3duKSA9PiB2b2lkfSBzZW5kUmVzcG9uc2UKICovCmZ1bmN0aW9uIGhhbmRsZUdldFNjcm9sbEluZm8oX21lc3NhZ2UsIHNlbmRSZXNwb25zZSkgewogIGNvbnN0IGRlID0gZG9jdW1lbnQuZG9jdW1lbnRFbGVtZW50OwogIGNvbnN0IGJvZHkgPSBkb2N1bWVudC5ib2R5OwogIHNlbmRSZXNwb25zZSh7CiAgICBzY3JvbGxIZWlnaHQ6IE1hdGgubWF4KGRlLnNjcm9sbEhlaWdodCwgYm9keSA/IGJvZHkuc2Nyb2xsSGVpZ2h0IDogMCwgZGUuY2xpZW50SGVpZ2h0KSwKICAgIGlubmVySGVpZ2h0OiB3aW5kb3cuaW5uZXJIZWlnaHQsCiAgICBpbm5lcldpZHRoOiB3aW5kb3cuaW5uZXJXaWR0aCwKICAgIHNjcm9sbFk6IHdpbmRvdy5zY3JvbGxZLAogICAgZGV2aWNlUGl4ZWxSYXRpbzogd2luZG93LmRldmljZVBpeGVsUmF0aW8gfHwgMSwKICB9KTsKfQoKLyoqCiAqIEBwYXJhbSB7dW5rbm93bn0gbWVzc2FnZQogKiBAcGFyYW0geyhyOiB1bmtub3duKSA9PiB2b2lkfSBzZW5kUmVzcG9uc2UKICovCmZ1bmN0aW9uIGhhbmRsZVNjcm9sbFRvKG1lc3NhZ2UsIHNlbmRSZXNwb25zZSkgewogIGNvbnN0IG0gPSAvKiogQHR5cGUge3sgeT86IG51bWJlciB9fSAqLyAobWVzc2FnZSk7CiAgY29uc3QgeSA9IHR5cGVvZiBtLnkgPT09ICJudW1iZXIiID8gbS55IDogMDsKICB3aW5kb3cuc2Nyb2xsVG8oeyB0b3A6IHksIGxlZnQ6IDAsIGJlaGF2aW9yOiAiaW5zdGFudCIgfSk7CiAgc2VuZFJlc3BvbnNlKHsgc2Nyb2xsWTogd2luZG93LnNjcm9sbFkgfSk7Cn0KCi8qKgogKiBAcGFyYW0ge3Vua25vd259IG1lc3NhZ2UKICogQHBhcmFtIHsocjogdW5rbm93bikgPT4gdm9pZH0gc2VuZFJlc3BvbnNlCiAqLwpmdW5jdGlvbiBoYW5kbGVIb3ZlckVsZW1lbnQobWVzc2FnZSwgc2VuZFJlc3BvbnNlKSB7CiAgY29uc3QgbSA9IC8qKiBAdHlwZSB7eyBzZWxlY3Rvcj86IHN0cmluZyB9fSAqLyAobWVzc2FnZSk7CiAgY29uc3Qgc2VsZWN0b3IgPSB0eXBlb2YgbS5zZWxlY3RvciA9PT0gInN0cmluZyIgPyBtLnNlbGVjdG9yIDogIiI7CiAgaWYgKCFzZWxlY3Rvci50cmltKCkpIHsKICAgIHNlbmRSZXNwb25zZSh7IHN1Y2Nlc3M6IGZhbHNlIH0pOwogICAgcmV0dXJuOwogIH0KICBjb25zdCBlbCA9IHF1ZXJ5U2VsZWN0b3JPclhQYXRoKHNlbGVjdG9yKTsKICBpZiAoIWVsKSB7CiAgICBzZW5kUmVzcG9uc2UoeyBzdWNjZXNzOiBmYWxzZSB9KTsKICAgIHJldHVybjsKICB9CiAgY29uc3QgciA9IGVsLmdldEJvdW5kaW5nQ2xpZW50UmVjdCgpOwogIGNvbnN0IHggPSByLmxlZnQgKyByLndpZHRoIC8gMjsKICBjb25zdCB5ID0gci50b3AgKyByLmhlaWdodCAvIDI7CiAgY29uc3QgaW5pdCA9IHsgYnViYmxlczogdHJ1ZSwgY2FuY2VsYWJsZTogdHJ1ZSwgdmlldzogd2luZG93LCBjbGllbnRYOiB4LCBjbGllbnRZOiB5IH07CiAgZWwuZGlzcGF0Y2hFdmVudChuZXcgTW91c2VFdmVudCgibW91c2Vtb3ZlIiwgaW5pdCkpOwogIGVsLmRpc3BhdGNoRXZlbnQobmV3IE1vdXNlRXZlbnQoIm1vdXNlb3ZlciIsIGluaXQpKTsKICBlbC5kaXNwYXRjaEV2ZW50KG5ldyBNb3VzZUV2ZW50KCJtb3VzZWVudGVyIiwgaW5pdCkpOwogIHNlbmRSZXNwb25zZSh7CiAgICBzdWNjZXNzOiB0cnVlLAogICAgZWxlbWVudDogewogICAgICB0YWc6IGVsLnRhZ05hbWUudG9Mb3dlckNhc2UoKSwKICAgICAgaWQ6IGVsLmlkIHx8IHVuZGVmaW5lZCwKICAgICAgdGV4dDogKGVsLnRleHRDb250ZW50IHx8ICIiKS50cmltKCkuc2xpY2UoMCwgMjAwKSwKICAgICAgcmVjdDogeyB4OiByLngsIHk6IHIueSwgd2lkdGg6IHIud2lkdGgsIGhlaWdodDogci5oZWlnaHQgfSwKICAgIH0sCiAgfSk7Cn0KCi8qKgogKiBAcGFyYW0ge3Vua25vd259IG1lc3NhZ2UKICogQHBhcmFtIHsocjogdW5rbm93bikgPT4gdm9pZH0gc2VuZFJlc3BvbnNlCiAqLwpmdW5jdGlvbiBoYW5kbGVTY3JpcHRJbmplY3QobWVzc2FnZSwgc2VuZFJlc3BvbnNlKSB7CiAgY29uc3QgbSA9IC8qKiBAdHlwZSB7eyBzY3JpcHQ/OiBzdHJpbmcgfX0gKi8gKG1lc3NhZ2UpOwogIGNvbnN0IHNjcmlwdCA9IHR5cGVvZiBtLnNjcmlwdCA9PT0gInN0cmluZyIgPyBtLnNjcmlwdCA6ICIiOwogIGlmICghc2NyaXB0KSB7CiAgICBzZW5kUmVzcG9uc2UoeyBzdWNjZXNzOiBmYWxzZSB9KTsKICAgIHJldHVybjsKICB9CiAgdHJ5IHsKICAgIGNvbnN0IHMgPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KCJzY3JpcHQiKTsKICAgIHMudGV4dENvbnRlbnQgPSBzY3JpcHQ7CiAgICBjb25zdCByb290ID0gZG9jdW1lbnQuZG9jdW1lbnRFbGVtZW50IHx8IGRvY3VtZW50LmhlYWQgfHwgZG9jdW1lbnQuYm9keTsKICAgIGlmICghcm9vdCkgewogICAgICBzZW5kUmVzcG9uc2UoeyBzdWNjZXNzOiBmYWxzZSB9KTsKICAgICAgcmV0dXJuOwogICAgfQogICAgcm9vdC5hcHBlbmRDaGlsZChzKTsKICAgIHMucmVtb3ZlKCk7CiAgICBzZW5kUmVzcG9uc2UoeyBzdWNjZXNzOiB0cnVlIH0pOwogIH0gY2F0Y2ggKGVycikgewogICAgc2VuZFJlc3BvbnNlKHsgc3VjY2VzczogZmFsc2UsIGVycm9yOiBTdHJpbmcoZXJyKSB9KTsKICB9Cn0KCi8qKgogKiBAcGFyYW0ge3Vua25vd259IG1lc3NhZ2UKICogQHBhcmFtIHsocjogdW5rbm93bikgPT4gdm9pZH0gc2VuZFJlc3BvbnNlCiAqLwpmdW5jdGlvbiBoYW5kbGVGaWxsRm9ybShtZXNzYWdlLCBzZW5kUmVzcG9uc2UpIHsKICBjb25zdCBtID0gLyoqIEB0eXBlIHt7CiAgICBmaWVsZHM/OiBBcnJheTx7IHNlbGVjdG9yPzogc3RyaW5nOyB2YWx1ZT86IHN0cmluZzsgdHlwZT86IHN0cmluZyB9PjsKICAgIHN1Ym1pdEFmdGVyPzogYm9vbGVhbjsKICAgIHN1Ym1pdFNlbGVjdG9yPzogc3RyaW5nOwogIH19ICovIChtZXNzYWdlKTsKICBjb25zdCBmaWVsZHMgPSBBcnJheS5pc0FycmF5KG0uZmllbGRzKSA/IG0uZmllbGRzIDogW107CiAgLyoqIEB0eXBlIHtBcnJheTx7IHNlbGVjdG9yOiBzdHJpbmc7IGVycm9yOiBzdHJpbmcgfT59ICovCiAgY29uc3QgZXJyb3JzID0gW107CiAgbGV0IGZpbGxlZCA9IDA7CgogIGZvciAoY29uc3QgZiBvZiBmaWVsZHMpIHsKICAgIGNvbnN0IHNlbCA9IHR5cGVvZiBmLnNlbGVjdG9yID09PSAic3RyaW5nIiA/IGYuc2VsZWN0b3IgOiAiIjsKICAgIGNvbnN0IHZhbCA9IHR5cGVvZiBmLnZhbHVlID09PSAic3RyaW5nIiA/IGYudmFsdWUgOiAiIjsKICAgIGNvbnN0IHR5cCA9IGYudHlwZSA9PT0gInNlbGVjdCIgfHwgZi50eXBlID09PSAiY2hlY2tib3giIHx8IGYudHlwZSA9PT0gInJhZGlvIiB8fCBmLnR5cGUgPT09ICJmaWxlIiA/IGYudHlwZSA6ICJ0ZXh0IjsKICAgIGlmICghc2VsKSB7CiAgICAgIGVycm9ycy5wdXNoKHsgc2VsZWN0b3I6IHNlbCwgZXJyb3I6ICJlbXB0eSBzZWxlY3RvciIgfSk7CiAgICAgIGNvbnRpbnVlOwogICAgfQogICAgY29uc3QgZWwgPSBxdWVyeVNlbGVjdG9yT3JYUGF0aChzZWwpOwogICAgaWYgKCFlbCkgewogICAgICBlcnJvcnMucHVzaCh7IHNlbGVjdG9yOiBzZWwsIGVycm9yOiAibm90IGZvdW5kIiB9KTsKICAgICAgY29udGludWU7CiAgICB9CiAgICB0cnkgewogICAgICBpZiAodHlwID09PSAiZmlsZSIpIHsKICAgICAgICBlcnJvcnMucHVzaCh7IHNlbGVjdG9yOiBzZWwsIGVycm9yOiAiZmlsZSBpbnB1dHMgYXJlIG5vdCBzdXBwb3J0ZWQiIH0pOwogICAgICAgIGNvbnRpbnVlOwogICAgICB9CiAgICAgIGlmICh0eXAgPT09ICJjaGVja2JveCIpIHsKICAgICAgICBjb25zdCBpbnB1dCA9IGVsIGluc3RhbmNlb2YgSFRNTElucHV0RWxlbWVudCA/IGVsIDogbnVsbDsKICAgICAgICBpZiAoIWlucHV0IHx8IGlucHV0LnR5cGUgIT09ICJjaGVja2JveCIpIHsKICAgICAgICAgIGVycm9ycy5wdXNoKHsgc2VsZWN0b3I6IHNlbCwgZXJyb3I6ICJub3QgYSBjaGVja2JveCBpbnB1dCIgfSk7CiAgICAgICAgICBjb250aW51ZTsKICAgICAgICB9CiAgICAgICAgY29uc3QgdmwgPSB2YWwudG9Mb3dlckNhc2UoKTsKICAgICAgICBpbnB1dC5jaGVja2VkID0gISh2bCA9PT0gImZhbHNlIiB8fCB2YWwgPT09ICIwIiB8fCB2bCA9PT0gIm9mZiIgfHwgdmFsID09PSAiIik7CiAgICAgICAgaW5wdXQuZGlzcGF0Y2hFdmVudChuZXcgRXZlbnQoImlucHV0IiwgeyBidWJibGVzOiB0cnVlIH0pKTsKICAgICAgICBpbnB1dC5kaXNwYXRjaEV2ZW50KG5ldyBFdmVudCgiY2hhbmdlIiwgeyBidWJibGVzOiB0cnVlIH0pKTsKICAgICAgICBmaWxsZWQgKz0gMTsKICAgICAgICBjb250aW51ZTsKICAgICAgfQogICAgICBpZiAodHlwID09PSAicmFkaW8iKSB7CiAgICAgICAgY29uc3QgaW5wdXQgPSBlbCBpbnN0YW5jZW9mIEhUTUxJbnB1dEVsZW1lbnQgPyBlbCA6IG51bGw7CiAgICAgICAgaWYgKCFpbnB1dCB8fCBpbnB1dC50eXBlICE9PSAicmFkaW8iKSB7CiAgICAgICAgICBlcnJvcnMucHVzaCh7IHNlbGVjdG9yOiBzZWwsIGVycm9yOiAibm90IGEgcmFkaW8gaW5wdXQiIH0pOwogICAgICAgICAgY29udGludWU7CiAgICAgICAgfQogICAgICAgIGNvbnN0IHZsID0gdmFsLnRvTG93ZXJDYXNlKCk7CiAgICAgICAgY29uc3Qgb2ZmID0gdmFsID09PSAiIiB8fCB2bCA9PT0gImZhbHNlIiB8fCB2YWwgPT09ICIwIiB8fCB2bCA9PT0gIm9mZiI7CiAgICAgICAgaWYgKG9mZikgewogICAgICAgICAgaW5wdXQuY2hlY2tlZCA9IGZhbHNlOwogICAgICAgIH0gZWxzZSB7CiAgICAgICAgICBpbnB1dC5jaGVja2VkID0gdHJ1ZTsKICAgICAgICAgIGlmIChpbnB1dC5mb3JtKSB7CiAgICAgICAgICAgIGNvbnN0IHJhZHMgPSBpbnB1dC5mb3JtLnF1ZXJ5U2VsZWN0b3JBbGwoJ2lucHV0W3R5cGU9InJhZGlvIl0nKTsKICAgICAgICAgICAgcmFkcy5mb3JFYWNoKCh4KSA9PiB7CiAgICAgICAgICAgICAgaWYgKHggaW5zdGFuY2VvZiBIVE1MSW5wdXRFbGVtZW50ICYmIHgubmFtZSA9PT0gaW5wdXQubmFtZSAmJiB4ICE9PSBpbnB1dCkgewogICAgICAgICAgICAgICAgeC5jaGVja2VkID0gZmFsc2U7CiAgICAgICAgICAgICAgfQogICAgICAgICAgICB9KTsKICAgICAgICAgIH0KICAgICAgICB9CiAgICAgICAgaW5wdXQuZGlzcGF0Y2hFdmVudChuZXcgRXZlbnQoImlucHV0IiwgeyBidWJibGVzOiB0cnVlIH0pKTsKICAgICAgICBpbnB1dC5kaXNwYXRjaEV2ZW50KG5ldyBFdmVudCgiY2hhbmdlIiwgeyBidWJibGVzOiB0cnVlIH0pKTsKICAgICAgICBmaWxsZWQgKz0gMTsKICAgICAgICBjb250aW51ZTsKICAgICAgfQogICAgICBpZiAodHlwID09PSAic2VsZWN0IiAmJiBlbCBpbnN0YW5jZW9mIEhUTUxTZWxlY3RFbGVtZW50KSB7CiAgICAgICAgbGV0IG1hdGNoZWQgPSBmYWxzZTsKICAgICAgICBmb3IgKGxldCBpID0gMDsgaSA8IGVsLm9wdGlvbnMubGVuZ3RoOyBpICs9IDEpIHsKICAgICAgICAgIGNvbnN0IG8gPSBlbC5vcHRpb25zW2ldOwogICAgICAgICAgaWYgKG8udmFsdWUgPT09IHZhbCB8fCBvLnRleHQgPT09IHZhbCkgewogICAgICAgICAgICBlbC5zZWxlY3RlZEluZGV4ID0gaTsKICAgICAgICAgICAgbWF0Y2hlZCA9IHRydWU7CiAgICAgICAgICAgIGJyZWFrOwogICAgICAgICAgfQogICAgICAgIH0KICAgICAgICBpZiAoIW1hdGNoZWQpIGVsLnZhbHVlID0gdmFsOwogICAgICAgIGVsLmRpc3BhdGNoRXZlbnQobmV3IEV2ZW50KCJpbnB1dCIsIHsgYnViYmxlczogdHJ1ZSB9KSk7CiAgICAgICAgZWwuZGlzcGF0Y2hFdmVudChuZXcgRXZlbnQoImNoYW5nZSIsIHsgYnViYmxlczogdHJ1ZSB9KSk7CiAgICAgICAgZmlsbGVkICs9IDE7CiAgICAgICAgY29udGludWU7CiAgICAgIH0KICAgICAgaWYgKGVsIGluc3RhbmNlb2YgSFRNTElucHV0RWxlbWVudCB8fCBlbCBpbnN0YW5jZW9mIEhUTUxUZXh0QXJlYUVsZW1lbnQpIHsKICAgICAgICBlbC5mb2N1cygpOwogICAgICAgIGVsLnZhbHVlID0gdmFsOwogICAgICAgIGVsLmRpc3BhdGNoRXZlbnQoCiAgICAgICAgICBuZXcgSW5wdXRFdmVudCgiaW5wdXQiLCB7IGJ1YmJsZXM6IHRydWUsIGRhdGE6IHZhbCwgaW5wdXRUeXBlOiAiaW5zZXJ0UmVwbGFjZW1lbnRUZXh0IiB9KSwKICAgICAgICApOwogICAgICAgIGVsLmRpc3BhdGNoRXZlbnQobmV3IEV2ZW50KCJjaGFuZ2UiLCB7IGJ1YmJsZXM6IHRydWUgfSkpOwogICAgICAgIGZpbGxlZCArPSAxOwogICAgICAgIGNvbnRpbnVlOwogICAgICB9CiAgICAgIGVycm9ycy5wdXNoKHsgc2VsZWN0b3I6IHNlbCwgZXJyb3I6ICJ1bnN1cHBvcnRlZCBlbGVtZW50IGZvciB0ZXh0IGZpbGwiIH0pOwogICAgfSBjYXRjaCAoZXJyKSB7CiAgICAgIGVycm9ycy5wdXNoKHsgc2VsZWN0b3I6IHNlbCwgZXJyb3I6IFN0cmluZyhlcnIpIH0pOwogICAgfQogIH0KCiAgbGV0IHN1Y2Nlc3MgPSBlcnJvcnMubGVuZ3RoID09PSAwOwogIGlmIChtLnN1Ym1pdEFmdGVyID09PSB0cnVlKSB7CiAgICBjb25zdCBzdWJTZWwgPSB0eXBlb2YgbS5zdWJtaXRTZWxlY3RvciA9PT0gInN0cmluZyIgPyBtLnN1Ym1pdFNlbGVjdG9yLnRyaW0oKSA6ICIiOwogICAgbGV0IHN1YiA9IHN1YlNlbCA/IHF1ZXJ5U2VsZWN0b3JPclhQYXRoKHN1YlNlbCkgOiBudWxsOwogICAgaWYgKCFzdWIgJiYgZmllbGRzWzBdKSB7CiAgICAgIGNvbnN0IGZpcnN0U2VsID0gdHlwZW9mIGZpZWxkc1swXS5zZWxlY3RvciA9PT0gInN0cmluZyIgPyBmaWVsZHNbMF0uc2VsZWN0b3IgOiAiIjsKICAgICAgY29uc3QgZmlyc3QgPSBmaXJzdFNlbCA/IHF1ZXJ5U2VsZWN0b3JPclhQYXRoKGZpcnN0U2VsKSA6IG51bGw7CiAgICAgIGNvbnN0IGZvcm0gPSBmaXJzdCAmJiBmaXJzdC5jbG9zZXN0ID8gZmlyc3QuY2xvc2VzdCgiZm9ybSIpIDogbnVsbDsKICAgICAgaWYgKGZvcm0pIHsKICAgICAgICBzdWIgPQogICAgICAgICAgZm9ybS5xdWVyeVNlbGVjdG9yKCdidXR0b25bdHlwZT0ic3VibWl0Il0sIGlucHV0W3R5cGU9InN1Ym1pdCJdLCBidXR0b246bm90KFt0eXBlXSknKTsKICAgICAgfQogICAgfQogICAgaWYgKHN1YikgewogICAgICBzeW50aGV0aWNDbGljayhzdWIpOwogICAgfSBlbHNlIHsKICAgICAgZXJyb3JzLnB1c2goeyBzZWxlY3RvcjogIltzdWJtaXRdIiwgZXJyb3I6ICJubyBzdWJtaXQgY29udHJvbCBmb3VuZCIgfSk7CiAgICAgIHN1Y2Nlc3MgPSBmYWxzZTsKICAgIH0KICB9CgogIHNlbmRSZXNwb25zZSh7IHN1Y2Nlc3MsIGZpbGxlZCwgZXJyb3JzIH0pOwp9CgovKioKICogQHBhcmFtIHt1bmtub3dufSBtZXNzYWdlCiAqIEBwYXJhbSB7KHI6IHVua25vd24pID0+IHZvaWR9IHNlbmRSZXNwb25zZQogKi8KZnVuY3Rpb24gaGFuZGxlR2V0U3RvcmFnZVBhZ2UobWVzc2FnZSwgc2VuZFJlc3BvbnNlKSB7CiAgY29uc3QgbSA9IC8qKiBAdHlwZSB7eyBzdG9yYWdlVHlwZT86IHN0cmluZzsga2V5Pzogc3RyaW5nIH19ICovIChtZXNzYWdlKTsKICBjb25zdCB1c2VTZXNzaW9uID0gbS5zdG9yYWdlVHlwZSA9PT0gInNlc3Npb24iOwogIGNvbnN0IHQgPSB1c2VTZXNzaW9uID8gc2Vzc2lvblN0b3JhZ2UgOiBsb2NhbFN0b3JhZ2U7CiAgY29uc3Qga2V5ID0gdHlwZW9mIG0ua2V5ID09PSAic3RyaW5nIiA/IG0ua2V5IDogdW5kZWZpbmVkOwogIC8qKiBAdHlwZSB7UmVjb3JkPHN0cmluZywgc3RyaW5nPn0gKi8KICBjb25zdCBkYXRhID0ge307CiAgdHJ5IHsKICAgIGlmIChrZXkpIHsKICAgICAgY29uc3QgdiA9IHQuZ2V0SXRlbShrZXkpOwogICAgICBpZiAodiAhPT0gbnVsbCkgZGF0YVtrZXldID0gdjsKICAgIH0gZWxzZSB7CiAgICAgIGZvciAobGV0IGkgPSAwOyBpIDwgdC5sZW5ndGg7IGkgKz0gMSkgewogICAgICAgIGNvbnN0IGsgPSB0LmtleShpKTsKICAgICAgICBpZiAoaykgZGF0YVtrXSA9IHQuZ2V0SXRlbShrKSA/PyAiIjsKICAgICAgfQogICAgfQogICAgc2VuZFJlc3BvbnNlKHsgZGF0YSwgY291bnQ6IE9iamVjdC5rZXlzKGRhdGEpLmxlbmd0aCB9KTsKICB9IGNhdGNoIChlcnIpIHsKICAgIHNlbmRSZXNwb25zZSh7IGRhdGE6IHt9LCBjb3VudDogMCwgZXJyb3I6IFN0cmluZyhlcnIpIH0pOwogIH0KfQoKLyoqCiAqIEBwYXJhbSB7dW5rbm93bn0gbWVzc2FnZQogKiBAcGFyYW0geyhyOiB1bmtub3duKSA9PiB2b2lkfSBzZW5kUmVzcG9uc2UKICovCmZ1bmN0aW9uIGhhbmRsZVNldFN0b3JhZ2VQYWdlKG1lc3NhZ2UsIHNlbmRSZXNwb25zZSkgewogIGNvbnN0IG0gPSAvKiogQHR5cGUge3sgc3RvcmFnZVR5cGU/OiBzdHJpbmc7IGtleT86IHN0cmluZzsgdmFsdWU/OiBzdHJpbmcgfX0gKi8gKG1lc3NhZ2UpOwogIGNvbnN0IHVzZVNlc3Npb24gPSBtLnN0b3JhZ2VUeXBlID09PSAic2Vzc2lvbiI7CiAgY29uc3QgdCA9IHVzZVNlc3Npb24gPyBzZXNzaW9uU3RvcmFnZSA6IGxvY2FsU3RvcmFnZTsKICBjb25zdCBrZXkgPSB0eXBlb2YgbS5rZXkgPT09ICJzdHJpbmciID8gbS5rZXkgOiAiIjsKICBjb25zdCB2YWx1ZSA9IHR5cGVvZiBtLnZhbHVlID09PSAic3RyaW5nIiA/IG0udmFsdWUgOiAiIjsKICBpZiAoIWtleSkgewogICAgc2VuZFJlc3BvbnNlKHsgc3VjY2VzczogZmFsc2UgfSk7CiAgICByZXR1cm47CiAgfQogIHRyeSB7CiAgICB0LnNldEl0ZW0oa2V5LCB2YWx1ZSk7CiAgICBzZW5kUmVzcG9uc2UoeyBzdWNjZXNzOiB0cnVlIH0pOwogIH0gY2F0Y2ggKGVycikgewogICAgc2VuZFJlc3BvbnNlKHsgc3VjY2VzczogZmFsc2UsIGVycm9yOiBTdHJpbmcoZXJyKSB9KTsKICB9Cn0KCmZ1bmN0aW9uIGhhbmRsZVJlYWRQYWdlKG1lc3NhZ2UsIHNlbmRSZXNwb25zZSkgewogIGNvbnN0IG0gPSAvKiogQHR5cGUge3sgZm9ybWF0Pzogc3RyaW5nIH19ICovIChtZXNzYWdlKTsKICBjb25zdCBmb3JtYXQgPQogICAgbS5mb3JtYXQgPT09ICJtYXJrZG93biIgfHwgbS5mb3JtYXQgPT09ICJ0ZXh0IiB8fCBtLmZvcm1hdCA9PT0gInN0cnVjdHVyZWQiID8gbS5mb3JtYXQgOiAic3RydWN0dXJlZCI7CiAgY29uc3Qgcm9vdCA9IGdldFJlYWRQYWdlUm9vdCgpOwogIGNvbnN0IHRpdGxlID0gZG9jdW1lbnQudGl0bGUgfHwgIiI7CiAgY29uc3QgdXJsID0gbG9jYXRpb24uaHJlZjsKCiAgaWYgKGZvcm1hdCA9PT0gInRleHQiKSB7CiAgICBjb25zdCBjbG9uZSA9IC8qKiBAdHlwZSB7SFRNTEVsZW1lbnR9ICovIChyb290LmNsb25lTm9kZSh0cnVlKSk7CiAgICBzdHJpcE5vaXNlKGNsb25lKTsKICAgIGNvbnN0IHRleHQgPSAoY2xvbmUuaW5uZXJUZXh0IHx8ICIiKS50cmltKCkucmVwbGFjZSgvXHMrL2csICIgIik7CiAgICBzZW5kUmVzcG9uc2UoeyB0ZXh0LCB1cmwsIHRpdGxlLCB3b3JkQ291bnQ6IHdvcmRDb3VudEZyb20odGV4dCkgfSk7CiAgICByZXR1cm47CiAgfQoKICBpZiAoZm9ybWF0ID09PSAibWFya2Rvd24iKSB7CiAgICBjb25zdCBjbG9uZSA9IC8qKiBAdHlwZSB7SFRNTEVsZW1lbnR9ICovIChyb290LmNsb25lTm9kZSh0cnVlKSk7CiAgICBzdHJpcE5vaXNlKGNsb25lKTsKICAgIGNvbnN0IG1hcmtkb3duID0gZWxlbWVudFRvTWFya2Rvd24oY2xvbmUpLnRyaW0oKTsKICAgIHNlbmRSZXNwb25zZSh7IG1hcmtkb3duLCB1cmwsIHRpdGxlLCB3b3JkQ291bnQ6IHdvcmRDb3VudEZyb20obWFya2Rvd24pIH0pOwogICAgcmV0dXJuOwogIH0KCiAgY29uc3Qgc3RydWN0dXJlZCA9IHJlYWRTdHJ1Y3R1cmVkKHJvb3QpOwogIGNvbnN0IHdjID0gd29yZENvdW50RnJvbShzdHJ1Y3R1cmVkLm1haW5UZXh0KTsKICBzZW5kUmVzcG9uc2UoeyAuLi5zdHJ1Y3R1cmVkLCB3b3JkQ291bnQ6IHdjIH0pOwp9CgovKiogQHR5cGUge1JlY29yZDxzdHJpbmcsIChtZXNzYWdlOiB1bmtub3duLCBzZW5kUmVzcG9uc2U6IChyOiB1bmtub3duKSA9PiB2b2lkKSA9PiB2b2lkPn0gKi8KY29uc3QgTUVTU0FHRV9IQU5ETEVSUyA9IHsKICBQT0tFX0NMSUNLX0VMRU1FTlQ6IGhhbmRsZUNsaWNrRWxlbWVudCwKICBQT0tFX1JFU09MVkVfQ0xJQ0tfUE9JTlQ6IGhhbmRsZVJlc29sdmVDbGlja1BvaW50LAogIFBPS0VfVFlQRV9URVhUOiBoYW5kbGVUeXBlVGV4dCwKICBQT0tFX1NDUk9MTF9XSU5ET1c6IGhhbmRsZVNjcm9sbFdpbmRvdywKICBQT0tFX0VWQUw6IGhhbmRsZUV2YWwsCiAgUE9LRV9HRVRfRE9NX1NOQVBTSE9UOiBoYW5kbGVHZXREb21TbmFwc2hvdCwKICBQT0tFX0dFVF9BMTFZX1RSRUU6IGhhbmRsZUdldEFjY2Vzc2liaWxpdHlUcmVlLAogIFBPS0VfRklORF9FTEVNRU5UOiBoYW5kbGVGaW5kRWxlbWVudCwKICBQT0tFX1JFQURfUEFHRTogaGFuZGxlUmVhZFBhZ2UsCiAgUE9LRV9XQUlUX0ZPUl9TRUxFQ1RPUjogaGFuZGxlV2FpdEZvclNlbGVjdG9yLAogIFBPS0VfR0VUX0NPTlNPTEVfTE9HUzogaGFuZGxlR2V0Q29uc29sZUxvZ3MsCiAgUE9LRV9DTEVBUl9DT05TT0xFX0xPR1M6IGhhbmRsZUNsZWFyQ29uc29sZUxvZ3MsCiAgUE9LRV9HRVRfUEFHRV9FUlJPUlM6IGhhbmRsZUdldFBhZ2VFcnJvcnMsCiAgUE9LRV9HRVRfU0NST0xMX0lORk86IGhhbmRsZUdldFNjcm9sbEluZm8sCiAgUE9LRV9TQ1JPTExfVE86IGhhbmRsZVNjcm9sbFRvLAogIFBPS0VfSE9WRVJfRUxFTUVOVDogaGFuZGxlSG92ZXJFbGVtZW50LAogIFBPS0VfU0NSSVBUX0lOSkVDVDogaGFuZGxlU2NyaXB0SW5qZWN0LAogIFBPS0VfRklMTF9GT1JNOiBoYW5kbGVGaWxsRm9ybSwKICBQT0tFX0dFVF9TVE9SQUdFOiBoYW5kbGVHZXRTdG9yYWdlUGFnZSwKICBQT0tFX1NFVF9TVE9SQUdFOiBoYW5kbGVTZXRTdG9yYWdlUGFnZSwKfTsKCmNocm9tZS5ydW50aW1lLm9uTWVzc2FnZS5hZGRMaXN0ZW5lcigobWVzc2FnZSwgX3NlbmRlciwgc2VuZFJlc3BvbnNlKSA9PiB7CiAgY29uc3QgdCA9IG1lc3NhZ2UgJiYgdHlwZW9mIG1lc3NhZ2UgPT09ICJvYmplY3QiICYmICJ0eXBlIiBpbiBtZXNzYWdlID8gU3RyaW5nKG1lc3NhZ2UudHlwZSkgOiAiIjsKICBjb25zdCBmbiA9IE1FU1NBR0VfSEFORExFUlNbdF07CiAgaWYgKCFmbikgcmV0dXJuIHVuZGVmaW5lZDsKICBxdWV1ZU1pY3JvdGFzaygoKSA9PiB7CiAgICB0cnkgewogICAgICBmbihtZXNzYWdlLCBzZW5kUmVzcG9uc2UpOwogICAgfSBjYXRjaCAoZXJyKSB7CiAgICAgIHNlbmRSZXNwb25zZSh7IHN1Y2Nlc3M6IGZhbHNlLCBlcnJvcjogU3RyaW5nKGVyciksIG9rOiBmYWxzZSB9KTsKICAgIH0KICB9KTsKICByZXR1cm4gdHJ1ZTsKfSk7Cg==
1
+ /**
2
+ * Relays automation commands from the service worker into the page.
3
+ */
4
+
5
+ const CONSOLE_RING_MAX = 500;
6
+ const PAGE_ERROR_RING_MAX = 200;
7
+
8
+ /** @type {Array<{ level: string; message: string; timestamp: number; stack?: string }>} */
9
+ let consoleRing = [];
10
+
11
+ /**
12
+ * Uncaught errors and unhandled rejections (separate from console ring).
13
+ * @type {Array<{ kind: string; message: string; stack?: string; filename?: string; lineno?: number; colno?: number; timestamp: number }>}
14
+ */
15
+ let pageErrorRing = [];
16
+
17
+ /**
18
+ * @param {{ kind: string; message: string; stack?: string; filename?: string; lineno?: number; colno?: number; timestamp: number }} entry
19
+ */
20
+ function pushPageError(entry) {
21
+ pageErrorRing.push(entry);
22
+ while (pageErrorRing.length > PAGE_ERROR_RING_MAX) pageErrorRing.shift();
23
+ }
24
+
25
+ window.addEventListener("error", (ev) => {
26
+ try {
27
+ pushPageError({
28
+ kind: "error",
29
+ message: ev.message || String(ev.error || "error"),
30
+ stack: ev.error instanceof Error ? ev.error.stack : undefined,
31
+ filename: ev.filename,
32
+ lineno: ev.lineno,
33
+ colno: ev.colno,
34
+ timestamp: Date.now(),
35
+ });
36
+ } catch {
37
+ /* ignore */
38
+ }
39
+ });
40
+
41
+ window.addEventListener("unhandledrejection", (ev) => {
42
+ try {
43
+ const reason = ev.reason;
44
+ const message =
45
+ reason instanceof Error ? reason.message : typeof reason === "string" ? reason : String(reason);
46
+ const stack = reason instanceof Error ? reason.stack : undefined;
47
+ pushPageError({
48
+ kind: "unhandledrejection",
49
+ message,
50
+ stack,
51
+ timestamp: Date.now(),
52
+ });
53
+ } catch {
54
+ /* ignore */
55
+ }
56
+ });
57
+
58
+ /**
59
+ * @param {unknown} a
60
+ */
61
+ function formatConsoleArg(a) {
62
+ if (a instanceof Error) return a.stack || a.message;
63
+ if (typeof a === "object" && a !== null) {
64
+ try {
65
+ return JSON.stringify(a);
66
+ } catch {
67
+ return String(a);
68
+ }
69
+ }
70
+ return String(a);
71
+ }
72
+
73
+ /**
74
+ * @param {string} level
75
+ * @param {unknown[]} args
76
+ */
77
+ function pushConsoleEntry(level, args) {
78
+ const message = args.map(formatConsoleArg).join(" ").slice(0, 20000);
79
+ const errArg = args.find((x) => x instanceof Error);
80
+ consoleRing.push({
81
+ level,
82
+ message,
83
+ timestamp: Date.now(),
84
+ stack: errArg instanceof Error ? errArg.stack : undefined,
85
+ });
86
+ while (consoleRing.length > CONSOLE_RING_MAX) consoleRing.shift();
87
+ }
88
+
89
+ ["log", "info", "warn", "error"].forEach((level) => {
90
+ const orig = console[level].bind(console);
91
+ console[level] = function pokeConsolePatched(...args) {
92
+ try {
93
+ pushConsoleEntry(level, args);
94
+ } catch {
95
+ /* ignore ring failures */
96
+ }
97
+ orig(...args);
98
+ };
99
+ });
100
+
101
+ /**
102
+ * Query selector across the document tree and inside open shadow roots (same-document; does not cross iframes).
103
+ * @param {Document | ShadowRoot | Element} root
104
+ * @param {string} selector
105
+ * @returns {Element[]}
106
+ */
107
+ function deepQueryAll(root, selector) {
108
+ /** @type {Element[]} */
109
+ const results = [];
110
+ try {
111
+ results.push(...root.querySelectorAll(selector));
112
+ } catch {
113
+ return results;
114
+ }
115
+ for (const el of root.querySelectorAll("*")) {
116
+ if (el.shadowRoot) {
117
+ results.push(...deepQueryAll(el.shadowRoot, selector));
118
+ }
119
+ }
120
+ return results;
121
+ }
122
+
123
+ /**
124
+ * XPath across the light tree and each open shadow root (shadow evaluated with that root as context).
125
+ * @param {string} expr
126
+ * @returns {Element[]}
127
+ */
128
+ function deepXPathAll(expr) {
129
+ /** @type {Element[]} */
130
+ const out = [];
131
+ /**
132
+ * @param {Document | ShadowRoot} context
133
+ */
134
+ function collectFrom(context) {
135
+ try {
136
+ const r = document.evaluate(expr, context, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
137
+ for (let i = 0; i < r.snapshotLength; i++) {
138
+ const n = r.snapshotItem(i);
139
+ if (n instanceof Element) out.push(n);
140
+ }
141
+ } catch {
142
+ /* ignore */
143
+ }
144
+ }
145
+ collectFrom(document);
146
+ /**
147
+ * @param {Element} el
148
+ */
149
+ function walk(el) {
150
+ if (el.shadowRoot) {
151
+ collectFrom(el.shadowRoot);
152
+ for (const c of el.shadowRoot.children) walk(c);
153
+ }
154
+ for (const c of el.children) walk(c);
155
+ }
156
+ if (document.documentElement) walk(document.documentElement);
157
+ return out;
158
+ }
159
+
160
+ /**
161
+ * @param {string} selector
162
+ * @returns {Element | null}
163
+ */
164
+ function querySelectorOrXPath(selector) {
165
+ const s = selector.trim();
166
+ if (s.startsWith("//") || s.toLowerCase().startsWith("xpath:")) {
167
+ const expr = s.toLowerCase().startsWith("xpath:") ? s.slice(6).trim() : s;
168
+ const all = deepXPathAll(expr);
169
+ return all[0] ?? null;
170
+ }
171
+ const all = deepQueryAll(document, s);
172
+ return all[0] ?? null;
173
+ }
174
+
175
+ /**
176
+ * @param {Element} el
177
+ */
178
+ function elementSummary(el) {
179
+ const tag = el.tagName.toLowerCase();
180
+ const id = el.id || undefined;
181
+ const classes = typeof el.className === "string" ? el.className : "";
182
+ let text = "";
183
+ if (el instanceof HTMLInputElement || el instanceof HTMLTextAreaElement) {
184
+ text = el.value?.slice(0, 200) ?? "";
185
+ } else {
186
+ text = (el.textContent || "").trim().slice(0, 200);
187
+ }
188
+ return { tag, id, classes, text };
189
+ }
190
+
191
+ /**
192
+ * Viewport client coordinates used as the synthetic click anchor (same as syntheticClick).
193
+ * @param {Element} el
194
+ */
195
+ function getSyntheticClickClientPoint(el) {
196
+ const r = el.getBoundingClientRect();
197
+ return {
198
+ x: r.left + Math.min(r.width / 2, 50),
199
+ y: r.top + Math.min(r.height / 2, 50),
200
+ };
201
+ }
202
+
203
+ /**
204
+ * @param {Element} el
205
+ */
206
+ function syntheticClick(el) {
207
+ const { x, y } = getSyntheticClickClientPoint(el);
208
+ const init = { bubbles: true, cancelable: true, view: window, clientX: x, clientY: y };
209
+ el.dispatchEvent(new MouseEvent("mousedown", init));
210
+ el.dispatchEvent(new MouseEvent("mouseup", init));
211
+ if (typeof el.click === "function") el.click();
212
+ else el.dispatchEvent(new MouseEvent("click", init));
213
+ }
214
+
215
+ /**
216
+ * @param {unknown} message
217
+ * @param {(r: unknown) => void} sendResponse
218
+ */
219
+ function handleClickElement(message, sendResponse) {
220
+ const m = /** @type {{ selector?: string }} */ (message);
221
+ const selector = typeof m.selector === "string" ? m.selector : "";
222
+ if (!selector) {
223
+ sendResponse({ success: false, error: "Missing selector" });
224
+ return;
225
+ }
226
+ const el = querySelectorOrXPath(selector);
227
+ if (!el) {
228
+ sendResponse({ success: false, error: "Element not found" });
229
+ return;
230
+ }
231
+ try {
232
+ syntheticClick(el);
233
+ sendResponse({ success: true, element: elementSummary(el) });
234
+ } catch (err) {
235
+ sendResponse({ success: false, error: String(err) });
236
+ }
237
+ }
238
+
239
+ /**
240
+ * @param {unknown} message
241
+ * @param {(r: unknown) => void} sendResponse
242
+ */
243
+ function handleResolveClickPoint(message, sendResponse) {
244
+ const m = /** @type {{ selector?: string }} */ (message);
245
+ const selector = typeof m.selector === "string" ? m.selector : "";
246
+ if (!selector) {
247
+ sendResponse({ success: false, error: "Missing selector" });
248
+ return;
249
+ }
250
+ const el = querySelectorOrXPath(selector);
251
+ if (!el) {
252
+ sendResponse({ success: false, error: "Element not found" });
253
+ return;
254
+ }
255
+ const { x, y } = getSyntheticClickClientPoint(el);
256
+ sendResponse({ success: true, x, y });
257
+ }
258
+
259
+ /**
260
+ * React / Draft.js-style editors listen for `beforeinput` + `input` (InputEvent) rather than only
261
+ * mutating textContent. Mirror native insertion order: beforeinput → DOM update → input → change.
262
+ * Helps placeholder clearing and submit affordances on Draft.js surfaces (e.g. X.com, LinkedIn).
263
+ * @param {HTMLElement} el
264
+ * @param {string} text
265
+ * @param {boolean} shouldClear
266
+ */
267
+ function insertTextIntoContentEditable(el, text, shouldClear) {
268
+ /** `focus()` can drop a programmatic selection; preserve caret for insert-at-cursor. */
269
+ let savedRange = null;
270
+ if (!shouldClear) {
271
+ const pre = window.getSelection();
272
+ if (pre && pre.rangeCount > 0) {
273
+ const r = pre.getRangeAt(0);
274
+ if (el.contains(r.commonAncestorContainer)) {
275
+ savedRange = r.cloneRange();
276
+ }
277
+ }
278
+ }
279
+
280
+ el.focus();
281
+ const sel = window.getSelection();
282
+ if (shouldClear) {
283
+ if (sel) {
284
+ const range = document.createRange();
285
+ range.selectNodeContents(el);
286
+ sel.removeAllRanges();
287
+ sel.addRange(range);
288
+ }
289
+ document.execCommand("delete");
290
+ } else if (savedRange && sel) {
291
+ sel.removeAllRanges();
292
+ sel.addRange(savedRange);
293
+ }
294
+
295
+ const evInit = /** @type {InputEventInit} */ ({
296
+ bubbles: true,
297
+ composed: true,
298
+ inputType: "insertText",
299
+ data: text,
300
+ });
301
+ el.dispatchEvent(new InputEvent("beforeinput", { ...evInit, cancelable: true }));
302
+
303
+ if (shouldClear) {
304
+ el.textContent = text;
305
+ } else if (sel && sel.rangeCount > 0) {
306
+ const range = sel.getRangeAt(0);
307
+ if (el.contains(range.commonAncestorContainer)) {
308
+ const cc = range.commonAncestorContainer;
309
+ if (cc.nodeType === 3) {
310
+ const node = /** @type {Text} */ (cc);
311
+ const offset = range.startOffset;
312
+ const t = node.textContent ?? "";
313
+ const before = t.slice(0, offset);
314
+ const after = t.slice(offset);
315
+ node.textContent = before + text + after;
316
+ range.setStart(node, before.length + text.length);
317
+ range.collapse(true);
318
+ sel.removeAllRanges();
319
+ sel.addRange(range);
320
+ } else {
321
+ range.deleteContents();
322
+ const tn = document.createTextNode(text);
323
+ range.insertNode(tn);
324
+ range.setStartAfter(tn);
325
+ range.collapse(true);
326
+ sel.removeAllRanges();
327
+ sel.addRange(range);
328
+ }
329
+ } else {
330
+ el.textContent = (el.textContent || "") + text;
331
+ }
332
+ } else {
333
+ el.textContent = (el.textContent || "") + text;
334
+ }
335
+
336
+ el.dispatchEvent(new InputEvent("input", { ...evInit, cancelable: false }));
337
+ el.dispatchEvent(new Event("change", { bubbles: true }));
338
+ }
339
+
340
+ /**
341
+ * Same InputEvent ordering for `<input>` / `<textarea>` (React-controlled fields).
342
+ * @param {HTMLInputElement | HTMLTextAreaElement} input
343
+ * @param {string} text
344
+ * @param {boolean} shouldClear
345
+ */
346
+ function insertTextIntoFormControl(input, text, shouldClear) {
347
+ let start = 0;
348
+ let end = input.value.length;
349
+ if (!shouldClear) {
350
+ start = typeof input.selectionStart === "number" ? input.selectionStart : input.value.length;
351
+ end = typeof input.selectionEnd === "number" ? input.selectionEnd : input.value.length;
352
+ }
353
+
354
+ input.focus();
355
+ if (shouldClear) {
356
+ input.select();
357
+ document.execCommand("delete");
358
+ }
359
+
360
+ const evInit = /** @type {InputEventInit} */ ({
361
+ bubbles: true,
362
+ composed: true,
363
+ inputType: "insertText",
364
+ data: text,
365
+ });
366
+ input.dispatchEvent(new InputEvent("beforeinput", { ...evInit, cancelable: true }));
367
+
368
+ if (shouldClear) {
369
+ input.value = text;
370
+ } else {
371
+ const v = input.value;
372
+ input.value = v.slice(0, start) + text + v.slice(end);
373
+ const pos = start + text.length;
374
+ if (typeof input.setSelectionRange === "function") {
375
+ input.setSelectionRange(pos, pos);
376
+ }
377
+ }
378
+
379
+ input.dispatchEvent(new InputEvent("input", { ...evInit, cancelable: false }));
380
+ input.dispatchEvent(new Event("change", { bubbles: true }));
381
+ }
382
+
383
+ /**
384
+ * @param {unknown} message
385
+ * @param {(r: unknown) => void} sendResponse
386
+ */
387
+ function handleTypeText(message, sendResponse) {
388
+ const m = /** @type {{ text?: string; selector?: string; clear?: boolean }} */ (message);
389
+ const text = typeof m.text === "string" ? m.text : "";
390
+ const shouldClear = m.clear !== false;
391
+ let el = null;
392
+ if (typeof m.selector === "string" && m.selector.trim()) {
393
+ el = querySelectorOrXPath(m.selector);
394
+ } else {
395
+ const a = document.activeElement;
396
+ el = a instanceof Element ? a : null;
397
+ }
398
+ if (!el || !(el instanceof HTMLElement)) {
399
+ sendResponse({ success: false, charsTyped: 0 });
400
+ return;
401
+ }
402
+
403
+ try {
404
+ if (el.isContentEditable) {
405
+ insertTextIntoContentEditable(el, text, shouldClear);
406
+ sendResponse({ success: true, charsTyped: text.length });
407
+ return;
408
+ }
409
+
410
+ const tag = el.tagName.toLowerCase();
411
+ if (tag === "input" || tag === "textarea") {
412
+ insertTextIntoFormControl(/** @type {HTMLInputElement | HTMLTextAreaElement} */ (el), text, shouldClear);
413
+ sendResponse({ success: true, charsTyped: text.length });
414
+ return;
415
+ }
416
+
417
+ sendResponse({ success: false, charsTyped: 0 });
418
+ } catch (err) {
419
+ sendResponse({ success: false, charsTyped: 0, error: String(err) });
420
+ }
421
+ }
422
+
423
+ /**
424
+ * @param {unknown} message
425
+ * @param {(r: unknown) => void} sendResponse
426
+ */
427
+ function handleScrollWindow(message, sendResponse) {
428
+ const m = /** @type {{ payload?: Record<string, unknown> }} */ (message);
429
+ const p = m.payload && typeof m.payload === "object" ? m.payload : {};
430
+ const behavior = p.behavior === "smooth" ? "smooth" : "auto";
431
+ const selector = typeof p.selector === "string" ? p.selector.trim() : "";
432
+ const dirRaw = typeof p.direction === "string" ? p.direction.toLowerCase() : "";
433
+ const dir =
434
+ dirRaw === "up" || dirRaw === "down" || dirRaw === "left" || dirRaw === "right" ? dirRaw : "";
435
+
436
+ try {
437
+ if (selector) {
438
+ const el = querySelectorOrXPath(selector);
439
+ if (!el) {
440
+ sendResponse({ success: false, scrollX: window.scrollX, scrollY: window.scrollY, error: "Element not found" });
441
+ return;
442
+ }
443
+ el.scrollIntoView({ behavior, block: "center", inline: "nearest" });
444
+ } else if (typeof p.x === "number" || typeof p.y === "number") {
445
+ const left = typeof p.x === "number" ? p.x : window.scrollX;
446
+ const top = typeof p.y === "number" ? p.y : window.scrollY;
447
+ window.scrollTo({ left, top, behavior });
448
+ } else {
449
+ let dx = typeof p.deltaX === "number" && Number.isFinite(p.deltaX) ? p.deltaX : 0;
450
+ let dy = typeof p.deltaY === "number" && Number.isFinite(p.deltaY) ? p.deltaY : 0;
451
+ if (dir) {
452
+ let amt = typeof p.amount === "number" && Number.isFinite(p.amount) ? Math.abs(p.amount) : NaN;
453
+ if (!Number.isFinite(amt) || amt === 0) {
454
+ if (dir === "up" || dir === "down") {
455
+ const fromDelta = typeof p.deltaY === "number" && Number.isFinite(p.deltaY) && p.deltaY !== 0;
456
+ amt = fromDelta ? Math.abs(p.deltaY) : Math.max(200, Math.floor(window.innerHeight * 0.85));
457
+ } else {
458
+ const fromDelta = typeof p.deltaX === "number" && Number.isFinite(p.deltaX) && p.deltaX !== 0;
459
+ amt = fromDelta ? Math.abs(p.deltaX) : Math.max(200, Math.floor(window.innerWidth * 0.85));
460
+ }
461
+ }
462
+ dx = dir === "left" ? -amt : dir === "right" ? amt : 0;
463
+ dy = dir === "up" ? -amt : dir === "down" ? amt : 0;
464
+ }
465
+ window.scrollBy({ left: dx, top: dy, behavior });
466
+ }
467
+ sendResponse({ success: true, scrollX: window.scrollX, scrollY: window.scrollY });
468
+ } catch (err) {
469
+ sendResponse({ success: false, scrollX: window.scrollX, scrollY: window.scrollY, error: String(err) });
470
+ }
471
+ }
472
+
473
+ /**
474
+ * @param {unknown} message
475
+ * @param {(r: unknown) => void} sendResponse
476
+ */
477
+ function handleEval(message, sendResponse) {
478
+ const m = /** @type {{ requestId?: string; code?: string; timeoutMs?: number }} */ (message);
479
+ const requestId = m.requestId || `poke-${Date.now()}-${Math.random().toString(16).slice(2)}`;
480
+ const code = String(m.code ?? "");
481
+ let finished = false;
482
+ const timeoutMs = typeof m.timeoutMs === "number" ? m.timeoutMs : 30000;
483
+
484
+ const timer = setTimeout(() => {
485
+ if (finished) return;
486
+ finished = true;
487
+ window.removeEventListener("message", onWindowMessage);
488
+ sendResponse({ ok: false, error: "evaluate_js timed out in content script" });
489
+ }, timeoutMs);
490
+
491
+ /**
492
+ * @param {MessageEvent} event
493
+ */
494
+ function onWindowMessage(event) {
495
+ if (event.source !== window) return;
496
+ const data = event.data;
497
+ if (!data || data.type !== "POKE_EVAL_RESULT" || data.requestId !== requestId) return;
498
+ if (finished) return;
499
+ finished = true;
500
+ clearTimeout(timer);
501
+ window.removeEventListener("message", onWindowMessage);
502
+ if (data.ok) {
503
+ sendResponse({ ok: true, result: data.result });
504
+ } else {
505
+ sendResponse({ ok: false, error: data.error || "evaluate failed" });
506
+ }
507
+ }
508
+
509
+ window.addEventListener("message", onWindowMessage);
510
+
511
+ const s = document.createElement("script");
512
+ s.textContent = `
513
+ (function () {
514
+ var requestId = ${JSON.stringify(requestId)};
515
+ try {
516
+ var result = (0, eval)(${JSON.stringify(code)});
517
+ window.postMessage({ type: "POKE_EVAL_RESULT", requestId: requestId, ok: true, result: result }, "*");
518
+ } catch (e) {
519
+ window.postMessage({ type: "POKE_EVAL_RESULT", requestId: requestId, ok: false, error: String(e) }, "*");
520
+ }
521
+ })();
522
+ `;
523
+ (document.documentElement || document.head || document.body).appendChild(s);
524
+ s.remove();
525
+ }
526
+
527
+ // --- Perception: shared helpers -------------------------------------------------
528
+
529
+ /**
530
+ * @param {Record<string, unknown>} obj
531
+ */
532
+ function compactJson(obj) {
533
+ return JSON.parse(JSON.stringify(obj));
534
+ }
535
+
536
+ /**
537
+ * @param {Element} el
538
+ */
539
+ function cssEscapeId(id) {
540
+ if (typeof CSS !== "undefined" && CSS.escape) return CSS.escape(id);
541
+ return id.replace(/([^\w-])/g, "\\$1");
542
+ }
543
+
544
+ /**
545
+ * @param {Element} el
546
+ */
547
+ function uniqueSelector(el) {
548
+ if (!(el instanceof Element)) return "";
549
+ if (el.id && deepQueryAll(document, `#${cssEscapeId(el.id)}`).length === 1) {
550
+ return `#${cssEscapeId(el.id)}`;
551
+ }
552
+ const parts = [];
553
+ let cur = el;
554
+ while (cur && cur.nodeType === Node.ELEMENT_NODE && cur !== document.documentElement) {
555
+ let part = cur.tagName.toLowerCase();
556
+ if (cur.id) {
557
+ parts.unshift(`#${cssEscapeId(cur.id)}`);
558
+ break;
559
+ }
560
+ const parent = cur.parentElement;
561
+ if (parent) {
562
+ const siblings = Array.from(parent.children).filter((c) => c.tagName === cur.tagName);
563
+ const idx = siblings.indexOf(cur) + 1;
564
+ if (siblings.length > 1) part += `:nth-of-type(${idx})`;
565
+ }
566
+ parts.unshift(part);
567
+ cur = /** @type {Element} */ (parent);
568
+ }
569
+ return parts.join(" > ");
570
+ }
571
+
572
+ /**
573
+ * @param {Element} el
574
+ */
575
+ function elementInteractive(el) {
576
+ if (!(el instanceof Element)) return false;
577
+ const tag = el.tagName.toLowerCase();
578
+ if (["a", "button", "input", "select", "textarea", "summary", "option", "label"].includes(tag)) {
579
+ return true;
580
+ }
581
+ const role = el.getAttribute("role");
582
+ if (
583
+ role &&
584
+ ["button", "link", "menuitem", "tab", "checkbox", "radio", "switch", "textbox", "searchbox", "combobox", "slider", "spinbutton"].includes(
585
+ role
586
+ )
587
+ ) {
588
+ return true;
589
+ }
590
+ if (el.hasAttribute("onclick")) return true;
591
+ if (el instanceof HTMLElement && el.isContentEditable) return true;
592
+ const tab = el.getAttribute("tabindex");
593
+ if (tab !== null && tab !== "-1" && !Number.isNaN(Number.parseInt(tab, 10))) return true;
594
+ return false;
595
+ }
596
+
597
+ /**
598
+ * @param {Element} el
599
+ * @param {boolean} includeHidden
600
+ */
601
+ function isSkippedHidden(el, includeHidden) {
602
+ if (includeHidden) return false;
603
+ if (!(el instanceof HTMLElement)) return true;
604
+ if (el === document.body || el === document.documentElement) return false;
605
+ const st = window.getComputedStyle(el);
606
+ if (st.display === "none" || st.visibility === "hidden") return true;
607
+ if (el.offsetParent === null) {
608
+ const pos = st.position;
609
+ if (pos !== "fixed" && pos !== "sticky") return true;
610
+ }
611
+ return false;
612
+ }
613
+
614
+ /**
615
+ * @param {Element} el
616
+ * @param {number} maxLen
617
+ */
618
+ function trimText(el, maxLen) {
619
+ let t = (el.textContent || "").trim().replace(/\s+/g, " ");
620
+ if (t.length > maxLen) t = t.slice(0, maxLen);
621
+ return t;
622
+ }
623
+
624
+ /**
625
+ * @param {Element} el
626
+ * @param {number} depth
627
+ * @param {number} maxDepth
628
+ * @param {boolean} includeHidden
629
+ * @param {boolean} [inShadow]
630
+ */
631
+ function buildDomSnapshotNode(el, depth, maxDepth, includeHidden, inShadow) {
632
+ if (depth > maxDepth) return null;
633
+ if (isSkippedHidden(el, includeHidden)) return null;
634
+ const r = el.getBoundingClientRect();
635
+ /** @type {Record<string, unknown>} */
636
+ const node = {
637
+ tag: el.tagName.toLowerCase(),
638
+ rect: { x: r.x, y: r.y, width: r.width, height: r.height },
639
+ interactive: elementInteractive(el),
640
+ };
641
+ if (inShadow) node.isShadow = true;
642
+ if (el.id) node.id = el.id;
643
+ const cls =
644
+ typeof el.className === "string" && el.className.trim()
645
+ ? el.className.trim().split(/\s+/).filter(Boolean)
646
+ : [];
647
+ if (cls.length) node.classes = cls;
648
+ const role = el.getAttribute("role");
649
+ if (role) node.role = role;
650
+ const al = el.getAttribute("aria-label");
651
+ if (al) node["aria-label"] = al;
652
+ const tx = trimText(el, 120);
653
+ if (tx) node.text = tx;
654
+ const childEls = Array.from(el.children);
655
+ const children = [];
656
+ for (const c of childEls) {
657
+ const sn = buildDomSnapshotNode(c, depth + 1, maxDepth, includeHidden, inShadow);
658
+ if (sn) children.push(sn);
659
+ }
660
+ if (el.shadowRoot) {
661
+ for (const c of Array.from(el.shadowRoot.children)) {
662
+ const sn = buildDomSnapshotNode(c, depth + 1, maxDepth, includeHidden, true);
663
+ if (sn) children.push(sn);
664
+ }
665
+ }
666
+ if (children.length) node.children = children;
667
+ return node;
668
+ }
669
+
670
+ /**
671
+ * @param {unknown} message
672
+ * @param {(r: unknown) => void} sendResponse
673
+ */
674
+ function handleGetDomSnapshot(message, sendResponse) {
675
+ const m = /** @type {{ includeHidden?: boolean; maxDepth?: number }} */ (message);
676
+ const includeHidden = m.includeHidden === true;
677
+ const maxDepth = typeof m.maxDepth === "number" && Number.isFinite(m.maxDepth) ? Math.max(0, Math.min(50, m.maxDepth)) : 6;
678
+ if (!document.body) {
679
+ sendResponse({ error: "No document.body" });
680
+ return;
681
+ }
682
+ const snapshot = buildDomSnapshotNode(document.body, 0, maxDepth, includeHidden);
683
+ sendResponse(
684
+ compactJson({
685
+ snapshot,
686
+ url: location.href,
687
+ title: document.title || "",
688
+ timestamp: Date.now(),
689
+ })
690
+ );
691
+ }
692
+
693
+ /**
694
+ * @param {Element} el
695
+ */
696
+ function impliedRole(el) {
697
+ const r = el.getAttribute("role");
698
+ if (r) return r;
699
+ const t = el.tagName.toLowerCase();
700
+ if (t === "a") return "link";
701
+ if (t === "button") return "button";
702
+ if (t === "select") return "combobox";
703
+ if (t === "textarea") return "textbox";
704
+ if (t === "img") return "img";
705
+ if (t === "form") return "form";
706
+ if (t === "input") {
707
+ const type = (/** @type {HTMLInputElement} */ (el)).type || "text";
708
+ if (type === "checkbox") return "checkbox";
709
+ if (type === "radio") return "radio";
710
+ if (type === "button" || type === "submit" || type === "reset") return "button";
711
+ return "textbox";
712
+ }
713
+ if (/^h[1-6]$/.test(t)) return "heading";
714
+ if (t === "p") return "paragraph";
715
+ if (t === "li") return "listitem";
716
+ return t;
717
+ }
718
+
719
+ /**
720
+ * @param {Element} el
721
+ */
722
+ function accessibilityName(el) {
723
+ const aria = el.getAttribute("aria-label");
724
+ if (aria && aria.trim()) return aria.trim().slice(0, 80);
725
+ if (el instanceof HTMLImageElement && el.alt) return el.alt.trim().slice(0, 80);
726
+ const title = el.getAttribute("title");
727
+ if (title && title.trim()) return title.trim().slice(0, 80);
728
+ const ph = el.getAttribute("aria-placeholder");
729
+ if (ph && ph.trim()) return ph.trim().slice(0, 80);
730
+ const it = (el.innerText || "").trim().replace(/\s+/g, " ");
731
+ return it.length > 80 ? it.slice(0, 80) : it;
732
+ }
733
+
734
+ /**
735
+ * @param {Element} el
736
+ */
737
+ function isFocusableInteractive(el) {
738
+ if (!(el instanceof HTMLElement)) return false;
739
+ if (el.hasAttribute("disabled")) return false;
740
+ if (elementInteractive(el)) {
741
+ const tab = el.getAttribute("tabindex");
742
+ if (tab === "-1" && !["A", "BUTTON", "INPUT", "SELECT", "TEXTAREA", "SUMMARY"].includes(el.tagName)) {
743
+ return false;
744
+ }
745
+ return true;
746
+ }
747
+ return false;
748
+ }
749
+
750
+ /**
751
+ * @param {unknown} message
752
+ * @param {(r: unknown) => void} sendResponse
753
+ */
754
+ function handleGetAccessibilityTree(message, sendResponse) {
755
+ const m = /** @type {{ interactiveOnly?: boolean }} */ (message);
756
+ const interactiveOnly = m.interactiveOnly === true;
757
+ const sel =
758
+ '[role], a, button, input, select, textarea, h1, h2, h3, h4, h5, h6, p, li, img, form';
759
+ const list = Array.from(document.querySelectorAll(sel));
760
+ /** @type {Array<Record<string, unknown>>} */
761
+ const raw = [];
762
+ for (const el of list) {
763
+ if (!(el instanceof Element)) continue;
764
+ if (isSkippedHidden(el, false)) continue;
765
+ if (interactiveOnly && !isFocusableInteractive(el)) continue;
766
+ const r = el.getBoundingClientRect();
767
+ const tag = el.tagName.toLowerCase();
768
+ /** @type {Record<string, unknown>} */
769
+ const row = {
770
+ role: impliedRole(el),
771
+ name: accessibilityName(el),
772
+ selector: uniqueSelector(el),
773
+ disabled: el instanceof HTMLElement && (el.hasAttribute("disabled") || /** @type {HTMLInputElement} */ (el).disabled === true),
774
+ rect: { x: r.x, y: r.y, w: r.width, h: r.height },
775
+ };
776
+ if (el.id) row.id = el.id;
777
+ if (/^h[1-6]$/.test(tag)) row.level = Number(tag[1]);
778
+ if (el instanceof HTMLInputElement || el instanceof HTMLTextAreaElement || el instanceof HTMLSelectElement) {
779
+ row.value = el.value;
780
+ if (el instanceof HTMLInputElement && (el.type === "checkbox" || el.type === "radio")) {
781
+ row.checked = el.checked;
782
+ }
783
+ }
784
+ raw.push(row);
785
+ }
786
+ raw.sort((a, b) => {
787
+ const ra = /** @type {{ x: number; y: number }} */ (a.rect);
788
+ const rb = /** @type {{ x: number; y: number }} */ (b.rect);
789
+ if (Math.abs(ra.y - rb.y) > 1) return ra.y - rb.y;
790
+ return ra.x - rb.x;
791
+ });
792
+ const nodes = raw.map((row) => compactJson({ ...row }));
793
+ sendResponse({
794
+ nodes,
795
+ count: nodes.length,
796
+ url: location.href,
797
+ });
798
+ }
799
+
800
+ /**
801
+ * @param {string} expr
802
+ * @returns {Element[]}
803
+ */
804
+ function xpathElements(expr) {
805
+ return deepXPathAll(expr);
806
+ }
807
+
808
+ /**
809
+ * @param {string} q
810
+ * @returns {Element[]}
811
+ */
812
+ function findElementsByText(q) {
813
+ const ql = q.toLowerCase().trim();
814
+ if (!ql) return [];
815
+ const all = deepQueryAll(document, "*");
816
+ /** @type {Element[]} */
817
+ const exact = [];
818
+ /** @type {Element[]} */
819
+ const partial = [];
820
+ for (const el of all) {
821
+ if (!(el instanceof HTMLElement)) continue;
822
+ const tn = el.tagName;
823
+ if (tn === "SCRIPT" || tn === "STYLE" || tn === "NOSCRIPT") continue;
824
+ const t = (el.innerText || "").trim();
825
+ if (!t) continue;
826
+ const tl = t.toLowerCase();
827
+ if (tl === ql) exact.push(el);
828
+ else if (tl.includes(ql)) partial.push(el);
829
+ }
830
+ const pool = exact.length ? exact : partial;
831
+ return filterOutAncestors(pool);
832
+ }
833
+
834
+ /**
835
+ * @param {Element[]} els
836
+ */
837
+ function filterOutAncestors(els) {
838
+ /** @type {Element[]} */
839
+ const out = [];
840
+ for (const el of els) {
841
+ let sub = false;
842
+ for (const o of els) {
843
+ if (o !== el && o.contains(el)) {
844
+ sub = true;
845
+ break;
846
+ }
847
+ }
848
+ if (!sub) out.push(el);
849
+ }
850
+ return out;
851
+ }
852
+
853
+ /**
854
+ * @param {string} q
855
+ * @returns {Element[]}
856
+ */
857
+ function findElementsByAria(q) {
858
+ const ql = q.toLowerCase().trim();
859
+ if (!ql) return [];
860
+ const all = deepQueryAll(document, "*");
861
+ /** @type {Element[]} */
862
+ const hits = [];
863
+ for (const el of all) {
864
+ if (!(el instanceof Element)) continue;
865
+ const tn = el.tagName;
866
+ if (tn === "SCRIPT" || tn === "STYLE" || tn === "NOSCRIPT") continue;
867
+ const chunks = [
868
+ el.getAttribute("aria-label"),
869
+ el.getAttribute("aria-placeholder"),
870
+ el.getAttribute("title"),
871
+ el instanceof HTMLImageElement ? el.alt : null,
872
+ ]
873
+ .filter(Boolean)
874
+ .map((s) => String(s).toLowerCase());
875
+ if (chunks.some((c) => c.includes(ql))) hits.push(el);
876
+ }
877
+ return filterOutAncestors(hits);
878
+ }
879
+
880
+ /**
881
+ * @param {Element} el
882
+ * @param {number} index
883
+ */
884
+ function toFoundElement(el, index) {
885
+ const r = el.getBoundingClientRect();
886
+ const cls =
887
+ typeof el.className === "string" && el.className.trim()
888
+ ? el.className.trim().split(/\s+/).filter(Boolean)
889
+ : [];
890
+ /** @type {Record<string, unknown>} */
891
+ const o = {
892
+ index,
893
+ tag: el.tagName.toLowerCase(),
894
+ text: (el.innerText || "").trim().slice(0, 200),
895
+ selector: uniqueSelector(el),
896
+ rect: { x: r.x, y: r.y, width: r.width, height: r.height },
897
+ interactive: elementInteractive(el),
898
+ };
899
+ if (el.id) o.id = el.id;
900
+ if (cls.length) o.classes = cls;
901
+ return compactJson(o);
902
+ }
903
+
904
+ /**
905
+ * @param {unknown} message
906
+ * @param {(r: unknown) => void} sendResponse
907
+ */
908
+ function handleFindElement(message, sendResponse) {
909
+ const m = /** @type {{ query?: string; strategy?: string }} */ (message);
910
+ const query = typeof m.query === "string" ? m.query : "";
911
+ const strategy = m.strategy === "css" || m.strategy === "text" || m.strategy === "aria" || m.strategy === "xpath" ? m.strategy : "auto";
912
+ if (!query.trim()) {
913
+ sendResponse({ elements: [], query: "", strategy_used: strategy });
914
+ return;
915
+ }
916
+
917
+ /** @type {Element[]} */
918
+ let found = [];
919
+ /** @type {string} */
920
+ let used = strategy;
921
+
922
+ function tryCss() {
923
+ try {
924
+ return deepQueryAll(document, query);
925
+ } catch {
926
+ return [];
927
+ }
928
+ }
929
+
930
+ if (strategy === "auto") {
931
+ found = tryCss();
932
+ used = "css";
933
+ if (found.length === 0) {
934
+ found = findElementsByText(query);
935
+ used = "text";
936
+ }
937
+ if (found.length === 0) {
938
+ found = findElementsByAria(query);
939
+ used = "aria";
940
+ }
941
+ } else if (strategy === "css") {
942
+ found = tryCss();
943
+ } else if (strategy === "text") {
944
+ found = findElementsByText(query);
945
+ } else if (strategy === "aria") {
946
+ found = findElementsByAria(query);
947
+ } else if (strategy === "xpath") {
948
+ found = xpathElements(query);
949
+ used = "xpath";
950
+ }
951
+
952
+ const top = found.slice(0, 5);
953
+ const elements = top.map((el, i) => toFoundElement(el, i));
954
+ sendResponse({ elements, query, strategy_used: used });
955
+ }
956
+
957
+ /**
958
+ * @returns {HTMLElement}
959
+ */
960
+ function getReadPageRoot() {
961
+ const main =
962
+ document.querySelector("main") ||
963
+ document.querySelector("article") ||
964
+ document.querySelector('[role="main"]');
965
+ if (main instanceof HTMLElement) return main;
966
+ return document.body || document.documentElement;
967
+ }
968
+
969
+ /**
970
+ * @param {HTMLElement} el
971
+ */
972
+ function stripNoise(el) {
973
+ el.querySelectorAll("script, style, noscript, nav, header, footer").forEach((n) => n.remove());
974
+ }
975
+
976
+ /**
977
+ * @param {string} text
978
+ */
979
+ function wordCountFrom(text) {
980
+ const w = text.trim().split(/\s+/).filter(Boolean);
981
+ return w.length;
982
+ }
983
+
984
+ /**
985
+ * @param {HTMLElement} root
986
+ */
987
+ function readStructured(root) {
988
+ const clone = /** @type {HTMLElement} */ (root.cloneNode(true));
989
+ stripNoise(clone);
990
+ const descMeta = document.querySelector('meta[name="description"]');
991
+ const description = descMeta?.getAttribute("content")?.trim() || "";
992
+ /** @type {{ level: number; text: string }[]} */
993
+ const headings = [];
994
+ clone.querySelectorAll("h1, h2, h3, h4, h5, h6").forEach((h) => {
995
+ const tag = h.tagName.toLowerCase();
996
+ headings.push({ level: Number(tag[1]), text: (h.textContent || "").trim() });
997
+ });
998
+ /** @type {{ text: string; href: string }[]} */
999
+ const links = [];
1000
+ clone.querySelectorAll("a[href]").forEach((a) => {
1001
+ const href = a.getAttribute("href") || "";
1002
+ links.push({ text: (a.textContent || "").trim(), href });
1003
+ });
1004
+ /** @type {{ alt: string; src: string }[]} */
1005
+ const images = [];
1006
+ clone.querySelectorAll("img[src]").forEach((img) => {
1007
+ images.push({ alt: img.getAttribute("alt") || "", src: img.getAttribute("src") || "" });
1008
+ });
1009
+ const mainText = (clone.innerText || "").trim().replace(/\s+/g, " ");
1010
+ return {
1011
+ title: document.title || "",
1012
+ url: location.href,
1013
+ description,
1014
+ mainText,
1015
+ headings,
1016
+ links,
1017
+ images,
1018
+ };
1019
+ }
1020
+
1021
+ /**
1022
+ * @param {HTMLElement} el
1023
+ * @returns {string}
1024
+ */
1025
+ function elementToMarkdown(el) {
1026
+ const tag = el.tagName.toLowerCase();
1027
+ if (["script", "style", "noscript", "nav", "header", "footer"].includes(tag)) return "";
1028
+ if (tag === "br") return "\n";
1029
+ if (el.childNodes.length === 0) return "";
1030
+
1031
+ /** @type {string[]} */
1032
+ const bits = [];
1033
+ for (const node of el.childNodes) {
1034
+ if (node.nodeType === Node.TEXT_NODE) {
1035
+ const t = node.textContent || "";
1036
+ if (t.trim()) bits.push(t);
1037
+ } else if (node.nodeType === Node.ELEMENT_NODE) {
1038
+ const child = /** @type {HTMLElement} */ (node);
1039
+ const ct = child.tagName.toLowerCase();
1040
+ if (["script", "style", "noscript", "nav", "header", "footer"].includes(ct)) continue;
1041
+ if (/^h[1-6]$/.test(ct)) {
1042
+ const level = Number(ct[1]);
1043
+ bits.push(`${"#".repeat(level)} ${(child.innerText || "").trim()}\n\n`);
1044
+ } else if (ct === "p") {
1045
+ bits.push(`${(child.innerText || "").trim()}\n\n`);
1046
+ } else if (ct === "a" && child.getAttribute("href")) {
1047
+ const href = child.getAttribute("href") || "";
1048
+ bits.push(`[${(child.textContent || "").trim()}](${href})`);
1049
+ } else if (ct === "ul") {
1050
+ for (const li of child.querySelectorAll(":scope > li")) {
1051
+ bits.push(`- ${(li.textContent || "").trim()}\n`);
1052
+ }
1053
+ bits.push("\n");
1054
+ } else if (ct === "ol") {
1055
+ let i = 1;
1056
+ for (const li of child.querySelectorAll(":scope > li")) {
1057
+ bits.push(`${i}. ${(li.textContent || "").trim()}\n`);
1058
+ i += 1;
1059
+ }
1060
+ bits.push("\n");
1061
+ } else if (ct === "pre") {
1062
+ bits.push(`\`\`\`\n${(child.textContent || "").trim()}\n\`\`\`\n\n`);
1063
+ } else if (ct === "code" && child.parentElement?.tagName.toLowerCase() !== "pre") {
1064
+ bits.push(`\`${(child.textContent || "").trim()}\``);
1065
+ } else if (ct === "strong" || ct === "b") {
1066
+ bits.push(`**${(child.textContent || "").trim()}**`);
1067
+ } else if (ct === "img" && child.getAttribute("src")) {
1068
+ const src = child.getAttribute("src") || "";
1069
+ const alt = child.getAttribute("alt") || "";
1070
+ bits.push(`![${alt}](${src})`);
1071
+ } else {
1072
+ bits.push(elementToMarkdown(child));
1073
+ }
1074
+ }
1075
+ }
1076
+ return bits.join("");
1077
+ }
1078
+
1079
+ /**
1080
+ * @param {unknown} message
1081
+ * @param {(r: unknown) => void} sendResponse
1082
+ */
1083
+ /**
1084
+ * @param {Element} el
1085
+ * @param {boolean} requireVisible
1086
+ */
1087
+ function elementMatchesVisible(el, requireVisible) {
1088
+ if (!requireVisible) return true;
1089
+ if (!(el instanceof HTMLElement)) return false;
1090
+ if (el.offsetParent === null) {
1091
+ const st = getComputedStyle(el);
1092
+ const pos = st.position;
1093
+ if (pos !== "fixed" && pos !== "sticky") return false;
1094
+ }
1095
+ const st = getComputedStyle(el);
1096
+ if (st.display === "none" || st.visibility === "hidden" || Number.parseFloat(st.opacity) === 0) {
1097
+ return false;
1098
+ }
1099
+ return true;
1100
+ }
1101
+
1102
+ /**
1103
+ * @param {unknown} message
1104
+ * @param {(r: unknown) => void} sendResponse
1105
+ */
1106
+ function handleWaitForSelector(message, sendResponse) {
1107
+ const m = /** @type {{ selector?: string; timeout?: number; visible?: boolean }} */ (message);
1108
+ const selector = typeof m.selector === "string" ? m.selector : "";
1109
+ const timeout = typeof m.timeout === "number" && m.timeout > 0 ? m.timeout : 10000;
1110
+ const visible = m.visible === true;
1111
+ const start = Date.now();
1112
+
1113
+ /** @type {ReturnType<typeof setInterval> | undefined} */
1114
+ let iv;
1115
+
1116
+ function tick() {
1117
+ const el = querySelectorOrXPath(selector);
1118
+ if (el && elementMatchesVisible(el, visible)) {
1119
+ if (iv !== undefined) clearInterval(iv);
1120
+ const r = el.getBoundingClientRect();
1121
+ sendResponse({
1122
+ found: true,
1123
+ selector,
1124
+ elapsed: Date.now() - start,
1125
+ element: {
1126
+ tag: el.tagName.toLowerCase(),
1127
+ id: el.id || undefined,
1128
+ text: (el.textContent || "").trim().slice(0, 200),
1129
+ rect: { x: r.x, y: r.y, width: r.width, height: r.height },
1130
+ },
1131
+ });
1132
+ return;
1133
+ }
1134
+ if (Date.now() - start >= timeout) {
1135
+ if (iv !== undefined) clearInterval(iv);
1136
+ sendResponse({
1137
+ found: false,
1138
+ selector,
1139
+ elapsed: Date.now() - start,
1140
+ error: "timeout",
1141
+ });
1142
+ }
1143
+ }
1144
+
1145
+ iv = setInterval(tick, 100);
1146
+ tick();
1147
+ }
1148
+
1149
+ /**
1150
+ * @param {unknown} message
1151
+ * @param {(r: unknown) => void} sendResponse
1152
+ */
1153
+ function handleGetConsoleLogs(message, sendResponse) {
1154
+ const m = /** @type {{ level?: string; limit?: number }} */ (message);
1155
+ const level = m.level === "error" || m.level === "warn" || m.level === "info" || m.level === "log" ? m.level : "all";
1156
+ const limit = typeof m.limit === "number" ? Math.min(500, Math.max(1, m.limit)) : 100;
1157
+ let logs = consoleRing;
1158
+ if (level !== "all") {
1159
+ logs = logs.filter((e) => e.level === level);
1160
+ }
1161
+ const sliced = logs.slice(-limit);
1162
+ sendResponse({ logs: sliced, count: sliced.length });
1163
+ }
1164
+
1165
+ /**
1166
+ * @param {unknown} _message
1167
+ * @param {(r: unknown) => void} sendResponse
1168
+ */
1169
+ function handleClearConsoleLogs(_message, sendResponse) {
1170
+ consoleRing = [];
1171
+ sendResponse({ cleared: true });
1172
+ }
1173
+
1174
+ /**
1175
+ * @param {unknown} message
1176
+ * @param {(r: unknown) => void} sendResponse
1177
+ */
1178
+ function handleGetPageErrors(message, sendResponse) {
1179
+ const m = /** @type {{ limit?: number }} */ (message);
1180
+ const limit = typeof m.limit === "number" ? Math.min(200, Math.max(1, m.limit)) : 50;
1181
+ const sliced = pageErrorRing.slice(-limit);
1182
+ sendResponse({ errors: sliced, count: sliced.length });
1183
+ }
1184
+
1185
+ /**
1186
+ * @param {unknown} _message
1187
+ * @param {(r: unknown) => void} sendResponse
1188
+ */
1189
+ function handleGetScrollInfo(_message, sendResponse) {
1190
+ const de = document.documentElement;
1191
+ const body = document.body;
1192
+ sendResponse({
1193
+ scrollHeight: Math.max(de.scrollHeight, body ? body.scrollHeight : 0, de.clientHeight),
1194
+ innerHeight: window.innerHeight,
1195
+ innerWidth: window.innerWidth,
1196
+ scrollY: window.scrollY,
1197
+ devicePixelRatio: window.devicePixelRatio || 1,
1198
+ });
1199
+ }
1200
+
1201
+ /**
1202
+ * @param {unknown} message
1203
+ * @param {(r: unknown) => void} sendResponse
1204
+ */
1205
+ function handleScrollTo(message, sendResponse) {
1206
+ const m = /** @type {{ y?: number }} */ (message);
1207
+ const y = typeof m.y === "number" ? m.y : 0;
1208
+ window.scrollTo({ top: y, left: 0, behavior: "instant" });
1209
+ sendResponse({ scrollY: window.scrollY });
1210
+ }
1211
+
1212
+ /**
1213
+ * @param {unknown} message
1214
+ * @param {(r: unknown) => void} sendResponse
1215
+ */
1216
+ function handleHoverElement(message, sendResponse) {
1217
+ const m = /** @type {{ selector?: string }} */ (message);
1218
+ const selector = typeof m.selector === "string" ? m.selector : "";
1219
+ if (!selector.trim()) {
1220
+ sendResponse({ success: false });
1221
+ return;
1222
+ }
1223
+ const el = querySelectorOrXPath(selector);
1224
+ if (!el) {
1225
+ sendResponse({ success: false });
1226
+ return;
1227
+ }
1228
+ const r = el.getBoundingClientRect();
1229
+ const x = r.left + r.width / 2;
1230
+ const y = r.top + r.height / 2;
1231
+ const init = { bubbles: true, cancelable: true, view: window, clientX: x, clientY: y };
1232
+ el.dispatchEvent(new MouseEvent("mousemove", init));
1233
+ el.dispatchEvent(new MouseEvent("mouseover", init));
1234
+ el.dispatchEvent(new MouseEvent("mouseenter", init));
1235
+ sendResponse({
1236
+ success: true,
1237
+ element: {
1238
+ tag: el.tagName.toLowerCase(),
1239
+ id: el.id || undefined,
1240
+ text: (el.textContent || "").trim().slice(0, 200),
1241
+ rect: { x: r.x, y: r.y, width: r.width, height: r.height },
1242
+ },
1243
+ });
1244
+ }
1245
+
1246
+ /**
1247
+ * @param {unknown} message
1248
+ * @param {(r: unknown) => void} sendResponse
1249
+ */
1250
+ function handleScriptInject(message, sendResponse) {
1251
+ const m = /** @type {{ script?: string }} */ (message);
1252
+ const script = typeof m.script === "string" ? m.script : "";
1253
+ if (!script) {
1254
+ sendResponse({ success: false });
1255
+ return;
1256
+ }
1257
+ try {
1258
+ const s = document.createElement("script");
1259
+ s.textContent = script;
1260
+ const root = document.documentElement || document.head || document.body;
1261
+ if (!root) {
1262
+ sendResponse({ success: false });
1263
+ return;
1264
+ }
1265
+ root.appendChild(s);
1266
+ s.remove();
1267
+ sendResponse({ success: true });
1268
+ } catch (err) {
1269
+ sendResponse({ success: false, error: String(err) });
1270
+ }
1271
+ }
1272
+
1273
+ /**
1274
+ * @param {unknown} message
1275
+ * @param {(r: unknown) => void} sendResponse
1276
+ */
1277
+ function handleFillForm(message, sendResponse) {
1278
+ const m = /** @type {{
1279
+ fields?: Array<{ selector?: string; value?: string; type?: string }>;
1280
+ submitAfter?: boolean;
1281
+ submitSelector?: string;
1282
+ }} */ (message);
1283
+ const fields = Array.isArray(m.fields) ? m.fields : [];
1284
+ /** @type {Array<{ selector: string; error: string }>} */
1285
+ const errors = [];
1286
+ let filled = 0;
1287
+
1288
+ for (const f of fields) {
1289
+ const sel = typeof f.selector === "string" ? f.selector : "";
1290
+ const val = typeof f.value === "string" ? f.value : "";
1291
+ const typ = f.type === "select" || f.type === "checkbox" || f.type === "radio" || f.type === "file" ? f.type : "text";
1292
+ if (!sel) {
1293
+ errors.push({ selector: sel, error: "empty selector" });
1294
+ continue;
1295
+ }
1296
+ const el = querySelectorOrXPath(sel);
1297
+ if (!el) {
1298
+ errors.push({ selector: sel, error: "not found" });
1299
+ continue;
1300
+ }
1301
+ try {
1302
+ if (typ === "file") {
1303
+ errors.push({ selector: sel, error: "file inputs are not supported" });
1304
+ continue;
1305
+ }
1306
+ if (typ === "checkbox") {
1307
+ const input = el instanceof HTMLInputElement ? el : null;
1308
+ if (!input || input.type !== "checkbox") {
1309
+ errors.push({ selector: sel, error: "not a checkbox input" });
1310
+ continue;
1311
+ }
1312
+ const vl = val.toLowerCase();
1313
+ input.checked = !(vl === "false" || val === "0" || vl === "off" || val === "");
1314
+ input.dispatchEvent(new Event("input", { bubbles: true }));
1315
+ input.dispatchEvent(new Event("change", { bubbles: true }));
1316
+ filled += 1;
1317
+ continue;
1318
+ }
1319
+ if (typ === "radio") {
1320
+ const input = el instanceof HTMLInputElement ? el : null;
1321
+ if (!input || input.type !== "radio") {
1322
+ errors.push({ selector: sel, error: "not a radio input" });
1323
+ continue;
1324
+ }
1325
+ const vl = val.toLowerCase();
1326
+ const off = val === "" || vl === "false" || val === "0" || vl === "off";
1327
+ if (off) {
1328
+ input.checked = false;
1329
+ } else {
1330
+ input.checked = true;
1331
+ if (input.form) {
1332
+ const rads = input.form.querySelectorAll('input[type="radio"]');
1333
+ rads.forEach((x) => {
1334
+ if (x instanceof HTMLInputElement && x.name === input.name && x !== input) {
1335
+ x.checked = false;
1336
+ }
1337
+ });
1338
+ }
1339
+ }
1340
+ input.dispatchEvent(new Event("input", { bubbles: true }));
1341
+ input.dispatchEvent(new Event("change", { bubbles: true }));
1342
+ filled += 1;
1343
+ continue;
1344
+ }
1345
+ if (typ === "select" && el instanceof HTMLSelectElement) {
1346
+ let matched = false;
1347
+ for (let i = 0; i < el.options.length; i += 1) {
1348
+ const o = el.options[i];
1349
+ if (o.value === val || o.text === val) {
1350
+ el.selectedIndex = i;
1351
+ matched = true;
1352
+ break;
1353
+ }
1354
+ }
1355
+ if (!matched) el.value = val;
1356
+ el.dispatchEvent(new Event("input", { bubbles: true }));
1357
+ el.dispatchEvent(new Event("change", { bubbles: true }));
1358
+ filled += 1;
1359
+ continue;
1360
+ }
1361
+ if (el instanceof HTMLInputElement || el instanceof HTMLTextAreaElement) {
1362
+ el.focus();
1363
+ el.value = val;
1364
+ el.dispatchEvent(
1365
+ new InputEvent("input", { bubbles: true, data: val, inputType: "insertReplacementText" }),
1366
+ );
1367
+ el.dispatchEvent(new Event("change", { bubbles: true }));
1368
+ filled += 1;
1369
+ continue;
1370
+ }
1371
+ errors.push({ selector: sel, error: "unsupported element for text fill" });
1372
+ } catch (err) {
1373
+ errors.push({ selector: sel, error: String(err) });
1374
+ }
1375
+ }
1376
+
1377
+ let success = errors.length === 0;
1378
+ if (m.submitAfter === true) {
1379
+ const subSel = typeof m.submitSelector === "string" ? m.submitSelector.trim() : "";
1380
+ let sub = subSel ? querySelectorOrXPath(subSel) : null;
1381
+ if (!sub && fields[0]) {
1382
+ const firstSel = typeof fields[0].selector === "string" ? fields[0].selector : "";
1383
+ const first = firstSel ? querySelectorOrXPath(firstSel) : null;
1384
+ const form = first && first.closest ? first.closest("form") : null;
1385
+ if (form) {
1386
+ sub =
1387
+ form.querySelector('button[type="submit"], input[type="submit"], button:not([type])');
1388
+ }
1389
+ }
1390
+ if (sub) {
1391
+ syntheticClick(sub);
1392
+ } else {
1393
+ errors.push({ selector: "[submit]", error: "no submit control found" });
1394
+ success = false;
1395
+ }
1396
+ }
1397
+
1398
+ sendResponse({ success, filled, errors });
1399
+ }
1400
+
1401
+ /**
1402
+ * @param {unknown} message
1403
+ * @param {(r: unknown) => void} sendResponse
1404
+ */
1405
+ function handleGetStoragePage(message, sendResponse) {
1406
+ const m = /** @type {{ storageType?: string; key?: string }} */ (message);
1407
+ const useSession = m.storageType === "session";
1408
+ const t = useSession ? sessionStorage : localStorage;
1409
+ const key = typeof m.key === "string" ? m.key : undefined;
1410
+ /** @type {Record<string, string>} */
1411
+ const data = {};
1412
+ try {
1413
+ if (key) {
1414
+ const v = t.getItem(key);
1415
+ if (v !== null) data[key] = v;
1416
+ } else {
1417
+ for (let i = 0; i < t.length; i += 1) {
1418
+ const k = t.key(i);
1419
+ if (k) data[k] = t.getItem(k) ?? "";
1420
+ }
1421
+ }
1422
+ sendResponse({ data, count: Object.keys(data).length });
1423
+ } catch (err) {
1424
+ sendResponse({ data: {}, count: 0, error: String(err) });
1425
+ }
1426
+ }
1427
+
1428
+ /**
1429
+ * @param {unknown} message
1430
+ * @param {(r: unknown) => void} sendResponse
1431
+ */
1432
+ function handleSetStoragePage(message, sendResponse) {
1433
+ const m = /** @type {{ storageType?: string; key?: string; value?: string }} */ (message);
1434
+ const useSession = m.storageType === "session";
1435
+ const t = useSession ? sessionStorage : localStorage;
1436
+ const key = typeof m.key === "string" ? m.key : "";
1437
+ const value = typeof m.value === "string" ? m.value : "";
1438
+ if (!key) {
1439
+ sendResponse({ success: false });
1440
+ return;
1441
+ }
1442
+ try {
1443
+ t.setItem(key, value);
1444
+ sendResponse({ success: true });
1445
+ } catch (err) {
1446
+ sendResponse({ success: false, error: String(err) });
1447
+ }
1448
+ }
1449
+
1450
+ function handleReadPage(message, sendResponse) {
1451
+ const m = /** @type {{ format?: string }} */ (message);
1452
+ const format =
1453
+ m.format === "markdown" || m.format === "text" || m.format === "structured" ? m.format : "structured";
1454
+ const root = getReadPageRoot();
1455
+ const title = document.title || "";
1456
+ const url = location.href;
1457
+
1458
+ if (format === "text") {
1459
+ const clone = /** @type {HTMLElement} */ (root.cloneNode(true));
1460
+ stripNoise(clone);
1461
+ const text = (clone.innerText || "").trim().replace(/\s+/g, " ");
1462
+ sendResponse({ text, url, title, wordCount: wordCountFrom(text) });
1463
+ return;
1464
+ }
1465
+
1466
+ if (format === "markdown") {
1467
+ const clone = /** @type {HTMLElement} */ (root.cloneNode(true));
1468
+ stripNoise(clone);
1469
+ const markdown = elementToMarkdown(clone).trim();
1470
+ sendResponse({ markdown, url, title, wordCount: wordCountFrom(markdown) });
1471
+ return;
1472
+ }
1473
+
1474
+ const structured = readStructured(root);
1475
+ const wc = wordCountFrom(structured.mainText);
1476
+ sendResponse({ ...structured, wordCount: wc });
1477
+ }
1478
+
1479
+ /** @type {Record<string, (message: unknown, sendResponse: (r: unknown) => void) => void>} */
1480
+ const MESSAGE_HANDLERS = {
1481
+ POKE_CLICK_ELEMENT: handleClickElement,
1482
+ POKE_RESOLVE_CLICK_POINT: handleResolveClickPoint,
1483
+ POKE_TYPE_TEXT: handleTypeText,
1484
+ POKE_SCROLL_WINDOW: handleScrollWindow,
1485
+ POKE_EVAL: handleEval,
1486
+ POKE_GET_DOM_SNAPSHOT: handleGetDomSnapshot,
1487
+ POKE_GET_A11Y_TREE: handleGetAccessibilityTree,
1488
+ POKE_FIND_ELEMENT: handleFindElement,
1489
+ POKE_READ_PAGE: handleReadPage,
1490
+ POKE_WAIT_FOR_SELECTOR: handleWaitForSelector,
1491
+ POKE_GET_CONSOLE_LOGS: handleGetConsoleLogs,
1492
+ POKE_CLEAR_CONSOLE_LOGS: handleClearConsoleLogs,
1493
+ POKE_GET_PAGE_ERRORS: handleGetPageErrors,
1494
+ POKE_GET_SCROLL_INFO: handleGetScrollInfo,
1495
+ POKE_SCROLL_TO: handleScrollTo,
1496
+ POKE_HOVER_ELEMENT: handleHoverElement,
1497
+ POKE_SCRIPT_INJECT: handleScriptInject,
1498
+ POKE_FILL_FORM: handleFillForm,
1499
+ POKE_GET_STORAGE: handleGetStoragePage,
1500
+ POKE_SET_STORAGE: handleSetStoragePage,
1501
+ };
1502
+
1503
+ chrome.runtime.onMessage.addListener((message, _sender, sendResponse) => {
1504
+ const t = message && typeof message === "object" && "type" in message ? String(message.type) : "";
1505
+ const fn = MESSAGE_HANDLERS[t];
1506
+ if (!fn) return undefined;
1507
+ queueMicrotask(() => {
1508
+ try {
1509
+ fn(message, sendResponse);
1510
+ } catch (err) {
1511
+ sendResponse({ success: false, error: String(err), ok: false });
1512
+ }
1513
+ });
1514
+ return true;
1515
+ });