4415 lines
256 KiB
HTML
4415 lines
256 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: 10px 10px 10px 0px;
|
|
}
|
|
|
|
.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: 5px;
|
|
}
|
|
|
|
.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: 10px 10px 10px 0px;
|
|
}
|
|
|
|
.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 {
|
|
width: 100%;
|
|
height: 100%;
|
|
background-color: white;
|
|
transition: background-color 0.3s;
|
|
}
|
|
|
|
#top {
|
|
height: 78px;
|
|
border-bottom: 2px solid black;
|
|
}
|
|
|
|
.dark #top {
|
|
border-bottom: 2px solid #ccc;
|
|
}
|
|
|
|
#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;
|
|
}
|
|
|
|
|
|
</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": false, "redraw_on_drag": true, "board_rotation": 0, "checkboxes": "Sourced,Placed", "bom_view": "left-right", "layer_view": "FB", "fields": ["Value", "Footprint"]}
|
|
///////////////////////////////////////////////
|
|
|
|
///////////////////////////////////////////////
|
|
var pcbdata = JSON.parse(LZString.decompressFromBase64("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XEVkOV6QOqhCeVUFU1lLWUUFe1lSeWdZd1lvWWfTP1lggCDZcNlzBVkAP7lhYDqoU8AtFFqcQtlzuUR5eqATWXr0QjJouHFwN8VlBVAlY1lzWWtZbiVyeUglX1ljcCQlUwVeeUegLCVWBUPFU8VkRVi5a/lUxUBFTkVbACpAPkV69GOFcUVyxXjFWUVQwApAB4VTwCLFaMV3JXdFZMVYOUfFTLlEiWKFfDlyuU5ZV0VDJXo5Zzl0xUslSAAM4AhFbDlHRUq5fKVExUa5b0VSpXMlZsV3oAeFUKVIAAKZZYASmX7CW6hqmXqZZpl2mV6ZTIVrGV8lfcV7RUylcoVeWUKlRoV+pUbFZDlKSDqoRQAtFGkWciV++VolQcVmJVDALIAx+WHFZDaIgC/5ZflVOWziP7lHcDGlZ5lYxWilYyV4pXY5T6VFRWv2ThlpOXwALelpTE5lYgVT9mcCVglBZWQFTSABQBgAPAA9aAdpUnwa6XxlR3AjRVSFUgVKBVoFbSA9gDhla0VQuG/5X+AkKGAFf/lIBUWZeAVnACQFdAVQ6VwFZAA16WIFQaAyBWoFZKhHZXEZUMVf4AmlREVQOXRFW/lWRUSlcRluhU6aaAVbABFFc4VPJV8gMRl2xWCADgVppWKZe4AymVWleBlamVTGBplCAB2lfplsGVGZcRl9xUFFQeVThWpla4VQwD6gOqViuVeEbKVquUelZrlJWXKlZsVmXI1oZPlA+WiFSPlY+WO5SFlfeWwVb4AM+VZBY2A8+Vj5UvlA6Er5Uulb+VDZc0VbADqwOqhjIAPlSyA7gDYALAA6mWMgE4upxWgZXRlYhV3pZIVVxXSFa+VchUgAJWAAFWalXKV7pU6lYqVWuUQVZDllADqoeeVlaVmlRaVKmV3lTaVT5W9mPaVbFW3FRxV3ZUgAE7lcxmkWSYVbuWe5ciQ3uXIkNYVtoD+5Z6AOJWAlcnlBJUAlYnlxJU9ZaSVA2Xr5RSVvGFjZYRVHFXbFZwJL9lvIZwJbyHh5UIV9FWiFbelDGXMVdcVjpUiAAwAHhXJFUsVqRXHla8Vm5XvFZmVMuWBQCJVdIClMWuVPhUblUyV3pXRVXflwVXClaFVaZWEFVoVmxU2gB4V55V0leuVGRURVTlVkOX+IMaVAOU/lXxVaxWlVdFVFRUulYzlwFXalS/l/FXgVQaVZVVKVeglk7y1/EGVm9H3EeqAlplTgEFVIQCAVUzlnRW8VS1VnpUCVe1V0VW7lSYQF5UJVS8VSVUZlVbl0VVnlXFVhVWJVcVVyVXblUMASgBcVa6V41Uo5dVVYFWRVatVouHjAH6V/5UhVc8VYVXLVbEVUVUXVWlVI1XcVSBVJ1V6ldNVKVUXVYkVUgCjVU1VE1WrFadVtVUXVTSVXVVbQMtgvVX9VU8AlplIQAdVjVVulcdVk1VA1YJVO6UCTrJOhqXuJUolAyVs0Wol7KWqcWol92EKJbolOqUGJdg05iXgBlYlxiU2JQ2ediWWJQUl3hGdqvqAXiWypR8ASSU+EREA5iUK6REAcSXvJRcA2yVR0Rkl9qVtJekl6QDjJU6lIyV5JVFQuSVFJeylpSUq8rklNSW4sVKlA5kNJYTVnWkFURqlDqW3URklatUfId0lstXYpbTuWSVDJdgA28C5JfuRYljm1ZMl2YnTJRklrNUApQOZuyVNJUPWjclzJbKl+KVa1UxFuLGRpQ8lliXepcLVL+m9ANslGXFqJQalwtUKuZ/4wdVdaREAYdU9lRcxNXk9lXvRZUDIpYilsdWApUPWaKXUpdvAmKVZ1XvRFiFZ1XilWKVEpVilnapwTFnVlKVYpeAGL8XhJQylqKWxpQ6lLKVspdolnKUZJVKlvKWGJTbVgqWWJZrVUaVipdolEqURAFKlMqVgkTqlb7mopUGlbSXKpcmE2iVqpbKlPdVGpVkF2qXaJQZ5+qXbJcalYJGmpdwJaiX21Q6lVqXU+NoltqVgkULVQuFH+YYlUSWupZRY2yWepbsmV9W+pVfVnaoqlv6lIaUipRGlIqVcpfXVSaUi1Zfs2yXJpXClKokuQQ95CiWmJfTVTiXcvC4lcKUf1YElcKU21d4RoSVp1ckl+SXH1TppayU6pQOZmyXz1TpplSXB1cclwdVnJU30uDVXJUg1A5kppR7VbSWQpfklUqWwpSEC/9XXIf+l8ABwJaIA7IACTgQAqADdpRKAoBESTlJOmwBUJToA5QBAIEAAAAA=="))
|
|
///////////////////////////////////////////////
|
|
|
|
///////////////////////////////////////////////
|
|
/* 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) {
|
|
text += (output == 'csv' ? `"${node.firstChild.nodeValue}"` : node.firstChild.nodeValue);
|
|
}
|
|
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") {
|
|
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,
|
|
}
|
|
|
|
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"] = {};
|
|
for (var id in pcbdata.bom.fields) {
|
|
pcbdata.bom.parsedValues[id] = parseValue(pcbdata.bom.fields[id][index])
|
|
}
|
|
}
|
|
}
|
|
|
|
function parseValue(val, ref) {
|
|
var inferUnit = (unit, ref) => {
|
|
if (unit) {
|
|
unit = unit.toLowerCase();
|
|
if (unit == 'Ω' || unit == "ohm" || unit == "ohms") {
|
|
unit = 'r';
|
|
}
|
|
unit = unit[0];
|
|
} else {
|
|
ref = /^([a-z]+)\d+$/i.exec(ref);
|
|
if (ref) {
|
|
ref = ref[1].toLowerCase();
|
|
if (ref == "c") unit = 'f';
|
|
else if (ref == "l") unit = 'h';
|
|
else if (ref == "r" || ref == "rv") unit = 'r';
|
|
else unit = null;
|
|
}
|
|
}
|
|
return unit;
|
|
};
|
|
val = val.replace(/,/g, "");
|
|
var match = units.valueRegex.exec(val);
|
|
var unit;
|
|
if (match) {
|
|
val = parseFloat(match[1]);
|
|
if (match[2]) {
|
|
val = val * units.getMultiplier(match[2]);
|
|
}
|
|
unit = inferUnit(match[3], ref);
|
|
if (!unit) return null;
|
|
else return {
|
|
val: val,
|
|
unit: unit,
|
|
extra: match[4],
|
|
}
|
|
}
|
|
match = units.valueAltRegex.exec(val);
|
|
if (match && (match[1] || match[4])) {
|
|
val = parseFloat(match[1] + "." + match[4]);
|
|
if (match[3]) {
|
|
val = val * units.getMultiplier(match[3]);
|
|
}
|
|
unit = inferUnit(match[2], ref);
|
|
if (!unit) return null;
|
|
else return {
|
|
val: val,
|
|
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 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;
|
|
setDarkMode(settings.darkMode);
|
|
document.getElementById("darkmodeCheckbox").checked = settings.darkMode;
|
|
setHighlightPin1(settings.highlightpin1);
|
|
document.getElementById("highlightpin1Checkbox").checked = settings.highlightpin1;
|
|
showFootprints(settings.show_footprints);
|
|
writeStorage("boardRotation", settings.boardRotation);
|
|
document.getElementById("boardRotation").value = settings.boardRotation / 5;
|
|
document.getElementById("rotationDegree").textContent = settings.boardRotation;
|
|
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: "default",
|
|
bomlayout: "default",
|
|
bommode: "grouped",
|
|
checkboxes: [],
|
|
checkboxStoredRefs: {},
|
|
darkMode: false,
|
|
highlightpin1: false,
|
|
redrawOnDrag: true,
|
|
boardRotation: 0,
|
|
renderPads: true,
|
|
renderReferences: true,
|
|
renderValues: true,
|
|
renderSilkscreen: true,
|
|
renderFabrication: true,
|
|
renderDnpOutline: false,
|
|
renderTracks: true,
|
|
renderZones: true,
|
|
columnOrder: [],
|
|
hiddenColumns: [],
|
|
}
|
|
|
|
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 (!["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;
|
|
|
|
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("darkmode", config.dark_mode, "darkmodeCheckbox", setDarkMode);
|
|
initBooleanSetting("highlightpin1", config.highlight_pin1, "highlightpin1Checkbox", setHighlightPin1);
|
|
|
|
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;
|
|
}
|
|
|
|
// 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 (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 {
|
|
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.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);
|
|
}
|
|
}
|
|
// 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) {
|
|
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, color, highlight) {
|
|
ctx = canvas.getContext("2d");
|
|
ctx.strokeStyle = color;
|
|
ctx.lineCap = "round";
|
|
for (var track of pcbdata.tracks[layer]) {
|
|
if (highlight && highlightedNet != track.net) continue;
|
|
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();
|
|
}
|
|
}
|
|
|
|
function drawZones(canvas, layer, color, highlight) {
|
|
ctx = canvas.getContext("2d");
|
|
ctx.strokeStyle = color;
|
|
ctx.fillStyle = color;
|
|
ctx.lineJoin = "round";
|
|
for (var zone of pcbdata.zones[layer]) {
|
|
if (!zone.path2d) {
|
|
zone.path2d = getPolygonsPath(zone);
|
|
}
|
|
if (highlight && highlightedNet != zone.net) continue;
|
|
ctx.fill(zone.path2d);
|
|
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.renderTracks) {
|
|
var trackColor = style.getPropertyValue(highlight ? '--track-color-highlight' : '--track-color');
|
|
drawTracks(canvas, layer, trackColor, highlight);
|
|
}
|
|
if (settings.renderZones) {
|
|
var zoneColor = style.getPropertyValue(highlight ? '--zone-color-highlight' : '--zone-color');
|
|
drawZones(canvas, layer, zoneColor, 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));
|
|
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) {
|
|
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));
|
|
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 bbox = applyRotation(pcbdata.edges_bbox);
|
|
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;
|
|
var flip = (layerdict.layer == "B");
|
|
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;
|
|
if (layerdict.layer == "B") {
|
|
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);
|
|
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 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();
|
|
}
|
|
|
|
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 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 createCheckboxChangeHandler(checkbox, references, row) {
|
|
return function () {
|
|
refsSet = getStoredCheckboxRefs(checkbox);
|
|
var markWhenChecked = settings.markWhenChecked == checkbox;
|
|
eventArgs = {
|
|
checkbox: checkbox,
|
|
refs: references,
|
|
}
|
|
if (this.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);
|
|
}
|
|
}
|
|
|
|
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 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 (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 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") {
|
|
th = createColumnHeader("Net name", "bom-netname", (a, b) => {
|
|
if (a > b) return -1;
|
|
if (a < b) return 1;
|
|
return 0;
|
|
});
|
|
tr.appendChild(th);
|
|
} 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] != b[i]) 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) {
|
|
while (bom.firstChild) {
|
|
bom.removeChild(bom.firstChild);
|
|
}
|
|
highlightHandlers = [];
|
|
footprintIndexToHandler = {};
|
|
netsToHandler = {};
|
|
currentHighlightedRowId = null;
|
|
var first = true;
|
|
if (settings.bommode == "netlist") {
|
|
bomtable = pcbdata.nets.slice();
|
|
} else {
|
|
switch (settings.canvaslayout) {
|
|
case 'F':
|
|
bomtable = pcbdata.bom.F.slice();
|
|
break;
|
|
case 'FB':
|
|
bomtable = pcbdata.bom.both.slice();
|
|
break;
|
|
case 'B':
|
|
bomtable = pcbdata.bom.B.slice();
|
|
break;
|
|
}
|
|
if (settings.bommode == "ungrouped") {
|
|
// expand bom table
|
|
expandedTable = []
|
|
for (var bomentry of bomtable) {
|
|
for (var ref of bomentry) {
|
|
expandedTable.push([ref]);
|
|
}
|
|
}
|
|
bomtable = expandedTable;
|
|
}
|
|
}
|
|
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);
|
|
} 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 = createCheckboxChangeHandler(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");
|
|
td.innerHTML = highlightFilter(Array.from(valueSet).join(", "));
|
|
tr.appendChild(td);
|
|
}
|
|
});
|
|
}
|
|
bom.appendChild(tr);
|
|
var handler = createRowHighlightHandler(tr.id, references, netname);
|
|
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("bot").style.height = "";
|
|
break;
|
|
case 'top-bottom':
|
|
document.getElementById("tb-btn").classList.add("depressed");
|
|
document.getElementById("frontcanvas").style.display = "";
|
|
document.getElementById("backcanvas").style.display = "";
|
|
document.getElementById("bot").style.height = "calc(100% - 80px)";
|
|
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("bot").style.height = "calc(100% - 80px)";
|
|
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 checkbox = bomrow.childNodes[checkboxnum].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 checkbox = bomrow.childNodes[checkboxnum + 1].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";
|
|
}
|
|
|
|
window.onload = function (e) {
|
|
initUtils();
|
|
initRender();
|
|
initStorage();
|
|
initDefaults();
|
|
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 style="float: right; height: 100%;">
|
|
<div class="hideonprint menu" style="float: right; top: 8px;">
|
|
<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="highlightpin1Checkbox" type="checkbox" onchange="setHighlightPin1(this.checked)">
|
|
Highlight first pin
|
|
</label>
|
|
<label class="menu-label">
|
|
<input id="dragCheckbox" type="checkbox" checked onchange="setRedrawOnDrag(this.checked)">
|
|
Continuous redraw on drag
|
|
</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">
|
|
<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"
|
|
style="float: right; position: relative; top: 8px">
|
|
<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"
|
|
style="float: right; position: relative; top: 8px">
|
|
<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"
|
|
style="float: right; position: relative; top: 8px">
|
|
<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" style="float: right; top: 8px;">
|
|
<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" style="float: right; top: 8px;">
|
|
<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>
|
|
</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 id="fileinfodiv" style="overflow: auto;">
|
|
<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>
|
|
<div id="bot" class="split" style="height: calc(100% - 80px)">
|
|
<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>
|