Exemplo n.º 1
0
function echoPage()
{
    $automation = AKAutomation::getInstance();
    ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Akeeba Kickstart 3.3.2</title>
<style type="text/css" media="all" rel="stylesheet">
<?php 
    echoCSS();
    ?>
</style>
<?php 
    if (function_exists('base64_decode')) {
        ?>
<script type="text/javascript">
<?php 
        $data = <<<ENDDATA
KGZ1bmN0aW9uKEEsdyl7ZnVuY3Rpb24gbWEoKXtpZighYy5pc1JlYWR5KXt0cnl7cy5kb2N1bWVudEVsZW1lbnQuZG9TY3JvbGwoImxlZnQiKX1jYXRjaChhKXtzZXRUaW1lb3V0KG1hLDEpO3JldHVybn1jLnJlYWR5KCl9fWZ1bmN0aW9uIFFhKGEsYil7Yi5zcmM/Yy5hamF4KHt1cmw6Yi5zcmMsYXN5bmM6ZmFsc2UsZGF0YVR5cGU6InNjcmlwdCJ9KTpjLmdsb2JhbEV2YWwoYi50ZXh0fHxiLnRleHRDb250ZW50fHxiLmlubmVySFRNTHx8IiIpO2IucGFyZW50Tm9kZSYmYi5wYXJlbnROb2RlLnJlbW92ZUNoaWxkKGIpfWZ1bmN0aW9uIFgoYSxiLGQsZixlLGope3ZhciBpPWEubGVuZ3RoO2lmKHR5cGVvZiBiPT09Im9iamVjdCIpe2Zvcih2YXIgbyBpbiBiKVgoYSxvLGJbb10sZixlLGQpO3JldHVybiBhfWlmKGQhPT13KXtmPSFqJiZmJiZjLmlzRnVuY3Rpb24oZCk7Zm9yKG89MDtvPGk7bysrKWUoYVtvXSxiLGY/ZC5jYWxsKGFbb10sbyxlKGFbb10sYikpOmQsaik7cmV0dXJuIGF9cmV0dXJuIGk/DQplKGFbMF0sYik6d31mdW5jdGlvbiBKKCl7cmV0dXJuKG5ldyBEYXRlKS5nZXRUaW1lKCl9ZnVuY3Rpb24gWSgpe3JldHVybiBmYWxzZX1mdW5jdGlvbiBaKCl7cmV0dXJuIHRydWV9ZnVuY3Rpb24gbmEoYSxiLGQpe2RbMF0udHlwZT1hO3JldHVybiBjLmV2ZW50LmhhbmRsZS5hcHBseShiLGQpfWZ1bmN0aW9uIG9hKGEpe3ZhciBiLGQ9W10sZj1bXSxlPWFyZ3VtZW50cyxqLGksbyxrLG4scjtpPWMuZGF0YSh0aGlzLCJldmVudHMiKTtpZighKGEubGl2ZUZpcmVkPT09dGhpc3x8IWl8fCFpLmxpdmV8fGEuYnV0dG9uJiZhLnR5cGU9PT0iY2xpY2siKSl7YS5saXZlRmlyZWQ9dGhpczt2YXIgdT1pLmxpdmUuc2xpY2UoMCk7Zm9yKGs9MDtrPHUubGVuZ3RoO2srKyl7aT11W2tdO2kub3JpZ1R5cGUucmVwbGFjZShPLCIiKT09PWEudHlwZT9mLnB1c2goaS5zZWxlY3Rvcik6dS5zcGxpY2Uoay0tLDEpfWo9YyhhLnRhcmdldCkuY2xvc2VzdChmLGEuY3VycmVudFRhcmdldCk7bj0wO2ZvcihyPQ0Kai5sZW5ndGg7bjxyO24rKylmb3Ioaz0wO2s8dS5sZW5ndGg7aysrKXtpPXVba107aWYoaltuXS5zZWxlY3Rvcj09PWkuc2VsZWN0b3Ipe289altuXS5lbGVtO2Y9bnVsbDtpZihpLnByZVR5cGU9PT0ibW91c2VlbnRlciJ8fGkucHJlVHlwZT09PSJtb3VzZWxlYXZlIilmPWMoYS5yZWxhdGVkVGFyZ2V0KS5jbG9zZXN0KGkuc2VsZWN0b3IpWzBdO2lmKCFmfHxmIT09bylkLnB1c2goe2VsZW06byxoYW5kbGVPYmo6aX0pfX1uPTA7Zm9yKHI9ZC5sZW5ndGg7bjxyO24rKyl7aj1kW25dO2EuY3VycmVudFRhcmdldD1qLmVsZW07YS5kYXRhPWouaGFuZGxlT2JqLmRhdGE7YS5oYW5kbGVPYmo9ai5oYW5kbGVPYmo7aWYoai5oYW5kbGVPYmoub3JpZ0hhbmRsZXIuYXBwbHkoai5lbGVtLGUpPT09ZmFsc2Upe2I9ZmFsc2U7YnJlYWt9fXJldHVybiBifX1mdW5jdGlvbiBwYShhLGIpe3JldHVybiJsaXZlLiIrKGEmJmEhPT0iKiI/YSsiLiI6IiIpK2IucmVwbGFjZSgvXC4vZywiYCIpLnJlcGxhY2UoLyAvZywNCiImIil9ZnVuY3Rpb24gcWEoYSl7cmV0dXJuIWF8fCFhLnBhcmVudE5vZGV8fGEucGFyZW50Tm9kZS5ub2RlVHlwZT09PTExfWZ1bmN0aW9uIHJhKGEsYil7dmFyIGQ9MDtiLmVhY2goZnVuY3Rpb24oKXtpZih0aGlzLm5vZGVOYW1lPT09KGFbZF0mJmFbZF0ubm9kZU5hbWUpKXt2YXIgZj1jLmRhdGEoYVtkKytdKSxlPWMuZGF0YSh0aGlzLGYpO2lmKGY9ZiYmZi5ldmVudHMpe2RlbGV0ZSBlLmhhbmRsZTtlLmV2ZW50cz17fTtmb3IodmFyIGogaW4gZilmb3IodmFyIGkgaW4gZltqXSljLmV2ZW50LmFkZCh0aGlzLGosZltqXVtpXSxmW2pdW2ldLmRhdGEpfX19KX1mdW5jdGlvbiBzYShhLGIsZCl7dmFyIGYsZSxqO2I9YiYmYlswXT9iWzBdLm93bmVyRG9jdW1lbnR8fGJbMF06cztpZihhLmxlbmd0aD09PTEmJnR5cGVvZiBhWzBdPT09InN0cmluZyImJmFbMF0ubGVuZ3RoPDUxMiYmYj09PXMmJiF0YS50ZXN0KGFbMF0pJiYoYy5zdXBwb3J0LmNoZWNrQ2xvbmV8fCF1YS50ZXN0KGFbMF0pKSl7ZT0NCnRydWU7aWYoaj1jLmZyYWdtZW50c1thWzBdXSlpZihqIT09MSlmPWp9aWYoIWYpe2Y9Yi5jcmVhdGVEb2N1bWVudEZyYWdtZW50KCk7Yy5jbGVhbihhLGIsZixkKX1pZihlKWMuZnJhZ21lbnRzW2FbMF1dPWo/ZjoxO3JldHVybntmcmFnbWVudDpmLGNhY2hlYWJsZTplfX1mdW5jdGlvbiBLKGEsYil7dmFyIGQ9e307Yy5lYWNoKHZhLmNvbmNhdC5hcHBseShbXSx2YS5zbGljZSgwLGIpKSxmdW5jdGlvbigpe2RbdGhpc109YX0pO3JldHVybiBkfWZ1bmN0aW9uIHdhKGEpe3JldHVybiJzY3JvbGxUbyJpbiBhJiZhLmRvY3VtZW50P2E6YS5ub2RlVHlwZT09PTk/YS5kZWZhdWx0Vmlld3x8YS5wYXJlbnRXaW5kb3c6ZmFsc2V9dmFyIGM9ZnVuY3Rpb24oYSxiKXtyZXR1cm4gbmV3IGMuZm4uaW5pdChhLGIpfSxSYT1BLmpRdWVyeSxTYT1BLiQscz1BLmRvY3VtZW50LFQsVGE9L15bXjxdKig8W1x3XFddKz4pW14+XSokfF4jKFtcdy1dKykkLyxVYT0vXi5bXjojXFtcLixdKiQvLFZhPS9cUy8sDQpXYT0vXihcc3xcdTAwQTApK3woXHN8XHUwMEEwKSskL2csWGE9L148KFx3KylccypcLz8+KD86PFwvXDE+KT8kLyxQPW5hdmlnYXRvci51c2VyQWdlbnQseGE9ZmFsc2UsUT1bXSxMLCQ9T2JqZWN0LnByb3RvdHlwZS50b1N0cmluZyxhYT1PYmplY3QucHJvdG90eXBlLmhhc093blByb3BlcnR5LGJhPUFycmF5LnByb3RvdHlwZS5wdXNoLFI9QXJyYXkucHJvdG90eXBlLnNsaWNlLHlhPUFycmF5LnByb3RvdHlwZS5pbmRleE9mO2MuZm49Yy5wcm90b3R5cGU9e2luaXQ6ZnVuY3Rpb24oYSxiKXt2YXIgZCxmO2lmKCFhKXJldHVybiB0aGlzO2lmKGEubm9kZVR5cGUpe3RoaXMuY29udGV4dD10aGlzWzBdPWE7dGhpcy5sZW5ndGg9MTtyZXR1cm4gdGhpc31pZihhPT09ImJvZHkiJiYhYil7dGhpcy5jb250ZXh0PXM7dGhpc1swXT1zLmJvZHk7dGhpcy5zZWxlY3Rvcj0iYm9keSI7dGhpcy5sZW5ndGg9MTtyZXR1cm4gdGhpc31pZih0eXBlb2YgYT09PSJzdHJpbmciKWlmKChkPVRhLmV4ZWMoYSkpJiYNCihkWzFdfHwhYikpaWYoZFsxXSl7Zj1iP2Iub3duZXJEb2N1bWVudHx8YjpzO2lmKGE9WGEuZXhlYyhhKSlpZihjLmlzUGxhaW5PYmplY3QoYikpe2E9W3MuY3JlYXRlRWxlbWVudChhWzFdKV07Yy5mbi5hdHRyLmNhbGwoYSxiLHRydWUpfWVsc2UgYT1bZi5jcmVhdGVFbGVtZW50KGFbMV0pXTtlbHNle2E9c2EoW2RbMV1dLFtmXSk7YT0oYS5jYWNoZWFibGU/YS5mcmFnbWVudC5jbG9uZU5vZGUodHJ1ZSk6YS5mcmFnbWVudCkuY2hpbGROb2Rlc31yZXR1cm4gYy5tZXJnZSh0aGlzLGEpfWVsc2V7aWYoYj1zLmdldEVsZW1lbnRCeUlkKGRbMl0pKXtpZihiLmlkIT09ZFsyXSlyZXR1cm4gVC5maW5kKGEpO3RoaXMubGVuZ3RoPTE7dGhpc1swXT1ifXRoaXMuY29udGV4dD1zO3RoaXMuc2VsZWN0b3I9YTtyZXR1cm4gdGhpc31lbHNlIGlmKCFiJiYvXlx3KyQvLnRlc3QoYSkpe3RoaXMuc2VsZWN0b3I9YTt0aGlzLmNvbnRleHQ9czthPXMuZ2V0RWxlbWVudHNCeVRhZ05hbWUoYSk7cmV0dXJuIGMubWVyZ2UodGhpcywNCmEpfWVsc2UgcmV0dXJuIWJ8fGIuanF1ZXJ5PyhifHxUKS5maW5kKGEpOmMoYikuZmluZChhKTtlbHNlIGlmKGMuaXNGdW5jdGlvbihhKSlyZXR1cm4gVC5yZWFkeShhKTtpZihhLnNlbGVjdG9yIT09dyl7dGhpcy5zZWxlY3Rvcj1hLnNlbGVjdG9yO3RoaXMuY29udGV4dD1hLmNvbnRleHR9cmV0dXJuIGMubWFrZUFycmF5KGEsdGhpcyl9LHNlbGVjdG9yOiIiLGpxdWVyeToiMS40LjIiLGxlbmd0aDowLHNpemU6ZnVuY3Rpb24oKXtyZXR1cm4gdGhpcy5sZW5ndGh9LHRvQXJyYXk6ZnVuY3Rpb24oKXtyZXR1cm4gUi5jYWxsKHRoaXMsMCl9LGdldDpmdW5jdGlvbihhKXtyZXR1cm4gYT09bnVsbD90aGlzLnRvQXJyYXkoKTphPDA/dGhpcy5zbGljZShhKVswXTp0aGlzW2FdfSxwdXNoU3RhY2s6ZnVuY3Rpb24oYSxiLGQpe3ZhciBmPWMoKTtjLmlzQXJyYXkoYSk/YmEuYXBwbHkoZixhKTpjLm1lcmdlKGYsYSk7Zi5wcmV2T2JqZWN0PXRoaXM7Zi5jb250ZXh0PXRoaXMuY29udGV4dDtpZihiPT09DQoiZmluZCIpZi5zZWxlY3Rvcj10aGlzLnNlbGVjdG9yKyh0aGlzLnNlbGVjdG9yPyIgIjoiIikrZDtlbHNlIGlmKGIpZi5zZWxlY3Rvcj10aGlzLnNlbGVjdG9yKyIuIitiKyIoIitkKyIpIjtyZXR1cm4gZn0sZWFjaDpmdW5jdGlvbihhLGIpe3JldHVybiBjLmVhY2godGhpcyxhLGIpfSxyZWFkeTpmdW5jdGlvbihhKXtjLmJpbmRSZWFkeSgpO2lmKGMuaXNSZWFkeSlhLmNhbGwocyxjKTtlbHNlIFEmJlEucHVzaChhKTtyZXR1cm4gdGhpc30sZXE6ZnVuY3Rpb24oYSl7cmV0dXJuIGE9PT0tMT90aGlzLnNsaWNlKGEpOnRoaXMuc2xpY2UoYSwrYSsxKX0sZmlyc3Q6ZnVuY3Rpb24oKXtyZXR1cm4gdGhpcy5lcSgwKX0sbGFzdDpmdW5jdGlvbigpe3JldHVybiB0aGlzLmVxKC0xKX0sc2xpY2U6ZnVuY3Rpb24oKXtyZXR1cm4gdGhpcy5wdXNoU3RhY2soUi5hcHBseSh0aGlzLGFyZ3VtZW50cyksInNsaWNlIixSLmNhbGwoYXJndW1lbnRzKS5qb2luKCIsIikpfSxtYXA6ZnVuY3Rpb24oYSl7cmV0dXJuIHRoaXMucHVzaFN0YWNrKGMubWFwKHRoaXMsDQpmdW5jdGlvbihiLGQpe3JldHVybiBhLmNhbGwoYixkLGIpfSkpfSxlbmQ6ZnVuY3Rpb24oKXtyZXR1cm4gdGhpcy5wcmV2T2JqZWN0fHxjKG51bGwpfSxwdXNoOmJhLHNvcnQ6W10uc29ydCxzcGxpY2U6W10uc3BsaWNlfTtjLmZuLmluaXQucHJvdG90eXBlPWMuZm47Yy5leHRlbmQ9Yy5mbi5leHRlbmQ9ZnVuY3Rpb24oKXt2YXIgYT1hcmd1bWVudHNbMF18fHt9LGI9MSxkPWFyZ3VtZW50cy5sZW5ndGgsZj1mYWxzZSxlLGosaSxvO2lmKHR5cGVvZiBhPT09ImJvb2xlYW4iKXtmPWE7YT1hcmd1bWVudHNbMV18fHt9O2I9Mn1pZih0eXBlb2YgYSE9PSJvYmplY3QiJiYhYy5pc0Z1bmN0aW9uKGEpKWE9e307aWYoZD09PWIpe2E9dGhpczstLWJ9Zm9yKDtiPGQ7YisrKWlmKChlPWFyZ3VtZW50c1tiXSkhPW51bGwpZm9yKGogaW4gZSl7aT1hW2pdO289ZVtqXTtpZihhIT09bylpZihmJiZvJiYoYy5pc1BsYWluT2JqZWN0KG8pfHxjLmlzQXJyYXkobykpKXtpPWkmJihjLmlzUGxhaW5PYmplY3QoaSl8fA0KYy5pc0FycmF5KGkpKT9pOmMuaXNBcnJheShvKT9bXTp7fTthW2pdPWMuZXh0ZW5kKGYsaSxvKX1lbHNlIGlmKG8hPT13KWFbal09b31yZXR1cm4gYX07Yy5leHRlbmQoe25vQ29uZmxpY3Q6ZnVuY3Rpb24oYSl7QS4kPVNhO2lmKGEpQS5qUXVlcnk9UmE7cmV0dXJuIGN9LGlzUmVhZHk6ZmFsc2UscmVhZHk6ZnVuY3Rpb24oKXtpZighYy5pc1JlYWR5KXtpZighcy5ib2R5KXJldHVybiBzZXRUaW1lb3V0KGMucmVhZHksMTMpO2MuaXNSZWFkeT10cnVlO2lmKFEpe2Zvcih2YXIgYSxiPTA7YT1RW2IrK107KWEuY2FsbChzLGMpO1E9bnVsbH1jLmZuLnRyaWdnZXJIYW5kbGVyJiZjKHMpLnRyaWdnZXJIYW5kbGVyKCJyZWFkeSIpfX0sYmluZFJlYWR5OmZ1bmN0aW9uKCl7aWYoIXhhKXt4YT10cnVlO2lmKHMucmVhZHlTdGF0ZT09PSJjb21wbGV0ZSIpcmV0dXJuIGMucmVhZHkoKTtpZihzLmFkZEV2ZW50TGlzdGVuZXIpe3MuYWRkRXZlbnRMaXN0ZW5lcigiRE9NQ29udGVudExvYWRlZCIsDQpMLGZhbHNlKTtBLmFkZEV2ZW50TGlzdGVuZXIoImxvYWQiLGMucmVhZHksZmFsc2UpfWVsc2UgaWYocy5hdHRhY2hFdmVudCl7cy5hdHRhY2hFdmVudCgib25yZWFkeXN0YXRlY2hhbmdlIixMKTtBLmF0dGFjaEV2ZW50KCJvbmxvYWQiLGMucmVhZHkpO3ZhciBhPWZhbHNlO3RyeXthPUEuZnJhbWVFbGVtZW50PT1udWxsfWNhdGNoKGIpe31zLmRvY3VtZW50RWxlbWVudC5kb1Njcm9sbCYmYSYmbWEoKX19fSxpc0Z1bmN0aW9uOmZ1bmN0aW9uKGEpe3JldHVybiAkLmNhbGwoYSk9PT0iW29iamVjdCBGdW5jdGlvbl0ifSxpc0FycmF5OmZ1bmN0aW9uKGEpe3JldHVybiAkLmNhbGwoYSk9PT0iW29iamVjdCBBcnJheV0ifSxpc1BsYWluT2JqZWN0OmZ1bmN0aW9uKGEpe2lmKCFhfHwkLmNhbGwoYSkhPT0iW29iamVjdCBPYmplY3RdInx8YS5ub2RlVHlwZXx8YS5zZXRJbnRlcnZhbClyZXR1cm4gZmFsc2U7aWYoYS5jb25zdHJ1Y3RvciYmIWFhLmNhbGwoYSwiY29uc3RydWN0b3IiKSYmIWFhLmNhbGwoYS5jb25zdHJ1Y3Rvci5wcm90b3R5cGUsDQoiaXNQcm90b3R5cGVPZiIpKXJldHVybiBmYWxzZTt2YXIgYjtmb3IoYiBpbiBhKTtyZXR1cm4gYj09PXd8fGFhLmNhbGwoYSxiKX0saXNFbXB0eU9iamVjdDpmdW5jdGlvbihhKXtmb3IodmFyIGIgaW4gYSlyZXR1cm4gZmFsc2U7cmV0dXJuIHRydWV9LGVycm9yOmZ1bmN0aW9uKGEpe3Rocm93IGE7fSxwYXJzZUpTT046ZnVuY3Rpb24oYSl7aWYodHlwZW9mIGEhPT0ic3RyaW5nInx8IWEpcmV0dXJuIG51bGw7YT1jLnRyaW0oYSk7aWYoL15bXF0sOnt9XHNdKiQvLnRlc3QoYS5yZXBsYWNlKC9cXCg/OlsiXFxcL2JmbnJ0XXx1WzAtOWEtZkEtRl17NH0pL2csIkAiKS5yZXBsYWNlKC8iW14iXFxcblxyXSoifHRydWV8ZmFsc2V8bnVsbHwtP1xkKyg/OlwuXGQqKT8oPzpbZUVdWytcLV0/XGQrKT8vZywiXSIpLnJlcGxhY2UoLyg/Ol58OnwsKSg/OlxzKlxbKSsvZywiIikpKXJldHVybiBBLkpTT04mJkEuSlNPTi5wYXJzZT9BLkpTT04ucGFyc2UoYSk6KG5ldyBGdW5jdGlvbigicmV0dXJuICIrDQphKSkoKTtlbHNlIGMuZXJyb3IoIkludmFsaWQgSlNPTjogIithKX0sbm9vcDpmdW5jdGlvbigpe30sZ2xvYmFsRXZhbDpmdW5jdGlvbihhKXtpZihhJiZWYS50ZXN0KGEpKXt2YXIgYj1zLmdldEVsZW1lbnRzQnlUYWdOYW1lKCJoZWFkIilbMF18fHMuZG9jdW1lbnRFbGVtZW50LGQ9cy5jcmVhdGVFbGVtZW50KCJzY3JpcHQiKTtkLnR5cGU9InRleHQvamF2YXNjcmlwdCI7aWYoYy5zdXBwb3J0LnNjcmlwdEV2YWwpZC5hcHBlbmRDaGlsZChzLmNyZWF0ZVRleHROb2RlKGEpKTtlbHNlIGQudGV4dD1hO2IuaW5zZXJ0QmVmb3JlKGQsYi5maXJzdENoaWxkKTtiLnJlbW92ZUNoaWxkKGQpfX0sbm9kZU5hbWU6ZnVuY3Rpb24oYSxiKXtyZXR1cm4gYS5ub2RlTmFtZSYmYS5ub2RlTmFtZS50b1VwcGVyQ2FzZSgpPT09Yi50b1VwcGVyQ2FzZSgpfSxlYWNoOmZ1bmN0aW9uKGEsYixkKXt2YXIgZixlPTAsaj1hLmxlbmd0aCxpPWo9PT13fHxjLmlzRnVuY3Rpb24oYSk7aWYoZClpZihpKWZvcihmIGluIGEpe2lmKGIuYXBwbHkoYVtmXSwNCmQpPT09ZmFsc2UpYnJlYWt9ZWxzZSBmb3IoO2U8ajspe2lmKGIuYXBwbHkoYVtlKytdLGQpPT09ZmFsc2UpYnJlYWt9ZWxzZSBpZihpKWZvcihmIGluIGEpe2lmKGIuY2FsbChhW2ZdLGYsYVtmXSk9PT1mYWxzZSlicmVha31lbHNlIGZvcihkPWFbMF07ZTxqJiZiLmNhbGwoZCxlLGQpIT09ZmFsc2U7ZD1hWysrZV0pO3JldHVybiBhfSx0cmltOmZ1bmN0aW9uKGEpe3JldHVybihhfHwiIikucmVwbGFjZShXYSwiIil9LG1ha2VBcnJheTpmdW5jdGlvbihhLGIpe2I9Ynx8W107aWYoYSE9bnVsbClhLmxlbmd0aD09bnVsbHx8dHlwZW9mIGE9PT0ic3RyaW5nInx8Yy5pc0Z1bmN0aW9uKGEpfHx0eXBlb2YgYSE9PSJmdW5jdGlvbiImJmEuc2V0SW50ZXJ2YWw/YmEuY2FsbChiLGEpOmMubWVyZ2UoYixhKTtyZXR1cm4gYn0saW5BcnJheTpmdW5jdGlvbihhLGIpe2lmKGIuaW5kZXhPZilyZXR1cm4gYi5pbmRleE9mKGEpO2Zvcih2YXIgZD0wLGY9Yi5sZW5ndGg7ZDxmO2QrKylpZihiW2RdPT09DQphKXJldHVybiBkO3JldHVybi0xfSxtZXJnZTpmdW5jdGlvbihhLGIpe3ZhciBkPWEubGVuZ3RoLGY9MDtpZih0eXBlb2YgYi5sZW5ndGg9PT0ibnVtYmVyIilmb3IodmFyIGU9Yi5sZW5ndGg7ZjxlO2YrKylhW2QrK109YltmXTtlbHNlIGZvcig7YltmXSE9PXc7KWFbZCsrXT1iW2YrK107YS5sZW5ndGg9ZDtyZXR1cm4gYX0sZ3JlcDpmdW5jdGlvbihhLGIsZCl7Zm9yKHZhciBmPVtdLGU9MCxqPWEubGVuZ3RoO2U8ajtlKyspIWQhPT0hYihhW2VdLGUpJiZmLnB1c2goYVtlXSk7cmV0dXJuIGZ9LG1hcDpmdW5jdGlvbihhLGIsZCl7Zm9yKHZhciBmPVtdLGUsaj0wLGk9YS5sZW5ndGg7ajxpO2orKyl7ZT1iKGFbal0saixkKTtpZihlIT1udWxsKWZbZi5sZW5ndGhdPWV9cmV0dXJuIGYuY29uY2F0LmFwcGx5KFtdLGYpfSxndWlkOjEscHJveHk6ZnVuY3Rpb24oYSxiLGQpe2lmKGFyZ3VtZW50cy5sZW5ndGg9PT0yKWlmKHR5cGVvZiBiPT09InN0cmluZyIpe2Q9YTthPWRbYl07Yj13fWVsc2UgaWYoYiYmDQohYy5pc0Z1bmN0aW9uKGIpKXtkPWI7Yj13fWlmKCFiJiZhKWI9ZnVuY3Rpb24oKXtyZXR1cm4gYS5hcHBseShkfHx0aGlzLGFyZ3VtZW50cyl9O2lmKGEpYi5ndWlkPWEuZ3VpZD1hLmd1aWR8fGIuZ3VpZHx8Yy5ndWlkKys7cmV0dXJuIGJ9LHVhTWF0Y2g6ZnVuY3Rpb24oYSl7YT1hLnRvTG93ZXJDYXNlKCk7YT0vKHdlYmtpdClbIFwvXShbXHcuXSspLy5leGVjKGEpfHwvKG9wZXJhKSg/Oi4qdmVyc2lvbik/WyBcL10oW1x3Ll0rKS8uZXhlYyhhKXx8Lyhtc2llKSAoW1x3Ll0rKS8uZXhlYyhhKXx8IS9jb21wYXRpYmxlLy50ZXN0KGEpJiYvKG1vemlsbGEpKD86Lio/IHJ2OihbXHcuXSspKT8vLmV4ZWMoYSl8fFtdO3JldHVybnticm93c2VyOmFbMV18fCIiLHZlcnNpb246YVsyXXx8IjAifX0sYnJvd3Nlcjp7fX0pO1A9Yy51YU1hdGNoKFApO2lmKFAuYnJvd3Nlcil7Yy5icm93c2VyW1AuYnJvd3Nlcl09dHJ1ZTtjLmJyb3dzZXIudmVyc2lvbj1QLnZlcnNpb259aWYoYy5icm93c2VyLndlYmtpdCljLmJyb3dzZXIuc2FmYXJpPQ0KdHJ1ZTtpZih5YSljLmluQXJyYXk9ZnVuY3Rpb24oYSxiKXtyZXR1cm4geWEuY2FsbChiLGEpfTtUPWMocyk7aWYocy5hZGRFdmVudExpc3RlbmVyKUw9ZnVuY3Rpb24oKXtzLnJlbW92ZUV2ZW50TGlzdGVuZXIoIkRPTUNvbnRlbnRMb2FkZWQiLEwsZmFsc2UpO2MucmVhZHkoKX07ZWxzZSBpZihzLmF0dGFjaEV2ZW50KUw9ZnVuY3Rpb24oKXtpZihzLnJlYWR5U3RhdGU9PT0iY29tcGxldGUiKXtzLmRldGFjaEV2ZW50KCJvbnJlYWR5c3RhdGVjaGFuZ2UiLEwpO2MucmVhZHkoKX19OyhmdW5jdGlvbigpe2Muc3VwcG9ydD17fTt2YXIgYT1zLmRvY3VtZW50RWxlbWVudCxiPXMuY3JlYXRlRWxlbWVudCgic2NyaXB0IiksZD1zLmNyZWF0ZUVsZW1lbnQoImRpdiIpLGY9InNjcmlwdCIrSigpO2Quc3R5bGUuZGlzcGxheT0ibm9uZSI7ZC5pbm5lckhUTUw9IiAgIDxsaW5rLz48dGFibGU+PC90YWJsZT48YSBocmVmPScvYScgc3R5bGU9J2NvbG9yOnJlZDtmbG9hdDpsZWZ0O29wYWNpdHk6LjU1Oyc+YTwvYT48aW5wdXQgdHlwZT0nY2hlY2tib3gnLz4iOw0KdmFyIGU9ZC5nZXRFbGVtZW50c0J5VGFnTmFtZSgiKiIpLGo9ZC5nZXRFbGVtZW50c0J5VGFnTmFtZSgiYSIpWzBdO2lmKCEoIWV8fCFlLmxlbmd0aHx8IWopKXtjLnN1cHBvcnQ9e2xlYWRpbmdXaGl0ZXNwYWNlOmQuZmlyc3RDaGlsZC5ub2RlVHlwZT09PTMsdGJvZHk6IWQuZ2V0RWxlbWVudHNCeVRhZ05hbWUoInRib2R5IikubGVuZ3RoLGh0bWxTZXJpYWxpemU6ISFkLmdldEVsZW1lbnRzQnlUYWdOYW1lKCJsaW5rIikubGVuZ3RoLHN0eWxlOi9yZWQvLnRlc3Qoai5nZXRBdHRyaWJ1dGUoInN0eWxlIikpLGhyZWZOb3JtYWxpemVkOmouZ2V0QXR0cmlidXRlKCJocmVmIik9PT0iL2EiLG9wYWNpdHk6L14wLjU1JC8udGVzdChqLnN0eWxlLm9wYWNpdHkpLGNzc0Zsb2F0OiEhai5zdHlsZS5jc3NGbG9hdCxjaGVja09uOmQuZ2V0RWxlbWVudHNCeVRhZ05hbWUoImlucHV0IilbMF0udmFsdWU9PT0ib24iLG9wdFNlbGVjdGVkOnMuY3JlYXRlRWxlbWVudCgic2VsZWN0IikuYXBwZW5kQ2hpbGQocy5jcmVhdGVFbGVtZW50KCJvcHRpb24iKSkuc2VsZWN0ZWQsDQpwYXJlbnROb2RlOmQucmVtb3ZlQ2hpbGQoZC5hcHBlbmRDaGlsZChzLmNyZWF0ZUVsZW1lbnQoImRpdiIpKSkucGFyZW50Tm9kZT09PW51bGwsZGVsZXRlRXhwYW5kbzp0cnVlLGNoZWNrQ2xvbmU6ZmFsc2Usc2NyaXB0RXZhbDpmYWxzZSxub0Nsb25lRXZlbnQ6dHJ1ZSxib3hNb2RlbDpudWxsfTtiLnR5cGU9InRleHQvamF2YXNjcmlwdCI7dHJ5e2IuYXBwZW5kQ2hpbGQocy5jcmVhdGVUZXh0Tm9kZSgid2luZG93LiIrZisiPTE7IikpfWNhdGNoKGkpe31hLmluc2VydEJlZm9yZShiLGEuZmlyc3RDaGlsZCk7aWYoQVtmXSl7Yy5zdXBwb3J0LnNjcmlwdEV2YWw9dHJ1ZTtkZWxldGUgQVtmXX10cnl7ZGVsZXRlIGIudGVzdH1jYXRjaChvKXtjLnN1cHBvcnQuZGVsZXRlRXhwYW5kbz1mYWxzZX1hLnJlbW92ZUNoaWxkKGIpO2lmKGQuYXR0YWNoRXZlbnQmJmQuZmlyZUV2ZW50KXtkLmF0dGFjaEV2ZW50KCJvbmNsaWNrIixmdW5jdGlvbiBrKCl7Yy5zdXBwb3J0Lm5vQ2xvbmVFdmVudD0NCmZhbHNlO2QuZGV0YWNoRXZlbnQoIm9uY2xpY2siLGspfSk7ZC5jbG9uZU5vZGUodHJ1ZSkuZmlyZUV2ZW50KCJvbmNsaWNrIil9ZD1zLmNyZWF0ZUVsZW1lbnQoImRpdiIpO2QuaW5uZXJIVE1MPSI8aW5wdXQgdHlwZT0ncmFkaW8nIG5hbWU9J3JhZGlvdGVzdCcgY2hlY2tlZD0nY2hlY2tlZCcvPiI7YT1zLmNyZWF0ZURvY3VtZW50RnJhZ21lbnQoKTthLmFwcGVuZENoaWxkKGQuZmlyc3RDaGlsZCk7Yy5zdXBwb3J0LmNoZWNrQ2xvbmU9YS5jbG9uZU5vZGUodHJ1ZSkuY2xvbmVOb2RlKHRydWUpLmxhc3RDaGlsZC5jaGVja2VkO2MoZnVuY3Rpb24oKXt2YXIgaz1zLmNyZWF0ZUVsZW1lbnQoImRpdiIpO2suc3R5bGUud2lkdGg9ay5zdHlsZS5wYWRkaW5nTGVmdD0iMXB4IjtzLmJvZHkuYXBwZW5kQ2hpbGQoayk7Yy5ib3hNb2RlbD1jLnN1cHBvcnQuYm94TW9kZWw9ay5vZmZzZXRXaWR0aD09PTI7cy5ib2R5LnJlbW92ZUNoaWxkKGspLnN0eWxlLmRpc3BsYXk9Im5vbmUifSk7YT1mdW5jdGlvbihrKXt2YXIgbj0NCnMuY3JlYXRlRWxlbWVudCgiZGl2Iik7az0ib24iK2s7dmFyIHI9ayBpbiBuO2lmKCFyKXtuLnNldEF0dHJpYnV0ZShrLCJyZXR1cm47Iik7cj10eXBlb2YgbltrXT09PSJmdW5jdGlvbiJ9cmV0dXJuIHJ9O2Muc3VwcG9ydC5zdWJtaXRCdWJibGVzPWEoInN1Ym1pdCIpO2Muc3VwcG9ydC5jaGFuZ2VCdWJibGVzPWEoImNoYW5nZSIpO2E9Yj1kPWU9aj1udWxsfX0pKCk7Yy5wcm9wcz17ImZvciI6Imh0bWxGb3IiLCJjbGFzcyI6ImNsYXNzTmFtZSIscmVhZG9ubHk6InJlYWRPbmx5IixtYXhsZW5ndGg6Im1heExlbmd0aCIsY2VsbHNwYWNpbmc6ImNlbGxTcGFjaW5nIixyb3dzcGFuOiJyb3dTcGFuIixjb2xzcGFuOiJjb2xTcGFuIix0YWJpbmRleDoidGFiSW5kZXgiLHVzZW1hcDoidXNlTWFwIixmcmFtZWJvcmRlcjoiZnJhbWVCb3JkZXIifTt2YXIgRz0ialF1ZXJ5IitKKCksWWE9MCx6YT17fTtjLmV4dGVuZCh7Y2FjaGU6e30sZXhwYW5kbzpHLG5vRGF0YTp7ZW1iZWQ6dHJ1ZSxvYmplY3Q6dHJ1ZSwNCmFwcGxldDp0cnVlfSxkYXRhOmZ1bmN0aW9uKGEsYixkKXtpZighKGEubm9kZU5hbWUmJmMubm9EYXRhW2Eubm9kZU5hbWUudG9Mb3dlckNhc2UoKV0pKXthPWE9PUE/emE6YTt2YXIgZj1hW0ddLGU9Yy5jYWNoZTtpZighZiYmdHlwZW9mIGI9PT0ic3RyaW5nIiYmZD09PXcpcmV0dXJuIG51bGw7Znx8KGY9KytZYSk7aWYodHlwZW9mIGI9PT0ib2JqZWN0Iil7YVtHXT1mO2VbZl09Yy5leHRlbmQodHJ1ZSx7fSxiKX1lbHNlIGlmKCFlW2ZdKXthW0ddPWY7ZVtmXT17fX1hPWVbZl07aWYoZCE9PXcpYVtiXT1kO3JldHVybiB0eXBlb2YgYj09PSJzdHJpbmciP2FbYl06YX19LHJlbW92ZURhdGE6ZnVuY3Rpb24oYSxiKXtpZighKGEubm9kZU5hbWUmJmMubm9EYXRhW2Eubm9kZU5hbWUudG9Mb3dlckNhc2UoKV0pKXthPWE9PUE/emE6YTt2YXIgZD1hW0ddLGY9Yy5jYWNoZSxlPWZbZF07aWYoYil7aWYoZSl7ZGVsZXRlIGVbYl07Yy5pc0VtcHR5T2JqZWN0KGUpJiZjLnJlbW92ZURhdGEoYSl9fWVsc2V7aWYoYy5zdXBwb3J0LmRlbGV0ZUV4cGFuZG8pZGVsZXRlIGFbYy5leHBhbmRvXTsNCmVsc2UgYS5yZW1vdmVBdHRyaWJ1dGUmJmEucmVtb3ZlQXR0cmlidXRlKGMuZXhwYW5kbyk7ZGVsZXRlIGZbZF19fX19KTtjLmZuLmV4dGVuZCh7ZGF0YTpmdW5jdGlvbihhLGIpe2lmKHR5cGVvZiBhPT09InVuZGVmaW5lZCImJnRoaXMubGVuZ3RoKXJldHVybiBjLmRhdGEodGhpc1swXSk7ZWxzZSBpZih0eXBlb2YgYT09PSJvYmplY3QiKXJldHVybiB0aGlzLmVhY2goZnVuY3Rpb24oKXtjLmRhdGEodGhpcyxhKX0pO3ZhciBkPWEuc3BsaXQoIi4iKTtkWzFdPWRbMV0/Ii4iK2RbMV06IiI7aWYoYj09PXcpe3ZhciBmPXRoaXMudHJpZ2dlckhhbmRsZXIoImdldERhdGEiK2RbMV0rIiEiLFtkWzBdXSk7aWYoZj09PXcmJnRoaXMubGVuZ3RoKWY9Yy5kYXRhKHRoaXNbMF0sYSk7cmV0dXJuIGY9PT13JiZkWzFdP3RoaXMuZGF0YShkWzBdKTpmfWVsc2UgcmV0dXJuIHRoaXMudHJpZ2dlcigic2V0RGF0YSIrZFsxXSsiISIsW2RbMF0sYl0pLmVhY2goZnVuY3Rpb24oKXtjLmRhdGEodGhpcywNCmEsYil9KX0scmVtb3ZlRGF0YTpmdW5jdGlvbihhKXtyZXR1cm4gdGhpcy5lYWNoKGZ1bmN0aW9uKCl7Yy5yZW1vdmVEYXRhKHRoaXMsYSl9KX19KTtjLmV4dGVuZCh7cXVldWU6ZnVuY3Rpb24oYSxiLGQpe2lmKGEpe2I9KGJ8fCJmeCIpKyJxdWV1ZSI7dmFyIGY9Yy5kYXRhKGEsYik7aWYoIWQpcmV0dXJuIGZ8fFtdO2lmKCFmfHxjLmlzQXJyYXkoZCkpZj1jLmRhdGEoYSxiLGMubWFrZUFycmF5KGQpKTtlbHNlIGYucHVzaChkKTtyZXR1cm4gZn19LGRlcXVldWU6ZnVuY3Rpb24oYSxiKXtiPWJ8fCJmeCI7dmFyIGQ9Yy5xdWV1ZShhLGIpLGY9ZC5zaGlmdCgpO2lmKGY9PT0iaW5wcm9ncmVzcyIpZj1kLnNoaWZ0KCk7aWYoZil7Yj09PSJmeCImJmQudW5zaGlmdCgiaW5wcm9ncmVzcyIpO2YuY2FsbChhLGZ1bmN0aW9uKCl7Yy5kZXF1ZXVlKGEsYil9KX19fSk7Yy5mbi5leHRlbmQoe3F1ZXVlOmZ1bmN0aW9uKGEsYil7aWYodHlwZW9mIGEhPT0ic3RyaW5nIil7Yj1hO2E9ImZ4In1pZihiPT09DQp3KXJldHVybiBjLnF1ZXVlKHRoaXNbMF0sYSk7cmV0dXJuIHRoaXMuZWFjaChmdW5jdGlvbigpe3ZhciBkPWMucXVldWUodGhpcyxhLGIpO2E9PT0iZngiJiZkWzBdIT09ImlucHJvZ3Jlc3MiJiZjLmRlcXVldWUodGhpcyxhKX0pfSxkZXF1ZXVlOmZ1bmN0aW9uKGEpe3JldHVybiB0aGlzLmVhY2goZnVuY3Rpb24oKXtjLmRlcXVldWUodGhpcyxhKX0pfSxkZWxheTpmdW5jdGlvbihhLGIpe2E9Yy5meD9jLmZ4LnNwZWVkc1thXXx8YTphO2I9Ynx8ImZ4IjtyZXR1cm4gdGhpcy5xdWV1ZShiLGZ1bmN0aW9uKCl7dmFyIGQ9dGhpcztzZXRUaW1lb3V0KGZ1bmN0aW9uKCl7Yy5kZXF1ZXVlKGQsYil9LGEpfSl9LGNsZWFyUXVldWU6ZnVuY3Rpb24oYSl7cmV0dXJuIHRoaXMucXVldWUoYXx8ImZ4IixbXSl9fSk7dmFyIEFhPS9bXG5cdF0vZyxjYT0vXHMrLyxaYT0vXHIvZywkYT0vaHJlZnxzcmN8c3R5bGUvLGFiPS8oYnV0dG9ufGlucHV0KS9pLGJiPS8oYnV0dG9ufGlucHV0fG9iamVjdHxzZWxlY3R8dGV4dGFyZWEpL2ksDQpjYj0vXihhfGFyZWEpJC9pLEJhPS9yYWRpb3xjaGVja2JveC87Yy5mbi5leHRlbmQoe2F0dHI6ZnVuY3Rpb24oYSxiKXtyZXR1cm4gWCh0aGlzLGEsYix0cnVlLGMuYXR0cil9LHJlbW92ZUF0dHI6ZnVuY3Rpb24oYSl7cmV0dXJuIHRoaXMuZWFjaChmdW5jdGlvbigpe2MuYXR0cih0aGlzLGEsIiIpO3RoaXMubm9kZVR5cGU9PT0xJiZ0aGlzLnJlbW92ZUF0dHJpYnV0ZShhKX0pfSxhZGRDbGFzczpmdW5jdGlvbihhKXtpZihjLmlzRnVuY3Rpb24oYSkpcmV0dXJuIHRoaXMuZWFjaChmdW5jdGlvbihuKXt2YXIgcj1jKHRoaXMpO3IuYWRkQ2xhc3MoYS5jYWxsKHRoaXMsbixyLmF0dHIoImNsYXNzIikpKX0pO2lmKGEmJnR5cGVvZiBhPT09InN0cmluZyIpZm9yKHZhciBiPShhfHwiIikuc3BsaXQoY2EpLGQ9MCxmPXRoaXMubGVuZ3RoO2Q8ZjtkKyspe3ZhciBlPXRoaXNbZF07aWYoZS5ub2RlVHlwZT09PTEpaWYoZS5jbGFzc05hbWUpe2Zvcih2YXIgaj0iICIrZS5jbGFzc05hbWUrIiAiLA0KaT1lLmNsYXNzTmFtZSxvPTAsaz1iLmxlbmd0aDtvPGs7bysrKWlmKGouaW5kZXhPZigiICIrYltvXSsiICIpPDApaSs9IiAiK2Jbb107ZS5jbGFzc05hbWU9Yy50cmltKGkpfWVsc2UgZS5jbGFzc05hbWU9YX1yZXR1cm4gdGhpc30scmVtb3ZlQ2xhc3M6ZnVuY3Rpb24oYSl7aWYoYy5pc0Z1bmN0aW9uKGEpKXJldHVybiB0aGlzLmVhY2goZnVuY3Rpb24oayl7dmFyIG49Yyh0aGlzKTtuLnJlbW92ZUNsYXNzKGEuY2FsbCh0aGlzLGssbi5hdHRyKCJjbGFzcyIpKSl9KTtpZihhJiZ0eXBlb2YgYT09PSJzdHJpbmcifHxhPT09dylmb3IodmFyIGI9KGF8fCIiKS5zcGxpdChjYSksZD0wLGY9dGhpcy5sZW5ndGg7ZDxmO2QrKyl7dmFyIGU9dGhpc1tkXTtpZihlLm5vZGVUeXBlPT09MSYmZS5jbGFzc05hbWUpaWYoYSl7Zm9yKHZhciBqPSgiICIrZS5jbGFzc05hbWUrIiAiKS5yZXBsYWNlKEFhLCIgIiksaT0wLG89Yi5sZW5ndGg7aTxvO2krKylqPWoucmVwbGFjZSgiICIrYltpXSsiICIsDQoiICIpO2UuY2xhc3NOYW1lPWMudHJpbShqKX1lbHNlIGUuY2xhc3NOYW1lPSIifXJldHVybiB0aGlzfSx0b2dnbGVDbGFzczpmdW5jdGlvbihhLGIpe3ZhciBkPXR5cGVvZiBhLGY9dHlwZW9mIGI9PT0iYm9vbGVhbiI7aWYoYy5pc0Z1bmN0aW9uKGEpKXJldHVybiB0aGlzLmVhY2goZnVuY3Rpb24oZSl7dmFyIGo9Yyh0aGlzKTtqLnRvZ2dsZUNsYXNzKGEuY2FsbCh0aGlzLGUsai5hdHRyKCJjbGFzcyIpLGIpLGIpfSk7cmV0dXJuIHRoaXMuZWFjaChmdW5jdGlvbigpe2lmKGQ9PT0ic3RyaW5nIilmb3IodmFyIGUsaj0wLGk9Yyh0aGlzKSxvPWIsaz1hLnNwbGl0KGNhKTtlPWtbaisrXTspe289Zj9vOiFpLmhhc0NsYXNzKGUpO2lbbz8iYWRkQ2xhc3MiOiJyZW1vdmVDbGFzcyJdKGUpfWVsc2UgaWYoZD09PSJ1bmRlZmluZWQifHxkPT09ImJvb2xlYW4iKXt0aGlzLmNsYXNzTmFtZSYmYy5kYXRhKHRoaXMsIl9fY2xhc3NOYW1lX18iLHRoaXMuY2xhc3NOYW1lKTt0aGlzLmNsYXNzTmFtZT0NCnRoaXMuY2xhc3NOYW1lfHxhPT09ZmFsc2U/IiI6Yy5kYXRhKHRoaXMsIl9fY2xhc3NOYW1lX18iKXx8IiJ9fSl9LGhhc0NsYXNzOmZ1bmN0aW9uKGEpe2E9IiAiK2ErIiAiO2Zvcih2YXIgYj0wLGQ9dGhpcy5sZW5ndGg7YjxkO2IrKylpZigoIiAiK3RoaXNbYl0uY2xhc3NOYW1lKyIgIikucmVwbGFjZShBYSwiICIpLmluZGV4T2YoYSk+LTEpcmV0dXJuIHRydWU7cmV0dXJuIGZhbHNlfSx2YWw6ZnVuY3Rpb24oYSl7aWYoYT09PXcpe3ZhciBiPXRoaXNbMF07aWYoYil7aWYoYy5ub2RlTmFtZShiLCJvcHRpb24iKSlyZXR1cm4oYi5hdHRyaWJ1dGVzLnZhbHVlfHx7fSkuc3BlY2lmaWVkP2IudmFsdWU6Yi50ZXh0O2lmKGMubm9kZU5hbWUoYiwic2VsZWN0Iikpe3ZhciBkPWIuc2VsZWN0ZWRJbmRleCxmPVtdLGU9Yi5vcHRpb25zO2I9Yi50eXBlPT09InNlbGVjdC1vbmUiO2lmKGQ8MClyZXR1cm4gbnVsbDt2YXIgaj1iP2Q6MDtmb3IoZD1iP2QrMTplLmxlbmd0aDtqPGQ7aisrKXt2YXIgaT0NCmVbal07aWYoaS5zZWxlY3RlZCl7YT1jKGkpLnZhbCgpO2lmKGIpcmV0dXJuIGE7Zi5wdXNoKGEpfX1yZXR1cm4gZn1pZihCYS50ZXN0KGIudHlwZSkmJiFjLnN1cHBvcnQuY2hlY2tPbilyZXR1cm4gYi5nZXRBdHRyaWJ1dGUoInZhbHVlIik9PT1udWxsPyJvbiI6Yi52YWx1ZTtyZXR1cm4oYi52YWx1ZXx8IiIpLnJlcGxhY2UoWmEsIiIpfXJldHVybiB3fXZhciBvPWMuaXNGdW5jdGlvbihhKTtyZXR1cm4gdGhpcy5lYWNoKGZ1bmN0aW9uKGspe3ZhciBuPWModGhpcykscj1hO2lmKHRoaXMubm9kZVR5cGU9PT0xKXtpZihvKXI9YS5jYWxsKHRoaXMsayxuLnZhbCgpKTtpZih0eXBlb2Ygcj09PSJudW1iZXIiKXIrPSIiO2lmKGMuaXNBcnJheShyKSYmQmEudGVzdCh0aGlzLnR5cGUpKXRoaXMuY2hlY2tlZD1jLmluQXJyYXkobi52YWwoKSxyKT49MDtlbHNlIGlmKGMubm9kZU5hbWUodGhpcywic2VsZWN0Iikpe3ZhciB1PWMubWFrZUFycmF5KHIpO2MoIm9wdGlvbiIsdGhpcykuZWFjaChmdW5jdGlvbigpe3RoaXMuc2VsZWN0ZWQ9DQpjLmluQXJyYXkoYyh0aGlzKS52YWwoKSx1KT49MH0pO2lmKCF1Lmxlbmd0aCl0aGlzLnNlbGVjdGVkSW5kZXg9LTF9ZWxzZSB0aGlzLnZhbHVlPXJ9fSl9fSk7Yy5leHRlbmQoe2F0dHJGbjp7dmFsOnRydWUsY3NzOnRydWUsaHRtbDp0cnVlLHRleHQ6dHJ1ZSxkYXRhOnRydWUsd2lkdGg6dHJ1ZSxoZWlnaHQ6dHJ1ZSxvZmZzZXQ6dHJ1ZX0sYXR0cjpmdW5jdGlvbihhLGIsZCxmKXtpZighYXx8YS5ub2RlVHlwZT09PTN8fGEubm9kZVR5cGU9PT04KXJldHVybiB3O2lmKGYmJmIgaW4gYy5hdHRyRm4pcmV0dXJuIGMoYSlbYl0oZCk7Zj1hLm5vZGVUeXBlIT09MXx8IWMuaXNYTUxEb2MoYSk7dmFyIGU9ZCE9PXc7Yj1mJiZjLnByb3BzW2JdfHxiO2lmKGEubm9kZVR5cGU9PT0xKXt2YXIgaj0kYS50ZXN0KGIpO2lmKGIgaW4gYSYmZiYmIWope2lmKGUpe2I9PT0idHlwZSImJmFiLnRlc3QoYS5ub2RlTmFtZSkmJmEucGFyZW50Tm9kZSYmYy5lcnJvcigidHlwZSBwcm9wZXJ0eSBjYW4ndCBiZSBjaGFuZ2VkIik7DQphW2JdPWR9aWYoYy5ub2RlTmFtZShhLCJmb3JtIikmJmEuZ2V0QXR0cmlidXRlTm9kZShiKSlyZXR1cm4gYS5nZXRBdHRyaWJ1dGVOb2RlKGIpLm5vZGVWYWx1ZTtpZihiPT09InRhYkluZGV4IilyZXR1cm4oYj1hLmdldEF0dHJpYnV0ZU5vZGUoInRhYkluZGV4IikpJiZiLnNwZWNpZmllZD9iLnZhbHVlOmJiLnRlc3QoYS5ub2RlTmFtZSl8fGNiLnRlc3QoYS5ub2RlTmFtZSkmJmEuaHJlZj8wOnc7cmV0dXJuIGFbYl19aWYoIWMuc3VwcG9ydC5zdHlsZSYmZiYmYj09PSJzdHlsZSIpe2lmKGUpYS5zdHlsZS5jc3NUZXh0PSIiK2Q7cmV0dXJuIGEuc3R5bGUuY3NzVGV4dH1lJiZhLnNldEF0dHJpYnV0ZShiLCIiK2QpO2E9IWMuc3VwcG9ydC5ocmVmTm9ybWFsaXplZCYmZiYmaj9hLmdldEF0dHJpYnV0ZShiLDIpOmEuZ2V0QXR0cmlidXRlKGIpO3JldHVybiBhPT09bnVsbD93OmF9cmV0dXJuIGMuc3R5bGUoYSxiLGQpfX0pO3ZhciBPPS9cLiguKikkLyxkYj1mdW5jdGlvbihhKXtyZXR1cm4gYS5yZXBsYWNlKC9bXlx3XHNcLlx8YF0vZywNCmZ1bmN0aW9uKGIpe3JldHVybiJcXCIrYn0pfTtjLmV2ZW50PXthZGQ6ZnVuY3Rpb24oYSxiLGQsZil7aWYoIShhLm5vZGVUeXBlPT09M3x8YS5ub2RlVHlwZT09PTgpKXtpZihhLnNldEludGVydmFsJiZhIT09QSYmIWEuZnJhbWVFbGVtZW50KWE9QTt2YXIgZSxqO2lmKGQuaGFuZGxlcil7ZT1kO2Q9ZS5oYW5kbGVyfWlmKCFkLmd1aWQpZC5ndWlkPWMuZ3VpZCsrO2lmKGo9Yy5kYXRhKGEpKXt2YXIgaT1qLmV2ZW50cz1qLmV2ZW50c3x8e30sbz1qLmhhbmRsZTtpZighbylqLmhhbmRsZT1vPWZ1bmN0aW9uKCl7cmV0dXJuIHR5cGVvZiBjIT09InVuZGVmaW5lZCImJiFjLmV2ZW50LnRyaWdnZXJlZD9jLmV2ZW50LmhhbmRsZS5hcHBseShvLmVsZW0sYXJndW1lbnRzKTp3fTtvLmVsZW09YTtiPWIuc3BsaXQoIiAiKTtmb3IodmFyIGssbj0wLHI7az1iW24rK107KXtqPWU/Yy5leHRlbmQoe30sZSk6e2hhbmRsZXI6ZCxkYXRhOmZ9O2lmKGsuaW5kZXhPZigiLiIpPi0xKXtyPWsuc3BsaXQoIi4iKTsNCms9ci5zaGlmdCgpO2oubmFtZXNwYWNlPXIuc2xpY2UoMCkuc29ydCgpLmpvaW4oIi4iKX1lbHNle3I9W107ai5uYW1lc3BhY2U9IiJ9ai50eXBlPWs7ai5ndWlkPWQuZ3VpZDt2YXIgdT1pW2tdLHo9Yy5ldmVudC5zcGVjaWFsW2tdfHx7fTtpZighdSl7dT1pW2tdPVtdO2lmKCF6LnNldHVwfHx6LnNldHVwLmNhbGwoYSxmLHIsbyk9PT1mYWxzZSlpZihhLmFkZEV2ZW50TGlzdGVuZXIpYS5hZGRFdmVudExpc3RlbmVyKGssbyxmYWxzZSk7ZWxzZSBhLmF0dGFjaEV2ZW50JiZhLmF0dGFjaEV2ZW50KCJvbiIrayxvKX1pZih6LmFkZCl7ei5hZGQuY2FsbChhLGopO2lmKCFqLmhhbmRsZXIuZ3VpZClqLmhhbmRsZXIuZ3VpZD1kLmd1aWR9dS5wdXNoKGopO2MuZXZlbnQuZ2xvYmFsW2tdPXRydWV9YT1udWxsfX19LGdsb2JhbDp7fSxyZW1vdmU6ZnVuY3Rpb24oYSxiLGQsZil7aWYoIShhLm5vZGVUeXBlPT09M3x8YS5ub2RlVHlwZT09PTgpKXt2YXIgZSxqPTAsaSxvLGssbixyLHUsej1jLmRhdGEoYSksDQpDPXomJnouZXZlbnRzO2lmKHomJkMpe2lmKGImJmIudHlwZSl7ZD1iLmhhbmRsZXI7Yj1iLnR5cGV9aWYoIWJ8fHR5cGVvZiBiPT09InN0cmluZyImJmIuY2hhckF0KDApPT09Ii4iKXtiPWJ8fCIiO2ZvcihlIGluIEMpYy5ldmVudC5yZW1vdmUoYSxlK2IpfWVsc2V7Zm9yKGI9Yi5zcGxpdCgiICIpO2U9YltqKytdOyl7bj1lO2k9ZS5pbmRleE9mKCIuIik8MDtvPVtdO2lmKCFpKXtvPWUuc3BsaXQoIi4iKTtlPW8uc2hpZnQoKTtrPW5ldyBSZWdFeHAoIihefFxcLikiK2MubWFwKG8uc2xpY2UoMCkuc29ydCgpLGRiKS5qb2luKCJcXC4oPzouKlxcLik/IikrIihcXC58JCkiKX1pZihyPUNbZV0paWYoZCl7bj1jLmV2ZW50LnNwZWNpYWxbZV18fHt9O2ZvcihCPWZ8fDA7QjxyLmxlbmd0aDtCKyspe3U9cltCXTtpZihkLmd1aWQ9PT11Lmd1aWQpe2lmKGl8fGsudGVzdCh1Lm5hbWVzcGFjZSkpe2Y9PW51bGwmJnIuc3BsaWNlKEItLSwxKTtuLnJlbW92ZSYmbi5yZW1vdmUuY2FsbChhLHUpfWlmKGYhPQ0KbnVsbClicmVha319aWYoci5sZW5ndGg9PT0wfHxmIT1udWxsJiZyLmxlbmd0aD09PTEpe2lmKCFuLnRlYXJkb3dufHxuLnRlYXJkb3duLmNhbGwoYSxvKT09PWZhbHNlKUNhKGEsZSx6LmhhbmRsZSk7ZGVsZXRlIENbZV19fWVsc2UgZm9yKHZhciBCPTA7QjxyLmxlbmd0aDtCKyspe3U9cltCXTtpZihpfHxrLnRlc3QodS5uYW1lc3BhY2UpKXtjLmV2ZW50LnJlbW92ZShhLG4sdS5oYW5kbGVyLEIpO3Iuc3BsaWNlKEItLSwxKX19fWlmKGMuaXNFbXB0eU9iamVjdChDKSl7aWYoYj16LmhhbmRsZSliLmVsZW09bnVsbDtkZWxldGUgei5ldmVudHM7ZGVsZXRlIHouaGFuZGxlO2MuaXNFbXB0eU9iamVjdCh6KSYmYy5yZW1vdmVEYXRhKGEpfX19fX0sdHJpZ2dlcjpmdW5jdGlvbihhLGIsZCxmKXt2YXIgZT1hLnR5cGV8fGE7aWYoIWYpe2E9dHlwZW9mIGE9PT0ib2JqZWN0Ij9hW0ddP2E6Yy5leHRlbmQoYy5FdmVudChlKSxhKTpjLkV2ZW50KGUpO2lmKGUuaW5kZXhPZigiISIpPj0wKXthLnR5cGU9DQplPWUuc2xpY2UoMCwtMSk7YS5leGNsdXNpdmU9dHJ1ZX1pZighZCl7YS5zdG9wUHJvcGFnYXRpb24oKTtjLmV2ZW50Lmdsb2JhbFtlXSYmYy5lYWNoKGMuY2FjaGUsZnVuY3Rpb24oKXt0aGlzLmV2ZW50cyYmdGhpcy5ldmVudHNbZV0mJmMuZXZlbnQudHJpZ2dlcihhLGIsdGhpcy5oYW5kbGUuZWxlbSl9KX1pZighZHx8ZC5ub2RlVHlwZT09PTN8fGQubm9kZVR5cGU9PT04KXJldHVybiB3O2EucmVzdWx0PXc7YS50YXJnZXQ9ZDtiPWMubWFrZUFycmF5KGIpO2IudW5zaGlmdChhKX1hLmN1cnJlbnRUYXJnZXQ9ZDsoZj1jLmRhdGEoZCwiaGFuZGxlIikpJiZmLmFwcGx5KGQsYik7Zj1kLnBhcmVudE5vZGV8fGQub3duZXJEb2N1bWVudDt0cnl7aWYoIShkJiZkLm5vZGVOYW1lJiZjLm5vRGF0YVtkLm5vZGVOYW1lLnRvTG93ZXJDYXNlKCldKSlpZihkWyJvbiIrZV0mJmRbIm9uIitlXS5hcHBseShkLGIpPT09ZmFsc2UpYS5yZXN1bHQ9ZmFsc2V9Y2F0Y2goail7fWlmKCFhLmlzUHJvcGFnYXRpb25TdG9wcGVkKCkmJg0KZiljLmV2ZW50LnRyaWdnZXIoYSxiLGYsdHJ1ZSk7ZWxzZSBpZighYS5pc0RlZmF1bHRQcmV2ZW50ZWQoKSl7Zj1hLnRhcmdldDt2YXIgaSxvPWMubm9kZU5hbWUoZiwiYSIpJiZlPT09ImNsaWNrIixrPWMuZXZlbnQuc3BlY2lhbFtlXXx8e307aWYoKCFrLl9kZWZhdWx0fHxrLl9kZWZhdWx0LmNhbGwoZCxhKT09PWZhbHNlKSYmIW8mJiEoZiYmZi5ub2RlTmFtZSYmYy5ub0RhdGFbZi5ub2RlTmFtZS50b0xvd2VyQ2FzZSgpXSkpe3RyeXtpZihmW2VdKXtpZihpPWZbIm9uIitlXSlmWyJvbiIrZV09bnVsbDtjLmV2ZW50LnRyaWdnZXJlZD10cnVlO2ZbZV0oKX19Y2F0Y2gobil7fWlmKGkpZlsib24iK2VdPWk7Yy5ldmVudC50cmlnZ2VyZWQ9ZmFsc2V9fX0saGFuZGxlOmZ1bmN0aW9uKGEpe3ZhciBiLGQsZixlO2E9YXJndW1lbnRzWzBdPWMuZXZlbnQuZml4KGF8fEEuZXZlbnQpO2EuY3VycmVudFRhcmdldD10aGlzO2I9YS50eXBlLmluZGV4T2YoIi4iKTwwJiYhYS5leGNsdXNpdmU7DQppZighYil7ZD1hLnR5cGUuc3BsaXQoIi4iKTthLnR5cGU9ZC5zaGlmdCgpO2Y9bmV3IFJlZ0V4cCgiKF58XFwuKSIrZC5zbGljZSgwKS5zb3J0KCkuam9pbigiXFwuKD86LipcXC4pPyIpKyIoXFwufCQpIil9ZT1jLmRhdGEodGhpcywiZXZlbnRzIik7ZD1lW2EudHlwZV07aWYoZSYmZCl7ZD1kLnNsaWNlKDApO2U9MDtmb3IodmFyIGo9ZC5sZW5ndGg7ZTxqO2UrKyl7dmFyIGk9ZFtlXTtpZihifHxmLnRlc3QoaS5uYW1lc3BhY2UpKXthLmhhbmRsZXI9aS5oYW5kbGVyO2EuZGF0YT1pLmRhdGE7YS5oYW5kbGVPYmo9aTtpPWkuaGFuZGxlci5hcHBseSh0aGlzLGFyZ3VtZW50cyk7aWYoaSE9PXcpe2EucmVzdWx0PWk7aWYoaT09PWZhbHNlKXthLnByZXZlbnREZWZhdWx0KCk7YS5zdG9wUHJvcGFnYXRpb24oKX19aWYoYS5pc0ltbWVkaWF0ZVByb3BhZ2F0aW9uU3RvcHBlZCgpKWJyZWFrfX19cmV0dXJuIGEucmVzdWx0fSxwcm9wczoiYWx0S2V5IGF0dHJDaGFuZ2UgYXR0ck5hbWUgYnViYmxlcyBidXR0b24gY2FuY2VsYWJsZSBjaGFyQ29kZSBjbGllbnRYIGNsaWVudFkgY3RybEtleSBjdXJyZW50VGFyZ2V0IGRhdGEgZGV0YWlsIGV2ZW50UGhhc2UgZnJvbUVsZW1lbnQgaGFuZGxlciBrZXlDb2RlIGxheWVyWCBsYXllclkgbWV0YUtleSBuZXdWYWx1ZSBvZmZzZXRYIG9mZnNldFkgb3JpZ2luYWxUYXJnZXQgcGFnZVggcGFnZVkgcHJldlZhbHVlIHJlbGF0ZWROb2RlIHJlbGF0ZWRUYXJnZXQgc2NyZWVuWCBzY3JlZW5ZIHNoaWZ0S2V5IHNyY0VsZW1lbnQgdGFyZ2V0IHRvRWxlbWVudCB2aWV3IHdoZWVsRGVsdGEgd2hpY2giLnNwbGl0KCIgIiksDQpmaXg6ZnVuY3Rpb24oYSl7aWYoYVtHXSlyZXR1cm4gYTt2YXIgYj1hO2E9Yy5FdmVudChiKTtmb3IodmFyIGQ9dGhpcy5wcm9wcy5sZW5ndGgsZjtkOyl7Zj10aGlzLnByb3BzWy0tZF07YVtmXT1iW2ZdfWlmKCFhLnRhcmdldClhLnRhcmdldD1hLnNyY0VsZW1lbnR8fHM7aWYoYS50YXJnZXQubm9kZVR5cGU9PT0zKWEudGFyZ2V0PWEudGFyZ2V0LnBhcmVudE5vZGU7aWYoIWEucmVsYXRlZFRhcmdldCYmYS5mcm9tRWxlbWVudClhLnJlbGF0ZWRUYXJnZXQ9YS5mcm9tRWxlbWVudD09PWEudGFyZ2V0P2EudG9FbGVtZW50OmEuZnJvbUVsZW1lbnQ7aWYoYS5wYWdlWD09bnVsbCYmYS5jbGllbnRYIT1udWxsKXtiPXMuZG9jdW1lbnRFbGVtZW50O2Q9cy5ib2R5O2EucGFnZVg9YS5jbGllbnRYKyhiJiZiLnNjcm9sbExlZnR8fGQmJmQuc2Nyb2xsTGVmdHx8MCktKGImJmIuY2xpZW50TGVmdHx8ZCYmZC5jbGllbnRMZWZ0fHwwKTthLnBhZ2VZPWEuY2xpZW50WSsoYiYmYi5zY3JvbGxUb3B8fA0KZCYmZC5zY3JvbGxUb3B8fDApLShiJiZiLmNsaWVudFRvcHx8ZCYmZC5jbGllbnRUb3B8fDApfWlmKCFhLndoaWNoJiYoYS5jaGFyQ29kZXx8YS5jaGFyQ29kZT09PTA/YS5jaGFyQ29kZTphLmtleUNvZGUpKWEud2hpY2g9YS5jaGFyQ29kZXx8YS5rZXlDb2RlO2lmKCFhLm1ldGFLZXkmJmEuY3RybEtleSlhLm1ldGFLZXk9YS5jdHJsS2V5O2lmKCFhLndoaWNoJiZhLmJ1dHRvbiE9PXcpYS53aGljaD1hLmJ1dHRvbiYxPzE6YS5idXR0b24mMj8zOmEuYnV0dG9uJjQ/MjowO3JldHVybiBhfSxndWlkOjFFOCxwcm94eTpjLnByb3h5LHNwZWNpYWw6e3JlYWR5OntzZXR1cDpjLmJpbmRSZWFkeSx0ZWFyZG93bjpjLm5vb3B9LGxpdmU6e2FkZDpmdW5jdGlvbihhKXtjLmV2ZW50LmFkZCh0aGlzLGEub3JpZ1R5cGUsYy5leHRlbmQoe30sYSx7aGFuZGxlcjpvYX0pKX0scmVtb3ZlOmZ1bmN0aW9uKGEpe3ZhciBiPXRydWUsZD1hLm9yaWdUeXBlLnJlcGxhY2UoTywiIik7Yy5lYWNoKGMuZGF0YSh0aGlzLA0KImV2ZW50cyIpLmxpdmV8fFtdLGZ1bmN0aW9uKCl7aWYoZD09PXRoaXMub3JpZ1R5cGUucmVwbGFjZShPLCIiKSlyZXR1cm4gYj1mYWxzZX0pO2ImJmMuZXZlbnQucmVtb3ZlKHRoaXMsYS5vcmlnVHlwZSxvYSl9fSxiZWZvcmV1bmxvYWQ6e3NldHVwOmZ1bmN0aW9uKGEsYixkKXtpZih0aGlzLnNldEludGVydmFsKXRoaXMub25iZWZvcmV1bmxvYWQ9ZDtyZXR1cm4gZmFsc2V9LHRlYXJkb3duOmZ1bmN0aW9uKGEsYil7aWYodGhpcy5vbmJlZm9yZXVubG9hZD09PWIpdGhpcy5vbmJlZm9yZXVubG9hZD1udWxsfX19fTt2YXIgQ2E9cy5yZW1vdmVFdmVudExpc3RlbmVyP2Z1bmN0aW9uKGEsYixkKXthLnJlbW92ZUV2ZW50TGlzdGVuZXIoYixkLGZhbHNlKX06ZnVuY3Rpb24oYSxiLGQpe2EuZGV0YWNoRXZlbnQoIm9uIitiLGQpfTtjLkV2ZW50PWZ1bmN0aW9uKGEpe2lmKCF0aGlzLnByZXZlbnREZWZhdWx0KXJldHVybiBuZXcgYy5FdmVudChhKTtpZihhJiZhLnR5cGUpe3RoaXMub3JpZ2luYWxFdmVudD0NCmE7dGhpcy50eXBlPWEudHlwZX1lbHNlIHRoaXMudHlwZT1hO3RoaXMudGltZVN0YW1wPUooKTt0aGlzW0ddPXRydWV9O2MuRXZlbnQucHJvdG90eXBlPXtwcmV2ZW50RGVmYXVsdDpmdW5jdGlvbigpe3RoaXMuaXNEZWZhdWx0UHJldmVudGVkPVo7dmFyIGE9dGhpcy5vcmlnaW5hbEV2ZW50O2lmKGEpe2EucHJldmVudERlZmF1bHQmJmEucHJldmVudERlZmF1bHQoKTthLnJldHVyblZhbHVlPWZhbHNlfX0sc3RvcFByb3BhZ2F0aW9uOmZ1bmN0aW9uKCl7dGhpcy5pc1Byb3BhZ2F0aW9uU3RvcHBlZD1aO3ZhciBhPXRoaXMub3JpZ2luYWxFdmVudDtpZihhKXthLnN0b3BQcm9wYWdhdGlvbiYmYS5zdG9wUHJvcGFnYXRpb24oKTthLmNhbmNlbEJ1YmJsZT10cnVlfX0sc3RvcEltbWVkaWF0ZVByb3BhZ2F0aW9uOmZ1bmN0aW9uKCl7dGhpcy5pc0ltbWVkaWF0ZVByb3BhZ2F0aW9uU3RvcHBlZD1aO3RoaXMuc3RvcFByb3BhZ2F0aW9uKCl9LGlzRGVmYXVsdFByZXZlbnRlZDpZLGlzUHJvcGFnYXRpb25TdG9wcGVkOlksDQppc0ltbWVkaWF0ZVByb3BhZ2F0aW9uU3RvcHBlZDpZfTt2YXIgRGE9ZnVuY3Rpb24oYSl7dmFyIGI9YS5yZWxhdGVkVGFyZ2V0O3RyeXtmb3IoO2ImJmIhPT10aGlzOyliPWIucGFyZW50Tm9kZTtpZihiIT09dGhpcyl7YS50eXBlPWEuZGF0YTtjLmV2ZW50LmhhbmRsZS5hcHBseSh0aGlzLGFyZ3VtZW50cyl9fWNhdGNoKGQpe319LEVhPWZ1bmN0aW9uKGEpe2EudHlwZT1hLmRhdGE7Yy5ldmVudC5oYW5kbGUuYXBwbHkodGhpcyxhcmd1bWVudHMpfTtjLmVhY2goe21vdXNlZW50ZXI6Im1vdXNlb3ZlciIsbW91c2VsZWF2ZToibW91c2VvdXQifSxmdW5jdGlvbihhLGIpe2MuZXZlbnQuc3BlY2lhbFthXT17c2V0dXA6ZnVuY3Rpb24oZCl7Yy5ldmVudC5hZGQodGhpcyxiLGQmJmQuc2VsZWN0b3I/RWE6RGEsYSl9LHRlYXJkb3duOmZ1bmN0aW9uKGQpe2MuZXZlbnQucmVtb3ZlKHRoaXMsYixkJiZkLnNlbGVjdG9yP0VhOkRhKX19fSk7aWYoIWMuc3VwcG9ydC5zdWJtaXRCdWJibGVzKWMuZXZlbnQuc3BlY2lhbC5zdWJtaXQ9DQp7c2V0dXA6ZnVuY3Rpb24oKXtpZih0aGlzLm5vZGVOYW1lLnRvTG93ZXJDYXNlKCkhPT0iZm9ybSIpe2MuZXZlbnQuYWRkKHRoaXMsImNsaWNrLnNwZWNpYWxTdWJtaXQiLGZ1bmN0aW9uKGEpe3ZhciBiPWEudGFyZ2V0LGQ9Yi50eXBlO2lmKChkPT09InN1Ym1pdCJ8fGQ9PT0iaW1hZ2UiKSYmYyhiKS5jbG9zZXN0KCJmb3JtIikubGVuZ3RoKXJldHVybiBuYSgic3VibWl0Iix0aGlzLGFyZ3VtZW50cyl9KTtjLmV2ZW50LmFkZCh0aGlzLCJrZXlwcmVzcy5zcGVjaWFsU3VibWl0IixmdW5jdGlvbihhKXt2YXIgYj1hLnRhcmdldCxkPWIudHlwZTtpZigoZD09PSJ0ZXh0Inx8ZD09PSJwYXNzd29yZCIpJiZjKGIpLmNsb3Nlc3QoImZvcm0iKS5sZW5ndGgmJmEua2V5Q29kZT09PTEzKXJldHVybiBuYSgic3VibWl0Iix0aGlzLGFyZ3VtZW50cyl9KX1lbHNlIHJldHVybiBmYWxzZX0sdGVhcmRvd246ZnVuY3Rpb24oKXtjLmV2ZW50LnJlbW92ZSh0aGlzLCIuc3BlY2lhbFN1Ym1pdCIpfX07DQppZighYy5zdXBwb3J0LmNoYW5nZUJ1YmJsZXMpe3ZhciBkYT0vdGV4dGFyZWF8aW5wdXR8c2VsZWN0L2ksZWEsRmE9ZnVuY3Rpb24oYSl7dmFyIGI9YS50eXBlLGQ9YS52YWx1ZTtpZihiPT09InJhZGlvInx8Yj09PSJjaGVja2JveCIpZD1hLmNoZWNrZWQ7ZWxzZSBpZihiPT09InNlbGVjdC1tdWx0aXBsZSIpZD1hLnNlbGVjdGVkSW5kZXg+LTE/Yy5tYXAoYS5vcHRpb25zLGZ1bmN0aW9uKGYpe3JldHVybiBmLnNlbGVjdGVkfSkuam9pbigiLSIpOiIiO2Vsc2UgaWYoYS5ub2RlTmFtZS50b0xvd2VyQ2FzZSgpPT09InNlbGVjdCIpZD1hLnNlbGVjdGVkSW5kZXg7cmV0dXJuIGR9LGZhPWZ1bmN0aW9uKGEsYil7dmFyIGQ9YS50YXJnZXQsZixlO2lmKCEoIWRhLnRlc3QoZC5ub2RlTmFtZSl8fGQucmVhZE9ubHkpKXtmPWMuZGF0YShkLCJfY2hhbmdlX2RhdGEiKTtlPUZhKGQpO2lmKGEudHlwZSE9PSJmb2N1c291dCJ8fGQudHlwZSE9PSJyYWRpbyIpYy5kYXRhKGQsIl9jaGFuZ2VfZGF0YSIsDQplKTtpZighKGY9PT13fHxlPT09ZikpaWYoZiE9bnVsbHx8ZSl7YS50eXBlPSJjaGFuZ2UiO3JldHVybiBjLmV2ZW50LnRyaWdnZXIoYSxiLGQpfX19O2MuZXZlbnQuc3BlY2lhbC5jaGFuZ2U9e2ZpbHRlcnM6e2ZvY3Vzb3V0OmZhLGNsaWNrOmZ1bmN0aW9uKGEpe3ZhciBiPWEudGFyZ2V0LGQ9Yi50eXBlO2lmKGQ9PT0icmFkaW8ifHxkPT09ImNoZWNrYm94Inx8Yi5ub2RlTmFtZS50b0xvd2VyQ2FzZSgpPT09InNlbGVjdCIpcmV0dXJuIGZhLmNhbGwodGhpcyxhKX0sa2V5ZG93bjpmdW5jdGlvbihhKXt2YXIgYj1hLnRhcmdldCxkPWIudHlwZTtpZihhLmtleUNvZGU9PT0xMyYmYi5ub2RlTmFtZS50b0xvd2VyQ2FzZSgpIT09InRleHRhcmVhInx8YS5rZXlDb2RlPT09MzImJihkPT09ImNoZWNrYm94Inx8ZD09PSJyYWRpbyIpfHxkPT09InNlbGVjdC1tdWx0aXBsZSIpcmV0dXJuIGZhLmNhbGwodGhpcyxhKX0sYmVmb3JlYWN0aXZhdGU6ZnVuY3Rpb24oYSl7YT1hLnRhcmdldDtjLmRhdGEoYSwNCiJfY2hhbmdlX2RhdGEiLEZhKGEpKX19LHNldHVwOmZ1bmN0aW9uKCl7aWYodGhpcy50eXBlPT09ImZpbGUiKXJldHVybiBmYWxzZTtmb3IodmFyIGEgaW4gZWEpYy5ldmVudC5hZGQodGhpcyxhKyIuc3BlY2lhbENoYW5nZSIsZWFbYV0pO3JldHVybiBkYS50ZXN0KHRoaXMubm9kZU5hbWUpfSx0ZWFyZG93bjpmdW5jdGlvbigpe2MuZXZlbnQucmVtb3ZlKHRoaXMsIi5zcGVjaWFsQ2hhbmdlIik7cmV0dXJuIGRhLnRlc3QodGhpcy5ub2RlTmFtZSl9fTtlYT1jLmV2ZW50LnNwZWNpYWwuY2hhbmdlLmZpbHRlcnN9cy5hZGRFdmVudExpc3RlbmVyJiZjLmVhY2goe2ZvY3VzOiJmb2N1c2luIixibHVyOiJmb2N1c291dCJ9LGZ1bmN0aW9uKGEsYil7ZnVuY3Rpb24gZChmKXtmPWMuZXZlbnQuZml4KGYpO2YudHlwZT1iO3JldHVybiBjLmV2ZW50LmhhbmRsZS5jYWxsKHRoaXMsZil9Yy5ldmVudC5zcGVjaWFsW2JdPXtzZXR1cDpmdW5jdGlvbigpe3RoaXMuYWRkRXZlbnRMaXN0ZW5lcihhLA0KZCx0cnVlKX0sdGVhcmRvd246ZnVuY3Rpb24oKXt0aGlzLnJlbW92ZUV2ZW50TGlzdGVuZXIoYSxkLHRydWUpfX19KTtjLmVhY2goWyJiaW5kIiwib25lIl0sZnVuY3Rpb24oYSxiKXtjLmZuW2JdPWZ1bmN0aW9uKGQsZixlKXtpZih0eXBlb2YgZD09PSJvYmplY3QiKXtmb3IodmFyIGogaW4gZCl0aGlzW2JdKGosZixkW2pdLGUpO3JldHVybiB0aGlzfWlmKGMuaXNGdW5jdGlvbihmKSl7ZT1mO2Y9d312YXIgaT1iPT09Im9uZSI/Yy5wcm94eShlLGZ1bmN0aW9uKGspe2ModGhpcykudW5iaW5kKGssaSk7cmV0dXJuIGUuYXBwbHkodGhpcyxhcmd1bWVudHMpfSk6ZTtpZihkPT09InVubG9hZCImJmIhPT0ib25lIil0aGlzLm9uZShkLGYsZSk7ZWxzZXtqPTA7Zm9yKHZhciBvPXRoaXMubGVuZ3RoO2o8bztqKyspYy5ldmVudC5hZGQodGhpc1tqXSxkLGksZil9cmV0dXJuIHRoaXN9fSk7Yy5mbi5leHRlbmQoe3VuYmluZDpmdW5jdGlvbihhLGIpe2lmKHR5cGVvZiBhPT09Im9iamVjdCImJg0KIWEucHJldmVudERlZmF1bHQpZm9yKHZhciBkIGluIGEpdGhpcy51bmJpbmQoZCxhW2RdKTtlbHNle2Q9MDtmb3IodmFyIGY9dGhpcy5sZW5ndGg7ZDxmO2QrKyljLmV2ZW50LnJlbW92ZSh0aGlzW2RdLGEsYil9cmV0dXJuIHRoaXN9LGRlbGVnYXRlOmZ1bmN0aW9uKGEsYixkLGYpe3JldHVybiB0aGlzLmxpdmUoYixkLGYsYSl9LHVuZGVsZWdhdGU6ZnVuY3Rpb24oYSxiLGQpe3JldHVybiBhcmd1bWVudHMubGVuZ3RoPT09MD90aGlzLnVuYmluZCgibGl2ZSIpOnRoaXMuZGllKGIsbnVsbCxkLGEpfSx0cmlnZ2VyOmZ1bmN0aW9uKGEsYil7cmV0dXJuIHRoaXMuZWFjaChmdW5jdGlvbigpe2MuZXZlbnQudHJpZ2dlcihhLGIsdGhpcyl9KX0sdHJpZ2dlckhhbmRsZXI6ZnVuY3Rpb24oYSxiKXtpZih0aGlzWzBdKXthPWMuRXZlbnQoYSk7YS5wcmV2ZW50RGVmYXVsdCgpO2Euc3RvcFByb3BhZ2F0aW9uKCk7Yy5ldmVudC50cmlnZ2VyKGEsYix0aGlzWzBdKTtyZXR1cm4gYS5yZXN1bHR9fSwNCnRvZ2dsZTpmdW5jdGlvbihhKXtmb3IodmFyIGI9YXJndW1lbnRzLGQ9MTtkPGIubGVuZ3RoOyljLnByb3h5KGEsYltkKytdKTtyZXR1cm4gdGhpcy5jbGljayhjLnByb3h5KGEsZnVuY3Rpb24oZil7dmFyIGU9KGMuZGF0YSh0aGlzLCJsYXN0VG9nZ2xlIithLmd1aWQpfHwwKSVkO2MuZGF0YSh0aGlzLCJsYXN0VG9nZ2xlIithLmd1aWQsZSsxKTtmLnByZXZlbnREZWZhdWx0KCk7cmV0dXJuIGJbZV0uYXBwbHkodGhpcyxhcmd1bWVudHMpfHxmYWxzZX0pKX0saG92ZXI6ZnVuY3Rpb24oYSxiKXtyZXR1cm4gdGhpcy5tb3VzZWVudGVyKGEpLm1vdXNlbGVhdmUoYnx8YSl9fSk7dmFyIEdhPXtmb2N1czoiZm9jdXNpbiIsYmx1cjoiZm9jdXNvdXQiLG1vdXNlZW50ZXI6Im1vdXNlb3ZlciIsbW91c2VsZWF2ZToibW91c2VvdXQifTtjLmVhY2goWyJsaXZlIiwiZGllIl0sZnVuY3Rpb24oYSxiKXtjLmZuW2JdPWZ1bmN0aW9uKGQsZixlLGope3ZhciBpLG89MCxrLG4scj1qfHx0aGlzLnNlbGVjdG9yLA0KdT1qP3RoaXM6Yyh0aGlzLmNvbnRleHQpO2lmKGMuaXNGdW5jdGlvbihmKSl7ZT1mO2Y9d31mb3IoZD0oZHx8IiIpLnNwbGl0KCIgIik7KGk9ZFtvKytdKSE9bnVsbDspe2o9Ty5leGVjKGkpO2s9IiI7aWYoail7az1qWzBdO2k9aS5yZXBsYWNlKE8sIiIpfWlmKGk9PT0iaG92ZXIiKWQucHVzaCgibW91c2VlbnRlciIraywibW91c2VsZWF2ZSIrayk7ZWxzZXtuPWk7aWYoaT09PSJmb2N1cyJ8fGk9PT0iYmx1ciIpe2QucHVzaChHYVtpXStrKTtpKz1rfWVsc2UgaT0oR2FbaV18fGkpK2s7Yj09PSJsaXZlIj91LmVhY2goZnVuY3Rpb24oKXtjLmV2ZW50LmFkZCh0aGlzLHBhKGkscikse2RhdGE6ZixzZWxlY3RvcjpyLGhhbmRsZXI6ZSxvcmlnVHlwZTppLG9yaWdIYW5kbGVyOmUscHJlVHlwZTpufSl9KTp1LnVuYmluZChwYShpLHIpLGUpfX1yZXR1cm4gdGhpc319KTtjLmVhY2goImJsdXIgZm9jdXMgZm9jdXNpbiBmb2N1c291dCBsb2FkIHJlc2l6ZSBzY3JvbGwgdW5sb2FkIGNsaWNrIGRibGNsaWNrIG1vdXNlZG93biBtb3VzZXVwIG1vdXNlbW92ZSBtb3VzZW92ZXIgbW91c2VvdXQgbW91c2VlbnRlciBtb3VzZWxlYXZlIGNoYW5nZSBzZWxlY3Qgc3VibWl0IGtleWRvd24ga2V5cHJlc3Mga2V5dXAgZXJyb3IiLnNwbGl0KCIgIiksDQpmdW5jdGlvbihhLGIpe2MuZm5bYl09ZnVuY3Rpb24oZCl7cmV0dXJuIGQ/dGhpcy5iaW5kKGIsZCk6dGhpcy50cmlnZ2VyKGIpfTtpZihjLmF0dHJGbiljLmF0dHJGbltiXT10cnVlfSk7QS5hdHRhY2hFdmVudCYmIUEuYWRkRXZlbnRMaXN0ZW5lciYmQS5hdHRhY2hFdmVudCgib251bmxvYWQiLGZ1bmN0aW9uKCl7Zm9yKHZhciBhIGluIGMuY2FjaGUpaWYoYy5jYWNoZVthXS5oYW5kbGUpdHJ5e2MuZXZlbnQucmVtb3ZlKGMuY2FjaGVbYV0uaGFuZGxlLmVsZW0pfWNhdGNoKGIpe319KTsoZnVuY3Rpb24oKXtmdW5jdGlvbiBhKGcpe2Zvcih2YXIgaD0iIixsLG09MDtnW21dO20rKyl7bD1nW21dO2lmKGwubm9kZVR5cGU9PT0zfHxsLm5vZGVUeXBlPT09NCloKz1sLm5vZGVWYWx1ZTtlbHNlIGlmKGwubm9kZVR5cGUhPT04KWgrPWEobC5jaGlsZE5vZGVzKX1yZXR1cm4gaH1mdW5jdGlvbiBiKGcsaCxsLG0scSxwKXtxPTA7Zm9yKHZhciB2PW0ubGVuZ3RoO3E8djtxKyspe3ZhciB0PW1bcV07DQppZih0KXt0PXRbZ107Zm9yKHZhciB5PWZhbHNlO3Q7KXtpZih0LnNpemNhY2hlPT09bCl7eT1tW3Quc2l6c2V0XTticmVha31pZih0Lm5vZGVUeXBlPT09MSYmIXApe3Quc2l6Y2FjaGU9bDt0LnNpenNldD1xfWlmKHQubm9kZU5hbWUudG9Mb3dlckNhc2UoKT09PWgpe3k9dDticmVha310PXRbZ119bVtxXT15fX19ZnVuY3Rpb24gZChnLGgsbCxtLHEscCl7cT0wO2Zvcih2YXIgdj1tLmxlbmd0aDtxPHY7cSsrKXt2YXIgdD1tW3FdO2lmKHQpe3Q9dFtnXTtmb3IodmFyIHk9ZmFsc2U7dDspe2lmKHQuc2l6Y2FjaGU9PT1sKXt5PW1bdC5zaXpzZXRdO2JyZWFrfWlmKHQubm9kZVR5cGU9PT0xKXtpZighcCl7dC5zaXpjYWNoZT1sO3Quc2l6c2V0PXF9aWYodHlwZW9mIGghPT0ic3RyaW5nIil7aWYodD09PWgpe3k9dHJ1ZTticmVha319ZWxzZSBpZihrLmZpbHRlcihoLFt0XSkubGVuZ3RoPjApe3k9dDticmVha319dD10W2ddfW1bcV09eX19fXZhciBmPS8oKD86XCgoPzpcKFteKCldK1wpfFteKCldKykrXCl8XFsoPzpcW1teW1xdXSpcXXxbJyJdW14nIl0qWyciXXxbXltcXSciXSspK1xdfFxcLnxbXiA+K34sKFxbXFxdKykrfFs+K35dKShccyosXHMqKT8oKD86LnxccnxcbikqKS9nLA0KZT0wLGo9T2JqZWN0LnByb3RvdHlwZS50b1N0cmluZyxpPWZhbHNlLG89dHJ1ZTtbMCwwXS5zb3J0KGZ1bmN0aW9uKCl7bz1mYWxzZTtyZXR1cm4gMH0pO3ZhciBrPWZ1bmN0aW9uKGcsaCxsLG0pe2w9bHx8W107dmFyIHE9aD1ofHxzO2lmKGgubm9kZVR5cGUhPT0xJiZoLm5vZGVUeXBlIT09OSlyZXR1cm5bXTtpZighZ3x8dHlwZW9mIGchPT0ic3RyaW5nIilyZXR1cm4gbDtmb3IodmFyIHA9W10sdix0LHksUyxIPXRydWUsTT14KGgpLEk9ZzsoZi5leGVjKCIiKSx2PWYuZXhlYyhJKSkhPT1udWxsOyl7ST12WzNdO3AucHVzaCh2WzFdKTtpZih2WzJdKXtTPXZbM107YnJlYWt9fWlmKHAubGVuZ3RoPjEmJnIuZXhlYyhnKSlpZihwLmxlbmd0aD09PTImJm4ucmVsYXRpdmVbcFswXV0pdD1nYShwWzBdK3BbMV0saCk7ZWxzZSBmb3IodD1uLnJlbGF0aXZlW3BbMF1dP1toXTprKHAuc2hpZnQoKSxoKTtwLmxlbmd0aDspe2c9cC5zaGlmdCgpO2lmKG4ucmVsYXRpdmVbZ10pZys9cC5zaGlmdCgpOw0KdD1nYShnLHQpfWVsc2V7aWYoIW0mJnAubGVuZ3RoPjEmJmgubm9kZVR5cGU9PT05JiYhTSYmbi5tYXRjaC5JRC50ZXN0KHBbMF0pJiYhbi5tYXRjaC5JRC50ZXN0KHBbcC5sZW5ndGgtMV0pKXt2PWsuZmluZChwLnNoaWZ0KCksaCxNKTtoPXYuZXhwcj9rLmZpbHRlcih2LmV4cHIsdi5zZXQpWzBdOnYuc2V0WzBdfWlmKGgpe3Y9bT97ZXhwcjpwLnBvcCgpLHNldDp6KG0pfTprLmZpbmQocC5wb3AoKSxwLmxlbmd0aD09PTEmJihwWzBdPT09In4ifHxwWzBdPT09IisiKSYmaC5wYXJlbnROb2RlP2gucGFyZW50Tm9kZTpoLE0pO3Q9di5leHByP2suZmlsdGVyKHYuZXhwcix2LnNldCk6di5zZXQ7aWYocC5sZW5ndGg+MCl5PXoodCk7ZWxzZSBIPWZhbHNlO2Zvcig7cC5sZW5ndGg7KXt2YXIgRD1wLnBvcCgpO3Y9RDtpZihuLnJlbGF0aXZlW0RdKXY9cC5wb3AoKTtlbHNlIEQ9IiI7aWYodj09bnVsbCl2PWg7bi5yZWxhdGl2ZVtEXSh5LHYsTSl9fWVsc2UgeT1bXX15fHwoeT10KTt5fHxrLmVycm9yKER8fA0KZyk7aWYoai5jYWxsKHkpPT09IltvYmplY3QgQXJyYXldIilpZihIKWlmKGgmJmgubm9kZVR5cGU9PT0xKWZvcihnPTA7eVtnXSE9bnVsbDtnKyspe2lmKHlbZ10mJih5W2ddPT09dHJ1ZXx8eVtnXS5ub2RlVHlwZT09PTEmJkUoaCx5W2ddKSkpbC5wdXNoKHRbZ10pfWVsc2UgZm9yKGc9MDt5W2ddIT1udWxsO2crKyl5W2ddJiZ5W2ddLm5vZGVUeXBlPT09MSYmbC5wdXNoKHRbZ10pO2Vsc2UgbC5wdXNoLmFwcGx5KGwseSk7ZWxzZSB6KHksbCk7aWYoUyl7ayhTLHEsbCxtKTtrLnVuaXF1ZVNvcnQobCl9cmV0dXJuIGx9O2sudW5pcXVlU29ydD1mdW5jdGlvbihnKXtpZihCKXtpPW87Zy5zb3J0KEIpO2lmKGkpZm9yKHZhciBoPTE7aDxnLmxlbmd0aDtoKyspZ1toXT09PWdbaC0xXSYmZy5zcGxpY2UoaC0tLDEpfXJldHVybiBnfTtrLm1hdGNoZXM9ZnVuY3Rpb24oZyxoKXtyZXR1cm4gayhnLG51bGwsbnVsbCxoKX07ay5maW5kPWZ1bmN0aW9uKGcsaCxsKXt2YXIgbSxxO2lmKCFnKXJldHVybltdOw0KZm9yKHZhciBwPTAsdj1uLm9yZGVyLmxlbmd0aDtwPHY7cCsrKXt2YXIgdD1uLm9yZGVyW3BdO2lmKHE9bi5sZWZ0TWF0Y2hbdF0uZXhlYyhnKSl7dmFyIHk9cVsxXTtxLnNwbGljZSgxLDEpO2lmKHkuc3Vic3RyKHkubGVuZ3RoLTEpIT09IlxcIil7cVsxXT0ocVsxXXx8IiIpLnJlcGxhY2UoL1xcL2csIiIpO209bi5maW5kW3RdKHEsaCxsKTtpZihtIT1udWxsKXtnPWcucmVwbGFjZShuLm1hdGNoW3RdLCIiKTticmVha319fX1tfHwobT1oLmdldEVsZW1lbnRzQnlUYWdOYW1lKCIqIikpO3JldHVybntzZXQ6bSxleHByOmd9fTtrLmZpbHRlcj1mdW5jdGlvbihnLGgsbCxtKXtmb3IodmFyIHE9ZyxwPVtdLHY9aCx0LHksUz1oJiZoWzBdJiZ4KGhbMF0pO2cmJmgubGVuZ3RoOyl7Zm9yKHZhciBIIGluIG4uZmlsdGVyKWlmKCh0PW4ubGVmdE1hdGNoW0hdLmV4ZWMoZykpIT1udWxsJiZ0WzJdKXt2YXIgTT1uLmZpbHRlcltIXSxJLEQ7RD10WzFdO3k9ZmFsc2U7dC5zcGxpY2UoMSwxKTtpZihELnN1YnN0cihELmxlbmd0aC0NCjEpIT09IlxcIil7aWYodj09PXApcD1bXTtpZihuLnByZUZpbHRlcltIXSlpZih0PW4ucHJlRmlsdGVyW0hdKHQsdixsLHAsbSxTKSl7aWYodD09PXRydWUpY29udGludWV9ZWxzZSB5PUk9dHJ1ZTtpZih0KWZvcih2YXIgVT0wOyhEPXZbVV0pIT1udWxsO1UrKylpZihEKXtJPU0oRCx0LFUsdik7dmFyIEhhPW1eISFJO2lmKGwmJkkhPW51bGwpaWYoSGEpeT10cnVlO2Vsc2UgdltVXT1mYWxzZTtlbHNlIGlmKEhhKXtwLnB1c2goRCk7eT10cnVlfX1pZihJIT09dyl7bHx8KHY9cCk7Zz1nLnJlcGxhY2Uobi5tYXRjaFtIXSwiIik7aWYoIXkpcmV0dXJuW107YnJlYWt9fX1pZihnPT09cSlpZih5PT1udWxsKWsuZXJyb3IoZyk7ZWxzZSBicmVhaztxPWd9cmV0dXJuIHZ9O2suZXJyb3I9ZnVuY3Rpb24oZyl7dGhyb3ciU3ludGF4IGVycm9yLCB1bnJlY29nbml6ZWQgZXhwcmVzc2lvbjogIitnO307dmFyIG49ay5zZWxlY3RvcnM9e29yZGVyOlsiSUQiLCJOQU1FIiwiVEFHIl0sbWF0Y2g6e0lEOi8jKCg/Oltcd1x1MDBjMC1cdUZGRkYtXXxcXC4pKykvLA0KQ0xBU1M6L1wuKCg/Oltcd1x1MDBjMC1cdUZGRkYtXXxcXC4pKykvLE5BTUU6L1xbbmFtZT1bJyJdKigoPzpbXHdcdTAwYzAtXHVGRkZGLV18XFwuKSspWyciXSpcXS8sQVRUUjovXFtccyooKD86W1x3XHUwMGMwLVx1RkZGRi1dfFxcLikrKVxzKig/OihcUz89KVxzKihbJyJdKikoLio/KVwzfClccypcXS8sVEFHOi9eKCg/Oltcd1x1MDBjMC1cdUZGRkZcKi1dfFxcLikrKS8sQ0hJTEQ6Lzoob25seXxudGh8bGFzdHxmaXJzdCktY2hpbGQoPzpcKChldmVufG9kZHxbXGRuKy1dKilcKSk/LyxQT1M6LzoobnRofGVxfGd0fGx0fGZpcnN0fGxhc3R8ZXZlbnxvZGQpKD86XCgoXGQqKVwpKT8oPz1bXi1dfCQpLyxQU0VVRE86LzooKD86W1x3XHUwMGMwLVx1RkZGRi1dfFxcLikrKSg/OlwoKFsnIl0/KSgoPzpcKFteXCldK1wpfFteXChcKV0qKSspXDJcKSk/L30sbGVmdE1hdGNoOnt9LGF0dHJNYXA6eyJjbGFzcyI6ImNsYXNzTmFtZSIsImZvciI6Imh0bWxGb3IifSxhdHRySGFuZGxlOntocmVmOmZ1bmN0aW9uKGcpe3JldHVybiBnLmdldEF0dHJpYnV0ZSgiaHJlZiIpfX0sDQpyZWxhdGl2ZTp7IisiOmZ1bmN0aW9uKGcsaCl7dmFyIGw9dHlwZW9mIGg9PT0ic3RyaW5nIixtPWwmJiEvXFcvLnRlc3QoaCk7bD1sJiYhbTtpZihtKWg9aC50b0xvd2VyQ2FzZSgpO209MDtmb3IodmFyIHE9Zy5sZW5ndGgscDttPHE7bSsrKWlmKHA9Z1ttXSl7Zm9yKDsocD1wLnByZXZpb3VzU2libGluZykmJnAubm9kZVR5cGUhPT0xOyk7Z1ttXT1sfHxwJiZwLm5vZGVOYW1lLnRvTG93ZXJDYXNlKCk9PT1oP3B8fGZhbHNlOnA9PT1ofWwmJmsuZmlsdGVyKGgsZyx0cnVlKX0sIj4iOmZ1bmN0aW9uKGcsaCl7dmFyIGw9dHlwZW9mIGg9PT0ic3RyaW5nIjtpZihsJiYhL1xXLy50ZXN0KGgpKXtoPWgudG9Mb3dlckNhc2UoKTtmb3IodmFyIG09MCxxPWcubGVuZ3RoO208cTttKyspe3ZhciBwPWdbbV07aWYocCl7bD1wLnBhcmVudE5vZGU7Z1ttXT1sLm5vZGVOYW1lLnRvTG93ZXJDYXNlKCk9PT1oP2w6ZmFsc2V9fX1lbHNle209MDtmb3IocT1nLmxlbmd0aDttPHE7bSsrKWlmKHA9Z1ttXSlnW21dPQ0KbD9wLnBhcmVudE5vZGU6cC5wYXJlbnROb2RlPT09aDtsJiZrLmZpbHRlcihoLGcsdHJ1ZSl9fSwiIjpmdW5jdGlvbihnLGgsbCl7dmFyIG09ZSsrLHE9ZDtpZih0eXBlb2YgaD09PSJzdHJpbmciJiYhL1xXLy50ZXN0KGgpKXt2YXIgcD1oPWgudG9Mb3dlckNhc2UoKTtxPWJ9cSgicGFyZW50Tm9kZSIsaCxtLGcscCxsKX0sIn4iOmZ1bmN0aW9uKGcsaCxsKXt2YXIgbT1lKysscT1kO2lmKHR5cGVvZiBoPT09InN0cmluZyImJiEvXFcvLnRlc3QoaCkpe3ZhciBwPWg9aC50b0xvd2VyQ2FzZSgpO3E9Yn1xKCJwcmV2aW91c1NpYmxpbmciLGgsbSxnLHAsbCl9fSxmaW5kOntJRDpmdW5jdGlvbihnLGgsbCl7aWYodHlwZW9mIGguZ2V0RWxlbWVudEJ5SWQhPT0idW5kZWZpbmVkIiYmIWwpcmV0dXJuKGc9aC5nZXRFbGVtZW50QnlJZChnWzFdKSk/W2ddOltdfSxOQU1FOmZ1bmN0aW9uKGcsaCl7aWYodHlwZW9mIGguZ2V0RWxlbWVudHNCeU5hbWUhPT0idW5kZWZpbmVkIil7dmFyIGw9W107DQpoPWguZ2V0RWxlbWVudHNCeU5hbWUoZ1sxXSk7Zm9yKHZhciBtPTAscT1oLmxlbmd0aDttPHE7bSsrKWhbbV0uZ2V0QXR0cmlidXRlKCJuYW1lIik9PT1nWzFdJiZsLnB1c2goaFttXSk7cmV0dXJuIGwubGVuZ3RoPT09MD9udWxsOmx9fSxUQUc6ZnVuY3Rpb24oZyxoKXtyZXR1cm4gaC5nZXRFbGVtZW50c0J5VGFnTmFtZShnWzFdKX19LHByZUZpbHRlcjp7Q0xBU1M6ZnVuY3Rpb24oZyxoLGwsbSxxLHApe2c9IiAiK2dbMV0ucmVwbGFjZSgvXFwvZywiIikrIiAiO2lmKHApcmV0dXJuIGc7cD0wO2Zvcih2YXIgdjsodj1oW3BdKSE9bnVsbDtwKyspaWYodilpZihxXih2LmNsYXNzTmFtZSYmKCIgIit2LmNsYXNzTmFtZSsiICIpLnJlcGxhY2UoL1tcdFxuXS9nLCIgIikuaW5kZXhPZihnKT49MCkpbHx8bS5wdXNoKHYpO2Vsc2UgaWYobCloW3BdPWZhbHNlO3JldHVybiBmYWxzZX0sSUQ6ZnVuY3Rpb24oZyl7cmV0dXJuIGdbMV0ucmVwbGFjZSgvXFwvZywiIil9LFRBRzpmdW5jdGlvbihnKXtyZXR1cm4gZ1sxXS50b0xvd2VyQ2FzZSgpfSwNCkNISUxEOmZ1bmN0aW9uKGcpe2lmKGdbMV09PT0ibnRoIil7dmFyIGg9LygtPykoXGQqKW4oKD86XCt8LSk/XGQqKS8uZXhlYyhnWzJdPT09ImV2ZW4iJiYiMm4ifHxnWzJdPT09Im9kZCImJiIybisxInx8IS9cRC8udGVzdChnWzJdKSYmIjBuKyIrZ1syXXx8Z1syXSk7Z1syXT1oWzFdKyhoWzJdfHwxKS0wO2dbM109aFszXS0wfWdbMF09ZSsrO3JldHVybiBnfSxBVFRSOmZ1bmN0aW9uKGcsaCxsLG0scSxwKXtoPWdbMV0ucmVwbGFjZSgvXFwvZywiIik7aWYoIXAmJm4uYXR0ck1hcFtoXSlnWzFdPW4uYXR0ck1hcFtoXTtpZihnWzJdPT09In49IilnWzRdPSIgIitnWzRdKyIgIjtyZXR1cm4gZ30sUFNFVURPOmZ1bmN0aW9uKGcsaCxsLG0scSl7aWYoZ1sxXT09PSJub3QiKWlmKChmLmV4ZWMoZ1szXSl8fCIiKS5sZW5ndGg+MXx8L15cdy8udGVzdChnWzNdKSlnWzNdPWsoZ1szXSxudWxsLG51bGwsaCk7ZWxzZXtnPWsuZmlsdGVyKGdbM10saCxsLHRydWVecSk7bHx8bS5wdXNoLmFwcGx5KG0sDQpnKTtyZXR1cm4gZmFsc2V9ZWxzZSBpZihuLm1hdGNoLlBPUy50ZXN0KGdbMF0pfHxuLm1hdGNoLkNISUxELnRlc3QoZ1swXSkpcmV0dXJuIHRydWU7cmV0dXJuIGd9LFBPUzpmdW5jdGlvbihnKXtnLnVuc2hpZnQodHJ1ZSk7cmV0dXJuIGd9fSxmaWx0ZXJzOntlbmFibGVkOmZ1bmN0aW9uKGcpe3JldHVybiBnLmRpc2FibGVkPT09ZmFsc2UmJmcudHlwZSE9PSJoaWRkZW4ifSxkaXNhYmxlZDpmdW5jdGlvbihnKXtyZXR1cm4gZy5kaXNhYmxlZD09PXRydWV9LGNoZWNrZWQ6ZnVuY3Rpb24oZyl7cmV0dXJuIGcuY2hlY2tlZD09PXRydWV9LHNlbGVjdGVkOmZ1bmN0aW9uKGcpe3JldHVybiBnLnNlbGVjdGVkPT09dHJ1ZX0scGFyZW50OmZ1bmN0aW9uKGcpe3JldHVybiEhZy5maXJzdENoaWxkfSxlbXB0eTpmdW5jdGlvbihnKXtyZXR1cm4hZy5maXJzdENoaWxkfSxoYXM6ZnVuY3Rpb24oZyxoLGwpe3JldHVybiEhayhsWzNdLGcpLmxlbmd0aH0saGVhZGVyOmZ1bmN0aW9uKGcpe3JldHVybi9oXGQvaS50ZXN0KGcubm9kZU5hbWUpfSwNCnRleHQ6ZnVuY3Rpb24oZyl7cmV0dXJuInRleHQiPT09Zy50eXBlfSxyYWRpbzpmdW5jdGlvbihnKXtyZXR1cm4icmFkaW8iPT09Zy50eXBlfSxjaGVja2JveDpmdW5jdGlvbihnKXtyZXR1cm4iY2hlY2tib3giPT09Zy50eXBlfSxmaWxlOmZ1bmN0aW9uKGcpe3JldHVybiJmaWxlIj09PWcudHlwZX0scGFzc3dvcmQ6ZnVuY3Rpb24oZyl7cmV0dXJuInBhc3N3b3JkIj09PWcudHlwZX0sc3VibWl0OmZ1bmN0aW9uKGcpe3JldHVybiJzdWJtaXQiPT09Zy50eXBlfSxpbWFnZTpmdW5jdGlvbihnKXtyZXR1cm4iaW1hZ2UiPT09Zy50eXBlfSxyZXNldDpmdW5jdGlvbihnKXtyZXR1cm4icmVzZXQiPT09Zy50eXBlfSxidXR0b246ZnVuY3Rpb24oZyl7cmV0dXJuImJ1dHRvbiI9PT1nLnR5cGV8fGcubm9kZU5hbWUudG9Mb3dlckNhc2UoKT09PSJidXR0b24ifSxpbnB1dDpmdW5jdGlvbihnKXtyZXR1cm4vaW5wdXR8c2VsZWN0fHRleHRhcmVhfGJ1dHRvbi9pLnRlc3QoZy5ub2RlTmFtZSl9fSwNCnNldEZpbHRlcnM6e2ZpcnN0OmZ1bmN0aW9uKGcsaCl7cmV0dXJuIGg9PT0wfSxsYXN0OmZ1bmN0aW9uKGcsaCxsLG0pe3JldHVybiBoPT09bS5sZW5ndGgtMX0sZXZlbjpmdW5jdGlvbihnLGgpe3JldHVybiBoJTI9PT0wfSxvZGQ6ZnVuY3Rpb24oZyxoKXtyZXR1cm4gaCUyPT09MX0sbHQ6ZnVuY3Rpb24oZyxoLGwpe3JldHVybiBoPGxbM10tMH0sZ3Q6ZnVuY3Rpb24oZyxoLGwpe3JldHVybiBoPmxbM10tMH0sbnRoOmZ1bmN0aW9uKGcsaCxsKXtyZXR1cm4gbFszXS0wPT09aH0sZXE6ZnVuY3Rpb24oZyxoLGwpe3JldHVybiBsWzNdLTA9PT1ofX0sZmlsdGVyOntQU0VVRE86ZnVuY3Rpb24oZyxoLGwsbSl7dmFyIHE9aFsxXSxwPW4uZmlsdGVyc1txXTtpZihwKXJldHVybiBwKGcsbCxoLG0pO2Vsc2UgaWYocT09PSJjb250YWlucyIpcmV0dXJuKGcudGV4dENvbnRlbnR8fGcuaW5uZXJUZXh0fHxhKFtnXSl8fCIiKS5pbmRleE9mKGhbM10pPj0wO2Vsc2UgaWYocT09PSJub3QiKXtoPQ0KaFszXTtsPTA7Zm9yKG09aC5sZW5ndGg7bDxtO2wrKylpZihoW2xdPT09ZylyZXR1cm4gZmFsc2U7cmV0dXJuIHRydWV9ZWxzZSBrLmVycm9yKCJTeW50YXggZXJyb3IsIHVucmVjb2duaXplZCBleHByZXNzaW9uOiAiK3EpfSxDSElMRDpmdW5jdGlvbihnLGgpe3ZhciBsPWhbMV0sbT1nO3N3aXRjaChsKXtjYXNlICJvbmx5IjpjYXNlICJmaXJzdCI6Zm9yKDttPW0ucHJldmlvdXNTaWJsaW5nOylpZihtLm5vZGVUeXBlPT09MSlyZXR1cm4gZmFsc2U7aWYobD09PSJmaXJzdCIpcmV0dXJuIHRydWU7bT1nO2Nhc2UgImxhc3QiOmZvcig7bT1tLm5leHRTaWJsaW5nOylpZihtLm5vZGVUeXBlPT09MSlyZXR1cm4gZmFsc2U7cmV0dXJuIHRydWU7Y2FzZSAibnRoIjpsPWhbMl07dmFyIHE9aFszXTtpZihsPT09MSYmcT09PTApcmV0dXJuIHRydWU7aD1oWzBdO3ZhciBwPWcucGFyZW50Tm9kZTtpZihwJiYocC5zaXpjYWNoZSE9PWh8fCFnLm5vZGVJbmRleCkpe3ZhciB2PTA7Zm9yKG09cC5maXJzdENoaWxkO207bT0NCm0ubmV4dFNpYmxpbmcpaWYobS5ub2RlVHlwZT09PTEpbS5ub2RlSW5kZXg9Kyt2O3Auc2l6Y2FjaGU9aH1nPWcubm9kZUluZGV4LXE7cmV0dXJuIGw9PT0wP2c9PT0wOmclbD09PTAmJmcvbD49MH19LElEOmZ1bmN0aW9uKGcsaCl7cmV0dXJuIGcubm9kZVR5cGU9PT0xJiZnLmdldEF0dHJpYnV0ZSgiaWQiKT09PWh9LFRBRzpmdW5jdGlvbihnLGgpe3JldHVybiBoPT09IioiJiZnLm5vZGVUeXBlPT09MXx8Zy5ub2RlTmFtZS50b0xvd2VyQ2FzZSgpPT09aH0sQ0xBU1M6ZnVuY3Rpb24oZyxoKXtyZXR1cm4oIiAiKyhnLmNsYXNzTmFtZXx8Zy5nZXRBdHRyaWJ1dGUoImNsYXNzIikpKyIgIikuaW5kZXhPZihoKT4tMX0sQVRUUjpmdW5jdGlvbihnLGgpe3ZhciBsPWhbMV07Zz1uLmF0dHJIYW5kbGVbbF0/bi5hdHRySGFuZGxlW2xdKGcpOmdbbF0hPW51bGw/Z1tsXTpnLmdldEF0dHJpYnV0ZShsKTtsPWcrIiI7dmFyIG09aFsyXTtoPWhbNF07cmV0dXJuIGc9PW51bGw/bT09PSIhPSI6bT09PQ0KIj0iP2w9PT1oOm09PT0iKj0iP2wuaW5kZXhPZihoKT49MDptPT09In49Ij8oIiAiK2wrIiAiKS5pbmRleE9mKGgpPj0wOiFoP2wmJmchPT1mYWxzZTptPT09IiE9Ij9sIT09aDptPT09Il49Ij9sLmluZGV4T2YoaCk9PT0wOm09PT0iJD0iP2wuc3Vic3RyKGwubGVuZ3RoLWgubGVuZ3RoKT09PWg6bT09PSJ8PSI/bD09PWh8fGwuc3Vic3RyKDAsaC5sZW5ndGgrMSk9PT1oKyItIjpmYWxzZX0sUE9TOmZ1bmN0aW9uKGcsaCxsLG0pe3ZhciBxPW4uc2V0RmlsdGVyc1toWzJdXTtpZihxKXJldHVybiBxKGcsbCxoLG0pfX19LHI9bi5tYXRjaC5QT1M7Zm9yKHZhciB1IGluIG4ubWF0Y2gpe24ubWF0Y2hbdV09bmV3IFJlZ0V4cChuLm1hdGNoW3VdLnNvdXJjZSsvKD8hW15cW10qXF0pKD8hW15cKF0qXCkpLy5zb3VyY2UpO24ubGVmdE1hdGNoW3VdPW5ldyBSZWdFeHAoLyheKD86LnxccnxcbikqPykvLnNvdXJjZStuLm1hdGNoW3VdLnNvdXJjZS5yZXBsYWNlKC9cXChcZCspL2csZnVuY3Rpb24oZywNCmgpe3JldHVybiJcXCIrKGgtMCsxKX0pKX12YXIgej1mdW5jdGlvbihnLGgpe2c9QXJyYXkucHJvdG90eXBlLnNsaWNlLmNhbGwoZywwKTtpZihoKXtoLnB1c2guYXBwbHkoaCxnKTtyZXR1cm4gaH1yZXR1cm4gZ307dHJ5e0FycmF5LnByb3RvdHlwZS5zbGljZS5jYWxsKHMuZG9jdW1lbnRFbGVtZW50LmNoaWxkTm9kZXMsMCl9Y2F0Y2goQyl7ej1mdW5jdGlvbihnLGgpe2g9aHx8W107aWYoai5jYWxsKGcpPT09IltvYmplY3QgQXJyYXldIilBcnJheS5wcm90b3R5cGUucHVzaC5hcHBseShoLGcpO2Vsc2UgaWYodHlwZW9mIGcubGVuZ3RoPT09Im51bWJlciIpZm9yKHZhciBsPTAsbT1nLmxlbmd0aDtsPG07bCsrKWgucHVzaChnW2xdKTtlbHNlIGZvcihsPTA7Z1tsXTtsKyspaC5wdXNoKGdbbF0pO3JldHVybiBofX12YXIgQjtpZihzLmRvY3VtZW50RWxlbWVudC5jb21wYXJlRG9jdW1lbnRQb3NpdGlvbilCPWZ1bmN0aW9uKGcsaCl7aWYoIWcuY29tcGFyZURvY3VtZW50UG9zaXRpb258fA0KIWguY29tcGFyZURvY3VtZW50UG9zaXRpb24pe2lmKGc9PWgpaT10cnVlO3JldHVybiBnLmNvbXBhcmVEb2N1bWVudFBvc2l0aW9uPy0xOjF9Zz1nLmNvbXBhcmVEb2N1bWVudFBvc2l0aW9uKGgpJjQ/LTE6Zz09PWg/MDoxO2lmKGc9PT0wKWk9dHJ1ZTtyZXR1cm4gZ307ZWxzZSBpZigic291cmNlSW5kZXgiaW4gcy5kb2N1bWVudEVsZW1lbnQpQj1mdW5jdGlvbihnLGgpe2lmKCFnLnNvdXJjZUluZGV4fHwhaC5zb3VyY2VJbmRleCl7aWYoZz09aClpPXRydWU7cmV0dXJuIGcuc291cmNlSW5kZXg/LTE6MX1nPWcuc291cmNlSW5kZXgtaC5zb3VyY2VJbmRleDtpZihnPT09MClpPXRydWU7cmV0dXJuIGd9O2Vsc2UgaWYocy5jcmVhdGVSYW5nZSlCPWZ1bmN0aW9uKGcsaCl7aWYoIWcub3duZXJEb2N1bWVudHx8IWgub3duZXJEb2N1bWVudCl7aWYoZz09aClpPXRydWU7cmV0dXJuIGcub3duZXJEb2N1bWVudD8tMToxfXZhciBsPWcub3duZXJEb2N1bWVudC5jcmVhdGVSYW5nZSgpLG09DQpoLm93bmVyRG9jdW1lbnQuY3JlYXRlUmFuZ2UoKTtsLnNldFN0YXJ0KGcsMCk7bC5zZXRFbmQoZywwKTttLnNldFN0YXJ0KGgsMCk7bS5zZXRFbmQoaCwwKTtnPWwuY29tcGFyZUJvdW5kYXJ5UG9pbnRzKFJhbmdlLlNUQVJUX1RPX0VORCxtKTtpZihnPT09MClpPXRydWU7cmV0dXJuIGd9OyhmdW5jdGlvbigpe3ZhciBnPXMuY3JlYXRlRWxlbWVudCgiZGl2IiksaD0ic2NyaXB0IisobmV3IERhdGUpLmdldFRpbWUoKTtnLmlubmVySFRNTD0iPGEgbmFtZT0nIitoKyInLz4iO3ZhciBsPXMuZG9jdW1lbnRFbGVtZW50O2wuaW5zZXJ0QmVmb3JlKGcsbC5maXJzdENoaWxkKTtpZihzLmdldEVsZW1lbnRCeUlkKGgpKXtuLmZpbmQuSUQ9ZnVuY3Rpb24obSxxLHApe2lmKHR5cGVvZiBxLmdldEVsZW1lbnRCeUlkIT09InVuZGVmaW5lZCImJiFwKXJldHVybihxPXEuZ2V0RWxlbWVudEJ5SWQobVsxXSkpP3EuaWQ9PT1tWzFdfHx0eXBlb2YgcS5nZXRBdHRyaWJ1dGVOb2RlIT09InVuZGVmaW5lZCImJg0KcS5nZXRBdHRyaWJ1dGVOb2RlKCJpZCIpLm5vZGVWYWx1ZT09PW1bMV0/W3FdOnc6W119O24uZmlsdGVyLklEPWZ1bmN0aW9uKG0scSl7dmFyIHA9dHlwZW9mIG0uZ2V0QXR0cmlidXRlTm9kZSE9PSJ1bmRlZmluZWQiJiZtLmdldEF0dHJpYnV0ZU5vZGUoImlkIik7cmV0dXJuIG0ubm9kZVR5cGU9PT0xJiZwJiZwLm5vZGVWYWx1ZT09PXF9fWwucmVtb3ZlQ2hpbGQoZyk7bD1nPW51bGx9KSgpOyhmdW5jdGlvbigpe3ZhciBnPXMuY3JlYXRlRWxlbWVudCgiZGl2Iik7Zy5hcHBlbmRDaGlsZChzLmNyZWF0ZUNvbW1lbnQoIiIpKTtpZihnLmdldEVsZW1lbnRzQnlUYWdOYW1lKCIqIikubGVuZ3RoPjApbi5maW5kLlRBRz1mdW5jdGlvbihoLGwpe2w9bC5nZXRFbGVtZW50c0J5VGFnTmFtZShoWzFdKTtpZihoWzFdPT09IioiKXtoPVtdO2Zvcih2YXIgbT0wO2xbbV07bSsrKWxbbV0ubm9kZVR5cGU9PT0xJiZoLnB1c2gobFttXSk7bD1ofXJldHVybiBsfTtnLmlubmVySFRNTD0iPGEgaHJlZj0nIyc+PC9hPiI7DQppZihnLmZpcnN0Q2hpbGQmJnR5cGVvZiBnLmZpcnN0Q2hpbGQuZ2V0QXR0cmlidXRlIT09InVuZGVmaW5lZCImJmcuZmlyc3RDaGlsZC5nZXRBdHRyaWJ1dGUoImhyZWYiKSE9PSIjIiluLmF0dHJIYW5kbGUuaHJlZj1mdW5jdGlvbihoKXtyZXR1cm4gaC5nZXRBdHRyaWJ1dGUoImhyZWYiLDIpfTtnPW51bGx9KSgpO3MucXVlcnlTZWxlY3RvckFsbCYmZnVuY3Rpb24oKXt2YXIgZz1rLGg9cy5jcmVhdGVFbGVtZW50KCJkaXYiKTtoLmlubmVySFRNTD0iPHAgY2xhc3M9J1RFU1QnPjwvcD4iO2lmKCEoaC5xdWVyeVNlbGVjdG9yQWxsJiZoLnF1ZXJ5U2VsZWN0b3JBbGwoIi5URVNUIikubGVuZ3RoPT09MCkpe2s9ZnVuY3Rpb24obSxxLHAsdil7cT1xfHxzO2lmKCF2JiZxLm5vZGVUeXBlPT09OSYmIXgocSkpdHJ5e3JldHVybiB6KHEucXVlcnlTZWxlY3RvckFsbChtKSxwKX1jYXRjaCh0KXt9cmV0dXJuIGcobSxxLHAsdil9O2Zvcih2YXIgbCBpbiBnKWtbbF09Z1tsXTtoPW51bGx9fSgpOw0KKGZ1bmN0aW9uKCl7dmFyIGc9cy5jcmVhdGVFbGVtZW50KCJkaXYiKTtnLmlubmVySFRNTD0iPGRpdiBjbGFzcz0ndGVzdCBlJz48L2Rpdj48ZGl2IGNsYXNzPSd0ZXN0Jz48L2Rpdj4iO2lmKCEoIWcuZ2V0RWxlbWVudHNCeUNsYXNzTmFtZXx8Zy5nZXRFbGVtZW50c0J5Q2xhc3NOYW1lKCJlIikubGVuZ3RoPT09MCkpe2cubGFzdENoaWxkLmNsYXNzTmFtZT0iZSI7aWYoZy5nZXRFbGVtZW50c0J5Q2xhc3NOYW1lKCJlIikubGVuZ3RoIT09MSl7bi5vcmRlci5zcGxpY2UoMSwwLCJDTEFTUyIpO24uZmluZC5DTEFTUz1mdW5jdGlvbihoLGwsbSl7aWYodHlwZW9mIGwuZ2V0RWxlbWVudHNCeUNsYXNzTmFtZSE9PSJ1bmRlZmluZWQiJiYhbSlyZXR1cm4gbC5nZXRFbGVtZW50c0J5Q2xhc3NOYW1lKGhbMV0pfTtnPW51bGx9fX0pKCk7dmFyIEU9cy5jb21wYXJlRG9jdW1lbnRQb3NpdGlvbj9mdW5jdGlvbihnLGgpe3JldHVybiEhKGcuY29tcGFyZURvY3VtZW50UG9zaXRpb24oaCkmMTYpfToNCmZ1bmN0aW9uKGcsaCl7cmV0dXJuIGchPT1oJiYoZy5jb250YWlucz9nLmNvbnRhaW5zKGgpOnRydWUpfSx4PWZ1bmN0aW9uKGcpe3JldHVybihnPShnP2cub3duZXJEb2N1bWVudHx8ZzowKS5kb2N1bWVudEVsZW1lbnQpP2cubm9kZU5hbWUhPT0iSFRNTCI6ZmFsc2V9LGdhPWZ1bmN0aW9uKGcsaCl7dmFyIGw9W10sbT0iIixxO2ZvcihoPWgubm9kZVR5cGU/W2hdOmg7cT1uLm1hdGNoLlBTRVVETy5leGVjKGcpOyl7bSs9cVswXTtnPWcucmVwbGFjZShuLm1hdGNoLlBTRVVETywiIil9Zz1uLnJlbGF0aXZlW2ddP2crIioiOmc7cT0wO2Zvcih2YXIgcD1oLmxlbmd0aDtxPHA7cSsrKWsoZyxoW3FdLGwpO3JldHVybiBrLmZpbHRlcihtLGwpfTtjLmZpbmQ9aztjLmV4cHI9ay5zZWxlY3RvcnM7Yy5leHByWyI6Il09Yy5leHByLmZpbHRlcnM7Yy51bmlxdWU9ay51bmlxdWVTb3J0O2MudGV4dD1hO2MuaXNYTUxEb2M9eDtjLmNvbnRhaW5zPUV9KSgpO3ZhciBlYj0vVW50aWwkLyxmYj0vXig/OnBhcmVudHN8cHJldlVudGlsfHByZXZBbGwpLywNCmdiPS8sLztSPUFycmF5LnByb3RvdHlwZS5zbGljZTt2YXIgSWE9ZnVuY3Rpb24oYSxiLGQpe2lmKGMuaXNGdW5jdGlvbihiKSlyZXR1cm4gYy5ncmVwKGEsZnVuY3Rpb24oZSxqKXtyZXR1cm4hIWIuY2FsbChlLGosZSk9PT1kfSk7ZWxzZSBpZihiLm5vZGVUeXBlKXJldHVybiBjLmdyZXAoYSxmdW5jdGlvbihlKXtyZXR1cm4gZT09PWI9PT1kfSk7ZWxzZSBpZih0eXBlb2YgYj09PSJzdHJpbmciKXt2YXIgZj1jLmdyZXAoYSxmdW5jdGlvbihlKXtyZXR1cm4gZS5ub2RlVHlwZT09PTF9KTtpZihVYS50ZXN0KGIpKXJldHVybiBjLmZpbHRlcihiLGYsIWQpO2Vsc2UgYj1jLmZpbHRlcihiLGYpfXJldHVybiBjLmdyZXAoYSxmdW5jdGlvbihlKXtyZXR1cm4gYy5pbkFycmF5KGUsYik+PTA9PT1kfSl9O2MuZm4uZXh0ZW5kKHtmaW5kOmZ1bmN0aW9uKGEpe2Zvcih2YXIgYj10aGlzLnB1c2hTdGFjaygiIiwiZmluZCIsYSksZD0wLGY9MCxlPXRoaXMubGVuZ3RoO2Y8ZTtmKyspe2Q9Yi5sZW5ndGg7DQpjLmZpbmQoYSx0aGlzW2ZdLGIpO2lmKGY+MClmb3IodmFyIGo9ZDtqPGIubGVuZ3RoO2orKylmb3IodmFyIGk9MDtpPGQ7aSsrKWlmKGJbaV09PT1iW2pdKXtiLnNwbGljZShqLS0sMSk7YnJlYWt9fXJldHVybiBifSxoYXM6ZnVuY3Rpb24oYSl7dmFyIGI9YyhhKTtyZXR1cm4gdGhpcy5maWx0ZXIoZnVuY3Rpb24oKXtmb3IodmFyIGQ9MCxmPWIubGVuZ3RoO2Q8ZjtkKyspaWYoYy5jb250YWlucyh0aGlzLGJbZF0pKXJldHVybiB0cnVlfSl9LG5vdDpmdW5jdGlvbihhKXtyZXR1cm4gdGhpcy5wdXNoU3RhY2soSWEodGhpcyxhLGZhbHNlKSwibm90IixhKX0sZmlsdGVyOmZ1bmN0aW9uKGEpe3JldHVybiB0aGlzLnB1c2hTdGFjayhJYSh0aGlzLGEsdHJ1ZSksImZpbHRlciIsYSl9LGlzOmZ1bmN0aW9uKGEpe3JldHVybiEhYSYmYy5maWx0ZXIoYSx0aGlzKS5sZW5ndGg+MH0sY2xvc2VzdDpmdW5jdGlvbihhLGIpe2lmKGMuaXNBcnJheShhKSl7dmFyIGQ9W10sZj10aGlzWzBdLGUsaj0NCnt9LGk7aWYoZiYmYS5sZW5ndGgpe2U9MDtmb3IodmFyIG89YS5sZW5ndGg7ZTxvO2UrKyl7aT1hW2VdO2pbaV18fChqW2ldPWMuZXhwci5tYXRjaC5QT1MudGVzdChpKT9jKGksYnx8dGhpcy5jb250ZXh0KTppKX1mb3IoO2YmJmYub3duZXJEb2N1bWVudCYmZiE9PWI7KXtmb3IoaSBpbiBqKXtlPWpbaV07aWYoZS5qcXVlcnk/ZS5pbmRleChmKT4tMTpjKGYpLmlzKGUpKXtkLnB1c2goe3NlbGVjdG9yOmksZWxlbTpmfSk7ZGVsZXRlIGpbaV19fWY9Zi5wYXJlbnROb2RlfX1yZXR1cm4gZH12YXIgaz1jLmV4cHIubWF0Y2guUE9TLnRlc3QoYSk/YyhhLGJ8fHRoaXMuY29udGV4dCk6bnVsbDtyZXR1cm4gdGhpcy5tYXAoZnVuY3Rpb24obixyKXtmb3IoO3ImJnIub3duZXJEb2N1bWVudCYmciE9PWI7KXtpZihrP2suaW5kZXgocik+LTE6YyhyKS5pcyhhKSlyZXR1cm4gcjtyPXIucGFyZW50Tm9kZX1yZXR1cm4gbnVsbH0pfSxpbmRleDpmdW5jdGlvbihhKXtpZighYXx8dHlwZW9mIGE9PT0NCiJzdHJpbmciKXJldHVybiBjLmluQXJyYXkodGhpc1swXSxhP2MoYSk6dGhpcy5wYXJlbnQoKS5jaGlsZHJlbigpKTtyZXR1cm4gYy5pbkFycmF5KGEuanF1ZXJ5P2FbMF06YSx0aGlzKX0sYWRkOmZ1bmN0aW9uKGEsYil7YT10eXBlb2YgYT09PSJzdHJpbmciP2MoYSxifHx0aGlzLmNvbnRleHQpOmMubWFrZUFycmF5KGEpO2I9Yy5tZXJnZSh0aGlzLmdldCgpLGEpO3JldHVybiB0aGlzLnB1c2hTdGFjayhxYShhWzBdKXx8cWEoYlswXSk/YjpjLnVuaXF1ZShiKSl9LGFuZFNlbGY6ZnVuY3Rpb24oKXtyZXR1cm4gdGhpcy5hZGQodGhpcy5wcmV2T2JqZWN0KX19KTtjLmVhY2goe3BhcmVudDpmdW5jdGlvbihhKXtyZXR1cm4oYT1hLnBhcmVudE5vZGUpJiZhLm5vZGVUeXBlIT09MTE/YTpudWxsfSxwYXJlbnRzOmZ1bmN0aW9uKGEpe3JldHVybiBjLmRpcihhLCJwYXJlbnROb2RlIil9LHBhcmVudHNVbnRpbDpmdW5jdGlvbihhLGIsZCl7cmV0dXJuIGMuZGlyKGEsInBhcmVudE5vZGUiLA0KZCl9LG5leHQ6ZnVuY3Rpb24oYSl7cmV0dXJuIGMubnRoKGEsMiwibmV4dFNpYmxpbmciKX0scHJldjpmdW5jdGlvbihhKXtyZXR1cm4gYy5udGgoYSwyLCJwcmV2aW91c1NpYmxpbmciKX0sbmV4dEFsbDpmdW5jdGlvbihhKXtyZXR1cm4gYy5kaXIoYSwibmV4dFNpYmxpbmciKX0scHJldkFsbDpmdW5jdGlvbihhKXtyZXR1cm4gYy5kaXIoYSwicHJldmlvdXNTaWJsaW5nIil9LG5leHRVbnRpbDpmdW5jdGlvbihhLGIsZCl7cmV0dXJuIGMuZGlyKGEsIm5leHRTaWJsaW5nIixkKX0scHJldlVudGlsOmZ1bmN0aW9uKGEsYixkKXtyZXR1cm4gYy5kaXIoYSwicHJldmlvdXNTaWJsaW5nIixkKX0sc2libGluZ3M6ZnVuY3Rpb24oYSl7cmV0dXJuIGMuc2libGluZyhhLnBhcmVudE5vZGUuZmlyc3RDaGlsZCxhKX0sY2hpbGRyZW46ZnVuY3Rpb24oYSl7cmV0dXJuIGMuc2libGluZyhhLmZpcnN0Q2hpbGQpfSxjb250ZW50czpmdW5jdGlvbihhKXtyZXR1cm4gYy5ub2RlTmFtZShhLCJpZnJhbWUiKT8NCmEuY29udGVudERvY3VtZW50fHxhLmNvbnRlbnRXaW5kb3cuZG9jdW1lbnQ6Yy5tYWtlQXJyYXkoYS5jaGlsZE5vZGVzKX19LGZ1bmN0aW9uKGEsYil7Yy5mblthXT1mdW5jdGlvbihkLGYpe3ZhciBlPWMubWFwKHRoaXMsYixkKTtlYi50ZXN0KGEpfHwoZj1kKTtpZihmJiZ0eXBlb2YgZj09PSJzdHJpbmciKWU9Yy5maWx0ZXIoZixlKTtlPXRoaXMubGVuZ3RoPjE/Yy51bmlxdWUoZSk6ZTtpZigodGhpcy5sZW5ndGg+MXx8Z2IudGVzdChmKSkmJmZiLnRlc3QoYSkpZT1lLnJldmVyc2UoKTtyZXR1cm4gdGhpcy5wdXNoU3RhY2soZSxhLFIuY2FsbChhcmd1bWVudHMpLmpvaW4oIiwiKSl9fSk7Yy5leHRlbmQoe2ZpbHRlcjpmdW5jdGlvbihhLGIsZCl7aWYoZClhPSI6bm90KCIrYSsiKSI7cmV0dXJuIGMuZmluZC5tYXRjaGVzKGEsYil9LGRpcjpmdW5jdGlvbihhLGIsZCl7dmFyIGY9W107Zm9yKGE9YVtiXTthJiZhLm5vZGVUeXBlIT09OSYmKGQ9PT13fHxhLm5vZGVUeXBlIT09MXx8IWMoYSkuaXMoZCkpOyl7YS5ub2RlVHlwZT09PQ0KMSYmZi5wdXNoKGEpO2E9YVtiXX1yZXR1cm4gZn0sbnRoOmZ1bmN0aW9uKGEsYixkKXtiPWJ8fDE7Zm9yKHZhciBmPTA7YTthPWFbZF0paWYoYS5ub2RlVHlwZT09PTEmJisrZj09PWIpYnJlYWs7cmV0dXJuIGF9LHNpYmxpbmc6ZnVuY3Rpb24oYSxiKXtmb3IodmFyIGQ9W107YTthPWEubmV4dFNpYmxpbmcpYS5ub2RlVHlwZT09PTEmJmEhPT1iJiZkLnB1c2goYSk7cmV0dXJuIGR9fSk7dmFyIEphPS8galF1ZXJ5XGQrPSIoPzpcZCt8bnVsbCkiL2csVj0vXlxzKy8sS2E9Lyg8KFtcdzpdKylbXj5dKj8pXC8+L2csaGI9L14oPzphcmVhfGJyfGNvbHxlbWJlZHxocnxpbWd8aW5wdXR8bGlua3xtZXRhfHBhcmFtKSQvaSxMYT0vPChbXHc6XSspLyxpYj0vPHRib2R5L2ksamI9Lzx8JiM/XHcrOy8sdGE9LzxzY3JpcHR8PG9iamVjdHw8ZW1iZWR8PG9wdGlvbnw8c3R5bGUvaSx1YT0vY2hlY2tlZFxzKig/OltePV18PVxzKi5jaGVja2VkLikvaSxNYT1mdW5jdGlvbihhLGIsZCl7cmV0dXJuIGhiLnRlc3QoZCk/DQphOmIrIj48LyIrZCsiPiJ9LEY9e29wdGlvbjpbMSwiPHNlbGVjdCBtdWx0aXBsZT0nbXVsdGlwbGUnPiIsIjwvc2VsZWN0PiJdLGxlZ2VuZDpbMSwiPGZpZWxkc2V0PiIsIjwvZmllbGRzZXQ+Il0sdGhlYWQ6WzEsIjx0YWJsZT4iLCI8L3RhYmxlPiJdLHRyOlsyLCI8dGFibGU+PHRib2R5PiIsIjwvdGJvZHk+PC90YWJsZT4iXSx0ZDpbMywiPHRhYmxlPjx0Ym9keT48dHI+IiwiPC90cj48L3Rib2R5PjwvdGFibGU+Il0sY29sOlsyLCI8dGFibGU+PHRib2R5PjwvdGJvZHk+PGNvbGdyb3VwPiIsIjwvY29sZ3JvdXA+PC90YWJsZT4iXSxhcmVhOlsxLCI8bWFwPiIsIjwvbWFwPiJdLF9kZWZhdWx0OlswLCIiLCIiXX07Ri5vcHRncm91cD1GLm9wdGlvbjtGLnRib2R5PUYudGZvb3Q9Ri5jb2xncm91cD1GLmNhcHRpb249Ri50aGVhZDtGLnRoPUYudGQ7aWYoIWMuc3VwcG9ydC5odG1sU2VyaWFsaXplKUYuX2RlZmF1bHQ9WzEsImRpdjxkaXY+IiwiPC9kaXY+Il07Yy5mbi5leHRlbmQoe3RleHQ6ZnVuY3Rpb24oYSl7aWYoYy5pc0Z1bmN0aW9uKGEpKXJldHVybiB0aGlzLmVhY2goZnVuY3Rpb24oYil7dmFyIGQ9DQpjKHRoaXMpO2QudGV4dChhLmNhbGwodGhpcyxiLGQudGV4dCgpKSl9KTtpZih0eXBlb2YgYSE9PSJvYmplY3QiJiZhIT09dylyZXR1cm4gdGhpcy5lbXB0eSgpLmFwcGVuZCgodGhpc1swXSYmdGhpc1swXS5vd25lckRvY3VtZW50fHxzKS5jcmVhdGVUZXh0Tm9kZShhKSk7cmV0dXJuIGMudGV4dCh0aGlzKX0sd3JhcEFsbDpmdW5jdGlvbihhKXtpZihjLmlzRnVuY3Rpb24oYSkpcmV0dXJuIHRoaXMuZWFjaChmdW5jdGlvbihkKXtjKHRoaXMpLndyYXBBbGwoYS5jYWxsKHRoaXMsZCkpfSk7aWYodGhpc1swXSl7dmFyIGI9YyhhLHRoaXNbMF0ub3duZXJEb2N1bWVudCkuZXEoMCkuY2xvbmUodHJ1ZSk7dGhpc1swXS5wYXJlbnROb2RlJiZiLmluc2VydEJlZm9yZSh0aGlzWzBdKTtiLm1hcChmdW5jdGlvbigpe2Zvcih2YXIgZD10aGlzO2QuZmlyc3RDaGlsZCYmZC5maXJzdENoaWxkLm5vZGVUeXBlPT09MTspZD1kLmZpcnN0Q2hpbGQ7cmV0dXJuIGR9KS5hcHBlbmQodGhpcyl9cmV0dXJuIHRoaXN9LA0Kd3JhcElubmVyOmZ1bmN0aW9uKGEpe2lmKGMuaXNGdW5jdGlvbihhKSlyZXR1cm4gdGhpcy5lYWNoKGZ1bmN0aW9uKGIpe2ModGhpcykud3JhcElubmVyKGEuY2FsbCh0aGlzLGIpKX0pO3JldHVybiB0aGlzLmVhY2goZnVuY3Rpb24oKXt2YXIgYj1jKHRoaXMpLGQ9Yi5jb250ZW50cygpO2QubGVuZ3RoP2Qud3JhcEFsbChhKTpiLmFwcGVuZChhKX0pfSx3cmFwOmZ1bmN0aW9uKGEpe3JldHVybiB0aGlzLmVhY2goZnVuY3Rpb24oKXtjKHRoaXMpLndyYXBBbGwoYSl9KX0sdW53cmFwOmZ1bmN0aW9uKCl7cmV0dXJuIHRoaXMucGFyZW50KCkuZWFjaChmdW5jdGlvbigpe2Mubm9kZU5hbWUodGhpcywiYm9keSIpfHxjKHRoaXMpLnJlcGxhY2VXaXRoKHRoaXMuY2hpbGROb2Rlcyl9KS5lbmQoKX0sYXBwZW5kOmZ1bmN0aW9uKCl7cmV0dXJuIHRoaXMuZG9tTWFuaXAoYXJndW1lbnRzLHRydWUsZnVuY3Rpb24oYSl7dGhpcy5ub2RlVHlwZT09PTEmJnRoaXMuYXBwZW5kQ2hpbGQoYSl9KX0sDQpwcmVwZW5kOmZ1bmN0aW9uKCl7cmV0dXJuIHRoaXMuZG9tTWFuaXAoYXJndW1lbnRzLHRydWUsZnVuY3Rpb24oYSl7dGhpcy5ub2RlVHlwZT09PTEmJnRoaXMuaW5zZXJ0QmVmb3JlKGEsdGhpcy5maXJzdENoaWxkKX0pfSxiZWZvcmU6ZnVuY3Rpb24oKXtpZih0aGlzWzBdJiZ0aGlzWzBdLnBhcmVudE5vZGUpcmV0dXJuIHRoaXMuZG9tTWFuaXAoYXJndW1lbnRzLGZhbHNlLGZ1bmN0aW9uKGIpe3RoaXMucGFyZW50Tm9kZS5pbnNlcnRCZWZvcmUoYix0aGlzKX0pO2Vsc2UgaWYoYXJndW1lbnRzLmxlbmd0aCl7dmFyIGE9Yyhhcmd1bWVudHNbMF0pO2EucHVzaC5hcHBseShhLHRoaXMudG9BcnJheSgpKTtyZXR1cm4gdGhpcy5wdXNoU3RhY2soYSwiYmVmb3JlIixhcmd1bWVudHMpfX0sYWZ0ZXI6ZnVuY3Rpb24oKXtpZih0aGlzWzBdJiZ0aGlzWzBdLnBhcmVudE5vZGUpcmV0dXJuIHRoaXMuZG9tTWFuaXAoYXJndW1lbnRzLGZhbHNlLGZ1bmN0aW9uKGIpe3RoaXMucGFyZW50Tm9kZS5pbnNlcnRCZWZvcmUoYiwNCnRoaXMubmV4dFNpYmxpbmcpfSk7ZWxzZSBpZihhcmd1bWVudHMubGVuZ3RoKXt2YXIgYT10aGlzLnB1c2hTdGFjayh0aGlzLCJhZnRlciIsYXJndW1lbnRzKTthLnB1c2guYXBwbHkoYSxjKGFyZ3VtZW50c1swXSkudG9BcnJheSgpKTtyZXR1cm4gYX19LHJlbW92ZTpmdW5jdGlvbihhLGIpe2Zvcih2YXIgZD0wLGY7KGY9dGhpc1tkXSkhPW51bGw7ZCsrKWlmKCFhfHxjLmZpbHRlcihhLFtmXSkubGVuZ3RoKXtpZighYiYmZi5ub2RlVHlwZT09PTEpe2MuY2xlYW5EYXRhKGYuZ2V0RWxlbWVudHNCeVRhZ05hbWUoIioiKSk7Yy5jbGVhbkRhdGEoW2ZdKX1mLnBhcmVudE5vZGUmJmYucGFyZW50Tm9kZS5yZW1vdmVDaGlsZChmKX1yZXR1cm4gdGhpc30sZW1wdHk6ZnVuY3Rpb24oKXtmb3IodmFyIGE9MCxiOyhiPXRoaXNbYV0pIT1udWxsO2ErKylmb3IoYi5ub2RlVHlwZT09PTEmJmMuY2xlYW5EYXRhKGIuZ2V0RWxlbWVudHNCeVRhZ05hbWUoIioiKSk7Yi5maXJzdENoaWxkOyliLnJlbW92ZUNoaWxkKGIuZmlyc3RDaGlsZCk7DQpyZXR1cm4gdGhpc30sY2xvbmU6ZnVuY3Rpb24oYSl7dmFyIGI9dGhpcy5tYXAoZnVuY3Rpb24oKXtpZighYy5zdXBwb3J0Lm5vQ2xvbmVFdmVudCYmIWMuaXNYTUxEb2ModGhpcykpe3ZhciBkPXRoaXMub3V0ZXJIVE1MLGY9dGhpcy5vd25lckRvY3VtZW50O2lmKCFkKXtkPWYuY3JlYXRlRWxlbWVudCgiZGl2Iik7ZC5hcHBlbmRDaGlsZCh0aGlzLmNsb25lTm9kZSh0cnVlKSk7ZD1kLmlubmVySFRNTH1yZXR1cm4gYy5jbGVhbihbZC5yZXBsYWNlKEphLCIiKS5yZXBsYWNlKC89KFtePSInPlxzXStcLyk+L2csJz0iJDEiPicpLnJlcGxhY2UoViwiIildLGYpWzBdfWVsc2UgcmV0dXJuIHRoaXMuY2xvbmVOb2RlKHRydWUpfSk7aWYoYT09PXRydWUpe3JhKHRoaXMsYik7cmEodGhpcy5maW5kKCIqIiksYi5maW5kKCIqIikpfXJldHVybiBifSxodG1sOmZ1bmN0aW9uKGEpe2lmKGE9PT13KXJldHVybiB0aGlzWzBdJiZ0aGlzWzBdLm5vZGVUeXBlPT09MT90aGlzWzBdLmlubmVySFRNTC5yZXBsYWNlKEphLA0KIiIpOm51bGw7ZWxzZSBpZih0eXBlb2YgYT09PSJzdHJpbmciJiYhdGEudGVzdChhKSYmKGMuc3VwcG9ydC5sZWFkaW5nV2hpdGVzcGFjZXx8IVYudGVzdChhKSkmJiFGWyhMYS5leGVjKGEpfHxbIiIsIiJdKVsxXS50b0xvd2VyQ2FzZSgpXSl7YT1hLnJlcGxhY2UoS2EsTWEpO3RyeXtmb3IodmFyIGI9MCxkPXRoaXMubGVuZ3RoO2I8ZDtiKyspaWYodGhpc1tiXS5ub2RlVHlwZT09PTEpe2MuY2xlYW5EYXRhKHRoaXNbYl0uZ2V0RWxlbWVudHNCeVRhZ05hbWUoIioiKSk7dGhpc1tiXS5pbm5lckhUTUw9YX19Y2F0Y2goZil7dGhpcy5lbXB0eSgpLmFwcGVuZChhKX19ZWxzZSBjLmlzRnVuY3Rpb24oYSk/dGhpcy5lYWNoKGZ1bmN0aW9uKGUpe3ZhciBqPWModGhpcyksaT1qLmh0bWwoKTtqLmVtcHR5KCkuYXBwZW5kKGZ1bmN0aW9uKCl7cmV0dXJuIGEuY2FsbCh0aGlzLGUsaSl9KX0pOnRoaXMuZW1wdHkoKS5hcHBlbmQoYSk7cmV0dXJuIHRoaXN9LHJlcGxhY2VXaXRoOmZ1bmN0aW9uKGEpe2lmKHRoaXNbMF0mJg0KdGhpc1swXS5wYXJlbnROb2RlKXtpZihjLmlzRnVuY3Rpb24oYSkpcmV0dXJuIHRoaXMuZWFjaChmdW5jdGlvbihiKXt2YXIgZD1jKHRoaXMpLGY9ZC5odG1sKCk7ZC5yZXBsYWNlV2l0aChhLmNhbGwodGhpcyxiLGYpKX0pO2lmKHR5cGVvZiBhIT09InN0cmluZyIpYT1jKGEpLmRldGFjaCgpO3JldHVybiB0aGlzLmVhY2goZnVuY3Rpb24oKXt2YXIgYj10aGlzLm5leHRTaWJsaW5nLGQ9dGhpcy5wYXJlbnROb2RlO2ModGhpcykucmVtb3ZlKCk7Yj9jKGIpLmJlZm9yZShhKTpjKGQpLmFwcGVuZChhKX0pfWVsc2UgcmV0dXJuIHRoaXMucHVzaFN0YWNrKGMoYy5pc0Z1bmN0aW9uKGEpP2EoKTphKSwicmVwbGFjZVdpdGgiLGEpfSxkZXRhY2g6ZnVuY3Rpb24oYSl7cmV0dXJuIHRoaXMucmVtb3ZlKGEsdHJ1ZSl9LGRvbU1hbmlwOmZ1bmN0aW9uKGEsYixkKXtmdW5jdGlvbiBmKHUpe3JldHVybiBjLm5vZGVOYW1lKHUsInRhYmxlIik/dS5nZXRFbGVtZW50c0J5VGFnTmFtZSgidGJvZHkiKVswXXx8DQp1LmFwcGVuZENoaWxkKHUub3duZXJEb2N1bWVudC5jcmVhdGVFbGVtZW50KCJ0Ym9keSIpKTp1fXZhciBlLGosaT1hWzBdLG89W10saztpZighYy5zdXBwb3J0LmNoZWNrQ2xvbmUmJmFyZ3VtZW50cy5sZW5ndGg9PT0zJiZ0eXBlb2YgaT09PSJzdHJpbmciJiZ1YS50ZXN0KGkpKXJldHVybiB0aGlzLmVhY2goZnVuY3Rpb24oKXtjKHRoaXMpLmRvbU1hbmlwKGEsYixkLHRydWUpfSk7aWYoYy5pc0Z1bmN0aW9uKGkpKXJldHVybiB0aGlzLmVhY2goZnVuY3Rpb24odSl7dmFyIHo9Yyh0aGlzKTthWzBdPWkuY2FsbCh0aGlzLHUsYj96Lmh0bWwoKTp3KTt6LmRvbU1hbmlwKGEsYixkKX0pO2lmKHRoaXNbMF0pe2U9aSYmaS5wYXJlbnROb2RlO2U9Yy5zdXBwb3J0LnBhcmVudE5vZGUmJmUmJmUubm9kZVR5cGU9PT0xMSYmZS5jaGlsZE5vZGVzLmxlbmd0aD09PXRoaXMubGVuZ3RoP3tmcmFnbWVudDplfTpzYShhLHRoaXMsbyk7az1lLmZyYWdtZW50O2lmKGo9ay5jaGlsZE5vZGVzLmxlbmd0aD09PQ0KMT8oaz1rLmZpcnN0Q2hpbGQpOmsuZmlyc3RDaGlsZCl7Yj1iJiZjLm5vZGVOYW1lKGosInRyIik7Zm9yKHZhciBuPTAscj10aGlzLmxlbmd0aDtuPHI7bisrKWQuY2FsbChiP2YodGhpc1tuXSxqKTp0aGlzW25dLG4+MHx8ZS5jYWNoZWFibGV8fHRoaXMubGVuZ3RoPjE/ay5jbG9uZU5vZGUodHJ1ZSk6ayl9by5sZW5ndGgmJmMuZWFjaChvLFFhKX1yZXR1cm4gdGhpc319KTtjLmZyYWdtZW50cz17fTtjLmVhY2goe2FwcGVuZFRvOiJhcHBlbmQiLHByZXBlbmRUbzoicHJlcGVuZCIsaW5zZXJ0QmVmb3JlOiJiZWZvcmUiLGluc2VydEFmdGVyOiJhZnRlciIscmVwbGFjZUFsbDoicmVwbGFjZVdpdGgifSxmdW5jdGlvbihhLGIpe2MuZm5bYV09ZnVuY3Rpb24oZCl7dmFyIGY9W107ZD1jKGQpO3ZhciBlPXRoaXMubGVuZ3RoPT09MSYmdGhpc1swXS5wYXJlbnROb2RlO2lmKGUmJmUubm9kZVR5cGU9PT0xMSYmZS5jaGlsZE5vZGVzLmxlbmd0aD09PTEmJmQubGVuZ3RoPT09MSl7ZFtiXSh0aGlzWzBdKTsNCnJldHVybiB0aGlzfWVsc2V7ZT0wO2Zvcih2YXIgaj1kLmxlbmd0aDtlPGo7ZSsrKXt2YXIgaT0oZT4wP3RoaXMuY2xvbmUodHJ1ZSk6dGhpcykuZ2V0KCk7Yy5mbltiXS5hcHBseShjKGRbZV0pLGkpO2Y9Zi5jb25jYXQoaSl9cmV0dXJuIHRoaXMucHVzaFN0YWNrKGYsYSxkLnNlbGVjdG9yKX19fSk7Yy5leHRlbmQoe2NsZWFuOmZ1bmN0aW9uKGEsYixkLGYpe2I9Ynx8cztpZih0eXBlb2YgYi5jcmVhdGVFbGVtZW50PT09InVuZGVmaW5lZCIpYj1iLm93bmVyRG9jdW1lbnR8fGJbMF0mJmJbMF0ub3duZXJEb2N1bWVudHx8cztmb3IodmFyIGU9W10saj0wLGk7KGk9YVtqXSkhPW51bGw7aisrKXtpZih0eXBlb2YgaT09PSJudW1iZXIiKWkrPSIiO2lmKGkpe2lmKHR5cGVvZiBpPT09InN0cmluZyImJiFqYi50ZXN0KGkpKWk9Yi5jcmVhdGVUZXh0Tm9kZShpKTtlbHNlIGlmKHR5cGVvZiBpPT09InN0cmluZyIpe2k9aS5yZXBsYWNlKEthLE1hKTt2YXIgbz0oTGEuZXhlYyhpKXx8WyIiLA0KIiJdKVsxXS50b0xvd2VyQ2FzZSgpLGs9RltvXXx8Ri5fZGVmYXVsdCxuPWtbMF0scj1iLmNyZWF0ZUVsZW1lbnQoImRpdiIpO2ZvcihyLmlubmVySFRNTD1rWzFdK2kra1syXTtuLS07KXI9ci5sYXN0Q2hpbGQ7aWYoIWMuc3VwcG9ydC50Ym9keSl7bj1pYi50ZXN0KGkpO289bz09PSJ0YWJsZSImJiFuP3IuZmlyc3RDaGlsZCYmci5maXJzdENoaWxkLmNoaWxkTm9kZXM6a1sxXT09PSI8dGFibGU+IiYmIW4/ci5jaGlsZE5vZGVzOltdO2ZvcihrPW8ubGVuZ3RoLTE7az49MDstLWspYy5ub2RlTmFtZShvW2tdLCJ0Ym9keSIpJiYhb1trXS5jaGlsZE5vZGVzLmxlbmd0aCYmb1trXS5wYXJlbnROb2RlLnJlbW92ZUNoaWxkKG9ba10pfSFjLnN1cHBvcnQubGVhZGluZ1doaXRlc3BhY2UmJlYudGVzdChpKSYmci5pbnNlcnRCZWZvcmUoYi5jcmVhdGVUZXh0Tm9kZShWLmV4ZWMoaSlbMF0pLHIuZmlyc3RDaGlsZCk7aT1yLmNoaWxkTm9kZXN9aWYoaS5ub2RlVHlwZSllLnB1c2goaSk7ZWxzZSBlPQ0KYy5tZXJnZShlLGkpfX1pZihkKWZvcihqPTA7ZVtqXTtqKyspaWYoZiYmYy5ub2RlTmFtZShlW2pdLCJzY3JpcHQiKSYmKCFlW2pdLnR5cGV8fGVbal0udHlwZS50b0xvd2VyQ2FzZSgpPT09InRleHQvamF2YXNjcmlwdCIpKWYucHVzaChlW2pdLnBhcmVudE5vZGU/ZVtqXS5wYXJlbnROb2RlLnJlbW92ZUNoaWxkKGVbal0pOmVbal0pO2Vsc2V7ZVtqXS5ub2RlVHlwZT09PTEmJmUuc3BsaWNlLmFwcGx5KGUsW2orMSwwXS5jb25jYXQoYy5tYWtlQXJyYXkoZVtqXS5nZXRFbGVtZW50c0J5VGFnTmFtZSgic2NyaXB0IikpKSk7ZC5hcHBlbmRDaGlsZChlW2pdKX1yZXR1cm4gZX0sY2xlYW5EYXRhOmZ1bmN0aW9uKGEpe2Zvcih2YXIgYixkLGY9Yy5jYWNoZSxlPWMuZXZlbnQuc3BlY2lhbCxqPWMuc3VwcG9ydC5kZWxldGVFeHBhbmRvLGk9MCxvOyhvPWFbaV0pIT1udWxsO2krKylpZihkPW9bYy5leHBhbmRvXSl7Yj1mW2RdO2lmKGIuZXZlbnRzKWZvcih2YXIgayBpbiBiLmV2ZW50cyllW2tdPw0KYy5ldmVudC5yZW1vdmUobyxrKTpDYShvLGssYi5oYW5kbGUpO2lmKGopZGVsZXRlIG9bYy5leHBhbmRvXTtlbHNlIG8ucmVtb3ZlQXR0cmlidXRlJiZvLnJlbW92ZUF0dHJpYnV0ZShjLmV4cGFuZG8pO2RlbGV0ZSBmW2RdfX19KTt2YXIga2I9L3otP2luZGV4fGZvbnQtP3dlaWdodHxvcGFjaXR5fHpvb218bGluZS0/aGVpZ2h0L2ksTmE9L2FscGhhXChbXildKlwpLyxPYT0vb3BhY2l0eT0oW14pXSopLyxoYT0vZmxvYXQvaSxpYT0vLShbYS16XSkvaWcsbGI9LyhbQS1aXSkvZyxtYj0vXi0/XGQrKD86cHgpPyQvaSxuYj0vXi0/XGQvLG9iPXtwb3NpdGlvbjoiYWJzb2x1dGUiLHZpc2liaWxpdHk6ImhpZGRlbiIsZGlzcGxheToiYmxvY2sifSxwYj1bIkxlZnQiLCJSaWdodCJdLHFiPVsiVG9wIiwiQm90dG9tIl0scmI9cy5kZWZhdWx0VmlldyYmcy5kZWZhdWx0Vmlldy5nZXRDb21wdXRlZFN0eWxlLFBhPWMuc3VwcG9ydC5jc3NGbG9hdD8iY3NzRmxvYXQiOiJzdHlsZUZsb2F0IixqYT0NCmZ1bmN0aW9uKGEsYil7cmV0dXJuIGIudG9VcHBlckNhc2UoKX07Yy5mbi5jc3M9ZnVuY3Rpb24oYSxiKXtyZXR1cm4gWCh0aGlzLGEsYix0cnVlLGZ1bmN0aW9uKGQsZixlKXtpZihlPT09dylyZXR1cm4gYy5jdXJDU1MoZCxmKTtpZih0eXBlb2YgZT09PSJudW1iZXIiJiYha2IudGVzdChmKSllKz0icHgiO2Muc3R5bGUoZCxmLGUpfSl9O2MuZXh0ZW5kKHtzdHlsZTpmdW5jdGlvbihhLGIsZCl7aWYoIWF8fGEubm9kZVR5cGU9PT0zfHxhLm5vZGVUeXBlPT09OClyZXR1cm4gdztpZigoYj09PSJ3aWR0aCJ8fGI9PT0iaGVpZ2h0IikmJnBhcnNlRmxvYXQoZCk8MClkPXc7dmFyIGY9YS5zdHlsZXx8YSxlPWQhPT13O2lmKCFjLnN1cHBvcnQub3BhY2l0eSYmYj09PSJvcGFjaXR5Iil7aWYoZSl7Zi56b29tPTE7Yj1wYXJzZUludChkLDEwKSsiIj09PSJOYU4iPyIiOiJhbHBoYShvcGFjaXR5PSIrZCoxMDArIikiO2E9Zi5maWx0ZXJ8fGMuY3VyQ1NTKGEsImZpbHRlciIpfHwiIjtmLmZpbHRlcj0NCk5hLnRlc3QoYSk/YS5yZXBsYWNlKE5hLGIpOmJ9cmV0dXJuIGYuZmlsdGVyJiZmLmZpbHRlci5pbmRleE9mKCJvcGFjaXR5PSIpPj0wP3BhcnNlRmxvYXQoT2EuZXhlYyhmLmZpbHRlcilbMV0pLzEwMCsiIjoiIn1pZihoYS50ZXN0KGIpKWI9UGE7Yj1iLnJlcGxhY2UoaWEsamEpO2lmKGUpZltiXT1kO3JldHVybiBmW2JdfSxjc3M6ZnVuY3Rpb24oYSxiLGQsZil7aWYoYj09PSJ3aWR0aCJ8fGI9PT0iaGVpZ2h0Iil7dmFyIGUsaj1iPT09IndpZHRoIj9wYjpxYjtmdW5jdGlvbiBpKCl7ZT1iPT09IndpZHRoIj9hLm9mZnNldFdpZHRoOmEub2Zmc2V0SGVpZ2h0O2YhPT0iYm9yZGVyIiYmYy5lYWNoKGosZnVuY3Rpb24oKXtmfHwoZS09cGFyc2VGbG9hdChjLmN1ckNTUyhhLCJwYWRkaW5nIit0aGlzLHRydWUpKXx8MCk7aWYoZj09PSJtYXJnaW4iKWUrPXBhcnNlRmxvYXQoYy5jdXJDU1MoYSwibWFyZ2luIit0aGlzLHRydWUpKXx8MDtlbHNlIGUtPXBhcnNlRmxvYXQoYy5jdXJDU1MoYSwNCiJib3JkZXIiK3RoaXMrIldpZHRoIix0cnVlKSl8fDB9KX1hLm9mZnNldFdpZHRoIT09MD9pKCk6Yy5zd2FwKGEsb2IsaSk7cmV0dXJuIE1hdGgubWF4KDAsTWF0aC5yb3VuZChlKSl9cmV0dXJuIGMuY3VyQ1NTKGEsYixkKX0sY3VyQ1NTOmZ1bmN0aW9uKGEsYixkKXt2YXIgZixlPWEuc3R5bGU7aWYoIWMuc3VwcG9ydC5vcGFjaXR5JiZiPT09Im9wYWNpdHkiJiZhLmN1cnJlbnRTdHlsZSl7Zj1PYS50ZXN0KGEuY3VycmVudFN0eWxlLmZpbHRlcnx8IiIpP3BhcnNlRmxvYXQoUmVnRXhwLiQxKS8xMDArIiI6IiI7cmV0dXJuIGY9PT0iIj8iMSI6Zn1pZihoYS50ZXN0KGIpKWI9UGE7aWYoIWQmJmUmJmVbYl0pZj1lW2JdO2Vsc2UgaWYocmIpe2lmKGhhLnRlc3QoYikpYj0iZmxvYXQiO2I9Yi5yZXBsYWNlKGxiLCItJDEiKS50b0xvd2VyQ2FzZSgpO2U9YS5vd25lckRvY3VtZW50LmRlZmF1bHRWaWV3O2lmKCFlKXJldHVybiBudWxsO2lmKGE9ZS5nZXRDb21wdXRlZFN0eWxlKGEsbnVsbCkpZj0NCmEuZ2V0UHJvcGVydHlWYWx1ZShiKTtpZihiPT09Im9wYWNpdHkiJiZmPT09IiIpZj0iMSJ9ZWxzZSBpZihhLmN1cnJlbnRTdHlsZSl7ZD1iLnJlcGxhY2UoaWEsamEpO2Y9YS5jdXJyZW50U3R5bGVbYl18fGEuY3VycmVudFN0eWxlW2RdO2lmKCFtYi50ZXN0KGYpJiZuYi50ZXN0KGYpKXtiPWUubGVmdDt2YXIgaj1hLnJ1bnRpbWVTdHlsZS5sZWZ0O2EucnVudGltZVN0eWxlLmxlZnQ9YS5jdXJyZW50U3R5bGUubGVmdDtlLmxlZnQ9ZD09PSJmb250U2l6ZSI/IjFlbSI6Znx8MDtmPWUucGl4ZWxMZWZ0KyJweCI7ZS5sZWZ0PWI7YS5ydW50aW1lU3R5bGUubGVmdD1qfX1yZXR1cm4gZn0sc3dhcDpmdW5jdGlvbihhLGIsZCl7dmFyIGY9e307Zm9yKHZhciBlIGluIGIpe2ZbZV09YS5zdHlsZVtlXTthLnN0eWxlW2VdPWJbZV19ZC5jYWxsKGEpO2ZvcihlIGluIGIpYS5zdHlsZVtlXT1mW2VdfX0pO2lmKGMuZXhwciYmYy5leHByLmZpbHRlcnMpe2MuZXhwci5maWx0ZXJzLmhpZGRlbj1mdW5jdGlvbihhKXt2YXIgYj0NCmEub2Zmc2V0V2lkdGgsZD1hLm9mZnNldEhlaWdodCxmPWEubm9kZU5hbWUudG9Mb3dlckNhc2UoKT09PSJ0ciI7cmV0dXJuIGI9PT0wJiZkPT09MCYmIWY/dHJ1ZTpiPjAmJmQ+MCYmIWY/ZmFsc2U6Yy5jdXJDU1MoYSwiZGlzcGxheSIpPT09Im5vbmUifTtjLmV4cHIuZmlsdGVycy52aXNpYmxlPWZ1bmN0aW9uKGEpe3JldHVybiFjLmV4cHIuZmlsdGVycy5oaWRkZW4oYSl9fXZhciBzYj1KKCksdGI9LzxzY3JpcHQoLnxccykqP1wvc2NyaXB0Pi9naSx1Yj0vc2VsZWN0fHRleHRhcmVhL2ksdmI9L2NvbG9yfGRhdGV8ZGF0ZXRpbWV8ZW1haWx8aGlkZGVufG1vbnRofG51bWJlcnxwYXNzd29yZHxyYW5nZXxzZWFyY2h8dGVsfHRleHR8dGltZXx1cmx8d2Vlay9pLE49Lz1cPygmfCQpLyxrYT0vXD8vLHdiPS8oXD98JilfPS4qPygmfCQpLyx4Yj0vXihcdys6KT9cL1wvKFteXC8/I10rKS8seWI9LyUyMC9nLHpiPWMuZm4ubG9hZDtjLmZuLmV4dGVuZCh7bG9hZDpmdW5jdGlvbihhLGIsZCl7aWYodHlwZW9mIGEhPT0NCiJzdHJpbmciKXJldHVybiB6Yi5jYWxsKHRoaXMsYSk7ZWxzZSBpZighdGhpcy5sZW5ndGgpcmV0dXJuIHRoaXM7dmFyIGY9YS5pbmRleE9mKCIgIik7aWYoZj49MCl7dmFyIGU9YS5zbGljZShmLGEubGVuZ3RoKTthPWEuc2xpY2UoMCxmKX1mPSJHRVQiO2lmKGIpaWYoYy5pc0Z1bmN0aW9uKGIpKXtkPWI7Yj1udWxsfWVsc2UgaWYodHlwZW9mIGI9PT0ib2JqZWN0Iil7Yj1jLnBhcmFtKGIsYy5hamF4U2V0dGluZ3MudHJhZGl0aW9uYWwpO2Y9IlBPU1QifXZhciBqPXRoaXM7Yy5hamF4KHt1cmw6YSx0eXBlOmYsZGF0YVR5cGU6Imh0bWwiLGRhdGE6Yixjb21wbGV0ZTpmdW5jdGlvbihpLG8pe2lmKG89PT0ic3VjY2VzcyJ8fG89PT0ibm90bW9kaWZpZWQiKWouaHRtbChlP2MoIjxkaXYgLz4iKS5hcHBlbmQoaS5yZXNwb25zZVRleHQucmVwbGFjZSh0YiwiIikpLmZpbmQoZSk6aS5yZXNwb25zZVRleHQpO2QmJmouZWFjaChkLFtpLnJlc3BvbnNlVGV4dCxvLGldKX19KTtyZXR1cm4gdGhpc30sDQpzZXJpYWxpemU6ZnVuY3Rpb24oKXtyZXR1cm4gYy5wYXJhbSh0aGlzLnNlcmlhbGl6ZUFycmF5KCkpfSxzZXJpYWxpemVBcnJheTpmdW5jdGlvbigpe3JldHVybiB0aGlzLm1hcChmdW5jdGlvbigpe3JldHVybiB0aGlzLmVsZW1lbnRzP2MubWFrZUFycmF5KHRoaXMuZWxlbWVudHMpOnRoaXN9KS5maWx0ZXIoZnVuY3Rpb24oKXtyZXR1cm4gdGhpcy5uYW1lJiYhdGhpcy5kaXNhYmxlZCYmKHRoaXMuY2hlY2tlZHx8dWIudGVzdCh0aGlzLm5vZGVOYW1lKXx8dmIudGVzdCh0aGlzLnR5cGUpKX0pLm1hcChmdW5jdGlvbihhLGIpe2E9Yyh0aGlzKS52YWwoKTtyZXR1cm4gYT09bnVsbD9udWxsOmMuaXNBcnJheShhKT9jLm1hcChhLGZ1bmN0aW9uKGQpe3JldHVybntuYW1lOmIubmFtZSx2YWx1ZTpkfX0pOntuYW1lOmIubmFtZSx2YWx1ZTphfX0pLmdldCgpfX0pO2MuZWFjaCgiYWpheFN0YXJ0IGFqYXhTdG9wIGFqYXhDb21wbGV0ZSBhamF4RXJyb3IgYWpheFN1Y2Nlc3MgYWpheFNlbmQiLnNwbGl0KCIgIiksDQpmdW5jdGlvbihhLGIpe2MuZm5bYl09ZnVuY3Rpb24oZCl7cmV0dXJuIHRoaXMuYmluZChiLGQpfX0pO2MuZXh0ZW5kKHtnZXQ6ZnVuY3Rpb24oYSxiLGQsZil7aWYoYy5pc0Z1bmN0aW9uKGIpKXtmPWZ8fGQ7ZD1iO2I9bnVsbH1yZXR1cm4gYy5hamF4KHt0eXBlOiJHRVQiLHVybDphLGRhdGE6YixzdWNjZXNzOmQsZGF0YVR5cGU6Zn0pfSxnZXRTY3JpcHQ6ZnVuY3Rpb24oYSxiKXtyZXR1cm4gYy5nZXQoYSxudWxsLGIsInNjcmlwdCIpfSxnZXRKU09OOmZ1bmN0aW9uKGEsYixkKXtyZXR1cm4gYy5nZXQoYSxiLGQsImpzb24iKX0scG9zdDpmdW5jdGlvbihhLGIsZCxmKXtpZihjLmlzRnVuY3Rpb24oYikpe2Y9Znx8ZDtkPWI7Yj17fX1yZXR1cm4gYy5hamF4KHt0eXBlOiJQT1NUIix1cmw6YSxkYXRhOmIsc3VjY2VzczpkLGRhdGFUeXBlOmZ9KX0sYWpheFNldHVwOmZ1bmN0aW9uKGEpe2MuZXh0ZW5kKGMuYWpheFNldHRpbmdzLGEpfSxhamF4U2V0dGluZ3M6e3VybDpsb2NhdGlvbi5ocmVmLA0KZ2xvYmFsOnRydWUsdHlwZToiR0VUIixjb250ZW50VHlwZToiYXBwbGljYXRpb24veC13d3ctZm9ybS11cmxlbmNvZGVkIixwcm9jZXNzRGF0YTp0cnVlLGFzeW5jOnRydWUseGhyOkEuWE1MSHR0cFJlcXVlc3QmJihBLmxvY2F0aW9uLnByb3RvY29sIT09ImZpbGU6Inx8IUEuQWN0aXZlWE9iamVjdCk/ZnVuY3Rpb24oKXtyZXR1cm4gbmV3IEEuWE1MSHR0cFJlcXVlc3R9OmZ1bmN0aW9uKCl7dHJ5e3JldHVybiBuZXcgQS5BY3RpdmVYT2JqZWN0KCJNaWNyb3NvZnQuWE1MSFRUUCIpfWNhdGNoKGEpe319LGFjY2VwdHM6e3htbDoiYXBwbGljYXRpb24veG1sLCB0ZXh0L3htbCIsaHRtbDoidGV4dC9odG1sIixzY3JpcHQ6InRleHQvamF2YXNjcmlwdCwgYXBwbGljYXRpb24vamF2YXNjcmlwdCIsanNvbjoiYXBwbGljYXRpb24vanNvbiwgdGV4dC9qYXZhc2NyaXB0Iix0ZXh0OiJ0ZXh0L3BsYWluIixfZGVmYXVsdDoiKi8qIn19LGxhc3RNb2RpZmllZDp7fSxldGFnOnt9LGFqYXg6ZnVuY3Rpb24oYSl7ZnVuY3Rpb24gYigpe2Uuc3VjY2VzcyYmDQplLnN1Y2Nlc3MuY2FsbChrLG8saSx4KTtlLmdsb2JhbCYmZigiYWpheFN1Y2Nlc3MiLFt4LGVdKX1mdW5jdGlvbiBkKCl7ZS5jb21wbGV0ZSYmZS5jb21wbGV0ZS5jYWxsKGsseCxpKTtlLmdsb2JhbCYmZigiYWpheENvbXBsZXRlIixbeCxlXSk7ZS5nbG9iYWwmJiEtLWMuYWN0aXZlJiZjLmV2ZW50LnRyaWdnZXIoImFqYXhTdG9wIil9ZnVuY3Rpb24gZihxLHApeyhlLmNvbnRleHQ/YyhlLmNvbnRleHQpOmMuZXZlbnQpLnRyaWdnZXIocSxwKX12YXIgZT1jLmV4dGVuZCh0cnVlLHt9LGMuYWpheFNldHRpbmdzLGEpLGosaSxvLGs9YSYmYS5jb250ZXh0fHxlLG49ZS50eXBlLnRvVXBwZXJDYXNlKCk7aWYoZS5kYXRhJiZlLnByb2Nlc3NEYXRhJiZ0eXBlb2YgZS5kYXRhIT09InN0cmluZyIpZS5kYXRhPWMucGFyYW0oZS5kYXRhLGUudHJhZGl0aW9uYWwpO2lmKGUuZGF0YVR5cGU9PT0ianNvbnAiKXtpZihuPT09IkdFVCIpTi50ZXN0KGUudXJsKXx8KGUudXJsKz0oa2EudGVzdChlLnVybCk/DQoiJiI6Ij8iKSsoZS5qc29ucHx8ImNhbGxiYWNrIikrIj0/Iik7ZWxzZSBpZighZS5kYXRhfHwhTi50ZXN0KGUuZGF0YSkpZS5kYXRhPShlLmRhdGE/ZS5kYXRhKyImIjoiIikrKGUuanNvbnB8fCJjYWxsYmFjayIpKyI9PyI7ZS5kYXRhVHlwZT0ianNvbiJ9aWYoZS5kYXRhVHlwZT09PSJqc29uIiYmKGUuZGF0YSYmTi50ZXN0KGUuZGF0YSl8fE4udGVzdChlLnVybCkpKXtqPWUuanNvbnBDYWxsYmFja3x8Impzb25wIitzYisrO2lmKGUuZGF0YSllLmRhdGE9KGUuZGF0YSsiIikucmVwbGFjZShOLCI9IitqKyIkMSIpO2UudXJsPWUudXJsLnJlcGxhY2UoTiwiPSIraisiJDEiKTtlLmRhdGFUeXBlPSJzY3JpcHQiO0Fbal09QVtqXXx8ZnVuY3Rpb24ocSl7bz1xO2IoKTtkKCk7QVtqXT13O3RyeXtkZWxldGUgQVtqXX1jYXRjaChwKXt9eiYmei5yZW1vdmVDaGlsZChDKX19aWYoZS5kYXRhVHlwZT09PSJzY3JpcHQiJiZlLmNhY2hlPT09bnVsbCllLmNhY2hlPWZhbHNlO2lmKGUuY2FjaGU9PT0NCmZhbHNlJiZuPT09IkdFVCIpe3ZhciByPUooKSx1PWUudXJsLnJlcGxhY2Uod2IsIiQxXz0iK3IrIiQyIik7ZS51cmw9dSsodT09PWUudXJsPyhrYS50ZXN0KGUudXJsKT8iJiI6Ij8iKSsiXz0iK3I6IiIpfWlmKGUuZGF0YSYmbj09PSJHRVQiKWUudXJsKz0oa2EudGVzdChlLnVybCk/IiYiOiI/IikrZS5kYXRhO2UuZ2xvYmFsJiYhYy5hY3RpdmUrKyYmYy5ldmVudC50cmlnZ2VyKCJhamF4U3RhcnQiKTtyPShyPXhiLmV4ZWMoZS51cmwpKSYmKHJbMV0mJnJbMV0hPT1sb2NhdGlvbi5wcm90b2NvbHx8clsyXSE9PWxvY2F0aW9uLmhvc3QpO2lmKGUuZGF0YVR5cGU9PT0ic2NyaXB0IiYmbj09PSJHRVQiJiZyKXt2YXIgej1zLmdldEVsZW1lbnRzQnlUYWdOYW1lKCJoZWFkIilbMF18fHMuZG9jdW1lbnRFbGVtZW50LEM9cy5jcmVhdGVFbGVtZW50KCJzY3JpcHQiKTtDLnNyYz1lLnVybDtpZihlLnNjcmlwdENoYXJzZXQpQy5jaGFyc2V0PWUuc2NyaXB0Q2hhcnNldDtpZighail7dmFyIEI9DQpmYWxzZTtDLm9ubG9hZD1DLm9ucmVhZHlzdGF0ZWNoYW5nZT1mdW5jdGlvbigpe2lmKCFCJiYoIXRoaXMucmVhZHlTdGF0ZXx8dGhpcy5yZWFkeVN0YXRlPT09ImxvYWRlZCJ8fHRoaXMucmVhZHlTdGF0ZT09PSJjb21wbGV0ZSIpKXtCPXRydWU7YigpO2QoKTtDLm9ubG9hZD1DLm9ucmVhZHlzdGF0ZWNoYW5nZT1udWxsO3omJkMucGFyZW50Tm9kZSYmei5yZW1vdmVDaGlsZChDKX19fXouaW5zZXJ0QmVmb3JlKEMsei5maXJzdENoaWxkKTtyZXR1cm4gd312YXIgRT1mYWxzZSx4PWUueGhyKCk7aWYoeCl7ZS51c2VybmFtZT94Lm9wZW4obixlLnVybCxlLmFzeW5jLGUudXNlcm5hbWUsZS5wYXNzd29yZCk6eC5vcGVuKG4sZS51cmwsZS5hc3luYyk7dHJ5e2lmKGUuZGF0YXx8YSYmYS5jb250ZW50VHlwZSl4LnNldFJlcXVlc3RIZWFkZXIoIkNvbnRlbnQtVHlwZSIsZS5jb250ZW50VHlwZSk7aWYoZS5pZk1vZGlmaWVkKXtjLmxhc3RNb2RpZmllZFtlLnVybF0mJnguc2V0UmVxdWVzdEhlYWRlcigiSWYtTW9kaWZpZWQtU2luY2UiLA0KYy5sYXN0TW9kaWZpZWRbZS51cmxdKTtjLmV0YWdbZS51cmxdJiZ4LnNldFJlcXVlc3RIZWFkZXIoIklmLU5vbmUtTWF0Y2giLGMuZXRhZ1tlLnVybF0pfXJ8fHguc2V0UmVxdWVzdEhlYWRlcigiWC1SZXF1ZXN0ZWQtV2l0aCIsIlhNTEh0dHBSZXF1ZXN0Iik7eC5zZXRSZXF1ZXN0SGVhZGVyKCJBY2NlcHQiLGUuZGF0YVR5cGUmJmUuYWNjZXB0c1tlLmRhdGFUeXBlXT9lLmFjY2VwdHNbZS5kYXRhVHlwZV0rIiwgKi8qIjplLmFjY2VwdHMuX2RlZmF1bHQpfWNhdGNoKGdhKXt9aWYoZS5iZWZvcmVTZW5kJiZlLmJlZm9yZVNlbmQuY2FsbChrLHgsZSk9PT1mYWxzZSl7ZS5nbG9iYWwmJiEtLWMuYWN0aXZlJiZjLmV2ZW50LnRyaWdnZXIoImFqYXhTdG9wIik7eC5hYm9ydCgpO3JldHVybiBmYWxzZX1lLmdsb2JhbCYmZigiYWpheFNlbmQiLFt4LGVdKTt2YXIgZz14Lm9ucmVhZHlzdGF0ZWNoYW5nZT1mdW5jdGlvbihxKXtpZigheHx8eC5yZWFkeVN0YXRlPT09MHx8cT09PSJhYm9ydCIpe0V8fA0KZCgpO0U9dHJ1ZTtpZih4KXgub25yZWFkeXN0YXRlY2hhbmdlPWMubm9vcH1lbHNlIGlmKCFFJiZ4JiYoeC5yZWFkeVN0YXRlPT09NHx8cT09PSJ0aW1lb3V0Iikpe0U9dHJ1ZTt4Lm9ucmVhZHlzdGF0ZWNoYW5nZT1jLm5vb3A7aT1xPT09InRpbWVvdXQiPyJ0aW1lb3V0IjohYy5odHRwU3VjY2Vzcyh4KT8iZXJyb3IiOmUuaWZNb2RpZmllZCYmYy5odHRwTm90TW9kaWZpZWQoeCxlLnVybCk/Im5vdG1vZGlmaWVkIjoic3VjY2VzcyI7dmFyIHA7aWYoaT09PSJzdWNjZXNzIil0cnl7bz1jLmh0dHBEYXRhKHgsZS5kYXRhVHlwZSxlKX1jYXRjaCh2KXtpPSJwYXJzZXJlcnJvciI7cD12fWlmKGk9PT0ic3VjY2VzcyJ8fGk9PT0ibm90bW9kaWZpZWQiKWp8fGIoKTtlbHNlIGMuaGFuZGxlRXJyb3IoZSx4LGkscCk7ZCgpO3E9PT0idGltZW91dCImJnguYWJvcnQoKTtpZihlLmFzeW5jKXg9bnVsbH19O3RyeXt2YXIgaD14LmFib3J0O3guYWJvcnQ9ZnVuY3Rpb24oKXt4JiZoLmNhbGwoeCk7DQpnKCJhYm9ydCIpfX1jYXRjaChsKXt9ZS5hc3luYyYmZS50aW1lb3V0PjAmJnNldFRpbWVvdXQoZnVuY3Rpb24oKXt4JiYhRSYmZygidGltZW91dCIpfSxlLnRpbWVvdXQpO3RyeXt4LnNlbmQobj09PSJQT1NUInx8bj09PSJQVVQifHxuPT09IkRFTEVURSI/ZS5kYXRhOm51bGwpfWNhdGNoKG0pe2MuaGFuZGxlRXJyb3IoZSx4LG51bGwsbSk7ZCgpfWUuYXN5bmN8fGcoKTtyZXR1cm4geH19LGhhbmRsZUVycm9yOmZ1bmN0aW9uKGEsYixkLGYpe2lmKGEuZXJyb3IpYS5lcnJvci5jYWxsKGEuY29udGV4dHx8YSxiLGQsZik7aWYoYS5nbG9iYWwpKGEuY29udGV4dD9jKGEuY29udGV4dCk6Yy5ldmVudCkudHJpZ2dlcigiYWpheEVycm9yIixbYixhLGZdKX0sYWN0aXZlOjAsaHR0cFN1Y2Nlc3M6ZnVuY3Rpb24oYSl7dHJ5e3JldHVybiFhLnN0YXR1cyYmbG9jYXRpb24ucHJvdG9jb2w9PT0iZmlsZToifHxhLnN0YXR1cz49MjAwJiZhLnN0YXR1czwzMDB8fGEuc3RhdHVzPT09MzA0fHxhLnN0YXR1cz09PQ0KMTIyM3x8YS5zdGF0dXM9PT0wfWNhdGNoKGIpe31yZXR1cm4gZmFsc2V9LGh0dHBOb3RNb2RpZmllZDpmdW5jdGlvbihhLGIpe3ZhciBkPWEuZ2V0UmVzcG9uc2VIZWFkZXIoIkxhc3QtTW9kaWZpZWQiKSxmPWEuZ2V0UmVzcG9uc2VIZWFkZXIoIkV0YWciKTtpZihkKWMubGFzdE1vZGlmaWVkW2JdPWQ7aWYoZiljLmV0YWdbYl09ZjtyZXR1cm4gYS5zdGF0dXM9PT0zMDR8fGEuc3RhdHVzPT09MH0saHR0cERhdGE6ZnVuY3Rpb24oYSxiLGQpe3ZhciBmPWEuZ2V0UmVzcG9uc2VIZWFkZXIoImNvbnRlbnQtdHlwZSIpfHwiIixlPWI9PT0ieG1sInx8IWImJmYuaW5kZXhPZigieG1sIik+PTA7YT1lP2EucmVzcG9uc2VYTUw6YS5yZXNwb25zZVRleHQ7ZSYmYS5kb2N1bWVudEVsZW1lbnQubm9kZU5hbWU9PT0icGFyc2VyZXJyb3IiJiZjLmVycm9yKCJwYXJzZXJlcnJvciIpO2lmKGQmJmQuZGF0YUZpbHRlcilhPWQuZGF0YUZpbHRlcihhLGIpO2lmKHR5cGVvZiBhPT09InN0cmluZyIpaWYoYj09PQ0KImpzb24ifHwhYiYmZi5pbmRleE9mKCJqc29uIik+PTApYT1jLnBhcnNlSlNPTihhKTtlbHNlIGlmKGI9PT0ic2NyaXB0Inx8IWImJmYuaW5kZXhPZigiamF2YXNjcmlwdCIpPj0wKWMuZ2xvYmFsRXZhbChhKTtyZXR1cm4gYX0scGFyYW06ZnVuY3Rpb24oYSxiKXtmdW5jdGlvbiBkKGksbyl7aWYoYy5pc0FycmF5KG8pKWMuZWFjaChvLGZ1bmN0aW9uKGssbil7Ynx8L1xbXF0kLy50ZXN0KGkpP2YoaSxuKTpkKGkrIlsiKyh0eXBlb2Ygbj09PSJvYmplY3QifHxjLmlzQXJyYXkobik/azoiIikrIl0iLG4pfSk7ZWxzZSFiJiZvIT1udWxsJiZ0eXBlb2Ygbz09PSJvYmplY3QiP2MuZWFjaChvLGZ1bmN0aW9uKGssbil7ZChpKyJbIitrKyJdIixuKX0pOmYoaSxvKX1mdW5jdGlvbiBmKGksbyl7bz1jLmlzRnVuY3Rpb24obyk/bygpOm87ZVtlLmxlbmd0aF09ZW5jb2RlVVJJQ29tcG9uZW50KGkpKyI9IitlbmNvZGVVUklDb21wb25lbnQobyl9dmFyIGU9W107aWYoYj09PXcpYj1jLmFqYXhTZXR0aW5ncy50cmFkaXRpb25hbDsNCmlmKGMuaXNBcnJheShhKXx8YS5qcXVlcnkpYy5lYWNoKGEsZnVuY3Rpb24oKXtmKHRoaXMubmFtZSx0aGlzLnZhbHVlKX0pO2Vsc2UgZm9yKHZhciBqIGluIGEpZChqLGFbal0pO3JldHVybiBlLmpvaW4oIiYiKS5yZXBsYWNlKHliLCIrIil9fSk7dmFyIGxhPXt9LEFiPS90b2dnbGV8c2hvd3xoaWRlLyxCYj0vXihbKy1dPSk/KFtcZCstLl0rKSguKikkLyxXLHZhPVtbImhlaWdodCIsIm1hcmdpblRvcCIsIm1hcmdpbkJvdHRvbSIsInBhZGRpbmdUb3AiLCJwYWRkaW5nQm90dG9tIl0sWyJ3aWR0aCIsIm1hcmdpbkxlZnQiLCJtYXJnaW5SaWdodCIsInBhZGRpbmdMZWZ0IiwicGFkZGluZ1JpZ2h0Il0sWyJvcGFjaXR5Il1dO2MuZm4uZXh0ZW5kKHtzaG93OmZ1bmN0aW9uKGEsYil7aWYoYXx8YT09PTApcmV0dXJuIHRoaXMuYW5pbWF0ZShLKCJzaG93IiwzKSxhLGIpO2Vsc2V7YT0wO2ZvcihiPXRoaXMubGVuZ3RoO2E8YjthKyspe3ZhciBkPWMuZGF0YSh0aGlzW2FdLCJvbGRkaXNwbGF5Iik7DQp0aGlzW2FdLnN0eWxlLmRpc3BsYXk9ZHx8IiI7aWYoYy5jc3ModGhpc1thXSwiZGlzcGxheSIpPT09Im5vbmUiKXtkPXRoaXNbYV0ubm9kZU5hbWU7dmFyIGY7aWYobGFbZF0pZj1sYVtkXTtlbHNle3ZhciBlPWMoIjwiK2QrIiAvPiIpLmFwcGVuZFRvKCJib2R5Iik7Zj1lLmNzcygiZGlzcGxheSIpO2lmKGY9PT0ibm9uZSIpZj0iYmxvY2siO2UucmVtb3ZlKCk7bGFbZF09Zn1jLmRhdGEodGhpc1thXSwib2xkZGlzcGxheSIsZil9fWE9MDtmb3IoYj10aGlzLmxlbmd0aDthPGI7YSsrKXRoaXNbYV0uc3R5bGUuZGlzcGxheT1jLmRhdGEodGhpc1thXSwib2xkZGlzcGxheSIpfHwiIjtyZXR1cm4gdGhpc319LGhpZGU6ZnVuY3Rpb24oYSxiKXtpZihhfHxhPT09MClyZXR1cm4gdGhpcy5hbmltYXRlKEsoImhpZGUiLDMpLGEsYik7ZWxzZXthPTA7Zm9yKGI9dGhpcy5sZW5ndGg7YTxiO2ErKyl7dmFyIGQ9Yy5kYXRhKHRoaXNbYV0sIm9sZGRpc3BsYXkiKTshZCYmZCE9PSJub25lIiYmYy5kYXRhKHRoaXNbYV0sDQoib2xkZGlzcGxheSIsYy5jc3ModGhpc1thXSwiZGlzcGxheSIpKX1hPTA7Zm9yKGI9dGhpcy5sZW5ndGg7YTxiO2ErKyl0aGlzW2FdLnN0eWxlLmRpc3BsYXk9Im5vbmUiO3JldHVybiB0aGlzfX0sX3RvZ2dsZTpjLmZuLnRvZ2dsZSx0b2dnbGU6ZnVuY3Rpb24oYSxiKXt2YXIgZD10eXBlb2YgYT09PSJib29sZWFuIjtpZihjLmlzRnVuY3Rpb24oYSkmJmMuaXNGdW5jdGlvbihiKSl0aGlzLl90b2dnbGUuYXBwbHkodGhpcyxhcmd1bWVudHMpO2Vsc2UgYT09bnVsbHx8ZD90aGlzLmVhY2goZnVuY3Rpb24oKXt2YXIgZj1kP2E6Yyh0aGlzKS5pcygiOmhpZGRlbiIpO2ModGhpcylbZj8ic2hvdyI6ImhpZGUiXSgpfSk6dGhpcy5hbmltYXRlKEsoInRvZ2dsZSIsMyksYSxiKTtyZXR1cm4gdGhpc30sZmFkZVRvOmZ1bmN0aW9uKGEsYixkKXtyZXR1cm4gdGhpcy5maWx0ZXIoIjpoaWRkZW4iKS5jc3MoIm9wYWNpdHkiLDApLnNob3coKS5lbmQoKS5hbmltYXRlKHtvcGFjaXR5OmJ9LGEsZCl9LA0KYW5pbWF0ZTpmdW5jdGlvbihhLGIsZCxmKXt2YXIgZT1jLnNwZWVkKGIsZCxmKTtpZihjLmlzRW1wdHlPYmplY3QoYSkpcmV0dXJuIHRoaXMuZWFjaChlLmNvbXBsZXRlKTtyZXR1cm4gdGhpc1tlLnF1ZXVlPT09ZmFsc2U/ImVhY2giOiJxdWV1ZSJdKGZ1bmN0aW9uKCl7dmFyIGo9Yy5leHRlbmQoe30sZSksaSxvPXRoaXMubm9kZVR5cGU9PT0xJiZjKHRoaXMpLmlzKCI6aGlkZGVuIiksaz10aGlzO2ZvcihpIGluIGEpe3ZhciBuPWkucmVwbGFjZShpYSxqYSk7aWYoaSE9PW4pe2Fbbl09YVtpXTtkZWxldGUgYVtpXTtpPW59aWYoYVtpXT09PSJoaWRlIiYmb3x8YVtpXT09PSJzaG93IiYmIW8pcmV0dXJuIGouY29tcGxldGUuY2FsbCh0aGlzKTtpZigoaT09PSJoZWlnaHQifHxpPT09IndpZHRoIikmJnRoaXMuc3R5bGUpe2ouZGlzcGxheT1jLmNzcyh0aGlzLCJkaXNwbGF5Iik7ai5vdmVyZmxvdz10aGlzLnN0eWxlLm92ZXJmbG93fWlmKGMuaXNBcnJheShhW2ldKSl7KGouc3BlY2lhbEVhc2luZz0NCmouc3BlY2lhbEVhc2luZ3x8e30pW2ldPWFbaV1bMV07YVtpXT1hW2ldWzBdfX1pZihqLm92ZXJmbG93IT1udWxsKXRoaXMuc3R5bGUub3ZlcmZsb3c9ImhpZGRlbiI7ai5jdXJBbmltPWMuZXh0ZW5kKHt9LGEpO2MuZWFjaChhLGZ1bmN0aW9uKHIsdSl7dmFyIHo9bmV3IGMuZngoayxqLHIpO2lmKEFiLnRlc3QodSkpelt1PT09InRvZ2dsZSI/bz8ic2hvdyI6ImhpZGUiOnVdKGEpO2Vsc2V7dmFyIEM9QmIuZXhlYyh1KSxCPXouY3VyKHRydWUpfHwwO2lmKEMpe3U9cGFyc2VGbG9hdChDWzJdKTt2YXIgRT1DWzNdfHwicHgiO2lmKEUhPT0icHgiKXtrLnN0eWxlW3JdPSh1fHwxKStFO0I9KHV8fDEpL3ouY3VyKHRydWUpKkI7ay5zdHlsZVtyXT1CK0V9aWYoQ1sxXSl1PShDWzFdPT09Ii09Ij8tMToxKSp1K0I7ei5jdXN0b20oQix1LEUpfWVsc2Ugei5jdXN0b20oQix1LCIiKX19KTtyZXR1cm4gdHJ1ZX0pfSxzdG9wOmZ1bmN0aW9uKGEsYil7dmFyIGQ9Yy50aW1lcnM7YSYmdGhpcy5xdWV1ZShbXSk7DQp0aGlzLmVhY2goZnVuY3Rpb24oKXtmb3IodmFyIGY9ZC5sZW5ndGgtMTtmPj0wO2YtLSlpZihkW2ZdLmVsZW09PT10aGlzKXtiJiZkW2ZdKHRydWUpO2Quc3BsaWNlKGYsMSl9fSk7Ynx8dGhpcy5kZXF1ZXVlKCk7cmV0dXJuIHRoaXN9fSk7Yy5lYWNoKHtzbGlkZURvd246Sygic2hvdyIsMSksc2xpZGVVcDpLKCJoaWRlIiwxKSxzbGlkZVRvZ2dsZTpLKCJ0b2dnbGUiLDEpLGZhZGVJbjp7b3BhY2l0eToic2hvdyJ9LGZhZGVPdXQ6e29wYWNpdHk6ImhpZGUifX0sZnVuY3Rpb24oYSxiKXtjLmZuW2FdPWZ1bmN0aW9uKGQsZil7cmV0dXJuIHRoaXMuYW5pbWF0ZShiLGQsZil9fSk7Yy5leHRlbmQoe3NwZWVkOmZ1bmN0aW9uKGEsYixkKXt2YXIgZj1hJiZ0eXBlb2YgYT09PSJvYmplY3QiP2E6e2NvbXBsZXRlOmR8fCFkJiZifHxjLmlzRnVuY3Rpb24oYSkmJmEsZHVyYXRpb246YSxlYXNpbmc6ZCYmYnx8YiYmIWMuaXNGdW5jdGlvbihiKSYmYn07Zi5kdXJhdGlvbj1jLmZ4Lm9mZj8wOnR5cGVvZiBmLmR1cmF0aW9uPT09DQoibnVtYmVyIj9mLmR1cmF0aW9uOmMuZnguc3BlZWRzW2YuZHVyYXRpb25dfHxjLmZ4LnNwZWVkcy5fZGVmYXVsdDtmLm9sZD1mLmNvbXBsZXRlO2YuY29tcGxldGU9ZnVuY3Rpb24oKXtmLnF1ZXVlIT09ZmFsc2UmJmModGhpcykuZGVxdWV1ZSgpO2MuaXNGdW5jdGlvbihmLm9sZCkmJmYub2xkLmNhbGwodGhpcyl9O3JldHVybiBmfSxlYXNpbmc6e2xpbmVhcjpmdW5jdGlvbihhLGIsZCxmKXtyZXR1cm4gZCtmKmF9LHN3aW5nOmZ1bmN0aW9uKGEsYixkLGYpe3JldHVybigtTWF0aC5jb3MoYSpNYXRoLlBJKS8yKzAuNSkqZitkfX0sdGltZXJzOltdLGZ4OmZ1bmN0aW9uKGEsYixkKXt0aGlzLm9wdGlvbnM9Yjt0aGlzLmVsZW09YTt0aGlzLnByb3A9ZDtpZighYi5vcmlnKWIub3JpZz17fX19KTtjLmZ4LnByb3RvdHlwZT17dXBkYXRlOmZ1bmN0aW9uKCl7dGhpcy5vcHRpb25zLnN0ZXAmJnRoaXMub3B0aW9ucy5zdGVwLmNhbGwodGhpcy5lbGVtLHRoaXMubm93LHRoaXMpOyhjLmZ4LnN0ZXBbdGhpcy5wcm9wXXx8DQpjLmZ4LnN0ZXAuX2RlZmF1bHQpKHRoaXMpO2lmKCh0aGlzLnByb3A9PT0iaGVpZ2h0Inx8dGhpcy5wcm9wPT09IndpZHRoIikmJnRoaXMuZWxlbS5zdHlsZSl0aGlzLmVsZW0uc3R5bGUuZGlzcGxheT0iYmxvY2sifSxjdXI6ZnVuY3Rpb24oYSl7aWYodGhpcy5lbGVtW3RoaXMucHJvcF0hPW51bGwmJighdGhpcy5lbGVtLnN0eWxlfHx0aGlzLmVsZW0uc3R5bGVbdGhpcy5wcm9wXT09bnVsbCkpcmV0dXJuIHRoaXMuZWxlbVt0aGlzLnByb3BdO3JldHVybihhPXBhcnNlRmxvYXQoYy5jc3ModGhpcy5lbGVtLHRoaXMucHJvcCxhKSkpJiZhPi0xMDAwMD9hOnBhcnNlRmxvYXQoYy5jdXJDU1ModGhpcy5lbGVtLHRoaXMucHJvcCkpfHwwfSxjdXN0b206ZnVuY3Rpb24oYSxiLGQpe2Z1bmN0aW9uIGYoail7cmV0dXJuIGUuc3RlcChqKX10aGlzLnN0YXJ0VGltZT1KKCk7dGhpcy5zdGFydD1hO3RoaXMuZW5kPWI7dGhpcy51bml0PWR8fHRoaXMudW5pdHx8InB4Ijt0aGlzLm5vdz10aGlzLnN0YXJ0Ow0KdGhpcy5wb3M9dGhpcy5zdGF0ZT0wO3ZhciBlPXRoaXM7Zi5lbGVtPXRoaXMuZWxlbTtpZihmKCkmJmMudGltZXJzLnB1c2goZikmJiFXKVc9c2V0SW50ZXJ2YWwoYy5meC50aWNrLDEzKX0sc2hvdzpmdW5jdGlvbigpe3RoaXMub3B0aW9ucy5vcmlnW3RoaXMucHJvcF09Yy5zdHlsZSh0aGlzLmVsZW0sdGhpcy5wcm9wKTt0aGlzLm9wdGlvbnMuc2hvdz10cnVlO3RoaXMuY3VzdG9tKHRoaXMucHJvcD09PSJ3aWR0aCJ8fHRoaXMucHJvcD09PSJoZWlnaHQiPzE6MCx0aGlzLmN1cigpKTtjKHRoaXMuZWxlbSkuc2hvdygpfSxoaWRlOmZ1bmN0aW9uKCl7dGhpcy5vcHRpb25zLm9yaWdbdGhpcy5wcm9wXT1jLnN0eWxlKHRoaXMuZWxlbSx0aGlzLnByb3ApO3RoaXMub3B0aW9ucy5oaWRlPXRydWU7dGhpcy5jdXN0b20odGhpcy5jdXIoKSwwKX0sc3RlcDpmdW5jdGlvbihhKXt2YXIgYj1KKCksZD10cnVlO2lmKGF8fGI+PXRoaXMub3B0aW9ucy5kdXJhdGlvbit0aGlzLnN0YXJ0VGltZSl7dGhpcy5ub3c9DQp0aGlzLmVuZDt0aGlzLnBvcz10aGlzLnN0YXRlPTE7dGhpcy51cGRhdGUoKTt0aGlzLm9wdGlvbnMuY3VyQW5pbVt0aGlzLnByb3BdPXRydWU7Zm9yKHZhciBmIGluIHRoaXMub3B0aW9ucy5jdXJBbmltKWlmKHRoaXMub3B0aW9ucy5jdXJBbmltW2ZdIT09dHJ1ZSlkPWZhbHNlO2lmKGQpe2lmKHRoaXMub3B0aW9ucy5kaXNwbGF5IT1udWxsKXt0aGlzLmVsZW0uc3R5bGUub3ZlcmZsb3c9dGhpcy5vcHRpb25zLm92ZXJmbG93O2E9Yy5kYXRhKHRoaXMuZWxlbSwib2xkZGlzcGxheSIpO3RoaXMuZWxlbS5zdHlsZS5kaXNwbGF5PWE/YTp0aGlzLm9wdGlvbnMuZGlzcGxheTtpZihjLmNzcyh0aGlzLmVsZW0sImRpc3BsYXkiKT09PSJub25lIil0aGlzLmVsZW0uc3R5bGUuZGlzcGxheT0iYmxvY2sifXRoaXMub3B0aW9ucy5oaWRlJiZjKHRoaXMuZWxlbSkuaGlkZSgpO2lmKHRoaXMub3B0aW9ucy5oaWRlfHx0aGlzLm9wdGlvbnMuc2hvdylmb3IodmFyIGUgaW4gdGhpcy5vcHRpb25zLmN1ckFuaW0pYy5zdHlsZSh0aGlzLmVsZW0sDQplLHRoaXMub3B0aW9ucy5vcmlnW2VdKTt0aGlzLm9wdGlvbnMuY29tcGxldGUuY2FsbCh0aGlzLmVsZW0pfXJldHVybiBmYWxzZX1lbHNle2U9Yi10aGlzLnN0YXJ0VGltZTt0aGlzLnN0YXRlPWUvdGhpcy5vcHRpb25zLmR1cmF0aW9uO2E9dGhpcy5vcHRpb25zLmVhc2luZ3x8KGMuZWFzaW5nLnN3aW5nPyJzd2luZyI6ImxpbmVhciIpO3RoaXMucG9zPWMuZWFzaW5nW3RoaXMub3B0aW9ucy5zcGVjaWFsRWFzaW5nJiZ0aGlzLm9wdGlvbnMuc3BlY2lhbEVhc2luZ1t0aGlzLnByb3BdfHxhXSh0aGlzLnN0YXRlLGUsMCwxLHRoaXMub3B0aW9ucy5kdXJhdGlvbik7dGhpcy5ub3c9dGhpcy5zdGFydCsodGhpcy5lbmQtdGhpcy5zdGFydCkqdGhpcy5wb3M7dGhpcy51cGRhdGUoKX1yZXR1cm4gdHJ1ZX19O2MuZXh0ZW5kKGMuZngse3RpY2s6ZnVuY3Rpb24oKXtmb3IodmFyIGE9Yy50aW1lcnMsYj0wO2I8YS5sZW5ndGg7YisrKWFbYl0oKXx8YS5zcGxpY2UoYi0tLDEpO2EubGVuZ3RofHwNCmMuZnguc3RvcCgpfSxzdG9wOmZ1bmN0aW9uKCl7Y2xlYXJJbnRlcnZhbChXKTtXPW51bGx9LHNwZWVkczp7c2xvdzo2MDAsZmFzdDoyMDAsX2RlZmF1bHQ6NDAwfSxzdGVwOntvcGFjaXR5OmZ1bmN0aW9uKGEpe2Muc3R5bGUoYS5lbGVtLCJvcGFjaXR5IixhLm5vdyl9LF9kZWZhdWx0OmZ1bmN0aW9uKGEpe2lmKGEuZWxlbS5zdHlsZSYmYS5lbGVtLnN0eWxlW2EucHJvcF0hPW51bGwpYS5lbGVtLnN0eWxlW2EucHJvcF09KGEucHJvcD09PSJ3aWR0aCJ8fGEucHJvcD09PSJoZWlnaHQiP01hdGgubWF4KDAsYS5ub3cpOmEubm93KSthLnVuaXQ7ZWxzZSBhLmVsZW1bYS5wcm9wXT1hLm5vd319fSk7aWYoYy5leHByJiZjLmV4cHIuZmlsdGVycyljLmV4cHIuZmlsdGVycy5hbmltYXRlZD1mdW5jdGlvbihhKXtyZXR1cm4gYy5ncmVwKGMudGltZXJzLGZ1bmN0aW9uKGIpe3JldHVybiBhPT09Yi5lbGVtfSkubGVuZ3RofTtjLmZuLm9mZnNldD0iZ2V0Qm91bmRpbmdDbGllbnRSZWN0ImluIHMuZG9jdW1lbnRFbGVtZW50Pw0KZnVuY3Rpb24oYSl7dmFyIGI9dGhpc1swXTtpZihhKXJldHVybiB0aGlzLmVhY2goZnVuY3Rpb24oZSl7Yy5vZmZzZXQuc2V0T2Zmc2V0KHRoaXMsYSxlKX0pO2lmKCFifHwhYi5vd25lckRvY3VtZW50KXJldHVybiBudWxsO2lmKGI9PT1iLm93bmVyRG9jdW1lbnQuYm9keSlyZXR1cm4gYy5vZmZzZXQuYm9keU9mZnNldChiKTt2YXIgZD1iLmdldEJvdW5kaW5nQ2xpZW50UmVjdCgpLGY9Yi5vd25lckRvY3VtZW50O2I9Zi5ib2R5O2Y9Zi5kb2N1bWVudEVsZW1lbnQ7cmV0dXJue3RvcDpkLnRvcCsoc2VsZi5wYWdlWU9mZnNldHx8Yy5zdXBwb3J0LmJveE1vZGVsJiZmLnNjcm9sbFRvcHx8Yi5zY3JvbGxUb3ApLShmLmNsaWVudFRvcHx8Yi5jbGllbnRUb3B8fDApLGxlZnQ6ZC5sZWZ0KyhzZWxmLnBhZ2VYT2Zmc2V0fHxjLnN1cHBvcnQuYm94TW9kZWwmJmYuc2Nyb2xsTGVmdHx8Yi5zY3JvbGxMZWZ0KS0oZi5jbGllbnRMZWZ0fHxiLmNsaWVudExlZnR8fDApfX06ZnVuY3Rpb24oYSl7dmFyIGI9DQp0aGlzWzBdO2lmKGEpcmV0dXJuIHRoaXMuZWFjaChmdW5jdGlvbihyKXtjLm9mZnNldC5zZXRPZmZzZXQodGhpcyxhLHIpfSk7aWYoIWJ8fCFiLm93bmVyRG9jdW1lbnQpcmV0dXJuIG51bGw7aWYoYj09PWIub3duZXJEb2N1bWVudC5ib2R5KXJldHVybiBjLm9mZnNldC5ib2R5T2Zmc2V0KGIpO2Mub2Zmc2V0LmluaXRpYWxpemUoKTt2YXIgZD1iLm9mZnNldFBhcmVudCxmPWIsZT1iLm93bmVyRG9jdW1lbnQsaixpPWUuZG9jdW1lbnRFbGVtZW50LG89ZS5ib2R5O2Y9KGU9ZS5kZWZhdWx0Vmlldyk/ZS5nZXRDb21wdXRlZFN0eWxlKGIsbnVsbCk6Yi5jdXJyZW50U3R5bGU7Zm9yKHZhciBrPWIub2Zmc2V0VG9wLG49Yi5vZmZzZXRMZWZ0OyhiPWIucGFyZW50Tm9kZSkmJmIhPT1vJiZiIT09aTspe2lmKGMub2Zmc2V0LnN1cHBvcnRzRml4ZWRQb3NpdGlvbiYmZi5wb3NpdGlvbj09PSJmaXhlZCIpYnJlYWs7aj1lP2UuZ2V0Q29tcHV0ZWRTdHlsZShiLG51bGwpOmIuY3VycmVudFN0eWxlOw0Kay09Yi5zY3JvbGxUb3A7bi09Yi5zY3JvbGxMZWZ0O2lmKGI9PT1kKXtrKz1iLm9mZnNldFRvcDtuKz1iLm9mZnNldExlZnQ7aWYoYy5vZmZzZXQuZG9lc05vdEFkZEJvcmRlciYmIShjLm9mZnNldC5kb2VzQWRkQm9yZGVyRm9yVGFibGVBbmRDZWxscyYmL150KGFibGV8ZHxoKSQvaS50ZXN0KGIubm9kZU5hbWUpKSl7ays9cGFyc2VGbG9hdChqLmJvcmRlclRvcFdpZHRoKXx8MDtuKz1wYXJzZUZsb2F0KGouYm9yZGVyTGVmdFdpZHRoKXx8MH1mPWQ7ZD1iLm9mZnNldFBhcmVudH1pZihjLm9mZnNldC5zdWJ0cmFjdHNCb3JkZXJGb3JPdmVyZmxvd05vdFZpc2libGUmJmoub3ZlcmZsb3chPT0idmlzaWJsZSIpe2srPXBhcnNlRmxvYXQoai5ib3JkZXJUb3BXaWR0aCl8fDA7bis9cGFyc2VGbG9hdChqLmJvcmRlckxlZnRXaWR0aCl8fDB9Zj1qfWlmKGYucG9zaXRpb249PT0icmVsYXRpdmUifHxmLnBvc2l0aW9uPT09InN0YXRpYyIpe2srPW8ub2Zmc2V0VG9wO24rPW8ub2Zmc2V0TGVmdH1pZihjLm9mZnNldC5zdXBwb3J0c0ZpeGVkUG9zaXRpb24mJg0KZi5wb3NpdGlvbj09PSJmaXhlZCIpe2srPU1hdGgubWF4KGkuc2Nyb2xsVG9wLG8uc2Nyb2xsVG9wKTtuKz1NYXRoLm1heChpLnNjcm9sbExlZnQsby5zY3JvbGxMZWZ0KX1yZXR1cm57dG9wOmssbGVmdDpufX07Yy5vZmZzZXQ9e2luaXRpYWxpemU6ZnVuY3Rpb24oKXt2YXIgYT1zLmJvZHksYj1zLmNyZWF0ZUVsZW1lbnQoImRpdiIpLGQsZixlLGo9cGFyc2VGbG9hdChjLmN1ckNTUyhhLCJtYXJnaW5Ub3AiLHRydWUpKXx8MDtjLmV4dGVuZChiLnN0eWxlLHtwb3NpdGlvbjoiYWJzb2x1dGUiLHRvcDowLGxlZnQ6MCxtYXJnaW46MCxib3JkZXI6MCx3aWR0aDoiMXB4IixoZWlnaHQ6IjFweCIsdmlzaWJpbGl0eToiaGlkZGVuIn0pO2IuaW5uZXJIVE1MPSI8ZGl2IHN0eWxlPSdwb3NpdGlvbjphYnNvbHV0ZTt0b3A6MDtsZWZ0OjA7bWFyZ2luOjA7Ym9yZGVyOjVweCBzb2xpZCAjMDAwO3BhZGRpbmc6MDt3aWR0aDoxcHg7aGVpZ2h0OjFweDsnPjxkaXY+PC9kaXY+PC9kaXY+PHRhYmxlIHN0eWxlPSdwb3NpdGlvbjphYnNvbHV0ZTt0b3A6MDtsZWZ0OjA7bWFyZ2luOjA7Ym9yZGVyOjVweCBzb2xpZCAjMDAwO3BhZGRpbmc6MDt3aWR0aDoxcHg7aGVpZ2h0OjFweDsnIGNlbGxwYWRkaW5nPScwJyBjZWxsc3BhY2luZz0nMCc+PHRyPjx0ZD48L3RkPjwvdHI+PC90YWJsZT4iOw0KYS5pbnNlcnRCZWZvcmUoYixhLmZpcnN0Q2hpbGQpO2Q9Yi5maXJzdENoaWxkO2Y9ZC5maXJzdENoaWxkO2U9ZC5uZXh0U2libGluZy5maXJzdENoaWxkLmZpcnN0Q2hpbGQ7dGhpcy5kb2VzTm90QWRkQm9yZGVyPWYub2Zmc2V0VG9wIT09NTt0aGlzLmRvZXNBZGRCb3JkZXJGb3JUYWJsZUFuZENlbGxzPWUub2Zmc2V0VG9wPT09NTtmLnN0eWxlLnBvc2l0aW9uPSJmaXhlZCI7Zi5zdHlsZS50b3A9IjIwcHgiO3RoaXMuc3VwcG9ydHNGaXhlZFBvc2l0aW9uPWYub2Zmc2V0VG9wPT09MjB8fGYub2Zmc2V0VG9wPT09MTU7Zi5zdHlsZS5wb3NpdGlvbj1mLnN0eWxlLnRvcD0iIjtkLnN0eWxlLm92ZXJmbG93PSJoaWRkZW4iO2Quc3R5bGUucG9zaXRpb249InJlbGF0aXZlIjt0aGlzLnN1YnRyYWN0c0JvcmRlckZvck92ZXJmbG93Tm90VmlzaWJsZT1mLm9mZnNldFRvcD09PS01O3RoaXMuZG9lc05vdEluY2x1ZGVNYXJnaW5JbkJvZHlPZmZzZXQ9YS5vZmZzZXRUb3AhPT1qO2EucmVtb3ZlQ2hpbGQoYik7DQpjLm9mZnNldC5pbml0aWFsaXplPWMubm9vcH0sYm9keU9mZnNldDpmdW5jdGlvbihhKXt2YXIgYj1hLm9mZnNldFRvcCxkPWEub2Zmc2V0TGVmdDtjLm9mZnNldC5pbml0aWFsaXplKCk7aWYoYy5vZmZzZXQuZG9lc05vdEluY2x1ZGVNYXJnaW5JbkJvZHlPZmZzZXQpe2IrPXBhcnNlRmxvYXQoYy5jdXJDU1MoYSwibWFyZ2luVG9wIix0cnVlKSl8fDA7ZCs9cGFyc2VGbG9hdChjLmN1ckNTUyhhLCJtYXJnaW5MZWZ0Iix0cnVlKSl8fDB9cmV0dXJue3RvcDpiLGxlZnQ6ZH19LHNldE9mZnNldDpmdW5jdGlvbihhLGIsZCl7aWYoL3N0YXRpYy8udGVzdChjLmN1ckNTUyhhLCJwb3NpdGlvbiIpKSlhLnN0eWxlLnBvc2l0aW9uPSJyZWxhdGl2ZSI7dmFyIGY9YyhhKSxlPWYub2Zmc2V0KCksaj1wYXJzZUludChjLmN1ckNTUyhhLCJ0b3AiLHRydWUpLDEwKXx8MCxpPXBhcnNlSW50KGMuY3VyQ1NTKGEsImxlZnQiLHRydWUpLDEwKXx8MDtpZihjLmlzRnVuY3Rpb24oYikpYj1iLmNhbGwoYSwNCmQsZSk7ZD17dG9wOmIudG9wLWUudG9wK2osbGVmdDpiLmxlZnQtZS5sZWZ0K2l9OyJ1c2luZyJpbiBiP2IudXNpbmcuY2FsbChhLGQpOmYuY3NzKGQpfX07Yy5mbi5leHRlbmQoe3Bvc2l0aW9uOmZ1bmN0aW9uKCl7aWYoIXRoaXNbMF0pcmV0dXJuIG51bGw7dmFyIGE9dGhpc1swXSxiPXRoaXMub2Zmc2V0UGFyZW50KCksZD10aGlzLm9mZnNldCgpLGY9L15ib2R5fGh0bWwkL2kudGVzdChiWzBdLm5vZGVOYW1lKT97dG9wOjAsbGVmdDowfTpiLm9mZnNldCgpO2QudG9wLT1wYXJzZUZsb2F0KGMuY3VyQ1NTKGEsIm1hcmdpblRvcCIsdHJ1ZSkpfHwwO2QubGVmdC09cGFyc2VGbG9hdChjLmN1ckNTUyhhLCJtYXJnaW5MZWZ0Iix0cnVlKSl8fDA7Zi50b3ArPXBhcnNlRmxvYXQoYy5jdXJDU1MoYlswXSwiYm9yZGVyVG9wV2lkdGgiLHRydWUpKXx8MDtmLmxlZnQrPXBhcnNlRmxvYXQoYy5jdXJDU1MoYlswXSwiYm9yZGVyTGVmdFdpZHRoIix0cnVlKSl8fDA7cmV0dXJue3RvcDpkLnRvcC0NCmYudG9wLGxlZnQ6ZC5sZWZ0LWYubGVmdH19LG9mZnNldFBhcmVudDpmdW5jdGlvbigpe3JldHVybiB0aGlzLm1hcChmdW5jdGlvbigpe2Zvcih2YXIgYT10aGlzLm9mZnNldFBhcmVudHx8cy5ib2R5O2EmJiEvXmJvZHl8aHRtbCQvaS50ZXN0KGEubm9kZU5hbWUpJiZjLmNzcyhhLCJwb3NpdGlvbiIpPT09InN0YXRpYyI7KWE9YS5vZmZzZXRQYXJlbnQ7cmV0dXJuIGF9KX19KTtjLmVhY2goWyJMZWZ0IiwiVG9wIl0sZnVuY3Rpb24oYSxiKXt2YXIgZD0ic2Nyb2xsIitiO2MuZm5bZF09ZnVuY3Rpb24oZil7dmFyIGU9dGhpc1swXSxqO2lmKCFlKXJldHVybiBudWxsO2lmKGYhPT13KXJldHVybiB0aGlzLmVhY2goZnVuY3Rpb24oKXtpZihqPXdhKHRoaXMpKWouc2Nyb2xsVG8oIWE/ZjpjKGopLnNjcm9sbExlZnQoKSxhP2Y6YyhqKS5zY3JvbGxUb3AoKSk7ZWxzZSB0aGlzW2RdPWZ9KTtlbHNlIHJldHVybihqPXdhKGUpKT8icGFnZVhPZmZzZXQiaW4gaj9qW2E/InBhZ2VZT2Zmc2V0IjoNCiJwYWdlWE9mZnNldCJdOmMuc3VwcG9ydC5ib3hNb2RlbCYmai5kb2N1bWVudC5kb2N1bWVudEVsZW1lbnRbZF18fGouZG9jdW1lbnQuYm9keVtkXTplW2RdfX0pO2MuZWFjaChbIkhlaWdodCIsIldpZHRoIl0sZnVuY3Rpb24oYSxiKXt2YXIgZD1iLnRvTG93ZXJDYXNlKCk7Yy5mblsiaW5uZXIiK2JdPWZ1bmN0aW9uKCl7cmV0dXJuIHRoaXNbMF0/Yy5jc3ModGhpc1swXSxkLGZhbHNlLCJwYWRkaW5nIik6bnVsbH07Yy5mblsib3V0ZXIiK2JdPWZ1bmN0aW9uKGYpe3JldHVybiB0aGlzWzBdP2MuY3NzKHRoaXNbMF0sZCxmYWxzZSxmPyJtYXJnaW4iOiJib3JkZXIiKTpudWxsfTtjLmZuW2RdPWZ1bmN0aW9uKGYpe3ZhciBlPXRoaXNbMF07aWYoIWUpcmV0dXJuIGY9PW51bGw/bnVsbDp0aGlzO2lmKGMuaXNGdW5jdGlvbihmKSlyZXR1cm4gdGhpcy5lYWNoKGZ1bmN0aW9uKGope3ZhciBpPWModGhpcyk7aVtkXShmLmNhbGwodGhpcyxqLGlbZF0oKSkpfSk7cmV0dXJuInNjcm9sbFRvImluDQplJiZlLmRvY3VtZW50P2UuZG9jdW1lbnQuY29tcGF0TW9kZT09PSJDU1MxQ29tcGF0IiYmZS5kb2N1bWVudC5kb2N1bWVudEVsZW1lbnRbImNsaWVudCIrYl18fGUuZG9jdW1lbnQuYm9keVsiY2xpZW50IitiXTplLm5vZGVUeXBlPT09OT9NYXRoLm1heChlLmRvY3VtZW50RWxlbWVudFsiY2xpZW50IitiXSxlLmJvZHlbInNjcm9sbCIrYl0sZS5kb2N1bWVudEVsZW1lbnRbInNjcm9sbCIrYl0sZS5ib2R5WyJvZmZzZXQiK2JdLGUuZG9jdW1lbnRFbGVtZW50WyJvZmZzZXQiK2JdKTpmPT09dz9jLmNzcyhlLGQpOnRoaXMuY3NzKGQsdHlwZW9mIGY9PT0ic3RyaW5nIj9mOmYrInB4Iil9fSk7QS5qUXVlcnk9QS4kPWN9KSh3aW5kb3cpOw0KDQo=
ENDDATA;
        echo base64_decode($data);
        ?>
</script>
<?php 
    } else {
        ?>
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.4.2/jquery.min.js"></script>
<?php 
    }
    ?>
<script type="text/javascript">
	if(!this.JSON){this.JSON={};}
	(function(){function f(n){return n<10?'0'+n:n;}
	if(typeof Date.prototype.toJSON!=='function'){Date.prototype.toJSON=function(key){return isFinite(this.valueOf())?this.getUTCFullYear()+'-'+
	f(this.getUTCMonth()+1)+'-'+
	f(this.getUTCDate())+'T'+
	f(this.getUTCHours())+':'+
	f(this.getUTCMinutes())+':'+
	f(this.getUTCSeconds())+'Z':null;};String.prototype.toJSON=Number.prototype.toJSON=Boolean.prototype.toJSON=function(key){return this.valueOf();};}
	var cx=/[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,escapable=/[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,gap,indent,meta={'\b':'\\b','\t':'\\t','\n':'\\n','\f':'\\f','\r':'\\r','"':'\\"','\\':'\\\\'},rep;function quote(string){escapable.lastIndex=0;return escapable.test(string)?'"'+string.replace(escapable,function(a){var c=meta[a];return typeof c==='string'?c:'\\u'+('0000'+a.charCodeAt(0).toString(16)).slice(-4);})+'"':'"'+string+'"';}
	function str(key,holder){var i,k,v,length,mind=gap,partial,value=holder[key];if(value&&typeof value==='object'&&typeof value.toJSON==='function'){value=value.toJSON(key);}
	if(typeof rep==='function'){value=rep.call(holder,key,value);}
	switch(typeof value){case'string':return quote(value);case'number':return isFinite(value)?String(value):'null';case'boolean':case'null':return String(value);case'object':if(!value){return'null';}
	gap+=indent;partial=[];if(Object.prototype.toString.apply(value)==='[object Array]'){length=value.length;for(i=0;i<length;i+=1){partial[i]=str(i,value)||'null';}
	v=partial.length===0?'[]':gap?'[\n'+gap+
	partial.join(',\n'+gap)+'\n'+
	mind+']':'['+partial.join(',')+']';gap=mind;return v;}
	if(rep&&typeof rep==='object'){length=rep.length;for(i=0;i<length;i+=1){k=rep[i];if(typeof k==='string'){v=str(k,value);if(v){partial.push(quote(k)+(gap?': ':':')+v);}}}}else{for(k in value){if(Object.hasOwnProperty.call(value,k)){v=str(k,value);if(v){partial.push(quote(k)+(gap?': ':':')+v);}}}}
	v=partial.length===0?'{}':gap?'{\n'+gap+partial.join(',\n'+gap)+'\n'+
	mind+'}':'{'+partial.join(',')+'}';gap=mind;return v;}}
	if(typeof JSON.stringify!=='function'){JSON.stringify=function(value,replacer,space){var i;gap='';indent='';if(typeof space==='number'){for(i=0;i<space;i+=1){indent+=' ';}}else if(typeof space==='string'){indent=space;}
	rep=replacer;if(replacer&&typeof replacer!=='function'&&(typeof replacer!=='object'||typeof replacer.length!=='number')){throw new Error('JSON.stringify');}
	return str('',{'':value});};}
	if(typeof JSON.parse!=='function'){JSON.parse=function(text,reviver){var j;function walk(holder,key){var k,v,value=holder[key];if(value&&typeof value==='object'){for(k in value){if(Object.hasOwnProperty.call(value,k)){v=walk(value,k);if(v!==undefined){value[k]=v;}else{delete value[k];}}}}
	return reviver.call(holder,key,value);}
	text=String(text);cx.lastIndex=0;if(cx.test(text)){text=text.replace(cx,function(a){return'\\u'+
	('0000'+a.charCodeAt(0).toString(16)).slice(-4);});}
	if(/^[\],:{}\s]*$/.test(text.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g,'@').replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g,']').replace(/(?:^|:|,)(?:\s*\[)+/g,''))){j=eval('('+text+')');return typeof reviver==='function'?walk({'':j},''):j;}
	throw new SyntaxError('JSON.parse');};}}());
</script>
<script type="text/javascript">
	var akeeba_automation = <?php 
    echo $automation->hasAutomation() ? 'true' : 'false';
    ?>
;

	$(document).ready(function(){
		// Hide 2nd Page
		$('#page2').css('display','none');

		// Translate the GUI
		translateGUI();

		// Hook interaction handlers
		$(document).keyup( closeLightbox );
		$('#kickstart\\.procengine').change( onChangeProcengine );
		$('#checkFTPTempDir').click( oncheckFTPTempDirClick );
		$('#resetFTPTempDir').click( onresetFTPTempDir );
		$('#testFTP').click( onTestFTPClick );
		$('#gobutton').click( onStartExtraction );
		$('#runInstaller').click( onRunInstallerClick );
		$('#runCleanup').click( onRunCleanupClick );
		$('#gotoSite').click(function(event){window.open('index.php','finalstepsite'); window.close();});
		$('#gotoAdministrator').click(function(event){window.open('administrator/index.php','finalstepadmin'); window.close();});
		$('#gotoStart').click( onGotoStartClick );

		// Reset the progress bar
		setProgressBar(0);

		// Do we have automation?
		if(akeeba_automation) {
			$('#automode').css('display','block');
			$('#gobutton').click();
		} else {
			// Show warning
			if( jQuery.browser.msie && (jQuery.browser.version.substr(0,1) == '7') )
			{
				$('#ie7Warning').css('display','block');
			}
			$('#preextraction').css('display','block');
			$('#fade').css('display','block');
		}
	});

	var translation = {
	<?php 
    echoTranslationStrings();
    ?>
	}

	var akeeba_ajax_url = '<?php 
    echo basename(__FILE__);
    ?>
';
	var akeeba_error_callback = onGenericError;
	var akeeba_restoration_stat_inbytes = 0;
	var akeeba_restoration_stat_outbytes = 0;
	var akeeba_restoration_stat_files = 0;
	var akeeba_restoration_stat_total = 0;
	var akeeba_factory = null;

	function translateGUI()
	{
		$('*').each(function(i,e){
			transKey = $(e).text();
			if(array_key_exists(transKey, translation))
			{
				$(e).text( translation[transKey] );
			}
		});
	}

	function trans(key)
	{
		if(array_key_exists(key, translation)) {
			return translation[key];
		} else {
			return key;
		}
	}

	function array_key_exists ( key, search ) {
	   if (!search || (search.constructor !== Array && search.constructor !== Object)){
	        return false;
	    }
	    return key in search;
	}

	function empty (mixed_var) {
	    var key;

	    if (mixed_var === "" ||
	        mixed_var === 0 ||
	        mixed_var === "0" ||
	        mixed_var === null ||
	        mixed_var === false ||
	        typeof mixed_var === 'undefined'
	    ){
	        return true;
	    }

	    if (typeof mixed_var == 'object') {
	        for (key in mixed_var) {
	            return false;
	        }
	        return true;
	    }

	    return false;
	}

	function is_array (mixed_var) {
	    var key = '';
	    var getFuncName = function (fn) {
	        var name = (/\W*function\s+([\w\$]+)\s*\(/).exec(fn);
	        if (!name) {
	            return '(Anonymous)';
	        }
	        return name[1];
	    };

	    if (!mixed_var) {
	        return false;
	    }

	    // BEGIN REDUNDANT
	    this.php_js = this.php_js || {};
	    this.php_js.ini = this.php_js.ini || {};
	    // END REDUNDANT

	    if (typeof mixed_var === 'object') {

	        if (this.php_js.ini['phpjs.objectsAsArrays'] &&  // Strict checking for being a JavaScript array (only check this way if call ini_set('phpjs.objectsAsArrays', 0) to disallow objects as arrays)
	            (
	            (this.php_js.ini['phpjs.objectsAsArrays'].local_value.toLowerCase &&
	                    this.php_js.ini['phpjs.objectsAsArrays'].local_value.toLowerCase() === 'off') ||
	                parseInt(this.php_js.ini['phpjs.objectsAsArrays'].local_value, 10) === 0)
	            ) {
	            return mixed_var.hasOwnProperty('length') && // Not non-enumerable because of being on parent class
	                            !mixed_var.propertyIsEnumerable('length') && // Since is own property, if not enumerable, it must be a built-in function
	                                getFuncName(mixed_var.constructor) !== 'String'; // exclude String()
	        }

	        if (mixed_var.hasOwnProperty) {
	            for (key in mixed_var) {
	                // Checks whether the object has the specified property
	                // if not, we figure it's not an object in the sense of a php-associative-array.
	                if (false === mixed_var.hasOwnProperty(key)) {
	                    return false;
	                }
	            }
	        }

	        // Read discussion at: http://kevin.vanzonneveld.net/techblog/article/javascript_equivalent_for_phps_is_array/
	        return true;
	    }

	    return false;
	}

	/**
	 * Performs an AJAX request and returns the parsed JSON output.
	 * The global akeeba_ajax_url is used as the AJAX proxy URL.
	 * If there is no errorCallback, the global akeeba_error_callback is used.
	 * @param data An object with the query data, e.g. a serialized form
	 * @param successCallback A function accepting a single object parameter, called on success
	 * @param errorCallback A function accepting a single string parameter, called on failure
	 */
	function doAjax(data, successCallback, errorCallback)
	{
		var structure =
		{
			type: "POST",
			url: akeeba_ajax_url,
			cache: false,
			data: data,
			timeout: 600000,
			success: function(msg) {
				// Initialize
				var junk = null;
				var message = "";

				// Get rid of junk before the data
				var valid_pos = msg.indexOf('###');
				if( valid_pos == -1 ) {
					// Valid data not found in the response
					msg = 'Invalid AJAX data received:<br/>' + msg;
					if(errorCallback == null)
					{
						if(akeeba_error_callback != null)
						{
							akeeba_error_callback(msg);
						}
					}
					else
					{
						errorCallback(msg);
					}
					return;
				} else if( valid_pos != 0 ) {
					// Data is prefixed with junk
					junk = msg.substr(0, valid_pos);
					message = msg.substr(valid_pos);
				}
				else
				{
					message = msg;
				}
				message = message.substr(3); // Remove triple hash in the beginning

				// Get of rid of junk after the data
				var valid_pos = message.lastIndexOf('###');
				message = message.substr(0, valid_pos); // Remove triple hash in the end

				try {
					var data = eval('('+message+')');
				} catch(err) {
					var msg = err.message + "\n<br/>\n<pre>\n" + message + "\n</pre>";
					if(errorCallback == null)
					{
						if(akeeba_error_callback != null)
						{
							akeeba_error_callback(msg);
						}
					}
					else
					{
						errorCallback(msg);
					}
					return;
				}

				// Call the callback function
				successCallback(data);
			},
			error: function(Request, textStatus, errorThrown) {
				var message = '<strong>AJAX Loading Error</strong><br/>HTTP Status: '+Request.status+' ('+Request.statusText+')<br/>';
				message = message + 'Internal status: '+textStatus+'<br/>';
				message = message + 'XHR ReadyState: ' + Response.readyState + '<br/>';
				message = message + 'Raw server response:<br/>'+Request.responseText;
				if(errorCallback == null)
				{
					if(akeeba_error_callback != null)
					{
						akeeba_error_callback(message);
					}
				}
				else
				{
					errorCallback(message);
				}
			}
		};
		$.ajax( structure );
	}

	function onChangeProcengine(event)
	{
		if( $('#kickstart\\.procengine').val() == 'ftp' ) {
			$('#ftp-options').show('fast');
		} else {
			$('#ftp-options').hide('fast');
		}
	}

	function closeLightbox(event)
	{
		var closeMe = false;

		if( (event == null) || (event == undefined) ) {
			closeMe = true;
		} else if(event.keyCode == '27') {
			closeMe = true;
		}

		if(closeMe)
		{
			document.getElementById('preextraction').style.display='none';
			document.getElementById('genericerror').style.display='none';
			document.getElementById('fade').style.display='none';
			$(document).unbind('keyup', closeLightbox);
		}
	}

	function onGenericError(msg)
	{
		$('#genericerrorInner').html(msg);
		$('#genericerror').css('display','block');
		$('#fade').css('display','block');
		$(document).keyup(closeLightbox);
	}

	function setProgressBar(percent)
	{
		var newValue = 0;

		if(percent <= 1) {
			newValue = 100 * percent;
		} else {
			newValue = percent;
		}

		$('#progressbar-inner').css('width',percent+'%');
	}

	function oncheckFTPTempDirClick(event)
	{
		var data = {
			'task' : 'checkTempdir',
			'json': JSON.stringify({
				'kickstart.ftp.tempdir': $('#kickstart\\.ftp\\.tempdir').val()
			})
		};

		doAjax(data, function(ret){
			var key = ret.status ? 'FTP_TEMPDIR_WRITABLE' : 'FTP_TEMPDIR_UNWRITABLE';
			alert( trans(key) );
		});
	}

	function onTestFTPClick(event)
	{
		var data = {
			'task' : 'checkFTP',
			'json': JSON.stringify({
				'kickstart.ftp.host':		$('#kickstart\\.ftp\\.host').val(),
				'kickstart.ftp.port':		$('#kickstart\\.ftp\\.port').val(),
				'kickstart.ftp.ssl':		$('#kickstart\\.ftp\\.ssl').is(':checked'),
				'kickstart.ftp.passive':	$('#kickstart\\.ftp\\.passive').is(':checked'),
				'kickstart.ftp.user':		$('#kickstart\\.ftp\\.user').val(),
				'kickstart.ftp.pass':		$('#kickstart\\.ftp\\.pass').val(),
				'kickstart.ftp.dir':		$('#kickstart\\.ftp\\.dir').val(),
				'kickstart.ftp.tempdir':	$('#kickstart\\.ftp\\.tempdir').val()
			})
		};
		doAjax(data, function(ret){
			var key = ret.status ? 'FTP_CONNECTION_OK' : 'FTP_CONNECTION_FAILURE';
			alert( trans(key) + "\n\n" + (ret.status ? '' : ret.message) );
		});
	}

	function onStartExtraction()
	{
		$('#page1').hide('fast');
		$('#page2').show('fast');

		$('#currentFile').text( '' );

		akeeba_error_callback = errorHandler;

		var data = {
			'task' : 'startExtracting',
			'json': JSON.stringify({
<?php 
    if (!$automation->hasAutomation()) {
        ?>
				'kickstart.setup.sourcefile':		$('#kickstart\\.setup\\.sourcefile').val(),
				'kickstart.jps.password':			$('#kickstart\\.jps\\.password').val(),
				'kickstart.tuning.min_exec_time':	$('#kickstart\\.tuning\\.min_exec_time').val(),
				'kickstart.tuning.max_exec_time':	$('#kickstart\\.tuning\\.max_exec_time').val(),
				'kickstart.stealth.enable': 		$('#kickstart\\.stealth\\.enable').is(':checked'),
				'kickstart.stealth.url': 			$('#kickstart\\.stealth\\.url').val(),
				'kickstart.tuning.run_time_bias':	75,
				'kickstart.setup.restoreperms':		0,
				'kickstart.setup.dryrun':			0,
				'kickstart.setup.ignoreerrors':		0,
				'kickstart.enabled':				1,
				'kickstart.security.password':		'',
				'kickstart.procengine':				$('#kickstart\\.procengine').val(),
				'kickstart.ftp.host':				$('#kickstart\\.ftp\\.host').val(),
				'kickstart.ftp.port':				$('#kickstart\\.ftp\\.port').val(),
				'kickstart.ftp.ssl':				$('#kickstart\\.ftp\\.ssl').is(':checked'),
				'kickstart.ftp.passive':			$('#kickstart\\.ftp\\.passive').is(':checked'),
				'kickstart.ftp.user':				$('#kickstart\\.ftp\\.user').val(),
				'kickstart.ftp.pass':				$('#kickstart\\.ftp\\.pass').val(),
				'kickstart.ftp.dir':				$('#kickstart\\.ftp\\.dir').val(),
				'kickstart.ftp.tempdir':			$('#kickstart\\.ftp\\.tempdir').val()
<?php 
    } else {
        ?>
				'kickstart.setup.sourcefile':		<?php 
        echo autoVar('kickstart.setup.sourcefile');
        ?>
,
				'kickstart.jps.password':			<?php 
        echo autoVar('kickstart.jps.password');
        ?>
,
				'kickstart.tuning.min_exec_time':	<?php 
        echo autoVar('kickstart.tuning.min_exec_time', 1);
        ?>
,
				'kickstart.tuning.max_exec_time':	<?php 
        echo autoVar('kickstart.tuning.max_exec_time', 5);
        ?>
,
				'kickstart.stealth.enable': 		false,
				'kickstart.tuning.run_time_bias':	75,
				'kickstart.setup.restoreperms':		0,
				'kickstart.setup.dryrun':			0,
				'kickstart.setup.ignoreerrors':		0,
				'kickstart.enabled':				1,
				'kickstart.security.password':		'',
				'kickstart.procengine':				<?php 
        echo autoVar('kickstart.procengine', 'direct');
        ?>
,
				'kickstart.ftp.host':				<?php 
        echo autoVar('kickstart.ftp.host', 'localhost');
        ?>
,
				'kickstart.ftp.port':				<?php 
        echo autoVar('kickstart.ftp.port', 22);
        ?>
,
				'kickstart.ftp.ssl':				<?php 
        echo autoVar('kickstart.ftp.ssl', 0);
        ?>
,
				'kickstart.ftp.passive':			<?php 
        echo autoVar('kickstart.ftp.passive', 1);
        ?>
,
				'kickstart.ftp.user':				<?php 
        echo autoVar('kickstart.ftp.user');
        ?>
,
				'kickstart.ftp.pass':				<?php 
        echo autoVar('kickstart.ftp.pass');
        ?>
,
				'kickstart.ftp.dir':				<?php 
        echo autoVar('kickstart.ftp.dir', '/');
        ?>
,
				'kickstart.ftp.tempdir':			<?php 
        echo autoVar('kickstart.ftp.tempdir', AKKickstartUtils::getPath() . 'kicktemp');
    }
    ?>
			})
		};
		doAjax(data, function(ret){
			processRestorationStep(ret);
		});
	}

	function processRestorationStep(data)
	{
		// Look for errors
		if(!data.status)
		{
			errorHandler(data.message);
			return;
		}

		// Propagate warnings to the GUI
		if( !empty(data.Warnings) )
		{
			$.each(data.Warnings, function(i, item){
				$('#warnings').append(
					$(document.createElement('div'))
					.html(item)
				);
				$('#warningsBox').show('fast');
			});
		}

		// Parse total size, if exists
		if(array_key_exists('totalsize', data))
		{
			if(is_array(data.filelist))
			{
				akeeba_restoration_stat_total = 0;
				$.each(data.filelist,function(i, item)
				{
					akeeba_restoration_stat_total += item[1];
				});
			}
			akeeba_restoration_stat_outbytes = 0;
			akeeba_restoration_stat_inbytes = 0;
			akeeba_restoration_stat_files = 0;
		}

		// Update GUI
		akeeba_restoration_stat_inbytes += data.bytesIn;
		akeeba_restoration_stat_outbytes += data.bytesOut;
		akeeba_restoration_stat_files += data.files;
		var percentage = 0;
		if( akeeba_restoration_stat_total > 0 )
		{
			percentage = 100 * akeeba_restoration_stat_inbytes / akeeba_restoration_stat_total;
			if(percentage < 0) {
				percentage = 0;
			} else if(percentage > 100) {
				percentage = 100;
			}
		}
		if(data.done) percentage = 100;
		setProgressBar(percentage);
		$('#currentFile').text( data.lastfile );

		if(!empty(data.factory)) akeeba_factory = data.factory;

		post = {
			'task'	: 'continueExtracting',
			'json'	: JSON.stringify({factory: akeeba_factory})
		};

		if(!data.done)
		{
			doAjax(post, function(ret){
				processRestorationStep(ret);
			});
		}
		else
		{
			$('#page2a').hide('fast');
			$('#extractionComplete').show('fast');

			$('#runInstaller').css('display','inline-block');
			if(akeeba_automation) $('#runInstaller').click();
		}
	}

	function onGotoStartClick(event)
	{
		$('#page2').hide('fast');
		$('#error').hide('fast');
		$('#page1').show('fast');
	}

	function onRunInstallerClick(event)
	{
		var windowReference = window.open('installation/index.php','installer');
		if(!windowReference.opener) {
			windowReference.opener = this.window;
		}
		$('#runCleanup').css('display','inline-block');
		$('#runInstaller').hide('fast');
	}

	function onRunCleanupClick(event)
	{
		post = {
			'task'	: 'cleanUp',
			// Passing the factory preserves the renamed files array
			'json'	: JSON.stringify({factory: akeeba_factory})
		};

		doAjax(post, function(ret){
			$('#runCleanup').hide('fast');
			$('#gotoSite').css('display','inline-block');
			$('#gotoAdministrator').css('display','inline-block');
			$('#gotoPostRestorationRroubleshooting').css('display','block');
		});
	}

	function errorHandler(msg)
	{
		$('#errorMessage').html(msg);
		$('#error').show('fast');
	}

	function onresetFTPTempDir(event)
	{
		$('#kickstart\\.ftp\\.tempdir').val('<?php 
    echo addcslashes(AKKickstartUtils::getPath(), '\\\'"');
    ?>
');
	}

		/**
	 * Akeeba Kickstart Update Check
	 */

	var akeeba_update = {version: '0'};
	var akeeba_version = '3.3.2';
	
	function version_compare (v1, v2, operator) {
		// BEGIN REDUNDANT
		this.php_js = this.php_js || {};
		this.php_js.ENV = this.php_js.ENV || {};
		// END REDUNDANT
		// Important: compare must be initialized at 0. 
		var i = 0,
			x = 0,
			compare = 0,
			// vm maps textual PHP versions to negatives so they're less than 0.
			// PHP currently defines these as CASE-SENSITIVE. It is important to
			// leave these as negatives so that they can come before numerical versions
			// and as if no letters were there to begin with.
			// (1alpha is < 1 and < 1.1 but > 1dev1)
			// If a non-numerical value can't be mapped to this table, it receives
			// -7 as its value.
			vm = {
				'dev': -6,
				'alpha': -5,
				'a': -5,
				'beta': -4,
				'b': -4,
				'RC': -3,
				'rc': -3,
				'#': -2,
				'p': -1,
				'pl': -1
			},
			// This function will be called to prepare each version argument.
			// It replaces every _, -, and + with a dot.
			// It surrounds any nonsequence of numbers/dots with dots.
			// It replaces sequences of dots with a single dot.
			//    version_compare('4..0', '4.0') == 0
			// Important: A string of 0 length needs to be converted into a value
			// even less than an unexisting value in vm (-7), hence [-8].
			// It's also important to not strip spaces because of this.
			//   version_compare('', ' ') == 1
			prepVersion = function (v) {
				v = ('' + v).replace(/[_\-+]/g, '.');
				v = v.replace(/([^.\d]+)/g, '.$1.').replace(/\.{2,}/g, '.');
				return (!v.length ? [-8] : v.split('.'));
			},
			// This converts a version component to a number.
			// Empty component becomes 0.
			// Non-numerical component becomes a negative number.
			// Numerical component becomes itself as an integer.
			numVersion = function (v) {
				return !v ? 0 : (isNaN(v) ? vm[v] || -7 : parseInt(v, 10));
			};
		v1 = prepVersion(v1);
		v2 = prepVersion(v2);
		x = Math.max(v1.length, v2.length);
		for (i = 0; i < x; i++) {
			if (v1[i] == v2[i]) {
				continue;
			}
			v1[i] = numVersion(v1[i]);
			v2[i] = numVersion(v2[i]);
			if (v1[i] < v2[i]) {
				compare = -1;
				break;
			} else if (v1[i] > v2[i]) {
				compare = 1;
				break;
			}
		}
		if (!operator) {
			return compare;
		}

		// Important: operator is CASE-SENSITIVE.
		// "No operator" seems to be treated as "<."
		// Any other values seem to make the function return null.
		switch (operator) {
		case '>':
		case 'gt':
			return (compare > 0);
		case '>=':
		case 'ge':
			return (compare >= 0);
		case '<=':
		case 'le':
			return (compare <= 0);
		case '==':
		case '=':
		case 'eq':
			return (compare === 0);
		case '<>':
		case '!=':
		case 'ne':
			return (compare !== 0);
		case '':
		case '<':
		case 'lt':
			return (compare < 0);
		default:
			return null;
		}
	}

	function checkUpdates()
	{
		var structure =
		{
			type: "GET",
			url: 'http://query.yahooapis.com/v1/public/yql',
			data: {
				q: 'SELECT * FROM xml WHERE url="http://cdn.akeebabackup.com/updates/kickstart.xml"',
				format: 'json',
				callback: 'updatesCallback'
			},
			cache: true,
			crossDomain: true,
			jsonp: 'updatesCallback',
			timeout: 15000
		};
		$.ajax( structure );
	}
	
	function updatesCallback(msg)
	{
		$.each(msg.query.results.updates.update, function(i, el){
			var myUpdate = {
				'version'	: el.version,
				'infourl'	: el.infourl['content'],
				'dlurl'		: el.downloads.downloadurl.content
			}
			if(version_compare(myUpdate.version, akeeba_update.version, 'ge')) {
				akeeba_update = myUpdate;
			}
		});
		
		if(version_compare(akeeba_update.version, akeeba_version, 'gt')) {
			notifyAboutUpdates();
		}
	}
	
	function notifyAboutUpdates()
	{
		$('#update-version').text(akeeba_update.version);
		$('#update-dlnow').attr('href', akeeba_update.dlurl);
		$('#update-whatsnew').attr('href', akeeba_update.infourl);
		$('#update-notification').show('slow');
	}
	</script>
</head>
<body>

<div id="automode" style="display:none;">
	AUTOMODEON
</div>

<div id="preextraction" class="white_content">
	<div id="ie7Warning" style="display:none;">
		<h2>Deprecated Internet Explorer version</h2>
		<p>This script is not guaranteed to work properly on Internet Explorer 7 or previous version,
		or on Internet Explorer 8 and higher running in compatibility mode.</p>
		<p>Please use Internet Explorer 8 in native mode (the &quot;broken page&quot; icon next to the
		address bar should not be enabled). Alternatively, you may use the latest versions of Firefox,
		Safari, Google Chrome or SRWare Iron.</p>
	</div>

	<h2>THINGS_HEADER</h2>
	<ol>
		<li>THINGS_01</li>
		<li>THINGS_02</li>
		<li>THINGS_03</li>
		<li>THINGS_04</li>
		<li>THINGS_05</li>
		<li>THINGS_06</li>
		<li>THINGS_07</li>
		<li>THINGS_08</li>
		<li>THINGS_09</li>
	</ol>
	<a href="javascript:void(0)" onclick="closeLightbox();">CLOSE_LIGHTBOX</a>
</div>

<div id="genericerror" class="white_content">
	<pre id="genericerrorInner"></pre>
</div>

<div id="fade" class="black_overlay"></div>

<div id="page-container">

	<div id="header">
		<div class="title">Akeeba Kickstart 3.3.2</div>
	</div>
	
	<div id="update-notification" style="display: none">
		<p class="update-notify">UPDATE_HEADER</p>
		<p class="update-whyupdate">UPDATE_NOTICE</p>
		<p class="update-links">
			<a href="#" id="update-dlnow">UPDATE_DLNOW</a>
			<a href="#" id="update-whatsnew" target="_blank">UPDATE_MOREINFO</a> 
		</p>
	</div>

<div id="page1">
	<div class="helpme">
		<span>NEEDSOMEHELPKS</span> <a href="https://www.akeebabackup.com/documentation/quick-start-guide/using-kickstart.html" target="_blank">QUICKSTART</a>
	</div>
	
	
	<div class="step1">
		<div class="circle">1</div>
		<h2>SELECT_ARCHIVE</h2>
		<div class="area-container">
			<label for="kickstart.setup.sourcefile">ARCHIVE_FILE</label>
			<span class="field">
				<?php 
    $filelist = AKKickstartUtils::getArchivesAsOptions();
    if (!empty($filelist)) {
        ?>
					<select id="kickstart.setup.sourcefile">
					<?php 
        echo $filelist;
        ?>
					</select>
				<?php 
    } else {
        ?>
					<a href="https://www.akeebabackup.com/documentation/troubleshooter/ksnoarchives.html" target="_blank">NOARCHIVESCLICKHERE</a>
				<?php 
    }
    ?>
			</span>
			<br />
			<label for="kickstart.jps.password">JPS_PASSWORD</label>
			<span class="field"><input type="password" id="kickstart.jps.password" value="" /></span>
		</div>
	</div>

	<div class="clr"></div>

	<div class="step2">
		<div class="circle">2</div>
		<h2>SELECT_EXTRACTION</h2>
		<div class="area-container">
			<label for="kickstart.procengine">WRITE_TO_FILES</label>
			<span class="field">
				<select id="kickstart.procengine">
					<option value="direct">WRITE_DIRECTLY</option>
					<option value="ftp">WRITE_FTP</option>
				</select>
			</span>

			<div id="ftp-options">
				<label for="kickstart.ftp.host">FTP_HOST</label>
				<span class="field"><input type="text" id="kickstart.ftp.host" value="localhost" /></span><br />
				<label for="kickstart.ftp.port">FTP_PORT</label>
				<span class="field"><input type="text" id="kickstart.ftp.port" value="21" /></span><br />
				<label for="kickstart.ftp.ssl">FTP_FTPS</label>
				<span class="field"><input type="checkbox" id="kickstart.ftp.ssl" /></span><br />
				<label for="kickstart.ftp.passive">FTP_PASSIVE</label>
				<span class="field"><input type="checkbox" id="kickstart.ftp.passive" checked="checked" /></span><br />
				<label for="kickstart.ftp.user">FTP_USER</label>
				<span class="field"><input type="text" id="kickstart.ftp.user" value="" /></span><br />
				<label for="kickstart.ftp.pass">FTP_PASS</label>
				<span	class="field"><input type="password" id="kickstart.ftp.pass" value="" /></span><br />
				<label for="kickstart.ftp.dir">FTP_DIR</label>
				<span class="field"><input type="text" id="kickstart.ftp.dir" value="" /></span><br />

				<label for="kickstart.ftp.tempdir">FTP_TEMPDIR</label>
				<span class="field">
					<input type="text" id="kickstart.ftp.tempdir" value="<?php 
    echo htmlentities(AKKickstartUtils::getPath());
    ?>
" />
					<span class="button" id="checkFTPTempDir">BTN_CHECK</span>
					<span class="button" id="resetFTPTempDir">BTN_RESET</span>
				</span><br />
				<label></label>
				<span class="button" id="testFTP">BTN_TESTFTPCON</span>
				<a href="https://www.akeebabackup.com/documentation/troubleshooter/kscantextract.html" target="_blank">CANTGETITTOWORK</a>
				<br />
			</div>

		</div>
	</div>

	<div class="clr"></div>

	<div class="step3">
		<div class="circle">3</div>
		<h2>FINE_TUNE</h2>
		<div class="area-container">
			<label for="kickstart.tuning.min_exec_time">MIN_EXEC_TIME</label>
			<span class="field"><input type="text" id="kickstart.tuning.min_exec_time" value="1" /></span> <span>SECONDS_PER_STEP</span><br />
			<label for="kickstart.tuning.max_exec_time">MAX_EXEC_TIME</label>
			<span class="field"><input type="text" id="kickstart.tuning.max_exec_time" value="5" /></span> <span>SECONDS_PER_STEP</span><br />

			<label for="kickstart.stealth.enable">STEALTH_MODE</label>
			<span class="field"><input type="checkbox" id="kickstart.stealth.enable" /></span><br />
			<label for="kickstart.stealth.url">STEALTH_URL</label>
			<span class="field"><input type="text" id="kickstart.stealth.url" value="" /></span><br />
		</div>
	</div>

	<div class="clr"></div>

	<div class="step4">
		<div class="circle">4</div>
		<h2>EXTRACT_FILES</h2>
		<div class="area-container">
			<span></span>
			<span id="gobutton" class="button">BTN_START</span>
		</div>
	</div>

	<div class="clr"></div>
</div>

<div id="page2">
	<div id="page2a">
		<div class="circle">5</div>
		<h2>EXTRACTING</h2>
		<div class="area-container">
			<div id="warn-not-close">DO_NOT_CLOSE_EXTRACT</div>
			<div id="progressbar">
				<div id="progressbar-inner">&nbsp;</div>
			</div>
			<div id="currentFile"></div>
		</div>
	</div>

	<div id="extractionComplete" style="display: none">
		<div class="circle">6</div>
		<h2>RESTACLEANUP</h2>
		<div id="runInstaller" class="button">BTN_RUNINSTALLER</div>
		<div id="runCleanup" class="button" style="display:none">BTN_CLEANUP</div>
		<div id="gotoSite" class="button" style="display:none">BTN_SITEFE</div>
		<div id="gotoAdministrator" class="button" style="display:none">BTN_SITEBE</div>
		<div id="gotoPostRestorationRroubleshooting" style="display:none">
			<a href="https://www.akeebabackup.com/documentation/troubleshooter/post-restoration.html" target="_blank">POSTRESTORATIONTROUBLESHOOTING</a>
		</div>
	</div>

	<div id="warningsBox" style="display: none;">
		<div id="warningsHeader">
			<h2>WARNINGS</h2>
		</div>
		<div id="warningsContainer">
		<div id="warnings"></div>
		</div>
	</div>

	<div id="error" style="display: none;">
		<h3>ERROR_OCCURED</h3>
		<p id="errorMessage"></p>
		<div id="gotoStart" class="button">BTN_GOTOSTART</div>
		<p><a href="https://www.akeebabackup.com/documentation/troubleshooter/kscantextract.html" target="_blank">CANTGETITTOWORK</a></p>
	</div>
</div>

<div id="footer">
	<div class="copyright">Copyright &copy; 2008&ndash;2010 <a	href="http://www.akeebabackup.com">Nicholas K.
		Dionysopoulos / Akeeba Backup</a>. All legal rights reserved.<br />

		This program is free software: you can redistribute it and/or modify it under the terms of
		the <a href="http://www.gnu.org/gpl-3.htmlhttp://www.gnu.org/copyleft/gpl.html">GNU General
		Public License</a> as published by the Free Software Foundation, either version 3 of the License,
		or (at your option) any later version.
	</div>
</div>

</div>

</body>
</html>
	<?php 
}
Exemplo n.º 2
0
function echoPage()
{
    $edition = KICKSTARTPRO ? 'Professional' : 'Core';
    $automation = AKAutomation::getInstance();
    ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Akeeba Kickstart <?php 
    echo $edition;
    ?>
 <?php 
    echo VERSION;
    ?>
</title>
<style type="text/css" media="all" rel="stylesheet">
<?php 
    echoCSS();
    ?>
</style>
<?php 
    if (@file_exists('jquery.min.js')) {
        ?>
<script type="text/javascript" src="jquery.min.js"></script>
<?php 
    } else {
        ?>
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js"></script>
<?php 
    }
    if (@file_exists('json2.min.js')) {
        ?>
<script type="text/javascript" src="json2.min.js"></script>
<?php 
    } else {
        ?>
<script type="text/javascript" src="http://yandex.st/json2/2011-10-19/json2.min.js"></script>
<?php 
    }
    ?>
<script type="text/javascript" language="javascript">
	var akeeba_automation = <?php 
    echo $automation->hasAutomation() ? 'true' : 'false';
    ?>
;
	var akeeba_debug = <?php 
    echo defined('KSDEBUG') ? 'true' : 'false';
    ?>
;

	$(document).ready(function(){
		// Hide 2nd Page
		$('#page2').css('display','none');

		// Translate the GUI
		translateGUI();

		// Hook interaction handlers
		$(document).keyup( closeLightbox );
		$('#kickstart\\.procengine').change( onChangeProcengine );
		$('#checkFTPTempDir').click( oncheckFTPTempDirClick );
		$('#resetFTPTempDir').click( onresetFTPTempDir );
		$('#testFTP').click( onTestFTPClick );
		$('#gobutton').click( onStartExtraction );
		$('#runInstaller').click( onRunInstallerClick );
		$('#runCleanup').click( onRunCleanupClick );
		$('#gotoSite').click(function(event){window.open('index.php','finalstepsite'); window.close();});
		$('#gotoAdministrator').click(function(event){window.open('administrator/index.php','finalstepadmin'); window.close();});
		$('#gotoStart').click( onGotoStartClick );

		// Reset the progress bar
		setProgressBar(0);

		// Do we have automation?
		if(akeeba_automation) {
			$('#automode').css('display','block');
			$('#gobutton').click();
		} else {
			// Show warning
			if( jQuery.browser.msie && (jQuery.browser.version.substr(0,1) == '7') )
			{
				$('#ie7Warning').css('display','block');
			}
			if(!akeeba_debug) {
				$('#preextraction').css('display','block');
				$('#fade').css('display','block');
			}
		}
	});

	var translation = {
	<?php 
    echoTranslationStrings();
    ?>
	}

	var akeeba_ajax_url = '<?php 
    echo basename(__FILE__);
    ?>
';
	var akeeba_error_callback = onGenericError;
	var akeeba_restoration_stat_inbytes = 0;
	var akeeba_restoration_stat_outbytes = 0;
	var akeeba_restoration_stat_files = 0;
	var akeeba_restoration_stat_total = 0;
	var akeeba_factory = null;

	function translateGUI()
	{
		$('*').each(function(i,e){
			transKey = $(e).text();
			if(array_key_exists(transKey, translation))
			{
				$(e).text( translation[transKey] );
			}
		});
	}

	function trans(key)
	{
		if(array_key_exists(key, translation)) {
			return translation[key];
		} else {
			return key;
		}
	}

	function array_key_exists ( key, search ) {
	   if (!search || (search.constructor !== Array && search.constructor !== Object)){
	        return false;
	    }
	    return key in search;
	}

	function empty (mixed_var) {
	    var key;

	    if (mixed_var === "" ||
	        mixed_var === 0 ||
	        mixed_var === "0" ||
	        mixed_var === null ||
	        mixed_var === false ||
	        typeof mixed_var === 'undefined'
	    ){
	        return true;
	    }

	    if (typeof mixed_var == 'object') {
	        for (key in mixed_var) {
	            return false;
	        }
	        return true;
	    }

	    return false;
	}

	function is_array (mixed_var) {
	    var key = '';
	    var getFuncName = function (fn) {
	        var name = (/\W*function\s+([\w\$]+)\s*\(/).exec(fn);
	        if (!name) {
	            return '(Anonymous)';
	        }
	        return name[1];
	    };

	    if (!mixed_var) {
	        return false;
	    }

	    // BEGIN REDUNDANT
	    this.php_js = this.php_js || {};
	    this.php_js.ini = this.php_js.ini || {};
	    // END REDUNDANT

	    if (typeof mixed_var === 'object') {

	        if (this.php_js.ini['phpjs.objectsAsArrays'] &&  // Strict checking for being a JavaScript array (only check this way if call ini_set('phpjs.objectsAsArrays', 0) to disallow objects as arrays)
	            (
	            (this.php_js.ini['phpjs.objectsAsArrays'].local_value.toLowerCase &&
	                    this.php_js.ini['phpjs.objectsAsArrays'].local_value.toLowerCase() === 'off') ||
	                parseInt(this.php_js.ini['phpjs.objectsAsArrays'].local_value, 10) === 0)
	            ) {
	            return mixed_var.hasOwnProperty('length') && // Not non-enumerable because of being on parent class
	                            !mixed_var.propertyIsEnumerable('length') && // Since is own property, if not enumerable, it must be a built-in function
	                                getFuncName(mixed_var.constructor) !== 'String'; // exclude String()
	        }

	        if (mixed_var.hasOwnProperty) {
	            for (key in mixed_var) {
	                // Checks whether the object has the specified property
	                // if not, we figure it's not an object in the sense of a php-associative-array.
	                if (false === mixed_var.hasOwnProperty(key)) {
	                    return false;
	                }
	            }
	        }

	        // Read discussion at: http://kevin.vanzonneveld.net/techblog/article/javascript_equivalent_for_phps_is_array/
	        return true;
	    }

	    return false;
	}

	/**
	 * Performs an AJAX request and returns the parsed JSON output.
	 * The global akeeba_ajax_url is used as the AJAX proxy URL.
	 * If there is no errorCallback, the global akeeba_error_callback is used.
	 * @param data An object with the query data, e.g. a serialized form
	 * @param successCallback A function accepting a single object parameter, called on success
	 * @param errorCallback A function accepting a single string parameter, called on failure
	 */
	function doAjax(data, successCallback, errorCallback)
	{
		var structure =
		{
			type: "POST",
			url: akeeba_ajax_url,
			cache: false,
			data: data,
			timeout: 600000,
			success: function(msg) {
				// Initialize
				var junk = null;
				var message = "";

				// Get rid of junk before the data
				var valid_pos = msg.indexOf('###');
				if( valid_pos == -1 ) {
					// Valid data not found in the response
					msg = 'Invalid AJAX data received:<br/>' + msg;
					if(errorCallback == null)
					{
						if(akeeba_error_callback != null)
						{
							akeeba_error_callback(msg);
						}
					}
					else
					{
						errorCallback(msg);
					}
					return;
				} else if( valid_pos != 0 ) {
					// Data is prefixed with junk
					junk = msg.substr(0, valid_pos);
					message = msg.substr(valid_pos);
				}
				else
				{
					message = msg;
				}
				message = message.substr(3); // Remove triple hash in the beginning

				// Get of rid of junk after the data
				var valid_pos = message.lastIndexOf('###');
				message = message.substr(0, valid_pos); // Remove triple hash in the end

				try {
					var data = eval('('+message+')');
				} catch(err) {
					var msg = err.message + "\n<br/>\n<pre>\n" + message + "\n</pre>";
					if(errorCallback == null)
					{
						if(akeeba_error_callback != null)
						{
							akeeba_error_callback(msg);
						}
					}
					else
					{
						errorCallback(msg);
					}
					return;
				}

				// Call the callback function
				successCallback(data);
			},
			error: function(Request, textStatus, errorThrown) {
				var message = '<strong>AJAX Loading Error</strong><br/>HTTP Status: '+Request.status+' ('+Request.statusText+')<br/>';
				message = message + 'Internal status: '+textStatus+'<br/>';
				message = message + 'XHR ReadyState: ' + Response.readyState + '<br/>';
				message = message + 'Raw server response:<br/>'+Request.responseText;
				if(errorCallback == null)
				{
					if(akeeba_error_callback != null)
					{
						akeeba_error_callback(message);
					}
				}
				else
				{
					errorCallback(message);
				}
			}
		};
		$.ajax( structure );
	}

	function onChangeProcengine(event)
	{
		if( $('#kickstart\\.procengine').val() == 'ftp' ) {
			$('#ftp-options').show('fast');
		} else {
			$('#ftp-options').hide('fast');
		}
	}

	function closeLightbox(event)
	{
		var closeMe = false;

		if( (event == null) || (event == undefined) ) {
			closeMe = true;
		} else if(event.keyCode == '27') {
			closeMe = true;
		}

		if(closeMe)
		{
			document.getElementById('preextraction').style.display='none';
			document.getElementById('genericerror').style.display='none';
			document.getElementById('fade').style.display='none';
			$(document).unbind('keyup', closeLightbox);
		}
	}

	function onGenericError(msg)
	{
		$('#genericerrorInner').html(msg);
		$('#genericerror').css('display','block');
		$('#fade').css('display','block');
		$(document).keyup(closeLightbox);
	}

	function setProgressBar(percent)
	{
		var newValue = 0;

		if(percent <= 1) {
			newValue = 100 * percent;
		} else {
			newValue = percent;
		}

		$('#progressbar-inner').css('width',percent+'%');
	}

	function oncheckFTPTempDirClick(event)
	{
		var data = {
			'task' : 'checkTempdir',
			'json': JSON.stringify({
				'kickstart.ftp.tempdir': $('#kickstart\\.ftp\\.tempdir').val()
			})
		};

		doAjax(data, function(ret){
			var key = ret.status ? 'FTP_TEMPDIR_WRITABLE' : 'FTP_TEMPDIR_UNWRITABLE';
			alert( trans(key) );
		});
	}

	function onTestFTPClick(event)
	{
		var data = {
			'task' : 'checkFTP',
			'json': JSON.stringify({
				'kickstart.ftp.host':		$('#kickstart\\.ftp\\.host').val(),
				'kickstart.ftp.port':		$('#kickstart\\.ftp\\.port').val(),
				'kickstart.ftp.ssl':		$('#kickstart\\.ftp\\.ssl').is(':checked'),
				'kickstart.ftp.passive':	$('#kickstart\\.ftp\\.passive').is(':checked'),
				'kickstart.ftp.user':		$('#kickstart\\.ftp\\.user').val(),
				'kickstart.ftp.pass':		$('#kickstart\\.ftp\\.pass').val(),
				'kickstart.ftp.dir':		$('#kickstart\\.ftp\\.dir').val(),
				'kickstart.ftp.tempdir':	$('#kickstart\\.ftp\\.tempdir').val()
			})
		};
		doAjax(data, function(ret){
			var key = ret.status ? 'FTP_CONNECTION_OK' : 'FTP_CONNECTION_FAILURE';
			alert( trans(key) + "\n\n" + (ret.status ? '' : ret.message) );
		});
	}

	function onStartExtraction()
	{
		$('#page1').hide('fast');
		$('#page2').show('fast');

		$('#currentFile').text( '' );

		akeeba_error_callback = errorHandler;

		var data = {
			'task' : 'startExtracting',
			'json': JSON.stringify({
<?php 
    if (!$automation->hasAutomation()) {
        ?>
				'kickstart.setup.sourcefile':		$('#kickstart\\.setup\\.sourcefile').val(),
				'kickstart.jps.password':			$('#kickstart\\.jps\\.password').val(),
				'kickstart.tuning.min_exec_time':	$('#kickstart\\.tuning\\.min_exec_time').val(),
				'kickstart.tuning.max_exec_time':	$('#kickstart\\.tuning\\.max_exec_time').val(),
				'kickstart.stealth.enable': 		$('#kickstart\\.stealth\\.enable').is(':checked'),
				'kickstart.stealth.url': 			$('#kickstart\\.stealth\\.url').val(),
				'kickstart.tuning.run_time_bias':	75,
				'kickstart.setup.restoreperms':		0,
				'kickstart.setup.dryrun':			0,
				'kickstart.setup.ignoreerrors':		$('#kickstart\\.setup\\.ignoreerrors').is(':checked'),
				'kickstart.enabled':				1,
				'kickstart.security.password':		'',
				'kickstart.procengine':				$('#kickstart\\.procengine').val(),
				'kickstart.ftp.host':				$('#kickstart\\.ftp\\.host').val(),
				'kickstart.ftp.port':				$('#kickstart\\.ftp\\.port').val(),
				'kickstart.ftp.ssl':				$('#kickstart\\.ftp\\.ssl').is(':checked'),
				'kickstart.ftp.passive':			$('#kickstart\\.ftp\\.passive').is(':checked'),
				'kickstart.ftp.user':				$('#kickstart\\.ftp\\.user').val(),
				'kickstart.ftp.pass':				$('#kickstart\\.ftp\\.pass').val(),
				'kickstart.ftp.dir':				$('#kickstart\\.ftp\\.dir').val(),
				'kickstart.ftp.tempdir':			$('#kickstart\\.ftp\\.tempdir').val()
<?php 
    } else {
        ?>
				'kickstart.setup.sourcefile':		<?php 
        echo autoVar('kickstart.setup.sourcefile');
        ?>
,
				'kickstart.jps.password':			<?php 
        echo autoVar('kickstart.jps.password');
        ?>
,
				'kickstart.tuning.min_exec_time':	<?php 
        echo autoVar('kickstart.tuning.min_exec_time', 1);
        ?>
,
				'kickstart.tuning.max_exec_time':	<?php 
        echo autoVar('kickstart.tuning.max_exec_time', 5);
        ?>
,
				'kickstart.stealth.enable': 		false,
				'kickstart.tuning.run_time_bias':	75,
				'kickstart.setup.restoreperms':		0,
				'kickstart.setup.dryrun':			0,
				'kickstart.setup.ignoreerrors':		<?php 
        echo autoVar('kickstart.setup.ignoreerrors', 0);
        ?>
,
				'kickstart.enabled':				1,
				'kickstart.security.password':		'',
				'kickstart.procengine':				<?php 
        echo autoVar('kickstart.procengine', 'direct');
        ?>
,
				'kickstart.ftp.host':				<?php 
        echo autoVar('kickstart.ftp.host', 'localhost');
        ?>
,
				'kickstart.ftp.port':				<?php 
        echo autoVar('kickstart.ftp.port', 22);
        ?>
,
				'kickstart.ftp.ssl':				<?php 
        echo autoVar('kickstart.ftp.ssl', 0);
        ?>
,
				'kickstart.ftp.passive':			<?php 
        echo autoVar('kickstart.ftp.passive', 1);
        ?>
,
				'kickstart.ftp.user':				<?php 
        echo autoVar('kickstart.ftp.user');
        ?>
,
				'kickstart.ftp.pass':				<?php 
        echo autoVar('kickstart.ftp.pass');
        ?>
,
				'kickstart.ftp.dir':				<?php 
        echo autoVar('kickstart.ftp.dir', '/');
        ?>
,
				'kickstart.ftp.tempdir':			<?php 
        echo autoVar('kickstart.ftp.tempdir', AKKickstartUtils::getPath() . 'kicktemp');
    }
    ?>
			})
		};
		doAjax(data, function(ret){
			processRestorationStep(ret);
		});
	}

	function processRestorationStep(data)
	{
		// Look for errors
		if(!data.status)
		{
			errorHandler(data.message);
			return;
		}

		// Propagate warnings to the GUI
		if( !empty(data.Warnings) )
		{
			$.each(data.Warnings, function(i, item){
				$('#warnings').append(
					$(document.createElement('div'))
					.html(item)
				);
				$('#warningsBox').show('fast');
			});
		}

		// Parse total size, if exists
		if(array_key_exists('totalsize', data))
		{
			if(is_array(data.filelist))
			{
				akeeba_restoration_stat_total = 0;
				$.each(data.filelist,function(i, item)
				{
					akeeba_restoration_stat_total += item[1];
				});
			}
			akeeba_restoration_stat_outbytes = 0;
			akeeba_restoration_stat_inbytes = 0;
			akeeba_restoration_stat_files = 0;
		}

		// Update GUI
		akeeba_restoration_stat_inbytes += data.bytesIn;
		akeeba_restoration_stat_outbytes += data.bytesOut;
		akeeba_restoration_stat_files += data.files;
		var percentage = 0;
		if( akeeba_restoration_stat_total > 0 )
		{
			percentage = 100 * akeeba_restoration_stat_inbytes / akeeba_restoration_stat_total;
			if(percentage < 0) {
				percentage = 0;
			} else if(percentage > 100) {
				percentage = 100;
			}
		}
		if(data.done) percentage = 100;
		setProgressBar(percentage);
		$('#currentFile').text( data.lastfile );

		if(!empty(data.factory)) akeeba_factory = data.factory;

		post = {
			'task'	: 'continueExtracting',
			'json'	: JSON.stringify({factory: akeeba_factory})
		};

		if(!data.done)
		{
			doAjax(post, function(ret){
				processRestorationStep(ret);
			});
		}
		else
		{
			$('#page2a').hide('fast');
			$('#extractionComplete').show('fast');

			$('#runInstaller').css('display','inline-block');
			if(akeeba_automation) $('#runInstaller').click();
		}
	}

	function onGotoStartClick(event)
	{
		$('#page2').hide('fast');
		$('#error').hide('fast');
		$('#page1').show('fast');
	}

	function onRunInstallerClick(event)
	{
		var windowReference = window.open('installation/index.php','installer');
		if(!windowReference.opener) {
			windowReference.opener = this.window;
		}
		$('#runCleanup').css('display','inline-block');
		$('#runInstaller').hide('fast');
	}

	function onRunCleanupClick(event)
	{
		post = {
			'task'	: 'cleanUp',
			// Passing the factory preserves the renamed files array
			'json'	: JSON.stringify({factory: akeeba_factory})
		};

		doAjax(post, function(ret){
			$('#runCleanup').hide('fast');
			$('#gotoSite').css('display','inline-block');
			$('#gotoAdministrator').css('display','inline-block');
			$('#gotoPostRestorationRroubleshooting').css('display','block');
		});
	}

	function errorHandler(msg)
	{
		$('#errorMessage').html(msg);
		$('#error').show('fast');
	}

	function onresetFTPTempDir(event)
	{
		$('#kickstart\\.ftp\\.tempdir').val('<?php 
    echo addcslashes(AKKickstartUtils::getPath(), '\\\'"');
    ?>
');
	}

	/**
	 * Akeeba Kickstart Update Check
	 */

	var akeeba_update = {version: '0'};
	var akeeba_version = '3.6.0';
	
	function version_compare (v1, v2, operator) {
		// BEGIN REDUNDANT
		this.php_js = this.php_js || {};
		this.php_js.ENV = this.php_js.ENV || {};
		// END REDUNDANT
		// Important: compare must be initialized at 0. 
		var i = 0,
			x = 0,
			compare = 0,
			// vm maps textual PHP versions to negatives so they're less than 0.
			// PHP currently defines these as CASE-SENSITIVE. It is important to
			// leave these as negatives so that they can come before numerical versions
			// and as if no letters were there to begin with.
			// (1alpha is < 1 and < 1.1 but > 1dev1)
			// If a non-numerical value can't be mapped to this table, it receives
			// -7 as its value.
			vm = {
				'dev': -6,
				'alpha': -5,
				'a': -5,
				'beta': -4,
				'b': -4,
				'RC': -3,
				'rc': -3,
				'#': -2,
				'p': -1,
				'pl': -1
			},
			// This function will be called to prepare each version argument.
			// It replaces every _, -, and + with a dot.
			// It surrounds any nonsequence of numbers/dots with dots.
			// It replaces sequences of dots with a single dot.
			//    version_compare('4..0', '4.0') == 0
			// Important: A string of 0 length needs to be converted into a value
			// even less than an unexisting value in vm (-7), hence [-8].
			// It's also important to not strip spaces because of this.
			//   version_compare('', ' ') == 1
			prepVersion = function (v) {
				v = ('' + v).replace(/[_\-+]/g, '.');
				v = v.replace(/([^.\d]+)/g, '.$1.').replace(/\.{2,}/g, '.');
				return (!v.length ? [-8] : v.split('.'));
			},
			// This converts a version component to a number.
			// Empty component becomes 0.
			// Non-numerical component becomes a negative number.
			// Numerical component becomes itself as an integer.
			numVersion = function (v) {
				return !v ? 0 : (isNaN(v) ? vm[v] || -7 : parseInt(v, 10));
			};
		v1 = prepVersion(v1);
		v2 = prepVersion(v2);
		x = Math.max(v1.length, v2.length);
		for (i = 0; i < x; i++) {
			if (v1[i] == v2[i]) {
				continue;
			}
			v1[i] = numVersion(v1[i]);
			v2[i] = numVersion(v2[i]);
			if (v1[i] < v2[i]) {
				compare = -1;
				break;
			} else if (v1[i] > v2[i]) {
				compare = 1;
				break;
			}
		}
		if (!operator) {
			return compare;
		}

		// Important: operator is CASE-SENSITIVE.
		// "No operator" seems to be treated as "<."
		// Any other values seem to make the function return null.
		switch (operator) {
		case '>':
		case 'gt':
			return (compare > 0);
		case '>=':
		case 'ge':
			return (compare >= 0);
		case '<=':
		case 'le':
			return (compare <= 0);
		case '==':
		case '=':
		case 'eq':
			return (compare === 0);
		case '<>':
		case '!=':
		case 'ne':
			return (compare !== 0);
		case '':
		case '<':
		case 'lt':
			return (compare < 0);
		default:
			return null;
		}
	}

	function checkUpdates()
	{
		var structure =
		{
			type: "GET",
			url: 'http://query.yahooapis.com/v1/public/yql',
			data: {
<?php 
    if (KICKSTARTPRO) {
        ?>
				q: 'SELECT * FROM xml WHERE url="http://nocdn.akeebabackup.com/updates/kickstart.xml"',
<?php 
    } else {
        ?>
				q: 'SELECT * FROM xml WHERE url="http://nocdn.akeebabackup.com/updates/kickstartpro.xml"',
<?php 
    }
    ?>
				format: 'json',
				callback: 'updatesCallback'
			},
			cache: true,
			crossDomain: true,
			jsonp: 'updatesCallback',
			timeout: 15000
		};
		$.ajax( structure );
	}
	
	function updatesCallback(msg)
	{
		$.each(msg.query.results.updates.update, function(i, el){
			var myUpdate = {
				'version'	: el.version,
				'infourl'	: el.infourl['content'],
				'dlurl'		: el.downloads.downloadurl.content
			}
			if(version_compare(myUpdate.version, akeeba_update.version, 'ge')) {
				akeeba_update = myUpdate;
			}
		});
		
		if(version_compare(akeeba_update.version, akeeba_version, 'gt')) {
			notifyAboutUpdates();
		}
	}
	
	function notifyAboutUpdates()
	{
		$('#update-version').text(akeeba_update.version);
		$('#update-dlnow').attr('href', akeeba_update.dlurl);
		$('#update-whatsnew').attr('href', akeeba_update.infourl);
		$('#update-notification').show('slow');
	}
	
	<?php 
    callExtraFeature('onExtraHeadJavascript');
    ?>
	</script>
</head>
<body>

<div id="automode" style="display:none;">
	AUTOMODEON
</div>

<div id="fade" class="black_overlay"></div>

<div id="page-container">

<div id="preextraction" class="white_content">
	<div id="ie7Warning" style="display:none;">
		<h2>Deprecated Internet Explorer version</h2>
		<p>This script is not guaranteed to work properly on Internet Explorer 7 or previous version,
		or on Internet Explorer 8 and higher running in compatibility mode.</p>
		<p>Please use Internet Explorer 8+ in native mode (the &quot;broken page&quot; icon next to the
		address bar should not be enabled). Alternatively, you may use the latest versions of Firefox,
		Safari, Google Chrome or SRWare Iron.</p>
	</div>

	<h2>THINGS_HEADER</h2>
	<ol>
		<li>THINGS_01</li>
		<li>THINGS_02</li>
		<li>THINGS_03</li>
		<li>THINGS_04</li>
		<li>THINGS_05</li>
		<li>THINGS_06</li>
		<li>THINGS_07</li>
		<li>THINGS_08</li>
		<li>THINGS_09</li>
	</ol>
	<a href="javascript:void(0)" onclick="closeLightbox();">CLOSE_LIGHTBOX</a>
</div>

<div id="genericerror" class="white_content">
	<pre id="genericerrorInner"></pre>
</div>

	<div id="header">
		<div class="title">Akeeba Kickstart <?php 
    echo $edition;
    ?>
 3.6.0</div>
	</div>
	
	<div id="update-notification" style="display: none">
		<p class="update-notify">UPDATE_HEADER</p>
		<p class="update-whyupdate">UPDATE_NOTICE</p>
		<p class="update-links">
			<a href="#" id="update-dlnow">UPDATE_DLNOW</a>
			<a href="#" id="update-whatsnew" target="_blank">UPDATE_MOREINFO</a> 
		</p>
	</div>

<div id="page1">
	<?php 
    callExtraFeature('onPage1');
    ?>
	
	<div id="page1-content">
		
	<div class="helpme">
		<span>NEEDSOMEHELPKS</span> <a href="https://www.akeebabackup.com/documentation/quick-start-guide/using-kickstart.html" target="_blank">QUICKSTART</a>
	</div>
	
	<div class="step1">
		<div class="circle">1</div>
		<h2>SELECT_ARCHIVE</h2>
		<div class="area-container">
			<?php 
    callExtraFeature('onPage1Step1');
    ?>
			<div class="clr"></div>
			
			<label for="kickstart.setup.sourcefile">ARCHIVE_FILE</label>
			<span class="field">
				<?php 
    $filelist = AKKickstartUtils::getArchivesAsOptions();
    if (!empty($filelist)) {
        ?>
					<select id="kickstart.setup.sourcefile">
					<?php 
        echo $filelist;
        ?>
					</select>
				<?php 
    } else {
        ?>
					<a href="https://www.akeebabackup.com/documentation/troubleshooter/ksnoarchives.html" target="_blank">NOARCHIVESCLICKHERE</a>
				<?php 
    }
    ?>
			</span>
			<br />
			<label for="kickstart.jps.password">JPS_PASSWORD</label>
			<span class="field"><input type="password" id="kickstart.jps.password" value="" /></span>
		</div>
	</div>

	<div class="clr"></div>

	<div class="step2">
		<div class="circle">2</div>
		<h2>SELECT_EXTRACTION</h2>
		<div class="area-container">
			<label for="kickstart.procengine">WRITE_TO_FILES</label>
			<span class="field">
				<select id="kickstart.procengine">
					<option value="direct">WRITE_DIRECTLY</option>
					<option value="ftp">WRITE_FTP</option>
				</select>
			</span><br/>
			
			<label for="kickstart.setup.ignoreerrors">IGNORE_MOST_ERRORS</label>
			<span class="field"><input type="checkbox" id="kickstart.setup.ignoreerrors" /></span>

			<div id="ftp-options">
				<label for="kickstart.ftp.host">FTP_HOST</label>
				<span class="field"><input type="text" id="kickstart.ftp.host" value="localhost" /></span><br />
				<label for="kickstart.ftp.port">FTP_PORT</label>
				<span class="field"><input type="text" id="kickstart.ftp.port" value="21" /></span><br />
				<label for="kickstart.ftp.ssl">FTP_FTPS</label>
				<span class="field"><input type="checkbox" id="kickstart.ftp.ssl" /></span><br />
				<label for="kickstart.ftp.passive">FTP_PASSIVE</label>
				<span class="field"><input type="checkbox" id="kickstart.ftp.passive" checked="checked" /></span><br />
				<label for="kickstart.ftp.user">FTP_USER</label>
				<span class="field"><input type="text" id="kickstart.ftp.user" value="" /></span><br />
				<label for="kickstart.ftp.pass">FTP_PASS</label>
				<span	class="field"><input type="password" id="kickstart.ftp.pass" value="" /></span><br />
				<label for="kickstart.ftp.dir">FTP_DIR</label>
				<span class="field"><input type="text" id="kickstart.ftp.dir" value="" /></span><br />

				<label for="kickstart.ftp.tempdir">FTP_TEMPDIR</label>
				<span class="field">
					<input type="text" id="kickstart.ftp.tempdir" value="<?php 
    echo htmlentities(AKKickstartUtils::getPath());
    ?>
" />
					<span class="button" id="checkFTPTempDir">BTN_CHECK</span>
					<span class="button" id="resetFTPTempDir">BTN_RESET</span>
				</span><br />
				<label></label>
				<span class="button" id="testFTP">BTN_TESTFTPCON</span>
				<a id="notWorking" class="button" href="https://www.akeebabackup.com/documentation/troubleshooter/kscantextract.html" target="_blank">CANTGETITTOWORK</a>
				<br />
			</div>

		</div>
	</div>

	<div class="clr"></div>

	<div class="step3">
		<div class="circle">3</div>
		<h2>FINE_TUNE</h2>
		<div class="area-container">
			<label for="kickstart.tuning.min_exec_time">MIN_EXEC_TIME</label>
			<span class="field"><input type="text" id="kickstart.tuning.min_exec_time" value="1" /></span> <span>SECONDS_PER_STEP</span><br />
			<label for="kickstart.tuning.max_exec_time">MAX_EXEC_TIME</label>
			<span class="field"><input type="text" id="kickstart.tuning.max_exec_time" value="5" /></span> <span>SECONDS_PER_STEP</span><br />

			<label for="kickstart.stealth.enable">STEALTH_MODE</label>
			<span class="field"><input type="checkbox" id="kickstart.stealth.enable" /></span><br />
			<label for="kickstart.stealth.url">STEALTH_URL</label>
			<span class="field"><input type="text" id="kickstart.stealth.url" value="" /></span><br />
		</div>
	</div>

	<div class="clr"></div>

	<div class="step4">
		<div class="circle">4</div>
		<h2>EXTRACT_FILES</h2>
		<div class="area-container">
			<span></span>
			<span id="gobutton" class="button">BTN_START</span>
		</div>
	</div>

	<div class="clr"></div>
	
	</div>
</div>

<div id="page2">
	<div id="page2a">
		<div class="circle">5</div>
		<h2>EXTRACTING</h2>
		<div class="area-container">
			<div id="warn-not-close">DO_NOT_CLOSE_EXTRACT</div>
			<div id="progressbar">
				<div id="progressbar-inner">&nbsp;</div>
			</div>
			<div id="currentFile"></div>
		</div>
	</div>

	<div id="extractionComplete" style="display: none">
		<div class="circle">6</div>
		<h2>RESTACLEANUP</h2>
		<div id="runInstaller" class="button">BTN_RUNINSTALLER</div>
		<div id="runCleanup" class="button" style="display:none">BTN_CLEANUP</div>
		<div id="gotoSite" class="button" style="display:none">BTN_SITEFE</div>
		<div id="gotoAdministrator" class="button" style="display:none">BTN_SITEBE</div>
		<div id="gotoPostRestorationRroubleshooting" style="display:none">
			<a href="https://www.akeebabackup.com/documentation/troubleshooter/post-restoration.html" target="_blank">POSTRESTORATIONTROUBLESHOOTING</a>
		</div>
	</div>

	<div id="warningsBox" style="display: none;">
		<div id="warningsHeader">
			<h2>WARNINGS</h2>
		</div>
		<div id="warningsContainer">
		<div id="warnings"></div>
		</div>
	</div>

	<div id="error" style="display: none;">
		<h3>ERROR_OCCURED</h3>
		<p id="errorMessage"></p>
		<div id="gotoStart" class="button">BTN_GOTOSTART</div>
		<div>
			<a href="https://www.akeebabackup.com/documentation/troubleshooter/kscantextract.html" target="_blank">CANTGETITTOWORK</a>
		</div>
	</div>
</div>

<div id="footer">
	<div class="copyright">Copyright &copy; 2008&ndash;2012 <a	href="http://www.akeebabackup.com">Nicholas K.
		Dionysopoulos / Akeeba Backup</a>. All legal rights reserved.<br />

		This program is free software: you can redistribute it and/or modify it under the terms of
		the <a href="http://www.gnu.org/gpl-3.htmlhttp://www.gnu.org/copyleft/gpl.html">GNU General
		Public License</a> as published by the Free Software Foundation, either version 3 of the License,
		or (at your option) any later version.<br />
		Design credits: <a href="http://internet-inspired.com/">Internet Inspired</a>, slightly modified by AkeebaBackup.com
	</div>
</div>

</div>

</body>
</html>
	<?php 
}
Exemplo n.º 3
0
/**
 * Akeeba Kickstart
 * A JSON-powered archive extraction tool
 *
 * @copyright   2010-2014 Nicholas K. Dionysopoulos / AkeebaBackup.com
 * @license     GNU GPL v2 or - at your option - any later version
 * @package     akeebabackup
 * @subpackage  kickstart
 */
function echoHeadJavascript()
{
    ?>
<script type="text/javascript" language="javascript">
	var akeeba_debug = <?php 
    echo defined('KSDEBUG') ? 'true' : 'false';
    ?>
;
	var sftp_path = '<?php 
    echo TranslateWinPath(defined('KSROOTDIR') ? KSROOTDIR : __DIR__);
    ?>
/';
	var isJoomla = true;

	/**
	 * Returns the version of Internet Explorer or a -1
	 * (indicating the use of another browser).
	 *
	 * @return   integer  MSIE version or -1
	 */
	function getInternetExplorerVersion()
	{
		var rv = -1; // Return value assumes failure.
		if (navigator.appName == 'Microsoft Internet Explorer')
		{
			var ua = navigator.userAgent;
			var re  = new RegExp("MSIE ([0-9]{1,}[\.0-9]{0,})");
			if (re.exec(ua) != null)
			{
				rv = parseFloat( RegExp.$1 );
			}
		}
		return rv;
	}

	$(document).ready(function(){
		// Hide 2nd Page
		$('#page2').css('display','none');

		// Translate the GUI
		translateGUI();

		// Hook interaction handlers
		$(document).keyup( closeLightbox );
		$('#kickstart\\.procengine').change( onChangeProcengine );
		$('#kickstart\\.setup\\.sourcepath').change( onArchiveListReload );
		$('#reloadArchives').click ( onArchiveListReload );
		$('#checkFTPTempDir').click( oncheckFTPTempDirClick );
		$('#resetFTPTempDir').click( onresetFTPTempDir );
		$('#browseFTP').click( onbrowseFTP );
		$('#testFTP').click( onTestFTPClick );
		$('#gobutton').click( onStartExtraction );
		$('#runInstaller').click( onRunInstallerClick );
		$('#runCleanup').click( onRunCleanupClick );
		$('#gotoSite').click(function(event){window.open('index.php','finalstepsite'); window.close();});
		$('#gotoAdministrator').click(function(event){window.open('administrator/index.php','finalstepadmin'); window.close();});
		$('#gotoStart').click( onGotoStartClick );

		// Reset the progress bar
		setProgressBar(0);

		// Show warning
		var msieVersion = getInternetExplorerVersion();
		if((msieVersion != -1) && (msieVersion <= 8.99))
		{
			$('#ie7Warning').css('display','block');
		}
		if(!akeeba_debug) {
			$('#preextraction').css('display','block');
			$('#fade').css('display','block');
		}

		// Trigger change, so we avoid problems if the user refreshes the page
		$('#kickstart\\.procengine').change();
	});

	var translation = {
		<?php 
    echoTranslationStrings();
    ?>
	}

	var akeeba_ajax_url = '<?php 
    echo defined('KSSELFNAME') ? KSSELFNAME : basename(__FILE__);
    ?>
';
	var akeeba_error_callback = onGenericError;
	var akeeba_restoration_stat_inbytes = 0;
	var akeeba_restoration_stat_outbytes = 0;
	var akeeba_restoration_stat_files = 0;
	var akeeba_restoration_stat_total = 0;
	var akeeba_factory = null;

	var akeeba_ftpbrowser_host = null;
	var akeeba_ftpbrowser_port = 21;
	var akeeba_ftpbrowser_username = null;
	var akeeba_ftpbrowser_password = null;
	var akeeba_ftpbrowser_passive = 1;
	var akeeba_ftpbrowser_ssl = 0;
	var akeeba_ftpbrowser_directory = '';

	var akeeba_sftpbrowser_host = null;
	var akeeba_sftpbrowser_port = 21;
	var akeeba_sftpbrowser_username = null;
	var akeeba_sftpbrowser_password = null;
	var akeeba_sftpbrowser_pubkey = null;
	var akeeba_sftpbrowser_privkey = null;
	var akeeba_sftpbrowser_directory = '';

	function translateGUI()
	{
		$('*').each(function(i,e){
			transKey = $(e).text();
			if(array_key_exists(transKey, translation))
			{
				$(e).text( translation[transKey] );
			}
		});
	}

	function trans(key)
	{
		if(array_key_exists(key, translation)) {
			return translation[key];
		} else {
			return key;
		}
	}

	function array_key_exists ( key, search ) {
		if (!search || (search.constructor !== Array && search.constructor !== Object)){
			return false;
		}
		return key in search;
	}

	function empty (mixed_var) {
		var key;

		if (mixed_var === "" ||
			mixed_var === 0 ||
			mixed_var === "0" ||
			mixed_var === null ||
			mixed_var === false ||
			typeof mixed_var === 'undefined'
		){
			return true;
		}

		if (typeof mixed_var == 'object') {
			for (key in mixed_var) {
				return false;
			}
			return true;
		}

		return false;
	}

	function is_array (mixed_var) {
		var key = '';
		var getFuncName = function (fn) {
			var name = (/\W*function\s+([\w\$]+)\s*\(/).exec(fn);
			if (!name) {
				return '(Anonymous)';
			}
			return name[1];
		};

		if (!mixed_var) {
			return false;
		}

		// BEGIN REDUNDANT
		this.php_js = this.php_js || {};
		this.php_js.ini = this.php_js.ini || {};
		// END REDUNDANT

		if (typeof mixed_var === 'object') {

			if (this.php_js.ini['phpjs.objectsAsArrays'] &&  // Strict checking for being a JavaScript array (only check this way if call ini_set('phpjs.objectsAsArrays', 0) to disallow objects as arrays)
				(
				(this.php_js.ini['phpjs.objectsAsArrays'].local_value.toLowerCase &&
				this.php_js.ini['phpjs.objectsAsArrays'].local_value.toLowerCase() === 'off') ||
				parseInt(this.php_js.ini['phpjs.objectsAsArrays'].local_value, 10) === 0)
			) {
				return mixed_var.hasOwnProperty('length') && // Not non-enumerable because of being on parent class
				!mixed_var.propertyIsEnumerable('length') && // Since is own property, if not enumerable, it must be a built-in function
				getFuncName(mixed_var.constructor) !== 'String'; // exclude String()
			}

			if (mixed_var.hasOwnProperty) {
				for (key in mixed_var) {
					// Checks whether the object has the specified property
					// if not, we figure it's not an object in the sense of a php-associative-array.
					if (false === mixed_var.hasOwnProperty(key)) {
						return false;
					}
				}
			}

			// Read discussion at: http://kevin.vanzonneveld.net/techblog/article/javascript_equivalent_for_phps_is_array/
			return true;
		}

		return false;
	}

	function resolvePath(filename)
	{
		filename = filename.replace('\/\/g', '\/');
		var parts = filename.split('/');
		var out = [];

		$.each(parts, function(i, part){
			if (part == '.') return;
			if (part == '..') {
				out.pop();
				return;
			}
			out.push(part);
		});

		return out.join('/');
	}

	/**
	 * Performs an AJAX request and returns the parsed JSON output.
	 * The global akeeba_ajax_url is used as the AJAX proxy URL.
	 * If there is no errorCallback, the global akeeba_error_callback is used.
	 * @param data An object with the query data, e.g. a serialized form
	 * @param successCallback A function accepting a single object parameter, called on success
	 * @param errorCallback A function accepting a single string parameter, called on failure
	 */
	function doAjax(data, successCallback, errorCallback)
	{
		var structure =
		{
			type: "POST",
			url: akeeba_ajax_url,
			cache: false,
			data: data,
			timeout: 600000,
			success: function(msg) {
				// Initialize
				var junk = null;
				var message = "";

				// Get rid of junk before the data
				var valid_pos = msg.indexOf('###');
				if( valid_pos == -1 ) {
					// Valid data not found in the response
					msg = 'Invalid AJAX data received:<br/>' + msg;
					if(errorCallback == null)
					{
						if(akeeba_error_callback != null)
						{
							akeeba_error_callback(msg);
						}
					}
					else
					{
						errorCallback(msg);
					}
					return;
				} else if( valid_pos != 0 ) {
					// Data is prefixed with junk
					junk = msg.substr(0, valid_pos);
					message = msg.substr(valid_pos);
				}
				else
				{
					message = msg;
				}
				message = message.substr(3); // Remove triple hash in the beginning

				// Get of rid of junk after the data
				var valid_pos = message.lastIndexOf('###');
				message = message.substr(0, valid_pos); // Remove triple hash in the end

				try {
					var data = eval('('+message+')');
				} catch(err) {
					var msg = err.message + "\n<br/>\n<pre>\n" + message + "\n</pre>";
					if(errorCallback == null)
					{
						if(akeeba_error_callback != null)
						{
							akeeba_error_callback(msg);
						}
					}
					else
					{
						errorCallback(msg);
					}
					return;
				}

				// Call the callback function
				successCallback(data);
			},
			error: function(Request, textStatus, errorThrown) {
				var message = '<strong>AJAX Loading Error</strong><br/>HTTP Status: '+Request.status+' ('+Request.statusText+')<br/>';
				message = message + 'Internal status: '+textStatus+'<br/>';
				message = message + 'XHR ReadyState: ' + Response.readyState + '<br/>';
				message = message + 'Raw server response:<br/>'+Request.responseText;
				if(errorCallback == null)
				{
					if(akeeba_error_callback != null)
					{
						akeeba_error_callback(message);
					}
				}
				else
				{
					errorCallback(message);
				}
			}
		};
		$.ajax( structure );
	}

	function onChangeProcengine(event)
	{
		if( $('#kickstart\\.procengine').val() == 'direct' )
		{
			$('#ftp-options').hide('fast');
		} else {
			$('#ftp-options').show('fast');
		}

		if($('#kickstart\\.procengine').val() == 'sftp' )
		{
			$('#ftp-ssl-passive').hide('fast');

			if($('#kickstart\\.ftp\\.dir').val() == ''){
				$('#kickstart\\.ftp\\.dir').val(sftp_path);
			}

			$('#testFTP').html(trans('BTN_TESTSFTPCON'))
		}
		else
		{
			$('#ftp-ssl-passive').show('fast');
			$('#testFTP').html(trans('BTN_TESTFTPCON'))
		}
	}

	function closeLightbox(event)
	{
		var closeMe = false;

		if( (event == null) || (event == undefined) ) {
			closeMe = true;
		} else if(event.keyCode == '27') {
			closeMe = true;
		}

		if(closeMe)
		{
			document.getElementById('preextraction').style.display='none';
			document.getElementById('genericerror').style.display='none';
			document.getElementById('fade').style.display='none';
			$(document).unbind('keyup', closeLightbox);
		}
	}

	function onGenericError(msg)
	{
		$('#genericerrorInner').html(msg);
		$('#genericerror').css('display','block');
		$('#fade').css('display','block');
		$(document).keyup(closeLightbox);
	}

	function setProgressBar(percent)
	{
		var newValue = 0;

		if(percent <= 1) {
			newValue = 100 * percent;
		} else {
			newValue = percent;
		}

		$('#progressbar-inner').css('width',percent+'%');
	}

	function oncheckFTPTempDirClick(event)
	{
		var data = {
			'task' : 'checkTempdir',
			'json': JSON.stringify({
				'kickstart.ftp.tempdir': $('#kickstart\\.ftp\\.tempdir').val()
			})
		};

		doAjax(data, function(ret){
			var key = ret.status ? 'FTP_TEMPDIR_WRITABLE' : 'FTP_TEMPDIR_UNWRITABLE';
			alert( trans(key) );
		});
	}

	function onTestFTPClick(event)
	{
		var type = 'ftp';

		if($('#kickstart\\.procengine').val() == 'sftp')
		{
			type = 'sftp';
		}

		var data = {
			'task' : 'checkFTP',
			'json': JSON.stringify({
				'type' : type,
				'kickstart.ftp.host':		$('#kickstart\\.ftp\\.host').val(),
				'kickstart.ftp.port':		$('#kickstart\\.ftp\\.port').val(),
				'kickstart.ftp.ssl':		$('#kickstart\\.ftp\\.ssl').is(':checked'),
				'kickstart.ftp.passive':	$('#kickstart\\.ftp\\.passive').is(':checked'),
				'kickstart.ftp.user':		$('#kickstart\\.ftp\\.user').val(),
				'kickstart.ftp.pass':		$('#kickstart\\.ftp\\.pass').val(),
				'kickstart.ftp.dir':		$('#kickstart\\.ftp\\.dir').val(),
				'kickstart.ftp.tempdir':	$('#kickstart\\.ftp\\.tempdir').val()
			})
		};
		doAjax(data, function(ret){
			if(type == 'ftp'){
				var key = ret.status ? 'FTP_CONNECTION_OK' : 'FTP_CONNECTION_FAILURE';
			}
			else{
				var key = ret.status ? 'SFTP_CONNECTION_OK' : 'SFTP_CONNECTION_FAILURE';
			}


			alert( trans(key) + "\n\n" + (ret.status ? '' : ret.message) );
		});
	}

	function onbrowseFTP ()
	{
		if($('#kickstart\\.procengine').val() != 'sftp')
		{
			akeeba_ftpbrowser_host      = $('#kickstart\\.ftp\\.host').val();
			akeeba_ftpbrowser_port      = $('#kickstart\\.ftp\\.port').val();
			akeeba_ftpbrowser_username  = $('#kickstart\\.ftp\\.user').val();
			akeeba_ftpbrowser_password  = $('#kickstart\\.ftp\\.pass').val();
			akeeba_ftpbrowser_passive   = $('#kickstart\\.ftp\\.passive').is(':checked');
			akeeba_ftpbrowser_ssl       = $('#kickstart\\.ftp\\.ssl').is(':checked');
			akeeba_ftpbrowser_directory = $('#kickstart\\.ftp\\.dir').val();

			var akeeba_onbrowseFTP_callback = function(path) {
				var charlist = ('/').replace(/([\[\]\(\)\.\?\/\*\{\}\+\$\^\:])/g, '$1');
				var re = new RegExp('^[' + charlist + ']+', 'g');
				path = '/' + (path+'').replace(re, '');
				$('#kickstart\\.ftp\\.dir').val(path);
			};

			akeeba_ftpbrowser_hook( akeeba_onbrowseFTP_callback );
		}
		else
		{
			akeeba_sftpbrowser_host = $('#kickstart\\.ftp\\.host').val();
			akeeba_sftpbrowser_port = $('#kickstart\\.ftp\\.port').val();
			akeeba_sftpbrowser_username = $('#kickstart\\.ftp\\.user').val();
			akeeba_sftpbrowser_password = $('#kickstart\\.ftp\\.pass').val();
			akeeba_sftpbrowser_directory = $('#kickstart\\.ftp\\.dir').val();

			var akeeba_postprocsftp_callback = function(path) {
				var charlist = ('/').replace(/([\[\]\(\)\.\?\/\*\{\}\+\$\^\:])/g, '$1');
				var re = new RegExp('^[' + charlist + ']+', 'g');
				path = '/' + (path+'').replace(re, '');
				$('#kickstart\\.ftp\\.dir').val(path);
			};

			akeeba_sftpbrowser_hook( akeeba_postprocsftp_callback );
		}
	}

	akeeba_ftpbrowser_hook = function( callback )
	{
		var ftp_dialog_element = $("#ftpdialog");
		var ftp_callback = function() {
			callback(akeeba_ftpbrowser_directory);
			ftp_dialog_element.dialog("close");
		};

		ftp_dialog_element.css('display','block');
		ftp_dialog_element.removeClass('ui-state-error');
		ftp_dialog_element.dialog({
			autoOpen	: false,
			title		: trans('CONFIG_UI_FTPBROWSER_TITLE'),
			draggable	: false,
			height		: 500,
			width		: 500,
			modal		: true,
			resizable	: false,
			buttons		: {
				"OK": ftp_callback,
				"Cancel": function() {
					ftp_dialog_element.dialog("close");
				}
			}
		});

		$('#ftpBrowserErrorContainer').css('display','none');
		$('#ftpBrowserFolderList').html('');
		$('#ak_crumbs').html('');

		ftp_dialog_element.dialog('open');

		if(empty(akeeba_ftpbrowser_directory)) akeeba_ftpbrowser_directory = '';

		var data = {
			'task'      : 'ftpbrowse',
			'json': JSON.stringify({
				'host'		: akeeba_ftpbrowser_host,
				'port'		: akeeba_ftpbrowser_port,
				'username'	: akeeba_ftpbrowser_username,
				'password'	: akeeba_ftpbrowser_password,
				'passive'	: (akeeba_ftpbrowser_passive ? 1 : 0),
				'ssl'		: (akeeba_ftpbrowser_ssl ? 1 : 0),
				'directory'	: akeeba_ftpbrowser_directory
			})
		};

		// Do AJAX call and Render results
		doAjax(
			data,
			function(data) {
				if(data.error != false) {
					// An error occured
					$('#ftpBrowserError').html(trans(data.error));
					$('#ftpBrowserErrorContainer').css('display','block');
					$('#ftpBrowserFolderList').css('display','none');
					$('#ak_crumbs').css('display','none');
				} else {
					// Create the interface
					$('#ftpBrowserErrorContainer').css('display','none');

					// Display the crumbs
					if(!empty(data.breadcrumbs)) {
						$('#ak_crumbs').css('display','block');
						$('#ak_crumbs').html('');
						var relativePath = '/';

						akeeba_ftpbrowser_addcrumb(trans('UI-ROOT'), '/', callback);

						$.each(data.breadcrumbs, function(i, crumb) {
							relativePath += '/'+crumb;

							akeeba_ftpbrowser_addcrumb(crumb, relativePath, callback);
						});
					} else {
						$('#ak_crumbs').hide();
					}

					// Display the list of directories
					if(!empty(data.list)) {
						$('#ftpBrowserFolderList').show();

						$.each(data.list, function(i, item) {
							akeeba_ftpbrowser_create_link(akeeba_ftpbrowser_directory+'/'+item, item, $('#ftpBrowserFolderList'), callback );
						});
					} else {
						$('#ftpBrowserFolderList').css('display','none');
					}
				}
			},
			function(message) {
				$('#ftpBrowserError').html(message);
				$('#ftpBrowserErrorContainer').css('display','block');
				$('#ftpBrowserFolderList').css('display','none');
				$('#ak_crumbs').css('display','none');
			}
		);
	};

	/**
	 * Creates a directory link for the FTP browser UI
	 */
	function akeeba_ftpbrowser_create_link(path, label, container, callback)
	{
		var row = $(document.createElement('tr'));
		var cell = $(document.createElement('td')).appendTo(row);

		var myElement = $(document.createElement('a'))
			.text(label)
			.click(function(){
				akeeba_ftpbrowser_directory = resolvePath(path);
				akeeba_ftpbrowser_hook(callback);
			})
			.appendTo(cell);
		row.appendTo($(container));
	}

	/**
	 * Adds a breadcrumb to the FTP browser
	 */
	function akeeba_ftpbrowser_addcrumb(crumb, relativePath, callback, last)
	{
		if(empty(last)) last = false;
		var li = $(document.createElement('li'));

		$(document.createElement('a'))
			.html(crumb)
			.click(function(e){
				akeeba_ftpbrowser_directory = relativePath;
				akeeba_ftpbrowser_hook(callback);
				e.preventDefault();
			})
			.appendTo(li);

		if(!last) {
			$(document.createElement('span'))
				.text('/')
				.addClass('divider')
				.appendTo(li);
		}

		li.appendTo('#ak_crumbs');
	}

	// FTP browser function
	akeeba_sftpbrowser_hook = function( callback )
	{
		var sftp_dialog_element = $("#ftpdialog");
		var sftp_callback = function() {
			callback(akeeba_sftpbrowser_directory);
			sftp_dialog_element.dialog("close");
		};

		sftp_dialog_element.css('display','block');
		sftp_dialog_element.removeClass('ui-state-error');
		sftp_dialog_element.dialog({
			autoOpen	: false,
			'title'		: trans('CONFIG_UI_SFTPBROWSER_TITLE'),
			draggable	: false,
			height		: 500,
			width		: 500,
			modal		: true,
			resizable	: false,
			buttons		: {
				"OK": sftp_callback,
				"Cancel": function() {
					sftp_dialog_element.dialog("close");
				}
			}
		});

		$('#ftpBrowserErrorContainer').css('display','none');
		$('#ftpBrowserFolderList').html('');
		$('#ak_crumbs').html('');

		sftp_dialog_element.dialog('open');

		if(empty(akeeba_sftpbrowser_directory)) akeeba_sftpbrowser_directory = '';

		var data = {
			'task'      : 'sftpbrowse',
			'json': JSON.stringify({
				'host'		: akeeba_sftpbrowser_host,
				'port'		: akeeba_sftpbrowser_port,
				'username'	: akeeba_sftpbrowser_username,
				'password'	: akeeba_sftpbrowser_password,
				'directory'	: akeeba_sftpbrowser_directory
			})
		};

		doAjax(
			data,
			function(data) {
				if(data.error != false) {
					// An error occured
					$('#ftpBrowserError').html(data.error);
					$('#ftpBrowserErrorContainer').css('display','block');
					$('#ftpBrowserFolderList').css('display','none');
					$('#ak_crumbs').css('display','none');
				} else {
					// Create the interface
					$('#ftpBrowserErrorContainer').css('display','none');

					// Display the crumbs
					if(!empty(data.breadcrumbs)) {
						$('#ak_crumbs').css('display','block');
						$('#ak_crumbs').html('');
						var relativePath = '/';

						akeeba_sftpbrowser_addcrumb(trans('UI-ROOT'), '/', callback);

						$.each(data.breadcrumbs, function(i, crumb) {
							relativePath += '/'+crumb;

							akeeba_sftpbrowser_addcrumb(crumb, relativePath, callback);
						});
					} else {
						$('#ftpBrowserCrumbs').css('display','none');
					}

					// Display the list of directories
					if(!empty(data.list)) {
						$('#ftpBrowserFolderList').css('display','block');

						$.each(data.list, function(i, item) {
							akeeba_sftpbrowser_create_link(akeeba_sftpbrowser_directory+'/'+item, item, $('#ftpBrowserFolderList'), callback );
						});
					} else {
						$('#ftpBrowserFolderList').css('display','none');
					}
				}
			},
			function(message) {
				$('#ftpBrowserError').html(message);
				$('#ftpBrowserErrorContainer').css('display','block');
				$('#ftpBrowserFolderList').css('display','none');
				$('#ftpBrowserCrumbs').css('display','none');
			}
		);
	};

	/**
	 * Creates a directory link for the SFTP browser UI
	 */
	function akeeba_sftpbrowser_create_link(path, label, container, callback)
	{
		var row = $(document.createElement('tr'));
		var cell = $(document.createElement('td')).appendTo(row);

		var myElement = $(document.createElement('a'))
			.text(label)
			.click(function(){
				akeeba_sftpbrowser_directory = resolvePath(path);
				akeeba_sftpbrowser_hook(callback);
			})
			.appendTo(cell);
		row.appendTo($(container));
	}

	/**
	 * Adds a breadcrumb to the SFTP browser
	 */
	function akeeba_sftpbrowser_addcrumb(crumb, relativePath, callback, last)
	{
		if(empty(last)) last = false;
		var li = $(document.createElement('li'));

		$(document.createElement('a'))
			.html(crumb)
			.click(function(e){
				akeeba_sftpbrowser_directory = relativePath;
				akeeba_sftpbrowser_hook(callback);
				e.preventDefault();
			})
			.appendTo(li);

		if(!last) {
			$(document.createElement('span'))
				.text('/')
				.addClass('divider')
				.appendTo(li);
		}

		li.appendTo('#ak_crumbs');
	}

	function onStartExtraction()
	{
		$('#page1').hide('fast');
		$('#page2').show('fast');

		$('#currentFile').text( '' );

		akeeba_error_callback = errorHandler;

		var data = {
			'task' : 'startExtracting',
			'json': JSON.stringify({
				'kickstart.setup.sourcefile':		$('#kickstart\\.setup\\.sourcefile').val(),
				'kickstart.jps.password':			$('#kickstart\\.jps\\.password').val(),
				'kickstart.tuning.min_exec_time':	$('#kickstart\\.tuning\\.min_exec_time').val(),
				'kickstart.tuning.max_exec_time':	$('#kickstart\\.tuning\\.max_exec_time').val(),
				'kickstart.stealth.enable': 		$('#kickstart\\.stealth\\.enable').is(':checked'),
				'kickstart.stealth.url': 			$('#kickstart\\.stealth\\.url').val(),
				'kickstart.tuning.run_time_bias':	75,
				'kickstart.setup.restoreperms':		0,
				'kickstart.setup.dryrun':			0,
				'kickstart.setup.ignoreerrors':		$('#kickstart\\.setup\\.ignoreerrors').is(':checked'),
				'kickstart.enabled':				1,
				'kickstart.security.password':		'',
				'kickstart.procengine':				$('#kickstart\\.procengine').val(),
				'kickstart.ftp.host':				$('#kickstart\\.ftp\\.host').val(),
				'kickstart.ftp.port':				$('#kickstart\\.ftp\\.port').val(),
				'kickstart.ftp.ssl':				$('#kickstart\\.ftp\\.ssl').is(':checked'),
				'kickstart.ftp.passive':			$('#kickstart\\.ftp\\.passive').is(':checked'),
				'kickstart.ftp.user':				$('#kickstart\\.ftp\\.user').val(),
				'kickstart.ftp.pass':				$('#kickstart\\.ftp\\.pass').val(),
				'kickstart.ftp.dir':				$('#kickstart\\.ftp\\.dir').val(),
				'kickstart.ftp.tempdir':			$('#kickstart\\.ftp\\.tempdir').val()
			})
		};
		doAjax(data, function(ret){
			processRestorationStep(ret);
		});
	}

	function processRestorationStep(data)
	{
		// Look for errors
		if(!data.status)
		{
			errorHandler(data.message);
			return;
		}

		// Propagate warnings to the GUI
		if( !empty(data.Warnings) )
		{
			$.each(data.Warnings, function(i, item){
				$('#warnings').append(
					$(document.createElement('div'))
						.html(item)
				);
				$('#warningsBox').show('fast');
			});
		}

		// Parse total size, if exists
		if(array_key_exists('totalsize', data))
		{
			if(is_array(data.filelist))
			{
				akeeba_restoration_stat_total = 0;
				$.each(data.filelist,function(i, item)
				{
					akeeba_restoration_stat_total += item[1];
				});
			}
			akeeba_restoration_stat_outbytes = 0;
			akeeba_restoration_stat_inbytes = 0;
			akeeba_restoration_stat_files = 0;
		}

		// Update GUI
		akeeba_restoration_stat_inbytes += data.bytesIn;
		akeeba_restoration_stat_outbytes += data.bytesOut;
		akeeba_restoration_stat_files += data.files;
		var percentage = 0;
		if( akeeba_restoration_stat_total > 0 )
		{
			percentage = 100 * akeeba_restoration_stat_inbytes / akeeba_restoration_stat_total;
			if(percentage < 0) {
				percentage = 0;
			} else if(percentage > 100) {
				percentage = 100;
			}
		}
		if(data.done) percentage = 100;
		setProgressBar(percentage);
		$('#currentFile').text( data.lastfile );

		if(!empty(data.factory)) akeeba_factory = data.factory;

		post = {
			'task'	: 'continueExtracting',
			'json'	: JSON.stringify({factory: akeeba_factory})
		};

		if(!data.done)
		{
			doAjax(post, function(ret){
				processRestorationStep(ret);
			});
		}
		else
		{
			$('#page2a').hide('fast');
			$('#extractionComplete').show('fast');

			$('#runInstaller').css('display','inline-block');
		}
	}

	function onGotoStartClick(event)
	{
		$('#page2').hide('fast');
		$('#error').hide('fast');
		$('#page1').show('fast');
	}

	function onRunInstallerClick(event)
	{
		var windowReference = window.open('installation/index.php','installer');
		if(!windowReference.opener) {
			windowReference.opener = this.window;
		}
		$('#runCleanup').css('display','inline-block');
		$('#runInstaller').hide('fast');
	}

	function onRunCleanupClick(event)
	{
		post = {
			'task'	: 'isJoomla',
			// Passing the factory preserves the renamed files array
			'json'	: JSON.stringify({factory: akeeba_factory})
		};

		doAjax(post, function(ret){
			isJoomla = ret;
			onRealRunCleanupClick();
		});
	}

	function onRealRunCleanupClick()
	{
		post = {
			'task'	: 'cleanUp',
			// Passing the factory preserves the renamed files array
			'json'	: JSON.stringify({factory: akeeba_factory})
		};

		doAjax(post, function(ret){
			$('#runCleanup').hide('fast');
			$('#gotoSite').css('display','inline-block');
			if (isJoomla)
			{
				$('#gotoAdministrator').css('display','inline-block');
			}
			else
			{
				$('#gotoAdministrator').css('display','none');
			}
			$('#gotoPostRestorationRroubleshooting').css('display','block');
		});

	}

	function errorHandler(msg)
	{
		$('#errorMessage').html(msg);
		$('#error').show('fast');
	}

	function onresetFTPTempDir(event)
	{
		$('#kickstart\\.ftp\\.tempdir').val('<?php 
    echo addcslashes(AKKickstartUtils::getPath(), '\\\'"');
    ?>
');
	}

	function onArchiveListReload()
	{
		post = {
			'task'	: 'listArchives',
			'json'	: JSON.stringify({path: $('#kickstart\\.setup\\.sourcepath').val()})
		}

		doAjax(post, function(ret){
			$('#sourcefileContainer').html(ret);
		});
	}

	/**
	 * Akeeba Kickstart Update Check
	 */

	var akeeba_update = {version: '0'};
	var akeeba_version = '4.0.0';

	function version_compare (v1, v2, operator) {
		// BEGIN REDUNDANT
		this.php_js = this.php_js || {};
		this.php_js.ENV = this.php_js.ENV || {};
		// END REDUNDANT
		// Important: compare must be initialized at 0.
		var i = 0,
			x = 0,
			compare = 0,
		// vm maps textual PHP versions to negatives so they're less than 0.
		// PHP currently defines these as CASE-SENSITIVE. It is important to
		// leave these as negatives so that they can come before numerical versions
		// and as if no letters were there to begin with.
		// (1alpha is < 1 and < 1.1 but > 1dev1)
		// If a non-numerical value can't be mapped to this table, it receives
		// -7 as its value.
			vm = {
				'dev': -6,
				'alpha': -5,
				'a': -5,
				'beta': -4,
				'b': -4,
				'RC': -3,
				'rc': -3,
				'#': -2,
				'p': -1,
				'pl': -1
			},
		// This function will be called to prepare each version argument.
		// It replaces every _, -, and + with a dot.
		// It surrounds any nonsequence of numbers/dots with dots.
		// It replaces sequences of dots with a single dot.
		//    version_compare('4..0', '4.0') == 0
		// Important: A string of 0 length needs to be converted into a value
		// even less than an unexisting value in vm (-7), hence [-8].
		// It's also important to not strip spaces because of this.
		//   version_compare('', ' ') == 1
			prepVersion = function (v) {
				v = ('' + v).replace(/[_\-+]/g, '.');
				v = v.replace(/([^.\d]+)/g, '.$1.').replace(/\.{2,}/g, '.');
				return (!v.length ? [-8] : v.split('.'));
			},
		// This converts a version component to a number.
		// Empty component becomes 0.
		// Non-numerical component becomes a negative number.
		// Numerical component becomes itself as an integer.
			numVersion = function (v) {
				return !v ? 0 : (isNaN(v) ? vm[v] || -7 : parseInt(v, 10));
			};
		v1 = prepVersion(v1);
		v2 = prepVersion(v2);
		x = Math.max(v1.length, v2.length);
		for (i = 0; i < x; i++) {
			if (v1[i] == v2[i]) {
				continue;
			}
			v1[i] = numVersion(v1[i]);
			v2[i] = numVersion(v2[i]);
			if (v1[i] < v2[i]) {
				compare = -1;
				break;
			} else if (v1[i] > v2[i]) {
				compare = 1;
				break;
			}
		}
		if (!operator) {
			return compare;
		}

		// Important: operator is CASE-SENSITIVE.
		// "No operator" seems to be treated as less than
		// Any other values seem to make the function return null.
		switch (operator) {
			case '>':
			case 'gt':
				return (compare > 0);
			case '>=':
			case 'ge':
				return (compare >= 0);
			case '<=':
			case 'le':
				return (compare <= 0);
			case '==':
			case '=':
			case 'eq':
				return (compare === 0);
			case '<>':
			case '!=':
			case 'ne':
				return (compare !== 0);
			case '':
			case '<':
			case 'lt':
				return (compare < 0);
			default:
				return null;
		}
	}

	function checkUpdates()
	{
		var structure =
		{
			type: "GET",
			url: 'http://query.yahooapis.com/v1/public/yql',
			data: {
				<?php 
    if (KICKSTARTPRO) {
        ?>
				q: 'SELECT * FROM xml WHERE url="http://nocdn.akeebabackup.com/updates/kickstart.xml"',
				<?php 
    } else {
        ?>
				q: 'SELECT * FROM xml WHERE url="http://nocdn.akeebabackup.com/updates/kickstartpro.xml"',
				<?php 
    }
    ?>
				format: 'json',
				callback: 'updatesCallback'
			},
			cache: true,
			crossDomain: true,
			jsonp: 'updatesCallback',
			timeout: 15000
		};
		$.ajax( structure );
	}

	function updatesCallback(msg)
	{
		$.each(msg.query.results.updates.update, function(i, el){
			var myUpdate = {
				'version'	: el.version,
				'infourl'	: el.infourl['content'],
				'dlurl'		: el.downloads.downloadurl.content
			}
			if(version_compare(myUpdate.version, akeeba_update.version, 'ge')) {
				akeeba_update = myUpdate;
			}
		});

		if(version_compare(akeeba_update.version, akeeba_version, 'gt')) {
			notifyAboutUpdates();
		}
	}

	function notifyAboutUpdates()
	{
		$('#update-version').text(akeeba_update.version);
		$('#update-dlnow').attr('href', akeeba_update.dlurl);
		$('#update-whatsnew').attr('href', akeeba_update.infourl);
		$('#update-notification').show('slow');
	}

	<?php 
    callExtraFeature('onExtraHeadJavascript');
    ?>
</script>
<?php 
}