diff --git a/bench/.buffalo-test.json b/bench/.buffalo-test.json index 171ff4c..80ec754 100644 --- a/bench/.buffalo-test.json +++ b/bench/.buffalo-test.json @@ -1,4 +1,762 @@ [ + { + "tag": "d041c060993957fa7721a818d905ca487124708b", + "timestamp": "2022-12-23T14:56:51.696Z", + "suites": { + "hyperscript": { + "Valyrian 5.0.8": { + "hz": 0, + "meanTime": 0, + "medianTime": 0, + "standardDeviation": 0, + "maxTime": 0, + "minTime": 0 + }, + "Valyrian next": { + "hz": 0, + "meanTime": 0, + "medianTime": 0, + "standardDeviation": 0, + "maxTime": 0, + "minTime": 0 + } + }, + "Set.has vs [].indexOf vs Object[key] vs key in Object": { + "Set.has": { + "hz": 0, + "meanTime": 0, + "medianTime": 0, + "standardDeviation": 0, + "maxTime": 0, + "minTime": 0 + }, + "[].indexOf": { + "hz": 0, + "meanTime": 0, + "medianTime": 0, + "standardDeviation": 0, + "maxTime": 0, + "minTime": 0 + }, + "Object[key]": { + "hz": 0, + "meanTime": 0, + "medianTime": 0, + "standardDeviation": 0, + "maxTime": 0, + "minTime": 0 + }, + "key in Object": { + "hz": 0, + "meanTime": 0, + "medianTime": 0, + "standardDeviation": 0, + "maxTime": 0, + "minTime": 0 + } + }, + "Function in Set vs Array": { + "Set.has": { + "hz": 0, + "meanTime": 0, + "medianTime": 0, + "standardDeviation": 0, + "maxTime": 0, + "minTime": 0 + }, + "[].indexOf": { + "hz": 0, + "meanTime": 0, + "medianTime": 0, + "standardDeviation": 0, + "maxTime": 0, + "minTime": 0 + } + }, + "Add function to set vs add function to array and cycle through them": { + "Set.add": { + "hz": 0, + "meanTime": 0, + "medianTime": 0, + "standardDeviation": 0, + "maxTime": 0, + "minTime": 0 + }, + "Array.push": { + "hz": 0, + "meanTime": 0, + "medianTime": 0, + "standardDeviation": 0, + "maxTime": 0, + "minTime": 0 + }, + "Set.add 2": { + "hz": 0, + "meanTime": 0, + "medianTime": 0, + "standardDeviation": 0, + "maxTime": 0, + "minTime": 0 + }, + "Array.push 2": { + "hz": 0, + "meanTime": 0, + "medianTime": 0, + "standardDeviation": 0, + "maxTime": 0, + "minTime": 0 + } + }, + "Object.keys for loop vs Object.keys for of vs for in": { + "Object.keys for loop": { + "hz": 0, + "meanTime": 0, + "medianTime": 0, + "standardDeviation": 0, + "maxTime": 0, + "minTime": 0 + }, + "Object.keys for of": { + "hz": 0, + "meanTime": 0, + "medianTime": 0, + "standardDeviation": 0, + "maxTime": 0, + "minTime": 0 + }, + "for in": { + "hz": 0, + "meanTime": 0, + "medianTime": 0, + "standardDeviation": 0, + "maxTime": 0, + "minTime": 0 + } + }, + "typeof function vs startsWith vs charAt vs string[0]": { + "typeof function": { + "hz": 0, + "meanTime": 0, + "medianTime": 0, + "standardDeviation": 0, + "maxTime": 0, + "minTime": 0 + }, + "startsWith": { + "hz": 0, + "meanTime": 0, + "medianTime": 0, + "standardDeviation": 0, + "maxTime": 0, + "minTime": 0 + }, + "charAt": { + "hz": 0, + "meanTime": 0, + "medianTime": 0, + "standardDeviation": 0, + "maxTime": 0, + "minTime": 0 + }, + "string[0]": { + "hz": 0, + "meanTime": 0, + "medianTime": 0, + "standardDeviation": 0, + "maxTime": 0, + "minTime": 0 + } + }, + "Array.isArray vs typeof object & Array.isArray": { + "Array.isArray": { + "hz": 0, + "meanTime": 0, + "medianTime": 0, + "standardDeviation": 0, + "maxTime": 0, + "minTime": 0 + }, + "typeof object & Array.isArray": { + "hz": 0, + "meanTime": 0, + "medianTime": 0, + "standardDeviation": 0, + "maxTime": 0, + "minTime": 0 + } + }, + "string comparison vs instance comparison vs property comparison": { + "string comparison": { + "hz": 0, + "meanTime": 0, + "medianTime": 0, + "standardDeviation": 0, + "maxTime": 0, + "minTime": 0 + }, + "instance comparison": { + "hz": 0, + "meanTime": 0, + "medianTime": 0, + "standardDeviation": 0, + "maxTime": 0, + "minTime": 0 + }, + "property comparison": { + "hz": 0, + "meanTime": 0, + "medianTime": 0, + "standardDeviation": 0, + "maxTime": 0, + "minTime": 0 + }, + "property string comparison": { + "hz": 0, + "meanTime": 0, + "medianTime": 0, + "standardDeviation": 0, + "maxTime": 0, + "minTime": 0 + }, + "typeof comparison": { + "hz": 0, + "meanTime": 0, + "medianTime": 0, + "standardDeviation": 0, + "maxTime": 0, + "minTime": 0 + } + }, + "For loop if/continue vs if/else": { + "for loop if/continue": { + "hz": 0, + "meanTime": 0, + "medianTime": 0, + "standardDeviation": 0, + "maxTime": 0, + "minTime": 0 + }, + "for loop if/else": { + "hz": 0, + "meanTime": 0, + "medianTime": 0, + "standardDeviation": 0, + "maxTime": 0, + "minTime": 0 + } + }, + "map array of strings vs reduce with object keys equals index": { + "map array of strings": { + "hz": 0, + "meanTime": 0, + "medianTime": 0, + "standardDeviation": 0, + "maxTime": 0, + "minTime": 0 + }, + "reduce with object keys equals index": { + "hz": 0, + "meanTime": 0, + "medianTime": 0, + "standardDeviation": 0, + "maxTime": 0, + "minTime": 0 + }, + "array by for": { + "hz": 0, + "meanTime": 0, + "medianTime": 0, + "standardDeviation": 0, + "maxTime": 0, + "minTime": 0 + }, + "object by for": { + "hz": 0, + "meanTime": 0, + "medianTime": 0, + "standardDeviation": 0, + "maxTime": 0, + "minTime": 0 + }, + "object map by for": { + "hz": 0, + "meanTime": 0, + "medianTime": 0, + "standardDeviation": 0, + "maxTime": 0, + "minTime": 0 + } + }, + "Symbol access vs direct access": { + "Direct access access": { + "hz": 0, + "meanTime": 0, + "medianTime": 0, + "standardDeviation": 0, + "maxTime": 0, + "minTime": 0 + }, + "Symbol access access": { + "hz": 0, + "meanTime": 0, + "medianTime": 0, + "standardDeviation": 0, + "maxTime": 0, + "minTime": 0 + } + }, + "Object.assign vs spreed": { + "Object.assign": { + "hz": 0, + "meanTime": 0, + "medianTime": 0, + "standardDeviation": 0, + "maxTime": 0, + "minTime": 0 + }, + "spreed": { + "hz": 0, + "meanTime": 0, + "medianTime": 0, + "standardDeviation": 0, + "maxTime": 0, + "minTime": 0 + } + }, + "Mount and update: Mount multiple types": { + "vOld": { + "hz": 0, + "meanTime": 0, + "medianTime": 0, + "standardDeviation": 0, + "maxTime": 0, + "minTime": 0 + }, + "v": { + "hz": 0, + "meanTime": 0, + "medianTime": 0, + "standardDeviation": 0, + "maxTime": 0, + "minTime": 0 + } + }, + "Mount and update: Mount single text": { + "vOld": { + "hz": 0, + "meanTime": 0, + "medianTime": 0, + "standardDeviation": 0, + "maxTime": 0, + "minTime": 0 + }, + "v": { + "hz": 0, + "meanTime": 0, + "medianTime": 0, + "standardDeviation": 0, + "maxTime": 0, + "minTime": 0 + } + }, + "Mount and update: Mount single text in div": { + "vOld": { + "hz": 0, + "meanTime": 0, + "medianTime": 0, + "standardDeviation": 0, + "maxTime": 0, + "minTime": 0 + }, + "v": { + "hz": 0, + "meanTime": 0, + "medianTime": 0, + "standardDeviation": 0, + "maxTime": 0, + "minTime": 0 + } + }, + "Mount and update: Update multiple types": { + "vOld": { + "hz": 0, + "meanTime": 0, + "medianTime": 0, + "standardDeviation": 0, + "maxTime": 0, + "minTime": 0 + }, + "v": { + "hz": 0, + "meanTime": 0, + "medianTime": 0, + "standardDeviation": 0, + "maxTime": 0, + "minTime": 0 + } + }, + "Mount and update: Update single text": { + "vOld": { + "hz": 0, + "meanTime": 0, + "medianTime": 0, + "standardDeviation": 0, + "maxTime": 0, + "minTime": 0 + }, + "v": { + "hz": 0, + "meanTime": 0, + "medianTime": 0, + "standardDeviation": 0, + "maxTime": 0, + "minTime": 0 + } + }, + "Mount and update: Render list": { + "vOld": { + "hz": 0, + "meanTime": 0, + "medianTime": 0, + "standardDeviation": 0, + "maxTime": 0, + "minTime": 0 + }, + "v": { + "hz": 0, + "meanTime": 0, + "medianTime": 0, + "standardDeviation": 0, + "maxTime": 0, + "minTime": 0 + } + }, + "Mount and update: Render keyed list": { + "vOld": { + "hz": 0, + "meanTime": 0, + "medianTime": 0, + "standardDeviation": 0, + "maxTime": 0, + "minTime": 0 + }, + "v": { + "hz": 0, + "meanTime": 0, + "medianTime": 0, + "standardDeviation": 0, + "maxTime": 0, + "minTime": 0 + } + }, + "Mount and update: Render keyed list -> stress": { + "vOld": { + "hz": 0, + "meanTime": 0, + "medianTime": 0, + "standardDeviation": 0, + "maxTime": 0, + "minTime": 0 + }, + "v": { + "hz": 0, + "meanTime": 0, + "medianTime": 0, + "standardDeviation": 0, + "maxTime": 0, + "minTime": 0 + } + }, + "Mount and update: Render keyed list -> swap keys on large set": { + "vOld": { + "hz": 0, + "meanTime": 0, + "medianTime": 0, + "standardDeviation": 0, + "maxTime": 0, + "minTime": 0 + }, + "v": { + "hz": 0, + "meanTime": 0, + "medianTime": 0, + "standardDeviation": 0, + "maxTime": 0, + "minTime": 0 + } + }, + "Mount and update: Update class": { + "vOld update": { + "hz": 0, + "meanTime": 0, + "medianTime": 0, + "standardDeviation": 0, + "maxTime": 0, + "minTime": 0 + }, + "v update": { + "hz": 0, + "meanTime": 0, + "medianTime": 0, + "standardDeviation": 0, + "maxTime": 0, + "minTime": 0 + } + }, + "Mount and update: Update class with hooks vs v-keep": { + "shouldupdate property": { + "hz": 0, + "meanTime": 0, + "medianTime": 0, + "standardDeviation": 0, + "maxTime": 0, + "minTime": 0 + }, + "useMemo hook": { + "hz": 0, + "meanTime": 0, + "medianTime": 0, + "standardDeviation": 0, + "maxTime": 0, + "minTime": 0 + } + }, + "Lifecycle vs hooks": { + "Hooks": { + "hz": 0, + "meanTime": 0, + "medianTime": 0, + "standardDeviation": 0, + "maxTime": 0, + "minTime": 0 + }, + "Lifecycle": { + "hz": 0, + "meanTime": 0, + "medianTime": 0, + "standardDeviation": 0, + "maxTime": 0, + "minTime": 0 + } + }, + "(No memo) Mount and update: Mount multiple types": { + "vOld": { + "hz": 0, + "meanTime": 0, + "medianTime": 0, + "standardDeviation": 0, + "maxTime": 0, + "minTime": 0 + }, + "v": { + "hz": 0, + "meanTime": 0, + "medianTime": 0, + "standardDeviation": 0, + "maxTime": 0, + "minTime": 0 + } + }, + "(No memo) Mount and update: Mount single text": { + "vOld": { + "hz": 0, + "meanTime": 0, + "medianTime": 0, + "standardDeviation": 0, + "maxTime": 0, + "minTime": 0 + }, + "v": { + "hz": 0, + "meanTime": 0, + "medianTime": 0, + "standardDeviation": 0, + "maxTime": 0, + "minTime": 0 + } + }, + "(No memo) Mount and update: Mount single text in div": { + "vOld": { + "hz": 0, + "meanTime": 0, + "medianTime": 0, + "standardDeviation": 0, + "maxTime": 0, + "minTime": 0 + }, + "v": { + "hz": 0, + "meanTime": 0, + "medianTime": 0, + "standardDeviation": 0, + "maxTime": 0, + "minTime": 0 + } + }, + "(No memo) Mount and update: Update multiple types": { + "vOld": { + "hz": 0, + "meanTime": 0, + "medianTime": 0, + "standardDeviation": 0, + "maxTime": 0, + "minTime": 0 + }, + "v": { + "hz": 0, + "meanTime": 0, + "medianTime": 0, + "standardDeviation": 0, + "maxTime": 0, + "minTime": 0 + } + }, + "(No memo) Mount and update: Update single text": { + "vOld": { + "hz": 0, + "meanTime": 0, + "medianTime": 0, + "standardDeviation": 0, + "maxTime": 0, + "minTime": 0 + }, + "v": { + "hz": 0, + "meanTime": 0, + "medianTime": 0, + "standardDeviation": 0, + "maxTime": 0, + "minTime": 0 + } + }, + "(No memo) Mount and update: Render list": { + "vOld": { + "hz": 0, + "meanTime": 0, + "medianTime": 0, + "standardDeviation": 0, + "maxTime": 0, + "minTime": 0 + }, + "v": { + "hz": 0, + "meanTime": 0, + "medianTime": 0, + "standardDeviation": 0, + "maxTime": 0, + "minTime": 0 + } + }, + "(No memo) Mount and update: Render keyed list": { + "vOld": { + "hz": 0, + "meanTime": 0, + "medianTime": 0, + "standardDeviation": 0, + "maxTime": 0, + "minTime": 0 + }, + "v": { + "hz": 0, + "meanTime": 0, + "medianTime": 0, + "standardDeviation": 0, + "maxTime": 0, + "minTime": 0 + } + }, + "(No memo) Mount and update: Render keyed list -> stress": { + "vOld": { + "hz": 0, + "meanTime": 0, + "medianTime": 0, + "standardDeviation": 0, + "maxTime": 0, + "minTime": 0 + }, + "v": { + "hz": 0, + "meanTime": 0, + "medianTime": 0, + "standardDeviation": 0, + "maxTime": 0, + "minTime": 0 + } + }, + "(No memo) Mount and update: Render keyed list -> swap keys on large set": { + "vOld": { + "hz": 0, + "meanTime": 0, + "medianTime": 0, + "standardDeviation": 0, + "maxTime": 0, + "minTime": 0 + }, + "v": { + "hz": 0, + "meanTime": 0, + "medianTime": 0, + "standardDeviation": 0, + "maxTime": 0, + "minTime": 0 + } + }, + "(No memo) Mount and update: Update class": { + "vOld update": { + "hz": 0, + "meanTime": 0, + "medianTime": 0, + "standardDeviation": 0, + "maxTime": 0, + "minTime": 0 + }, + "v update": { + "hz": 0, + "meanTime": 0, + "medianTime": 0, + "standardDeviation": 0, + "maxTime": 0, + "minTime": 0 + } + }, + "(No memo) Mount and update: Update class with hooks vs v-keep": { + "shouldupdate property": { + "hz": 0, + "meanTime": 0, + "medianTime": 0, + "standardDeviation": 0, + "maxTime": 0, + "minTime": 0 + }, + "useMemo hook": { + "hz": 0, + "meanTime": 0, + "medianTime": 0, + "standardDeviation": 0, + "maxTime": 0, + "minTime": 0 + } + }, + "(No memo) Lifecycle vs hooks": { + "Hooks": { + "hz": 0, + "meanTime": 0, + "medianTime": 0, + "standardDeviation": 0, + "maxTime": 0, + "minTime": 0 + }, + "Lifecycle": { + "hz": 0, + "meanTime": 0, + "medianTime": 0, + "standardDeviation": 0, + "maxTime": 0, + "minTime": 0 + } + } + } + }, { "tag": "adb6b0549b64dbd7d7f3528b23fb14d47b160724", "timestamp": "2022-10-22T06:36:19.766Z", @@ -503,7 +1261,7 @@ "minTime": 0 } }, - "Mount and update: Update class with hooks vs shouldupdate property": { + "Mount and update: Update class with hooks vs v-keep": { "shouldupdate property": { "hz": 0, "meanTime": 0, @@ -719,7 +1477,7 @@ "minTime": 0 } }, - "(No memo) Mount and update: Update class with hooks vs shouldupdate property": { + "(No memo) Mount and update: Update class with hooks vs v-keep": { "shouldupdate property": { "hz": 0, "meanTime": 0, @@ -1217,7 +1975,7 @@ "minTime": 0.1939620077610016 } }, - "Mount and update: Update class with hooks vs shouldupdate property": { + "Mount and update: Update class with hooks vs v-keep": { "shouldupdate property": { "hz": 80.46503429510979, "meanTime": 12.427758327082133, @@ -1433,7 +2191,7 @@ "minTime": 0.20003999769687653 } }, - "(No memo) Mount and update: Update class with hooks vs shouldupdate property": { + "(No memo) Mount and update: Update class with hooks vs v-keep": { "shouldupdate property": { "hz": 82.86565618578464, "meanTime": 12.06772559379729, @@ -1931,7 +2689,7 @@ "minTime": 0 } }, - "Mount and update: Update class with hooks vs shouldupdate property": { + "Mount and update: Update class with hooks vs v-keep": { "shouldupdate property": { "hz": 0, "meanTime": 0, @@ -2147,7 +2905,7 @@ "minTime": 0 } }, - "(No memo) Mount and update: Update class with hooks vs shouldupdate property": { + "(No memo) Mount and update: Update class with hooks vs v-keep": { "shouldupdate property": { "hz": 0, "meanTime": 0, @@ -2645,7 +3403,7 @@ "minTime": 0 } }, - "Mount and update: Update class with hooks vs shouldupdate property": { + "Mount and update: Update class with hooks vs v-keep": { "shouldupdate property": { "hz": 0, "meanTime": 0, @@ -2861,7 +3619,7 @@ "minTime": 0 } }, - "(No memo) Mount and update: Update class with hooks vs shouldupdate property": { + "(No memo) Mount and update: Update class with hooks vs v-keep": { "shouldupdate property": { "hz": 0, "meanTime": 0, @@ -3359,7 +4117,7 @@ "minTime": 0 } }, - "Mount and update: Update class with hooks vs shouldupdate property": { + "Mount and update: Update class with hooks vs v-keep": { "shouldupdate property": { "hz": 0, "meanTime": 0, @@ -3575,7 +4333,7 @@ "minTime": 0 } }, - "(No memo) Mount and update: Update class with hooks vs shouldupdate property": { + "(No memo) Mount and update: Update class with hooks vs v-keep": { "shouldupdate property": { "hz": 0, "meanTime": 0, @@ -4073,7 +4831,7 @@ "minTime": 0 } }, - "Mount and update: Update class with hooks vs shouldupdate property": { + "Mount and update: Update class with hooks vs v-keep": { "shouldupdate property": { "hz": 0, "meanTime": 0, @@ -4289,7 +5047,7 @@ "minTime": 0 } }, - "(No memo) Mount and update: Update class with hooks vs shouldupdate property": { + "(No memo) Mount and update: Update class with hooks vs v-keep": { "shouldupdate property": { "hz": 0, "meanTime": 0, @@ -4787,7 +5545,7 @@ "minTime": 0 } }, - "Mount and update: Update class with hooks vs shouldupdate property": { + "Mount and update: Update class with hooks vs v-keep": { "shouldupdate property": { "hz": 0, "meanTime": 0, @@ -5003,7 +5761,7 @@ "minTime": 0 } }, - "(No memo) Mount and update: Update class with hooks vs shouldupdate property": { + "(No memo) Mount and update: Update class with hooks vs v-keep": { "shouldupdate property": { "hz": 0, "meanTime": 0, @@ -5501,7 +6259,7 @@ "minTime": 0 } }, - "Mount and update: Update class with hooks vs shouldupdate property": { + "Mount and update: Update class with hooks vs v-keep": { "shouldupdate property": { "hz": 0, "meanTime": 0, @@ -5717,7 +6475,7 @@ "minTime": 0 } }, - "(No memo) Mount and update: Update class with hooks vs shouldupdate property": { + "(No memo) Mount and update: Update class with hooks vs v-keep": { "shouldupdate property": { "hz": 0, "meanTime": 0, @@ -6215,7 +6973,7 @@ "minTime": 0 } }, - "Mount and update: Update class with hooks vs shouldupdate property": { + "Mount and update: Update class with hooks vs v-keep": { "shouldupdate property": { "hz": 0, "meanTime": 0, @@ -6431,7 +7189,7 @@ "minTime": 0 } }, - "(No memo) Mount and update: Update class with hooks vs shouldupdate property": { + "(No memo) Mount and update: Update class with hooks vs v-keep": { "shouldupdate property": { "hz": 0, "meanTime": 0, @@ -6929,7 +7687,7 @@ "minTime": 0 } }, - "Mount and update: Update class with hooks vs shouldupdate property": { + "Mount and update: Update class with hooks vs v-keep": { "shouldupdate property": { "hz": 0, "meanTime": 0, @@ -7145,7 +7903,7 @@ "minTime": 0 } }, - "(No memo) Mount and update: Update class with hooks vs shouldupdate property": { + "(No memo) Mount and update: Update class with hooks vs v-keep": { "shouldupdate property": { "hz": 0, "meanTime": 0, @@ -7643,7 +8401,7 @@ "minTime": 0 } }, - "Mount and update: Update class with hooks vs shouldupdate property": { + "Mount and update: Update class with hooks vs v-keep": { "shouldupdate property": { "hz": 0, "meanTime": 0, @@ -7859,7 +8617,7 @@ "minTime": 0 } }, - "(No memo) Mount and update: Update class with hooks vs shouldupdate property": { + "(No memo) Mount and update: Update class with hooks vs v-keep": { "shouldupdate property": { "hz": 0, "meanTime": 0, @@ -8357,7 +9115,7 @@ "minTime": 0.24100999999791384 } }, - "Mount and update: Update class with hooks vs shouldupdate property": { + "Mount and update: Update class with hooks vs v-keep": { "shouldupdate property": { "hz": 75.56214368584229, "meanTime": 13.234140155652641, @@ -8573,7 +9331,7 @@ "minTime": 0.25677600037306547 } }, - "(No memo) Mount and update: Update class with hooks vs shouldupdate property": { + "(No memo) Mount and update: Update class with hooks vs v-keep": { "shouldupdate property": { "hz": 75.3227963068509, "meanTime": 13.276193251325774, @@ -9071,7 +9829,7 @@ "minTime": 0 } }, - "Mount and update: Update class with hooks vs shouldupdate property": { + "Mount and update: Update class with hooks vs v-keep": { "shouldupdate property": { "hz": 0, "meanTime": 0, @@ -9287,7 +10045,7 @@ "minTime": 0 } }, - "(No memo) Mount and update: Update class with hooks vs shouldupdate property": { + "(No memo) Mount and update: Update class with hooks vs v-keep": { "shouldupdate property": { "hz": 0, "meanTime": 0, @@ -9785,7 +10543,7 @@ "minTime": 0 } }, - "Mount and update: Update class with hooks vs shouldupdate property": { + "Mount and update: Update class with hooks vs v-keep": { "shouldupdate property": { "hz": 0, "meanTime": 0, @@ -10283,7 +11041,7 @@ "minTime": 0 } }, - "Mount and update: Update class with hooks vs shouldupdate property": { + "Mount and update: Update class with hooks vs v-keep": { "shouldupdate property": { "hz": 0, "meanTime": 0, @@ -10781,7 +11539,7 @@ "minTime": 0 } }, - "Mount and update: Update class with hooks vs shouldupdate property": { + "Mount and update: Update class with hooks vs v-keep": { "shouldupdate property": { "hz": 0, "meanTime": 0, @@ -11279,7 +12037,7 @@ "minTime": 0 } }, - "Mount and update: Update class with hooks vs shouldupdate property": { + "Mount and update: Update class with hooks vs v-keep": { "shouldupdate property": { "hz": 0, "meanTime": 0, @@ -11777,7 +12535,7 @@ "minTime": 0 } }, - "Mount and update: Update class with hooks vs shouldupdate property": { + "Mount and update: Update class with hooks vs v-keep": { "shouldupdate property": { "hz": 0, "meanTime": 0, @@ -12275,7 +13033,7 @@ "minTime": 0 } }, - "Mount and update: Update class with hooks vs shouldupdate property": { + "Mount and update: Update class with hooks vs v-keep": { "shouldupdate property": { "hz": 0, "meanTime": 0, @@ -12773,7 +13531,7 @@ "minTime": 0 } }, - "Mount and update: Update class with hooks vs shouldupdate property": { + "Mount and update: Update class with hooks vs v-keep": { "shouldupdate property": { "hz": 2739.5849699635282, "meanTime": 0.3650187933442024, diff --git a/bench/mount_n_update_bench.js b/bench/mount_n_update_bench.js index c8b1257..eed498a 100644 --- a/bench/mount_n_update_bench.js +++ b/bench/mount_n_update_bench.js @@ -779,15 +779,12 @@ compare("Mount and update: Update class", () => { }); }); -compare("Mount and update: Update class with hooks vs shouldupdate property", () => { +compare("Mount and update: Update class with hooks vs v-keep", () => { let updateClass2 = ""; let Component = () => (
{ -
vnode.props.class !== oldVnode.props.class} - > +
test
} diff --git a/bench/mount_n_update_no_memo_bench.js b/bench/mount_n_update_no_memo_bench.js index 906cdba..1550933 100644 --- a/bench/mount_n_update_no_memo_bench.js +++ b/bench/mount_n_update_no_memo_bench.js @@ -804,15 +804,12 @@ compare("(No memo) Mount and update: Update class", () => { }); }); -compare("(No memo) Mount and update: Update class with hooks vs shouldupdate property", () => { +compare.only("(No memo) Mount and update: Update class with hooks vs v-keep", () => { let updateClass2 = ""; let Component = () => (
{ -
vnode.props.class !== oldVnode.props.class} - > +
test
} diff --git a/dist/index.js b/dist/index.js index 2659d69..e7179d3 100644 --- a/dist/index.js +++ b/dist/index.js @@ -40,6 +40,7 @@ __export(lib_exports, { unmount: () => unmount, update: () => update, updateAttributes: () => updateAttributes, + updateVnode: () => updateVnode, v: () => v }); module.exports = __toCommonJS(lib_exports); @@ -307,11 +308,12 @@ function directive(name, directive2) { } function sharedSetAttribute(name, value, newVnode, oldVnode) { if (typeof value === "function") { - if (name in eventListenerNames === false) { - mainVnode.dom.addEventListener(name.slice(2), eventListener); - eventListenerNames[name] = true; + let lowercaseName = name.toLowerCase(); + if (lowercaseName in eventListenerNames === false) { + mainVnode.dom.addEventListener(lowercaseName.slice(2), eventListener); + eventListenerNames[lowercaseName] = true; } - newVnode.dom[`v-${name}`] = value; + newVnode.dom[`v-${lowercaseName}`] = value; return; } if (name in newVnode.dom && newVnode.isSVG === false) { @@ -410,41 +412,18 @@ function patch(newVnode, oldVnode) { for (let i = 0; i < newTree.length; i++) { let newChild = newTree[i]; if (newChild instanceof Vnode && newChild.tag !== textTag) { - if (typeof newChild.tag !== "string") { - current.component = newChild.tag; - newTree.splice( - i--, - 1, - ("view" in newChild.tag ? newChild.tag.view.bind(newChild.tag) : newChild.tag.bind(newChild.tag))( - newChild.props, - ...newChild.children - ) - ); + if (typeof newChild.tag === "string") { continue; } - newChild.isSVG = newVnode.isSVG || newChild.tag === "svg"; - if (i < oldTreeLength) { - let oldChild = oldTree[i]; - if (newChild.tag === oldChild.tag) { - newChild.dom = oldChild.dom; - if ("v-keep" in newChild.props && newChild.props["v-keep"] === oldChild.props["v-keep"]) { - newChild.children = oldChild.children; - continue; - } - updateAttributes(newChild, oldChild); - patch(newChild, oldChild); - continue; - } - newChild.dom = createDomElement(newChild.tag, newChild.isSVG); - updateAttributes(newChild); - newVnode.dom.replaceChild(newChild.dom, oldChild.dom); - patch(newChild); - continue; - } - newChild.dom = createDomElement(newChild.tag, newChild.isSVG); - updateAttributes(newChild); - newVnode.dom.appendChild(newChild.dom); - patch(newChild); + current.component = newChild.tag; + newTree.splice( + i--, + 1, + ("view" in newChild.tag ? newChild.tag.view.bind(newChild.tag) : newChild.tag.bind(newChild.tag))( + newChild.props, + ...newChild.children + ) + ); continue; } if (Array.isArray(newChild)) { @@ -455,26 +434,55 @@ function patch(newVnode, oldVnode) { newTree.splice(i--, 1); continue; } - newTree[i] = new Vnode(textTag, {}, []); if (newChild instanceof Vnode) { - newTree[i].dom = newChild.dom; - newChild = newChild.dom.textContent; + newChild.children[0] = newChild.dom.textContent; + continue; } + newChild = newTree[i] = new Vnode(textTag, {}, [newChild]); + } + for (let i = 0; i < newTree.length; i++) { + let newChild = newTree[i]; + if (newChild.tag === textTag) { + if (i < oldTreeLength) { + let oldChild = oldTree[i]; + if (oldChild.tag === textTag) { + newChild.dom = oldChild.dom; + if (newChild.children[0] != oldChild.dom.textContent) { + oldChild.dom.textContent = newChild.children[0]; + } + continue; + } + newChild.dom = document.createTextNode(newChild.children[0]); + newVnode.dom.replaceChild(newChild.dom, oldChild.dom); + continue; + } + newChild.dom = document.createTextNode(newChild.children[0]); + newVnode.dom.appendChild(newChild.dom); + continue; + } + newChild.isSVG = newVnode.isSVG || newChild.tag === "svg"; if (i < oldTreeLength) { let oldChild = oldTree[i]; - if (oldChild.tag === textTag) { - newTree[i].dom = oldChild.dom; - if (newChild != oldChild.dom.textContent) { - oldChild.dom.textContent = newChild; + if (newChild.tag === oldChild.tag) { + newChild.dom = oldChild.dom; + if ("v-keep" in newChild.props && newChild.props["v-keep"] === oldChild.props["v-keep"]) { + newChild.children = oldChild.children; + continue; } + updateAttributes(newChild, oldChild); + patch(newChild, oldChild); continue; } - newTree[i].dom = document.createTextNode(newChild); - newVnode.dom.replaceChild(newTree[i].dom, oldChild.dom); + newChild.dom = createDomElement(newChild.tag, newChild.isSVG); + updateAttributes(newChild); + newVnode.dom.replaceChild(newChild.dom, oldChild.dom); + patch(newChild); continue; } - newTree[i].dom = document.createTextNode(newChild); - newVnode.dom.appendChild(newTree[i].dom); + newChild.dom = createDomElement(newChild.tag, newChild.isSVG); + updateAttributes(newChild); + newVnode.dom.appendChild(newChild.dom); + patch(newChild); } for (let i = newTree.length; i < oldTreeLength; i++) { newVnode.dom.removeChild(oldTree[i].dom); @@ -498,6 +506,21 @@ function update() { } } } +function updateVnode(vnode, oldVnode) { + callSet(onCleanupSet); + patch(vnode, oldVnode); + oldVnode.tag = vnode.tag; + oldVnode.props = { ...vnode.props }; + oldVnode.children = [...vnode.children]; + callSet(isMounted ? onUpdateSet : onMountSet); + isMounted = true; + current.vnode = null; + current.oldVnode = null; + current.component = null; + if (isNodeJs) { + return vnode.dom.innerHTML; + } +} function unmount() { if (mainVnode) { mainComponent = new Vnode(() => null, {}, []); diff --git a/dist/index.min.js b/dist/index.min.js index dff57de..d76f544 100644 --- a/dist/index.min.js +++ b/dist/index.min.js @@ -1 +1 @@ -(()=>{var e="#text",t=Boolean("undefined"!=typeof process&&process.versions&&process.versions.node);function o(e,t=!1){return t?document.createElementNS("http://www.w3.org/2000/svg",e):document.createElement(e)}var n=function(e,t,o){this.tag=e,this.props=t,this.children=o};function l(e){return e&&("function"==typeof e||"object"==typeof e&&"view"in e)}var i=e=>e instanceof n,d=e=>i(e)&&l(e.tag);function r(t){let o=[];for(let l=0,i=t.childNodes.length;lr(e))}var s=null,a=null,c=!1,u={vnode:null,oldVnode:null,component:null},m={key:!0,state:!0,"v-keep":!0,"v-if":!0,"v-unless":!0,"v-for":!0,"v-show":!0,"v-class":!0,"v-html":!0,"v-model":!0,"v-create":!0,"v-update":!0,"v-cleanup":!0},f=new Set,v=new Set,h=new Set,g=new Set;function y(e){f.add(e)}function w(e){for(let t of e)t();e.clear()}var k={};function C(e){let t=e.target,o=`v-on${e.type}`;for(;t;){if(t[o])return t[o](e,t),void(e.defaultPrevented||A());t=t.parentNode}}var N=e=>(t,o,n)=>{if(e?t:!t){let e=document.createTextNode("");return n&&n.dom&&n.dom.parentNode&&n.dom.parentNode.replaceChild(e,n.dom),o.tag="#text",o.children=[],o.props={},o.dom=e,!1}},V={"v-if":N(!1),"v-unless":N(!0),"v-for":(e,t)=>{let o=[],n=t.children[0];for(let t=0,l=e.length;t{t.dom.style.display=e?"":"none"},"v-class":(e,t)=>{for(let o in e)t.dom.classList.toggle(o,e[o])},"v-html":(e,t)=>{t.children=[p(e)]},"v-model":([e,t,o],n,l)=>{let i,d=o=>e[t]=o.target.value;if("input"===n.tag)switch(o=o||"oninput",n.props.type){case"checkbox":Array.isArray(e[t])?(d=o=>{let n=o.target.value,l=e[t].indexOf(n);-1===l?e[t].push(n):e[t].splice(l,1)},i=-1!==e[t].indexOf(n.dom.value)):"value"in n.props?(d=()=>{e[t]===n.props.value?e[t]=null:e[t]=n.props.value},i=e[t]===n.props.value):(d=()=>e[t]=!e[t],i=e[t]),x("checked",i,n);break;case"radio":x("checked",e[t]===n.dom.value,n);break;default:x("value",e[t],n)}else"select"===n.tag?(o=o||"onclick",n.props.multiple?(d=o=>{let n=o.target.value;if(o.ctrlKey){let o=e[t].indexOf(n);-1===o?e[t].push(n):e[t].splice(o,1)}else e[t].splice(0,e[t].length),e[t].push(n)},n.children.forEach(o=>{if("option"===o.tag){let n="value"in o.props?o.props.value:o.children.join("").trim();o.props.selected=-1!==e[t].indexOf(n)}})):n.children.forEach(o=>{if("option"===o.tag){let n="value"in o.props?o.props.value:o.children.join("").trim();o.props.selected=n===e[t]}})):"textarea"===n.tag&&(o=o||"oninput",n.children=[e[t]]);let r=n.props[o];x(o,e=>{d(e),r&&r(e)},n,l)},"v-create":(e,t,o)=>{if(!o){let o=e(t);"function"==typeof o&&y(o)}},"v-update":(e,t,o)=>{if(o){let n=e(t,o);"function"==typeof n&&y(n)}},"v-cleanup":(e,t,o)=>{y(()=>e(t,o))}};function x(e,t,o,n){if("function"==typeof t)return e in k==!1&&(a.dom.addEventListener(e.slice(2),C),k[e]=!0),void(o.dom[`v-${e}`]=t);e in o.dom&&!1===o.isSVG?o.dom[e]!=t&&(o.dom[e]=t):n&&t===n.props[e]||(!1===t?o.dom.removeAttribute(e):o.dom.setAttribute(e,t))}function S(e,t){if(t)for(let o in t.props)o in e.props==!1&&o in k==!1&&o in m==!1&&(o in e.dom&&!1===e.isSVG?e.dom[o]=null:e.dom.removeAttribute(o));for(let o in e.props)if(o in m){if(o in V&&!1===V[o](e.props[o],e,t))break}else x(o,e.props[o],e,t)}function b(t,l){let i=t.children,d=l?.children||[],r=d.length;if(r&&i[0]instanceof n&&"key"in i[0].props&&"key"in d[0].props){let e=i.length,n={};for(let e=0;enull,{},[]);let e=A();w(g);for(let e in k)a.dom.removeEventListener(e.slice(2).toLowerCase(),C),Reflect.deleteProperty(k,e);return s=null,a=null,c=!1,u.vnode=null,u.oldVnode=null,u.component=null,e}}var E=(e,t={},...o)=>new n(e,t||{},o);E.fragment=(e,...t)=>t;var L={Vnode:n,createDomElement:o,current:u,directive:function(e,t){let o=`v-${e}`;V[o]=t,m[o]=!0},directives:V,isComponent:l,isNodeJs:t,isVnode:i,isVnodeComponent:d,mount:function(e,i){let p="string"==typeof e?t?o(e,"svg"===e):document.querySelectorAll(e)[0]:e,c=d(i)?i:l(i)?new n(i,{},[]):new n(()=>i,{},[]);return s&&s.tag!==c.tag&&G(),s=c,a=r(p),A()},onCleanup:y,onMount:function(e){c||v.add(e)},onUnmount:function(e){c||g.add(e)},onUpdate:function(e){h.add(e)},patch:b,reservedProps:m,setAttribute:function(e,t,o,n){e in m||(o.props[e]=t,x(e,t,o,n))},trust:p,unmount:G,update:A,updateAttributes:S,v:E};"undefined"!=typeof module?module.exports=L:self.Valyrian=L})();//# sourceMappingURL=index.min.js.map \ No newline at end of file +(()=>{var e="#text",t=Boolean("undefined"!=typeof process&&process.versions&&process.versions.node);function o(e,t=!1){return t?document.createElementNS("http://www.w3.org/2000/svg",e):document.createElement(e)}var n=function(e,t,o){this.tag=e,this.props=t,this.children=o};function l(e){return e&&("function"==typeof e||"object"==typeof e&&"view"in e)}var i=e=>e instanceof n,d=e=>i(e)&&l(e.tag);function r(t){let o=[];for(let l=0,i=t.childNodes.length;lr(e))}var s=null,a=null,c=!1,u={vnode:null,oldVnode:null,component:null},m={key:!0,state:!0,"v-keep":!0,"v-if":!0,"v-unless":!0,"v-for":!0,"v-show":!0,"v-class":!0,"v-html":!0,"v-model":!0,"v-create":!0,"v-update":!0,"v-cleanup":!0},f=new Set,v=new Set,h=new Set,g=new Set;function y(e){f.add(e)}function w(e){for(let t of e)t();e.clear()}var k={};function V(e){let t=e.target,o=`v-on${e.type}`;for(;t;){if(t[o])return t[o](e,t),void(e.defaultPrevented||A());t=t.parentNode}}var C=e=>(t,o,n)=>{if(e?t:!t){let e=document.createTextNode("");return n&&n.dom&&n.dom.parentNode&&n.dom.parentNode.replaceChild(e,n.dom),o.tag="#text",o.children=[],o.props={},o.dom=e,!1}},N={"v-if":C(!1),"v-unless":C(!0),"v-for":(e,t)=>{let o=[],n=t.children[0];for(let t=0,l=e.length;t{t.dom.style.display=e?"":"none"},"v-class":(e,t)=>{for(let o in e)t.dom.classList.toggle(o,e[o])},"v-html":(e,t)=>{t.children=[p(e)]},"v-model":([e,t,o],n,l)=>{let i,d=o=>e[t]=o.target.value;if("input"===n.tag)switch(o=o||"oninput",n.props.type){case"checkbox":Array.isArray(e[t])?(d=o=>{let n=o.target.value,l=e[t].indexOf(n);-1===l?e[t].push(n):e[t].splice(l,1)},i=-1!==e[t].indexOf(n.dom.value)):"value"in n.props?(d=()=>{e[t]===n.props.value?e[t]=null:e[t]=n.props.value},i=e[t]===n.props.value):(d=()=>e[t]=!e[t],i=e[t]),x("checked",i,n);break;case"radio":x("checked",e[t]===n.dom.value,n);break;default:x("value",e[t],n)}else"select"===n.tag?(o=o||"onclick",n.props.multiple?(d=o=>{let n=o.target.value;if(o.ctrlKey){let o=e[t].indexOf(n);-1===o?e[t].push(n):e[t].splice(o,1)}else e[t].splice(0,e[t].length),e[t].push(n)},n.children.forEach(o=>{if("option"===o.tag){let n="value"in o.props?o.props.value:o.children.join("").trim();o.props.selected=-1!==e[t].indexOf(n)}})):n.children.forEach(o=>{if("option"===o.tag){let n="value"in o.props?o.props.value:o.children.join("").trim();o.props.selected=n===e[t]}})):"textarea"===n.tag&&(o=o||"oninput",n.children=[e[t]]);let r=n.props[o];x(o,e=>{d(e),r&&r(e)},n,l)},"v-create":(e,t,o)=>{if(!o){let o=e(t);"function"==typeof o&&y(o)}},"v-update":(e,t,o)=>{if(o){let n=e(t,o);"function"==typeof n&&y(n)}},"v-cleanup":(e,t,o)=>{y(()=>e(t,o))}};function x(e,t,o,n){if("function"==typeof t){let n=e.toLowerCase();return n in k==!1&&(a.dom.addEventListener(n.slice(2),V),k[n]=!0),void(o.dom[`v-${n}`]=t)}e in o.dom&&!1===o.isSVG?o.dom[e]!=t&&(o.dom[e]=t):n&&t===n.props[e]||(!1===t?o.dom.removeAttribute(e):o.dom.setAttribute(e,t))}function S(e,t){if(t)for(let o in t.props)o in e.props==!1&&o in k==!1&&o in m==!1&&(o in e.dom&&!1===e.isSVG?e.dom[o]=null:e.dom.removeAttribute(o));for(let o in e.props)if(o in m){if(o in N&&!1===N[o](e.props[o],e,t))break}else x(o,e.props[o],e,t)}function b(t,l){let i=t.children,d=l?.children||[],r=d.length;if(r&&i[0]instanceof n&&"key"in i[0].props&&"key"in d[0].props){let e=i.length,n={};for(let e=0;enull,{},[]);let e=A();w(g);for(let e in k)a.dom.removeEventListener(e.slice(2).toLowerCase(),V),Reflect.deleteProperty(k,e);return s=null,a=null,c=!1,u.vnode=null,u.oldVnode=null,u.component=null,e}}var L=(e,t={},...o)=>new n(e,t||{},o);L.fragment=(e,...t)=>t;var T={Vnode:n,createDomElement:o,current:u,directive:function(e,t){let o=`v-${e}`;N[o]=t,m[o]=!0},directives:N,isComponent:l,isNodeJs:t,isVnode:i,isVnodeComponent:d,mount:function(e,i){let p="string"==typeof e?t?o(e,"svg"===e):document.querySelectorAll(e)[0]:e,c=d(i)?i:l(i)?new n(i,{},[]):new n(()=>i,{},[]);return s&&s.tag!==c.tag&&G(),s=c,a=r(p),A()},onCleanup:y,onMount:function(e){c||v.add(e)},onUnmount:function(e){c||g.add(e)},onUpdate:function(e){h.add(e)},patch:b,reservedProps:m,setAttribute:function(e,t,o,n){e in m||(o.props[e]=t,x(e,t,o,n))},trust:p,unmount:G,update:A,updateAttributes:S,updateVnode:function(e,o){if(w(f),b(e,o),o.tag=e.tag,o.props={...e.props},o.children=[...e.children],w(c?h:v),c=!0,u.vnode=null,u.oldVnode=null,u.component=null,t)return e.dom.innerHTML},v:L};"undefined"!=typeof module?module.exports=T:self.Valyrian=T})();//# sourceMappingURL=index.min.js.map \ No newline at end of file diff --git a/dist/index.min.js.map b/dist/index.min.js.map index 5503321..f9c445c 100644 --- a/dist/index.min.js.map +++ b/dist/index.min.js.map @@ -1 +1 @@ -//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJ0ZXh0VGFnIiwiaXNOb2RlSnMiLCJCb29sZWFuIiwicHJvY2VzcyIsInZlcnNpb25zIiwibm9kZSIsImNyZWF0ZURvbUVsZW1lbnQiLCJ0YWciLCJpc1NWRyIsImRvY3VtZW50IiwiY3JlYXRlRWxlbWVudE5TIiwiY3JlYXRlRWxlbWVudCIsIlZub2RlIiwicHJvcHMiLCJjaGlsZHJlbiIsInRoaXMiLCJpc0NvbXBvbmVudCIsImNvbXBvbmVudCIsImlzVm5vZGUiLCJvYmplY3QiLCJpc1Zub2RlQ29tcG9uZW50IiwiZG9tVG9Wbm9kZSIsImRvbSIsImkiLCJsIiwiY2hpbGROb2RlcyIsImxlbmd0aCIsImNoaWxkRG9tIiwibm9kZVR5cGUiLCJwdXNoIiwidm5vZGUiLCJhdHRyaWJ1dGVzIiwiYXR0ciIsIm5vZGVOYW1lIiwibm9kZVZhbHVlIiwidGFnTmFtZSIsInRvTG93ZXJDYXNlIiwidHJ1c3QiLCJodG1sU3RyaW5nIiwiZGl2IiwiaW5uZXJIVE1MIiwidHJpbSIsIm1hcCIsImNhbGwiLCJpdGVtIiwibWFpbkNvbXBvbmVudCIsIm1haW5Wbm9kZSIsImlzTW91bnRlZCIsImN1cnJlbnQiLCJvbGRWbm9kZSIsInJlc2VydmVkUHJvcHMiLCJrZXkiLCJzdGF0ZSIsIm9uQ2xlYW51cFNldCIsIlNldCIsIm9uTW91bnRTZXQiLCJvblVwZGF0ZVNldCIsIm9uVW5tb3VudFNldCIsIm9uQ2xlYW51cCIsImNhbGxiYWNrIiwiYWRkIiwiY2FsbFNldCIsInNldCIsImNsZWFyIiwiZXZlbnRMaXN0ZW5lck5hbWVzIiwiZXZlbnRMaXN0ZW5lciIsImUiLCJ0YXJnZXQiLCJuYW1lIiwidHlwZSIsImRlZmF1bHRQcmV2ZW50ZWQiLCJ1cGRhdGUiLCJwYXJlbnROb2RlIiwiaGlkZURpcmVjdGl2ZSIsInRlc3QiLCJib29sIiwib2xkbm9kZSIsIm5ld2RvbSIsImNyZWF0ZVRleHROb2RlIiwicmVwbGFjZUNoaWxkIiwiZGlyZWN0aXZlcyIsIm5ld0NoaWxkcmVuIiwic3R5bGUiLCJkaXNwbGF5IiwiY2xhc3NlcyIsImNsYXNzTGlzdCIsInRvZ2dsZSIsImh0bWwiLCJtb2RlbCIsInByb3BlcnR5IiwiZXZlbnQiLCJ2YWx1ZSIsImhhbmRsZXIiLCJBcnJheSIsImlzQXJyYXkiLCJ2YWwiLCJpZHgiLCJpbmRleE9mIiwic3BsaWNlIiwic2hhcmVkU2V0QXR0cmlidXRlIiwibXVsdGlwbGUiLCJjdHJsS2V5IiwiZm9yRWFjaCIsImNoaWxkIiwiam9pbiIsInNlbGVjdGVkIiwicHJldkhhbmRsZXIiLCJjbGVhbnVwIiwibmV3Vm5vZGUiLCJhZGRFdmVudExpc3RlbmVyIiwic2xpY2UiLCJyZW1vdmVBdHRyaWJ1dGUiLCJzZXRBdHRyaWJ1dGUiLCJ1cGRhdGVBdHRyaWJ1dGVzIiwicGF0Y2giLCJuZXdUcmVlIiwib2xkVHJlZSIsIm9sZFRyZWVMZW5ndGgiLCJuZXdUcmVlTGVuZ3RoIiwib2xkS2V5ZWRMaXN0IiwibmV3S2V5ZWRMaXN0IiwibmV3Q2hpbGQiLCJvbGRDaGlsZCIsInNob3VsZFBhdGNoIiwiYXBwZW5kQ2hpbGQiLCJyZW1vdmVDaGlsZCIsInZpZXciLCJiaW5kIiwidGV4dENvbnRlbnQiLCJvbGRNYWluVm5vZGUiLCJ1bm1vdW50IiwicmVzdWx0IiwicmVtb3ZlRXZlbnRMaXN0ZW5lciIsIlJlZmxlY3QiLCJkZWxldGVQcm9wZXJ0eSIsInYiLCJ0YWdPckNvbXBvbmVudCIsImZyYWdtZW50IiwiXyIsImRpcmVjdGl2ZSIsImRpcmVjdGl2ZU5hbWUiLCJjb250YWluZXIiLCJxdWVyeVNlbGVjdG9yQWxsIiwidm5vZGVDb21wb25lbnQiXSwic291cmNlcyI6WyIuLi9saWIvaW5kZXgudHMiXSwic291cmNlc0NvbnRlbnQiOlsiLyogZXNsaW50LWRpc2FibGUgbm8tdXNlLWJlZm9yZS1kZWZpbmUgKi9cbi8qIGVzbGludC1kaXNhYmxlIGluZGVudCAqL1xuLyogZXNsaW50LWRpc2FibGUgc29uYXJqcy9jb2duaXRpdmUtY29tcGxleGl0eSAqL1xuLyogZXNsaW50LWRpc2FibGUgY29tcGxleGl0eSAqL1xuXG4vLyBUaGUgVm5vZGVQcm9wZXJ0aWVzIGludGVyZmFjZSByZXByZXNlbnRzIHByb3BlcnRpZXMgdGhhdCBjYW4gYmUgcGFzc2VkIHRvIGEgdmlydHVhbCBub2RlLlxuZXhwb3J0IGludGVyZmFjZSBWbm9kZVByb3BlcnRpZXMge1xuICAvLyBBIHVuaXF1ZSBrZXkgZm9yIHRoZSB2aXJ0dWFsIG5vZGUsIHdoaWNoIGNhbiBiZSBhIHN0cmluZyBvciBhIG51bWJlci5cbiAgLy8gVGhpcyBpcyB1c2VmdWwgZm9yIG9wdGltaXppbmcgdXBkYXRlcyBpbiBhIGxpc3Qgb2Ygbm9kZXMuXG4gIGtleT86IHN0cmluZyB8IG51bWJlcjtcbiAgLy8gQSBzdGF0ZSBvYmplY3QgdGhhdCBpcyBhc3NvY2lhdGVkIHdpdGggdGhlIHZpcnR1YWwgbm9kZS5cbiAgc3RhdGU/OiBhbnk7XG4gIC8vIEFuIGluZGV4IHNpZ25hdHVyZSB0aGF0IGFsbG93cyBmb3IgYW55IG90aGVyIHByb3BlcnRpZXMgdG8gYmUgYWRkZWQuXG4gIFtrZXk6IHN0cmluZyB8IG51bWJlciB8IHN5bWJvbF06IGFueTtcbn1cblxuLy8gVGhlIERvbUVsZW1lbnQgaW50ZXJmYWNlIGV4dGVuZHMgdGhlIEVsZW1lbnQgaW50ZXJmYWNlIHdpdGggYW4gaW5kZXggc2lnbmF0dXJlLlxuLy8gVGhpcyBhbGxvd3MgZm9yIGFueSBhZGRpdGlvbmFsIHByb3BlcnRpZXMgdG8gYmUgYWRkZWQgdG8gRE9NIGVsZW1lbnRzLlxuZXhwb3J0IGludGVyZmFjZSBEb21FbGVtZW50IGV4dGVuZHMgRWxlbWVudCB7XG4gIFtrZXk6IHN0cmluZ106IGFueTtcbn1cblxuLy8gVGhlIFZub2RlSW50ZXJmYWNlIHJlcHJlc2VudHMgYSB2aXJ0dWFsIG5vZGUuIEl0IGhhcyBhIG51bWJlciBvZiBvcHRpb25hbCBmaWVsZHMsXG4vLyBpbmNsdWRpbmcgYSB0YWcsIHByb3BzLCBjaGlsZHJlbiwgYW5kIGEgRE9NIGVsZW1lbnQuXG5leHBvcnQgaW50ZXJmYWNlIFZub2RlSW50ZXJmYWNlIHtcbiAgLy8gVGhlIGNvbnN0cnVjdG9yIGZvciB0aGUgdmlydHVhbCBub2RlLiBJdCB0YWtlcyBhIHRhZywgcHJvcHMsIGFuZCBjaGlsZHJlbiBhcyBhcmd1bWVudHMuXG4gIC8vIFRoZSB0YWcgY2FuIGJlIGEgc3RyaW5nLCBhIGNvbXBvbmVudCwgb3IgYSBQT0pPIGNvbXBvbmVudC5cbiAgLy8gZXNsaW50LWRpc2FibGUtbmV4dC1saW5lIG5vLXVudXNlZC12YXJzXG4gIG5ldyAodGFnOiBzdHJpbmcgfCBDb21wb25lbnQgfCBQT0pPQ29tcG9uZW50LCBwcm9wczogVm5vZGVQcm9wZXJ0aWVzLCBjaGlsZHJlbjogQ2hpbGRyZW4pOiBWbm9kZUludGVyZmFjZTtcbiAgLy8gVGhlIHRhZyBmb3IgdGhlIHZpcnR1YWwgbm9kZS4gSXQgY2FuIGJlIGEgc3RyaW5nLCBhIGNvbXBvbmVudCwgb3IgYSBQT0pPIGNvbXBvbmVudC5cbiAgdGFnOiBzdHJpbmcgfCBDb21wb25lbnQgfCBQT0pPQ29tcG9uZW50O1xuICAvLyBUaGUgcHJvcHMgZm9yIHRoZSB2aXJ0dWFsIG5vZGUuXG4gIHByb3BzOiBWbm9kZVByb3BlcnRpZXM7XG4gIC8vIFRoZSBjaGlsZHJlbiBmb3IgdGhlIHZpcnR1YWwgbm9kZS5cbiAgY2hpbGRyZW46IENoaWxkcmVuO1xuICAvLyBBIGJvb2xlYW4gaW5kaWNhdGluZyB3aGV0aGVyIHRoZSB2aXJ0dWFsIG5vZGUgaXMgYW4gU1ZHIGVsZW1lbnQuXG4gIGlzU1ZHPzogYm9vbGVhbjtcbiAgLy8gVGhlIERPTSBlbGVtZW50IHRoYXQgY29ycmVzcG9uZHMgdG8gdGhlIHZpcnR1YWwgbm9kZS5cbiAgZG9tPzogRG9tRWxlbWVudDtcbiAgLy8gQSBib29sZWFuIGluZGljYXRpbmcgd2hldGhlciB0aGUgdmlydHVhbCBub2RlIGhhcyBiZWVuIHByb2Nlc3NlZCBpbiB0aGUga2V5ZWQgZGlmZmluZyBhbGdvcml0aG0uXG4gIHByb2Nlc3NlZD86IGJvb2xlYW47XG4gIC8vIEFuIGluZGV4IHNpZ25hdHVyZSB0aGF0IGFsbG93cyBmb3IgYW55IGFkZGl0aW9uYWwgcHJvcGVydGllcyB0byBiZSBhZGRlZCB0byB0aGUgdmlydHVhbCBub2RlLlxuICBba2V5OiBzdHJpbmcgfCBudW1iZXIgfCBzeW1ib2xdOiBhbnk7XG59XG5cbi8vIFRoZSBWbm9kZVdpdGhEb20gaW50ZXJmYWNlIHJlcHJlc2VudHMgYSB2aXJ0dWFsIG5vZGUgdGhhdCBoYXMgYSBET00gZWxlbWVudCBhc3NvY2lhdGVkIHdpdGggaXQuXG5leHBvcnQgaW50ZXJmYWNlIFZub2RlV2l0aERvbSBleHRlbmRzIFZub2RlSW50ZXJmYWNlIHtcbiAgZG9tOiBEb21FbGVtZW50O1xufVxuXG4vLyBUaGUgQ29tcG9uZW50IGludGVyZmFjZSByZXByZXNlbnRzIGEgZnVuY3Rpb24gdGhhdCByZXR1cm5zIGEgdmlydHVhbCBub2RlIG9yIGEgbGlzdCBvZiB2aXJ0dWFsIG5vZGVzLlxuLy8gSXQgY2FuIGFsc28gaGF2ZSBhZGRpdGlvbmFsIHByb3BlcnRpZXMuXG5leHBvcnQgaW50ZXJmYWNlIENvbXBvbmVudCB7XG4gIC8vIFRoZSBmdW5jdGlvbiB0aGF0IHJldHVybnMgYSB2aXJ0dWFsIG5vZGUgb3IgYSBsaXN0IG9mIHZpcnR1YWwgbm9kZXMuXG4gIC8vIEl0IGNhbiB0YWtlIHByb3BzIGFuZCBjaGlsZHJlbiBhcyBhcmd1bWVudHMuXG4gIC8vIGVzbGludC1kaXNhYmxlLW5leHQtbGluZSBuby11bnVzZWQtdmFyc1xuICAocHJvcHM/OiBWbm9kZVByb3BlcnRpZXMgfCBudWxsLCAuLi5jaGlsZHJlbjogYW55W10pOiBWbm9kZUludGVyZmFjZSB8IENoaWxkcmVuIHwgYW55O1xuICAvLyBBbiBpbmRleCBzaWduYXR1cmUgdGhhdCBhbGxvd3MgZm9yIGFueSBhZGRpdGlvbmFsIHByb3BlcnRpZXMgdG8gYmUgYWRkZWQgdG8gdGhlIGNvbXBvbmVudC5cbiAgW2tleTogc3RyaW5nXTogYW55O1xufVxuXG4vLyBUaGUgUE9KT0NvbXBvbmVudCBpbnRlcmZhY2UgcmVwcmVzZW50cyBhIFwicGxhaW4gb2xkIEphdmFTY3JpcHQgb2JqZWN0XCIgKFBPSk8pIGNvbXBvbmVudC5cbi8vIEl0IGhhcyBhIHZpZXcgZnVuY3Rpb24gdGhhdCByZXR1cm5zIGEgdmlydHVhbCBub2RlIG9yIGEgbGlzdCBvZiB2aXJ0dWFsIG5vZGVzLFxuLy8gYXMgd2VsbCBhcyBvcHRpb25hbCBwcm9wcyBhbmQgY2hpbGRyZW4uXG4vLyBJdCBjYW4gYmUgdXNlZCBhbHNvIHRvIGlkZW50aWZ5IGNsYXNzIGluc3RhbmNlIGNvbXBvbmVudHMuXG5leHBvcnQgaW50ZXJmYWNlIFBPSk9Db21wb25lbnQge1xuICAvLyBUaGUgdmlldyBmdW5jdGlvbiB0aGF0IHJldHVybnMgYSB2aXJ0dWFsIG5vZGUgb3IgYSBsaXN0IG9mIHZpcnR1YWwgbm9kZXMuXG4gIHZpZXc6IENvbXBvbmVudDtcbiAgLy8gVGhlIHByb3BzIGZvciB0aGUgY29tcG9uZW50LlxuICBwcm9wcz86IFZub2RlUHJvcGVydGllcyB8IG51bGw7XG4gIC8vIFRoZSBjaGlsZHJlbiBmb3IgdGhlIGNvbXBvbmVudC5cbiAgY2hpbGRyZW4/OiBhbnlbXTtcbiAgLy8gQW4gaW5kZXggc2lnbmF0dXJlIHRoYXQgYWxsb3dzIGZvciBhbnkgYWRkaXRpb25hbCBwcm9wZXJ0aWVzIHRvIGJlIGFkZGVkIHRvIHRoZSBQT0pPIGNvbXBvbmVudC5cbiAgW2tleTogc3RyaW5nXTogYW55O1xufVxuXG4vLyBUaGUgVm5vZGVDb21wb25lbnRJbnRlcmZhY2UgcmVwcmVzZW50cyBhIHZpcnR1YWwgbm9kZSB0aGF0IGhhcyBhIGNvbXBvbmVudCBhcyBpdHMgdGFnLlxuLy8gSXQgaGFzIHByb3BzIGFuZCBjaGlsZHJlbiwganVzdCBsaWtlIGEgcmVndWxhciB2aXJ0dWFsIG5vZGUuXG5leHBvcnQgaW50ZXJmYWNlIFZub2RlQ29tcG9uZW50SW50ZXJmYWNlIGV4dGVuZHMgVm5vZGVJbnRlcmZhY2Uge1xuICB0YWc6IENvbXBvbmVudCB8IFBPSk9Db21wb25lbnQ7XG4gIHByb3BzOiBWbm9kZVByb3BlcnRpZXM7XG4gIGNoaWxkcmVuOiBDaGlsZHJlbjtcbn1cblxuLy8gVGhlIENoaWxkcmVuIGludGVyZmFjZSByZXByZXNlbnRzIGEgbGlzdCBvZiB2aXJ0dWFsIG5vZGVzIG9yIG90aGVyIHZhbHVlcy5cbmV4cG9ydCBpbnRlcmZhY2UgQ2hpbGRyZW4gZXh0ZW5kcyBBcnJheTxWbm9kZUludGVyZmFjZSB8IFZub2RlQ29tcG9uZW50SW50ZXJmYWNlIHwgYW55PiB7fVxuXG4vLyBUaGUgRGlyZWN0aXZlIGludGVyZmFjZSByZXByZXNlbnRzIGEgZnVuY3Rpb24gdGhhdCBjYW4gYmUgYXBwbGllZCB0byBhIHZpcnR1YWwgbm9kZS5cbi8vIEl0IHJlY2VpdmVzIHRoZSB2YWx1ZSwgdmlydHVhbCBub2RlLCBhbmQgb2xkIHZpcnR1YWwgbm9kZSBhcyBhcmd1bWVudHMsIGFuZCBjYW4gcmV0dXJuIGEgYm9vbGVhbiB2YWx1ZS5cbi8vIElmIG9ubHkgdGhlIHZpcnR1YWwgbm9kZSBpcyBwYXNzZWQsIGl0IG1lYW5zIGl0cyB0aGUgb24gY3JlYXRlIHBoYXNlIGZvciB0aGUgdi1ub2RlLlxuLy8gSWYgdGhlIG9sZCB2aXJ0dWFsIG5vZGUgaXMgYWxzbyBwYXNzZWQsIGl0IG1lYW5zIGl0cyB0aGUgb24gdXBkYXRlIHBoYXNlIGZvciB0aGUgdi1ub2RlLlxuZXhwb3J0IGludGVyZmFjZSBEaXJlY3RpdmUge1xuICAvLyBlc2xpbnQtZGlzYWJsZS1uZXh0LWxpbmUgbm8tdW51c2VkLXZhcnNcbiAgKHZhbHVlOiBhbnksIHZub2RlOiBWbm9kZVdpdGhEb20sIG9sZFZub2RlPzogVm5vZGVXaXRoRG9tKTogdm9pZCB8IGJvb2xlYW47XG59XG5cbi8vIFRoZSBEaXJlY3RpdmVzIGludGVyZmFjZSBpcyBhIG1hcHBpbmcgb2YgZGlyZWN0aXZlIG5hbWVzIHRvIERpcmVjdGl2ZSBmdW5jdGlvbnMuXG5leHBvcnQgaW50ZXJmYWNlIERpcmVjdGl2ZXMge1xuICBba2V5OiBzdHJpbmddOiBEaXJlY3RpdmU7XG59XG5cbi8vIFRoZSBSZXNlcnZlZFByb3BzIGludGVyZmFjZSBpcyBhIG1hcHBpbmcgb2YgcmVzZXJ2ZWQgcHJvcCBuYW1lcyB0byB0aGUgdmFsdWUgYHRydWVgLlxuLy8gVGhlc2UgcHJvcCBuYW1lcyBjYW5ub3QgYmUgdXNlZCBhcyBjdXN0b20gcHJvcCBuYW1lcy5cbmV4cG9ydCBpbnRlcmZhY2UgUmVzZXJ2ZWRQcm9wcyB7XG4gIFtrZXk6IHN0cmluZ106IHRydWU7XG59XG5cbi8vIFRoZSBDdXJyZW50IGludGVyZmFjZSByZXByZXNlbnRzIHRoZSBjdXJyZW50IGNvbXBvbmVudCBhbmQgdmlydHVhbCBub2RlIHRoYXQgYXJlIGJlaW5nIHByb2Nlc3NlZC5cbmV4cG9ydCBpbnRlcmZhY2UgQ3VycmVudCB7XG4gIC8vIFRoZSBjdXJyZW50IGNvbXBvbmVudC4gSXQgY2FuIGJlIGEgY29tcG9uZW50LCBhIFBPSk8gY29tcG9uZW50LCBvciBudWxsLlxuICBjb21wb25lbnQ6IENvbXBvbmVudCB8IFBPSk9Db21wb25lbnQgfCBudWxsO1xuICAvLyBUaGUgY3VycmVudCB2aXJ0dWFsIG5vZGUuIEl0IG11c3QgaGF2ZSBhIERPTSBlbGVtZW50IGFzc29jaWF0ZWQgd2l0aCBpdC5cbiAgdm5vZGU6IFZub2RlV2l0aERvbSB8IG51bGw7XG4gIC8vIFRoZSBvbGQgdmlydHVhbCBub2RlLiBJdCBtdXN0IGhhdmUgYSBET00gZWxlbWVudCBhc3NvY2lhdGVkIHdpdGggaXQuXG4gIG9sZFZub2RlPzogVm5vZGVXaXRoRG9tIHwgbnVsbDtcbn1cblxuLy8gVGhlIFYgZnVuY3Rpb24gaXMgdGhlIG1haW4gZnVuY3Rpb24gZm9yIGNyZWF0aW5nIHZpcnR1YWwgbm9kZXMuXG4vLyBJdCB0YWtlcyBhIHRhZyBvciBjb21wb25lbnQsIHByb3BzLCBhbmQgY2hpbGRyZW4gYXMgYXJndW1lbnRzLCBhbmQgcmV0dXJucyBhIHZpcnR1YWwgbm9kZS5cbmV4cG9ydCBpbnRlcmZhY2UgViB7XG4gIC8vIGVzbGludC1kaXNhYmxlLW5leHQtbGluZSBuby11bnVzZWQtdmFycywgbm8tdXNlLWJlZm9yZS1kZWZpbmVcbiAgKHRhZ09yQ29tcG9uZW50OiBzdHJpbmcgfCBDb21wb25lbnQgfCBQT0pPQ29tcG9uZW50LCBwcm9wczogVm5vZGVQcm9wZXJ0aWVzIHwgbnVsbCwgLi4uY2hpbGRyZW46IENoaWxkcmVuKTpcbiAgICB8IFZub2RlSW50ZXJmYWNlXG4gICAgfCBWbm9kZUNvbXBvbmVudEludGVyZmFjZTtcbiAgLy8gZXNsaW50LWRpc2FibGUtbmV4dC1saW5lIG5vLXVudXNlZC12YXJzLCBuby11c2UtYmVmb3JlLWRlZmluZVxuICBmcmFnbWVudChfOiBhbnksIC4uLmNoaWxkcmVuOiBDaGlsZHJlbik6IENoaWxkcmVuO1xufVxuLy8gJ3RleHRUYWcnIGlzIGEgY29uc3RhbnQgc3RyaW5nIHRoYXQgaXMgdXNlZCB0byByZXByZXNlbnQgdGV4dCBub2RlcyBpbiB0aGUgdmlydHVhbCBET00uXG5jb25zdCB0ZXh0VGFnID0gXCIjdGV4dFwiO1xuXG4vLyAnaXNOb2RlSnMnIGlzIGEgYm9vbGVhbiB0aGF0IGlzIHRydWUgaWYgdGhlIGNvZGUgaXMgcnVubmluZyBpbiBhIE5vZGUuanMgZW52aXJvbm1lbnQgYW5kIGZhbHNlIG90aGVyd2lzZS5cbi8vIEl0IGlzIGRldGVybWluZWQgYnkgY2hlY2tpbmcgaWYgdGhlICdwcm9jZXNzJyBnbG9iYWwgb2JqZWN0IGlzIGRlZmluZWQgYW5kIGhhcyBhICd2ZXJzaW9ucycgcHJvcGVydHkuXG5leHBvcnQgbGV0IGlzTm9kZUpzID0gQm9vbGVhbih0eXBlb2YgcHJvY2VzcyAhPT0gXCJ1bmRlZmluZWRcIiAmJiBwcm9jZXNzLnZlcnNpb25zICYmIHByb2Nlc3MudmVyc2lvbnMubm9kZSk7XG5cbi8vICdjcmVhdGVEb21FbGVtZW50JyBpcyBhIGZ1bmN0aW9uIHRoYXQgY3JlYXRlcyBhIG5ldyBET00gZWxlbWVudCB3aXRoIHRoZSBzcGVjaWZpZWQgdGFnIG5hbWUuXG4vLyBJZiAnaXNTVkcnIGlzIHRydWUsIGl0IGNyZWF0ZXMgYW4gU1ZHIGVsZW1lbnQgaW5zdGVhZCBvZiBhIHJlZ3VsYXIgRE9NIGVsZW1lbnQuXG5leHBvcnQgZnVuY3Rpb24gY3JlYXRlRG9tRWxlbWVudCh0YWc6IHN0cmluZywgaXNTVkc6IGJvb2xlYW4gPSBmYWxzZSk6IERvbUVsZW1lbnQge1xuICByZXR1cm4gaXNTVkcgPyBkb2N1bWVudC5jcmVhdGVFbGVtZW50TlMoXCJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2Z1wiLCB0YWcpIDogZG9jdW1lbnQuY3JlYXRlRWxlbWVudCh0YWcpO1xufVxuXG4vLyAnVm5vZGUnIGlzIGEgY2xhc3MgdGhhdCByZXByZXNlbnRzIGEgdmlydHVhbCBET00gbm9kZS5cbi8vIEl0IGhhcyB0aHJlZSBwcm9wZXJ0aWVzOiAndGFnJywgJ3Byb3BzJywgYW5kICdjaGlsZHJlbicuXG4vLyAnVm5vZGUnIGlzIGV4cG9ydGVkIGFzIGFuIG9iamVjdCB3aXRoIGEgdHlwZSBvZiAnVm5vZGVJbnRlcmZhY2UnLlxuLy8gVGhlICdhcyB1bmtub3duIGFzIFZub2RlSW50ZXJmYWNlJyBpcyB1c2VkIHRvIHRlbGwgVHlwZVNjcmlwdCB0aGF0IHRoZSAnVm5vZGUnIGZ1bmN0aW9uIGhhcyB0aGUgc2FtZSB0eXBlIGFzICdWbm9kZUludGVyZmFjZScuXG5leHBvcnQgY29uc3QgVm5vZGUgPSBmdW5jdGlvbiBWbm9kZSh0aGlzOiBWbm9kZUludGVyZmFjZSwgdGFnOiBzdHJpbmcsIHByb3BzOiBWbm9kZVByb3BlcnRpZXMsIGNoaWxkcmVuOiBDaGlsZHJlbikge1xuICAvLyAndGhpcycgcmVmZXJzIHRvIHRoZSBjdXJyZW50IGluc3RhbmNlIG9mICdWbm9kZScuXG4gIHRoaXMudGFnID0gdGFnO1xuICB0aGlzLnByb3BzID0gcHJvcHM7XG4gIHRoaXMuY2hpbGRyZW4gPSBjaGlsZHJlbjtcbn0gYXMgdW5rbm93biBhcyBWbm9kZUludGVyZmFjZTtcblxuLy8gJ2lzQ29tcG9uZW50JyBpcyBhIGZ1bmN0aW9uIHRoYXQgcmV0dXJucyB0cnVlIGlmIHRoZSBnaXZlbiAnY29tcG9uZW50JyBpcyBhIHZhbGlkIGNvbXBvbmVudCBhbmQgZmFsc2Ugb3RoZXJ3aXNlLlxuLy8gQSBjb21wb25lbnQgaXMgZWl0aGVyIGEgZnVuY3Rpb24gb3IgYW4gb2JqZWN0IHdpdGggYSAndmlldycgZnVuY3Rpb24uXG5leHBvcnQgZnVuY3Rpb24gaXNDb21wb25lbnQoY29tcG9uZW50KTogY29tcG9uZW50IGlzIENvbXBvbmVudCB7XG4gIHJldHVybiBjb21wb25lbnQgJiYgKHR5cGVvZiBjb21wb25lbnQgPT09IFwiZnVuY3Rpb25cIiB8fCAodHlwZW9mIGNvbXBvbmVudCA9PT0gXCJvYmplY3RcIiAmJiBcInZpZXdcIiBpbiBjb21wb25lbnQpKTtcbn1cblxuLy8gJ2lzVm5vZGUnIGlzIGEgZnVuY3Rpb24gdGhhdCByZXR1cm5zIHRydWUgaWYgdGhlIGdpdmVuICdvYmplY3QnIGlzIGEgJ1Zub2RlJyBpbnN0YW5jZSBhbmQgZmFsc2Ugb3RoZXJ3aXNlLlxuZXhwb3J0IGNvbnN0IGlzVm5vZGUgPSAob2JqZWN0PzogdW5rbm93biB8IFZub2RlSW50ZXJmYWNlKTogb2JqZWN0IGlzIFZub2RlSW50ZXJmYWNlID0+IHtcbiAgLy8gVXNlIHRoZSAnaW5zdGFuY2VvZicgb3BlcmF0b3IgdG8gY2hlY2sgaWYgJ29iamVjdCcgaXMgYW4gaW5zdGFuY2Ugb2YgJ1Zub2RlJy5cbiAgcmV0dXJuIG9iamVjdCBpbnN0YW5jZW9mIFZub2RlO1xufTtcblxuLy8gJ2lzVm5vZGVDb21wb25lbnQnIGlzIGEgZnVuY3Rpb24gdGhhdCByZXR1cm5zIHRydWUgaWYgdGhlIGdpdmVuICdvYmplY3QnIGlzIGEgJ1Zub2RlJyBpbnN0YW5jZSB3aXRoIGEgJ3RhZycgcHJvcGVydHkgdGhhdCBpcyBhIHZhbGlkIGNvbXBvbmVudC5cbi8vIEl0IHJldHVybnMgZmFsc2Ugb3RoZXJ3aXNlLlxuZXhwb3J0IGNvbnN0IGlzVm5vZGVDb21wb25lbnQgPSAob2JqZWN0PzogdW5rbm93biB8IFZub2RlQ29tcG9uZW50SW50ZXJmYWNlKTogb2JqZWN0IGlzIFZub2RlQ29tcG9uZW50SW50ZXJmYWNlID0+IHtcbiAgLy8gQ2hlY2sgaWYgJ29iamVjdCcgaXMgYSAnVm5vZGUnIGluc3RhbmNlIGFuZCBpdHMgJ3RhZycgcHJvcGVydHkgaXMgYSB2YWxpZCBjb21wb25lbnQuXG4gIHJldHVybiBpc1Zub2RlKG9iamVjdCkgJiYgaXNDb21wb25lbnQob2JqZWN0LnRhZyk7XG59O1xuXG4vLyAnZG9tVG9Wbm9kZScgaXMgYSBmdW5jdGlvbiB0aGF0IGNvbnZlcnRzIGEgRE9NIG5vZGUgdG8gYSAnVm5vZGUnIGluc3RhbmNlLlxuZnVuY3Rpb24gZG9tVG9Wbm9kZShkb206IGFueSk6IFZub2RlV2l0aERvbSB7XG4gIGxldCBjaGlsZHJlbjogVm5vZGVXaXRoRG9tW10gPSBbXTtcbiAgLy8gSXRlcmF0ZSB0aHJvdWdoIGFsbCBjaGlsZCBub2RlcyBvZiAnZG9tJy5cbiAgZm9yIChsZXQgaSA9IDAsIGwgPSBkb20uY2hpbGROb2Rlcy5sZW5ndGg7IGkgPCBsOyBpKyspIHtcbiAgICBsZXQgY2hpbGREb20gPSBkb20uY2hpbGROb2Rlc1tpXTtcbiAgICAvLyBJZiB0aGUgY2hpbGQgbm9kZSBpcyBhIHRleHQgbm9kZSwgY3JlYXRlIGEgJ1Zub2RlJyBpbnN0YW5jZSB3aXRoIHRoZSAndGV4dFRhZycgY29uc3RhbnQgYXMgdGhlICd0YWcnIHByb3BlcnR5LlxuICAgIC8vIFNldCB0aGUgJ2RvbScgcHJvcGVydHkgb2YgdGhlICdWbm9kZScgaW5zdGFuY2UgdG8gdGhlIGNoaWxkIERPTSBub2RlLlxuICAgIC8vIFB1c2ggdGhlICdWbm9kZScgaW5zdGFuY2UgdG8gdGhlICdjaGlsZHJlbicgYXJyYXkuXG4gICAgaWYgKGNoaWxkRG9tLm5vZGVUeXBlID09PSAzKSB7XG4gICAgICBsZXQgdm5vZGUgPSBuZXcgVm5vZGUodGV4dFRhZywge30sIFtdKTtcbiAgICAgIHZub2RlLmRvbSA9IGNoaWxkRG9tO1xuICAgICAgY2hpbGRyZW4ucHVzaCh2bm9kZSBhcyBWbm9kZVdpdGhEb20pO1xuICAgICAgY29udGludWU7XG4gICAgfVxuXG4gICAgLy8gSWYgdGhlIGNoaWxkIG5vZGUgaXMgYW4gZWxlbWVudCBub2RlLCByZWN1cnNpdmVseSBjYWxsICdkb21Ub1Zub2RlJyB0byBjb252ZXJ0IGl0IHRvIGEgJ1Zub2RlJyBpbnN0YW5jZS5cbiAgICAvLyBQdXNoIHRoZSAnVm5vZGUnIGluc3RhbmNlIHRvIHRoZSAnY2hpbGRyZW4nIGFycmF5LlxuICAgIGlmIChjaGlsZERvbS5ub2RlVHlwZSA9PT0gMSkge1xuICAgICAgY2hpbGRyZW4ucHVzaChkb21Ub1Zub2RlKGNoaWxkRG9tKSk7XG4gICAgfVxuICB9XG5cbiAgbGV0IHByb3BzOiBWbm9kZVByb3BlcnRpZXMgPSB7fTtcbiAgLy8gSXRlcmF0ZSB0aHJvdWdoIGFsbCBhdHRyaWJ1dGVzIG9mICdkb20nLlxuICBmb3IgKGxldCBpID0gMCwgbCA9IGRvbS5hdHRyaWJ1dGVzLmxlbmd0aDsgaSA8IGw7IGkrKykge1xuICAgIGxldCBhdHRyID0gZG9tLmF0dHJpYnV0ZXNbaV07XG4gICAgLy8gQWRkIHRoZSBhdHRyaWJ1dGUgdG8gdGhlICdwcm9wcycgb2JqZWN0LCB1c2luZyB0aGUgYXR0cmlidXRlJ3MgbmFtZSBhcyB0aGUga2V5IGFuZCBpdHMgdmFsdWUgYXMgdGhlIHZhbHVlLlxuICAgIHByb3BzW2F0dHIubm9kZU5hbWVdID0gYXR0ci5ub2RlVmFsdWU7XG4gIH1cblxuICAvLyBDcmVhdGUgYSBuZXcgJ1Zub2RlJyBpbnN0YW5jZSB3aXRoIHRoZSAndGFnJyBwcm9wZXJ0eSBzZXQgdG8gdGhlIGxvd2VyY2FzZSB2ZXJzaW9uIG9mIHRoZSBET00gbm9kZSdzIHRhZyBuYW1lLlxuICAvLyBTZXQgdGhlICdwcm9wcycgYW5kICdjaGlsZHJlbicgcHJvcGVydGllcyB0byB0aGUgJ3Byb3BzJyBhbmQgJ2NoaWxkcmVuJyBhcnJheXMgcmVzcGVjdGl2ZWx5LlxuICAvLyBTZXQgdGhlICdkb20nIHByb3BlcnR5IG9mIHRoZSAnVm5vZGUnIGluc3RhbmNlIHRvIHRoZSBET00gbm9kZS5cbiAgbGV0IHZub2RlID0gbmV3IFZub2RlKGRvbS50YWdOYW1lLnRvTG93ZXJDYXNlKCksIHByb3BzLCBjaGlsZHJlbik7XG4gIHZub2RlLmRvbSA9IGRvbTtcbiAgcmV0dXJuIHZub2RlIGFzIFZub2RlV2l0aERvbTtcbn1cblxuLy8gVGhpcyBmdW5jdGlvbiB0YWtlcyBpbiBhbiBIVE1MIHN0cmluZyBhbmQgY3JlYXRlcyBhIHZpcnR1YWwgbm9kZSByZXByZXNlbnRhdGlvbiBvZiBpdFxuLy8gdXNpbmcgdGhlIGBkb21Ub1Zub2RlYCBmdW5jdGlvbi4gSXQgZG9lcyB0aGlzIGJ5IGNyZWF0aW5nIGEgbmV3IGBkaXZgIGVsZW1lbnQsIHNldHRpbmdcbi8vIGl0cyBgaW5uZXJIVE1MYCB0byB0aGUgcHJvdmlkZWQgSFRNTCBzdHJpbmcsIGFuZCB0aGVuIHVzaW5nIGBtYXBgIHRvIGl0ZXJhdGUgb3ZlciB0aGVcbi8vIGBjaGlsZE5vZGVzYCBvZiB0aGUgYGRpdmAgZWxlbWVudCwgcGFzc2luZyBlYWNoIG9uZSB0byBgZG9tVG9Wbm9kZWAgdG8gY3JlYXRlIGEgdmlydHVhbFxuLy8gbm9kZSByZXByZXNlbnRhdGlvbiBvZiBpdC4gVGhlIHJlc3VsdGluZyBhcnJheSBvZiB2aXJ0dWFsIG5vZGVzIGlzIHRoZW4gcmV0dXJuZWQuXG5leHBvcnQgZnVuY3Rpb24gdHJ1c3QoaHRtbFN0cmluZzogc3RyaW5nKSB7XG4gIGxldCBkaXYgPSBjcmVhdGVEb21FbGVtZW50KFwiZGl2XCIpO1xuICBkaXYuaW5uZXJIVE1MID0gaHRtbFN0cmluZy50cmltKCk7XG5cbiAgcmV0dXJuIFtdLm1hcC5jYWxsKGRpdi5jaGlsZE5vZGVzLCAoaXRlbSkgPT4gZG9tVG9Wbm9kZShpdGVtKSk7XG59XG5cbi8qID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09ICovXG4vKiBNYWluIENvbXBvbmVudCBpbXBsZW1lbnRhdGlvbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAqL1xuLyogPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0gKi9cblxuLy8gVGhlc2UgdmFyaWFibGVzIGFyZSB1c2VkIHRvIHN0b3JlIHRoZSBtYWluIGNvbXBvbmVudCwgdGhlIG1haW4gdmlydHVhbCBub2RlLCBhbmQgd2hldGhlclxuLy8gdGhlIG1haW4gY29tcG9uZW50IGlzIGN1cnJlbnRseSBtb3VudGVkLlxubGV0IG1haW5Db21wb25lbnQ6IFZub2RlQ29tcG9uZW50SW50ZXJmYWNlIHwgbnVsbCA9IG51bGw7XG5sZXQgbWFpblZub2RlOiBWbm9kZVdpdGhEb20gfCBudWxsID0gbnVsbDtcbmxldCBpc01vdW50ZWQgPSBmYWxzZTtcblxuLy8gVGhpcyBvYmplY3QgaXMgdXNlZCB0byBzdG9yZSB0aGUgY3VycmVudCB2aXJ0dWFsIG5vZGUgYW5kIGNvbXBvbmVudCBiZWluZyByZW5kZXJlZC5cbmV4cG9ydCBjb25zdCBjdXJyZW50OiBDdXJyZW50ID0ge1xuICB2bm9kZTogbnVsbCxcbiAgb2xkVm5vZGU6IG51bGwsXG4gIGNvbXBvbmVudDogbnVsbFxufTtcblxuLyogUmVzZXJ2ZWQgcHJvcHMgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0gKi9cbi8vIFRoaXMgb2JqZWN0IGlzIHVzZWQgdG8gc3RvcmUgdGhlIG5hbWVzIG9mIHJlc2VydmVkIHByb3BzLCB3aGljaCBhcmUgcHJvcHMgdGhhdCBhcmUgcmVzZXJ2ZWRcbi8vIGZvciBzcGVjaWFsIHB1cnBvc2VzIGFuZCBzaG91bGQgbm90IGJlIHVzZWQgYXMgcmVndWxhciBjb21wb25lbnQgcHJvcHMuXG5leHBvcnQgY29uc3QgcmVzZXJ2ZWRQcm9wczogUmVjb3JkPHN0cmluZywgdHJ1ZT4gPSB7XG4gIGtleTogdHJ1ZSxcbiAgc3RhdGU6IHRydWUsXG4gIFwidi1rZWVwXCI6IHRydWUsXG5cbiAgLy8gQnVpbHQgaW4gZGlyZWN0aXZlc1xuICBcInYtaWZcIjogdHJ1ZSxcbiAgXCJ2LXVubGVzc1wiOiB0cnVlLFxuICBcInYtZm9yXCI6IHRydWUsXG4gIFwidi1zaG93XCI6IHRydWUsXG4gIFwidi1jbGFzc1wiOiB0cnVlLFxuICBcInYtaHRtbFwiOiB0cnVlLFxuICBcInYtbW9kZWxcIjogdHJ1ZSxcbiAgXCJ2LWNyZWF0ZVwiOiB0cnVlLFxuICBcInYtdXBkYXRlXCI6IHRydWUsXG4gIFwidi1jbGVhbnVwXCI6IHRydWVcbn07XG5cbi8qIE1vdW50aW5nLCBVcGRhdGluZywgQ2xlYW51cCBhbmQgVW5tb3VudGluZyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tICovXG4vLyBUaGVzZSBzZXRzIGFyZSB1c2VkIHRvIHN0b3JlIGNhbGxiYWNrcyBmb3IgdmFyaW91cyBsaWZlY3ljbGUgZXZlbnRzOiBtb3VudGluZywgdXBkYXRpbmcsXG4vLyBjbGVhbmluZyB1cCwgYW5kIHVubW91bnRpbmcuXG5jb25zdCBvbkNsZWFudXBTZXQ6IFNldDxGdW5jdGlvbj4gPSBuZXcgU2V0KCk7XG5jb25zdCBvbk1vdW50U2V0OiBTZXQ8RnVuY3Rpb24+ID0gbmV3IFNldCgpO1xuY29uc3Qgb25VcGRhdGVTZXQ6IFNldDxGdW5jdGlvbj4gPSBuZXcgU2V0KCk7XG5jb25zdCBvblVubW91bnRTZXQ6IFNldDxGdW5jdGlvbj4gPSBuZXcgU2V0KCk7XG5cbi8vIFRoZXNlIGZ1bmN0aW9ucyBhbGxvdyB1c2VycyB0byByZWdpc3RlciBjYWxsYmFja3MgZm9yIHRoZSBjb3JyZXNwb25kaW5nIGxpZmVjeWNsZSBldmVudHMuXG5leHBvcnQgZnVuY3Rpb24gb25Nb3VudChjYWxsYmFjaykge1xuICBpZiAoIWlzTW91bnRlZCkge1xuICAgIG9uTW91bnRTZXQuYWRkKGNhbGxiYWNrKTtcbiAgfVxufVxuXG5leHBvcnQgZnVuY3Rpb24gb25VcGRhdGUoY2FsbGJhY2spIHtcbiAgb25VcGRhdGVTZXQuYWRkKGNhbGxiYWNrKTtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIG9uQ2xlYW51cChjYWxsYmFjaykge1xuICBvbkNsZWFudXBTZXQuYWRkKGNhbGxiYWNrKTtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIG9uVW5tb3VudChjYWxsYmFjaykge1xuICBpZiAoIWlzTW91bnRlZCkge1xuICAgIG9uVW5tb3VudFNldC5hZGQoY2FsbGJhY2spO1xuICB9XG59XG5cbi8vIFRoaXMgZnVuY3Rpb24gaXMgdXNlZCB0byBjYWxsIGFsbCB0aGUgY2FsbGJhY2tzIGluIGEgZ2l2ZW4gc2V0LlxuZnVuY3Rpb24gY2FsbFNldChzZXQpIHtcbiAgZm9yIChsZXQgY2FsbGJhY2sgb2Ygc2V0KSB7XG4gICAgY2FsbGJhY2soKTtcbiAgfVxuXG4gIHNldC5jbGVhcigpO1xufVxuXG4vKiBFdmVudCBsaXN0ZW5lciAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSAqL1xuXG4vLyBUaGlzIG9iamVjdCBzdG9yZXMgdGhlIG5hbWVzIG9mIGV2ZW50IGxpc3RlbmVycyB0aGF0IGhhdmUgYmVlbiBhZGRlZFxuY29uc3QgZXZlbnRMaXN0ZW5lck5hbWVzOiBSZWNvcmQ8c3RyaW5nLCB0cnVlPiA9IHt9O1xuXG4vLyBUaGlzIGZ1bmN0aW9uIGlzIGNhbGxlZCB3aGVuIGFuIGV2ZW50IG9jY3Vyc1xuZnVuY3Rpb24gZXZlbnRMaXN0ZW5lcihlOiBFdmVudCkge1xuICAvLyBDb252ZXJ0IHRoZSB0YXJnZXQgb2YgdGhlIGV2ZW50IHRvIGEgRE9NIGVsZW1lbnRcbiAgbGV0IGRvbSA9IGUudGFyZ2V0IGFzIERvbUVsZW1lbnQ7XG5cbiAgLy8gQ3JlYXRlIHRoZSBuYW1lIG9mIHRoZSBldmVudCBsaXN0ZW5lciBieSBhZGRpbmcgXCJ2LW9uXCIgdG8gdGhlIGV2ZW50IHR5cGVcbiAgbGV0IG5hbWUgPSBgdi1vbiR7ZS50eXBlfWA7XG5cbiAgLy8gS2VlcCBnb2luZyB1cCB0aGUgRE9NIHRyZWUgdW50aWwgd2UgZmluZCBhbiBlbGVtZW50IHdpdGggYW4gZXZlbnQgbGlzdGVuZXJcbiAgLy8gbWF0Y2hpbmcgdGhlIGV2ZW50IHR5cGVcbiAgd2hpbGUgKGRvbSkge1xuICAgIGlmIChkb21bbmFtZV0pIHtcbiAgICAgIC8vIENhbGwgdGhlIGV2ZW50IGxpc3RlbmVyIGZ1bmN0aW9uXG4gICAgICBkb21bbmFtZV0oZSwgZG9tKTtcblxuICAgICAgLy8gSWYgdGhlIGRlZmF1bHQgYWN0aW9uIG9mIHRoZSBldmVudCBoYXNuJ3QgYmVlbiBwcmV2ZW50ZWQsIHVwZGF0ZSB0aGUgRE9NXG4gICAgICBpZiAoIWUuZGVmYXVsdFByZXZlbnRlZCkge1xuICAgICAgICB1cGRhdGUoKTtcbiAgICAgIH1cbiAgICAgIHJldHVybjtcbiAgICB9XG4gICAgZG9tID0gZG9tLnBhcmVudE5vZGUgYXMgRG9tRWxlbWVudDtcbiAgfVxufVxuXG4vKiBEaXJlY3RpdmVzIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSAqL1xuXG4vLyBUaGlzIGZ1bmN0aW9uIGNyZWF0ZXMgYSBkaXJlY3RpdmUgdGhhdCBoaWRlcyBhbiBlbGVtZW50IGJhc2VkIG9uIGEgY29uZGl0aW9uXG5sZXQgaGlkZURpcmVjdGl2ZSA9ICh0ZXN0OiBib29sZWFuKSA9PiAoYm9vbDogYm9vbGVhbiwgdm5vZGU6IFZub2RlSW50ZXJmYWNlLCBvbGRub2RlPzogVm5vZGVJbnRlcmZhY2UpID0+IHtcbiAgLy8gSWYgdGVzdCBpcyB0cnVlLCB1c2UgdGhlIHZhbHVlIG9mIGJvb2wuIE90aGVyd2lzZSwgdXNlIHRoZSBvcHBvc2l0ZSBvZiBib29sLlxuICBsZXQgdmFsdWUgPSB0ZXN0ID8gYm9vbCA6ICFib29sO1xuXG4gIC8vIElmIHRoZSB2YWx1ZSBpcyB0cnVlLCBoaWRlIHRoZSBlbGVtZW50IGJ5IHJlcGxhY2luZyBpdCB3aXRoIGEgdGV4dCBub2RlXG4gIGlmICh2YWx1ZSkge1xuICAgIGxldCBuZXdkb20gPSBkb2N1bWVudC5jcmVhdGVUZXh0Tm9kZShcIlwiKTtcbiAgICBpZiAob2xkbm9kZSAmJiBvbGRub2RlLmRvbSAmJiBvbGRub2RlLmRvbS5wYXJlbnROb2RlKSB7XG4gICAgICBvbGRub2RlLmRvbS5wYXJlbnROb2RlLnJlcGxhY2VDaGlsZChuZXdkb20sIG9sZG5vZGUuZG9tKTtcbiAgICB9XG4gICAgdm5vZGUudGFnID0gXCIjdGV4dFwiO1xuICAgIHZub2RlLmNoaWxkcmVuID0gW107XG4gICAgdm5vZGUucHJvcHMgPSB7fTtcbiAgICB2bm9kZS5kb20gPSBuZXdkb20gYXMgdW5rbm93biBhcyBEb21FbGVtZW50O1xuICAgIHJldHVybiBmYWxzZTtcbiAgfVxufTtcblxuLy8gVGhpcyBvYmplY3Qgc3RvcmVzIGFsbCB0aGUgYXZhaWxhYmxlIGRpcmVjdGl2ZXNcbmV4cG9ydCBjb25zdCBkaXJlY3RpdmVzOiBEaXJlY3RpdmVzID0ge1xuICAvLyBUaGUgXCJ2LWlmXCIgZGlyZWN0aXZlIGhpZGVzIGFuIGVsZW1lbnQgaWYgdGhlIGdpdmVuIGNvbmRpdGlvbiBpcyBmYWxzZVxuICBcInYtaWZcIjogaGlkZURpcmVjdGl2ZShmYWxzZSksXG5cbiAgLy8gVGhlIFwidi11bmxlc3NcIiBkaXJlY3RpdmUgaGlkZXMgYW4gZWxlbWVudCBpZiB0aGUgZ2l2ZW4gY29uZGl0aW9uIGlzIHRydWVcbiAgXCJ2LXVubGVzc1wiOiBoaWRlRGlyZWN0aXZlKHRydWUpLFxuXG4gIC8vIFRoZSBcInYtZm9yXCIgZGlyZWN0aXZlIGNyZWF0ZXMgYSBsb29wIGFuZCBhcHBsaWVzIGEgY2FsbGJhY2sgZnVuY3Rpb24gdG8gZWFjaCBpdGVtIGluIHRoZSBsb29wXG4gIFwidi1mb3JcIjogKHNldDogdW5rbm93bltdLCB2bm9kZTogVm5vZGVXaXRoRG9tKSA9PiB7XG4gICAgbGV0IG5ld0NoaWxkcmVuOiBWbm9kZUludGVyZmFjZVtdID0gW107XG4gICAgbGV0IGNhbGxiYWNrID0gdm5vZGUuY2hpbGRyZW5bMF07XG4gICAgZm9yIChsZXQgaSA9IDAsIGwgPSBzZXQubGVuZ3RoOyBpIDwgbDsgaSsrKSB7XG4gICAgICBuZXdDaGlsZHJlbi5wdXNoKGNhbGxiYWNrKHNldFtpXSwgaSkpO1xuICAgIH1cbiAgICB2bm9kZS5jaGlsZHJlbiA9IG5ld0NoaWxkcmVuO1xuICB9LFxuXG4gIC8vIFRoZSBcInYtc2hvd1wiIGRpcmVjdGl2ZSBzaG93cyBvciBoaWRlcyBhbiBlbGVtZW50IGJ5IHNldHRpbmcgdGhlIFwiZGlzcGxheVwiIHN0eWxlIHByb3BlcnR5XG4gIFwidi1zaG93XCI6IChib29sOiBib29sZWFuLCB2bm9kZTogVm5vZGVXaXRoRG9tKSA9PiB7XG4gICAgKFxuICAgICAgdm5vZGUuZG9tIGFzIHVua25vd24gYXMge1xuICAgICAgICBzdHlsZTogeyBkaXNwbGF5OiBzdHJpbmcgfTtcbiAgICAgIH1cbiAgICApLnN0eWxlLmRpc3BsYXkgPSBib29sID8gXCJcIiA6IFwibm9uZVwiO1xuICB9LFxuXG4gIC8vIFRoZSBcInYtY2xhc3NcIiBkaXJlY3RpdmUgYWRkcyBvciByZW1vdmVzIGNsYXNzIG5hbWVzIGZyb20gYW4gZWxlbWVudCBiYXNlZCBvbiBhIGNvbmRpdGlvblxuICBcInYtY2xhc3NcIjogKGNsYXNzZXM6IHsgW3g6IHN0cmluZ106IGJvb2xlYW4gfSwgdm5vZGU6IFZub2RlV2l0aERvbSkgPT4ge1xuICAgIC8vIExvb3AgdGhyb3VnaCBhbGwgdGhlIGNsYXNzIG5hbWVzIGluIHRoZSBjbGFzc2VzIG9iamVjdFxuICAgIGZvciAobGV0IG5hbWUgaW4gY2xhc3Nlcykge1xuICAgICAgLy8gQWRkIG9yIHJlbW92ZSB0aGUgY2xhc3MgbmFtZSBmcm9tIHRoZSBlbGVtZW50J3MgY2xhc3MgbGlzdCBiYXNlZCBvbiB0aGUgdmFsdWUgaW4gdGhlIGNsYXNzZXMgb2JqZWN0XG4gICAgICAodm5vZGUuZG9tIGFzIERvbUVsZW1lbnQpLmNsYXNzTGlzdC50b2dnbGUobmFtZSwgY2xhc3Nlc1tuYW1lXSk7XG4gICAgfVxuICB9LFxuXG4gIC8vIFRoZSBcInYtaHRtbFwiIGRpcmVjdGl2ZSBzZXRzIHRoZSBpbm5lciBIVE1MIG9mIGFuIGVsZW1lbnQgdG8gdGhlIGdpdmVuIEhUTUwgc3RyaW5nXG4gIFwidi1odG1sXCI6IChodG1sOiBzdHJpbmcsIHZub2RlOiBWbm9kZVdpdGhEb20pID0+IHtcbiAgICAvLyBTZXQgdGhlIGNoaWxkcmVuIG9mIHRoZSB2bm9kZSB0byBhIHRydXN0ZWQgdmVyc2lvbiBvZiB0aGUgSFRNTCBzdHJpbmdcbiAgICB2bm9kZS5jaGlsZHJlbiA9IFt0cnVzdChodG1sKV07XG4gIH0sXG5cbiAgLy8gVGhlIFwidi1tb2RlbFwiIGRpcmVjdGl2ZSBiaW5kcyB0aGUgdmFsdWUgb2YgYW4gaW5wdXQgZWxlbWVudCB0byBhIG1vZGVsIHByb3BlcnR5XG4gIFwidi1tb2RlbFwiOiAoW21vZGVsLCBwcm9wZXJ0eSwgZXZlbnRdOiBhbnlbXSwgdm5vZGU6IFZub2RlV2l0aERvbSwgb2xkVm5vZGU/OiBWbm9kZVdpdGhEb20pID0+IHtcbiAgICBsZXQgdmFsdWU7XG4gICAgLy8gVGhpcyBmdW5jdGlvbiB1cGRhdGVzIHRoZSBtb2RlbCBwcm9wZXJ0eSB3aGVuIHRoZSBpbnB1dCBlbGVtZW50J3MgdmFsdWUgY2hhbmdlc1xuICAgIGxldCBoYW5kbGVyID0gKGU6IEV2ZW50KSA9PiAobW9kZWxbcHJvcGVydHldID0gKGUudGFyZ2V0IGFzIERvbUVsZW1lbnQgJiBSZWNvcmQ8c3RyaW5nLCBhbnk+KS52YWx1ZSk7XG4gICAgaWYgKHZub2RlLnRhZyA9PT0gXCJpbnB1dFwiKSB7XG4gICAgICAvLyBJZiB0aGUgZWxlbWVudCBpcyBhbiBpbnB1dCwgdXNlIHRoZSBcImlucHV0XCIgZXZlbnQgYnkgZGVmYXVsdFxuICAgICAgZXZlbnQgPSBldmVudCB8fCBcIm9uaW5wdXRcIjtcbiAgICAgIC8vIERlcGVuZGluZyBvbiB0aGUgdHlwZSBvZiBpbnB1dCBlbGVtZW50LCB1c2UgYSBkaWZmZXJlbnQgaGFuZGxlciBmdW5jdGlvblxuICAgICAgc3dpdGNoICh2bm9kZS5wcm9wcy50eXBlKSB7XG4gICAgICAgIGNhc2UgXCJjaGVja2JveFwiOiB7XG4gICAgICAgICAgaWYgKEFycmF5LmlzQXJyYXkobW9kZWxbcHJvcGVydHldKSkge1xuICAgICAgICAgICAgLy8gSWYgdGhlIG1vZGVsIHByb3BlcnR5IGlzIGFuIGFycmF5LCBhZGQgb3IgcmVtb3ZlIHRoZSB2YWx1ZSBmcm9tIHRoZSBhcnJheSB3aGVuIHRoZSBjaGVja2JveCBpcyBjaGVja2VkIG9yIHVuY2hlY2tlZFxuICAgICAgICAgICAgaGFuZGxlciA9IChlOiBFdmVudCkgPT4ge1xuICAgICAgICAgICAgICBsZXQgdmFsID0gKGUudGFyZ2V0IGFzIERvbUVsZW1lbnQgJiBSZWNvcmQ8c3RyaW5nLCBhbnk+KS52YWx1ZTtcbiAgICAgICAgICAgICAgbGV0IGlkeCA9IG1vZGVsW3Byb3BlcnR5XS5pbmRleE9mKHZhbCk7XG4gICAgICAgICAgICAgIGlmIChpZHggPT09IC0xKSB7XG4gICAgICAgICAgICAgICAgbW9kZWxbcHJvcGVydHldLnB1c2godmFsKTtcbiAgICAgICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgICAgICBtb2RlbFtwcm9wZXJ0eV0uc3BsaWNlKGlkeCwgMSk7XG4gICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIH07XG4gICAgICAgICAgICAvLyBJZiB0aGUgdmFsdWUgaXMgaW4gdGhlIGFycmF5LCBzZXQgdGhlIGNoZWNrYm94IHRvIGJlIGNoZWNrZWRcbiAgICAgICAgICAgIHZhbHVlID0gbW9kZWxbcHJvcGVydHldLmluZGV4T2Yodm5vZGUuZG9tLnZhbHVlKSAhPT0gLTE7XG4gICAgICAgICAgfSBlbHNlIGlmIChcInZhbHVlXCIgaW4gdm5vZGUucHJvcHMpIHtcbiAgICAgICAgICAgIC8vIElmIHRoZSBpbnB1dCBlbGVtZW50IGhhcyBhIFwidmFsdWVcIiBhdHRyaWJ1dGUsIHVzZSBpdCB0byBkZXRlcm1pbmUgdGhlIGNoZWNrZWQgc3RhdGVcbiAgICAgICAgICAgIGhhbmRsZXIgPSAoKSA9PiB7XG4gICAgICAgICAgICAgIGlmIChtb2RlbFtwcm9wZXJ0eV0gPT09IHZub2RlLnByb3BzLnZhbHVlKSB7XG4gICAgICAgICAgICAgICAgbW9kZWxbcHJvcGVydHldID0gbnVsbDtcbiAgICAgICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgICAgICBtb2RlbFtwcm9wZXJ0eV0gPSB2bm9kZS5wcm9wcy52YWx1ZTtcbiAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfTtcbiAgICAgICAgICAgIHZhbHVlID0gbW9kZWxbcHJvcGVydHldID09PSB2bm9kZS5wcm9wcy52YWx1ZTtcbiAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgLy8gSWYgdGhlcmUgaXMgbm8gXCJ2YWx1ZVwiIGF0dHJpYnV0ZSwgdXNlIGEgYm9vbGVhbiB2YWx1ZSBmb3IgdGhlIG1vZGVsIHByb3BlcnR5XG4gICAgICAgICAgICBoYW5kbGVyID0gKCkgPT4gKG1vZGVsW3Byb3BlcnR5XSA9ICFtb2RlbFtwcm9wZXJ0eV0pO1xuICAgICAgICAgICAgdmFsdWUgPSBtb2RlbFtwcm9wZXJ0eV07XG4gICAgICAgICAgfVxuICAgICAgICAgIC8vIFNldCB0aGUgXCJjaGVja2VkXCIgYXR0cmlidXRlIG9uIHRoZSBpbnB1dCBlbGVtZW50XG4gICAgICAgICAgLy8gZXNsaW50LWRpc2FibGUtbmV4dC1saW5lIG5vLXVzZS1iZWZvcmUtZGVmaW5lXG4gICAgICAgICAgc2hhcmVkU2V0QXR0cmlidXRlKFwiY2hlY2tlZFwiLCB2YWx1ZSwgdm5vZGUpO1xuICAgICAgICAgIGJyZWFrO1xuICAgICAgICB9XG4gICAgICAgIGNhc2UgXCJyYWRpb1wiOiB7XG4gICAgICAgICAgLy8gSWYgdGhlIGVsZW1lbnQgaXMgYSByYWRpbyBidXR0b24sIHNldCB0aGUgXCJjaGVja2VkXCIgYXR0cmlidXRlIGJhc2VkIG9uIHRoZSB2YWx1ZSBvZiB0aGUgbW9kZWwgcHJvcGVydHlcbiAgICAgICAgICAvLyBlc2xpbnQtZGlzYWJsZS1uZXh0LWxpbmUgbm8tdXNlLWJlZm9yZS1kZWZpbmVcbiAgICAgICAgICBzaGFyZWRTZXRBdHRyaWJ1dGUoXCJjaGVja2VkXCIsIG1vZGVsW3Byb3BlcnR5XSA9PT0gdm5vZGUuZG9tLnZhbHVlLCB2bm9kZSk7XG4gICAgICAgICAgYnJlYWs7XG4gICAgICAgIH1cbiAgICAgICAgZGVmYXVsdDoge1xuICAgICAgICAgIC8vIEZvciBhbGwgb3RoZXIgaW5wdXQgdHlwZXMsIHNldCB0aGUgXCJ2YWx1ZVwiIGF0dHJpYnV0ZSBiYXNlZCBvbiB0aGUgdmFsdWUgb2YgdGhlIG1vZGVsIHByb3BlcnR5XG4gICAgICAgICAgLy8gZXNsaW50LWRpc2FibGUtbmV4dC1saW5lIG5vLXVzZS1iZWZvcmUtZGVmaW5lXG4gICAgICAgICAgc2hhcmVkU2V0QXR0cmlidXRlKFwidmFsdWVcIiwgbW9kZWxbcHJvcGVydHldLCB2bm9kZSk7XG4gICAgICAgIH1cbiAgICAgIH1cbiAgICB9IGVsc2UgaWYgKHZub2RlLnRhZyA9PT0gXCJzZWxlY3RcIikge1xuICAgICAgLy8gSWYgdGhlIGVsZW1lbnQgaXMgYSBzZWxlY3QgZWxlbWVudCwgdXNlIHRoZSBcImNsaWNrXCIgZXZlbnQgYnkgZGVmYXVsdFxuICAgICAgZXZlbnQgPSBldmVudCB8fCBcIm9uY2xpY2tcIjtcbiAgICAgIGlmICh2bm9kZS5wcm9wcy5tdWx0aXBsZSkge1xuICAgICAgICAvLyBJZiB0aGUgc2VsZWN0IGVsZW1lbnQgYWxsb3dzIG11bHRpcGxlIHNlbGVjdGlvbnMsIHVwZGF0ZSB0aGUgbW9kZWwgcHJvcGVydHkgd2l0aCBhbiBhcnJheSBvZiBzZWxlY3RlZCB2YWx1ZXNcbiAgICAgICAgaGFuZGxlciA9IChlOiBFdmVudCAmIFJlY29yZDxzdHJpbmcsIGFueT4pID0+IHtcbiAgICAgICAgICBsZXQgdmFsID0gKGUudGFyZ2V0IGFzIERvbUVsZW1lbnQgJiBSZWNvcmQ8c3RyaW5nLCBhbnk+KS52YWx1ZTtcbiAgICAgICAgICBpZiAoZS5jdHJsS2V5KSB7XG4gICAgICAgICAgICAvLyBJZiB0aGUgQ3RybCBrZXkgaXMgcHJlc3NlZCwgYWRkIG9yIHJlbW92ZSB0aGUgdmFsdWUgZnJvbSB0aGUgYXJyYXlcbiAgICAgICAgICAgIGxldCBpZHggPSBtb2RlbFtwcm9wZXJ0eV0uaW5kZXhPZih2YWwpO1xuICAgICAgICAgICAgaWYgKGlkeCA9PT0gLTEpIHtcbiAgICAgICAgICAgICAgbW9kZWxbcHJvcGVydHldLnB1c2godmFsKTtcbiAgICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICAgIG1vZGVsW3Byb3BlcnR5XS5zcGxpY2UoaWR4LCAxKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgLy8gSWYgdGhlIEN0cmwga2V5IGlzIG5vdCBwcmVzc2VkLCBzZXQgdGhlIG1vZGVsIHByb3BlcnR5IHRvIGFuIGFycmF5IHdpdGggdGhlIHNlbGVjdGVkIHZhbHVlXG4gICAgICAgICAgICBtb2RlbFtwcm9wZXJ0eV0uc3BsaWNlKDAsIG1vZGVsW3Byb3BlcnR5XS5sZW5ndGgpO1xuICAgICAgICAgICAgbW9kZWxbcHJvcGVydHldLnB1c2godmFsKTtcbiAgICAgICAgICB9XG4gICAgICAgIH07XG4gICAgICAgIC8vIFNldCB0aGUgXCJzZWxlY3RlZFwiIGF0dHJpYnV0ZSBvbiB0aGUgb3B0aW9ucyBiYXNlZCBvbiB3aGV0aGVyIHRoZXkgYXJlIGluIHRoZSBtb2RlbCBwcm9wZXJ0eSBhcnJheVxuICAgICAgICB2bm9kZS5jaGlsZHJlbi5mb3JFYWNoKChjaGlsZDogVm5vZGVJbnRlcmZhY2UpID0+IHtcbiAgICAgICAgICBpZiAoY2hpbGQudGFnID09PSBcIm9wdGlvblwiKSB7XG4gICAgICAgICAgICBsZXQgdmFsdWUgPSBcInZhbHVlXCIgaW4gY2hpbGQucHJvcHMgPyBjaGlsZC5wcm9wcy52YWx1ZSA6IGNoaWxkLmNoaWxkcmVuLmpvaW4oXCJcIikudHJpbSgpO1xuICAgICAgICAgICAgY2hpbGQucHJvcHMuc2VsZWN0ZWQgPSBtb2RlbFtwcm9wZXJ0eV0uaW5kZXhPZih2YWx1ZSkgIT09IC0xO1xuICAgICAgICAgIH1cbiAgICAgICAgfSk7XG4gICAgICB9IGVsc2Uge1xuICAgICAgICAvLyBJZiB0aGUgc2VsZWN0IGVsZW1lbnQgZG9lcyBub3QgYWxsb3cgbXVsdGlwbGUgc2VsZWN0aW9ucywgc2V0IHRoZSBcInNlbGVjdGVkXCIgYXR0cmlidXRlIG9uIHRoZSBvcHRpb25zIGJhc2VkIG9uIHRoZSB2YWx1ZSBvZiB0aGUgbW9kZWwgcHJvcGVydHlcbiAgICAgICAgdm5vZGUuY2hpbGRyZW4uZm9yRWFjaCgoY2hpbGQ6IFZub2RlSW50ZXJmYWNlKSA9PiB7XG4gICAgICAgICAgaWYgKGNoaWxkLnRhZyA9PT0gXCJvcHRpb25cIikge1xuICAgICAgICAgICAgbGV0IHZhbHVlID0gXCJ2YWx1ZVwiIGluIGNoaWxkLnByb3BzID8gY2hpbGQucHJvcHMudmFsdWUgOiBjaGlsZC5jaGlsZHJlbi5qb2luKFwiXCIpLnRyaW0oKTtcbiAgICAgICAgICAgIGNoaWxkLnByb3BzLnNlbGVjdGVkID0gdmFsdWUgPT09IG1vZGVsW3Byb3BlcnR5XTtcbiAgICAgICAgICB9XG4gICAgICAgIH0pO1xuICAgICAgfVxuICAgIH0gZWxzZSBpZiAodm5vZGUudGFnID09PSBcInRleHRhcmVhXCIpIHtcbiAgICAgIC8vIElmIHRoZSBlbGVtZW50IGlzIGEgdGV4dGFyZWEsIHVzZSB0aGUgXCJpbnB1dFwiIGV2ZW50IGJ5IGRlZmF1bHRcbiAgICAgIGV2ZW50ID0gZXZlbnQgfHwgXCJvbmlucHV0XCI7XG4gICAgICAvLyBTZXQgdGhlIHRleHRhcmVhJ3MgY29udGVudCB0byB0aGUgdmFsdWUgb2YgdGhlIG1vZGVsIHByb3BlcnR5XG4gICAgICB2bm9kZS5jaGlsZHJlbiA9IFttb2RlbFtwcm9wZXJ0eV1dO1xuICAgIH1cblxuICAgIC8vIFdlIGFzc3VtZSB0aGF0IHRoZSBwcmV2IGhhbmRsZXIgaWYgYW55IHdpbGwgbm90IGJlIGNoYW5nZWQgYnkgdGhlIHVzZXIgYWNyb3NzIHBhdGNoc1xuICAgIGxldCBwcmV2SGFuZGxlciA9IHZub2RlLnByb3BzW2V2ZW50XTtcblxuICAgIC8vIFNldCB0aGUgZXZlbnQgaGFuZGxlciBvbiB0aGUgZWxlbWVudFxuICAgIC8vIGVzbGludC1kaXNhYmxlLW5leHQtbGluZSBuby11c2UtYmVmb3JlLWRlZmluZVxuICAgIHNoYXJlZFNldEF0dHJpYnV0ZShcbiAgICAgIGV2ZW50LFxuICAgICAgKGU6IEV2ZW50KSA9PiB7XG4gICAgICAgIGhhbmRsZXIoZSk7XG5cbiAgICAgICAgLy8gSWYgdGhlIHByZXZpb3VzIGhhbmRsZXIgaXMgZGVmaW5lZCwgY2FsbCBpdCBhZnRlciB0aGUgbW9kZWwgaGFzIGJlZW4gdXBkYXRlZFxuICAgICAgICBpZiAocHJldkhhbmRsZXIpIHtcbiAgICAgICAgICBwcmV2SGFuZGxlcihlKTtcbiAgICAgICAgfVxuICAgICAgfSxcbiAgICAgIHZub2RlLFxuICAgICAgb2xkVm5vZGVcbiAgICApO1xuICB9LFxuXG4gIC8vIFRoZSBcInYtY3JlYXRlXCIgZGlyZWN0aXZlIGlzIGNhbGxlZCB3aGVuIGEgbmV3IHZpcnR1YWwgbm9kZSBpcyBjcmVhdGVkLlxuICAvLyBUaGUgcHJvdmlkZWQgY2FsbGJhY2sgZnVuY3Rpb24gaXMgY2FsbGVkIHdpdGggdGhlIG5ldyB2aXJ0dWFsIG5vZGUgYXMgYW4gYXJndW1lbnQuXG4gIC8vIFRoaXMgZGlyZWN0aXZlIGlzIG9ubHkgY2FsbGVkIG9uY2UgcGVyIHZpcnR1YWwgbm9kZSwgd2hlbiBpdCBpcyBmaXJzdCBjcmVhdGVkLlxuICAvLyBlc2xpbnQtZGlzYWJsZS1uZXh0LWxpbmUgbm8tdW51c2VkLXZhcnNcbiAgXCJ2LWNyZWF0ZVwiOiAoY2FsbGJhY2s6ICh2bm9kZTogVm5vZGVXaXRoRG9tKSA9PiB2b2lkLCB2bm9kZTogVm5vZGVXaXRoRG9tLCBvbGRWbm9kZT86IFZub2RlV2l0aERvbSkgPT4ge1xuICAgIC8vIElmIHRoaXMgaXMgbm90IGFuIHVwZGF0ZSwgY2FsbCB0aGUgY2FsbGJhY2sgZnVuY3Rpb24gd2l0aCB0aGUgbmV3IHZpcnR1YWwgbm9kZVxuICAgIGlmICghb2xkVm5vZGUpIHtcbiAgICAgIGxldCBjbGVhbnVwID0gY2FsbGJhY2sodm5vZGUpO1xuXG4gICAgICAvLyBJZiB0aGUgY2FsbGJhY2sgZnVuY3Rpb24gcmV0dXJucyBhIGZ1bmN0aW9uLCBjYWxsIGl0IHdoZW4gdGhlIHVwZGF0ZSBpcyBnb25uYSBiZSBjbGVhbmVkIHVwXG4gICAgICBpZiAodHlwZW9mIGNsZWFudXAgPT09IFwiZnVuY3Rpb25cIikge1xuICAgICAgICBvbkNsZWFudXAoY2xlYW51cCk7XG4gICAgICB9XG4gICAgfVxuICB9LFxuXG4gIC8vIFRoZSBcInYtdXBkYXRlXCIgZGlyZWN0aXZlIGlzIGNhbGxlZCB3aGVuIGFuIGV4aXN0aW5nIHZpcnR1YWwgbm9kZSBpcyB1cGRhdGVkLlxuICAvLyBUaGUgcHJvdmlkZWQgY2FsbGJhY2sgZnVuY3Rpb24gaXMgY2FsbGVkIHdpdGggdGhlIG5ldyBhbmQgb2xkIHZpcnR1YWwgbm9kZXMgYXMgYXJndW1lbnRzLlxuICAvLyBUaGlzIGRpcmVjdGl2ZSBpcyBvbmx5IGNhbGxlZCBvbmNlIHBlciB2aXJ0dWFsIG5vZGUgdXBkYXRlLlxuICBcInYtdXBkYXRlXCI6IChcbiAgICAvLyBlc2xpbnQtZGlzYWJsZS1uZXh0LWxpbmUgbm8tdW51c2VkLXZhcnNcbiAgICBjYWxsYmFjazogKHZub2RlOiBWbm9kZVdpdGhEb20sIG9sZFZub2RlOiBWbm9kZVdpdGhEb20pID0+IHZvaWQsXG4gICAgdm5vZGU6IFZub2RlV2l0aERvbSxcbiAgICBvbGRWbm9kZT86IFZub2RlV2l0aERvbVxuICApID0+IHtcbiAgICAvLyBJZiB0aGlzIGlzIGFuIHVwZGF0ZSwgY2FsbCB0aGUgY2FsbGJhY2sgZnVuY3Rpb24gd2l0aCB0aGUgbmV3IGFuZCBvbGQgdmlydHVhbCBub2Rlc1xuICAgIGlmIChvbGRWbm9kZSkge1xuICAgICAgbGV0IGNsZWFudXAgPSBjYWxsYmFjayh2bm9kZSwgb2xkVm5vZGUpO1xuXG4gICAgICAvLyBJZiB0aGUgY2FsbGJhY2sgZnVuY3Rpb24gcmV0dXJucyBhIGZ1bmN0aW9uLCBjYWxsIGl0IHdoZW4gdGhlIHVwZGF0ZSBpcyBnb25uYSBiZSBjbGVhbmVkIHVwXG4gICAgICBpZiAodHlwZW9mIGNsZWFudXAgPT09IFwiZnVuY3Rpb25cIikge1xuICAgICAgICBvbkNsZWFudXAoY2xlYW51cCk7XG4gICAgICB9XG4gICAgfVxuICB9LFxuXG4gIC8vIFRoZSBcInYtY2xlYW51cFwiIGRpcmVjdGl2ZSBpcyBjYWxsZWQgd2hlbiB0aGUgdXBkYXRlIGlzIGNsZWFuZWQgdXAuXG4gIC8vIFRoZSBwcm92aWRlZCBjYWxsYmFjayBmdW5jdGlvbiBpcyBjYWxsZWQgd2l0aCB0aGUgb2xkIHZpcnR1YWwgbm9kZSBhcyBhbiBhcmd1bWVudC5cbiAgLy8gVGhpcyBkaXJlY3RpdmUgaXMgb25seSBjYWxsZWQgb25jZSBwZXIgdmlydHVhbCBub2RlLCB3aGVuIHRoZSB1cGRhdGUgaXMgY2xlYW5lZCB1cC5cbiAgXCJ2LWNsZWFudXBcIjogKFxuICAgIC8vIGVzbGludC1kaXNhYmxlLW5leHQtbGluZSBuby11bnVzZWQtdmFyc1xuICAgIGNhbGxiYWNrOiAodm5vZGU6IFZub2RlV2l0aERvbSwgb2xkVm5vZGU/OiBWbm9kZVdpdGhEb20pID0+IHZvaWQsXG4gICAgdm5vZGU6IFZub2RlV2l0aERvbSxcbiAgICBvbGRWbm9kZT86IFZub2RlV2l0aERvbVxuICApID0+IHtcbiAgICAvLyBBZGQgdGhlIGNhbGxiYWNrIGZ1bmN0aW9uIHRvIHRoZSBsaXN0IG9mIGNsZWFudXAgZnVuY3Rpb25zIHRvIGJlIGNhbGxlZCB3aGVuIHRoZSB1cGRhdGUgaXMgY2xlYW5lZCB1cFxuICAgIG9uQ2xlYW51cCgoKSA9PiBjYWxsYmFjayh2bm9kZSwgb2xkVm5vZGUpKTtcbiAgfVxufTtcbi8vIEFkZCBhIGRpcmVjdGl2ZSB0byB0aGUgZ2xvYmFsIGRpcmVjdGl2ZXMgb2JqZWN0LCB3aXRoIHRoZSBrZXkgYmVpbmcgdGhlIG5hbWVcbi8vIHByZWNlZGVkIGJ5IFwidi1cIi4gQWxzbyBhZGQgdGhlIG5hbWUgdG8gdGhlIGdsb2JhbCByZXNlcnZlZFByb3BzIG9iamVjdC5cbmV4cG9ydCBmdW5jdGlvbiBkaXJlY3RpdmUobmFtZTogc3RyaW5nLCBkaXJlY3RpdmU6IERpcmVjdGl2ZSkge1xuICBsZXQgZGlyZWN0aXZlTmFtZSA9IGB2LSR7bmFtZX1gO1xuICBkaXJlY3RpdmVzW2RpcmVjdGl2ZU5hbWVdID0gZGlyZWN0aXZlO1xuICByZXNlcnZlZFByb3BzW2RpcmVjdGl2ZU5hbWVdID0gdHJ1ZTtcbn1cblxuLy8gU2V0IGFuIGF0dHJpYnV0ZSBvbiBhIHZpcnR1YWwgRE9NIG5vZGUgYW5kIHVwZGF0ZSB0aGUgYWN0dWFsIERPTSBlbGVtZW50LlxuLy8gSWYgdGhlIGF0dHJpYnV0ZSB2YWx1ZSBpcyBhIGZ1bmN0aW9uLCBhZGQgYW4gZXZlbnQgbGlzdGVuZXIgZm9yIHRoZSBhdHRyaWJ1dGVcbi8vIG5hbWUgdG8gdGhlIERPTSBlbGVtZW50IHJlcHJlc2VudGVkIGJ5IG1haW5Wbm9kZS5cbi8vIElmIG9sZFZub2RlIGlzIHByb3ZpZGVkLCBjb21wYXJlIHRoZSBuZXcgYXR0cmlidXRlIHZhbHVlIHRvIHRoZSBvbGQgdmFsdWVcbi8vIGFuZCBvbmx5IHVwZGF0ZSB0aGUgYXR0cmlidXRlIGlmIHRoZSB2YWx1ZXMgYXJlIGRpZmZlcmVudC5cbmZ1bmN0aW9uIHNoYXJlZFNldEF0dHJpYnV0ZShuYW1lOiBzdHJpbmcsIHZhbHVlOiBhbnksIG5ld1Zub2RlOiBWbm9kZVdpdGhEb20sIG9sZFZub2RlPzogVm5vZGVXaXRoRG9tKTogdm9pZCB8IGJvb2xlYW4ge1xuICAvLyBJZiB0aGUgYXR0cmlidXRlIHZhbHVlIGlzIGEgZnVuY3Rpb24sIGFkZCBhbiBldmVudCBsaXN0ZW5lciBmb3IgdGhlIGF0dHJpYnV0ZVxuICAvLyBuYW1lIHRvIHRoZSBET00gZWxlbWVudCByZXByZXNlbnRlZCBieSBtYWluVm5vZGUuXG4gIGlmICh0eXBlb2YgdmFsdWUgPT09IFwiZnVuY3Rpb25cIikge1xuICAgIC8vIE9ubHkgYWRkIHRoZSBldmVudCBsaXN0ZW5lciBpZiBpdCBoYXNuJ3QgYmVlbiBhZGRlZCB5ZXQuXG4gICAgaWYgKG5hbWUgaW4gZXZlbnRMaXN0ZW5lck5hbWVzID09PSBmYWxzZSkge1xuICAgICAgKG1haW5Wbm9kZSBhcyBWbm9kZVdpdGhEb20pLmRvbS5hZGRFdmVudExpc3RlbmVyKG5hbWUuc2xpY2UoMiksIGV2ZW50TGlzdGVuZXIpO1xuICAgICAgZXZlbnRMaXN0ZW5lck5hbWVzW25hbWVdID0gdHJ1ZTtcbiAgICB9XG4gICAgbmV3Vm5vZGUuZG9tW2B2LSR7bmFtZX1gXSA9IHZhbHVlO1xuICAgIHJldHVybjtcbiAgfVxuXG4gIC8vIElmIHRoZSBhdHRyaWJ1dGUgaXMgcHJlc2VudCBvbiB0aGUgRE9NIGVsZW1lbnQgYW5kIG5ld1Zub2RlIGlzIG5vdCBhbiBTVkcsXG4gIC8vIHVwZGF0ZSB0aGUgYXR0cmlidXRlIGlmIHRoZSB2YWx1ZSBoYXMgY2hhbmdlZC5cbiAgaWYgKG5hbWUgaW4gbmV3Vm5vZGUuZG9tICYmIG5ld1Zub2RlLmlzU1ZHID09PSBmYWxzZSkge1xuICAgIC8vIGVzbGludC1kaXNhYmxlLW5leHQtbGluZSBlcWVxZXFcbiAgICBpZiAobmV3Vm5vZGUuZG9tW25hbWVdICE9IHZhbHVlKSB7XG4gICAgICBuZXdWbm9kZS5kb21bbmFtZV0gPSB2YWx1ZTtcbiAgICB9XG4gICAgcmV0dXJuO1xuICB9XG5cbiAgLy8gSWYgb2xkVm5vZGUgaXMgbm90IHByb3ZpZGVkIG9yIHRoZSBhdHRyaWJ1dGUgdmFsdWUgaGFzIGNoYW5nZWQsIHVwZGF0ZSB0aGVcbiAgLy8gYXR0cmlidXRlIG9uIHRoZSBET00gZWxlbWVudC5cbiAgaWYgKCFvbGRWbm9kZSB8fCB2YWx1ZSAhPT0gb2xkVm5vZGUucHJvcHNbbmFtZV0pIHtcbiAgICBpZiAodmFsdWUgPT09IGZhbHNlKSB7XG4gICAgICBuZXdWbm9kZS5kb20ucmVtb3ZlQXR0cmlidXRlKG5hbWUpO1xuICAgIH0gZWxzZSB7XG4gICAgICBuZXdWbm9kZS5kb20uc2V0QXR0cmlidXRlKG5hbWUsIHZhbHVlKTtcbiAgICB9XG4gIH1cbn1cblxuLy8gU2V0IGFuIGF0dHJpYnV0ZSBvbiBhIHZpcnR1YWwgRE9NIG5vZGUgYW5kIHVwZGF0ZSB0aGUgYWN0dWFsIERPTSBlbGVtZW50LlxuLy8gU2tpcCB0aGUgYXR0cmlidXRlIGlmIGl0IGlzIGluIHRoZSByZXNlcnZlZFByb3BzIG9iamVjdC5cbmV4cG9ydCBmdW5jdGlvbiBzZXRBdHRyaWJ1dGUobmFtZTogc3RyaW5nLCB2YWx1ZTogYW55LCBuZXdWbm9kZTogVm5vZGVXaXRoRG9tLCBvbGRWbm9kZT86IFZub2RlV2l0aERvbSk6IHZvaWQge1xuICBpZiAobmFtZSBpbiByZXNlcnZlZFByb3BzKSB7XG4gICAgcmV0dXJuO1xuICB9XG4gIG5ld1Zub2RlLnByb3BzW25hbWVdID0gdmFsdWU7XG4gIHNoYXJlZFNldEF0dHJpYnV0ZShuYW1lLCB2YWx1ZSwgbmV3Vm5vZGUgYXMgVm5vZGVXaXRoRG9tLCBvbGRWbm9kZSk7XG59XG5cbi8vIFVwZGF0ZSB0aGUgYXR0cmlidXRlcyBvbiBhIHZpcnR1YWwgRE9NIG5vZGUuIElmIG9sZFZub2RlIGlzIHByb3ZpZGVkLCByZW1vdmVcbi8vIGF0dHJpYnV0ZXMgZnJvbSB0aGUgRE9NIGVsZW1lbnQgdGhhdCBhcmUgbm90IHByZXNlbnQgaW4gbmV3Vm5vZGUucHJvcHMgYnV0IGFyZVxuLy8gcHJlc2VudCBpbiBvbGRWbm9kZS5wcm9wcy4gVGhlbiwgaXRlcmF0ZSBvdmVyIHRoZSBhdHRyaWJ1dGVzIGluIG5ld1Zub2RlLnByb3BzXG4vLyBhbmQgdXBkYXRlIHRoZSBET00gZWxlbWVudCB3aXRoIHRoZSBhdHRyaWJ1dGVzIHVzaW5nIHRoZSBzaGFyZWRTZXRBdHRyaWJ1dGVcbi8vIGZ1bmN0aW9uLiBJZiBhbiBhdHRyaWJ1dGUgaXMgaW4gdGhlIHJlc2VydmVkUHJvcHMgb2JqZWN0IGFuZCBoYXMgYSBjb3JyZXNwb25kaW5nXG4vLyBkaXJlY3RpdmUgaW4gdGhlIGRpcmVjdGl2ZXMgb2JqZWN0LCBjYWxsIHRoZSBkaXJlY3RpdmUgd2l0aCB0aGUgYXR0cmlidXRlIHZhbHVlXG4vLyBhbmQgdGhlIHR3byB2aXJ0dWFsIERPTSBub2RlcyBhcyBhcmd1bWVudHMuIElmIHRoZSBkaXJlY3RpdmUgcmV0dXJucyBmYWxzZSwgZXhpdFxuLy8gdGhlIGxvb3AuXG5leHBvcnQgZnVuY3Rpb24gdXBkYXRlQXR0cmlidXRlcyhuZXdWbm9kZTogVm5vZGVXaXRoRG9tLCBvbGRWbm9kZT86IFZub2RlV2l0aERvbSk6IHZvaWQge1xuICAvLyBJZiBvbGRWbm9kZSBpcyBwcm92aWRlZCwgcmVtb3ZlIGF0dHJpYnV0ZXMgZnJvbSB0aGUgRE9NIGVsZW1lbnQgdGhhdCBhcmUgbm90XG4gIC8vIHByZXNlbnQgaW4gbmV3Vm5vZGUucHJvcHMgYnV0IGFyZSBwcmVzZW50IGluIG9sZFZub2RlLnByb3BzLlxuICBpZiAob2xkVm5vZGUpIHtcbiAgICBmb3IgKGxldCBuYW1lIGluIG9sZFZub2RlLnByb3BzKSB7XG4gICAgICBpZiAobmFtZSBpbiBuZXdWbm9kZS5wcm9wcyA9PT0gZmFsc2UgJiYgbmFtZSBpbiBldmVudExpc3RlbmVyTmFtZXMgPT09IGZhbHNlICYmIG5hbWUgaW4gcmVzZXJ2ZWRQcm9wcyA9PT0gZmFsc2UpIHtcbiAgICAgICAgaWYgKG5hbWUgaW4gbmV3Vm5vZGUuZG9tICYmIG5ld1Zub2RlLmlzU1ZHID09PSBmYWxzZSkge1xuICAgICAgICAgIG5ld1Zub2RlLmRvbVtuYW1lXSA9IG51bGw7XG4gICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgbmV3Vm5vZGUuZG9tLnJlbW92ZUF0dHJpYnV0ZShuYW1lKTtcbiAgICAgICAgfVxuICAgICAgfVxuICAgIH1cbiAgfVxuXG4gIC8vIEl0ZXJhdGUgb3ZlciB0aGUgYXR0cmlidXRlcyBpbiBuZXdWbm9kZS5wcm9wcyBhbmQgdXBkYXRlIHRoZSBET00gZWxlbWVudCB3aXRoXG4gIC8vIHRoZSBhdHRyaWJ1dGVzIHVzaW5nIHRoZSBzaGFyZWRTZXRBdHRyaWJ1dGUgZnVuY3Rpb24uXG4gIGZvciAobGV0IG5hbWUgaW4gbmV3Vm5vZGUucHJvcHMpIHtcbiAgICBpZiAobmFtZSBpbiByZXNlcnZlZFByb3BzKSB7XG4gICAgICAvLyBJZiB0aGVyZSBpcyBhIGRpcmVjdGl2ZSBmb3IgdGhlIGF0dHJpYnV0ZSwgY2FsbCBpdCB3aXRoIHRoZSBhdHRyaWJ1dGUgdmFsdWVcbiAgICAgIC8vIGFuZCB0aGUgdHdvIHZpcnR1YWwgRE9NIG5vZGVzIGFzIGFyZ3VtZW50cy4gSWYgdGhlIGRpcmVjdGl2ZSByZXR1cm5zIGZhbHNlLFxuICAgICAgLy8gZXhpdCB0aGUgbG9vcC5cbiAgICAgIGlmIChuYW1lIGluIGRpcmVjdGl2ZXMgJiYgZGlyZWN0aXZlc1tuYW1lXShuZXdWbm9kZS5wcm9wc1tuYW1lXSwgbmV3Vm5vZGUsIG9sZFZub2RlKSA9PT0gZmFsc2UpIHtcbiAgICAgICAgYnJlYWs7XG4gICAgICB9XG4gICAgICBjb250aW51ZTtcbiAgICB9XG4gICAgc2hhcmVkU2V0QXR0cmlidXRlKG5hbWUsIG5ld1Zub2RlLnByb3BzW25hbWVdLCBuZXdWbm9kZSwgb2xkVm5vZGUpO1xuICB9XG59XG5cbi8qIHBhdGNoIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0gKi9cblxuLy8gUGF0Y2ggYSBET00gbm9kZSB3aXRoIGEgbmV3IFZOb2RlIHRyZWVcbmV4cG9ydCBmdW5jdGlvbiBwYXRjaChuZXdWbm9kZTogVm5vZGVXaXRoRG9tLCBvbGRWbm9kZT86IFZub2RlV2l0aERvbSk6IHZvaWQge1xuICAvLyBHZXQgdGhlIGNoaWxkcmVuIG9mIHRoZSBuZXcgYW5kIG9sZCB2aXJ0dWFsIERPTSBub2Rlc1xuICBsZXQgbmV3VHJlZSA9IG5ld1Zub2RlLmNoaWxkcmVuO1xuICBsZXQgb2xkVHJlZSA9IG9sZFZub2RlPy5jaGlsZHJlbiB8fCBbXTtcbiAgLy8gR2V0IHRoZSBsZW5ndGggb2YgdGhlIG9sZCB0cmVlXG4gIGxldCBvbGRUcmVlTGVuZ3RoID0gb2xkVHJlZS5sZW5ndGg7XG5cbiAgLy8gSWYgdGhlIG9sZCB0cmVlIGhhcyBjaGlsZHJlbiBhbmQgdGhlIGZpcnN0IGNoaWxkIG9mIHRoZSBuZXcgdHJlZSBpcyBhIFZOb2RlIHdpdGggYSBcImtleVwiXG4gIC8vIGF0dHJpYnV0ZSBhbmQgdGhlIGZpcnN0IGNoaWxkIG9mIHRoZSBvbGQgdHJlZSBpcyBhIFZOb2RlIHdpdGggYSBcImtleVwiIGF0dHJpYnV0ZSwgdXBkYXRlXG4gIC8vIHRoZSBET00gZWxlbWVudCBpbiBwbGFjZSBieSBjb21wYXJpbmcgdGhlIGtleXMgb2YgdGhlIG5vZGVzIGluIHRoZSB0cmVlcy5cbiAgaWYgKG9sZFRyZWVMZW5ndGggJiYgbmV3VHJlZVswXSBpbnN0YW5jZW9mIFZub2RlICYmIFwia2V5XCIgaW4gbmV3VHJlZVswXS5wcm9wcyAmJiBcImtleVwiIGluIG9sZFRyZWVbMF0ucHJvcHMpIHtcbiAgICAvLyBHZXQgdGhlIGxlbmd0aHMgb2YgdGhlIG5ldyBhbmQgb2xkIHRyZWVzXG4gICAgbGV0IG5ld1RyZWVMZW5ndGggPSBuZXdUcmVlLmxlbmd0aDtcblxuICAgIC8vIENyZWF0ZSBhbiBvYmplY3QgdGhhdCBtYXBzIGtleXMgdG8gaW5kaWNlcyBpbiB0aGUgb2xkIHRyZWVcbiAgICBsZXQgb2xkS2V5ZWRMaXN0OiB7IFtrZXk6IHN0cmluZ106IG51bWJlciB9ID0ge307XG4gICAgZm9yIChsZXQgaSA9IDA7IGkgPCBvbGRUcmVlTGVuZ3RoOyBpKyspIHtcbiAgICAgIG9sZEtleWVkTGlzdFtvbGRUcmVlW2ldLnByb3BzLmtleV0gPSBpO1xuICAgIH1cblxuICAgIC8vIENyZWF0ZSBhbiBvYmplY3QgdGhhdCBtYXBzIGtleXMgdG8gaW5kaWNlcyBpbiB0aGUgbmV3IHRyZWVcbiAgICBsZXQgbmV3S2V5ZWRMaXN0OiB7IFtrZXk6IHN0cmluZ106IG51bWJlciB9ID0ge307XG4gICAgZm9yIChsZXQgaSA9IDA7IGkgPCBuZXdUcmVlTGVuZ3RoOyBpKyspIHtcbiAgICAgIG5ld0tleWVkTGlzdFtuZXdUcmVlW2ldLnByb3BzLmtleV0gPSBpO1xuICAgIH1cblxuICAgIC8vIEl0ZXJhdGUgb3ZlciB0aGUgbmV3IHRyZWVcbiAgICBmb3IgKGxldCBpID0gMDsgaSA8IG5ld1RyZWVMZW5ndGg7IGkrKykge1xuICAgICAgLy8gR2V0IHRoZSBjdXJyZW50IG5ldyBjaGlsZCBhbmQgdGhlIGNvcnJlc3BvbmRpbmcgb2xkIGNoaWxkXG4gICAgICBsZXQgbmV3Q2hpbGQgPSBuZXdUcmVlW2ldO1xuICAgICAgbGV0IG9sZENoaWxkID0gb2xkVHJlZVtvbGRLZXllZExpc3RbbmV3Q2hpbGQucHJvcHMua2V5XV07XG4gICAgICAvLyBJbml0aWFsaXplIGEgZmxhZyB0byBkZXRlcm1pbmUgd2hldGhlciB0byBwYXRjaCB0aGUgY2hpbGRcbiAgICAgIGxldCBzaG91bGRQYXRjaCA9IHRydWU7XG5cbiAgICAgIC8vIElmIHRoZSBvbGQgY2hpbGQgZXhpc3RzLCB1cGRhdGUgdGhlIERPTSBlbGVtZW50IG9mIHRoZSBuZXcgY2hpbGQgdG8gbWF0Y2ggdGhlIG9sZCBjaGlsZCdzIERPTSBlbGVtZW50XG4gICAgICBpZiAob2xkQ2hpbGQpIHtcbiAgICAgICAgbmV3Q2hpbGQuZG9tID0gb2xkQ2hpbGQuZG9tO1xuICAgICAgICAvLyBJZiB0aGUgbmV3IGFuZCBvbGQgY2hpbGRyZW4gaGF2ZSB0aGUgc2FtZSBcInYta2VlcFwiIGF0dHJpYnV0ZSB2YWx1ZSwgdXBkYXRlIHRoZSBjaGlsZHJlbiBvZiB0aGUgbmV3IGNoaWxkIHRvIG1hdGNoIHRoZSBvbGQgY2hpbGQncyBjaGlsZHJlblxuICAgICAgICBpZiAoXCJ2LWtlZXBcIiBpbiBuZXdDaGlsZC5wcm9wcyAmJiBuZXdDaGlsZC5wcm9wc1tcInYta2VlcFwiXSA9PT0gb2xkQ2hpbGQucHJvcHNbXCJ2LWtlZXBcIl0pIHtcbiAgICAgICAgICBuZXdDaGlsZC5jaGlsZHJlbiA9IG9sZENoaWxkLmNoaWxkcmVuO1xuICAgICAgICAgIC8vIFNldCB0aGUgc2hvdWxkUGF0Y2ggZmxhZyB0byBmYWxzZVxuICAgICAgICAgIHNob3VsZFBhdGNoID0gZmFsc2U7XG4gICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgdXBkYXRlQXR0cmlidXRlcyhuZXdDaGlsZCwgb2xkQ2hpbGQpO1xuICAgICAgICB9XG5cbiAgICAgICAgLy8gSWYgdGhlIG9sZCBjaGlsZCBkb2VzIG5vdCBleGlzdCwgY3JlYXRlIGEgbmV3IERPTSBlbGVtZW50IGZvciB0aGUgbmV3IGNoaWxkIGFuZCB1cGRhdGUgaXRzIGF0dHJpYnV0ZXNcbiAgICAgIH0gZWxzZSB7XG4gICAgICAgIG5ld0NoaWxkLmRvbSA9IGNyZWF0ZURvbUVsZW1lbnQobmV3Q2hpbGQudGFnLCBuZXdDaGlsZC5pc1NWRyk7XG4gICAgICAgIHVwZGF0ZUF0dHJpYnV0ZXMobmV3Q2hpbGQpO1xuICAgICAgfVxuXG4gICAgICAvLyBJZiB0aGUgbmV3IGNoaWxkJ3MgRE9NIGVsZW1lbnQgaXMgbm90IHRoZSBpLXRoIGNoaWxkIG9mIHRoZSBwYXJlbnQgRE9NIGVsZW1lbnQsIGluc2VydCBpdFxuICAgICAgaWYgKCFuZXdWbm9kZS5kb20uY2hpbGROb2Rlc1tpXSkge1xuICAgICAgICBuZXdWbm9kZS5kb20uYXBwZW5kQ2hpbGQobmV3Q2hpbGQuZG9tKTtcblxuICAgICAgICAvLyBJZiB0aGUgbmV3IGNoaWxkJ3MgRE9NIGVsZW1lbnQgaXMgbm90IHRoZSBzYW1lIGFzIHRoZSBpLXRoIGNoaWxkIG9mIHRoZSBwYXJlbnQgRE9NIGVsZW1lbnQsIHJlcGxhY2UgdGhlIGktdGggY2hpbGQgd2l0aCB0aGUgbmV3IGNoaWxkJ3MgRE9NIGVsZW1lbnRcbiAgICAgIH0gZWxzZSBpZiAobmV3Vm5vZGUuZG9tLmNoaWxkTm9kZXNbaV0gIT09IG5ld0NoaWxkLmRvbSkge1xuICAgICAgICBuZXdWbm9kZS5kb20ucmVwbGFjZUNoaWxkKG5ld0NoaWxkLmRvbSwgbmV3Vm5vZGUuZG9tLmNoaWxkTm9kZXNbaV0pO1xuICAgICAgfVxuXG4gICAgICAvLyBJZiB0aGUgc2hvdWxkUGF0Y2ggZmxhZyBpcyB0cnVlLCByZWN1cnNpdmVseSBjYWxsIHRoZSBwYXRjaCBmdW5jdGlvbiBvbiB0aGUgbmV3IGNoaWxkLCBwYXNzaW5nIGluIHRoZSBvbGQgY2hpbGQgYXMgdGhlIHNlY29uZCBhcmd1bWVudFxuICAgICAgc2hvdWxkUGF0Y2ggJiYgcGF0Y2gobmV3Q2hpbGQsIG9sZENoaWxkKTtcbiAgICB9XG5cbiAgICAvLyBGb3IgdGhlIHJlc3Qgb2YgdGhlIGNoaWxkcmVuLCB3ZSBzaG91bGQgcmVtb3ZlIHRoZW0gZnJvbSB0aGUgRE9NXG4gICAgZm9yIChsZXQgaSA9IG5ld1RyZWVMZW5ndGg7IGkgPCBvbGRUcmVlTGVuZ3RoOyBpKyspIHtcbiAgICAgIC8vIElmIHRoZSBpLXRoIGNoaWxkIG9mIHRoZSBvbGQgdHJlZSBkb2VzIG5vdCBoYXZlIGEgY29ycmVzcG9uZGluZyBrZXkgaW4gdGhlIG5ldyB0cmVlLCByZW1vdmUgaXRzIERPTSBlbGVtZW50IGZyb20gdGhlIHBhcmVudCBET00gZWxlbWVudFxuICAgICAgaWYgKCFuZXdLZXllZExpc3Rbb2xkVHJlZVtpXS5wcm9wcy5rZXldKSB7XG4gICAgICAgIG9sZFRyZWVbaV0uZG9tLnBhcmVudE5vZGUgJiYgb2xkVHJlZVtpXS5kb20ucGFyZW50Tm9kZS5yZW1vdmVDaGlsZChvbGRUcmVlW2ldLmRvbSk7XG4gICAgICB9XG4gICAgfVxuICAgIHJldHVybjtcbiAgfVxuXG4gIC8vIElmIHRoZSBuZXcgdHJlZSBoYXMgbm8gY2hpbGRyZW4sIHNldCB0aGUgdGV4dCBjb250ZW50IG9mIHRoZSBwYXJlbnQgRE9NIGVsZW1lbnQgdG8gYW4gZW1wdHkgc3RyaW5nXG4gIGlmIChuZXdUcmVlLmxlbmd0aCA9PT0gMCkge1xuICAgIG5ld1Zub2RlLmRvbS50ZXh0Q29udGVudCA9IFwiXCI7XG4gICAgcmV0dXJuO1xuICB9XG5cbiAgLy8gU2V0IHRoZSBnbG9iYWwgY3VycmVudCBvYmplY3QgdG8gdGhlIG5ldyBhbmQgb2xkIHZpcnR1YWwgRE9NIG5vZGVzXG4gIGN1cnJlbnQudm5vZGUgPSBuZXdWbm9kZTtcbiAgY3VycmVudC5vbGRWbm9kZSA9IG9sZFZub2RlO1xuXG4gIC8vIEZsYXR0ZW4gdGhlIG5ldyB0cmVlXG4gIGZvciAobGV0IGkgPSAwOyBpIDwgbmV3VHJlZS5sZW5ndGg7IGkrKykge1xuICAgIGxldCBuZXdDaGlsZCA9IG5ld1RyZWVbaV07XG5cbiAgICAvLyBJZiB0aGUgbmV3IGNoaWxkIGlzIGEgVm5vZGUgYW5kIGlzIG5vdCBhIHRleHQgbm9kZVxuICAgIGlmIChuZXdDaGlsZCBpbnN0YW5jZW9mIFZub2RlICYmIG5ld0NoaWxkLnRhZyAhPT0gdGV4dFRhZykge1xuICAgICAgLy8gSWYgdGhlIHRhZyBvZiB0aGUgbmV3IGNoaWxkIGlzIG5vdCBhIHN0cmluZywgaXQgaXMgYSBjb21wb25lbnRcbiAgICAgIGlmICh0eXBlb2YgbmV3Q2hpbGQudGFnICE9PSBcInN0cmluZ1wiKSB7XG4gICAgICAgIC8vIFNldCB0aGUgY3VycmVudCBjb21wb25lbnQgdG8gdGhlIHRhZyBvZiB0aGUgbmV3IGNoaWxkXG4gICAgICAgIGN1cnJlbnQuY29tcG9uZW50ID0gbmV3Q2hpbGQudGFnO1xuICAgICAgICAvLyBSZXBsYWNlIHRoZSBuZXcgY2hpbGQgd2l0aCB0aGUgcmVzdWx0IG9mIGNhbGxpbmcgaXRzIHZpZXcgb3IgYmluZCBtZXRob2QsIHBhc3NpbmcgaW4gdGhlIHByb3BzIGFuZCBjaGlsZHJlbiBhcyBhcmd1bWVudHNcbiAgICAgICAgbmV3VHJlZS5zcGxpY2UoXG4gICAgICAgICAgaS0tLFxuICAgICAgICAgIDEsXG4gICAgICAgICAgKFwidmlld1wiIGluIG5ld0NoaWxkLnRhZyA/IG5ld0NoaWxkLnRhZy52aWV3LmJpbmQobmV3Q2hpbGQudGFnKSA6IG5ld0NoaWxkLnRhZy5iaW5kKG5ld0NoaWxkLnRhZykpKFxuICAgICAgICAgICAgbmV3Q2hpbGQucHJvcHMsXG4gICAgICAgICAgICAuLi5uZXdDaGlsZC5jaGlsZHJlblxuICAgICAgICAgIClcbiAgICAgICAgKTtcbiAgICAgICAgY29udGludWU7XG4gICAgICB9XG5cbiAgICAgIC8vIFNldCB0aGUgaXNTVkcgZmxhZyBmb3IgdGhlIG5ldyBjaGlsZCBpZiBpdCBpcyBhbiBTVkcgZWxlbWVudCBvciBpZiB0aGUgcGFyZW50IGlzIGFuIFNWRyBlbGVtZW50XG4gICAgICBuZXdDaGlsZC5pc1NWRyA9IG5ld1Zub2RlLmlzU1ZHIHx8IG5ld0NoaWxkLnRhZyA9PT0gXCJzdmdcIjtcblxuICAgICAgLy8gSWYgdGhlcmUgaXMgYW4gb2xkIGNoaWxkIGF0IHRoZSBzYW1lIGluZGV4XG4gICAgICBpZiAoaSA8IG9sZFRyZWVMZW5ndGgpIHtcbiAgICAgICAgbGV0IG9sZENoaWxkID0gb2xkVHJlZVtpXTtcbiAgICAgICAgLy8gSWYgdGhlIHRhZyBvZiB0aGUgbmV3IGNoaWxkIGlzIHRoZSBzYW1lIGFzIHRoZSB0YWcgb2YgdGhlIG9sZCBjaGlsZFxuICAgICAgICBpZiAobmV3Q2hpbGQudGFnID09PSBvbGRDaGlsZC50YWcpIHtcbiAgICAgICAgICAvLyBTZXQgdGhlIGRvbSBwcm9wZXJ0eSBvZiB0aGUgbmV3IGNoaWxkIHRvIHRoZSBkb20gcHJvcGVydHkgb2YgdGhlIG9sZCBjaGlsZFxuICAgICAgICAgIG5ld0NoaWxkLmRvbSA9IG9sZENoaWxkLmRvbTtcbiAgICAgICAgICAvLyBJZiB0aGUgdi1rZWVwIHByb3AgaXMgdGhlIHNhbWUgZm9yIGJvdGggdGhlIG5ldyBhbmQgb2xkIGNoaWxkLCBzZXQgdGhlIGNoaWxkcmVuIG9mIHRoZSBuZXcgY2hpbGQgdG8gdGhlIGNoaWxkcmVuIG9mIHRoZSBvbGQgY2hpbGRcbiAgICAgICAgICBpZiAoXCJ2LWtlZXBcIiBpbiBuZXdDaGlsZC5wcm9wcyAmJiBuZXdDaGlsZC5wcm9wc1tcInYta2VlcFwiXSA9PT0gb2xkQ2hpbGQucHJvcHNbXCJ2LWtlZXBcIl0pIHtcbiAgICAgICAgICAgIG5ld0NoaWxkLmNoaWxkcmVuID0gb2xkQ2hpbGQuY2hpbGRyZW47XG4gICAgICAgICAgICBjb250aW51ZTtcbiAgICAgICAgICB9XG5cbiAgICAgICAgICAvLyBVcGRhdGUgdGhlIGF0dHJpYnV0ZXMgb2YgdGhlIG5ldyBjaGlsZCBiYXNlZCBvbiB0aGUgb2xkIGNoaWxkXG4gICAgICAgICAgdXBkYXRlQXR0cmlidXRlcyhuZXdDaGlsZCBhcyBWbm9kZVdpdGhEb20sIG9sZENoaWxkKTtcbiAgICAgICAgICAvLyBSZWN1cnNpdmVseSBwYXRjaCB0aGUgbmV3IGFuZCBvbGQgY2hpbGRyZW5cbiAgICAgICAgICBwYXRjaChuZXdDaGlsZCBhcyBWbm9kZVdpdGhEb20sIG9sZENoaWxkKTtcbiAgICAgICAgICBjb250aW51ZTtcbiAgICAgICAgfVxuXG4gICAgICAgIC8vIENyZWF0ZSBhIG5ldyBkb20gZWxlbWVudCBmb3IgdGhlIG5ldyBjaGlsZFxuICAgICAgICBuZXdDaGlsZC5kb20gPSBjcmVhdGVEb21FbGVtZW50KG5ld0NoaWxkLnRhZywgbmV3Q2hpbGQuaXNTVkcpO1xuICAgICAgICAvLyBVcGRhdGUgdGhlIGF0dHJpYnV0ZXMgb2YgdGhlIG5ldyBjaGlsZFxuICAgICAgICB1cGRhdGVBdHRyaWJ1dGVzKG5ld0NoaWxkIGFzIFZub2RlV2l0aERvbSk7XG4gICAgICAgIC8vIFJlcGxhY2UgdGhlIG9sZCBjaGlsZCBpbiB0aGUgZG9tIHdpdGggdGhlIG5ldyBjaGlsZFxuICAgICAgICBuZXdWbm9kZS5kb20ucmVwbGFjZUNoaWxkKG5ld0NoaWxkLmRvbSwgb2xkQ2hpbGQuZG9tKTtcbiAgICAgICAgLy8gUmVjdXJzaXZlbHkgcGF0Y2ggdGhlIG5ldyBjaGlsZFxuICAgICAgICBwYXRjaChuZXdDaGlsZCBhcyBWbm9kZVdpdGhEb20pO1xuICAgICAgICBjb250aW51ZTtcbiAgICAgIH1cblxuICAgICAgLy8gQ3JlYXRlIGEgbmV3IGRvbSBlbGVtZW50IGZvciB0aGUgbmV3IGNoaWxkXG4gICAgICBuZXdDaGlsZC5kb20gPSBjcmVhdGVEb21FbGVtZW50KG5ld0NoaWxkLnRhZywgbmV3Q2hpbGQuaXNTVkcpO1xuICAgICAgLy8gVXBkYXRlIHRoZSBhdHRyaWJ1dGVzIG9mIHRoZSBuZXcgY2hpbGRcbiAgICAgIHVwZGF0ZUF0dHJpYnV0ZXMobmV3Q2hpbGQgYXMgVm5vZGVXaXRoRG9tKTtcbiAgICAgIC8vIEFwcGVuZCB0aGUgbmV3IGNoaWxkIHRvIHRoZSBkb21cbiAgICAgIG5ld1Zub2RlLmRvbS5hcHBlbmRDaGlsZChuZXdDaGlsZC5kb20pO1xuICAgICAgLy8gUmVjdXJzaXZlbHkgcGF0Y2ggdGhlIG5ldyBjaGlsZFxuICAgICAgcGF0Y2gobmV3Q2hpbGQgYXMgVm5vZGVXaXRoRG9tKTtcbiAgICAgIGNvbnRpbnVlO1xuICAgIH1cblxuICAgIC8vIElmIHRoZSBuZXcgY2hpbGQgaXMgYW4gYXJyYXksIGZsYXR0ZW4gaXQgYW5kIGNvbnRpbnVlIHRoZSBsb29wXG4gICAgaWYgKEFycmF5LmlzQXJyYXkobmV3Q2hpbGQpKSB7XG4gICAgICBuZXdUcmVlLnNwbGljZShpLS0sIDEsIC4uLm5ld0NoaWxkKTtcbiAgICAgIGNvbnRpbnVlO1xuICAgIH1cblxuICAgIC8vIElmIHRoZSBuZXcgY2hpbGQgaXMgbnVsbCBvciB1bmRlZmluZWQsIHJlbW92ZSBpdCBmcm9tIHRoZSBuZXcgdHJlZSBhbmQgY29udGludWUgdGhlIGxvb3BcbiAgICBpZiAobmV3Q2hpbGQgPT09IG51bGwgfHwgbmV3Q2hpbGQgPT09IHVuZGVmaW5lZCkge1xuICAgICAgbmV3VHJlZS5zcGxpY2UoaS0tLCAxKTtcbiAgICAgIGNvbnRpbnVlO1xuICAgIH1cblxuICAgIC8vIElmIHRoZSBuZXcgY2hpbGQgaXMgbm90IGEgVm5vZGUsIHdyYXAgaXQgaW4gYSB0ZXh0IFZub2RlXG4gICAgbmV3VHJlZVtpXSA9IG5ldyBWbm9kZSh0ZXh0VGFnLCB7fSwgW10pO1xuICAgIC8vIElmIHRoZSBuZXcgY2hpbGQgaXMgYSBWbm9kZSwgc2V0IHRoZSBkb20gcHJvcGVydHkgb2YgdGhlIHRleHQgVm5vZGUgdG8gdGhlIGRvbSBwcm9wZXJ0eSBvZiB0aGUgbmV3IGNoaWxkXG4gICAgaWYgKG5ld0NoaWxkIGluc3RhbmNlb2YgVm5vZGUpIHtcbiAgICAgIG5ld1RyZWVbaV0uZG9tID0gbmV3Q2hpbGQuZG9tO1xuICAgICAgLy8gU2V0IHRoZSBuZXcgY2hpbGQgdG8gdGhlIHRleHQgY29udGVudCBvZiBpdHMgZG9tIHByb3BlcnR5XG4gICAgICBuZXdDaGlsZCA9IChuZXdDaGlsZCBhcyBWbm9kZVdpdGhEb20pLmRvbS50ZXh0Q29udGVudDtcbiAgICB9XG5cbiAgICAvLyBJZiB0aGVyZSBpcyBhbiBvbGQgY2hpbGQgYXQgdGhlIHNhbWUgaW5kZXhcbiAgICBpZiAoaSA8IG9sZFRyZWVMZW5ndGgpIHtcbiAgICAgIGxldCBvbGRDaGlsZCA9IG9sZFRyZWVbaV07XG5cbiAgICAgIC8vIElmIHRoZSBvbGQgY2hpbGQgaXMgYSB0ZXh0IG5vZGVcbiAgICAgIGlmIChvbGRDaGlsZC50YWcgPT09IHRleHRUYWcpIHtcbiAgICAgICAgLy8gU2V0IHRoZSBkb20gcHJvcGVydHkgb2YgdGhlIHRleHQgVm5vZGUgdG8gdGhlIGRvbSBwcm9wZXJ0eSBvZiB0aGUgb2xkIGNoaWxkXG4gICAgICAgIG5ld1RyZWVbaV0uZG9tID0gb2xkQ2hpbGQuZG9tO1xuICAgICAgICAvLyBJZiB0aGUgdGV4dCBjb250ZW50IG9mIHRoZSBvbGQgY2hpbGQgaXMgZGlmZmVyZW50IGZyb20gdGhlIG5ldyBjaGlsZCwgdXBkYXRlIHRoZSB0ZXh0IGNvbnRlbnQgb2YgdGhlIG9sZCBjaGlsZFxuICAgICAgICAvLyBlc2xpbnQtZGlzYWJsZS1uZXh0LWxpbmUgZXFlcWVxXG4gICAgICAgIGlmIChuZXdDaGlsZCAhPSBvbGRDaGlsZC5kb20udGV4dENvbnRlbnQpIHtcbiAgICAgICAgICBvbGRDaGlsZC5kb20udGV4dENvbnRlbnQgPSBuZXdDaGlsZDtcbiAgICAgICAgfVxuICAgICAgICBjb250aW51ZTtcbiAgICAgIH1cblxuICAgICAgLy8gQ3JlYXRlIGEgbmV3IHRleHQgbm9kZSBmb3IgdGhlIG5ldyBjaGlsZFxuICAgICAgbmV3VHJlZVtpXS5kb20gPSBkb2N1bWVudC5jcmVhdGVUZXh0Tm9kZShuZXdDaGlsZCk7XG4gICAgICAvLyBSZXBsYWNlIHRoZSBvbGQgY2hpbGQgaW4gdGhlIGRvbSB3aXRoIHRoZSBuZXcgdGV4dCBub2RlXG4gICAgICBuZXdWbm9kZS5kb20ucmVwbGFjZUNoaWxkKG5ld1RyZWVbaV0uZG9tLCBvbGRDaGlsZC5kb20pO1xuICAgICAgY29udGludWU7XG4gICAgfVxuXG4gICAgLy8gQ3JlYXRlIGEgbmV3IHRleHQgbm9kZSBmb3IgdGhlIG5ldyBjaGlsZFxuICAgIG5ld1RyZWVbaV0uZG9tID0gZG9jdW1lbnQuY3JlYXRlVGV4dE5vZGUobmV3Q2hpbGQpO1xuICAgIC8vIEFwcGVuZCB0aGUgbmV3IHRleHQgbm9kZSB0byB0aGUgZG9tXG4gICAgbmV3Vm5vZGUuZG9tLmFwcGVuZENoaWxkKG5ld1RyZWVbaV0uZG9tKTtcbiAgfVxuXG4gIC8vIFJlbW92ZSBhbnkgb2xkIGNoaWxkcmVuIHRoYXQgYXJlIG5vIGxvbmdlciBwcmVzZW50IGluIHRoZSBuZXcgdHJlZVxuICBmb3IgKGxldCBpID0gbmV3VHJlZS5sZW5ndGg7IGkgPCBvbGRUcmVlTGVuZ3RoOyBpKyspIHtcbiAgICBuZXdWbm9kZS5kb20ucmVtb3ZlQ2hpbGQob2xkVHJlZVtpXS5kb20pO1xuICB9XG59XG5cbi8vIFVwZGF0ZSB0aGUgbWFpbiBWbm9kZVxuZXhwb3J0IGZ1bmN0aW9uIHVwZGF0ZSgpOiB2b2lkIHwgc3RyaW5nIHtcbiAgLy8gSWYgdGhlIG1haW4gVm5vZGUgZXhpc3RzXG4gIGlmIChtYWluVm5vZGUpIHtcbiAgICAvLyBDYWxsIGFueSBjbGVhbnVwIGZ1bmN0aW9ucyB0aGF0IGFyZSByZWdpc3RlcmVkIHdpdGggdGhlIG9uQ2xlYW51cFNldCBzZXRcbiAgICBjYWxsU2V0KG9uQ2xlYW51cFNldCk7XG4gICAgLy8gU3RvcmUgYSByZWZlcmVuY2UgdG8gdGhlIG9sZCBtYWluIFZub2RlXG4gICAgbGV0IG9sZE1haW5Wbm9kZSA9IG1haW5Wbm9kZTtcbiAgICAvLyBDcmVhdGUgYSBuZXcgbWFpbiBWbm9kZSB3aXRoIHRoZSBtYWluIGNvbXBvbmVudCBhcyBpdHMgb25seSBjaGlsZFxuICAgIG1haW5Wbm9kZSA9IG5ldyBWbm9kZShvbGRNYWluVm5vZGUudGFnLCBvbGRNYWluVm5vZGUucHJvcHMsIFttYWluQ29tcG9uZW50XSkgYXMgVm5vZGVXaXRoRG9tO1xuICAgIG1haW5Wbm9kZS5kb20gPSBvbGRNYWluVm5vZGUuZG9tO1xuICAgIG1haW5Wbm9kZS5pc1NWRyA9IG9sZE1haW5Wbm9kZS5pc1NWRztcblxuICAgIC8vIFJlY3Vyc2l2ZWx5IHBhdGNoIHRoZSBuZXcgYW5kIG9sZCBtYWluIFZub2Rlc1xuICAgIHBhdGNoKG1haW5Wbm9kZSwgb2xkTWFpblZub2RlKTtcblxuICAgIC8vIENhbGwgYW55IHVwZGF0ZSBvciBtb3VudCBmdW5jdGlvbnMgdGhhdCBhcmUgcmVnaXN0ZXJlZCB3aXRoIHRoZSBvblVwZGF0ZVNldCBvciBvbk1vdW50U2V0IHNldFxuICAgIGNhbGxTZXQoaXNNb3VudGVkID8gb25VcGRhdGVTZXQgOiBvbk1vdW50U2V0KTtcblxuICAgIC8vIFNldCB0aGUgaXNNb3VudGVkIGZsYWcgdG8gdHJ1ZVxuICAgIGlzTW91bnRlZCA9IHRydWU7XG5cbiAgICAvLyBSZXNldCB0aGUgY3VycmVudCB2bm9kZSwgb2xkVm5vZGUsIGFuZCBjb21wb25lbnQgcHJvcGVydGllc1xuICAgIGN1cnJlbnQudm5vZGUgPSBudWxsO1xuICAgIGN1cnJlbnQub2xkVm5vZGUgPSBudWxsO1xuICAgIGN1cnJlbnQuY29tcG9uZW50ID0gbnVsbDtcblxuICAgIC8vIElmIHRoZSBjb2RlIGlzIHJ1bm5pbmcgaW4gYSBOb2RlLmpzIGVudmlyb25tZW50LCByZXR1cm4gdGhlIGlubmVyIEhUTUwgb2YgdGhlIG1haW4gVm5vZGUncyBkb20gZWxlbWVudFxuICAgIGlmIChpc05vZGVKcykge1xuICAgICAgcmV0dXJuIG1haW5Wbm9kZS5kb20uaW5uZXJIVE1MO1xuICAgIH1cbiAgfVxufVxuXG4vLyBVbm1vdW50IHRoZSBtYWluIFZub2RlXG5leHBvcnQgZnVuY3Rpb24gdW5tb3VudCgpIHtcbiAgLy8gSWYgdGhlIG1haW4gVm5vZGUgZXhpc3RzXG4gIGlmIChtYWluVm5vZGUpIHtcbiAgICAvLyBTZXQgdGhlIG1haW4gY29tcG9uZW50IHRvIGEgbnVsbCBWbm9kZVxuICAgIG1haW5Db21wb25lbnQgPSBuZXcgVm5vZGUoKCkgPT4gbnVsbCwge30sIFtdKSBhcyBWbm9kZUNvbXBvbmVudEludGVyZmFjZTtcbiAgICAvLyBVcGRhdGUgdGhlIG1haW4gVm5vZGVcbiAgICBsZXQgcmVzdWx0ID0gdXBkYXRlKCk7XG4gICAgLy8gQ2FsbCBhbnkgdW5tb3VudCBmdW5jdGlvbnMgdGhhdCBhcmUgcmVnaXN0ZXJlZCB3aXRoIHRoZSBvblVubW91bnRTZXQgc2V0XG4gICAgY2FsbFNldChvblVubW91bnRTZXQpO1xuXG4gICAgLy8gUmVtb3ZlIGFueSBldmVudCBsaXN0ZW5lcnMgdGhhdCB3ZXJlIGFkZGVkIHRvIHRoZSBtYWluIFZub2RlJ3MgZG9tIGVsZW1lbnRcbiAgICBmb3IgKGxldCBuYW1lIGluIGV2ZW50TGlzdGVuZXJOYW1lcykge1xuICAgICAgbWFpblZub2RlLmRvbS5yZW1vdmVFdmVudExpc3RlbmVyKG5hbWUuc2xpY2UoMikudG9Mb3dlckNhc2UoKSwgZXZlbnRMaXN0ZW5lcik7XG4gICAgICBSZWZsZWN0LmRlbGV0ZVByb3BlcnR5KGV2ZW50TGlzdGVuZXJOYW1lcywgbmFtZSk7XG4gICAgfVxuXG4gICAgLy8gUmVzZXQgdGhlIG1haW4gY29tcG9uZW50IGFuZCBtYWluIFZub2RlXG4gICAgbWFpbkNvbXBvbmVudCA9IG51bGw7XG4gICAgbWFpblZub2RlID0gbnVsbDtcbiAgICAvLyBTZXQgdGhlIGlzTW91bnRlZCBmbGFnIHRvIGZhbHNlXG4gICAgaXNNb3VudGVkID0gZmFsc2U7XG4gICAgLy8gUmVzZXQgdGhlIGN1cnJlbnQgdm5vZGUsIG9sZFZub2RlLCBhbmQgY29tcG9uZW50IHByb3BlcnRpZXNcbiAgICBjdXJyZW50LnZub2RlID0gbnVsbDtcbiAgICBjdXJyZW50Lm9sZFZub2RlID0gbnVsbDtcbiAgICBjdXJyZW50LmNvbXBvbmVudCA9IG51bGw7XG4gICAgLy8gUmV0dXJuIHRoZSByZXN1bHQgb2YgdXBkYXRpbmcgdGhlIG1haW4gVm5vZGVcbiAgICByZXR1cm4gcmVzdWx0O1xuICB9XG59XG4vLyBUaGlzIGZ1bmN0aW9uIHRha2VzIGluIGEgRE9NIGVsZW1lbnQgb3IgYSBET00gZWxlbWVudCBzZWxlY3RvciBhbmQgYSBjb21wb25lbnQgdG8gYmUgbW91bnRlZCBvbiBpdC5cbmV4cG9ydCBmdW5jdGlvbiBtb3VudChkb20sIGNvbXBvbmVudCkge1xuICAvLyBDaGVjayBpZiB0aGUgJ2RvbScgYXJndW1lbnQgaXMgYSBzdHJpbmcuIElmIGl0IGlzLCBzZWxlY3QgdGhlIGZpcnN0IGVsZW1lbnQgdGhhdCBtYXRjaGVzIHRoZSBnaXZlbiBzZWxlY3Rvci5cbiAgLy8gT3RoZXJ3aXNlLCB1c2UgdGhlICdkb20nIGFyZ3VtZW50IGFzIHRoZSBjb250YWluZXIuXG4gIGxldCBjb250YWluZXIgPVxuICAgIHR5cGVvZiBkb20gPT09IFwic3RyaW5nXCJcbiAgICAgID8gaXNOb2RlSnNcbiAgICAgICAgPyBjcmVhdGVEb21FbGVtZW50KGRvbSwgZG9tID09PSBcInN2Z1wiKVxuICAgICAgICA6IGRvY3VtZW50LnF1ZXJ5U2VsZWN0b3JBbGwoZG9tKVswXVxuICAgICAgOiBkb207XG5cbiAgLy8gQ2hlY2sgaWYgdGhlICdjb21wb25lbnQnIGFyZ3VtZW50IGlzIGEgVm5vZGUgY29tcG9uZW50IG9yIGEgcmVndWxhciBjb21wb25lbnQuXG4gIC8vIElmIGl0J3MgYSByZWd1bGFyIGNvbXBvbmVudCwgY3JlYXRlIGEgbmV3IFZub2RlIGNvbXBvbmVudCB1c2luZyB0aGUgJ2NvbXBvbmVudCcgYXJndW1lbnQgYXMgdGhlIHRhZy5cbiAgLy8gSWYgaXQncyBub3QgYSBjb21wb25lbnQgYXQgYWxsLCBjcmVhdGUgYSBuZXcgVm5vZGUgY29tcG9uZW50IHdpdGggdGhlICdjb21wb25lbnQnIGFyZ3VtZW50IGFzIHRoZSByZW5kZXJpbmcgZnVuY3Rpb24uXG4gIGxldCB2bm9kZUNvbXBvbmVudCA9IGlzVm5vZGVDb21wb25lbnQoY29tcG9uZW50KVxuICAgID8gY29tcG9uZW50XG4gICAgOiBpc0NvbXBvbmVudChjb21wb25lbnQpXG4gICAgPyBuZXcgVm5vZGUoY29tcG9uZW50LCB7fSwgW10pXG4gICAgOiBuZXcgVm5vZGUoKCkgPT4gY29tcG9uZW50LCB7fSwgW10pO1xuXG4gIC8vIElmIGEgbWFpbiBjb21wb25lbnQgYWxyZWFkeSBleGlzdHMgYW5kIGl0J3Mgbm90IHRoZSBzYW1lIGFzIHRoZSBjdXJyZW50ICd2bm9kZUNvbXBvbmVudCcsIHVubW91bnQgaXQuXG4gIGlmIChtYWluQ29tcG9uZW50ICYmIG1haW5Db21wb25lbnQudGFnICE9PSB2bm9kZUNvbXBvbmVudC50YWcpIHtcbiAgICB1bm1vdW50KCk7XG4gIH1cblxuICAvLyBTZXQgdGhlICd2bm9kZUNvbXBvbmVudCcgYXMgdGhlIG1haW4gY29tcG9uZW50LlxuICBtYWluQ29tcG9uZW50ID0gdm5vZGVDb21wb25lbnQgYXMgVm5vZGVDb21wb25lbnRJbnRlcmZhY2U7XG4gIC8vIENvbnZlcnQgdGhlIGNvbnRhaW5lciBlbGVtZW50IHRvIGEgVm5vZGUuXG4gIG1haW5Wbm9kZSA9IGRvbVRvVm5vZGUoY29udGFpbmVyKTtcbiAgLy8gVXBkYXRlIHRoZSBET00gd2l0aCB0aGUgbmV3IGNvbXBvbmVudC5cbiAgcmV0dXJuIHVwZGF0ZSgpO1xufVxuXG4vLyBUaGlzIGlzIGEgdXRpbGl0eSBmdW5jdGlvbiBmb3IgY3JlYXRpbmcgVm5vZGUgb2JqZWN0cy5cbi8vIEl0IHRha2VzIGluIGEgdGFnIG9yIGNvbXBvbmVudCwgYW5kIG9wdGlvbmFsIHByb3BzIGFuZCBjaGlsZHJlbiBhcmd1bWVudHMuXG5leHBvcnQgY29uc3QgdjogViA9ICh0YWdPckNvbXBvbmVudCwgcHJvcHMgPSB7fSwgLi4uY2hpbGRyZW4pID0+IHtcbiAgLy8gUmV0dXJuIGEgbmV3IFZub2RlIG9iamVjdCB1c2luZyB0aGUgZ2l2ZW4gYXJndW1lbnRzLlxuICByZXR1cm4gbmV3IFZub2RlKHRhZ09yQ29tcG9uZW50LCBwcm9wcyB8fCB7fSwgY2hpbGRyZW4pO1xufTtcblxuLy8gVGhpcyB1dGlsaXR5IGZ1bmN0aW9uIGNyZWF0ZXMgYSBmcmFnbWVudCBWbm9kZS5cbi8vIEl0IHRha2VzIGluIGEgcGxhY2Vob2xkZXIgYW5kIHRoZSBjaGlsZHJlbiBhcmd1bWVudHMsIHJldHVybnMgb25seSB0aGUgY2hpbGRyZW4uXG52LmZyYWdtZW50ID0gKF86IFZub2RlUHJvcGVydGllcywgLi4uY2hpbGRyZW46IENoaWxkcmVuKSA9PiBjaGlsZHJlbjtcbiJdLCJtYXBwaW5ncyI6Ik1BZ0lBLElBQU1BLEVBQVUsUUFJTEMsRUFBV0MsUUFBMkIsb0JBQVpDLFNBQTJCQSxRQUFRQyxVQUFZRCxRQUFRQyxTQUFTQyxNQUk5RixTQUFTQyxF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 \ No newline at end of file +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJ0ZXh0VGFnIiwiaXNOb2RlSnMiLCJCb29sZWFuIiwicHJvY2VzcyIsInZlcnNpb25zIiwibm9kZSIsImNyZWF0ZURvbUVsZW1lbnQiLCJ0YWciLCJpc1NWRyIsImRvY3VtZW50IiwiY3JlYXRlRWxlbWVudE5TIiwiY3JlYXRlRWxlbWVudCIsIlZub2RlIiwicHJvcHMiLCJjaGlsZHJlbiIsInRoaXMiLCJpc0NvbXBvbmVudCIsImNvbXBvbmVudCIsImlzVm5vZGUiLCJvYmplY3QiLCJpc1Zub2RlQ29tcG9uZW50IiwiZG9tVG9Wbm9kZSIsImRvbSIsImkiLCJsIiwiY2hpbGROb2RlcyIsImxlbmd0aCIsImNoaWxkRG9tIiwibm9kZVR5cGUiLCJwdXNoIiwidm5vZGUiLCJhdHRyaWJ1dGVzIiwiYXR0ciIsIm5vZGVOYW1lIiwibm9kZVZhbHVlIiwidGFnTmFtZSIsInRvTG93ZXJDYXNlIiwidHJ1c3QiLCJodG1sU3RyaW5nIiwiZGl2IiwiaW5uZXJIVE1MIiwidHJpbSIsIm1hcCIsImNhbGwiLCJpdGVtIiwibWFpbkNvbXBvbmVudCIsIm1haW5Wbm9kZSIsImlzTW91bnRlZCIsImN1cnJlbnQiLCJvbGRWbm9kZSIsInJlc2VydmVkUHJvcHMiLCJrZXkiLCJzdGF0ZSIsIm9uQ2xlYW51cFNldCIsIlNldCIsIm9uTW91bnRTZXQiLCJvblVwZGF0ZVNldCIsIm9uVW5tb3VudFNldCIsIm9uQ2xlYW51cCIsImNhbGxiYWNrIiwiYWRkIiwiY2FsbFNldCIsInNldCIsImNsZWFyIiwiZXZlbnRMaXN0ZW5lck5hbWVzIiwiZXZlbnRMaXN0ZW5lciIsImUiLCJ0YXJnZXQiLCJuYW1lIiwidHlwZSIsImRlZmF1bHRQcmV2ZW50ZWQiLCJ1cGRhdGUiLCJwYXJlbnROb2RlIiwiaGlkZURpcmVjdGl2ZSIsInRlc3QiLCJib29sIiwib2xkbm9kZSIsIm5ld2RvbSIsImNyZWF0ZVRleHROb2RlIiwicmVwbGFjZUNoaWxkIiwiZGlyZWN0aXZlcyIsIm5ld0NoaWxkcmVuIiwic3R5bGUiLCJkaXNwbGF5IiwiY2xhc3NlcyIsImNsYXNzTGlzdCIsInRvZ2dsZSIsImh0bWwiLCJtb2RlbCIsInByb3BlcnR5IiwiZXZlbnQiLCJ2YWx1ZSIsImhhbmRsZXIiLCJBcnJheSIsImlzQXJyYXkiLCJ2YWwiLCJpZHgiLCJpbmRleE9mIiwic3BsaWNlIiwic2hhcmVkU2V0QXR0cmlidXRlIiwibXVsdGlwbGUiLCJjdHJsS2V5IiwiZm9yRWFjaCIsImNoaWxkIiwiam9pbiIsInNlbGVjdGVkIiwicHJldkhhbmRsZXIiLCJjbGVhbnVwIiwibmV3Vm5vZGUiLCJsb3dlcmNhc2VOYW1lIiwiYWRkRXZlbnRMaXN0ZW5lciIsInNsaWNlIiwicmVtb3ZlQXR0cmlidXRlIiwic2V0QXR0cmlidXRlIiwidXBkYXRlQXR0cmlidXRlcyIsInBhdGNoIiwibmV3VHJlZSIsIm9sZFRyZWUiLCJvbGRUcmVlTGVuZ3RoIiwibmV3VHJlZUxlbmd0aCIsIm9sZEtleWVkTGlzdCIsIm5ld0tleWVkTGlzdCIsIm5ld0NoaWxkIiwib2xkQ2hpbGQiLCJzaG91bGRQYXRjaCIsImFwcGVuZENoaWxkIiwicmVtb3ZlQ2hpbGQiLCJ2aWV3IiwiYmluZCIsInRleHRDb250ZW50Iiwib2xkTWFpblZub2RlIiwidW5tb3VudCIsInJlc3VsdCIsInJlbW92ZUV2ZW50TGlzdGVuZXIiLCJSZWZsZWN0IiwiZGVsZXRlUHJvcGVydHkiLCJ2IiwidGFnT3JDb21wb25lbnQiLCJmcmFnbWVudCIsIl8iLCJkaXJlY3RpdmUiLCJkaXJlY3RpdmVOYW1lIiwiY29udGFpbmVyIiwicXVlcnlTZWxlY3RvckFsbCIsInZub2RlQ29tcG9uZW50Il0sInNvdXJjZXMiOlsiLi4vbGliL2luZGV4LnRzIl0sInNvdXJjZXNDb250ZW50IjpbIi8qIGVzbGludC1kaXNhYmxlIG5vLXVzZS1iZWZvcmUtZGVmaW5lICovXG4vKiBlc2xpbnQtZGlzYWJsZSBpbmRlbnQgKi9cbi8qIGVzbGludC1kaXNhYmxlIHNvbmFyanMvY29nbml0aXZlLWNvbXBsZXhpdHkgKi9cbi8qIGVzbGludC1kaXNhYmxlIGNvbXBsZXhpdHkgKi9cblxuLy8gVGhlIFZub2RlUHJvcGVydGllcyBpbnRlcmZhY2UgcmVwcmVzZW50cyBwcm9wZXJ0aWVzIHRoYXQgY2FuIGJlIHBhc3NlZCB0byBhIHZpcnR1YWwgbm9kZS5cbmV4cG9ydCBpbnRlcmZhY2UgVm5vZGVQcm9wZXJ0aWVzIHtcbiAgLy8gQSB1bmlxdWUga2V5IGZvciB0aGUgdmlydHVhbCBub2RlLCB3aGljaCBjYW4gYmUgYSBzdHJpbmcgb3IgYSBudW1iZXIuXG4gIC8vIFRoaXMgaXMgdXNlZnVsIGZvciBvcHRpbWl6aW5nIHVwZGF0ZXMgaW4gYSBsaXN0IG9mIG5vZGVzLlxuICBrZXk/OiBzdHJpbmcgfCBudW1iZXI7XG4gIC8vIEEgc3RhdGUgb2JqZWN0IHRoYXQgaXMgYXNzb2NpYXRlZCB3aXRoIHRoZSB2aXJ0dWFsIG5vZGUuXG4gIHN0YXRlPzogYW55O1xuICAvLyBBbiBpbmRleCBzaWduYXR1cmUgdGhhdCBhbGxvd3MgZm9yIGFueSBvdGhlciBwcm9wZXJ0aWVzIHRvIGJlIGFkZGVkLlxuICBba2V5OiBzdHJpbmcgfCBudW1iZXIgfCBzeW1ib2xdOiBhbnk7XG59XG5cbi8vIFRoZSBEb21FbGVtZW50IGludGVyZmFjZSBleHRlbmRzIHRoZSBFbGVtZW50IGludGVyZmFjZSB3aXRoIGFuIGluZGV4IHNpZ25hdHVyZS5cbi8vIFRoaXMgYWxsb3dzIGZvciBhbnkgYWRkaXRpb25hbCBwcm9wZXJ0aWVzIHRvIGJlIGFkZGVkIHRvIERPTSBlbGVtZW50cy5cbmV4cG9ydCBpbnRlcmZhY2UgRG9tRWxlbWVudCBleHRlbmRzIEVsZW1lbnQge1xuICBba2V5OiBzdHJpbmddOiBhbnk7XG59XG5cbi8vIFRoZSBWbm9kZUludGVyZmFjZSByZXByZXNlbnRzIGEgdmlydHVhbCBub2RlLiBJdCBoYXMgYSBudW1iZXIgb2Ygb3B0aW9uYWwgZmllbGRzLFxuLy8gaW5jbHVkaW5nIGEgdGFnLCBwcm9wcywgY2hpbGRyZW4sIGFuZCBhIERPTSBlbGVtZW50LlxuZXhwb3J0IGludGVyZmFjZSBWbm9kZUludGVyZmFjZSB7XG4gIC8vIFRoZSBjb25zdHJ1Y3RvciBmb3IgdGhlIHZpcnR1YWwgbm9kZS4gSXQgdGFrZXMgYSB0YWcsIHByb3BzLCBhbmQgY2hpbGRyZW4gYXMgYXJndW1lbnRzLlxuICAvLyBUaGUgdGFnIGNhbiBiZSBhIHN0cmluZywgYSBjb21wb25lbnQsIG9yIGEgUE9KTyBjb21wb25lbnQuXG4gIC8vIGVzbGludC1kaXNhYmxlLW5leHQtbGluZSBuby11bnVzZWQtdmFyc1xuICBuZXcgKHRhZzogc3RyaW5nIHwgQ29tcG9uZW50IHwgUE9KT0NvbXBvbmVudCwgcHJvcHM6IFZub2RlUHJvcGVydGllcywgY2hpbGRyZW46IENoaWxkcmVuKTogVm5vZGVJbnRlcmZhY2U7XG4gIC8vIFRoZSB0YWcgZm9yIHRoZSB2aXJ0dWFsIG5vZGUuIEl0IGNhbiBiZSBhIHN0cmluZywgYSBjb21wb25lbnQsIG9yIGEgUE9KTyBjb21wb25lbnQuXG4gIHRhZzogc3RyaW5nIHwgQ29tcG9uZW50IHwgUE9KT0NvbXBvbmVudDtcbiAgLy8gVGhlIHByb3BzIGZvciB0aGUgdmlydHVhbCBub2RlLlxuICBwcm9wczogVm5vZGVQcm9wZXJ0aWVzO1xuICAvLyBUaGUgY2hpbGRyZW4gZm9yIHRoZSB2aXJ0dWFsIG5vZGUuXG4gIGNoaWxkcmVuOiBDaGlsZHJlbjtcbiAgLy8gQSBib29sZWFuIGluZGljYXRpbmcgd2hldGhlciB0aGUgdmlydHVhbCBub2RlIGlzIGFuIFNWRyBlbGVtZW50LlxuICBpc1NWRz86IGJvb2xlYW47XG4gIC8vIFRoZSBET00gZWxlbWVudCB0aGF0IGNvcnJlc3BvbmRzIHRvIHRoZSB2aXJ0dWFsIG5vZGUuXG4gIGRvbT86IERvbUVsZW1lbnQ7XG4gIC8vIEEgYm9vbGVhbiBpbmRpY2F0aW5nIHdoZXRoZXIgdGhlIHZpcnR1YWwgbm9kZSBoYXMgYmVlbiBwcm9jZXNzZWQgaW4gdGhlIGtleWVkIGRpZmZpbmcgYWxnb3JpdGhtLlxuICBwcm9jZXNzZWQ/OiBib29sZWFuO1xuICAvLyBBbiBpbmRleCBzaWduYXR1cmUgdGhhdCBhbGxvd3MgZm9yIGFueSBhZGRpdGlvbmFsIHByb3BlcnRpZXMgdG8gYmUgYWRkZWQgdG8gdGhlIHZpcnR1YWwgbm9kZS5cbiAgW2tleTogc3RyaW5nIHwgbnVtYmVyIHwgc3ltYm9sXTogYW55O1xufVxuXG4vLyBUaGUgVm5vZGVXaXRoRG9tIGludGVyZmFjZSByZXByZXNlbnRzIGEgdmlydHVhbCBub2RlIHRoYXQgaGFzIGEgRE9NIGVsZW1lbnQgYXNzb2NpYXRlZCB3aXRoIGl0LlxuZXhwb3J0IGludGVyZmFjZSBWbm9kZVdpdGhEb20gZXh0ZW5kcyBWbm9kZUludGVyZmFjZSB7XG4gIGRvbTogRG9tRWxlbWVudDtcbn1cblxuLy8gVGhlIENvbXBvbmVudCBpbnRlcmZhY2UgcmVwcmVzZW50cyBhIGZ1bmN0aW9uIHRoYXQgcmV0dXJucyBhIHZpcnR1YWwgbm9kZSBvciBhIGxpc3Qgb2YgdmlydHVhbCBub2Rlcy5cbi8vIEl0IGNhbiBhbHNvIGhhdmUgYWRkaXRpb25hbCBwcm9wZXJ0aWVzLlxuZXhwb3J0IGludGVyZmFjZSBDb21wb25lbnQge1xuICAvLyBUaGUgZnVuY3Rpb24gdGhhdCByZXR1cm5zIGEgdmlydHVhbCBub2RlIG9yIGEgbGlzdCBvZiB2aXJ0dWFsIG5vZGVzLlxuICAvLyBJdCBjYW4gdGFrZSBwcm9wcyBhbmQgY2hpbGRyZW4gYXMgYXJndW1lbnRzLlxuICAvLyBlc2xpbnQtZGlzYWJsZS1uZXh0LWxpbmUgbm8tdW51c2VkLXZhcnNcbiAgKHByb3BzPzogVm5vZGVQcm9wZXJ0aWVzIHwgbnVsbCwgLi4uY2hpbGRyZW46IGFueVtdKTogVm5vZGVJbnRlcmZhY2UgfCBDaGlsZHJlbiB8IGFueTtcbiAgLy8gQW4gaW5kZXggc2lnbmF0dXJlIHRoYXQgYWxsb3dzIGZvciBhbnkgYWRkaXRpb25hbCBwcm9wZXJ0aWVzIHRvIGJlIGFkZGVkIHRvIHRoZSBjb21wb25lbnQuXG4gIFtrZXk6IHN0cmluZ106IGFueTtcbn1cblxuLy8gVGhlIFBPSk9Db21wb25lbnQgaW50ZXJmYWNlIHJlcHJlc2VudHMgYSBcInBsYWluIG9sZCBKYXZhU2NyaXB0IG9iamVjdFwiIChQT0pPKSBjb21wb25lbnQuXG4vLyBJdCBoYXMgYSB2aWV3IGZ1bmN0aW9uIHRoYXQgcmV0dXJucyBhIHZpcnR1YWwgbm9kZSBvciBhIGxpc3Qgb2YgdmlydHVhbCBub2Rlcyxcbi8vIGFzIHdlbGwgYXMgb3B0aW9uYWwgcHJvcHMgYW5kIGNoaWxkcmVuLlxuLy8gSXQgY2FuIGJlIHVzZWQgYWxzbyB0byBpZGVudGlmeSBjbGFzcyBpbnN0YW5jZSBjb21wb25lbnRzLlxuZXhwb3J0IGludGVyZmFjZSBQT0pPQ29tcG9uZW50IHtcbiAgLy8gVGhlIHZpZXcgZnVuY3Rpb24gdGhhdCByZXR1cm5zIGEgdmlydHVhbCBub2RlIG9yIGEgbGlzdCBvZiB2aXJ0dWFsIG5vZGVzLlxuICB2aWV3OiBDb21wb25lbnQ7XG4gIC8vIFRoZSBwcm9wcyBmb3IgdGhlIGNvbXBvbmVudC5cbiAgcHJvcHM/OiBWbm9kZVByb3BlcnRpZXMgfCBudWxsO1xuICAvLyBUaGUgY2hpbGRyZW4gZm9yIHRoZSBjb21wb25lbnQuXG4gIGNoaWxkcmVuPzogYW55W107XG4gIC8vIEFuIGluZGV4IHNpZ25hdHVyZSB0aGF0IGFsbG93cyBmb3IgYW55IGFkZGl0aW9uYWwgcHJvcGVydGllcyB0byBiZSBhZGRlZCB0byB0aGUgUE9KTyBjb21wb25lbnQuXG4gIFtrZXk6IHN0cmluZ106IGFueTtcbn1cblxuLy8gVGhlIFZub2RlQ29tcG9uZW50SW50ZXJmYWNlIHJlcHJlc2VudHMgYSB2aXJ0dWFsIG5vZGUgdGhhdCBoYXMgYSBjb21wb25lbnQgYXMgaXRzIHRhZy5cbi8vIEl0IGhhcyBwcm9wcyBhbmQgY2hpbGRyZW4sIGp1c3QgbGlrZSBhIHJlZ3VsYXIgdmlydHVhbCBub2RlLlxuZXhwb3J0IGludGVyZmFjZSBWbm9kZUNvbXBvbmVudEludGVyZmFjZSBleHRlbmRzIFZub2RlSW50ZXJmYWNlIHtcbiAgdGFnOiBDb21wb25lbnQgfCBQT0pPQ29tcG9uZW50O1xuICBwcm9wczogVm5vZGVQcm9wZXJ0aWVzO1xuICBjaGlsZHJlbjogQ2hpbGRyZW47XG59XG5cbi8vIFRoZSBDaGlsZHJlbiBpbnRlcmZhY2UgcmVwcmVzZW50cyBhIGxpc3Qgb2YgdmlydHVhbCBub2RlcyBvciBvdGhlciB2YWx1ZXMuXG5leHBvcnQgaW50ZXJmYWNlIENoaWxkcmVuIGV4dGVuZHMgQXJyYXk8Vm5vZGVJbnRlcmZhY2UgfCBWbm9kZUNvbXBvbmVudEludGVyZmFjZSB8IGFueT4ge31cblxuLy8gVGhlIERpcmVjdGl2ZSBpbnRlcmZhY2UgcmVwcmVzZW50cyBhIGZ1bmN0aW9uIHRoYXQgY2FuIGJlIGFwcGxpZWQgdG8gYSB2aXJ0dWFsIG5vZGUuXG4vLyBJdCByZWNlaXZlcyB0aGUgdmFsdWUsIHZpcnR1YWwgbm9kZSwgYW5kIG9sZCB2aXJ0dWFsIG5vZGUgYXMgYXJndW1lbnRzLCBhbmQgY2FuIHJldHVybiBhIGJvb2xlYW4gdmFsdWUuXG4vLyBJZiBvbmx5IHRoZSB2aXJ0dWFsIG5vZGUgaXMgcGFzc2VkLCBpdCBtZWFucyBpdHMgdGhlIG9uIGNyZWF0ZSBwaGFzZSBmb3IgdGhlIHYtbm9kZS5cbi8vIElmIHRoZSBvbGQgdmlydHVhbCBub2RlIGlzIGFsc28gcGFzc2VkLCBpdCBtZWFucyBpdHMgdGhlIG9uIHVwZGF0ZSBwaGFzZSBmb3IgdGhlIHYtbm9kZS5cbmV4cG9ydCBpbnRlcmZhY2UgRGlyZWN0aXZlIHtcbiAgLy8gZXNsaW50LWRpc2FibGUtbmV4dC1saW5lIG5vLXVudXNlZC12YXJzXG4gICh2YWx1ZTogYW55LCB2bm9kZTogVm5vZGVXaXRoRG9tLCBvbGRWbm9kZT86IFZub2RlV2l0aERvbSk6IHZvaWQgfCBib29sZWFuO1xufVxuXG4vLyBUaGUgRGlyZWN0aXZlcyBpbnRlcmZhY2UgaXMgYSBtYXBwaW5nIG9mIGRpcmVjdGl2ZSBuYW1lcyB0byBEaXJlY3RpdmUgZnVuY3Rpb25zLlxuZXhwb3J0IGludGVyZmFjZSBEaXJlY3RpdmVzIHtcbiAgW2tleTogc3RyaW5nXTogRGlyZWN0aXZlO1xufVxuXG4vLyBUaGUgUmVzZXJ2ZWRQcm9wcyBpbnRlcmZhY2UgaXMgYSBtYXBwaW5nIG9mIHJlc2VydmVkIHByb3AgbmFtZXMgdG8gdGhlIHZhbHVlIGB0cnVlYC5cbi8vIFRoZXNlIHByb3AgbmFtZXMgY2Fubm90IGJlIHVzZWQgYXMgY3VzdG9tIHByb3AgbmFtZXMuXG5leHBvcnQgaW50ZXJmYWNlIFJlc2VydmVkUHJvcHMge1xuICBba2V5OiBzdHJpbmddOiB0cnVlO1xufVxuXG4vLyBUaGUgQ3VycmVudCBpbnRlcmZhY2UgcmVwcmVzZW50cyB0aGUgY3VycmVudCBjb21wb25lbnQgYW5kIHZpcnR1YWwgbm9kZSB0aGF0IGFyZSBiZWluZyBwcm9jZXNzZWQuXG5leHBvcnQgaW50ZXJmYWNlIEN1cnJlbnQge1xuICAvLyBUaGUgY3VycmVudCBjb21wb25lbnQuIEl0IGNhbiBiZSBhIGNvbXBvbmVudCwgYSBQT0pPIGNvbXBvbmVudCwgb3IgbnVsbC5cbiAgY29tcG9uZW50OiBDb21wb25lbnQgfCBQT0pPQ29tcG9uZW50IHwgbnVsbDtcbiAgLy8gVGhlIGN1cnJlbnQgdmlydHVhbCBub2RlLiBJdCBtdXN0IGhhdmUgYSBET00gZWxlbWVudCBhc3NvY2lhdGVkIHdpdGggaXQuXG4gIHZub2RlOiBWbm9kZVdpdGhEb20gfCBudWxsO1xuICAvLyBUaGUgb2xkIHZpcnR1YWwgbm9kZS4gSXQgbXVzdCBoYXZlIGEgRE9NIGVsZW1lbnQgYXNzb2NpYXRlZCB3aXRoIGl0LlxuICBvbGRWbm9kZT86IFZub2RlV2l0aERvbSB8IG51bGw7XG59XG5cbi8vIFRoZSBWIGZ1bmN0aW9uIGlzIHRoZSBtYWluIGZ1bmN0aW9uIGZvciBjcmVhdGluZyB2aXJ0dWFsIG5vZGVzLlxuLy8gSXQgdGFrZXMgYSB0YWcgb3IgY29tcG9uZW50LCBwcm9wcywgYW5kIGNoaWxkcmVuIGFzIGFyZ3VtZW50cywgYW5kIHJldHVybnMgYSB2aXJ0dWFsIG5vZGUuXG5leHBvcnQgaW50ZXJmYWNlIFYge1xuICAvLyBlc2xpbnQtZGlzYWJsZS1uZXh0LWxpbmUgbm8tdW51c2VkLXZhcnMsIG5vLXVzZS1iZWZvcmUtZGVmaW5lXG4gICh0YWdPckNvbXBvbmVudDogc3RyaW5nIHwgQ29tcG9uZW50IHwgUE9KT0NvbXBvbmVudCwgcHJvcHM6IFZub2RlUHJvcGVydGllcyB8IG51bGwsIC4uLmNoaWxkcmVuOiBDaGlsZHJlbik6XG4gICAgfCBWbm9kZUludGVyZmFjZVxuICAgIHwgVm5vZGVDb21wb25lbnRJbnRlcmZhY2U7XG4gIC8vIGVzbGludC1kaXNhYmxlLW5leHQtbGluZSBuby11bnVzZWQtdmFycywgbm8tdXNlLWJlZm9yZS1kZWZpbmVcbiAgZnJhZ21lbnQoXzogYW55LCAuLi5jaGlsZHJlbjogQ2hpbGRyZW4pOiBDaGlsZHJlbjtcbn1cbi8vICd0ZXh0VGFnJyBpcyBhIGNvbnN0YW50IHN0cmluZyB0aGF0IGlzIHVzZWQgdG8gcmVwcmVzZW50IHRleHQgbm9kZXMgaW4gdGhlIHZpcnR1YWwgRE9NLlxuY29uc3QgdGV4dFRhZyA9IFwiI3RleHRcIjtcblxuLy8gJ2lzTm9kZUpzJyBpcyBhIGJvb2xlYW4gdGhhdCBpcyB0cnVlIGlmIHRoZSBjb2RlIGlzIHJ1bm5pbmcgaW4gYSBOb2RlLmpzIGVudmlyb25tZW50IGFuZCBmYWxzZSBvdGhlcndpc2UuXG4vLyBJdCBpcyBkZXRlcm1pbmVkIGJ5IGNoZWNraW5nIGlmIHRoZSAncHJvY2VzcycgZ2xvYmFsIG9iamVjdCBpcyBkZWZpbmVkIGFuZCBoYXMgYSAndmVyc2lvbnMnIHByb3BlcnR5LlxuZXhwb3J0IGxldCBpc05vZGVKcyA9IEJvb2xlYW4odHlwZW9mIHByb2Nlc3MgIT09IFwidW5kZWZpbmVkXCIgJiYgcHJvY2Vzcy52ZXJzaW9ucyAmJiBwcm9jZXNzLnZlcnNpb25zLm5vZGUpO1xuXG4vLyAnY3JlYXRlRG9tRWxlbWVudCcgaXMgYSBmdW5jdGlvbiB0aGF0IGNyZWF0ZXMgYSBuZXcgRE9NIGVsZW1lbnQgd2l0aCB0aGUgc3BlY2lmaWVkIHRhZyBuYW1lLlxuLy8gSWYgJ2lzU1ZHJyBpcyB0cnVlLCBpdCBjcmVhdGVzIGFuIFNWRyBlbGVtZW50IGluc3RlYWQgb2YgYSByZWd1bGFyIERPTSBlbGVtZW50LlxuZXhwb3J0IGZ1bmN0aW9uIGNyZWF0ZURvbUVsZW1lbnQodGFnOiBzdHJpbmcsIGlzU1ZHOiBib29sZWFuID0gZmFsc2UpOiBEb21FbGVtZW50IHtcbiAgcmV0dXJuIGlzU1ZHID8gZG9jdW1lbnQuY3JlYXRlRWxlbWVudE5TKFwiaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmdcIiwgdGFnKSA6IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQodGFnKTtcbn1cblxuLy8gJ1Zub2RlJyBpcyBhIGNsYXNzIHRoYXQgcmVwcmVzZW50cyBhIHZpcnR1YWwgRE9NIG5vZGUuXG4vLyBJdCBoYXMgdGhyZWUgcHJvcGVydGllczogJ3RhZycsICdwcm9wcycsIGFuZCAnY2hpbGRyZW4nLlxuLy8gJ1Zub2RlJyBpcyBleHBvcnRlZCBhcyBhbiBvYmplY3Qgd2l0aCBhIHR5cGUgb2YgJ1Zub2RlSW50ZXJmYWNlJy5cbi8vIFRoZSAnYXMgdW5rbm93biBhcyBWbm9kZUludGVyZmFjZScgaXMgdXNlZCB0byB0ZWxsIFR5cGVTY3JpcHQgdGhhdCB0aGUgJ1Zub2RlJyBmdW5jdGlvbiBoYXMgdGhlIHNhbWUgdHlwZSBhcyAnVm5vZGVJbnRlcmZhY2UnLlxuZXhwb3J0IGNvbnN0IFZub2RlID0gZnVuY3Rpb24gVm5vZGUodGhpczogVm5vZGVJbnRlcmZhY2UsIHRhZzogc3RyaW5nLCBwcm9wczogVm5vZGVQcm9wZXJ0aWVzLCBjaGlsZHJlbjogQ2hpbGRyZW4pIHtcbiAgLy8gJ3RoaXMnIHJlZmVycyB0byB0aGUgY3VycmVudCBpbnN0YW5jZSBvZiAnVm5vZGUnLlxuICB0aGlzLnRhZyA9IHRhZztcbiAgdGhpcy5wcm9wcyA9IHByb3BzO1xuICB0aGlzLmNoaWxkcmVuID0gY2hpbGRyZW47XG59IGFzIHVua25vd24gYXMgVm5vZGVJbnRlcmZhY2U7XG5cbi8vICdpc0NvbXBvbmVudCcgaXMgYSBmdW5jdGlvbiB0aGF0IHJldHVybnMgdHJ1ZSBpZiB0aGUgZ2l2ZW4gJ2NvbXBvbmVudCcgaXMgYSB2YWxpZCBjb21wb25lbnQgYW5kIGZhbHNlIG90aGVyd2lzZS5cbi8vIEEgY29tcG9uZW50IGlzIGVpdGhlciBhIGZ1bmN0aW9uIG9yIGFuIG9iamVjdCB3aXRoIGEgJ3ZpZXcnIGZ1bmN0aW9uLlxuZXhwb3J0IGZ1bmN0aW9uIGlzQ29tcG9uZW50KGNvbXBvbmVudCk6IGNvbXBvbmVudCBpcyBDb21wb25lbnQge1xuICByZXR1cm4gY29tcG9uZW50ICYmICh0eXBlb2YgY29tcG9uZW50ID09PSBcImZ1bmN0aW9uXCIgfHwgKHR5cGVvZiBjb21wb25lbnQgPT09IFwib2JqZWN0XCIgJiYgXCJ2aWV3XCIgaW4gY29tcG9uZW50KSk7XG59XG5cbi8vICdpc1Zub2RlJyBpcyBhIGZ1bmN0aW9uIHRoYXQgcmV0dXJucyB0cnVlIGlmIHRoZSBnaXZlbiAnb2JqZWN0JyBpcyBhICdWbm9kZScgaW5zdGFuY2UgYW5kIGZhbHNlIG90aGVyd2lzZS5cbmV4cG9ydCBjb25zdCBpc1Zub2RlID0gKG9iamVjdD86IHVua25vd24gfCBWbm9kZUludGVyZmFjZSk6IG9iamVjdCBpcyBWbm9kZUludGVyZmFjZSA9PiB7XG4gIC8vIFVzZSB0aGUgJ2luc3RhbmNlb2YnIG9wZXJhdG9yIHRvIGNoZWNrIGlmICdvYmplY3QnIGlzIGFuIGluc3RhbmNlIG9mICdWbm9kZScuXG4gIHJldHVybiBvYmplY3QgaW5zdGFuY2VvZiBWbm9kZTtcbn07XG5cbi8vICdpc1Zub2RlQ29tcG9uZW50JyBpcyBhIGZ1bmN0aW9uIHRoYXQgcmV0dXJucyB0cnVlIGlmIHRoZSBnaXZlbiAnb2JqZWN0JyBpcyBhICdWbm9kZScgaW5zdGFuY2Ugd2l0aCBhICd0YWcnIHByb3BlcnR5IHRoYXQgaXMgYSB2YWxpZCBjb21wb25lbnQuXG4vLyBJdCByZXR1cm5zIGZhbHNlIG90aGVyd2lzZS5cbmV4cG9ydCBjb25zdCBpc1Zub2RlQ29tcG9uZW50ID0gKG9iamVjdD86IHVua25vd24gfCBWbm9kZUNvbXBvbmVudEludGVyZmFjZSk6IG9iamVjdCBpcyBWbm9kZUNvbXBvbmVudEludGVyZmFjZSA9PiB7XG4gIC8vIENoZWNrIGlmICdvYmplY3QnIGlzIGEgJ1Zub2RlJyBpbnN0YW5jZSBhbmQgaXRzICd0YWcnIHByb3BlcnR5IGlzIGEgdmFsaWQgY29tcG9uZW50LlxuICByZXR1cm4gaXNWbm9kZShvYmplY3QpICYmIGlzQ29tcG9uZW50KG9iamVjdC50YWcpO1xufTtcblxuLy8gJ2RvbVRvVm5vZGUnIGlzIGEgZnVuY3Rpb24gdGhhdCBjb252ZXJ0cyBhIERPTSBub2RlIHRvIGEgJ1Zub2RlJyBpbnN0YW5jZS5cbmZ1bmN0aW9uIGRvbVRvVm5vZGUoZG9tOiBhbnkpOiBWbm9kZVdpdGhEb20ge1xuICBsZXQgY2hpbGRyZW46IFZub2RlV2l0aERvbVtdID0gW107XG4gIC8vIEl0ZXJhdGUgdGhyb3VnaCBhbGwgY2hpbGQgbm9kZXMgb2YgJ2RvbScuXG4gIGZvciAobGV0IGkgPSAwLCBsID0gZG9tLmNoaWxkTm9kZXMubGVuZ3RoOyBpIDwgbDsgaSsrKSB7XG4gICAgbGV0IGNoaWxkRG9tID0gZG9tLmNoaWxkTm9kZXNbaV07XG4gICAgLy8gSWYgdGhlIGNoaWxkIG5vZGUgaXMgYSB0ZXh0IG5vZGUsIGNyZWF0ZSBhICdWbm9kZScgaW5zdGFuY2Ugd2l0aCB0aGUgJ3RleHRUYWcnIGNvbnN0YW50IGFzIHRoZSAndGFnJyBwcm9wZXJ0eS5cbiAgICAvLyBTZXQgdGhlICdkb20nIHByb3BlcnR5IG9mIHRoZSAnVm5vZGUnIGluc3RhbmNlIHRvIHRoZSBjaGlsZCBET00gbm9kZS5cbiAgICAvLyBQdXNoIHRoZSAnVm5vZGUnIGluc3RhbmNlIHRvIHRoZSAnY2hpbGRyZW4nIGFycmF5LlxuICAgIGlmIChjaGlsZERvbS5ub2RlVHlwZSA9PT0gMykge1xuICAgICAgbGV0IHZub2RlID0gbmV3IFZub2RlKHRleHRUYWcsIHt9LCBbXSk7XG4gICAgICB2bm9kZS5kb20gPSBjaGlsZERvbTtcbiAgICAgIGNoaWxkcmVuLnB1c2godm5vZGUgYXMgVm5vZGVXaXRoRG9tKTtcbiAgICAgIGNvbnRpbnVlO1xuICAgIH1cblxuICAgIC8vIElmIHRoZSBjaGlsZCBub2RlIGlzIGFuIGVsZW1lbnQgbm9kZSwgcmVjdXJzaXZlbHkgY2FsbCAnZG9tVG9Wbm9kZScgdG8gY29udmVydCBpdCB0byBhICdWbm9kZScgaW5zdGFuY2UuXG4gICAgLy8gUHVzaCB0aGUgJ1Zub2RlJyBpbnN0YW5jZSB0byB0aGUgJ2NoaWxkcmVuJyBhcnJheS5cbiAgICBpZiAoY2hpbGREb20ubm9kZVR5cGUgPT09IDEpIHtcbiAgICAgIGNoaWxkcmVuLnB1c2goZG9tVG9Wbm9kZShjaGlsZERvbSkpO1xuICAgIH1cbiAgfVxuXG4gIGxldCBwcm9wczogVm5vZGVQcm9wZXJ0aWVzID0ge307XG4gIC8vIEl0ZXJhdGUgdGhyb3VnaCBhbGwgYXR0cmlidXRlcyBvZiAnZG9tJy5cbiAgZm9yIChsZXQgaSA9IDAsIGwgPSBkb20uYXR0cmlidXRlcy5sZW5ndGg7IGkgPCBsOyBpKyspIHtcbiAgICBsZXQgYXR0ciA9IGRvbS5hdHRyaWJ1dGVzW2ldO1xuICAgIC8vIEFkZCB0aGUgYXR0cmlidXRlIHRvIHRoZSAncHJvcHMnIG9iamVjdCwgdXNpbmcgdGhlIGF0dHJpYnV0ZSdzIG5hbWUgYXMgdGhlIGtleSBhbmQgaXRzIHZhbHVlIGFzIHRoZSB2YWx1ZS5cbiAgICBwcm9wc1thdHRyLm5vZGVOYW1lXSA9IGF0dHIubm9kZVZhbHVlO1xuICB9XG5cbiAgLy8gQ3JlYXRlIGEgbmV3ICdWbm9kZScgaW5zdGFuY2Ugd2l0aCB0aGUgJ3RhZycgcHJvcGVydHkgc2V0IHRvIHRoZSBsb3dlcmNhc2UgdmVyc2lvbiBvZiB0aGUgRE9NIG5vZGUncyB0YWcgbmFtZS5cbiAgLy8gU2V0IHRoZSAncHJvcHMnIGFuZCAnY2hpbGRyZW4nIHByb3BlcnRpZXMgdG8gdGhlICdwcm9wcycgYW5kICdjaGlsZHJlbicgYXJyYXlzIHJlc3BlY3RpdmVseS5cbiAgLy8gU2V0IHRoZSAnZG9tJyBwcm9wZXJ0eSBvZiB0aGUgJ1Zub2RlJyBpbnN0YW5jZSB0byB0aGUgRE9NIG5vZGUuXG4gIGxldCB2bm9kZSA9IG5ldyBWbm9kZShkb20udGFnTmFtZS50b0xvd2VyQ2FzZSgpLCBwcm9wcywgY2hpbGRyZW4pO1xuICB2bm9kZS5kb20gPSBkb207XG4gIHJldHVybiB2bm9kZSBhcyBWbm9kZVdpdGhEb207XG59XG5cbi8vIFRoaXMgZnVuY3Rpb24gdGFrZXMgaW4gYW4gSFRNTCBzdHJpbmcgYW5kIGNyZWF0ZXMgYSB2aXJ0dWFsIG5vZGUgcmVwcmVzZW50YXRpb24gb2YgaXRcbi8vIHVzaW5nIHRoZSBgZG9tVG9Wbm9kZWAgZnVuY3Rpb24uIEl0IGRvZXMgdGhpcyBieSBjcmVhdGluZyBhIG5ldyBgZGl2YCBlbGVtZW50LCBzZXR0aW5nXG4vLyBpdHMgYGlubmVySFRNTGAgdG8gdGhlIHByb3ZpZGVkIEhUTUwgc3RyaW5nLCBhbmQgdGhlbiB1c2luZyBgbWFwYCB0byBpdGVyYXRlIG92ZXIgdGhlXG4vLyBgY2hpbGROb2Rlc2Agb2YgdGhlIGBkaXZgIGVsZW1lbnQsIHBhc3NpbmcgZWFjaCBvbmUgdG8gYGRvbVRvVm5vZGVgIHRvIGNyZWF0ZSBhIHZpcnR1YWxcbi8vIG5vZGUgcmVwcmVzZW50YXRpb24gb2YgaXQuIFRoZSByZXN1bHRpbmcgYXJyYXkgb2YgdmlydHVhbCBub2RlcyBpcyB0aGVuIHJldHVybmVkLlxuZXhwb3J0IGZ1bmN0aW9uIHRydXN0KGh0bWxTdHJpbmc6IHN0cmluZykge1xuICBsZXQgZGl2ID0gY3JlYXRlRG9tRWxlbWVudChcImRpdlwiKTtcbiAgZGl2LmlubmVySFRNTCA9IGh0bWxTdHJpbmcudHJpbSgpO1xuXG4gIHJldHVybiBbXS5tYXAuY2FsbChkaXYuY2hpbGROb2RlcywgKGl0ZW0pID0+IGRvbVRvVm5vZGUoaXRlbSkpO1xufVxuXG4vKiA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PSAqL1xuLyogTWFpbiBDb21wb25lbnQgaW1wbGVtZW50YXRpb24gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKi9cbi8qID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09ICovXG5cbi8vIFRoZXNlIHZhcmlhYmxlcyBhcmUgdXNlZCB0byBzdG9yZSB0aGUgbWFpbiBjb21wb25lbnQsIHRoZSBtYWluIHZpcnR1YWwgbm9kZSwgYW5kIHdoZXRoZXJcbi8vIHRoZSBtYWluIGNvbXBvbmVudCBpcyBjdXJyZW50bHkgbW91bnRlZC5cbmxldCBtYWluQ29tcG9uZW50OiBWbm9kZUNvbXBvbmVudEludGVyZmFjZSB8IG51bGwgPSBudWxsO1xubGV0IG1haW5Wbm9kZTogVm5vZGVXaXRoRG9tIHwgbnVsbCA9IG51bGw7XG5sZXQgaXNNb3VudGVkID0gZmFsc2U7XG5cbi8vIFRoaXMgb2JqZWN0IGlzIHVzZWQgdG8gc3RvcmUgdGhlIGN1cnJlbnQgdmlydHVhbCBub2RlIGFuZCBjb21wb25lbnQgYmVpbmcgcmVuZGVyZWQuXG5leHBvcnQgY29uc3QgY3VycmVudDogQ3VycmVudCA9IHtcbiAgdm5vZGU6IG51bGwsXG4gIG9sZFZub2RlOiBudWxsLFxuICBjb21wb25lbnQ6IG51bGxcbn07XG5cbi8qIFJlc2VydmVkIHByb3BzIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tICovXG4vLyBUaGlzIG9iamVjdCBpcyB1c2VkIHRvIHN0b3JlIHRoZSBuYW1lcyBvZiByZXNlcnZlZCBwcm9wcywgd2hpY2ggYXJlIHByb3BzIHRoYXQgYXJlIHJlc2VydmVkXG4vLyBmb3Igc3BlY2lhbCBwdXJwb3NlcyBhbmQgc2hvdWxkIG5vdCBiZSB1c2VkIGFzIHJlZ3VsYXIgY29tcG9uZW50IHByb3BzLlxuZXhwb3J0IGNvbnN0IHJlc2VydmVkUHJvcHM6IFJlY29yZDxzdHJpbmcsIHRydWU+ID0ge1xuICBrZXk6IHRydWUsXG4gIHN0YXRlOiB0cnVlLFxuICBcInYta2VlcFwiOiB0cnVlLFxuXG4gIC8vIEJ1aWx0IGluIGRpcmVjdGl2ZXNcbiAgXCJ2LWlmXCI6IHRydWUsXG4gIFwidi11bmxlc3NcIjogdHJ1ZSxcbiAgXCJ2LWZvclwiOiB0cnVlLFxuICBcInYtc2hvd1wiOiB0cnVlLFxuICBcInYtY2xhc3NcIjogdHJ1ZSxcbiAgXCJ2LWh0bWxcIjogdHJ1ZSxcbiAgXCJ2LW1vZGVsXCI6IHRydWUsXG4gIFwidi1jcmVhdGVcIjogdHJ1ZSxcbiAgXCJ2LXVwZGF0ZVwiOiB0cnVlLFxuICBcInYtY2xlYW51cFwiOiB0cnVlXG59O1xuXG4vKiBNb3VudGluZywgVXBkYXRpbmcsIENsZWFudXAgYW5kIFVubW91bnRpbmcgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSAqL1xuLy8gVGhlc2Ugc2V0cyBhcmUgdXNlZCB0byBzdG9yZSBjYWxsYmFja3MgZm9yIHZhcmlvdXMgbGlmZWN5Y2xlIGV2ZW50czogbW91bnRpbmcsIHVwZGF0aW5nLFxuLy8gY2xlYW5pbmcgdXAsIGFuZCB1bm1vdW50aW5nLlxuY29uc3Qgb25DbGVhbnVwU2V0OiBTZXQ8RnVuY3Rpb24+ID0gbmV3IFNldCgpO1xuY29uc3Qgb25Nb3VudFNldDogU2V0PEZ1bmN0aW9uPiA9IG5ldyBTZXQoKTtcbmNvbnN0IG9uVXBkYXRlU2V0OiBTZXQ8RnVuY3Rpb24+ID0gbmV3IFNldCgpO1xuY29uc3Qgb25Vbm1vdW50U2V0OiBTZXQ8RnVuY3Rpb24+ID0gbmV3IFNldCgpO1xuXG4vLyBUaGVzZSBmdW5jdGlvbnMgYWxsb3cgdXNlcnMgdG8gcmVnaXN0ZXIgY2FsbGJhY2tzIGZvciB0aGUgY29ycmVzcG9uZGluZyBsaWZlY3ljbGUgZXZlbnRzLlxuZXhwb3J0IGZ1bmN0aW9uIG9uTW91bnQoY2FsbGJhY2spIHtcbiAgaWYgKCFpc01vdW50ZWQpIHtcbiAgICBvbk1vdW50U2V0LmFkZChjYWxsYmFjayk7XG4gIH1cbn1cblxuZXhwb3J0IGZ1bmN0aW9uIG9uVXBkYXRlKGNhbGxiYWNrKSB7XG4gIG9uVXBkYXRlU2V0LmFkZChjYWxsYmFjayk7XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBvbkNsZWFudXAoY2FsbGJhY2spIHtcbiAgb25DbGVhbnVwU2V0LmFkZChjYWxsYmFjayk7XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBvblVubW91bnQoY2FsbGJhY2spIHtcbiAgaWYgKCFpc01vdW50ZWQpIHtcbiAgICBvblVubW91bnRTZXQuYWRkKGNhbGxiYWNrKTtcbiAgfVxufVxuXG4vLyBUaGlzIGZ1bmN0aW9uIGlzIHVzZWQgdG8gY2FsbCBhbGwgdGhlIGNhbGxiYWNrcyBpbiBhIGdpdmVuIHNldC5cbmZ1bmN0aW9uIGNhbGxTZXQoc2V0KSB7XG4gIGZvciAobGV0IGNhbGxiYWNrIG9mIHNldCkge1xuICAgIGNhbGxiYWNrKCk7XG4gIH1cblxuICBzZXQuY2xlYXIoKTtcbn1cblxuLyogRXZlbnQgbGlzdGVuZXIgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0gKi9cblxuLy8gVGhpcyBvYmplY3Qgc3RvcmVzIHRoZSBuYW1lcyBvZiBldmVudCBsaXN0ZW5lcnMgdGhhdCBoYXZlIGJlZW4gYWRkZWRcbmNvbnN0IGV2ZW50TGlzdGVuZXJOYW1lczogUmVjb3JkPHN0cmluZywgdHJ1ZT4gPSB7fTtcblxuLy8gVGhpcyBmdW5jdGlvbiBpcyBjYWxsZWQgd2hlbiBhbiBldmVudCBvY2N1cnNcbmZ1bmN0aW9uIGV2ZW50TGlzdGVuZXIoZTogRXZlbnQpIHtcbiAgLy8gQ29udmVydCB0aGUgdGFyZ2V0IG9mIHRoZSBldmVudCB0byBhIERPTSBlbGVtZW50XG4gIGxldCBkb20gPSBlLnRhcmdldCBhcyBEb21FbGVtZW50O1xuXG4gIC8vIENyZWF0ZSB0aGUgbmFtZSBvZiB0aGUgZXZlbnQgbGlzdGVuZXIgYnkgYWRkaW5nIFwidi1vblwiIHRvIHRoZSBldmVudCB0eXBlXG4gIGxldCBuYW1lID0gYHYtb24ke2UudHlwZX1gO1xuXG4gIC8vIEtlZXAgZ29pbmcgdXAgdGhlIERPTSB0cmVlIHVudGlsIHdlIGZpbmQgYW4gZWxlbWVudCB3aXRoIGFuIGV2ZW50IGxpc3RlbmVyXG4gIC8vIG1hdGNoaW5nIHRoZSBldmVudCB0eXBlXG4gIHdoaWxlIChkb20pIHtcbiAgICBpZiAoZG9tW25hbWVdKSB7XG4gICAgICAvLyBDYWxsIHRoZSBldmVudCBsaXN0ZW5lciBmdW5jdGlvblxuICAgICAgZG9tW25hbWVdKGUsIGRvbSk7XG5cbiAgICAgIC8vIElmIHRoZSBkZWZhdWx0IGFjdGlvbiBvZiB0aGUgZXZlbnQgaGFzbid0IGJlZW4gcHJldmVudGVkLCB1cGRhdGUgdGhlIERPTVxuICAgICAgaWYgKCFlLmRlZmF1bHRQcmV2ZW50ZWQpIHtcbiAgICAgICAgdXBkYXRlKCk7XG4gICAgICB9XG4gICAgICByZXR1cm47XG4gICAgfVxuICAgIGRvbSA9IGRvbS5wYXJlbnROb2RlIGFzIERvbUVsZW1lbnQ7XG4gIH1cbn1cblxuLyogRGlyZWN0aXZlcyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0gKi9cblxuLy8gVGhpcyBmdW5jdGlvbiBjcmVhdGVzIGEgZGlyZWN0aXZlIHRoYXQgaGlkZXMgYW4gZWxlbWVudCBiYXNlZCBvbiBhIGNvbmRpdGlvblxubGV0IGhpZGVEaXJlY3RpdmUgPSAodGVzdDogYm9vbGVhbikgPT4gKGJvb2w6IGJvb2xlYW4sIHZub2RlOiBWbm9kZUludGVyZmFjZSwgb2xkbm9kZT86IFZub2RlSW50ZXJmYWNlKSA9PiB7XG4gIC8vIElmIHRlc3QgaXMgdHJ1ZSwgdXNlIHRoZSB2YWx1ZSBvZiBib29sLiBPdGhlcndpc2UsIHVzZSB0aGUgb3Bwb3NpdGUgb2YgYm9vbC5cbiAgbGV0IHZhbHVlID0gdGVzdCA/IGJvb2wgOiAhYm9vbDtcblxuICAvLyBJZiB0aGUgdmFsdWUgaXMgdHJ1ZSwgaGlkZSB0aGUgZWxlbWVudCBieSByZXBsYWNpbmcgaXQgd2l0aCBhIHRleHQgbm9kZVxuICBpZiAodmFsdWUpIHtcbiAgICBsZXQgbmV3ZG9tID0gZG9jdW1lbnQuY3JlYXRlVGV4dE5vZGUoXCJcIik7XG4gICAgaWYgKG9sZG5vZGUgJiYgb2xkbm9kZS5kb20gJiYgb2xkbm9kZS5kb20ucGFyZW50Tm9kZSkge1xuICAgICAgb2xkbm9kZS5kb20ucGFyZW50Tm9kZS5yZXBsYWNlQ2hpbGQobmV3ZG9tLCBvbGRub2RlLmRvbSk7XG4gICAgfVxuICAgIHZub2RlLnRhZyA9IFwiI3RleHRcIjtcbiAgICB2bm9kZS5jaGlsZHJlbiA9IFtdO1xuICAgIHZub2RlLnByb3BzID0ge307XG4gICAgdm5vZGUuZG9tID0gbmV3ZG9tIGFzIHVua25vd24gYXMgRG9tRWxlbWVudDtcbiAgICByZXR1cm4gZmFsc2U7XG4gIH1cbn07XG5cbi8vIFRoaXMgb2JqZWN0IHN0b3JlcyBhbGwgdGhlIGF2YWlsYWJsZSBkaXJlY3RpdmVzXG5leHBvcnQgY29uc3QgZGlyZWN0aXZlczogRGlyZWN0aXZlcyA9IHtcbiAgLy8gVGhlIFwidi1pZlwiIGRpcmVjdGl2ZSBoaWRlcyBhbiBlbGVtZW50IGlmIHRoZSBnaXZlbiBjb25kaXRpb24gaXMgZmFsc2VcbiAgXCJ2LWlmXCI6IGhpZGVEaXJlY3RpdmUoZmFsc2UpLFxuXG4gIC8vIFRoZSBcInYtdW5sZXNzXCIgZGlyZWN0aXZlIGhpZGVzIGFuIGVsZW1lbnQgaWYgdGhlIGdpdmVuIGNvbmRpdGlvbiBpcyB0cnVlXG4gIFwidi11bmxlc3NcIjogaGlkZURpcmVjdGl2ZSh0cnVlKSxcblxuICAvLyBUaGUgXCJ2LWZvclwiIGRpcmVjdGl2ZSBjcmVhdGVzIGEgbG9vcCBhbmQgYXBwbGllcyBhIGNhbGxiYWNrIGZ1bmN0aW9uIHRvIGVhY2ggaXRlbSBpbiB0aGUgbG9vcFxuICBcInYtZm9yXCI6IChzZXQ6IHVua25vd25bXSwgdm5vZGU6IFZub2RlV2l0aERvbSkgPT4ge1xuICAgIGxldCBuZXdDaGlsZHJlbjogVm5vZGVJbnRlcmZhY2VbXSA9IFtdO1xuICAgIGxldCBjYWxsYmFjayA9IHZub2RlLmNoaWxkcmVuWzBdO1xuICAgIGZvciAobGV0IGkgPSAwLCBsID0gc2V0Lmxlbmd0aDsgaSA8IGw7IGkrKykge1xuICAgICAgbmV3Q2hpbGRyZW4ucHVzaChjYWxsYmFjayhzZXRbaV0sIGkpKTtcbiAgICB9XG4gICAgdm5vZGUuY2hpbGRyZW4gPSBuZXdDaGlsZHJlbjtcbiAgfSxcblxuICAvLyBUaGUgXCJ2LXNob3dcIiBkaXJlY3RpdmUgc2hvd3Mgb3IgaGlkZXMgYW4gZWxlbWVudCBieSBzZXR0aW5nIHRoZSBcImRpc3BsYXlcIiBzdHlsZSBwcm9wZXJ0eVxuICBcInYtc2hvd1wiOiAoYm9vbDogYm9vbGVhbiwgdm5vZGU6IFZub2RlV2l0aERvbSkgPT4ge1xuICAgIChcbiAgICAgIHZub2RlLmRvbSBhcyB1bmtub3duIGFzIHtcbiAgICAgICAgc3R5bGU6IHsgZGlzcGxheTogc3RyaW5nIH07XG4gICAgICB9XG4gICAgKS5zdHlsZS5kaXNwbGF5ID0gYm9vbCA/IFwiXCIgOiBcIm5vbmVcIjtcbiAgfSxcblxuICAvLyBUaGUgXCJ2LWNsYXNzXCIgZGlyZWN0aXZlIGFkZHMgb3IgcmVtb3ZlcyBjbGFzcyBuYW1lcyBmcm9tIGFuIGVsZW1lbnQgYmFzZWQgb24gYSBjb25kaXRpb25cbiAgXCJ2LWNsYXNzXCI6IChjbGFzc2VzOiB7IFt4OiBzdHJpbmddOiBib29sZWFuIH0sIHZub2RlOiBWbm9kZVdpdGhEb20pID0+IHtcbiAgICAvLyBMb29wIHRocm91Z2ggYWxsIHRoZSBjbGFzcyBuYW1lcyBpbiB0aGUgY2xhc3NlcyBvYmplY3RcbiAgICBmb3IgKGxldCBuYW1lIGluIGNsYXNzZXMpIHtcbiAgICAgIC8vIEFkZCBvciByZW1vdmUgdGhlIGNsYXNzIG5hbWUgZnJvbSB0aGUgZWxlbWVudCdzIGNsYXNzIGxpc3QgYmFzZWQgb24gdGhlIHZhbHVlIGluIHRoZSBjbGFzc2VzIG9iamVjdFxuICAgICAgKHZub2RlLmRvbSBhcyBEb21FbGVtZW50KS5jbGFzc0xpc3QudG9nZ2xlKG5hbWUsIGNsYXNzZXNbbmFtZV0pO1xuICAgIH1cbiAgfSxcblxuICAvLyBUaGUgXCJ2LWh0bWxcIiBkaXJlY3RpdmUgc2V0cyB0aGUgaW5uZXIgSFRNTCBvZiBhbiBlbGVtZW50IHRvIHRoZSBnaXZlbiBIVE1MIHN0cmluZ1xuICBcInYtaHRtbFwiOiAoaHRtbDogc3RyaW5nLCB2bm9kZTogVm5vZGVXaXRoRG9tKSA9PiB7XG4gICAgLy8gU2V0IHRoZSBjaGlsZHJlbiBvZiB0aGUgdm5vZGUgdG8gYSB0cnVzdGVkIHZlcnNpb24gb2YgdGhlIEhUTUwgc3RyaW5nXG4gICAgdm5vZGUuY2hpbGRyZW4gPSBbdHJ1c3QoaHRtbCldO1xuICB9LFxuXG4gIC8vIFRoZSBcInYtbW9kZWxcIiBkaXJlY3RpdmUgYmluZHMgdGhlIHZhbHVlIG9mIGFuIGlucHV0IGVsZW1lbnQgdG8gYSBtb2RlbCBwcm9wZXJ0eVxuICBcInYtbW9kZWxcIjogKFttb2RlbCwgcHJvcGVydHksIGV2ZW50XTogYW55W10sIHZub2RlOiBWbm9kZVdpdGhEb20sIG9sZFZub2RlPzogVm5vZGVXaXRoRG9tKSA9PiB7XG4gICAgbGV0IHZhbHVlO1xuICAgIC8vIFRoaXMgZnVuY3Rpb24gdXBkYXRlcyB0aGUgbW9kZWwgcHJvcGVydHkgd2hlbiB0aGUgaW5wdXQgZWxlbWVudCdzIHZhbHVlIGNoYW5nZXNcbiAgICBsZXQgaGFuZGxlciA9IChlOiBFdmVudCkgPT4gKG1vZGVsW3Byb3BlcnR5XSA9IChlLnRhcmdldCBhcyBEb21FbGVtZW50ICYgUmVjb3JkPHN0cmluZywgYW55PikudmFsdWUpO1xuICAgIGlmICh2bm9kZS50YWcgPT09IFwiaW5wdXRcIikge1xuICAgICAgLy8gSWYgdGhlIGVsZW1lbnQgaXMgYW4gaW5wdXQsIHVzZSB0aGUgXCJpbnB1dFwiIGV2ZW50IGJ5IGRlZmF1bHRcbiAgICAgIGV2ZW50ID0gZXZlbnQgfHwgXCJvbmlucHV0XCI7XG4gICAgICAvLyBEZXBlbmRpbmcgb24gdGhlIHR5cGUgb2YgaW5wdXQgZWxlbWVudCwgdXNlIGEgZGlmZmVyZW50IGhhbmRsZXIgZnVuY3Rpb25cbiAgICAgIHN3aXRjaCAodm5vZGUucHJvcHMudHlwZSkge1xuICAgICAgICBjYXNlIFwiY2hlY2tib3hcIjoge1xuICAgICAgICAgIGlmIChBcnJheS5pc0FycmF5KG1vZGVsW3Byb3BlcnR5XSkpIHtcbiAgICAgICAgICAgIC8vIElmIHRoZSBtb2RlbCBwcm9wZXJ0eSBpcyBhbiBhcnJheSwgYWRkIG9yIHJlbW92ZSB0aGUgdmFsdWUgZnJvbSB0aGUgYXJyYXkgd2hlbiB0aGUgY2hlY2tib3ggaXMgY2hlY2tlZCBvciB1bmNoZWNrZWRcbiAgICAgICAgICAgIGhhbmRsZXIgPSAoZTogRXZlbnQpID0+IHtcbiAgICAgICAgICAgICAgbGV0IHZhbCA9IChlLnRhcmdldCBhcyBEb21FbGVtZW50ICYgUmVjb3JkPHN0cmluZywgYW55PikudmFsdWU7XG4gICAgICAgICAgICAgIGxldCBpZHggPSBtb2RlbFtwcm9wZXJ0eV0uaW5kZXhPZih2YWwpO1xuICAgICAgICAgICAgICBpZiAoaWR4ID09PSAtMSkge1xuICAgICAgICAgICAgICAgIG1vZGVsW3Byb3BlcnR5XS5wdXNoKHZhbCk7XG4gICAgICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICAgICAgbW9kZWxbcHJvcGVydHldLnNwbGljZShpZHgsIDEpO1xuICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9O1xuICAgICAgICAgICAgLy8gSWYgdGhlIHZhbHVlIGlzIGluIHRoZSBhcnJheSwgc2V0IHRoZSBjaGVja2JveCB0byBiZSBjaGVja2VkXG4gICAgICAgICAgICB2YWx1ZSA9IG1vZGVsW3Byb3BlcnR5XS5pbmRleE9mKHZub2RlLmRvbS52YWx1ZSkgIT09IC0xO1xuICAgICAgICAgIH0gZWxzZSBpZiAoXCJ2YWx1ZVwiIGluIHZub2RlLnByb3BzKSB7XG4gICAgICAgICAgICAvLyBJZiB0aGUgaW5wdXQgZWxlbWVudCBoYXMgYSBcInZhbHVlXCIgYXR0cmlidXRlLCB1c2UgaXQgdG8gZGV0ZXJtaW5lIHRoZSBjaGVja2VkIHN0YXRlXG4gICAgICAgICAgICBoYW5kbGVyID0gKCkgPT4ge1xuICAgICAgICAgICAgICBpZiAobW9kZWxbcHJvcGVydHldID09PSB2bm9kZS5wcm9wcy52YWx1ZSkge1xuICAgICAgICAgICAgICAgIG1vZGVsW3Byb3BlcnR5XSA9IG51bGw7XG4gICAgICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICAgICAgbW9kZWxbcHJvcGVydHldID0gdm5vZGUucHJvcHMudmFsdWU7XG4gICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIH07XG4gICAgICAgICAgICB2YWx1ZSA9IG1vZGVsW3Byb3BlcnR5XSA9PT0gdm5vZGUucHJvcHMudmFsdWU7XG4gICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgIC8vIElmIHRoZXJlIGlzIG5vIFwidmFsdWVcIiBhdHRyaWJ1dGUsIHVzZSBhIGJvb2xlYW4gdmFsdWUgZm9yIHRoZSBtb2RlbCBwcm9wZXJ0eVxuICAgICAgICAgICAgaGFuZGxlciA9ICgpID0+IChtb2RlbFtwcm9wZXJ0eV0gPSAhbW9kZWxbcHJvcGVydHldKTtcbiAgICAgICAgICAgIHZhbHVlID0gbW9kZWxbcHJvcGVydHldO1xuICAgICAgICAgIH1cbiAgICAgICAgICAvLyBTZXQgdGhlIFwiY2hlY2tlZFwiIGF0dHJpYnV0ZSBvbiB0aGUgaW5wdXQgZWxlbWVudFxuICAgICAgICAgIC8vIGVzbGludC1kaXNhYmxlLW5leHQtbGluZSBuby11c2UtYmVmb3JlLWRlZmluZVxuICAgICAgICAgIHNoYXJlZFNldEF0dHJpYnV0ZShcImNoZWNrZWRcIiwgdmFsdWUsIHZub2RlKTtcbiAgICAgICAgICBicmVhaztcbiAgICAgICAgfVxuICAgICAgICBjYXNlIFwicmFkaW9cIjoge1xuICAgICAgICAgIC8vIElmIHRoZSBlbGVtZW50IGlzIGEgcmFkaW8gYnV0dG9uLCBzZXQgdGhlIFwiY2hlY2tlZFwiIGF0dHJpYnV0ZSBiYXNlZCBvbiB0aGUgdmFsdWUgb2YgdGhlIG1vZGVsIHByb3BlcnR5XG4gICAgICAgICAgLy8gZXNsaW50LWRpc2FibGUtbmV4dC1saW5lIG5vLXVzZS1iZWZvcmUtZGVmaW5lXG4gICAgICAgICAgc2hhcmVkU2V0QXR0cmlidXRlKFwiY2hlY2tlZFwiLCBtb2RlbFtwcm9wZXJ0eV0gPT09IHZub2RlLmRvbS52YWx1ZSwgdm5vZGUpO1xuICAgICAgICAgIGJyZWFrO1xuICAgICAgICB9XG4gICAgICAgIGRlZmF1bHQ6IHtcbiAgICAgICAgICAvLyBGb3IgYWxsIG90aGVyIGlucHV0IHR5cGVzLCBzZXQgdGhlIFwidmFsdWVcIiBhdHRyaWJ1dGUgYmFzZWQgb24gdGhlIHZhbHVlIG9mIHRoZSBtb2RlbCBwcm9wZXJ0eVxuICAgICAgICAgIC8vIGVzbGludC1kaXNhYmxlLW5leHQtbGluZSBuby11c2UtYmVmb3JlLWRlZmluZVxuICAgICAgICAgIHNoYXJlZFNldEF0dHJpYnV0ZShcInZhbHVlXCIsIG1vZGVsW3Byb3BlcnR5XSwgdm5vZGUpO1xuICAgICAgICB9XG4gICAgICB9XG4gICAgfSBlbHNlIGlmICh2bm9kZS50YWcgPT09IFwic2VsZWN0XCIpIHtcbiAgICAgIC8vIElmIHRoZSBlbGVtZW50IGlzIGEgc2VsZWN0IGVsZW1lbnQsIHVzZSB0aGUgXCJjbGlja1wiIGV2ZW50IGJ5IGRlZmF1bHRcbiAgICAgIGV2ZW50ID0gZXZlbnQgfHwgXCJvbmNsaWNrXCI7XG4gICAgICBpZiAodm5vZGUucHJvcHMubXVsdGlwbGUpIHtcbiAgICAgICAgLy8gSWYgdGhlIHNlbGVjdCBlbGVtZW50IGFsbG93cyBtdWx0aXBsZSBzZWxlY3Rpb25zLCB1cGRhdGUgdGhlIG1vZGVsIHByb3BlcnR5IHdpdGggYW4gYXJyYXkgb2Ygc2VsZWN0ZWQgdmFsdWVzXG4gICAgICAgIGhhbmRsZXIgPSAoZTogRXZlbnQgJiBSZWNvcmQ8c3RyaW5nLCBhbnk+KSA9PiB7XG4gICAgICAgICAgbGV0IHZhbCA9IChlLnRhcmdldCBhcyBEb21FbGVtZW50ICYgUmVjb3JkPHN0cmluZywgYW55PikudmFsdWU7XG4gICAgICAgICAgaWYgKGUuY3RybEtleSkge1xuICAgICAgICAgICAgLy8gSWYgdGhlIEN0cmwga2V5IGlzIHByZXNzZWQsIGFkZCBvciByZW1vdmUgdGhlIHZhbHVlIGZyb20gdGhlIGFycmF5XG4gICAgICAgICAgICBsZXQgaWR4ID0gbW9kZWxbcHJvcGVydHldLmluZGV4T2YodmFsKTtcbiAgICAgICAgICAgIGlmIChpZHggPT09IC0xKSB7XG4gICAgICAgICAgICAgIG1vZGVsW3Byb3BlcnR5XS5wdXNoKHZhbCk7XG4gICAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgICBtb2RlbFtwcm9wZXJ0eV0uc3BsaWNlKGlkeCwgMSk7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgIC8vIElmIHRoZSBDdHJsIGtleSBpcyBub3QgcHJlc3NlZCwgc2V0IHRoZSBtb2RlbCBwcm9wZXJ0eSB0byBhbiBhcnJheSB3aXRoIHRoZSBzZWxlY3RlZCB2YWx1ZVxuICAgICAgICAgICAgbW9kZWxbcHJvcGVydHldLnNwbGljZSgwLCBtb2RlbFtwcm9wZXJ0eV0ubGVuZ3RoKTtcbiAgICAgICAgICAgIG1vZGVsW3Byb3BlcnR5XS5wdXNoKHZhbCk7XG4gICAgICAgICAgfVxuICAgICAgICB9O1xuICAgICAgICAvLyBTZXQgdGhlIFwic2VsZWN0ZWRcIiBhdHRyaWJ1dGUgb24gdGhlIG9wdGlvbnMgYmFzZWQgb24gd2hldGhlciB0aGV5IGFyZSBpbiB0aGUgbW9kZWwgcHJvcGVydHkgYXJyYXlcbiAgICAgICAgdm5vZGUuY2hpbGRyZW4uZm9yRWFjaCgoY2hpbGQ6IFZub2RlSW50ZXJmYWNlKSA9PiB7XG4gICAgICAgICAgaWYgKGNoaWxkLnRhZyA9PT0gXCJvcHRpb25cIikge1xuICAgICAgICAgICAgbGV0IHZhbHVlID0gXCJ2YWx1ZVwiIGluIGNoaWxkLnByb3BzID8gY2hpbGQucHJvcHMudmFsdWUgOiBjaGlsZC5jaGlsZHJlbi5qb2luKFwiXCIpLnRyaW0oKTtcbiAgICAgICAgICAgIGNoaWxkLnByb3BzLnNlbGVjdGVkID0gbW9kZWxbcHJvcGVydHldLmluZGV4T2YodmFsdWUpICE9PSAtMTtcbiAgICAgICAgICB9XG4gICAgICAgIH0pO1xuICAgICAgfSBlbHNlIHtcbiAgICAgICAgLy8gSWYgdGhlIHNlbGVjdCBlbGVtZW50IGRvZXMgbm90IGFsbG93IG11bHRpcGxlIHNlbGVjdGlvbnMsIHNldCB0aGUgXCJzZWxlY3RlZFwiIGF0dHJpYnV0ZSBvbiB0aGUgb3B0aW9ucyBiYXNlZCBvbiB0aGUgdmFsdWUgb2YgdGhlIG1vZGVsIHByb3BlcnR5XG4gICAgICAgIHZub2RlLmNoaWxkcmVuLmZvckVhY2goKGNoaWxkOiBWbm9kZUludGVyZmFjZSkgPT4ge1xuICAgICAgICAgIGlmIChjaGlsZC50YWcgPT09IFwib3B0aW9uXCIpIHtcbiAgICAgICAgICAgIGxldCB2YWx1ZSA9IFwidmFsdWVcIiBpbiBjaGlsZC5wcm9wcyA/IGNoaWxkLnByb3BzLnZhbHVlIDogY2hpbGQuY2hpbGRyZW4uam9pbihcIlwiKS50cmltKCk7XG4gICAgICAgICAgICBjaGlsZC5wcm9wcy5zZWxlY3RlZCA9IHZhbHVlID09PSBtb2RlbFtwcm9wZXJ0eV07XG4gICAgICAgICAgfVxuICAgICAgICB9KTtcbiAgICAgIH1cbiAgICB9IGVsc2UgaWYgKHZub2RlLnRhZyA9PT0gXCJ0ZXh0YXJlYVwiKSB7XG4gICAgICAvLyBJZiB0aGUgZWxlbWVudCBpcyBhIHRleHRhcmVhLCB1c2UgdGhlIFwiaW5wdXRcIiBldmVudCBieSBkZWZhdWx0XG4gICAgICBldmVudCA9IGV2ZW50IHx8IFwib25pbnB1dFwiO1xuICAgICAgLy8gU2V0IHRoZSB0ZXh0YXJlYSdzIGNvbnRlbnQgdG8gdGhlIHZhbHVlIG9mIHRoZSBtb2RlbCBwcm9wZXJ0eVxuICAgICAgdm5vZGUuY2hpbGRyZW4gPSBbbW9kZWxbcHJvcGVydHldXTtcbiAgICB9XG5cbiAgICAvLyBXZSBhc3N1bWUgdGhhdCB0aGUgcHJldiBoYW5kbGVyIGlmIGFueSB3aWxsIG5vdCBiZSBjaGFuZ2VkIGJ5IHRoZSB1c2VyIGFjcm9zcyBwYXRjaHNcbiAgICBsZXQgcHJldkhhbmRsZXIgPSB2bm9kZS5wcm9wc1tldmVudF07XG5cbiAgICAvLyBTZXQgdGhlIGV2ZW50IGhhbmRsZXIgb24gdGhlIGVsZW1lbnRcbiAgICAvLyBlc2xpbnQtZGlzYWJsZS1uZXh0LWxpbmUgbm8tdXNlLWJlZm9yZS1kZWZpbmVcbiAgICBzaGFyZWRTZXRBdHRyaWJ1dGUoXG4gICAgICBldmVudCxcbiAgICAgIChlOiBFdmVudCkgPT4ge1xuICAgICAgICBoYW5kbGVyKGUpO1xuXG4gICAgICAgIC8vIElmIHRoZSBwcmV2aW91cyBoYW5kbGVyIGlzIGRlZmluZWQsIGNhbGwgaXQgYWZ0ZXIgdGhlIG1vZGVsIGhhcyBiZWVuIHVwZGF0ZWRcbiAgICAgICAgaWYgKHByZXZIYW5kbGVyKSB7XG4gICAgICAgICAgcHJldkhhbmRsZXIoZSk7XG4gICAgICAgIH1cbiAgICAgIH0sXG4gICAgICB2bm9kZSxcbiAgICAgIG9sZFZub2RlXG4gICAgKTtcbiAgfSxcblxuICAvLyBUaGUgXCJ2LWNyZWF0ZVwiIGRpcmVjdGl2ZSBpcyBjYWxsZWQgd2hlbiBhIG5ldyB2aXJ0dWFsIG5vZGUgaXMgY3JlYXRlZC5cbiAgLy8gVGhlIHByb3ZpZGVkIGNhbGxiYWNrIGZ1bmN0aW9uIGlzIGNhbGxlZCB3aXRoIHRoZSBuZXcgdmlydHVhbCBub2RlIGFzIGFuIGFyZ3VtZW50LlxuICAvLyBUaGlzIGRpcmVjdGl2ZSBpcyBvbmx5IGNhbGxlZCBvbmNlIHBlciB2aXJ0dWFsIG5vZGUsIHdoZW4gaXQgaXMgZmlyc3QgY3JlYXRlZC5cbiAgLy8gZXNsaW50LWRpc2FibGUtbmV4dC1saW5lIG5vLXVudXNlZC12YXJzXG4gIFwidi1jcmVhdGVcIjogKGNhbGxiYWNrOiAodm5vZGU6IFZub2RlV2l0aERvbSkgPT4gdm9pZCwgdm5vZGU6IFZub2RlV2l0aERvbSwgb2xkVm5vZGU/OiBWbm9kZVdpdGhEb20pID0+IHtcbiAgICAvLyBJZiB0aGlzIGlzIG5vdCBhbiB1cGRhdGUsIGNhbGwgdGhlIGNhbGxiYWNrIGZ1bmN0aW9uIHdpdGggdGhlIG5ldyB2aXJ0dWFsIG5vZGVcbiAgICBpZiAoIW9sZFZub2RlKSB7XG4gICAgICBsZXQgY2xlYW51cCA9IGNhbGxiYWNrKHZub2RlKTtcblxuICAgICAgLy8gSWYgdGhlIGNhbGxiYWNrIGZ1bmN0aW9uIHJldHVybnMgYSBmdW5jdGlvbiwgY2FsbCBpdCB3aGVuIHRoZSB1cGRhdGUgaXMgZ29ubmEgYmUgY2xlYW5lZCB1cFxuICAgICAgaWYgKHR5cGVvZiBjbGVhbnVwID09PSBcImZ1bmN0aW9uXCIpIHtcbiAgICAgICAgb25DbGVhbnVwKGNsZWFudXApO1xuICAgICAgfVxuICAgIH1cbiAgfSxcblxuICAvLyBUaGUgXCJ2LXVwZGF0ZVwiIGRpcmVjdGl2ZSBpcyBjYWxsZWQgd2hlbiBhbiBleGlzdGluZyB2aXJ0dWFsIG5vZGUgaXMgdXBkYXRlZC5cbiAgLy8gVGhlIHByb3ZpZGVkIGNhbGxiYWNrIGZ1bmN0aW9uIGlzIGNhbGxlZCB3aXRoIHRoZSBuZXcgYW5kIG9sZCB2aXJ0dWFsIG5vZGVzIGFzIGFyZ3VtZW50cy5cbiAgLy8gVGhpcyBkaXJlY3RpdmUgaXMgb25seSBjYWxsZWQgb25jZSBwZXIgdmlydHVhbCBub2RlIHVwZGF0ZS5cbiAgXCJ2LXVwZGF0ZVwiOiAoXG4gICAgLy8gZXNsaW50LWRpc2FibGUtbmV4dC1saW5lIG5vLXVudXNlZC12YXJzXG4gICAgY2FsbGJhY2s6ICh2bm9kZTogVm5vZGVXaXRoRG9tLCBvbGRWbm9kZTogVm5vZGVXaXRoRG9tKSA9PiB2b2lkLFxuICAgIHZub2RlOiBWbm9kZVdpdGhEb20sXG4gICAgb2xkVm5vZGU/OiBWbm9kZVdpdGhEb21cbiAgKSA9PiB7XG4gICAgLy8gSWYgdGhpcyBpcyBhbiB1cGRhdGUsIGNhbGwgdGhlIGNhbGxiYWNrIGZ1bmN0aW9uIHdpdGggdGhlIG5ldyBhbmQgb2xkIHZpcnR1YWwgbm9kZXNcbiAgICBpZiAob2xkVm5vZGUpIHtcbiAgICAgIGxldCBjbGVhbnVwID0gY2FsbGJhY2sodm5vZGUsIG9sZFZub2RlKTtcblxuICAgICAgLy8gSWYgdGhlIGNhbGxiYWNrIGZ1bmN0aW9uIHJldHVybnMgYSBmdW5jdGlvbiwgY2FsbCBpdCB3aGVuIHRoZSB1cGRhdGUgaXMgZ29ubmEgYmUgY2xlYW5lZCB1cFxuICAgICAgaWYgKHR5cGVvZiBjbGVhbnVwID09PSBcImZ1bmN0aW9uXCIpIHtcbiAgICAgICAgb25DbGVhbnVwKGNsZWFudXApO1xuICAgICAgfVxuICAgIH1cbiAgfSxcblxuICAvLyBUaGUgXCJ2LWNsZWFudXBcIiBkaXJlY3RpdmUgaXMgY2FsbGVkIHdoZW4gdGhlIHVwZGF0ZSBpcyBjbGVhbmVkIHVwLlxuICAvLyBUaGUgcHJvdmlkZWQgY2FsbGJhY2sgZnVuY3Rpb24gaXMgY2FsbGVkIHdpdGggdGhlIG9sZCB2aXJ0dWFsIG5vZGUgYXMgYW4gYXJndW1lbnQuXG4gIC8vIFRoaXMgZGlyZWN0aXZlIGlzIG9ubHkgY2FsbGVkIG9uY2UgcGVyIHZpcnR1YWwgbm9kZSwgd2hlbiB0aGUgdXBkYXRlIGlzIGNsZWFuZWQgdXAuXG4gIFwidi1jbGVhbnVwXCI6IChcbiAgICAvLyBlc2xpbnQtZGlzYWJsZS1uZXh0LWxpbmUgbm8tdW51c2VkLXZhcnNcbiAgICBjYWxsYmFjazogKHZub2RlOiBWbm9kZVdpdGhEb20sIG9sZFZub2RlPzogVm5vZGVXaXRoRG9tKSA9PiB2b2lkLFxuICAgIHZub2RlOiBWbm9kZVdpdGhEb20sXG4gICAgb2xkVm5vZGU/OiBWbm9kZVdpdGhEb21cbiAgKSA9PiB7XG4gICAgLy8gQWRkIHRoZSBjYWxsYmFjayBmdW5jdGlvbiB0byB0aGUgbGlzdCBvZiBjbGVhbnVwIGZ1bmN0aW9ucyB0byBiZSBjYWxsZWQgd2hlbiB0aGUgdXBkYXRlIGlzIGNsZWFuZWQgdXBcbiAgICBvbkNsZWFudXAoKCkgPT4gY2FsbGJhY2sodm5vZGUsIG9sZFZub2RlKSk7XG4gIH1cbn07XG4vLyBBZGQgYSBkaXJlY3RpdmUgdG8gdGhlIGdsb2JhbCBkaXJlY3RpdmVzIG9iamVjdCwgd2l0aCB0aGUga2V5IGJlaW5nIHRoZSBuYW1lXG4vLyBwcmVjZWRlZCBieSBcInYtXCIuIEFsc28gYWRkIHRoZSBuYW1lIHRvIHRoZSBnbG9iYWwgcmVzZXJ2ZWRQcm9wcyBvYmplY3QuXG5leHBvcnQgZnVuY3Rpb24gZGlyZWN0aXZlKG5hbWU6IHN0cmluZywgZGlyZWN0aXZlOiBEaXJlY3RpdmUpIHtcbiAgbGV0IGRpcmVjdGl2ZU5hbWUgPSBgdi0ke25hbWV9YDtcbiAgZGlyZWN0aXZlc1tkaXJlY3RpdmVOYW1lXSA9IGRpcmVjdGl2ZTtcbiAgcmVzZXJ2ZWRQcm9wc1tkaXJlY3RpdmVOYW1lXSA9IHRydWU7XG59XG5cbi8vIFNldCBhbiBhdHRyaWJ1dGUgb24gYSB2aXJ0dWFsIERPTSBub2RlIGFuZCB1cGRhdGUgdGhlIGFjdHVhbCBET00gZWxlbWVudC5cbi8vIElmIHRoZSBhdHRyaWJ1dGUgdmFsdWUgaXMgYSBmdW5jdGlvbiwgYWRkIGFuIGV2ZW50IGxpc3RlbmVyIGZvciB0aGUgYXR0cmlidXRlXG4vLyBuYW1lIHRvIHRoZSBET00gZWxlbWVudCByZXByZXNlbnRlZCBieSBtYWluVm5vZGUuXG4vLyBJZiBvbGRWbm9kZSBpcyBwcm92aWRlZCwgY29tcGFyZSB0aGUgbmV3IGF0dHJpYnV0ZSB2YWx1ZSB0byB0aGUgb2xkIHZhbHVlXG4vLyBhbmQgb25seSB1cGRhdGUgdGhlIGF0dHJpYnV0ZSBpZiB0aGUgdmFsdWVzIGFyZSBkaWZmZXJlbnQuXG5mdW5jdGlvbiBzaGFyZWRTZXRBdHRyaWJ1dGUobmFtZTogc3RyaW5nLCB2YWx1ZTogYW55LCBuZXdWbm9kZTogVm5vZGVXaXRoRG9tLCBvbGRWbm9kZT86IFZub2RlV2l0aERvbSk6IHZvaWQgfCBib29sZWFuIHtcbiAgLy8gSWYgdGhlIGF0dHJpYnV0ZSB2YWx1ZSBpcyBhIGZ1bmN0aW9uLCBhZGQgYW4gZXZlbnQgbGlzdGVuZXIgZm9yIHRoZSBhdHRyaWJ1dGVcbiAgLy8gbmFtZSB0byB0aGUgRE9NIGVsZW1lbnQgcmVwcmVzZW50ZWQgYnkgbWFpblZub2RlLlxuICBpZiAodHlwZW9mIHZhbHVlID09PSBcImZ1bmN0aW9uXCIpIHtcbiAgICAvLyBXZSBjaGFuZ2UgdGhlIG5hbWUgb2YgdGhlIGV2ZW50IHRvIGxvd2VyY2FzZSB0byBhdm9pZCBpc3N1ZXMgd2l0aCBjYXNlIHNlbnNpdGl2aXR5LlxuICAgIC8vIEV4IFwib25DbGlja1wiIGFuZCBcIm9uY2xpY2tcIiBhcmUgdGhlIHNhbWUgZXZlbnQuXG4gICAgbGV0IGxvd2VyY2FzZU5hbWUgPSBuYW1lLnRvTG93ZXJDYXNlKCk7XG4gICAgLy8gT25seSBhZGQgdGhlIGV2ZW50IGxpc3RlbmVyIGlmIGl0IGhhc24ndCBiZWVuIGFkZGVkIHlldC5cbiAgICBpZiAobG93ZXJjYXNlTmFtZSBpbiBldmVudExpc3RlbmVyTmFtZXMgPT09IGZhbHNlKSB7XG4gICAgICAobWFpblZub2RlIGFzIFZub2RlV2l0aERvbSkuZG9tLmFkZEV2ZW50TGlzdGVuZXIobG93ZXJjYXNlTmFtZS5zbGljZSgyKSwgZXZlbnRMaXN0ZW5lcik7XG4gICAgICBldmVudExpc3RlbmVyTmFtZXNbbG93ZXJjYXNlTmFtZV0gPSB0cnVlO1xuICAgIH1cbiAgICBuZXdWbm9kZS5kb21bYHYtJHtsb3dlcmNhc2VOYW1lfWBdID0gdmFsdWU7XG4gICAgcmV0dXJuO1xuICB9XG5cbiAgLy8gSWYgdGhlIGF0dHJpYnV0ZSBpcyBwcmVzZW50IG9uIHRoZSBET00gZWxlbWVudCBhbmQgbmV3Vm5vZGUgaXMgbm90IGFuIFNWRyxcbiAgLy8gdXBkYXRlIHRoZSBhdHRyaWJ1dGUgaWYgdGhlIHZhbHVlIGhhcyBjaGFuZ2VkLlxuICBpZiAobmFtZSBpbiBuZXdWbm9kZS5kb20gJiYgbmV3Vm5vZGUuaXNTVkcgPT09IGZhbHNlKSB7XG4gICAgLy8gZXNsaW50LWRpc2FibGUtbmV4dC1saW5lIGVxZXFlcVxuICAgIGlmIChuZXdWbm9kZS5kb21bbmFtZV0gIT0gdmFsdWUpIHtcbiAgICAgIG5ld1Zub2RlLmRvbVtuYW1lXSA9IHZhbHVlO1xuICAgIH1cbiAgICByZXR1cm47XG4gIH1cblxuICAvLyBJZiBvbGRWbm9kZSBpcyBub3QgcHJvdmlkZWQgb3IgdGhlIGF0dHJpYnV0ZSB2YWx1ZSBoYXMgY2hhbmdlZCwgdXBkYXRlIHRoZVxuICAvLyBhdHRyaWJ1dGUgb24gdGhlIERPTSBlbGVtZW50LlxuICBpZiAoIW9sZFZub2RlIHx8IHZhbHVlICE9PSBvbGRWbm9kZS5wcm9wc1tuYW1lXSkge1xuICAgIGlmICh2YWx1ZSA9PT0gZmFsc2UpIHtcbiAgICAgIG5ld1Zub2RlLmRvbS5yZW1vdmVBdHRyaWJ1dGUobmFtZSk7XG4gICAgfSBlbHNlIHtcbiAgICAgIG5ld1Zub2RlLmRvbS5zZXRBdHRyaWJ1dGUobmFtZSwgdmFsdWUpO1xuICAgIH1cbiAgfVxufVxuXG4vLyBTZXQgYW4gYXR0cmlidXRlIG9uIGEgdmlydHVhbCBET00gbm9kZSBhbmQgdXBkYXRlIHRoZSBhY3R1YWwgRE9NIGVsZW1lbnQuXG4vLyBTa2lwIHRoZSBhdHRyaWJ1dGUgaWYgaXQgaXMgaW4gdGhlIHJlc2VydmVkUHJvcHMgb2JqZWN0LlxuZXhwb3J0IGZ1bmN0aW9uIHNldEF0dHJpYnV0ZShuYW1lOiBzdHJpbmcsIHZhbHVlOiBhbnksIG5ld1Zub2RlOiBWbm9kZVdpdGhEb20sIG9sZFZub2RlPzogVm5vZGVXaXRoRG9tKTogdm9pZCB7XG4gIGlmIChuYW1lIGluIHJlc2VydmVkUHJvcHMpIHtcbiAgICByZXR1cm47XG4gIH1cbiAgbmV3Vm5vZGUucHJvcHNbbmFtZV0gPSB2YWx1ZTtcbiAgc2hhcmVkU2V0QXR0cmlidXRlKG5hbWUsIHZhbHVlLCBuZXdWbm9kZSBhcyBWbm9kZVdpdGhEb20sIG9sZFZub2RlKTtcbn1cblxuLy8gVXBkYXRlIHRoZSBhdHRyaWJ1dGVzIG9uIGEgdmlydHVhbCBET00gbm9kZS4gSWYgb2xkVm5vZGUgaXMgcHJvdmlkZWQsIHJlbW92ZVxuLy8gYXR0cmlidXRlcyBmcm9tIHRoZSBET00gZWxlbWVudCB0aGF0IGFyZSBub3QgcHJlc2VudCBpbiBuZXdWbm9kZS5wcm9wcyBidXQgYXJlXG4vLyBwcmVzZW50IGluIG9sZFZub2RlLnByb3BzLiBUaGVuLCBpdGVyYXRlIG92ZXIgdGhlIGF0dHJpYnV0ZXMgaW4gbmV3Vm5vZGUucHJvcHNcbi8vIGFuZCB1cGRhdGUgdGhlIERPTSBlbGVtZW50IHdpdGggdGhlIGF0dHJpYnV0ZXMgdXNpbmcgdGhlIHNoYXJlZFNldEF0dHJpYnV0ZVxuLy8gZnVuY3Rpb24uIElmIGFuIGF0dHJpYnV0ZSBpcyBpbiB0aGUgcmVzZXJ2ZWRQcm9wcyBvYmplY3QgYW5kIGhhcyBhIGNvcnJlc3BvbmRpbmdcbi8vIGRpcmVjdGl2ZSBpbiB0aGUgZGlyZWN0aXZlcyBvYmplY3QsIGNhbGwgdGhlIGRpcmVjdGl2ZSB3aXRoIHRoZSBhdHRyaWJ1dGUgdmFsdWVcbi8vIGFuZCB0aGUgdHdvIHZpcnR1YWwgRE9NIG5vZGVzIGFzIGFyZ3VtZW50cy4gSWYgdGhlIGRpcmVjdGl2ZSByZXR1cm5zIGZhbHNlLCBleGl0XG4vLyB0aGUgbG9vcC5cbmV4cG9ydCBmdW5jdGlvbiB1cGRhdGVBdHRyaWJ1dGVzKG5ld1Zub2RlOiBWbm9kZVdpdGhEb20sIG9sZFZub2RlPzogVm5vZGVXaXRoRG9tKTogdm9pZCB7XG4gIC8vIElmIG9sZFZub2RlIGlzIHByb3ZpZGVkLCByZW1vdmUgYXR0cmlidXRlcyBmcm9tIHRoZSBET00gZWxlbWVudCB0aGF0IGFyZSBub3RcbiAgLy8gcHJlc2VudCBpbiBuZXdWbm9kZS5wcm9wcyBidXQgYXJlIHByZXNlbnQgaW4gb2xkVm5vZGUucHJvcHMuXG4gIGlmIChvbGRWbm9kZSkge1xuICAgIGZvciAobGV0IG5hbWUgaW4gb2xkVm5vZGUucHJvcHMpIHtcbiAgICAgIGlmIChuYW1lIGluIG5ld1Zub2RlLnByb3BzID09PSBmYWxzZSAmJiBuYW1lIGluIGV2ZW50TGlzdGVuZXJOYW1lcyA9PT0gZmFsc2UgJiYgbmFtZSBpbiByZXNlcnZlZFByb3BzID09PSBmYWxzZSkge1xuICAgICAgICBpZiAobmFtZSBpbiBuZXdWbm9kZS5kb20gJiYgbmV3Vm5vZGUuaXNTVkcgPT09IGZhbHNlKSB7XG4gICAgICAgICAgbmV3Vm5vZGUuZG9tW25hbWVdID0gbnVsbDtcbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICBuZXdWbm9kZS5kb20ucmVtb3ZlQXR0cmlidXRlKG5hbWUpO1xuICAgICAgICB9XG4gICAgICB9XG4gICAgfVxuICB9XG5cbiAgLy8gSXRlcmF0ZSBvdmVyIHRoZSBhdHRyaWJ1dGVzIGluIG5ld1Zub2RlLnByb3BzIGFuZCB1cGRhdGUgdGhlIERPTSBlbGVtZW50IHdpdGhcbiAgLy8gdGhlIGF0dHJpYnV0ZXMgdXNpbmcgdGhlIHNoYXJlZFNldEF0dHJpYnV0ZSBmdW5jdGlvbi5cbiAgZm9yIChsZXQgbmFtZSBpbiBuZXdWbm9kZS5wcm9wcykge1xuICAgIGlmIChuYW1lIGluIHJlc2VydmVkUHJvcHMpIHtcbiAgICAgIC8vIElmIHRoZXJlIGlzIGEgZGlyZWN0aXZlIGZvciB0aGUgYXR0cmlidXRlLCBjYWxsIGl0IHdpdGggdGhlIGF0dHJpYnV0ZSB2YWx1ZVxuICAgICAgLy8gYW5kIHRoZSB0d28gdmlydHVhbCBET00gbm9kZXMgYXMgYXJndW1lbnRzLiBJZiB0aGUgZGlyZWN0aXZlIHJldHVybnMgZmFsc2UsXG4gICAgICAvLyBleGl0IHRoZSBsb29wLlxuICAgICAgaWYgKG5hbWUgaW4gZGlyZWN0aXZlcyAmJiBkaXJlY3RpdmVzW25hbWVdKG5ld1Zub2RlLnByb3BzW25hbWVdLCBuZXdWbm9kZSwgb2xkVm5vZGUpID09PSBmYWxzZSkge1xuICAgICAgICBicmVhaztcbiAgICAgIH1cbiAgICAgIGNvbnRpbnVlO1xuICAgIH1cbiAgICBzaGFyZWRTZXRBdHRyaWJ1dGUobmFtZSwgbmV3Vm5vZGUucHJvcHNbbmFtZV0sIG5ld1Zub2RlLCBvbGRWbm9kZSk7XG4gIH1cbn1cblxuLyogcGF0Y2ggLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSAqL1xuXG4vLyBQYXRjaCBhIERPTSBub2RlIHdpdGggYSBuZXcgVk5vZGUgdHJlZVxuZXhwb3J0IGZ1bmN0aW9uIHBhdGNoKG5ld1Zub2RlOiBWbm9kZVdpdGhEb20sIG9sZFZub2RlPzogVm5vZGVXaXRoRG9tKTogdm9pZCB7XG4gIC8vIEdldCB0aGUgY2hpbGRyZW4gb2YgdGhlIG5ldyBhbmQgb2xkIHZpcnR1YWwgRE9NIG5vZGVzXG4gIGxldCBuZXdUcmVlID0gbmV3Vm5vZGUuY2hpbGRyZW47XG4gIGxldCBvbGRUcmVlID0gb2xkVm5vZGU/LmNoaWxkcmVuIHx8IFtdO1xuICAvLyBHZXQgdGhlIGxlbmd0aCBvZiB0aGUgb2xkIHRyZWVcbiAgbGV0IG9sZFRyZWVMZW5ndGggPSBvbGRUcmVlLmxlbmd0aDtcblxuICAvLyBJZiB0aGUgb2xkIHRyZWUgaGFzIGNoaWxkcmVuIGFuZCB0aGUgZmlyc3QgY2hpbGQgb2YgdGhlIG5ldyB0cmVlIGlzIGEgVk5vZGUgd2l0aCBhIFwia2V5XCJcbiAgLy8gYXR0cmlidXRlIGFuZCB0aGUgZmlyc3QgY2hpbGQgb2YgdGhlIG9sZCB0cmVlIGlzIGEgVk5vZGUgd2l0aCBhIFwia2V5XCIgYXR0cmlidXRlLCB1cGRhdGVcbiAgLy8gdGhlIERPTSBlbGVtZW50IGluIHBsYWNlIGJ5IGNvbXBhcmluZyB0aGUga2V5cyBvZiB0aGUgbm9kZXMgaW4gdGhlIHRyZWVzLlxuICBpZiAob2xkVHJlZUxlbmd0aCAmJiBuZXdUcmVlWzBdIGluc3RhbmNlb2YgVm5vZGUgJiYgXCJrZXlcIiBpbiBuZXdUcmVlWzBdLnByb3BzICYmIFwia2V5XCIgaW4gb2xkVHJlZVswXS5wcm9wcykge1xuICAgIC8vIEdldCB0aGUgbGVuZ3RocyBvZiB0aGUgbmV3IGFuZCBvbGQgdHJlZXNcbiAgICBsZXQgbmV3VHJlZUxlbmd0aCA9IG5ld1RyZWUubGVuZ3RoO1xuXG4gICAgLy8gQ3JlYXRlIGFuIG9iamVjdCB0aGF0IG1hcHMga2V5cyB0byBpbmRpY2VzIGluIHRoZSBvbGQgdHJlZVxuICAgIGxldCBvbGRLZXllZExpc3Q6IHsgW2tleTogc3RyaW5nXTogbnVtYmVyIH0gPSB7fTtcbiAgICBmb3IgKGxldCBpID0gMDsgaSA8IG9sZFRyZWVMZW5ndGg7IGkrKykge1xuICAgICAgb2xkS2V5ZWRMaXN0W29sZFRyZWVbaV0ucHJvcHMua2V5XSA9IGk7XG4gICAgfVxuXG4gICAgLy8gQ3JlYXRlIGFuIG9iamVjdCB0aGF0IG1hcHMga2V5cyB0byBpbmRpY2VzIGluIHRoZSBuZXcgdHJlZVxuICAgIGxldCBuZXdLZXllZExpc3Q6IHsgW2tleTogc3RyaW5nXTogbnVtYmVyIH0gPSB7fTtcbiAgICBmb3IgKGxldCBpID0gMDsgaSA8IG5ld1RyZWVMZW5ndGg7IGkrKykge1xuICAgICAgbmV3S2V5ZWRMaXN0W25ld1RyZWVbaV0ucHJvcHMua2V5XSA9IGk7XG4gICAgfVxuXG4gICAgLy8gSXRlcmF0ZSBvdmVyIHRoZSBuZXcgdHJlZVxuICAgIGZvciAobGV0IGkgPSAwOyBpIDwgbmV3VHJlZUxlbmd0aDsgaSsrKSB7XG4gICAgICAvLyBHZXQgdGhlIGN1cnJlbnQgbmV3IGNoaWxkIGFuZCB0aGUgY29ycmVzcG9uZGluZyBvbGQgY2hpbGRcbiAgICAgIGxldCBuZXdDaGlsZCA9IG5ld1RyZWVbaV07XG4gICAgICBsZXQgb2xkQ2hpbGQgPSBvbGRUcmVlW29sZEtleWVkTGlzdFtuZXdDaGlsZC5wcm9wcy5rZXldXTtcbiAgICAgIC8vIEluaXRpYWxpemUgYSBmbGFnIHRvIGRldGVybWluZSB3aGV0aGVyIHRvIHBhdGNoIHRoZSBjaGlsZFxuICAgICAgbGV0IHNob3VsZFBhdGNoID0gdHJ1ZTtcblxuICAgICAgLy8gSWYgdGhlIG9sZCBjaGlsZCBleGlzdHMsIHVwZGF0ZSB0aGUgRE9NIGVsZW1lbnQgb2YgdGhlIG5ldyBjaGlsZCB0byBtYXRjaCB0aGUgb2xkIGNoaWxkJ3MgRE9NIGVsZW1lbnRcbiAgICAgIGlmIChvbGRDaGlsZCkge1xuICAgICAgICBuZXdDaGlsZC5kb20gPSBvbGRDaGlsZC5kb207XG4gICAgICAgIC8vIElmIHRoZSBuZXcgYW5kIG9sZCBjaGlsZHJlbiBoYXZlIHRoZSBzYW1lIFwidi1rZWVwXCIgYXR0cmlidXRlIHZhbHVlLCB1cGRhdGUgdGhlIGNoaWxkcmVuIG9mIHRoZSBuZXcgY2hpbGQgdG8gbWF0Y2ggdGhlIG9sZCBjaGlsZCdzIGNoaWxkcmVuXG4gICAgICAgIGlmIChcInYta2VlcFwiIGluIG5ld0NoaWxkLnByb3BzICYmIG5ld0NoaWxkLnByb3BzW1widi1rZWVwXCJdID09PSBvbGRDaGlsZC5wcm9wc1tcInYta2VlcFwiXSkge1xuICAgICAgICAgIG5ld0NoaWxkLmNoaWxkcmVuID0gb2xkQ2hpbGQuY2hpbGRyZW47XG4gICAgICAgICAgLy8gU2V0IHRoZSBzaG91bGRQYXRjaCBmbGFnIHRvIGZhbHNlXG4gICAgICAgICAgc2hvdWxkUGF0Y2ggPSBmYWxzZTtcbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICB1cGRhdGVBdHRyaWJ1dGVzKG5ld0NoaWxkLCBvbGRDaGlsZCk7XG4gICAgICAgIH1cblxuICAgICAgICAvLyBJZiB0aGUgb2xkIGNoaWxkIGRvZXMgbm90IGV4aXN0LCBjcmVhdGUgYSBuZXcgRE9NIGVsZW1lbnQgZm9yIHRoZSBuZXcgY2hpbGQgYW5kIHVwZGF0ZSBpdHMgYXR0cmlidXRlc1xuICAgICAgfSBlbHNlIHtcbiAgICAgICAgbmV3Q2hpbGQuZG9tID0gY3JlYXRlRG9tRWxlbWVudChuZXdDaGlsZC50YWcsIG5ld0NoaWxkLmlzU1ZHKTtcbiAgICAgICAgdXBkYXRlQXR0cmlidXRlcyhuZXdDaGlsZCk7XG4gICAgICB9XG5cbiAgICAgIC8vIElmIHRoZSBuZXcgY2hpbGQncyBET00gZWxlbWVudCBpcyBub3QgdGhlIGktdGggY2hpbGQgb2YgdGhlIHBhcmVudCBET00gZWxlbWVudCwgaW5zZXJ0IGl0XG4gICAgICBpZiAoIW5ld1Zub2RlLmRvbS5jaGlsZE5vZGVzW2ldKSB7XG4gICAgICAgIG5ld1Zub2RlLmRvbS5hcHBlbmRDaGlsZChuZXdDaGlsZC5kb20pO1xuXG4gICAgICAgIC8vIElmIHRoZSBuZXcgY2hpbGQncyBET00gZWxlbWVudCBpcyBub3QgdGhlIHNhbWUgYXMgdGhlIGktdGggY2hpbGQgb2YgdGhlIHBhcmVudCBET00gZWxlbWVudCwgcmVwbGFjZSB0aGUgaS10aCBjaGlsZCB3aXRoIHRoZSBuZXcgY2hpbGQncyBET00gZWxlbWVudFxuICAgICAgfSBlbHNlIGlmIChuZXdWbm9kZS5kb20uY2hpbGROb2Rlc1tpXSAhPT0gbmV3Q2hpbGQuZG9tKSB7XG4gICAgICAgIG5ld1Zub2RlLmRvbS5yZXBsYWNlQ2hpbGQobmV3Q2hpbGQuZG9tLCBuZXdWbm9kZS5kb20uY2hpbGROb2Rlc1tpXSk7XG4gICAgICB9XG5cbiAgICAgIC8vIElmIHRoZSBzaG91bGRQYXRjaCBmbGFnIGlzIHRydWUsIHJlY3Vyc2l2ZWx5IGNhbGwgdGhlIHBhdGNoIGZ1bmN0aW9uIG9uIHRoZSBuZXcgY2hpbGQsIHBhc3NpbmcgaW4gdGhlIG9sZCBjaGlsZCBhcyB0aGUgc2Vjb25kIGFyZ3VtZW50XG4gICAgICBzaG91bGRQYXRjaCAmJiBwYXRjaChuZXdDaGlsZCwgb2xkQ2hpbGQpO1xuICAgIH1cblxuICAgIC8vIEZvciB0aGUgcmVzdCBvZiB0aGUgY2hpbGRyZW4sIHdlIHNob3VsZCByZW1vdmUgdGhlbSBmcm9tIHRoZSBET01cbiAgICBmb3IgKGxldCBpID0gbmV3VHJlZUxlbmd0aDsgaSA8IG9sZFRyZWVMZW5ndGg7IGkrKykge1xuICAgICAgLy8gSWYgdGhlIGktdGggY2hpbGQgb2YgdGhlIG9sZCB0cmVlIGRvZXMgbm90IGhhdmUgYSBjb3JyZXNwb25kaW5nIGtleSBpbiB0aGUgbmV3IHRyZWUsIHJlbW92ZSBpdHMgRE9NIGVsZW1lbnQgZnJvbSB0aGUgcGFyZW50IERPTSBlbGVtZW50XG4gICAgICBpZiAoIW5ld0tleWVkTGlzdFtvbGRUcmVlW2ldLnByb3BzLmtleV0pIHtcbiAgICAgICAgb2xkVHJlZVtpXS5kb20ucGFyZW50Tm9kZSAmJiBvbGRUcmVlW2ldLmRvbS5wYXJlbnROb2RlLnJlbW92ZUNoaWxkKG9sZFRyZWVbaV0uZG9tKTtcbiAgICAgIH1cbiAgICB9XG4gICAgcmV0dXJuO1xuICB9XG5cbiAgLy8gSWYgdGhlIG5ldyB0cmVlIGhhcyBubyBjaGlsZHJlbiwgc2V0IHRoZSB0ZXh0IGNvbnRlbnQgb2YgdGhlIHBhcmVudCBET00gZWxlbWVudCB0byBhbiBlbXB0eSBzdHJpbmdcbiAgaWYgKG5ld1RyZWUubGVuZ3RoID09PSAwKSB7XG4gICAgbmV3Vm5vZGUuZG9tLnRleHRDb250ZW50ID0gXCJcIjtcbiAgICByZXR1cm47XG4gIH1cblxuICAvLyBTZXQgdGhlIGdsb2JhbCBjdXJyZW50IG9iamVjdCB0byB0aGUgbmV3IGFuZCBvbGQgdmlydHVhbCBET00gbm9kZXNcbiAgY3VycmVudC52bm9kZSA9IG5ld1Zub2RlO1xuICBjdXJyZW50Lm9sZFZub2RlID0gb2xkVm5vZGU7XG5cbiAgLy8gRmxhdHRlbiB0aGUgbmV3IHRyZWVcbiAgLy8gVGFrZSBpbnRvIGFjY291bnQgdGhhdCBpcyBuZWNlc3NhcnkgdG8gZmxhdHRlbiB0aGUgdHJlZSBiZWZvcmUgdGhlIHBhdGNoIHByb2Nlc3NcbiAgLy8gdG8gbGV0IHRoZSBob29rcyBhbmQgc2lnbmFscyB3b3JrIHByb3Blcmx5XG4gIGZvciAobGV0IGkgPSAwOyBpIDwgbmV3VHJlZS5sZW5ndGg7IGkrKykge1xuICAgIGxldCBuZXdDaGlsZCA9IG5ld1RyZWVbaV07XG5cbiAgICAvLyBJZiB0aGUgbmV3IGNoaWxkIGlzIGEgVm5vZGUgYW5kIGlzIG5vdCBhIHRleHQgbm9kZVxuICAgIGlmIChuZXdDaGlsZCBpbnN0YW5jZW9mIFZub2RlICYmIG5ld0NoaWxkLnRhZyAhPT0gdGV4dFRhZykge1xuICAgICAgLy8gSWYgdGhlIG5ldyBjaGlsZCB0YWcgaXMgYSBzdHJpbmcganVzdCBjb250aW51ZSB0aGUgbG9vcFxuICAgICAgaWYgKHR5cGVvZiBuZXdDaGlsZC50YWcgPT09IFwic3RyaW5nXCIpIHtcbiAgICAgICAgY29udGludWU7XG4gICAgICB9XG5cbiAgICAgIC8vIElmIHRoZSB0YWcgb2YgdGhlIG5ldyBjaGlsZCBpcyBub3QgYSBzdHJpbmcsIGl0IGlzIGEgY29tcG9uZW50XG4gICAgICAvLyBTZXQgdGhlIGN1cnJlbnQgY29tcG9uZW50IHRvIHRoZSB0YWcgb2YgdGhlIG5ldyBjaGlsZFxuICAgICAgY3VycmVudC5jb21wb25lbnQgPSBuZXdDaGlsZC50YWc7XG4gICAgICAvLyBSZXBsYWNlIHRoZSBuZXcgY2hpbGQgd2l0aCB0aGUgcmVzdWx0IG9mIGNhbGxpbmcgaXRzIHZpZXcgb3IgYmluZCBtZXRob2QsIHBhc3NpbmcgaW4gdGhlIHByb3BzIGFuZCBjaGlsZHJlbiBhcyBhcmd1bWVudHNcbiAgICAgIG5ld1RyZWUuc3BsaWNlKFxuICAgICAgICBpLS0sXG4gICAgICAgIDEsXG4gICAgICAgIChcInZpZXdcIiBpbiBuZXdDaGlsZC50YWcgPyBuZXdDaGlsZC50YWcudmlldy5iaW5kKG5ld0NoaWxkLnRhZykgOiBuZXdDaGlsZC50YWcuYmluZChuZXdDaGlsZC50YWcpKShcbiAgICAgICAgICBuZXdDaGlsZC5wcm9wcyxcbiAgICAgICAgICAuLi5uZXdDaGlsZC5jaGlsZHJlblxuICAgICAgICApXG4gICAgICApO1xuICAgICAgY29udGludWU7XG4gICAgfVxuXG4gICAgLy8gSWYgdGhlIG5ldyBjaGlsZCBpcyBhbiBhcnJheSwgZmxhdHRlbiBpdCBhbmQgY29udGludWUgdGhlIGxvb3BcbiAgICBpZiAoQXJyYXkuaXNBcnJheShuZXdDaGlsZCkpIHtcbiAgICAgIG5ld1RyZWUuc3BsaWNlKGktLSwgMSwgLi4ubmV3Q2hpbGQpO1xuICAgICAgY29udGludWU7XG4gICAgfVxuXG4gICAgLy8gSWYgdGhlIG5ldyBjaGlsZCBpcyBudWxsIG9yIHVuZGVmaW5lZCwgcmVtb3ZlIGl0IGZyb20gdGhlIG5ldyB0cmVlIGFuZCBjb250aW51ZSB0aGUgbG9vcFxuICAgIGlmIChuZXdDaGlsZCA9PT0gbnVsbCB8fCBuZXdDaGlsZCA9PT0gdW5kZWZpbmVkKSB7XG4gICAgICBuZXdUcmVlLnNwbGljZShpLS0sIDEpO1xuICAgICAgY29udGludWU7XG4gICAgfVxuXG4gICAgLy8gSWYgdGhlIG5ldyBjaGlsZCBpcyBhIFZub2RlLCBzZXQgdGhlIHRleHQgb2YgdGhlIFZub2RlIHRvIHRoZSB0ZXh0IGNvbnRlbnQgb2YgaXRzIGRvbSBwcm9wZXJ0eVxuICAgIGlmIChuZXdDaGlsZCBpbnN0YW5jZW9mIFZub2RlKSB7XG4gICAgICAvLyBTZXQgdGhlIG5ldyBjaGlsZCB0byB0aGUgdGV4dCBjb250ZW50IG9mIGl0cyBkb20gcHJvcGVydHlcbiAgICAgIG5ld0NoaWxkLmNoaWxkcmVuWzBdID0gKG5ld0NoaWxkIGFzIFZub2RlV2l0aERvbSkuZG9tLnRleHRDb250ZW50O1xuICAgICAgY29udGludWU7XG4gICAgfVxuXG4gICAgLy8gSWYgdGhlIG5ldyBjaGlsZCBpcyBub3QgYSBWbm9kZSwgd3JhcCBpdCBpbiBhIHRleHQgVm5vZGVcbiAgICBuZXdDaGlsZCA9IG5ld1RyZWVbaV0gPSBuZXcgVm5vZGUodGV4dFRhZywge30sIFtuZXdDaGlsZF0pO1xuICB9XG5cbiAgLy8gUGF0Y2ggdGhlIHRoZSBvbGQgdHJlZVxuICBmb3IgKGxldCBpID0gMDsgaSA8IG5ld1RyZWUubGVuZ3RoOyBpKyspIHtcbiAgICBsZXQgbmV3Q2hpbGQgPSBuZXdUcmVlW2ldO1xuXG4gICAgLy8gSWYgdGhlIG5ldyBjaGlsZCBpcyBhIHRleHQgdm5vZGVcbiAgICBpZiAobmV3Q2hpbGQudGFnID09PSB0ZXh0VGFnKSB7XG4gICAgICAvLyBJZiB0aGVyZSBpcyBhbiBvbGQgY2hpbGQgYXQgdGhlIHNhbWUgaW5kZXhcbiAgICAgIGlmIChpIDwgb2xkVHJlZUxlbmd0aCkge1xuICAgICAgICBsZXQgb2xkQ2hpbGQgPSBvbGRUcmVlW2ldO1xuXG4gICAgICAgIC8vIElmIHRoZSBvbGQgY2hpbGQgaXMgYSB0ZXh0IG5vZGVcbiAgICAgICAgaWYgKG9sZENoaWxkLnRhZyA9PT0gdGV4dFRhZykge1xuICAgICAgICAgIC8vIFNldCB0aGUgZG9tIHByb3BlcnR5IG9mIHRoZSB0ZXh0IFZub2RlIHRvIHRoZSBkb20gcHJvcGVydHkgb2YgdGhlIG9sZCBjaGlsZFxuICAgICAgICAgIG5ld0NoaWxkLmRvbSA9IG9sZENoaWxkLmRvbTtcbiAgICAgICAgICAvLyBJZiB0aGUgdGV4dCBjb250ZW50IG9mIHRoZSBvbGQgY2hpbGQgaXMgZGlmZmVyZW50IGZyb20gdGhlIG5ldyBjaGlsZCwgdXBkYXRlIHRoZSB0ZXh0IGNvbnRlbnQgb2YgdGhlIG9sZCBjaGlsZFxuICAgICAgICAgIC8vIGVzbGludC1kaXNhYmxlLW5leHQtbGluZSBlcWVxZXFcbiAgICAgICAgICBpZiAobmV3Q2hpbGQuY2hpbGRyZW5bMF0gIT0gb2xkQ2hpbGQuZG9tLnRleHRDb250ZW50KSB7XG4gICAgICAgICAgICBvbGRDaGlsZC5kb20udGV4dENvbnRlbnQgPSBuZXdDaGlsZC5jaGlsZHJlblswXTtcbiAgICAgICAgICB9XG4gICAgICAgICAgY29udGludWU7XG4gICAgICAgIH1cblxuICAgICAgICAvLyBDcmVhdGUgYSBuZXcgdGV4dCBub2RlIGZvciB0aGUgbmV3IGNoaWxkXG4gICAgICAgIG5ld0NoaWxkLmRvbSA9IGRvY3VtZW50LmNyZWF0ZVRleHROb2RlKG5ld0NoaWxkLmNoaWxkcmVuWzBdKTtcbiAgICAgICAgLy8gUmVwbGFjZSB0aGUgb2xkIGNoaWxkIGluIHRoZSBkb20gd2l0aCB0aGUgbmV3IHRleHQgbm9kZVxuICAgICAgICBuZXdWbm9kZS5kb20ucmVwbGFjZUNoaWxkKG5ld0NoaWxkLmRvbSwgb2xkQ2hpbGQuZG9tKTtcbiAgICAgICAgY29udGludWU7XG4gICAgICB9XG5cbiAgICAgIC8vIENyZWF0ZSBhIG5ldyB0ZXh0IG5vZGUgZm9yIHRoZSBuZXcgY2hpbGRcbiAgICAgIG5ld0NoaWxkLmRvbSA9IGRvY3VtZW50LmNyZWF0ZVRleHROb2RlKG5ld0NoaWxkLmNoaWxkcmVuWzBdKTtcbiAgICAgIC8vIEFwcGVuZCB0aGUgbmV3IHRleHQgbm9kZSB0byB0aGUgZG9tXG4gICAgICBuZXdWbm9kZS5kb20uYXBwZW5kQ2hpbGQobmV3Q2hpbGQuZG9tKTtcbiAgICAgIGNvbnRpbnVlO1xuICAgIH1cblxuICAgIC8vIElmIHRoZSBuZXcgY2hpbGQgaXMgbm90IGEgdGV4dCBub2RlXG4gICAgLy8gU2V0IHRoZSBpc1NWRyBmbGFnIGZvciB0aGUgbmV3IGNoaWxkIGlmIGl0IGlzIGFuIFNWRyBlbGVtZW50IG9yIGlmIHRoZSBwYXJlbnQgaXMgYW4gU1ZHIGVsZW1lbnRcbiAgICBuZXdDaGlsZC5pc1NWRyA9IG5ld1Zub2RlLmlzU1ZHIHx8IG5ld0NoaWxkLnRhZyA9PT0gXCJzdmdcIjtcblxuICAgIC8vIElmIHRoZXJlIGlzIGFuIG9sZCBjaGlsZCBhdCB0aGUgc2FtZSBpbmRleFxuICAgIGlmIChpIDwgb2xkVHJlZUxlbmd0aCkge1xuICAgICAgbGV0IG9sZENoaWxkID0gb2xkVHJlZVtpXTtcbiAgICAgIC8vIElmIHRoZSB0YWcgb2YgdGhlIG5ldyBjaGlsZCBpcyB0aGUgc2FtZSBhcyB0aGUgdGFnIG9mIHRoZSBvbGQgY2hpbGRcbiAgICAgIGlmIChuZXdDaGlsZC50YWcgPT09IG9sZENoaWxkLnRhZykge1xuICAgICAgICAvLyBTZXQgdGhlIGRvbSBwcm9wZXJ0eSBvZiB0aGUgbmV3IGNoaWxkIHRvIHRoZSBkb20gcHJvcGVydHkgb2YgdGhlIG9sZCBjaGlsZFxuICAgICAgICBuZXdDaGlsZC5kb20gPSBvbGRDaGlsZC5kb207XG4gICAgICAgIC8vIElmIHRoZSB2LWtlZXAgcHJvcCBpcyB0aGUgc2FtZSBmb3IgYm90aCB0aGUgbmV3IGFuZCBvbGQgY2hpbGQsIHNldCB0aGUgY2hpbGRyZW4gb2YgdGhlIG5ldyBjaGlsZCB0byB0aGUgY2hpbGRyZW4gb2YgdGhlIG9sZCBjaGlsZFxuICAgICAgICBpZiAoXCJ2LWtlZXBcIiBpbiBuZXdDaGlsZC5wcm9wcyAmJiBuZXdDaGlsZC5wcm9wc1tcInYta2VlcFwiXSA9PT0gb2xkQ2hpbGQucHJvcHNbXCJ2LWtlZXBcIl0pIHtcbiAgICAgICAgICBuZXdDaGlsZC5jaGlsZHJlbiA9IG9sZENoaWxkLmNoaWxkcmVuO1xuICAgICAgICAgIGNvbnRpbnVlO1xuICAgICAgICB9XG5cbiAgICAgICAgLy8gVXBkYXRlIHRoZSBhdHRyaWJ1dGVzIG9mIHRoZSBuZXcgY2hpbGQgYmFzZWQgb24gdGhlIG9sZCBjaGlsZFxuICAgICAgICB1cGRhdGVBdHRyaWJ1dGVzKG5ld0NoaWxkIGFzIFZub2RlV2l0aERvbSwgb2xkQ2hpbGQpO1xuICAgICAgICAvLyBSZWN1cnNpdmVseSBwYXRjaCB0aGUgbmV3IGFuZCBvbGQgY2hpbGRyZW5cbiAgICAgICAgcGF0Y2gobmV3Q2hpbGQgYXMgVm5vZGVXaXRoRG9tLCBvbGRDaGlsZCk7XG4gICAgICAgIGNvbnRpbnVlO1xuICAgICAgfVxuXG4gICAgICAvLyBDcmVhdGUgYSBuZXcgZG9tIGVsZW1lbnQgZm9yIHRoZSBuZXcgY2hpbGRcbiAgICAgIG5ld0NoaWxkLmRvbSA9IGNyZWF0ZURvbUVsZW1lbnQobmV3Q2hpbGQudGFnIGFzIHN0cmluZywgbmV3Q2hpbGQuaXNTVkcpO1xuICAgICAgLy8gVXBkYXRlIHRoZSBhdHRyaWJ1dGVzIG9mIHRoZSBuZXcgY2hpbGRcbiAgICAgIHVwZGF0ZUF0dHJpYnV0ZXMobmV3Q2hpbGQgYXMgVm5vZGVXaXRoRG9tKTtcbiAgICAgIC8vIFJlcGxhY2UgdGhlIG9sZCBjaGlsZCBpbiB0aGUgZG9tIHdpdGggdGhlIG5ldyBjaGlsZFxuICAgICAgbmV3Vm5vZGUuZG9tLnJlcGxhY2VDaGlsZChuZXdDaGlsZC5kb20sIG9sZENoaWxkLmRvbSk7XG4gICAgICAvLyBSZWN1cnNpdmVseSBwYXRjaCB0aGUgbmV3IGNoaWxkXG4gICAgICBwYXRjaChuZXdDaGlsZCBhcyBWbm9kZVdpdGhEb20pO1xuICAgICAgY29udGludWU7XG4gICAgfVxuXG4gICAgLy8gQ3JlYXRlIGEgbmV3IGRvbSBlbGVtZW50IGZvciB0aGUgbmV3IGNoaWxkXG4gICAgbmV3Q2hpbGQuZG9tID0gY3JlYXRlRG9tRWxlbWVudChuZXdDaGlsZC50YWcgYXMgc3RyaW5nLCBuZXdDaGlsZC5pc1NWRyk7XG4gICAgLy8gVXBkYXRlIHRoZSBhdHRyaWJ1dGVzIG9mIHRoZSBuZXcgY2hpbGRcbiAgICB1cGRhdGVBdHRyaWJ1dGVzKG5ld0NoaWxkIGFzIFZub2RlV2l0aERvbSk7XG4gICAgLy8gQXBwZW5kIHRoZSBuZXcgY2hpbGQgdG8gdGhlIGRvbVxuICAgIG5ld1Zub2RlLmRvbS5hcHBlbmRDaGlsZChuZXdDaGlsZC5kb20pO1xuICAgIC8vIFJlY3Vyc2l2ZWx5IHBhdGNoIHRoZSBuZXcgY2hpbGRcbiAgICBwYXRjaChuZXdDaGlsZCBhcyBWbm9kZVdpdGhEb20pO1xuICB9XG5cbiAgLy8gUmVtb3ZlIGFueSBvbGQgY2hpbGRyZW4gdGhhdCBhcmUgbm8gbG9uZ2VyIHByZXNlbnQgaW4gdGhlIG5ldyB0cmVlXG4gIGZvciAobGV0IGkgPSBuZXdUcmVlLmxlbmd0aDsgaSA8IG9sZFRyZWVMZW5ndGg7IGkrKykge1xuICAgIG5ld1Zub2RlLmRvbS5yZW1vdmVDaGlsZChvbGRUcmVlW2ldLmRvbSk7XG4gIH1cbn1cblxuLy8gVXBkYXRlIHRoZSBtYWluIFZub2RlXG5leHBvcnQgZnVuY3Rpb24gdXBkYXRlKCk6IHZvaWQgfCBzdHJpbmcge1xuICAvLyBJZiB0aGUgbWFpbiBWbm9kZSBleGlzdHNcbiAgaWYgKG1haW5Wbm9kZSkge1xuICAgIC8vIENhbGwgYW55IGNsZWFudXAgZnVuY3Rpb25zIHRoYXQgYXJlIHJlZ2lzdGVyZWQgd2l0aCB0aGUgb25DbGVhbnVwU2V0IHNldFxuICAgIGNhbGxTZXQob25DbGVhbnVwU2V0KTtcbiAgICAvLyBTdG9yZSBhIHJlZmVyZW5jZSB0byB0aGUgb2xkIG1haW4gVm5vZGVcbiAgICBsZXQgb2xkTWFpblZub2RlID0gbWFpblZub2RlO1xuICAgIC8vIENyZWF0ZSBhIG5ldyBtYWluIFZub2RlIHdpdGggdGhlIG1haW4gY29tcG9uZW50IGFzIGl0cyBvbmx5IGNoaWxkXG4gICAgbWFpblZub2RlID0gbmV3IFZub2RlKG9sZE1haW5Wbm9kZS50YWcsIG9sZE1haW5Wbm9kZS5wcm9wcywgW21haW5Db21wb25lbnRdKSBhcyBWbm9kZVdpdGhEb207XG4gICAgbWFpblZub2RlLmRvbSA9IG9sZE1haW5Wbm9kZS5kb207XG4gICAgbWFpblZub2RlLmlzU1ZHID0gb2xkTWFpblZub2RlLmlzU1ZHO1xuXG4gICAgLy8gUmVjdXJzaXZlbHkgcGF0Y2ggdGhlIG5ldyBhbmQgb2xkIG1haW4gVm5vZGVzXG4gICAgcGF0Y2gobWFpblZub2RlLCBvbGRNYWluVm5vZGUpO1xuXG4gICAgLy8gQ2FsbCBhbnkgdXBkYXRlIG9yIG1vdW50IGZ1bmN0aW9ucyB0aGF0IGFyZSByZWdpc3RlcmVkIHdpdGggdGhlIG9uVXBkYXRlU2V0IG9yIG9uTW91bnRTZXQgc2V0XG4gICAgY2FsbFNldChpc01vdW50ZWQgPyBvblVwZGF0ZVNldCA6IG9uTW91bnRTZXQpO1xuXG4gICAgLy8gU2V0IHRoZSBpc01vdW50ZWQgZmxhZyB0byB0cnVlXG4gICAgaXNNb3VudGVkID0gdHJ1ZTtcblxuICAgIC8vIFJlc2V0IHRoZSBjdXJyZW50IHZub2RlLCBvbGRWbm9kZSwgYW5kIGNvbXBvbmVudCBwcm9wZXJ0aWVzXG4gICAgY3VycmVudC52bm9kZSA9IG51bGw7XG4gICAgY3VycmVudC5vbGRWbm9kZSA9IG51bGw7XG4gICAgY3VycmVudC5jb21wb25lbnQgPSBudWxsO1xuXG4gICAgLy8gSWYgdGhlIGNvZGUgaXMgcnVubmluZyBpbiBhIE5vZGUuanMgZW52aXJvbm1lbnQsIHJldHVybiB0aGUgaW5uZXIgSFRNTCBvZiB0aGUgbWFpbiBWbm9kZSdzIGRvbSBlbGVtZW50XG4gICAgaWYgKGlzTm9kZUpzKSB7XG4gICAgICByZXR1cm4gbWFpblZub2RlLmRvbS5pbm5lckhUTUw7XG4gICAgfVxuICB9XG59XG5cbi8vIFVwZGF0ZSBjdXN0b20gVm5vZGVcbi8vIEl0IGlzIGFzc3VtZWQgdGhhdCBhIGZpcnN0IG1vdW50IGhhcyBhbHJlYWR5IG9jY3VycmVkLCBzbyxcbi8vIHRoZSBvbGRWbm9kZSBpcyBub3QgbnVsbCBhbmQgdGhlIGRvbSBwcm9wZXJ0eSBvZiB0aGUgb2xkVm5vZGUgaXMgbm90IG51bGxcbi8vIFlvdSBuZWVkIHRvIHNldCB0aGUgZG9tIHByb3BlcnR5IG9mIHRoZSBuZXdWbm9kZSB0byB0aGUgZG9tIHByb3BlcnR5IG9mIHRoZSBvbGRWbm9kZVxuLy8gVGhlIHNhbWUgd2l0aCB0aGUgaXNTVkcgcHJvcGVydHlcbi8vIFByZWZlciB0aGlzIGZ1bmN0aW9uIG92ZXIgcGF0Y2ggdG8gYWxsb3cgZm9yIGNsZWFudXAsIG9uVXBkYXRlIGFuZCBvbk1vdW50IHNldHMgdG8gYmUgY2FsbGVkXG5leHBvcnQgZnVuY3Rpb24gdXBkYXRlVm5vZGUodm5vZGU6IFZub2RlV2l0aERvbSwgb2xkVm5vZGU6IFZub2RlV2l0aERvbSk6IHN0cmluZyB8IHZvaWQge1xuICAvLyBDYWxsIGFueSBjbGVhbnVwIGZ1bmN0aW9ucyB0aGF0IGFyZSByZWdpc3RlcmVkIHdpdGggdGhlIG9uQ2xlYW51cFNldCBzZXRcbiAgY2FsbFNldChvbkNsZWFudXBTZXQpO1xuXG4gIC8vIFJlY3Vyc2l2ZWx5IHBhdGNoIHRoZSBuZXcgYW5kIG9sZCBtYWluIFZub2Rlc1xuICBwYXRjaCh2bm9kZSwgb2xkVm5vZGUpO1xuXG4gIC8vIFNldCB0aGUgdm5vZGUgcHJvcGVydGllcyB0byB0aGUgb2xkIHZub2RlXG4gIG9sZFZub2RlLnRhZyA9IHZub2RlLnRhZztcbiAgb2xkVm5vZGUucHJvcHMgPSB7IC4uLnZub2RlLnByb3BzIH07XG4gIG9sZFZub2RlLmNoaWxkcmVuID0gWy4uLnZub2RlLmNoaWxkcmVuXTtcblxuICAvLyBDYWxsIGFueSB1cGRhdGUgb3IgbW91bnQgZnVuY3Rpb25zIHRoYXQgYXJlIHJlZ2lzdGVyZWQgd2l0aCB0aGUgb25VcGRhdGVTZXQgb3Igb25Nb3VudFNldCBzZXRcbiAgY2FsbFNldChpc01vdW50ZWQgPyBvblVwZGF0ZVNldCA6IG9uTW91bnRTZXQpO1xuXG4gIC8vIFNldCB0aGUgaXNNb3VudGVkIGZsYWcgdG8gdHJ1ZVxuICBpc01vdW50ZWQgPSB0cnVlO1xuXG4gIC8vIFJlc2V0IHRoZSBjdXJyZW50IHZub2RlLCBvbGRWbm9kZSwgYW5kIGNvbXBvbmVudCBwcm9wZXJ0aWVzXG4gIGN1cnJlbnQudm5vZGUgPSBudWxsO1xuICBjdXJyZW50Lm9sZFZub2RlID0gbnVsbDtcbiAgY3VycmVudC5jb21wb25lbnQgPSBudWxsO1xuXG4gIGlmIChpc05vZGVKcykge1xuICAgIHJldHVybiB2bm9kZS5kb20uaW5uZXJIVE1MO1xuICB9XG59XG5cbi8vIFVubW91bnQgdGhlIG1haW4gVm5vZGVcbmV4cG9ydCBmdW5jdGlvbiB1bm1vdW50KCkge1xuICAvLyBJZiB0aGUgbWFpbiBWbm9kZSBleGlzdHNcbiAgaWYgKG1haW5Wbm9kZSkge1xuICAgIC8vIFNldCB0aGUgbWFpbiBjb21wb25lbnQgdG8gYSBudWxsIFZub2RlXG4gICAgbWFpbkNvbXBvbmVudCA9IG5ldyBWbm9kZSgoKSA9PiBudWxsLCB7fSwgW10pIGFzIFZub2RlQ29tcG9uZW50SW50ZXJmYWNlO1xuICAgIC8vIFVwZGF0ZSB0aGUgbWFpbiBWbm9kZVxuICAgIGxldCByZXN1bHQgPSB1cGRhdGUoKTtcbiAgICAvLyBDYWxsIGFueSB1bm1vdW50IGZ1bmN0aW9ucyB0aGF0IGFyZSByZWdpc3RlcmVkIHdpdGggdGhlIG9uVW5tb3VudFNldCBzZXRcbiAgICBjYWxsU2V0KG9uVW5tb3VudFNldCk7XG5cbiAgICAvLyBSZW1vdmUgYW55IGV2ZW50IGxpc3RlbmVycyB0aGF0IHdlcmUgYWRkZWQgdG8gdGhlIG1haW4gVm5vZGUncyBkb20gZWxlbWVudFxuICAgIGZvciAobGV0IG5hbWUgaW4gZXZlbnRMaXN0ZW5lck5hbWVzKSB7XG4gICAgICBtYWluVm5vZGUuZG9tLnJlbW92ZUV2ZW50TGlzdGVuZXIobmFtZS5zbGljZSgyKS50b0xvd2VyQ2FzZSgpLCBldmVudExpc3RlbmVyKTtcbiAgICAgIFJlZmxlY3QuZGVsZXRlUHJvcGVydHkoZXZlbnRMaXN0ZW5lck5hbWVzLCBuYW1lKTtcbiAgICB9XG5cbiAgICAvLyBSZXNldCB0aGUgbWFpbiBjb21wb25lbnQgYW5kIG1haW4gVm5vZGVcbiAgICBtYWluQ29tcG9uZW50ID0gbnVsbDtcbiAgICBtYWluVm5vZGUgPSBudWxsO1xuICAgIC8vIFNldCB0aGUgaXNNb3VudGVkIGZsYWcgdG8gZmFsc2VcbiAgICBpc01vdW50ZWQgPSBmYWxzZTtcbiAgICAvLyBSZXNldCB0aGUgY3VycmVudCB2bm9kZSwgb2xkVm5vZGUsIGFuZCBjb21wb25lbnQgcHJvcGVydGllc1xuICAgIGN1cnJlbnQudm5vZGUgPSBudWxsO1xuICAgIGN1cnJlbnQub2xkVm5vZGUgPSBudWxsO1xuICAgIGN1cnJlbnQuY29tcG9uZW50ID0gbnVsbDtcbiAgICAvLyBSZXR1cm4gdGhlIHJlc3VsdCBvZiB1cGRhdGluZyB0aGUgbWFpbiBWbm9kZVxuICAgIHJldHVybiByZXN1bHQ7XG4gIH1cbn1cbi8vIFRoaXMgZnVuY3Rpb24gdGFrZXMgaW4gYSBET00gZWxlbWVudCBvciBhIERPTSBlbGVtZW50IHNlbGVjdG9yIGFuZCBhIGNvbXBvbmVudCB0byBiZSBtb3VudGVkIG9uIGl0LlxuZXhwb3J0IGZ1bmN0aW9uIG1vdW50KGRvbSwgY29tcG9uZW50KSB7XG4gIC8vIENoZWNrIGlmIHRoZSAnZG9tJyBhcmd1bWVudCBpcyBhIHN0cmluZy4gSWYgaXQgaXMsIHNlbGVjdCB0aGUgZmlyc3QgZWxlbWVudCB0aGF0IG1hdGNoZXMgdGhlIGdpdmVuIHNlbGVjdG9yLlxuICAvLyBPdGhlcndpc2UsIHVzZSB0aGUgJ2RvbScgYXJndW1lbnQgYXMgdGhlIGNvbnRhaW5lci5cbiAgbGV0IGNvbnRhaW5lciA9XG4gICAgdHlwZW9mIGRvbSA9PT0gXCJzdHJpbmdcIlxuICAgICAgPyBpc05vZGVKc1xuICAgICAgICA/IGNyZWF0ZURvbUVsZW1lbnQoZG9tLCBkb20gPT09IFwic3ZnXCIpXG4gICAgICAgIDogZG9jdW1lbnQucXVlcnlTZWxlY3RvckFsbChkb20pWzBdXG4gICAgICA6IGRvbTtcblxuICAvLyBDaGVjayBpZiB0aGUgJ2NvbXBvbmVudCcgYXJndW1lbnQgaXMgYSBWbm9kZSBjb21wb25lbnQgb3IgYSByZWd1bGFyIGNvbXBvbmVudC5cbiAgLy8gSWYgaXQncyBhIHJlZ3VsYXIgY29tcG9uZW50LCBjcmVhdGUgYSBuZXcgVm5vZGUgY29tcG9uZW50IHVzaW5nIHRoZSAnY29tcG9uZW50JyBhcmd1bWVudCBhcyB0aGUgdGFnLlxuICAvLyBJZiBpdCdzIG5vdCBhIGNvbXBvbmVudCBhdCBhbGwsIGNyZWF0ZSBhIG5ldyBWbm9kZSBjb21wb25lbnQgd2l0aCB0aGUgJ2NvbXBvbmVudCcgYXJndW1lbnQgYXMgdGhlIHJlbmRlcmluZyBmdW5jdGlvbi5cbiAgbGV0IHZub2RlQ29tcG9uZW50ID0gaXNWbm9kZUNvbXBvbmVudChjb21wb25lbnQpXG4gICAgPyBjb21wb25lbnRcbiAgICA6IGlzQ29tcG9uZW50KGNvbXBvbmVudClcbiAgICA/IG5ldyBWbm9kZShjb21wb25lbnQsIHt9LCBbXSlcbiAgICA6IG5ldyBWbm9kZSgoKSA9PiBjb21wb25lbnQsIHt9LCBbXSk7XG5cbiAgLy8gSWYgYSBtYWluIGNvbXBvbmVudCBhbHJlYWR5IGV4aXN0cyBhbmQgaXQncyBub3QgdGhlIHNhbWUgYXMgdGhlIGN1cnJlbnQgJ3Zub2RlQ29tcG9uZW50JywgdW5tb3VudCBpdC5cbiAgaWYgKG1haW5Db21wb25lbnQgJiYgbWFpbkNvbXBvbmVudC50YWcgIT09IHZub2RlQ29tcG9uZW50LnRhZykge1xuICAgIHVubW91bnQoKTtcbiAgfVxuXG4gIC8vIFNldCB0aGUgJ3Zub2RlQ29tcG9uZW50JyBhcyB0aGUgbWFpbiBjb21wb25lbnQuXG4gIG1haW5Db21wb25lbnQgPSB2bm9kZUNvbXBvbmVudCBhcyBWbm9kZUNvbXBvbmVudEludGVyZmFjZTtcbiAgLy8gQ29udmVydCB0aGUgY29udGFpbmVyIGVsZW1lbnQgdG8gYSBWbm9kZS5cbiAgbWFpblZub2RlID0gZG9tVG9Wbm9kZShjb250YWluZXIpO1xuICAvLyBVcGRhdGUgdGhlIERPTSB3aXRoIHRoZSBuZXcgY29tcG9uZW50LlxuICByZXR1cm4gdXBkYXRlKCk7XG59XG5cbi8vIFRoaXMgaXMgYSB1dGlsaXR5IGZ1bmN0aW9uIGZvciBjcmVhdGluZyBWbm9kZSBvYmplY3RzLlxuLy8gSXQgdGFrZXMgaW4gYSB0YWcgb3IgY29tcG9uZW50LCBhbmQgb3B0aW9uYWwgcHJvcHMgYW5kIGNoaWxkcmVuIGFyZ3VtZW50cy5cbmV4cG9ydCBjb25zdCB2OiBWID0gKHRhZ09yQ29tcG9uZW50LCBwcm9wcyA9IHt9LCAuLi5jaGlsZHJlbikgPT4ge1xuICAvLyBSZXR1cm4gYSBuZXcgVm5vZGUgb2JqZWN0IHVzaW5nIHRoZSBnaXZlbiBhcmd1bWVudHMuXG4gIHJldHVybiBuZXcgVm5vZGUodGFnT3JDb21wb25lbnQsIHByb3BzIHx8IHt9LCBjaGlsZHJlbik7XG59O1xuXG4vLyBUaGlzIHV0aWxpdHkgZnVuY3Rpb24gY3JlYXRlcyBhIGZyYWdtZW50IFZub2RlLlxuLy8gSXQgdGFrZXMgaW4gYSBwbGFjZWhvbGRlciBhbmQgdGhlIGNoaWxkcmVuIGFyZ3VtZW50cywgcmV0dXJucyBvbmx5IHRoZSBjaGlsZHJlbi5cbnYuZnJhZ21lbnQgPSAoXzogVm5vZGVQcm9wZXJ0aWVzLCAuLi5jaGlsZHJlbjogQ2hpbGRyZW4pID0+IGNoaWxkcmVuO1xuIl0sIm1hcHBpbmdzIjoiTUFnSUEsSUFBTUEsRUFBVSxRQUlMQyxFQUFXQyxRQUEyQixvQkFBWkMsU0FBMkJBLFFBQVFDLFVBQVlELFFBQVFDLFNBQVNDLE1BSTlGLFNBQVNDLEVBQWlCQyxFQUFhQyxHQUFpQixHQUM3RCxPQUFPQSxFQUFRQyxTQUFTQyxnQkFBZ0IsNkJBQThCSCxHQUFPRSxTQUFTRSxjQUFjSixFQUN0RyxDQU1PLElBQU1LLEVBQVEsU0FBcUNMLEVBQWFNLEVBQXdCQyxHQUU3RkMsS0FBS1IsSUFBTUEsRUFDWFEsS0FBS0YsTUFBUUEsRUFDYkUsS0FBS0QsU0FBV0EsQ0FDbEIsRUFJTyxTQUFTRSxFQUFZQyxHQUMxQixPQUFPQSxJQUFtQyxtQkFBZEEsR0FBa0QsaUJBQWRBLEdBQTBCLFNBQVVBLEVBQ3RHLENBR08sSUFBTUMsRUFBV0MsR0FFZkEsYUFBa0JQLEVBS2RRLEVBQW9CRCxHQUV4QkQsRUFBUUMsSUFBV0gsRUFBWUcsRUFBT1osS0FJL0MsU0FBU2MsRUFBV0MsR0FDbEIsSUFBSVIsRUFBMkIsR0FFL0IsUUFBU1MsRUFBSSxFQUFHQyxFQUFJRixFQUFJRyxXQUFXQyxPQUFRSCxFQUFJQyxFQUFHRCxJQUFLLENBQ3JELElBQUlJLEVBQVdMLEVBQUlHLFdBQVdGLEdBSTlCLEdBQTBCLElBQXRCSSxFQUFTQyxTQVNhLElBQXRCRCxFQUFTQyxVQUNYZCxFQUFTZSxLQUFLUixFQUFXTSxRQVYzQixDQUNFLElBQUlHLEVBQVEsSUFBSWxCLEVBQU1aLEVBQVMsQ0FBQyxFQUFHLElBQ25DOEIsRUFBTVIsSUFBTUssRUFDWmIsRUFBU2UsS0FBS0MsRUFFaEIsQ0FPRixDQUVBLElBQUlqQixFQUF5QixDQUFDLEVBRTlCLFFBQVNVLEVBQUksRUFBR0MsRUFBSUYsRUFBSVMsV0FBV0wsT0FBUUgsRUFBSUMsRUFBR0QsSUFBSyxDQUNyRCxJQUFJUyxFQUFP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 \ No newline at end of file diff --git a/dist/index.mjs b/dist/index.mjs index b05d382..75353b0 100644 --- a/dist/index.mjs +++ b/dist/index.mjs @@ -263,11 +263,12 @@ function directive(name, directive2) { } function sharedSetAttribute(name, value, newVnode, oldVnode) { if (typeof value === "function") { - if (name in eventListenerNames === false) { - mainVnode.dom.addEventListener(name.slice(2), eventListener); - eventListenerNames[name] = true; + let lowercaseName = name.toLowerCase(); + if (lowercaseName in eventListenerNames === false) { + mainVnode.dom.addEventListener(lowercaseName.slice(2), eventListener); + eventListenerNames[lowercaseName] = true; } - newVnode.dom[`v-${name}`] = value; + newVnode.dom[`v-${lowercaseName}`] = value; return; } if (name in newVnode.dom && newVnode.isSVG === false) { @@ -366,41 +367,18 @@ function patch(newVnode, oldVnode) { for (let i = 0; i < newTree.length; i++) { let newChild = newTree[i]; if (newChild instanceof Vnode && newChild.tag !== textTag) { - if (typeof newChild.tag !== "string") { - current.component = newChild.tag; - newTree.splice( - i--, - 1, - ("view" in newChild.tag ? newChild.tag.view.bind(newChild.tag) : newChild.tag.bind(newChild.tag))( - newChild.props, - ...newChild.children - ) - ); + if (typeof newChild.tag === "string") { continue; } - newChild.isSVG = newVnode.isSVG || newChild.tag === "svg"; - if (i < oldTreeLength) { - let oldChild = oldTree[i]; - if (newChild.tag === oldChild.tag) { - newChild.dom = oldChild.dom; - if ("v-keep" in newChild.props && newChild.props["v-keep"] === oldChild.props["v-keep"]) { - newChild.children = oldChild.children; - continue; - } - updateAttributes(newChild, oldChild); - patch(newChild, oldChild); - continue; - } - newChild.dom = createDomElement(newChild.tag, newChild.isSVG); - updateAttributes(newChild); - newVnode.dom.replaceChild(newChild.dom, oldChild.dom); - patch(newChild); - continue; - } - newChild.dom = createDomElement(newChild.tag, newChild.isSVG); - updateAttributes(newChild); - newVnode.dom.appendChild(newChild.dom); - patch(newChild); + current.component = newChild.tag; + newTree.splice( + i--, + 1, + ("view" in newChild.tag ? newChild.tag.view.bind(newChild.tag) : newChild.tag.bind(newChild.tag))( + newChild.props, + ...newChild.children + ) + ); continue; } if (Array.isArray(newChild)) { @@ -411,26 +389,55 @@ function patch(newVnode, oldVnode) { newTree.splice(i--, 1); continue; } - newTree[i] = new Vnode(textTag, {}, []); if (newChild instanceof Vnode) { - newTree[i].dom = newChild.dom; - newChild = newChild.dom.textContent; + newChild.children[0] = newChild.dom.textContent; + continue; } + newChild = newTree[i] = new Vnode(textTag, {}, [newChild]); + } + for (let i = 0; i < newTree.length; i++) { + let newChild = newTree[i]; + if (newChild.tag === textTag) { + if (i < oldTreeLength) { + let oldChild = oldTree[i]; + if (oldChild.tag === textTag) { + newChild.dom = oldChild.dom; + if (newChild.children[0] != oldChild.dom.textContent) { + oldChild.dom.textContent = newChild.children[0]; + } + continue; + } + newChild.dom = document.createTextNode(newChild.children[0]); + newVnode.dom.replaceChild(newChild.dom, oldChild.dom); + continue; + } + newChild.dom = document.createTextNode(newChild.children[0]); + newVnode.dom.appendChild(newChild.dom); + continue; + } + newChild.isSVG = newVnode.isSVG || newChild.tag === "svg"; if (i < oldTreeLength) { let oldChild = oldTree[i]; - if (oldChild.tag === textTag) { - newTree[i].dom = oldChild.dom; - if (newChild != oldChild.dom.textContent) { - oldChild.dom.textContent = newChild; + if (newChild.tag === oldChild.tag) { + newChild.dom = oldChild.dom; + if ("v-keep" in newChild.props && newChild.props["v-keep"] === oldChild.props["v-keep"]) { + newChild.children = oldChild.children; + continue; } + updateAttributes(newChild, oldChild); + patch(newChild, oldChild); continue; } - newTree[i].dom = document.createTextNode(newChild); - newVnode.dom.replaceChild(newTree[i].dom, oldChild.dom); + newChild.dom = createDomElement(newChild.tag, newChild.isSVG); + updateAttributes(newChild); + newVnode.dom.replaceChild(newChild.dom, oldChild.dom); + patch(newChild); continue; } - newTree[i].dom = document.createTextNode(newChild); - newVnode.dom.appendChild(newTree[i].dom); + newChild.dom = createDomElement(newChild.tag, newChild.isSVG); + updateAttributes(newChild); + newVnode.dom.appendChild(newChild.dom); + patch(newChild); } for (let i = newTree.length; i < oldTreeLength; i++) { newVnode.dom.removeChild(oldTree[i].dom); @@ -454,6 +461,21 @@ function update() { } } } +function updateVnode(vnode, oldVnode) { + callSet(onCleanupSet); + patch(vnode, oldVnode); + oldVnode.tag = vnode.tag; + oldVnode.props = { ...vnode.props }; + oldVnode.children = [...vnode.children]; + callSet(isMounted ? onUpdateSet : onMountSet); + isMounted = true; + current.vnode = null; + current.oldVnode = null; + current.component = null; + if (isNodeJs) { + return vnode.dom.innerHTML; + } +} function unmount() { if (mainVnode) { mainComponent = new Vnode(() => null, {}, []); @@ -508,5 +530,6 @@ export { unmount, update, updateAttributes, + updateVnode, v }; diff --git a/dist/proxy-signal/index.js b/dist/proxy-signal/index.js new file mode 100644 index 0000000..0df1664 --- /dev/null +++ b/dist/proxy-signal/index.js @@ -0,0 +1,136 @@ +var __defProp = Object.defineProperty; +var __getOwnPropDesc = Object.getOwnPropertyDescriptor; +var __getOwnPropNames = Object.getOwnPropertyNames; +var __hasOwnProp = Object.prototype.hasOwnProperty; +var __export = (target, all) => { + for (var name in all) + __defProp(target, name, { get: all[name], enumerable: true }); +}; +var __copyProps = (to, from, except, desc) => { + if (from && typeof from === "object" || typeof from === "function") { + for (let key of __getOwnPropNames(from)) + if (!__hasOwnProp.call(to, key) && key !== except) + __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); + } + return to; +}; +var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); + +// lib/proxy-signal/index.ts +var proxy_signal_exports = {}; +__export(proxy_signal_exports, { + ProxySignal: () => ProxySignal +}); +module.exports = __toCommonJS(proxy_signal_exports); +var import_valyrian = require("valyrian.js"); +function makeUnsubscribe(subscriptions, computed, handler, cleanup) { + if (typeof cleanup === "function") { + computed.cleanup = cleanup; + } + computed.unsubscribe = () => { + subscriptions.delete(handler); + computed?.cleanup(); + }; +} +function createSubscription(signal, subscriptions, handler) { + if (subscriptions.has(handler) === false) { + let computed = ProxySignal(() => handler(signal.value)); + let cleanup = computed(); + makeUnsubscribe(subscriptions, computed, handler, cleanup); + subscriptions.set(handler, computed); + } + return subscriptions.get(handler); +} +var updateTimeout; +function delayedUpdate() { + clearTimeout(updateTimeout); + updateTimeout = setTimeout(import_valyrian.update); +} +function ProxySignal(value) { + let subscriptions = /* @__PURE__ */ new Map(); + let getters = {}; + let forceUpdate = false; + let signal = new Proxy( + function(valOrPath, handler) { + if (typeof valOrPath === "undefined") { + return signal.value; + } + if (typeof valOrPath === "function") { + return createSubscription(signal, subscriptions, valOrPath); + } + if (typeof valOrPath === "string" && typeof handler !== "undefined") { + let parsed = valOrPath.split("."); + let result = signal.value; + let next; + while (parsed.length) { + next = parsed.shift(); + if (parsed.length > 0) { + if (typeof result[next] !== "object") { + result[next] = {}; + } + result = result[next]; + } else { + result[next] = typeof handler === "function" ? handler(result[next]) : handler; + } + } + forceUpdate = true; + signal.value = signal.value; + return signal.value; + } + signal.value = valOrPath; + return signal.value; + }, + { + set(state, prop, val) { + if (prop === "value" || prop === "unsubscribe" || prop === "cleanup") { + let old = state[prop]; + state[prop] = val; + if (prop === "value" && (forceUpdate || val !== old)) { + forceUpdate = false; + for (let [handler, computed] of subscriptions) { + computed.cleanup(); + let cleanup = handler(val); + makeUnsubscribe(subscriptions, computed, handler, cleanup); + } + delayedUpdate(); + } + return true; + } + return false; + }, + get(state, prop) { + if (prop === "value") { + return typeof state.value === "function" ? state.value() : state.value; + } + if (prop === "cleanup" || prop === "unsubscribe" || prop === "getter") { + return state[prop]; + } + if (prop in getters) { + return getters[prop](state.value); + } + } + } + ); + Object.defineProperties(signal, { + value: { value, writable: true, enumerable: true }, + cleanup: { + value() { + for (let [handler, computed] of subscriptions) { + computed.unsubscribe(); + } + }, + writable: true, + enumerable: true + }, + getter: { + value(name, handler) { + if (name in getters) { + throw new Error("Named computed already exists."); + } + getters[name] = handler; + }, + enumerable: true + } + }); + return signal; +} diff --git a/dist/proxy-signal/index.mjs b/dist/proxy-signal/index.mjs new file mode 100644 index 0000000..100470b --- /dev/null +++ b/dist/proxy-signal/index.mjs @@ -0,0 +1,116 @@ +// lib/proxy-signal/index.ts +import { update } from "valyrian.js"; +function makeUnsubscribe(subscriptions, computed, handler, cleanup) { + if (typeof cleanup === "function") { + computed.cleanup = cleanup; + } + computed.unsubscribe = () => { + subscriptions.delete(handler); + computed?.cleanup(); + }; +} +function createSubscription(signal, subscriptions, handler) { + if (subscriptions.has(handler) === false) { + let computed = ProxySignal(() => handler(signal.value)); + let cleanup = computed(); + makeUnsubscribe(subscriptions, computed, handler, cleanup); + subscriptions.set(handler, computed); + } + return subscriptions.get(handler); +} +var updateTimeout; +function delayedUpdate() { + clearTimeout(updateTimeout); + updateTimeout = setTimeout(update); +} +function ProxySignal(value) { + let subscriptions = /* @__PURE__ */ new Map(); + let getters = {}; + let forceUpdate = false; + let signal = new Proxy( + function(valOrPath, handler) { + if (typeof valOrPath === "undefined") { + return signal.value; + } + if (typeof valOrPath === "function") { + return createSubscription(signal, subscriptions, valOrPath); + } + if (typeof valOrPath === "string" && typeof handler !== "undefined") { + let parsed = valOrPath.split("."); + let result = signal.value; + let next; + while (parsed.length) { + next = parsed.shift(); + if (parsed.length > 0) { + if (typeof result[next] !== "object") { + result[next] = {}; + } + result = result[next]; + } else { + result[next] = typeof handler === "function" ? handler(result[next]) : handler; + } + } + forceUpdate = true; + signal.value = signal.value; + return signal.value; + } + signal.value = valOrPath; + return signal.value; + }, + { + set(state, prop, val) { + if (prop === "value" || prop === "unsubscribe" || prop === "cleanup") { + let old = state[prop]; + state[prop] = val; + if (prop === "value" && (forceUpdate || val !== old)) { + forceUpdate = false; + for (let [handler, computed] of subscriptions) { + computed.cleanup(); + let cleanup = handler(val); + makeUnsubscribe(subscriptions, computed, handler, cleanup); + } + delayedUpdate(); + } + return true; + } + return false; + }, + get(state, prop) { + if (prop === "value") { + return typeof state.value === "function" ? state.value() : state.value; + } + if (prop === "cleanup" || prop === "unsubscribe" || prop === "getter") { + return state[prop]; + } + if (prop in getters) { + return getters[prop](state.value); + } + } + } + ); + Object.defineProperties(signal, { + value: { value, writable: true, enumerable: true }, + cleanup: { + value() { + for (let [handler, computed] of subscriptions) { + computed.unsubscribe(); + } + }, + writable: true, + enumerable: true + }, + getter: { + value(name, handler) { + if (name in getters) { + throw new Error("Named computed already exists."); + } + getters[name] = handler; + }, + enumerable: true + } + }); + return signal; +} +export { + ProxySignal +}; diff --git a/dist/signal/index.js b/dist/signal/index.js index 950fa8a..d23d591 100644 --- a/dist/signal/index.js +++ b/dist/signal/index.js @@ -23,114 +23,55 @@ __export(signal_exports, { }); module.exports = __toCommonJS(signal_exports); var import_valyrian = require("valyrian.js"); -function makeUnsubscribe(subscriptions, computed, handler, cleanup) { - if (typeof cleanup === "function") { - computed.cleanup = cleanup; +function Signal(initialValue) { + const context = { ...import_valyrian.current }; + if (context.vnode) { + if (!context.vnode.signals) { + context.vnode.signals = context.oldVnode?.signals || []; + context.vnode.calls = -1; + context.vnode.subscribers = context.oldVnode?.subscribers || []; + context.vnode.initialChildren = [...context.vnode.children]; + } + let signal2 = context.vnode.signals[++context.vnode.calls]; + if (signal2) { + return signal2; + } } - computed.unsubscribe = () => { - subscriptions.delete(handler); - computed?.cleanup(); + let value = initialValue; + const subscribers = []; + const subscribe = (callback) => { + if (subscribers.indexOf(callback) === -1) { + subscribers.push(callback); + } }; -} -function createSubscription(signal, subscriptions, handler) { - if (subscriptions.has(handler) === false) { - let computed = Signal(() => handler(signal.value)); - let cleanup = computed(); - makeUnsubscribe(subscriptions, computed, handler, cleanup); - subscriptions.set(handler, computed); + function get() { + return value; } - return subscriptions.get(handler); -} -var updateTimeout; -function delayedUpdate() { - clearTimeout(updateTimeout); - updateTimeout = setTimeout(import_valyrian.update); -} -function Signal(value) { - let subscriptions = /* @__PURE__ */ new Map(); - let getters = {}; - let forceUpdate = false; - let signal = new Proxy( - function(valOrPath, handler) { - if (typeof valOrPath === "undefined") { - return signal.value; - } - if (typeof valOrPath === "function") { - return createSubscription(signal, subscriptions, valOrPath); - } - if (typeof valOrPath === "string" && typeof handler !== "undefined") { - let parsed = valOrPath.split("."); - let result = signal.value; - let next; - while (parsed.length) { - next = parsed.shift(); - if (parsed.length > 0) { - if (typeof result[next] !== "object") { - result[next] = {}; - } - result = result[next]; - } else { - result[next] = typeof handler === "function" ? handler(result[next]) : handler; - } - } - forceUpdate = true; - signal.value = signal.value; - return signal.value; - } - signal.value = valOrPath; - return signal.value; - }, - { - set(state, prop, val) { - if (prop === "value" || prop === "unsubscribe" || prop === "cleanup") { - let old = state[prop]; - state[prop] = val; - if (prop === "value" && (forceUpdate || val !== old)) { - forceUpdate = false; - for (let [handler, computed] of subscriptions) { - computed.cleanup(); - let cleanup = handler(val); - makeUnsubscribe(subscriptions, computed, handler, cleanup); - } - delayedUpdate(); - } - return true; - } - return false; - }, - get(state, prop) { - if (prop === "value") { - return typeof state.value === "function" ? state.value() : state.value; - } - if (prop === "cleanup" || prop === "unsubscribe" || prop === "getter") { - return state[prop]; - } - if (prop in getters) { - return getters[prop](state.value); - } - } + get.value = value; + get.toJSON = get.valueOf = get; + get.toString = () => `${value}`; + const set = (newValue) => { + value = newValue; + get.value = value; + for (let i = 0, l = subscribers.length; i < l; i++) { + subscribers[i](value); } - ); - Object.defineProperties(signal, { - value: { value, writable: true, enumerable: true }, - cleanup: { - value() { - for (let [handler, computed] of subscriptions) { - computed.unsubscribe(); - } - }, - writable: true, - enumerable: true - }, - getter: { - value(name, handler) { - if (name in getters) { - throw new Error("Named computed already exists."); - } - getters[name] = handler; - }, - enumerable: true + if (context.vnode) { + let newVnode = (0, import_valyrian.v)(context.vnode.tag, context.vnode.props, ...context.vnode.initialChildren); + newVnode.dom = context.vnode.dom; + newVnode.isSVG = context.vnode.isSVG; + context.vnode.subscribers.forEach( + (subscribers2) => subscribers2.length = 0 + ); + context.vnode.subscribers = []; + return (0, import_valyrian.updateVnode)(newVnode, context.vnode); } - }); + return (0, import_valyrian.update)(); + }; + let signal = [get, set, subscribe]; + if (context.vnode) { + context.vnode.signals.push(signal); + context.vnode.subscribers.push(subscribers); + } return signal; } diff --git a/dist/signal/index.mjs b/dist/signal/index.mjs index bdc5c09..735ec20 100644 --- a/dist/signal/index.mjs +++ b/dist/signal/index.mjs @@ -1,114 +1,55 @@ // lib/signal/index.ts -import { update } from "valyrian.js"; -function makeUnsubscribe(subscriptions, computed, handler, cleanup) { - if (typeof cleanup === "function") { - computed.cleanup = cleanup; +import { current, update, updateVnode, v } from "valyrian.js"; +function Signal(initialValue) { + const context = { ...current }; + if (context.vnode) { + if (!context.vnode.signals) { + context.vnode.signals = context.oldVnode?.signals || []; + context.vnode.calls = -1; + context.vnode.subscribers = context.oldVnode?.subscribers || []; + context.vnode.initialChildren = [...context.vnode.children]; + } + let signal2 = context.vnode.signals[++context.vnode.calls]; + if (signal2) { + return signal2; + } } - computed.unsubscribe = () => { - subscriptions.delete(handler); - computed?.cleanup(); + let value = initialValue; + const subscribers = []; + const subscribe = (callback) => { + if (subscribers.indexOf(callback) === -1) { + subscribers.push(callback); + } }; -} -function createSubscription(signal, subscriptions, handler) { - if (subscriptions.has(handler) === false) { - let computed = Signal(() => handler(signal.value)); - let cleanup = computed(); - makeUnsubscribe(subscriptions, computed, handler, cleanup); - subscriptions.set(handler, computed); + function get() { + return value; } - return subscriptions.get(handler); -} -var updateTimeout; -function delayedUpdate() { - clearTimeout(updateTimeout); - updateTimeout = setTimeout(update); -} -function Signal(value) { - let subscriptions = /* @__PURE__ */ new Map(); - let getters = {}; - let forceUpdate = false; - let signal = new Proxy( - function(valOrPath, handler) { - if (typeof valOrPath === "undefined") { - return signal.value; - } - if (typeof valOrPath === "function") { - return createSubscription(signal, subscriptions, valOrPath); - } - if (typeof valOrPath === "string" && typeof handler !== "undefined") { - let parsed = valOrPath.split("."); - let result = signal.value; - let next; - while (parsed.length) { - next = parsed.shift(); - if (parsed.length > 0) { - if (typeof result[next] !== "object") { - result[next] = {}; - } - result = result[next]; - } else { - result[next] = typeof handler === "function" ? handler(result[next]) : handler; - } - } - forceUpdate = true; - signal.value = signal.value; - return signal.value; - } - signal.value = valOrPath; - return signal.value; - }, - { - set(state, prop, val) { - if (prop === "value" || prop === "unsubscribe" || prop === "cleanup") { - let old = state[prop]; - state[prop] = val; - if (prop === "value" && (forceUpdate || val !== old)) { - forceUpdate = false; - for (let [handler, computed] of subscriptions) { - computed.cleanup(); - let cleanup = handler(val); - makeUnsubscribe(subscriptions, computed, handler, cleanup); - } - delayedUpdate(); - } - return true; - } - return false; - }, - get(state, prop) { - if (prop === "value") { - return typeof state.value === "function" ? state.value() : state.value; - } - if (prop === "cleanup" || prop === "unsubscribe" || prop === "getter") { - return state[prop]; - } - if (prop in getters) { - return getters[prop](state.value); - } - } + get.value = value; + get.toJSON = get.valueOf = get; + get.toString = () => `${value}`; + const set = (newValue) => { + value = newValue; + get.value = value; + for (let i = 0, l = subscribers.length; i < l; i++) { + subscribers[i](value); } - ); - Object.defineProperties(signal, { - value: { value, writable: true, enumerable: true }, - cleanup: { - value() { - for (let [handler, computed] of subscriptions) { - computed.unsubscribe(); - } - }, - writable: true, - enumerable: true - }, - getter: { - value(name, handler) { - if (name in getters) { - throw new Error("Named computed already exists."); - } - getters[name] = handler; - }, - enumerable: true + if (context.vnode) { + let newVnode = v(context.vnode.tag, context.vnode.props, ...context.vnode.initialChildren); + newVnode.dom = context.vnode.dom; + newVnode.isSVG = context.vnode.isSVG; + context.vnode.subscribers.forEach( + (subscribers2) => subscribers2.length = 0 + ); + context.vnode.subscribers = []; + return updateVnode(newVnode, context.vnode); } - }); + return update(); + }; + let signal = [get, set, subscribe]; + if (context.vnode) { + context.vnode.signals.push(signal); + context.vnode.subscribers.push(subscribers); + } return signal; } export { diff --git a/lib/index.ts b/lib/index.ts index 6f25704..efc1de6 100644 --- a/lib/index.ts +++ b/lib/index.ts @@ -574,12 +574,15 @@ function sharedSetAttribute(name: string, value: any, newVnode: VnodeWithDom, ol // If the attribute value is a function, add an event listener for the attribute // name to the DOM element represented by mainVnode. if (typeof value === "function") { + // We change the name of the event to lowercase to avoid issues with case sensitivity. + // Ex "onClick" and "onclick" are the same event. + let lowercaseName = name.toLowerCase(); // Only add the event listener if it hasn't been added yet. - if (name in eventListenerNames === false) { - (mainVnode as VnodeWithDom).dom.addEventListener(name.slice(2), eventListener); - eventListenerNames[name] = true; + if (lowercaseName in eventListenerNames === false) { + (mainVnode as VnodeWithDom).dom.addEventListener(lowercaseName.slice(2), eventListener); + eventListenerNames[lowercaseName] = true; } - newVnode.dom[`v-${name}`] = value; + newVnode.dom[`v-${lowercaseName}`] = value; return; } @@ -742,69 +745,30 @@ export function patch(newVnode: VnodeWithDom, oldVnode?: VnodeWithDom): void { current.oldVnode = oldVnode; // Flatten the new tree + // Take into account that is necessary to flatten the tree before the patch process + // to let the hooks and signals work properly for (let i = 0; i < newTree.length; i++) { let newChild = newTree[i]; // If the new child is a Vnode and is not a text node if (newChild instanceof Vnode && newChild.tag !== textTag) { - // If the tag of the new child is not a string, it is a component - if (typeof newChild.tag !== "string") { - // Set the current component to the tag of the new child - current.component = newChild.tag; - // Replace the new child with the result of calling its view or bind method, passing in the props and children as arguments - newTree.splice( - i--, - 1, - ("view" in newChild.tag ? newChild.tag.view.bind(newChild.tag) : newChild.tag.bind(newChild.tag))( - newChild.props, - ...newChild.children - ) - ); + // If the new child tag is a string just continue the loop + if (typeof newChild.tag === "string") { continue; } - // Set the isSVG flag for the new child if it is an SVG element or if the parent is an SVG element - newChild.isSVG = newVnode.isSVG || newChild.tag === "svg"; - - // If there is an old child at the same index - if (i < oldTreeLength) { - let oldChild = oldTree[i]; - // If the tag of the new child is the same as the tag of the old child - if (newChild.tag === oldChild.tag) { - // Set the dom property of the new child to the dom property of the old child - newChild.dom = oldChild.dom; - // If the v-keep prop is the same for both the new and old child, set the children of the new child to the children of the old child - if ("v-keep" in newChild.props && newChild.props["v-keep"] === oldChild.props["v-keep"]) { - newChild.children = oldChild.children; - continue; - } - - // Update the attributes of the new child based on the old child - updateAttributes(newChild as VnodeWithDom, oldChild); - // Recursively patch the new and old children - patch(newChild as VnodeWithDom, oldChild); - continue; - } - - // Create a new dom element for the new child - newChild.dom = createDomElement(newChild.tag, newChild.isSVG); - // Update the attributes of the new child - updateAttributes(newChild as VnodeWithDom); - // Replace the old child in the dom with the new child - newVnode.dom.replaceChild(newChild.dom, oldChild.dom); - // Recursively patch the new child - patch(newChild as VnodeWithDom); - continue; - } - - // Create a new dom element for the new child - newChild.dom = createDomElement(newChild.tag, newChild.isSVG); - // Update the attributes of the new child - updateAttributes(newChild as VnodeWithDom); - // Append the new child to the dom - newVnode.dom.appendChild(newChild.dom); - // Recursively patch the new child - patch(newChild as VnodeWithDom); + // If the tag of the new child is not a string, it is a component + // Set the current component to the tag of the new child + current.component = newChild.tag; + // Replace the new child with the result of calling its view or bind method, passing in the props and children as arguments + newTree.splice( + i--, + 1, + ("view" in newChild.tag ? newChild.tag.view.bind(newChild.tag) : newChild.tag.bind(newChild.tag))( + newChild.props, + ...newChild.children + ) + ); continue; } @@ -820,42 +784,96 @@ export function patch(newVnode: VnodeWithDom, oldVnode?: VnodeWithDom): void { continue; } - // If the new child is not a Vnode, wrap it in a text Vnode - newTree[i] = new Vnode(textTag, {}, []); - // If the new child is a Vnode, set the dom property of the text Vnode to the dom property of the new child + // If the new child is a Vnode, set the text of the Vnode to the text content of its dom property if (newChild instanceof Vnode) { - newTree[i].dom = newChild.dom; // Set the new child to the text content of its dom property - newChild = (newChild as VnodeWithDom).dom.textContent; + newChild.children[0] = (newChild as VnodeWithDom).dom.textContent; + continue; } + // If the new child is not a Vnode, wrap it in a text Vnode + newChild = newTree[i] = new Vnode(textTag, {}, [newChild]); + } + + // Patch the the old tree + for (let i = 0; i < newTree.length; i++) { + let newChild = newTree[i]; + + // If the new child is a text vnode + if (newChild.tag === textTag) { + // If there is an old child at the same index + if (i < oldTreeLength) { + let oldChild = oldTree[i]; + + // If the old child is a text node + if (oldChild.tag === textTag) { + // Set the dom property of the text Vnode to the dom property of the old child + newChild.dom = oldChild.dom; + // If the text content of the old child is different from the new child, update the text content of the old child + // eslint-disable-next-line eqeqeq + if (newChild.children[0] != oldChild.dom.textContent) { + oldChild.dom.textContent = newChild.children[0]; + } + continue; + } + + // Create a new text node for the new child + newChild.dom = document.createTextNode(newChild.children[0]); + // Replace the old child in the dom with the new text node + newVnode.dom.replaceChild(newChild.dom, oldChild.dom); + continue; + } + + // Create a new text node for the new child + newChild.dom = document.createTextNode(newChild.children[0]); + // Append the new text node to the dom + newVnode.dom.appendChild(newChild.dom); + continue; + } + + // If the new child is not a text node + // Set the isSVG flag for the new child if it is an SVG element or if the parent is an SVG element + newChild.isSVG = newVnode.isSVG || newChild.tag === "svg"; + // If there is an old child at the same index if (i < oldTreeLength) { let oldChild = oldTree[i]; - - // If the old child is a text node - if (oldChild.tag === textTag) { - // Set the dom property of the text Vnode to the dom property of the old child - newTree[i].dom = oldChild.dom; - // If the text content of the old child is different from the new child, update the text content of the old child - // eslint-disable-next-line eqeqeq - if (newChild != oldChild.dom.textContent) { - oldChild.dom.textContent = newChild; + // If the tag of the new child is the same as the tag of the old child + if (newChild.tag === oldChild.tag) { + // Set the dom property of the new child to the dom property of the old child + newChild.dom = oldChild.dom; + // If the v-keep prop is the same for both the new and old child, set the children of the new child to the children of the old child + if ("v-keep" in newChild.props && newChild.props["v-keep"] === oldChild.props["v-keep"]) { + newChild.children = oldChild.children; + continue; } + + // Update the attributes of the new child based on the old child + updateAttributes(newChild as VnodeWithDom, oldChild); + // Recursively patch the new and old children + patch(newChild as VnodeWithDom, oldChild); continue; } - // Create a new text node for the new child - newTree[i].dom = document.createTextNode(newChild); - // Replace the old child in the dom with the new text node - newVnode.dom.replaceChild(newTree[i].dom, oldChild.dom); + // Create a new dom element for the new child + newChild.dom = createDomElement(newChild.tag as string, newChild.isSVG); + // Update the attributes of the new child + updateAttributes(newChild as VnodeWithDom); + // Replace the old child in the dom with the new child + newVnode.dom.replaceChild(newChild.dom, oldChild.dom); + // Recursively patch the new child + patch(newChild as VnodeWithDom); continue; } - // Create a new text node for the new child - newTree[i].dom = document.createTextNode(newChild); - // Append the new text node to the dom - newVnode.dom.appendChild(newTree[i].dom); + // Create a new dom element for the new child + newChild.dom = createDomElement(newChild.tag as string, newChild.isSVG); + // Update the attributes of the new child + updateAttributes(newChild as VnodeWithDom); + // Append the new child to the dom + newVnode.dom.appendChild(newChild.dom); + // Recursively patch the new child + patch(newChild as VnodeWithDom); } // Remove any old children that are no longer present in the new tree @@ -898,6 +916,40 @@ export function update(): void | string { } } +// Update custom Vnode +// It is assumed that a first mount has already occurred, so, +// the oldVnode is not null and the dom property of the oldVnode is not null +// You need to set the dom property of the newVnode to the dom property of the oldVnode +// The same with the isSVG property +// Prefer this function over patch to allow for cleanup, onUpdate and onMount sets to be called +export function updateVnode(vnode: VnodeWithDom, oldVnode: VnodeWithDom): string | void { + // Call any cleanup functions that are registered with the onCleanupSet set + callSet(onCleanupSet); + + // Recursively patch the new and old main Vnodes + patch(vnode, oldVnode); + + // Set the vnode properties to the old vnode + oldVnode.tag = vnode.tag; + oldVnode.props = { ...vnode.props }; + oldVnode.children = [...vnode.children]; + + // Call any update or mount functions that are registered with the onUpdateSet or onMountSet set + callSet(isMounted ? onUpdateSet : onMountSet); + + // Set the isMounted flag to true + isMounted = true; + + // Reset the current vnode, oldVnode, and component properties + current.vnode = null; + current.oldVnode = null; + current.component = null; + + if (isNodeJs) { + return vnode.dom.innerHTML; + } +} + // Unmount the main Vnode export function unmount() { // If the main Vnode exists diff --git a/lib/interfaces.ts b/lib/interfaces.ts index 79cd1c6..6a7cd41 100644 --- a/lib/interfaces.ts +++ b/lib/interfaces.ts @@ -89,6 +89,7 @@ declare module "valyrian.js" { export function updateAttributes(newVnode: VnodeWithDom, oldVnode?: VnodeWithDom): void; export function patch(newVnode: VnodeWithDom, oldVnode?: VnodeWithDom): void; export function update(): void | string; + export function updateVnode(vnode: VnodeWithDom, oldVnode: VnodeWithDom): string | void; export function unmount(): string | void; export function mount(dom: any, component: any): string | void; export const v: V; diff --git a/lib/proxy-signal/index.ts b/lib/proxy-signal/index.ts new file mode 100644 index 0000000..c0682ff --- /dev/null +++ b/lib/proxy-signal/index.ts @@ -0,0 +1,187 @@ +import { update } from "valyrian.js"; + +/* eslint-disable no-use-before-define */ +interface Cleanup { + (): void; +} + +interface Subscription { + // eslint-disable-next-line no-unused-vars + (value: ProxySignal["value"]): void | Cleanup; +} + +interface Subscriptions extends Map {} + +interface Getter { + // eslint-disable-next-line no-unused-vars + (value: ProxySignal["value"]): any; +} + +interface Getters { + [key: string | symbol]: Getter; +} + +interface ProxySignal { + // Works as a getter of the value + (): ProxySignal["value"]; + // Works as a subscription to the value + // eslint-disable-next-line no-unused-vars + (value: Subscription): ProxySignal; + // Works as a setter with a path and a handler + // eslint-disable-next-line no-unused-vars + (path: string, handler: (valueAtPathPosition: any) => any): ProxySignal["value"]; + // Works as a setter with a path and a value + // eslint-disable-next-line no-unused-vars + (path: string, value: any): ProxySignal["value"]; + // Works as a setter with a value + // eslint-disable-next-line no-unused-vars + (value: any): ProxySignal["value"]; + // Gets the current value of the signal. + value: any; + // Cleanup function to be called to remove all subscriptions. + cleanup: () => void; + // Creates a getter on the signal. + // eslint-disable-next-line no-unused-vars + getter: (name: string, handler: Getter) => any; + // To access the getters on the signal. + [key: string | number | symbol]: any; +} + +function makeUnsubscribe( + subscriptions: Subscriptions, + computed: ProxySignal, + handler: Subscription, + cleanup?: Cleanup +) { + if (typeof cleanup === "function") { + computed.cleanup = cleanup; + } + computed.unsubscribe = () => { + subscriptions.delete(handler); + computed?.cleanup(); + }; +} + +function createSubscription(signal: ProxySignal, subscriptions: Subscriptions, handler: Subscription) { + if (subscriptions.has(handler) === false) { + // eslint-disable-next-line no-use-before-define + let computed = ProxySignal(() => handler(signal.value)); + let cleanup = computed(); // Execute to register itself + makeUnsubscribe(subscriptions, computed, handler, cleanup); + subscriptions.set(handler, computed); + } + + return subscriptions.get(handler); +} + +let updateTimeout: any; +function delayedUpdate() { + clearTimeout(updateTimeout); + updateTimeout = setTimeout(update); +} + +// eslint-disable-next-line sonarjs/cognitive-complexity +export function ProxySignal(value: any): ProxySignal { + let subscriptions = new Map(); + let getters: Getters = {}; + + let forceUpdate = false; + + let signal: ProxySignal = new Proxy( + // eslint-disable-next-line no-unused-vars + function (valOrPath?: any | Subscription, handler?: (valueAtPathPosition: any) => any) { + // Works as a getter + if (typeof valOrPath === "undefined") { + return signal.value; + } + + // Works as a subscription + if (typeof valOrPath === "function") { + return createSubscription(signal, subscriptions, valOrPath); + } + + // Works as a setter with a path + if (typeof valOrPath === "string" && typeof handler !== "undefined") { + let parsed = valOrPath.split("."); + let result = signal.value; + let next; + while (parsed.length) { + next = parsed.shift() as string; + if (parsed.length > 0) { + if (typeof result[next] !== "object") { + result[next] = {}; + } + result = result[next]; + } else { + result[next] = typeof handler === "function" ? handler(result[next]) : handler; + } + } + forceUpdate = true; + signal.value = signal.value; + return signal.value; + } + + // Works as a setter with a value + signal.value = valOrPath; + return signal.value; + } as ProxySignal, + { + set(state, prop, val) { + if (prop === "value" || prop === "unsubscribe" || prop === "cleanup") { + let old = state[prop]; + state[prop] = val; + if (prop === "value" && (forceUpdate || val !== old)) { + forceUpdate = false; + for (let [handler, computed] of subscriptions) { + computed.cleanup(); + let cleanup = handler(val); + makeUnsubscribe(subscriptions, computed, handler, cleanup); + } + delayedUpdate(); + } + return true; + } + return false; + }, + get(state, prop) { + if (prop === "value") { + return typeof state.value === "function" ? state.value() : state.value; + } + + if (prop === "cleanup" || prop === "unsubscribe" || prop === "getter") { + return state[prop]; + } + + if (prop in getters) { + return getters[prop](state.value); + } + } + } + ); + + Object.defineProperties(signal, { + value: { value, writable: true, enumerable: true }, + cleanup: { + value() { + // eslint-disable-next-line no-unused-vars + for (let [handler, computed] of subscriptions) { + computed.unsubscribe(); + } + }, + writable: true, + enumerable: true + }, + getter: { + value(name: string, handler: Getter) { + if (name in getters) { + throw new Error("Named computed already exists."); + } + + getters[name] = handler; + }, + enumerable: true + } + }); + + return signal; +} diff --git a/lib/signal/index.ts b/lib/signal/index.ts index 732064e..66a0158 100644 --- a/lib/signal/index.ts +++ b/lib/signal/index.ts @@ -1,182 +1,102 @@ -import { update } from "valyrian.js"; - -/* eslint-disable no-use-before-define */ -interface Cleanup { - (): void; -} - -interface Subscription { - // eslint-disable-next-line no-unused-vars - (value: Signal["value"]): void | Cleanup; -} +import { VnodeWithDom, current, update, updateVnode, v } from "valyrian.js"; + +export function Signal(initialValue) { + // Create a copy of the current context object + const context = { ...current }; + + // Check if the context object has a vnode property + if (context.vnode) { + // Is first call + if (!context.vnode.signals) { + // Set the signals property to the signals property of the oldVnode object, or an empty array if that doesn't exist + context.vnode.signals = context.oldVnode?.signals || []; + // Set the calls property to -1 + context.vnode.calls = -1; + // Set the subscribers property to the subscribers property of the oldVnode object, or an empty array if that doesn't exist + context.vnode.subscribers = context.oldVnode?.subscribers || []; + + // Set the initialChildren property of the vnode object to a copy of the children array of the vnode object + context.vnode.initialChildren = [...context.vnode.children]; + } -interface Subscriptions extends Map {} + // Assign the signal variable to the signal stored at the index of the vnode object's calls property in the vnode's signals array + let signal = context.vnode.signals[++context.vnode.calls]; -interface Getter { - // eslint-disable-next-line no-unused-vars - (value: Signal["value"]): any; -} + // If a signal has already been assigned to the signal variable, return it + if (signal) { + return signal; + } + } -interface Getters { - [key: string | symbol]: Getter; -} + // Declare a variable to store the current value of the Signal + let value = initialValue; -interface Signal { - // Works as a getter of the value - (): Signal["value"]; - // Works as a subscription to the value - // eslint-disable-next-line no-unused-vars - (value: Subscription): Signal; - // Works as a setter with a path and a handler - // eslint-disable-next-line no-unused-vars - (path: string, handler: (valueAtPathPosition: any) => any): Signal["value"]; - // Works as a setter with a path and a value - // eslint-disable-next-line no-unused-vars - (path: string, value: any): Signal["value"]; - // Works as a setter with a value - // eslint-disable-next-line no-unused-vars - (value: any): Signal["value"]; - // Gets the current value of the signal. - value: any; - // Cleanup function to be called to remove all subscriptions. - cleanup: () => void; - // Creates a getter on the signal. - // eslint-disable-next-line no-unused-vars - getter: (name: string, handler: Getter) => any; - // To access the getters on the signal. - [key: string | number | symbol]: any; -} + // Create an array to store functions that have subscribed to changes to the Signal's value + const subscribers = []; -function makeUnsubscribe(subscriptions: Subscriptions, computed: Signal, handler: Subscription, cleanup?: Cleanup) { - if (typeof cleanup === "function") { - computed.cleanup = cleanup; - } - computed.unsubscribe = () => { - subscriptions.delete(handler); - computed?.cleanup(); + // Define a function that allows other parts of the code to subscribe to changes to the Signal's value + const subscribe = (callback) => { + // Add the callback function to the subscribers array + if (subscribers.indexOf(callback) === -1) { + subscribers.push(callback); + } }; -} -function createSubscription(signal: Signal, subscriptions: Subscriptions, handler: Subscription) { - if (subscriptions.has(handler) === false) { - // eslint-disable-next-line no-use-before-define - let computed = Signal(() => handler(signal.value)); - let cleanup = computed(); // Execute to register itself - makeUnsubscribe(subscriptions, computed, handler, cleanup); - subscriptions.set(handler, computed); + // Define a function that returns the current value of the Signal + function get() { + return value; } + // Add value, toJSON, valueOf, and toString properties to the get function + get.value = value; + get.toJSON = get.valueOf = get; + get.toString = () => `${value}`; + + // Define a function that allows the value of the Signal to be updated and notifies any subscribed functions of the change + const set = (newValue) => { + // Update the value of the Signal + value = newValue; + // Update the value property of the get function + get.value = value; + // Call each subscribed function with the new value of the Signal as an argument + for (let i = 0, l = subscribers.length; i < l; i++) { + subscribers[i](value); + } - return subscriptions.get(handler); -} + // Check if the context object has a vnode property + if (context.vnode) { + // If it does, create a new vnode object based on the original vnode, its children, and its DOM and SVG properties + let newVnode = v(context.vnode.tag, context.vnode.props, ...context.vnode.initialChildren) as VnodeWithDom; + newVnode.dom = context.vnode.dom; + newVnode.isSVG = context.vnode.isSVG; + + // Clear the subscribers array by setting the length property to 0 + context.vnode.subscribers.forEach( + (subscribers) => + // Setting the length property to 0 is faster than clearing the array with a loop + (subscribers.length = 0) + ); + + // Clear the subscribers array by setting it to an empty array + context.vnode.subscribers = []; + + // Return the result of updating the original vnode with the new vnode + return updateVnode(newVnode, context.vnode); + } -let updateTimeout: any; -function delayedUpdate() { - clearTimeout(updateTimeout); - updateTimeout = setTimeout(update); -} + // If the context object doesn't have a vnode property, return the result of calling the update function + return update(); + }; -// eslint-disable-next-line sonarjs/cognitive-complexity -export function Signal(value: any): Signal { - let subscriptions = new Map(); - let getters: Getters = {}; - - let forceUpdate = false; - - let signal: Signal = new Proxy( - // eslint-disable-next-line no-unused-vars - function (valOrPath?: any | Subscription, handler?: (valueAtPathPosition: any) => any) { - // Works as a getter - if (typeof valOrPath === "undefined") { - return signal.value; - } - - // Works as a subscription - if (typeof valOrPath === "function") { - return createSubscription(signal, subscriptions, valOrPath); - } - - // Works as a setter with a path - if (typeof valOrPath === "string" && typeof handler !== "undefined") { - let parsed = valOrPath.split("."); - let result = signal.value; - let next; - while (parsed.length) { - next = parsed.shift() as string; - if (parsed.length > 0) { - if (typeof result[next] !== "object") { - result[next] = {}; - } - result = result[next]; - } else { - result[next] = typeof handler === "function" ? handler(result[next]) : handler; - } - } - forceUpdate = true; - signal.value = signal.value; - return signal.value; - } - - // Works as a setter with a value - signal.value = valOrPath; - return signal.value; - } as Signal, - { - set(state, prop, val) { - if (prop === "value" || prop === "unsubscribe" || prop === "cleanup") { - let old = state[prop]; - state[prop] = val; - if (prop === "value" && (forceUpdate || val !== old)) { - forceUpdate = false; - for (let [handler, computed] of subscriptions) { - computed.cleanup(); - let cleanup = handler(val); - makeUnsubscribe(subscriptions, computed, handler, cleanup); - } - delayedUpdate(); - } - return true; - } - return false; - }, - get(state, prop) { - if (prop === "value") { - return typeof state.value === "function" ? state.value() : state.value; - } - - if (prop === "cleanup" || prop === "unsubscribe" || prop === "getter") { - return state[prop]; - } - - if (prop in getters) { - return getters[prop](state.value); - } - } - } - ); - - Object.defineProperties(signal, { - value: { value, writable: true, enumerable: true }, - cleanup: { - value() { - // eslint-disable-next-line no-unused-vars - for (let [handler, computed] of subscriptions) { - computed.unsubscribe(); - } - }, - writable: true, - enumerable: true - }, - getter: { - value(name: string, handler: Getter) { - if (name in getters) { - throw new Error("Named computed already exists."); - } - - getters[name] = handler; - }, - enumerable: true - } - }); + // Assign the signal variable an array containing the get, set, and subscribe functions + let signal = [get, set, subscribe]; + + // If the context object has a vnode property, add the signal to the vnode's signals array + // and add the subscribers array to the vnode's subscribers array + if (context.vnode) { + context.vnode.signals.push(signal); + context.vnode.subscribers.push(subscribers); + } + // Return the signal return signal; } diff --git a/source.js b/source.js index 25fc95c..1ffc3c6 100644 --- a/source.js +++ b/source.js @@ -287,6 +287,16 @@ async function copy({ entryPoint, outfileName }) { ] }); + await build({ + globalName: "ValyrianProxySignal", + entryPoint: "./lib/proxy-signal/index.ts", + outfileName: "./dist/proxy-signal/index", + clean: false, + minify: false, + libCheck, + external: ["valyrian.js"] + }); + await build({ globalName: "ValyrianSignal", entryPoint: "./lib/signal/index.ts", diff --git a/test/hooks_test.js b/test/hooks_test.js index f277df9..891dc14 100644 --- a/test/hooks_test.js +++ b/test/hooks_test.js @@ -388,15 +388,12 @@ describe("Hooks", () => { expect(computedTimes).toEqual(2); }); - it("Update class with hooks vs shouldupdate property", () => { + it("Update class with hooks vs v-keep", () => { let updateClass = ""; let Component = () => (
{ -
vnode.props.class !== oldVnode.props.class} - > +
test
} diff --git a/test/proxy-signals_test.js b/test/proxy-signals_test.js new file mode 100644 index 0000000..605e2e8 --- /dev/null +++ b/test/proxy-signals_test.js @@ -0,0 +1,183 @@ +import "valyrian.js/node"; + +import { mount, onUnmount, unmount, update, v } from "valyrian.js"; + +import { ProxySignal } from "../lib/proxy-signal"; +import expect from "expect"; + +describe("ProxySignals", () => { + it("should create a signal", async () => { + // Create signal + let counter = ProxySignal(0); + + // Read value + counter(); + counter.value; + + // Set value + counter(0); + counter.value = 0; + // Set deeply value with function + // counter('path', (current) => current); + // Set deeply value + // counter('path', 0) + + // Effectful computed / Subscription + let effect = counter((val) => expect(val).toBeGreaterThanOrEqual(0)); + + // Named computed / Getter + counter.getter("hello", (val) => "hello " + val); + + // Pure computed + let computed = counter((val) => "hello " + val); + + // Unlinked Pure computed + let unlinked = ProxySignal(() => "hello " + counter.value); + + let interval = setInterval(() => (counter.value += 1), 10); + expect(counter.hello).toEqual("hello 0"); + expect(computed()).toEqual("hello 0"); + expect(computed.value).toEqual("hello 0"); + expect(unlinked()).toEqual("hello 0"); + expect(unlinked.value).toEqual("hello 0"); + + await new Promise((resolve) => setTimeout(() => resolve(), 22)); + // effect.unsubscribe(); + counter.cleanup(); + expect(counter.hello).toEqual("hello 2"); + expect(computed()).toEqual("hello 2"); + expect(computed.value).toEqual("hello 2"); + expect(unlinked()).toEqual("hello 2"); + expect(unlinked.value).toEqual("hello 2"); + + await new Promise((resolve) => setTimeout(() => resolve(), 22)); + clearInterval(interval); + expect(counter.hello).toEqual("hello 4"); + expect(computed()).toEqual("hello 4"); + expect(computed.value).toEqual("hello 4"); + expect(unlinked()).toEqual("hello 4"); + expect(unlinked.value).toEqual("hello 4"); + }); + + it("should test effect cleanup", async () => { + let delay = ProxySignal(10); + let count = ProxySignal(0); + let effectInterval = delay((delay) => { + let interval = setInterval(() => { + count.value = count.value + 1; + }, delay); + return () => clearInterval(interval); + }); + + await new Promise((resolve) => setTimeout(() => resolve(), 10)); + expect(count()).toEqual(1); + expect(count.value).toEqual(1); + delay(5); + await new Promise((resolve) => setTimeout(() => resolve(), 20)); + expect(count()).toEqual(4); + expect(count.value).toEqual(4); + effectInterval.cleanup(); + await new Promise((resolve) => setTimeout(() => resolve(), 20)); + expect(count()).toEqual(4); + expect(count.value).toEqual(4); + delay.cleanup(); + count.cleanup(); + }); + + it("should test deep state effect cleanup", async () => { + let state = ProxySignal({ + count: 0, + delay: 10 + }); + let effectInterval2 = state(() => { + let interval = setInterval(() => { + state("count", (current) => current + 1); + }, state.value.delay); + return () => clearInterval(interval); + }); + + await new Promise((resolve) => setTimeout(() => resolve(), 10)); + expect(state()).toEqual({ count: 1, delay: 10 }); + expect(state.value).toEqual({ count: 1, delay: 10 }); + state("delay", 5); + await new Promise((resolve) => setTimeout(() => resolve(), 20)); + expect(state()).toEqual({ count: 4, delay: 5 }); + expect(state.value).toEqual({ count: 4, delay: 5 }); + effectInterval2.cleanup(); + await new Promise((resolve) => setTimeout(() => resolve(), 20)); + expect(state()).toEqual({ count: 4, delay: 5 }); + expect(state.value).toEqual({ count: 4, delay: 5 }); + }); +}); + +describe("Hooks like pattern", () => { + it("should create a simple counter", async () => { + let Counter = (ms) => { + let count = ProxySignal(0); + let interval = setInterval(() => { + count.value = count.value + 1; + }, ms); + onUnmount(() => clearInterval(interval)); + return () =>
{count.value}
; + }; + + let Component = Counter(10); + + let result = mount("div", Component); + expect(result).toEqual("
0
"); + await new Promise((resolve) => setTimeout(() => resolve(), 25)); + result = update(); + expect(result).toEqual("
2
"); + unmount(); + }); + + it("should create a counter with delay change", async () => { + let Counter = (ms) => { + let delay = ProxySignal(ms); + let count = ProxySignal(0); + let interval = delay((delay) => { + let interval = setInterval(() => { + count.value = count.value + 1; + }, delay); + return () => clearInterval(interval); + }); + onUnmount(() => interval.cleanup()); + return () =>
{count.value}
; + }; + + let Component = Counter(10); + + let result = mount("div", Component); + expect(result).toEqual("
0
"); + await new Promise((resolve) => setTimeout(() => resolve(), 22)); + result = update(); + expect(result).toEqual("
2
"); + unmount(); + }); + + it("should create a counter with deep state", async () => { + let Counter = (ms) => { + let state = ProxySignal({ + count: 0, + delay: ms + }); + let interval = state(() => { + let interval = setInterval(() => { + state("count", (current) => current + 1); + }, state.value.delay); + return () => clearInterval(interval); + }); + onUnmount(() => interval.cleanup()); + return () =>
{state.value.count}
; + }; + + let Component = Counter(10); + + let result = mount("div", Component); + expect(result).toEqual("
0
"); + await new Promise((resolve) => setTimeout(() => resolve(), 25)); + result = update(); + expect(result).toEqual("
2
"); + unmount(); + }); +}); diff --git a/test/signals_test.js b/test/signals_test.js index f95732b..01bc96e 100644 --- a/test/signals_test.js +++ b/test/signals_test.js @@ -1,182 +1,197 @@ +/* eslint-disable no-console */ import "valyrian.js/node"; -import { mount, unmount, update, v } from "valyrian.js"; +import { describe, it } from "mocha"; +// eslint-disable-next-line no-unused-vars +import { mount, update, v } from "valyrian.js"; -import { Signal } from "../lib/signal"; +import { Signal } from "valyrian.js/signal"; import expect from "expect"; -// TODO: onremove lifecycle has been removed, so this test is broken because does not clear timeouts and intervals - -describe("Signals", () => { - it("should create a signal", async () => { - // Create signal - let counter = Signal(0); - - // Read value - counter(); - counter.value; - - // Set value - counter(0); - counter.value = 0; - // Set deeply value with function - // counter('path', (current) => current); - // Set deeply value - // counter('path', 0) - - // Effectful computed / Subscription - let effect = counter((val) => expect(val).toBeGreaterThanOrEqual(0)); - - // Named computed / Getter - counter.getter("hello", (val) => "hello " + val); - - // Pure computed - let computed = counter((val) => "hello " + val); - - // Unlinked Pure computed - let unlinked = Signal(() => "hello " + counter.value); - - let interval = setInterval(() => (counter.value += 1), 10); - expect(counter.hello).toEqual("hello 0"); - expect(computed()).toEqual("hello 0"); - expect(computed.value).toEqual("hello 0"); - expect(unlinked()).toEqual("hello 0"); - expect(unlinked.value).toEqual("hello 0"); - - await new Promise((resolve) => setTimeout(() => resolve(), 22)); - // effect.unsubscribe(); - counter.cleanup(); - expect(counter.hello).toEqual("hello 2"); - expect(computed()).toEqual("hello 2"); - expect(computed.value).toEqual("hello 2"); - expect(unlinked()).toEqual("hello 2"); - expect(unlinked.value).toEqual("hello 2"); - - await new Promise((resolve) => setTimeout(() => resolve(), 22)); - clearInterval(interval); - expect(counter.hello).toEqual("hello 4"); - expect(computed()).toEqual("hello 4"); - expect(computed.value).toEqual("hello 4"); - expect(unlinked()).toEqual("hello 4"); - expect(unlinked.value).toEqual("hello 4"); - }); - - it("should test effect cleanup", async () => { - let delay = Signal(10); - let count = Signal(0); - let effectInterval = delay((delay) => { - let interval = setInterval(() => { - count.value = count.value + 1; - }, delay); - return () => clearInterval(interval); - }); - - await new Promise((resolve) => setTimeout(() => resolve(), 10)); - expect(count()).toEqual(1); +describe("Signal", () => { + it("should test basic signal", () => { + let [count, setCount, subscribe] = Signal(0); + subscribe(() => console.log(`The count is now ${count}`)); + expect(count()).toEqual(0); + setCount(1); expect(count.value).toEqual(1); - delay(5); - await new Promise((resolve) => setTimeout(() => resolve(), 20)); - expect(count()).toEqual(4); - expect(count.value).toEqual(4); - effectInterval.cleanup(); - await new Promise((resolve) => setTimeout(() => resolve(), 20)); - expect(count()).toEqual(4); - expect(count.value).toEqual(4); - delay.cleanup(); - count.cleanup(); + expect(`${count}`).toEqual("1"); }); - it("should test deep state effect cleanup", async () => { - let state = Signal({ - count: 0, - delay: 10 - }); - let effectInterval2 = state(() => { - let interval = setInterval(() => { - state("count", (current) => current + 1); - }, state.value.delay); - return () => clearInterval(interval); - }); - - await new Promise((resolve) => setTimeout(() => resolve(), 10)); - expect(state()).toEqual({ count: 1, delay: 10 }); - expect(state.value).toEqual({ count: 1, delay: 10 }); - state("delay", 5); - await new Promise((resolve) => setTimeout(() => resolve(), 20)); - expect(state()).toEqual({ count: 4, delay: 5 }); - expect(state.value).toEqual({ count: 4, delay: 5 }); - effectInterval2.cleanup(); - await new Promise((resolve) => setTimeout(() => resolve(), 20)); - expect(state()).toEqual({ count: 4, delay: 5 }); - expect(state.value).toEqual({ count: 4, delay: 5 }); + it("should test basic signal inside a component", async () => { + let setCountOut = null; + let countOut = null; + + function increment() { + setCountOut(countOut + 1); + } + + function decrement() { + setCountOut(countOut - 1); + } + + function Counter() { + const [count, setCount, subscribe] = Signal(0); + setCountOut = setCount; + countOut = count; + subscribe(() => console.log(`The count is now ${count}`)); + return ( +
+ + {count} + +
+ ); + } + + let div = document.createElement("div"); + + let res = mount(div, ); + expect(res).toEqual("
0
"); + increment(); + expect(div.innerHTML).toEqual("
1
"); + increment(); + expect(div.innerHTML).toEqual("
2
"); + res = update(); + expect(res).toEqual("
2
"); }); -}); -describe("Hooks like pattern", () => { - it("should create a simple counter", async () => { - let Counter = (ms) => { - let count = Signal(0); - let interval = setInterval(() => { - count.value = count.value + 1; - }, ms); - return () =>
clearInterval(interval)}>{count.value}
; - }; - - let Component = Counter(10); - - let result = mount("div", Component); - expect(result).toEqual("
0
"); - await new Promise((resolve) => setTimeout(() => resolve(), 25)); - result = update(); - expect(result).toEqual("
2
"); - unmount(); + it("should test basic signal outside a component", async () => { + let [count, setCount, subscribe] = Signal(0); + subscribe(() => console.log(`The count is now ${count}`)); + + function Counter() { + return ( +
+ + +
+ ); + } + + function Display() { + return ( +
+ {count} +
+ ); + } + + function App() { + return ( +
+ + +
+ ); + } + + let div = document.createElement("div"); + + let res = mount(div, ); + expect(res).toEqual("
0
"); + setCount(1); + expect(div.innerHTML).toEqual( + "
1
" + ); + setCount(2); + expect(div.innerHTML).toEqual( + "
2
" + ); + res = update(); + expect(res).toEqual("
2
"); }); - it("should create a counter with delay change", async () => { - let Counter = (ms) => { - let delay = Signal(ms); - let count = Signal(0); - let interval = delay((delay) => { - let interval = setInterval(() => { - count.value = count.value + 1; - }, delay); - return () => clearInterval(interval); - }); - return () =>
{count.value}
; - }; - - let Component = Counter(10); - - let result = mount("div", Component); - expect(result).toEqual("
0
"); - await new Promise((resolve) => setTimeout(() => resolve(), 22)); - result = update(); - expect(result).toEqual("
2
"); - unmount(); + it("should test multiple signals", () => { + let setCountOut = null; + let setCount2Out = null; + function Counter() { + const [count, setCount, subscribe] = Signal(0); + const [count2, setCount2, subscribe2] = Signal(0); + setCountOut = setCount; + setCount2Out = setCount2; + subscribe(() => console.log(`The count is now ${count}`)); + subscribe2(() => console.log(`The count2 is now ${count2}`)); + return ( +
+ + {count} + + + {count2} + +
+ ); + } + + let div = document.createElement("div"); + + let res = mount(div, ); + expect(res).toEqual( + "
00
" + ); + setCountOut(1); + expect(div.innerHTML).toEqual( + "
10
" + ); + setCount2Out(1); + expect(div.innerHTML).toEqual( + "
11
" + ); }); - it("should create a counter with deep state", async () => { - let Counter = (ms) => { - let state = Signal({ - count: 0, - delay: ms - }); - let interval = state(() => { - let interval = setInterval(() => { - state("count", (current) => current + 1); - }, state.value.delay); - return () => clearInterval(interval); - }); - return () =>
{state.value.count}
; - }; - - let Component = Counter(10); - - let result = mount("div", Component); - expect(result).toEqual("
0
"); - await new Promise((resolve) => setTimeout(() => resolve(), 25)); - result = update(); - expect(result).toEqual("
2
"); - unmount(); + it("should test signal inside a component with a signal outside the component", () => { + let setCount1Out = null; + let setCount2Out = null; + let setCount3Out = null; + + function Counter() { + let [count, setCount, subscribe] = Signal(0); + if (!setCount1Out) { + setCount1Out = setCount; + } else if (!setCount2Out) { + setCount2Out = setCount; + } + subscribe(() => console.log(`The count is now ${count}`)); + return ( +
+ + {count} + +
+ ); + } + + function App() { + let [count, setCount, subscribe] = Signal(0); + setCount3Out = setCount; + subscribe(() => console.log(`The app count is now ${count}`)); + return ( +
+ + +
{count}
+
+ ); + } + + let div = document.createElement("div"); + + let res = mount(div, ); + expect(res).toEqual( + '
0
0
0
' + ); + setCount1Out(1); + expect(div.innerHTML).toEqual( + '
1
0
0
' + ); + setCount2Out(1); + expect(div.innerHTML).toEqual( + '
1
1
0
' + ); + setCount3Out(1); + expect(div.innerHTML).toEqual( + '
1
1
1
' + ); }); });