/* JQUERY ENGINE */ /* JSPDF ENGINE */ /* KONVA ENGINE */ /* MARKJS ENGINE */ var additionalRowStorage; if (typeof additionalRowStorage === 'undefined') { var additionalRowStorage = { 'ELEMENT_IDS': {} }; } // populate all session_key fields $('.session_key').text(sessionKey); // just used in rail report // rail summary has to be built first if (typeof buildRailSummaryFromJson != 'undefined') { buildRailSummaryFromJson(); } if (typeof buildJsonTable != 'undefined') { buildJsonTable(null, true); } // this function will clear the cache for all reports with the same uid function clearReportCache() { localStorage.removeItem(sessionKey); var thisUid = sessionKey.split('/')[0]; for (var cache in localStorage) { var uid = cache.split('/')[0]; if (thisUid == uid) { localStorage.removeItem(cache); } } } function cacheWebPage() { delayedExecutionOrCancel('cacheWebPage', () => { localStorage.setItem(sessionKey, JSON.stringify(additionalRowStorage)); }, 500); } function loadFromCache() { var cache = localStorage.getItem(sessionKey); // if there is a cache we want to use that. // the presence of a cache means the external json file (if it exists/existed) // has already been loaded into memory for this report instance if (cache != null) { additionalRowStorage = JSON.parse(cache); if (!additionalRowStorage['ELEMENT_IDS']) { additionalRowStorage['ELEMENT_IDS'] = {}; } } cacheWebPage(); } function textRegexMatchArray(text, regex) { try { var reg = new RegExp(regex, 'i'); return text.match(reg); } catch { return false; } } function equalsContainsMatches(text, match) { if (text === match) return true; if (text.indexOf(match) > -1) return true; return textRegexMatchArray(text, match); } function escapeRegex(string) { if (!escapeRegex.regex) { escapeRegex.regex = /[-\/\\^$*+?.()|[\]{}]/g; } return string.replace(escapeRegex.regex, '\\$&'); } function getCurrentDirectory() { // origin will be http if its on the webserver and file if its local if (window.location.origin.match(/https?:\/\//i)) { const ret = window.location.href.split('/').slice(0, -1).join('/'); return ret; } // start at index 1 because index 0 is empty meaning it adds a slash at the beginning // and we dont want the extra slash. // end at index -1 because the last index is the file name and we dont want that either. var dir = window.location.pathname.split('/').slice(1, -1).join('/'); // linux directory structure fix. if the window location pathname doesnt include the drive, we add the file prefix. this fixes it in bills vm. if (!dir.match(/^\w+:\//i)) { dir = 'file:///' + dir; } return dir; } function moveCaretToEnd(el) { var range = document.createRange(); range.selectNode(el); range.setStart(el, 0); range.setEnd(el, 1); range.collapse(); var sel = window.getSelection(); sel.removeAllRanges(); sel.addRange(range); } function copyTextToClipboard(text) { //Create a textbox field where we can insert text to. var copyFrom = document.createElement("textarea"); //Set the text content to be the text you wished to copy. copyFrom.textContent = text; //Append the textbox field into the body as a child. //"execCommand()" only works when there exists selected text, and the text is inside //document.body (meaning the text is part of a valid rendered HTML element). document.body.appendChild(copyFrom); //Select all the text! copyFrom.select(); //Execute command document.execCommand('copy'); //(Optional) De-select the text using blur(). copyFrom.blur(); //Remove the textbox field from the document.body, so no other JavaScript nor //other elements can get access to this. document.body.removeChild(copyFrom); } function downloadTextToFile(text, fileName) { var a = document.createElement("a"); var blob = new Blob([text], { type: 'application/text' }); a.href = window.URL.createObjectURL(blob); a.download = fileName; document.body.appendChild(a); a.click(); document.body.removeChild(a); } function closeModal() { $('.modal').css('display', 'none'); } function showSettingsModal() { if (!document.getElementById('settings_modal_button').classList.contains('disabled')) { $('#modal_CSET').css('display', 'block'); } } // this will queue a delayed execution keyed to the name that is given. // if the same name is given again, it will cancel the previous one in favor of the next one. function delayedExecutionOrCancel(name, callback, time) { clearTimeout(delayedExecutionOrCancel[name]); delayedExecutionOrCancel[name] = setTimeout(callback, time); } // ALL report web listeners need to go in here so they can be reloaded when we reload the table data function initializeEventListeners() { // reset functions that have cached data getHeaderTextFromCell.tableHeaders = undefined; getHeaderTextFromCell.cache = undefined getActiveRailName.netName = undefined; col_id_from_header_name.tableHeaders = undefined; col_id_from_header_name.cache = undefined lastSelectedRow = null; createTableSortFunctions(); $('.sorted-table').tablesort(); loadFromCache(); setTableRowSelectionEvents(); setRowKeys(); setEditableCellListeners(); // needs to be above loadAdditionalRowStorage loadAdditionalRowStorage(); setTableCellHeightRestrictionEvents(); initRailSummaryStuff(); setHighlightOnClickEvents(); } function initializeEventListeners_PostLoad() { if (typeof initCustomContextMenu != 'undefined') { initCustomContextMenu(); } if (typeof initCustomRailContextMenu != 'undefined') { initCustomRailContextMenu(); } $('body').css("visibility", "visible"); /* we want the last thing to be the prefilter */ if (typeof setPrefilledFilter != 'undefined') { setPrefilledFilter(); } /* so the uri prefilter option works - needs to happen after body is visible */ if (typeof filterTable != 'undefined') { filterTable(true); } } // the idea here is the user can select either the 'All' button or one or many of the other buttons. // pressing the 'All' button will desect all the other buttons. // pressing any other button will select it and deselect the 'All' button if it's selected function filterTableBy(event, type) { // if no type is selected then if (!type) { $('.table-sub-filter-button').removeClass('selected'); } else { $('.table-sub-filter-button.default').removeClass('selected'); } $(event.currentTarget).toggleClass('selected'); if ($('.table-sub-filter-button.selected').length == 0) { $('.table-sub-filter-button.default').addClass('selected'); } filterTable(); } function filterTable(skipWait = false) { var filterSelection = $('#table-filter-column-selection'); if (!filterSelection.length) return; delayedExecutionOrCancel('filterTable', () => { saveFilteredTableSearchInputs(); $('.overflow_div .sorted-table tbody').unmark(); const colName = filterSelection.children('option:selected').text(); const colIdx = col_id_from_header_name(colName); if (colIdx < 0) return; if (filterSelection.find(':selected').hasClass('is-number-range')) { $('#table-filter-text-input-container').css('display', 'none'); $('#table-filter-range-input-container').css('display', ''); hideRowRange(colIdx); } else { $('#table-filter-text-input-container').css('display', ''); $('#table-filter-range-input-container').css('display', 'none'); hideRows(colIdx); } const rowCount = $('.overflow_div .sorted-table tbody tr:visible').length; $('#filter-search-result-text').text(rowCount); }, skipWait ? 0 : 1000); } function hideRowRange(colIdx) { var rangeLow = $('#table-filter-range-input-low'); var rangeHigh = $('#table-filter-range-input-high'); const showEdited = $('#table-sub-filter-edited').hasClass('selected'); const showVoltMissmatch = $('#table-sub-filter-volt-mismatch').hasClass('selected'); const pinVoltColumn = col_id_from_header_name('pin volt'); const netVoltColumn = col_id_from_header_name('net volt'); for (const rowEl of $('.overflow_div .sorted-table tbody tr').toArray()) { const row = $(rowEl); const children = row.children(); if (showEdited) { var noEdits = true; for (const cell of children) { if ($(cell).hasClass('edited')) { noEdits = false; break; } } if (noEdits) { row.css('display', 'none'); continue; } } if (showVoltMissmatch && pinVoltColumn >= 0 && netVoltColumn >= 0) { const pinVolt = children.eq(pinVoltColumn).text().replace(/[^0-9.\-]/ig, ''); const netVolt = children.eq(netVoltColumn).text().replace(/[^0-9.\-]/ig, ''); if (!pinVolt || !netVolt || pinVolt == netVolt) { row.css('display', 'none'); continue; } } const cell = children.eq(colIdx); const cellNumber = Number(cell.text().replace(/[^0-9.\-]/ig, '')); if (Number.isNaN(cellNumber) || cellNumber < rangeLow.val() || cellNumber > rangeHigh.val()) { cell.parent('tr').hide(); } else { cell.parent('tr').show(); } } } function hideRows(colIdx) { const filter = $('#table-filter-text-input-field').val(); const matchByContains = $('#table-filter-text-input-logic').val() === 'c'; const filterValues = filter.split(','); const showEdited = $('#table-sub-filter-edited').hasClass('selected'); const showVoltMissmatch = $('#table-sub-filter-volt-mismatch').hasClass('selected'); const pinVoltColumn = col_id_from_header_name('pin volt'); const netVoltColumn = col_id_from_header_name('net volt'); for (const rowEl of $('.overflow_div .sorted-table tbody tr').toArray()) { const row = $(rowEl); const children = row.children(); if (showEdited) { var noEdits = true; for (const cell of children) { if ($(cell).hasClass('edited')) { noEdits = false; break; } } if (noEdits) { row.css('display', 'none'); continue; } } if (showVoltMissmatch && pinVoltColumn >= 0 && netVoltColumn >= 0) { const pinVolt = children.eq(pinVoltColumn).text().replace(/[^0-9.\-]/ig, ''); const netVolt = children.eq(netVoltColumn).text().replace(/[^0-9.\-]/ig, ''); if (!pinVolt || !netVolt || pinVolt == netVolt) { row.css('display', 'none'); continue; } } // if theres no filter applied we want to show the row if (filterValues.length == 0) { row.css('display', ''); continue; } const cell = children.eq(colIdx); const cellText = cell.text().trim(); if (matchByContains) { for (const searchString of filterValues) { if (equalsContainsMatches(cellText, searchString)) { row.css('display', ''); cell.markRegExp(new RegExp(searchString, 'i')); break; } else { row.css('display', 'none'); } } } else { for (const searchString of filterValues) { if (equalsContainsMatches(cellText, searchString)) { row.css('display', 'none'); break; } else { row.css('display', ''); } } } } } function acceptRejectRows(rowElements, newValue, saveButtonOn = true) { var rows = $(rowElements); if (rows.length <= 0) return; var arCellId = col_id_from_header_name('a/r|accept.+?reject'); var msCellId = col_id_from_header_name('match.+?score'); // if there is no accept reject row we wont have anything to edit so no point in continuing... if (arCellId < 0) return; var msClass, arClass, nodeClass; if (newValue >= 100) { msClass = 'override-good'; arClass = 'accept'; nodeClass = 'green-node'; } else if (newValue >= 0) { msClass = 'override-bad'; arClass = 'reject'; nodeClass = 'red-node'; } rows.toArray().forEach(rowElement => { var row = $(rowElement); var rowKey = getRowKey(row); var cells = row.children(); var arCell = $(cells[arCellId]); var acceptRejectCellMap = getRowCellMap(rowKey, cells[arCellId]); // clear existing values arCell.removeClass('accept') .removeClass('reject'); delete acceptRejectCellMap['css-class']; delete acceptRejectCellMap['text']; if (newValue >= 0) { arCell.addClass(arClass); acceptRejectCellMap['css-class'] = arClass; } row.find('td .highlight-on-click').toArray().forEach(element => setNodeColor(element, nodeClass)); // if no match score row, then we stop here, otherwise its an ep compare/verify report and we edit the match score if (msCellId < 0) return; var msCell = $(cells[msCellId]); var matchScoreCellMap = getRowCellMap(rowKey, cells[msCellId]); // clear existing values msCell.removeClass('override-bad'); msCell.removeClass('override-good'); delete matchScoreCellMap['css-class']; delete matchScoreCellMap['overrideValue']; if (newValue >= 0) { msCell.addClass(msClass); matchScoreCellMap['css-class'] = msClass; matchScoreCellMap['overrideValue'] = newValue; // this gets picked up by NetBom to be put in the Excel report } }); if (saveButtonOn) { cacheWebPage(); } } $('body').keydown( function (event) { if (event.key === "Shift" || event.key === "Control" || event.key === "Alt") { return; } if (event.target.id === 'table-filter-text-input-field') { if (event.key === 'Escape') { // $('input#table-filter-text-input-field').val(''); $('input#table-filter-text-input-field').blur(); // filterTable(); } else if (event.key === 'ArrowUp') { var sel = $('#table-filter-column-selection'); var optionIndex = sel[0].options.selectedIndex; var nextIndex = optionIndex - 1; if (nextIndex < 0) return; sel.val(sel[0].options[nextIndex].value); } else if (event.key === 'ArrowDown') { var sel = $('#table-filter-column-selection'); var optionIndex = sel[0].options.selectedIndex; var nextIndex = optionIndex + 1; if (nextIndex >= sel[0].options.length) return; sel.val(sel[0].options[nextIndex].value); } } if (event.target.localName !== 'body') return; if (event.key === "ArrowUp") { var allRows = $(".overflow_div .sorted-table tbody tr"); var selectedTableRow = $('.overflow_div .sorted-table tbody tr.selectedRow:first'); var rowToSelect; for (var i = $(selectedTableRow).index() - 1; i < allRows.length; i--) { if ($(allRows[i]).css('display') !== 'none') { if (i >= 0) { rowToSelect = allRows[i]; } break; } } if (rowToSelect) { // deselect all rows next to the selected row if (!event.ctrlKey && !event.shiftKey) { deselectTableRow($(selectedTableRow).siblings()); deselectTableRow($(selectedTableRow)); } // select our new row selectTableRow(rowToSelect); } // we dont want the scroll bar to move if we are pressing arrow keys event.preventDefault(); } else if (event.key === "ArrowDown") { var allRows = $(".overflow_div .sorted-table tbody tr"); var selectedTableRow = $('.overflow_div .sorted-table tbody tr.selectedRow:last'); var rowToSelect; for (var i = $(selectedTableRow).index() + 1; i < allRows.length; i++) { if ($(allRows[i]).css('display') !== 'none') { if (i < allRows.length) { rowToSelect = allRows[i]; } break; } } if (rowToSelect) { // deselect all rows next to the selected row if (!event.ctrlKey && !event.shiftKey) { deselectTableRow($(selectedTableRow).siblings()); deselectTableRow($(selectedTableRow)); } // select our new row selectTableRow(rowToSelect); } // we dont want the scroll bar to move if we are pressing arrow keys event.preventDefault(); } else if (event.ctrlKey && !event.shiftKey && event.key !== 'Backspace') { // ctrl + key if (event.altKey) { // ctrl + alt + key switch (event.key.toLowerCase()) { case 'a': // accept var selectedTableRow = $('.overflow_div .sorted-table tbody tr.selectedRow'); acceptRejectRows(selectedTableRow, '100', true); break; case 'r': // reject var selectedTableRow = $('.overflow_div .sorted-table tbody tr.selectedRow'); acceptRejectRows(selectedTableRow, '0', true); break; case 'c': // clear var selectedTableRow = $('.overflow_div .sorted-table tbody tr.selectedRow'); acceptRejectRows(selectedTableRow, '-1', true); break; case 'enter': // accept rows and set selected row to next row var selectedTableRow = $('.overflow_div .sorted-table tbody tr.selectedRow'); acceptRejectRows(selectedTableRow, '100', true); // get all rows so we can use the index of the last table row var allRows = $(".overflow_div .sorted-table tbody tr"); deselectTableRow(null); for (var i = selectedTableRow.last().index() + 1; i < allRows.length; i++) { if ($(allRows[i]).css('display') !== 'none') { if (i < allRows.length) { selectTableRow(allRows[i]); } break; } } break; default: return; } } else { // ctrl + key switch (event.key.toLowerCase()) { case 'a': // select all visible rows var allRows = $(".overflow_div .sorted-table tbody tr:visible"); selectTableRow(allRows); break; case 's': // save edited rows saveAdditionalRowStorage(); break; case 'f': // jump to filter $('#table-filter-text-input-field').select(); break; default: return; } } // we dont want normal ctrl+key things to happen if we have custom ones in place event.preventDefault(); } else { writeToSelectedCells(event); } }); function writeToSelectedCells(event) { // we have to check against the literal 'true' because it holds true or false as a string lol if (document.activeElement.contentEditable !== 'true') { return; } var commentCellIdx = $(document.activeElement).index(); // make sure we get an index otherwise we dont want to do anything if (commentCellIdx >= 0) { var selectedTableRow = $('.overflow_div .sorted-table tbody tr.selectedRow'); // we return if the selected rows are 1 because if that is the case we let the parent function take care of writing, we dont need to bulk write a single row if (selectedTableRow.length <= 1) return 0; if (event.key.length === 1) { // regular characters selectedTableRow.toArray().forEach(row => { var commentCell = $(row).children()[commentCellIdx]; $(commentCell).text($(commentCell).text() + event.key); userCellEditCallback(commentCell); }); } else if (event.key === 'Backspace') { if (event.ctrlKey) { // delete word by word selectedTableRow.toArray().forEach(row => { var commentCell = $(row).children()[commentCellIdx]; var txt = $(commentCell).text(); $(commentCell).text(txt.substring(0, txt.lastIndexOf(' '))); userCellEditCallback(commentCell); }); } else { selectedTableRow.toArray().forEach(row => { var commentCell = $(row).children()[commentCellIdx]; var txt = $(commentCell).text(); $(commentCell).text(txt.substring(0, txt.length - 1)); userCellEditCallback(commentCell); }); } } } return commentCellIdx >= 0; } /* A simple, lightweight jQuery plugin for creating sortable tables. https://github.com/kylefox/jquery-tablesort Version 0.0.11 */ function createTableSortFunctions() { (function ($) { $.tablesort = function ($table, settings) { var self = this; this.$table = $table; this.$thead = this.$table.find('thead'); this.settings = $.extend({}, $.tablesort.defaults, settings); this.$sortCells = this.$thead.length > 0 ? this.$thead.find('th:not(.no-sort)') : this.$table.find('th:not(.no-sort)'); this.$sortCells.on('dblclick.tablesort', function () { self.sort($(this)); }); this.index = null; this.$th = null; this.direction = null; this.sorting = false; this.firstSort = true; self.sort($(this.$sortCells[0])); }; $.tablesort.prototype = { sort: function (th, direction) { if (this.sorting) return; this.sorting = true; //click on a different column if (this.index !== th.index()) { this.direction = 'asc'; this.index = th.index(); } else if (direction !== 'asc' && direction !== 'desc') this.direction = this.direction === 'asc' ? 'desc' : 'asc'; else this.direction = direction; direction = this.direction == 'asc' ? 1 : -1; if (this.firstSort) { this.sorting = false; this.firstSort = false; return; } var start = new Date(), self = this, table = this.$table, rowsContainer = table.find('tbody').length > 0 ? table.find('tbody') : table, rows = rowsContainer.find('tr'), // .has('td, th') <--- this slows things down tremendously cells = rows.find(':nth-child(' + (th.index() + 1) + ')').filter('td, th'), sortBy = th.data().sortBy, sortedMap = []; var unsortedValues = cells.map(function (idx, cell) { if (sortBy) return (typeof sortBy === 'function') ? sortBy($(th), $(cell), self) : sortBy; return ($(this).data().sortValue != null ? $(this).data().sortValue : $(this).text()); }); if (unsortedValues.length === 0) { this.sorting = false; return; } self.$table.trigger('tablesort:start', [self]); self.log("Sorting by " + this.index + ' ' + this.direction); // Try to force a browser redraw self.$table.css("display"); // Run sorting asynchronously on a timeout to force browser redraw after // `tablesort:start` callback. Also avoids locking up the browser too much. setTimeout(function () { self.$sortCells.removeClass(self.settings.asc + ' ' + self.settings.desc); for (var i = 0, length = unsortedValues.length; i < length; i++) { sortedMap.push({ index: i, cell: cells[i], row: rows[i], value: unsortedValues[i] }); } var sorter = new Intl.Collator('en', { sensitivity: 'base', numeric: true }); sortedMap.sort(function (a, b) { // should be fastest javascript compare function return sorter.compare(a.value, b.value) * direction; return a.value.localeCompare(b.value, undefined, { numeric: true, sensitivity: 'base' }) * direction; return self.settings.compare(a.value, b.value) * direction; }); $.each(sortedMap, function (i, entry) { rowsContainer.append(entry.row); }); th.addClass(self.settings[self.direction]); self.sorting = false; self.log('Sort finished in ' + ((new Date()).getTime() - start.getTime()) + 'ms'); self.$table.trigger('tablesort:complete', [self]); //Try to force a browser redraw self.$table.css("display"); }, unsortedValues.length > 2000 ? 200 : 10); }, log: function (msg) { if (($.tablesort.DEBUG || this.settings.debug) && console && console.log) { console.log('[tablesort] ' + msg); } }, destroy: function () { this.$sortCells.off('dblclick.tablesort'); this.$table.data('tablesort', null); return null; } }; $.tablesort.DEBUG = false; $.tablesort.defaults = { debug: $.tablesort.DEBUG, asc: 'sorted ascending', desc: 'sorted descending', compare: function (a, b) { if (a > b) { return 1; } else if (a < b) { return -1; } else { return 0; } } }; $.fn.tablesort = function (settings) { var table, sortable, previous; return this.each(function () { table = $(this); previous = table.data('tablesort'); if (previous) { previous.destroy(); } table.data('tablesort', new $.tablesort(table, settings)); }); }; })(window.Zepto || window.jQuery); } // reference to jquery row object var lastSelectedRow = null; function selectTableRow(r) { var row = $(r); if (row.css("display") !== "none") { row.addClass('selectedRow'); lastSelectedRow = row.last(); } } function deselectTableRow(r) { var row = r ? $(r) : $('.selectedRow'); row.removeClass('selectedRow'); lastSelectedRow = row.last(); } function setTableRowSelectionEvents() { $(".sorted-table tbody tr").click(function (event) { if (!event.ctrlKey && !event.shiftKey) { deselectTableRow($(this).siblings()); } if (!event.shiftKey || (event.shiftKey && !lastSelectedRow.length)) { if ($(this).hasClass('selectedRow')) { deselectTableRow(this); } else { selectTableRow(this); } } else { // order the two selected indexes var [low, high] = [lastSelectedRow.index() + 1, this.rowIndex].sort((a, b) => a - b); var rows = $(".sorted-table tr").filter(idx => idx <= high && idx >= low); selectTableRow(rows); window.getSelection().removeAllRanges(); // remove text selection that happens when a user shift clicks } }); } ["keyup", "keydown"].forEach((event) => { window.addEventListener(event, (e) => { document.onselectstart = function () { return !(e.key == "Shift" && e.shiftKey); } }); }); function col_id_from_header_name(name) { // we cache return values because theres a lot of repetitive text processing happening if (col_id_from_header_name.cache == undefined) { col_id_from_header_name.cache = {}; col_id_from_header_name.tableHeaders = $('.overflow_div .sorted-table thead tr th').toArray(); } if (col_id_from_header_name.cache[name]) { return col_id_from_header_name.cache[name]; } var rowId = -1; col_id_from_header_name.tableHeaders.every(h => { var header = $(h); // split on html tags that arent line breaks (
) so it doesnt match the items in drop-down menus in the headers if (equalsContainsMatches(header.html().split(/<[^<>]{3,}>/i)[0].trim().replace(/\n|
/ig, ''), name)) { rowId = header.index(); return false; // break out of loop } return true; // continue loop }); col_id_from_header_name.cache[name] = rowId; return rowId; } function getHeaderTextFromCell(cell) { var index = $(cell).index(); // we cache return values because theres a lot of repetitive text processing happening if (getHeaderTextFromCell.cache == undefined) { getHeaderTextFromCell.cache = {}; getHeaderTextFromCell.tableHeaders = $('.overflow_div .sorted-table thead tr th').toArray(); } if (getHeaderTextFromCell.cache[index]) { return getHeaderTextFromCell.cache[index]; } // need to trim because the ::after css attribute that adds the sorting arrow adds a space which messes with our dictionary keys var ret = $(getHeaderTextFromCell.tableHeaders[index].firstChild).text().trim(); getHeaderTextFromCell.cache[index] = ret; return ret; } // this needs to happen before any data is loaded function setRowKeys() { const rowKeyHeaderIndexes = []; const tableHeaders = $('.sorted-table thead tr th').toArray(); for (var i = 0; i < tableHeaders.length; i++) { if (!$(tableHeaders[i]).text().match(/accept(.+)reject|a\/r|comment/i)) { rowKeyHeaderIndexes.push(i); } } // assign row keys so they dont change when we edit cells $('.sorted-table tbody tr').toArray().forEach(row => { var tds = $(row).find('td'); var keyVals = []; tds.toArray().forEach(row => { var i = row.cellIndex; if (rowKeyHeaderIndexes.includes(i)) { keyVals.push($(tds[i]).text()); } }); var key = keyVals.sort().join('').replace(/\n|\s/ig, '').toUpperCase(); row['ROW_KEY'] = key; }); } function getRowKey(row) { // should be a reference to the tr return $(row)[0]['ROW_KEY']; } // TODO make this more versatile and usable on the table that contains the 'cell' variable instead of a hard coded table reference function setFocusCellBelow(cell) { var selectedTableRow = $(cell).parent(); var headerName = getHeaderTextFromCell(cell); // get all rows so we can use the index of the last table row var allRows = $(".overflow_div .sorted-table tbody tr"); for (var i = selectedTableRow.last().index() + 1; i < allRows.length; i++) { if ($(allRows[i]).css('display') !== 'none') { if (i < allRows.length) { document.activeElement.blur(); deselectTableRow(); selectTableRow(allRows[i]); var cellBelow = allRows[i].cells[col_id_from_header_name(headerName)]; $(cellBelow).focus(); moveCaretToEnd(cellBelow); } return; } } } // TODO make this more versatile and usable on the table that contains the 'cell' variable instead of a hard coded table reference function setFocusCellAbove(cell) { var selectedTableRow = $(cell).parent(); var headerName = getHeaderTextFromCell(cell); // get all rows so we can use the index of the last table row var allRows = $(".overflow_div .sorted-table tbody tr"); for (var i = selectedTableRow.last().index() - 1; i < allRows.length; i--) { if ($(allRows[i]).css('display') !== 'none') { if (i < allRows.length) { document.activeElement.blur(); deselectTableRow(); selectTableRow(allRows[i]); var cellAbove = allRows[i].cells[col_id_from_header_name(headerName)]; $(cellAbove).focus(); moveCaretToEnd(cellAbove); } return; } } } function setEditableCellListeners() { $(".sorted-table tbody tr td").toArray().forEach(cellEl => { if (cellEl.contentEditable != 'true') return; var cell = $(cellEl); // when overrides are loaded we want to first revert the original text so we // store it in the cells cell[0]['ORIGINAL_TEXT'] = cell.text().trim(); // we are trimming the text to remove the trailing whitespace which makes it more annoying to edit fields because // they look like they end at the word but theyre annoyingly padded sometimes cell.text(cell.text().trim()); cell.on('keyup', (event) => { // this returns a zero if it didnt write to any cells -- basically means there are no selected rows if (!writeToSelectedCells(event)) { userCellEditCallback(cell); restyle_from_derating(); } }); }); } function userCellEditCallback(cell) { var jCell = $(cell); var rowKey = getRowKey(jCell.parent()); var cellMap = getRowCellMap(rowKey, cell); cellMap['text'] = jCell.text(); if (jCell.text() == jCell[0]['ORIGINAL_TEXT']) { cellMap['css-class'] = ''; jCell.removeClass('edited'); } else { cellMap['css-class'] = 'edited'; jCell.addClass('edited'); } cacheWebPage(); } function saveAdditionalRowStorage() { var reportUid = sessionKey.split('/')[0]; var toSave = {}; for (var key in localStorage) { if (key.startsWith(reportUid)) { var reportName = key.split('/')[1]; toSave[reportName] = localStorage.getItem(key); } } var a = document.createElement("a"); var blob = new Blob(["var additionalRowStorage = " + JSON.stringify(toSave, null, 2)], { type: 'application/json' }); a.href = window.URL.createObjectURL(blob); a.download = jsonSaveFileName; a.click(); } /* this needs to be called at the very end of the document load so that the row keys can be established first */ function loadAdditionalRowStorage(manualMatchScoreMap) { if (manualMatchScoreMap != undefined) { // reset original values before we overwrite them again $('.overflow_div .sorted-table tbody tr td') .toArray() .forEach(cell => { var cellRef = $(cell); cellRef.removeClass('override-bad'); cellRef.removeClass('override-good'); cellRef.removeClass('accept'); cellRef.removeClass('reject'); if (cellRef[0]['ORIGINAL_TEXT']) { cellRef.text(cellRef[0]['ORIGINAL_TEXT']); } }); $('.highlight-on-click.highlighted').removeClass('highlighted'); var reportUid = sessionKey.split('/')[0]; for (var reportName in manualMatchScoreMap) { localStorage.setItem(reportUid + '/' + reportName, manualMatchScoreMap[reportName]); } loadFromCache(); } try { if (additionalRowStorage['ELEMENT_IDS']) { for (var key in additionalRowStorage['ELEMENT_IDS']) { var info = additionalRowStorage['ELEMENT_IDS'][key]; if (info['text']) { $(key).text(info['text']); } if (info['css-class']) { $(key).addClass(info['css-class']); } } } $('.overflow_div .sorted-table tbody tr').toArray().forEach(row => { var rowKey = getRowKey(row); if (additionalRowStorage[rowKey]) { var cells = $(row).children(); for (var header in additionalRowStorage[rowKey]) { var columnId = col_id_from_header_name(header); if (columnId < 0) { console.warn('Unable to find column for table header: ' + header); continue; } if (additionalRowStorage[rowKey][header]['text']) { $(cells[columnId]).text(additionalRowStorage[rowKey][header]['text']); } if (additionalRowStorage[rowKey][header]['css-class']) { $(cells[columnId]).addClass(additionalRowStorage[rowKey][header]['css-class']); } if (additionalRowStorage[rowKey][header]['highlighted']) { $(cells[columnId]).find('.highlight-on-click') .toArray() .forEach(n => { var node = $(n); var color = additionalRowStorage[rowKey][header]['highlighted'][node.text()]; if (color) { node.addClass('highlighted').addClass(color); } }); } } } }); if (getActiveRailName() != null) { restyle_from_derating(true); recalculateSummaryCapacitance(); // has to be after the cap_derating_change call } } catch (e) { console.log('Failed to load match score override values.'); console.log(e); } if (manualMatchScoreMap && window.parent) { window.parent.postMessage({ 'reload_from_cache': manualMatchScoreMap, 'location': window.location.href, }, '*'); } } function overloadAdditionalRowStorage() { // from: https://stackoverflow.com/questions/6711002/how-can-i-get-javascript-to-read-from-a-json-file var input = document.createElement('input'); input.type = 'file'; input.onchange = e => { // getting a hold of the file reference var file = e.target.files[0]; // setting up the reader var reader = new FileReader(); reader.readAsText(file, 'UTF-8'); // here we tell the reader what to do when it's done reading... reader.onload = readerEvent => { var content = readerEvent.target.result; // this is the content! // we add this line so its javascript when we save to file -- we need to remove to parse as json content = content.replace(/^var additionalRowStorage =/i, ''); var jsonobj = JSON.parse(content); loadAdditionalRowStorage(jsonobj); } } input.click(); } function getRowCellMap(rowKey, cell) { var cellKey = getHeaderTextFromCell(cell); var rowMap, cellMap; if (!additionalRowStorage[rowKey]) { rowMap = {}; additionalRowStorage[rowKey] = rowMap; } else { rowMap = additionalRowStorage[rowKey]; } if (!rowMap[cellKey]) { cellMap = {}; rowMap[cellKey] = cellMap; } else { cellMap = rowMap[cellKey]; } return cellMap; } function setTableCellHeightRestrictionEvents() { var cellsToEdit = []; $('.table-cell-height-restriction').toArray().forEach(el => { // this means its cut off by height restriction if (el.scrollHeight > el.clientHeight) { cellsToEdit.push(el); } }); cellsToEdit.forEach(element => { var floatingContainer = $('
'); floatingContainer.addClass('floating-table-buttons'); var jumpToTopButton = $(''); jumpToTopButton.text(" \u2191 ") // the first css gives it its style, the second one is used to differentiate it from the other buttons // but since we pass a reference to the click event callback we dont even need to differentiate it .addClass('expand-icon-btn-styling') .addClass('jump-icon-btn') .click(e => { e.stopPropagation(); // using a jquery accessor here instead of walking up the dom because the structure often changes var scrollWindow = $('.overflow_div .sorted-table').get(0); // .get() returns the dom element instead of a jquery object like .first() element.scrollIntoView({ behavior: "auto", block: "start", inline: "nearest" }); scrollWindow.scrollTo(0, scrollWindow.scrollTop - 60); // move 60 pixels above to offset header and give some margin onResizeWindow(); }); var expandCollapsedRowButton = $(''); expandCollapsedRowButton.text(" \u21F2 ") .addClass('expand-icon-btn-styling') .addClass('expand-icon-btn') .click(e => { e.stopPropagation(); // closest will traverse up and grab the first matching thing var row = $(e.target).closest('tr').first(); // we want to grab all the elements in all the cells of this row var allCellsInRow = row.find('.table-cell-height-restriction'); var allContainersInRow = row.find('.floating-table-buttons'); var importantRowNames = row.find('.important-sticky-left'); var importantRowNameTds = []; $.each(importantRowNames, function (i, importantRowInnerCell) { importantRowNameTds.push(importantRowInnerCell.closest('td')); }); var allExpandButtonsInRow = row.find('.expand-icon-btn'); var allJumpButtonsInRow = row.find('.jump-icon-btn'); var closestTable = $(e.target).closest('table'); var firstHeader = null; var firstOverflowDiv = null; if (closestTable) { firstHeader = closestTable.find('th').first(); firstOverflowDiv = closestTable.closest('.overflow_div'); } allCellsInRow.toggleClass('expanded'); allContainersInRow.toggleClass('expanded'); if (allCellsInRow.hasClass('expanded')) { allExpandButtonsInRow.text(" \u21F1 "); allJumpButtonsInRow.css("visibility", "visible"); allContainersInRow.css("top", firstHeader.outerHeight() + 1); // height of header plus 1 pixel for distancing importantRowNames.css("top", firstOverflowDiv.outerHeight() / 2); // height of table, divided by 2 $.each(importantRowNameTds, function (i, td) { $(td).attr('valign',"TOP"); }); } else { allExpandButtonsInRow.text(" \u21F2 "); allJumpButtonsInRow.css("visibility", "hidden"); element.scrollIntoView({ behavior: "auto", block: "center", inline: "nearest" }); allContainersInRow.css("top", 0); importantRowNames.css("top", 0); $.each(importantRowNameTds, function (i, td) { $(td).attr('valign',null); }); } onResizeWindow(); }); floatingContainer.append(expandCollapsedRowButton); floatingContainer.append(jumpToTopButton); // add our button container to the top of the cell container $(element).prepend(floatingContainer); $(element).closest('td').addClass('with-expand-icon'); }); } function saveFilteredTableSearchInputs() { /* var filter = $('#table-filter-text-input-field'); var rangeLow = $('#table-filter-range-input-low'); var rangeHigh = $('#table-filter-range-input-high'); var matchCriteria = $('#table-filter-text-input-logic'); sessionStorage.setItem('#table-filter-text-input-field', filter.val()); sessionStorage.setItem('#table-filter-range-input-low', rangeLow.val()); sessionStorage.setItem('#table-filter-range-input-high', rangeHigh.val()); sessionStorage.setItem('#table-filter-text-input-logic', matchCriteria.val()); */ var filterSelection = $('#table-filter-column-selection'); var filterColumnText = filterSelection[0].selectedOptions[0].textContent; sessionStorage.setItem('#table-filter-column-selection', filterColumnText); } function loadFilteredTableSearchInputs() { /* var filter = sessionStorage.getItem('#table-filter-text-input-field'); var rangeLow = sessionStorage.getItem('#table-filter-range-input-low'); var rangeHigh = sessionStorage.getItem('#table-filter-range-input-high'); var matchCriteria = sessionStorage.getItem('#table-filter-text-input-logic'); if (filter) { $('#table-filter-text-input-field').val(filter); } if (rangeLow) { $('#table-filter-range-input-low').val(rangeLow); } if (rangeHigh) { $('#table-filter-range-input-high').val(rangeHigh); } if (matchCriteria) { $('#table-filter-text-input-logic').val(matchCriteria); } */ var filterSelection = sessionStorage.getItem('#table-filter-column-selection'); if (filterSelection && $('#table-filter-column-selection').length > 0) { for (var option of $('#table-filter-column-selection')[0]) { if (filterSelection == option.textContent) { $('#table-filter-column-selection').val($(option).val()); break; } } } } // https://html-online.com/articles/get-url-parameters-javascript/ function getUrlVars() { var vars = {}; var parts = window.location.href.replace(/[?&]+([^=&]+)=([^&]*)/gi, function (m, key, value) { vars[key] = value; }); return vars; } // https://html-online.com/articles/get-url-parameters-javascript/ function getUrlParam(parameter, defaultvalue) { var urlparameter = defaultvalue; if (window.location.href.indexOf(parameter) > -1) { urlparameter = getUrlVars()[parameter]; } return urlparameter; } function setPrefilledFilter() { var vars = getUrlVars(); if (vars.net_trace_net && vars.net_trace_table) { const netTraceNet = decodeURIComponent(vars.net_trace_net); const netTraceTable = decodeURIComponent(vars.net_trace_table); this.reportName = decodeURIComponent(vars.report_name); if (netTraceTable.match(/new/i)) { document.title = 'New Net Trace: ' + netTraceNet; } else if (netTraceTable.match(/old/i)) { document.title = 'Old Net Trace: ' + netTraceNet; } else { document.title = 'Net Trace: ' + netTraceNet; } setBodyKonvaNetTrace(netTraceTable, netTraceNet); return; } if (vars['table_only']) { $('.accordion_header').css('display', 'none'); $('.nl_header_text').css('display', 'none'); $('.nl_header_button_column').css('display', 'none'); $('.sheet-change-dropdown').css('display', 'none'); $('.nl_header_logo').css('display', 'none'); $('.nl_header_wrapper').css( { 'box-shadow': 'none', // remove box shadow 'border-top': 'none', // remove border 'padding': 0, // remove padding 'height': 0, // remove forced height }); var html = ''; $('.nl_header_right_button_row').first().prepend(html); $('.nl_header_right_button_row').last().css('display', 'none'); $('.nl_header_right_button_container').css('margin-top', '10px'); // set external window button event $('#button_open_in_ex_win').click((e) => { window.open(window.location.pathname) }); $('.octopart-button').detach().prependTo($('#button_open_in_ex_win').parent()); } if (vars['custom_title']) { var title = vars['custom_title'].replace(/__/g, ' '); $('#download-excel-btn').after('

' + title + '

'); } if (vars['prefilter']) { var filterInfo = vars['prefilter'].split(':'); var column = filterInfo[0]; var filterVal = decodeURIComponent(filterInfo[1]); filterVal = escapeRegex(filterVal); if (vars['prefiltertype'] === 'equals') { filterVal = '^' + filterVal + '$'; } column = column.replace(/_/g, ' ').toUpperCase(); var desiredColumnName = ''; for (var option of $('#table-filter-column-selection')[0]) { if (option.textContent.toUpperCase() == column) { desiredColumnName = $(option).val(); break; } } if (desiredColumnName) { $('#table-filter-column-selection').val(desiredColumnName); } $('#table-filter-text-input-field').val(filterVal); } else { loadFilteredTableSearchInputs(); } } // handler for clicking anywhere in the page $(this).click(function (event) { // disable all context menus $(".context-menu").css('display', 'none'); }); function ctxmenu_init_subgroup_empty(id) { var divId = "#" + id; var sgId = "#" + id + '-sg'; var spl = divId.split('-'); spl.pop(); var divSectionToClose = spl.join('-'); $(sgId).hover( function (e) { // handler in // we only care about the mouseenter event if (e.handleObj.origType === 'mouseleave') return; // close all other sub groups $(divSectionToClose + " .context-menu").css('display', 'none'); } ); } function ctxmenu_init_subgroup_items(id) { var divId = "#" + id; var sgId = "#" + id + '-sg'; var spl = divId.split('-'); spl.pop(); var divSectionToClose = spl.join('-'); $(sgId).hover( function (e) { // handler in // we only care about the mouseenter event if (e.handleObj.origType === 'mouseleave') return; // close all other sub groups $(divSectionToClose + " .context-menu").css('display', 'none'); var parentWidth = $(divId).width(); var top = e.target.offsetTop ? e.target.offsetTop : top; var left = parentWidth; $(divId).css('display', 'none'); $(divId) .toggle() .css( { display: 'block', top: top + "px", left: left + "px" } ); } ); } function makeTclReadableMarker(allMarkerData) { var markerText = ''; for (var rowInfo of allMarkerData) { var topLevel = ''; for (var rowKey in rowInfo) { if (rowKey === 'markerData') continue; topLevel += rowKey + ':' + rowInfo[rowKey] + '\n'; } for (var markerEntry of rowInfo.markerData) { for (var markerMap of markerEntry) { markerText += markerMap.use_case.toUpperCase() + '='; if (markerMap[markerMap.use_case] != undefined) { markerText += markerMap[markerMap.use_case]; } else { markerText += 'UNKNOWN'; } markerText += '\n'; markerText += topLevel; for (var key in markerMap) { markerText += key.toUpperCase() + ':' + markerMap[key] + '\n'; } markerText += '\n'; } } } return markerText; } function generateMarkerText(allMarkerData) { var markerFile = []; markerFile.push("(marker_file"); markerFile.push(" (version 1.0)"); markerFile.push(" (markers"); var markerCount = 1; for (var rowInfo of allMarkerData) { var reportColumn = rowInfo.reportColumn; reportColumn = reportColumn[0].toUpperCase() + reportColumn.substring(1); for (var markerData of rowInfo.markerData) { for (var marker of markerData) { markerFile.push(" ("); var longMsg = 'Report= ' + reportName; longMsg += '\n Block= ' + marker.block; longMsg += '\n Report= ' + reportColumn; longMsg += '\n Instance= ' + marker.page_instance; longMsg += '\n PhysicalPath= ' + marker.phys_path; longMsg += '\n LogicalPath= ' + marker.path; markerFile.push(' (tool "CheckPlus")'); markerFile.push(' (class "LOGICAL")'); markerFile.push(" (severity 30)"); markerFile.push(" (error_num " + markerCount++ + ")"); if (marker.use_case === 'refdes') { var shortMsg = reportName + ' ' + marker.refdes + ' -> ' + marker.description; markerFile.push(' (short_msg "' + shortMsg + '")'); markerFile.push(' (long_msg "' + longMsg + '")'); markerFile.push(" (location"); markerFile.push(" ("); markerFile.push(' (object_kind "instance")'); markerFile.push(' (canonical_name "_!drawerror inst ' + marker.page_instance + ';")'); markerFile.push(' (parent_canonical_name "DRAWERROR inst ' + marker.page_instance + ';")'); } else if (marker.use_case === 'node') { var shortMsg = reportName + ' ' + marker.node_name + ' -> ' + marker.net_name; markerFile.push(' (short_msg "' + shortMsg + '")'); markerFile.push(' (long_msg "' + longMsg + '")'); markerFile.push(" (location"); markerFile.push(" ("); markerFile.push(' (object_kind "instance_port")'); markerFile.push(' (canonical_name "_!drawerror pin ' + marker.page_instance + "." + marker.pin_name + ';")'); markerFile.push(' (parent_canonical_name "DRAWERROR pin ' + marker.page_instance + ';")'); } markerFile.push(' (drawing_name "' + marker.path + '")'); markerFile.push(" )"); markerFile.push(" )"); markerFile.push(" )"); } } } markerFile.push(" )"); markerFile.push(")"); return markerFile.join('\n'); } function makeAllegroPcbList(rows, columnName) { if (rows.length <= 0) return; const cellId = col_id_from_header_name(columnName); var markers = []; rows.toArray().forEach(rowEl => { const row = $(rowEl); const cell = row.find('td').eq(cellId); const nodeList = []; const refdesList = []; cell.find('.marker-data-node') .toArray() .forEach(cellEl => { const cellRef = $(cellEl); nodeList.push(cellRef.text().replace(/\n/g, '').trim()); }); cell.find('.marker-data-refdes') .toArray() .forEach(cellEl => { const cellRef = $(cellEl); refdesList.push(cellRef.text().replace(/\n/g, '').trim()); }); markers = markers.concat(nodeList, refdesList); }); return markers.join("\n"); } function makeMarkerFiles(rows, lookupTable, columnName) { if (rows.length <= 0) return; const cellId = col_id_from_header_name(columnName); var markers = []; rows.toArray().forEach(rowEl => { const row = $(rowEl); const cell = row.find('td').eq(cellId); const cellMarkerInfo = []; cell.find('.marker-data-node') .toArray() .forEach(cellEl => { const cellRef = $(cellEl); const nodeName = cellRef.text().replace(/\n/g, '').trim(); const nodeInfo = lookupTable.node_info[nodeName]; if (!nodeInfo || !nodeInfo.marker_data) return; const markerData = []; for (const markerRef of nodeInfo.marker_data) { // create a deep copy so we arent editing the original const markerCopy = JSON.parse(JSON.stringify(markerRef)); markerCopy.use_case = 'node'; markerCopy.part_number = nodeInfo.part_number; markerCopy.description = lookupTable.part_info[nodeInfo.part_number].description; markerCopy.node_name = nodeInfo.node_name; markerCopy.node = nodeInfo.node_name; markerCopy.net_name = nodeInfo.net_name; markerCopy.pin_name = nodeInfo.pin_name; markerData.push(markerCopy); } cellMarkerInfo.push(markerData); }); cell.find('.marker-data-refdes') .toArray() .forEach(cellEl => { const cellRef = $(cellEl); const refdes = cellRef.text().replace(/\n/g, '').trim(); const refdesInfo = lookupTable.refdes_info[refdes]; if (!refdesInfo || !refdesInfo.marker_data) return; const markerData = []; for (const markerRef of refdesInfo.marker_data) { // create a deep copy so we arent editing the original const markerCopy = JSON.parse(JSON.stringify(markerRef)); markerCopy.use_case = 'refdes'; markerCopy.part_number = refdesInfo.part_number; markerCopy.description = lookupTable.part_info[refdesInfo.part_number].description; markerCopy.refdes = refdesInfo.refdes; markerData.push(markerCopy); } cellMarkerInfo.push(markerData); }); markers.push({ 'reportColumn': columnName, 'reportName': reportName, 'markerData': cellMarkerInfo }); }); return markers; } function setBodyKonvaNetTrace(tableVariableName, netName) { $('body').html(''); var container = $('
'); container.css('padding', '10px'); var crossProbeContainer = $(''); crossProbeContainer.addClass('noprint'); crossProbeContainer.text('Ctrl+Scroll to zoom in/out | Ctrl+Click and drag to pan | '); var netTraceSearchBar = $(''); netTraceSearchBar.on('keyup', (e) => { const searchText = $(e.target).val(); var searchGroup = stage.findOne('.search-group'); var parentLayer = searchGroup.getParent(); // delete all the old search nodes searchGroup.destroy(); // create a new search group searchGroup = new Konva.Group({ name: 'search-group' }); // add the new search group to the search drawing layer parentLayer.add(searchGroup); if (!searchText) return; for (const textObject of stage.findOne('.drawing-group').find('Text')) { // TODO cache this query for optimization const textToSearch = textObject.text(); const match = equalsContainsMatches(textToSearch, searchText); if (match) { const searchLabel = new Konva.Label({ name: 'search-label', opacity: 1, x: textObject.getAbsolutePosition(stage).x - 1, y: textObject.getAbsolutePosition(stage).y - 1, }); searchLabel.add(new Konva.Tag({ fill: 'yellow', cornerRadius: 1, })); searchLabel.add(new Konva.Text({ text: textToSearch, fontSize: textObject.getAttr('fontSize'), fontStyle: textObject.getAttr('fontStyle'), padding: 1, })); searchGroup.add(searchLabel); } } }); crossProbeContainer.append(netTraceSearchBar); var ahdlButton = $(''); var captureButton = $(''); var pcbButton = $(''); ahdlButton.click((e) => { var markers = netTraceMarker(dataTable, netName); var text = generateMarkerText(markers); downloadTextToFile(text, reportName + '_AHDL_NetTrace.mkr'); }); captureButton.click((e) => { var markers = netTraceMarker(dataTable, netName); var text = makeTclReadableMarker(markers); downloadTextToFile(text, reportName + '_Capture_NetTrace.mkr'); }); pcbButton.click((e) => { var text = currentTraceNodes.join('\n'); downloadTextToFile(text, reportName + '_PCB_NetTrace.txt'); }); crossProbeContainer.append(ahdlButton); crossProbeContainer.append(captureButton); crossProbeContainer.append(pcbButton); container.append(crossProbeContainer); var rightButtonContainer = $(''); rightButtonContainer.addClass('noprint'); rightButtonContainer.css('float', 'right'); var refreshButton = $('