Last active
October 15, 2022 05:55
-
-
Save levantAJ/681b5c91220658b983ca422814a0ba22 to your computer and use it in GitHub Desktop.
Auto Fill - Filling Form
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
function autoFillFillingLog(message) { | |
window.webkit.messageHandlers.autoFillFillingShippingInfoLog.postMessage(message); | |
} | |
autoFillFillingLog("Beginning...."); | |
/// The configuration xpath will be replace before injected to IAB | |
/// format: [{xPath, key, value}] | |
var autoFillFillingConfigurationXPathsString = "checkout-configuration-xpaths"; | |
/// Contains list of timers which used for waiting to be the node to be appeared | |
var autoFillFillingNodeIntervalIDs = {}; // {configuration.key: intervalID} | |
function autoFillFillingGetNode(xPath) { | |
var formNode = document.evaluate(xPath, document, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue; | |
return formNode | |
} | |
function autoFillFillingStartInputAnimation(xPath) { | |
var node = autoFillFillingGetNode(xPath); | |
var startEvent = new Event('animationstart', { bubbles: true, cancelable: true }); | |
node.dispatchEvent(startEvent); | |
var endEvent = new Event('animationend', { bubbles: true, cancelable: true }); | |
node.dispatchEvent(endEvent); | |
} | |
// Work for Nike | |
function autoFillFillingInsertTextValue(el, value) { | |
if (el) { | |
el.focus(); | |
el.select(); | |
try { | |
if (!document.execCommand('insertText', false, value)) { | |
// Fallback for Firefox: just replace the value | |
el.value = value; | |
} | |
el.dispatchEvent(new Event('change', {bubbles: true})); // usually not needed | |
} catch (e) { | |
autoFillFillingLog("error caught: " + e) | |
} | |
} | |
} | |
// Work for Adidas, iHerb | |
function autoFillFillingSetReactJSValue(el, value) { | |
const previousValue = el.value; | |
if (el.type === 'checkbox' || el.type === 'radio') { | |
if ((!!value && !el.checked) || (!!!value && el.checked)) { | |
el.click(); | |
} | |
} else el.value = value; | |
const tracker = el._valueTracker; | |
if (tracker) { | |
tracker.setValue(previousValue); | |
} | |
// 'change' instead of 'input', see https://github.com/facebook/react/issues/11488#issuecomment-381590324 | |
el.dispatchEvent(new Event('change', { bubbles: true })); | |
} | |
function autoFillFillingNormalValue(node, value) { | |
node.value = value; | |
node.setAttribute("value", value); | |
node.dispatchEvent(new Event('change', { bubbles: true })); | |
node.dispatchEvent(new Event('input', { bubbles: true })); | |
} | |
function autoFillFillingValue(node, xPath, value) { | |
if (node.value) { | |
// Only fill the empty input | |
return; | |
} | |
// Start animation to get rid of placeholder state | |
// e.g. Adidas, iHerb | |
autoFillFillingStartInputAnimation(xPath); | |
// Set value for normal merchants: ICONIC, NET-A-PORTER | |
autoFillFillingNormalValue(node, value); | |
// Set value for Adidas, iHerb | |
autoFillFillingSetReactJSValue(node, value); | |
// Set value for Nike | |
autoFillFillingInsertTextValue(node, value); | |
} | |
/// configuration = {xPath, key, value} | |
function autoFillFillingObserveNodeAppeared(configuration) { | |
var xPath = configuration.xPath; | |
var key = configuration.key; | |
var intervalID = autoFillFillingNodeIntervalIDs[key]; | |
// Get node by xPath | |
var node = autoFillFillingGetNode(xPath); | |
if (node) { | |
// Remove interval when node is appeared | |
if (intervalID) { | |
clearInterval(intervalID); | |
} | |
node.addEventListener("focus", function(e) { | |
if (node.value) { | |
autoFillFillingLog("Focused but has value: " + key); | |
} else { | |
window.webkit.messageHandlers.autoFillFillingShippingInfo.postMessage(key); | |
autoFillFillingLog("Focused: " + key); | |
} | |
}); | |
autoFillFillingLog("Found: " + key); | |
} else { | |
autoFillFillingLog("Looking for: " + key); | |
// Start a timer if needed | |
if (!intervalID) { | |
var newInterval = setInterval(() => autoFillFillingObserveNodeAppeared(configuration), 3000); | |
autoFillFillingNodeIntervalIDs[key] = newInterval; | |
autoFillFillingLog("Start timer for: " + key); | |
} | |
} | |
} | |
function makeConfigurationXPaths(configuration) { | |
return JSON.parse(configuration); | |
} | |
function autoFillFillingObserve() { | |
var configurationXPaths = makeConfigurationXPaths(autoFillFillingConfigurationXPathsString); | |
for (let i = 0; i < configurationXPaths.length; i++) { | |
autoFillFillingObserveNodeAppeared(configurationXPaths[i]) | |
} | |
} | |
// Start to observe whether the node is focus to notify the IAB to show AutoFill pop-up | |
autoFillFillingObserve(); | |
function autoFillFillNode(configuration) { | |
var xPath = configuration.xPath; | |
var key = configuration.key; | |
var value = configuration.value; | |
// Get node by xPath | |
var node = autoFillFillingGetNode(xPath); | |
if (node) { | |
autoFillFillingValue(node, xPath, value); | |
autoFillFillingLog("Filled: " + key + " = " + value); | |
} else { | |
autoFillFillingLog("Filling failed because node not found " + key); | |
} | |
} | |
function autoFillFillNodes(configurationXPaths) { | |
for (let i = 0; i < configurationXPaths.length; i++) { | |
autoFillFillNode(configurationXPaths[i]); | |
} | |
} | |
function autoFillGetActiveNodeConfiguration(configurationXPaths) { | |
for (let i = 0; i < configurationXPaths.length; i++) { | |
var configuration = configurationXPaths[i]; | |
var xPath = configuration.xPath; | |
var node = autoFillFillingGetNode(xPath); | |
if (node && node === document.activeElement) { | |
return configuration; | |
} | |
} | |
return null; | |
} | |
/// Call this method to fill data into all fields | |
/// e.g. autoFillFillAllData("[{"xPath": "input", "key":"given-name", "value": "John"}]") | |
function autoFillFillAllData(configuration) { | |
var configurationXPaths = makeConfigurationXPaths(configuration); | |
// Track the current active node configuration | |
// Fill it at the end | |
var activeConfiguration = autoFillGetActiveNodeConfiguration(configurationXPaths); | |
// Fill the remaining nodes | |
autoFillFillNodes(configurationXPaths); | |
// We have to fill the very first focus node at last to by pass the validation | |
autoFillFillNode(activeConfiguration); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment