936 lines
66 KiB
JavaScript
936 lines
66 KiB
JavaScript
"use strict";
|
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
exports.Instance = exports.Discourser = exports.escape = exports.logger = void 0;
|
|
const constants_1 = require("../../constants");
|
|
const osr_cli_commons_1 = require("@plastichub/osr-cli-commons");
|
|
const debug_1 = require("@plastichub/core/debug");
|
|
exports.logger = (0, debug_1.logger)(constants_1.MODULE_NAME);
|
|
const write_1 = require("@plastichub/fs/write");
|
|
const write_2 = require("@plastichub/fs/write");
|
|
const exists_1 = require("@plastichub/fs/exists");
|
|
const native_promise_pool_1 = require("native-promise-pool");
|
|
const path_1 = require("path");
|
|
const axios_1 = require("axios");
|
|
const fs = require("fs");
|
|
const path = require("path");
|
|
const FormData = require("form-data");
|
|
const https = require('https');
|
|
const request = require("request");
|
|
const fetch = require('isomorphic-unfetch');
|
|
const escape = (path) => path.replace(/[^\w]/g, '-').replace(/-+/, '-');
|
|
exports.escape = escape;
|
|
const generate_password_1 = require("generate-password");
|
|
/**
|
|
* Discourser is an API Client for the [Discourse API](https://docs.discourse.org)
|
|
* It special features are:
|
|
* - TypeScript Types
|
|
* - Respecting Rate Limits
|
|
* - Optional Heavy Caching
|
|
* - Post Modifiers (can be used for global find and replace across all posts on the forum)
|
|
*/
|
|
class Discourser {
|
|
/**
|
|
* Construct our Discourser instance
|
|
* See {@link IDiscourserConfig} for available configuration.
|
|
*/
|
|
constructor(config) {
|
|
this.host = config.host;
|
|
this.key = config.key;
|
|
this.username = config.username;
|
|
this.cache = config.cache;
|
|
this.useCache = config.useCache;
|
|
this.dry = config.dry || false;
|
|
this.pool = new native_promise_pool_1.default(config.rateLimitConcurrency || 60);
|
|
}
|
|
/** Get the URL of a topic */
|
|
getTopicURL(topic) {
|
|
if (typeof topic === 'number') {
|
|
return `${this.host}/t/${topic}`;
|
|
}
|
|
return `${this.host}/t/${topic.slug}/${topic.id}`;
|
|
}
|
|
/** Fetch a discourse API URL, with rate limit concurrency and optional caching */
|
|
async fetch({ url, useCache, request }) {
|
|
// check if cache is enabled
|
|
useCache = false;
|
|
const cache = this.cache &&
|
|
((request === null || request === void 0 ? void 0 : request.method) || 'get') === 'get' &&
|
|
(0, path_1.join)(this.cache, (0, exports.escape)(url));
|
|
// check if we should and can read from cache
|
|
if (cache &&
|
|
this.useCache !== false &&
|
|
useCache !== false &&
|
|
((0, exists_1.sync)(cache))) {
|
|
const result = (0, write_2.sync)(cache, 'json');
|
|
return result;
|
|
}
|
|
// fetch
|
|
const result = await this.pool.open(() => this._fetch({ url, request }));
|
|
// write to cache if cache is enabled
|
|
if (cache) {
|
|
(0, write_1.sync)(cache, result);
|
|
}
|
|
// return the result
|
|
return result;
|
|
}
|
|
/** Fetch a discourse API URL, with rate limit retries */
|
|
async _post(url, data) {
|
|
var _a;
|
|
const opts = {
|
|
headers: {
|
|
'Api-Key': this.key,
|
|
'Api-Username': this.username,
|
|
},
|
|
};
|
|
let d = data;
|
|
const res = await axios_1.default.post(url, d, {
|
|
headers: opts.headers
|
|
});
|
|
// fetch text then parse as json, so that when errors occur we can output what it was
|
|
// rather than being stuck with errors like these:
|
|
// FetchError: invalid json response body at https://discuss.bevry.me/posts/507.json reason: Unexpected token < in JSON at position 0
|
|
const text = await res.data;
|
|
// check if there are errors
|
|
if (typeof data.errors !== 'undefined') {
|
|
// check if the error is a rate limit
|
|
const wait = (_a = data.extras) === null || _a === void 0 ? void 0 : _a.wait_seconds;
|
|
if (wait != null) {
|
|
// if it was, try later
|
|
// return await retry(wait + 1)
|
|
}
|
|
// otherwise fail
|
|
// logger.debug({ data, url, opts })
|
|
return Promise.reject(new Error(`fetch of [${url}] received failed response:\n${data}`));
|
|
}
|
|
return text;
|
|
}
|
|
/** Fetch a discourse API URL, with rate limit retries */
|
|
async _fetch({ url, request }) {
|
|
var _a;
|
|
const httpsAgent = new https.Agent({
|
|
rejectUnauthorized: false,
|
|
});
|
|
const opts = {
|
|
...request,
|
|
headers: {
|
|
Accept: 'application/json',
|
|
'Content-Type': 'application/json',
|
|
'Api-Key': this.key,
|
|
'Api-Username': this.username,
|
|
...request === null || request === void 0 ? void 0 : request.headers
|
|
},
|
|
rejectUnauthorized: false,
|
|
agent: httpsAgent
|
|
};
|
|
const retry = (seconds) => {
|
|
return new Promise((resolve, reject) => {
|
|
setTimeout(() => this._fetch({ url, request })
|
|
.then(resolve)
|
|
.catch(reject), (seconds || 60) * 1000);
|
|
});
|
|
};
|
|
try {
|
|
const res = await fetch(url, opts);
|
|
// fetch text then parse as json, so that when errors occur we can output what it was
|
|
// rather than being stuck with errors like these:
|
|
// FetchError: invalid json response body at https://discuss.bevry.me/posts/507.json reason: Unexpected token < in JSON at position 0
|
|
const text = await res.text();
|
|
let data;
|
|
try {
|
|
data = JSON.parse(text);
|
|
}
|
|
catch (err) {
|
|
// check if it was cloudflare reporting that the server has been hit too hard
|
|
if (text.includes('Please try again in a few minutes')) {
|
|
exports.logger.debug('server has stalled, trying again in a minute');
|
|
return await retry(60);
|
|
}
|
|
// otherwise log the error page and die
|
|
// logger.debug({ text, url , opts })
|
|
return Promise.reject(exports.logger.error(text, url, opts, err) &&
|
|
new Error(`fetch of [${url}] received invalid response:\n${text}`));
|
|
}
|
|
// check if there are errors
|
|
if (typeof data.errors !== 'undefined') {
|
|
// check if the error is a rate limit
|
|
const wait = (_a = data.extras) === null || _a === void 0 ? void 0 : _a.wait_seconds;
|
|
if (wait != null) {
|
|
// if it was, try later
|
|
return await retry(wait + 1);
|
|
}
|
|
// otherwise fail
|
|
// logger.debug({ data, url, opts })
|
|
return Promise.reject(new Error(`fetch of [${url}] received failed response:\n${data}`));
|
|
}
|
|
return data;
|
|
}
|
|
catch (err) {
|
|
// logger.debug({ err, url, opts })
|
|
return Promise.reject(exports.logger.error(`fetch of [${url}] failed with error`, err));
|
|
}
|
|
}
|
|
// =================================
|
|
// Search
|
|
/**
|
|
* API Helper for {@link .search}
|
|
* https://docs.discourse.org/#tag/Search/operation/search
|
|
*/
|
|
async search(query, params = '', opts = {}) {
|
|
let url = `${this.host}/search.json?q=${encodeURIComponent(query)} ${encodeURIComponent(params)}`;
|
|
return await this.fetch({ url, ...opts });
|
|
}
|
|
// =================================
|
|
// Tags
|
|
/**
|
|
* API Helper for {@link .getTags}
|
|
*/
|
|
async getTagsResponse(opts = {}) {
|
|
const url = `${this.host}/tags.json`;
|
|
return await this.fetch({ url, ...opts });
|
|
}
|
|
/**
|
|
* Fetch the whole information, for all categories of the forum
|
|
*/
|
|
async getTags(opts = {}) {
|
|
const response = await this.getTagsResponse(opts);
|
|
const tags = response.tags;
|
|
return tags;
|
|
}
|
|
async createTag(name) {
|
|
const url = `${this.host}/tag_groups.json`;
|
|
try {
|
|
return await this._post(url, {
|
|
name
|
|
});
|
|
}
|
|
catch (error) {
|
|
debugger;
|
|
}
|
|
}
|
|
// =================================
|
|
// CATEGORIES
|
|
/**
|
|
* API Helper for {@link .getCategories}
|
|
*/
|
|
async getCategoriesResponse(opts = {}) {
|
|
const url = `${this.host}/categories.json` +
|
|
(opts.include_subcategories ? '?include_subcategories=true' : '');
|
|
return await this.fetch({ url, ...opts });
|
|
}
|
|
/**
|
|
* Fetch the whole information, for all categories of the forum
|
|
*/
|
|
async getCategories(opts = {}) {
|
|
const response = await this.getCategoriesResponse(opts);
|
|
const categories = response.category_list.categories;
|
|
return categories;
|
|
}
|
|
/**
|
|
* API Helper for {@link .getTopicItemsOfCategory}
|
|
* Discourse does not provide an API for fetching category information for a specific category.
|
|
* Instead, all that it provides is a way of getting the topics for a specific category.
|
|
*/
|
|
async getCategoryResponse(categoryID, opts = {}) {
|
|
const url = `${this.host}/c/${categoryID}.json` +
|
|
(opts.page !== 0 ? `?page=${opts.page}` : '');
|
|
return await this.fetch({ url, ...opts });
|
|
}
|
|
// =================================
|
|
// TOPICS
|
|
/**
|
|
* Fetch the partial information, for all topics of a specific category
|
|
*/
|
|
async getTopicItemsOfCategory(categoryID, opts = {}) {
|
|
// prepare and fetch
|
|
let page = opts.page || 0;
|
|
const response = await this.getCategoryResponse(categoryID, {
|
|
...opts,
|
|
page,
|
|
});
|
|
let topics = response.topic_list.topics;
|
|
// fetch the next page
|
|
if (topics.length === response.topic_list.per_page) {
|
|
page += 1;
|
|
const more = await this.getTopicItemsOfCategory(categoryID, {
|
|
...opts,
|
|
page,
|
|
});
|
|
topics.push(...more);
|
|
}
|
|
// if we are the first page, then output count as we now have all of them
|
|
if (page === 0) {
|
|
const ids = topics.map((i) => i.id);
|
|
}
|
|
topics = topics.filter((t) => t.visible === true);
|
|
return topics;
|
|
}
|
|
/**
|
|
* Fetch the partial information, for all topics of specific categoires
|
|
*/
|
|
async getTopicItemsOfCategories(categoryIDs, opts = {}) {
|
|
// fetch topic items for specific categories
|
|
try {
|
|
const topicsOfCategories = await Promise.all(categoryIDs.map((id) => this.getTopicItemsOfCategory(id, opts)));
|
|
// @ts-ignore
|
|
return topicsOfCategories.flat();
|
|
}
|
|
catch (error) {
|
|
exports.logger.error(error);
|
|
}
|
|
}
|
|
/**
|
|
* Fetch the partial information, for all topics of the forum
|
|
*/
|
|
async getTopicItems(opts = {}) {
|
|
const categories = await this.getCategories();
|
|
const categoryIDs = categories.map((i) => i.id);
|
|
return this.getTopicItemsOfCategories(categoryIDs, opts);
|
|
}
|
|
/**
|
|
* Fetch the whole information, for a specific topic of the forum
|
|
*/
|
|
getTopic(id, opts = {}) {
|
|
const url = `${this.host}/t/${id}.json`;
|
|
return this.fetch({ url, ...opts });
|
|
}
|
|
/**
|
|
* Fetch the whole information, for all topics, or specific topics, of the forum
|
|
*/
|
|
async getTopics(topicIDs, opts = {}) {
|
|
// if no topics, use all topics
|
|
if (!topicIDs) {
|
|
const topics = await this.getTopicItems(opts);
|
|
topicIDs = topics.map((i) => i.id);
|
|
}
|
|
// fetch whole topics
|
|
return Promise.all(topicIDs.map((id) => this.getTopic(id, opts)));
|
|
}
|
|
async updateTopicVisibility(topicID, listed = true, visible = 'visible') {
|
|
const url = `${this.host}/t/${topicID}/status`;
|
|
let ret = await fetch(url, {
|
|
"headers": {
|
|
"content-type": "application/x-www-form-urlencoded; charset=UTF-8",
|
|
'Api-Key': this.key,
|
|
'Api-Username': this.username
|
|
},
|
|
"body": `status=${visible}&enabled=${listed}`,
|
|
"method": "PUT"
|
|
});
|
|
return ret;
|
|
}
|
|
async updateTopicTimestamp(topicID, timestamp, token) {
|
|
let time;
|
|
if (typeof timestamp === 'number') {
|
|
time = timestamp;
|
|
}
|
|
else if (typeof timestamp === 'number') {
|
|
time = Number(timestamp);
|
|
}
|
|
else if (timestamp instanceof Date) {
|
|
// ms to seconds
|
|
time = timestamp.getTime() / 1000;
|
|
}
|
|
else {
|
|
return Promise.reject(new Error('invalid timestamp format'));
|
|
}
|
|
const url = `${this.host}/t/${topicID}/change-timestamp`;
|
|
let ret = await fetch(url, {
|
|
"headers": {
|
|
"accept-language": "en-GB,en-US;q=0.9,en;q=0.8,de;q=0.7,es;q=0.6,fr;q=0.5",
|
|
"content-type": "application/x-www-form-urlencoded; charset=UTF-8",
|
|
"x-csrf-token": token,
|
|
"x-requested-with": "XMLHttpRequest",
|
|
"cookie": "_bypass_cache=true; _ga_MBZGKNMDWC=GS1.1.1685892974.20.1.1685893082.0.0.0; _ga_P4SR15V1XR=GS1.1.1687459978.51.1.1687460537.0.0.0; _ga_H8W78Y3P2B=GS1.1.1687604701.23.0.1687604701.0.0.0; _ga=GA1.1.401826746.1678337758; _t=xQ05qW5JFxLM9Pq0lIwG6ez74Z1q2OLpak0DzRx8VdFYE5eI3oJXhLURPrdm2zIcHmYcBj9q%2BKdHhGz5N6j9mXitYzcMwkXHL3K9GYKdO4gJ8tBQimpmd1HFaRhB9Ml9aJ8WviqQWDZDOYwEUKFcWw3wbAalfQtbdIbUSX8gH9sG6DLFU3HiEg7tWModRy%2BoFrTm6QOalDuajRW3nBazau%2FiY8ZCVm2g30Y10CBDfqJHL1ztV8XM4kEIeulLNTzGVtSb7uuO1OcjZRSb--aDgCPEalq7SIpnH5--HWCNf5readaeij3oDl9b9w%3D%3D; __profilin=p%3Dt%2Ca%3Deef38e031f99cc8240f3518e1b8811cf; _forum_session=RkEWuzKI1QXBYCnP6KRamD8mweZ3h9%2B6G%2Fi23gAWUgy8gp8FuiyQD5lKU0Fbx3FzzaM4SiQcvnIiEAnb5P4OYjlvstqwWlfRp%2B9is7iX8StwYGiYsncHQ5LrzSbV3y9mR7sj%2F8JZ8evQOe2ZZjZB3iEkppsGrmyFrw5PsUgSphRTZm70SKIw96JrW17yK4hhLqtk%2BaQPgNu4oJl42YfXAr%2FCBldcBUKXFeHppYmv61WECV0531hCo7GcA4t06B9QpSr%2BeoiM1Ok9tpQrAlZf36Ka4lVCTyXXu3SNvbtvfd9tZMJCWDYv69jdMsezuOaEP870pk9qYPaL4x6nAY5EXO3u9usCggqQ1B1EydCK9uMy7ZUCIo9wONw7QOIgEQ%3D%3D--GMqYSb2H7xXVDky6--R9gVciBqwC0IL9LefywrFw%3D%3D; _ga_GVR8PEPG6C=GS1.1.1687710574.106.1.1687710599.0.0.0",
|
|
'Api-Key': this.key,
|
|
'Api-Username': this.username
|
|
},
|
|
"body": `timestamp=${time}`,
|
|
"method": "PUT"
|
|
});
|
|
if (ret && ret.status === 200) {
|
|
return true;
|
|
}
|
|
return;
|
|
/*
|
|
let data = new FormData();
|
|
data.append('timestamp', time);
|
|
|
|
try {
|
|
let ret = await axios.put(url, data, {
|
|
headers: {
|
|
//'Accept-Language': 'en-US,en;q=0.8',
|
|
//'content-type': 'application/x-www-form-urlencoded; charset=UTF-8',
|
|
//'Content-Type': `multipart/form-data; boundary=${data._boundary}`,
|
|
'Api-Key': this.key,
|
|
'Api-Username': this.username
|
|
}
|
|
});
|
|
debugger
|
|
} catch (error) {
|
|
debugger
|
|
}
|
|
|
|
|
|
return
|
|
*/
|
|
/*
|
|
var options = {
|
|
method: 'PUT',
|
|
url: url,
|
|
headers:
|
|
{
|
|
'content-type': 'application/x-www-form-urlencoded; charset=UTF-8',
|
|
'Api-Key': this.key,
|
|
'Api-Username': this.username
|
|
},
|
|
body: `timestamp=${time}`
|
|
};
|
|
|
|
new Promise((resolve, reject) => {
|
|
request(options, function (error, response, body) {
|
|
if (error) {
|
|
throw new Error(error);
|
|
} else {
|
|
resolve(body);
|
|
}
|
|
});
|
|
});
|
|
*/
|
|
/*
|
|
var options = {
|
|
method: 'POST',
|
|
url: url,
|
|
headers:
|
|
{
|
|
'content-type': 'application/x-www-form-urlencoded; charset=UTF-8',
|
|
'Api-Key': this.key,
|
|
'Api-Username': this.username
|
|
},
|
|
body
|
|
}
|
|
|
|
|
|
return new Promise((resolve, reject) => {
|
|
request(options, function (error, response, body) {
|
|
if (error) {
|
|
throw new Error(error);
|
|
} else {
|
|
resolve(body);
|
|
}
|
|
});
|
|
});
|
|
*/
|
|
// prepare the request
|
|
const request = {
|
|
timestamp: time,
|
|
};
|
|
// send the update
|
|
exports.logger.debug('updating', topicID, 'topic timestamp with', request);
|
|
//const url = `${this.host}/t/${topicID}/change-timestamp`
|
|
const response = await this.fetch({
|
|
url,
|
|
request: {
|
|
method: 'put',
|
|
body: `timestamp=${time}`,
|
|
headers: {
|
|
'Api-Key': this.key,
|
|
'Api-Username': this.username,
|
|
"content-type": "application/x-www-form-urlencoded; charset=UTF-8"
|
|
}
|
|
}
|
|
});
|
|
// check it
|
|
if (response.success !== 'OK') {
|
|
return Promise.reject(new Error(`timestamp update of topic ${topicID} failed:\n${{
|
|
url,
|
|
request,
|
|
response,
|
|
}}`));
|
|
}
|
|
return response;
|
|
}
|
|
// =================================
|
|
// POSTS
|
|
/**
|
|
* API Helper for {@link .getPostItemsOfTopic}
|
|
*/
|
|
async getPostItemsOfTopicResponse(topicID, opts = {}) {
|
|
const url = `${this.host}/t/${topicID}/posts.json`;
|
|
const response = await this.fetch({ url, ...opts });
|
|
return response;
|
|
}
|
|
async _createUser(name, email, pUserGroup) {
|
|
const pwd = (0, generate_password_1.generate)({
|
|
length: 10,
|
|
numbers: true
|
|
});
|
|
let user = await this.createUser({
|
|
"name": name,
|
|
"email": email,
|
|
"password": pwd,
|
|
"username": name,
|
|
"active": true,
|
|
"approved": true,
|
|
"user_fields[1]": true
|
|
});
|
|
if (user && user.user_id) {
|
|
await this.updateGroup(name, pUserGroup);
|
|
return { ...user, password: pwd };
|
|
}
|
|
else {
|
|
if (user && user.message && user.message == 'Username must be unique\nPrimary email has already been taken') {
|
|
return null;
|
|
}
|
|
else if (user && user.message && user.message == 'Your account is activated and ready to use.') {
|
|
if (user.user_id) {
|
|
return { ...user, password: pwd };
|
|
}
|
|
return null;
|
|
}
|
|
else {
|
|
console.log('cant create user ' + name, user);
|
|
}
|
|
return null;
|
|
}
|
|
}
|
|
async getUsers(page) {
|
|
const url = `${this.host}/admin/users/list/active.json?page=` + page;
|
|
const response = await this.fetch({ url });
|
|
return response;
|
|
}
|
|
async getUser(id) {
|
|
const url = `${this.host}/admin/users/${id}.json`;
|
|
const response = await this.fetch({ url });
|
|
return response;
|
|
}
|
|
/**
|
|
* Fetch the partial information, for all posts of a specific topic
|
|
*/
|
|
async getPostItemsOfTopic(topicID, opts = {}) {
|
|
const response = await this.getPostItemsOfTopicResponse(topicID, opts);
|
|
const posts = response.post_stream.posts;
|
|
const ids = posts.map((i) => i.id);
|
|
return posts;
|
|
}
|
|
/**
|
|
* Fetch the partial information, for all posts of specific topics
|
|
*/
|
|
async getPostItemsOfTopics(topicIDs, opts = {}) {
|
|
// fetch post items for specific topics
|
|
const postItemsOfTopics = await Promise.all(topicIDs.map((id) => this.getPostItemsOfTopic(id, opts)));
|
|
// @ts-ignore
|
|
return postItemsOfTopics.flat();
|
|
}
|
|
/**
|
|
* Fetch the partial information, for all posts of a specific category
|
|
*/
|
|
async getPostItemsOfCategory(categoryID, opts = {}) {
|
|
// fetch topics for the category
|
|
const topics = await this.getTopicItemsOfCategory(categoryID, opts);
|
|
const topicIDs = topics.map((i) => i.id);
|
|
// fetch
|
|
const posts = await this.getPostItemsOfTopics(topicIDs);
|
|
const ids = posts.map((i) => i.id);
|
|
return posts;
|
|
}
|
|
/**
|
|
* Fetch the partial information, for all posts of specific categories
|
|
*/
|
|
async getPostItemsOfCategories(categoryIDs, opts = {}) {
|
|
// fetch post items for specific categories
|
|
const postItemsOfCategories = await Promise.all(categoryIDs.map((id) => this.getPostItemsOfCategory(id, opts)));
|
|
// @ts-ignore
|
|
return postItemsOfCategories.flat();
|
|
}
|
|
/**
|
|
* Fetch the partial information, for all posts of the forum
|
|
*/
|
|
async getPostItems(opts = {}) {
|
|
const categories = await this.getCategories();
|
|
const categoryIDs = categories.map((i) => i.id);
|
|
return this.getPostItemsOfCategories(categoryIDs, opts);
|
|
}
|
|
/**
|
|
* Fetch the whole information, for a specific post of the forum
|
|
*/
|
|
getPost(id, opts = {}) {
|
|
const url = `${this.host}/posts/${id}.json`;
|
|
return this.fetch({ url, ...opts });
|
|
}
|
|
async createReply(postId, raw, category) {
|
|
const url = `${this.host}/posts.json`;
|
|
let data = new FormData();
|
|
data.append('topic_id', '' + postId);
|
|
data.append('raw', raw);
|
|
data.append('nested_post', 'true');
|
|
data.append('category', category);
|
|
var options = {
|
|
method: 'POST',
|
|
url: url,
|
|
headers: {
|
|
'content-type': 'application/x-www-form-urlencoded; charset=UTF-8',
|
|
'Api-Key': this.key,
|
|
'Api-Username': this.username
|
|
},
|
|
"body": `raw=${raw}&unlist_topic=false&category=${category}&topic_id=${postId}&is_warning=false&archetype=regular&featured_link=&shared_draft=false&nested_post=true`,
|
|
};
|
|
return new Promise((resolve, reject) => {
|
|
request(options, function (error, response, body) {
|
|
if (error) {
|
|
throw new Error(error);
|
|
}
|
|
else {
|
|
resolve(body);
|
|
}
|
|
});
|
|
});
|
|
}
|
|
async changeOwner(postId, topicId, owner) {
|
|
const url = `${this.host}/t/${topicId}/change-owner.json`;
|
|
var options = {
|
|
method: 'POST',
|
|
url: url,
|
|
headers: {
|
|
'content-type': 'application/x-www-form-urlencoded; charset=UTF-8',
|
|
'Api-Key': this.key,
|
|
'Api-Username': this.username
|
|
},
|
|
body: `post_ids%5B%5D=${postId}&username=${owner}`
|
|
};
|
|
return new Promise((resolve, reject) => {
|
|
request(options, function (error, response, body) {
|
|
if (error) {
|
|
throw new Error(error);
|
|
}
|
|
else {
|
|
resolve(body);
|
|
}
|
|
});
|
|
});
|
|
}
|
|
async createUser(data) {
|
|
const url = `${this.host}/users`;
|
|
return await this._post(url, data);
|
|
}
|
|
async getUserByUsername(username) {
|
|
const url = `${this.host}/u/${username}.json`;
|
|
const response = await this.fetch({ url });
|
|
return response.user;
|
|
}
|
|
async setUserAvatar(user_name, upload_id) {
|
|
// fetch whole posts
|
|
const url = `${this.host}/u/${user_name}/preferences/avatar/pick.json`;
|
|
return await axios_1.default.put(url, {
|
|
upload_id,
|
|
username: user_name,
|
|
type: 'uploaded'
|
|
}, {
|
|
headers: {
|
|
'accept': 'application/json',
|
|
'Accept-Language': 'en-US,en;q=0.8',
|
|
'Api-Key': this.key,
|
|
'Api-Username': this.username
|
|
}
|
|
});
|
|
}
|
|
async updateUser(user_name, args) {
|
|
const url = `${this.host}/u/${user_name}.json`;
|
|
return await axios_1.default.put(url, {
|
|
...args
|
|
}, {
|
|
headers: {
|
|
'accept': 'application/json',
|
|
'Accept-Language': 'en-US,en;q=0.8',
|
|
'Api-Key': this.key,
|
|
'Api-Username': this.username
|
|
}
|
|
});
|
|
}
|
|
async updateGroup(user_name, group) {
|
|
// fetch whole posts
|
|
const url = `${this.host}/groups/${group}/members.json`;
|
|
const t = axios_1.default.put(url, {
|
|
usernames: user_name,
|
|
notify_users: false
|
|
}, {
|
|
headers: {
|
|
'accept': 'application/json',
|
|
'Accept-Language': 'en-US,en;q=0.8',
|
|
'Api-Key': this.key,
|
|
'Api-Username': this.username
|
|
}
|
|
});
|
|
t.then((d) => {
|
|
}).catch((e) => {
|
|
//debugger;
|
|
});
|
|
return t;
|
|
}
|
|
async upload(userId, file) {
|
|
// fetch whole posts
|
|
const url = `${this.host}/uploads.json`;
|
|
let data = new FormData();
|
|
const fsData = path.parse(file);
|
|
data.append('file', file, fsData.base);
|
|
data.append('user_id', userId);
|
|
data.append('upload_type', 'avatar');
|
|
data.append('file', fs.createReadStream(file));
|
|
return await axios_1.default.post(url, data, {
|
|
headers: {
|
|
'accept': 'application/json',
|
|
'Accept-Language': 'en-US,en;q=0.8',
|
|
'Content-Type': `multipart/form-data; boundary=${data._boundary}`,
|
|
'Api-Key': this.key,
|
|
'Api-Username': this.username
|
|
}
|
|
});
|
|
}
|
|
async uploadFile(userId, file) {
|
|
// fetch whole posts
|
|
const url = `${this.host}/uploads.json`;
|
|
let data = new FormData();
|
|
const fsData = path.parse(file);
|
|
data.append('file', file, fsData.base);
|
|
data.append('user_id', userId);
|
|
data.append('upload_type', 'composer');
|
|
data.append('file', fs.createReadStream(file));
|
|
return await axios_1.default.post(url, data, {
|
|
headers: {
|
|
'accept': 'application/json',
|
|
'Accept-Language': 'en-US,en;q=0.8',
|
|
'Content-Type': `multipart/form-data; boundary=${data._boundary}`,
|
|
'Api-Key': this.key,
|
|
'Api-Username': this.username
|
|
}
|
|
});
|
|
}
|
|
// =================================
|
|
// POSTS: UPDATING
|
|
/**
|
|
* Fetch the whole information, for all posts, or specific posts, of the forum
|
|
*/
|
|
async getPosts(postIDs, opts = {}) {
|
|
// if no posts, use all
|
|
if (!postIDs) {
|
|
const posts = await this.getPostItems(opts);
|
|
postIDs = posts.map((i) => i.id);
|
|
}
|
|
// fetch whole posts
|
|
return await Promise.all(postIDs.map((id) => this.getPost(id, opts)));
|
|
}
|
|
async createPost(title, raw, category) {
|
|
// fetch whole posts
|
|
const url = `${this.host}/posts`;
|
|
return new Promise((resolve) => {
|
|
return this._post(url, { raw, title, category }).then((d) => {
|
|
resolve(d);
|
|
}).catch((e) => {
|
|
resolve(e.response.data);
|
|
});
|
|
});
|
|
}
|
|
/**
|
|
* Update a post with the content
|
|
* @param postID the identifier of the post to update
|
|
* @param content the new raw content for the post
|
|
* @param reason the reason, if provided, for modifying the post
|
|
* @param old if the old raw content is provided, then the update verified that you are working with the latest post content before applying the update
|
|
*/
|
|
async updatePost(postID, content, reason = 'api update', old) {
|
|
// prepare the request
|
|
const data = {
|
|
post: {
|
|
raw: content,
|
|
edit_reason: reason,
|
|
},
|
|
};
|
|
if (old) {
|
|
data.post.raw_old = old;
|
|
}
|
|
// send the update
|
|
const url = `${this.host}/posts/${postID}.json`;
|
|
const response = await this.fetch({
|
|
url,
|
|
request: {
|
|
method: 'put',
|
|
body: JSON.stringify(data),
|
|
},
|
|
});
|
|
// return the response
|
|
return response.post;
|
|
}
|
|
/**
|
|
* Update post meta
|
|
*/
|
|
async updateTopic(postId, category_id, title, tags) {
|
|
const data = {
|
|
title,
|
|
tags: tags || [],
|
|
featuredLink: null,
|
|
category_id
|
|
};
|
|
const url = `${this.host}/t/${postId}.json`;
|
|
const response = await this.fetch({
|
|
url,
|
|
request: {
|
|
method: 'put',
|
|
body: JSON.stringify(data),
|
|
},
|
|
});
|
|
return response.basic_topic;
|
|
}
|
|
async rebakePost(postID) {
|
|
const url = `${this.host}/posts/${postID}/rebake`;
|
|
exports.logger.debug('rebaking', postID);
|
|
const response = await this.fetch({
|
|
url,
|
|
request: {
|
|
method: 'put'
|
|
}
|
|
});
|
|
exports.logger.debug('rebaked', postID);
|
|
return response;
|
|
}
|
|
/**
|
|
* Modify a post using a modifier
|
|
*/
|
|
async modifyPost(post, modifier) {
|
|
// check if we received a post item, instead of a post response
|
|
if (post.raw == null) {
|
|
post = await this.getPost(post.id);
|
|
}
|
|
// check
|
|
if (!post.raw) {
|
|
return Promise.resolve(null);
|
|
}
|
|
// replace
|
|
const { result, reason } = modifier(post);
|
|
if (result === post.raw) {
|
|
// if (post.cooked) {
|
|
// const { result, reason } = modifier(post.cooked)
|
|
// if (result !== post.cooked) {
|
|
// logger.debug(
|
|
// 'replace did have an effect on cooked post',
|
|
// postID,
|
|
// 'so rebaking it'
|
|
// )
|
|
// return await this.rebakePost(postID)
|
|
// }
|
|
// }
|
|
return Promise.resolve(null);
|
|
}
|
|
// dry
|
|
if (this.dry) {
|
|
return Promise.resolve({
|
|
...post,
|
|
result,
|
|
reason,
|
|
});
|
|
}
|
|
// update
|
|
try {
|
|
return await this.updatePost(post.id, result, reason, post.raw);
|
|
}
|
|
catch (err) {
|
|
if (err.message.includes('That post was edited by another user and your changes can no longer be saved.')) {
|
|
return this.modifyPost(await this.getPost(post.id, { useCache: false }), modifier);
|
|
}
|
|
exports.logger.error(err);
|
|
return Promise.reject(`modifying post ${post.id} failed`);
|
|
}
|
|
}
|
|
/**
|
|
* Modify a post (via its post identifier) using a modifier
|
|
*/
|
|
async modifyPostID(post, modifier) {
|
|
return this.modifyPost(await this.getPost(post), modifier);
|
|
}
|
|
/**
|
|
* Modify a post (via fetching the whole post from the partial post identifier) using a modifier
|
|
*/
|
|
async modifyPostItem(post, modifier) {
|
|
return this.modifyPost(await this.getPost(post.id), modifier);
|
|
}
|
|
/**
|
|
* Run the post modifier on all specified posts
|
|
*/
|
|
async modifyPosts(posts, modifier) {
|
|
const updates = await Promise.all(posts.map((post) => this.modifyPost(post, modifier)));
|
|
const updated = updates.filter((i) => i);
|
|
return updated;
|
|
}
|
|
// =================================
|
|
// THREADS
|
|
/**
|
|
* Fetch the partial information, for all posts of a specific topic
|
|
* Alias of {@link .getPostItemsOfTopic}.
|
|
*/
|
|
async getThread(topicID, opts = {}) {
|
|
const topic = await this.getTopic(topicID, opts);
|
|
const [post, ...replies] = await this.getPostItemsOfTopic(topicID, opts);
|
|
return {
|
|
topic,
|
|
post,
|
|
replies,
|
|
};
|
|
}
|
|
/**
|
|
* Fetch the partial information, for all posts of specific topics, grouped by topic
|
|
*/
|
|
async getThreads(topicIDs, opts = {}) {
|
|
return await Promise.all(topicIDs.map((id) => this.getThread(id, opts)));
|
|
}
|
|
/**
|
|
* Fetch the partial information, for all posts of specific categories, grouped by topic
|
|
*/
|
|
async getThreadsOfCategory(categoryID, opts = {}) {
|
|
// fetch topics for the category
|
|
const topics = await this.getTopicItemsOfCategory(categoryID, opts);
|
|
const topicIDs = topics.map((i) => i.id);
|
|
// return threads
|
|
return await this.getThreads(topicIDs);
|
|
}
|
|
/**
|
|
* Fetch the partial information, for all posts of specific categories, grouped by category, then topic
|
|
*/
|
|
async getThreadsOfCategories(categoryIDs, opts = {}) {
|
|
return await Promise.all(categoryIDs.map((id) => this.getThreadsOfCategory(id, opts)));
|
|
}
|
|
async updateUserProfile(userId, prefs) {
|
|
const url = `${this.host}/u/${userId}.json`;
|
|
let data = new FormData();
|
|
data.append('bio_raw', prefs.bio_raw);
|
|
prefs.location && data.append('location', prefs.location);
|
|
prefs.website && data.append('website', prefs.website);
|
|
return await axios_1.default.put(url, data, {
|
|
headers: {
|
|
'accept': 'application/json',
|
|
'Accept-Language': 'en-US,en;q=0.8',
|
|
"content-type": "application/x-www-form-urlencoded; charset=UTF-8",
|
|
'Api-Key': this.key,
|
|
'Api-Username': this.username
|
|
}
|
|
});
|
|
}
|
|
}
|
|
exports.Discourser = Discourser;
|
|
const Instance = (config, key = 'discourse_admin') => {
|
|
return new Discourser(config || (0, osr_cli_commons_1.CONFIG_DEFAULT)()[key]);
|
|
/*
|
|
|
|
d.getTopicItemsOfCategories([cat]).then(posts => {
|
|
//console.log('posts', posts)
|
|
let content = "<ul>"
|
|
posts = posts.map((p) => {
|
|
const url = `${config.discourse.host}/t/${p.id}`;
|
|
const title = `${p.fancy_title}`;
|
|
return `<li><a href="${url}">${title}</a></li>`;
|
|
}).join('\n');
|
|
content += posts + "</ul>";
|
|
resolve(content);
|
|
|
|
});
|
|
*/
|
|
};
|
|
exports.Instance = Instance;
|
|
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvbGliL2Rpc2NvdXJzZS9pbmRleC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7QUFBQSwrQ0FBNkM7QUFFN0MsaUVBQTREO0FBSTVELGtEQUEwRDtBQUU3QyxRQUFBLE1BQU0sR0FBUSxJQUFBLGNBQU8sRUFBQyx1QkFBVyxDQUFDLENBQUE7QUFFL0MsZ0RBQW9EO0FBQ3BELGdEQUFtRDtBQUNuRCxrREFBc0Q7QUFFdEQsNkRBQTZDO0FBRzdDLCtCQUEyQjtBQUMzQixpQ0FBeUI7QUFDekIseUJBQXdCO0FBQ3hCLDZCQUE0QjtBQUM1QixzQ0FBcUM7QUFFckMsTUFBTSxLQUFLLEdBQUcsT0FBTyxDQUFDLE9BQU8sQ0FBQyxDQUFBO0FBQzlCLE1BQU0sT0FBTyxHQUFHLE9BQU8sQ0FBQyxTQUFTLENBQUMsQ0FBQTtBQUNsQyxNQUFNLEtBQUssR0FBRyxPQUFPLENBQUMsb0JBQW9CLENBQUMsQ0FBQTtBQUVwQyxNQUFNLE1BQU0sR0FBRyxDQUFDLElBQVksRUFBRSxFQUFFLENBQ3RDLElBQUksQ0FBQyxPQUFPLENBQUMsUUFBUSxFQUFFLEdBQUcsQ0FBQyxDQUFDLE9BQU8sQ0FBQyxJQUFJLEVBQUUsR0FBRyxDQUFDLENBQUE7QUFEbEMsUUFBQSxNQUFNLFVBQzRCO0FBRS9DLHlEQUE0QztBQXNDNUM7Ozs7Ozs7R0FPRztBQUNILE1BQWEsVUFBVTtJQVN0Qjs7O09BR0c7SUFDSCxZQUFZLE1BQXlCO1FBQ3BDLElBQUksQ0FBQyxJQUFJLEdBQUcsTUFBTSxDQUFDLElBQUksQ0FBQTtRQUN2QixJQUFJLENBQUMsR0FBRyxHQUFHLE1BQU0sQ0FBQyxHQUFHLENBQUE7UUFDckIsSUFBSSxDQUFDLFFBQVEsR0FBRyxNQUFNLENBQUMsUUFBUSxDQUFBO1FBQy9CLElBQUksQ0FBQyxLQUFLLEdBQUcsTUFBTSxDQUFDLEtBQUssQ0FBQTtRQUN6QixJQUFJLENBQUMsUUFBUSxHQUFHLE1BQU0sQ0FBQyxRQUFRLENBQUE7UUFDL0IsSUFBSSxDQUFDLEdBQUcsR0FBRyxNQUFNLENBQUMsR0FBRyxJQUFJLEtBQUssQ0FBQTtRQUM5QixJQUFJLENBQUMsSUFBSSxHQUFHLElBQUksNkJBQVcsQ0FBQyxNQUFNLENBQUMsb0JBQW9CLElBQUksRUFBRSxDQUFDLENBQUE7SUFDL0QsQ0FBQztJQUVELDZCQUE2QjtJQUM3QixXQUFXLENBQUMsS0FBeUM7UUFDcEQsSUFBSSxPQUFPLEtBQUssS0FBSyxRQUFRLEVBQUU7WUFDOUIsT0FBTyxHQUFHLElBQUksQ0FBQyxJQUFJLE1BQU0sS0FBSyxFQUFFLENBQUE7U0FDaEM7UUFDRCxPQUFPLEdBQUcsSUFBSSxDQUFDLElBQUksTUFBTSxLQUFLLENBQUMsSUFBSSxJQUFJLEtBQUssQ0FBQyxFQUFFLEVBQUUsQ0FBQTtJQUNsRCxDQUFDO0lBRUQsa0ZBQWtGO0lBQ2xGLEtBQUssQ0FBQyxLQUFLLENBQUksRUFBRSxHQUFHLEVBQUUsUUFBUSxFQUFFLE9BQU8sRUFBZTtRQUNyRCw0QkFBNEI7UUFDNUIsUUFBUSxHQUFHLEtBQUssQ0FBQTtRQUNoQixNQUFNLEtBQUssR0FDVixJQUFJLENBQUMsS0FBSztZQUNWLENBQUMsQ0FBQSxPQUFPLGFBQVAsT0FBTyx1QkFBUCxPQUFPLENBQUUsTUFBTSxLQUFJLEtBQUssQ0FBQyxLQUFLLEtBQUs7WUFDcEMsSUFBQSxXQUFJLEVBQUMsSUFBSSxDQUFDLEtBQUssRUFBRSxJQUFBLGNBQU0sRUFBQyxHQUFHLENBQUMsQ0FBQyxDQUFBO1FBQzlCLDZDQUE2QztRQUM3QyxJQUNDLEtBQUs7WUFDTCxJQUFJLENBQUMsUUFBUSxLQUFLLEtBQUs7WUFDdkIsUUFBUSxLQUFLLEtBQUs7WUFDbEIsQ0FBQyxJQUFBLGFBQU0sRUFBQyxLQUFLLENBQUMsQ0FBQyxFQUNkO1lBQ0QsTUFBTSxNQUFNLEdBQUcsSUFBQSxZQUFJLEVBQUMsS0FBSyxFQUFFLE1BQU0sQ0FBQyxDQUFBO1lBQ2xDLE9BQVEsTUFBdUIsQ0FBQTtTQUMvQjtRQUNELFFBQVE7UUFDUixNQUFNLE1BQU0sR0FBRyxNQUFNLElBQUksQ0FBQyxJQUFJLENBQUMsSUFBSSxDQUFDLEdBQUcsRUFBRSxDQUFDLElBQUksQ0FBQyxNQUFNLENBQUksRUFBRSxHQUFHLEVBQUUsT0FBTyxFQUFFLENBQUMsQ0FBQyxDQUFBO1FBQzNFLHFDQUFxQztRQUNyQyxJQUFJLEtBQUssRUFBRTtZQUNWLElBQUEsWUFBSyxFQUFDLEtBQUssRUFBRSxNQUFhLENBQUMsQ0FBQTtTQUMzQjtRQUNELG9CQUFvQjtRQUNwQixPQUFPLE1BQU0sQ0FBQTtJQUNkLENBQUM7SUFHRCx5REFBeUQ7SUFDakQsS0FBSyxDQUFDLEtBQUssQ0FBSSxHQUFHLEVBQUUsSUFBSTs7UUFDL0IsTUFBTSxJQUFJLEdBQWdCO1lBQ3pCLE9BQU8sRUFBRTtnQkFDUixTQUFTLEVBQUUsSUFBSSxDQUFDLEdBQUc7Z0JBQ25CLGNBQWMsRUFBRSxJQUFJLENBQUMsUUFBUTthQUM3QjtTQUNELENBQUE7UUFDRCxJQUFJLENBQUMsR0FBRyxJQUFJLENBQUM7UUFDYixNQUFNLEdBQUcsR0FBRyxNQUFNLGVBQUssQ0FBQyxJQUFJLENBQUMsR0FBRyxFQUFFLENBQUMsRUFBRTtZQUNwQyxPQUFPLEVBQUUsSUFBSSxDQUFDLE9BQWM7U0FDNUIsQ0FBQyxDQUFDO1FBRUgscUZBQXFGO1FBQ3JGLGtEQUFrRDtRQUNsRCxxSUFBcUk7UUFDckksTUFBTSxJQUFJLEdBQUcsTUFBTSxHQUFHLENBQUMsSUFBSSxDQUFDO1FBQzVCLDRCQUE0QjtRQUM1QixJQUFJLE9BQVEsSUFBWSxDQUFDLE1BQU0sS0FBSyxXQUFXLEVBQUU7WUFDaEQscUNBQXFDO1lBQ3JDLE1BQU0sSUFBSSxHQUFXLE1BQUMsSUFBWSxDQUFDLE1BQU0sMENBQUUsWUFBWSxDQUFBO1lBQ3ZELElBQUksSUFBSSxJQUFJLElBQUksRUFBRTtnQkFDakIsdUJBQXVCO2dCQUN2QiwrQkFBK0I7YUFDL0I7WUFFRCxpQkFBaUI7WUFDakIsb0NBQW9DO1lBQ3BDLE9BQU8sT0FBTyxDQUFDLE1BQU0sQ0FDcEIsSUFBSSxLQUFLLENBQ1IsYUFBYSxHQUFHLGdDQUFnQyxJQUFJLEVBQUUsQ0FDdEQsQ0FDRCxDQUFBO1NBQ0Q7UUFDRCxPQUFPLElBQUksQ0FBQztJQUNiLENBQUM7SUFJRCx5REFBeUQ7SUFDakQsS0FBSyxDQUFDLE1BQU0sQ0FBSSxFQUFFLEdBQUcsRUFBRSxPQUFPLEVBQWU7O1FBRXBELE1BQU0sVUFBVSxHQUFHLElBQUksS0FBSyxDQUFDLEtBQUssQ0FBQztZQUNsQyxrQkFBa0IsRUFBRSxLQUFLO1NBQ3pCLENBQUMsQ0FBQztRQUNILE1BQU0sSUFBSSxHQUFHO1lBQ1osR0FBRyxPQUFPO1lBQ1YsT0FBTyxFQUFFO2dCQUNSLE1BQU0sRUFBRSxrQkFBa0I7Z0JBQzFCLGNBQWMsRUFBRSxrQkFBa0I7Z0JBQ2xDLFNBQVMsRUFBRSxJQUFJLENBQUMsR0FBRztnQkFDbkIsY0FBYyxFQUFFLElBQUksQ0FBQyxRQUFRO2dCQUM3QixHQUFHLE9BQU8sYUFBUCxPQUFPLHVCQUFQLE9BQU8sQ0FBRSxPQUFPO2FBQ25CO1lBQ0Qsa0JBQWtCLEVBQUUsS0FBSztZQUN6QixLQUFLLEVBQUUsVUFBVTtTQUNqQixDQUFBO1FBRUQsTUFBTSxLQUFLLEdBQUcsQ0FBQyxPQUFlLEVBQUUsRUFBRTtZQUNqQyxPQUFPLElBQUksT0FBTyxDQUFJLENBQUMsT0FBTyxFQUFFLE1BQU0sRUFBRSxFQUFFO2dCQUN6QyxVQUFVLENBQ1QsR0FBRyxFQUFFLENBQ0osSUFBSSxDQUFDLE1BQU0sQ0FBSSxFQUFFLEdBQUcsRUFBRSxPQUFPLEVBQUUsQ0FBQztxQkFDOUIsSUFBSSxDQUFDLE9BQU8sQ0FBQztxQkFDYixLQUFLLENBQUMsTUFBTSxDQUFDLEVBQ2hCLENBQUMsT0FBTyxJQUFJLEVBQUUsQ0FBQyxHQUFHLElBQUksQ0FDdEIsQ0FBQTtZQUNGLENBQUMsQ0FBQyxDQUFBO1FBQ0gsQ0FBQyxDQUFBO1FBRUQsSUFBSTtZQUNILE1BQU0sR0FBRyxHQUFHLE1BQU0sS0FBSyxDQUFDLEdBQUcsRUFBRSxJQUFJLENBQUMsQ0FBQztZQUNuQyxxRkFBcUY7WUFDckYsa0RBQWtEO1lBQ2xELHFJQUFxSTtZQUNySSxNQUFNLElBQUksR0FBRyxNQUFNLEdBQUcsQ0FBQyxJQUFJLEVBQUUsQ0FBQTtZQUM3QixJQUFJLElBQU8sQ0FBQTtZQUVYLElBQUk7Z0JBQ0gsSUFBSSxHQUFHLElBQUksQ0FBQyxLQUFLLENBQUMsSUFBSSxDQUFNLENBQUE7YUFDNUI7WUFBQyxPQUFPLEdBQUcsRUFBRTtnQkFDYiw2RUFBNkU7Z0JBQzdFLElBQUksSUFBSSxDQUFDLFFBQVEsQ0FBQyxtQ0FBbUMsQ0FBQyxFQUFFO29CQUN2RCxjQUFNLENBQUMsS0FBSyxDQUFDLDhDQUE4QyxDQUFDLENBQUE7b0JBQzVELE9BQU8sTUFBTSxLQUFLLENBQUMsRUFBRSxDQUFDLENBQUE7aUJBQ3RCO2dCQUNELHVDQUF1QztnQkFDdkMscUNBQXFDO2dCQUNyQyxPQUFPLE9BQU8sQ0FBQyxNQUFNLENBQ3BCLGNBQU0sQ0FBQyxLQUFLLENBQUMsSUFBSSxFQUFFLEdBQUcsRUFBRSxJQUFJLEVBQUUsR0FBRyxDQUFDO29CQUNsQyxJQUFJLEtBQUssQ0FBQyxhQUFhLEdBQUcsaUNBQWlDLElBQUksRUFBRSxDQUFDLENBQ2xFLENBQUE7YUFDRDtZQUVELDRCQUE0QjtZQUM1QixJQUFJLE9BQVEsSUFBWSxDQUFDLE1BQU0sS0FBSyxXQUFXLEVBQUU7Z0JBQ2hELHFDQUFxQztnQkFDckMsTUFBTSxJQUFJLEdBQVcsTUFBQyxJQUFZLENBQUMsTUFBTSwwQ0FBRSxZQUFZLENBQUE7Z0JBQ3ZELElBQUksSUFBSSxJQUFJLElBQUksRUFBRTtvQkFDakIsdUJBQXVCO29CQUN2QixPQUFPLE1BQU0sS0FBSyxDQUFDLElBQUksR0FBRyxDQUFDLENBQUMsQ0FBQTtpQkFDNUI7Z0JBRUQsaUJBQWlCO2dCQUNqQixvQ0FBb0M7Z0JBQ3BDLE9BQU8sT0FBTyxDQUFDLE1BQU0sQ0FDcEIsSUFBSSxLQUFLLENBQ1IsYUFBYSxHQUFHLGdDQUFnQyxJQUFJLEVBQUUsQ0FDdEQsQ0FDRCxDQUFBO2FBQ0Q7WUFDRCxPQUFPLElBQUksQ0FBQTtTQUNYO1FBQUMsT0FBTyxHQUFHLEVBQUU7WUFDYixtQ0FBbUM7WUFDbkMsT0FBTyxPQUFPLENBQUMsTUFBTSxDQUNwQixjQUFNLENBQUMsS0FBSyxDQUFDLGFBQWEsR0FBRyxxQkFBcUIsRUFBRSxHQUFHLENBQUMsQ0FDeEQsQ0FBQTtTQUNEO0lBQ0YsQ0FBQztJQUVELG9DQUFvQztJQUNwQyxTQUFTO0lBRVQ7OztPQUdHO0lBQ0ksS0FBSyxDQUFDLE1BQU0sQ0FDbEIsS0FBYSxFQUNiLFNBQWlCLEVBQUUsRUFDbkIsT0FBcUIsRUFBRTtRQUV2QixJQUFJLEdBQUcsR0FBRyxHQUFHLElBQUksQ0FBQyxJQUFJLGtCQUFrQixrQkFBa0IsQ0FBQyxLQUFLLENBQUMsSUFBSSxrQkFBa0IsQ0FBQyxNQUFNLENBQUMsRUFBRSxDQUFDO1FBQ2xHLE9BQU8sTUFBTSxJQUFJLENBQUMsS0FBSyxDQUFnQixFQUFFLEdBQUcsRUFBRSxHQUFHLElBQUksRUFBRSxDQUFDLENBQUE7SUFDekQsQ0FBQztJQUNELG9DQUFvQztJQUNwQyxPQUFPO0lBRVA7O09BRUc7SUFDTyxLQUFLLENBQUMsZUFBZSxDQUM5QixPQUFxQixFQUFFO1FBRXZCLE1BQU0sR0FBRyxHQUFHLEdBQUcsSUFBSSxDQUFDLElBQUksWUFBWSxDQUFBO1FBQ3BDLE9BQU8sTUFBTSxJQUFJLENBQUMsS0FBSyxDQUFlLEVBQUUsR0FBRyxFQUFFLEdBQUcsSUFBSSxFQUFFLENBQUMsQ0FBQTtJQUN4RCxDQUFDO0lBRUQ7O09BRUc7SUFDSCxLQUFLLENBQUMsT0FBTyxDQUFDLE9BQXFCLEVBQUU7UUFDcEMsTUFBTSxRQUFRLEdBQUcsTUFBTSxJQUFJLENBQUMsZUFBZSxDQUFDLElBQUksQ0FBQyxDQUFBO1FBQ2pELE1BQU0sSUFBSSxHQUFHLFFBQVEsQ0FBQyxJQUFJLENBQUE7UUFDMUIsT0FBTyxJQUFJLENBQUE7SUFDWixDQUFDO0lBRUQsS0FBSyxDQUFDLFNBQVMsQ0FBQyxJQUFJO1FBQ25CLE1BQU0sR0FBRyxHQUFHLEdBQUcsSUFBSSxDQUFDLElBQUksa0JBQWtCLENBQUE7UUFDMUMsSUFBSTtZQUNILE9BQU8sTUFBTSxJQUFJLENBQUMsS0FBSyxDQUFDLEdBQUcsRUFBRTtnQkFDNUIsSUFBSTthQUNKLENBQUMsQ0FBQztTQUNIO1FBQUMsT0FBTyxLQUFLLEVBQUU7WUFDZixRQUFRLENBQUE7U0FDUjtJQUVGLENBQUM7SUFFRCxvQ0FBb0M7SUFDcEMsYUFBYTtJQUViOztPQUVHO0lBQ08sS0FBSyxDQUFDLHFCQUFxQixDQUNwQyxPQUFxQixFQUFFO1FBRXZCLE1BQU0sR0FBRyxHQUFHLEdBQUcsSUFBSSxDQUFDLElBQUksa0JBQWtCO1lBQ3pDLENBQUMsSUFBSSxDQUFDLHFCQUFxQixDQUFDLENBQUMsQ0FBQyw2QkFBNkIsQ0FBQyxDQUFDLENBQUMsRUFBRSxDQUFDLENBQUE7UUFDbEUsT0FBTyxNQUFNLElBQUksQ0FBQyxLQUFLLENBQXFCLEVBQUUsR0FBRyxFQUFFLEdBQUcsSUFBSSxFQUFFLENBQUMsQ0FBQTtJQUM5RCxDQUFDO0lBRUQ7O09BRUc7SUFDSCxLQUFLLENBQUMsYUFBYSxDQUFDLE9BQXFCLEVBQUU7UUFDMUMsTUFBTSxRQUFRLEdBQUcsTUFBTSxJQUFJLENBQUMscUJBQXFCLENBQUMsSUFBSSxDQUFDLENBQUE7UUFDdkQsTUFBTSxVQUFVLEdBQUcsUUFBUSxDQUFDLGFBQWEsQ0FBQyxVQUFVLENBQUE7UUFDcEQsT0FBTyxVQUFVLENBQUE7SUFDbEIsQ0FBQztJQUVEOzs7O09BSUc7SUFDTyxLQUFLLENBQUMsbUJBQW1CLENBQ2xDLFVBQWtCLEVBQ2xCLE9BQXFCLEVBQUU7UUFFdkIsTUFBTSxHQUFHLEdBQ1IsR0FBRyxJQUFJLENBQUMsSUFBSSxNQUFNLFVBQVUsT0FBTztZQUNuQyxDQUFDLElBQUksQ0FBQyxJQUFJLEtBQUssQ0FBQyxDQUFDLENBQUMsQ0FBQyxTQUFTLElBQUksQ0FBQyxJQUFJLEVBQUUsQ0FBQyxDQUFDLENBQUMsRUFBRSxDQUFDLENBQUE7UUFDOUMsT0FBTyxNQUFNLElBQUksQ0FBQyxLQUFLLENBQW1CLEVBQUUsR0FBRyxFQUFFLEdBQUcsSUFBSSxFQUFFLENBQUMsQ0FBQTtJQUM1RCxDQUFDO0lBRUQsb0NBQW9DO0lBQ3BDLFNBQVM7SUFFVDs7T0FFRztJQUNILEtBQUssQ0FBQyx1QkFBdUIsQ0FDNUIsVUFBa0IsRUFDbEIsT0FBcUIsRUFBRTtRQUV2QixvQkFBb0I7UUFDcEIsSUFBSSxJQUFJLEdBQUcsSUFBSSxDQUFDLElBQUksSUFBSSxDQUFDLENBQUE7UUFDekIsTUFBTSxRQUFRLEdBQUcsTUFBTSxJQUFJLENBQUMsbUJBQW1CLENBQUMsVUFBVSxFQUFFO1lBQzNELEdBQUcsSUFBSTtZQUNQLElBQUk7U0FDSixDQUFDLENBQUE7UUFFRixJQUFJLE1BQU0sR0FBZ0IsUUFBUSxDQUFDLFVBQVUsQ0FBQyxNQUFNLENBQUE7UUFFcEQsc0JBQXNCO1FBQ3RCLElBQUksTUFBTSxDQUFDLE1BQU0sS0FBSyxRQUFRLENBQUMsVUFBVSxDQUFDLFFBQVEsRUFBRTtZQUNuRCxJQUFJLElBQUksQ0FBQyxDQUFBO1lBQ1QsTUFBTSxJQUFJLEdBQUcsTUFBTSxJQUFJLENBQUMsdUJBQXVCLENBQUMsVUFBVSxFQUFFO2dCQUMzRCxHQUFHLElBQUk7Z0JBQ1AsSUFBSTthQUNKLENBQUMsQ0FBQTtZQUNGLE1BQU0sQ0FBQyxJQUFJLENBQUMsR0FBRyxJQUFJLENBQUMsQ0FBQTtTQUNwQjtRQUVELHlFQUF5RTtRQUN6RSxJQUFJLElBQUksS0FBSyxDQUFDLEVBQUU7WUFDZixNQUFNLEdBQUcsR0FBRyxNQUFNLENBQUMsR0FBRyxDQUFDLENBQUMsQ0FBWSxFQUFFLEVBQUUsQ0FBQyxDQUFDLENBQUMsRUFBRSxDQUFDLENBQUE7U0FDOUM7UUFFRCxNQUFNLEdBQUcsTUFBTSxDQUFDLE1BQU0sQ0FBQyxDQUFDLENBQUMsRUFBRSxFQUFFLENBQUMsQ0FBQyxDQUFDLE9BQU8sS0FBSyxJQUFJLENBQUMsQ0FBQztRQUVsRCxPQUFPLE1BQU0sQ0FBQTtJQUNkLENBQUM7SUFFRDs7T0FFRztJQUNILEtBQUssQ0FBQyx5QkFBeUIsQ0FDOUIsV0FBcUIsRUFDckIsT0FBcUIsRUFBRTtRQUV2Qiw0Q0FBNEM7UUFDNUMsSUFBSTtZQUNILE1BQU0sa0JBQWtCLEdBQUcsTUFBTSxPQUFPLENBQUMsR0FBRyxDQUMzQyxXQUFXLENBQUMsR0FBRyxDQUFDLENBQUMsRUFBRSxFQUFFLEVBQUUsQ0FBQyxJQUFJLENBQUMsdUJBQXVCLENBQUMsRUFBRSxFQUFFLElBQUksQ0FBQyxDQUFDLENBQy9ELENBQUE7WUFDRCxhQUFhO1lBQ2IsT0FBTyxrQkFBa0IsQ0FBQyxJQUFJLEVBQUUsQ0FBQTtTQUNoQztRQUFDLE9BQU8sS0FBSyxFQUFFO1lBQ2YsY0FBTSxDQUFDLEtBQUssQ0FBQyxLQUFLLENBQUMsQ0FBQztTQUNwQjtJQUNGLENBQUM7SUFFRDs7T0FFRztJQUNILEtBQUssQ0FBQyxhQUFhLENBQUMsT0FBcUIsRUFBRTtRQUMxQyxNQUFNLFVBQVUsR0FBRyxNQUFNLElBQUksQ0FBQyxhQUFhLEVBQUUsQ0FBQTtRQUM3QyxNQUFNLFdBQVcsR0FBRyxVQUFVLENBQUMsR0FBRyxDQUFDLENBQUMsQ0FBQyxFQUFFLEVBQUUsQ0FBQyxDQUFDLENBQUMsRUFBRSxDQUFDLENBQUE7UUFDL0MsT0FBTyxJQUFJLENBQUMseUJBQXlCLENBQUMsV0FBVyxFQUFFLElBQUksQ0FBQyxDQUFBO0lBQ3pELENBQUM7SUFFRDs7T0FFRztJQUNILFFBQVEsQ0FBQyxFQUFVLEVBQUUsT0FBcUIsRUFBRTtRQUMzQyxNQUFNLEdBQUcsR0FBRyxHQUFHLElBQUksQ0FBQyxJQUFJLE1BQU0sRUFBRSxPQUFPLENBQUE7UUFDdkMsT0FBTyxJQUFJLENBQUMsS0FBSyxDQUFnQixFQUFFLEdBQUcsRUFBRSxHQUFHLElBQUksRUFBRSxDQUFDLENBQUE7SUFDbkQsQ0FBQztJQUVEOztPQUVHO0lBQ0gsS0FBSyxDQUFDLFNBQVMsQ0FDZCxRQUEwQixFQUMxQixPQUFxQixFQUFFO1FBRXZCLCtCQUErQjtRQUMvQixJQUFJLENBQUMsUUFBUSxFQUFFO1lBQ2QsTUFBTSxNQUFNLEdBQUcsTUFBTSxJQUFJLENBQUMsYUFBYSxDQUFDLElBQUksQ0FBQyxDQUFBO1lBQzdDLFFBQVEsR0FBRyxNQUFNLENBQUMsR0FBRyxDQUFDLENBQUMsQ0FBQyxFQUFFLEVBQUUsQ0FBQyxDQUFDLENBQUMsRUFBRSxDQUFDLENBQUE7U0FDbEM7UUFFRCxxQkFBcUI7UUFDckIsT0FBTyxPQUFPLENBQUMsR0FBRyxDQUFDLFFBQVEsQ0FBQyxHQUFHLENBQUMsQ0FBQyxFQUFFLEVBQUUsRUFBRSxDQUFDLElBQUksQ0FBQyxRQUFRLENBQUMsRUFBRSxFQUFFLElBQUksQ0FBQyxDQUFDLENBQUMsQ0FBQTtJQUNsRSxDQUFDO0lBRUQsS0FBSyxDQUFDLHFCQUFxQixDQUMxQixPQUFlLEVBQ2YsU0FBa0IsSUFBSSxFQUN0QixVQUF1QixTQUFTO1FBRWhDLE1BQU0sR0FBRyxHQUFHLEdBQUcsSUFBSSxDQUFDLElBQUksTUFBTSxPQUFPLFNBQVMsQ0FBQTtRQUM5QyxJQUFJLEdBQUcsR0FBRyxNQUFNLEtBQUssQ0FBQyxHQUFHLEVBQUU7WUFDMUIsU0FBUyxFQUFFO2dCQUNWLGNBQWMsRUFBRSxrREFBa0Q7Z0JBQ2xFLFNBQVMsRUFBRSxJQUFJLENBQUMsR0FBRztnQkFDbkIsY0FBYyxFQUFFLElBQUksQ0FBQyxRQUFRO2FBQzdCO1lBQ0QsTUFBTSxFQUFFLFVBQVUsT0FBTyxZQUFZLE1BQU0sRUFBRTtZQUM3QyxRQUFRLEVBQUUsS0FBSztTQUNmLENBQUMsQ0FBQztRQUVILE9BQU8sR0FBRyxDQUFBO0lBQ1gsQ0FBQztJQUVELEtBQUssQ0FBQyxvQkFBb0IsQ0FDekIsT0FBZSxFQUNmLFNBQWlDLEVBQ2pDLEtBQWE7UUFFYixJQUFJLElBQVksQ0FBQTtRQUNoQixJQUFJLE9BQU8sU0FBUyxLQUFLLFFBQVEsRUFBRTtZQUNsQyxJQUFJLEdBQUcsU0FBUyxDQUFBO1NBQ2hCO2FBQU0sSUFBSSxPQUFPLFNBQVMsS0FBSyxRQUFRLEVBQUU7WUFDekMsSUFBSSxHQUFHLE1BQU0sQ0FBQyxTQUFTLENBQUMsQ0FBQTtTQUN4QjthQUFNLElBQUksU0FBUyxZQUFZLElBQUksRUFBRTtZQUNyQyxnQkFBZ0I7WUFDaEIsSUFBSSxHQUFHLFNBQVMsQ0FBQyxPQUFPLEVBQUUsR0FBRyxJQUFJLENBQUE7U0FDakM7YUFBTTtZQUNOLE9BQU8sT0FBTyxDQUFDLE1BQU0sQ0FBQyxJQUFJLEtBQUssQ0FBQywwQkFBMEIsQ0FBQyxDQUFDLENBQUE7U0FDNUQ7UUFFRCxNQUFNLEdBQUcsR0FBRyxHQUFHLElBQUksQ0FBQyxJQUFJLE1BQU0sT0FBTyxtQkFBbUIsQ0FBQTtRQUV4RCxJQUFJLEdBQUcsR0FBRyxNQUFNLEtBQUssQ0FBQyxHQUFHLEVBQUU7WUFDMUIsU0FBUyxFQUFFO2dCQUVWLGlCQUFpQixFQUFFLHVEQUF1RDtnQkFDMUUsY0FBYyxFQUFFLGtEQUFrRDtnQkFDbEUsY0FBYyxFQUFFLEtBQUs7Z0JBQ3JCLGtCQUFrQixFQUFFLGdCQUFnQjtnQkFDcEMsUUFBUSxFQUFFLHdtQ0FBd21DO2dCQUNsbkMsU0FBUyxFQUFFLElBQUksQ0FBQyxHQUFHO2dCQUNuQixjQUFjLEVBQUUsSUFBSSxDQUFDLFFBQVE7YUFDN0I7WUFDRCxNQUFNLEVBQUUsYUFBYSxJQUFJLEVBQUU7WUFDM0IsUUFBUSxFQUFFLEtBQUs7U0FDZixDQUFDLENBQUM7UUFFSCxJQUFJLEdBQUcsSUFBSSxHQUFHLENBQUMsTUFBTSxLQUFLLEdBQUcsRUFBRTtZQUM5QixPQUFPLElBQUksQ0FBQTtTQUNYO1FBQ0QsT0FBTTtRQUVOOzs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7VUFxQkU7UUFFRjs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7OztVQXNCRTtRQUVGOzs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7OztFQXVCQTtRQUdBLHNCQUFzQjtRQUN0QixNQUFNLE9BQU8sR0FBZ0M7WUFDNUMsU0FBUyxFQUFFLElBQUk7U0FDZixDQUFBO1FBRUQsa0JBQWtCO1FBQ2xCLGNBQU0sQ0FBQyxLQUFLLENBQUMsVUFBVSxFQUFFLE9BQU8sRUFBRSxzQkFBc0IsRUFBRSxPQUFPLENBQUMsQ0FBQTtRQUNsRSwwREFBMEQ7UUFDMUQsTUFBTSxRQUFRLEdBQUcsTUFBTSxJQUFJLENBQUMsS0FBSyxDQUErQjtZQUMvRCxHQUFHO1lBQ0gsT0FBTyxFQUFFO2dCQUNSLE1BQU0sRUFBRSxLQUFLO2dCQUNiLElBQUksRUFBRSxhQUFhLElBQUksRUFBRTtnQkFDekIsT0FBTyxFQUFFO29CQUNSLFNBQVMsRUFBRSxJQUFJLENBQUMsR0FBRztvQkFDbkIsY0FBYyxFQUFFLElBQUksQ0FBQyxRQUFRO29CQUM3QixjQUFjLEVBQUUsa0RBQWtEO2lCQUVsRTthQUNEO1NBQ0QsQ0FBQyxDQUFBO1FBRUYsV0FBVztRQUNYLElBQUksUUFBUSxDQUFDLE9BQU8sS0FBSyxJQUFJLEVBQUU7WUFDOUIsT0FBTyxPQUFPLENBQUMsTUFBTSxDQUNwQixJQUFJLEtBQUssQ0FDUiw2QkFBNkIsT0FBTyxhQUFhO2dCQUNoRCxHQUFHO2dCQUNILE9BQU87Z0JBQ1AsUUFBUTthQUNSLEVBQUUsQ0FDSCxDQUNELENBQUE7U0FDRDtRQUVELE9BQU8sUUFBUSxDQUFBO0lBQ2hCLENBQUM7SUFFRCxvQ0FBb0M7SUFDcEMsUUFBUTtJQUVSOztPQUVHO0lBQ08sS0FBSyxDQUFDLDJCQUEyQixDQUMxQyxPQUFlLEVBQ2YsT0FBcUIsRUFBRTtRQUV2QixNQUFNLEdBQUcsR0FBRyxHQUFHLElBQUksQ0FBQyxJQUFJLE1BQU0sT0FBTyxhQUFhLENBQUE7UUFDbEQsTUFBTSxRQUFRLEdBQUcsTUFBTSxJQUFJLENBQUMsS0FBSyxDQUFnQixFQUFFLEdBQUcsRUFBRSxHQUFHLElBQUksRUFBRSxDQUFDLENBQUE7UUFDbEUsT0FBTyxRQUFRLENBQUE7SUFDaEIsQ0FBQztJQUVELEtBQUssQ0FBQyxXQUFXLENBQUMsSUFBSSxFQUFFLEtBQUssRUFBRSxVQUFVO1FBRXhDLE1BQU0sR0FBRyxHQUFHLElBQUEsNEJBQVEsRUFBQztZQUNwQixNQUFNLEVBQUUsRUFBRTtZQUNWLE9BQU8sRUFBRSxJQUFJO1NBQ2IsQ0FBQyxDQUFBO1FBRUYsSUFBSSxJQUFJLEdBQUcsTUFBTSxJQUFJLENBQUMsVUFBVSxDQUFDO1lBQ2hDLE1BQU0sRUFBRSxJQUFJO1lBQ1osT0FBTyxFQUFFLEtBQUs7WUFDZCxVQUFVLEVBQUUsR0FBRztZQUNmLFVBQVUsRUFBRSxJQUFJO1lBQ2hCLFFBQVEsRUFBRSxJQUFJO1lBQ2QsVUFBVSxFQUFFLElBQUk7WUFDaEIsZ0JBQWdCLEVBQUUsSUFBSTtTQUN0QixDQUErQixDQUFDO1FBRWpDLElBQUksSUFBSSxJQUFJLElBQUksQ0FBQyxPQUFPLEVBQUU7WUFDekIsTUFBTSxJQUFJLENBQUMsV0FBVyxDQUFDLElBQUksRUFBRSxVQUFVLENBQUMsQ0FBQztZQUN6QyxPQUFPLEVBQUUsR0FBRyxJQUFJLEVBQUUsUUFBUSxFQUFFLEdBQUcsRUFBRSxDQUFDO1NBQ2xDO2FBQU07WUFDTixJQUFJLElBQUksSUFBSSxJQUFJLENBQUMsT0FBTyxJQUFJLElBQUksQ0FBQyxPQUFPLElBQUksK0RBQStELEVBQUU7Z0JBQzVHLE9BQU8sSUFBSSxDQUFDO2FBQ1o7aUJBQU0sSUFBSSxJQUFJLElBQUksSUFBSSxDQUFDLE9BQU8sSUFBSSxJQUFJLENBQUMsT0FBTyxJQUFJLDZDQUE2QyxFQUFFO2dCQUNqRyxJQUFJLElBQUksQ0FBQyxPQUFPLEVBQUU7b0JBQ2pCLE9BQU8sRUFBRSxHQUFHLElBQUksRUFBRSxRQUFRLEVBQUUsR0FBRyxFQUFFLENBQUM7aUJBQ2xDO2dCQUNELE9BQU8sSUFBSSxDQUFDO2FBQ1o7aUJBQU07Z0JBQ04sT0FBTyxDQUFDLEdBQUcsQ0FBQyxtQkFBbUIsR0FBRyxJQUFJLEVBQUUsSUFBSSxDQUFDLENBQUM7YUFDOUM7WUFDRCxPQUFPLElBQUksQ0FBQztTQUNaO0lBQ0YsQ0FBQztJQUVELEtBQUssQ0FBQyxRQUFRLENBQUMsSUFBSTtRQUNsQixNQUFNLEdBQUcsR0FBRyxHQUFHLElBQUksQ0FBQyxJQUFJLHFDQUFxQyxHQUFHLElBQUksQ0FBQTtRQUNwRSxNQUFNLFFBQVEsR0FBRyxNQUFNLElBQUksQ0FBQyxLQUFLLENBQWlCLEVBQUUsR0FBRyxFQUFFLENBQUMsQ0FBQTtRQUMxRCxPQUFPLFFBQVEsQ0FBQTtJQUNoQixDQUFDO0lBRUQsS0FBSyxDQUFDLE9BQU8sQ0FBQyxFQUFFO1FBQ2YsTUFBTSxHQUFHLEdBQUcsR0FBRyxJQUFJLENBQUMsSUFBSSxnQkFBZ0IsRUFBRSxPQUFPLENBQUE7UUFDakQsTUFBTSxRQUFRLEdBQUcsTUFBTSxJQUFJLENBQUMsS0FBSyxDQUFjLEVBQUUsR0FBRyxFQUFFLENBQUMsQ0FBQTtRQUN2RCxPQUFPLFFBQVEsQ0FBQztJQUNqQixDQUFDO0lBRUQ7O09BRUc7SUFDSCxLQUFLLENBQUMsbUJBQW1CLENBQ3hCLE9BQWUsRUFDZixPQUFxQixFQUFFO1FBRXZCLE1BQU0sUUFBUSxHQUFHLE1BQU0sSUFBSSxDQUFDLDJCQUEyQixDQUFDLE9BQU8sRUFBRSxJQUFJLENBQUMsQ0FBQTtRQUN0RSxNQUFNLEtBQUssR0FBZSxRQUFRLENBQUMsV0FBVyxDQUFDLEtBQUssQ0FBQTtRQUNwRCxNQUFNLEdBQUcsR0FBRyxLQUFLLENBQUMsR0FBRyxDQUFDLENBQUMsQ0FBQyxFQUFFLEVBQUUsQ0FBQyxDQUFDLENBQUMsRUFBRSxDQUFDLENBQUE7UUFDbEMsT0FBTyxLQUFLLENBQUE7SUFDYixDQUFDO0lBRUQ7O09BRUc7SUFDSCxLQUFLLENBQUMsb0JBQW9CLENBQ3pCLFFBQWtCLEVBQ2xCLE9BQXFCLEVBQUU7UUFFdkIsdUNBQXVDO1FBQ3ZDLE1BQU0saUJBQWlCLEdBQUcsTUFBTSxPQUFPLENBQUMsR0FBRyxDQUMxQyxRQUFRLENBQUMsR0FBRyxDQUFDLENBQUMsRUFBRSxFQUFFLEVBQUUsQ0FBQyxJQUFJLENBQUMsbUJBQW1CLENBQUMsRUFBRSxFQUFFLElBQUksQ0FBQyxDQUFDLENBQ3hELENBQUE7UUFFRCxhQUFhO1FBQ2IsT0FBTyxpQkFBaUIsQ0FBQyxJQUFJLEVBQUUsQ0FBQTtJQUNoQyxDQUFDO0lBRUQ7O09BRUc7SUFDSCxLQUFLLENBQUMsc0JBQXNCLENBQzNCLFVBQWtCLEVBQ2xCLE9BQXFCLEVBQUU7UUFFdkIsZ0NBQWdDO1FBQ2hDLE1BQU0sTUFBTSxHQUFHLE1BQU0sSUFBSSxDQUFDLHVCQUF1QixDQUFDLFVBQVUsRUFBRSxJQUFJLENBQUMsQ0FBQTtRQUNuRSxNQUFNLFFBQVEsR0FBRyxNQUFNLENBQUMsR0FBRyxDQUFDLENBQUMsQ0FBQyxFQUFFLEVBQUUsQ0FBQyxDQUFDLENBQUMsRUFBRSxDQUFDLENBQUE7UUFFeEMsUUFBUTtRQUNSLE1BQU0sS0FBSyxHQUFHLE1BQU0sSUFBSSxDQUFDLG9CQUFvQixDQUFDLFFBQVEsQ0FBQyxDQUFBO1FBQ3ZELE1BQU0sR0FBRyxHQUFHLEtBQUssQ0FBQyxHQUFHLENBQUMsQ0FBQyxDQUFDLEVBQUUsRUFBRSxDQUFDLENBQUMsQ0FBQyxFQUFFLENBQUMsQ0FBQTtRQUNsQyxPQUFPLEtBQUssQ0FBQTtJQUNiLENBQUM7SUFFRDs7T0FFRztJQUNILEtBQUssQ0FBQyx3QkFBd0IsQ0FDN0IsV0FBcUIsRUFDckIsT0FBcUIsRUFBRTtRQUV2QiwyQ0FBMkM7UUFDM0MsTUFBTSxxQkFBcUIsR0FBRyxNQUFNLE9BQU8sQ0FBQyxHQUFHLENBQzlDLFdBQVcsQ0FBQyxHQUFHLENBQUMsQ0FBQyxFQUFFLEVBQUUsRUFBRSxDQUFDLElBQUksQ0FBQyxzQkFBc0IsQ0FBQyxFQUFFLEVBQUUsSUFBSSxDQUFDLENBQUMsQ0FDOUQsQ0FBQTtRQUVELGFBQWE7UUFDYixPQUFPLHFCQUFxQixDQUFDLElBQUksRUFBRSxDQUFBO0lBQ3BDLENBQUM7SUFFRDs7T0FFRztJQUNILEtBQUssQ0FBQyxZQUFZLENBQUMsT0FBcUIsRUFBRTtRQUN6QyxNQUFNLFVBQVUsR0FBRyxNQUFNLElBQUksQ0FBQyxhQUFhLEVBQUUsQ0FBQTtRQUM3QyxNQUFNLFdBQVcsR0FBRyxVQUFVLENBQUMsR0FBRyxDQUFDLENBQUMsQ0FBQyxFQUFFLEVBQUUsQ0FBQyxDQUFDLENBQUMsRUFBRSxDQUFDLENBQUE7UUFDL0MsT0FBTyxJQUFJLENBQUMsd0JBQXdCLENBQUMsV0FBVyxFQUFFLElBQUksQ0FBQyxDQUFBO0lBQ3hELENBQUM7SUFFRDs7T0FFRztJQUNILE9BQU8sQ0FBQyxFQUFVLEVBQUUsT0FBcUIsRUFBRTtRQUMxQyxNQUFNLEdBQUcsR0FBRyxHQUFHLElBQUksQ0FBQyxJQUFJLFVBQVUsRUFBRSxPQUFPLENBQUE7UUFDM0MsT0FBTyxJQUFJLENBQUMsS0FBSyxDQUFlLEVBQUUsR0FBRyxFQUFFLEdBQUcsSUFBSSxFQUFFLENBQUMsQ0FBQTtJQUNsRCxDQUFDO0lBRUQsS0FBSyxDQUFDLFdBQVcsQ0FBQyxNQUFNLEVBQUUsR0FBRyxFQUFFLFFBQVE7UUFFdEMsTUFBTSxHQUFHLEdBQUcsR0FBRyxJQUFJLENBQUMsSUFBSSxhQUFhLENBQUM7UUFFdEMsSUFBSSxJQUFJLEdBQVEsSUFBSSxRQUFRLEVBQUUsQ0FBQztRQUMvQixJQUFJLENBQUMsTUFBTSxDQUFDLFVBQVUsRUFBRSxFQUFFLEdBQUcsTUFBTSxDQUFDLENBQUM7UUFDckMsSUFBSSxDQUFDLE1BQU0sQ0FBQyxLQUFLLEVBQUUsR0FBRyxDQUFDLENBQUM7UUFDeEIsSUFBSSxDQUFDLE1BQU0sQ0FBQyxhQUFhLEVBQUUsTUFBTSxDQUFDLENBQUM7UUFDbkMsSUFBSSxDQUFDLE1BQU0sQ0FBQyxVQUFVLEVBQUUsUUFBUSxDQUFDLENBQUM7UUFDbEMsSUFBSSxPQUFPLEdBQUc7WUFDYixNQUFNLEVBQUUsTUFBTTtZQUNkLEdBQUcsRUFBRSxHQUFHO1lBQ1IsT0FBTyxFQUNQO2dCQUNDLGNBQWMsRUFBRSxrREFBa0Q7Z0JBQ2xFLFNBQVMsRUFBRSxJQUFJLENBQUMsR0FBRztnQkFDbkIsY0FBYyxFQUFFLElBQUksQ0FBQyxRQUFRO2FBQzdCO1lBQ0QsTUFBTSxFQUFFLE9BQU8sR0FBRyxnQ0FBZ0MsUUFBUSxhQUFhLE1BQU0sd0ZBQXdGO1NBQ3JLLENBQUM7UUFFRixPQUFPLElBQUksT0FBTyxDQUFDLENBQUMsT0FBTyxFQUFFLE1BQU0sRUFBRSxFQUFFO1lBQ3RDLE9BQU8sQ0FBQyxPQUFPLEVBQUUsVUFBVSxLQUFLLEVBQUUsUUFBUSxFQUFFLElBQUk7Z0JBQy9DLElBQUksS0FBSyxFQUFFO29CQUNWLE1BQU0sSUFBSSxLQUFLLENBQUMsS0FBSyxDQUFDLENBQUM7aUJBQ3ZCO3FCQUFNO29CQUNOLE9BQU8sQ0FBQyxJQUFJLENBQUMsQ0FBQztpQkFDZDtZQUNGLENBQUMsQ0FBQyxDQUFDO1FBQ0osQ0FBQyxDQUFDLENBQUM7SUFFSixDQUFDO0lBRUQsS0FBSyxDQUFDLFdBQVcsQ0FBQyxNQUF1QixFQUFFLE9BQXdCLEVBQUUsS0FBYTtRQUVqRixNQUFNLEdBQUcsR0FBRyxHQUFHLElBQUksQ0FBQyxJQUFJLE1BQU0sT0FBTyxvQkFBb0IsQ0FBQztRQUMxRCxJQUFJLE9BQU8sR0FBRztZQUNiLE1BQU0sRUFBRSxNQUFNO1lBQ2QsR0FBRyxFQUFFLEdBQUc7WUFDUixPQUFPLEVBQ1A7Z0JBQ0MsY0FBYyxFQUFFLGtEQUFrRDtnQkFDbEUsU0FBUyxFQUFFLElBQUksQ0FBQyxHQUFHO2dCQUNuQixjQUFjLEVBQUUsSUFBSSxDQUFDLFFBQVE7YUFDN0I7WUFDRCxJQUFJLEVBQUUsa0JBQWtCLE1BQU0sYUFBYSxLQUFLLEVBQUU7U0FDbEQsQ0FBQztRQUdGLE9BQU8sSUFBSSxPQUFPLENBQUMsQ0FBQyxPQUFPLEVBQUUsTUFBTSxFQUFFLEVBQUU7WUFDdEMsT0FBTyxDQUFDLE9BQU8sRUFBRSxVQUFVLEtBQUssRUFBRSxRQUFRLEVBQUUsSUFBSTtnQkFDL0MsSUFBSSxLQUFLLEVBQUU7b0JBQ1YsTUFBTSxJQUFJLEtBQUssQ0FBQyxLQUFLLENBQUMsQ0FBQztpQkFDdkI7cUJBQU07b0JBQ04sT0FBTyxDQUFDLElBQUksQ0FBQyxDQUFDO2lCQUNkO1lBQ0YsQ0FBQyxDQUFDLENBQUM7UUFDSixDQUFDLENBQUMsQ0FBQztJQUNKLENBQUM7SUFFRCxLQUFLLENBQUMsVUFBVSxDQUFDLElBQUk7UUFDcEIsTUFBTSxHQUFHLEdBQUcsR0FBRyxJQUFJLENBQUMsSUFBSSxRQUFRLENBQUE7UUFDaEMsT0FBTyxNQUFNLElBQUksQ0FBQyxLQUFLLENBQUMsR0FBRyxFQUFFLElBQUksQ0FBQyxDQUFDO0lBQ3BDLENBQUM7SUFFRCxLQUFLLENBQUMsaUJBQWlCLENBQUMsUUFBUTtRQUMvQixNQUFNLEdBQUcsR0FBRyxHQUFHLElBQUksQ0FBQyxJQUFJLE1BQU0sUUFBUSxPQUFPLENBQUE7UUFDN0MsTUFBTSxRQUFRLEdBQUcsTUFBTSxJQUFJLENBQUMsS0FBSyxDQUFjLEVBQUUsR0FBRyxFQUFFLENBQUMsQ0FBQTtRQUN2RCxPQUFRLFFBQWdCLENBQUMsSUFBSSxDQUFBO0lBQzlCLENBQUM7SUFFRCxLQUFLLENBQUMsYUFBYSxDQUFDLFNBQVMsRUFBRSxTQUFTO1FBQ3ZDLG9CQUFvQjtRQUNwQixNQUFNLEdBQUcsR0FBRyxHQUFHLElBQUksQ0FBQyxJQUFJLE1BQU0sU0FBUywrQkFBK0IsQ0FBQztRQUN2RSxPQUFPLE1BQU0sZUFBSyxDQUFDLEdBQUcsQ0FBQyxHQUFHLEVBQUU7WUFDM0IsU0FBUztZQUNULFFBQVEsRUFBRSxTQUFTO1lBQ25CLElBQUksRUFBRSxVQUFVO1NBQ2hCLEVBQUU7WUFDRixPQUFPLEVBQUU7Z0JBQ1IsUUFBUSxFQUFFLGtCQUFrQjtnQkFDNUIsaUJBQWlCLEVBQUUsZ0JBQWdCO2dCQUNuQyxTQUFTLEVBQUUsSUFBSSxDQUFDLEdBQUc7Z0JBQ25CLGNBQWMsRUFBRSxJQUFJLENBQUMsUUFBUTthQUM3QjtTQUNELENBQUMsQ0FBQztJQUNKLENBQUM7SUFFRCxLQUFLLENBQUMsVUFBVSxDQUFDLFNBQVMsRUFBRSxJQUFJO1FBRS9CLE1BQU0sR0FBRyxHQUFHLEdBQUcsSUFBSSxDQUFDLElBQUksTUFBTSxTQUFTLE9BQU8sQ0FBQztRQUMvQyxPQUFPLE1BQU0sZUFBSyxDQUFDLEdBQUcsQ0FBQyxHQUFHLEVBQUU7WUFDM0IsR0FBRyxJQUFJO1NBQ1AsRUFBRTtZQUNGLE9BQU8sRUFBRTtnQkFDUixRQUFRLEVBQUUsa0JBQWtCO2dCQUM1QixpQkFBaUIsRUFBRSxnQkFBZ0I7Z0JBQ25DLFNBQVMsRUFBRSxJQUFJLENBQUMsR0FBRztnQkFDbkIsY0FBYyxFQUFFLElBQUksQ0FBQyxRQUFRO2FBQzdCO1NBQ0QsQ0FBQyxDQUFDO0lBQ0osQ0FBQztJQUVELEtBQUssQ0FBQyxXQUFXLENBQUMsU0FBUyxFQUFFLEtBQUs7UUFDakMsb0JBQW9CO1FBQ3BCLE1BQU0sR0FBRyxHQUFHLEdBQUcsSUFBSSxDQUFDLElBQUksV0FBVyxLQUFLLGVBQWUsQ0FBQztRQUN4RCxNQUFNLENBQUMsR0FBRyxlQUFLLENBQUMsR0FBRyxDQUFDLEdBQUcsRUFBRTtZQUN4QixTQUFTLEVBQUUsU0FBUztZQUNwQixZQUFZLEVBQUUsS0FBSztTQUNuQixFQUFFO1lBQ0YsT0FBTyxFQUFFO2dCQUNSLFFBQVEsRUFBRSxrQkFBa0I7Z0JBQzVCLGlCQUFpQixFQUFFLGdCQUFnQjtnQkFDbkMsU0FBUyxFQUFFLElBQUksQ0FBQyxHQUFHO2dCQUNuQixjQUFjLEVBQUUsSUFBSSxDQUFDLFFBQVE7YUFDN0I7U0FDRCxDQUFDLENBQUM7UUFDSCxDQUFDLENBQUMsSUFBSSxDQUFDLENBQUMsQ0FBQyxFQUFFLEVBQUU7UUFFYixDQUFDLENBQUMsQ0FBQyxLQUFLLENBQUMsQ0FBQyxDQUFDLEVBQUUsRUFBRTtZQUNkLFdBQVc7UUFDWixDQUFDLENBQUMsQ0FBQTtRQUNGLE9BQU8sQ0FBQyxDQUFDO0lBQ1YsQ0FBQztJQUlELEtBQUssQ0FBQyxNQUFNLENBQUMsTUFBTSxFQUFFLElBQUk7UUFDeEIsb0JBQW9CO1FBQ3BCLE1BQU0sR0FBRyxHQUFHLEdBQUcsSUFBSSxDQUFDLElBQUksZUFBZSxDQUFDO1FBRXhDLElBQUksSUFBSSxHQUFRLElBQUksUUFBUSxFQUFFLENBQUM7UUFDL0IsTUFBTSxNQUFNLEdBQUcsSUFBSSxDQUFDLEtBQUssQ0FBQyxJQUFJLENBQUMsQ0FBQztRQUNoQyxJQUFJLENBQUMsTUFBTSxDQUFDLE1BQU0sRUFBRSxJQUFJLEVBQUUsTUFBTSxDQUFDLElBQUksQ0FBQyxDQUFDO1FBQ3ZDLElBQUksQ0FBQyxNQUFNLENBQUMsU0FBUyxFQUFFLE1BQU0sQ0FBQyxDQUFDO1FBQy9CLElBQUksQ0FBQyxNQUFNLENBQUMsYUFBYSxFQUFFLFFBQVEsQ0FBQyxDQUFDO1FBQ3JDLElBQUksQ0FBQyxNQUFNLENBQUMsTUFBTSxFQUFFLEVBQUUsQ0FBQyxnQkFBZ0IsQ0FBQyxJQUFJLENBQUMsQ0FBQyxDQUFDO1FBRS9DLE9BQU8sTUFBTSxlQUFLLENBQUMsSUFBSSxDQUFDLEdBQUcsRUFBRSxJQUFJLEVBQUU7WUFDbEMsT0FBTyxFQUFFO2dCQUNSLFFBQVEsRUFBRSxrQkFBa0I7Z0JBQzVCLGlCQUFpQixFQUFFLGdCQUFnQjtnQkFDbkMsY0FBYyxFQUFFLGlDQUFpQyxJQUFJLENBQUMsU0FBUyxFQUFFO2dCQUNqRSxTQUFTLEVBQUUsSUFBSSxDQUFDLEdBQUc7Z0JBQ25CLGNBQWMsRUFBRSxJQUFJLENBQUMsUUFBUTthQUM3QjtTQUNELENBQUMsQ0FBQztJQUNKLENBQUM7SUFFRCxLQUFLLENBQUMsVUFBVSxDQUFDLE1BQU0sRUFBRSxJQUFJO1FBQzVCLG9CQUFvQjtRQUNwQixNQUFNLEdBQUcsR0FBRyxHQUFHLElBQUksQ0FBQyxJQUFJLGVBQWUsQ0FBQztRQUN4QyxJQUFJLElBQUksR0FBUSxJQUFJLFFBQVEsRUFBRSxDQUFDO1FBQy9CLE1BQU0sTUFBTSxHQUFHLElBQUksQ0FBQyxLQUFLLENBQUMsSUFBSSxDQUFDLENBQUM7UUFDaEMsSUFBSSxDQUFDLE1BQU0sQ0FBQyxNQUFNLEVBQUUsSUFBSSxFQUFFLE1BQU0sQ0FBQyxJQUFJLENBQUMsQ0FBQztRQUN2QyxJQUFJLENBQUMsTUFBTSxDQUFDLFNBQVMsRUFBRSxNQUFNLENBQUMsQ0FBQztRQUMvQixJQUFJLENBQUMsTUFBTSxDQUFDLGFBQWEsRUFBRSxVQUFVLENBQUMsQ0FBQztRQUN2QyxJQUFJLENBQUMsTUFBTSxDQUFDLE1BQU0sRUFBRSxFQUFFLENBQUMsZ0JBQWdCLENBQUMsSUFBSSxDQUFDLENBQUMsQ0FBQztRQUUvQyxPQUFPLE1BQU0sZUFBSyxDQUFDLElBQUksQ0FBQyxHQUFHLEVBQUUsSUFBSSxFQUFFO1lBQ2xDLE9BQU8sRUFBRTtnQkFDUixRQUFRLEVBQUUsa0JBQWtCO2dCQUM1QixpQkFBaUIsRUFBRSxnQkFBZ0I7Z0JBQ25DLGNBQWMsRUFBRSxpQ0FBaUMsSUFBSSxDQUFDLFNBQVMsRUFBRTtnQkFDakUsU0FBUyxFQUFFLElBQUksQ0FBQyxHQUFHO2dCQUNuQixjQUFjLEVBQUUsSUFBSSxDQUFDLFFBQVE7YUFDN0I7U0FDRCxDQUFDLENBQUM7SUFDSixDQUFDO0lBRUQsb0NBQW9DO0lBQ3BDLGtCQUFrQjtJQUVsQjs7T0FFRztJQUNILEtBQUssQ0FBQyxRQUFRLENBQ2IsT0FBeUIsRUFDekIsT0FBcUIsRUFBRTtRQUV2Qix1QkFBdUI7UUFDdkIsSUFBSSxDQUFDLE9BQU8sRUFBRTtZQUNiLE1BQU0sS0FBSyxHQUFHLE1BQU0sSUFBSSxDQUFDLFlBQVksQ0FBQyxJQUFJLENBQUMsQ0FBQTtZQUMzQyxPQUFPLEdBQUcsS0FBSyxDQUFDLEdBQUcsQ0FBQyxDQUFDLENBQUMsRUFBRSxFQUFFLENBQUMsQ0FBQyxDQUFDLEVBQUUsQ0FBQyxDQUFBO1NBQ2hDO1FBRUQsb0JBQW9CO1FBQ3BCLE9BQU8sTUFBTSxPQUFPLENBQUMsR0FBRyxDQUFDLE9BQU8sQ0FBQyxHQUFHLENBQUMsQ0FBQyxFQUFFLEVBQUUsRUFBRSxDQUFDLElBQUksQ0FBQyxPQUFPLENBQUMsRUFBRSxFQUFFLElBQUksQ0FBQyxDQUFDLENBQUMsQ0FBQTtJQUN0RSxDQUFDO0lBRUQsS0FBSyxDQUFDLFVBQVUsQ0FDZixLQUFhLEVBQ2IsR0FBVyxFQUNYLFFBQWdCO1FBRWhCLG9CQUFvQjtRQUNwQixNQUFNLEdBQUcsR0FBRyxHQUFHLElBQUksQ0FBQyxJQUFJLFFBQVEsQ0FBQTtRQUNoQyxPQUFPLElBQUksT0FBTyxDQUFDLENBQUMsT0FBTyxFQUFFLEVBQUU7WUFDOUIsT0FBTyxJQUFJLENBQUMsS0FBSyxDQUFDLEdBQUcsRUFBRSxFQUFFLEdBQUcsRUFBRSxLQUFLLEVBQUUsUUFBUSxFQUFFLENBQUMsQ0FBQyxJQUFJLENBQUMsQ0FBQyxDQUFDLEVBQUUsRUFBRTtnQkFDM0QsT0FBTyxDQUFDLENBQUMsQ0FBQyxDQUFDO1lBQ1osQ0FBQyxDQUFDLENBQUMsS0FBSyxDQUFDLENBQUMsQ0FBQyxFQUFFLEVBQUU7Z0JBQ2QsT0FBTyxDQUFDLENBQUMsQ0FBQyxRQUFRLENBQUMsSUFBSSxDQUFDLENBQUM7WUFDMUIsQ0FBQyxDQUFDLENBQUE7UUFDSCxDQUFDLENBQUMsQ0FBQztJQUNKLENBQUM7SUFFRDs7Ozs7O09BTUc7SUFDSCxLQUFLLENBQUMsVUFBVSxDQUNmLE1BQWMsRUFDZCxPQUFlLEVBQ2YsU0FBaUIsWUFBWSxFQUM3QixHQUFZO1FBRVosc0JBQXNCO1FBQ3RCLE1BQU0sSUFBSSxHQUFzQjtZQUMvQixJQUFJLEVBQUU7Z0JBQ0wsR0FBRyxFQUFFLE9BQU87Z0JBQ1osV0FBVyxFQUFFLE1BQU07YUFDbkI7U0FDRCxDQUFBO1FBQ0QsSUFBSSxHQUFHLEVBQUU7WUFDUixJQUFJLENBQUMsSUFBSSxDQUFDLE9BQU8sR0FBRyxHQUFHLENBQUE7U0FDdkI7UUFFRCxrQkFBa0I7UUFDbEIsTUFBTSxHQUFHLEdBQUcsR0FBRyxJQUFJLENBQUMsSUFBSSxVQUFVLE1BQU0sT0FBTyxDQUFBO1FBQy9DLE1BQU0sUUFBUSxHQUFHLE1BQU0sSUFBSSxDQUFDLEtBQUssQ0FBcUI7WUFDckQsR0FBRztZQUNILE9BQU8sRUFBRTtnQkFDUixNQUFNLEVBQUUsS0FBSztnQkFDYixJQUFJLEVBQUUsSUFBSSxDQUFDLFNBQVMsQ0FBQyxJQUFJLENBQUM7YUFDMUI7U0FDRCxDQUFDLENBQUE7UUFFRixzQkFBc0I7UUFDdEIsT0FBTyxRQUFRLENBQUMsSUFBSSxDQUFBO0lBQ3JCLENBQUM7SUFFRDs7T0FFRztJQUNILEtBQUssQ0FBQyxXQUFXLENBQ2hCLE1BQWMsRUFDZCxXQUFtQixFQUNuQixLQUFhLEVBQ2IsSUFBZTtRQUVmLE1BQU0sSUFBSSxHQUNWO1lBQ0MsS0FBSztZQUNMLElBQUksRUFBRSxJQUFJLElBQUksRUFBRTtZQUNoQixZQUFZLEVBQUUsSUFBSTtZQUNsQixXQUFXO1NBQ1gsQ0FBQTtRQUVELE1BQU0sR0FBRyxHQUFHLEdBQUcsSUFBSSxDQUFDLElBQUksTUFBTSxNQUFNLE9BQU8sQ0FBQTtRQUMzQyxNQUFNLFFBQVEsR0FBRyxNQUFNLElBQUksQ0FBQyxLQUFLLENBQU07WUFDdEMsR0FBRztZQUNILE9BQU8sRUFBRTtnQkFDUixNQUFNLEVBQUUsS0FBSztnQkFDYixJQUFJLEVBQUUsSUFBSSxDQUFDLFNBQVMsQ0FBQyxJQUFJLENBQUM7YUFDMUI7U0FDRCxDQUFDLENBQUE7UUFDRixPQUFPLFFBQVEsQ0FBQyxXQUFXLENBQUE7SUFDNUIsQ0FBQztJQUlELEtBQUssQ0FBQyxVQUFVLENBQUMsTUFBYztRQUU5QixNQUFNLEdBQUcsR0FBRyxHQUFHLElBQUksQ0FBQyxJQUFJLFVBQVUsTUFBTSxTQUFTLENBQUE7UUFDakQsY0FBTSxDQUFDLEtBQUssQ0FBQyxVQUFVLEVBQUUsTUFBTSxDQUFDLENBQUE7UUFFaEMsTUFBTSxRQUFRLEdBQUcsTUFBTSxJQUFJLENBQUMsS0FBSyxDQUFlO1lBQy9DLEdBQUc7WUFDSCxPQUFPLEVBQUU7Z0JBQ1IsTUFBTSxFQUFFLEtBQUs7YUFDYjtTQUNELENBQUMsQ0FBQTtRQUNGLGNBQU0sQ0FBQyxLQUFLLENBQUMsU0FBUyxFQUFFLE1BQU0sQ0FBQyxDQUFBO1FBQy9CLE9BQU8sUUFBUSxDQUFBO0lBQ2hCLENBQUM7SUFJRDs7T0FFRztJQUNILEtBQUssQ0FBQyxVQUFVLENBQ2YsSUFBa0IsRUFDbEIsUUFBc0I7UUFFdEIsK0RBQStEO1FBQy9ELElBQUksSUFBSSxDQUFDLEdBQUcsSUFBSSxJQUFJLEVBQUU7WUFDckIsSUFBSSxHQUFHLE1BQU0sSUFBSSxDQUFDLE9BQU8sQ0FBQyxJQUFJLENBQUMsRUFBRSxDQUFDLENBQUE7U0FDbEM7UUFFRCxRQUFRO1FBQ1IsSUFBSSxDQUFDLElBQUksQ0FBQyxHQUFHLEVBQUU7WUFDZCxPQUFPLE9BQU8sQ0FBQyxPQUFPLENBQUMsSUFBSSxDQUFDLENBQUE7U0FDNUI7UUFFRCxVQUFVO1FBQ1YsTUFBTSxFQUFFLE1BQU0sRUFBRSxNQUFNLEVBQUUsR0FBRyxRQUFRLENBQUMsSUFBSSxDQUFDLENBQUE7UUFDekMsSUFBSSxNQUFNLEtBQUssSUFBSSxDQUFDLEdBQUcsRUFBRTtZQUN4QixxQkFBcUI7WUFDckIsb0RBQW9EO1lBQ3BELGlDQUFpQztZQUNqQyxrQkFBa0I7WUFDbEIsa0RBQWtEO1lBQ2xELGFBQWE7WUFDYixzQkFBc0I7WUFDdEIsTUFBTTtZQUNOLHlDQUF5QztZQUN6QyxLQUFLO1lBQ0wsSUFBSTtZQUNKLE9BQU8sT0FBTyxDQUFDLE9BQU8sQ0FBQyxJQUFJLENBQUMsQ0FBQTtTQUM1QjtRQUVELE1BQU07UUFDTixJQUFJLElBQUksQ0FBQyxHQUFHLEVBQUU7WUFDYixPQUFPLE9BQU8sQ0FBQyxPQUFPLENBQUM7Z0JBQ3RCLEdBQUssSUFBbUM7Z0JBQ3hDLE1BQU07Z0JBQ04sTUFBTTthQUNOLENBQUMsQ0FBQTtTQUNGO1FBRUQsU0FBUztRQUNULElBQUk7WUFDSCxPQUFPLE1BQU0sSUFBSSxDQUFDLFVBQVUsQ0FBQyxJQUFJLENBQUMsRUFBRSxFQUFFLE1BQU0sRUFBRSxNQUFNLEVBQUUsSUFBSSxDQUFDLEdBQUcsQ0FBQyxDQUFBO1NBQy9EO1FBQUMsT0FBTyxHQUFHLEVBQUU7WUFDYixJQUNDLEdBQUcsQ0FBQyxPQUFPLENBQUMsUUFBUSxDQUNuQiwrRUFBK0UsQ0FDL0UsRUFDQTtnQkFDRCxPQUFPLElBQUksQ0FBQyxVQUFVLENBQ3JCLE1BQU0sSUFBSSxDQUFDLE9BQU8sQ0FBQyxJQUFJLENBQUMsRUFBRSxFQUFFLEVBQUUsUUFBUSxFQUFFLEtBQUssRUFBRSxDQUFDLEVBQ2hELFFBQVEsQ0FDUixDQUFBO2FBQ0Q7WUFDRCxjQUFNLENBQUMsS0FBSyxDQUFDLEdBQUcsQ0FBQyxDQUFDO1lBQ2xCLE9BQU8sT0FBTyxDQUFDLE1BQU0sQ0FBQyxrQkFBa0IsSUFBSSxDQUFDLEVBQUUsU0FBUyxDQUFDLENBQUE7U0FDekQ7SUFDRixDQUFDO0lBRUQ7O09BRUc7SUFDSCxLQUFLLENBQUMsWUFBWSxDQUFDLElBQVksRUFBRSxRQUFzQjtRQUN0RCxPQUFPLElBQUksQ0FBQyxVQUFVLENBQUMsTUFBTSxJQUFJLENBQUMsT0FBTyxDQUFDLElBQUksQ0FBQyxFQUFFLFFBQVEsQ0FBQyxDQUFBO0lBQzNELENBQUM7SUFFRDs7T0FFRztJQUNILEtBQUssQ0FBQyxjQUFjLENBQUMsSUFBYyxFQUFFLFFBQXNCO1FBQzFELE9BQU8sSUFBSSxDQUFDLFVBQVUsQ0FBQyxNQUFNLElBQUksQ0FBQyxPQUFPLENBQUMsSUFBSSxDQUFDLEVBQUUsQ0FBQyxFQUFFLFFBQVEsQ0FBQyxDQUFBO0lBQzlELENBQUM7SUFFRDs7T0FFRztJQUNILEtBQUssQ0FBQyxXQUFXLENBQ2hCLEtBQXFCLEVBQ3JCLFFBQXNCO1FBRXRCLE1BQU0sT0FBTyxHQUFHLE1BQU0sT0FBTyxDQUFDLEdBQUcsQ0FDaEMsS0FBSyxDQUFDLEdBQUcsQ0FBQyxDQUFDLElBQUksRUFBRSxFQUFFLENBQUMsSUFBSSxDQUFDLFVBQVUsQ0FBQyxJQUFJLEVBQUUsUUFBUSxDQUFDLENBQUMsQ0FDcEQsQ0FBQTtRQUNELE1BQU0sT0FBTyxHQUFHLE9BQU8sQ0FBQyxNQUFNLENBQUMsQ0FBQyxDQUFDLEVBQUUsRUFBRSxDQUFDLENBQUMsQ0FBcUIsQ0FBQTtRQUM1RCxPQUFPLE9BQU8sQ0FBQTtJQUNmLENBQUM7SUFFRCxvQ0FBb0M7SUFDcEMsVUFBVTtJQUVWOzs7T0FHRztJQUNILEtBQUssQ0FBQyxTQUFTLENBQUMsT0FBZSxFQUFFLE9BQXFCLEVBQUU7UUFDdkQsTUFBTSxLQUFLLEdBQUcsTUFBTSxJQUFJLENBQUMsUUFBUSxDQUFDLE9BQU8sRUFBRSxJQUFJLENBQUMsQ0FBQTtRQUNoRCxNQUFNLENBQUMsSUFBSSxFQUFFLEdBQUcsT0FBTyxDQUFDLEdBQUcsTUFBTSxJQUFJLENBQUMsbUJBQW1CLENBQUMsT0FBTyxFQUFFLElBQUksQ0FBQyxDQUFBO1FBQ3hFLE9BQU87WUFDTixLQUFLO1lBQ0wsSUFBSTtZQUNKLE9BQU87U0FDUCxDQUFBO0lBQ0YsQ0FBQztJQUVEOztPQUVHO0lBQ0gsS0FBSyxDQUFDLFVBQVUsQ0FDZixRQUFrQixFQUNsQixPQUFxQixFQUFFO1FBRXZCLE9BQU8sTUFBTSxPQUFPLENBQUMsR0FBRyxDQUFDLFFBQVEsQ0FBQyxHQUFHLENBQUMsQ0FBQyxFQUFFLEVBQUUsRUFBRSxDQUFDLElBQUksQ0FBQyxTQUFTLENBQUMsRUFBRSxFQUFFLElBQUksQ0FBQyxDQUFDLENBQUMsQ0FBQTtJQUN6RSxDQUFDO0lBRUQ7O09BRUc7SUFDSCxLQUFLLENBQUMsb0JBQW9CLENBQ3pCLFVBQWtCLEVBQ2xCLE9BQXFCLEVBQUU7UUFFdkIsZ0NBQWdDO1FBQ2hDLE1BQU0sTUFBTSxHQUFHLE1BQU0sSUFBSSxDQUFDLHVCQUF1QixDQUFDLFVBQVUsRUFBRSxJQUFJLENBQUMsQ0FBQTtRQUNuRSxNQUFNLFFBQVEsR0FBRyxNQUFNLENBQUMsR0FBRyxDQUFDLENBQUMsQ0FBQyxFQUFFLEVBQUUsQ0FBQyxDQUFDLENBQUMsRUFBRSxDQUFDLENBQUE7UUFDeEMsaUJBQWlCO1FBQ2pCLE9BQU8sTUFBTSxJQUFJLENBQUMsVUFBVSxDQUFDLFFBQVEsQ0FBQyxDQUFBO0lBQ3ZDLENBQUM7SUFFRDs7T0FFRztJQUNILEtBQUssQ0FBQyxzQkFBc0IsQ0FDM0IsV0FBcUIsRUFDckIsT0FBcUIsRUFBRTtRQUV2QixPQUFPLE1BQU0sT0FBTyxDQUFDLEdBQUcsQ0FDdkIsV0FBVyxDQUFDLEdBQUcsQ0FBQyxDQUFDLEVBQUUsRUFBRSxFQUFFLENBQUMsSUFBSSxDQUFDLG9CQUFvQixDQUFDLEVBQUUsRUFBRSxJQUFJLENBQUMsQ0FBQyxDQUM1RCxDQUFBO0lBQ0YsQ0FBQztJQUVELEtBQUssQ0FBQyxpQkFBaUIsQ0FBQyxNQUFNLEVBQUUsS0FBNEI7UUFFM0QsTUFBTSxHQUFHLEdBQUcsR0FBRyxJQUFJLENBQUMsSUFBSSxNQUFNLE1BQU0sT0FBTyxDQUFBO1FBQzNDLElBQUksSUFBSSxHQUFRLElBQUksUUFBUSxFQUFFLENBQUE7UUFFOUIsSUFBSSxDQUFDLE1BQU0sQ0FBQyxTQUFTLEVBQUUsS0FBSyxDQUFDLE9BQU8sQ0FBQyxDQUFBO1FBRXJDLEtBQUssQ0FBQyxRQUFRLElBQUksSUFBSSxDQUFDLE1BQU0sQ0FBQyxVQUFVLEVBQUUsS0FBSyxDQUFDLFFBQVEsQ0FBQyxDQUFBO1FBQ3pELEtBQUssQ0FBQyxPQUFPLElBQUksSUFBSSxDQUFDLE1BQU0sQ0FBQyxTQUFTLEVBQUUsS0FBSyxDQUFDLE9BQU8sQ0FBQyxDQUFBO1FBRXRELE9BQU8sTUFBTSxlQUFLLENBQUMsR0FBRyxDQUFDLEdBQUcsRUFBRSxJQUFJLEVBQUU7WUFDakMsT0FBTyxFQUFFO2dCQUNSLFFBQVEsRUFBRSxrQkFBa0I7Z0JBQzVCLGlCQUFpQixFQUFFLGdCQUFnQjtnQkFDbkMsY0FBYyxFQUFFLGtEQUFrRDtnQkFDbEUsU0FBUyxFQUFFLElBQUksQ0FBQyxHQUFHO2dCQUNuQixjQUFjLEVBQUUsSUFBSSxDQUFDLFFBQVE7YUFDN0I7U0FDRCxDQUFDLENBQUE7SUFDSCxDQUFDO0NBMEVEO0FBNXFDRCxnQ0E0cUNDO0FBR00sTUFBTSxRQUFRLEdBQUcsQ0FBQyxNQUF5QixFQUFFLE1BQTJCLGlCQUFpQixFQUFFLEVBQUU7SUFFbkcsT0FBTyxJQUFJLFVBQVUsQ0FBQyxNQUFNLElBQUssSUFBQSxnQ0FBYyxHQUFpQixDQUFDLEdBQUcsQ0FBQyxDQUFDLENBQUE7SUFFdEU7Ozs7Ozs7Ozs7Ozs7O01BY0U7QUFDSCxDQUFDLENBQUE7QUFuQlksUUFBQSxRQUFRLFlBbUJwQiJ9
|