bytefun-ai-mcp
Version:
ByteFun AI MCP服务 - 打通产品设计、UI设计、代码开发的服务平台,支持设计稿转代码和跨平台原生代码开发
286 lines (269 loc) • 52.9 kB
JavaScript
export const understandingCodeFrameworkTemplate = `
# **必须先理解项目代码框架10个知识点**
## 1. **理解View组件的声明和使用规则**
- 这不是html、react等开发,这是一个新的开发框架,是基于src/lib/uilib文件夹里面的UI组件库来开发的
- View组件的基类是BaseView,容器基类是BaseContainerView,所有View组件都继承自BaseView
- 查看'src/lib/uilib/'文件夹下面的文件名字,理解总共有多少种UI组件
- 所有view组件的声明,必须使用\`findViewById(id: string)\`方法赋值,其中里面的参数\`id\`必须从\`src/{moduleNameEN}/{pageNameEN}/{pageNameEN}.md\`查找获取到正确的id的值
- 禁止修改\`src/{moduleNameEN}/{pageNameEN}/{pageNameEN}.md\`文件里面的代码,此文件只读
- View组件的声明必须在类文件开始时先声明,并且必须要声明具体的View组件类型,不得声明为BaseView,不得在任何函数体里面通过findViewById来声明View组件,比如:
- ✅ 正确的写法:
\`\`\`typescript
public class HomePage extends Page {
private nameTextView: TextView = this.findViewById('nameTextView') as TextView
....
}
\`\`\`
- ❌ 错误的写法(没有声具体的View组件类型):
\`\`\`typescript
private nameTextView = this.findViewById('nameTextView')
\`\`\`
- ❌ 错误的写法(在函数体里面通过findViewById来声明View组件):
\`\`\`typescript
private oneFunction() {
const nameTextView: TextView = this.findViewById('nameTextView') as TextView
....
}
\`\`\`
- 所有View组件的显示、隐藏占位、隐藏不占位的设置都必须使用View的\`visibility\`属性进行设置,不得使用\`station\`属性。
- 所有View组件的normal、readOnly、focus、selected、disabled状态的变化都必须使用view的属性station来直接设置,比如:\`this.nameInputView.station = ViewStation.Focus\`,可选值有:ViewStation.NORMAL、ViewStation.READONLY、ViewStation.FOCUSED、ViewStation.SELECTED、ViewStation.DISABLED。
- 所有View组件(包括BaseView、BaseContainer以及它们的子类)的点击事件,都可以直接在view组件的声明后面添加\`setOnClickListener\`方法来监听点击事件,比如:\`this.xxxContainer.setOnClickListener(() => { this.nameTextView.text = '点击了' + this.nameTextView.text })\`
- 所有的dialog对话框,必须调用Dialog组件的show方法来显示,关闭时必须调用Dialog组件的hide方法来关闭,比如:this.xxxDialog.show()、this.xxxDialog.hide(),禁止使用view的visibility属性来显示隐藏对话框。
- 所有的DropDownMenu下拉菜单,必须调用DropDownMenu组件的show方法来显示,关闭时必须调用DropDownMenu组件的hide方法来关闭,比如:this.xxxDropDownMenu.show()、this.xxxDropDownMenu.hide(),禁止使用view的visibility属性来显示隐藏下拉菜单。
- 所有的SideSlideContainer侧滑容器,必须调用SideSlideContainer组件的show方法来显示,关闭时必须调用SideSlideContainer组件的hide方法来关闭,比如:this.xxxSideSlideContainer.show()、this.xxxSideSlideContainer.hide(),禁止使用view的visibility属性来显示隐藏侧滑容器。
## 2. **理解唯一的能力方法AllFunction.ts以及json相关代码**
- 使用'read_file'工具阅读并理解'src/lib/AllFunction.ts'文件,理解该工程能使用的唯一的能力方法
- 使用'read_file'工具阅读并理解'src/lib/JsonObject.ts'和'src/lib/JsonArray.ts'文件,理解这两个文件定义的json相关的方法
- 请严格按照唯一的能力方法AllFunction.ts,不要新增、删除或修改任何AllFunction接口声明;也不要调用其他未在AllFunction中出现的函数或模块
- 所有的toast提示,必须调用AllFunction.showToast方法来显示,并且如用户描述没有明确说明需要toast提示,你不得擅自进行Toast提示。
- 所有的dialog对话框,必须调用Dialog组件的show方法来显示,关闭时必须调用Dialog组件的hide方法来关闭,比如:this.xxxDialog.show()、this.xxxDialog.hide(),禁止使用view的visibility属性来显示隐藏对话框。
- 所有的DropDownMenu下拉菜单,必须调用DropDownMenu组件的show方法来显示,关闭时必须调用DropDownMenu组件的hide方法来关闭,比如:this.xxxDropDownMenu.show()、this.xxxDropDownMenu.hide(),禁止使用view的visibility属性来显示隐藏下拉菜单。
- 所有的SideSlideContainer侧滑容器,必须调用SideSlideContainer组件的show方法来显示,关闭时必须调用SideSlideContainer组件的hide方法来关闭,比如:this.xxxSideSlideContainer.show()、this.xxxSideSlideContainer.hide(),禁止使用view的visibility属性来显示隐藏侧滑容器。
- 延时执行AllFunction.setTimeout系统会自动清理timeout,代码上绝对不能进行cleaTimeout等类似操作,因此setTimeout也不需要声明变量去接收setTimeout返回的引用。
- 所有localStorage本地数据存储都必须使用AllFunction的saveDatabaseBoolean、saveDatabaseNumber、saveDatabaseString、saveDatabaseArray、saveDatabaseObject方法来存储数据,使用getDatabaseBoolean、getDatabaseNumber、getDatabaseString、getDatabaseArray、getDatabaseObject方法来获取数据。
## 3. **理解Ref、watch**
- Ref的数据类型只能是以下5中类型:数字使用Ref<number>,字符串使用Ref<string>,布尔量使用Ref<boolean>,对象使用Ref<any>,数组使用Ref<Array<any>>或Ref<Array<any> | null>或Ref<any[]>或Ref<any[] | null>。
- 使用'read_file'工具阅读并理解'src/lib/Ref.ts'文件,理解Ref和watch的定义用法
- watch是监听Ref的变化,然后做相关UI更新或其他业务逻辑,第一个参数是监听的Ref数组,第二个参数是监听的回调函数,回调函数中可以做相关UI更新或其他业务逻辑。
- Ref是响应式数据源,watch是监听Ref的变化,然后做相关UI更新或其他业务逻辑,比如:
\`\`\`typescript
private nameTextRef: Ref<string> = ref('')
private otherCountRef: Ref<number> = ref(0)
watch([this.nameTextRef, this.otherCountRef], () => {
// 可以先相关的UI更新或业务逻辑
if (this.otherCountRef.value > 5) {
this.nameTextView.text = this.nameTextRef.value + '5'
} else {
this.nameTextView.text = this.nameTextRef.value + '10'
}
})
\`\`\`
- \`src/lib/uilib/Ref.ts\`里面的\`watch\`函数,第一个入参\`refs: Ref<T>[]\`绝对禁止先声明局ref数组部变量再传入,正确的写法应该是直接写数组:\`[ref1, ref2]\`就行
- ❌ 错误的写法:
\`\`\` typescript
const refArray = [ref1, ref2]
watch(refArray, (newValues: T[]) => {
// 监听ref1和ref2的变化
})
\`\`\`
- ✅ 正确的写法:
\`\`\` typescript
watch([ref1, ref2], (newValues: T[]) => {
// 监听ref1和ref2的变化
})
\`\`\`
## 4. **理解setDataList方法的静态数据与动态数据场景**
- 对于动态数据容器ViewPager、ListView的setDataList方法,如果数据是动态的,比如:从后端接口获取的,才能使用setDataList方法来设置数据。静态数据已经在UI页面上写好了,你不得使用setDataList方法来设置数据。
- 判断标准:如果UI页面中已经有完整的轮播项目/列表项目,就是静态数据,如果是网络API返回的数据列表就是动态数据。
- ✅ 静态数据容器(UI页面中已写好内容):不使用setDataList()
- ✅ 动态数据容器(需要从API获取):才使用setDataList()
## 5. **View组件,理解每个组件可能里面已经有了响应式数据源,如果组件里面已经有了响应式数据源,那么你不得再自己创建ref变量或者使用onSelectChange等回调触发UI更新**
- 例子:ViewPager的currentSelectIndex和currentSelectIndexRef属性已经是响应式数据源,可以直接使用currentSelectIndexRef来绑定自动更新UI。
public initView() {
this.showIndexTextView.text = this.viewPager.currentSelectIndexRef.value + 1 + '/' + this.viewPager.dataList.length
}
- 例子:ViewPager的currentSelectIndex和currentSelectIndexRef属性已经是响应式数据源,可以直接使用currentSelectIndexRef来绑定自动更新UI,比如:
\`\`\`typescript
public initView() {
watch([this.viewPager.currentSelectIndexRef], () => {
this.showIndexTextView.text = this.viewPager.currentSelectIndexRef.value + 1 + '/' + this.viewPager.dataList.length
})
\`\`\`
- tab组件和轮播组件的选择变化监听,必须使用\`ViewPager\`或\`TabContainer\`的\`onSelectChange(func: (item: any, position: number) => void)\`函数来监听选择变化
## 6. **理解页面跳转**
- 编写跳转页面的代码时,必须要先查看目标页面的ts文件,确认目标页面是否存在,如果存在那么必须要使用\`AllFunction.startPage(new xxxPage(xxx)\`来跳转到目标页面xxxPage,绝对不能敷衍地使用\`AllFunction.showToast\`来模拟提示跳转页面,除非目标页面不存在。
- 页面跳转必须使用AllFunction.startPage实现,startPage传入Page的子类对象,如:AllFunction.startPage(new ProductPage(this.productID))。
- ❌ 页面跳转不需考虑模块化、循环依赖、渐进式开发,绝对禁止屏蔽页面跳转的代码,所有页面都已经存在,不会出现编译出错的,必须明确写上页面跳转的代码和目标页面所需的跳转参数。
- 在Application.ts设置跳转启动页,比如:
// 应用网站启动时回调的第一个函数,用于初始化一些全局的东西。
protected onApplicationCreate(): void {
AllFunction.startPage(new SplashPage())
}
- ✅ 引入其他类只允许一种写法:在类文件顶部进行import:import GuidePage from '../guidePage/guidePage'。
- ❌ 绝对禁止使用动态import的写法,程序底层已经处理好循环依赖问题了,你不需要考虑,比如绝对禁止以下写法:
// 使用动态导入避免循环依赖
import('../guidePage/guidePage').then(({ default: GuidePage }) => {
AllFunction.startPage(new GuidePage())
})
- ❌ 绝对禁止使用require的写法,程序底层已经处理好循环依赖问题了,你不需要考虑,比如绝对禁止以下写法:
require('../guidePage/guidePage').default
- ✅ 正确的写法是:AllFunction.startPage(new GuidePage())。然后在类文件顶部进行import:import GuidePage from '../guidePage/guidePage'。
- 如果页面跳转需要传递参数,那么需要使用Page的子类构造函数来传递参数,比如:AllFunction.startPage(new GuidePage(this.productID))。
- 在实现页面跳转代码的时候,必须严格按照以下三步来实现:
- **第一步:先分析跳转的目标页面需要传递哪些参数**
- **第二步:修改目标页面构造函数**:
\`\`\`typescript
export default class ReadingPage extends Page {
private bookId: number
private chapterTitle: string
constructor(bookId: number, chapterTitle: string) {
super()
this.bookId = bookId
this.chapterTitle = chapterTitle
}
}
\`\`\`
- **第三步:修改跳转代码**
\`\`\`typescript
const bookData = this.readingHistoryDataRef.value[0]
AllFunction.startPage(new ReadingPage(bookData.id, bookData.chapterTitle))
\`\`\`
## 7. **理解基础开发规则**
- lib文件夹里面的代码文件都是内置框架代码,绝对禁止对lib文件夹的代码进行增删改操作
- 任何页面(特别是闪屏页)的业务逻辑绝对不能放到Application.ts进行处理,Application.ts只进行全局网络拦截处理、全局静态变量、全局静态方法的定义和实现
- 如果是多个页面共用的数据变量,可以考虑声明为全局静态变量,并在Application.ts中进行声明。
- 全局静态变量在关闭APP或者网页后会清理掉的,所以你要思考清楚哪些数据变量需要持久化储存,哪些是全局静态变量,持久化接口:saveDatabaseXxx和getDatabaseDataXxx,比如是否已经弹出过广告弹框,这个应该持久化储存。
- 声明全局变量、局部变量必须要声明数据类型,并且绝对禁止使用any,请使用具体类型来声明
- 轮播ViewPager对应的索引指示器容器,这个索引指示器不需要额外代码实现指示器点的切换UI更新逻辑,本身ViewPager与IndicatorContainer底层已经实现了切换的UI更新逻辑的了,只需要viewPager.bindIndicator(indicatorContainer)即可。
- Tab选项卡\`TabNavigateBar\`和\`TabNavigateBarItem\`的点击切换UI更新逻辑,不得在ts代码里面进行,因为Tab选项卡内部已经封装好了滑动和点击切换UI更新逻辑,你不需要再手动实现。
- 底部导航栏\`bottomTabNavigateBar\`和\`bottomTabNavigateBarItem\`的滑动和点击切换UI更新逻辑,不得在ts代码里面进行,因为底部导航栏内部已经封装好了点击切换UI更新逻辑,你不需要再手动实现,比如首页主体页的底部导航栏,不需要实现点击切换UI更新逻辑,因为底部导航栏的点击切换UI更新逻辑已经自动实现了。
- 每一个页面(如loginPage)必须在page文件夹下面创建一个页面文件夹(如login),然后页面的ts文件(如loginPage.ts)创建在页面文件夹下面。
## 8. **理解后端API接口的定义与使用**
- 使用'read_file'工具读取'src/backendApi/backendApiInfo.md'后端API使用说明文件,必须缓存记住所有可用的后端API接口列表。
- 后端API接口的定义与使用,必须使用src/backendApi文件夹里面的各种API类中定义的方法,不允许使用未在backendApi文件夹中定义的后端API,比如:backendApi/account/getUserInfo.ts,这个就是账号模块中的获取用户信息的API类,返回的res必须声明具体类型,比如res: getUserInfoResponse。
- request接口只需定义即可,不需要实现,底层已经实现。
- API类代码示例:
\`\`\`typescript
import { NetFailData } from '../../lib/NetFailData'
import BaseApi from '../BaseApi'
// 账号系统-账号密码登录接口请求数据
export interface postClientBizAuthLoginRequest {
username?: string
password?: string
key?: string
captcha?: string
}
// 账号系统-账号密码登录接口返回数据
export interface postClientBizAuthLoginResponse {
code: number
msg: string
data: {
access_token: string
}
}
// 账号系统-账号密码登录接口
export default class postClientBizAuthLogin extends BaseApi {
public static request_ArtuX1d8a6A(
request: postClientBizAuthLoginRequest,
success: (res: postClientBizAuthLoginResponse) => void,
fail: (error: NetFailData) => void
): void { }
}
\`\`\`
- API类的使用方式:先import API类,然后调用API类的request方法,比如:
import { NetFailData } from "../../lib/NetFailData"
import { postClientBizAuthLogin } from '../../backendApi/account/postClientBizAuthLogin'
...
const request: postClientBizAuthLoginRequest = {
username: 'admin',
password: '123456',
key: '123456',
captcha: '123456'
}
postClientBizAuthLogin.request_ArtuX1d8a6A(request, (response: postClientBizAuthLogoutResponse) => {
// 请求成功后的处理
}, (error: NetFailData) => {
// 请求失败后的处理
})
...
## 9. **理解调用后端api接口返回数据后与UI数据绑定**
- 必须查看xxxPage.md文件里面的该节点是否有声明fromApiOrCacheData(true)的情况,如果有声明表示该节点需要从后端api接口获取数据,那么必须封装一个名字叫做\`bindDataFromNet\`的方法来处理调用后端api接口返回数据后与UI数据绑定的逻辑。
- 在调用后端api接口返回数据后,必须严格参考\`api接口返回数据后与UI数据绑定例子\`,需要根据vtype来做数据绑定
- vtype="verticalListView"、vtype="horizontalListView"、vtype="viewPager"、vtype="radioGroup"、vtype="checkBoxGroup"、vtype="inlineBlockAndWrap"类型的节点组件,需要使用setDataList方法来绑定数据,比如:
this.xxxVerticalListView.setDataList(xxxResponse.data.list)、this.xxxHorizontalListView.setDataList(xxxResponse.xxxList)、this.xxxViewPager.setDataList(xxxResponse.xxxList)
- \`setDataList\`的入参必须是直接写后端api返回的数据路径,比如:xxxResponse.data.xxxList,绝对不能使用全局变量和局部变量进行数据绑定,不能写其他逻辑代码,比如:不能写if、for、while等循环语句,不能写函数调用等。
- vtype="listItemCard"、vtype="oneViewPagerContent"类型的节点组件,需要深度遍历它的第1个item的孩子,然后结合后端api返回的xxxResponse的数据结构给第1个item设置数据,注意只需给第1个item节点设置数据,而不是所有item节点都设置数据,并且必须是直接写后端api返回的数据路径的第一个itemData的数据路径,比如:const itemData = dataList[0]; this.recommendBookTitle1.text = itemData.xxx.titleText,绝对不能使用全局变量和局部变量进行数据绑定,不能写其他逻辑代码,比如:不能写if、for、while等循环语句,如果需要逻辑处理后才能得出this.recommendBookTitle1.text的值,那就必须要封装一个\`getxxx(itemData: any): string\`方法来获取,然后:this.recommendBookTitle1.text = this.getxxx(itemData)
- api接口返回数据后与UI数据绑定例子:
\`\`\`typescript
// 后端xxxApi返回的xxxResponse数据结构
export interface getRecommendBookListResponse {
code: number
msg: string
data: {
list: [
{
id: number // 书籍ID
title: string // 书籍标题
author: string // 作者名称
coverUrl: string // 封面图片URL
description: string // 书籍简介
categoryName: string // 分类名称
}
] // 书籍列表
total: number // 总数量
page: number // 当前页码
size: number // 每页数量
}
}
...
private bindDataFromNet(getRecommendBookListData: getRecommendBookListResponse) {
// 设置书籍列表数据
this.recommendBookList.setDataList(getRecommendBookListData.data.listData)
// 设置第1个item节点数据
const listItemData = getRecommendBookListData.data.listData[0]
this.recommendBookCover1.src = listItemData.coverUrl
this.recommendBookTitle1.text = listItemData.title
this.recommendBookAuthor1.text = listItemData.author
...
// 设置轮播数据
this.recommendViewPager.setDataList(getRecommendBookListData.data.viewPagerData)
// 设置第1个item节点数据
const viePagerItemData = getRecommendBookListData.data.viewPagerData[0]
this.recommendImage1.src = viePagerItemData.coverUrl
this.carouselTitle1.text = this.getCarouselTitleData(viePagerItemData) // 由于有复杂的逻辑处理,所以必须要封装一个方法来获取
}
private getCarouselTitleData(itemData: any) {
let title = ''
if (itemData.title) {
title = itemData.title
} else if (itemData.description) {
title = itemData.description
} else {
title = itemData.categoryName
}
return title
}
...
\`\`\`
- 设置第1个item节点数据时,如果表达式右侧涉及逻辑运算,必须封装成一个类成员方法,如:
\`\`\`typescript
// 涉及逻辑运算时改为调用一个类成员方法
this.recommendImage1.src = this.xxxMethod(getRecommendBookListResponse)
// 如果表达式右侧涉及逻辑运算,封装成一个类成员方法
private xxxMethod(res: getRecommendBookListResponse): string {
let tmp = getRecommendBookListResponse.data.list[0].coverUrl
if (tmp && tmp.length > 5) {
return tmp + '长名字'
}
return tmp
}
\`\`\`
## 10. 一些高级View组件内部已封装好逻辑,因此一些View组件不需要做以下事情
- 轮播ViewPager的指示器点的切换UI更新逻辑,不得在ts代码里面进行,因为ViewPager的指示器点的切换UI更新逻辑已经自动实现了,你不需要再手动实现。
- Tab选项卡\`TabNavigateBar\`和\`TabNavigateBarItem\`的点击切换UI更新逻辑,不得在ts代码里面进行,因为Tab选项卡内部已经封装好了滑动和点击切换UI更新逻辑,你不需要再手动实现。
- 底部导航栏\`bottomTabNavigateBar\`和\`bottomTabNavigateBarItem\`的滑动和点击切换UI更新逻辑,不得在ts代码里面进行,因为底部导航栏内部已经封装好了点击切换UI更新逻辑,你不需要再手动实现。
- 子页面容器\`SubPageContainer\`不需要编写任何设置页面或切换页面的逻辑。
- 禁止对ImageView进行color属性的设置。
- 多状态容器\`MultiStateContainer\`,禁止对其内部的子View组件进行直接设置,而必须通过\`currentStationRef\`来切换状态,修改\`currentStationRef\`切换状态后就会自动显示该状态下的UI组件,并且隐藏其他状态下的UI组件。
`;
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYXBwQ29kZVJ1bGUuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi9zcmMvYXBwQ29kZVJ1bGUudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsTUFBTSxDQUFDLE1BQU0sa0NBQWtDLEdBQUc7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7O0NBNFJqRCxDQUFDIiwic291cmNlc0NvbnRlbnQiOlsiZXhwb3J0IGNvbnN0IHVuZGVyc3RhbmRpbmdDb2RlRnJhbWV3b3JrVGVtcGxhdGUgPSBgXG4jICoq5b+F6aG75YWI55CG6Kej6aG555uu5Luj56CB5qGG5p62MTDkuKrnn6Xor4bngrkqKlxuIyMgMS4gKirnkIbop6NWaWV357uE5Lu255qE5aOw5piO5ZKM5L2/55So6KeE5YiZKipcbiAgLSDov5nkuI3mmK9odG1s44CBcmVhY3TnrYnlvIDlj5HvvIzov5nmmK/kuIDkuKrmlrDnmoTlvIDlj5HmoYbmnrbvvIzmmK/ln7rkuo5zcmMvbGliL3VpbGli5paH5Lu25aS56YeM6Z2i55qEVUnnu4Tku7blupPmnaXlvIDlj5HnmoRcbiAgLSBWaWV357uE5Lu255qE5Z+657G75pivQmFzZVZpZXfvvIzlrrnlmajln7rnsbvmmK9CYXNlQ29udGFpbmVyVmlld++8jOaJgOaciVZpZXfnu4Tku7bpg73nu6fmib/oh6pCYXNlVmlld1xuICAtIOafpeeciydzcmMvbGliL3VpbGliLyfmlofku7blpLnkuIvpnaLnmoTmlofku7blkI3lrZfvvIznkIbop6PmgLvlhbHmnInlpJrlsJHnp41VSee7hOS7tlxuICAtIOaJgOaciXZpZXfnu4Tku7bnmoTlo7DmmI7vvIzlv4Xpobvkvb/nlKhcXGBmaW5kVmlld0J5SWQoaWQ6IHN0cmluZylcXGDmlrnms5XotYvlgLzvvIzlhbbkuK3ph4zpnaLnmoTlj4LmlbBcXGBpZFxcYOW/hemhu+S7jlxcYHNyYy97bW9kdWxlTmFtZUVOfS97cGFnZU5hbWVFTn0ve3BhZ2VOYW1lRU59Lm1kXFxg5p+l5om+6I635Y+W5Yiw5q2j56Gu55qEaWTnmoTlgLxcbiAgLSDnpoHmraLkv67mlLlcXGBzcmMve21vZHVsZU5hbWVFTn0ve3BhZ2VOYW1lRU59L3twYWdlTmFtZUVOfS5tZFxcYOaWh+S7tumHjOmdoueahOS7o+egge+8jOatpOaWh+S7tuWPquivu1xuICAtIFZpZXfnu4Tku7bnmoTlo7DmmI7lv4XpobvlnKjnsbvmlofku7blvIDlp4vml7blhYjlo7DmmI7vvIzlubbkuJTlv4XpobvopoHlo7DmmI7lhbfkvZPnmoRWaWV357uE5Lu257G75Z6L77yM5LiN5b6X5aOw5piO5Li6QmFzZVZpZXfvvIzkuI3lvpflnKjku7vkvZXlh73mlbDkvZPph4zpnaLpgJrov4dmaW5kVmlld0J5SWTmnaXlo7DmmI5WaWV357uE5Lu277yM5q+U5aaC77yaXG4gICAgLSDinIUg5q2j56Gu55qE5YaZ5rOV77yaXG4gICAgXFxgXFxgXFxgdHlwZXNjcmlwdFxuICAgIHB1YmxpYyBjbGFzcyBIb21lUGFnZSBleHRlbmRzIFBhZ2Uge1xuICAgICAgcHJpdmF0ZSBuYW1lVGV4dFZpZXc6IFRleHRWaWV3ID0gdGhpcy5maW5kVmlld0J5SWQoJ25hbWVUZXh0VmlldycpIGFzIFRleHRWaWV3XG4gICAgICAuLi4uXG4gICAgfVxuICAgIFxcYFxcYFxcYFxuICAgIC0g4p2MIOmUmeivr+eahOWGmeazle+8iOayoeacieWjsOWFt+S9k+eahFZpZXfnu4Tku7bnsbvlnovvvInvvJpcbiAgICBcXGBcXGBcXGB0eXBlc2NyaXB0XG4gICAgcHJpdmF0ZSBuYW1lVGV4dFZpZXcgPSB0aGlzLmZpbmRWaWV3QnlJZCgnbmFtZVRleHRWaWV3JylcbiAgICBcXGBcXGBcXGBcbiAgICAtIOKdjCDplJnor6/nmoTlhpnms5XvvIjlnKjlh73mlbDkvZPph4zpnaLpgJrov4dmaW5kVmlld0J5SWTmnaXlo7DmmI5WaWV357uE5Lu277yJ77yaXG4gICAgXFxgXFxgXFxgdHlwZXNjcmlwdFxuICAgIHByaXZhdGUgb25lRnVuY3Rpb24oKSB7XG4gICAgICBjb25zdCBuYW1lVGV4dFZpZXc6IFRleHRWaWV3ID0gdGhpcy5maW5kVmlld0J5SWQoJ25hbWVUZXh0VmlldycpIGFzIFRleHRWaWV3XG4gICAgICAuLi4uXG4gICAgfVxuICAgIFxcYFxcYFxcYFxuICAtIOaJgOaciVZpZXfnu4Tku7bnmoTmmL7npLrjgIHpmpDol4/ljaDkvY3jgIHpmpDol4/kuI3ljaDkvY3nmoTorr7nva7pg73lv4Xpobvkvb/nlKhWaWV355qEXFxgdmlzaWJpbGl0eVxcYOWxnuaAp+i/m+ihjOiuvue9ru+8jOS4jeW+l+S9v+eUqFxcYHN0YXRpb25cXGDlsZ7mgKfjgIJcbiAgLSDmiYDmnIlWaWV357uE5Lu255qEbm9ybWFs44CBcmVhZE9ubHnjgIFmb2N1c+OAgXNlbGVjdGVk44CBZGlzYWJsZWTnirbmgIHnmoTlj5jljJbpg73lv4Xpobvkvb/nlKh2aWV355qE5bGe5oCnc3RhdGlvbuadpeebtOaOpeiuvue9ru+8jOavlOWmgu+8mlxcYHRoaXMubmFtZUlucHV0Vmlldy5zdGF0aW9uID0gVmlld1N0YXRpb24uRm9jdXNcXGDvvIzlj6/pgInlgLzmnInvvJpWaWV3U3RhdGlvbi5OT1JNQUzjgIFWaWV3U3RhdGlvbi5SRUFET05MWeOAgVZpZXdTdGF0aW9uLkZPQ1VTRUTjgIFWaWV3U3RhdGlvbi5TRUxFQ1RFROOAgVZpZXdTdGF0aW9uLkRJU0FCTEVE44CCXG4gIC0g5omA5pyJVmlld+e7hOS7tijljIXmi6xCYXNlVmlld+OAgUJhc2VDb250YWluZXLku6Xlj4rlroPku6znmoTlrZDnsbsp55qE54K55Ye75LqL5Lu277yM6YO95Y+v5Lul55u05o6l5Zyodmlld+e7hOS7tueahOWjsOaYjuWQjumdoua3u+WKoFxcYHNldE9uQ2xpY2tMaXN0ZW5lclxcYOaWueazleadpeebkeWQrOeCueWHu+S6i+S7tu+8jOavlOWmgu+8mlxcYHRoaXMueHh4Q29udGFpbmVyLnNldE9uQ2xpY2tMaXN0ZW5lcigoKSA9PiB7IHRoaXMubmFtZVRleHRWaWV3LnRleHQgPSAn54K55Ye75LqGJyArIHRoaXMubmFtZVRleHRWaWV3LnRleHQgfSlcXGBcbiAgLSDmiYDmnInnmoRkaWFsb2flr7nor53moYbvvIzlv4XpobvosIPnlKhEaWFsb2fnu4Tku7bnmoRzaG935pa55rOV5p2l5pi+56S677yM5YWz6Zet5pe25b+F6aG76LCD55SoRGlhbG9n57uE5Lu255qEaGlkZeaWueazleadpeWFs+mXre+8jOavlOWmgu+8mnRoaXMueHh4RGlhbG9nLnNob3coKeOAgXRoaXMueHh4RGlhbG9nLmhpZGUoKe+8jOemgeatouS9v+eUqHZpZXfnmoR2aXNpYmlsaXR55bGe5oCn5p2l5pi+56S66ZqQ6JeP5a+56K+d5qGG44CCXG4gIC0g5omA5pyJ55qERHJvcERvd25NZW515LiL5ouJ6I+c5Y2V77yM5b+F6aG76LCD55SoRHJvcERvd25NZW5157uE5Lu255qEc2hvd+aWueazleadpeaYvuekuu+8jOWFs+mXreaXtuW/hemhu+iwg+eUqERyb3BEb3duTWVudee7hOS7tueahGhpZGXmlrnms5XmnaXlhbPpl63vvIzmr5TlpoLvvJp0aGlzLnh4eERyb3BEb3duTWVudS5zaG93KCnjgIF0aGlzLnh4eERyb3BEb3duTWVudS5oaWRlKCnvvIznpoHmraLkvb/nlKh2aWV355qEdmlzaWJpbGl0eeWxnuaAp+adpeaYvuekuumakOiXj+S4i+aLieiPnOWNleOAglxuICAtIOaJgOacieeahFNpZGVTbGlkZUNvbnRhaW5lcuS+p+a7keWuueWZqO+8jOW/hemhu+iwg+eUqFNpZGVTbGlkZUNvbnRhaW5lcue7hOS7tueahHNob3fmlrnms5XmnaXmmL7npLrvvIzlhbPpl63ml7blv4XpobvosIPnlKhTaWRlU2xpZGVDb250YWluZXLnu4Tku7bnmoRoaWRl5pa55rOV5p2l5YWz6Zet77yM5q+U5aaC77yadGhpcy54eHhTaWRlU2xpZGVDb250YWluZXIuc2hvdygp44CBdGhpcy54eHhTaWRlU2xpZGVDb250YWluZXIuaGlkZSgp77yM56aB5q2i5L2/55Sodmlld+eahHZpc2liaWxpdHnlsZ7mgKfmnaXmmL7npLrpmpDol4/kvqfmu5HlrrnlmajjgIJcblxuIyMgMi4gKirnkIbop6PllK/kuIDnmoTog73lipvmlrnms5VBbGxGdW5jdGlvbi50c+S7peWPimpzb27nm7jlhbPku6PnoIEqKlxuICAtIOS9v+eUqCdyZWFkX2ZpbGUn5bel5YW36ZiF6K+75bm255CG6KejJ3NyYy9saWIvQWxsRnVuY3Rpb24udHMn5paH5Lu277yM55CG6Kej6K+l5bel56iL6IO95L2/55So55qE5ZSv5LiA55qE6IO95Yqb5pa55rOVXG4gIC0g5L2/55SoJ3JlYWRfZmlsZSflt6XlhbfpmIXor7vlubbnkIbop6Mnc3JjL2xpYi9Kc29uT2JqZWN0LnRzJ+WSjCdzcmMvbGliL0pzb25BcnJheS50cyfmlofku7bvvIznkIbop6Pov5nkuKTkuKrmlofku7blrprkuYnnmoRqc29u55u45YWz55qE5pa55rOVXG4gIC0g6K+35Lil5qC85oyJ54Wn5ZSv5LiA55qE6IO95Yqb5pa55rOVQWxsRnVuY3Rpb24udHPvvIzkuI3opoHmlrDlop7jgIHliKDpmaTmiJbkv67mlLnku7vkvZVBbGxGdW5jdGlvbuaOpeWPo+WjsOaYju+8m+S5n+S4jeimgeiwg+eUqOWFtuS7luacquWcqEFsbEZ1bmN0aW9u5Lit5Ye6546w55qE5Ye95pWw5oiW5qih5Z2XXG4gIC0g5omA5pyJ55qEdG9hc3Tmj5DnpLrvvIzlv4XpobvosIPnlKhBbGxGdW5jdGlvbi5zaG93VG9hc3Tmlrnms5XmnaXmmL7npLrvvIzlubbkuJTlpoLnlKjmiLfmj4/ov7DmsqHmnInmmI7noa7or7TmmI7pnIDopoF0b2FzdOaPkOekuu+8jOS9oOS4jeW+l+aTheiHqui/m+ihjFRvYXN05o+Q56S644CCXG4gIC0g5omA5pyJ55qEZGlhbG9n5a+56K+d5qGG77yM5b+F6aG76LCD55SoRGlhbG9n57uE5Lu255qEc2hvd+aWueazleadpeaYvuekuu+8jOWFs+mXreaXtuW/hemhu+iwg+eUqERpYWxvZ+e7hOS7tueahGhpZGXmlrnms5XmnaXlhbPpl63vvIzmr5TlpoLvvJp0aGlzLnh4eERpYWxvZy5zaG93KCnjgIF0aGlzLnh4eERpYWxvZy5oaWRlKCnvvIznpoHmraLkvb/nlKh2aWV355qEdmlzaWJpbGl0eeWxnuaAp+adpeaYvuekuumakOiXj+WvueivneahhuOAglxuICAtIOaJgOacieeahERyb3BEb3duTWVudeS4i+aLieiPnOWNle+8jOW/hemhu+iwg+eUqERyb3BEb3duTWVudee7hOS7tueahHNob3fmlrnms5XmnaXmmL7npLrvvIzlhbPpl63ml7blv4XpobvosIPnlKhEcm9wRG93bk1lbnXnu4Tku7bnmoRoaWRl5pa55rOV5p2l5YWz6Zet77yM5q+U5aaC77yadGhpcy54eHhEcm9wRG93bk1lbnUuc2hvdygp44CBdGhpcy54eHhEcm9wRG93bk1lbnUuaGlkZSgp77yM56aB5q2i5L2/55Sodmlld+eahHZpc2liaWxpdHnlsZ7mgKfmnaXmmL7npLrpmpDol4/kuIvmi4noj5zljZXjgIJcbiAgLSDmiYDmnInnmoRTaWRlU2xpZGVDb250YWluZXLkvqfmu5HlrrnlmajvvIzlv4XpobvosIPnlKhTaWRlU2xpZGVDb250YWluZXLnu4Tku7bnmoRzaG935pa55rOV5p2l5pi+56S677yM5YWz6Zet5pe25b+F6aG76LCD55SoU2lkZVNsaWRlQ29udGFpbmVy57uE5Lu255qEaGlkZeaWueazleadpeWFs+mXre+8jOavlOWmgu+8mnRoaXMueHh4U2lkZVNsaWRlQ29udGFpbmVyLnNob3coKeOAgXRoaXMueHh4U2lkZVNsaWRlQ29udGFpbmVyLmhpZGUoKe+8jOemgeatouS9v+eUqHZpZXfnmoR2aXNpYmlsaXR55bGe5oCn5p2l5pi+56S66ZqQ6JeP5L6n5ruR5a655Zmo44CCXG4gIC0g5bu25pe25omn6KGMQWxsRnVuY3Rpb24uc2V0VGltZW91dOezu+e7n+S8muiHquWKqOa4heeQhnRpbWVvdXTvvIzku6PnoIHkuIrnu53lr7nkuI3og73ov5vooYxjbGVhVGltZW91dOetieexu+S8vOaTjeS9nO+8jOWboOatpHNldFRpbWVvdXTkuZ/kuI3pnIDopoHlo7DmmI7lj5jph4/ljrvmjqXmlLZzZXRUaW1lb3V06L+U5Zue55qE5byV55So44CCXG4gIC0g5omA5pyJbG9jYWxTdG9yYWdl5pys5Zyw5pWw5o2u5a2Y5YKo6YO95b+F6aG75L2/55SoQWxsRnVuY3Rpb27nmoRzYXZlRGF0YWJhc2VCb29sZWFu44CBc2F2ZURhdGFiYXNlTnVtYmVy44CBc2F2ZURhdGFiYXNlU3RyaW5n44CBc2F2ZURhdGFiYXNlQXJyYXnjgIFzYXZlRGF0YWJhc2VPYmplY3Tmlrnms5XmnaXlrZjlgqjmlbDmja7vvIzkvb/nlKhnZXREYXRhYmFzZUJvb2xlYW7jgIFnZXREYXRhYmFzZU51bWJlcuOAgWdldERhdGFiYXNlU3RyaW5n44CBZ2V0RGF0YWJhc2VBcnJheeOAgWdldERhdGFiYXNlT2JqZWN05pa55rOV5p2l6I635Y+W5pWw5o2u44CCXG5cbiMjIDMuICoq55CG6KejUmVm44CBd2F0Y2gqKlxuICAtIFJlZueahOaVsOaNruexu+Wei+WPquiDveaYr+S7peS4izXkuK3nsbvlnovvvJrmlbDlrZfkvb/nlKhSZWY8bnVtYmVyPu+8jOWtl+espuS4suS9v+eUqFJlZjxzdHJpbmc+77yM5biD5bCU6YeP5L2/55SoUmVmPGJvb2xlYW4+77yM5a+56LGh5L2/55SoUmVmPGFueT7vvIzmlbDnu4Tkvb/nlKhSZWY8QXJyYXk8YW55Pj7miJZSZWY8QXJyYXk8YW55PiB8IG51bGw+5oiWUmVmPGFueVtdPuaIllJlZjxhbnlbXSB8IG51bGw+44CCXG4gIC0g5L2/55SoJ3JlYWRfZmlsZSflt6XlhbfpmIXor7vlubbnkIbop6Mnc3JjL2xpYi9SZWYudHMn5paH5Lu277yM55CG6KejUmVm5ZKMd2F0Y2jnmoTlrprkuYnnlKjms5VcbiAgLSB3YXRjaOaYr+ebkeWQrFJlZueahOWPmOWMlu+8jOeEtuWQjuWBmuebuOWFs1VJ5pu05paw5oiW5YW25LuW5Lia5Yqh6YC76L6R77yM56ys5LiA5Liq5Y+C5pWw5piv55uR5ZCs55qEUmVm5pWw57uE77yM56ys5LqM5Liq5Y+C5pWw5piv55uR5ZCs55qE5Zue6LCD5Ye95pWw77yM5Zue6LCD5Ye95pWw5Lit5Y+v5Lul5YGa55u45YWzVUnmm7TmlrDmiJblhbbku5bkuJrliqHpgLvovpHjgIJcbiAgLSBSZWbmmK/lk43lupTlvI/mlbDmja7mupDvvIx3YXRjaOaYr+ebkeWQrFJlZueahOWPmOWMlu+8jOeEtuWQjuWBmuebuOWFs1VJ5pu05paw5oiW5YW25LuW5Lia5Yqh6YC76L6R77yM5q+U5aaC77yaXG4gIFxcYFxcYFxcYHR5cGVzY3JpcHRcbiAgcHJpdmF0ZSBuYW1lVGV4dFJlZjogUmVmPHN0cmluZz4gPSByZWYoJycpXG4gIHByaXZhdGUgb3RoZXJDb3VudFJlZjogUmVmPG51bWJlcj4gPSByZWYoMClcbiAgd2F0Y2goW3RoaXMubmFtZVRleHRSZWYsIHRoaXMub3RoZXJDb3VudFJlZl0sICgpID0+IHtcbiAgICAvLyDlj6/ku6XlhYjnm7jlhbPnmoRVSeabtOaWsOaIluS4muWKoemAu+i+kVxuICAgIGlmICh0aGlzLm90aGVyQ291bnRSZWYudmFsdWUgPiA1KSB7XG4gICAgICB0aGlzLm5hbWVUZXh0Vmlldy50ZXh0ID0gdGhpcy5uYW1lVGV4dFJlZi52YWx1ZSArICc1J1xuICAgICB9IGVsc2Uge1xuICAgICAgdGhpcy5uYW1lVGV4dFZpZXcudGV4dCA9IHRoaXMubmFtZVRleHRSZWYudmFsdWUgKyAnMTAnXG4gICAgfVxuICB9KVxuICBcXGBcXGBcXGBcbiAgLSBcXGBzcmMvbGliL3VpbGliL1JlZi50c1xcYOmHjOmdoueahFxcYHdhdGNoXFxg5Ye95pWw77yM56ys5LiA5Liq5YWl5Y+CXFxgcmVmczogUmVmPFQ+W11cXGDnu53lr7nnpoHmraLlhYjlo7DmmI7lsYByZWbmlbDnu4Tpg6jlj5jph4/lho3kvKDlhaXvvIzmraPnoa7nmoTlhpnms5XlupTor6XmmK/nm7TmjqXlhpnmlbDnu4TvvJpcXGBbcmVmMSwgcmVmMl1cXGDlsLHooYxcbiAgLSDinYwg6ZSZ6K+v55qE5YaZ5rOV77yaXG4gICAgXFxgXFxgXFxgIHR5cGVzY3JpcHRcbiAgICBjb25zdCByZWZBcnJheSA9IFtyZWYxLCByZWYyXVxuICAgIHdhdGNoKHJlZkFycmF5LCAobmV3VmFsdWVzOiBUW10pID0+IHtcbiAgICAgICAgLy8g55uR5ZCscmVmMeWSjHJlZjLnmoTlj5jljJZcbiAgICB9KVxuICAgIFxcYFxcYFxcYFxuICAtIOKchSDmraPnoa7nmoTlhpnms5XvvJpcbiAgICBcXGBcXGBcXGAgdHlwZXNjcmlwdFxuICAgIHdhdGNoKFtyZWYxLCByZWYyXSwgKG5ld1ZhbHVlczogVFtdKSA9PiB7XG4gICAgICAgIC8vIOebkeWQrHJlZjHlkoxyZWYy55qE5Y+Y5YyWXG4gICAgfSlcbiAgICBcXGBcXGBcXGBcblxuIyMgNC4gKirnkIbop6NzZXREYXRhTGlzdOaWueazleeahOmdmeaAgeaVsOaNruS4juWKqOaAgeaVsOaNruWcuuaZryoqXG4gIC0g5a+55LqO5Yqo5oCB5pWw5o2u5a655ZmoVmlld1BhZ2Vy44CBTGlzdFZpZXfnmoRzZXREYXRhTGlzdOaWueazle+8jOWmguaenOaVsOaNruaYr+WKqOaAgeeahO+8jOavlOWmgu+8muS7juWQjuerr+aOpeWPo+iOt+WPlueahO+8jOaJjeiDveS9v+eUqHNldERhdGFMaXN05pa55rOV5p2l6K6+572u5pWw5o2u44CC6Z2Z5oCB5pWw5o2u5bey57uP5ZyoVUnpobXpnaLkuIrlhpnlpb3kuobvvIzkvaDkuI3lvpfkvb/nlKhzZXREYXRhTGlzdOaWueazleadpeiuvue9ruaVsOaNruOAglxuICAtIOWIpOaWreagh+WHhu+8muWmguaenFVJ6aG16Z2i5Lit5bey57uP5pyJ5a6M5pW055qE6L2u5pKt6aG555uuL+WIl+ihqOmhueebru+8jOWwseaYr+mdmeaAgeaVsOaNru+8jOWmguaenOaYr+e9kee7nEFQSei/lOWbnueahOaVsOaNruWIl+ihqOWwseaYr+WKqOaAgeaVsOaNruOAglxuICAtIOKchSDpnZnmgIHmlbDmja7lrrnlmajvvIhVSemhtemdouS4reW3suWGmeWlveWGheWuue+8ie+8muS4jeS9v+eUqHNldERhdGFMaXN0KClcbiAgLSDinIUg5Yqo5oCB5pWw5o2u5a655Zmo77yI6ZyA6KaB5LuOQVBJ6I635Y+W77yJ77ya5omN5L2/55Soc2V0RGF0YUxpc3QoKVxuXG4jIyA1LiAqKlZpZXfnu4Tku7bvvIznkIbop6Pmr4/kuKrnu4Tku7blj6/og73ph4zpnaLlt7Lnu4/mnInkuoblk43lupTlvI/mlbDmja7mupDvvIzlpoLmnpznu4Tku7bph4zpnaLlt7Lnu4/mnInkuoblk43lupTlvI/mlbDmja7mupDvvIzpgqPkuYjkvaDkuI3lvpflho3oh6rlt7HliJvlu7pyZWblj5jph4/miJbogIXkvb/nlKhvblNlbGVjdENoYW5nZeetieWbnuiwg+inpuWPkVVJ5pu05pawKipcbiAgLSDkvovlrZDvvJpWaWV3UGFnZXLnmoRjdXJyZW50U2VsZWN0SW5kZXjlkoxjdXJyZW50U2VsZWN0SW5kZXhSZWblsZ7mgKflt7Lnu4/mmK/lk43lupTlvI/mlbDmja7mupDvvIzlj6/ku6Xnm7TmjqXkvb/nlKhjdXJyZW50U2VsZWN0SW5kZXhSZWbmnaXnu5Hlrproh6rliqjmm7TmlrBVSeOAglxuICAgIHB1YmxpYyBpbml0VmlldygpIHtcbiAgICAgIHRoaXMuc2hvd0luZGV4VGV4dFZpZXcudGV4dCA9IHRoaXMudmlld1BhZ2VyLmN1cnJlbnRTZWxlY3RJbmRleFJlZi52YWx1ZSArIDEgKyAnLycgKyB0aGlzLnZpZXdQYWdlci5kYXRhTGlzdC5sZW5ndGhcbiAgICB9XG4gIC0g5L6L5a2Q77yaVmlld1BhZ2Vy55qEY3VycmVudFNlbGVjdEluZGV45ZKMY3VycmVudFNlbGVjdEluZGV4UmVm5bGe5oCn5bey57uP5piv5ZON5bqU5byP5pWw5o2u5rqQ77yM5Y+v5Lul55u05o6l5L2/55SoY3VycmVudFNlbGVjdEluZGV4UmVm5p2l57uR5a6a6Ieq5Yqo5pu05pawVUnvvIzmr5TlpoLvvJpcbiAgICBcXGBcXGBcXGB0eXBlc2NyaXB0XG4gICAgcHVibGljIGluaXRWaWV3KCkge1xuICAgICAgd2F0Y2goW3RoaXMudmlld1BhZ2VyLmN1cnJlbnRTZWxlY3RJbmRleFJlZl0sICgpID0+IHtcbiAgICAgICAgdGhpcy5zaG93SW5kZXhUZXh0Vmlldy50ZXh0ID0gdGhpcy52aWV3UGFnZXIuY3VycmVudFNlbGVjdEluZGV4UmVmLnZhbHVlICsgMSArICcvJyArIHRoaXMudmlld1BhZ2VyLmRhdGFMaXN0Lmxlbmd0aFxuICAgICAgfSlcbiAgICBcXGBcXGBcXGBcbiAgLSB0YWLnu4Tku7blkozova7mkq3nu4Tku7bnmoTpgInmi6nlj5jljJbnm5HlkKzvvIzlv4Xpobvkvb/nlKhcXGBWaWV3UGFnZXJcXGDmiJZcXGBUYWJDb250YWluZXJcXGDnmoRcXGBvblNlbGVjdENoYW5nZShmdW5jOiAoaXRlbTogYW55LCBwb3NpdGlvbjogbnVtYmVyKSA9PiB2b2lkKVxcYOWHveaVsOadpeebkeWQrOmAieaLqeWPmOWMllxuXG4jIyA2LiAqKueQhuino+mhtemdoui3s+i9rCoqXG4gIC0g57yW5YaZ6Lez6L2s6aG16Z2i55qE5Luj56CB5pe277yM5b+F6aG76KaB5YWI5p+l55yL55uu5qCH6aG16Z2i55qEdHPmlofku7bvvIznoa7orqTnm67moIfpobXpnaLmmK/lkKblrZjlnKjvvIzlpoLmnpzlrZjlnKjpgqPkuYjlv4XpobvopoHkvb/nlKhcXGBBbGxGdW5jdGlvbi5zdGFydFBhZ2UobmV3IHh4eFBhZ2UoeHh4KVxcYOadpei3s+i9rOWIsOebruagh+mhtemdonh4eFBhZ2XvvIznu53lr7nkuI3og73mlbfooY3lnLDkvb/nlKhcXGBBbGxGdW5jdGlvbi5zaG93VG9hc3RcXGDmnaXmqKHmi5/mj5DnpLrot7PovazpobXpnaLvvIzpmaTpnZ7nm67moIfpobXpnaLkuI3lrZjlnKjjgIJcbiAgLSDpobXpnaLot7Povazlv4Xpobvkvb/nlKhBbGxGdW5jdGlvbi5zdGFydFBhZ2Xlrp7njrDvvIxzdGFydFBhZ2XkvKDlhaVQYWdl55qE5a2Q57G75a+56LGh77yM5aaC77yaQWxsRnVuY3Rpb24uc3RhcnRQYWdlKG5ldyBQcm9kdWN0UGFnZSh0aGlzLnByb2R1Y3RJRCkp44CCXG4gIC0g4p2MIOmhtemdoui3s+i9rOS4jemcgOiAg+iZkeaooeWdl+WMluOAgeW+queOr+S+nei1luOAgea4kOi/m+W8j+W8gOWPke+8jOe7neWvueemgeatouWxj+iUvemhtemdoui3s+i9rOeahOS7o+egge+8jOaJgOaciemhtemdoumDveW3sue7j+WtmOWcqO+8jOS4jeS8muWHuueOsOe8luivkeWHuumUmeeahO+8jOW/hemhu+aYjuehruWGmeS4iumhtemdoui3s+i9rOeahOS7o+eggeWSjOebruagh+mhtemdouaJgOmcgOeahOi3s+i9rOWPguaVsOOAglxuICAtIOWcqEFwcGxpY2F0aW9uLnRz6K6+572u6Lez6L2s5ZCv5Yqo6aG177yM5q+U5aaC77yaXHRcbiAgICAvLyDlupTnlKjnvZHnq5nlkK/liqjml7blm57osIPnmoTnrKzkuIDkuKrlh73mlbDvvIznlKjkuo7liJ3lp4vljJbkuIDkupvlhajlsYDnmoTkuJzopb/jgIJcbiAgICBwcm90ZWN0ZWQgb25BcHBsaWNhdGlvbkNyZWF0ZSgpOiB2b2lkIHtcbiAgICAgIEFsbEZ1bmN0aW9uLnN0YXJ0UGFnZShuZXcgU3BsYXNoUGFnZSgpKVxuICAgIH1cbiAgLSDinIUg5byV5YWl5YW25LuW57G75Y+q5YWB6K645LiA56eN5YaZ5rOV77ya5Zyo57G75paH5Lu26aG26YOo6L+b6KGMaW1wb3J077yaaW1wb3J0IEd1aWRlUGFnZSBmcm9tICcuLi9ndWlkZVBhZ2UvZ3VpZGVQYWdlJ+OAglxuICAtIOKdjCDnu53lr7nnpoHmraLkvb/nlKjliqjmgIFpbXBvcnTnmoTlhpnms5XvvIznqIvluo/lupXlsYLlt7Lnu4/lpITnkIblpb3lvqrnjq/kvp3otZbpl67popjkuobvvIzkvaDkuI3pnIDopoHogIPomZHvvIzmr5TlpoLnu53lr7nnpoHmraLku6XkuIvlhpnms5XvvJpcbiAgICAgICAgLy8g5L2/55So5Yqo5oCB5a+85YWl6YG/5YWN5b6q546v5L6d6LWWXG4gICAgICAgIGltcG9ydCgnLi4vZ3VpZGVQYWdlL2d1aWRlUGFnZScpLnRoZW4oKHsgZGVmYXVsdDogR3VpZGVQYWdlIH0pID0+IHtcbiAgICAgICAgICAgIEFsbEZ1bmN0aW9uLnN0YXJ0UGFnZShuZXcgR3VpZGVQYWdlKCkpXG4gICAgICAgIH0pXG4gIC0g4p2MIOe7neWvueemgeatouS9v+eUqHJlcXVpcmXnmoTlhpnms5XvvIznqIvluo/lupXlsYLlt7Lnu4/lpITnkIblpb3lvqrnjq/kvp3otZbpl67popjkuobvvIzkvaDkuI3pnIDopoHogIPomZHvvIzmr5TlpoLnu53lr7nnpoHmraLku6XkuIvlhpnms5XvvJpcbiAgICAgICAgcmVxdWlyZSgnLi4vZ3VpZGVQYWdlL2d1aWRlUGFnZScpLmRlZmF1bHRcbiAgLSDinIUg5q2j56Gu55qE5YaZ5rOV5piv77yaQWxsRnVuY3Rpb24uc3RhcnRQYWdlKG5ldyBHdWlkZVBhZ2UoKSnjgILnhLblkI7lnKjnsbvmlofku7bpobbpg6jov5vooYxpbXBvcnTvvJppbXBvcnQgR3VpZGVQYWdlIGZyb20gJy4uL2d1aWRlUGFnZS9ndWlkZVBhZ2Un44CCXG4gIC0g5aaC5p6c6aG16Z2i6Lez6L2s6ZyA6KaB5Lyg6YCS5Y+C5pWw77yM6YKj5LmI6ZyA6KaB5L2/55SoUGFnZeeahOWtkOexu+aehOmAoOWHveaVsOadpeS8oOmAkuWPguaVsO+8jOavlOWmgu+8mkFsbEZ1bmN0aW9uLnN0YXJ0UGFnZShuZXcgR3VpZGVQYWdlKHRoaXMucHJvZHVjdElEKSnjgIJcbiAgLSDlnKjlrp7njrDpobXpnaLot7Povazku6PnoIHnmoTml7blgJnvvIzlv4XpobvkuKXmoLzmjInnhafku6XkuIvkuInmraXmnaXlrp7njrDvvJpcbiAgICAgIC0gKirnrKzkuIDmraXvvJrlhYjliIbmnpDot7PovaznmoTnm67moIfpobXpnaLpnIDopoHkvKDpgJLlk6rkupvlj4LmlbAqKlxuICAgICAgLSAqKuesrOS6jOatpe+8muS/ruaUueebruagh+mhtemdouaehOmAoOWHveaVsCoq77yaXG4gICAgICBcXGBcXGBcXGB0eXBlc2NyaXB0XG4gICAgICBleHBvcnQgZGVmYXVsdCBjbGFzcyBSZWFkaW5nUGFnZSBleHRlbmRzIFBhZ2Uge1xuICAgICAgICAgIHByaXZhdGUgYm9va0lkOiBudW1iZXJcbiAgICAgICAgICBwcml2YXRlIGNoYXB0ZXJUaXRsZTogc3RyaW5nXG4gICAgICAgICAgXG4gICAgICAgICAgY29uc3RydWN0b3IoYm9va0lkOiBudW1iZXIsIGNoYXB0ZXJUaXRsZTogc3RyaW5nKSB7XG4gICAgICAgICAgICAgIHN1cGVyKClcbiAgICAgICAgICAgICAgdGhpcy5ib29rSWQgPSBib29rSWRcbiAgICAgICAgICAgICAgdGhpcy5jaGFwdGVyVGl0bGUgPSBjaGFwdGVyVGl0bGVcbiAgICAgICAgICB9XG4gICAgICB9XG4gICAgICBcXGBcXGBcXGBcbiAgICAgIC0gKirnrKzkuInmraXvvJrkv67mlLnot7Povazku6PnoIEqKlxuICAgICAgXFxgXFxgXFxgdHlwZXNjcmlwdFxuICAgICAgY29uc3QgYm9va0RhdGEgPSB0aGlzLnJlYWRpbmdIaXN0b3J5RGF0YVJlZi52YWx1ZVswXVxuICAgICAgQWxsRnVuY3Rpb24uc3RhcnRQYWdlKG5ldyBSZWFkaW5nUGFnZShib29rRGF0YS5pZCwgYm9va0RhdGEuY2hhcHRlclRpdGxlKSlcbiAgICAgIFxcYFxcYFxcYFxuXG4jIyA3LiAqKueQhuino+WfuuehgOW8gOWPkeinhOWImSoqXG4gIC0gbGli5paH5Lu25aS56YeM6Z2i55qE5Luj56CB5paH5Lu26YO95piv5YaF572u5qGG5p625Luj56CB77yM57ud5a+556aB5q2i5a+5bGli5paH5Lu25aS555qE5Luj56CB6L+b6KGM5aKe5Yig5pS55pON5L2cXG4gIC0g5Lu75L2V6aG16Z2i77yI54m55Yir5piv6Zeq5bGP6aG177yJ55qE5Lia5Yqh6YC76L6R57ud5a+55LiN6IO95pS+5YiwQXBwbGljYXRpb24udHPov5vooYzlpITnkIbvvIxBcHBsaWNhdGlvbi50c+WPqui/m+ihjOWFqOWxgOe9kee7nOaLpuaIquWkhOeQhuOAgeWFqOWxgOmdmeaAgeWPmOmHj+OAgeWFqOWxgOmdmeaAgeaWueazleeahOWumuS5ieWSjOWunueOsFxuICAtIOWmguaenOaYr+WkmuS4qumhtemdouWFseeUqOeahOaVsOaNruWPmOmHj++8jOWPr+S7peiAg+iZkeWjsOaYjuS4uuWFqOWxgOmdmeaAgeWPmOmHj++8jOW5tuWcqEFwcGxpY2F0aW9uLnRz5Lit6L+b6KGM5aOw5piO44CCXG4gIC0g5YWo5bGA6Z2Z5oCB5Y+Y6YeP5Zyo5YWz6ZetQVBQ5oiW6ICF572R6aG15ZCO5Lya5riF55CG5o6J55qE77yM5omA5Lul5L2g6KaB5oCd6ICD5riF5qWa5ZOq5Lqb5pWw5o2u5Y+Y6YeP6ZyA6KaB5oyB5LmF5YyW5YKo5a2Y77yM5ZOq5Lqb5piv5YWo5bGA6Z2Z5oCB5Y+Y6YeP77yM5oyB5LmF5YyW5o6l5Y+j77yac2F2ZURhdGFiYXNlWHh45ZKMZ2V0RGF0YWJhc2VEYXRhWHh477yM5q+U5aaC5piv5ZCm5bey57uP5by55Ye66L+H5bm/5ZGK5by55qGG77yM6L+Z5Liq5bqU6K+l5oyB5LmF5YyW5YKo5a2Y44CCXG4gIC0g5aOw5piO5YWo5bGA5Y+Y6YeP44CB5bGA6YOo5Y+Y6YeP5b+F6aG76KaB5aOw5piO5pWw5o2u57G75Z6L77yM5bm25LiU57ud5a+556aB5q2i5L2/55SoYW5577yM6K+35L2/55So5YW35L2T57G75Z6L5p2l5aOw5piOXG4gIC0g6L2u5pKtVmlld1BhZ2Vy5a+55bqU55qE57Si5byV5oyH56S65Zmo5a655Zmo77yM6L+Z5Liq57Si5byV5oyH56S65Zmo5LiN6ZyA6KaB6aKd5aSW5Luj56CB5a6e546w5oyH56S65Zmo54K555qE5YiH5o2iVUnmm7TmlrDpgLvovpHvvIzmnKzouqtWaWV3UGFnZXLkuI5JbmRpY2F0b3JDb250YWluZXLlupXlsYLlt7Lnu4/lrp7njrDkuobliIfmjaLnmoRVSeabtOaWsOmAu+i+keeahOS6hu+8jOWPqumcgOimgXZpZXdQYWdlci5iaW5kSW5kaWNhdG9yKGluZGljYXRvckNvbnRhaW5lcinljbPlj6/jgIJcbiAgLSBUYWLpgInpobnljaFcXGBUYWJOYXZpZ2F0ZUJhclxcYOWSjFxcYFRhYk5hdmlnYXRlQmFySXRlbVxcYOeahOeCueWHu+WIh+aNolVJ5pu05paw6YC76L6R77yM5LiN5b6X5ZyodHPku6PnoIHph4zpnaLov5vooYzvvIzlm6DkuLpUYWLpgInpobnljaHlhoXpg6jlt7Lnu4/lsIHoo4Xlpb3kuobmu5Hliqjlkozngrnlh7vliIfmjaJVSeabtOaWsOmAu+i+ke+8jOS9oOS4jemcgOimgeWGjeaJi+WKqOWunueOsOOAglxuICAtIOW6lemDqOWvvOiIquagj1xcYGJvdHRvbVRhYk5hdmlnYXRlQmFyXFxg5ZKMXFxgYm90dG9tVGFiTmF2aWdhdGVCYXJJdGVtXFxg55qE5ruR5Yqo5ZKM54K55Ye75YiH5o2iVUnmm7TmlrDpgLvovpHvvIzkuI3lvpflnKh0c+S7o+eggemHjOmdoui/m+ihjO+8jOWboOS4uuW6lemDqOWvvOiIquagj+WGhemDqOW3sue7j+WwgeijheWlveS6hueCueWHu+WIh+aNolVJ5pu05paw6YC76L6R77yM5L2g5LiN6ZyA6KaB5YaN5omL5Yqo5a6e546w77yM5q+U5aaC6aaW6aG15Li75L2T6aG155qE5bqV6YOo5a+86Iiq5qCP77yM5LiN6ZyA6KaB5a6e546w54K55Ye75YiH5o2iVUnmm7TmlrDpgLvovpHvvIzlm6DkuLrlupXpg6jlr7zoiKrmoI/nmoTngrnlh7vliIfmjaJVSeabtOaWsOmAu+i+keW3sue7j+iHquWKqOWunueOsOS6huOAglxuICAtIOavj+S4gOS4qumhtemdoijlpoJsb2dpblBhZ2Up5b+F6aG75ZyocGFnZeaWh+S7tuWkueS4i+mdouWIm+W7uuS4gOS4qumhtemdouaWh+S7tuWkuSjlpoJsb2dpbinvvIznhLblkI7pobXpnaLnmoR0c+aWh+S7tijlpoJsb2dpblBhZ2UudHMp5Yib5bu65Zyo6aG16Z2i5paH5Lu25aS55LiL6Z2i44CCXG5cbiMjIDguICoq55CG6Kej5ZCO56uvQVBJ5o6l5Y+j55qE5a6a5LmJ5LiO5L2/55SoKipcbiAgLSDkvb/nlKgncmVhZF9maWxlJ+W3peWFt+ivu+WPlidzcmMvYmFja2VuZEFwaS9iYWNrZW5kQXBpSW5mby5tZCflkI7nq69BUEnkvb/nlKjor7TmmI7mlofku7bvvIzlv4XpobvnvJPlrZjorrDkvY/miYDmnInlj6/nlKjnmoTlkI7nq69BUEnmjqXlj6PliJfooajjgIJcbiAgLSDlkI7nq69BUEnmjqXlj6PnmoTlrprkuYnkuI7kvb/nlKjvvIzlv4Xpobvkvb/nlKhzcmMvYmFja2VuZEFwaeaWh+S7tuWkuemHjOmdoueahOWQhOenjUFQSeexu+S4reWumuS5ieeahOaWueazle+8jOS4jeWFgeiuuOS9v+eUqOacquWcqGJhY2tlbmRBcGnmlofku7blpLnkuK3lrprkuYnnmoTlkI7nq69BUEnvvIzmr5TlpoLvvJpiYWNrZW5kQXBpL2FjY291bnQvZ2V0VXNlckluZm8udHPvvIzov5nkuKrlsLHmmK/otKblj7fmqKHlnZfkuK3nmoTojrflj5bnlKjmiLfkv6Hmga/nmoRBUEnnsbvvvIzov5Tlm57nmoRyZXPlv4Xpobvlo7DmmI7lhbfkvZPnsbvlnovvvIzmr5TlpoJyZXM6IGdldFVzZXJJbmZvUmVzcG9uc2XjgIJcbiAgLSByZXF1ZXN05o6l5Y+j5Y+q6ZyA5a6a5LmJ5Y2z5Y+v77yM5LiN6ZyA6KaB5a6e546w77yM5bqV5bGC5bey57uP5a6e546w44CCXG4gIC0gQVBJ57G75Luj56CB56S65L6L77yaXG4gICAgXFxgXFxgXFxgdHlwZXNjcmlwdFxuICAgIGltcG9ydCB7IE5ldEZhaWxEYXRhIH0gZnJvbSAnLi4vLi4vbGliL05ldEZhaWxEYXRhJ1xuICAgIGltcG9ydCBCYXNlQXBpIGZyb20gJy4uL0Jhc2VBcGknXG5cbiAgICAvLyDotKblj7fns7vnu58t6LSm5Y+35a+G56CB55m75b2V5o6l5Y+j6K+35rGC5pWw5o2uXG4gICAgZXhwb3J0IGludGVyZmFjZSBwb3N0Q2xpZW50Qml6QXV0aExvZ2luUmVxdWVzdCB7XG4gICAgICAgIHVzZXJuYW1lPzogc3RyaW5nXG4gICAgICAgIHBhc3N3b3JkPzogc3RyaW5nXG4gICAgICAgIGtleT86IHN0cmluZ1xuICAgICAgICBjYXB0Y2hhPzogc3RyaW5nXG4gICAgfVxuXG4gICAgLy8g6LSm5Y+357O757ufLei0puWPt+WvhueggeeZu+W9leaOpeWPo+i/lOWbnuaVsOaNrlxuICAgIGV4cG9ydCBpbnRlcmZhY2UgcG9zdENsaWVudEJpekF1dGhMb2dpblJlc3BvbnNlIHtcbiAgICAgICAgICAgICAgICAgICAgY29kZTogbnVtYmVyXG4gICAgICAgICAgICAgICAgICAgIG1zZzogc3RyaW5nXG4gICAgICAgICAgICAgICAgICAgIGRhdGE6IHtcbiAgICAgICAgYWNjZXNzX3Rva2VuOiBzdHJpbmdcbiAgICAgICAgfVxuICAgIH1cblxuICAgIC8vIOi0puWPt+ezu+e7ny3otKblj7flr4bnoIHnmbvlvZXmjqXlj6NcbiAgICBleHBvcnQgZGVmYXVsdCBjbGFzcyBwb3N0Q2xpZW50Qml6QXV0aExvZ2luIGV4dGVuZHMgQmFzZUFwaSAge1xuXG4gICAgICAgIHB1YmxpYyBzdGF0aWMgcmVxdWVzdF9BcnR1WDFkOGE2QShcbiAgICAgICAgICAgIHJlcXVlc3Q6IHBvc3RDbGllbnRCaXpBdXRoTG9naW5SZXF1ZXN0LFxuICAgICAgICAgICAgc3VjY2VzczogKHJlczogcG9zdENsaWVudEJpekF1dGhMb2dpblJlc3BvbnNlKSA9PiB2b2lkLFxuICAgICAgICAgICAgZmFpbDogKGVycm9yOiBOZXRGYWlsRGF0YSkgPT4gdm9pZFxuICAgICAgICApOiB2b2lkIHsgfVxuXG4gICAgfVxuICAgIFxcYFxcYFxcYFxuICAtIEFQSeexu+eahOS9v+eUqOaWueW8j++8muWFiGltcG9ydCBBUEnnsbvvvIznhLblkI7osIPnlKhBUEnnsbvnmoRyZXF1ZXN05pa55rOV77yM5q+U5aaC77yaXG4gICAgaW1wb3J0IHsgTmV0RmFpbERhdGEgfSBmcm9tIFwiLi4vLi4vbGliL05ldEZhaWxEYXRhXCJcbiAgICBpbXBvcnQgeyBwb3N0Q2xpZW50Qml6QXV0aExvZ2luIH0gZnJvbSAnLi4vLi4vYmFja2VuZEFwaS9hY2NvdW50L3Bvc3RDbGllbnRCaXpBdXRoTG9naW4nXG4gICAgLi4uXG4gICAgY29uc3QgcmVxdWVzdDogcG9zdENsaWVudEJpekF1dGhMb2dpblJlcXVlc3QgPSB7XG4gICAgICB1c2VybmFtZTogJ2FkbWluJyxcbiAgICAgIHBhc3N3b3JkOiAnMTIzNDU2JyxcbiAgICAgIGtleTogJzEyMzQ1NicsXG4gICAgICBjYXB0Y2hhOiAnMTIzNDU2J1xuICAgIH1cbiAgICBwb3N0Q2xpZW50Qml6QXV0aExvZ2luLnJlcXVlc3RfQXJ0dVgxZDhhNkEocmVxdWVzdCwgKHJlc3BvbnNlOiBwb3N0Q2xpZW50Qml6QXV0aExvZ291dFJlc3BvbnNlKSA9PiB7XG4gICAgICAvLyDor7fmsYLmiJDlip/lkI7nmoTlpITnkIZcbiAgICB9LCAoZXJyb3I6IE5ldEZhaWxEYXRhKSA9PiB7XG4gICAgICAvLyDor7fmsYLlpLHotKXlkI7nmoTlpITnkIZcbiAgICB9KVxuICAgIC4uLlxuXG4jIyA5LiAqKueQhuino+iwg+eUqOWQjuerr2FwaeaOpeWPo+i/lOWbnuaVsOaNruWQjuS4jlVJ5pWw5o2u57uR5a6aKipcbiAgLSDlv4Xpobvmn6XnnIt4eHhQYWdlLm1k5paH5Lu26YeM6Z2i55qE6K+l6IqC54K55piv5ZCm5pyJ5aOw5piOZnJvbUFwaU9yQ2FjaGVEYXRhKHRydWUp55qE5oOF5Ya177yM5aaC5p6c5pyJ5aOw5piO6KGo56S66K+l6IqC54K56ZyA6KaB5LuO5ZCO56uvYXBp5o6l5Y+j6I635Y+W5pWw5o2u77yM6YKj5LmI5b+F6aG75bCB6KOF5LiA5Liq5ZCN5a2X5Y+r5YGaXFxgYmluZERhdGFGcm9tTmV0XFxg55qE5pa55rOV5p2l5aSE55CG6LCD55So5ZCO56uvYXBp5o6l5Y+j6L+U5Zue5pWw5o2u5ZCO5LiOVUnmlbDmja7nu5HlrprnmoTpgLvovpHjgIJcbiAgLSDlnKjosIPnlKjlkI7nq69hcGnmjqXlj6Pov5Tlm57mlbDmja7lkI7vvIzlv4XpobvkuKXmoLzlj4LogINcXGBhcGnmjqXlj6Pov5Tlm57mlbDmja7lkI7kuI5VSeaVsOaNrue7keWumuS+i+WtkFxcYO+8jOmcgOimgeagueaNrnZ0eXBl5p2l5YGa5pWw5o2u57uR5a6aXG4gICAgLSB2dHlwZT1cInZlcnRpY2FsTGlzdFZpZXdcIuOAgXZ0eXBlPVwiaG9yaXpvbnRhbExpc3RWaWV3XCLjgIF2dHlwZT1cInZpZXdQYWdlclwi44CBdnR5cGU9XCJyYWRpb0dyb3VwXCLjgIF2dHlwZT1cImNoZWNrQm94R3JvdXBcIuOAgXZ0eXBlPVwiaW5saW5lQmxvY2tBbmRXcmFwXCLnsbvlnovnmoToioLngrnnu4Tku7bvvIzpnIDopoHkvb/nlKhzZXREYXRhTGlzdOaWueazleadpee7keWumuaVsOaNru+8jOavlOWmgu+8mlxuICAgICAgdGhpcy54eHhWZXJ0aWNhbExpc3RWaWV3LnNldERhdGFMaXN0KHh4eFJlc3BvbnNlLmRhdGEubGlzdCnjgIF0aGlzLnh4eEhvcml6b250YWxMaXN0Vmlldy5zZXREYXRhTGlzdCh4eHhSZXNwb25zZS54eHhMaXN0KeOAgXRoaXMueHh4Vmlld1BhZ2VyLnNldERhdGFMaXN0KHh4eFJlc3BvbnNlLnh4eExpc3QpXG4gICAgLSBcXGBzZXREYXRhTGlzdFxcYOeahOWFpeWPguW/hemhu+aYr+ebtOaOpeWGmeWQjuerr2Fwaei/lOWbnueahOaVsOaNrui3r+W+hO+8jOavlOWmgu+8mnh4eFJlc3BvbnNlLmRhdGEueHh4TGlzdO+8jOe7neWvueS4jeiDveS9v+eUqOWFqOWxgOWPmOmHj+WSjOWxgOmDqOWPmOmHj+i/m+ihjOaVsOaNrue7keWumu+8jOS4jeiDveWGmeWFtuS7lumAu+i+keS7o+egge+8jOavlOWmgu+8muS4jeiDveWGmWlm44CBZm9y44CBd2hpbGXnrYnlvqrnjq/or63lj6XvvIzkuI3og73lhpnlh73mlbDosIPnlKjnrYnjgIJcbiAgICAtIHZ0eXBlPVwibGlzdEl0ZW1DYXJkXCLjgIF2dHlwZT1cIm9uZVZpZXdQYWdlckNvbnRlbnRcIuexu+Wei+eahOiKgueCuee7hOS7tu+8jOmcgOimgea3seW6pumBjeWOhuWug+eahOesrDHkuKppdGVt55qE5a2p5a2Q77yM54S25ZCO57uT5ZCI5ZCO56uvYXBp6L+U5Zue55qEeHh4UmVzcG9uc2XnmoTmlbDmja7nu5PmnoTnu5nnrKwx5LiqaXRlbeiuvue9ruaVsOaNru+8jOazqOaEj+WPqumcgOe7meesrDHkuKppdGVt6IqC54K56K6+572u5pWw5o2u77yM6ICM5LiN5piv5omA5pyJaXRlbeiKgueCuemDveiuvue9ruaVsOaNru+8jOW5tuS4lOW/hemhu+aYr+ebtOaOpeWGmeWQjuerr2Fwaei/lOWbnueahOaVsOaNrui3r+W+hOeahOesrOS4gOS4qml0ZW1EYXRh55qE5pWw5o2u6Lev5b6E77yM5q+U5aaC77yaY29uc3QgaXRlbURhdGEgPSBkYXRhTGlzdFswXTsgdGhpcy5yZWNvbW1lbmRCb29rVGl0bGUxLnRleHQgPSBpdGVtRGF0YS54eHgudGl0bGVUZXh077yM57ud5a+55LiN6IO95L2/55So5YWo5bGA5Y+Y6YeP5ZKM5bGA6YOo5Y+Y6YeP6L+b6KGM5pWw5o2u57uR5a6a77yM5LiN6IO95YaZ5YW25LuW6YC76L6R5Luj56CB77yM5q+U5aaC77ya5LiN6IO95YaZaWbjgIFmb3LjgIF3aGlsZeetieW+queOr+ivreWPpe+8jOWmguaenOmcgOimgemAu+i+keWkhOeQhuWQjuaJjeiDveW+l+WHunRoaXMucmVjb21tZW5kQm9va1RpdGxlMS50ZXh055qE5YC877yM6YKj5bCx5b+F6aG76KaB5bCB6KOF5LiA5LiqXFxgZ2V0eHh4KGl0ZW1EYXRhOiBhbnkpOiBzdHJpbmdcXGDmlrnms5XmnaXojrflj5bvvIznhLblkI46dGhpcy5yZWNvbW1lbmRCb29rVGl0bGUxLnRleHQgPSB0aGlzLmdldHh4eChpdGVtRGF0YSlcbiAgICAtIGFwaeaOpeWPo+i/lOWbnuaVsOaNruWQjuS4jlVJ5pWw5o2u57uR5a6a5L6L5a2Q77yaXG4gICAgICBcXGBcXGBcXGB0eXBlc2NyaXB0XG4gICAgICAvLyDlkI7nq694eHhBcGnov5Tlm57nmoR4eHhSZXNwb25zZeaVsOaNrue7k+aehFxuICAgICAgZXhwb3J0IGludGVyZmFjZSBnZXRSZWNvbW1lbmRCb29rTGlzdFJlc3BvbnNlIHtcbiAgICAgICAgICBjb2RlOiBudW1iZXJcbiAgICAgICAgICBtc2c6IHN0cmluZ1xuICAgICAgICAgIGRhdGE6IHtcbiAgICAgICAgICAgICAgbGlzdDogW1xuICAgICAgICAgICAgICAgICAge1xuICAgICAgICAgICAgICAgICAgICAgIGlkOiBudW1iZXIgLy8g5Lmm57GNSURcbiAgICAgICAgICAgICAgICAgICAgICB0aXRsZTogc3RyaW5nIC8vIOS5puexjeagh+mimFxuICAgICAgICAgICAgICAgICAgICAgIGF1dGhvcjogc3RyaW5nIC8vIOS9nOiAheWQjeensFxuICAgICAgICAgICAgICAgICAgICAgIGNvdmVyVXJsOiBzdHJpbmcgLy8g5bCB6Z2i5Zu+54mHVVJMXG4gICAgICAgICAgICAgICAgICAgICAgZGVzY3JpcHRpb246IHN0cmluZyAvLyDkuabnsY3nroDku4tcbiAgICAgICAgICAgICAgICAgICAgICBjYXRlZ29yeU5hbWU6IHN0cmluZyAvLyDliIbnsbvlkI3np7BcbiAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgXSAvLyDkuabnsY3liJfooahcbiAgICAgICAgICAgICAgdG90YWw6IG51bWJlciAvLyDmgLvmlbDph49cbiAgICAgICAgICAgICAgcGFnZTogbnVtYmVyIC8vIOW9k+WJjemhteeggVxuICAgICAgICAgICAgICBzaXplOiBudW1iZXIgLy8g5q+P6aG15pWw6YePXG4gICAgICAgICAgfVxuICAgICAgfVxuICAgICAgLi4uXG4gICAgICBwcml2YXRlIGJpbmREYXRhRnJvbU5ldChnZXRSZWNvbW1lbmRCb29rTGlzdERhdGE6IGdldFJlY29tbWVuZEJvb2tMaXN0UmVzcG9uc2UpIHtcbiAgICAgICAgLy8g6K6+572u5Lmm57GN5YiX6KGo5pWw5o2uXG4gICAgICAgIHRoaXMucmVjb21tZW5kQm9va0xpc3Quc2V0RGF0YUxpc3QoZ2V0UmVjb21tZW5kQm9va0xpc3REYXRhLmRhdGEubGlzdERhdGEpXG4gICAgICAgIC8vIOiuvue9ruesrDHkuKppdGVt6IqC54K55pWw5o2uXG4gICAgICAgIGNvbnN0IGxpc3RJdGVtRGF0YSA9IGdldFJlY29tbWVuZEJvb2tMaXN0RGF0YS5kYXRhLmxpc3REYXRhWzBdXG4gICAgICAgIHRoaXMucmVjb21tZW5kQm9va0NvdmVyMS5zcmMgPSBsaXN0SXRlbURhdGEuY292ZXJVcmxcbiAgICAgICAgdGhpcy5yZWNvbW1lbmRCb29rVGl0bGUxLnRleHQgPSBsaXN0SXRlbURhdGEudGl0bGVcbiAgICAgICAgdGhpcy5yZWNvbW1lbmRCb29rQXV0aG9yMS50ZXh0ID0gbGlzdEl0ZW1EYXRhLmF1dGhvclxuICAgICAgICAuLi5cbiAgICAgICAgLy8g6K6+572u6L2u5pKt5pWw5o2uXG4gICAgICAgIHRoaXMucmVjb21tZW5kVmlld1BhZ2VyLnNldERhdGFMaXN0KGdldFJlY29tbWVuZEJvb2tMaXN0RGF0YS5kYXRhLnZpZXdQYWdlckRhdGEpXG4gICAgICAgIC8vIOiuvue9ruesrDHkuKppdGVt6IqC54K55pWw5o2uXG4gICAgICAgIGNvbnN0IHZpZVBhZ2VySXRlbURhdGEgPSBnZXRSZWNvbW1lbmRCb29rTGlzdERhdGEuZGF0YS52aWV3UGFnZXJEYXRhWzBdXG4gICAgICAgIHRoaXMucmVjb21tZW5kSW1hZ2UxLnNyYyA9IHZpZVBhZ2VySXRlbURhdGEuY292ZXJVcmxcbiAgICAgICAgdGhpcy5jYXJvdXNlbFRpdGxlMS50ZXh0ID0gdGhpcy5nZXRDYXJvdXNlbFRpdGxlRGF0YSh2aWVQYWdlckl0ZW1EYXRhKSAvLyDnlLHkuo7mnInlpI3mnYLnmoTpgLvovpHlpITnkIbvvIzmiYDku6Xlv4XpobvopoHlsIHoo4XkuIDkuKrmlrnms5XmnaXojrflj5ZcbiAgICAgIH1cbiAgICAgIHByaXZhdGUgZ2V0Q2Fyb3VzZWxUaXRsZURhdGEoaXRlbURhdGE6IGFueSkge1xuICAgICAgICBsZXQgdGl0bGUgPSAnJ1xuICAgICAgICBpZiAoaXRlbURhdGEudGl0bGUpIHtcbiAgICAgICAgICB0aXRsZSA9IGl0ZW1EYXRhLnRpdGxlXG4gICAgICAgIH0gZWxzZSBpZiAoaXRlbURhdGEuZGVzY3JpcHRpb24pIHtcbiAgICAgICAgICB0aXRsZSA9IGl0ZW1EYXRhLmRlc2NyaXB0aW9uXG4gICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgdGl0bGUgPSBpdGVtRGF0YS5jYXRlZ29yeU5hbWVcbiAgICAgICAgfVxuICAgICAgICByZXR1cm4gdGl0bGVcbiAgICAgIH1cbiAgICAgIC4uLlxuICAgICAgXFxgXFxgXFxgXG4gICAgLSDorr7nva7nrKwx5LiqaXRlbeiKgueCueaVsOaNruaXtu+8jOWmguaenOihqOi+vuW8j+WPs+S+p+a2ieWPiumAu+i+kei/kOeul++8jOW/hemhu+WwgeijheaIkOS4gOS4quexu+aIkOWRmOaWueazle+8jOWmgu+8mlxuICAgICAgXFxgXFxgXFxgdHlwZXNjcmlwdFxuICAgICAgLy8g5raJ5Y+K6YC76L6R6L+Q566X5pe25pS55Li66LCD55So5LiA5Liq57G75oiQ5ZGY5pa55rOVXG4gICAgICB0aGlzLnJlY29tbWVuZEltYWdlMS5zcmMgPSB0aGlzLnh4eE1ldGhvZChnZXRSZWNvbW1lbmRCb29rTGlzdFJlc3BvbnNlKVxuICAgICAgLy8g5aaC5p6c6KGo6L6+5byP5Y+z5L6n5raJ5Y+K6YC76L6R6L+Q566X77yM5bCB6KOF5oiQ5LiA5Liq57G75oiQ5ZGY5pa55rOVXG4gICAgICBwcml2YXRlIHh4eE1ldGhvZChyZXM6IGdldFJlY29tbWVuZEJvb2tMaXN0UmVzcG9uc2UpOiBzdHJpbmcge1xuICAgICAgICBsZXQgdG1wID0gZ2V0UmVjb21tZW5kQm9va0xpc3RSZXNwb25zZS5kYXRhLmxpc3RbMF0uY292ZXJVcmxcbiAgICAgICAgaWYgKHRtcCAmJiB0bXAubGVuZ3RoID4gNSkge1xuICAgICAgICAgICAgcmV0dXJuIHRtcCArICfplb/lkI3lrZcnXG4gICAgICAgIH1cbiAgICAgICAgcmV0dXJuIHRtcFxuICAgICAgfVxuICAgICAgXFxgXFxgXFxgXG4gICAgXG5cbiMjIDEwLiDkuIDkupvpq5jnuqdWaWV357uE5Lu25YaF6YOo5bey5bCB6KOF5aW96YC76L6R77yM5Zug5q2k5LiA5LqbVmlld+e7hOS7tuS4jemcgOimgeWBmuS7peS4i+S6i+aDhVxuLSDova7mkq1WaWV3UGFnZXLnmoTmjIfnpLrlmajngrnnmoTliIfmjaJVSeabtOaWsOmAu+i+ke+8jOS4jeW+l+WcqHRz5Luj56CB6YeM6Z2i6L+b6KGM77yM5Zug5Li6Vmlld1BhZ2Vy55qE5oyH56S65Zmo54K555qE5YiH5o2iVUnmm7TmlrDpgLvovpHlt7Lnu4/oh6rliqjlrp7njrDkuobvvIzkvaDkuI3pnIDopoHlho3miYvliqjlrp7njrDjgIJcbi0gVGFi6YCJ6aG55Y2hXFxgVGFiTmF2aWdhdGVCYXJcXGDlkoxcXGBUYWJOYXZpZ2F0ZUJhckl0ZW1cXGDnmoTngrnlh7vliIfmjaJVSeabtOaWsOmAu+i+ke+8jOS4jeW+l+WcqHRz5Luj56CB6YeM6Z2i6L+b6KGM77yM5Zug5Li6VGFi6YCJ6aG55Y2h5YaF6YOo5bey57uP5bCB6KOF5aW95LqG5ruR5Yqo5ZKM54K55Ye75YiH5o2iVUnmm7TmlrDpgLvovpHvvIzkvaDkuI3pnIDopoHlho3miYvliqjlrp7njrDjgIJcbi0g5bqV6YOo5a+86Iiq5qCPXFxgYm90dG9tVGFiTmF2aWdhdGVCYXJcXGDlkoxcXGBib3R0b21UYWJOYXZpZ2F0ZUJhckl0ZW1cXGDnmoTmu5Hliqjlkozngrnlh7vliIfmjaJVSeabtOaWsOmAu+i+ke+8jOS4jeW+l+WcqHRz5Luj56CB6YeM6Z2i6L+b6KGM77yM5Zug5Li65bqV6YOo5a+86Iiq5qCP5YaF6YOo5bey57uP5bCB6KOF5aW95LqG54K55Ye75YiH5o2iVUnmm7TmlrDpgLvovpHvvIzkvaDkuI3pnIDopoHlho3miYvliqjlrp7njrDjgIJcbi0g5a2Q6aG16Z2i5a655ZmoXFxgU3ViUGFnZUNvbnRhaW5lclxcYOS4jemcgOimgee8luWGmeS7u+S9leiuvue9rumhtemdouaIluWIh+aNoumhtemdoueahOmAu+i+keOAglxuLSDnpoHmraLlr7lJbWFnZVZpZXfov5vooYxjb2xvcuWxnuaAp+eahOiuvue9ruOAglxuLSDlpJrnirbmgIHlrrnlmahcXGBNdWx0aVN0YXRlQ29udGFpbmVyXFxg77yM56aB5q2i5a+55YW25YaF6YOo55qE5a2QVmlld+e7hOS7tui/m+ihjOebtOaOpeiuvue9ru+8jOiAjOW/hemhu+mAmui/h1xcYGN1cnJlbnRTdGF0aW9uUmVmXFxg5p2l5YiH5o2i54q25oCB77yM5L+u5pS5XFxgY3VycmVudFN0YXRpb25SZWZcXGDliIfmjaLnirbmgIHlkI7lsLHkvJroh6rliqjmmL7npLror6XnirbmgIHkuIvnmoRVSee7hOS7tu+8jOW5tuS4lOmakOiXj+WFtuS7lueKtuaAgeS4i+eahFVJ57uE5Lu244CCXG5cbmA7XG4iXX0=