4710 lines
280 KiB
HTML
4710 lines
280 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, "fields": ["Value", "Footprint"]}
|
|
///////////////////////////////////////////////
|
|
|
|
///////////////////////////////////////////////
|
|
var pcbdata = JSON.parse(LZString.decompressFromBase64("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"))
|
|
///////////////////////////////////////////////
|
|
|
|
///////////////////////////////////////////////
|
|
/* 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") || "";
|
|
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";
|
|
ctx.lineWidth = text.thickness;
|
|
if ("svgpath" in text) {
|
|
ctx.stroke(new Path2D(text.svgpath));
|
|
ctx.restore();
|
|
return;
|
|
}
|
|
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("userSelect");
|
|
|
|
// 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>
|