### Mutation Observer for Touch Action Attributes Source: https://github.com/open-ephys/onix-headstage-rhs2116/blob/main/pcb/manufacturing/bom/headstage-rhs2116.html Sets up a MutationObserver to watch for changes in 'touch-action' attributes on elements and their subtrees. It installs the observer on document load or immediately for existing subtrees. ```javascript 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:functi ``` -------------------------------- ### Handle Touch Start Event in JavaScript Source: https://github.com/open-ephys/onix-headstage-rhs2116/blob/main/pcb/manufacturing/bom/headstage-rhs2116.html Initiates touch event processing. It calls `vacuumTouches`, sets the primary touch, dedups synthetic mouse events, and increments click count if not scrolling. ```javascript touchstart:function(a){this.vacuumTouches(a),this.setPrimaryTouch(a.changedTouches\[0\],this.dedupSynthMouse(a),this.scrolling||(this.clickCount++,this.processTouches(a,this.overDown))} ``` -------------------------------- ### Pointer Event Polyfill Map Implementation Source: https://github.com/open-ephys/onix-headstage-rhs2116/blob/main/pcb/manufacturing/bom/headstage-rhs2116.html Provides a custom Map implementation using an array, suitable for environments where the native `Map` object might not be available or for specific polyfill needs. It includes methods like `set`, `has`, `delete`, `get`, `clear`, and `forEach`. ```javascript function b(){this.array=[],this.size=0}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)}}; ``` -------------------------------- ### Initialize Application State Source: https://github.com/open-ephys/onix-headstage-rhs2116/blob/main/pcb/manufacturing/bom/headstage-rhs2116.html Sets up the initial environment, event listeners, and UI state on window load. ```javascript 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; }); } ``` -------------------------------- ### Get Column Order Name Source: https://github.com/open-ephys/onix-headstage-rhs2116/blob/main/pcb/manufacturing/bom/headstage-rhs2116.html Extracts the column name attribute, mapping specific checkbox classes to a generic identifier. ```javascript function getColumnOrderName(elem) { var cname = elem.getAttribute("col_name"); if (cname === "bom-checkbox") return "checkboxes"; else return cname; } ``` -------------------------------- ### Get Circle Path Source: https://github.com/open-ephys/onix-headstage-rhs2116/blob/main/pcb/manufacturing/bom/headstage-rhs2116.html Returns a Path2D object representing a circle with a given radius. This is a utility function likely used in conjunction with other drawing functions. ```javascript function getCirclePath(radius) { var path ``` -------------------------------- ### Clone Repository and Navigate Source: https://context7.com/open-ephys/onix-headstage-rhs2116/llms.txt Command to clone the repository and navigate into the project directory. Replace `` with the actual URL. ```bash # Clone the repository git clone cd headstage-rhs2116 ``` -------------------------------- ### Initialize BOM Table Source: https://github.com/open-ephys/onix-headstage-rhs2116/blob/main/pcb/manufacturing/bom/headstage-rhs2116.html Sets up the table structure by populating headers, body, and handlers. ```javascript function populateBomTable() { populateBomHeader(); populateBomBody(); setBomHandlers(); resizableGrid(bomhead); } ``` -------------------------------- ### Initialize BOM and Event Listeners Source: https://github.com/open-ephys/onix-headstage-rhs2116/blob/main/test-board/bom/test-board.html Initializes the BOM, sets up event listeners for fullscreen changes and window resizing. Ensures proper rendering and responsiveness. ```javascript bombody = 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); ``` -------------------------------- ### Split.js JavaScript for Dragging Logic Source: https://github.com/open-ephys/onix-headstage-rhs2116/blob/main/test-board/bom/test-board.html Core JavaScript logic for Split.js, handling mouse and touch events to enable dragging and resizing of split panes. Includes event listener setup and cleanup. ```javascript 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.dr ``` -------------------------------- ### Default Settings Initialization Source: https://github.com/open-ephys/onix-headstage-rhs2116/blob/main/pcb/manufacturing/bom/headstage-rhs2116.html Initializes default settings by reading from storage or using configuration defaults. It ensures that settings like bomlayout and bommode are valid. ```javascript 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("canvaslayo ``` -------------------------------- ### Initialize Canvas Rendering Source: https://github.com/open-ephys/onix-headstage-rhs2116/blob/main/pcb/manufacturing/bom/headstage-rhs2116.html Sets up the front and back canvas transformation states and attaches mouse event handlers. ```javascript 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); } ``` -------------------------------- ### Initialize UI Settings Source: https://github.com/open-ephys/onix-headstage-rhs2116/blob/main/test-board/bom/test-board.html Initializes and applies user settings for hidden columns and column order from local storage. Ensures settings are valid and fall back to defaults if necessary. ```javascript 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; ``` -------------------------------- ### Window Load Initialization Source: https://github.com/open-ephys/onix-headstage-rhs2116/blob/main/test-board/bom/test-board.html Initializes various components and settings when the window loads, including utility functions, rendering, storage, defaults, gutters, and metadata. ```javascript window.onload = function (e) { initUtils(); initRender(); initStorage(); initDefaults(); cleanGutters(); populateMetadata(); dbgdiv = document.getElementById("dbg"); bom = docum ``` -------------------------------- ### Export Manufacturing Files Source: https://context7.com/open-ephys/onix-headstage-rhs2116/llms.txt Instructions for generating fabrication outputs from the KiCad environment. ```bash # From KiCad, use the following exports: # # File → Fabrication Outputs → Gerbers (.gbr) # File → Fabrication Outputs → Drill Files (.drl) # File → Fabrication Outputs → Component Placement (.csv) # File → Fabrication Outputs → BOM (.csv) # # Current manufacturing files are in: # pcb/manufacturing/gerber/ - Gerber files # pcb/manufacturing/assembly/ - Pick and place files # pcb/manufacturing/bom/ - Bill of materials ``` -------------------------------- ### Initialize Resizable Table Grid Source: https://github.com/open-ephys/onix-headstage-rhs2116/blob/main/pcb/manufacturing/bom/headstage-rhs2116.html Sets up column resizing handles for a table header, excluding specific columns and applying percentage-based widths. ```javascript 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.stop ``` -------------------------------- ### Initialize Default Settings Source: https://github.com/open-ephys/onix-headstage-rhs2116/blob/main/test-board/bom/test-board.html Initializes default settings for the application, reading from local storage or using configuration defaults. Handles various UI elements like checkboxes and layout options. ```javascript 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 } ``` -------------------------------- ### Open KiCad Project Source: https://context7.com/open-ephys/onix-headstage-rhs2116/llms.txt Command to open the headstage project in KiCad 8.0 or later. ```bash kicad pcb/headstage-rhs2116.kicad_pro ``` -------------------------------- ### Initialize Canvas Rendering State Source: https://github.com/open-ephys/onix-headstage-rhs2116/blob/main/test-board/bom/test-board.html Sets up the initial transformation and DOM references for front and back canvas layers. ```javascript 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); } ``` -------------------------------- ### Load Settings from File Source: https://github.com/open-ephys/onix-headstage-rhs2116/blob/main/pcb/manufacturing/bom/headstage-rhs2116.html Loads settings from a selected JSON file. It validates the file type and metadata before applying the settings. Includes user prompts for confirmation if metadata mismatches. ```javascript 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(); } ``` -------------------------------- ### Save File Function Source: https://github.com/open-ephys/onix-headstage-rhs2116/blob/main/test-board/bom/test-board.html Creates a downloadable link for a given blob and filename, then programmatically clicks it to initiate the download. ```javascript function saveFile(filename, blob) { var link = document.createElement("a"); var objurl = URL.createObjectURL(blob); link.download = filename; link.href = objurl; link.click(); } ``` -------------------------------- ### Update UI and Storage with Settings Source: https://github.com/open-ephys/onix-headstage-rhs2116/blob/main/test-board/bom/test-board.html Applies settings to the UI elements and writes them to storage. This function is called during initialization and when settings change. ```javascript function updateSettings() { 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); } ``` -------------------------------- ### Initialize Resizable Table Grid Source: https://github.com/open-ephys/onix-headstage-rhs2116/blob/main/test-board/bom/test-board.html Adds interactive handles to table columns to allow user-driven resizing. ```javascript 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)) } } ``` -------------------------------- ### Initialize Split.js Source: https://github.com/open-ephys/onix-headstage-rhs2116/blob/main/test-board/bom/test-board.html Initializes Split.js to create a resizable split view between two elements. Configure minimum sizes, gutter sizes, and callbacks for drag events. ```javascript Split(["#one", "#two"], { sizes: [50, 50], minSize: [100, 100], gutterSize: 10, direction: "horizontal", cursor: "col-resize", onDragEnd: function() { console.log("Drag ended"); } }); ``` -------------------------------- ### Project Configuration Object Source: https://github.com/open-ephys/onix-headstage-rhs2116/blob/main/pcb/manufacturing/bom/headstage-rhs2116.html Defines configuration settings for the project, including UI preferences like dark mode, visibility of elements, board rotation, and display options for checkboxes, BOM view, and layer view. ```javascript var config = {"dark_mode": true, "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", "Voltage", "CASE/PACKAGE", "Tolerance"]} ``` -------------------------------- ### Populate BOM Header Source: https://github.com/open-ephys/onix-headstage-rhs2116/blob/main/test-board/bom/test-board.html Initializes the BOM table header and builds the visibility toggle menu. ```javascript 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); }) ``` -------------------------------- ### Settings Export Source: https://github.com/open-ephys/onix-headstage-rhs2116/blob/main/pcb/manufacturing/bom/headstage-rhs2116.html Serializes current application settings into a JSON blob for saving. ```javascript 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/jso ``` -------------------------------- ### Load Application Settings Source: https://github.com/open-ephys/onix-headstage-rhs2116/blob/main/test-board/bom/test-board.html Allows the user to load application settings from a previously saved JSON file. Includes validation to ensure the file is a compatible settings file and prompts the user if metadata does not match. ```javascript 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(); } ``` -------------------------------- ### Initialize and Manage Split Panes Source: https://github.com/open-ephys/onix-headstage-rhs2116/blob/main/pcb/manufacturing/bom/headstage-rhs2116.html Initializes and manages horizontal and vertical split panes for the BOM and canvas views. Destroys existing split instances if they exist before creating new ones. Ensures layout adjustments are called on drag end. ```javascript dd("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); } ``` -------------------------------- ### Initialize Splitter UI Source: https://github.com/open-ephys/onix-headstage-rhs2116/blob/main/test-board/bom/test-board.html Initializes a resizable UI splitter component. It sets up event listeners for drag and drop functionality on gutters and elements. Requires a list of elements to split and configuration options. ```javascript 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"):(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 f0&&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}}}); ``` -------------------------------- ### Initialize and Manage Browser Storage Source: https://github.com/open-ephys/onix-headstage-rhs2116/blob/main/test-board/bom/test-board.html Functions to initialize, read, and write to localStorage or sessionStorage with a project-specific prefix. ```javascript 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); } } ``` -------------------------------- ### LZ-String Compression Algorithm Source: https://github.com/open-ephys/onix-headstage-rhs2116/blob/main/pcb/manufacturing/bom/headstage-rhs2116.html A library for compressing and decompressing strings using an LZ-based algorithm. ```javascript 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>=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= ``` -------------------------------- ### Manage UI Visibility and Settings Source: https://github.com/open-ephys/onix-headstage-rhs2116/blob/main/test-board/bom/test-board.html Functions to update local storage and application settings for various UI elements, triggering a redraw if initialization is complete. ```javascript ge("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(); } ``` -------------------------------- ### Test Board KiCad Project Files Source: https://context7.com/open-ephys/onix-headstage-rhs2116/llms.txt This directory contains the KiCad project files for the companion test board, including schematic, PCB layout, and project files, along with exported PDF and BOM. ```text test-board/ ├── test-board.kicad_sch # Test board schematic ├── test-board.kicad_pcb # Test board PCB layout ├── test-board.kicad_pro # KiCad project ├── test-board.pdf # Schematic PDF export ├── bom/ │ └── test-board.html # Interactive BOM ├── gerber/ # Manufacturing Gerbers ├── datasheets/ │ └── A79026-001.pdf # Connector datasheet └── models/ ├── 413194-1.stp # 3D model (STEP) └── 413194-1.wrl # 3D model (VRML) ``` -------------------------------- ### Manage Browser Storage Source: https://github.com/open-ephys/onix-headstage-rhs2116/blob/main/pcb/manufacturing/bom/headstage-rhs2116.html Initializes and provides access to localStorage or sessionStorage with a project-specific prefix. ```javascript 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); } } ``` -------------------------------- ### Initialize Boolean Settings Source: https://github.com/open-ephys/onix-headstage-rhs2116/blob/main/pcb/manufacturing/bom/headstage-rhs2116.html Configures checkbox states from local storage or default values and triggers associated callback functions. ```javascript 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); } ``` -------------------------------- ### Handle User Interactions Source: https://github.com/open-ephys/onix-headstage-rhs2116/blob/main/test-board/bom/test-board.html Event handlers for footprint and net selection, as well as filter updates. ```javascript 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; } } } ``` ```javascript function netClicked(net) { if (net in netsToHandler) { netsToHandler[net](); smoothScrollToRow(currentHighlightedRowId); } else { clearHighlightedFootprints(); highlightedNet = net; drawHighlights(); } } ``` ```javascript function updateFilter(input) { filter = input.toLowerCase(); populateBomTable(); } ``` ```javascript function updateRefLookup(input) { reflookup = input.toLowerCase(); populateBomTable(); } ``` -------------------------------- ### Initialize Flexbox Layout Resizer Source: https://github.com/open-ephys/onix-headstage-rhs2116/blob/main/pcb/manufacturing/bom/headstage-rhs2116.html A utility for creating resizable flexbox layouts. It handles mouse and touch events for dragging gutters between elements. ```javascript lect="",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 f0&&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}}}); ```