mirror of
https://github.com/Alex-2-Graf/Leningrad2-Upgrade-Kit.git
synced 2026-04-16 04:24:23 +03:00
4728 lines
277 KiB
HTML
4728 lines
277 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="en">
|
|
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<title>Interactive BOM for KiCAD</title>
|
|
<style type="text/css">
|
|
:root {
|
|
--pcb-edge-color: black;
|
|
--pad-color: #878787;
|
|
--pad-hole-color: #CCCCCC;
|
|
--pad-color-highlight: #D04040;
|
|
--pad-color-highlight-both: #D0D040;
|
|
--pad-color-highlight-marked: #44a344;
|
|
--pin1-outline-color: #ffb629;
|
|
--pin1-outline-color-highlight: #ffb629;
|
|
--pin1-outline-color-highlight-both: #fcbb39;
|
|
--pin1-outline-color-highlight-marked: #fdbe41;
|
|
--silkscreen-edge-color: #aa4;
|
|
--silkscreen-polygon-color: #4aa;
|
|
--silkscreen-text-color: #4aa;
|
|
--fabrication-edge-color: #907651;
|
|
--fabrication-polygon-color: #907651;
|
|
--fabrication-text-color: #a27c24;
|
|
--track-color: #def5f1;
|
|
--track-color-highlight: #D04040;
|
|
--zone-color: #def5f1;
|
|
--zone-color-highlight: #d0404080;
|
|
}
|
|
|
|
html,
|
|
body {
|
|
margin: 0px;
|
|
height: 100%;
|
|
font-family: Verdana, sans-serif;
|
|
}
|
|
|
|
.dark.topmostdiv {
|
|
--pcb-edge-color: #eee;
|
|
--pad-color: #808080;
|
|
--pin1-outline-color: #ffa800;
|
|
--pin1-outline-color-highlight: #ccff00;
|
|
--track-color: #42524f;
|
|
--zone-color: #42524f;
|
|
background-color: #252c30;
|
|
color: #eee;
|
|
}
|
|
|
|
button {
|
|
background-color: #eee;
|
|
border: 1px solid #888;
|
|
color: black;
|
|
height: 44px;
|
|
width: 44px;
|
|
text-align: center;
|
|
text-decoration: none;
|
|
display: inline-block;
|
|
font-size: 14px;
|
|
font-weight: bolder;
|
|
}
|
|
|
|
.dark button {
|
|
/* This will be inverted */
|
|
background-color: #c3b7b5;
|
|
}
|
|
|
|
button.depressed {
|
|
background-color: #0a0;
|
|
color: white;
|
|
}
|
|
|
|
.dark button.depressed {
|
|
/* This will be inverted */
|
|
background-color: #b3b;
|
|
}
|
|
|
|
button:focus {
|
|
outline: 0;
|
|
}
|
|
|
|
button#tb-btn {
|
|
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8.47 8.47'%3E%3Crect transform='translate(0 -288.53)' ry='1.17' y='288.8' x='.27' height='7.94' width='7.94' fill='%23f9f9f9'/%3E%3Cg transform='translate(0 -288.53)'%3E%3Crect width='7.94' height='7.94' x='.27' y='288.8' ry='1.17' fill='none' stroke='%23000' stroke-width='.4' stroke-linejoin='round'/%3E%3Cpath d='M1.32 290.12h5.82M1.32 291.45h5.82' fill='none' stroke='%23000' stroke-width='.4'/%3E%3Cpath d='M4.37 292.5v4.23M.26 292.63H8.2' fill='none' stroke='%23000' stroke-width='.3'/%3E%3Ctext font-weight='700' font-size='3.17' font-family='sans-serif'%3E%3Ctspan x='1.35' y='295.73'%3EF%3C/tspan%3E%3Ctspan x='5.03' y='295.68'%3EB%3C/tspan%3E%3C/text%3E%3C/g%3E%3C/svg%3E%0A");
|
|
}
|
|
|
|
button#lr-btn {
|
|
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8.47 8.47'%3E%3Crect transform='translate(0 -288.53)' ry='1.17' y='288.8' x='.27' height='7.94' width='7.94' fill='%23f9f9f9'/%3E%3Cg transform='translate(0 -288.53)'%3E%3Crect width='7.94' height='7.94' x='.27' y='288.8' ry='1.17' fill='none' stroke='%23000' stroke-width='.4' stroke-linejoin='round'/%3E%3Cpath d='M1.06 290.12H3.7m-2.64 1.33H3.7m-2.64 1.32H3.7m-2.64 1.3H3.7m-2.64 1.33H3.7' fill='none' stroke='%23000' stroke-width='.4'/%3E%3Cpath d='M4.37 288.8v7.94m0-4.11h3.96' fill='none' stroke='%23000' stroke-width='.3'/%3E%3Ctext font-weight='700' font-size='3.17' font-family='sans-serif'%3E%3Ctspan x='5.11' y='291.96'%3EF%3C/tspan%3E%3Ctspan x='5.03' y='295.68'%3EB%3C/tspan%3E%3C/text%3E%3C/g%3E%3C/svg%3E%0A");
|
|
}
|
|
|
|
button#bom-btn {
|
|
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8.47 8.47'%3E%3Crect transform='translate(0 -288.53)' ry='1.17' y='288.8' x='.27' height='7.94' width='7.94' fill='%23f9f9f9'/%3E%3Cg transform='translate(0 -288.53)' fill='none' stroke='%23000' stroke-width='.4'%3E%3Crect width='7.94' height='7.94' x='.27' y='288.8' ry='1.17' stroke-linejoin='round'/%3E%3Cpath d='M1.59 290.12h5.29M1.59 291.45h5.33M1.59 292.75h5.33M1.59 294.09h5.33M1.59 295.41h5.33'/%3E%3C/g%3E%3C/svg%3E");
|
|
}
|
|
|
|
button#bom-grouped-btn {
|
|
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='32' height='32'%3E%3Cg stroke='%23000' stroke-linejoin='round' class='layer'%3E%3Crect width='29' height='29' x='1.5' y='1.5' stroke-width='2' fill='%23fff' rx='5' ry='5'/%3E%3Cpath stroke-linecap='square' stroke-width='2' d='M6 10h4m4 0h5m4 0h3M6.1 22h3m3.9 0h5m4 0h4m-16-8h4m4 0h4'/%3E%3Cpath stroke-linecap='null' d='M5 17.5h22M5 26.6h22M5 5.5h22'/%3E%3C/g%3E%3C/svg%3E");
|
|
}
|
|
|
|
button#bom-ungrouped-btn {
|
|
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='32' height='32'%3E%3Cg stroke='%23000' stroke-linejoin='round' class='layer'%3E%3Crect width='29' height='29' x='1.5' y='1.5' stroke-width='2' fill='%23fff' rx='5' ry='5'/%3E%3Cpath stroke-linecap='square' stroke-width='2' d='M6 10h4m-4 8h3m-3 8h4'/%3E%3Cpath stroke-linecap='null' d='M5 13.5h22m-22 8h22M5 5.5h22'/%3E%3C/g%3E%3C/svg%3E");
|
|
}
|
|
|
|
button#bom-netlist-btn {
|
|
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='32' height='32'%3E%3Cg fill='none' stroke='%23000' class='layer'%3E%3Crect width='29' height='29' x='1.5' y='1.5' stroke-width='2' fill='%23fff' rx='5' ry='5'/%3E%3Cpath stroke-width='2' d='M6 26l6-6v-8m13.8-6.3l-6 6v8'/%3E%3Ccircle cx='11.8' cy='9.5' r='2.8' stroke-width='2'/%3E%3Ccircle cx='19.8' cy='22.8' r='2.8' stroke-width='2'/%3E%3C/g%3E%3C/svg%3E");
|
|
}
|
|
|
|
button#copy {
|
|
background-image: url("data:image/svg+xml,%3Csvg height='48' viewBox='0 0 48 48' width='48' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M0 0h48v48h-48z' fill='none'/%3E%3Cpath d='M32 2h-24c-2.21 0-4 1.79-4 4v28h4v-28h24v-4zm6 8h-22c-2.21 0-4 1.79-4 4v28c0 2.21 1.79 4 4 4h22c2.21 0 4-1.79 4-4v-28c0-2.21-1.79-4-4-4zm0 32h-22v-28h22v28z'/%3E%3C/svg%3E");
|
|
background-position: 6px 6px;
|
|
background-repeat: no-repeat;
|
|
background-size: 26px 26px;
|
|
border-radius: 6px;
|
|
height: 40px;
|
|
width: 40px;
|
|
margin: 10px 5px;
|
|
}
|
|
|
|
button#copy:active {
|
|
box-shadow: inset 0px 0px 5px #6c6c6c;
|
|
}
|
|
|
|
textarea.clipboard-temp {
|
|
position: fixed;
|
|
top: 0;
|
|
left: 0;
|
|
width: 2em;
|
|
height: 2em;
|
|
padding: 0;
|
|
border: None;
|
|
outline: None;
|
|
box-shadow: None;
|
|
background: transparent;
|
|
}
|
|
|
|
.left-most-button {
|
|
border-right: 0;
|
|
border-top-left-radius: 6px;
|
|
border-bottom-left-radius: 6px;
|
|
}
|
|
|
|
.middle-button {
|
|
border-right: 0;
|
|
}
|
|
|
|
.right-most-button {
|
|
border-top-right-radius: 6px;
|
|
border-bottom-right-radius: 6px;
|
|
}
|
|
|
|
.button-container {
|
|
font-size: 0;
|
|
margin: 0.4rem 0.4rem 0.4rem 0;
|
|
}
|
|
|
|
.dark .button-container {
|
|
filter: invert(1);
|
|
}
|
|
|
|
.button-container button {
|
|
background-size: 32px 32px;
|
|
background-position: 5px 5px;
|
|
background-repeat: no-repeat;
|
|
}
|
|
|
|
@media print {
|
|
.hideonprint {
|
|
display: none;
|
|
}
|
|
}
|
|
|
|
canvas {
|
|
cursor: crosshair;
|
|
}
|
|
|
|
canvas:active {
|
|
cursor: grabbing;
|
|
}
|
|
|
|
.fileinfo {
|
|
width: 100%;
|
|
max-width: 1000px;
|
|
border: none;
|
|
padding: 3px;
|
|
}
|
|
|
|
.fileinfo .title {
|
|
font-size: 20pt;
|
|
font-weight: bold;
|
|
}
|
|
|
|
.fileinfo td {
|
|
overflow: hidden;
|
|
white-space: nowrap;
|
|
max-width: 1px;
|
|
width: 50%;
|
|
text-overflow: ellipsis;
|
|
}
|
|
|
|
.bom {
|
|
border-collapse: collapse;
|
|
font-family: Consolas, "DejaVu Sans Mono", Monaco, monospace;
|
|
font-size: 10pt;
|
|
table-layout: fixed;
|
|
width: 100%;
|
|
margin-top: 1px;
|
|
position: relative;
|
|
}
|
|
|
|
.bom th,
|
|
.bom td {
|
|
border: 1px solid black;
|
|
padding: 5px;
|
|
word-wrap: break-word;
|
|
text-align: center;
|
|
position: relative;
|
|
}
|
|
|
|
.dark .bom th,
|
|
.dark .bom td {
|
|
border: 1px solid #777;
|
|
}
|
|
|
|
.bom th {
|
|
background-color: #CCCCCC;
|
|
background-clip: padding-box;
|
|
}
|
|
|
|
.dark .bom th {
|
|
background-color: #3b4749;
|
|
}
|
|
|
|
.bom tr.highlighted:nth-child(n) {
|
|
background-color: #cfc;
|
|
}
|
|
|
|
.dark .bom tr.highlighted:nth-child(n) {
|
|
background-color: #226022;
|
|
}
|
|
|
|
.bom tr:nth-child(even) {
|
|
background-color: #f2f2f2;
|
|
}
|
|
|
|
.dark .bom tr:nth-child(even) {
|
|
background-color: #313b40;
|
|
}
|
|
|
|
.bom tr.checked {
|
|
color: #1cb53d;
|
|
}
|
|
|
|
.dark .bom tr.checked {
|
|
color: #2cce54;
|
|
}
|
|
|
|
.bom tr {
|
|
transition: background-color 0.2s;
|
|
}
|
|
|
|
.bom .numCol {
|
|
width: 30px;
|
|
}
|
|
|
|
.bom .value {
|
|
width: 15%;
|
|
}
|
|
|
|
.bom .quantity {
|
|
width: 65px;
|
|
}
|
|
|
|
.bom th .sortmark {
|
|
position: absolute;
|
|
right: 1px;
|
|
top: 1px;
|
|
margin-top: -5px;
|
|
border-width: 5px;
|
|
border-style: solid;
|
|
border-color: transparent transparent #221 transparent;
|
|
transform-origin: 50% 85%;
|
|
transition: opacity 0.2s, transform 0.4s;
|
|
}
|
|
|
|
.dark .bom th .sortmark {
|
|
filter: invert(1);
|
|
}
|
|
|
|
.bom th .sortmark.none {
|
|
opacity: 0;
|
|
}
|
|
|
|
.bom th .sortmark.desc {
|
|
transform: rotate(180deg);
|
|
}
|
|
|
|
.bom th:hover .sortmark.none {
|
|
opacity: 0.5;
|
|
}
|
|
|
|
.bom .bom-checkbox {
|
|
width: 30px;
|
|
position: relative;
|
|
user-select: none;
|
|
-moz-user-select: none;
|
|
}
|
|
|
|
.bom .bom-checkbox:before {
|
|
content: "";
|
|
position: absolute;
|
|
border-width: 15px;
|
|
border-style: solid;
|
|
border-color: #51829f transparent transparent transparent;
|
|
visibility: hidden;
|
|
top: -15px;
|
|
}
|
|
|
|
.bom .bom-checkbox:after {
|
|
content: "Double click to set/unset all";
|
|
position: absolute;
|
|
color: white;
|
|
top: -35px;
|
|
left: -26px;
|
|
background: #51829f;
|
|
padding: 5px 15px;
|
|
border-radius: 8px;
|
|
white-space: nowrap;
|
|
visibility: hidden;
|
|
}
|
|
|
|
.bom .bom-checkbox:hover:before,
|
|
.bom .bom-checkbox:hover:after {
|
|
visibility: visible;
|
|
transition: visibility 0.2s linear 1s;
|
|
}
|
|
|
|
.split {
|
|
-webkit-box-sizing: border-box;
|
|
-moz-box-sizing: border-box;
|
|
box-sizing: border-box;
|
|
overflow-y: auto;
|
|
overflow-x: hidden;
|
|
background-color: inherit;
|
|
}
|
|
|
|
.split.split-horizontal,
|
|
.gutter.gutter-horizontal {
|
|
height: 100%;
|
|
float: left;
|
|
}
|
|
|
|
.gutter {
|
|
background-color: #ddd;
|
|
background-repeat: no-repeat;
|
|
background-position: 50%;
|
|
transition: background-color 0.3s;
|
|
}
|
|
|
|
.dark .gutter {
|
|
background-color: #777;
|
|
}
|
|
|
|
.gutter.gutter-horizontal {
|
|
background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAeCAYAAADkftS9AAAAIklEQVQoU2M4c+bMfxAGAgYYmwGrIIiDjrELjpo5aiZeMwF+yNnOs5KSvgAAAABJRU5ErkJggg==');
|
|
cursor: ew-resize;
|
|
width: 5px;
|
|
}
|
|
|
|
.gutter.gutter-vertical {
|
|
background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAB4AAAAFAQMAAABo7865AAAABlBMVEVHcEzMzMzyAv2sAAAAAXRSTlMAQObYZgAAABBJREFUeF5jOAMEEAIEEFwAn3kMwcB6I2AAAAAASUVORK5CYII=');
|
|
cursor: ns-resize;
|
|
height: 5px;
|
|
}
|
|
|
|
.searchbox {
|
|
float: left;
|
|
height: 40px;
|
|
margin: 10px 5px;
|
|
padding: 12px 32px;
|
|
font-family: Consolas, "DejaVu Sans Mono", Monaco, monospace;
|
|
font-size: 18px;
|
|
box-sizing: border-box;
|
|
border: 1px solid #888;
|
|
border-radius: 6px;
|
|
outline: none;
|
|
background-color: #eee;
|
|
transition: background-color 0.2s, border 0.2s;
|
|
background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAABNklEQVQ4T8XSMUvDQBQH8P/LElFa/AIZHcTBQSz0I/gFstTBRR2KUC4ldDxw7h0Bl3RRUATxi4iiODgoiLNrbQYp5J6cpJJqomkX33Z37/14d/dIa33MzDuYI4johOI4XhyNRteO46zNYjDzAxE1yBZprVeZ+QbAUhXEGJMA2Ox2u4+fQIa0mPmsCgCgJYQ4t7lfgF0opQYAdv9ABkKI/UnOFCClXKjX61cA1osQY8x9kiRNKeV7IWA3oyhaSdP0FkAtjxhj3hzH2RBCPOf3pzqYHCilfAAX+URm9oMguPzeWSGQvUcMYC8rOBJCHBRdqxTo9/vbRHRqi8bj8XKv1xvODbiuW2u32/bvf0SlDv4XYOY7z/Mavu+nM1+BmQ+NMc0wDF/LprP0DbTWW0T00ul0nn4b7Q87+X4Qmfiq2wAAAABJRU5ErkJggg==');
|
|
background-position: 10px 10px;
|
|
background-repeat: no-repeat;
|
|
}
|
|
|
|
.dark .searchbox {
|
|
background-color: #111;
|
|
color: #eee;
|
|
}
|
|
|
|
.searchbox::placeholder {
|
|
color: #ccc;
|
|
}
|
|
|
|
.dark .searchbox::placeholder {
|
|
color: #666;
|
|
}
|
|
|
|
.filter {
|
|
width: calc(60% - 64px);
|
|
}
|
|
|
|
.reflookup {
|
|
width: calc(40% - 10px);
|
|
}
|
|
|
|
input[type=text]:focus {
|
|
background-color: white;
|
|
border: 1px solid #333;
|
|
}
|
|
|
|
.dark input[type=text]:focus {
|
|
background-color: #333;
|
|
border: 1px solid #ccc;
|
|
}
|
|
|
|
mark.highlight {
|
|
background-color: #5050ff;
|
|
color: #fff;
|
|
padding: 2px;
|
|
border-radius: 6px;
|
|
}
|
|
|
|
.dark mark.highlight {
|
|
background-color: #76a6da;
|
|
color: #111;
|
|
}
|
|
|
|
.menubtn {
|
|
background-color: white;
|
|
border: none;
|
|
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='36' height='36' viewBox='0 0 20 20'%3E%3Cpath fill='none' d='M0 0h20v20H0V0z'/%3E%3Cpath d='M15.95 10.78c.03-.25.05-.51.05-.78s-.02-.53-.06-.78l1.69-1.32c.15-.12.19-.34.1-.51l-1.6-2.77c-.1-.18-.31-.24-.49-.18l-1.99.8c-.42-.32-.86-.58-1.35-.78L12 2.34c-.03-.2-.2-.34-.4-.34H8.4c-.2 0-.36.14-.39.34l-.3 2.12c-.49.2-.94.47-1.35.78l-1.99-.8c-.18-.07-.39 0-.49.18l-1.6 2.77c-.1.18-.06.39.1.51l1.69 1.32c-.04.25-.07.52-.07.78s.02.53.06.78L2.37 12.1c-.15.12-.19.34-.1.51l1.6 2.77c.1.18.31.24.49.18l1.99-.8c.42.32.86.58 1.35.78l.3 2.12c.04.2.2.34.4.34h3.2c.2 0 .37-.14.39-.34l.3-2.12c.49-.2.94-.47 1.35-.78l1.99.8c.18.07.39 0 .49-.18l1.6-2.77c.1-.18.06-.39-.1-.51l-1.67-1.32zM10 13c-1.65 0-3-1.35-3-3s1.35-3 3-3 3 1.35 3 3-1.35 3-3 3z'/%3E%3C/svg%3E%0A");
|
|
background-position: center;
|
|
background-repeat: no-repeat;
|
|
}
|
|
|
|
.statsbtn {
|
|
background-color: white;
|
|
border: none;
|
|
background-image: url("data:image/svg+xml,%3Csvg width='36' height='36' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M4 6h28v24H4V6zm0 8h28v8H4m9-16v24h10V5.8' fill='none' stroke='%23000' stroke-width='2'/%3E%3C/svg%3E");
|
|
background-position: center;
|
|
background-repeat: no-repeat;
|
|
}
|
|
|
|
.iobtn {
|
|
background-color: white;
|
|
border: none;
|
|
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='36' height='36'%3E%3Cpath fill='none' stroke='%23000' stroke-width='2' d='M3 33v-7l6.8-7h16.5l6.7 7v7H3zM3.2 26H33M21 9l5-5.9 5 6h-2.5V15h-5V9H21zm-4.9 0l-5 6-5-6h2.5V3h5v6h2.5z'/%3E%3Cpath fill='none' stroke='%23000' d='M6.1 29.5H10'/%3E%3C/svg%3E");
|
|
background-position: center;
|
|
background-repeat: no-repeat;
|
|
}
|
|
|
|
.visbtn {
|
|
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24'%3E%3Cpath fill='none' stroke='%23333' d='M2.5 4.5h5v15h-5zM9.5 4.5h5v15h-5zM16.5 4.5h5v15h-5z'/%3E%3C/svg%3E");
|
|
background-position: center;
|
|
background-repeat: no-repeat;
|
|
padding: 15px;
|
|
}
|
|
|
|
#vismenu-content {
|
|
left: 0px;
|
|
font-family: Verdana, sans-serif;
|
|
}
|
|
|
|
.dark .statsbtn,
|
|
.dark .savebtn,
|
|
.dark .menubtn,
|
|
.dark .iobtn,
|
|
.dark .visbtn {
|
|
filter: invert(1);
|
|
}
|
|
|
|
.flexbox {
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: space-between;
|
|
width: 100%;
|
|
}
|
|
|
|
.savebtn {
|
|
background-color: #d6d6d6;
|
|
width: auto;
|
|
height: 30px;
|
|
flex-grow: 1;
|
|
margin: 5px;
|
|
border-radius: 4px;
|
|
}
|
|
|
|
.savebtn:active {
|
|
background-color: #0a0;
|
|
color: white;
|
|
}
|
|
|
|
.dark .savebtn:active {
|
|
/* This will be inverted */
|
|
background-color: #b3b;
|
|
}
|
|
|
|
.stats {
|
|
border-collapse: collapse;
|
|
font-size: 12pt;
|
|
table-layout: fixed;
|
|
width: 100%;
|
|
min-width: 450px;
|
|
}
|
|
|
|
.dark .stats td {
|
|
border: 1px solid #bbb;
|
|
}
|
|
|
|
.stats td {
|
|
border: 1px solid black;
|
|
padding: 5px;
|
|
word-wrap: break-word;
|
|
text-align: center;
|
|
position: relative;
|
|
}
|
|
|
|
#checkbox-stats div {
|
|
position: absolute;
|
|
left: 0;
|
|
top: 0;
|
|
height: 100%;
|
|
width: 100%;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
}
|
|
|
|
#checkbox-stats .bar {
|
|
background-color: rgba(28, 251, 0, 0.6);
|
|
}
|
|
|
|
.menu {
|
|
position: relative;
|
|
display: inline-block;
|
|
margin: 0.4rem 0.4rem 0.4rem 0;
|
|
}
|
|
|
|
.menu-content {
|
|
font-size: 12pt !important;
|
|
text-align: left !important;
|
|
font-weight: normal !important;
|
|
display: none;
|
|
position: absolute;
|
|
background-color: white;
|
|
right: 0;
|
|
min-width: 300px;
|
|
box-shadow: 0px 8px 16px 0px rgba(0, 0, 0, 0.2);
|
|
z-index: 100;
|
|
padding: 8px;
|
|
}
|
|
|
|
.dark .menu-content {
|
|
background-color: #111;
|
|
}
|
|
|
|
.menu:hover .menu-content {
|
|
display: block;
|
|
}
|
|
|
|
.menu:hover .menubtn,
|
|
.menu:hover .iobtn,
|
|
.menu:hover .statsbtn {
|
|
background-color: #eee;
|
|
}
|
|
|
|
.menu-label {
|
|
display: inline-block;
|
|
padding: 8px;
|
|
border: 1px solid #ccc;
|
|
border-top: 0;
|
|
width: calc(100% - 18px);
|
|
}
|
|
|
|
.menu-label-top {
|
|
border-top: 1px solid #ccc;
|
|
}
|
|
|
|
.menu-textbox {
|
|
float: left;
|
|
height: 24px;
|
|
margin: 10px 5px;
|
|
padding: 5px 5px;
|
|
font-family: Consolas, "DejaVu Sans Mono", Monaco, monospace;
|
|
font-size: 14px;
|
|
box-sizing: border-box;
|
|
border: 1px solid #888;
|
|
border-radius: 4px;
|
|
outline: none;
|
|
background-color: #eee;
|
|
transition: background-color 0.2s, border 0.2s;
|
|
width: calc(100% - 10px);
|
|
}
|
|
|
|
.menu-textbox.invalid,
|
|
.dark .menu-textbox.invalid {
|
|
color: red;
|
|
}
|
|
|
|
.dark .menu-textbox {
|
|
background-color: #222;
|
|
color: #eee;
|
|
}
|
|
|
|
.radio-container {
|
|
margin: 4px;
|
|
}
|
|
|
|
.topmostdiv {
|
|
display: flex;
|
|
flex-direction: column;
|
|
width: 100%;
|
|
background-color: white;
|
|
transition: background-color 0.3s;
|
|
min-height: 100%;
|
|
}
|
|
|
|
#top {
|
|
display: flex;
|
|
flex-wrap: wrap;
|
|
justify-content: flex-end;
|
|
align-items: center;
|
|
}
|
|
|
|
#topdivider {
|
|
border-bottom: 2px solid black;
|
|
display: flex;
|
|
justify-content: center;
|
|
align-items: center;
|
|
}
|
|
|
|
.dark #topdivider {
|
|
border-bottom: 2px solid #ccc;
|
|
}
|
|
|
|
#topdivider>div {
|
|
position: relative;
|
|
}
|
|
|
|
#toptoggle {
|
|
cursor: pointer;
|
|
user-select: none;
|
|
position: absolute;
|
|
padding: 0.1rem 0.3rem;
|
|
top: -0.4rem;
|
|
left: -1rem;
|
|
font-size: 1.4rem;
|
|
line-height: 60%;
|
|
border: 1px solid black;
|
|
border-radius: 1rem;
|
|
background-color: #fff;
|
|
z-index: 100;
|
|
}
|
|
|
|
.flipped {
|
|
transform: rotate(0.5turn);
|
|
}
|
|
|
|
.dark #toptoggle {
|
|
border: 1px solid #fff;
|
|
background-color: #222;
|
|
}
|
|
|
|
#fileinfodiv {
|
|
flex: 20rem 1 0;
|
|
overflow: auto;
|
|
}
|
|
|
|
#bomcontrols {
|
|
display: flex;
|
|
flex-direction: row-reverse;
|
|
}
|
|
|
|
#bomcontrols>* {
|
|
flex-shrink: 0;
|
|
}
|
|
|
|
#dbg {
|
|
display: block;
|
|
}
|
|
|
|
::-webkit-scrollbar {
|
|
width: 8px;
|
|
}
|
|
|
|
::-webkit-scrollbar-track {
|
|
background: #aaa;
|
|
}
|
|
|
|
::-webkit-scrollbar-thumb {
|
|
background: #666;
|
|
border-radius: 3px;
|
|
}
|
|
|
|
::-webkit-scrollbar-thumb:hover {
|
|
background: #555;
|
|
}
|
|
|
|
.slider {
|
|
-webkit-appearance: none;
|
|
width: 100%;
|
|
margin: 3px 0;
|
|
padding: 0;
|
|
outline: none;
|
|
opacity: 0.7;
|
|
-webkit-transition: .2s;
|
|
transition: opacity .2s;
|
|
border-radius: 3px;
|
|
}
|
|
|
|
.slider:hover {
|
|
opacity: 1;
|
|
}
|
|
|
|
.slider:focus {
|
|
outline: none;
|
|
}
|
|
|
|
.slider::-webkit-slider-runnable-track {
|
|
-webkit-appearance: none;
|
|
width: 100%;
|
|
height: 8px;
|
|
background: #d3d3d3;
|
|
border-radius: 3px;
|
|
border: none;
|
|
}
|
|
|
|
.slider::-webkit-slider-thumb {
|
|
-webkit-appearance: none;
|
|
width: 15px;
|
|
height: 15px;
|
|
border-radius: 50%;
|
|
background: #0a0;
|
|
cursor: pointer;
|
|
margin-top: -4px;
|
|
}
|
|
|
|
.dark .slider::-webkit-slider-thumb {
|
|
background: #3d3;
|
|
}
|
|
|
|
.slider::-moz-range-thumb {
|
|
width: 15px;
|
|
height: 15px;
|
|
border-radius: 50%;
|
|
background: #0a0;
|
|
cursor: pointer;
|
|
}
|
|
|
|
.slider::-moz-range-track {
|
|
height: 8px;
|
|
background: #d3d3d3;
|
|
border-radius: 3px;
|
|
}
|
|
|
|
.dark .slider::-moz-range-thumb {
|
|
background: #3d3;
|
|
}
|
|
|
|
.slider::-ms-track {
|
|
width: 100%;
|
|
height: 8px;
|
|
border-width: 3px 0;
|
|
background: transparent;
|
|
border-color: transparent;
|
|
color: transparent;
|
|
transition: opacity .2s;
|
|
}
|
|
|
|
.slider::-ms-fill-lower {
|
|
background: #d3d3d3;
|
|
border: none;
|
|
border-radius: 3px;
|
|
}
|
|
|
|
.slider::-ms-fill-upper {
|
|
background: #d3d3d3;
|
|
border: none;
|
|
border-radius: 3px;
|
|
}
|
|
|
|
.slider::-ms-thumb {
|
|
width: 15px;
|
|
height: 15px;
|
|
border-radius: 50%;
|
|
background: #0a0;
|
|
cursor: pointer;
|
|
margin: 0;
|
|
}
|
|
|
|
.shameless-plug {
|
|
font-size: 0.8em;
|
|
text-align: center;
|
|
display: block;
|
|
}
|
|
|
|
a {
|
|
color: #0278a4;
|
|
}
|
|
|
|
.dark a {
|
|
color: #00b9fd;
|
|
}
|
|
|
|
#frontcanvas,
|
|
#backcanvas {
|
|
touch-action: none;
|
|
}
|
|
|
|
.placeholder {
|
|
border: 1px dashed #9f9fda !important;
|
|
background-color: #edf2f7 !important;
|
|
}
|
|
|
|
.dragging {
|
|
z-index: 999;
|
|
}
|
|
|
|
.dark .dragging>table>tbody>tr {
|
|
background-color: #252c30;
|
|
}
|
|
|
|
.dark .placeholder {
|
|
filter: invert(1);
|
|
}
|
|
|
|
.column-spacer {
|
|
top: 0;
|
|
left: 0;
|
|
width: calc(100% - 4px);
|
|
position: absolute;
|
|
cursor: pointer;
|
|
user-select: none;
|
|
height: 100%;
|
|
}
|
|
|
|
.column-width-handle {
|
|
top: 0;
|
|
right: 0;
|
|
width: 4px;
|
|
position: absolute;
|
|
cursor: col-resize;
|
|
user-select: none;
|
|
height: 100%;
|
|
}
|
|
|
|
.column-width-handle:hover {
|
|
background-color: #4f99bd;
|
|
}
|
|
|
|
.help-link {
|
|
border: 1px solid #0278a4;
|
|
padding-inline: 0.3rem;
|
|
border-radius: 3px;
|
|
cursor: pointer;
|
|
}
|
|
|
|
.dark .help-link {
|
|
border: 1px solid #00b9fd;
|
|
}
|
|
|
|
.bom-color {
|
|
width: 20%;
|
|
}
|
|
|
|
.color-column input {
|
|
width: 1.6rem;
|
|
height: 1rem;
|
|
border: 1px solid black;
|
|
cursor: pointer;
|
|
padding: 0;
|
|
}
|
|
|
|
/* removes default styling from input color element */
|
|
::-webkit-color-swatch {
|
|
border: none;
|
|
}
|
|
|
|
::-webkit-color-swatch-wrapper {
|
|
padding: 0;
|
|
}
|
|
|
|
::-moz-color-swatch,
|
|
::-moz-focus-inner {
|
|
border: none;
|
|
}
|
|
|
|
::-moz-focus-inner {
|
|
padding: 0;
|
|
}
|
|
/* #bomhead {
|
|
position: sticky;
|
|
top: 0px;
|
|
z-index: 1;
|
|
} */
|
|
</style>
|
|
<script type="text/javascript" >
|
|
///////////////////////////////////////////////
|
|
/*
|
|
Split.js - v1.3.5
|
|
MIT License
|
|
https://github.com/nathancahill/Split.js
|
|
*/
|
|
!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?module.exports=t():"function"==typeof define&&define.amd?define(t):e.Split=t()}(this,function(){"use strict";var e=window,t=e.document,n="addEventListener",i="removeEventListener",r="getBoundingClientRect",s=function(){return!1},o=e.attachEvent&&!e[n],a=["","-webkit-","-moz-","-o-"].filter(function(e){var n=t.createElement("div");return n.style.cssText="width:"+e+"calc(9px)",!!n.style.length}).shift()+"calc",l=function(e){return"string"==typeof e||e instanceof String?t.querySelector(e):e};return function(u,c){function z(e,t,n){var i=A(y,t,n);Object.keys(i).forEach(function(t){return e.style[t]=i[t]})}function h(e,t){var n=B(y,t);Object.keys(n).forEach(function(t){return e.style[t]=n[t]})}function f(e){var t=E[this.a],n=E[this.b],i=t.size+n.size;t.size=e/this.size*i,n.size=i-e/this.size*i,z(t.element,t.size,this.aGutterSize),z(n.element,n.size,this.bGutterSize)}function m(e){var t;this.dragging&&((t="touches"in e?e.touches[0][b]-this.start:e[b]-this.start)<=E[this.a].minSize+M+this.aGutterSize?t=E[this.a].minSize+this.aGutterSize:t>=this.size-(E[this.b].minSize+M+this.bGutterSize)&&(t=this.size-(E[this.b].minSize+this.bGutterSize)),f.call(this,t),c.onDrag&&c.onDrag())}function g(){var e=E[this.a].element,t=E[this.b].element;this.size=e[r]()[y]+t[r]()[y]+this.aGutterSize+this.bGutterSize,this.start=e[r]()[G]}function d(){var t=this,n=E[t.a].element,r=E[t.b].element;t.dragging&&c.onDragEnd&&c.onDragEnd(),t.dragging=!1,e[i]("mouseup",t.stop),e[i]("touchend",t.stop),e[i]("touchcancel",t.stop),t.parent[i]("mousemove",t.move),t.parent[i]("touchmove",t.move),delete t.stop,delete t.move,n[i]("selectstart",s),n[i]("dragstart",s),r[i]("selectstart",s),r[i]("dragstart",s),n.style.userSelect="",n.style.webkitUserSelect="",n.style.MozUserSelect="",n.style.pointerEvents="",r.style.userSelect="",r.style.webkitUserSelect="",r.style.MozUserSelect="",r.style.pointerEvents="",t.gutter.style.cursor="",t.parent.style.cursor=""}function S(t){var i=this,r=E[i.a].element,o=E[i.b].element;!i.dragging&&c.onDragStart&&c.onDragStart(),t.preventDefault(),i.dragging=!0,i.move=m.bind(i),i.stop=d.bind(i),e[n]("mouseup",i.stop),e[n]("touchend",i.stop),e[n]("touchcancel",i.stop),i.parent[n]("mousemove",i.move),i.parent[n]("touchmove",i.move),r[n]("selectstart",s),r[n]("dragstart",s),o[n]("selectstart",s),o[n]("dragstart",s),r.style.userSelect="none",r.style.webkitUserSelect="none",r.style.MozUserSelect="none",r.style.pointerEvents="none",o.style.userSelect="none",o.style.webkitUserSelect="none",o.style.MozUserSelect="none",o.style.pointerEvents="none",i.gutter.style.cursor=j,i.parent.style.cursor=j,g.call(i)}function v(e){e.forEach(function(t,n){if(n>0){var i=F[n-1],r=E[i.a],s=E[i.b];r.size=e[n-1],s.size=t,z(r.element,r.size,i.aGutterSize),z(s.element,s.size,i.bGutterSize)}})}function p(){F.forEach(function(e){e.parent.removeChild(e.gutter),E[e.a].element.style[y]="",E[e.b].element.style[y]=""})}void 0===c&&(c={});var y,b,G,E,w=l(u[0]).parentNode,D=e.getComputedStyle(w).flexDirection,U=c.sizes||u.map(function(){return 100/u.length}),k=void 0!==c.minSize?c.minSize:100,x=Array.isArray(k)?k:u.map(function(){return k}),L=void 0!==c.gutterSize?c.gutterSize:10,M=void 0!==c.snapOffset?c.snapOffset:30,O=c.direction||"horizontal",j=c.cursor||("horizontal"===O?"ew-resize":"ns-resize"),C=c.gutter||function(e,n){var i=t.createElement("div");return i.className="gutter gutter-"+n,i},A=c.elementStyle||function(e,t,n){var i={};return"string"==typeof t||t instanceof String?i[e]=t:i[e]=o?t+"%":a+"("+t+"% - "+n+"px)",i},B=c.gutterStyle||function(e,t){return n={},n[e]=t+"px",n;var n};"horizontal"===O?(y="width","clientWidth",b="clientX",G="left","paddingLeft"):"vertical"===O&&(y="height","clientHeight",b="clientY",G="top","paddingTop");var F=[];return E=u.map(function(e,t){var i,s={element:l(e),size:U[t],minSize:x[t]};if(t>0&&(i={a:t-1,b:t,dragging:!1,isFirst:1===t,isLast:t===u.length-1,direction:O,parent:w},i.aGutterSize=L,i.bGutterSize=L,i.isFirst&&(i.aGutterSize=L/2),i.isLast&&(i.bGutterSize=L/2),"row-reverse"===D||"column-reverse"===D)){var a=i.a;i.a=i.b,i.b=a}if(!o&&t>0){var c=C(t,O);h(c,L),c[n]("mousedown",S.bind(i)),c[n]("touchstart",S.bind(i)),w.insertBefore(c,s.element),i.gutter=c}0===t||t===u.length-1?z(s.element,s.size,L/2):z(s.element,s.size,L);var f=s.element[r]()[y];return f<s.minSize&&(s.minSize=f),t>0&&F.push(i),s}),o?{setSizes:v,destroy:p}:{setSizes:v,getSizes:function(){return E.map(function(e){return e.size})},collapse:function(e){if(e===F.length){var t=F[e-1];g.call(t),o||f.call(t,t.size-t.bGutterSize)}else{var n=F[e];g.call(n),o||f.call(n,n.aGutterSize)}},destroy:p}}});
|
|
|
|
///////////////////////////////////////////////
|
|
|
|
///////////////////////////////////////////////
|
|
// Copyright (c) 2013 Pieroxy <pieroxy@pieroxy.net>
|
|
// This work is free. You can redistribute it and/or modify it
|
|
// under the terms of the WTFPL, Version 2
|
|
// For more information see LICENSE.txt or http://www.wtfpl.net/
|
|
//
|
|
// For more information, the home page:
|
|
// http://pieroxy.net/blog/pages/lz-string/testing.html
|
|
//
|
|
// LZ-based compression algorithm, version 1.4.4
|
|
var LZString=function(){var o=String.fromCharCode,i={};var n={decompressFromBase64:function(o){return null==o?"":""==o?null:n._decompress(o.length,32,function(n){return function(o,n){if(!i[o]){i[o]={};for(var t=0;t<o.length;t++)i[o][o.charAt(t)]=t}return i[o][n]}("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=",o.charAt(n))})},_decompress:function(i,n,t){var r,e,a,s,p,u,l,f=[],c=4,d=4,h=3,v="",g=[],m={val:t(0),position:n,index:1};for(r=0;r<3;r+=1)f[r]=r;for(a=0,p=Math.pow(2,2),u=1;u!=p;)s=m.val&m.position,m.position>>=1,0==m.position&&(m.position=n,m.val=t(m.index++)),a|=(s>0?1:0)*u,u<<=1;switch(a){case 0:for(a=0,p=Math.pow(2,8),u=1;u!=p;)s=m.val&m.position,m.position>>=1,0==m.position&&(m.position=n,m.val=t(m.index++)),a|=(s>0?1:0)*u,u<<=1;l=o(a);break;case 1:for(a=0,p=Math.pow(2,16),u=1;u!=p;)s=m.val&m.position,m.position>>=1,0==m.position&&(m.position=n,m.val=t(m.index++)),a|=(s>0?1:0)*u,u<<=1;l=o(a);break;case 2:return""}for(f[3]=l,e=l,g.push(l);;){if(m.index>i)return"";for(a=0,p=Math.pow(2,h),u=1;u!=p;)s=m.val&m.position,m.position>>=1,0==m.position&&(m.position=n,m.val=t(m.index++)),a|=(s>0?1:0)*u,u<<=1;switch(l=a){case 0:for(a=0,p=Math.pow(2,8),u=1;u!=p;)s=m.val&m.position,m.position>>=1,0==m.position&&(m.position=n,m.val=t(m.index++)),a|=(s>0?1:0)*u,u<<=1;f[d++]=o(a),l=d-1,c--;break;case 1:for(a=0,p=Math.pow(2,16),u=1;u!=p;)s=m.val&m.position,m.position>>=1,0==m.position&&(m.position=n,m.val=t(m.index++)),a|=(s>0?1:0)*u,u<<=1;f[d++]=o(a),l=d-1,c--;break;case 2:return g.join("")}if(0==c&&(c=Math.pow(2,h),h++),f[l])v=f[l];else{if(l!==d)return null;v=e+e.charAt(0)}g.push(v),f[d++]=e+v.charAt(0),e=v,0==--c&&(c=Math.pow(2,h),h++)}}};return n}();"function"==typeof define&&define.amd?define(function(){return LZString}):"undefined"!=typeof module&&null!=module?module.exports=LZString:"undefined"!=typeof angular&&null!=angular&&angular.module("LZString",[]).factory("LZString",function(){return LZString});
|
|
///////////////////////////////////////////////
|
|
|
|
///////////////////////////////////////////////
|
|
/*!
|
|
* PEP v0.4.3 | https://github.com/jquery/PEP
|
|
* Copyright jQuery Foundation and other contributors | http://jquery.org/license
|
|
*/
|
|
!function(a,b){"object"==typeof exports&&"undefined"!=typeof module?module.exports=b():"function"==typeof define&&define.amd?define(b):a.PointerEventsPolyfill=b()}(this,function(){"use strict";function a(a,b){b=b||Object.create(null);var c=document.createEvent("Event");c.initEvent(a,b.bubbles||!1,b.cancelable||!1);
|
|
for(var d,e=2;e<m.length;e++)d=m[e],c[d]=b[d]||n[e];c.buttons=b.buttons||0;
|
|
var f=0;return f=b.pressure&&c.buttons?b.pressure:c.buttons?.5:0,c.x=c.clientX,c.y=c.clientY,c.pointerId=b.pointerId||0,c.width=b.width||0,c.height=b.height||0,c.pressure=f,c.tiltX=b.tiltX||0,c.tiltY=b.tiltY||0,c.twist=b.twist||0,c.tangentialPressure=b.tangentialPressure||0,c.pointerType=b.pointerType||"",c.hwTimestamp=b.hwTimestamp||0,c.isPrimary=b.isPrimary||!1,c}function b(){this.array=[],this.size=0}function c(a,b,c,d){this.addCallback=a.bind(d),this.removeCallback=b.bind(d),this.changedCallback=c.bind(d),A&&(this.observer=new A(this.mutationWatcher.bind(this)))}function d(a){return"body /shadow-deep/ "+e(a)}function e(a){return'[touch-action="'+a+'"]'}function f(a){return"{ -ms-touch-action: "+a+"; touch-action: "+a+"; }"}function g(){if(F){D.forEach(function(a){String(a)===a?(E+=e(a)+f(a)+"\n",G&&(E+=d(a)+f(a)+"\n")):(E+=a.selectors.map(e)+f(a.rule)+"\n",G&&(E+=a.selectors.map(d)+f(a.rule)+"\n"))});var a=document.createElement("style");a.textContent=E,document.head.appendChild(a)}}function h(){if(!window.PointerEvent){if(window.PointerEvent=a,window.navigator.msPointerEnabled){var b=window.navigator.msMaxTouchPoints;Object.defineProperty(window.navigator,"maxTouchPoints",{value:b,enumerable:!0}),u.registerSource("ms",_)}else Object.defineProperty(window.navigator,"maxTouchPoints",{value:0,enumerable:!0}),u.registerSource("mouse",N),void 0!==window.ontouchstart&&u.registerSource("touch",V);u.register(document)}}function i(a){if(!u.pointermap.has(a)){var b=new Error("InvalidPointerId");throw b.name="InvalidPointerId",b}}function j(a){for(var b=a.parentNode;b&&b!==a.ownerDocument;)b=b.parentNode;if(!b){var c=new Error("InvalidStateError");throw c.name="InvalidStateError",c}}function k(a){var b=u.pointermap.get(a);return 0!==b.buttons}function l(){window.Element&&!Element.prototype.setPointerCapture&&Object.defineProperties(Element.prototype,{setPointerCapture:{value:W},releasePointerCapture:{value:X},hasPointerCapture:{value:Y}})}
|
|
var m=["bubbles","cancelable","view","detail","screenX","screenY","clientX","clientY","ctrlKey","altKey","shiftKey","metaKey","button","relatedTarget","pageX","pageY"],n=[!1,!1,null,null,0,0,0,0,!1,!1,!1,!1,0,null,0,0],o=window.Map&&window.Map.prototype.forEach,p=o?Map:b;b.prototype={set:function(a,b){return void 0===b?this["delete"](a):(this.has(a)||this.size++,void(this.array[a]=b))},has:function(a){return void 0!==this.array[a]},"delete":function(a){this.has(a)&&(delete this.array[a],this.size--)},get:function(a){return this.array[a]},clear:function(){this.array.length=0,this.size=0},forEach:function(a,b){return this.array.forEach(function(c,d){a.call(b,c,d,this)},this)}};var q=["bubbles","cancelable","view","detail","screenX","screenY","clientX","clientY","ctrlKey","altKey","shiftKey","metaKey","button","relatedTarget","buttons","pointerId","width","height","pressure","tiltX","tiltY","pointerType","hwTimestamp","isPrimary","type","target","currentTarget","which","pageX","pageY","timeStamp"],r=[!1,!1,null,null,0,0,0,0,!1,!1,!1,!1,0,null,0,0,0,0,0,0,0,"",0,!1,"",null,null,0,0,0,0],s={pointerover:1,pointerout:1,pointerenter:1,pointerleave:1},t="undefined"!=typeof SVGElementInstance,u={pointermap:new p,eventMap:Object.create(null),captureInfo:Object.create(null),eventSources:Object.create(null),eventSourceList:[],registerSource:function(a,b){var c=b,d=c.events;d&&(d.forEach(function(a){c[a]&&(this.eventMap[a]=c[a].bind(c))},this),this.eventSources[a]=c,this.eventSourceList.push(c))},register:function(a){for(var b,c=this.eventSourceList.length,d=0;d<c&&(b=this.eventSourceList[d]);d++)
|
|
b.register.call(b,a)},unregister:function(a){for(var b,c=this.eventSourceList.length,d=0;d<c&&(b=this.eventSourceList[d]);d++)
|
|
b.unregister.call(b,a)},contains:function(a,b){try{return a.contains(b)}catch(c){return!1}},down:function(a){a.bubbles=!0,this.fireEvent("pointerdown",a)},move:function(a){a.bubbles=!0,this.fireEvent("pointermove",a)},up:function(a){a.bubbles=!0,this.fireEvent("pointerup",a)},enter:function(a){a.bubbles=!1,this.fireEvent("pointerenter",a)},leave:function(a){a.bubbles=!1,this.fireEvent("pointerleave",a)},over:function(a){a.bubbles=!0,this.fireEvent("pointerover",a)},out:function(a){a.bubbles=!0,this.fireEvent("pointerout",a)},cancel:function(a){a.bubbles=!0,this.fireEvent("pointercancel",a)},leaveOut:function(a){this.out(a),this.propagate(a,this.leave,!1)},enterOver:function(a){this.over(a),this.propagate(a,this.enter,!0)},eventHandler:function(a){if(!a._handledByPE){var b=a.type,c=this.eventMap&&this.eventMap[b];c&&c(a),a._handledByPE=!0}},listen:function(a,b){b.forEach(function(b){this.addEvent(a,b)},this)},unlisten:function(a,b){b.forEach(function(b){this.removeEvent(a,b)},this)},addEvent:function(a,b){a.addEventListener(b,this.boundHandler)},removeEvent:function(a,b){a.removeEventListener(b,this.boundHandler)},makeEvent:function(b,c){this.captureInfo[c.pointerId]&&(c.relatedTarget=null);var d=new a(b,c);return c.preventDefault&&(d.preventDefault=c.preventDefault),d._target=d._target||c.target,d},fireEvent:function(a,b){var c=this.makeEvent(a,b);return this.dispatchEvent(c)},cloneEvent:function(a){for(var b,c=Object.create(null),d=0;d<q.length;d++)b=q[d],c[b]=a[b]||r[d],!t||"target"!==b&&"relatedTarget"!==b||c[b]instanceof SVGElementInstance&&(c[b]=c[b].correspondingUseElement);return a.preventDefault&&(c.preventDefault=function(){a.preventDefault()}),c},getTarget:function(a){var b=this.captureInfo[a.pointerId];return b?a._target!==b&&a.type in s?void 0:b:a._target},propagate:function(a,b,c){for(var d=a.target,e=[];d!==document&&!d.contains(a.relatedTarget);) if(e.push(d),d=d.parentNode,!d)return;c&&e.reverse(),e.forEach(function(c){a.target=c,b.call(this,a)},this)},setCapture:function(b,c,d){this.captureInfo[b]&&this.releaseCapture(b,d),this.captureInfo[b]=c,this.implicitRelease=this.releaseCapture.bind(this,b,d),document.addEventListener("pointerup",this.implicitRelease),document.addEventListener("pointercancel",this.implicitRelease);var e=new a("gotpointercapture");e.pointerId=b,e._target=c,d||this.asyncDispatchEvent(e)},releaseCapture:function(b,c){var d=this.captureInfo[b];if(d){this.captureInfo[b]=void 0,document.removeEventListener("pointerup",this.implicitRelease),document.removeEventListener("pointercancel",this.implicitRelease);var e=new a("lostpointercapture");e.pointerId=b,e._target=d,c||this.asyncDispatchEvent(e)}},dispatchEvent:/*scope.external.dispatchEvent || */function(a){var b=this.getTarget(a);if(b)return b.dispatchEvent(a)},asyncDispatchEvent:function(a){requestAnimationFrame(this.dispatchEvent.bind(this,a))}};u.boundHandler=u.eventHandler.bind(u);var v={shadow:function(a){if(a)return a.shadowRoot||a.webkitShadowRoot},canTarget:function(a){return a&&Boolean(a.elementFromPoint)},targetingShadow:function(a){var b=this.shadow(a);if(this.canTarget(b))return b},olderShadow:function(a){var b=a.olderShadowRoot;if(!b){var c=a.querySelector("shadow");c&&(b=c.olderShadowRoot)}return b},allShadows:function(a){for(var b=[],c=this.shadow(a);c;)b.push(c),c=this.olderShadow(c);return b},searchRoot:function(a,b,c){if(a){var d,e,f=a.elementFromPoint(b,c);for(e=this.targetingShadow(f);e;){if(d=e.elementFromPoint(b,c)){var g=this.targetingShadow(d);return this.searchRoot(g,b,c)||d} e=this.olderShadow(e)} return f}},owner:function(a){
|
|
for(var b=a;b.parentNode;)b=b.parentNode;
|
|
return b.nodeType!==Node.DOCUMENT_NODE&&b.nodeType!==Node.DOCUMENT_FRAGMENT_NODE&&(b=document),b},findTarget:function(a){var b=a.clientX,c=a.clientY,d=this.owner(a.target);
|
|
return d.elementFromPoint(b,c)||(d=document),this.searchRoot(d,b,c)}},w=Array.prototype.forEach.call.bind(Array.prototype.forEach),x=Array.prototype.map.call.bind(Array.prototype.map),y=Array.prototype.slice.call.bind(Array.prototype.slice),z=Array.prototype.filter.call.bind(Array.prototype.filter),A=window.MutationObserver||window.WebKitMutationObserver,B="[touch-action]",C={subtree:!0,childList:!0,attributes:!0,attributeOldValue:!0,attributeFilter:["touch-action"]};c.prototype={watchSubtree:function(a){
|
|
//
|
|
this.observer&&v.canTarget(a)&&this.observer.observe(a,C)},enableOnSubtree:function(a){this.watchSubtree(a),a===document&&"complete"!==document.readyState?this.installOnLoad():this.installNewSubtree(a)},installNewSubtree:function(a){w(this.findElements(a),this.addElement,this)},findElements:function(a){return a.querySelectorAll?a.querySelectorAll(B):[]},removeElement:function(a){this.removeCallback(a)},addElement:function(a){this.addCallback(a)},elementChanged:function(a,b){this.changedCallback(a,b)},concatLists:function(a,b){return a.concat(y(b))},
|
|
installOnLoad:function(){document.addEventListener("readystatechange",function(){"complete"===document.readyState&&this.installNewSubtree(document)}.bind(this))},isElement:function(a){return a.nodeType===Node.ELEMENT_NODE},flattenMutationTree:function(a){
|
|
var b=x(a,this.findElements,this);
|
|
return b.push(z(a,this.isElement)),b.reduce(this.concatLists,[])},mutationWatcher:function(a){a.forEach(this.mutationHandler,this)},mutationHandler:function(a){if("childList"===a.type){var b=this.flattenMutationTree(a.addedNodes);b.forEach(this.addElement,this);var c=this.flattenMutationTree(a.removedNodes);c.forEach(this.removeElement,this)}else"attributes"===a.type&&this.elementChanged(a.target,a.oldValue)}};var D=["none","auto","pan-x","pan-y",{rule:"pan-x pan-y",selectors:["pan-x pan-y","pan-y pan-x"]}],E="",F=window.PointerEvent||window.MSPointerEvent,G=!window.ShadowDOMPolyfill&&document.head.createShadowRoot,H=u.pointermap,I=25,J=[1,4,2,8,16],K=!1;try{K=1===new MouseEvent("test",{buttons:1}).buttons}catch(L){}
|
|
var M,N={POINTER_ID:1,POINTER_TYPE:"mouse",events:["mousedown","mousemove","mouseup","mouseover","mouseout"],register:function(a){u.listen(a,this.events)},unregister:function(a){u.unlisten(a,this.events)},lastTouches:[],
|
|
isEventSimulatedFromTouch:function(a){for(var b,c=this.lastTouches,d=a.clientX,e=a.clientY,f=0,g=c.length;f<g&&(b=c[f]);f++){
|
|
var h=Math.abs(d-b.x),i=Math.abs(e-b.y);if(h<=I&&i<=I)return!0}},prepareEvent:function(a){var b=u.cloneEvent(a),c=b.preventDefault;return b.preventDefault=function(){a.preventDefault(),c()},b.pointerId=this.POINTER_ID,b.isPrimary=!0,b.pointerType=this.POINTER_TYPE,b},prepareButtonsForMove:function(a,b){var c=H.get(this.POINTER_ID);
|
|
0!==b.which&&c?a.buttons=c.buttons:a.buttons=0,b.buttons=a.buttons},mousedown:function(a){if(!this.isEventSimulatedFromTouch(a)){var b=H.get(this.POINTER_ID),c=this.prepareEvent(a);K||(c.buttons=J[c.button],b&&(c.buttons|=b.buttons),a.buttons=c.buttons),H.set(this.POINTER_ID,a),b&&0!==b.buttons?u.move(c):u.down(c)}},mousemove:function(a){if(!this.isEventSimulatedFromTouch(a)){var b=this.prepareEvent(a);K||this.prepareButtonsForMove(b,a),b.button=-1,H.set(this.POINTER_ID,a),u.move(b)}},mouseup:function(a){if(!this.isEventSimulatedFromTouch(a)){var b=H.get(this.POINTER_ID),c=this.prepareEvent(a);if(!K){var d=J[c.button];
|
|
c.buttons=b?b.buttons&~d:0,a.buttons=c.buttons}H.set(this.POINTER_ID,a),
|
|
c.buttons&=~J[c.button],0===c.buttons?u.up(c):u.move(c)}},mouseover:function(a){if(!this.isEventSimulatedFromTouch(a)){var b=this.prepareEvent(a);K||this.prepareButtonsForMove(b,a),b.button=-1,H.set(this.POINTER_ID,a),u.enterOver(b)}},mouseout:function(a){if(!this.isEventSimulatedFromTouch(a)){var b=this.prepareEvent(a);K||this.prepareButtonsForMove(b,a),b.button=-1,u.leaveOut(b)}},cancel:function(a){var b=this.prepareEvent(a);u.cancel(b),this.deactivateMouse()},deactivateMouse:function(){H["delete"](this.POINTER_ID)}},O=u.captureInfo,P=v.findTarget.bind(v),Q=v.allShadows.bind(v),R=u.pointermap,S=2500,T=200,U="touch-action",V={events:["touchstart","touchmove","touchend","touchcancel"],register:function(a){M.enableOnSubtree(a)},unregister:function(){},elementAdded:function(a){var b=a.getAttribute(U),c=this.touchActionToScrollType(b);c&&(a._scrollType=c,u.listen(a,this.events),
|
|
Q(a).forEach(function(a){a._scrollType=c,u.listen(a,this.events)},this))},elementRemoved:function(a){a._scrollType=void 0,u.unlisten(a,this.events),
|
|
Q(a).forEach(function(a){a._scrollType=void 0,u.unlisten(a,this.events)},this)},elementChanged:function(a,b){var c=a.getAttribute(U),d=this.touchActionToScrollType(c),e=this.touchActionToScrollType(b);
|
|
d&&e?(a._scrollType=d,Q(a).forEach(function(a){a._scrollType=d},this)):e?this.elementRemoved(a):d&&this.elementAdded(a)},scrollTypes:{EMITTER:"none",XSCROLLER:"pan-x",YSCROLLER:"pan-y",SCROLLER:/^(?:pan-x pan-y)|(?:pan-y pan-x)|auto$/},touchActionToScrollType:function(a){var b=a,c=this.scrollTypes;return"none"===b?"none":b===c.XSCROLLER?"X":b===c.YSCROLLER?"Y":c.SCROLLER.exec(b)?"XY":void 0},POINTER_TYPE:"touch",firstTouch:null,isPrimaryTouch:function(a){return this.firstTouch===a.identifier},setPrimaryTouch:function(a){
|
|
(0===R.size||1===R.size&&R.has(1))&&(this.firstTouch=a.identifier,this.firstXY={X:a.clientX,Y:a.clientY},this.scrolling=!1,this.cancelResetClickCount())},removePrimaryPointer:function(a){a.isPrimary&&(this.firstTouch=null,this.firstXY=null,this.resetClickCount())},clickCount:0,resetId:null,resetClickCount:function(){var a=function(){this.clickCount=0,this.resetId=null}.bind(this);this.resetId=setTimeout(a,T)},cancelResetClickCount:function(){this.resetId&&clearTimeout(this.resetId)},typeToButtons:function(a){var b=0;return"touchstart"!==a&&"touchmove"!==a||(b=1),b},touchToPointer:function(a){var b=this.currentTouchEvent,c=u.cloneEvent(a),d=c.pointerId=a.identifier+2;c.target=O[d]||P(c),c.bubbles=!0,c.cancelable=!0,c.detail=this.clickCount,c.button=0,c.buttons=this.typeToButtons(b.type),c.width=2*(a.radiusX||a.webkitRadiusX||0),c.height=2*(a.radiusY||a.webkitRadiusY||0),c.pressure=a.force||a.webkitForce||.5,c.isPrimary=this.isPrimaryTouch(a),c.pointerType=this.POINTER_TYPE,
|
|
c.altKey=b.altKey,c.ctrlKey=b.ctrlKey,c.metaKey=b.metaKey,c.shiftKey=b.shiftKey;
|
|
var e=this;return c.preventDefault=function(){e.scrolling=!1,e.firstXY=null,b.preventDefault()},c},processTouches:function(a,b){var c=a.changedTouches;this.currentTouchEvent=a;for(var d,e=0;e<c.length;e++)d=c[e],b.call(this,this.touchToPointer(d))},
|
|
shouldScroll:function(a){if(this.firstXY){var b,c=a.currentTarget._scrollType;if("none"===c)
|
|
b=!1;else if("XY"===c)
|
|
b=!0;else{var d=a.changedTouches[0],e=c,f="Y"===c?"X":"Y",g=Math.abs(d["client"+e]-this.firstXY[e]),h=Math.abs(d["client"+f]-this.firstXY[f]);
|
|
b=g>=h}return this.firstXY=null,b}},findTouch:function(a,b){for(var c,d=0,e=a.length;d<e&&(c=a[d]);d++)if(c.identifier===b)return!0},
|
|
vacuumTouches:function(a){var b=a.touches;
|
|
if(R.size>=b.length){var c=[];R.forEach(function(a,d){
|
|
if(1!==d&&!this.findTouch(b,d-2)){var e=a.out;c.push(e)}},this),c.forEach(this.cancelOut,this)}},touchstart:function(a){this.vacuumTouches(a),this.setPrimaryTouch(a.changedTouches[0]),this.dedupSynthMouse(a),this.scrolling||(this.clickCount++,this.processTouches(a,this.overDown))},overDown:function(a){R.set(a.pointerId,{target:a.target,out:a,outTarget:a.target}),u.enterOver(a),u.down(a)},touchmove:function(a){this.scrolling||(this.shouldScroll(a)?(this.scrolling=!0,this.touchcancel(a)):(a.preventDefault(),this.processTouches(a,this.moveOverOut)))},moveOverOut:function(a){var b=a,c=R.get(b.pointerId);
|
|
if(c){var d=c.out,e=c.outTarget;u.move(b),d&&e!==b.target&&(d.relatedTarget=b.target,b.relatedTarget=e,
|
|
d.target=e,b.target?(u.leaveOut(d),u.enterOver(b)):(
|
|
b.target=e,b.relatedTarget=null,this.cancelOut(b))),c.out=b,c.outTarget=b.target}},touchend:function(a){this.dedupSynthMouse(a),this.processTouches(a,this.upOut)},upOut:function(a){this.scrolling||(u.up(a),u.leaveOut(a)),this.cleanUpPointer(a)},touchcancel:function(a){this.processTouches(a,this.cancelOut)},cancelOut:function(a){u.cancel(a),u.leaveOut(a),this.cleanUpPointer(a)},cleanUpPointer:function(a){R["delete"](a.pointerId),this.removePrimaryPointer(a)},
|
|
dedupSynthMouse:function(a){var b=N.lastTouches,c=a.changedTouches[0];
|
|
if(this.isPrimaryTouch(c)){
|
|
var d={x:c.clientX,y:c.clientY};b.push(d);var e=function(a,b){var c=a.indexOf(b);c>-1&&a.splice(c,1)}.bind(null,b,d);setTimeout(e,S)}}};M=new c(V.elementAdded,V.elementRemoved,V.elementChanged,V);var W,X,Y,Z=u.pointermap,$=window.MSPointerEvent&&"number"==typeof window.MSPointerEvent.MSPOINTER_TYPE_MOUSE,_={events:["MSPointerDown","MSPointerMove","MSPointerUp","MSPointerOut","MSPointerOver","MSPointerCancel","MSGotPointerCapture","MSLostPointerCapture"],register:function(a){u.listen(a,this.events)},unregister:function(a){u.unlisten(a,this.events)},POINTER_TYPES:["","unavailable","touch","pen","mouse"],prepareEvent:function(a){var b=a;return $&&(b=u.cloneEvent(a),b.pointerType=this.POINTER_TYPES[a.pointerType]),b},cleanup:function(a){Z["delete"](a)},MSPointerDown:function(a){Z.set(a.pointerId,a);var b=this.prepareEvent(a);u.down(b)},MSPointerMove:function(a){var b=this.prepareEvent(a);u.move(b)},MSPointerUp:function(a){var b=this.prepareEvent(a);u.up(b),this.cleanup(a.pointerId)},MSPointerOut:function(a){var b=this.prepareEvent(a);u.leaveOut(b)},MSPointerOver:function(a){var b=this.prepareEvent(a);u.enterOver(b)},MSPointerCancel:function(a){var b=this.prepareEvent(a);u.cancel(b),this.cleanup(a.pointerId)},MSLostPointerCapture:function(a){var b=u.makeEvent("lostpointercapture",a);u.dispatchEvent(b)},MSGotPointerCapture:function(a){var b=u.makeEvent("gotpointercapture",a);u.dispatchEvent(b)}},aa=window.navigator;aa.msPointerEnabled?(W=function(a){i(a),j(this),k(a)&&(u.setCapture(a,this,!0),this.msSetPointerCapture(a))},X=function(a){i(a),u.releaseCapture(a,!0),this.msReleasePointerCapture(a)}):(W=function(a){i(a),j(this),k(a)&&u.setCapture(a,this)},X=function(a){i(a),u.releaseCapture(a)}),Y=function(a){return!!u.captureInfo[a]},g(),h(),l();var ba={dispatcher:u,Installer:c,PointerEvent:a,PointerMap:p,targetFinding:v};return ba});
|
|
|
|
///////////////////////////////////////////////
|
|
|
|
///////////////////////////////////////////////
|
|
var config = {"dark_mode": false, "show_pads": true, "show_fabrication": false, "show_silkscreen": true, "highlight_pin1": "none", "redraw_on_drag": true, "board_rotation": 0, "checkboxes": "Sourced,Placed", "bom_view": "left-right", "layer_view": "FB", "offset_back_rotation": false, "kicad_text_formatting": true, "mark_when_checked": "", "fields": ["Value", "Footprint"]}
|
|
///////////////////////////////////////////////
|
|
|
|
///////////////////////////////////////////////
|
|
var pcbdata = JSON.parse(LZString.decompressFromBase64("N4IgpgJg5mDOD6AjRB7AHiAXAAlAWwEsA7DHARgAYAmAOgA4AWAVgBpsRCiBPLbANgY0A7MzYcAhmlLYqFMjQCcTVuzySe5AMx0azJgF8xkGLF4BtUABcuABzC8QNlABsuUFERBinpnGYpsFAC6YuJEUM72OAHsTq7uRL7YZimUtHRCbALCQiHJsvIKmlmCQrlsZgWKxdhk2rqaeWZp9Jm19QyNQXkgAO4EEJYAFrwUNBQqIABmBM6REKP6PRAATuL94UmgsLMA1rAAxitgYJ44oABi5lZDBAe7RHBJYwxisABuUDbiww4AsmQGPI6GQqEI+CwyGQmOM6JoKJ0ADKA2gMIQKBhgyHQ2HwzoAzHCPhMQFkbF8Gh1Ch8PhUZFA+ig8Hkynwml00mMsEQqGCKns+m0cEkoHYvls2kEzQNMh0Ch0Fn82n0wRojFY3nCBRCTRUOhSmVyhWa9E6vUq3TozGZKEUpVUAmCJRMPiysVas10ekwoRkIRMKhUd2m3X6wEwqhAzS6932700SN1GNQmGMBgYoTx6PEm1kCkUTRMTEMeMBoQInl51nU5Xh4R+gNB23V9kEoSUhh8CgYlkFouR+kU52u41VuOAilMEQ01jN8edmjaIF0BS9wvFwcJugrknY1MMdNozd6ndklO4hGaTdL2WrzUrqguvibqed4nB7WhzfDt0mz/mwF2wnbtXnvBRHxpel20TaMmxxeU8SvQCEyjZMcTBQsyAUY9tyUM8cU7TQFCUAkdG7Rh/Q/T16TIhQKNnMcJQdQFaPo906KEOUSxYykil9YowIg58eJvFcqK/HiaWIhFxIAhgdDoLttHdB8nxoxRlBHPcLyRHif1HO0mIJBQuXBfD83XAdoTGCgCyoTRc0MmsOSYMZRLvRjnORVyaFfGlKws/tuJ8qSFBk880wzbyxlC8KcREbVu2i3zpxpbTlHA6Nkvc7TCOIphkvItEGJhPKlHpEywvRYjtM0PgMinCrTNdbS/ToPUyCakFuXwmEEMvJqquIjzBFUyDrPGOjitkr0Jpyv9qImvy0sEp8ARJXywQRRyW1rDay22mb1vkA60VjJjvPkTpZW7d1AyYbDoROrazs1XUoW0S6aFpQM6AYwR7se/aXtzcUvKezby1ewFFyoD6wwDIkRTgpyBWhWhloC8ZLOC1ErT1bSMTkP7vNoG8PJ9dNyyBsnGCw1rOI60nLXVUcnQyLjmcxlTwLU9GkZRGbvOlCZ5SnIXoWlGDox5oThcXOqp3w9nOIReXrrlCnKUZkF1c0G6tYS6n5ezJXCYYYmCsllCkwEqs+w3a3Rb+nbx0R4VBbnIzoQB3CAwlostx3FHduYwOTyUOCVc5n3Fzpkb6F58bA+5haJMDxSCxBAO+tcl3Zb55QOy7JRtP63Si+A0uvec9adFihjUb29tToM0O68miisbd1i0UrMG0anRR5XdIE+Ac4Sh99f1I3dLt6oDbyW5etnhGobt1pMjXldh+HvJM1u7sfIGTOngNQJh8cu14hzoXO8Hr/JnfAeRa+1UxBP3rqWbr+W0HfNtICV+Yw/7BnXo9N++ME4cRjg9BWOYQ5XxAalV23tXT1hnhfaOas8zPShqvMawl0Hbwlug02Qgo7fWpH9DkZDFYUPdCSXB3FiH62zm9OG39X54IRKvF+uCMHn3vmjWknc0T/yvrQCOTA7YA2Pq/WmQIFA7y/p9PMeN1QJyYa6FhGiP5z2oYvdRcclE7xDABUR0jZGJzlsYoqZR2Icxwegs+s9NQwOcTw+SZc4QDQESQ88bUmYCPIXBGEdUGqZhCfQsJwgqZJX8Ww3qigLayitugw+zYHZWRcQ2NxnkRG0Hsagh+Ui/Z20Co7URT9zaWwUSzfR54jYJNEe/KgWsIn+iidU+OySgm62MVYnxiF6nFMcarBgAJx7wKnIgi6eZpShOEZKBZkNeG1Xql01+ggxk11bHmHZU0HFpwsYsmJ6UHr2U0MZb6fBpIXx0EwbcAgrYbSwrfB5vlnnMHWi8Nht0oQmULBMV5fyDbYnbO1PgIh1rSlTrKHIyi4beUEKdXMOgyhIrILCkx9NAWLlcsoFF4w84ughQSkFxLXF2yBYSpg60fTEk9gioiII7neUZcjbEtKQUMrEcyKE7ZgVEuhJy5lOhWV5iBhSDWAK/QUuUASeQ0smxwwTLfHBhJbK6jqpCNV9lfQIiVWszseqMZQzUQyU6PJZD0BdOjek8gfp6lAramc0LhIMm6gK21U5LyzS1XZXVoJkH+oJLQH8NpAwpX9YKAWopQR4yMYSWKAlo0OWIq6ONy1ZzRtvPJK2hIc16qdTdRqKJbnSTTVdMETy40ewTWq2CmhMRxsjSW9VhruKdBtrBDtBrKDdulLK40TadWtsBHCl6Nqa3+gDdKZ1jAO0jgmFEnti7XW0HzSTSdJqbVk2qlm3dI6zWWmTVLVCqrzWxore2xNMbOhhgPA0jU0a/WPotG0qNyqNWTMBKqfG37vplrXai1KubZ11v/ZWsK1aFb2Qnc+zOGb+2/vjM7MloIf1dtIjffip732WugpegjFqA3EaTKq0Nj6bmLtzUmjk6ZDQArfWRpqGH90PstQpJSo6GPqQbVercaJ7ICaZZiDtjYQSPXkvQXjHbm2IZMhx1DXamqyrvPqtDgITJ0YU+Oxjp88lwak1hJq1KO38XaYWjF4nVWFALGUbFS1Uozs7YO5KqdWNhpc2+Nz1ooMhTuWFV18hFMuTGMh0d2GPMQyrhB9zOCNrkLPGOhDLkrr/M089DqQNMvgvvYRhGii8XeY/fzcmqnB3rRK5p6jajEbSKo7cl04JmaVZDUSbsQD+ZtNHTFYkHr2vxz1fV2aiMOu2vBN1v9MiZm+iq0ls5OZUvXpo9bFLHaAvjeHVl/T6X5Z6aw4l4K0owrRZO8S+xbnwvEveb6ULm1ct3b4oCUjPmXTjCDRdgdSX8zfdPX9V0Rii7nb1SLN8AEi7etpODjsspk4wiO/ZS7or5upaltpyunZuy5ulKSRSfB1r/Z1Xxs9LlUxMlh4VtjaONPvfK0XaWab+No/i6ejNCgj1Fww1GrdK4C0cpJWLIDK7y3Y5Lgl7bQvmf7ZbRT5j2XUfQ6p/Z5XJP7IXfC8TjS9rNNrYa0OTSpWYtGuhEbvXy6QNL2F/nY70voRARx9XNL8ubebeO794Ky98GLdm8p0lfOuPjYDyLyTv0zPQh0D+JXt2o/xrg177yEqRue6xzoe7b3jva+hKHu30biouR41nVbwfk+4v12X+PmfN3k/L9d09LqKFISefyzjIFIxRNbzXjtsGnPl8m8CMWWV49RdS0Pv6I+4G88s3UazUzIu8dL0V4BcmS+jeDwvtf2hS+F9Xw3Bnajr5NcPz/GyRzON77zDZAH97Ofc8qiPe927XmP4u2L7pN/SdbdrWftvcuJ1j8/Y1ck9r8YMJg/dV8x8ADmJplpZXUxhptlEWFltZkN93U2tVlMlfVadshtV7I6sq9shM9c0BtWsiFfYdw01ECS4etsgalOs8IiJtkK9x94dCcWChl7df9X58dndOMV8DlWDT8CQxhpFUsfRYMqBPUxC/YMctQ7IidARZDTw9VUVGwkILYX0o1VQXRiJGNF8s41DdA9DwJHVbdMNIxfINDzCq5XVBABBpwA1lUSNQRRoM1mBzCVU9VSpuwHJND5A7CfDfJiJGAZN5AVNQQkclB5QA1DDlIoiFDdQZDtDgj0RFD6QVC8Jgi7k84n0HNqpNNdDgtGMSsxJEj0jkjw1t9R1ij9C415pHwkjpC41M800KRpCmVPUikjlc0KQ6oGxu0Cjho9UZVTDmJmALCo0IxZQVw10NcEimi4ZcIhBHQaiJDmjPUYQ0U4dKiWiJw8Ms8mi9ilC0RmpVVJCMiDjoCKipDPUKQbiUcTjcM2kii4kto10TIq4bVShrQKBVidNDQNiTj1JXi0i7jQSoFdiITASAkni7jfl+Uo1pQQSIY4TLiqi0SkloSrifJdljiET+YSDgjqBGBkVrZXFc0+oXU4Z1owM3xkTFAjRqBiUD9QRpRtxNJgpRol8cTkihcll2TNihdsCOS/oXRgokdAw9QbUztmSFdXF7DtYmRxsfQ8klSBj/RJTvppTFJjDHDOxxsANNFwTcS1TMFTTMTPtQFEjSTMROp498TqSyTnNr4G8hTOSJT95h5R0USYS4FF1ZThTc90c4dPSBBvT2c3Dxhptgo89xZozOiSRJ5o9jc3iDT6py8Y9jCig2EW94zpjFxSRCxIzncqTfIKwoRvSPcmjcjYjvTAzLT9i4EYcLjgy3SL8ciYjtxV8e9EiHopoIExg+yrDczZR8zziczoxxypkt1XtXUMT9iMkQZjC4QrR6UBEZ9birj0Etz4SdzgRVc+Slyilb8UdwzdF/8hS6J5QWTjERy+RfQwQf45yPljDmASjRlOzoyMyXyvtv9oyRAPjuFJzoy1yMR0kIjA9VyPCNzRFszEjli5jeCfTgikLtRZyE8myiFdsCt9z+ShD3SCTFCplSpsTAK/julShxM01fiPiplqKRQ01FyKCsKhTUTiRBE3F8LpCbk+s9VoJbJ2VASvMgIJhmBDN41VV2w5BqAesfJZcmRxg0h5KwVNYBLKRxKQcQEVylLZBqQgYYpdTc0hVlE8xJ4jLfoTKvkDkIsTUo1IVoVCxPUgVU92llK5LgpLK9Rc1lMVLZtgR5MlLZK4ZcYpiNKQr5KpFUI00xKiwQcyZ6FUtBKDLmZ6clKiJbRJ4MZdKwQchOFxscqoYo0dBIxfEaZFc9V652lpCHSJskqqrGRPwu9orbZGrkDtRsrwrQQ/LPKvoos9VerQqvpYpXVo8dNMD9pXNGrpMdQu8rp8ZR1qrwJucws3LSr5J4Q8sK8NKygCqvp+KMqzLk4Tppq9KhLtrRqNLKB4qMsaiIr/KcUT9zrUqNs3K4qJL5ZRKPLhrY49yZL/LiVFK8qbrPrY4ayUrhLA50q8rMrzLiUYaW42UJ1A4Ia5MdQXR5YRyhr5LVQ9seqfrAQddgaAa+q0dnq8rIrtSRz2wudj5XkIxgCNLFIMbJ5SpFrdqKEoRVTddXRNN2w9rubvI7QyzmanLMbzde0ZYlK6aHpXl+iGrQQM8ZF/i6qFaVtGraQVq1apa00FIsI5rhbi5cd2qJq2bebSscbtTDr3KqaZcmalbFAzahcRz9bmqQBDBcAQBhg7gHgnhRhKQIR2APgvgfgRgcAQAARKBdAigBBihfRNpwJ5JkRo6MhmAucWAE6Awk6wxo6gQRAZFM75Aul2oU6Yo6YgxywNJGYy6NJ/i9SE6S7mIoRvpFYi7hApxS6oRAj0RCxMhG7O7m6rp/jwJVwB6a7u7dAR72lM6xglAJ6W7pydQFQE7/i4YJgU7lVlAiwV7Aj5J2lhIW6/QihxYE6EdMqo7ECpoICKEdToUwRa6kU7kyRb6aR96ORo6YjOIIRb7yx2lZRH7txtxK6hR0RKBZpP6gH2pM70hcyMRH7XJv6YHFBcjGBH7QRn7M6RZ/jMyoRItFY5AsHxhmAygrZo6ZE/QiIiGEQpx/Ra6KGdQx7oraGyGr7GAb7mHSHa606Hof6yltAolP6MHlFkH3oudWG66kHb7JUJbP7EGG68ZOhOx0HpCRHb7lAJgHJuGC6M71HnldRa70R2HWBX7pCKFBGQF9ZGHkHaR76ASW7/R66f6IcWHN6ExY7OhqGSH/Qo6cs4YTGXGuHJ7Gx/HkHIxXHJ6DwuxZ5pGuxqQ3G6o6IW1kHOhaQSQ3H2k6pPHb60QlG+APa2AbhfbHhYBnhA63hPhvhfgI6AQihiGsn47h7Axk73ldBwndGmmyramI1oVmBGnW75QOQ6mENQRZ76B4Qu66nXInH27x5BnamF0n7K78HBnkQMRKRFITGVnJmzssI4RXgz7CwnkhmdkW1AV266hlBJm+RKB9YLmdRoUol1mWILYTHlVBUKE1m+RNn269R0Rx4vmNn3wE60RGBmCsI+R5JXn27lBOx2lAW8G7ms7wIKErZ1mCwgR6YE65mdn6AQVvFsWJmhmUTMnT6ToUXHw1nFmMHK6ToiwtbanVQlmxmqo5BAXs7CwV7F8VxoRAWuxYWuXxnVmIWtRomDntmTnWhAw74q7FIeW0WnRbmX657/i2WRWMRjGWW4QChAWNXoXV6HpMrdWlX7m6oyhAW+m8x+6nUiXdWoWb6bX5nWmW1NWcniJzGqXdBbG1GMYHJ2piWKyZn1H/RlFZopntBgGbGAxwHPWZEgGQGiRx54HWnpmpH0hMQnluI6m4XMHb7txqFPXtBiRCHb7Q2gm6n5RKGmGnaMg6HWnK3rHb7uby3Fl7WTGt1ZQW22n06+HKQXQMhPWc2fXeJ0xiRY3bI02ExfRzdx2g2yY0QW1hJs3vWX7UR7IZEnnh12ne3OwFkA2XX9XUR3WH762QRG2j3tQwQFm8WBXkHm2631n4Q53KRO2H3TmgQE3MRCwtGRWXmOG2nv2nm5Ee2UnXRx5JXpDVHV2Y7L2gPFwJ2FGUoHoAGRWn3J3wQk2s2AYV3kGyTM3AXmneHcO5RcHnm23kGy2H3dmkmDmI1X20WOTUnsn0gSOw2OT8WV703HxGBGWqFb2m2MRxG+WmPaO+3NlAXfQaO736PjWCxoP72FWmTi35P+2nnqSQOBPR20W+ocPb6wQUwl2dOoPRGF3lGsJ1OiOcmwODGRXWs+mKOZPzPxhdO6Pa2mACmvafb7gSmym8wKnQ7qn2Br2D2ICqQchxEA2h2zx4QuQWbZ2kGwu/puwAWU2I3oGwuUXJZY20umx9ZiHJ1HopmEOeQ8vBUtbB2cO6hAjlBUXC3NnCGqub5cWG2qHGv3lmuz3WuYuiJtB93yOwuevcXiwiOwvrplEGOvXjPRvyw1WiuZmwv6WdWU3ivIQYufojXWmovVuYo6JjnB3t3tvhA6pHnC3+uYuygIvPWWu7xzuRA9rr25Rb2Buihrn4P5vuuXvJWMXRmwu7k/pXu/3Zw1vdvXvCOM6wuHmTuRXIPMGIe7vPnUOVvEvXIuckJH2ke8uEQCuCPKvSu/RyvoeDu2uavKXf2zvAjPvPXlEpO2vKfWmwW47VvqvZkJvHud6meX3wR2leP+X2fpvbIHT1m46BBQIYuxveWRXJP9nDvBvJXlE5OZe6f1m6JlPDvxftPxgiexeZvBejPYeYvFvZu9eRHfvAwNulBNeQOIfjvzXbPenC7afeu1njfouKfevr3LxGe58EwZFZA0et2rfdQffdQCxumUGC6BIg/5I/QqP1UP2Of6oY+JvU29Tvfo/SHr3n7IOE+z232gWgenVc+JvqfpfGuVeN3AXvu8U8vy+f3nmTXvfqBCJJXAfVuilaQPXf2fnvfv5Q2EXu+g/dQihk2yPNW0+QU0Hf2G+o/Umw333zm0+i/PW2fvFF+k/PWKET6bQZ+IlPWs/qco+J+/11n0waWOeukhPbPSXjQ8upe4PefV+8vE/y3Sgucm+c/1+RWMhpWC/6wOJZO7mjXC/mO3Vbk9xmQICWsrzAHy9F2AA6LkUkjDgcLWFDaFG310BL9QBY/KPkXw85FNvO/taIOU2DqVMw6/wPBroE6CvhDuAYf4gj3Iad0SuICJ5HF3IFy1nQ1ArnKj1rrjxXm0XGKDqCy7kCeBEwPgcIDTrgtyGSgdgYl3HgE96BCXGLmFD9Ck886bKbfnPVZQf1hy4IMHBl00G118eugmLnUDohaCKBfdUXsOX0GsCDa74MLjH3G70MGBh3OGF2AdKSC5aQPMQuBCW4eDpB3XeqBt3kGp8xeB4PbjYM6peD0BZjCxuYKoELdZAe1AwToKfwG9EhFCXxouCkHKBDuw/Lup/QjY/dFBmgzIVayXC5DrBR9JykunsGVCIiZlIoC4KLA3U3GcgaZpHzcjwgD6j9QoblzciBDlEl9YPuRA57DQ/e3AltCINGH6FQ+5AwwakKkRgggmedSgZREa7WhlhiBNIAmiD6BgygPjSekRE8FoC9hmw6oNuCKGLD9hG5KocvVF5XDlhgRaoaL1nSPDJogKSPidExDxNJ6bQ7VAn1lCPh3BGgi4blydQuhF2uA72rcHwGlMA6d8YgQF3DpBdARLWB7LtViKR52ok0TEKnzyrlgLhCgAENiMWIC1MRj0EkaOgpAEjpMyIbEU3mpzSE7UVrYSNiNvBCIMGKSZ+txGxEd48RQ4CCtGGJEixWYNqMkYSLpEijcRNqakeSOFFoiji4o2keOXcyeMlKdQRhvKJnDojORNIrEVKJlIaU9QTfIQF4HYDvBxAzgXgGQE9pWBbAUQYOmACgB4BTglgM0SAFgCWBxAKwN0X4FRGF1agxxRoEYCIALA/RcIPtmwAqLBj2A/QQYMiOHJUBbR3te0Q4AOAEAVgBwSIO6M9HejfRyQWUKWijFBiegawCAAQAACuZTcCGIDjGBdExyYrzn7ThGECERHokgYF0jqXNFwB4aTCwDlBHcsIOCQsD2Oj6rgBxsg/yMiBHFERIwykAcc0NxHTjSoFsVdP2JeAvcEkI4mRNnVYADiomkeEcaiyczrishLsB0iONR6E5TxrKJWNOLtBFh5cp4g8Y9DqgoQlACofcZuNfEdFL2p4xcQBDfEtoxxp4yceNCAm9j6YA4gQSFnvHuMGEA49qA9g5BvjIwH408WnTMhwSfYT4gcT4LUSXjx414vCcFmIjLjhAj4E8SROGivjtihYazKePwmzQRxB4WyOLEQl3cj0M4pOvOMiz6cJ0M4yCeOMQL6wQsAIVCX+PRCKAzezE/okJMzqVQZJcE1iWuKkmcDhoykjIB6lPFyAzIYYCCSBM4iKAokBkvsVJIAn6S5JhkkyI+CVjcRBJ1kn3nZPInyRwQzIKScCkjjkTjmyBBSRQL5rkS5mMieOgfCBC4j3RFoq0eQEbGpiI6sAJ0S6KIBui3gXon0eYDqCoogw2AMoL5B6CnAwxBYltJtDYDYiDwPQOsQmMpBJjCmKYuwA4HinOjXROY1KfmOaBFS4QKgHKUwDymhj0pRU/2NlJbjlSBg9YqqTFLqlxSEpTUlKXmL6nuEVApUhgD1IKltTMpJU1EEtNrEjTKpoIcaQ6I9FTSkpzU2aX6IcgJgg6XU5aX1KHCiBBpuUrafGPhHVTPOsUx0Y1KOkzS0pp06CBdKGkhiVpGo86etIoHDTHprY56XaImlvTEpyU4Oi1L6k/TgZZU/6ddJSQLSNpoM0abtJqlNifO8IyYCHSqbIiuxjAYDJrFYDEQxBY4ukaUFtjFBKZnEambKFGgi4yQlM7VuVlJlA56Y7MuUKuhpnDxtAt0SmdqF3ECy8IuIyELZEDq0MHSpMiWQTEoAvAs440eWVnABTSyHoSjDkFzMtxKzvoO4WaFzNZlSyRJCPUmfxGjACRpZjM5UaTJXTVwbZjAO2ZlJ8o2gbZDkHWQBgDAJFpZ9UD8QLOtB81TZIRbWQLN9nKyi2rIwQBHJln95mZbTMyKuBFkzxyoCcwvMpF5lsSokpMqTOLHZm6hOZDhA2BTNPjOysIxI0qD1DvAezzZVcpOSHNtlYjwkxoaWfm35lpJ4MO+EOSiw9TEj+ihRGuYgXLkUjUwJs/WU3KJEI4cRPlRuSPLpH5hWYpcqmXbJhDDRDoIs7kJgT+iCz1K+s6gP/R5FjzzxIc1VsFIimWjrRe0+qYdNhkej4Z4YujmwEpm1orpj8p2s/Ojx8BMZO0iGbVP2kNSYZx0r6QWOxEiNagcgYQG/NAVPyIFa1bqQ9Kxl/zrAUMg6e9Lvm5iQFzQDIHak/koNoF2C2BXRHwWILf518yaeguAWtTO2uC7AC/P9AEKaFf0PBVzh/lPTyF0M6aXDJOmgLIUKgSgMXUYVgKyQcCqBaQvYU4zXpaCoBZ9OoU4LmFoi8oOwHynpT5F/CyBdoAQWxjtpEizzjCObG+cCZHY4mQCCsJFg6IUExApJxwRNFCwRQCTNBOsXcQmizAZgJ+MQKRgfKyIJoq5EDCgSMO40HxemD7ELiQQS4/ssEssVZDccHIHxQfNPHWYHs3i0qPFXcUoRyZySglPYqDADj9KfcbxaikiXCSEwtkPuKYu2JFK/JeSzAkEosVj1T4Ti8pb5EqVST5IAU6MuYrMlbxFIkeKwncjMY/0D4YSgCFYU4hgMDmumAJcJCsIdTgpfkpzCFndHHApgV8nGforxmtijFSIsgX0v/rhQJg9Ac8UhF2Xy9QIByi2JHGuTRlkCfYm6kyVxwOkUcNyvFAcscIjgClTtECXcv1i4iNyVhR9LSFbmv9toVsUcixBeXAriopi3Zl8oOXskjQHygFYrIRAKFio3imFbcpRUkMUQpi1FNGGvGDoqpQs2aGYvxUrQCwi4PmmbieXgqa5EacmY8oxUvKyYVK5xQDHnoLYWguRA1B8sSWcrqA1hTXB8pbTRg6VJhZCcKvl4azUQMS45aqEUid4pZLKocWyoonaylVlKlVR8ukxPINVFyn2dqvvp6q7JsSxVrCoxg+ySVZqzFekCFnJsZlRqloLmVgjarzOrc+lQ8txW+QyVPISBc6ps6kqo5UsnuiiDDBWERVrXTWZat5UcqzwfsvuChysJlV0YIc7lT+ysLoxfJE835dMr5ATAGJ2anyrmo8prj9ZaaqJE8tpCPpU1CaklUyqHmCq1ENK2Ff0K1XXkq1sczolOOjKyVS10suGCFgrV5rdxIcv0KGo+WZqs80ssdeEqTXR9/Y+sgddtBjWyyQ5XawJTHM6CRrW1sE6MhGobXKAhVe6qVQ2reWJq8aCq1uTtx9lDrvVQawtUDg+UUJ1VD6iWjMrdWprLlt6lmk2HjV6SPlzyg9dGuuW0qQ5AxXdQ6oYSLr/IG6pqrqsXUqrb1z6p8frJnUjK8V966dchKtUNBL1a6xDZKqVkNr11xa/dWBtZVerANLASmeWoA2gbKZrkBNYaoYSUz9VomMCh+spngabFDhNyfZGo02SYNxatpYqpfkEboyN1BiWJsHUfLe1+cwTTBvRUoM5xCoLjaGtBX1qBNU9S5Upqo0MaaJj0StSpq020ae1Ss+Te8UfUSb81pcBmYOtBWbrRVWm9ImiujJIrVNp8HNYioVVnQaNX6j5cFOvH6aaJAWn1VprY1yr0BVazIGpt+WIqi2BMF+UJuFVbrk5NkxDZRvo2lVktIGkCcQrhCaw/0YKvLY8mA3Nq+xxCw9U2p2RThtJxCs9ThsnWvBiFQFKzUmrYkyx8tdqorZpu1DfRa1um+jS3DK1nYO1qm9sNionRWE5NMWibbKonU2aKZQqVlSupPF9aflXijpWFvW3qan1c4iTOtpW0cb0JfWiLcxqDCnaTVdGvLcNqPXFaKt7YBreduo0C0vNx23VX1pc11tRl+2i7ZCm62hag1fWzkvxFW0LZgdRyhbSOuB0A6zN2cl7aiu+2jbjNn2t7eVp5mPba1F8qKbUA4XSKuF98nhc0ARTjxOpItLReAF6l+iSdhYYsTJWJBsLwZeOwBQTswXUKad/CymgzpRmnS6WnO+nRToqm6LIZAC2+VQvSnTbS42U8nYwo51sBwQHYQXToqZ2SLUFLOj6dwqwWNdt68umXTzoLFMiTa0uxXYzuwANjVdouyhbIr6l866dznCnSot53WEydJu8RSrpemoK4gbgDwO6J8DmAYgwQUIOEGzHRBvALgb3YkHMCpAxwt0hXWLCaAZTiGLu+PRUET00M7dURBPTHv51VTup3QN3WbqqliAZgcwSAIsAt0OBvRBwcXX6KaJG6Fd4YUseIHLFVjrR4wWRWEAiAOibI/0zvSHtqADiYgfQZXYXuxl6LimBA0fVsqJlkCsV0YdETvKLBMyKVCqw0YvqEkAgUVLEfsRUuX2QtPxhSvfSSm5A5KHCWkzAhSvnpCILZ7UZAsiBX0/Q9SXM+EMFM308lfoEIdfUftX1P7D90mTfWRTfJf7lRBytiZOn7FOgdQREAAw0CAN/7I8KKn/Z/phC2hO82O1ZR7st0yLNd1C/4t9BKl9QFAjC+UAmCyk7zuwpu83ZgbTEZisx9ga3X6OpAGyCDk0JvS3urFZTh9YM0fcgqkXq6MFD8gsXCteD8AWZm05RVTqEMoGRDVdLJpQbGkV6KF2BwnVruEPy7+h4hynQDOjppRspGh+Q2PpF03yrdOB9KToYehZAgUAgWXYUFp38ArDmhoXe7qMNKHWdgh4nQNnUOUrNDjugsToaDp3JvDBh3g2rrF0MG/DYWOw4Ea4g2GFYnU5WY4ZH1UGXDnCjXSofZ2RH4jU9Hw5IeJ22Gsjg6YI8zrCOmHqd+Rrw4Uf115G4jlhvFoke4PJH/5xh5Q2zrMN8hHwtRuQ1UZNCEN7DQRgvY0ZQVYG3DRO5sAeE6PWHuj8qRMBMfqNILijJh9I2YejwKg+jXRiQ9obLkiLAj6xrg/McUOpGBDoxtVANJ2OTGNjEusmHDFmNFGDj+OtI60dr07IAjDhxhQ6u2OvGBjCh6g64YePuHORLx/oxcdr1zaPjQJvY2QruP8Ga9Bur4iIbOM5GAZ7lDEGCd2NOGeDCxlo/8af6rGETjC7AaifOMQnhdTR340ca11FS/Faxok74banUlCTcxyEz8cOMwm2p1IwE7sdpOAz7UNxr44YdJMsnwjbUoFLyeBMFitCAi0U8SecMCn7j5J6hV6nhOfGxTzQFNKKGpOMmSTQx5oyMa13PplIGpxhZMULFSn0TgxvgyUaWN+iFwNYw090bOJc1TTSR74ykblOsnYSHJmk7kYmi1UnTDRl07KehNCn+YMQPE90cRhygGTtx5k26eDMpwlT4Jrk0XDOZ+n9jMZoM6UYLFDwR4dplU47kOVRm+TIR4Y38dGMBkspYZvM2/GuO5npTGJqE5aceMFjREfnWs1yfgotRazZpgM9qbJPunsgbQSs1ofSmcVXIhZ7Rf6f5O9nBTmZ5oNChQhSn2zZEdU0Oe7NTmLTixps3ObLmLnvTVdJ5OObrPmnQjm5/49IwrPKnhzfonUFVMPNrnizOp0s1rpEByZdzAM/0LcjvPOn1zJ5rE6MZykJnOTe50qr0dXPfmHzfZ4M2pMAtemAZ0Elc5efvOYndTci1ELicvNcmIxHUVM0yddMZmrToC6km+dUWpgazYFycxBZnMEXsFLcT04idUUSpOz5FtM3hcbP/GwolIYi36I4tKCcLWpjc3+a13gR+tXFgsWRz4synpzsZ2c2Ty8NAWAZ4lrs+BeQtPnqFNGgI2IcYXqW5LRJpCw2dPOjHtLehxM96cUuyHdLyltZRPpbFT7/OM+mpmOprZWzNa1mWaAnQyCq0xq30JIY5fMb54MUyax6IS1qqaYArTMkFnMUZH1wfLCdIiBJS8sHmucDpWK52DCi5odAbS5vMiATrtJwIeI0qnTSPCOXcrhox2pLHBYpW7koXPKnTTv2OW4rHIvKizSGyOX5InVaSvlXKzBXg5SlZ9cqMbrPkEy+IwK9leBDlgdR+I5DW5cKDjlpaTV/SlOABB6dkyqA3qyNeUGtBPLu1da+o38LsI9QmlWWdldoD1RqAixDPKul9DHWcgRYH1MrVFXXX2oW1dKyUqI2PXxrRxAq9FvetbXHaMiMORtaesm1HaWtKDL/S1J3XvLCPV+nKE1yNU+rkeaRhBW4phX+rUifQvlahschlr5lEqjqQ/FLKwAKy6KVZdhGGK7LpAhyzea5xETAc7Ufq4skE4Lro0wDf/ZQwNk1hOMrNxGwuhI4rQ1U3NoK3Cj+iSz701AcgtleHRKDK8IaOqzebiuRxT0eYSfFcpfNPJ+wF2QW9lbP2c2O0Wtv0KUBDbhQ1URQG9C+aes+ze8qsctObenYSYxbZoNOS+bLYtR700hByFdYNs1szIBGYmONFtu8ghM/St3F7Ytv8b70YysG4bYejG3bVRiF86df8h03nsXt9W6LZZv03EbTLOQJXg6nY3VQGbTldGkjtuWY5TNoO5lAnQvmW0UBlnBWQRzCRq7C7Oa1InHjxzq75d09P8SH5WwXzB4SiWwW1CPcokfd6W73kQil2vkGtvW5nankfmN2e8gW7PeytI5owh0Y7PrY/MUJ6bCWU211Z9BG3HsQ9juR+bDt13u71mFe60EDuN427nt0+3baExjmOCDYegOu3DvRpg7Vdg+zHdrwl2r7292efejztX3x4eIIPJvbomqxc7y91+/3bNjAP6JV9hW8zaFANQ3L4SZu3Xa/vY2Iw7rI4hjD9uN28H2oAh+dI9vJXMHtdru47d7vhJO7x2Y+zbZXED3x7ZtqB4veBCz2lr1IvUEA+jSuCHU+PCiTveTts2FduVia1IlwTx3fxpDv+0RCSvZW5J1DxB7I4oGsPgHsDhXWncVkZ2U7CuxO/zbjvY3eHojiO+g6Wstw/WLGMmDHYSQ5TZAB4H1HY7CgbwY+JS5x1ze0eCUvHM9gx49r5tuZBb6Bkm+mbYv/mTr6IYsfmEBCMLX60TwMSuO/lFmVL8p9Kb/XpiBiOi8keJ0KCyeIVQQ0Z1iwZefNCgLD2TqekoqvMFjf6FT20jImKeBmInZTrUKuEqc7i8nbT4sU6EfBNOpL+Frc4DaoaVOXkXTuEO0441K6KLaT903m0Se6igQ4zhZ6Mqwj9OBLKFjJ3R0mdMj6bXT6nj09bpUB1nv5zZ9eYjT1OmR52fZ5c/ULHPUn+lwS9QtLY1RAxDhIg90Zec7PjSJzks+k/OeKBXnnIixfs6BdMiygvzx8/89qciwRnP5D53mZvMFhvnugKEJC8gsyWkXcLymumHiewudn1I6p3pfCelPnnIsFZ+87xcxkUXNd9F1RaGdIuVnQEOoFS5HoxOcgdL6S9RfZvnM3n30BFzU+aA3neXiZDIJy8Gdnn8cBTkGjqCpcivwXRLyyyS6ecZP8ctz/l3K/Vf2RxXLTsl32x2fQQYxgrnl5c8Jc6vSXqr3iCi7uRyuwXj5FJxOZYvNOLX15/HGC+giMBbXBLjlw8+VdnOYX6qa1wK65M3mCChzzPb65KcqvXXgbu3dGAd17nMc3riF5G+dfRuA3YIIN1S8zeHPaB5r9N0K6lhMu44RrkN0W+TeKuZnjz/14W6LJZvPniyaV6czID5ua37N0SXbo/hUuO3lTlN469wtpu238t9Vza4bdZLixcpSt064Ge6vLXdizt7K7Hfzve3U7gdzO5dcBvFHhz0d4i8Zs7OzszAVt6pbnfuuAECb983u/Zd9ujzPZjZ8e5jedB63u7hoPu+HianJLd76F7W8fd27NIVLn9yu6Pdfv2baobd8G8TcswJ3cmB1ze5/N/O5nw6Yt1rP/crOzXqb9dwW/Zskgn3xrm89h6g+36gPCHgBAa9botux3+HwD+h8/fEeMoYHql3R8DF+kjXxLqN0O7hSXPHt0KBj6a59f9v+Lpz+9wG4HL0eKPYLlEg9CI9QW4Up78sOR+fcieqP/Hj94J+A83mk2on59xp6Y8USYPrHwd0J9rfaferLaKl8Z4VdSfMXC6Clxq7HfQpX3uRSz9y/U/FvhQZn1D3x9g+UWuXDLlEk29s/PvJOUHmcE5989/87d6Ic95a6C9KevPsz6Tx3Rw9lvEvwXwsKF8lcpfAxkKPp2O5DZXvV3An+Dwl+qiafcPfpBz/KHS//nyvdumhFS5K+xf9PGH9j+MyS8QeJnUHg81V+fMclsXkKdEFS46+NelXbHwz+zYyBteL3rQV99vW696uJvdutK4N48/XumvNHhLwc75c7uyvTtGbyx5G8Ge1P1HRb6W4g+bfdRBXlT0V6s8aRJvlrqQZ1+pBzf7vnHjSPRZjcPfhvVbv12N+pt2uAvO3kYjp82rPePvp77UNU+S9A+LvoP2p6czu/Xn4fUHuFrD6FdI+svgLugPE/R8w/qPqnuZzshs/beQ3hP198jOU/1mfvan0n8WLIj3PEXNPr79O/W+YubmCPuHy+zJ/0+4v1b372z9p/ENsfnP/L6j69u55SvJP/V8j7kCi+Xz4vwMWREk+fO+QvH1bwd+a98+rXEvvc3yHE9xxufa3/H1Bd187OyInEIX1hArey+AY2Lyl8r9jc6fFY1vh347VxxC+w3TPtdyz+c8Awif4H98779fcutnfObhXy+25+S/Q/uPin8eeu8++632vgPwn8d+6hnfPbsq3qCF/p+LPePuPwy5jkjv/fGTgv0H7zBp/LnGecn7h5L8i/c/ULgn1kPZ9o/G/UHroWn7BfK0sf9vrd578K/1/jfL7xP8X8H86eTwzvgD2VYoRC+J/OfmP7e6N+s/IPW3ov4j6X9ClX5dfjF/H9A9h+0k0/lb5d8p+jfqfJHof6v8o/r/aQzvi/wdb2ZC+b/aHuf3B/7+L/GP8L+/5c6ljTPmfC/+P2/4OtwwH/lb6b+9LmeYOEZ/hz5B06/kRDO+UAQAGAB9vnAGP+PPlT4N+AgBAHN+6AaP7tIsASIaO09kPJ7V+Ilr35XeL/vH71QGAV7YUBo/ms4gBPnmAHMGYfomBC+1AdH4oBx/g34MIy/kL5cB6/oQGG+efgwG8BAAVF6r+wgcgECBZAfn5EglAS+bMgo/tQDO+8gfgFPIPAUgGeekgVv7SBqxu/72+OgSjgQszvvoHZaMHpL76BEger7e+2gXgF9KK/hz5LoOnp2xGBNgaVSLuDPvQA2BFgd94cBA/tuCyBo0PoH44Xfk/7eeErv+YBBAvpn56B5gRoGWBv/tIG2muge4GJBBgSIDO+KQaVSKQQvikFeBP/oIHhBKDP4GFBjgdt6aBoAQUEZ0TAcojZB6gWr7eBh3nM59QsgU0ElB3/l77xBZ5i0H4BSgPE5dBs/uwENBUFn1A2Bdvoi7DBUHrgiy+4wVUF2BQrtMFsBZQfQH/mfUMYHwcp3u+YrBEwVOBTBRemH5sgvQbsFCkPsDsGtmjtBMwHBpwQYEBgOwVlJnBFBp84RgWwfwFxB+Qc+YRgLgUWQHBs8I4FtBffloGdBW4AL76wXwYEF9ssvv9riIOenoDxOEIaQzsuzoLL5kQFEJRAY+sgKIG1OSNJCF26aIZy7pimYtmJDBgIUk5HcbBpWJJAJ1pwZNe6ypPrDk0+pTYoiOCgXQTWSoliJAQrWmKJrwhIsSITaHIXqIUiPIaMSchdsstp9wqqA8TJk28j9KRwrqDdLcidImyHSigocRB9MVytuDawiob1bkidIhnjshgoXyHEiGKJNoYiEorMRFkD2LFSsgmoqaFMhRxLKImhaoQbalWeVMaJoGYgJFIYGPgTJY4KnZtGJCKpVDUC+hdAXiF0G7popD8uxYj6BGuZYmSEB0tpgMG4yNIZGKIi9lgyGjQIgJip2gTinSJI4YLPbZMGY6ruo7ymKPBr5hnijug7ySEr+r5gUytmGtAsKmvLDKRsqmDphLypcReSncpWFSy0RM5KdyxYa3JI48kLji1h9UC+qvKpStvJph9YVQjbQShFzIthNcu849hc4bCoBB7SvLLNMvsuzDjqCcolB9q1JDJqdyUIIfJdhVUnFqdy8IE5p3KBYTgg7yI4ShpwqWYb2GRe/YaeGbaFYWCC/qfUDWEdh84VLJbh4ShWG/hhKukTth74VBooquREkpPh6EoSoCAGSp3J3heYQ4TjhrIsk6KqsEShG1hmELZALh/WvBE7yF4ZGqLhUETvK2SJ4sBFhQoERGA1c/Kv+EAQBEQWCRqX4YprnhJIDhEnhbhG+FoRr0A+EqqhNsTa46pNgYr4yFNp2LEik4bcpYQi4NhoCyadJJEYoM4TrLNhXylJF0yqoRGCVsC6vKjBwaiIBEqRkKJcphgXMh+HYgGeFbIj46+ixAMQEqDJGdyi4swqCoHdJrjOYekZJEPEhkbWFyReKPKgfI9ERJHeRQqNhqVyb9lBpVgkuOxquR3kTKg5akUR5AdEbsnLKkWoUYZDmRtYX2G9g54kfIGyL6vbB2qVsIvo8scUBZD4RpUC9wrQ9sJlG1hUTOhGMQqUeeEq8islWBWUntgRFsRcqDKjiaDEZeFVgyMOWHURq6s2B64N4Sgb8s0tGFHzanctSAjqzYOFFIQ8sjNwJkPUUdrrhxmoNG7aCcl5FxRqLgeHLhbkRswxK4shuFtw6siRA/hKkSsFLhykZJFI4JqsFGbRtULZG7R3keEiPRfUPjwCoBEDFEoGPVFng4gQ0VlHX4cyueArYmIOJF1hkkaUDAaT0QnCsyWUfdGagvURuSkRmkXBBFua4VdHeRaMRXIbRJkR9AAIZ4ZZEFo2IBJ5HqhMQ5F1ArIOZFgxnYUeHYw+EXaA/RF8LAw3q1MUBEt0XcChzQx2IMqhJyd0WzEnQy0f5EeQAsSqp8xKkXPRvaXMeQJLyqEV8inK2IDFATRUsdHTVynkWzFiEvMWdGYqp8FDEYxNcgfDLRSUb+pbwX0YiglhW8J1Gr299NbJAomuAkg7y1UTxE2SScrWFTRBat2BTsa4ZAbw6lALbF2qh0atEexvkUbJCxUsibEsRUsR7EgxXstJKBxOsaTHexfaqfAGarscSDuxJkBzFNhawZGoZxA2vVF4avsW04aSnco7Huy8cbpGlRmGrphXaJcWVG+q4cd2oOxcLE7HSR24QRENRrcn7GawWEYxH6xnsVhChOgkagHBm2Ig4gQK6QIQGYWoDFB45eIQfF5eh08aP7oh2Cmb4zxvwaQH/BoxqTISY48YyBCKi8S0AggovmqH/EUYgKpHx3RmGE7xh8c8H1BGvsB4nxbQOv7LxpoafG7xF8XPF3GVeqGFVyZ8RPGkhreuQDt6OBn3rd6wCVoagJ1ooProe1ITZa0hokSYq0xkYL8rUaxdAMRGIoIChAoJwlhbC5YUdKeTByyiASgYJxdKda2a7fBqxkMtMMhLUaFCQWj4JRIOTK0JU9CnaYJ4IEwlEJuCWzYUxoqpbY4JpJJHiYJ8vIrZEJcICYJR0ZyJcqoJLCcqJsJZCRTKFAQFGiASJ3lm7LSJXCYIkxyRwvxqcJAiY9AUxwif7D8JuWIPE2ivPsB6YJ1ARxZ+gsuosjPy2griG0GBITJYOMQdNYnVO0YYAmF6cYYsFhBqhtIZRiQfHCCmB3pv4aBJtqnp4vBUgf8ZqG7QBKisK3RrEne8wSaL5hJ7QBEmy6nhu0C9ekSXfFWB/xi3SaMUAUHxGM73hEbfQMiMUkiinrnQF+JGRiSi6o2SWeJlJ1RkUnhJWoDUmfxw8S4ngijSUmBiIsRm0lNJrKO+5H+gwS4lboVSeMwhJ2hlkn9JKSbUmzu1OqUCBJOSbYmJwkztGDTJqScRirJ2yVMYyocLlskLJXSZ6HcuUkZxB7JJyca4Io2oJslrJiyRu7NA6aLcHHJdUG8YnQBTm8m5JeQdEnHGPJFcnvJ3RtNr1O3yaL55UeAWCnApk4EC5QppyeMncuyJpCkPJeZu5QmCgKT8ntBrwdQrYCGKfiZOoCznCnxh+SaMYji1AHindGlJl8kopxKR0GkptFhSl5mb4nl5NJ1yb4lLJ4psOSMpxrkRDuM9yfsnwp98e6Zqm3KVyZeoRyTSnspTyXWBugrKUCl5m+poSmSpUSZvF6mAtK8k0pYqUODUpAqbSnYp6UgpRTJ1yWKlbwoKcql5JdKVroRmoqd6YbQ0PkSlSpmHsmbIpuqUmZ4q/KWykqp5QVamQoOgUSlJmMlEqm6pjqW27VmNqQDIBkOqZ6kWp+qX6Idm4aSOZboZqcGlepSwVrqjmCaXGnvOHqfKl6pfyemnLmmac2aGuOaZil/B3qc84gIfqZqnemgRpijcpIab9744Gqa6l7mZMFGm5pjaWp5I4RqbmlmByadGm/Jqqc86lURaUK6mUpacfGIELqcal7mlUBKkppMafmlyKUsNWmtpcFhogNpqaXUkkWb9mOmTRHaWWkbxFaQxaUq+6Tgpv+DqdukcpzQBxbQo/Cv6mmW5+JOmPJmHopbJJfaaZaQsW6UunDp6UmRwiK8yZ+kKW6QO+BypR6WMlCpwZkZYfpMyf+nfp6SYulDpJ6dxa6YxSfElwZqGbcjtJg6Wu6wJ5NsmH0hXYpgkQsa+sgj8sgjK1RpW/4i6hqImCfPrgG+4kaCCJ5qJarPizGfolROM4TRlgsEDPk5MJC4tNjlociXZL+KPVFQlIwC+jQTEgI+GwklyjEn4ScZxcDSCZAIIOgKVsSmcKAL6V0BxkqJpGU/o6ZGmSnRuuioUxkaZKiVplvYamYOGyJZyOaEKZw/BZkgx7GUZl4x+mZ/pQUzTKaJuhl8mE5nJQzpYmrG2IsoDrJNQMFnrxkGVIrBhziecmkJwMiFliAniRwYwJ1lgRnti2yg5YW8GLBmHKku6hbxpg94ZmFtqFvMPwlhdoGWEa8HEFWExkLEVzjYwU4RYrhKdWdlmthChO2ElZ3/CeGQRPWCVlHCL4XBFDhTnAVl5hSOJhFOcLWTXKjZM4bUx9QLrK1kDqBquNlzZuEYeouwazBZArhTJKGr8RHodCKpZIkYRliRS2SpGVQ4mllnLZ3KD7zOR62fVmSRumOTLMQFvBEgpq3NBo5rZWEBtnyRb2YtbqsnWa9nyJQzD6ByARMa9nU8y6k5xyeIOSyi1Mn2QFHfZTzLDkeQtNGdmzZ+kbt7bQM2UyShRQEDXHNZF2Y5HA5m2njkqRhLu0odZyURzbtZa8n1ksgYDlbLO8HgTlGTglURDmn8RUVPZQRFvDJkEqYUeZFo8o2dFq05D2QzlXMgKiyBlAZOdsR4M7USgy1ZpUGA5I5znMVmlQjuJyryovsYtlPZaIWNE45Eqk5z3QBaurm45KBk+yLRYlDprjZ7UNWoE5R2udkk5sud2p25e0dvZ3iluatE9RmUZjlUgpak1EzhuvLCDu5D4nrlO5UUUrl5ZqOddFv2/sW7nW58UAlEM53uYtE5hB0frmPghuavZ1RWuZox2wGefTlOchEK1w4gAOTdkTZLIGnlqIxOZHl05QosdkQxIjq7kh5WsE45YSTnEwjsR54OXlhs8ud1EaRTGvnmq5fSBNEW8EAuREwwGQNuHD5CucGAuxredLkJwLuZ7Zc51IDVE62VOYzkoaMMMXlOcpWavAL5/uVVnT5ZOYjkqQtkZXnPRmlBbnk5sSM3lHovWTBHoQlqpjml5b0HnGN5xMZTlGIZ+QnAa59kLxwH5tMerac5HRJvy/Rw6Dmow5t2d5FSIwGm/lIJj+R9mQFwsR/mA5XrN1Fbosqk/n45LdFvlL5guXAWa4EBc/kt0neQzlEFW9EeqwFOhsLkIFRBeXQxKhBfjlRx1BV/kKxV2doAMFsKpVA5asBR7E02juSgYS5H0TZC25EYCAUXwtIcHnhI2uXbBiE8BSwXgM9eYvnH5hcblbDQHBdrHKUZ4TwV+UWhQIVuS9cRfma5ohceEqFfuQzkh8QMR7GqFxEKYnzx3LsJaNC78ZPGmWEaAGEo4s8Xml/p3Fq4VrxWlv6G+Fr6W27rMuqk4VaWPhaEWBFY3sQr8aERXmZ1ZIRTfGi+0RW4Vf+fheqh/xe8XQHfx0GdsQ6B58YQFJZbekPps6kCYQJD6+UqUUD6PeoKn4ZB2elkphXYqPZMJH5sSDhWqKP6qZ0N0W0UGyJ8lvZN8aiHIHganRR3TZy2tmIJ2qwxYwBiyodhtor0gUO3buEoapnTAFUcmMWcQExQrqdgANoMXByOjnLST29UL0UUgXYNDbtF5kcsUVJ3RcEom0hjjvRZ2MiafTHFa9GMVHMcNnsUByXtvCANymxXHRV22HBcUYQL9i+bSEFxX2ALF8HN8X7g0xdXZVaK9D6DTMNtqqCkkp9EDkqaYxdcWlwLRXcVz24SDgxYsTxdDa4l3xccXYlV9iCUK6jjCfYzEzkRcWmgfcsI7HwIxLcXhWOYUcWXF4jmvKtaJjCSUslxkiglYlvJRN6HQW9p1SYEW9gVq3Qp9qPQ9YLRSDov0a8q8zRgtheYlzOQoO04fmjep85ql8uu4Sy+2pXdLbBWpVqDy60EAlmCpJKc+YC0NQObbrBWzsaXZSupZEVqeVpfLrSGL8TlI6gOpeMzghR3CaUpQXTok5dSh/rH7LpGTi6UGlkPnubhljjmaXxhtRZsoIJZAvbJPINxV0UuyHdOcUCl6Zc7JmwfRWiUbRSVmPTwloxTuGPokpVCWX2O4cgQEwcen3QtRToNWVzFU7KsWllGxdFEA2XMoWUXFGUFBhcyBaAtiGOzxQnL+gmZTyXplxIM0VjlWIqiiAqjxVQjmyqKOjDKQ7xb2WqgHqPiVesHZaqCV0KxS/bbxO5djBHW6cuuVFlHgdMW5yS5XCUjFHcrnKzl/dKiVFyFSc0Vpl2MQ7EnlFxScVKRXrMSXslzcm0y0lCJTnIvRNJRSWils4R1E0SH5aSUdh/ZS/RTlU8mGEZSs8syV2yhkPyUvlFIhZCDqwxXSXbyCpRsWpg0pXDGwVwxUTDnyPmTjpmJ3Sdy5gKnUm6X7xGkF6Vwgx8Rc5+lhpXma0VfpbZLHxBkUxW2l78lLo2lPFYnCulruhxW8VDpd6VOloYRJVBlDFYJUtwwZfP6xpvCiJURlQirJWmlEWYMbxltlodmIJwlu1CiZmoSaHCWj4BqH4icolhAYwrqCyGFcNlXqFah1lRQKihgoX9AsiazPOzShSoalY9Z0BRZUCiKodezmVToXZWesIVfyJCh2MXUxGVzIVFWFcjNvZnqi/hFPIxVA9jKHxV4VRrYchLoWiDKl1FUM7CWdyBGEkh3RnUx3MxIW4GeF7ANFn0GMlkQljxgYewCFFhAj4nXp0qQcp2GlANSItJ3yuooq5ovkwasBTBmkGJJ4SOoo9Vg1bTTqKOyMEE3JIomfGzVU1YxUQKk1YknTVi1bCDLVUut1VxI6ybdCrVe1dJXBmFMQdW7Vo1XmanVkziiqVex1RMmMB3ofxV+GdidgCPVjifiF1VsWTIGvVj2lGHN6MYa1WUh6vrpXwJ+lWQJ9aaVnFV8hazEKjy4eInZW1Mj2h1bQ1BtDqSOVplcvCuVnIu5UtWp2tZjcUsoXVbracNTKKAuQVXswKwFlWFUU10YAFXxVtTNHiroioplUU14iOAbJVVocQqQ1toSzXEKtNaFVbgJovlX+Z7FgLRPxJYmVXxJJVVVWLBtVe6Z1ZCik1UgALVd4lA1d8SDVJh9RURlLWtMiBK8iSiDgjz2v2v2JFIBtdxAtFH6mpnKhm2i0VGqVtX5jEO9YHrXAgvUQA6gaamTSAp5ttQhICxPYRbXoSamSSDwRRteqrWZxVI3a61ISgtTTZXtg9ghKICCXJrF7tXPRLyV9lhBEa/YhERm1adalqMSqda/a44VEpFj3II9oQarqE4uFE51TmguIgxadSer/iida/YykiqvuLh1V9ufC4SLwG3Wv2fNLqoLijdd7U5KICLXWv2cdVEqe17YVvbJ1GyeDmD1edWeH+1fdVnV8RiWUTa7ZCYXAma1hMtrWx19GqUArgs9ZHU8y7Rtdmj1e9dYT0FBdfOoXajbm7Ju1eWrfUuwOtbcgsaHHl7UtyVkdRookB9UVYfmO+ETF1MfoEtZqkeWu2l31Z9aA1iCB4XIH0aaDoOrANL9RdoICeuVPV5aXDgvUgNFWk6i7sQVj2ksadLO/XAYJ2idCMRI+CHVPinCTVwylVcvLGUNfteEjbaJDXVEfmzdb5oENk9RGAJaqmtg3tKGpfXWiJx3P7bSGTmkQlFAbah+aF14OookW5EjR1r0yyDSiBjFaVlJoKNfxYyDw6ojZF7sacgWNrSJYjbuowNaDdMndqhjVg0hE54msW6NdDQ9jP1Y9cnL4MLEaY08y06duE6NKOjFA4NYxSCAaNICLtz32ToItrUaTAoQ2SNbMiAikNSEH/Xba4Tcw3bExtRxYaMHDUQ0favjRY091RqnemeNEDRVouN4Sqg05NxjUI2INQTdUDiNSOJxpz0WjZE1xN6qkQnUAKDQw1A6ERJxKUOk0Kup1NLTUo2BNdTfxL526jWuI8WVTZY3uNpTQY1H19jYU1KE5tuZoxaiYkepuN1ahxb/EfeU43jilUEM271etZ5quN1FMZorg/Wrw2jQMzf2IHwfjclYBNI6vs2JN8dn1ByNJzRfVJNk6FQxXNZzffUFNUqMXH5NUEgfAo8AYM/UqZOShla3kTtmvLZ0VmYJosx2TVBItwETUnV61MLcw3jN/YpjqHNRIHs0C0P9SPajQtWsyDyKZJDuiyN2eci2jNhtdsSScLUCfG9NoDnbVm+KzZg1QSGeKRK4NTtSEqlU+PLKAIN6dexE4K9TYo2v2XLbdBhhdyHaoINdjf2KTgvzbg4st0LZrwoNSLTy1UtmzSEoStl9V83ji2xK81Qt44naBMtore7Wpg+Lb3bytBrQvXYt+hTvrGSMjU6BgtrwGGHLNLeYS1zKiFUy3C1CKQy4nWIikZL1QXTp2bxFc1V2mqlwGM/LM5MIR/J0KIbXdXx+IJdlL1wc1SG4et8urG0+l4Cl61xte5h0QXSSbZG0MuyOcG1fIobSm1Zt5pagrZFmLhGCZtBsgAlJAERB3rB6YCeUWhilRTdDgJVIftkJlYNQ5Zz6QBkiHL6HJI/qf6PbWzZb63iCfHzyhKtvo8tY7TdVlgsODgp7CLVpfpTg1+kKiBgdVg/of6xLQ5DZ5b+nJgbto7d/r9txLfKBjiMBluoL6g7Qga3NTFMS0kgoqqe0MZtrTJRTt7+mvqPaLoK6HmivmUPEi1oxngZB0b1YkmlUZBr9XvVIYcGZDVQWSB2JZ/1V4lz0atdO4a1bYtvVHZeBoHZP65GR3KodVKnuI6UvGffqml9mWZkgGE2oZEuZxHSETcZC4rRmzQf7XlH/iQmVEh/tmUWJmVlTHaJnQSM4CPi0dAmSqzD8+Hc5z+QqmYZnkdC8NpnqZQ7RnihqHmeJ0IGknaZkvAumQoWidVmcJ2ydPvIR28d0Bkp3MdRHWp1od0nfmoftIAO6F+ZbrTEmPa8WS/EkGVJuFmgdMWUM4QdlnVW2xh8HXhlttelVrVHZ6zCCDPlv5YVyKsmZRhWkFJsnmXlYWWZbhb2gFQnknRkxRJRpyFvE9CSytZUeUJdvUQCXL040Al0xdPxQDbhduxRK0fFWWSF0ElcvHTJQVTMt53yZKFZHjrMFCGomDlCPELwzwy5QV1QYQvNXIXFWxSHbBFTZYCWZdqKE2Vgli+Q4Qddp9nF2Kc96W8XFlHcrV3NROFf0Vz8jIL52tFbNlzmjdJXQzkd8+XX50i5TZZSXloFvL7zdyoFfoUQFU4JBXVdhXP9hslK3dFW/i/JfBU3Z7SOhU7dCBZb7ClkhPoU3ZtzOpRSlh8iXkhdCpdnmutUGfVVSuolZqVxF4PZJXMV2bexbQ9clWVUI9WlUkWK+fFVpYI9jpcW0qVt6Wj3ZS9FZLUko6PXD2GWePYj1Q9L7GxVKVz/l4ViWZPYpVpFEBHdLcVJPaoYiwCIoXFT+iSez38KxBQb7fmBgCqXgdPPWfGnwEft6Y3VHPR7Fc9T/oL0FVMSSL21A0xuL0AyFKm0JRiCKDL2wecvT+2qGOyFL1AdmSYyDqKhvXyY69Znb+3696imL3EGVvWfGm9svXYUOddvUr0BWxBor2vZWvWubm9oPdy6S9nOmIQq96Uv70a9bvWb1O9MSS72dYgYEb0ggAfaQYGGPvRaXs6b0Sb0J9UxhyQG96fY71C9Lian3292fTcn59gYoH2J9EfaMbwQHPbagx9Gfcb3FipfeH13GXugkC+6KAEkD+AgQD0CVFQ+s30eA7fakCQK6vUr3QQ9Ps0AUqwOBr0j9CeuP0c9fPdP0REs/cqjHO+erL3F6swPMDl64+mTZ1FyHQZVHNHCWXIVdAXc5YMyY7UV0nyBcvmV5dPMhnF8yB3SgYxdKctMWpdSxaho5gi+SgZpd+shbAtlWXRMX6yWst125wPVn7KGywXSfITynssaznFE8mf379wNk7JH9FEmomQDjXbxpsZZamAMisW3S8q+NuXQN0hyP/S/Y9do6u/2C8I3Q3JP9lZU11sZWcjN2lAc3Zf1hd8A7ZqH9q3VKQNyqAygU4DDapPK7dIcu3L393qjSX6yvcuCBndg8nPIVd13Qg6IDq3fd1AOcgzV0KDzCqf3SDVrhvKKSX3W90xd+8qu09ZMg/yo2Q5YORWftlFeX1CW0PfQovxgDSIpc1EGSGW09t6dD0CKYihT1QgZ8fAqo9RPXQpfyGPZT2+DJCtj2hl3Fnj3WDjPSoD2D3g0z2uDkZQpYuDkCtT2hBN6RTUxDiQxEOeDccNpU9miHXSFedEeVBKm1yuevkOKRQ+HnVAAdYUD51V+ZnVcijucflqZ7lU1kNDTqJXXb5nWYHUj1d+UvUAI8EflkHguEtHX5KteVBJDD4gyMPjiERDpGkFF2a3UzhDOSPkLYiEidELDA+aBJtDk+dXXD1PYYd1z5DdX0MC5LdV3XzDQ2QMMOKxw8MPdDaSkHWDZNQzXU7DDQwNgp5LBXhIO1DOQflqZWPOUM75tQ/KCXKO2aZ0b1aWbv3g1BQ8nL71B4W/nPMMkoLzH5wRZK2Y5z2UYmP1B3XCMojAJMrydZUzJK0M5kOaoPf1kI0Dmf1gDRiNwjYDe9ksFwlpxCEjiBcwnUjGOe0MsaqjUMxwj6DUTmsjBzTV0Nh+DQ82f51OcQ0VkmefuC1NV0FQ1ZslMLQ2ijOwzdFNNgo3nm4Fomuw18j1hHhpEJAgJLkAIGdQI1y5qBVQyiNokjgjD5aw5o2X5UhUS3CWvLROgW8BueQmytfLQl2m5MWtI1GFAeYs22GxQ6Xnaj/BbSNEJD0Gk1ZZVuZnJSjNjRMMlNwSd6OejuTQBABjIzeqNcj1YAM2pNDeRGBp5tmsE1r5iw2E1yjI+FzlxsaUAk2wtpw7U3pjyoz8MJNmrTUOZNR+T6NRjYbHCMON3owfk8WBo+KMVDKTZa0ujw2RdoREirbmOyj9rbfkq57Tc01951o6mMKJdo1aMm5PjR2PsaMY4s0aCHoxdkcW4YzSBe5joyU2JoFeSsEjNA4/7ml5+zXckujB49s1NZO49Wr7NcY4VzTja4i83+jKY5c0/Nzw2aNOtT4xmNT5d4w3n1jgdCFo0Fsw2+N/Nv2QhJAtfQ8AXHh+zdISQtLwwi3yjYIze2Fjb+d6FZN84/OIYtNIzHzaSeLboWwwRLTgr6Nho4RUfZZIJS2SFWOYC0xko4w0OMtv4y8Nstekpjlt5grTJS9jUuRnVCt34uoVQSKre1kNDzE6RMHjfE/aNwTYYdc2PZDQxq3+jDQzq00SnE+q2HK2EweMmtROXoWYTnJaaNTs4E9WGjjL4zLDOtNEiD3J9/6eEWptYRdUB5tqgaz1qW4RX61pFMRTZOWT8GV8iJtlbWVXGTRbdVVppalv4UxtLk3EXM5zk960OToQ+kXht+bYT0xFJk1kWZi8tbkUBTc1SrU1tICXW0B0DbRABNt0CTUXudoNZ52IJqzVLJFZBjTU2FZuWaS3JNrcuVkYNxTbtU4MJjfS2TZW2Xk11TJ4SBE3NVU68rz01DWVNdZg4SC2bQo4VNnDDeU3cpxMZSlfVx9v6sRGdTYrbBEdTf6AnaOqKKrmTdxr9v/UORvEYVNxwn9YSqrZU4ACPfte2dv3ttOU2QJDTCKDpFYt0rR5BAdaTWq2mRFSchIINvCC9nyoZtaXWXT5KK9P/NjqvKjRxyDmErkxBkTep/TW09Maaxt02Mbia4M1WDnTcLXtHnTCDQC2Kg5kVK3TTOICXV0O7000jAac9cDE1xi9aOCjZMjUVP22f0UdofmH8IOjpQZM40085hM4tksNPmrmA3RDTeqgFx8UNjMoG9deeAyxVdYXnFRIEFfahNGUUrDVNX2ES1ewVsn03KN1cLVEuqodsc1ZIkOrHW6NWSLDqnTCpUWqwz5+eHXnNaLbHnbEFGkq3eRToG9qnTo0HnELNCRDDDNdk9t40+5pQGYVe20s/9D3TfLRI2xqBiETMKw96pvkTR5DSTMOEeM+U335Ds3S1VT1sxzOYzY+RbNNT7iKbN4NsSCdHm1wc/BrwQSs/7OxIdMlK1WytM/tF4Qgsx7OBIDKl03TROIFnNeNCs2nMizwzfrN0xAs0bPQI4BQ3Pv56M1rMeQHJP5rKz7ueV6fNRzT7EUxNsyXOG5KJI7PuzA0RTG/TK02Fp4xk8xnPv5Os6A4fqeMYPPpNoUT3NkSWre/kwzm83jGtzq85Qisyos73Wrw5c6/Yd1Ac3nPpMU8z7NyI72WPOj5t84iXDwpc98wPKFc/3PigkszXNWzs1dXPNzu8/ZpfToURjDrRH5pHBZqPCchIAkUM06h+zSLZPSzRbc9zFORAxbs2x5wIJ3Pm2OLZWBVDIKoLN3NtMUnPt15LfhBboyM9S3ALl8602ozUiHpLMtNC7DDEqCDRTNxQgRMXNwOeyuIVrwCanq0qRPMSOBBW8C5/TxzUc4rGXKtjaBrkCvkRdPTT0vZHNCz5AofMFzA0bwUiLTzX3GZxddRnWc9KcU3WMzYcSgsYOfU/eHlxRi8fMGLg8sy2IzhcZos7zqi5rM7zKseA04z0dKTjygi8/fluQUCwZOWpzzgm2xFuHv4udYvoHqVBtJffWAFtgGVNi3xyGZ5PD+0bYkVGlPofkXJtgGSkufOGbRkUfxHkzunXmubeEshLnzvkuJLwQxHSltznuW1ZLBRTB3Vt4CSUXJTZRb3oNLVRS23A1WU1vXGKs+mdi129MoDNQYl+ulUI6GIDZwUqOsPnK0052Aga82lAxMuuZFKkvpc8Qy50AcgFKgeDeVwOmqBHoqyznYQEn2mWDsaFKpZB6kfWu+2d49+osjZVCOkojgs2y0OJLapBjyzcQqy+mBCIfWkmALtcKF45/adMeVjj9WkjzICicy9/VqJdWdJAgGZ2IMt9aWcL8vdL5xVCuZwP2YtOQrsyyAYF+ioRDWTLU8hBHNaKKwgYOEzWoCuorzSq5VvL2YBfoF26yyP2PL9+miuGifWtyCmDytWvWAjuQ4mWdtkypo75FifI9AexAiuzVcrQ7a5SidGqmyggGQq3biQKoqwgY2SyiCAOFAvDBfpfEV4nGpFI5ij1i8rcgPyvoFj4DXkqxsqy8qHk3K7XRFsEq4auyJCnbICOygVAMMQMOlPI7BqTtGiGxCtDM5YtAE7JWU6GaEguoCqBtPHKerLy7+qognLONDkMZQK6sCqtjCHbkM7SDqICqXNFBiqClq7OBxr/SsatysCZAKuCJ3grghiqUq/onDkyqxqo6CZgixCLLLQPGu2rmlLyDWyFITwJmCmjByICqlAi/bR0Da24g+rEPp7bR0fK2qICq7q2nLdrrktTh+qIaMJkFrREnGrWr5WHqs5rDq3muZC+9AgNJpbQh/Q6ZSayKvK22MYUk7i7a8utCO26/RJnKUTkRBHoR9Jqu9rQoKmuT0H2d3I3xRq4cKzivshPH3rLdEox9wRa1euvr7UEusUSUeAwn/0IA+jZPs+ifKv02vqkBs4RmQh+KlWmayBsaQYG3OubrSmW+sCokq0hs+LOPcNN4BUarb0vsaS0wKDVA4WfG+NuGymDEbIRIRvEBZak9Vj9/PhAoxQ+3r+koZUhlRt+ycQ8H3tG2G/wKUbWARPLOFqveKDkbhS6UvMbY/URv0bHdKRsOQQm9kM09om1htCbsweO3jGEmwN5BTLG7xttyNG+O0DS+srD0ibcS4wbibem+xuMGfIFAFtySQxYO4Gxm9LLVBiSeZvXVc9DEtYpIQxptcbGkKRsLOdm7JvJDHVbZsp1pG2JASbxEDxvYbERMCEObCYJkNycYWzFvCbxriipwwtwYP0JbAbeB22bgRGi5Rb90JkPd0cW6IpPQuG3DaFbJIAVuuD6dcVu6bkCpVvqbYm6xs/o/GxxvnSFm41vlbkCvpzFb0DKIqdbdWwpuiK+sE1tmb7jHkVhY2WwZu5L7m5kOYQuG/rDqKYWGVt9bryuFtZCQ20IZaJKW2Fj2bE2ykPLbmQ9dCzbu4KIoHbS25luWga27RvSR+22lvtVmHntuiKTCLNvXVnyS5vlphm1NsPbi23mZYqozJ9uvbx6e9v1bmm+SwXbhKluqZDyHO1s2soO1ipkooiuPD/bkWb4vB9Z29Cgw78qpkNo7UO/WDo7FZPNs472O/6C47FDJkNE7hO9tuJb6hKNtagiO44Pyb9264NiJuG+raZDTO6dsNb19rhsT9oiuPnY7ZlFzu3Srg/zvs7wOxpC472kqIp4QfOxTtcmKKtTCZDxELTvKVbm0DvYbRSJFvfbvxAGF9rGuzku7btm0UgJbsu4bbXVhu0rtybgO/1stA+W1FtsoGRTbs7b/m6xtJpuOyLb27X23rtO7mm3OSu7O1R2vm7fm3dsG76qLjt8QGRQQHtbtC6HvZwu8b1uO7Qe87tFkMO9SRPxAqoNuR7BKMnsxk6inY4B71myjuJ7mVMQYoGCS2nsU76WzJYM7Ta7ruy7KBnkVBree7n1+9we2iBZ7BqBkWt7Ge49tjV8EhkXd78e225V7FqlnsDEfex7sV7ze4nuQ7Pe/oi7x0+wPtjeTBlzhq7TtDDv9ElQU6rjbnu3dtAQdEFktG7EvTq0pb6QDdtMblu3gZ90WSzXuH7/5FfuN78vb+0TaitXGsy7Evc2E57NO8tVy0GRWTs97woD/vj7t24PuPa4CuWtb7te5Wg/7W+xPsOdYtfXveWWe8cwZFWO0tt8K8BwjvF7FZMftke21RVUtA8+4lvqpfe+XtAHi+xMum71hDDtEHc+4Adn7k22P1kQ48H3vgHEvdQf4H0B6QfAeJBuyQd7B+6r1sHTa6fuxL9BwoWioz8jC2NgVncrSRDZEIZ2+b+e4wYZ48BydvfbAhw3uDV/oSvtF761RRIZF2hwvtcHmQR/szbOh7wFp7tB8Ie7bEqDLC7x6e6YfYHdhwYfumJBihix7fB8H1qHU7Pfu69uBkC2RDAtIeoeFsu7TRM9ey5IcaHAHOHvX7/B7oex7uuzAcxJGVmBnW7r+zEdmHPuxEeuQFmy7vEGnh0VtLbjyCpvW7LB2kfYHDu9vuD7jyDHtur7h4waeHqtBEeugqe+rtUHsR26vxHnB84f1wR20LupHHh20d+qJB3QdWH2UQrse7wRwMfyr3hxb2qGGKFL0zWrR2YcLHERwXSs7tR0IYCHY1tMe+9DnRigohjO9Ef9HSx+MzbHhk4od4sm25/v2HpO0MeWHHVQpAz0iiuMesHkxx3SnHyO+cfikpOyUdHHlx9OwRHfgfDvrHY/ZscIHERyFiY7hx3UevHGBwUfSSqeyDu5HrxwQcJHv7dHjeIf20ifHHygO8eYbJBtqAQ7Px9CfYnHB8Mf3HgLtduLHlx53tLbymGdWQKKh4QevHjJ6ieqGymATADbfR8SeXH+hxUeL7ymILsaKzx6UfTbFh65tODhccvnTbRJxsevHjh3ydcHymJck9bwJ5QCgncewqfOHymD2A9bUJ7KfHHEe7SeaU1O7VuqHrx6aean4HV8RUmFW8Ke/HeW2KdvbIh7ytdVNWzKcgn5p6Sd3Hd23CbU7DR9ceiK/p04dWnRAq4OxbAZ2GcdHZJz6fKk5G6FsRndmwK6snuBl8TVHPm1icpbc9PIdN7DnUqv4bTtBmdxnuJyrsqF5G2K4JnkWFZs5nMSTZI5mem9pt4GZh/gyDV6WhZtX0hZ6ptJnnRyGdm8Mmx2cTy2Zw/tsnIU3xv9nNssWcSnVhX0lsbY59xtGnMxhJvjws563QtnKEOookbFZxRvzn6YORuxlEx02fWEq5zqu7nuO2qcnnR53Dvf9pm+tuaUHm2iAXneRX8ikb5KRJudAF505tZCz5/mcwB8522efBUW9+cTn9O4JrkbaXgBegXg566Zy14HccWPn/581U1LvABSEpZh0x50gjDllzJ/chQ/lyFhSOL3SlDOF8NHhcPQ9bV9Rb9n4r21EcamBYXkw8yIAR1F3Uq1DE9UYjvhFF77UkRB9m4q1DNw2nK3h+FzkpjD0cnJiMXYdTHXyyxbOxLL1hYYqwiAvEmTVvhPJCJfF1O0Ypfx1tyCnnyyBtnJcugS4U6ASXqmQnUlRznNQClwcw8MM7yoqE60XDeFSNvPNhl4NkVhNF78PTDHYU5dmX4IHtNUVeAsJFHT6F6mHCXFWoxQ8qG0U5fosmrY5eMXwRb03BRfvCmrDMmLZ5GhXq6fA04xfilMyKti+lkz4jSMMFeZXcERTJ8EfNHdGhXZSDtEMXFWpetAzIV5FeVXBqHdHSkzCdqhyzEV2Y2Zwrmi1c8yTqEpIdhDV76MZXnFx9oLUFuXxfZK6iS2O1hmbJTOcJ41yXFAM+Y0NeLZTcW4ROjkMHy3txdUIlpdXpMZg5xMycoeQOLpEUkx7XPpPXOkRiDFI264oOoeHb0nWqeRfzk0dEy2jTVzZziXhKCtfOgV15peyX3DSdfJsmF5Ff7XVmv9dmNr4C3lfXbozKBiS1VwU3dQQN6pfONGCO8o7hWl6pqWUukXpdvXJTaDdHo5Bo9cUyOlHrlnXtAlmNqgi16VBzXEIBxak3EUXhejXCTf1fEXqNylB95rFxdpo3WcYpCRXWwv+quXXN7FwS0rNyU3nYp1wNdM3H13UDDh/F9InPXX5fJDPIlN1BRvznco+DE30t782JRxl1JoREpDTHHiMiY5dcS3yN99dC3wLTyLw3EzbDcug1MW5cZxXYO1fm39zcKC5XMlxeNoZv469e3jumN1e43Jl3uIHwnTcrfnXxEwfAM3ct8RIh3hN7Te4S/tyzei39zetwc3DV5eNMtiVyJenwacT+wdXazWvjtXpFhReVQVDXNFx3SzTreS3dNzZDq3VURTclN5EOWhE3a2ufjvZPt1JoV3Stx7f5yX+M1cu384gXd/zwN98053BwsZEUXKeATF2gI9OC02w1oMVciXE2hs39344nPfDXDt96He33d5+IC0Gd0BXKUAysS1tXdbBZc3XxQHhOm3aUaowUtsh/deC3J8ZXe83rLcXC3gqd/fftc8oMFGTXMkDy233BUUCAf3k4C00z3ISk7gCLskW5cPEStALflXXEyzA+y1tyJc5OKIDnIr3UpNtc+8qtzvLi3Rd6g9USlOAdd4OHXkWHstWcYuzctPae7criy1xa0J3Zd7hLIPFcYzeUPKdz1cUX5pEjdZ3FrZbeyxnN8q2dwdd3nc5KdoJuJZxF3H3XwP1oBhslno8W0CRTHFYvH2TwZwvE5A5k2m1wWq8aFMWT8jzRXblcUwxVjx0j5adehqj3o+YWMqFlJ6PyZ6elvxcj8a6Px2j1FPV6I8b/E+TgUwhfsGRRbW1d6KU00seP5ABlP6P5ySdZBZhBqFksG9wQZvQXPSd9XkGArirVwdKFz5doXnS520YoxECf1aJHxSQZE76Io+zf89+vXCxE+cnjTL6jyHUCEJhTyAY2RODBdoxyOTwoW1ory+ypETuT6QbpEFMrr7WxTTzdB0rOyMvlFW3B7DbHLfbQ9a1PmbrsvWe/3bU/+gry3CiCFwkC4fjWsONmyBjNHcU9DF2bMtlNPXYCDpf1DQEU9vHWT8OhLPTT9VAn9cKI6Ob6qZ6SSU3pzyWVWFIzwVfRCvy37HgGaz8voGx8Kts9KMYqxUlvKHz4c+c9CICbThsNzzrEzq2z2kysdIL/s8US2effqnwAL7ZqDPNy18TJEqAifzrPnPSSBZPZTwPGr1AkV5cHT8T9lN+XxGRGg2hIcirSyJMqj8rWyljOZm0xhxRKu0vlLz0X5wDGhMwsZIRKs+TKZrIfRHsBWmzLp31K7TE/9zzxnF5gr+njGGshCQfDsv+ibzZbPbL65kDz7DKwMpQeHXjFiJWT17cUZxmdN4n9YveJm6vK1CImnZMfO4IQr3KqOrBKIaxyS900tNLKTCUa716gv3/UcwQM39fqqED2rLq8MvGa0y+CJMnkMUADsr7q/BKt69LKbUH9MOiIMw638hXMob3Jy9rbkD/0ToPCdvTtrw5Na+H0Fy4erNapr36vDoib/TIGvHq9G+VPJmtIQdyFMWG/ZwDGozJ0Zgb9K/eqdL9bMqvy8opAQCH9PvWevwby28F+ub1podv+6/2+NrbkBN6nrSJdS9evNnDDA1vcav6/6J+K+8+9vsiaUBZMqGwp0zv3bwgNxvTyFHQatQb9LITsOESnTltlz6fKqs7juhDRMqfJrIhvHeTe+U3Mr0q+RhPz4q/SY4jxKfR0XAXVkXViW3md5tf7+Y/U6HioB/KPwfUquRD1Iv63dnLiaB+hT7UMQZKr7TgrX890Z227fvWUgrWYZQhrWciG2H6knwfoK0h8Jgdg0OCEfsR6Cu9VVhX6B5tGIBR8/vQ4OB+MGNkt8GgrMH+h9jemH+Iea8JH/ZBYfzEwx8CfznHx/4f9OkJ88fMOzZI7ndCkBASfsnx2B8fFTm8ujJdO5bvcfCn7KB8fxVZp8cf3pxh9EfI/VJ9Eh9KwgEaPAWYZ/nSxnzVQ8ftVPJ/raMvoklAooR0KiOf5nwUmWfZzCR9sIPH1599bGn+trZLsu0CgxgCnzvj2fE2mZ//vPYm4mRfQF+p+Wfu7N5+w4Cn0l/+fln2ObefypyctuffjxZ+UfLcEs5OfWQv4fNK8XyIcBfSNMZ/x8Cn117pfBXzqTVfFVX1q0g5XykOVfn5iR+3SLX66ARfa8NV9tAn2rl/AffhpZ8iA1X7aao6bX9KkdfnEMZ+3tPH3N99f7UPN+NJwOlF8jfHhg189KJH3DvA6vX/V8/vIR/N8KKENcN+wf5yZZ8omJH66B2DtNEV/ufFfVd9BfEvbpgGmENdkubf5Aj+90+xnzOB4KclPZ+MHf31LrEK1INN+YeAXxdbGfqLHgoCKQP/Dgkf/oG4mV+EPwZ+UfShzD/BbxCoWII/0rCR9PWeCvj+HfWH9lrGff0Ch9k/CP/CDk/lQXzXnfnHxYlEfEqA9/Rf9ingp+fj31rpQ/m0yR+iyHP598Xf+Xz99tMoO62u0fgQ/aQI/aX5dXCFdg0kdo/XH8z/12RvWeeBDmXyT94KzQqr85C6v6z9ffPP3V+y/JSij82UCP619G90bfVobfQvx58Y/nX1MZuQEv/VoHfXP+zrK/cnkb0e2eCp7+a/gQ+N9G9B4PL9xIiv0z/2/S3478VklP2IKh/7ppgkIg+H18S5fXJq+vyBlMvD/+ftC6saUy4P7EaeKz8spiu/eXwUlBrg3+ydi/UwzUA5/Nv4z9x/GMGBlc1L37Mm64LCoL+1/J1VE4yHKSGL+RY3X+iex/HfzkDB/Sgkb0JQLCgz/6fXH+kAhF+WkX8p/A2KT9yYA//dUvceCit9G9SB4EPr//n+z3B/Ef5dXpAPv23+T/FieS7R/Af1MaH//v/r+2/FfXwRV/CkWL9X/LWhP/in8mxTF+8eCjSBP/9AF/9F/Bv5jhIhosxwjrX112M/JdfCAC3fmYYpYKb8Lfpf9f/oEM4AVADqdI258PlUcf/tn90AaklFkA39CjhgCtfjf92/i4lh0HUAtfsn9Qks/9HkK/8nTu19o3tH8ZfjclKAV6xl/uckDnkAC0ZPdBZdPSp2AcjYPCgADBRngopfvADMAaL9sAc0ou/uF9hAQL8WAQFlhbKb9OfowCEAXzVCASf84/gug2PhKhyAdoYmATT8xAd/8ifnP8KAUoCqfjv9+XIN8gOvgDAhsT9kAc9VBxHD8m/mYYmAbj9TAeCA0AYj8pAYENSQGIDIvHD8tAY4DjAVWsxAUZUAfoYDtAQEDc/qYCvjoENAfh4CwfjX9VASdUxSMJ8QXLEDaaMf83/up8zsAdUIaqz8U/s/97vjICCkmdg7DGd9LAaUCxAXT9HKGUCqgRUCHAut9qgW/ZCgRX1unoN8MWg0D9/sX9mgZrxs/q9p2gSoCMgRV8atIt8/AdTp8gf19UkoT4ePt/8uAUoCuPE0DufrNUUPkjUGgUgDOgQsDjTjx8jfooCegWb9/Pu0ZSvtr9UgWV8JgR2BhPhr8D/rMCVfnsC+2DsCGAXkDLgQwCDfo+QxPqICjgUICbAcTpvmDsDJARcDvgekCaATN8AYC+d1tLkCjAd8D+gQCDIfgDAERA58GgboDrgb9BbPqED/ATsC7PgiCXgdYDtgbZ94gQMD2vgDAjtm8tmPn4YxgVp8EQb60ncDMCdgXE4EQQthNPpSDJPicCOTlCscPsToxgU94EQYCcoVkH1RgZcDFAgiC6fkx96QaFM6ICcCLwnR9eqpgls/jdJRQWR9FAEKDiPtcD9YG4kHiCyDaYlKC5MKKCazKh95QQ8Q0PgkCXEjHIOjKFNzfLEDoPpqCUPmtVfgYB9VPsrsv3lokq/tSISPp2xAPqKC1vscVVQbys6ge6DXQeqCr/MV9ARHm0/QR8C3oCM4rHsF8NmLKD1HmsD2dJupzJk6D1QZBcZjjGCexOZNqPqmdUwXZ1PqgFk4smo9nOjgBkLplNULkS9EniiImioC82BvcVDWPq8V5PcVDimbAmBqgsjuE29BBjIsJSmlpjJNCVFiqLZp1GQMvGrMVCBirII6uMU95JrJhlmo0HmLgMDZB8UE7HKVG5FANU7B0VYBlcUbyI7Jh5FcVkSu7IzZH01XirHJ/ZFHYISiAM8BiHZgSr6pI5ECV/im/0jytXY8Su2DRZJWUYSrQNb+iWU+7BuDB3oXJGwRiVl5JPIEGl8VDwUdxCSgeDJwXwMm6qeDDlM+DqSuG84Oqd0GSgOQaoEuD+rKyVZBmuCOSnyVFBshDEbJyVsEhWDmWkKVfNFoMWrOKV/+ke99BsnMeikhCYyMD0KKuvUzjrU59SuEMjSok4ohn1tf6M9s3BoEt7Sq4NNFD6VPSoEMEkoi46IX4NmIeGV6IYi5hIYJDgwR6VtdoIoGIaxCqzkOdnnOGVYhqG0eIZxC/oP05WVh20URFh1sEmglgcCssgIAIBZ5MYkh2kOAQrNIlXivfpYLhwk6EjugL9p5YkGs5xKEpvpAnNZCZEggYXIYC8mmBJ0feFIljIe5ChWCIlgQPrAtOtZ05aDokvIf5DDip5DUIcokFCpchWXhFCeVjZIwofTJEoU09yqEYkZVCYkqIYCMaIR6c3Eg4lAOqQZ7EtrBMwc4dYLiVCbEtB1XHoDVUnD0AAAELXAWUy99TwBh6dvrVFGyBd9ZpY99cPQJAfvrPJAWjy8AMQ2CNwjiGAaFdYVRga9HSgaMMaGahZtBTQsr5zbBPR5UdkCAZchhPsNz7jQ277y7JXoJ1ZgTyecaHNCW6wLQrI7KIJRSHQpMBvYXaF47HRDLQibRrLM6DXQtiLndO6GouCNgBhdaGhUWaEQpShgZAE6GYIGMTjQg8BfsPAI6GLoRqQ1PQQpaFBgsBaH9KRASvQ4qBZMBaHjfBqAIw7cCdgJ+LfvDIDrgV6FB1VgKp0WyBA4XGG3WLEDXQxmAiCXGGReXX7ffNZaAwpShKQO5CYwxAiq0M0pbQpcB77a6GMw0VSvQpyg0/ZGFyAE4qvQkQCYsJmFrwZoTnQpShsoAoCww0UoIucaEdvNuicwtcjsgg3S+pJfSTOfwy2QAsCvQg+oBgXnoxQQVChPeWFpWUA6SCdhij9SWFPIadgnQ6+gWwpqz/WfWH42UFg6w3UAJFHQxqgGvqqwsQQUQUGExQUXLfQgWiXMakCww2FjRgHmFt2dUxCCAMBpgHmGroDk5Rwp8ABwlKA3QX2FQ2F2DEwyZYywnQQSwvKi+8NqzIw1vbbgXGFtQN2E7cY0RywpSijsayqwwr4rauSGFz3W/Siw5DgUQBGEyZGIqsCO4pJw6PhvgE6GDhXFz1wzaBKwR2FKwfTaHQofgEnJ6HsJB76HQ3pjVHSQQ7uQ6FpgUuFyxKaK4w+Xhzw5BB9MYIJbQgF6PHDuGwQCuErQyCYvnDuGbIA6H0wzNg7VUNYI7c+GWVbsAFgG2GZQLRTjQqAxOUG2HA4Q+Gb3IWRNw7+jUgr2E+dahC9w/Qi3wyFDR8Gsz7w6EIDw/7g66J6FKCGXrywq2EeDWBHmwnWEmwtaGpNXBKoIiYA6nVgRD2Mz7yw/5gnwz6FuBeWFbFDWExNSiLdAIIB46FqGt9dqHAJTqFB6bx5m6MPTxAPvpR6Z5LlZK5C3BBFCgsRdjLQ/MDBwDWErPd6ACIklBpgUPopQNHbPwzkT5qbsCYwx5CCcW6oG6WJwvINaGxtYxjLQhsLNsSRHCw9MJaIlJCVvKAKa9KqzxnA3RrybjjGIvY4/dAxGckToi6IpfRBg55KpgTpCgwuY4h8B1zOIrUCDbNxEVJd1iHwxlD/fV3pUIJCR0woMQXcSRHvtP0YGI7lT1OBFDxUZQBhIm6L+sXxFXMKQQxIzhA6fV7J+oF1AGI7ejKBeJG0gN3yQwuiQ12VJH9sWeJeIvpj/7YJHxUHgQGIzKgSI2pE6IbWElIhWArgA0z/Ze+GtI8xFbgLbqSI4VpfFAxFFqHQIk6DEBduNpHhMfOHBI5H6AqYZFh4GZEPgJc5tIj7LDQzXo+CIoAGI8V7xKYJHkyDXZeI3PAxrSRFwgKxhJI04FIoY5GhIs5FKyInaSI9UC8gAxGXgCZh3IuiCo8ZaHswMKRrQ5TA9cEhHXKbaC8BKSLYIyODvIxkCsSUGEB4QuSeIwCgDETxhK9dk6a4KFGjKOQD6cDXp+UP1gyIvpQfwZU74odeBPkEFGJI/lioo4hgWwTpLPJO5z7CIlEJ/MOGQwvGh4MYxFKnNEIYo32DA4UZHKYZ5DQgEFHX4PmFwonERhSEFFFJD6HanRwjbw68hGMPpKAopQCdgDFEQrMRqso2EDDQZaEdzTvCYwpU4/qRVF2oUhyCo4y4YMdVFgsGz48o3hA6gb6EokWxiNVQFHgMIpyQwhdAi8UmGg5XmCWog3RwoUnS2o25IomBEDqo3JjL7SlFR4RFG7YakAAogU7iZdVE12JWiUoyjjqoraBohIlHdVUhwRo7BFwuQFHYwlcDqorlE+hQFGiqcnzPJPgjYwrVEy3FNEEBK+HonIP7GoyaDPkXxEosXhjqoiYBygcVGpkdpCIfK1HYwMyjqIrbL9KKtGDMCX44onVYLw+9AWKAgKUojGEQwg3SkvMRJyo7aDWA55IRoQcJfIumIb+YdGJwXNEsQIdGTo4xoJLW5K1scEDLQ8pyAQFtFMENTbzo/2RwwD6Honf+h2wk6yvMeOG3JNJi/Iz+xKyGIauoyt4iot9AasM1F+Ud1hPok6wJ/YOEGoqFg5wk6z+EX7Y4ojt6ko+9C9MLDDRo96A0gLdH8uLoRaoqVDSYaDHCtLJGqRRxj7oldEEiAsD0ojQYqw9DFqgM6pSRTvBqgaDFGgPQhEoubaW+YjE0IANFZCeqDc6edG7hOs5SRTagTeKtHBYGSA8oq2FZwFNGEoWfqTKQ8BxooHBao2S6YoCNEWwV9FQNAuiiY8DhYYriBs7R1E+8TZgaw3OJAMQ+FSweXiAYqSLIEBZDBorJidozTEH1HAKNogRhhQOAJy/dOGNo66CsoevovsOqCmeCzFv0HBG2odRBlgD1H7MSOHV9X6DUo+TGuScsBuFWQgIYMJEHPacDx9FtChsX1GWgbqAhYjmBl+CzEbscWDhLD9gXKD1GqMDfa2oIOQOorNGaqTBgJY6GH4IoUjxucBjWYxjTUAcLHASSNjhLR8RJgYNFE7eLGdYc7p2QD1G3gVAThLR/SYgHTGE4HZxbCZQR2wtTH70ePrggUSSqYqdibMcWpX0ZYi3w1dImDW4K2oR7hQsCNGUJTrF4sAbHBo5BK2ombG6gNyQ6YjKSjY1oAvIIbGiqEzHWYniTwI/LHL0G062oDMB0wYNFzEMFxZnU4QeornDYwo7F4QPiGZYzNjJbYsQRECOElokXg6IT7F9sLWE9Y7yzygbFH6oHHD9w+TFo7a6D/YyDgC8dVHcrZI5NoJYQlo8aw7+bPBQoJXzyY6mBG6dHEiYFHEYwy5z5YTXC6o2hi8bJtC/KEtE+CTZAw44ZbKIzLGPYo0Aw44LCbomlFfYbxpwBZVCOERpys4oNDnYznFkkQ+GzVRJji1HmICKb6HigD1DTYrei3kJlF5zb4Ke4M3i0BA3Q2+FKr/Y4+hhCEFFbQaTaBiQIgNWW+G++E5Gc6QIgq3IsAgol/T4YHXGTQfhGs45Ihwsf7Eg4soBPomOSnWR6HHYWgTFI5XFtMdIT24phAsuVnG4iI9H243Jiu/MlGWgWxhG4r7AY0J3GBsD7HhLaUKGYz3GEoRzHObTgT4o79jYuSLACKO2EDdG8g7Yu5BVWcXF+IxJjWYkaYugEFFv0cb5FYjECtFEFGuAnMBFYmPg9I0PHow1orWY8RByY0PHiMFcCLY1vYfkR5GVJduFpYsByHuFZG3kFZx/ISLzXIkND6BP5AZsAJF9sByCXONyBVZDFEzEW0CLYt15oYxCguoRHHJvWcSr4rcCNVavon0BtG9I7MBkbcJbrsWCANIoP5vxaPqBgcM5n4p6xFHJzG+gGvFtIoP4mDazG8gF5YGIuW4jCcJb10Z7p5IniR+Yr7CioJ9E9pOxRKYlJA52efHlgV0DtwqSK4QDMAGIrmh/YnlH/cRzBoEwbCJBC5IhsFnG9I/Zjp+C5I3UKDFtI5AjBQolHVCIuEUEzvDJMHlFo7Wgm9IpJhuSagkCsb6GxOPUA2HfFDXorfGG6Tti6bKSKbPMvGQw/MAyIDMBEo2FgqHThFVSQohSEqkB5YpkSa4B8BEogtC2YsRFJkOJHdKYshiIzliypfFBvgczhiI5cCNgNQlrLZZEqIkwgXCLVGYsbvEmE7/gb7KSJmsHDGciRsCdAFVF1uETBiIoaIwE/+ibMHwl3JDE74oIxGiEqwl23GTJEo1dpueMQnAYZQRYYg2wIhWImAqV2HRoyjCcE1ujSQXnqF/VJhhI5nItoIsCUo4Xh5EqRHvrHlFJgZfI+En2SFEnlFohY4KxEkUBIMWokCAFIFWElHilKKIl22DInN8bImMLE7FMiLdRAMMjFESEomriIYk8ok9bUjPQlSotQnp1O2HAFWZAzordTaAMRE6YJYkjgeYkkoMTE8Ig+BtCdipeI/KCMYp8bnwAxFJMBwL4oPQht+CgnFQApzCE7rA/nFgmOYApFe3F2A5wpKKckagmugeRFoEnE5Gg/FAUIVdBnImbjgQGdG90aQiQEnooQ+IlHSYb/gxI6OFXQ/FC1sZmR5IvmTdbfFCUJNrFtI6Zj5/QAluEcs69I4sDi2azHLNThANIzugPw8JaXMN85tIwbaM8SkmbMDLGIUY9jx9Jxw/KYZHxUAwnV9HgSYk3pEfZKFjWYwbZgXXkmRwGIa2ofWDr0A/GkgKmACkl5ZK4g5HEwGwJjvEQDuotpHdgL1HhLIiANY1Ulo7HvGO4U3GqkwdCckl4C1oQgmd4nRDVHNLHPdRkkzKcyr7HNLF9wV7GAUY9rCBcJrLEMJHIRFmhRLPAbOAxPGeKWkHBLNCQ9BVnGIMHwTWYudCYI/3GReXoydYW/QyZEFG7sfuzWYgXDAMeMkplNEkXY+6AgY/5QDDPeEXY5Uk8k0PFEQacBuFBfpjqHOFl2ZMg2BBfr/0QXE9idGAR4vsD+QTXH5QGT5u4vpiZoxMjSQDnFJ6Wlys4yCZRge3Ed8ByCa4x9Da4t3FiNfom6+T0lq4vWEPE0PFVcD3zHYd7pykntS1aXQwK4+iTuk5Sg6oKXGkGKxhA4x7G5kpfqVscLE8SF/HYYcLEs0Ehgw49EAQQEnH02XckykbcCBYjpJJ0f7EFYunHsUBHa7kusrc4rHGYgXIjvkpdp2YrHHPdFZyRGN/HhYihB3hd8kv6VmHsUfHh/Qy3EykTVbqo/iDqgGHEYQIUmZYyCLNYpcmRKH7FYhS3HUIUeFCkc7rqjZ7F32dVFjmUJhx44GGdgD1EjleOEXYyYTwUlHDLgFFFx420BhEzLGakiNhJkoHDJoozFaNUzEeBSDgTY6SJjKL0lv2RJH7Yx7i74okAFoCSkXovpLRLZFEvkjwlp5EvGPMZx5vYuyDBE31DPk8LFrLE0wVYwIQcoizGroYaG+oC5T9ExmwrfePpsRUkkWYjqBIUzrAFoOdGZY7WQpfdyliJL7ZeUtyQnwtLEx2aQjUUg2iAndylHo9R5vYtxRH4sd4gknOEceEaYCk1rAgUt7EhMRUnwcZ8lPoz5ZwwOKk+8YLDSoisj/cMAk+wCZweorWTgI21BAEtKlCkElHWgYkmAQfgkXLELBYY4fij0YNGzWLVGKYkUGNozJiLsaEmaMCNFAMMckXJNnjhYsEDAwolEjlCcoRo4CRCY8Wx/wzLGUMJdrUE5IjjbTLHJcEXhSEw8DYUoUjIoi/xMY2xiRgaDE50BNFAoVFjUk+dHOyDvhRE5RBskyGFoOBVS9E5pgIYaDEJQVanlE4UC9U+dEPMaWHlEosBN4iOyBgO25pExwmIYlcA8sSlFE7IMkHouYjgIwFGOMP3EHov0ZFHdNETwb6EnWWQTiouk6JNRDE0gH5gGonOxLU0DHgQBEA8I6PDjI0kG/Ui9FYY7VBLeR6lLdP5iUoqxibIxmmT4M2A/okcC3wiNA+UNZFKnEXgYo+lQvIHYmdwZFFVotqBOEpP7TgIHHgIGdFqnbglVojbR00pLhKEkWCIMHo6Aoi5T0fRtHOwU4Ja049ovk+uiz7HFG2QbjgpoxiIIk11FVZFNGIUumkTwE0HyY5thXwgPAFaIqlMgTWnanJhCJUwOj/EbQmc+VwFxo28AQY66ASUkqwME/FA6YakAvkpcAdI6NH8SD1FddTwkg40dhhUhKCi08SjLNeHHJcK2kCnOThA4hAlSoAdG0gOcnsUNKwGExGmzEXVFcomAnygf2QSU2DBhQUWlVcETE84y8DXxVSI6XLzGh48iCOEaNGzIJQmcbaPj90sdRbkhHAC4aNGpIT2Gh4rQkQYsRhm4uQBHCCDEGocsmbTbQAwEqIj1IyMmkMBImVsbukdKaOEZ0zkgrE1nFJsWmwGololZk95yPuZOlPIOOi14gdR4HQFH30NimG2JNi9E7sCyABPHN4lWgLOVSLIufglOgK2SmoconrwbPGY+N/yJorI5vE8Anc7IDHfsb6FfheyDRk9NHFQSUnHcNEm3JXzEQkwsRdCO5HbQDslNEIcTNMS5GQcCEnjYoQkKQZtYH48Jj7HM6YusIGlLEK0DBbV7JW5G9GM0eqB7ws6Y/4mBmSOBRFEgI8INIpUGkA4JF/cStgNI30DowgZEpvA/EWwVJgDIz+nRUxIiPuVJCRI8bj8E9mhv0FtEx2CUh5I6ci8bEnTR04clYkqJjUwknRpARFGjZAMD4w6qg5gA/ECAA6kDI2HH2M6qK89aqjmUO2E9pC4RuMqhDjkW+FA5ETA+Mu25WMNAlwU49E6kP8leIqAyzEAZG3wD+JRMxjSjU2NonrTxneIy2wzIja4j4ogmHgHyma9f1grkpojKNdcncM91anE/TgaY/WjrgCEl0QCZz8M3uhhrMRFY8KPya9H/psU6sJygM1H1wVr76kqwki4TWn1wPEDME2Ql8rWfqPIEcrI04ZmAiHBHxIwcJGwzkRz4TVaRI6OGp+WIkaiIwi1Inlgk0pkQTgP4k06MqhPonVr7MKxH9aeyC6U+ZlIoNZHVUZ2CaEnTCFo4DC+gfymciSiSHU+uC5MfokZtFLEDIn2QFkzkRfFTNgDI7/jvMhWDTsIJnbgDGjTE/WDfo/7ITOMxGyEoWRHMAZF/cWFmcib4T5wURnV4qqFWEhMmYMjFDmKQhkyoWDjHI+0iuEpkTnwc7p3I/HiRMtwkkgEGl3Ijpl7U0lmoMZOk7ojFH5E1xRFE3IliIpNjUZC+mcs5ImYsCeE4o5gTIspkRBSTBnl/Tuhcs5LaXouk7U8QIks0O5GGoRhmTgLC74MmkAlE/sAl045EMM0YmkOK2kBWN/EHMigRakUZHdHcfIZEzKjd2JxmIEo1l2KFnaiM6UiPM5QmvInFlUIVHgZE05ljmSJGMwEPHzM1mzHMhewQ44ZkPMlhnxI++HbbYZmNE45nFQBnTdAGhG9Qn3RtQ/3QMI8YBdQ5hE9QthGR6PwCpAdyiaKFaga9W1SSojFEhfV5HGIljj97Z5IyrRhiYwujggw5aHJQrmigwqdGawMJHpaD4lK9bBjqIetmkfdFEa9Xf7mUbtmFia8kdspXLv42EzFweeh9s6lzGM8dnCMcVHkuOa7dss+TFMkWCKY2+ECnaVgfQ5tIbY4tnEMUGkjswETb2JdnBKEQBTs9rihUyGGF/ciBrQpNyUIy9kNJAolTsyt7jIpdlnWIQnJXSTGQw6PA6MXnqLIJYTgQZaHfs36CYwnAH92QDmAuVwGgwxZC4Qcgnjs8Wz02Kdk9cKVFLsv7i7Mwt6k4JdnTYP+m7YIEQ5wgU5uKX9noCdkiIo75HWYKDkx0MeCHwuk6MMLdmbQPNngcqtBuwjjw9cBjmlJG9mcvR7isc7QA8EieZgtVjmKEKdnKk7jxfslBjw0wTnjIywnPJL+Q/3WjluOC9kG6VMj/WcjmhEQnAMc1iT4wwnwPMBjmhY02E3MN8DfQ8ahQoYxGvzfgnR4fj7FMlXHvIbTl5U4znqdWhgMcrWRW0mOSMRHikE0M2lriJXoF+M+DHsu5Ia9JEr4XJdkbI2znAwypKDs5Wz3o4ogg0wdkfwc4mzvLC6ts7WAyZYLkcQeVKVs0j7dYUZEzlaOm7svKnp1PzmZEwlCDsotmZc6F4igQdnw8UGGQxIcmDsl/QUk+8DMye873s8+glc+6lWgbtkDqVHj5cu5IzwdrkuoRjHUkARg5c0KgR0+CA4NJ9HpaZZYa9FYJSo76HSfShigwkvYPMvDkoQBDCjIxblhKdrnEgb/ZK9EaJUs9ygxrIShTc9VBziXdmwQaHE7cosg+dddmUqJzBrc1FzWQa7msoB5lHcscx90btn2kF1FktNHZ2wreDikp+lSkNOIkcmUDlCC7lzMdenvcnfDIYvBpbUd7kZQWFFNIYRkJc6PjmUI7neNAQTvczm5AuHEBISJCTdsphDV4o7mURORl4858gV0hsKEw5bkUMR7ga9fMDgkiYB48rah6YiyCuCKjneqDDgfQwRE6YXdmLE02HlZK1jLcr4kCANaHSTDpHdswFR5UmnmkGLHhi8nHA8c4ArU8IHmbIcbiS8l/Qbc+9mOMZyxK9foilKFZnjsxuFuwjNrvQZblVQCSiS8xIQlPbtmdUXBKS8yWCcQEVExccWy+Ym3kD2GREO8rblrIhmK3kiWEO8mhCtkscAFAFUmFSRMSJkrXm3mLTmp6YwSzIfGHj3Zyiu88dbA5SXmroArQJ6GLiyAJyigw/MCdAS8Ap8sQiocwnkkwnPlBwf5mh81WhVqQvkXhMNY28jpnxMgbiDMK+EdEP1An0oPnwcTXAc8rKnq2QvmuWYXmwwSCmd80WRzwlRwJQcvmICCKlVgT2RlUcvlW5PClVgORka4iPkvAQ8BM8hoB23OPn3MwmlrRCCB0wtbjzwNdHRRG8jnw87gq3UL5jGF1gzstqSZ4nMAawkx4rUWaExcBVSyCSXmUCZfaF89GHyM0PnLLR5iF8wdTJHGfmxEE6nz8+E65M/oivgfEnn8jSBC87vkYQHegp80snHcG3l9gVwmY8ZgCJoM3ljwOjFtSL7GGQtvlsINtER8qYZqIlXmmo2/lZbAWGYw/ohQoamkYC08LBJFXmKQT2QwCvthkRWgVKMLfk90acijI/oig2A/lu8F/Qq8jKRKMvLhp8kFBm8t/H/8wqRL9WCDGIgR4jhCuGCCyany4scBfEqILiCjZjLuOcBKC+3lPCQ1g8InJwxrYgWsgA+rd8lF5ZBPAW56J8gq8otiyCwIhqkuXlFkWtDcCq3FAiM3m+8SgWNceujB00Pl+gCdhWCmMiDoNvl4MUKgMCv2mZMRPkggShhBC4fh/06sKWrKFGlcVdAgMrJCiC/QU+wNIChCrWGxCnuiDkeAWxVBgXjkOJE6tNICaC5p7FgG3kaChgXIMoxg28ubb7IxrjAcgFHj3DFg+CvYTIJRPlisBwXtMUaniEzDFFC0nmj85nlm0hgVdCVwGE858nagQYUqkD6FA9e9LlC/2RuUtGYWwaYWmC7jgSEwnnWxVgXqoISg8IteSXKC2GCCmhgiM7mYeWSTm08TuiTCt7wnYwQXgMTWlryQsSucwQX92djHczGsB7UwQW8MKFlozRNBZMqgXgkjiCE8hYUMKUwXik9Yp/C/1Gu8sbZ2KO7mx0SLyDCisA2nbHnjIs/mNcLoSXgVHmURF4Vb0TODGI6i6hY9YXJbZoSo8/2Q+Cg2zAii7luOHBi5C99r+k+YVEwdYW54GYmkilmi1U0rjvtWwUCw+zmmC0dZwiiyDGEjkWDMTpG5RVJDe8r7HiIDPnYwaqBCivdmZoRPk4i3YVQURmA6CzQrYIoIVJcHjmclW0ARCskgLcsXYG2XIVB/IQkGtYzHKi56mo8grRb4zHj0SJg6ki7mlBCvsDio2JyUJL/lOUFSFjgM6zHC4oTp4m3nV4xEWKCGIRkC0j6aQXYWVncV7YC+6kgYu/mGdUUUZoWxiF86CkiYZgXX4gAX3pEy6P8kCBCU5vngcaOmP8z2TLo37hiwR/k0Ca4KJi7uya0sYidEW/lVpeHiP87FSyCyxjDLP0UAU6tGF8j2HYo3/mOYA/kvAbmj5CylQ3kWIVeLDwkcCnsS+8QMUEoC/h8CtOL28tyCNgJBHNgDCAYscvkigLHkN82YXl8jNj60hmJPIGsU987UCiitoRbFcvkkU/Pk/IzvmMwSOFozA+hggTvk/3H/lA9dyqF8iFicsEYWXIb3kFrDCBbCrHKqcgAXe5AxmZ85gCfkt3nYwv0XR0w0iF8oSjOUUIW5WWbnSSUxlVCrYrXczkjKhUoUew7tn+sb8U9s5QTds9hK0CVAVx9BLlQGBIVNRWxjTwgmhmgBP5m8jviB8tLlyeOTyoCrrpi80xEDiy3zR8OiW3kfwW36MYX3s8DjoiTwWJgC6ppckuA52RPn1vXdn8sWIiCS9RCQS3hgDkkvniwiSWYigcXwcoNkE0Fnh34tGZhCmoXuUWFjMyQnn/oH6lpc3cQ1EiKCskn7kx0caxHc4VpRgd7kniUHkTgVLkE0ZxxsEi7ko8SfDvcng4XcxRzbi7tkOQDwmYwvBzdwzyUO0nyWeOKzn3s1Xktik3K3k8blF8/UV4bYPz3s8EkMSC7nxox0n7cyRl1c+CDBQ1wX7c0xG2c0WTaodrl9ZNaGKsJxyRS95DYWTznjMfiCQSg1AIYfLlGot5FxS3phwihwjAoLMmCadki89clHKCtLmuCP0b5csBGgCgmgaiOYh9SwEQ5i22h1dKkXO4u2xLsqVAuo5znjcBjkfiNNEG4oZEic0hz/UzUDSkSpGO0R7HCtfLnKCVPFrSkEChY/aWJCK6lScwFybPHhE7INqzCbNLlgGJ+mjafgWvslonKchrms82SiuSKdnmwjaluctPkawlEgmDIiW20HygJolEjfCXSVucoskaY6ZbNMJdmgygGW3IRISvs/GBTs6LR9ue6VPQOEWBvD8RLs7jlmo4WyihFDnqqEdmtYaXn3s1VjHwKdn9gIERLsmhChsuFD+oi8VNcpkAgcmDgX/WdnAwzBlS2ENgJc9GCK1at7asZmWcyy5iEc4GFdIGrlbqcjkAUmLGzs3cWIc/YRzM22hpxMxkXLZYgfSuso8Is5D/cZbkqyDul/s1OSYcmrGIcj6DAytlFFI9jnsvZblUk4/kf/Z8mMMr4hsRMGXnSbGFA8kp5bFJ9noJEqVJgDmF4xP3gIgD6UU/OYVquORmRSz+n+yM9l/MMQVpcy5hcM5tLFQEqWMwohHNpGpmRSvKk4nM9mx8hLmnM7vGRy2zFpyn/ooMkUTrsVnnmVNiRTsgsA4MHLkXCPTEjovYT+So9EawqdFYoTyWEQXnpTow1CncumD4Y3mk3897lawuYURoDFjRyuyX+MRjEn7XuKw8msBrQk/ZWgSCVtKQmEFs6SRL6YyVjmJulLypQB80IHm+8CCgVy7uxpivSVVWTWkiwDCCs81r6yrCuXvIJRnuUKSA4MPeW2wDCUmCfDHYMH4GUS/uyWivGKLUs5nuUJLgkgQjlK0WVaW8yFnkcoQXGS6SDZ8EdnHwGtEgS5IjvCtTGcsCcXOcNCTGIxtwGURBWG02rE8Jf9B3C4cglYq2mLIStg5i4wSb0tmU0/Bpmfi6djscvrmYssAWa4NHEf/KQSNc5vkxrWfpnIdenti+Dg5kxDlKIU0kDcVyCa8vGLHcfKBNiy1aCsyBYQsRBWEoReUjsuxTdYQvkx2bIgyKtkCIKlTIXKJ9krELfk7caTBay86SfImMXi8J9nLEYTnN86qCQTJ9lKAJcAv80LFFyilBMi3v79KUZGFvZtgv8zEDHnEdlJ03sXTJUflToOOjPis8rxuamVFIzcXms4OVfINJiaKusJKg6mX5sJhVgC87CrHEdnA4Orpf8iFg/8hdDZgUumKCcH7OyoXlxsF/mmCZ+Wt0M0WRYMJTYc/rSsSRBVaSSVFoyheBdSxLiQ5djkWoX6V387FQoKteDyimMWdrRGX2eDiAxipyh6Y61F+8fRWcCcjlSQVwXncF1h4EkeZpgTxWb8BqkjsvyzNKrYTYecjm90LtkACuTyeyb6W90enmJiooAmREdkYswvmPMIOr5c+F6wcsAVtwxwqagI8JsoY5XuSwqWsgX/FFikuklcvZj/cY5XBCzGHYcEcCeKunKTShWDDy45XvtWfqqgc3B/KpTl9S1r668y5Xb0FSV4qQ1B/KyiKxcgbqQ08sVxIdUAfQ4uTWVYcXUjQ4r5cjvjD8F/mUyglVK84lVVQa6XZRBAnEqplCPKnUCkkXFV0wS87WzJRIxizCBOE0aCZMcZUeKR3G2c4JJ3S37hPkfCUBNWu7AqtU6PKvCCyyy5V2MHKV4QU/FgCu/gaw4YJqk2ZV8ON8UVgAVW3cOmhrQwgxZ0gAXs42frSGNHYcKigV0MjSIGoVfldwOJGM0UWUv8+HhnC4NYMUgAWwYFSVVyW+ARKvKypCi7lvgHOxf85cBQ8sPHiIR0X8fHVUUcuahf88UVKqtnll851VKCDTG5FTfh+KjiA1MsyXHwblVMkTyRmS2moDSxQSmos4WJ8Z2QRqy5Chq4WE6o0wVyUF1Co8wVAk0zHhuKiKDX4A+VuC55C7Mg0UsYjkV6it8XZUs0V70XbhnChqCKSzHhOYANX9PRAU9jfOh3c5Umxo8tXT0N8W5EB6ASi0kjEwMyVVqeTlUC5fJh7UHnAw3ZUqCtIC2SVNXN4IoWSaJAnlNWyRFCkeiYYl7mURNmkqCxzCvIl7npgWQBnqisDw8giCZoYxVxKj1BzwsihgOVfn2KYJRHcvNl/i5zYIQADW3wC5WJcLh5Rq92xj+fVUzcM1F17O5Iv8wGnHq8Pz0C/VVeShQU6cJdqWqqUlnClWQxiiZBHcnOxPgAjV28u7nEavkEmKztgGM9mDikCJVJytZHWtCcoRKp8hfFLrnBizcWuAxjE8kDUS4qj1Ca07FokcE1UDqPUjlSyMxDMxLhtKafmQxPuBIa0wi1SuyCFi5vlJcAOUEq1MbBKmn74YgboqkTpXfwb5W+QNuz7EuHj+okrlRIyjUKqs162c8xTCw45VJWaTUAIFdYxigOUAqz/GxKiHjZ5SrkUCcdExi7YSeanfDbStbi36QXYwwNewl045UZyfLm1UaqDAqvQBvKpmzHKpmjlS7BXpqrmjASM5UUsRBVzUMxXlS3SRD2NlVTQHRUrg8DXncD8hoc4eAxKGMWpKWjlIScciVaumhsy0xWZKqxSQsnRUms35HncZfZ+88GWL0ypXICtdHgyjKAuK/2RtKnRh5Yu/mcCOeESeJWhJq+Im0cxhjb/ZvnU8ekXLzSt52K6oAOMnRVVWHVaOig2iEcqSDrFL/lTQTBULoElHyqjLgasK+ELoQFAdkxQSMaFsXTPfZUeq7YnUy8b5rag+qt40mUFEx0l38nQQGSwWVA4PxXow3KxTs3sTow/JXfrRGVP8gEWLaw9mOKr3FB/L/m3kUJX/1F/nVCWjnik005gCzV7T8mAFeCjhXCwlSV8EM3ibilTLsRSBX8fNzVrcZFz608twTgeRWx0xDn3wqlli8EWzkcwbGrq57gY4xDlly1fk12QbYmyramd8jRi5Mr/xDqu8WSMspUGoXAXN80daIy8JgaEgAXJcLahPs5Mg1Ch3kEiAZXpKBDGK62cT3a9VC01VnmZoSfBPsgWGEM+dIeWf+U0UyCUU/d1gVymeDWk9O6YvGeVMkdpgYS8ypW0i5xc8SCW8wq+EXOb/7byoezTioRJgk1nmGsWtgbyo0Df0pSUQC53XtWSnnFQaZlTo8mnc8+6Atiljh+UjHkfQD6G2qcZmZ6pMBLytch7creCZuQeViCHQRUI+NmZsuhHJsgPSpsphH96DNkR6fqGAUIBip7E7XSYRFHUUaGHa7MZ5FsWvGGQizbWeXhiy4h5nvtM+J+eCpXl49enDQ0Zb50d9UdKd+jYbCTw0CTvXiqK1jj6t464QeMllypfVxIHS61kxdiW+DfX0qmlma4tqLXVPzxjgzlFUwCKmjLDBhjSmrRlUQfX1gfNQN0o+Vt6+sAwq68ggQHo6jLS1Y1888hJcZI6jLJUGY4zLH/LY/XHMTKUdzBWHH6gzHqos6G5kDfWPcZEmNopKyb/W5jjMd9oIG2VZpDRjjbil8kpPDBjIGrN78o6tEGSh/QoInnG8gX2WX6OfCNq6bQpgKXq7ME4pDY4aBv4jfWwcaHX04sPV5FHZAykX6VPKYCQ4I6dq5IxtFAMHZGEqNUkDkXVGBCeXFb6RWARsj0iBCZJaPkOpS6op8h7wrfRc4B7AaGjKAWbfEHIKknFEGzaqZuHUXiG+lhpDX2BSoS8mGiCBTzS8IXiGwdCJBFFRuvEwVY4skiNVH7ZzKtCn70KXprlXBJA4wqngIrFSiyG9FXa/hWbVZoRg6izHKhGaqhyNXnyYiwTXVQOauKiSntcWrFy7Y+hOIo4KScHgly7EKxFU7pHBEuXY4tCSkVqwDFy7AHWIo0l5/0Aw03sR9ynUmbh1nRAyf0sJEn7JCTYbQTW9cd6lwMFI2JwfnnvUyDgJFRab5qM7XF2XlqbVWDCp0xmmrQv4mjGuFgfo8xqSwKY3IJY4VvoakBD9QlTL7cazQYx9DmKKY2dUCTUCOHVaXnOFQaq9o3NlI8JnxEaIrUGo1mhPag3G/0U4RaDF2KR9kQKFMadIV42AqIFwHKXUjz6gvB2QP3YpjBxlC078qCcJ42ICEEnQYwGmKKu5TPkZPmzGxkopbKQpisRDHVQfqrSRNehgmu2znE85SFiDGUR2HVC0G+XL+yP9FHcLaAWbUqKz896kpgamEHKP+W0Y96mbG5/ZS5T8BLG1RUiGuiSRmRDEYw+k50Sf2SY0smQBKj43NKCsBLG9Wyj0J42nLIzVvoeNxQsscII4PY2cCfI1SkM5iHw1EA6XOrmvKHE57czU2RgbU3REHfBgmkiqim8b7P0GE2vgQVkHKSMwGUGE3jcEk1v2Z2GzGwyHzG5sI/Qck1QwBsBPGsyh1eRmlv4/4Sim2OihY2k1YYbXYKlPmRCm3phuCH02J8dAWgY18UomjsHg8xmnHMH2xBmnzrsS+dGtYEVxMGQmGQTGE3f/epx5moSA80lKDIEbDbtM2mWzGuump7crLuse43qjbuSHVfSgXCXk3PdbXYZtMIX3G+dVA4M+IK0GlkamqRGHQQ6pHMff4rossDb2fs1ZCdMC2SgvD3U4aodRV4l7GjvUpbEx6JEvY123U4JMGQhkN7FcAWbGVDAYpY2pIXmDTmneja6+dETwd/iHVHRDRIxml2KZ7k3m80JlmvuiIgw6pUS86V34fPEGEkaqHrE03i2BU3UibDyXG3sQko6c1zoahAwm05l34kaqNEsE1PgMBjgWjc2pmsoCLMm82N8iU13CNc2t0Q9Rlm7DWRwpgytYTiDwWxXYHCnc32KMaX/ouTh1migTjkM9HM3efTTmoP7asQs0+wSs0RYj2yWmo4Q0W0dhgOGE3JEUvYqs4cBQWrJjXVY4qz8Ic0+yFonTmm1FOq7M0FAZQJDVZGzkm3xRfS983owAVUF4P0ZwgJC37K+i36qeHkjVNUm5Oe81JWTtEQdVEl7GhtjsW0nSMyPY1LgduGEWkoh7GzFBokwi2XWFS3ZGvIpjEcfI9muqDIopi044AA31/ezxpLQ5J+sS42vgRi2HVZi1f6z+wXhcy0yoZcBxW/9HTwA80NAbaAcmhAnTnGVDA5WU1CgGhh+7cgWMAe41x1Io47m+pqfm4uw6YBIUVWlon0WqAynmmK18OTS1oObWlMWx8Q5Glmxgazs2WgLyWnU/qDeWmOik6Ms1d4zI2TgW5nkmxXbSEdi3VEhlnYMEHFDWvGEks+lTmcMK0x0M2lgmy9if/GK3UjLZmuFY9hMW/wgmW+jE8vMS1T0QnD0WjVinM6c2aktqynUk2G3WlIk+0toSmCW63ndUiko4TY3rgW63eMd2m2wdi1Kgz8kGBBsAUG/ohUgF8nX4CCDTml1Dp0FNH3Uxy0dELiC8K9wqFyMi3a80pQ5UrBK5bUc1oKoqlkckI1ySSFk+0tkA/mzgXcqCNGuA/0lMGP1gfhCNGA0vA40228ml09wrSsX43kC7BFA4nqXXKmm0PgYGVJuQn4xWkNAAmotwQ6wK1PsEtHxuetFMWsIjbSlHATwTmm7VZ+xZmrymUMU9kxWoezuGryl9Y9a1tKcRgPY7ew8WozklovQiIEpi3qgKq0yebBlnmgWESUpLn5Gia1iJfHHhgM82f4l8lJcc7yEWgbF2U4ySKqG82y2WXGv6uFxDVdcA60z3GricRjgWjNA0KzskYMda1C82Gkz092w0WzuiZoTlE8sV06TgGqhOs75gx8Xq2jsIfl9k/NR4Unc3ikCyme45BKw2G20RILcmksdW1K2mgSMMzdQPqiaqNfL+lm4kEns2teD6ULcm4iZejgW9yr70/5RvG7C2scAA1rleRGt2gzFua/5RZa6c1EwNwTxkxhitkpfa4E+Mk6Idi3/gLq140edU0W0hyVE/3HBQwC3GSAF6r03BIXyw6qJQTpw24hqDJLJj57ULcmc4Z/YyUNhAMGmOROYaraBpVbVm4rUhS9ICAUOWXG9cLiVqnbWC2SXu1yccy1AQd7W925mSKW3fb6HUPH7G66qCUP3jgM/uy0siBTQQdRC8StzSUSFw3EYSamr0zRRZIvAx/MEjU24/+hnGoVBuKdSXYcK0AWbObScCcBlLqek48hXTHp2j65nxEjpSCfXEbMMIR5FGFpGgTlEWKlLbWOBZCF46InRkvAx+QOuEV2sBgFOP9o+UczV7qTNWYOqlUjy8NQZ27XZWlXJi92jbGabMWoC8Xh0fkMKRcOsQSPqwvGDhRWEgOj2Bv24qk/WtR3PqGe2ooWIhSO9VK2Y8BkSE2/RmOmcCZSgbrs8sx1OYPvGn01/VpLSFDfwPbnFyeplmOwTE52u+iGQsx0mYm9HNSo9GiO322EmvpT0SFo2MHTgGn0m3gpbJQ7PdaPFt2OSgF9ZBUis5qXUwa6op4NchT66zUF9Wc1OBPJ3KENPqzIIxjl4g00Ikkgyk6W8Wn0qIhdO+uBXqwvFvCjfYZPQ0jJOr5AIciBR7HGhieOhJHa7BSDvtJu1R/apXTOuugEEVMk02a3r1MMZz+42IjAG606OMVfUbXI1Gi9XiD/0M3EzW3/U2SLajAymOQlw7Z3rsK+nnSCHzYbaT5VqXh1XIKGBnOp/WJ2iTRqStJaCaIxiy4x3AjhH53iMApn7AkCBnO0LFEYnnECCam3F6wvBkGzgRnO/HnA2g9xziM51xMTF4aG2VbbO06wsQXVFbybZ3Kk2+DEumUip7TzQ1cF8kzwLY0exSLHl2nClQGG04Mu+lWaWq229iM51k04KXyYncSaMbl2GkOc1woKrgkOgu4CJD1HP0EQ3rNG6jKU1bmpwuTjUgEtGuKsqWKLacjXyxmyOEUWG44a75GYzZD4YgtY0mozEnm1OEh8E9adU0KijIl4D2eOpXuFF2Cj8oyibyiakl0p+lsMTxSiY3KwfQuegE0rMlSwPUCK7DXrG4skgRooxEfQtgUau0j7mUwN3wcI8JDY4qV4EmtBSo8LHp1EnZK9E6DjwlNGqwcVGloGY1O05vCYKm1hgrfNH9K6N28q8DVfWkWmgwwoBohL4W9o02xP01VaVsIU3U8Y6FK9JNILsJY3IERjRLy6/DTA9mkrqpeUG5f4i9GjVjZ6+Dgn0Bq0e2fCVkwBnGlWzogCy1EBz4ME0tE751tugBC5ESK1TFF1EYwR7hD2i1Sak0ZEYwKaA/M+c3D4JeVESGNXzot8CtYc90rUY414wLDDtykP6zeRmm7sM21ruu3kT81906YJAnpscMCXGqW1OE2BhQGaDGraj+VCJCJBCm95AJoj1RJ0aDFz4R82fywI33GxND92kdmaMX6DQYhCAQndD2hEWqnRoAF5wCg9ktWxFGKJOZhtKwCAI6yGGFALPnJyvthKIDFGBUZ8nsc+IkUSjewj0Y7W8QFWiMemP78iqWC3Kp9FoJbmi0cgLF/OtVCC0pwkXoe0g5wnN2YQJ9lRMR2nPJT5JwRHRUm4gA1XQQ2Fw6yCZa0ZaFhYbmlPs4UC+s/VBv0LT2RwKPVqodRBxy+DjQobdVKel9iH0xnVpgb6E9jZ2Rw6n5T9ErM4h8S2WDbAplwdHSncK36HLQxfDcgNz1MOw+F8SVhWWgWS4YoyLB/DF9VOo7shBe2Lg8chdDwvJL37MUp2JKp8jXyys5m0uHVQwNpRJe55SNKwFAAmzz1cXBZVlUJQ1qoCdijUiTyosQT3YwGnILKoJ2Ne7rDmCkdlsoELzUejsC9iHRV0wc4x2enNaiajV56AHtH6oC1HkclmiMRXT2doBJW7zHNYyem2BEcQ5XBJJvl2e6VgV0mrQpPO2EU8RBj7SuunVerQVMIfaX50JWVZbNIAaw3Xxf0pb0qyMxn4gi5R2wrM7iw/Lm6gddjfQ7lgDU8qWZUDnVTYNMAAo3Qh6w8L0v6q3L5cvODRayGF+wrhXlS+lhD2tG5zSgzWrtJ9FGUL4Zg+tJjKOwymNssH01oj73M3JRgEqisCbQurGn8a5Wb5SZXA+heUCajmw6XJL1FsRfG1ShYVZk7wQ6IOlVpWMaW4KsJSeapQSDMLPQF3KplEa5Qhna5AnveqNUaiA5C8+teDs/C7n/sz8nCE+t4Aas3gnW4nS6YBtVaiuRnIy1PRSRfsD8671US5X1lMYvQD9c6wj+EC2FMYzDFUi3IrJEEVEEY6dgAovC7zwWaEoY9RBHc9MIbiiX1pAQ7kXcmtHbazX0B4fZWhqzUU5Gg2k1cAkXPUiX3IufDHv7Renh+7XJRqyGmMkwFGNCrEXDwKPCm+lYy6SM4VIocRma+79ljKv4WhECWFYM2tiQigmlESrBkq8RPklYxSVXo7+g7iz2TDunP3VAbexX86lzeExv3AMLWhVCiZzW+xZ30O0oUFEtP1agetF+i/j5Hsxv2yXB9Vm8z8BQozXqfShUVbtD1BZ6bo5c0Fv0+q2z3/ZXriXoia1Lupf1fIEcq89cS0DoXf00RBNE9VCeB0wmZlp0UUX+sRI3E6R5Ar8g/2HKFQ3H+lFhmom6Q/Kc+F7M9ZCh8hqLbSsZE9UHhEyUHE5yWu/26eOyCT9asDE88f09cfkVQO0MAyIkxG9xCAO2SQbC7+sZR00CAM/KFFi7+3d3ge2GqTPHv3gCFb2ORfy1Eu9v1304Ik+RZDhzMs6bSlXnqw1c3AVw25JKuuhlCocem7+ixWg+4fpbgRgMcBpRGjIn6QQQQv10nP4aYwn6Tp1Gf0Cnc7qABlCBdCJgNJ/AXj0B3iAUw3303Av4YQBvBiSs1QNf09VkQBoE1Ka5X1x8ARiS8nQ1akvwzSfX3iP+lFg02CX0WqrhlDgUEWm+m50EBP0VN0u5XaB24Vt87Km1ugjHfsPoV2oG962BscwoMh4hjwWwPiMU/0dJbMBBBoxWS8xmTHwWwPKEJAlAWyHiJBpRBxB2YhMukInQyqQU9FcWy2B2QT12sKKDhKPU2+3PADi6JjI/WwPI2tvn0saE2qByFlhCyXljmamAS+57KYM6KI0/eQPWEx3CP8sxiPMrX1qgNcWN+M1gS+tHaSGsfluCUin4E1drYCyy6O+jOIr+kf1XMav2nZD8gt+kGmFq1QN+NZDGZhDdiIomyBhSOYWGQEGm4+5ZooizwWYYtj3VU28liKxeTmkpL0sBzoUxkFJmPB7gnBaiyAnKx4NJgfCW08+enbBhKDTMhsI3EiX002W3VWiwIPbBpZGZ+w1DcGzrCE5d4UNhYKRnBvFlRqmpkLCx4M6Gkv2L4sJHaCOKyBSvKy6MyH3kOfj6E8/13luzoSq3UkXRwwb2dYPNlhSwFwqEpL0fkfNSJ8zNhGeyxh7qzwWos3EPzlNKUdEeXBsUjxoNeGcXqYg4P9fNZYq8v1A5wmgjmUXINhSCJBJeiKXHB5fmbIdL2z8x/3jOrq2L4bqqP+scwAU9L0ba2oPLMw0P/0bvlTgRJgxe3drR8gZihsdL1CZFv3fcjkOr7ZMWh8yhUeelJB90R/2MMYZazeuSjasSXnfrGI0G6L7EJWwMM6G3b326NHYmBwyEFMuUUiqEwOq0w+FZ1XJgCBw8qvIv0Nxse9FABkuC3en6Ad0mSgosWt01el/Rpo/+0IQIr1fE/DEGQpCRWhhG1phtJB5WdL2/QGQPSkfRHEhrnhBIxyIhWJ71HcJpHdhzNAehs7HVh7OLXq55IeKULFphofipMJL0jhKFCYBmeA3ohjZZwJQPLLHlhJe/tjF47gOHgTyl1YgyjIY4bSkgW+EvAR8QtiluB00AaVpYztij8pGqxEGUP3cuEVI1V2FWhwoQjen6ZziEVljvZFwfQ36qcsYH3r09IPcB4kBHK4kNjwGGHcBxFUGB9ylve8VFWlFb7ihw0iMY8J2+8e8PiIFJ4QBtzhM+y0BtWDWEhHU5bMhx3FphxXZw20CPD8NdFkQA9jI+p7Dpm17Lc0D7IbhyjA+Mztj70ZkMt4yRH3QQCDMhuxnHMtNU9M8cOBquEWuBDcVWh9vE8c1wJC8hCNVocJldCc4H8RxjTW84JFncsO1yRkGlB6lPCzEXsN6ELI6SIiATzwWcPZq3xHXQWU3Mw57oU0xOTQwpUPsgOeFAtccjihu3k1fV7L4y442Z4ya6SI47hmMdL2nCnxkwo3z3SSGTL8MiJCYe4kPDLXzmKRjXVhIzAUYGllAeMW+HVcHzpmR+0jRinr3IMpYQ6RhRXJhwWrQu4JFUwFHw9e8UmM42pEdQRT3Z4eXDTM0rTAu2b012OgVqM5vBLe5yjBA0RnvtS8N6e2mG6I1xQMGvT37sz3qrQuqME6syMIrMcO44ounBI1mAZOrbZCyD+marc71niOKzeo+myNem+n60pU6LiWb2YvC3GR0rz2ke+J0ti1M4ZsLaME0i0PRosYQ8epzA3UKInXunj2E4KGkTEkCBVWmj354uVFCyTZ5Yes6xUi01JQmrD33QV3Fa+xQhLG59WPCrX0IEkVlFITOAys1uhNHe433wtOLUE5cAo2rdCa4Z0WTKW6xDm6/CVCxgnOgYGMlKZfZYYv+i3+t2zf0HGlmA3zGvGkNixc+7KG0vY1RMfCVoZe9KSW/FQV03TAfkLq1HusxjUEucSNm0LWi0+dX4qfGnK2OVE6XM9iIYzrUwEvODmu/00IQN6MuVTbHixyZ6i0sYkq20mkdI0Wnn4zKUBPARhyov5h3sg9GuA6ZnOxe+iIY18B6Ym52TPck20quVHvIb4mzG96Bz0pRAwmw3HJ0mwk9mwwR00/V2eW/TjJ02jGCG1EDd6j+l/yoc1Xi08V0nLbkAerTj8M9UB8Rt2z2KFSU8YCn5lmlW6YsXRF8QXc0oQdMJ1M7/jjGqRCVyrHmxtdkCXGoEQiuEnSuCG9FlIeuiRI6Jjme2haYQU1majLI6gexnkJRuPpKy6RxWMHSNhrfD2TJOZjCI1AqdUeD1+0/VleatrmM0wdCkS7KOziCuPEMfYQto74S9kkMOMh4ZY6Rs3hsUmj1ghnSMG6yMN8294UZWPuDA2l2rq2BKNP8xr0Q6n/kZWZmTr+gWwlPHS3ZR7ilLe0hh+sNyOIE2KNvHZgRuRxakRRvfVIG4JFerYWV2erngXqz+PqIa0mkJF1A+M/xjS6n+Pd4zBU6hBeDOerkDLahFDkQS1azetiVY8tIHNMLaMukUn3/acMDoJzKzGIzHQnIx+POyXyRAR/FSfWl2pz4H8MVkfY1IJ+lg/8xh0vu2eM26vAN1kpeNyYJ6y4R9xhp5Jb35sEUDIB+lh3RxOBzfNaEj9bOSzekLDA67gOPquVhiJmn5KB4CBwhk2zWMv7U5h9JE9eq+3PByOAnJOz2mCaOGBh38XT0xhzYwv7U9VEHxqJtcini90F90LaPrSlSXHFEwRiewoAlwXIPshuW2OJgRiii8xSvywxO2SVf19o+i052NJh9B1ViXGg+Rpa9/mZqIU1+0ifxj8zhDqS+GNHMXIOQTJUHwe1FhmMnJwo8PON8ORcXqdEwage1z2P+/wlVWllTgkyMWt7SGUF4NPkd038RWsMs2phg3nB8VwQwm0NQbBmt78WjmAKitPLIeVM3FgX4PNPFMD8WxA1m81ViKSt9ADDUn0i0JunwWkiUKi49o1MxDELIUNm08n2H40hdhoh02yyOldGHFCOUMiydXvUwuRXwzkp4gd6n5sUn3U5ENj0W46VlE7mYLIa+XT/Ccmo86mDAyk/YgkztUho4jHiit8XUjEcrvJ1yoXcyZ4x8YjE/9WdV+jCZkR2YaBrI8poe096lfE4LVktISilWxBi3EuiTmcBq0HmaKXpgQZhCm1C0TAZP2pMVq1iCfhW89car2eME18ybBFHcsxghm9mkCwilNLtDZPAOKPAQp9UIiqYjF9cu7kYMefSAp2JNHc7qphB9mkgpolOaUdML3GnpTXC405LCAa2JodlPH0KVW9o7/4I0kvbjOiWlAiUX39ko2kFafGEl7d1g+0lp1o0k3K5EdI2ugNkA8pnpP42xzA8pv+VxWmAFWyUX2PiM+MwAudBvilIUMG1tgG0C1PTsLG2mIMRUjRENjKu1wSYMkaKawYykPMFBkCFKtRFU0di+mC7lSoPNyNo17mXxwJCF2E209a0X1iY6r1S2PaXRp0VBK+uqn4wLUX7O371gFXe6JSypLHG6DkBy9VWXgQaPy2slRXelBgn0ON3asIQlAMzdbBo7OgWcu1Athj1Ev6b6OG2JLhY29olOciiSx0IHHkk3tM47C51xp7+gAq6dj+u+HF7MDTkUSCtlCkdVlJ2cqXI/OW5oU0NQlc+yMAcxtH7+njn71O25DYyvnrpxsDKR9ihWYDqX1gMBxY29hL7mglW66lHEdSAWV4qGhg+0sJSmUt6DL7atMcke1Cgqn3hZ8iSlzEaXhJa0sOU40MBo0m6XEMhA0ruwrX+sEllnYG8jfRg9yVJVel9gSGlTs3mASpnnEE0sRW7MU0C8OpMCPGw5WCoVzkZqLbWEczKDrGeclhC/CVnYHS7OOiMEh+w5Wex4R1uKR5WbWyF2kGThl1pzYUMs7DiPcRGU3kQelbgD8Jsy5fZ06m3GyCfBXX2GO3D2ubb9e9fH4owmHzs8x1ZBsxRmQEXU5AbvGr0gmlVqcTnNWcvH4ONpXMkWU2v8AoCIysRIgoEFEqirqoUxXxDxmmZSuAx6V4sXxW2Z9MIpeokCkMcBlp0VrUmXVek7gcEMzzd4O8O3MhNHamWwsCZ3IZrrUx0bmOPI+tGhKhMkjJ3ODK2aWVDw/xmTQNUk6K0xks26QzIC2jnfCQbUrIgfFtKiATyZjSJTBxDmQ1GhkiYdHUdSdhlXZdOiIc52QaO0QpiNBWUi8BpHk0mGWwwEjiIM9vkjcxtzRsAbPrgGxWW+HB0uKJ6AEy2EC3MAbNQsN1Nru7a2fW7YhHdZ3Xy3RrO1aKqAF6tuwAmjgb/qtd3dQVKwxI275EIyq4yExIhSQL9hLy8b4J/GJHjMstlmAjmAxIkLAV0/9GMotAkmXceV7+tAlKMIQlHsPIi2I5fL60rKEhYWxHdQHjlXGPZjz4j8RxsQd1ESBg3bCgkSDuuFgo22nkcQJtnHc1lkkoQlCHu9LmckRpnXCHhFbodl3Y525hWstd14Mf46rMyQXE5zXivB1ZmtfF1H5GP7irE52SjUwKg1gHOECPYJJrQofAl8TQmEwtKXF0F7hZkjogNgYXNotJo6aErSBpu+sAbi2+H9EQMmYw1oaOYEomsoWcTRuyoOjEz0Aq55pQXhnwkXKOeEkNMNYZEp8V0MnTK3wbnNus+b1frAOUZEoXnM54a1OYLlk8CN8PPQCW1cszeVrIulgi8a3N5GzBm+1P+ViI/Qq89L4RI0sRGlKhGkkNVRglEkq0bqyJi0wo1m/yq2mZYHxOR58xRXwrbbccRXMVSuPrRulwn+5uYiS5uGoh5n9QawsLAQCPbnUiczhY8sLCVWh3MZgfCWc4gXj+5rDCMY95iuwtvN30vXOPq1nOxE6cDfRrQWmJqwmqwMXJy5mNFo5glP+EaN3yIw+G6gvhxhuzXgq0DIk9KRIQLQiWQMGlUEuodBEpIYJIZEpQShs4uquKxFFDgMDi75k5GEw16GyUIPVjYy93jQ1WigZmmEEEEBFVrBq5kw6OlKysSjEm2GFzfFm1iUFAO/59UafwmMghsk6E2EsJFPtDNBWugBAjlHOHMudMIwF0zggF4+hlgBaFRMZFyvQ5BKMwdAtiY4G3QQCxVOEk8PikEAuqFAFHWuma12w2GqFyHhFd1S5CIo6h3p89AsAkjTPLaOrqpw2fjrG5bQkcUWF+sYN0Dw7CJUip35tQV/P/IF9ViEWUmvQqxjMqiQvH0rAvQJ1OEsQTohYFzai5MmyCnCV/NCB6cVsu9hhPoggvwBs51c0CdEZUNaPXVf244RV/NoCql1tMDxgIwlWRnOqnDqSidI6CAvo02Z5B2FzhBp9DqQZSBGFqnV3EZPDNDFRn6EtBgvoPYf9AIwyJSp7euChqKguCjBR2PIbjm4wgvFpLDKxiNLZmPaQES/6jKwdSEllceLaBp9OpPwFteC6SAp0aOezxCw9z0WbDDLL0IWFhCty3WHVMM6wyEKlF7kC4iV6GhsSaFrOggKSwdotGMMi0gWT60TLSCZ5FECySMp9F0+MsBp9IcR4xg6xuxRy2V+JJjgcoAknw7g6QYu2EZ4degqQkgy0CdkUKc7WCqwEYvKUfyCHwjajkQVPbTVXhjrFtYJ5ctR3S2AA0SoUwjIOpf5y2+JLpgfB0eBEy4Gci63ZYkB1Mqp1kZWfEPqKNA5x9cDnR8fPNqO+PPmex5Crc7R2MgA2gnF5pR6+wJ3N4Kq1jMo8JpOs0CSM8DkwprQ0gHB5m3w6KzYqHx0bis+OGhcmkMOu+h3kkTlW5NOhmOjcW1sBjlBoOktpMWU1fyVP10l26UIly9i3MDktISIHkzhFYu0WW0B8yzUmf2kIhMIYyVXIKIg+OrPnkZoFAHIHm14lpsMhSx1PnFpyLARzyXj5PE2b3QvUQ8hKDYbbLx8lvHmt52EtA4KSDGlwfMxOtFPbyxmEfyvAwcwR9Bi89pDCC24vol4SV1dNy33fO3nCSwnDJLWmgYcDH26YBs2lFgXhEw9XnzU7DbLmIsMYStcj5Gi6yCcSCVWgHrgF9LDAi2S3lK0W1HWdL4ovCw4PsCNZ14gfWMAC/6WZGln4awSfnG3fMv8KjnUG8A8yl7CVAQS/HWDEAvpkK4rUThpKxlO2yRES87hpIBZ2B0ZygcKusFVFzSj6UYcVa0EcoF9JP2SKv1hhO8AUQieRU6YEb14GF7h8QJsUSkEI0GRaViIK4sCdZCEsWhoDWoud2MxOo9HtajsVXIWEsf2rctRERcub3JLlNiyhgzl2dNA0sXiesmcv2eWNPN8mWXal7yxpkpsXBKeY2BObuwcK3xQUGkA4JJ+RURwgR2S+ziOJii5RpDdVJvFzLV108q1WlQsSZa1iReG9VIvYmMUQ6jEsWM3rXm4Hx39KC4XObMsU+Oy5hEKzz2AQOkte8jIX053dh0l7XIOCw2lbFue5KMXIWTU1h3+SHcC6i32IzlsDkcirpDTnSL4nrHoULJtJ0e2Q4rlChJEUln5QpkpYVnsO0vEYDGhNC9GEiGtkLRBpYVVWEh3Mud6Bgi1B71NMx3R0xhhSV5ZpAlzXhSoPStbQNX67VCMNE+wQVgs2g0PETkiMkwQXTweQ0PELoOyi0j5SCc63rkMA21C8yhd23ujPRjkU4+7C3CgBVS6ih7Ct20fVuivehvemKvUIdf1xCpfRDWg0g+CjD2/644p5U2yWY8GPhbFuxO8gBdVm09ytyxVumLa4ZY+Uwi19MelOKCF7gjGycDvppNUWK9G20Wjpk2iulBMWz+myp80W8gIa1xWXJhBC8azFm8gUSijKQLwJ62Wp3IULm9K3CM/yuXCg+hDW/13o8pYV6wxG1Y5u4Wc4xunTm2B0XpwQULwRc3UCgQWc4uWjrWm5HKO/av9Fac16y2iszWqqv/YE8XlCqfnYbJHP3WpYUq8Hm3bCt+j6C+fQYOu5RD2DMOAitwQR0g5Qn44rV6e6olPG1StxV3eC2MKGthEGGtsIMfVBmhbK4iiiDbmhUrP0NGsG5H00J626t2E8M3GSIA3lCv3gjV2dHoivckHK3arcU9b21CkrbU16yrwUy4VlRHatgMBv07q05TsWk7nz60rigsQU5yOeiPlqmpmzV58MZVnQS369Whj+m9W4cwG02rJNX8Qek6cCxatf8nxE0WzRQSaxQSpK1u24JJlAq13JjsW3ETi051XPamK06oQk23a+6n527/61ui2t27Q6pSWv52KCd+Vnm87CNqzWs1o+O144ypXKktykjVPIUcKl9GCWg2Rv4vSsHyam3ugw1BMVurqdIiDpaUXIXaoGi3b2fljx1vNngWj/kOCwFBSm981ysdmtUClp1mHB4jt02kXNdc61x9Rf1LC5thDWtcigp14VysKuvBYGO2XC+unz26qCN1inhRgCksJ/Bdi5C2tiZlp9rd0WkU6Gp4s3It2vKoGsBqVoVO3Vw+SQVkwT548oW9cRUvKB9p1LCzNz/l98ReApYXEwNUuDbJ6y5Cx5hmOxWBFk3IVD8a8s0Yj9jsVliUH15UIZVsRoKmibQe02ivk0t/kgO0C251jLh7UEu0wtOSi/qxWAz66xyuSS1WOENJ1y0d8tY60pR/28pWCwjZW/0g0veWM+lsqkMA+O8MYqKuuJIN2hjoq71hpnR7TsJYcWd0N/x/tEGLyKzY0/mlFoRuD8sWEikta0MdlgCpRA6Gnx2rtYcVbtXRNqO/MMriu5CwNhHapIYfkhsJ4vc5KPXdcJRiClg2Q74TcUQisUvQU9CUAC2zFakQJ0uwTxUeO0A6NnXA3yNn6BwVt+w+dRBVbtOdBWlpFARK/EMYVmxBiY38uEwyCux0MwNgCmlkX+Jct3JR2t7QuBnbFtiR7p5vmVUkY2MHJ+HhajuspltEI5GtbiaG3su4IOdAJamq1lOrWHJV8uhc8kJsC8ejXjvFIvnSNp3zl7jlDls0M5qtyAv5w4uk2/cuoxsp2wYFyvqFtsVlOgCmtSjZKVGxlrr0SKV8l+u3bFnJuJlu3mjOiiMxojCWpMYappAvECJlmqhpOzeUogN3VnQjpuhwxMvCljpsMNxXn3pZ+2HKb9iK88iAsM+0u0S+9nABoRvfrchXjs/6wtG8J1t2JHk2e7c0YtLbUuSsTEUl6CnMCTyVwREo3YN/6zZy7/jam28MdSIrliY5ku7SwLlrkCkvRwxsAzS3gVqO/hXP8taVnwWEtvFmwNrS/j6KNmFqSosJFUMiChql4KQKxg6xc8MZTMlzkjjF6F6zRt5sTeBg3VUfRg+OtezXB+/1VQMysE02S7gc4KQCSxx04RBEs0s2jGBOo5jAtzUbfsMlsC4BEtKU32uDQpvhwt3dgZSQJ36EREUHWR9AYcQJ2e1L4t4pzivTYEcKgl8Snnl60AXpjluKM2BtKJFG02RSqvcto5hfFtezWO+0u13DFEFWNxQUlqYq5JkTkBY0+uvZkZNKHRRywNl9EY+5WiUMDpvoE8Dk2S1l3+lxUqWt9dhkWiZadipYvw0kMtnMTKVuNmfWMHaxlfFgWGURFMv4qDJ3TVPuBTFo1FQRmqzBQ6m2Y/f5g6wknhp9PEU5p/EQgk4/mhQ4i0Yo9VIf25stLCG9FkiUVClFgYhzpgeEtE/6wNO8oMMFqhCeYhp0/9EVnnhqTCVtlVW4winWHF3dh7UBGGyU3stTi6r1zae9LttvW6iF/c1DlzuOxFj2xFsAvozgOm0CFoiu9lhAm87Cdt+YUItg8yAuUqZ0tDl7uXZtylRWyIcuju/AveGRcuF/azCiF9VlpDWs6X8qQutFX43yl8fmntx9VmF6JQbXKQsk8M5392ObECF2XnWF7ePfy2Go02N53oCNnhSFw9k3t3+lnxj1ztWbF2bW2IvHwaGMQKe7I0/MtvmVRm1Bl/Njgd5QTam3OIGJ50K6SXduWtLiEDwxISQduYRV+vQtGBq+FiEcmkAGj1zX4chGexJdpYFsIgJojWKPY0QvZZx2HBQoiRSFlW635glAAUxduhgVd1SLfc2MMtgNf0dAvu4stsbQkbnKyO0hYFp8AqSpUm6ur2EQsSUzXQo4RB1V6FK0UfbXQ/1ViewNLfM9AsUMNin06BMnoFkXi01ymhxWOYXhNK/3X50LUwFxjMRx0zv9KVOFGphIMDw8mkC8X/M6oNNv1ZVTVkwo8L9EmSjiUOjtxIfwglEqQQUosmFjKelNXOC6i0Ft+wjpjPPcEmLvAMaWNWEjVMbwt9xGNgfPUjP3kL9Jdq55jGi5MWfNN5/3OiwH3PFwGOHJEtMCxcnujphefPA45GvXrF5O1dn11IEt3j1orlmMaUn1L9LWgO529pFBnmIYcTYk2emM1y50MBXE8ImYIPnOU1JImj5rwXvCzqPZ8gfPWMlSVhYC7jlu6kT+IvXMK2IIuF146WTdvu1KywlxzfMPMswKBuj58ZlL5vrG1d6ZNVuu9TgcLln4taN0JqEVnHFQ+vGIjT1aSU/PNKZBWPdzZ4ZOmVBjKH/lXQH8AmEy8BxIr4TetvQm1oNdGwLN72aEnS4HC7Aq2MIz0CPTeWjI4uhIbVYktEhNHF0DcW2uh8S36JfMS5MtVWE6bPG5uEtwRVYnASIhFjWWZCrExTLRuzeV28xpmmCbLuAuHFOfd/1Eu2td310XFuxEpplp61kCsdvnvKEIoMk5j2ypMgGtpozOOPu04m3WApHSOChgDZlb4XItd2Ck4GU+gaUik+oNY6cuEmyXJuXlmpKO9IwzWXorGnBYAbN80b+A3Z0LUH4xKxB6tUrgMCEllgTm4F6uFjfx5RnfrP7WwMY6Xz4j+B4gDeWosN3u2KE2MVyjcVie1E1oxtzK94mhm4QZ2Ug0o35eIsxjD8dRVHy4ZFbc50UEKw0ipMwMlvhv9n+sM5GeKC4OCKg+gis3vIhCpRWB6rZFZ84LU4ApWCPI6mB661JAvG1UkTsNmWPucBkQUdjkdut+tgqbcU5ZscHR4jmBIw0mWJI3h2atur0DMMQ2e4pCSfeZeZnWIe2cqzHmCcspOy4k5EzwQTk1o8Y2cqzcvicn+68O7+i9MdfvAo1nHktS7XeZzWC14nqjj94UAXZsxRr5wjkY0axlL2gEkja5Za0O/XwCK5V6y0Zslctzr0l0Ven+E9PtjN7Ohn608XFAyRya4+mPCZphAgBxMieKOJEHucFWa45vLKc48u1kzhC+yhzPjcOx23gFb7bK5hnX69/MavZH4o2m5hHhUzM6CZKXQZ+iudeyruU4iiBlal1tFUph1qZ6Y3lu4oETeWTnBdrG0uqpAndPX8Ufpl5D8iw5DFsWw1hQyLWGogumQ0uhmGgj6BQU3HuPK2fj4elEh90FBmFKYFASUj0sI+6THjeklinoglUfiSbMksPuhYq/lyo8H2lR469Nc8P1jw41miWD+pqIZgZjem8qWcdBwcgNt2GpG25jUUxGEmDrRk+0o7oA+ytB+0xinFrfQfFsDSlUgOeF0USanBo7uG2c3ColomqiT+sTUSEuN0k8fTWTJhweICZIhdcotQ+04Y2ni61rrkYNHbQf7lizWc1tpw0iCp3eGCGwt6v+ojUKVn2lcygNWmy213RvUS08pyal7crdjbWjof9KIqk74LnuBIUNTBoxAm2yh/oDY/G2So68VZZw3uZYoESt7IjWo8UFPuFT5GhqgRRGouNGHrHlNREIbFeC3xSLDvwiQ2wbnJ+gyjusKtEWKgNXUIJxubUgfH5px3GXGm8hjD2ECMyIc1XqtNFfhXriXGzkgOR+CDqjUlP6M8jVrLfgnpADP0nDo5hNxj0BPYxKXOURhlqlHEVEagemem3xCfc8PwjgaFN9FnlOvYJY0JQf7gUppOjluoUALsI31sgSgRCx58mBSxYm/e8pzoGrNVc44jGO82dW8A4jG/gktWJI25PjMMlQu+k9vs09d5aiy5Nlms9jQ2r300svd2IoBXhe+vzOXG59T60ixGJN9E3IJTP1GRy425x05MpIR72IY6ViYa6uibplC09KNvmKBiU2t7GZO+VeC14MbJPdVKYormyqU28lIcmmtSUMSzfiYxqVH6oucD5QS8NrsDvgdJ7CK1JtW0dJtAVgm8Zu2Jw/FBnFdEAD0UVziD6B1xs6GJJufAaZ18g7gT0WcB+D2URIPUo9ufnzohD1pojogiCar0gx9YqQCyDhwxieMfxmcXBYWkMEew1G5BkVQnEsxNXMR/2Q8yLu2GTqjd8tUAgetRMTUFv3mKNiRiJ5Lgqh3hhGe6t3/NyXljljTPVu/Qh+ioiQeEsRNG5icf0q/D00e2+AKi5ej9sGRP7KwMPhMVhOj0ErEmBuoliJ5QjfRgGhaydBMYxhsMJ17hM1dvBN9l0SRIJpyhEItkJUGxhMWFDhMzW4MN2engvMqoVBT8yMO3k7ZOORFfWCGkXMpgShM+us+Mi5uOgyBrUgxrJBMucjhNyRZqMdJGiPyoOzU7xsvXqd17I4ROc2kJWDDhMteiT9n+PA16uMYMS5Cze6bBYS4JFK0f1EkT38VxIxloS5E6NpIF1E6hBXOze3IgKR2iNGMcb2F8InbhM95X+V8T15WafkbUAf0sT/KAVMz2Jplnr3QU15uORv1CMM0hLc5BePq2E6NcanxkeEvlOMJixnhMtECbCpBPFgQSdRacypIJ8QTVxtqzRsMRNsCfhmbUBB2MOWco6R3xDJx52Cj8jKyYoGAcEe5ULge7IsSkS41qnMJQ6R7DPeTieAI0qgHjI8k3MyPsS1I3PAKJqRC01GieDwmsCgelRk+Mw9S4uxmmuCVIm1I3bhitzOM5ygZEogduOJckoWiMiSjMxwOjHSsyNOUZUsZjgbH2ZuY4955MfYK3RF80cpPRTmyXHImgQJtspATlLuNEwWVPpoF5ZFBvyiqwck1RgA5DQ0v4cuWtC2R0vEpWjsIhpEmhiWmjDjmxjCAnuo92fcaNGQUoc2bPafmtndcOzGx3DfRhTQHyRDFH2rDE1ULrOzGuFhLR2GA4hC6c1o4Ym5WMs1Y9mAkQCPpgGxzZhyog8QQjicp10tQlhKEztUWzikXEtkDJSikLKhcFHeqbugqW+ej4Yg2KuSPC3aT0amnNTDEgW7eyExxAnxUPY2pO3okrqhlntpLkgwxp5Bzm6KhcJ9gn54sE3vICLOMEoeHkztoUEz+xTwe/FQAxr27vB+D2hUXOnzlS0eM0sWD6AjjFgIoc3xCqWlRei0NYezSCj8reC2+yGODM2wlLut+OmCbLX4ob9ieDtRPOaqIkY4mBMTOZ321En5QAm4uhsoK6fvINZZUTg+jrT26xvx5eh9oL6ly3G+M4RajHivZ5s9epNi5y8ok/3NDunUSWAb0uvmPxzF6dUSlE1ot+NqgJCSUozSDkZ0Uac4SlGQTI72qhull1I2b3FgBPO3JWhmxz0VAV0hSAbXOzvD0c7p1MvKnTRo4Rbh/7J8OGBNCyU8X3+60CVRv1DIY4p72eLaPz6E9Z+TnBhLe7tGUMtphDUnr38C++MXce8PH2DycKwbTHBRk0duRlLG9hzZhmRpUFXY4kPHSkuBuR6yBYRrN1Ui+svOl3H1wF/Wks/QCDA++HiXoln6Mwq0PP0HcuORtZW9huZiiSxSNyseTPIIEKf3xoyrA+6UNlRrJSosDcMYp4uct8nTAbhxBiCR0j6jSl+fPIfhnYO7UPO6MHDBInBiCx4kMSEsdSSI36P3hv+Vr97gPjIts3ALwsTMJncBCUBiPZ2iAOt1j0OHgMFmoLjlhUR6dFzC9VK3tESPN4W4lkiCZgIRxAvATkjjVejcSUeiANMoMIgbhiCCmwjIvvpjcMAuiAMzQjTMDYEwQyBpN1odgbD3UiAMkMfisG6DxSVvUGGmUeR2WRy1ZiBnsRQoNeew2ApFsBgmNKhjpkyBxTNzmq+gmkSRORWcUMUQUNh8JixSdzwCC7Mw1zskXkMq8CXLIBj308eiB2YMqB1gsmxeOMK+E45ZFxbRgIXCJw6xTj1udqqmQNKugmmze6fFrol+2RIAJdGSvQOaKbvskCwZh6B3ztvx2N2iir9vrG6wVrKwMMU/dluhhz8BxB0Ih2dueixljIMTMOsPOUDAfug59siLp2gz50Pl80M3hFewZjgeiVqpUmpdGVc0PyMXsODqewM/t6AXjz9OMDiiwnAy3v72oNvkvT8b18SQlUpiz3MdhgQTz+/YpURyOtdizJj7m2cP8KrMfeV8xSzh3dj+B48vkZ0NCcMm3m/Q3H1tWWPFZIM0P7L5LgAx0Fod8KiOiqHZeki5fLA27wSXWk0Wawe8Od4PDvY85BNJe5mQKpp/0Gh4kOJoWikRQbVXihv2nxUbSXgIR4MplDukWIkniPBiARohxfHlJmyDH0IoPU5YsCghrbWE812F5YzTGZFxUc8OiX0nJ02GclFp74rpbnySoxkX+jOJf0bvkYZhAOnZP3i5BgF4RA8wOXXRZMNJaIdQh8B6tCmmxOBwFyPiGZP1ca32VQXA0t+sgmuEzTFaux/2UYNj2aYyZsOj7eif+xYNKQDYNGO2lfewnkuh8yiSMwMYP6EVCVdBn5la+/ZVFBhWjZLiX0+yVyXNgfy1KQCX2N9gpHr7Ef4NB6/AtjryUx2gjFpMBNMz80pKSB9VDxUP0Uq0NhC2B50v+B45jR+jwORmXIMjWgVWqRRKAI990H9KboPH0FUPP0fX1fEVFhW06kShYntEoYv1jX+6hAk0lDGtfPRO1UBYP7RbqomBjbFv1lDGfPEwObiCld9sAuhKBlWQGJzukEEBtfaTpQ3/0xISUJ20A/9cP0iaS8cG2bAmqBqJt+8rB0g4hAO5+s0BWLhe0cBw1viL/XV6qvwwKQSOCz9H6QnFR31LroslKBsxj0ps6YplOJEEFjqQX+1OfjWDhM0/d13t+yWDT86CBDq9dexcDAfXrjNDjr72FayKdfR0tAOVhBsN8yPNe2YMz0aBvKmfmzXoDEMsPC4QRmN+/pSuh5sAWKRw2Lrg5o0sstfyImf0DMqkOQbgoC2S+JH5QJ+kPEQ9BPr06tprpbHH0Xf1NHSpJxBgWE5iknQE080O3pwgNiMhUVtWbqBoB9OjYCjt4DBjFDAMBQXg2lk04B2mqQC+JCD+lAnjJ3iAGcDgPivP3k6tN+iD+/fnTM8e7sNnldsiksX26DENDr4KHsbpr3WT9NFyUQCUHyKCOAok8Dger6tsVoddoSBkPgLYQMTxlYWkiwuTTB12mLZ7mY1UVwUSoqJU3L3PHu+vYmBSh8Bhl5leCoIENyYQGnFrxb19qtEKDRgjHHweDWKUyNfOfZoUu+gF4Ab1yjYkTdU+Y61dKCwVOGsWSOGEgrR6p8s3ld5leD0a1UAITCA8rlomUMI7nqcoP1oZJkBvir9jcw1QME65lW4ldkP4ro4Si+tFXFr+quUBi6KFc4kNycaJhEayuXXB4waPiLn0PMkZOZvHWU5DxzzEh5BIYsfLnj5FcvEhw/V4Eo5oSEl8OdNywcwc1iM1Ik0Dj8q0Oxt/Qe4IY8MhEMjUEqp8C+R0aKHp8pWYsWcNliutO3fLQNlLuOgpq5weiwZYf8CNeimasmks20Re7zmGCaQDmX8R4KtcMlx16AXkNaSNPl9Si5T7b5VYmDxccz2rOpd4171EtjKPCMSgM2+EOcbMXCCealOFa2hXEeWSlWkgORXJRxfGqDhMbfy/rukOM5VKg4GXKoaTBpSiFariBud/0Cum2vAYizel5euZ5XvVp95hdIdjl8yH6ABL8MbicgQQwJ7mjn05eZGMMVuw7icqCc+NwJtlOoWhzbXjI8z0SxLblw6nUmRdjQQn6GpVD2YHf/cBQULoGll8Lu1BQwbpWWwXH3uVcuWfavqueR5ZbscxFO9TkpV2vEHWiSGe1ZnUaItZy/uZhtjeIcjRgKJ3XFkrL3dzZlnfHwdHV9MIn1I45ZqIytew3ovT3TwNnUWxx+NhCIoMEK05kZRjWyk+tTHT0FicI4S9GTY5cAkTyhgGM1dLu2Rr2uWuXWnWVxN4seBcq61xSEJxLGeymHnKzhndPsm6hyTk+1WYg9kascsft8W0Bw6rwW2xoeMt0xGVSkopPUC91fNpM9v1TpenoexfFGe2hbqssd0Q+cqn3m3SQ9y8Phie1xxRMDeVw0E00KjunMPgVokroiwnOiieVZTmOhl1gvXmcCEdB1O3k3ZzFCijoOp+tj93quyK1tQcVEnWF1XwWrJgqSk6zzqkZM7uz2T69i5dPJkIjmVPHOP6dltY0x9yPZnS70sd6kZsad2WgELfvUoCi+6tpiHFAUdUt1XvQFq61z9vHPy664dypydDO65Wws2nphFZnt2vSobHVoxCfq7AtAlopd3MqhzCq0XYf5sd3Md0Bdhh0stFo9leEKJr/yDEgvNSCEzsOph5jRuy12MM0W3WwuXObGwBV9UyOCOwuYg3UTqlby5GHgwmIfIuYeHi0IHGCNB12ouH2SaHsiMLQ40SfWrdh+ERQudyz1NSoooPn4PhwRp4GuOwpV1zJ2I1Go3fNb11yenPR73cu0rJFU5oR3qqDuMhuZFxp/QgpbdZrCzxtG9Mb36+HwkP82tFqAI3w+CYjTN/pqqza7dO5Z808n/WHm3VxfYT8orYq1W0KRETTlEV879tpG+fusgftgWbC2JFsQvHXhgi1AoH/omduRCo8Mo8xumjOJkfljyGmVYHIY509Sxo/8SQB3owrp2tnNU6pkzhmAu1kCdsWslSYc9vL55Z3HMaMNrOw/MaOlJ3y+BQrsJGsee4+VN1N9S5mT0+m3x2Js1cLjGn0jyx4mgEu6SVfVbasw6ZBRXbl4tOgDO4uCBC7Y+s2CctQx8vFwRRm2+pMkjgMl5CyXVlsBYKfVziDEuaQct3tFb+hpOrSuTZmcpIVGUuY6jpTPdKqsZFn6DjHzET0N3hCy46Bm8N8p3xk9GGvHvxGwk/3FoH7FtgsgTMmEcreBO2gQLHhoDLEEk+kOM+36U18tVQa0kF+YHDYtvk3HOpGscNkGn/9tCS4l9d0sc6TOQTbesSN3u3snyCtfFFmjr2iCiQVnVZtF/3HL7IRu5ckgeBsTRRmOx9Wrw/3FgsNRsEmy8O6ESOxmO1GMjJ7cq1UMyspgXPf+4y9ewNwCtzmsFVhGwytbUTS0F2PxmGVodmWOsBiwlzyjmezKSKwTuvKorcka2Axs52Ox2p7sysBJuW0IqrKM2V50B2dzKQBN+e36qeMkpParYqg3HsYnnY3Tmk5FAMeMkwRtKscyR+2FB9K1KwE+hm4+dWJWgzWR15skdZjW232kAfgGGK0lwY43Qgw2OBWonmco5H6M2ny3stxzakkJi3oz6PFIVfY7kW6hCVH5yjrkwi0xT1enzcxcswpDNFNnpvMyW495oDtoSVG7Kv1a9jNaUm834qCp3awf6zpWrbqh7yFgQQNKteC8Bk4MJq1K2hnEHkugX+FoS3HMDQ0g7s82L46A3qNPE0TW3nuQ4nzPpWj2Gh76Z4Mkjq2/16inHtO+0vuDviSukND52u1ndp+KWBW49p6H2qj52vuDCu4hLccwK2JgUrGwK/O2LVmOkIKoa1RgMVutsZv1dVsNaaH1FiK1/Xwz2i5aYZ0c3OOAA0EK+4cw2xKBjS+BXSK3aoykJiV9Uyt6t22hk1Djc8CCGG1nc92l8OFo05OHzPnDxmEw2j1AjJ2FxPN263fcK61QoKZtK5mfeOrM6G3WiXVCmmbciGw5I72YjEq0NS/L82IjEYnNZvnhGNDm/YSIeiq1+2JA9VcQG2IEpA/0IX60TlMs2O4hHCSXxcPQpygSSX5iOwHkQT9V0V1SjwmFk2+DBN8CGmJMWav6cOW0BPazDnWw+RKEgJ6oW9a06rEMegY55BuWu0BISDH0QHhCWHVcreNmjdijOhmJOORs0G2e6viIz9kHo8TLsW0kjYBi6cJ166u3Cjk1y3eY2xOYEf8uVJC9W0pSA136mZUKk3GSSTg4mjpHB26nItEvZMWyqGtWgIItRON/hJm3xB7+Pad/Mp43793/fzlMqizXhrMqWlHhaG6bopW4qnoSM0146oU3vN3s9qkQGIrm5qhPGkcJbH7M2f0qR09pJyVQWsdsnX0227X3+FpLDPITsRDG1mk68NgfKctElw0atTcPMmjWDSmzhkz2wkevYaU3QFly86XJ42jsBaX+muMYvVr1gnFUq1jwNa/SRcUhRm2th9XiErUdvaeRmyE330YK3WEBAmp7d4KkC5U2mEyE1tCIscYp6S2im6HP43wYluWkaKJH142m2M42BptO2mWtPmE1iyvgHglAFAJ6/awVpuvGpUGOm83DcgV40G2DGsGChg3tpV04l7W6yvG7jiKN76LdYei3+W1N13Ke+H9J191XITq+roPLWvuzFBVV2bL+ozaewi1PaQGBxn2Xkp6qGvfODH/009cfo3I2NffMiOT32G6SQDsxmmZQG61u3/RgbX9EP9GsIj2oKtE+yYO1b9zKCsYnS7m3pqhK0C2kpPAO80MMtMbMK52bVOiccu5spJD4CKNIiSlmsJkCbVP7gYcSV31NFLaBzXxAvkiQmk6PO8q3AdOcCLYvIReGGhHg02CnM4pCOhu9GRqI18QEVnWox2xRGw2/mD1doKmhFXMXrHHmITar70LHfnkGa0827cqu93VF9wEo3VPJo5Y2hqC7cTaqha7C/PzHD2EqUyPfymFSunX2AtytA1uvYu9TsbD0IGv5l8G/XVAUBA3DG+o2aj7vtZA/PFdGgHHU5+TE3KMclb6NwjxH6oA1cfo3OG8z3dLNuObVMWAvs6g1OUeo2OYHE5ZH9g+cG1Fi1kzOnzG3ZhVcXh28ITXMQKP9P1ow8+ICaraZ9Vus4G8WwX60VhS1+nEiYLYuAyvYTcD30WQGtJiU4ye+76yZ6OHYh8UZSA2gunA2D25h9H9z3GmVrp3L68c0SaRwiVGk1EGm1fUhbs42AywVAFn7U3f1UkCy415jRLtB/ewnuPBkzf0pbP0juVD4/ABnvUEpnQ2147SeSPpyJ3cWvHia5/Wb8GePN47qpkWkeY6G1fVgsWg0T69UWs44ydpLElgKqVelk0tcfyPu7g6oEFFmD9RTgyiSipMtiRPgY/V2biEnJccq1+ec3DXIsRJVV5QcUZVPuA05/Ww2FHg342G2cGtU7qS8aq3tJx/VATVcNIuxGaPqDfzZ/xghG/Xp2mj/FTI7Xb69Xccf4u+m36/XpWsR3sUMzg2hEfA+1kErFmHbpZuOTLOYcX2ujaPotoE4+ArF4oEfVtAlhrP3bUcQPtjyWSgEPpukv3rxFfrgh9fztrNagI6HIGsXcH42v3mWxjj0sTLOw3Wq2Z9f6ypM5qwKOv0iuKgbOx0fHhwGnSmnEiHzcPsQSarbHPk09hLH64zslEgOU2b0ZZy0dSXVhaZjGP3diRd8e6O4bJ/6FX73Sbu+nH6oChodpyAY0ZA3cEoFlrMmOu2vCdi5523naF2ndv41YmReEo0MZrDA3MqJgzP8ZGh78XNTFIB9UwSbMCPDxmbVczRVWzMLUjKl+JoVYnBKCg3igWZC1dicC8gKl+88I1nbI+Q1Ag9d6Y98xOmGsXHcvt0e33zNhOs+KLiMJ+9/MK1iaEs2tR3k8DvA2QmzimfW+wMsDq5mNa+1/EH3IPQkC4bU1yIXHCfdzlusux8hAxkwkvIEu0S458nmvoYNUv/y2B9glm54cB8s33PNoZgi2zVZFGbE5DhQGIB/DLApniWjpnxGhD1QvswGAz7Q3MBAfOX9tJZyIDKQlExhiaQUw2DhvLvTkVe/JkZYfHFJabH3j2yaWkkrYw6N+bTTvBcspXVP3iNin6hok0cfo0fscovJEhDDDVLU8v6KVloH0e9NHM+MklT2rxG4C3V5lrCZp7aZhG3PNjtkQ3FyJFCDdkcImv0weNJirv3plO8YF7HPmMV2/ARW6ED52QT6vrrC3gSPPxC+o3TgbqqR5w/WVPzkJX32In9pqe+3IaTBx51Dn9GpSAaZpysTseo23WczEpdkji/6hFVie9yI5gC+/I8ol9N+0uNu31xR962Injjn81rlDa4hd/8Dvv4CMKJ+nQgQKO/70CXKqdoOHQfv2eEdvbd4mgDAI7FAt/5+o0k+1yf3jpfSj33vtIf7GHlWzKRgs2DuAiYA2FKb+CaF6Bx7v24dDt+wVRGiDtFF1JC4IKI052OW33Q/PHNvneied1KyFTsHazJnj/suvd9ve5etew1KynKt2/n3WU08hONi/3yqsgF1vb0ulXxaMtGFqPql9TizzuYvKbdu3gdfbtx8Sx0Kl/owrMnDaJDlUv1C2RdxSrp+Ydqa4TT98QOp82YyiQ8w2Yi/G3+ZGe36pMJN2/dYQ1g8w1rBavl9iylnmGoWhNNb6ciVFFpXktGlXEE5wttfz0w3sh1/OE3oj8zm/6xCw/aEdvwOyedgb1PvqhB2KV/NnQ8llu3plDZ+r2GkOBqB53in5Ge2Q6kOTd+1UUkvKUXt0p3q0mqt4sfJtvtOPqpYvFkqO+w2OcSWt5fL0m82aiqkTkfQCJE+3udUElqqSIEp++ssQhmV+Ey57vxA0vFxgUJvt2/2KTpd7F5Qj2eKY2s0RFEbFziVPGgXjqgJYvERuG+qsIBdrfy95w3wFDmGtb9kszE3uzjFsbMSZ6dXq1hvVvYvPs4s117Ssngci8JgcJ40NgHwRffpxzAmko8Il/ckM3gTqk6L7+Pu/N+vI6OEA/uKzH39UBOOL78Skcj+fKK/MicryVgOKY09Pbb+oudBybVK/3lujKxZDgO9EsMb+ZUHAtu3xmDs3vYvccwvty7AgNNf/FR3xqn+Lsab89z3O9U/tU5BFxiwRDlO+EwhNsp4Rdh7v5o0KJpI5TCAr9GgfVveqYY+V32FvYlim/9Gw9RC9vYtpMKX3bTB3Fwt9Vm90Ue/fCL4vTtv3bVPYVngcoheAfrE2cWkTk702q2GgrnhNf1mhSOtQ3A2uY6aMJ+/UIdlsKRTF4FP8BYIl7q8qP27yImvYtmgG4uYGzpsnu3Fmb8Z/WPYwr3UlzY0kO7p7368Dmc3BR03SsIVXFhxdeG54yJgOFvbiwVBUvtCQMsvOm5mlXwm8umUd9zl+c3XdnjVpu+5ZY41wmOdC/3/c2fWuEwrFuRAPYa2XxSi+9D8cNVNcvQDHvj9hYlprkwLyt/Tscb3SfR4d4f3hilyhtP5v8+BD3tLmYrmf8TSyCUDEcziV3j+DGSk9a0G5qW5x2HmAwPO9Sopv947XqUFf3zHst3I+3fPO8Nm67ncflv83p3Z1LNjur5v5Sty28wvgR4CLLE67lGpxPjTvxekC8zjoX3hF0ATU80Jbl+f1EkXCV01jzvQyFXJ1ziT+oCvymiM/9h4DcEDt8gGFlNSqBy5HzfJQVlhyFXOag932s4HMtJoFcBDAC9YRwdYwQjKjj/CpJrmSLLBsBzLW3/efZaFX8gYL8Ruk/TcvkQ+AHvfrQT1zvLaGEn73E3YcVDwB8BAr9W9h6rYJp51TzvfopPFSVgQWA3b0zYKANnGxqZbzZ/HSSscLUq1HqNI1M5OwVVeACo70MhYqNzuD5oRgDXZi/1HQC3HG4AkAVLVUGfXADp4AMAyLAqwQvfIsNhxQ6Rb/8qfxGnSpU8vxTvWQ9KlQ7qY+8SqSM1DrV3wSmNFOF8dSjFBH8DU3RVRFV+bxKrJzUXkDyKCzhaqw8UWgRCawT+EzEYxVATMID1g041J/lrqkIMWc0/FUYYf9Bfv3ngOENuyypzSE0ZXwHLO7hHhT+NIChiK1boYBhMTQylEnUH8UyNTBwDkHo1dWV+bwjUG7VFYlyIDIDuxTdFTeFts1FNMYlKdQJuTKBIbwjYfA8DeDo1TE0RQAzYY5VYk26A0wgOgP60b+A4b1+VRYC46CorUU0EdhiBdMUVg06vD1AvE0C1AZdZr0reTVUPFFsEWa8qlz6VQ0hOrz2oEgDYvWeQboDi1hiA6+xVaAuA62wv+QHUWq0fQG+HD1VmBAVNSnAcwCCFTNwZ9WUiLUghq35lKGsS9S8rZthZby1AN4sxqxopR4CpBD/FCngeBG6A1P83e3uFB8B+bzYZZKtlUEvYRct9wAmcWis1i06vaGt1hUVgMAFRTXJAwYUtuXjhG01v1lprPLhMIExeKGs3vR5rMLApSzhvLiBRg3krS0Mlr01bEmstqEeAgtVLK01WPE1GUCegIkUowF9rSmAnKGhA2Z0ogLiQTEUhqzisfm92EipTG9VDUBCNA+xC6iCFdf8lQNvJHFMghSjwAi1JCH2TCNUf+iJvHIBXYS/5SpJ/gIEZb9hUdVghY4DOGU8VMIVLryXTFLUJSDMODXtddR81TtYTrz9fbICMpBXtHMIg6gwbKEUkzVkEf789lTNA3xkJKAS1Vy0Ab2sgCFVZxE6vWckoIzW4YCAlQM2oTI8ixVwQXMDFPz+VPHVCa1XFN2sGNhVTSG89hGZ1P2FPwH5vV5hHS0TFEXgVixbkQst0xXWKeQ1MHF2nKjVM3E6vNDh8dSsoOG9yQxPLDugSX1FNcJg0gAI1JSBbvx0NUcC5vnzoX79I0VmVHCVrQO7ocdsTFXFIWUDDyjepDZU3R0m/Hlg7gPIuJ8Rlv1jDS1VTQHpAgJpuuw+Ama0n7xORYXZFtUVKY+8PYDY9J2tq0RTvdbhhxRBJAqMZpi7JO1VXFQvvdUY27Bf5QOxj7x0QNOICNWA5PO8/C0y1PwUO30V2M7U1uEfcYs07nEwxcLUeyyiNaZhJFU4ETTZFymVseRVqYAvvfUNNxVhYaxkojXKDICslt3Igs3smxVCIbc0wMFTlJsUd6zAgqIhMQMnFXmBAIKwwLstvBExVHu8OID8VMxhtP22mCEQeawkLICUojRGJDhVpWGuXbaYjGHNrYcgwhRGNZCI17U/FQUMVAKwwKoDzvyV/OmgIlVAlFd8/UFFPe9k6xWq2QpRmBDAVS54n7zjYXuc9eSkgac5ClBUycpteuFR/F0AKwBjLanEpAMlVZbkZ4EfEPO9C5AdlTfVKjWalS9t1eW6wRRteNE5YYyUXjxa/MiF/INqfS8C4GyVfJSUUykctQOZIgONLBdgVANQdIHk+tSHPZCJuq01LJdpb31nhR7k9CEzLTKRBOCB5bWdsUSxUUxUqpRHARcsC7DESSUt3OWg/Pek+ZRdyYT9H3DFbC55xiS3vTNglZVTOa41JP0KgyCVrKiSYQz8t5XK5ZhkqXwPEGrkafgvvJkAb+2lpO2tx2lqoIptV0ArAel8zQC+LfxE93xTpMX93byK3Tz9TkQpbEzEx4CAfd7o4Wx86SfcQ/1usb+VcWWPCeR8QSUT4E3844Uj/D7JjjWisSxFODT4RL4s59z0fCF1Q926OOCkgH1IaMb9BsCpAqQ0GGBzhQkshoNWgwC8RORHCGiMQvzHUK4sI/2ptZzloE2xLBcDOoNsLRGDS307/avFgZUURCBNm3yEoMb9JrkS/U/h5cDxbT2QQazAwCeBcf09RFD8dSCsdL786NTAgncRcf0SYGsA873h7L79K2HpOXjR4DW1bF2B+jTfxeTNNARbjKn8wHGWHaw4pGSp/Utkmf3L4eI0SrQ1LDH82+wDvaM1cf36pKw1h4DAYJr9B5wR/Ob4b0SEjdZkNbz6jcDla0GO/CYcdYNjJC78i1wpbTocRb37sTS0NqB1WJM1BfUYZa6Yub2OtJr9zKC2VGm9GAxhgjsBgoXiAgCVcfx9gZGDhglu+Mb8FkHHLUU05OHJdQb9sQKjvJJgBhUG/ZNV4jQzASSsU4PTqAO9+0yuLS5g/xB9vB3Eli3s8Ny0GBg15doswiDMOQOZtUFy/aVg2AKUgfIMoETqJY+9LGyUJAI5NkHffRoV4vys/X3xJGU87AQRVX3OkM2kQCyIXLQ0VfFcVIosdeD0A28hXmHDhFu1SX1NlJz8Z+xuqdekyOwM1U2wL72LFTS0xHWRcKl8/aUDbQUZ1bw/vGgsEYTk8bQtDkH+fBGFxSRcNQnxeEDLbTFgw30z/Y08xP2BXIB9hlEXbH+4ySDtfBCAf4IRwDGC0d1tAgeFroDOsVe8dh0I7e0hUXR/fHrVRO19NY+8PyC1vL2F08UMtb2RJswPXUe8dwCKLMhlDfxg4bmgsCwmYPe8f21cgnDsiknffDNhbXSgdICDV7yTfVTtUGBLfWVYZ7SAef1FR7wtHIos70THffY1fvSADJ6B4jQGGcV5r83TqPd8v2Allf98VXizffKBau1joKIF+P2mYVfMLhDrg2AwIfRS7VyQ+/1NAIFlb9ATHKQCh+G77MB5bJHiNTiUX3wUINqBL/0yoWd90Z3iNKXgnMyAteugRfyPRb58KJHGZFO9L1xDzMqkMAPOwBNsa81TLXyCpiUjfT+kL3xxTAA1ZRAEEGj8t9y9zSMwBENDFft99zUqbVD9uK2SJP+g7IJMIXJhscwXgWrRR70nTUfMdUGT/M8QAU0y7XupV7xEHDIkfx2ZfWGBGwE+7RmAUIJedBQ9Fuw5TKl83+BCQ/lxEwHzfPBhDAmSJKIgYLRq0OgUfCUtWIB8n7RMJZJD4jTbFPHsKBDXlIB9YWG/lQ5JeQBGQoFsLWTf4CL8bgVQNKwlx4Wr/dIRXJ3STQmdYv2TITQlpSH8LaEFRUGtzTYtMjXM2VDk5XwNsPP8rhRlzemlJkL0IY5D54BENJDNFCDlfMIQ/f2H4MBMnmQaAmZ9crDs7ZK8VXmQNYfhep0zCSVE8ij7aF5YSiT2HCg0OSHmHT7so6R/NE1FbyQyJdkg0X3rACCU6ew/gZ/VAhHxZWEAKLw31Q4oHmUaZZAUS7QifKuk+ezOGP39bfUmfDNVxSWP1BBUYcx8oQU5utQoYWxFGZD+ffpRLGSW6Ms9MDRivLZlJQOEveR8a0RbnXpFJGTeLZA0BGEYZIkY75UFQmbg5bVRKOGCV9AGxZZ8GXikdGA05zVXsZxxd9SToT+k8kX+Yek0EB15gP/FtJzufG8hOZzPxGplygPRfcpNwkGAjFw1bXikHMkkL7BINSiROUOFQJ5CPixZ6M/F43HyNdjhZoMmRWDBoUMS8F6FJkVC1TR8HmSH4LZFIeDBQg2QEHi2RTf9d9SkgcjMPh3AxeR9/IBIYR5Evsz9/aGFanWb7K8t8UPqZMLMiyWxfFpDXFFszOgVb9V5sFOlbM1+7BNDNkHMzUFF3IMwNA0gK9Sb6BNlWoViANvoa9UCAOvV2AG76VhEm9Q4RCogukGmxAGAqPRFQk5ERgmEwSbMx5CI9PdQhlTaRZBtDnBdYZZ9gGEnQ47gMfRNaRzFncWiYU4kfYBHQylQd0OuJfsNw1BLgc58XILgCAvwsyW0RBIVw1H3oc586Gz5cEVRgUK+wSblH0PlbRpl/0H0Ce51YP2F7TeUl0LFxRplWUC/Qqz5jjSwqCKkk1BKeTYleWmAw3vd70LfJPlwMpGONNeRbvknQo8IN0KdoISCM1DMvCgki71zcHb1bEVqxLDCNWFsRDwUJNHHQNAkT6HDccXlRn3nDPlx9KFafH+xhAlHQpZw42VbQqvUk2T8ADqEe0JAAPtCO0MzZZvVzyAEtK9w2IiXvJKxpsSAtcBgEDQMg4TDJMNwzBWC3CU9AJs8SOHZcVlAMfVfmdQIl2k37VQUESXBcETCRyXcxCTDgbWc5Fc9dRD0wm3FxbHEwsPF5TxkjSzDLfDQHTUlbMIVRVnFQbUcw9c8SKU8CaSJdkJ5xCKD2XF+UMg1i20qcXJg0OzlIRa8OnHkRbgc+6Q6cKuMpMORzDpx2ySkw9lk4sOS/NA14qA8wncR7z0VsMLDn1CkwyzC/MByw3zCo8HywypxPZC6tBjNEsPOZTu8F0QSKbZknMPkxbVgfKRqwyrD6sNyw/QpdUSEw3tw9MNYwmMxaEQ4w5IAuMMD0XtDuoX7QvqFB0JcUUMBXMPnxSpJUsQHkLzCjewTjDpxGNFSZCAV2XFelCEllsI6w2TCje3ygSzCidmBtRlBv/VMwzbComTtuGIIReAGzWMgYgg3YfolUwEIwgeQMnQNadMIVMNUYXdD+wHZcDqBMpWpyKgRKnDnEPbC66F9lZQkv6Ql7cF9vsOCFWXsACSeZAOVqmQ6gSzCv6UIZQioejkaw+fExoHZcOzDbES+hFHDasOOwg7CmRDZAa+VIwnMqFTCd6HOwv5cVMMnwJbCJtV8wtiROUP9kVSlcrWNAtpFn+A8wzRQ4rR7SRHFgBX0IGJEmOCewwEQMkXOJZ1lDIV1Q7SMQcOCUPJFkXA8w2HD5s3GwjHDQti6w10wesI7Q+hFa9QGwnjChsL4wgdDs2SG9bIde3DyYAJcg5CvcWg8AlwhgmTCUJyImY3DGvQg7M3CWdzDWU7ClBEjDfdt1AgR2OqNJBRWwkJk8o2dwqLDQ9z09LyVfMPPrPKNqRhiCEV5Ko0eWFTDoKUjDRRw3CnZw1p9h6C0lYrCLQwWjECB1Ai3aFCclEAaw9fZVvzs9V/tcsJ8oWOd9E18whxktoz0ATkkCWVLQnr0ccASWUlkrvwzw8fM3CRVuSMNipzSw2tAC8M1fdlwVaEfjcPCW8PhBWeNJZg0wzvC7PW7wl3D5My22a5VRWVdw2eNpmG9cR3DVoyl1FbDbcNWjSHBLcIdnP2kYgl2wxr1xg2XwszCx8KuzXzDe6FjnGTJxcMhwyqMrHzewg/C3cLXoY/CRcIJ3FHlAsMNZFndl8g0wnWs+dwjpUVlPX0Nw55FRnFr7Vuc+ayvcPL0AlwngZfC1VV/w8vCz/QYpWXDmoTbQ6vVOMJTZZXDeMMcANtCBMIHCXeczFB+DTnDECLUHcY0ECKYwn7MGcNckfdCHGXw9DAjcCNkQuEliDT5cEJVPs1B3UgjlSVSZSZ5J0Im8TLML+BsCZW9lnwuwkqozmBM7G4VMMKaAkzt8wG63YkI2CKNZTY1psXGHa3NBCMOcMgjVmUYiYDC06Ax9FKIRvWczCMkSe2iNMQiqCNWJegtlCIUI4ZlZczAoQU1Me3xVUgiN2Ex7DClSCJdQwwjr4lGUSM1TCP3Q9AlNiSlQMwjeNB59RnMjCI6UM4dHCLsI5pQejUZzPQiOlDjhTHt8fT5ccRBtu0DoTkk1ylUIo1R/CO7rVZklCPCI2TVVmVP4Q5wPCS2ZEWht9H8IjxFKeznweIj8alWJKkkMiP7zXpk+zX8I5BIMiRBUDIjgYRufc6CCiIKZWUdHhX+UAdRd0Mww7cpKiJOOC9Du2DhwnIAlXRKI1ojn1BSCbcpciK8RKV0MiI4fPoiC8XiIj5A4SR4EeIiIiKN7Elt4iOpwnAj4iP2ENbC5iP8I/fdLsyWIjpRkCNAIqSx5cNgIxXDu0OgI1XDYCP4w0bDMcB8+OLCSsTmpWjC3CU4ZCNFJxBdw/ZVih0ufUZw7jTbTIUddRCtkRrF6JGEwsDhXMQ9lXtw27AHTZ8gdsKthailuzX1wiSdIcQpPXtwACNCPKtQPMIptUI8uvxWwi7gC6TotFbD/GHxxQODqWTYpKbUn8MPNT5CniBzYArCI3W3sAykVHGCwlpCcgj6RQtNVEnUCR9FwsSqXWkjAcOopbZDKnAkVIHEMFxRwtWDu0wOFbZk5iEltbewYgkSEMNtG3FWdJ5lGwMptB/lvsPv5G4jZ0JxwrKh6bWS4FTCxY3kxOcRcSMpUfG0pZRDw9H8VSLcUGIIgHSxtCCBqiNGrce9RbW9cO615sQJpFTCwcyMxI5FisOtIpI1Uw1Jw33gRhyXVYrCidlKxaGFCcI5wozFTBD1I5Yh2SPzlFTC2hEYpP9DvsPupE209hHFw0tlqKVvTFTDrKmrvMnFgBVKXHCkpokTw90j503zZQLDCsNbvNUjfKh+xSEia8K/dSHFu1xbw64jQj1TIlvD1inCxWrQ6zlFZdEjqKXXAOEjeP1cxEuCATEklROk4SJjnFykIkBWw0oifSK+wgEwDTSxtRfEzCInPD0iqa0ZZcUkRhwnI+pd+hyqzOLCme2NdQ8AW8KlPJI1O0VJZB/CZD1DJM4jqSKlLS7CH8M2IqRRtiL90SAilcLTZBvVhsPYRDXDHaAxTf7DcSlDAPFsQcTgCBhoGXxE5NDNWCOFaK4sQBRKqMTEw/3uZO8iNHHKTM1kGsPlyAP8LpRxbfQJ2aGtxQP8e/GUZOz9E/2qlEqp9Q0T/b/ghCNP3ce9v2UpmYkJVC2MlcdFGCJMIElkk/nTJXwhdwNnZS5wuCJKlCChnyPOkFts4pSXREqoEcDN1bytUsRWCb0jx2X/XNCilXTDbPD5MCMD1N2UwSUOcELBK/xhRMQilwDdlWyMxCKHZJdkd6GaIrnhfvRWMQDEftHUnC6Vu8W6I/r4KWyl/YDD871x/VC1OkSRRSxVLf0QEWSjeQFx/US9DnGgpEOC46D0otd4xW2iLXMlIYgH/PYsPrWAw6pC4WzxZSSj9gjfI0KhmiLHnJyjl8n3QwnAvizHMHTC0wmq9KgEtJDEIpZFaYM1nP5FiwxLnMnFenBYjN8ijGGAw/ZV2f2/+S4JIDHOCS38O8EEo3UgTf2XQsQiGcRN/YANIqPF9S38tSB8olQiY/x+nSgiNCMdoY6VLznkIk1thLhfxAIJyqL2LcbhMqP1g+78RHWaInODs/yuYQSiZ2gY5Cas+XBqZOK1c/UUo2bJZqSOlaajS0Vcnb9lrxGJCFiB8PW/ZB8UVqOIjUajiAyWINDt0TjwOJhlziLWlJWRIKLWCW10eMGfrWxR16Fx/PWdWCKLXE39+kT4I66iTf01HVgi7IC1/ZhCSqg/CLX9hhWJCNPJBf0/MS4JOGgnfVX8oWGoo6UhsSysTL6ipkWxLW5hwaM9kIKjTcy+ojRgvyPVGfCiYeF5bZFU3qIfIw8jPdHAI3rD/ACgI88ju9EvIrNlkgBzZV7QJdkSINDgk4WnAZQJbFFDAlL8CeT4IhXUvYUFoaiixs0I7XxBqsJVyQJsoESJgU6iccBRtWmg8OxcUMFkliy2KU6jkKMG/DNA0KOCUDCjSPgcjMWjviO1bCXI5aOOYK4t8VB5o1Fw+aNp/VyR8KN/IzWiWyVYIrYomYIxpL6iiHxvIwlFNqPRgUEs5mHwotW1eW2V3Eqo+RVBLWFh90OkQ3lsn2A9oicBuYI6RKQjKBAVbdWtzKLUfK2DHGBcopRAvi2T3FyikL0tbE4oXKOhWdr9OP2X8LSR2i0OxPlxsrUI7Cs1zKMSHdosn63MoqxguaNoxFyj+Exdhd8CM6KMqQjskUKkI83AhYReQWgiRNBS/fpQxCIvdIWFZzWUI1AMB4QSgNwoAgnfgl+EjxyKogzc2aL2oSdCpvx1hUNFxqLKoWIt6bGqIoBlkEnaLYXdBKPpVLOi3BA9o/KiB4V24XTYZlGKokTk2JD5w9qjyxzp8Da51CJaokQQmqMnCU+imjgCo6qi1v0eIsCgwGDjo798/kUFrNb93WUEon5dXvxTAVKj7oJ4zYijlKE+g9VA/uBdo3ettW2AkB2i3FCtgssIvqP9RHWDSQDQokNVI4P3zC2jFyLW/eHkGaKVlWiACuyeoxd89iyrTfCjQwA/bXldWAgZo4CR2izeLcGjYyXaLA+h0aMKghejHLz+ohGi86PcfbfE6aDzovj9dqJLw/+EWGV2ogkQdYWtrL6jGGKgRVXlkaN8UIWEYiHgYy2j8RBs9U6jnS2vlV7Rh8NxKBdcX4QxhDmiJ2CWkXGj9pGPIztDTyL2I4mjRgFJo+AimSDhI+GAbn3gwgExWvgEI9kDv8NC5PnstSA8w9l0Oez0tK9xuOW5facVwXB1w1Zk3GOZyA7sbMQMlUllFsNWJSDg0sN7w+ZllNxbwxvC0iOP5HHCfZEZfZIjfmXZjRl9BsDjI3phMeyLw6z1r5WSvbcUgyKvfDYU9KO15HJiDTT8Y/Jjsc1EkZilimMh7AMFisNEkW1l2YxUw2xDd4HTJdfZgCViJUpiA8MIVPQlJAJRZbMirCXrRabDm51Awrw4gqRMeXS9YiWTUdQJUWRFfYQ8syP2YVJiA8IVpVQjlwEpwokMSeyH7X5kceUiYyzDNyQ2Y8Jiqg0iI0acOnErwv1ksmIOY/j45X0uw8LCKmMaYqP5AiPegK5iUylq7W5jLMLTiGQjjuUpI55ibCMQI8S1iMMiIsIiATGbImnNEyMyJOK1ysg5gF3DUV08Yhx0ATFiIZFCvGK+QHxju6H1w6CjJmU2eRFjNkN69IZjlQL+QpO9FKJ6qcEjlX3XAWoIN8OVfNcggSKOwlFlb2kJYsliBiQpY7fDy6yxZGljWSMxwp5l7+Wlw2rtktlpIpvhkULUIkHD56M8YlFiQcJ65Pnt8iKeZRLpGmQ74cXCffV6ZCwk3sPFIvntjrRlY9wNemWlYN7CzIA/Qrl1isK6bYXsIWEJw5MjZEU/QkPDZsNkJaDDKcPpwqVi7SRMeStE+ewUQ3zDs8L57ccUfcP8PXplcsV8wiUgiiN4I8ljIaUAw0kirMI57ahANMO2gU4kD6n3Io6iWCU2YNLCGEwOJPnDCqxWJDRiHAC0Y3YjC9H2I9NkDGNGwwvgWfwBMExiHZ2H4JsjvmNnjQ1kVsKS4GBMnHSeYwYjjsFt/HbDoSI0nYHDDsJQnLSQrmO9tRr0aRR2w/4jpxytAK9wW2K5nIcRaggNwrmd9SyvcG6AUPQgJJEiD3wzHI3M0SJJReD1KBCeYgpkt0DaUFvC7EUnYz4iOnFRxVmcByMnI60keiBnIgzUpyK5nMgDSyJ3Y+dE5EUuwwbBJZzQYv+4lGLFsRMA0sLcxLD0u2JWwl/C1EwxzMFiNHUcTNAh38I+bWeMfBCKYytBD41a0FbCgG10ncYj38M0tQ8hq2JHw+4ievQ9rB3COj2gnW7DJnTDbQ8gLZwUwh8jIOKLUUsjGxUg48u8VyJQYt8cyaR7wma1px2/YkUBx70USWmoW8LXoSJNXBF8w1p9VVjtxa/C6WJXRWgRvWN3YMNseiFdIlFlFjVZnXmDAsKpwhmc5H1+ZOPDV2PrtOUiPbwzHcSgYcO1ArD1n1DewgljPo23IiHDBWM/Y+INOSI8lNRNbMK5IyDjHcxU4ybMh8ClOb7ClZC2jZrpaSJM7YugkKBVYjt4SJxrAJnDnuiLYtoQ0yO1In+NXwCZwmbhi91apLUja2MxTOpjLoxjwgTiai1Q491iccOVIt8dq1GKwlW8kExwbN7DwyL3HDO9lCTqLcyc8GSlI2YdDEy6jHHDgyLMTXEdQuNmow9jo6UTw57DO2NyA4rComD+jZFE9SOUAsRMMAzdIhzjGHDwlUnDnyBkTNbE6cNR3PzM5mJmYzTjKSOStdWcutx9wwBMUFnGYvSE893V/AYlObkjDR+9LsMOY8T06BUsw7egePVoxKJiVWQPYuz1AhFBxaNisfFjYiOh42K7QxNi9GND0NXCRsOvImr038UnQkOsYE3rnAKj+QD9DRKBR6OGTX/DPfWuUemdW534VU6iq/UjDG6A5CJ04alVko1NlBiiRBC2jVwQzCOEIlncVqDQo1GDKo0g5L6jJ0AWjKGB4GKLJRr0nwEcxd4JlQhYndckliBh4lidDUBkY1HjJJwONP6jZpSQTHX1EiEdopBM8OIYoyWBIwwgofCiseHLdQoAg02GosW9H2IawgJoX6MY4rOBr6PqogWxgqxPo78dsePvotnji6BEwVKjfaJInAolUqMSRSzi36EEo+7jZ43XKaii5KHnHBA5ayIG5Y41wRGlQ20gloQdnGPgKeJ+9VaMNWCe44Rc7PWsZNwoY4J04r1gAsNV40cdUXHKw0cgpoEa9VXFxqN8UMPCSZTAoOmAbeP7RUgiTLkqjTp4C6LagSqNvG23cWstKow2ojpR4J37LfdCnmxt4u+lmiOqJQ/DlbHmI1Y8M8O9w8IiIaNLwpqCMiLjDZpR2QHiI5WwRuKXwjoiYEwnqfdDxZUM4qzj5iMdxIXinHEOcP+UleLiQKc0+XB3oDKMcR1D4uGEqJywYjpRe6GL3Mzja+O0VPPcAKQr4mZU0eLcpW/somBInGeBcCM1gX9jLkIzo0hgxE1iwn8gx+Kw9eUDe+KH4rmcrGEj4vviuZxK4ivi2+LETNq4K+JVoz9iOpAr47MBrE2U42viqWLcTYIjycBgTJtiRiLT5PcdpwCXQ6soieIoIvdRgpHXjZujH0NXraCdHN0TIJzBvx3PuQ5xEwFR3dYDJ0OWIJb13aJAEvBgFoz3wgATktkLnaxg6MP05H3icUwAE8bdZ4zQkNqiiyHtnWeNZiAMpd+00R0/wyQ1sySmgAJcKwEj4o/i/Qx86bdx7oC2jUvkjuJE3FtDusPxohXDNuMYRQbDk2N24q8jyaPGhUaUnuOdkJOEeBIYo/Q1VO3HeL6i3i34E50BTqKtkYWiSlCmdZRl62xw7IxEfyO8aQjtYUKQo26wk4VUYeXE2n1hYKQsNsXwojGht20xYAykbsKY/JIMSqgp+Ozt76zYnOvQ+BIRhLP936LK5MBDo2DO4/NQEYSHsSdCyEkI7AZdJ0ODnWIsHGVrIguwk+LZoyYRmiMZnLmi1wwAEsdDxoXEYfdD1EHGNf0s+HCow0khDv3OxPNR2fwHUfdCjK25guNhMhKSxETlbyOAwjDMri0BUEKik9CAoiaFMhP4gK6DUKPDcbBNRqMVqDNRrGX5LWQSk1ANQEqVkyX/Q8El2uSOZTPjxuKsMGvjoT2S4CHkwOMqdSKUDl3Mo+dVV5RiUMQjFaVmbUejl6Lx5X5MCeNJ4hnl7+L+o8+BIpVOhSQT9zXnlK2E0KOw8TKUgUCUEfCjheGX/aOk0KIGxQMsSlGgpcwTGYUC5cmkSqm5XK4t97Q8wp+s0/zBRFHCBfxN/Ur0UcKODPFtHsNZI/AS9i2tAb1jCwKtg0UjtmVP4S1sjURiCLDBep0V8AzDs9kIZWmgSsPZcYHIE20NLaojqcj2cLujbwPME7Dw/BJHI6WodBIHhCERwMJuiYntDoXEoaij+wGuDEjpCBO2Ie+FYixC5D8iLIzAQ4P8OGMsEi3igqWm5Yz9uRLQoiwSk4UJQG9C1RUXbJxM0RLsYoWE8RMBE3OD/4UhVb7DStRrgw9QgyKRQO1sE+VjwvzjXv1RpUnDv6AB/Z+M7SPTws4J/aJNY8jMt43Y4gYlH8QulKydxmJxTIKjvKKWYw+jEfSiWXK11JUURH/sUWV44y39fn1JwgK9qSz1YFTD7SEglcAMpSLQ1WdkHGUFIgzj2uXXhFHCG1RclCPDwCSCLC2JYWEeE/QgJJS2lcwT8LzF5dcDiRJlIMXlTQBKqIpFExKO4UUlybjvbdXlnUVYI1mjKJQB7BijF93HZP/NvuMg4DCU5KMEo3jAMJX/8TlVatDd1E8BzKJDXcdlHmG6o2rQimyTYKFkzFCEoCSVaGGAw9dDjJX+fZojABMqg9qdw3Da7OKUUXlzcWCASpT3oqDwEy1x/d1lpsTFIGmDqS0YogjxMIB0oicjM+l1Yg6wMqLgCdjhklWSoh7cPSE8oUEsJ3Sg8Q9BQS1L5AjxHxIx/K5AbAl9Qn+i08nOxTPpIu39CIBidPE64OFtj2mqw9B8xWxRE/7CsgTkrf+FPLy3E6qAk4WhhC/jnhSSLFlsEMOrfMT8f9wAE/1hT2wTTf5RObCwLImCK+MfQcDsBGGaI2jFciweWWSjY6PIQloSLM0wQrj1qsME1IIsoHX+YPKjw+Xk7eaMXaJCoYQSexOJCY9pPXDW4jgT20J2IlgTuMJgIlqEBMN8aNdMKKF7EDcN9CBmE2HtiQztuZilRoDtDNST/0FHovuAqIw3tUejReLUk2rROKKTRJUM0kBkYvwglQ2FhChihQ3C4G9DIYiUJKxQN92X8RUMJlwhE4uQNHWHkQVkzFHIk9L17c3iIsDZmw1BxBojgfTJwpdCDqyK9UkA5xKkgcUMq0GaIlzCKBLQki20+d1qontQ+HBZ3ftUkJON4iwprxJQYGHdVtmIYg9xq8VjnCP8txJ5eVaNflXKk1xUWJw/IUEIoRUM4jvjryFSsP/idqieUFqTdJz0okLCE23lWBLie1AigkWdH1XDcO7Mh4zHYhDC2N1A9CcAQBOWTVKcFwIf4oLi78A1EQKTcxPvNf10fBPXYIW8kmNr4rx1XjTHgHwS1+MvNDGFcCJHLXaTfmL6UO7t7zSOk8yjpi12kxZi3eKX0TaTz6N2xcjMocyiYk2YqRxG2EUSvsHkvRvCXaL0IUD1EswYo5ygoPUPRcHj8SPQKTgig4GuDBARLSL4Iv+Ur+IPMVgjQiBoTRHEUlBJZPnjzsXJuPfjFuO7RJCixcNWjMWB9hKVdG3jZVkFo8Wi8ox3EMmSz+JQgLJEXFCNdbAT7OD4ImdtZ4xEEKJjwkAYnSsM7SU4abLDx5yvEQQSDxLKXNU8KeK1kU3coxRdojGFTd2vNa5QjJLKXcmEpCKthGZcbJImEpyT7JNskq0MR7z4IjmTeZJ5IquQjazKXUWR5qNP4F9j3hBYo4s9CGXeYBJ1iQjdXaucQOCtk+Btk+NpQq2T8WhYnFsMCxLlYP/iYmStkgJRdJ16g2sh1l2n4sjjiQnnVGjjnhzJxZFN73V4gUltsKMHCe41wrzQozUCV0VIXeOTxSVVvJ2dt8X0tPOBqKNlde40tOFSo9qtUzXCYAKjtEH4tUKhrpOucAuS9mEX41ycj2DkZeIjzFH0tEhgpxJ1WY8017GAwvQUhTSyYTnR1MLyTN71c3DrxeD0FWR08Fns/oz5kPcSU/TJ4hHZQQjXIbhMq3xfEgXB1Z0owOeSecMknfYQ8pKHVQzjNUJfEmcBi92L4nTwzIBm4ngQ15OBwFicc+L3kjcUtox+3UEJ6/wLwk3kXxPPk2OcE+K/Jc3j4fzHkrjVC5wj4reTVRLyjCCAfxJyAFJi8oxmYPeT4o0D3SXdQJIQ3XXDCBO2fOK0oKAnpUCSov31ku5J6pOULYKN2eCHk1r44pKmiSdCEOCojcUhhOJq0HBj+Iw6ZXASL8hl3IkBV/gQww9QlQ0XEcNxZY3MkjLjEyCyuWcNkRyXQ8kVjJKXQ7qBxQ2UAuuTicTUkh/FmiJUYg+c/CEj4mIVZw1I7XvjJUSUktSjtRFx9fsdZKK5lBgS5cKYEiSSdGK24+vUSaLEkgTCTZl4TFOj5TwGo8yim6W8fRJEO6PIzPS4ReMidYyQ38OuUOb5V9U9ov6SZw1VJObYgZOWYrxE16B0wmYgmwN5JUnivqK4gVJlE0Hh49xgMON5JVdojuPokoJSq7S28Gq1K+1YkzaAhhJWROahJ0IApa1DI5NCEiLt2SVCwn/jfmzPxItdw3Amkj/FrIFQwgygBsyYQJ6Ss4HVQsmQtBLlIURiGcLPYX+TAVzQJQCA8pNLY44h4rC3Em1Y0CR/A9qSVkPmfAgh6pJ5PIgl8L3Kk6tM15GLtQZSzkUexemjhnzeffVih5L6Uo1i9YXqktpShWONTIeScmSyI/ljryAMg7Zi0FJBo5V9StVzceuhbWXSZHtQqhJMJOq9w3BopHwluUUTIXSN5WWIYxzRn0PeYuuTpwC5ZFEsK+JEUirtu3U74pfjwiTuNbdx54E+7S14M6LisL3N7EVIIqSiKuz+JGZQ1USSQoChBKKwwQbt5cAp48kMfCU9AIGSfOhMJTNBoeJ5krFlCFTuouc0kyL5wm1C2RJ6Y6PhqKKz5MZD3ZR/IqfNl9yJkvlDE4ARwxkSDFLaRXuhOdEZEulS/6FHE8m4qWNGyDpSVxBI4PJE8qTlokkk/8WFrJGS2/QJJCcp8GMkZTlCqEJNo7+A8ny/pN6iL2NsUGUCvqMIlG/FLez+oxe1/UKCfHHj8SPLaGa0GKM/dYZEEaIYoj/lhkVmlb7jDWMQocmlTJMi7Rm9FKIbKfAiOwDPw8ajccBwZF0JJKJmLEvZnuhzo5SiCeOCUr6i2fS2REA8g1PQI9+dCVKyEZYhuswwNFxRiLXmzfJS1BLQ7OJpBuPKaZJNsCK0I2sg8lQZwsAs7ZM1YkVDDeIQOPHCK93mom11WUNBYN2TVyIOJKFgThPdktVjjBOyiMZCQUFLU28k2c2ECAgiOmKvwy7NSlCNfYBUkKNftQ3NmKT5UklkYUlLXbCizrAdzBqMmSXIdUfMrYVrElm0wHg69W0hdAMjzK5NRyC1oTYlkMDhU1TiUuwUnSgjqvSw3TaDSCLsUTdSVIXMIgu9D3zPUhgY4PUvUo9SfyAtFSPM91I6UbGTsalJxCvio8GTzD7JQhPPPEPN/iJQEuetkiV+UihTkFPCJTMU+XBEEEdTrmPqk9BSQe2QEuBTLWJ6YkliCPDltB8RMyPYoYBSacxHbPeS3JCKIzfgt5Ky4g4lUxLvkhZcKCWyNO+SBcIXQs5hf5Ovkn4k/GJNRGQC+iPsAl8SR2L6IiERqNIw03pFrGSnk62C/8V4QKeT4FIT7J10txPvoAbMCgDuU8RFv5W+iJok+Hwyk1UkJZIAE3/jvH0G4pEpumM7xTugfBPIE1nF56H3o71Q06G8fPSTt3EiUrTTkBQ8EjiBv5FEkw4iI9AgIvrCiaLUU/RiNFNGwwPpQwHhCZ/tafV8QypxL2BJZJ34CyMKZG6cylwx1Fyjn1F7DALTgvGjUubcAKTyk5Dg4rS8WUcSnUR5jSZEAJO3YpkDjBifwuLTktKrWVLSQiDnwO8U2EDHkixictMqYwCggtIK0v+iyaV3ZSVEeROkkM+MGlB+gR4S/NMolARQYggPkT/9rbxRw6I1jS2lcFYo5S0kpF/EObQ0daT4Oz2vwmqSKZTzLalkD6DU5ODSM2NnEE39luL8RR0Sjmwdw18S3yKtyJ5jC2MfIjl9RnHQcUEtAIFOwtbSvxL/g0ZxkSKtg0dhTsN6I/AJXF3/YsLTcGPn0U7CYOKgRAbEW8IhXKUTtqWvw6tMkaDysJ0i4rQm0XCTvsMk4sBD2iP049hJT217kwESDvwELJwd5mSpJLAt2EnUCQQipOwnANETjCxBoEWw0KMyga4McSPwosrS3NJ80ixEz4280tJjPNPFDGCMPMMJ02n1FfXZcA+ogi1SbGqVAsIpYsnSadL1YjpDQKTVIzVZoJM58PJi6vzZ0gQTWSMkIqTt/5xFYpBcBCz0EoMiGNMrhPqtauPYkmOhuQB9w+CTF4VHIgBBTmMLbJ3tHtPG0rujU8JCRPkToESeYrsjiv3IYmfC9ZIulPBir3Fv4wb85GW7YyOC9AHsYxaiAlLm4of5eWxIZXtxfKIulHE5HMWg+YmDsogxYoJ1E/yPE7XCdxK7Qb/DkhKa5BDA4SMRU7oTP6Ue08mkIeSjY/yRbXS3gM6EPMJJRQ4ThBg50u89t5Tm+J7CQH37E/11zOPLHe7IHdK+Qunj6xOknbZkfXWbEnsjWSNjE+9keCwlEtCCq9M+AtETiwGMlWplLMNZ0yCUYfya067U7xSUQDvSmdLAFUkB/sLGIC3lPxW70inSqoHIZc8VR9ProenS1SMp0y5dydI801zTJkUtUhfSTdOYVW8hLMNJ0qgCJSAp076jO+XNwexiUnnRVcbF18LLE5hVUK3103JtSPkoE0ZxM2LP06/S3CUYlTvlEhFyw8cgj9Of07JjV+QfxcQJ9i1fAwPpXVNkRCwkn9P/0wpki7yAM9tSO6AjjVPl39Ktkp8s/9PAM3sREBTgM+Bjg1KoA+/SiGWBkvfS0DMgMbA1UDIv4q7kj9PP0rbwcwAIMrAy8tzW1UBM10MikrfSEqJD2Agzt9IQw7DCz9PoM0VFEeKoAg6i+2nRUtgyolhNRL+Sz9PYMg2RC9NoVMglgvHx3M/TJtJRwRJEDE1T5YQydPEsbRBVlTzHk26VhxQUM5HwtSzvFIew8pLBo9FUyhFBCLQy7xTmJVQy9xUV1Cl5DDNz04yROSQOeIwy9eT/oUwzEy3/8A54lCW1eVSkHDOu5edRdDIW1ef8FzjqpDwzBpQkJRQyfDIOsT8BIJOiEMb9rbF/k0m5cfxdWcIzIvG5gx2dkfBiMy1szdPiMzeUmGKiYxDwUjMLbLi9gfASMsBDt12SM8jM2QjIg7IyMjKsJMthFDPM/drt1SVzTP+g9CVHoaIyajNWZIJj4jIaMoglHEWaM9DCZj10MioyaSV9idoyAnx+9Poz+9VOdbIyWjMQdYcBBjL7JaDUJjNfvS4tpjJwpWCSovQx9XbBVCRGMpYy+HRSCRDxRjN7RZwzFjPotdhtxak2M1K9rCQMpQ4z6LQp1coytjII9EwQujMuMofBCMNOMk+TtG1WMuvCvBRuMjH0XCEK09ilujJZkvjQ5jMYIcElNDO+Mv7dhpWeM2cM04nqMjH0mBGFYr4zLjOtdJdSvjJKM/iNNSQ2M6B4idPSnXNNETOj6cZF8jPFDfTg6NNRMpL1OiwxMrZdKa3cMqwz+IzN4IdTmnjuXUkyXNNv4izSNuJUU1gSVcPYEyzS9uK4EiOwVDXMo7ScGrSNRWgiTCP9NGgU3VNxlf010Q0EojmQ9LwvtBpxl5OupUdgHaOCUzS84xRWo0sNiMVCjQpwPFJXRSqt4GMoYz29UmCsIkyjTqQhEUPidwGbdMkh+FPMYJWkTMJPQ2KV5MRMPNuTDWDoPOrpYhOwkuYdbiPA0y61qsXqI/+j0dOiUZHiWgUTvcRAgqW6WRXS+XWAjMeTq8VafPXc9fAaktCks+HKkhjiPSGtg+MyUbSZ3cvCEB19dH0h0yQPcfEjszJRM6GduB1VpYTT9b04fdcStxIrPHnE4+iwUwdBwGUTQNCSvikqPaW1wNIGk5zCb3lzcGxjPcTdULBTdCwgHOCJahI/Ymel/mASk9BszcQwmcNwPxELxYFAQpNoMn+1VmKTUB0yzcQ4UhTSkfxvtX2TfYEkY6p4SCM7JGzVpM1DPcNRWFM9xEIT90KsYazDRYFCEgstNcXqgpdD5GE1xMWA10IeYSo9lMMfQn9TvMOc0/wjLVDINa/d/CJ1QtA1m8CPMt3Nr7xTUD/js1LqwhHYABOGgJe9wj07JU5k0KQZ4BTTMlMyxet45xIcHRCzw3HIw/dMXUBgwtCyXz3Lw8UBSKOZdHCyTfhsHAiy+wPpIksi6MMhpUdN8uzAs5GS40yH4f9CsBK8pDVgl0PPM+zEzEMfQu9DE6QR0x9DT0O7TEXcT0I83bW0uLL3UVJTYsTwpIiTVNOJM/hTMiIsxMTlE+PJIzuEK+J8I8sitpLc0BYj4cQtDfhSoKRPJCYjiBP3TONhgMIkoQQ8adn+wtco84GrpND03NDxYxMyZG1P42mpiXRss9YiDm3ENJMza+I2k5yyu5Ihna0kcH2so3TTDBzqMcTTYfSgpWCztpMSAvSzLZNb4/Hgt0wDDWviyaSxtFTIt6OLkfjFlLKekwyFE7yd7XAjGZGVdfFQQlPWVbzFj2iO4wuiVsSGkjOi/uANIsC03eNE/ZakocVEokq0LaVnMnkgBcGDvOrlt6PvPHCI/TJ6o00ye+PGog2hm3Qw4Uej16PoxdFdurOS2U6kMCSd49sDNTIXsMQigrUNM9NiZlBPU06k9TOukqTN6MSWsv6idTNWsgfjGaBiMXWktqQ/I+TN1aVvJH8j5BNtM6QMSeOdI3WlTrFOou9EsbTtM4BiLxPxwM5hOKJFpeG0XaK14li9scOpICjSkjV9I9+igdJcpCKihJKQrSV0o5OXUj7i+XUVUlajthBBIgzMVqO2JaikqjKIZc1TQj1EkLOTDn3Us/egGKPlTeHFL6XnUobEbPU5UiNSfsRm4eGiSL2/LNxS3uD8skkjJBLUYmCzLqMUYqmzgrMQoSDE6bNUpTmZkCFZsrOSueDvTZGwsbM5YaulB8U/6Oi9/FWB4wHNxDQLVM1TWuNfvRzAHaJ3wbgccmQYo5czX7wh8TijRZELxIoSXaKcIflFiNyEkldT4XQopISS0BX5Rasyl6JwM1+9hU3fo6kjMsOuUAaz6cR5eOFSfbSIqB2ygcUH7QyTBDI9IdrCrFLNshCyS+HFMli19000pCXilBybBKJYnVJ+xREjxqPtdeHFmvWuUHe9gMDuk65RF8SCNAnBBKMnDbOkVLNHIeadQjzIs++jO6OLI/ZieeLWMsDhMCMCo+HF9tDEIhWTo7N47GZRG6NCPDSyarNcPA2RrKU5VVSTmdNoI93j901hUiujSlDawo/FHJIbpdFIM6PoEtulPzJ/IENhtbLHUn8ht6G7PDDVDNPbDCu1nQU746ypmyW9vA+lwixtxUXI3lMJPdw9a+PHE+MlglASUqyzZ7SEQfwjMEAxPPxiAMED7Jlg+pP+UCbxhnTk4YDCaBEoHBzVH7KF5YZ1Pl0M0pKzfSUVIogzXwHxRSGDVnGobaE8uqh+0G9TE8R+DaayQq3DtO+zWxP2PcO0VXRdoobsFzL0oh1TwGXFJYhjqIiLIpO1nkErE18iOzNnsvgjL2FX1DWzo5P5UtA1QVPd7F9TzyHNXOJQKe1CPKYc8ZKZUiEjOSTZU5V0VOSpU78ykjXsnH8iQaQmpAbEOaOQ4ZN1SkmQYxO9uaHJExWijLPDZB2jjVMus3lSAcUTvPW9geJVM3WkzrKhsi6zTrKOs8dSaf02pD+Ups0Y0TN1CB1ochNtV0lFosloUzIutF/EM8mMcweEqTP8gLw81yALE72S67Mxsp2TxuOcfBtTi6ILpKUUnZLLIurDkoLdktxzNrHAMxnCyDW3HK2TgLPnJTvA0KM2eIzDlvRpE/h0JH1BxbvI9HUexVgidBGGdMRcLaOSwxPFYrKNUx1jQ8VSsp7iANLWPFlJpZPMU/PFUhKFYZ51tKImExyjQ8U2VMDw3JM9xBpytvFxEYp0zNIr4lwi1jzMoHwSdLg+PTEUt7NlxRwhUsTLsBaSzFGrxfdDVGBRPAByVfDDbPGhgHRBSbd8bcWJULcTLiyQHcDgCPH2YGsymhII8a0Bo8SyEj8TgJR/MudjsNMqncA11imo0hxln00uoiTxvrJwpGOyniHvkuNMA5TXkiKVrsVFJc8SMzNqoRSikXx9tHTAdjIRFW6yrrNzcUVAmjXcxPTkXpMETFFwQaQ2vCZxiFIKJT01OuVPswPtQGDCTH8gL7GhTIUyKKBqMuNlqEWL0cQBEABWAO4AfgAIAH3RzgBAAK4A/AB8OVRQixEqqbTZSZCr6SMIDDDx0cJ4aKlpcpWoYngQBWDwFDkKkfFY/SiDBHlI+XNeqK4xmXOrOUlIhXJJEbkFCpCdwYGRdeW5csVyKTBlcu6QBXK5MIqQsAhykAVy1zB5ctqQlXMlc/EwlXI1cvUEdpG1clUQKSRz+IkFsFFhcCHY9PmNchVyV0mxga1yNKl9KB7YbXPhEE1zGQir+NlEhFCtcuhQvXL5Md1zFIUgUKMFMLGEhP1yn/BNc+VAeIUccbdUbkgxQa0psx1k2CNzFIUpoGNyU/mjKQShE3Ltc5YxnXIkcVNzvTCj4C6QE3NFc+SE+pG9jXXQSlBfiAty7dG7Hf1ys3NOkMtyMfBrcy6pk3P87TNzGBPYw5gTOMM76OzSduLZMzgSUgGJ0BNzy3Mh6NqRKlOHcm2sx3Mbc+M1fchz0VeEV+lg8NfpS9AKkCgATXMc6V6ognmK+GzoN3PDcu4xWXIc6CqF13NYMaqEAakL0VYx5XJLcozZdACyAUDlMDhkMXBUbQXxeHY4YklKgCoxA+RuSF4BX3Ifc1dzujjYAIeB5LGD6H9z+ABvc2tyL3KEMQDzpkH/cy9z4TGA8ndzQPK2+BMxKjDiKVUBajEQ889zqXJA+K9zjLFQ8+f5MPMCMVDytXLrc4kFvVCyAECwJQVOeEjzPHGLc9DzbAQpJeqA33BmBAMQ6PKVstDykwWgBYjz+AFI8/aoagDo8+0iWPKfcwyxkPL6MbDyv0kw8quh8PO/ME1yT+E/crSwt4Gk8kDzqPNvSB8Rf3JFCYgxAPL/ciyxJzEk8pTygPJMsBSxtPPU8r9zCPNvSWTydPMg8sSxtPIg8jTysZEk8kzyxPLfcrkxWNGvc8EwCPK36Ql4OlgyyFEREYE5sNKB5zGJgZUQvPKE0FgB5zGiZJLAikEQ0ILz+vF4YOqhq3QmKecx1pS+gaBx/YDo8vzzI8A2gZTpXgGS8zIMp5ERgANZlIHnMXkAO5ERgDWxxYF88t+gGsH/RJhJfPKy85mB/llXAKryTQkRgeRwCYFK8uOgdsGUobCp8vPzreWBvPIhAecxLVkrKObAevIi8zhVgpG688Lz5zGcccFgcvKe7ILyyIGq8p2BAvMy8wkQoRH48y0o+y2to4KhFhVEhdbzRSRVZaHU+PLyhDxwO6128xkB9vJDcbVI1bwp0hsAqPNlMPdyAQipMqqoVagxgeTzWPPDEDN9panpcm50PvJu8qSw7vK3id7y6XLzBU9yXvNW81Cw1zjWfb1zwfPfianp3XOYYCYIlNnpEJlDWgB+8w7ysLEFOE/Y/QnFLffYUfI+OUBRFEQ/2fElrHnx8+HycfMw2NUJtuSOCBHy4fMcCLs5NPKM8yeg0/kKhS6oY5EqhGHzd3KcSLMECkhzBdxIgfMiwEHzDvPOUPZJYlO+2F9z2gAziTtIJPPp8wXyxfPXSADyKkkCScXyHBkpAb9z5fJl82dJVekA8usBcMjdcqXzNfMmIdXz/0m08rXyJfLp8uDzyBBdSYXzEtlF8/pJLfJc8hTzzfMCSYQdA/iF8viEDvNx81kF2PMT0EqTa+jq5Skw5LTd8zDZTqgDCP3zVQQpiYaEipG982Dz7fLD84PyTZlD831hHfK5EUnySzik8ppJLfIc8wTy09Fd8u3zXvLEsTPyipHT80JIP3PaAJ3z+fPd8t7oH0kV8mTzMPJMEJDJbXLN8urIOen184DJDfKTCWvztfNbEGzya/K2SQvyFLHz8kUQlfMfcw7zHPLV8lvysMgt87PzJfNc8jZQEng88rsQAvJg0VbhdVX889vhF/O7EFA1QvPa89exLxE2WaLzpJH/6N8RLwDuoRLzcuEXkVzI0vOY6Ep4sszZsQbzAvKv8++EV/LizOGx7/MxWGrB7n0VsF/zz/LQcaYTuxAf81LyAnmMqEcQdb0moI91uMm7EUMA6rBy80jpwAuFAJUoI3JzBWzopjBZ84VzDzjL8/aQ/vO5+BAKLVF58rlyc/NB8oyYp6F8fZHykekIC7Hy0ApT8mVROdDdcUyZoWBlQ5PyJTmzYNIYMfMJ6SZ4yAqj83PzceggMtgLjXBa0amEDAlp86zz6fN4CqgLeIBoCj5zRAvICiU5S2E9aa1p9nE9ab7T6Avk2aQK/SiyZXDwMixUCttz7fI1cmQLPNiNKFNpZAskCpQL1ArukVQKQ3GMC52xNAo4C01ypHgMCjipyXA0CxQLLdgjEN+IALBfiRkIpHgUCwwKnArFqL0pYaWseewLJKj8CvALUfJ8CkwK3AtCCiwLHApEOBqpxaiZcsqp7nW+8rwKMArUsYuhYgtKqFx4T3L589gL8Aqg8tpCyPIdcnT9DPLN8/E1rq1MCt/ZUXBmgqILdtkz5Vu1nHit8ioLDqizs7ILDvKD8yoKffN6tOoLggvL81oKbzQtc6qZ+a18mPjyNIWOmJJ4n/STsQlRgMTU6eW5yVDJaU5R0oTutd2QNe0aeZY8yynqmQUcNnhwib1ZqLl2eN64p1G2CwVYaKJAGHkhXnjGC8lRjgq+eRJFw1neCI8oo4i7KeE1vGAdIaXpGyhPCfqkkoVa8dexOqh/uBJAGXV9LX1Rv1TOWGxZfgq6yCf0VlnF87Co7lF+1Gjpk4lf6G01lBFheflwQBn2C6VZUXDACm004ImRC9RcXwnWClQp3yghC1BgVvJaChAKoOmZ8xcASpGJCgPyaDA+qOP4iQpXOY9zYOlwCqfz7fIVqVlT0gp4Co4TEguaC1BRkgtb8hoS4goyC+kKz3K6CzDZb6DzFbKRPNFZcBRQjJCA+RkKrApFC074s7S6cUUKpJGlC03ytAtXZTqRDQnicAEsNQqOqTkLhQu1C5yY/3hDcA0K1HiNcnXyzfKMkVy9TQsVC+ULNoCqCjqoD3IA6b7ZWPjJC2kK9QspCsDpK9kdC8kLOXMFCmUKcgqEMR0LsAs3csgxgwvdCiOhuQsYMIMLUAv5Cspg/QtVCqwLhmGtKWwKeAsxwBwKvAuiCtMKwgq0sFET0wvDC+TYIamTC3QKKegimFMKhQpT83MLswrKqSsLIgozC3bYPeml6KVyx+g96Ofo6wo6qBsKbeii2el1OwvzC8/YWwqn6bnplKB8ZL3p/QoF8qPprOibC88B6XQd6CkLJznHCnsLKdiHCjst7Qru2KPpNeknCtXpRmQokFcLB9ij6DzFcNi2NdcKdwq4+Yvphnll0U8L9wrbCyH4Lwob6TXYlwsvxM0LO/LYwqzSCaID0bbiWEQc068jB+hXXbPpmwuAwLdcLYRn6Y7sDE2/C4CLl+gMMRdyN+miASTyswvNc/wZHLROgV1ynwob8rMLXBmDc0yxKwrQipCLR9Ek8ysK4IpICuyYw3NnCgsLMIqDcvoLCwoL+LaorwqCKSgL5dHFCkgLaAqlCooKmQtoi60LXJlICsULdQuIiy3ZGAp1Co0LTLFxZPiLmIsTCwSLDQt6qYQLzJkfCnCKhAtEitiK4ilYi+IopIuHIE1y5QqICwnzkvFFCw+I2fItCkGN4+joxdiFNIsVQ48KjvFhAAnzrziFcE0KtIuMiuZxLIvPicyK2oAaAPSLLAoDCiyLHIpLxd0pdIvci6yLwOk3uFEIcfgGxPwL9znvSL/4AoucigXy5vL8i4n936SROTF4u/i47IILRwq5CjnzyoUYCfWRddie83yBqIrG8RHzOpHoiuwKofKYi7yKFHnCYSSKGKhKiziKVQsECs3y0fKEizHztuUKirKKH4mJ8nyZ+IpUeLHy5Iu4ikQ5yfNB+BULL4mp8xSKioq+qeQIonll0ZAKRoqSC5KKTqgQC7dzlakQuQgR4wqqi6Pz02D8i7dCjhEpBaaA6FB5IaKLGorUBAgDIhmLkbaKLgXnVfaLgMEOi7iLIwvKSNP5JwCB857zewpEOZQLfXKoi/iEw2iwiwaL3WheisiLQ2mSWRCK3orPMYwL8Iuei8BQAYs6ilIZtAsdcopYwlmBi8sLJzgPcnnyQwtZ8xwKLorH6WGKmfNmimqFgfPDchqFzAGoRT2hpgBQAFABLABsAIlykpHb6UABllAcAAAAlUEB3RGQAdABeAFAAE8jQFHZciWpmqjAAZwBGYrMAAABaZpoVAG5i7PYegB2AAAAvB0QzAHaKNgAk0nfCmyBcYu+ACABSYuUUpmKwliVqIWKRYqugKMRdAEliupYhgHEAJKKqQrEAKSxfgDEAVYB1+lgALWKdYs9CkAAjYrmAZWLk2TaARAgegBQAKYApgHikVqR+sJ6AZwBxAC4AMAAVgHb6Clz3REahHoAbAGIAMgBdsg5ihORGXNZC62K/RBr89WKe3MTYt4BTYv2kFAB3Qj1iqRQDYvYAS2LnABNi7WL0Asmi9OKiXKtiggBhYpti7tDqnAdip2KwABdi2zT2AHdiz2LvYvMAX2KxAH9ipYBDYrWADYAoAHb6N2KPYq9ihwArgGTEcmKI6AAAYUoAGmLUAGkABmLtGMKkQioVAo8SNmKOYr5iv2k2AD5ivWEBYsLikWKccjYAdWl3ws5ipZppYub0OWLQ4pHEYLYALGqcSOKCxHwMWoBvoHfCraBNYuzij0KYsn1ikYAW4uNihOK74s+qDOLT4sJo1YxIsHtix2LnYskk5XCa4q9in2KrgEbikAAA4qDikOKJ4rpMNSpSpBPi1eL0pHPip1Ar4qroYooX4ojC3OLZTDTii2L84szitBKaqgwS9+L4EsgIr+L6AB/i8uLK4rPIsQBAErrivwAG4vYAJuLlgFbi4gB24uxiqhKu4pWAHuKoRH7i9gAB4roAEeK6YvJc0OK8WlAuKMJZ4qgS+eKUQiXijwoP4vXi7ABN4tji5toV3O8APeKmoSES31IREpXiouKo4qgCS+KFEt8eLOKzYvvi1OLH4rzi5+Lb4vQS3WLTEoLirRKbNJISuaoy4r/ipkzuMOoS4BK/YrAS7wAIEtM6IRKWOA0St4AiErPinRKYPHSmaooPRDwSkABWXIfi90R34rCS1lzCEpsSz+Lu0PsS3+KK4v/izuLa4tcS0BLqEUYS9YBmEo7ithLa4s4SvuLmVnYAAABVYOKxAFpiseL5YuJ0Oe5OpFqS0sQxEvb6JeLdfj5i4+S/EviSllAVAA2gE60WTIb1cYBd4tli1RKoEsciDaLj4op0D+KnUDVioJLuoRvi1BQVgBQACsRQxGOAA4A75E5co0FMEpMS7BKzEsMSt+KcEo/irILv4rEABxKUkqcSgBL2EoyS+hL3EtiATxL9plDil6Zez06EcZL/EuaARBLdErYEvpLUEvMS/BLLEo2SqJK9kpiSghK9kueSg5LSEqOS5JKKEt0Y/JKgEvrikBLLkrxcr2g7ktqS/fYnko6S15LpkvTZWZKc4p+SyJKn4qtigFKfkriSkWKQUqSS8hLUkqhSmhLkgDoSkAAm4pqkRFKz0Ehvcu0PRGeSyZKL4vRSj5L44q+S8JKMEpxSqxLcEs5S2JKgUviS4lKyEscSmzTKEuri85KYUrcS+FLx4vb6EZLezwuLTRKVYrRSjWLPkp2S90QeUq2SvFL+UsBS42LgUoQBQ5L2AGOSiFLVFIlS9JKpUsyS5MQ6Uo2i83y/AomSlVLY4pCSgxKsUvNizVLokp1SglLBUqJSg1LQUqNS8FKyUrNS6FLaEthS6lKwEqtS4ZL7ksI5PsAlUoQSwJLVUo5S9VKU4tQULBL3UsTS3lL9kp9SklLRUsJo8VKQABcSi1K4UvDSuVLI0pe5fbz7UrjSx1LMUtfijVLjEr+S7ZKXUpiyQlLk2TsSkVKTkrFSyFLA0opSswAqUppShFKI0qRS8idGUvLSqZL40uDofFLXUtrS3FK+UrTSrVLM4v1SltKwUtJS05K0kqDSylKQ0t7S2VKzDAHSzrAtuRjS7RKR0srStVKG0s+qN1L/ko9S82Km0uISxJLW0pNS5kz80uDS6VKi0q3S+lKafJRS5VKK0veS+toE0uPSmtLk0s2S1NKf0qnSjNKF0r9SpdL20tNSvNLJUofSy1LaUv7Sl9LB9CHS5lKHUs/Szx4x0vPSoxK/0rrS7VKZ0svS2xLr0sXS7NLXYvJSi5LQ0plS6pL5UpZC8Fl2kvfSg9KUMsaWNDKZ0tPS+tLq0qAy+dL8MtAywjKq4sgy81LoMsLS2DK5UpKScOKqMuDoJDKP0t6Sr9KGMsAy9gAmMuwyqTLZ0uAy9jKQAGNSgNLuMtXS7tL10rDS/jLS3PlzeHy30tjS2jLxMtQy0JL0MpPSydL00vHSxtKvUubSxTLlMuXS4jKC0tIyp9L63J0ylrFYymHS1lLR0uMyxjKzMvkyizLdkr1SoVLM0pvSlTL70rXSx9KtMucyxD0EUBQQplLUUrEy7voq0osSidLMMqAyvzKsMrnSwLKQMqUy/1K7Ms7SkjKN0vIywTLktzLS0TKDMoSyo9KWMukynzKAMsqy+TK2MtPc4LLcstUyrtKe0s0yvtKBMvycKNKhSViymjKPMsPS79Laspky6dK5MtwyhJKGsoIyttKc0o7S5rL8srayzdLIstn6DcQ9Mv3SvrK6MrjiyTLBsuqys9KcMqsyq9Lxso4yybKiMryyhzKCsoPizrLEnTcy0rLVssMy+jKvMrkyobLnUtqy0bLhUomy29LnEqgysLKYMvay7TKTLyHGPdKAkrKymZKKsqSyjDL9pBTS7bKRst2yvDL9suyysDKpsogy0LL1MvCy77LqdAHS8LJlsoBy67LysoGykHLTMpSy8zKTMvSyhTKYctsy8DK70o+ypHKvsvmywqRzsuh8jHKXkviyoHKccu+S5LKwcv/SiHKnsqhysbLDUthyzjLc0sRy1rKyMrOylzK0ou6y9zKkEv6yjbLcct/StnKicrSy1jLMspsynLKycveynjLPsr4ylHKacpcyz7Q7UquyiXK1sqdS+XKqsvxy3zLCcoVy71KsstJy+HLycrVyynKNcupytqRacutGS7K4ssByjFLgcpZy0HKHAHBy5jLpcvNy6zKScuVy63LVcrUywXKnMq1yx80qRkQy13KscqZyqXLPcrxy2XLUsrNy9NL6sp5yq3Kjspmyk7K5ssKyp3LtBHpyllL9cpuy9bK7ss2yk3Kasr9y1PLFcsDyuHLM8oFyjTKhcuGSorLXQoh/ajL9Mtjy93Lmcq5S7FKtst9yhPKicrTy31LecsOyrjL68uRyh3LveEfNKJ4C8uQy4vLDcpTy35Lk8p2ygLKLcqVy2vLR8opysPKIsojyhER0cv+yhnK3cvZS+PLu8tZy73L2cr7yk/LLMpXygPL08qDyuvLN8oby8PLHcpcyhDKZ8sZyzvLj8oiS3vLZMs5y6/K9stvy9fL+cofy8fLyMpLSw9yPcR6y9vKi8uxyz/LuUu/y4bLf8usS1fKa8r5y6bKx8qpy3PKXMrGS/fLC8reSufLEsv7ypNKk8oJy5fKkCpvyofKM8o3y23Kt8s1ympL4Mt+qN/LD8oky0vLK8sXykgrIcr/y6HKACtQKhHLgCowK61K/IvzynArZ8pgKlgrCCuNy4grTctIKjLLkCu4KkfKgCuoKx/Lt8roKjaKo8sYKjvKj8rEKy/LE8rPyuXKF8ueyoLLXspCyvgr7ctAKgdLncvUK6Aq48q0Kr/Ly8o5y1gqDCstyu/KqCtDypQraCooyxb5dcpjyqwqP8psKuAq7CovygVLOCu5yigrnCoUK1wrH0uyStuK8ks7SwpKapG4SkAAKYoYAfhKqktDitdzxotZi9mLxEp5ixeKeYv3ysWKZADBCQ9KBkv3i4ZL0ipmi+1K8Ahjig3KCCu0KmXLdCqXyjgqyCsgI22K2IUoK8IqWsobyjxKiAGDirxKyipdC8AqBXEqKtWLNDFEKx7KI6CTiy+QiCoaK9grECpkK4uLC9FLisIq0CpMKxzKoityS1hLYiojoXuL4iuKSkAAAACkqABSK+mLyMpiCuTzMirnisMIdEO5ir/x6cvC6KMQ5PmKK5RLBkqpc8jKlyxUAecxlmMgKvwBp/nFiu1BPMvGK8Qq2CqkKpor5ip+K2hRp/kaylXKzksUKkArQ4uZCrIBaLE0MD+LfisKKodF8Co9yuorpiojoH3Kf8ocKrnLUSshKowqmsvQK0wq7kp9cuzzkSqQy6mEYwoxKrvLbCskKivKgStGyxKYIiChK4PKYSoiKzJLuit6K25Km8qnirDy33PtSmkrYyjGKo3LgSqZKrEqq8uVSmIA2SuJK6EqV0s6KuEqm8oBLc4rvirPikUqKdDFKhfKHsvFKlkrgEjlKg7K3ss5KpUr+CuGSrQgJ4UpK4QrNSoBK8UrdSv0KrnLWSu4w9oqVithKs0q5Up7QI7YrSrbyqOKbSslyvwqe8oCKvErmSsdKg0rnSuWK3gq3SrJK80rxCTVK4Ur+FFpK7UrvMqDKhAr8SuCKp0rlcJdKyMquSujKj0rFETjK6kqEytFK6wrASqlKiUr7CpDK9MqwyszKiMqbcpzKxzLlCriwa0p73OtKosqtSpLKu0r4CtLKoIrmirPi2UrwysAK10r6ytOy4ZK5sGaxb0qRMtRSv0qaisxKhkqZipBKuYr7Uv7KmsrByuzK00rcyoNScXMCyqnKtsrbSp1Krsq9StDK5cr2SvvyqMqGyvcK1vAUQgnK9UqXkunKukrYCsDKxkqKyrLK/UrjyvlKjkrFStmyxvLi0rJabcrlUrvKpMr7soPKh0qqyrfKo0rjCrPKkcq5UvQQR45ryvjKtWLiyt8K7sr/CqfKwIrdUt7Kl5KwKuHy40rPyuzy78qRzF68P8qEEoAqjsr9ypTK5CrPUtAqtWKVyp4Kusr1yvPKifKaQCu2QUqqSp3KhCr2yqQqzsryKsPKqiqL4poq+QqhyvoqqCqRzHGoIirfSt3K/0qKKtPynErz8uDKl8qjyuoqk8qXCqEqnPLQ4pBYAowhSsLK9iq9yuTK1Cq5Kp7KsEq+ysUq98rTyuHK1Srhkr+8MSqNSokqmcr6SpQq+crJSoMqpcrjKvAqkkrViuEq68xJwGbK285Wyu0qySquKr0q1MrKyowqjMqlKo6Kr8qn8uPoGMgrKtvKmyr7yoDK6SrpMtkqoKr5Kt4qw0rsKogqsyr8KvDEWly4Kq0qi+LEKs0KqSqvcpkqvQrpCucqviqwqsEqiKrGyoZcjSrWKv/KuKrAKrLywKriqv8ykKrqyqqqtcqaqvcK6LLPWhbKn0rrKr8q2yqHysSq6EQyqtBKiqr0qqzKuiqeqony9iYYqrpYYar4qraq+orSqsaKxcqkMqwqmaqQ8pUq7KrQFC+8lirfKoKqjiqiqoCqhyrnyqcqraqXKoyqtyrIKvMquVKziqOqwarYquWq5qqI6HmSxZLVgDAAFZKllDmiwvRAyVeccsq0KsoqjqrtqtrK3aq5qvIyt1B6quOqulgdKqAq7iqQKrBqm6qdqpNKqGrQ4v4CxarrCDeq0irdKouqkGqL0oUqyqqTKuUqjGrhkqDEbGqSKs4qsirWqp4qlGqSatcqhUr7Mt4yhiroatcCKmqmqrxqxGq6auRqwyrMKtRqiGr0arwqyKq7+X6qnyqXqqWqk6qEapaqgmr9KvQq/mrQqtJq8KqRasbKrZI7DDyqtirpav8q2mq5apSqq6rUUvBq1crZqtVq3qqKSvPRBqriKq5qmmr8avWq2Yq0yoZq6aqhatwq1mqPKsnitSpX6Cr8eCrtapGqhKqSqqSqiarNqsNqwWrjashq02qJ8o6Aa0oLarhqxMruatlqu2qFyodqxWrOquVq6qrw6vIyi0qpHmjqyWqcap9qlarzqoTqxyqFaqmqgcraKrDq12qHqvSkT0rPWmzqycrGqtxqm2qear1q1ar/cqjio2qy6uFqiur9qtVMWMrspFrqm8qpavhqnWrbaoDqjaqk6pLq/iqcKpZq9XK2atDi2TAKnE9qy2rxKobqs6rdasLqy6ri6uuqxmrbquZq47Ku6siqt5Ao6vKVGOrCquYKluqJCubq+mrk6vbqgSruqvTq0OKxyqzq4+qc6upq1eqR6vGqsergquvqkOqO6pdqmeq3auaAT7AazEXqk+rTqrPqgurR6vtqr+qJ6q6qk2r96sbKy8rOpH7q72qh6t9q8+rgavlq0Grv6u3qtGq/6rty2eqI0t/Kvurn6rrqq2qV6vAaterIGsTq6Bqt6qdq0OrO6v/qyuq40nxA+XRkGvyq1Br86soaj+qoGtSqx2rS6tvquBrGGu7q1ZBL4xAal+rrarfqpur16sJqq/K+GsnqzKq9qsiqpirtcTEa0hrl6rzq96qgSvtK8qraGv4aqeq96qEapRrRKuIa86c1GqGqjRq46tYK7RrJqt0a+Rq7qqyqyKr1KtYakhqB6tzqjhrNGrLK6xqg6plKn+qBGvLqwxrGyssqkxqvavYa2OrG6vjqqhqi6qwamBrU6rvq+Br3Cp0cI+rTGtca1+qKGvfq3Er9as3q4OqcGudq6er8GoAaqKqXArYarWr3GssarRrgKp0a7Jq6Gt/qvJqaCvmq3KrimvrqixrwmqsaipqbGqqavRqFGvJquVK6quca5JqUGrCayRqImu4a6hreGuwa6pq/GoYa/JqmGpg3JBFVGpSaiRq0mqkayJqN6uia2xrYGv8a6ZrhGoWq4Jql6vMa0pqWmvKapGrKmp8anJr6GrwaupryMogmJJqQmpKawZqlmuGajJr0GtfK3xr9Gqzy+JqJ8qeq+ZqBmtPqozL0Gq8a8er1mtiawRqtmsiqmGq+mtuappqDmqGa1prjmvaa05qJmrea0kqCGrlSrGrdmtAamWrYWt5qk5qEEpvqpFr3KpmaqJkbmr2a16rmmphao5rsWvha3FrXmq6a++qKao5q9FrxGvIav5qIGpGaqJqiarSqzpr7GsUatWrM8QhaklrB6vuallquGqeaq+qYmqZqj8ramrcKiOqLln5ajFrh6uWatlrVmo5auRqNmqmasPL1is2ATYrmsriKr2gEioHizQBjisESiyqvKo5CplYsiqaSnN04QmwATmLQGHpy1ewIfC6SoVqfHili54rSirlSxJqzWvFyvArgktqK5ZK75ABamhrq8rkK/Fr7qvASnorIEo9a05gvWr1yn1rmlkUSuyrHysvqvmricpDa2lr96s1alhK/AEVK3VqyYr2KimKFACNa9wqdmo5cxpLzAAkS3mK8ipeqgoqJYqeK2IAVEteKoRKAfKVipDKqitGKsprPGraa7xqFirti4FrNmsFynkrI2tUUQ6qW2tRSttrMWqNS5OKL6ukazBqVWv5qxAgS4rVai5rH8szamIqdWu2KrhK9ipKS/JgKktHik4qhEvZc4doGkota8tqphkra8QF98p3MGQBKzlVSkoqhkp6aw9rIWGEK9/LyWuVqBZKlkp+q1ZL/qtkKbEqVmpka9qr52sMKiVrTKr2qwdq+ip6akdrnCTmqb1q2UoearFrk2pxa//LQivOaqVrlSog6kc5oZgFakQqO2rnKmdrMmrWa4NrkOpqagxrQWtqqyDqY5GfapgrhWvSa5Krnmq5yl7LgOrJqulr0Opa7IkBKOo0KuDqKWoQ6qlqkOqzSyZrl2rQ64dqRzluedjqfCtfa3Dq/2tna2RrAOqcKlDqSOsuaoRKR2og6UTq42sOaztq4Wu7a3jql2tQ690qhOpSgxT4c6pfazjr1OspazTquCqI6/jqdOo3KnKrFYuU6wzqqOtuy/5qu2sBawjq+OtDahxraqsPa1j4VOtg66jrFWtFalNrB8rc69NqAmuLa9lzXEh86idqTOu46szqQiuC67lrumppcxWKQtUi6hVrHmto6sVqguu06+TrpWqua8LrQg1S6tBrWWoC6xDrzOvi63er3mtC6+prkutcoQrrOGpo6wOqXOtkKizr3Op5asLrFYpNsbnwYOqi6iTqlWv/agfLXOuy6yrrSOuLakdrOurq6jxreupK6njqyuqG65FqGEpbinJKtWuza8lLc2qZWASJEiqOK3dqBEt6q7nzUYuOAE9q/AAra3Ir+YurakIg/isZS8rK72sbaiNK9utKhHOrx2rS6+Dq8Oro64IqF2sWKubqCWvDa3kr8XjuS5AK4YrMal5LHuqK6zlLJiqtEX9q+uqk6gDqM0taKpYq5OuG6jVrFuuiK7VrqErW6hIr9ivKS9gBKkv3aiNKfXJt813yDusuK9yJK2tuKy9rc4AeKykB3wsUS67rkgE+a8UJAkitiffLCSv+KvRL58oa6z+qxmpRKiEqWesY6lWqPmreKjap2gEZ6l6rmevRK31rZyvsql7rMuviS0XqKdFwaqzqUWrY833yB/PlatbKE2uPyz6qP2t+qukKymEBq9pwMGvw6udrxWp3qyVqcuulSsDq+SoEygUr8epg8H5qwGqgSNnr/Ooy6wLqOmrsairr5usJa7+AGgBd823rQmt+al1raiqm653rSuoFqs5riOoR63Lq56sqaH3rVeuLy9Xq/ap0KqXqXeoRarlr3eq+6yKrq6pj6plqyWodEePqnOo06prrqWrD6yzqzet0660xe6pt62PrxesTasarputi6pWreerTq/nq56vzKtPzXfLt6qnr9EuK64PqZutD6xFqQupG6ifLD6qz6wHrBWv96lpYJeqTapPqQ+ob6k3qQOsS6v0RH6pH6hZrmWoD6yfra+p76+vqU6sb6uJqquvIyoBrAMh789vq/evt6tfqa+v9qyHrDeuk643r5etL66zqszFxZZfqO+tZ6wP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aPTga2mGA/No/i57aXahK22or3tu3Gz7bBdhMIW7agpvu2grbfFrDK17bINtOW9baFVuu26Haftth2j3z/tt4cBHb+yqR24zrwdrDmyHaEytUC6baV2v3W5DaV5oeW1zaHAAHiwtq+lqk22EBfEouK29bz2uXixTakwjPmizaL5scABtrZloFm9TbRcsHWssbHVsKW51bi1qXWzRadloxmubbuLFM2iTYvMXVW+1b6Fo7Wwtb61sXWwzaENtWAJDbl5oKSmnb1utJWs9aJNspW/ha2gBpWr5bhFvpWvnRL2qZWgfQCUFZWqLaQlrN2kqRPVqrW5Na/WvfamGav2vRin9qytvnW+jrJdq120Dq9tp0W7GaAeuV26tah1rV2utbt1qD22Nas1uM87WbQNvfWrTbZVu/WvTbA9rHWhPbmZrEsbGaMisj2j3bUto62pGb49t3W6Oax5vY28zbJus3WgPa3urL2uzasdst8ZPaLdvd2mjaZ5us2rPbNdpz29OaxLF0mqva09q/WsXaf1q72gzae9t+2mOax4gL26vbvVtNW8XbR1u728vavxqeqtVaZ9o3WufaR9vr27Pal9s7myfbBvmlWwfaa9o32zPat9sX2xvauxq7mj6a11rb25Zba9vV2uPbt9vP2ivbdJoHW9dalZuP2zvbT9rH2nfaCmrHm1/ab9vT24faT9owqndan9uX28Pb8nBnWyzaJtvDmx/b2JtYWyna9du7ig3aEipKSjzaTdovW68xrtqPay3bUFoUUBlbbduxRFla62r52q+b2Vosq7A6n2rf2nlavdr5WuGbCBARmkvbhVrgO5paQ9pl27RbAmslWt6AoDuL2y7bOtt1WktbxxsKavXNCCUL29vbn5uAOmTqz9vgOuNapJBgtI7axDtv2j/bytps2lrqf9s96uQ6aLTdmw/bZ9to2lQ7R9ts2mQ7E9uEOy2bFDsAOyNb59o127/awDt324NbXZt1m3rKVduj2uVbY9oq2/1bydov2+NafnSa2lbKnDtK2mA7eNtAOow7c9rvGr7a45oAOofaLDs32kA6G9uCO3vbQjuC1NmaIjqP2vQ669piOlg7ehpCOrw7Q+Spmtfb39tSO+/a3DqCO1g6hDrsOqDqeDva2vg7S9oyOpkaJ9s0OsjEHDrDWovbKjo+2/g73Dp22+I6TDpyOrDrrZucOjP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pi/6poHr6gLe7CHFvusQBTRDAe8u6xAEru6u7a7t4emB627vge76B+7qQe/R6UHuaUUe7x7swe7Za+EtoSkQBEQD/ug2wAAHoAAB0KxFtUpx6seAOAam6QADfu7mK+AC/upGolHvYAQtraEoYAUu6qzo0eqB7tHpbu3R7O7qMe3u6YnqHukx60HrMeye6sHrEAYeK8HsIexEBiHtIe9gByHtwQKgAqHpoeuh78nr+AS0QwADYejh7C4u4euu6BHqEe6e6Z7pAAKgA1HorOyB6tHobuyJ64HuievgADHuQewe7UHvgAdB6J7qnu1J797spS6x6/7sokRx7nHqBAcQBXHqBAKYAoQFfuxu737rzAXx6dSH8ekAA6gHriw+6xAGPu0+767tXunu6N7t8gLe6d7rwAbZbAQC2eyEAj7sXuw4Az7qEAC+7jnqvu057znpweylLwHvUelp7oHvaehEA9Hq6exB6envieosBTHowe5J7znpEeylKbrpKSpu6AAHl3RAHu4gBWHrAAZvQvYvgAMgA0AALAXp6Env6epJ6hnvYAP0AS7qaexIrPnoie2B6fns6e7p64nr6egZ7zHunuyx63nsJesJ7Wnp0ejp6EHtiev57MXqBexJ6QXtxejZ7AntGeksA/7uoASZ6seEQAWZ6noEWe5Z727rXuvx6znp6AWQACXogezR6vntJe9u7yXv+eyl6sXupe0F65Xvqexp7FXvCetp6VXt+eil72XsBe3+6uXsGelJ72AEDAS5757pue5e79nvPuw57L7sHu5565XvvuiF6KxHLEFABsAGhekpKhHrEAAe79irGu/4g97tAeylLtnp4S+167noeepgATnpvu2V6xAEfAeuKxnqFesV7RXqme8V6yHqWe7mKpXpleve7wXu7S1N6KAGFeoEAM3qx4AsAJXtze1Z7aQHWesEAS7ooAcR6j7qkemR7yxDke8YAFHorIOt66XqLegV7bIFLesgBy3pk5Kt7AQBreqgA63r5e7tL3nuaepV6SXqie1l7DHtNeql6cXqtekAB4QAbept6dnpbe2R7tHvke4x7XIHWe/WBbXuuek+7bnsde+57nXsee116E3u2W3UB64uCe0J7iXsNe+d6YnsXezt7OXuxe7l7V3rqgeuLEQGheoK7YXrEAaF6/7tYete6O7pQAdwB4ABlQfu6LgBoAP4Abrr/u8u74Ut9ijXCsbo823G6YFpxu2m6B4qJuym6B4tJug6FybpZu4W76dpZu7D7Fbs9uoeKWbvlugeL2bqju+27ubqyAT26BbpZugbD+btFu4W7xbvFiyW6elpxu+W6C2rNEfj7FbqduqmKVbqtuqmL1buY+7W7KbopigE7XbvtuimKjbsI+nqacbpbcc26ATq6la26PNtCpc27HbuFuslacbvk+pm6ykrNELoBfbu9uz26/bpxu5fpo7pVu+j7w7oBOuO77bscWnG6w7u7S5O7xYtTu0BLUPqDuvq7Q7oYEvGKkpHgAba7xAHpi3GKCAFQAHu73gCAS0lyzgHNEDthxgA9oIAAAA="))
|
|
///////////////////////////////////////////////
|
|
|
|
///////////////////////////////////////////////
|
|
/* Utility functions */
|
|
|
|
var storagePrefix = 'KiCad_HTML_BOM__' + pcbdata.metadata.title + '__' +
|
|
pcbdata.metadata.revision + '__#';
|
|
var storage;
|
|
|
|
function initStorage(key) {
|
|
try {
|
|
window.localStorage.getItem("blank");
|
|
storage = window.localStorage;
|
|
} catch (e) {
|
|
// localStorage not available
|
|
}
|
|
if (!storage) {
|
|
try {
|
|
window.sessionStorage.getItem("blank");
|
|
storage = window.sessionStorage;
|
|
} catch (e) {
|
|
// sessionStorage also not available
|
|
}
|
|
}
|
|
}
|
|
|
|
function readStorage(key) {
|
|
if (storage) {
|
|
return storage.getItem(storagePrefix + key);
|
|
} else {
|
|
return null;
|
|
}
|
|
}
|
|
|
|
function writeStorage(key, value) {
|
|
if (storage) {
|
|
storage.setItem(storagePrefix + key, value);
|
|
}
|
|
}
|
|
|
|
function fancyDblClickHandler(el, onsingle, ondouble) {
|
|
return function () {
|
|
if (el.getAttribute("data-dblclick") == null) {
|
|
el.setAttribute("data-dblclick", 1);
|
|
setTimeout(function () {
|
|
if (el.getAttribute("data-dblclick") == 1) {
|
|
onsingle();
|
|
}
|
|
el.removeAttribute("data-dblclick");
|
|
}, 200);
|
|
} else {
|
|
el.removeAttribute("data-dblclick");
|
|
ondouble();
|
|
}
|
|
}
|
|
}
|
|
|
|
function smoothScrollToRow(rowid) {
|
|
document.getElementById(rowid).scrollIntoView({
|
|
behavior: "smooth",
|
|
block: "center",
|
|
inline: "nearest"
|
|
});
|
|
}
|
|
|
|
function focusInputField(input) {
|
|
input.scrollIntoView(false);
|
|
input.focus();
|
|
input.select();
|
|
}
|
|
|
|
function saveBomTable(output) {
|
|
var text = '';
|
|
for (var node of bomhead.childNodes[0].childNodes) {
|
|
if (node.firstChild) {
|
|
var name = node.firstChild.nodeValue ?? "";
|
|
text += (output == 'csv' ? `"${name}"` : name);
|
|
}
|
|
if (node != bomhead.childNodes[0].lastChild) {
|
|
text += (output == 'csv' ? ',' : '\t');
|
|
}
|
|
}
|
|
text += '\n';
|
|
for (var row of bombody.childNodes) {
|
|
for (var cell of row.childNodes) {
|
|
let val = '';
|
|
for (var node of cell.childNodes) {
|
|
if (node.nodeName == "INPUT") {
|
|
if (node.checked) {
|
|
val += '✓';
|
|
}
|
|
} else if ((node.nodeName == "MARK") || (node.nodeName == "A")) {
|
|
val += node.firstChild.nodeValue;
|
|
} else {
|
|
val += node.nodeValue;
|
|
}
|
|
}
|
|
if (output == 'csv') {
|
|
val = val.replace(/\"/g, '\"\"'); // pair of double-quote characters
|
|
if (isNumeric(val)) {
|
|
val = +val; // use number
|
|
} else {
|
|
val = `"${val}"`; // enclosed within double-quote
|
|
}
|
|
}
|
|
text += val;
|
|
if (cell != row.lastChild) {
|
|
text += (output == 'csv' ? ',' : '\t');
|
|
}
|
|
}
|
|
text += '\n';
|
|
}
|
|
|
|
if (output != 'clipboard') {
|
|
// To file: csv or txt
|
|
var blob = new Blob([text], {
|
|
type: `text/${output}`
|
|
});
|
|
saveFile(`${pcbdata.metadata.title}.${output}`, blob);
|
|
} else {
|
|
// To clipboard
|
|
var textArea = document.createElement("textarea");
|
|
textArea.classList.add('clipboard-temp');
|
|
textArea.value = text;
|
|
|
|
document.body.appendChild(textArea);
|
|
textArea.focus();
|
|
textArea.select();
|
|
|
|
try {
|
|
if (document.execCommand('copy')) {
|
|
console.log('Bom copied to clipboard.');
|
|
}
|
|
} catch (err) {
|
|
console.log('Can not copy to clipboard.');
|
|
}
|
|
|
|
document.body.removeChild(textArea);
|
|
}
|
|
}
|
|
|
|
function isNumeric(str) {
|
|
/* https://stackoverflow.com/a/175787 */
|
|
return (typeof str != "string" ? false : !isNaN(str) && !isNaN(parseFloat(str)));
|
|
}
|
|
|
|
function removeGutterNode(node) {
|
|
for (var i = 0; i < node.childNodes.length; i++) {
|
|
if (node.childNodes[i].classList &&
|
|
node.childNodes[i].classList.contains("gutter")) {
|
|
node.removeChild(node.childNodes[i]);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
function cleanGutters() {
|
|
removeGutterNode(document.getElementById("bot"));
|
|
removeGutterNode(document.getElementById("canvasdiv"));
|
|
}
|
|
|
|
var units = {
|
|
prefixes: {
|
|
giga: ["G", "g", "giga", "Giga", "GIGA"],
|
|
mega: ["M", "mega", "Mega", "MEGA"],
|
|
kilo: ["K", "k", "kilo", "Kilo", "KILO"],
|
|
milli: ["m", "milli", "Milli", "MILLI"],
|
|
micro: ["U", "u", "micro", "Micro", "MICRO", "μ", "µ"], // different utf8 μ
|
|
nano: ["N", "n", "nano", "Nano", "NANO"],
|
|
pico: ["P", "p", "pico", "Pico", "PICO"],
|
|
},
|
|
unitsShort: ["R", "r", "Ω", "F", "f", "H", "h"],
|
|
unitsLong: [
|
|
"OHM", "Ohm", "ohm", "ohms",
|
|
"FARAD", "Farad", "farad",
|
|
"HENRY", "Henry", "henry"
|
|
],
|
|
getMultiplier: function (s) {
|
|
if (this.prefixes.giga.includes(s)) return 1e9;
|
|
if (this.prefixes.mega.includes(s)) return 1e6;
|
|
if (this.prefixes.kilo.includes(s)) return 1e3;
|
|
if (this.prefixes.milli.includes(s)) return 1e-3;
|
|
if (this.prefixes.micro.includes(s)) return 1e-6;
|
|
if (this.prefixes.nano.includes(s)) return 1e-9;
|
|
if (this.prefixes.pico.includes(s)) return 1e-12;
|
|
return 1;
|
|
},
|
|
valueRegex: null,
|
|
valueAltRegex: null,
|
|
}
|
|
|
|
function initUtils() {
|
|
var allPrefixes = units.prefixes.giga
|
|
.concat(units.prefixes.mega)
|
|
.concat(units.prefixes.kilo)
|
|
.concat(units.prefixes.milli)
|
|
.concat(units.prefixes.micro)
|
|
.concat(units.prefixes.nano)
|
|
.concat(units.prefixes.pico);
|
|
var allUnits = units.unitsShort.concat(units.unitsLong);
|
|
units.valueRegex = new RegExp("^([0-9\.]+)" +
|
|
"\\s*(" + allPrefixes.join("|") + ")?" +
|
|
"(" + allUnits.join("|") + ")?" +
|
|
"(\\b.*)?$", "");
|
|
units.valueAltRegex = new RegExp("^([0-9]*)" +
|
|
"(" + units.unitsShort.join("|") + ")?" +
|
|
"([GgMmKkUuNnPp])?" +
|
|
"([0-9]*)" +
|
|
"(\\b.*)?$", "");
|
|
if (config.fields.includes("Value")) {
|
|
var index = config.fields.indexOf("Value");
|
|
pcbdata.bom["parsedValues"] = {};
|
|
var allList = getBomListByLayer('FB').flat();
|
|
for (var id in pcbdata.bom.fields) {
|
|
var ref_key = allList.find(item => item[1] == Number(id)) || [];
|
|
pcbdata.bom.parsedValues[id] = parseValue(pcbdata.bom.fields[id][index], ref_key[0] || '');
|
|
}
|
|
}
|
|
}
|
|
|
|
function parseValue(val, ref) {
|
|
var inferUnit = (unit, ref) => {
|
|
if (unit) {
|
|
unit = unit.toLowerCase();
|
|
if (unit == 'Ω' || unit == "ohm" || unit == "ohms") {
|
|
unit = 'r';
|
|
}
|
|
return unit[0];
|
|
}
|
|
|
|
var resarr = /^([a-z]+)\d+$/i.exec(ref);
|
|
switch (Array.isArray(resarr) && resarr[1].toLowerCase()) {
|
|
case "c": return 'f';
|
|
case "l": return 'h';
|
|
case "r":
|
|
case "rv": return 'r';
|
|
}
|
|
return null;
|
|
};
|
|
val = val.replace(/,/g, "");
|
|
var match = units.valueRegex.exec(val);
|
|
if (Array.isArray(match)) {
|
|
var unit = inferUnit(match[3], ref);
|
|
var val_i = parseFloat(match[1]);
|
|
if (!unit) return null;
|
|
if (match[2]) {
|
|
val_i = val_i * units.getMultiplier(match[2]);
|
|
}
|
|
return {
|
|
val: val_i,
|
|
unit: unit,
|
|
extra: match[4],
|
|
}
|
|
}
|
|
|
|
match = units.valueAltRegex.exec(val);
|
|
if (Array.isArray(match) && (match[1] || match[4])) {
|
|
var unit = inferUnit(match[2], ref);
|
|
var val_i = parseFloat(match[1] + "." + match[4]);
|
|
if (!unit) return null;
|
|
if (match[3]) {
|
|
val_i = val_i * units.getMultiplier(match[3]);
|
|
}
|
|
return {
|
|
val: val_i,
|
|
unit: unit,
|
|
extra: match[5],
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
|
|
function valueCompare(a, b, stra, strb) {
|
|
if (a === null && b === null) {
|
|
// Failed to parse both values, compare them as strings.
|
|
if (stra != strb) return stra > strb ? 1 : -1;
|
|
else return 0;
|
|
} else if (a === null) {
|
|
return 1;
|
|
} else if (b === null) {
|
|
return -1;
|
|
} else {
|
|
if (a.unit != b.unit) return a.unit > b.unit ? 1 : -1;
|
|
else if (a.val != b.val) return a.val > b.val ? 1 : -1;
|
|
else if (a.extra != b.extra) return a.extra > b.extra ? 1 : -1;
|
|
else return 0;
|
|
}
|
|
}
|
|
|
|
function validateSaveImgDimension(element) {
|
|
var valid = false;
|
|
var intValue = 0;
|
|
if (/^[1-9]\d*$/.test(element.value)) {
|
|
intValue = parseInt(element.value);
|
|
if (intValue <= 16000) {
|
|
valid = true;
|
|
}
|
|
}
|
|
if (valid) {
|
|
element.classList.remove("invalid");
|
|
} else {
|
|
element.classList.add("invalid");
|
|
}
|
|
return intValue;
|
|
}
|
|
|
|
function saveImage(layer) {
|
|
var width = validateSaveImgDimension(document.getElementById("render-save-width"));
|
|
var height = validateSaveImgDimension(document.getElementById("render-save-height"));
|
|
var bgcolor = null;
|
|
if (!document.getElementById("render-save-transparent").checked) {
|
|
var style = getComputedStyle(topmostdiv);
|
|
bgcolor = style.getPropertyValue("background-color");
|
|
}
|
|
if (!width || !height) return;
|
|
|
|
// Prepare image
|
|
var canvas = document.createElement("canvas");
|
|
var layerdict = {
|
|
transform: {
|
|
x: 0,
|
|
y: 0,
|
|
s: 1,
|
|
panx: 0,
|
|
pany: 0,
|
|
zoom: 1,
|
|
},
|
|
bg: canvas,
|
|
fab: canvas,
|
|
silk: canvas,
|
|
highlight: canvas,
|
|
layer: layer,
|
|
}
|
|
// Do the rendering
|
|
recalcLayerScale(layerdict, width, height);
|
|
prepareLayer(layerdict);
|
|
clearCanvas(canvas, bgcolor);
|
|
drawBackground(layerdict, false);
|
|
drawHighlightsOnLayer(layerdict, false);
|
|
|
|
// Save image
|
|
var imgdata = canvas.toDataURL("image/png");
|
|
|
|
var filename = pcbdata.metadata.title;
|
|
if (pcbdata.metadata.revision) {
|
|
filename += `.${pcbdata.metadata.revision}`;
|
|
}
|
|
filename += `.${layer}.png`;
|
|
saveFile(filename, dataURLtoBlob(imgdata));
|
|
}
|
|
|
|
function saveSettings() {
|
|
var data = {
|
|
type: "InteractiveHtmlBom settings",
|
|
version: 1,
|
|
pcbmetadata: pcbdata.metadata,
|
|
settings: settings,
|
|
}
|
|
var blob = new Blob([JSON.stringify(data, null, 4)], {
|
|
type: "application/json"
|
|
});
|
|
saveFile(`${pcbdata.metadata.title}.settings.json`, blob);
|
|
}
|
|
|
|
function loadSettings() {
|
|
var input = document.createElement("input");
|
|
input.type = "file";
|
|
input.accept = ".settings.json";
|
|
input.onchange = function (e) {
|
|
var file = e.target.files[0];
|
|
var reader = new FileReader();
|
|
reader.onload = readerEvent => {
|
|
var content = readerEvent.target.result;
|
|
var newSettings;
|
|
try {
|
|
newSettings = JSON.parse(content);
|
|
} catch (e) {
|
|
alert("Selected file is not InteractiveHtmlBom settings file.");
|
|
return;
|
|
}
|
|
if (newSettings.type != "InteractiveHtmlBom settings") {
|
|
alert("Selected file is not InteractiveHtmlBom settings file.");
|
|
return;
|
|
}
|
|
var metadataMatches = newSettings.hasOwnProperty("pcbmetadata");
|
|
if (metadataMatches) {
|
|
for (var k in pcbdata.metadata) {
|
|
if (!newSettings.pcbmetadata.hasOwnProperty(k) || newSettings.pcbmetadata[k] != pcbdata.metadata[k]) {
|
|
metadataMatches = false;
|
|
}
|
|
}
|
|
}
|
|
if (!metadataMatches) {
|
|
var currentMetadata = JSON.stringify(pcbdata.metadata, null, 4);
|
|
var fileMetadata = JSON.stringify(newSettings.pcbmetadata, null, 4);
|
|
if (!confirm(
|
|
`Settins file metadata does not match current metadata.\n\n` +
|
|
`Page metadata:\n${currentMetadata}\n\n` +
|
|
`Settings file metadata:\n${fileMetadata}\n\n` +
|
|
`Press OK if you would like to import settings anyway.`)) {
|
|
return;
|
|
}
|
|
}
|
|
overwriteSettings(newSettings.settings);
|
|
}
|
|
reader.readAsText(file, 'UTF-8');
|
|
}
|
|
input.click();
|
|
}
|
|
|
|
function resetSettings() {
|
|
if (!confirm(
|
|
`This will reset all checkbox states and other settings.\n\n` +
|
|
`Press OK if you want to continue.`)) {
|
|
return;
|
|
}
|
|
if (storage) {
|
|
var keys = [];
|
|
for (var i = 0; i < storage.length; i++) {
|
|
var key = storage.key(i);
|
|
if (key.startsWith(storagePrefix)) keys.push(key);
|
|
}
|
|
for (var key of keys) storage.removeItem(key);
|
|
}
|
|
location.reload();
|
|
}
|
|
|
|
function overwriteSettings(newSettings) {
|
|
initDone = false;
|
|
Object.assign(settings, newSettings);
|
|
writeStorage("bomlayout", settings.bomlayout);
|
|
writeStorage("bommode", settings.bommode);
|
|
writeStorage("canvaslayout", settings.canvaslayout);
|
|
writeStorage("bomCheckboxes", settings.checkboxes.join(","));
|
|
document.getElementById("bomCheckboxes").value = settings.checkboxes.join(",");
|
|
for (var checkbox of settings.checkboxes) {
|
|
writeStorage("checkbox_" + checkbox, settings.checkboxStoredRefs[checkbox]);
|
|
}
|
|
writeStorage("markWhenChecked", settings.markWhenChecked);
|
|
padsVisible(settings.renderPads);
|
|
document.getElementById("padsCheckbox").checked = settings.renderPads;
|
|
fabricationVisible(settings.renderFabrication);
|
|
document.getElementById("fabricationCheckbox").checked = settings.renderFabrication;
|
|
silkscreenVisible(settings.renderSilkscreen);
|
|
document.getElementById("silkscreenCheckbox").checked = settings.renderSilkscreen;
|
|
referencesVisible(settings.renderReferences);
|
|
document.getElementById("referencesCheckbox").checked = settings.renderReferences;
|
|
valuesVisible(settings.renderValues);
|
|
document.getElementById("valuesCheckbox").checked = settings.renderValues;
|
|
tracksVisible(settings.renderTracks);
|
|
document.getElementById("tracksCheckbox").checked = settings.renderTracks;
|
|
zonesVisible(settings.renderZones);
|
|
document.getElementById("zonesCheckbox").checked = settings.renderZones;
|
|
dnpOutline(settings.renderDnpOutline);
|
|
document.getElementById("dnpOutlineCheckbox").checked = settings.renderDnpOutline;
|
|
setRedrawOnDrag(settings.redrawOnDrag);
|
|
document.getElementById("dragCheckbox").checked = settings.redrawOnDrag;
|
|
setHighlightRowOnClick(settings.highlightRowOnClick);
|
|
document.getElementById("highlightRowOnClickCheckbox").checked = settings.highlightRowOnClick;
|
|
setDarkMode(settings.darkMode);
|
|
document.getElementById("darkmodeCheckbox").checked = settings.darkMode;
|
|
setHighlightPin1(settings.highlightpin1);
|
|
document.forms.highlightpin1.highlightpin1.value = settings.highlightpin1;
|
|
writeStorage("boardRotation", settings.boardRotation);
|
|
document.getElementById("boardRotation").value = settings.boardRotation / 5;
|
|
document.getElementById("rotationDegree").textContent = settings.boardRotation;
|
|
setOffsetBackRotation(settings.offsetBackRotation);
|
|
document.getElementById("offsetBackRotationCheckbox").checked = settings.offsetBackRotation;
|
|
initDone = true;
|
|
prepCheckboxes();
|
|
changeBomLayout(settings.bomlayout);
|
|
}
|
|
|
|
function saveFile(filename, blob) {
|
|
var link = document.createElement("a");
|
|
var objurl = URL.createObjectURL(blob);
|
|
link.download = filename;
|
|
link.href = objurl;
|
|
link.click();
|
|
}
|
|
|
|
function dataURLtoBlob(dataurl) {
|
|
var arr = dataurl.split(','),
|
|
mime = arr[0].match(/:(.*?);/)[1],
|
|
bstr = atob(arr[1]),
|
|
n = bstr.length,
|
|
u8arr = new Uint8Array(n);
|
|
while (n--) {
|
|
u8arr[n] = bstr.charCodeAt(n);
|
|
}
|
|
return new Blob([u8arr], {
|
|
type: mime
|
|
});
|
|
}
|
|
|
|
var settings = {
|
|
canvaslayout: "FB",
|
|
bomlayout: "left-right",
|
|
bommode: "grouped",
|
|
checkboxes: [],
|
|
checkboxStoredRefs: {},
|
|
darkMode: false,
|
|
highlightpin1: "none",
|
|
redrawOnDrag: true,
|
|
boardRotation: 0,
|
|
offsetBackRotation: false,
|
|
renderPads: true,
|
|
renderReferences: true,
|
|
renderValues: true,
|
|
renderSilkscreen: true,
|
|
renderFabrication: true,
|
|
renderDnpOutline: false,
|
|
renderTracks: true,
|
|
renderZones: true,
|
|
columnOrder: [],
|
|
hiddenColumns: [],
|
|
netColors: {},
|
|
}
|
|
|
|
function initDefaults() {
|
|
settings.bomlayout = readStorage("bomlayout");
|
|
if (settings.bomlayout === null) {
|
|
settings.bomlayout = config.bom_view;
|
|
}
|
|
if (!['bom-only', 'left-right', 'top-bottom'].includes(settings.bomlayout)) {
|
|
settings.bomlayout = config.bom_view;
|
|
}
|
|
settings.bommode = readStorage("bommode");
|
|
if (settings.bommode === null) {
|
|
settings.bommode = "grouped";
|
|
}
|
|
if (settings.bommode == "netlist" && !pcbdata.nets) {
|
|
settings.bommode = "grouped";
|
|
}
|
|
if (!["grouped", "ungrouped", "netlist"].includes(settings.bommode)) {
|
|
settings.bommode = "grouped";
|
|
}
|
|
settings.canvaslayout = readStorage("canvaslayout");
|
|
if (settings.canvaslayout === null) {
|
|
settings.canvaslayout = config.layer_view;
|
|
}
|
|
var bomCheckboxes = readStorage("bomCheckboxes");
|
|
if (bomCheckboxes === null) {
|
|
bomCheckboxes = config.checkboxes;
|
|
}
|
|
settings.checkboxes = bomCheckboxes.split(",").filter((e) => e);
|
|
document.getElementById("bomCheckboxes").value = bomCheckboxes;
|
|
|
|
var highlightpin1 = readStorage("highlightpin1") || config.highlight_pin1;
|
|
if (highlightpin1 === "false") highlightpin1 = "none";
|
|
if (highlightpin1 === "true") highlightpin1 = "all";
|
|
setHighlightPin1(highlightpin1);
|
|
document.forms.highlightpin1.highlightpin1.value = highlightpin1;
|
|
|
|
settings.markWhenChecked = readStorage("markWhenChecked");
|
|
if (settings.markWhenChecked == null) {
|
|
settings.markWhenChecked = config.mark_when_checked;
|
|
}
|
|
populateMarkWhenCheckedOptions();
|
|
|
|
function initBooleanSetting(storageString, def, elementId, func) {
|
|
var b = readStorage(storageString);
|
|
if (b === null) {
|
|
b = def;
|
|
} else {
|
|
b = (b == "true");
|
|
}
|
|
document.getElementById(elementId).checked = b;
|
|
func(b);
|
|
}
|
|
|
|
initBooleanSetting("padsVisible", config.show_pads, "padsCheckbox", padsVisible);
|
|
initBooleanSetting("fabricationVisible", config.show_fabrication, "fabricationCheckbox", fabricationVisible);
|
|
initBooleanSetting("silkscreenVisible", config.show_silkscreen, "silkscreenCheckbox", silkscreenVisible);
|
|
initBooleanSetting("referencesVisible", true, "referencesCheckbox", referencesVisible);
|
|
initBooleanSetting("valuesVisible", true, "valuesCheckbox", valuesVisible);
|
|
if ("tracks" in pcbdata) {
|
|
initBooleanSetting("tracksVisible", true, "tracksCheckbox", tracksVisible);
|
|
initBooleanSetting("zonesVisible", true, "zonesCheckbox", zonesVisible);
|
|
} else {
|
|
document.getElementById("tracksAndZonesCheckboxes").style.display = "none";
|
|
tracksVisible(false);
|
|
zonesVisible(false);
|
|
}
|
|
initBooleanSetting("dnpOutline", false, "dnpOutlineCheckbox", dnpOutline);
|
|
initBooleanSetting("redrawOnDrag", config.redraw_on_drag, "dragCheckbox", setRedrawOnDrag);
|
|
initBooleanSetting("highlightRowOnClick", false, "highlightRowOnClickCheckbox", setHighlightRowOnClick);
|
|
initBooleanSetting("darkmode", config.dark_mode, "darkmodeCheckbox", setDarkMode);
|
|
|
|
var fields = ["checkboxes", "References"].concat(config.fields).concat(["Quantity"]);
|
|
var hcols = JSON.parse(readStorage("hiddenColumns"));
|
|
if (hcols === null) {
|
|
hcols = [];
|
|
}
|
|
settings.hiddenColumns = hcols.filter(e => fields.includes(e));
|
|
|
|
var cord = JSON.parse(readStorage("columnOrder"));
|
|
if (cord === null) {
|
|
cord = fields;
|
|
} else {
|
|
cord = cord.filter(e => fields.includes(e));
|
|
if (cord.length != fields.length)
|
|
cord = fields;
|
|
}
|
|
settings.columnOrder = cord;
|
|
|
|
settings.boardRotation = readStorage("boardRotation");
|
|
if (settings.boardRotation === null) {
|
|
settings.boardRotation = config.board_rotation * 5;
|
|
} else {
|
|
settings.boardRotation = parseInt(settings.boardRotation);
|
|
}
|
|
document.getElementById("boardRotation").value = settings.boardRotation / 5;
|
|
document.getElementById("rotationDegree").textContent = settings.boardRotation;
|
|
initBooleanSetting("offsetBackRotation", config.offset_back_rotation, "offsetBackRotationCheckbox", setOffsetBackRotation);
|
|
|
|
settings.netColors = JSON.parse(readStorage("netColors")) || {};
|
|
}
|
|
|
|
// Helper classes for user js callbacks.
|
|
|
|
const IBOM_EVENT_TYPES = {
|
|
ALL: "all",
|
|
HIGHLIGHT_EVENT: "highlightEvent",
|
|
CHECKBOX_CHANGE_EVENT: "checkboxChangeEvent",
|
|
BOM_BODY_CHANGE_EVENT: "bomBodyChangeEvent",
|
|
}
|
|
|
|
const EventHandler = {
|
|
callbacks: {},
|
|
init: function () {
|
|
for (eventType of Object.values(IBOM_EVENT_TYPES))
|
|
this.callbacks[eventType] = [];
|
|
},
|
|
registerCallback: function (eventType, callback) {
|
|
this.callbacks[eventType].push(callback);
|
|
},
|
|
emitEvent: function (eventType, eventArgs) {
|
|
event = {
|
|
eventType: eventType,
|
|
args: eventArgs,
|
|
}
|
|
var callback;
|
|
for (callback of this.callbacks[eventType])
|
|
callback(event);
|
|
for (callback of this.callbacks[IBOM_EVENT_TYPES.ALL])
|
|
callback(event);
|
|
}
|
|
}
|
|
EventHandler.init();
|
|
|
|
///////////////////////////////////////////////
|
|
|
|
///////////////////////////////////////////////
|
|
/* PCB rendering code */
|
|
|
|
var emptyContext2d = document.createElement("canvas").getContext("2d");
|
|
|
|
function deg2rad(deg) {
|
|
return deg * Math.PI / 180;
|
|
}
|
|
|
|
function calcFontPoint(linepoint, text, offsetx, offsety, tilt) {
|
|
var point = [
|
|
linepoint[0] * text.width + offsetx,
|
|
linepoint[1] * text.height + offsety
|
|
];
|
|
// This approximates pcbnew behavior with how text tilts depending on horizontal justification
|
|
point[0] -= (linepoint[1] + 0.5 * (1 + text.justify[0])) * text.height * tilt;
|
|
return point;
|
|
}
|
|
|
|
function drawText(ctx, text, color) {
|
|
if ("ref" in text && !settings.renderReferences) return;
|
|
if ("val" in text && !settings.renderValues) return;
|
|
ctx.save();
|
|
ctx.fillStyle = color;
|
|
ctx.strokeStyle = color;
|
|
ctx.lineCap = "round";
|
|
ctx.lineJoin = "round";
|
|
if ("svgpath" in text) {
|
|
if ("thickness" in text) {
|
|
ctx.lineWidth = text.thickness;
|
|
ctx.stroke(new Path2D(text.svgpath));
|
|
} else if ("fillrule" in text) {
|
|
ctx.fill(new Path2D(text.svgpath), text.fillrule);
|
|
}
|
|
ctx.restore();
|
|
return;
|
|
}
|
|
ctx.lineWidth = text.thickness;
|
|
if ("polygons" in text) {
|
|
ctx.fill(getPolygonsPath(text));
|
|
ctx.restore();
|
|
return;
|
|
}
|
|
ctx.translate(...text.pos);
|
|
ctx.translate(text.thickness * 0.5, 0);
|
|
var angle = -text.angle;
|
|
if (text.attr.includes("mirrored")) {
|
|
ctx.scale(-1, 1);
|
|
angle = -angle;
|
|
}
|
|
var tilt = 0;
|
|
if (text.attr.includes("italic")) {
|
|
tilt = 0.125;
|
|
}
|
|
var interline = text.height * 1.5 + text.thickness;
|
|
var txt = text.text.split("\n");
|
|
// KiCad ignores last empty line.
|
|
if (txt[txt.length - 1] == '') txt.pop();
|
|
ctx.rotate(deg2rad(angle));
|
|
var offsety = (1 - text.justify[1]) / 2 * text.height; // One line offset
|
|
offsety -= (txt.length - 1) * (text.justify[1] + 1) / 2 * interline; // Multiline offset
|
|
for (var i in txt) {
|
|
var lineWidth = text.thickness + interline / 2 * tilt;
|
|
for (var j = 0; j < txt[i].length; j++) {
|
|
if (txt[i][j] == '\t') {
|
|
var fourSpaces = 4 * pcbdata.font_data[' '].w * text.width;
|
|
lineWidth += fourSpaces - lineWidth % fourSpaces;
|
|
} else {
|
|
if (txt[i][j] == '~') {
|
|
j++;
|
|
if (j == txt[i].length)
|
|
break;
|
|
}
|
|
lineWidth += pcbdata.font_data[txt[i][j]].w * text.width;
|
|
}
|
|
}
|
|
var offsetx = -lineWidth * (text.justify[0] + 1) / 2;
|
|
var inOverbar = false;
|
|
for (var j = 0; j < txt[i].length; j++) {
|
|
if (config.kicad_text_formatting) {
|
|
if (txt[i][j] == '\t') {
|
|
var fourSpaces = 4 * pcbdata.font_data[' '].w * text.width;
|
|
offsetx += fourSpaces - offsetx % fourSpaces;
|
|
continue;
|
|
} else if (txt[i][j] == '~') {
|
|
j++;
|
|
if (j == txt[i].length)
|
|
break;
|
|
if (txt[i][j] != '~') {
|
|
inOverbar = !inOverbar;
|
|
}
|
|
}
|
|
}
|
|
var glyph = pcbdata.font_data[txt[i][j]];
|
|
if (inOverbar) {
|
|
var overbarStart = [offsetx, -text.height * 1.4 + offsety];
|
|
var overbarEnd = [offsetx + text.width * glyph.w, overbarStart[1]];
|
|
|
|
if (!lastHadOverbar) {
|
|
overbarStart[0] += text.height * 1.4 * tilt;
|
|
lastHadOverbar = true;
|
|
}
|
|
ctx.beginPath();
|
|
ctx.moveTo(...overbarStart);
|
|
ctx.lineTo(...overbarEnd);
|
|
ctx.stroke();
|
|
} else {
|
|
lastHadOverbar = false;
|
|
}
|
|
for (var line of glyph.l) {
|
|
ctx.beginPath();
|
|
ctx.moveTo(...calcFontPoint(line[0], text, offsetx, offsety, tilt));
|
|
for (var k = 1; k < line.length; k++) {
|
|
ctx.lineTo(...calcFontPoint(line[k], text, offsetx, offsety, tilt));
|
|
}
|
|
ctx.stroke();
|
|
}
|
|
offsetx += glyph.w * text.width;
|
|
}
|
|
offsety += interline;
|
|
}
|
|
ctx.restore();
|
|
}
|
|
|
|
function drawedge(ctx, scalefactor, edge, color) {
|
|
ctx.strokeStyle = color;
|
|
ctx.fillStyle = color;
|
|
ctx.lineWidth = Math.max(1 / scalefactor, edge.width);
|
|
ctx.lineCap = "round";
|
|
ctx.lineJoin = "round";
|
|
if ("svgpath" in edge) {
|
|
ctx.stroke(new Path2D(edge.svgpath));
|
|
} else {
|
|
ctx.beginPath();
|
|
if (edge.type == "segment") {
|
|
ctx.moveTo(...edge.start);
|
|
ctx.lineTo(...edge.end);
|
|
}
|
|
if (edge.type == "rect") {
|
|
ctx.moveTo(...edge.start);
|
|
ctx.lineTo(edge.start[0], edge.end[1]);
|
|
ctx.lineTo(...edge.end);
|
|
ctx.lineTo(edge.end[0], edge.start[1]);
|
|
ctx.lineTo(...edge.start);
|
|
}
|
|
if (edge.type == "arc") {
|
|
ctx.arc(
|
|
...edge.start,
|
|
edge.radius,
|
|
deg2rad(edge.startangle),
|
|
deg2rad(edge.endangle));
|
|
}
|
|
if (edge.type == "circle") {
|
|
ctx.arc(
|
|
...edge.start,
|
|
edge.radius,
|
|
0, 2 * Math.PI);
|
|
ctx.closePath();
|
|
}
|
|
if (edge.type == "curve") {
|
|
ctx.moveTo(...edge.start);
|
|
ctx.bezierCurveTo(...edge.cpa, ...edge.cpb, ...edge.end);
|
|
}
|
|
if("filled" in edge && edge.filled)
|
|
ctx.fill();
|
|
else
|
|
ctx.stroke();
|
|
}
|
|
}
|
|
|
|
function getChamferedRectPath(size, radius, chamfpos, chamfratio) {
|
|
// chamfpos is a bitmask, left = 1, right = 2, bottom left = 4, bottom right = 8
|
|
var path = new Path2D();
|
|
var width = size[0];
|
|
var height = size[1];
|
|
var x = width * -0.5;
|
|
var y = height * -0.5;
|
|
var chamfOffset = Math.min(width, height) * chamfratio;
|
|
path.moveTo(x, 0);
|
|
if (chamfpos & 4) {
|
|
path.lineTo(x, y + height - chamfOffset);
|
|
path.lineTo(x + chamfOffset, y + height);
|
|
path.lineTo(0, y + height);
|
|
} else {
|
|
path.arcTo(x, y + height, x + width, y + height, radius);
|
|
}
|
|
if (chamfpos & 8) {
|
|
path.lineTo(x + width - chamfOffset, y + height);
|
|
path.lineTo(x + width, y + height - chamfOffset);
|
|
path.lineTo(x + width, 0);
|
|
} else {
|
|
path.arcTo(x + width, y + height, x + width, y, radius);
|
|
}
|
|
if (chamfpos & 2) {
|
|
path.lineTo(x + width, y + chamfOffset);
|
|
path.lineTo(x + width - chamfOffset, y);
|
|
path.lineTo(0, y);
|
|
} else {
|
|
path.arcTo(x + width, y, x, y, radius);
|
|
}
|
|
if (chamfpos & 1) {
|
|
path.lineTo(x + chamfOffset, y);
|
|
path.lineTo(x, y + chamfOffset);
|
|
path.lineTo(x, 0);
|
|
} else {
|
|
path.arcTo(x, y, x, y + height, radius);
|
|
}
|
|
path.closePath();
|
|
return path;
|
|
}
|
|
|
|
function getOblongPath(size) {
|
|
return getChamferedRectPath(size, Math.min(size[0], size[1]) / 2, 0, 0);
|
|
}
|
|
|
|
function getPolygonsPath(shape) {
|
|
if (shape.path2d) {
|
|
return shape.path2d;
|
|
}
|
|
if ("svgpath" in shape) {
|
|
shape.path2d = new Path2D(shape.svgpath);
|
|
} else {
|
|
var path = new Path2D();
|
|
for (var polygon of shape.polygons) {
|
|
path.moveTo(...polygon[0]);
|
|
for (var i = 1; i < polygon.length; i++) {
|
|
path.lineTo(...polygon[i]);
|
|
}
|
|
path.closePath();
|
|
}
|
|
shape.path2d = path;
|
|
}
|
|
return shape.path2d;
|
|
}
|
|
|
|
function drawPolygonShape(ctx, scalefactor, shape, color) {
|
|
ctx.save();
|
|
if (!("svgpath" in shape)) {
|
|
ctx.translate(...shape.pos);
|
|
ctx.rotate(deg2rad(-shape.angle));
|
|
}
|
|
if("filled" in shape && !shape.filled) {
|
|
ctx.strokeStyle = color;
|
|
ctx.lineWidth = Math.max(1 / scalefactor, shape.width);
|
|
ctx.lineCap = "round";
|
|
ctx.lineJoin = "round";
|
|
ctx.stroke(getPolygonsPath(shape));
|
|
} else {
|
|
ctx.fillStyle = color;
|
|
ctx.fill(getPolygonsPath(shape));
|
|
}
|
|
ctx.restore();
|
|
}
|
|
|
|
function drawDrawing(ctx, scalefactor, drawing, color) {
|
|
if (["segment", "arc", "circle", "curve", "rect"].includes(drawing.type)) {
|
|
drawedge(ctx, scalefactor, drawing, color);
|
|
} else if (drawing.type == "polygon") {
|
|
drawPolygonShape(ctx, scalefactor, drawing, color);
|
|
} else {
|
|
drawText(ctx, drawing, color);
|
|
}
|
|
}
|
|
|
|
function getCirclePath(radius) {
|
|
var path = new Path2D();
|
|
path.arc(0, 0, radius, 0, 2 * Math.PI);
|
|
path.closePath();
|
|
return path;
|
|
}
|
|
|
|
function getCachedPadPath(pad) {
|
|
if (!pad.path2d) {
|
|
// if path2d is not set, build one and cache it on pad object
|
|
if (pad.shape == "rect") {
|
|
pad.path2d = new Path2D();
|
|
pad.path2d.rect(...pad.size.map(c => -c * 0.5), ...pad.size);
|
|
} else if (pad.shape == "oval") {
|
|
pad.path2d = getOblongPath(pad.size);
|
|
} else if (pad.shape == "circle") {
|
|
pad.path2d = getCirclePath(pad.size[0] / 2);
|
|
} else if (pad.shape == "roundrect") {
|
|
pad.path2d = getChamferedRectPath(pad.size, pad.radius, 0, 0);
|
|
} else if (pad.shape == "chamfrect") {
|
|
pad.path2d = getChamferedRectPath(pad.size, pad.radius, pad.chamfpos, pad.chamfratio)
|
|
} else if (pad.shape == "custom") {
|
|
pad.path2d = getPolygonsPath(pad);
|
|
}
|
|
}
|
|
return pad.path2d;
|
|
}
|
|
|
|
function drawPad(ctx, pad, color, outline) {
|
|
ctx.save();
|
|
ctx.translate(...pad.pos);
|
|
ctx.rotate(-deg2rad(pad.angle));
|
|
if (pad.offset) {
|
|
ctx.translate(...pad.offset);
|
|
}
|
|
ctx.fillStyle = color;
|
|
ctx.strokeStyle = color;
|
|
var path = getCachedPadPath(pad);
|
|
if (outline) {
|
|
ctx.stroke(path);
|
|
} else {
|
|
ctx.fill(path);
|
|
}
|
|
ctx.restore();
|
|
}
|
|
|
|
function drawPadHole(ctx, pad, padHoleColor) {
|
|
if (pad.type != "th") return;
|
|
ctx.save();
|
|
ctx.translate(...pad.pos);
|
|
ctx.rotate(-deg2rad(pad.angle));
|
|
ctx.fillStyle = padHoleColor;
|
|
if (pad.drillshape == "oblong") {
|
|
ctx.fill(getOblongPath(pad.drillsize));
|
|
} else if (pad.drillshape == "rect") {
|
|
ctx.fill(getChamferedRectPath(pad.drillsize, 0, 0, 0));
|
|
} else {
|
|
ctx.fill(getCirclePath(pad.drillsize[0] / 2));
|
|
}
|
|
ctx.restore();
|
|
}
|
|
|
|
function drawFootprint(ctx, layer, scalefactor, footprint, colors, highlight, outline) {
|
|
if (highlight) {
|
|
// draw bounding box
|
|
if (footprint.layer == layer) {
|
|
ctx.save();
|
|
ctx.globalAlpha = 0.2;
|
|
ctx.translate(...footprint.bbox.pos);
|
|
ctx.rotate(deg2rad(-footprint.bbox.angle));
|
|
ctx.translate(...footprint.bbox.relpos);
|
|
ctx.fillStyle = colors.pad;
|
|
ctx.fillRect(0, 0, ...footprint.bbox.size);
|
|
ctx.globalAlpha = 1;
|
|
ctx.strokeStyle = colors.pad;
|
|
ctx.lineWidth = 3 / scalefactor;
|
|
ctx.strokeRect(0, 0, ...footprint.bbox.size);
|
|
ctx.restore();
|
|
}
|
|
}
|
|
// draw drawings
|
|
for (var drawing of footprint.drawings) {
|
|
if (drawing.layer == layer) {
|
|
drawDrawing(ctx, scalefactor, drawing.drawing, colors.pad);
|
|
}
|
|
}
|
|
ctx.lineWidth = 3 / scalefactor;
|
|
// draw pads
|
|
if (settings.renderPads) {
|
|
for (var pad of footprint.pads) {
|
|
if (pad.layers.includes(layer)) {
|
|
drawPad(ctx, pad, colors.pad, outline);
|
|
if (pad.pin1 &&
|
|
(settings.highlightpin1 == "all" ||
|
|
settings.highlightpin1 == "selected" && highlight)) {
|
|
drawPad(ctx, pad, colors.outline, true);
|
|
}
|
|
}
|
|
}
|
|
for (var pad of footprint.pads) {
|
|
drawPadHole(ctx, pad, colors.padHole);
|
|
}
|
|
}
|
|
}
|
|
|
|
function drawEdgeCuts(canvas, scalefactor) {
|
|
var ctx = canvas.getContext("2d");
|
|
var edgecolor = getComputedStyle(topmostdiv).getPropertyValue('--pcb-edge-color');
|
|
for (var edge of pcbdata.edges) {
|
|
drawDrawing(ctx, scalefactor, edge, edgecolor);
|
|
}
|
|
}
|
|
|
|
function drawFootprints(canvas, layer, scalefactor, highlight) {
|
|
var ctx = canvas.getContext("2d");
|
|
ctx.lineWidth = 3 / scalefactor;
|
|
var style = getComputedStyle(topmostdiv);
|
|
|
|
var colors = {
|
|
pad: style.getPropertyValue('--pad-color'),
|
|
padHole: style.getPropertyValue('--pad-hole-color'),
|
|
outline: style.getPropertyValue('--pin1-outline-color'),
|
|
}
|
|
|
|
for (var i = 0; i < pcbdata.footprints.length; i++) {
|
|
var mod = pcbdata.footprints[i];
|
|
var outline = settings.renderDnpOutline && pcbdata.bom.skipped.includes(i);
|
|
var h = highlightedFootprints.includes(i);
|
|
var d = markedFootprints.has(i);
|
|
if (highlight) {
|
|
if(h && d) {
|
|
colors.pad = style.getPropertyValue('--pad-color-highlight-both');
|
|
colors.outline = style.getPropertyValue('--pin1-outline-color-highlight-both');
|
|
} else if (h) {
|
|
colors.pad = style.getPropertyValue('--pad-color-highlight');
|
|
colors.outline = style.getPropertyValue('--pin1-outline-color-highlight');
|
|
} else if (d) {
|
|
colors.pad = style.getPropertyValue('--pad-color-highlight-marked');
|
|
colors.outline = style.getPropertyValue('--pin1-outline-color-highlight-marked');
|
|
}
|
|
}
|
|
if( h || d || !highlight) {
|
|
drawFootprint(ctx, layer, scalefactor, mod, colors, highlight, outline);
|
|
}
|
|
}
|
|
}
|
|
|
|
function drawBgLayer(layername, canvas, layer, scalefactor, edgeColor, polygonColor, textColor) {
|
|
var ctx = canvas.getContext("2d");
|
|
for (var d of pcbdata.drawings[layername][layer]) {
|
|
if (["segment", "arc", "circle", "curve", "rect"].includes(d.type)) {
|
|
drawedge(ctx, scalefactor, d, edgeColor);
|
|
} else if (d.type == "polygon") {
|
|
drawPolygonShape(ctx, scalefactor, d, polygonColor);
|
|
} else {
|
|
drawText(ctx, d, textColor);
|
|
}
|
|
}
|
|
}
|
|
|
|
function drawTracks(canvas, layer, defaultColor, highlight) {
|
|
ctx = canvas.getContext("2d");
|
|
ctx.lineCap = "round";
|
|
|
|
var hasHole = (track) => (
|
|
'drillsize' in track &&
|
|
track.start[0] == track.end[0] &&
|
|
track.start[1] == track.end[1]);
|
|
|
|
// First draw tracks and tented vias
|
|
for (var track of pcbdata.tracks[layer]) {
|
|
if (highlight && highlightedNet != track.net) continue;
|
|
if (!hasHole(track)) {
|
|
ctx.strokeStyle = highlight ? defaultColor : settings.netColors[track.net] || defaultColor;
|
|
ctx.lineWidth = track.width;
|
|
ctx.beginPath();
|
|
if ('radius' in track) {
|
|
ctx.arc(
|
|
...track.center,
|
|
track.radius,
|
|
deg2rad(track.startangle),
|
|
deg2rad(track.endangle));
|
|
} else {
|
|
ctx.moveTo(...track.start);
|
|
ctx.lineTo(...track.end);
|
|
}
|
|
ctx.stroke();
|
|
}
|
|
}
|
|
// Second pass to draw untented vias
|
|
var style = getComputedStyle(topmostdiv);
|
|
var holeColor = style.getPropertyValue('--pad-hole-color')
|
|
|
|
for (var track of pcbdata.tracks[layer]) {
|
|
if (highlight && highlightedNet != track.net) continue;
|
|
if (hasHole(track)) {
|
|
ctx.strokeStyle = highlight ? defaultColor : settings.netColors[track.net] || defaultColor;
|
|
ctx.lineWidth = track.width;
|
|
ctx.beginPath();
|
|
ctx.moveTo(...track.start);
|
|
ctx.lineTo(...track.end);
|
|
ctx.stroke();
|
|
ctx.strokeStyle = holeColor;
|
|
ctx.lineWidth = track.drillsize;
|
|
ctx.lineTo(...track.end);
|
|
ctx.stroke();
|
|
}
|
|
}
|
|
}
|
|
|
|
function drawZones(canvas, layer, defaultColor, highlight) {
|
|
ctx = canvas.getContext("2d");
|
|
ctx.lineJoin = "round";
|
|
for (var zone of pcbdata.zones[layer]) {
|
|
if (highlight && highlightedNet != zone.net) continue;
|
|
ctx.strokeStyle = highlight ? defaultColor : settings.netColors[zone.net] || defaultColor;
|
|
ctx.fillStyle = highlight ? defaultColor : settings.netColors[zone.net] || defaultColor;
|
|
if (!zone.path2d) {
|
|
zone.path2d = getPolygonsPath(zone);
|
|
}
|
|
ctx.fill(zone.path2d, zone.fillrule || "nonzero");
|
|
if (zone.width > 0) {
|
|
ctx.lineWidth = zone.width;
|
|
ctx.stroke(zone.path2d);
|
|
}
|
|
}
|
|
}
|
|
|
|
function clearCanvas(canvas, color = null) {
|
|
var ctx = canvas.getContext("2d");
|
|
ctx.save();
|
|
ctx.setTransform(1, 0, 0, 1, 0, 0);
|
|
if (color) {
|
|
ctx.fillStyle = color;
|
|
ctx.fillRect(0, 0, canvas.width, canvas.height);
|
|
} else {
|
|
if (!window.matchMedia("print").matches)
|
|
ctx.clearRect(0, 0, canvas.width, canvas.height);
|
|
}
|
|
ctx.restore();
|
|
}
|
|
|
|
function drawNets(canvas, layer, highlight) {
|
|
var style = getComputedStyle(topmostdiv);
|
|
if (settings.renderZones) {
|
|
var zoneColor = style.getPropertyValue(highlight ? '--zone-color-highlight' : '--zone-color');
|
|
drawZones(canvas, layer, zoneColor, highlight);
|
|
}
|
|
if (settings.renderTracks) {
|
|
var trackColor = style.getPropertyValue(highlight ? '--track-color-highlight' : '--track-color');
|
|
drawTracks(canvas, layer, trackColor, highlight);
|
|
}
|
|
if (highlight && settings.renderPads) {
|
|
var padColor = style.getPropertyValue('--pad-color-highlight');
|
|
var padHoleColor = style.getPropertyValue('--pad-hole-color');
|
|
var ctx = canvas.getContext("2d");
|
|
for (var footprint of pcbdata.footprints) {
|
|
// draw pads
|
|
var padDrawn = false;
|
|
for (var pad of footprint.pads) {
|
|
if (highlightedNet != pad.net) continue;
|
|
if (pad.layers.includes(layer)) {
|
|
drawPad(ctx, pad, padColor, false);
|
|
padDrawn = true;
|
|
}
|
|
}
|
|
if (padDrawn) {
|
|
// redraw all pad holes because some pads may overlap
|
|
for (var pad of footprint.pads) {
|
|
drawPadHole(ctx, pad, padHoleColor);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
function drawHighlightsOnLayer(canvasdict, clear = true) {
|
|
if (clear) {
|
|
clearCanvas(canvasdict.highlight);
|
|
}
|
|
if (markedFootprints.size > 0 || highlightedFootprints.length > 0) {
|
|
drawFootprints(canvasdict.highlight, canvasdict.layer,
|
|
canvasdict.transform.s * canvasdict.transform.zoom, true);
|
|
}
|
|
if (highlightedNet !== null) {
|
|
drawNets(canvasdict.highlight, canvasdict.layer, true);
|
|
}
|
|
}
|
|
|
|
function drawHighlights() {
|
|
drawHighlightsOnLayer(allcanvas.front);
|
|
drawHighlightsOnLayer(allcanvas.back);
|
|
}
|
|
|
|
function drawBackground(canvasdict, clear = true) {
|
|
if (clear) {
|
|
clearCanvas(canvasdict.bg);
|
|
clearCanvas(canvasdict.fab);
|
|
clearCanvas(canvasdict.silk);
|
|
}
|
|
|
|
drawNets(canvasdict.bg, canvasdict.layer, false);
|
|
drawFootprints(canvasdict.bg, canvasdict.layer,
|
|
canvasdict.transform.s * canvasdict.transform.zoom, false);
|
|
|
|
drawEdgeCuts(canvasdict.bg, canvasdict.transform.s * canvasdict.transform.zoom);
|
|
|
|
var style = getComputedStyle(topmostdiv);
|
|
var edgeColor = style.getPropertyValue('--silkscreen-edge-color');
|
|
var polygonColor = style.getPropertyValue('--silkscreen-polygon-color');
|
|
var textColor = style.getPropertyValue('--silkscreen-text-color');
|
|
if (settings.renderSilkscreen) {
|
|
drawBgLayer(
|
|
"silkscreen", canvasdict.silk, canvasdict.layer,
|
|
canvasdict.transform.s * canvasdict.transform.zoom,
|
|
edgeColor, polygonColor, textColor);
|
|
}
|
|
edgeColor = style.getPropertyValue('--fabrication-edge-color');
|
|
polygonColor = style.getPropertyValue('--fabrication-polygon-color');
|
|
textColor = style.getPropertyValue('--fabrication-text-color');
|
|
if (settings.renderFabrication) {
|
|
drawBgLayer(
|
|
"fabrication", canvasdict.fab, canvasdict.layer,
|
|
canvasdict.transform.s * canvasdict.transform.zoom,
|
|
edgeColor, polygonColor, textColor);
|
|
}
|
|
}
|
|
|
|
function prepareCanvas(canvas, flip, transform) {
|
|
var ctx = canvas.getContext("2d");
|
|
ctx.setTransform(1, 0, 0, 1, 0, 0);
|
|
ctx.scale(transform.zoom, transform.zoom);
|
|
ctx.translate(transform.panx, transform.pany);
|
|
if (flip) {
|
|
ctx.scale(-1, 1);
|
|
}
|
|
ctx.translate(transform.x, transform.y);
|
|
ctx.rotate(deg2rad(settings.boardRotation + (flip && settings.offsetBackRotation ? - 180 : 0)));
|
|
ctx.scale(transform.s, transform.s);
|
|
}
|
|
|
|
function prepareLayer(canvasdict) {
|
|
var flip = (canvasdict.layer === "B");
|
|
for (var c of ["bg", "fab", "silk", "highlight"]) {
|
|
prepareCanvas(canvasdict[c], flip, canvasdict.transform);
|
|
}
|
|
}
|
|
|
|
function rotateVector(v, angle) {
|
|
angle = deg2rad(angle);
|
|
return [
|
|
v[0] * Math.cos(angle) - v[1] * Math.sin(angle),
|
|
v[0] * Math.sin(angle) + v[1] * Math.cos(angle)
|
|
];
|
|
}
|
|
|
|
function applyRotation(bbox, flip) {
|
|
var corners = [
|
|
[bbox.minx, bbox.miny],
|
|
[bbox.minx, bbox.maxy],
|
|
[bbox.maxx, bbox.miny],
|
|
[bbox.maxx, bbox.maxy],
|
|
];
|
|
corners = corners.map((v) => rotateVector(v, settings.boardRotation + (flip && settings.offsetBackRotation ? - 180 : 0)));
|
|
return {
|
|
minx: corners.reduce((a, v) => Math.min(a, v[0]), Infinity),
|
|
miny: corners.reduce((a, v) => Math.min(a, v[1]), Infinity),
|
|
maxx: corners.reduce((a, v) => Math.max(a, v[0]), -Infinity),
|
|
maxy: corners.reduce((a, v) => Math.max(a, v[1]), -Infinity),
|
|
}
|
|
}
|
|
|
|
function recalcLayerScale(layerdict, width, height) {
|
|
var flip = (layerdict.layer === "B");
|
|
var bbox = applyRotation(pcbdata.edges_bbox, flip);
|
|
var scalefactor = 0.98 * Math.min(
|
|
width / (bbox.maxx - bbox.minx),
|
|
height / (bbox.maxy - bbox.miny)
|
|
);
|
|
if (scalefactor < 0.1) {
|
|
scalefactor = 1;
|
|
}
|
|
layerdict.transform.s = scalefactor;
|
|
if (flip) {
|
|
layerdict.transform.x = -((bbox.maxx + bbox.minx) * scalefactor + width) * 0.5;
|
|
} else {
|
|
layerdict.transform.x = -((bbox.maxx + bbox.minx) * scalefactor - width) * 0.5;
|
|
}
|
|
layerdict.transform.y = -((bbox.maxy + bbox.miny) * scalefactor - height) * 0.5;
|
|
for (var c of ["bg", "fab", "silk", "highlight"]) {
|
|
canvas = layerdict[c];
|
|
canvas.width = width;
|
|
canvas.height = height;
|
|
canvas.style.width = (width / devicePixelRatio) + "px";
|
|
canvas.style.height = (height / devicePixelRatio) + "px";
|
|
}
|
|
}
|
|
|
|
function redrawCanvas(layerdict) {
|
|
prepareLayer(layerdict);
|
|
drawBackground(layerdict);
|
|
drawHighlightsOnLayer(layerdict);
|
|
}
|
|
|
|
function resizeCanvas(layerdict) {
|
|
var canvasdivid = {
|
|
"F": "frontcanvas",
|
|
"B": "backcanvas"
|
|
} [layerdict.layer];
|
|
var width = document.getElementById(canvasdivid).clientWidth * devicePixelRatio;
|
|
var height = document.getElementById(canvasdivid).clientHeight * devicePixelRatio;
|
|
recalcLayerScale(layerdict, width, height);
|
|
redrawCanvas(layerdict);
|
|
}
|
|
|
|
function resizeAll() {
|
|
resizeCanvas(allcanvas.front);
|
|
resizeCanvas(allcanvas.back);
|
|
}
|
|
|
|
function pointWithinDistanceToSegment(x, y, x1, y1, x2, y2, d) {
|
|
var A = x - x1;
|
|
var B = y - y1;
|
|
var C = x2 - x1;
|
|
var D = y2 - y1;
|
|
|
|
var dot = A * C + B * D;
|
|
var len_sq = C * C + D * D;
|
|
var dx, dy;
|
|
if (len_sq == 0) {
|
|
// start and end of the segment coincide
|
|
dx = x - x1;
|
|
dy = y - y1;
|
|
} else {
|
|
var param = dot / len_sq;
|
|
var xx, yy;
|
|
if (param < 0) {
|
|
xx = x1;
|
|
yy = y1;
|
|
} else if (param > 1) {
|
|
xx = x2;
|
|
yy = y2;
|
|
} else {
|
|
xx = x1 + param * C;
|
|
yy = y1 + param * D;
|
|
}
|
|
dx = x - xx;
|
|
dy = y - yy;
|
|
}
|
|
return dx * dx + dy * dy <= d * d;
|
|
}
|
|
|
|
function modulo(n, mod) {
|
|
return ((n % mod) + mod) % mod;
|
|
}
|
|
|
|
function pointWithinDistanceToArc(x, y, xc, yc, radius, startangle, endangle, d) {
|
|
var dx = x - xc;
|
|
var dy = y - yc;
|
|
var r_sq = dx * dx + dy * dy;
|
|
var rmin = Math.max(0, radius - d);
|
|
var rmax = radius + d;
|
|
|
|
if (r_sq < rmin * rmin || r_sq > rmax * rmax)
|
|
return false;
|
|
|
|
var angle1 = modulo(deg2rad(startangle), 2 * Math.PI);
|
|
var dx1 = xc + radius * Math.cos(angle1) - x;
|
|
var dy1 = yc + radius * Math.sin(angle1) - y;
|
|
if (dx1 * dx1 + dy1 * dy1 <= d * d)
|
|
return true;
|
|
|
|
var angle2 = modulo(deg2rad(endangle), 2 * Math.PI);
|
|
var dx2 = xc + radius * Math.cos(angle2) - x;
|
|
var dy2 = yc + radius * Math.sin(angle2) - y;
|
|
if (dx2 * dx2 + dy2 * dy2 <= d * d)
|
|
return true;
|
|
|
|
var angle = modulo(Math.atan2(dy, dx), 2 * Math.PI);
|
|
if (angle1 > angle2)
|
|
return (angle >= angle2 || angle <= angle1);
|
|
else
|
|
return (angle >= angle1 && angle <= angle2);
|
|
}
|
|
|
|
function pointWithinPad(x, y, pad) {
|
|
var v = [x - pad.pos[0], y - pad.pos[1]];
|
|
v = rotateVector(v, pad.angle);
|
|
if (pad.offset) {
|
|
v[0] -= pad.offset[0];
|
|
v[1] -= pad.offset[1];
|
|
}
|
|
return emptyContext2d.isPointInPath(getCachedPadPath(pad), ...v);
|
|
}
|
|
|
|
function netHitScan(layer, x, y) {
|
|
// Check track segments
|
|
if (settings.renderTracks && pcbdata.tracks) {
|
|
for (var track of pcbdata.tracks[layer]) {
|
|
if ('radius' in track) {
|
|
if (pointWithinDistanceToArc(x, y, ...track.center, track.radius, track.startangle, track.endangle, track.width / 2)) {
|
|
return track.net;
|
|
}
|
|
} else {
|
|
if (pointWithinDistanceToSegment(x, y, ...track.start, ...track.end, track.width / 2)) {
|
|
return track.net;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
// Check pads
|
|
if (settings.renderPads) {
|
|
for (var footprint of pcbdata.footprints) {
|
|
for (var pad of footprint.pads) {
|
|
if (pad.layers.includes(layer) && pointWithinPad(x, y, pad)) {
|
|
return pad.net;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
|
|
function pointWithinFootprintBbox(x, y, bbox) {
|
|
var v = [x - bbox.pos[0], y - bbox.pos[1]];
|
|
v = rotateVector(v, bbox.angle);
|
|
return bbox.relpos[0] <= v[0] && v[0] <= bbox.relpos[0] + bbox.size[0] &&
|
|
bbox.relpos[1] <= v[1] && v[1] <= bbox.relpos[1] + bbox.size[1];
|
|
}
|
|
|
|
function bboxHitScan(layer, x, y) {
|
|
var result = [];
|
|
for (var i = 0; i < pcbdata.footprints.length; i++) {
|
|
var footprint = pcbdata.footprints[i];
|
|
if (footprint.layer == layer) {
|
|
if (pointWithinFootprintBbox(x, y, footprint.bbox)) {
|
|
result.push(i);
|
|
}
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
function handlePointerDown(e, layerdict) {
|
|
if (e.button != 0 && e.button != 1) {
|
|
return;
|
|
}
|
|
e.preventDefault();
|
|
e.stopPropagation();
|
|
|
|
if (!e.hasOwnProperty("offsetX")) {
|
|
// The polyfill doesn't set this properly
|
|
e.offsetX = e.pageX - e.currentTarget.offsetLeft;
|
|
e.offsetY = e.pageY - e.currentTarget.offsetTop;
|
|
}
|
|
|
|
layerdict.pointerStates[e.pointerId] = {
|
|
distanceTravelled: 0,
|
|
lastX: e.offsetX,
|
|
lastY: e.offsetY,
|
|
downTime: Date.now(),
|
|
};
|
|
}
|
|
|
|
function handleMouseClick(e, layerdict) {
|
|
if (!e.hasOwnProperty("offsetX")) {
|
|
// The polyfill doesn't set this properly
|
|
e.offsetX = e.pageX - e.currentTarget.offsetLeft;
|
|
e.offsetY = e.pageY - e.currentTarget.offsetTop;
|
|
}
|
|
|
|
var x = e.offsetX;
|
|
var y = e.offsetY;
|
|
var t = layerdict.transform;
|
|
var flip = layerdict.layer === "B";
|
|
if (flip) {
|
|
x = (devicePixelRatio * x / t.zoom - t.panx + t.x) / -t.s;
|
|
} else {
|
|
x = (devicePixelRatio * x / t.zoom - t.panx - t.x) / t.s;
|
|
}
|
|
y = (devicePixelRatio * y / t.zoom - t.y - t.pany) / t.s;
|
|
var v = rotateVector([x, y], -settings.boardRotation + (flip && settings.offsetBackRotation ? - 180 : 0));
|
|
if ("nets" in pcbdata) {
|
|
var net = netHitScan(layerdict.layer, ...v);
|
|
if (net !== highlightedNet) {
|
|
netClicked(net);
|
|
}
|
|
}
|
|
if (highlightedNet === null) {
|
|
var footprints = bboxHitScan(layerdict.layer, ...v);
|
|
if (footprints.length > 0) {
|
|
footprintsClicked(footprints);
|
|
}
|
|
}
|
|
}
|
|
|
|
function handlePointerLeave(e, layerdict) {
|
|
e.preventDefault();
|
|
e.stopPropagation();
|
|
|
|
if (!settings.redrawOnDrag) {
|
|
redrawCanvas(layerdict);
|
|
}
|
|
|
|
delete layerdict.pointerStates[e.pointerId];
|
|
}
|
|
|
|
function resetTransform(layerdict) {
|
|
layerdict.transform.panx = 0;
|
|
layerdict.transform.pany = 0;
|
|
layerdict.transform.zoom = 1;
|
|
redrawCanvas(layerdict);
|
|
}
|
|
|
|
function handlePointerUp(e, layerdict) {
|
|
if (!e.hasOwnProperty("offsetX")) {
|
|
// The polyfill doesn't set this properly
|
|
e.offsetX = e.pageX - e.currentTarget.offsetLeft;
|
|
e.offsetY = e.pageY - e.currentTarget.offsetTop;
|
|
}
|
|
|
|
e.preventDefault();
|
|
e.stopPropagation();
|
|
|
|
if (e.button == 2) {
|
|
// Reset pan and zoom on right click.
|
|
resetTransform(layerdict);
|
|
layerdict.anotherPointerTapped = false;
|
|
return;
|
|
}
|
|
|
|
// We haven't necessarily had a pointermove event since the interaction started, so make sure we update this now
|
|
var ptr = layerdict.pointerStates[e.pointerId];
|
|
ptr.distanceTravelled += Math.abs(e.offsetX - ptr.lastX) + Math.abs(e.offsetY - ptr.lastY);
|
|
|
|
if (e.button == 0 && ptr.distanceTravelled < 10 && Date.now() - ptr.downTime <= 500) {
|
|
if (Object.keys(layerdict.pointerStates).length == 1) {
|
|
if (layerdict.anotherPointerTapped) {
|
|
// This is the second pointer coming off of a two-finger tap
|
|
resetTransform(layerdict);
|
|
} else {
|
|
// This is just a regular tap
|
|
handleMouseClick(e, layerdict);
|
|
}
|
|
layerdict.anotherPointerTapped = false;
|
|
} else {
|
|
// This is the first finger coming off of what could become a two-finger tap
|
|
layerdict.anotherPointerTapped = true;
|
|
}
|
|
} else {
|
|
if (!settings.redrawOnDrag) {
|
|
redrawCanvas(layerdict);
|
|
}
|
|
layerdict.anotherPointerTapped = false;
|
|
}
|
|
|
|
delete layerdict.pointerStates[e.pointerId];
|
|
}
|
|
|
|
function handlePointerMove(e, layerdict) {
|
|
if (!layerdict.pointerStates.hasOwnProperty(e.pointerId)) {
|
|
return;
|
|
}
|
|
e.preventDefault();
|
|
e.stopPropagation();
|
|
|
|
if (!e.hasOwnProperty("offsetX")) {
|
|
// The polyfill doesn't set this properly
|
|
e.offsetX = e.pageX - e.currentTarget.offsetLeft;
|
|
e.offsetY = e.pageY - e.currentTarget.offsetTop;
|
|
}
|
|
|
|
var thisPtr = layerdict.pointerStates[e.pointerId];
|
|
|
|
var dx = e.offsetX - thisPtr.lastX;
|
|
var dy = e.offsetY - thisPtr.lastY;
|
|
|
|
// If this number is low on pointer up, we count the action as a click
|
|
thisPtr.distanceTravelled += Math.abs(dx) + Math.abs(dy);
|
|
|
|
if (Object.keys(layerdict.pointerStates).length == 1) {
|
|
// This is a simple drag
|
|
layerdict.transform.panx += devicePixelRatio * dx / layerdict.transform.zoom;
|
|
layerdict.transform.pany += devicePixelRatio * dy / layerdict.transform.zoom;
|
|
} else if (Object.keys(layerdict.pointerStates).length == 2) {
|
|
var otherPtr = Object.values(layerdict.pointerStates).filter((ptr) => ptr != thisPtr)[0];
|
|
|
|
var oldDist = Math.sqrt(Math.pow(thisPtr.lastX - otherPtr.lastX, 2) + Math.pow(thisPtr.lastY - otherPtr.lastY, 2));
|
|
var newDist = Math.sqrt(Math.pow(e.offsetX - otherPtr.lastX, 2) + Math.pow(e.offsetY - otherPtr.lastY, 2));
|
|
|
|
var scaleFactor = newDist / oldDist;
|
|
|
|
if (scaleFactor != NaN) {
|
|
layerdict.transform.zoom *= scaleFactor;
|
|
|
|
var zoomd = (1 - scaleFactor) / layerdict.transform.zoom;
|
|
layerdict.transform.panx += devicePixelRatio * otherPtr.lastX * zoomd;
|
|
layerdict.transform.pany += devicePixelRatio * otherPtr.lastY * zoomd;
|
|
}
|
|
}
|
|
|
|
thisPtr.lastX = e.offsetX;
|
|
thisPtr.lastY = e.offsetY;
|
|
|
|
if (settings.redrawOnDrag) {
|
|
redrawCanvas(layerdict);
|
|
}
|
|
}
|
|
|
|
function handleMouseWheel(e, layerdict) {
|
|
e.preventDefault();
|
|
e.stopPropagation();
|
|
var t = layerdict.transform;
|
|
var wheeldelta = e.deltaY;
|
|
if (e.deltaMode == 1) {
|
|
// FF only, scroll by lines
|
|
wheeldelta *= 30;
|
|
} else if (e.deltaMode == 2) {
|
|
wheeldelta *= 300;
|
|
}
|
|
var m = Math.pow(1.1, -wheeldelta / 40);
|
|
// Limit amount of zoom per tick.
|
|
if (m > 2) {
|
|
m = 2;
|
|
} else if (m < 0.5) {
|
|
m = 0.5;
|
|
}
|
|
t.zoom *= m;
|
|
var zoomd = (1 - m) / t.zoom;
|
|
t.panx += devicePixelRatio * e.offsetX * zoomd;
|
|
t.pany += devicePixelRatio * e.offsetY * zoomd;
|
|
redrawCanvas(layerdict);
|
|
}
|
|
|
|
function addMouseHandlers(div, layerdict) {
|
|
div.addEventListener("pointerdown", function(e) {
|
|
handlePointerDown(e, layerdict);
|
|
});
|
|
div.addEventListener("pointermove", function(e) {
|
|
handlePointerMove(e, layerdict);
|
|
});
|
|
div.addEventListener("pointerup", function(e) {
|
|
handlePointerUp(e, layerdict);
|
|
});
|
|
var pointerleave = function(e) {
|
|
handlePointerLeave(e, layerdict);
|
|
}
|
|
div.addEventListener("pointercancel", pointerleave);
|
|
div.addEventListener("pointerleave", pointerleave);
|
|
div.addEventListener("pointerout", pointerleave);
|
|
|
|
div.onwheel = function(e) {
|
|
handleMouseWheel(e, layerdict);
|
|
}
|
|
for (var element of [div, layerdict.bg, layerdict.fab, layerdict.silk, layerdict.highlight]) {
|
|
element.addEventListener("contextmenu", function(e) {
|
|
e.preventDefault();
|
|
}, false);
|
|
}
|
|
}
|
|
|
|
function setRedrawOnDrag(value) {
|
|
settings.redrawOnDrag = value;
|
|
writeStorage("redrawOnDrag", value);
|
|
}
|
|
|
|
function setBoardRotation(value) {
|
|
settings.boardRotation = value * 5;
|
|
writeStorage("boardRotation", settings.boardRotation);
|
|
document.getElementById("rotationDegree").textContent = settings.boardRotation;
|
|
resizeAll();
|
|
}
|
|
|
|
function setOffsetBackRotation(value) {
|
|
settings.offsetBackRotation = value;
|
|
writeStorage("offsetBackRotation", value);
|
|
resizeAll();
|
|
}
|
|
|
|
function initRender() {
|
|
allcanvas = {
|
|
front: {
|
|
transform: {
|
|
x: 0,
|
|
y: 0,
|
|
s: 1,
|
|
panx: 0,
|
|
pany: 0,
|
|
zoom: 1,
|
|
},
|
|
pointerStates: {},
|
|
anotherPointerTapped: false,
|
|
bg: document.getElementById("F_bg"),
|
|
fab: document.getElementById("F_fab"),
|
|
silk: document.getElementById("F_slk"),
|
|
highlight: document.getElementById("F_hl"),
|
|
layer: "F",
|
|
},
|
|
back: {
|
|
transform: {
|
|
x: 0,
|
|
y: 0,
|
|
s: 1,
|
|
panx: 0,
|
|
pany: 0,
|
|
zoom: 1,
|
|
},
|
|
pointerStates: {},
|
|
anotherPointerTapped: false,
|
|
bg: document.getElementById("B_bg"),
|
|
fab: document.getElementById("B_fab"),
|
|
silk: document.getElementById("B_slk"),
|
|
highlight: document.getElementById("B_hl"),
|
|
layer: "B",
|
|
}
|
|
};
|
|
addMouseHandlers(document.getElementById("frontcanvas"), allcanvas.front);
|
|
addMouseHandlers(document.getElementById("backcanvas"), allcanvas.back);
|
|
}
|
|
|
|
///////////////////////////////////////////////
|
|
|
|
///////////////////////////////////////////////
|
|
/*
|
|
* Table reordering via Drag'n'Drop
|
|
* Inspired by: https://htmldom.dev/drag-and-drop-table-column
|
|
*/
|
|
|
|
function setBomHandlers() {
|
|
|
|
const bom = document.getElementById('bomtable');
|
|
|
|
let dragName;
|
|
let placeHolderElements;
|
|
let draggingElement;
|
|
let forcePopulation;
|
|
let xOffset;
|
|
let yOffset;
|
|
let wasDragged;
|
|
|
|
const mouseUpHandler = function(e) {
|
|
// Delete dragging element
|
|
draggingElement.remove();
|
|
|
|
// Make BOM selectable again
|
|
bom.style.removeProperty("user-select");
|
|
|
|
// Remove listeners
|
|
document.removeEventListener('mousemove', mouseMoveHandler);
|
|
document.removeEventListener('mouseup', mouseUpHandler);
|
|
|
|
if (wasDragged) {
|
|
// Redraw whole BOM
|
|
populateBomTable();
|
|
}
|
|
}
|
|
|
|
const mouseMoveHandler = function(e) {
|
|
// Notice the dragging
|
|
wasDragged = true;
|
|
|
|
// Make the dragged element visible
|
|
draggingElement.style.removeProperty("display");
|
|
|
|
// Set elements position to mouse position
|
|
draggingElement.style.left = `${e.screenX - xOffset}px`;
|
|
draggingElement.style.top = `${e.screenY - yOffset}px`;
|
|
|
|
// Forced redrawing of BOM table
|
|
if (forcePopulation) {
|
|
forcePopulation = false;
|
|
// Copy array
|
|
phe = Array.from(placeHolderElements);
|
|
// populate BOM table again
|
|
populateBomHeader(dragName, phe);
|
|
populateBomBody(dragName, phe);
|
|
}
|
|
|
|
// Set up array of hidden columns
|
|
var hiddenColumns = Array.from(settings.hiddenColumns);
|
|
// In the ungrouped mode, quantity don't exist
|
|
if (settings.bommode === "ungrouped")
|
|
hiddenColumns.push("Quantity");
|
|
// If no checkbox fields can be found, we consider them hidden
|
|
if (settings.checkboxes.length == 0)
|
|
hiddenColumns.push("checkboxes");
|
|
|
|
// Get table headers and group them into checkboxes, extrafields and normal headers
|
|
const bh = document.getElementById("bomhead");
|
|
headers = Array.from(bh.querySelectorAll("th"))
|
|
headers.shift() // numCol is not part of the columnOrder
|
|
headerGroups = []
|
|
lastCompoundClass = null;
|
|
for (i = 0; i < settings.columnOrder.length; i++) {
|
|
cElem = settings.columnOrder[i];
|
|
if (hiddenColumns.includes(cElem)) {
|
|
// Hidden columns appear as a dummy element
|
|
headerGroups.push([]);
|
|
continue;
|
|
}
|
|
elem = headers.filter(e => getColumnOrderName(e) === cElem)[0];
|
|
if (elem.classList.contains("bom-checkbox")) {
|
|
if (lastCompoundClass === "bom-checkbox") {
|
|
cbGroup = headerGroups.pop();
|
|
cbGroup.push(elem);
|
|
headerGroups.push(cbGroup);
|
|
} else {
|
|
lastCompoundClass = "bom-checkbox";
|
|
headerGroups.push([elem])
|
|
}
|
|
} else {
|
|
headerGroups.push([elem])
|
|
}
|
|
}
|
|
|
|
// Copy settings.columnOrder
|
|
var columns = Array.from(settings.columnOrder)
|
|
|
|
// Set up array with indices of hidden columns
|
|
var hiddenIndices = hiddenColumns.map(e => settings.columnOrder.indexOf(e));
|
|
var dragIndex = columns.indexOf(dragName);
|
|
var swapIndex = dragIndex;
|
|
var swapDone = false;
|
|
|
|
// Check if the current dragged element is swapable with the left or right element
|
|
if (dragIndex > 0) {
|
|
// Get left headers boundingbox
|
|
swapIndex = dragIndex - 1;
|
|
while (hiddenIndices.includes(swapIndex) && swapIndex > 0)
|
|
swapIndex--;
|
|
if (!hiddenIndices.includes(swapIndex)) {
|
|
box = getBoundingClientRectFromMultiple(headerGroups[swapIndex]);
|
|
if (e.clientX < box.left + window.scrollX + (box.width / 2)) {
|
|
swapElement = columns[dragIndex];
|
|
columns.splice(dragIndex, 1);
|
|
columns.splice(swapIndex, 0, swapElement);
|
|
forcePopulation = true;
|
|
swapDone = true;
|
|
}
|
|
}
|
|
}
|
|
if ((!swapDone) && dragIndex < headerGroups.length - 1) {
|
|
// Get right headers boundingbox
|
|
swapIndex = dragIndex + 1;
|
|
while (hiddenIndices.includes(swapIndex))
|
|
swapIndex++;
|
|
if (swapIndex < headerGroups.length) {
|
|
box = getBoundingClientRectFromMultiple(headerGroups[swapIndex]);
|
|
if (e.clientX > box.left + window.scrollX + (box.width / 2)) {
|
|
swapElement = columns[dragIndex];
|
|
columns.splice(dragIndex, 1);
|
|
columns.splice(swapIndex, 0, swapElement);
|
|
forcePopulation = true;
|
|
swapDone = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Write back change to storage
|
|
if (swapDone) {
|
|
settings.columnOrder = columns
|
|
writeStorage("columnOrder", JSON.stringify(columns));
|
|
}
|
|
|
|
}
|
|
|
|
const mouseDownHandler = function(e) {
|
|
var target = e.target;
|
|
if (target.tagName.toLowerCase() != "td")
|
|
target = target.parentElement;
|
|
|
|
// Used to check if a dragging has ever happened
|
|
wasDragged = false;
|
|
|
|
// Create new element which will be displayed as the dragged column
|
|
draggingElement = document.createElement("div")
|
|
draggingElement.classList.add("dragging");
|
|
draggingElement.style.display = "none";
|
|
draggingElement.style.position = "absolute";
|
|
draggingElement.style.overflow = "hidden";
|
|
|
|
// Get bomhead and bombody elements
|
|
const bh = document.getElementById("bomhead");
|
|
const bb = document.getElementById("bombody");
|
|
|
|
// Get all compound headers for the current column
|
|
var compoundHeaders;
|
|
if (target.classList.contains("bom-checkbox")) {
|
|
compoundHeaders = Array.from(bh.querySelectorAll("th.bom-checkbox"));
|
|
} else {
|
|
compoundHeaders = [target];
|
|
}
|
|
|
|
// Create new table which will display the column
|
|
var newTable = document.createElement("table");
|
|
newTable.classList.add("bom");
|
|
newTable.style.background = "white";
|
|
draggingElement.append(newTable);
|
|
|
|
// Create new header element
|
|
var newHeader = document.createElement("thead");
|
|
newTable.append(newHeader);
|
|
|
|
// Set up array for storing all placeholder elements
|
|
placeHolderElements = [];
|
|
|
|
// Add all compound headers to the new thead element and placeholders
|
|
compoundHeaders.forEach(function(h) {
|
|
clone = cloneElementWithDimensions(h);
|
|
newHeader.append(clone);
|
|
placeHolderElements.push(clone);
|
|
});
|
|
|
|
// Create new body element
|
|
var newBody = document.createElement("tbody");
|
|
newTable.append(newBody);
|
|
|
|
// Get indices for compound headers
|
|
var idxs = compoundHeaders.map(e => getBomTableHeaderIndex(e));
|
|
|
|
// For each row in the BOM body...
|
|
var rows = bb.querySelectorAll("tr");
|
|
rows.forEach(function(row) {
|
|
// ..get the cells for the compound column
|
|
const tds = row.querySelectorAll("td");
|
|
var copytds = idxs.map(i => tds[i]);
|
|
// Add them to the new element and the placeholders
|
|
var newRow = document.createElement("tr");
|
|
copytds.forEach(function(td) {
|
|
clone = cloneElementWithDimensions(td);
|
|
newRow.append(clone);
|
|
placeHolderElements.push(clone);
|
|
});
|
|
newBody.append(newRow);
|
|
});
|
|
|
|
// Compute width for compound header
|
|
var width = compoundHeaders.reduce((acc, x) => acc + x.clientWidth, 0);
|
|
draggingElement.style.width = `${width}px`;
|
|
|
|
// Insert the new dragging element and disable selection on BOM
|
|
bom.insertBefore(draggingElement, null);
|
|
bom.style.userSelect = "none";
|
|
|
|
// Determine the mouse position offset
|
|
xOffset = e.screenX - compoundHeaders.reduce((acc, x) => Math.min(acc, x.offsetLeft), compoundHeaders[0].offsetLeft);
|
|
yOffset = e.screenY - compoundHeaders[0].offsetTop;
|
|
|
|
// Get name for the column in settings.columnOrder
|
|
dragName = getColumnOrderName(target);
|
|
|
|
// Change text and class for placeholder elements
|
|
placeHolderElements = placeHolderElements.map(function(e) {
|
|
newElem = cloneElementWithDimensions(e);
|
|
newElem.textContent = "";
|
|
newElem.classList.add("placeholder");
|
|
return newElem;
|
|
});
|
|
|
|
// On next mouse move, the whole BOM needs to be redrawn to show the placeholders
|
|
forcePopulation = true;
|
|
|
|
// Add listeners for move and up on mouse
|
|
document.addEventListener('mousemove', mouseMoveHandler);
|
|
document.addEventListener('mouseup', mouseUpHandler);
|
|
}
|
|
|
|
// In netlist mode, there is nothing to reorder
|
|
if (settings.bommode === "netlist")
|
|
return;
|
|
|
|
// Add mouseDownHandler to every column except the numCol
|
|
bom.querySelectorAll("th")
|
|
.forEach(function(head) {
|
|
if (!head.classList.contains("numCol")) {
|
|
head.onmousedown = mouseDownHandler;
|
|
}
|
|
});
|
|
|
|
}
|
|
|
|
function getBoundingClientRectFromMultiple(elements) {
|
|
var elems = Array.from(elements);
|
|
|
|
if (elems.length == 0)
|
|
return null;
|
|
|
|
var box = elems.shift()
|
|
.getBoundingClientRect();
|
|
|
|
elems.forEach(function(elem) {
|
|
var elembox = elem.getBoundingClientRect();
|
|
box.left = Math.min(elembox.left, box.left);
|
|
box.top = Math.min(elembox.top, box.top);
|
|
box.width += elembox.width;
|
|
box.height = Math.max(elembox.height, box.height);
|
|
});
|
|
|
|
return box;
|
|
}
|
|
|
|
function cloneElementWithDimensions(elem) {
|
|
var newElem = elem.cloneNode(true);
|
|
newElem.style.height = window.getComputedStyle(elem).height;
|
|
newElem.style.width = window.getComputedStyle(elem).width;
|
|
return newElem;
|
|
}
|
|
|
|
function getBomTableHeaderIndex(elem) {
|
|
const bh = document.getElementById('bomhead');
|
|
const ths = Array.from(bh.querySelectorAll("th"));
|
|
return ths.indexOf(elem);
|
|
}
|
|
|
|
function getColumnOrderName(elem) {
|
|
var cname = elem.getAttribute("col_name");
|
|
if (cname === "bom-checkbox")
|
|
return "checkboxes";
|
|
else
|
|
return cname;
|
|
}
|
|
|
|
function resizableGrid(tablehead) {
|
|
var cols = tablehead.firstElementChild.children;
|
|
var rowWidth = tablehead.offsetWidth;
|
|
|
|
for (var i = 1; i < cols.length; i++) {
|
|
if (cols[i].classList.contains("bom-checkbox"))
|
|
continue;
|
|
cols[i].style.width = ((cols[i].clientWidth - paddingDiff(cols[i])) * 100 / rowWidth) + '%';
|
|
}
|
|
|
|
for (var i = 1; i < cols.length - 1; i++) {
|
|
var div = document.createElement('div');
|
|
div.className = "column-width-handle";
|
|
cols[i].appendChild(div);
|
|
setListeners(div);
|
|
}
|
|
|
|
function setListeners(div) {
|
|
var startX, curCol, nxtCol, curColWidth, nxtColWidth, rowWidth;
|
|
|
|
div.addEventListener('mousedown', function(e) {
|
|
e.preventDefault();
|
|
e.stopPropagation();
|
|
|
|
curCol = e.target.parentElement;
|
|
nxtCol = curCol.nextElementSibling;
|
|
startX = e.pageX;
|
|
|
|
var padding = paddingDiff(curCol);
|
|
|
|
rowWidth = curCol.parentElement.offsetWidth;
|
|
curColWidth = curCol.clientWidth - padding;
|
|
nxtColWidth = nxtCol.clientWidth - padding;
|
|
});
|
|
|
|
document.addEventListener('mousemove', function(e) {
|
|
if (startX) {
|
|
var diffX = e.pageX - startX;
|
|
diffX = -Math.min(-diffX, curColWidth - 20);
|
|
diffX = Math.min(diffX, nxtColWidth - 20);
|
|
|
|
curCol.style.width = ((curColWidth + diffX) * 100 / rowWidth) + '%';
|
|
nxtCol.style.width = ((nxtColWidth - diffX) * 100 / rowWidth) + '%';
|
|
console.log(`${curColWidth + nxtColWidth} ${(curColWidth + diffX) * 100 / rowWidth + (nxtColWidth - diffX) * 100 / rowWidth}`);
|
|
}
|
|
});
|
|
|
|
document.addEventListener('mouseup', function(e) {
|
|
curCol = undefined;
|
|
nxtCol = undefined;
|
|
startX = undefined;
|
|
nxtColWidth = undefined;
|
|
curColWidth = undefined
|
|
});
|
|
}
|
|
|
|
function paddingDiff(col) {
|
|
|
|
if (getStyleVal(col, 'box-sizing') == 'border-box') {
|
|
return 0;
|
|
}
|
|
|
|
var padLeft = getStyleVal(col, 'padding-left');
|
|
var padRight = getStyleVal(col, 'padding-right');
|
|
return (parseInt(padLeft) + parseInt(padRight));
|
|
|
|
}
|
|
|
|
function getStyleVal(elm, css) {
|
|
return (window.getComputedStyle(elm, null).getPropertyValue(css))
|
|
}
|
|
}
|
|
|
|
///////////////////////////////////////////////
|
|
|
|
///////////////////////////////////////////////
|
|
/* DOM manipulation and misc code */
|
|
|
|
var bomsplit;
|
|
var canvassplit;
|
|
var initDone = false;
|
|
var bomSortFunction = null;
|
|
var currentSortColumn = null;
|
|
var currentSortOrder = null;
|
|
var currentHighlightedRowId;
|
|
var highlightHandlers = [];
|
|
var footprintIndexToHandler = {};
|
|
var netsToHandler = {};
|
|
var markedFootprints = new Set();
|
|
var highlightedFootprints = [];
|
|
var highlightedNet = null;
|
|
var lastClicked;
|
|
|
|
function dbg(html) {
|
|
dbgdiv.innerHTML = html;
|
|
}
|
|
|
|
function redrawIfInitDone() {
|
|
if (initDone) {
|
|
redrawCanvas(allcanvas.front);
|
|
redrawCanvas(allcanvas.back);
|
|
}
|
|
}
|
|
|
|
function padsVisible(value) {
|
|
writeStorage("padsVisible", value);
|
|
settings.renderPads = value;
|
|
redrawIfInitDone();
|
|
}
|
|
|
|
function referencesVisible(value) {
|
|
writeStorage("referencesVisible", value);
|
|
settings.renderReferences = value;
|
|
redrawIfInitDone();
|
|
}
|
|
|
|
function valuesVisible(value) {
|
|
writeStorage("valuesVisible", value);
|
|
settings.renderValues = value;
|
|
redrawIfInitDone();
|
|
}
|
|
|
|
function tracksVisible(value) {
|
|
writeStorage("tracksVisible", value);
|
|
settings.renderTracks = value;
|
|
redrawIfInitDone();
|
|
}
|
|
|
|
function zonesVisible(value) {
|
|
writeStorage("zonesVisible", value);
|
|
settings.renderZones = value;
|
|
redrawIfInitDone();
|
|
}
|
|
|
|
function dnpOutline(value) {
|
|
writeStorage("dnpOutline", value);
|
|
settings.renderDnpOutline = value;
|
|
redrawIfInitDone();
|
|
}
|
|
|
|
function setDarkMode(value) {
|
|
if (value) {
|
|
topmostdiv.classList.add("dark");
|
|
} else {
|
|
topmostdiv.classList.remove("dark");
|
|
}
|
|
writeStorage("darkmode", value);
|
|
settings.darkMode = value;
|
|
redrawIfInitDone();
|
|
if (initDone) {
|
|
populateBomTable();
|
|
}
|
|
}
|
|
|
|
function setShowBOMColumn(field, value) {
|
|
if (field === "references") {
|
|
var rl = document.getElementById("reflookup");
|
|
rl.disabled = !value;
|
|
if (!value) {
|
|
rl.value = "";
|
|
updateRefLookup("");
|
|
}
|
|
}
|
|
|
|
var n = settings.hiddenColumns.indexOf(field);
|
|
if (value) {
|
|
if (n != -1) {
|
|
settings.hiddenColumns.splice(n, 1);
|
|
}
|
|
} else {
|
|
if (n == -1) {
|
|
settings.hiddenColumns.push(field);
|
|
}
|
|
}
|
|
|
|
writeStorage("hiddenColumns", JSON.stringify(settings.hiddenColumns));
|
|
|
|
if (initDone) {
|
|
populateBomTable();
|
|
}
|
|
|
|
redrawIfInitDone();
|
|
}
|
|
|
|
|
|
function setFullscreen(value) {
|
|
if (value) {
|
|
document.documentElement.requestFullscreen();
|
|
} else {
|
|
document.exitFullscreen();
|
|
}
|
|
}
|
|
|
|
function fabricationVisible(value) {
|
|
writeStorage("fabricationVisible", value);
|
|
settings.renderFabrication = value;
|
|
redrawIfInitDone();
|
|
}
|
|
|
|
function silkscreenVisible(value) {
|
|
writeStorage("silkscreenVisible", value);
|
|
settings.renderSilkscreen = value;
|
|
redrawIfInitDone();
|
|
}
|
|
|
|
function setHighlightPin1(value) {
|
|
writeStorage("highlightpin1", value);
|
|
settings.highlightpin1 = value;
|
|
redrawIfInitDone();
|
|
}
|
|
|
|
function setHighlightRowOnClick(value) {
|
|
settings.highlightRowOnClick = value;
|
|
writeStorage("highlightRowOnClick", value);
|
|
if (initDone) {
|
|
populateBomTable();
|
|
}
|
|
}
|
|
|
|
function getStoredCheckboxRefs(checkbox) {
|
|
function convert(ref) {
|
|
var intref = parseInt(ref);
|
|
if (isNaN(intref)) {
|
|
for (var i = 0; i < pcbdata.footprints.length; i++) {
|
|
if (pcbdata.footprints[i].ref == ref) {
|
|
return i;
|
|
}
|
|
}
|
|
return -1;
|
|
} else {
|
|
return intref;
|
|
}
|
|
}
|
|
if (!(checkbox in settings.checkboxStoredRefs)) {
|
|
var val = readStorage("checkbox_" + checkbox);
|
|
settings.checkboxStoredRefs[checkbox] = val ? val : "";
|
|
}
|
|
if (!settings.checkboxStoredRefs[checkbox]) {
|
|
return new Set();
|
|
} else {
|
|
return new Set(settings.checkboxStoredRefs[checkbox].split(",").map(r => convert(r)).filter(a => a >= 0));
|
|
}
|
|
}
|
|
|
|
function getCheckboxState(checkbox, references) {
|
|
var storedRefsSet = getStoredCheckboxRefs(checkbox);
|
|
var currentRefsSet = new Set(references.map(r => r[1]));
|
|
// Get difference of current - stored
|
|
var difference = new Set(currentRefsSet);
|
|
for (ref of storedRefsSet) {
|
|
difference.delete(ref);
|
|
}
|
|
if (difference.size == 0) {
|
|
// All the current refs are stored
|
|
return "checked";
|
|
} else if (difference.size == currentRefsSet.size) {
|
|
// None of the current refs are stored
|
|
return "unchecked";
|
|
} else {
|
|
// Some of the refs are stored
|
|
return "indeterminate";
|
|
}
|
|
}
|
|
|
|
function setBomCheckboxState(checkbox, element, references) {
|
|
var state = getCheckboxState(checkbox, references);
|
|
element.checked = (state == "checked");
|
|
element.indeterminate = (state == "indeterminate");
|
|
}
|
|
|
|
function createCheckboxHandlers(input, checkbox, references, row) {
|
|
var clickHandler = () => {
|
|
refsSet = getStoredCheckboxRefs(checkbox);
|
|
var markWhenChecked = settings.markWhenChecked == checkbox;
|
|
eventArgs = {
|
|
checkbox: checkbox,
|
|
refs: references,
|
|
}
|
|
if (input.checked) {
|
|
// checkbox ticked
|
|
for (var ref of references) {
|
|
refsSet.add(ref[1]);
|
|
}
|
|
if (markWhenChecked) {
|
|
row.classList.add("checked");
|
|
for (var ref of references) {
|
|
markedFootprints.add(ref[1]);
|
|
}
|
|
drawHighlights();
|
|
}
|
|
eventArgs.state = 'checked';
|
|
} else {
|
|
// checkbox unticked
|
|
for (var ref of references) {
|
|
refsSet.delete(ref[1]);
|
|
}
|
|
if (markWhenChecked) {
|
|
row.classList.remove("checked");
|
|
for (var ref of references) {
|
|
markedFootprints.delete(ref[1]);
|
|
}
|
|
drawHighlights();
|
|
}
|
|
eventArgs.state = 'unchecked';
|
|
}
|
|
settings.checkboxStoredRefs[checkbox] = [...refsSet].join(",");
|
|
writeStorage("checkbox_" + checkbox, settings.checkboxStoredRefs[checkbox]);
|
|
updateCheckboxStats(checkbox);
|
|
EventHandler.emitEvent(IBOM_EVENT_TYPES.CHECKBOX_CHANGE_EVENT, eventArgs);
|
|
}
|
|
|
|
return [
|
|
(e) => {
|
|
clickHandler();
|
|
},
|
|
(e) => {
|
|
e.preventDefault();
|
|
if (row.onmousemove) row.onmousemove();
|
|
},
|
|
(e) => {
|
|
e.preventDefault();
|
|
input.checked = !input.checked;
|
|
input.indeterminate = false;
|
|
clickHandler();
|
|
}
|
|
];
|
|
}
|
|
|
|
function clearHighlightedFootprints() {
|
|
if (currentHighlightedRowId) {
|
|
document.getElementById(currentHighlightedRowId).classList.remove("highlighted");
|
|
currentHighlightedRowId = null;
|
|
highlightedFootprints = [];
|
|
highlightedNet = null;
|
|
}
|
|
}
|
|
|
|
function createRowHighlightHandler(rowid, refs, net) {
|
|
return function () {
|
|
if (currentHighlightedRowId) {
|
|
if (currentHighlightedRowId == rowid) {
|
|
return;
|
|
}
|
|
document.getElementById(currentHighlightedRowId).classList.remove("highlighted");
|
|
}
|
|
document.getElementById(rowid).classList.add("highlighted");
|
|
currentHighlightedRowId = rowid;
|
|
highlightedFootprints = refs ? refs.map(r => r[1]) : [];
|
|
highlightedNet = net;
|
|
drawHighlights();
|
|
EventHandler.emitEvent(
|
|
IBOM_EVENT_TYPES.HIGHLIGHT_EVENT, {
|
|
rowid: rowid,
|
|
refs: refs,
|
|
net: net
|
|
});
|
|
}
|
|
}
|
|
|
|
function updateNetColors() {
|
|
writeStorage("netColors", JSON.stringify(settings.netColors));
|
|
redrawIfInitDone();
|
|
}
|
|
|
|
function netColorChangeHandler(net) {
|
|
return (event) => {
|
|
settings.netColors[net] = event.target.value;
|
|
updateNetColors();
|
|
}
|
|
}
|
|
|
|
function netColorRightClick(net) {
|
|
return (event) => {
|
|
if (event.button == 2) {
|
|
event.preventDefault();
|
|
event.stopPropagation();
|
|
|
|
var style = getComputedStyle(topmostdiv);
|
|
var defaultNetColor = style.getPropertyValue('--track-color').trim();
|
|
event.target.value = defaultNetColor;
|
|
delete settings.netColors[net];
|
|
updateNetColors();
|
|
}
|
|
}
|
|
}
|
|
|
|
function entryMatches(entry) {
|
|
if (settings.bommode == "netlist") {
|
|
// entry is just a net name
|
|
return entry.toLowerCase().indexOf(filter) >= 0;
|
|
}
|
|
// check refs
|
|
if (!settings.hiddenColumns.includes("References")) {
|
|
for (var ref of entry) {
|
|
if (ref[0].toLowerCase().indexOf(filter) >= 0) {
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
// check fields
|
|
for (var i in config.fields) {
|
|
var f = config.fields[i];
|
|
if (!settings.hiddenColumns.includes(f)) {
|
|
for (var ref of entry) {
|
|
if (String(pcbdata.bom.fields[ref[1]][i]).toLowerCase().indexOf(filter) >= 0) {
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
function findRefInEntry(entry) {
|
|
return entry.filter(r => r[0].toLowerCase() == reflookup);
|
|
}
|
|
|
|
function highlightFilter(s) {
|
|
if (!filter) {
|
|
return s;
|
|
}
|
|
var parts = s.toLowerCase().split(filter);
|
|
if (parts.length == 1) {
|
|
return s;
|
|
}
|
|
var r = "";
|
|
var pos = 0;
|
|
for (var i in parts) {
|
|
if (i > 0) {
|
|
r += '<mark class="highlight">' +
|
|
s.substring(pos, pos + filter.length) +
|
|
'</mark>';
|
|
pos += filter.length;
|
|
}
|
|
r += s.substring(pos, pos + parts[i].length);
|
|
pos += parts[i].length;
|
|
}
|
|
return r;
|
|
}
|
|
|
|
function getBomListByLayer(layer) {
|
|
switch (layer) {
|
|
case 'F': return pcbdata.bom.F.slice();
|
|
case 'B': return pcbdata.bom.B.slice();
|
|
case 'FB': return pcbdata.bom.both.slice();
|
|
}
|
|
return [];
|
|
}
|
|
|
|
function getSelectedBomList() {
|
|
if (settings.bommode == "netlist") {
|
|
return pcbdata.nets.slice();
|
|
}
|
|
var out = getBomListByLayer(settings.canvaslayout);
|
|
|
|
if (settings.bommode == "ungrouped") {
|
|
// expand bom table
|
|
var expandedTable = [];
|
|
for (var bomentry of out) {
|
|
for (var ref of bomentry) {
|
|
expandedTable.push([ref]);
|
|
}
|
|
}
|
|
return expandedTable;
|
|
}
|
|
|
|
return out;
|
|
}
|
|
|
|
function checkboxSetUnsetAllHandler(checkboxname) {
|
|
return function () {
|
|
var checkboxnum = 0;
|
|
while (checkboxnum < settings.checkboxes.length &&
|
|
settings.checkboxes[checkboxnum].toLowerCase() != checkboxname.toLowerCase()) {
|
|
checkboxnum++;
|
|
}
|
|
if (checkboxnum >= settings.checkboxes.length) {
|
|
return;
|
|
}
|
|
var allset = true;
|
|
var checkbox;
|
|
var row;
|
|
for (row of bombody.childNodes) {
|
|
checkbox = row.childNodes[checkboxnum + 1].childNodes[0];
|
|
if (!checkbox.checked || checkbox.indeterminate) {
|
|
allset = false;
|
|
break;
|
|
}
|
|
}
|
|
for (row of bombody.childNodes) {
|
|
checkbox = row.childNodes[checkboxnum + 1].childNodes[0];
|
|
checkbox.checked = !allset;
|
|
checkbox.indeterminate = false;
|
|
checkbox.onchange();
|
|
}
|
|
}
|
|
}
|
|
|
|
function createColumnHeader(name, cls, comparator, is_checkbox = false) {
|
|
var th = document.createElement("TH");
|
|
th.innerHTML = name;
|
|
th.classList.add(cls);
|
|
if (is_checkbox)
|
|
th.setAttribute("col_name", "bom-checkbox");
|
|
else
|
|
th.setAttribute("col_name", name);
|
|
var span = document.createElement("SPAN");
|
|
span.classList.add("sortmark");
|
|
span.classList.add("none");
|
|
th.appendChild(span);
|
|
var spacer = document.createElement("div");
|
|
spacer.className = "column-spacer";
|
|
th.appendChild(spacer);
|
|
spacer.onclick = function () {
|
|
if (currentSortColumn && th !== currentSortColumn) {
|
|
// Currently sorted by another column
|
|
currentSortColumn.childNodes[1].classList.remove(currentSortOrder);
|
|
currentSortColumn.childNodes[1].classList.add("none");
|
|
currentSortColumn = null;
|
|
currentSortOrder = null;
|
|
}
|
|
if (currentSortColumn && th === currentSortColumn) {
|
|
// Already sorted by this column
|
|
if (currentSortOrder == "asc") {
|
|
// Sort by this column, descending order
|
|
bomSortFunction = function (a, b) {
|
|
return -comparator(a, b);
|
|
}
|
|
currentSortColumn.childNodes[1].classList.remove("asc");
|
|
currentSortColumn.childNodes[1].classList.add("desc");
|
|
currentSortOrder = "desc";
|
|
} else {
|
|
// Unsort
|
|
bomSortFunction = null;
|
|
currentSortColumn.childNodes[1].classList.remove("desc");
|
|
currentSortColumn.childNodes[1].classList.add("none");
|
|
currentSortColumn = null;
|
|
currentSortOrder = null;
|
|
}
|
|
} else {
|
|
// Sort by this column, ascending order
|
|
bomSortFunction = comparator;
|
|
currentSortColumn = th;
|
|
currentSortColumn.childNodes[1].classList.remove("none");
|
|
currentSortColumn.childNodes[1].classList.add("asc");
|
|
currentSortOrder = "asc";
|
|
}
|
|
populateBomBody();
|
|
}
|
|
if (is_checkbox) {
|
|
spacer.onclick = fancyDblClickHandler(
|
|
spacer, spacer.onclick, checkboxSetUnsetAllHandler(name));
|
|
}
|
|
return th;
|
|
}
|
|
|
|
function populateBomHeader(placeHolderColumn = null, placeHolderElements = null) {
|
|
while (bomhead.firstChild) {
|
|
bomhead.removeChild(bomhead.firstChild);
|
|
}
|
|
var tr = document.createElement("TR");
|
|
var th = document.createElement("TH");
|
|
th.classList.add("numCol");
|
|
|
|
var vismenu = document.createElement("div");
|
|
vismenu.id = "vismenu";
|
|
vismenu.classList.add("menu");
|
|
|
|
var visbutton = document.createElement("div");
|
|
visbutton.classList.add("visbtn");
|
|
visbutton.classList.add("hideonprint");
|
|
|
|
var viscontent = document.createElement("div");
|
|
viscontent.classList.add("menu-content");
|
|
viscontent.id = "vismenu-content";
|
|
|
|
settings.columnOrder.forEach(column => {
|
|
if (typeof column !== "string")
|
|
return;
|
|
|
|
// Skip empty columns
|
|
if (column === "checkboxes" && settings.checkboxes.length == 0)
|
|
return;
|
|
else if (column === "Quantity" && settings.bommode == "ungrouped")
|
|
return;
|
|
|
|
var label = document.createElement("label");
|
|
label.classList.add("menu-label");
|
|
|
|
var input = document.createElement("input");
|
|
input.classList.add("visibility_checkbox");
|
|
input.type = "checkbox";
|
|
input.onchange = function (e) {
|
|
setShowBOMColumn(column, e.target.checked)
|
|
};
|
|
input.checked = !(settings.hiddenColumns.includes(column));
|
|
|
|
label.appendChild(input);
|
|
if (column.length > 0)
|
|
label.append(column[0].toUpperCase() + column.slice(1));
|
|
|
|
viscontent.appendChild(label);
|
|
});
|
|
|
|
viscontent.childNodes[0].classList.add("menu-label-top");
|
|
|
|
vismenu.appendChild(visbutton);
|
|
if (settings.bommode != "netlist") {
|
|
vismenu.appendChild(viscontent);
|
|
th.appendChild(vismenu);
|
|
}
|
|
tr.appendChild(th);
|
|
|
|
var checkboxCompareClosure = function (checkbox) {
|
|
return (a, b) => {
|
|
var stateA = getCheckboxState(checkbox, a);
|
|
var stateB = getCheckboxState(checkbox, b);
|
|
if (stateA > stateB) return -1;
|
|
if (stateA < stateB) return 1;
|
|
return 0;
|
|
}
|
|
}
|
|
var stringFieldCompareClosure = function (fieldIndex) {
|
|
return (a, b) => {
|
|
var fa = pcbdata.bom.fields[a[0][1]][fieldIndex];
|
|
var fb = pcbdata.bom.fields[b[0][1]][fieldIndex];
|
|
if (fa != fb) return fa > fb ? 1 : -1;
|
|
else return 0;
|
|
}
|
|
}
|
|
var referenceRegex = /(?<prefix>[^0-9]+)(?<number>[0-9]+)/;
|
|
var compareRefs = (a, b) => {
|
|
var ra = referenceRegex.exec(a);
|
|
var rb = referenceRegex.exec(b);
|
|
if (ra === null || rb === null) {
|
|
if (a != b) return a > b ? 1 : -1;
|
|
return 0;
|
|
} else {
|
|
if (ra.groups.prefix != rb.groups.prefix) {
|
|
return ra.groups.prefix > rb.groups.prefix ? 1 : -1;
|
|
}
|
|
if (ra.groups.number != rb.groups.number) {
|
|
return parseInt(ra.groups.number) > parseInt(rb.groups.number) ? 1 : -1;
|
|
}
|
|
return 0;
|
|
}
|
|
}
|
|
if (settings.bommode == "netlist") {
|
|
tr.appendChild(createColumnHeader("Net name", "bom-netname", (a, b) => {
|
|
if (a > b) return -1;
|
|
if (a < b) return 1;
|
|
return 0;
|
|
}));
|
|
tr.appendChild(createColumnHeader("Color", "bom-color", (a, b) => {
|
|
return 0;
|
|
}));
|
|
} else {
|
|
// Filter hidden columns
|
|
var columns = settings.columnOrder.filter(e => !settings.hiddenColumns.includes(e));
|
|
var valueIndex = config.fields.indexOf("Value");
|
|
var footprintIndex = config.fields.indexOf("Footprint");
|
|
columns.forEach((column) => {
|
|
if (column === placeHolderColumn) {
|
|
var n = 1;
|
|
if (column === "checkboxes")
|
|
n = settings.checkboxes.length;
|
|
for (i = 0; i < n; i++) {
|
|
td = placeHolderElements.shift();
|
|
tr.appendChild(td);
|
|
}
|
|
return;
|
|
} else if (column === "checkboxes") {
|
|
for (var checkbox of settings.checkboxes) {
|
|
th = createColumnHeader(
|
|
checkbox, "bom-checkbox", checkboxCompareClosure(checkbox), true);
|
|
tr.appendChild(th);
|
|
}
|
|
} else if (column === "References") {
|
|
tr.appendChild(createColumnHeader("References", "references", (a, b) => {
|
|
var i = 0;
|
|
while (i < a.length && i < b.length) {
|
|
if (a[i][0] != b[i][0]) return compareRefs(a[i][0], b[i][0]);
|
|
i++;
|
|
}
|
|
return a.length - b.length;
|
|
}));
|
|
} else if (column === "Value") {
|
|
tr.appendChild(createColumnHeader("Value", "value", (a, b) => {
|
|
var ra = a[0][1], rb = b[0][1];
|
|
return valueCompare(
|
|
pcbdata.bom.parsedValues[ra], pcbdata.bom.parsedValues[rb],
|
|
pcbdata.bom.fields[ra][valueIndex], pcbdata.bom.fields[rb][valueIndex]);
|
|
}));
|
|
return;
|
|
} else if (column === "Footprint") {
|
|
tr.appendChild(createColumnHeader(
|
|
"Footprint", "footprint", stringFieldCompareClosure(footprintIndex)));
|
|
} else if (column === "Quantity" && settings.bommode == "grouped") {
|
|
tr.appendChild(createColumnHeader("Quantity", "quantity", (a, b) => {
|
|
return a.length - b.length;
|
|
}));
|
|
} else {
|
|
// Other fields
|
|
var i = config.fields.indexOf(column);
|
|
if (i < 0)
|
|
return;
|
|
tr.appendChild(createColumnHeader(
|
|
column, `field${i + 1}`, stringFieldCompareClosure(i)));
|
|
}
|
|
});
|
|
}
|
|
bomhead.appendChild(tr);
|
|
}
|
|
|
|
function populateBomBody(placeholderColumn = null, placeHolderElements = null) {
|
|
const urlRegex = /^(https?:\/\/[^\s\/$.?#][^\s]*|file:\/\/([a-zA-Z]:|\/)[^\x00]+)$/;
|
|
while (bom.firstChild) {
|
|
bom.removeChild(bom.firstChild);
|
|
}
|
|
highlightHandlers = [];
|
|
footprintIndexToHandler = {};
|
|
netsToHandler = {};
|
|
currentHighlightedRowId = null;
|
|
var first = true;
|
|
var style = getComputedStyle(topmostdiv);
|
|
var defaultNetColor = style.getPropertyValue('--track-color').trim();
|
|
|
|
bomtable = getSelectedBomList();
|
|
|
|
if (bomSortFunction) {
|
|
bomtable = bomtable.sort(bomSortFunction);
|
|
}
|
|
for (var i in bomtable) {
|
|
var bomentry = bomtable[i];
|
|
if (filter && !entryMatches(bomentry)) {
|
|
continue;
|
|
}
|
|
var references = null;
|
|
var netname = null;
|
|
var tr = document.createElement("TR");
|
|
var td = document.createElement("TD");
|
|
var rownum = +i + 1;
|
|
tr.id = "bomrow" + rownum;
|
|
td.textContent = rownum;
|
|
tr.appendChild(td);
|
|
if (settings.bommode == "netlist") {
|
|
netname = bomentry;
|
|
td = document.createElement("TD");
|
|
td.innerHTML = highlightFilter(netname ? netname : "<no net>");
|
|
tr.appendChild(td);
|
|
var color = settings.netColors[netname] || defaultNetColor;
|
|
td = document.createElement("TD");
|
|
var colorBox = document.createElement("INPUT");
|
|
colorBox.type = "color";
|
|
colorBox.value = color;
|
|
colorBox.onchange = netColorChangeHandler(netname);
|
|
colorBox.onmouseup = netColorRightClick(netname);
|
|
colorBox.oncontextmenu = (e) => e.preventDefault();
|
|
td.appendChild(colorBox);
|
|
td.classList.add("color-column");
|
|
tr.appendChild(td);
|
|
} else {
|
|
if (reflookup) {
|
|
references = findRefInEntry(bomentry);
|
|
if (references.length == 0) {
|
|
continue;
|
|
}
|
|
} else {
|
|
references = bomentry;
|
|
}
|
|
// Filter hidden columns
|
|
var columns = settings.columnOrder.filter(e => !settings.hiddenColumns.includes(e));
|
|
columns.forEach((column) => {
|
|
if (column === placeholderColumn) {
|
|
var n = 1;
|
|
if (column === "checkboxes")
|
|
n = settings.checkboxes.length;
|
|
for (i = 0; i < n; i++) {
|
|
td = placeHolderElements.shift();
|
|
tr.appendChild(td);
|
|
}
|
|
return;
|
|
} else if (column === "checkboxes") {
|
|
for (var checkbox of settings.checkboxes) {
|
|
if (checkbox) {
|
|
td = document.createElement("TD");
|
|
var input = document.createElement("input");
|
|
input.type = "checkbox";
|
|
[input.onchange, td.ontouchstart, td.ontouchend] = createCheckboxHandlers(input, checkbox, references, tr);
|
|
setBomCheckboxState(checkbox, input, references);
|
|
if (input.checked && settings.markWhenChecked == checkbox) {
|
|
tr.classList.add("checked");
|
|
}
|
|
td.appendChild(input);
|
|
tr.appendChild(td);
|
|
}
|
|
}
|
|
} else if (column === "References") {
|
|
td = document.createElement("TD");
|
|
td.innerHTML = highlightFilter(references.map(r => r[0]).join(", "));
|
|
tr.appendChild(td);
|
|
} else if (column === "Quantity" && settings.bommode == "grouped") {
|
|
// Quantity
|
|
td = document.createElement("TD");
|
|
td.textContent = references.length;
|
|
tr.appendChild(td);
|
|
} else {
|
|
// All the other fields
|
|
var field_index = config.fields.indexOf(column)
|
|
if (field_index < 0)
|
|
return;
|
|
var valueSet = new Set();
|
|
references.map(r => r[1]).forEach((id) => valueSet.add(pcbdata.bom.fields[id][field_index]));
|
|
td = document.createElement("TD");
|
|
var output = new Array();
|
|
for (let item of valueSet) {
|
|
const visible = highlightFilter(String(item));
|
|
if (typeof item === 'string' && item.match(urlRegex)) {
|
|
output.push(`<a href="${item}" target="_blank">${visible}</a>`);
|
|
} else {
|
|
output.push(visible);
|
|
}
|
|
}
|
|
td.innerHTML = output.join(", ");
|
|
tr.appendChild(td);
|
|
}
|
|
});
|
|
}
|
|
bom.appendChild(tr);
|
|
var handler = createRowHighlightHandler(tr.id, references, netname);
|
|
if (settings.highlightRowOnClick) {
|
|
tr.onmousedown = handler;
|
|
} else {
|
|
tr.onmousemove = handler;
|
|
}
|
|
highlightHandlers.push({
|
|
id: tr.id,
|
|
handler: handler,
|
|
});
|
|
if (references !== null) {
|
|
for (var refIndex of references.map(r => r[1])) {
|
|
footprintIndexToHandler[refIndex] = handler;
|
|
}
|
|
}
|
|
if (netname !== null) {
|
|
netsToHandler[netname] = handler;
|
|
}
|
|
if ((filter || reflookup) && first) {
|
|
handler();
|
|
first = false;
|
|
}
|
|
}
|
|
EventHandler.emitEvent(
|
|
IBOM_EVENT_TYPES.BOM_BODY_CHANGE_EVENT, {
|
|
filter: filter,
|
|
reflookup: reflookup,
|
|
checkboxes: settings.checkboxes,
|
|
bommode: settings.bommode,
|
|
});
|
|
}
|
|
|
|
function highlightPreviousRow() {
|
|
if (!currentHighlightedRowId) {
|
|
highlightHandlers[highlightHandlers.length - 1].handler();
|
|
} else {
|
|
if (highlightHandlers.length > 1 &&
|
|
highlightHandlers[0].id == currentHighlightedRowId) {
|
|
highlightHandlers[highlightHandlers.length - 1].handler();
|
|
} else {
|
|
for (var i = 0; i < highlightHandlers.length - 1; i++) {
|
|
if (highlightHandlers[i + 1].id == currentHighlightedRowId) {
|
|
highlightHandlers[i].handler();
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
smoothScrollToRow(currentHighlightedRowId);
|
|
}
|
|
|
|
function highlightNextRow() {
|
|
if (!currentHighlightedRowId) {
|
|
highlightHandlers[0].handler();
|
|
} else {
|
|
if (highlightHandlers.length > 1 &&
|
|
highlightHandlers[highlightHandlers.length - 1].id == currentHighlightedRowId) {
|
|
highlightHandlers[0].handler();
|
|
} else {
|
|
for (var i = 1; i < highlightHandlers.length; i++) {
|
|
if (highlightHandlers[i - 1].id == currentHighlightedRowId) {
|
|
highlightHandlers[i].handler();
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
smoothScrollToRow(currentHighlightedRowId);
|
|
}
|
|
|
|
function populateBomTable() {
|
|
populateBomHeader();
|
|
populateBomBody();
|
|
setBomHandlers();
|
|
resizableGrid(bomhead);
|
|
}
|
|
|
|
function footprintsClicked(footprintIndexes) {
|
|
var lastClickedIndex = footprintIndexes.indexOf(lastClicked);
|
|
for (var i = 1; i <= footprintIndexes.length; i++) {
|
|
var refIndex = footprintIndexes[(lastClickedIndex + i) % footprintIndexes.length];
|
|
if (refIndex in footprintIndexToHandler) {
|
|
lastClicked = refIndex;
|
|
footprintIndexToHandler[refIndex]();
|
|
smoothScrollToRow(currentHighlightedRowId);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
function netClicked(net) {
|
|
if (net in netsToHandler) {
|
|
netsToHandler[net]();
|
|
smoothScrollToRow(currentHighlightedRowId);
|
|
} else {
|
|
clearHighlightedFootprints();
|
|
highlightedNet = net;
|
|
drawHighlights();
|
|
}
|
|
}
|
|
|
|
function updateFilter(input) {
|
|
filter = input.toLowerCase();
|
|
populateBomTable();
|
|
}
|
|
|
|
function updateRefLookup(input) {
|
|
reflookup = input.toLowerCase();
|
|
populateBomTable();
|
|
}
|
|
|
|
function changeCanvasLayout(layout) {
|
|
document.getElementById("fl-btn").classList.remove("depressed");
|
|
document.getElementById("fb-btn").classList.remove("depressed");
|
|
document.getElementById("bl-btn").classList.remove("depressed");
|
|
switch (layout) {
|
|
case 'F':
|
|
document.getElementById("fl-btn").classList.add("depressed");
|
|
if (settings.bomlayout != "bom-only") {
|
|
canvassplit.collapse(1);
|
|
}
|
|
break;
|
|
case 'B':
|
|
document.getElementById("bl-btn").classList.add("depressed");
|
|
if (settings.bomlayout != "bom-only") {
|
|
canvassplit.collapse(0);
|
|
}
|
|
break;
|
|
default:
|
|
document.getElementById("fb-btn").classList.add("depressed");
|
|
if (settings.bomlayout != "bom-only") {
|
|
canvassplit.setSizes([50, 50]);
|
|
}
|
|
}
|
|
settings.canvaslayout = layout;
|
|
writeStorage("canvaslayout", layout);
|
|
resizeAll();
|
|
changeBomMode(settings.bommode);
|
|
}
|
|
|
|
function populateMetadata() {
|
|
document.getElementById("title").innerHTML = pcbdata.metadata.title;
|
|
document.getElementById("revision").innerHTML = "Rev: " + pcbdata.metadata.revision;
|
|
document.getElementById("company").innerHTML = pcbdata.metadata.company;
|
|
document.getElementById("filedate").innerHTML = pcbdata.metadata.date;
|
|
if (pcbdata.metadata.title != "") {
|
|
document.title = pcbdata.metadata.title + " BOM";
|
|
}
|
|
// Calculate board stats
|
|
var fp_f = 0,
|
|
fp_b = 0,
|
|
pads_f = 0,
|
|
pads_b = 0,
|
|
pads_th = 0;
|
|
for (var i = 0; i < pcbdata.footprints.length; i++) {
|
|
if (pcbdata.bom.skipped.includes(i)) continue;
|
|
var mod = pcbdata.footprints[i];
|
|
if (mod.layer == "F") {
|
|
fp_f++;
|
|
} else {
|
|
fp_b++;
|
|
}
|
|
for (var pad of mod.pads) {
|
|
if (pad.type == "th") {
|
|
pads_th++;
|
|
} else {
|
|
if (pad.layers.includes("F")) {
|
|
pads_f++;
|
|
}
|
|
if (pad.layers.includes("B")) {
|
|
pads_b++;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
document.getElementById("stats-components-front").innerHTML = fp_f;
|
|
document.getElementById("stats-components-back").innerHTML = fp_b;
|
|
document.getElementById("stats-components-total").innerHTML = fp_f + fp_b;
|
|
document.getElementById("stats-groups-front").innerHTML = pcbdata.bom.F.length;
|
|
document.getElementById("stats-groups-back").innerHTML = pcbdata.bom.B.length;
|
|
document.getElementById("stats-groups-total").innerHTML = pcbdata.bom.both.length;
|
|
document.getElementById("stats-smd-pads-front").innerHTML = pads_f;
|
|
document.getElementById("stats-smd-pads-back").innerHTML = pads_b;
|
|
document.getElementById("stats-smd-pads-total").innerHTML = pads_f + pads_b;
|
|
document.getElementById("stats-th-pads").innerHTML = pads_th;
|
|
// Update version string
|
|
document.getElementById("github-link").innerHTML = "InteractiveHtmlBom " +
|
|
/^v\d+\.\d+/.exec(pcbdata.ibom_version)[0];
|
|
}
|
|
|
|
function changeBomLayout(layout) {
|
|
document.getElementById("bom-btn").classList.remove("depressed");
|
|
document.getElementById("lr-btn").classList.remove("depressed");
|
|
document.getElementById("tb-btn").classList.remove("depressed");
|
|
switch (layout) {
|
|
case 'bom-only':
|
|
document.getElementById("bom-btn").classList.add("depressed");
|
|
if (bomsplit) {
|
|
bomsplit.destroy();
|
|
bomsplit = null;
|
|
canvassplit.destroy();
|
|
canvassplit = null;
|
|
}
|
|
document.getElementById("frontcanvas").style.display = "none";
|
|
document.getElementById("backcanvas").style.display = "none";
|
|
document.getElementById("topmostdiv").style.height = "";
|
|
document.getElementById("topmostdiv").style.display = "block";
|
|
break;
|
|
case 'top-bottom':
|
|
document.getElementById("tb-btn").classList.add("depressed");
|
|
document.getElementById("frontcanvas").style.display = "";
|
|
document.getElementById("backcanvas").style.display = "";
|
|
document.getElementById("topmostdiv").style.height = "100%";
|
|
document.getElementById("topmostdiv").style.display = "flex";
|
|
document.getElementById("bomdiv").classList.remove("split-horizontal");
|
|
document.getElementById("canvasdiv").classList.remove("split-horizontal");
|
|
document.getElementById("frontcanvas").classList.add("split-horizontal");
|
|
document.getElementById("backcanvas").classList.add("split-horizontal");
|
|
if (bomsplit) {
|
|
bomsplit.destroy();
|
|
bomsplit = null;
|
|
canvassplit.destroy();
|
|
canvassplit = null;
|
|
}
|
|
bomsplit = Split(['#bomdiv', '#canvasdiv'], {
|
|
sizes: [50, 50],
|
|
onDragEnd: resizeAll,
|
|
direction: "vertical",
|
|
gutterSize: 5
|
|
});
|
|
canvassplit = Split(['#frontcanvas', '#backcanvas'], {
|
|
sizes: [50, 50],
|
|
gutterSize: 5,
|
|
onDragEnd: resizeAll
|
|
});
|
|
break;
|
|
case 'left-right':
|
|
document.getElementById("lr-btn").classList.add("depressed");
|
|
document.getElementById("frontcanvas").style.display = "";
|
|
document.getElementById("backcanvas").style.display = "";
|
|
document.getElementById("topmostdiv").style.height = "100%";
|
|
document.getElementById("topmostdiv").style.display = "flex";
|
|
document.getElementById("bomdiv").classList.add("split-horizontal");
|
|
document.getElementById("canvasdiv").classList.add("split-horizontal");
|
|
document.getElementById("frontcanvas").classList.remove("split-horizontal");
|
|
document.getElementById("backcanvas").classList.remove("split-horizontal");
|
|
if (bomsplit) {
|
|
bomsplit.destroy();
|
|
bomsplit = null;
|
|
canvassplit.destroy();
|
|
canvassplit = null;
|
|
}
|
|
bomsplit = Split(['#bomdiv', '#canvasdiv'], {
|
|
sizes: [50, 50],
|
|
onDragEnd: resizeAll,
|
|
gutterSize: 5
|
|
});
|
|
canvassplit = Split(['#frontcanvas', '#backcanvas'], {
|
|
sizes: [50, 50],
|
|
gutterSize: 5,
|
|
direction: "vertical",
|
|
onDragEnd: resizeAll
|
|
});
|
|
}
|
|
settings.bomlayout = layout;
|
|
writeStorage("bomlayout", layout);
|
|
changeCanvasLayout(settings.canvaslayout);
|
|
}
|
|
|
|
function changeBomMode(mode) {
|
|
document.getElementById("bom-grouped-btn").classList.remove("depressed");
|
|
document.getElementById("bom-ungrouped-btn").classList.remove("depressed");
|
|
document.getElementById("bom-netlist-btn").classList.remove("depressed");
|
|
var chkbxs = document.getElementsByClassName("visibility_checkbox");
|
|
|
|
switch (mode) {
|
|
case 'grouped':
|
|
document.getElementById("bom-grouped-btn").classList.add("depressed");
|
|
for (var i = 0; i < chkbxs.length; i++) {
|
|
chkbxs[i].disabled = false;
|
|
}
|
|
break;
|
|
case 'ungrouped':
|
|
document.getElementById("bom-ungrouped-btn").classList.add("depressed");
|
|
for (var i = 0; i < chkbxs.length; i++) {
|
|
chkbxs[i].disabled = false;
|
|
}
|
|
break;
|
|
case 'netlist':
|
|
document.getElementById("bom-netlist-btn").classList.add("depressed");
|
|
for (var i = 0; i < chkbxs.length; i++) {
|
|
chkbxs[i].disabled = true;
|
|
}
|
|
}
|
|
|
|
writeStorage("bommode", mode);
|
|
if (mode != settings.bommode) {
|
|
settings.bommode = mode;
|
|
bomSortFunction = null;
|
|
currentSortColumn = null;
|
|
currentSortOrder = null;
|
|
clearHighlightedFootprints();
|
|
}
|
|
populateBomTable();
|
|
}
|
|
|
|
function focusFilterField() {
|
|
focusInputField(document.getElementById("filter"));
|
|
}
|
|
|
|
function focusRefLookupField() {
|
|
focusInputField(document.getElementById("reflookup"));
|
|
}
|
|
|
|
function toggleBomCheckbox(bomrowid, checkboxnum) {
|
|
if (!bomrowid || checkboxnum > settings.checkboxes.length) {
|
|
return;
|
|
}
|
|
var bomrow = document.getElementById(bomrowid);
|
|
var childNum = checkboxnum + settings.columnOrder.indexOf("checkboxes");
|
|
var checkbox = bomrow.childNodes[childNum].childNodes[0];
|
|
checkbox.checked = !checkbox.checked;
|
|
checkbox.indeterminate = false;
|
|
checkbox.onchange();
|
|
}
|
|
|
|
function checkBomCheckbox(bomrowid, checkboxname) {
|
|
var checkboxnum = 0;
|
|
while (checkboxnum < settings.checkboxes.length &&
|
|
settings.checkboxes[checkboxnum].toLowerCase() != checkboxname.toLowerCase()) {
|
|
checkboxnum++;
|
|
}
|
|
if (!bomrowid || checkboxnum >= settings.checkboxes.length) {
|
|
return;
|
|
}
|
|
var bomrow = document.getElementById(bomrowid);
|
|
var childNum = checkboxnum + 1 + settings.columnOrder.indexOf("checkboxes");
|
|
var checkbox = bomrow.childNodes[childNum].childNodes[0];
|
|
checkbox.checked = true;
|
|
checkbox.indeterminate = false;
|
|
checkbox.onchange();
|
|
}
|
|
|
|
function setBomCheckboxes(value) {
|
|
writeStorage("bomCheckboxes", value);
|
|
settings.checkboxes = value.split(",").map((e) => e.trim()).filter((e) => e);
|
|
prepCheckboxes();
|
|
populateMarkWhenCheckedOptions();
|
|
setMarkWhenChecked(settings.markWhenChecked);
|
|
}
|
|
|
|
function setMarkWhenChecked(value) {
|
|
writeStorage("markWhenChecked", value);
|
|
settings.markWhenChecked = value;
|
|
markedFootprints.clear();
|
|
for (var ref of (value ? getStoredCheckboxRefs(value) : [])) {
|
|
markedFootprints.add(ref);
|
|
}
|
|
populateBomTable();
|
|
drawHighlights();
|
|
}
|
|
|
|
function prepCheckboxes() {
|
|
var table = document.getElementById("checkbox-stats");
|
|
while (table.childElementCount > 1) {
|
|
table.removeChild(table.lastChild);
|
|
}
|
|
if (settings.checkboxes.length) {
|
|
table.style.display = "";
|
|
} else {
|
|
table.style.display = "none";
|
|
}
|
|
for (var checkbox of settings.checkboxes) {
|
|
var tr = document.createElement("TR");
|
|
var td = document.createElement("TD");
|
|
td.innerHTML = checkbox;
|
|
tr.appendChild(td);
|
|
td = document.createElement("TD");
|
|
td.id = "checkbox-stats-" + checkbox;
|
|
var progressbar = document.createElement("div");
|
|
progressbar.classList.add("bar");
|
|
td.appendChild(progressbar);
|
|
var text = document.createElement("div");
|
|
text.classList.add("text");
|
|
td.appendChild(text);
|
|
tr.appendChild(td);
|
|
table.appendChild(tr);
|
|
updateCheckboxStats(checkbox);
|
|
}
|
|
}
|
|
|
|
function populateMarkWhenCheckedOptions() {
|
|
var container = document.getElementById("markWhenCheckedContainer");
|
|
|
|
if (settings.checkboxes.length == 0) {
|
|
container.parentElement.style.display = "none";
|
|
return;
|
|
}
|
|
|
|
container.innerHTML = '';
|
|
container.parentElement.style.display = "inline-block";
|
|
|
|
function createOption(name, displayName) {
|
|
var id = "markWhenChecked-" + name;
|
|
|
|
var div = document.createElement("div");
|
|
div.classList.add("radio-container");
|
|
|
|
var input = document.createElement("input");
|
|
input.type = "radio";
|
|
input.name = "markWhenChecked";
|
|
input.value = name;
|
|
input.id = id;
|
|
input.onchange = () => setMarkWhenChecked(name);
|
|
div.appendChild(input);
|
|
|
|
// Preserve the selected element when the checkboxes change
|
|
if (name == settings.markWhenChecked) {
|
|
input.checked = true;
|
|
}
|
|
|
|
var label = document.createElement("label");
|
|
label.innerHTML = displayName;
|
|
label.htmlFor = id;
|
|
div.appendChild(label);
|
|
|
|
container.appendChild(div);
|
|
}
|
|
createOption("", "None");
|
|
for (var checkbox of settings.checkboxes) {
|
|
createOption(checkbox, checkbox);
|
|
}
|
|
}
|
|
|
|
function updateCheckboxStats(checkbox) {
|
|
var checked = getStoredCheckboxRefs(checkbox).size;
|
|
var total = pcbdata.footprints.length - pcbdata.bom.skipped.length;
|
|
var percent = checked * 100.0 / total;
|
|
var td = document.getElementById("checkbox-stats-" + checkbox);
|
|
td.firstChild.style.width = percent + "%";
|
|
td.lastChild.innerHTML = checked + "/" + total + " (" + Math.round(percent) + "%)";
|
|
}
|
|
|
|
function constrain(number, min, max) {
|
|
return Math.min(Math.max(parseInt(number), min), max);
|
|
}
|
|
|
|
document.onkeydown = function (e) {
|
|
switch (e.key) {
|
|
case "n":
|
|
if (document.activeElement.type == "text") {
|
|
return;
|
|
}
|
|
if (currentHighlightedRowId !== null) {
|
|
checkBomCheckbox(currentHighlightedRowId, "placed");
|
|
highlightNextRow();
|
|
e.preventDefault();
|
|
}
|
|
break;
|
|
case "ArrowUp":
|
|
highlightPreviousRow();
|
|
e.preventDefault();
|
|
break;
|
|
case "ArrowDown":
|
|
highlightNextRow();
|
|
e.preventDefault();
|
|
break;
|
|
case "ArrowLeft":
|
|
case "ArrowRight":
|
|
if (document.activeElement.type != "text") {
|
|
e.preventDefault();
|
|
let boardRotationElement = document.getElementById("boardRotation")
|
|
settings.boardRotation = parseInt(boardRotationElement.value); // degrees / 5
|
|
if (e.key == "ArrowLeft") {
|
|
settings.boardRotation += 3; // 15 degrees
|
|
}
|
|
else {
|
|
settings.boardRotation -= 3;
|
|
}
|
|
settings.boardRotation = constrain(settings.boardRotation, boardRotationElement.min, boardRotationElement.max);
|
|
boardRotationElement.value = settings.boardRotation
|
|
setBoardRotation(settings.boardRotation);
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
if (e.altKey) {
|
|
switch (e.key) {
|
|
case "f":
|
|
focusFilterField();
|
|
e.preventDefault();
|
|
break;
|
|
case "r":
|
|
focusRefLookupField();
|
|
e.preventDefault();
|
|
break;
|
|
case "z":
|
|
changeBomLayout("bom-only");
|
|
e.preventDefault();
|
|
break;
|
|
case "x":
|
|
changeBomLayout("left-right");
|
|
e.preventDefault();
|
|
break;
|
|
case "c":
|
|
changeBomLayout("top-bottom");
|
|
e.preventDefault();
|
|
break;
|
|
case "v":
|
|
changeCanvasLayout("F");
|
|
e.preventDefault();
|
|
break;
|
|
case "b":
|
|
changeCanvasLayout("FB");
|
|
e.preventDefault();
|
|
break;
|
|
case "n":
|
|
changeCanvasLayout("B");
|
|
e.preventDefault();
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
if (e.key >= '1' && e.key <= '9') {
|
|
toggleBomCheckbox(currentHighlightedRowId, parseInt(e.key));
|
|
e.preventDefault();
|
|
}
|
|
}
|
|
}
|
|
|
|
function hideNetlistButton() {
|
|
document.getElementById("bom-ungrouped-btn").classList.remove("middle-button");
|
|
document.getElementById("bom-ungrouped-btn").classList.add("right-most-button");
|
|
document.getElementById("bom-netlist-btn").style.display = "none";
|
|
}
|
|
|
|
function topToggle() {
|
|
var top = document.getElementById("top");
|
|
var toptoggle = document.getElementById("toptoggle");
|
|
if (top.style.display === "none") {
|
|
top.style.display = "flex";
|
|
toptoggle.classList.remove("flipped");
|
|
} else {
|
|
top.style.display = "none";
|
|
toptoggle.classList.add("flipped");
|
|
}
|
|
}
|
|
|
|
window.onload = function (e) {
|
|
initRender();
|
|
initStorage();
|
|
initDefaults();
|
|
initUtils();
|
|
cleanGutters();
|
|
populateMetadata();
|
|
dbgdiv = document.getElementById("dbg");
|
|
bom = document.getElementById("bombody");
|
|
bomhead = document.getElementById("bomhead");
|
|
filter = "";
|
|
reflookup = "";
|
|
if (!("nets" in pcbdata)) {
|
|
hideNetlistButton();
|
|
}
|
|
initDone = true;
|
|
setBomCheckboxes(document.getElementById("bomCheckboxes").value);
|
|
// Triggers render
|
|
changeBomLayout(settings.bomlayout);
|
|
|
|
// Users may leave fullscreen without touching the checkbox. Uncheck.
|
|
document.addEventListener('fullscreenchange', () => {
|
|
if (!document.fullscreenElement)
|
|
document.getElementById('fullscreenCheckbox').checked = false;
|
|
});
|
|
}
|
|
|
|
window.onresize = resizeAll;
|
|
window.matchMedia("print").addListener(resizeAll);
|
|
|
|
///////////////////////////////////////////////
|
|
|
|
///////////////////////////////////////////////
|
|
// EventHandler.registerCallback(IBOM_EVENT_TYPES.BOM_BODY_CHANGE_EVENT, () => {
|
|
// for(var tr of bom.childNodes) {
|
|
// tr.onclick = tr.onmousemove;
|
|
// tr.onmousemove = null;
|
|
// };
|
|
// });
|
|
|
|
///////////////////////////////////////////////
|
|
</script>
|
|
</head>
|
|
|
|
<body>
|
|
|
|
<div id="topmostdiv" class="topmostdiv">
|
|
<div id="top">
|
|
<div id="fileinfodiv">
|
|
<table class="fileinfo">
|
|
<tbody>
|
|
<tr>
|
|
<td id="title" class="title" style="width: 70%">
|
|
Title
|
|
</td>
|
|
<td id="revision" class="title" style="width: 30%">
|
|
Revision
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<td id="company">
|
|
Company
|
|
</td>
|
|
<td id="filedate">
|
|
Date
|
|
</td>
|
|
</tr>
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
<div id="bomcontrols">
|
|
<div class="hideonprint menu">
|
|
<button class="menubtn"></button>
|
|
<div class="menu-content">
|
|
<label class="menu-label menu-label-top" style="width: calc(50% - 18px)">
|
|
<input id="darkmodeCheckbox" type="checkbox" onchange="setDarkMode(this.checked)">
|
|
Dark mode
|
|
</label><!-- This comment eats space! All of it!
|
|
--><label class="menu-label menu-label-top" style="width: calc(50% - 17px); border-left: 0;">
|
|
<input id="fullscreenCheckbox" type="checkbox" onchange="setFullscreen(this.checked)">
|
|
Full Screen
|
|
</label>
|
|
<label class="menu-label" style="width: calc(50% - 18px)">
|
|
<input id="fabricationCheckbox" type="checkbox" checked onchange="fabricationVisible(this.checked)">
|
|
Fab layer
|
|
</label><!-- This comment eats space! All of it!
|
|
--><label class="menu-label" style="width: calc(50% - 17px); border-left: 0;">
|
|
<input id="silkscreenCheckbox" type="checkbox" checked onchange="silkscreenVisible(this.checked)">
|
|
Silkscreen
|
|
</label>
|
|
<label class="menu-label" style="width: calc(50% - 18px)">
|
|
<input id="referencesCheckbox" type="checkbox" checked onchange="referencesVisible(this.checked)">
|
|
References
|
|
</label><!-- This comment eats space! All of it!
|
|
--><label class="menu-label" style="width: calc(50% - 17px); border-left: 0;">
|
|
<input id="valuesCheckbox" type="checkbox" checked onchange="valuesVisible(this.checked)">
|
|
Values
|
|
</label>
|
|
<div id="tracksAndZonesCheckboxes">
|
|
<label class="menu-label" style="width: calc(50% - 18px)">
|
|
<input id="tracksCheckbox" type="checkbox" checked onchange="tracksVisible(this.checked)">
|
|
Tracks
|
|
</label><!-- This comment eats space! All of it!
|
|
--><label class="menu-label" style="width: calc(50% - 17px); border-left: 0;">
|
|
<input id="zonesCheckbox" type="checkbox" checked onchange="zonesVisible(this.checked)">
|
|
Zones
|
|
</label>
|
|
</div>
|
|
<label class="menu-label" style="width: calc(50% - 18px)">
|
|
<input id="padsCheckbox" type="checkbox" checked onchange="padsVisible(this.checked)">
|
|
Pads
|
|
</label><!-- This comment eats space! All of it!
|
|
--><label class="menu-label" style="width: calc(50% - 17px); border-left: 0;">
|
|
<input id="dnpOutlineCheckbox" type="checkbox" checked onchange="dnpOutline(this.checked)">
|
|
DNP outlined
|
|
</label>
|
|
<label class="menu-label">
|
|
<input id="highlightRowOnClickCheckbox" type="checkbox" checked onchange="setHighlightRowOnClick(this.checked)">
|
|
Highlight row on click
|
|
</label>
|
|
<label class="menu-label">
|
|
<input id="dragCheckbox" type="checkbox" checked onchange="setRedrawOnDrag(this.checked)">
|
|
Continuous redraw on drag
|
|
</label>
|
|
<label class="menu-label">
|
|
Highlight first pin
|
|
<form id="highlightpin1">
|
|
<div class="flexbox">
|
|
<label>
|
|
<input type="radio" name="highlightpin1" value="none" onchange="setHighlightPin1('none')">
|
|
None
|
|
</label>
|
|
<label>
|
|
<input type="radio" name="highlightpin1" value="all" onchange="setHighlightPin1('all')">
|
|
All
|
|
</label>
|
|
<label>
|
|
<input type="radio" name="highlightpin1" value="selected" onchange="setHighlightPin1('selected')">
|
|
Selected
|
|
</label>
|
|
</div>
|
|
</form>
|
|
</label>
|
|
<label class="menu-label">
|
|
<span>Board rotation</span>
|
|
<span style="float: right"><span id="rotationDegree">0</span>°</span>
|
|
<input id="boardRotation" type="range" min="-36" max="36" value="0" class="slider" oninput="setBoardRotation(this.value)">
|
|
</label>
|
|
<label class="menu-label">
|
|
<input id="offsetBackRotationCheckbox" type="checkbox" onchange="setOffsetBackRotation(this.checked)">
|
|
Offset back rotation
|
|
</label>
|
|
<label class="menu-label">
|
|
<div style="margin-left: 5px">Bom checkboxes</div>
|
|
<input id="bomCheckboxes" class="menu-textbox" type=text
|
|
oninput="setBomCheckboxes(this.value)">
|
|
</label>
|
|
<label class="menu-label">
|
|
<div style="margin-left: 5px">Mark when checked</div>
|
|
<div id="markWhenCheckedContainer"></div>
|
|
</label>
|
|
<label class="menu-label">
|
|
<span class="shameless-plug">
|
|
<span>Created using</span>
|
|
<a id="github-link" target="blank" href="https://github.com/openscopeproject/InteractiveHtmlBom">InteractiveHtmlBom</a>
|
|
<a target="blank" title="Mouse and keyboard help" href="https://github.com/openscopeproject/InteractiveHtmlBom/wiki/Usage#bom-page-mouse-actions" style="text-decoration: none;"><label class="help-link">?</label></a>
|
|
</span>
|
|
</label>
|
|
</div>
|
|
</div>
|
|
<div class="button-container hideonprint">
|
|
<button id="fl-btn" class="left-most-button" onclick="changeCanvasLayout('F')"
|
|
title="Front only">F
|
|
</button>
|
|
<button id="fb-btn" class="middle-button" onclick="changeCanvasLayout('FB')"
|
|
title="Front and Back">FB
|
|
</button>
|
|
<button id="bl-btn" class="right-most-button" onclick="changeCanvasLayout('B')"
|
|
title="Back only">B
|
|
</button>
|
|
</div>
|
|
<div class="button-container hideonprint">
|
|
<button id="bom-btn" class="left-most-button" onclick="changeBomLayout('bom-only')"
|
|
title="BOM only"></button>
|
|
<button id="lr-btn" class="middle-button" onclick="changeBomLayout('left-right')"
|
|
title="BOM left, drawings right"></button>
|
|
<button id="tb-btn" class="right-most-button" onclick="changeBomLayout('top-bottom')"
|
|
title="BOM top, drawings bot"></button>
|
|
</div>
|
|
<div class="button-container hideonprint">
|
|
<button id="bom-grouped-btn" class="left-most-button" onclick="changeBomMode('grouped')"
|
|
title="Grouped BOM"></button>
|
|
<button id="bom-ungrouped-btn" class="middle-button" onclick="changeBomMode('ungrouped')"
|
|
title="Ungrouped BOM"></button>
|
|
<button id="bom-netlist-btn" class="right-most-button" onclick="changeBomMode('netlist')"
|
|
title="Netlist"></button>
|
|
</div>
|
|
<div class="hideonprint menu">
|
|
<button class="statsbtn"></button>
|
|
<div class="menu-content">
|
|
<table class="stats">
|
|
<tbody>
|
|
<tr>
|
|
<td width="40%">Board stats</td>
|
|
<td>Front</td>
|
|
<td>Back</td>
|
|
<td>Total</td>
|
|
</tr>
|
|
<tr>
|
|
<td>Components</td>
|
|
<td id="stats-components-front">~</td>
|
|
<td id="stats-components-back">~</td>
|
|
<td id="stats-components-total">~</td>
|
|
</tr>
|
|
<tr>
|
|
<td>Groups</td>
|
|
<td id="stats-groups-front">~</td>
|
|
<td id="stats-groups-back">~</td>
|
|
<td id="stats-groups-total">~</td>
|
|
</tr>
|
|
<tr>
|
|
<td>SMD pads</td>
|
|
<td id="stats-smd-pads-front">~</td>
|
|
<td id="stats-smd-pads-back">~</td>
|
|
<td id="stats-smd-pads-total">~</td>
|
|
</tr>
|
|
<tr>
|
|
<td>TH pads</td>
|
|
<td colspan=3 id="stats-th-pads">~</td>
|
|
</tr>
|
|
</tbody>
|
|
</table>
|
|
<table class="stats">
|
|
<col width="40%"/><col />
|
|
<tbody id="checkbox-stats">
|
|
<tr>
|
|
<td colspan=2 style="border-top: 0">Checkboxes</td>
|
|
</tr>
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
<div class="hideonprint menu">
|
|
<button class="iobtn"></button>
|
|
<div class="menu-content">
|
|
<div class="menu-label menu-label-top">
|
|
<div style="margin-left: 5px;">Save board image</div>
|
|
<div class="flexbox">
|
|
<input id="render-save-width" class="menu-textbox" type="text" value="1000" placeholder="Width"
|
|
style="flex-grow: 1; width: 50px;" oninput="validateSaveImgDimension(this)">
|
|
<span>X</span>
|
|
<input id="render-save-height" class="menu-textbox" type="text" value="1000" placeholder="Height"
|
|
style="flex-grow: 1; width: 50px;" oninput="validateSaveImgDimension(this)">
|
|
</div>
|
|
<label>
|
|
<input id="render-save-transparent" type="checkbox">
|
|
Transparent background
|
|
</label>
|
|
<div class="flexbox">
|
|
<button class="savebtn" onclick="saveImage('F')">Front</button>
|
|
<button class="savebtn" onclick="saveImage('B')">Back</button>
|
|
</div>
|
|
</div>
|
|
<div class="menu-label">
|
|
<span style="margin-left: 5px;">Config and checkbox state</span>
|
|
<div class="flexbox">
|
|
<button class="savebtn" onclick="saveSettings()">Export</button>
|
|
<button class="savebtn" onclick="loadSettings()">Import</button>
|
|
<button class="savebtn" onclick="resetSettings()">Reset</button>
|
|
</div>
|
|
</div>
|
|
<div class="menu-label">
|
|
<span style="margin-left: 5px;">Save bom table as</span>
|
|
<div class="flexbox">
|
|
<button class="savebtn" onclick="saveBomTable('csv')">csv</button>
|
|
<button class="savebtn" onclick="saveBomTable('txt')">txt</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div id="topdivider">
|
|
<div class="hideonprint">
|
|
<div id="toptoggle" onclick="topToggle()">︽</div>
|
|
</div>
|
|
</div>
|
|
<div id="bot" class="split" style="flex: 1 1">
|
|
<div id="bomdiv" class="split split-horizontal">
|
|
<div style="width: 100%">
|
|
<input id="reflookup" class="textbox searchbox reflookup hideonprint" type="text" placeholder="Ref lookup"
|
|
oninput="updateRefLookup(this.value)">
|
|
<input id="filter" class="textbox searchbox filter hideonprint" type="text" placeholder="Filter"
|
|
oninput="updateFilter(this.value)">
|
|
<div class="button-container hideonprint" style="float: left; margin: 0;">
|
|
<button id="copy" title="Copy bom table to clipboard"
|
|
onclick="saveBomTable('clipboard')"></button>
|
|
</div>
|
|
</div>
|
|
<div id="dbg"></div>
|
|
<table class="bom" id="bomtable">
|
|
<thead id="bomhead">
|
|
</thead>
|
|
<tbody id="bombody">
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
<div id="canvasdiv" class="split split-horizontal">
|
|
<div id="frontcanvas" class="split" touch-action="none" style="overflow: hidden">
|
|
<div style="position: relative; width: 100%; height: 100%;">
|
|
<canvas id="F_bg" style="position: absolute; left: 0; top: 0; z-index: 0;"></canvas>
|
|
<canvas id="F_fab" style="position: absolute; left: 0; top: 0; z-index: 1;"></canvas>
|
|
<canvas id="F_slk" style="position: absolute; left: 0; top: 0; z-index: 2;"></canvas>
|
|
<canvas id="F_hl" style="position: absolute; left: 0; top: 0; z-index: 3;"></canvas>
|
|
</div>
|
|
</div>
|
|
<div id="backcanvas" class="split" touch-action="none" style="overflow: hidden">
|
|
<div style="position: relative; width: 100%; height: 100%;">
|
|
<canvas id="B_bg" style="position: absolute; left: 0; top: 0; z-index: 0;"></canvas>
|
|
<canvas id="B_fab" style="position: absolute; left: 0; top: 0; z-index: 1;"></canvas>
|
|
<canvas id="B_slk" style="position: absolute; left: 0; top: 0; z-index: 2;"></canvas>
|
|
<canvas id="B_hl" style="position: absolute; left: 0; top: 0; z-index: 3;"></canvas>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
</body>
|
|
|
|
</html>
|