Skip to content

Instantly share code, notes, and snippets.

@levantAJ
Last active October 15, 2022 05:55
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save levantAJ/681b5c91220658b983ca422814a0ba22 to your computer and use it in GitHub Desktop.
Save levantAJ/681b5c91220658b983ca422814a0ba22 to your computer and use it in GitHub Desktop.
Auto Fill - Filling Form
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