diff --git a/.gitignore b/.gitignore index 8279ddd3..1b86d41f 100644 --- a/.gitignore +++ b/.gitignore @@ -6,3 +6,4 @@ test releases demo config.ini +InteractiveHtmlBom/web/user* diff --git a/InteractiveHtmlBom/core/ibom.py b/InteractiveHtmlBom/core/ibom.py index 2712834c..5d0e6368 100644 --- a/InteractiveHtmlBom/core/ibom.py +++ b/InteractiveHtmlBom/core/ibom.py @@ -224,10 +224,11 @@ def get_compressed_pcbdata(pcbdata): def generate_file(pcb_file_dir, pcb_file_name, pcbdata, config): def get_file_content(file_name): path = os.path.join(os.path.dirname(__file__), "..", "web", file_name) + if not os.path.exists(path): + return "" with io.open(path, 'r', encoding='utf-8') as f: return f.read() - log.info("Dumping pcb json data") if os.path.isabs(config.bom_dest_dir): bom_file_dir = config.bom_dest_dir @@ -239,18 +240,26 @@ def get_file_content(file_name): bom_file_dir = os.path.dirname(bom_file_name) if not os.path.isdir(bom_file_dir): os.makedirs(bom_file_dir) + log.info("Compressing pcb data") + compressed_pcbdata = get_compressed_pcbdata(pcbdata) + log.info("Dumping pcb data") config_js = "var config = " + config.get_html_config() html = get_file_content("ibom.html") html = html.replace('///CSS///', get_file_content('ibom.css')) + html = html.replace('///USERCSS///', get_file_content('user.css')) html = html.replace('///SPLITJS///', get_file_content('split.js')) html = html.replace('///LZ-STRING///', get_file_content('lz-string.js')) html = html.replace('///POINTER_EVENTS_POLYFILL///', get_file_content('pep.js')) html = html.replace('///CONFIG///', config_js) - html = html.replace('///PCBDATA///', get_compressed_pcbdata(pcbdata)) + html = html.replace('///PCBDATA///', compressed_pcbdata) html = html.replace('///UTILJS///', get_file_content('util.js')) html = html.replace('///RENDERJS///', get_file_content('render.js')) html = html.replace('///IBOMJS///', get_file_content('ibom.js')) + html = html.replace('///USERJS///', get_file_content('user.js')) + html = html.replace('///USERHEADER///', get_file_content('userheader.html')) + html = html.replace('///USERFOOTER///', get_file_content('userfooter.html')) + with io.open(bom_file_name, 'wt', encoding='utf-8') as bom: bom.write(html) log.info("Created file %s", bom_file_name) diff --git a/InteractiveHtmlBom/web/ibom.html b/InteractiveHtmlBom/web/ibom.html index a6674319..ba616ee4 100644 --- a/InteractiveHtmlBom/web/ibom.html +++ b/InteractiveHtmlBom/web/ibom.html @@ -7,6 +7,7 @@ Interactive BOM for KiCAD +///USERHEADER///
@@ -292,6 +297,7 @@
+///USERFOOTER/// diff --git a/InteractiveHtmlBom/web/ibom.js b/InteractiveHtmlBom/web/ibom.js index 68489cd5..3c6bf2e1 100644 --- a/InteractiveHtmlBom/web/ibom.js +++ b/InteractiveHtmlBom/web/ibom.js @@ -153,6 +153,10 @@ function createCheckboxChangeHandler(checkbox, references) { return function(evt) { refsSet = getStoredCheckboxRefs(checkbox); var darkenWhenChecked = settings.darkenWhenChecked == checkbox; + eventArgs = { + checkbox: checkbox, + refs: references, + } if (this.checked) { // checkbox ticked for (var ref of references) { @@ -161,6 +165,7 @@ function createCheckboxChangeHandler(checkbox, references) { if (darkenWhenChecked) { evt.target.parentElement.parentElement.classList.add("checked"); } + eventArgs.state = 'checked'; } else { // checkbox unticked for (var ref of references) { @@ -169,10 +174,12 @@ function createCheckboxChangeHandler(checkbox, references) { if (darkenWhenChecked) { evt.target.parentElement.parentElement.classList.remove("checked"); } + 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); } } @@ -198,6 +205,13 @@ function createRowHighlightHandler(rowid, refs, net) { highlightedModules = refs ? refs.map(r => r[1]) : []; highlightedNet = net; drawHighlights(); + EventHandler.emitEvent( + IBOM_EVENT_TYPES.HIGHLIGHT_EVENT, + { + rowid: rowid, + refs: refs, + net: net + }); } } @@ -871,7 +885,7 @@ function populateDarkenWhenCheckedOptions() { container.innerHTML = ''; container.parentElement.style.display = "inline-block"; - + function createOption(name, displayName) { var id = "darkenWhenChecked-" + name; diff --git a/InteractiveHtmlBom/web/util.js b/InteractiveHtmlBom/web/util.js index 953f3e93..6cae1378 100644 --- a/InteractiveHtmlBom/web/util.js +++ b/InteractiveHtmlBom/web/util.js @@ -518,3 +518,34 @@ function initDefaults() { 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", +} + +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();