From 2352b47ffdc512327cad7aa0318472fce9f99d50 Mon Sep 17 00:00:00 2001 From: "Stanko K.R." Date: Fri, 19 Sep 2025 16:23:45 +0200 Subject: [PATCH 1/9] Create Integrations and add link unfurling Implement content fetching Originally implemented a SAX parser to avoid the body size limit, but it turns out that Nokogiri doesn't support SAX parsers for HTML5. This was quite confusing because the parser is called HTML::SAX::Parser and everywhere else HTML is aliased to HTML5. I had to abandon this approach after spending a few hours on it, and then opted for the same approach that Campfire uses - fetch the whole body and parse it. But I lowered the body size to 2MB after looking up what the median web page body size is - 160KB 50%%, 285KB 90%%. --- Gemfile | 4 +- app/assets/stylesheets/lexxy.css | 9 + .../basecamps/callbacks_controller.rb | 8 + .../integrations/basecamps_controller.rb | 12 + app/controllers/unfurl_links_controller.rb | 20 + app/helpers/rich_text_helper.rb | 32 + .../controllers/popup_window_controller.js | 7 + .../controllers/unfurl_link_controller.js | 109 ++ app/javascript/models/cookie.js | 58 + app/jobs/integration/basecamp/set_up_job.rb | 5 + app/models/integration.rb | 5 + app/models/integration/basecamp.rb | 96 ++ app/models/link.rb | 28 + app/models/link/basecamp_unfurler.rb | 51 + app/models/link/fetch.rb | 114 ++ app/models/link/fizzy_unfurler.rb | 57 + app/models/link/metadata.rb | 49 + app/models/link/open_graph_unfurler.rb | 54 + app/models/user.rb | 5 + app/models/webhook/delivery.rb | 18 +- .../basecamps/callbacks/show.html.erb | 8 + .../integrations/basecamps/create.html.erb | 7 + app/views/integrations/basecamps/new.html.erb | 8 + config/brakeman.ignore | 23 + config/importmap.rb | 1 + config/routes.rb | 10 + .../20250919122919_create_integrations.rb | 11 + lib/network_guard.rb | 40 + .../basecamps/callbacks_controller_test.rb | 28 + .../integrations/basecamps_controller_test.rb | 39 + test/fixtures/integrations.yml | 7 + test/models/integration/basecamp_test.rb | 99 ++ test/models/link/basecamp_unfurler_test.rb | 75 + test/models/link/fetch_test.rb | 69 + test/models/link/fizzy_unfurler_test.rb | 40 + test/models/link/metadata_test.rb | 30 + test/models/link/open_graph_unfurler_test.rb | 42 + test/models/link_test.rb | 16 + test/vcr_cassettes/link_test-test_unfurl.yml | 1353 +++++++++++++++++ 39 files changed, 2632 insertions(+), 15 deletions(-) create mode 100644 app/controllers/integrations/basecamps/callbacks_controller.rb create mode 100644 app/controllers/integrations/basecamps_controller.rb create mode 100644 app/controllers/unfurl_links_controller.rb create mode 100644 app/javascript/controllers/popup_window_controller.js create mode 100644 app/javascript/controllers/unfurl_link_controller.js create mode 100644 app/javascript/models/cookie.js create mode 100644 app/jobs/integration/basecamp/set_up_job.rb create mode 100644 app/models/integration.rb create mode 100644 app/models/integration/basecamp.rb create mode 100644 app/models/link.rb create mode 100644 app/models/link/basecamp_unfurler.rb create mode 100644 app/models/link/fetch.rb create mode 100644 app/models/link/fizzy_unfurler.rb create mode 100644 app/models/link/metadata.rb create mode 100644 app/models/link/open_graph_unfurler.rb create mode 100644 app/views/integrations/basecamps/callbacks/show.html.erb create mode 100644 app/views/integrations/basecamps/create.html.erb create mode 100644 app/views/integrations/basecamps/new.html.erb create mode 100644 db/migrate/20250919122919_create_integrations.rb create mode 100644 lib/network_guard.rb create mode 100644 test/controllers/integrations/basecamps/callbacks_controller_test.rb create mode 100644 test/controllers/integrations/basecamps_controller_test.rb create mode 100644 test/fixtures/integrations.yml create mode 100644 test/models/integration/basecamp_test.rb create mode 100644 test/models/link/basecamp_unfurler_test.rb create mode 100644 test/models/link/fetch_test.rb create mode 100644 test/models/link/fizzy_unfurler_test.rb create mode 100644 test/models/link/metadata_test.rb create mode 100644 test/models/link/open_graph_unfurler_test.rb create mode 100644 test/models/link_test.rb create mode 100644 test/vcr_cassettes/link_test-test_unfurl.yml diff --git a/Gemfile b/Gemfile index 7f7a463bbb..2ebf2b54cd 100644 --- a/Gemfile +++ b/Gemfile @@ -27,7 +27,7 @@ gem "rqrcode" gem "redcarpet" gem "rouge" gem "jbuilder" -gem "lexxy" +gem "lexxy", bc: "lexxy" gem "image_processing", "~> 1.14" gem "platform_agent" gem "aws-sdk-s3", require: false @@ -63,3 +63,5 @@ group :test do gem "vcr" gem "mocha" end + +gem "oauth2", "~> 2.0" diff --git a/app/assets/stylesheets/lexxy.css b/app/assets/stylesheets/lexxy.css index e3f8f8cbdc..7d63cbe3bb 100644 --- a/app/assets/stylesheets/lexxy.css +++ b/app/assets/stylesheets/lexxy.css @@ -34,6 +34,15 @@ } } + lexxy-editor[data-controller~="unfurl-link"] { + display: flex; + flex-direction: column; + + [data-unfurl-link-target~="linkAccountsPrompt"] { + order: 999; + } + } + .lexxy-dialog-actions { display: flex; font-size: var(--text-x-small); diff --git a/app/controllers/integrations/basecamps/callbacks_controller.rb b/app/controllers/integrations/basecamps/callbacks_controller.rb new file mode 100644 index 0000000000..72d73b9819 --- /dev/null +++ b/app/controllers/integrations/basecamps/callbacks_controller.rb @@ -0,0 +1,8 @@ +class Integrations::Basecamps::CallbacksController < ApplicationController + skip_before_action :require_tenant + allow_unauthenticated_access + + def show + Integration::Basecamp.set_up_later(code: params[:code], state: params[:state]) + end +end diff --git a/app/controllers/integrations/basecamps_controller.rb b/app/controllers/integrations/basecamps_controller.rb new file mode 100644 index 0000000000..2889ceca4b --- /dev/null +++ b/app/controllers/integrations/basecamps_controller.rb @@ -0,0 +1,12 @@ +class Integrations::BasecampsController < ApplicationController + def new + end + + def create + integration = Integration::Basecamp.find_or_create_by(owner: Current.user) + + unless integration.setup? + redirect_to integration.authorization_url, allow_other_host: true + end + end +end diff --git a/app/controllers/unfurl_links_controller.rb b/app/controllers/unfurl_links_controller.rb new file mode 100644 index 0000000000..cbd6bf9a19 --- /dev/null +++ b/app/controllers/unfurl_links_controller.rb @@ -0,0 +1,20 @@ +class UnfurlLinksController < ApplicationController + rate_limit to: 50, within: 1.hour, by: -> { Current.user.id } + + def create + link = Link.unfurl(url_param) + + if link.metadata + render json: link.metadata + else + head :no_content + end + rescue Link::BasecampUnfurler::MissingIntegrationError + render json: { error: :basecamp_integration_not_set_up }, status: :unprocessable_entity + end + + private + def url_param + params.require(:url) + end +end diff --git a/app/helpers/rich_text_helper.rb b/app/helpers/rich_text_helper.rb index c1bc2232a5..d5d1193091 100644 --- a/app/helpers/rich_text_helper.rb +++ b/app/helpers/rich_text_helper.rb @@ -22,4 +22,36 @@ def code_language_picker def general_prompts(board) safe_join([ mentions_prompt(board), cards_prompt, code_language_picker ]) end + + def lexxy_rich_textarea_tag(name, value = nil, options = {}, &block) + options = options.symbolize_keys + unfurl_links = options.key?(:unfurl_links) ? options.delete(:unfurl_links) : true + + if unfurl_links + data = options[:data] ||= {} + + data[:controller] = token_list(data[:controller], "unfurl-link") + data[:unfurl_link_url_value] ||= unfurl_link_path + data[:unfurl_link_set_up_basecamp_integration_url_value] ||= new_basecamp_integration_url + data[:action] = token_list(data[:action], "lexxy:insert-link->unfurl-link#unfurl") + end + + super(name, value, options) do + concat link_unfurling_prompt if unfurl_links + concat capture(&block) if block_given? + end + end + + private + def link_unfurling_prompt + content_tag(:div, hidden: true, class: "flex gap justify-space-between align-center", data: { unfurl_link_target: "linkAccountsPrompt" }) do + concat content_tag(:p, "You can link your Basecamp account to get link previews!") + concat( + content_tag(:div, class: "flex gap") do + concat button_tag("Link accounts", class: "btn", data: { action: "unfurl-link#setUpBasecampIntegration" }) + concat button_tag("Remind me later", class: "btn", data: { action: "unfurl-link#closePrompt", unfurl_link_intent_param: "dismiss" }) + end + ) + end + end end diff --git a/app/javascript/controllers/popup_window_controller.js b/app/javascript/controllers/popup_window_controller.js new file mode 100644 index 0000000000..922ff11536 --- /dev/null +++ b/app/javascript/controllers/popup_window_controller.js @@ -0,0 +1,7 @@ +import { Controller } from "@hotwired/stimulus" + +export default class extends Controller { + close() { + window.close() + } +} \ No newline at end of file diff --git a/app/javascript/controllers/unfurl_link_controller.js b/app/javascript/controllers/unfurl_link_controller.js new file mode 100644 index 0000000000..e31cc23743 --- /dev/null +++ b/app/javascript/controllers/unfurl_link_controller.js @@ -0,0 +1,109 @@ +import { Controller } from "@hotwired/stimulus"; +import { post } from "@rails/request.js" +import Cookie from "models/cookie" + +export default class extends Controller { + static targets = [ "linkAccountsPrompt" ] + static values = { + url: String, + setUpBasecampIntegrationUrl: String + } + + static MAX_DISMISSAL_COUNT = 3 + static DISMISSAL_COUNTER_KEY = "basecamp_integration_dimissal_count" + + #accountLinkingDismissed + + unfurl(event) { + this.#unfurlLink(event.detail.url, event.detail) + } + + setUpBasecampIntegration() { + this.#openPopup(this.setUpBasecampIntegrationUrlValue, { widht: 400, height: 600 }) + } + + closePrompt(event) { + this.linkAccountsPromptTarget.hidden = true + + if (event.params.intent === "dismiss") { + this.#incrementDismissalCounter() + } + } + + async #unfurlLink(url, callbacks) { + const { response } = await post( + this.urlValue, + { + body: JSON.stringify({ url }), + headers: { + "Content-Type": "application/json", + "Accept": "application/json" + } + } + ) + + let metadata = null + + if (response.status !== 204) { + metadata = await response.json() + } + + if (metadata?.error) { + this.#handleError(metadata) + } else if (metadata) { + this.#insertUnfurledLink(metadata, callbacks) + } + } + + #insertUnfurledLink(metadata, callbacks) { + callbacks.replaceLinkWith(this.#renderUnfurledLinkHTML(metadata)) + } + + #renderUnfurledLinkHTML(metadata) { + return `${metadata.title}` + } + + #handleError({ error, ...data }) { + switch (error) { + case "basecamp_integration_not_set_up": + if (this.#shouldShowAccountLinkingPrompt()) { + this.#promptBasecampIntegrationSetUp() + } + break; + default: + throw new Error(`Unknown API error: ${error}`) + break; + } + } + + #promptBasecampIntegrationSetUp() { + this.linkAccountsPromptTarget.hidden = false + } + + #openPopup(url, options, onClose) { + const { width, height } = options + const left = (window.screen.width - width) / 2 + const top = (window.screen.height - height) / 2 + + window.open( + url, + "_blank", + `width=${width},height=${height},left=${left},top=${top},resizable=yes,scrollbars=yes,toolbar=no,menubar=no,location=no,status=no` + ) + } + + #incrementDismissalCounter() { + this.#cookie.increment(this.constructor.DISMISSAL_COUNTER_KEY) + this.#accountLinkingDismissed = true + } + + #shouldShowAccountLinkingPrompt() { + const dismissalCount = this.#cookie.get(this.constructor.DISMISSAL_COUNTER_KEY) || 0 + return !this.#accountLinkingDismissed && (dismissalCount < this.constructor.MAX_DISMISSAL_COUNT) + } + + get #cookie() { + const name = `link-accounts-prompt-${Current.user.id}` + return Cookie.find(name) || new Cookie(name) + } +} diff --git a/app/javascript/models/cookie.js b/app/javascript/models/cookie.js new file mode 100644 index 0000000000..2e2d6b7e78 --- /dev/null +++ b/app/javascript/models/cookie.js @@ -0,0 +1,58 @@ +export default class Cookie { + static DEFAULT_EXPIRATION = 20 * 365 * 24 * 60 * 60 * 1000 + + static find(name) { + const value = document.cookie + .split("; ") + .find(row => row.startsWith(`${name}=`)) + ?.split("=")[1] + + if (!value) return null + + try { + const data = JSON.parse(decodeURIComponent(value)) + return new Cookie(name, data) + } catch { + return new Cookie(name, { value: decodeURIComponent(value) }) + } + } + + constructor(name, data = {}, options = {}) { + this.name = name + this.data = data + this.options = options + } + + get(key) { + return this.data[key] + } + + set(key, value) { + this.data[key] = value + this.save() + } + + increment(key, amount = 1) { + const currentValue = this.data[key] || 0 + + try { + this.set(key, currentValue + amount) + } catch { + this.set(key, amount) + } + } + + save() { + const value = encodeURIComponent(JSON.stringify(this.data)) + const defaultExpires = new Date(Date.now() + this.constructor.DEFAULT_EXPIRATION) + const expires = `; expires=${(this.options.expires || defaultExpires).toUTCString()}` + const path = `; path=${this.options.path || "/"}` + const sameSite = this.options.sameSite ? `; SameSite=${this.options.sameSite}` : "; SameSite=Lax" + + document.cookie = `${this.name}=${value}${expires}${path}${sameSite}` + } + + delete() { + document.cookie = `${this.name}=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/` + } +} diff --git a/app/jobs/integration/basecamp/set_up_job.rb b/app/jobs/integration/basecamp/set_up_job.rb new file mode 100644 index 0000000000..96de96c6c3 --- /dev/null +++ b/app/jobs/integration/basecamp/set_up_job.rb @@ -0,0 +1,5 @@ +class Integration::Basecamp::SetUpJob < ApplicationJob + def perform(code:, state:) + Integration::Basecamp.set_up(code: code, state: state) + end +end diff --git a/app/models/integration.rb b/app/models/integration.rb new file mode 100644 index 0000000000..1286c6db66 --- /dev/null +++ b/app/models/integration.rb @@ -0,0 +1,5 @@ +class Integration < ApplicationRecord + belongs_to :owner, class_name: "User" + + store :data, coder: JSON +end diff --git a/app/models/integration/basecamp.rb b/app/models/integration/basecamp.rb new file mode 100644 index 0000000000..3efa4e52b6 --- /dev/null +++ b/app/models/integration/basecamp.rb @@ -0,0 +1,96 @@ +class Integration::Basecamp < Integration + AUTH_URL_STATE_EXPIRATION = 30.minutes + AUTH_URL_STATE_PURPOSE = "setup-basecamp-integration".freeze + + store_accessor :data, :access_token, :refresh_token + + class << self + def set_up_later(code:, state:) + Integration::Basecamp::SetUpJob.perform_later(code: code, state: state) + end + + def set_up(code:, state:) + with_tenant_from_state(state) do + integration = locate_by_state(state) + integration.set_up(code) unless integration.setup? + end + end + + private + def locate_by_state(state) + GlobalID::Locator.locate_signed(state, for: AUTH_URL_STATE_PURPOSE) + end + + def with_tenant_from_state(state, &block) + sgid = SignedGlobalID.parse(state, for: AUTH_URL_STATE_PURPOSE) + with_tenant(sgid.tenant, &block) + end + end + + def authorization_url + oauth_client + .auth_code + .authorize_url( + redirect_uri: return_url, + state: as_state + ) + .sub("response_type=code", "type=web_server") + end + + def set_up(authorization_code) + access_token = oauth_client.auth_code.get_token(authorization_code, token_method: :post, redirect_uri: return_url, type: :web_server) + + self.access_token = access_token.token + self.refresh_token = access_token.refresh_token + + save! + end + + def setup? + access_token.present? && refresh_token.present? + end + + def refresh_tokens + if refresh_token.present? + response = oauth_client.request( + :post, + "/authorization/token", + body: { + refresh_token: refresh_token, + type: "refresh", + client_id: credentials[:client_id], + client_secret: credentials[:client_secret] + } + ).parsed + + self.access_token = response["access_token"] + + save! + end + end + + private + def oauth_client + @oauth_client ||= OAuth2::Client.new( + credentials[:client_id], + credentials[:client_secret], + site: credentials[:oauth_server_url], + authorize_url: "/authorization/new", + token_url: "/authorization/token", + auth_scheme: :request_body + ) + end + + def credentials + Rails.application.credentials.integrations.basecamp + end + + def return_url + options = Rails.application.config.action_controller.default_url_options.merge(script_name: nil) + Rails.application.routes.url_helpers.basecamp_integration_callback_url(**options) + end + + def as_state + to_sgid(expires_in: AUTH_URL_STATE_EXPIRATION, for: AUTH_URL_STATE_PURPOSE).to_s + end +end diff --git a/app/models/link.rb b/app/models/link.rb new file mode 100644 index 0000000000..316f65ee88 --- /dev/null +++ b/app/models/link.rb @@ -0,0 +1,28 @@ +class Link + UNFURLERS = [ + FizzyUnfurler, + BasecampUnfurler + ] + + attr_reader :uri, :metadata + + def self.unfurl(url, **options) + new(url).unfurl(**options) + end + + def initialize(url) + @uri = URI.parse(url) + @metadata = nil + end + + def unfurl(**options) + options[:user] = Current.user unless options.key?(:user) + unfurler = UNFURLERS.find { |unfurler| unfurler.unfurls?(uri) } + + if unfurler + @metadata = unfurler.new(uri, **options).unfurl + end + + self + end +end diff --git a/app/models/link/basecamp_unfurler.rb b/app/models/link/basecamp_unfurler.rb new file mode 100644 index 0000000000..5fde6d83a2 --- /dev/null +++ b/app/models/link/basecamp_unfurler.rb @@ -0,0 +1,51 @@ +class Link::BasecampUnfurler < Link::OpenGraphUnfurler + class MissingIntegrationError < StandardError; end + + def self.unfurls?(uri) + uri.host.ends_with?(".basecamp.com") || uri.host.ends_with?(".basecamp.localhost") + rescue URI::InvalidURIError + false + end + + def unfurl + if integration.present? + fetch_metadata + else + raise MissingIntegrationError + end + end + + private + def fetch_metadata + retrying = false + + begin + fetch = Link::Fetch.new(uri, headers: headers) + + if fetch.http_url? && fetch.html_content? + document = Nokogiri::HTML5(fetch.content) + Link::Metadata.new(**extract_metadata_from_document(document)) + end + rescue Link::Fetch::UnsuccesfulRequestError => e + if retrying + raise + elsif e.response.is_a?(Net::HTTPUnauthorized) + integration.refresh_tokens + retrying = true + retry + end + end + end + + def headers + { "Authorization" => "Bearer #{integration.access_token}" } + end + + def integration + @integration ||= begin + integration = user.integrations.with_basecamp + raise(MissingIntegrationError) if integration.nil? || !integration.setup? + integration + end + end +end diff --git a/app/models/link/fetch.rb b/app/models/link/fetch.rb new file mode 100644 index 0000000000..96a77c8650 --- /dev/null +++ b/app/models/link/fetch.rb @@ -0,0 +1,114 @@ +class Link::Fetch + class Error < StandardError; end + class TooManyRedirectsError < Error; end + class RedirectDeniedError < Error; end + class BodyTooLargeError < Error; end + class UnsuccesfulRequestError < Error + attr_reader :response + + def initialize(response) + @response = response + super("HTTP response code: #{response.code}") + end + end + + DEFAULT_USER_AGENT = "Mozilla/5.0 (compatible; FizzyLinkUnfurler/1.0.0)".freeze + MAX_BODY_SIZE = 2.megabytes + MAX_REDIRECTS = 10 + DNS_RESOLUTION_TIMEOUT = 2.seconds + + attr_reader :uri, :headers, :max_body_size, :dns_resolution_timeout + + def initialize(url, headers: {}, max_body_size: MAX_BODY_SIZE, dns_resolution_timeout: DNS_RESOLUTION_TIMEOUT) + @uri = URI.parse(url) + @headers = default_headers.merge(headers) + @max_body_size = max_body_size + @dns_resolution_timeout = dns_resolution_timeout + end + + def http_url? + uri.is_a?(URI::HTTP) + end + + def html_content? + content_type&.starts_with? "text/html" + end + + def content_type + request uri, Net::HTTP::Head do |response| + if response.is_a?(Net::HTTPSuccess) + return response["Content-Type"] + else + raise UnsuccesfulRequestError, response + end + end + end + + def content + request uri, Net::HTTP::Get do |response| + if response.is_a?(Net::HTTPSuccess) + body_size = 0 + buffer = StringIO.new + + response.read_body do |chunk| + body_size += chunk.bytesize + + if body_size <= max_body_size + buffer << chunk + else + raise BodyTooLargeError + end + end + + return buffer.string + else + raise UnsuccesfulRequestError, response + end + end + end + + private + def request(uri, request_class, ip_address: nil) + ip_address ||= resolve_ip_address(uri.host) + + MAX_REDIRECTS.times do + Net::HTTP.start(uri.host, uri.port, ipaddr: ip_address, use_ssl: uri.scheme == "https") do |http| + request = request_class.new(uri) + + headers.each do |header, value| + request[header] = value + end + + http.request(request) do |response| + if response.is_a?(Net::HTTPRedirection) + uri, ip_address = resolve_redirect(response["location"]) + else + yield response + end + end + end + end + + raise TooManyRedirectsError + end + + def default_headers + { + "Accept" => "text/html,application/xhtml+xml", + "User-Agent" => DEFAULT_USER_AGENT + } + end + + def resolve_redirect(location) + uri = URI.parse(location) + raise RedirectDeniedError unless uri.is_a?(URI::HTTP) + + [ uri, resolve_ip_address(uri.host) ] + rescue NetworkGuard::RestrictedHostError + raise RedirectDeniedError + end + + def resolve_ip_address(hostname) + NetworkGuard.resolve(hostname, timeout: dns_resolution_timeout).sample + end +end diff --git a/app/models/link/fizzy_unfurler.rb b/app/models/link/fizzy_unfurler.rb new file mode 100644 index 0000000000..a328f06a37 --- /dev/null +++ b/app/models/link/fizzy_unfurler.rb @@ -0,0 +1,57 @@ +class Link::FizzyUnfurler + class << self + def unfurls?(uri) + uri.host == fizzy_host && uri.port == fizzy_port + rescue URI::InvalidURIError + false + end + + private + def fizzy_host + url_options[:host] + end + + def fizzy_port + url_options[:port] + end + + def url_options + Rails.application.config.action_mailer.default_url_options + end + end + + attr_reader :uri, :user + + def initialize(uri, user:, **) + @uri = uri + @user = user + end + + def unfurl + tenant, path = extract_tenant_from_path(uri.path) + + if tenant == ApplicationRecord.current_tenant + target = Rails.application.routes.recognize_path(path) rescue {} + + case target + in { controller: "cards", action: "show", id: id } then unfurl_card(id) + else nil + end + end + end + + private + def extract_tenant_from_path(path) + parts = path.match(%r{\A/(?\d+)(?.+)\Z}) + + [ parts[:tenant], parts[:path] ] + end + + def unfurl_card(id) + card = user.accessible_cards.find_by(id: id) + + if card + Link::Metadata.new(title: card.title, canonical_url: uri.to_s) + end + end +end diff --git a/app/models/link/metadata.rb b/app/models/link/metadata.rb new file mode 100644 index 0000000000..61ffa8f604 --- /dev/null +++ b/app/models/link/metadata.rb @@ -0,0 +1,49 @@ +class Link::Metadata + include ActionView::Helpers::SanitizeHelper + + attr_reader :title, :description, :image_url, :canonical_url, + :unsafe_title, :unsafe_description, :unsafe_image_url, :unsafe_canonical_url + + def initialize(**attributes) + @unsafe_canonical_url = attributes[:canonical_url] + @canonical_url = sanitize_url(@unsafe_canonical_url) + + @unsafe_title = attributes[:title] + @title = sanitize_text(@unsafe_title) + + @unsafe_description = attributes[:description] + @description = sanitize_text(@unsafe_description) + + @unsafe_image_url = attributes[:image_url] + @image_url = sanitize_url(absolute_uri(@unsafe_image_url, relative_to: @canonical_url)) + end + + private + def sanitize_text(content) + sanitize(strip_tags(content)) + end + + def sanitize_url(url, relative_to: nil) + uri = URI.parse(url) + + if uri.is_a?(URI::HTTP) && uri.absolute? + uri.to_s + else + nil + end + rescue URI::InvalidURIError + nil + end + + def absolute_uri(url, relative_to:) + uri = URI.parse(url) + + if uri.absolute? + uri + else + URI.parse(relative_to) + uri + end + rescue URI::InvalidURIError + nil + end +end diff --git a/app/models/link/open_graph_unfurler.rb b/app/models/link/open_graph_unfurler.rb new file mode 100644 index 0000000000..cda834a0a8 --- /dev/null +++ b/app/models/link/open_graph_unfurler.rb @@ -0,0 +1,54 @@ +class Link::OpenGraphUnfurler + attr_reader :uri, :user + + def self.unfurls?(uri) + uri.is_a?(URI::HTTP) + end + + def initialize(uri, user: nil, **options) + @uri = uri + @user = user + end + + def unfurl + fetch = Link::Fetch.new(uri) + + if fetch.http_url? && fetch.html_content? + content = fetch.content + document = Nokogiri::HTML5(content) + Link::Metadata.new(**extract_metadata_from_document(document)) + end + end + + private + def extract_metadata_from_document(document) + Hash.new.tap do |metadata| + metadata[:canonical_url] = extract_canonical_url_from_document(document) || uri.to_s + metadata[:title] = extract_title_from_document(document) + metadata[:description] = extract_description_from_document(document) + metadata[:image_url] = extract_image_url_from_document(document) + end + end + + def extract_canonical_url_from_document(document) + document.at_css('meta[property="og:url"]')&.get_attribute("content") || + document.at_css('link[rel="canonical"]')&.get_attribute("href") + end + + def extract_title_from_document(document) + document.at_css('meta[property="og:title"]')&.get_attribute("content") || + document.at_css('meta[name="twitter:title"]')&.get_attribute("content") || + document.at_css("title")&.text&.strip + end + + def extract_description_from_document(document) + document.at_css('meta[property="og:description"]')&.get_attribute("content") || + document.at_css('meta[name="twitter:description"]')&.get_attribute("content") || + document.at_css('meta[name="description"]')&.get_attribute("content") + end + + def extract_image_url_from_document(document) + document.at_css('meta[property="og:image"]')&.get_attribute("content") || + document.at_css('meta[name="twitter:image"]')&.get_attribute("content") + end +end diff --git a/app/models/user.rb b/app/models/user.rb index 68df97a65e..90929e29a2 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -17,6 +17,11 @@ class User < ApplicationRecord has_many :pins, dependent: :destroy has_many :pinned_cards, through: :pins, source: :card has_many :exports, class_name: "Account::Export", dependent: :destroy + has_many :integrations, foreign_key: :owner_id, inverse_of: :owner, dependent: :destroy do + def with_basecamp + find_by(type: "Integration::Basecamp") + end + end scope :with_avatars, -> { preload(:account, :avatar_attachment) } diff --git a/app/models/webhook/delivery.rb b/app/models/webhook/delivery.rb index ce69c01add..79fcd0f0f2 100644 --- a/app/models/webhook/delivery.rb +++ b/app/models/webhook/delivery.rb @@ -54,8 +54,8 @@ def succeeded? private def perform_request - if private_uri? - { error: :private_uri } + if restricted_uri? + { error: :restricted_uri } else response = http.request( Net::HTTP::Post.new(uri, headers).tap { |request| request.body = payload } @@ -73,18 +73,8 @@ def perform_request { error: :failed_tls } end - def private_uri? - ip_addresses = [] - - Resolv::DNS.open(timeouts: DNS_RESOLUTION_TIMEOUT) do |dns| - dns.each_address(uri.host) do |ip_address| - ip_addresses << IPAddr.new(ip_address) - end - end - - ip_addresses.any? do |ip| - ip.private? || ip.loopback? || ip.link_local? || ip.ipv4_mapped? || DISALLOWED_IP_RANGES.any? { |range| range.include?(ip) } - end + def restricted_uri? + NetworkGuard.restricted_host?(uri.host, timeout: DNS_RESOLUTION_TIMEOUT) end def uri diff --git a/app/views/integrations/basecamps/callbacks/show.html.erb b/app/views/integrations/basecamps/callbacks/show.html.erb new file mode 100644 index 0000000000..f1fc2629cb --- /dev/null +++ b/app/views/integrations/basecamps/callbacks/show.html.erb @@ -0,0 +1,8 @@ +<% @hide_footer_frames = true %> + +
+

Your accounts are now linked!

+

From now on, any Basecamp link you paste in will get a preview.

+ + +
diff --git a/app/views/integrations/basecamps/create.html.erb b/app/views/integrations/basecamps/create.html.erb new file mode 100644 index 0000000000..615201fb75 --- /dev/null +++ b/app/views/integrations/basecamps/create.html.erb @@ -0,0 +1,7 @@ +<% @hide_footer_frames = true %> + +
+

You have already setup your Basecamp integration

+ + +
diff --git a/app/views/integrations/basecamps/new.html.erb b/app/views/integrations/basecamps/new.html.erb new file mode 100644 index 0000000000..5d916c986f --- /dev/null +++ b/app/views/integrations/basecamps/new.html.erb @@ -0,0 +1,8 @@ +<% @hide_footer_frames = true %> + +
+

We will redirect you to Basecamp to login.

+ + <%= button_to "Link your Basecamp Account", basecamp_integration_path, method: :post, data: { turbo: false }, class: "btn" %> + +
diff --git a/config/brakeman.ignore b/config/brakeman.ignore index 6c99b817b6..0209c3033a 100644 --- a/config/brakeman.ignore +++ b/config/brakeman.ignore @@ -46,6 +46,29 @@ ], "note": "" }, + { + "warning_type": "Redirect", + "warning_code": 18, + "fingerprint": "a8fc3f4864b089c35f52d7d5607a60f70aedb99ba46b9b0f2ca41ddc8386a03c", + "check_name": "Redirect", + "message": "Possible unprotected redirect", + "file": "app/controllers/integrations/basecamps_controller.rb", + "line": 9, + "link": "https://brakemanscanner.org/docs/warning_types/redirect/", + "code": "redirect_to(Integration::Basecamp.find_or_create_by(:owner => Current.user).authorization_url, :allow_other_host => true)", + "render_path": null, + "location": { + "type": "method", + "class": "Integrations::BasecampsController", + "method": "create" + }, + "user_input": "Integration::Basecamp.find_or_create_by(:owner => Current.user).authorization_url", + "confidence": "Weak", + "cwe_id": [ + 601 + ], + "note": "" + }, { "warning_type": "Mass Assignment", "warning_code": 70, diff --git a/config/importmap.rb b/config/importmap.rb index 6988153410..b8179b189c 100644 --- a/config/importmap.rb +++ b/config/importmap.rb @@ -9,6 +9,7 @@ pin_all_from "app/javascript/controllers", under: "controllers" pin_all_from "app/javascript/helpers", under: "helpers" pin_all_from "app/javascript/initializers", under: "initializers" +pin_all_from "app/javascript/models", under: "models" pin "marked" # @15.0.11 pin "lexxy" pin "@rails/activestorage", to: "activestorage.esm.js" diff --git a/config/routes.rb b/config/routes.rb index bbce4fe01d..0a654923af 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -186,6 +186,8 @@ end end + resource :unfurl_link, only: :create + namespace :public do resources :boards do scope module: :boards do @@ -202,6 +204,14 @@ end end + scope :integrations, module: :integrations do + resource :basecamp, only: %i[ new create destroy ], as: :basecamp_integration do + scope module: :basecamps do + resource :callback, only: :show + end + end + end + direct :published_board do |board, options| route_for :public_board, board.publication.key end diff --git a/db/migrate/20250919122919_create_integrations.rb b/db/migrate/20250919122919_create_integrations.rb new file mode 100644 index 0000000000..99ed32b70d --- /dev/null +++ b/db/migrate/20250919122919_create_integrations.rb @@ -0,0 +1,11 @@ +class CreateIntegrations < ActiveRecord::Migration[8.1] + def change + create_table :integrations do |t| + t.text :data + t.string :type + t.belongs_to :owner, null: false, foreign_key: { to_table: :users } + + t.timestamps + end + end +end diff --git a/lib/network_guard.rb b/lib/network_guard.rb new file mode 100644 index 0000000000..ccb6ecc56c --- /dev/null +++ b/lib/network_guard.rb @@ -0,0 +1,40 @@ +module NetworkGuard + class RestrictedHostError < StandardError; end + + extend self + + RESTRICTED_IP_RANGES = [ + # IPv4 mapped to IPv6 + IPAddr.new("::ffff:0:0/96"), + # Broadcasts + IPAddr.new("0.0.0.0/8") + ].freeze + + def restricted_host?(hostname, **options) + resolve(hostname, **options).any? + rescue RestrictedHostError + false + end + + def resolve(hostname, timeout: nil) + ip_addresses = [] + + Resolv::DNS.open(timeouts: timeout) do |dns| + dns.each_address(hostname) do |ip_address| + ip_addresses << IPAddr.new(ip_address) + end + end + + if ip_addresses.any? { |ip_address| restricted_ip_address?(ip_address) } + raise RestrictedHostError + else + ip_addresses + end + end + + def restricted_ip_address?(ip_address) + ip_address.private? || + ip_address.loopback? || + DISALLOWED_IP_RANGES.any? { |range| range.include?(ip_address) } + end +end diff --git a/test/controllers/integrations/basecamps/callbacks_controller_test.rb b/test/controllers/integrations/basecamps/callbacks_controller_test.rb new file mode 100644 index 0000000000..45a678d244 --- /dev/null +++ b/test/controllers/integrations/basecamps/callbacks_controller_test.rb @@ -0,0 +1,28 @@ +require "test_helper" + +class Integrations::Basecamps::CallbacksControllerTest < ActionDispatch::IntegrationTest + setup do + @original_url_options = Rails.application.config.action_controller.default_url_options + Rails.application.config.action_controller.default_url_options = { host: "example.com" } + end + + teardown do + Rails.application.config.action_controller.default_url_options = @original_url_options + end + + test "show enqueues job to set up integration" do + integration = integrations(:kevins_basecamp) + integration.update!(data: {}) + + state = integration.to_sgid( + expires_in: Integration::Basecamp::AUTH_URL_STATE_EXPIRATION, + for: Integration::Basecamp::AUTH_URL_STATE_PURPOSE + ).to_s + + assert_enqueued_with(job: Integration::Basecamp::SetUpJob, args: [ { code: "test_code", state: state } ]) do + get basecamp_integration_callback_path, params: { code: "test_code", state: state } + end + + assert_response :success + end +end diff --git a/test/controllers/integrations/basecamps_controller_test.rb b/test/controllers/integrations/basecamps_controller_test.rb new file mode 100644 index 0000000000..14c13c0154 --- /dev/null +++ b/test/controllers/integrations/basecamps_controller_test.rb @@ -0,0 +1,39 @@ +require "test_helper" + +class Integrations::BasecampsControllerTest < ActionDispatch::IntegrationTest + setup do + sign_in_as :kevin + @original_url_options = Rails.application.config.action_controller.default_url_options + Rails.application.config.action_controller.default_url_options = { host: "example.com" } + end + + teardown do + Rails.application.config.action_controller.default_url_options = @original_url_options + end + + test "new" do + get new_basecamp_integration_path + assert_response :success + end + + test "create" do + sign_in_as :kevin + + assert_no_difference "Integration::Basecamp.count" do + post basecamp_integration_path + end + + assert_response :success, "Renders the 'integration already setup' screen" + + users(:kevin).integrations.delete_all + + assert_difference "Integration::Basecamp.count", 1 do + post basecamp_integration_path + end + + integration = Integration::Basecamp.last + assert_equal users(:kevin), integration.owner + assert_response :redirect, "Redirects to launchpad" + assert_match %r{launchpad\.localhost:3011/authorization/new}, response.redirect_url + end +end diff --git a/test/fixtures/integrations.yml b/test/fixtures/integrations.yml new file mode 100644 index 0000000000..deaa98d2b3 --- /dev/null +++ b/test/fixtures/integrations.yml @@ -0,0 +1,7 @@ +kevins_basecamp: + type: "Integration::Basecamp" + owner: kevin + data: '<%= { + access_token: "BAhbB0kiAjoBeyJjbGllbnRfaWQiOiI5ZDJmZjU2NDM4YTg1MTNjMjAwNDQ3NTJkMWYzNjE2ZTA3NmUwYTU3IiwiZXhwaXJlc19hdCI6IjIwMjUtMTAtMDZUMTM6MjM6NDlaIiwidXNlcl9pZHMiOlsyNjIxOTgyNTAsMTI2NTEwMDgzLDk0OTc3OTE2NSw5NTA3NTg3OSw4NjgyMTE0NDEsNzA3NTQwNDE1LDIwOTUwMDc0Miw2NDUzOTU2OTAsODc5OTczNzU2LDk3NjAyNDY4LDkwMDk4Nzc1OSwxMTg0NzE1MDQsMjQ5MTUxMjgzLDQwMDE1OTMwNCw4MDk5MDk3Nl0sInZlcnNpb24iOjEsImFwaV9kZWFkYm9sdCI6ImRjOTUzYmYwMDdmMTQyNGY0YWYyN2FjMzI0NGYyZjYzIn0GOgZFVEl1OglUaW1lDc1kH8CpNxZfCToNbmFub19udW1pAjICOg1uYW5vX2RlbmkGOg1zdWJtaWNybyIHViA6CXpvbmVJIghVVEMGOwBG--56891137f381109fc1c075ca95d871b529040fba", + refresh_token: "BAhbB0kiAjoBeyJjbGllbnRfaWQiOiI5ZDJmZjU2NDM4YTg1MTNjMjAwNDQ3NTJkMWYzNjE2ZTA3NmUwYTU3IiwiZXhwaXJlc19hdCI6IjIwMzUtMDktMjJUMTM6MjM6NDlaIiwidXNlcl9pZHMiOlsyNjIxOTgyNTAsMTI2NTEwMDgzLDk0OTc3OTE2NSw5NTA3NTg3OSw4NjgyMTE0NDEsNzA3NTQwNDE1LDIwOTUwMDc0Miw2NDUzOTU2OTAsODc5OTczNzU2LDk3NjAyNDY4LDkwMDk4Nzc1OSwxMTg0NzE1MDQsMjQ5MTUxMjgzLDQwMDE1OTMwNCw4MDk5MDk3Nl0sInZlcnNpb24iOjEsImFwaV9kZWFkYm9sdCI6ImRjOTUzYmYwMDdmMTQyNGY0YWYyN2FjMzI0NGYyZjYzIn0GOgZFVEl1OglUaW1lDc3iIcAlWBZfCToNbmFub19udW1pAX46DW5hbm9fZGVuaQY6DXN1Ym1pY3JvIgcSYDoJem9uZUkiCFVUQwY7AEY=--c94d9dee4fe6d86c5f30cc1b6b2352c142676557" + }.to_json %>' diff --git a/test/models/integration/basecamp_test.rb b/test/models/integration/basecamp_test.rb new file mode 100644 index 0000000000..d3b11daa7c --- /dev/null +++ b/test/models/integration/basecamp_test.rb @@ -0,0 +1,99 @@ +require "test_helper" + +class Integration::BasecampTest < ActiveSupport::TestCase + setup do + @integration = integrations(:kevins_basecamp) + @original_url_options = Rails.application.config.action_controller.default_url_options + Rails.application.config.action_controller.default_url_options = { host: "example.com" } + end + + teardown do + Rails.application.config.action_controller.default_url_options = @original_url_options + end + + test "setup?" do + assert @integration.setup? + + @integration.access_token = nil + @integration.refresh_token = nil + + assert_not @integration.setup? + end + + test "authorization_url" do + url = @integration.authorization_url + + assert_match %r{launchpad\.localhost:3011/authorization/new}, url + assert_match(/client_id=/, url) + assert_match(/redirect_uri=/, url) + assert_match(/state=/, url) + assert_match(/type=web_server/, url) + end + + test "refresh_tokens" do + credentials = Rails.application.credentials.integrations.basecamp + + stub_request(:post, "#{credentials[:oauth_server_url]}/authorization/token") + .to_return( + status: 200, + body: { access_token: "refreshed_access_token" }.to_json, + headers: { "Content-Type" => "application/json" } + ) + + @integration.refresh_tokens + + assert_equal "refreshed_access_token", @integration.access_token + + @integration.update!(data: { access_token: "original_token" }) + @integration.refresh_tokens + assert_equal "original_token", @integration.reload.access_token + end + + test "set_up_later" do + state = @integration.to_sgid( + expires_in: Integration::Basecamp::AUTH_URL_STATE_EXPIRATION, + for: Integration::Basecamp::AUTH_URL_STATE_PURPOSE + ).to_s + + assert_enqueued_with(job: Integration::Basecamp::SetUpJob, args: [ { code: "code", state: state } ]) do + Integration::Basecamp.set_up_later(code: "code", state: state) + end + end + + test "set_up" do + @integration.update!(data: {}) + credentials = Rails.application.credentials.integrations.basecamp + + state = @integration.to_sgid( + expires_in: Integration::Basecamp::AUTH_URL_STATE_EXPIRATION, + for: Integration::Basecamp::AUTH_URL_STATE_PURPOSE + ).to_s + + stub_request(:post, "#{credentials[:oauth_server_url]}/authorization/token") + .to_return( + status: 200, + body: { + access_token: "new_access_token", + refresh_token: "new_refresh_token" + }.to_json, + headers: { "Content-Type" => "application/json" } + ) + + Integration::Basecamp.set_up(code: "code", state: state) + + assert @integration.reload.setup? + assert_equal "new_access_token", @integration.access_token + assert_equal "new_refresh_token", @integration.refresh_token + + state = @integration.to_sgid( + expires_in: Integration::Basecamp::AUTH_URL_STATE_EXPIRATION, + for: Integration::Basecamp::AUTH_URL_STATE_PURPOSE + ).to_s + + original_access_token = @integration.access_token + + Integration::Basecamp.set_up(code: "code", state: state) + + assert_equal original_access_token, @integration.reload.access_token, "Set up is skipped if the integration already is setup" + end +end diff --git a/test/models/link/basecamp_unfurler_test.rb b/test/models/link/basecamp_unfurler_test.rb new file mode 100644 index 0000000000..7490a31f4d --- /dev/null +++ b/test/models/link/basecamp_unfurler_test.rb @@ -0,0 +1,75 @@ +require "test_helper" + +class Link::BasecampUnfurlerTest < ActiveSupport::TestCase + test "unfurls?" do + assert Link::BasecampUnfurler.unfurls?(URI.parse("https://3.basecamp.com/123/projects/456")) + assert Link::BasecampUnfurler.unfurls?(URI.parse("https://classic.basecamp.com/999/todos/123")) + assert Link::BasecampUnfurler.unfurls?(URI.parse("https://3.basecamp.localhost:3001/test")) + + assert_not Link::BasecampUnfurler.unfurls?(URI.parse("https://example.com/page")) + assert_not Link::BasecampUnfurler.unfurls?(URI.parse("https://notbasecamp.com/page")) + end + + test "unfurl" do + url = "https://3.basecamp.com/123/projects/456" + user_with_basecamp_integration = users(:kevin) + integration = user_with_basecamp_integration.integrations.with_basecamp + user_without_basecamp_integration = users(:jz) + + stub_request(:head, url) + .with(headers: { "Authorization" => "Bearer #{integration.access_token}" }) + .to_return(status: 200, headers: { "Content-Type" => "text/html" }) + + stub_request(:get, url) + .with(headers: { "Authorization" => "Bearer #{integration.access_token}" }) + .to_return(status: 200, body: <<~HTML) + + + Basecamp Project + + + HTML + + metadata = Link::BasecampUnfurler.new(URI.parse(url), user: user_with_basecamp_integration).unfurl + + assert_equal "Basecamp Project", metadata.title + + assert_raises(Link::BasecampUnfurler::MissingIntegrationError) do + Link::BasecampUnfurler.new(URI.parse(url), user: user_without_basecamp_integration).unfurl + end + end + + test "unfurl with an expired access token" do + url = "https://3.basecamp.com/123/projects/456" + user = users(:kevin) + integration = user.integrations.with_basecamp + original_token = integration.access_token + + stub_request(:head, url) + .with(headers: { "Authorization" => "Bearer #{original_token}" }) + .to_return(status: 401) + + stub_request(:head, url) + .with(headers: { "Authorization" => "Bearer refreshed_token" }) + .to_return(status: 200, headers: { "Content-Type" => "text/html" }) + + stub_request(:get, url) + .with(headers: { "Authorization" => "Bearer refreshed_token" }) + .to_return(status: 200, body: <<~HTML) + + + Refreshed Access + + + HTML + + Integration::Basecamp.any_instance.expects(:refresh_tokens).once.with do |a| + Integration::Basecamp.any_instance.stubs(:access_token).returns("refreshed_token") + true + end + + metadata = Link::BasecampUnfurler.new(URI.parse(url), user: user).unfurl + + assert_equal "Refreshed Access", metadata.title + end +end diff --git a/test/models/link/fetch_test.rb b/test/models/link/fetch_test.rb new file mode 100644 index 0000000000..acf6ee84a6 --- /dev/null +++ b/test/models/link/fetch_test.rb @@ -0,0 +1,69 @@ +require "test_helper" + +class Link::FetchTest < ActiveSupport::TestCase + test "http_url?" do + fetch = Link::Fetch.new("https://example.com/page") + assert fetch.http_url? + + non_http_fetch = Link::Fetch.new("ftp://example.com/file") + assert_not non_http_fetch.http_url? + end + + test "html_content?" do + fetch = Link::Fetch.new("https://example.com/page") + + stub_request(:head, "https://example.com/page") + .to_return(status: 200, headers: { "Content-Type" => "text/html; charset=utf-8" }) + + assert fetch.html_content? + + stub_request(:head, "https://example.com/image") + .to_return(status: 200, headers: { "Content-Type" => "image/jpeg" }) + + image_fetch = Link::Fetch.new("https://example.com/image") + assert_not image_fetch.html_content? + end + + test "content_type" do + fetch = Link::Fetch.new("https://example.com/page") + + stub_request(:head, "https://example.com/page") + .to_return(status: 200, headers: { "Content-Type" => "text/html; charset=utf-8" }) + + assert_equal "text/html; charset=utf-8", fetch.content_type + + stub_request(:head, "https://example.com/error") + .to_return(status: 404) + + error_fetch = Link::Fetch.new("https://example.com/error") + assert_raises(Link::Fetch::UnsuccesfulRequestError) do + error_fetch.content_type + end + end + + test "content" do + fetch = Link::Fetch.new("https://example.com/page") + + stub_request(:get, "https://example.com/page") + .to_return(status: 200, body: "Test") + + content = fetch.content + assert_includes content, "Test" + + stub_request(:get, "https://example.com/large") + .to_return(status: 200, body: "x" * (Link::Fetch::MAX_BODY_SIZE + 1)) + + large_fetch = Link::Fetch.new("https://example.com/large") + assert_raises(Link::Fetch::BodyTooLargeError) do + large_fetch.content + end + + stub_request(:get, "https://example.com/error") + .to_return(status: 404) + + error_fetch = Link::Fetch.new("https://example.com/error") + assert_raises(Link::Fetch::UnsuccesfulRequestError) do + error_fetch.content + end + end +end diff --git a/test/models/link/fizzy_unfurler_test.rb b/test/models/link/fizzy_unfurler_test.rb new file mode 100644 index 0000000000..ed39370f45 --- /dev/null +++ b/test/models/link/fizzy_unfurler_test.rb @@ -0,0 +1,40 @@ +require "test_helper" + +class Link::FizzyUnfurlerTest < ActiveSupport::TestCase + setup do + @original_url_options = Rails.application.config.action_mailer.default_url_options + Rails.application.config.action_mailer.default_url_options = { host: "fizzy.example.com", port: 3000 } + end + + teardown do + Rails.application.config.action_mailer.default_url_options = @original_url_options + end + + test "unfurls?" do + assert Link::FizzyUnfurler.unfurls?(URI.parse("https://fizzy.example.com:3000/123/cards/456")) + assert Link::FizzyUnfurler.unfurls?(URI.parse("http://fizzy.example.com:3000/123/any/path")) + + assert_not Link::FizzyUnfurler.unfurls?(URI.parse("https://other.example.com:3000/123/cards/456")) + assert_not Link::FizzyUnfurler.unfurls?(URI.parse("https://fizzy.example.com:3001/123/cards/456")) + end + + test "unfurl" do + user = users(:david) + card = cards(:logo) + tenant_id = ApplicationRecord.current_tenant + url = "https://fizzy.example.com:3000/#{tenant_id}/cards/#{card.id}" + + metadata = Link::FizzyUnfurler.new(URI.parse(url), user: user).unfurl + + assert_equal card.title, metadata.title + assert_equal url, metadata.canonical_url + + # Test different tenant + different_tenant_url = "https://fizzy.example.com:3000/999/cards/#{card.id}" + assert_nil Link::FizzyUnfurler.new(URI.parse(different_tenant_url), user: user).unfurl + + # Test non-existent card + non_existent_url = "https://fizzy.example.com:3000/#{tenant_id}/cards/99999" + assert_nil Link::FizzyUnfurler.new(URI.parse(non_existent_url), user: user).unfurl + end +end diff --git a/test/models/link/metadata_test.rb b/test/models/link/metadata_test.rb new file mode 100644 index 0000000000..96e9fc12d6 --- /dev/null +++ b/test/models/link/metadata_test.rb @@ -0,0 +1,30 @@ +require "test_helper" + +class Link::MetadataTest < ActiveSupport::TestCase + test "initialize" do + metadata = Link::Metadata.new( + title: "Safe TitleBold", + description: "

Paragraph with link

", + canonical_url: "https://example.com/page", + image_url: "/images/photo.jpg" + ) + + assert_equal "alert('xss')Safe TitleBold", metadata.title + assert_equal "Paragraph with link", metadata.description + assert_equal "https://example.com/page", metadata.canonical_url + assert_equal "https://example.com/images/photo.jpg", metadata.image_url + + assert_equal "Safe TitleBold", metadata.unsafe_title + assert_equal "

Paragraph with link

", metadata.unsafe_description + assert_equal "https://example.com/page", metadata.unsafe_canonical_url + assert_equal "/images/photo.jpg", metadata.unsafe_image_url + + invalid_metadata = Link::Metadata.new( + canonical_url: "javascript:alert('xss')", + image_url: "ftp://example.com/image.jpg" + ) + + assert_nil invalid_metadata.canonical_url + assert_nil invalid_metadata.image_url + end +end diff --git a/test/models/link/open_graph_unfurler_test.rb b/test/models/link/open_graph_unfurler_test.rb new file mode 100644 index 0000000000..a118c549ed --- /dev/null +++ b/test/models/link/open_graph_unfurler_test.rb @@ -0,0 +1,42 @@ +require "test_helper" + +class Link::OpenGraphUnfurlerTest < ActiveSupport::TestCase + test "unfurls?" do + assert Link::OpenGraphUnfurler.unfurls?(URI.parse("https://example.com/page")) + assert Link::OpenGraphUnfurler.unfurls?(URI.parse("https://any-site.com/path")) + assert_not Link::OpenGraphUnfurler.unfurls?(URI.parse("ftp://any-site.com/path")) + end + + test "unfurl" do + url = "https://example.com/page" + + stub_request(:head, url) + .to_return(status: 200, headers: { "Content-Type" => "text/html" }) + + stub_request(:get, url) + .to_return(status: 200, body: <<~HTML) + + + Page Title + + + + + + + HTML + + metadata = Link::OpenGraphUnfurler.new(URI.parse(url)).unfurl + + assert_equal "OG Title", metadata.title + assert_equal "OG Description", metadata.description + assert_equal "https://example.com/canonical", metadata.canonical_url + assert_equal "https://example.com/image.jpg", metadata.image_url + + stub_request(:head, "https://example.com/non-html") + .to_return(status: 200, headers: { "Content-Type" => "application/json" }) + + assert_nil Link::OpenGraphUnfurler.new(URI.parse("https://example.com/non-html")).unfurl + assert_nil Link::OpenGraphUnfurler.new(URI.parse("ftp://example.com/file")).unfurl + end +end diff --git a/test/models/link_test.rb b/test/models/link_test.rb new file mode 100644 index 0000000000..7027ffebcb --- /dev/null +++ b/test/models/link_test.rb @@ -0,0 +1,16 @@ +require "test_helper" + +class LinkTest < ActiveSupport::TestCase + include VcrTestHelper + + test "unfurl" do + user = users(:kevin) + link = Link.new("http://3.basecamp.localhost:3001/181900405/buckets/1042979247/messages/783526101") + + assert_changes -> { link.metadata }, from: nil do + link.unfurl(user: user) + end + + assert_kind_of Link::Metadata, link.metadata + end +end diff --git a/test/vcr_cassettes/link_test-test_unfurl.yml b/test/vcr_cassettes/link_test-test_unfurl.yml new file mode 100644 index 0000000000..2fd0a841c8 --- /dev/null +++ b/test/vcr_cassettes/link_test-test_unfurl.yml @@ -0,0 +1,1353 @@ +--- +http_interactions: +- request: + method: head + uri: http://3.basecamp.localhost:3001/181900405/buckets/1042979247/messages/783526101 + body: + encoding: US-ASCII + base64_string: '' + headers: + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + Accept: + - text/html,application/xhtml+xml + User-Agent: + - Mozilla/5.0 (compatible; FizzyLinkUnfurler/1.0.0) + Host: + - 3.basecamp.localhost:3001 + Authorization: + - Bearer BAhbB0kiAjoBeyJjbGllbnRfaWQiOiI5ZDJmZjU2NDM4YTg1MTNjMjAwNDQ3NTJkMWYzNjE2ZTA3NmUwYTU3IiwiZXhwaXJlc19hdCI6IjIwMjUtMTAtMDZUMTM6MjM6NDlaIiwidXNlcl9pZHMiOlsyNjIxOTgyNTAsMTI2NTEwMDgzLDk0OTc3OTE2NSw5NTA3NTg3OSw4NjgyMTE0NDEsNzA3NTQwNDE1LDIwOTUwMDc0Miw2NDUzOTU2OTAsODc5OTczNzU2LDk3NjAyNDY4LDkwMDk4Nzc1OSwxMTg0NzE1MDQsMjQ5MTUxMjgzLDQwMDE1OTMwNCw4MDk5MDk3Nl0sInZlcnNpb24iOjEsImFwaV9kZWFkYm9sdCI6ImRjOTUzYmYwMDdmMTQyNGY0YWYyN2FjMzI0NGYyZjYzIn0GOgZFVEl1OglUaW1lDc1kH8CpNxZfCToNbmFub19udW1pAjICOg1uYW5vX2RlbmkGOg1zdWJtaWNybyIHViA6CXpvbmVJIghVVEMGOwBG--56891137f381109fc1c075ca95d871b529040fba + response: + status: + code: 200 + message: OK + headers: + X-Frame-Options: + - SAMEORIGIN + X-Xss-Protection: + - 1; mode=block + X-Content-Type-Options: + - nosniff + X-Permitted-Cross-Domain-Policies: + - none + Referrer-Policy: + - strict-origin-when-cross-origin + X-Robots-Tag: + - none + Etag: + - W/"f8535957f7932dfab75d1b9c73c405a2" + Link: + - "; + rel=preload; as=style; nopush" + Content-Type: + - text/html; charset=utf-8 + Vary: + - Accept + Cache-Control: + - max-age=0, private, must-revalidate + Content-Security-Policy: + - 'font-src ''self'' blob: data: http://bc3-cdn.localhost:3001 fonts.gstatic.com + https://rsms.me https://use.typekit.net; object-src ''none''; script-src ''self'' + http://bc3-cdn.localhost:3001 *.braintreegateway.com *.sentry-cdn.com https://basecamp.com + https://www.dropbox.com/static/api/2/dropins.js https://platform.twitter.com + hcaptcha.com *.hcaptcha.com beacon-v2.helpscout.net ''sha256-tkb4UZeVJlSA6VA48VjvAuLrjlKnMlkZ3aYzHpTVQ+Y='' + https://cdn01.boxcdn.net/js/static/select.js embedr.flickr.com/assets/ widgets.flickr.com/embedr/ + ''sha256-aCvRIQ79zbEtvxwsqDbuavE4Sa35jGPLpcm4Y1yIUA0='' ''sha256-Ez5uQZcKGUzoXDIfdPNg1TIyAIO39xBSe6Xba9lAhR4='' + https://assets.tickspot.com https://secure.tickspot.com ''report-sample'' + ''nonce-da39a3ee5e6b4b0d3255bfef95601890afd80709''; style-src ''self'' ''unsafe-inline'' + http://bc3-cdn.localhost:3001 fonts.googleapis.com https://rsms.me/inter/inter.css + https://assets.tickspot.com ''report-sample''; base-uri ''self''; form-action + ''self'' http://launchpad.localhost:3011 https://basecamp.com/; frame-ancestors + ''self'' *.basecamp.localhost:3001' + Set-Cookie: + - _bc3_session=ORfzly73kTg7AiyusolH7VtRLA84WwlkK0b%2Bm2eMgtqhQgZotWamDLxidAsasZNL7cLR4rDxoukuSofTe4W48wbTf8HWlTvt19d38m1KGqVzQVOcXjWhBMzlEGAozVdxCaS5k3tA%2BMFKmwO1uzIFbh7ewMLWQoAW5Phl9XdRNK1p7t%2B5UJZ2ozmpmM30%2F4g6TKaeO2hsBeS06%2FtEPp%2BcM12Ye0es9LstNcli4NVoqU6ocsrrXCDAHndoCrrHmhuvp2jvB%2B7nu9hg8PGtZfH39nTw5GaBgw32dAQmamri%2FIxOLidKGIUE3Lbt2eUmLs3tApJu1%2F7imxFrmA4pN%2BN0MkXa--OleJmKOsdGD0SKmc--eCEPaKpBigvQC3hiUOUhRA%3D%3D; + path=/181900405; expires=Thu, 09 Oct 2025 12:04:38 GMT; HttpOnly; SameSite=Lax + X-Ratelimit: + - '{"name":"General","period":60,"limit":1000,"remaining":1000,"until":"2025-09-25T12:05:00Z"}' + X-Request-Id: + - 1178fc0e-3ff1-4ab9-9073-7c404a99c422 + X-Runtime: + - '0.172064' + Content-Length: + - '0' + body: + encoding: UTF-8 + base64_string: '' + recorded_at: Thu, 25 Sep 2025 12:04:38 GMT +- request: + method: get + uri: http://3.basecamp.localhost:3001/181900405/buckets/1042979247/messages/783526101 + body: + encoding: US-ASCII + base64_string: '' + headers: + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + Accept: + - text/html,application/xhtml+xml + User-Agent: + - Mozilla/5.0 (compatible; FizzyLinkUnfurler/1.0.0) + Host: + - 3.basecamp.localhost:3001 + Authorization: + - Bearer BAhbB0kiAjoBeyJjbGllbnRfaWQiOiI5ZDJmZjU2NDM4YTg1MTNjMjAwNDQ3NTJkMWYzNjE2ZTA3NmUwYTU3IiwiZXhwaXJlc19hdCI6IjIwMjUtMTAtMDZUMTM6MjM6NDlaIiwidXNlcl9pZHMiOlsyNjIxOTgyNTAsMTI2NTEwMDgzLDk0OTc3OTE2NSw5NTA3NTg3OSw4NjgyMTE0NDEsNzA3NTQwNDE1LDIwOTUwMDc0Miw2NDUzOTU2OTAsODc5OTczNzU2LDk3NjAyNDY4LDkwMDk4Nzc1OSwxMTg0NzE1MDQsMjQ5MTUxMjgzLDQwMDE1OTMwNCw4MDk5MDk3Nl0sInZlcnNpb24iOjEsImFwaV9kZWFkYm9sdCI6ImRjOTUzYmYwMDdmMTQyNGY0YWYyN2FjMzI0NGYyZjYzIn0GOgZFVEl1OglUaW1lDc1kH8CpNxZfCToNbmFub19udW1pAjICOg1uYW5vX2RlbmkGOg1zdWJtaWNybyIHViA6CXpvbmVJIghVVEMGOwBG--56891137f381109fc1c075ca95d871b529040fba + response: + status: + code: 200 + message: OK + headers: + X-Frame-Options: + - SAMEORIGIN + X-Xss-Protection: + - 1; mode=block + X-Content-Type-Options: + - nosniff + X-Permitted-Cross-Domain-Policies: + - none + Referrer-Policy: + - strict-origin-when-cross-origin + X-Robots-Tag: + - none + Etag: + - W/"f8535957f7932dfab75d1b9c73c405a2" + Link: + - "; + rel=preload; as=style; nopush" + Content-Type: + - text/html; charset=utf-8 + Vary: + - Accept + Cache-Control: + - max-age=0, private, must-revalidate + Content-Security-Policy: + - 'font-src ''self'' blob: data: http://bc3-cdn.localhost:3001 fonts.gstatic.com + https://rsms.me https://use.typekit.net; object-src ''none''; script-src ''self'' + http://bc3-cdn.localhost:3001 *.braintreegateway.com *.sentry-cdn.com https://basecamp.com + https://www.dropbox.com/static/api/2/dropins.js https://platform.twitter.com + hcaptcha.com *.hcaptcha.com beacon-v2.helpscout.net ''sha256-tkb4UZeVJlSA6VA48VjvAuLrjlKnMlkZ3aYzHpTVQ+Y='' + https://cdn01.boxcdn.net/js/static/select.js embedr.flickr.com/assets/ widgets.flickr.com/embedr/ + ''sha256-aCvRIQ79zbEtvxwsqDbuavE4Sa35jGPLpcm4Y1yIUA0='' ''sha256-Ez5uQZcKGUzoXDIfdPNg1TIyAIO39xBSe6Xba9lAhR4='' + https://assets.tickspot.com https://secure.tickspot.com ''report-sample'' + ''nonce-da39a3ee5e6b4b0d3255bfef95601890afd80709''; style-src ''self'' ''unsafe-inline'' + http://bc3-cdn.localhost:3001 fonts.googleapis.com https://rsms.me/inter/inter.css + https://assets.tickspot.com ''report-sample''; base-uri ''self''; form-action + ''self'' http://launchpad.localhost:3011 https://basecamp.com/; frame-ancestors + ''self'' *.basecamp.localhost:3001' + Set-Cookie: + - _bc3_session=zq3pFi4eTL4Wk8nZMgEkwchem5PDVPvr50qtCnqA8fW01AWc0ThgA56PYGTdXa8tgtff%2Bd24XyNJVmonmSm742MK2ADDDEAy4Dn%2FSF9kT1qermnQxkImG%2FNNNwd7ihjjHh1AAa7ZpZiQa2M1r8ce%2B8Z94TKOn%2BSOmq75XYLNWXl%2Fq5w%2F6fp%2BbogzKChTWnGKvF%2BnQu3IFDX5Gh5zjRDeB30JRKn%2B98oZoeYc6%2FxzfQnev%2FlrLO56U42R2VxkJyntqkrm2ix9xLZ4BBu8gp2GbdK4xtj861cwyIYMVGF0M7DVOg4CyYEqLfYzGwZJTtK0AeCvoWA8cEWqMNnbTnKKvd%2F7--wwLfQMSQ90gDsUBq--tEWZZB4P6aRu%2Bk94i%2Bzrfg%3D%3D; + path=/181900405; expires=Thu, 09 Oct 2025 12:04:38 GMT; HttpOnly; SameSite=Lax + X-Ratelimit: + - '{"name":"General","period":60,"limit":1000,"remaining":1000,"until":"2025-09-25T12:05:00Z"}' + X-Request-Id: + - a248c7b5-4a4c-467a-bfe6-a86e9f7038d5 + X-Runtime: + - '0.193966' + Content-Length: + - '54089' + body: + encoding: ASCII-8BIT + base64_string: | + CjwhRE9DVFlQRSBodG1sPgo8aHRtbCBsYW5nPSJlbiIKICAgIGNsYXNzPSIi + CiAgICBkYXRhLXRoZW1lPSJkZWZhdWx0IgogICAgZGF0YS1jb250cm9sbGVy + PSJwbGF0Zm9ybSBjb2xvci1zY2hlbWUiCiAgICBkYXRhLWNvbG9yLXNjaGVt + ZT0iIgogICAgZGF0YS1icmlkZ2UtcGxhdGZvcm09IiI+CjxoZWFkPgo8bWV0 + YSBjaGFyc2V0PSJ1dGYtOCI+Cgo8dGl0bGUgZGF0YS1icmlkZ2UtYWx0PSLw + n5OiIEEgZGVjYWRlISI+8J+ToiBBIGRlY2FkZSE8L3RpdGxlPgoKPG1ldGEg + bmFtZT0idmlld3BvcnQiIGNvbnRlbnQ9IndpZHRoPWRldmljZS13aWR0aCwg + aW5pdGlhbC1zY2FsZT0xLCBtYXhpbXVtLXNjYWxlPTEsIHVzZXItc2NhbGFi + bGU9bm8iPgo8bWV0YSBuYW1lPSJyb2JvdHMiIGNvbnRlbnQ9Im5vbmUiPgoK + PG1ldGEgbmFtZT0icmVmZXJyZXIiIGNvbnRlbnQ9Im9yaWdpbi13aGVuLWNy + b3NzLW9yaWdpbiI+Cgo8bWV0YSBuYW1lPSJ0aGVtZS1jb2xvciIgY29udGVu + dD0iaHNsKDIwMi41LCA0Mi4xJSwgNy41JSkiIG1lZGlhPSIocHJlZmVycy1j + b2xvci1zY2hlbWU6IGRhcmspIj4KCjxtZXRhIG5hbWU9ImNzcmYtcGFyYW0i + IGNvbnRlbnQ9ImF1dGhlbnRpY2l0eV90b2tlbiIgLz4KPG1ldGEgbmFtZT0i + Y3NyZi10b2tlbiIgY29udGVudD0iTUdqVzktRUZjckhNeFFRczJySkt2QXRB + NGp2eXR4S0FhY1VacTZuejczbzhaTzZFckd0VUZQNnFtOHNEdy1DcW9zMmNW + Tm5rbXRndzF3Y25HR1dweWciIC8+Cgo8bWV0YSBuYW1lPSJ0dXJiby1yb290 + IiBjb250ZW50PSIvMTgxOTAwNDA1IiAvPgoKPG1ldGEgbmFtZT0idHVyYm8t + Y2FjaGUtY29udHJvbCIgY29udGVudD0iY2FjaGUiIC8+Cgo8bWV0YSBuYW1l + PSJ0dXJiby1wcmVmZXRjaCIgY29udGVudD0idHJ1ZSIgLz4KCjxtZXRhIG5h + bWU9ImNhYmxlLXVybCIgY29udGVudD0id3M6Ly8zLmJhc2VjYW1wLmxvY2Fs + aG9zdDozMDAxLzE4MTkwMDQwNS9jYWJsZSIgLz4KCjxtZXRhIG5hbWU9ImN1 + cnJlbnQtZW52IiBjb250ZW50PSJkZXZlbG9wbWVudCIgLz48bWV0YSBuYW1l + PSJjdXJyZW50LXN0YWdlIiBjb250ZW50PSJkZXZlbG9wbWVudCIgLz48bWV0 + YSBuYW1lPSJjdXJyZW50LXJlbGVhc2UiIGNvbnRlbnQ9IjU3NzY2ZjhjOTRl + MjExNmFmNWU4NjljOTNiNmExODhjYTg2MTNiYjQiIC8+CjxtZXRhIG5hbWU9 + ImN1cnJlbnQtYWNjb3VudC1zbHVnLXBhdGgiIGNvbnRlbnQ9Ii8xODE5MDA0 + MDUiIC8+PG1ldGEgbmFtZT0iY3VycmVudC1hY2NvdW50LW9uLXBlci1zZWF0 + LWJ1c2luZXNzLW1vZGVsIiBjb250ZW50PSJ0cnVlIiAvPjxtZXRhIG5hbWU9 + ImN1cnJlbnQtYWNjb3VudC1vbi1tb2Rlcm4tYnVzaW5lc3MtbW9kZWwiIGNv + bnRlbnQ9InRydWUiIC8+PG1ldGEgbmFtZT0iY3VycmVudC1hY2NvdW50LXBy + by1wYWNrIiBjb250ZW50PSJmYWxzZSIgLz48bWV0YSBuYW1lPSJjdXJyZW50 + LWFjY291bnQtcmVzdHJpY3RzLWFyY2hpdmUtYW5kLXRyYXNoIiBjb250ZW50 + PSJmYWxzZSIgLz4KPG1ldGEgbmFtZT0iY3VycmVudC1wZXJzb24taWQiIGNv + bnRlbnQ9IjUyNTQ2MDIwNyIgLz48bWV0YSBuYW1lPSJjdXJyZW50LXBlcnNv + bi1naWQiIGNvbnRlbnQ9ImdpZDovL2JjMy9QZXJzb24vNTI1NDYwMjA3IiAv + PjxtZXRhIG5hbWU9ImN1cnJlbnQtcGVyc29uLW5hbWUiIGNvbnRlbnQ9IkRh + dmlkIEhlaW5lbWVpZXIgSGFuc3NvbiIgLz48bWV0YSBuYW1lPSJjdXJyZW50 + LXBlcnNvbi1lbWFpbC1hZGRyZXNzIiBjb250ZW50PSJkYXZpZEAzN3NpZ25h + bHMuY29tIiAvPjxtZXRhIG5hbWU9ImN1cnJlbnQtcGVyc29uLWF2YXRhci11 + cmwiIGNvbnRlbnQ9Imh0dHA6Ly8zLmJhc2VjYW1wLmxvY2FsaG9zdDozMDAx + LzE4MTkwMDQwNS9teS9hdmF0YXIiIC8+PG1ldGEgbmFtZT0iY3VycmVudC1w + ZXJzb24tb3duZXIiIGNvbnRlbnQ9InRydWUiIC8+PG1ldGEgbmFtZT0iY3Vy + cmVudC1wZXJzb24tYWRtaW4iIGNvbnRlbnQ9InRydWUiIC8+PG1ldGEgbmFt + ZT0iY3VycmVudC1wZXJzb24tc3RhZmYiIGNvbnRlbnQ9InRydWUiIC8+PG1l + dGEgbmFtZT0iY3VycmVudC1wZXJzb24tY2xpZW50IiBjb250ZW50PSJmYWxz + ZSIgLz48bWV0YSBuYW1lPSJjdXJyZW50LXBlcnNvbi1lbXBsb3llZSIgY29u + dGVudD0idHJ1ZSIgLz48bWV0YSBuYW1lPSJjdXJyZW50LXBlcnNvbi1maXJz + dC13ZWVrLWRheS1pbnQiIGNvbnRlbnQ9IjAiIC8+PG1ldGEgbmFtZT0iY3Vy + cmVudC1wZXJzb24tZmlyc3QtdGltZS1ib29zdGVyIiBjb250ZW50PSJmYWxz + ZSIgLz48bWV0YSBuYW1lPSJjdXJyZW50LXBlcnNvbi10aW1lLWZvcm1hdCIg + Y29udGVudD0idHdlbHZlX2hvdXIiIC8+CjxtZXRhIG5hbWU9ImN1cnJlbnQt + cGFnZS1leGNsdWRlLWZyb20tcmVjZW50LWhpc3RvcnkiIGNvbnRlbnQ9ImZh + bHNlIiAvPjxtZXRhIG5hbWU9ImN1cnJlbnQtcGFnZS10eXBlIiBjb250ZW50 + PSJtZXNzYWdlIiAvPjxtZXRhIG5hbWU9ImN1cnJlbnQtcGFnZS10aXRsZSIg + Y29udGVudD0i8J+ToiBBIGRlY2FkZSEiIC8+PG1ldGEgbmFtZT0iY3VycmVu + dC1wYWdlLXN1YnRpdGxlIiBjb250ZW50PSJUZW4teWVhciBhbm5pdmVyc2Fy + eSIgLz4KCgoKCgo8bWV0YSBuYW1lPSJjdXJyZW50LWFjY291bnQtbmFtZSIg + Y29udGVudD0iQmFzZWNhbXAmIzM5O3MgQmFzZWNhbXAiIC8+PG1ldGEgbmFt + ZT0iY3VycmVudC1idWNrZXQtaWQiIGNvbnRlbnQ9IjEwNDI5NzkyNDciIC8+ + PG1ldGEgbmFtZT0iY3VycmVudC1idWNrZXQtbmFtZSIgY29udGVudD0iVGVu + LXllYXIgYW5uaXZlcnNhcnkiIC8+PG1ldGEgbmFtZT0iY3VycmVudC1idWNr + ZXQtc3RhdHVzIiBjb250ZW50PSJhY3RpdmUiIC8+PG1ldGEgbmFtZT0iY3Vy + cmVudC1idWNrZXQtcGF0aCIgY29udGVudD0iLzE4MTkwMDQwNS9idWNrZXRz + LzEwNDI5NzkyNDciIC8+PG1ldGEgbmFtZT0iY3VycmVudC1idWNrZXQtdHlw + ZSIgY29udGVudD0iUHJvamVjdCIgLz48bWV0YSBuYW1lPSJjdXJyZW50LXJl + Y29yZGluZy1pZCIgY29udGVudD0iNzgzNTI2MTAxIiAvPjxtZXRhIG5hbWU9 + ImN1cnJlbnQtcmVjb3JkaW5nLXR5cGUiIGNvbnRlbnQ9Ik1lc3NhZ2UiIC8+ + PG1ldGEgbmFtZT0iY3VycmVudC1yZWNvcmRpbmctdGl0bGUiIGNvbnRlbnQ9 + IkEgZGVjYWRlISIgLz48bWV0YSBuYW1lPSJjdXJyZW50LXJlY29yZGluZy1p + cy1kb2NrZWQiIGNvbnRlbnQ9ImZhbHNlIiAvPjxtZXRhIG5hbWU9ImN1cnJl + bnQtZG9jay10b29sLXRpdGxlIiBjb250ZW50PSJNZXNzYWdlIEJvYXJkIiAv + PjxtZXRhIG5hbWU9ImN1cnJlbnQtZG9jay10b29sLXVybCIgY29udGVudD0i + aHR0cDovLzMuYmFzZWNhbXAubG9jYWxob3N0OjMwMDEvMTgxOTAwNDA1L2J1 + Y2tldHMvMTA0Mjk3OTI0Ny9tZXNzYWdlX2JvYXJkcy8xMjIyMDE2NjUiIC8+ + CgoKPG1ldGEgbmFtZT0iYXNzZXRzLXZlcnNpb24iIGNvbnRlbnQ9InYxIiAv + PgoKPG1ldGEgbmFtZT0iY3NwLW5vbmNlIiBjb250ZW50PSJkYTM5YTNlZTVl + NmI0YjBkMzI1NWJmZWY5NTYwMTg5MGFmZDgwNzA5IiAvPgoKICA8bWV0YSBu + YW1lPSJhcHBsZS1pdHVuZXMtYXBwIiBjb250ZW50PSJhcHAtaWQ9MTAxNTYw + MzI0OCI+CgoKCjxzY3JpcHQgbm9uY2U9ImRhMzlhM2VlNWU2YjRiMGQzMjU1 + YmZlZjk1NjAxODkwYWZkODA3MDkiPgovLzwhW0NEQVRBWwooZnVuY3Rpb24o + c3R5bGUpIHsKICBzdHlsZS5pbm5lclRleHQgPSAiLmxvYWRpbmdfX2hpZGUg + eyBvcGFjaXR5OiAwIH0iOwogIGRvY3VtZW50LmhlYWQuYXBwZW5kQ2hpbGQo + c3R5bGUpOwogIGFkZEV2ZW50TGlzdGVuZXIoIkRPTUNvbnRlbnRMb2FkZWQi + LCBmdW5jdGlvbigpIHsKICAgIHJlcXVlc3RBbmltYXRpb25GcmFtZShmdW5j + dGlvbigpIHsKICAgICAgZG9jdW1lbnQuaGVhZC5yZW1vdmVDaGlsZChzdHls + ZSkKICAgIH0pCiAgfSkKfSkoZG9jdW1lbnQuY3JlYXRlRWxlbWVudCgic3R5 + bGUiKSkKCi8vXV0+Cjwvc2NyaXB0PgoKPGxpbmsgcmVsPSJkbnMtcHJlZmV0 + Y2giIGhyZWY9Ii8vYmMzLWNkbi5sb2NhbGhvc3Q6MzAwMSIgLz4KCjxsaW5r + IHJlbD0ic3R5bGVzaGVldCIgaHJlZj0iLy9iYzMtY2RuLmxvY2FsaG9zdDoz + MDAxL2Fzc2V0cy9kZXNrdG9wLTMwZjUwMzg1M2M5OTJlYTFhYTQwNjBmMTc3 + OTUyYzAzOTZkNDM0MjI5YWE1OWZhYzhiODQwOWRjM2RjNWE0ZTUuY3NzIiBt + ZWRpYT0iYWxsIiBkYXRhLXR1cmJvLXRyYWNrPSJyZWxvYWQiIC8+CgogIDxz + Y3JpcHQgc3JjPSIvL2JjMy1jZG4ubG9jYWxob3N0OjMwMDEvYXNzZXRzL2J1 + aWxkcy9saWJyYXJpZXMtNGZmZDEzYzFkZTRjZDQzNzY5MDg3NWIxNzVkMjk1 + ODg5OTAzYTgyMDQxNTE4ZjI5Mjc3ZWFlNTU5Mjg5M2Q2Yi5qcyIgZGVmZXI9 + ImRlZmVyIiBkYXRhLXR1cmJvLXRyYWNrPSJyZWxvYWQiPjwvc2NyaXB0Pgog + IDxzY3JpcHQgc3JjPSIvL2JjMy1jZG4ubG9jYWxob3N0OjMwMDEvYXNzZXRz + L2J1aWxkcy9kZXNrdG9wLWFhMjVlYjUwM2QzNDFkMjU0OTJlODA3YWUxMjdj + ZDc2YTIxMDExN2IxMTRjMWY5MTk5NjY0MmIxYWI2ODgxYWIuanMiIGRlZmVy + PSJkZWZlciIgZGF0YS10dXJiby10cmFjaz0icmVsb2FkIj48L3NjcmlwdD4K + ICA8c2NyaXB0IHNyYz0iLy9iYzMtY2RuLmxvY2FsaG9zdDozMDAxL2Fzc2V0 + cy9idWlsZHMvYXBwbGljYXRpb24tYjYwNTdjMzZjNTQ3YWJiZTE0Y2YwNGU5 + MzlmMTU2NGFmMDI2MmYzMzA0OWI0NzU2OGZkN2QzZTcwYTlmNDlkNC5qcyIg + ZGVmZXI9ImRlZmVyIiBkYXRhLXR1cmJvLXRyYWNrPSJyZWxvYWQiPjwvc2Ny + aXB0PgoKPGxpbmsgcmVsPSJzdWJyZXNvdXJjZSIgaHJlZj0iLy9iYzMtY2Ru + LmxvY2FsaG9zdDozMDAxL2Fzc2V0cy9idWlsZHMvcmljaF90ZXh0LTA3ODRj + NzU1M2RkM2I2MDEzNDVhYmJlY2YwY2E2ZGNhYzRjZjQ4MTZhNDJlNjU4NmM0 + NzZkY2UyZTJiZjgyY2YuanMiIGRhdGEtc2NyaXB0PSJidWlsZHMvcmljaF90 + ZXh0IiBkYXRhLXR1cmJvLXRyYWNrPSJyZWxvYWQiIC8+Cgo8bGluayByZWw9 + ImFwcGxlLXRvdWNoLWljb24iIHNpemVzPSIxOTZ4MTk2IiBocmVmPSIvYXBw + bGUtdG91Y2gtaWNvbi5wbmciPgo8bGluayByZWw9InNob3J0Y3V0IGljb24i + IHR5cGU9ImltYWdlL3BuZyIgc2l6ZXM9IjMyeDMyIiBocmVmPSIvZmF2aWNv + bi0zMngzMi5wbmciPgoKPGxpbmsgcmVsPSJtYW5pZmVzdCIgaHJlZj0iLzE4 + MTkwMDQwNS9tYW5pZmVzdC5qc29uIiBjcm9zc29yaWdpbj0idXNlLWNyZWRl + bnRpYWxzIj4KCgoKPC9oZWFkPgoKCjxib2R5IGNsYXNzPSIiICBkYXRhLWNv + bnRyb2xsZXI9IndlYi1ub3RpZmljYXRpb24tcGVybWlzc2lvbiI+CiAgPGEg + aHJlZj0iI2p1bXAtbWVudSIgY2xhc3M9ImEtZm9yLXNjcmVlbi1yZWFkZXIg + bmF2X19hY2Nlc3NpYmlsaXR5LWJ1dHRvbiBidG4gYnRuLS1zbWFsbCB1LWhp + ZGUtb24tcGhvbmUgbG9hZGluZ19faGlkZSIgdGFiaW5kZXg9IjAiIGRhdGEt + dHVyYm89ImZhbHNlIiBkYXRhLWJlaGF2aW9yPSJzaG93X2p1bXBfbWVudV9i + dXR0b24iPgogICAgU2hvdyBKdW1wIE1lbnUgPHNwYW4gY2xhc3M9ImJ0bl9f + a2V5Ym9hcmQtc2hvcnRjdXQiPjxzcGFuIGNsYXNzPSJhLWZvci1zY3JlZW4t + cmVhZGVyIj4sIHNob3J0Y3V0IDwvc3Bhbj48c3BhbiBkYXRhLXJvbGU9Imp1 + bXAtbWVudS1tb2RpZmllci1rZXkiPkN0cmw8L3NwYW4+Sjwvc3Bhbj4KICA8 + L2E+CgogIDxhIGhyZWY9IiNtYWluLWNvbnRlbnQiIGNsYXNzPSJhLWZvci1z + Y3JlZW4tcmVhZGVyIG5hdl9fYWNjZXNzaWJpbGl0eS1idXR0b24gYnRuIGJ0 + bi0tc21hbGwgdS1oaWRlLW9uLXBob25lIGxvYWRpbmdfX2hpZGUiIGRhdGEt + dHVyYm89ImZhbHNlIj4KICAgIFNraXAgdG8gbWFpbiBjb250ZW50CiAgPC9h + PgoKICA8ZGl2IGNsYXNzPSJuYXYiPgogIDxuYXYgaWQ9Im15X25hdmlnYXRp + b24iCiAgICAgIGFyaWEtbGFiZWw9Ik1haW4iCiAgICAgIGNsYXNzPSJuYXZf + X2JhciIKICAgICAgZGF0YS10dXJiby1wZXJtYW5lbnQKICAgICAgZGF0YS1j + b250cm9sbGVyPSJzY3JvbGwtdG9nZ2xlIgogICAgICBkYXRhLWFjdGlvbj0i + c2Nyb2xsQHdpbmRvdy0+c2Nyb2xsLXRvZ2dsZSN0b2dnbGUiCiAgICAgIGRh + dGEtc2Nyb2xsLXRvZ2dsZS1jbGFzcz0ibmF2X19iYXItLXNjcm9sbGVkIj4K + CiAgICA8ZGl2IGNsYXNzPSJuYXYtbWVudSBuYXYtbWVudV9fYWNjb3VudHMi + IGRhdGEtYmVoYXZpb3I9ImV4cGFuZGFibGUgbG9hZF9vbl9leHBhbmQiCiAg + ZGF0YS1tZW51LXNlY3Rpb249ImFjY291bnRzIiBkYXRhLWxvYWQtdXJsPSIv + MTgxOTAwNDA1L215L25hdmlnYXRpb24vYWNjb3VudHMiIGRhdGEtbG9hZC10 + YXJnZXQ9IiNuYXZpZ2F0aW9uX2FjY291bnRzIj4KICA8ZGl2IGNsYXNzPSJj + b2xsYXBzaWJsZV9jb250ZW50IiBkYXRhLWJlaGF2aW9yPSJjb2xsYXBzZV9v + bl9jbGlja291dHNpZGUiPgogICAgPGEgaHJlZj0iLzE4MTkwMDQwNS8iIGNs + YXNzPSJuYXZfX2xpbmstLWFjY291bnRzIiBkYXRhLWJlaGF2aW9yPSJ0b2dn + bGVfZXhwYW5zaW9uX29uX2NsaWNrIgogICAgICBhcmlhLWxhYmVsPSJTd2l0 + Y2ggYWNjb3VudHMiIHJvbGU9ImJ1dHRvbiIgYXJpYS1oYXNwb3B1cD0idHJ1 + ZSI+CiAgICAgIDxzdmcgd2lkdGg9IjEzNSIgaGVpZ2h0PSIzMSIgdmlld0Jv + eD0iMCAwIDEzNSAzMSIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cu + dzMub3JnLzIwMDAvc3ZnIj48cGF0aCBkPSJNMzkuMjg4IDguMTQ0aDUuNjE5 + YzMuMTEyIDAgNC43MjggMS4yNzYgNC43MjggMy42NzN2LjExOWEyLjkzNyAy + LjkzNyAwIDAgMS0yLjMxMyAzLjA1NyAzLjE0IDMuMTQgMCAwIDEgMi44NTUg + My4zN3YuMDgzYzAgMi42MTYtMS43MTcgMy45OTQtNS4wMTMgMy45OTRoLTUu + ODc2VjguMTQ0Wm01LjA5NSA1Ljg2N2MxLjU2MSAwIDIuMTc2LS41NiAyLjE3 + Ni0xLjgzNnYtLjA4M2MwLTEuMTk0LS42NzktMS43MzUtMi4xOTQtMS43MzVo + LTEuOTE5djMuNjcybDEuOTM3LS4wMThabS4yNzYgNi4xOGMxLjU5NyAwIDIu + MzIzLS43MjYgMi4zMjMtMi4wMnYtLjA4M2MwLTEuMzIzLS43MjYtMS45OTMt + Mi40OC0xLjk5M2gtMi4wNTZWMjAuMmwyLjIxMy0uMDA5Wk01MS4wMjIgMTku + NTNjMC0yLjQ0MyAyLjIzLTMuMzYxIDUuNDM1LTMuMzYxaDEuMTc1di0uNDE0 + YzAtMS4yMzktLjM3Ni0xLjkxOS0xLjY5OC0xLjkxOWExLjU4IDEuNTggMCAw + IDAtMS43NjMgMS40NzloLTIuNzU1Yy4xODQtMi40OCAyLjE0LTMuNTgxIDQu + NzAxLTMuNTgxIDIuNTYyIDAgNC4zNyAxLjAzNyA0LjM3IDMuODc1djYuODEy + SDU3LjY3di0xLjI1OGEzLjUzNiAzLjUzNiAwIDAgMS0zLjE1OCAxLjQ3OWMt + MS44OTIgMC0zLjQ5LS45MTgtMy40OS0zLjExM1ptNi42MS0uNzYzdi0uOTE4 + aC0xLjEyYy0xLjY4IDAtMi42NTMuMzY3LTIuNjUzIDEuNDc4IDAgLjc2Mi40 + NTkgMS4yNTggMS41MTUgMS4yNTggMS4yNzYuMDM3IDIuMjU4LS42NjEgMi4y + NTgtMS44MThaTTYxLjcwOSAxOS4xNDNoMi43Yy4xMTkuOTE5LjU3OCAxLjQ3 + OSAxLjgzNiAxLjQ3OSAxLjEyIDAgMS42NDMtLjQxMyAxLjY0My0xLjEzOSAw + LS43MjUtLjYyNC0xLjAxOS0yLjEyLTEuMjMtMi43NTUtLjQyMi0zLjg1Ny0x + LjIyMS0zLjg1Ny0zLjI2IDAtMi4wMzggMi4wMDItMy4yNTkgNC4xMzItMy4y + NTkgMi4zMjIgMCA0LjA1OC44NDUgNC4zMTUgMy4yNDFoLTIuNjU0Yy0uMTU2 + LS44NjMtLjY0Mi0xLjI1OC0xLjYzNC0xLjI1OC0uOTkxIDAtMS40NzguNDQt + MS40NzggMS4wNzQgMCAuNjM0LjQ5Ni45MTkgMi4wMSAxLjEzOSAyLjYxNy4z + NzYgNC4wNTkgMS4wMzcgNC4wNTkgMy4yNzggMCAyLjI0LTEuNjM0IDMuNDM0 + LTQuMzk4IDMuNDM0cy00LjQ1My0xLjI0LTQuNTU0LTMuNDk4Wk03MS40MDUg + MTcuMzI2di0uMTU3YTUuMTg5IDUuMTg5IDAgMCAxIDUuMzctNS40MzVjMi43 + IDAgNS4wOTYgMS41OCA1LjA5NiA1LjMxNnYuNzk5aC03LjVjLjA3MyAxLjcz + NSAxLjAxOCAyLjc1NCAyLjU4OCAyLjc1NCAxLjM0IDAgMi4wMDItLjU3OCAy + LjE4Ni0xLjQ2aDIuNzU0Yy0uMzQgMi4yNi0yLjE0IDMuNTE3LTQuOTk1IDMu + NTE3LTMuMjA0LS4wMTgtNS41LTEuOTkyLTUuNS01LjMzNFptNy42NDgtMS4y + NThjLS4wOTItMS41OC0uOS0yLjI5Ni0yLjI3Ny0yLjI5NmEyLjM5NyAyLjM5 + NyAwIDAgMC0yLjQwNiAyLjI5Nmg0LjY4M1pNODIuNjM0IDE3LjMyNnYtLjE1 + N2E1LjE4OCA1LjE4OCAwIDAgMSA1LjM5LTUuNDM1YzIuNDIzIDAgNC42NTQg + MS4wNTYgNC45MiA0LjA1OEg5MC4xOWExLjkyOSAxLjkyOSAwIDAgMC0yLjEy + MS0xLjc4MWMtMS40OTcgMC0yLjQ5NyAxLjEyLTIuNDk3IDMuMTIydi4xNTZj + MCAyLjEwMi45MTggMy4xNzcgMi41NjEgMy4xNzdhMi4xMTEgMi4xMTEgMCAw + IDAgMi4yMzEtMi4wMmgyLjYxN2MtLjE1NiAyLjQ3OS0xLjk1NiA0LjE5Ni00 + Ljk5NSA0LjE5NnMtNS4zNTItMS45MDEtNS4zNTItNS4zMTZaTTkzLjY1MSAx + OS41M2MwLTIuNDQzIDIuMjMxLTMuMzYxIDUuNDI3LTMuMzYxaDEuMTg0di0u + NDE0YzAtMS4yMzktLjM4Ni0xLjkxOS0xLjY5OC0xLjkxOWExLjU4IDEuNTgg + MCAwIDAtMS43ODIgMS40NzloLTIuNzU0Yy4xODMtMi40OCAyLjE0LTMuNTgx + IDQuNjkyLTMuNTgxczQuMzc5IDEuMDM3IDQuMzc5IDMuODc1djYuODEyaC0y + LjgxOXYtMS4yNThhMy41MyAzLjUzIDAgMCAxLTMuMTU4IDEuNDc5Yy0xLjgz + NiAwLTMuNDctLjkxOC0zLjQ3LTMuMTEzWm02LjYxMS0uNzYzdi0uOTE4SDk5 + LjE2Yy0xLjY4IDAtMi42NjIuMzY3LTIuNjYyIDEuNDc4IDAgLjc2Mi40Njgg + MS4yNTggMS41MjQgMS4yNTggMS4yOTQuMDM3IDIuMjc3LS42NjEgMi4yNzct + MS44MThoLS4wMzdaTTEwNS4xMzcgMTEuOTM2aDIuODkzdjEuNTk4YTMuNTY3 + IDMuNTY3IDAgMCAxIDMuMTk1LTEuODM3IDIuODg2IDIuODg2IDAgMCAxIDIu + OTIgMS44MzcgNC4xMzggNC4xMzggMCAwIDEgMy41MTYtMS44MzZjMS45Mzcg + MCAzLjQzNCAxLjIyIDMuNDM0IDMuOTkzdjYuNjkzaC0yLjg3NFYxNi4wNWMw + LTEuMzQtLjU5Ny0xLjkzNy0xLjY0My0xLjkzN2ExLjk0IDEuOTQgMCAwIDAt + Mi4wMTEgMi4xNHY2LjE3aC0yLjg4M3YtNi4zNzNjMC0xLjM0LS42MTUtMS45 + MzctMS42MzQtMS45MzdhMS45NCAxLjk0IDAgMCAwLTEuOTI5IDEuMzE1IDEu + OTIgMS45MiAwIDAgMC0uMDkxLjgyNHY2LjE3aC0yLjg5M1YxMS45MzZaTTEy + My4wNzggMTEuOTM2aDIuOTAxdjEuNjQzYTMuODg3IDMuODg3IDAgMCAxIDMu + MzMzLTEuODgyYzIuNTYyIDAgNC41OTEgMS45IDQuNTkxIDUuMzUzdi4xNjVj + MCAzLjQ1My0xLjk1NiA1LjM5LTQuNTkxIDUuMzlhMy42NyAzLjY3IDAgMCAx + LTMuMzMzLTEuODM2djUuMjE1aC0yLjkwMVYxMS45MzZabTcuODUgNS4xMzJj + MC0yLjExMS0xLjAzNy0zLjE0OS0yLjQ5Ny0zLjE0OXMtMi41NTMgMS4wNTYt + Mi41NTMgMy4xNXYuMTY1YzAgMi4wOTMgMS4wMDEgMy4xMTIgMi41OCAzLjEx + MiAxLjU4IDAgMi40Ny0xLjA2NSAyLjQ3LTMuMDc2di0uMjAyWk0xOC4wODgu + MjdjOS4xIDAgMTUuMjE1IDEwLjUxOCAxNS45NzcgMjEuOTM3LjAyLjMxMy0u + MDUzLjYyNi0uMjEyLjg5Ni0zLjE0IDUuMzUtMTAuMDYxIDYuNTI3LTE1Ljcz + NyA2LjU1OC01LjQ4Ny4xLTEwLjctMi4xODgtMTQuNDEyLTYuMzAxYTEuNTY2 + IDEuNTY2IDAgMCAxLS4zMDMtMS42IDM2LjE3NyAzNi4xNzcgMCAwIDEgMS45 + MTItNC4xNDdjMS4wNTItMS45MjggMi42NDQtNC42ODEgNS4xNTQtNC43NjMg + Mi4zNDMgMCAzLjUxNiAyLjE3NCA1LjExNCAzLjUxOSAxLjYzMy0xLjY3MiAy + LjU1Mi0zLjk0IDMuNTY3LTYuMDE0YTEuNTY1IDEuNTY1IDAgMCAxIDIuODM3 + IDEuMzI2Yy0uODg1IDEuODI5LTEuODE0IDMuNjUxLTIuOTU0IDUuMzM2LTEu + MTcyIDEuNzMyLTIuMDczIDIuNjM2LTMuMzMgMi42MzYtLjc0NiAwLTEuMzg1 + LS4yOTItMi4wMy0uODAxLTEuMTAzLS45Mi0xLjkzNy0yLjA4OC0zLjE1LTIu + ODczLTEuNTY3Ljc4NS0yLjk5IDQuMDc5LTMuODI0IDUuOTggMi45MjUgMi44 + OCA2Ljg5OCA0LjU1IDExLjAwOCA0LjU3MyA0LjYyMi0uMDI4IDEwLjI4Ni0u + NDkgMTMuMTk3LTQuNjItLjU3NS03LjExMS00LjAxMy0xOC4zNzctMTIuODE0 + LTE4LjUxLTcuMDk3IDAtMTEuNzU0IDUuMDQ3LTE0Ljc3NSAxMy42NDRBMS41 + NjUgMS41NjUgMCAxIDEgLjM2IDE2LjAwOEMzLjc3MSA2LjI5OSA5LjMzMy4y + NyAxOC4wODguMjdaIiBmaWxsPSJ2YXIoLS1jb2xvci1pbmspIi8+PC9zdmc+ + CiAgICAgIDxzcGFuIGNsYXNzPSJhLWZvci1zY3JlZW4tcmVhZGVyIj5IUTwv + c3Bhbj4KICAgIDwvYT4KICAgIDxkaXYgY2xhc3M9Im5hdi1tZW51X19zaGVl + dCBleHBhbmRlZF9jb250ZW50IiBpZD0ibmF2aWdhdGlvbl9hY2NvdW50cyI+ + PC9kaXY+CiAgPC9kaXY+CjwvZGl2PgoKCiAgICA8dWwgY2xhc3M9Im5hdl9f + bWFpbiBsaXN0LS11bmJ1bGxldGVkIGxvYWRpbmdfX2hpZGUiPgogICAgICA8 + bGkgY2xhc3M9Im5hdl9faXRlbSI+CiAgICAgICAgPGEgY2xhc3M9Im5hdl9f + bGluayBuYXZfX2xpbmstLWhvbWUiIGhyZWY9Ii8xODE5MDA0MDUvcHJvamVj + dHMiPgogICAgICAgICAgPHN2ZyB2aWV3Qm94PSIwIDAgMjQgMjQiPjxwYXRo + IGQ9Im0xMi4yIDIxLjcgNy40IDEuM2guMmMuMiAwIC4zLS4xLjQtLjJsMi4y + LTIuMmMuMy0uMi4zLS44IDAtMS0uMi0uMy0uOC0uMy0xIDBsLS4zLjQgMS42 + LTcuMSAxLTEuMmMuMi0uMS4zLS4zLjItLjctLjEtLjMtLjItLjUtLjQtLjYt + LjItLjEtLjUgMC0uNy4ybC0uNC40LTYuMS05LjdjLS4yLS4zLS44LS41LTEu + MS0uMkw5LjQgNC42Yy0uMS4xLS4yLjEtLjMuMkwxLjggMTcuNGwtLjYtMWMt + LjEtLjItLjMtLjUtLjUtLjVzLS41LjEtLjYuM2MtLjEuNC0uMS42IDAgLjhs + MS4xIDIuMnYuMWMuMS4xLjEuMi4zLjMuMS4xLjIuMS4zLjFsNS4xLjkgMi44 + LTguOCAyLjUgOS45Wk0xMC45IDUuNiAxNS40IDNsNS43IDkuMi0uMS43LTEu + NiA3LTYtMTAuMS0yLjUtNC4yWiIgZmlsbD0iY3VycmVudENvbG9yIi8+PC9z + dmc+CiAgICAgICAgICBIb21lCjwvYT4gICAgICA8L2xpPgoKICAgICAgPGxp + IGNsYXNzPSJuYXZfX2l0ZW0iIGRhdGEtbWVudS1zZWN0aW9uPSJsaW5ldXAi + PgogICAgICAgIDxhIGNsYXNzPSJuYXZfX2xpbmsiIGhyZWY9Ii8xODE5MDA0 + MDUvbGluZXVwIj4KICAgICAgICAgIDxzdmcgdmlld0JveD0iMCAwIDI0IDI0 + Ij48cGF0aCBkPSJNOCA2YTIgMiAwIDAgMSAyLTJoMTBhMiAyIDAgMSAxIDAg + NEgxMGEyIDIgMCAwIDEtMi0yWm0tNiA2YTIgMiAwIDAgMSAyLTJoMTBhMiAy + IDAgMSAxIDAgNEg0YTIgMiAwIDAgMS0yLTJabTUgNGEyIDIgMCAxIDAgMCA0 + aDEwYTIgMiAwIDEgMCAwLTRIN1oiIGZpbGw9ImN1cnJlbnRDb2xvciIvPjwv + c3ZnPgogICAgICAgICAgTGluZXVwCjwvYT4gICAgICA8L2xpPgoKICAgICAg + ICA8bGkgY2xhc3M9Im5hdl9faXRlbSBuYXYtbWVudSIgZGF0YS1iZWhhdmlv + cj0iZXhwYW5kYWJsZSBsb2FkX29uX2V4cGFuZCIKICAgIGRhdGEtbWVudS1z + ZWN0aW9uPSJwaW5ncyIgZGF0YS1sb2FkLXVybD0iLzE4MTkwMDQwNS9teS9u + YXZpZ2F0aW9uL3BpbmdzIiBkYXRhLWxvYWQtdGFyZ2V0PSIjbmF2aWdhdGlv + bl9waW5ncyI+CiAgPGRpdiBjbGFzcz0iY29sbGFwc2libGVfY29udGVudCIg + ZGF0YS1iZWhhdmlvcj0iY29sbGFwc2Vfb25fY2xpY2tvdXRzaWRlIj4KICAg + IDxhIGNsYXNzPSJuYXZfX2xpbmsgbmF2X19saW5rLS1waW5ncyIgcm9sZT0i + YnV0dG9uIiBhcmlhLWhhc3BvcHVwPSJ0cnVlIiBkYXRhLWJlaGF2aW9yPSJ0 + b2dnbGVfZXhwYW5zaW9uX29uX2NsaWNrIiBocmVmPSIvMTgxOTAwNDA1L2Np + cmNsZXMiPgogICAgICA8c3ZnIHZpZXdib3g9IjAgMCAyNCAyNCI+PHBhdGgg + ZmlsbD0iY3VycmVudENvbG9yIiBkPSJNNSAzYTQgNCAwIDAgMC00IDR2NWE0 + LjAwMiA0LjAwMiAwIDAgMCAzIDMuODc0VjE5YTEgMSAwIDAgMCAxLjYyNS43 + OEwxMC4zNSAxNkgxNWE0IDQgMCAwIDAgNC00VjdhNCA0IDAgMCAwLTQtNEg1 + WiIvPjxwYXRoIGZpbGw9ImN1cnJlbnRDb2xvciIgZD0iTTEzIDIwYy0xLjMy + IDAtMi40OS0uNjM5LTMuMjE4LTEuNjI0bDEuMDk1LS44NzZIMTVhNS41IDUu + NSAwIDAgMCA1LjUtNS41VjkuMjlBNC4wMDEgNC4wMDEgMCAwIDEgMjMgMTN2 + M2E0LjAwMiA0LjAwMiAwIDAgMS0zIDMuODc0VjIyYTEgMSAwIDAgMS0xLjYu + OEwxNC42NjcgMjBIMTNaIi8+PC9zdmc+CiAgICAgIFBpbmdzCiAgICAgIDxz + cGFuIGNsYXNzPSJ1bnJlYWQtYmFkZ2UgdW5yZWFkLWJhZGdlLS1mb3ItbmF2 + IiBkYXRhLXVucmVhZC1iYWRnZT0icGluZ3MiIGRhdGEtYmFkZ2UtY291bnQ9 + IjAiPgogICAgICAgIDxzcGFuIGNsYXNzPSJhLWZvci1zY3JlZW4tcmVhZGVy + Ij51bnJlYWQ8L3NwYW4+CiAgICAgIDwvc3Bhbj4KPC9hPgogICAgPGRpdiBj + bGFzcz0ibmF2LW1lbnVfX3NoZWV0IG5hdi1tZW51X19zaGVldC0tbGFyZ2Ug + ZXhwYW5kZWRfY29udGVudCIgaWQ9Im5hdmlnYXRpb25fcGluZ3MiPjwvZGl2 + PgogIDwvZGl2Pgo8L2xpPgoKCiAgICAgIDxsaSBjbGFzcz0ibmF2X19pdGVt + IG5hdi1tZW51IiBkYXRhLWJlaGF2aW9yPSJleHBhbmRhYmxlIGxvYWRfb25f + ZXhwYW5kIgogICAgZGF0YS1tZW51LXNlY3Rpb249InJlYWRpbmdzIiBkYXRh + LWxvYWQtdXJsPSIvMTgxOTAwNDA1L215L25hdmlnYXRpb24vcmVhZGluZ3Mi + IGRhdGEtbG9hZC10YXJnZXQ9IiNuYXZpZ2F0aW9uX3JlYWRpbmdzIj4KICA8 + ZGl2IGNsYXNzPSJjb2xsYXBzaWJsZV9jb250ZW50IiBkYXRhLWJlaGF2aW9y + PSJjb2xsYXBzZV9vbl9jbGlja291dHNpZGUiPgogICAgPGEgY2xhc3M9Im5h + dl9fbGluayBuYXZfX2xpbmstLWhleSIgcm9sZT0iYnV0dG9uIiBpZD0ibmF2 + X19saW5rLS1oZXkiIGFyaWEtaGFzcG9wdXA9InRydWUiIGRhdGEtYmVoYXZp + b3I9InRvZ2dsZV9leHBhbnNpb25fb25fY2xpY2siIGRhdGEtY29udHJvbGxl + cj0iYW5pbWF0aW9uIiBkYXRhLWFuaW1hdGlvbi1ldmVudC1uYW1lLXZhbHVl + PSJiYzphZGQtdG8taGV5IiBkYXRhLWFuaW1hdGlvbi1wbGF5LWNsYXNzPSJu + YXZfX2xpbmstLWFkZC10by1oZXkiIGhyZWY9Ii8xODE5MDA0MDUvbXkvcmVh + ZGluZ3MiPgogICAgICA8c3ZnIHZpZXdib3g9IjAgMCAyNCAyNCI+PHBhdGgg + ZmlsbD0iY3VycmVudENvbG9yIiBmaWxsLXJ1bGU9ImV2ZW5vZGQiIGQ9Ik0x + LjAxMyAxMy44MzhhLjk5Mi45OTIgMCAwIDAtLjAxMy4xODRWMjBhMiAyIDAg + MCAwIDIgMmgxOGEyIDIgMCAwIDAgMi0ydi01Ljk3NmEuOTkuOTkgMCAwIDAt + LjA2OC0uMzg2TDE5LjQyIDMuOTc1QTMgMyAwIDAgMCAxNi41OTkgMkg3LjQw + MWEzIDMgMCAwIDAtMi44MiAxLjk3NWwtMy41MTIgOS42NmEuOTkyLjk5MiAw + IDAgMC0uMDU2LjIwM1ptNS40NDgtOS4xOEExIDEgMCAwIDEgNy40MDEgNGg5 + LjE5OGExIDEgMCAwIDEgLjk0LjY1OEwyMC41NzIgMTNoLTMuODUxYy0uODcg + MC0xLjU1NC41NDYtMS44OTggMS4xNzJDMTQuNDAzIDE0LjkzOCAxMy41NDYg + MTYgMTIgMTZzLTIuNDAzLTEuMDYyLTIuODIzLTEuODI4QzguODMzIDEzLjU0 + NiA4LjE0OSAxMyA3LjI3OSAxM0gzLjQyOEw2LjQ2IDQuNjU4WiIgY2xpcC1y + dWxlPSJldmVub2RkIi8+PC9zdmc+CiAgICAgIEhleSEgPHNwYW4gY2xhc3M9 + ImEtZm9yLXNjcmVlbi1yZWFkZXIiPk5vdGlmaWNhdGlvbiBpbmJveDwvc3Bh + bj4KICAgICAgPHNwYW4gY2xhc3M9InVucmVhZC1iYWRnZSB1bnJlYWQtYmFk + Z2UtLWZvci1uYXYiIGRhdGEtdW5yZWFkLWJhZGdlPSJoZXlzdGFjayIgZGF0 + YS1iYWRnZS1jb3VudD0iMCI+CiAgICAgICAgPHNwYW4gY2xhc3M9ImEtZm9y + LXNjcmVlbi1yZWFkZXIiPnVucmVhZDwvc3Bhbj4KICAgICAgPC9zcGFuPgo8 + L2E+ICAgIDxzcGFuIGNsYXNzPSJuYXZfX2xpbmstLWFuaW1hdGVkLXBhbmVs + Ij48L3NwYW4+CiAgICA8ZGl2IGNsYXNzPSJuYXYtbWVudV9fc2hlZXQgbmF2 + LW1lbnVfX3NoZWV0LS1sYXJnZSBuYXYtbWVudV9fc2hlZXQtLXVucGFkLXRv + cCBleHBhbmRlZF9jb250ZW50IiBpZD0ibmF2aWdhdGlvbl9yZWFkaW5ncyI+ + PC9kaXY+CiAgPC9kaXY+CjwvbGk+CgoKICAgICAgPGxpIGNsYXNzPSJuYXZf + X2l0ZW0iIGRhdGEtbWVudS1zZWN0aW9uPSJhY3Rpdml0eSI+CiAgICAgICAg + PGEgY2xhc3M9Im5hdl9fbGluayBuYXZfX2xpbmstLWFjdGl2aXR5IiBocmVm + PSIvMTgxOTAwNDA1L3JlcG9ydHMvcHJvZ3Jlc3MiPgogICAgICAgIDxzdmcg + dmlld0JveD0iMCAwIDI0IDI0Ij48cGF0aCBmaWxsPSJjdXJyZW50Q29sb3Ii + IGZpbGwtcnVsZT0iZXZlbm9kZCIgZD0iTTE5Ljc3OCAxOS43NzhsLjAwMi0u + MDAyQzIxLjc3IDE3Ljc4NiAyMyAxNS4wMzYgMjMgMTJjMC02LjA3NS00Ljky + NS0xMS0xMS0xMVMxIDUuOTI1IDEgMTJhMTAuOTYzIDEwLjk2MyAwIDAwMy4z + MTYgNy44N0ExMC45NjQgMTAuOTY0IDAgMDAxMiAyM2ExMC45NjMgMTAuOTYz + IDAgMDA3Ljc3OC0zLjIyMnpNMTMgMy4wNTV2OC41M2w2LjAzMiA2LjAzM0E5 + LjAwMSA5LjAwMSAwIDAwMTMgMy4wNTV6IiBjbGlwLXJ1bGU9ImV2ZW5vZGQi + Lz48L3N2Zz4KICAgICAgICBBY3Rpdml0eQo8L2E+ICAgICAgPC9saT4KCiAg + ICAgIDxsaSBjbGFzcz0ibmF2X19pdGVtIG5hdi1tZW51IgogICAgZGF0YS1i + ZWhhdmlvcj0iZXhwYW5kYWJsZSBsb2FkX29uX2V4cGFuZCBteV9zdHVmZl90 + b2dnbGVfc2hvcnRjdXQiCiAgICBkYXRhLW1lbnUtc2VjdGlvbj0ibXkiCiAg + ICBkYXRhLWxvYWQtdXJsPSIvMTgxOTAwNDA1L215L25hdmlnYXRpb24vbXlf + c3R1ZmYiCiAgICBkYXRhLWxvYWQtdGFyZ2V0PSIjbmF2aWdhdGlvbl9teV9z + dHVmZiI+CiAgPGRpdiBjbGFzcz0iY29sbGFwc2libGVfY29udGVudCIgZGF0 + YS1iZWhhdmlvcj0iY29sbGFwc2Vfb25fY2xpY2tvdXRzaWRlIj4KICAgIDxh + IGhyZWY9Ii8xODE5MDA0MDUvbXkvYXNzaWdubWVudHMiIGNsYXNzPSJuYXZf + X2xpbmsgbmF2X19saW5rLS1teS1zdHVmZiIgZGF0YS1iZWhhdmlvcj0idG9n + Z2xlX2V4cGFuc2lvbl9vbl9jbGljayIKICAgICAgZGF0YS1yb2xlPSJuYXYt + c2hvcnRjdXQtdGl0bGUiIGRhdGEtbWFjLXNob3J0Y3V0LXRpdGxlPSJLZXli + b2FyZCBzaG9ydGN1dDog4oyYICsgOyIKICAgICAgcm9sZT0iYnV0dG9uIiBh + cmlhLWxhYmVsPSJNeSBTdHVmZiIgYXJpYS1oYXNwb3B1cD0idHJ1ZSIgdGl0 + bGU9IktleWJvYXJkIHNob3J0Y3V0OiBDdHJsICsgOyI+CiAgICAgIDxzdmcg + dmlld2JveD0iMCAwIDI0IDI0Ij48cGF0aCBmaWxsPSJjdXJyZW50Q29sb3Ii + IGQ9Ik05IDExYTEuNSAxLjUgMCAxIDAgMC0zIDEuNSAxLjUgMCAwIDAgMCAz + Wk0xNi41IDkuNWExLjUgMS41IDAgMSAxLTMgMCAxLjUgMS41IDAgMCAxIDMg + MFpNOC41ODkgMTQuMjRhMSAxIDAgMCAwLTEuMzAxIDEuNTJBNy4yMjUgNy4y + MjUgMCAwIDAgMTIgMTcuNWE3LjIyNSA3LjIyNSAwIDAgMCA0LjcxMi0xLjc0 + IDEgMSAwIDEgMC0xLjMtMS41MkE1LjIyNSA1LjIyNSAwIDAgMSAxMiAxNS41 + YTUuMjI1IDUuMjI1IDAgMCAxLTMuNDExLTEuMjZaIi8+PHBhdGggZmlsbD0i + Y3VycmVudENvbG9yIiBmaWxsLXJ1bGU9ImV2ZW5vZGQiIGQ9Ik0xMiAxQzUu + OTI1IDEgMSA1LjkyNSAxIDEyczQuOTI1IDExIDExIDExIDExLTQuOTI1IDEx + LTExUzE4LjA3NSAxIDEyIDFaTTMgMTJhOSA5IDAgMSAxIDE4IDAgOSA5IDAg + MCAxLTE4IDBaIiBjbGlwLXJ1bGU9ImV2ZW5vZGQiLz48L3N2Zz4KICAgICAg + PHNwYW4gY2xhc3M9InUtaGlkZS1vbi1tZWRpYS1tZWRpdW0iPk1lPC9zcGFu + PgogICAgICA8c3BhbiBjbGFzcz0idS1oaWRlLW9uLW1lZGlhLXNtYWxsIj5N + eSBTdHVmZjwvc3Bhbj4KICAgIDwvYT4KCiAgICA8ZGl2IGNsYXNzPSJuYXYt + bWVudV9fc2hlZXQgbmF2LW1lbnVfX3NoZWV0LS1zbWFsbCBleHBhbmRlZF9j + b250ZW50IiBpZD0ibmF2aWdhdGlvbl9teV9zdHVmZiI+CiAgICA8L2Rpdj4K + ICA8L2Rpdj4KPC9saT4KCiAgICAgIDxsaSBjbGFzcz0ibmF2X19pdGVtIG5h + di1tZW51IgogICAgZGF0YS1tZW51LXNlY3Rpb249InNlYXJjaCIKICAgIGRh + dGEtYmVoYXZpb3I9ImV4cGFuZGFibGUgc2VhcmNoX3RvZ2dsZV9zaG9ydGN1 + dCIKICAgIGRhdGEtY29udHJvbGxlcj0ic2VhcmNoIGxpc3Qtc2VsZWN0aW9u + IgogICAgZGF0YS1hY3Rpb249IgogICAgICBjbGljay0+c2VhcmNoI2Nsb3Nl + U2VhcmNoV2hlbk5hdmlnYXRpbmcKICAgICAgYmVmb3JlLWNvbGxhcHNlLT5z + ZWFyY2gjc2F2ZVBvc2l0aW9uQW5kRXhwaXJhdGlvbgogICAgICBleHBhbmQt + PnNlYXJjaCNvcGVuCiAgICAgIGtleWRvd25Ad2luZG93LT5saXN0LXNlbGVj + dGlvbiN1cGRhdGVTZWxlY3Rpb25XaXRoS2V5Ym9hcmQiCiAgICBkYXRhLXNl + YXJjaC11cmw9Ii8xODE5MDA0MDUvbXkvbmF2aWdhdGlvbi9zZWFyY2hlcy9u + ZXciCiAgICBkYXRhLWxpc3Qtc2VsZWN0aW9uLXNlbGVjdGVkLWNsYXNzPSJz + ZWFyY2gtcmVzdWx0LS1zZWxlY3RlZCI+CiAgPGRpdiBjbGFzcz0iY29sbGFw + c2libGVfY29udGVudCIgZGF0YS1iZWhhdmlvcj0iY29sbGFwc2Vfb25fY2xp + Y2tvdXRzaWRlIj4KICAgIDxhIGNsYXNzPSJuYXZfX2xpbmsgbmF2X19saW5r + LS1zZWFyY2giIHJvbGU9ImJ1dHRvbiIgYXJpYS1oYXNwb3B1cD0idHJ1ZSIg + dGl0bGU9IktleWJvYXJkIHNob3J0Y3V0OiBDdHJsICsgLyIgZGF0YS1iZWhh + dmlvcj0idG9nZ2xlX2V4cGFuc2lvbl9vbl9jbGljayIgZGF0YS1yb2xlPSJu + YXYtc2hvcnRjdXQtdGl0bGUiIGRhdGEtbWFjLXNob3J0Y3V0LXRpdGxlPSJL + ZXlib2FyZCBzaG9ydGN1dDog4oyYICsgLyIgaHJlZj0iLzE4MTkwMDQwNS9z + ZWFyY2giPgogICAgICA8c3ZnIHZpZXdib3g9IjAgMCAyNCAyNCI+PHBhdGgg + ZmlsbD0iY3VycmVudENvbG9yIiBmaWxsLXJ1bGU9ImV2ZW5vZGQiIGQ9Ik0x + MSAzYTggOCAwIDEwNC44NTIgMTQuMzYxYzEuMDIxIDEuMjE0IDMuMjkzIDMu + OTA1IDMuNzQ0IDQuMzU3YTEuNSAxLjUgMCAwMDIuMTIxLTIuMTIyYy0uNDE4 + LS40MTgtMy4xMzUtMi43MTQtNC4zNTYtMy43NDNBOCA4IDAgMDAxMSAzem0t + NiA4YTYgNiAwIDExMTIgMCA2IDYgMCAwMS0xMiAweiIgY2xpcC1ydWxlPSJl + dmVub2RkIi8+PC9zdmc+CiAgICAgIEZpbmQKPC9hPgogICAgPGRpdiBjbGFz + cz0ibmF2LW1lbnVfX3NoZWV0IG5hdi1tZW51X19zaGVldC0tbGFyZ2UgZXhw + YW5kZWRfY29udGVudCIgZGF0YS1zZWFyY2gtdGFyZ2V0PSJmb3JtQ29udGFp + bmVyIj4KICAgIDwvZGl2PgogIDwvZGl2Pgo8L2xpPgoKICAgIDwvdWw+Cgog + ICAgPGRpdiBjbGFzcz0ibmF2LW1lbnUgbmF2LW1lbnVfX21lIiBkYXRhLWJl + aGF2aW9yPSJleHBhbmRhYmxlIGxvYWRfb25fZXhwYW5kIgogICAgZGF0YS1t + ZW51LXNlY3Rpb249Im1lIiBkYXRhLWxvYWQtdXJsPSIvMTgxOTAwNDA1L215 + L25hdmlnYXRpb24vbWUiIGRhdGEtbG9hZC10YXJnZXQ9IiNuYXZpZ2F0aW9u + X21lIj4KICA8ZGl2IGNsYXNzPSJjb2xsYXBzaWJsZV9jb250ZW50IiBkYXRh + LWJlaGF2aW9yPSJjb2xsYXBzZV9vbl9jbGlja291dHNpZGUiPgogICAgPGEg + aHJlZj0iLzE4MTkwMDQwNS9teS9wcm9maWxlIiBjbGFzcz0ibmF2X19saW5r + LS1tZSIgZGF0YS1iZWhhdmlvcj0idG9nZ2xlX2V4cGFuc2lvbl9vbl9jbGlj + ayIKICAgICAgcm9sZT0iYnV0dG9uIiBhcmlhLWxhYmVsPSJOb3RpZmljYXRp + b24gT3B0aW9ucyBhbmQgUGVyc29uYWwgU2V0dGluZ3MiIGFyaWEtaGFzcG9w + dXA9InRydWUiPgogICAgICA8c3BhbiBjbGFzcz0ibm90aWZpY2F0aW9uLXN0 + YXR1cyB1LWhpZGUtb24tbWVkaWEtc21hbGwiIGRhdGEtcm9sZT0ibXlfbm90 + aWZpY2F0aW9uc19iYWRnZSIgZGF0YS11cmw9Ii8xODE5MDA0MDUvbXkvbm90 + aWZpY2F0aW9ucyI+CiAgICAgICAgPHNwYW4gY2xhc3M9Im5vdGlmaWNhdGlv + bi1zdGF0dXNfX2xhYmVsIG5vdGlmaWNhdGlvbi1zdGF0dXNfX2xhYmVsLS1z + bm9vemVkIj5Gb2N1c2luZzwvc3Bhbj4KICAgICAgICA8c3BhbiBjbGFzcz0i + bm90aWZpY2F0aW9uLXN0YXR1c19fbGFiZWwgbm90aWZpY2F0aW9uLXN0YXR1 + c19fbGFiZWwtLW9mZiI+T2ZmPC9zcGFuPgogICAgICA8L3NwYW4+CgogICAg + ICA8aW1nIGNsYXNzPSJhdmF0YXIiIGRhdGEtY3VycmVudC1wZXJzb24tYXZh + dGFyPSJ0cnVlIiBhbHQ9Ik15IGF2YXRhciIgc3JjPSJodHRwOi8vMy5iYXNl + Y2FtcC5sb2NhbGhvc3Q6MzAwMS8xODE5MDA0MDUvbXkvYXZhdGFyIiB3aWR0 + aD0iMTMwIiBoZWlnaHQ9IjEzMCIgLz4KCiAgICAgIDxzcGFuIGNsYXNzPSJh + LWZvci1zY3JlZW4tcmVhZGVyIj5NZTwvc3Bhbj4KICAgIDwvYT4KCiAgICA8 + ZGl2IGNsYXNzPSJuYXYtbWVudV9fc2hlZXQgbmF2LW1lbnVfX3NoZWV0LS1h + bGlnbi1yaWdodCBleHBhbmRlZF9jb250ZW50IiBpZD0ibmF2aWdhdGlvbl9t + ZSI+CiAgICA8L2Rpdj4KICA8L2Rpdj4KPC9kaXY+CgogIDwvbmF2Pgo8L2Rp + dj4KCgogIDxub3NjcmlwdD4KICA8ZGl2IGNsYXNzPSJub3NjcmlwdC1tZXNz + YWdlIGNlbnRlcmVkIHUtbWFyZ2luLWNlbnRlcmVkIj4KICAgIDxzcGFuPuKa + oO+4jzwvc3Bhbj4KICAgIDxzcGFuIGNsYXNzPSJub3NjcmlwdC1tZXNzYWdl + X19jb250ZW50Ij4KICAgICAgU29tZSBCYXNlY2FtcCBmZWF0dXJlcyB3b27i + gJl0IHdvcmsgdW5sZXNzIHlvdSBlbmFibGUgSmF2YVNjcmlwdCBpbiB5b3Vy + IGJyb3dzZXIuCiAgICAgIDxhIGNsYXNzPSJkZWNvcmF0ZWQiIHRhcmdldD0i + X2JsYW5rIiBocmVmPSJodHRwOi8vZW5hYmxlLWphdmFzY3JpcHQuY29tIj5M + ZWFybiBtb3Jl4oCmPC9hPgogICAgPC9zcGFuPgogIDwvZGl2Pgo8L25vc2Ny + aXB0PgoKCiAgPGRpdiBjbGFzcz0ibG9hZGluZ19faGlkZSI+CiAgICA8ZGl2 + IGNsYXNzPSJoZWxwLWJ1dHRvbiBhcHAtbW9iaWxlX19oaWRlIiBkYXRhLXJv + bGU9ImhlbHBfYnV0dG9uX2NvbnRhaW5lciIgZGF0YS1iZWFjb24taWQ9Ijkx + MzgwOGQyLTEwMmMtNDVmYy1iZmQyLTA4NjhiMWExNDczOCI+PGJ1dHRvbiBu + YW1lPSJidXR0b24iIHR5cGU9ImJ1dHRvbiIgY2xhc3M9ImhlbHAtYnV0dG9u + X19pY29uIiBkYXRhLWJlaGF2aW9yPSJoZWxwX2J1dHRvbiIgdGFiaW5kZXg9 + IjEiPkdldCBoZWxwPC9idXR0b24+PC9kaXY+CiAgPC9kaXY+CgogIDxtYWlu + IGlkPSJtYWluLWNvbnRlbnQiIGNsYXNzPSJsb2FkaW5nX19oaWRlICB1LWhp + ZGUtZm9jdXMiIGRhdGEtYXBwZWFyaW5nLW9uPSJCQWg3QmtraUMxOXlZV2xz + Y3dZNkJrVlVld2RKSWdsa1lYUmhCanNBVkVraUxXZHBaRG92TDJKak15OVNa + V052Y21ScGJtY3ZOemd6TlRJMk1UQXhQMlY0Y0dseVpYTmZhVzRHT3dCVVNT + SUljSFZ5QmpzQVZFa2lEWEpsWVdSaFlteGxCanNBVkE9PS0tNGZlOTg0NzM1 + MzUwMDBlYjY5Zjc5Y2Q2YzZkNTRhMDdiNjdhMWZmOSIgZGF0YS1idWNrZXQt + dXJsPSIvMTgxOTAwNDA1L2J1Y2tldHMvMTA0Mjk3OTI0NyIgZGF0YS1idWNr + ZXQtaWQ9IjEwNDI5NzkyNDciIGRhdGEtY29udHJvbGxlcj0iYnJpZGdlLS1w + YWdlIGJyaWRnZS0taW5zZXRzIj4KICAgIDxkaXYgY2xhc3M9InUtZGlzcGxh + eS1uIiBkYXRhLWNvbnRyb2xsZXI9ImJlYWNvbiIgZGF0YS1iZWFjb24tdXJs + LXZhbHVlPSIvMTgxOTAwNDA1L3Byb2plY3RzLzEwNDI5NzkyNDcvcmVjZW50 + X3Zpc2l0Ij48L2Rpdj4KICAgIDxzZWN0aW9uIGNsYXNzPSJzeXN0ZW0tZGVn + cmFkYXRpb25zLWJhbm5lciBmb3JtYXR0ZWRfY29udGVudCI+CiAgPGgxIGNs + YXNzPSJ0eHQtLXdhcm5pbmcgY2VudGVyZWQgcHVzaF9oYWxmLS1ib3R0b20i + PgogICAgQmFzZWNhbXAgaXNu4oCZdCBmdWxseSBmdW5jdGlvbmFsIHJpZ2h0 + IG5vdy4KICA8L2gxPgoKICA8cCBkYXRhLXN5c3RlbS1kZWdyYWRhdGlvbj0i + dXBsb2Fkc19kaXNhYmxlZCIgY2xhc3M9InB1c2gtLWJvdHRvbSI+CiAgICA8 + c3Ryb25nPkZpbGUgdXBsb2FkaW5nIGlzIGRpc2FibGVkPC9zdHJvbmc+IGR1 + ZSB0byBhIHNlcnZpY2UgZGlzcnVwdGlvbi4KICAgIFlvdXIgZmlsZXMgYXJl + IHNhZmUsIGJ1dCB5b3UgY2Fu4oCZdCB1cGxvYWQgbmV3IGZpbGVzIHRvIG1l + c3NhZ2VzLCBjb21tZW50cywgZG9jdW1lbnRzLCBvciB0aGUgRG9jcyAmIEZp + bGVzIHNlY3Rpb24uCiAgPC9wPgoKICA8cCBkYXRhLXN5c3RlbS1kZWdyYWRh + dGlvbj0ic2VhcmNoX2Rpc2FibGVkIiBjbGFzcz0icHVzaC0tYm90dG9tIj4K + ICAgIDxzdHJvbmc+U2VhcmNoIGlzIG9mZmxpbmU8L3N0cm9uZz4gZHVlIHRv + IGEgc2VydmljZSBkaXNydXB0aW9uLgogICAgWW91ciBkYXRhIGlzIHNhZmUs + IGFuZCB5b3UgY2FuIGNvbnRpbnVlIHRvIHVzZSBCYXNlY2FtcC4KICA8L3A+ + CgogIDxwIGRhdGEtc3lzdGVtLWRlZ3JhZGF0aW9uPSJyZWFkb25seV9kYXRh + YmFzZSIgY2xhc3M9InB1c2gtLWJvdHRvbSI+CiAgICA8c3Ryb25nPlRoZSBk + YXRhYmFzZSBpcyBpbiByZWFkLW9ubHkgbW9kZTwvc3Ryb25nPiBkdWUgdG8g + YSBzZXJ2aWNlIGRpc3J1cHRpb24uCiAgICBZb3VyIGRhdGEgaXMgc2FmZSwg + YnV0IHlvdSBjYW7igJl0IGN1cnJlbnRseSBwb3N0IG5ldyBtZXNzYWdlcywg + dG8tZG9zLCBkb2N1bWVudHMsIGV0Yy4KICA8L3A+CgogIDxwIGRhdGEtc3lz + dGVtLWRlZ3JhZGF0aW9uPSJhY3Rpdml0eV90aW1lbGluZV9kaXNhYmxlZCIg + Y2xhc3M9InB1c2gtLWJvdHRvbSI+CiAgICA8c3Ryb25nPkFjdGl2aXR5IHRp + bWVsaW5lcyBhcmUgdGVtcG9yYXJpbHkgZGlzYWJsZWQ8L3N0cm9uZz4gZHVl + IHRvIGEgc2VydmljZSBkaXNydXB0aW9uLgogICAgWW91ciBkYXRhIGlzIHNh + ZmUsIGFuZCB5b3UgY2FuIGNvbnRpbnVlIHRvIHVzZSBCYXNlY2FtcC4KICA8 + L3A+CgogIDxmb290ZXI+CiAgICBXZeKAmXJlIHRlcnJpYmx5IHNvcnJ5IGZv + ciB0aGUgZGlzcnVwdGlvbiBhbmQgYXJlIHdvcmtpbmcgZGlsaWdlbnRseSB0 + byBicmluZyBCYXNlY2FtcCBiYWNrIHRvIDEwMCUuPGJyPgogICAgVmlzaXQg + PGEgaHJlZj0iaHR0cHM6Ly9iYXNlY2FtcC5jb20vc3RhdHVzIiB0YXJnZXQ9 + Il9ibGFuayI+b3VyIHN0YXR1cyBzaXRlPC9hPiBmb3IgdXBkYXRlcy4KICA8 + L2Zvb3Rlcj4KPC9zZWN0aW9uPgoKICAgIDxkaXYgaWQ9ImFubm91bmNlbWVu + dF9iYW5uZXIiIGNsYXNzPSJwdXNoX2hhbGYtLXNpZGVzIiBkYXRhLWNvbnRy + b2xsZXI9ImFubm91bmNlbWVudC1iYW5uZXIiIGRhdGEtYW5ub3VuY2VtZW50 + LWJhbm5lci11cmw9Imh0dHA6Ly8zLmJhc2VjYW1wLmxvY2FsaG9zdDozMDAx + LzE4MTkwMDQwNS9teS9hbm5vdW5jZW1lbnQiIGRhdGEtYW5ub3VuY2VtZW50 + LWJhbm5lci1yZWFkLXVybD0iaHR0cDovLzMuYmFzZWNhbXAubG9jYWxob3N0 + OjMwMDEvMTgxOTAwNDA1L215L2Fubm91bmNlbWVudC9yZWFkIiBkYXRhLXR1 + cmJvLXBlcm1hbmVudD0idHJ1ZSI+PC9kaXY+CiAgICAKCgoKPHNwYW4gZGF0 + YS1iZWhhdmlvcj0iZmxhc2hfbWVzc2FnZV9jb250YWluZXIiPgogIAo8L3Nw + YW4+CgogIAoKCiAgCgo8bmF2IGNsYXNzPSJyZWNvcmRpbmctYnJlYWRjcnVt + YnMgcmVjb3JkaW5nLWJyZWFkY3J1bWJzLS1tZXNzYWdlIGNlbnRlcmVkIHUt + cG9zaXRpb24tY29udGV4dCBhcHAtbW9iaWxlX19oaWRlIiBhcmlhLWxhYmVs + PSJicmVhZGNydW1iIiBkYXRhLWNvbnRyb2xsZXI9ImJyZWFkY3J1bWJzIiBk + YXRhLWFjdGlvbj0iY2xpY2stPmJyZWFkY3J1bWJzI25hdmlnYXRlQmFja09u + ZUxldmVsIiBkYXRhLWJyZWFkY3J1bWJzLXRhcmdldD0ibmF2YmFyIj4KICAg + IDxkaXYgY2xhc3M9InByb2plY3QtYmFkZ2VzIj4KICAgICAgCiAgICAgIAog + ICAgPC9kaXY+CgogIDxoMSBjbGFzcz0icmVjb3JkaW5nLWJyZWFkY3J1bWIi + IGRhdGEtYnJpZGdlLWhlYWRlcj4KICAgIAo8ZGl2IGNsYXNzPSJyZWNvcmRp + bmctYnJlYWRjcnVtYl9fdGl0bGUiIGRhdGEtYmVoYXZpb3I9ImV4cGFuZGFi + bGUiPgogIDxzcGFuIGNsYXNzPSJhLWZvci1zY3JlZW4tcmVhZGVyIj5xdWlj + ayBuYXY8L3NwYW4+CgogIDxzdHJvbmcgY2xhc3M9InUtcG9zaXRpb24tY29u + dGV4dCI+CgogICAgPGJ1dHRvbiBuYW1lPSJidXR0b24iIHR5cGU9InN1Ym1p + dCIgY2xhc3M9InJlY29yZGluZy1icmVhZGNydW1iX19qdW1wIiBhcmlhLWV4 + cGFuZGVkPSJmYWxzZSIgZGF0YS1icmVhZGNydW1icy10YXJnZXQ9Im9wZW5K + dW1wTWVudSIgZGF0YS1iZWhhdmlvcj0idG9nZ2xlX2V4cGFuc2lvbl9vbl9j + bGljayB0b2dnbGVfYnJlYWRjcnVtYnNfbWVudSI+CiAgICAgIDxzdmcgY2xh + c3M9InN2Zy1pY29uIHN2Zy1pY29uLS1ncmlkLXNvbGlkIiBhcmlhLWhpZGRl + bj0idHJ1ZSI+PHVzZSBocmVmPSIvYXNzZXRzL2ljb25zL3Nwcml0ZXMtNThk + YjAzZWE3Y2IzMGQzOTU1NmY4OWI3N2EzY2IzODg5ZTZkZjY2YTQ0ZmY3MjVj + YThkM2FmYWU3OTUxZjczNS5zdmcjZ3JpZC1zb2xpZCI+PC91c2U+PC9zdmc+ + CiAgICAgIDxzdmcgY2xhc3M9InN2Zy1pY29uIHN2Zy1pY29uLS1jaGV2cm9u + LWRvd24iIGFyaWEtaGlkZGVuPSJ0cnVlIj48dXNlIGhyZWY9Ii9hc3NldHMv + aWNvbnMvc3ByaXRlcy01OGRiMDNlYTdjYjMwZDM5NTU2Zjg5Yjc3YTNjYjM4 + ODllNmRmNjZhNDRmZjcyNWNhOGQzYWZhZTc5NTFmNzM1LnN2ZyNjaGV2cm9u + LWRvd24iPjwvdXNlPjwvc3ZnPgo8L2J1dHRvbj4gICAgPGEgY2xhc3M9ImRl + Y29yYXRlZCIgZGF0YS1icmVhZGNydW1icy10YXJnZXQ9ImxpbmsiIGhyZWY9 + Ii8xODE5MDA0MDUvcHJvamVjdHMvMTA0Mjk3OTI0NyI+VGVuLXllYXIgYW5u + aXZlcnNhcnk8L2E+CiAgPC9zdHJvbmc+CgogIDxzcGFuIGNsYXNzPSJyZWNv + cmRpbmctYnJlYWRjcnVtYl9fY2hpbGRyZW4iPgogICAgCiAgICAgIDxzcGFu + IGNsYXNzPSJyZWNvcmRpbmctYnJlYWRjcnVtYl9fc2VwYXJhdG9yIiBhcmlh + LWhpZGRlbj0idHJ1ZSI+IOKAuiA8L3NwYW4+PHNwYW4gY2xhc3M9InJlY29y + ZGluZy1icmVhZGNydW1iX19saW5rIj48YSBjbGFzcz0iZGVjb3JhdGVkIiBk + YXRhLWJyZWFkY3J1bWJzLXRhcmdldD0ibGluayIgaHJlZj0iLzE4MTkwMDQw + NS9idWNrZXRzLzEwNDI5NzkyNDcvbWVzc2FnZV9ib2FyZHMvMTIyMjAxNjY1 + Ij5NZXNzYWdlIEJvYXJkPC9hPjwvc3Bhbj4KCiAgPC9zcGFuPgoKICA8ZGl2 + IGNsYXNzPSJyZWNvcmRpbmctYnJlYWRjcnVtYl9fanVtcC1tZW51IHJlY29y + ZGluZy1icmVhZGNydW1iX19qdW1wLW1lbnUtLTktdXAgY2VudGVyZWQiIGRh + dGEtYmVoYXZpb3I9ImNvbGxhcHNlX29uX2NsaWNrb3V0c2lkZSIgZGF0YS1i + cmVhZGNydW1icy10YXJnZXQ9Imp1bXBNZW51Ij4KICAgICAgICA8YSBjbGFz + cz0icmVjb3JkaW5nLWJyZWFkY3J1bWJfX2p1bXAtYWN0aW9uIHJlY29yZGlu + Zy1icmVhZGNydW1iX19qdW1wLWFjdGlvbi0tbWVzc2FnZS1ib2FyZCIgaHJl + Zj0iLzE4MTkwMDQwNS9idWNrZXRzLzEwNDI5NzkyNDcvbWVzc2FnZV9ib2Fy + ZHMvMTIyMjAxNjY1Ij4KICAgICAgICAgIDxzcGFuIGNsYXNzPSJyZWNvcmRp + bmctYnJlYWRjcnVtYl9fanVtcC10aXRsZSB0eHQtLXRydW5jYXRlIj5NZXNz + YWdlIEJvYXJkPC9zcGFuPgogICAgICAgICAgPHNwYW4gY2xhc3M9InRvb2wt + aWNvbiByZWNvcmRpbmctYnJlYWRjcnVtYl9fanVtcC1pY29uIHRvb2wtaWNv + bi0tZmlsbGVkIiBzdHlsZT0iLS10b29sLWljb24tY29sb3I6IHZhcigtLWNv + bG9yLWJsdWUtNTApOyAiPjxzdmcgY2xhc3M9InN2Zy1pY29uIHN2Zy1pY29u + LS1tZWdhcGhvbmUtc29saWQiIGFyaWEtaGlkZGVuPSJ0cnVlIj48dXNlIGhy + ZWY9Ii9hc3NldHMvaWNvbnMvc3ByaXRlcy01OGRiMDNlYTdjYjMwZDM5NTU2 + Zjg5Yjc3YTNjYjM4ODllNmRmNjZhNDRmZjcyNWNhOGQzYWZhZTc5NTFmNzM1 + LnN2ZyNtZWdhcGhvbmUtc29saWQiPjwvdXNlPjwvc3ZnPjwvc3Bhbj4KPC9h + PiAgICAgICAgPGEgY2xhc3M9InJlY29yZGluZy1icmVhZGNydW1iX19qdW1w + LWFjdGlvbiByZWNvcmRpbmctYnJlYWRjcnVtYl9fanVtcC1hY3Rpb24tLWNo + YXQiIGhyZWY9Ii8xODE5MDA0MDUvYnVja2V0cy8xMDQyOTc5MjQ3L2NoYXRz + LzE2MjQ5NjM4MiI+CiAgICAgICAgICA8c3BhbiBjbGFzcz0icmVjb3JkaW5n + LWJyZWFkY3J1bWJfX2p1bXAtdGl0bGUgdHh0LS10cnVuY2F0ZSI+Q2hhdDwv + c3Bhbj4KICAgICAgICAgIDxzcGFuIGNsYXNzPSJ0b29sLWljb24gcmVjb3Jk + aW5nLWJyZWFkY3J1bWJfX2p1bXAtaWNvbiB0b29sLWljb24tLWZpbGxlZCIg + c3R5bGU9Ii0tdG9vbC1pY29uLWNvbG9yOiB2YXIoLS1jb2xvci1hcXVhLTUw + KTsgIj48c3ZnIGNsYXNzPSJzdmctaWNvbiBzdmctaWNvbi0tbWVzc2FnZXMt + c29saWQiIGFyaWEtaGlkZGVuPSJ0cnVlIj48dXNlIGhyZWY9Ii9hc3NldHMv + aWNvbnMvc3ByaXRlcy01OGRiMDNlYTdjYjMwZDM5NTU2Zjg5Yjc3YTNjYjM4 + ODllNmRmNjZhNDRmZjcyNWNhOGQzYWZhZTc5NTFmNzM1LnN2ZyNtZXNzYWdl + cy1zb2xpZCI+PC91c2U+PC9zdmc+PC9zcGFuPgo8L2E+ICAgICAgICA8YSBj + bGFzcz0icmVjb3JkaW5nLWJyZWFkY3J1bWJfX2p1bXAtYWN0aW9uIHJlY29y + ZGluZy1icmVhZGNydW1iX19qdW1wLWFjdGlvbi0tdG9kb3NldCIgaHJlZj0i + LzE4MTkwMDQwNS9idWNrZXRzLzEwNDI5NzkyNDcvdG9kb3NldHMvNDg3Njg3 + Mjg2Ij4KICAgICAgICAgIDxzcGFuIGNsYXNzPSJyZWNvcmRpbmctYnJlYWRj + cnVtYl9fanVtcC10aXRsZSB0eHQtLXRydW5jYXRlIj5Uby1kb3M8L3NwYW4+ + CiAgICAgICAgICA8c3BhbiBjbGFzcz0idG9vbC1pY29uIHJlY29yZGluZy1i + cmVhZGNydW1iX19qdW1wLWljb24gdG9vbC1pY29uLS1maWxsZWQiIHN0eWxl + PSItLXRvb2wtaWNvbi1jb2xvcjogdmFyKC0tY29sb3ItZ3JlZW4tNTApOyAi + PjxzdmcgY2xhc3M9InN2Zy1pY29uIHN2Zy1pY29uLS1jaGVjay1zb2xpZCIg + YXJpYS1oaWRkZW49InRydWUiPjx1c2UgaHJlZj0iL2Fzc2V0cy9pY29ucy9z + cHJpdGVzLTU4ZGIwM2VhN2NiMzBkMzk1NTZmODliNzdhM2NiMzg4OWU2ZGY2 + NmE0NGZmNzI1Y2E4ZDNhZmFlNzk1MWY3MzUuc3ZnI2NoZWNrLXNvbGlkIj48 + L3VzZT48L3N2Zz48L3NwYW4+CjwvYT4gICAgICAgIDxhIGNsYXNzPSJyZWNv + cmRpbmctYnJlYWRjcnVtYl9fanVtcC1hY3Rpb24gcmVjb3JkaW5nLWJyZWFk + Y3J1bWJfX2p1bXAtYWN0aW9uLS12YXVsdCIgaHJlZj0iLzE4MTkwMDQwNS9i + dWNrZXRzLzEwNDI5NzkyNDcvdmF1bHRzLzUzOTkzNTc4NSI+CiAgICAgICAg + ICA8c3BhbiBjbGFzcz0icmVjb3JkaW5nLWJyZWFkY3J1bWJfX2p1bXAtdGl0 + bGUgdHh0LS10cnVuY2F0ZSI+RG9jcyAmYW1wOyBGaWxlczwvc3Bhbj4KICAg + ICAgICAgIDxzcGFuIGNsYXNzPSJ0b29sLWljb24gcmVjb3JkaW5nLWJyZWFk + Y3J1bWJfX2p1bXAtaWNvbiB0b29sLWljb24tLWZpbGxlZCIgc3R5bGU9Ii0t + dG9vbC1pY29uLWNvbG9yOiB2YXIoLS1jb2xvci15ZWxsb3ctNTApOyAiPjxz + dmcgY2xhc3M9InN2Zy1pY29uIHN2Zy1pY29uLS1mb2xkZXItc29saWQiIGFy + aWEtaGlkZGVuPSJ0cnVlIj48dXNlIGhyZWY9Ii9hc3NldHMvaWNvbnMvc3By + aXRlcy01OGRiMDNlYTdjYjMwZDM5NTU2Zjg5Yjc3YTNjYjM4ODllNmRmNjZh + NDRmZjcyNWNhOGQzYWZhZTc5NTFmNzM1LnN2ZyNmb2xkZXItc29saWQiPjwv + dXNlPjwvc3ZnPjwvc3Bhbj4KPC9hPiAgICAgICAgPGEgY2xhc3M9InJlY29y + ZGluZy1icmVhZGNydW1iX19qdW1wLWFjdGlvbiByZWNvcmRpbmctYnJlYWRj + cnVtYl9fanVtcC1hY3Rpb24tLXF1ZXN0aW9ubmFpcmUiIGhyZWY9Ii8xODE5 + MDA0MDUvYnVja2V0cy8xMDQyOTc5MjQ3L3F1ZXN0aW9ubmFpcmVzLzczMTM3 + ODkyNSI+CiAgICAgICAgICA8c3BhbiBjbGFzcz0icmVjb3JkaW5nLWJyZWFk + Y3J1bWJfX2p1bXAtdGl0bGUgdHh0LS10cnVuY2F0ZSI+Q2hlY2staW5zPC9z + cGFuPgogICAgICAgICAgPHNwYW4gY2xhc3M9InRvb2wtaWNvbiByZWNvcmRp + bmctYnJlYWRjcnVtYl9fanVtcC1pY29uIHRvb2wtaWNvbi0tZmlsbGVkIiBz + dHlsZT0iLS10b29sLWljb24tY29sb3I6IHZhcigtLWNvbG9yLXZpb2xldC01 + MCk7ICI+PHN2ZyBjbGFzcz0ic3ZnLWljb24gc3ZnLWljb24tLXF1ZXN0aW9u + IiBhcmlhLWhpZGRlbj0idHJ1ZSI+PHVzZSBocmVmPSIvYXNzZXRzL2ljb25z + L3Nwcml0ZXMtNThkYjAzZWE3Y2IzMGQzOTU1NmY4OWI3N2EzY2IzODg5ZTZk + ZjY2YTQ0ZmY3MjVjYThkM2FmYWU3OTUxZjczNS5zdmcjcXVlc3Rpb24iPjwv + dXNlPjwvc3ZnPjwvc3Bhbj4KPC9hPiAgICAgICAgPGEgY2xhc3M9InJlY29y + ZGluZy1icmVhZGNydW1iX19qdW1wLWFjdGlvbiByZWNvcmRpbmctYnJlYWRj + cnVtYl9fanVtcC1hY3Rpb24tLXNjaGVkdWxlIiBocmVmPSIvMTgxOTAwNDA1 + L2J1Y2tldHMvMTA0Mjk3OTI0Ny9zY2hlZHVsZXMvNzE0NTg1NzE1Ij4KICAg + ICAgICAgIDxzcGFuIGNsYXNzPSJyZWNvcmRpbmctYnJlYWRjcnVtYl9fanVt + cC10aXRsZSB0eHQtLXRydW5jYXRlIj5TY2hlZHVsZTwvc3Bhbj4KICAgICAg + ICAgIDxzcGFuIGNsYXNzPSJ0b29sLWljb24gcmVjb3JkaW5nLWJyZWFkY3J1 + bWJfX2p1bXAtaWNvbiB0b29sLWljb24tLWZpbGxlZCIgc3R5bGU9Ii0tdG9v + bC1pY29uLWNvbG9yOiB2YXIoLS1jb2xvci1waW5rLTUwKTsgIj48c3ZnIGNs + YXNzPSJzdmctaWNvbiBzdmctaWNvbi0tY2FsZW5kYXItc29saWQiIGFyaWEt + aGlkZGVuPSJ0cnVlIj48dXNlIGhyZWY9Ii9hc3NldHMvaWNvbnMvc3ByaXRl + cy01OGRiMDNlYTdjYjMwZDM5NTU2Zjg5Yjc3YTNjYjM4ODllNmRmNjZhNDRm + ZjcyNWNhOGQzYWZhZTc5NTFmNzM1LnN2ZyNjYWxlbmRhci1zb2xpZCI+PC91 + c2U+PC9zdmc+PC9zcGFuPgo8L2E+ICAgICAgICA8YSBjbGFzcz0icmVjb3Jk + aW5nLWJyZWFkY3J1bWJfX2p1bXAtYWN0aW9uIHJlY29yZGluZy1icmVhZGNy + dW1iX19qdW1wLWFjdGlvbi0taW5ib3giIGhyZWY9Ii8xODE5MDA0MDUvYnVj + a2V0cy8xMDQyOTc5MjQ3L2luYm94ZXMvNTU0NjYzNDg3Ij4KICAgICAgICAg + IDxzcGFuIGNsYXNzPSJyZWNvcmRpbmctYnJlYWRjcnVtYl9fanVtcC10aXRs + ZSB0eHQtLXRydW5jYXRlIj5Gb3J3YXJkczwvc3Bhbj4KICAgICAgICAgIDxz + cGFuIGNsYXNzPSJ0b29sLWljb24gcmVjb3JkaW5nLWJyZWFkY3J1bWJfX2p1 + bXAtaWNvbiB0b29sLWljb24tLWZpbGxlZCIgc3R5bGU9Ii0tdG9vbC1pY29u + LWNvbG9yOiB2YXIoLS1jb2xvci1wdXJwbGUtNTApOyAiPjxzdmcgY2xhc3M9 + InN2Zy1pY29uIHN2Zy1pY29uLS1lbWFpbC1zb2xpZCIgYXJpYS1oaWRkZW49 + InRydWUiPjx1c2UgaHJlZj0iL2Fzc2V0cy9pY29ucy9zcHJpdGVzLTU4ZGIw + M2VhN2NiMzBkMzk1NTZmODliNzdhM2NiMzg4OWU2ZGY2NmE0NGZmNzI1Y2E4 + ZDNhZmFlNzk1MWY3MzUuc3ZnI2VtYWlsLXNvbGlkIj48L3VzZT48L3N2Zz48 + L3NwYW4+CjwvYT4gICAgICAgIDxhIHRhcmdldD0iX2JsYW5rIiBjbGFzcz0i + cmVjb3JkaW5nLWJyZWFkY3J1bWJfX2p1bXAtYWN0aW9uIHJlY29yZGluZy1i + cmVhZGNydW1iX19qdW1wLWRvb3IiIGhyZWY9Imh0dHBzOi8vZ2l0aHViLmNv + bS9iYXNlY2FtcCI+CiAgICAgICAgICA8c3BhbiBjbGFzcz0icmVjb3JkaW5n + LWJyZWFkY3J1bWJfX2p1bXAtdGl0bGUgdHh0LS10cnVuY2F0ZSI+U291cmNl + IENvZGVzPC9zcGFuPgogICAgICAgICAgPGRpdiBjbGFzcz0iYXBwX2ljb24g + YXBwX2ljb24tLWJyZWFkY3J1bWIiPgogICAgICAgICAgICA8aW1nIGNsYXNz + PSJhcHBfaWNvbiIgc3JjPSIvL2JjMy1jZG4ubG9jYWxob3N0OjMwMDEvYXNz + ZXRzL2ljb25zL2FwcF9pY29ucy9naXRodWItMzUzNjM0MzIzMWYyYWQ1NWM3 + ZmRjY2I2ZjAwMzQ0MGZkMGE3ZTc3NWRmYTUxYmQxNTUyNWUxNWJjNjlhNmQy + Yy5wbmciIC8+CiAgICAgICAgICA8L2Rpdj4KPC9hPiAgICAgICAgPGEgY2xh + c3M9InJlY29yZGluZy1icmVhZGNydW1iX19qdW1wLWFjdGlvbiByZWNvcmRp + bmctYnJlYWRjcnVtYl9fanVtcC1hY3Rpb24tLWthbmJhbi1ib2FyZCIgaHJl + Zj0iLzE4MTkwMDQwNS9idWNrZXRzLzEwNDI5NzkyNDcvY2FyZF90YWJsZXMv + NDE2ODg3NDgzIj4KICAgICAgICAgIDxzcGFuIGNsYXNzPSJyZWNvcmRpbmct + YnJlYWRjcnVtYl9fanVtcC10aXRsZSB0eHQtLXRydW5jYXRlIj5QbGFubmlu + Zzwvc3Bhbj4KICAgICAgICAgIDxzcGFuIGNsYXNzPSJ0b29sLWljb24gcmVj + b3JkaW5nLWJyZWFkY3J1bWJfX2p1bXAtaWNvbiB0b29sLWljb24tLWZpbGxl + ZCIgc3R5bGU9Ii0tdG9vbC1pY29uLWNvbG9yOiB2YXIoLS1jb2xvci1vcmFu + Z2UtNTApOyAiPjxzdmcgY2xhc3M9InN2Zy1pY29uIHN2Zy1pY29uLS1jYXJk + LXRhYmxlLXNvbGlkIiBhcmlhLWhpZGRlbj0idHJ1ZSI+PHVzZSBocmVmPSIv + YXNzZXRzL2ljb25zL3Nwcml0ZXMtNThkYjAzZWE3Y2IzMGQzOTU1NmY4OWI3 + N2EzY2IzODg5ZTZkZjY2YTQ0ZmY3MjVjYThkM2FmYWU3OTUxZjczNS5zdmcj + Y2FyZC10YWJsZS1zb2xpZCI+PC91c2U+PC9zdmc+PC9zcGFuPgo8L2E+ICA8 + L2Rpdj4KPC9kaXY+CiAgPC9oMT4KPC9uYXY+CgoKPGRpdiBjbGFzcz0icGFu + ZWwgcGFuZWwtLXBlcm1hIiBkYXRhLWNvbnRyb2xsZXI9InN1YnNjcmlwdGlv + bnMtdmlld2VyICIgZGF0YS1jcmVhdG9yLWlkPSI1MjU0NjAyMDciPjxiYy1t + b2RhbCBvcGVuZWQ9ImZhbHNlIiBkYXRhLXN1YnNjcmlwdGlvbnMtdmlld2Vy + LXRhcmdldD0ic3Vic2NyaXB0aW9uc01vZGFsIiBkYXRhLWFjdGlvbj0iY2xv + c2UtJmd0O3N1YnNjcmlwdGlvbnMtdmlld2VyI3VucG9wdWxhdGVNb2RhbEVs + ZW1lbnQiPjwvYmMtbW9kYWw+CiAgPGRpdiBjbGFzcz0icHJvamVjdC1iYWRn + ZXMiPgogICAgCiAgPC9kaXY+CgogIAogIAogIDxkaXYgY2xhc3M9InBhbmVs + X19yaWdodC1hY3Rpb25zIj4KICAgIDxhIGNsYXNzPSJidG4gYnRuLS1zbWFs + bCBidG4taWNvbi0tbGVmdCBhcHAtbW9iaWxlX19oaWRlIHotMSIgZGF0YS12 + aXNpYmxlLXRvLWNyZWF0b3Itb3ItYWRtaW49IiIgaHJlZj0iLzE4MTkwMDQw + NS9idWNrZXRzLzEwNDI5NzkyNDcvbWVzc2FnZXMvNzgzNTI2MTAxL2VkaXQi + PkVkaXQ8L2E+CiAgPC9kaXY+CgoKPGFzaWRlIGNsYXNzPSJwZXJtYS10b29s + YmFyIGFwcC1tb2JpbGVfX2hpZGUiIGRhdGEtY3JlYXRvci1pZD0iNTI1NDYw + MjA3Ij4KICAKCjxzcGFuIGNsYXNzPSJhY3Rpb24tc2hlZXQiIGRhdGEtYmVo + YXZpb3I9ImV4cGFuZGFibGUgY29sbGFwc2Vfb25fY2xpY2siIGRhdGEtY29u + dHJvbGxlcj0iZGVza3RvcC1tb2RhbCI+CiAgPGJ1dHRvbiBuYW1lPSJidXR0 + b24iIHR5cGU9ImJ1dHRvbiIgdGl0bGU9IlNob3cgb3B0aW9uc+KApiIgYXJp + YS1sYWJlbD0iT3B0aW9ucyBtZW51IiBjbGFzcz0iYWN0aW9uLXNoZWV0X19l + eHBhbnNpb24tdG9nZ2xlIGJ0biBidG4tLWljb24gYnRuLS1vdmVyZmxvdy1p + Y29uIGJ0bi0tc21hbGwgIiBkYXRhLWJyaWRnZS1vcHRpb25zLW1lbnU9IiIg + ZGF0YS1iZWhhdmlvcj0idG9nZ2xlX2V4cGFuc2lvbl9vbl9jbGljayB0b2dn + bGVfZmlsZV9tZW51Ij5GaWxl4oCmPC9idXR0b24+CgogIDxkaXYgY2xhc3M9 + ImFjdGlvbi1zaGVldF9fY29udGVudCIgZGF0YS1iZWhhdmlvcj0iY29sbGFw + c2Vfb25fY2xpY2tvdXRzaWRlIj4KICAgIDxidXR0b24gbmFtZT0iYnV0dG9u + IiB0eXBlPSJzdWJtaXQiIGFyaWEtbGFiZWw9IkNsb3NlIG1lbnUiIGNsYXNz + PSJhY3Rpb24tc2hlZXRfX2Nsb3NlIGJ0biBidG4tLXNtYWxsIGJ0bi0taWNv + biBidG4tLWNsb3NlLXNoZWV0LWljb24gIiBkYXRhLWJlaGF2aW9yPSJjb2xs + YXBzZV9vbl9jbGljayI+Q2xvc2U8L2J1dHRvbj4KCiAgICA8YSBjbGFzcz0i + YWN0aW9uLXNoZWV0X19hY3Rpb24gYWN0aW9uLXNoZWV0X19hY3Rpb24tLWVk + aXQiIGRhdGEtYnJpZGdlLW1lbnUtYWN0aW9uPSJ0cnVlIiBkYXRhLWJyaWRn + ZS1hY3Rpb24tdHlwZT0iZWRpdCIgZGF0YS12aXNpYmxlLXRvLWNyZWF0b3It + b3ItYWRtaW49ImZhbHNlIiBocmVmPSIvMTgxOTAwNDA1L2J1Y2tldHMvMTA0 + Mjk3OTI0Ny9tZXNzYWdlcy83ODM1MjYxMDEvZWRpdCI+RWRpdDwvYT4KCiAg + ICAgICAgPGEgY2xhc3M9ImFjdGlvbi1zaGVldF9fYWN0aW9uIGFjdGlvbi1z + aGVldF9fYWN0aW9uLS1tb3ZlIiBkYXRhLWJyaWRnZS1tZW51LWFjdGlvbj0i + dHJ1ZSIgZGF0YS1icmlkZ2UtYWN0aW9uLXR5cGU9Im1vdmUiIGRhdGEtYWN0 + aW9uPSJkZXNrdG9wLW1vZGFsI29wZW5Nb2RhbCIgZGF0YS1iZWhhdmlvcj0i + Y29sbGFwc2Vfb25fY2xpY2siIGhyZWY9Ii8xODE5MDA0MDUvYnVja2V0cy8x + MDQyOTc5MjQ3L3JlY29yZGluZ3MvNzgzNTI2MTAxL21vdmVzL25ldyI+TW92 + ZTwvYT4KCiAgICA8YSBjbGFzcz0iYWN0aW9uLXNoZWV0X19hY3Rpb24gYWN0 + aW9uLXNoZWV0X19hY3Rpb24tLWNvcHkiIGRhdGEtYnJpZGdlLW1lbnUtYWN0 + aW9uPSJ0cnVlIiBkYXRhLWJyaWRnZS1hY3Rpb24tdHlwZT0iY29weSIgZGF0 + YS1hY3Rpb249ImRlc2t0b3AtbW9kYWwjb3Blbk1vZGFsIiBkYXRhLWJlaGF2 + aW9yPSJjb2xsYXBzZV9vbl9jbGljayIgaHJlZj0iLzE4MTkwMDQwNS9idWNr + ZXRzLzEwNDI5NzkyNDcvcmVjb3JkaW5ncy83ODM1MjYxMDEvY29waWVzL25l + dyI+Q29weTwvYT4KCiAgICAKCgogICAgICAgIDxhIGNsYXNzPSJhY3Rpb24t + c2hlZXRfX2FjdGlvbiBhY3Rpb24tc2hlZXRfX2FjdGlvbi0tYXJjaGl2ZSIg + ZGF0YS1icmlkZ2UtbWVudS1hY3Rpb249InRydWUiIGRhdGEtYnJpZGdlLWFj + dGlvbi10eXBlPSJhcmNoaXZlIiBkYXRhLWRpc2FibGUtd2l0aD0iQXJjaGl2 + aW5n4oCmIiBkYXRhLXR1cmJvLXByZWZldGNoPSJmYWxzZSIgZGF0YS1yZW1v + dGU9InRydWUiIHJlbD0ibm9mb2xsb3ciIGRhdGEtbWV0aG9kPSJQVVQiIGhy + ZWY9Ii8xODE5MDA0MDUvYnVja2V0cy8xMDQyOTc5MjQ3L3JlY29yZGluZ3Mv + NzgzNTI2MTAxL3N0YXR1cy9hcmNoaXZlZCI+QXJjaGl2ZTwvYT4KCiAgICAg + ICAgPGEgY2xhc3M9ImFjdGlvbi1zaGVldF9fYWN0aW9uIGFjdGlvbi1zaGVl + dF9fYWN0aW9uLS10cmFzaCIgZGF0YS1icmlkZ2UtbWVudS1hY3Rpb249InRy + dWUiIGRhdGEtYnJpZGdlLWFjdGlvbi10eXBlPSJkZWxldGUiIGRhdGEtZGlz + YWJsZS13aXRoPSJNb3ZpbmcgdG8gdGhlIHRyYXNo4oCmIiBkYXRhLXR1cmJv + LXByZWZldGNoPSJmYWxzZSIgZGF0YS1yZW1vdGU9InRydWUiIHJlbD0ibm9m + b2xsb3ciIGRhdGEtbWV0aG9kPSJQVVQiIGhyZWY9Ii8xODE5MDA0MDUvYnVj + a2V0cy8xMDQyOTc5MjQ3L3JlY29yZGluZ3MvNzgzNTI2MTAxL3N0YXR1cy90 + cmFzaGVkIj5QdXQgaW4gdGhlIHRyYXNoPC9hPgoKICAgIDxhIHRpdGxlPSJB + ZGQvcmVtb3ZlIGJvb2ttYXJr4oCmIiBkYXRhLWNvbnRyb2xsZXI9ImJvb2tt + YXJrcyIgZGF0YS1hY3Rpb249ImJvb2ttYXJrcyN0b2dnbGVCb29rbWFyayIg + ZGF0YS1ib29rbWFya3MtdXJsPSIvMTgxOTAwNDA1L215L2Jvb2ttYXJrcy9C + QWg3QmtraUMxOXlZV2xzY3dZNkJrVlVld2RKSWdsa1lYUmhCanNBVkVraUxX + ZHBaRG92TDJKak15OVNaV052Y21ScGJtY3ZOemd6TlRJMk1UQXhQMlY0Y0ds + eVpYTmZhVzRHT3dCVVNTSUljSFZ5QmpzQVZFa2lEWEpsWVdSaFlteGxCanNB + VkE9PS0tNGZlOTg0NzM1MzUwMDBlYjY5Zjc5Y2Q2YzZkNTRhMDdiNjdhMWZm + OSIgZGF0YS1ib29rbWFya3MtYm9va21hcmtlZC1jbGFzcz0iYnRuLS1ib29r + bWFya2VkLWljb24iIGRhdGEtYnJpZGdlLWFjdGlvbi10eXBlPSJib29rbWFy + ayIgZGF0YS10dXJiby1wZXJtYW5lbnQ9InRydWUiIGRhdGEtYnJpZGdlLW1l + bnUtYWN0aW9uPSJ0cnVlIiBjbGFzcz0iYWN0aW9uLXNoZWV0X19hY3Rpb24g + YWN0aW9uLXNoZWV0X19hY3Rpb24tLWJvb2ttYXJrIiBpZD0idG9nZ2xlX2Jv + b2ttYXJrXzc4MzUyNjEwMSIgaHJlZj0iIyI+Qm9va21hcms8L2E+CiAgICAK + ICAgIDxhIGNsYXNzPSJhY3Rpb24tc2hlZXRfX2FjdGlvbiBhY3Rpb24tc2hl + ZXRfX2FjdGlvbi0tY29tbWVudHMtb2ZmIiBkYXRhLWJyaWRnZS1tZW51LWFj + dGlvbj0idHJ1ZSIgZGF0YS1yZW1vdGU9InRydWUiIGRhdGEtbWV0aG9kPSJw + b3N0IiBkYXRhLWJyaWRnZS1hY3Rpb24tdHlwZT0iY29tbWVudHMtb2ZmIiBk + YXRhLXZpc2libGUtdG8tY3JlYXRvci1vci1hZG1pbj0idHJ1ZSIgZGF0YS10 + dXJiby1wcmVmZXRjaD0iZmFsc2UiIGhyZWY9Ii8xODE5MDA0MDUvYnVja2V0 + cy8xMDQyOTc5MjQ3L3JlY29yZGluZ3MvNzgzNTI2MTAxL2NvbW1lbnRzX2Ns + b3N1cmUiPkNsb3NlIGNvbW1lbnRzPC9hPgogICAgPGEgY2xhc3M9ImFjdGlv + bi1zaGVldF9fYWN0aW9uIGFjdGlvbi1zaGVldF9fYWN0aW9uLS1tZW1vcnki + IGRhdGEtYnJpZGdlLW1lbnUtYWN0aW9uPSJ0cnVlIiBkYXRhLWNvbnRyb2xs + ZXI9Im1lbW9yeSIgZGF0YS1icmlkZ2UtYWN0aW9uLXR5cGU9Im1lbW9yeSIg + ZGF0YS1tZW1vcnktdXJsLXZhbHVlPSIvMTgxOTAwNDA1L2J1Y2tldHMvMTA0 + Mjk3OTI0Ny9yZWNvcmRpbmdzLzc4MzUyNjEwMS9tZW1vcnkiIGRhdGEtbWVt + b3J5LWlkZW50aWZpZXItdmFsdWU9IloybGtPaTh2WW1NekwxSmxZMjl5Wkds + dVp5ODNPRE0xTWpZeE1ERSIgZGF0YS1tZW1vcnktbWVtb3JpemVkLXZhbHVl + PSJmYWxzZSIgZGF0YS1tZW1vcnktYWRkLWxhYmVsLXZhbHVlPSJEb27igJl0 + IEZvcmdldCIgZGF0YS1tZW1vcnktcmVtb3ZlLWxhYmVsLXZhbHVlPSJSZW1v + dmUgZnJvbSBEb27igJl0IEZvcmdldCIgZGF0YS12aXNpYmxlLXRvLWNyZWF0 + b3Itb3ItYWRtaW49ImZhbHNlIiBkYXRhLWFjdGlvbj0ibWVtb3J5I3RvZ2ds + ZSIgdGl0bGU9IkRvbuKAmXQgRm9yZ2V0IiBocmVmPSIjIj5Eb27igJl0IEZv + cmdldDwvYT4KICAgIDxmb3JtIGNsYXNzPSJhY3Rpb24tc2hlZXRfX2FjdGlv + bi0tYnV0dG9uIiBtZXRob2Q9InBvc3QiIGFjdGlvbj0iLzE4MTkwMDQwNS9i + dWNrZXRzLzEwNDI5NzkyNDcvcmVjb3JkaW5ncy83ODM1MjYxMDEvcGluIj48 + aW5wdXQgdHlwZT0iaGlkZGVuIiBuYW1lPSJfbWV0aG9kIiB2YWx1ZT0iZGVs + ZXRlIiBhdXRvY29tcGxldGU9Im9mZiIgLz48YnV0dG9uIGNsYXNzPSJhY3Rp + b24tc2hlZXRfX2FjdGlvbiBhY3Rpb24tc2hlZXRfX2FjdGlvbl9fYnV0dG9u + IGFjdGlvbi1zaGVldF9fYWN0aW9uLS1waW4iIGRhdGEtYnJpZGdlLW1lbnUt + YWN0aW9uPSJ0cnVlIiBkYXRhLWJyaWRnZS1hY3Rpb24tdHlwZT0icGluIiBk + YXRhLXBpbm5lZD0idHJ1ZSIgdGl0bGU9IlVucGluIiB0eXBlPSJzdWJtaXQi + PlVucGluPC9idXR0b24+PGlucHV0IHR5cGU9ImhpZGRlbiIgbmFtZT0iYXV0 + aGVudGljaXR5X3Rva2VuIiB2YWx1ZT0ielE1QlJDcnJVaWdXMFBoRjhUN3JM + NElRY3RYc1VocXczeGxLUHhFTWhYTmFJbUJyTkFzUjdVN2RaY0JER2pzRk9y + ZXJWZmw5SEZud2h5TlZlNWUzSVEiIGF1dG9jb21wbGV0ZT0ib2ZmIiAvPjwv + Zm9ybT4KCiAgICAgIDxhIGNsYXNzPSJhY3Rpb24tc2hlZXRfX2FjdGlvbiBh + Y3Rpb24tc2hlZXRfX2FjdGlvbi0tZXhwb3J0IiBkYXRhLWJlaGF2aW9yPSJl + eHBvcnQiIGhyZWY9Ii8xODE5MDA0MDUvYnVja2V0cy8xMDQyOTc5MjQ3L3Jl + Y29yZGluZ3MvNzgzNTI2MTAxL2F0dGFjaG1lbnRfZXhwb3J0cyI+RG93bmxv + YWQgYWxsIGF0dGFjaG1lbnRzPC9hPgoKICAgIAoKICAgICAgPGg1IGNsYXNz + PSJhY3Rpb24tc2hlZXRfX2JyZWFrIHB1c2hfaGFsZi0tdG9wIHB1c2hfcXVh + cnRlci0tYm90dG9tIj5TaGFyZTwvaDU+CgogICAgICA8YSBjbGFzcz0iYWN0 + aW9uLXNoZWV0X19hY3Rpb24gYWN0aW9uLXNoZWV0X19hY3Rpb24tLWxpbmsi + IGRhdGEtYnJpZGdlLW1lbnUtYWN0aW9uPSJ0cnVlIiBkYXRhLWJyaWRnZS1h + Y3Rpb24tdHlwZT0ibGluayIgdGl0bGU9IkdldCBhIHB1YmxpYyBsaW5rIiBo + cmVmPSIvMTgxOTAwNDA1L2J1Y2tldHMvMTA0Mjk3OTI0Ny9yZWNvcmRpbmdz + Lzc4MzUyNjEwMS9wdWJsaWNhdGlvbiI+R2V0IGEgcHVibGljIGxpbms8L2E+ + CiAgICAgIDxhIGNsYXNzPSJhY3Rpb24tc2hlZXRfX2FjdGlvbiBhY3Rpb24t + c2hlZXRfX2FjdGlvbi0tc2hhcmUiIGRhdGEtYnJpZGdlLW1lbnUtYWN0aW9u + PSJ0cnVlIiBkYXRhLWJyaWRnZS1hY3Rpb24tdHlwZT0ibm90aWZ5IiB0aXRs + ZT0iTm90aWZ5IHNvbWVvbmUiIGhyZWY9Ii8xODE5MDA0MDUvYnVja2V0cy8x + MDQyOTc5MjQ3L3JlY29yZGluZ3MvNzgzNTI2MTAxL25vdGlmaWNhdGlvbnMv + bmV3Ij5TZW5kIHRoaXMgdG8gc29tZW9uZTwvYT4KCiAgICAgIAoKICAgIAoK + ICAgICAgPGg1IGNsYXNzPSJhY3Rpb24tc2hlZXRfX2JyZWFrIHB1c2hfaGFs + Zi0tdG9wIHB1c2hfcXVhcnRlci0tYm90dG9tIj5IaXN0b3J5PC9oNT4KCiAg + ICAgIDxhIGNsYXNzPSJhY3Rpb24tc2hlZXRfX2FjdGlvbiBhY3Rpb24tc2hl + ZXRfX2FjdGlvbi0taGlzdG9yeSIgZGF0YS1icmlkZ2UtbWVudS1hY3Rpb249 + InRydWUiIGRhdGEtYnJpZGdlLWFjdGlvbi10eXBlPSJoaXN0b3J5IiBocmVm + PSIvMTgxOTAwNDA1L2J1Y2tldHMvMTA0Mjk3OTI0Ny9yZWNvcmRpbmdzLzc4 + MzUyNjEwMS9ldmVudHMiPlZpZXcgY2hhbmdlIGxvZzwvYT4KCiAgICAgICAg + PHNwYW4gY2xhc3M9InUtaGlkZS1vbi10ZW1wbGF0ZSI+CiAgICA8YSBocmVm + PSIjIiBjbGFzcz0iYWN0aW9uLXNoZWV0X19hY3Rpb24gYWN0aW9uLXNoZWV0 + X19hY3Rpb24tLXBlb3BsZSIgZGF0YS1hY3Rpb249InN1YnNjcmlwdGlvbnMt + dmlld2VyI29wZW5TdWJzY3JpcHRpb25zTW9kYWwiIGRhdGEtdXJsPSIvMTgx + OTAwNDA1L2J1Y2tldHMvMTA0Mjk3OTI0Ny9yZWNvcmRpbmdzLzc4MzUyNjEw + MS9ub3RpZmllZF9yZWNpcGllbnRzIj4KICAgICAgTm90aWZpZWQKICAgICAg + MSBwZXJzb24KICAgIDwvYT4KPC9zcGFuPgoKCiAgICAKICA8L2Rpdj4KCiAg + PGJjLW1vZGFsIG9wZW5lZD0iZmFsc2UiCiAgICBkYXRhLWRlc2t0b3AtbW9k + YWwtdGFyZ2V0PSJtb2RhbCIKICAgIGRhdGEtYWN0aW9uPSJjbG9zZS0+ZGVz + a3RvcC1tb2RhbCNyZXNldE1vZGFsIgogICAgY2xvc2Utb24tc3VibWl0PSJm + YWxzZSIKICAgIGNsb3NlLW9uLWNsaWNrLW91dHNpZGU9ImZhbHNlIj4KICA8 + L2JjLW1vZGFsPgo8L3NwYW4+Cgo8L2FzaWRlPgoKCiAgCgoKICAKICA8YXJ0 + aWNsZSBjbGFzcz0ibWVzc2FnZSByZWNvcmRhYmxlIHJlY29yZGluZyIgZGF0 + YS1yZWNvcmRpbmctaWQ9Ijc4MzUyNjEwMSIgZGF0YS1jcmVhdG9yLWlkPSI1 + MjU0NjAyMDciIGRhdGEtdXJsPSIvMTgxOTAwNDA1L2J1Y2tldHMvMTA0Mjk3 + OTI0Ny9tZXNzYWdlcy83ODM1MjYxMDEiIGRhdGEtcmVhZGFibGUtaWRlbnRp + Zmllcj0iWjJsa09pOHZZbU16TDFKbFkyOXlaR2x1Wnk4M09ETTFNall4TURF + IiBpZD0icmVjb3JkaW5nXzc4MzUyNjEwMSI+PHNwYW4gY2xhc3M9ImFuY2hv + ciByZWNvcmRhYmxlX19hbmNob3IiIGlkPSJfX3JlY29yZGluZ183ODM1MjYx + MDEiPjwvc3Bhbj4KICAgIDxoZWFkZXIgY2xhc3M9Im1lc3NhZ2VfX2hlYWRl + ciI+CiAgICAgICAgPGRpdiBjbGFzcz0ibWVzc2FnZV9faW50cm8gbWVzc2Fn + ZV9faW50cm8tLWNhdGVnb3J5Ij7wn5OiIEFubm91bmNlbWVudDwvZGl2Pgog + ICAgICA8aDEgY2xhc3M9Im1lc3NhZ2VfX3N1YmplY3QgZmx1c2gtLXRvcCBw + dXNoX3F1YXJ0ZXItLWJvdHRvbSI+CiAgICAgICAgPHR1cmJvLWZyYW1lIGNs + YXNzPSJpbmxpbmUtZWRpdC1mb3JtIiBpZD0icmVjb3JkaW5nX3RpdGxlX3Jl + Y29yZGluZ183ODM1MjYxMDEiPjxhIHJvbGU9ImJ1dHRvbiIgZGF0YS1iZWhh + dmlvcj0icHJldmVudF9saW5rX2FjdGlvbiIgZGF0YS10dXJiby1tZXRob2Q9 + ImdldCIgYXJpYS1kZXNjcmlwdGlvbj0iUmVuYW1lIHRoaXMgbWVzc2FnZS4i + IGhyZWY9Ii8xODE5MDA0MDUvYnVja2V0cy8xMDQyOTc5MjQ3L3JlY29yZGlu + Z3MvNzgzNTI2MTAxL3RpdGxlL2VkaXQiPkEgZGVjYWRlITwvYT48L3R1cmJv + LWZyYW1lPgogICAgICA8L2gxPgoKICAgICAgPGRpdiBjbGFzcz0ibWVzc2Fn + ZV9fYXR0cmlidXRpb24iPgogICAgICAgIDxkaXYgY2xhc3M9Im1lc3NhZ2Vf + X21ldGEiPgogICAgICAgICAgPGZpZ3VyZSBjbGFzcz0ibWVzc2FnZV9fYXZh + dGFyIj4KICAgICAgICAgICAgPGltZyBjbGFzcz0iYXZhdGFyIG1lc3NhZ2Vf + X2F2YXRhci1pbWFnZSIgZGF0YS1iZWhhdmlvcj0icmljaF9hdmF0YXIiIGRh + dGEtcmljaC1hdmF0YXItdXJsPSIvMTgxOTAwNDA1L3Blb3BsZS81MjU0NjAy + MDcvcmljaF9hdmF0YXIiIGRhdGEtYXZhdGFyLWZvci1wZXJzb24taWQ9IjUy + NTQ2MDIwNyIgYWx0PSJEYXZpZCBIZWluZW1laWVyIEhhbnNzb24iIHRpdGxl + PSJEYXZpZCBIZWluZW1laWVyIEhhbnNzb24sIEJhc2VjYW1wIiBzcmM9Imh0 + dHA6Ly9iYzMtY2RuLmxvY2FsaG9zdDozMDAxLzE4MTkwMDQwNS9wZW9wbGUv + QkFocEJPJTJGaVVSOD0tLWExNDMwOGVmOGIxMDFhNjYxN2EzNDVlNDRlZmEw + ZTM1NWVjZDA0MmUvYXZhdGFyP3Y9MSIgd2lkdGg9IjEzMCIgaGVpZ2h0PSIx + MzAiIC8+CiAgICAgICAgICA8L2ZpZ3VyZT4KICAgICAgICAgIDxkaXY+CiAg + ICAgICAgICAgIERhdmlkIEhlaW5lbWVpZXIgSGFuc3NvbgogICAgICAgICAg + ICAKICAgICAgICAgIDwvZGl2PgogICAgICAgIDwvZGl2PgoKICAgICAgICAg + IDxkaXYgY2xhc3M9Im1lc3NhZ2VfX21ldGEgdS1oaWRlLW9uLXRlbXBsYXRl + Ij4KICAgICAgICAgICAgPHRpbWUgZGF0ZXRpbWU9IjIwMjUtMDgtMDhUMTc6 + MjY6NTZaIiBkYXRhLWxvY2FsPSJkYXRlIj5BdWd1c3QgIDgsIDIwMjUgIDU6 + MjZwbTwvdGltZT4KICAgICAgICAgIDwvZGl2PgoKICAgICAgICAgIDxkaXYg + Y2xhc3M9Im1lc3NhZ2VfX21ldGEiPgogICAgICAgICAgICAgIDxhIGhyZWY9 + IiMiIGRhdGEtYWN0aW9uPSJzdWJzY3JpcHRpb25zLXZpZXdlciNvcGVuU3Vi + c2NyaXB0aW9uc01vZGFsIiBkYXRhLXVybD0iLzE4MTkwMDQwNS9idWNrZXRz + LzEwNDI5NzkyNDcvcmVjb3JkaW5ncy83ODM1MjYxMDEvbm90aWZpZWRfcmVj + aXBpZW50cyI+CiAgICBOb3RpZmllZCAxIHBlcnNvbgogIDwvYT4KCiAgICAg + ICAgICA8L2Rpdj4KICAgICAgPC9kaXY+CiAgICA8L2hlYWRlcj4KCiAgICA8 + c2VjdGlvbiBjbGFzcz0icHVzaF9kb3VibGUtLXRvcCI+CiAgICAgIDxkaXYg + Y2xhc3M9ImZvcm1hdHRlZF9jb250ZW50IGZvcm1hdHRlZF9jb250ZW50LS1s + YXJnZSI+CiAgICAgICAgV2hhdCBmdW4sIGRvbid0IHlvdSB0aGluayBASkY/ + CiAgICAgIDwvZGl2PgogICAgPC9zZWN0aW9uPgoKICAgICAgPGRpdiBjbGFz + cz0icHVzaC0tdG9wIHUtaGlkZS1vbi10ZW1wbGF0ZSI+CiAgICAgICAgPGRp + diBjbGFzcz0iYm9vc3RzIGJvb3N0cy0tc21hbGwgbWV0YWRhdGEgcHVzaC0t + Ym90dG9tICIKICAgIGRhdGEtY29udHJvbGxlcj0iYm9vc3RzIgogICAgZGF0 + YS1ib29zdHMtaGFzLWJvb3N0cy1zZWxlY3Rvci12YWx1ZT0iLmJvb3N0Om5v + dCguYm9vc3QtZm9ybSkiCiAgICBkYXRhLWJvb3N0cy1hZGRpbmctc2VsZWN0 + b3ItdmFsdWU9Ii5ib29zdC1mb3JtLmV4cGFuZGVkIgogICAgZGF0YS1ib29z + dHMtZW1wdHktY2xhc3M9ImJvb3N0cy0tZW1wdHkiCiAgICBkYXRhLWJvb3N0 + cy1hZGRpbmctY2xhc3M9ImJvb3N0cy0tYWRkaW5nIgogICAgZGF0YS1ib29z + dHMtZGVsZXRpbmctY2xhc3M9ImJvb3N0LS1kZWxldGluZyI+CiAgPHR1cmJv + LWZyYW1lIGNsYXNzPSJ1LWRpc3BsYXktYyIgaWQ9ImJvb3N0c19yZWNvcmRp + bmdfNzgzNTI2MTAxIj4KICAgICAgPGRpdiBjbGFzcz0iYm9vc3QiIGRhdGEt + Y3JlYXRvci1pZD0iMjcwOTEzNzg5IiBkYXRhLWJlaGF2aW9yPSJleHBhbmRh + YmxlIiBkYXRhLWJvb3N0LWlkPSI5NzE2MzY0MzciPgogICAgPGJ1dHRvbiBj + bGFzcz0iYS1mb3Itc2NyZWVuLXJlYWRlciIgYXJpYS1leHBhbmRlZD0iZmFs + c2UiIGRhdGEtdmlzaWJsZS10by1jcmVhdG9yLW9yLWFkbWluPSJ0cnVlIiBk + YXRhLWJlaGF2aW9yPSJ0b2dnbGVfZXhwYW5zaW9uX29uX2NsaWNrIj4KICAg + ICAgICAgIDxzcGFuIGFyaWEtb3ducz0iY29udGVudF9ib29zdF85NzE2MzY0 + MzciPjwvc3Bhbj4KPC9idXR0b24+CiAgICA8aW1nIGNsYXNzPSJhdmF0YXIg + Ym9vc3RfX2F2YXRhciIgYXJpYS1oaWRkZW49InRydWUiIGRhdGEtYmVoYXZp + b3I9InJpY2hfYXZhdGFyIiBkYXRhLXJpY2gtYXZhdGFyLXVybD0iLzE4MTkw + MDQwNS9wZW9wbGUvMjcwOTEzNzg5L3JpY2hfYXZhdGFyIiBkYXRhLWF2YXRh + ci1mb3ItcGVyc29uLWlkPSIyNzA5MTM3ODkiIGFsdD0iSmFzb24gRnJpZWQi + IHRpdGxlPSJKYXNvbiBGcmllZCwgQmFzZWNhbXAiIHNyYz0iaHR0cDovL2Jj + My1jZG4ubG9jYWxob3N0OjMwMDEvMTgxOTAwNDA1L3Blb3BsZS9CQWhwQlAz + UUpSQT0tLTc0YTY4ODM2MzEwZTljODZhMzlhZDQ1NzVjZjZhZjc3YzM3ZWE0 + NDUvYXZhdGFyP3Y9MSIgd2lkdGg9IjEzMCIgaGVpZ2h0PSIxMzAiIC8+Cgog + ICAgPGEgZGF0YS10dXJiby1tZXRob2Q9ImRlbGV0ZSIgZGF0YS12aXNpYmxl + LXRvLWNyZWF0b3Itb3ItYWRtaW49InRydWUiIGRhdGEtYWN0aW9uPSJib29z + dHMjc2hvd0RlbGV0aW5nRWZmZWN0IiBkYXRhLWJvb3N0cy1wYXJlbnQtaWQt + cGFyYW09Ijk3MTYzNjQzNyIgY2xhc3M9ImJ0biBidG4tLXNtYWxsIGJ0bi0t + Ym9yZGVybGVzcyBidG4tLWJvb3N0LWRlbGV0ZSBidG4tLWJvb3N0LXRyYXNo + IGV4cGFuZGVkX2NvbnRlbnQiIGFyaWEtbGFiZWw9IkRlbGV0ZSB0aGlzIGJv + b3N0IiByb2xlPSJidXR0b24iIGhyZWY9Ii8xODE5MDA0MDUvYnVja2V0cy8x + MDQyOTc5MjQ3L2Jvb3N0cy85NzE2MzY0Mzc/Ym9vc3QlNUJib29zdGFibGVf + Z2lkJTVEPVoybGtPaTh2WW1NekwxSmxZMjl5WkdsdVp5ODNPRE0xTWpZeE1E + RSI+PC9hPgoKICAgIDxzcGFuIGNsYXNzPSJib29zdF9fY29udGVudCB1LW5v + d3JhcCAiIGRhdGEtYmVoYXZpb3I9InRvZ2dsZV9leHBhbnNpb25fb25fY2xp + Y2sgY29sbGFwc2Vfb25fY2xpY2tvdXRzaWRlIgogICAgICBpZD0iY29udGVu + dF9ib29zdF85NzE2MzY0MzciPgogICAgICA8c3BhbiBhcmlhLWhpZGRlbj0i + dHJ1ZSI+Y29uZ3JhdHMhPC9zcGFuPgogICAgICA8c3BhbiBjbGFzcz0iYS1m + b3Itc2NyZWVuLXJlYWRlciI+SmFzb24gRi4gYm9vc3RlZCB0aGUgbWVzc2Fn + ZSB3aXRoICYjMzk7Y29uZ3JhdHMhJiMzOTs8L3NwYW4+CiAgICA8L3NwYW4+ + CiAgPC9kaXY+CiAgPGRpdiBjbGFzcz0iYm9vc3QiIGRhdGEtY3JlYXRvci1p + ZD0iOTUwNzM3NDU2IiBkYXRhLWJlaGF2aW9yPSJleHBhbmRhYmxlIiBkYXRh + LWJvb3N0LWlkPSI0MTk5NTIxOTgiPgogICAgPGJ1dHRvbiBjbGFzcz0iYS1m + b3Itc2NyZWVuLXJlYWRlciIgYXJpYS1leHBhbmRlZD0iZmFsc2UiIGRhdGEt + dmlzaWJsZS10by1jcmVhdG9yLW9yLWFkbWluPSJ0cnVlIiBkYXRhLWJlaGF2 + aW9yPSJ0b2dnbGVfZXhwYW5zaW9uX29uX2NsaWNrIj4KICAgICAgICAgIDxz + cGFuIGFyaWEtb3ducz0iY29udGVudF9ib29zdF80MTk5NTIxOTgiPjwvc3Bh + bj4KPC9idXR0b24+CiAgICA8aW1nIGNsYXNzPSJhdmF0YXIgYm9vc3RfX2F2 + YXRhciIgYXJpYS1oaWRkZW49InRydWUiIGRhdGEtYmVoYXZpb3I9InJpY2hf + YXZhdGFyIiBkYXRhLXJpY2gtYXZhdGFyLXVybD0iLzE4MTkwMDQwNS9wZW9w + bGUvOTUwNzM3NDU2L3JpY2hfYXZhdGFyIiBkYXRhLWF2YXRhci1mb3ItcGVy + c29uLWlkPSI5NTA3Mzc0NTYiIGFsdD0iUnlhbiBTaW5nZXIiIHRpdGxlPSJS + eWFuIFNpbmdlciwgQmFzZWNhbXAiIHNyYz0iaHR0cDovL2JjMy1jZG4ubG9j + YWxob3N0OjMwMDEvMTgxOTAwNDA1L3Blb3BsZS9CQWhwQkRBYXF6Zz0tLWU1 + YTlkMWRlMTA1NDBlNWUwNmFmMmI3ZWUzZmQ0YzQxMjk1ZTlkMzEvYXZhdGFy + P3Y9MSIgd2lkdGg9IjEzMCIgaGVpZ2h0PSIxMzAiIC8+CgogICAgPGEgZGF0 + YS10dXJiby1tZXRob2Q9ImRlbGV0ZSIgZGF0YS12aXNpYmxlLXRvLWNyZWF0 + b3Itb3ItYWRtaW49InRydWUiIGRhdGEtYWN0aW9uPSJib29zdHMjc2hvd0Rl + bGV0aW5nRWZmZWN0IiBkYXRhLWJvb3N0cy1wYXJlbnQtaWQtcGFyYW09IjQx + OTk1MjE5OCIgY2xhc3M9ImJ0biBidG4tLXNtYWxsIGJ0bi0tYm9yZGVybGVz + cyBidG4tLWJvb3N0LWRlbGV0ZSBidG4tLWJvb3N0LXRyYXNoIGV4cGFuZGVk + X2NvbnRlbnQiIGFyaWEtbGFiZWw9IkRlbGV0ZSB0aGlzIGJvb3N0IiByb2xl + PSJidXR0b24iIGhyZWY9Ii8xODE5MDA0MDUvYnVja2V0cy8xMDQyOTc5MjQ3 + L2Jvb3N0cy80MTk5NTIxOTg/Ym9vc3QlNUJib29zdGFibGVfZ2lkJTVEPVoy + bGtPaTh2WW1NekwxSmxZMjl5WkdsdVp5ODNPRE0xTWpZeE1ERSI+PC9hPgoK + ICAgIDxzcGFuIGNsYXNzPSJib29zdF9fY29udGVudCB1LW5vd3JhcCAiIGRh + dGEtYmVoYXZpb3I9InRvZ2dsZV9leHBhbnNpb25fb25fY2xpY2sgY29sbGFw + c2Vfb25fY2xpY2tvdXRzaWRlIgogICAgICBpZD0iY29udGVudF9ib29zdF80 + MTk5NTIxOTgiPgogICAgICA8c3BhbiBhcmlhLWhpZGRlbj0idHJ1ZSI+Z29v + ZCBqb2IhPC9zcGFuPgogICAgICA8c3BhbiBjbGFzcz0iYS1mb3Itc2NyZWVu + LXJlYWRlciI+UnlhbiBTLiBib29zdGVkIHRoZSBtZXNzYWdlIHdpdGggJiMz + OTtnb29kIGpvYiEmIzM5Ozwvc3Bhbj4KICAgIDwvc3Bhbj4KICA8L2Rpdj4K + ICA8ZGl2IGNsYXNzPSJib29zdCIgZGF0YS1jcmVhdG9yLWlkPSIzMjMwNjY1 + ODEiIGRhdGEtYmVoYXZpb3I9ImV4cGFuZGFibGUiIGRhdGEtYm9vc3QtaWQ9 + Ijk1NDg3MjExOCI+CiAgICA8YnV0dG9uIGNsYXNzPSJhLWZvci1zY3JlZW4t + cmVhZGVyIiBhcmlhLWV4cGFuZGVkPSJmYWxzZSIgZGF0YS12aXNpYmxlLXRv + LWNyZWF0b3Itb3ItYWRtaW49InRydWUiIGRhdGEtYmVoYXZpb3I9InRvZ2ds + ZV9leHBhbnNpb25fb25fY2xpY2siPgogICAgICAgICAgPHNwYW4gYXJpYS1v + d25zPSJjb250ZW50X2Jvb3N0Xzk1NDg3MjExOCI+PC9zcGFuPgo8L2J1dHRv + bj4KICAgIDxpbWcgY2xhc3M9ImF2YXRhciBib29zdF9fYXZhdGFyIiBhcmlh + LWhpZGRlbj0idHJ1ZSIgZGF0YS1iZWhhdmlvcj0icmljaF9hdmF0YXIiIGRh + dGEtcmljaC1hdmF0YXItdXJsPSIvMTgxOTAwNDA1L3Blb3BsZS8zMjMwNjY1 + ODEvcmljaF9hdmF0YXIiIGRhdGEtYXZhdGFyLWZvci1wZXJzb24taWQ9IjMy + MzA2NjU4MSIgYWx0PSJKZXJlbXkgS2VtcGVyIiB0aXRsZT0iSmVyZW15IEtl + bXBlciwgQmFzZWNhbXAiIHNyYz0iaHR0cDovL2JjMy1jZG4ubG9jYWxob3N0 + OjMwMDEvMTgxOTAwNDA1L3Blb3BsZS9CQWhwQk5XYVFSTT0tLTVmNjgyMDYw + NmIzZDJiODg0OGRjZDliZjdmMTkwMWIyMWJiNmY0M2MvYXZhdGFyP3Y9MSIg + d2lkdGg9IjEzMCIgaGVpZ2h0PSIxMzAiIC8+CgogICAgPGEgZGF0YS10dXJi + by1tZXRob2Q9ImRlbGV0ZSIgZGF0YS12aXNpYmxlLXRvLWNyZWF0b3Itb3It + YWRtaW49InRydWUiIGRhdGEtYWN0aW9uPSJib29zdHMjc2hvd0RlbGV0aW5n + RWZmZWN0IiBkYXRhLWJvb3N0cy1wYXJlbnQtaWQtcGFyYW09Ijk1NDg3MjEx + OCIgY2xhc3M9ImJ0biBidG4tLXNtYWxsIGJ0bi0tYm9yZGVybGVzcyBidG4t + LWJvb3N0LWRlbGV0ZSBidG4tLWJvb3N0LXRyYXNoIGV4cGFuZGVkX2NvbnRl + bnQiIGFyaWEtbGFiZWw9IkRlbGV0ZSB0aGlzIGJvb3N0IiByb2xlPSJidXR0 + b24iIGhyZWY9Ii8xODE5MDA0MDUvYnVja2V0cy8xMDQyOTc5MjQ3L2Jvb3N0 + cy85NTQ4NzIxMTg/Ym9vc3QlNUJib29zdGFibGVfZ2lkJTVEPVoybGtPaTh2 + WW1NekwxSmxZMjl5WkdsdVp5ODNPRE0xTWpZeE1ERSI+PC9hPgoKICAgIDxz + cGFuIGNsYXNzPSJib29zdF9fY29udGVudCB1LW5vd3JhcCAiIGRhdGEtYmVo + YXZpb3I9InRvZ2dsZV9leHBhbnNpb25fb25fY2xpY2sgY29sbGFwc2Vfb25f + Y2xpY2tvdXRzaWRlIgogICAgICBpZD0iY29udGVudF9ib29zdF85NTQ4NzIx + MTgiPgogICAgICA8c3BhbiBhcmlhLWhpZGRlbj0idHJ1ZSI+bmljZSE8L3Nw + YW4+CiAgICAgIDxzcGFuIGNsYXNzPSJhLWZvci1zY3JlZW4tcmVhZGVyIj5K + ZXJlbXkgSy4gYm9vc3RlZCB0aGUgbWVzc2FnZSB3aXRoICYjMzk7bmljZSEm + IzM5Ozwvc3Bhbj4KICAgIDwvc3Bhbj4KICA8L2Rpdj4KCjwvdHVyYm8tZnJh + bWU+CiAgPHR1cmJvLWZyYW1lIGNsYXNzPSJ1LWRpc3BsYXktYyIgaWQ9Im5l + d19ib29zdF9yZWNvcmRpbmdfNzgzNTI2MTAxIj4KICAgICAgPGRpdiBjbGFz + cz0iYm9vc3QtZm9ybV9fbGluay1jb250YWluZXIiPgogICAgICAgIDxhIGNs + YXNzPSJib29zdHMtZm9ybV9fbGluayIgZGF0YS1jb250cm9sbGVyPSJzb2Z0 + LWtleWJvYXJkIiBkYXRhLWFjdGlvbj0ic29mdC1rZXlib2FyZCNvcGVuIiBo + cmVmPSIvMTgxOTAwNDA1L2J1Y2tldHMvMTA0Mjk3OTI0Ny9ib29zdHMvbmV3 + P2Jvb3N0JTVCYm9vc3RhYmxlX2dpZCU1RD1aMmxrT2k4dlltTXpMMUpsWTI5 + eVpHbHVaeTgzT0RNMU1qWXhNREUiPgogICAgICAgICAgPHNwYW4gY2xhc3M9 + ImEtZm9yLXNjcmVlbi1yZWFkZXIiPk5ldyBib29zdDwvc3Bhbj4KPC9hPiAg + ICAgIDwvZGl2Pgo8L3R1cmJvLWZyYW1lPjwvZGl2PgoKICAgICAgPC9kaXY+ + CjwvYXJ0aWNsZT4KICA8c2VjdGlvbiBjbGFzcz0icmVjb3JkYWJsZS1kaXNj + dXNzaW9uIgogIGRhdGEtY29udHJvbGxlcj0idG9nZ2xlLWNsYXNzIgogIGRh + dGEtdG9nZ2xlLWNsYXNzLXRvZ2dsZS1jbGFzcz0iYmFja2xpbmtzX2FjdGl2 + ZSI+CiAgICA8dHVyYm8tZnJhbWUgaWQ9ImJhY2tsaW5rc19yZWNvcmRpbmdf + NzgzNTI2MTAxIiBzcmM9Ii8xODE5MDA0MDUvYnVja2V0cy8xMDQyOTc5MjQ3 + L3JlY29yZGluZ3MvNzgzNTI2MTAxL2JhY2tsaW5rcyI+PC90dXJiby1mcmFt + ZT4KCiAgCjx0dXJiby1mcmFtZSBkYXRhLWNvbnRyb2xsZXI9InRocmVhZCIg + ZGF0YS1hY3Rpb249Imhhc2hjaGFuZ2VAd2luZG93LSZndDt0aHJlYWQjYW5j + aG9yQ2hhbmdlZCIgaWQ9InRocmVhZF9yZWNvcmRpbmdfNzgzNTI2MTAxIiB0 + YXJnZXQ9Il90b3AiPgogIDxzZWN0aW9uIGNsYXNzPSJ0aHJlYWQgdGhyZWFk + LS1jb21tZW50cyI+CiAgICAgIDxkaXYgaWQ9InRocmVhZF9lbnRyaWVzIiBj + bGFzcz0idGhyZWFkX19lbnRyaWVzIiBkYXRhLXVybD0iLzE4MTkwMDQwNS9i + dWNrZXRzLzEwNDI5NzkyNDcvcmVjb3JkaW5ncy83ODM1MjYxMDEvdGhyZWFk + IiBkYXRhLXRocmVhZC1mb3ItcmVjb3JkaW5nPSI3ODM1MjYxMDEiIGRhdGEt + YnJpZGdlLXRocmVhZD0iIiBkYXRhLWJyaWRnZS10aHJlYWQtY3JlYXRvci1u + YW1lPSJEYXZpZCBIZWluZW1laWVyIEhhbnNzb24iIGRhdGEtYnJpZGdlLXRo + cmVhZC1jcmVhdG9yLWF2YXRhci11cmw9Imh0dHA6Ly9iYzMtY2RuLmxvY2Fs + aG9zdDozMDAxLzE4MTkwMDQwNS9wZW9wbGUvQkFocEJPJTJGaVVSOD0tLWEx + NDMwOGVmOGIxMDFhNjYxN2EzNDVlNDRlZmEwZTM1NWVjZDA0MmUvYXZhdGFy + P3Y9MSI+CgoKICAgICAgICAgICAgPGFydGljbGUgZGF0YS1yZWNvcmRpbmct + aWQ9IjQyODk5ODIzNSIgZGF0YS1jcmVhdG9yLWlkPSIyNzA5MTM3ODkiIGRh + dGEtdXJsPSIvMTgxOTAwNDA1L2J1Y2tldHMvMTA0Mjk3OTI0Ny9jb21tZW50 + cy80Mjg5OTgyMzUiIGRhdGEtcmVhZGFibGUtaWRlbnRpZmllcj0iWjJsa09p + OHZZbU16TDFKbFkyOXlaR2x1Wnk4ME1qZzVPVGd5TXpVIiBkYXRhLXBhcmVu + dC1pZD0iNzgzNTI2MTAxIiBkYXRhLXRocmVhZC1lbnRyeS1naWQ9IiZxdW90 + O2dpZDovL2JjMy9SZWNvcmRpbmcvNDI4OTk4MjM1JnF1b3Q7IiBkYXRhLXR5 + cGU9ImNvbW1lbnQiIGNsYXNzPSJ0aHJlYWQtZW50cnkgcmVjb3JkaW5nIiBp + ZD0icmVjb3JkaW5nXzQyODk5ODIzNSI+PHNwYW4gY2xhc3M9ImFuY2hvciBy + ZWNvcmRhYmxlX19hbmNob3IiIGlkPSJfX3JlY29yZGluZ180Mjg5OTgyMzUi + Pjwvc3Bhbj4KICA8aGVhZGVyIGNsYXNzPSJ0aHJlYWQtZW50cnlfX2hlYWRl + ciI+CiAgICAgIDxkaXYgY2xhc3M9InRocmVhZC1lbnRyeV9fdGltZSBtZXRh + ZGF0YSI+CiAgICAgICAgPGEgaHJlZj0iaHR0cDovLzMuYmFzZWNhbXAubG9j + YWxob3N0OjMwMDEvMTgxOTAwNDA1L2J1Y2tldHMvMTA0Mjk3OTI0Ny9tZXNz + YWdlcy83ODM1MjYxMDEjX19yZWNvcmRpbmdfNDI4OTk4MjM1Ij48dGltZSBk + YXRldGltZT0iMjAyNS0wOC0wOFQxNzoyNjo1NloiIGRhdGEtbG9jYWw9Indl + ZWtkYXkiPkF1Z3VzdCAgOCwgMjAyNSAgNToyNnBtPC90aW1lPjwvYT4KICAg + ICAgICA8YSBocmVmPSJodHRwOi8vMy5iYXNlY2FtcC5sb2NhbGhvc3Q6MzAw + MS8xODE5MDA0MDUvYnVja2V0cy8xMDQyOTc5MjQ3L21lc3NhZ2VzLzc4MzUy + NjEwMSNfX3JlY29yZGluZ180Mjg5OTgyMzUiPjx0aW1lIGRhdGV0aW1lPSIy + MDI1LTA4LTA4VDE3OjI2OjU2WiIgZGF0YS1sb2NhbD0idGltZS1vci1kYXRl + Ij5BdWd1c3QgIDgsIDIwMjUgIDU6MjZwbTwvdGltZT48L2E+CiAgICAgIDwv + ZGl2PgoKICAgIDxpbWcgY2xhc3M9ImF2YXRhciB0aHJlYWQtZW50cnlfX2F2 + YXRhciIgZGF0YS1iZWhhdmlvcj0icmljaF9hdmF0YXIiIGRhdGEtcmljaC1h + dmF0YXItdXJsPSIvMTgxOTAwNDA1L3Blb3BsZS8yNzA5MTM3ODkvcmljaF9h + dmF0YXIiIGRhdGEtYXZhdGFyLWZvci1wZXJzb24taWQ9IjI3MDkxMzc4OSIg + YWx0PSJKYXNvbiBGcmllZCIgdGl0bGU9Ikphc29uIEZyaWVkLCBCYXNlY2Ft + cCIgc3JjPSJodHRwOi8vYmMzLWNkbi5sb2NhbGhvc3Q6MzAwMS8xODE5MDA0 + MDUvcGVvcGxlL0JBaHBCUDNRSlJBPS0tNzRhNjg4MzYzMTBlOWM4NmEzOWFk + NDU3NWNmNmFmNzdjMzdlYTQ0NS9hdmF0YXI/dj0xIiB3aWR0aD0iMTMwIiBo + ZWlnaHQ9IjEzMCIgLz4KCiAgICAgIDxzcGFuIGNsYXNzPSJ0aHJlYWQtZW50 + cnlfX2F1dGhvciI+CiAgICAgICAgPHN0cm9uZz5KYXNvbiBGcmllZDwvc3Ry + b25nPgogICAgICAgIAogICAgICA8L3NwYW4+CiAgPC9oZWFkZXI+CgogIDxk + aXYgY2xhc3M9InRocmVhZC1lbnRyeV9fY29udGVudCBmb3JtYXR0ZWRfY29u + dGVudCI+CiAgICBUaGF0J3MgU09PTyBncmVhdCEgQ2hlY2sgaXQgb3V0LCBA + UlMKICA8L2Rpdj4KCiAgICA8ZGl2IGNsYXNzPSJib29zdHMgYm9vc3RzLS1z + bWFsbCBtZXRhZGF0YSBwdXNoLS1ib3R0b20gYm9vc3RzLS10aHJlYWRhYmxl + IgogICAgZGF0YS1jb250cm9sbGVyPSJib29zdHMiCiAgICBkYXRhLWJvb3N0 + cy1oYXMtYm9vc3RzLXNlbGVjdG9yLXZhbHVlPSIuYm9vc3Q6bm90KC5ib29z + dC1mb3JtKSIKICAgIGRhdGEtYm9vc3RzLWFkZGluZy1zZWxlY3Rvci12YWx1 + ZT0iLmJvb3N0LWZvcm0uZXhwYW5kZWQiCiAgICBkYXRhLWJvb3N0cy1lbXB0 + eS1jbGFzcz0iYm9vc3RzLS1lbXB0eSIKICAgIGRhdGEtYm9vc3RzLWFkZGlu + Zy1jbGFzcz0iYm9vc3RzLS1hZGRpbmciCiAgICBkYXRhLWJvb3N0cy1kZWxl + dGluZy1jbGFzcz0iYm9vc3QtLWRlbGV0aW5nIj4KICA8dHVyYm8tZnJhbWUg + Y2xhc3M9InUtZGlzcGxheS1jIiBpZD0iYm9vc3RzX3JlY29yZGluZ180Mjg5 + OTgyMzUiPgo8L3R1cmJvLWZyYW1lPgogIDx0dXJiby1mcmFtZSBjbGFzcz0i + dS1kaXNwbGF5LWMiIGlkPSJuZXdfYm9vc3RfcmVjb3JkaW5nXzQyODk5ODIz + NSI+CiAgICAgIDxkaXYgY2xhc3M9ImJvb3N0LWZvcm1fX2xpbmstY29udGFp + bmVyIj4KICAgICAgICA8YSBjbGFzcz0iYm9vc3RzLWZvcm1fX2xpbmsiIGRh + dGEtY29udHJvbGxlcj0ic29mdC1rZXlib2FyZCIgZGF0YS1hY3Rpb249InNv + ZnQta2V5Ym9hcmQjb3BlbiIgaHJlZj0iLzE4MTkwMDQwNS9idWNrZXRzLzEw + NDI5NzkyNDcvYm9vc3RzL25ldz9ib29zdCU1QmJvb3N0YWJsZV9naWQlNUQ9 + WjJsa09pOHZZbU16TDFKbFkyOXlaR2x1Wnk4ME1qZzVPVGd5TXpVIj4KICAg + ICAgICAgIDxzcGFuIGNsYXNzPSJhLWZvci1zY3JlZW4tcmVhZGVyIj5OZXcg + Ym9vc3Q8L3NwYW4+CjwvYT4gICAgICA8L2Rpdj4KPC90dXJiby1mcmFtZT48 + L2Rpdj4KCgoKICA8c3BhbiBjbGFzcz0idGhyZWFkLWVudHJ5X19vcHRpb25z + Ij4KICAgIDxzcGFuIGNsYXNzPSJhY3Rpb24tc2hlZXQgYWN0aW9uLXNoZWV0 + LS1mb3ItdGhyZWFkLWVudHJ5IHRocmVhZC1lbnRyeV9fbWVudSB1LXBvc2l0 + aW9uLWNvbnRleHQiIGRhdGEtYmVoYXZpb3I9ImV4cGFuZGFibGUiPgoKICAg + ICAgPGJ1dHRvbiBuYW1lPSJidXR0b24iIHR5cGU9ImJ1dHRvbiIgdGl0bGU9 + IlNob3cgb3B0aW9uc+KApiIgYXJpYS1sYWJlbD0iT3B0aW9ucyBtZW51IiBj + bGFzcz0iYWN0aW9uLXNoZWV0X19leHBhbnNpb24tdG9nZ2xlIGJ0biBidG4t + LWljb24gYnRuLS1vdmVyZmxvdy1pY29uIGJ0bi0tc21hbGwgYnRuLS1ib3Jk + ZXJsZXNzIGJ0bi0tb3ZlcmZsb3ctaWNvbi1saWdodCIgZGF0YS1iZWhhdmlv + cj0idG9nZ2xlX2V4cGFuc2lvbl9vbl9jbGljayB0b2dnbGVfZmlsZV9tZW51 + Ij5GaWxl4oCmPC9idXR0b24+CgogICAgICA8c3BhbiBjbGFzcz0iYWN0aW9u + LXNoZWV0X19jb250ZW50IiBkYXRhLWJlaGF2aW9yPSJjb2xsYXBzZV9vbl9j + bGlja291dHNpZGUiPgogICAgICAgIDxidXR0b24gY2xhc3M9ImFjdGlvbi1z + aGVldF9fY2xvc2UgYnRuIGJ0bi0tc21hbGwgYnRuLS1pY29uIGJ0bi0tY2xv + c2Utc2hlZXQtaWNvbiIgYXJpYS1sYWJlbD0iQ2xvc2UgbWVudSIgdHlwZT0i + YnV0dG9uIiBkYXRhLWJlaGF2aW9yPSJjb2xsYXBzZV9vbl9jbGljayI+PC9i + dXR0b24+CgogICAgICAgIDx0dXJiby1mcmFtZSBsb2FkaW5nPSJsYXp5IiBj + bGFzcz0iYWN0aW9uLXNoZWV0X19sYXp5LW9wdGlvbnMiIGlkPSJvcHRpb25z + X3JlY29yZGluZ180Mjg5OTgyMzUiIHNyYz0iLzE4MTkwMDQwNS9idWNrZXRz + LzEwNDI5NzkyNDcvcmVjb3JkaW5ncy83ODM1MjYxMDEvY29tbWVudHMvNDI4 + OTk4MjM1L29wdGlvbnMiPgogICAgICAgICAgPGRpdiBjbGFzcz0ic3Bpbm5l + ciBzcGlubmVyLS1sYXp5LXR1cmJvIj4KICA8ZGl2IGNsYXNzPSJzcGlubmVy + X19kb3Qgc3Bpbm5lcl9fZG90LS0xIj48L2Rpdj4KICA8ZGl2IGNsYXNzPSJz + cGlubmVyX19kb3Qgc3Bpbm5lcl9fZG90LS0yIj48L2Rpdj4KICA8ZGl2IGNs + YXNzPSJzcGlubmVyX19kb3Qgc3Bpbm5lcl9fZG90LS0zIj48L2Rpdj4KPC9k + aXY+CjwvdHVyYm8tZnJhbWU+ICAgICAgPC9zcGFuPgogICAgPC9zcGFuPgog + IDwvc3Bhbj4KPC9hcnRpY2xlPgogICAgPGFydGljbGUgZGF0YS1yZWNvcmRp + bmctaWQ9IjgxNjQ5MjA2NCIgZGF0YS1jcmVhdG9yLWlkPSIzMjMwNjY1ODEi + IGRhdGEtdXJsPSIvMTgxOTAwNDA1L2J1Y2tldHMvMTA0Mjk3OTI0Ny9jb21t + ZW50cy84MTY0OTIwNjQiIGRhdGEtcmVhZGFibGUtaWRlbnRpZmllcj0iWjJs + a09pOHZZbU16TDFKbFkyOXlaR2x1Wnk4NE1UWTBPVEl3TmpRIiBkYXRhLXBh + cmVudC1pZD0iNzgzNTI2MTAxIiBkYXRhLXRocmVhZC1lbnRyeS1naWQ9IiZx + dW90O2dpZDovL2JjMy9SZWNvcmRpbmcvODE2NDkyMDY0JnF1b3Q7IiBkYXRh + LXR5cGU9ImNvbW1lbnQiIGNsYXNzPSJ0aHJlYWQtZW50cnkgcmVjb3JkaW5n + IiBpZD0icmVjb3JkaW5nXzgxNjQ5MjA2NCI+PHNwYW4gY2xhc3M9ImFuY2hv + ciByZWNvcmRhYmxlX19hbmNob3IiIGlkPSJfX3JlY29yZGluZ184MTY0OTIw + NjQiPjwvc3Bhbj4KICA8aGVhZGVyIGNsYXNzPSJ0aHJlYWQtZW50cnlfX2hl + YWRlciI+CiAgICAgIDxkaXYgY2xhc3M9InRocmVhZC1lbnRyeV9fdGltZSBt + ZXRhZGF0YSI+CiAgICAgICAgPGEgaHJlZj0iaHR0cDovLzMuYmFzZWNhbXAu + bG9jYWxob3N0OjMwMDEvMTgxOTAwNDA1L2J1Y2tldHMvMTA0Mjk3OTI0Ny9t + ZXNzYWdlcy83ODM1MjYxMDEjX19yZWNvcmRpbmdfODE2NDkyMDY0Ij48dGlt + ZSBkYXRldGltZT0iMjAyNS0wOC0wOFQxNzoyNjo1NloiIGRhdGEtbG9jYWw9 + IndlZWtkYXkiPkF1Z3VzdCAgOCwgMjAyNSAgNToyNnBtPC90aW1lPjwvYT4K + ICAgICAgICA8YSBocmVmPSJodHRwOi8vMy5iYXNlY2FtcC5sb2NhbGhvc3Q6 + MzAwMS8xODE5MDA0MDUvYnVja2V0cy8xMDQyOTc5MjQ3L21lc3NhZ2VzLzc4 + MzUyNjEwMSNfX3JlY29yZGluZ184MTY0OTIwNjQiPjx0aW1lIGRhdGV0aW1l + PSIyMDI1LTA4LTA4VDE3OjI2OjU2WiIgZGF0YS1sb2NhbD0idGltZS1vci1k + YXRlIj5BdWd1c3QgIDgsIDIwMjUgIDU6MjZwbTwvdGltZT48L2E+CiAgICAg + IDwvZGl2PgoKICAgIDxpbWcgY2xhc3M9ImF2YXRhciB0aHJlYWQtZW50cnlf + X2F2YXRhciIgZGF0YS1iZWhhdmlvcj0icmljaF9hdmF0YXIiIGRhdGEtcmlj + aC1hdmF0YXItdXJsPSIvMTgxOTAwNDA1L3Blb3BsZS8zMjMwNjY1ODEvcmlj + aF9hdmF0YXIiIGRhdGEtYXZhdGFyLWZvci1wZXJzb24taWQ9IjMyMzA2NjU4 + MSIgYWx0PSJKZXJlbXkgS2VtcGVyIiB0aXRsZT0iSmVyZW15IEtlbXBlciwg + QmFzZWNhbXAiIHNyYz0iaHR0cDovL2JjMy1jZG4ubG9jYWxob3N0OjMwMDEv + MTgxOTAwNDA1L3Blb3BsZS9CQWhwQk5XYVFSTT0tLTVmNjgyMDYwNmIzZDJi + ODg0OGRjZDliZjdmMTkwMWIyMWJiNmY0M2MvYXZhdGFyP3Y9MSIgd2lkdGg9 + IjEzMCIgaGVpZ2h0PSIxMzAiIC8+CgogICAgICA8c3BhbiBjbGFzcz0idGhy + ZWFkLWVudHJ5X19hdXRob3IiPgogICAgICAgIDxzdHJvbmc+SmVyZW15IEtl + bXBlcjwvc3Ryb25nPgogICAgICAgIAogICAgICA8L3NwYW4+CiAgPC9oZWFk + ZXI+CgogIDxkaXYgY2xhc3M9InRocmVhZC1lbnRyeV9fY29udGVudCBmb3Jt + YXR0ZWRfY29udGVudCI+CiAgICBOaWNlIQogIDwvZGl2PgoKICAgIDxkaXYg + Y2xhc3M9ImJvb3N0cyBib29zdHMtLXNtYWxsIG1ldGFkYXRhIHB1c2gtLWJv + dHRvbSBib29zdHMtLXRocmVhZGFibGUiCiAgICBkYXRhLWNvbnRyb2xsZXI9 + ImJvb3N0cyIKICAgIGRhdGEtYm9vc3RzLWhhcy1ib29zdHMtc2VsZWN0b3It + dmFsdWU9Ii5ib29zdDpub3QoLmJvb3N0LWZvcm0pIgogICAgZGF0YS1ib29z + dHMtYWRkaW5nLXNlbGVjdG9yLXZhbHVlPSIuYm9vc3QtZm9ybS5leHBhbmRl + ZCIKICAgIGRhdGEtYm9vc3RzLWVtcHR5LWNsYXNzPSJib29zdHMtLWVtcHR5 + IgogICAgZGF0YS1ib29zdHMtYWRkaW5nLWNsYXNzPSJib29zdHMtLWFkZGlu + ZyIKICAgIGRhdGEtYm9vc3RzLWRlbGV0aW5nLWNsYXNzPSJib29zdC0tZGVs + ZXRpbmciPgogIDx0dXJiby1mcmFtZSBjbGFzcz0idS1kaXNwbGF5LWMiIGlk + PSJib29zdHNfcmVjb3JkaW5nXzgxNjQ5MjA2NCI+CjwvdHVyYm8tZnJhbWU+ + CiAgPHR1cmJvLWZyYW1lIGNsYXNzPSJ1LWRpc3BsYXktYyIgaWQ9Im5ld19i + b29zdF9yZWNvcmRpbmdfODE2NDkyMDY0Ij4KICAgICAgPGRpdiBjbGFzcz0i + Ym9vc3QtZm9ybV9fbGluay1jb250YWluZXIiPgogICAgICAgIDxhIGNsYXNz + PSJib29zdHMtZm9ybV9fbGluayIgZGF0YS1jb250cm9sbGVyPSJzb2Z0LWtl + eWJvYXJkIiBkYXRhLWFjdGlvbj0ic29mdC1rZXlib2FyZCNvcGVuIiBocmVm + PSIvMTgxOTAwNDA1L2J1Y2tldHMvMTA0Mjk3OTI0Ny9ib29zdHMvbmV3P2Jv + b3N0JTVCYm9vc3RhYmxlX2dpZCU1RD1aMmxrT2k4dlltTXpMMUpsWTI5eVpH + bHVaeTg0TVRZME9USXdOalEiPgogICAgICAgICAgPHNwYW4gY2xhc3M9ImEt + Zm9yLXNjcmVlbi1yZWFkZXIiPk5ldyBib29zdDwvc3Bhbj4KPC9hPiAgICAg + IDwvZGl2Pgo8L3R1cmJvLWZyYW1lPjwvZGl2PgoKCgogIDxzcGFuIGNsYXNz + PSJ0aHJlYWQtZW50cnlfX29wdGlvbnMiPgogICAgPHNwYW4gY2xhc3M9ImFj + dGlvbi1zaGVldCBhY3Rpb24tc2hlZXQtLWZvci10aHJlYWQtZW50cnkgdGhy + ZWFkLWVudHJ5X19tZW51IHUtcG9zaXRpb24tY29udGV4dCIgZGF0YS1iZWhh + dmlvcj0iZXhwYW5kYWJsZSI+CgogICAgICA8YnV0dG9uIG5hbWU9ImJ1dHRv + biIgdHlwZT0iYnV0dG9uIiB0aXRsZT0iU2hvdyBvcHRpb25z4oCmIiBhcmlh + LWxhYmVsPSJPcHRpb25zIG1lbnUiIGNsYXNzPSJhY3Rpb24tc2hlZXRfX2V4 + cGFuc2lvbi10b2dnbGUgYnRuIGJ0bi0taWNvbiBidG4tLW92ZXJmbG93LWlj + b24gYnRuLS1zbWFsbCBidG4tLWJvcmRlcmxlc3MgYnRuLS1vdmVyZmxvdy1p + Y29uLWxpZ2h0IiBkYXRhLWJlaGF2aW9yPSJ0b2dnbGVfZXhwYW5zaW9uX29u + X2NsaWNrIHRvZ2dsZV9maWxlX21lbnUiPkZpbGXigKY8L2J1dHRvbj4KCiAg + ICAgIDxzcGFuIGNsYXNzPSJhY3Rpb24tc2hlZXRfX2NvbnRlbnQiIGRhdGEt + YmVoYXZpb3I9ImNvbGxhcHNlX29uX2NsaWNrb3V0c2lkZSI+CiAgICAgICAg + PGJ1dHRvbiBjbGFzcz0iYWN0aW9uLXNoZWV0X19jbG9zZSBidG4gYnRuLS1z + bWFsbCBidG4tLWljb24gYnRuLS1jbG9zZS1zaGVldC1pY29uIiBhcmlhLWxh + YmVsPSJDbG9zZSBtZW51IiB0eXBlPSJidXR0b24iIGRhdGEtYmVoYXZpb3I9 + ImNvbGxhcHNlX29uX2NsaWNrIj48L2J1dHRvbj4KCiAgICAgICAgPHR1cmJv + LWZyYW1lIGxvYWRpbmc9ImxhenkiIGNsYXNzPSJhY3Rpb24tc2hlZXRfX2xh + enktb3B0aW9ucyIgaWQ9Im9wdGlvbnNfcmVjb3JkaW5nXzgxNjQ5MjA2NCIg + c3JjPSIvMTgxOTAwNDA1L2J1Y2tldHMvMTA0Mjk3OTI0Ny9yZWNvcmRpbmdz + Lzc4MzUyNjEwMS9jb21tZW50cy84MTY0OTIwNjQvb3B0aW9ucyI+CiAgICAg + ICAgICA8ZGl2IGNsYXNzPSJzcGlubmVyIHNwaW5uZXItLWxhenktdHVyYm8i + PgogIDxkaXYgY2xhc3M9InNwaW5uZXJfX2RvdCBzcGlubmVyX19kb3QtLTEi + PjwvZGl2PgogIDxkaXYgY2xhc3M9InNwaW5uZXJfX2RvdCBzcGlubmVyX19k + b3QtLTIiPjwvZGl2PgogIDxkaXYgY2xhc3M9InNwaW5uZXJfX2RvdCBzcGlu + bmVyX19kb3QtLTMiPjwvZGl2Pgo8L2Rpdj4KPC90dXJiby1mcmFtZT4gICAg + ICA8L3NwYW4+CiAgICA8L3NwYW4+CiAgPC9zcGFuPgo8L2FydGljbGU+Cgo8 + L2Rpdj4KICAgICAgICA8YXJ0aWNsZSBjbGFzcz0idGhyZWFkLWVudHJ5IHRo + cmVhZC1lbnRyeS0tZm9ybSBwdXNoLS1ib3R0b20gcmVjb3JkaW5nIiBpZD0i + bmV3X2NvbW1lbnRfZm9yX3JlY29yZGluZ183ODM1MjYxMDEiIGRhdGEtcmVj + b3JkaW5nLWlkPSI3ODM1MjYxMDEiIGRhdGEtY3JlYXRvci1pZD0iNTI1NDYw + MjA3IiBkYXRhLXVybD0iLzE4MTkwMDQwNS9idWNrZXRzLzEwNDI5NzkyNDcv + bWVzc2FnZXMvNzgzNTI2MTAxIiBkYXRhLXJlYWRhYmxlLWlkZW50aWZpZXI9 + IloybGtPaTh2WW1NekwxSmxZMjl5WkdsdVp5ODNPRE0xTWpZeE1ERSIgZGF0 + YS1iZWhhdmlvcj0iZXhwYW5kYWJsZSI+PHNwYW4gY2xhc3M9ImFuY2hvciBy + ZWNvcmRhYmxlX19hbmNob3IiIGlkPSJfX3JlY29yZGluZ183ODM1MjYxMDEi + Pjwvc3Bhbj4KICAgIDxpbWcgY2xhc3M9ImF2YXRhciB0aHJlYWQtZW50cnlf + X2F2YXRhciIgZGF0YS1iZWhhdmlvcj0icmljaF9hdmF0YXIiIGRhdGEtcmlj + aC1hdmF0YXItdXJsPSIvMTgxOTAwNDA1L3Blb3BsZS81MjU0NjAyMDcvcmlj + aF9hdmF0YXIiIGRhdGEtY3VycmVudC1wZXJzb24tYXZhdGFyPSJ0cnVlIiBh + bHQ9Ik15IGF2YXRhciIgc3JjPSJodHRwOi8vMy5iYXNlY2FtcC5sb2NhbGhv + c3Q6MzAwMS8xODE5MDA0MDUvbXkvYXZhdGFyIiB3aWR0aD0iMTMwIiBoZWln + aHQ9IjEzMCIgLz4KCiAgICA8ZGl2IGNsYXNzPSJjb2xsYXBzZWRfY29udGVu + dCBhcHAtbW9iaWxlX19oaWRlIj4KICAgICAgPGJ1dHRvbiBuYW1lPSJidXR0 + b24iIHR5cGU9ImJ1dHRvbiIgY2xhc3M9InByb21wdCBidG4tLWRvdWJsZS1o + ZWlnaHQgYWxpZ24tLWxlZnQiIGRhdGEtYmVoYXZpb3I9ImV4cGFuZF9vbl9j + bGljayI+QWRkIGEgY29tbWVudCBoZXJl4oCmPC9idXR0b24+CiAgICA8L2Rp + dj4KCiAgICA8ZGl2IGNsYXNzPSJleHBhbmRlZF9jb250ZW50IGFwcC1tb2Jp + bGVfX2hpZGUiPgogICAgICA8dHVyYm8tZnJhbWUgaWQ9Im5ld19jb21tZW50 + X2Zvcm1fcmVjb3JkaW5nXzc4MzUyNjEwMSIgc3JjPSIvMTgxOTAwNDA1L2J1 + Y2tldHMvMTA0Mjk3OTI0Ny9yZWNvcmRpbmdzLzc4MzUyNjEwMS9jb21tZW50 + cy9uZXciIHRhcmdldD0iX3RvcCI+PC90dXJiby1mcmFtZT4KICAgIDwvZGl2 + Pgo8L2FydGljbGU+CgogICAgPHNlY3Rpb24gY2xhc3M9InRocmVhZF9fc3Vi + c2NyaXB0aW9ucyI+CiAgPGFydGljbGUgY2xhc3M9InRocmVhZC1lbnRyeSB0 + aHJlYWQtZW50cnlfX3N1YnNjcmlwdGlvbnMgdS1oaWRlLW9uLXRlbXBsYXRl + Ij4KICAgIDxkaXYgY2xhc3M9InRocmVhZF9fc3Vic2NyaWJlcnMiPgogICAg + ICA8c3BhbiBjbGFzcz0iYW5jaG9yIiBpZD0iX19zdWJzY3JpcHRpb25zIj48 + L3NwYW4+CgogICAgICA8aDQgY2xhc3M9ImZsdXNoIj5TdWJzY3JpYmVyczwv + aDQ+CgogICAgICA8cCBjbGFzcz0idGhyZWFkX19zdWJzY3JpcHRpb24tZXhw + bGFuYXRpb24gZmx1c2gtLXRvcCBwdXNoX2hhbGYtLWJvdHRvbSI+CgogICAg + ICAgIDxzcGFuIGRhdGEtcm9sZT0ic3Vic2NyaWJlcnMtY291bnQiPgogICAg + ICAgICAgICAyIHBlb3BsZQogICAgICAgIDwvc3Bhbj4KCiAgICAgICAgd2ls + bCBiZSBub3RpZmllZCB3aGVuIHNvbWVvbmUgY29tbWVudHMgb24gdGhpcyBt + ZXNzYWdlLgogICAgICA8L3A+CgogICAgICA8ZGl2IGNsYXNzPSJhdmF0YXIt + Z3JvdXAgYXZhdGFyLWdyb3VwLS1zbWFsbCIgZGF0YS1yb2xlPSJzdWJzY3Jp + cHRpb24tYXZhdGFycyI+CiAgICAgICAgPGltZyBkYXRhLWJlaGF2aW9yPSJy + aWNoX2F2YXRhciIgZGF0YS1yaWNoLWF2YXRhci11cmw9Ii8xODE5MDA0MDUv + cGVvcGxlLzUyNTQ2MDIwNy9yaWNoX2F2YXRhciIgZGF0YS1hdmF0YXItZm9y + LXBlcnNvbi1pZD0iNTI1NDYwMjA3IiBhbHQ9IkRhdmlkIEhlaW5lbWVpZXIg + SGFuc3NvbiIgdGl0bGU9IkRhdmlkIEhlaW5lbWVpZXIgSGFuc3NvbiwgQmFz + ZWNhbXAiIGNsYXNzPSJhdmF0YXIiIHNyYz0iaHR0cDovL2JjMy1jZG4ubG9j + YWxob3N0OjMwMDEvMTgxOTAwNDA1L3Blb3BsZS9CQWhwQk8lMkZpVVI4PS0t + YTE0MzA4ZWY4YjEwMWE2NjE3YTM0NWU0NGVmYTBlMzU1ZWNkMDQyZS9hdmF0 + YXI/dj0xIiB3aWR0aD0iMTMwIiBoZWlnaHQ9IjEzMCIgLz4gPGltZyBkYXRh + LWJlaGF2aW9yPSJyaWNoX2F2YXRhciIgZGF0YS1yaWNoLWF2YXRhci11cmw9 + Ii8xODE5MDA0MDUvcGVvcGxlLzI3MDkxMzc4OS9yaWNoX2F2YXRhciIgZGF0 + YS1hdmF0YXItZm9yLXBlcnNvbi1pZD0iMjcwOTEzNzg5IiBhbHQ9Ikphc29u + IEZyaWVkIiB0aXRsZT0iSmFzb24gRnJpZWQsIEJhc2VjYW1wIiBjbGFzcz0i + YXZhdGFyIiBzcmM9Imh0dHA6Ly9iYzMtY2RuLmxvY2FsaG9zdDozMDAxLzE4 + MTkwMDQwNS9wZW9wbGUvQkFocEJQM1FKUkE9LS03NGE2ODgzNjMxMGU5Yzg2 + YTM5YWQ0NTc1Y2Y2YWY3N2MzN2VhNDQ1L2F2YXRhcj92PTEiIHdpZHRoPSIx + MzAiIGhlaWdodD0iMTMwIiAvPgoKICAgICAgICA8YSBjbGFzcz0iYnRuIGJ0 + bi0tc21hbGwgc2VsZi1zdGFydCIgaHJlZj0iLzE4MTkwMDQwNS9idWNrZXRz + LzEwNDI5NzkyNDcvcmVjb3JkaW5ncy83ODM1MjYxMDEvc3Vic2NyaXB0aW9u + L2VkaXQiPkFkZC9yZW1vdmUgcGVvcGxl4oCmPC9hPgogICAgICA8L2Rpdj4K + ICAgIDwvZGl2PgoKICAgIDxkaXYgY2xhc3M9InRocmVhZF9fc2VsZi1zdWJz + Y3JpcHRpb24iIGRhdGEtcm9sZT0idGhyZWFkLXNlbGYtc3Vic2NyaXB0aW9u + Ij4KICA8ZGl2IGNsYXNzPSJ0aHJlYWRfX3NlbGYtc3Vic2NyaWJlZCI+CiAg + ICA8aDQgY2xhc3M9ImZsdXNoLS1ib3R0b20iPllvdeKAmXJlIHN1YnNjcmli + ZWQ8L2g0PgogICAgPHAgY2xhc3M9InRocmVhZF9fc3Vic2NyaXB0aW9uLWV4 + cGxhbmF0aW9uIGZsdXNoLS10b3AgcHVzaF9oYWxmLS1ib3R0b20iPgogICAg + ICAgIFlvdeKAmWxsIGdldCBhIG5vdGlmaWNhdGlvbiB3aGVuIHNvbWVvbmUg + Y29tbWVudHMgb24gdGhpcyBtZXNzYWdlLgogICAgICA8L3A+CiAgPC9kaXY+ + CgogIDxkaXYgY2xhc3M9InRocmVhZF9fc2VsZi11bnN1YnNjcmliZWQiPgog + ICAgPGg0IGNsYXNzPSJmbHVzaC0tYm90dG9tIj5Zb3XigJlyZSBub3Qgc3Vi + c2NyaWJlZDwvaDQ+CiAgICA8cCBjbGFzcz0idGhyZWFkX19zdWJzY3JpcHRp + b24tZXhwbGFuYXRpb24gZmx1c2gtLXRvcCBwdXNoX2hhbGYtLWJvdHRvbSI+ + CiAgICAgICAgWW91IHdvbuKAmXQgYmUgbm90aWZpZWQgd2hlbiBjb21tZW50 + cyBhcmUgcG9zdGVkLgogICAgICA8L3A+CiAgPC9kaXY+CgogIDxidXR0b24g + bmFtZT0iYnV0dG9uIiB0eXBlPSJzdWJtaXQiIGNsYXNzPSJidG4gYnRuLS1z + bWFsbCIgZGF0YS11cmw9Ii8xODE5MDA0MDUvYnVja2V0cy8xMDQyOTc5MjQ3 + L3JlY29yZGluZ3MvNzgzNTI2MTAxL3N1YnNjcmlwdGlvbiIgZGF0YS1iZWhh + dmlvcj0idG9nZ2xlX3N1YnNjcmlwdGlvbl9vbl9yZWNvcmRpbmciPlVuc3Vi + c2NyaWJlIG1lPC9idXR0b24+CjwvZGl2PgoKICA8L2FydGljbGU+Cjwvc2Vj + dGlvbj4KCiAgPC9zZWN0aW9uPgo8L3R1cmJvLWZyYW1lPgo8L3NlY3Rpb24+ + Cgo8L2Rpdj4KICA8L21haW4+CgogIAogIDxkaXYgY2xhc3M9ImJhY2stdG8t + dG9wIGJhY2stdG8tdG9wLS1pbnRlcnNlY3RpbmcgYXBwLW1vYmlsZV9faGlk + ZSB1LWhpZGUtb24tcGhvbmUiIGRhdGEtY29udHJvbGxlcj0idmlld3BvcnQt + ZW50cmFuY2UtdG9nZ2xlIiBkYXRhLXZpZXdwb3J0LWVudHJhbmNlLXRvZ2ds + ZS1jbGFzcz0iYmFjay10by10b3AtLWludGVyc2VjdGluZyI+PGJ1dHRvbiBj + bGFzcz0iYmFjay10by10b3BfX2J1dHRvbiBidG4gYnRuLS1zbWFsbCBidG4t + LXdpdGgtaWNvbiBidG4tLWFycm93LXRvcC1pY29uIiBkYXRhLWFjdGlvbj0i + Y2xpY2stJmd0O3ZpZXdwb3J0LWVudHJhbmNlLXRvZ2dsZSNiYWNrVG9Ub3Ai + PkJhY2sgdG8gdG9wPC9idXR0b24+PC9kaXY+CjwvYm9keT4KPC9odG1sPgo= + recorded_at: Thu, 25 Sep 2025 12:04:38 GMT +recorded_with: VCR 6.3.1 From 02bf13c17148d7cd0e0112b31b5cc3a425f83e74 Mon Sep 17 00:00:00 2001 From: Jason Zimdars Date: Wed, 8 Oct 2025 12:18:42 -0500 Subject: [PATCH 2/9] Copy and style for prompt and popup --- app/assets/stylesheets/rich-text-content.css | 15 +++++++++++++++ app/helpers/rich_text_helper.rb | 10 +++++----- app/views/integrations/basecamps/new.html.erb | 13 ++++++++----- 3 files changed, 28 insertions(+), 10 deletions(-) diff --git a/app/assets/stylesheets/rich-text-content.css b/app/assets/stylesheets/rich-text-content.css index 38c7722e2f..b8602d77b8 100644 --- a/app/assets/stylesheets/rich-text-content.css +++ b/app/assets/stylesheets/rich-text-content.css @@ -393,4 +393,19 @@ line-height: inherit; margin-block: 1em; } + + .unfurl-notice { + background-color: var(--color-selected); + border-radius: 0.5em; + font-size: var(--text-small); + padding: 0.5em 0.5em 0.5em 1em; + + p { + margin: 0; + } + + .btn { + white-space: nowrap; + } + } } diff --git a/app/helpers/rich_text_helper.rb b/app/helpers/rich_text_helper.rb index d5d1193091..aacd5777b3 100644 --- a/app/helpers/rich_text_helper.rb +++ b/app/helpers/rich_text_helper.rb @@ -44,12 +44,12 @@ def lexxy_rich_textarea_tag(name, value = nil, options = {}, &block) private def link_unfurling_prompt - content_tag(:div, hidden: true, class: "flex gap justify-space-between align-center", data: { unfurl_link_target: "linkAccountsPrompt" }) do - concat content_tag(:p, "You can link your Basecamp account to get link previews!") + content_tag(:div, hidden: true, class: "unfurl-notice flex gap justify-space-between align-center", data: { unfurl_link_target: "linkAccountsPrompt" }) do + concat content_tag(:p, "Connect your account to get previews of Basecamp links?") concat( - content_tag(:div, class: "flex gap") do - concat button_tag("Link accounts", class: "btn", data: { action: "unfurl-link#setUpBasecampIntegration" }) - concat button_tag("Remind me later", class: "btn", data: { action: "unfurl-link#closePrompt", unfurl_link_intent_param: "dismiss" }) + content_tag(:div, class: "flex gap-half") do + concat button_tag("Yes, connect…", class: "btn btn--link", data: { action: "unfurl-link#setUpBasecampIntegration" }) + concat button_tag("Not now", class: "btn fill-transparent", data: { action: "unfurl-link#closePrompt", unfurl_link_intent_param: "dismiss" }) end ) end diff --git a/app/views/integrations/basecamps/new.html.erb b/app/views/integrations/basecamps/new.html.erb index 5d916c986f..303af659a8 100644 --- a/app/views/integrations/basecamps/new.html.erb +++ b/app/views/integrations/basecamps/new.html.erb @@ -1,8 +1,11 @@ <% @hide_footer_frames = true %> -
-

We will redirect you to Basecamp to login.

- - <%= button_to "Link your Basecamp Account", basecamp_integration_path, method: :post, data: { turbo: false }, class: "btn" %> - +
+ Connect to Basecamp to auto-preview links? +

Do you want to allow Fizzy to automatically turn pasted Basecamp URLs in link previews? For example, a URL like http://3.basecamp.com/1234/ becomes Important message.

+

In the next step you'll sign in to Basecamp and authorize the connection to Fizzy.

+
+ <%= button_to "Yes, continue…", basecamp_integration_path, method: :post, data: { turbo: false }, class: "btn btn--link" %> + +
From 11f79a2117c04a5acd37ad353d7d6861fc3c4ce6 Mon Sep 17 00:00:00 2001 From: Jason Zimdars Date: Wed, 8 Oct 2025 14:11:21 -0500 Subject: [PATCH 3/9] Copy edit --- app/views/integrations/basecamps/new.html.erb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/views/integrations/basecamps/new.html.erb b/app/views/integrations/basecamps/new.html.erb index 303af659a8..2aa7a8ca89 100644 --- a/app/views/integrations/basecamps/new.html.erb +++ b/app/views/integrations/basecamps/new.html.erb @@ -2,7 +2,7 @@
Connect to Basecamp to auto-preview links? -

Do you want to allow Fizzy to automatically turn pasted Basecamp URLs in link previews? For example, a URL like http://3.basecamp.com/1234/ becomes Important message.

+

If you want links from Basecamp to use the title of the item ("important message") rather than just the URL ("http://basecamp.com/1234"), connect your Basecamp account to Fizzy.

In the next step you'll sign in to Basecamp and authorize the connection to Fizzy.

<%= button_to "Yes, continue…", basecamp_integration_path, method: :post, data: { turbo: false }, class: "btn btn--link" %> From 6660643e092ac7d853b72846b9071a6b99236bfb Mon Sep 17 00:00:00 2001 From: Jason Zimdars Date: Wed, 8 Oct 2025 14:11:49 -0500 Subject: [PATCH 4/9] Copy and style for done and already --- app/views/integrations/basecamps/callbacks/show.html.erb | 8 ++++---- app/views/integrations/basecamps/create.html.erb | 7 ++++--- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/app/views/integrations/basecamps/callbacks/show.html.erb b/app/views/integrations/basecamps/callbacks/show.html.erb index f1fc2629cb..b143db0d60 100644 --- a/app/views/integrations/basecamps/callbacks/show.html.erb +++ b/app/views/integrations/basecamps/callbacks/show.html.erb @@ -1,8 +1,8 @@ <% @hide_footer_frames = true %> -
-

Your accounts are now linked!

-

From now on, any Basecamp link you paste in will get a preview.

+
+ Your accounts are now linked +

From now on, any Basecamp link you paste into Fizzy will automatically be turned into a preview.

- +
diff --git a/app/views/integrations/basecamps/create.html.erb b/app/views/integrations/basecamps/create.html.erb index 615201fb75..643afdcb6c 100644 --- a/app/views/integrations/basecamps/create.html.erb +++ b/app/views/integrations/basecamps/create.html.erb @@ -1,7 +1,8 @@ <% @hide_footer_frames = true %> -
-

You have already setup your Basecamp integration

+
+ Your accounts are already linked +

There's nothing left to do. Any Basecamp link you paste into Fizzy will automatically be turned into a preview

- +
From 8240a68176c111b65a0d890da7ff184457e6db29 Mon Sep 17 00:00:00 2001 From: Jason Zimdars Date: Wed, 8 Oct 2025 14:22:40 -0500 Subject: [PATCH 5/9] Lose the parens --- app/views/integrations/basecamps/new.html.erb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/views/integrations/basecamps/new.html.erb b/app/views/integrations/basecamps/new.html.erb index 2aa7a8ca89..a6005171ef 100644 --- a/app/views/integrations/basecamps/new.html.erb +++ b/app/views/integrations/basecamps/new.html.erb @@ -2,7 +2,7 @@
Connect to Basecamp to auto-preview links? -

If you want links from Basecamp to use the title of the item ("important message") rather than just the URL ("http://basecamp.com/1234"), connect your Basecamp account to Fizzy.

+

If you want links from Basecamp to use the title of the item "important message" rather than just the URL "http://basecamp.com/1234", connect your Basecamp account to Fizzy.

In the next step you'll sign in to Basecamp and authorize the connection to Fizzy.

<%= button_to "Yes, continue…", basecamp_integration_path, method: :post, data: { turbo: false }, class: "btn btn--link" %> From 312201108dbe794723ba011118e214d2cec43abc Mon Sep 17 00:00:00 2001 From: "Stanko K.R." Date: Fri, 10 Oct 2025 10:00:09 +0200 Subject: [PATCH 6/9] Move Basecamp unfurling to Fizzy SAAS The Basecamp unfurler depends on a trussted OAuth app so it isn't usable by people that don't have access to our OAuth server. Therefore I'm moving it to the SAAS part of the app. I also took the opportunity to remove any secrets that caused gitleaks to trigger. These aren't real production secrets but secrets I used locally during development. To avoid HackerOne and other kinds of reports I'm removing them and marking them as clearly being just for development. --- Gemfile | 5 +- .../basecamps/callbacks_controller.rb | 8 - .../integrations/basecamps_controller.rb | 12 - app/models/integration/basecamp.rb | 96 -- app/models/link/basecamp_unfurler.rb | 51 - app/models/user.rb | 6 +- config/routes.rb | 8 - .../basecamps/callbacks_controller_test.rb | 28 - .../integrations/basecamps_controller_test.rb | 39 - test/models/integration/basecamp_test.rb | 99 -- test/models/link/basecamp_unfurler_test.rb | 75 - test/vcr_cassettes/link_test-test_unfurl.yml | 1353 ----------------- 12 files changed, 3 insertions(+), 1777 deletions(-) delete mode 100644 app/controllers/integrations/basecamps/callbacks_controller.rb delete mode 100644 app/controllers/integrations/basecamps_controller.rb delete mode 100644 app/models/integration/basecamp.rb delete mode 100644 app/models/link/basecamp_unfurler.rb delete mode 100644 test/controllers/integrations/basecamps/callbacks_controller_test.rb delete mode 100644 test/controllers/integrations/basecamps_controller_test.rb delete mode 100644 test/models/integration/basecamp_test.rb delete mode 100644 test/models/link/basecamp_unfurler_test.rb delete mode 100644 test/vcr_cassettes/link_test-test_unfurl.yml diff --git a/Gemfile b/Gemfile index 2ebf2b54cd..1fd6f696b0 100644 --- a/Gemfile +++ b/Gemfile @@ -27,7 +27,7 @@ gem "rqrcode" gem "redcarpet" gem "rouge" gem "jbuilder" -gem "lexxy", bc: "lexxy" +gem "lexxy" gem "image_processing", "~> 1.14" gem "platform_agent" gem "aws-sdk-s3", require: false @@ -36,6 +36,7 @@ gem "net-http-persistent" gem "rubyzip", require: "zip" gem "mittens" gem "useragent", bc: "useragent" +gem "oauth2", "~> 2.0" # Operations gem "autotuner" @@ -63,5 +64,3 @@ group :test do gem "vcr" gem "mocha" end - -gem "oauth2", "~> 2.0" diff --git a/app/controllers/integrations/basecamps/callbacks_controller.rb b/app/controllers/integrations/basecamps/callbacks_controller.rb deleted file mode 100644 index 72d73b9819..0000000000 --- a/app/controllers/integrations/basecamps/callbacks_controller.rb +++ /dev/null @@ -1,8 +0,0 @@ -class Integrations::Basecamps::CallbacksController < ApplicationController - skip_before_action :require_tenant - allow_unauthenticated_access - - def show - Integration::Basecamp.set_up_later(code: params[:code], state: params[:state]) - end -end diff --git a/app/controllers/integrations/basecamps_controller.rb b/app/controllers/integrations/basecamps_controller.rb deleted file mode 100644 index 2889ceca4b..0000000000 --- a/app/controllers/integrations/basecamps_controller.rb +++ /dev/null @@ -1,12 +0,0 @@ -class Integrations::BasecampsController < ApplicationController - def new - end - - def create - integration = Integration::Basecamp.find_or_create_by(owner: Current.user) - - unless integration.setup? - redirect_to integration.authorization_url, allow_other_host: true - end - end -end diff --git a/app/models/integration/basecamp.rb b/app/models/integration/basecamp.rb deleted file mode 100644 index 3efa4e52b6..0000000000 --- a/app/models/integration/basecamp.rb +++ /dev/null @@ -1,96 +0,0 @@ -class Integration::Basecamp < Integration - AUTH_URL_STATE_EXPIRATION = 30.minutes - AUTH_URL_STATE_PURPOSE = "setup-basecamp-integration".freeze - - store_accessor :data, :access_token, :refresh_token - - class << self - def set_up_later(code:, state:) - Integration::Basecamp::SetUpJob.perform_later(code: code, state: state) - end - - def set_up(code:, state:) - with_tenant_from_state(state) do - integration = locate_by_state(state) - integration.set_up(code) unless integration.setup? - end - end - - private - def locate_by_state(state) - GlobalID::Locator.locate_signed(state, for: AUTH_URL_STATE_PURPOSE) - end - - def with_tenant_from_state(state, &block) - sgid = SignedGlobalID.parse(state, for: AUTH_URL_STATE_PURPOSE) - with_tenant(sgid.tenant, &block) - end - end - - def authorization_url - oauth_client - .auth_code - .authorize_url( - redirect_uri: return_url, - state: as_state - ) - .sub("response_type=code", "type=web_server") - end - - def set_up(authorization_code) - access_token = oauth_client.auth_code.get_token(authorization_code, token_method: :post, redirect_uri: return_url, type: :web_server) - - self.access_token = access_token.token - self.refresh_token = access_token.refresh_token - - save! - end - - def setup? - access_token.present? && refresh_token.present? - end - - def refresh_tokens - if refresh_token.present? - response = oauth_client.request( - :post, - "/authorization/token", - body: { - refresh_token: refresh_token, - type: "refresh", - client_id: credentials[:client_id], - client_secret: credentials[:client_secret] - } - ).parsed - - self.access_token = response["access_token"] - - save! - end - end - - private - def oauth_client - @oauth_client ||= OAuth2::Client.new( - credentials[:client_id], - credentials[:client_secret], - site: credentials[:oauth_server_url], - authorize_url: "/authorization/new", - token_url: "/authorization/token", - auth_scheme: :request_body - ) - end - - def credentials - Rails.application.credentials.integrations.basecamp - end - - def return_url - options = Rails.application.config.action_controller.default_url_options.merge(script_name: nil) - Rails.application.routes.url_helpers.basecamp_integration_callback_url(**options) - end - - def as_state - to_sgid(expires_in: AUTH_URL_STATE_EXPIRATION, for: AUTH_URL_STATE_PURPOSE).to_s - end -end diff --git a/app/models/link/basecamp_unfurler.rb b/app/models/link/basecamp_unfurler.rb deleted file mode 100644 index 5fde6d83a2..0000000000 --- a/app/models/link/basecamp_unfurler.rb +++ /dev/null @@ -1,51 +0,0 @@ -class Link::BasecampUnfurler < Link::OpenGraphUnfurler - class MissingIntegrationError < StandardError; end - - def self.unfurls?(uri) - uri.host.ends_with?(".basecamp.com") || uri.host.ends_with?(".basecamp.localhost") - rescue URI::InvalidURIError - false - end - - def unfurl - if integration.present? - fetch_metadata - else - raise MissingIntegrationError - end - end - - private - def fetch_metadata - retrying = false - - begin - fetch = Link::Fetch.new(uri, headers: headers) - - if fetch.http_url? && fetch.html_content? - document = Nokogiri::HTML5(fetch.content) - Link::Metadata.new(**extract_metadata_from_document(document)) - end - rescue Link::Fetch::UnsuccesfulRequestError => e - if retrying - raise - elsif e.response.is_a?(Net::HTTPUnauthorized) - integration.refresh_tokens - retrying = true - retry - end - end - end - - def headers - { "Authorization" => "Bearer #{integration.access_token}" } - end - - def integration - @integration ||= begin - integration = user.integrations.with_basecamp - raise(MissingIntegrationError) if integration.nil? || !integration.setup? - integration - end - end -end diff --git a/app/models/user.rb b/app/models/user.rb index 90929e29a2..4e7bea851c 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -17,11 +17,7 @@ class User < ApplicationRecord has_many :pins, dependent: :destroy has_many :pinned_cards, through: :pins, source: :card has_many :exports, class_name: "Account::Export", dependent: :destroy - has_many :integrations, foreign_key: :owner_id, inverse_of: :owner, dependent: :destroy do - def with_basecamp - find_by(type: "Integration::Basecamp") - end - end + has_many :integrations, foreign_key: :owner_id, inverse_of: :owner, dependent: :destroy scope :with_avatars, -> { preload(:account, :avatar_attachment) } diff --git a/config/routes.rb b/config/routes.rb index 0a654923af..78232d3ba5 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -204,14 +204,6 @@ end end - scope :integrations, module: :integrations do - resource :basecamp, only: %i[ new create destroy ], as: :basecamp_integration do - scope module: :basecamps do - resource :callback, only: :show - end - end - end - direct :published_board do |board, options| route_for :public_board, board.publication.key end diff --git a/test/controllers/integrations/basecamps/callbacks_controller_test.rb b/test/controllers/integrations/basecamps/callbacks_controller_test.rb deleted file mode 100644 index 45a678d244..0000000000 --- a/test/controllers/integrations/basecamps/callbacks_controller_test.rb +++ /dev/null @@ -1,28 +0,0 @@ -require "test_helper" - -class Integrations::Basecamps::CallbacksControllerTest < ActionDispatch::IntegrationTest - setup do - @original_url_options = Rails.application.config.action_controller.default_url_options - Rails.application.config.action_controller.default_url_options = { host: "example.com" } - end - - teardown do - Rails.application.config.action_controller.default_url_options = @original_url_options - end - - test "show enqueues job to set up integration" do - integration = integrations(:kevins_basecamp) - integration.update!(data: {}) - - state = integration.to_sgid( - expires_in: Integration::Basecamp::AUTH_URL_STATE_EXPIRATION, - for: Integration::Basecamp::AUTH_URL_STATE_PURPOSE - ).to_s - - assert_enqueued_with(job: Integration::Basecamp::SetUpJob, args: [ { code: "test_code", state: state } ]) do - get basecamp_integration_callback_path, params: { code: "test_code", state: state } - end - - assert_response :success - end -end diff --git a/test/controllers/integrations/basecamps_controller_test.rb b/test/controllers/integrations/basecamps_controller_test.rb deleted file mode 100644 index 14c13c0154..0000000000 --- a/test/controllers/integrations/basecamps_controller_test.rb +++ /dev/null @@ -1,39 +0,0 @@ -require "test_helper" - -class Integrations::BasecampsControllerTest < ActionDispatch::IntegrationTest - setup do - sign_in_as :kevin - @original_url_options = Rails.application.config.action_controller.default_url_options - Rails.application.config.action_controller.default_url_options = { host: "example.com" } - end - - teardown do - Rails.application.config.action_controller.default_url_options = @original_url_options - end - - test "new" do - get new_basecamp_integration_path - assert_response :success - end - - test "create" do - sign_in_as :kevin - - assert_no_difference "Integration::Basecamp.count" do - post basecamp_integration_path - end - - assert_response :success, "Renders the 'integration already setup' screen" - - users(:kevin).integrations.delete_all - - assert_difference "Integration::Basecamp.count", 1 do - post basecamp_integration_path - end - - integration = Integration::Basecamp.last - assert_equal users(:kevin), integration.owner - assert_response :redirect, "Redirects to launchpad" - assert_match %r{launchpad\.localhost:3011/authorization/new}, response.redirect_url - end -end diff --git a/test/models/integration/basecamp_test.rb b/test/models/integration/basecamp_test.rb deleted file mode 100644 index d3b11daa7c..0000000000 --- a/test/models/integration/basecamp_test.rb +++ /dev/null @@ -1,99 +0,0 @@ -require "test_helper" - -class Integration::BasecampTest < ActiveSupport::TestCase - setup do - @integration = integrations(:kevins_basecamp) - @original_url_options = Rails.application.config.action_controller.default_url_options - Rails.application.config.action_controller.default_url_options = { host: "example.com" } - end - - teardown do - Rails.application.config.action_controller.default_url_options = @original_url_options - end - - test "setup?" do - assert @integration.setup? - - @integration.access_token = nil - @integration.refresh_token = nil - - assert_not @integration.setup? - end - - test "authorization_url" do - url = @integration.authorization_url - - assert_match %r{launchpad\.localhost:3011/authorization/new}, url - assert_match(/client_id=/, url) - assert_match(/redirect_uri=/, url) - assert_match(/state=/, url) - assert_match(/type=web_server/, url) - end - - test "refresh_tokens" do - credentials = Rails.application.credentials.integrations.basecamp - - stub_request(:post, "#{credentials[:oauth_server_url]}/authorization/token") - .to_return( - status: 200, - body: { access_token: "refreshed_access_token" }.to_json, - headers: { "Content-Type" => "application/json" } - ) - - @integration.refresh_tokens - - assert_equal "refreshed_access_token", @integration.access_token - - @integration.update!(data: { access_token: "original_token" }) - @integration.refresh_tokens - assert_equal "original_token", @integration.reload.access_token - end - - test "set_up_later" do - state = @integration.to_sgid( - expires_in: Integration::Basecamp::AUTH_URL_STATE_EXPIRATION, - for: Integration::Basecamp::AUTH_URL_STATE_PURPOSE - ).to_s - - assert_enqueued_with(job: Integration::Basecamp::SetUpJob, args: [ { code: "code", state: state } ]) do - Integration::Basecamp.set_up_later(code: "code", state: state) - end - end - - test "set_up" do - @integration.update!(data: {}) - credentials = Rails.application.credentials.integrations.basecamp - - state = @integration.to_sgid( - expires_in: Integration::Basecamp::AUTH_URL_STATE_EXPIRATION, - for: Integration::Basecamp::AUTH_URL_STATE_PURPOSE - ).to_s - - stub_request(:post, "#{credentials[:oauth_server_url]}/authorization/token") - .to_return( - status: 200, - body: { - access_token: "new_access_token", - refresh_token: "new_refresh_token" - }.to_json, - headers: { "Content-Type" => "application/json" } - ) - - Integration::Basecamp.set_up(code: "code", state: state) - - assert @integration.reload.setup? - assert_equal "new_access_token", @integration.access_token - assert_equal "new_refresh_token", @integration.refresh_token - - state = @integration.to_sgid( - expires_in: Integration::Basecamp::AUTH_URL_STATE_EXPIRATION, - for: Integration::Basecamp::AUTH_URL_STATE_PURPOSE - ).to_s - - original_access_token = @integration.access_token - - Integration::Basecamp.set_up(code: "code", state: state) - - assert_equal original_access_token, @integration.reload.access_token, "Set up is skipped if the integration already is setup" - end -end diff --git a/test/models/link/basecamp_unfurler_test.rb b/test/models/link/basecamp_unfurler_test.rb deleted file mode 100644 index 7490a31f4d..0000000000 --- a/test/models/link/basecamp_unfurler_test.rb +++ /dev/null @@ -1,75 +0,0 @@ -require "test_helper" - -class Link::BasecampUnfurlerTest < ActiveSupport::TestCase - test "unfurls?" do - assert Link::BasecampUnfurler.unfurls?(URI.parse("https://3.basecamp.com/123/projects/456")) - assert Link::BasecampUnfurler.unfurls?(URI.parse("https://classic.basecamp.com/999/todos/123")) - assert Link::BasecampUnfurler.unfurls?(URI.parse("https://3.basecamp.localhost:3001/test")) - - assert_not Link::BasecampUnfurler.unfurls?(URI.parse("https://example.com/page")) - assert_not Link::BasecampUnfurler.unfurls?(URI.parse("https://notbasecamp.com/page")) - end - - test "unfurl" do - url = "https://3.basecamp.com/123/projects/456" - user_with_basecamp_integration = users(:kevin) - integration = user_with_basecamp_integration.integrations.with_basecamp - user_without_basecamp_integration = users(:jz) - - stub_request(:head, url) - .with(headers: { "Authorization" => "Bearer #{integration.access_token}" }) - .to_return(status: 200, headers: { "Content-Type" => "text/html" }) - - stub_request(:get, url) - .with(headers: { "Authorization" => "Bearer #{integration.access_token}" }) - .to_return(status: 200, body: <<~HTML) - - - Basecamp Project - - - HTML - - metadata = Link::BasecampUnfurler.new(URI.parse(url), user: user_with_basecamp_integration).unfurl - - assert_equal "Basecamp Project", metadata.title - - assert_raises(Link::BasecampUnfurler::MissingIntegrationError) do - Link::BasecampUnfurler.new(URI.parse(url), user: user_without_basecamp_integration).unfurl - end - end - - test "unfurl with an expired access token" do - url = "https://3.basecamp.com/123/projects/456" - user = users(:kevin) - integration = user.integrations.with_basecamp - original_token = integration.access_token - - stub_request(:head, url) - .with(headers: { "Authorization" => "Bearer #{original_token}" }) - .to_return(status: 401) - - stub_request(:head, url) - .with(headers: { "Authorization" => "Bearer refreshed_token" }) - .to_return(status: 200, headers: { "Content-Type" => "text/html" }) - - stub_request(:get, url) - .with(headers: { "Authorization" => "Bearer refreshed_token" }) - .to_return(status: 200, body: <<~HTML) - - - Refreshed Access - - - HTML - - Integration::Basecamp.any_instance.expects(:refresh_tokens).once.with do |a| - Integration::Basecamp.any_instance.stubs(:access_token).returns("refreshed_token") - true - end - - metadata = Link::BasecampUnfurler.new(URI.parse(url), user: user).unfurl - - assert_equal "Refreshed Access", metadata.title - end -end diff --git a/test/vcr_cassettes/link_test-test_unfurl.yml b/test/vcr_cassettes/link_test-test_unfurl.yml deleted file mode 100644 index 2fd0a841c8..0000000000 --- a/test/vcr_cassettes/link_test-test_unfurl.yml +++ /dev/null @@ -1,1353 +0,0 @@ ---- -http_interactions: -- request: - method: head - uri: http://3.basecamp.localhost:3001/181900405/buckets/1042979247/messages/783526101 - body: - encoding: US-ASCII - base64_string: '' - headers: - Accept-Encoding: - - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 - Accept: - - text/html,application/xhtml+xml - User-Agent: - - Mozilla/5.0 (compatible; FizzyLinkUnfurler/1.0.0) - Host: - - 3.basecamp.localhost:3001 - Authorization: - - Bearer BAhbB0kiAjoBeyJjbGllbnRfaWQiOiI5ZDJmZjU2NDM4YTg1MTNjMjAwNDQ3NTJkMWYzNjE2ZTA3NmUwYTU3IiwiZXhwaXJlc19hdCI6IjIwMjUtMTAtMDZUMTM6MjM6NDlaIiwidXNlcl9pZHMiOlsyNjIxOTgyNTAsMTI2NTEwMDgzLDk0OTc3OTE2NSw5NTA3NTg3OSw4NjgyMTE0NDEsNzA3NTQwNDE1LDIwOTUwMDc0Miw2NDUzOTU2OTAsODc5OTczNzU2LDk3NjAyNDY4LDkwMDk4Nzc1OSwxMTg0NzE1MDQsMjQ5MTUxMjgzLDQwMDE1OTMwNCw4MDk5MDk3Nl0sInZlcnNpb24iOjEsImFwaV9kZWFkYm9sdCI6ImRjOTUzYmYwMDdmMTQyNGY0YWYyN2FjMzI0NGYyZjYzIn0GOgZFVEl1OglUaW1lDc1kH8CpNxZfCToNbmFub19udW1pAjICOg1uYW5vX2RlbmkGOg1zdWJtaWNybyIHViA6CXpvbmVJIghVVEMGOwBG--56891137f381109fc1c075ca95d871b529040fba - response: - status: - code: 200 - message: OK - headers: - X-Frame-Options: - - SAMEORIGIN - X-Xss-Protection: - - 1; mode=block - X-Content-Type-Options: - - nosniff - X-Permitted-Cross-Domain-Policies: - - none - Referrer-Policy: - - strict-origin-when-cross-origin - X-Robots-Tag: - - none - Etag: - - W/"f8535957f7932dfab75d1b9c73c405a2" - Link: - - "; - rel=preload; as=style; nopush" - Content-Type: - - text/html; charset=utf-8 - Vary: - - Accept - Cache-Control: - - max-age=0, private, must-revalidate - Content-Security-Policy: - - 'font-src ''self'' blob: data: http://bc3-cdn.localhost:3001 fonts.gstatic.com - https://rsms.me https://use.typekit.net; object-src ''none''; script-src ''self'' - http://bc3-cdn.localhost:3001 *.braintreegateway.com *.sentry-cdn.com https://basecamp.com - https://www.dropbox.com/static/api/2/dropins.js https://platform.twitter.com - hcaptcha.com *.hcaptcha.com beacon-v2.helpscout.net ''sha256-tkb4UZeVJlSA6VA48VjvAuLrjlKnMlkZ3aYzHpTVQ+Y='' - https://cdn01.boxcdn.net/js/static/select.js embedr.flickr.com/assets/ widgets.flickr.com/embedr/ - ''sha256-aCvRIQ79zbEtvxwsqDbuavE4Sa35jGPLpcm4Y1yIUA0='' ''sha256-Ez5uQZcKGUzoXDIfdPNg1TIyAIO39xBSe6Xba9lAhR4='' - https://assets.tickspot.com https://secure.tickspot.com ''report-sample'' - ''nonce-da39a3ee5e6b4b0d3255bfef95601890afd80709''; style-src ''self'' ''unsafe-inline'' - http://bc3-cdn.localhost:3001 fonts.googleapis.com https://rsms.me/inter/inter.css - https://assets.tickspot.com ''report-sample''; base-uri ''self''; form-action - ''self'' http://launchpad.localhost:3011 https://basecamp.com/; frame-ancestors - ''self'' *.basecamp.localhost:3001' - Set-Cookie: - - _bc3_session=ORfzly73kTg7AiyusolH7VtRLA84WwlkK0b%2Bm2eMgtqhQgZotWamDLxidAsasZNL7cLR4rDxoukuSofTe4W48wbTf8HWlTvt19d38m1KGqVzQVOcXjWhBMzlEGAozVdxCaS5k3tA%2BMFKmwO1uzIFbh7ewMLWQoAW5Phl9XdRNK1p7t%2B5UJZ2ozmpmM30%2F4g6TKaeO2hsBeS06%2FtEPp%2BcM12Ye0es9LstNcli4NVoqU6ocsrrXCDAHndoCrrHmhuvp2jvB%2B7nu9hg8PGtZfH39nTw5GaBgw32dAQmamri%2FIxOLidKGIUE3Lbt2eUmLs3tApJu1%2F7imxFrmA4pN%2BN0MkXa--OleJmKOsdGD0SKmc--eCEPaKpBigvQC3hiUOUhRA%3D%3D; - path=/181900405; expires=Thu, 09 Oct 2025 12:04:38 GMT; HttpOnly; SameSite=Lax - X-Ratelimit: - - '{"name":"General","period":60,"limit":1000,"remaining":1000,"until":"2025-09-25T12:05:00Z"}' - X-Request-Id: - - 1178fc0e-3ff1-4ab9-9073-7c404a99c422 - X-Runtime: - - '0.172064' - Content-Length: - - '0' - body: - encoding: UTF-8 - base64_string: '' - recorded_at: Thu, 25 Sep 2025 12:04:38 GMT -- request: - method: get - uri: http://3.basecamp.localhost:3001/181900405/buckets/1042979247/messages/783526101 - body: - encoding: US-ASCII - base64_string: '' - headers: - Accept-Encoding: - - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 - Accept: - - text/html,application/xhtml+xml - User-Agent: - - Mozilla/5.0 (compatible; FizzyLinkUnfurler/1.0.0) - Host: - - 3.basecamp.localhost:3001 - Authorization: - - Bearer BAhbB0kiAjoBeyJjbGllbnRfaWQiOiI5ZDJmZjU2NDM4YTg1MTNjMjAwNDQ3NTJkMWYzNjE2ZTA3NmUwYTU3IiwiZXhwaXJlc19hdCI6IjIwMjUtMTAtMDZUMTM6MjM6NDlaIiwidXNlcl9pZHMiOlsyNjIxOTgyNTAsMTI2NTEwMDgzLDk0OTc3OTE2NSw5NTA3NTg3OSw4NjgyMTE0NDEsNzA3NTQwNDE1LDIwOTUwMDc0Miw2NDUzOTU2OTAsODc5OTczNzU2LDk3NjAyNDY4LDkwMDk4Nzc1OSwxMTg0NzE1MDQsMjQ5MTUxMjgzLDQwMDE1OTMwNCw4MDk5MDk3Nl0sInZlcnNpb24iOjEsImFwaV9kZWFkYm9sdCI6ImRjOTUzYmYwMDdmMTQyNGY0YWYyN2FjMzI0NGYyZjYzIn0GOgZFVEl1OglUaW1lDc1kH8CpNxZfCToNbmFub19udW1pAjICOg1uYW5vX2RlbmkGOg1zdWJtaWNybyIHViA6CXpvbmVJIghVVEMGOwBG--56891137f381109fc1c075ca95d871b529040fba - response: - status: - code: 200 - message: OK - headers: - X-Frame-Options: - - SAMEORIGIN - X-Xss-Protection: - - 1; mode=block - X-Content-Type-Options: - - nosniff - X-Permitted-Cross-Domain-Policies: - - none - Referrer-Policy: - - strict-origin-when-cross-origin - X-Robots-Tag: - - none - Etag: - - W/"f8535957f7932dfab75d1b9c73c405a2" - Link: - - "; - rel=preload; as=style; nopush" - Content-Type: - - text/html; charset=utf-8 - Vary: - - Accept - Cache-Control: - - max-age=0, private, must-revalidate - Content-Security-Policy: - - 'font-src ''self'' blob: data: http://bc3-cdn.localhost:3001 fonts.gstatic.com - https://rsms.me https://use.typekit.net; object-src ''none''; script-src ''self'' - http://bc3-cdn.localhost:3001 *.braintreegateway.com *.sentry-cdn.com https://basecamp.com - https://www.dropbox.com/static/api/2/dropins.js https://platform.twitter.com - hcaptcha.com *.hcaptcha.com beacon-v2.helpscout.net ''sha256-tkb4UZeVJlSA6VA48VjvAuLrjlKnMlkZ3aYzHpTVQ+Y='' - https://cdn01.boxcdn.net/js/static/select.js embedr.flickr.com/assets/ widgets.flickr.com/embedr/ - ''sha256-aCvRIQ79zbEtvxwsqDbuavE4Sa35jGPLpcm4Y1yIUA0='' ''sha256-Ez5uQZcKGUzoXDIfdPNg1TIyAIO39xBSe6Xba9lAhR4='' - https://assets.tickspot.com https://secure.tickspot.com ''report-sample'' - ''nonce-da39a3ee5e6b4b0d3255bfef95601890afd80709''; style-src ''self'' ''unsafe-inline'' - http://bc3-cdn.localhost:3001 fonts.googleapis.com https://rsms.me/inter/inter.css - https://assets.tickspot.com ''report-sample''; base-uri ''self''; form-action - ''self'' http://launchpad.localhost:3011 https://basecamp.com/; frame-ancestors - ''self'' *.basecamp.localhost:3001' - Set-Cookie: - - _bc3_session=zq3pFi4eTL4Wk8nZMgEkwchem5PDVPvr50qtCnqA8fW01AWc0ThgA56PYGTdXa8tgtff%2Bd24XyNJVmonmSm742MK2ADDDEAy4Dn%2FSF9kT1qermnQxkImG%2FNNNwd7ihjjHh1AAa7ZpZiQa2M1r8ce%2B8Z94TKOn%2BSOmq75XYLNWXl%2Fq5w%2F6fp%2BbogzKChTWnGKvF%2BnQu3IFDX5Gh5zjRDeB30JRKn%2B98oZoeYc6%2FxzfQnev%2FlrLO56U42R2VxkJyntqkrm2ix9xLZ4BBu8gp2GbdK4xtj861cwyIYMVGF0M7DVOg4CyYEqLfYzGwZJTtK0AeCvoWA8cEWqMNnbTnKKvd%2F7--wwLfQMSQ90gDsUBq--tEWZZB4P6aRu%2Bk94i%2Bzrfg%3D%3D; - path=/181900405; expires=Thu, 09 Oct 2025 12:04:38 GMT; HttpOnly; SameSite=Lax - X-Ratelimit: - - '{"name":"General","period":60,"limit":1000,"remaining":1000,"until":"2025-09-25T12:05:00Z"}' - X-Request-Id: - - a248c7b5-4a4c-467a-bfe6-a86e9f7038d5 - X-Runtime: - - '0.193966' - Content-Length: - - '54089' - body: - encoding: ASCII-8BIT - base64_string: | - CjwhRE9DVFlQRSBodG1sPgo8aHRtbCBsYW5nPSJlbiIKICAgIGNsYXNzPSIi - CiAgICBkYXRhLXRoZW1lPSJkZWZhdWx0IgogICAgZGF0YS1jb250cm9sbGVy - PSJwbGF0Zm9ybSBjb2xvci1zY2hlbWUiCiAgICBkYXRhLWNvbG9yLXNjaGVt - ZT0iIgogICAgZGF0YS1icmlkZ2UtcGxhdGZvcm09IiI+CjxoZWFkPgo8bWV0 - YSBjaGFyc2V0PSJ1dGYtOCI+Cgo8dGl0bGUgZGF0YS1icmlkZ2UtYWx0PSLw - n5OiIEEgZGVjYWRlISI+8J+ToiBBIGRlY2FkZSE8L3RpdGxlPgoKPG1ldGEg - bmFtZT0idmlld3BvcnQiIGNvbnRlbnQ9IndpZHRoPWRldmljZS13aWR0aCwg - aW5pdGlhbC1zY2FsZT0xLCBtYXhpbXVtLXNjYWxlPTEsIHVzZXItc2NhbGFi - bGU9bm8iPgo8bWV0YSBuYW1lPSJyb2JvdHMiIGNvbnRlbnQ9Im5vbmUiPgoK - PG1ldGEgbmFtZT0icmVmZXJyZXIiIGNvbnRlbnQ9Im9yaWdpbi13aGVuLWNy - b3NzLW9yaWdpbiI+Cgo8bWV0YSBuYW1lPSJ0aGVtZS1jb2xvciIgY29udGVu - dD0iaHNsKDIwMi41LCA0Mi4xJSwgNy41JSkiIG1lZGlhPSIocHJlZmVycy1j - b2xvci1zY2hlbWU6IGRhcmspIj4KCjxtZXRhIG5hbWU9ImNzcmYtcGFyYW0i - IGNvbnRlbnQ9ImF1dGhlbnRpY2l0eV90b2tlbiIgLz4KPG1ldGEgbmFtZT0i - Y3NyZi10b2tlbiIgY29udGVudD0iTUdqVzktRUZjckhNeFFRczJySkt2QXRB - NGp2eXR4S0FhY1VacTZuejczbzhaTzZFckd0VUZQNnFtOHNEdy1DcW9zMmNW - Tm5rbXRndzF3Y25HR1dweWciIC8+Cgo8bWV0YSBuYW1lPSJ0dXJiby1yb290 - IiBjb250ZW50PSIvMTgxOTAwNDA1IiAvPgoKPG1ldGEgbmFtZT0idHVyYm8t - Y2FjaGUtY29udHJvbCIgY29udGVudD0iY2FjaGUiIC8+Cgo8bWV0YSBuYW1l - PSJ0dXJiby1wcmVmZXRjaCIgY29udGVudD0idHJ1ZSIgLz4KCjxtZXRhIG5h - bWU9ImNhYmxlLXVybCIgY29udGVudD0id3M6Ly8zLmJhc2VjYW1wLmxvY2Fs - aG9zdDozMDAxLzE4MTkwMDQwNS9jYWJsZSIgLz4KCjxtZXRhIG5hbWU9ImN1 - cnJlbnQtZW52IiBjb250ZW50PSJkZXZlbG9wbWVudCIgLz48bWV0YSBuYW1l - PSJjdXJyZW50LXN0YWdlIiBjb250ZW50PSJkZXZlbG9wbWVudCIgLz48bWV0 - YSBuYW1lPSJjdXJyZW50LXJlbGVhc2UiIGNvbnRlbnQ9IjU3NzY2ZjhjOTRl - MjExNmFmNWU4NjljOTNiNmExODhjYTg2MTNiYjQiIC8+CjxtZXRhIG5hbWU9 - ImN1cnJlbnQtYWNjb3VudC1zbHVnLXBhdGgiIGNvbnRlbnQ9Ii8xODE5MDA0 - MDUiIC8+PG1ldGEgbmFtZT0iY3VycmVudC1hY2NvdW50LW9uLXBlci1zZWF0 - LWJ1c2luZXNzLW1vZGVsIiBjb250ZW50PSJ0cnVlIiAvPjxtZXRhIG5hbWU9 - ImN1cnJlbnQtYWNjb3VudC1vbi1tb2Rlcm4tYnVzaW5lc3MtbW9kZWwiIGNv - bnRlbnQ9InRydWUiIC8+PG1ldGEgbmFtZT0iY3VycmVudC1hY2NvdW50LXBy - by1wYWNrIiBjb250ZW50PSJmYWxzZSIgLz48bWV0YSBuYW1lPSJjdXJyZW50 - LWFjY291bnQtcmVzdHJpY3RzLWFyY2hpdmUtYW5kLXRyYXNoIiBjb250ZW50 - PSJmYWxzZSIgLz4KPG1ldGEgbmFtZT0iY3VycmVudC1wZXJzb24taWQiIGNv - bnRlbnQ9IjUyNTQ2MDIwNyIgLz48bWV0YSBuYW1lPSJjdXJyZW50LXBlcnNv - bi1naWQiIGNvbnRlbnQ9ImdpZDovL2JjMy9QZXJzb24vNTI1NDYwMjA3IiAv - PjxtZXRhIG5hbWU9ImN1cnJlbnQtcGVyc29uLW5hbWUiIGNvbnRlbnQ9IkRh - dmlkIEhlaW5lbWVpZXIgSGFuc3NvbiIgLz48bWV0YSBuYW1lPSJjdXJyZW50 - LXBlcnNvbi1lbWFpbC1hZGRyZXNzIiBjb250ZW50PSJkYXZpZEAzN3NpZ25h - bHMuY29tIiAvPjxtZXRhIG5hbWU9ImN1cnJlbnQtcGVyc29uLWF2YXRhci11 - cmwiIGNvbnRlbnQ9Imh0dHA6Ly8zLmJhc2VjYW1wLmxvY2FsaG9zdDozMDAx - LzE4MTkwMDQwNS9teS9hdmF0YXIiIC8+PG1ldGEgbmFtZT0iY3VycmVudC1w - ZXJzb24tb3duZXIiIGNvbnRlbnQ9InRydWUiIC8+PG1ldGEgbmFtZT0iY3Vy - cmVudC1wZXJzb24tYWRtaW4iIGNvbnRlbnQ9InRydWUiIC8+PG1ldGEgbmFt - ZT0iY3VycmVudC1wZXJzb24tc3RhZmYiIGNvbnRlbnQ9InRydWUiIC8+PG1l - dGEgbmFtZT0iY3VycmVudC1wZXJzb24tY2xpZW50IiBjb250ZW50PSJmYWxz - ZSIgLz48bWV0YSBuYW1lPSJjdXJyZW50LXBlcnNvbi1lbXBsb3llZSIgY29u - dGVudD0idHJ1ZSIgLz48bWV0YSBuYW1lPSJjdXJyZW50LXBlcnNvbi1maXJz - dC13ZWVrLWRheS1pbnQiIGNvbnRlbnQ9IjAiIC8+PG1ldGEgbmFtZT0iY3Vy - cmVudC1wZXJzb24tZmlyc3QtdGltZS1ib29zdGVyIiBjb250ZW50PSJmYWxz - ZSIgLz48bWV0YSBuYW1lPSJjdXJyZW50LXBlcnNvbi10aW1lLWZvcm1hdCIg - Y29udGVudD0idHdlbHZlX2hvdXIiIC8+CjxtZXRhIG5hbWU9ImN1cnJlbnQt - cGFnZS1leGNsdWRlLWZyb20tcmVjZW50LWhpc3RvcnkiIGNvbnRlbnQ9ImZh - bHNlIiAvPjxtZXRhIG5hbWU9ImN1cnJlbnQtcGFnZS10eXBlIiBjb250ZW50 - PSJtZXNzYWdlIiAvPjxtZXRhIG5hbWU9ImN1cnJlbnQtcGFnZS10aXRsZSIg - Y29udGVudD0i8J+ToiBBIGRlY2FkZSEiIC8+PG1ldGEgbmFtZT0iY3VycmVu - dC1wYWdlLXN1YnRpdGxlIiBjb250ZW50PSJUZW4teWVhciBhbm5pdmVyc2Fy - eSIgLz4KCgoKCgo8bWV0YSBuYW1lPSJjdXJyZW50LWFjY291bnQtbmFtZSIg - Y29udGVudD0iQmFzZWNhbXAmIzM5O3MgQmFzZWNhbXAiIC8+PG1ldGEgbmFt - ZT0iY3VycmVudC1idWNrZXQtaWQiIGNvbnRlbnQ9IjEwNDI5NzkyNDciIC8+ - PG1ldGEgbmFtZT0iY3VycmVudC1idWNrZXQtbmFtZSIgY29udGVudD0iVGVu - LXllYXIgYW5uaXZlcnNhcnkiIC8+PG1ldGEgbmFtZT0iY3VycmVudC1idWNr - ZXQtc3RhdHVzIiBjb250ZW50PSJhY3RpdmUiIC8+PG1ldGEgbmFtZT0iY3Vy - cmVudC1idWNrZXQtcGF0aCIgY29udGVudD0iLzE4MTkwMDQwNS9idWNrZXRz - LzEwNDI5NzkyNDciIC8+PG1ldGEgbmFtZT0iY3VycmVudC1idWNrZXQtdHlw - ZSIgY29udGVudD0iUHJvamVjdCIgLz48bWV0YSBuYW1lPSJjdXJyZW50LXJl - Y29yZGluZy1pZCIgY29udGVudD0iNzgzNTI2MTAxIiAvPjxtZXRhIG5hbWU9 - ImN1cnJlbnQtcmVjb3JkaW5nLXR5cGUiIGNvbnRlbnQ9Ik1lc3NhZ2UiIC8+ - PG1ldGEgbmFtZT0iY3VycmVudC1yZWNvcmRpbmctdGl0bGUiIGNvbnRlbnQ9 - IkEgZGVjYWRlISIgLz48bWV0YSBuYW1lPSJjdXJyZW50LXJlY29yZGluZy1p - cy1kb2NrZWQiIGNvbnRlbnQ9ImZhbHNlIiAvPjxtZXRhIG5hbWU9ImN1cnJl - bnQtZG9jay10b29sLXRpdGxlIiBjb250ZW50PSJNZXNzYWdlIEJvYXJkIiAv - PjxtZXRhIG5hbWU9ImN1cnJlbnQtZG9jay10b29sLXVybCIgY29udGVudD0i - aHR0cDovLzMuYmFzZWNhbXAubG9jYWxob3N0OjMwMDEvMTgxOTAwNDA1L2J1 - Y2tldHMvMTA0Mjk3OTI0Ny9tZXNzYWdlX2JvYXJkcy8xMjIyMDE2NjUiIC8+ - CgoKPG1ldGEgbmFtZT0iYXNzZXRzLXZlcnNpb24iIGNvbnRlbnQ9InYxIiAv - PgoKPG1ldGEgbmFtZT0iY3NwLW5vbmNlIiBjb250ZW50PSJkYTM5YTNlZTVl - NmI0YjBkMzI1NWJmZWY5NTYwMTg5MGFmZDgwNzA5IiAvPgoKICA8bWV0YSBu - YW1lPSJhcHBsZS1pdHVuZXMtYXBwIiBjb250ZW50PSJhcHAtaWQ9MTAxNTYw - MzI0OCI+CgoKCjxzY3JpcHQgbm9uY2U9ImRhMzlhM2VlNWU2YjRiMGQzMjU1 - YmZlZjk1NjAxODkwYWZkODA3MDkiPgovLzwhW0NEQVRBWwooZnVuY3Rpb24o - c3R5bGUpIHsKICBzdHlsZS5pbm5lclRleHQgPSAiLmxvYWRpbmdfX2hpZGUg - eyBvcGFjaXR5OiAwIH0iOwogIGRvY3VtZW50LmhlYWQuYXBwZW5kQ2hpbGQo - c3R5bGUpOwogIGFkZEV2ZW50TGlzdGVuZXIoIkRPTUNvbnRlbnRMb2FkZWQi - LCBmdW5jdGlvbigpIHsKICAgIHJlcXVlc3RBbmltYXRpb25GcmFtZShmdW5j - dGlvbigpIHsKICAgICAgZG9jdW1lbnQuaGVhZC5yZW1vdmVDaGlsZChzdHls - ZSkKICAgIH0pCiAgfSkKfSkoZG9jdW1lbnQuY3JlYXRlRWxlbWVudCgic3R5 - bGUiKSkKCi8vXV0+Cjwvc2NyaXB0PgoKPGxpbmsgcmVsPSJkbnMtcHJlZmV0 - Y2giIGhyZWY9Ii8vYmMzLWNkbi5sb2NhbGhvc3Q6MzAwMSIgLz4KCjxsaW5r - IHJlbD0ic3R5bGVzaGVldCIgaHJlZj0iLy9iYzMtY2RuLmxvY2FsaG9zdDoz - MDAxL2Fzc2V0cy9kZXNrdG9wLTMwZjUwMzg1M2M5OTJlYTFhYTQwNjBmMTc3 - OTUyYzAzOTZkNDM0MjI5YWE1OWZhYzhiODQwOWRjM2RjNWE0ZTUuY3NzIiBt - ZWRpYT0iYWxsIiBkYXRhLXR1cmJvLXRyYWNrPSJyZWxvYWQiIC8+CgogIDxz - Y3JpcHQgc3JjPSIvL2JjMy1jZG4ubG9jYWxob3N0OjMwMDEvYXNzZXRzL2J1 - aWxkcy9saWJyYXJpZXMtNGZmZDEzYzFkZTRjZDQzNzY5MDg3NWIxNzVkMjk1 - ODg5OTAzYTgyMDQxNTE4ZjI5Mjc3ZWFlNTU5Mjg5M2Q2Yi5qcyIgZGVmZXI9 - ImRlZmVyIiBkYXRhLXR1cmJvLXRyYWNrPSJyZWxvYWQiPjwvc2NyaXB0Pgog - IDxzY3JpcHQgc3JjPSIvL2JjMy1jZG4ubG9jYWxob3N0OjMwMDEvYXNzZXRz - L2J1aWxkcy9kZXNrdG9wLWFhMjVlYjUwM2QzNDFkMjU0OTJlODA3YWUxMjdj - ZDc2YTIxMDExN2IxMTRjMWY5MTk5NjY0MmIxYWI2ODgxYWIuanMiIGRlZmVy - PSJkZWZlciIgZGF0YS10dXJiby10cmFjaz0icmVsb2FkIj48L3NjcmlwdD4K - ICA8c2NyaXB0IHNyYz0iLy9iYzMtY2RuLmxvY2FsaG9zdDozMDAxL2Fzc2V0 - cy9idWlsZHMvYXBwbGljYXRpb24tYjYwNTdjMzZjNTQ3YWJiZTE0Y2YwNGU5 - MzlmMTU2NGFmMDI2MmYzMzA0OWI0NzU2OGZkN2QzZTcwYTlmNDlkNC5qcyIg - ZGVmZXI9ImRlZmVyIiBkYXRhLXR1cmJvLXRyYWNrPSJyZWxvYWQiPjwvc2Ny - aXB0PgoKPGxpbmsgcmVsPSJzdWJyZXNvdXJjZSIgaHJlZj0iLy9iYzMtY2Ru - LmxvY2FsaG9zdDozMDAxL2Fzc2V0cy9idWlsZHMvcmljaF90ZXh0LTA3ODRj - NzU1M2RkM2I2MDEzNDVhYmJlY2YwY2E2ZGNhYzRjZjQ4MTZhNDJlNjU4NmM0 - NzZkY2UyZTJiZjgyY2YuanMiIGRhdGEtc2NyaXB0PSJidWlsZHMvcmljaF90 - ZXh0IiBkYXRhLXR1cmJvLXRyYWNrPSJyZWxvYWQiIC8+Cgo8bGluayByZWw9 - ImFwcGxlLXRvdWNoLWljb24iIHNpemVzPSIxOTZ4MTk2IiBocmVmPSIvYXBw - bGUtdG91Y2gtaWNvbi5wbmciPgo8bGluayByZWw9InNob3J0Y3V0IGljb24i - IHR5cGU9ImltYWdlL3BuZyIgc2l6ZXM9IjMyeDMyIiBocmVmPSIvZmF2aWNv - bi0zMngzMi5wbmciPgoKPGxpbmsgcmVsPSJtYW5pZmVzdCIgaHJlZj0iLzE4 - MTkwMDQwNS9tYW5pZmVzdC5qc29uIiBjcm9zc29yaWdpbj0idXNlLWNyZWRl - bnRpYWxzIj4KCgoKPC9oZWFkPgoKCjxib2R5IGNsYXNzPSIiICBkYXRhLWNv - bnRyb2xsZXI9IndlYi1ub3RpZmljYXRpb24tcGVybWlzc2lvbiI+CiAgPGEg - aHJlZj0iI2p1bXAtbWVudSIgY2xhc3M9ImEtZm9yLXNjcmVlbi1yZWFkZXIg - bmF2X19hY2Nlc3NpYmlsaXR5LWJ1dHRvbiBidG4gYnRuLS1zbWFsbCB1LWhp - ZGUtb24tcGhvbmUgbG9hZGluZ19faGlkZSIgdGFiaW5kZXg9IjAiIGRhdGEt - dHVyYm89ImZhbHNlIiBkYXRhLWJlaGF2aW9yPSJzaG93X2p1bXBfbWVudV9i - dXR0b24iPgogICAgU2hvdyBKdW1wIE1lbnUgPHNwYW4gY2xhc3M9ImJ0bl9f - a2V5Ym9hcmQtc2hvcnRjdXQiPjxzcGFuIGNsYXNzPSJhLWZvci1zY3JlZW4t - cmVhZGVyIj4sIHNob3J0Y3V0IDwvc3Bhbj48c3BhbiBkYXRhLXJvbGU9Imp1 - bXAtbWVudS1tb2RpZmllci1rZXkiPkN0cmw8L3NwYW4+Sjwvc3Bhbj4KICA8 - L2E+CgogIDxhIGhyZWY9IiNtYWluLWNvbnRlbnQiIGNsYXNzPSJhLWZvci1z - Y3JlZW4tcmVhZGVyIG5hdl9fYWNjZXNzaWJpbGl0eS1idXR0b24gYnRuIGJ0 - bi0tc21hbGwgdS1oaWRlLW9uLXBob25lIGxvYWRpbmdfX2hpZGUiIGRhdGEt - dHVyYm89ImZhbHNlIj4KICAgIFNraXAgdG8gbWFpbiBjb250ZW50CiAgPC9h - PgoKICA8ZGl2IGNsYXNzPSJuYXYiPgogIDxuYXYgaWQ9Im15X25hdmlnYXRp - b24iCiAgICAgIGFyaWEtbGFiZWw9Ik1haW4iCiAgICAgIGNsYXNzPSJuYXZf - X2JhciIKICAgICAgZGF0YS10dXJiby1wZXJtYW5lbnQKICAgICAgZGF0YS1j - b250cm9sbGVyPSJzY3JvbGwtdG9nZ2xlIgogICAgICBkYXRhLWFjdGlvbj0i - c2Nyb2xsQHdpbmRvdy0+c2Nyb2xsLXRvZ2dsZSN0b2dnbGUiCiAgICAgIGRh - dGEtc2Nyb2xsLXRvZ2dsZS1jbGFzcz0ibmF2X19iYXItLXNjcm9sbGVkIj4K - CiAgICA8ZGl2IGNsYXNzPSJuYXYtbWVudSBuYXYtbWVudV9fYWNjb3VudHMi - IGRhdGEtYmVoYXZpb3I9ImV4cGFuZGFibGUgbG9hZF9vbl9leHBhbmQiCiAg - ZGF0YS1tZW51LXNlY3Rpb249ImFjY291bnRzIiBkYXRhLWxvYWQtdXJsPSIv - MTgxOTAwNDA1L215L25hdmlnYXRpb24vYWNjb3VudHMiIGRhdGEtbG9hZC10 - YXJnZXQ9IiNuYXZpZ2F0aW9uX2FjY291bnRzIj4KICA8ZGl2IGNsYXNzPSJj - b2xsYXBzaWJsZV9jb250ZW50IiBkYXRhLWJlaGF2aW9yPSJjb2xsYXBzZV9v - bl9jbGlja291dHNpZGUiPgogICAgPGEgaHJlZj0iLzE4MTkwMDQwNS8iIGNs - YXNzPSJuYXZfX2xpbmstLWFjY291bnRzIiBkYXRhLWJlaGF2aW9yPSJ0b2dn - bGVfZXhwYW5zaW9uX29uX2NsaWNrIgogICAgICBhcmlhLWxhYmVsPSJTd2l0 - Y2ggYWNjb3VudHMiIHJvbGU9ImJ1dHRvbiIgYXJpYS1oYXNwb3B1cD0idHJ1 - ZSI+CiAgICAgIDxzdmcgd2lkdGg9IjEzNSIgaGVpZ2h0PSIzMSIgdmlld0Jv - eD0iMCAwIDEzNSAzMSIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cu - dzMub3JnLzIwMDAvc3ZnIj48cGF0aCBkPSJNMzkuMjg4IDguMTQ0aDUuNjE5 - YzMuMTEyIDAgNC43MjggMS4yNzYgNC43MjggMy42NzN2LjExOWEyLjkzNyAy - LjkzNyAwIDAgMS0yLjMxMyAzLjA1NyAzLjE0IDMuMTQgMCAwIDEgMi44NTUg - My4zN3YuMDgzYzAgMi42MTYtMS43MTcgMy45OTQtNS4wMTMgMy45OTRoLTUu - ODc2VjguMTQ0Wm01LjA5NSA1Ljg2N2MxLjU2MSAwIDIuMTc2LS41NiAyLjE3 - Ni0xLjgzNnYtLjA4M2MwLTEuMTk0LS42NzktMS43MzUtMi4xOTQtMS43MzVo - LTEuOTE5djMuNjcybDEuOTM3LS4wMThabS4yNzYgNi4xOGMxLjU5NyAwIDIu - MzIzLS43MjYgMi4zMjMtMi4wMnYtLjA4M2MwLTEuMzIzLS43MjYtMS45OTMt - Mi40OC0xLjk5M2gtMi4wNTZWMjAuMmwyLjIxMy0uMDA5Wk01MS4wMjIgMTku - NTNjMC0yLjQ0MyAyLjIzLTMuMzYxIDUuNDM1LTMuMzYxaDEuMTc1di0uNDE0 - YzAtMS4yMzktLjM3Ni0xLjkxOS0xLjY5OC0xLjkxOWExLjU4IDEuNTggMCAw - IDAtMS43NjMgMS40NzloLTIuNzU1Yy4xODQtMi40OCAyLjE0LTMuNTgxIDQu - NzAxLTMuNTgxIDIuNTYyIDAgNC4zNyAxLjAzNyA0LjM3IDMuODc1djYuODEy - SDU3LjY3di0xLjI1OGEzLjUzNiAzLjUzNiAwIDAgMS0zLjE1OCAxLjQ3OWMt - MS44OTIgMC0zLjQ5LS45MTgtMy40OS0zLjExM1ptNi42MS0uNzYzdi0uOTE4 - aC0xLjEyYy0xLjY4IDAtMi42NTMuMzY3LTIuNjUzIDEuNDc4IDAgLjc2Mi40 - NTkgMS4yNTggMS41MTUgMS4yNTggMS4yNzYuMDM3IDIuMjU4LS42NjEgMi4y - NTgtMS44MThaTTYxLjcwOSAxOS4xNDNoMi43Yy4xMTkuOTE5LjU3OCAxLjQ3 - OSAxLjgzNiAxLjQ3OSAxLjEyIDAgMS42NDMtLjQxMyAxLjY0My0xLjEzOSAw - LS43MjUtLjYyNC0xLjAxOS0yLjEyLTEuMjMtMi43NTUtLjQyMi0zLjg1Ny0x - LjIyMS0zLjg1Ny0zLjI2IDAtMi4wMzggMi4wMDItMy4yNTkgNC4xMzItMy4y - NTkgMi4zMjIgMCA0LjA1OC44NDUgNC4zMTUgMy4yNDFoLTIuNjU0Yy0uMTU2 - LS44NjMtLjY0Mi0xLjI1OC0xLjYzNC0xLjI1OC0uOTkxIDAtMS40NzguNDQt - MS40NzggMS4wNzQgMCAuNjM0LjQ5Ni45MTkgMi4wMSAxLjEzOSAyLjYxNy4z - NzYgNC4wNTkgMS4wMzcgNC4wNTkgMy4yNzggMCAyLjI0LTEuNjM0IDMuNDM0 - LTQuMzk4IDMuNDM0cy00LjQ1My0xLjI0LTQuNTU0LTMuNDk4Wk03MS40MDUg - MTcuMzI2di0uMTU3YTUuMTg5IDUuMTg5IDAgMCAxIDUuMzctNS40MzVjMi43 - IDAgNS4wOTYgMS41OCA1LjA5NiA1LjMxNnYuNzk5aC03LjVjLjA3MyAxLjcz - NSAxLjAxOCAyLjc1NCAyLjU4OCAyLjc1NCAxLjM0IDAgMi4wMDItLjU3OCAy - LjE4Ni0xLjQ2aDIuNzU0Yy0uMzQgMi4yNi0yLjE0IDMuNTE3LTQuOTk1IDMu - NTE3LTMuMjA0LS4wMTgtNS41LTEuOTkyLTUuNS01LjMzNFptNy42NDgtMS4y - NThjLS4wOTItMS41OC0uOS0yLjI5Ni0yLjI3Ny0yLjI5NmEyLjM5NyAyLjM5 - NyAwIDAgMC0yLjQwNiAyLjI5Nmg0LjY4M1pNODIuNjM0IDE3LjMyNnYtLjE1 - N2E1LjE4OCA1LjE4OCAwIDAgMSA1LjM5LTUuNDM1YzIuNDIzIDAgNC42NTQg - MS4wNTYgNC45MiA0LjA1OEg5MC4xOWExLjkyOSAxLjkyOSAwIDAgMC0yLjEy - MS0xLjc4MWMtMS40OTcgMC0yLjQ5NyAxLjEyLTIuNDk3IDMuMTIydi4xNTZj - MCAyLjEwMi45MTggMy4xNzcgMi41NjEgMy4xNzdhMi4xMTEgMi4xMTEgMCAw - IDAgMi4yMzEtMi4wMmgyLjYxN2MtLjE1NiAyLjQ3OS0xLjk1NiA0LjE5Ni00 - Ljk5NSA0LjE5NnMtNS4zNTItMS45MDEtNS4zNTItNS4zMTZaTTkzLjY1MSAx - OS41M2MwLTIuNDQzIDIuMjMxLTMuMzYxIDUuNDI3LTMuMzYxaDEuMTg0di0u - NDE0YzAtMS4yMzktLjM4Ni0xLjkxOS0xLjY5OC0xLjkxOWExLjU4IDEuNTgg - MCAwIDAtMS43ODIgMS40NzloLTIuNzU0Yy4xODMtMi40OCAyLjE0LTMuNTgx - IDQuNjkyLTMuNTgxczQuMzc5IDEuMDM3IDQuMzc5IDMuODc1djYuODEyaC0y - LjgxOXYtMS4yNThhMy41MyAzLjUzIDAgMCAxLTMuMTU4IDEuNDc5Yy0xLjgz - NiAwLTMuNDctLjkxOC0zLjQ3LTMuMTEzWm02LjYxMS0uNzYzdi0uOTE4SDk5 - LjE2Yy0xLjY4IDAtMi42NjIuMzY3LTIuNjYyIDEuNDc4IDAgLjc2Mi40Njgg - MS4yNTggMS41MjQgMS4yNTggMS4yOTQuMDM3IDIuMjc3LS42NjEgMi4yNzct - MS44MThoLS4wMzdaTTEwNS4xMzcgMTEuOTM2aDIuODkzdjEuNTk4YTMuNTY3 - IDMuNTY3IDAgMCAxIDMuMTk1LTEuODM3IDIuODg2IDIuODg2IDAgMCAxIDIu - OTIgMS44MzcgNC4xMzggNC4xMzggMCAwIDEgMy41MTYtMS44MzZjMS45Mzcg - MCAzLjQzNCAxLjIyIDMuNDM0IDMuOTkzdjYuNjkzaC0yLjg3NFYxNi4wNWMw - LTEuMzQtLjU5Ny0xLjkzNy0xLjY0My0xLjkzN2ExLjk0IDEuOTQgMCAwIDAt - Mi4wMTEgMi4xNHY2LjE3aC0yLjg4M3YtNi4zNzNjMC0xLjM0LS42MTUtMS45 - MzctMS42MzQtMS45MzdhMS45NCAxLjk0IDAgMCAwLTEuOTI5IDEuMzE1IDEu - OTIgMS45MiAwIDAgMC0uMDkxLjgyNHY2LjE3aC0yLjg5M1YxMS45MzZaTTEy - My4wNzggMTEuOTM2aDIuOTAxdjEuNjQzYTMuODg3IDMuODg3IDAgMCAxIDMu - MzMzLTEuODgyYzIuNTYyIDAgNC41OTEgMS45IDQuNTkxIDUuMzUzdi4xNjVj - MCAzLjQ1My0xLjk1NiA1LjM5LTQuNTkxIDUuMzlhMy42NyAzLjY3IDAgMCAx - LTMuMzMzLTEuODM2djUuMjE1aC0yLjkwMVYxMS45MzZabTcuODUgNS4xMzJj - MC0yLjExMS0xLjAzNy0zLjE0OS0yLjQ5Ny0zLjE0OXMtMi41NTMgMS4wNTYt - Mi41NTMgMy4xNXYuMTY1YzAgMi4wOTMgMS4wMDEgMy4xMTIgMi41OCAzLjEx - MiAxLjU4IDAgMi40Ny0xLjA2NSAyLjQ3LTMuMDc2di0uMjAyWk0xOC4wODgu - MjdjOS4xIDAgMTUuMjE1IDEwLjUxOCAxNS45NzcgMjEuOTM3LjAyLjMxMy0u - MDUzLjYyNi0uMjEyLjg5Ni0zLjE0IDUuMzUtMTAuMDYxIDYuNTI3LTE1Ljcz - NyA2LjU1OC01LjQ4Ny4xLTEwLjctMi4xODgtMTQuNDEyLTYuMzAxYTEuNTY2 - IDEuNTY2IDAgMCAxLS4zMDMtMS42IDM2LjE3NyAzNi4xNzcgMCAwIDEgMS45 - MTItNC4xNDdjMS4wNTItMS45MjggMi42NDQtNC42ODEgNS4xNTQtNC43NjMg - Mi4zNDMgMCAzLjUxNiAyLjE3NCA1LjExNCAzLjUxOSAxLjYzMy0xLjY3MiAy - LjU1Mi0zLjk0IDMuNTY3LTYuMDE0YTEuNTY1IDEuNTY1IDAgMCAxIDIuODM3 - IDEuMzI2Yy0uODg1IDEuODI5LTEuODE0IDMuNjUxLTIuOTU0IDUuMzM2LTEu - MTcyIDEuNzMyLTIuMDczIDIuNjM2LTMuMzMgMi42MzYtLjc0NiAwLTEuMzg1 - LS4yOTItMi4wMy0uODAxLTEuMTAzLS45Mi0xLjkzNy0yLjA4OC0zLjE1LTIu - ODczLTEuNTY3Ljc4NS0yLjk5IDQuMDc5LTMuODI0IDUuOTggMi45MjUgMi44 - OCA2Ljg5OCA0LjU1IDExLjAwOCA0LjU3MyA0LjYyMi0uMDI4IDEwLjI4Ni0u - NDkgMTMuMTk3LTQuNjItLjU3NS03LjExMS00LjAxMy0xOC4zNzctMTIuODE0 - LTE4LjUxLTcuMDk3IDAtMTEuNzU0IDUuMDQ3LTE0Ljc3NSAxMy42NDRBMS41 - NjUgMS41NjUgMCAxIDEgLjM2IDE2LjAwOEMzLjc3MSA2LjI5OSA5LjMzMy4y - NyAxOC4wODguMjdaIiBmaWxsPSJ2YXIoLS1jb2xvci1pbmspIi8+PC9zdmc+ - CiAgICAgIDxzcGFuIGNsYXNzPSJhLWZvci1zY3JlZW4tcmVhZGVyIj5IUTwv - c3Bhbj4KICAgIDwvYT4KICAgIDxkaXYgY2xhc3M9Im5hdi1tZW51X19zaGVl - dCBleHBhbmRlZF9jb250ZW50IiBpZD0ibmF2aWdhdGlvbl9hY2NvdW50cyI+ - PC9kaXY+CiAgPC9kaXY+CjwvZGl2PgoKCiAgICA8dWwgY2xhc3M9Im5hdl9f - bWFpbiBsaXN0LS11bmJ1bGxldGVkIGxvYWRpbmdfX2hpZGUiPgogICAgICA8 - bGkgY2xhc3M9Im5hdl9faXRlbSI+CiAgICAgICAgPGEgY2xhc3M9Im5hdl9f - bGluayBuYXZfX2xpbmstLWhvbWUiIGhyZWY9Ii8xODE5MDA0MDUvcHJvamVj - dHMiPgogICAgICAgICAgPHN2ZyB2aWV3Qm94PSIwIDAgMjQgMjQiPjxwYXRo - IGQ9Im0xMi4yIDIxLjcgNy40IDEuM2guMmMuMiAwIC4zLS4xLjQtLjJsMi4y - LTIuMmMuMy0uMi4zLS44IDAtMS0uMi0uMy0uOC0uMy0xIDBsLS4zLjQgMS42 - LTcuMSAxLTEuMmMuMi0uMS4zLS4zLjItLjctLjEtLjMtLjItLjUtLjQtLjYt - LjItLjEtLjUgMC0uNy4ybC0uNC40LTYuMS05LjdjLS4yLS4zLS44LS41LTEu - MS0uMkw5LjQgNC42Yy0uMS4xLS4yLjEtLjMuMkwxLjggMTcuNGwtLjYtMWMt - LjEtLjItLjMtLjUtLjUtLjVzLS41LjEtLjYuM2MtLjEuNC0uMS42IDAgLjhs - MS4xIDIuMnYuMWMuMS4xLjEuMi4zLjMuMS4xLjIuMS4zLjFsNS4xLjkgMi44 - LTguOCAyLjUgOS45Wk0xMC45IDUuNiAxNS40IDNsNS43IDkuMi0uMS43LTEu - NiA3LTYtMTAuMS0yLjUtNC4yWiIgZmlsbD0iY3VycmVudENvbG9yIi8+PC9z - dmc+CiAgICAgICAgICBIb21lCjwvYT4gICAgICA8L2xpPgoKICAgICAgPGxp - IGNsYXNzPSJuYXZfX2l0ZW0iIGRhdGEtbWVudS1zZWN0aW9uPSJsaW5ldXAi - PgogICAgICAgIDxhIGNsYXNzPSJuYXZfX2xpbmsiIGhyZWY9Ii8xODE5MDA0 - MDUvbGluZXVwIj4KICAgICAgICAgIDxzdmcgdmlld0JveD0iMCAwIDI0IDI0 - Ij48cGF0aCBkPSJNOCA2YTIgMiAwIDAgMSAyLTJoMTBhMiAyIDAgMSAxIDAg - NEgxMGEyIDIgMCAwIDEtMi0yWm0tNiA2YTIgMiAwIDAgMSAyLTJoMTBhMiAy - IDAgMSAxIDAgNEg0YTIgMiAwIDAgMS0yLTJabTUgNGEyIDIgMCAxIDAgMCA0 - aDEwYTIgMiAwIDEgMCAwLTRIN1oiIGZpbGw9ImN1cnJlbnRDb2xvciIvPjwv - c3ZnPgogICAgICAgICAgTGluZXVwCjwvYT4gICAgICA8L2xpPgoKICAgICAg - ICA8bGkgY2xhc3M9Im5hdl9faXRlbSBuYXYtbWVudSIgZGF0YS1iZWhhdmlv - cj0iZXhwYW5kYWJsZSBsb2FkX29uX2V4cGFuZCIKICAgIGRhdGEtbWVudS1z - ZWN0aW9uPSJwaW5ncyIgZGF0YS1sb2FkLXVybD0iLzE4MTkwMDQwNS9teS9u - YXZpZ2F0aW9uL3BpbmdzIiBkYXRhLWxvYWQtdGFyZ2V0PSIjbmF2aWdhdGlv - bl9waW5ncyI+CiAgPGRpdiBjbGFzcz0iY29sbGFwc2libGVfY29udGVudCIg - ZGF0YS1iZWhhdmlvcj0iY29sbGFwc2Vfb25fY2xpY2tvdXRzaWRlIj4KICAg - IDxhIGNsYXNzPSJuYXZfX2xpbmsgbmF2X19saW5rLS1waW5ncyIgcm9sZT0i - YnV0dG9uIiBhcmlhLWhhc3BvcHVwPSJ0cnVlIiBkYXRhLWJlaGF2aW9yPSJ0 - b2dnbGVfZXhwYW5zaW9uX29uX2NsaWNrIiBocmVmPSIvMTgxOTAwNDA1L2Np - cmNsZXMiPgogICAgICA8c3ZnIHZpZXdib3g9IjAgMCAyNCAyNCI+PHBhdGgg - ZmlsbD0iY3VycmVudENvbG9yIiBkPSJNNSAzYTQgNCAwIDAgMC00IDR2NWE0 - LjAwMiA0LjAwMiAwIDAgMCAzIDMuODc0VjE5YTEgMSAwIDAgMCAxLjYyNS43 - OEwxMC4zNSAxNkgxNWE0IDQgMCAwIDAgNC00VjdhNCA0IDAgMCAwLTQtNEg1 - WiIvPjxwYXRoIGZpbGw9ImN1cnJlbnRDb2xvciIgZD0iTTEzIDIwYy0xLjMy - IDAtMi40OS0uNjM5LTMuMjE4LTEuNjI0bDEuMDk1LS44NzZIMTVhNS41IDUu - NSAwIDAgMCA1LjUtNS41VjkuMjlBNC4wMDEgNC4wMDEgMCAwIDEgMjMgMTN2 - M2E0LjAwMiA0LjAwMiAwIDAgMS0zIDMuODc0VjIyYTEgMSAwIDAgMS0xLjYu - OEwxNC42NjcgMjBIMTNaIi8+PC9zdmc+CiAgICAgIFBpbmdzCiAgICAgIDxz - cGFuIGNsYXNzPSJ1bnJlYWQtYmFkZ2UgdW5yZWFkLWJhZGdlLS1mb3ItbmF2 - IiBkYXRhLXVucmVhZC1iYWRnZT0icGluZ3MiIGRhdGEtYmFkZ2UtY291bnQ9 - IjAiPgogICAgICAgIDxzcGFuIGNsYXNzPSJhLWZvci1zY3JlZW4tcmVhZGVy - Ij51bnJlYWQ8L3NwYW4+CiAgICAgIDwvc3Bhbj4KPC9hPgogICAgPGRpdiBj - bGFzcz0ibmF2LW1lbnVfX3NoZWV0IG5hdi1tZW51X19zaGVldC0tbGFyZ2Ug - ZXhwYW5kZWRfY29udGVudCIgaWQ9Im5hdmlnYXRpb25fcGluZ3MiPjwvZGl2 - PgogIDwvZGl2Pgo8L2xpPgoKCiAgICAgIDxsaSBjbGFzcz0ibmF2X19pdGVt - IG5hdi1tZW51IiBkYXRhLWJlaGF2aW9yPSJleHBhbmRhYmxlIGxvYWRfb25f - ZXhwYW5kIgogICAgZGF0YS1tZW51LXNlY3Rpb249InJlYWRpbmdzIiBkYXRh - LWxvYWQtdXJsPSIvMTgxOTAwNDA1L215L25hdmlnYXRpb24vcmVhZGluZ3Mi - IGRhdGEtbG9hZC10YXJnZXQ9IiNuYXZpZ2F0aW9uX3JlYWRpbmdzIj4KICA8 - ZGl2IGNsYXNzPSJjb2xsYXBzaWJsZV9jb250ZW50IiBkYXRhLWJlaGF2aW9y - PSJjb2xsYXBzZV9vbl9jbGlja291dHNpZGUiPgogICAgPGEgY2xhc3M9Im5h - dl9fbGluayBuYXZfX2xpbmstLWhleSIgcm9sZT0iYnV0dG9uIiBpZD0ibmF2 - X19saW5rLS1oZXkiIGFyaWEtaGFzcG9wdXA9InRydWUiIGRhdGEtYmVoYXZp - b3I9InRvZ2dsZV9leHBhbnNpb25fb25fY2xpY2siIGRhdGEtY29udHJvbGxl - cj0iYW5pbWF0aW9uIiBkYXRhLWFuaW1hdGlvbi1ldmVudC1uYW1lLXZhbHVl - PSJiYzphZGQtdG8taGV5IiBkYXRhLWFuaW1hdGlvbi1wbGF5LWNsYXNzPSJu - YXZfX2xpbmstLWFkZC10by1oZXkiIGhyZWY9Ii8xODE5MDA0MDUvbXkvcmVh - ZGluZ3MiPgogICAgICA8c3ZnIHZpZXdib3g9IjAgMCAyNCAyNCI+PHBhdGgg - ZmlsbD0iY3VycmVudENvbG9yIiBmaWxsLXJ1bGU9ImV2ZW5vZGQiIGQ9Ik0x - LjAxMyAxMy44MzhhLjk5Mi45OTIgMCAwIDAtLjAxMy4xODRWMjBhMiAyIDAg - MCAwIDIgMmgxOGEyIDIgMCAwIDAgMi0ydi01Ljk3NmEuOTkuOTkgMCAwIDAt - LjA2OC0uMzg2TDE5LjQyIDMuOTc1QTMgMyAwIDAgMCAxNi41OTkgMkg3LjQw - MWEzIDMgMCAwIDAtMi44MiAxLjk3NWwtMy41MTIgOS42NmEuOTkyLjk5MiAw - IDAgMC0uMDU2LjIwM1ptNS40NDgtOS4xOEExIDEgMCAwIDEgNy40MDEgNGg5 - LjE5OGExIDEgMCAwIDEgLjk0LjY1OEwyMC41NzIgMTNoLTMuODUxYy0uODcg - MC0xLjU1NC41NDYtMS44OTggMS4xNzJDMTQuNDAzIDE0LjkzOCAxMy41NDYg - MTYgMTIgMTZzLTIuNDAzLTEuMDYyLTIuODIzLTEuODI4QzguODMzIDEzLjU0 - NiA4LjE0OSAxMyA3LjI3OSAxM0gzLjQyOEw2LjQ2IDQuNjU4WiIgY2xpcC1y - dWxlPSJldmVub2RkIi8+PC9zdmc+CiAgICAgIEhleSEgPHNwYW4gY2xhc3M9 - ImEtZm9yLXNjcmVlbi1yZWFkZXIiPk5vdGlmaWNhdGlvbiBpbmJveDwvc3Bh - bj4KICAgICAgPHNwYW4gY2xhc3M9InVucmVhZC1iYWRnZSB1bnJlYWQtYmFk - Z2UtLWZvci1uYXYiIGRhdGEtdW5yZWFkLWJhZGdlPSJoZXlzdGFjayIgZGF0 - YS1iYWRnZS1jb3VudD0iMCI+CiAgICAgICAgPHNwYW4gY2xhc3M9ImEtZm9y - LXNjcmVlbi1yZWFkZXIiPnVucmVhZDwvc3Bhbj4KICAgICAgPC9zcGFuPgo8 - L2E+ICAgIDxzcGFuIGNsYXNzPSJuYXZfX2xpbmstLWFuaW1hdGVkLXBhbmVs - Ij48L3NwYW4+CiAgICA8ZGl2IGNsYXNzPSJuYXYtbWVudV9fc2hlZXQgbmF2 - LW1lbnVfX3NoZWV0LS1sYXJnZSBuYXYtbWVudV9fc2hlZXQtLXVucGFkLXRv - cCBleHBhbmRlZF9jb250ZW50IiBpZD0ibmF2aWdhdGlvbl9yZWFkaW5ncyI+ - PC9kaXY+CiAgPC9kaXY+CjwvbGk+CgoKICAgICAgPGxpIGNsYXNzPSJuYXZf - X2l0ZW0iIGRhdGEtbWVudS1zZWN0aW9uPSJhY3Rpdml0eSI+CiAgICAgICAg - PGEgY2xhc3M9Im5hdl9fbGluayBuYXZfX2xpbmstLWFjdGl2aXR5IiBocmVm - PSIvMTgxOTAwNDA1L3JlcG9ydHMvcHJvZ3Jlc3MiPgogICAgICAgIDxzdmcg - dmlld0JveD0iMCAwIDI0IDI0Ij48cGF0aCBmaWxsPSJjdXJyZW50Q29sb3Ii - IGZpbGwtcnVsZT0iZXZlbm9kZCIgZD0iTTE5Ljc3OCAxOS43NzhsLjAwMi0u - MDAyQzIxLjc3IDE3Ljc4NiAyMyAxNS4wMzYgMjMgMTJjMC02LjA3NS00Ljky - NS0xMS0xMS0xMVMxIDUuOTI1IDEgMTJhMTAuOTYzIDEwLjk2MyAwIDAwMy4z - MTYgNy44N0ExMC45NjQgMTAuOTY0IDAgMDAxMiAyM2ExMC45NjMgMTAuOTYz - IDAgMDA3Ljc3OC0zLjIyMnpNMTMgMy4wNTV2OC41M2w2LjAzMiA2LjAzM0E5 - LjAwMSA5LjAwMSAwIDAwMTMgMy4wNTV6IiBjbGlwLXJ1bGU9ImV2ZW5vZGQi - Lz48L3N2Zz4KICAgICAgICBBY3Rpdml0eQo8L2E+ICAgICAgPC9saT4KCiAg - ICAgIDxsaSBjbGFzcz0ibmF2X19pdGVtIG5hdi1tZW51IgogICAgZGF0YS1i - ZWhhdmlvcj0iZXhwYW5kYWJsZSBsb2FkX29uX2V4cGFuZCBteV9zdHVmZl90 - b2dnbGVfc2hvcnRjdXQiCiAgICBkYXRhLW1lbnUtc2VjdGlvbj0ibXkiCiAg - ICBkYXRhLWxvYWQtdXJsPSIvMTgxOTAwNDA1L215L25hdmlnYXRpb24vbXlf - c3R1ZmYiCiAgICBkYXRhLWxvYWQtdGFyZ2V0PSIjbmF2aWdhdGlvbl9teV9z - dHVmZiI+CiAgPGRpdiBjbGFzcz0iY29sbGFwc2libGVfY29udGVudCIgZGF0 - YS1iZWhhdmlvcj0iY29sbGFwc2Vfb25fY2xpY2tvdXRzaWRlIj4KICAgIDxh - IGhyZWY9Ii8xODE5MDA0MDUvbXkvYXNzaWdubWVudHMiIGNsYXNzPSJuYXZf - X2xpbmsgbmF2X19saW5rLS1teS1zdHVmZiIgZGF0YS1iZWhhdmlvcj0idG9n - Z2xlX2V4cGFuc2lvbl9vbl9jbGljayIKICAgICAgZGF0YS1yb2xlPSJuYXYt - c2hvcnRjdXQtdGl0bGUiIGRhdGEtbWFjLXNob3J0Y3V0LXRpdGxlPSJLZXli - b2FyZCBzaG9ydGN1dDog4oyYICsgOyIKICAgICAgcm9sZT0iYnV0dG9uIiBh - cmlhLWxhYmVsPSJNeSBTdHVmZiIgYXJpYS1oYXNwb3B1cD0idHJ1ZSIgdGl0 - bGU9IktleWJvYXJkIHNob3J0Y3V0OiBDdHJsICsgOyI+CiAgICAgIDxzdmcg - dmlld2JveD0iMCAwIDI0IDI0Ij48cGF0aCBmaWxsPSJjdXJyZW50Q29sb3Ii - IGQ9Ik05IDExYTEuNSAxLjUgMCAxIDAgMC0zIDEuNSAxLjUgMCAwIDAgMCAz - Wk0xNi41IDkuNWExLjUgMS41IDAgMSAxLTMgMCAxLjUgMS41IDAgMCAxIDMg - MFpNOC41ODkgMTQuMjRhMSAxIDAgMCAwLTEuMzAxIDEuNTJBNy4yMjUgNy4y - MjUgMCAwIDAgMTIgMTcuNWE3LjIyNSA3LjIyNSAwIDAgMCA0LjcxMi0xLjc0 - IDEgMSAwIDEgMC0xLjMtMS41MkE1LjIyNSA1LjIyNSAwIDAgMSAxMiAxNS41 - YTUuMjI1IDUuMjI1IDAgMCAxLTMuNDExLTEuMjZaIi8+PHBhdGggZmlsbD0i - Y3VycmVudENvbG9yIiBmaWxsLXJ1bGU9ImV2ZW5vZGQiIGQ9Ik0xMiAxQzUu - OTI1IDEgMSA1LjkyNSAxIDEyczQuOTI1IDExIDExIDExIDExLTQuOTI1IDEx - LTExUzE4LjA3NSAxIDEyIDFaTTMgMTJhOSA5IDAgMSAxIDE4IDAgOSA5IDAg - MCAxLTE4IDBaIiBjbGlwLXJ1bGU9ImV2ZW5vZGQiLz48L3N2Zz4KICAgICAg - PHNwYW4gY2xhc3M9InUtaGlkZS1vbi1tZWRpYS1tZWRpdW0iPk1lPC9zcGFu - PgogICAgICA8c3BhbiBjbGFzcz0idS1oaWRlLW9uLW1lZGlhLXNtYWxsIj5N - eSBTdHVmZjwvc3Bhbj4KICAgIDwvYT4KCiAgICA8ZGl2IGNsYXNzPSJuYXYt - bWVudV9fc2hlZXQgbmF2LW1lbnVfX3NoZWV0LS1zbWFsbCBleHBhbmRlZF9j - b250ZW50IiBpZD0ibmF2aWdhdGlvbl9teV9zdHVmZiI+CiAgICA8L2Rpdj4K - ICA8L2Rpdj4KPC9saT4KCiAgICAgIDxsaSBjbGFzcz0ibmF2X19pdGVtIG5h - di1tZW51IgogICAgZGF0YS1tZW51LXNlY3Rpb249InNlYXJjaCIKICAgIGRh - dGEtYmVoYXZpb3I9ImV4cGFuZGFibGUgc2VhcmNoX3RvZ2dsZV9zaG9ydGN1 - dCIKICAgIGRhdGEtY29udHJvbGxlcj0ic2VhcmNoIGxpc3Qtc2VsZWN0aW9u - IgogICAgZGF0YS1hY3Rpb249IgogICAgICBjbGljay0+c2VhcmNoI2Nsb3Nl - U2VhcmNoV2hlbk5hdmlnYXRpbmcKICAgICAgYmVmb3JlLWNvbGxhcHNlLT5z - ZWFyY2gjc2F2ZVBvc2l0aW9uQW5kRXhwaXJhdGlvbgogICAgICBleHBhbmQt - PnNlYXJjaCNvcGVuCiAgICAgIGtleWRvd25Ad2luZG93LT5saXN0LXNlbGVj - dGlvbiN1cGRhdGVTZWxlY3Rpb25XaXRoS2V5Ym9hcmQiCiAgICBkYXRhLXNl - YXJjaC11cmw9Ii8xODE5MDA0MDUvbXkvbmF2aWdhdGlvbi9zZWFyY2hlcy9u - ZXciCiAgICBkYXRhLWxpc3Qtc2VsZWN0aW9uLXNlbGVjdGVkLWNsYXNzPSJz - ZWFyY2gtcmVzdWx0LS1zZWxlY3RlZCI+CiAgPGRpdiBjbGFzcz0iY29sbGFw - c2libGVfY29udGVudCIgZGF0YS1iZWhhdmlvcj0iY29sbGFwc2Vfb25fY2xp - Y2tvdXRzaWRlIj4KICAgIDxhIGNsYXNzPSJuYXZfX2xpbmsgbmF2X19saW5r - LS1zZWFyY2giIHJvbGU9ImJ1dHRvbiIgYXJpYS1oYXNwb3B1cD0idHJ1ZSIg - dGl0bGU9IktleWJvYXJkIHNob3J0Y3V0OiBDdHJsICsgLyIgZGF0YS1iZWhh - dmlvcj0idG9nZ2xlX2V4cGFuc2lvbl9vbl9jbGljayIgZGF0YS1yb2xlPSJu - YXYtc2hvcnRjdXQtdGl0bGUiIGRhdGEtbWFjLXNob3J0Y3V0LXRpdGxlPSJL - ZXlib2FyZCBzaG9ydGN1dDog4oyYICsgLyIgaHJlZj0iLzE4MTkwMDQwNS9z - ZWFyY2giPgogICAgICA8c3ZnIHZpZXdib3g9IjAgMCAyNCAyNCI+PHBhdGgg - ZmlsbD0iY3VycmVudENvbG9yIiBmaWxsLXJ1bGU9ImV2ZW5vZGQiIGQ9Ik0x - MSAzYTggOCAwIDEwNC44NTIgMTQuMzYxYzEuMDIxIDEuMjE0IDMuMjkzIDMu - OTA1IDMuNzQ0IDQuMzU3YTEuNSAxLjUgMCAwMDIuMTIxLTIuMTIyYy0uNDE4 - LS40MTgtMy4xMzUtMi43MTQtNC4zNTYtMy43NDNBOCA4IDAgMDAxMSAzem0t - NiA4YTYgNiAwIDExMTIgMCA2IDYgMCAwMS0xMiAweiIgY2xpcC1ydWxlPSJl - dmVub2RkIi8+PC9zdmc+CiAgICAgIEZpbmQKPC9hPgogICAgPGRpdiBjbGFz - cz0ibmF2LW1lbnVfX3NoZWV0IG5hdi1tZW51X19zaGVldC0tbGFyZ2UgZXhw - YW5kZWRfY29udGVudCIgZGF0YS1zZWFyY2gtdGFyZ2V0PSJmb3JtQ29udGFp - bmVyIj4KICAgIDwvZGl2PgogIDwvZGl2Pgo8L2xpPgoKICAgIDwvdWw+Cgog - ICAgPGRpdiBjbGFzcz0ibmF2LW1lbnUgbmF2LW1lbnVfX21lIiBkYXRhLWJl - aGF2aW9yPSJleHBhbmRhYmxlIGxvYWRfb25fZXhwYW5kIgogICAgZGF0YS1t - ZW51LXNlY3Rpb249Im1lIiBkYXRhLWxvYWQtdXJsPSIvMTgxOTAwNDA1L215 - L25hdmlnYXRpb24vbWUiIGRhdGEtbG9hZC10YXJnZXQ9IiNuYXZpZ2F0aW9u - X21lIj4KICA8ZGl2IGNsYXNzPSJjb2xsYXBzaWJsZV9jb250ZW50IiBkYXRh - LWJlaGF2aW9yPSJjb2xsYXBzZV9vbl9jbGlja291dHNpZGUiPgogICAgPGEg - aHJlZj0iLzE4MTkwMDQwNS9teS9wcm9maWxlIiBjbGFzcz0ibmF2X19saW5r - LS1tZSIgZGF0YS1iZWhhdmlvcj0idG9nZ2xlX2V4cGFuc2lvbl9vbl9jbGlj - ayIKICAgICAgcm9sZT0iYnV0dG9uIiBhcmlhLWxhYmVsPSJOb3RpZmljYXRp - b24gT3B0aW9ucyBhbmQgUGVyc29uYWwgU2V0dGluZ3MiIGFyaWEtaGFzcG9w - dXA9InRydWUiPgogICAgICA8c3BhbiBjbGFzcz0ibm90aWZpY2F0aW9uLXN0 - YXR1cyB1LWhpZGUtb24tbWVkaWEtc21hbGwiIGRhdGEtcm9sZT0ibXlfbm90 - aWZpY2F0aW9uc19iYWRnZSIgZGF0YS11cmw9Ii8xODE5MDA0MDUvbXkvbm90 - aWZpY2F0aW9ucyI+CiAgICAgICAgPHNwYW4gY2xhc3M9Im5vdGlmaWNhdGlv - bi1zdGF0dXNfX2xhYmVsIG5vdGlmaWNhdGlvbi1zdGF0dXNfX2xhYmVsLS1z - bm9vemVkIj5Gb2N1c2luZzwvc3Bhbj4KICAgICAgICA8c3BhbiBjbGFzcz0i - bm90aWZpY2F0aW9uLXN0YXR1c19fbGFiZWwgbm90aWZpY2F0aW9uLXN0YXR1 - c19fbGFiZWwtLW9mZiI+T2ZmPC9zcGFuPgogICAgICA8L3NwYW4+CgogICAg - ICA8aW1nIGNsYXNzPSJhdmF0YXIiIGRhdGEtY3VycmVudC1wZXJzb24tYXZh - dGFyPSJ0cnVlIiBhbHQ9Ik15IGF2YXRhciIgc3JjPSJodHRwOi8vMy5iYXNl - Y2FtcC5sb2NhbGhvc3Q6MzAwMS8xODE5MDA0MDUvbXkvYXZhdGFyIiB3aWR0 - aD0iMTMwIiBoZWlnaHQ9IjEzMCIgLz4KCiAgICAgIDxzcGFuIGNsYXNzPSJh - LWZvci1zY3JlZW4tcmVhZGVyIj5NZTwvc3Bhbj4KICAgIDwvYT4KCiAgICA8 - ZGl2IGNsYXNzPSJuYXYtbWVudV9fc2hlZXQgbmF2LW1lbnVfX3NoZWV0LS1h - bGlnbi1yaWdodCBleHBhbmRlZF9jb250ZW50IiBpZD0ibmF2aWdhdGlvbl9t - ZSI+CiAgICA8L2Rpdj4KICA8L2Rpdj4KPC9kaXY+CgogIDwvbmF2Pgo8L2Rp - dj4KCgogIDxub3NjcmlwdD4KICA8ZGl2IGNsYXNzPSJub3NjcmlwdC1tZXNz - YWdlIGNlbnRlcmVkIHUtbWFyZ2luLWNlbnRlcmVkIj4KICAgIDxzcGFuPuKa - oO+4jzwvc3Bhbj4KICAgIDxzcGFuIGNsYXNzPSJub3NjcmlwdC1tZXNzYWdl - X19jb250ZW50Ij4KICAgICAgU29tZSBCYXNlY2FtcCBmZWF0dXJlcyB3b27i - gJl0IHdvcmsgdW5sZXNzIHlvdSBlbmFibGUgSmF2YVNjcmlwdCBpbiB5b3Vy - IGJyb3dzZXIuCiAgICAgIDxhIGNsYXNzPSJkZWNvcmF0ZWQiIHRhcmdldD0i - X2JsYW5rIiBocmVmPSJodHRwOi8vZW5hYmxlLWphdmFzY3JpcHQuY29tIj5M - ZWFybiBtb3Jl4oCmPC9hPgogICAgPC9zcGFuPgogIDwvZGl2Pgo8L25vc2Ny - aXB0PgoKCiAgPGRpdiBjbGFzcz0ibG9hZGluZ19faGlkZSI+CiAgICA8ZGl2 - IGNsYXNzPSJoZWxwLWJ1dHRvbiBhcHAtbW9iaWxlX19oaWRlIiBkYXRhLXJv - bGU9ImhlbHBfYnV0dG9uX2NvbnRhaW5lciIgZGF0YS1iZWFjb24taWQ9Ijkx - MzgwOGQyLTEwMmMtNDVmYy1iZmQyLTA4NjhiMWExNDczOCI+PGJ1dHRvbiBu - YW1lPSJidXR0b24iIHR5cGU9ImJ1dHRvbiIgY2xhc3M9ImhlbHAtYnV0dG9u - X19pY29uIiBkYXRhLWJlaGF2aW9yPSJoZWxwX2J1dHRvbiIgdGFiaW5kZXg9 - IjEiPkdldCBoZWxwPC9idXR0b24+PC9kaXY+CiAgPC9kaXY+CgogIDxtYWlu - IGlkPSJtYWluLWNvbnRlbnQiIGNsYXNzPSJsb2FkaW5nX19oaWRlICB1LWhp - ZGUtZm9jdXMiIGRhdGEtYXBwZWFyaW5nLW9uPSJCQWg3QmtraUMxOXlZV2xz - Y3dZNkJrVlVld2RKSWdsa1lYUmhCanNBVkVraUxXZHBaRG92TDJKak15OVNa - V052Y21ScGJtY3ZOemd6TlRJMk1UQXhQMlY0Y0dseVpYTmZhVzRHT3dCVVNT - SUljSFZ5QmpzQVZFa2lEWEpsWVdSaFlteGxCanNBVkE9PS0tNGZlOTg0NzM1 - MzUwMDBlYjY5Zjc5Y2Q2YzZkNTRhMDdiNjdhMWZmOSIgZGF0YS1idWNrZXQt - dXJsPSIvMTgxOTAwNDA1L2J1Y2tldHMvMTA0Mjk3OTI0NyIgZGF0YS1idWNr - ZXQtaWQ9IjEwNDI5NzkyNDciIGRhdGEtY29udHJvbGxlcj0iYnJpZGdlLS1w - YWdlIGJyaWRnZS0taW5zZXRzIj4KICAgIDxkaXYgY2xhc3M9InUtZGlzcGxh - eS1uIiBkYXRhLWNvbnRyb2xsZXI9ImJlYWNvbiIgZGF0YS1iZWFjb24tdXJs - LXZhbHVlPSIvMTgxOTAwNDA1L3Byb2plY3RzLzEwNDI5NzkyNDcvcmVjZW50 - X3Zpc2l0Ij48L2Rpdj4KICAgIDxzZWN0aW9uIGNsYXNzPSJzeXN0ZW0tZGVn - cmFkYXRpb25zLWJhbm5lciBmb3JtYXR0ZWRfY29udGVudCI+CiAgPGgxIGNs - YXNzPSJ0eHQtLXdhcm5pbmcgY2VudGVyZWQgcHVzaF9oYWxmLS1ib3R0b20i - PgogICAgQmFzZWNhbXAgaXNu4oCZdCBmdWxseSBmdW5jdGlvbmFsIHJpZ2h0 - IG5vdy4KICA8L2gxPgoKICA8cCBkYXRhLXN5c3RlbS1kZWdyYWRhdGlvbj0i - dXBsb2Fkc19kaXNhYmxlZCIgY2xhc3M9InB1c2gtLWJvdHRvbSI+CiAgICA8 - c3Ryb25nPkZpbGUgdXBsb2FkaW5nIGlzIGRpc2FibGVkPC9zdHJvbmc+IGR1 - ZSB0byBhIHNlcnZpY2UgZGlzcnVwdGlvbi4KICAgIFlvdXIgZmlsZXMgYXJl - IHNhZmUsIGJ1dCB5b3UgY2Fu4oCZdCB1cGxvYWQgbmV3IGZpbGVzIHRvIG1l - c3NhZ2VzLCBjb21tZW50cywgZG9jdW1lbnRzLCBvciB0aGUgRG9jcyAmIEZp - bGVzIHNlY3Rpb24uCiAgPC9wPgoKICA8cCBkYXRhLXN5c3RlbS1kZWdyYWRh - dGlvbj0ic2VhcmNoX2Rpc2FibGVkIiBjbGFzcz0icHVzaC0tYm90dG9tIj4K - ICAgIDxzdHJvbmc+U2VhcmNoIGlzIG9mZmxpbmU8L3N0cm9uZz4gZHVlIHRv - IGEgc2VydmljZSBkaXNydXB0aW9uLgogICAgWW91ciBkYXRhIGlzIHNhZmUs - IGFuZCB5b3UgY2FuIGNvbnRpbnVlIHRvIHVzZSBCYXNlY2FtcC4KICA8L3A+ - CgogIDxwIGRhdGEtc3lzdGVtLWRlZ3JhZGF0aW9uPSJyZWFkb25seV9kYXRh - YmFzZSIgY2xhc3M9InB1c2gtLWJvdHRvbSI+CiAgICA8c3Ryb25nPlRoZSBk - YXRhYmFzZSBpcyBpbiByZWFkLW9ubHkgbW9kZTwvc3Ryb25nPiBkdWUgdG8g - YSBzZXJ2aWNlIGRpc3J1cHRpb24uCiAgICBZb3VyIGRhdGEgaXMgc2FmZSwg - YnV0IHlvdSBjYW7igJl0IGN1cnJlbnRseSBwb3N0IG5ldyBtZXNzYWdlcywg - dG8tZG9zLCBkb2N1bWVudHMsIGV0Yy4KICA8L3A+CgogIDxwIGRhdGEtc3lz - dGVtLWRlZ3JhZGF0aW9uPSJhY3Rpdml0eV90aW1lbGluZV9kaXNhYmxlZCIg - Y2xhc3M9InB1c2gtLWJvdHRvbSI+CiAgICA8c3Ryb25nPkFjdGl2aXR5IHRp - bWVsaW5lcyBhcmUgdGVtcG9yYXJpbHkgZGlzYWJsZWQ8L3N0cm9uZz4gZHVl - IHRvIGEgc2VydmljZSBkaXNydXB0aW9uLgogICAgWW91ciBkYXRhIGlzIHNh - ZmUsIGFuZCB5b3UgY2FuIGNvbnRpbnVlIHRvIHVzZSBCYXNlY2FtcC4KICA8 - L3A+CgogIDxmb290ZXI+CiAgICBXZeKAmXJlIHRlcnJpYmx5IHNvcnJ5IGZv - ciB0aGUgZGlzcnVwdGlvbiBhbmQgYXJlIHdvcmtpbmcgZGlsaWdlbnRseSB0 - byBicmluZyBCYXNlY2FtcCBiYWNrIHRvIDEwMCUuPGJyPgogICAgVmlzaXQg - PGEgaHJlZj0iaHR0cHM6Ly9iYXNlY2FtcC5jb20vc3RhdHVzIiB0YXJnZXQ9 - Il9ibGFuayI+b3VyIHN0YXR1cyBzaXRlPC9hPiBmb3IgdXBkYXRlcy4KICA8 - L2Zvb3Rlcj4KPC9zZWN0aW9uPgoKICAgIDxkaXYgaWQ9ImFubm91bmNlbWVu - dF9iYW5uZXIiIGNsYXNzPSJwdXNoX2hhbGYtLXNpZGVzIiBkYXRhLWNvbnRy - b2xsZXI9ImFubm91bmNlbWVudC1iYW5uZXIiIGRhdGEtYW5ub3VuY2VtZW50 - LWJhbm5lci11cmw9Imh0dHA6Ly8zLmJhc2VjYW1wLmxvY2FsaG9zdDozMDAx - LzE4MTkwMDQwNS9teS9hbm5vdW5jZW1lbnQiIGRhdGEtYW5ub3VuY2VtZW50 - LWJhbm5lci1yZWFkLXVybD0iaHR0cDovLzMuYmFzZWNhbXAubG9jYWxob3N0 - OjMwMDEvMTgxOTAwNDA1L215L2Fubm91bmNlbWVudC9yZWFkIiBkYXRhLXR1 - cmJvLXBlcm1hbmVudD0idHJ1ZSI+PC9kaXY+CiAgICAKCgoKPHNwYW4gZGF0 - YS1iZWhhdmlvcj0iZmxhc2hfbWVzc2FnZV9jb250YWluZXIiPgogIAo8L3Nw - YW4+CgogIAoKCiAgCgo8bmF2IGNsYXNzPSJyZWNvcmRpbmctYnJlYWRjcnVt - YnMgcmVjb3JkaW5nLWJyZWFkY3J1bWJzLS1tZXNzYWdlIGNlbnRlcmVkIHUt - cG9zaXRpb24tY29udGV4dCBhcHAtbW9iaWxlX19oaWRlIiBhcmlhLWxhYmVs - PSJicmVhZGNydW1iIiBkYXRhLWNvbnRyb2xsZXI9ImJyZWFkY3J1bWJzIiBk - YXRhLWFjdGlvbj0iY2xpY2stPmJyZWFkY3J1bWJzI25hdmlnYXRlQmFja09u - ZUxldmVsIiBkYXRhLWJyZWFkY3J1bWJzLXRhcmdldD0ibmF2YmFyIj4KICAg - IDxkaXYgY2xhc3M9InByb2plY3QtYmFkZ2VzIj4KICAgICAgCiAgICAgIAog - ICAgPC9kaXY+CgogIDxoMSBjbGFzcz0icmVjb3JkaW5nLWJyZWFkY3J1bWIi - IGRhdGEtYnJpZGdlLWhlYWRlcj4KICAgIAo8ZGl2IGNsYXNzPSJyZWNvcmRp - bmctYnJlYWRjcnVtYl9fdGl0bGUiIGRhdGEtYmVoYXZpb3I9ImV4cGFuZGFi - bGUiPgogIDxzcGFuIGNsYXNzPSJhLWZvci1zY3JlZW4tcmVhZGVyIj5xdWlj - ayBuYXY8L3NwYW4+CgogIDxzdHJvbmcgY2xhc3M9InUtcG9zaXRpb24tY29u - dGV4dCI+CgogICAgPGJ1dHRvbiBuYW1lPSJidXR0b24iIHR5cGU9InN1Ym1p - dCIgY2xhc3M9InJlY29yZGluZy1icmVhZGNydW1iX19qdW1wIiBhcmlhLWV4 - cGFuZGVkPSJmYWxzZSIgZGF0YS1icmVhZGNydW1icy10YXJnZXQ9Im9wZW5K - dW1wTWVudSIgZGF0YS1iZWhhdmlvcj0idG9nZ2xlX2V4cGFuc2lvbl9vbl9j - bGljayB0b2dnbGVfYnJlYWRjcnVtYnNfbWVudSI+CiAgICAgIDxzdmcgY2xh - c3M9InN2Zy1pY29uIHN2Zy1pY29uLS1ncmlkLXNvbGlkIiBhcmlhLWhpZGRl - bj0idHJ1ZSI+PHVzZSBocmVmPSIvYXNzZXRzL2ljb25zL3Nwcml0ZXMtNThk - YjAzZWE3Y2IzMGQzOTU1NmY4OWI3N2EzY2IzODg5ZTZkZjY2YTQ0ZmY3MjVj - YThkM2FmYWU3OTUxZjczNS5zdmcjZ3JpZC1zb2xpZCI+PC91c2U+PC9zdmc+ - CiAgICAgIDxzdmcgY2xhc3M9InN2Zy1pY29uIHN2Zy1pY29uLS1jaGV2cm9u - LWRvd24iIGFyaWEtaGlkZGVuPSJ0cnVlIj48dXNlIGhyZWY9Ii9hc3NldHMv - aWNvbnMvc3ByaXRlcy01OGRiMDNlYTdjYjMwZDM5NTU2Zjg5Yjc3YTNjYjM4 - ODllNmRmNjZhNDRmZjcyNWNhOGQzYWZhZTc5NTFmNzM1LnN2ZyNjaGV2cm9u - LWRvd24iPjwvdXNlPjwvc3ZnPgo8L2J1dHRvbj4gICAgPGEgY2xhc3M9ImRl - Y29yYXRlZCIgZGF0YS1icmVhZGNydW1icy10YXJnZXQ9ImxpbmsiIGhyZWY9 - Ii8xODE5MDA0MDUvcHJvamVjdHMvMTA0Mjk3OTI0NyI+VGVuLXllYXIgYW5u - aXZlcnNhcnk8L2E+CiAgPC9zdHJvbmc+CgogIDxzcGFuIGNsYXNzPSJyZWNv - cmRpbmctYnJlYWRjcnVtYl9fY2hpbGRyZW4iPgogICAgCiAgICAgIDxzcGFu - IGNsYXNzPSJyZWNvcmRpbmctYnJlYWRjcnVtYl9fc2VwYXJhdG9yIiBhcmlh - LWhpZGRlbj0idHJ1ZSI+IOKAuiA8L3NwYW4+PHNwYW4gY2xhc3M9InJlY29y - ZGluZy1icmVhZGNydW1iX19saW5rIj48YSBjbGFzcz0iZGVjb3JhdGVkIiBk - YXRhLWJyZWFkY3J1bWJzLXRhcmdldD0ibGluayIgaHJlZj0iLzE4MTkwMDQw - NS9idWNrZXRzLzEwNDI5NzkyNDcvbWVzc2FnZV9ib2FyZHMvMTIyMjAxNjY1 - Ij5NZXNzYWdlIEJvYXJkPC9hPjwvc3Bhbj4KCiAgPC9zcGFuPgoKICA8ZGl2 - IGNsYXNzPSJyZWNvcmRpbmctYnJlYWRjcnVtYl9fanVtcC1tZW51IHJlY29y - ZGluZy1icmVhZGNydW1iX19qdW1wLW1lbnUtLTktdXAgY2VudGVyZWQiIGRh - dGEtYmVoYXZpb3I9ImNvbGxhcHNlX29uX2NsaWNrb3V0c2lkZSIgZGF0YS1i - cmVhZGNydW1icy10YXJnZXQ9Imp1bXBNZW51Ij4KICAgICAgICA8YSBjbGFz - cz0icmVjb3JkaW5nLWJyZWFkY3J1bWJfX2p1bXAtYWN0aW9uIHJlY29yZGlu - Zy1icmVhZGNydW1iX19qdW1wLWFjdGlvbi0tbWVzc2FnZS1ib2FyZCIgaHJl - Zj0iLzE4MTkwMDQwNS9idWNrZXRzLzEwNDI5NzkyNDcvbWVzc2FnZV9ib2Fy - ZHMvMTIyMjAxNjY1Ij4KICAgICAgICAgIDxzcGFuIGNsYXNzPSJyZWNvcmRp - bmctYnJlYWRjcnVtYl9fanVtcC10aXRsZSB0eHQtLXRydW5jYXRlIj5NZXNz - YWdlIEJvYXJkPC9zcGFuPgogICAgICAgICAgPHNwYW4gY2xhc3M9InRvb2wt - aWNvbiByZWNvcmRpbmctYnJlYWRjcnVtYl9fanVtcC1pY29uIHRvb2wtaWNv - bi0tZmlsbGVkIiBzdHlsZT0iLS10b29sLWljb24tY29sb3I6IHZhcigtLWNv - bG9yLWJsdWUtNTApOyAiPjxzdmcgY2xhc3M9InN2Zy1pY29uIHN2Zy1pY29u - LS1tZWdhcGhvbmUtc29saWQiIGFyaWEtaGlkZGVuPSJ0cnVlIj48dXNlIGhy - ZWY9Ii9hc3NldHMvaWNvbnMvc3ByaXRlcy01OGRiMDNlYTdjYjMwZDM5NTU2 - Zjg5Yjc3YTNjYjM4ODllNmRmNjZhNDRmZjcyNWNhOGQzYWZhZTc5NTFmNzM1 - LnN2ZyNtZWdhcGhvbmUtc29saWQiPjwvdXNlPjwvc3ZnPjwvc3Bhbj4KPC9h - PiAgICAgICAgPGEgY2xhc3M9InJlY29yZGluZy1icmVhZGNydW1iX19qdW1w - LWFjdGlvbiByZWNvcmRpbmctYnJlYWRjcnVtYl9fanVtcC1hY3Rpb24tLWNo - YXQiIGhyZWY9Ii8xODE5MDA0MDUvYnVja2V0cy8xMDQyOTc5MjQ3L2NoYXRz - LzE2MjQ5NjM4MiI+CiAgICAgICAgICA8c3BhbiBjbGFzcz0icmVjb3JkaW5n - LWJyZWFkY3J1bWJfX2p1bXAtdGl0bGUgdHh0LS10cnVuY2F0ZSI+Q2hhdDwv - c3Bhbj4KICAgICAgICAgIDxzcGFuIGNsYXNzPSJ0b29sLWljb24gcmVjb3Jk - aW5nLWJyZWFkY3J1bWJfX2p1bXAtaWNvbiB0b29sLWljb24tLWZpbGxlZCIg - c3R5bGU9Ii0tdG9vbC1pY29uLWNvbG9yOiB2YXIoLS1jb2xvci1hcXVhLTUw - KTsgIj48c3ZnIGNsYXNzPSJzdmctaWNvbiBzdmctaWNvbi0tbWVzc2FnZXMt - c29saWQiIGFyaWEtaGlkZGVuPSJ0cnVlIj48dXNlIGhyZWY9Ii9hc3NldHMv - aWNvbnMvc3ByaXRlcy01OGRiMDNlYTdjYjMwZDM5NTU2Zjg5Yjc3YTNjYjM4 - ODllNmRmNjZhNDRmZjcyNWNhOGQzYWZhZTc5NTFmNzM1LnN2ZyNtZXNzYWdl - cy1zb2xpZCI+PC91c2U+PC9zdmc+PC9zcGFuPgo8L2E+ICAgICAgICA8YSBj - bGFzcz0icmVjb3JkaW5nLWJyZWFkY3J1bWJfX2p1bXAtYWN0aW9uIHJlY29y - ZGluZy1icmVhZGNydW1iX19qdW1wLWFjdGlvbi0tdG9kb3NldCIgaHJlZj0i - LzE4MTkwMDQwNS9idWNrZXRzLzEwNDI5NzkyNDcvdG9kb3NldHMvNDg3Njg3 - Mjg2Ij4KICAgICAgICAgIDxzcGFuIGNsYXNzPSJyZWNvcmRpbmctYnJlYWRj - cnVtYl9fanVtcC10aXRsZSB0eHQtLXRydW5jYXRlIj5Uby1kb3M8L3NwYW4+ - CiAgICAgICAgICA8c3BhbiBjbGFzcz0idG9vbC1pY29uIHJlY29yZGluZy1i - cmVhZGNydW1iX19qdW1wLWljb24gdG9vbC1pY29uLS1maWxsZWQiIHN0eWxl - PSItLXRvb2wtaWNvbi1jb2xvcjogdmFyKC0tY29sb3ItZ3JlZW4tNTApOyAi - PjxzdmcgY2xhc3M9InN2Zy1pY29uIHN2Zy1pY29uLS1jaGVjay1zb2xpZCIg - YXJpYS1oaWRkZW49InRydWUiPjx1c2UgaHJlZj0iL2Fzc2V0cy9pY29ucy9z - cHJpdGVzLTU4ZGIwM2VhN2NiMzBkMzk1NTZmODliNzdhM2NiMzg4OWU2ZGY2 - NmE0NGZmNzI1Y2E4ZDNhZmFlNzk1MWY3MzUuc3ZnI2NoZWNrLXNvbGlkIj48 - L3VzZT48L3N2Zz48L3NwYW4+CjwvYT4gICAgICAgIDxhIGNsYXNzPSJyZWNv - cmRpbmctYnJlYWRjcnVtYl9fanVtcC1hY3Rpb24gcmVjb3JkaW5nLWJyZWFk - Y3J1bWJfX2p1bXAtYWN0aW9uLS12YXVsdCIgaHJlZj0iLzE4MTkwMDQwNS9i - dWNrZXRzLzEwNDI5NzkyNDcvdmF1bHRzLzUzOTkzNTc4NSI+CiAgICAgICAg - ICA8c3BhbiBjbGFzcz0icmVjb3JkaW5nLWJyZWFkY3J1bWJfX2p1bXAtdGl0 - bGUgdHh0LS10cnVuY2F0ZSI+RG9jcyAmYW1wOyBGaWxlczwvc3Bhbj4KICAg - ICAgICAgIDxzcGFuIGNsYXNzPSJ0b29sLWljb24gcmVjb3JkaW5nLWJyZWFk - Y3J1bWJfX2p1bXAtaWNvbiB0b29sLWljb24tLWZpbGxlZCIgc3R5bGU9Ii0t - dG9vbC1pY29uLWNvbG9yOiB2YXIoLS1jb2xvci15ZWxsb3ctNTApOyAiPjxz - dmcgY2xhc3M9InN2Zy1pY29uIHN2Zy1pY29uLS1mb2xkZXItc29saWQiIGFy - aWEtaGlkZGVuPSJ0cnVlIj48dXNlIGhyZWY9Ii9hc3NldHMvaWNvbnMvc3By - aXRlcy01OGRiMDNlYTdjYjMwZDM5NTU2Zjg5Yjc3YTNjYjM4ODllNmRmNjZh - NDRmZjcyNWNhOGQzYWZhZTc5NTFmNzM1LnN2ZyNmb2xkZXItc29saWQiPjwv - dXNlPjwvc3ZnPjwvc3Bhbj4KPC9hPiAgICAgICAgPGEgY2xhc3M9InJlY29y - ZGluZy1icmVhZGNydW1iX19qdW1wLWFjdGlvbiByZWNvcmRpbmctYnJlYWRj - cnVtYl9fanVtcC1hY3Rpb24tLXF1ZXN0aW9ubmFpcmUiIGhyZWY9Ii8xODE5 - MDA0MDUvYnVja2V0cy8xMDQyOTc5MjQ3L3F1ZXN0aW9ubmFpcmVzLzczMTM3 - ODkyNSI+CiAgICAgICAgICA8c3BhbiBjbGFzcz0icmVjb3JkaW5nLWJyZWFk - Y3J1bWJfX2p1bXAtdGl0bGUgdHh0LS10cnVuY2F0ZSI+Q2hlY2staW5zPC9z - cGFuPgogICAgICAgICAgPHNwYW4gY2xhc3M9InRvb2wtaWNvbiByZWNvcmRp - bmctYnJlYWRjcnVtYl9fanVtcC1pY29uIHRvb2wtaWNvbi0tZmlsbGVkIiBz - dHlsZT0iLS10b29sLWljb24tY29sb3I6IHZhcigtLWNvbG9yLXZpb2xldC01 - MCk7ICI+PHN2ZyBjbGFzcz0ic3ZnLWljb24gc3ZnLWljb24tLXF1ZXN0aW9u - IiBhcmlhLWhpZGRlbj0idHJ1ZSI+PHVzZSBocmVmPSIvYXNzZXRzL2ljb25z - L3Nwcml0ZXMtNThkYjAzZWE3Y2IzMGQzOTU1NmY4OWI3N2EzY2IzODg5ZTZk - ZjY2YTQ0ZmY3MjVjYThkM2FmYWU3OTUxZjczNS5zdmcjcXVlc3Rpb24iPjwv - dXNlPjwvc3ZnPjwvc3Bhbj4KPC9hPiAgICAgICAgPGEgY2xhc3M9InJlY29y - ZGluZy1icmVhZGNydW1iX19qdW1wLWFjdGlvbiByZWNvcmRpbmctYnJlYWRj - cnVtYl9fanVtcC1hY3Rpb24tLXNjaGVkdWxlIiBocmVmPSIvMTgxOTAwNDA1 - L2J1Y2tldHMvMTA0Mjk3OTI0Ny9zY2hlZHVsZXMvNzE0NTg1NzE1Ij4KICAg - ICAgICAgIDxzcGFuIGNsYXNzPSJyZWNvcmRpbmctYnJlYWRjcnVtYl9fanVt - cC10aXRsZSB0eHQtLXRydW5jYXRlIj5TY2hlZHVsZTwvc3Bhbj4KICAgICAg - ICAgIDxzcGFuIGNsYXNzPSJ0b29sLWljb24gcmVjb3JkaW5nLWJyZWFkY3J1 - bWJfX2p1bXAtaWNvbiB0b29sLWljb24tLWZpbGxlZCIgc3R5bGU9Ii0tdG9v - bC1pY29uLWNvbG9yOiB2YXIoLS1jb2xvci1waW5rLTUwKTsgIj48c3ZnIGNs - YXNzPSJzdmctaWNvbiBzdmctaWNvbi0tY2FsZW5kYXItc29saWQiIGFyaWEt - aGlkZGVuPSJ0cnVlIj48dXNlIGhyZWY9Ii9hc3NldHMvaWNvbnMvc3ByaXRl - cy01OGRiMDNlYTdjYjMwZDM5NTU2Zjg5Yjc3YTNjYjM4ODllNmRmNjZhNDRm - ZjcyNWNhOGQzYWZhZTc5NTFmNzM1LnN2ZyNjYWxlbmRhci1zb2xpZCI+PC91 - c2U+PC9zdmc+PC9zcGFuPgo8L2E+ICAgICAgICA8YSBjbGFzcz0icmVjb3Jk - aW5nLWJyZWFkY3J1bWJfX2p1bXAtYWN0aW9uIHJlY29yZGluZy1icmVhZGNy - dW1iX19qdW1wLWFjdGlvbi0taW5ib3giIGhyZWY9Ii8xODE5MDA0MDUvYnVj - a2V0cy8xMDQyOTc5MjQ3L2luYm94ZXMvNTU0NjYzNDg3Ij4KICAgICAgICAg - IDxzcGFuIGNsYXNzPSJyZWNvcmRpbmctYnJlYWRjcnVtYl9fanVtcC10aXRs - ZSB0eHQtLXRydW5jYXRlIj5Gb3J3YXJkczwvc3Bhbj4KICAgICAgICAgIDxz - cGFuIGNsYXNzPSJ0b29sLWljb24gcmVjb3JkaW5nLWJyZWFkY3J1bWJfX2p1 - bXAtaWNvbiB0b29sLWljb24tLWZpbGxlZCIgc3R5bGU9Ii0tdG9vbC1pY29u - LWNvbG9yOiB2YXIoLS1jb2xvci1wdXJwbGUtNTApOyAiPjxzdmcgY2xhc3M9 - InN2Zy1pY29uIHN2Zy1pY29uLS1lbWFpbC1zb2xpZCIgYXJpYS1oaWRkZW49 - InRydWUiPjx1c2UgaHJlZj0iL2Fzc2V0cy9pY29ucy9zcHJpdGVzLTU4ZGIw - M2VhN2NiMzBkMzk1NTZmODliNzdhM2NiMzg4OWU2ZGY2NmE0NGZmNzI1Y2E4 - ZDNhZmFlNzk1MWY3MzUuc3ZnI2VtYWlsLXNvbGlkIj48L3VzZT48L3N2Zz48 - L3NwYW4+CjwvYT4gICAgICAgIDxhIHRhcmdldD0iX2JsYW5rIiBjbGFzcz0i - cmVjb3JkaW5nLWJyZWFkY3J1bWJfX2p1bXAtYWN0aW9uIHJlY29yZGluZy1i - cmVhZGNydW1iX19qdW1wLWRvb3IiIGhyZWY9Imh0dHBzOi8vZ2l0aHViLmNv - bS9iYXNlY2FtcCI+CiAgICAgICAgICA8c3BhbiBjbGFzcz0icmVjb3JkaW5n - LWJyZWFkY3J1bWJfX2p1bXAtdGl0bGUgdHh0LS10cnVuY2F0ZSI+U291cmNl - IENvZGVzPC9zcGFuPgogICAgICAgICAgPGRpdiBjbGFzcz0iYXBwX2ljb24g - YXBwX2ljb24tLWJyZWFkY3J1bWIiPgogICAgICAgICAgICA8aW1nIGNsYXNz - PSJhcHBfaWNvbiIgc3JjPSIvL2JjMy1jZG4ubG9jYWxob3N0OjMwMDEvYXNz - ZXRzL2ljb25zL2FwcF9pY29ucy9naXRodWItMzUzNjM0MzIzMWYyYWQ1NWM3 - ZmRjY2I2ZjAwMzQ0MGZkMGE3ZTc3NWRmYTUxYmQxNTUyNWUxNWJjNjlhNmQy - Yy5wbmciIC8+CiAgICAgICAgICA8L2Rpdj4KPC9hPiAgICAgICAgPGEgY2xh - c3M9InJlY29yZGluZy1icmVhZGNydW1iX19qdW1wLWFjdGlvbiByZWNvcmRp - bmctYnJlYWRjcnVtYl9fanVtcC1hY3Rpb24tLWthbmJhbi1ib2FyZCIgaHJl - Zj0iLzE4MTkwMDQwNS9idWNrZXRzLzEwNDI5NzkyNDcvY2FyZF90YWJsZXMv - NDE2ODg3NDgzIj4KICAgICAgICAgIDxzcGFuIGNsYXNzPSJyZWNvcmRpbmct - YnJlYWRjcnVtYl9fanVtcC10aXRsZSB0eHQtLXRydW5jYXRlIj5QbGFubmlu - Zzwvc3Bhbj4KICAgICAgICAgIDxzcGFuIGNsYXNzPSJ0b29sLWljb24gcmVj - b3JkaW5nLWJyZWFkY3J1bWJfX2p1bXAtaWNvbiB0b29sLWljb24tLWZpbGxl - ZCIgc3R5bGU9Ii0tdG9vbC1pY29uLWNvbG9yOiB2YXIoLS1jb2xvci1vcmFu - Z2UtNTApOyAiPjxzdmcgY2xhc3M9InN2Zy1pY29uIHN2Zy1pY29uLS1jYXJk - LXRhYmxlLXNvbGlkIiBhcmlhLWhpZGRlbj0idHJ1ZSI+PHVzZSBocmVmPSIv - YXNzZXRzL2ljb25zL3Nwcml0ZXMtNThkYjAzZWE3Y2IzMGQzOTU1NmY4OWI3 - N2EzY2IzODg5ZTZkZjY2YTQ0ZmY3MjVjYThkM2FmYWU3OTUxZjczNS5zdmcj - Y2FyZC10YWJsZS1zb2xpZCI+PC91c2U+PC9zdmc+PC9zcGFuPgo8L2E+ICA8 - L2Rpdj4KPC9kaXY+CiAgPC9oMT4KPC9uYXY+CgoKPGRpdiBjbGFzcz0icGFu - ZWwgcGFuZWwtLXBlcm1hIiBkYXRhLWNvbnRyb2xsZXI9InN1YnNjcmlwdGlv - bnMtdmlld2VyICIgZGF0YS1jcmVhdG9yLWlkPSI1MjU0NjAyMDciPjxiYy1t - b2RhbCBvcGVuZWQ9ImZhbHNlIiBkYXRhLXN1YnNjcmlwdGlvbnMtdmlld2Vy - LXRhcmdldD0ic3Vic2NyaXB0aW9uc01vZGFsIiBkYXRhLWFjdGlvbj0iY2xv - c2UtJmd0O3N1YnNjcmlwdGlvbnMtdmlld2VyI3VucG9wdWxhdGVNb2RhbEVs - ZW1lbnQiPjwvYmMtbW9kYWw+CiAgPGRpdiBjbGFzcz0icHJvamVjdC1iYWRn - ZXMiPgogICAgCiAgPC9kaXY+CgogIAogIAogIDxkaXYgY2xhc3M9InBhbmVs - X19yaWdodC1hY3Rpb25zIj4KICAgIDxhIGNsYXNzPSJidG4gYnRuLS1zbWFs - bCBidG4taWNvbi0tbGVmdCBhcHAtbW9iaWxlX19oaWRlIHotMSIgZGF0YS12 - aXNpYmxlLXRvLWNyZWF0b3Itb3ItYWRtaW49IiIgaHJlZj0iLzE4MTkwMDQw - NS9idWNrZXRzLzEwNDI5NzkyNDcvbWVzc2FnZXMvNzgzNTI2MTAxL2VkaXQi - PkVkaXQ8L2E+CiAgPC9kaXY+CgoKPGFzaWRlIGNsYXNzPSJwZXJtYS10b29s - YmFyIGFwcC1tb2JpbGVfX2hpZGUiIGRhdGEtY3JlYXRvci1pZD0iNTI1NDYw - MjA3Ij4KICAKCjxzcGFuIGNsYXNzPSJhY3Rpb24tc2hlZXQiIGRhdGEtYmVo - YXZpb3I9ImV4cGFuZGFibGUgY29sbGFwc2Vfb25fY2xpY2siIGRhdGEtY29u - dHJvbGxlcj0iZGVza3RvcC1tb2RhbCI+CiAgPGJ1dHRvbiBuYW1lPSJidXR0 - b24iIHR5cGU9ImJ1dHRvbiIgdGl0bGU9IlNob3cgb3B0aW9uc+KApiIgYXJp - YS1sYWJlbD0iT3B0aW9ucyBtZW51IiBjbGFzcz0iYWN0aW9uLXNoZWV0X19l - eHBhbnNpb24tdG9nZ2xlIGJ0biBidG4tLWljb24gYnRuLS1vdmVyZmxvdy1p - Y29uIGJ0bi0tc21hbGwgIiBkYXRhLWJyaWRnZS1vcHRpb25zLW1lbnU9IiIg - ZGF0YS1iZWhhdmlvcj0idG9nZ2xlX2V4cGFuc2lvbl9vbl9jbGljayB0b2dn - bGVfZmlsZV9tZW51Ij5GaWxl4oCmPC9idXR0b24+CgogIDxkaXYgY2xhc3M9 - ImFjdGlvbi1zaGVldF9fY29udGVudCIgZGF0YS1iZWhhdmlvcj0iY29sbGFw - c2Vfb25fY2xpY2tvdXRzaWRlIj4KICAgIDxidXR0b24gbmFtZT0iYnV0dG9u - IiB0eXBlPSJzdWJtaXQiIGFyaWEtbGFiZWw9IkNsb3NlIG1lbnUiIGNsYXNz - PSJhY3Rpb24tc2hlZXRfX2Nsb3NlIGJ0biBidG4tLXNtYWxsIGJ0bi0taWNv - biBidG4tLWNsb3NlLXNoZWV0LWljb24gIiBkYXRhLWJlaGF2aW9yPSJjb2xs - YXBzZV9vbl9jbGljayI+Q2xvc2U8L2J1dHRvbj4KCiAgICA8YSBjbGFzcz0i - YWN0aW9uLXNoZWV0X19hY3Rpb24gYWN0aW9uLXNoZWV0X19hY3Rpb24tLWVk - aXQiIGRhdGEtYnJpZGdlLW1lbnUtYWN0aW9uPSJ0cnVlIiBkYXRhLWJyaWRn - ZS1hY3Rpb24tdHlwZT0iZWRpdCIgZGF0YS12aXNpYmxlLXRvLWNyZWF0b3It - b3ItYWRtaW49ImZhbHNlIiBocmVmPSIvMTgxOTAwNDA1L2J1Y2tldHMvMTA0 - Mjk3OTI0Ny9tZXNzYWdlcy83ODM1MjYxMDEvZWRpdCI+RWRpdDwvYT4KCiAg - ICAgICAgPGEgY2xhc3M9ImFjdGlvbi1zaGVldF9fYWN0aW9uIGFjdGlvbi1z - aGVldF9fYWN0aW9uLS1tb3ZlIiBkYXRhLWJyaWRnZS1tZW51LWFjdGlvbj0i - dHJ1ZSIgZGF0YS1icmlkZ2UtYWN0aW9uLXR5cGU9Im1vdmUiIGRhdGEtYWN0 - aW9uPSJkZXNrdG9wLW1vZGFsI29wZW5Nb2RhbCIgZGF0YS1iZWhhdmlvcj0i - Y29sbGFwc2Vfb25fY2xpY2siIGhyZWY9Ii8xODE5MDA0MDUvYnVja2V0cy8x - MDQyOTc5MjQ3L3JlY29yZGluZ3MvNzgzNTI2MTAxL21vdmVzL25ldyI+TW92 - ZTwvYT4KCiAgICA8YSBjbGFzcz0iYWN0aW9uLXNoZWV0X19hY3Rpb24gYWN0 - aW9uLXNoZWV0X19hY3Rpb24tLWNvcHkiIGRhdGEtYnJpZGdlLW1lbnUtYWN0 - aW9uPSJ0cnVlIiBkYXRhLWJyaWRnZS1hY3Rpb24tdHlwZT0iY29weSIgZGF0 - YS1hY3Rpb249ImRlc2t0b3AtbW9kYWwjb3Blbk1vZGFsIiBkYXRhLWJlaGF2 - aW9yPSJjb2xsYXBzZV9vbl9jbGljayIgaHJlZj0iLzE4MTkwMDQwNS9idWNr - ZXRzLzEwNDI5NzkyNDcvcmVjb3JkaW5ncy83ODM1MjYxMDEvY29waWVzL25l - dyI+Q29weTwvYT4KCiAgICAKCgogICAgICAgIDxhIGNsYXNzPSJhY3Rpb24t - c2hlZXRfX2FjdGlvbiBhY3Rpb24tc2hlZXRfX2FjdGlvbi0tYXJjaGl2ZSIg - ZGF0YS1icmlkZ2UtbWVudS1hY3Rpb249InRydWUiIGRhdGEtYnJpZGdlLWFj - dGlvbi10eXBlPSJhcmNoaXZlIiBkYXRhLWRpc2FibGUtd2l0aD0iQXJjaGl2 - aW5n4oCmIiBkYXRhLXR1cmJvLXByZWZldGNoPSJmYWxzZSIgZGF0YS1yZW1v - dGU9InRydWUiIHJlbD0ibm9mb2xsb3ciIGRhdGEtbWV0aG9kPSJQVVQiIGhy - ZWY9Ii8xODE5MDA0MDUvYnVja2V0cy8xMDQyOTc5MjQ3L3JlY29yZGluZ3Mv - NzgzNTI2MTAxL3N0YXR1cy9hcmNoaXZlZCI+QXJjaGl2ZTwvYT4KCiAgICAg - ICAgPGEgY2xhc3M9ImFjdGlvbi1zaGVldF9fYWN0aW9uIGFjdGlvbi1zaGVl - dF9fYWN0aW9uLS10cmFzaCIgZGF0YS1icmlkZ2UtbWVudS1hY3Rpb249InRy - dWUiIGRhdGEtYnJpZGdlLWFjdGlvbi10eXBlPSJkZWxldGUiIGRhdGEtZGlz - YWJsZS13aXRoPSJNb3ZpbmcgdG8gdGhlIHRyYXNo4oCmIiBkYXRhLXR1cmJv - LXByZWZldGNoPSJmYWxzZSIgZGF0YS1yZW1vdGU9InRydWUiIHJlbD0ibm9m - b2xsb3ciIGRhdGEtbWV0aG9kPSJQVVQiIGhyZWY9Ii8xODE5MDA0MDUvYnVj - a2V0cy8xMDQyOTc5MjQ3L3JlY29yZGluZ3MvNzgzNTI2MTAxL3N0YXR1cy90 - cmFzaGVkIj5QdXQgaW4gdGhlIHRyYXNoPC9hPgoKICAgIDxhIHRpdGxlPSJB - ZGQvcmVtb3ZlIGJvb2ttYXJr4oCmIiBkYXRhLWNvbnRyb2xsZXI9ImJvb2tt - YXJrcyIgZGF0YS1hY3Rpb249ImJvb2ttYXJrcyN0b2dnbGVCb29rbWFyayIg - ZGF0YS1ib29rbWFya3MtdXJsPSIvMTgxOTAwNDA1L215L2Jvb2ttYXJrcy9C - QWg3QmtraUMxOXlZV2xzY3dZNkJrVlVld2RKSWdsa1lYUmhCanNBVkVraUxX - ZHBaRG92TDJKak15OVNaV052Y21ScGJtY3ZOemd6TlRJMk1UQXhQMlY0Y0ds - eVpYTmZhVzRHT3dCVVNTSUljSFZ5QmpzQVZFa2lEWEpsWVdSaFlteGxCanNB - VkE9PS0tNGZlOTg0NzM1MzUwMDBlYjY5Zjc5Y2Q2YzZkNTRhMDdiNjdhMWZm - OSIgZGF0YS1ib29rbWFya3MtYm9va21hcmtlZC1jbGFzcz0iYnRuLS1ib29r - bWFya2VkLWljb24iIGRhdGEtYnJpZGdlLWFjdGlvbi10eXBlPSJib29rbWFy - ayIgZGF0YS10dXJiby1wZXJtYW5lbnQ9InRydWUiIGRhdGEtYnJpZGdlLW1l - bnUtYWN0aW9uPSJ0cnVlIiBjbGFzcz0iYWN0aW9uLXNoZWV0X19hY3Rpb24g - YWN0aW9uLXNoZWV0X19hY3Rpb24tLWJvb2ttYXJrIiBpZD0idG9nZ2xlX2Jv - b2ttYXJrXzc4MzUyNjEwMSIgaHJlZj0iIyI+Qm9va21hcms8L2E+CiAgICAK - ICAgIDxhIGNsYXNzPSJhY3Rpb24tc2hlZXRfX2FjdGlvbiBhY3Rpb24tc2hl - ZXRfX2FjdGlvbi0tY29tbWVudHMtb2ZmIiBkYXRhLWJyaWRnZS1tZW51LWFj - dGlvbj0idHJ1ZSIgZGF0YS1yZW1vdGU9InRydWUiIGRhdGEtbWV0aG9kPSJw - b3N0IiBkYXRhLWJyaWRnZS1hY3Rpb24tdHlwZT0iY29tbWVudHMtb2ZmIiBk - YXRhLXZpc2libGUtdG8tY3JlYXRvci1vci1hZG1pbj0idHJ1ZSIgZGF0YS10 - dXJiby1wcmVmZXRjaD0iZmFsc2UiIGhyZWY9Ii8xODE5MDA0MDUvYnVja2V0 - cy8xMDQyOTc5MjQ3L3JlY29yZGluZ3MvNzgzNTI2MTAxL2NvbW1lbnRzX2Ns - b3N1cmUiPkNsb3NlIGNvbW1lbnRzPC9hPgogICAgPGEgY2xhc3M9ImFjdGlv - bi1zaGVldF9fYWN0aW9uIGFjdGlvbi1zaGVldF9fYWN0aW9uLS1tZW1vcnki - IGRhdGEtYnJpZGdlLW1lbnUtYWN0aW9uPSJ0cnVlIiBkYXRhLWNvbnRyb2xs - ZXI9Im1lbW9yeSIgZGF0YS1icmlkZ2UtYWN0aW9uLXR5cGU9Im1lbW9yeSIg - ZGF0YS1tZW1vcnktdXJsLXZhbHVlPSIvMTgxOTAwNDA1L2J1Y2tldHMvMTA0 - Mjk3OTI0Ny9yZWNvcmRpbmdzLzc4MzUyNjEwMS9tZW1vcnkiIGRhdGEtbWVt - b3J5LWlkZW50aWZpZXItdmFsdWU9IloybGtPaTh2WW1NekwxSmxZMjl5Wkds - dVp5ODNPRE0xTWpZeE1ERSIgZGF0YS1tZW1vcnktbWVtb3JpemVkLXZhbHVl - PSJmYWxzZSIgZGF0YS1tZW1vcnktYWRkLWxhYmVsLXZhbHVlPSJEb27igJl0 - IEZvcmdldCIgZGF0YS1tZW1vcnktcmVtb3ZlLWxhYmVsLXZhbHVlPSJSZW1v - dmUgZnJvbSBEb27igJl0IEZvcmdldCIgZGF0YS12aXNpYmxlLXRvLWNyZWF0 - b3Itb3ItYWRtaW49ImZhbHNlIiBkYXRhLWFjdGlvbj0ibWVtb3J5I3RvZ2ds - ZSIgdGl0bGU9IkRvbuKAmXQgRm9yZ2V0IiBocmVmPSIjIj5Eb27igJl0IEZv - cmdldDwvYT4KICAgIDxmb3JtIGNsYXNzPSJhY3Rpb24tc2hlZXRfX2FjdGlv - bi0tYnV0dG9uIiBtZXRob2Q9InBvc3QiIGFjdGlvbj0iLzE4MTkwMDQwNS9i - dWNrZXRzLzEwNDI5NzkyNDcvcmVjb3JkaW5ncy83ODM1MjYxMDEvcGluIj48 - aW5wdXQgdHlwZT0iaGlkZGVuIiBuYW1lPSJfbWV0aG9kIiB2YWx1ZT0iZGVs - ZXRlIiBhdXRvY29tcGxldGU9Im9mZiIgLz48YnV0dG9uIGNsYXNzPSJhY3Rp - b24tc2hlZXRfX2FjdGlvbiBhY3Rpb24tc2hlZXRfX2FjdGlvbl9fYnV0dG9u - IGFjdGlvbi1zaGVldF9fYWN0aW9uLS1waW4iIGRhdGEtYnJpZGdlLW1lbnUt - YWN0aW9uPSJ0cnVlIiBkYXRhLWJyaWRnZS1hY3Rpb24tdHlwZT0icGluIiBk - YXRhLXBpbm5lZD0idHJ1ZSIgdGl0bGU9IlVucGluIiB0eXBlPSJzdWJtaXQi - PlVucGluPC9idXR0b24+PGlucHV0IHR5cGU9ImhpZGRlbiIgbmFtZT0iYXV0 - aGVudGljaXR5X3Rva2VuIiB2YWx1ZT0ielE1QlJDcnJVaWdXMFBoRjhUN3JM - NElRY3RYc1VocXczeGxLUHhFTWhYTmFJbUJyTkFzUjdVN2RaY0JER2pzRk9y - ZXJWZmw5SEZud2h5TlZlNWUzSVEiIGF1dG9jb21wbGV0ZT0ib2ZmIiAvPjwv - Zm9ybT4KCiAgICAgIDxhIGNsYXNzPSJhY3Rpb24tc2hlZXRfX2FjdGlvbiBh - Y3Rpb24tc2hlZXRfX2FjdGlvbi0tZXhwb3J0IiBkYXRhLWJlaGF2aW9yPSJl - eHBvcnQiIGhyZWY9Ii8xODE5MDA0MDUvYnVja2V0cy8xMDQyOTc5MjQ3L3Jl - Y29yZGluZ3MvNzgzNTI2MTAxL2F0dGFjaG1lbnRfZXhwb3J0cyI+RG93bmxv - YWQgYWxsIGF0dGFjaG1lbnRzPC9hPgoKICAgIAoKICAgICAgPGg1IGNsYXNz - PSJhY3Rpb24tc2hlZXRfX2JyZWFrIHB1c2hfaGFsZi0tdG9wIHB1c2hfcXVh - cnRlci0tYm90dG9tIj5TaGFyZTwvaDU+CgogICAgICA8YSBjbGFzcz0iYWN0 - aW9uLXNoZWV0X19hY3Rpb24gYWN0aW9uLXNoZWV0X19hY3Rpb24tLWxpbmsi - IGRhdGEtYnJpZGdlLW1lbnUtYWN0aW9uPSJ0cnVlIiBkYXRhLWJyaWRnZS1h - Y3Rpb24tdHlwZT0ibGluayIgdGl0bGU9IkdldCBhIHB1YmxpYyBsaW5rIiBo - cmVmPSIvMTgxOTAwNDA1L2J1Y2tldHMvMTA0Mjk3OTI0Ny9yZWNvcmRpbmdz - Lzc4MzUyNjEwMS9wdWJsaWNhdGlvbiI+R2V0IGEgcHVibGljIGxpbms8L2E+ - CiAgICAgIDxhIGNsYXNzPSJhY3Rpb24tc2hlZXRfX2FjdGlvbiBhY3Rpb24t - c2hlZXRfX2FjdGlvbi0tc2hhcmUiIGRhdGEtYnJpZGdlLW1lbnUtYWN0aW9u - PSJ0cnVlIiBkYXRhLWJyaWRnZS1hY3Rpb24tdHlwZT0ibm90aWZ5IiB0aXRs - ZT0iTm90aWZ5IHNvbWVvbmUiIGhyZWY9Ii8xODE5MDA0MDUvYnVja2V0cy8x - MDQyOTc5MjQ3L3JlY29yZGluZ3MvNzgzNTI2MTAxL25vdGlmaWNhdGlvbnMv - bmV3Ij5TZW5kIHRoaXMgdG8gc29tZW9uZTwvYT4KCiAgICAgIAoKICAgIAoK - ICAgICAgPGg1IGNsYXNzPSJhY3Rpb24tc2hlZXRfX2JyZWFrIHB1c2hfaGFs - Zi0tdG9wIHB1c2hfcXVhcnRlci0tYm90dG9tIj5IaXN0b3J5PC9oNT4KCiAg - ICAgIDxhIGNsYXNzPSJhY3Rpb24tc2hlZXRfX2FjdGlvbiBhY3Rpb24tc2hl - ZXRfX2FjdGlvbi0taGlzdG9yeSIgZGF0YS1icmlkZ2UtbWVudS1hY3Rpb249 - InRydWUiIGRhdGEtYnJpZGdlLWFjdGlvbi10eXBlPSJoaXN0b3J5IiBocmVm - PSIvMTgxOTAwNDA1L2J1Y2tldHMvMTA0Mjk3OTI0Ny9yZWNvcmRpbmdzLzc4 - MzUyNjEwMS9ldmVudHMiPlZpZXcgY2hhbmdlIGxvZzwvYT4KCiAgICAgICAg - PHNwYW4gY2xhc3M9InUtaGlkZS1vbi10ZW1wbGF0ZSI+CiAgICA8YSBocmVm - PSIjIiBjbGFzcz0iYWN0aW9uLXNoZWV0X19hY3Rpb24gYWN0aW9uLXNoZWV0 - X19hY3Rpb24tLXBlb3BsZSIgZGF0YS1hY3Rpb249InN1YnNjcmlwdGlvbnMt - dmlld2VyI29wZW5TdWJzY3JpcHRpb25zTW9kYWwiIGRhdGEtdXJsPSIvMTgx - OTAwNDA1L2J1Y2tldHMvMTA0Mjk3OTI0Ny9yZWNvcmRpbmdzLzc4MzUyNjEw - MS9ub3RpZmllZF9yZWNpcGllbnRzIj4KICAgICAgTm90aWZpZWQKICAgICAg - MSBwZXJzb24KICAgIDwvYT4KPC9zcGFuPgoKCiAgICAKICA8L2Rpdj4KCiAg - PGJjLW1vZGFsIG9wZW5lZD0iZmFsc2UiCiAgICBkYXRhLWRlc2t0b3AtbW9k - YWwtdGFyZ2V0PSJtb2RhbCIKICAgIGRhdGEtYWN0aW9uPSJjbG9zZS0+ZGVz - a3RvcC1tb2RhbCNyZXNldE1vZGFsIgogICAgY2xvc2Utb24tc3VibWl0PSJm - YWxzZSIKICAgIGNsb3NlLW9uLWNsaWNrLW91dHNpZGU9ImZhbHNlIj4KICA8 - L2JjLW1vZGFsPgo8L3NwYW4+Cgo8L2FzaWRlPgoKCiAgCgoKICAKICA8YXJ0 - aWNsZSBjbGFzcz0ibWVzc2FnZSByZWNvcmRhYmxlIHJlY29yZGluZyIgZGF0 - YS1yZWNvcmRpbmctaWQ9Ijc4MzUyNjEwMSIgZGF0YS1jcmVhdG9yLWlkPSI1 - MjU0NjAyMDciIGRhdGEtdXJsPSIvMTgxOTAwNDA1L2J1Y2tldHMvMTA0Mjk3 - OTI0Ny9tZXNzYWdlcy83ODM1MjYxMDEiIGRhdGEtcmVhZGFibGUtaWRlbnRp - Zmllcj0iWjJsa09pOHZZbU16TDFKbFkyOXlaR2x1Wnk4M09ETTFNall4TURF - IiBpZD0icmVjb3JkaW5nXzc4MzUyNjEwMSI+PHNwYW4gY2xhc3M9ImFuY2hv - ciByZWNvcmRhYmxlX19hbmNob3IiIGlkPSJfX3JlY29yZGluZ183ODM1MjYx - MDEiPjwvc3Bhbj4KICAgIDxoZWFkZXIgY2xhc3M9Im1lc3NhZ2VfX2hlYWRl - ciI+CiAgICAgICAgPGRpdiBjbGFzcz0ibWVzc2FnZV9faW50cm8gbWVzc2Fn - ZV9faW50cm8tLWNhdGVnb3J5Ij7wn5OiIEFubm91bmNlbWVudDwvZGl2Pgog - ICAgICA8aDEgY2xhc3M9Im1lc3NhZ2VfX3N1YmplY3QgZmx1c2gtLXRvcCBw - dXNoX3F1YXJ0ZXItLWJvdHRvbSI+CiAgICAgICAgPHR1cmJvLWZyYW1lIGNs - YXNzPSJpbmxpbmUtZWRpdC1mb3JtIiBpZD0icmVjb3JkaW5nX3RpdGxlX3Jl - Y29yZGluZ183ODM1MjYxMDEiPjxhIHJvbGU9ImJ1dHRvbiIgZGF0YS1iZWhh - dmlvcj0icHJldmVudF9saW5rX2FjdGlvbiIgZGF0YS10dXJiby1tZXRob2Q9 - ImdldCIgYXJpYS1kZXNjcmlwdGlvbj0iUmVuYW1lIHRoaXMgbWVzc2FnZS4i - IGhyZWY9Ii8xODE5MDA0MDUvYnVja2V0cy8xMDQyOTc5MjQ3L3JlY29yZGlu - Z3MvNzgzNTI2MTAxL3RpdGxlL2VkaXQiPkEgZGVjYWRlITwvYT48L3R1cmJv - LWZyYW1lPgogICAgICA8L2gxPgoKICAgICAgPGRpdiBjbGFzcz0ibWVzc2Fn - ZV9fYXR0cmlidXRpb24iPgogICAgICAgIDxkaXYgY2xhc3M9Im1lc3NhZ2Vf - X21ldGEiPgogICAgICAgICAgPGZpZ3VyZSBjbGFzcz0ibWVzc2FnZV9fYXZh - dGFyIj4KICAgICAgICAgICAgPGltZyBjbGFzcz0iYXZhdGFyIG1lc3NhZ2Vf - X2F2YXRhci1pbWFnZSIgZGF0YS1iZWhhdmlvcj0icmljaF9hdmF0YXIiIGRh - dGEtcmljaC1hdmF0YXItdXJsPSIvMTgxOTAwNDA1L3Blb3BsZS81MjU0NjAy - MDcvcmljaF9hdmF0YXIiIGRhdGEtYXZhdGFyLWZvci1wZXJzb24taWQ9IjUy - NTQ2MDIwNyIgYWx0PSJEYXZpZCBIZWluZW1laWVyIEhhbnNzb24iIHRpdGxl - PSJEYXZpZCBIZWluZW1laWVyIEhhbnNzb24sIEJhc2VjYW1wIiBzcmM9Imh0 - dHA6Ly9iYzMtY2RuLmxvY2FsaG9zdDozMDAxLzE4MTkwMDQwNS9wZW9wbGUv - QkFocEJPJTJGaVVSOD0tLWExNDMwOGVmOGIxMDFhNjYxN2EzNDVlNDRlZmEw - ZTM1NWVjZDA0MmUvYXZhdGFyP3Y9MSIgd2lkdGg9IjEzMCIgaGVpZ2h0PSIx - MzAiIC8+CiAgICAgICAgICA8L2ZpZ3VyZT4KICAgICAgICAgIDxkaXY+CiAg - ICAgICAgICAgIERhdmlkIEhlaW5lbWVpZXIgSGFuc3NvbgogICAgICAgICAg - ICAKICAgICAgICAgIDwvZGl2PgogICAgICAgIDwvZGl2PgoKICAgICAgICAg - IDxkaXYgY2xhc3M9Im1lc3NhZ2VfX21ldGEgdS1oaWRlLW9uLXRlbXBsYXRl - Ij4KICAgICAgICAgICAgPHRpbWUgZGF0ZXRpbWU9IjIwMjUtMDgtMDhUMTc6 - MjY6NTZaIiBkYXRhLWxvY2FsPSJkYXRlIj5BdWd1c3QgIDgsIDIwMjUgIDU6 - MjZwbTwvdGltZT4KICAgICAgICAgIDwvZGl2PgoKICAgICAgICAgIDxkaXYg - Y2xhc3M9Im1lc3NhZ2VfX21ldGEiPgogICAgICAgICAgICAgIDxhIGhyZWY9 - IiMiIGRhdGEtYWN0aW9uPSJzdWJzY3JpcHRpb25zLXZpZXdlciNvcGVuU3Vi - c2NyaXB0aW9uc01vZGFsIiBkYXRhLXVybD0iLzE4MTkwMDQwNS9idWNrZXRz - LzEwNDI5NzkyNDcvcmVjb3JkaW5ncy83ODM1MjYxMDEvbm90aWZpZWRfcmVj - aXBpZW50cyI+CiAgICBOb3RpZmllZCAxIHBlcnNvbgogIDwvYT4KCiAgICAg - ICAgICA8L2Rpdj4KICAgICAgPC9kaXY+CiAgICA8L2hlYWRlcj4KCiAgICA8 - c2VjdGlvbiBjbGFzcz0icHVzaF9kb3VibGUtLXRvcCI+CiAgICAgIDxkaXYg - Y2xhc3M9ImZvcm1hdHRlZF9jb250ZW50IGZvcm1hdHRlZF9jb250ZW50LS1s - YXJnZSI+CiAgICAgICAgV2hhdCBmdW4sIGRvbid0IHlvdSB0aGluayBASkY/ - CiAgICAgIDwvZGl2PgogICAgPC9zZWN0aW9uPgoKICAgICAgPGRpdiBjbGFz - cz0icHVzaC0tdG9wIHUtaGlkZS1vbi10ZW1wbGF0ZSI+CiAgICAgICAgPGRp - diBjbGFzcz0iYm9vc3RzIGJvb3N0cy0tc21hbGwgbWV0YWRhdGEgcHVzaC0t - Ym90dG9tICIKICAgIGRhdGEtY29udHJvbGxlcj0iYm9vc3RzIgogICAgZGF0 - YS1ib29zdHMtaGFzLWJvb3N0cy1zZWxlY3Rvci12YWx1ZT0iLmJvb3N0Om5v - dCguYm9vc3QtZm9ybSkiCiAgICBkYXRhLWJvb3N0cy1hZGRpbmctc2VsZWN0 - b3ItdmFsdWU9Ii5ib29zdC1mb3JtLmV4cGFuZGVkIgogICAgZGF0YS1ib29z - dHMtZW1wdHktY2xhc3M9ImJvb3N0cy0tZW1wdHkiCiAgICBkYXRhLWJvb3N0 - cy1hZGRpbmctY2xhc3M9ImJvb3N0cy0tYWRkaW5nIgogICAgZGF0YS1ib29z - dHMtZGVsZXRpbmctY2xhc3M9ImJvb3N0LS1kZWxldGluZyI+CiAgPHR1cmJv - LWZyYW1lIGNsYXNzPSJ1LWRpc3BsYXktYyIgaWQ9ImJvb3N0c19yZWNvcmRp - bmdfNzgzNTI2MTAxIj4KICAgICAgPGRpdiBjbGFzcz0iYm9vc3QiIGRhdGEt - Y3JlYXRvci1pZD0iMjcwOTEzNzg5IiBkYXRhLWJlaGF2aW9yPSJleHBhbmRh - YmxlIiBkYXRhLWJvb3N0LWlkPSI5NzE2MzY0MzciPgogICAgPGJ1dHRvbiBj - bGFzcz0iYS1mb3Itc2NyZWVuLXJlYWRlciIgYXJpYS1leHBhbmRlZD0iZmFs - c2UiIGRhdGEtdmlzaWJsZS10by1jcmVhdG9yLW9yLWFkbWluPSJ0cnVlIiBk - YXRhLWJlaGF2aW9yPSJ0b2dnbGVfZXhwYW5zaW9uX29uX2NsaWNrIj4KICAg - ICAgICAgIDxzcGFuIGFyaWEtb3ducz0iY29udGVudF9ib29zdF85NzE2MzY0 - MzciPjwvc3Bhbj4KPC9idXR0b24+CiAgICA8aW1nIGNsYXNzPSJhdmF0YXIg - Ym9vc3RfX2F2YXRhciIgYXJpYS1oaWRkZW49InRydWUiIGRhdGEtYmVoYXZp - b3I9InJpY2hfYXZhdGFyIiBkYXRhLXJpY2gtYXZhdGFyLXVybD0iLzE4MTkw - MDQwNS9wZW9wbGUvMjcwOTEzNzg5L3JpY2hfYXZhdGFyIiBkYXRhLWF2YXRh - ci1mb3ItcGVyc29uLWlkPSIyNzA5MTM3ODkiIGFsdD0iSmFzb24gRnJpZWQi - IHRpdGxlPSJKYXNvbiBGcmllZCwgQmFzZWNhbXAiIHNyYz0iaHR0cDovL2Jj - My1jZG4ubG9jYWxob3N0OjMwMDEvMTgxOTAwNDA1L3Blb3BsZS9CQWhwQlAz - UUpSQT0tLTc0YTY4ODM2MzEwZTljODZhMzlhZDQ1NzVjZjZhZjc3YzM3ZWE0 - NDUvYXZhdGFyP3Y9MSIgd2lkdGg9IjEzMCIgaGVpZ2h0PSIxMzAiIC8+Cgog - ICAgPGEgZGF0YS10dXJiby1tZXRob2Q9ImRlbGV0ZSIgZGF0YS12aXNpYmxl - LXRvLWNyZWF0b3Itb3ItYWRtaW49InRydWUiIGRhdGEtYWN0aW9uPSJib29z - dHMjc2hvd0RlbGV0aW5nRWZmZWN0IiBkYXRhLWJvb3N0cy1wYXJlbnQtaWQt - cGFyYW09Ijk3MTYzNjQzNyIgY2xhc3M9ImJ0biBidG4tLXNtYWxsIGJ0bi0t - Ym9yZGVybGVzcyBidG4tLWJvb3N0LWRlbGV0ZSBidG4tLWJvb3N0LXRyYXNo - IGV4cGFuZGVkX2NvbnRlbnQiIGFyaWEtbGFiZWw9IkRlbGV0ZSB0aGlzIGJv - b3N0IiByb2xlPSJidXR0b24iIGhyZWY9Ii8xODE5MDA0MDUvYnVja2V0cy8x - MDQyOTc5MjQ3L2Jvb3N0cy85NzE2MzY0Mzc/Ym9vc3QlNUJib29zdGFibGVf - Z2lkJTVEPVoybGtPaTh2WW1NekwxSmxZMjl5WkdsdVp5ODNPRE0xTWpZeE1E - RSI+PC9hPgoKICAgIDxzcGFuIGNsYXNzPSJib29zdF9fY29udGVudCB1LW5v - d3JhcCAiIGRhdGEtYmVoYXZpb3I9InRvZ2dsZV9leHBhbnNpb25fb25fY2xp - Y2sgY29sbGFwc2Vfb25fY2xpY2tvdXRzaWRlIgogICAgICBpZD0iY29udGVu - dF9ib29zdF85NzE2MzY0MzciPgogICAgICA8c3BhbiBhcmlhLWhpZGRlbj0i - dHJ1ZSI+Y29uZ3JhdHMhPC9zcGFuPgogICAgICA8c3BhbiBjbGFzcz0iYS1m - b3Itc2NyZWVuLXJlYWRlciI+SmFzb24gRi4gYm9vc3RlZCB0aGUgbWVzc2Fn - ZSB3aXRoICYjMzk7Y29uZ3JhdHMhJiMzOTs8L3NwYW4+CiAgICA8L3NwYW4+ - CiAgPC9kaXY+CiAgPGRpdiBjbGFzcz0iYm9vc3QiIGRhdGEtY3JlYXRvci1p - ZD0iOTUwNzM3NDU2IiBkYXRhLWJlaGF2aW9yPSJleHBhbmRhYmxlIiBkYXRh - LWJvb3N0LWlkPSI0MTk5NTIxOTgiPgogICAgPGJ1dHRvbiBjbGFzcz0iYS1m - b3Itc2NyZWVuLXJlYWRlciIgYXJpYS1leHBhbmRlZD0iZmFsc2UiIGRhdGEt - dmlzaWJsZS10by1jcmVhdG9yLW9yLWFkbWluPSJ0cnVlIiBkYXRhLWJlaGF2 - aW9yPSJ0b2dnbGVfZXhwYW5zaW9uX29uX2NsaWNrIj4KICAgICAgICAgIDxz - cGFuIGFyaWEtb3ducz0iY29udGVudF9ib29zdF80MTk5NTIxOTgiPjwvc3Bh - bj4KPC9idXR0b24+CiAgICA8aW1nIGNsYXNzPSJhdmF0YXIgYm9vc3RfX2F2 - YXRhciIgYXJpYS1oaWRkZW49InRydWUiIGRhdGEtYmVoYXZpb3I9InJpY2hf - YXZhdGFyIiBkYXRhLXJpY2gtYXZhdGFyLXVybD0iLzE4MTkwMDQwNS9wZW9w - bGUvOTUwNzM3NDU2L3JpY2hfYXZhdGFyIiBkYXRhLWF2YXRhci1mb3ItcGVy - c29uLWlkPSI5NTA3Mzc0NTYiIGFsdD0iUnlhbiBTaW5nZXIiIHRpdGxlPSJS - eWFuIFNpbmdlciwgQmFzZWNhbXAiIHNyYz0iaHR0cDovL2JjMy1jZG4ubG9j - YWxob3N0OjMwMDEvMTgxOTAwNDA1L3Blb3BsZS9CQWhwQkRBYXF6Zz0tLWU1 - YTlkMWRlMTA1NDBlNWUwNmFmMmI3ZWUzZmQ0YzQxMjk1ZTlkMzEvYXZhdGFy - P3Y9MSIgd2lkdGg9IjEzMCIgaGVpZ2h0PSIxMzAiIC8+CgogICAgPGEgZGF0 - YS10dXJiby1tZXRob2Q9ImRlbGV0ZSIgZGF0YS12aXNpYmxlLXRvLWNyZWF0 - b3Itb3ItYWRtaW49InRydWUiIGRhdGEtYWN0aW9uPSJib29zdHMjc2hvd0Rl - bGV0aW5nRWZmZWN0IiBkYXRhLWJvb3N0cy1wYXJlbnQtaWQtcGFyYW09IjQx - OTk1MjE5OCIgY2xhc3M9ImJ0biBidG4tLXNtYWxsIGJ0bi0tYm9yZGVybGVz - cyBidG4tLWJvb3N0LWRlbGV0ZSBidG4tLWJvb3N0LXRyYXNoIGV4cGFuZGVk - X2NvbnRlbnQiIGFyaWEtbGFiZWw9IkRlbGV0ZSB0aGlzIGJvb3N0IiByb2xl - PSJidXR0b24iIGhyZWY9Ii8xODE5MDA0MDUvYnVja2V0cy8xMDQyOTc5MjQ3 - L2Jvb3N0cy80MTk5NTIxOTg/Ym9vc3QlNUJib29zdGFibGVfZ2lkJTVEPVoy - bGtPaTh2WW1NekwxSmxZMjl5WkdsdVp5ODNPRE0xTWpZeE1ERSI+PC9hPgoK - ICAgIDxzcGFuIGNsYXNzPSJib29zdF9fY29udGVudCB1LW5vd3JhcCAiIGRh - dGEtYmVoYXZpb3I9InRvZ2dsZV9leHBhbnNpb25fb25fY2xpY2sgY29sbGFw - c2Vfb25fY2xpY2tvdXRzaWRlIgogICAgICBpZD0iY29udGVudF9ib29zdF80 - MTk5NTIxOTgiPgogICAgICA8c3BhbiBhcmlhLWhpZGRlbj0idHJ1ZSI+Z29v - ZCBqb2IhPC9zcGFuPgogICAgICA8c3BhbiBjbGFzcz0iYS1mb3Itc2NyZWVu - LXJlYWRlciI+UnlhbiBTLiBib29zdGVkIHRoZSBtZXNzYWdlIHdpdGggJiMz - OTtnb29kIGpvYiEmIzM5Ozwvc3Bhbj4KICAgIDwvc3Bhbj4KICA8L2Rpdj4K - ICA8ZGl2IGNsYXNzPSJib29zdCIgZGF0YS1jcmVhdG9yLWlkPSIzMjMwNjY1 - ODEiIGRhdGEtYmVoYXZpb3I9ImV4cGFuZGFibGUiIGRhdGEtYm9vc3QtaWQ9 - Ijk1NDg3MjExOCI+CiAgICA8YnV0dG9uIGNsYXNzPSJhLWZvci1zY3JlZW4t - cmVhZGVyIiBhcmlhLWV4cGFuZGVkPSJmYWxzZSIgZGF0YS12aXNpYmxlLXRv - LWNyZWF0b3Itb3ItYWRtaW49InRydWUiIGRhdGEtYmVoYXZpb3I9InRvZ2ds - ZV9leHBhbnNpb25fb25fY2xpY2siPgogICAgICAgICAgPHNwYW4gYXJpYS1v - d25zPSJjb250ZW50X2Jvb3N0Xzk1NDg3MjExOCI+PC9zcGFuPgo8L2J1dHRv - bj4KICAgIDxpbWcgY2xhc3M9ImF2YXRhciBib29zdF9fYXZhdGFyIiBhcmlh - LWhpZGRlbj0idHJ1ZSIgZGF0YS1iZWhhdmlvcj0icmljaF9hdmF0YXIiIGRh - dGEtcmljaC1hdmF0YXItdXJsPSIvMTgxOTAwNDA1L3Blb3BsZS8zMjMwNjY1 - ODEvcmljaF9hdmF0YXIiIGRhdGEtYXZhdGFyLWZvci1wZXJzb24taWQ9IjMy - MzA2NjU4MSIgYWx0PSJKZXJlbXkgS2VtcGVyIiB0aXRsZT0iSmVyZW15IEtl - bXBlciwgQmFzZWNhbXAiIHNyYz0iaHR0cDovL2JjMy1jZG4ubG9jYWxob3N0 - OjMwMDEvMTgxOTAwNDA1L3Blb3BsZS9CQWhwQk5XYVFSTT0tLTVmNjgyMDYw - NmIzZDJiODg0OGRjZDliZjdmMTkwMWIyMWJiNmY0M2MvYXZhdGFyP3Y9MSIg - d2lkdGg9IjEzMCIgaGVpZ2h0PSIxMzAiIC8+CgogICAgPGEgZGF0YS10dXJi - by1tZXRob2Q9ImRlbGV0ZSIgZGF0YS12aXNpYmxlLXRvLWNyZWF0b3Itb3It - YWRtaW49InRydWUiIGRhdGEtYWN0aW9uPSJib29zdHMjc2hvd0RlbGV0aW5n - RWZmZWN0IiBkYXRhLWJvb3N0cy1wYXJlbnQtaWQtcGFyYW09Ijk1NDg3MjEx - OCIgY2xhc3M9ImJ0biBidG4tLXNtYWxsIGJ0bi0tYm9yZGVybGVzcyBidG4t - LWJvb3N0LWRlbGV0ZSBidG4tLWJvb3N0LXRyYXNoIGV4cGFuZGVkX2NvbnRl - bnQiIGFyaWEtbGFiZWw9IkRlbGV0ZSB0aGlzIGJvb3N0IiByb2xlPSJidXR0 - b24iIGhyZWY9Ii8xODE5MDA0MDUvYnVja2V0cy8xMDQyOTc5MjQ3L2Jvb3N0 - cy85NTQ4NzIxMTg/Ym9vc3QlNUJib29zdGFibGVfZ2lkJTVEPVoybGtPaTh2 - WW1NekwxSmxZMjl5WkdsdVp5ODNPRE0xTWpZeE1ERSI+PC9hPgoKICAgIDxz - cGFuIGNsYXNzPSJib29zdF9fY29udGVudCB1LW5vd3JhcCAiIGRhdGEtYmVo - YXZpb3I9InRvZ2dsZV9leHBhbnNpb25fb25fY2xpY2sgY29sbGFwc2Vfb25f - Y2xpY2tvdXRzaWRlIgogICAgICBpZD0iY29udGVudF9ib29zdF85NTQ4NzIx - MTgiPgogICAgICA8c3BhbiBhcmlhLWhpZGRlbj0idHJ1ZSI+bmljZSE8L3Nw - YW4+CiAgICAgIDxzcGFuIGNsYXNzPSJhLWZvci1zY3JlZW4tcmVhZGVyIj5K - ZXJlbXkgSy4gYm9vc3RlZCB0aGUgbWVzc2FnZSB3aXRoICYjMzk7bmljZSEm - IzM5Ozwvc3Bhbj4KICAgIDwvc3Bhbj4KICA8L2Rpdj4KCjwvdHVyYm8tZnJh - bWU+CiAgPHR1cmJvLWZyYW1lIGNsYXNzPSJ1LWRpc3BsYXktYyIgaWQ9Im5l - d19ib29zdF9yZWNvcmRpbmdfNzgzNTI2MTAxIj4KICAgICAgPGRpdiBjbGFz - cz0iYm9vc3QtZm9ybV9fbGluay1jb250YWluZXIiPgogICAgICAgIDxhIGNs - YXNzPSJib29zdHMtZm9ybV9fbGluayIgZGF0YS1jb250cm9sbGVyPSJzb2Z0 - LWtleWJvYXJkIiBkYXRhLWFjdGlvbj0ic29mdC1rZXlib2FyZCNvcGVuIiBo - cmVmPSIvMTgxOTAwNDA1L2J1Y2tldHMvMTA0Mjk3OTI0Ny9ib29zdHMvbmV3 - P2Jvb3N0JTVCYm9vc3RhYmxlX2dpZCU1RD1aMmxrT2k4dlltTXpMMUpsWTI5 - eVpHbHVaeTgzT0RNMU1qWXhNREUiPgogICAgICAgICAgPHNwYW4gY2xhc3M9 - ImEtZm9yLXNjcmVlbi1yZWFkZXIiPk5ldyBib29zdDwvc3Bhbj4KPC9hPiAg - ICAgIDwvZGl2Pgo8L3R1cmJvLWZyYW1lPjwvZGl2PgoKICAgICAgPC9kaXY+ - CjwvYXJ0aWNsZT4KICA8c2VjdGlvbiBjbGFzcz0icmVjb3JkYWJsZS1kaXNj - dXNzaW9uIgogIGRhdGEtY29udHJvbGxlcj0idG9nZ2xlLWNsYXNzIgogIGRh - dGEtdG9nZ2xlLWNsYXNzLXRvZ2dsZS1jbGFzcz0iYmFja2xpbmtzX2FjdGl2 - ZSI+CiAgICA8dHVyYm8tZnJhbWUgaWQ9ImJhY2tsaW5rc19yZWNvcmRpbmdf - NzgzNTI2MTAxIiBzcmM9Ii8xODE5MDA0MDUvYnVja2V0cy8xMDQyOTc5MjQ3 - L3JlY29yZGluZ3MvNzgzNTI2MTAxL2JhY2tsaW5rcyI+PC90dXJiby1mcmFt - ZT4KCiAgCjx0dXJiby1mcmFtZSBkYXRhLWNvbnRyb2xsZXI9InRocmVhZCIg - ZGF0YS1hY3Rpb249Imhhc2hjaGFuZ2VAd2luZG93LSZndDt0aHJlYWQjYW5j - aG9yQ2hhbmdlZCIgaWQ9InRocmVhZF9yZWNvcmRpbmdfNzgzNTI2MTAxIiB0 - YXJnZXQ9Il90b3AiPgogIDxzZWN0aW9uIGNsYXNzPSJ0aHJlYWQgdGhyZWFk - LS1jb21tZW50cyI+CiAgICAgIDxkaXYgaWQ9InRocmVhZF9lbnRyaWVzIiBj - bGFzcz0idGhyZWFkX19lbnRyaWVzIiBkYXRhLXVybD0iLzE4MTkwMDQwNS9i - dWNrZXRzLzEwNDI5NzkyNDcvcmVjb3JkaW5ncy83ODM1MjYxMDEvdGhyZWFk - IiBkYXRhLXRocmVhZC1mb3ItcmVjb3JkaW5nPSI3ODM1MjYxMDEiIGRhdGEt - YnJpZGdlLXRocmVhZD0iIiBkYXRhLWJyaWRnZS10aHJlYWQtY3JlYXRvci1u - YW1lPSJEYXZpZCBIZWluZW1laWVyIEhhbnNzb24iIGRhdGEtYnJpZGdlLXRo - cmVhZC1jcmVhdG9yLWF2YXRhci11cmw9Imh0dHA6Ly9iYzMtY2RuLmxvY2Fs - aG9zdDozMDAxLzE4MTkwMDQwNS9wZW9wbGUvQkFocEJPJTJGaVVSOD0tLWEx - NDMwOGVmOGIxMDFhNjYxN2EzNDVlNDRlZmEwZTM1NWVjZDA0MmUvYXZhdGFy - P3Y9MSI+CgoKICAgICAgICAgICAgPGFydGljbGUgZGF0YS1yZWNvcmRpbmct - aWQ9IjQyODk5ODIzNSIgZGF0YS1jcmVhdG9yLWlkPSIyNzA5MTM3ODkiIGRh - dGEtdXJsPSIvMTgxOTAwNDA1L2J1Y2tldHMvMTA0Mjk3OTI0Ny9jb21tZW50 - cy80Mjg5OTgyMzUiIGRhdGEtcmVhZGFibGUtaWRlbnRpZmllcj0iWjJsa09p - OHZZbU16TDFKbFkyOXlaR2x1Wnk4ME1qZzVPVGd5TXpVIiBkYXRhLXBhcmVu - dC1pZD0iNzgzNTI2MTAxIiBkYXRhLXRocmVhZC1lbnRyeS1naWQ9IiZxdW90 - O2dpZDovL2JjMy9SZWNvcmRpbmcvNDI4OTk4MjM1JnF1b3Q7IiBkYXRhLXR5 - cGU9ImNvbW1lbnQiIGNsYXNzPSJ0aHJlYWQtZW50cnkgcmVjb3JkaW5nIiBp - ZD0icmVjb3JkaW5nXzQyODk5ODIzNSI+PHNwYW4gY2xhc3M9ImFuY2hvciBy - ZWNvcmRhYmxlX19hbmNob3IiIGlkPSJfX3JlY29yZGluZ180Mjg5OTgyMzUi - Pjwvc3Bhbj4KICA8aGVhZGVyIGNsYXNzPSJ0aHJlYWQtZW50cnlfX2hlYWRl - ciI+CiAgICAgIDxkaXYgY2xhc3M9InRocmVhZC1lbnRyeV9fdGltZSBtZXRh - ZGF0YSI+CiAgICAgICAgPGEgaHJlZj0iaHR0cDovLzMuYmFzZWNhbXAubG9j - YWxob3N0OjMwMDEvMTgxOTAwNDA1L2J1Y2tldHMvMTA0Mjk3OTI0Ny9tZXNz - YWdlcy83ODM1MjYxMDEjX19yZWNvcmRpbmdfNDI4OTk4MjM1Ij48dGltZSBk - YXRldGltZT0iMjAyNS0wOC0wOFQxNzoyNjo1NloiIGRhdGEtbG9jYWw9Indl - ZWtkYXkiPkF1Z3VzdCAgOCwgMjAyNSAgNToyNnBtPC90aW1lPjwvYT4KICAg - ICAgICA8YSBocmVmPSJodHRwOi8vMy5iYXNlY2FtcC5sb2NhbGhvc3Q6MzAw - MS8xODE5MDA0MDUvYnVja2V0cy8xMDQyOTc5MjQ3L21lc3NhZ2VzLzc4MzUy - NjEwMSNfX3JlY29yZGluZ180Mjg5OTgyMzUiPjx0aW1lIGRhdGV0aW1lPSIy - MDI1LTA4LTA4VDE3OjI2OjU2WiIgZGF0YS1sb2NhbD0idGltZS1vci1kYXRl - Ij5BdWd1c3QgIDgsIDIwMjUgIDU6MjZwbTwvdGltZT48L2E+CiAgICAgIDwv - ZGl2PgoKICAgIDxpbWcgY2xhc3M9ImF2YXRhciB0aHJlYWQtZW50cnlfX2F2 - YXRhciIgZGF0YS1iZWhhdmlvcj0icmljaF9hdmF0YXIiIGRhdGEtcmljaC1h - dmF0YXItdXJsPSIvMTgxOTAwNDA1L3Blb3BsZS8yNzA5MTM3ODkvcmljaF9h - dmF0YXIiIGRhdGEtYXZhdGFyLWZvci1wZXJzb24taWQ9IjI3MDkxMzc4OSIg - YWx0PSJKYXNvbiBGcmllZCIgdGl0bGU9Ikphc29uIEZyaWVkLCBCYXNlY2Ft - cCIgc3JjPSJodHRwOi8vYmMzLWNkbi5sb2NhbGhvc3Q6MzAwMS8xODE5MDA0 - MDUvcGVvcGxlL0JBaHBCUDNRSlJBPS0tNzRhNjg4MzYzMTBlOWM4NmEzOWFk - NDU3NWNmNmFmNzdjMzdlYTQ0NS9hdmF0YXI/dj0xIiB3aWR0aD0iMTMwIiBo - ZWlnaHQ9IjEzMCIgLz4KCiAgICAgIDxzcGFuIGNsYXNzPSJ0aHJlYWQtZW50 - cnlfX2F1dGhvciI+CiAgICAgICAgPHN0cm9uZz5KYXNvbiBGcmllZDwvc3Ry - b25nPgogICAgICAgIAogICAgICA8L3NwYW4+CiAgPC9oZWFkZXI+CgogIDxk - aXYgY2xhc3M9InRocmVhZC1lbnRyeV9fY29udGVudCBmb3JtYXR0ZWRfY29u - dGVudCI+CiAgICBUaGF0J3MgU09PTyBncmVhdCEgQ2hlY2sgaXQgb3V0LCBA - UlMKICA8L2Rpdj4KCiAgICA8ZGl2IGNsYXNzPSJib29zdHMgYm9vc3RzLS1z - bWFsbCBtZXRhZGF0YSBwdXNoLS1ib3R0b20gYm9vc3RzLS10aHJlYWRhYmxl - IgogICAgZGF0YS1jb250cm9sbGVyPSJib29zdHMiCiAgICBkYXRhLWJvb3N0 - cy1oYXMtYm9vc3RzLXNlbGVjdG9yLXZhbHVlPSIuYm9vc3Q6bm90KC5ib29z - dC1mb3JtKSIKICAgIGRhdGEtYm9vc3RzLWFkZGluZy1zZWxlY3Rvci12YWx1 - ZT0iLmJvb3N0LWZvcm0uZXhwYW5kZWQiCiAgICBkYXRhLWJvb3N0cy1lbXB0 - eS1jbGFzcz0iYm9vc3RzLS1lbXB0eSIKICAgIGRhdGEtYm9vc3RzLWFkZGlu - Zy1jbGFzcz0iYm9vc3RzLS1hZGRpbmciCiAgICBkYXRhLWJvb3N0cy1kZWxl - dGluZy1jbGFzcz0iYm9vc3QtLWRlbGV0aW5nIj4KICA8dHVyYm8tZnJhbWUg - Y2xhc3M9InUtZGlzcGxheS1jIiBpZD0iYm9vc3RzX3JlY29yZGluZ180Mjg5 - OTgyMzUiPgo8L3R1cmJvLWZyYW1lPgogIDx0dXJiby1mcmFtZSBjbGFzcz0i - dS1kaXNwbGF5LWMiIGlkPSJuZXdfYm9vc3RfcmVjb3JkaW5nXzQyODk5ODIz - NSI+CiAgICAgIDxkaXYgY2xhc3M9ImJvb3N0LWZvcm1fX2xpbmstY29udGFp - bmVyIj4KICAgICAgICA8YSBjbGFzcz0iYm9vc3RzLWZvcm1fX2xpbmsiIGRh - dGEtY29udHJvbGxlcj0ic29mdC1rZXlib2FyZCIgZGF0YS1hY3Rpb249InNv - ZnQta2V5Ym9hcmQjb3BlbiIgaHJlZj0iLzE4MTkwMDQwNS9idWNrZXRzLzEw - NDI5NzkyNDcvYm9vc3RzL25ldz9ib29zdCU1QmJvb3N0YWJsZV9naWQlNUQ9 - WjJsa09pOHZZbU16TDFKbFkyOXlaR2x1Wnk4ME1qZzVPVGd5TXpVIj4KICAg - ICAgICAgIDxzcGFuIGNsYXNzPSJhLWZvci1zY3JlZW4tcmVhZGVyIj5OZXcg - Ym9vc3Q8L3NwYW4+CjwvYT4gICAgICA8L2Rpdj4KPC90dXJiby1mcmFtZT48 - L2Rpdj4KCgoKICA8c3BhbiBjbGFzcz0idGhyZWFkLWVudHJ5X19vcHRpb25z - Ij4KICAgIDxzcGFuIGNsYXNzPSJhY3Rpb24tc2hlZXQgYWN0aW9uLXNoZWV0 - LS1mb3ItdGhyZWFkLWVudHJ5IHRocmVhZC1lbnRyeV9fbWVudSB1LXBvc2l0 - aW9uLWNvbnRleHQiIGRhdGEtYmVoYXZpb3I9ImV4cGFuZGFibGUiPgoKICAg - ICAgPGJ1dHRvbiBuYW1lPSJidXR0b24iIHR5cGU9ImJ1dHRvbiIgdGl0bGU9 - IlNob3cgb3B0aW9uc+KApiIgYXJpYS1sYWJlbD0iT3B0aW9ucyBtZW51IiBj - bGFzcz0iYWN0aW9uLXNoZWV0X19leHBhbnNpb24tdG9nZ2xlIGJ0biBidG4t - LWljb24gYnRuLS1vdmVyZmxvdy1pY29uIGJ0bi0tc21hbGwgYnRuLS1ib3Jk - ZXJsZXNzIGJ0bi0tb3ZlcmZsb3ctaWNvbi1saWdodCIgZGF0YS1iZWhhdmlv - cj0idG9nZ2xlX2V4cGFuc2lvbl9vbl9jbGljayB0b2dnbGVfZmlsZV9tZW51 - Ij5GaWxl4oCmPC9idXR0b24+CgogICAgICA8c3BhbiBjbGFzcz0iYWN0aW9u - LXNoZWV0X19jb250ZW50IiBkYXRhLWJlaGF2aW9yPSJjb2xsYXBzZV9vbl9j - bGlja291dHNpZGUiPgogICAgICAgIDxidXR0b24gY2xhc3M9ImFjdGlvbi1z - aGVldF9fY2xvc2UgYnRuIGJ0bi0tc21hbGwgYnRuLS1pY29uIGJ0bi0tY2xv - c2Utc2hlZXQtaWNvbiIgYXJpYS1sYWJlbD0iQ2xvc2UgbWVudSIgdHlwZT0i - YnV0dG9uIiBkYXRhLWJlaGF2aW9yPSJjb2xsYXBzZV9vbl9jbGljayI+PC9i - dXR0b24+CgogICAgICAgIDx0dXJiby1mcmFtZSBsb2FkaW5nPSJsYXp5IiBj - bGFzcz0iYWN0aW9uLXNoZWV0X19sYXp5LW9wdGlvbnMiIGlkPSJvcHRpb25z - X3JlY29yZGluZ180Mjg5OTgyMzUiIHNyYz0iLzE4MTkwMDQwNS9idWNrZXRz - LzEwNDI5NzkyNDcvcmVjb3JkaW5ncy83ODM1MjYxMDEvY29tbWVudHMvNDI4 - OTk4MjM1L29wdGlvbnMiPgogICAgICAgICAgPGRpdiBjbGFzcz0ic3Bpbm5l - ciBzcGlubmVyLS1sYXp5LXR1cmJvIj4KICA8ZGl2IGNsYXNzPSJzcGlubmVy - X19kb3Qgc3Bpbm5lcl9fZG90LS0xIj48L2Rpdj4KICA8ZGl2IGNsYXNzPSJz - cGlubmVyX19kb3Qgc3Bpbm5lcl9fZG90LS0yIj48L2Rpdj4KICA8ZGl2IGNs - YXNzPSJzcGlubmVyX19kb3Qgc3Bpbm5lcl9fZG90LS0zIj48L2Rpdj4KPC9k - aXY+CjwvdHVyYm8tZnJhbWU+ICAgICAgPC9zcGFuPgogICAgPC9zcGFuPgog - IDwvc3Bhbj4KPC9hcnRpY2xlPgogICAgPGFydGljbGUgZGF0YS1yZWNvcmRp - bmctaWQ9IjgxNjQ5MjA2NCIgZGF0YS1jcmVhdG9yLWlkPSIzMjMwNjY1ODEi - IGRhdGEtdXJsPSIvMTgxOTAwNDA1L2J1Y2tldHMvMTA0Mjk3OTI0Ny9jb21t - ZW50cy84MTY0OTIwNjQiIGRhdGEtcmVhZGFibGUtaWRlbnRpZmllcj0iWjJs - a09pOHZZbU16TDFKbFkyOXlaR2x1Wnk4NE1UWTBPVEl3TmpRIiBkYXRhLXBh - cmVudC1pZD0iNzgzNTI2MTAxIiBkYXRhLXRocmVhZC1lbnRyeS1naWQ9IiZx - dW90O2dpZDovL2JjMy9SZWNvcmRpbmcvODE2NDkyMDY0JnF1b3Q7IiBkYXRh - LXR5cGU9ImNvbW1lbnQiIGNsYXNzPSJ0aHJlYWQtZW50cnkgcmVjb3JkaW5n - IiBpZD0icmVjb3JkaW5nXzgxNjQ5MjA2NCI+PHNwYW4gY2xhc3M9ImFuY2hv - ciByZWNvcmRhYmxlX19hbmNob3IiIGlkPSJfX3JlY29yZGluZ184MTY0OTIw - NjQiPjwvc3Bhbj4KICA8aGVhZGVyIGNsYXNzPSJ0aHJlYWQtZW50cnlfX2hl - YWRlciI+CiAgICAgIDxkaXYgY2xhc3M9InRocmVhZC1lbnRyeV9fdGltZSBt - ZXRhZGF0YSI+CiAgICAgICAgPGEgaHJlZj0iaHR0cDovLzMuYmFzZWNhbXAu - bG9jYWxob3N0OjMwMDEvMTgxOTAwNDA1L2J1Y2tldHMvMTA0Mjk3OTI0Ny9t - ZXNzYWdlcy83ODM1MjYxMDEjX19yZWNvcmRpbmdfODE2NDkyMDY0Ij48dGlt - ZSBkYXRldGltZT0iMjAyNS0wOC0wOFQxNzoyNjo1NloiIGRhdGEtbG9jYWw9 - IndlZWtkYXkiPkF1Z3VzdCAgOCwgMjAyNSAgNToyNnBtPC90aW1lPjwvYT4K - ICAgICAgICA8YSBocmVmPSJodHRwOi8vMy5iYXNlY2FtcC5sb2NhbGhvc3Q6 - MzAwMS8xODE5MDA0MDUvYnVja2V0cy8xMDQyOTc5MjQ3L21lc3NhZ2VzLzc4 - MzUyNjEwMSNfX3JlY29yZGluZ184MTY0OTIwNjQiPjx0aW1lIGRhdGV0aW1l - PSIyMDI1LTA4LTA4VDE3OjI2OjU2WiIgZGF0YS1sb2NhbD0idGltZS1vci1k - YXRlIj5BdWd1c3QgIDgsIDIwMjUgIDU6MjZwbTwvdGltZT48L2E+CiAgICAg - IDwvZGl2PgoKICAgIDxpbWcgY2xhc3M9ImF2YXRhciB0aHJlYWQtZW50cnlf - X2F2YXRhciIgZGF0YS1iZWhhdmlvcj0icmljaF9hdmF0YXIiIGRhdGEtcmlj - aC1hdmF0YXItdXJsPSIvMTgxOTAwNDA1L3Blb3BsZS8zMjMwNjY1ODEvcmlj - aF9hdmF0YXIiIGRhdGEtYXZhdGFyLWZvci1wZXJzb24taWQ9IjMyMzA2NjU4 - MSIgYWx0PSJKZXJlbXkgS2VtcGVyIiB0aXRsZT0iSmVyZW15IEtlbXBlciwg - QmFzZWNhbXAiIHNyYz0iaHR0cDovL2JjMy1jZG4ubG9jYWxob3N0OjMwMDEv - MTgxOTAwNDA1L3Blb3BsZS9CQWhwQk5XYVFSTT0tLTVmNjgyMDYwNmIzZDJi - ODg0OGRjZDliZjdmMTkwMWIyMWJiNmY0M2MvYXZhdGFyP3Y9MSIgd2lkdGg9 - IjEzMCIgaGVpZ2h0PSIxMzAiIC8+CgogICAgICA8c3BhbiBjbGFzcz0idGhy - ZWFkLWVudHJ5X19hdXRob3IiPgogICAgICAgIDxzdHJvbmc+SmVyZW15IEtl - bXBlcjwvc3Ryb25nPgogICAgICAgIAogICAgICA8L3NwYW4+CiAgPC9oZWFk - ZXI+CgogIDxkaXYgY2xhc3M9InRocmVhZC1lbnRyeV9fY29udGVudCBmb3Jt - YXR0ZWRfY29udGVudCI+CiAgICBOaWNlIQogIDwvZGl2PgoKICAgIDxkaXYg - Y2xhc3M9ImJvb3N0cyBib29zdHMtLXNtYWxsIG1ldGFkYXRhIHB1c2gtLWJv - dHRvbSBib29zdHMtLXRocmVhZGFibGUiCiAgICBkYXRhLWNvbnRyb2xsZXI9 - ImJvb3N0cyIKICAgIGRhdGEtYm9vc3RzLWhhcy1ib29zdHMtc2VsZWN0b3It - dmFsdWU9Ii5ib29zdDpub3QoLmJvb3N0LWZvcm0pIgogICAgZGF0YS1ib29z - dHMtYWRkaW5nLXNlbGVjdG9yLXZhbHVlPSIuYm9vc3QtZm9ybS5leHBhbmRl - ZCIKICAgIGRhdGEtYm9vc3RzLWVtcHR5LWNsYXNzPSJib29zdHMtLWVtcHR5 - IgogICAgZGF0YS1ib29zdHMtYWRkaW5nLWNsYXNzPSJib29zdHMtLWFkZGlu - ZyIKICAgIGRhdGEtYm9vc3RzLWRlbGV0aW5nLWNsYXNzPSJib29zdC0tZGVs - ZXRpbmciPgogIDx0dXJiby1mcmFtZSBjbGFzcz0idS1kaXNwbGF5LWMiIGlk - PSJib29zdHNfcmVjb3JkaW5nXzgxNjQ5MjA2NCI+CjwvdHVyYm8tZnJhbWU+ - CiAgPHR1cmJvLWZyYW1lIGNsYXNzPSJ1LWRpc3BsYXktYyIgaWQ9Im5ld19i - b29zdF9yZWNvcmRpbmdfODE2NDkyMDY0Ij4KICAgICAgPGRpdiBjbGFzcz0i - Ym9vc3QtZm9ybV9fbGluay1jb250YWluZXIiPgogICAgICAgIDxhIGNsYXNz - PSJib29zdHMtZm9ybV9fbGluayIgZGF0YS1jb250cm9sbGVyPSJzb2Z0LWtl - eWJvYXJkIiBkYXRhLWFjdGlvbj0ic29mdC1rZXlib2FyZCNvcGVuIiBocmVm - PSIvMTgxOTAwNDA1L2J1Y2tldHMvMTA0Mjk3OTI0Ny9ib29zdHMvbmV3P2Jv - b3N0JTVCYm9vc3RhYmxlX2dpZCU1RD1aMmxrT2k4dlltTXpMMUpsWTI5eVpH - bHVaeTg0TVRZME9USXdOalEiPgogICAgICAgICAgPHNwYW4gY2xhc3M9ImEt - Zm9yLXNjcmVlbi1yZWFkZXIiPk5ldyBib29zdDwvc3Bhbj4KPC9hPiAgICAg - IDwvZGl2Pgo8L3R1cmJvLWZyYW1lPjwvZGl2PgoKCgogIDxzcGFuIGNsYXNz - PSJ0aHJlYWQtZW50cnlfX29wdGlvbnMiPgogICAgPHNwYW4gY2xhc3M9ImFj - dGlvbi1zaGVldCBhY3Rpb24tc2hlZXQtLWZvci10aHJlYWQtZW50cnkgdGhy - ZWFkLWVudHJ5X19tZW51IHUtcG9zaXRpb24tY29udGV4dCIgZGF0YS1iZWhh - dmlvcj0iZXhwYW5kYWJsZSI+CgogICAgICA8YnV0dG9uIG5hbWU9ImJ1dHRv - biIgdHlwZT0iYnV0dG9uIiB0aXRsZT0iU2hvdyBvcHRpb25z4oCmIiBhcmlh - LWxhYmVsPSJPcHRpb25zIG1lbnUiIGNsYXNzPSJhY3Rpb24tc2hlZXRfX2V4 - cGFuc2lvbi10b2dnbGUgYnRuIGJ0bi0taWNvbiBidG4tLW92ZXJmbG93LWlj - b24gYnRuLS1zbWFsbCBidG4tLWJvcmRlcmxlc3MgYnRuLS1vdmVyZmxvdy1p - Y29uLWxpZ2h0IiBkYXRhLWJlaGF2aW9yPSJ0b2dnbGVfZXhwYW5zaW9uX29u - X2NsaWNrIHRvZ2dsZV9maWxlX21lbnUiPkZpbGXigKY8L2J1dHRvbj4KCiAg - ICAgIDxzcGFuIGNsYXNzPSJhY3Rpb24tc2hlZXRfX2NvbnRlbnQiIGRhdGEt - YmVoYXZpb3I9ImNvbGxhcHNlX29uX2NsaWNrb3V0c2lkZSI+CiAgICAgICAg - PGJ1dHRvbiBjbGFzcz0iYWN0aW9uLXNoZWV0X19jbG9zZSBidG4gYnRuLS1z - bWFsbCBidG4tLWljb24gYnRuLS1jbG9zZS1zaGVldC1pY29uIiBhcmlhLWxh - YmVsPSJDbG9zZSBtZW51IiB0eXBlPSJidXR0b24iIGRhdGEtYmVoYXZpb3I9 - ImNvbGxhcHNlX29uX2NsaWNrIj48L2J1dHRvbj4KCiAgICAgICAgPHR1cmJv - LWZyYW1lIGxvYWRpbmc9ImxhenkiIGNsYXNzPSJhY3Rpb24tc2hlZXRfX2xh - enktb3B0aW9ucyIgaWQ9Im9wdGlvbnNfcmVjb3JkaW5nXzgxNjQ5MjA2NCIg - c3JjPSIvMTgxOTAwNDA1L2J1Y2tldHMvMTA0Mjk3OTI0Ny9yZWNvcmRpbmdz - Lzc4MzUyNjEwMS9jb21tZW50cy84MTY0OTIwNjQvb3B0aW9ucyI+CiAgICAg - ICAgICA8ZGl2IGNsYXNzPSJzcGlubmVyIHNwaW5uZXItLWxhenktdHVyYm8i - PgogIDxkaXYgY2xhc3M9InNwaW5uZXJfX2RvdCBzcGlubmVyX19kb3QtLTEi - PjwvZGl2PgogIDxkaXYgY2xhc3M9InNwaW5uZXJfX2RvdCBzcGlubmVyX19k - b3QtLTIiPjwvZGl2PgogIDxkaXYgY2xhc3M9InNwaW5uZXJfX2RvdCBzcGlu - bmVyX19kb3QtLTMiPjwvZGl2Pgo8L2Rpdj4KPC90dXJiby1mcmFtZT4gICAg - ICA8L3NwYW4+CiAgICA8L3NwYW4+CiAgPC9zcGFuPgo8L2FydGljbGU+Cgo8 - L2Rpdj4KICAgICAgICA8YXJ0aWNsZSBjbGFzcz0idGhyZWFkLWVudHJ5IHRo - cmVhZC1lbnRyeS0tZm9ybSBwdXNoLS1ib3R0b20gcmVjb3JkaW5nIiBpZD0i - bmV3X2NvbW1lbnRfZm9yX3JlY29yZGluZ183ODM1MjYxMDEiIGRhdGEtcmVj - b3JkaW5nLWlkPSI3ODM1MjYxMDEiIGRhdGEtY3JlYXRvci1pZD0iNTI1NDYw - MjA3IiBkYXRhLXVybD0iLzE4MTkwMDQwNS9idWNrZXRzLzEwNDI5NzkyNDcv - bWVzc2FnZXMvNzgzNTI2MTAxIiBkYXRhLXJlYWRhYmxlLWlkZW50aWZpZXI9 - IloybGtPaTh2WW1NekwxSmxZMjl5WkdsdVp5ODNPRE0xTWpZeE1ERSIgZGF0 - YS1iZWhhdmlvcj0iZXhwYW5kYWJsZSI+PHNwYW4gY2xhc3M9ImFuY2hvciBy - ZWNvcmRhYmxlX19hbmNob3IiIGlkPSJfX3JlY29yZGluZ183ODM1MjYxMDEi - Pjwvc3Bhbj4KICAgIDxpbWcgY2xhc3M9ImF2YXRhciB0aHJlYWQtZW50cnlf - X2F2YXRhciIgZGF0YS1iZWhhdmlvcj0icmljaF9hdmF0YXIiIGRhdGEtcmlj - aC1hdmF0YXItdXJsPSIvMTgxOTAwNDA1L3Blb3BsZS81MjU0NjAyMDcvcmlj - aF9hdmF0YXIiIGRhdGEtY3VycmVudC1wZXJzb24tYXZhdGFyPSJ0cnVlIiBh - bHQ9Ik15IGF2YXRhciIgc3JjPSJodHRwOi8vMy5iYXNlY2FtcC5sb2NhbGhv - c3Q6MzAwMS8xODE5MDA0MDUvbXkvYXZhdGFyIiB3aWR0aD0iMTMwIiBoZWln - aHQ9IjEzMCIgLz4KCiAgICA8ZGl2IGNsYXNzPSJjb2xsYXBzZWRfY29udGVu - dCBhcHAtbW9iaWxlX19oaWRlIj4KICAgICAgPGJ1dHRvbiBuYW1lPSJidXR0 - b24iIHR5cGU9ImJ1dHRvbiIgY2xhc3M9InByb21wdCBidG4tLWRvdWJsZS1o - ZWlnaHQgYWxpZ24tLWxlZnQiIGRhdGEtYmVoYXZpb3I9ImV4cGFuZF9vbl9j - bGljayI+QWRkIGEgY29tbWVudCBoZXJl4oCmPC9idXR0b24+CiAgICA8L2Rp - dj4KCiAgICA8ZGl2IGNsYXNzPSJleHBhbmRlZF9jb250ZW50IGFwcC1tb2Jp - bGVfX2hpZGUiPgogICAgICA8dHVyYm8tZnJhbWUgaWQ9Im5ld19jb21tZW50 - X2Zvcm1fcmVjb3JkaW5nXzc4MzUyNjEwMSIgc3JjPSIvMTgxOTAwNDA1L2J1 - Y2tldHMvMTA0Mjk3OTI0Ny9yZWNvcmRpbmdzLzc4MzUyNjEwMS9jb21tZW50 - cy9uZXciIHRhcmdldD0iX3RvcCI+PC90dXJiby1mcmFtZT4KICAgIDwvZGl2 - Pgo8L2FydGljbGU+CgogICAgPHNlY3Rpb24gY2xhc3M9InRocmVhZF9fc3Vi - c2NyaXB0aW9ucyI+CiAgPGFydGljbGUgY2xhc3M9InRocmVhZC1lbnRyeSB0 - aHJlYWQtZW50cnlfX3N1YnNjcmlwdGlvbnMgdS1oaWRlLW9uLXRlbXBsYXRl - Ij4KICAgIDxkaXYgY2xhc3M9InRocmVhZF9fc3Vic2NyaWJlcnMiPgogICAg - ICA8c3BhbiBjbGFzcz0iYW5jaG9yIiBpZD0iX19zdWJzY3JpcHRpb25zIj48 - L3NwYW4+CgogICAgICA8aDQgY2xhc3M9ImZsdXNoIj5TdWJzY3JpYmVyczwv - aDQ+CgogICAgICA8cCBjbGFzcz0idGhyZWFkX19zdWJzY3JpcHRpb24tZXhw - bGFuYXRpb24gZmx1c2gtLXRvcCBwdXNoX2hhbGYtLWJvdHRvbSI+CgogICAg - ICAgIDxzcGFuIGRhdGEtcm9sZT0ic3Vic2NyaWJlcnMtY291bnQiPgogICAg - ICAgICAgICAyIHBlb3BsZQogICAgICAgIDwvc3Bhbj4KCiAgICAgICAgd2ls - bCBiZSBub3RpZmllZCB3aGVuIHNvbWVvbmUgY29tbWVudHMgb24gdGhpcyBt - ZXNzYWdlLgogICAgICA8L3A+CgogICAgICA8ZGl2IGNsYXNzPSJhdmF0YXIt - Z3JvdXAgYXZhdGFyLWdyb3VwLS1zbWFsbCIgZGF0YS1yb2xlPSJzdWJzY3Jp - cHRpb24tYXZhdGFycyI+CiAgICAgICAgPGltZyBkYXRhLWJlaGF2aW9yPSJy - aWNoX2F2YXRhciIgZGF0YS1yaWNoLWF2YXRhci11cmw9Ii8xODE5MDA0MDUv - cGVvcGxlLzUyNTQ2MDIwNy9yaWNoX2F2YXRhciIgZGF0YS1hdmF0YXItZm9y - LXBlcnNvbi1pZD0iNTI1NDYwMjA3IiBhbHQ9IkRhdmlkIEhlaW5lbWVpZXIg - SGFuc3NvbiIgdGl0bGU9IkRhdmlkIEhlaW5lbWVpZXIgSGFuc3NvbiwgQmFz - ZWNhbXAiIGNsYXNzPSJhdmF0YXIiIHNyYz0iaHR0cDovL2JjMy1jZG4ubG9j - YWxob3N0OjMwMDEvMTgxOTAwNDA1L3Blb3BsZS9CQWhwQk8lMkZpVVI4PS0t - YTE0MzA4ZWY4YjEwMWE2NjE3YTM0NWU0NGVmYTBlMzU1ZWNkMDQyZS9hdmF0 - YXI/dj0xIiB3aWR0aD0iMTMwIiBoZWlnaHQ9IjEzMCIgLz4gPGltZyBkYXRh - LWJlaGF2aW9yPSJyaWNoX2F2YXRhciIgZGF0YS1yaWNoLWF2YXRhci11cmw9 - Ii8xODE5MDA0MDUvcGVvcGxlLzI3MDkxMzc4OS9yaWNoX2F2YXRhciIgZGF0 - YS1hdmF0YXItZm9yLXBlcnNvbi1pZD0iMjcwOTEzNzg5IiBhbHQ9Ikphc29u - IEZyaWVkIiB0aXRsZT0iSmFzb24gRnJpZWQsIEJhc2VjYW1wIiBjbGFzcz0i - YXZhdGFyIiBzcmM9Imh0dHA6Ly9iYzMtY2RuLmxvY2FsaG9zdDozMDAxLzE4 - MTkwMDQwNS9wZW9wbGUvQkFocEJQM1FKUkE9LS03NGE2ODgzNjMxMGU5Yzg2 - YTM5YWQ0NTc1Y2Y2YWY3N2MzN2VhNDQ1L2F2YXRhcj92PTEiIHdpZHRoPSIx - MzAiIGhlaWdodD0iMTMwIiAvPgoKICAgICAgICA8YSBjbGFzcz0iYnRuIGJ0 - bi0tc21hbGwgc2VsZi1zdGFydCIgaHJlZj0iLzE4MTkwMDQwNS9idWNrZXRz - LzEwNDI5NzkyNDcvcmVjb3JkaW5ncy83ODM1MjYxMDEvc3Vic2NyaXB0aW9u - L2VkaXQiPkFkZC9yZW1vdmUgcGVvcGxl4oCmPC9hPgogICAgICA8L2Rpdj4K - ICAgIDwvZGl2PgoKICAgIDxkaXYgY2xhc3M9InRocmVhZF9fc2VsZi1zdWJz - Y3JpcHRpb24iIGRhdGEtcm9sZT0idGhyZWFkLXNlbGYtc3Vic2NyaXB0aW9u - Ij4KICA8ZGl2IGNsYXNzPSJ0aHJlYWRfX3NlbGYtc3Vic2NyaWJlZCI+CiAg - ICA8aDQgY2xhc3M9ImZsdXNoLS1ib3R0b20iPllvdeKAmXJlIHN1YnNjcmli - ZWQ8L2g0PgogICAgPHAgY2xhc3M9InRocmVhZF9fc3Vic2NyaXB0aW9uLWV4 - cGxhbmF0aW9uIGZsdXNoLS10b3AgcHVzaF9oYWxmLS1ib3R0b20iPgogICAg - ICAgIFlvdeKAmWxsIGdldCBhIG5vdGlmaWNhdGlvbiB3aGVuIHNvbWVvbmUg - Y29tbWVudHMgb24gdGhpcyBtZXNzYWdlLgogICAgICA8L3A+CiAgPC9kaXY+ - CgogIDxkaXYgY2xhc3M9InRocmVhZF9fc2VsZi11bnN1YnNjcmliZWQiPgog - ICAgPGg0IGNsYXNzPSJmbHVzaC0tYm90dG9tIj5Zb3XigJlyZSBub3Qgc3Vi - c2NyaWJlZDwvaDQ+CiAgICA8cCBjbGFzcz0idGhyZWFkX19zdWJzY3JpcHRp - b24tZXhwbGFuYXRpb24gZmx1c2gtLXRvcCBwdXNoX2hhbGYtLWJvdHRvbSI+ - CiAgICAgICAgWW91IHdvbuKAmXQgYmUgbm90aWZpZWQgd2hlbiBjb21tZW50 - cyBhcmUgcG9zdGVkLgogICAgICA8L3A+CiAgPC9kaXY+CgogIDxidXR0b24g - bmFtZT0iYnV0dG9uIiB0eXBlPSJzdWJtaXQiIGNsYXNzPSJidG4gYnRuLS1z - bWFsbCIgZGF0YS11cmw9Ii8xODE5MDA0MDUvYnVja2V0cy8xMDQyOTc5MjQ3 - L3JlY29yZGluZ3MvNzgzNTI2MTAxL3N1YnNjcmlwdGlvbiIgZGF0YS1iZWhh - dmlvcj0idG9nZ2xlX3N1YnNjcmlwdGlvbl9vbl9yZWNvcmRpbmciPlVuc3Vi - c2NyaWJlIG1lPC9idXR0b24+CjwvZGl2PgoKICA8L2FydGljbGU+Cjwvc2Vj - dGlvbj4KCiAgPC9zZWN0aW9uPgo8L3R1cmJvLWZyYW1lPgo8L3NlY3Rpb24+ - Cgo8L2Rpdj4KICA8L21haW4+CgogIAogIDxkaXYgY2xhc3M9ImJhY2stdG8t - dG9wIGJhY2stdG8tdG9wLS1pbnRlcnNlY3RpbmcgYXBwLW1vYmlsZV9faGlk - ZSB1LWhpZGUtb24tcGhvbmUiIGRhdGEtY29udHJvbGxlcj0idmlld3BvcnQt - ZW50cmFuY2UtdG9nZ2xlIiBkYXRhLXZpZXdwb3J0LWVudHJhbmNlLXRvZ2ds - ZS1jbGFzcz0iYmFjay10by10b3AtLWludGVyc2VjdGluZyI+PGJ1dHRvbiBj - bGFzcz0iYmFjay10by10b3BfX2J1dHRvbiBidG4gYnRuLS1zbWFsbCBidG4t - LXdpdGgtaWNvbiBidG4tLWFycm93LXRvcC1pY29uIiBkYXRhLWFjdGlvbj0i - Y2xpY2stJmd0O3ZpZXdwb3J0LWVudHJhbmNlLXRvZ2dsZSNiYWNrVG9Ub3Ai - PkJhY2sgdG8gdG9wPC9idXR0b24+PC9kaXY+CjwvYm9keT4KPC9odG1sPgo= - recorded_at: Thu, 25 Sep 2025 12:04:38 GMT -recorded_with: VCR 6.3.1 From 98f6b13663b92167898bb006aa6cf6f1ed96b89c Mon Sep 17 00:00:00 2001 From: "Stanko K.R." Date: Tue, 2 Dec 2025 15:39:57 +0100 Subject: [PATCH 7/9] Generalize unfurler setup --- app/controllers/unfurl_links_controller.rb | 8 ++++--- .../controllers/unfurl_link_controller.js | 14 ++++++------- app/models/link.rb | 21 ++++++++++++------- app/models/link/fetch.rb | 4 ++++ app/models/link/fizzy_unfurler.rb | 8 +++++++ app/models/link/open_graph_unfurler.rb | 8 +++++++ 6 files changed, 45 insertions(+), 18 deletions(-) diff --git a/app/controllers/unfurl_links_controller.rb b/app/controllers/unfurl_links_controller.rb index cbd6bf9a19..38554758ba 100644 --- a/app/controllers/unfurl_links_controller.rb +++ b/app/controllers/unfurl_links_controller.rb @@ -4,13 +4,15 @@ class UnfurlLinksController < ApplicationController def create link = Link.unfurl(url_param) - if link.metadata + if link.unfurler.requires_setup? + render \ + json: { error: :unfurler_requires_setup, config: link.unfurler.setup_config }, + status: :unprocessable_entity + elsif link.metadata render json: link.metadata else head :no_content end - rescue Link::BasecampUnfurler::MissingIntegrationError - render json: { error: :basecamp_integration_not_set_up }, status: :unprocessable_entity end private diff --git a/app/javascript/controllers/unfurl_link_controller.js b/app/javascript/controllers/unfurl_link_controller.js index e31cc23743..5cade3b4ed 100644 --- a/app/javascript/controllers/unfurl_link_controller.js +++ b/app/javascript/controllers/unfurl_link_controller.js @@ -19,7 +19,7 @@ export default class extends Controller { } setUpBasecampIntegration() { - this.#openPopup(this.setUpBasecampIntegrationUrlValue, { widht: 400, height: 600 }) + this.#openPopup(this.setUpBasecampIntegrationUrlValue, { width: 400, height: 600 }) } closePrompt(event) { @@ -65,9 +65,9 @@ export default class extends Controller { #handleError({ error, ...data }) { switch (error) { - case "basecamp_integration_not_set_up": - if (this.#shouldShowAccountLinkingPrompt()) { - this.#promptBasecampIntegrationSetUp() + case "unfurler_requires_setup": + if (this.#shouldShowUnfurlerSetupPrompt()) { + this.#promptUnfurlerSetUp(data.config) } break; default: @@ -76,7 +76,7 @@ export default class extends Controller { } } - #promptBasecampIntegrationSetUp() { + #promptUnfurlerSetUp() { this.linkAccountsPromptTarget.hidden = false } @@ -97,13 +97,13 @@ export default class extends Controller { this.#accountLinkingDismissed = true } - #shouldShowAccountLinkingPrompt() { + #shouldShowUnfurlerSetupPrompt() { const dismissalCount = this.#cookie.get(this.constructor.DISMISSAL_COUNTER_KEY) || 0 return !this.#accountLinkingDismissed && (dismissalCount < this.constructor.MAX_DISMISSAL_COUNT) } get #cookie() { - const name = `link-accounts-prompt-${Current.user.id}` + const name = `link-unfurler-setup-prompt` return Cookie.find(name) || new Cookie(name) } } diff --git a/app/models/link.rb b/app/models/link.rb index 316f65ee88..14a6015dbf 100644 --- a/app/models/link.rb +++ b/app/models/link.rb @@ -4,25 +4,30 @@ class Link BasecampUnfurler ] - attr_reader :uri, :metadata + attr_reader :uri, :metadata, :user def self.unfurl(url, **options) new(url).unfurl(**options) end - def initialize(url) + def initialize(url, user: Current.user) @uri = URI.parse(url) @metadata = nil + @user = user end - def unfurl(**options) - options[:user] = Current.user unless options.key?(:user) - unfurler = UNFURLERS.find { |unfurler| unfurler.unfurls?(uri) } - - if unfurler - @metadata = unfurler.new(uri, **options).unfurl + def unfurl + if unfurler&.setup? + @metadata = unfurler.unfurl end self end + + def unfurler + @unfurler ||= begin + unfurler = UNFURLERS.find { |unfurler| unfurler.unfurls?(uri) } + unfurler&.new(uri, user: user) + end + end end diff --git a/app/models/link/fetch.rb b/app/models/link/fetch.rb index 96a77c8650..5992675cf5 100644 --- a/app/models/link/fetch.rb +++ b/app/models/link/fetch.rb @@ -1,8 +1,12 @@ class Link::Fetch class Error < StandardError; end + class TooManyRedirectsError < Error; end + class RedirectDeniedError < Error; end + class BodyTooLargeError < Error; end + class UnsuccesfulRequestError < Error attr_reader :response diff --git a/app/models/link/fizzy_unfurler.rb b/app/models/link/fizzy_unfurler.rb index a328f06a37..cf09dbe287 100644 --- a/app/models/link/fizzy_unfurler.rb +++ b/app/models/link/fizzy_unfurler.rb @@ -27,6 +27,14 @@ def initialize(uri, user:, **) @user = user end + def requires_setup? + false + end + + def setup_config + nil + end + def unfurl tenant, path = extract_tenant_from_path(uri.path) diff --git a/app/models/link/open_graph_unfurler.rb b/app/models/link/open_graph_unfurler.rb index cda834a0a8..9a4e05c3f6 100644 --- a/app/models/link/open_graph_unfurler.rb +++ b/app/models/link/open_graph_unfurler.rb @@ -10,6 +10,14 @@ def initialize(uri, user: nil, **options) @user = user end + def requires_setup? + false + end + + def setup_config + nil + end + def unfurl fetch = Link::Fetch.new(uri) From 23623ac370a5f85283e1affa450e80113b570f24 Mon Sep 17 00:00:00 2001 From: "Stanko K.R." Date: Tue, 2 Dec 2025 15:47:15 +0100 Subject: [PATCH 8/9] Fix migrations after rebase --- Gemfile | 1 - Gemfile.saas.lock | 24 +++++++++++++++++++ ... => 20251202144419_create_integrations.rb} | 2 +- db/schema.rb | 11 ++++++++- 4 files changed, 35 insertions(+), 3 deletions(-) rename db/migrate/{20250919122919_create_integrations.rb => 20251202144419_create_integrations.rb} (71%) diff --git a/Gemfile b/Gemfile index 1fd6f696b0..7f7a463bbb 100644 --- a/Gemfile +++ b/Gemfile @@ -36,7 +36,6 @@ gem "net-http-persistent" gem "rubyzip", require: "zip" gem "mittens" gem "useragent", bc: "useragent" -gem "oauth2", "~> 2.0" # Operations gem "autotuner" diff --git a/Gemfile.saas.lock b/Gemfile.saas.lock index f0cfa6dc0d..1fff655ef9 100644 --- a/Gemfile.saas.lock +++ b/Gemfile.saas.lock @@ -235,6 +235,12 @@ GEM tzinfo faker (3.5.2) i18n (>= 1.8.11, < 2) + faraday (2.14.0) + faraday-net_http (>= 2.0, < 3.5) + json + logger + faraday-net_http (3.4.1) + net-http (>= 0.5.0) ffi (1.17.2-aarch64-linux-gnu) ffi (1.17.2-aarch64-linux-musl) ffi (1.17.2-arm-linux-gnu) @@ -251,6 +257,7 @@ GEM globalid (1.3.0) activesupport (>= 6.1) hashdiff (1.2.1) + hashie (5.0.0) i18n (1.14.7) concurrent-ruby (~> 1.0) image_processing (1.14.0) @@ -323,6 +330,10 @@ GEM mocha (2.8.2) ruby2_keywords (>= 0.0.5) msgpack (1.8.0) + multi_xml (0.7.2) + bigdecimal (~> 3.1) + net-http (0.6.0) + uri net-http-persistent (4.0.6) connection_pool (~> 2.2, >= 2.2.4) net-imap (0.5.12) @@ -354,6 +365,14 @@ GEM racc (~> 1.4) nokogiri (1.18.10-x86_64-linux-musl) racc (~> 1.4) + oauth2 (2.0.17) + faraday (>= 0.17.3, < 4.0) + jwt (>= 1.0, < 4.0) + logger (~> 1.2) + multi_xml (~> 0.5) + rack (>= 1.2, < 4) + snaky_hash (~> 2.0, >= 2.0.3) + version_gem (~> 1.1, >= 1.1.9) openssl (3.3.2) ostruct (0.6.3) parallel (1.27.0) @@ -493,6 +512,9 @@ GEM sentry-ruby (6.2.0) bigdecimal concurrent-ruby (~> 1.0, >= 1.0.2) + snaky_hash (2.0.3) + hashie (>= 0.1.0, < 6) + version_gem (>= 1.1.8, < 3) sniffer (0.5.0) anyway_config (>= 1.0) dry-initializer (~> 3) @@ -548,6 +570,7 @@ GEM uri (1.1.1) vcr (6.3.1) base64 + version_gem (1.1.9) web-console (4.2.1) actionview (>= 6.0.0) activemodel (>= 6.0.0) @@ -631,6 +654,7 @@ DEPENDENCIES mittens mocha net-http-persistent + oauth2 (~> 2.0) platform_agent prometheus-client-mmap (~> 1.3) propshaft diff --git a/db/migrate/20250919122919_create_integrations.rb b/db/migrate/20251202144419_create_integrations.rb similarity index 71% rename from db/migrate/20250919122919_create_integrations.rb rename to db/migrate/20251202144419_create_integrations.rb index 99ed32b70d..9eed74e091 100644 --- a/db/migrate/20250919122919_create_integrations.rb +++ b/db/migrate/20251202144419_create_integrations.rb @@ -3,7 +3,7 @@ def change create_table :integrations do |t| t.text :data t.string :type - t.belongs_to :owner, null: false, foreign_key: { to_table: :users } + t.belongs_to :owner, null: false, foreign_key: false t.timestamps end diff --git a/db/schema.rb b/db/schema.rb index b84d3c1603..8a9bf2b102 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -10,7 +10,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema[8.2].define(version: 2025_12_01_100607) do +ActiveRecord::Schema[8.2].define(version: 2025_12_02_144419) do create_table "accesses", id: :uuid, charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t| t.datetime "accessed_at" t.uuid "account_id", null: false @@ -321,6 +321,15 @@ t.index ["email_address"], name: "index_identities_on_email_address", unique: true end + create_table "integrations", charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t| + t.datetime "created_at", null: false + t.text "data" + t.bigint "owner_id", null: false + t.string "type" + t.datetime "updated_at", null: false + t.index ["owner_id"], name: "index_integrations_on_owner_id" + end + create_table "magic_links", id: :uuid, charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t| t.string "code", null: false t.datetime "created_at", null: false From 33effe6686e7807027ceadc886ce0f7e4bf26cc3 Mon Sep 17 00:00:00 2001 From: "Stanko K.R." Date: Tue, 2 Dec 2025 15:48:28 +0100 Subject: [PATCH 9/9] Remove reference to the basecamp unfurler --- app/models/link.rb | 1 - 1 file changed, 1 deletion(-) diff --git a/app/models/link.rb b/app/models/link.rb index 14a6015dbf..eb6b5b0601 100644 --- a/app/models/link.rb +++ b/app/models/link.rb @@ -1,7 +1,6 @@ class Link UNFURLERS = [ FizzyUnfurler, - BasecampUnfurler ] attr_reader :uri, :metadata, :user