|
(function() { |
|
const treeFilter = (node, predicate) => { |
|
const walker = (node, predicate) => { |
|
if (predicate(node)) { |
|
return node; |
|
} |
|
|
|
if (!node.children) { |
|
return null; |
|
} |
|
|
|
for (const child of node.children) { |
|
const found = walker(child, predicate); |
|
if (found) { |
|
return found; |
|
} |
|
} |
|
return null; |
|
}; |
|
return walker(node, predicate); |
|
}; |
|
|
|
const petabytesToTB = u => u * 1024; |
|
const terabytesToTB = u => u; |
|
const gigabytesToTB = u => u / 1024; |
|
const megabytesToTB = u => u / 1024 / 1024; |
|
const kilobytesToTB = u => u / 1024 / 1024 / 1024; |
|
const bytesToTB = u => u / 1024 / 1024 / 1024 / 1024; |
|
|
|
const extractUnitString = str => { |
|
const match = str.match(/\d+(\.\d+)? (B|KB|MB|GB|TB|PB)/); |
|
if (!match) { |
|
throw new Error(`could not find a unit string in "${str}"`); |
|
} |
|
return match[0]; |
|
}; |
|
|
|
const conversionFn = unitString => { |
|
if (unitString.endsWith(` B`)) return bytesToTB; |
|
if (unitString.endsWith(` KB`)) return kilobytesToTB; |
|
if (unitString.endsWith(` MB`)) return megabytesToTB; |
|
if (unitString.endsWith(` GB`)) return gigabytesToTB; |
|
if (unitString.endsWith(` TB`)) return terabytesToTB; |
|
if (unitString.endsWith(` PB`)) return petabytesToTB; |
|
throw new Error(`could not determine units in "${unitString}"`); |
|
}; |
|
|
|
const unitStringToTB = unitString => conversionFn(unitString)(parseFloat(unitString)); |
|
|
|
const tbToMoney = tb => `$${(tb * 5.0).toFixed(2)}`; |
|
|
|
const isQueryValidatorNode = e => |
|
e.firstChild && |
|
e.firstChild.nodeType === 3 && |
|
e.firstChild.textContent.includes("query will process"); |
|
|
|
const appendCostToQueryInfo = (queryInfo, cost) => |
|
queryInfo.replace("when run", `and cost ${cost} when run`); |
|
|
|
const queryInfoContainsCost = queryInfo => queryInfo.includes("cost"); |
|
|
|
const addCostToQueryInfoNode = textNode => { |
|
const queryInfo = textNode.data; |
|
|
|
if (queryInfoContainsCost(queryInfo)) { |
|
return; |
|
} |
|
|
|
const unitString = extractUnitString(queryInfo); |
|
if (!unitString) { |
|
return; |
|
} |
|
const cost = tbToMoney(unitStringToTB(unitString)); |
|
textNode.data = appendCostToQueryInfo(queryInfo, cost); |
|
}; |
|
|
|
const addMutationObserver = () => { |
|
const validatorNode = treeFilter(document.body, isQueryValidatorNode); |
|
if (!validatorNode) { |
|
setTimeout(addMutationObserver, 1000); |
|
return; |
|
} |
|
const textNode = validatorNode.firstChild; |
|
if (!textNode) { |
|
setTimeout(addMutationObserver, 1000); |
|
return; |
|
} |
|
const config = { characterData: true, childList: false, subtree: false }; |
|
const callback = mutationList => { |
|
const textNode = mutationList[0].target; |
|
addCostToQueryInfoNode(textNode); |
|
}; |
|
const observer = new MutationObserver(callback); |
|
observer.observe(textNode, config); |
|
addCostToQueryInfoNode(textNode); |
|
}; |
|
|
|
addMutationObserver(); |
|
})(); |