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.
- package/extension/background.js +2553 -1
- package/extension/content.js +1515 -1
- package/extension/manifest.json +1 -1
- package/package.json +1 -1
package/extension/content.js
CHANGED
|
@@ -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(``);
|
|
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
|
+
});
|