/* 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();
}
if (typeof filterTableBy != 'undefined') {
filterTableBy(null, 'default');
}
$('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);
}
setOctopartAvailabilityColors();
}
function open_excel_link(path) {
var url = (navigator.platform.match(/win/i) ? 'ms-excel:ofe|u|' : '') + getCurrentDirectory() + '/' + path;
window.open(url);
}
// 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');
}
if (event) {
$(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 + "$"); // need to make the regex match exactly
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) {
const rangeLow = $('#table-filter-range-input-low');
const rangeHigh = $('#table-filter-range-input-high');
const showAll = $('#table-sub-filter-all').hasClass('selected');
const showEdited = $('#table-sub-filter-edited').hasClass('selected');
const showVoltMissmatch = $('#table-sub-filter-volt-mismatch').hasClass('selected');
const showAddedXnet = $('#table-sub-filter-added-xnets').hasClass('selected');
const showModifiedXnet = $('#table-sub-filter-modified-xnets').hasClass('selected');
const showRemovedXnet = $('#table-sub-filter-removed-xnets').hasClass('selected');
const xnetStateColumn = col_id_from_header_name('state');
const checkXNetState = xnetStateColumn >= 0 && (showAddedXnet || showModifiedXnet || showRemovedXnet);
const pinVoltColumn = col_id_from_header_name('pin / bank volt');
const netVoltColumn = col_id_from_header_name('net volt');
const octopartGreen = $('#table-sub-filter-octo-green').hasClass('selected');
const octopartLime = $('#table-sub-filter-octo-lime').hasClass('selected');
const octopartOrange = $('#table-sub-filter-octo-orange').hasClass('selected');
const octopartRed = $('#table-sub-filter-octo-red').hasClass('selected');
const octoPartColumn = col_id_from_header_name('part number');
const checkOctopart = octoPartColumn >= 0 && (octopartGreen || octopartLime || octopartOrange || octopartRed);
for (const rowEl of $('.overflow_div .sorted-table tbody tr').toArray()) {
const row = $(rowEl);
const children = row.children();
if (!showAll) {
row.css('display', '');
if (showEdited) {
var noEdits = true;
for (const cell of children.toArray()) {
if ($(cell).hasClass('edited')) {
noEdits = false;
break;
}
}
if (noEdits) {
row.css('display', 'none');
continue;
}
}
if (showVoltMissmatch && pinVoltColumn >= 0 && netVoltColumn >= 0) {
if (!children.eq(pinVoltColumn).hasClass('red-cell')) {
row.css('display', 'none');
continue;
}
}
if (checkOctopart) {
const cell = children.eq(octoPartColumn);
var hide = false;
if (octopartGreen && cell.hasClass('green-cell')) {
hide = true;
}
else if (octopartLime && cell.hasClass('yellow-cell')) {
hide = true;
}
else if (octopartOrange && cell.hasClass('orange-cell')) {
hide = true;
}
else if (octopartRed && cell.hasClass('red-cell')) {
hide = true;
}
if (!hide) {
row.css('display', 'none');
continue;
}
}
if (checkXNetState) {
const stateValue = children.eq(xnetStateColumn).text();
var hide = false;
if (showAddedXnet && stateValue.match(/added/i)) {
hide = true;
}
else if (showModifiedXnet && stateValue.match(/modified/i)) {
hide = true;
}
else if (showRemovedXnet && stateValue.match(/removed/i)) {
hide = true;
}
if (!hide) {
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 showAll = $('#table-sub-filter-all').hasClass('selected');
const showEdited = $('#table-sub-filter-edited').hasClass('selected');
const showVoltMissmatch = $('#table-sub-filter-volt-mismatch').hasClass('selected');
const showAddedXnet = $('#table-sub-filter-added-xnets').hasClass('selected');
const showModifiedXnet = $('#table-sub-filter-modified-xnets').hasClass('selected');
const showRemovedXnet = $('#table-sub-filter-removed-xnets').hasClass('selected');
const xnetStateColumn = col_id_from_header_name('state');
const checkXNetState = xnetStateColumn >= 0 && (showAddedXnet || showModifiedXnet || showRemovedXnet);
const pinVoltColumn = col_id_from_header_name('pin / bank volt');
const netVoltColumn = col_id_from_header_name('net volt');
const octopartGreen = $('#table-sub-filter-octo-green').hasClass('selected');
const octopartLime = $('#table-sub-filter-octo-lime').hasClass('selected');
const octopartOrange = $('#table-sub-filter-octo-orange').hasClass('selected');
const octopartRed = $('#table-sub-filter-octo-red').hasClass('selected');
const octoPartColumn = col_id_from_header_name('part number');
const checkOctopart = octoPartColumn >= 0 && (octopartGreen || octopartLime || octopartOrange || octopartRed);
for (const rowEl of $('.overflow_div .sorted-table tbody tr').toArray()) {
const row = $(rowEl);
const children = row.children();
if (!showAll) {
row.css('display', '');
if (showEdited) {
var noEdits = true;
for (const cell of children.toArray()) {
if ($(cell).hasClass('edited')) {
noEdits = false;
break;
}
}
if (noEdits) {
row.css('display', 'none');
continue;
}
}
if (showVoltMissmatch && pinVoltColumn >= 0 && netVoltColumn >= 0) {
if (!children.eq(pinVoltColumn).hasClass('red-cell')) {
row.css('display', 'none');
continue;
}
}
if (checkOctopart) {
const cell = children.eq(octoPartColumn);
var hide = false;
if (octopartGreen && cell.hasClass('green-cell')) {
hide = true;
}
else if (octopartLime && cell.hasClass('yellow-cell')) {
hide = true;
}
else if (octopartOrange && cell.hasClass('orange-cell')) {
hide = true;
}
else if (octopartRed && cell.hasClass('red-cell')) {
hide = true;
}
if (!hide) {
row.css('display', 'none');
continue;
}
}
if (checkXNetState) {
const stateValue = children.eq(xnetStateColumn).text();
var hide = false;
if (showAddedXnet && stateValue.match(/added/i)) {
hide = true;
}
else if (showModifiedXnet && stateValue.match(/modified/i)) {
hide = true;
}
else if (showRemovedXnet && stateValue.match(/removed/i)) {
hide = true;
}
if (!hide) {
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);
edit_pin_net_volt_cell(row);
});
} 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);
edit_pin_net_volt_cell(row);
});
}
else {
selectedTableRow.toArray().forEach(row => {
var commentCell = $(row).children()[commentCellIdx];
var txt = $(commentCell).text();
$(commentCell).text(txt.substring(0, txt.length - 1));
userCellEditCallback(commentCell);
edit_pin_net_volt_cell(row);
});
}
}
}
restyle_from_derating();
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 = 'desc';
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();
edit_pin_net_volt_cell(cell.parent());
}
});
});
}
function setOctopartAvailabilityColors() {
// if the octopart button is disabled we dont want to
if ($('.octopart-button').hasClass('disabled')) return;
const pn_cell_idx = col_id_from_header_name('part number');
const qty_cell_idx = col_id_from_header_name('qty');
if (pn_cell_idx < 0 || qty_cell_idx < 0) return;
const all_stock_cell_idx = [];
$('.sorted-table thead tr th').toArray().forEach(cellEl => {
if (cellEl.innerText.match(/stock/i)) {
all_stock_cell_idx.push($(cellEl).index());
}
});
if (all_stock_cell_idx.length <= 0) return;
const build_quantity = $('#build-quantity').val();
//console.log('Build quantity: ' + build_quantity);
$(".sorted-table tbody tr").toArray().forEach(rowEl => {
const rowKids = $(rowEl).children();
const pn_cell = rowKids.eq(pn_cell_idx);
const qty_cell = rowKids.eq(qty_cell_idx);
const required_quantity = Number(qty_cell.text().trim()) * build_quantity;
var hasOctopartInfo = false;
var availableVendors = 0;
for (const stock_cell_idx of all_stock_cell_idx) {
const count = rowKids.eq(stock_cell_idx).text().trim();
if (count != '-') {
hasOctopartInfo = true;
}
if (Number(count) >= required_quantity) {
availableVendors += 1;
}
}
pn_cell.removeClass();
if (!hasOctopartInfo) {
pn_cell.addClass('gray-cell');
}
else if (availableVendors >= all_stock_cell_idx.length) {
pn_cell.addClass('green-cell');
}
else if (availableVendors >= 2) {
pn_cell.addClass('yellow-cell');
}
else if (availableVendors >= 1) {
pn_cell.addClass('orange-cell');
}
else {
pn_cell.addClass('red-cell');
}
});
}
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);
}
});
}
}
}
edit_pin_net_volt_cell(row);
});
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 = $('