UNPKG

@ribajs/core

Version:

Core module of Riba.js

809 lines 78.8 kB
import { jest } from '@jest/globals'; import { Riba, Binder } from "./index.js"; import { Data } from "../spec/lib/moch.data.js"; import { dotAdapter } from "./adapters/index.js"; import { TextBinder, HtmlBinder, EachStarBinder, ValueBinder, AddClassBinder, } from "./binders/index.js"; describe("riba.Binder", () => { const riba = new Riba(); riba.module.adapter.register(dotAdapter); riba.module.binder.register(TextBinder); riba.module.binder.register(HtmlBinder); riba.module.binder.register(ValueBinder); riba.module.binder.register(EachStarBinder); riba.module.binder.register(AddClassBinder); let model; let el; let view; let binding; let originalPrefix; let adapter; let routineFn; beforeEach(() => { originalPrefix = riba.prefix; riba.prefix = ["data"]; adapter = riba.adapters["."]; el = document.createElement("div"); el.setAttribute("data-text", "obj.name"); view = riba.bind(el, { obj: { name: "test" } }); binding = view.bindings[0]; model = binding.model; }); afterEach(() => { riba.prefix = originalPrefix; }); it("gets assigned the proper binder routine matching the identifier", () => { routineFn = binding.name; expect(routineFn).toEqual("text"); expect(routineFn).toEqual(riba.binders.text.key); }); describe("when bind to non configurable properties", () => { let data; beforeEach(() => { data = { name: "x", items: [], }; Object.defineProperty(data, "name", { enumerable: true, configurable: false, writable: true, value: "y", }); el.setAttribute("data-show", "obj.items.length"); }); it("does not throw", () => { expect(() => { riba.bind(el, { obj: data }); }).not.toThrow(); }); }); describe("with multiple prefixes", () => { beforeEach(() => { riba.prefix = ["foo", "bar", "any"]; el.setAttribute("foo-a", "'a'"); el.setAttribute("bar-b", "'b'"); el.setAttribute("any-c", "'c'"); el.setAttribute("rv-d", "'d'"); }); it("All prefixes should be applied", () => { expect(() => { riba.bind(el, {}); }).not.toThrow(); expect(el.getAttribute("a")).toEqual("a"); expect(el.getAttribute("b")).toEqual("b"); expect(el.getAttribute("c")).toEqual("c"); expect(el.getAttribute("d")).toBeNull(); }); }); describe("with formatters", () => { let valueInput; beforeEach(() => { const awesomeFormatter = { name: "awesome", read: (value) => "awesome " + value, }; const totallyFormatter = { name: "totally", read: (value, prefix) => prefix + " totally " + value, }; const placeholderFormatter = { name: "and", read: (value, affix) => value + affix, }; riba.module.formatter.register(awesomeFormatter); riba.module.formatter.register(totallyFormatter); riba.module.formatter.register(placeholderFormatter, "and", true); riba.module.formatter.register(placeholderFormatter, "radical", true); riba.module.formatter.register(placeholderFormatter, "totally", true); }); it("register all formatters", () => { valueInput = document.createElement("input"); valueInput.setAttribute("type", "text"); valueInput.setAttribute("data-value", "obj.name | awesome | radical | totally"); view = riba.bind(valueInput, { obj: { name: "nothing" } }); binding = view.bindings[0]; expect(binding.formatters).toEqual(["awesome", "radical", "totally"]); }); it("allows arguments with pipes", () => { valueInput = document.createElement("input"); valueInput.setAttribute("type", "text"); valueInput.setAttribute("data-value", `obj.name | awesome | totally 'arg | with || pipes' 'and more args' | and 'others formatters' with 'pi||pes'`); view = riba.bind(valueInput, { obj: { name: "nothing" } }); binding = view.bindings[0]; expect(binding.formatters).toEqual([ "awesome", `totally 'arg | with || pipes' 'and more args'`, `and 'others formatters' with 'pi||pes'`, ]); }); }); describe("bind()", () => { it("subscribes to the model for changes via the adapter", () => { jest.spyOn(adapter, "observe"); binding._bind(); expect(adapter.observe).toHaveBeenCalledWith(model, "name", binding); }); it(`calls the binder's bind method if one exists`, () => { expect(() => { binding._bind(); }).not.toThrow(); binding.bind = () => { return; }; jest.spyOn(binding, "bind"); binding._bind(); expect(binding.bind).toHaveBeenCalled(); }); describe("with preloadData set to true", () => { beforeEach(() => { riba.preloadData = true; }); it("sets the initial value", () => { jest.spyOn(binding, "set"); binding._bind(); expect(binding.set).toHaveBeenCalledWith("test"); }); }); }); describe("unbind()", () => { describe("without a binder.unbind defined", () => { it("should not throw an error", () => { expect(() => { binding._unbind(); }).not.toThrow(); }); }); describe("with a binder.unbind defined", () => { beforeEach(() => { binding.unbind = () => { }; }); it("should not throw an error", () => { expect(() => { binding._unbind(); }).not.toThrow(); }); it(`calls the binder's unbind method`, () => { jest.spyOn(binding, "unbind"); binding._unbind(); expect(binding.unbind).toHaveBeenCalled(); }); }); }); describe("set()", () => { it("performs the binding routine with the supplied value", () => { routineFn = jest.spyOn(binding, "routine"); binding.set("sweater"); expect(routineFn).toHaveBeenCalledWith(el, "sweater"); }); it("applies any formatters to the value before performing the routine", () => { if (!view.options.formatters) { throw new Error("formatters are undefined!"); } routineFn = jest.spyOn(binding, "routine"); view.options.formatters.awesome = { name: "awesome", read(value) { return "awesome " + value; }, }; if (binding.formatters) { binding.formatters.push("awesome"); } binding.set("sweater"); expect(binding.routine).toHaveBeenCalledWith(el, "awesome sweater"); }); }); describe("publish()", () => { let numberInput; it(`should publish the value of a number input`, () => { numberInput = document.createElement("input"); numberInput.setAttribute("type", "number"); numberInput.setAttribute("data-value", "obj.num"); view = riba.bind(numberInput, { obj: { num: 42 } }); binding = view.bindings[0]; model = binding.model; numberInput.value = "42"; jest.spyOn(adapter, "set"); binding.publish(); expect(adapter.set).toHaveBeenCalledWith(model, "num", "42"); }); }); describe("publishTwoWay()", () => { let numberInput; let valueInput; it("applies a two-way read formatter to function same as a single-way", () => { const awesomeFormatter = { read: (value) => "awesome " + value, }; riba.module.formatter.register(awesomeFormatter, "awesome"); routineFn = jest.spyOn(binding, "routine"); if (!binding.formatters) { throw new Error("Formatters not set!"); } binding.formatters.push("awesome"); binding.set("sweater"); expect(routineFn).toHaveBeenCalledWith(el, "awesome sweater"); }); it(`should publish the value of a number input`, () => { riba.formatters.awesome = { publish: (value) => "awesome " + value, }; numberInput = document.createElement("input"); numberInput.setAttribute("type", "number"); numberInput.setAttribute("data-value", "obj.num | awesome"); view = riba.bind(numberInput, { obj: { num: 42 } }); binding = view.bindings[0]; model = binding.model; numberInput.value = "42"; binding.publish(); expect(adapter.set).toHaveBeenCalledWith(model, "num", "awesome 42"); }); it(`should format a value in both directions`, () => { riba.formatters.awesome = { publish: (value) => "awesome " + value, read: (value) => value + " is awesome", }; jest.spyOn(binding, "routine"); valueInput = document.createElement("input"); valueInput.setAttribute("type", "text"); valueInput.setAttribute("data-value", "obj.name | awesome"); view = riba.bind(valueInput, { obj: { name: "nothing" } }); binding = view.bindings[0]; model = binding.model; jest.spyOn(binding, "routine"); valueInput.value = "charles"; binding.publish(); expect(binding.name).toEqual("value"); expect(adapter.set).toHaveBeenCalledWith(model, "name", "awesome charles"); binding.set("fred"); expect(binding.routine).toHaveBeenCalledWith(valueInput, "fred is awesome"); }); it(`should resolve formatter arguments to their values`, () => { riba.formatters.withArguments = { name: "withArguments", publish: (value, arg1, arg2) => { return value + ":" + arg1 + ":" + arg2; }, read: (value, arg1, arg2) => { return value.replace(":" + arg1 + ":" + arg2, ""); }, }; valueInput = document.createElement("input"); valueInput.setAttribute("type", "text"); valueInput.setAttribute("data-value", `obj.name | withArguments config.age 'male'`); view = riba.bind(valueInput, { obj: { name: "nothing", }, config: { age: 50, }, }); binding = view.bindings[0]; jest.spyOn(binding, "routine"); model = binding.model; expect(binding.name).toEqual("value"); valueInput.value = "bobby"; binding.publish(); expect(adapter.set).toHaveBeenCalledWith(model, "name", "bobby:50:male"); expect(valueInput.value).toEqual("bobby"); binding.set("andy:50:male"); expect(binding.routine).toHaveBeenCalledWith(valueInput, "andy"); }); it(`should resolve formatter arguments correctly with multiple formatters`, () => { riba.formatters.wrap = { name: "wrap", publish: (value, arg1, arg2) => { return arg1 + value + arg2; }, read: (value, arg1, arg2) => { return value.replace(arg1, "").replace(arg2, ""); }, }; riba.formatters.saveAsCase = { name: "saveAsCase", publish: (value, typeCase) => { return value["to" + typeCase + "Case"](); }, read: (value, typeCase) => { return value[typeCase === "Upper" ? "toLowerCase" : "toUpperCase"](); }, }; valueInput = document.createElement("input"); valueInput.setAttribute("type", "text"); valueInput.setAttribute("data-value", `obj.name | saveAsCase config.typeCase | wrap config.curly '}' | wrap config.square ']' | wrap config.paren ')'`); view = riba.bind(valueInput, { obj: { name: "nothing", }, config: { paren: "(", square: "[", curly: "{", typeCase: "Upper", }, }); binding = view.bindings[0]; model = binding.model; jest.spyOn(binding, "routine"); valueInput.value = "bobby"; binding.publish(); expect(adapter.set).toHaveBeenCalledWith(model, "name", "{[(BOBBY)]}"); expect(valueInput.value).toEqual("bobby"); binding.set("{[(ANDY)]}"); expect(binding.routine).toHaveBeenCalledWith(valueInput, "andy"); }); it(`should not fail or format if the specified binding function doesn't exist`, () => { riba.formatters.awesome = {}; valueInput = document.createElement("input"); valueInput.setAttribute("type", "text"); valueInput.setAttribute("data-value", "obj.name | awesome"); view = riba.bind(valueInput, { obj: { name: "nothing" } }); binding = view.bindings[0]; model = binding.model; jest.spyOn(binding, "routine"); valueInput.value = "charles"; binding.publish(); expect(adapter.set).toHaveBeenCalledWith(model, "name", "charles"); binding.set("fred"); expect(binding.routine).toHaveBeenCalledWith(valueInput, "fred"); }); it(`should apply read binders left to right, and write binders right to left`, () => { riba.formatters.totally = { name: "totally", publish: (value) => value + " totally", read: (value) => value + " totally", }; riba.formatters.awesome = { name: "awesome", publish: (value) => value + " is awesome", read: (value) => value + " is awesome", }; valueInput = document.createElement("input"); valueInput.setAttribute("type", "text"); valueInput.setAttribute("data-value", "obj.name | awesome | totally"); view = riba.bind(valueInput, { obj: { name: "nothing" } }); binding = view.bindings[0]; model = binding.model; jest.spyOn(binding, "routine"); binding.set("fred"); expect(binding.routine).toHaveBeenCalledWith(valueInput, "fred is awesome totally"); valueInput.value = "fred"; binding.publish(); expect(adapter.set).toHaveBeenCalledWith(model, "name", "fred totally is awesome"); }); it(`binders in a chain should be skipped if they're not there`, () => { riba.formatters.totally = { name: "totally", publish: (value) => value + " totally", read: (value) => value + " totally", }; riba.formatters.radical = { name: "radical", publish: (value) => value + " is radical", }; riba.formatters.awesome = { name: "awesome", read: (value) => value + " is awesome", }; valueInput = document.createElement("input"); valueInput.setAttribute("type", "text"); valueInput.setAttribute("data-value", "obj.name | awesome | radical | totally"); view = riba.bind(valueInput, { obj: { name: "nothing" } }); binding = view.bindings[0]; jest.spyOn(binding, "routine"); expect(binding.name).toEqual("value"); model = binding.model; binding.set("fred"); expect(binding.routine).toHaveBeenCalledWith(valueInput, "fred is awesome totally"); valueInput.value = "fred"; binding.publish(); expect(adapter.set).toHaveBeenCalledWith(model, "name", "fred totally is radical"); }); }); describe("formattedValue()", () => { it("applies the current formatters on the supplied value", () => { if (!view.options.formatters) { throw new Error("formatters are undefined!"); } view.options.formatters.awesome = { name: "awesome", read(value) { return "awesome " + value; }, }; if (!binding.formatters) { throw new Error("Formatters not set!"); } binding.formatters.push("awesome"); expect(binding.formattedValue("hat")).toEqual("awesome hat"); }); describe("with a multi-argument formatter string", () => { beforeEach(() => { if (!view.options.formatters) { throw new Error("formatters are undefined!"); } view.options.formatters.awesome = { name: "awesome", read(value, prefix) { return prefix + " awesome " + value; }, }; if (!binding.formatters) { throw new Error("Formatters not set!"); } binding.formatters.push(`awesome 'super'`); }); it("applies the formatter with arguments", () => { expect(binding.formattedValue("jacket")).toEqual("super awesome jacket"); }); }); describe("with a formatter string with pipes in argument", () => { beforeEach(() => { if (!view.options.formatters) { throw new Error("formatters are undefined!"); } view.options.formatters.totally = { name: "totally", read(value, prefix) { return prefix + " totally " + value; }, }; if (!binding.formatters) { throw new Error("Formatters not set!"); } binding.formatters.push(`totally 'arg | with || pipes'`); }); it("applies the formatter with arguments with pipes", () => { expect(binding.formattedValue("jacket")).toEqual("arg | with || pipes totally jacket"); }); }); }); describe("getValue()", () => { it("should use binder.getValue() if present", () => { binding.getValue = () => { return "foo"; }; expect(binding._getValue(el)).toEqual("foo"); }); it("binder.getValue() should have access to passed element", () => { binding.getValue = (_el) => { return _el.dataset.foo; }; el.dataset.foo = "bar"; expect(binding._getValue(el)).toEqual("bar"); }); it("binder.getValue() should have access to binding", () => { binding.getValue = function () { return this.foo; }; binding.foo = "bar"; expect(binding._getValue(el)).toEqual("bar"); }); }); }); describe("Functional", () => { const riba = new Riba(); riba.module.adapter.register(dotAdapter); riba.module.binder.register(TextBinder); riba.module.binder.register(HtmlBinder); riba.module.binder.register(ValueBinder); riba.module.binder.register(EachStarBinder); riba.module.binder.register(AddClassBinder); let data; let bindData; let el; let input; let originalPrefix; let adapter; beforeEach(() => { originalPrefix = riba.prefix; riba.prefix = ["data"]; adapter = { name: ":", observe: (obj, keypath, callback) => { obj.on(keypath, callback); }, unobserve: (obj, keypath, callback) => { obj.off(keypath, callback); }, get: (obj, keypath) => { return obj.get(keypath); }, set: (obj, keypath, value) => { const attributes = {}; attributes[keypath] = value; obj.set(attributes); }, }; riba.module.adapter.register(adapter); riba.configure({ preloadData: true }); data = new Data({ foo: "bar", items: [{ name: "a" }, { name: "b" }], }); bindData = { data }; el = document.createElement("div"); el.setAttribute("rv-value", ""); input = document.createElement("input"); input.setAttribute("type", "text"); }); afterEach(() => { riba.prefix = originalPrefix; }); describe("Binds", () => { describe("Text", () => { it("should set the text content of the element", () => { el.setAttribute("data-text", "data:foo"); riba.bind(el, bindData); expect(el.textContent).toEqual(data.get("foo")); }); it("should correctly handle HTML in the content", () => { el.setAttribute("data-text", "data:foo"); const value = "<b>Fail</b>"; data.set({ foo: value }); riba.bind(el, bindData); expect(el.textContent).toEqual(value); }); }); describe("HTML", () => { it("should set the html content of the element", () => { el.setAttribute("data-html", "data:foo"); riba.bind(el, bindData); expect(el.textContent).toEqual(data.get("foo")); }); it("should correctly handle HTML in the content", () => { el.setAttribute("data-html", "data:foo"); const value = "<b>Fail</b>"; data.set({ foo: value }); riba.bind(el, bindData); expect(el.innerHTML).toEqual(value); }); }); describe("Value", () => { it("should set the value of the element", () => { input.setAttribute("data-value", "data:foo"); riba.bind(input, bindData); expect(input.value).toEqual(data.get("foo")); }); }); describe("Multiple", () => { it("should bind a list of multiple elements", () => { el.setAttribute("data-html", "data:foo"); input.setAttribute("data-value", "data:foo"); riba.bind([el, input], bindData); expect(el.textContent).toEqual(data.get("foo")); expect(input.value).toEqual(data.get("foo")); }); }); describe("Priority", () => { let mockA; let mockB; let mockC; let mockD; let mockE; let mockF; let mockG; beforeEach(() => { mockA = jest.fn(); mockB = jest.fn(); mockC = jest.fn(); mockD = jest.fn(); mockE = jest.fn(); mockF = jest.fn(); mockG = jest.fn(); class ABinder extends Binder { static key = "a"; bind = mockA; routine() { } } class BBinder extends Binder { static key = "b"; bind = mockB; routine() { } } class CBinder extends Binder { static key = "c"; priority = 10; bind = mockC; routine() { } } class DBinder extends Binder { static key = "d"; priority = 30; bind = mockD; routine() { } } class EBinder extends Binder { static key = "e"; priority = 5; bind = mockE; routine() { } } class FBinder extends Binder { static key = "f"; priority = 2; bind = mockF; routine() { } } class GBinder extends Binder { static key = "g"; priority = 1; bind = mockG; routine() { } } riba.module.binder.register(ABinder); riba.module.binder.register(BBinder); riba.module.binder.register(CBinder); riba.module.binder.register(DBinder); riba.module.binder.register(EBinder); riba.module.binder.register(FBinder); riba.module.binder.register(GBinder); el.setAttribute("data-a", "data:foo"); el.setAttribute("data-b", "data:foo"); el.setAttribute("data-c", "data:foo"); el.setAttribute("data-d", "data:foo"); el.setAttribute("data-e", "data:foo"); el.setAttribute("data-f", "data:foo"); el.setAttribute("data-g", "data:foo"); }); describe("a:10, b:30", () => { beforeEach(() => { const view = riba.bind(el, bindData); const c = view.bindings[1]; const d = view.bindings[0]; expect(c.name).toEqual("c"); expect(c.priority).toEqual(10); expect(d.name).toEqual("d"); expect(d.priority).toEqual(30); }); it("should bind d before c", () => { expect(mockD).toHaveBeenCalledBefore(mockC); }); }); describe("a:5, b:2", () => { beforeEach(() => { const view = riba.bind(el, bindData); const e = view.bindings[2]; const f = view.bindings[3]; expect(e.name).toEqual("e"); expect(e.priority).toEqual(5); expect(f.name).toEqual("f"); expect(f.priority).toEqual(2); }); it("should bind e before f", () => { expect(mockE).toHaveBeenCalledBefore(mockF); }); }); describe("a:undefined, g:1", () => { beforeEach(() => { const view = riba.bind(el, bindData); const g = view.bindings[4]; expect(g.name).toEqual("g"); expect(g.priority).toEqual(1); riba.bind(el, bindData); }); it("should bind g before a", () => { expect(mockG).toHaveBeenCalledBefore(mockA); }); }); }); describe("Iteration", () => { let listItem; let list; beforeEach(() => { list = document.createElement("ul"); el.appendChild(list); listItem = document.createElement("li"); listItem.setAttribute("data-each-item", "data:items"); list.appendChild(listItem); }); it("should loop over a collection and create new instances of that element + children", () => { expect(el.getElementsByTagName("li").length).toEqual(1); riba.bind(el, bindData); expect(el.getElementsByTagName("li").length).toEqual(2); }); it("should not fail if the collection being bound to is null", () => { data.set({ items: null }); riba.bind(el, bindData); expect(el.getElementsByTagName("li").length).toEqual(0); }); it("should re-loop over the collection and create new instances when the array changes", () => { riba.bind(el, bindData); expect(el.getElementsByTagName("li").length).toEqual(2); const newItems = [{ name: "a" }, { name: "b" }, { name: "c" }]; data.set({ items: newItems }); expect(el.getElementsByTagName("li").length).toEqual(3); }); it("should allow binding to the iterated item as well as any parent contexts", () => { const span1 = document.createElement("span"); span1.setAttribute("data-text", "item.name"); const span2 = document.createElement("span"); span2.setAttribute("data-text", "data:foo"); listItem.appendChild(span1); listItem.appendChild(span2); riba.bind(el, bindData); expect(el.getElementsByTagName("span")[0].textContent).toEqual("a"); expect(el.getElementsByTagName("span")[1].textContent).toEqual("bar"); }); it("should allow binding to the iterated element directly", () => { listItem.setAttribute("data-text", "item.name"); listItem.setAttribute("data-add-class", "data:foo"); riba.bind(el, bindData); expect(el.getElementsByTagName("li")[0].textContent).toEqual("a"); expect(el.getElementsByTagName("li")[0].className).toEqual("bar"); }); it("should insert items between any surrounding elements", () => { const firstItem = document.createElement("li"); const lastItem = document.createElement("li"); firstItem.textContent = "first"; lastItem.textContent = "last"; list.appendChild(lastItem); list.insertBefore(firstItem, listItem); listItem.setAttribute("data-text", "item.name"); riba.bind(el, bindData); expect(el.getElementsByTagName("li")[0].textContent).toEqual("first"); expect(el.getElementsByTagName("li")[1].textContent).toEqual("a"); expect(el.getElementsByTagName("li")[2].textContent).toEqual("b"); expect(el.getElementsByTagName("li")[3].textContent).toEqual("last"); }); it.skip("should allow binding to the iterated element index", () => { listItem.setAttribute("data-index", "%item%"); riba.bind(el, bindData); expect(el.getElementsByTagName("li")[0].getAttribute("index")).toEqual("0"); expect(el.getElementsByTagName("li")[1].getAttribute("index")).toEqual("1"); }); it.skip("should allow the developer to configure the index attribute available in the iteration", () => { listItem.setAttribute("data-index", "itemIndex"); listItem.setAttribute("index-property", "itemIndex"); riba.bind(el, bindData); expect(el.outerHTML).toEqual(""); expect(el.getElementsByTagName("li")[0].getAttribute("index")).toEqual("0"); expect(el.getElementsByTagName("li")[1].getAttribute("index")).toEqual("1"); }); }); }); describe("Updates", () => { it("should change the value", () => { el.setAttribute("data-text", "data:foo"); riba.bind(el, bindData); data.set({ foo: "some new value" }); expect(el.textContent).toEqual(data.get("foo")); }); }); describe("Input", () => { it("should update the model value", () => { input.setAttribute("data-value", "data:foo"); riba.bind(input, bindData); input.value = "some new value"; const event = document.createEvent("HTMLEvents"); event.initEvent("input", true, true); input.dispatchEvent(event); expect(data.get("foo")).toEqual("some new value"); }); it("should allow to change the event listened", () => { let event; input.setAttribute("data-value", "data:foo"); input.setAttribute("event-name", "blur"); riba.bind(input, bindData); input.value = "some new value"; event = document.createEvent("HTMLEvents"); event.initEvent("input", true, true); input.dispatchEvent(event); expect(data.get("foo")).toEqual("bar"); event = document.createEvent("HTMLEvents"); event.initEvent("blur", true, true); input.dispatchEvent(event); expect(data.get("foo")).toEqual("some new value"); }); }); }); //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYmluZGVyLnNwZWMuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvYmluZGVyLnNwZWMudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxFQUFFLElBQUksRUFBRSxNQUFNLGVBQWUsQ0FBQztBQUNyQyxPQUFPLEVBQUUsSUFBSSxFQUFRLE1BQU0sRUFBRSxNQUFNLFlBQVksQ0FBQztBQUNoRCxPQUFPLEVBQUUsSUFBSSxFQUFFLE1BQU0sMEJBQTBCLENBQUM7QUFDaEQsT0FBTyxFQUFFLFVBQVUsRUFBRSxNQUFNLHFCQUFxQixDQUFDO0FBQ2pELE9BQU8sRUFDTCxVQUFVLEVBQ1YsVUFBVSxFQUNWLGNBQWMsRUFDZCxXQUFXLEVBQ1gsY0FBYyxHQUNmLE1BQU0sb0JBQW9CLENBQUM7QUFHNUIsUUFBUSxDQUFDLGFBQWEsRUFBRSxHQUFHLEVBQUU7SUFDM0IsTUFBTSxJQUFJLEdBQUcsSUFBSSxJQUFJLEVBQUUsQ0FBQztJQUN4QixJQUFJLENBQUMsTUFBTSxDQUFDLE9BQU8sQ0FBQyxRQUFRLENBQUMsVUFBVSxDQUFDLENBQUM7SUFDekMsSUFBSSxDQUFDLE1BQU0sQ0FBQyxNQUFNLENBQUMsUUFBUSxDQUFDLFVBQVUsQ0FBQyxDQUFDO0lBQ3hDLElBQUksQ0FBQyxNQUFNLENBQUMsTUFBTSxDQUFDLFFBQVEsQ0FBQyxVQUFVLENBQUMsQ0FBQztJQUN4QyxJQUFJLENBQUMsTUFBTSxDQUFDLE1BQU0sQ0FBQyxRQUFRLENBQUMsV0FBVyxDQUFDLENBQUM7SUFDekMsSUFBSSxDQUFDLE1BQU0sQ0FBQyxNQUFNLENBQUMsUUFBUSxDQUFDLGNBQWMsQ0FBQyxDQUFDO0lBQzVDLElBQUksQ0FBQyxNQUFNLENBQUMsTUFBTSxDQUFDLFFBQVEsQ0FBQyxjQUFjLENBQUMsQ0FBQztJQUU1QyxJQUFJLEtBQWEsQ0FBQztJQUNsQixJQUFJLEVBQWUsQ0FBQztJQUNwQixJQUFJLElBQVUsQ0FBQztJQUNmLElBQUksT0FBaUMsQ0FBQztJQUN0QyxJQUFJLGNBQXdCLENBQUM7SUFDN0IsSUFBSSxPQUFnQixDQUFDO0lBQ3JCLElBQUksU0FBUyxDQUFDO0lBRWQsVUFBVSxDQUFDLEdBQUcsRUFBRTtRQUNkLGNBQWMsR0FBRyxJQUFJLENBQUMsTUFBTSxDQUFDO1FBQzdCLElBQUksQ0FBQyxNQUFNLEdBQUcsQ0FBQyxNQUFNLENBQUMsQ0FBQztRQUN2QixPQUFPLEdBQUcsSUFBSSxDQUFDLFFBQVEsQ0FBQyxHQUFHLENBQUMsQ0FBQztRQUU3QixFQUFFLEdBQUcsUUFBUSxDQUFDLGFBQWEsQ0FBQyxLQUFLLENBQUMsQ0FBQztRQUNuQyxFQUFFLENBQUMsWUFBWSxDQUFDLFdBQVcsRUFBRSxVQUFVLENBQUMsQ0FBQztRQUV6QyxJQUFJLEdBQUcsSUFBSSxDQUFDLElBQUksQ0FBQyxFQUFFLEVBQUUsRUFBRSxHQUFHLEVBQUUsRUFBRSxJQUFJLEVBQUUsTUFBTSxFQUFFLEVBQUUsQ0FBQyxDQUFDO1FBQ2hELE9BQU8sR0FBRyxJQUFJLENBQUMsUUFBUSxDQUFDLENBQUMsQ0FBZSxDQUFDO1FBQ3pDLEtBQUssR0FBRyxPQUFPLENBQUMsS0FBSyxDQUFDO0lBQ3hCLENBQUMsQ0FBQyxDQUFDO0lBRUgsU0FBUyxDQUFDLEdBQUcsRUFBRTtRQUNiLElBQUksQ0FBQyxNQUFNLEdBQUcsY0FBYyxDQUFDO0lBQy9CLENBQUMsQ0FBQyxDQUFDO0lBRUgsRUFBRSxDQUFDLGlFQUFpRSxFQUFFLEdBQUcsRUFBRTtRQUN6RSxTQUFTLEdBQUcsT0FBTyxDQUFDLElBQUksQ0FBQztRQUN6QixNQUFNLENBQUMsU0FBUyxDQUFDLENBQUMsT0FBTyxDQUFDLE1BQU0sQ0FBQyxDQUFDO1FBQ2xDLE1BQU0sQ0FBQyxTQUFTLENBQUMsQ0FBQyxPQUFPLENBQUMsSUFBSSxDQUFDLE9BQU8sQ0FBQyxJQUFJLENBQUMsR0FBRyxDQUFDLENBQUM7SUFDbkQsQ0FBQyxDQUFDLENBQUM7SUFFSCxRQUFRLENBQUMsMENBQTBDLEVBQUUsR0FBRyxFQUFFO1FBQ3hELElBQUksSUFBUyxDQUFDO1FBQ2QsVUFBVSxDQUFDLEdBQUcsRUFBRTtZQUNkLElBQUksR0FBRztnQkFDTCxJQUFJLEVBQUUsR0FBRztnQkFDVCxLQUFLLEVBQUUsRUFBRTthQUNWLENBQUM7WUFDRixNQUFNLENBQUMsY0FBYyxDQUFDLElBQUksRUFBRSxNQUFNLEVBQUU7Z0JBQ2xDLFVBQVUsRUFBRSxJQUFJO2dCQUNoQixZQUFZLEVBQUUsS0FBSztnQkFDbkIsUUFBUSxFQUFFLElBQUk7Z0JBQ2QsS0FBSyxFQUFFLEdBQUc7YUFDWCxDQUFDLENBQUM7WUFDSCxFQUFFLENBQUMsWUFBWSxDQUFDLFdBQVcsRUFBRSxrQkFBa0IsQ0FBQyxDQUFDO1FBQ25ELENBQUMsQ0FBQyxDQUFDO1FBRUgsRUFBRSxDQUFDLGdCQUFnQixFQUFFLEdBQUcsRUFBRTtZQUN4QixNQUFNLENBQUMsR0FBRyxFQUFFO2dCQUNWLElBQUksQ0FBQyxJQUFJLENBQUMsRUFBRSxFQUFFLEVBQUUsR0FBRyxFQUFFLElBQUksRUFBRSxDQUFDLENBQUM7WUFDL0IsQ0FBQyxDQUFDLENBQUMsR0FBRyxDQUFDLE9BQU8sRUFBRSxDQUFDO1FBQ25CLENBQUMsQ0FBQyxDQUFDO0lBQ0wsQ0FBQyxDQUFDLENBQUM7SUFFSCxRQUFRLENBQUMsd0JBQXdCLEVBQUUsR0FBRyxFQUFFO1FBQ3RDLFVBQVUsQ0FBQyxHQUFHLEVBQUU7WUFDZCxJQUFJLENBQUMsTUFBTSxHQUFHLENBQUMsS0FBSyxFQUFFLEtBQUssRUFBRSxLQUFLLENBQUMsQ0FBQztZQUdwQyxFQUFFLENBQUMsWUFBWSxDQUFDLE9BQU8sRUFBRSxLQUFLLENBQUMsQ0FBQztZQUNoQyxFQUFFLENBQUMsWUFBWSxDQUFDLE9BQU8sRUFBRSxLQUFLLENBQUMsQ0FBQztZQUNoQyxFQUFFLENBQUMsWUFBWSxDQUFDLE9BQU8sRUFBRSxLQUFLLENBQUMsQ0FBQztZQUdoQyxFQUFFLENBQUMsWUFBWSxDQUFDLE1BQU0sRUFBRSxLQUFLLENBQUMsQ0FBQztRQUNqQyxDQUFDLENBQUMsQ0FBQztRQUVILEVBQUUsQ0FBQyxnQ0FBZ0MsRUFBRSxHQUFHLEVBQUU7WUFDeEMsTUFBTSxDQUFDLEdBQUcsRUFBRTtnQkFDVixJQUFJLENBQUMsSUFBSSxDQUFDLEVBQUUsRUFBRSxFQUFFLENBQUMsQ0FBQztZQUNwQixDQUFDLENBQUMsQ0FBQyxHQUFHLENBQUMsT0FBTyxFQUFFLENBQUM7WUFHakIsTUFBTSxDQUFDLEVBQUUsQ0FBQyxZQUFZLENBQUMsR0FBRyxDQUFDLENBQUMsQ0FBQyxPQUFPLENBQUMsR0FBRyxDQUFDLENBQUM7WUFDMUMsTUFBTSxDQUFDLEVBQUUsQ0FBQyxZQUFZLENBQUMsR0FBRyxDQUFDLENBQUMsQ0FBQyxPQUFPLENBQUMsR0FBRyxDQUFDLENBQUM7WUFDMUMsTUFBTSxDQUFDLEVBQUUsQ0FBQyxZQUFZLENBQUMsR0FBRyxDQUFDLENBQUMsQ0FBQyxPQUFPLENBQUMsR0FBRyxDQUFDLENBQUM7WUFHMUMsTUFBTSxDQUFDLEVBQUUsQ0FBQyxZQUFZLENBQUMsR0FBRyxDQUFDLENBQUMsQ0FBQyxRQUFRLEVBQUUsQ0FBQztRQUMxQyxDQUFDLENBQUMsQ0FBQztJQUNMLENBQUMsQ0FBQyxDQUFDO0lBRUgsUUFBUSxDQUFDLGlCQUFpQixFQUFFLEdBQUcsRUFBRTtRQUMvQixJQUFJLFVBQTRCLENBQUM7UUFFakMsVUFBVSxDQUFDLEdBQUcsRUFBRTtZQUNkLE1BQU0sZ0JBQWdCLEdBQUc7Z0JBQ3ZCLElBQUksRUFBRSxTQUFTO2dCQUNmLElBQUksRUFBRSxDQUFDLEtBQUssRUFBRSxFQUFFLENBQUMsVUFBVSxHQUFHLEtBQUs7YUFDdkIsQ0FBQztZQUVmLE1BQU0sZ0JBQWdCLEdBQUc7Z0JBQ3ZCLElBQUksRUFBRSxTQUFTO2dCQUNmLElBQUksRUFBRSxDQUFDLEtBQUssRUFBRSxNQUFNLEVBQUUsRUFBRSxDQUFDLE1BQU0sR0FBRyxXQUFXLEdBQUcsS0FBSzthQUN6QyxDQUFDO1lBRWYsTUFBTSxvQkFBb0IsR0FBRztnQkFDM0IsSUFBSSxFQUFFLEtBQUs7Z0JBQ1gsSUFBSSxFQUFFLENBQUMsS0FBSyxFQUFFLEtBQUssRUFBRSxFQUFFLENBQUMsS0FBSyxHQUFHLEtBQUs7YUFDekIsQ0FBQztZQUVmLElBQUksQ0FBQyxNQUFNLENBQUMsU0FBUyxDQUFDLFFBQVEsQ0FBQyxnQkFBZ0IsQ0FBQyxDQUFDO1lBQ2pELElBQUksQ0FBQyxNQUFNLENBQUMsU0FBUyxDQUFDLFFBQVEsQ0FBQyxnQkFBZ0IsQ0FBQyxDQUFDO1lBQ2pELElBQUksQ0FBQyxNQUFNLENBQUMsU0FBUyxDQUFDLFFBQVEsQ0FBQyxvQkFBb0IsRUFBRSxLQUFLLEVBQUUsSUFBSSxDQUFDLENBQUM7WUFDbEUsSUFBSSxDQUFDLE1BQU0sQ0FBQyxTQUFTLENBQUMsUUFBUSxDQUFDLG9CQUFvQixFQUFFLFNBQVMsRUFBRSxJQUFJLENBQUMsQ0FBQztZQUN0RSxJQUFJLENBQUMsTUFBTSxDQUFDLFNBQVMsQ0FBQyxRQUFRLENBQUMsb0JBQW9CLEVBQUUsU0FBUyxFQUFFLElBQUksQ0FBQyxDQUFDO1FBQ3hFLENBQUMsQ0FBQyxDQUFDO1FBRUgsRUFBRSxDQUFDLHlCQUF5QixFQUFFLEdBQUcsRUFBRTtZQUNqQyxVQUFVLEdBQUcsUUFBUSxDQUFDLGFBQWEsQ0FBQyxPQUFPLENBQUMsQ0FBQztZQUM3QyxVQUFVLENBQUMsWUFBWSxDQUFDLE1BQU0sRUFBRSxNQUFNLENBQUMsQ0FBQztZQUN4QyxVQUFVLENBQUMsWUFBWSxDQUNyQixZQUFZLEVBQ1osd0NBQXdDLENBQ3pDLENBQUM7WUFFRixJQUFJLEdBQUcsSUFBSSxDQUFDLElBQUksQ0FBQyxVQUFVLEVBQUUsRUFBRSxHQUFHLEVBQUUsRUFBRSxJQUFJLEVBQUUsU0FBUyxFQUFFLEVBQUUsQ0FBQyxDQUFDO1lBQzNELE9BQU8sR0FBRyxJQUFJLENBQUMsUUFBUSxDQUFDLENBQUMsQ0FBZSxDQUFDO1lBQ3pDLE1BQU0sQ0FBQyxPQUFPLENBQUMsVUFBVSxDQUFDLENBQUMsT0FBTyxDQUFDLENBQUMsU0FBUyxFQUFFLFNBQVMsRUFBRSxTQUFTLENBQUMsQ0FBQyxDQUFDO1FBQ3hFLENBQUMsQ0FBQyxDQUFDO1FBRUgsRUFBRSxDQUFDLDZCQUE2QixFQUFFLEdBQUcsRUFBRTtZQUNyQyxVQUFVLEdBQUcsUUFBUSxDQUFDLGFBQWEsQ0FBQyxPQUFPLENBQUMsQ0FBQztZQUM3QyxVQUFVLENBQUMsWUFBWSxDQUFDLE1BQU0sRUFBRSxNQUFNLENBQUMsQ0FBQztZQUN4QyxVQUFVLENBQUMsWUFBWSxDQUNyQixZQUFZLEVBQ1osNkdBQTZHLENBQzlHLENBQUM7WUFFRixJQUFJLEdBQUcsSUFBSSxDQUFDLElBQUksQ0FBQyxVQUFVLEVBQUUsRUFBRSxHQUFHLEVBQUUsRUFBRSxJQUFJLEVBQUUsU0FBUyxFQUFFLEVBQUUsQ0FBQyxDQUFDO1lBQzNELE9BQU8sR0FBRyxJQUFJLENBQUMsUUFBUSxDQUFDLENBQUMsQ0FBZSxDQUFDO1lBRXpDLE1BQU0sQ0FBQyxPQUFPLENBQUMsVUFBVSxDQUFDLENBQUMsT0FBTyxDQUFDO2dCQUNqQyxTQUFTO2dCQUNULCtDQUErQztnQkFDL0Msd0NBQXdDO2FBQ3pDLENBQUMsQ0FBQztRQUNMLENBQUMsQ0FBQyxDQUFDO0lBQ0wsQ0FBQyxDQUFDLENBQUM7SUFFSCxRQUFRLENBQUMsUUFBUSxFQUFFLEdBQUcsRUFBRTtRQUN0QixFQUFFLENBQUMscURBQXFELEVBQUUsR0FBRyxFQUFFO1lBQzdELElBQUksQ0FBQyxLQUFLLENBQUMsT0FBTyxFQUFFLFNBQVMsQ0FBQyxDQUFDO1lBQy9CLE9BQU8sQ0FBQyxLQUFLLEVBQUUsQ0FBQztZQUNoQixNQUFNLENBQUMsT0FBTyxDQUFDLE9BQU8sQ0FBQyxDQUFDLG9CQUFvQixDQUFDLEtBQUssRUFBRSxNQUFNLEVBQUUsT0FBTyxDQUFDLENBQUM7UUFDdkUsQ0FBQyxDQUFDLENBQUM7UUFFSCxFQUFFLENBQUMsOENBQThDLEVBQUUsR0FBRyxFQUFFO1lBQ3RELE1BQU0sQ0FBQyxHQUFHLEVBQUU7Z0JBQ1YsT0FBTyxDQUFDLEtBQUssRUFBRSxDQUFDO1lBQ2xCLENBQUMsQ0FBQyxDQUFDLEdBQUcsQ0FBQyxPQUFPLEVBQUUsQ0FBQztZQUVqQixPQUFPLENBQUMsSUFBSSxHQUFHLEdBQUcsRUFBRTtnQkFDbEIsT0FBTztZQUNULENBQUMsQ0FBQztZQUNGLElBQUksQ0FBQyxLQUFLLENBQUMsT0FBTyxFQUFFLE1BQWEsQ0FBQyxDQUFDO1lBRW5DLE9BQU8sQ0FBQyxLQUFLLEVBQUUsQ0FBQztZQUNoQixNQUFNLENBQUMsT0FBTyxDQUFDLElBQUksQ0FBQyxDQUFDLGdCQUFnQixFQUFFLENBQUM7UUFDMUMsQ0FBQyxDQUFDLENBQUM7UUFFSCxRQUFRLENBQUMsOEJBQThCLEVBQUUsR0FBRyxFQUFFO1lBQzVDLFVBQVUsQ0FBQyxHQUFHLEVBQUU7Z0JBQ2QsSUFBSSxDQUFDLFdBQVcsR0FBRyxJQUFJLENBQUM7WUFDMUIsQ0FBQyxDQUFDLENBQUM7WUFFSCxFQUFFLENBQUMsd0JBQXdCLEVBQUUsR0FBRyxFQUFFO2dCQUNoQyxJQUFJLENBQUMsS0FBSyxDQUFDLE9BQU8sRUFBRSxLQUFLLENBQUMsQ0FBQztnQkFDM0IsT0FBTyxDQUFDLEtBQUssRUFBRSxDQUFDO2dCQUNoQixNQUFNLENBQUMsT0FBTyxDQUFDLEdBQUcsQ0FBQyxDQUFDLG9CQUFvQixDQUFDLE1BQU0sQ0FBQyxDQUFDO1lBQ25ELENBQUMsQ0FBQyxDQUFDO1FBQ0wsQ0FBQyxDQUFDLENBQUM7SUFDTCxDQUFDLENBQUMsQ0FBQztJQUVILFFBQVEsQ0FBQyxVQUFVLEVBQUUsR0FBRyxFQUFFO1FBQ3hCLFFBQVEsQ0FBQyxpQ0FBaUMsRUFBRSxHQUFHLEVBQUU7WUFDL0MsRUFBRSxDQUFDLDJCQUEyQixFQUFFLEdBQUcsRUFBRTtnQkFDbkMsTUFBTSxDQUFDLEdBQUcsRUFBRTtvQkFDVixPQUFPLENBQUMsT0FBTyxFQUFFLENBQUM7Z0JBQ3BCLENBQUMsQ0FBQyxDQUFDLEdBQUcsQ0FBQyxPQUFPLEVBQUUsQ0FBQztZQUNuQixDQUFDLENBQUMsQ0FBQztRQUNMLENBQUMsQ0FBQyxDQUFDO1FBRUgsUUFBUSxDQUFDLDhCQUE4QixFQUFFLEdBQUcsRUFBRTtZQUM1QyxVQUFVLENBQUMsR0FBRyxFQUFFO2dCQUNkLE9BQU8sQ0FBQyxNQUFNLEdBQUcsR0FBRyxFQUFFO2dCQUV0QixDQUFDLENBQUM7WUFDSixDQUFDLENBQUMsQ0FBQztZQUVILEVBQUUsQ0FBQywyQkFBMkIsRUFBRSxHQUFHLEVBQUU7Z0JBQ25DLE1BQU0sQ0FBQyxHQUFHLEVBQUU7b0JBQ1YsT0FBTyxDQUFDLE9BQU8sRUFBRSxDQUFDO2dCQUNwQixDQUFDLENBQUMsQ0FBQyxHQUFHLENBQUMsT0FBTyxFQUFFLENBQUM7WUFDbkIsQ0FBQyxDQUFDLENBQUM7WUFFSCxFQUFFLENBQUMsa0NBQWtDLEVBQUUsR0FBRyxFQUFFO2dCQUMxQyxJQUFJLENBQUMsS0FBSyxDQUFDLE9BQU8sRUFBRSxRQUFlLENBQUMsQ0FBQztnQkFDckMsT0FBTyxDQUFDLE9BQU8sRUFBRSxDQUFDO2dCQUNsQixNQUFNLENBQUMsT0FBTyxDQUFDLE1BQU0sQ0FBQyxDQUFDLGdCQUFnQixFQUFFLENBQUM7WUFDNUMsQ0FBQyxDQUFDLENBQUM7UUFDTCxDQUFDLENBQUMsQ0FBQztJQUNMLENBQUMsQ0FBQyxDQUFDO0lBRUgsUUFBUSxDQUFDLE9BQU8sRUFBRSxHQUFHLEVBQUU7UUFDckIsRUFBRSxDQUFDLHNEQUFzRCxFQUFFLEdBQUcsRUFBRTtZQUM5RCxTQUFTLEdBQUcsSUFBSSxDQUFDLEtBQUssQ0FBQyxPQUFPLEVBQUUsU0FBUyxDQUFDLENBQUM7WUFFM0MsT0FBTyxDQUFDLEdBQUcsQ0FBQyxTQUFTLENBQUMsQ0FBQztZQUN2QixNQUFNLENBQUMsU0FBUyxDQUFDLENBQUMsb0JBQW9CLENBQUMsRUFBRSxFQUFFLFNBQVMsQ0FBQyxDQUFDO1FBQ3hELENBQUMsQ0FBQyxDQUFDO1FBRUgsRUFBRSxDQUFDLG1FQUFtRSxFQUFFLEdBQUcsRUFBRTtZQUMzRSxJQUFJLENBQUMsSUFBSSxDQUFDLE9BQU8sQ0FBQyxVQUFVLEVBQUUsQ0FBQztnQkFDN0IsTUFBTSxJQUFJLEtBQUssQ0FBQywyQkFBMkIsQ0FBQyxDQUFDO1lBQy9DLENBQUM7WUFDRCxTQUFTLEdBQUcsSUFBSSxDQUFDLEtBQUssQ0FBQyxPQUFPLEVBQUUsU0FBUyxDQUFDLENBQUM7WUFDM0MsSUFBSSxDQUFDLE9BQU8sQ0FBQyxVQUFVLENBQUMsT0FBTyxHQUFHO2dCQUNoQyxJQUFJLEVBQUUsU0FBUztnQkFDZixJQUFJLENBQUMsS0FBYTtvQkFDaEIsT0FBTyxVQUFVLEdBQUcsS0FBSyxDQUFDO2dCQUM1QixDQUFDO2FBQ0YsQ0FBQztZQUVGLElBQUksT0FBTyxDQUFDLFVBQVUsRUFBRSxDQUFDO2dCQUN2QixPQUFPLENBQUMsVUFBVSxDQUFDLElBQUksQ0FBQyxTQUFTLENBQUMsQ0FBQztZQUNyQyxDQUFDO1lBQ0QsT0FBTyxDQUFDLEdBQUcsQ0FBQyxTQUFTLENBQUMsQ0FBQztZQUV2QixNQUFNLENBQUMsT0FBTyxDQUFDLE9BQU8sQ0FBQyxDQUFDLG9CQUFvQixDQUFDLEVBQUUsRUFBRSxpQkFBaUIsQ0FBQyxDQUFDO1FBQ3RFLENBQUMsQ0FBQyxDQUFDO0lBQ0wsQ0FBQyxDQUFDLENBQUM7SUFFSCxRQUFRLENBQUMsV0FBVyxFQUFFLEdBQUcsRUFBRTtRQUN6QixJQUFJLFdBQTZCLENBQUM7UUFDbEMsRUFBRSxDQUFDLDRDQUE0QyxFQUFFLEdBQUcsRUFBRTtZQUNwRCxXQUFXLEdBQUcsUUFBUSxDQUFDLGFBQWEsQ0FBQyxPQUFPLENBQUMsQ0FBQztZQUM5QyxXQUFXLENBQUMsWUFBWSxDQUFDLE1BQU0sRUFBRSxRQUFRLENBQUMsQ0FBQztZQUMzQyxXQUFXLENBQUMsWUFBWSxDQUFDLFlBQVksRUFBRSxTQUFTLENBQUMsQ0FBQztZQUVsRCxJQUFJLEdBQUcsSUFBSSxDQUFDLElBQUksQ0FBQyxXQUFXLEVBQUUsRUFBRSxHQUFHLEVBQUUsRUFBRSxHQUFHLEVBQUUsRUFBRSxFQUFFLEVBQUUsQ0FBQyxDQUFDO1lBQ3BELE9BQU8sR0FBRyxJQUFJLENBQUMsUUFBUSxDQUFDLENBQUMsQ0FBZSxDQUFDO1lBQ3pDLEtBQUssR0FBRyxPQUFPLENBQUMsS0FBSyxDQUFDO1lBRXRCLFdBQVcsQ0FBQyxLQUFLLEdBQUcsSUFBSSxDQUFDO1lBRXpCLElBQUksQ0FBQyxLQUFLLENBQUMsT0FBTyxFQUFFLEtBQUssQ0FBQyxDQUFDO1lBQzNCLE9BQU8sQ0FBQyxPQUFPLEVBQUUsQ0FBQztZQUNsQixNQUFNLENBQUMsT0FBTyxDQUFDLEdBQUcsQ0FBQyxDQUFDLG9CQUFvQixDQUFDLEtBQUssRUFBRSxLQUFLLEVBQUUsSUFBSSxDQUFDLENBQUM7UUFDL0QsQ0FBQyxDQUFDLENBQUM7SUFDTCxDQUFDLENBQUMsQ0FBQztJQUVILFFBQVEsQ0FBQyxpQkFBaUIsRUFBRSxHQUFHLEVBQUU7UUFDL0IsSUFBSSxXQUFXLENBQUM7UUFDaEIsSUFBSSxVQUFVLENBQUM7UUFDZixFQUFFLENBQUMsbUVBQW1FLEVBQUUsR0FBRyxFQUFFO1lBQzNFLE1BQU0sZ0JBQWdCLEdBQUc7Z0JBQ3ZCLElBQUksRUFBRSxDQUFDLEtBQUssRUFBRSxFQUFFLENBQUMsVUFBVSxHQUFHLEtBQUs7YUFDdkIsQ0FBQztZQUVmLElBQUksQ0FBQyxNQUFNLENBQUMsU0FBUyxDQUFDLFFBQVEsQ0FBQyxnQkFBZ0IsRUFBRSxTQUFTLENBQUMsQ0FBQztZQUU1RCxTQUFTLEdBQUcsSUFBSSxDQUFDLEtBQUssQ0FBQyxPQUFPLEVBQUUsU0FBUyxDQUFDLENBQUM7WUFFM0MsSUFBSSxDQUFDLE9BQU8sQ0FBQyxVQUFVLEVBQUUsQ0FBQztnQkFDeEIsTUFBTSxJQUFJLEtBQUssQ0FBQyxxQkFBcUIsQ0FBQyxDQUFDO1lBQ3pDLENBQUM7WUFFRCxPQUFPLENBQUMsVUFBVSxDQUFDLElBQUksQ0FBQyxTQUFTLENBQUMsQ0FBQztZQUVuQyxPQUFPLENBQUMsR0FBRyxDQUFDLFNBQVMsQ0FBQyxDQUFDO1lBQ3ZCLE1BQU0sQ0FBQyxTQUFTLENBQUMsQ0FBQyxvQkFBb0IsQ0FBQyxFQUFFLEVBQUUsaUJBQWlCLENBQUMsQ0FBQztRQUNoRSxDQUFDLENBQUMsQ0FBQztRQUVILEVBQUUsQ0FBQyw0Q0FBNEMsRUFBRSxHQUFHLEVBQUU7WUFFbkQsSUFBSSxDQUFDLFVBQVUsQ0FBQyxPQUFlLEdBQUc7Z0JBQ2pDLE9BQU8sRUFBRSxDQUFDLEtBQWEsRUFBRSxFQUFFLENBQUMsVUFBVSxHQUFHLEtBQUs7YUFDL0MsQ0FBQztZQUVGLFdBQVcsR0FBRyxRQUFRLENBQUMsYUFBYSxDQUFDLE9BQU8sQ0FBQyxDQUFDO1lBQzlDLFdBQVcsQ0FBQyxZQUFZLENBQUMsTUFBTSxFQUFFLFFBQVEsQ0FBQyxDQUFDO1lBQzNDLFdBQVcsQ0FBQyxZQUFZLENBQUMsWUFBWSxFQUFFLG1CQUFtQixDQUFDLENBQUM7WUFFNUQsSUFBSSxHQUFHLElBQUksQ0FBQyxJQUFJLENBQUMsV0FBVyxFQUFFLEVBQUUsR0FBRyxFQUFFLEVBQUUsR0FBRyxFQUFFLEVBQUUsRUFBRSxFQUFFLENBQUMsQ0FBQztZQUNwRCxPQUFPLEdBQUcsSUFBSSxDQUFDLFFBQVEsQ0FBQyxDQUFDLENBQWUsQ0FBQztZQUN6QyxLQUFLLEdBQUcsT0FBTyxDQUFDLEtBQUssQ0FBQztZQUV0QixXQUFXLENBQUMsS0FBSyxHQUFHLElBQUksQ0FBQztZQUV6QixPQUFPLENBQUMsT0FBTyxFQUFFLENBQUM7WUFDbEIsTUFBTSxDQUFDLE9BQU8sQ0FBQyxHQUFHLENBQUMsQ0FBQyxvQkFBb0IsQ0FBQyxLQUFLLEVBQUUsS0FBSyxFQUFFLFlBQVksQ0FBQyxDQUFDO1FBQ3ZFLENBQUMsQ0FBQyxDQUFDO1FBRUgsRUFBRSxDQUFDLDBDQUEwQyxFQUFFLEdBQUcsRUFBRTtZQUVqRCxJQUFJLENBQUMsVUFBVSxDQUFDLE9BQWUsR0FBRztnQkFDakMsT0FBTyxFQUFFLENBQUMsS0FBYSxFQUFFLEVBQUUsQ0FBQyxVQUFVLEdBQUcsS0FBSztnQkFDOUMsSUFBSSxFQUFFLENBQUMsS0FBYSxFQUFFLEVBQUUsQ0FBQyxLQUFLLEdBQUcsYUFBYTthQUMvQyxDQUFDO1lBRUYsSUFBSSxDQUFDLEtBQUssQ0FBQyxPQUFPLEVBQUUsU0FBUyxDQUFDLENBQUM7WUFFL0IsVUFBVSxHQUFHLFFBQVEsQ0FBQyxhQUFhLENBQUMsT0FBTyxDQUFDLENBQUM7WUFDN0MsVUFBVSxDQUFDLFlBQVksQ0FBQyxNQUFNLEVBQUUsTUFBTSxDQUFDLENBQUM7WUFDeEMsVUFBVSxDQUFDLFlBQVksQ0FBQyxZQUFZLEVBQUUsb0JBQW9CLENBQUMsQ0FBQztZQUU1RCxJQUFJLEdBQUcsSUFBSSxDQUFDLElBQUksQ0FBQyxVQUFVLEVBQUUsRUFBRSxHQUFHLEVBQUUsRUFBRSxJQUFJLEVBQUUsU0FBUyxFQUFFLEVBQUUsQ0FBQyxDQUFDO1lBQzNELE9BQU8sR0FBRyxJQUFJLENBQUMsUUFBUSxDQUFDLENBQUMsQ0FBZ0IsQ0FBQztZQUMxQyxLQUFLLEdBQUcsT0FBTyxDQUFDLEtBQUssQ0FBQztZQUV0QixJQUFJLENBQUMsS0FBSyxDQUFDLE9BQU8sRUFBRSxTQUFTLENBQUMsQ0FBQztZQUUvQixVQUFVLENBQUMsS0FBSyxHQUFHLFNBQVMsQ0FBQztZQUM3QixPQUFPLENBQUMsT0FBTyxFQUFFLENBQUM7WUFFbEIsTUFBTSxDQUFDLE9BQU8sQ0FBQyxJQUFJLENBQUMsQ0FBQyxPQUFPLENBQUMsT0FBTyxDQUFDLENBQUM7WUFFdEMsTUFBTSxDQUFDLE9BQU8sQ0FBQyxHQUFHLENBQUMsQ0FBQyxvQkFBb0IsQ0FDdEMsS0FBSyxFQUNMLE1BQU0sRUFDTixpQkFBaUIsQ0FDbEIsQ0FBQztZQUVGLE9BQU8sQ0FBQyxHQUFHLENBQUMsTUFBTSxDQUFDLENBQUM7WUFDcEIsTUFBTSxDQUFDLE9BQU8sQ0FBQyxPQUFPLENBQUMsQ0FBQyxvQkFBb0IsQ0FDMUMsVUFBVSxFQUNWLGlCQUFpQixDQUNsQixDQUFDO1FBQ0osQ0FBQyxDQUFDLENBQUM7UUFFSCxFQUFFLENBQUMsb0RBQW9ELEVBQUUsR0FBRyxFQUFFO1lBQzNELElBQUksQ0FBQyxVQUFVLENBQUMsYUFBMkIsR0FBRztnQkFDN0MsSUFBSSxFQUFFLGVBQWU7Z0JBQ3JCLE9BQU8sRUFBRSxDQUFDLEtBQUssRUFBRSxJQUFJLEVBQUUsSUFBSSxFQUFFLEVBQUU7b0JBQzdCLE9BQU8sS0FBSyxHQUFHLEdBQUcsR0FBRyxJQUFJLEdBQUcsR0FBRyxHQUFHLElBQUksQ0FBQztnQkFDekMsQ0FBQztnQkFDRCxJQUFJLEVBQUUsQ0FBQyxLQUFLLEVBQUUsSUFBSSxFQUFFLElBQUksRUFBRSxFQUFFO29CQUMxQixPQUFPLEtBQUssQ0FBQyxPQUFPLENBQUMsR0FBRyxHQUFHLElBQUksR0FBRyxHQUFHLEdBQUcsSUFBSSxFQUFFLEVBQUUsQ0FBQyxDQUFDO2dCQUNwRCxDQUFDO2FBQ0YsQ0FBQztZQUVGLFVBQVUsR0FBRyxRQUFRLENBQUMsYUFBYSxDQUFDLE9BQU8sQ0FBQyxDQUFDO1lBQzdDLFVBQVUsQ0FBQyxZQUFZLENBQUMsTUFBTSxFQUFFLE1BQU0sQ0FBQyxDQUFDO1lBQ3hDLFVBQVUsQ0FBQyxZQUFZLENBQ3JCLFlBQVksRUFDWiw0Q0FBNEMsQ0FDN0MsQ0FBQztZQUVGLElBQUksR0FBRyxJQUFJLENBQUMsSUFBSSxDQUFDLFVBQVUsRUFBRTtnQkFDM0IsR0FBRyxFQUFFO29CQUNILElBQUksRUFBRSxTQUFTO2lCQUNoQjtnQkFDRCxNQUFNLEVBQUU7b0JBQ04sR0FBRyxFQUFFLEVBQUU7aUJBQ1I7YUFDRixDQUFDLENBQUM7WUFFSCxPQUFPLEdBQUcsSUFBSSxDQUFDLFFBQVEsQ0FBQyxDQUFDLENBQWdCLENBQUM7WUFDMUMsSUFBSSxDQUFDLEtBQUssQ0FBQyxPQUFPLEVBQUUsU0FBUyxDQUFDLENBQUM7WUFDL0IsS0FBSyxHQUFHLE9BQU8sQ0FBQyxLQUFLLENBQUM7WUFFdEIsTUFBTSxDQUFDLE9BQU8sQ0FBQyxJQUFJLENBQUMsQ0FBQyxPQUFPLENBQUMsT0FBTyxDQUFDLENBQUM7WUFFdEMsVUFBVSxDQUFDLEtBQUssR0FBRyxPQUFPLENBQUM7WUFDM0IsT0FBTyxDQUFDLE9BQU8sRUFBRSxDQUFDO1lBQ2xCLE1BQU0sQ0FBQyxPQUFPLENBQUMsR0FBRyxDQUFDLENBQUMsb0JBQW9CLENBQUMsS0FBSyxFQUFFLE1BQU0sRUFBRSxlQUFlLENBQUMsQ0FBQztZQUV6RSxNQUFNLENBQUMsVUFBVSxDQUFDLEtBQUssQ0FBQyxDQUFDLE9BQU8sQ0FBQyxPQUFPLENBQUMsQ0FBQztZQUUxQyxPQUFPLENBQUMsR0FBRyxDQUFDLGNBQWMsQ0FBQyxDQUFDO1lBQzVCLE1BQU0sQ0FBQy