Skip to content

Instantly share code, notes, and snippets.

@longnull
Last active July 4, 2023 13:09
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save longnull/7bb9f946f3bba27a7d1df272291eef9f to your computer and use it in GitHub Desktop.
Save longnull/7bb9f946f3bba27a7d1df272291eef9f to your computer and use it in GitHub Desktop.
AliExpress Address Randomizer
// ==UserScript==
// @name AliExpress Address Randomizer
// @description Рандомизирует данные по заданному шаблону и заполняет форму адреса доставки
// @author longnull
// @namespace longnull
// @version 1.2.1
// @updateURL https://gist.github.com/longnull/7bb9f946f3bba27a7d1df272291eef9f/raw/AliExpressAddressRandomizer.user.js
// @downloadURL https://gist.github.com/longnull/7bb9f946f3bba27a7d1df272291eef9f/raw/AliExpressAddressRandomizer.user.js
// @match *://ilogisticsaddress.aliexpress.com/addressList.htm*
// @match *://ilogisticsaddress.aliexpress.ru/addressList.htm*
// @match *://shoppingcart.aliexpress.com/orders.htm*
// @match *://shoppingcart.aliexpress.ru/orders.htm*
// @match *://shoppingcart.aliexpress.com/order/confirm_order.htm*
// @match *://shoppingcart.aliexpress.ru/order/confirm_order.htm*
// @match *://www.aliexpress.com/p/trade/confirm.html*
// @match *://aliexpress.ru/checkout*
// @match *://aliexpress.ru/address-list*
// @grant GM_xmlhttpRequest
// @connect worldnamegenerator.com
// @connect *
// @noframes
// ==/UserScript==
(() => {
'use strict';
// ================================================================================================================================================================
// Изменения
// ================================================================================================================================================================
// v1.2.1
// - Обновлён под актульный aliexpress.ru
// v1.2
// - Обновлён под новый сайт aliexpress.ru (также нужно обновить скрипт для шаблонов - https://gist.github.com/longnull/7bb9f946f3bba27a7d1df272291eef9f/raw/AliExpressAddressRandomizerTemplates.user.js)
// - Добавлено отключение появления новой формы адреса на странице оформления (теперь не нужен отдельный скрипт)
// - Изменён стиль кнопок
// v1.1.8
// - Добавлен новый адрес страницы оформления
// - Обновлён шаблон, который берёт данные с сайта
// ================================================================================================================================================================
// Синтаксис шаблонов
// ================================================================================================================================================================
// Генераторы
// ----------------------------------------------------------------------------------------------------------------------------------------------------------------
// {1|2|3} | Один случайный элемент из перечисленных
// | Элементы разделяются символом "|", могут быть пустыми
// |
// | Примеры:
// | {Василий|Иван|Пётр} - Одно из трёх имён
// | {,|} - Запятая или ничего
// ----------------------------------------------------------------------------------------------------------------------------------------------------------------
// [1|2|3| ] | Случайный порядок перечисленных элементов
// | Элементы разделяются символом "|", последний элемент - разделитель, который будет вставлен между элементами в сгенерированной строке
// |
// | Пример:
// | [1|2|3| ] - Случайный порядок цифр с пробелом между ними
// ----------------------------------------------------------------------------------------------------------------------------------------------------------------
// %rs(len,chars)% | Случайная строка
// |
// | Параметры:
// | len - Длина строки. Если указан диапазон (например, "6-12"), то длина будет случайной в указанном диапазоне
// | chars (необязательный) - Наборы символов из которых будет сгенерирована строка. Значение по умолчанию - "zZ0"
// | Доступные наборы символов:
// | Английский алфавит в нижнем регистре - нужно указать любую букву английского алфавита в нижнем регистре
// | Английский алфавит в верхнем регистре - нужно указать любую букву английского алфавита в верхнем регистре
// | Русский алфавит в нижнем регистре - нужно указать любую букву русского алфавита в нижнем регистре
// | Русский алфавит в верхнем регистре - нужно указать любую букву русского алфавита в верхнем регистре
// | Цифры - нужно указать любую цифру
// | Примеры:
// | %rs(10,zZ0)% - Строка длиной в 10 символов, в которой могут быть буквы английского алфавита в обоих регистрах и цифры
// | %rs(10,яЯ)% - Строка длиной в 10 символов, в которой могут быть буквы русского алфавита в обоих регистрах
// | %rs(6-12,0)% - Строка со случайной длиной от 6 до 12 символов, в которой могут быть только цифры
// ----------------------------------------------------------------------------------------------------------------------------------------------------------------
// %rn(range,len)% | Случайное число
// |
// | Параметры:
// | range - Диапазон случайного числа в виде "1-100"
// | len (необязательный) - Дополнение нулями до указанной длины
// | Примеры:
// | %rn(1000-10000)% - Случайное число от 1000 до 10000
// | %rn(1-12,2)% - Случайное число от 1 до 12 дополненное нулём
// ----------------------------------------------------------------------------------------------------------------------------------------------------------------
// %rw(len,cap)% | Случайное "слово" (чередуемые согласные и гласные буквы английского алфавита)
// | Примеры "слов": Buvemu, Ditubituj, Humanena, Cihoho
// |
// | Параметры:
// | len - Длина "слова". Если указан диапазон (например, "6-12"), то длина будет случайной в указанном диапазоне
// | cap (необязательный) - Если указано "1" или "true", то первая буква будет заглавной. Значение по умолчанию - не делать заглавной
// | Примеры:
// | %rw(10)% - Случайное "слово" длиной в 10 символов
// | %rw(6-10,1)% - Случайное "слово" со случайной длиной от 6 до 10 символов и начинающееся с заглавной буквы
// ----------------------------------------------------------------------------------------------------------------------------------------------------------------
// %dt(format)% | Строка с элементами даты или времени
// |
// | Параметры:
// | format - Формат строки. Может содержать токены даты/времени и другие символы. Значение по умолчанию - "dd.MM.yyyy"
// | Список доступных токенов:
// | yy - Год из двух цифр
// | yyyy - Год из четырёх цифр
// | M - Месяц не дополняемый нулём
// | MM - Месяц дополняемый нулём
// | MMM - Короткое название месяца (на английском)
// | MMMM - Полное название месяца (на английском)
// | EEE - Короткое название дня недели (на английском)
// | EEEE - Полное название дня недели (на английском)
// | d - День не дополняемый нулём
// | dd - День дополняемый нулём
// | h - Час в 12-часовом формате не дополняемый нулём
// | hh - Час в 12-часовом формате дополняемый нулём
// | H - Час в 24-часовом формате не дополняемый нулём
// | HH - Час в 24-часовом формате дополняемый нулём
// | m - Минута не дополняемая нулём
// | mm - Минута дополняемая нулём
// | aaa - AM/PM
// | s - Секунда не дополняемая нулём
// | ss - Секунда дополняемая нулём
// | S - Миллисекунда
// | Примеры:
// | %dt(ddMMyyyyHHmm)% - Строка вида "010120201200"
// | %dt(dd.MM.yyyy HH:mm)% - Строка вида "01.01.2020 12:00"
// ----------------------------------------------------------------------------------------------------------------------------------------------------------------
// Сохранение и повторное использование значений из генераторов
// ----------------------------------------------------------------------------------------------------------------------------------------------------------------
// $имя | Сохранение сгенерированной строки в переменную с указанным именем
// |
// | Примеры:
// | {1|2|3$цифра}
// | [Pupkin|Vasiliy|Vasilevich| $фио]
// | %rs(6-12)$символы%
// ----------------------------------------------------------------------------------------------------------------------------------------------------------------
// %имя% | Значение переменной с указанным именем
// | При использовании переменных стоит учитывать порядок обработки полей шаблона, т.к. переменную нельзя использовать раньше, чем она была создана
// | Поля шаблона при генерации обрабатываются в таком порядке: country, province, city, name, address, address2, zip, mobileCode, mobile, extra
// |
// | Пример:
// | %фио%
// ----------------------------------------------------------------------------------------------------------------------------------------------------------------
// Прочее
// --------------------------------------------------------------------------------
// * | Выбрать случайный элемент в выпадающем списке (для полей country, province, city)
// |
// | Пример:
// | province: '*'
// ----------------------------------------------------------------------------------------------------------------------------------------------------------------
// ================================================================================================================================================================
// Шаблоны
// ================================================================================================================================================================
// Чтобы добавить шаблон, нужно скопировать имеющийся шаблон от метки начала до метки конца, вставить рядом с другим шаблоном (порядок кнопок будет таким же) и изменить имя
// Всегда храните свои шаблоны в отдельном файле, не храните их только в скрипте, иначе потеряете их при обновлении скрипта
// Все поля в шаблонах необязательные
//
// Шаблоны можно вставить в отдельный скрипт, чтобы не приходилось их вставлять обратно в основной скрипт после обновлений
// Скрипт для вставки шаблонов: https://gist.github.com/longnull/7bb9f946f3bba27a7d1df272291eef9f/raw/AliExpressAddressRandomizerTemplates.user.js
// Иногда может быть необходимо обновить скрипт с шаблонами, если, например, в основном скрипте изменились @match строки вверху (они должны совпадать)
let templates = {
// --- Начало шаблона ---
// Имя шаблона. Используется для подписей кнопок и в логике скрипта, не должно повторяться
'BY': {
// ФИО
// В данном примере используется переменная из поля variables
name: '%фио%',
// Страна
country: 'Belarus',
// Область/регион/штат
// Может быть строкой, массивом и объектом с отдельными значениями для текстового поля и для выпадающего списка (см. примеры ниже)
// В данном примере используется переменная из поля variables
province: '%город%ska{y|j}a oblast',
// Город
// Может быть строкой, массивом и объектом с отдельными значениями для текстового поля и для выпадающего списка (см. примеры ниже)
// В данном примере используется переменная из поля variables
city: '%город%',
// Адрес
address: '{Belarus, %город%, |%город%, |}ul{.|ica|itsa|} Arbuzna{y|j}a{,| -|$dlm} d{.|om} {n|n.|no|no.|nomer|} 9%dlm% k{v|w}{.|ar|ar.|art|art.|artira} {n|n.|no|no.|nomer|} 99%dlm% %rs(6-12,a)%',
// Адрес 2
address2: '%rs(6-10,a)%',
// Индекс
zip: '234567',
// Код страны
mobileCode: '+375',
// Номер телефона
mobile: '%rn(70-99)%%rn(1111111-9999999)%',
// Переменные шаблона
// Переменные шаблона перезаписывают глобальные переменные с таким же именем
// Значения присваиваются до заполнения каких-либо полей, соответственно, переменные можно использовать в любом поле (%город%)
variables: {
'фио': '[Pupkin|Vasili{y|i}|Vasil{i|}evich| ]',
'город': '{Gomel|Homel|Homiel|Homyel}',
},
// true - Выбрать адресом по умолчанию
// false - Не выбирать
default: true,
// true - Нажать кнопку сохранения адреса
// false - Не нажимать
save: false
},
// --- Конец шаблона ---
// --- Начало шаблона ---
'RU': {
name: '{[Пупкин|Василий|Васильевич| ]|[Иванов|Иван|Иванович| ]}',
country: 'Russian Federation',
province: 'Moskva g',
city: 'Moskva g',
address: 'ул{ица|.|} Арбузная{,| -|$dlm} д{.|ом|} 7%dlm% кв{.|артира|} 77 %rs(6-12,a)%',
address2: '%rs(6-12,a)%',
zip: '123456',
mobileCode: '+7',
mobile: '%rn(1111111111-9999999999)%',
// Информация для таможни (не обязательно)
extra: [
{
selector: '#lastName',
value: 'Пупкин'
},
{
selector: '#firstName',
value: 'Василий'
},
{
selector: '#middleName',
value: 'Васильевич'
},
{
selector: '#passportNo',
value: '%rn(1111111111-9999999999)%'
},
{
selector: '#passportNoDate input',
value: '%rn(1-28,2)%-%rn(1-12,2)%-%rn(2000-2010)%'
},
{
selector: '#passportOrganization',
value: 'Кем-то'
},
{
selector: '#birthday input',
value: '%rn(1-28,2)%-%rn(1-12,2)%-%rn(1980-1990)%'
},
],
default: true,
save: false
},
// --- Конец шаблона ---
// --- Начало шаблона ---
'UA': {
name: '%фио%',
country: 'Ukraine',
province: 'Kyiv',
city: 'Kyiv',
address: 'ul{.|ica|itsa|} Arbuzna{y|j}a, d{.|om} 11, kv{.|ar.|art.|artira} 99',
address2: '%rs(4-6,a)%',
zip: '%rn(11111-88888)%',
mobileCode: '+380',
mobile: '%rn(1111111111-9999999999)%',
default: false,
save: false
},
// --- Конец шаблона ---
// --- Начало шаблона ---
'US (rnd)': {
name: '%rw(4-8,1)% %rw(6-10,1)%',
country: 'United States',
province: '*',
city: {
// Значение для текстового поля
text: '%rw(6-12,1)%',
// Значение для выпадающего списка
select: '*'
},
address: '%rs(10-16,a0)%',
address2: '%rs(6-12,a)%',
zip: '%rn(11111-99999)%',
mobile: '%rn(1111111111-9999999999)%',
default: false,
save: false
},
// --- Конец шаблона ---
// --- Начало шаблона ---
// Пример шаблона, который берёт данные с worldnamegenerator.com
'US (wng)': async () => {
const response = await httpRequest({
url: 'https://www.worldnamegenerator.com/usa_address_generator'
});
if (response.status !== 200) {
return false;
}
const html = document.createElement('html');
html.innerHTML = response.responseText;
const items = html.querySelectorAll('.common-table tr td:nth-child(2)');
if (!items.length) {
return false;
}
const tmp = {
name: items[0].textContent.trim().replace(/\u00a0/g, ' ').replace(/\s{2,}/g, ' '),
country: 'United States',
province: items[9].textContent.trim(),
city: items[7].textContent.trim(),
address: items[6].textContent.trim().replace(/\u00a0/g, ' ').replace(/\s{2,}/g, ' '),
zip: items[10].textContent.trim(),
mobile: items[12].textContent.trim().replace(/\D/g, '')
};
html.remove();
return tmp;
},
// --- Конец шаблона ---
// --- Начало шаблона ---
// Шаблон для aliexpress.ru
'Россия': {
name: 'Иванов Иван Иванович',
country: ['Россия', 'Russian Federation', 'Rossiya'],
province: 'Москва г',
city: 'Москва г',
address: 'ул. Арбузная, дом 7, квартира 77',
zip: '123456',
mobileCode: '+7',
mobile: '%rn(1111111111-9999999999)%',
default: true,
save: false,
// true - шаблон будет отображаться на aliexpress.ru, иначе только на aliexpress.com
ru: true
},
// --- Конец шаблона ---
// --- Начало шаблона ---
// Шаблон для aliexpress.ru
'Беларусь': {
name: '%фио%',
country: ['Беларусь', 'Belarus'],
province: 'Minsk',
city: 'Minsk',
address: 'ul. Arbuznaya, dom 9, kvartira 99',
zip: '234567',
mobileCode: '+375',
mobile: '%rn(111111111-999999999)%',
default: true,
save: false,
ru: true
},
// --- Конец шаблона ---
// --- Начало шаблона ---
// Шаблон для aliexpress.ru
'Узбекистан': {
name: '%фио%',
country: ['Узбекистан', 'Uzbekistan', 'O\'zbekiston'],
province: 'Toshkent',
city: 'Bektemir Tumani',
address: 'ul. Arbuznaya, dom 9, kvartira 99',
zip: '100033',
mobileCode: '+998',
mobile: '%rn(111111111-999999999)%',
default: true,
save: false,
ru: true
},
// --- Конец шаблона ---
};
// ================================================================================================================================================================
// Глобальные переменные
// ================================================================================================================================================================
// Переменные, которые доступны в любом шаблоне
const variables = {
// 'имя': 'значение'
'фио': '[Ivanov|Ivan|Ivanovich| ]',
};
// ================================================================================================================================================================
// Настройки
// ================================================================================================================================================================
const config = {
// Отключение появления новой формы адреса на странице оформления
disableNewAddressForm: true,
// Режим отладки
debug: false
};
// ================================================================================================================================================================
const httpRequest = (params) => {
return new Promise((resolve) => {
params.timeout = 30000;
params.onload = resolve;
params.onerror = resolve;
params.ontimeout = resolve;
params.onabort = resolve;
if (!params.method) {
params.method = 'GET';
}
const func = typeof GM.xmlHttpRequest !== 'undefined' ? GM.xmlHttpRequest : GM_xmlhttpRequest;
func(params);
});
};
const log = {
debug(...args) {
if (config && config.debug) {
const ar = Array.prototype.slice.call(args, 0);
ar.unshift('AAR ::');
console.log(...ar);
}
}
};
const sleep = async (ms) => {
return new Promise((resolve) => {
setTimeout(resolve, ms);
});
};
const waitForFetch = (url, startTimeout = 0, endTimeout = 0, max = 0) => {
log.debug('waitForFetch : url =', url, ', startTimeout =', startTimeout, ', endTimeout =', endTimeout, ', max =', max);
if (!Array.isArray(url)) {
url = [url];
}
const fetchOrig = unsafeWindow.fetch;
const promise = new Promise((resolve) => {
let startTimer;
let endTimer;
let requestCount = 0;
let responseCount = 0;
let urlIndex = 0;
const resolveAndRestore = () => {
log.debug('waitForFetch : resolveAndRestore :', url);
resolve();
unsafeWindow.fetch = fetchOrig;
};
unsafeWindow.fetch = async (resource, options) => {
log.debug('waitForFetch : request :', resource);
if (!resource.includes(url[urlIndex])) {
return fetchOrig(resource, options);
}
requestCount++;
urlIndex++;
if (startTimer) {
clearTimeout(startTimer);
startTimer = null;
}
if (endTimer) {
clearTimeout(endTimer);
endTimer = null;
}
log.debug('waitForFetch : request match :', resource);
return fetchOrig(resource, options).then((response) => {
log.debug('waitForFetch : response :', response);
responseCount++;
if (!endTimeout || (max && responseCount >= max) || (requestCount == responseCount && urlIndex >= url.length)) {
log.debug('waitForFetch : finish (if)');
resolveAndRestore();
} else if (endTimeout && requestCount == responseCount) {
endTimer = setTimeout(() => {
log.debug('waitForFetch : finish (endTimeout)');
resolveAndRestore();
}, endTimeout);
}
return response;
}).catch(() => {
log.debug('waitForFetch : request error :', url);
resolveAndRestore();
});
};
if (startTimeout) {
startTimer = setTimeout(() => {
log.debug('waitForFetch : finish (startTimeout)');
resolveAndRestore();
}, startTimeout);
}
});
promise.catch(() => {
log.debug('waitForFetch : catch :', url);
unsafeWindow.fetch = fetchOrig;
});
return promise;
}
const waitForJsonp = (url, startTimeout = 0, endTimeout = 0, max = 0) => {
log.debug('waitForJsonp : url =', url, ', startTimeout =', startTimeout, ', endTimeout =', endTimeout, ', max =', max);
return new Promise((resolve) => {
let startTimer;
let endTimer;
let callbacksCount = 0;
let matchCount = 0;
const results = [];
const observer = new MutationObserver((mutations) => {
for (const m of mutations) {
for (const n of m.addedNodes) {
if (!n.src) {
continue;
}
if (Array.isArray(url)) {
let found = false;
for (const u of url) {
if (n.src.includes(u)) {
found = true;
break;
}
}
if (!found) {
continue;
}
} else if (!n.src.includes(url)) {
continue;
}
const match = n.src.match(/[?|&](?:callback|cb)=([a-zA-Z0-9_\-]+)/);
if (match) {
matchCount++;
log.debug('waitForJsonp : match : matchCount =', matchCount, ', url =', url, ', match =', match);
if (startTimer) {
clearTimeout(startTimer);
startTimer = null;
}
if (endTimer) {
clearTimeout(endTimer);
endTimer = null;
}
const originalCallback = unsafeWindow[match[1]];
unsafeWindow[match[1]] = (obj) => {
callbacksCount++;
log.debug('waitForJsonp : callback : matchCount =', matchCount, ', callbacksCount =', callbacksCount, ', obj =', obj);
originalCallback(obj);
results.push(obj);
if (!endTimeout || (max && callbacksCount >= max)) {
log.debug('waitForJsonp : finish (if)');
observer.disconnect();
return resolve(results);
}
if (matchCount === callbacksCount) {
endTimer = setTimeout(() => {
log.debug('waitForJsonp : finish (endTimeout)');
observer.disconnect();
resolve(results);
}, endTimeout);
}
};
}
}
}
});
observer.observe(document.documentElement, {
childList: true,
subtree: true
});
if (startTimeout) {
startTimer = setTimeout(() => {
log.debug('waitForJsonp : finish (startTimeout)');
observer.disconnect();
resolve(results);
}, startTimeout);
}
});
};
const waitForElement = (selectors, waitForExistence = true, visible = false, parent = document, interval = 250, seconds = 0) => {
log.debug('waitForElement : selectors =', selectors, ', waitForExistence =', waitForExistence, ', visible =', visible, ', parent =', parent, ', interval =', interval, ', seconds =', seconds);
const isVisible = (e) => {
return !!(e.offsetWidth || e.offsetHeight || e.getClientRects().length);
};
return new Promise((resolve) => {
if (!Array.isArray(selectors)) {
selectors = [selectors];
}
seconds = seconds * 1000;
const startTime = Date.now();
const check = () => {
let found = true;
for (const s of selectors) {
const el = parent.querySelector(s);
if ((waitForExistence && (!el || (visible && !isVisible(el)))) || (!waitForExistence && el)) {
found = false;
break;
}
}
if (found) {
return resolve(true);
}
if (seconds > 0 && Date.now() - startTime > seconds) {
return resolve(false);
}
setTimeout(check, interval);
};
check();
});
};
function randomizeText(text, vars = {}, index = 0, type = 0) {
// https://stackoverflow.com/a/52789490
const formatDate = (date, patternStr) => {
if (!patternStr) {
patternStr = 'dd.MM.yyyy';
}
const monthNames = [
'January', 'February', 'March', 'April', 'May', 'June', 'July',
'August', 'September', 'October', 'November', 'December'
];
const dayOfWeekNames = [
'Sunday', 'Monday', 'Tuesday',
'Wednesday', 'Thursday', 'Friday', 'Saturday'
];
const day = date.getDate(),
month = date.getMonth(),
year = date.getFullYear(),
hour = date.getHours(),
minute = date.getMinutes(),
second = date.getSeconds(),
miliseconds = date.getMilliseconds(),
h = hour % 12,
hh = twoDigitPad(h),
HH = twoDigitPad(hour),
mm = twoDigitPad(minute),
ss = twoDigitPad(second),
aaa = hour < 12 ? 'AM' : 'PM',
EEEE = dayOfWeekNames[date.getDay()],
EEE = EEEE.substr(0, 3),
dd = twoDigitPad(day),
M = month + 1,
MM = twoDigitPad(M),
MMMM = monthNames[month],
MMM = MMMM.substr(0, 3),
yyyy = year + '',
yy = yyyy.substr(2, 2);
patternStr = patternStr
.replace('hh', hh).replace('h', h)
.replace('HH', HH).replace('H', hour)
.replace('mm', mm).replace('m', minute)
.replace('ss', ss).replace('s', second)
.replace('S', miliseconds)
.replace('dd', dd).replace('d', day)
.replace('EEEE', EEEE).replace('EEE', EEE)
.replace('yyyy', yyyy)
.replace('yy', yy)
.replace('aaa', aaa);
if (patternStr.indexOf('MMM') > -1) {
patternStr = patternStr
.replace('MMMM', MMMM)
.replace('MMM', MMM);
} else {
patternStr = patternStr
.replace('MM', MM)
.replace('M', M);
}
return patternStr;
};
const twoDigitPad = (num) => {
return num < 10 ? '0' + num : num;
};
const randomString = (length, chars = 'zZ0') => {
let characters = '';
if (chars.search(/[a-z]/) !== -1) {
characters += 'abcdefghijklmnopqrstuvwxyz';
}
if (chars.search(/[A-Z]/) !== -1) {
characters += 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
}
if (chars.search(/[а-яё]/) !== -1) {
characters += 'абвгдеёжзийклмнопрстуфхцчшщъыьэюя';
}
if (chars.search(/[А-ЯЁ]/) !== -1) {
characters += 'АБВГДЕЁЖЗИЙКЛМНОПРСТУФХЦЧШЩЪЫЬЭЮЯ';
}
if (chars.search(/[0-9]/) !== -1) {
characters += '0123456789';
}
const charactersLength = characters.length;
let result = '';
for (let i = 0; i < length; i++) {
result += characters.charAt(Math.floor(Math.random() * charactersLength));
}
return result;
};
// http://jsfiddle.net/amando96/XjUJM/
const randomWord = (length, capitalize = false) => {
const consonants = 'bcdfghjlmnpqrstv'.split('');
const vowels = 'aeiou'.split('');
let word = '';
for (let i = 0; i < length / 2; i++) {
const randConsonant = consonants[randomRange(0, consonants.length - 1)];
const randVowel = vowels[randomRange(0, vowels.length - 1)];
word += capitalize && i === 0 ? randConsonant.toUpperCase() : randConsonant;
word += i * 2 < length - 1 ? randVowel : '';
}
return word;
};
const randomRange = (min, max) => {
return Math.floor(Math.random() * (max + 1 - min)) + min;
};
// https://medium.com/@nitinpatel_20236/how-to-shuffle-correctly-shuffle-an-array-in-javascript-15ea3f84bfb
const arrayShuffle = (array) => {
for (let i = array.length - 1; i > 0; i--) {
const j = Math.floor(Math.random() * (i + 1));
const temp = array[i];
array[i] = array[j];
array[j] = temp;
}
return array;
};
const funcs = {
rs(params) {
if (!params || !params.length) {
return '';
}
let len;
let chars = params.length > 1 ? params[1] : 'aA0';
const spl = params[0].split('-');
if (spl.length > 1) {
const min = parseInt(spl[0].trim());
const max = parseInt(spl[1].trim());
if (!min || !max) {
return '';
}
len = randomRange(min, max);
} else {
len = parseInt(spl[0]);
}
if (!len) {
return '';
}
return randomString(len, chars);
},
rw(params) {
if (!params || !params.length) {
return '';
}
let len;
let cap = (params.length > 1 && (params[1] === '1' || params[1].toLowerCase() === 'true')) ? true : false;
const spl = params[0].split('-');
if (spl.length > 1) {
const min = parseInt(spl[0].trim());
const max = parseInt(spl[1].trim());
if (!min || !max) {
return '';
}
len = randomRange(min, max);
} else {
len = parseInt(spl[0]);
}
if (!len) {
return '';
}
return randomWord(len, cap);
},
rn(params) {
if (!params || !params.length) {
return '';
}
const spl = params[0].split('-');
if (spl.length < 2) {
return '';
}
const min = parseInt(spl[0].trim());
const max = parseInt(spl[1].trim());
if (min === NaN || max === NaN) {
return '';
}
let rnd = `${randomRange(min, max)}`;
if (params.length > 1 && params[1].search(/^\d+$/) > -1) {
rnd = rnd.padStart(parseInt(params[1]), '0');
}
return rnd;
},
dt(params) {
if (!params || !params.length) {
return formatDate(new Date());
}
return formatDate(new Date(), params[0]);
}
};
const sub = index > 0;
const arr = [];
let cur = '';
while (index < text.length) {
if (text[index] == '{' || text[index] == '[') {
const res = randomizeText(text, vars, index + 1, text[index] == '{' ? 0 : 1);
cur += res.text;
index = res.index;
} else {
if (text[index] == '|') {
arr.push(cur);
cur = '';
} else if (sub && ((type == 0 && text[index] == '}') || (type == 1 && text[index] == ']'))) {
arr.push(cur);
index++;
break;
} else {
cur += text[index];
}
index++;
}
}
if (sub) {
let result;
let value;
const varName = arr[arr.length - 1].match(/\$(.+)/);
if (varName) {
arr[arr.length - 1] = arr[arr.length - 1].replace(/\$(.+)/, '');
}
if (type == 0) {
if (arr.length > 1) {
value = arr[randomRange(0, arr.length - 1)];
result = {text: value, index: index};
} else if (arr.length == 1) {
if (randomRange(1, 100) > 50) {
value = arr[0];
result = {text: arr[0], index: index};
} else {
value = '';
result = {text: value, index: index};
}
}
} else if (type == 1) {
const sep = arr.pop();
arrayShuffle(arr);
value = arr.join(sep);
result = {text: value, index: index};
}
if (varName) {
vars[varName[1]] = value;
}
return result;
}
cur = cur.replace(/\%([a-zA-Z_]+?)\((.+?)\)(?:\$(.+?))?\%/g, (m, func, params, varName) => {
if (funcs[func]) {
let paramsArray = [];
if (params) {
paramsArray = params.trim().split(',').map((p) => p.trim());
}
const res = funcs[func](paramsArray);
if (varName) {
vars[varName] = res;
}
return res;
}
});
const keys = Object.keys(vars);
for (const k of keys) {
cur = cur.replace(new RegExp(`\\%${k}\\%`, 'g'), vars[k]);
}
return cur;
};
const refreshInput = (input) => {
if (typeof input === 'string') {
input = document.querySelector(input);
}
if (!input) {
return;
}
let props = Object.keys(input).filter((i) => i.indexOf('__reactEventHandlers') >= 0);
if (!props.length) {
props = Object.keys(input).filter((i) => i.indexOf('__reactProps') >= 0);
}
if (props.length) {
if (input[props[0]].onChange) {
input[props[0]].onChange({target: {value: input.value}});
}
if (input[props[0]].onKeyDown) {
input[props[0]].onKeyDown({target: {value: input.value}});
}
if (input[props[0]].onBlur) {
input[props[0]].onBlur({});
}
}
input.dispatchEvent(new Event('change', {bubbles: true}));
input.dispatchEvent(new Event('keyup', {bubbles: true}));
input.dispatchEvent(new Event('keydown', {bubbles: true}));
input.dispatchEvent(new Event('keypress', {bubbles: true}));
input.dispatchEvent(new Event('input', {bubbles: true}));
input.dispatchEvent(new Event('blur', {bubbles: true}));
};
const setInput = (input, value) => {
if (typeof input === 'string') {
input = document.querySelector(input);
}
if (!input) {
return;
}
input.value = value;
refreshInput(input);
};
const select = async (combo, currentItem, waitAfterClick, items, itemToSelect) => {
const compareItem = (item) => {
item = item.trim().toLowerCase();
if (Array.isArray(itemToSelect)) {
for (const i of itemToSelect) {
if (i.toLowerCase() === item) {
return i;
}
}
} else if (itemToSelect.toLowerCase() === item) {
return itemToSelect;
}
return false;
};
log.debug('select : combo =', combo, ', currentItem =', currentItem, ', waitAfterClick =', waitAfterClick, ', items =', items, ', itemToSelect =', itemToSelect);
if (combo) {
const comboElement = typeof combo === 'string' ? document.querySelector(combo) : combo;
if (!comboElement) {
log.debug('select : combo element not found');
return null;
}
const currentItemElement = currentItem.startsWith('>') ? comboElement.querySelector(currentItem.replace(/^>\s*/, '')) : document.querySelector(currentItem);
let currentItemValue;
if (currentItemElement) {
if (currentItemElement.nodeName === 'INPUT') {
currentItemValue = currentItemElement.value;
} else {
currentItemValue = currentItemElement.innerText.trim();
}
}
if (currentItemValue && itemToSelect !== '*' && compareItem(currentItemValue)) {
log.debug('select : already selected');
return false;
}
log.debug('select : clicking combo');
comboElement.click();
log.debug(`select : waiting for "${waitAfterClick}"`);
await waitForElement(waitAfterClick.replace(/^>\s*/, ''), true, true, waitAfterClick.startsWith('>') ? comboElement : document);
}
const itemElements = items.startsWith('>') ? comboElement.querySelectorAll(items.replace(/^>\s*/, '')) : document.querySelectorAll(items);
if (!itemElements.length) {
log.debug('select : items not found');
return null;
}
if (itemToSelect === '*') {
log.debug('select : clicking random item');
const idx = Math.floor(Math.random() * itemElements.length);
itemElements[idx].click();
return itemElements[idx];
}
for (const i of itemElements) {
const match = compareItem(i.innerText)
if (match) {
log.debug(`select : clicking : ${match}`);
i.click();
return i;
}
}
log.debug('select : item not found');
};
const fill = (template) => {
const fillAfterCountry = async () => {
if (template.province) {
if (isAliexpressRu) {
if (document.querySelector('input[name="provinceSearch"]')) {
log.debug('fill : province is an input with suggestions');
const el = document.querySelector('input[name="provinceSearch"]');
let province = typeof template.province === 'object' && !Array.isArray(template.province) ? template.province.select : template.province;
if (Array.isArray(province)) {
province = province.map((v) => randomizeText(v, vars));
} else {
province = randomizeText(province, vars);
}
if ((Array.isArray(province) && province.includes(el.value)) || (!Array.isArray(province) && province === el.value)) {
log.debug('fill : province already selected');
} else {
log.debug('fill : province :', province);
const provinceItemElement = await select(
'div[class*="AddressFormWidget_Section__container"] > div[class*="AddressFormWidget_Select__root"]:nth-child(2) div[id*="toggle-button"]',
'input[name="provinceSearch"]',
'div[class*="AddressFormWidget_Section__container"] > div[class*="AddressFormWidget_Select__root"]:nth-child(2) ul[class*="AddressFormWidget_SelectComponent__dropdownMenuOpen"]',
'div[class*="AddressFormWidget_Section__container"] > div[class*="AddressFormWidget_Select__root"]:nth-child(2) ul[class*="AddressFormWidget_SelectComponent__dropdownMenuOpen"] li',
province
);
if (provinceItemElement === null) {
log.debug('fill : province select error');
return;
}
}
} else {
log.debug('fill : province is an input');
let province = typeof template.province === 'object' && !Array.isArray(template.province) ? template.province.text : template.province;
province = randomizeText(Array.isArray(province) ? province[0] : province, vars);
log.debug('fill : province :', province);
setInput('input[name="province"]', province);
}
} else {
if (document.querySelector(`.input-default.country .selector-item:not(:last-child) .next-select`)) {
log.debug('fill : province is a combobox');
await waitForElement(`.input-default.country .selector-item:not(:last-child) .next-select .next-select-inner:not(.next-disabled)`);
let province = typeof template.province === 'object' && !Array.isArray(template.province) ? template.province.select : template.province;
if (Array.isArray(province)) {
province = province.map((v) => randomizeText(v, vars));
} else {
province = randomizeText(province, vars);
}
log.debug('fill : province :', province);
const provinceItemElement = await select(
`.input-default.country .selector-item:not(:last-child) .next-select`,
'> em',
`.input-default.country .selector-item:not(:last-child) .next-select-popup-wrap .dropdown-content .next-menu-item`,
`.input-default.country .selector-item:not(:last-child) .next-select-popup-wrap .dropdown-content .next-menu-item`,
province
);
if (provinceItemElement === null) {
log.debug('fill : province select error');
return;
}
await sleep(300);
await waitForElement(`.input-default.country .selector-item:last-child .next-input:not(.next-disabled)`);
} else {
log.debug('fill : province is not a combobox');
await waitForElement(`.input-default.country .selector-item:not(:last-child) .next-input:not(.next-disabled)`);
let province = typeof template.province === 'object' && !Array.isArray(template.province) ? template.province.text : template.province;
province = randomizeText(Array.isArray(province) ? province[0] : province, vars);
log.debug('fill : province :', province);
setInput(`.input-default.country .selector-item:not(:last-child) .next-input input`, province);
}
}
} else if (template.country) {
const provinceInputElement = document.querySelector(`.input-default.country .selector-item:not(:last-child) .next-input input`);
if (provinceInputElement) {
if (currentProvince) {
setInput(provinceInputElement, '');
setInput(provinceInputElement, currentProvince);
}
}
}
if (template.city) {
if (isAliexpressRu) {
if (document.querySelector('input[name="citySearch"]')) {
log.debug('fill : city is an input with suggestions');
const el = document.querySelector('input[name="citySearch"]');
let city = typeof template.city === 'object' && !Array.isArray(template.city) ? template.city.select : template.city;
if (Array.isArray(city)) {
city = city.map((v) => randomizeText(v, vars));
} else {
city = randomizeText(city, vars);
}
if ((Array.isArray(city) && city.includes(el.value)) || (!Array.isArray(city) && city === el.value)) {
log.debug('fill : city already selected');
} else {
log.debug('fill : city :', city);
await Promise.all([
await waitForFetch('city-suggests', 1000, 1000, 1),
setInput('input[name="citySearch"]', city)
]);
await sleep(500);
document.querySelector('div[class*="AddressFormWidget_Section__container"] > div[class*="AddressFormWidget_Select__root"]:nth-child(3) div[id*="toggle-button"]').click();
await sleep(500);
const cityItemElement = await select(
'', '', '',
'div[class*="AddressFormWidget_Section__container"] > div[class*="AddressFormWidget_Select__root"]:nth-child(3) ul[class*="AddressFormWidget_SelectComponent__dropdownMenuOpen"] li',
city
);
if (cityItemElement === null) {
log.debug('fill : city select error');
return;
}
}
} else {
log.debug('fill : city is an input');
let city = typeof template.city === 'object' && !Array.isArray(template.city) ? template.city.text : template.city;
city = randomizeText(Array.isArray(city) ? city[0] : city, vars);
log.debug('fill : city :', city);
setInput('input[name="city"]', city);
}
} else {
if (document.querySelector(`.input-default.country .selector-item:last-child .next-select`)) {
log.debug('fill : city is a combobox');
let city = typeof template.city === 'object' && !Array.isArray(template.city) ? template.city.select : template.city;
if (Array.isArray(city)) {
city = city.map((v) => randomizeText(v, vars));
} else {
city = randomizeText(city, vars);
}
log.debug('fill : city :', city);
await waitForElement(`.input-default.country .selector-item:last-child .next-select .next-select-inner:not(.next-disabled)`);
const citySearchElement = document.querySelector(`.input-default.country .selector-item:last-child .next-search input`)
if (citySearchElement) {
setInput(citySearchElement, '');
}
const cityItemElement = await select(
`.input-default.country .selector-item:last-child .next-select`,
'> em',
`.input-default.country .selector-item:last-child .next-select-popup-wrap .dropdown-content .next-menu-item:nth-child(2)`,
`.input-default.country .selector-item:last-child .next-select-popup-wrap .dropdown-content .next-menu-item`,
city
);
if (cityItemElement === null) {
log.debug('fill : city select error');
return;
}
} else {
log.debug('fill : city is not a combobox');
await waitForElement(`.input-default.country .selector-item:last-child .next-input:not(.next-disabled)`);
let city = typeof template.city === 'object' && !Array.isArray(template.city) ? template.city.text : template.city;
city = randomizeText(Array.isArray(city) ? city[0] : city, vars);
log.debug('fill : city :', city);
setInput(`.input-default.country .selector-item:last-child .next-input input`, city);
}
}
} else if (template.country) {
const cityInputElement = document.querySelector(`.input-default.country .selector-item:last-child .next-input input`);
if (cityInputElement) {
if (currentCity) {
setInput(cityInputElement, '');
setInput(cityInputElement, currentCity);
}
}
}
if (template.name) {
log.debug('fill : name');
setInput('input[name="contactPerson"], #contactPerson', randomizeText(template.name, vars));
} else if (template.country) {
refreshInput('input[name="contactPerson"], #contactPerson');
}
if (template.address) {
log.debug('fill : address');
setInput('input[name="address"], input[name="addressSearch"], #address', randomizeText(template.address, vars));
} else if (template.country) {
refreshInput('input[name="address"], input[name="addressSearch"], #address');
}
if (template.address2) {
log.debug('fill : address2');
setInput('#address2', randomizeText(template.address2, vars));
} else if (template.country) {
refreshInput('#address2');
}
if (template.zip) {
const zip = randomizeText(template.zip, vars);
if (isAliexpressRu) {
if (document.querySelector('input[name="zipSearch"]')) {
log.debug('fill : zip is an input with suggestions');
log.debug('fill : zip :', zip);
const zipItemElement = await select(
'div[class*="AddressFormWidget_Section__container"] div[class*="AddressFormWidget_Form__zipInput"] div[id*="toggle-button"]',
'input[name="zipSearch"]',
'div[class*="AddressFormWidget_Section__container"] div[class*="AddressFormWidget_Form__zipInput"] ul[class*="AddressFormWidget_SelectComponent__dropdownMenuOpen"]',
'div[class*="AddressFormWidget_Section__container"] div[class*="AddressFormWidget_Form__zipInput"] ul[class*="AddressFormWidget_SelectComponent__dropdownMenuOpen"] li',
zip
);
if (zipItemElement === null) {
log.debug('fill : zip select error');
return;
}
} else {
log.debug('fill : zip is an input');
setInput('input[name="zip"]', zip);
}
} else {
setInput('#zip', zip);
}
} else if (template.country) {
refreshInput('input[name="zip"], input[name="zipSearch"], #zip');
}
if (template.mobileCode) {
log.debug('fill : mobileCode');
setInput('input[name="phoneCountry"], #phoneCountry', randomizeText(template.mobileCode, vars));
} else if (template.country) {
refreshInput('input[name="phoneCountry"], #phoneCountry');
}
if (template.mobile) {
log.debug('fill : mobile');
setInput('input[name="mobileNo"], input[name="phoneNumber"], #mobileNo', randomizeText(template.mobile, vars));
} else if (template.country) {
refreshInput('input[name="mobileNo"], input[name="phoneNumber"]');
}
if (Array.isArray(template.extra)) {
log.debug('fill : extra');
for (const e of template.extra) {
if (e.selector && typeof e.value !== 'undefined') {
const element = document.querySelector(e.selector);
if (element) {
if (element.querySelector('.next-select')) {
log.debug(`fill : extra "${e.selector}" is a combobox`);
await select(
`${e.selector} .next-select`,
'> em',
`${e.selector} .next-select-popup-wrap .dropdown-content`,
`${e.selector} .next-select-popup-wrap .dropdown-content .next-menu-item`,
randomizeText(e.value, vars)
);
} if (element.type === 'checkbox' || element.querySelector('input[type="checkbox"]')) {
log.debug(`fill : extra "${e.selector}" is a checkbox`);
const input = element.type === 'checkbox' ? element : element.querySelector('input[type="checkbox"]');
if (input.checked != !!e.value) {
input.click();
}
} else {
log.debug(`fill : extra "${e.selector}" is probably a text field`);
setInput(element.nodeName === 'INPUT' ? element : element.querySelector('input'), randomizeText(e.value, vars));
}
}
}
}
}
let el = document.querySelector('.ship-info .term-wrap .next-checkbox input');
if (el && !el.checked) {
log.debug('fill : clicking terms');
el.click();
}
if (typeof template.default === 'boolean') {
el = document.querySelector('.ship-info > div:not(.term-wrap) .next-checkbox input, label[class*="AddressList_AddressForm__checkbox"] input');
if (el && el.checked != template.default) {
log.debug('fill : clicking default');
el.click();
}
}
if (template.save) {
await sleep(200);
el = document.querySelector('.ship-info .address-save .next-btn-primary, div[class*="CheckoutShippingMethod_AddressForm__controls"] button[type="submit"], div[class*="AddressList_Header__header"] button[type="submit"]');
if (el) {
log.debug('fill : clicking save');
el.click();
}
}
};
const vars = {};
let currentProvince;
let currentCity;
if (typeof variables === 'object') {
log.debug('fill : global variables');
for (const v in variables) {
vars[v] = randomizeText(variables[v], vars);
}
}
if (typeof template.variables === 'object') {
log.debug('fill : template variables');
for (const v in template.variables) {
vars[v] = randomizeText(template.variables[v], vars);
}
}
return new Promise(async (resolve) => {
if (typeof template === 'function') {
let funcRes = template();
if (funcRes instanceof Promise) {
funcRes = await funcRes;
}
if (typeof funcRes !== 'object') {
return resolve();
}
template = funcRes;
}
if (!template.country) {
log.debug('fill : country is not defined');
await fillAfterCountry();
return resolve();
}
let currentCountry;
let countryToSelect;
if (isAliexpressRu) {
currentCountry = document.querySelector('input[name="countrySearch"]').value;
} else {
currentCountry = document.querySelector('div:not([style*="display: none"]) > .list_country .next-select .country-name, div:not([style*="display: none"]) > .country_for_select .next-select .country-name').textContent.trim();
}
if (Array.isArray(template.country)) {
countryToSelect = template.country.map((v) => randomizeText(v, vars));
} else {
countryToSelect = randomizeText(template.country, vars);
}
if ((Array.isArray(countryToSelect) && countryToSelect.includes(currentCountry)) ||
(!Array.isArray(countryToSelect) && countryToSelect === currentCountry)
) {
log.debug('fill : country already selected :', countryToSelect);
await fillAfterCountry();
return resolve();
}
const provinceInputElement = document.querySelector(`.input-default.country .selector-item:not(:last-child) .next-input input`);
if (provinceInputElement) {
currentProvince = provinceInputElement.value;
}
const cityInputElement = document.querySelector(`.input-default.country .selector-item:last-child .next-input input`);
if (cityInputElement) {
currentCity = cityInputElement.value;
}
if (isAliexpressRu) {
waitForFetch('province-suggests', 1000, 500, 1).then(async () => {
await fillAfterCountry();
return resolve();
});
} else {
waitForJsonp(['AjaxQueryCountries', 'AjaxQueryAddress'], 1000, 500).then(async () => {
await fillAfterCountry();
return resolve();
});
}
log.debug('fill : selecting country : country =', countryToSelect);
let countryItemElement;
if (isAliexpressRu) {
countryItemElement = await select(
'div[class*="AddressFormWidget_SelectComponent__select"] div[id*="toggle-button"]',
'input[name="countrySearch"]',
'div[class*="AddressFormWidget_SelectComponent__select"] ul[class*="AddressFormWidget_SelectComponent__dropdownMenuOpen"]',
'div[class*="AddressFormWidget_SelectComponent__select"] ul[class*="AddressFormWidget_SelectComponent__dropdownMenuOpen"] li',
countryToSelect
);
} else {
countryItemElement = await select(
'div:not([style*="display: none"]) > .list_country .next-select, div:not([style*="display: none"]) > .country_for_select .next-select',
'div:not([style*="display: none"]) > .list_country .next-select .country-name, div:not([style*="display: none"]) > .country_for_select .next-select .country-name',
'div:not([style*="display: none"]) > .list_country .next-select-popup-wrap .dropdown-content, div:not([style*="display: none"]) > .country_for_select .next-select-popup-wrap .dropdown-content',
'div:not([style*="display: none"]) > .list_country .next-select-popup-wrap .dropdown-content .country-name, div:not([style*="display: none"]) > .country_for_select .next-select-popup-wrap .dropdown-content .country-name',
countryToSelect
);
}
if (countryItemElement === null) {
log.debug('fill : country select error');
return resolve();
}
});
}
const init = () => {
log.debug('init');
const enableButtons = (value) => {
if (value) {
buttonsContainer.classList.remove('disabled');
spinner.style.display = 'none';
} else {
buttonsContainer.classList.add('disabled');
spinner.style.display = 'block';
}
};
const mainContainer = document.createElement('div');
mainContainer.id = 'addr-rnd';
mainContainer.innerHTML = `
<div class="addr-rnd-buttons"></div>
<div class="addr-rnd-spinner">
<div class="addr-rnd-spinner1 sk-child"></div>
<div class="addr-rnd-spinner2 sk-child"></div>
<div class="addr-rnd-spinner3 sk-child"></div>
<div class="addr-rnd-spinner4 sk-child"></div>
<div class="addr-rnd-spinner5 sk-child"></div>
<div class="addr-rnd-spinner6 sk-child"></div>
<div class="addr-rnd-spinner7 sk-child"></div>
<div class="addr-rnd-spinner8 sk-child"></div>
<div class="addr-rnd-spinner9 sk-child"></div>
<div class="addr-rnd-spinner10 sk-child"></div>
<div class="addr-rnd-spinner11 sk-child"></div>
<div class="addr-rnd-spinner12 sk-child"></div>
</div>
`;
const style = document.createElement('style');
style.innerHTML = `
#addr-rnd {
display: flex;
position: relative;
border-bottom: 1px solid #ccc;
margin-bottom: 10px;
}
#addr-rnd .addr-rnd-buttons {
flex-grow: 1;
}
#addr-rnd .addr-rnd-buttons .addr-rnd-btn {
margin: 0 4px 4px 0;
padding: 6px 10px;
height: 28px;
line-height: 14px;
color: #000;
background-color: #f2f2f2;
border: 1px solid #cacaca;
border-radius: 4px;
font-size: 12px;
position: relative;
display: inline-block;
box-shadow: none;
text-decoration: none;
text-align: center;
text-transform: none;
white-space: nowrap;
vertical-align: middle;
user-select: none;
transition: all .3s ease-out;
cursor: pointer;
}
#addr-rnd .addr-rnd-buttons .addr-rnd-btn:hover {
background-color: #f9f9f9;
}
#addr-rnd .addr-rnd-buttons.disabled {
pointer-events: none;
}
#addr-rnd .addr-rnd-buttons.disabled .addr-rnd-btn {
opacity: 0.5;
}
/* https://tobiasahlin.com/spinkit/ */
#addr-rnd .addr-rnd-spinner {
display: none;
width: 26px;
height: 26px;
position: relative;
}
#addr-rnd .addr-rnd-spinner .sk-child {
width: 100%;
height: 100%;
position: absolute;
left: 0;
top: 0;
}
#addr-rnd .addr-rnd-spinner .sk-child:before {
content: '';
display: block;
margin: 0 auto;
width: 15%;
height: 15%;
background-color: #333;
border-radius: 100%;
animation: addr-rnd-spinnerBounceDelay 1.2s infinite ease-in-out both;
}
#addr-rnd .addr-rnd-spinner .addr-rnd-spinner2 {
transform: rotate(30deg);
}
#addr-rnd .addr-rnd-spinner .addr-rnd-spinner3 {
transform: rotate(60deg);
}
#addr-rnd .addr-rnd-spinner .addr-rnd-spinner4 {
transform: rotate(90deg);
}
#addr-rnd .addr-rnd-spinner .addr-rnd-spinner5 {
transform: rotate(120deg);
}
#addr-rnd .addr-rnd-spinner .addr-rnd-spinner6 {
transform: rotate(150deg);
}
#addr-rnd .addr-rnd-spinner .addr-rnd-spinner7 {
transform: rotate(180deg);
}
#addr-rnd .addr-rnd-spinner .addr-rnd-spinner8 {
transform: rotate(210deg);
}
#addr-rnd .addr-rnd-spinner .addr-rnd-spinner9 {
transform: rotate(240deg);
}
#addr-rnd .addr-rnd-spinner .addr-rnd-spinner10 {
transform: rotate(270deg);
}
#addr-rnd .addr-rnd-spinner .addr-rnd-spinner11 {
transform: rotate(300deg);
}
#addr-rnd .addr-rnd-spinner .addr-rnd-spinner12 {
transform: rotate(330deg);
}
#addr-rnd .addr-rnd-spinner .addr-rnd-spinner2:before {
animation-delay: -1.1s;
}
#addr-rnd .addr-rnd-spinner .addr-rnd-spinner3:before {
animation-delay: -1s;
}
#addr-rnd .addr-rnd-spinner .addr-rnd-spinner4:before {
animation-delay: -0.9s;
}
#addr-rnd .addr-rnd-spinner .addr-rnd-spinner5:before {
animation-delay: -0.8s;
}
#addr-rnd .addr-rnd-spinner .addr-rnd-spinner6:before {
animation-delay: -0.7s;
}
#addr-rnd .addr-rnd-spinner .addr-rnd-spinner7:before {
animation-delay: -0.6s;
}
#addr-rnd .addr-rnd-spinner .addr-rnd-spinner8:before {
animation-delay: -0.5s;
}
#addr-rnd .addr-rnd-spinner .addr-rnd-spinner9:before {
animation-delay: -0.4s;
}
#addr-rnd .addr-rnd-spinner .addr-rnd-spinner10:before {
animation-delay: -0.3s;
}
#addr-rnd .addr-rnd-spinner .addr-rnd-spinner11:before {
animation-delay: -0.2s;
}
#addr-rnd .addr-rnd-spinner .addr-rnd-spinner12:before {
animation-delay: -0.1s;
}
@-webkit-keyframes addr-rnd-spinnerBounceDelay {
0%, 80%, 100% {
transform: scale(0);
}
40% {
transform: scale(1);
}
}
@keyframes addr-rnd-spinnerBounceDelay {
0%, 80%, 100% {
transform: scale(0);
}
40% {
transform: scale(1);
}
}
`;
mainContainer.appendChild(style);
if (unsafeWindow.aarTemplates) {
templates = unsafeWindow.aarTemplates;
}
const parent = document.querySelector('.ship-info, div[class*="AddressList_AddressList__addressForm"], div[class*="AddressFormWidget_ModalContainer"] div[class*="snow-dialog-v2_SnowDialogV2__closeBtn"] ~ div');
const buttonsContainer = mainContainer.querySelector('.addr-rnd-buttons');
const spinner = mainContainer.querySelector('.addr-rnd-spinner');
const tmps = Object.keys(templates);
let filling = false;
for (const t of tmps) {
if ((isAliexpressRu && templates[t].ru !== true) || (!isAliexpressRu && templates[t].ru === true)) {
continue;
}
const btn = document.createElement('button');
btn.className = 'addr-rnd-btn';
btn.innerText = t;
btn.addEventListener('click', () => {
if (filling) {
return;
}
filling = true;
enableButtons(false);
fill(templates[t]).then(() => {
filling = false;
enableButtons(true);
});
});
buttonsContainer.appendChild(btn);
}
parent.prepend(mainContainer);
};
const isAliexpressRu = location.host.includes('aliexpress.ru');
let initialized = false;
log.debug('start');
setInterval(() => {
const el = document.querySelector('.ship-info, input[class*="AddressFormWidget_SelectComponent__inputField"]');
if (el) {
if (!initialized) {
initialized = true;
if (el.classList.contains('ship-info')) {
waitForElement('.ship-info .list_country .country-item').then(init);
} else {
init();
}
}
} else {
initialized = false;
}
}, 500);
if (config.disableNewAddressForm && location.pathname == '/p/trade/confirm.html') {
setTimeout(() => {
if (!`${unsafeWindow.XMLHttpRequest}`.includes('getsupportcountrynew')) {
// https://gist.github.com/jasperck/130651ca255eec1772074e5d5428c363
const XHR = unsafeWindow.XMLHttpRequest;
function newXHR() {
const xhr = new XHR();
xhr.addEventListener('readystatechange', () => {
if (xhr.readyState === 4 && xhr.responseURL.includes('mtop.aliexpress.address.sdk.getsupportcountrynew')) {
const originalResponse = xhr.responseText;
Object.defineProperty(xhr, 'responseText', { writable: true });
xhr.responseText = originalResponse.replace(/"sdkCountryList":\[.*?\]/, '"sdkCountryList":[]');
}
});
return xhr;
}
newXHR.prototype = XHR.prototype;
unsafeWindow.XMLHttpRequest = newXHR;
}
}, 1000);
}
})();
// ==UserScript==
// @name AliExpress Address Randomizer Templates
// @description Шаблоны для AliExpress Address Randomizer
// @author longnull
// @namespace longnull
// @version 1.0.2
// @match *://ilogisticsaddress.aliexpress.com/addressList.htm*
// @match *://ilogisticsaddress.aliexpress.ru/addressList.htm*
// @match *://shoppingcart.aliexpress.com/orders.htm*
// @match *://shoppingcart.aliexpress.ru/orders.htm*
// @match *://shoppingcart.aliexpress.com/order/confirm_order.htm*
// @match *://shoppingcart.aliexpress.ru/order/confirm_order.htm*
// @match *://www.aliexpress.com/p/trade/confirm.html*
// @match *://aliexpress.ru/checkout*
// @match *://aliexpress.ru/address-list*
// @grant GM_xmlhttpRequest
// @connect *
// @noframes
// ==/UserScript==
(() => {
'use strict';
unsafeWindow.aarTemplates = {
// Шаблоны
};
const httpRequest = (params) => {
return new Promise((resolve) => {
params.timeout = 30000;
params.onload = resolve;
params.onerror = resolve;
params.ontimeout = resolve;
params.onabort = resolve;
if (!params.method) {
params.method = 'GET';
}
const func = typeof GM.xmlHttpRequest !== 'undefined' ? GM.xmlHttpRequest : GM_xmlhttpRequest;
func(params);
});
};
})();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment