您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
2GIS mobile phone number copier allow you to copy any Russian mobile phone number to your device"s clipboard
// ==UserScript== // @name 2GIS phone number copier // @namespace http://2gis.ru/ // @license MIT // @version 0.5.0 // @description 2GIS mobile phone number copier allow you to copy any Russian mobile phone number to your device"s clipboard // @author Kenya-West // @include https://2gis.ru/* // @grant GM_setClipboard // @grant GM_getValue // @grant GM_setValue // ==/UserScript== // store url on load let currentPage = location.href; let recordList = getFromGM("recordList"); let tableMode = recordList.length ? true : false; urlCheckCounter = 0; // listen for changes setInterval(function() { if (currentPage !== location.href || urlCheckCounter === 0) { // page has changed, set new page as "current" currentPage = location.href; main(); } urlCheckCounter++; }, 175); function main() { // Grand-parent for parentElems const phoneContainer = document.querySelector("._172gbf8:has(>._49kxlr>._b0ke8)"); // Parents for hrefs const phoneContainerChild = document.querySelector("._172gbf8 ._49kxlr:has(>._b0ke8)"); // // INIT FUNCTION SET // if (expandTels()) { placeTableMode(); addListenersOnRender(); setTimeout(() => { placeIcons(); }, 100); // placeIcons(elemsMedia, parentElemsMedia, parentHrefsMedia, numberLinkMedia); } /** * Expands "Показать телефоны" button * * The top-level checking function * * @returns `boolean` */ function expandTels() { const event = new Event("click", {bubbles: true}) const expandElem = document.querySelector("._1ns0i7c"); if (expandElem) { expandElem.dispatchEvent(event); return true; } else { return false; } } // // PLACE FUNCTION SET // function placeIcons() { // Parents for hrefs const parents = document.querySelectorAll("._49kxlr > ._b0ke8"); // Parent elements a[href] that are parents for elems const hrefs = document.querySelectorAll("._49kxlr > ._b0ke8 > a"); // Number link like `tel:` containing number. Usually it"s the hrefs[index] const telLink = document.querySelectorAll("._49kxlr ._b0ke8 > a"); // Elements that contain phone number in innerText const elems = document.querySelectorAll("._49kxlr > ._b0ke8 > a"); elems.forEach((element, index) => { let whatsappLink = preparePhoneNumber(telLink[index].href, index); let phoneLink = preparePhoneNumber(telLink[index].href, index); let phoneNumber = preparePhoneNumber(telLink[index].href, index); if (whatsappLink && !parents[index].getAttribute("whatsapp-linked")) { let whatsapp = "https://wa.me/" + whatsappLink; let button = prepareWhatsappButton(whatsapp, index); parents[index].insertBefore(button, hrefs[index]); parents[index].setAttribute("whatsapp-linked", true); storePhoneOrLink("whatsappLink", whatsapp); } if (phoneLink) { let phone = "tel:+" + phoneLink; } if (phoneNumber && !parents[index].getAttribute("phone-linked")) { let phone = `+${phoneNumber[0]} ${phoneNumber[1]}${phoneNumber[2]}${phoneNumber[3]} ${phoneNumber[4]}${phoneNumber[5]}${phoneNumber[6]}-${phoneNumber[7]}${phoneNumber[8]}-${phoneNumber[9]}${phoneNumber[10]}` let button = preparePhoneButton(phone, index); parents[index].insertBefore(button, hrefs[index]); parents[index].setAttribute("phone-linked", true); storePhoneOrLink("phone", phone); } updateState(); }); } function placeCounter() { let counter = prepareCounter(); if(counter) { // let element = prepareCounter(); phoneContainerChild.appendChild(counter); } updateState(); } function placeTableMode() { let tableModeButton = prepareTableModeButton(); phoneContainerChild.appendChild(tableModeButton); tableModeCheckbox.checked = tableMode; } function tableAddRemoveButtonManager(value) { if (value) { placeTableAddRemoveButton(); } else { removeTableAddRemoveButton(); } } function placeTableAddRemoveButton() { const host = phoneContainerChild.querySelector("#tableModeAddRemoveButtonHost"); if (!host) { let tableModeAddRemoveButton = prepareTableModeAddRemoveButton(); phoneContainerChild.appendChild(tableModeAddRemoveButton); phoneContainer.querySelector("#tableModeAddRemoveButton").addEventListener("click", (element) => { pushOrRemoveRecord(); }) } } function removeTableAddRemoveButton() { const elem = phoneContainerChild.querySelector("#tableModeAddRemoveButtonHost"); if (elem) { elem.parentNode.removeChild(elem) } } function tableCopyButtonManager(value) { if (value) { placeTableCopyButton(); } else { removeTableCopyButton(); } } function placeTableCopyButton() { const host = phoneContainerChild.querySelector("#tableCopyButtonHost"); if (!host) { let tableCopyButton = prepareTableCopyButton(); phoneContainerChild.appendChild(tableCopyButton); phoneContainer.querySelector("#tableCopyButton").addEventListener("click", (element) => { copyTable(); }) } } function removeTableCopyButton() { const elem = phoneContainerChild.querySelector("#tableCopyButtonHost"); if (elem) { elem.parentNode.removeChild(elem) } } function placeTable() { const data = recordList; // { // id: firmId ?? recordList.length, // name: firmName, // shortUrl: value.shorturl ?? "", // fullUrl: location.href, // phones: [...getPhonesOrLinks("phone")], // whatsappLinks: [...getPhonesOrLinks("whatsappLink")] // } phoneContainer.insertAdjacentHTML( `beforeend`, ` <table id="contactsTable" bordercolor="gray" border="2"> <thead> <tr> <th>📄 Название</th> <th>📞 Номер</th> <th>🔗 Ссылка</th> <th>💭 Комментарий</th> </tr> </thead> <tbody> ${[...data] .map( (_, i) => `<tr>${[...Array(4)] .map( (_, j) => { if (j===1) { return `<td><a href="${data[i].shortUrl ?? data[i].fullUrl}">${data[i].name}</a></td>`} if (j===2) { return `<td>${data[i].phones.map((_, k) => { return `${_}<br>` }).join("")}</td>`} if (j===3) { return `<td>${data[i].whatsappLinks.map((_, k) => { return `<a href="${_}">${_}</a><br>` }).join("")}</td>`} if (j===4) { return `<td></td>`} } ) .join("")} </tr>` ) .join("")} </tbody> </table> ` ); } function removeTable() { const elem = phoneContainer.querySelector("#contactsTable"); elem.parentNode.removeChild(elem); } // // PREPARE FUNCTION SET // function preparePhoneNumber(phone, index) { const regex = /79\d+/; if (regex.test(phone)) { let result = regex.exec(phone); if (result[0].length < 11) { return null; } return result[0] } else { return null } } function prepareWhatsappButton(whatsappLink, index) { const button = document.createElement("img"); button.id = "whatsappLink" + index; button.src = "https://upload.wikimedia.org/wikipedia/commons/1/19/WhatsApp_logo-color-vertical.svg"; button.style.width = "18px"; button.style.height = "18px"; button.style.display = "inline-block"; button.style.cursor = "pointer"; button.style.marginRight = "5px"; button.setAttribute("whatsapp-link", whatsappLink); button.addEventListener("click", (element) => { if (element.target && element.target.id === button.id) { GM_setClipboard(whatsappLink); element.toElement.style.backgroundImage = "green"; window.setTimeout(() => { element.toElement.style.backgroundImage = "none"; }, 1000); } }); return button; } function preparePhoneButton(phone, index) { const button = document.createElement("img"); button.id = "phone" + index; button.src = "https://upload.wikimedia.org/wikipedia/commons/8/83/Circle-icons-phone.svg"; button.style.width = "18px"; button.style.height = "18px"; button.style.display = "inline-block"; button.style.cursor = "pointer"; button.style.marginRight = "5px"; button.setAttribute("phone", phone); button.style.tra button.addEventListener("click", (element) => { if (element.target && element.target.id === button.id) { GM_setClipboard(phone); element.toElement.style.backgroundImage = "green"; window.setTimeout(() => { element.toElement.style.backgroundImage = "none"; }, 1000); } }); return button; } function prepareTableModeButton() { let innerHTML = `<ul> <li class="_gyromm" id="tableMode"> <label class="_vvxysz1" title="Режим таблицы" ><div class="_okzfjf"> <svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32" fill="black"> <path d="M19 18V6H5v15a5 5 0 0 0 5 5h12a5 5 0 0 0 5-5v-3zm-6 3a3 3 0 0 1-6 0V8h10v10h-4zm12 0a3 3 0 0 1-3 3h-8a5 5 0 0 0 1-3v-1h10z"></path> <path d="M9 10h6v2H9zM9 14h4v2H9z"></path> </svg> </div> <input id="tableModeCheckbox" class="_1u9fru1" type="checkbox" value=0 /><span class="_1iurgbx" >Режим таблицы</span ></label > </li> </ul> ` const button = document.createElement("div"); button.classList.add("_b0ke8"); button.innerHTML = innerHTML; return button; } function prepareTableModeAddRemoveButton() { let innerHTML = `<div class="_rtsy3" id="tableModeAddRemoveButtonHost" > <div class="_jro6t0"> <div class="_11pijuk"> <button id="tableModeAddRemoveButton" class="_1uwefnfu" type="button">Добавить</button> </div> </div> </div> ` const button = document.createElement("div"); button.innerHTML = innerHTML; return button; } function prepareTableCopyButton() { let innerHTML = `<div class="_rtsy3" id="tableCopyButtonHost" > <div class="_jro6t0"> <div class="_11pijuk"> <button id="tableCopyButton" class="_1uwefnfu" style="background-color: #f2f2f2;color: black;margin-top: 5px;" type="button">Копировать</button> </div> </div> </div> ` const button = document.createElement("div"); button.innerHTML = innerHTML; return button; } function prepareCounter() { const counter = document.createElement("div"); counter.classList.add("_b0ke8"); counter.setAttribute("id", "recordListCounter"); return counter; } // // LISTENERS AND SERVICES // function updateState() { updateCounter(); changeTableModeAddRemoveButtonState(phoneContainerChild.querySelector("#tableModeAddRemoveButton")); storeValue("recordList", recordList); tableModeValueChangeHandler(tableMode); tableAddRemoveButtonManager(tableMode); tableCopyButtonManager(recordList.length > 0); } function updateCounter() { const counter = phoneContainer.querySelector("#recordListCounter"); if (counter) { counter.innerText = `Количество материалов: ${recordList.length}`; if (!tableMode) { counter.parentNode.removeChild(counter) } } else if (!counter && tableMode) { placeCounter(); } } function changeTableModeAddRemoveButtonState(elem) { const firmId = /firm\/(\d+)/gi.exec(location.href)[1]; if (phoneContainerChild.querySelector("#tableModeAddRemoveButtonHost")) { if (recordList.find((record) => record.id === firmId)) { elem.textContent = "Удалить"; elem.style.backgroundColor = "#299400"; elem.style.color = "white"; } else { elem.textContent = "Добавить"; elem.style.backgroundColor = "#f2f2f2"; elem.style.color = "black"; } } } function addListenersOnRender() { const tableModeHost = phoneContainer.querySelector("#tableMode"); const tableModeCheckbox = phoneContainer.querySelector("#tableModeCheckbox"); if (tableModeHost) { tableModeHost.addEventListener("click", (element) => { tableModeCheckbox.checked = !tableModeCheckbox.checked; tableMode = tableModeCheckbox.checked; updateState(); }); } } function tableModeValueChangeHandler(checked) { // change view const host = phoneContainer.querySelector("#tableMode"); const label = host?.querySelector("._vvxysz1, ._1t53cmw5"); const icon = host?.querySelector("._okzfjf, ._1laf6tw8"); if (host && label && icon) { if (checked) { label.style.backgroundColor = "rgb(48, 173, 0)"; label.style.color = "white"; icon.querySelector("svg").setAttribute("fill", "white"); } else { label.style.backgroundColor = "inherit"; label.style.color = "initial"; icon.querySelector("svg").setAttribute("fill", "back"); } } } function storeValue(key, value) { GM_setValue(JSON.stringify(key, value)); } // DATA PARSING const _phones = []; const _whatsappLinks = []; /** * Sets phones or links to a runtime var * @param {string} mode "phone", "whatsappLink" * @param {string} data string value (phone or link) */ function storePhoneOrLink(mode, data) { switch (mode) { case "phone": _phones.push(data) break; case "whatsappLink": _whatsappLinks.push(data) break; } } /** * Returns phones or links from a runtime var * @param {string} mode "phone", "whatsappLink" */ function getPhonesOrLinks(mode) { switch (mode) { case "phone": return _phones; case "whatsappLink": return _whatsappLinks; } } async function fetchData() { let response = await fetch(`https://go.2gis.com/add/?mode=json&encoded=true&url=${encodeURI(location.href)}`); return response.json(); } function pushOrRemoveRecord() { const firmId = /firm\/(\d+)/gi.exec(location.href)[1]; const firmName = document.querySelector("._6htn2u ._oqoid").innerText; // removes a record if (recordList.find((record) => record.id === firmId)) { recordList = recordList.filter((record) => record.id !== firmId); } // adds a record else { fetchData().then((value) => { recordList.push({ id: firmId ?? recordList.length, name: firmName, shortUrl: value.shorturl ?? "", fullUrl: location.href, phones: [...getPhonesOrLinks("phone")], whatsappLinks: [...getPhonesOrLinks("whatsappLink")] }); }) .finally(() => updateState()) } updateState(); } function copyTable() { placeTable(); const table = phoneContainer.querySelector("#contactsTable"); try { // create a Range object var range = document.createRange(); var sel = window.getSelection(); // set the Node to select the "range" range.selectNode(table); sel.removeAllRanges(); sel.addRange(range); // add the Range to the set of window selections // window.getSelection().addRange(range); // execute 'copy', can't 'cut' in this case document.execCommand('copy'); } catch(e) {} removeTable(); } } // // FUNCTIONS THAT NEED GLOBAL SCOPE // function getFromGM(key) { try { return JSON.parse(GM_getValue(key)); } catch (error) { return [] } }