The Conduit and Wire Weight Calculator assists in planning electrical conduit runs by calculating total weights, determining fill percentages, checking ampacity and grounding requirements, and exporting data to PDF.
index.html: The main HTML file that includes the structure of the calculator, forms, and buttons.
Handles adding conduits and wires to the entry list.
Function: Adds a conduit entry to the list.
const isFreeAir = document.getElementById('freeAir').checked;
freeAir
)const conduitType = document.getElementById('conduitType1').value; const conduitSize = document.getElementById('conduitSize1').value; const conduitLength = document.getElementById('conduitLength1').value;
conduitType1
, conduitSize1
, and conduitLength1
)const listItem = document.createElement('li'); if (isFreeAir) { listItem.innerHTML = `Conduit: Type - Free Air `; } else { listItem.innerHTML = `Conduit: Type - ${conduitType}, Size - ${conduitSize}, Length - ${conduitLength} ft `; }
document.getElementById('entryList').appendChild(listItem);
entryList
)document.getElementById('conduitType1').selectedIndex = 0; document.getElementById('conduitSize1').selectedIndex = 0; document.getElementById('conduitLength1').value = '10'; document.getElementById('freeAir').checked = false; toggleFreeAir();
conduitType1
, conduitSize1
, conduitLength1
, and freeAir
)updateTotalWeightAndWireFill();
updateEmptyEntries('entryList'); updateEmptyEntries('setsList');
Function: Adds a wire entry to the list.
const wireType = document.getElementById('wireType1').value; const wireSize = document.getElementById('wireSize1').value; const wireLength = document.getElementById('wireLength1').value; const wireCount = document.getElementById('wireCount1').value;
wireType1
, wireSize1
, wireLength1
, and wireCount1
)const listItem = document.createElement('li'); listItem.innerHTML = `Wire: Type - ${wireType}, Size - ${wireSize}, Length - ${wireLength} ft, Count - ${wireCount} `;
document.getElementById('entryList').appendChild(listItem);
entryList
)document.getElementById('wireType1').selectedIndex = 0; document.getElementById('wireSize1').selectedIndex = 0; document.getElementById('wireLength1').value = '10'; document.getElementById('wireCount1').value = '1';
wireType1
, wireSize1
, wireLength1
, and wireCount1
)updateTotalWeightAndWireFill();
updateEmptyEntries('entryList'); updateEmptyEntries('setsList');
Similar to addWire()
, but specifically for non-CCC wires.
Similar to addWire()
, but specifically for EGC wires.
Similar to addWire()
, but specifically for GEC wires.
Similar to addWire()
, but specifically for SBJ wires.
Function: Removes a specific item from the entry list.
const listItem = button.parentElement;
listItem.remove();
updateTotalWeightAndWireFill();
updateEmptyEntries('entryList'); updateEmptyEntries('setsList');
Function: Resets the current list of entries.
document.getElementById('entryList').innerHTML = '';
entryList
)document.getElementById('totalWeight').innerText = '0 lbs'; document.getElementById('wireFill').innerText = '0%';
totalWeight
and wireFill
)document.getElementById('conduitType1').selectedIndex = 0; document.getElementById('conduitSize1').selectedIndex = 0; document.getElementById('conduitLength1').value = '10'; document.getElementById('wireType1').selectedIndex = 0; document.getElementById('wireSize1').selectedIndex = 0; document.getElementById('wireLength1').value = '10'; document.getElementById('wireCount1').value = '1'; document.getElementById('freeAir').checked = false; toggleFreeAir();
conduitType1
, conduitSize1
, conduitLength1
, wireType1
, wireSize1
, wireLength1
, wireCount1
, and freeAir
)populateConduitSizes(document.getElementById('conduitSize1'), 'Western Tube EMT');
populateWireSizes(document.getElementById('wireSize1'));
updateEmptyEntries('entryList'); updateEmptyEntries('setsList');
Handles checking ampacity based on wire size, temperature rating, and derating factors.
Function: Initiates an ampacity check for a specific set of wires.
const setItem = document.getElementById(`set-${setId}`);
set-${setId}
)const conductorDetails = extractConductorDetails(setItem.innerText);
extractConductorDetails()
.showAmpacityResults(setId, conductorDetails);
Function: Extracts conductor details from a given text input.
const conduitMatch = text.match(/Conduit: Type - ([^,]+)/);
const wireMatches = text.match(/Wire: Type - (\w+-?\w*), Size - (\d+\/?\d*), Length - (\d+) ft, Count - (\d+)/g);
if (conduitMatch) { conduitType = conduitMatch[1]; } else { conduitType = "No Conduit"; } if (wireMatches) { wireMatches.forEach(match => { const details = {}; const matchDetails = match.match(/Wire: Type - (\w+-?\w*), Size - (\d+\/?\d*), Length - (\d+) ft, Count - (\d+)/); if (matchDetails) { details.wireType = matchDetails[1]; details.wireSize = matchDetails[2]; details.wireLength = parseInt(matchDetails[3]); details.wireCount = parseInt(matchDetails[4]); detailsList.push(details); } }); } return { conduitType, detailsList };
Function: Retrieves the base ampacity for a given wire size and temperature rating.
if (ampacityData.ampacity[wireSize] && ampacityData.ampacity[wireSize][tempRating]) { return ampacityData.ampacity[wireSize][tempRating]; }
ampacityData
based on wire size and temperature rating.return ampacityData.ampacity[wireSize][tempRating];
Function: Applies derating factors based on the number of wires.
for (let range in ampacityData.ccc_derating) { const [min, max] = range.includes('and above') ? [parseInt(range), Infinity] : range.split('-').map(Number); if ((max && wireCount >= min && wireCount <= max) || (!max && wireCount >= min)) { deratingFactor = ampacityData.ccc_derating[range]; break; } }
ampacityData.ccc_derating
to find the appropriate derating factor based on wire count.return baseAmpacity * deratingFactor;
Function: Calculates the ampacities of wires considering conduit type and temperature rating.
const totalWireCount = detailsList.reduce((total, details) => total + details.wireCount, 0); const ampacities = detailsList.map(details => { let baseAmpacity; if (conduitType === "Free Air" || conduitType === "No Conduit") { baseAmpacity = freeAirCopperData[details.wireSize] ? freeAirCopperData[details.wireSize][tempRating] : undefined; } else { baseAmpacity = getBaseAmpacity(details.wireSize, tempRating); } ...
if (baseAmpacity === undefined) { return { wireSize: details.wireSize, wireType: details.wireType, ampacity: 'N/A' }; } let deratedAmpacity; if (conduitType === "Free Air" || conduitType === "No Conduit") { deratedAmpacity = baseAmpacity; } else { deratedAmpacity = applyDerating(baseAmpacity, totalWireCount); } return { wireSize: details.wireSize, wireType: details.wireType, ampacity: deratedAmpacity };
return ampacities;
Function: Applies ambient temperature derating to the calculated ampacities.
const deratingFactor = ampacityData.ambient_temp_derating[tempRating].find(([tempRange, factor]) => { const [minTemp, maxTemp] = tempRange.split('-').map(Number); return ambientTemperature >= minTemp && ambientTemperature <= maxTemp; })[1];
return ampacities.map(ampacity => { if (ampacity.ampacity === 'N/A') return ampacity; return { ...ampacity, ampacity: ampacity.ampacity * deratingFactor }; });
Function: Displays the ampacity results.
const tempRating = prompt('Enter the temperature rating (60, 75, or 90°C):');
const ampacities = calculateAmpacities(conductorDetails.detailsList, tempRating, conductorDetails.conduitType);
if (ambientTemperature) { ampacities = applyAmbientTemperatureDerating(ampacities, ambientTemperature, tempRating); }
document.getElementById('ampacityResults').innerHTML = ampacities.map(ampacity => { return `${ampacity.wireType} ${ampacity.wireSize}: ${ampacity.ampacity}A`; }).join('
');
ampacityResults
)Contains functions for calculating the total weight and wire fill percentage.
Function: Calculates the total weight of conduits and wires, determines the fill percentage, and adds the set to the list.
let totalWeight = 0; let totalWireArea = 0; let totalConduitArea = 0; let numConductors = 0; let hasFreeAir = false;
const listItems = document.getElementById('entryList').getElementsByTagName('li'); for (let item of listItems) { const text = item.innerText; ...
if (text.startsWith('Conduit:')) { const typeMatch = text.match(/Type - ([^,]+)/); const sizeMatch = text.match(/Size - ([^,]+)/); const lengthMatch = text.match(/Length - ([^ ]+)/); if (typeMatch && sizeMatch && lengthMatch) { const type = typeMatch[1]; const size = sizeMatch[1]; const length = parseFloat(lengthMatch[1]); if (type === "Free Air") { hasFreeAir = true; } else { const weightPerFootIndex = ["1/2", "3/4", "1", "1-1/4", "1-1/2", "2", "2-1/2", "3", "3-1/2", "4"].indexOf(size); const weightPerFoot = conduitData[type][weightPerFootIndex]; const conduitArea = conduitSizeData[type][size]; if (weightPerFoot !== undefined && !isNaN(weightPerFoot)) { totalWeight += weightPerFoot * length; totalConduitArea += conduitArea; } } } }
} else if (text.startsWith('Wire:') || text.startsWith('Wire (Non-CCC):') || text.startsWith('Wire (EGC):') || text.startsWith('Wire (GEC):') || text.startsWith('Wire (SBJ):')) { const typeMatch = text.match(/Type - ([^,]+)/); const sizeMatch = text.match(/Size - ([^,]+)/); const lengthMatch = text.match(/Length - ([^ ]+)/); const countMatch = text.match(/Count - ([^ ]+)/); if (typeMatch && sizeMatch && lengthMatch && countMatch) { const type = typeMatch[1]; const size = sizeMatch[1]; const length = parseFloat(lengthMatch[1]); const count = parseInt(countMatch[1]); const wire = wireData.find(w => w.size === size); if (wire) { const weightPerFoot = type === 'THWN-2' ? wire.THWN2.weight / 1000 : wire.XHHW2.weight / 1000; if (weightPerFoot !== undefined && !isNaN(weightPerFoot)) { totalWeight += weightPerFoot * length * count; const wireArea = type === 'THWN-2' ? wire.THWN2.area : wire.XHHW2.area; totalWireArea += wireArea * count; numConductors += count; } } } }
if (!hasFreeAir && totalConduitArea > 0) { const fillPercentage = (totalWireArea / totalConduitArea) * 100; document.getElementById('wireFill').innerText = `${fillPercentage.toFixed(2)}%`; } else { document.getElementById('wireFill').innerText = 'N/A'; }
document.getElementById('totalWeight').innerText = `${totalWeight.toFixed(2)} lbs`;
totalWeight
)Function: Allows editing of a specific set.
const setItem = document.getElementById(`set-${setId}`);
set-${setId}
)const conduitMatches = setItem.innerText.match(/Conduit: Type - ([^,]+), Size - ([^,]+), Length - ([^ ]+) ft/); if (conduitMatches) { const [_, type, size, length] = conduitMatches; document.getElementById('conduitType1').value = type; document.getElementById('conduitSize1').value = size; document.getElementById('conduitLength1').value = length; }
const wireMatches = setItem.innerText.match(/Wire: Type - ([^,]+), Size - ([^,]+), Length - ([^ ]+) ft, Count - ([^ ]+)/g); if (wireMatches) { wireMatches.forEach(match => { const [_, type, size, length, count] = match.match(/Wire: Type - ([^,]+), Size - ([^,]+), Length - ([^ ]+) ft, Count - ([^ ]+)/); document.getElementById('wireType1').value = type; document.getElementById('wireSize1').value = size; document.getElementById('wireLength1').value = length; document.getElementById('wireCount1').value = count; addWire(); }); }
setItem.remove();
Contains functions for removing items and sets.
Function: Removes a specific item from the entry list.
const listItem = button.parentElement;
listItem.remove();
updateTotalWeightAndWireFill();
updateEmptyEntries('entryList'); updateEmptyEntries('setsList');
Function: Removes a specific set from the list of sets.
const setItem = document.getElementById(`set-${setId}`);
set-${setId}
)const weightMatch = setItem.innerText.match(/Total Weight: ([^ ]+) lbs/);
if (weightMatch) { const setWeight = parseFloat(weightMatch[1]); const cumulativeWeightElem = document.getElementById('cumulativeWeight'); const currentCumulativeWeight = parseFloat(cumulativeWeightElem.innerText); cumulativeWeightElem.innerText = `${(currentCumulativeWeight - setWeight).toFixed(2)} lbs`; }
setItem.remove();
Contains functions for resetting the entry list and sets.
Function: Resets the entire list of entries and sets.
document.getElementById('entryList').innerHTML = '';
entryList
)document.getElementById('setsList').innerHTML = '';
setsList
)document.getElementById('cumulativeWeight').innerText = '0 lbs';
cumulativeWeight
)document.getElementById('conduitType1').selectedIndex = 0; document.getElementById('conduitSize1').selectedIndex = 0; document.getElementById('conduitLength1').value = '10'; document.getElementById('wireType1').selectedIndex = 0; document.getElementById('wireSize1').selectedIndex = 0; document.getElementById('wireLength1').value = '10'; document.getElementById('wireCount1').value = '1'; document.getElementById('freeAir').checked = false; toggleFreeAir();
conduitType1
, conduitSize1
, conduitLength1
, wireType1
, wireSize1
, wireLength1
, wireCount1
, and freeAir
)populateConduitSizes(document.getElementById('conduitSize1'), 'Western Tube EMT');
populateWireSizes(document.getElementById('wireSize1'));
updateEmptyEntries('entryList'); updateEmptyEntries('setsList');
Function: Resets the current list of entries.
document.getElementById('entryList').innerHTML = '';
entryList
)document.getElementById('totalWeight').innerText = '0 lbs'; document.getElementById('wireFill').innerText = '0%';
totalWeight
and wireFill
)document.getElementById('conduitType1').selectedIndex = 0; document.getElementById('conduitSize1').selectedIndex = 0; document.getElementById('conduitLength1').value = '10'; document.getElementById('wireType1').selectedIndex = 0; document.getElementById('wireSize1').selectedIndex = 0; document.getElementById('wireLength1').value = '10'; document.getElementById('wireCount1').value = '1'; document.getElementById('freeAir').checked = false; toggleFreeAir();
conduitType1
, conduitSize1
, conduitLength1
, wireType1
, wireSize1
, wireLength1
, wireCount1
, and freeAir
)populateConduitSizes(document.getElementById('conduitSize1'), 'Western Tube EMT');
populateWireSizes(document.getElementById('wireSize1'));
updateEmptyEntries('entryList'); updateEmptyEntries('setsList');
Contains functions for updating the total weight and wire fill.
Function: Updates the total weight and wire fill percentage based on the current entries.
let totalWeight = 0; let totalWireArea = 0; let totalConduitArea = 0; let numConductors = 0; let hasFreeAir = false;
const listItems = document.getElementById('entryList').getElementsByTagName('li'); for (let item of listItems) { const text = item.innerText; ...
if (text.startsWith('Conduit:')) { const typeMatch = text.match(/Type - ([^,]+)/); const sizeMatch = text.match(/Size - ([^,]+)/); const lengthMatch = text.match(/Length - ([^ ]+)/); if (typeMatch && sizeMatch && lengthMatch) { const type = typeMatch[1]; const size = sizeMatch[1]; const length = parseFloat(lengthMatch[1]); if (type === "Free Air") { hasFreeAir = true; } else { const weightPerFootIndex = ["1/2", "3/4", "1", "1-1/4", "1-1/2", "2", "2-1/2", "3", "3-1/2", "4"].indexOf(size); const weightPerFoot = conduitData[type][weightPerFootIndex]; const conduitArea = conduitSizeData[type][size]; if (weightPerFoot !== undefined && !isNaN(weightPerFoot)) { totalWeight += weightPerFoot * length; totalConduitArea += conduitArea; } } } }
} else if (text.startsWith('Wire:') || text.startsWith('Wire (Non-CCC):') || text.startsWith('Wire (EGC):') || text.startsWith('Wire (GEC):') || text.startsWith('Wire (SBJ):')) { const typeMatch = text.match(/Type - ([^,]+)/); const sizeMatch = text.match(/Size - ([^,]+)/); const lengthMatch = text.match(/Length - ([^ ]+)/); const countMatch = text.match(/Count - ([^ ]+)/); if (typeMatch && sizeMatch && lengthMatch && countMatch) { const type = typeMatch[1]; const size = sizeMatch[1]; const length = parseFloat(lengthMatch[1]); const count = parseInt(countMatch[1]); const wire = wireData.find(w => w.size === size); if (wire) { const weightPerFoot = type === 'THWN-2' ? wire.THWN2.weight / 1000 : wire.XHHW2.weight / 1000; if (weightPerFoot !== undefined && !isNaN(weightPerFoot)) { totalWeight += weightPerFoot * length * count; const wireArea = type === 'THWN-2' ? wire.THWN2.area : wire.XHHW2.area; totalWireArea += wireArea * count; numConductors += count; } } } }
if (!hasFreeAir && totalConduitArea > 0) { const fillPercentage = (totalWireArea / totalConduitArea) * 100; document.getElementById('wireFill').innerText = `${fillPercentage.toFixed(2)}%`; } else { document.getElementById('wireFill').innerText = 'N/A'; }
document.getElementById('totalWeight').innerText = `${totalWeight.toFixed(2)} lbs`;
totalWeight
)Contains functions for checking EGC requirements.
Function: Initiates an EGC check for a specific set.
const setItem = document.getElementById(`set-${setId}`);
set-${setId}
)const breakerSize = prompt('Enter the size of the largest breaker or fuse in the circuit (in amps):');
const matchingData = getMatchingEgcData(breakerSize);
const egcDetails = extractEgcDetails(setItem.innerText);
extractEgcDetails()
.showEgcResults(egcDetails, breakerSize, matchingData);
Function: Finds the appropriate EGC size based on the breaker or fuse size.
const egcSizeRange = Object.keys(egcData).find(range => { const [min, max] = range.includes('and above') ? [parseInt(range), Infinity] : range.split('-').map(Number); return (max && breakerSize >= min && breakerSize <= max) || (!max && breakerSize >= min); }); return egcData[egcSizeRange];
egcData
to find a matching range based on the breaker or fuse size.return egcData[egcSizeRange];
Function: Extracts EGC details from a given text input.
const wireMatches = text.match(/Wire \(EGC\): Type - ([^,]+), Size - ([^,]+), Length - ([^ ]+) ft, Count - ([^ ]+)/g);
const detailsList = []; if (wireMatches) { wireMatches.forEach(match => { const details = {}; const matchDetails = match.match(/Wire \(EGC\): Type - ([^,]+), Size - ([^,]+), Length - ([^ ]+) ft, Count - ([^ ]+)/); if (matchDetails) { details.wireType = matchDetails[1]; details.wireSize = matchDetails[2]; details.wireLength = parseInt(matchDetails[3]); details.wireCount = parseInt(matchDetails[4]); detailsList.push(details); } }); } return detailsList;
Function: Displays the EGC results.
detailsList.forEach(details => { const requiredSize = matchingData.size; const actualSize = details.wireSize; const result = compareSizes(actualSize, requiredSize); results.push({ wireType: details.wireType, wireSize: details.wireSize, requiredSize, result }); });
document.getElementById('egcResults').innerHTML = results.map(result => { return `Type: ${result.wireType}, Size: ${result.wireSize}, Required: ${result.requiredSize} - ${result.result ? 'OK' : 'Not OK'}`; }).join('
');
egcResults
)Contains data used for calculations and checks.
Initializes the application and handles event listeners.
Function: Initializes the application when the page is fully loaded.
document.getElementById('addConduitBtn').addEventListener('click', addConduit); document.getElementById('addWireBtn').addEventListener('click', addWire); document.getElementById('calculateBtn').addEventListener('click', calculateWeight); document.getElementById('resetButton').addEventListener('click', resetList); document.getElementById('darkModeToggle').addEventListener('click', toggleDarkMode);
populateConduitSizes(document.getElementById('conduitSize1'), 'Western Tube EMT'); populateWireSizes(document.getElementById('wireSize1'));
updateEmptyEntries('entryList'); updateEmptyEntries('setsList');
Function: Exports the conduit and wire data to a PDF.
const doc = new jsPDF();
doc.text('Conduit and Wire Weight Calculator', 10, 10);
const setsList = document.getElementById('setsList').innerText; doc.text(setsList, 10, 20);
setsList
)doc.save('conduit_wire_weight_calculator.pdf');
Contains utility functions for the application.
Function: Returns the allowable fill percentage based on the number of conductors.
switch (numConductors) { case 1: return 53; case 2: return 31; default: return 40; }
Function: Populates a dropdown with conduit sizes based on the selected conduit type.
selectElement.innerHTML = '';
for (let size of Object.keys(conduitSizeData[conduitType])) { const option = document.createElement('option'); option.value = size; option.text = size; selectElement.add(option); }
conduitSizeData
.Function: Populates a dropdown with wire sizes.
selectElement.innerHTML = '';
for (let wire of wireData) { const option = document.createElement('option'); option.value = wire.size; option.text = wire.size; selectElement.add(option); }
wireData
.Function: Toggles the display of conduit type, size, and length fields based on the "Free Air" checkbox.
const freeAirCheckbox = document.getElementById('freeAir'); const conduitFields = document.getElementById('conduitFields'); if (freeAirCheckbox.checked) { conduitFields.style.display = 'none'; } else { conduitFields.style.display = 'block'; }
freeAir
and conduitFields
)Function: Updates the display if the list is empty.
const listElement = document.getElementById(listId); if (listElement.children.length === 0) { const emptyItem = document.createElement('li'); emptyItem.className = 'empty'; emptyItem.innerText = 'Empty'; listElement.appendChild(emptyItem); } else { const emptyItem = listElement.querySelector('.empty'); if (emptyItem) { emptyItem.remove(); } }
listId
)Function: Compares two wire sizes.
const sizeOrder = ['18', '16', '14', '12', '10', '8', '6', '4', '3', '2', '1', '1/0', '2/0', '3/0', '4/0']; const size1Index = sizeOrder.indexOf(size1); const size2Index = sizeOrder.indexOf(size2);
return size1Index <= size2Index;