mirror of
https://github.com/Alex-2-Graf/ZX-EQ.git
synced 2026-06-15 12:11:35 +03:00
4718 lines
271 KiB
HTML
4718 lines
271 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;
|
|
}
|
|
|
|
</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("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zV7Lvs9Jya7J8csRy8HNicwtxvzJE0txyK7MEcsJy2HMns2xzSnOicvxycnMqcmCzgnIAsgYyq7PIckpzMnN8c8pz/HPac0Bz6zN6Mphzo7NSc+pzhHNgcgBysnKEskYTcnO9qd+ywHKos2pzSHLXs3pyN7Oac+xytzN+4xZyr7Jqc9By6nJ6cnOyMnMfsnZy9LIkM/ZzB7NGcuYzxnJScwCykdPoswOz+nLKc7JyJDOccthCfDMOcixyMHKecmxyXnPOcwSzdnLf4xZy1HJWc8Sy1nKKso+zTnL6coFzdLJ8sp/TPnKyM+hygrM/sgpy27JOcpozAXPPMj4ysrNPkzHsS7Nucv8zknMKctJysHNOMzhyWnMGcgLTkXJGchmyknJvs9ZyinIpcjsyNzK7MxFzaXMx7FByIXJMs35zjnMwczZzsHNec6lz3nJUckxyMDPyclezMXMFc2FytnJFci5zOXI+c7lyOJM6c32zWbI7sqyyZnKicxVzYrK5ciVyEnN5ckez+XOZc8lyhXMpcuxzgXMhMpGzllJRsgbTZHJL09UzFHNxswqT4KzFNYLSCzKJso0zjrIgM06yYtMMc7vTKxNNSfpCktJLMmkzHrID08Uyw+LesnHE8tPN4tHjpbK1sv6z+bNvIBvFEtOm7IUyQzIjc5PSo3Ky05kzY3JjMpkA4bMAch/jEzLJ4gvTpHNRsonjHXKhAZ6TNTORM7UzHVIZHfMyDlKJso5SM3JLM1vTdHPb0/RyLrKMcpkdm3PBc4lzVLIy0lmzrHPYcnFzOzIvMiwzHHOZU5tzXHMScsZzSXJlc/5yJ3Ifs3FzFrKts/wAOJIVsn5yMXO/sqZyvHLhcjdz5LOTszkAd3LypMayzHMZckJyY7Nlc7Fz13KncvFzhrKug6VS8nLVc5hzL9NXcxpzJ3PZc6dz8XKHFN9zJXI/ciZzHnNl0n9zH3L/c59ztbN7AC9yeXOHc+2zTXMPc4pz5XPhczczLnPAYoDz6XOvcpdymXOhcshy5XOFctDyOXL1c+5SsPOWchDyD7Pw8jZzCPItc7ZyrXKVczDyL3IOcxdy7nOXcg9ysXOecyDydLPQ8xjzX3OY8m5yGXNw829zJnM48gFzuPOis6hzEHPDUr5zF7IYc4TyunJYc5DzWXLPMp9zN3LPc2DzvBNMcpeyFPPVc8dyIPLOck9zLbI08syBm3JY8o1z0XOlcjjz73K48wzy1PNPc1AyZPKWcsoEQPIec7pybPPE8uzyoPPU8xzyzPLw0izzzHP3cseyxPLXcrzyePJI8gDyNHGbc+DyhPLY8vDys7Jhch9ywvMk8mJy53IpHKpy5PLRcwLyrPOC8jzzQvOPc+zzjPN889LzbbNY8klz4vMgc5TzzXLZc8Lz/3JfcybTovKvcnTy4vJE8sDyhjPy81DyjPN7sxUybXIrc4wBYTIdc+EzbAGekjazszI+khZz3xxbcwmyDrOJsjtzSbK7ci5TF2EH4gxyblP7cm0lJvKHc2LzyvIVUkfTs7KS8grzvPIc8n0zebLK0/xzJvIXcgLyb3MU8r9zwPL/sppyFXIY80jzauMMSTBQLvMo8khzqPJZc6rzVPMO8ory57IX8dWS39Nc8slyqvNo8mryUvNacvGyAfPfcvdycvL+c27ytXMicqlzdXMi864xofOA82Hz7nJB8kLyDPIO82rzoPJfs5Fx0fJi8nDyWvOu8v2zv3Lu839z8fJ88/7zn5AsUikdgfJXchHyYHKR8y1yEXKe837iXvL1oIlytvJHcvTy9vNs8vHyIfJpcqHz6fJRcqVysfOZ89rzcfM68wrzuvJ4c9HzlLLK8/nzP3Ip8lnyInPu84jy6vJg8ilxJvOV8y7zdPLV8jVzf7MR8rXyuvOfsrdzUTUwUTbzSfO288nyTfPCcm/SiPIt8qTzYnP18pyzMfPY83LzKfLN86nyRfLFcvZz9fIo8vnzEPM+8s1ywfJ+8mnyjvPwcylwpj38897yBHKQ8nHyqfIk8xNy3fLO8v9ggOMLkr3yKvNCclPy/fLT8nuzLfJM8otDAoINUw3yyfIF8xLyhfLl837yFfPd8rPyYfJV8sPyEvII8/by6/Oj8v7zY/Mm8w1zE/Pccu9zffNZ883z5fJL84gTlrLtc1MzBvIxs2wBhtPrcpRzG3Mw83vgfQCQQmbS+eNa0tXFOIA6kqsdPeAG/Qyi+3MDcjRx1BnbALMU1IHx0qnc8BM+0t2SNuLwAXchJTOhsr8AqgBv83cgKIIN4OATn/J6AMwAHo1kINkyE3OL84FSZ9JO8/6zAmG2cE/zg+j20i/zJSLW06/yRAFv8+/yWtMNAJ/zYApf8ru83/KQCuAL7oxQAH/z43NH8gAKG7JeTMAL8lAgCt5FL/Lh04jSP/JZsqMzH/Of81/ylAHQCu/zMAuwC6Pi//M1svAL8HKKwa15M5FEQYgK8qVICyNzyAuQC+AL3rItAegLaAv0AegLP/O/8lzFPrOLcuZzCeMLs1ag2+KLkbgKw3MgCu3SWRIoCqGyEApECmgLUAroCj/zGAukC3/zcAsZUhQLzCUIClQLSbI5gXgKoAptAUdTNApjc2OdxAt0C3MA0AoMCr/ysAqMCnAL6/MyshQLHlgsCs/zodLUC7XSBArgCygKH/MQClwLhgH0CwQLDAty02GzvrP/80wKG7KKrAIKeAo6AdQKmBIcC/NynAuAEqIK1ABiCjAKPAqYCsaTEgtYC5IL8HOs5TgLT/PSCvgKc3NCCu/zwgu0C5wLkArECvILYguKCrwLmApMC5uS+7NduNILVApIC2wLfwHsCwQKmguECloLb/LaC9/yOgqkC+IKZArKC4OzfApSCjhQBgqsC4IL9/JgCsIKtAomC9oKpgr0CyYKGAs6C+YLjAp8CtgK8VN4MNYLtHOsCjIKQgrMs7ILXTJ0C1oKDgr2Co4K5goHEBYKWAqWC84LgHJZjK4Lz/KGCzILd+IeC5rTdgpmC/YLXAsKCt4LPApOC7wKu/PKMwuyF9EmgGoLBgpsCoEKdBJBC6CAIgqeCiELogsOCyQKYQo+C04L4QuWCnhzwPkHAcALUQtuCzYLRgu2CxwK7AteC6YKJAriCokK4QoD817jEQvsCf4KggsBCu4LTVMxCqgLIgueCyEL8QpZC5OVPgp6Co+TC7PQiGiAuAsCC1QTGsF5CmkKGgqECnHEcQpQCkULXgoJCkoLYzMWCktz5AobsxJxkYDlC2oLhgrEgFULxgrVCw4KmQvcC94LxQuJC9kKDQp7840sKQqICqkK6gugC2kLGgp2Cq0LGQpeC8ELoQp1Coty9QrkCtPjpQuihbkKFQpuCj0KGQvuCsYKfQtyCgMKbQtmCwkL7QrZC9PyKgqcc0hDIwupMjYLGRLUsgULsQutC/0LmQuOC1kLugrOCzMKkXJ/rHMLgeLzCq/yvQtVCxMLRApLC20LUwpzgdMKkgt6CjQygaiUCk0L3QrNCgsL4wvpCkYK/Qs1CgMLtQq6C0oKvgv1CsMKG7PdMY0KUQvWCpUL8wuEUwsLmgrHCvEKtQrFCjsKKwpJCn4KLdItmWsKfePrCsgK4wrpCnILYwpbC8cLSwrtC3cLpwslC1CTUDProTUpXQssC64LTwv4C88LvQpHC80KkwtbClMKgwuOkkMKBnKfCjQzPzP2gNMAlwo/ClcKGwotChMKrwvyCyQAoQsnC2EK9wsdCucLEHLabP5w3wvlC3MLYIrPC/kLhwsvC0cL/wpvCtsKgIva0kCK3nI5C/ALNrGPC0WBowsHCtcLiIseC4sLyIsAiqcLdQpnC0ML+hMLs0vBZQugigEK0Qr5CrgT1wrBC68KtwonCncKEgp4i0CLaIuk84/zcItNC9EKTBPEi30KyIqki28L2wtkix8LS3N8s0ALlIoHC1SKtQstC5sKkIrcCziK0IofCysL7VPwcggKjIuXCkSLlQu/CpsLEIuFCrSKKIq4i4MK5IpoilwT2ApsOBiLOpM/C+oLXIrMi9yLcQoKC0UKywrTC9CKMwrsisgyh6CCixULnItXCrYKfwpIiv8LJIqii7cKYovvC7iK9Iv8isgzzAscimCLUorgisKKEItIi7KLkIuiiu8LdItsi7rSyDP8C0qLhIupCtKLGwvCi6qKLIpQimSKJQsaiuBTKgv6rVqKeQvKiwiKxItYi0EKNIpqiyyKigvqi/qL9wtfUyoLfVGSipiKTIonCrqKsop6iuqKdIoWijCKZpK+M/oKRoqjCkKLPQvgi38LqAo8inKLpIryihqLFooSizwzVguOi/CKxoq/CoiKLwrYizcLrou0iyiLZTOoi0Vz9IuZUy4LnorrCgiK3oomij6KpovMiq6Laotyi+aKHQviipqLfgsMqVaLTotjC96KMos+izSLvoq8i6yKCooGiwGKAtKRC1GKwYtCijGK3Iu6imGLZosDC7yLgIt8igGKiouAc8kKoIspCpyL2ooqi8mLNosuiyKLYYpui+GLOwvKCh6KiYq5CkGKTwtJis6LKoouioUKeYupi1CLywpsi+6KkYpycs8ISYteismKIYsxiqGKIoo1CzyKrIvli/GLFYsGi2JzIeFVi9mLxoo0CyaKsQo3C7GLeYp+i2mKqIvpilHzLpMz8odBRYsYitGLSIs5iqqKtoqpi3qLbor2ixGKjYqGc26c3YuCi8WL0Yo1iimKfYpliv2L+YriirsKlYo+c7MLQ4pSis2LwYotiyGKrYoki7aK4Yt2ihGKE4qDi2lyawpTitaLRIozizWKs4uminOK+YrzigWLvgqWivFTjzFNimMLPYsjirmLpYp1inGK9YtiihWL9ouYk/ByFwubi5iL0oqji7mLO4tti3GL9Yp8iwqLQROk8o8KS4o9iv8KvYqli9ULkwrmi2uL44sFixOLF1P7XIeL1ookC9uLV4oAi9eLfoq+sx2LHvPT4yCyCKj3isuKsgstiwUKj4o4ik+L7Yr+i8+KOfMvixRTsIuqC1mKyorTi9WLy4tHijuK14ppivGLp4oJixmKAtOBwPsKhItGiv+KJYuXizKKx4uASuWKe4oNivuKP4sgSwqBBIp/itqKW4qXituLvYqQS4+KQEqniumKZ4oOiifzK3Ptc6tyhvKhAcvjjSE2s8bzU9Dyrc+SMy1bcmbzqyCb03K0TTIkoUHjOxCv8i0yttPZ42BjPfKsCt6BZ71vivXjXrMQSj6z84q3isCLk3L9M1PRAN3M8/HTxErG6DmKnTOkS10y43M3i+uLuwrLcvrSVrPx44by5HNsAcvjfQEYSibSPzJL5VhLwoGm8jRz6OHa47hL8TJDM/hKtpMESq6z31NlI0RLrgvUS9aLIbKli3RLe4sDihSLZ3P0spODfErUSjXiAku0SrWLpTLkS/RKpQp68yRzbXKoSqfzLRJrc1gB6EqsSnJyXJwJsrEzjADCkhnhN/LF4VxLluPcSgPTPEquc+6DbfOpM/xLJEpD4uJLK4qR4u6L0EsAC9OUeTN7MhZzmIPzM6JK/eNiSiUz24uCStBLQkqdCiRzetKkc/ryZHMyS2hLskuRAXJKJDKyrPvT2EscSzhLtgBcSk6zeErzASpLk9OqSzCTh0LqS4HiGkpcihpTAkpkSkZKwEsNisJKgApTc2Yw/8OiM6HSTko6il6yhksIS2RK64tnCviKUksmStJLpkqrcxVwskvK4+fyXXNtk4pgpHglvT1yW+KgwJvSaaHHEqt4hAEk4VbTz9P2SsFKBtmZOH3ROJPEIWfisUFb885KdEqZ/b6DPkt4iksSOks+4vmylEt2gcFKK5Wt6IuTsUr/43FKqPOggZpLBQuIgGXEA4oLihSKJkqTMv5KBvNmSmfy6EstQRZLttJYSjFK2xVWShvTryOcSgXhykr4S5FLRuJW86my1vIA3ce8k601055LlvIy0llKIgsuSshLwEswi8JKyPOHQtVK9tI1StyAIeIhs7VKEAt1Sh2LyEtJSn5KeUr68vlLAUrmSkAALEuFSvZz8kuXUnaiDTKJs9ZLSktF0mqTtkrd6f1zVvMP83Ii6t0EmPKzjkpiSxpK+pLeSoJLC3L1S65KnQsNS2rjmwKjS9VLY0tOSrRKE0ouSpNLbUv1S75LDEqmS51KzEroSywQxvOsS5hLXMLZUiVLsTKlSrhKZUq2StxL5UqQgVFKbRLswpryY0oGSuNKdpLzSglK2krGSg1LbkspS+5La0qB8sNyzUvTi6HiB0viSmGyOUvkSrlLKEv+S6hKXUoFS+ZKaAA9S0FyvUruA+xLCkuKS6VKykpbSipK20sVS0kzw0ptJD6CmFCzS3tKc0vDMudKWktN4xdKkkoUStNK9nN6SvdLTUuzSl5LLUsfS1lKC0tfiu1LFlN686EzV0oyS9dKvNNL0+hKq+Ibcraz59NFS5zzF5N9SjhKSks2S31zg0rhtSXSO0srE2BiQ/KjC6dL/4tnS6Nz80qHSzlLU0tHS7pLlEvHvXnyCMp/SzRKH0pIywdKX0q+S+1KS0t5SmZLIMrVMuhLYMoX8+DLBpi9S1942EocSyVKnEqbS49KMMtbS0NKlUsvS3cjI0swA29KbUEGSpjL50tZM4lL5IooyzpKKUqoy3lCGwIUy79K70t/Ss5KrUvesm1KgMqLStjLuUvLcsDKy0qBS8vjAEG3SikTKm2ZOGxYUMrWStDLm0sky09KKzIgPGTLLTIUEvaj0TNcysRL6MvNS1aA/0pUyp9KEkvUyvyKR0q0y07zMEvyYoLLMNn6SpTLe3I+UkzKC3LIypdLxkpXS2zLXUosSkFLlHN2U2xKCkt+k/1L0MqHslbS5Uukyi9L/Mo/MnnCjkp94wjL4EtzSyLKAMuyy19Kbkviy4ALE4NSfJrLGIpayiOLGMrzc5jLEktYykDLUkqdSzjLy0tYAMrjeMtBSt/j57MPbX5l60qKSw6z2+M64rZLF0zqknyCpeN8yurKhEqWywWzhbyTLKwL7TLbS8GzjMv/SnVLAMrPi4DKyUq6S8rTlsr2UQNKowouysGz6TNeS9rLbss6yibLx/NAy5UzVrNrc2bK3UoiQRzKovNVSkB8KszWyw9LxMreyvrjqsp2SttKIZIDc+rKNHGnQ59AEcp7StLKfIItS67KfsutSu7LZAo0yuLLyUoSyo1KocqxyxTKJErxy8LKCctGy1TKPkr0S/7KHUusyoHKTEpByuzKOwAhynDTSsqQygKzYcsbSjZLPMqqyzDLdkugCnDKHtFlI/DL6ktCymdKpEpuyonK/spJS9CT30vfUpODZcpxy2nKjMrayxnKosoXS8bLVcoByqbKbMpmyuzLnXOKyndK8Xwa0fdLyso8yiTKxcqky7DK0cqOyhZz7oMAAmnKNEvNi4jL9co6yljLjcuO8nrK7ktxfKRdPcoMy3HLdcpGywBKzMvuyizLJst+S6bKAUtByixLfNL4yphK+cvl/OtKRMobSsTKRcsdy1ASkcpDS9LKpcpw06X9u0uay+XLRIHxyvXKY8uJy/6KUfMey7TLILJ5wvvynkqrylaAdvMyy1pKA8tJy4tKrMqMSyfzgcqOEoFKyuLG0xfzbZKp7CvRIUqFytrijrLxM7bKeoF2yxkT9stLyqSw6ewnvNs0QbLq0rXivsoiyv3Lfst7y2LLi0vVyshSp8ogoYFRscrWkrNzLsr3yhnK68pVyvvLLMryy83KCss0AXnKUhH5ylTS3MtEyirLRcsLy8XKUcqpsw7KvEo/M6X9VXKnSjvKKwC7ypXLTMvryt+LePLYy0/Lpcvl/CAqQssMyzVKMstgKrLKj8oZizCKB8tLS1/KN0rdS27AP8p2jQQD5zLX81DKj0qvypoAi8qwyzYK18sp3Xdz0CsjyhjLfcofy3ArG8qDy8nLesoEApncT9IjynXL2CsVywnK4Csfy4/Ln8sBy4xKaeK5y11LhtIAABSKyifLQXNJ7BriqCscS+lR58tP4fEy4FGDLHzKmCpiyCwpv8MikqMLE+KAKtSzBgDMAFAA8AGzldJi5xXmgfcB8sxgU5wq85V04BxgNxU38gChEqFwE9wrgsqKSsGxZbl8K42BZf0381SQ4sH5xOQN+ALCK3xNacRZjEjh1yICK9DxWEFwEx9JE6E8KgjQpyVwEsgQqcWxMzIqmSOjMjIrrAvXiIorpUrjLJIrUPCmlEXLyis38yorLoDCKoxhcioqK7YIm0rSKxBAiio5QObAm0p2cdNAOioAQRdNN/MwFObAiiubISkiRcvJnUIqtxR8K8YrRokxrSkBN/L8KuYriQAWK8GNHCpWKomsYsrwKk/LKMticvrJ2pLDc8wqfMtHUqwqbCrsK82jTJIWK2YqNxSmKpBMZiqnJa4rA3CCK8YqQioeK8IqBiuPgGypXitiK7ors/HifU/ii414SEXK2iqWKuoqm0pyKsNt8iqqKzfzyirWKkorlxUcKuEr/ir9cKorESrQE0Er9wHBKh4qASuxk4EqvipsqXASeir+KtAS3ip6gD4rhivhKx4qxirPCVhAHiu8K24qyGnuKikrE+MWK2krfCvBjTfyhACv4Uoqm0tcK5YquSsUXTYruCvYypPK10pTyrMy4Mozy+ojI0sxyO3K/Uody2gr+uMAK2rKqzJqS6UrdqC9y5TKD8uVyrgqL4qbyinL00t6SlopMvMGyqArW4ujy4ZL4CoeytnLB8vSS4fL+Uqgy8xLRvIlK6tLzSTDy+R1ZSuoK+HKeEudyxgrXctAKwaYPcvdKoQrvcoVyppLsCp7yo3Kn8rVynYr/HN6Sqklo0sryjAqfctEKrUrxCp1K9+KTcsTys3Lk8rsywdAyCt93cvyfzPUc3/L5Su9K7zKXcrDS9HLbyDswivzUsuEK5MqwyrEKnArIyskK6Mrg8rHS/grCyuqc1gr6ytDK+NKmyojKwUrdSutKwgqcyoKytPLFsoWcr1Lt9w9K9zKaCrLKmrKKyr8yt3KAyrxfXBINSr7S5lLwyufSlsqtiqQKmMqcnLjK9crgys1KzgrdyqFKggqOMrHK4gqLEor0lQqbEujg/zzZ8r/ygvKg0p9KxkTUcsrKlcqy8sfKxnzICqTK8GT6ctryi0qJCr3KtsreCpDy9UiE/PbygCrq8qAq80r3ktjyknLWyszKx1LsytFK3Mrx8v4yzozEMoN858rSytlS5HLlSppsg5KaMsE8ujLYKtayhCrE0tAqoUrkCs6MlRLyKrlyyirhso4KkCr0ysQKhPK0Ko5y2QqR8tdSgABVZQrsKrFoKKUpHkWQn/LsTPZCdripKuS025QjYDkq4irlUvyIBZCsLJ94zUANMFPKw+KGeHPK4crFEp0y4ZNQ0GG/UdA9tPUq8JBNKveSzHkOKoi8hQLuFBGQovAOpI0wUHioIBEKxsrUyrVC7SqhyozK/ALDKovy2ZCw3NMq7gkXKv7KtyrY5w8qlnLA8vsinyqgSNUqxiKAqvMqleKwqpCS8jL8CrisqKq69HEqqwK4qs3K/FKmcssqnSqvKsiquxBbrJiqzqSsqvvStiqLKsSq0ZLkqv7y1Kqiqs3yjKrrgrKqqPKKqoSqy0r48v4itKrychMq7Ir4qpkSvKrPKs4qzqr6qoyYEqqYkF6q7Kru8ptAKqqrkvaS7yqRqqybMaqeQAmq8qqUysASgarwqqjK4arGxGtUJarmqqCq/tKByumq9qqU0pSqyBKoqqlaPaqVqpaqtaqtKpOquarCqp2q5cRNdP2qhsrgqvWqmark0oeq3yy0qu1lHqqMSr6qx4LPqsLS06raqvOqhar1NBeq66qDqq3Ko6rfwGBq8zLQassyuqqdqq4tKGqAasmq7cr4avuq4dKwaokM9wg3sWMq/yroareqw6qQquOq2irdKp+qhaq7pnRqjSrMarhqpCAEarjypGrS/L6Q+MU2SAcq8JAnKsBq3KrmauQqsCqbKoF4OaQ8Ipt4kpgYkF5qg3LNrBxqmqrkaoC01WhHeBVCLmrxap5ASWqH4v5qhvKqaqf08KhqZl6kTmrL5McqvMBnKtJq2GryauxqymqCqupq9os/Ksyqkmq+yrJqj6qZapyys6qIjLW2DmrRavEElWrjavtq02rHaotqoaqG7JEHRWrHFGVqnmqGarNqpmqnaq6y8ZLtauGQkWrQ6qNqtWrsQo1qhArrKsDqul53aoTqiWrw6r9qqyr/3LMC8ZD46oNq7mrE6pzqu6r/arTq9gKG8Uzq4uqvaqTq7QKU6qtK9gLmJRrqiAzDauzq1arXKtzq/KqA6ubq4Wq9ao9q0/i66rLqyqqo6tZy9gLRKqLqtuqS6o7qm6qu6vLqvOq8XL8C/uq150Hq4kqw6s7q96qF6p7qyuqrapxrImrbaoxqzeqHau3qward6u1qnyZW6qHs9urVapHqtqqK6vzqwOrmbivq9YTh6uPq32rT6s2qlCrA6rqOF+r/RLfqueqt6tHqh+ql6p/qleqlatrqjerAGpPq4BrF6rCMvuz5kIHqrOrb6vfqnKqpas2TEBr4GpSC7hKp6uvqmeqUGugaj+rYGp3qx+rKgtsq3BrX6qgamGq0GvVqseqIqq+M6UI/6u+EgBrqGqmq82q4GpxUvuzAaGDq/Wrp6pYak2qaGuTquhqtqpSC508KGv/qqhqBGrYayOrMGs4a+ar3bFpq/6r6atQa6Rrpatkas3S+7Ldq8RrmGskan2rBGobq4Rrv6sqC7ZxMcSuqo+rCGv0a4QLG6o6qlIKTGuRxA+qmqrtqojLbquIas+rSGsOi68AAcUaq/HTXqr0a1RqMGo4ajRrbGs8a1JUzGuUaixr/Go2qpKrnarxq6bi7GsyTMJqzKrvq/qrrGtZqvoKQmss0OmqkmpUarGqZGsCahELgmrfXUZDiavMa1hrcmrUa/JrSQo8aopqLPCUa7JqImvKagJqSGtAa4xqMmr1eLJrAqqkaxpqomuqqmJq5avxqtKqqEjqazpq/Gu6a1Jrvqs8M+JrhqESakZrnGvnq1xqv6sFqwpqZ728a6HTfGrmaoBr76sqag8Klkqma8slhmvrqqxrDGqWa1pq31w1KDprDmvcq45qLysmatpq83AOa5JqgauuarWqq9Kmakc4HmpyaxmqKmuaarBrTmsTbBxqfGqcaqirWqpSa55rLatuat9dmXQuax5q+arBa3urqmqbPAFq1mqBa1iqXGq2an5q5Gr+a5k5VmqjC9ZrgWrRa0Fr1GoKax6rsWpmay5rQqrha8+rXmraaoZqSmvCaspqvmqaatxqWmoRanGscWupMvFrUWvma9FrmWt+a1lqgNyRa3FqUWrNKkFqnmqJaqpqIWpDLdlrgeM5akVqCWrFa7ZqqwupalIjIao+ahprGWp6a2arcav6auJqMmvRlNVqGWojq75reWsxar4ytGsbFBuBkGu9qjZqYGp5axZqbmqr0sRqLWpSyiRrS6s+ao1qmWvtal5qlku4auyrLWsgat1r1Wo9azVqvqu1a0vzSuV1qslgXWp0awNrDWu7qk1qgmpJa6lCyWpha9BqQ2pBqiZqq9PIa51qrWvJaimrFWoMSr4ycGpzagNrZ6rjaz+romujql2rpuMQaqNrc2tTa2hrxWp2a0irI2o3SaNq9RP4a0ZqNWvGasNqFAt/q+2BcnHra91r42q9a8Fr5aufqgdr7KtLaghry2oWaytrx6rIMy+rJ2v9avhrdGptaohq7Wvna+hrtavk45dr22rFqtdr8Wu5awlqC2uSSierC6pLa1drY2q6a7trKWvca7Wq8FT9a/drPasParlrNmpPajFrE2r3qmOVpWrUq4Vr8Euoqj9qE2uJasgyW6r3aodqg2pHardqRGqrq/uq62una61qj2vfahVrP2pA67WrXbifaiDrZ2s3a3pqq2tia9niM6vA6hDq82vYa1DqJWvlquOrL2rwaztr12ssaq5qm2qVa12q4OrbarDqb2uDantrZarZq81r4Oqvastq2Oqg63DqF2u1qp1qeOuo619q5WuPalDrgOvI6iIzfWqI63jqZ2v46itrBOu3a+WrGGvk6sTrr2q7a9jq72pZal/LryodKqEB+KqwqyUqKXBbxafLVspzyopK1nib0gvpxxNV0EgKSVEl480BDCuccTeM9rIhc0Hjb8pgKxlrjXAY6gxL6Ks/HIoAPeNMKuESvOs+ynzqPWr8609qFEt2K4Lr0TIVQ2sSWAEBYCLqtUsaa3Tq+WuDiuBQOVA86pLq8wG86tLrfOo46vpquKvZymQrC+LkKm8rSMDIKjyA31zibDQqSyvnKwiri8rpy89KVSu206dD6uv/Ktgqwssi6s8rpOrYCwLqx73anXHSNyta6wrqPWqQqzWrLav06jCqCsoAAUQTgdySq0oC0zrN6ontgLJ4oIFhy4Mx2uIVFV7SChBYAUCQLCoP8u1zuKvK60xLcysty+8qa0tA4tAqoUrnKr0rmuoYKtKLDCqg40ry/EtNKgDrRWqZyqbrU6tIaobq2p0Cgu7q6ypDK2jr/Gt+6purLypFKiDKU8sW65brnSszU7V14MHFABdhMNlhyxz4zkFs69nMWrA6kwzgpE2CABG1G4AMKv0rZuph6uzL24DIK/FsWCvu6xrrHupPSxcrxuoOy9rrdlPvfN7yYKp66wCq+uvYqsjrBuoPK+jSecLZ6iiqOergqrnrEKoy601rSertKrjLSuPsyoSrTOtq6mZDIpAkq9bK88oDShcqiKqXKkArKctGXabTuut7KsHrGmoh6mxq9KrxUv/Ddep7K0HqkOtta0jKYuuXS6Qqh8s5yviriCrK48Ur08pdKyW9wqghSyzqD0phS7YA4Uo6khFKWACRSpGTDCpUiHet3CGrIOlLIdBaoYmBM7Lo6pHj2Up56ioKAerD63eNsjJWc+lKY+rxS8HrCUv86s9qoevQqsnq38pM693raLRVIwez8Kqa6+nqNet9Kr8r/Spw02UimKu1yy3q32ut6sbKBuuT6vnrF1KTgpvrEyuF6q3qN2pt6pPrC2sl6x3r7Su4y7JK7yuEqilwuUAZ8qUgGutzyl8qFSvoKiXKbQFc6y2jzhICskHqSOsNyjvqAuq76j9KbxTn6rfr2ev16gfr4+p3Kvfr8+tH63irx+pl64NBKeq/yivzK+rp6rzKGeo/K4Armeo1y+X9aytP6lvqFUom6/rrR2t7qobq44KIci3qjiu+yybrxeq/am/qKuqd6wzrskrZIfMqCmy96hfqVeqX69XqWuo/6trqSKtIEwLKVspPK9LLc3KAG6DqjGoB6raiIKC/HIgbGepIG7nqr+ti6uAaLuoKy7GzJyoEyo/rnVMpkl/r88uX6pUrNeq/693KOBtmDJaqhsok65DqfupgG4lqAeoVlR2jgQjG6yDr6BuAG3eqmBsq6xAa3UsEAfMrlkrn6rgarOrhyngasBue6gRK/SoiM2BjoKqF6s/rW+sH69vqlBv+6g/qe9JUSswbmKv76ywaL+uiymwa9Ovt620qx+ul6xyT7Mqn60zqcurbPUNziysX6girq+uwGzArDCuKY4IaPupYqgAasCsZao3q0mp4Kp7K4rJyYqrSaBpwGwAbFBrIGk5qC+p4q+Aa7+t8G0BBH+vl/COzler0GtXqnutX6+IamCvDgknynBosG+Ia6BrF6vPq30rsGhrKyhpz8iAbiBqgG0gaVOpg6/IbzutUGifr1BtYGq3KEMtcw8oaQhowGsIa3+pr6l7rjBtIq0DjphtiG5waxBrb6iQa2hu6yiCqOysB67PyZjJ6G4dqchoGGoxqhhod62/rAUveAJwBPACtATwAauK/8swBRIBAAAAAtAAANeYB5uoAARTZoXOACAFDgR6NWADuAKoAbCsLgTgBbADqEouBLgB+JHgA1cQYARYAaAAgoTABSMEwAK5AEKlujSYBGgBAAfjAcbNOKgqTUAD3AcJ9yuIp+YhANxXK42DKjVxLEirjagG9bKkakTJHgMkayuKdK40hGRqt2KQBGRt80/gDgUq3S/kBGRoWS+2A+Rop+OKDlxSpG+tyOwH6nOcVyuPFKkiAJRpLE91KQZLJG+zLagHaK5cU3UopG2ka3UsElHKBFRtykoPquRvoS8UrbsB1G2DKPkVVG+hLBJW4QRUapoFqAbgCSCsNGxIqSCopG8GMSCsElTMANxTlGpEzR0P6E8vinSokXM0ardn/3M0apAFqAKdg5RrzKoPrZf3UGq3YD1DlGkobggEjGixKnSsZS70b38uS64qB3RrBy+tyYYzByin5z0EVG8HLkuoTGoVLBuOdGvwaQxrDbMHKkTO/gfManSrOgWUa3Ut80/3iwxp5G8Xi5l3UG3zTIhjNG0gqg+o1G1PLagF4auUaKesO64UahB3UG2oB/CvUGsUaKxsKy/saZaAzGwSrlRplvEUaQAGM65Ub0xuXGutzagEHQMkbnpIp+Hciy9KVGn0Adxor4k0bjxvL4wSUcXwPG+kbIxpG8rca2xuektkbnRuek3zS25WXGxQr63NrQesby+Lh6lySbRuexDMa5/K3G9cbRxtG02oAZby648OACAEsAawBVwBKYW15D4yb425TQAC3StPjV4CIAC0SyuPgAMIwGAHgAPvBO/Ex426MqgHgABQregCXAaQA0AAaXPAA8AHgAAAAJSYAIAAAAZS7FCAAaePeAeaSSxLQmjCasJpJ0XCbRkHwmswBCJuIm0ibsKAomkdAqJtom+iamJumAFibC+PeATMQ0+JomgAARBiaFCvmAfpAeAFwmu4AlJpUmtSa0jCDy471UJtHgdCa7gEwm7CbeJo0AEuACJrTgISaIADIm0Sah4HEmuibPgCkmmSag8vDBBSblJtUm9SbNJv0AbSavJr0m94A11A8mnSbvJtIRPybdJocy94ANrMMmmgBjJv0AUyaeJrwmyyaBJusmkibbJpEmyibqJqcmxibmJtYm/QBd5DT44bSAABVS+Kcku4AFCuIAGiawAF6AZ7jcJrQAP8BiJuXEcSaAADVnuK/8qoBEKneAfHdgpv8mwBAfJtYAcKb1JqDypniYprimubLuJoampKarJqImtKa7JsymiSbnJtym2Sb9AAWS0aauJrMmqaaUppmm4SalgHsmhgBHJskmpabhpvkm/oTBprSMfqaQADOmyKaVptocNaaTJommnCbNpsEm2aaMprEmrKbDpukmvKbWACSAQnirpo0msKbPJoimtdQ2Jvcm06bAZtCmrSbwZoCmlaagpv6Ezib7po2mvibkpuemnabyJvmm7KaXJq+mkAA8yp6moGaLpr+m4aaCprBmkKbzpoBmkmbrpu+m7qb+hPeG+AAAADkwADwAFAB5XEGAeAB5usUmpyT5uvgAIcbWABpm2mb5upLgAAB5eAAzoFZm9mb5uqDy0gq0+OdEO4By+Iem8yb+JpRm9KbbsHsmmgADpsWmz6blptYAecBCeOlm/QBZZsRmiybpppsmuxQVZrVmnKaNZolmk6aSxJKsGWa5Zqem1KbhJuVmwMIzZsxmzWaQAEsStPibZr1mu2akZqNml6anZufgF2ajprkmgya4ZtHgfoAMJoUK1maAAElS+PmAPpB5AEWAHCaAAGkGZsx4+YAAAHUFpvNm1ya5JtBmjibw4Ftmg2aFZodmpWaaAFNm96b1Ztzm/QAShqlmoDAi5sSm32atpuNmuPAK5uzm12aJZuim/oTkbgbmyaam5sVmk2bnZsrmnOasZvfytPie5u9m4ubkZtLmwebA5uHmjua5JqpmksTdZuySn2bDZubm/2by5qHm9ubg5v0ACnq65pbGt1K15pLm7aay5rbmjGbd5tYAb+BCeIoAQubJ5sbm9eaB5tbm7eaL5otm94Bqus9m3YBe5sem/uaZ5ufmuead5rfmvebbprDm2Kb1pofmk+aW5t2m9GaPpurmq+bQ5oLm7+b5Zunm0+bZ5vAQIOagFqvm/OaQABXmo+ap5r9mx2at5oAW1+a4FpAABzK0+Iy6JBb7ZtQW/+b0Fvnmy+ayFq7mksSJ5tXm/BaN5sIW8+bYFqxm4hAb5qMm8Ba+5sfmv+boFremwBbSFqeQQnjKFvvmgRbIFs3mzhaq5qxm8BBCeJYWvBaIFpQWluaA5roW0RaFFvYmkABmprK4sri7gFdm4bT+gDwAawAvAHmAdQBiJuFQcSaBZusAIgB4AHL4/qTIABemohb9ptsK94BEEB1m+uapFp/mwRaaFo0W1Wb6FswWnBaQFpLEyRbWFtUWghaz5pfmrha3ZsZQCRbuxoiW6Ra1FtkWmJb5FriW7BblFv1myJb2FuiW4hbYlv0m2Gbl5u8WpJbfFpkWjha0lpHmuJamFpwWkpaVFuSWqJa0FsCWrRa4lqJmksT5uoUKsuAoYAYmoqbo5tXgBEa7gCbgL4aE4FUm1eB4ACSACiawgHEmhQqooCom/Sal5pAAJuAipv0qeYAI6EMWgWaiposW30BVlvgAIqbrowqmogBMxHeAc9Ab5rvm0pbkFsaW2hbmlpIWrGaCUASWw+bsloaW3JamlowW0ha7ZzT4puABZqckgWa7gAFmhiaaJqzmpuAUAF7FYWaMwHEmhOAqABLgUviGJuTmtyaEFpAALJbj5pSWipb8lvSWtybsFtvmqhbf5v8WohbNFpuWt2aSIC8WiOaTJqjm+brY5vjmmcok5vgAVOabhszmlpa3JpqW3BbHlrKW5Fa8lrxWgpbjlraW2paHlqRWy5aAlteW25aFlvCW+paWVr5W3Fbrlo5W/QAzoFOWrFa/FvUW8VaBVrdmioIKFsSWkVaLlueWq5bFVqDy7hAvFp5Wthan5v5WoJbSFtyvVCazlrVW6hb5VrkWqpbtVvhW4VbmVvVWg1aFVqNWrGbyIHuW2VbylrZWiVa0VvBEonjhl3K4wSUNRsRMrcbA1uZGisayuLZG/cafNNqALkbGeJbGh0aNTK3G+NbdRuwAEcbJRs3G/kAuRt3GkMazxvFKjQAzxtPG1UbnpIvGs8brxrPG5kb7xoUcrcanxor4l8b6xsAm/kBgJslG0CaMKiJrRVw/VrHy/sa2xvmy/sb/xuXGxnjxxtTWqkbpRuAPZcb5Rt0c/MblRsxrNUb+xt7GrUa2xvoSoUb9RoYSwbjexp+mwbjLxvNGkMap1osS3Mb9RssSwbiHRosSp0arRtdGkdbRxvL4z0bD1t9G6caAxv3G+hLPRtLG8MaU1oTGw45Duu3WuMbeAATGh/rDuo3W1MbsACeQL8br5sO60sbqusO63saCxv/WosaTRpA2i0bpxocy5LrD1uQG4IAfowzGl6TlRsfWlsbNAB1Gjsa91pbG8BArRt80gcawcpbG7lpUNrFG7db+RvF46cbtZuS6ucblxoXGoPqlxtHG1cag+sbWksT3xuVGvqdR1p/Gv8a0Y2y4vsUzAHgAO4aRAHjgC6MCAFQAaibc4Ad4h6N+xShAXOBVkECAbxAgAAAA=="))
|
|
///////////////////////////////////////////////
|
|
|
|
///////////////////////////////////////////////
|
|
/* 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);
|
|
|
|
///////////////////////////////////////////////
|
|
|
|
///////////////////////////////////////////////
|
|
|
|
///////////////////////////////////////////////
|
|
</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>
|